From 9e2fed983f046715beec69e0355ad6b2fd3a5903 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 23 Nov 2007 15:22:58 -0500 Subject: [PATCH] Linux 2.2.19pre1 o Basic page aging (Neil Schemenauer) | This is a beginning to trying to get the VM right | Next stage is to go through Andrea's stuff and sort | it out the way I want it. o E820 memory detect backport from 2.4 (Michael Chen) o Fix cs46xx refusing to run on emachines400 (Alan Cox) o Fix parport docs (Tim Waugh) o Fix USB serial name reporting (Alan Cox) o Fix else warning in initio scsi (John Fort) o Fix incorrect timeout (that couldnt occur fortunately) in sched.c (Andrew Archibald) o Fix A20 fix credits (Christian Lademann) o Support for OnStream SC-x0 tape drives (Willem Riede, Kurt Garloff) o Intel 815 added to the AGPGART code (Robert M Love) o 3Ware scsi fixes (Arnaldo Carvalho de Melo) o Clean up scsi_init_malloc no mem case (Arnaldo Carvalho de Melo) o Fix dead module parameter in ip_masq_user.c (Keith Owens) o Switch max_files and friends to a struct to (Tigran Aivazian) be sure they stay together o Update microcode driver (Tigran Aivazian) o Fix free memory dereference in lance driver (Eli Carter) o ISOfs fixes (Andries Brouwer) o Watchdog driver for Advantech boards (Marek Michalkiewicz) o ISDN updates (Karsten Keil) o Docs fix (Pavel Rabel) o wake_one semantics for accept() (Andrew Morton) o Masquerade updates (Juanjo Ciarlante) o Add support for long serialnums on the Metricom (Alex Belits) o Onboard ethernet driver for the Intel 'Panther' (Ard van Breemen, boards Andries Brouwer) o VIA686a timer reset to 18Hz background (Vojtech Pavlik) o 3c527 driver rewrite (Richard Procter) | This supercedes my driver because | - it works for more people | - he has time to use his MCA box to debug it o Minix subpartition support (Anand Krishnamurthy Rajeev Pillai) o Remove unused() crap from DRM. You will need to hand load agp as well if needed (Alan Cox) --- CREDITS | 13 + Documentation/Changes | 25 + Documentation/Configure.help | 111 +- Documentation/filesystems/minix-part.txt | 43 + Documentation/ioctl-number.txt | 2 + Documentation/isdn/README.HiSax | 19 +- Documentation/networking/ip_masq/rc.masqfw | 106 + Documentation/s390/cds.txt | 2 +- MAINTAINERS | 14 + Makefile | 4 +- arch/i386/boot/setup.S | 74 +- arch/i386/config.in | 12 +- arch/i386/kernel/microcode.c | 122 +- arch/i386/kernel/setup.c | 117 +- arch/i386/kernel/time.c | 24 + drivers/block/genhd.c | 74 +- drivers/char/Config.in | 17 +- drivers/char/Makefile | 8 + drivers/char/advantechwdt.c | 243 + drivers/char/agp/agp_backend.h | 1 + drivers/char/agp/agpgart_be.c | 7 + drivers/char/console.c | 1 + drivers/char/drm/gamma_drv.c | 5 - drivers/char/drm/i810_drv.c | 5 - drivers/char/drm/mga_drv.c | 5 - drivers/char/drm/r128_drv.c | 5 - drivers/char/drm/tdfx_drv.c | 7 - drivers/char/misc.c | 3 + drivers/isdn/Config.in | 15 +- drivers/isdn/Makefile | 4 +- drivers/isdn/act2000/Makefile | 2 +- drivers/isdn/act2000/act2000.h | 27 +- drivers/isdn/act2000/act2000_isa.c | 41 +- drivers/isdn/act2000/act2000_isa.h | 13 +- drivers/isdn/act2000/capi.c | 30 +- drivers/isdn/act2000/capi.h | 21 +- drivers/isdn/act2000/module.c | 71 +- drivers/isdn/avmb1/Makefile | 118 +- drivers/isdn/avmb1/avm_cs.c | 528 ++ drivers/isdn/avmb1/b1.c | 7 +- drivers/isdn/avmb1/b1dma.c | 10 +- drivers/isdn/avmb1/b1isa.c | 16 +- drivers/isdn/avmb1/b1pci.c | 62 +- drivers/isdn/avmb1/b1pcmcia.c | 25 +- drivers/isdn/avmb1/c4.c | 149 +- drivers/isdn/avmb1/capi.c | 1710 ++++++- drivers/isdn/avmb1/capicmd.h | 17 +- drivers/isdn/avmb1/capidev.h | 35 +- drivers/isdn/avmb1/capidrv.c | 318 +- drivers/isdn/avmb1/capifs.c | 609 +++ drivers/isdn/avmb1/capifs.h | 25 + drivers/isdn/avmb1/capiutil.c | 23 +- drivers/isdn/avmb1/capiutil.h | 72 +- drivers/isdn/avmb1/kcapi.c | 302 +- drivers/isdn/avmb1/t1isa.c | 17 +- drivers/isdn/avmb1/t1pci.c | 47 +- drivers/isdn/divert/divert_procfs.c | 19 +- drivers/isdn/hisax/Makefile | 16 +- drivers/isdn/hisax/amd7930.c | 21 +- drivers/isdn/hisax/arcofi.c | 39 +- drivers/isdn/hisax/arcofi.h | 23 +- drivers/isdn/hisax/asuscom.c | 39 +- drivers/isdn/hisax/avm_a1.c | 64 +- drivers/isdn/hisax/avm_a1p.c | 28 +- drivers/isdn/hisax/avm_pci.c | 95 +- drivers/isdn/hisax/bkm_a4t.c | 65 +- drivers/isdn/hisax/bkm_a8.c | 322 +- drivers/isdn/hisax/bkm_ax.h | 24 +- drivers/isdn/hisax/callc.c | 201 +- drivers/isdn/hisax/cert.c | 21 +- drivers/isdn/hisax/config.c | 375 +- drivers/isdn/hisax/diva.c | 137 +- drivers/isdn/hisax/elsa.c | 129 +- drivers/isdn/hisax/elsa_ser.c | 7 + drivers/isdn/hisax/fsm.c | 46 +- drivers/isdn/hisax/gazel.c | 70 +- drivers/isdn/hisax/hfc_2bds0.c | 37 +- drivers/isdn/hisax/hfc_2bds0.h | 14 +- drivers/isdn/hisax/hfc_2bs0.c | 47 +- drivers/isdn/hisax/hfc_2bs0.h | 13 +- drivers/isdn/hisax/hfc_pci.c | 135 +- drivers/isdn/hisax/hfc_pci.h | 26 +- drivers/isdn/hisax/hfc_sx.c | 31 +- drivers/isdn/hisax/hfc_sx.h | 12 +- drivers/isdn/hisax/hfcscard.c | 33 +- drivers/isdn/hisax/hisax.h | 234 +- drivers/isdn/hisax/hscx.c | 57 +- drivers/isdn/hisax/hscx.h | 19 +- drivers/isdn/hisax/hscx_irq.c | 44 +- drivers/isdn/hisax/icc.c | 685 +++ drivers/isdn/hisax/icc.h | 73 + drivers/isdn/hisax/ipac.h | 20 +- drivers/isdn/hisax/isac.c | 80 +- drivers/isdn/hisax/isac.h | 23 +- drivers/isdn/hisax/isar.c | 99 +- drivers/isdn/hisax/isar.h | 30 +- drivers/isdn/hisax/isdnl1.c | 296 +- drivers/isdn/hisax/isdnl1.h | 32 +- drivers/isdn/hisax/isdnl2.c | 78 +- drivers/isdn/hisax/isdnl2.h | 8 +- drivers/isdn/hisax/isdnl3.c | 70 +- drivers/isdn/hisax/isdnl3.h | 36 +- drivers/isdn/hisax/isurf.c | 40 +- drivers/isdn/hisax/ix1_micro.c | 45 +- drivers/isdn/hisax/jade.c | 7 +- drivers/isdn/hisax/jade.h | 8 +- drivers/isdn/hisax/jade_irq.c | 7 +- drivers/isdn/hisax/l3_1tr6.c | 53 +- drivers/isdn/hisax/l3_1tr6.h | 12 +- drivers/isdn/hisax/l3dss1.c | 333 +- drivers/isdn/hisax/l3dss1.h | 31 +- drivers/isdn/hisax/l3ni1.c | 3172 ++++++++++++ drivers/isdn/hisax/l3ni1.h | 136 + drivers/isdn/hisax/lmgr.c | 26 +- drivers/isdn/hisax/md5sums.asc | 38 +- drivers/isdn/hisax/mic.c | 34 +- drivers/isdn/hisax/netjet.c | 324 +- drivers/isdn/hisax/netjet.h | 77 + drivers/isdn/hisax/niccy.c | 62 +- drivers/isdn/hisax/nj_s.c | 257 + drivers/isdn/hisax/nj_u.c | 260 + drivers/isdn/hisax/q931.c | 394 +- drivers/isdn/hisax/rawhdlc.c | 5 +- drivers/isdn/hisax/rawhdlc.h | 6 +- drivers/isdn/hisax/s0box.c | 7 +- drivers/isdn/hisax/saphir.c | 39 +- drivers/isdn/hisax/sedlbauer.c | 155 +- drivers/isdn/hisax/sportster.c | 49 +- drivers/isdn/hisax/tei.c | 80 +- drivers/isdn/hisax/teleint.c | 51 +- drivers/isdn/hisax/teles0.c | 52 +- drivers/isdn/hisax/teles3.c | 82 +- drivers/isdn/hisax/telespci.c | 58 +- drivers/isdn/hisax/w6692.c | 86 +- drivers/isdn/hisax/w6692.h | 14 +- drivers/isdn/isdn_common.c | 151 +- drivers/isdn/isdn_concap.c | 14 +- drivers/isdn/isdn_net.c | 361 +- drivers/isdn/isdn_net.h | 83 +- drivers/isdn/isdn_ppp.c | 317 +- drivers/isdn/isdn_tty.c | 48 +- drivers/isdn/isdn_tty.h | 2 + drivers/isdn/sc/debug.c | 11 +- drivers/isdn/sc/debug.h | 11 +- drivers/isdn/sc/init.c | 10 +- drivers/isdn/sc/message.c | 2 +- drivers/isdn/sc/timer.c | 10 +- drivers/net/3c527.c | 713 +-- drivers/net/3c527.h | 53 +- drivers/net/Config.in | 1 + drivers/net/Makefile | 5 + drivers/net/Space.c | 4 + drivers/net/lance.c | 3 +- drivers/net/lp486e.c | 1401 ++++++ drivers/net/strip.c | 39 +- drivers/scsi/3w-xxxx.c | 42 +- drivers/scsi/Config.in | 1 + drivers/scsi/Makefile | 8 + drivers/scsi/README.osst | 259 + drivers/scsi/hosts.c | 3 + drivers/scsi/hosts.h | 1 + drivers/scsi/i60uscsi.c | 4 +- drivers/scsi/osst.c | 5112 ++++++++++++++++++++ drivers/scsi/osst.h | 513 ++ drivers/scsi/osst_detect.h | 5 + drivers/scsi/osst_options.h | 100 + drivers/scsi/scsi.h | 2 +- drivers/scsi/sr.c | 47 +- drivers/scsi/st.c | 18 + drivers/sound/cs46xx.c | 2 +- drivers/usb/serial/usbserial.c | 2 +- fs/Config.in | 1 + fs/file_table.c | 22 +- fs/isofs/dir.c | 130 +- fs/isofs/inode.c | 465 +- fs/isofs/joliet.c | 6 +- fs/isofs/namei.c | 222 +- fs/isofs/rock.c | 45 +- fs/isofs/util.c | 7 +- include/asm-i386/e820.h | 40 + include/asm-i386/msr.h | 4 + include/asm-i386/processor.h | 17 + include/linux/agp_backend.h | 1 + include/linux/blk.h | 10 +- include/linux/capi.h | 14 + include/linux/fs.h | 7 +- include/linux/isdn.h | 33 +- include/linux/isdn_ppp.h | 6 +- include/linux/isdnif.h | 21 +- include/linux/iso_fs.h | 27 +- include/linux/kernelcapi.h | 105 +- include/linux/major.h | 2 + include/linux/miscdevice.h | 1 + include/linux/mm.h | 4 + include/linux/mtio.h | 3 + include/linux/sched.h | 7 +- include/net/ip_masq.h | 99 +- init/main.c | 4 + kernel/sched.c | 22 +- kernel/sysctl.c | 4 +- mm/filemap.c | 25 +- mm/page_alloc.c | 1 + net/ipv4/ip_masq.c | 51 +- net/ipv4/ip_masq_app.c | 195 +- net/ipv4/ip_masq_ftp.c | 623 ++- net/ipv4/ip_masq_user.c | 1 - net/ipv4/tcp.c | 3 + net/netsyms.c | 2 +- net/unix/af_unix.c | 2 +- 209 files changed, 20770 insertions(+), 6176 deletions(-) create mode 100644 Documentation/filesystems/minix-part.txt create mode 100644 Documentation/networking/ip_masq/rc.masqfw create mode 100644 drivers/char/advantechwdt.c create mode 100644 drivers/isdn/avmb1/avm_cs.c create mode 100644 drivers/isdn/avmb1/capifs.c create mode 100644 drivers/isdn/avmb1/capifs.h create mode 100644 drivers/isdn/hisax/icc.c create mode 100644 drivers/isdn/hisax/icc.h create mode 100644 drivers/isdn/hisax/l3ni1.c create mode 100644 drivers/isdn/hisax/l3ni1.h create mode 100644 drivers/isdn/hisax/netjet.h create mode 100644 drivers/isdn/hisax/nj_s.c create mode 100644 drivers/isdn/hisax/nj_u.c create mode 100644 drivers/net/lp486e.c create mode 100644 drivers/scsi/README.osst create mode 100644 drivers/scsi/osst.c create mode 100644 drivers/scsi/osst.h create mode 100644 drivers/scsi/osst_detect.h create mode 100644 drivers/scsi/osst_options.h create mode 100644 include/asm-i386/e820.h diff --git a/CREDITS b/CREDITS index 47cd9ee2d8f6..1f7b8f7c81f5 100644 --- a/CREDITS +++ b/CREDITS @@ -23,6 +23,12 @@ D: NFS over TCP patches S: University of Limerick S: Ireland +N: Tigran Aivazian +E: tigran@veritas.com +W: http://www.moses.uklinux.net/patches +D: Intel IA32 microcode update driver +S: United Kingdom + N: Werner Almesberger E: werner.almesberger@lrc.di.epfl.ch D: dosfs, LILO, some fd features, various other hacks here and there @@ -1335,6 +1341,13 @@ S: 8786 Niwot Road S: Niwot, Colorado 80503 S: USA +N: Robert M. Love +E: rml@tech9.net +E: rml@ufl.edu +D: misc. kernel hacking and debugging +S: Gainesville, Florida 32608 +S: USA + N: H.J. Lu E: hjl@gnu.ai.mit.edu D: GCC + libraries hacker diff --git a/Documentation/Changes b/Documentation/Changes index 131b4302f3a6..371eb98e7c7f 100644 --- a/Documentation/Changes +++ b/Documentation/Changes @@ -517,6 +517,31 @@ ISDN4Linux Older isdn4k-utils versions don't support EXTRAVERSION into kernel version string. A upgrade to isdn4k-utils.v3.1beta7 or later is recomented. +Intel IA32 microcode +==================== + +A driver has been added to allow updating of Intel IA32 microcode, +accessible as a misc character device. If you have not done so already +you might want to: + +mkdir /dev/cpu +mknod /dev/cpu/microcode c 10 184 +chmod 0644 /dev/cpu/microcode + +as root before you can use this. You'll probably also want to +get the user-space microcode_ctl utility to use with this. The utility is +available at: + +http://www.urbanmyth.org/microcode + +If you have compiled the driver as a module you may need to add +the following line: + +alias char-major-10-184 microcode + +to your /etc/modules.conf file. + + Where to get the files ********************** diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 3f86c6f47b12..f9c2acc4a052 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -1765,7 +1765,7 @@ CONFIG_M386 - "586" for generic Pentium CPUs, possibly lacking the TSC (time stamp counter) register. - "Pentium" for the Intel Pentium/Pentium MMX, AMD K5, K6 and - K6-3D. + K6-3D, as well as the Cyrix/VIA CyrixIII - "PPro" for the Cyrix/IBM/National Semiconductor 6x86MX, MII and Intel Pentium II/Pentium Pro. @@ -2233,10 +2233,18 @@ CONFIG_PARPORT The module will be called parport.o. If you have more than one parallel port and want to specify which port and IRQ to be used by this driver at module load time, read - Documentation/networking/net-modules.txt. + Documentation/parport.txt. If unsure, say Y. +IEEE1284 transfer modes +CONFIG_PARPORT_1284 + If you have a printer that supports status readback or device ID, or + want to use a device that uses enhanced parallel port transfer modes + such as EPP, you can say Y here to enable advanced IEEE 1284 + transfer modes. Also say Y if you want device ID information to + appear in /proc/parport/*/autoprobe*. It is safe to say N. + PC-style hardware CONFIG_PARPORT_PC You should say Y here if you have a PC-style parallel port. All IBM @@ -3771,6 +3779,24 @@ CONFIG_CHR_DEV_ST module, say M here and read Documentation/modules.txt and Documentation/scsi.txt . +OnStream SC-x0 SCSI tape support +CONFIG_CHR_DEV_OSST + The OnStream SC-x0 SCSI tape drives can not be driven by the + standard st driver, but instead need this special osst driver and + use the /dev/osstX char device nodes (major 206). + For more information, you may have a look at the SCSI-HOWTO + ftp://metalab.unc.edu/pub/Linux/docs/HOWTO and + drivers/scsi/README.osst in the kernel source. + Most info may be found on http://linux1.onstream.nl/test/ + Please also have a look at the standard st docu, as most of it + applies to osst as well. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called osst.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt and + Documentation/scsi.txt . + SCSI CDROM support CONFIG_BLK_DEV_SR If you want to use a SCSI CDROM under Linux, say Y and read the @@ -6790,6 +6816,16 @@ CONFIG_APRICOT Documentation/networking/net-modules.txt. The module will be called apricot.o. +LP486E on board Ethernet +CONFIG_LP486E + If you have an Intel Panther motherboard with on board 82596 network + (Ethernet) controller, say Y here. + The initialization code tries to guess the ethernet address by looking + at the EISA configuration area. Probably it finds precisely one thing + that looks like an address, but if this fails use the command + ifconfig eth0 hw ether a1:a2:a3:a4:a5:a6 + to assign the address (revealed by the BIOS Setup) by hand. + Generic DECchip & DIGITAL EtherWORKS PCI/EISA CONFIG_DE4X5 This is support for the DIGITAL series of PCI/EISA Ethernet cards. @@ -8730,6 +8766,12 @@ CONFIG_MAC_PARTITION partition tables of Macintosh hard drives, and thus use partitions on those drives. +Minix subpartition support +CONFIG_MINIX_SUBPARTITION + Minix 2.0.0/2.0.2 subpartition table support for Linux. + Say Y here if you want to mount and use Minix 2.0.0/2.0.2 + subpartitions. + SMB filesystem support (to mount Windows shares etc...) CONFIG_SMB_FS SMB (Server Message Block) is the protocol Windows for Workgroups @@ -10411,12 +10453,12 @@ CONFIG_60XX_WDT it as a module. The module will be called sbc60xxwdt.o. CONFIG_MICROCODE - /dev/cpu/microcode - Intel P6 CPU microcode support + /dev/cpu/microcode - Intel IA32 CPU microcode support If you say Y here you will be able to update the microcode on - Intel processors in the P6 family, e.g. Pentium Pro, Pentium II, - Pentium III, Xeon etc. You will obviously need the actual microcode - binary data itself which is not shipped with the Linux kernel. + Intel processors in the IA32 family, e.g. Pentium Pro, Pentium II, + Pentium III, Xeon, Pentium 4 etc. You will obviously need the actual + microcode binary data itself which is not shipped with the Linux kernel. For latest news and information on obtaining all the required ingredients for this driver, check: @@ -10425,7 +10467,9 @@ CONFIG_MICROCODE This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called microcode.o. If you want to compile it as - a module, say M here and read Documentation/modules.txt. + a module, say M here and read Documentation/modules.txt. If you use + modprobe or kmod you may also want to add the line + 'alias char-major-10-184 microcode' to your /etc/modules.conf file. /dev/cpu/*/msr - Model-specific register support CONFIG_X86_MSR @@ -10476,8 +10520,8 @@ CONFIG_AGP Intel 440LX/BX/GX support CONFIG_AGP_INTEL - This option give you AGP support for the GLX component of the - "soon to be released" XFree86-4 on Intel 440LX/BX/GX chipsets. + This option gives you AGP support for the GLX component of + XFree86 4.x on Intel 440LX/BX/GX, 815, and 840 chipsets. For the moment, most people should say no, unless you want to test the GLX component which can be downloaded from @@ -10485,9 +10529,9 @@ CONFIG_AGP_INTEL Intel I810/I810 DC100/I810e support CONFIG_AGP_I810 - This option give you AGP support for the Xserver for the intel - 810 chipset boards. This is required to do any useful video - modes. + This option gives you AGP support for XFree86 on the Intel 810 + and 815 chipsets for their on-board integrated graphics. This + is required to do any useful video modes with these boards. VIA VP3/MVP3/Apollo Pro support CONFIG_AGP_VIA @@ -10976,8 +11020,7 @@ CONFIG_TRIX_BOOT_FILE Support for OPTi MAD16 and/or Mozart based cards CONFIG_SOUND_MAD16 Answer Y if your card has a Mozart (OAK OTI-601) or MAD16 (OPTi - 82C928 or 82C929 or 82C931) audio interface chip. For the 82C931, - please read drivers/sound/README.C931. These chips are currently + 82C928 or 82C929 or 82C931) audio interface chip. These chips are quite common so it's possible that many no-name cards have one of them. In addition the MAD16 chip is used in some cards made by known manufacturers such as Turtle Beach (Tropez), Reveal (some models) @@ -11560,6 +11603,10 @@ HiSax Support for german 1TR6 CONFIG_HISAX_1TR6 Enable this if you have a old german 1TR6 line. +HiSax Support for US NI1 +CONFIG_HISAX_NI1 + Enable this if you like to use ISDN in US on a NI1 basic rate interface. + Teles 16.0/8.0 CONFIG_HISAX_16_0 This enables HiSax support for the Teles ISDN-cards S0-16.0, S0-8 @@ -11688,6 +11735,13 @@ CONFIG_HISAX_NETJET See Documentation/isdn/README.HiSax on how to configure it using a different D-channel protocol, or non-standard IRQ/port settings. +NETspider U card +CONFIG_HISAX_NETJET_U + This enables HiSax support for the Netspider U interface ISDN card from + Traverse Technologies. + See Documentation/isdn/README.HiSax on how to configure it using a + different D-channel protocol, or non-standard IRQ/port settings. + Niccy PnP/PCI card CONFIG_HISAX_NICCY This enables HiSax support for the Dr. Neuhaus Niccy PnP or PCI. @@ -11800,22 +11854,19 @@ CONFIG_ISDN_TTY_FAX an ISDN-fax-machine. This must be supported by the lowlevel driver also. See Documentation/isdn/README.fax for more information. -AVM CAPI2.0 support -CONFIG_ISDN_DRV_AVMB1 - This enables support for the AVM B1/T1 ISDN networking cards.In - addition, a CAPI (Common ISDN Application Programming Interface, a - standard making it easy for programs to access ISDN hardware, see - http://www.capi.org/; to browse the WWW, you need to have access to - a machine on the Internet that has a program like lynx or netscape) - interface for this card is provided. In order to use this card, - additional firmware is necessary, which has to be downloaded into - the card using a utility which is distributed separately. Please - read the file Documentation/isdn/README.avmb1. - - This code is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called avmb1.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. +CAPI2.0 support +CONFIG_ISDN_CAPI + This option enables a CAPI2.0 (Common ISDN Application Programming + Interface), a standard making it easy for programs to access ISDN + hardware, see http://www.capi.org/ + +CAPI2.0 Middleware support +CONFIG_ISDN_CAPI_MIDDLEWARE + This option enables CAPI2.0 Middleware support + +CAPI2.0 filesystem support +CONFIG_ISDN_CAPIFS + This option enables CAPI CAPI2.0 filesystem support AVM B1 ISA support CONFIG_ISDN_DRV_AVMB1_B1ISA diff --git a/Documentation/filesystems/minix-part.txt b/Documentation/filesystems/minix-part.txt new file mode 100644 index 000000000000..0eb8957f954b --- /dev/null +++ b/Documentation/filesystems/minix-part.txt @@ -0,0 +1,43 @@ +Minix 2.0.0/2.0.2 subpartition support. +======================================= + + Minix subpartition support is provided in `linux/drivers/block/genhd.c'. +Minix partitions are shown with the device name suffixed with an `@' +followed by any detected subpartitions inside angle brackets, like this: + +Partition check: + hda: hda1 hda2@ < hda5 hda6 > hda3 hda4 + +Usage +===== + +Add entries to /etc/fstab, change for your setup: + /dev/hda5 /mnt/minix minix rw,noauto 0 0 + /dev/hda6 /mnt/minix/usr minix rw,noauto 0 0 + +To mount your Minix filesystems: + mount /mnt/minix # mount / + mount /mnt/minix/usr # mount /usr + + +Note +==== + +The first sector of a Minix 2.0.0/2.0.2 partition containing +subpartitions looks like the master boot record (MBR) on the first +sector of the hard disk. + +It has + 1) a small loader program which loads the boot sector from + the Minix root partition (usually hd?a). + 2) a small partition table which gives the starting + C/H/S values and sizes for the 4 Minix subpartitions. + +If there are no subpartitions then the first sector of the Minix +partition contains the usual boot sector. + +Bug Reports +=========== + +Anand Krishnamurthy +Rajeev V. Pillai diff --git a/Documentation/ioctl-number.txt b/Documentation/ioctl-number.txt index 195491fa46e2..6f16292116f2 100644 --- a/Documentation/ioctl-number.txt +++ b/Documentation/ioctl-number.txt @@ -113,6 +113,8 @@ Code Seq# Include File Comments 'z' 40-7F CAN bas card in development: +'6' 00-10 Intel IA32 microcode update driver + 0x89 00-0F asm-i386/sockios.h 0x89 10-DF linux/sockios.h 0x89 E0-EF linux/sockios.h SIOCPROTOPRIVATE range diff --git a/Documentation/isdn/README.HiSax b/Documentation/isdn/README.HiSax index fb64b1ecd2fc..63308c20a1fc 100644 --- a/Documentation/isdn/README.HiSax +++ b/Documentation/isdn/README.HiSax @@ -53,7 +53,8 @@ Sedlbauer Speed Star/Speed Star2 (PCMCIA) Sedlbauer ISDN-Controller PC/104 USR Sportster internal TA (compatible Stollmann tina-pp V3) ith Kommunikationstechnik GmbH MIC 16 ISA card -Traverse Technologie NETjet PCI S0 card +Traverse Technologie NETjet PCI S0 card and NETspider U card +Ovislink ISDN sc100-p card (NETjet driver) Dr. Neuhaus Niccy PnP/PCI Siemens I-Surf 1.0 Siemens I-Surf 2.0 (with IPAC, try type 12 asuscom) @@ -134,7 +135,12 @@ mem=,0xd0000. See example 6 below. The parameter for the D-Channel protocol may be omitted if you selected the correct one during kernel config. Valid values are "1" for German 1TR6, -"2" for EDSS1 (Euro ISDN) and "3" for leased lines (no D-Channel). +"2" for EDSS1 (Euro ISDN), "3" for leased lines (no D-Channel) and "4" +for US NI1. +With US NI1 you have to include your SPID into the MSN setting in the form +: for example (your phonenumber is 1234 your SPID 5678): +AT&E1234:5678 on ttyI interfaces +isdnctrl eaz ippp0 1234:5678 on network devices The Creatix/Teles PnP cards use io1= and io2= instead of io= for specifying the I/O addresses of the ISAC and HSCX chips, respectively. @@ -186,8 +192,9 @@ Card types: 34 Gazel ISDN cards (PCI) none 35 HFC 2BDS0 PCI none 36 W6692 based PCI cards none - 37 HFC 2BDS0 S+, SP/PCMCIA irq,io (pcmcia must be set with cardmgr) - + 37 HFC 2BDS0 S+, SP irq,io + 38 NETspider U PCI card none + 39 HFC 2BDS0 SP/PCMCIA irq,io (set with cardmgr) At the moment IRQ sharing is only possible with PCI cards. Please make sure that your IRQ is free and enabled for ISA use. @@ -291,7 +298,9 @@ type 34 Gazel ISDN cards (PCI) no parameter 35 HFC 2BDS0 PCI no parameter 36 W6692 based PCI cards none - 37 HFC 2BDS0 S+,SP/PCMCIA pa=irq, pb=io + 37 HFC 2BDS0 S+,SP/PCMCIA ONLY WORKS AS A MODULE ! + 38 NETspider U PCI card none + Running the driver ------------------ diff --git a/Documentation/networking/ip_masq/rc.masqfw b/Documentation/networking/ip_masq/rc.masqfw new file mode 100644 index 000000000000..15f36b25467e --- /dev/null +++ b/Documentation/networking/ip_masq/rc.masqfw @@ -0,0 +1,106 @@ +#!/bin/sh +# +# rc.masqfw v0.2 +# +# Author: Juanjo Ciarlante +# 03-Oct-00 +# +# Setup reverse masquerading (ala portfw) with using firewall mark-ing +# _AND_ userspace "redir" support for complete redirection (even local +# connects). +# +# Semantics: +# Let ip_masq_mfw intercept truly "forward-able" packets based +# on ipchains ruling _AND_ leave the rest for redir tool. +# +# Requirement: +# - ipmasqadm http://juanjox.kernelnotes.org/ +# . setups kernel mark forwarding) +# - redir 2.2.x http://freshmeat.net/search/?q=redir +# . setups "local" socket forwarding) +# - ip_masq_ftp PATCHED for firewall marking (module parm "in_mark") +# . support internal server forwarding for PASV clients) +# +# Setup: +# EXT_IP, EXT_PORT: external (visible) IP address (can be '0') and port. +# INT_IP, INT_PORT: internal server address and port +# IN_MARK: arbitrary value to use when marking pkts (ipchains -m) +# FTP: if not-null activate ftp support +# REDIR: path to redir-2.2.x (set to "" to avoid using) + +export PATH="/sbin:/usr/sbin:$PATH" + +EXT_IP=192.168.2.16 +EXT_PORT=2021 +INT_IP=10.1.1.128 +INT_PORT=21 +IN_MARK=4321 +FTP=1 +REDIR="/usr/sbin/redir" + +FW_IPCHAINS="-i eth0 -d $EXT_IP $EXT_PORT -p tcp" +PID_FILE="/var/run/redir-$EXT_IP-$EXT_PORT" + +# seems ascii art... but it runs!! =) +# +run() { + $* || { + echo "-> '$*'" + return 1 + } +} + +get_pid() { + test -f $PID_FILE || return 1 + typeset pid=`cat $PID_FILE` + test -n "$pid" || return 1 + kill -0 $pid || return 1 + echo $pid +} +exit_err() { + ERR=$1;shift + echo $@ >&2 + exit $ERR +} +redir_on() { + test -n "$REDIR" || return 1 + test -n "$FTP" && REDIR="$REDIR --ftp=both" + $SHELL -c 'echo $$ > '"$PID_FILE + exec $REDIR \ + --laddr $EXT_IP --lport $EXT_PORT \ + --caddr $INT_IP --cport $INT_PORT + " & + +} +redir_off() { + test -n "$REDIR" || return 1 + if pid=`get_pid`;then + kill $pid + rm $PID_FILE + fi + fuser -k -n tcp $EXT_PORT +} + +case "$1" in +start) + #run modprobe ip_masq_mfw + run ipmasqadm mfw -I -m $IN_MARK -r $INT_IP $INT_PORT + if test -n "$FTP";then + run modprobe ip_masq_ftp in_mark=$IN_MARK || \ + exit_err 1 "Incorrect ftp module version ?" + fi + run ipchains -m $IN_MARK -I input $FW_IPCHAINS + redir_on + ;; +stop) + run ipchains -m $IN_MARK -D input $FW_IPCHAINS + if test -n "$FTP";then + run rmmod ip_masq_ftp + fi + run ipmasqadm mfw -D -m $IN_MARK -r $INT_IP $INT_PORT + #run ipmasqadm mfw -F + #run rmmod ip_masq_mfw + redir_off + ;; + +esac diff --git a/Documentation/s390/cds.txt b/Documentation/s390/cds.txt index f79d2e1fb022..ac3f6be7b7c6 100644 --- a/Documentation/s390/cds.txt +++ b/Documentation/s390/cds.txt @@ -241,7 +241,7 @@ The device driver may use these commands as appropriate. The get_dev_info_by_irq() / get_dev_info_by_devno() functions return: - 0 - sucessful completion + 0 - successful completion -ENODEV - irq or devno don't specify a known subchannel or device number. -EINVAL - invalid devinfo value. diff --git a/MAINTAINERS b/MAINTAINERS index 6f62f8af930e..f2d7e7b82818 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -493,6 +493,13 @@ W: http://paulbristow.net/linux L: linux-kernel@vger.kernel.org S: Maintained +INTEL IA32 MICROCODE UPDATE DRIVER +P: Tigran Aivazian +M: Tigran Aivazian +W: http://www.urbanmyth.org/microcode +L: linux-kernel@vger.kernel.org +S: Maintained + IP FIREWALL P: Paul Russell M: Paul.Russell@rustcorp.com.au @@ -725,6 +732,13 @@ L: linux-tr@emissary.aus-etc.com W: http://www.linuxtr.net S: Maintained +ONSTREAM SCSI TAPE DRIVER +P: Willem Riede +M: osst@riede.org +L: osst@linux1.onstream.nl +L: linux-scsi@vger.rutgers.edu +S: Maintained + OPL3-SA2, SA3, and SAx DRIVER P: Scott Murray M: scott@spiteful.org diff --git a/Makefile b/Makefile index 78e6d405d69f..f057cc53e5b5 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 2 -SUBLEVEL = 18 -EXTRAVERSION = +SUBLEVEL = 19 +EXTRAVERSION = pre1 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) diff --git a/arch/i386/boot/setup.S b/arch/i386/boot/setup.S index d1a8c5960ac5..821e877752f4 100644 --- a/arch/i386/boot/setup.S +++ b/arch/i386/boot/setup.S @@ -32,7 +32,7 @@ ! ! ! A20 gating fiddled to work on AMD Elan AmSC4xx series by kira@linuxgrrls.org -! july 1999 +! july 1999 - from a 2.0 patch by Christian Lademann ! #define __ASSEMBLY__ @@ -248,16 +248,46 @@ loader_panic_mess: loader_ok: ! Get memory size (extended mem, kB) - + xor eax, eax + mov [0x1e0], eax #ifndef STANDARD_MEMORY_BIOS_CALL - push ebx - - xor ebx,ebx ! preload new memory slot with 0k - mov [0x1e0], ebx - - mov ax,#0xe801 + mov [0x1e8], al +meme820: + mov edx, #0x534d4150 + xor ebx, ebx + mov di,#0x2d0 + +jmpe820: + mov eax,#0x0000e820 + mov ecx,#20 + push ds + pop es int 0x15 - jc oldstylemem + jc bail820 + + cmp eax,#0x534d4150 + jne bail820 + + cmp 16(di),#1 + jne again820 + +good820: + mov al,[0x1e8] + cmp al,#32 + jnl bail820 + + incb [0x1e8] + mov ax,di + add ax,#20 + mov di,ax +again820: + cmp ebx,#0 + jne jmpe820 +bail820: +meme801: + mov ax,#0xe801 + int 0x15 + jc mem88 ! Memory size is in 1 k chunksizes, to avoid confusing loadlin. ! We store the 0xe801 memory size in a completely different place, @@ -266,23 +296,19 @@ loader_ok: ! alternative new memory detection scheme, and it's sensible ! to write everything into the same place.) - and ebx, #0xffff ! clear sign extend - shl ebx, 6 ! and go from 64k to 1k chunks - mov [0x1e0],ebx ! store extended memory size - - and eax, #0xffff ! clear sign extend - add [0x1e0],eax ! and add lower memory into total size. - - ! and fall into the old memory detection code to populate the - ! compatibility slot. + and ebx, #0xffff ! clear sign extend + shl ebx, 6 ! and go from 64k to 1k chunks + mov [0x1e0],ebx ! store extented memory size + + and eax, #0xffff ! clear sign extend + add [0x1e0],eax ! and add lower memory into total size. -oldstylemem: - pop ebx -#else - mov dword ptr [0x1e0], #0 + ! and fall into the old memory detection to populate the + ! compatibility slot. +mem88: #endif - mov ah,#0x88 - int 0x15 + mov ah,#0x88 + int 0x15 mov [2],ax ! Set the keyboard repeat rate to the max diff --git a/arch/i386/config.in b/arch/i386/config.in index 1b01142ef828..978d985485ba 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -12,11 +12,11 @@ endmenu mainmenu_option next_comment comment 'Processor type and features' choice 'Processor family' \ - "386 CONFIG_M386 \ - 486/Cx486 CONFIG_M486 \ - 586/K5/5x86/6x86 CONFIG_M586 \ - Pentium/K6/TSC CONFIG_M586TSC \ - PPro/6x86MX CONFIG_M686" PPro + "386 CONFIG_M386 \ + 486/Cx486 CONFIG_M486 \ + 586/K5/5x86/6x86 CONFIG_M586 \ + Pentium/K6/TSC/CyrixIII CONFIG_M586TSC \ + PPro/6x86MX CONFIG_M686" PPro # # Define implied options from the CPU selection here # @@ -33,7 +33,7 @@ if [ "$CONFIG_M686" = "y" ]; then define_bool CONFIG_X86_GOOD_APIC y fi -tristate '/dev/cpu/microcode - Intel P6 CPU microcode support' CONFIG_MICROCODE +tristate '/dev/cpu/microcode - Intel IA32 CPU microcode support' CONFIG_MICROCODE tristate '/dev/cpu/*/msr - Model-specific register support' CONFIG_X86_MSR tristate '/dev/cpu/*/cpuid - CPU information support' CONFIG_X86_CPUID diff --git a/arch/i386/kernel/microcode.c b/arch/i386/kernel/microcode.c index f97a15d68adc..27f1c8c90fdc 100644 --- a/arch/i386/kernel/microcode.c +++ b/arch/i386/kernel/microcode.c @@ -4,12 +4,13 @@ * Copyright (C) 2000 Tigran Aivazian * * This driver allows to upgrade microcode on Intel processors - * belonging to P6 family - PentiumPro, Pentium II, Pentium III etc. + * belonging to IA32 family - PentiumPro, Pentium II, Pentium III, + * Pentium II Xeon, Pentium III Xeon, Pentium 4 etc. * - * Reference: Section 8.10 of Volume III, Intel Pentium III Manual, - * Order Number 243192 or download from: + * Reference: Section 8.10 of Volume III, Intel Pentium 4 Manual, + * Order Number 245472 or free download from: * - * http://developer.intel.com/design/pentiumii/manuals/243192.htm + * http://developer.intel.com/design/pentium4/manuals/245472.htm * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,6 +31,8 @@ * Added MICROCODE_IOCFREE ioctl to clear memory. * 1.05 09 June 2000, Simon Trimmer * Messages for error cases (non intel & no suitable microcode). + * 1.06 07 Dec 2000, Tigran Aivazian + * Pentium 4 support + backported fixes from 2.4 */ #include @@ -37,7 +40,6 @@ #include #include #include -#include #include #include @@ -45,45 +47,25 @@ #include #include -#define MICROCODE_MINOR 184 -#define MICROCODE_IOCFREE _IO('6',0) -struct microcode { - unsigned int hdrver; - unsigned int rev; - unsigned int date; - unsigned int sig; - unsigned int cksum; - unsigned int ldrver; - unsigned int pf; - unsigned int reserved[5]; - unsigned int bits[500]; -}; - -#define MICROCODE_VERSION "1.05" +#define MICROCODE_VERSION "1.06" -MODULE_DESCRIPTION("Intel CPU (P6) microcode update driver"); +MODULE_DESCRIPTION("Intel CPU (IA-32) microcode update driver"); MODULE_AUTHOR("Tigran Aivazian "); EXPORT_NO_SYMBOLS; /* VFS interface */ static int microcode_open(struct inode *, struct file *); -static int microcode_release(struct inode *, struct file *); static ssize_t microcode_read(struct file *, char *, size_t, loff_t *); static ssize_t microcode_write(struct file *, const char *, size_t, loff_t *); static int microcode_ioctl(struct inode *, struct file *, unsigned int, unsigned long); +/* read()/write()/ioctl() are serialized on this */ +static struct semaphore microcode_sem = MUTEX; /* internal helpers to do the work */ static int do_microcode_update(void); static void do_update_one(void *); -/* - * Bits in microcode_status. (31 bits of room for future expansion) - */ -#define MICROCODE_IS_OPEN 0 /* set if device is in use */ - -static unsigned long microcode_status; - /* the actual array of microcode blocks, each 2048 bytes */ static struct microcode *microcode; static unsigned int microcode_num; @@ -95,7 +77,6 @@ static struct file_operations microcode_fops = { write: microcode_write, ioctl: microcode_ioctl, open: microcode_open, - release: microcode_release, }; static struct miscdevice microcode_dev = { @@ -114,18 +95,18 @@ int __init microcode_init(void) MICROCODE_MINOR); error = 1; } - printk(KERN_INFO "P6 Microcode Update Driver v%s registered\n", + printk(KERN_INFO "IA-32 Microcode Update Driver: v%s \n", MICROCODE_VERSION); return 0; } #ifdef MODULE -static void microcode_exit(void) +void cleanup_module(void) { misc_deregister(µcode_dev); if (mc_applied) kfree(mc_applied); - printk(KERN_INFO "P6 Microcode Update Driver v%s unregistered\n", + printk(KERN_INFO "IA-32 Microcode Update Driver v%s unregistered\n", MICROCODE_VERSION); } @@ -133,34 +114,14 @@ int init_module(void) { return microcode_init(); } -void cleanup_module(void) -{ - microcode_exit(); -} #endif -/* - * We enforce only one user at a time here with open/close. - */ static int microcode_open(struct inode *inode, struct file *file) { - if (!capable(CAP_SYS_RAWIO)) - return -EPERM; - - /* one at a time, please */ - if (test_and_set_bit(MICROCODE_IS_OPEN, µcode_status)) - return -EBUSY; - - return 0; -} - -static int microcode_release(struct inode *inode, struct file *file) -{ - clear_bit(MICROCODE_IS_OPEN, µcode_status); - return 0; + return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; } -/* a pointer to 'struct update_req' is passed to the IPI handler = do_update_one() +/* * update_req[cpu].err is set to 1 if update failed on 'cpu', 0 otherwise * if err==0, microcode[update_req[cpu].slot] points to applied block of microcode */ @@ -174,10 +135,10 @@ static int do_microcode_update(void) int i, error = 0, err; struct microcode *m; - if (smp_call_function(do_update_one, (void *)update_req, 1, 1) != 0) + if (smp_call_function(do_update_one, NULL, 1, 1) != 0) panic("do_microcode_update(): timed out waiting for other CPUs\n"); - do_update_one((void *)update_req); + do_update_one(NULL); for (i=0; ierr = 1; /* be pessimistic */ - if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6){ - printk(KERN_ERR "microcode: CPU%d not an Intel P6\n", cpu_num ); + if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 || + test_bit(X86_FEATURE_30, &c->x86_capability)) { /* IA64 */ + printk(KERN_ERR "microcode: CPU%d not a capable Intel processor\n", cpu_num); return; } sig = c->x86_mask + (c->x86_model<<4) + (c->x86<<8); - if (c->x86_model >= 5) { - /* get processor flags from BBL_CR_OVRD MSR (0x17) */ - rdmsr(0x17, val[0], val[1]); + if ((c->x86_model >= 5) || (c->x86 > 6)) { + /* get processor flags from MSR 0x17 */ + rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]); pf = 1 << ((val[1] >> 18) & 7); } @@ -219,7 +181,12 @@ static void do_update_one(void *arg) found=1; - rdmsr(0x8B, val[0], rev); + /* trick, to work even if there was no prior update by the BIOS */ + wrmsr(MSR_IA32_UCODE_REV, 0, 0); + __asm__ __volatile__ ("cpuid" : : : "ax", "bx", "cx", "dx"); + + /* get current (on-cpu) revision into rev (ignore val[0]) */ + rdmsr(MSR_IA32_UCODE_REV, val[0], rev); if (microcode[i].rev < rev) { printk(KERN_ERR "microcode: CPU%d not 'upgrading' to earlier revision" @@ -241,10 +208,16 @@ static void do_update_one(void *arg) break; } - wrmsr(0x79, (unsigned int)(m->bits), 0); + /* write microcode via MSR 0x79 */ + wrmsr(MSR_IA32_UCODE_WRITE, (unsigned int)(m->bits), 0); + + /* serialize */ __asm__ __volatile__ ("cpuid" : : : "ax", "bx", "cx", "dx"); - rdmsr(0x8B, val[0], val[1]); + /* get the current revision from MSR 0x8B */ + rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]); + + /* notify the caller of success on this cpu */ req->err = 0; req->slot = i; printk(KERN_ERR "microcode: CPU%d updated from revision " @@ -255,18 +228,23 @@ static void do_update_one(void *arg) } if(!found) - printk(KERN_ERR "microcode: found no data for CPU%d (sig=%x, pflags=%d)\n", cpu_num, sig, pf); + printk(KERN_ERR "microcode: CPU%d no microcode found! (sig=%x, pflags=%d)\n", + cpu_num, sig, pf); } static ssize_t microcode_read(struct file *file, char *buf, size_t len, loff_t *ppos) { if (*ppos >= mc_fsize) return 0; + down(µcode_sem); if (*ppos + len > mc_fsize) len = mc_fsize - *ppos; - if (copy_to_user(buf, mc_applied + *ppos, len)) + if (copy_to_user(buf, mc_applied + *ppos, len)) { + up(µcode_sem); return -EFAULT; + } *ppos += len; + up(µcode_sem); return len; } @@ -279,17 +257,18 @@ static ssize_t microcode_write(struct file *file, const char *buf, size_t len, l sizeof(struct microcode)); return -EINVAL; } + down(µcode_sem); if (!mc_applied) { mc_applied = kmalloc(smp_num_cpus*sizeof(struct microcode), GFP_KERNEL); if (!mc_applied) { + up(µcode_sem); printk(KERN_ERR "microcode: out of memory for saved microcode\n"); return -ENOMEM; } memset(mc_applied, 0, mc_fsize); } - lock_kernel(); microcode_num = len/sizeof(struct microcode); microcode = vmalloc(len); if (!microcode) { @@ -310,7 +289,7 @@ static ssize_t microcode_write(struct file *file, const char *buf, size_t len, l out_fsize: vfree(microcode); out_unlock: - unlock_kernel(); + up(µcode_sem); return ret; } @@ -319,6 +298,7 @@ static int microcode_ioctl(struct inode *inode, struct file *file, { switch(cmd) { case MICROCODE_IOCFREE: + down(µcode_sem); if (mc_applied) { memset(mc_applied, 0, mc_fsize); kfree(mc_applied); @@ -326,8 +306,10 @@ static int microcode_ioctl(struct inode *inode, struct file *file, printk(KERN_WARNING "microcode: freed %d bytes\n", mc_fsize); mc_fsize = 0; + up(µcode_sem); return 0; } + up(µcode_sem); return -ENODATA; default: diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index db9fa01eef1d..7b771822b4f5 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -73,6 +73,7 @@ #include #include #include +#include /* * Machine setup.. @@ -103,6 +104,7 @@ struct sys_desc_table_struct { unsigned short length; unsigned char table[0]; }; +struct e820map e820; unsigned char aux_device_present; @@ -123,6 +125,8 @@ extern unsigned long cpu_khz; #define SCREEN_INFO (*(struct screen_info *) (PARAM+0)) #define EXT_MEM_K (*(unsigned short *) (PARAM+2)) #define ALT_MEM_K (*(unsigned long *) (PARAM+0x1e0)) +#define E820_MAP_NR (*(char*) (PARAM+E820NR)) +#define E820_MAP ((struct e820entry *) (PARAM+E820MAP)) #define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+0x40)) #define DRIVE_INFO (*(struct drive_info_struct *) (PARAM+0x80)) #define SYS_DESC_TABLE (*(struct sys_desc_table_struct*)(PARAM+0xa0)) @@ -268,6 +272,87 @@ visws_get_board_type_and_rev(void) visws_board_rev); } #endif +void __init add_memory_region(unsigned long long start, + unsigned long long size, int type) +{ + int x = e820.nr_map; + + if (x == E820MAX) { + printk("Ooops! Too many entries in the memory map!\n"); + return; + } + + e820.map[x].addr = start; + e820.map[x].size = size; + e820.map[x].type = type; + e820.nr_map++; +} /* add_memory_region */ + +static int __init copy_e820_map(struct e820entry * biosmap, int nr_map) +{ + /* Only one memory region (or negative)? Ignore it */ + if (nr_map < 2) + return -1; + + do { + unsigned long long start = biosmap->addr; + unsigned long long size = biosmap->size; + unsigned long long end = start + size; + unsigned long type = biosmap->type; + + /* Overflow in 64 bits? Ignore the memory map. */ + if (start > end) + return -1; + + /* + * Some BIOSes claim RAM in the 640k - 1M region. + * Not right. Fix it up. + */ + if (type == E820_RAM) { + if (start < 0x100000ULL && end > 0xA0000ULL) { + if (start < 0xA0000ULL) + add_memory_region(start, 0xA0000ULL-start, type); + if (end < 0x100000ULL) + continue; + start = 0x100000ULL; + size = end - start; + } + } + add_memory_region(start, size, type); + } while (biosmap++,--nr_map); + return 0; +} + +#define LOWMEMSIZE() (0x9f000) + +void __init setup_memory_region(void) +{ + char *who = "BIOS-e820"; + + /* + * Try to copy the BIOS-supplied E820-map. + * + * Otherwise fake a memory map; one section from 0k->640k, + * the next section from 1mb->appropriate_mem_k + */ + if (copy_e820_map(E820_MAP, E820_MAP_NR) < 0) { + unsigned long mem_size; + + /* compare results from other methods and take the greater */ + if (ALT_MEM_K < EXT_MEM_K) { + mem_size = EXT_MEM_K; + who = "BIOS-88"; + } else { + mem_size = ALT_MEM_K; + who = "BIOS-e801"; + } + + e820.nr_map = 0; + add_memory_region(0, LOWMEMSIZE(), E820_RAM); + add_memory_region(HIGH_MEMORY, mem_size << 10, E820_RAM); + } + printk("BIOS-provided physical RAM map:\n"); +} /* setup_memory_region */ static char command_line[COMMAND_LINE_SIZE] = { 0, }; @@ -278,9 +363,11 @@ __initfunc(void setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * memory_end_p)) { unsigned long memory_start, memory_end; + unsigned long max_pfn; char c = ' ', *to = command_line, *from = COMMAND_LINE; int len = 0; int read_endbase_from_BIOS = 1; + int i; #ifdef CONFIG_VISWS visws_get_board_type_and_rev(); @@ -297,17 +384,7 @@ __initfunc(void setup_arch(char **cmdline_p, BIOS_revision = SYS_DESC_TABLE.table[2]; } aux_device_present = AUX_DEVICE_INFO; - memory_end = (1<<20) + (EXT_MEM_K<<10); -#ifndef STANDARD_MEMORY_BIOS_CALL - { - unsigned long memory_alt_end = (1<<20) + (ALT_MEM_K<<10); - /* printk(KERN_DEBUG "Memory sizing: %08x %08x\n", memory_end, memory_alt_end); */ - if (memory_alt_end > memory_end) - memory_end = memory_alt_end; - } -#endif - memory_end &= PAGE_MASK; #ifdef CONFIG_BLK_DEV_RAM rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK; rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0); @@ -325,6 +402,26 @@ __initfunc(void setup_arch(char **cmdline_p, memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE); saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; + setup_memory_region(); + +#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) +#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) + max_pfn = 0; + for (i = 0; i < e820.nr_map; i++) { + unsigned long start, end; + /* RAM? */ + if (e820.map[i].type != E820_RAM) + continue; + start = PFN_UP(e820.map[i].addr); + end = PFN_DOWN(e820.map[i].addr + e820.map[i].size); + if (start >= end) + continue; + if (end > max_pfn) + max_pfn = end; + } + memory_end = (max_pfn << PAGE_SHIFT); + + for (;;) { /* * "mem=nopentium" disables the 4MB page tables. diff --git a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c index 78dbaa8d36c3..5014ba282048 100644 --- a/arch/i386/kernel/time.c +++ b/arch/i386/kernel/time.c @@ -176,6 +176,14 @@ static unsigned long do_slow_gettimeoffset(void) count |= inb_p(0x40) << 8; + /* VIA686a test code... reset the latch if count > max */ + if (count > LATCH-1) { + outb_p(0x34, 0x43); + outb_p(LATCH & 0xff, 0x40); + outb(LATCH >> 8, 0x40); + count = LATCH - 1; + } + /* * avoiding timer inconsistencies (they are rare, but they happen)... * there are two kinds of problems that must be avoided here: @@ -468,6 +476,22 @@ static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) count = inb_p(0x40); /* read the latched count */ count |= inb(0x40) << 8; + + /* VIA686a test code... reset the latch if count > max */ + if (count > LATCH-1) { + static int last_whine; + outb_p(0x34, 0x43); + outb_p(LATCH & 0xff, 0x40); + outb(LATCH >> 8, 0x40); + count = LATCH - 1; + if(time_after(jiffies, last_whine)) + { + printk(KERN_WARNING "probable hardware bug: clock timer configuration lost - probably a VIA686a.\n"); + printk(KERN_WARNING "probable hardware bug: restoring chip configuration.\n"); + last_whine = jiffies + HZ; + } + } + #if 0 spin_unlock(&i8253_lock); #endif diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c index 25e9d786d5e5..bb1ee3e69ea9 100644 --- a/drivers/block/genhd.c +++ b/drivers/block/genhd.c @@ -54,6 +54,14 @@ le32_to_cpu(__a); \ }) +#define MSDOS_LABEL_MAGIC1 0x55 +#define MSDOS_LABEL_MAGIC2 0xAA + +static inline int +msdos_magic_present(unsigned char *p) { + return (p[0] == MSDOS_LABEL_MAGIC1 && p[1] == MSDOS_LABEL_MAGIC2); +} + struct gendisk *gendisk_head = NULL; static int current_minor = 0; @@ -244,8 +252,6 @@ static unsigned int get_ptable_blocksize(kdev_t dev) * only for the actual data partitions. */ -#define MSDOS_LABEL_MAGIC 0xAA55 - static void extended_partition(struct gendisk *hd, kdev_t dev, int sector_size) { struct buffer_head *bh; @@ -273,10 +279,10 @@ static void extended_partition(struct gendisk *hd, kdev_t dev, int sector_size) */ bh->b_state = 0; - if ((*(unsigned short *) (bh->b_data+510)) != cpu_to_le16(MSDOS_LABEL_MAGIC)) + if (!(msdos_magic_present(bh->b_data + 510))) goto done; - p = (struct partition *) (0x1BE + bh->b_data); + p = (struct partition *) (bh->b_data + 0x1be); this_size = hd->part[MINOR(dev)].nr_sects; @@ -499,7 +505,51 @@ static void unixware_partition(struct gendisk *hd, kdev_t dev) printk(" >"); } #endif + +#ifdef CONFIG_MINIX_SUBPARTITION +/* + * Minix 2.0.0/2.0.2 subpartition support. + * Anand Krishnamurthy + * Rajeev V. Pillai + */ +#define MINIX_PARTITION 0x81 /* Minix Partition ID */ +#define MINIX_NR_SUBPARTITIONS 4 +static void minix_partition(struct gendisk *hd, kdev_t dev) +{ + struct buffer_head *bh; + struct partition *p; + int mask = (1 << hd->minor_shift) - 1; + int i; + if (!(bh = bread(dev, 0, get_ptable_blocksize(dev)))) + return; + bh->b_state = 0; + + p = (struct partition *)(bh->b_data + 0x1be); + + /* The first sector of a Minix partition can have either + * a secondary MBR describing its subpartitions, or + * the normal boot sector. */ + if (msdos_magic_present(bh->b_data + 510) && + SYS_IND(p) == MINIX_PARTITION) { /* subpartition table present */ + + printk(" <"); + for (i = 0; i < MINIX_NR_SUBPARTITIONS; i++, p++) { + if ((current_minor & mask) == 0) + break; + /* add each partition in use */ + if (SYS_IND(p) == MINIX_PARTITION) { + add_partition(hd, current_minor, + START_SECT(p), NR_SECTS(p), 0); + current_minor++; + } + } + printk(" >"); + } + brelse(bh); +} +#endif /* CONFIG_MINIX_SUBPARTITION */ + static int msdos_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector) { int i, minor = current_minor; @@ -540,11 +590,11 @@ read_mbr: #ifdef CONFIG_BLK_DEV_IDE check_table: #endif - if (*(unsigned short *) (0x1fe + data) != cpu_to_le16(MSDOS_LABEL_MAGIC)) { + if (!(msdos_magic_present(data + 510))) { brelse(bh); return 0; } - p = (struct partition *) (0x1be + data); + p = (struct partition *) (data + 0x1be); #ifdef CONFIG_BLK_DEV_IDE if (!tested_for_xlate++) { /* Do this only once per disk */ @@ -649,8 +699,8 @@ check_table: 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, - ptype(SYS_IND(p))); + add_partition(hd, minor, first_sector+START_SECT(p)*sector_size, + NR_SECTS(p)*sector_size, ptype(SYS_IND(p))); if (is_extended_partition(p)) { printk(" <"); /* @@ -682,6 +732,12 @@ check_table: } } #endif +#ifdef CONFIG_MINIX_SUBPARTITION + if (SYS_IND(p) == MINIX_PARTITION) { + printk("@"); /* Minix partitions are indicated by '@' */ + minix_partition(hd, MKDEV(hd->major, minor)); + } +#endif #ifdef CONFIG_UNIXWARE_DISKLABEL if (SYS_IND(p) == UNIXWARE_PARTITION) unixware_partition(hd, MKDEV(hd->major, minor)); @@ -709,7 +765,7 @@ check_table: /* * Check for old-style Disk Manager partition table */ - if (*(unsigned short *) (data+0xfc) == cpu_to_le16(MSDOS_LABEL_MAGIC)) { + if (msdos_magic_present(data + 0xfc)) { p = (struct partition *) (0x1be + data); for (i = 4 ; i < 16 ; i++, current_minor++) { p--; diff --git a/drivers/char/Config.in b/drivers/char/Config.in index 26793bfd2841..28e8de47fcea 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -99,7 +99,13 @@ if [ "$CONFIG_WATCHDOG" != "n" ]; then mainmenu_option next_comment comment 'Watchdog Cards' bool ' Disable watchdog shutdown on close' CONFIG_WATCHDOG_NOWAYOUT - tristate ' WDT Watchdog timer' CONFIG_WDT + tristate ' Acquire SBC Watchdog Timer' CONFIG_ACQUIRE_WDT + tristate ' Advantech SBC Watchdog Timer' CONFIG_ADVANTECH_WDT + tristate ' Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG + tristate ' Mixcom Watchdog' CONFIG_MIXCOMWD + tristate ' SBC-60XX Watchdog Timer' CONFIG_60XX_WDT + tristate ' Software Watchdog' CONFIG_SOFT_WATCHDOG + tristate ' WDT ISA Watchdog timer' CONFIG_WDT if [ "$CONFIG_WDT" != "n" ]; then bool ' WDT501 features' CONFIG_WDT_501 if [ "$CONFIG_WDT_501" = "y" ]; then @@ -107,11 +113,6 @@ if [ "$CONFIG_WATCHDOG" != "n" ]; then fi fi tristate ' WDT PCI Watchdog timer' CONFIG_WDTPCI - tristate ' Software Watchdog' CONFIG_SOFT_WATCHDOG - tristate ' Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG - tristate ' Acquire SBC Watchdog Timer' CONFIG_ACQUIRE_WDT - tristate ' SBC-60XX Watchdog Timer' CONFIG_60XX_WDT - tristate ' Mixcom Watchdog' CONFIG_MIXCOMWD endmenu fi @@ -128,8 +129,8 @@ fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate '/dev/agpgart (AGP Support) (EXPERIMENTAL)' CONFIG_AGP n if [ "$CONFIG_AGP" != "n" ]; then - bool ' Intel 440LX/BX/GX support' CONFIG_AGP_INTEL - bool ' Intel I810/I810 DC100/I810e support' CONFIG_AGP_I810 + bool ' Intel 440LX/BX/GX and I815/820 support' CONFIG_AGP_INTEL + bool ' Intel I810/I815 (on-board video) support' CONFIG_AGP_I810 bool ' VIA VP3/MVP3/Apollo Pro support' CONFIG_AGP_VIA bool ' AMD Irongate support' CONFIG_AGP_AMD bool ' Generic SiS support' CONFIG_AGP_SIS diff --git a/drivers/char/Makefile b/drivers/char/Makefile index e3572c36d207..b20ee607885d 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -295,6 +295,14 @@ else endif endif +ifeq ($(CONFIG_ADVANTECH_WDT),y) +O_OBJS += advantechwdt.o +else + ifeq ($(CONFIG_ADVANTECH_WDT),m) + M_OBJS += advantechwdt.o + endif +endif + ifeq ($(CONFIG_60XX_WDT),y) O_OBJS += sbc60xxwdt.o else diff --git a/drivers/char/advantechwdt.c b/drivers/char/advantechwdt.c new file mode 100644 index 000000000000..ac13a6bf26de --- /dev/null +++ b/drivers/char/advantechwdt.c @@ -0,0 +1,243 @@ +/* + * Advantech Single Board Computer WDT driver for Linux 2.2.x + * + * (c) Copyright 2000 Marek Michalkiewicz + * + * Based on acquirewdt.c which is based on wdt.c. + * Original copyright messages: + * + * (c) Copyright 1996 Alan Cox , All Rights Reserved. + * http://www.redhat.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 1995 Alan Cox + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int advwdt_is_open = 0; + +/* + * You must set these - there is no sane way to probe for this board. + * + * To enable or restart, write the timeout value in seconds (1 to 63) + * to I/O port WDT_START. To disable, read I/O port WDT_STOP. + * Both are 0x443 for most boards (tested on a PCA-6276VE-00B1), but + * check your manual (at least the PCA-6159 seems to be different - + * the manual says WDT_STOP is 0x43, not 0x443). + * (0x43 is also a write-only control register for the 8254 timer!) + * + * TODO: module parameters to set the I/O port addresses and NOWAYOUT + * option at load time. + */ + +#define WDT_STOP 0x443 +#define WDT_START 0x443 + +#define WD_TIMO 60 /* 1 minute */ + + +/* + * Kernel methods. + */ + + +static void +advwdt_ping(void) +{ + /* Write a watchdog value */ + outb_p(WD_TIMO, WDT_START); +} + +static ssize_t +advwdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; + + if (count) { + advwdt_ping(); + return 1; + } + return 0; +} + +static ssize_t +advwdt_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static int +advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + static struct watchdog_info ident = { + WDIOF_KEEPALIVEPING, 1, "Advantech WDT" + }; + + switch (cmd) { + case WDIOC_GETSUPPORT: + if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))) + return -EFAULT; + break; + + case WDIOC_GETSTATUS: + if (copy_to_user((int *)arg, &advwdt_is_open, sizeof(int))) + return -EFAULT; + break; + + case WDIOC_KEEPALIVE: + advwdt_ping(); + break; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static int +advwdt_open(struct inode *inode, struct file *file) +{ + switch (MINOR(inode->i_rdev)) { + case WATCHDOG_MINOR: + if (advwdt_is_open) + return -EBUSY; + MOD_INC_USE_COUNT; + /* + * Activate + */ + + advwdt_is_open = 1; + advwdt_ping(); + return 0; + default: + return -ENODEV; + } +} + +static int +advwdt_close(struct inode *inode, struct file *file) +{ + if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) { +#ifndef CONFIG_WATCHDOG_NOWAYOUT + inb_p(WDT_STOP); +#endif + advwdt_is_open = 0; + } + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * Notifier for system down + */ + +static int +advwdt_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) { + /* Turn the card off */ + inb_p(WDT_STOP); + } + return NOTIFY_DONE; +} + +/* + * Kernel Interfaces + */ + + +static struct file_operations advwdt_fops = { + NULL, + advwdt_read, + advwdt_write, + NULL, /* No Readdir */ + NULL, /* No Select */ + advwdt_ioctl, + NULL, /* No mmap */ + advwdt_open, + NULL, /* flush */ + advwdt_close +}; + +static struct miscdevice advwdt_miscdev = { + WATCHDOG_MINOR, + "watchdog", + &advwdt_fops +}; + + +/* + * The WDT card needs to learn about soft shutdowns in order to + * turn the timebomb registers off. + */ + +static struct notifier_block advwdt_notifier = { + advwdt_notify_sys, + NULL, + 0 +}; + +#ifdef MODULE + +#define advwdt_init init_module + +void +cleanup_module(void) +{ + misc_deregister(&advwdt_miscdev); + unregister_reboot_notifier(&advwdt_notifier); +#if WDT_START != WDT_STOP + release_region(WDT_STOP,1); +#endif + release_region(WDT_START,1); +} + +#endif + +int __init +advwdt_init(void) +{ + printk("WDT driver for Advantech single board computer initialising.\n"); + + misc_register(&advwdt_miscdev); +#if WDT_START != WDT_STOP + request_region(WDT_STOP, 1, "Advantech WDT"); +#endif + request_region(WDT_START, 1, "Advantech WDT"); + register_reboot_notifier(&advwdt_notifier); + return 0; +} + +/* end of advantechwdt.c */ + diff --git a/drivers/char/agp/agp_backend.h b/drivers/char/agp/agp_backend.h index 750af5e01656..3a1be22fe1fd 100644 --- a/drivers/char/agp/agp_backend.h +++ b/drivers/char/agp/agp_backend.h @@ -45,6 +45,7 @@ enum chipset_type { INTEL_BX, INTEL_GX, INTEL_I810, + INTEL_I815, INTEL_I840, VIA_GENERIC, VIA_VP3, diff --git a/drivers/char/agp/agpgart_be.c b/drivers/char/agp/agpgart_be.c index 4d96e96461ec..5c6c39cbd870 100644 --- a/drivers/char/agp/agpgart_be.c +++ b/drivers/char/agp/agpgart_be.c @@ -2026,6 +2026,13 @@ static struct { "Intel", "440GX", intel_generic_setup }, + /* could we add support for PCI_DEVICE_ID_INTEL_815_1 too ? */ + { PCI_DEVICE_ID_INTEL_815_0, + PCI_VENDOR_ID_INTEL, + INTEL_I815, + "Intel", + "i815", + intel_generic_setup }, { PCI_DEVICE_ID_INTEL_840_0, PCI_VENDOR_ID_INTEL, INTEL_I840, diff --git a/drivers/char/console.c b/drivers/char/console.c index c249016a4024..e75951e9d020 100644 --- a/drivers/char/console.c +++ b/drivers/char/console.c @@ -2571,6 +2571,7 @@ void unblank_screen(void) console_blanked = 0; if (console_blank_hook) console_blank_hook(0); + set_palette(currcons); if (sw->con_blank(vc_cons[currcons].d, 0)) /* Low-level driver cannot restore -> do it ourselves */ update_screen(fg_console); diff --git a/drivers/char/drm/gamma_drv.c b/drivers/char/drm/gamma_drv.c index bdd77267b93d..90d9ee5a0962 100644 --- a/drivers/char/drm/gamma_drv.c +++ b/drivers/char/drm/gamma_drv.c @@ -35,11 +35,6 @@ #include -static void __attribute__((unused)) unused(void) -{ - agp_enable(0); -} - #ifndef PCI_DEVICE_ID_3DLABS_GAMMA #define PCI_DEVICE_ID_3DLABS_GAMMA 0x0008 #endif diff --git a/drivers/char/drm/i810_drv.c b/drivers/char/drm/i810_drv.c index 4f1550f356e5..afc6f24a8e71 100644 --- a/drivers/char/drm/i810_drv.c +++ b/drivers/char/drm/i810_drv.c @@ -35,11 +35,6 @@ #include -static void __attribute__((unused)) unused(void) -{ - agp_enable(0); -} - #define I810_NAME "i810" #define I810_DESC "Intel I810" #define I810_DATE "20000719" diff --git a/drivers/char/drm/mga_drv.c b/drivers/char/drm/mga_drv.c index bb5d07f4d4cf..dfbb3d9deb92 100644 --- a/drivers/char/drm/mga_drv.c +++ b/drivers/char/drm/mga_drv.c @@ -36,11 +36,6 @@ #include -static void __attribute__((unused)) unused(void) -{ - agp_enable(0); -} - #define MGA_NAME "mga" #define MGA_DESC "Matrox G200/G400" #define MGA_DATE "20000910" diff --git a/drivers/char/drm/r128_drv.c b/drivers/char/drm/r128_drv.c index ddeec7175170..b0e0138506c6 100644 --- a/drivers/char/drm/r128_drv.c +++ b/drivers/char/drm/r128_drv.c @@ -35,11 +35,6 @@ #include -static void __attribute__((unused)) unused(void) -{ - agp_enable(0); -} - #define R128_NAME "r128" #define R128_DESC "ATI Rage 128" #define R128_DATE "20000719" diff --git a/drivers/char/drm/tdfx_drv.c b/drivers/char/drm/tdfx_drv.c index a9287ec34150..6b849132871f 100644 --- a/drivers/char/drm/tdfx_drv.c +++ b/drivers/char/drm/tdfx_drv.c @@ -38,13 +38,6 @@ #include #endif -static void __attribute__((unused)) unused(void) -{ -#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) - agp_enable(0); -#endif -} - #define TDFX_NAME "tdfx" #define TDFX_DESC "3dfx Banshee/Voodoo3+" #define TDFX_DATE "20000719" diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 1f4210d574ec..05a8f3b5748f 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -230,6 +230,9 @@ int __init misc_init(void) #ifdef CONFIG_ACQUIRE_WDT acq_init(); #endif +#ifdef CONFIG_ADVANTECH_WDT + advwdt_init(); +#endif #ifdef CONFIG_60XX_WDT sbc60xxwdt_init(); #endif diff --git a/drivers/isdn/Config.in b/drivers/isdn/Config.in index 2ba7789d914c..7b8111881a66 100644 --- a/drivers/isdn/Config.in +++ b/drivers/isdn/Config.in @@ -10,7 +10,7 @@ if [ "$CONFIG_INET" != "n" ]; then 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 + bool ' Support AT-Fax Class 1 and 2 commands' CONFIG_ISDN_TTY_FAX fi if [ "$CONFIG_X25" != "n" ]; then bool ' X.25 PLP on top of ISDN' CONFIG_ISDN_X25 @@ -37,6 +37,7 @@ if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then bool ' Disable keypad protocol option' CONFIG_HISAX_NO_KEYPAD fi bool ' HiSax Support for german 1TR6' CONFIG_HISAX_1TR6 + bool ' HiSax Support for US NI1' CONFIG_HISAX_NI1 comment ' HiSax supported cards' bool ' Teles 16.0/8.0' CONFIG_HISAX_16_0 bool ' Teles 16.3 or PNP or PCMCIA' CONFIG_HISAX_16_3 @@ -44,7 +45,6 @@ if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then bool ' Teles S0Box' CONFIG_HISAX_S0BOX bool ' AVM A1 (Fritz)' CONFIG_HISAX_AVM_A1 bool ' AVM PnP/PCI (Fritz!PnP/PCI)' CONFIG_HISAX_FRITZPCI - bool ' AVM A1 PCMCIA (Fritz)' CONFIG_HISAX_AVM_A1_PCMCIA bool ' Elsa cards' CONFIG_HISAX_ELSA bool ' ITK ix1-micro Revision 2' CONFIG_HISAX_IX1MICROR2 bool ' Eicon.Diehl Diva cards' CONFIG_HISAX_DIEHLDIVA @@ -55,6 +55,7 @@ if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then bool ' USR Sportster internal TA' CONFIG_HISAX_SPORTSTER bool ' MIC card' CONFIG_HISAX_MIC bool ' NETjet card' CONFIG_HISAX_NETJET + bool ' NETspider U card' CONFIG_HISAX_NETJET_U bool ' Niccy PnP/PCI card' CONFIG_HISAX_NICCY bool ' Siemens I-Surf card' CONFIG_HISAX_ISURF bool ' HST Saphir card' CONFIG_HISAX_HSTSAPHIR @@ -85,8 +86,14 @@ dep_tristate 'Eicon 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 +dep_tristate 'CAPI2.0 support' CONFIG_ISDN_CAPI $CONFIG_ISDN +if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then + bool 'CAPI2.0 Middleware support' CONFIG_ISDN_CAPI_MIDDLEWARE + if [ "$CONFIG_ISDN_CAPI_MIDDLEWARE" != "n" ]; then + bool 'CAPI2.0 filesystem support' CONFIG_ISDN_CAPIFS + fi +fi +if [ "$CONFIG_ISDN_CAPI" != "n" ]; then bool ' AVM B1 ISA support' CONFIG_ISDN_DRV_AVMB1_B1ISA bool ' AVM B1 PCI support' CONFIG_ISDN_DRV_AVMB1_B1PCI if [ "$CONFIG_ISDN_DRV_AVMB1_B1PCI" != "n" ]; then diff --git a/drivers/isdn/Makefile b/drivers/isdn/Makefile index 04be19f9cd69..2414eddd2ebc 100644 --- a/drivers/isdn/Makefile +++ b/drivers/isdn/Makefile @@ -97,12 +97,12 @@ else endif endif -ifeq ($(CONFIG_ISDN_DRV_AVMB1),y) +ifeq ($(CONFIG_ISDN_CAPI),y) L_OBJS += avmb1/avmb1.o SUB_DIRS += avmb1 MOD_SUB_DIRS += avmb1 else - ifeq ($(CONFIG_ISDN_DRV_AVMB1),m) + ifeq ($(CONFIG_ISDN_CAPI),m) MOD_SUB_DIRS += avmb1 endif endif diff --git a/drivers/isdn/act2000/Makefile b/drivers/isdn/act2000/Makefile index 0e3c4a7e18f7..31312e8f6df1 100644 --- a/drivers/isdn/act2000/Makefile +++ b/drivers/isdn/act2000/Makefile @@ -7,7 +7,7 @@ ifeq ($(CONFIG_ISDN_DRV_ACT2000),y) O_TARGET += act2000.o else ifeq ($(CONFIG_ISDN_DRV_ACT2000),m) - O_TARGET += act2000.o + O_TARGET += act2000.o M_OBJS = act2000.o endif endif diff --git a/drivers/isdn/act2000/act2000.h b/drivers/isdn/act2000/act2000.h index 5d35a12ecd3a..432f433cb396 100644 --- a/drivers/isdn/act2000/act2000.h +++ b/drivers/isdn/act2000/act2000.h @@ -1,4 +1,4 @@ -/* $Id: act2000.h,v 1.7 1999/04/12 13:13:54 fritz Exp $ +/* $Id: act2000.h,v 1.8 2000/11/12 16:32:06 kai Exp $ * * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000. * @@ -19,31 +19,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * $Log: act2000.h,v $ - * Revision 1.7 1999/04/12 13:13:54 fritz - * Made cards pointer static to avoid name-clash. - * - * Revision 1.6 1998/11/05 22:12:38 fritz - * Changed mail-address. - * - * Revision 1.5 1997/10/09 22:22:59 fritz - * New HL<->LL interface: - * New BSENT callback with nr. of bytes included. - * Sending without ACK. - * - * Revision 1.4 1997/09/25 17:25:37 fritz - * Support for adding cards at runtime. - * Support for new Firmware. - * - * Revision 1.3 1997/09/24 23:11:43 fritz - * Optimized IRQ load and polling-mode. - * - * Revision 1.2 1997/09/24 19:44:12 fritz - * Added MSN mapping support, some cleanup. - * - * Revision 1.1 1997/09/23 18:00:05 fritz - * New driver for IBM Active 2000. - * */ #ifndef act2000_h diff --git a/drivers/isdn/act2000/act2000_isa.c b/drivers/isdn/act2000/act2000_isa.c index 1be45734f8e2..216269dcae72 100644 --- a/drivers/isdn/act2000/act2000_isa.c +++ b/drivers/isdn/act2000/act2000_isa.c @@ -1,4 +1,4 @@ -/* $Id: act2000_isa.c,v 1.10 1999/10/24 18:46:05 fritz Exp $ +/* $Id: act2000_isa.c,v 1.11 2000/11/12 16:32:06 kai Exp $ * * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version). * @@ -19,43 +19,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * $Log: act2000_isa.c,v $ - * Revision 1.10 1999/10/24 18:46:05 fritz - * Changed isa_ prefix to act2000_isa_ to prevent name-clash in latest - * kernels. - * - * Revision 1.9 1999/09/04 06:20:04 keil - * Changes from kernel set_current_state() - * - * Revision 1.8 1999/01/05 18:29:25 he - * merged remaining schedule_timeout() changes from 2.1.127 - * - * Revision 1.7 1998/11/05 22:12:41 fritz - * Changed mail-address. - * - * Revision 1.6 1998/06/17 19:51:09 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.5 1998/02/12 23:06:47 keil - * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() - * - * Revision 1.4 1997/10/09 22:23:00 fritz - * New HL<->LL interface: - * New BSENT callback with nr. of bytes included. - * Sending without ACK. - * - * Revision 1.3 1997/09/25 17:25:38 fritz - * Support for adding cards at runtime. - * Support for new Firmware. - * - * Revision 1.2 1997/09/24 23:11:44 fritz - * Optimized IRQ load and polling-mode. - * - * Revision 1.1 1997/09/23 18:00:05 fritz - * New driver for IBM Active 2000. - * */ #define __NO_VERSION__ @@ -78,7 +41,7 @@ static void act2000_isa_delay(long t) { sti(); - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(t); sti(); } diff --git a/drivers/isdn/act2000/act2000_isa.h b/drivers/isdn/act2000/act2000_isa.h index ff3e5419aeb7..0b705d33fe59 100644 --- a/drivers/isdn/act2000/act2000_isa.h +++ b/drivers/isdn/act2000/act2000_isa.h @@ -1,4 +1,4 @@ -/* $Id: act2000_isa.h,v 1.3 1999/10/24 18:46:05 fritz Exp $ +/* $Id: act2000_isa.h,v 1.4 2000/11/12 16:32:06 kai Exp $ * * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version). * @@ -19,17 +19,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * $Log: act2000_isa.h,v $ - * Revision 1.3 1999/10/24 18:46:05 fritz - * Changed isa_ prefix to act2000_isa_ to prevent name-clash in latest - * kernels. - * - * Revision 1.2 1998/11/05 22:12:43 fritz - * Changed mail-address. - * - * Revision 1.1 1997/09/23 18:00:07 fritz - * New driver for IBM Active 2000. - * */ #ifndef act2000_isa_h diff --git a/drivers/isdn/act2000/capi.c b/drivers/isdn/act2000/capi.c index 9502b314e1c3..ad2471a171b7 100644 --- a/drivers/isdn/act2000/capi.c +++ b/drivers/isdn/act2000/capi.c @@ -1,4 +1,4 @@ -/* $Id: capi.c,v 1.8 1998/11/05 22:12:46 fritz Exp $ +/* $Id: capi.c,v 1.9 2000/11/12 16:32:06 kai Exp $ * * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000. * CAPI encoder/decoder @@ -20,34 +20,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * $Log: capi.c,v $ - * Revision 1.8 1998/11/05 22:12:46 fritz - * Changed mail-address. - * - * Revision 1.7 1998/02/23 23:35:41 fritz - * Eliminated some compiler warnings. - * - * Revision 1.6 1998/02/12 23:06:50 keil - * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() - * - * Revision 1.5 1997/10/09 22:23:02 fritz - * New HL<->LL interface: - * New BSENT callback with nr. of bytes included. - * Sending without ACK. - * - * Revision 1.4 1997/09/25 17:25:39 fritz - * Support for adding cards at runtime. - * Support for new Firmware. - * - * Revision 1.3 1997/09/24 19:44:14 fritz - * Added MSN mapping support, some cleanup. - * - * Revision 1.2 1997/09/23 19:41:24 fritz - * Disabled Logging of DATA_B3_IND/RESP/REQ/CONF Messages. - * - * Revision 1.1 1997/09/23 18:00:08 fritz - * New driver for IBM Active 2000. - * */ #define __NO_VERSION__ diff --git a/drivers/isdn/act2000/capi.h b/drivers/isdn/act2000/capi.h index 0500550db90f..88e9b1aabf24 100644 --- a/drivers/isdn/act2000/capi.h +++ b/drivers/isdn/act2000/capi.h @@ -1,4 +1,4 @@ -/* $Id: capi.h,v 1.5 1998/11/05 22:12:48 fritz Exp $ +/* $Id: capi.h,v 1.6 2000/11/12 16:32:06 kai Exp $ * * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000. * @@ -19,25 +19,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * $Log: capi.h,v $ - * Revision 1.5 1998/11/05 22:12:48 fritz - * Changed mail-address. - * - * Revision 1.4 1997/10/01 09:21:04 fritz - * Removed old compatibility stuff for 2.0.X kernels. - * From now on, this code is for 2.1.X ONLY! - * Old stuff is still in the separate branch. - * - * Revision 1.3 1997/09/25 17:25:41 fritz - * Support for adding cards at runtime. - * Support for new Firmware. - * - * Revision 1.2 1997/09/24 19:44:15 fritz - * Added MSN mapping support, some cleanup. - * - * Revision 1.1 1997/09/23 18:00:10 fritz - * New driver for IBM Active 2000. - * */ #ifndef CAPI_H diff --git a/drivers/isdn/act2000/module.c b/drivers/isdn/act2000/module.c index 158d9463ed78..c602d945f27e 100644 --- a/drivers/isdn/act2000/module.c +++ b/drivers/isdn/act2000/module.c @@ -1,4 +1,4 @@ -/* $Id: module.c,v 1.11 1999/10/30 09:48:04 keil Exp $ +/* $Id: module.c,v 1.14 2000/11/12 16:32:06 kai Exp $ * * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000. * @@ -19,44 +19,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * $Log: module.c,v $ - * Revision 1.11 1999/10/30 09:48:04 keil - * miss one prefix act2000 - * - * Revision 1.10 1999/10/24 18:46:05 fritz - * Changed isa_ prefix to act2000_isa_ to prevent name-clash in latest - * kernels. - * - * Revision 1.9 1999/04/12 13:13:56 fritz - * Made cards pointer static to avoid name-clash. - * - * Revision 1.8 1998/11/05 22:12:51 fritz - * Changed mail-address. - * - * Revision 1.7 1998/02/12 23:06:52 keil - * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() - * - * Revision 1.6 1998/01/31 22:10:42 keil - * changes for 2.1.82 - * - * Revision 1.5 1997/10/09 22:23:04 fritz - * New HL<->LL interface: - * New BSENT callback with nr. of bytes included. - * Sending without ACK. - * - * Revision 1.4 1997/09/25 17:25:43 fritz - * Support for adding cards at runtime. - * Support for new Firmware. - * - * Revision 1.3 1997/09/24 23:11:45 fritz - * Optimized IRQ load and polling-mode. - * - * Revision 1.2 1997/09/24 19:44:17 fritz - * Added MSN mapping support, some cleanup. - * - * Revision 1.1 1997/09/23 18:00:13 fritz - * New driver for IBM Active 2000. - * */ #include "act2000.h" @@ -563,37 +525,6 @@ act2000_readstatus(u_char * buf, int len, int user, act2000_card * card) return count; } -static void -act2000_putmsg(act2000_card *card, char c) -{ - ulong flags; - - save_flags(flags); - cli(); - *card->status_buf_write++ = c; - if (card->status_buf_write == card->status_buf_read) { - if (++card->status_buf_read > card->status_buf_end) - card->status_buf_read = card->status_buf; - } - if (card->status_buf_write > card->status_buf_end) - card->status_buf_write = card->status_buf; - restore_flags(flags); -} - -static void -act2000_logstat(struct act2000_card *card, char *str) -{ - char *p = str; - isdn_ctrl c; - - while (*p) - act2000_putmsg(card, *p++); - c.command = ISDN_STAT_STAVAIL; - c.driver = card->myid; - c.arg = strlen(str); - card->interface.statcallb(&c); -} - /* * Find card with given driverId */ diff --git a/drivers/isdn/avmb1/Makefile b/drivers/isdn/avmb1/Makefile index bfeb81939a0b..74eba296bb7d 100644 --- a/drivers/isdn/avmb1/Makefile +++ b/drivers/isdn/avmb1/Makefile @@ -1,5 +1,5 @@ # -# $Id: Makefile,v 1.8 2000/01/25 14:33:38 calle Exp $ +# $Id: Makefile,v 1.18 2000/04/03 16:39:25 calle Exp $ # # Makefile for the CAPI and AVM-B1 device drivers. # @@ -11,6 +11,55 @@ # parent makes.. # # $Log: Makefile,v $ +# Revision 1.18 2000/04/03 16:39:25 calle +# Makefile checked in with future things :-( +# +# Revision 1.17 2000/04/03 16:38:05 calle +# made suppress_pollack static. +# +# Revision 1.16 2000/03/17 12:15:44 calle +# ALL_SUB_DIRS were wrong. +# +# Revision 1.15 2000/03/16 15:21:03 calle +# Bugfix in c4_remove: loop 5 times instead of 4 :-( +# +# Revision 1.14 2000/03/13 17:50:55 calle +# Added avm_cs.c for 2.3.x PCMCIA support. +# +# Revision 1.13 2000/03/08 17:06:33 calle +# - changes for devfs and 2.3.49 +# - capifs now configurable (no need with devfs) +# - New Middleware ioctl CAPI_NCCI_GETUNIT +# - Middleware again tested with 2.2.14 and 2.3.49 (with and without devfs) +# +# Revision 1.12 2000/03/06 18:00:23 calle +# - Middleware extention now working with 2.3.49 (capifs). +# - Fixed typos in debug section of capi.c +# - Bugfix: Makefile corrected for b1pcmcia.c +# +# Revision 1.11 2000/03/06 09:17:07 calle +# - capifs: fileoperations now in inode (change for 2.3.49) +# - Config.in: Middleware extention not a tristate, uups. +# +# Revision 1.10 2000/03/03 16:48:38 calle +# - Added CAPI2.0 Middleware support (CONFIG_ISDN_CAPI) +# It is now possible to create a connection with a CAPI2.0 applikation +# and than to handle the data connection from /dev/capi/ (capifs) and also +# using async or sync PPP on this connection. +# The two major device number 190 and 191 are not confirmed yet, +# but I want to save the code in cvs, before I go on. +# +# Revision 1.9 2000/03/03 15:50:42 calle +# - kernel CAPI: +# - Changed parameter "param" in capi_signal from __u32 to void *. +# - rewrote notifier handling in kcapi.c +# - new notifier NCCI_UP and NCCI_DOWN +# - User CAPI: +# - /dev/capi20 is now a cloning device. +# - middleware extentions prepared. +# - capidrv.c +# - locking of list operations and module count updates. +# # Revision 1.8 2000/01/25 14:33:38 calle # - Added Support AVM B1 PCI V4.0 (tested with prototype) # - splitted up t1pci.c into b1dma.c for common function with b1pciv4 @@ -64,6 +113,9 @@ # # +SUB_DIRS := +MOD_SUB_DIRS := +ALL_SUB_DIRS := # # Objects that don't export a symtab # @@ -82,53 +134,53 @@ MX_OBJS := # used as module O_TARGET := # used for .o targets (from O and OX objects) L_TARGET := # used for .a targets (from L and LX objects) -ifeq ($(CONFIG_ISDN_DRV_AVMB1),y) +ifeq ($(CONFIG_ISDN_CAPI),y) O_TARGET += avmb1.o OX_OBJS += kcapi.o O_OBJS += capi.o + ifdef CONFIG_ISDN_CAPIFS + OX_OBJS += capifs.o + endif ifdef CONFIG_ISDN_DRV_AVMB1_B1ISA - O_OBJS += b1isa.o + O_OBJS += b1isa.o endif ifdef CONFIG_ISDN_DRV_AVMB1_B1PCI - O_OBJS += b1pci.o + 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 + O_OBJS += t1isa.o endif ifdef CONFIG_ISDN_DRV_AVMB1_T1PCI - O_OBJS += t1pci.o + O_OBJS += t1pci.o endif ifdef CONFIG_ISDN_DRV_AVMB1_C4 - O_OBJS += c4.o + O_OBJS += c4.o endif OX_OBJS += capiutil.o capidrv.o b1.o b1dma.o else - ifeq ($(CONFIG_ISDN_DRV_AVMB1),m) - O_TARGET += kernelcapi.o - OX_OBJS += kcapi.o - M_OBJS += capi.o kernelcapi.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 - ifdef CONFIG_ISDN_DRV_AVMB1_B1PCMCIA - MX_OBJS += b1pcmcia.o - endif - ifdef CONFIG_ISDN_DRV_AVMB1_T1PCI - M_OBJS += t1pci.o - endif - ifdef CONFIG_ISDN_DRV_AVMB1_C4 - M_OBJS += c4.o - endif - MX_OBJS += capiutil.o capidrv.o b1.o b1dma.o + ifeq ($(CONFIG_ISDN_CAPI),m) + O_TARGET += kernelcapi.o + OX_OBJS += kcapi.o + M_OBJS += capi.o kernelcapi.o + ifdef CONFIG_ISDN_CAPIFS + MX_OBJS += capifs.o + endif + 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 + ifdef CONFIG_ISDN_DRV_AVMB1_T1PCI + M_OBJS += t1pci.o + endif + ifdef CONFIG_ISDN_DRV_AVMB1_C4 + M_OBJS += c4.o + endif + MX_OBJS += capiutil.o capidrv.o b1.o b1dma.o endif endif diff --git a/drivers/isdn/avmb1/avm_cs.c b/drivers/isdn/avmb1/avm_cs.c new file mode 100644 index 000000000000..4b8c9a1b487b --- /dev/null +++ b/drivers/isdn/avmb1/avm_cs.c @@ -0,0 +1,528 @@ +/*====================================================================== + + A PCMCIA client driver for AVM B1/M1/M2 + + Written by Carsten Paeth, calle@calle.in-berlin.de + +======================================================================*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/*====================================================================*/ + +/* Parameters that can be set with 'insmod' */ + +/* This means pick from 15, 12, 11, 10, 9, 7, 5, 4, and 3 */ +static int default_irq_list[10] = { 15, 12, 11, 10, 9, 7, 5, 4, 3, -1 }; +static int irq_list[10] = { -1 }; + +MODULE_PARM(irq_list, "1-10i"); + +/*====================================================================*/ + +/* + The event() function is this driver's Card Services event handler. + It will be called by Card Services when an appropriate card status + event is received. The config() and release() entry points are + used to configure or release a socket, in response to card insertion + and ejection events. They are invoked from the skeleton event + handler. +*/ + +static void avmcs_config(dev_link_t *link); +static void avmcs_release(u_long arg); +static int avmcs_event(event_t event, int priority, + event_callback_args_t *args); + +/* + The attach() and detach() entry points are used to create and destroy + "instances" of the driver, where each instance represents everything + needed to manage one actual PCMCIA card. +*/ + +static dev_link_t *avmcs_attach(void); +static void avmcs_detach(dev_link_t *); + +/* + The dev_info variable is the "key" that is used to match up this + device driver with appropriate cards, through the card configuration + database. +*/ + +static dev_info_t dev_info = "avm_cs"; + +/* + A linked list of "instances" of the skeleton device. Each actual + PCMCIA card corresponds to one device instance, and is described + by one dev_link_t structure (defined in ds.h). + + You may not want to use a linked list for this -- for example, the + memory card driver uses an array of dev_link_t pointers, where minor + device numbers are used to derive the corresponding array index. +*/ + +static dev_link_t *dev_list = NULL; + +/* + A dev_link_t structure has fields for most things that are needed + to keep track of a socket, but there will usually be some device + specific information that also needs to be kept track of. The + 'priv' pointer in a dev_link_t structure can be used to point to + a device-specific private data structure, like this. + + A driver needs to provide a dev_node_t structure for each device + on a card. In some cases, there is only one device per card (for + example, ethernet cards, modems). In other cases, there may be + many actual or logical devices (SCSI adapters, memory cards with + multiple partitions). The dev_node_t structures need to be kept + in a linked list starting at the 'dev' field of a dev_link_t + structure. We allocate them in the card's private data structure, + because they generally can't be allocated dynamically. +*/ + +typedef struct local_info_t { + dev_node_t node; +} local_info_t; + +/*====================================================================*/ + +static void cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +} + +/*====================================================================== + + avmcs_attach() creates an "instance" of the driver, allocating + local data structures for one device. The device is registered + with Card Services. + + The dev_link structure is initialized, but we don't actually + configure the card at this point -- we wait until we receive a + card insertion event. + +======================================================================*/ + +static dev_link_t *avmcs_attach(void) +{ + client_reg_t client_reg; + dev_link_t *link; + local_info_t *local; + int ret, i; + + /* Initialize the dev_link_t structure */ + link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL); + memset(link, 0, sizeof(struct dev_link_t)); + link->release.function = &avmcs_release; + link->release.data = (u_long)link; + + /* The io structure describes IO port mapping */ + link->io.NumPorts1 = 16; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + link->io.NumPorts2 = 16; + link->io.Attributes2 = IO_DATA_PATH_WIDTH_16; + link->io.IOAddrLines = 5; + + /* Interrupt setup */ + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED; + + link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID; + if (irq_list[0] != -1) { + for (i = 0; i < 10 && irq_list[i] > 0; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + } else { + for (i = 0; i < 10 && default_irq_list[i] > 0; i++) + link->irq.IRQInfo2 |= 1 << default_irq_list[i]; + } + + /* General socket configuration */ + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.Vcc = 50; + link->conf.IntType = INT_MEMORY_AND_IO; + link->conf.ConfigIndex = 1; + link->conf.Present = PRESENT_OPTION; + + /* Allocate space for private device-specific data */ + local = kmalloc(sizeof(local_info_t), GFP_KERNEL); + memset(local, 0, sizeof(local_info_t)); + link->priv = local; + + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.EventMask = + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &avmcs_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + ret = CardServices(RegisterClient, &link->handle, &client_reg); + if (ret != 0) { + cs_error(link->handle, RegisterClient, ret); + avmcs_detach(link); + return NULL; + } + + return link; +} /* avmcs_attach */ + +/*====================================================================== + + This deletes a driver "instance". The device is de-registered + with Card Services. If it has been released, all local data + structures are freed. Otherwise, the structures will be freed + when the device is released. + +======================================================================*/ + +static void avmcs_detach(dev_link_t *link) +{ + dev_link_t **linkp; + + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) break; + if (*linkp == NULL) + return; + + /* + If the device is currently configured and active, we won't + actually delete it yet. Instead, it is marked so that when + the release() function is called, that will trigger a proper + detach(). + */ + if (link->state & DEV_CONFIG) { + link->state |= DEV_STALE_LINK; + return; + } + + /* Break the link with Card Services */ + if (link->handle) + CardServices(DeregisterClient, link->handle); + + /* Unlink device structure, free pieces */ + *linkp = link->next; + if (link->priv) { + kfree_s(link->priv, sizeof(local_info_t)); + } + kfree_s(link, sizeof(struct dev_link_t)); + +} /* avmcs_detach */ + +/*====================================================================== + + avmcs_config() is scheduled to run after a CARD_INSERTION event + is received, to configure the PCMCIA socket, and to make the + ethernet device available to the system. + +======================================================================*/ + +static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple, + cisparse_t *parse) +{ + int i; + i = CardServices(fn, handle, tuple); + if (i != CS_SUCCESS) return i; + i = CardServices(GetTupleData, handle, tuple); + if (i != CS_SUCCESS) return i; + return CardServices(ParseTuple, handle, tuple, parse); +} + +#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c) +#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c) + +static void avmcs_config(dev_link_t *link) +{ + client_handle_t handle; + tuple_t tuple; + cisparse_t parse; + cistpl_cftable_entry_t *cf = &parse.cftable_entry; + local_info_t *dev; + int i; + u_char buf[64]; + char devname[128]; + int cardtype; + int (*addcard)(unsigned int port, unsigned irq); + + handle = link->handle; + dev = link->priv; + + /* + This reads the card's CONFIG tuple to find its configuration + registers. + */ + do { + tuple.DesiredTuple = CISTPL_CONFIG; + i = CardServices(GetFirstTuple, handle, &tuple); + if (i != CS_SUCCESS) break; + tuple.TupleData = buf; + tuple.TupleDataMax = 64; + tuple.TupleOffset = 0; + i = CardServices(GetTupleData, handle, &tuple); + if (i != CS_SUCCESS) break; + i = CardServices(ParseTuple, handle, &tuple, &parse); + if (i != CS_SUCCESS) break; + link->conf.ConfigBase = parse.config.base; + } while (0); + if (i != CS_SUCCESS) { + cs_error(link->handle, ParseTuple, i); + link->state &= ~DEV_CONFIG_PENDING; + return; + } + + /* Configure card */ + link->state |= DEV_CONFIG; + + do { + + tuple.Attributes = 0; + tuple.TupleData = buf; + tuple.TupleDataMax = 254; + tuple.TupleOffset = 0; + tuple.DesiredTuple = CISTPL_VERS_1; + + devname[0] = 0; + if( !first_tuple(handle, &tuple, &parse) && parse.version_1.ns > 1 ) { + strncpy(devname,parse.version_1.str + parse.version_1.ofs[1], + sizeof(devname)); + } + /* + * find IO port + */ + tuple.TupleData = (cisdata_t *)buf; + tuple.TupleOffset = 0; tuple.TupleDataMax = 255; + tuple.Attributes = 0; + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + i = first_tuple(handle, &tuple, &parse); + while (i == CS_SUCCESS) { + if (cf->io.nwin > 0) { + link->conf.ConfigIndex = cf->index; + link->io.BasePort1 = cf->io.win[0].base; + link->io.NumPorts1 = cf->io.win[0].len; + printk(KERN_INFO "avm_cs: testing i/o %#x-%#x\n", + link->io.BasePort1, + link->io.BasePort1+link->io.NumPorts1); + i = CardServices(RequestIO, link->handle, &link->io); + if (i == CS_SUCCESS) goto found_port; + } + i = next_tuple(handle, &tuple, &parse); + } + +found_port: + if (i != CS_SUCCESS) { + cs_error(link->handle, RequestIO, i); + break; + } + + /* + * allocate an interrupt line + */ + i = CardServices(RequestIRQ, link->handle, &link->irq); + if (i != CS_SUCCESS) { + cs_error(link->handle, RequestIRQ, i); + CardServices(ReleaseIO, link->handle, &link->io); + break; + } + + /* + * configure the PCMCIA socket + */ + i = CardServices(RequestConfiguration, link->handle, &link->conf); + if (i != CS_SUCCESS) { + cs_error(link->handle, RequestConfiguration, i); + CardServices(ReleaseIO, link->handle, &link->io); + CardServices(ReleaseIRQ, link->handle, &link->irq); + break; + } + + } while (0); + + /* At this point, the dev_node_t structure(s) should be + initialized and arranged in a linked list at link->dev. */ + + if (devname[0]) { + char *s = strrchr(devname, ' '); + if (!s) + s = devname; + else s++; + strcpy(dev->node.dev_name, s); + if (strcmp("M1", s) == 0) { + cardtype = AVM_CARDTYPE_M1; + } else if (strcmp("M2", s) == 0) { + cardtype = AVM_CARDTYPE_M2; + } else { + cardtype = AVM_CARDTYPE_B1; + } + } else { + strcpy(dev->node.dev_name, "b1"); + cardtype = AVM_CARDTYPE_B1; + } + + dev->node.major = 64; + dev->node.minor = 0; + link->dev = &dev->node; + + link->state &= ~DEV_CONFIG_PENDING; + /* If any step failed, release any partially configured state */ + if (i != 0) { + avmcs_release((u_long)link); + return; + } + + + switch (cardtype) { + case AVM_CARDTYPE_M1: addcard = b1pcmcia_addcard_m1; break; + case AVM_CARDTYPE_M2: addcard = b1pcmcia_addcard_m2; break; + default: + case AVM_CARDTYPE_B1: addcard = b1pcmcia_addcard_b1; break; + } + if ((i = (*addcard)(link->io.BasePort1, link->irq.AssignedIRQ)) < 0) { + printk(KERN_ERR "avm_cs: failed to add AVM-%s-Controller at i/o %#x, irq %d\n", + dev->node.dev_name, link->io.BasePort1, link->irq.AssignedIRQ); + avmcs_release((u_long)link); + return; + } + dev->node.minor = i; + +} /* avmcs_config */ + +/*====================================================================== + + After a card is removed, avmcs_release() will unregister the net + device, and release the PCMCIA configuration. If the device is + still open, this will be postponed until it is closed. + +======================================================================*/ + +static void avmcs_release(u_long arg) +{ + dev_link_t *link = (dev_link_t *)arg; + + /* + If the device is currently in use, we won't release until it + is actually closed. + */ + if (link->open) { + link->state |= DEV_STALE_CONFIG; + return; + } + + b1pcmcia_delcard(link->io.BasePort1, link->irq.AssignedIRQ); + + /* Unlink the device chain */ + link->dev = NULL; + + /* Don't bother checking to see if these succeed or not */ + CardServices(ReleaseConfiguration, link->handle); + CardServices(ReleaseIO, link->handle, &link->io); + CardServices(ReleaseIRQ, link->handle, &link->irq); + link->state &= ~DEV_CONFIG; + + if (link->state & DEV_STALE_LINK) + avmcs_detach(link); + +} /* avmcs_release */ + +/*====================================================================== + + The card status event handler. Mostly, this schedules other + stuff to run after an event is received. A CARD_REMOVAL event + also sets some flags to discourage the net drivers from trying + to talk to the card any more. + + When a CARD_REMOVAL event is received, we immediately set a flag + to block future accesses to this device. All the functions that + actually access the device should check this flag to make sure + the card is still present. + +======================================================================*/ + +static int avmcs_event(event_t event, int priority, + event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) { + link->release.expires = jiffies + (HZ/20); + add_timer(&link->release); + } + break; + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + avmcs_config(link); + break; + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + if (link->state & DEV_CONFIG) + CardServices(ReleaseConfiguration, link->handle); + break; + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if (link->state & DEV_CONFIG) + CardServices(RequestConfiguration, link->handle, &link->conf); + break; + } + return 0; +} /* avmcs_event */ + +/*====================================================================*/ + +int init_module(void) +{ + servinfo_t serv; + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_NOTICE "avm_cs: Card Services release " + "does not match!\n"); + return -1; + } + register_pccard_driver(&dev_info, &avmcs_attach, &avmcs_detach); + return 0; +} + +void cleanup_module(void) +{ + unregister_pccard_driver(&dev_info); + while (dev_list != NULL) { + if (dev_list->state & DEV_CONFIG) + avmcs_release((u_long)dev_list); + avmcs_detach(dev_list); + } +} diff --git a/drivers/isdn/avmb1/b1.c b/drivers/isdn/avmb1/b1.c index e7b0898f3ecf..4fa739255833 100644 --- a/drivers/isdn/avmb1/b1.c +++ b/drivers/isdn/avmb1/b1.c @@ -1,11 +1,14 @@ /* - * $Id: b1.c,v 1.13 2000/01/25 14:33:38 calle Exp $ + * $Id: b1.c,v 1.14 2000/06/19 16:51:53 keil Exp $ * * Common module for AVM B1 cards. * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: b1.c,v $ + * Revision 1.14 2000/06/19 16:51:53 keil + * don't free skb in irq context + * * Revision 1.13 2000/01/25 14:33:38 calle * - Added Support AVM B1 PCI V4.0 (tested with prototype) * - splitted up t1pci.c into b1dma.c for common function with b1pciv4 @@ -91,7 +94,7 @@ #include "capicmd.h" #include "capiutil.h" -static char *revision = "$Revision: 1.13 $"; +static char *revision = "$Revision: 1.14 $"; /* ------------------------------------------------------------- */ diff --git a/drivers/isdn/avmb1/b1dma.c b/drivers/isdn/avmb1/b1dma.c index 8486124058d6..740153f8e869 100644 --- a/drivers/isdn/avmb1/b1dma.c +++ b/drivers/isdn/avmb1/b1dma.c @@ -1,11 +1,17 @@ /* - * $Id: b1dma.c,v 1.3 2000/02/26 01:00:53 keil Exp $ + * $Id: b1dma.c,v 1.5 2000/06/19 16:51:53 keil Exp $ * * Common module for AVM B1 cards that support dma with AMCC * * (c) Copyright 2000 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: b1dma.c,v $ + * Revision 1.5 2000/06/19 16:51:53 keil + * don't free skb in irq context + * + * Revision 1.4 2000/04/03 16:38:05 calle + * made suppress_pollack static. + * * Revision 1.3 2000/02/26 01:00:53 keil * changes from 2.3.47 * @@ -34,7 +40,7 @@ #include "capicmd.h" #include "capiutil.h" -static char *revision = "$Revision: 1.3 $"; +static char *revision = "$Revision: 1.5 $"; /* ------------------------------------------------------------- */ diff --git a/drivers/isdn/avmb1/b1isa.c b/drivers/isdn/avmb1/b1isa.c index 590e825b6d82..9c67a0a42342 100644 --- a/drivers/isdn/avmb1/b1isa.c +++ b/drivers/isdn/avmb1/b1isa.c @@ -1,11 +1,15 @@ /* - * $Id: b1isa.c,v 1.7 2000/02/02 18:36:03 calle Exp $ + * $Id: b1isa.c,v 1.8 2000/04/03 13:29:24 calle Exp $ * * Module for AVM B1 ISA-card. * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: b1isa.c,v $ + * Revision 1.8 2000/04/03 13:29:24 calle + * make Tim Waugh happy (module unload races in 2.3.99-pre3). + * no real problem there, but now it is much cleaner ... + * * Revision 1.7 2000/02/02 18:36:03 calle * - Modules are now locked while init_module is running * - fixed problem with memory mapping if address is not aligned @@ -69,7 +73,7 @@ #include "capilli.h" #include "avmcard.h" -static char *revision = "$Revision: 1.7 $"; +static char *revision = "$Revision: 1.8 $"; /* ------------------------------------------------------------- */ @@ -265,6 +269,9 @@ int b1isa_init(void) { struct capi_driver *driver = &b1isa_driver; char *p; + int retval = 0; + + MOD_INC_USE_COUNT; if ((p = strchr(revision, ':'))) { strncpy(driver->revision, p + 1, sizeof(driver->revision)); @@ -279,9 +286,10 @@ int b1isa_init(void) if (!di) { printk(KERN_ERR "%s: failed to attach capi_driver\n", driver->name); - return -EIO; + retval = -EIO; } - return 0; + MOD_DEC_USE_COUNT; + return retval; } #ifdef MODULE diff --git a/drivers/isdn/avmb1/b1pci.c b/drivers/isdn/avmb1/b1pci.c index c41fa2ad4707..a1b61af00eb9 100644 --- a/drivers/isdn/avmb1/b1pci.c +++ b/drivers/isdn/avmb1/b1pci.c @@ -1,11 +1,28 @@ /* - * $Id: b1pci.c,v 1.20 2000/02/02 18:36:03 calle Exp $ + * $Id: b1pci.c,v 1.25 2000/05/29 12:29:18 keil Exp $ * * Module for AVM B1 PCI-card. * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: b1pci.c,v $ + * Revision 1.25 2000/05/29 12:29:18 keil + * make pci_enable_dev compatible to 2.2 kernel versions + * + * Revision 1.24 2000/05/19 15:43:22 calle + * added calls to pci_device_start(). + * + * Revision 1.23 2000/05/06 00:52:36 kai + * merged changes from kernel tree + * fixed timer and net_device->name breakage + * + * Revision 1.22 2000/04/21 13:01:33 calle + * Revision in b1pciv4 driver was missing. + * + * Revision 1.21 2000/04/03 13:29:24 calle + * make Tim Waugh happy (module unload races in 2.3.99-pre3). + * no real problem there, but now it is much cleaner ... + * * Revision 1.20 2000/02/02 18:36:03 calle * - Modules are now locked while init_module is running * - fixed problem with memory mapping if address is not aligned @@ -70,12 +87,13 @@ #include #include #include +#include #include "capicmd.h" #include "capiutil.h" #include "capilli.h" #include "avmcard.h" -static char *revision = "$Revision: 1.20 $"; +static char *revision = "$Revision: 1.25 $"; /* ------------------------------------------------------------- */ @@ -469,6 +487,19 @@ static int add_card(struct pci_dev *dev) param.membase = dev->base_address[ 0] & PCI_BASE_ADDRESS_MEM_MASK; param.port = dev->base_address[ 2] & PCI_BASE_ADDRESS_IO_MASK; param.irq = dev->irq; + + retval = pci_enable_device (dev); + if (retval != 0) { + printk(KERN_ERR + "%s: failed to enable AVM-B1 V4 at i/o %#x, irq %d, mem %#x err=%d\n", + driver->name, param.port, param.irq, param.membase, retval); +#ifdef MODULE + cleanup_module(); +#endif + MOD_DEC_USE_COUNT; + return -EIO; + } + printk(KERN_INFO "%s: PCI BIOS reports AVM-B1 V4 at i/o %#x, irq %d, mem %#x\n", driver->name, param.port, param.irq, param.membase); @@ -486,6 +517,18 @@ static int add_card(struct pci_dev *dev) param.membase = 0; param.port = dev->base_address[ 1] & PCI_BASE_ADDRESS_IO_MASK; param.irq = dev->irq; + + retval = pci_enable_device (dev); + if (retval != 0) { + printk(KERN_ERR + "%s: failed to enable AVM-B1 at i/o %#x, irq %d, err=%d\n", + driver->name, param.port, param.irq, retval); +#ifdef MODULE + cleanup_module(); +#endif + MOD_DEC_USE_COUNT; + return -EIO; + } printk(KERN_INFO "%s: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n", driver->name, param.port, param.irq); @@ -509,10 +552,18 @@ int b1pci_init(void) char *p; int retval; + MOD_INC_USE_COUNT; + if ((p = strchr(revision, ':'))) { strncpy(driver->revision, p + 1, sizeof(driver->revision)); p = strchr(driver->revision, '$'); *p = 0; +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 + p = strchr(revision, ':'); + strncpy(driverv4->revision, p + 1, sizeof(driverv4->revision)); + p = strchr(driverv4->revision, '$'); + *p = 0; +#endif } printk(KERN_INFO "%s: revision %s\n", driver->name, driver->revision); @@ -522,6 +573,7 @@ int b1pci_init(void) if (!di) { printk(KERN_ERR "%s: failed to attach capi_driver\n", driver->name); + MOD_DEC_USE_COUNT; return -EIO; } @@ -534,6 +586,7 @@ int b1pci_init(void) detach_capi_driver(driver); printk(KERN_ERR "%s: failed to attach capi_driver\n", driverv4->name); + MOD_DEC_USE_COUNT; return -EIO; } #endif @@ -545,6 +598,7 @@ int b1pci_init(void) #ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 detach_capi_driver(driverv4); #endif + MOD_DEC_USE_COUNT; return -EIO; } @@ -554,6 +608,7 @@ int b1pci_init(void) #ifdef MODULE cleanup_module(); #endif + MOD_DEC_USE_COUNT; return retval; } ncards++; @@ -561,12 +616,15 @@ int b1pci_init(void) if (ncards) { printk(KERN_INFO "%s: %d B1-PCI card(s) detected\n", driver->name, ncards); + MOD_DEC_USE_COUNT; return 0; } printk(KERN_ERR "%s: NO B1-PCI card detected\n", driver->name); + MOD_DEC_USE_COUNT; return -ESRCH; #else printk(KERN_ERR "%s: kernel not compiled with PCI.\n", driver->name); + MOD_DEC_USE_COUNT; return -EIO; #endif } diff --git a/drivers/isdn/avmb1/b1pcmcia.c b/drivers/isdn/avmb1/b1pcmcia.c index 6e39c43d2b68..c3537c363cb1 100644 --- a/drivers/isdn/avmb1/b1pcmcia.c +++ b/drivers/isdn/avmb1/b1pcmcia.c @@ -1,11 +1,24 @@ /* - * $Id: b1pcmcia.c,v 1.7 2000/02/02 18:36:03 calle Exp $ + * $Id: b1pcmcia.c,v 1.10 2000/05/06 00:52:36 kai 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.10 2000/05/06 00:52:36 kai + * merged changes from kernel tree + * fixed timer and net_device->name breakage + * + * Revision 1.9 2000/04/03 13:29:24 calle + * make Tim Waugh happy (module unload races in 2.3.99-pre3). + * no real problem there, but now it is much cleaner ... + * + * Revision 1.8 2000/03/06 18:00:23 calle + * - Middleware extention now working with 2.3.49 (capifs). + * - Fixed typos in debug section of capi.c + * - Bugfix: Makefile corrected for b1pcmcia.c + * * Revision 1.7 2000/02/02 18:36:03 calle * - Modules are now locked while init_module is running * - fixed problem with memory mapping if address is not aligned @@ -70,7 +83,7 @@ #include "capilli.h" #include "avmcard.h" -static char *revision = "$Revision: 1.7 $"; +static char *revision = "$Revision: 1.10 $"; /* ------------------------------------------------------------- */ @@ -296,6 +309,9 @@ int b1pcmcia_init(void) { struct capi_driver *driver = &b1pcmcia_driver; char *p; + int retval = 0; + + MOD_INC_USE_COUNT; if ((p = strchr(revision, ':'))) { strncpy(driver->revision, p + 1, sizeof(driver->revision)); @@ -310,9 +326,10 @@ int b1pcmcia_init(void) if (!di) { printk(KERN_ERR "%s: failed to attach capi_driver\n", driver->name); - return -EIO; + retval = -EIO; } - return 0; + MOD_DEC_USE_COUNT; + return retval; } #ifdef MODULE diff --git a/drivers/isdn/avmb1/c4.c b/drivers/isdn/avmb1/c4.c index 65e1af3b0f7c..68163a141ef0 100644 --- a/drivers/isdn/avmb1/c4.c +++ b/drivers/isdn/avmb1/c4.c @@ -1,11 +1,34 @@ /* - * $Id: c4.c,v 1.5 2000/03/16 15:21:03 calle Exp $ + * $Id: c4.c,v 1.12 2000/06/19 16:51:53 keil Exp $ * * Module for AVM C4 card. * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: c4.c,v $ + * Revision 1.12 2000/06/19 16:51:53 keil + * don't free skb in irq context + * + * Revision 1.11 2000/06/19 15:11:24 keil + * avoid use of freed structs + * changes from 2.4.0-ac21 + * + * Revision 1.10 2000/05/29 12:29:18 keil + * make pci_enable_dev compatible to 2.2 kernel versions + * + * Revision 1.9 2000/05/19 15:43:22 calle + * added calls to pci_device_start(). + * + * Revision 1.8 2000/04/03 16:38:05 calle + * made suppress_pollack static. + * + * Revision 1.7 2000/04/03 13:29:24 calle + * make Tim Waugh happy (module unload races in 2.3.99-pre3). + * no real problem there, but now it is much cleaner ... + * + * Revision 1.6 2000/03/17 12:21:08 calle + * send patchvalues now working. + * * Revision 1.5 2000/03/16 15:21:03 calle * Bugfix in c4_remove: loop 5 times instead of 4 :-( * @@ -36,6 +59,7 @@ #include #include #include +#include #include #include #include "capicmd.h" @@ -43,7 +67,7 @@ #include "capilli.h" #include "avmcard.h" -static char *revision = "$Revision: 1.5 $"; +static char *revision = "$Revision: 1.12 $"; #undef CONFIG_C4_DEBUG #undef CONFIG_C4_POLLDEBUG @@ -774,45 +798,78 @@ static void c4_send_init(avmcard *card) c4_dispatch_tx(card); } -static int c4_send_config(avmcard *card, capiloaddatapart * config) +static int queue_sendconfigword(avmcard *card, __u32 val) { struct sk_buff *skb; - __u8 val[sizeof(__u32)]; void *p; - unsigned char *dp; - int left, retval; - - skb = alloc_skb(12 + ((config->len+3)/4)*5, GFP_ATOMIC); + + skb = alloc_skb(3+4, GFP_ATOMIC); if (!skb) { - printk(KERN_CRIT "%s: no memory, can't send config.\n", + printk(KERN_CRIT "%s: no memory, send config\n", card->name); - return -ENOMEM; + return -ENOMEM; } p = skb->data; _put_byte(&p, 0); _put_byte(&p, 0); _put_byte(&p, SEND_CONFIG); - _put_word(&p, 1); + _put_word(&p, val); + skb_put(skb, (__u8 *)p - (__u8 *)skb->data); + + skb_queue_tail(&card->dma->send_queue, skb); + c4_dispatch_tx(card); + return 0; +} + +static int queue_sendconfig(avmcard *card, char cval[4]) +{ + struct sk_buff *skb; + void *p; + + skb = alloc_skb(3+4, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, send config\n", + card->name); + return -ENOMEM; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); _put_byte(&p, SEND_CONFIG); - _put_word(&p, config->len); /* 12 */ + _put_byte(&p, cval[0]); + _put_byte(&p, cval[1]); + _put_byte(&p, cval[2]); + _put_byte(&p, cval[3]); + skb_put(skb, (__u8 *)p - (__u8 *)skb->data); + + skb_queue_tail(&card->dma->send_queue, skb); + c4_dispatch_tx(card); + return 0; +} + +static int c4_send_config(avmcard *card, capiloaddatapart * config) +{ + __u8 val[4]; + unsigned char *dp; + int left, retval; + + if ((retval = queue_sendconfigword(card, 1)) != 0) + return retval; + if ((retval = queue_sendconfigword(card, config->len)) != 0) + return retval; dp = config->data; left = config->len; while (left >= sizeof(__u32)) { if (config->user) { retval = copy_from_user(val, dp, sizeof(val)); - if (retval) { - dev_kfree_skb(skb); + if (retval) return -EFAULT; - } } else { memcpy(val, dp, sizeof(val)); } - _put_byte(&p, SEND_CONFIG); - _put_byte(&p, val[0]); - _put_byte(&p, val[1]); - _put_byte(&p, val[2]); - _put_byte(&p, val[3]); + if ((retval = queue_sendconfig(card, val)) != 0) + return retval; left -= sizeof(val); dp += sizeof(val); } @@ -820,25 +877,15 @@ static int c4_send_config(avmcard *card, capiloaddatapart * config) memset(val, 0, sizeof(val)); if (config->user) { retval = copy_from_user(&val, dp, left); - if (retval) { - dev_kfree_skb(skb); + if (retval) return -EFAULT; - } } else { memcpy(&val, dp, left); } - _put_byte(&p, SEND_CONFIG); - _put_byte(&p, val[0]); - _put_byte(&p, val[1]); - _put_byte(&p, val[2]); - _put_byte(&p, val[3]); + if ((retval = queue_sendconfig(card, val)) != 0) + return retval; } - skb_put(skb, (__u8 *)p - (__u8 *)skb->data); - - skb_queue_tail(&card->dma->send_queue, skb); - c4_dispatch_tx(card); - return 0; } @@ -875,8 +922,15 @@ static int c4_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) c4outmeml(card->mbase+DOORBELL, DBELL_UP_ARM); restore_flags(flags); - if (data->configuration.len > 0 && data->configuration.data) - c4_send_config(card, &data->configuration); + if (data->configuration.len > 0 && data->configuration.data) { + retval = c4_send_config(card, &data->configuration); + if (retval) { + printk(KERN_ERR "%s: failed to set config!!\n", + card->name); + c4_reset(card); + return retval; + } + } c4_send_init(card); @@ -910,8 +964,10 @@ static void c4_remove_ctr(struct capi_ctr *ctrl) for (i=0; i < 4; i++) { cinfo = &card->ctrlinfo[i]; - if (cinfo->capi_ctrl) + if (cinfo->capi_ctrl) { di->detach_ctr(cinfo->capi_ctrl); + cinfo->capi_ctrl = NULL; + } } free_irq(card->irq, card); @@ -1124,7 +1180,6 @@ static int c4_add_card(struct capi_driver *driver, struct capicardparams *p) cinfo = (avmctrl_info *) kmalloc(sizeof(avmctrl_info)*4, GFP_ATOMIC); if (!cinfo) { printk(KERN_WARNING "%s: no memory.\n", driver->name); - kfree(card->ctrlinfo); kfree(card->dma); kfree(card); MOD_DEC_USE_COUNT; @@ -1282,6 +1337,8 @@ int c4_init(void) char *p; int retval; + MOD_INC_USE_COUNT; + if ((p = strchr(revision, ':'))) { strncpy(driver->revision, p + 1, sizeof(driver->revision)); p = strchr(driver->revision, '$'); @@ -1295,6 +1352,7 @@ int c4_init(void) if (!di) { printk(KERN_ERR "%s: failed to attach capi_driver\n", driver->name); + MOD_DEC_USE_COUNT; return -EIO; } @@ -1302,6 +1360,7 @@ int c4_init(void) if (!pci_present()) { printk(KERN_ERR "%s: no PCI bus present\n", driver->name); detach_capi_driver(driver); + MOD_DEC_USE_COUNT; return -EIO; } @@ -1314,6 +1373,18 @@ int c4_init(void) param.irq = dev->irq; param.membase = dev->base_address[ 0] & PCI_BASE_ADDRESS_MEM_MASK; + retval = pci_enable_device (dev); + if (retval != 0) { + printk(KERN_ERR + "%s: failed to enable AVM-C4 at i/o %#x, irq %d, mem %#x err=%d\n", + driver->name, param.port, param.irq, param.membase, retval); +#ifdef MODULE + cleanup_module(); +#endif + MOD_DEC_USE_COUNT; + return -EIO; + } + printk(KERN_INFO "%s: PCI BIOS reports AVM-C4 at i/o %#x, irq %d, mem %#x\n", driver->name, param.port, param.irq, param.membase); @@ -1325,6 +1396,7 @@ int c4_init(void) #ifdef MODULE cleanup_module(); #endif + MOD_DEC_USE_COUNT; return retval; } ncards++; @@ -1332,12 +1404,15 @@ int c4_init(void) if (ncards) { printk(KERN_INFO "%s: %d C4 card(s) detected\n", driver->name, ncards); + MOD_DEC_USE_COUNT; return 0; } printk(KERN_ERR "%s: NO C4 card detected\n", driver->name); + MOD_DEC_USE_COUNT; return -ESRCH; #else printk(KERN_ERR "%s: kernel not compiled with PCI.\n", driver->name); + MOD_DEC_USE_COUNT; return -EIO; #endif } diff --git a/drivers/isdn/avmb1/capi.c b/drivers/isdn/avmb1/capi.c index 6fe377df6d17..108a64c88a6a 100644 --- a/drivers/isdn/avmb1/capi.c +++ b/drivers/isdn/avmb1/capi.c @@ -1,11 +1,68 @@ /* - * $Id: capi.c,v 1.23 2000/02/26 01:00:53 keil Exp $ + * $Id: capi.c,v 1.35 2000/06/19 15:11:24 keil Exp $ * * CAPI 2.0 Interface for Linux * * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capi.c,v $ + * Revision 1.35 2000/06/19 15:11:24 keil + * avoid use of freed structs + * changes from 2.4.0-ac21 + * + * Revision 1.34 2000/06/18 16:09:54 keil + * more changes for 2.4 + * + * Revision 1.33 2000/05/18 16:35:43 calle + * Uaaahh. Bad memory leak fixed. + * + * Revision 1.32 2000/04/21 12:38:42 calle + * Bugfix: error in proc_ functions, begin-off => off-begin + * + * Revision 1.31 2000/04/03 13:29:24 calle + * make Tim Waugh happy (module unload races in 2.3.99-pre3). + * no real problem there, but now it is much cleaner ... + * + * Revision 1.30 2000/03/19 12:31:36 calle + * PPP over CAPI raw driver disabled for now, ppp_generic has been changed. + * + * Revision 1.29 2000/03/13 17:48:13 calle + * removed unused variable. + * + * Revision 1.28 2000/03/08 17:06:33 calle + * - changes for devfs and 2.3.49 + * - capifs now configurable (no need with devfs) + * - New Middleware ioctl CAPI_NCCI_GETUNIT + * - Middleware again tested with 2.2.14 and 2.3.49 (with and without devfs) + * + * Revision 1.27 2000/03/06 18:00:23 calle + * - Middleware extention now working with 2.3.49 (capifs). + * - Fixed typos in debug section of capi.c + * - Bugfix: Makefile corrected for b1pcmcia.c + * + * Revision 1.26 2000/03/03 16:48:38 calle + * - Added CAPI2.0 Middleware support (CONFIG_ISDN_CAPI) + * It is now possible to create a connection with a CAPI2.0 applikation + * and than to handle the data connection from /dev/capi/ (capifs) and also + * using async or sync PPP on this connection. + * The two major device number 190 and 191 are not confirmed yet, + * but I want to save the code in cvs, before I go on. + * + * Revision 1.25 2000/03/03 16:37:11 kai + * incorporated some cosmetic changes from the official kernel tree back + * into CVS + * + * Revision 1.24 2000/03/03 15:50:42 calle + * - kernel CAPI: + * - Changed parameter "param" in capi_signal from __u32 to void *. + * - rewrote notifier handling in kcapi.c + * - new notifier NCCI_UP and NCCI_DOWN + * - User CAPI: + * - /dev/capi20 is now a cloning device. + * - middleware extentions prepared. + * - capidrv.c + * - locking of list operations and module count updates. + * * Revision 1.23 2000/02/26 01:00:53 keil * changes from 2.3.47 * @@ -126,84 +183,705 @@ #include #include #include +#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE +#include +#ifdef CONFIG_PPP +#include +#include +#include +#undef CAPI_PPP_ON_RAW_DEVICE +#endif /* CONFIG_PPP */ +#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ #include #include #include #include #include - #include "capiutil.h" #include "capicmd.h" -#include "capidev.h" +#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE +#include "capifs.h" +#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ + +static char *revision = "$Revision: 1.35 $"; MODULE_AUTHOR("Carsten Paeth (calle@calle.in-berlin.de)"); +#undef _DEBUG_REFCOUNT /* alloc/free and open/close debug */ +#undef _DEBUG_TTYFUNCS /* call to tty_driver */ +#undef _DEBUG_DATAFLOW /* data flow */ + /* -------- driver information -------------------------------------- */ int capi_major = 68; /* allocated */ +#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE +int capi_rawmajor = 190; +int capi_ttymajor = 191; +#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ MODULE_PARM(capi_major, "i"); +#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE +MODULE_PARM(capi_rawmajor, "i"); +MODULE_PARM(capi_ttymajor, "i"); +#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ + +/* -------- defines ------------------------------------------------- */ + +#define CAPINC_MAX_RECVQUEUE 10 +#define CAPINC_MAX_SENDQUEUE 10 +#define CAPI_MAX_BLKSIZE 2048 + +/* -------- data structures ----------------------------------------- */ + +struct capidev; +struct capincci; +#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE +struct capiminor; + +struct capiminor { + struct capiminor *next; + struct capincci *nccip; + unsigned int minor; + + __u16 applid; + __u32 ncci; + __u16 datahandle; + __u16 msgid; + + struct file *file; + struct tty_struct *tty; + int ttyinstop; + int ttyoutstop; + struct sk_buff *ttyskb; + atomic_t ttyopencount; + + struct sk_buff_head inqueue; + int inbytes; + struct sk_buff_head outqueue; + int outbytes; + + /* for raw device */ + struct sk_buff_head recvqueue; + struct wait_queue *recvwait; + struct wait_queue *sendwait; + + /* transmit path */ + struct datahandle_queue { + struct datahandle_queue *next; + __u16 datahandle; + } *ackqueue; + int nack; + +}; +#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ + +struct capincci { + struct capincci *next; + __u32 ncci; + struct capidev *cdev; +#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE + struct capiminor *minorp; +#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ +}; + +struct capidev { + struct capidev *next; + struct file *file; + __u16 applid; + __u16 errcode; + unsigned int minor; + unsigned userflags; + + struct sk_buff_head recvqueue; + struct wait_queue *recvwait; + + /* Statistic */ + unsigned long nrecvctlpkt; + unsigned long nrecvdatapkt; + unsigned long nsentctlpkt; + unsigned long nsentdatapkt; + + struct capincci *nccis; +}; /* -------- global variables ---------------------------------------- */ -static struct capidev capidevs[CAPI_MAXMINOR + 1]; -struct capi_interface *capifuncs; +static struct capi_interface *capifuncs = 0; +static struct capidev *capidev_openlist = 0; +#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE +static struct capiminor *minors = 0; +#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ -/* -------- function called by lower level -------------------------- */ -static void capi_signal(__u16 applid, __u32 minor) +#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE +/* -------- datahandles --------------------------------------------- */ + +int capincci_add_ack(struct capiminor *mp, __u16 datahandle) +{ + struct datahandle_queue *n, **pp; + + n = (struct datahandle_queue *) + kmalloc(sizeof(struct datahandle_queue), GFP_ATOMIC); + if (!n) { + printk(KERN_ERR "capi: alloc datahandle failed\n"); + return -1; + } + n->next = 0; + n->datahandle = datahandle; + for (pp = &mp->ackqueue; *pp; pp = &(*pp)->next) ; + *pp = n; + mp->nack++; + return 0; +} + +int capiminor_del_ack(struct capiminor *mp, __u16 datahandle) +{ + struct datahandle_queue **pp, *p; + + for (pp = &mp->ackqueue; *pp; pp = &(*pp)->next) { + if ((*pp)->datahandle == datahandle) { + p = *pp; + *pp = (*pp)->next; + kfree(p); + mp->nack--; + return 0; + } + } + return -1; +} + +void capiminor_del_all_ack(struct capiminor *mp) +{ + struct datahandle_queue **pp, *p; + + for (pp = &mp->ackqueue; *pp; pp = &(*pp)->next) { + p = *pp; + *pp = (*pp)->next; + kfree(p); + mp->nack--; + } +} + + +/* -------- struct capiminor ---------------------------------------- */ + +struct capiminor *capiminor_alloc(__u16 applid, __u32 ncci) +{ + struct capiminor *mp, **pp; + unsigned int minor = 0; + + MOD_INC_USE_COUNT; + mp = (struct capiminor *)kmalloc(sizeof(struct capiminor), GFP_ATOMIC); + if (!mp) { + MOD_DEC_USE_COUNT; + printk(KERN_ERR "capi: can't alloc capiminor\n"); + return 0; + } +#ifdef _DEBUG_REFCOUNT + printk(KERN_DEBUG "capiminor_alloc %d\n", GET_USE_COUNT(&__this_module)); +#endif + memset(mp, 0, sizeof(struct capiminor)); + mp->applid = applid; + mp->ncci = ncci; + mp->msgid = 0; + atomic_set(&mp->ttyopencount,0); + + skb_queue_head_init(&mp->inqueue); + skb_queue_head_init(&mp->outqueue); + + skb_queue_head_init(&mp->recvqueue); + + for (pp = &minors; *pp; pp = &(*pp)->next) { + if ((*pp)->minor < minor) + continue; + if ((*pp)->minor > minor) + break; + minor++; + } + mp->minor = minor; + mp->next = *pp; + *pp = mp; + return mp; +} + +void capiminor_free(struct capiminor *mp) +{ + struct capiminor **pp; + struct sk_buff *skb; + + pp = &minors; + while (*pp) { + if (*pp == mp) { + *pp = (*pp)->next; + if (mp->ttyskb) kfree_skb(mp->ttyskb); + mp->ttyskb = 0; + while ((skb = skb_dequeue(&mp->recvqueue)) != 0) + kfree_skb(skb); + while ((skb = skb_dequeue(&mp->inqueue)) != 0) + kfree_skb(skb); + while ((skb = skb_dequeue(&mp->outqueue)) != 0) + kfree_skb(skb); + capiminor_del_all_ack(mp); + kfree(mp); + MOD_DEC_USE_COUNT; +#ifdef _DEBUG_REFCOUNT + printk(KERN_DEBUG "capiminor_free %d\n", GET_USE_COUNT(&__this_module)); +#endif + return; + } else { + pp = &(*pp)->next; + } + } +} + +struct capiminor *capiminor_find(unsigned int minor) +{ + struct capiminor *p; + for (p = minors; p && p->minor != minor; p = p->next) + ; + return p; +} +#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ + +/* -------- struct capincci ----------------------------------------- */ + +static struct capincci *capincci_alloc(struct capidev *cdev, __u32 ncci) +{ + struct capincci *np, **pp; +#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE + struct capiminor *mp = 0; + kdev_t kdev; +#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ + + np = (struct capincci *)kmalloc(sizeof(struct capincci), GFP_ATOMIC); + if (!np) + return 0; + memset(np, 0, sizeof(struct capincci)); + np->ncci = ncci; + np->cdev = cdev; + for (pp=&cdev->nccis; *pp; pp = &(*pp)->next) + ; + *pp = np; +#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE + mp = 0; + if (cdev->userflags & CAPIFLAG_HIGHJACKING) + mp = np->minorp = capiminor_alloc(cdev->applid, ncci); + if (mp) { + mp->nccip = np; +#ifdef _DEBUG_REFCOUNT + printk(KERN_DEBUG "set mp->nccip\n"); +#endif +#ifdef CONFIG_ISDN_CAPIFS + kdev = MKDEV(capi_rawmajor, mp->minor); + capifs_new_ncci('r', mp->minor, kdev); + kdev = MKDEV(capi_ttymajor, mp->minor); + capifs_new_ncci(0, mp->minor, kdev); +#endif + } +#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ + return np; +} + +static void capincci_free(struct capidev *cdev, __u32 ncci) +{ + struct capincci *np, **pp; +#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE + struct capiminor *mp; +#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ + + pp=&cdev->nccis; + while (*pp) { + np = *pp; + if (ncci == 0xffffffff || np->ncci == ncci) { + *pp = (*pp)->next; +#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE + if ((mp = np->minorp) != 0) { +#ifdef CONFIG_ISDN_CAPIFS + capifs_free_ncci('r', mp->minor); + capifs_free_ncci(0, mp->minor); +#endif + if (mp->tty) { + mp->nccip = 0; +#ifdef _DEBUG_REFCOUNT + printk(KERN_DEBUG "reset mp->nccip\n"); +#endif + tty_hangup(mp->tty); + } else if (mp->file) { + mp->nccip = 0; +#ifdef _DEBUG_REFCOUNT + printk(KERN_DEBUG "reset mp->nccip\n"); +#endif + wake_up_interruptible(&mp->recvwait); + wake_up_interruptible(&mp->sendwait); + } else { + capiminor_free(mp); + } + } +#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ + kfree(np); + if (*pp == 0) return; + } else { + pp = &(*pp)->next; + } + } +} + +struct capincci *capincci_find(struct capidev *cdev, __u32 ncci) +{ + struct capincci *p; + + for (p=cdev->nccis; p ; p = p->next) { + if (p->ncci == ncci) + break; + } + return p; +} + +/* -------- struct capidev ------------------------------------------ */ + +static struct capidev *capidev_alloc(struct file *file) { struct capidev *cdev; + struct capidev **pp; + + cdev = (struct capidev *)kmalloc(sizeof(struct capidev), GFP_KERNEL); + if (!cdev) + return 0; + memset(cdev, 0, sizeof(struct capidev)); + cdev->file = file; + cdev->minor = MINOR(file->f_dentry->d_inode->i_rdev); + + skb_queue_head_init(&cdev->recvqueue); + pp=&capidev_openlist; + while (*pp) pp = &(*pp)->next; + *pp = cdev; + return cdev; +} + +static void capidev_free(struct capidev *cdev) +{ + struct capidev **pp; + struct sk_buff *skb; + + if (cdev->applid) + (*capifuncs->capi_release) (cdev->applid); + cdev->applid = 0; + + while ((skb = skb_dequeue(&cdev->recvqueue)) != 0) { + kfree_skb(skb); + } + + pp=&capidev_openlist; + while (*pp && *pp != cdev) pp = &(*pp)->next; + if (*pp) + *pp = cdev->next; + + kfree(cdev); +} + +static struct capidev *capidev_find(__u16 applid) +{ + struct capidev *p; + for (p=capidev_openlist; p; p = p->next) { + if (p->applid == applid) + break; + } + return p; +} + +#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE +/* -------- handle data queue --------------------------------------- */ + +struct sk_buff * +gen_data_b3_resp_for(struct capiminor *mp, struct sk_buff *skb) +{ + struct sk_buff *nskb; + nskb = alloc_skb(CAPI_DATA_B3_RESP_LEN, GFP_ATOMIC); + if (nskb) { + __u16 datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4+4+2); + unsigned char *s = skb_put(nskb, CAPI_DATA_B3_RESP_LEN); + capimsg_setu16(s, 0, CAPI_DATA_B3_RESP_LEN); + capimsg_setu16(s, 2, mp->applid); + capimsg_setu8 (s, 4, CAPI_DATA_B3); + capimsg_setu8 (s, 5, CAPI_RESP); + capimsg_setu16(s, 6, mp->msgid++); + capimsg_setu32(s, 8, mp->ncci); + capimsg_setu16(s, 12, datahandle); + } + return nskb; +} + +int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb) +{ + struct sk_buff *nskb; + unsigned int datalen; + __u16 errcode, datahandle; + + datalen = skb->len - CAPIMSG_LEN(skb->data); + if (mp->tty) { + if (mp->tty->ldisc.receive_buf == 0) { + printk(KERN_ERR "capi: ldisc has no receive_buf function\n"); + return -1; + } + if (mp->ttyinstop) { +#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) + printk(KERN_DEBUG "capi: recv tty throttled\n"); +#endif + return -1; + } + if (mp->tty->ldisc.receive_room && + mp->tty->ldisc.receive_room(mp->tty) < datalen) { +#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) + printk(KERN_DEBUG "capi: no room in tty\n"); +#endif + return -1; + } + if ((nskb = gen_data_b3_resp_for(mp, skb)) == 0) { + printk(KERN_ERR "capi: gen_data_b3_resp failed\n"); + return -1; + } + datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4); + errcode = (*capifuncs->capi_put_message)(mp->applid, nskb); + if (errcode != CAPI_NOERROR) { + printk(KERN_ERR "capi: send DATA_B3_RESP failed=%x\n", + errcode); + kfree_skb(nskb); + return -1; + } + (void)skb_pull(skb, CAPIMSG_LEN(skb->data)); +#ifdef _DEBUG_DATAFLOW + printk(KERN_DEBUG "capi: DATA_B3_RESP %u len=%d => ldisc\n", + datahandle, skb->len); +#endif + mp->tty->ldisc.receive_buf(mp->tty, skb->data, 0, skb->len); + kfree_skb(skb); + return 0; + + } else if (mp->file) { + if (skb_queue_len(&mp->recvqueue) > CAPINC_MAX_RECVQUEUE) { +#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) + printk(KERN_DEBUG "capi: no room in raw queue\n"); +#endif + return -1; + } + if ((nskb = gen_data_b3_resp_for(mp, skb)) == 0) { + printk(KERN_ERR "capi: gen_data_b3_resp failed\n"); + return -1; + } + datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4); + errcode = (*capifuncs->capi_put_message)(mp->applid, nskb); + if (errcode != CAPI_NOERROR) { + printk(KERN_ERR "capi: send DATA_B3_RESP failed=%x\n", + errcode); + kfree_skb(nskb); + return -1; + } + (void)skb_pull(skb, CAPIMSG_LEN(skb->data)); +#ifdef _DEBUG_DATAFLOW + printk(KERN_DEBUG "capi: DATA_B3_RESP %u len=%d => raw\n", + datahandle, skb->len); +#endif + skb_queue_tail(&mp->recvqueue, skb); + wake_up_interruptible(&mp->recvwait); + return 0; + } +#ifdef _DEBUG_DATAFLOW + printk(KERN_DEBUG "capi: currently no receiver\n"); +#endif + return -1; +} + +void handle_minor_recv(struct capiminor *mp) +{ + struct sk_buff *skb; + while ((skb = skb_dequeue(&mp->inqueue)) != 0) { + unsigned int len = skb->len; + mp->inbytes -= len; + if (handle_recv_skb(mp, skb) < 0) { + skb_queue_head(&mp->inqueue, skb); + mp->inbytes += len; + return; + } + } +} + +int handle_minor_send(struct capiminor *mp) +{ + struct sk_buff *skb; + __u16 len; + int count = 0; + __u16 errcode; + __u16 datahandle; + + if (mp->tty && mp->ttyoutstop) { +#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) + printk(KERN_DEBUG "capi: send: tty stopped\n"); +#endif + return 0; + } + + while ((skb = skb_dequeue(&mp->outqueue)) != 0) { + datahandle = mp->datahandle; + len = (__u16)skb->len; + skb_push(skb, CAPI_DATA_B3_REQ_LEN); + memset(skb->data, 0, CAPI_DATA_B3_REQ_LEN); + capimsg_setu16(skb->data, 0, CAPI_DATA_B3_REQ_LEN); + capimsg_setu16(skb->data, 2, mp->applid); + capimsg_setu8 (skb->data, 4, CAPI_DATA_B3); + capimsg_setu8 (skb->data, 5, CAPI_REQ); + capimsg_setu16(skb->data, 6, mp->msgid++); + capimsg_setu32(skb->data, 8, mp->ncci); /* NCCI */ + capimsg_setu32(skb->data, 12, (__u32) skb->data); /* Data32 */ + capimsg_setu16(skb->data, 16, len); /* Data length */ + capimsg_setu16(skb->data, 18, datahandle); + capimsg_setu16(skb->data, 20, 0); /* Flags */ + + if (capincci_add_ack(mp, datahandle) < 0) { + skb_pull(skb, CAPI_DATA_B3_REQ_LEN); + skb_queue_head(&mp->outqueue, skb); + return count; + } + errcode = (*capifuncs->capi_put_message) (mp->applid, skb); + if (errcode == CAPI_NOERROR) { + mp->datahandle++; + count++; + mp->outbytes -= len; +#ifdef _DEBUG_DATAFLOW + printk(KERN_DEBUG "capi: DATA_B3_REQ %u len=%u\n", + datahandle, len); +#endif + continue; + } + capiminor_del_ack(mp, datahandle); + + if (errcode == CAPI_SENDQUEUEFULL) { + skb_pull(skb, CAPI_DATA_B3_REQ_LEN); + skb_queue_head(&mp->outqueue, skb); + break; + } + + /* ups, drop packet */ + printk(KERN_ERR "capi: put_message = %x\n", errcode); + mp->outbytes -= len; + kfree_skb(skb); + } + if (count) + wake_up_interruptible(&mp->sendwait); + return count; +} + +#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ +/* -------- function called by lower level -------------------------- */ + +static void capi_signal(__u16 applid, void *param) +{ + struct capidev *cdev = (struct capidev *)param; +#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE + struct capiminor *mp; + __u16 datahandle; +#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ + struct capincci *np; struct sk_buff *skb = 0; + __u32 ncci; - if (minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) { - printk(KERN_ERR "BUG: capi_signal: illegal minor %d\n", minor); + (void) (*capifuncs->capi_get_message) (applid, &skb); + if (!skb) { + printk(KERN_ERR "BUG: capi_signal: no skb\n"); return; } - cdev = &capidevs[minor]; - (void) (*capifuncs->capi_get_message) (applid, &skb); - if (skb) { - skb_queue_tail(&cdev->recv_queue, skb); - wake_up_interruptible(&cdev->recv_wait); + + if (CAPIMSG_COMMAND(skb->data) != CAPI_DATA_B3) { + skb_queue_tail(&cdev->recvqueue, skb); + wake_up_interruptible(&cdev->recvwait); + return; + } + ncci = CAPIMSG_CONTROL(skb->data); + for (np = cdev->nccis; np && np->ncci != ncci; np = np->next) + ; + if (!np) { + printk(KERN_ERR "BUG: capi_signal: ncci not found\n"); + skb_queue_tail(&cdev->recvqueue, skb); + wake_up_interruptible(&cdev->recvwait); + return; + } +#ifndef CONFIG_ISDN_CAPI_MIDDLEWARE + skb_queue_tail(&cdev->recvqueue, skb); + wake_up_interruptible(&cdev->recvwait); +#else /* CONFIG_ISDN_CAPI_MIDDLEWARE */ + mp = np->minorp; + if (!mp) { + skb_queue_tail(&cdev->recvqueue, skb); + wake_up_interruptible(&cdev->recvwait); + return; + } + + + if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) { + datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4+4+2); +#ifdef _DEBUG_DATAFLOW + printk(KERN_DEBUG "capi_signal: DATA_B3_IND %u len=%d\n", + datahandle, skb->len-CAPIMSG_LEN(skb->data)); +#endif + skb_queue_tail(&mp->inqueue, skb); + mp->inbytes += skb->len; + handle_minor_recv(mp); + + } else if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_CONF) { + + datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4); +#ifdef _DEBUG_DATAFLOW + printk(KERN_DEBUG "capi_signal: DATA_B3_CONF %u 0x%x\n", + datahandle, + CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4+2)); +#endif + kfree_skb(skb); + (void)capiminor_del_ack(mp, datahandle); + if (mp->tty) { + if (mp->tty->ldisc.write_wakeup) + mp->tty->ldisc.write_wakeup(mp->tty); + } else { + wake_up_interruptible(&mp->sendwait); + } + (void)handle_minor_send(mp); + } else { - printk(KERN_ERR "BUG: capi_signal: no skb\n"); + /* ups, let capi application handle it :-) */ + skb_queue_tail(&cdev->recvqueue, skb); + wake_up_interruptible(&cdev->recvwait); } +#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ } -/* -------- file_operations ----------------------------------------- */ +/* -------- file_operations for capidev ----------------------------- */ -static long long capi_llseek(struct file *file, - long long offset, int origin) +static loff_t +capi_llseek(struct file *file, loff_t offset, int origin) { return -ESPIPE; } -static ssize_t capi_read(struct file *file, char *buf, - size_t count, loff_t *ppos) +static ssize_t +capi_read(struct file *file, char *buf, size_t count, loff_t *ppos) { - struct inode *inode = file->f_dentry->d_inode; - unsigned int minor = MINOR(inode->i_rdev); - struct capidev *cdev; + struct capidev *cdev = (struct capidev *)file->private_data; struct sk_buff *skb; int retval; size_t copied; - if (ppos != &file->f_pos) + if (ppos != &file->f_pos) return -ESPIPE; - if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) + if (!cdev->applid) return -ENODEV; - cdev = &capidevs[minor]; - - if ((skb = skb_dequeue(&cdev->recv_queue)) == 0) { + if ((skb = skb_dequeue(&cdev->recvqueue)) == 0) { if (file->f_flags & O_NONBLOCK) return -EAGAIN; for (;;) { - interruptible_sleep_on(&cdev->recv_wait); - if ((skb = skb_dequeue(&cdev->recv_queue)) != 0) + interruptible_sleep_on(&cdev->recvwait); + if ((skb = skb_dequeue(&cdev->recvqueue)) != 0) break; if (signal_pending(current)) break; @@ -212,63 +890,58 @@ static ssize_t capi_read(struct file *file, char *buf, return -ERESTARTNOHAND; } if (skb->len > count) { - skb_queue_head(&cdev->recv_queue, skb); + skb_queue_head(&cdev->recvqueue, skb); return -EMSGSIZE; } retval = copy_to_user(buf, skb->data, skb->len); if (retval) { - skb_queue_head(&cdev->recv_queue, skb); + skb_queue_head(&cdev->recvqueue, skb); return retval; } copied = skb->len; - if (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3 - && CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) + if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_IND) { cdev->nrecvdatapkt++; - else cdev->nrecvctlpkt++; + } else { + cdev->nrecvctlpkt++; + } + kfree_skb(skb); return copied; } -static ssize_t capi_write(struct file *file, const char *buf, - size_t count, loff_t *ppos) +static ssize_t +capi_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { - struct inode *inode = file->f_dentry->d_inode; - unsigned int minor = MINOR(inode->i_rdev); - struct capidev *cdev; + struct capidev *cdev = (struct capidev *)file->private_data; struct sk_buff *skb; int retval; - __u8 cmd; - __u8 subcmd; __u16 mlen; - if (ppos != &file->f_pos) + if (ppos != &file->f_pos) return -ESPIPE; - if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) + if (!cdev->applid) return -ENODEV; - cdev = &capidevs[minor]; - skb = alloc_skb(count, GFP_USER); if ((retval = copy_from_user(skb_put(skb, count), buf, count))) { kfree_skb(skb); return retval; } - cmd = CAPIMSG_COMMAND(skb->data); - subcmd = CAPIMSG_SUBCOMMAND(skb->data); mlen = CAPIMSG_LEN(skb->data); - if (cmd == CAPI_DATA_B3 && subcmd == CAPI_REQ) { - __u16 dlen = CAPIMSG_DATALEN(skb->data); - if (mlen + dlen != count) { + if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) { + if (mlen + CAPIMSG_DATALEN(skb->data) != count) { + kfree_skb(skb); + return -EINVAL; + } + } else { + if (mlen != count) { kfree_skb(skb); return -EINVAL; } - } else if (mlen != count) { - kfree_skb(skb); - return -EINVAL; } CAPIMSG_SETAPPID(skb->data, cdev->applid); @@ -278,63 +951,56 @@ static ssize_t capi_write(struct file *file, const char *buf, kfree_skb(skb); return -EIO; } - if (cmd == CAPI_DATA_B3 && subcmd == CAPI_REQ) + if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) { cdev->nsentdatapkt++; - else cdev->nsentctlpkt++; + } else { + cdev->nsentctlpkt++; + } return count; } static unsigned int capi_poll(struct file *file, poll_table * wait) { + struct capidev *cdev = (struct capidev *)file->private_data; unsigned int mask = 0; - unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); - struct capidev *cdev; - if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) + if (!cdev->applid) return POLLERR; - cdev = &capidevs[minor]; - poll_wait(file, &(cdev->recv_wait), wait); + poll_wait(file, &(cdev->recvwait), wait); mask = POLLOUT | POLLWRNORM; - if (!skb_queue_empty(&cdev->recv_queue)) + if (!skb_queue_empty(&cdev->recvqueue)) mask |= POLLIN | POLLRDNORM; return mask; } -static int capi_ioctl(struct inode *inode, struct file *file, +static int +capi_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - unsigned int minor = MINOR(inode->i_rdev); - struct capidev *cdev; + struct capidev *cdev = (struct capidev *)file->private_data; capi_ioctl_struct data; - int retval; - - - if (minor >= CAPI_MAXMINOR || !capidevs[minor].is_open) - return -ENODEV; - - cdev = &capidevs[minor]; + int retval = -EINVAL; switch (cmd) { case CAPI_REGISTER: { - if (!minor) - return -EINVAL; retval = copy_from_user((void *) &data.rparams, (void *) arg, sizeof(struct capi_register_params)); if (retval) return -EFAULT; - if (cdev->is_registered) + if (cdev->applid) return -EEXIST; cdev->errcode = (*capifuncs->capi_register) (&data.rparams, &cdev->applid); - if (cdev->errcode) + if (cdev->errcode) { + cdev->applid = 0; return -EIO; - (void) (*capifuncs->capi_set_signal) (cdev->applid, capi_signal, minor); - cdev->is_registered = 1; + } + (void) (*capifuncs->capi_set_signal) (cdev->applid, capi_signal, cdev); } - return 0; + return (int)cdev->applid; case CAPI_GET_VERSION: { @@ -440,8 +1106,6 @@ static int capi_ioctl(struct inode *inode, struct file *file, case CAPI_MANUFACTURER_CMD: { struct capi_manufacturer_cmd mcmd; - if (minor) - return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; retval = copy_from_user((void *) &mcmd, (void *) arg, @@ -451,107 +1115,721 @@ static int capi_ioctl(struct inode *inode, struct file *file, return (*capifuncs->capi_manufacturer) (mcmd.cmd, mcmd.data); } return 0; + + case CAPI_SET_FLAGS: + case CAPI_CLR_FLAGS: + { + unsigned userflags; + retval = copy_from_user((void *) &userflags, + (void *) arg, + sizeof(userflags)); + if (retval) + return -EFAULT; + if (cmd == CAPI_SET_FLAGS) + cdev->userflags |= userflags; + else + cdev->userflags &= ~userflags; + } + return 0; + + case CAPI_GET_FLAGS: + { + retval = copy_to_user((void *) arg, + (void *) &cdev->userflags, + sizeof(cdev->userflags)); + if (retval) + return -EFAULT; + } + return 0; + + case CAPI_NCCI_OPENCOUNT: + { + struct capincci *nccip; +#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE + struct capiminor *mp; +#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ + unsigned ncci; + int count = 0; + retval = copy_from_user((void *) &ncci, + (void *) arg, + sizeof(ncci)); + if (retval) + return -EFAULT; + nccip = capincci_find(cdev, (__u32) ncci); + if (!nccip) + return 0; +#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE + if ((mp = nccip->minorp) != 0) { + count += atomic_read(&mp->ttyopencount); + if (mp->file) + count++; + } +#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ + return count; + } + return 0; + +#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE + case CAPI_NCCI_GETUNIT: + { + struct capincci *nccip; + struct capiminor *mp; + unsigned ncci; + retval = copy_from_user((void *) &ncci, + (void *) arg, + sizeof(ncci)); + if (retval) + return -EFAULT; + nccip = capincci_find(cdev, (__u32) ncci); + if (!nccip || (mp = nccip->minorp) == 0) + return -ESRCH; + return mp->minor; + } + return 0; +#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ } return -EINVAL; } -static int capi_open(struct inode *inode, struct file *file) +static int +capi_open(struct inode *inode, struct file *file) { - unsigned int minor = MINOR(inode->i_rdev); + if (file->private_data) + return -EEXIST; - if (minor >= CAPI_MAXMINOR) - return -ENXIO; - - if (minor) { - if (capidevs[minor].is_open) - return -EEXIST; - - capidevs[minor].is_open = 1; - skb_queue_head_init(&capidevs[minor].recv_queue); - MOD_INC_USE_COUNT; - capidevs[minor].nopen++; - - } else { - capidevs[minor].is_open++; - MOD_INC_USE_COUNT; - } + if ((file->private_data = capidev_alloc(file)) == 0) + return -ENOMEM; + MOD_INC_USE_COUNT; +#ifdef _DEBUG_REFCOUNT + printk(KERN_DEBUG "capi_open %d\n", GET_USE_COUNT(&__this_module)); +#endif return 0; } static int capi_release(struct inode *inode, struct file *file) { - unsigned int minor = MINOR(inode->i_rdev); - struct capidev *cdev; - struct sk_buff *skb; - - if (minor >= CAPI_MAXMINOR || !capidevs[minor].is_open) { - printk(KERN_ERR "capi20: release minor %d ???\n", minor); - return 0; - } - cdev = &capidevs[minor]; - - if (minor) { - - if (cdev->is_registered) - (*capifuncs->capi_release) (cdev->applid); - - cdev->is_registered = 0; - cdev->applid = 0; - - while ((skb = skb_dequeue(&cdev->recv_queue)) != 0) { - kfree_skb(skb); - } - cdev->is_open = 0; - } else { - cdev->is_open--; - } + struct capidev *cdev = (struct capidev *)file->private_data; + capincci_free(cdev, 0xffffffff); + capidev_free(cdev); + file->private_data = NULL; + MOD_DEC_USE_COUNT; +#ifdef _DEBUG_REFCOUNT + printk(KERN_DEBUG "capi_release %d\n", GET_USE_COUNT(&__this_module)); +#endif return 0; } static struct file_operations capi_fops = { - capi_llseek, - capi_read, - capi_write, - NULL, /* capi_readdir */ - capi_poll, - capi_ioctl, - NULL, /* capi_mmap */ - capi_open, - NULL, /* capi_flush */ - capi_release, - NULL, /* capi_fsync */ - NULL, /* capi_fasync */ + llseek: capi_llseek, + read: capi_read, + write: capi_write, + poll: capi_poll, + ioctl: capi_ioctl, + open: capi_open, + release: capi_release, }; -/* -------- /proc functions ----------------------------------- */ +#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE +/* -------- file_operations for capincci ---------------------------- */ -/* - * /proc/capi/capi20: - * minor opencount nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt +static int +capinc_raw_open(struct inode *inode, struct file *file) +{ + struct capiminor *mp; + + if (file->private_data) + return -EEXIST; + if ((mp = capiminor_find(MINOR(file->f_dentry->d_inode->i_rdev))) == 0) + return -ENXIO; + if (mp->nccip == 0) + return -ENXIO; + if (mp->file) + return -EBUSY; + +#ifdef _DEBUG_REFCOUNT + printk(KERN_DEBUG "capi_raw_open %d\n", GET_USE_COUNT(&__this_module)); +#endif + + mp->datahandle = 0; + mp->file = file; + file->private_data = (void *)mp; + handle_minor_recv(mp); + return 0; +} + +static loff_t +capinc_raw_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +static ssize_t +capinc_raw_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + struct capiminor *mp = (struct capiminor *)file->private_data; + struct sk_buff *skb; + int retval; + size_t copied = 0; + + if (ppos != &file->f_pos) + return -ESPIPE; + + if (!mp || !mp->nccip) + return -EINVAL; + + if ((skb = skb_dequeue(&mp->recvqueue)) == 0) { + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + for (;;) { + interruptible_sleep_on(&mp->recvwait); + if (mp->nccip == 0) + return 0; + if ((skb = skb_dequeue(&mp->recvqueue)) != 0) + break; + if (signal_pending(current)) + break; + } + if (skb == 0) + return -ERESTARTNOHAND; + } + do { + if (count < skb->len) { + retval = copy_to_user(buf, skb->data, count); + if (retval) { + skb_queue_head(&mp->recvqueue, skb); + return retval; + } + skb_pull(skb, count); + skb_queue_head(&mp->recvqueue, skb); + copied += count; + return copied; + } else { + retval = copy_to_user(buf, skb->data, skb->len); + if (retval) { + skb_queue_head(&mp->recvqueue, skb); + return copied; + } + copied += skb->len; + count -= skb->len; + buf += skb->len; + kfree_skb(skb); + } + } while ((skb = skb_dequeue(&mp->recvqueue)) != 0); + + return copied; +} + +static ssize_t +capinc_raw_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + struct capiminor *mp = (struct capiminor *)file->private_data; + struct sk_buff *skb; + int retval; + + if (ppos != &file->f_pos) + return -ESPIPE; + + if (!mp || !mp->nccip) + return -EINVAL; + + skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+count, GFP_USER); + + skb_reserve(skb, CAPI_DATA_B3_REQ_LEN); + if ((retval = copy_from_user(skb_put(skb, count), buf, count))) { + kfree_skb(skb); + return retval; + } + + while (skb_queue_len(&mp->outqueue) > CAPINC_MAX_SENDQUEUE) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + interruptible_sleep_on(&mp->sendwait); + if (mp->nccip == 0) { + kfree_skb(skb); + return -EIO; + } + if (signal_pending(current)) + return -ERESTARTNOHAND; + } + skb_queue_tail(&mp->outqueue, skb); + mp->outbytes += skb->len; + (void)handle_minor_send(mp); + return count; +} + +static unsigned int +capinc_raw_poll(struct file *file, poll_table * wait) +{ + struct capiminor *mp = (struct capiminor *)file->private_data; + unsigned int mask = 0; + + if (!mp || !mp->nccip) + return POLLERR|POLLHUP; + + poll_wait(file, &(mp->recvwait), wait); + if (!skb_queue_empty(&mp->recvqueue)) + mask |= POLLIN | POLLRDNORM; + poll_wait(file, &(mp->sendwait), wait); + if (skb_queue_len(&mp->outqueue) > CAPINC_MAX_SENDQUEUE) + mask = POLLOUT | POLLWRNORM; + return mask; +} + +static int +capinc_raw_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct capiminor *mp = (struct capiminor *)file->private_data; + if (!mp || !mp->nccip) + return -EINVAL; + + switch (cmd) { + } + return -EINVAL; +} + +static int +capinc_raw_release(struct inode *inode, struct file *file) +{ + struct capiminor *mp = (struct capiminor *)file->private_data; + + if (mp) { + mp->file = 0; + if (mp->nccip == 0) { + capiminor_free(mp); + file->private_data = NULL; + } + } + +#ifdef _DEBUG_REFCOUNT + printk(KERN_DEBUG "capinc_raw_release %d\n", GET_USE_COUNT(&__this_module)); +#endif + return 0; +} + +static struct file_operations capinc_raw_fops = +{ + llseek: capinc_raw_llseek, + read: capinc_raw_read, + write: capinc_raw_write, + poll: capinc_raw_poll, + ioctl: capinc_raw_ioctl, + open: capinc_raw_open, + release: capinc_raw_release, +}; + +/* -------- tty_operations for capincci ----------------------------- */ + +int capinc_tty_open(struct tty_struct * tty, struct file * file) +{ + struct capiminor *mp; + + if ((mp = capiminor_find(MINOR(file->f_dentry->d_inode->i_rdev))) == 0) + return -ENXIO; + if (mp->nccip == 0) + return -ENXIO; + if (mp->file) + return -EBUSY; + + skb_queue_head_init(&mp->recvqueue); + tty->driver_data = (void *)mp; +#ifdef _DEBUG_REFCOUNT + printk(KERN_DEBUG "capi_tty_open %d\n", GET_USE_COUNT(&__this_module)); +#endif + if (atomic_read(&mp->ttyopencount) == 0) + mp->tty = tty; + atomic_inc(&mp->ttyopencount); +#ifdef _DEBUG_REFCOUNT + printk(KERN_DEBUG "capinc_tty_open ocount=%d\n", atomic_read(&mp->ttyopencount)); +#endif + handle_minor_recv(mp); + return 0; +} + +void capinc_tty_close(struct tty_struct * tty, struct file * file) +{ + struct capiminor *mp; + + mp = (struct capiminor *)tty->driver_data; + if (mp) { + if (atomic_dec_and_test(&mp->ttyopencount)) { +#ifdef _DEBUG_REFCOUNT + printk(KERN_DEBUG "capinc_tty_close lastclose\n"); +#endif + tty->driver_data = (void *)0; + mp->tty = 0; + } +#ifdef _DEBUG_REFCOUNT + printk(KERN_DEBUG "capinc_tty_close ocount=%d\n", atomic_read(&mp->ttyopencount)); +#endif + if (mp->nccip == 0) + capiminor_free(mp); + } + +#ifdef _DEBUG_REFCOUNT + printk(KERN_DEBUG "capinc_tty_close\n"); +#endif +} + +int capinc_tty_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + struct capiminor *mp = (struct capiminor *)tty->driver_data; + struct sk_buff *skb; + int retval; + +#ifdef _DEBUG_TTYFUNCS + printk(KERN_DEBUG "capinc_tty_write(from_user=%d,count=%d)\n", + from_user, count); +#endif + + if (!mp || !mp->nccip) { +#ifdef _DEBUG_TTYFUNCS + printk(KERN_DEBUG "capinc_tty_write: mp or mp->ncci NULL\n"); +#endif + return 0; + } + + skb = mp->ttyskb; + if (skb) { + mp->ttyskb = 0; + skb_queue_tail(&mp->outqueue, skb); + mp->outbytes += skb->len; + } + + skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+count, GFP_ATOMIC); + if (!skb) { + printk(KERN_ERR "capinc_tty_write: alloc_skb failed\n"); + return -ENOMEM; + } + + skb_reserve(skb, CAPI_DATA_B3_REQ_LEN); + if (from_user) { + if ((retval = copy_from_user(skb_put(skb, count), buf, count))) { + kfree_skb(skb); +#ifdef _DEBUG_TTYFUNCS + printk(KERN_DEBUG "capinc_tty_write: copy_from_user=%d\n", retval); +#endif + return retval; + } + } else { + memcpy(skb_put(skb, count), buf, count); + } + + skb_queue_tail(&mp->outqueue, skb); + mp->outbytes += skb->len; + (void)handle_minor_send(mp); + (void)handle_minor_recv(mp); + return count; +} + +void capinc_tty_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct capiminor *mp = (struct capiminor *)tty->driver_data; + struct sk_buff *skb; + +#ifdef _DEBUG_TTYFUNCS + printk(KERN_DEBUG "capinc_put_char(%u)\n", ch); +#endif + + if (!mp || !mp->nccip) { +#ifdef _DEBUG_TTYFUNCS + printk(KERN_DEBUG "capinc_tty_put_char: mp or mp->ncci NULL\n"); +#endif + return; + } + + skb = mp->ttyskb; + if (skb) { + if (skb_tailroom(skb) > 0) { + *(skb_put(skb, 1)) = ch; + return; + } + mp->ttyskb = 0; + skb_queue_tail(&mp->outqueue, skb); + mp->outbytes += skb->len; + (void)handle_minor_send(mp); + } + skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+CAPI_MAX_BLKSIZE, GFP_ATOMIC); + if (skb) { + skb_reserve(skb, CAPI_DATA_B3_REQ_LEN); + *(skb_put(skb, 1)) = ch; + mp->ttyskb = skb; + } else { + printk(KERN_ERR "capinc_put_char: char %u lost\n", ch); + } +} + +void capinc_tty_flush_chars(struct tty_struct *tty) +{ + struct capiminor *mp = (struct capiminor *)tty->driver_data; + struct sk_buff *skb; + +#ifdef _DEBUG_TTYFUNCS + printk(KERN_DEBUG "capinc_tty_flush_chars\n"); +#endif + + if (!mp || !mp->nccip) { +#ifdef _DEBUG_TTYFUNCS + printk(KERN_DEBUG "capinc_tty_flush_chars: mp or mp->ncci NULL\n"); +#endif + return; + } + + skb = mp->ttyskb; + if (skb) { + mp->ttyskb = 0; + skb_queue_tail(&mp->outqueue, skb); + mp->outbytes += skb->len; + (void)handle_minor_send(mp); + } + (void)handle_minor_recv(mp); +} + +int capinc_tty_write_room(struct tty_struct *tty) +{ + struct capiminor *mp = (struct capiminor *)tty->driver_data; + int room; + if (!mp || !mp->nccip) { +#ifdef _DEBUG_TTYFUNCS + printk(KERN_DEBUG "capinc_tty_write_room: mp or mp->ncci NULL\n"); +#endif + return 0; + } + room = CAPINC_MAX_SENDQUEUE-skb_queue_len(&mp->outqueue); + room *= CAPI_MAX_BLKSIZE; +#ifdef _DEBUG_TTYFUNCS + printk(KERN_DEBUG "capinc_tty_write_room = %d\n", room); +#endif + return room; +} + +int capinc_tty_chars_in_buffer(struct tty_struct *tty) +{ + struct capiminor *mp = (struct capiminor *)tty->driver_data; + if (!mp || !mp->nccip) { +#ifdef _DEBUG_TTYFUNCS + printk(KERN_DEBUG "capinc_tty_chars_in_buffer: mp or mp->ncci NULL\n"); +#endif + return 0; + } +#ifdef _DEBUG_TTYFUNCS + printk(KERN_DEBUG "capinc_tty_chars_in_buffer = %d nack=%d sq=%d rq=%d\n", + mp->outbytes, mp->nack, + skb_queue_len(&mp->outqueue), + skb_queue_len(&mp->inqueue)); +#endif + return mp->outbytes; +} + +int capinc_tty_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + return -ENOIOCTLCMD; +} + +void capinc_tty_set_termios(struct tty_struct *tty, struct termios * old) +{ +#ifdef _DEBUG_TTYFUNCS + printk(KERN_DEBUG "capinc_tty_set_termios\n"); +#endif +} + +void capinc_tty_throttle(struct tty_struct * tty) +{ + struct capiminor *mp = (struct capiminor *)tty->driver_data; +#ifdef _DEBUG_TTYFUNCS + printk(KERN_DEBUG "capinc_tty_throttle\n"); +#endif + if (mp) + mp->ttyinstop = 1; +} + +void capinc_tty_unthrottle(struct tty_struct * tty) +{ + struct capiminor *mp = (struct capiminor *)tty->driver_data; +#ifdef _DEBUG_TTYFUNCS + printk(KERN_DEBUG "capinc_tty_unthrottle\n"); +#endif + if (mp) { + mp->ttyinstop = 0; + handle_minor_recv(mp); + } +} + +void capinc_tty_stop(struct tty_struct *tty) +{ + struct capiminor *mp = (struct capiminor *)tty->driver_data; +#ifdef _DEBUG_TTYFUNCS + printk(KERN_DEBUG "capinc_tty_stop\n"); +#endif + if (mp) { + mp->ttyoutstop = 1; + } +} + +void capinc_tty_start(struct tty_struct *tty) +{ + struct capiminor *mp = (struct capiminor *)tty->driver_data; +#ifdef _DEBUG_TTYFUNCS + printk(KERN_DEBUG "capinc_tty_start\n"); +#endif + if (mp) { + mp->ttyoutstop = 0; + (void)handle_minor_send(mp); + } +} + +void capinc_tty_hangup(struct tty_struct *tty) +{ +#ifdef _DEBUG_TTYFUNCS + printk(KERN_DEBUG "capinc_tty_hangup\n"); +#endif +} + +void capinc_tty_break_ctl(struct tty_struct *tty, int state) +{ +#ifdef _DEBUG_TTYFUNCS + printk(KERN_DEBUG "capinc_tty_break_ctl(%d)\n", state); +#endif +} + +void capinc_tty_flush_buffer(struct tty_struct *tty) +{ +#ifdef _DEBUG_TTYFUNCS + printk(KERN_DEBUG "capinc_tty_flush_buffer\n"); +#endif +} + +void capinc_tty_set_ldisc(struct tty_struct *tty) +{ +#ifdef _DEBUG_TTYFUNCS + printk(KERN_DEBUG "capinc_tty_set_ldisc\n"); +#endif +} + +void capinc_tty_send_xchar(struct tty_struct *tty, char ch) +{ +#ifdef _DEBUG_TTYFUNCS + printk(KERN_DEBUG "capinc_tty_send_xchar(%d)\n", ch); +#endif +} + +int capinc_tty_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + return 0; +} + +int capinc_write_proc(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + return 0; +} + +#define CAPINC_NR_PORTS 256 +static struct tty_driver capinc_tty_driver; +static int capinc_tty_refcount; +static struct tty_struct *capinc_tty_table[CAPINC_NR_PORTS]; +static struct termios *capinc_tty_termios[CAPINC_NR_PORTS]; +static struct termios *capinc_tty_termios_locked[CAPINC_NR_PORTS]; + +int capinc_tty_init(void) +{ + struct tty_driver *drv = &capinc_tty_driver; + + /* Initialize the tty_driver structure */ + + memset(drv, 0, sizeof(struct tty_driver)); + drv->magic = TTY_DRIVER_MAGIC; +#if (LINUX_VERSION_CODE > 0x20100) + drv->driver_name = "capi_nc"; +#endif + drv->name = "capi/%d"; + drv->major = capi_ttymajor; + drv->minor_start = 0; + drv->num = CAPINC_NR_PORTS; + drv->type = TTY_DRIVER_TYPE_SERIAL; + drv->subtype = SERIAL_TYPE_NORMAL; + drv->init_termios = tty_std_termios; + drv->init_termios.c_iflag = ICRNL; + drv->init_termios.c_oflag = OPOST | ONLCR; + drv->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + drv->init_termios.c_lflag = 0; + drv->flags = TTY_DRIVER_REAL_RAW|TTY_DRIVER_RESET_TERMIOS; + drv->refcount = &capinc_tty_refcount; + drv->table = capinc_tty_table; + drv->termios = capinc_tty_termios; + drv->termios_locked = capinc_tty_termios_locked; + + drv->open = capinc_tty_open; + drv->close = capinc_tty_close; + drv->write = capinc_tty_write; + drv->put_char = capinc_tty_put_char; + drv->flush_chars = capinc_tty_flush_chars; + drv->write_room = capinc_tty_write_room; + drv->chars_in_buffer = capinc_tty_chars_in_buffer; + drv->ioctl = capinc_tty_ioctl; + drv->set_termios = capinc_tty_set_termios; + drv->throttle = capinc_tty_throttle; + drv->unthrottle = capinc_tty_unthrottle; + drv->stop = capinc_tty_stop; + drv->start = capinc_tty_start; + drv->hangup = capinc_tty_hangup; +#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ + drv->break_ctl = capinc_tty_break_ctl; +#endif + drv->flush_buffer = capinc_tty_flush_buffer; + drv->set_ldisc = capinc_tty_set_ldisc; +#if (LINUX_VERSION_CODE >= 131343) + drv->send_xchar = capinc_tty_send_xchar; + drv->read_proc = capinc_tty_read_proc; +#endif + if (tty_register_driver(drv)) { + printk(KERN_ERR "Couldn't register capi_nc driver\n"); + return -1; + } + return 0; +} + +void capinc_tty_exit(void) +{ + struct tty_driver *drv = &capinc_tty_driver; + int retval; + if ((retval = tty_unregister_driver(drv))) + printk(KERN_ERR "capi: failed to unregister capi_nc driver (%d)\n", retval); +} + +#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ + +/* -------- /proc functions ----------------------------------------- */ + +/* + * /proc/capi/capi20: + * minor applid 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; + struct capidev *cdev; 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); + for (cdev=capidev_openlist; cdev; cdev = cdev->next) { + len += sprintf(page+len, "%d %d %lu %lu %lu %lu\n", + cdev->minor, + cdev->applid, + cdev->nrecvctlpkt, + cdev->nrecvdatapkt, + cdev->nsentctlpkt, + cdev->nsentdatapkt); if (len+begin > off+count) goto endloop; if (len+begin < off) { @@ -560,7 +1838,7 @@ static int proc_capidev_read_proc(char *page, char **start, off_t off, } } endloop: - if (i >= CAPI_MAXMINOR) + if (cdev == 0) *eof = 1; if (off >= len+begin) return 0; @@ -568,6 +1846,45 @@ endloop: return ((count < begin+len-off) ? count : begin+len-off); } +/* + * /proc/capi/capi20ncci: + * applid ncci + */ +static int proc_capincci_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct capidev *cdev; + struct capincci *np; + int len = 0; + off_t begin = 0; + + for (cdev=capidev_openlist; cdev; cdev = cdev->next) { + for (np=cdev->nccis; np; np = np->next) { + len += sprintf(page+len, "%d 0x%x%s\n", + cdev->applid, + np->ncci, +#ifndef CONFIG_ISDN_CAPI_MIDDLEWARE + ""); +#else /* CONFIG_ISDN_CAPI_MIDDLEWARE */ + np->minorp && np->minorp->file ? " open" : ""); +#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ + if (len+begin > off+count) + goto endloop; + if (len+begin < off) { + begin += len; + len = 0; + } + } + } +endloop: + if (cdev == 0) + *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; @@ -577,6 +1894,7 @@ static struct procfsentries { } procfsentries[] = { /* { "capi", S_IFDIR, 0 }, */ { "capi/capi20", 0 , proc_capidev_read_proc }, + { "capi/capi20ncci", 0 , proc_capincci_read_proc }, }; static void proc_init(void) @@ -604,42 +1922,122 @@ static void proc_exit(void) } } } + /* -------- init function and module interface ---------------------- */ + +static void lower_callback(unsigned int cmd, __u32 contr, void *data) +{ + struct capi_ncciinfo *np; + struct capidev *cdev; + + switch (cmd) { + case KCI_CONTRUP: + printk(KERN_INFO "capi: controller %hu up\n", contr); + break; + case KCI_CONTRDOWN: + printk(KERN_INFO "capi: controller %hu down\n", contr); + break; + case KCI_NCCIUP: + np = (struct capi_ncciinfo *)data; + if ((cdev = capidev_find(np->applid)) == 0) + return; + (void)capincci_alloc(cdev, np->ncci); + break; + case KCI_NCCIDOWN: + np = (struct capi_ncciinfo *)data; + if ((cdev = capidev_find(np->applid)) == 0) + return; + (void)capincci_free(cdev, np->ncci); + break; + } +} + #ifdef MODULE #define capi_init init_module #endif static struct capi_interface_user cuser = { "capi20", - 0, + lower_callback, }; +static char rev[10]; + int capi_init(void) { - - memset(capidevs, 0, sizeof(capidevs)); + char *p; + + MOD_INC_USE_COUNT; + + if ((p = strchr(revision, ':'))) { + strcpy(rev, p + 2); + p = strchr(rev, '$'); + *(p-1) = 0; + } else + strcpy(rev, "???"); if (register_chrdev(capi_major, "capi20", &capi_fops)) { printk(KERN_ERR "capi20: unable to get major %d\n", capi_major); + MOD_DEC_USE_COUNT; + return -EIO; + } + +#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE + if (register_chrdev(capi_rawmajor, "capi/r%d", &capinc_raw_fops)) { + unregister_chrdev(capi_major, "capi20"); + printk(KERN_ERR "capi20: unable to get major %d\n", capi_rawmajor); + MOD_DEC_USE_COUNT; return -EIO; } +#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ printk(KERN_NOTICE "capi20: started up with major %d\n", capi_major); if ((capifuncs = attach_capi_interface(&cuser)) == 0) { + + MOD_DEC_USE_COUNT; unregister_chrdev(capi_major, "capi20"); +#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE + unregister_chrdev(capi_rawmajor, "capi/r%d"); +#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ return -EIO; } + +#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE + if (capinc_tty_init() < 0) { + (void) detach_capi_interface(&cuser); + unregister_chrdev(capi_major, "capi20"); + unregister_chrdev(capi_rawmajor, "capi/r%d"); + MOD_DEC_USE_COUNT; + return -ENOMEM; + } +#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ + + (void)proc_init(); + + printk(KERN_NOTICE "capi20: Rev%s: started up with major %d\n", + rev, capi_major); + + MOD_DEC_USE_COUNT; return 0; } #ifdef MODULE void cleanup_module(void) { +#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE +#endif (void)proc_exit(); + unregister_chrdev(capi_major, "capi20"); + +#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE + capinc_tty_exit(); + unregister_chrdev(capi_rawmajor, "capi/r%d"); +#endif (void) detach_capi_interface(&cuser); + printk(KERN_NOTICE "capi: Rev%s: unloaded\n", rev); } #endif diff --git a/drivers/isdn/avmb1/capicmd.h b/drivers/isdn/avmb1/capicmd.h index 104ef824f66b..970365f7a168 100644 --- a/drivers/isdn/avmb1/capicmd.h +++ b/drivers/isdn/avmb1/capicmd.h @@ -1,11 +1,22 @@ /* - * $Id: capicmd.h,v 1.1 1997/03/04 21:50:30 calle Exp $ + * $Id: capicmd.h,v 1.2 2000/03/03 15:50:42 calle Exp $ * * CAPI 2.0 Interface for Linux * * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capicmd.h,v $ + * Revision 1.2 2000/03/03 15:50:42 calle + * - kernel CAPI: + * - Changed parameter "param" in capi_signal from __u32 to void *. + * - rewrote notifier handling in kcapi.c + * - new notifier NCCI_UP and NCCI_DOWN + * - User CAPI: + * - /dev/capi20 is now a cloning device. + * - middleware extentions prepared. + * - capidrv.c + * - locking of list operations and module count updates. + * * Revision 1.1 1997/03/04 21:50:30 calle * Frirst version in isdn4linux * @@ -20,6 +31,10 @@ #ifndef __CAPICMD_H__ #define __CAPICMD_H__ +#define CAPI_MSG_BASELEN 8 +#define CAPI_DATA_B3_REQ_LEN (CAPI_MSG_BASELEN+4+4+2+2+2) +#define CAPI_DATA_B3_RESP_LEN (CAPI_MSG_BASELEN+4+2) + /*----- CAPI commands -----*/ #define CAPI_ALERT 0x01 #define CAPI_CONNECT 0x02 diff --git a/drivers/isdn/avmb1/capidev.h b/drivers/isdn/avmb1/capidev.h index 1bc55bdff0a4..764f130e8a12 100644 --- a/drivers/isdn/avmb1/capidev.h +++ b/drivers/isdn/avmb1/capidev.h @@ -1,11 +1,22 @@ /* - * $Id: capidev.h,v 1.4 1999/07/01 15:26:32 calle Exp $ + * $Id: capidev.h,v 1.5 2000/03/03 15:50:42 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.5 2000/03/03 15:50:42 calle + * - kernel CAPI: + * - Changed parameter "param" in capi_signal from __u32 to void *. + * - rewrote notifier handling in kcapi.c + * - new notifier NCCI_UP and NCCI_DOWN + * - User CAPI: + * - /dev/capi20 is now a cloning device. + * - middleware extentions prepared. + * - capidrv.c + * - locking of list operations and module count updates. + * * 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: @@ -40,18 +51,18 @@ */ struct capidev { - int is_open; - int is_registered; - __u16 applid; + struct capidev *next; + struct file *file; + __u16 applid; + __u16 errcode; + unsigned int minor; + struct sk_buff_head recv_queue; struct wait_queue *recv_wait; - __u16 errcode; + /* Statistic */ - unsigned long nopen; - unsigned long nrecvctlpkt; - unsigned long nrecvdatapkt; - unsigned long nsentctlpkt; - unsigned long nsentdatapkt; + 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 79bb370d12fa..58b2eb9c6631 100644 --- a/drivers/isdn/avmb1/capidrv.c +++ b/drivers/isdn/avmb1/capidrv.c @@ -1,11 +1,46 @@ /* - * $Id: capidrv.c,v 1.29 1999/12/06 17:13:06 calle Exp $ + * $Id: capidrv.c,v 1.36 2000/06/26 15:13:41 keil Exp $ * * ISDN4Linux Driver, using capi20 interface (kernelcapi) * * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capidrv.c,v $ + * Revision 1.36 2000/06/26 15:13:41 keil + * features should be or'ed + * + * Revision 1.35 2000/06/19 15:11:25 keil + * avoid use of freed structs + * changes from 2.4.0-ac21 + * + * Revision 1.34 2000/06/19 13:13:55 calle + * Added Modemsupport! + * + * Revision 1.33 2000/05/06 00:52:36 kai + * merged changes from kernel tree + * fixed timer and net_device->name breakage + * + * Revision 1.32 2000/04/07 15:19:58 calle + * remove warnings + * + * Revision 1.31 2000/04/06 15:01:25 calle + * Bugfix: crash in capidrv.c when reseting a capi controller. + * - changed code order on remove of controller. + * - using tq_schedule for notifier in kcapi.c. + * - now using spin_lock_irqsave() and spin_unlock_irqrestore(). + * strange: sometimes even MP hang on unload of isdn.o ... + * + * Revision 1.30 2000/03/03 15:50:42 calle + * - kernel CAPI: + * - Changed parameter "param" in capi_signal from __u32 to void *. + * - rewrote notifier handling in kcapi.c + * - new notifier NCCI_UP and NCCI_DOWN + * - User CAPI: + * - /dev/capi20 is now a cloning device. + * - middleware extentions prepared. + * - capidrv.c + * - locking of list operations and module count updates. + * * Revision 1.29 1999/12/06 17:13:06 calle * Added controller watchdog. * @@ -171,8 +206,8 @@ #include "capicmd.h" #include "capidrv.h" -static char *revision = "$Revision: 1.29 $"; -int debugmode = 0; +static char *revision = "$Revision: 1.36 $"; +static int debugmode = 0; MODULE_AUTHOR("Carsten Paeth "); MODULE_PARM(debugmode, "i"); @@ -280,6 +315,7 @@ typedef struct capidrv_bchan capidrv_bchan; /* -------- data definitions ----------------------------------------- */ static capidrv_data global; +static spinlock_t global_lock = SPIN_LOCK_UNLOCKED; static struct capi_interface *capifuncs; static void handle_dtrace_data(capidrv_contr *card, @@ -305,6 +341,8 @@ static inline __u32 b1prot(int l2, int l3) return 2; case ISDN_PROTO_L2_FAX: return 4; + case ISDN_PROTO_L2_MODEM: + return 8; } } @@ -321,6 +359,7 @@ static inline __u32 b2prot(int l2, int l3) case ISDN_PROTO_L2_V11096: case ISDN_PROTO_L2_V11019: case ISDN_PROTO_L2_V11038: + case ISDN_PROTO_L2_MODEM: return 1; case ISDN_PROTO_L2_FAX: return 4; @@ -338,6 +377,7 @@ static inline __u32 b3prot(int l2, int l3) case ISDN_PROTO_L2_V11096: case ISDN_PROTO_L2_V11019: case ISDN_PROTO_L2_V11038: + case ISDN_PROTO_L2_MODEM: default: return 0; case ISDN_PROTO_L2_FAX: @@ -439,26 +479,28 @@ static inline __u8 cip2si2(__u16 cipval) static inline capidrv_contr *findcontrbydriverid(int driverid) { - capidrv_contr *p = global.contr_list; + unsigned long flags; + capidrv_contr *p; - while (p) { + spin_lock_irqsave(&global_lock, flags); + for (p = global.contr_list; p; p = p->next) if (p->myid == driverid) - return p; - p = p->next; - } - return (capidrv_contr *) 0; + break; + spin_unlock_irqrestore(&global_lock, flags); + return p; } static capidrv_contr *findcontrbynumber(__u32 contr) { + unsigned long flags; capidrv_contr *p = global.contr_list; - while (p) { + spin_lock_irqsave(&global_lock, flags); + for (p = global.contr_list; p; p = p->next) if (p->contrnr == contr) - return p; - p = p->next; - } - return (capidrv_contr *) 0; + break; + spin_unlock_irqrestore(&global_lock, flags); + return p; } @@ -1501,7 +1543,7 @@ static void handle_data(_cmsg * cmsg, struct sk_buff *skb) static _cmsg s_cmsg; -static void capidrv_signal(__u16 applid, __u32 dummy) +static void capidrv_signal(__u16 applid, void *dummy) { struct sk_buff *skb = 0; @@ -1545,47 +1587,41 @@ static void capidrv_signal(__u16 applid, __u32 dummy) static void handle_dtrace_data(capidrv_contr *card, int send, int level2, __u8 *data, __u16 len) { - long flags; - __u8 *p, *end; - isdn_ctrl cmd; + __u8 *p, *end; + isdn_ctrl cmd; - if (!len) { - printk(KERN_DEBUG "capidrv-%d: avmb1_q931_data: len == %d\n", + if (!len) { + printk(KERN_DEBUG "capidrv-%d: avmb1_q931_data: len == %d\n", card->contrnr, len); - return; - } + return; + } - save_flags(flags); - cli(); - - if (level2) { - PUTBYTE_TO_STATUS(card, 'D'); - PUTBYTE_TO_STATUS(card, '2'); - PUTBYTE_TO_STATUS(card, send ? '>' : '<'); - PUTBYTE_TO_STATUS(card, ':'); - } else { - PUTBYTE_TO_STATUS(card, 'D'); - PUTBYTE_TO_STATUS(card, '3'); - PUTBYTE_TO_STATUS(card, send ? '>' : '<'); - PUTBYTE_TO_STATUS(card, ':'); - } + if (level2) { + PUTBYTE_TO_STATUS(card, 'D'); + PUTBYTE_TO_STATUS(card, '2'); + PUTBYTE_TO_STATUS(card, send ? '>' : '<'); + PUTBYTE_TO_STATUS(card, ':'); + } else { + PUTBYTE_TO_STATUS(card, 'D'); + PUTBYTE_TO_STATUS(card, '3'); + PUTBYTE_TO_STATUS(card, send ? '>' : '<'); + PUTBYTE_TO_STATUS(card, ':'); + } + + for (p = data, end = data+len; p < end; p++) { + __u8 w; + PUTBYTE_TO_STATUS(card, ' '); + w = (*p >> 4) & 0xf; + PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w); + w = *p & 0xf; + PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w); + } + PUTBYTE_TO_STATUS(card, '\n'); - for (p = data, end = data+len; p < end; p++) { - __u8 w; - PUTBYTE_TO_STATUS(card, ' '); - w = (*p >> 4) & 0xf; - PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w); - w = *p & 0xf; - PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w); - } - PUTBYTE_TO_STATUS(card, '\n'); - - restore_flags(flags); - - cmd.command = ISDN_STAT_STAVAIL; - cmd.driver = card->myid; - cmd.arg = len*3+5; - card->interface.statcallb(&cmd); + cmd.command = ISDN_STAT_STAVAIL; + cmd.driver = card->myid; + cmd.arg = len*3+5; + card->interface.statcallb(&cmd); } /* ------------------------------------------------------------------- */ @@ -1990,8 +2026,8 @@ static int if_command(isdn_ctrl * c) return capidrv_command(c, card); printk(KERN_ERR - "capidrv-%d: if_command %d called with invalid driverId %d!\n", - card->contrnr, c->command, c->driver); + "capidrv: if_command %d called with invalid driverId %d!\n", + c->command, c->driver); return -ENODEV; } @@ -2147,50 +2183,6 @@ 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 void send_listen(capidrv_contr *card) { @@ -2218,14 +2210,18 @@ static void listentimerfunc(unsigned long x) static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) { capidrv_contr *card; + long flags; isdn_ctrl cmd; char id[20]; int i; + MOD_INC_USE_COUNT; + sprintf(id, "capidrv-%d", contr); if (!(card = (capidrv_contr *) kmalloc(sizeof(capidrv_contr), GFP_ATOMIC))) { printk(KERN_WARNING "capidrv: (%s) Could not allocate contr-struct.\n", id); + MOD_DEC_USE_COUNT; return -1; } memset(card, 0, sizeof(capidrv_contr)); @@ -2238,6 +2234,7 @@ static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) printk(KERN_WARNING "capidrv: (%s) Could not allocate bchan-structs.\n", id); kfree(card); + MOD_DEC_USE_COUNT; return -1; } card->interface.channels = profp->nbchannel; @@ -2246,41 +2243,49 @@ static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) card->interface.writebuf_skb = if_sendbuf; card->interface.writecmd = 0; card->interface.readstat = if_readstat; - card->interface.features = ISDN_FEATURE_L2_X75I | - ISDN_FEATURE_L2_X75UI | - ISDN_FEATURE_L2_X75BUI | - ISDN_FEATURE_L2_HDLC | - ISDN_FEATURE_L2_TRANS | - ISDN_FEATURE_L3_TRANS | - ISDN_FEATURE_L2_V11096 | - ISDN_FEATURE_L2_V11019 | - ISDN_FEATURE_L2_V11038 | - ISDN_FEATURE_P_UNKNOWN; + card->interface.features = ISDN_FEATURE_L2_HDLC | + ISDN_FEATURE_L2_TRANS | + ISDN_FEATURE_L3_TRANS | + ISDN_FEATURE_P_UNKNOWN | + ISDN_FEATURE_L2_X75I | + ISDN_FEATURE_L2_X75UI | + ISDN_FEATURE_L2_X75BUI; + if (profp->support1 & (1<<2)) + card->interface.features |= ISDN_FEATURE_L2_V11096 | + ISDN_FEATURE_L2_V11019 | + ISDN_FEATURE_L2_V11038; + if (profp->support1 & (1<<8)) + card->interface.features |= ISDN_FEATURE_L2_MODEM; card->interface.hl_hdrlen = 22; /* len of DATA_B3_REQ */ strncpy(card->interface.id, id, sizeof(card->interface.id) - 1); - card->next = global.contr_list; - global.contr_list = card; - global.ncontr++; + + card->q931_read = card->q931_buf; card->q931_write = card->q931_buf; card->q931_end = card->q931_buf + sizeof(card->q931_buf) - 1; if (!register_isdn(&card->interface)) { - global.contr_list = global.contr_list->next; printk(KERN_ERR "capidrv: Unable to register contr %s\n", id); kfree(card->bchans); kfree(card); + MOD_DEC_USE_COUNT; return -1; } card->myid = card->interface.channels; + spin_lock_irqsave(&global_lock, flags); + card->next = global.contr_list; + global.contr_list = card; + global.ncontr++; + spin_unlock_irqrestore(&global_lock, flags); + memset(card->bchans, 0, sizeof(capidrv_bchan) * card->nbchan); for (i = 0; i < card->nbchan; i++) { card->bchans[i].contr = card; } - cmd.driver = card->myid; cmd.command = ISDN_STAT_RUN; + cmd.driver = card->myid; card->interface.statcallb(&cmd); card->cipmask = 0x1FFF03FF; /* any */ @@ -2303,45 +2308,82 @@ static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) static int capidrv_delcontr(__u16 contr) { capidrv_contr **pp, *card; + unsigned long flags; isdn_ctrl cmd; - int i; - for (pp = &global.contr_list; *pp; pp = &(*pp)->next) { - if ((*pp)->contrnr == contr) + spin_lock_irqsave(&global_lock, flags); + for (card = global.contr_list; card; card = card->next) { + if (card->contrnr == contr) break; } - if (!*pp) { + if (!card) { + spin_unlock_irqrestore(&global_lock, flags); printk(KERN_ERR "capidrv: delcontr: no contr %u\n", contr); return -1; } - card = *pp; + spin_unlock_irqrestore(&global_lock, flags); + + del_timer(&card->listentimer); if (debugmode) printk(KERN_DEBUG "capidrv-%d: id=%d unloading\n", card->contrnr, card->myid); - cmd.command = ISDN_STAT_UNLOAD; + cmd.command = ISDN_STAT_STOP; cmd.driver = card->myid; card->interface.statcallb(&cmd); - *pp = (*pp)->next; - global.ncontr--; + while (card->nbchan) { - for (i = 0; i < card->nbchan; i++) { - if (card->bchans[i].nccip) - free_ncci(card, card->bchans[i].nccip); - if (card->bchans[i].plcip) - free_plci(card, card->bchans[i].plcip); + cmd.command = ISDN_STAT_DISCH; + cmd.driver = card->myid; + cmd.arg = card->nbchan-1; + cmd.parm.num[0] = 0; + if (debugmode) + printk(KERN_DEBUG "capidrv-%d: id=%d disable chan=%ld\n", + card->contrnr, card->myid, cmd.arg); + card->interface.statcallb(&cmd); + + if (card->bchans[card->nbchan-1].nccip) + free_ncci(card, card->bchans[card->nbchan-1].nccip); + if (card->bchans[card->nbchan-1].plcip) + free_plci(card, card->bchans[card->nbchan-1].plcip); if (card->plci_list) printk(KERN_ERR "capidrv: bug in free_plci()\n"); + card->nbchan--; } kfree(card->bchans); - del_timer(&card->listentimer); + card->bchans = 0; + + if (debugmode) + printk(KERN_DEBUG "capidrv-%d: id=%d isdn unload\n", + card->contrnr, card->myid); + + cmd.command = ISDN_STAT_UNLOAD; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + + if (debugmode) + printk(KERN_DEBUG "capidrv-%d: id=%d remove contr from list\n", + card->contrnr, card->myid); + + spin_lock_irqsave(&global_lock, flags); + for (pp = &global.contr_list; *pp; pp = &(*pp)->next) { + if (*pp == card) { + *pp = (*pp)->next; + card->next = 0; + global.ncontr--; + break; + } + } + spin_unlock_irqrestore(&global_lock, flags); printk(KERN_INFO "%s: now down.\n", card->name); kfree(card); + MOD_DEC_USE_COUNT; + return 0; } @@ -2438,10 +2480,14 @@ int capidrv_init(void) __u32 ncontr, contr; __u16 errcode; + MOD_INC_USE_COUNT; + capifuncs = attach_capi_interface(&cuser); - if (!capifuncs) + if (!capifuncs) { + MOD_DEC_USE_COUNT; return -EIO; + } if ((p = strchr(revision, ':'))) { strcpy(rev, p + 1); @@ -2456,6 +2502,7 @@ int capidrv_init(void) errcode = (*capifuncs->capi_register) (&rparam, &global.appid); if (errcode) { detach_capi_interface(&cuser); + MOD_DEC_USE_COUNT; return -EIO; } @@ -2463,6 +2510,7 @@ int capidrv_init(void) if (errcode != CAPI_NOERROR) { (void) (*capifuncs->capi_release) (global.appid); detach_capi_interface(&cuser); + MOD_DEC_USE_COUNT; return -EIO; } @@ -2477,17 +2525,19 @@ int capidrv_init(void) } proc_init(); + printk(KERN_NOTICE "capidrv: Rev%s: loaded\n", rev); + MOD_DEC_USE_COUNT; + return 0; } #ifdef MODULE void cleanup_module(void) { - capidrv_contr *card, *next; char rev[10]; char *p; - if ((p = strchr(revision, ':'))) { + if ((p = strchr(revision, ':')) != 0) { strcpy(rev, p + 1); p = strchr(rev, '$'); *p = 0; @@ -2495,14 +2545,10 @@ void cleanup_module(void) strcpy(rev, " ??? "); } - 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/capifs.c b/drivers/isdn/avmb1/capifs.c new file mode 100644 index 000000000000..073a788466bd --- /dev/null +++ b/drivers/isdn/avmb1/capifs.c @@ -0,0 +1,609 @@ +/* + * $Id: capifs.c,v 1.7 2000/06/18 16:09:54 keil Exp $ + * + * (c) Copyright 2000 by Carsten Paeth (calle@calle.de) + * + * Heavily based on devpts filesystem from H. Peter Anvin + * + * $Log: capifs.c,v $ + * Revision 1.7 2000/06/18 16:09:54 keil + * more changes for 2.4 + * + * Revision 1.6 2000/04/03 13:29:25 calle + * make Tim Waugh happy (module unload races in 2.3.99-pre3). + * no real problem there, but now it is much cleaner ... + * + * Revision 1.5 2000/03/13 17:49:52 calle + * make it running with 2.3.51. + * + * Revision 1.4 2000/03/08 17:06:33 calle + * - changes for devfs and 2.3.49 + * - capifs now configurable (no need with devfs) + * - New Middleware ioctl CAPI_NCCI_GETUNIT + * - Middleware again tested with 2.2.14 and 2.3.49 (with and without devfs) + * + * Revision 1.3 2000/03/06 18:00:23 calle + * - Middleware extention now working with 2.3.49 (capifs). + * - Fixed typos in debug section of capi.c + * - Bugfix: Makefile corrected for b1pcmcia.c + * + * Revision 1.2 2000/03/06 09:17:07 calle + * - capifs: fileoperations now in inode (change for 2.3.49) + * - Config.in: Middleware extention not a tristate, uups. + * + * Revision 1.1 2000/03/03 16:48:38 calle + * - Added CAPI2.0 Middleware support (CONFIG_ISDN_CAPI) + * It is now possible to create a connection with a CAPI2.0 applikation + * and than to handle the data connection from /dev/capi/ (capifs) and also + * using async or sync PPP on this connection. + * The two major device number 190 and 191 are not confirmed yet, + * but I want to save the code in cvs, before I go on. + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Carsten Paeth "); + +static char *revision = "$Revision: 1.7 $"; + +struct capifs_ncci { + struct inode *inode; + char used; + char type; + unsigned int num; + kdev_t kdev; +}; + +struct capifs_sb_info { + u32 magic; + struct super_block *next; + struct super_block **back; + int setuid; + int setgid; + uid_t uid; + gid_t gid; + umode_t mode; + + unsigned int max_ncci; + struct capifs_ncci *nccis; +}; + +#define CAPIFS_SUPER_MAGIC (('C'<<8)|'N') +#define CAPIFS_SBI_MAGIC (('C'<<24)|('A'<<16)|('P'<<8)|'I') + +static inline struct capifs_sb_info *SBI(struct super_block *sb) +{ + return (struct capifs_sb_info *)(sb->u.generic_sbp); +} + +/* ------------------------------------------------------------------ */ + +static int capifs_root_readdir(struct file *,void *,filldir_t); +static struct dentry *capifs_root_lookup(struct inode *,struct dentry *); +static int capifs_revalidate(struct dentry *, int); + +static struct file_operations capifs_root_operations = { + readdir: capifs_root_readdir, +}; + +struct inode_operations capifs_root_inode_operations = { + &capifs_root_operations, /* file operations */ + lookup: capifs_root_lookup, +}; + +struct inode_operations capifs_inode_operations; + +static struct dentry_operations capifs_dentry_operations = { + d_revalidate: capifs_revalidate, +}; + +/* + * /dev/capi/%d + * /dev/capi/r%d + */ + +static int capifs_root_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct inode * inode = filp->f_dentry->d_inode; + struct capifs_sb_info * sbi = SBI(filp->f_dentry->d_inode->i_sb); + off_t nr; + char numbuf[32]; + + nr = filp->f_pos; + + switch(nr) + { + case 0: + if (filldir(dirent, ".", 1, nr, inode->i_ino) < 0) + return 0; + filp->f_pos = ++nr; + /* fall through */ + case 1: + if (filldir(dirent, "..", 2, nr, inode->i_ino) < 0) + return 0; + filp->f_pos = ++nr; + /* fall through */ + default: + while (nr < sbi->max_ncci) { + int n = nr - 2; + struct capifs_ncci *np = &sbi->nccis[n]; + if (np->inode && np->used) { + char *p = numbuf; + if (np->type) *p++ = np->type; + sprintf(p, "%u", np->num); + if ( filldir(dirent, numbuf, strlen(numbuf), nr, nr) < 0 ) + return 0; + } + filp->f_pos = ++nr; + } + break; + } + + return 0; +} + +/* + * Revalidate is called on every cache lookup. We use it to check that + * the ncci really does still exist. Never revalidate negative dentries; + * for simplicity (fix later?) + */ +static int capifs_revalidate(struct dentry * dentry, int flags) +{ + struct capifs_sb_info *sbi; + + if ( !dentry->d_inode ) + return 0; + + sbi = SBI(dentry->d_inode->i_sb); + + return ( sbi->nccis[dentry->d_inode->i_ino - 2].inode == dentry->d_inode ); +} + +static struct dentry *capifs_root_lookup(struct inode * dir, struct dentry * dentry) +{ + struct capifs_sb_info *sbi = SBI(dir->i_sb); + struct capifs_ncci *np; + unsigned int i; + char numbuf[32]; + char *p, *tmp; + unsigned int num; + char type = 0; + + dentry->d_inode = NULL; /* Assume failure */ + dentry->d_op = &capifs_dentry_operations; + + if (dentry->d_name.len >= sizeof(numbuf) ) + return NULL; + strncpy(numbuf, dentry->d_name.name, dentry->d_name.len); + numbuf[dentry->d_name.len] = 0; + p = numbuf; + if (!isdigit(*p)) type = *p++; + tmp = p; + num = (unsigned int)simple_strtoul(p, &tmp, 10); + if (tmp == p || *tmp) + return NULL; + + for (i = 0, np = sbi->nccis ; i < sbi->max_ncci; i++, np++) { + if (np->used && np->num == num && np->type == type) + break; + } + + if ( i >= sbi->max_ncci ) + return NULL; + + dentry->d_inode = np->inode; + if ( dentry->d_inode ) + dentry->d_inode->i_count++; + + d_add(dentry, dentry->d_inode); + + return NULL; +} + +/* ------------------------------------------------------------------ */ + +static struct super_block *mounts = NULL; + +static void capifs_put_super(struct super_block *sb) +{ + struct capifs_sb_info *sbi = SBI(sb); + struct inode *inode; + int i; + + for ( i = 0 ; i < sbi->max_ncci ; i++ ) { + if ( (inode = sbi->nccis[i].inode) ) { + if (inode->i_count != 1 ) + printk("capifs_put_super: badness: entry %d count %d\n", + i, (unsigned)inode->i_count); + inode->i_nlink--; + iput(inode); + } + } + + *sbi->back = sbi->next; + if ( sbi->next ) + SBI(sbi->next)->back = sbi->back; + + kfree(sbi->nccis); + kfree(sbi); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,51) + MOD_DEC_USE_COUNT; +#endif +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,51) +static int capifs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz); +static void capifs_write_inode(struct inode *inode) { }; +#else +static int capifs_statfs(struct super_block *sb, struct statfs *buf); +#endif +static void capifs_read_inode(struct inode *inode); + +static struct super_operations capifs_sops = { + read_inode: capifs_read_inode, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,51) + write_inode: capifs_write_inode, +#endif + put_super: capifs_put_super, + statfs: capifs_statfs, +}; + +static int capifs_parse_options(char *options, struct capifs_sb_info *sbi) +{ + int setuid = 0; + int setgid = 0; + uid_t uid = 0; + gid_t gid = 0; + umode_t mode = 0600; + unsigned int maxncci = 512; + char *this_char, *value; + + this_char = NULL; + if ( options ) + this_char = strtok(options,","); + for ( ; this_char; this_char = strtok(NULL,",")) { + if ((value = strchr(this_char,'=')) != NULL) + *value++ = 0; + if (!strcmp(this_char,"uid")) { + if (!value || !*value) + return 1; + uid = simple_strtoul(value,&value,0); + if (*value) + return 1; + setuid = 1; + } + else if (!strcmp(this_char,"gid")) { + if (!value || !*value) + return 1; + gid = simple_strtoul(value,&value,0); + if (*value) + return 1; + setgid = 1; + } + else if (!strcmp(this_char,"mode")) { + if (!value || !*value) + return 1; + mode = simple_strtoul(value,&value,8); + if (*value) + return 1; + } + else if (!strcmp(this_char,"maxncci")) { + if (!value || !*value) + return 1; + maxncci = simple_strtoul(value,&value,8); + if (*value) + return 1; + } + else + return 1; + } + sbi->setuid = setuid; + sbi->setgid = setgid; + sbi->uid = uid; + sbi->gid = gid; + sbi->mode = mode & ~S_IFMT; + sbi->max_ncci = maxncci; + + return 0; +} + +struct super_block *capifs_read_super(struct super_block *s, void *data, + int silent) +{ + struct inode * root_inode; + struct dentry * root; + struct capifs_sb_info *sbi; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,51) + MOD_INC_USE_COUNT; + lock_super(s); +#endif + /* Super block already completed? */ + if (s->s_root) + goto out; + + sbi = (struct capifs_sb_info *) kmalloc(sizeof(struct capifs_sb_info), GFP_KERNEL); + if ( !sbi ) + goto fail; + + memset(sbi, 0, sizeof(struct capifs_sb_info)); + sbi->magic = CAPIFS_SBI_MAGIC; + + if ( capifs_parse_options(data,sbi) ) { + kfree(sbi); + printk("capifs: called with bogus options\n"); + goto fail; + } + + sbi->nccis = kmalloc(sizeof(struct capifs_ncci) * sbi->max_ncci, GFP_KERNEL); + if ( !sbi->nccis ) { + kfree(sbi); + goto fail; + } + memset(sbi->nccis, 0, sizeof(struct capifs_ncci) * sbi->max_ncci); + + s->u.generic_sbp = (void *) sbi; + s->s_blocksize = 1024; + s->s_blocksize_bits = 10; + s->s_magic = CAPIFS_SUPER_MAGIC; + s->s_op = &capifs_sops; + s->s_root = NULL; + + /* + * Get the root inode and dentry, but defer checking for errors. + */ + root_inode = iget(s, 1); /* inode 1 == root directory */ + root = d_alloc_root(root_inode, NULL); + + /* + * Check whether somebody else completed the super block. + */ + if (s->s_root) { + if (root) dput(root); + else iput(root_inode); + goto out; + } + + if (!root) { + printk("capifs: get root dentry failed\n"); + /* + * iput() can block, so we clear the super block first. + */ + iput(root_inode); + kfree(sbi->nccis); + kfree(sbi); + goto fail; + } + + /* + * Check whether somebody else completed the super block. + */ + if (s->s_root) + goto out; + + /* + * Success! Install the root dentry now to indicate completion. + */ + s->s_root = root; + + sbi->next = mounts; + if ( sbi->next ) + SBI(sbi->next)->back = &(sbi->next); + sbi->back = &mounts; + mounts = s; + +out: /* Success ... somebody else completed the super block for us. */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,51) + unlock_super(s); +#endif + return s; +fail: +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,51) + unlock_super(s); + MOD_DEC_USE_COUNT; +#endif + return NULL; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,51) +static int capifs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) +{ + struct statfs tmp; + + tmp.f_type = CAPIFS_SUPER_MAGIC; + tmp.f_bsize = 1024; + tmp.f_blocks = 0; + tmp.f_bfree = 0; + tmp.f_bavail = 0; + tmp.f_files = 0; + tmp.f_ffree = 0; + tmp.f_namelen = NAME_MAX; + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; +} +#else +static int capifs_statfs(struct super_block *sb, struct statfs *buf) +{ + buf->f_type = CAPIFS_SUPER_MAGIC; + buf->f_bsize = 1024; + buf->f_blocks = 0; + buf->f_bfree = 0; + buf->f_bavail = 0; + buf->f_files = 0; + buf->f_ffree = 0; + buf->f_namelen = NAME_MAX; + return 0; +} +#endif + +static void capifs_read_inode(struct inode *inode) +{ + ino_t ino = inode->i_ino; + struct capifs_sb_info *sbi = SBI(inode->i_sb); + + inode->i_op = NULL; + inode->i_mode = 0; + inode->i_nlink = 0; + inode->i_size = 0; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + inode->i_blocks = 0; + inode->i_blksize = 1024; + inode->i_uid = inode->i_gid = 0; + + if ( ino == 1 ) { + inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR; + inode->i_op = &capifs_root_inode_operations; + inode->i_nlink = 2; + return; + } + + + ino -= 2; + if ( ino >= sbi->max_ncci ) + return; /* Bogus */ + + inode->i_mode = S_IFCHR; + inode->i_op = &chrdev_inode_operations; + + return; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,51) +static struct file_system_type capifs_fs_type = { + "capifs", + 0, + capifs_read_super, + NULL +}; +#else +static DECLARE_FSTYPE(capifs_fs_type, "capifs", capifs_read_super, 0); +#endif + +void capifs_new_ncci(char type, unsigned int num, kdev_t device) +{ + struct super_block *sb; + struct capifs_sb_info *sbi; + struct capifs_ncci *np; + ino_t ino; + + for ( sb = mounts ; sb ; sb = sbi->next ) { + sbi = SBI(sb); + + for (ino = 0, np = sbi->nccis ; ino < sbi->max_ncci; ino++, np++) { + if (np->used == 0) { + np->used = 1; + np->type = type; + np->num = num; + np->kdev = device; + break; + } + } + + if ((np->inode = iget(sb, ino+2)) != 0) { + struct inode *inode = np->inode; + inode->i_uid = sbi->setuid ? sbi->uid : current->fsuid; + inode->i_gid = sbi->setgid ? sbi->gid : current->fsgid; + inode->i_mode = sbi->mode | S_IFCHR; + inode->i_rdev = np->kdev; + inode->i_nlink++; + } + } +} + +void capifs_free_ncci(char type, unsigned int num) +{ + struct super_block *sb; + struct capifs_sb_info *sbi; + struct inode *inode; + struct capifs_ncci *np; + ino_t ino; + + for ( sb = mounts ; sb ; sb = sbi->next ) { + sbi = SBI(sb); + + for (ino = 0, np = sbi->nccis ; ino < sbi->max_ncci; ino++, np++) { + if (!np->used || np->type != type || np->num != num) + continue; + if (np->inode) { + inode = np->inode; + np->inode = 0; + np->used = 0; + inode->i_nlink--; + iput(inode); + break; + } + } + } +} + +int __init capifs_init(void) +{ + char rev[10]; + char *p; + int err; + + MOD_INC_USE_COUNT; + + if ((p = strchr(revision, ':'))) { + strcpy(rev, p + 1); + p = strchr(rev, '$'); + *p = 0; + } else + strcpy(rev, "1.0"); + + err = register_filesystem(&capifs_fs_type); + if (err) { + MOD_DEC_USE_COUNT; + return err; + } +#ifdef MODULE + printk(KERN_NOTICE "capifs: Rev%s: loaded\n", rev); +#else + printk(KERN_NOTICE "capifs: Rev%s: started\n", rev); +#endif + MOD_DEC_USE_COUNT; + return 0; +} + +void capifs_exit(void) +{ + unregister_filesystem(&capifs_fs_type); +} + +EXPORT_SYMBOL(capifs_new_ncci); +EXPORT_SYMBOL(capifs_free_ncci); + +#ifdef MODULE + +int init_module(void) +{ + return capifs_init(); +} + +void cleanup_module(void) +{ + capifs_exit(); +} + +#endif diff --git a/drivers/isdn/avmb1/capifs.h b/drivers/isdn/avmb1/capifs.h new file mode 100644 index 000000000000..f6b8072ed3e4 --- /dev/null +++ b/drivers/isdn/avmb1/capifs.h @@ -0,0 +1,25 @@ +/* + * $Id: capifs.h,v 1.2 2000/03/08 17:06:33 calle Exp $ + * + * (c) Copyright 2000 by Carsten Paeth (calle@calle.de) + * + * $Log: capifs.h,v $ + * Revision 1.2 2000/03/08 17:06:33 calle + * - changes for devfs and 2.3.49 + * - capifs now configurable (no need with devfs) + * - New Middleware ioctl CAPI_NCCI_GETUNIT + * - Middleware again tested with 2.2.14 and 2.3.49 (with and without devfs) + * + * Revision 1.1 2000/03/03 16:48:38 calle + * - Added CAPI2.0 Middleware support (CONFIG_ISDN_CAPI) + * It is now possible to create a connection with a CAPI2.0 applikation + * and than to handle the data connection from /dev/capi/ (capifs) and also + * using async or sync PPP on this connection. + * The two major device number 190 and 191 are not confirmed yet, + * but I want to save the code in cvs, before I go on. + * + * + */ + +void capifs_new_ncci(char type, unsigned int num, kdev_t device); +void capifs_free_ncci(char type, unsigned int num); diff --git a/drivers/isdn/avmb1/capiutil.c b/drivers/isdn/avmb1/capiutil.c index 3482f2a97cbd..6f10025f52f4 100644 --- a/drivers/isdn/avmb1/capiutil.c +++ b/drivers/isdn/avmb1/capiutil.c @@ -1,5 +1,5 @@ /* - * $Id: capiutil.c,v 1.10 1999/08/31 11:19:54 paul Exp $ + * $Id: capiutil.c,v 1.11 2000/03/03 15:50:42 calle Exp $ * * CAPI 2.0 convert capi message to capi message struct * @@ -7,6 +7,17 @@ * Rewritten for Linux 1996 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capiutil.c,v $ + * Revision 1.11 2000/03/03 15:50:42 calle + * - kernel CAPI: + * - Changed parameter "param" in capi_signal from __u32 to void *. + * - rewrote notifier handling in kcapi.c + * - new notifier NCCI_UP and NCCI_DOWN + * - User CAPI: + * - /dev/capi20 is now a cloning device. + * - middleware extentions prepared. + * - capidrv.c + * - locking of list operations and module count updates. + * * Revision 1.10 1999/08/31 11:19:54 paul * various spelling corrections (new checksums may be needed, Karsten!) * @@ -794,7 +805,7 @@ static char *pnames[] = /*15 */ "Class", /*16 */ "ConnectedNumber", /*17 */ "ConnectedSubaddress", - /*18 */ "Data", + /*18 */ "Data32", /*19 */ "DataHandle", /*1a */ "DataLength", /*1b */ "FacilityConfirmationParameter", @@ -892,13 +903,7 @@ static void protocol_message_2_pars(_cmsg * cmsg, int level) cmsg->l += 2; break; case _CDWORD: - if (strcmp(NAME, "Data") == 0) { - bufprint("%-*s = ", slen, NAME); - printstructlen((__u8 *) * (__u32 *) (cmsg->m + cmsg->l), - *(__u16 *) (cmsg->m + cmsg->l + sizeof(__u32))); - bufprint("\n"); - } else - bufprint("%-*s = 0x%lx\n", slen, NAME, *(__u32 *) (cmsg->m + cmsg->l)); + bufprint("%-*s = 0x%lx\n", slen, NAME, *(__u32 *) (cmsg->m + cmsg->l)); cmsg->l += 4; break; case _CSTRUCT: diff --git a/drivers/isdn/avmb1/capiutil.h b/drivers/isdn/avmb1/capiutil.h index 3825ac308378..8435ede452c9 100644 --- a/drivers/isdn/avmb1/capiutil.h +++ b/drivers/isdn/avmb1/capiutil.h @@ -1,5 +1,5 @@ /* - * $Id: capiutil.h,v 1.4 1999/09/15 08:16:03 calle Exp $ + * $Id: capiutil.h,v 1.5 2000/03/03 15:50:42 calle Exp $ * * CAPI 2.0 defines & types * @@ -7,6 +7,17 @@ * Rewritten for Linux 1996 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capiutil.h,v $ + * Revision 1.5 2000/03/03 15:50:42 calle + * - kernel CAPI: + * - Changed parameter "param" in capi_signal from __u32 to void *. + * - rewrote notifier handling in kcapi.c + * - new notifier NCCI_UP and NCCI_DOWN + * - User CAPI: + * - /dev/capi20 is now a cloning device. + * - middleware extentions prepared. + * - capidrv.c + * - locking of list operations and module count updates. + * * Revision 1.4 1999/09/15 08:16:03 calle * Implementation of 64Bit extention complete. * @@ -36,28 +47,47 @@ #include -#define CAPIMSG_LEN(m) (m[0] | (m[1] << 8)) -#define CAPIMSG_APPID(m) (m[2] | (m[3] << 8)) -#define CAPIMSG_COMMAND(m) (m[4]) -#define CAPIMSG_SUBCOMMAND(m) (m[5]) -#define CAPIMSG_MSGID(m) (m[6] | (m[7] << 8)) +#define CAPIMSG_BASELEN 8 +#define CAPIMSG_U8(m, off) (m[off]) +#define CAPIMSG_U16(m, off) (m[off]|(m[(off)+1]<<8)) +#define CAPIMSG_U32(m, off) (m[off]|(m[(off)+1]<<8)|(m[(off)+2]<<16)|(m[(off)+3]<<24)) +#define CAPIMSG_LEN(m) CAPIMSG_U16(m,0) +#define CAPIMSG_APPID(m) CAPIMSG_U16(m,2) +#define CAPIMSG_COMMAND(m) CAPIMSG_U8(m,4) +#define CAPIMSG_SUBCOMMAND(m) CAPIMSG_U8(m,5) +#define CAPIMSG_CMD(m) (((m[4])<<8)|(m[5])) +#define CAPIMSG_MSGID(m) CAPIMSG_U16(m,6) #define CAPIMSG_CONTROLLER(m) (m[8] & 0x7f) -#define CAPIMSG_CONTROL(m) (m[8]|(m[9]<<8)|(m[10]<<16)|(m[11]<<24)) +#define CAPIMSG_CONTROL(m) CAPIMSG_U32(m, 8) #define CAPIMSG_NCCI(m) CAPIMSG_CONTROL(m) -#define CAPIMSG_DATA(m) (m[12]|(m[13]<<8)|(m[14]<<16)|(m[15]<<24)) -#define CAPIMSG_DATALEN(m) (m[16] | (m[17]<<8)) - -#define CAPIMSG_SETAPPID(m, applid) \ - do { \ - ((__u8 *)m)[2] = (__u16)(applid) & 0xff; \ - ((__u8 *)m)[3] = ((__u16)(applid) >> 8) & 0xff; \ - } while (0) - -#define CAPIMSG_SETLEN(m, len) \ - do { \ - ((__u8 *)m)[0] = (__u16)(len) & 0xff; \ - ((__u8 *)m)[1] = ((__u16)(len) >> 8) & 0xff; \ - } while (0) +#define CAPIMSG_DATALEN(m) CAPIMSG_U16(m,16) /* DATA_B3_REQ */ + +static inline void capimsg_setu8(void *m, int off, __u8 val) +{ + ((__u8 *)m)[off] = val; +} + +static inline void capimsg_setu16(void *m, int off, __u16 val) +{ + ((__u8 *)m)[off] = val & 0xff; + ((__u8 *)m)[off+1] = (val >> 8) & 0xff; +} + +static inline void capimsg_setu32(void *m, int off, __u32 val) +{ + ((__u8 *)m)[off] = val & 0xff; + ((__u8 *)m)[off+1] = (val >> 8) & 0xff; + ((__u8 *)m)[off+2] = (val >> 16) & 0xff; + ((__u8 *)m)[off+3] = (val >> 24) & 0xff; +} + +#define CAPIMSG_SETLEN(m, len) capimsg_setu16(m, 0, len) +#define CAPIMSG_SETAPPID(m, applid) capimsg_setu16(m, 2, applid) +#define CAPIMSG_SETCOMMAND(m,cmd) capimsg_setu8(m, 4, cmd) +#define CAPIMSG_SETSUBCOMMAND(m, cmd) capimsg_setu8(m, 5, cmd) +#define CAPIMSG_SETMSGID(m, msgid) capimsg_setu16(m, 6, msgid) +#define CAPIMSG_SETCONTROL(m, contr) capimsg_setu32(m, 8, contr) +#define CAPIMSG_SETDATALEN(m, len) capimsg_setu16(m, 16, len) /*----- basic-type definitions -----*/ diff --git a/drivers/isdn/avmb1/kcapi.c b/drivers/isdn/avmb1/kcapi.c index 67fe63fb3632..9a0d92f86a52 100644 --- a/drivers/isdn/avmb1/kcapi.c +++ b/drivers/isdn/avmb1/kcapi.c @@ -1,11 +1,39 @@ /* - * $Id: kcapi.c,v 1.12 2000/01/28 16:45:39 calle Exp $ + * $Id: kcapi.c,v 1.17 2000/04/21 13:00:56 calle Exp $ * * Kernel CAPI 2.0 Module * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: kcapi.c,v $ + * Revision 1.17 2000/04/21 13:00:56 calle + * Bugfix: driver_proc_info was also wrong. + * + * Revision 1.16 2000/04/21 12:38:42 calle + * Bugfix: error in proc_ functions, begin-off => off-begin + * + * Revision 1.15 2000/04/06 15:01:25 calle + * Bugfix: crash in capidrv.c when reseting a capi controller. + * - changed code order on remove of controller. + * - using tq_schedule for notifier in kcapi.c. + * - now using spin_lock_irqsave() and spin_unlock_irqrestore(). + * strange: sometimes even MP hang on unload of isdn.o ... + * + * Revision 1.14 2000/04/03 13:29:25 calle + * make Tim Waugh happy (module unload races in 2.3.99-pre3). + * no real problem there, but now it is much cleaner ... + * + * Revision 1.13 2000/03/03 15:50:42 calle + * - kernel CAPI: + * - Changed parameter "param" in capi_signal from __u32 to void *. + * - rewrote notifier handling in kcapi.c + * - new notifier NCCI_UP and NCCI_DOWN + * - User CAPI: + * - /dev/capi20 is now a cloning device. + * - middleware extentions prepared. + * - capidrv.c + * - locking of list operations and module count updates. + * * Revision 1.12 2000/01/28 16:45:39 calle * new manufacturer command KCAPI_CMD_ADDCARD (generic addcard), * will search named driver and call the add_card function if one exist. @@ -78,6 +106,7 @@ #include #include #include +#include #include #include "capicmd.h" #include "capiutil.h" @@ -86,7 +115,7 @@ #include #endif -static char *revision = "$Revision: 1.12 $"; +static char *revision = "$Revision: 1.17 $"; /* ------------------------------------------------------------- */ @@ -125,8 +154,8 @@ struct capi_appl { __u16 applid; capi_register_params rparam; int releasing; - __u32 param; - void (*signal) (__u16 applid, __u32 param); + void *param; + void (*signal) (__u16 applid, void *param); struct sk_buff_head recv_queue; int nncci; struct capi_ncci *nccilist; @@ -137,6 +166,14 @@ struct capi_appl { unsigned long nsentdatapkt; }; +struct capi_notifier { + struct capi_notifier *next; + unsigned int cmd; + __u32 controller; + __u16 applid; + __u32 ncci; +}; + /* ------------------------------------------------------------- */ static struct capi_version driver_version = {2, 0, 1, 1<<4}; @@ -160,9 +197,9 @@ 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 spinlock_t capi_users_lock = SPIN_LOCK_UNLOCKED; static struct capi_driver *drivers; -static long notify_up_set = 0; -static long notify_down_set = 0; +static spinlock_t drivers_lock = SPIN_LOCK_UNLOCKED; static struct tq_struct tq_state_notify; static struct tq_struct tq_recv_notify; @@ -304,6 +341,7 @@ static int proc_driver_read_proc(char *page, char **start, off_t off, int len = 0; off_t begin = 0; + spin_lock(&drivers_lock); for (driver = drivers; driver; driver = driver->next) { len += sprintf(page+len, "%-32s %d %s\n", driver->name, @@ -317,6 +355,7 @@ static int proc_driver_read_proc(char *page, char **start, off_t off, } } endloop: + spin_unlock(&drivers_lock); if (!driver) *eof = 1; if (off >= len+begin) @@ -336,6 +375,7 @@ static int proc_users_read_proc(char *page, char **start, off_t off, int len = 0; off_t begin = 0; + spin_lock(&capi_users_lock); for (cp = capi_users; cp ; cp = cp->next) { len += sprintf(page+len, "%s\n", cp->name); if (len+begin > off+count) @@ -346,6 +386,7 @@ static int proc_users_read_proc(char *page, char **start, off_t off, } } endloop: + spin_unlock(&capi_users_lock); if (cp == 0) *eof = 1; if (off >= len+begin) @@ -510,6 +551,167 @@ static void proc_capi_exit(void) } } +/* -------- Notifier handling --------------------------------- */ + +static struct capi_notifier_list{ + struct capi_notifier *head; + struct capi_notifier *tail; +} notifier_list; + +static spinlock_t notifier_lock = SPIN_LOCK_UNLOCKED; + +static inline void notify_enqueue(struct capi_notifier *np) +{ + struct capi_notifier_list *q = ¬ifier_list; + unsigned long flags; + + spin_lock_irqsave(¬ifier_lock, flags); + if (q->tail) { + q->tail->next = np; + q->tail = np; + } else { + q->head = q->tail = np; + } + spin_unlock_irqrestore(¬ifier_lock, flags); +} + +static inline struct capi_notifier *notify_dequeue(void) +{ + struct capi_notifier_list *q = ¬ifier_list; + struct capi_notifier *np = 0; + unsigned long flags; + + spin_lock_irqsave(¬ifier_lock, flags); + if (q->head) { + np = q->head; + if ((q->head = np->next) == 0) + q->tail = 0; + np->next = 0; + } + spin_unlock_irqrestore(¬ifier_lock, flags); + return np; +} + +static int notify_push(unsigned int cmd, __u32 controller, + __u16 applid, __u32 ncci) +{ + struct capi_notifier *np; + + MOD_INC_USE_COUNT; + np = (struct capi_notifier *)kmalloc(sizeof(struct capi_notifier), GFP_ATOMIC); + if (!np) { + MOD_DEC_USE_COUNT; + return -1; + } + memset(np, 0, sizeof(struct capi_notifier)); + np->cmd = cmd; + np->controller = controller; + np->applid = applid; + np->ncci = ncci; + notify_enqueue(np); + /* + * The notifier will result in adding/deleteing + * of devices. Devices can only removed in + * user process, not in bh. + */ + queue_task(&tq_state_notify, &tq_scheduler); + return 0; +} + +/* -------- KCI_CONTRUP --------------------------------------- */ + +static void notify_up(__u32 contr) +{ + struct capi_interface_user *p; + + printk(KERN_NOTICE "kcapi: notify up contr %d\n", contr); + spin_lock(&capi_users_lock); + for (p = capi_users; p; p = p->next) { + if (!p->callback) continue; + (*p->callback) (KCI_CONTRUP, contr, &CARD(contr)->profile); + } + spin_unlock(&capi_users_lock); +} + +/* -------- KCI_CONTRDOWN ------------------------------------- */ + +static void notify_down(__u32 contr) +{ + struct capi_interface_user *p; + printk(KERN_NOTICE "kcapi: notify down contr %d\n", contr); + spin_lock(&capi_users_lock); + for (p = capi_users; p; p = p->next) { + if (!p->callback) continue; + (*p->callback) (KCI_CONTRDOWN, contr, 0); + } + spin_unlock(&capi_users_lock); +} + +/* -------- KCI_NCCIUP ---------------------------------------- */ + +static void notify_ncciup(__u32 contr, __u16 applid, __u32 ncci) +{ + struct capi_interface_user *p; + struct capi_ncciinfo n; + n.applid = applid; + n.ncci = ncci; + /*printk(KERN_NOTICE "kcapi: notify up contr %d\n", contr);*/ + spin_lock(&capi_users_lock); + for (p = capi_users; p; p = p->next) { + if (!p->callback) continue; + (*p->callback) (KCI_NCCIUP, contr, &n); + } + spin_unlock(&capi_users_lock); +}; + +/* -------- KCI_NCCIDOWN -------------------------------------- */ + +static void notify_nccidown(__u32 contr, __u16 applid, __u32 ncci) +{ + struct capi_interface_user *p; + struct capi_ncciinfo n; + n.applid = applid; + n.ncci = ncci; + /*printk(KERN_NOTICE "kcapi: notify down contr %d\n", contr);*/ + spin_lock(&capi_users_lock); + for (p = capi_users; p; p = p->next) { + if (!p->callback) continue; + (*p->callback) (KCI_NCCIDOWN, contr, &n); + } + spin_unlock(&capi_users_lock); +}; + +/* ------------------------------------------------------------ */ + +static void inline notify_doit(struct capi_notifier *np) +{ + switch (np->cmd) { + case KCI_CONTRUP: + notify_up(np->controller); + break; + case KCI_CONTRDOWN: + notify_down(np->controller); + break; + case KCI_NCCIUP: + notify_ncciup(np->controller, np->applid, np->ncci); + break; + case KCI_NCCIDOWN: + notify_nccidown(np->controller, np->applid, np->ncci); + break; + } +} + +static void notify_handler(void *dummy) +{ + struct capi_notifier *np; + + while ((np = notify_dequeue()) != 0) { + notify_doit(np); + kfree(np); + MOD_DEC_USE_COUNT; + } +} + /* -------- NCCI Handling ------------------------------------- */ static inline void mq_init(struct capi_ncci * np) @@ -617,6 +819,7 @@ static void controllercb_new_ncci(struct capi_ctr * card, APPL(appl)->nncci++; printk(KERN_INFO "kcapi: appl %d ncci 0x%x up\n", appl, ncci); + notify_push(KCI_NCCIUP, CARDNR(card), appl, ncci); } static void controllercb_free_ncci(struct capi_ctr * card, @@ -634,6 +837,7 @@ static void controllercb_free_ncci(struct capi_ctr * card, kfree(np); APPL(appl)->nncci--; printk(KERN_INFO "kcapi: appl %d ncci 0x%x down\n", appl, ncci); + notify_push(KCI_NCCIDOWN, CARDNR(card), appl, ncci); return; } } @@ -734,42 +938,6 @@ error: kfree_skb(skb); } -/* -------- Notifier ------------------------------------------ */ - -static void notify_up(__u32 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(__u32 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) -{ - __u32 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; @@ -782,11 +950,10 @@ static void controllercb_ready(struct capi_ctr * card) 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); + + notify_push(KCI_CONTRUP, CARDNR(card), 0, 0); } static void controllercb_reseted(struct capi_ctr * card) @@ -812,6 +979,7 @@ static void controllercb_reseted(struct capi_ctr * card) struct capi_ncci *np = *pp; *pp = np->next; printk(KERN_INFO "kcapi: appl %d ncci 0x%x forced down!\n", appl, np->ncci); + notify_push(KCI_NCCIDOWN, CARDNR(card), appl, np->ncci); kfree(np); nextpp = pp; } else { @@ -819,9 +987,10 @@ static void controllercb_reseted(struct capi_ctr * card) } } } - set_bit(CARDNR(card), ¬ify_down_set); - queue_task(&tq_state_notify, &tq_scheduler); + printk(KERN_NOTICE "kcapi: card %d down.\n", CARDNR(card)); + + notify_push(KCI_CONTRDOWN, CARDNR(card), 0, 0); } static void controllercb_suspend_output(struct capi_ctr *card) @@ -937,7 +1106,7 @@ static int driver_read_proc(char *page, char **start, off_t off, if (len < off) return 0; *eof = 1; - *start = page - off; + *start = page + off; return ((count < len-off) ? count : len-off); } @@ -952,9 +1121,12 @@ struct capi_driver_interface *attach_capi_driver(struct capi_driver *driver) { struct capi_driver **pp; + MOD_INC_USE_COUNT; + spin_lock(&drivers_lock); for (pp = &drivers; *pp; pp = &(*pp)->next) ; driver->next = 0; *pp = driver; + spin_unlock(&drivers_lock); printk(KERN_NOTICE "kcapi: driver %s attached\n", driver->name); sprintf(driver->procfn, "capi/drivers/%s", driver->name); driver->procent = create_proc_entry(driver->procfn, 0, 0); @@ -974,6 +1146,7 @@ struct capi_driver_interface *attach_capi_driver(struct capi_driver *driver) void detach_capi_driver(struct capi_driver *driver) { struct capi_driver **pp; + spin_lock(&drivers_lock); for (pp = &drivers; *pp && *pp != driver; pp = &(*pp)->next) ; if (*pp) { *pp = (*pp)->next; @@ -981,10 +1154,12 @@ void detach_capi_driver(struct capi_driver *driver) } else { printk(KERN_ERR "kcapi: driver %s double detach ?\n", driver->name); } + spin_unlock(&drivers_lock); if (driver->procent) { remove_proc_entry(driver->procfn, 0); driver->procent = 0; } + MOD_DEC_USE_COUNT; } /* ------------------------------------------------------------- */ @@ -1041,6 +1216,7 @@ static __u16 capi_release(__u16 applid) if (!VALID_APPLID(applid) || APPL(applid)->releasing) return CAPI_ILLAPPNR; + APPL(applid)->releasing++; while ((skb = skb_dequeue(&APPL(applid)->recv_queue)) != 0) kfree_skb(skb); for (i = 0; i < CAPI_MAXCONTR; i++) { @@ -1049,6 +1225,7 @@ static __u16 capi_release(__u16 applid) APPL(applid)->releasing++; cards[i].driver->release_appl(&cards[i], applid); } + APPL(applid)->releasing--; if (APPL(applid)->releasing <= 0) { APPL(applid)->signal = 0; APPL_MARK_FREE(applid); @@ -1128,8 +1305,8 @@ static __u16 capi_get_message(__u16 applid, struct sk_buff **msgp) } static __u16 capi_set_signal(__u16 applid, - void (*signal) (__u16 applid, __u32 param), - __u32 param) + void (*signal) (__u16 applid, void *param), + void *param) { if (!VALID_APPLID(applid)) return CAPI_ILLAPPNR; @@ -1194,10 +1371,12 @@ static __u16 capi_get_profile(__u32 contr, struct capi_profile *profp) static struct capi_driver *find_driver(char *name) { struct capi_driver *dp; + spin_lock(&drivers_lock); for (dp = drivers; dp; dp = dp->next) if (strcmp(dp->name, name) == 0) - return dp; - return 0; + break; + spin_unlock(&drivers_lock); + return dp; } #ifdef CONFIG_AVMB1_COMPAT @@ -1272,6 +1451,10 @@ static int old_capi_manufacturer(unsigned int cmd, void *data) card = CARD(ldef.contr); if (card->cardstate == CARD_FREE) return -ESRCH; + if (card->driver->load_firmware == 0) { + printk(KERN_DEBUG "kcapi: load: driver \%s\" has no load function\n", card->driver->name); + return -ESRCH; + } if (ldef.t4file.len <= 0) { printk(KERN_DEBUG "kcapi: load: invalid parameter: length of t4file is %d ?\n", ldef.t4file.len); @@ -1490,16 +1673,20 @@ struct capi_interface *attach_capi_interface(struct capi_interface_user *userp) { struct capi_interface_user *p; + MOD_INC_USE_COUNT; + spin_lock(&capi_users_lock); for (p = capi_users; p; p = p->next) { if (p == userp) { + spin_unlock(&capi_users_lock); printk(KERN_ERR "kcapi: double attach from %s\n", userp->name); + MOD_DEC_USE_COUNT; return 0; } } userp->next = capi_users; capi_users = userp; - MOD_INC_USE_COUNT; + spin_unlock(&capi_users_lock); printk(KERN_NOTICE "kcapi: %s attached\n", userp->name); return &avmb1_interface; @@ -1509,15 +1696,18 @@ int detach_capi_interface(struct capi_interface_user *userp) { struct capi_interface_user **pp; + spin_lock(&capi_users_lock); for (pp = &capi_users; *pp; pp = &(*pp)->next) { if (*pp == userp) { *pp = userp->next; + spin_unlock(&capi_users_lock); userp->next = 0; - MOD_DEC_USE_COUNT; printk(KERN_NOTICE "kcapi: %s detached\n", userp->name); + MOD_DEC_USE_COUNT; return 0; } } + spin_unlock(&capi_users_lock); printk(KERN_ERR "kcapi: double detach from %s\n", userp->name); return -1; } @@ -1565,6 +1755,8 @@ int kcapi_init(void) char *p; char rev[10]; + MOD_INC_USE_COUNT; + skb_queue_head_init(&recv_queue); /* init_bh(CAPI_BH, do_capi_bh); */ @@ -1606,6 +1798,7 @@ int kcapi_init(void) (void)c4_init(); #endif #endif + MOD_DEC_USE_COUNT; return 0; } @@ -1623,7 +1816,6 @@ void cleanup_module(void) strcpy(rev, "1.0"); } - schedule(); /* execute queued tasks .... */ proc_capi_exit(); printk(KERN_NOTICE "CAPI-driver Rev%s: unloaded\n", rev); } diff --git a/drivers/isdn/avmb1/t1isa.c b/drivers/isdn/avmb1/t1isa.c index f2da3dc86f03..f8f334e759e7 100644 --- a/drivers/isdn/avmb1/t1isa.c +++ b/drivers/isdn/avmb1/t1isa.c @@ -1,11 +1,15 @@ /* - * $Id: t1isa.c,v 1.10 2000/02/02 18:36:04 calle Exp $ + * $Id: t1isa.c,v 1.11 2000/04/03 13:29:25 calle Exp $ * * Module for AVM T1 HEMA-card. * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: t1isa.c,v $ + * Revision 1.11 2000/04/03 13:29:25 calle + * make Tim Waugh happy (module unload races in 2.3.99-pre3). + * no real problem there, but now it is much cleaner ... + * * Revision 1.10 2000/02/02 18:36:04 calle * - Modules are now locked while init_module is running * - fixed problem with memory mapping if address is not aligned @@ -81,7 +85,7 @@ #include "capilli.h" #include "avmcard.h" -static char *revision = "$Revision: 1.10 $"; +static char *revision = "$Revision: 1.11 $"; /* ------------------------------------------------------------- */ @@ -598,6 +602,9 @@ int t1isa_init(void) { struct capi_driver *driver = &t1isa_driver; char *p; + int retval = 0; + + MOD_INC_USE_COUNT; if ((p = strchr(revision, ':'))) { strncpy(driver->revision, p + 1, sizeof(driver->revision)); @@ -612,9 +619,11 @@ int t1isa_init(void) if (!di) { printk(KERN_ERR "%s: failed to attach capi_driver\n", driver->name); - return -EIO; + retval = -EIO; } - return 0; + + MOD_DEC_USE_COUNT; + return retval; } #ifdef MODULE diff --git a/drivers/isdn/avmb1/t1pci.c b/drivers/isdn/avmb1/t1pci.c index e67143130032..f89791dd16e9 100644 --- a/drivers/isdn/avmb1/t1pci.c +++ b/drivers/isdn/avmb1/t1pci.c @@ -1,11 +1,25 @@ /* - * $Id: t1pci.c,v 1.5 2000/02/02 18:36:04 calle Exp $ + * $Id: t1pci.c,v 1.9 2000/05/19 15:43:22 calle Exp $ * * Module for AVM T1 PCI-card. * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: t1pci.c,v $ + * Revision 1.9 2000/05/19 15:43:22 calle + * added calls to pci_device_start(). + * + * Revision 1.8 2000/05/06 00:52:36 kai + * merged changes from kernel tree + * fixed timer and net_device->name breakage + * + * Revision 1.7 2000/04/07 15:26:55 calle + * better error message if cabel not connected or T1 has no power. + * + * Revision 1.6 2000/04/03 13:29:25 calle + * make Tim Waugh happy (module unload races in 2.3.99-pre3). + * no real problem there, but now it is much cleaner ... + * * Revision 1.5 2000/02/02 18:36:04 calle * - Modules are now locked while init_module is running * - fixed problem with memory mapping if address is not aligned @@ -42,12 +56,13 @@ #include #include #include +#include #include "capicmd.h" #include "capiutil.h" #include "capilli.h" #include "avmcard.h" -static char *revision = "$Revision: 1.5 $"; +static char *revision = "$Revision: 1.9 $"; #undef CONFIG_T1PCI_DEBUG #undef CONFIG_T1PCI_POLLDEBUG @@ -164,7 +179,11 @@ static int t1pci_add_card(struct capi_driver *driver, struct capicardparams *p) b1dma_reset(card); if ((retval = t1pci_detect(card)) != 0) { - printk(KERN_NOTICE "%s: NO card at 0x%x (%d)\n", + if (retval < 6) + printk(KERN_NOTICE "%s: NO card at 0x%x (%d)\n", + driver->name, card->port, retval); + else + printk(KERN_NOTICE "%s: card at 0x%x, but cabel not connected or T1 has no power (%d)\n", driver->name, card->port, retval); iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK)); kfree(card->ctrlinfo); @@ -264,6 +283,8 @@ int t1pci_init(void) char *p; int retval; + MOD_INC_USE_COUNT; + if ((p = strchr(revision, ':'))) { strncpy(driver->revision, p + 1, sizeof(driver->revision)); p = strchr(driver->revision, '$'); @@ -277,6 +298,7 @@ int t1pci_init(void) if (!di) { printk(KERN_ERR "%s: failed to attach capi_driver\n", driver->name); + MOD_DEC_USE_COUNT; return -EIO; } @@ -284,6 +306,7 @@ int t1pci_init(void) if (!pci_present()) { printk(KERN_ERR "%s: no PCI bus present\n", driver->name); detach_capi_driver(driver); + MOD_DEC_USE_COUNT; return -EIO; } @@ -291,9 +314,21 @@ int t1pci_init(void) struct capicardparams param; param.port = dev->base_address[ 1] & PCI_BASE_ADDRESS_IO_MASK; - param.irq = dev->irq; + param.irq = dev->irq; param.membase = dev->base_address[ 0] & PCI_BASE_ADDRESS_MEM_MASK; + retval = pci_enable_device (dev); + if (retval != 0) { + printk(KERN_ERR + "%s: failed to enable AVM-T1-PCI at i/o %#x, irq %d, mem %#x err=%d\n", + driver->name, param.port, param.irq, param.membase, retval); +#ifdef MODULE + cleanup_module(); +#endif + MOD_DEC_USE_COUNT; + return -EIO; + } + printk(KERN_INFO "%s: PCI BIOS reports AVM-T1-PCI at i/o %#x, irq %d, mem %#x\n", driver->name, param.port, param.irq, param.membase); @@ -305,6 +340,7 @@ int t1pci_init(void) #ifdef MODULE cleanup_module(); #endif + MOD_DEC_USE_COUNT; return retval; } ncards++; @@ -312,12 +348,15 @@ int t1pci_init(void) if (ncards) { printk(KERN_INFO "%s: %d T1-PCI card(s) detected\n", driver->name, ncards); + MOD_DEC_USE_COUNT; return 0; } printk(KERN_ERR "%s: NO T1-PCI card detected\n", driver->name); + MOD_DEC_USE_COUNT; return -ESRCH; #else printk(KERN_ERR "%s: kernel not compiled with PCI.\n", driver->name); + MOD_DEC_USE_COUNT; return -EIO; #endif } diff --git a/drivers/isdn/divert/divert_procfs.c b/drivers/isdn/divert/divert_procfs.c index 776d4c177e6e..6dc1b1738d6a 100644 --- a/drivers/isdn/divert/divert_procfs.c +++ b/drivers/isdn/divert/divert_procfs.c @@ -281,19 +281,14 @@ isdn_divert_lseek(struct file *file, loff_t offset, int orig) 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 */ + llseek: isdn_divert_lseek, + read: isdn_divert_read, + write: isdn_divert_write, + poll: isdn_divert_poll, + ioctl: isdn_divert_ioctl, + open: isdn_divert_open, + release: isdn_divert_close, }; - struct inode_operations divert_file_inode_operations; /****************************/ diff --git a/drivers/isdn/hisax/Makefile b/drivers/isdn/hisax/Makefile index 7bc340de9c9c..071e3b1caa1d 100644 --- a/drivers/isdn/hisax/Makefile +++ b/drivers/isdn/hisax/Makefile @@ -31,6 +31,8 @@ HFC_OBJ := HFC_2BDS0 := JADE_OBJ := W6692_OBJ := +NETJ_OBJ := +ICC_OBJ := ifeq ($(CONFIG_HISAX_16_0),y) O_OBJS += teles0.o @@ -124,10 +126,17 @@ ifeq ($(CONFIG_HISAX_MIC),y) endif ifeq ($(CONFIG_HISAX_NETJET),y) - O_OBJS += netjet.o + O_OBJS += nj_s.o + NETJ_OBJ := netjet.o ISAC_OBJ := isac.o endif +ifeq ($(CONFIG_HISAX_NETJET_U),y) + O_OBJS += nj_u.o + NETJ_OBJ := netjet.o + ICC_OBJ := icc.o +endif + ifeq ($(CONFIG_HISAX_HFCS),y) O_OBJS += hfcscard.o HFC_2BDS0 := hfc_2bds0.o @@ -189,7 +198,7 @@ ifeq ($(ISAC_OBJ), isac.o) endif O_OBJS += $(ISAC_OBJ) $(HSCX_OBJ) $(ISAR_OBJ) $(JADE_OBJ) -O_OBJS += $(HFC_OBJ) $(HFC_2BDS0) $(W6692_OBJ) +O_OBJS += $(HFC_OBJ) $(HFC_2BDS0) $(W6692_OBJ) $(NETJ_OBJ) $(ICC_OBJ) OX_OBJS += config.o O_TARGET := @@ -208,10 +217,9 @@ 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 diva.c + elsa.c diva.c sedlbauer.c CERT = $(shell md5sum -c md5sums.asc >> /dev/null;echo $$?) cert.o: $(MD5FILES) md5sums.asc $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -D CERTIFICATION=$(CERT) -c -o cert.o cert.c - diff --git a/drivers/isdn/hisax/amd7930.c b/drivers/isdn/hisax/amd7930.c index 2770aa65e1f0..d4fb0452adf9 100644 --- a/drivers/isdn/hisax/amd7930.c +++ b/drivers/isdn/hisax/amd7930.c @@ -1,25 +1,9 @@ -/* $Id: amd7930.c,v 1.3 1999/07/12 21:04:52 keil Exp $ +/* $Id: amd7930.c,v 1.4 2000/06/26 08:59:12 keil Exp $ * * HiSax ISDN driver - chip specific routines for AMD 7930 * * Author Brent Baccala (baccala@FreeSoft.org) * - * - * - * $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() - * - * Revision 1.1 1998/02/03 23:20:51 keil - * New files for SPARC isdn support - * - * Revision 1.1 1998/01/08 04:17:12 baccala - * ISDN comes to the Sparc. Key points: - * * - Existing ISDN HiSax driver provides all the smarts * - it compiles, runs, talks to an isolated phone switch, connects * to a Cisco, pings go through @@ -30,6 +14,7 @@ * * The code is unreliable enough to be consider alpha * + * This file is (c) under GNU PUBLIC LICENSE * * Advanced Micro Devices' Am79C30A is an ISDN/audio chip used in the * SparcStation 1+. The chip provides microphone and speaker interfaces @@ -109,7 +94,7 @@ #include "rawhdlc.h" #include -static const char *amd7930_revision = "$Revision: 1.3 $"; +static const char *amd7930_revision = "$Revision: 1.4 $"; #define RCV_BUFSIZE 1024 /* Size of raw receive buffer in bytes */ #define RCV_BUFBLKS 4 /* Number of blocks to divide buffer into diff --git a/drivers/isdn/hisax/arcofi.c b/drivers/isdn/hisax/arcofi.c index 04a854d2d2a1..67752d4c12ff 100644 --- a/drivers/isdn/hisax/arcofi.c +++ b/drivers/isdn/hisax/arcofi.c @@ -1,43 +1,10 @@ -/* $Id: arcofi.c,v 1.10 1999/12/23 15:09:32 keil Exp $ - +/* $Id: arcofi.c,v 1.11 2000/06/26 08:59:12 keil Exp $ + * * arcofi.c Ansteuerung ARCOFI 2165 * * Author Karsten Keil (keil@isdn4linux.de) * - * - * - * $Log: arcofi.c,v $ - * Revision 1.10 1999/12/23 15:09:32 keil - * change email - * - * Revision 1.9 1999/12/19 13:09:41 keil - * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for - * signal proof delays - * - * Revision 1.8 1999/08/25 16:50:51 keil - * Fix bugs which cause 2.3.14 hangs (waitqueue init) - * - * 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 - * - * Revision 1.5 1998/09/27 12:52:57 keil - * cosmetics - * - * Revision 1.4 1998/08/20 13:50:24 keil - * More support for hybrid modem (not working yet) - * - * Revision 1.3 1998/05/25 12:57:38 keil - * HiSax golden code from certification, Don't use !!! - * No leased lines, no X75, but many changes. - * - * Revision 1.2 1998/04/15 16:47:16 keil - * new interface - * - * Revision 1.1 1997/10/29 18:51:20 keil - * New files + * This file is (c) under GNU PUBLIC LICENSE * */ diff --git a/drivers/isdn/hisax/arcofi.h b/drivers/isdn/hisax/arcofi.h index 7d1c445e5a45..d80157146054 100644 --- a/drivers/isdn/hisax/arcofi.h +++ b/drivers/isdn/hisax/arcofi.h @@ -1,27 +1,10 @@ -/* $Id: arcofi.h,v 1.5 1999/12/23 15:09:32 keil Exp $ - +/* $Id: arcofi.h,v 1.6 2000/06/26 08:59:12 keil Exp $ + * * arcofi.h Ansteuerung ARCOFI 2165 * * Author Karsten Keil (keil@isdn4linux.de) * - * - * - * $Log: arcofi.h,v $ - * Revision 1.5 1999/12/23 15:09:32 keil - * change email - * - * 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. - * - * Revision 1.2 1998/04/15 16:47:17 keil - * new interface - * - * Revision 1.1 1997/10/29 18:51:20 keil - * New files + * This file is (c) under GNU PUBLIC LICENSE * */ diff --git a/drivers/isdn/hisax/asuscom.c b/drivers/isdn/hisax/asuscom.c index 5ae993417d85..b86f5db6a076 100644 --- a/drivers/isdn/hisax/asuscom.c +++ b/drivers/isdn/hisax/asuscom.c @@ -1,39 +1,12 @@ -/* $Id: asuscom.c,v 1.9 1999/12/19 13:09:41 keil Exp $ - +/* $Id: asuscom.c,v 1.10 2000/06/26 08:59:12 keil Exp $ + * * asuscom.c low level stuff for ASUSCOM NETWORK INC. ISDNLink cards * * Author Karsten Keil (keil@isdn4linux.de) * * Thanks to ASUSCOM NETWORK INC. Taiwan and Dynalink NL for informations * - * - * $Log: asuscom.c,v $ - * Revision 1.9 1999/12/19 13:09:41 keil - * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for - * signal proof delays - * - * Revision 1.8 1999/09/04 06:20:05 keil - * Changes from kernel set_current_state() - * - * 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 - * - * Revision 1.4 1998/06/18 23:18:20 keil - * Support for new IPAC card - * - * Revision 1.3 1998/04/15 16:46:53 keil - * new init code - * - * Revision 1.2 1998/02/02 13:27:06 keil - * New - * + * This file is (c) under GNU PUBLIC LICENSE * */ @@ -46,7 +19,7 @@ extern const char *CardType[]; -const char *Asuscom_revision = "$Revision: 1.9 $"; +const char *Asuscom_revision = "$Revision: 1.10 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -295,13 +268,13 @@ reset_asuscom(struct IsdnCardState *cs) byteout(cs->hw.asus.adr, ASUS_RESET); /* Reset On */ save_flags(flags); sti(); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); 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_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); if (cs->subtyp == ASUS_IPAC) { writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_CONF, 0x0); diff --git a/drivers/isdn/hisax/avm_a1.c b/drivers/isdn/hisax/avm_a1.c index 55df36f0f738..cc3ce44909fb 100644 --- a/drivers/isdn/hisax/avm_a1.c +++ b/drivers/isdn/hisax/avm_a1.c @@ -1,66 +1,10 @@ -/* $Id: avm_a1.c,v 2.11 1999/07/12 21:04:54 keil Exp $ - +/* $Id: avm_a1.c,v 2.12 2000/06/26 08:59:12 keil Exp $ + * * avm_a1.c low level stuff for AVM A1 (Fritz) isdn cards * * Author Karsten Keil (keil@isdn4linux.de) * - * - * $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 - * - * Revision 2.9 1998/08/13 23:36:12 keil - * HiSax 3.1 - don't work stable with current LinkLevel - * - * Revision 2.8 1998/04/15 16:44:27 keil - * new init code - * - * Revision 2.7 1998/02/02 13:29:37 keil - * fast io - * - * Revision 2.6 1998/01/13 23:09:46 keil - * really disable timer - * - * Revision 2.5 1998/01/02 06:50:29 calle - * Perodic timer of A1 now disabled, no need for linux driver. - * - * Revision 2.4 1997/11/08 21:35:42 keil - * new l1 init - * - * Revision 2.3 1997/11/06 17:13:32 keil - * New 2.1 init code - * - * Revision 2.2 1997/10/29 18:55:48 keil - * changes for 2.1.60 (irq2dev_map) - * - * Revision 2.1 1997/07/27 21:47:13 keil - * new interface structures - * - * Revision 2.0 1997/06/26 11:02:48 keil - * New Layer and card interface - * - * Revision 1.6 1997/04/13 19:54:07 keil - * Change in IRQ check delay for SMP - * - * Revision 1.5 1997/04/06 22:54:10 keil - * Using SKB's - * - * Revision 1.4 1997/01/27 15:50:21 keil - * SMP proof,cosmetics - * - * Revision 1.3 1997/01/21 22:14:20 keil - * cleanups - * - * Revision 1.2 1996/10/27 22:07:31 keil - * cosmetic changes - * - * Revision 1.1 1996/10/13 20:04:49 keil - * Initial revision - * + * This file is (c) under GNU PUBLIC LICENSE * */ #define __NO_VERSION__ @@ -70,7 +14,7 @@ #include "isdnl1.h" extern const char *CardType[]; -static const char *avm_revision = "$Revision: 2.11 $"; +static const char *avm_revision = "$Revision: 2.12 $"; #define AVM_A1_STAT_ISAC 0x01 #define AVM_A1_STAT_HSCX 0x02 diff --git a/drivers/isdn/hisax/avm_a1p.c b/drivers/isdn/hisax/avm_a1p.c index 76382c3722b7..a9a132b98557 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.5 1999/09/01 08:26:34 calle Exp $ +/* $Id: avm_a1p.c,v 2.6 2000/06/26 08:59:12 keil Exp $ * * avm_a1p.c low level stuff for the following AVM cards: * A1 PCMCIA @@ -7,29 +7,7 @@ * * Author Carsten Paeth (calle@calle.in-berlin.de) * - * $Log: avm_a1p.c,v $ - * Revision 2.5 1999/09/01 08:26:34 calle - * Patch from Daniel Beichl to make A1 PCMCIA work again. - * - * 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 - * - * Revision 2.2 1998/08/13 23:36:13 keil - * HiSax 3.1 - don't work stable with current LinkLevel - * - * Revision 2.1 1998/07/15 15:01:23 calle - * Support for AVM passive PCMCIA cards: - * A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0 - * - * Revision 1.1.2.1 1998/07/15 14:43:26 calle - * Support for AVM passive PCMCIA cards: - * A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0 - * - * + * This file is (c) under GNU PUBLIC LICENSE */ #define __NO_VERSION__ #include "hisax.h" @@ -74,7 +52,7 @@ #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) -static const char *avm_revision = "$Revision: 2.5 $"; +static const char *avm_revision = "$Revision: 2.6 $"; static inline u_char ReadISAC(struct IsdnCardState *cs, u_char offset) diff --git a/drivers/isdn/hisax/avm_pci.c b/drivers/isdn/hisax/avm_pci.c index 0d937c15df56..2a485cf37a92 100644 --- a/drivers/isdn/hisax/avm_pci.c +++ b/drivers/isdn/hisax/avm_pci.c @@ -1,57 +1,11 @@ -/* $Id: avm_pci.c,v 1.14 1999/12/19 13:09:41 keil Exp $ - +/* $Id: avm_pci.c,v 1.18 2000/08/20 07:34:04 keil Exp $ + * * avm_pci.c low level stuff for AVM Fritz!PCI and ISA PnP isdn cards * Thanks to AVM, Berlin for informations * * Author Karsten Keil (keil@isdn4linux.de) * - * - * $Log: avm_pci.c,v $ - * Revision 1.14 1999/12/19 13:09:41 keil - * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for - * signal proof delays - * - * Revision 1.13 1999/12/03 12:10:14 keil - * Bugfix: Wrong channel use on hangup of channel 2 - * - * Revision 1.12 1999/09/04 06:20:05 keil - * Changes from kernel set_current_state() - * - * 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 - * - * Revision 1.6 1998/11/27 19:59:28 keil - * set subtype for Fritz!PCI - * - * Revision 1.5 1998/11/27 12:56:45 keil - * forgot to update setup function name - * - * Revision 1.4 1998/11/15 23:53:19 keil - * Fritz!PnP; changes from 2.0 - * - * Revision 1.3 1998/09/27 23:53:39 keil - * Fix error handling - * - * Revision 1.2 1998/09/27 12:54:55 keil - * bcs assign was lost in setstack, very bad results - * - * Revision 1.1 1998/08/20 13:47:30 keil - * first version - * - * + * This file is (c) under GNU PUBLIC LICENSE * */ #define __NO_VERSION__ @@ -63,13 +17,17 @@ #include extern const char *CardType[]; -static const char *avm_pci_rev = "$Revision: 1.14 $"; +static const char *avm_pci_rev = "$Revision: 1.18 $"; #define AVM_FRITZ_PCI 1 #define AVM_FRITZ_PNP 2 -#define PCI_VENDOR_AVM 0x1244 -#define PCI_FRITZPCI_ID 0xa00 +#ifndef PCI_VENDOR_ID_AVM +#define PCI_VENDOR_ID_AVM 0x1244 +#endif +#ifndef PCI_DEVICE_ID_AVM_FRITZ +#define PCI_DEVICE_ID_AVM_FRITZ 0xa00 +#endif #define HDLC_FIFO 0x0 #define HDLC_STATUS 0x4 @@ -339,7 +297,15 @@ hdlc_empty_fifo(struct BCState *bcs, int count) if (cs->subtyp == AVM_FRITZ_PCI) { outl(idx, cs->hw.avm.cfg_reg + 4); while (cnt < count) { +#ifdef __powerpc__ +#ifdef CONFIG_APUS + *ptr++ = in_le32((unsigned *)(cs->hw.avm.isac +_IO_BASE)); +#else + *ptr++ = in_be32((unsigned *)(cs->hw.avm.isac +_IO_BASE)); +#endif /* CONFIG_APUS */ +#else *ptr++ = inl(cs->hw.avm.isac); +#endif /* __powerpc__ */ cnt += 4; } } else { @@ -395,7 +361,15 @@ hdlc_fill_fifo(struct BCState *bcs) write_ctrl(bcs, 3); /* sets the correct index too */ if (cs->subtyp == AVM_FRITZ_PCI) { while (cnthw.avm.isac +_IO_BASE), *ptr++); +#else + out_be32((unsigned *)(cs->hw.avm.isac +_IO_BASE), *ptr++); +#endif /* CONFIG_APUS */ +#else outl(*ptr++, cs->hw.avm.isac); +#endif /* __powerpc__ */ cnt += 4; } } else { @@ -751,11 +725,11 @@ reset_avmpcipnp(struct IsdnCardState *cs) save_flags(flags); sti(); outb(AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER, cs->hw.avm.cfg_reg + 2); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER | AVM_STATUS0_ENA_IRQ, cs->hw.avm.cfg_reg + 2); outb(AVM_STATUS1_ENA_IOM | cs->irq, cs->hw.avm.cfg_reg + 3); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ printk(KERN_INFO "AVM PCI/PnP: S1 %x\n", inb(cs->hw.avm.cfg_reg + 3)); } @@ -813,17 +787,18 @@ setup_avm_pcipnp(struct IsdnCard *card)) printk(KERN_ERR "FritzPCI: no PCI bus present\n"); return(0); } - if ((dev_avm = pci_find_device(PCI_VENDOR_AVM, - PCI_FRITZPCI_ID, dev_avm))) { + if ((dev_avm = pci_find_device(PCI_VENDOR_ID_AVM, + PCI_DEVICE_ID_AVM_FRITZ, dev_avm))) { cs->irq = dev_avm->irq; if (!cs->irq) { - printk(KERN_WARNING "FritzPCI: No IRQ for PCI card found\n"); + printk(KERN_ERR "FritzPCI: No IRQ for PCI card found\n"); return(0); } - cs->hw.avm.cfg_reg = dev_avm->base_address[ 1] & - PCI_BASE_ADDRESS_IO_MASK; + if (pci_enable_device(dev_avm)) + return(0); + cs->hw.avm.cfg_reg = dev_avm->base_address[ 1] & PCI_BASE_ADDRESS_IO_MASK; if (!cs->hw.avm.cfg_reg) { - printk(KERN_WARNING "FritzPCI: No IO-Adr for PCI card found\n"); + printk(KERN_ERR "FritzPCI: No IO-Adr for PCI card found\n"); return(0); } cs->subtyp = AVM_FRITZ_PCI; diff --git a/drivers/isdn/hisax/bkm_a4t.c b/drivers/isdn/hisax/bkm_a4t.c index 3bc987e87984..4bbb6cc675e7 100644 --- a/drivers/isdn/hisax/bkm_a4t.c +++ b/drivers/isdn/hisax/bkm_a4t.c @@ -1,4 +1,4 @@ -/* $Id: bkm_a4t.c,v 1.9 1999/12/19 13:09:41 keil Exp $ +/* $Id: bkm_a4t.c,v 1.11 2000/06/26 08:59:12 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 @@ -6,36 +6,7 @@ * * Author Roland Klabunde (R.Klabunde@Berkom.de) * - * $Log: bkm_a4t.c,v $ - * Revision 1.9 1999/12/19 13:09:41 keil - * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for - * signal proof delays - * - * Revision 1.8 1999/09/04 06:20:05 keil - * Changes from kernel set_current_state() - * - * Revision 1.7 1999/08/22 20:26:55 calle - * backported changes from kernel 2.3.14: - * - several #include "config.h" gone, others come. - * - "struct device" changed to "struct net_device" in 2.3.14, added a - * define in isdn_compat.h for older kernel versions. - * - * 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 - * + * This file is (c) under GNU PUBLIC LICENSE * */ @@ -47,12 +18,12 @@ #include "hscx.h" #include "jade.h" #include "isdnl1.h" -#include "bkm_ax.h" #include +#include "bkm_ax.h" extern const char *CardType[]; -const char *bkm_a4t_revision = "$Revision: 1.9 $"; +const char *bkm_a4t_revision = "$Revision: 1.11 $"; static inline u_char @@ -235,11 +206,11 @@ reset_bkm(struct IsdnCardState *cs) sti(); /* Issue the I20 soft reset */ pI20_Regs->i20SysControl = 0xFF; /* all in */ - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10 * HZ) / 1000); /* Remove the soft reset */ pI20_Regs->i20SysControl = sysRESET | 0xFF; - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10 * HZ) / 1000); /* Set our configuration */ pI20_Regs->i20SysControl = sysRESET | sysCFG; @@ -250,14 +221,14 @@ reset_bkm(struct IsdnCardState *cs) g_A4T_ISAC_RES | g_A4T_JADE_BOOTR | g_A4T_ISAR_BOOTR; - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); 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_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10 * HZ) / 1000); restore_flags(flags); } @@ -316,15 +287,20 @@ __initfunc(int 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)) { + while ((dev_a4t = pci_find_device(PCI_VENDOR_ID_ZORAN, + PCI_DEVICE_ID_ZORAN_36120, dev_a4t))) { + u16 sub_sys; + u16 sub_vendor; + + pci_read_config_word(dev_a4t, PCI_SUBSYSTEM_VENDOR_ID, &sub_vendor); + pci_read_config_word(dev_a4t, PCI_SUBSYSTEM_ID, &sub_sys); + if ((sub_sys == A4T_SUBSYS_ID) && (sub_vendor == A4T_SUBVEN_ID)) { + if (pci_enable_device(dev_a4t)) + return(0); found = 1; - pci_memaddr = dev_a4t->base_address[ 0]; + pci_memaddr = dev_a4t->base_address[ 0] & PCI_BASE_ADDRESS_MEM_MASK; cs->irq = dev_a4t->irq; + break; } } if (!found) { @@ -339,7 +315,6 @@ __initfunc(int 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); diff --git a/drivers/isdn/hisax/bkm_a8.c b/drivers/isdn/hisax/bkm_a8.c index df963a70991b..d9b2e80e04cc 100644 --- a/drivers/isdn/hisax/bkm_a8.c +++ b/drivers/isdn/hisax/bkm_a8.c @@ -1,4 +1,4 @@ -/* $Id: bkm_a8.c,v 1.9 1999/12/19 13:09:41 keil Exp $ +/* $Id: bkm_a8.c,v 1.12 2000/06/26 08:59:12 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 @@ -6,36 +6,7 @@ * * Author Roland Klabunde (R.Klabunde@Berkom.de) * - * $Log: bkm_a8.c,v $ - * Revision 1.9 1999/12/19 13:09:41 keil - * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for - * signal proof delays - * - * Revision 1.8 1999/09/04 06:20:05 keil - * Changes from kernel set_current_state() - * - * Revision 1.7 1999/08/22 20:26:58 calle - * backported changes from kernel 2.3.14: - * - several #include "config.h" gone, others come. - * - "struct device" changed to "struct net_device" in 2.3.14, added a - * define in isdn_compat.h for older kernel versions. - * - * 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 - * + * This file is (c) under GNU PUBLIC LICENSE * */ #define __NO_VERSION__ @@ -46,29 +17,16 @@ #include "ipac.h" #include "hscx.h" #include "isdnl1.h" -#include "bkm_ax.h" #include +#include "bkm_ax.h" + +#if CONFIG_PCI #define ATTEMPT_PCI_REMAPPING /* Required for PLX rev 1 */ extern const char *CardType[]; -const char sct_quadro_revision[] = "$Revision: 1.9 $"; - -/* 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 */ -}; +const char sct_quadro_revision[] = "$Revision: 1.12 $"; static const char *sct_quadro_subtypes[] = { @@ -167,19 +125,13 @@ 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_ipac_active(struct IsdnCardState *cs, u_int active) { - /* set activation state */ - ipac_state[ipac_nr].active = active; + /* set irq mask */ + writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK, + active ? 0xc0 : 0xff); } /* @@ -202,13 +154,14 @@ 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); - + if (!(ista & 0x3f)) /* not this IPAC */ + return; Start_IPAC: if (cs->debug & L1_DEB_IPAC) debugl1(cs, "IPAC ISTA %02X", ista); @@ -245,30 +198,15 @@ bkm_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs) 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) { - /* ?? */ + release_region(cs->hw.ax.base & 0xffffffc0, 256); + if (cs->subtyp == SCT_1) + release_region(cs->hw.ax.plx_adr, 256); } static void @@ -278,11 +216,6 @@ enable_bkm_int(struct IsdnCardState *cs, unsigned bEnable) 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)); } } @@ -292,26 +225,17 @@ 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_UNINTERRUPTIBLE; - 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_UNINTERRUPTIBLE; - schedule_timeout((10 * HZ) / 1000); - restore_flags(flags); - } + if (cs->subtyp == SCT_1) { + wordout(cs->hw.ax.plx_adr + 0x50, (wordin(cs->hw.ax.plx_adr + 0x50) & ~4)); + save_flags(flags); + sti(); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((10 * HZ) / 1000); + /* Remove the soft reset */ + wordout(cs->hw.ax.plx_adr + 0x50, (wordin(cs->hw.ax.plx_adr + 0x50) | 4)); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((10 * HZ) / 1000); + restore_flags(flags); } } @@ -321,20 +245,19 @@ BKM_card_msg(struct IsdnCardState *cs, int mt, void *arg) switch (mt) { case CARD_RESET: /* Disable ints */ - set_ipac_active(cs->subtyp, 0); + set_ipac_active(cs, 0); enable_bkm_int(cs, 0); reset_bkm(cs); return (0); case CARD_RELEASE: /* Sanity */ - set_ipac_active(cs->subtyp, 0); + set_ipac_active(cs, 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); + set_ipac_active(cs, 1); inithscxisac(cs, 3); /* Enable ints */ enable_bkm_int(cs, 1); @@ -345,18 +268,38 @@ BKM_card_msg(struct IsdnCardState *cs, int mt, void *arg) return (0); } +__initfunc(int +sct_alloc_io(u_int adr, u_int len)) +{ + if (check_region(adr, len)) { + printk(KERN_WARNING + "HiSax: Scitel port %#x-%#x already in use\n", + adr, adr + len); + return (1); + } else { + request_region(adr, len, "scitel"); + } + return(0); +} + static struct pci_dev *dev_a8 __initdata = NULL; +static u16 sub_vendor_id __initdata = 0; +static u16 sub_sys_id __initdata = 0; +static u_char pci_bus __initdata = 0; +static u_char pci_device_fn __initdata = 0; +static u_char pci_irq __initdata = 0; + +#endif /* CONFIG_PCI */ __initfunc(int - setup_sct_quadro(struct IsdnCard *card)) +setup_sct_quadro(struct IsdnCard *card)) { +#if CONFIG_PCI 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_char 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)); @@ -368,32 +311,61 @@ __initfunc(int /* Identify subtype by para[0] */ if (card->para[0] >= SCT_1 && card->para[0] <= SCT_4) cs->subtyp = card->para[0]; - else + else { printk(KERN_WARNING "HiSax: %s: Invalid subcontroller in configuration, default to 1\n", - CardType[card->typ]); -#if CONFIG_PCI - if (!pci_present()) { - printk(KERN_ERR "bkm_a4t: no PCI bus present\n"); + CardType[card->typ]); 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 = dev_a8->base_address[ 1]; - pci_irq = dev_a8->irq; - pci_bus = dev_a8->bus->number; - pci_device_fn = dev_a8->devfn; - } - } - if (!found) { - printk(KERN_WARNING "HiSax: %s (%s): Card not found\n", - CardType[card->typ], - sct_quadro_subtypes[cs->subtyp]); + if ((cs->subtyp != SCT_1) && ((sub_sys_id != SCT_SUBSYS_ID) || + (sub_vendor_id != SCT_SUBVEN_ID))) return (0); + if (cs->subtyp == SCT_1) { + if (!pci_present()) { + printk(KERN_ERR "bkm_a4t: no PCI bus present\n"); + return (0); + } + while ((dev_a8 = pci_find_device(PCI_VENDOR_ID_PLX, + PCI_DEVICE_ID_PLX_9050, dev_a8))) { + + pci_read_config_word(dev_a8, PCI_SUBSYSTEM_VENDOR_ID, &sub_vendor_id); + pci_read_config_word(dev_a8, PCI_SUBSYSTEM_ID, &sub_sys_id); + if ((sub_sys_id == SCT_SUBSYS_ID) && + (sub_vendor_id == SCT_SUBVEN_ID)) { + if (pci_enable_device(dev_a8)) + return(0); + pci_ioaddr1 = dev_a8->base_address[ 1] & PCI_BASE_ADDRESS_IO_MASK; + pci_irq = dev_a8->irq; + pci_bus = dev_a8->bus->number; + pci_device_fn = dev_a8->devfn; + found = 1; + break; + } + } + if (!found) { + printk(KERN_WARNING "HiSax: %s (%s): Card not found\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); + dev_a8->base_address[ 1] = pci_ioaddr1; + } +#endif /* End HACK */ } if (!pci_irq) { /* IRQ range check ?? */ printk(KERN_WARNING "HiSax: %s (%s): No IRQ\n", @@ -401,25 +373,6 @@ __initfunc(int 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); - dev_a8->base_address[ 1] = pci_ioaddr1; - } -/* 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); @@ -446,23 +399,42 @@ __initfunc(int /* 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; + switch(cs->subtyp) { + case 1: + cs->hw.ax.base = pci_ioaddr5 + 0x00; + if (sct_alloc_io(pci_ioaddr1, 256)) + return(0); + if (sct_alloc_io(pci_ioaddr5, 256)) + return(0); + /* disable all IPAC */ + writereg(pci_ioaddr5, pci_ioaddr5 + 4, + IPAC_MASK, 0xFF); + writereg(pci_ioaddr4 + 0x08, pci_ioaddr4 + 0x0c, + IPAC_MASK, 0xFF); + writereg(pci_ioaddr3 + 0x10, pci_ioaddr3 + 0x14, + IPAC_MASK, 0xFF); + writereg(pci_ioaddr2 + 0x20, pci_ioaddr2 + 0x24, + IPAC_MASK, 0xFF); + break; + case 2: + cs->hw.ax.base = pci_ioaddr4 + 0x08; + if (sct_alloc_io(pci_ioaddr4, 256)) + return(0); + break; + case 3: + cs->hw.ax.base = pci_ioaddr3 + 0x10; + if (sct_alloc_io(pci_ioaddr3, 256)) + return(0); + break; + case 4: + cs->hw.ax.base = pci_ioaddr2 + 0x20; + if (sct_alloc_io(pci_ioaddr2, 256)) + return(0); + break; + } /* 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], @@ -473,19 +445,6 @@ __initfunc(int 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; @@ -502,4 +461,7 @@ __initfunc(int sct_quadro_subtypes[cs->subtyp], readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ID)); return (1); +#else + printk(KERN_ERR "HiSax: bkm_a8 only supported on PCI Systems\n"); +#endif /* CONFIG_PCI */ } diff --git a/drivers/isdn/hisax/bkm_ax.h b/drivers/isdn/hisax/bkm_ax.h index e2d5fa70e4bd..e513c81a6731 100644 --- a/drivers/isdn/hisax/bkm_ax.h +++ b/drivers/isdn/hisax/bkm_ax.h @@ -1,13 +1,9 @@ -/* $Id: bkm_ax.h,v 1.2 1999/07/01 08:07:55 keil Exp $ +/* $Id: bkm_ax.h,v 1.4 2000/06/26 08:59:12 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 - * - * + * This file is (c) under GNU PUBLIC LICENSE * */ @@ -23,13 +19,21 @@ /* A4T */ -#define I20_DEVICE_ID 0x6120 /* I20 PCI device ID */ -#define I20_VENDOR_ID 0x11DE /* I20 PCI vendor ID */ +#ifndef PCI_VENDOR_ID_ZORAN +#define PCI_VENDOR_ID_ZORAN 0x11DE +#endif +#ifndef PCI_DEVICE_ID_ZORAN_36120 +#define PCI_DEVICE_ID_ZORAN_36120 0x6120 +#endif #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 +#ifndef PCI_VENDOR_ID_PLX +#define PCI_VENDOR_ID_PLX 0x10B5 +#endif +#ifndef PCI_DEVICE_ID_PLX_9050 +#define PCI_DEVICE_ID_PLX_9050 0x9050 +#endif #define SCT_SUBVEN_ID 0x0871 #define SCT_SUBSYS_ID 0xFFA8 diff --git a/drivers/isdn/hisax/callc.c b/drivers/isdn/hisax/callc.c index 4a01218c32de..4d3290392292 100644 --- a/drivers/isdn/hisax/callc.c +++ b/drivers/isdn/hisax/callc.c @@ -1,5 +1,5 @@ -/* $Id: callc.c,v 2.40 1999/12/19 12:59:56 keil Exp $ - +/* $Id: callc.c,v 2.47 2000/06/26 08:59:12 keil Exp $ + * * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden * @@ -10,153 +10,6 @@ * Thanks to Jan den Ouden * Fritz Elfert * - * $Log: callc.c,v $ - * Revision 2.40 1999/12/19 12:59:56 keil - * fix leased line handling - * and cosmetics - * - * Revision 2.39 1999/10/14 20:25:28 keil - * add a statistic for error monitoring - * - * Revision 2.38 1999/10/11 22:16:27 keil - * Suspend/Resume is possible without explicit ID too - * - * Revision 2.37 1999/09/20 19:49:47 keil - * Fix wrong init of PStack - * - * Revision 2.36 1999/09/20 12:13:13 keil - * Fix hang if no protocol was selected - * - * Revision 2.35 1999/09/04 06:20:05 keil - * Changes from kernel set_current_state() - * - * Revision 2.34 1999/08/25 20:02:34 werner - * Changed return values for stat_icall(w) from 3->4 and 4->5 because of conflicts - * with existing software definitions. (PtP incomplete called party number) - * - * Revision 2.33 1999/08/25 17:00:09 keil - * Make ISAR V32bis modem running - * Make LL->HL interface open for additional commands - * - * Revision 2.32 1999/08/22 20:27:01 calle - * backported changes from kernel 2.3.14: - * - several #include "config.h" gone, others come. - * - "struct device" changed to "struct net_device" in 2.3.14, added a - * define in isdn_compat.h for older kernel versions. - * - * 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 - * - * Revision 2.24 1998/11/15 23:54:24 keil - * changes from 2.0 - * - * Revision 2.23 1998/09/30 22:21:57 keil - * cosmetics - * - * Revision 2.22 1998/08/20 13:50:29 keil - * More support for hybrid modem (not working yet) - * - * Revision 2.21 1998/08/13 23:36:15 keil - * HiSax 3.1 - don't work stable with current LinkLevel - * - * Revision 2.20 1998/06/26 15:13:05 fritz - * Added handling of STAT_ICALL with incomplete CPN. - * Added AT&L for ttyI emulator. - * Added more locking stuff in tty_write. - * - * Revision 2.19 1998/05/25 14:08:06 keil - * HiSax 3.0 - * fixed X.75 and leased line to work again - * Point2Point and fixed TEI are runtime options now: - * hisaxctrl 7 1 set PTP - * hisaxctrl 8 - * set fixed TEI to TEIVALUE (0-63) - * - * Revision 2.18 1998/05/25 12:57:40 keil - * HiSax golden code from certification, Don't use !!! - * No leased lines, no X75, but many changes. - * - * Revision 2.17 1998/04/15 16:46:06 keil - * RESUME support - * - * Revision 2.16 1998/04/10 10:35:17 paul - * fixed (silly?) warnings from egcs on Alpha. - * - * Revision 2.15 1998/03/19 13:18:37 keil - * Start of a CAPI like interface for supplementary Service - * first service: SUSPEND - * - * Revision 2.14 1998/03/07 22:56:54 tsbogend - * made HiSax working on Linux/Alpha - * - * Revision 2.13 1998/02/12 23:07:16 keil - * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() - * - * Revision 2.12 1998/02/09 10:55:54 keil - * New leased line mode - * - * Revision 2.11 1998/02/02 13:35:19 keil - * config B-channel delay - * - * Revision 2.10 1997/11/06 17:09:15 keil - * New 2.1 init code - * - * Revision 2.9 1997/10/29 19:01:58 keil - * new LL interface - * - * Revision 2.8 1997/10/10 20:56:44 fritz - * New HL interface. - * - * Revision 2.7 1997/10/01 09:21:28 fritz - * Removed old compatibility stuff for 2.0.X kernels. - * From now on, this code is for 2.1.X ONLY! - * Old stuff is still in the separate branch. - * - * Revision 2.6 1997/09/11 17:26:58 keil - * Open B-channel if here are incomming packets - * - * Revision 2.5 1997/08/07 17:46:05 keil - * Fix Incomming Call without broadcast - * - * Revision 2.4 1997/08/03 14:37:58 keil - * Activate Layer2 in PtP mode - * - * Revision 2.3 1997/07/31 19:23:40 keil - * LAYER2_WATCHING for PtP - * - * Revision 2.2 1997/07/31 11:48:18 keil - * experimental REJECT after ALERTING - * - * Revision 2.1 1997/07/30 17:12:59 keil - * more changes for 'One TEI per card' - * - * Revision 2.0 1997/07/27 21:12:21 keil - * CRef based L3; new channel handling; many other stuff - * - * Revision 1.31 1997/06/26 11:09:23 keil - * New managment and minor changes - * - * old logs removed /KKe - * */ #define __NO_VERSION__ @@ -167,7 +20,7 @@ #define MOD_USE_COUNT ( GET_USE_COUNT (&__this_module)) #endif /* MODULE */ -const char *lli_revision = "$Revision: 2.40 $"; +const char *lli_revision = "$Revision: 2.47 $"; extern struct IsdnCard cards[]; extern int nrcards; @@ -349,6 +202,8 @@ lli_deliver_cause(struct Channel *chanp) { isdn_ctrl ic; + if (!chanp->proc) + return; if (chanp->proc->para.cause == NO_CAUSE) return; ic.driver = chanp->cs->myid; @@ -511,7 +366,7 @@ lli_deliver_call(struct FsmInst *fi, int event, void *arg) * No need to return "unknown" for calls without OAD, * cause that's handled in linklevel now (replaced by '0') */ - ic.parm.setup = chanp->proc->para.setup; + memcpy(&ic.parm.setup, &chanp->proc->para.setup, sizeof(setup_parm)); ret = chanp->cs->iif.statcallb(&ic); if (chanp->debug & 1) link_debug(chanp, 1, "statcallb ret=%d", ret); @@ -528,12 +383,16 @@ lli_deliver_call(struct FsmInst *fi, int event, void *arg) FsmChangeState(fi, ST_IN_PROCEED_SEND); chanp->d_st->lli.l4l3(chanp->d_st, CC_PROCEED_SEND | REQUEST, chanp->proc); if (ret == 5) { - chanp->setup = ic.parm.setup; + memcpy(&chanp->setup, &ic.parm.setup, sizeof(setup_parm)); chanp->d_st->lli.l4l3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc); } break; case 2: /* Rejecting Call */ break; + case 3: /* incomplete number */ + FsmDelTimer(&chanp->drel_timer, 61); + chanp->d_st->lli.l4l3(chanp->d_st, CC_MORE_INFO | REQUEST, chanp->proc); + break; case 0: /* OK, nobody likes this call */ default: /* statcallb problems */ chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc); @@ -641,7 +500,8 @@ lli_disconnect_req(struct FsmInst *fi, int event, void *arg) lli_leased_hup(fi, chanp); } else { FsmChangeState(fi, ST_WAIT_DRELEASE); - chanp->proc->para.cause = 0x10; /* Normal Call Clearing */ + if (chanp->proc) + chanp->proc->para.cause = 0x10; /* Normal Call Clearing */ chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc); } @@ -656,7 +516,8 @@ lli_disconnect_reject(struct FsmInst *fi, int event, void *arg) lli_leased_hup(fi, chanp); } else { FsmChangeState(fi, ST_WAIT_DRELEASE); - chanp->proc->para.cause = 0x15; /* Call Rejected */ + if (chanp->proc) + chanp->proc->para.cause = 0x15; /* Call Rejected */ chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc); } @@ -688,7 +549,8 @@ lli_reject_req(struct FsmInst *fi, int event, void *arg) return; } #ifndef ALERT_REJECT - chanp->proc->para.cause = 0x15; /* Call Rejected */ + if (chanp->proc) + 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 @@ -937,6 +799,8 @@ static struct FsmNode fnlist[] HISAX_INITDATA = {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_WAIT_LL, EV_SETUP_IND, lli_deliver_call}, + {ST_IN_WAIT_LL, EV_SETUP_ERR, lli_error}, {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}, @@ -1098,6 +962,9 @@ dchan_l3l4(struct PStack *st, int pr, void *arg) return; switch (pr) { + case (CC_MORE_INFO | INDICATION): + FsmEvent(&chanp->fi, EV_SETUP_IND, NULL); + break; case (CC_DISCONNECT | INDICATION): FsmEvent(&chanp->fi, EV_DISCONNECT_IND, NULL); break; @@ -1245,7 +1112,7 @@ init_chan(int chan, struct IsdnCardState *csta) chanp->fi.printdebug = callc_debug; FsmInitTimer(&chanp->fi, &chanp->dial_timer); FsmInitTimer(&chanp->fi, &chanp->drel_timer); - if (!chan || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) { + if (!chan || (test_bit(FLG_TWO_DCHAN, &csta->HW_Flags) && chan < 2)) { init_d_st(chanp); } else { chanp->d_st = csta->channel->d_st; @@ -1318,9 +1185,12 @@ lldata_handler(struct PStack *st, int pr, void *arg) switch (pr) { case (DL_DATA | INDICATION): - if (chanp->data_open) + if (chanp->data_open) { + if (chanp->debug & 0x800) + link_debug(chanp, 0, "lldata: %d", skb->len); chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb); - else { + } else { + link_debug(chanp, 0, "lldata: channel not open"); dev_kfree_skb(skb); } break; @@ -1347,10 +1217,12 @@ lltrans_handler(struct PStack *st, int pr, void *arg) switch (pr) { case (PH_DATA | INDICATION): - if (chanp->data_open) + if (chanp->data_open) { + if (chanp->debug & 0x800) + link_debug(chanp, 0, "lltrans: %d", skb->len); chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb); - else { - link_debug(chanp, 0, "channel not open"); + } else { + link_debug(chanp, 0, "lltrans: channel not open"); dev_kfree_skb(skb); } break; @@ -1375,6 +1247,8 @@ ll_writewakeup(struct PStack *st, int len) struct Channel *chanp = st->lli.userdata; isdn_ctrl ic; + if (chanp->debug & 0x800) + link_debug(chanp, 0, "llwakeup: %d", len); ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BSENT; ic.arg = chanp->chan; @@ -1648,7 +1522,7 @@ HiSax_command(isdn_ctrl * ic) link_debug(chanp, 1, "DIAL %s -> %s (%d,%d)", ic->parm.setup.eazmsn, ic->parm.setup.phone, ic->parm.setup.si1, ic->parm.setup.si2); - chanp->setup = ic->parm.setup; + memcpy(&chanp->setup, &ic->parm.setup, sizeof(setup_parm)); if (!strcmp(chanp->setup.eazmsn, "0")) chanp->setup.eazmsn[0] = '\0'; /* this solution is dirty and may be change, if @@ -1668,6 +1542,7 @@ HiSax_command(isdn_ctrl * ic) break; case (ISDN_CMD_ACCEPTD): chanp = csta->channel + ic->arg; + memcpy(&chanp->setup, &ic->parm.setup, sizeof(setup_parm)); if (chanp->debug & 1) link_debug(chanp, 1, "ACCEPTD"); FsmEvent(&chanp->fi, EV_ACCEPTD, NULL); @@ -1864,7 +1739,7 @@ HiSax_command(isdn_ctrl * ic) chanp = csta->channel + ic->arg; if (chanp->debug & 1) link_debug(chanp, 1, "REDIR"); - chanp->setup = ic->parm.setup; + memcpy(&chanp->setup, &ic->parm.setup, sizeof(setup_parm)); FsmEvent(&chanp->fi, EV_REDIR, NULL); break; diff --git a/drivers/isdn/hisax/cert.c b/drivers/isdn/hisax/cert.c index 03f416d5bff3..64f3bccc8c57 100644 --- a/drivers/isdn/hisax/cert.c +++ b/drivers/isdn/hisax/cert.c @@ -1,22 +1,11 @@ -/* $Id: cert.c,v 2.2 1999/08/07 17:35:05 keil Exp $ - +/* $Id: cert.c,v 2.3 2000/06/26 08:59:12 keil Exp $ + * * Author Karsten Keil (keil@isdn4linux.de) * * This file is (c) under GNU PUBLIC LICENSE * For changes and modifications please read * ../../../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 - * - * Revision 1.2.2.1 1998/11/03 21:46:37 keil - * first version - * - * */ #include @@ -28,11 +17,9 @@ certification_check(int output) { #if CERTIFICATION == 0 if (output) { printk(KERN_INFO "HiSax: Approval certification valid\n"); - printk(KERN_INFO "HiSax: Approved with ELSA Quickstep series cards\n"); - 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 ELSA Microlink PCI cards\n"); printk(KERN_INFO "HiSax: Approved with Eicon Technology Diva 2.01 PCI cards\n"); + printk(KERN_INFO "HiSax: Approved with Sedlbauer Speedfax + cards\n"); } return(0); #endif diff --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c index 99ce02141b9f..83530459e571 100644 --- a/drivers/isdn/hisax/config.c +++ b/drivers/isdn/hisax/config.c @@ -1,161 +1,9 @@ -/* $Id: config.c,v 2.44 2000/02/26 00:35:12 keil Exp $ - +/* $Id: config.c,v 2.52 2000/10/02 17:33:43 keil Exp $ + * * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden * - * - * $Log: config.c,v $ - * Revision 2.44 2000/02/26 00:35:12 keil - * Fix skb freeing in interrupt context - * - * Revision 2.43 2000/01/20 19:49:36 keil - * Support teles 13.3c vendor version 2.1 - * - * Revision 2.42 1999/12/19 13:09:41 keil - * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for - * signal proof delays - * - * Revision 2.41 1999/11/18 00:00:43 werner - * - * Added support for HFC-S+ and HFC-SP cards - * - * Revision 2.40 1999/10/30 13:09:45 keil - * Version 3.3c - * - * Revision 2.39 1999/10/16 14:44:45 keil - * Fix module parm if only NICCY was selected - * - * Revision 2.38 1999/10/14 20:25:28 keil - * add a statistic for error monitoring - * - * Revision 2.37 1999/09/20 12:11:08 keil - * Fix hang if no protocol was selected - * - * Revision 2.36 1999/09/07 05:43:58 werner - * - * Added io as parameter 0 for HFC-PCI cards, if manual selection needed. - * - * Revision 2.35 1999/09/04 06:35:09 keil - * Winbond W6692 support - * - * Revision 2.34 1999/09/04 06:20:06 keil - * Changes from kernel set_current_state() - * - * Revision 2.33 1999/08/30 11:57:52 keil - * Fix broken avm pcmcia - * - * Revision 2.32 1999/08/28 22:11:10 keil - * __setup function should be static - * - * Revision 2.31 1999/08/25 16:47:43 keil - * Support new __setup; allow to add FEATURES after register_isdn - * - * 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 . - * - * If anyone is annoyed by exporting symbols deep inside the code, please - * contact me. - * - * Revision 2.22 1999/02/04 21:41:53 keil - * Fix printk msg - * - * Revision 2.21 1999/02/04 10:48:52 keil - * Fix readstat bug - * - * Revision 2.20 1998/11/15 23:54:28 keil - * changes from 2.0 - * - * Revision 2.19 1998/08/13 23:36:18 keil - * HiSax 3.1 - don't work stable with current LinkLevel - * - * Revision 2.18 1998/07/30 21:01:37 niemann - * Fixed Sedlbauer Speed Fax PCMCIA missing isdnl3new - * - * Revision 2.17 1998/07/15 15:01:26 calle - * Support for AVM passive PCMCIA cards: - * A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0 - * - * Revision 2.16 1998/05/25 14:10:03 keil - * HiSax 3.0 - * X.75 and leased are working again. - * - * Revision 2.15 1998/05/25 12:57:43 keil - * HiSax golden code from certification, Don't use !!! - * No leased lines, no X75, but many changes. - * - * Revision 2.14 1998/04/15 16:38:25 keil - * Add S0Box and Teles PCI support - * - * Revision 2.13 1998/03/09 23:19:23 keil - * Changes for PCMCIA - * - * Revision 2.12 1998/02/11 17:28:02 keil - * Niccy PnP/PCI support - * - * Revision 2.11 1998/02/09 21:26:13 keil - * fix export module for 2.1 - * - * Revision 2.10 1998/02/09 18:46:05 keil - * Support for Sedlbauer PCMCIA (Marcus Niemann) - * - * Revision 2.9 1998/02/03 23:31:28 keil - * add AMD7930 support - * - * Revision 2.8 1998/02/02 13:32:59 keil - * New card support - * - * Revision 2.7 1998/01/31 21:41:44 keil - * changes for newer 2.1 kernels - * - * Revision 2.6 1997/11/08 21:35:43 keil - * new l1 init - * - * Revision 2.5 1997/11/06 17:15:08 keil - * New 2.1 init; PCMCIA wrapper changes - * - * Revision 2.4 1997/10/29 19:07:52 keil - * changes for 2.1 - * - * Revision 2.3 1997/10/01 09:21:33 fritz - * Removed old compatibility stuff for 2.0.X kernels. - * From now on, this code is for 2.1.X ONLY! - * Old stuff is still in the separate branch. - * - * Revision 2.2 1997/09/11 17:24:46 keil - * Add new cards - * - * Revision 2.1 1997/07/27 21:41:35 keil - * version change - * - * Revision 2.0 1997/06/26 11:06:28 keil - * New card and L1 interface. - * Eicon.Diehl Diva and Dynalink IS64PH support - * - * old changes removed /KKe + * This file is (c) under GNU PUBLIC LICENSE * */ #include @@ -198,7 +46,7 @@ * 17 MIC card p0=irq p1=iobase * 18 ELSA Quickstep 1000PCI no parameter * 19 Compaq ISDN S0 ISA card p0=irq p1=IO0 (HSCX) p2=IO1 (ISAC) p3=IO2 - * 20 Travers Technologies NETjet PCI card + * 20 Travers Technologies NETjet-S PCI card * 21 TELES PCI no parameter * 22 Sedlbauer Speed Star p0=irq p1=iobase * 23 reserved @@ -216,6 +64,8 @@ * 35 HFC 2BDS0 PCI none * 36 Winbond 6692 PCI none * 37 HFC 2BDS0 S+/SP p0=irq p1=iobase + * 38 Travers Technologies NETspider-U PCI card + * 39 HFC 2BDS0-SP PCMCIA p0=irq p1=iobase * * protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6 or ISDN_PTYPE_NI1 * @@ -227,11 +77,11 @@ const char *CardType[] = "AVM A1", "Elsa ML", "Elsa Quickstep", "Teles PCMCIA", "ITK ix1-micro Rev.2", "Elsa PCMCIA", "Eicon.Diehl Diva", "ISDNLink", "TeleInt", "Teles 16.3c", "Sedlbauer Speed Card", "USR Sportster", "ith mic Linux", "Elsa PCI", - "Compaq ISA", "NETjet", "Teles PCI", "Sedlbauer Speed Star (PCMCIA)", + "Compaq ISA", "NETjet-S", "Teles PCI", "Sedlbauer Speed Star (PCMCIA)", "AMD 7930", "NICCY", "S0Box", "AVM A1 (PCMCIA)", "AVM Fritz PnP/PCI", "Sedlbauer Speed Fax +", "Siemens I-Surf", "Acer P10", "HST Saphir", "Telekom A4T", "Scitel Quadro", "Gazel", "HFC 2BDS0 PCI", "Winbond 6692", - "HFC 2BDS0 SX", + "HFC 2BDS0 SX", "NETspider-U", "HFC-2BDS0-SP PCMCIA", }; void HiSax_closecard(int cardnr); @@ -257,7 +107,6 @@ EXPORT_SYMBOL(elsa_init_pcmcia); #define DEFAULT_CFG {11,0x170,0,0} int avm_a1_init_pcmcia(void*, int, int*, int); EXPORT_SYMBOL(avm_a1_init_pcmcia); -EXPORT_SYMBOL(HiSax_closecard); #endif /* CONFIG_HISAX_AVM_A1_PCMCIA */ #ifdef CONFIG_HISAX_FRITZPCI @@ -349,7 +198,7 @@ EXPORT_SYMBOL(sedl_init_pcmcia); #ifdef CONFIG_HISAX_NETJET #undef DEFAULT_CARD #undef DEFAULT_CFG -#define DEFAULT_CARD ISDN_CTYPE_NETJET +#define DEFAULT_CARD ISDN_CTYPE_NETJET_S #define DEFAULT_CFG {0,0,0,0} #endif @@ -372,6 +221,8 @@ EXPORT_SYMBOL(sedl_init_pcmcia); #undef DEFAULT_CFG #define DEFAULT_CARD ISDN_CTYPE_HFC_SX #define DEFAULT_CFG {5,0x2E0,0,0} +int hfc_init_pcmcia(void*, int, int*, int); +EXPORT_SYMBOL(hfc_init_pcmcia); #endif @@ -431,22 +282,29 @@ EXPORT_SYMBOL(sedl_init_pcmcia); #define DEFAULT_CFG {0,0,0,0} #endif +#ifdef CONFIG_HISAX_NETJET_U +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_NETJET_U +#define DEFAULT_CFG {0,0,0,0} +#endif + #ifdef CONFIG_HISAX_1TR6 #define DEFAULT_PROTO ISDN_PTYPE_1TR6 #define DEFAULT_PROTO_NAME "1TR6" #endif -#ifdef CONFIG_HISAX_EURO -#undef DEFAULT_PROTO -#define DEFAULT_PROTO ISDN_PTYPE_EURO -#undef DEFAULT_PROTO_NAME -#define DEFAULT_PROTO_NAME "EURO" -#endif #ifdef CONFIG_HISAX_NI1 #undef DEFAULT_PROTO #define DEFAULT_PROTO ISDN_PTYPE_NI1 #undef DEFAULT_PROTO_NAME #define DEFAULT_PROTO_NAME "NI1" #endif +#ifdef CONFIG_HISAX_EURO +#undef DEFAULT_PROTO +#define DEFAULT_PROTO ISDN_PTYPE_EURO +#undef DEFAULT_PROTO_NAME +#define DEFAULT_PROTO_NAME "EURO" +#endif #ifndef DEFAULT_PROTO #define DEFAULT_PROTO ISDN_PTYPE_UNKNOWN #define DEFAULT_PROTO_NAME "UNKNOWN" @@ -455,6 +313,10 @@ EXPORT_SYMBOL(sedl_init_pcmcia); #error "HiSax: No cards configured" #endif +int hisax_init_pcmcia(void *, int *, struct IsdnCard *); +EXPORT_SYMBOL(hisax_init_pcmcia); +EXPORT_SYMBOL(HiSax_closecard); + #define FIRST_CARD { \ DEFAULT_CARD, \ DEFAULT_PROTO, \ @@ -551,9 +413,9 @@ HiSaxVersion(void)) printk(KERN_INFO "HiSax: Linux Driver for passive ISDN cards\n"); #ifdef MODULE - printk(KERN_INFO "HiSax: Version 3.3e (module)\n"); + printk(KERN_INFO "HiSax: Version 3.5 (module)\n"); #else - printk(KERN_INFO "HiSax: Version 3.3e (kernel)\n"); + printk(KERN_INFO "HiSax: Version 3.5 (kernel)\n"); #endif strcpy(tmp, l1_revision); printk(KERN_INFO "HiSax: Layer1 Revision %s\n", HiSax_getrev(tmp)); @@ -689,8 +551,8 @@ extern int setup_sportster(struct IsdnCard *card); extern int setup_mic(struct IsdnCard *card); #endif -#if CARD_NETJET -extern int setup_netjet(struct IsdnCard *card); +#if CARD_NETJET_S +extern int setup_netjet_s(struct IsdnCard *card); #endif #if CARD_HFCS @@ -741,6 +603,10 @@ extern int setup_gazel(struct IsdnCard *card); extern int setup_w6692(struct IsdnCard *card); #endif +#if CARD_NETJET_U +extern int setup_netjet_u(struct IsdnCard *card); +#endif + /* * Find card with given driverId */ @@ -943,7 +809,7 @@ ll_stop(struct IsdnCardState *cs) ic.command = ISDN_STAT_STOP; ic.driver = cs->myid; cs->iif.statcallb(&ic); - CallcFreeChan(cs); +// CallcFreeChan(cs); } static void @@ -1012,7 +878,7 @@ HISAX_INITFUNC(static int init_card(struct IsdnCardState *cs)) while (cnt) { cs->cardmsg(cs, CARD_INIT, NULL); sti(); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); /* Timeout 10ms */ schedule_timeout((10*HZ)/1000); restore_flags(flags); @@ -1066,6 +932,8 @@ checkcard(int cardnr, char *id, int *busy_flag)) cs->busy_flag = busy_flag; cs->irq_flags = I4L_IRQ_FLAG; #if TEI_PER_CARD + if (card->protocol == ISDN_PTYPE_NI1) + test_and_set_bit(FLG_TWO_DCHAN, &cs->HW_Flags); #else test_and_set_bit(FLG_TWO_DCHAN, &cs->HW_Flags); #endif @@ -1210,9 +1078,9 @@ checkcard(int cardnr, char *id, int *busy_flag)) ret = setup_mic(card); break; #endif -#if CARD_NETJET - case ISDN_CTYPE_NETJET: - ret = setup_netjet(card); +#if CARD_NETJET_S + case ISDN_CTYPE_NETJET_S: + ret = setup_netjet_s(card); break; #endif #if CARD_HFCS @@ -1275,6 +1143,11 @@ checkcard(int cardnr, char *id, int *busy_flag)) case ISDN_CTYPE_W6692: ret = setup_w6692(card); break; +#endif +#if CARD_NETJET_U + case ISDN_CTYPE_NETJET_U: + ret = setup_netjet_u(card); + break; #endif default: printk(KERN_WARNING @@ -1395,6 +1268,9 @@ HiSax_closecard(int cardnr) if (cards[cardnr].cs) { ll_stop(cards[cardnr].cs); release_tei(cards[cardnr].cs); + + CallcFreeChan(cards[cardnr].cs); + closecard(cardnr); if (cards[cardnr].cs->irq) free_irq(cards[cardnr].cs->irq, cards[cardnr].cs); @@ -1453,10 +1329,19 @@ HiSax_reportcard(int cardnr, int sel) __initfunc(int HiSax_init(void)) { - int i; + int i,j; #ifdef MODULE int nzproto = 0; + if (!type[0]) { + /* We 'll register drivers later, but init basic functions*/ + CallcNew(); + Isdnl3New(); + Isdnl2New(); + TeiNew(); + Isdnl1New(); + return 0; + } #ifdef CONFIG_HISAX_ELSA if (type[0] == ISDN_CTYPE_ELSA_PCMCIA) { /* we have exported and return in this case */ @@ -1475,47 +1360,53 @@ HiSax_init(void)) return 0; } #endif +#ifdef CONFIG_HISAX_HFC_SX + if (type[0] == ISDN_CTYPE_HFC_SP_PCMCIA) { + /* we have to export and return in this case */ + return 0; + } +#endif #endif nrcards = 0; HiSaxVersion(); #ifdef MODULE if (id) /* If id= string used */ HiSax_id = id; - for (i = 0; i < HISAX_MAX_CARDS; i++) { - cards[i].typ = type[i]; + for (i = j = 0; j < HISAX_MAX_CARDS; i++) { + cards[j].typ = type[i]; if (protocol[i]) { - cards[i].protocol = protocol[i]; + cards[j].protocol = protocol[i]; nzproto++; } switch (type[i]) { case ISDN_CTYPE_16_0: - cards[i].para[0] = irq[i]; - cards[i].para[1] = mem[i]; - cards[i].para[2] = io[i]; + cards[j].para[0] = irq[i]; + cards[j].para[1] = mem[i]; + cards[j].para[2] = io[i]; break; case ISDN_CTYPE_8_0: - cards[i].para[0] = irq[i]; - cards[i].para[1] = mem[i]; + cards[j].para[0] = irq[i]; + cards[j].para[1] = mem[i]; break; #ifdef IO0_IO1 case ISDN_CTYPE_PNP: case ISDN_CTYPE_NICCY: - cards[i].para[0] = irq[i]; - cards[i].para[1] = io0[i]; - cards[i].para[2] = io1[i]; + cards[j].para[0] = irq[i]; + cards[j].para[1] = io0[i]; + cards[j].para[2] = io1[i]; break; case ISDN_CTYPE_COMPAQ_ISA: - cards[i].para[0] = irq[i]; - cards[i].para[1] = io0[i]; - cards[i].para[2] = io1[i]; - cards[i].para[3] = io[i]; + cards[j].para[0] = irq[i]; + cards[j].para[1] = io0[i]; + cards[j].para[2] = io1[i]; + cards[j].para[3] = io[i]; break; #endif case ISDN_CTYPE_ELSA: case ISDN_CTYPE_HFC_PCI: - cards[i].para[0] = io[i]; + cards[j].para[0] = io[i]; break; case ISDN_CTYPE_16_3: case ISDN_CTYPE_TELESPCMCIA: @@ -1539,26 +1430,43 @@ HiSax_init(void)) case ISDN_CTYPE_HSTSAPHIR: case ISDN_CTYPE_GAZEL: case ISDN_CTYPE_HFC_SX: - cards[i].para[0] = irq[i]; - cards[i].para[1] = io[i]; + case ISDN_CTYPE_HFC_SP_PCMCIA: + cards[j].para[0] = irq[i]; + cards[j].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]; + cards[j].para[0] = irq[i]; + cards[j].para[1] = io[i]; + cards[j].para[2] = mem[i]; break; case ISDN_CTYPE_ELSA_PCI: - case ISDN_CTYPE_NETJET: + case ISDN_CTYPE_NETJET_S: case ISDN_CTYPE_AMD7930: case ISDN_CTYPE_TELESPCI: case ISDN_CTYPE_W6692: + case ISDN_CTYPE_NETJET_U: break; case ISDN_CTYPE_BKM_A4T: - break; + break; case ISDN_CTYPE_SCT_QUADRO: - cards[i].para[0] = irq[i]; + if (irq[i]) { + cards[j].para[0] = irq[i]; + } else { + /* QUADRO is a 4 BRI card */ + cards[j++].para[0] = 1; + cards[j].typ = ISDN_CTYPE_SCT_QUADRO; + cards[j].protocol = protocol[i]; + cards[j++].para[0] = 2; + cards[j].typ = ISDN_CTYPE_SCT_QUADRO; + cards[j].protocol = protocol[i]; + cards[j++].para[0] = 3; + cards[j].typ = ISDN_CTYPE_SCT_QUADRO; + cards[j].protocol = protocol[i]; + cards[j].para[0] = 4; + } break; } + j++; } if (!nzproto) { printk(KERN_WARNING "HiSax: Warning - no protocol specified\n"); @@ -1666,6 +1574,55 @@ int elsa_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot) } #endif +#ifdef CONFIG_HISAX_HFC_SX +int hfc_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot) +{ +#ifdef MODULE + int i; + int nzproto = 0; + + nrcards = 0; + HiSaxVersion(); + /* Initialize all structs, even though we only accept + two pcmcia cards + */ + for (i = 0; i < HISAX_MAX_CARDS; i++) { + cards[i].para[0] = irq[i]; + cards[i].para[1] = io[i]; + cards[i].typ = type[i]; + if (protocol[i]) { + cards[i].protocol = protocol[i]; + nzproto++; + } + } + cards[0].para[0] = pcm_irq; + cards[0].para[1] = (int)pcm_iob; + cards[0].protocol = prot; + cards[0].typ = ISDN_CTYPE_HFC_SP_PCMCIA; + nzproto = 1; + + if (!HiSax_id) + HiSax_id = HiSaxID; + if (!HiSaxID[0]) + strcpy(HiSaxID, "HiSax"); + for (i = 0; i < HISAX_MAX_CARDS; i++) + if (cards[i].typ > 0) + nrcards++; + printk(KERN_DEBUG "HiSax: Total %d card%s defined\n", + nrcards, (nrcards > 1) ? "s" : ""); + + Isdnl1New(); + CallcNew(); + Isdnl3New(); + Isdnl2New(); + TeiNew(); + HiSax_inithardware(busy_flag); + printk(KERN_NOTICE "HiSax: module installed\n"); +#endif + return (0); +} +#endif + #ifdef CONFIG_HISAX_SEDLBAUER int sedl_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot) { @@ -1763,3 +1720,21 @@ int avm_a1_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot) return (0); } #endif + +int hisax_init_pcmcia(void *pcm_iob, int *busy_flag, struct IsdnCard *card) +{ + u_char ids[16]; + int ret = -1; + + cards[nrcards] = *card; + if (nrcards) + sprintf(ids, "HiSax%d", nrcards); + else + sprintf(ids, "HiSax"); + if (!checkcard(nrcards, ids, busy_flag)) { + return(-1); + } + ret = nrcards; + nrcards++; + return (ret); +} diff --git a/drivers/isdn/hisax/diva.c b/drivers/isdn/hisax/diva.c index 48e7e8075012..638b19a1f801 100644 --- a/drivers/isdn/hisax/diva.c +++ b/drivers/isdn/hisax/diva.c @@ -1,5 +1,5 @@ -/* $Id: diva.c,v 1.18 1999/12/19 13:09:41 keil Exp $ - +/* $Id: diva.c,v 1.21 2000/06/26 08:59:12 keil Exp $ + * * diva.c low level stuff for Eicon.Diehl Diva Family ISDN cards * * Author Karsten Keil (keil@isdn4linux.de) @@ -10,67 +10,6 @@ * * Thanks to Eicon Technology for documents and informations * - * - * $Log: diva.c,v $ - * Revision 1.18 1999/12/19 13:09:41 keil - * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for - * signal proof delays - * - * Revision 1.17 1999/09/04 06:20:06 keil - * Changes from kernel set_current_state() - * - * 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 - * - * Revision 1.9 1998/06/27 22:52:03 keil - * support for Diva 2.01 - * - * Revision 1.8 1998/05/25 12:57:46 keil - * HiSax golden code from certification, Don't use !!! - * No leased lines, no X75, but many changes. - * - * Revision 1.7 1998/04/15 16:42:36 keil - * new init code - * new PCI init (2.1.94) - * - * Revision 1.6 1998/03/07 22:56:57 tsbogend - * made HiSax working on Linux/Alpha - * - * Revision 1.5 1998/02/02 13:29:38 keil - * fast io - * - * Revision 1.4 1997/11/08 21:35:44 keil - * new l1 init - * - * Revision 1.3 1997/11/06 17:13:33 keil - * New 2.1 init code - * - * Revision 1.2 1997/10/29 18:55:55 keil - * changes for 2.1.60 (irq2dev_map) - * - * Revision 1.1 1997/09/18 17:11:20 keil - * first version - * - * */ #define __NO_VERSION__ @@ -84,7 +23,7 @@ extern const char *CardType[]; -const char *Diva_revision = "$Revision: 1.18 $"; +const char *Diva_revision = "$Revision: 1.21 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -108,12 +47,18 @@ const char *Diva_revision = "$Revision: 1.18 $"; #define DIVA_IPAC_PCI 4 /* PCI stuff */ -#define PCI_VENDOR_EICON_DIEHL 0x1133 -#define PCI_DIVA20PRO_ID 0xe001 -#define PCI_DIVA20_ID 0xe002 -#define PCI_DIVA20PRO_U_ID 0xe003 -#define PCI_DIVA20_U_ID 0xe004 -#define PCI_DIVA_201 0xe005 +#ifndef PCI_VENDOR_ID_EICON +#define PCI_VENDOR_ID_EICON 0x1133 +#endif +#ifndef PCI_DEVICE_ID_EICON_DIVA20 +#define PCI_DEVICE_ID_EICON_DIVA20 0xe002 +#endif +#ifndef PCI_DEVICE_ID_EICON_DIVA20_U +#define PCI_DEVICE_ID_EICON_DIVA20_U 0xe004 +#endif +#ifndef PCI_DEVICE_ID_EICON_DIVA201 +#define PCI_DEVICE_ID_EICON_DIVA201 0xe005 +#endif /* CTRL (Read) */ #define DIVA_IRQ_STAT 0x01 @@ -131,10 +76,16 @@ const char *Diva_revision = "$Revision: 1.18 $"; /* Siemens PITA */ #define PITA_MISC_REG 0x1c +#ifdef __BIG_ENDIAN +#define PITA_PARA_SOFTRESET 0x00000001 +#define PITA_PARA_MPX_MODE 0x00000004 +#define PITA_INT0_ENABLE 0x00000200 +#else #define PITA_PARA_SOFTRESET 0x01000000 #define PITA_PARA_MPX_MODE 0x04000000 #define PITA_INT0_ENABLE 0x00020000 -#define PITA_INT0_STATUS 0x00000002 +#endif +#define PITA_INT0_STATUS 0x02 static inline u_char readreg(unsigned int ale, unsigned int adr, u_char off) @@ -183,7 +134,7 @@ writefifo(unsigned int ale, unsigned int adr, u_char off, u_char *data, int size static inline u_char memreadreg(unsigned long adr, u_char off) { - return(0xff & *((unsigned int *) + return(*((unsigned char *) (((unsigned int *)adr) + off))); } @@ -754,30 +705,30 @@ reset_diva(struct IsdnCardState *cs) sti(); if (cs->subtyp == DIVA_IPAC_ISA) { writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x20); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x00); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xc0); } 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_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); *ireg = PITA_PARA_MPX_MODE; - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); 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_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); 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_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); if (cs->subtyp == DIVA_ISA) cs->hw.diva.ctrl_reg |= DIVA_ISA_LED_A; @@ -931,28 +882,30 @@ setup_diva(struct IsdnCard *card)) } cs->subtyp = 0; - if ((dev_diva = pci_find_device(PCI_VENDOR_EICON_DIEHL, - PCI_DIVA20_ID, dev_diva))) { + if ((dev_diva = pci_find_device(PCI_VENDOR_ID_EICON, + PCI_DEVICE_ID_EICON_DIVA20, dev_diva))) { + if (pci_enable_device(dev_diva)) + return(0); cs->subtyp = DIVA_PCI; cs->irq = dev_diva->irq; - cs->hw.diva.cfg_reg = dev_diva->base_address[ 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->hw.diva.cfg_reg = dev_diva->base_address[ 2] & PCI_BASE_ADDRESS_IO_MASK; + } else if ((dev_diva_u = pci_find_device(PCI_VENDOR_ID_EICON, + PCI_DEVICE_ID_EICON_DIVA20_U, dev_diva_u))) { + if (pci_enable_device(dev_diva_u)) + return(0); cs->subtyp = DIVA_PCI; cs->irq = dev_diva_u->irq; - cs->hw.diva.cfg_reg = dev_diva_u->base_address[ 2] - & PCI_BASE_ADDRESS_IO_MASK; - } else if ((dev_diva201 = pci_find_device(PCI_VENDOR_EICON_DIEHL, - PCI_DIVA_201, dev_diva201))) { + cs->hw.diva.cfg_reg = dev_diva_u->base_address[ 2] & PCI_BASE_ADDRESS_IO_MASK; + } else if ((dev_diva201 = pci_find_device(PCI_VENDOR_ID_EICON, + PCI_DEVICE_ID_EICON_DIVA201, dev_diva201))) { + if (pci_enable_device(dev_diva201)) + return(0); cs->subtyp = DIVA_IPAC_PCI; cs->irq = dev_diva201->irq; cs->hw.diva.pci_cfg = - (ulong) ioremap((dev_diva201->base_address[ 0] - & PCI_BASE_ADDRESS_IO_MASK), 4096); + (ulong) ioremap(dev_diva201->base_address[ 0] & PCI_BASE_ADDRESS_MEM_MASK, 4096); cs->hw.diva.cfg_reg = - (ulong) ioremap((dev_diva201->base_address[ 1] - & PCI_BASE_ADDRESS_IO_MASK), 4096); + (ulong) ioremap(dev_diva201->base_address[ 1] & PCI_BASE_ADDRESS_MEM_MASK, 4096); } else { printk(KERN_WARNING "Diva: No PCI card found\n"); return(0); diff --git a/drivers/isdn/hisax/elsa.c b/drivers/isdn/hisax/elsa.c index c804b9482f61..422895edcaae 100644 --- a/drivers/isdn/hisax/elsa.c +++ b/drivers/isdn/hisax/elsa.c @@ -1,5 +1,5 @@ -/* $Id: elsa.c,v 2.20 1999/12/19 13:09:42 keil Exp $ - +/* $Id: elsa.c,v 2.23 2000/06/26 08:59:12 keil Exp $ + * * elsa.c low level stuff for Elsa isdn cards * * Author Karsten Keil (keil@isdn4linux.de) @@ -13,80 +13,6 @@ * Klaus Lichtenwalder (Klaus.Lichtenwalder@WebForum.DE) * for ELSA PCMCIA support * - * $Log: elsa.c,v $ - * Revision 2.20 1999/12/19 13:09:42 keil - * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for - * signal proof delays - * - * Revision 2.19 1999/09/04 06:20:06 keil - * Changes from kernel set_current_state() - * - * Revision 2.18 1999/08/25 16:50:54 keil - * Fix bugs which cause 2.3.14 hangs (waitqueue init) - * - * 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 - * - * Revision 2.11 1998/08/20 13:50:34 keil - * More support for hybrid modem (not working yet) - * - * Revision 2.10 1998/08/13 23:36:22 keil - * HiSax 3.1 - don't work stable with current LinkLevel - * - * Revision 2.9 1998/05/25 12:57:48 keil - * HiSax golden code from certification, Don't use !!! - * No leased lines, no X75, but many changes. - * - * Revision 2.8 1998/04/15 16:41:42 keil - * QS3000 PCI support - * new init code - * new PCI init (2.1.94) - * - * Revision 2.7 1998/03/07 22:56:58 tsbogend - * made HiSax working on Linux/Alpha - * - * Revision 2.6 1998/02/02 13:29:40 keil - * fast io - * - * Revision 2.5 1998/01/31 21:41:45 keil - * changes for newer 2.1 kernels - * - * Revision 2.4 1997/11/08 21:35:46 keil - * new l1 init - * - * Revision 2.3 1997/11/06 17:15:09 keil - * New 2.1 init; PCMCIA wrapper changes - * - * Revision 2.2 1997/10/29 18:57:09 keil - * changes for 2.1.60, arcofi support - * - * Revision 2.1 1997/07/27 21:47:08 keil - * new interface structures - * - * Revision 2.0 1997/06/26 11:02:40 keil - * New Layer and card interface - * - * old changes removed KKe - * */ #define __NO_VERSION__ @@ -103,10 +29,10 @@ extern const char *CardType[]; -const char *Elsa_revision = "$Revision: 2.20 $"; +const char *Elsa_revision = "$Revision: 2.23 $"; 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", "Microlink PCI", "QS 3000 PCI", "PCMCIA-IPAC" }; const char *ITACVer[] = @@ -140,9 +66,15 @@ const char *ITACVer[] = #define ELSA_PCMCIA_IPAC 11 /* PCI stuff */ -#define PCI_VENDOR_ELSA 0x1048 -#define PCI_QS1000_ID 0x1000 -#define PCI_QS3000_ID 0x3000 +#ifndef PCI_VENDOR_ID_ELSA +#define PCI_VENDOR_ID_ELSA 0x1048 +#endif +#ifndef PCI_DEVICE_ID_ELSA_MIRCOLINK +#define PCI_DEVICE_ID_ELSA_MIRCOLINK 0x1000 +#endif +#ifndef PCI_DEVICE_ID_ELSA_QS3000 +#define PCI_DEVICE_ID_ELSA_QS3000 0x3000 +#endif #define ELSA_PCI_IRQ_MASK 0x04 /* ITAC Registeradressen (only Microlink PC) */ @@ -582,10 +514,10 @@ reset_elsa(struct IsdnCardState *cs) save_flags(flags); sti(); writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x20); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x00); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xc0); schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ restore_flags(flags); @@ -789,7 +721,7 @@ Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg) cs->hw.elsa.status |= ELSA_TIMER_AKTIV; byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); byteout(cs->hw.elsa.timer, 0); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((110*HZ)/1000); restore_flags(flags); cs->hw.elsa.ctrl_reg &= ~ELSA_ENA_TIMER_INT; @@ -797,7 +729,8 @@ Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg) cs->hw.elsa.status &= ~ELSA_TIMER_AKTIV; printk(KERN_INFO "Elsa: %d timer tics in 110 msek\n", cs->hw.elsa.counter); - if (abs(cs->hw.elsa.counter - 13) < 3) { + if ((cs->hw.elsa.counter > 10) && + (cs->hw.elsa.counter < 16)) { printk(KERN_INFO "Elsa: timer and irq OK\n"); ret = 0; } else { @@ -1056,22 +989,22 @@ setup_elsa(struct IsdnCard *card) return(0); } cs->subtyp = 0; - if ((dev_qs1000 = pci_find_device(PCI_VENDOR_ELSA, PCI_QS1000_ID, - dev_qs1000))) { - cs->subtyp = ELSA_QS1000PCI; + if ((dev_qs1000 = pci_find_device(PCI_VENDOR_ID_ELSA, + PCI_DEVICE_ID_ELSA_MIRCOLINK, dev_qs1000))) { + if (pci_enable_device(dev_qs1000)) + return(0); + cs->subtyp = ELSA_QS1000PCI; cs->irq = dev_qs1000->irq; - cs->hw.elsa.cfg = dev_qs1000->base_address[ 1] & - PCI_BASE_ADDRESS_IO_MASK; - cs->hw.elsa.base = dev_qs1000->base_address[ 3] & - PCI_BASE_ADDRESS_IO_MASK; - } else if ((dev_qs3000 = pci_find_device(PCI_VENDOR_ELSA, - PCI_QS3000_ID, dev_qs3000))) { + cs->hw.elsa.cfg = dev_qs1000->base_address[ 1] & PCI_BASE_ADDRESS_IO_MASK; + cs->hw.elsa.base = dev_qs1000->base_address[ 3] & PCI_BASE_ADDRESS_IO_MASK; + } else if ((dev_qs3000 = pci_find_device(PCI_VENDOR_ID_ELSA, + PCI_DEVICE_ID_ELSA_QS3000, dev_qs3000))) { + if (pci_enable_device(dev_qs3000)) + return(0); cs->subtyp = ELSA_QS3000PCI; cs->irq = dev_qs3000->irq; - cs->hw.elsa.cfg = dev_qs3000->base_address[ 1] & - PCI_BASE_ADDRESS_IO_MASK; - cs->hw.elsa.base = dev_qs3000->base_address[ 3] & - PCI_BASE_ADDRESS_IO_MASK; + cs->hw.elsa.cfg = dev_qs3000->base_address[ 1] & PCI_BASE_ADDRESS_IO_MASK; + cs->hw.elsa.base = dev_qs3000->base_address[ 3] & PCI_BASE_ADDRESS_IO_MASK; } else { printk(KERN_WARNING "Elsa: No PCI card found\n"); return(0); diff --git a/drivers/isdn/hisax/elsa_ser.c b/drivers/isdn/hisax/elsa_ser.c index 529a74815169..15afa3d58bdf 100644 --- a/drivers/isdn/hisax/elsa_ser.c +++ b/drivers/isdn/hisax/elsa_ser.c @@ -1,3 +1,10 @@ +/* $Id: elsa_ser.c,v 2.9 2000/06/26 08:59:12 keil Exp $ + * + * stuff for the serial modem on ELSA cards + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ #include #include #include diff --git a/drivers/isdn/hisax/fsm.c b/drivers/isdn/hisax/fsm.c index 665fa2c74a1a..a483b5824f17 100644 --- a/drivers/isdn/hisax/fsm.c +++ b/drivers/isdn/hisax/fsm.c @@ -1,48 +1,12 @@ -/* $Id: fsm.c,v 1.11 1999/12/23 15:09:32 keil Exp $ - +/* $Id: fsm.c,v 1.13 2000/06/26 08:59:12 keil Exp $ + * * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden * * Thanks to Jan den Ouden * Fritz Elfert * - * $Log: fsm.c,v $ - * Revision 1.11 1999/12/23 15:09:32 keil - * change email - * - * Revision 1.10 1998/11/15 23:54:39 keil - * changes from 2.0 - * - * Revision 1.9 1998/03/26 07:10:02 paul - * The jumpmatrix table in struct Fsm was an array of "int". This is not - * large enough for pointers to functions on Linux/Alpha (instant crash - * on "insmod hisax). Now there is a typedef for the pointer to function. - * This also prevents warnings about "incompatible pointer types". - * - * Revision 1.8 1998/03/07 22:56:59 tsbogend - * made HiSax working on Linux/Alpha - * - * Revision 1.7 1997/11/06 17:09:13 keil - * New 2.1 init code - * - * Revision 1.6 1997/07/27 21:42:25 keil - * proof Fsm routines - * - * Revision 1.5 1997/06/26 11:10:05 keil - * Restart timer function added - * - * Revision 1.4 1997/04/06 22:56:42 keil - * Some cosmetic changes - * - * Revision 1.3 1997/02/16 01:04:08 fritz - * Bugfix: Changed timer handling caused hang with 2.1.X - * - * Revision 1.2 1997/01/09 20:57:27 keil - * cleanup & FSM_TIMER_DEBUG - * - * Revision 1.1 1996/10/13 20:04:52 keil - * Initial revision - * + * This file is (c) under GNU PUBLIC LICENSE * */ #define __NO_VERSION__ @@ -156,7 +120,7 @@ FsmAddTimer(struct FsmTimer *ft, (long) ft, millisec, where); #endif - if (ft->tl.next || ft->tl.prev) { + if (timer_pending(&ft->tl)) { printk(KERN_WARNING "FsmAddTimer: timer already active!\n"); ft->fi->printdebug(ft->fi, "FsmAddTimer already active!"); return -1; @@ -180,7 +144,7 @@ FsmRestartTimer(struct FsmTimer *ft, (long) ft, millisec, where); #endif - if (ft->tl.next || ft->tl.prev) + if (timer_pending(&ft->tl)) del_timer(&ft->tl); init_timer(&ft->tl); ft->event = event; diff --git a/drivers/isdn/hisax/gazel.c b/drivers/isdn/hisax/gazel.c index 56b05b5206a8..c008472d8c68 100644 --- a/drivers/isdn/hisax/gazel.c +++ b/drivers/isdn/hisax/gazel.c @@ -1,32 +1,11 @@ -/* $Id: gazel.c,v 2.6 1999/08/22 20:27:03 calle Exp $ - +/* $Id: gazel.c,v 2.8 2000/06/26 08:59:12 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.6 1999/08/22 20:27:03 calle - * backported changes from kernel 2.3.14: - * - several #include "config.h" gone, others come. - * - "struct device" changed to "struct net_device" in 2.3.14, added a - * define in isdn_compat.h for older kernel versions. - * - * 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 + * This file is (c) under GNU PUBLIC LICENSE * */ #include @@ -39,7 +18,7 @@ #include extern const char *CardType[]; -const char *gazel_revision = "$Revision: 2.6 $"; +const char *gazel_revision = "$Revision: 2.8 $"; #define R647 1 #define R685 2 @@ -47,10 +26,18 @@ const char *gazel_revision = "$Revision: 2.6 $"; #define R742 4 /* Gazel R685 stuff */ -#define GAZEL_MANUFACTURER 0x10b5 -#define GAZEL_R685 0x1030 -#define GAZEL_R753 0x1152 -#define GAZEL_DJINN_ITOO 0x1151 +#ifndef PCI_VENDOR_ID_PLX +#define PCI_VENDOR_ID_PLX 0x10b5 +#endif +#ifndef PCI_DEVICE_ID_PLX_R685 +#define PCI_DEVICE_ID_PLX_R685 0x1030 +#endif +#ifndef PCI_DEVICE_ID_PLX_R753 +#define PCI_DEVICE_ID_PLX_R753 0x1152 +#endif +#ifndef PCI_DEVICE_ID_PLX_DJINN_ITOO +#define PCI_DEVICE_ID_PLX_DJINN_ITOO 0x1151 +#endif #define PLX_CNTRL 0x50 /* registre de controle PLX */ #define RESET_GAZEL 0x4 @@ -586,24 +573,25 @@ setup_gazelpci(struct IsdnCardState *cs) printk(KERN_WARNING "Gazel: No PCI bus present\n"); return 1; } - seekcard = GAZEL_R685; + seekcard = PCI_DEVICE_ID_PLX_R685; for (nbseek = 0; nbseek < 3; nbseek++) { - if ((dev_tel = pci_find_device(GAZEL_MANUFACTURER, seekcard, dev_tel))) { - + if ((dev_tel = pci_find_device(PCI_VENDOR_ID_PLX, seekcard, dev_tel))) { + if (pci_enable_device(dev_tel)) + return 1; pci_irq = dev_tel->irq; - pci_ioaddr0 = dev_tel->base_address[ 1]; - pci_ioaddr1 = dev_tel->base_address[ 2]; + pci_ioaddr0 = dev_tel->base_address[ 1] & PCI_BASE_ADDRESS_IO_MASK; + pci_ioaddr1 = dev_tel->base_address[ 2] & PCI_BASE_ADDRESS_IO_MASK; found = 1; } if (found) break; else { switch (seekcard) { - case GAZEL_R685: - seekcard = GAZEL_R753; + case PCI_DEVICE_ID_PLX_R685: + seekcard = PCI_DEVICE_ID_PLX_R753; break; - case GAZEL_R753: - seekcard = GAZEL_DJINN_ITOO; + case PCI_DEVICE_ID_PLX_R753: + seekcard = PCI_DEVICE_ID_PLX_DJINN_ITOO; break; } } @@ -632,7 +620,7 @@ setup_gazelpci(struct IsdnCardState *cs) cs->irq_flags |= SA_SHIRQ; switch (seekcard) { - case GAZEL_R685: + case PCI_DEVICE_ID_PLX_R685: printk(KERN_INFO "Gazel: Card PCI R685 found\n"); cs->subtyp = R685; cs->dc.isac.adf2 = 0x87; @@ -643,8 +631,8 @@ setup_gazelpci(struct IsdnCardState *cs) "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: + case PCI_DEVICE_ID_PLX_R753: + case PCI_DEVICE_ID_PLX_DJINN_ITOO: printk(KERN_INFO "Gazel: Card PCI R753 found\n"); cs->subtyp = R753; test_and_set_bit(HW_IPAC, &cs->HW_Flags); diff --git a/drivers/isdn/hisax/hfc_2bds0.c b/drivers/isdn/hisax/hfc_2bds0.c index 2b22cdfea09a..977adf330e28 100644 --- a/drivers/isdn/hisax/hfc_2bds0.c +++ b/drivers/isdn/hisax/hfc_2bds0.c @@ -1,43 +1,10 @@ -/* $Id: hfc_2bds0.c,v 1.11 1999/12/23 15:09:32 keil Exp $ +/* $Id: hfc_2bds0.c,v 1.13 2000/06/26 08:59:12 keil Exp $ * * specific routines for CCD's HFC 2BDS0 * * Author Karsten Keil (keil@isdn4linux.de) * - * - * $Log: hfc_2bds0.c,v $ - * Revision 1.11 1999/12/23 15:09:32 keil - * change email - * - * Revision 1.10 1999/10/14 20:25:28 keil - * add a statistic for error monitoring - * - * 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 - * - * Revision 1.7 1998/09/30 22:24:45 keil - * Fix missing line in setstack* - * - * Revision 1.6 1998/08/13 23:36:26 keil - * HiSax 3.1 - don't work stable with current LinkLevel - * - * Revision 1.5 1998/06/27 22:52:58 keil - * make 16.3c working with 3.0 - * - * Revision 1.4 1998/05/25 12:57:52 keil - * HiSax golden code from certification, Don't use !!! - * No leased lines, no X75, but many changes. - * - * Revision 1.3 1998/02/12 23:07:22 keil - * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() - * - * Revision 1.2 1998/02/02 13:26:13 keil - * New - * - * + * This file is (c) under GNU PUBLIC LICENSE * */ #define __NO_VERSION__ diff --git a/drivers/isdn/hisax/hfc_2bds0.h b/drivers/isdn/hisax/hfc_2bds0.h index 32f703662ae7..66cf0b11aa87 100644 --- a/drivers/isdn/hisax/hfc_2bds0.h +++ b/drivers/isdn/hisax/hfc_2bds0.h @@ -1,18 +1,10 @@ -/* $Id: hfc_2bds0.h,v 1.3 1999/12/23 15:09:32 keil Exp $ - +/* $Id: hfc_2bds0.h,v 1.4 2000/06/26 08:59:12 keil Exp $ + * * specific defines for CCD's HFC 2BDS0 * * Author Karsten Keil (keil@isdn4linux.de) * - * - * $Log: hfc_2bds0.h,v $ - * Revision 1.3 1999/12/23 15:09:32 keil - * change email - * - * Revision 1.2 1998/02/02 13:26:15 keil - * New - * - * + * This file is (c) under GNU PUBLIC LICENSE * */ diff --git a/drivers/isdn/hisax/hfc_2bs0.c b/drivers/isdn/hisax/hfc_2bs0.c index f3edefa3e4e4..e1e8ba7cd294 100644 --- a/drivers/isdn/hisax/hfc_2bs0.c +++ b/drivers/isdn/hisax/hfc_2bs0.c @@ -1,49 +1,10 @@ -/* $Id: hfc_2bs0.c,v 1.12 1999/12/19 14:17:12 keil Exp $ - +/* $Id: hfc_2bs0.c,v 1.15 2000/07/26 20:46:47 keil Exp $ + * * specific routines for CCD's HFC 2BS0 * * Author Karsten Keil (keil@isdn4linux.de) * - * - * $Log: hfc_2bs0.c,v $ - * Revision 1.12 1999/12/19 14:17:12 keil - * fix compiler warning - * - * Revision 1.11 1999/11/21 12:41:18 werner - * - * Implemented full audio support - * - * Revision 1.10 1999/10/14 20:25:28 keil - * add a statistic for error monitoring - * - * 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 - * - * Revision 1.7 1998/09/30 22:24:46 keil - * Fix missing line in setstack* - * - * Revision 1.6 1998/08/13 23:36:28 keil - * HiSax 3.1 - don't work stable with current LinkLevel - * - * Revision 1.5 1998/05/25 12:57:54 keil - * HiSax golden code from certification, Don't use !!! - * No leased lines, no X75, but many changes. - * - * Revision 1.4 1998/02/12 23:07:29 keil - * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() - * - * Revision 1.3 1997/11/06 17:13:35 keil - * New 2.1 init code - * - * Revision 1.2 1997/10/29 19:04:47 keil - * changes for 2.1 - * - * Revision 1.1 1997/09/11 17:31:33 keil - * Common part for HFC 2BS0 based cards - * + * This file is (c) under GNU PUBLIC LICENSE * */ @@ -242,7 +203,7 @@ hfc_empty_fifo(struct BCState *bcs, int count) ptr = skb_put(skb, count); idx = 0; cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel); - while ((idx < count - 3) && WaitNoBusy(cs)) { + while ((idx < count) && WaitNoBusy(cs)) { *ptr++ = cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip); idx++; } diff --git a/drivers/isdn/hisax/hfc_2bs0.h b/drivers/isdn/hisax/hfc_2bs0.h index 981ae7cb3d68..dd978e781aa0 100644 --- a/drivers/isdn/hisax/hfc_2bs0.h +++ b/drivers/isdn/hisax/hfc_2bs0.h @@ -1,17 +1,10 @@ -/* $Id: hfc_2bs0.h,v 1.2 1999/12/23 15:09:32 keil Exp $ - +/* $Id: hfc_2bs0.h,v 1.3 2000/06/26 08:59:13 keil Exp $ + * * specific defines for CCD's HFC 2BS0 * * Author Karsten Keil (keil@isdn4linux.de) * - * - * $Log: hfc_2bs0.h,v $ - * Revision 1.2 1999/12/23 15:09:32 keil - * change email - * - * Revision 1.1 1997/09/11 17:31:34 keil - * Common part for HFC 2BS0 based cards - * + * This file is (c) under GNU PUBLIC LICENSE * */ diff --git a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c index 33fdc308d3e7..2f4301019e71 100644 --- a/drivers/isdn/hisax/hfc_pci.c +++ b/drivers/isdn/hisax/hfc_pci.c @@ -1,4 +1,4 @@ -/* $Id: hfc_pci.c,v 1.27 2000/02/26 00:35:12 keil Exp $ +/* $Id: hfc_pci.c,v 1.31 2000/08/20 07:32:55 keil Exp $ * hfc_pci.c low level driver for CCD´s hfc-pci based cards * @@ -22,101 +22,6 @@ * 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.27 2000/02/26 00:35:12 keil - * Fix skb freeing in interrupt context - * - * Revision 1.26 2000/02/09 20:22:55 werner - * - * Updated PCI-ID table - * - * Revision 1.25 1999/12/19 13:09:42 keil - * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for - * signal proof delays - * - * Revision 1.24 1999/11/17 23:59:55 werner - * - * removed unneeded data - * - * Revision 1.23 1999/11/07 17:01:55 keil - * fix for 2.3 pci structs - * - * Revision 1.22 1999/10/10 20:14:27 werner - * - * Correct B2-chan usage in conjuntion with echo mode. First implementation of NT-leased line mode. - * - * Revision 1.21 1999/10/02 17:47:49 werner - * - * Changed init order, added correction for page alignment with shared mem - * - * Revision 1.20 1999/09/07 06:18:55 werner - * - * Added io parameter for HFC-PCI based cards. Needed only with multiple cards - * when initialisation/selection order needs to be set. - * - * Revision 1.19 1999/09/04 06:20:06 keil - * Changes from kernel set_current_state() - * - * Revision 1.18 1999/08/29 17:05:44 werner - * corrected tx_lo line setup. Datasheet is not correct. - * - * Revision 1.17 1999/08/28 21:04:27 werner - * Implemented full audio support (transparent mode) - * - * Revision 1.16 1999/08/25 17:01:27 keil - * Use new LL->HL auxcmd call - * - * Revision 1.15 1999/08/22 20:27:05 calle - * backported changes from kernel 2.3.14: - * - several #include "config.h" gone, others come. - * - "struct device" changed to "struct net_device" in 2.3.14, added a - * define in isdn_compat.h for older kernel versions. - * - * Revision 1.14 1999/08/12 18:59:45 werner - * Added further manufacturer and device ids to PCI list - * - * 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 - * - * - * */ #include @@ -129,7 +34,7 @@ extern const char *CardType[]; -static const char *hfcpci_revision = "$Revision: 1.27 $"; +static const char *hfcpci_revision = "$Revision: 1.31 $"; /* table entry in the PCI devices list */ typedef struct { @@ -139,7 +44,9 @@ typedef struct { char *card_name; } PCI_ENTRY; -#define NT_T1_COUNT 20 /* number of 3.125ms interrupts for G2 timeout */ +#define NT_T1_COUNT 20 /* number of 3.125ms interrupts for G2 timeout */ +#define CLKDEL_TE 0x0e /* CLKDEL in TE mode */ +#define CLKDEL_NT 0x6c /* CLKDEL in NT mode */ static const PCI_ENTRY id_list[] = { @@ -158,7 +65,10 @@ static const PCI_ENTRY id_list[] = {0x1051, 0x0100, "Motorola MC145575", "MC145575"}, {0x1397, 0xB100, "Seyeon", "B100"}, {0x15B0, 0x2BD0, "Zoltrix", "2BD0"}, - {0x114f, 0x71, "Digi intl.","Digicom"}, + {0x114F, 0x70,"Digi International", "Digi DataFire Micro V IOM2 (Europe)"}, + {0x114F, 0x71,"Digi International", "Digi DataFire Micro V (Europe)"}, + {0x114F, 0x72,"Digi International", "Digi DataFire Micro V IOM2 (North America)"}, + {0x114F, 0x73,"Digi International", "Digi DataFire Micro V (North America)"}, {0, 0, NULL, NULL}, }; @@ -180,7 +90,7 @@ release_io_hfcpci(struct IsdnCardState *cs) restore_flags(flags); Write_hfc(cs, HFCPCI_CIRM, HFCPCI_RESET); /* Reset On */ sti(); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((30 * HZ) / 1000); /* Timeout 30ms */ Write_hfc(cs, HFCPCI_CIRM, 0); /* Reset Off */ #if CONFIG_PCI @@ -211,10 +121,10 @@ reset_hfcpci(struct IsdnCardState *cs) 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 */ sti(); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((30 * HZ) / 1000); /* Timeout 30ms */ Write_hfc(cs, HFCPCI_CIRM, 0); /* Reset Off */ - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((20 * HZ) / 1000); /* Timeout 20ms */ if (Read_hfc(cs, HFCPCI_STATUS) & 2) printk(KERN_WARNING "HFC-PCI init bit busy\n"); @@ -225,7 +135,7 @@ reset_hfcpci(struct IsdnCardState *cs) cs->hw.hfcpci.trm = 0 + HFCPCI_BTRANS_THRESMASK; /* no echo connect , threshold */ Write_hfc(cs, HFCPCI_TRM, cs->hw.hfcpci.trm); - Write_hfc(cs, HFCPCI_CLKDEL, 0x0e); /* ST-Bit delay for TE-Mode */ + Write_hfc(cs, HFCPCI_CLKDEL, CLKDEL_TE); /* 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 */ @@ -349,6 +259,9 @@ hfcpci_empty_fifo(struct BCState *bcs, bzfifo_type * bz, u_char * bdata, int cou (*(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); +#ifdef ERROR_STATISTIC + bcs->err_inv++; +#endif bz->za[new_f2].z2 = new_z2; bz->f2 = new_f2; /* next buffer */ skb = NULL; @@ -415,6 +328,9 @@ receive_dmsg(struct IsdnCardState *cs) (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]); +#ifdef ERROR_STATISTIC + cs->err_rx++; +#endif 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))) { @@ -601,6 +517,9 @@ hfcpci_fill_dfifo(struct IsdnCardState *cs) if (fcnt > (MAX_D_FRAMES - 1)) { if (cs->debug & L1_DEB_ISAC) debugl1(cs, "hfcpci_fill_Dfifo more as 14 frames"); +#ifdef ERROR_STATISTIC + cs->err_tx++; +#endif return; } /* now determine free bytes in FIFO buffer */ @@ -835,6 +754,7 @@ hfcpci_auxcmd(struct IsdnCardState *cs, isdn_ctrl * ic) (!(cs->hw.hfcpci.int_m1 & (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC + HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC)))) { save_flags(flags); cli(); + Write_hfc(cs, HFCPCI_CLKDEL, CLKDEL_NT); /* ST-Bit delay for NT-Mode */ Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 0); /* HFC ST G0 */ udelay(10); cs->hw.hfcpci.sctrl |= SCTRL_MODE_NT; @@ -1690,7 +1610,7 @@ hfcpci_card_msg(struct IsdnCardState *cs, int mt, void *arg) inithfcpci(cs); save_flags(flags); sti(); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((80 * HZ) / 1000); /* Timeout 80ms */ /* now switch timer interrupt off */ cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_TIMER; @@ -1720,6 +1640,9 @@ __initfunc(int int i; struct pci_dev *tmp_hfcpci = NULL; +#ifdef __BIG_ENDIAN +#error "not running on big endian machines now" +#endif strcpy(tmp, hfcpci_revision); printk(KERN_INFO "HiSax: HFC-PCI driver Rev. %s\n", HiSax_getrev(tmp)); #if CONFIG_PCI @@ -1738,6 +1661,8 @@ __initfunc(int dev_hfcpci); i++; if (tmp_hfcpci) { + if (pci_enable_device(tmp_hfcpci)) + continue; if ((card->para[0]) && (card->para[0] != (tmp_hfcpci->base_address[ 0] & PCI_BASE_ADDRESS_IO_MASK))) continue; else @@ -1761,7 +1686,6 @@ __initfunc(int printk(KERN_WARNING "HFC-PCI: No PCI card found\n"); return (0); } -#ifdef notdef if (((int) cs->hw.hfcpci.pci_io & (PAGE_SIZE - 1))) { printk(KERN_WARNING "HFC-PCI shared mem address will be corrected\n"); pcibios_write_config_word(cs->hw.hfcpci.pci_bus, @@ -1795,7 +1719,6 @@ __initfunc(int } dev_hfcpci->base_address[1] = (int) cs->hw.hfcpci.pci_io; } -#endif if (!cs->hw.hfcpci.pci_io) { printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n"); return (0); diff --git a/drivers/isdn/hisax/hfc_pci.h b/drivers/isdn/hisax/hfc_pci.h index e8597d73049e..9f6bd78eeee4 100644 --- a/drivers/isdn/hisax/hfc_pci.h +++ b/drivers/isdn/hisax/hfc_pci.h @@ -1,5 +1,5 @@ -/* $Id: hfc_pci.h,v 1.7 1999/10/10 20:13:06 werner Exp $ - +/* $Id: hfc_pci.h,v 1.8 2000/06/26 08:59:13 keil Exp $ + * * specific defines for CCD's HFC 2BDS0 PCI chips * * Author Werner Cornelius (werner@isdn4linux.de) @@ -20,28 +20,6 @@ * 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.7 1999/10/10 20:13:06 werner - * - * Corrected timer constant - * - * Revision 1.6 1999/08/28 21:04:29 werner - * Implemented full audio support (transparent mode) - * - * 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 - * - * - * */ /*********************************************/ diff --git a/drivers/isdn/hisax/hfc_sx.c b/drivers/isdn/hisax/hfc_sx.c index 17086d64d8e0..46fac21a4da2 100644 --- a/drivers/isdn/hisax/hfc_sx.c +++ b/drivers/isdn/hisax/hfc_sx.c @@ -1,4 +1,4 @@ -/* $Id: hfc_sx.c,v 1.3 2000/01/20 19:49:36 keil Exp $ +/* $Id: hfc_sx.c,v 1.7 2000/09/08 22:55:59 werner Exp $ * hfc_sx.c low level driver for CCD´s hfc-s+/sp based cards * @@ -21,24 +21,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * $Log: hfc_sx.c,v $ - * Revision 1.3 2000/01/20 19:49:36 keil - * Support teles 13.3c vendor version 2.1 - * - * Revision 1.2 1999/12/19 13:09:42 keil - * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for - * signal proof delays - * - * Revision 1.1 1999/11/18 00:09:18 werner - * - * Initial release of files for HFC-S+ and HFC-SP cards with 32K-RAM. - * Audio and Echo are supported. - * - * - * */ -#include #define __NO_VERSION__ #include "hisax.h" #include "hfc_sx.h" @@ -47,7 +31,7 @@ extern const char *CardType[]; -static const char *hfcsx_revision = "$Revision: 1.3 $"; +static const char *hfcsx_revision = "$Revision: 1.7 $"; /***************************************/ /* IRQ-table for CCDs demo board */ @@ -361,7 +345,7 @@ release_io_hfcsx(struct IsdnCardState *cs) restore_flags(flags); Write_hfc(cs, HFCSX_CIRM, HFCSX_RESET); /* Reset On */ sti(); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((30 * HZ) / 1000); /* Timeout 30ms */ Write_hfc(cs, HFCSX_CIRM, 0); /* Reset Off */ del_timer(&cs->hw.hfcsx.timer); @@ -408,10 +392,10 @@ reset_hfcsx(struct IsdnCardState *cs) while (1) { Write_hfc(cs, HFCSX_CIRM, HFCSX_RESET | cs->hw.hfcsx.cirm ); /* Reset */ sti(); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((30 * HZ) / 1000); /* Timeout 30ms */ Write_hfc(cs, HFCSX_CIRM, cs->hw.hfcsx.cirm); /* Reset Off */ - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((20 * HZ) / 1000); /* Timeout 20ms */ if (Read_hfc(cs, HFCSX_STATUS) & 2) printk(KERN_WARNING "HFC-SX init bit busy\n"); @@ -1471,7 +1455,7 @@ hfcsx_card_msg(struct IsdnCardState *cs, int mt, void *arg) inithfcsx(cs); save_flags(flags); sti(); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((80 * HZ) / 1000); /* Timeout 80ms */ /* now switch timer interrupt off */ cs->hw.hfcsx.int_m1 &= ~HFCSX_INTS_TIMER; @@ -1502,7 +1486,8 @@ __initfunc(int cs->hw.hfcsx.int_s1 = 0; cs->dc.hfcsx.ph_state = 0; cs->hw.hfcsx.fifo = 255; - if (cs->typ == ISDN_CTYPE_HFC_SX) { + if ((cs->typ == ISDN_CTYPE_HFC_SX) || + (cs->typ == ISDN_CTYPE_HFC_SP_PCMCIA)) { if ((!cs->hw.hfcsx.base) || check_region((cs->hw.hfcsx.base), 2)) { printk(KERN_WARNING diff --git a/drivers/isdn/hisax/hfc_sx.h b/drivers/isdn/hisax/hfc_sx.h index ebb8d3e7d45e..41c8056bcbfd 100644 --- a/drivers/isdn/hisax/hfc_sx.h +++ b/drivers/isdn/hisax/hfc_sx.h @@ -1,5 +1,5 @@ -/* $Id: hfc_sx.h,v 1.1 1999/11/18 00:09:18 werner Exp $ - +/* $Id: hfc_sx.h,v 1.2 2000/06/26 08:59:13 keil Exp $ + * * specific defines for CCD's HFC 2BDS0 S+,SP chips * * Author Werner Cornelius (werner@isdn4linux.de) @@ -20,14 +20,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * $Log: hfc_sx.h,v $ - * Revision 1.1 1999/11/18 00:09:18 werner - * - * Initial release of files for HFC-S+ and HFC-SP cards with 32K-RAM. - * Audio and Echo are supported. - * - * - * */ /*********************************************/ diff --git a/drivers/isdn/hisax/hfcscard.c b/drivers/isdn/hisax/hfcscard.c index b2220913a28a..18993bf0d89e 100644 --- a/drivers/isdn/hisax/hfcscard.c +++ b/drivers/isdn/hisax/hfcscard.c @@ -1,29 +1,10 @@ -/* $Id: hfcscard.c,v 1.6 1999/12/19 13:09:42 keil Exp $ - +/* $Id: hfcscard.c,v 1.7 2000/06/26 08:59:13 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.6 1999/12/19 13:09:42 keil - * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for - * signal proof delays - * - * Revision 1.5 1999/09/04 06:20:06 keil - * Changes from kernel set_current_state() - * - * 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 - * - * + * This file is (c) under GNU PUBLIC LICENSE * */ @@ -34,7 +15,7 @@ extern const char *CardType[]; -static const char *hfcs_revision = "$Revision: 1.6 $"; +static const char *hfcs_revision = "$Revision: 1.7 $"; static void hfcs_interrupt(int intno, void *dev_id, struct pt_regs *regs) @@ -89,13 +70,13 @@ reset_hfcs(struct IsdnCardState *cs) cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm); /* Reset On */ save_flags(flags); sti(); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); 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_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); if (cs->typ == ISDN_CTYPE_TELES3C) cs->hw.hfcD.cirm |= HFCD_INTB; @@ -142,7 +123,7 @@ hfcs_card_msg(struct IsdnCardState *cs, int mt, void *arg) init2bds0(cs); save_flags(flags); sti(); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((80*HZ)/1000); cs->hw.hfcD.ctmt |= HFCD_TIM800; cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt); diff --git a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h index 8175c5d61434..81aed1e9e5f4 100644 --- a/drivers/isdn/hisax/hisax.h +++ b/drivers/isdn/hisax/hisax.h @@ -1,143 +1,8 @@ -/* $Id: hisax.h,v 2.41 2000/02/26 00:35:13 keil Exp $ - - * Basic declarations, defines and prototypes - * - * $Log: hisax.h,v $ - * Revision 2.41 2000/02/26 00:35:13 keil - * Fix skb freeing in interrupt context - * - * Revision 2.40 2000/01/20 19:51:46 keil - * Fix AddTimer message - * Change CONFIG defines - * - * Revision 2.39 1999/11/18 00:00:43 werner - * - * Added support for HFC-S+ and HFC-SP cards - * - * Revision 2.38 1999/11/14 23:37:03 keil - * new ISA memory mapped IO - * - * Revision 2.37 1999/10/14 20:25:28 keil - * add a statistic for error monitoring - * - * Revision 2.36 1999/10/10 20:16:15 werner - * - * Added variable to hfcpci union. - * - * Revision 2.35 1999/09/04 06:35:09 keil - * Winbond W6692 support - * - * Revision 2.34 1999/08/25 17:00:04 keil - * Make ISAR V32bis modem running - * Make LL->HL interface open for additional commands - * - * 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 - * - * Revision 2.25 1998/09/30 22:28:42 keil - * More work for ISAR support - * - * Revision 2.24 1998/08/20 13:50:39 keil - * More support for hybrid modem (not working yet) - * - * Revision 2.23 1998/08/13 23:36:31 keil - * HiSax 3.1 - don't work stable with current LinkLevel - * - * Revision 2.22 1998/07/15 15:01:28 calle - * Support for AVM passive PCMCIA cards: - * A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0 - * - * Revision 2.21 1998/05/25 14:10:05 keil - * HiSax 3.0 - * X.75 and leased are working again. - * - * Revision 2.20 1998/05/25 12:57:57 keil - * HiSax golden code from certification, Don't use !!! - * No leased lines, no X75, but many changes. - * - * Revision 2.19 1998/04/15 16:39:15 keil - * Add S0Box and Teles PCI support - * - * Revision 2.18 1998/03/26 07:10:04 paul - * The jumpmatrix table in struct Fsm was an array of "int". This is not - * large enough for pointers to functions on Linux/Alpha (instant crash - * on "insmod hisax). Now there is a typedef for the pointer to function. - * This also prevents warnings about "incompatible pointer types". - * - * Revision 2.17 1998/03/19 13:18:43 keil - * Start of a CAPI like interface for supplementary Service - * first service: SUSPEND - * - * Revision 2.16 1998/03/09 23:19:25 keil - * Changes for PCMCIA - * - * Revision 2.14 1998/02/11 17:28:04 keil - * Niccy PnP/PCI support - * - * Revision 2.13 1998/02/09 18:46:02 keil - * Support for Sedlbauer PCMCIA (Marcus Niemann) - * - * Revision 2.12 1998/02/03 23:31:30 keil - * add AMD7930 support - * - * Revision 2.11 1998/02/02 13:33:00 keil - * New card support - * - * Revision 2.10 1997/11/08 21:37:52 keil - * new l1 init;new Compaq card - * - * Revision 2.9 1997/11/06 17:09:09 keil - * New 2.1 init code - * - * Revision 2.8 1997/10/29 19:04:13 keil - * new L1; changes for 2.1 - * - * Revision 2.7 1997/10/10 20:56:47 fritz - * New HL interface. - * - * Revision 2.6 1997/09/11 17:25:51 keil - * Add new cards +/* $Id: hisax.h,v 2.49 2000/09/08 22:55:59 werner Exp $ * - * Revision 2.5 1997/08/03 14:36:31 keil - * Implement RESTART procedure - * - * Revision 2.4 1997/07/31 19:25:20 keil - * PTP_DATA_LINK support - * - * Revision 2.3 1997/07/31 11:50:17 keil - * ONE TEI and FIXED TEI handling - * - * Revision 2.2 1997/07/30 17:13:02 keil - * more changes for 'One TEI per card' - * - * Revision 2.1 1997/07/27 21:45:13 keil - * new main structures - * - * Revision 2.0 1997/06/26 11:06:27 keil - * New card and L1 interface. - * Eicon.Diehl Diva and Dynalink IS64PH support + * Basic declarations, defines and prototypes * - * old changes removed KKe + * This file is (c) under GNU PUBLIC LICENSE * */ #include @@ -173,8 +38,11 @@ #define HW_POWERUP 0x0008 #define HW_ACTIVATE 0x0010 #define HW_DEACTIVATE 0x0018 + +#define HW_INFO1 0x0010 #define HW_INFO2 0x0020 #define HW_INFO3 0x0030 +#define HW_INFO4 0x0040 #define HW_INFO4_P8 0x0040 #define HW_INFO4_P10 0x0048 #define HW_RSYNC 0x0060 @@ -224,6 +92,7 @@ #define CC_SUSPEND 0x0370 #define CC_PROCEED_SEND 0x0374 #define CC_REDIR 0x0378 +#define CC_T302 0x0382 #define CC_T303 0x0383 #define CC_T304 0x0384 #define CC_T305 0x0385 @@ -234,6 +103,7 @@ #define CC_T313 0x0393 #define CC_T318 0x0398 #define CC_T319 0x0399 +#define CC_TSPID 0x03A0 #define CC_NOSETUP_RSP 0x03E0 #define CC_SETUP_ERR 0x03E1 #define CC_SUSPEND_ERR 0x03E2 @@ -242,6 +112,7 @@ #define CC_RELEASE_ERR 0x03E5 #define CC_RESTART 0x03F4 #define CC_TDSS1_IO 0x13F4 /* DSS1 IO user timer */ +#define CC_TNI1_IO 0x13F5 /* NI1 IO user timer */ /* define maximum number of possible waiting incoming calls */ #define MAX_WAITING_CALLS 2 @@ -249,13 +120,19 @@ #ifdef __KERNEL__ -/* include only l3dss1 specific process structures, but no other defines */ +/* include l3dss1 & ni1 specific process structures, but no other defines */ #ifdef CONFIG_HISAX_EURO #define l3dss1_process #include "l3dss1.h" #undef l3dss1_process #endif CONFIG_HISAX_EURO +#ifdef CONFIG_HISAX_NI1 + #define l3ni1_process + #include "l3ni1.h" + #undef l3ni1_process +#endif CONFIG_HISAX_NI1 + #define MAX_DFRAME_LEN 260 #define MAX_DFRAME_LEN_L1 300 #define HSCX_BUFMAX 4096 @@ -318,6 +195,7 @@ struct L3Timer { #define FLG_L1_ACTTIMER 4 #define FLG_L1_T3RUN 5 #define FLG_L1_PULL_REQ 6 +#define FLG_L1_UINT 7 struct Layer1 { void *hardware; @@ -432,7 +310,7 @@ struct PStack { struct Layer3 l3; struct LLInterface lli; struct Management ma; - int protocol; /* EDSS1 or 1TR6 */ + int protocol; /* EDSS1, 1TR6 or NI1 */ /* protocol specific data fields */ union @@ -440,6 +318,9 @@ struct PStack { #ifdef CONFIG_HISAX_EURO dss1_stk_priv dss1; /* private dss1 data */ #endif CONFIG_HISAX_EURO +#ifdef CONFIG_HISAX_NI1 + ni1_stk_priv ni1; /* private ni1 data */ +#endif CONFIG_HISAX_NI1 } prot; }; @@ -461,6 +342,9 @@ struct l3_process { #ifdef CONFIG_HISAX_EURO dss1_proc_priv dss1; /* private dss1 data */ #endif CONFIG_HISAX_EURO +#ifdef CONFIG_HISAX_NI1 + ni1_proc_priv ni1; /* private ni1 data */ +#endif CONFIG_HISAX_NI1 } prot; }; @@ -499,6 +383,7 @@ struct isar_hw { u_char mod; u_char newcmd; u_char newmod; + char try_mod; struct timer_list ftimer; u_char *rcvbuf; /* B-Channel receive Buffer */ u_char conmsg[16]; @@ -506,10 +391,17 @@ struct isar_hw { }; struct hdlc_stat_reg { +#ifdef __BIG_ENDIAN + u_char fill __attribute__((packed)); + u_char mode __attribute__((packed)); + u_char xml __attribute__((packed)); + u_char cmd __attribute__((packed)); +#else u_char cmd __attribute__((packed)); u_char xml __attribute__((packed)); u_char mode __attribute__((packed)); u_char fill __attribute__((packed)); +#endif }; struct hdlc_hw { @@ -938,6 +830,22 @@ struct w6692_chip { int ph_state; }; +struct icc_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; + struct wait_queue *arcofi_wait; + u_char arcofi_bc; + u_char arcofi_state; + u_char mocr; + u_char adf2; +}; + #define HW_IOM1 0 #define HW_IPAC 1 #define HW_ISAR 2 @@ -948,6 +856,7 @@ struct w6692_chip { #define FLG_LOCK_ATOMIC 7 #define FLG_ARCOFI_TIMER 8 #define FLG_ARCOFI_ERROR 9 +#define FLG_HW_L1_UINT 10 struct IsdnCardState { unsigned char typ; @@ -1016,6 +925,7 @@ struct IsdnCardState { struct hfcpci_chip hfcpci; struct hfcsx_chip hfcsx; struct w6692_chip w6692; + struct icc_chip icc; } dc; u_char *rcvbuf; int rcvidx; @@ -1057,7 +967,7 @@ struct IsdnCardState { #define ISDN_CTYPE_MIC 17 #define ISDN_CTYPE_ELSA_PCI 18 #define ISDN_CTYPE_COMPAQ_ISA 19 -#define ISDN_CTYPE_NETJET 20 +#define ISDN_CTYPE_NETJET_S 20 #define ISDN_CTYPE_TELESPCI 21 #define ISDN_CTYPE_SEDLBAUER_PCMCIA 22 #define ISDN_CTYPE_AMD7930 23 @@ -1075,7 +985,9 @@ struct IsdnCardState { #define ISDN_CTYPE_HFC_PCI 35 #define ISDN_CTYPE_W6692 36 #define ISDN_CTYPE_HFC_SX 37 -#define ISDN_CTYPE_COUNT 37 +#define ISDN_CTYPE_NETJET_U 38 +#define ISDN_CTYPE_HFC_SP_PCMCIA 39 +#define ISDN_CTYPE_COUNT 39 #ifdef ISDN_CHIP_ISAC @@ -1224,12 +1136,12 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_NETJET -#define CARD_NETJET 1 +#define CARD_NETJET_S 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif #else -#define CARD_NETJET 0 +#define CARD_NETJET_S 0 #endif #ifdef CONFIG_HISAX_HFCS @@ -1337,17 +1249,19 @@ struct IsdnCardState { #define CARD_W6692 0 #endif -#define TEI_PER_CARD 0 - -#ifdef CONFIG_HISAX_1TR6 -#undef TEI_PER_CARD -#define TEI_PER_CARD 1 +#ifdef CONFIG_HISAX_NETJET_U +#define CARD_NETJET_U 1 +#ifndef ISDN_CHIP_ICC +#define ISDN_CHIP_ICC 1 +#endif +#ifndef HISAX_UINTERFACE +#define HISAX_UINTERFACE 1 +#endif +#else +#define CARD_NETJET_U 0 #endif -#ifdef CONFIG_HISAX_EURO -#undef TEI_PER_CARD #define TEI_PER_CARD 1 -#endif /* L1 Debug */ #define L1_DEB_WARN 0x01 @@ -1371,7 +1285,7 @@ extern void Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, struct IsdnCard { int typ; - int protocol; /* EDSS1 or 1TR6 */ + int protocol; /* EDSS1, 1TR6 or NI1 */ unsigned int para[4]; struct IsdnCardState *cs; }; @@ -1448,3 +1362,17 @@ char *HiSax_getrev(const char *revision); void TeiNew(void); void TeiFree(void); int certification_check(int output); +#ifdef __powerpc__ +#include +static inline int pci_enable_device(struct pci_dev *dev) +{ + u16 cmd; + pci_read_config_word(dev, PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_IO | PCI_COMMAND_SERR; + cmd &= ~PCI_COMMAND_FAST_BACK; + pci_write_config_word(dev, PCI_COMMAND, cmd); + return(0); +} +#else +#define pci_enable_device(dev) !dev +#endif /* __powerpc__ */ diff --git a/drivers/isdn/hisax/hscx.c b/drivers/isdn/hisax/hscx.c index 1c57cd3f1c43..8eb88a64bc21 100644 --- a/drivers/isdn/hisax/hscx.c +++ b/drivers/isdn/hisax/hscx.c @@ -1,61 +1,10 @@ -/* $Id: hscx.c,v 1.17 1999/07/01 08:11:41 keil Exp $ - +/* $Id: hscx.c,v 1.19 2000/06/26 08:59:13 keil Exp $ + * * hscx.c HSCX specific routines * * Author Karsten Keil (keil@isdn4linux.de) * - * - * $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 - * - * Revision 1.15 1998/08/20 13:50:42 keil - * More support for hybrid modem (not working yet) - * - * Revision 1.14 1998/08/13 23:36:33 keil - * HiSax 3.1 - don't work stable with current LinkLevel - * - * Revision 1.13 1998/06/26 22:03:28 keil - * send flags between hdlc frames - * - * Revision 1.12 1998/06/09 18:26:01 keil - * PH_DEACTIVATE B-channel every time signaled to higher layer - * - * Revision 1.11 1998/05/25 14:10:07 keil - * HiSax 3.0 - * X.75 and leased are working again. - * - * Revision 1.10 1998/05/25 12:57:59 keil - * HiSax golden code from certification, Don't use !!! - * No leased lines, no X75, but many changes. - * - * Revision 1.9 1998/04/15 16:45:33 keil - * new init code - * - * Revision 1.8 1998/03/19 13:16:24 keil - * fix the correct release of the hscx - * - * Revision 1.7 1998/02/12 23:07:36 keil - * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() - * - * Revision 1.6 1998/02/02 13:41:12 keil - * new init - * - * Revision 1.5 1997/11/06 17:09:34 keil - * New 2.1 init code - * - * Revision 1.4 1997/10/29 19:01:06 keil - * changes for 2.1 - * - * Revision 1.3 1997/07/27 21:38:34 keil - * new B-channel interface - * - * Revision 1.2 1997/06/26 11:16:17 keil - * first version - * + * This file is (c) under GNU PUBLIC LICENSE * */ diff --git a/drivers/isdn/hisax/hscx.h b/drivers/isdn/hisax/hscx.h index 00b566a44193..32fc2708d1a9 100644 --- a/drivers/isdn/hisax/hscx.h +++ b/drivers/isdn/hisax/hscx.h @@ -1,23 +1,10 @@ -/* $Id: hscx.h,v 1.5 1999/12/23 15:09:32 keil Exp $ - +/* $Id: hscx.h,v 1.6 2000/06/26 08:59:13 keil Exp $ + * * hscx.h HSCX specific defines * * Author Karsten Keil (keil@isdn4linux.de) * - * - * $Log: hscx.h,v $ - * Revision 1.5 1999/12/23 15:09:32 keil - * change email - * - * Revision 1.4 1998/04/15 16:45:34 keil - * new init code - * - * Revision 1.3 1997/07/27 21:38:35 keil - * new B-channel interface - * - * Revision 1.2 1997/06/26 11:16:18 keil - * first version - * + * This file is (c) under GNU PUBLIC LICENSE * */ diff --git a/drivers/isdn/hisax/hscx_irq.c b/drivers/isdn/hisax/hscx_irq.c index 655508f704e9..49cdfd7be5eb 100644 --- a/drivers/isdn/hisax/hscx_irq.c +++ b/drivers/isdn/hisax/hscx_irq.c @@ -1,50 +1,12 @@ -/* $Id: hscx_irq.c,v 1.13 1999/10/14 20:25:28 keil Exp $ - +/* $Id: hscx_irq.c,v 1.15 2000/06/26 08:59:13 keil Exp $ + * * hscx_irq.c low level b-channel stuff for Siemens HSCX * * Author Karsten Keil (keil@isdn4linux.de) * * This is an include file for fast inline IRQ stuff * - * $Log: hscx_irq.c,v $ - * Revision 1.13 1999/10/14 20:25:28 keil - * add a statistic for error monitoring - * - * 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 - * - * Revision 1.10 1998/08/13 23:36:35 keil - * HiSax 3.1 - don't work stable with current LinkLevel - * - * Revision 1.9 1998/06/24 14:44:51 keil - * Fix recovery of TX IRQ loss - * - * Revision 1.8 1998/04/10 10:35:22 paul - * fixed (silly?) warnings from egcs on Alpha. - * - * Revision 1.7 1998/02/12 23:07:37 keil - * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() - * - * Revision 1.6 1997/10/29 19:01:07 keil - * changes for 2.1 - * - * Revision 1.5 1997/10/01 09:21:35 fritz - * Removed old compatibility stuff for 2.0.X kernels. - * From now on, this code is for 2.1.X ONLY! - * Old stuff is still in the separate branch. - * - * Revision 1.4 1997/08/15 17:48:02 keil - * cosmetic - * - * Revision 1.3 1997/07/27 21:38:36 keil - * new B-channel interface - * - * Revision 1.2 1997/06/26 11:16:19 keil - * first version - * + * This file is (c) under GNU PUBLIC LICENSE * */ diff --git a/drivers/isdn/hisax/icc.c b/drivers/isdn/hisax/icc.c new file mode 100644 index 000000000000..b0d28cc7fbd0 --- /dev/null +++ b/drivers/isdn/hisax/icc.c @@ -0,0 +1,685 @@ +// $Id: icc.c,v 1.3 2000/08/20 07:34:04 keil Exp $ +//----------------------------------------------------------------------------- +// +// ICC specific routines +// +// Author Matt Henderson & Guy Ellis - Traverse Tecnologies Pty Ltd +// www.traverse.com.au +// +// 1999.6.25 Initial implementation of routines for Siemens ISDN +// Communication Controler PEB 2070 based on the ISAC routines +// written by Karsten Keil. +// +// This file is (c) under GNU PUBLIC LICENSE +// +//----------------------------------------------------------------------------- + +#define __NO_VERSION__ +#include "hisax.h" +#include "icc.h" +// #include "arcofi.h" +#include "isdnl1.h" +#include + +#define DBUSY_TIMER_VALUE 80 +#define ARCOFI_USE 0 + +static char *ICCVer[] HISAX_INITDATA = +{"2070 A1/A3", "2070 B1", "2070 B2/B3", "2070 V2.4"}; + +void +ICCVersion(struct IsdnCardState *cs, char *s) +{ + int val; + + val = cs->readisac(cs, ICC_RBCH); + printk(KERN_INFO "%s ICC version (%x): %s\n", s, val, ICCVer[(val >> 5) & 3]); +} + +static void +ph_command(struct IsdnCardState *cs, unsigned int command) +{ + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ph_command %x", command); + cs->writeisac(cs, ICC_CIX0, (command << 2) | 3); +} + + +static void +icc_new_ph(struct IsdnCardState *cs) +{ + switch (cs->dc.icc.ph_state) { + case (ICC_IND_EI1): + ph_command(cs, ICC_CMD_DI); + l1_msg(cs, HW_RESET | INDICATION, NULL); + break; + case (ICC_IND_DC): + l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL); + break; + case (ICC_IND_DR): + l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL); + break; + case (ICC_IND_PU): + l1_msg(cs, HW_POWERUP | CONFIRM, NULL); + break; + case (ICC_IND_FJ): + l1_msg(cs, HW_RSYNC | INDICATION, NULL); + break; + case (ICC_IND_AR): + l1_msg(cs, HW_INFO2 | INDICATION, NULL); + break; + case (ICC_IND_AI): + l1_msg(cs, HW_INFO4 | INDICATION, NULL); + break; + default: + break; + } +} + +static void +icc_bh(struct IsdnCardState *cs) +{ + struct PStack *stptr; + + if (!cs) + return; + if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) { + if (cs->debug) + debugl1(cs, "D-Channel Busy cleared"); + stptr = cs->stlist; + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL); + stptr = stptr->next; + } + } + if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) + icc_new_ph(cs); + if (test_and_clear_bit(D_RCVBUFREADY, &cs->event)) + DChannel_proc_rcv(cs); + if (test_and_clear_bit(D_XMTBUFREADY, &cs->event)) + DChannel_proc_xmt(cs); +#if ARCOFI_USE + if (!test_bit(HW_ARCOFI, &cs->HW_Flags)) + return; + if (test_and_clear_bit(D_RX_MON1, &cs->event)) + arcofi_fsm(cs, ARCOFI_RX_END, NULL); + if (test_and_clear_bit(D_TX_MON1, &cs->event)) + arcofi_fsm(cs, ARCOFI_TX_END, NULL); +#endif +} + +void +icc_empty_fifo(struct IsdnCardState *cs, int count) +{ + u_char *ptr; + long flags; + + if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO)) + debugl1(cs, "icc_empty_fifo"); + + if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "icc_empty_fifo overrun %d", + cs->rcvidx + count); + cs->writeisac(cs, ICC_CMDR, 0x80); + cs->rcvidx = 0; + return; + } + ptr = cs->rcvbuf + cs->rcvidx; + cs->rcvidx += count; + save_flags(flags); + cli(); + cs->readisacfifo(cs, ptr, count); + cs->writeisac(cs, ICC_CMDR, 0x80); + restore_flags(flags); + if (cs->debug & L1_DEB_ISAC_FIFO) { + char *t = cs->dlog; + + t += sprintf(t, "icc_empty_fifo cnt %d", count); + QuickHex(t, ptr, count); + debugl1(cs, cs->dlog); + } +} + +static void +icc_fill_fifo(struct IsdnCardState *cs) +{ + int count, more; + u_char *ptr; + long flags; + + if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO)) + debugl1(cs, "icc_fill_fifo"); + + if (!cs->tx_skb) + return; + + count = cs->tx_skb->len; + if (count <= 0) + return; + + more = 0; + if (count > 32) { + more = !0; + count = 32; + } + save_flags(flags); + cli(); + ptr = cs->tx_skb->data; + skb_pull(cs->tx_skb, count); + cs->tx_cnt += count; + cs->writeisacfifo(cs, ptr, count); + cs->writeisac(cs, ICC_CMDR, more ? 0x8 : 0xa); + if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) { + debugl1(cs, "icc_fill_fifo dbusytimer running"); + del_timer(&cs->dbusytimer); + } + init_timer(&cs->dbusytimer); + cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000); + add_timer(&cs->dbusytimer); + restore_flags(flags); + if (cs->debug & L1_DEB_ISAC_FIFO) { + char *t = cs->dlog; + + t += sprintf(t, "icc_fill_fifo cnt %d", count); + QuickHex(t, ptr, count); + debugl1(cs, cs->dlog); + } +} + +void +icc_sched_event(struct IsdnCardState *cs, int event) +{ + test_and_set_bit(event, &cs->event); + queue_task(&cs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +void +icc_interrupt(struct IsdnCardState *cs, u_char val) +{ + u_char exval, v1; + struct sk_buff *skb; + unsigned int count; + long flags; + + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ICC interrupt %x", val); + if (val & 0x80) { /* RME */ + exval = cs->readisac(cs, ICC_RSTA); + if ((exval & 0x70) != 0x20) { + if (exval & 0x40) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ICC RDO"); +#ifdef ERROR_STATISTIC + cs->err_rx++; +#endif + } + if (!(exval & 0x20)) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ICC CRC error"); +#ifdef ERROR_STATISTIC + cs->err_crc++; +#endif + } + cs->writeisac(cs, ICC_CMDR, 0x80); + } else { + count = cs->readisac(cs, ICC_RBCL) & 0x1f; + if (count == 0) + count = 32; + icc_empty_fifo(cs, count); + save_flags(flags); + cli(); + if ((count = cs->rcvidx) > 0) { + cs->rcvidx = 0; + if (!(skb = alloc_skb(count, GFP_ATOMIC))) + printk(KERN_WARNING "HiSax: D receive out of memory\n"); + else { + memcpy(skb_put(skb, count), cs->rcvbuf, count); + skb_queue_tail(&cs->rq, skb); + } + } + restore_flags(flags); + } + cs->rcvidx = 0; + icc_sched_event(cs, D_RCVBUFREADY); + } + if (val & 0x40) { /* RPF */ + icc_empty_fifo(cs, 32); + } + if (val & 0x20) { /* RSC */ + /* never */ + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ICC RSC interrupt"); + } + if (val & 0x10) { /* XPR */ + if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) + del_timer(&cs->dbusytimer); + if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) + icc_sched_event(cs, D_CLEARBUSY); + if (cs->tx_skb) { + if (cs->tx_skb->len) { + icc_fill_fifo(cs); + goto afterXPR; + } else { + dev_kfree_skb(cs->tx_skb); + cs->tx_cnt = 0; + cs->tx_skb = NULL; + } + } + if ((cs->tx_skb = skb_dequeue(&cs->sq))) { + cs->tx_cnt = 0; + icc_fill_fifo(cs); + } else + icc_sched_event(cs, D_XMTBUFREADY); + } + afterXPR: + if (val & 0x04) { /* CISQ */ + exval = cs->readisac(cs, ICC_CIR0); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ICC CIR0 %02X", exval ); + if (exval & 2) { + cs->dc.icc.ph_state = (exval >> 2) & 0xf; + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ph_state change %x", cs->dc.icc.ph_state); + icc_sched_event(cs, D_L1STATECHANGE); + } + if (exval & 1) { + exval = cs->readisac(cs, ICC_CIR1); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ICC CIR1 %02X", exval ); + } + } + if (val & 0x02) { /* SIN */ + /* never */ + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ICC SIN interrupt"); + } + if (val & 0x01) { /* EXI */ + exval = cs->readisac(cs, ICC_EXIR); + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ICC EXIR %02x", exval); + if (exval & 0x80) { /* XMR */ + debugl1(cs, "ICC XMR"); + printk(KERN_WARNING "HiSax: ICC XMR\n"); + } + if (exval & 0x40) { /* XDU */ + debugl1(cs, "ICC XDU"); + printk(KERN_WARNING "HiSax: ICC XDU\n"); +#ifdef ERROR_STATISTIC + cs->err_tx++; +#endif + 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)) + icc_sched_event(cs, D_CLEARBUSY); + if (cs->tx_skb) { /* Restart frame */ + skb_push(cs->tx_skb, cs->tx_cnt); + cs->tx_cnt = 0; + icc_fill_fifo(cs); + } else { + printk(KERN_WARNING "HiSax: ICC XDU no skb\n"); + debugl1(cs, "ICC XDU no skb"); + } + } + if (exval & 0x04) { /* MOS */ + v1 = cs->readisac(cs, ICC_MOSR); + if (cs->debug & L1_DEB_MONITOR) + debugl1(cs, "ICC MOSR %02x", v1); +#if ARCOFI_USE + if (v1 & 0x08) { + if (!cs->dc.icc.mon_rx) { + if (!(cs->dc.icc.mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ICC MON RX out of memory!"); + cs->dc.icc.mocr &= 0xf0; + cs->dc.icc.mocr |= 0x0a; + cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr); + goto afterMONR0; + } else + cs->dc.icc.mon_rxp = 0; + } + if (cs->dc.icc.mon_rxp >= MAX_MON_FRAME) { + cs->dc.icc.mocr &= 0xf0; + cs->dc.icc.mocr |= 0x0a; + cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr); + cs->dc.icc.mon_rxp = 0; + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ICC MON RX overflow!"); + goto afterMONR0; + } + cs->dc.icc.mon_rx[cs->dc.icc.mon_rxp++] = cs->readisac(cs, ICC_MOR0); + if (cs->debug & L1_DEB_MONITOR) + debugl1(cs, "ICC MOR0 %02x", cs->dc.icc.mon_rx[cs->dc.icc.mon_rxp -1]); + if (cs->dc.icc.mon_rxp == 1) { + cs->dc.icc.mocr |= 0x04; + cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr); + } + } + afterMONR0: + if (v1 & 0x80) { + if (!cs->dc.icc.mon_rx) { + if (!(cs->dc.icc.mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ICC MON RX out of memory!"); + cs->dc.icc.mocr &= 0x0f; + cs->dc.icc.mocr |= 0xa0; + cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr); + goto afterMONR1; + } else + cs->dc.icc.mon_rxp = 0; + } + if (cs->dc.icc.mon_rxp >= MAX_MON_FRAME) { + cs->dc.icc.mocr &= 0x0f; + cs->dc.icc.mocr |= 0xa0; + cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr); + cs->dc.icc.mon_rxp = 0; + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ICC MON RX overflow!"); + goto afterMONR1; + } + cs->dc.icc.mon_rx[cs->dc.icc.mon_rxp++] = cs->readisac(cs, ICC_MOR1); + if (cs->debug & L1_DEB_MONITOR) + debugl1(cs, "ICC MOR1 %02x", cs->dc.icc.mon_rx[cs->dc.icc.mon_rxp -1]); + cs->dc.icc.mocr |= 0x40; + cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr); + } + afterMONR1: + if (v1 & 0x04) { + cs->dc.icc.mocr &= 0xf0; + cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr); + cs->dc.icc.mocr |= 0x0a; + cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr); + icc_sched_event(cs, D_RX_MON0); + } + if (v1 & 0x40) { + cs->dc.icc.mocr &= 0x0f; + cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr); + cs->dc.icc.mocr |= 0xa0; + cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr); + icc_sched_event(cs, D_RX_MON1); + } + if (v1 & 0x02) { + if ((!cs->dc.icc.mon_tx) || (cs->dc.icc.mon_txc && + (cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc) && + !(v1 & 0x08))) { + cs->dc.icc.mocr &= 0xf0; + cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr); + cs->dc.icc.mocr |= 0x0a; + cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr); + if (cs->dc.icc.mon_txc && + (cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc)) + icc_sched_event(cs, D_TX_MON0); + goto AfterMOX0; + } + if (cs->dc.icc.mon_txc && (cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc)) { + icc_sched_event(cs, D_TX_MON0); + goto AfterMOX0; + } + cs->writeisac(cs, ICC_MOX0, + cs->dc.icc.mon_tx[cs->dc.icc.mon_txp++]); + if (cs->debug & L1_DEB_MONITOR) + debugl1(cs, "ICC %02x -> MOX0", cs->dc.icc.mon_tx[cs->dc.icc.mon_txp -1]); + } + AfterMOX0: + if (v1 & 0x20) { + if ((!cs->dc.icc.mon_tx) || (cs->dc.icc.mon_txc && + (cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc) && + !(v1 & 0x80))) { + cs->dc.icc.mocr &= 0x0f; + cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr); + cs->dc.icc.mocr |= 0xa0; + cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr); + if (cs->dc.icc.mon_txc && + (cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc)) + icc_sched_event(cs, D_TX_MON1); + goto AfterMOX1; + } + if (cs->dc.icc.mon_txc && (cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc)) { + icc_sched_event(cs, D_TX_MON1); + goto AfterMOX1; + } + cs->writeisac(cs, ICC_MOX1, + cs->dc.icc.mon_tx[cs->dc.icc.mon_txp++]); + if (cs->debug & L1_DEB_MONITOR) + debugl1(cs, "ICC %02x -> MOX1", cs->dc.icc.mon_tx[cs->dc.icc.mon_txp -1]); + } + AfterMOX1: +#endif + } + } +} + +static void +ICC_l1hw(struct PStack *st, int pr, void *arg) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; + struct sk_buff *skb = arg; + int val; + + 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 + icc_fill_fifo(cs); + } + break; + case (PH_PULL |INDICATION): + if (cs->tx_skb) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, " l2l1 tx_skb exist this shouldn't happen"); + skb_queue_tail(&cs->sq, skb); + break; + } + if (cs->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 + icc_fill_fifo(cs); + break; + case (PH_PULL | REQUEST): +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + debugl1(cs, "-> PH_REQUEST_PULL"); +#endif + if (!cs->tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + case (HW_RESET | REQUEST): + if ((cs->dc.icc.ph_state == ICC_IND_EI1) || + (cs->dc.icc.ph_state == ICC_IND_DR)) + ph_command(cs, ICC_CMD_DI); + else + ph_command(cs, ICC_CMD_RES); + break; + case (HW_ENABLE | REQUEST): + ph_command(cs, ICC_CMD_DI); + break; + case (HW_INFO1 | REQUEST): + ph_command(cs, ICC_CMD_AR); + break; + case (HW_INFO3 | REQUEST): + ph_command(cs, ICC_CMD_AI); + break; + case (HW_TESTLOOP | REQUEST): + val = 0; + if (1 & (long) arg) + val |= 0x0c; + if (2 & (long) arg) + val |= 0x3; + if (test_bit(HW_IOM1, &cs->HW_Flags)) { + /* IOM 1 Mode */ + if (!val) { + cs->writeisac(cs, ICC_SPCR, 0xa); + cs->writeisac(cs, ICC_ADF1, 0x2); + } else { + cs->writeisac(cs, ICC_SPCR, val); + cs->writeisac(cs, ICC_ADF1, 0xa); + } + } else { + /* IOM 2 Mode */ + cs->writeisac(cs, ICC_SPCR, val); + if (val) + cs->writeisac(cs, ICC_ADF1, 0x8); + else + cs->writeisac(cs, ICC_ADF1, 0x0); + } + break; + case (HW_DEACTIVATE | RESPONSE): + discard_queue(&cs->rq); + discard_queue(&cs->sq); + if (cs->tx_skb) { + dev_kfree_skb(cs->tx_skb); + cs->tx_skb = NULL; + } + if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) + del_timer(&cs->dbusytimer); + if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) + icc_sched_event(cs, D_CLEARBUSY); + break; + default: + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "icc_l1hw unknown %04x", pr); + break; + } +} + +void +setstack_icc(struct PStack *st, struct IsdnCardState *cs) +{ + st->l1.l1hw = ICC_l1hw; +} + +void +DC_Close_icc(struct IsdnCardState *cs) { + if (cs->dc.icc.mon_rx) { + kfree(cs->dc.icc.mon_rx); + cs->dc.icc.mon_rx = NULL; + } + if (cs->dc.icc.mon_tx) { + kfree(cs->dc.icc.mon_tx); + cs->dc.icc.mon_tx = NULL; + } +} + +static void +dbusy_timer_handler(struct IsdnCardState *cs) +{ + struct PStack *stptr; + int rbch, star; + + if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) { + rbch = cs->readisac(cs, ICC_RBCH); + star = cs->readisac(cs, ICC_STAR); + if (cs->debug) + debugl1(cs, "D-Channel Busy RBCH %02x STAR %02x", + rbch, star); + if (rbch & ICC_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) { + dev_kfree_skb(cs->tx_skb); + cs->tx_cnt = 0; + cs->tx_skb = NULL; + } else { + printk(KERN_WARNING "HiSax: ICC D-Channel Busy no skb\n"); + debugl1(cs, "D-Channel Busy no skb"); + } + cs->writeisac(cs, ICC_CMDR, 0x01); /* Transmitter reset */ + cs->irq_func(cs->irq, cs, NULL); + } + } +} + +HISAX_INITFUNC(void +initicc(struct IsdnCardState *cs)) +{ + cs->tqueue.routine = (void *) (void *) icc_bh; + cs->setstack_d = setstack_icc; + cs->DC_Close = DC_Close_icc; + cs->dc.icc.mon_tx = NULL; + cs->dc.icc.mon_rx = NULL; + cs->dbusytimer.function = (void *) dbusy_timer_handler; + cs->dbusytimer.data = (long) cs; + init_timer(&cs->dbusytimer); + cs->writeisac(cs, ICC_MASK, 0xff); + cs->dc.icc.mocr = 0xaa; + if (test_bit(HW_IOM1, &cs->HW_Flags)) { + /* IOM 1 Mode */ + cs->writeisac(cs, ICC_ADF2, 0x0); + cs->writeisac(cs, ICC_SPCR, 0xa); + cs->writeisac(cs, ICC_ADF1, 0x2); + cs->writeisac(cs, ICC_STCR, 0x70); + cs->writeisac(cs, ICC_MODE, 0xc9); + } else { + /* IOM 2 Mode */ + if (!cs->dc.icc.adf2) + cs->dc.icc.adf2 = 0x80; + cs->writeisac(cs, ICC_ADF2, cs->dc.icc.adf2); + cs->writeisac(cs, ICC_SQXR, 0xa0); + cs->writeisac(cs, ICC_SPCR, 0x20); + cs->writeisac(cs, ICC_STCR, 0x70); + cs->writeisac(cs, ICC_MODE, 0xca); + cs->writeisac(cs, ICC_TIMR, 0x00); + cs->writeisac(cs, ICC_ADF1, 0x20); + } + ph_command(cs, ICC_CMD_RES); + cs->writeisac(cs, ICC_MASK, 0x0); + ph_command(cs, ICC_CMD_DI); +} + +HISAX_INITFUNC(void +clear_pending_icc_ints(struct IsdnCardState *cs)) +{ + int val, eval; + + val = cs->readisac(cs, ICC_STAR); + debugl1(cs, "ICC STAR %x", val); + val = cs->readisac(cs, ICC_MODE); + debugl1(cs, "ICC MODE %x", val); + val = cs->readisac(cs, ICC_ADF2); + debugl1(cs, "ICC ADF2 %x", val); + val = cs->readisac(cs, ICC_ISTA); + debugl1(cs, "ICC ISTA %x", val); + if (val & 0x01) { + eval = cs->readisac(cs, ICC_EXIR); + debugl1(cs, "ICC EXIR %x", eval); + } + val = cs->readisac(cs, ICC_CIR0); + debugl1(cs, "ICC CIR0 %x", val); + cs->dc.icc.ph_state = (val >> 2) & 0xf; + icc_sched_event(cs, D_L1STATECHANGE); + /* Disable all IRQ */ + cs->writeisac(cs, ICC_MASK, 0xFF); +} diff --git a/drivers/isdn/hisax/icc.h b/drivers/isdn/hisax/icc.h new file mode 100644 index 000000000000..b3ecbdcf8211 --- /dev/null +++ b/drivers/isdn/hisax/icc.h @@ -0,0 +1,73 @@ +// $Id: icc.h,v 1.2 2000/06/26 08:59:13 keil Exp $ +//----------------------------------------------------------------------------- +// +// ICC specific routines +// +// Author Matt Henderson & Guy Ellis - Traverse Tecnologies Pty Ltd +// www.traverse.com.au +// +// 1999.7.14 Initial implementation of routines for Siemens ISDN +// Communication Controler PEB 2070 based on the ISAC routines +// written by Karsten Keil. +// +// This file is (c) under GNU PUBLIC LICENSE +// +//----------------------------------------------------------------------------- + + +/* All Registers original Siemens Spec */ + +#define ICC_MASK 0x20 +#define ICC_ISTA 0x20 +#define ICC_STAR 0x21 +#define ICC_CMDR 0x21 +#define ICC_EXIR 0x24 +#define ICC_ADF2 0x39 +#define ICC_SPCR 0x30 +#define ICC_ADF1 0x38 +#define ICC_CIR0 0x31 +#define ICC_CIX0 0x31 +#define ICC_CIR1 0x33 +#define ICC_CIX1 0x33 +#define ICC_STCR 0x37 +#define ICC_MODE 0x22 +#define ICC_RSTA 0x27 +#define ICC_RBCL 0x25 +#define ICC_RBCH 0x2A +#define ICC_TIMR 0x23 +#define ICC_SQXR 0x3b +#define ICC_MOSR 0x3a +#define ICC_MOCR 0x3a +#define ICC_MOR0 0x32 +#define ICC_MOX0 0x32 +#define ICC_MOR1 0x34 +#define ICC_MOX1 0x34 + +#define ICC_RBCH_XAC 0x80 + +#define ICC_CMD_TIM 0x0 +#define ICC_CMD_RES 0x1 +#define ICC_CMD_DU 0x3 +#define ICC_CMD_EI1 0x4 +#define ICC_CMD_SSP 0x5 +#define ICC_CMD_DT 0x6 +#define ICC_CMD_AR 0x8 +#define ICC_CMD_ARL 0xA +#define ICC_CMD_AI 0xC +#define ICC_CMD_DI 0xF + +#define ICC_IND_DR 0x0 +#define ICC_IND_FJ 0x2 +#define ICC_IND_EI1 0x4 +#define ICC_IND_INT 0x6 +#define ICC_IND_PU 0x7 +#define ICC_IND_AR 0x8 +#define ICC_IND_ARL 0xA +#define ICC_IND_AI 0xC +#define ICC_IND_AIL 0xE +#define ICC_IND_DC 0xF + +extern void ICCVersion(struct IsdnCardState *cs, char *s); +extern void initicc(struct IsdnCardState *cs); +extern void icc_interrupt(struct IsdnCardState *cs, u_char val); +extern void clear_pending_icc_ints(struct IsdnCardState *cs); diff --git a/drivers/isdn/hisax/ipac.h b/drivers/isdn/hisax/ipac.h index 34a85dd6b857..eff327f9e4fb 100644 --- a/drivers/isdn/hisax/ipac.h +++ b/drivers/isdn/hisax/ipac.h @@ -1,24 +1,10 @@ -/* $Id: ipac.h,v 1.4 1999/12/23 15:09:32 keil Exp $ - +/* $Id: ipac.h,v 1.5 2000/06/26 08:59:13 keil Exp $ + * * ipac.h IPAC specific defines * * Author Karsten Keil (keil@isdn4linux.de) * - * - * $Log: ipac.h,v $ - * Revision 1.4 1999/12/23 15:09:32 keil - * change email - * - * Revision 1.3 1998/04/15 16:48:09 keil - * IPAC_ATX added - * - * Revision 1.2 1997/10/29 18:51:21 keil - * New files - * - * Revision 1.1.2.1 1997/10/17 22:10:48 keil - * new files on 2.0 - * - * + * This file is (c) under GNU PUBLIC LICENSE * */ diff --git a/drivers/isdn/hisax/isac.c b/drivers/isdn/hisax/isac.c index a992f76c48ee..cfeb3e3f9b7a 100644 --- a/drivers/isdn/hisax/isac.c +++ b/drivers/isdn/hisax/isac.c @@ -1,5 +1,5 @@ -/* $Id: isac.c,v 1.24 1999/10/14 20:25:28 keil Exp $ - +/* $Id: isac.c,v 1.26 2000/06/26 08:59:13 keil Exp $ + * * isac.c ISAC specific routines * * Author Karsten Keil (keil@isdn4linux.de) @@ -7,82 +7,6 @@ * This file is (c) under GNU PUBLIC LICENSE * For changes and modifications please read * ../../../Documentation/isdn/HiSax.cert - * - * $Log: isac.c,v $ - * Revision 1.24 1999/10/14 20:25:28 keil - * add a statistic for error monitoring - * - * Revision 1.23 1999/08/25 16:50:52 keil - * Fix bugs which cause 2.3.14 hangs (waitqueue init) - * - * 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 - * - * Revision 1.17 1998/08/13 23:36:37 keil - * HiSax 3.1 - don't work stable with current LinkLevel - * - * Revision 1.16 1998/05/25 12:58:01 keil - * HiSax golden code from certification, Don't use !!! - * No leased lines, no X75, but many changes. - * - * Revision 1.15 1998/04/15 16:45:32 keil - * new init code - * - * Revision 1.14 1998/04/10 10:35:26 paul - * fixed (silly?) warnings from egcs on Alpha. - * - * Revision 1.13 1998/03/07 22:57:01 tsbogend - * made HiSax working on Linux/Alpha - * - * Revision 1.12 1998/02/12 23:07:40 keil - * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() - * - * Revision 1.11 1998/02/09 10:54:49 keil - * fixes for leased mode - * - * Revision 1.10 1998/02/02 13:37:37 keil - * new init - * - * Revision 1.9 1997/11/06 17:09:07 keil - * New 2.1 init code - * - * Revision 1.8 1997/10/29 19:00:03 keil - * new layer1,changes for 2.1 - * - * Revision 1.7 1997/10/01 09:21:37 fritz - * Removed old compatibility stuff for 2.0.X kernels. - * From now on, this code is for 2.1.X ONLY! - * Old stuff is still in the separate branch. - * - * Revision 1.6 1997/08/15 17:47:08 keil - * avoid oops because a uninitialised timer - * - * Revision 1.5 1997/08/07 17:48:49 keil - * fix wrong parenthesis - * - * Revision 1.4 1997/07/30 17:11:59 keil - * fixed Timer3 - * - * Revision 1.3 1997/07/27 21:37:40 keil - * T3 implemented; supervisor l1timer; B-channel TEST_LOOP - * - * Revision 1.2 1997/06/26 11:16:15 keil - * first version - * - * */ #define __NO_VERSION__ diff --git a/drivers/isdn/hisax/isac.h b/drivers/isdn/hisax/isac.h index a8a46e13508c..751e8de1d59e 100644 --- a/drivers/isdn/hisax/isac.h +++ b/drivers/isdn/hisax/isac.h @@ -1,27 +1,10 @@ -/* $Id: isac.h,v 1.6 1999/12/23 15:09:32 keil Exp $ - +/* $Id: isac.h,v 1.7 2000/06/26 08:59:13 keil Exp $ + * * isac.h ISAC specific defines * * Author Karsten Keil (keil@isdn4linux.de) * - * - * $Log: isac.h,v $ - * Revision 1.6 1999/12/23 15:09:32 keil - * change email - * - * Revision 1.5 1998/05/25 12:58:03 keil - * HiSax golden code from certification, Don't use !!! - * No leased lines, no X75, but many changes. - * - * Revision 1.4 1997/10/29 19:09:34 keil - * new L1 - * - * Revision 1.3 1997/07/27 21:37:41 keil - * T3 implemented; supervisor l1timer; B-channel TEST_LOOP - * - * Revision 1.2 1997/06/26 11:16:16 keil - * first version - * + * This file is (c) under GNU PUBLIC LICENSE * */ diff --git a/drivers/isdn/hisax/isar.c b/drivers/isdn/hisax/isar.c index bfff8670754c..cfb7c6815028 100644 --- a/drivers/isdn/hisax/isar.c +++ b/drivers/isdn/hisax/isar.c @@ -1,39 +1,10 @@ -/* $Id: isar.c,v 1.9 2000/01/20 19:47:45 keil Exp $ - +/* $Id: isar.c,v 1.15 2000/06/26 08:59:13 keil Exp $ + * * isar.c ISAR (Siemens PSB 7110) specific routines * * Author Karsten Keil (keil@isdn4linux.de) * - * - * $Log: isar.c,v $ - * Revision 1.9 2000/01/20 19:47:45 keil - * Add Fax Class 1 support - * - * Revision 1.8 1999/12/19 13:00:56 keil - * Fix races in setting a new mode - * - * Revision 1.7 1999/10/14 20:25:29 keil - * add a statistic for error monitoring - * - * Revision 1.6 1999/08/31 11:20:20 paul - * various spelling corrections (new checksums may be needed, Karsten!) - * - * Revision 1.5 1999/08/25 16:59:55 keil - * Make ISAR V32bis modem running - * Make LL->HL interface open for additional commands - * - * 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 - * - * Revision 1.1 1998/08/13 23:33:47 keil - * First version, only init - * + * This file is (c) under GNU PUBLIC LICENSE * */ @@ -272,6 +243,14 @@ isar_load_firmware(struct IsdnCardState *cs, u_char *buf) printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret); goto reterror; } +#ifdef __BIG_ENDIAN + sadr = (blk_head.sadr & 0xff)*256 + blk_head.sadr/256; + blk_head.sadr = sadr; + sadr = (blk_head.len & 0xff)*256 + blk_head.len/256; + blk_head.len = sadr; + sadr = (blk_head.d_key & 0xff)*256 + blk_head.d_key/256; + blk_head.d_key = sadr; +#endif /* __BIG_ENDIAN */ cnt += BLK_HEAD_SIZE; p += BLK_HEAD_SIZE; printk(KERN_DEBUG"isar firmware block (%#x,%5d,%#x)\n", @@ -313,8 +292,13 @@ isar_load_firmware(struct IsdnCardState *cs, u_char *buf) #endif sadr += noc; while(noc) { +#ifdef __BIG_ENDIAN + *mp++ = *sp % 256; + *mp++ = *sp / 256; +#else *mp++ = *sp / 256; *mp++ = *sp % 256; +#endif /* __BIG_ENDIAN */ sp++; noc--; } @@ -557,8 +541,9 @@ isar_rcv_frame(struct IsdnCardState *cs, struct BCState *bcs) rcv_mbox(cs, ireg, ptr); if (ireg->cmsb & HDLC_FED) { if (bcs->hw.isar.rcvidx < 3) { /* last 2 bytes are the FCS */ - printk(KERN_WARNING "ISAR: HDLC frame too short(%d)\n", - bcs->hw.isar.rcvidx); + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar frame to short %d", + bcs->hw.isar.rcvidx); } else if (!(skb = dev_alloc_skb(bcs->hw.isar.rcvidx-2))) { printk(KERN_WARNING "ISAR: receive out of memory\n"); } else { @@ -567,6 +552,7 @@ isar_rcv_frame(struct IsdnCardState *cs, struct BCState *bcs) skb_queue_tail(&bcs->rqueue, skb); isar_sched_event(bcs, B_RCVBUFREADY); } + bcs->hw.isar.rcvidx = 0; } } break; @@ -635,13 +621,16 @@ isar_rcv_frame(struct IsdnCardState *cs, struct BCState *bcs) bcs->hw.isar.rcvidx += ireg->clsb; rcv_mbox(cs, ireg, ptr); if (ireg->cmsb & HDLC_FED) { + int len = bcs->hw.isar.rcvidx + + dle_count(bcs->hw.isar.rcvbuf, bcs->hw.isar.rcvidx); if (bcs->hw.isar.rcvidx < 3) { /* last 2 bytes are the FCS */ - printk(KERN_WARNING "ISAR: HDLC frame too short(%d)\n", - bcs->hw.isar.rcvidx); + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar frame to short %d", + bcs->hw.isar.rcvidx); } else if (!(skb = dev_alloc_skb(bcs->hw.isar.rcvidx))) { printk(KERN_WARNING "ISAR: receive out of memory\n"); } else { - memcpy(skb_put(skb, bcs->hw.isar.rcvidx), + insert_dle((u_char *)skb_put(skb, len), bcs->hw.isar.rcvbuf, bcs->hw.isar.rcvidx); skb_queue_tail(&bcs->rqueue, skb); @@ -649,8 +638,20 @@ isar_rcv_frame(struct IsdnCardState *cs, struct BCState *bcs) send_DLE_ETX(bcs); isar_sched_event(bcs, B_LL_OK); } + bcs->hw.isar.rcvidx = 0; } } + if (ireg->cmsb & SART_NMD) { /* ABORT */ + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar_rcv_frame: no more data"); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + bcs->hw.isar.rcvidx = 0; + send_DLE_ETX(bcs); + sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | + ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, 0, NULL); + bcs->hw.isar.state = STFAX_ESCAPE; + isar_sched_event(bcs, B_LL_NOCARRIER); + } break; default: printk(KERN_ERR"isar_rcv_frame mode (%x)error\n", bcs->mode); @@ -1073,6 +1074,9 @@ isar_pump_statev_fax(struct BCState *bcs, u_char devt) { debugl1(cs, "pump stev RSP_DISC"); if (bcs->hw.isar.state == STFAX_ESCAPE) { switch(bcs->hw.isar.newcmd) { + case 0: + bcs->hw.isar.state = STFAX_READY; + break; case PCTRL_CMD_FTH: case PCTRL_CMD_FTM: p1 = 10; @@ -1082,13 +1086,14 @@ isar_pump_statev_fax(struct BCState *bcs, u_char devt) { break; case PCTRL_CMD_FRH: case PCTRL_CMD_FRM: - p1 = bcs->hw.isar.newmod; + p1 = bcs->hw.isar.mod = bcs->hw.isar.newmod; bcs->hw.isar.newmod = 0; bcs->hw.isar.cmd = bcs->hw.isar.newcmd; bcs->hw.isar.newcmd = 0; sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, bcs->hw.isar.cmd, 1, &p1); bcs->hw.isar.state = STFAX_LINE; + bcs->hw.isar.try_mod = 3; break; default: if (cs->debug & L1_DEB_HSCX) @@ -1114,13 +1119,14 @@ isar_pump_statev_fax(struct BCState *bcs, u_char devt) { if (cs->debug & L1_DEB_HSCX) debugl1(cs, "pump stev RSP_SILDET"); if (bcs->hw.isar.state == STFAX_SILDET) { - p1 = bcs->hw.isar.newmod; + p1 = bcs->hw.isar.mod = bcs->hw.isar.newmod; bcs->hw.isar.newmod = 0; bcs->hw.isar.cmd = bcs->hw.isar.newcmd; bcs->hw.isar.newcmd = 0; sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, bcs->hw.isar.cmd, 1, &p1); bcs->hw.isar.state = STFAX_LINE; + bcs->hw.isar.try_mod = 3; } break; case PSEV_RSP_SILOFF: @@ -1128,6 +1134,17 @@ isar_pump_statev_fax(struct BCState *bcs, u_char devt) { debugl1(cs, "pump stev RSP_SILOFF"); break; case PSEV_RSP_FCERR: + if (bcs->hw.isar.state == STFAX_LINE) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev RSP_FCERR try %d", + bcs->hw.isar.try_mod); + if (bcs->hw.isar.try_mod--) { + sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, + bcs->hw.isar.cmd, 1, + &bcs->hw.isar.mod); + break; + } + } if (cs->debug & L1_DEB_HSCX) debugl1(cs, "pump stev RSP_FCERR"); bcs->hw.isar.state = STFAX_ESCAPE; @@ -1438,6 +1455,7 @@ isar_pump_cmd(struct BCState *bcs, u_char cmd, u_char para) bcs->hw.isar.mod = para; bcs->hw.isar.newmod = 0; bcs->hw.isar.newcmd = 0; + bcs->hw.isar.try_mod = 3; } else if ((bcs->hw.isar.state == STFAX_ACTIV) && (bcs->hw.isar.cmd == PCTRL_CMD_FTM) && (bcs->hw.isar.mod == para)) { @@ -1460,6 +1478,7 @@ isar_pump_cmd(struct BCState *bcs, u_char cmd, u_char para) bcs->hw.isar.mod = para; bcs->hw.isar.newmod = 0; bcs->hw.isar.newcmd = 0; + bcs->hw.isar.try_mod = 3; } else if ((bcs->hw.isar.state == STFAX_ACTIV) && (bcs->hw.isar.cmd == PCTRL_CMD_FTH) && (bcs->hw.isar.mod == para)) { @@ -1482,6 +1501,7 @@ isar_pump_cmd(struct BCState *bcs, u_char cmd, u_char para) bcs->hw.isar.mod = para; bcs->hw.isar.newmod = 0; bcs->hw.isar.newcmd = 0; + bcs->hw.isar.try_mod = 3; } else if ((bcs->hw.isar.state == STFAX_ACTIV) && (bcs->hw.isar.cmd == PCTRL_CMD_FRM) && (bcs->hw.isar.mod == para)) { @@ -1504,6 +1524,7 @@ isar_pump_cmd(struct BCState *bcs, u_char cmd, u_char para) bcs->hw.isar.mod = para; bcs->hw.isar.newmod = 0; bcs->hw.isar.newcmd = 0; + bcs->hw.isar.try_mod = 3; } else if ((bcs->hw.isar.state == STFAX_ACTIV) && (bcs->hw.isar.cmd == PCTRL_CMD_FRH) && (bcs->hw.isar.mod == para)) { diff --git a/drivers/isdn/hisax/isar.h b/drivers/isdn/hisax/isar.h index ec3bff89ea97..d3462183402d 100644 --- a/drivers/isdn/hisax/isar.h +++ b/drivers/isdn/hisax/isar.h @@ -1,32 +1,10 @@ -/* $Id: isar.h,v 1.7 2000/01/20 19:47:45 keil Exp $ +/* $Id: isar.h,v 1.9 2000/06/26 08:59:13 keil Exp $ + * * isar.h ISAR (Siemens PSB 7110) specific defines * * Author Karsten Keil (keil@isdn4linux.de) * - * - * $Log: isar.h,v $ - * Revision 1.7 2000/01/20 19:47:45 keil - * Add Fax Class 1 support - * - * Revision 1.6 1999/10/14 20:25:29 keil - * add a statistic for error monitoring - * - * Revision 1.5 1999/08/25 16:59:59 keil - * Make ISAR V32bis modem running - * Make LL->HL interface open for additional commands - * - * 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 - * - * Revision 1.1 1998/08/13 23:33:48 keil - * First version, only init - * + * This file is (c) under GNU PUBLIC LICENSE * */ @@ -208,7 +186,7 @@ #define HDLC_ERROR 0x1c #define HDLC_ERR_FAD 0x10 #define HDLC_ERR_RER 0x08 -#define HDLC_ERR_CER 0x01 +#define HDLC_ERR_CER 0x04 #define SART_NMD 0x01 #define BSTAT_RDM0 0x1 diff --git a/drivers/isdn/hisax/isdnl1.c b/drivers/isdn/hisax/isdnl1.c index 7715991cbc34..f523f378dcf8 100644 --- a/drivers/isdn/hisax/isdnl1.c +++ b/drivers/isdn/hisax/isdnl1.c @@ -1,5 +1,5 @@ -/* $Id: isdnl1.c,v 2.37 2000/01/20 19:51:46 keil Exp $ - +/* $Id: isdnl1.c,v 2.39 2000/06/26 08:59:13 keil Exp $ + * * isdnl1.c common low level stuff for Siemens Chipsetbased isdn cards * based on the teles driver from Jan den Ouden * @@ -13,136 +13,9 @@ * Fritz Elfert * Beat Doebeli * - * - * $Log: isdnl1.c,v $ - * Revision 2.37 2000/01/20 19:51:46 keil - * Fix AddTimer message - * Change CONFIG defines - * - * Revision 2.36 1999/08/25 16:50:57 keil - * Fix bugs which cause 2.3.14 hangs (waitqueue init) - * - * Revision 2.35 1999/08/22 20:27:07 calle - * backported changes from kernel 2.3.14: - * - several #include "config.h" gone, others come. - * - "struct device" changed to "struct net_device" in 2.3.14, added a - * define in isdn_compat.h for older kernel versions. - * - * 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 - * - * Revision 2.30 1998/09/30 22:27:00 keil - * Add init of l1.Flags - * - * Revision 2.29 1998/09/27 23:54:43 keil - * cosmetics - * - * Revision 2.28 1998/09/27 12:52:23 keil - * Fix against segfault, if the driver cannot allocate an IRQ channel - * - * Revision 2.27 1998/08/13 23:36:39 keil - * HiSax 3.1 - don't work stable with current LinkLevel - * - * Revision 2.26 1998/07/15 15:01:31 calle - * Support for AVM passive PCMCIA cards: - * A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0 - * - * Revision 2.25 1998/05/25 14:10:09 keil - * HiSax 3.0 - * X.75 and leased are working again. - * - * Revision 2.24 1998/05/25 12:58:04 keil - * HiSax golden code from certification, Don't use !!! - * No leased lines, no X75, but many changes. - * - * Revision 2.22 1998/04/15 16:40:13 keil - * Add S0Box and Teles PCI support - * Fix cardnr overwrite bug - * - * Revision 2.21 1998/04/10 10:35:28 paul - * fixed (silly?) warnings from egcs on Alpha. - * - * Revision 2.20 1998/03/09 23:19:27 keil - * Changes for PCMCIA - * - * Revision 2.18 1998/02/12 23:07:42 keil - * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() - * - * Revision 2.17 1998/02/11 17:28:07 keil - * Niccy PnP/PCI support - * - * Revision 2.16 1998/02/09 18:46:08 keil - * Support for Sedlbauer PCMCIA (Marcus Niemann) - * - * Revision 2.15 1998/02/09 10:54:51 keil - * fixes for leased mode - * - * Revision 2.14 1998/02/03 23:31:31 keil - * add AMD7930 support - * - * Revision 2.13 1998/02/02 13:33:02 keil - * New card support - * - * Revision 2.12 1998/01/31 21:41:48 keil - * changes for newer 2.1 kernels - * - * Revision 2.11 1997/11/12 15:01:23 keil - * COMPAQ_ISA changes - * - * Revision 2.10 1997/11/08 21:35:48 keil - * new l1 init - * - * Revision 2.9 1997/11/06 17:09:18 keil - * New 2.1 init code - * - * Revision 2.8 1997/10/29 19:00:05 keil - * new layer1,changes for 2.1 - * - * Revision 2.7 1997/10/10 20:56:50 fritz - * New HL interface. - * - * Revision 2.6 1997/09/12 10:05:16 keil - * ISDN_CTRL_DEBUG define - * - * Revision 2.5 1997/09/11 17:24:45 keil - * Add new cards - * - * Revision 2.4 1997/08/15 17:47:09 keil - * avoid oops because a uninitialised timer - * - * Revision 2.3 1997/08/01 11:16:40 keil - * cosmetics - * - * Revision 2.2 1997/07/30 17:11:08 keil - * L1deactivated exported - * - * Revision 2.1 1997/07/27 21:35:38 keil - * new layer1 interface - * - * Revision 2.0 1997/06/26 11:02:53 keil - * New Layer and card interface - * - * Revision 1.15 1997/05/27 15:17:55 fritz - * Added changes for recent 2.1.x kernels: - * changed return type of isdn_close - * queue_task_* -> queue_task - * clear/set_bit -> test_and_... where apropriate. - * changed type of hard_header_cache parameter. - * - * old changes removed KKe - * */ -const char *l1_revision = "$Revision: 2.37 $"; +const char *l1_revision = "$Revision: 2.39 $"; #define __NO_VERSION__ #include "hisax.h" @@ -155,7 +28,7 @@ struct Fsm l1fsm_b = {NULL, 0, 0, NULL, NULL}; static -struct Fsm l1fsm_d = +struct Fsm l1fsm_s = {NULL, 0, 0, NULL, NULL}; enum { @@ -168,9 +41,9 @@ enum { ST_L1_F8, }; -#define L1D_STATE_COUNT (ST_L1_F8+1) +#define L1S_STATE_COUNT (ST_L1_F8+1) -static char *strL1DState[] = +static char *strL1SState[] = { "ST_L1_F2", "ST_L1_F3", @@ -181,6 +54,29 @@ static char *strL1DState[] = "ST_L1_F8", }; +#ifdef HISAX_UINTERFACE +static +struct Fsm l1fsm_u = +{NULL, 0, 0, NULL, NULL}; + +enum { + ST_L1_RESET, + ST_L1_DEACT, + ST_L1_SYNC2, + ST_L1_TRANS, +}; + +#define L1U_STATE_COUNT (ST_L1_TRANS+1) + +static char *strL1UState[] = +{ + "ST_L1_RESET", + "ST_L1_DEACT", + "ST_L1_SYNC2", + "ST_L1_TRANS", +}; +#endif + enum { ST_L1_NULL, ST_L1_WAIT_ACT, @@ -559,7 +455,7 @@ l1_deact_cnf(struct FsmInst *fi, int event, void *arg) } static void -l1_deact_req(struct FsmInst *fi, int event, void *arg) +l1_deact_req_s(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; @@ -569,7 +465,7 @@ l1_deact_req(struct FsmInst *fi, int event, void *arg) } static void -l1_power_up(struct FsmInst *fi, int event, void *arg) +l1_power_up_s(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; @@ -599,7 +495,12 @@ l1_info2_ind(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - FsmChangeState(fi, ST_L1_F6); +#ifdef HISAX_UINTERFACE + if (test_bit(FLG_L1_UINT, &st->l1.Flags)) + FsmChangeState(fi, ST_L1_SYNC2); + else +#endif + FsmChangeState(fi, ST_L1_F6); st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL); } @@ -608,7 +509,12 @@ l1_info4_ind(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - FsmChangeState(fi, ST_L1_F7); +#ifdef HISAX_UINTERFACE + if (test_bit(FLG_L1_UINT, &st->l1.Flags)) + FsmChangeState(fi, ST_L1_TRANS); + else +#endif + FsmChangeState(fi, ST_L1_F7); st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL); if (test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags)) FsmDelTimer(&st->l1.timer, 4); @@ -628,6 +534,10 @@ l1_timer3(struct FsmInst *fi, int event, void *arg) test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags); if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) L1deactivated(st->l1.hardware); + +#ifdef HISAX_UINTERFACE + if (!test_bit(FLG_L1_UINT, &st->l1.Flags)) +#endif if (st->l1.l1m.state != ST_L1_F6) { FsmChangeState(fi, ST_L1_F3); st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL); @@ -656,7 +566,7 @@ l1_timer_deact(struct FsmInst *fi, int event, void *arg) } static void -l1_activate(struct FsmInst *fi, int event, void *arg) +l1_activate_s(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; @@ -674,9 +584,9 @@ l1_activate_no(struct FsmInst *fi, int event, void *arg) } } -static struct FsmNode L1DFnList[] HISAX_INITDATA = +static struct FsmNode L1SFnList[] HISAX_INITDATA = { - {ST_L1_F3, EV_PH_ACTIVATE, l1_activate}, + {ST_L1_F3, EV_PH_ACTIVATE, l1_activate_s}, {ST_L1_F6, EV_PH_ACTIVATE, l1_activate_no}, {ST_L1_F8, EV_PH_ACTIVATE, l1_activate_no}, {ST_L1_F3, EV_RESET_IND, l1_reset}, @@ -691,10 +601,10 @@ static struct FsmNode L1DFnList[] HISAX_INITDATA = {ST_L1_F6, EV_DEACT_CNF, l1_deact_cnf}, {ST_L1_F7, EV_DEACT_CNF, l1_deact_cnf}, {ST_L1_F8, EV_DEACT_CNF, l1_deact_cnf}, - {ST_L1_F6, EV_DEACT_IND, l1_deact_req}, - {ST_L1_F7, EV_DEACT_IND, l1_deact_req}, - {ST_L1_F8, EV_DEACT_IND, l1_deact_req}, - {ST_L1_F3, EV_POWER_UP, l1_power_up}, + {ST_L1_F6, EV_DEACT_IND, l1_deact_req_s}, + {ST_L1_F7, EV_DEACT_IND, l1_deact_req_s}, + {ST_L1_F8, EV_DEACT_IND, l1_deact_req_s}, + {ST_L1_F3, EV_POWER_UP, l1_power_up_s}, {ST_L1_F4, EV_RSYNC_IND, l1_go_F5}, {ST_L1_F6, EV_RSYNC_IND, l1_go_F8}, {ST_L1_F7, EV_RSYNC_IND, l1_go_F8}, @@ -722,7 +632,68 @@ static struct FsmNode L1DFnList[] HISAX_INITDATA = {ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact}, }; -#define L1D_FN_COUNT (sizeof(L1DFnList)/sizeof(struct FsmNode)) +#define L1S_FN_COUNT (sizeof(L1SFnList)/sizeof(struct FsmNode)) + +#ifdef HISAX_UINTERFACE +static void +l1_deact_req_u(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + FsmChangeState(fi, ST_L1_RESET); + FsmRestartTimer(&st->l1.timer, 550, EV_TIMER_DEACT, NULL, 2); + test_and_set_bit(FLG_L1_DEACTTIMER, &st->l1.Flags); + st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL); +} + +static void +l1_power_up_u(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + FsmRestartTimer(&st->l1.timer, TIMER3_VALUE, EV_TIMER3, NULL, 2); + test_and_set_bit(FLG_L1_T3RUN, &st->l1.Flags); +} + +static void +l1_info0_ind(struct FsmInst *fi, int event, void *arg) +{ + FsmChangeState(fi, ST_L1_DEACT); +} + +static void +l1_activate_u(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + st->l1.l1hw(st, HW_INFO1 | REQUEST, NULL); +} + +static struct FsmNode L1UFnList[] HISAX_INITDATA = +{ + {ST_L1_RESET, EV_DEACT_IND, l1_deact_req_u}, + {ST_L1_DEACT, EV_DEACT_IND, l1_deact_req_u}, + {ST_L1_SYNC2, EV_DEACT_IND, l1_deact_req_u}, + {ST_L1_TRANS, EV_DEACT_IND, l1_deact_req_u}, + {ST_L1_DEACT, EV_PH_ACTIVATE, l1_activate_u}, + {ST_L1_DEACT, EV_POWER_UP, l1_power_up_u}, + {ST_L1_DEACT, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_TRANS, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_RESET, EV_DEACT_CNF, l1_info0_ind}, + {ST_L1_DEACT, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_SYNC2, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_RESET, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_DEACT, EV_TIMER3, l1_timer3}, + {ST_L1_SYNC2, EV_TIMER3, l1_timer3}, + {ST_L1_TRANS, EV_TIMER_ACT, l1_timer_act}, + {ST_L1_DEACT, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_SYNC2, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_RESET, EV_TIMER_DEACT, l1_timer_deact}, +}; + +#define L1U_FN_COUNT (sizeof(L1UFnList)/sizeof(struct FsmNode)) + +#endif static void l1b_activate(struct FsmInst *fi, int event, void *arg) @@ -772,11 +743,18 @@ static struct FsmNode L1BFnList[] HISAX_INITDATA = HISAX_INITFUNC(void Isdnl1New(void)) { - l1fsm_d.state_count = L1D_STATE_COUNT; - l1fsm_d.event_count = L1_EVENT_COUNT; - l1fsm_d.strEvent = strL1Event; - l1fsm_d.strState = strL1DState; - FsmNew(&l1fsm_d, L1DFnList, L1D_FN_COUNT); +#ifdef HISAX_UINTERFACE + l1fsm_u.state_count = L1U_STATE_COUNT; + l1fsm_u.event_count = L1_EVENT_COUNT; + l1fsm_u.strEvent = strL1Event; + l1fsm_u.strState = strL1UState; + FsmNew(&l1fsm_u, L1UFnList, L1U_FN_COUNT); +#endif + l1fsm_s.state_count = L1S_STATE_COUNT; + l1fsm_s.event_count = L1_EVENT_COUNT; + l1fsm_s.strEvent = strL1Event; + l1fsm_s.strState = strL1SState; + FsmNew(&l1fsm_s, L1SFnList, L1S_FN_COUNT); l1fsm_b.state_count = L1B_STATE_COUNT; l1fsm_b.event_count = L1_EVENT_COUNT; l1fsm_b.strEvent = strL1Event; @@ -786,7 +764,10 @@ HISAX_INITFUNC(void Isdnl1New(void)) void Isdnl1Free(void) { - FsmFree(&l1fsm_d); +#ifdef HISAX_UINTERFACE + FsmFree(&l1fsm_u); +#endif + FsmFree(&l1fsm_s); FsmFree(&l1fsm_b); } @@ -804,7 +785,7 @@ dch_l2l1(struct PStack *st, int pr, void *arg) case (PH_ACTIVATE | REQUEST): if (cs->debug) debugl1(cs, "PH_ACTIVATE_REQ %s", - strL1DState[st->l1.l1m.state]); + st->l1.l1m.fsm->strState[st->l1.l1m.state]); if (test_bit(FLG_L1_ACTIVATED, &st->l1.Flags)) st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL); else { @@ -884,8 +865,16 @@ setstack_HiSax(struct PStack *st, struct IsdnCardState *cs) { st->l1.hardware = cs; st->protocol = cs->protocol; - st->l1.l1m.fsm = &l1fsm_d; + st->l1.l1m.fsm = &l1fsm_s; st->l1.l1m.state = ST_L1_F3; + st->l1.Flags = 0; +#ifdef HISAX_UINTERFACE + if (test_bit(FLG_HW_L1_UINT, &cs->HW_Flags)) { + st->l1.l1m.fsm = &l1fsm_u; + st->l1.l1m.state = ST_L1_RESET; + st->l1.Flags = FLG_L1_UINT; + } +#endif st->l1.l1m.debug = cs->debug; st->l1.l1m.userdata = st; st->l1.l1m.userint = 0; @@ -895,7 +884,6 @@ setstack_HiSax(struct PStack *st, struct IsdnCardState *cs) setstack_manager(st); st->l1.stlistp = &(cs->stlist); st->l2.l2l1 = dch_l2l1; - st->l1.Flags = 0; cs->setstack_d(st, cs); } diff --git a/drivers/isdn/hisax/isdnl1.h b/drivers/isdn/hisax/isdnl1.h index 01a96b7dd1a3..040a95aa5d36 100644 --- a/drivers/isdn/hisax/isdnl1.h +++ b/drivers/isdn/hisax/isdnl1.h @@ -1,34 +1,8 @@ -/* $Id: isdnl1.h,v 2.8 1998/11/15 23:54:59 keil Exp $ - - * $Log: isdnl1.h,v $ - * Revision 2.8 1998/11/15 23:54:59 keil - * changes from 2.0 - * - * Revision 2.7 1998/09/30 22:21:55 keil - * cosmetics - * - * Revision 2.6 1998/05/25 12:58:06 keil - * HiSax golden code from certification, Don't use !!! - * No leased lines, no X75, but many changes. - * - * Revision 2.5 1998/02/02 13:36:58 keil - * more debug - * - * Revision 2.4 1997/11/08 21:35:49 keil - * new l1 init - * - * Revision 2.3 1997/10/29 19:07:53 keil - * changes for 2.1 - * - * Revision 2.2 1997/07/30 17:11:09 keil - * L1deactivated exported - * - * Revision 2.1 1997/07/27 21:43:58 keil - * new l1 interface +/* $Id: isdnl1.h,v 2.9 2000/06/26 08:59:13 keil Exp $ * - * Revision 2.0 1997/06/26 11:02:55 keil - * New Layer and card interface + * Layer 1 defines * + * This file is (c) under GNU PUBLIC LICENSE * */ diff --git a/drivers/isdn/hisax/isdnl2.c b/drivers/isdn/hisax/isdnl2.c index 69e320262ccd..c20d0d21fe02 100644 --- a/drivers/isdn/hisax/isdnl2.c +++ b/drivers/isdn/hisax/isdnl2.c @@ -1,5 +1,5 @@ -/* $Id: isdnl2.c,v 2.20 1999/08/25 16:52:04 keil Exp $ - +/* $Id: isdnl2.c,v 2.23 2000/06/26 08:59:13 keil Exp $ + * * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden * @@ -10,80 +10,12 @@ * Thanks to Jan den Ouden * Fritz Elfert * - * $Log: isdnl2.c,v $ - * Revision 2.20 1999/08/25 16:52:04 keil - * Make gcc on AXP happy - * - * 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 - * - * Revision 2.15 1998/08/13 23:36:42 keil - * HiSax 3.1 - don't work stable with current LinkLevel - * - * Revision 2.14 1998/06/19 15:19:18 keil - * fix LAPB tx_cnt for none I-frames - * - * Revision 2.13 1998/06/18 23:17:20 keil - * LAPB bugfix - * - * Revision 2.12 1998/05/25 14:10:12 keil - * HiSax 3.0 - * X.75 and leased are working again. - * - * Revision 2.11 1998/05/25 12:58:08 keil - * HiSax golden code from certification, Don't use !!! - * No leased lines, no X75, but many changes. - * - * Revision 2.9 1998/04/10 10:35:30 paul - * fixed (silly?) warnings from egcs on Alpha. - * - * Revision 2.8 1998/03/07 22:57:04 tsbogend - * made HiSax working on Linux/Alpha - * - * Revision 2.7 1998/02/12 23:07:47 keil - * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() - * - * Revision 2.6 1998/02/02 13:36:15 keil - * bugfix X.75 win calculation - * - * Revision 2.5 1997/11/06 17:09:22 keil - * New 2.1 init code - * - * Revision 2.4 1997/10/29 19:02:01 keil - * new LL interface - * - * Revision 2.3 1997/10/01 09:21:39 fritz - * Removed old compatibility stuff for 2.0.X kernels. - * From now on, this code is for 2.1.X ONLY! - * Old stuff is still in the separate branch. - * - * Revision 2.2 1997/07/31 11:49:05 keil - * Error handling for no TEI assign - * - * Revision 2.1 1997/07/27 21:34:38 keil - * cosmetics - * - * Revision 2.0 1997/06/26 11:07:29 keil - * New q.921 and X.75 Layer2 - * - * - * Old log removed KKe - * */ #define __NO_VERSION__ #include "hisax.h" #include "isdnl2.h" -const char *l2_revision = "$Revision: 2.20 $"; +const char *l2_revision = "$Revision: 2.23 $"; static void l2m_debug(struct FsmInst *fi, char *fmt, ...); @@ -361,7 +293,7 @@ IsRNR(u_char * data, struct PStack *st) int iframe_error(struct PStack *st, struct sk_buff *skb) { - int i = l2addrsize(&st->l2) + (test_bit(FLG_MOD128, &st->l2.flag) ? 1 : 0); + int i = l2addrsize(&st->l2) + (test_bit(FLG_MOD128, &st->l2.flag) ? 2 : 1); int rsp = *skb->data & 0x2; if (test_bit(FLG_ORIG, &st->l2.flag)) @@ -371,7 +303,7 @@ iframe_error(struct PStack *st, struct sk_buff *skb) return 'L'; - if (skb->len <= i) + if (skb->len < i) return 'N'; if ((skb->len - i) > st->l2.maxlen) diff --git a/drivers/isdn/hisax/isdnl2.h b/drivers/isdn/hisax/isdnl2.h index ac21d1c4ddb7..28e753c8c374 100644 --- a/drivers/isdn/hisax/isdnl2.h +++ b/drivers/isdn/hisax/isdnl2.h @@ -1,4 +1,10 @@ -/* isdnl2.h */ +/* $Id: isdnl2.h,v 1.3 2000/06/26 08:59:13 keil Exp $ + * + * Layer 2 defines + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ #define RR 0x01 #define RNR 0x05 diff --git a/drivers/isdn/hisax/isdnl3.c b/drivers/isdn/hisax/isdnl3.c index 6dc8bd9d06c8..b4e473670516 100644 --- a/drivers/isdn/hisax/isdnl3.c +++ b/drivers/isdn/hisax/isdnl3.c @@ -1,5 +1,5 @@ -/* $Id: isdnl3.c,v 2.10 1999/07/21 14:46:19 keil Exp $ - +/* $Id: isdnl3.c,v 2.14 2000/06/26 08:59:13 keil Exp $ + * * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden * @@ -10,65 +10,13 @@ * Thanks to Jan den Ouden * 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 - * - * Revision 2.7 1998/05/25 14:10:15 keil - * HiSax 3.0 - * X.75 and leased are working again. - * - * Revision 2.6 1998/05/25 12:58:11 keil - * HiSax golden code from certification, Don't use !!! - * No leased lines, no X75, but many changes. - * - * Revision 2.5 1998/02/12 23:07:52 keil - * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() - * - * Revision 2.4 1997/11/06 17:09:25 keil - * New 2.1 init code - * - * Revision 2.3 1997/10/29 19:07:53 keil - * changes for 2.1 - * - * Revision 2.2 1997/10/01 09:21:41 fritz - * Removed old compatibility stuff for 2.0.X kernels. - * From now on, this code is for 2.1.X ONLY! - * Old stuff is still in the separate branch. - * - * Revision 2.1 1997/08/03 14:36:32 keil - * Implement RESTART procedure - * - * Revision 2.0 1997/07/27 21:15:42 keil - * New Callref based layer3 - * - * Revision 1.11 1997/06/26 11:11:44 keil - * SET_SKBFREE now on creation of a SKB - * - * Revision 1.10 1997/04/06 22:54:16 keil - * Using SKB's - * - * Revision 1.9 1997/03/25 23:11:25 keil - * US NI-1 protocol - * - * Revision 1.8 1997/03/21 18:53:44 keil - * Report no protocol error to syslog too - * - * Remove old logs /KKe - * */ #define __NO_VERSION__ #include "hisax.h" #include "isdnl3.h" #include -const char *l3_revision = "$Revision: 2.10 $"; +const char *l3_revision = "$Revision: 2.14 $"; static struct Fsm l3fsm = @@ -234,7 +182,7 @@ int L3AddTimer(struct L3Timer *t, int millisec, int event) { - if (t->tl.next || t->tl.prev) { + if (timer_pending(&t->tl)) { printk(KERN_WARNING "L3AddTimer: timer already active!\n"); return -1; } @@ -286,7 +234,7 @@ no_l3_proto_spec(struct PStack *st, isdn_ctrl *ic) extern void setstack_dss1(struct PStack *st); #endif -#ifdef CONFIG_HISAX_NI1 +#ifdef CONFIG_HISAX_NI1 extern void setstack_ni1(struct PStack *st); #endif @@ -375,10 +323,13 @@ static void l3ml3p(struct PStack *st, int pr) { struct l3_process *p = st->l3.proc; + struct l3_process *np; while (p) { + /* p might be kfreed under us, so we need to save where we want to go on */ + np = p->next; st->l3.l3ml3(st, pr, p); - p = p->next; + p = np; } } @@ -405,7 +356,7 @@ setstack_l3dc(struct PStack *st, struct Channel *chanp) setstack_dss1(st); } else #endif -#ifdef CONFIG_HISAX_NI1 +#ifdef CONFIG_HISAX_NI1 if (st->protocol == ISDN_PTYPE_NI1) { setstack_ni1(st); } else @@ -593,7 +544,6 @@ static struct FsmNode L3FnList[] HISAX_INITDATA = void l3_msg(struct PStack *st, int pr, void *arg) { - switch (pr) { case (DL_DATA | REQUEST): if (st->l3.l3m.state == ST_L3_LC_ESTAB) { diff --git a/drivers/isdn/hisax/isdnl3.h b/drivers/isdn/hisax/isdnl3.h index c7050f5f13f9..6027e1822efa 100644 --- a/drivers/isdn/hisax/isdnl3.h +++ b/drivers/isdn/hisax/isdnl3.h @@ -1,38 +1,6 @@ -/* $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 - * - * Revision 2.2 1998/05/25 14:10:17 keil - * HiSax 3.0 - * X.75 and leased are working again. - * - * Revision 2.1 1998/05/25 12:58:13 keil - * HiSax golden code from certification, Don't use !!! - * No leased lines, no X75, but many changes. - * - * Revision 2.0 1997/07/27 21:15:42 keil - * New Callref based layer3 - * - * Revision 1.4 1997/06/26 11:20:57 keil - * ? - * - * Revision 1.3 1997/04/06 22:54:17 keil - * Using SKB's - * - * Revision 1.2 1997/01/21 22:31:28 keil - * new statemachine; L3 timers - * - * Revision 1.1 1996/10/13 20:03:47 keil - * Initial revision +/* $Id: isdnl3.h,v 2.6 2000/06/26 08:59:13 keil Exp $ * + * This file is (c) under GNU PUBLIC LICENSE * */ diff --git a/drivers/isdn/hisax/isurf.c b/drivers/isdn/hisax/isurf.c index 914ecdea454c..467028a619bd 100644 --- a/drivers/isdn/hisax/isurf.c +++ b/drivers/isdn/hisax/isurf.c @@ -1,38 +1,10 @@ -/* $Id: isurf.c,v 1.8 1999/12/19 13:09:42 keil Exp $ - +/* $Id: isurf.c,v 1.9 2000/06/26 08:59:13 keil Exp $ + * * isurf.c low level stuff for Siemens I-Surf/I-Talk cards * * Author Karsten Keil (keil@isdn4linux.de) * - * $Log: isurf.c,v $ - * Revision 1.8 1999/12/19 13:09:42 keil - * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for - * signal proof delays - * - * Revision 1.7 1999/11/14 23:37:03 keil - * new ISA memory mapped IO - * - * Revision 1.6 1999/09/04 06:20:06 keil - * Changes from kernel set_current_state() - * - * Revision 1.5 1999/08/25 17:00:02 keil - * Make ISAR V32bis modem running - * Make LL->HL interface open for additional commands - * - * Revision 1.4 1999/08/22 20:27:09 calle - * backported changes from kernel 2.3.14: - * - several #include "config.h" gone, others come. - * - "struct device" changed to "struct net_device" in 2.3.14, added a - * define in isdn_compat.h for older kernel versions. - * - * Revision 1.3 1999/07/12 21:05:18 keil - * fix race in IRQ handling - * added watchdog for lost IRQs - * - * Revision 1.2 1999/07/01 08:07:56 keil - * Initial version - * - * + * This file is (c) under GNU PUBLIC LICENSE * */ @@ -44,7 +16,7 @@ extern const char *CardType[]; -static const char *ISurf_revision = "$Revision: 1.8 $"; +static const char *ISurf_revision = "$Revision: 1.9 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -164,10 +136,10 @@ reset_isurf(struct IsdnCardState *cs, u_char chips) byteout(cs->hw.isurf.reset, chips); /* Reset On */ save_flags(flags); sti(); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); byteout(cs->hw.isurf.reset, ISURF_ISAR_EA); /* Reset Off */ - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); restore_flags(flags); } diff --git a/drivers/isdn/hisax/ix1_micro.c b/drivers/isdn/hisax/ix1_micro.c index cb94d5bb392c..01976b82fc1e 100644 --- a/drivers/isdn/hisax/ix1_micro.c +++ b/drivers/isdn/hisax/ix1_micro.c @@ -1,5 +1,5 @@ -/* $Id: ix1_micro.c,v 2.8 1999/07/12 21:05:19 keil Exp $ - +/* $Id: ix1_micro.c,v 2.9 2000/06/26 08:59:13 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 * @@ -10,45 +10,6 @@ * Fritz Elfert * 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 - * - * Revision 2.6 1998/02/11 17:28:09 keil - * Niccy PnP/PCI support - * - * Revision 2.5 1998/02/02 13:29:42 keil - * fast io - * - * Revision 2.4 1997/11/08 21:35:50 keil - * new l1 init - * - * Revision 2.3 1997/11/06 17:09:35 keil - * New 2.1 init code - * - * Revision 2.2 1997/10/29 18:55:51 keil - * changes for 2.1.60 (irq2dev_map) - * - * Revision 2.1 1997/07/27 21:47:09 keil - * new interface structures - * - * Revision 2.0 1997/06/26 11:02:50 keil - * New Layer and card interface - * - * Revision 1.3 1997/04/13 19:54:02 keil - * Change in IRQ check delay for SMP - * - * Revision 1.2 1997/04/06 22:54:21 keil - * Using SKB's - * - * Revision 1.1 1997/01/27 15:43:10 keil - * first version - * - * */ /* @@ -88,7 +49,7 @@ #include "isdnl1.h" extern const char *CardType[]; -const char *ix1_revision = "$Revision: 2.8 $"; +const char *ix1_revision = "$Revision: 2.9 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) diff --git a/drivers/isdn/hisax/jade.c b/drivers/isdn/hisax/jade.c index 4f07eed875a9..b61ce27e14b9 100644 --- a/drivers/isdn/hisax/jade.c +++ b/drivers/isdn/hisax/jade.c @@ -1,13 +1,10 @@ -/* $Id: jade.c,v 1.2 1999/07/01 08:07:57 keil Exp $ +/* $Id: jade.c,v 1.4 2000/06/26 08:59:14 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 - * + * This file is (c) under GNU PUBLIC LICENSE * */ diff --git a/drivers/isdn/hisax/jade.h b/drivers/isdn/hisax/jade.h index bcbe6b29181a..b94b968e42bb 100644 --- a/drivers/isdn/hisax/jade.h +++ b/drivers/isdn/hisax/jade.h @@ -1,13 +1,9 @@ -/* $Id: jade.h,v 1.2 1999/07/01 08:07:58 keil Exp $ +/* $Id: jade.h,v 1.3 2000/06/26 08:59:14 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 - * + * This file is (c) under GNU PUBLIC LICENSE * */ diff --git a/drivers/isdn/hisax/jade_irq.c b/drivers/isdn/hisax/jade_irq.c index e54c80c1a082..2a86e95d4653 100644 --- a/drivers/isdn/hisax/jade_irq.c +++ b/drivers/isdn/hisax/jade_irq.c @@ -1,13 +1,10 @@ -/* $Id: jade_irq.c,v 1.2 1999/07/01 08:07:59 keil Exp $ +/* $Id: jade_irq.c,v 1.4 2000/06/26 08:59:14 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 - * + * This file is (c) under GNU PUBLIC LICENSE * */ diff --git a/drivers/isdn/hisax/l3_1tr6.c b/drivers/isdn/hisax/l3_1tr6.c index e6297bd94d18..831d788b5a9d 100644 --- a/drivers/isdn/hisax/l3_1tr6.c +++ b/drivers/isdn/hisax/l3_1tr6.c @@ -1,5 +1,5 @@ -/* $Id: l3_1tr6.c,v 2.10 2000/01/20 19:42:01 keil Exp $ - +/* $Id: l3_1tr6.c,v 2.12 2000/08/20 07:31:30 keil Exp $ + * * German 1TR6 D-channel protocol * * Author Karsten Keil (keil@isdn4linux.de) @@ -8,51 +8,6 @@ * For changes and modifications please read * ../../../Documentation/isdn/HiSax.cert * - * - * $Log: l3_1tr6.c,v $ - * Revision 2.10 2000/01/20 19:42:01 keil - * Fixed uninitialiesed location - * - * 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 - * - * Revision 2.7 1998/08/13 23:36:45 keil - * HiSax 3.1 - don't work stable with current LinkLevel - * - * Revision 2.6 1998/05/25 14:10:18 keil - * HiSax 3.0 - * X.75 and leased are working again. - * - * Revision 2.5 1998/05/25 12:58:14 keil - * HiSax golden code from certification, Don't use !!! - * No leased lines, no X75, but many changes. - * - * Revision 2.4 1998/02/12 23:07:57 keil - * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() - * - * Revision 2.3 1997/11/06 17:12:24 keil - * KERN_NOTICE --> KERN_INFO - * - * Revision 2.2 1997/10/29 19:03:00 keil - * changes for 2.1 - * - * Revision 2.1 1997/08/03 15:28:09 keil - * release L3 empty processes - * - * Revision 2.0 1997/07/27 21:15:45 keil - * New Callref based layer3 - * - * Revision 1.12 1997/06/26 11:11:45 keil - * SET_SKBFREE now on creation of a SKB - * - * Revision 1.11 1997/04/06 22:54:18 keil - * Using SKB's - * - * Old Log removed /KKe - * */ #define __NO_VERSION__ @@ -62,7 +17,7 @@ #include extern char *HiSax_getrev(const char *revision); -const char *l3_1tr6_revision = "$Revision: 2.10 $"; +const char *l3_1tr6_revision = "$Revision: 2.12 $"; #define MsgHead(ptr, cref, mty, dis) \ *ptr++ = dis; \ @@ -928,7 +883,7 @@ down1tr6(struct PStack *st, int pr, void *arg) } else { proc->chan = chan; chan->proc = proc; - proc->para.setup = chan->setup; + memcpy(&proc->para.setup, &chan->setup, sizeof(setup_parm)); proc->callref = cr; } } else { diff --git a/drivers/isdn/hisax/l3_1tr6.h b/drivers/isdn/hisax/l3_1tr6.h index 7c0ba56b291c..f9d46f659e62 100644 --- a/drivers/isdn/hisax/l3_1tr6.h +++ b/drivers/isdn/hisax/l3_1tr6.h @@ -1,16 +1,8 @@ -/* $Id: l3_1tr6.h,v 2.1 1998/08/13 23:36:48 keil Exp $ +/* $Id: l3_1tr6.h,v 2.2 2000/06/26 08:59:14 keil Exp $ * * German 1TR6 D-channel protocol defines * - * $Log: l3_1tr6.h,v $ - * Revision 2.1 1998/08/13 23:36:48 keil - * HiSax 3.1 - don't work stable with current LinkLevel - * - * Revision 2.0 1997/07/27 21:15:47 keil - * New Callref based layer3 - * - * Revision 1.1 1996/10/13 20:03:48 keil - * Initial revision + * This file is (c) under GNU PUBLIC LICENSE * */ #ifndef l3_1tr6 diff --git a/drivers/isdn/hisax/l3dss1.c b/drivers/isdn/hisax/l3dss1.c index 2b0451a0af3e..8778bf3a0eb4 100644 --- a/drivers/isdn/hisax/l3dss1.c +++ b/drivers/isdn/hisax/l3dss1.c @@ -1,5 +1,5 @@ -/* $Id: l3dss1.c,v 2.23 2000/02/26 01:38:14 keil Exp $ - +/* $Id: l3dss1.c,v 2.29 2000/06/26 08:59:14 keil Exp $ + * * EURO/DSS1 D-channel protocol * * Author Karsten Keil (keil@isdn4linux.de) @@ -12,91 +12,6 @@ * Thanks to Jan den Ouden * Fritz Elfert * - * $Log: l3dss1.c,v $ - * Revision 2.23 2000/02/26 01:38:14 keil - * Fixes for V.110 encoding LLC from Jens Jakobsen - * - * Revision 2.22 2000/01/20 19:44:20 keil - * Fixed uninitialiesed location - * Fixed redirecting number IE in Setup - * Changes from certification - * option for disabling use of KEYPAD protocol - * - * Revision 2.21 1999/12/19 20:25:17 keil - * fixed LLC for outgoing analog calls - * IE Signal is valid on older local switches - * - * Revision 2.20 1999/10/11 22:16:27 keil - * Suspend/Resume is possible without explicit ID too - * - * Revision 2.19 1999/08/25 16:55:23 keil - * Fix for test case TC10011 - * - * 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 - * - * Revision 2.11 1998/08/13 23:36:51 keil - * HiSax 3.1 - don't work stable with current LinkLevel - * - * Revision 2.10 1998/05/25 14:10:20 keil - * HiSax 3.0 - * X.75 and leased are working again. - * - * Revision 2.9 1998/05/25 12:58:17 keil - * HiSax golden code from certification, Don't use !!! - * No leased lines, no X75, but many changes. - * - * Revision 2.8 1998/03/19 13:18:47 keil - * Start of a CAPI like interface for supplementary Service - * first service: SUSPEND - * - * Revision 2.7 1998/02/12 23:08:01 keil - * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() - * - * Revision 2.6 1998/02/03 23:26:35 keil - * V110 extensions from Thomas Pfeiffer - * - * Revision 2.5 1998/02/02 13:34:28 keil - * Support australian Microlink net and german AOCD - * - * Revision 2.4 1997/11/06 17:12:25 keil - * KERN_NOTICE --> KERN_INFO - * - * Revision 2.3 1997/10/29 19:03:01 keil - * changes for 2.1 - * - * Revision 2.2 1997/08/07 17:44:36 keil - * Fix RESTART - * - * Revision 2.1 1997/08/03 14:36:33 keil - * Implement RESTART procedure - * - * Revision 2.0 1997/07/27 21:15:43 keil - * New Callref based layer3 - * - * Revision 1.17 1997/06/26 11:11:46 keil - * SET_SKBFREE now on creation of a SKB - * - * Revision 1.15 1997/04/17 11:50:48 keil - * pa->loc was undefined, if it was not send by the exchange - * - * Old log removed /KKe - * */ #define __NO_VERSION__ @@ -107,7 +22,7 @@ #include extern char *HiSax_getrev(const char *revision); -const char *dss1_revision = "$Revision: 2.23 $"; +const char *dss1_revision = "$Revision: 2.29 $"; #define EXT_BEARER_CAPS 1 @@ -806,6 +721,9 @@ check_infoelements(struct l3_process *pc, struct sk_buff *skb, int *checklist) u_char *p, ie; int l, newpos, oldpos; int err_seq = 0, err_len = 0, err_compr = 0, err_ureg = 0; + u_char codeset = 0; + u_char old_codeset = 0; + u_char codelock = 1; p = skb->data; /* skip cr */ @@ -814,20 +732,34 @@ check_infoelements(struct l3_process *pc, struct sk_buff *skb, int *checklist) 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++; + if ((*p & 0xf0) == 0x90) { /* shift codeset */ + old_codeset = codeset; + codeset = *p & 7; + if (*p & 0x08) + codelock = 0; + else + codelock = 1; + if (pc->debug & L3_DEB_CHECK) + l3_debug(pc->st, "check IE shift%scodeset %d->%d", + codelock ? " locking ": " ", old_codeset, codeset); + p++; + continue; + } + if (!codeset) { /* only codeset 0 */ + 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 - oldpos = newpos; + err_ureg++; } - } else { - if (ie_in_set(pc, *p, comp_required)) - err_compr++; - else - err_ureg++; } ie = *p++; if (ie & 0x80) { @@ -837,12 +769,19 @@ check_infoelements(struct l3_process *pc, struct sk_buff *skb, int *checklist) p += l; l += 2; } - if (l > getmax_ie_len(ie)) + if (!codeset && (l > getmax_ie_len(ie))) err_len++; + if (!codelock) { + if (pc->debug & L3_DEB_CHECK) + l3_debug(pc->st, "check IE shift back codeset %d->%d", + codeset, old_codeset); + codeset = old_codeset; + codelock = 1; + } } 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", + l3_debug(pc->st, "check IE MT(%x) %d/%d/%d/%d", mt, err_compr, err_ureg, err_len, err_seq); if (err_compr) return(ERR_IE_COMPREHENSION); @@ -1044,7 +983,7 @@ l3dss1_release_cmpl(struct l3_process *pc, u_char pr, void *arg) #if EXT_BEARER_CAPS -u_char * +static u_char * EncodeASyncParams(u_char * p, u_char si2) { // 7c 06 88 90 21 42 00 bb @@ -1109,7 +1048,7 @@ EncodeASyncParams(u_char * p, u_char si2) return p + 3; } -u_char +static u_char EncodeSyncParams(u_char si2, u_char ai) { @@ -1313,39 +1252,31 @@ l3dss1_setup_req(struct l3_process *pc, u_char pr, /* * Set Bearer Capability, Map info from 1TR6-convention to EDSS1 */ - if (!send_keypad) - switch (pc->para.setup.si1) { - case 1: /* Telephony */ - *p++ = 0x4; /* BC-IE-code */ - *p++ = 0x3; /* Length */ - *p++ = 0x90; /* Coding Std. CCITT, 3.1 kHz audio */ - *p++ = 0x90; /* Circuit-Mode 64kbps */ - *p++ = 0xa3; /* A-Law Audio */ - break; - case 5: /* Datatransmission 64k, BTX */ - case 7: /* Datatransmission 64k */ - default: - *p++ = 0x4; /* BC-IE-code */ - *p++ = 0x2; /* Length */ - *p++ = 0x88; /* Coding Std. CCITT, unrestr. dig. Inform. */ - *p++ = 0x90; /* Circuit-Mode 64kbps */ - break; - } - 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 */ + switch (pc->para.setup.si1) { + case 1: /* Telephony */ + *p++ = IE_BEARER; + *p++ = 0x3; /* Length */ + *p++ = 0x90; /* Coding Std. CCITT, 3.1 kHz audio */ + *p++ = 0x90; /* Circuit-Mode 64kbps */ + *p++ = 0xa3; /* A-Law Audio */ + break; + case 5: /* Datatransmission 64k, BTX */ + case 7: /* Datatransmission 64k */ + default: + *p++ = IE_BEARER; + *p++ = 0x2; /* Length */ + *p++ = 0x88; /* Coding Std. CCITT, unrestr. dig. Inform. */ + *p++ = 0x90; /* Circuit-Mode 64kbps */ + break; + } + + if (send_keypad) { + *p++ = IE_KEYPAD; *p++ = strlen(teln); while (*teln) - *p++ = (*teln++) & 0x7F; - } + *p++ = (*teln++) & 0x7F; + } - /* * What about info2? Mapping to High-Layer-Compatibility? */ @@ -1394,7 +1325,7 @@ l3dss1_setup_req(struct l3_process *pc, u_char pr, sp++; } if (*msn) { - *p++ = 0x6c; + *p++ = IE_CALLING_PN; *p++ = strlen(msn) + (screen ? 2 : 1); /* Classify as AnyPref. */ if (screen) { @@ -1407,7 +1338,7 @@ l3dss1_setup_req(struct l3_process *pc, u_char pr, } if (sub) { *sub++ = '.'; - *p++ = 0x6d; /* Calling party subaddress */ + *p++ = IE_CALLING_SUB; *p++ = strlen(sub) + 2; *p++ = 0x80; /* NSAP coded */ *p++ = 0x50; /* local IDI format */ @@ -1425,33 +1356,27 @@ l3dss1_setup_req(struct l3_process *pc, u_char pr, } 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; - } + *p++ = IE_CALLED_PN; + *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++ = IE_CALLED_SUB; + *p++ = strlen(sub) + 2; + *p++ = 0x80; /* NSAP coded */ + *p++ = 0x50; /* local IDI format */ + while (*sub) + *p++ = *sub++ & 0x7f; + } } #if EXT_BEARER_CAPS - 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; + if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175)) { // sync. Bitratenadaption, V.110/X.30 + + *p++ = IE_LLC; *p++ = 0x04; *p++ = 0x88; *p++ = 0x90; @@ -1459,7 +1384,7 @@ l3dss1_setup_req(struct l3_process *pc, u_char pr, *p++ = EncodeSyncParams(pc->para.setup.si2 - 160, 0x80); } else if ((pc->para.setup.si2 >= 176) && (pc->para.setup.si2 <= 191)) { // sync. Bitratenadaption, V.120 - *p++ = 0x7c; + *p++ = IE_LLC; *p++ = 0x05; *p++ = 0x88; *p++ = 0x90; @@ -1468,7 +1393,7 @@ l3dss1_setup_req(struct l3_process *pc, u_char pr, *p++ = 0x82; } else if (pc->para.setup.si2 >= 192) { // async. Bitratenadaption, V.110/X.30 - *p++ = 0x7c; + *p++ = IE_LLC; *p++ = 0x06; *p++ = 0x88; *p++ = 0x90; @@ -1477,18 +1402,18 @@ l3dss1_setup_req(struct l3_process *pc, u_char pr, #ifndef CONFIG_HISAX_NO_LLC } else { switch (pc->para.setup.si1) { - case 1: /* Telephony */ - *p++ = 0x7c; /* BC-IE-code */ - *p++ = 0x3; /* Length */ - *p++ = 0x90; /* Coding Std. CCITT, 3.1 kHz audio */ - *p++ = 0x90; /* Circuit-Mode 64kbps */ - *p++ = 0xa3; /* A-Law Audio */ + case 1: /* Telephony */ + *p++ = IE_LLC; + *p++ = 0x3; /* Length */ + *p++ = 0x90; /* Coding Std. CCITT, 3.1 kHz audio */ + *p++ = 0x90; /* Circuit-Mode 64kbps */ + *p++ = 0xa3; /* A-Law Audio */ break; - case 5: /* Datatransmission 64k, BTX */ - case 7: /* Datatransmission 64k */ + case 5: /* Datatransmission 64k, BTX */ + case 7: /* Datatransmission 64k */ default: - *p++ = 0x7c; /* BC-IE-code */ - *p++ = 0x2; /* Length */ + *p++ = IE_LLC; + *p++ = 0x2; /* Length */ *p++ = 0x88; /* Coding Std. CCITT, unrestr. dig. Inform. */ *p++ = 0x90; /* Circuit-Mode 64kbps */ break; @@ -1988,6 +1913,16 @@ l3dss1_proceed_req(struct l3_process *pc, u_char pr, pc->st->l3.l3l4(pc->st, CC_PROCEED_SEND | INDICATION, pc); } +static void +l3dss1_setup_ack_req(struct l3_process *pc, u_char pr, + void *arg) +{ + newl3state(pc, 25); + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T302, CC_T302); + l3dss1_message(pc, MT_SETUP_ACKNOWLEDGE); +} + /********************************************/ /* deliver a incoming display message to HL */ /********************************************/ @@ -2025,7 +1960,7 @@ l3dss1_progress(struct l3_process *pc, u_char pr, void *arg) if (p[1] != 2) { err = 1; pc->para.cause = 100; - } else if (p[2] & 0x60) { + } else if (!(p[2] & 0x70)) { switch (p[2]) { case 0x80: case 0x81: @@ -2129,9 +2064,22 @@ l3dss1_information(struct l3_process *pc, u_char pr, void *arg) { int ret; struct sk_buff *skb = arg; + u_char *p; + char tmp[32]; ret = check_infoelements(pc, skb, ie_INFORMATION); - l3dss1_std_ie_err(pc, ret); + if (ret) + l3dss1_std_ie_err(pc, ret); + if (pc->state == 25) { /* overlap receiving */ + L3DelTimer(&pc->timer); + p = skb->data; + if ((p = findie(p, skb->len, 0x70, 0))) { + iecpy(tmp, p, 1); + strcat(pc->para.setup.eazmsn, tmp); + pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc); + } + L3AddTimer(&pc->timer, T302, CC_T302); + } } /******************************/ @@ -2356,6 +2304,16 @@ l3dss1_dummy(struct l3_process *pc, u_char pr, void *arg) { } +static void +l3dss1_t302(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->para.loc = 0; + pc->para.cause = 28; /* invalid number */ + l3dss1_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); +} + static void l3dss1_t303(struct l3_process *pc, u_char pr, void *arg) { @@ -2375,6 +2333,7 @@ static void l3dss1_t304(struct l3_process *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); + pc->para.loc = 0; pc->para.cause = 102; l3dss1_disconnect_req(pc, pr, NULL); pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); @@ -2414,6 +2373,7 @@ static void l3dss1_t310(struct l3_process *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); + pc->para.loc = 0; pc->para.cause = 102; l3dss1_disconnect_req(pc, pr, NULL); pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); @@ -2423,6 +2383,7 @@ static void l3dss1_t313(struct l3_process *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); + pc->para.loc = 0; pc->para.cause = 102; l3dss1_disconnect_req(pc, pr, NULL); pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc); @@ -2812,32 +2773,36 @@ 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(9) | SBIT(10), + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(25), CC_DISCONNECT | REQUEST, l3dss1_disconnect_req}, {SBIT(12), CC_RELEASE | REQUEST, l3dss1_release_req}, {ALL_STATES, CC_RESTART | REQUEST, l3dss1_restart}, - {SBIT(6), + {SBIT(6) | SBIT(25), CC_IGNORE | REQUEST, l3dss1_reset}, - {SBIT(6), + {SBIT(6) | SBIT(25), CC_REJECT | REQUEST, l3dss1_reject_req}, - {SBIT(6), + {SBIT(6) | SBIT(25), CC_PROCEED_SEND | REQUEST, l3dss1_proceed_req}, - {SBIT(6) | SBIT(9), + {SBIT(6), + CC_MORE_INFO | REQUEST, l3dss1_setup_ack_req}, + {SBIT(25), + CC_MORE_INFO | REQUEST, l3dss1_dummy}, + {SBIT(6) | SBIT(9) | SBIT(25), CC_ALERTING | REQUEST, l3dss1_alert_req}, - {SBIT(6) | SBIT(7) | SBIT(9), + {SBIT(6) | SBIT(7) | SBIT(9) | SBIT(25), 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), + {SBIT(7) | SBIT(9) | SBIT(25), 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(25), + CC_T302, l3dss1_t302}, {SBIT(1), CC_T303, l3dss1_t303}, {SBIT(2), @@ -3180,7 +3145,7 @@ dss1down(struct PStack *st, int pr, void *arg) if ((proc = dss1_new_l3_process(st, cr))) { proc->chan = chan; chan->proc = proc; - proc->para.setup = chan->setup; + memcpy(&proc->para.setup, &chan->setup, sizeof(setup_parm)); proc->callref = cr; } } else { diff --git a/drivers/isdn/hisax/l3dss1.h b/drivers/isdn/hisax/l3dss1.h index 2acb95c01631..4913f17bd46e 100644 --- a/drivers/isdn/hisax/l3dss1.h +++ b/drivers/isdn/hisax/l3dss1.h @@ -1,39 +1,14 @@ -/* $Id: l3dss1.h,v 1.8 2000/01/20 19:46:15 keil Exp $ +/* $Id: l3dss1.h,v 1.10 2000/06/26 08:59:14 keil Exp $ * * DSS1 (Euro) D-channel protocol defines * - * $Log: l3dss1.h,v $ - * Revision 1.8 2000/01/20 19:46:15 keil - * Changes from certification - * - * 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 - * - * Revision 1.5 1998/02/02 13:34:30 keil - * Support australian Microlink net and german AOCD - * - * Revision 1.4 1997/10/29 19:07:54 keil - * changes for 2.1 - * - * Revision 1.3 1997/08/07 17:44:37 keil - * Fix RESTART - * - * Revision 1.2 1997/08/03 14:36:34 keil - * Implement RESTART procedure - * - * Revision 1.1 1997/07/27 21:08:38 keil - * new - * - * + * This file is (c) under GNU PUBLIC LICENSE * */ #ifndef l3dss1_process +#define T302 15000 #define T303 4000 #define T304 30000 #define T305 30000 diff --git a/drivers/isdn/hisax/l3ni1.c b/drivers/isdn/hisax/l3ni1.c new file mode 100644 index 000000000000..09efee2d0633 --- /dev/null +++ b/drivers/isdn/hisax/l3ni1.c @@ -0,0 +1,3172 @@ +// $Id: l3ni1.c,v 2.3 2000/06/26 08:59:14 keil Exp $ +//----------------------------------------------------------------------------- +// +// NI1 D-channel protocol +// +// Author Matt Henderson & Guy Ellis - Traverse Tecnologies Pty Ltd +// www.traverse.com.au +// +// 2000.6.6 Initial implementation of routines for US NI1 +// Layer 3 protocol based on the EURO/DSS1 D-channel protocol +// driver written by Karsten Keil et al. Thanks also for the +// code provided by Ragnar Paulson and Will Scales. +// +// This file is (c) under GNU PUBLIC LICENSE +// +//----------------------------------------------------------------------------- + +#define __NO_VERSION__ +#include "hisax.h" +#include "isdnl3.h" +#include "l3ni1.h" +#include +#include + +extern char *HiSax_getrev(const char *revision); +const char *ni1_revision = "$Revision: 2.3 $"; + +#define EXT_BEARER_CAPS 1 + +#define MsgHead(ptr, cref, mty) \ + *ptr++ = 0x8; \ + if (cref == -1) { \ + *ptr++ = 0x0; \ + } else { \ + *ptr++ = 0x1; \ + *ptr++ = cref^0x80; \ + } \ + *ptr++ = mty + + +/**********************************************/ +/* 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.ni1.last_invoke_id + 1; /* try new id */ + while ((i) && (p->prot.ni1.invoke_used[retval >> 3] == 0xFF)) { + p->prot.ni1.last_invoke_id = (retval & 0xF8) + 8; + i--; + } + if (i) { + while (p->prot.ni1.invoke_used[retval >> 3] & (1 << (retval & 7))) + retval++; + } else + retval = 0; + p->prot.ni1.last_invoke_id = retval; + p->prot.ni1.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.ni1.invoke_used[id >> 3] &= ~(1 << (id & 7)); + restore_flags(flags); +} /* free_invoke_id */ + + +/**********************************************************/ +/* create a new l3 process and fill in ni1 specific data */ +/**********************************************************/ +static struct l3_process +*ni1_new_l3_process(struct PStack *st, int cr) +{ struct l3_process *proc; + + if (!(proc = new_l3_process(st, cr))) + return(NULL); + + proc->prot.ni1.invoke_id = 0; + proc->prot.ni1.remote_operation = 0; + proc->prot.ni1.uus1_data[0] = '\0'; + + return(proc); +} /* ni1_new_l3_process */ + +/************************************************/ +/* free a l3 process and all ni1 specific data */ +/************************************************/ +static void +ni1_release_l3_process(struct l3_process *p) +{ + free_invoke_id(p->st,p->prot.ni1.invoke_id); + release_l3_process(p); +} /* ni1_release_l3_process */ + +/********************************************************/ +/* search a process with invoke id id and dummy callref */ +/********************************************************/ +static struct l3_process * +l3ni1_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.ni1.invoke_id == id)) + return(pc); + pc = pc->next; + } + return(NULL); +} /* l3ni1_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 +l3ni1_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 = l3ni1_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 = NI1_STAT_INVOKE_RES; + ic.parm.ni1_io.hl_id = pc->prot.ni1.invoke_id; + ic.parm.ni1_io.ll_id = pc->prot.ni1.ll_id; + ic.parm.ni1_io.proc = pc->prot.ni1.proc; + ic.parm.ni1_io.timeout= 0; + ic.parm.ni1_io.datalen = nlen; + ic.parm.ni1_io.data = p; + free_invoke_id(pc->st, pc->prot.ni1.invoke_id); + pc->prot.ni1.invoke_id = 0; /* reset id */ + + cs->iif.statcallb(&ic); + ni1_release_l3_process(pc); + } + else + l3_debug(st, "dummy return result id=0x%x result len=%d",id,nlen); +} /* l3ni1_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 +l3ni1_dummy_error_return(struct PStack *st, int id, ulong error) +{ isdn_ctrl ic; + struct IsdnCardState *cs; + struct l3_process *pc = NULL; + + if ((pc = l3ni1_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 = NI1_STAT_INVOKE_ERR; + ic.parm.ni1_io.hl_id = pc->prot.ni1.invoke_id; + ic.parm.ni1_io.ll_id = pc->prot.ni1.ll_id; + ic.parm.ni1_io.proc = pc->prot.ni1.proc; + ic.parm.ni1_io.timeout= error; + ic.parm.ni1_io.datalen = 0; + ic.parm.ni1_io.data = NULL; + free_invoke_id(pc->st, pc->prot.ni1.invoke_id); + pc->prot.ni1.invoke_id = 0; /* reset id */ + + cs->iif.statcallb(&ic); + ni1_release_l3_process(pc); + } + else + l3_debug(st, "dummy return error id=0x%x error=0x%lx",id,error); +} /* l3ni1_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 +l3ni1_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 = NI1_STAT_INVOKE_BRD; + ic.parm.ni1_io.hl_id = id; + ic.parm.ni1_io.ll_id = 0; + ic.parm.ni1_io.proc = ident; + ic.parm.ni1_io.timeout= 0; + ic.parm.ni1_io.datalen = nlen; + ic.parm.ni1_io.data = p; + + cs->iif.statcallb(&ic); +} /* l3ni1_dummy_invoke */ + +static void +l3ni1_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(st, "qd_len == 0"); + return; + } + if ((*p & 0x1F) != 0x11) { /* Service discriminator, supplementary service */ + l3_debug(st, "supplementary service != 0x11"); + return; + } + while (qd_len > 0 && !(*p & 0x80)) { /* extension ? */ + p++; + qd_len--; + } + if (qd_len < 2) { + l3_debug(st, "qd_len < 2"); + return; + } + p++; + qd_len--; + if ((*p & 0xE0) != 0xA0) { /* class and form */ + l3_debug(st, "class and form != 0xA0"); + return; + } + + 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(st, "nlen < 2 22"); + return; + } + if (*p != 0x02) { /* operation value */ + l3_debug(st, "operation value !=0x02"); + return; + } + p++; + nlen--; + ilen = *p++; + nlen--; + if (ilen > nlen || ilen == 0) { + l3_debug(st, "ilen > nlen || ilen == 0 22"); + return; + } + nlen -= ilen; + ident = 0; + while (ilen > 0) { + ident = (ident << 8) | (*p++ & 0xFF); + ilen--; + } + + if (!pc) + { + l3ni1_dummy_invoke(st, cr, id, ident, p, nlen); + return; + } + l3_debug(st, "invoke break"); + break; + case 2: /* return result */ + /* if no process available handle separately */ + if (!pc) + { if (cr == -1) + l3ni1_dummy_return_result(st, id, p, nlen); + return; + } + if ((pc->prot.ni1.invoke_id) && (pc->prot.ni1.invoke_id == id)) + { /* Diversion successfull */ + free_invoke_id(st,pc->prot.ni1.invoke_id); + pc->prot.ni1.remote_result = 0; /* success */ + pc->prot.ni1.invoke_id = 0; + pc->redir_result = pc->prot.ni1.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 */ + 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) + l3ni1_dummy_error_return(st, id, err_ret); + return; + } + if ((pc->prot.ni1.invoke_id) && (pc->prot.ni1.invoke_id == id)) + { /* Deflection error */ + free_invoke_id(st,pc->prot.ni1.invoke_id); + pc->prot.ni1.remote_result = err_ret; /* result */ + pc->prot.ni1.invoke_id = 0; + pc->redir_result = pc->prot.ni1.remote_result; + st->l3.l3l4(st, CC_REDIR | INDICATION, pc); + } /* Deflection error */ + else + l3_debug(st,"return result unknown identifier"); + break; + default: + l3_debug(st, "facility default break tag=0x%02x",cp_tag); + break; + } +} + +static void +l3ni1_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 +l3ni1_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 +l3ni1_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 +l3ni1_msg_without_setup(struct l3_process *pc, u_char pr, void *arg) +{ + /* This routine is called if here was no SETUP made (checks in ni1up and in + * l3ni1_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 l3ni1_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); + ni1_release_l3_process(pc); +} + +static int ie_ALERTING[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1, + IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, 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_SIGNAL, + IE_CONNECT_PN, IE_CONNECT_SUB, IE_LLC, IE_HLC, IE_USER_USER, -1}; +static int ie_CONNECT_ACKNOWLEDGE[] = {IE_CHANNEL_ID, IE_DISPLAY, IE_SIGNAL, -1}; +static int ie_DISCONNECT[] = {IE_CAUSE | IE_MANDATORY, IE_FACILITY, + IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, IE_USER_USER, -1}; +static int ie_INFORMATION[] = {IE_COMPLETE, IE_DISPLAY, IE_KEYPAD, IE_SIGNAL, + 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_SIGNAL, 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_SIGNAL, 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_SIGNAL, IE_CALLING_PN, + IE_CALLING_SUB, IE_CALLED_PN, IE_CALLED_SUB, IE_REDIR_NR, + IE_LLC, IE_HLC, IE_USER_USER, -1}; +static int ie_SETUP_ACKNOWLEDGE[] = {IE_CHANNEL_ID | IE_MANDATORY, IE_FACILITY, + IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, -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 ((*checklist & 0xff) == ie) { + if (ie & 0x80) + return(-ret); + else + return(ret); + } + ret++; + checklist++; + } + return(0); +} + +static int +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; + u_char codeset = 0; + u_char old_codeset = 0; + u_char codelock = 1; + + p = skb->data; + /* skip cr */ + p++; + l = (*p++) & 0xf; + p += l; + mt = *p++; + oldpos = 0; + while ((p - skb->data) < skb->len) { + if ((*p & 0xf0) == 0x90) { /* shift codeset */ + old_codeset = codeset; + codeset = *p & 7; + if (*p & 0x08) + codelock = 0; + else + codelock = 1; + if (pc->debug & L3_DEB_CHECK) + l3_debug(pc->st, "check IE shift%scodeset %d->%d", + codelock ? " locking ": " ", old_codeset, codeset); + p++; + continue; + } + if (!codeset) { /* only codeset 0 */ + 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 (!codeset && (l > getmax_ie_len(ie))) + err_len++; + if (!codelock) { + if (pc->debug & L3_DEB_CHECK) + l3_debug(pc->st, "check IE shift back codeset %d->%d", + codeset, old_codeset); + codeset = old_codeset; + codelock = 1; + } + } + if (err_compr | err_ureg | err_len | err_seq) { + if (pc->debug & L3_DEB_CHECK) + l3_debug(pc->st, "check IE 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 +l3ni1_check_messagetype_validity(struct l3_process *pc, int mt, void *arg) +{ + 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_ACKNOWLEDGE: + case MT_RESUME_REJECT: + case MT_SUSPEND_ACKNOWLEDGE: + case MT_SUSPEND_REJECT: + case MT_USER_INFORMATION: + case MT_RESTART: + case MT_RESTART_ACKNOWLEDGE: + case MT_CONGESTION_CONTROL: + case MT_STATUS: + case MT_STATUS_ENQUIRY: + if (pc->debug & L3_DEB_CHECK) + l3_debug(pc->st, "l3ni1_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, "l3ni1_check_messagetype_validity mt(%x) fail", mt); + pc->para.cause = 97; + l3ni1_status_send(pc, 0, NULL); + return(1); + } + return(0); +} + +static void +l3ni1_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; + l3ni1_status_send(pc, 0, NULL); + break; + case ERR_IE_UNRECOGNIZED: + pc->para.cause = 99; + l3ni1_status_send(pc, 0, NULL); + break; + case ERR_IE_LENGTH: + pc->para.cause = 100; + l3ni1_status_send(pc, 0, NULL); + break; + case ERR_IE_SEQUENCE: + default: + break; + } +} + +static int +l3ni1_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 +l3ni1_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 +l3ni1_msg_with_uus(struct l3_process *pc, u_char cmd) +{ + struct sk_buff *skb; + u_char tmp[16+40]; + u_char *p = tmp; + int l; + + MsgHead(p, pc->callref, cmd); + + if (pc->prot.ni1.uus1_data[0]) + { *p++ = IE_USER_USER; /* UUS info element */ + *p++ = strlen(pc->prot.ni1.uus1_data) + 1; + *p++ = 0x04; /* IA5 chars */ + strcpy(p,pc->prot.ni1.uus1_data); + p += strlen(pc->prot.ni1.uus1_data); + pc->prot.ni1.uus1_data[0] = '\0'; + } + + 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); +} /* l3ni1_msg_with_uus */ + +static void +l3ni1_release_req(struct l3_process *pc, u_char pr, void *arg) +{ + StopAllL3Timer(pc); + newl3state(pc, 19); + if (!pc->prot.ni1.uus1_data[0]) + l3ni1_message(pc, MT_RELEASE); + else + l3ni1_msg_with_uus(pc, MT_RELEASE); + L3AddTimer(&pc->timer, T308, CC_T308_1); +} + +static void +l3ni1_release_cmpl(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int ret; + + if ((ret = l3ni1_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); + newl3state(pc, 0); + pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc); + ni1_release_l3_process(pc); +} + +#if EXT_BEARER_CAPS + +static u_char * +EncodeASyncParams(u_char * p, u_char si2) +{ // 7c 06 88 90 21 42 00 bb + + p[0] = 0; + p[1] = 0x40; // Intermediate rate: 16 kbit/s jj 2000.02.19 + p[2] = 0x80; + if (si2 & 32) // 7 data bits + + p[2] += 16; + else // 8 data bits + + p[2] += 24; + + if (si2 & 16) // 2 stop bits + + p[2] += 96; + else // 1 stop bit + + p[2] += 32; + + if (si2 & 8) // even parity + + p[2] += 2; + else // no parity + + p[2] += 3; + + switch (si2 & 0x07) { + case 0: + p[0] = 66; // 1200 bit/s + + break; + case 1: + p[0] = 88; // 1200/75 bit/s + + break; + case 2: + p[0] = 87; // 75/1200 bit/s + + break; + case 3: + p[0] = 67; // 2400 bit/s + + break; + case 4: + p[0] = 69; // 4800 bit/s + + break; + case 5: + p[0] = 72; // 9600 bit/s + + break; + case 6: + p[0] = 73; // 14400 bit/s + + break; + case 7: + p[0] = 75; // 19200 bit/s + + break; + } + return p + 3; +} + +static u_char +EncodeSyncParams(u_char si2, u_char ai) +{ + + switch (si2) { + case 0: + return ai + 2; // 1200 bit/s + + case 1: + return ai + 24; // 1200/75 bit/s + + case 2: + return ai + 23; // 75/1200 bit/s + + case 3: + return ai + 3; // 2400 bit/s + + case 4: + return ai + 5; // 4800 bit/s + + case 5: + return ai + 8; // 9600 bit/s + + case 6: + return ai + 9; // 14400 bit/s + + case 7: + return ai + 11; // 19200 bit/s + + case 8: + return ai + 14; // 48000 bit/s + + case 9: + return ai + 15; // 56000 bit/s + + case 15: + return ai + 40; // negotiate bit/s + + default: + break; + } + return ai; +} + + +static u_char +DecodeASyncParams(u_char si2, u_char * p) +{ + u_char info; + + switch (p[5]) { + case 66: // 1200 bit/s + + break; // si2 don't change + + case 88: // 1200/75 bit/s + + si2 += 1; + break; + case 87: // 75/1200 bit/s + + si2 += 2; + break; + case 67: // 2400 bit/s + + si2 += 3; + break; + case 69: // 4800 bit/s + + si2 += 4; + break; + case 72: // 9600 bit/s + + si2 += 5; + break; + case 73: // 14400 bit/s + + si2 += 6; + break; + case 75: // 19200 bit/s + + si2 += 7; + break; + } + + info = p[7] & 0x7f; + if ((info & 16) && (!(info & 8))) // 7 data bits + + si2 += 32; // else 8 data bits + + if ((info & 96) == 96) // 2 stop bits + + si2 += 16; // else 1 stop bit + + if ((info & 2) && (!(info & 1))) // even parity + + si2 += 8; // else no parity + + return si2; +} + + +static u_char +DecodeSyncParams(u_char si2, u_char info) +{ + info &= 0x7f; + switch (info) { + case 40: // bit/s negotiation failed ai := 165 not 175! + + return si2 + 15; + case 15: // 56000 bit/s failed, ai := 0 not 169 ! + + return si2 + 9; + case 14: // 48000 bit/s + + return si2 + 8; + case 11: // 19200 bit/s + + return si2 + 7; + case 9: // 14400 bit/s + + return si2 + 6; + case 8: // 9600 bit/s + + return si2 + 5; + case 5: // 4800 bit/s + + return si2 + 4; + case 3: // 2400 bit/s + + return si2 + 3; + case 23: // 75/1200 bit/s + + return si2 + 2; + case 24: // 1200/75 bit/s + + return si2 + 1; + default: // 1200 bit/s + + return si2; + } +} + +static u_char +DecodeSI2(struct sk_buff *skb) +{ + u_char *p; //, *pend=skb->data + skb->len; + + if ((p = findie(skb->data, skb->len, 0x7c, 0))) { + switch (p[4] & 0x0f) { + case 0x01: + if (p[1] == 0x04) // sync. Bitratenadaption + + return DecodeSyncParams(160, p[5]); // V.110/X.30 + + else if (p[1] == 0x06) // async. Bitratenadaption + + return DecodeASyncParams(192, p); // V.110/X.30 + + break; + case 0x08: // if (p[5] == 0x02) // sync. Bitratenadaption + if (p[1] > 3) + return DecodeSyncParams(176, p[5]); // V.120 + break; + } + } + return 0; +} + +#endif + + +static void +l3ni1_setup_req(struct l3_process *pc, u_char pr, + void *arg) +{ + struct sk_buff *skb; + u_char tmp[128]; + u_char *p = tmp; + + u_char *teln; + u_char *sub; + u_char *sp; + int l; + + MsgHead(p, pc->callref, MT_SETUP); + + teln = pc->para.setup.phone; + + *p++ = 0xa1; /* complete indicator */ + /* + * Set Bearer Capability, Map info from 1TR6-convention to NI1 + */ + switch (pc->para.setup.si1) { + case 1: /* Telephony */ + *p++ = IE_BEARER; + *p++ = 0x3; /* Length */ + *p++ = 0x90; /* Coding Std. CCITT, 3.1 kHz audio */ + *p++ = 0x90; /* Circuit-Mode 64kbps */ + *p++ = 0xa3; /* A-Law Audio */ + break; + case 5: /* Datatransmission 64k, BTX */ + case 7: /* Datatransmission 64k */ + default: + *p++ = IE_BEARER; + *p++ = 0x2; /* Length */ + *p++ = 0x88; /* Coding Std. CCITT, unrestr. dig. Inform. */ + *p++ = 0x90; /* Circuit-Mode 64kbps */ + break; + } + + sub = NULL; + sp = teln; + while (*sp) { + if ('.' == *sp) { + sub = sp; + *sp = 0; + } else + sp++; + } + + *p++ = IE_KEYPAD; + *p++ = strlen(teln); + while (*teln) + *p++ = (*teln++) & 0x7F; + + if (sub) + *sub++ = '.'; + +#if EXT_BEARER_CAPS + if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175)) { // sync. Bitratenadaption, V.110/X.30 + + *p++ = IE_LLC; + *p++ = 0x04; + *p++ = 0x88; + *p++ = 0x90; + *p++ = 0x21; + *p++ = EncodeSyncParams(pc->para.setup.si2 - 160, 0x80); + } else if ((pc->para.setup.si2 >= 176) && (pc->para.setup.si2 <= 191)) { // sync. Bitratenadaption, V.120 + + *p++ = IE_LLC; + *p++ = 0x05; + *p++ = 0x88; + *p++ = 0x90; + *p++ = 0x28; + *p++ = EncodeSyncParams(pc->para.setup.si2 - 176, 0); + *p++ = 0x82; + } else if (pc->para.setup.si2 >= 192) { // async. Bitratenadaption, V.110/X.30 + + *p++ = IE_LLC; + *p++ = 0x06; + *p++ = 0x88; + *p++ = 0x90; + *p++ = 0x21; + p = EncodeASyncParams(p, pc->para.setup.si2 - 192); + } else { + switch (pc->para.setup.si1) { + case 1: /* Telephony */ + *p++ = IE_LLC; + *p++ = 0x3; /* Length */ + *p++ = 0x90; /* Coding Std. CCITT, 3.1 kHz audio */ + *p++ = 0x90; /* Circuit-Mode 64kbps */ + *p++ = 0xa3; /* A-Law Audio */ + break; + case 5: /* Datatransmission 64k, BTX */ + case 7: /* Datatransmission 64k */ + default: + *p++ = IE_LLC; + *p++ = 0x2; /* Length */ + *p++ = 0x88; /* Coding Std. CCITT, unrestr. dig. Inform. */ + *p++ = 0x90; /* Circuit-Mode 64kbps */ + break; + } + } +#endif + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) +{ + return; +} + memcpy(skb_put(skb, l), tmp, l); + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T303, CC_T303); + newl3state(pc, 1); + l3_msg(pc->st, DL_DATA | REQUEST, skb); +} + +static void +l3ni1_call_proc(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int id, ret; + + if ((id = l3ni1_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; + l3ni1_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; + l3ni1_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) { + l3ni1_std_ie_err(pc, ret); + return; + } + L3DelTimer(&pc->timer); + newl3state(pc, 3); + L3AddTimer(&pc->timer, T310, CC_T310); + if (ret) /* STATUS for none mandatory IE errors after actions are taken */ + l3ni1_std_ie_err(pc, ret); + pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc); +} + +static void +l3ni1_setup_ack(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int id, ret; + + if ((id = l3ni1_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; + l3ni1_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; + l3ni1_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) { + l3ni1_std_ie_err(pc, ret); + return; + } + L3DelTimer(&pc->timer); + newl3state(pc, 2); + L3AddTimer(&pc->timer, T304, CC_T304); + if (ret) /* STATUS for none mandatory IE errors after actions are taken */ + l3ni1_std_ie_err(pc, ret); + pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc); +} + +static void +l3ni1_disconnect(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + u_char *p; + int ret; + u_char cause = 0; + + StopAllL3Timer(pc); + if ((ret = l3ni1_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))) + l3ni1_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); + if (cause) + newl3state(pc, 19); + if (11 != ret) + pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc); + else if (!cause) + l3ni1_release_req(pc, pr, NULL); + if (cause) { + l3ni1_message_cause(pc, MT_RELEASE, cause); + L3AddTimer(&pc->timer, T308, CC_T308_1); + } +} + +static void +l3ni1_connect(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int ret; + + ret = check_infoelements(pc, skb, ie_CONNECT); + if (ERR_IE_COMPREHENSION == ret) { + l3ni1_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) + l3ni1_std_ie_err(pc, ret); + pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc); +} + +static void +l3ni1_alerting(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int ret; + + ret = check_infoelements(pc, skb, ie_ALERTING); + if (ERR_IE_COMPREHENSION == ret) { + l3ni1_std_ie_err(pc, ret); + return; + } + L3DelTimer(&pc->timer); /* T304 */ + newl3state(pc, 4); + if (ret) + l3ni1_std_ie_err(pc, ret); + pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc); +} + +static void +l3ni1_setup(struct l3_process *pc, u_char pr, void *arg) +{ + u_char *p; + int bcfound = 0; + char tmp[80]; + struct sk_buff *skb = arg; + int id; + int err = 0; + + /* + * Bearer Capabilities + */ + p = skb->data; + /* 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; + l3ni1_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; + l3ni1_msg_without_setup(pc, pr, NULL); + return; + } + /* + * Channel Identification + */ + if ((id = l3ni1_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; + l3ni1_msg_without_setup(pc, pr, NULL); + return; + } + bcfound++; + } 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 with wrong chid ret %d", id); + if (id == -1) + pc->para.cause = 96; + else + pc->para.cause = 100; + l3ni1_msg_without_setup(pc, pr, NULL); + return; + } + /* Now we are on none mandatory IEs */ + err = check_infoelements(pc, skb, ie_SETUP); + if (ERR_IE_COMPREHENSION == err) { + pc->para.cause = 96; + l3ni1_msg_without_setup(pc, pr, NULL); + return; + } + p = skb->data; + if ((p = findie(p, skb->len, 0x70, 0))) + iecpy(pc->para.setup.eazmsn, p, 1); + else + pc->para.setup.eazmsn[0] = 0; + + p = skb->data; + if ((p = findie(p, skb->len, 0x71, 0))) { + /* Called party subaddress */ + if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) { + tmp[0] = '.'; + iecpy(&tmp[1], p, 2); + strcat(pc->para.setup.eazmsn, tmp); + } else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "wrong called subaddress"); + } + p = skb->data; + if ((p = findie(p, skb->len, 0x6c, 0))) { + pc->para.setup.plan = p[2]; + if (p[2] & 0x80) { + iecpy(pc->para.setup.phone, p, 1); + pc->para.setup.screen = 0; + } else { + iecpy(pc->para.setup.phone, p, 2); + pc->para.setup.screen = p[3]; + } + } else { + pc->para.setup.phone[0] = 0; + pc->para.setup.plan = 0; + pc->para.setup.screen = 0; + } + p = skb->data; + if ((p = findie(p, skb->len, 0x6d, 0))) { + /* Calling party subaddress */ + if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) { + tmp[0] = '.'; + iecpy(&tmp[1], p, 2); + strcat(pc->para.setup.phone, tmp); + } else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "wrong calling subaddress"); + } + newl3state(pc, 6); + if (err) /* STATUS for none mandatory IE errors after actions are taken */ + l3ni1_std_ie_err(pc, err); + pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc); +} + +static void +l3ni1_reset(struct l3_process *pc, u_char pr, void *arg) +{ + ni1_release_l3_process(pc); +} + +static void +l3ni1_disconnect_req(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb; + u_char tmp[16+40]; + u_char *p = tmp; + int l; + u_char cause = 16; + + if (pc->para.cause != NO_CAUSE) + cause = pc->para.cause; + + StopAllL3Timer(pc); + + MsgHead(p, pc->callref, MT_DISCONNECT); + + *p++ = IE_CAUSE; + *p++ = 0x2; + *p++ = 0x80; + *p++ = cause | 0x80; + + if (pc->prot.ni1.uus1_data[0]) + { *p++ = IE_USER_USER; /* UUS info element */ + *p++ = strlen(pc->prot.ni1.uus1_data) + 1; + *p++ = 0x04; /* IA5 chars */ + strcpy(p,pc->prot.ni1.uus1_data); + p += strlen(pc->prot.ni1.uus1_data); + pc->prot.ni1.uus1_data[0] = '\0'; + } + + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + newl3state(pc, 11); + l3_msg(pc->st, DL_DATA | REQUEST, skb); + L3AddTimer(&pc->timer, T305, CC_T305); +} + +static void +l3ni1_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"); + l3ni1_disconnect_req(pc, pr, arg); + return; + } + newl3state(pc, 8); + l3ni1_message(pc, MT_CONNECT); + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T313, CC_T313); +} + +static void +l3ni1_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) { + l3ni1_std_ie_err(pc, ret); + return; + } + newl3state(pc, 10); + L3DelTimer(&pc->timer); + if (ret) + l3ni1_std_ie_err(pc, ret); + pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc); +} + +static void +l3ni1_reject_req(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb; + u_char tmp[16]; + u_char *p = tmp; + int l; + u_char cause = 21; + + if (pc->para.cause != NO_CAUSE) + cause = pc->para.cause; + + MsgHead(p, pc->callref, MT_RELEASE_COMPLETE); + + *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); + pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + newl3state(pc, 0); + ni1_release_l3_process(pc); +} + +static void +l3ni1_release(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + u_char *p; + int ret, cause=0; + + StopAllL3Timer(pc); + if ((ret = l3ni1_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))) { + l3ni1_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) + l3ni1_message_cause(pc, MT_RELEASE_COMPLETE, cause); + else + l3ni1_message(pc, MT_RELEASE_COMPLETE); + pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + newl3state(pc, 0); + ni1_release_l3_process(pc); +} + +static void +l3ni1_alert_req(struct l3_process *pc, u_char pr, + void *arg) +{ + newl3state(pc, 7); + if (!pc->prot.ni1.uus1_data[0]) + l3ni1_message(pc, MT_ALERTING); + else + l3ni1_msg_with_uus(pc, MT_ALERTING); +} + +static void +l3ni1_proceed_req(struct l3_process *pc, u_char pr, + void *arg) +{ + newl3state(pc, 9); + l3ni1_message(pc, MT_CALL_PROCEEDING); + pc->st->l3.l3l4(pc->st, CC_PROCEED_SEND | INDICATION, pc); +} + +static void +l3ni1_setup_ack_req(struct l3_process *pc, u_char pr, + void *arg) +{ + newl3state(pc, 25); + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T302, CC_T302); + l3ni1_message(pc, MT_SETUP_ACKNOWLEDGE); +} + +/********************************************/ +/* deliver a incoming display message to HL */ +/********************************************/ +static void +l3ni1_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); +} /* l3ni1_deliver_display */ + + +static void +l3ni1_progress(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_PROGRESS, 0))) { + if (p[1] != 2) { + err = 1; + pc->para.cause = 100; + } else if (!(p[2] & 0x70)) { + 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); + l3ni1_status_send(pc, pr, NULL); + return; + } + /* Now we are on none mandatory IEs */ + err = check_infoelements(pc, skb, ie_PROGRESS); + if (err) + l3ni1_std_ie_err(pc, err); + if (ERR_IE_COMPREHENSION != err) + pc->st->l3.l3l4(pc->st, CC_PROGRESS | INDICATION, pc); +} + +static void +l3ni1_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); + l3ni1_status_send(pc, pr, NULL); + return; + } + /* Now we are on none mandatory IEs */ + err = check_infoelements(pc, skb, ie_NOTIFY); + if (err) + l3ni1_std_ie_err(pc, err); + if (ERR_IE_COMPREHENSION != err) + pc->st->l3.l3l4(pc->st, CC_NOTIFY | INDICATION, pc); +} + +static void +l3ni1_status_enq(struct l3_process *pc, u_char pr, void *arg) +{ + int ret; + struct sk_buff *skb = arg; + + ret = check_infoelements(pc, skb, ie_STATUS_ENQUIRY); + l3ni1_std_ie_err(pc, ret); + pc->para.cause = 30; /* response to STATUS_ENQUIRY */ + l3ni1_status_send(pc, pr, NULL); +} + +static void +l3ni1_information(struct l3_process *pc, u_char pr, void *arg) +{ + int ret; + struct sk_buff *skb = arg; + u_char *p; + char tmp[32]; + + ret = check_infoelements(pc, skb, ie_INFORMATION); + if (ret) + l3ni1_std_ie_err(pc, ret); + if (pc->state == 25) { /* overlap receiving */ + L3DelTimer(&pc->timer); + p = skb->data; + if ((p = findie(p, skb->len, 0x70, 0))) { + iecpy(tmp, p, 1); + strcat(pc->para.setup.eazmsn, tmp); + pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc); + } + L3AddTimer(&pc->timer, T302, CC_T302); + } +} + +/******************************/ +/* handle deflection requests */ +/******************************/ +static void l3ni1_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.ni1.uus1_data,pc->chan->setup.eazmsn); /* copy uus element if available */ + if (!pc->chan->setup.phone[0]) + { pc->para.cause = -1; + l3ni1_disconnect_req(pc,pr,arg); /* disconnect immediately */ + return; + } /* only uus */ + + if (pc->prot.ni1.invoke_id) + free_invoke_id(pc->st,pc->prot.ni1.invoke_id); + + if (!(pc->prot.ni1.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.ni1.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; + memcpy(skb_put(skb, l), tmp, l); + + l3_msg(pc->st, DL_DATA | REQUEST, skb); +} /* l3ni1_redir_req */ + +/********************************************/ +/* handle deflection request in early state */ +/********************************************/ +static void l3ni1_redir_req_early(struct l3_process *pc, u_char pr, void *arg) +{ + l3ni1_proceed_req(pc,pr,arg); + l3ni1_redir_req(pc,pr,arg); +} /* l3ni1_redir_req_early */ + +/***********************************************/ +/* handle special commands for this protocol. */ +/* Examples are call independant services like */ +/* remote operations with dummy callref. */ +/***********************************************/ +static int l3ni1_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 NI1_CMD_INVOKE: + if (ic->parm.ni1_io.datalen < 0) return(-2); /* invalid parameter */ + + for (proc_len = 1, i = ic->parm.ni1_io.proc >> 8; i; i++) + i = i >> 8; /* add one byte */ + l = ic->parm.ni1_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.ni1_io.proc >> (i-1)) & 0xFF; + memcpy(p, ic->parm.ni1_io.data, ic->parm.ni1_io.datalen); /* copy data */ + l = (p - temp) + ic->parm.ni1_io.datalen; /* total length */ + + if (ic->parm.ni1_io.timeout > 0) + if (!(pc = ni1_new_l3_process(st, -1))) + { free_invoke_id(st, id); + return(-2); + } + pc->prot.ni1.ll_id = ic->parm.ni1_io.ll_id; /* remember id */ + pc->prot.ni1.proc = ic->parm.ni1_io.proc; /* and procedure */ + + if (!(skb = l3_alloc_skb(l))) + { free_invoke_id(st, id); + if (pc) ni1_release_l3_process(pc); + return(-2); + } + memcpy(skb_put(skb, l), temp, l); + + if (pc) + { pc->prot.ni1.invoke_id = id; /* remember id */ + L3AddTimer(&pc->timer, ic->parm.ni1_io.timeout, CC_TNI1_IO | REQUEST); + } + + l3_msg(st, DL_DATA | REQUEST, skb); + ic->parm.ni1_io.hl_id = id; /* return id */ + return(0); + + case NI1_CMD_INVOKE_ABORT: + if ((pc = l3ni1_search_dummy_proc(st, ic->parm.ni1_io.hl_id))) + { L3DelTimer(&pc->timer); /* remove timer */ + ni1_release_l3_process(pc); + return(0); + } + else + { l3_debug(st, "l3ni1_cmd_global abort unknown id"); + return(-2); + } + break; + + default: + l3_debug(st, "l3ni1_cmd_global unknown cmd 0x%lx", ic->arg); + return(-1); + } /* switch ic-> arg */ + return(-1); +} /* l3ni1_cmd_global */ + +static void +l3ni1_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 = NI1_STAT_INVOKE_ERR; + ic.parm.ni1_io.hl_id = pc->prot.ni1.invoke_id; + ic.parm.ni1_io.ll_id = pc->prot.ni1.ll_id; + ic.parm.ni1_io.proc = pc->prot.ni1.proc; + ic.parm.ni1_io.timeout= -1; + ic.parm.ni1_io.datalen = 0; + ic.parm.ni1_io.data = NULL; + free_invoke_id(pc->st, pc->prot.ni1.invoke_id); + pc->prot.ni1.invoke_id = 0; /* reset id */ + + cs->iif.statcallb(&ic); + + ni1_release_l3_process(pc); +} /* l3ni1_io_timer */ + +static void +l3ni1_release_ind(struct l3_process *pc, u_char pr, void *arg) +{ + u_char *p; + struct sk_buff *skb = arg; + int callState = 0; + p = skb->data; + + if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) { + p++; + if (1 == *p++) + callState = *p; + } + if (callState == 0) { + /* ETS 300-104 7.6.1, 8.6.1, 10.6.1... and 16.1 + * set down layer 3 without sending any message + */ + pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + newl3state(pc, 0); + ni1_release_l3_process(pc); + } else { + pc->st->l3.l3l4(pc->st, CC_IGNORE | INDICATION, pc); + } +} + +static void +l3ni1_dummy(struct l3_process *pc, u_char pr, void *arg) +{ +} + +static void +l3ni1_t302(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->para.loc = 0; + pc->para.cause = 28; /* invalid number */ + l3ni1_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); +} + +static void +l3ni1_t303(struct l3_process *pc, u_char pr, void *arg) +{ + if (pc->N303 > 0) { + pc->N303--; + L3DelTimer(&pc->timer); + l3ni1_setup_req(pc, pr, arg); + } else { + L3DelTimer(&pc->timer); + l3ni1_message_cause(pc, MT_RELEASE_COMPLETE, 102); + pc->st->l3.l3l4(pc->st, CC_NOSETUP_RSP, pc); + ni1_release_l3_process(pc); + } +} + +static void +l3ni1_t304(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->para.loc = 0; + pc->para.cause = 102; + l3ni1_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); + +} + +static void +l3ni1_t305(struct l3_process *pc, u_char pr, void *arg) +{ + u_char tmp[16]; + u_char *p = tmp; + int l; + struct sk_buff *skb; + u_char cause = 16; + + L3DelTimer(&pc->timer); + 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 | 0x80; + + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + newl3state(pc, 19); + l3_msg(pc->st, DL_DATA | REQUEST, skb); + L3AddTimer(&pc->timer, T308, CC_T308_1); +} + +static void +l3ni1_t310(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->para.loc = 0; + pc->para.cause = 102; + l3ni1_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); +} + +static void +l3ni1_t313(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->para.loc = 0; + pc->para.cause = 102; + l3ni1_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc); +} + +static void +l3ni1_t308_1(struct l3_process *pc, u_char pr, void *arg) +{ + newl3state(pc, 19); + L3DelTimer(&pc->timer); + l3ni1_message(pc, MT_RELEASE); + L3AddTimer(&pc->timer, T308, CC_T308_2); +} + +static void +l3ni1_t308_2(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc); + ni1_release_l3_process(pc); +} + +static void +l3ni1_t318(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->para.cause = 102; /* Timer expiry */ + pc->para.loc = 0; /* local */ + pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc); + newl3state(pc, 19); + l3ni1_message(pc, MT_RELEASE); + L3AddTimer(&pc->timer, T308, CC_T308_1); +} + +static void +l3ni1_t319(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->para.cause = 102; /* Timer expiry */ + pc->para.loc = 0; /* local */ + pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc); + newl3state(pc, 10); +} + +static void +l3ni1_restart(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + ni1_release_l3_process(pc); +} + +static void +l3ni1_status(struct l3_process *pc, u_char pr, void *arg) +{ + u_char *p; + struct sk_buff *skb = arg; + int ret; + u_char cause = 0, callState = 0; + + if ((ret = l3ni1_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; + if (!ie_in_set(pc, *p, l3_valid_states)) + cause = 100; + } else + cause = 100; + } else + 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; + l3ni1_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 == 111 and call + * state == 0, then we must set down layer 3 + */ + pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + newl3state(pc, 0); + ni1_release_l3_process(pc); + } +} + +static void +l3ni1_facility(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int ret; + + ret = check_infoelements(pc, skb, ie_FACILITY); + l3ni1_std_ie_err(pc, ret); + { + u_char *p; + if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) + l3ni1_parse_facility(pc->st, pc, pc->callref, p); + } +} + +static void +l3ni1_suspend_req(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb; + u_char tmp[32]; + u_char *p = tmp; + u_char i, l; + u_char *msg = pc->chan->setup.phone; + + MsgHead(p, pc->callref, MT_SUSPEND); + l = *msg++; + if (l && (l <= 10)) { /* Max length 10 octets */ + *p++ = IE_CALL_ID; + *p++ = l; + for (i = 0; i < l; i++) + *p++ = *msg++; + } else if (l) { + l3_debug(pc->st, "SUS wrong CALL_ID len %d", l); + return; + } + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + l3_msg(pc->st, DL_DATA | REQUEST, skb); + newl3state(pc, 15); + L3AddTimer(&pc->timer, T319, CC_T319); +} + +static void +l3ni1_suspend_ack(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int ret; + + L3DelTimer(&pc->timer); + newl3state(pc, 0); + pc->para.cause = NO_CAUSE; + pc->st->l3.l3l4(pc->st, CC_SUSPEND | CONFIRM, 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); + ni1_release_l3_process(pc); +} + +static void +l3ni1_suspend_rej(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int ret; + + if ((ret = l3ni1_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; + l3ni1_status_send(pc, pr, NULL); + return; + } + ret = check_infoelements(pc, skb, ie_SUSPEND_REJECT); + if (ERR_IE_COMPREHENSION == ret) { + l3ni1_std_ie_err(pc, ret); + return; + } + 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 */ + l3ni1_std_ie_err(pc, ret); +} + +static void +l3ni1_resume_req(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb; + u_char tmp[32]; + u_char *p = tmp; + u_char i, l; + u_char *msg = pc->para.setup.phone; + + MsgHead(p, pc->callref, MT_RESUME); + + l = *msg++; + if (l && (l <= 10)) { /* Max length 10 octets */ + *p++ = IE_CALL_ID; + *p++ = l; + for (i = 0; i < l; i++) + *p++ = *msg++; + } else if (l) { + l3_debug(pc->st, "RES wrong CALL_ID len %d", l); + return; + } + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + l3_msg(pc->st, DL_DATA | REQUEST, skb); + newl3state(pc, 17); + L3AddTimer(&pc->timer, T318, CC_T318); +} + +static void +l3ni1_resume_ack(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int id, ret; + + if ((id = l3ni1_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; + l3ni1_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; + l3ni1_status_send(pc, pr, NULL); + return; + } + ret = check_infoelements(pc, skb, ie_RESUME_ACKNOWLEDGE); + if (ERR_IE_COMPREHENSION == ret) { + l3ni1_std_ie_err(pc, ret); + return; + } + L3DelTimer(&pc->timer); + 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 */ + l3ni1_std_ie_err(pc, ret); +} + +static void +l3ni1_resume_rej(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int ret; + + if ((ret = l3ni1_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; + l3ni1_status_send(pc, pr, NULL); + return; + } + ret = check_infoelements(pc, skb, ie_RESUME_REJECT); + if (ERR_IE_COMPREHENSION == ret) { + l3ni1_std_ie_err(pc, ret); + return; + } + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc); + newl3state(pc, 0); + if (ret) /* STATUS for none mandatory IE errors after actions are taken */ + l3ni1_std_ie_err(pc, ret); + ni1_release_l3_process(pc); +} + +static void +l3ni1_global_restart(struct l3_process *pc, u_char pr, void *arg) +{ + u_char tmp[32]; + u_char *p; + u_char ri, ch = 0, chan = 0; + int l; + struct sk_buff *skb = arg; + struct l3_process *up; + + newl3state(pc, 2); + L3DelTimer(&pc->timer); + p = skb->data; + if ((p = findie(p, skb->len, IE_RESTART_IND, 0))) { + ri = p[2]; + l3_debug(pc->st, "Restart %x", ri); + } else { + l3_debug(pc->st, "Restart without restart IE"); + ri = 0x86; + } + p = skb->data; + if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { + chan = p[2] & 3; + ch = p[2]; + if (pc->st->l3.debug) + l3_debug(pc->st, "Restart for channel %d", chan); + } + newl3state(pc, 2); + up = pc->st->l3.proc; + while (up) { + if ((ri & 7) == 7) + up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up); + else if (up->para.bchannel == chan) + up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up); + up = up->next; + } + p = tmp; + MsgHead(p, pc->callref, MT_RESTART_ACKNOWLEDGE); + if (chan) { + *p++ = IE_CHANNEL_ID; + *p++ = 1; + *p++ = ch | 0x80; + } + *p++ = 0x79; /* RESTART Ind */ + *p++ = 1; + *p++ = ri; + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + newl3state(pc, 0); + l3_msg(pc->st, DL_DATA | REQUEST, skb); +} + +static void +l3ni1_dl_reset(struct l3_process *pc, u_char pr, void *arg) +{ + pc->para.cause = 0x29; /* Temporary failure */ + pc->para.loc = 0; + l3ni1_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); +} + +static void +l3ni1_dl_release(struct l3_process *pc, u_char pr, void *arg) +{ + newl3state(pc, 0); + pc->para.cause = 0x1b; /* Destination out of order */ + pc->para.loc = 0; + pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + release_l3_process(pc); +} + +static void +l3ni1_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 +l3ni1_dl_reest_status(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + + pc->para.cause = 0x1F; /* normal, unspecified */ + l3ni1_status_send(pc, 0, NULL); +} + +static void l3ni1_SendSpid( struct l3_process *pc, u_char pr, struct sk_buff *skb, int iNewState ) +{ + u_char * p; + char * pSPID; + struct Channel * pChan = pc->st->lli.userdata; + int l; + + if ( skb ) + dev_kfree_skb( skb); + + if ( !( pSPID = strchr( pChan->setup.eazmsn, ':' ) ) ) + { + printk( KERN_ERR "SPID not supplied in EAZMSN %s\n", pChan->setup.eazmsn ); + newl3state( pc, 0 ); + pc->st->l3.l3l2( pc->st, DL_RELEASE | REQUEST, NULL ); + return; + } + + l = strlen( ++pSPID ); + if ( !( skb = l3_alloc_skb( 5+l ) ) ) + { + printk( KERN_ERR "HiSax can't get memory to send SPID\n" ); + return; + } + + p = skb_put( skb, 5 ); + *p++ = PROTO_DIS_EURO; + *p++ = 0; + *p++ = MT_INFORMATION; + *p++ = IE_SPID; + *p++ = l; + + memcpy( skb_put( skb, l ), pSPID, l ); + + newl3state( pc, iNewState ); + + L3DelTimer( &pc->timer ); + L3AddTimer( &pc->timer, TSPID, CC_TSPID ); + + pc->st->l3.l3l2( pc->st, DL_DATA | REQUEST, skb ); +} + +static void l3ni1_spid_send( struct l3_process *pc, u_char pr, void *arg ) +{ + l3ni1_SendSpid( pc, pr, arg, 20 ); +} + +void l3ni1_spid_epid( struct l3_process *pc, u_char pr, void *arg ) +{ + struct sk_buff *skb = arg; + + if ( skb->data[ 1 ] == 0 ) + if ( skb->data[ 3 ] == IE_ENDPOINT_ID ) + { + L3DelTimer( &pc->timer ); + newl3state( pc, 0 ); + l3_msg( pc->st, DL_ESTABLISH | CONFIRM, NULL ); + } + dev_kfree_skb( skb); +} + +static void l3ni1_spid_tout( struct l3_process *pc, u_char pr, void *arg ) +{ + if ( pc->state < 22 ) + l3ni1_SendSpid( pc, pr, arg, pc->state+1 ); + else + { + L3DelTimer( &pc->timer ); + dev_kfree_skb( arg); + + printk( KERN_ERR "SPID not accepted\n" ); + newl3state( pc, 0 ); + pc->st->l3.l3l2( pc->st, DL_RELEASE | REQUEST, NULL ); + } +} + +/* *INDENT-OFF* */ +static struct stateentry downstatelist[] = +{ + {SBIT(0), + CC_SETUP | REQUEST, l3ni1_setup_req}, + {SBIT(0), + CC_RESUME | REQUEST, l3ni1_resume_req}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(25), + CC_DISCONNECT | REQUEST, l3ni1_disconnect_req}, + {SBIT(12), + CC_RELEASE | REQUEST, l3ni1_release_req}, + {ALL_STATES, + CC_RESTART | REQUEST, l3ni1_restart}, + {SBIT(6) | SBIT(25), + CC_IGNORE | REQUEST, l3ni1_reset}, + {SBIT(6) | SBIT(25), + CC_REJECT | REQUEST, l3ni1_reject_req}, + {SBIT(6) | SBIT(25), + CC_PROCEED_SEND | REQUEST, l3ni1_proceed_req}, + {SBIT(6), + CC_MORE_INFO | REQUEST, l3ni1_setup_ack_req}, + {SBIT(25), + CC_MORE_INFO | REQUEST, l3ni1_dummy}, + {SBIT(6) | SBIT(9) | SBIT(25), + CC_ALERTING | REQUEST, l3ni1_alert_req}, + {SBIT(6) | SBIT(7) | SBIT(9) | SBIT(25), + CC_SETUP | RESPONSE, l3ni1_setup_rsp}, + {SBIT(10), + CC_SUSPEND | REQUEST, l3ni1_suspend_req}, + {SBIT(7) | SBIT(9) | SBIT(25), + CC_REDIR | REQUEST, l3ni1_redir_req}, + {SBIT(6), + CC_REDIR | REQUEST, l3ni1_redir_req_early}, + {SBIT(9) | SBIT(25), + CC_DISCONNECT | REQUEST, l3ni1_disconnect_req}, + {SBIT(25), + CC_T302, l3ni1_t302}, + {SBIT(1), + CC_T303, l3ni1_t303}, + {SBIT(2), + CC_T304, l3ni1_t304}, + {SBIT(3), + CC_T310, l3ni1_t310}, + {SBIT(8), + CC_T313, l3ni1_t313}, + {SBIT(11), + CC_T305, l3ni1_t305}, + {SBIT(15), + CC_T319, l3ni1_t319}, + {SBIT(17), + CC_T318, l3ni1_t318}, + {SBIT(19), + CC_T308_1, l3ni1_t308_1}, + {SBIT(19), + CC_T308_2, l3ni1_t308_2}, + {SBIT(10), + CC_T309, l3ni1_dl_release}, + { SBIT( 20 ) | SBIT( 21 ) | SBIT( 22 ), + CC_TSPID, l3ni1_spid_tout }, +}; + +#define DOWNSLLEN \ + (sizeof(downstatelist) / sizeof(struct stateentry)) + +static struct stateentry datastatelist[] = +{ + {ALL_STATES, + MT_STATUS_ENQUIRY, l3ni1_status_enq}, + {ALL_STATES, + MT_FACILITY, l3ni1_facility}, + {SBIT(19), + MT_STATUS, l3ni1_release_ind}, + {ALL_STATES, + MT_STATUS, l3ni1_status}, + {SBIT(0), + MT_SETUP, l3ni1_setup}, + {SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) | + SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25), + MT_SETUP, l3ni1_dummy}, + {SBIT(1) | SBIT(2), + MT_CALL_PROCEEDING, l3ni1_call_proc}, + {SBIT(1), + MT_SETUP_ACKNOWLEDGE, l3ni1_setup_ack}, + {SBIT(2) | SBIT(3), + MT_ALERTING, l3ni1_alerting}, + {SBIT(2) | SBIT(3), + MT_PROGRESS, l3ni1_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, l3ni1_information}, + {SBIT(10) | SBIT(11) | SBIT(15), + MT_NOTIFY, l3ni1_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(25), + MT_RELEASE_COMPLETE, l3ni1_release_cmpl}, + {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, l3ni1_release}, + {SBIT(19), MT_RELEASE, l3ni1_release_ind}, + {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, l3ni1_disconnect}, + {SBIT(19), + MT_DISCONNECT, l3ni1_dummy}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4), + MT_CONNECT, l3ni1_connect}, + {SBIT(8), + MT_CONNECT_ACKNOWLEDGE, l3ni1_connect_ack}, + {SBIT(15), + MT_SUSPEND_ACKNOWLEDGE, l3ni1_suspend_ack}, + {SBIT(15), + MT_SUSPEND_REJECT, l3ni1_suspend_rej}, + {SBIT(17), + MT_RESUME_ACKNOWLEDGE, l3ni1_resume_ack}, + {SBIT(17), + MT_RESUME_REJECT, l3ni1_resume_rej}, +}; + +#define DATASLLEN \ + (sizeof(datastatelist) / sizeof(struct stateentry)) + +static struct stateentry globalmes_list[] = +{ + {ALL_STATES, + MT_STATUS, l3ni1_status}, + {SBIT(0), + MT_RESTART, l3ni1_global_restart}, +/* {SBIT(1), + MT_RESTART_ACKNOWLEDGE, l3ni1_restart_ack}, +*/ + { SBIT( 0 ), MT_DL_ESTABLISHED, l3ni1_spid_send }, + { SBIT( 20 ) | SBIT( 21 ) | SBIT( 22 ), MT_INFORMATION, l3ni1_spid_epid }, +}; +#define GLOBALM_LEN \ + (sizeof(globalmes_list) / sizeof(struct stateentry)) + +static struct stateentry manstatelist[] = +{ + {SBIT(2), + DL_ESTABLISH | INDICATION, l3ni1_dl_reset}, + {SBIT(10), + DL_ESTABLISH | CONFIRM, l3ni1_dl_reest_status}, + {SBIT(10), + DL_RELEASE | INDICATION, l3ni1_dl_reestablish}, + {ALL_STATES, + DL_RELEASE | INDICATION, l3ni1_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; + + if ( skb ) + proc->callref = skb->data[2]; /* cr flag */ + else + proc->callref = 0; + 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) { + if (st->l3.debug & L3_DEB_STATE) { + l3_debug(st, "ni1 global state %d mt %x unhandled", + proc->state, mt); + } + 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, "ni1 global %d mt %x", + proc->state, mt); + } + globalmes_list[i].rout(proc, mt, skb); + } +} + +static void +ni1up(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; + + switch (pr) { + case (DL_DATA | INDICATION): + case (DL_UNIT_DATA | INDICATION): + break; + case (DL_ESTABLISH | INDICATION): + case (DL_RELEASE | INDICATION): + case (DL_RELEASE | CONFIRM): + l3_msg(st, pr, arg); + return; + break; + + case (DL_ESTABLISH | CONFIRM): + global_handler( st, MT_DL_ESTABLISHED, NULL ); + return; + + default: + printk(KERN_ERR "HiSax ni1up unknown pr=%04x\n", pr); + return; + } + if (skb->len < 3) { + l3_debug(st, "ni1up frame too short(%d)", skb->len); + dev_kfree_skb(skb); + return; + } + + if (skb->data[0] != PROTO_DIS_EURO) { + if (st->l3.debug & L3_DEB_PROTERR) { + l3_debug(st, "ni1up%sunexpected discriminator %x message len %d", + (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", + skb->data[0], skb->len); + } + dev_kfree_skb(skb); + return; + } + cr = getcallref(skb->data); + if (skb->len < ((skb->data[1] & 0x0f) + 3)) { + l3_debug(st, "ni1up frame too short(%d)", skb->len); + dev_kfree_skb(skb); + return; + } + mt = skb->data[skb->data[1] + 2]; + if (st->l3.debug & L3_DEB_STATE) + l3_debug(st, "ni1up cr %d", cr); + if (cr == -2) { /* wrong Callref */ + if (st->l3.debug & L3_DEB_WARN) + l3_debug(st, "ni1up wrong Callref"); + dev_kfree_skb(skb); + return; + } else if (cr == -1) { /* Dummy Callref */ + if (mt == MT_FACILITY) + { + if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) { + l3ni1_parse_facility(st, NULL, + (pr == (DL_DATA | INDICATION)) ? -1 : -2, p); + dev_kfree_skb(skb); + return; + } + } + else + { + global_handler(st, mt, skb); + return; + } + + if (st->l3.debug & L3_DEB_WARN) + l3_debug(st, "ni1up dummy Callref (no facility msg or ie)"); + dev_kfree_skb(skb); + 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, "ni1up Global CallRef"); + global_handler(st, mt, skb); + dev_kfree_skb(skb); + return; + } else if (!(proc = getl3proc(st, cr))) { + /* No transaction process exist, that means no call with + * this callreference is active + */ + if (mt == MT_SETUP) { + /* Setup creates a new transaction process */ + if (skb->data[2] & 0x80) { + /* Setup with wrong CREF flag */ + if (st->l3.debug & L3_DEB_STATE) + l3_debug(st, "ni1up wrong CRef flag"); + dev_kfree_skb(skb); + return; + } + if (!(proc = ni1_new_l3_process(st, cr))) { + /* May be to answer with RELEASE_COMPLETE and + * CAUSE 0x2f "Resource unavailable", but this + * need a new_l3_process too ... arghh + */ + dev_kfree_skb(skb); + return; + } + } else if (mt == MT_STATUS) { + cause = 0; + if ((ptr = findie(skb->data, skb->len, IE_CAUSE, 0)) != NULL) { + ptr++; + if (*ptr++ == 2) + ptr++; + cause = *ptr & 0x7f; + } + callState = 0; + if ((ptr = findie(skb->data, skb->len, IE_CALL_STATE, 0)) != NULL) { + ptr++; + if (*ptr++ == 2) + ptr++; + callState = *ptr; + } + /* 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 + */ + if ((proc = ni1_new_l3_process(st, cr))) { + proc->para.cause = 101; + l3ni1_msg_without_setup(proc, 0, NULL); + } + } + dev_kfree_skb(skb); + return; + } else if (mt == MT_RELEASE_COMPLETE) { + dev_kfree_skb(skb); + return; + } else { + /* ETS 300-104 part 2 + * if setup has not been made and a message type + * (except MT_SETUP and RELEASE_COMPLETE) is received, + * we must send MT_RELEASE_COMPLETE cause 81 */ + dev_kfree_skb(skb); + if ((proc = ni1_new_l3_process(st, cr))) { + proc->para.cause = 81; + l3ni1_msg_without_setup(proc, 0, NULL); + } + return; + } + } + if (l3ni1_check_messagetype_validity(proc, mt, skb)) { + dev_kfree_skb(skb); + return; + } + if ((p = findie(skb->data, skb->len, IE_DISPLAY, 0)) != NULL) + l3ni1_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) { + if (st->l3.debug & L3_DEB_STATE) { + l3_debug(st, "ni1up%sstate %d mt %#x unhandled", + (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", + proc->state, mt); + } + if ((MT_RELEASE_COMPLETE != mt) && (MT_RELEASE != mt)) { + proc->para.cause = 101; + l3ni1_status_send(proc, pr, skb); + } + } else { + if (st->l3.debug & L3_DEB_STATE) { + l3_debug(st, "ni1up%sstate %d mt %x", + (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", + proc->state, mt); + } + datastatelist[i].rout(proc, pr, skb); + } + dev_kfree_skb(skb); + return; +} + +static void +ni1down(struct PStack *st, int pr, void *arg) +{ + int i, cr; + struct l3_process *proc; + struct Channel *chan; + + 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 = ni1_new_l3_process(st, cr))) { + proc->chan = chan; + chan->proc = proc; + memcpy(&proc->para.setup, &chan->setup, sizeof(setup_parm)); + proc->callref = cr; + } + } else { + proc = arg; + } + if (!proc) { + printk(KERN_ERR "HiSax ni1down without proc pr=%04x\n", pr); + return; + } + + if ( pr == (CC_TNI1_IO | REQUEST)) { + l3ni1_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, "ni1down state %d prim %#x unhandled", + proc->state, pr); + } + } else { + if (st->l3.debug & L3_DEB_STATE) { + l3_debug(st, "ni1down state %d prim %#x", + proc->state, pr); + } + downstatelist[i].rout(proc, pr, arg); + } +} + +static void +ni1man(struct PStack *st, int pr, void *arg) +{ + int i; + struct l3_process *proc = arg; + + if (!proc) { + printk(KERN_ERR "HiSax ni1man 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 ni1man state %d prim %#x unhandled", + proc->callref & 0x7f, proc->state, pr); + } + } else { + if (st->l3.debug & L3_DEB_STATE) { + l3_debug(st, "cr %d ni1man state %d prim %#x", + proc->callref & 0x7f, proc->state, pr); + } + manstatelist[i].rout(proc, pr, arg); + } +} + +void +setstack_ni1(struct PStack *st) +{ + char tmp[64]; + int i; + + st->lli.l4l3 = ni1down; + st->lli.l4l3_proto = l3ni1_cmd_global; + st->l2.l2l3 = ni1up; + st->l3.l3ml3 = ni1man; + st->l3.N303 = 1; + st->prot.ni1.last_invoke_id = 0; + st->prot.ni1.invoke_used[0] = 1; /* Bit 0 must always be set to 1 */ + i = 1; + while (i < 32) + st->prot.ni1.invoke_used[i++] = 0; + + if (!(st->l3.global = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) { + printk(KERN_ERR "HiSax can't get memory for ni1 global CR\n"); + } else { + st->l3.global->state = 0; + st->l3.global->callref = 0; + st->l3.global->next = NULL; + st->l3.global->debug = L3_DEB_WARN; + st->l3.global->st = st; + st->l3.global->N303 = 1; + st->l3.global->prot.ni1.invoke_id = 0; + + L3InitTimer(st->l3.global, &st->l3.global->timer); + } + strcpy(tmp, ni1_revision); + printk(KERN_INFO "HiSax: National ISDN-1 Rev. %s\n", HiSax_getrev(tmp)); +} diff --git a/drivers/isdn/hisax/l3ni1.h b/drivers/isdn/hisax/l3ni1.h new file mode 100644 index 000000000000..24c919a1762b --- /dev/null +++ b/drivers/isdn/hisax/l3ni1.h @@ -0,0 +1,136 @@ +// $Id: l3ni1.h,v 2.2 2000/06/26 08:59:14 keil Exp $ +//----------------------------------------------------------------------------- +// +// NI1 D-channel protocol +// +// Author Matt Henderson & Guy Ellis - Traverse Tecnologies Pty Ltd +// www.traverse.com.au +// +// 2000.6.6 Initial implementation of routines for US NI1 +// Layer 3 protocol based on the EURO/DSS1 D-channel protocol +// driver written by Karsten Keil et al. Thanks also for the +// code provided by Ragnar Paulson. +// +// This file is (c) under GNU PUBLIC LICENSE +// +//----------------------------------------------------------------------------- + +#ifndef l3ni1_process + +#define T302 15000 +#define T303 4000 +#define T304 30000 +#define T305 30000 +#define T308 4000 +/* for layer 1 certification T309 < layer1 T3 (e.g. 4000) */ +/* This makes some tests easier and quicker */ +#define T309 40000 +#define T310 30000 +#define T313 4000 +#define T318 4000 +#define T319 4000 +#define TSPID 2000 + +/* + * 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_DL_ESTABLISHED 0xfe + +#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_SPID 0x3a +#define IE_ENDPOINT_ID 0x3b +#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 l3ni1_process */ + +/* l3ni1 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 */ + } ni1_proc_priv; + +/* l3dni1 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 */ + } ni1_stk_priv; + +#endif /* only l3dni1_process */ diff --git a/drivers/isdn/hisax/lmgr.c b/drivers/isdn/hisax/lmgr.c index 6097fe54797c..99afed2e6ba6 100644 --- a/drivers/isdn/hisax/lmgr.c +++ b/drivers/isdn/hisax/lmgr.c @@ -1,30 +1,10 @@ -/* $Id: lmgr.c,v 1.6 1999/07/01 08:12:04 keil Exp $ - - * Author Karsten Keil (keil@isdn4linux.de) +/* $Id: lmgr.c,v 1.7 2000/06/26 08:59:14 keil Exp $ * + * Author Karsten Keil (keil@isdn4linux.de) * * 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 - * - * Revision 1.4 1998/05/25 12:58:19 keil - * HiSax golden code from certification, Don't use !!! - * No leased lines, no X75, but many changes. - * - * Revision 1.3 1998/03/07 22:57:06 tsbogend - * made HiSax working on Linux/Alpha - * - * Revision 1.2 1997/10/29 19:09:34 keil - * new L1 - * - * Revision 1.1 1997/06/26 11:17:25 keil - * first version - * + * This file is (c) under GNU PUBLIC LICENSE * */ diff --git a/drivers/isdn/hisax/md5sums.asc b/drivers/isdn/hisax/md5sums.asc index 167ea322721b..f135d5e7ca7f 100644 --- a/drivers/isdn/hisax/md5sums.asc +++ b/drivers/isdn/hisax/md5sums.asc @@ -2,30 +2,32 @@ # 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 and -# Eicon Technology Diva 2.01 PCI cards in the moment. +# The certification is valid only for ELSA Microlink PCI, +# Eicon Technology Diva 2.01 PCI and Sedlbauer SpeedFax + +# cards in the moment. # Read ../../../Documentation/isdn/HiSax.cert for more informations. # -3c2b1c96274cba97a8261d1cecc662b8 isac.c -dd3955847bbf680b41233478fe521d88 isdnl1.c -bb51bd223040b511c18f091da5ab6456 isdnl2.c -b7aa7f97b2374967a4aca7c52991142c isdnl3.c -a23fbf8879c1432b04640b8b04bdf419 tei.c -ce248e56c2e1326012d0b25f92bbf99b callc.c -bf9605b36429898f7be6630034e83230 cert.c -6ce0a184127be1a44747e2017ed24ad9 l3dss1.c -a3a570781f828b6d59e6b231653133de l3_1tr6.c -8188deeb4a1b34c574cd51638cd214d0 elsa.c -a3c2b8e9d2c623658888b5f1d4317c2a diva.c +4131693d878d465faf0a4a4b4c6b811e isac.c +a29f5270c0c89626d8d6fa5dd09e7005 isdnl1.c +fbe41751c8130a8c3c607bfe1b41cb4e isdnl2.c +7915b7e802b98f6f4f05b931c4736ad4 isdnl3.c +7c31c12b3c2cfde33596bd2c406f775c tei.c +f1fbd532016f005e01decf36e5197d8f callc.c +a1834e9b2ec068440cff2e899eff4710 cert.c +a1f908f8b4f225c5c2f2a13842549b72 l3dss1.c +3d0a3f88fe48f3151813fafb22ea6119 l3_1tr6.c +6f3420dca0f50084f82471a238545a95 elsa.c +65da7b33bbfa454df2f99636b83f0610 diva.c +4bdb7e35627b7e25f17fccb669fda835 sedlbauer.c # end of md5sums -----BEGIN PGP SIGNATURE----- Version: 2.6.3i Charset: noconv -iQCVAwUBONEHHTpxHvX/mS9tAQG/pAP/UyLx23V9mOgiYaId1Gm6xthyM8TP4bfE -ov4cNmUHQtHINLXtkAP+eacw/kCiiYhdxBIrNaUC+KQkyiFPOs8frx6espxTfDte -VZDhKDY5NYch2n3OKPLX26pNNJeu7DELMpFR6vvGByokv1RRYVs2wBk0Q5dnfPaq -WsW0HeL/vBA= -=fSCb +iQCVAwUBOVctcDpxHvX/mS9tAQFOhAP+OpOSxDz46cEHjy4jLsYz44/yKZzFAXz4 +s0tYmixrFbbS5XoT1GZqzEF0n/EycO9jsp6d0eanDCg25UX7ehu9dtOJw0o6qRr2 +4M/EbloHK2G1aW4gI8W2eWRNRqTZQJ2cjPJD/V/27jDbxBBP31NvOSYvVxrbBzJG +qZHWUDiQxRo= +=mYrg -----END PGP SIGNATURE----- diff --git a/drivers/isdn/hisax/mic.c b/drivers/isdn/hisax/mic.c index be65f1101ca0..db5d2ee46020 100644 --- a/drivers/isdn/hisax/mic.c +++ b/drivers/isdn/hisax/mic.c @@ -1,38 +1,12 @@ -/* $Id: mic.c,v 1.8 1999/07/12 21:05:20 keil Exp $ - +/* $Id: mic.c,v 1.9 2000/06/26 08:59:14 keil Exp $ + * * mic.c low level stuff for mic cards * * Copyright (C) 1997 * * Author Stephan von Krawczynski * - * - * $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 - * - * Revision 1.6 1998/02/17 15:39:57 keil - * fix reset problem - * - * Revision 1.5 1998/02/02 13:29:43 keil - * fast io - * - * Revision 1.4 1997/11/08 21:35:51 keil - * new l1 init - * - * Revision 1.3 1997/11/06 17:09:11 keil - * New 2.1 init code - * - * Revision 1.2 1997/10/29 18:51:17 keil - * New files - * - * Revision 1.1.2.1 1997/10/17 22:10:54 keil - * new files on 2.0 - * + * This file is (c) under GNU PUBLIC LICENSE * */ @@ -44,7 +18,7 @@ extern const char *CardType[]; -const char *mic_revision = "$Revision: 1.8 $"; +const char *mic_revision = "$Revision: 1.9 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) diff --git a/drivers/isdn/hisax/netjet.c b/drivers/isdn/hisax/netjet.c index b2598d2661ec..6568ab41f80e 100644 --- a/drivers/isdn/hisax/netjet.c +++ b/drivers/isdn/hisax/netjet.c @@ -1,67 +1,12 @@ -/* $Id: netjet.c,v 1.17 1999/12/19 13:09:42 keil Exp $ - +/* $Id: netjet.c,v 1.20 2000/06/26 08:59:14 keil Exp $ + * * netjet.c low level stuff for Traverse Technologie NETJet ISDN cards * * Author Karsten Keil (keil@isdn4linux.de) * * Thanks to Traverse Technologie Australia for documents and informations * - * $Log: netjet.c,v $ - * Revision 1.17 1999/12/19 13:09:42 keil - * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for - * signal proof delays - * - * Revision 1.16 1999/10/14 20:25:29 keil - * add a statistic for error monitoring - * - * Revision 1.15 1999/09/04 06:20:06 keil - * Changes from kernel set_current_state() - * - * Revision 1.14 1999/08/31 11:20:25 paul - * various spelling corrections (new checksums may be needed, Karsten!) - * - * 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 - * - * Revision 1.7 1998/09/30 22:24:48 keil - * Fix missing line in setstack* - * - * Revision 1.6 1998/08/13 23:36:54 keil - * HiSax 3.1 - don't work stable with current LinkLevel - * - * Revision 1.5 1998/05/25 12:58:21 keil - * HiSax golden code from certification, Don't use !!! - * No leased lines, no X75, but many changes. - * - * Revision 1.4 1998/04/15 16:42:35 keil - * new init code - * new PCI init (2.1.94) - * - * Revision 1.3 1998/02/12 23:08:05 keil - * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() - * - * Revision 1.2 1998/02/02 13:32:06 keil - * New - * - * + * This file is (c) under GNU PUBLIC LICENSE * */ @@ -74,6 +19,7 @@ #include #include #include +#include "netjet.h" #ifndef bus_to_virt #define bus_to_virt (u_int *) @@ -83,61 +29,12 @@ #define virt_to_bus (u_int) #endif -extern const char *CardType[]; - -const char *NETjet_revision = "$Revision: 1.17 $"; - -#define byteout(addr,val) outb(val,addr) -#define bytein(addr) inb(addr) - -/* PCI stuff */ -#define PCI_VENDOR_TRAVERSE_TECH 0xe159 -#define PCI_NETJET_ID 0x0001 - -#define NETJET_CTRL 0x00 -#define NETJET_DMACTRL 0x01 -#define NETJET_AUXCTRL 0x02 -#define NETJET_AUXDATA 0x03 -#define NETJET_IRQMASK0 0x04 -#define NETJET_IRQMASK1 0x05 -#define NETJET_IRQSTAT0 0x06 -#define NETJET_IRQSTAT1 0x07 -#define NETJET_DMA_READ_START 0x08 -#define NETJET_DMA_READ_IRQ 0x0c -#define NETJET_DMA_READ_END 0x10 -#define NETJET_DMA_READ_ADR 0x14 -#define NETJET_DMA_WRITE_START 0x18 -#define NETJET_DMA_WRITE_IRQ 0x1c -#define NETJET_DMA_WRITE_END 0x20 -#define NETJET_DMA_WRITE_ADR 0x24 -#define NETJET_PULSE_CNT 0x28 - -#define NETJET_ISAC_OFF 0xc0 -#define NETJET_ISACIRQ 0x10 -#define NETJET_IRQM0_READ 0x0c -#define NETJET_IRQM0_READ_1 0x04 -#define NETJET_IRQM0_READ_2 0x08 -#define NETJET_IRQM0_WRITE 0x03 -#define NETJET_IRQM0_WRITE_1 0x01 -#define NETJET_IRQM0_WRITE_2 0x02 - -#define NETJET_DMA_TXSIZE 512 -#define NETJET_DMA_RXSIZE 128 - -#define HDLC_ZERO_SEARCH 0 -#define HDLC_FLAG_SEARCH 1 -#define HDLC_FLAG_FOUND 2 -#define HDLC_FRAME_FOUND 3 -#define HDLC_NULL 4 -#define HDLC_PART 5 -#define HDLC_FULL 6 - -#define HDLC_FLAG_VALUE 0x7e +const char *NETjet_revision = "$Revision: 1.20 $"; /* Interface functions */ -static u_char -ReadISAC(struct IsdnCardState *cs, u_char offset) +u_char +NETjet_ReadIC(struct IsdnCardState *cs, u_char offset) { long flags; u_char ret; @@ -152,8 +49,8 @@ ReadISAC(struct IsdnCardState *cs, u_char offset) return(ret); } -static void -WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +void +NETjet_WriteIC(struct IsdnCardState *cs, u_char offset, u_char value) { long flags; @@ -166,8 +63,8 @@ WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) restore_flags(flags); } -static void -ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size) +void +NETjet_ReadICfifo(struct IsdnCardState *cs, u_char *data, int size) { cs->hw.njet.auxd &= 0xfc; byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); @@ -210,8 +107,8 @@ __u16 fcstab[256] = 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 }; -static void -WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size) +void +NETjet_WriteICfifo(struct IsdnCardState *cs, u_char *data, int size) { cs->hw.njet.auxd &= 0xfc; byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); @@ -297,15 +194,6 @@ mode_tiger(struct BCState *bcs, int mode, int bc) bytein(cs->hw.njet.base + NETJET_PULSE_CNT)); } -static u_char dummyrr(struct IsdnCardState *cs, int chan, u_char off) -{ - return(5); -} - -static void dummywr(struct IsdnCardState *cs, int chan, u_char off, u_char value) -{ -} - static void printframe(struct IsdnCardState *cs, u_char *buf, int count, char *s) { char tmp[128]; char *t = tmp; @@ -596,7 +484,7 @@ static void read_raw(struct BCState *bcs, u_int *buf, int cnt){ bcs->hw.tiger.r_bitcnt = bitcnt; } -static void read_tiger(struct IsdnCardState *cs) { +void read_tiger(struct IsdnCardState *cs) { u_int *p; int cnt = NETJET_DMA_RXSIZE/2; @@ -627,7 +515,7 @@ static void read_tiger(struct IsdnCardState *cs) { static void write_raw(struct BCState *bcs, u_int *buf, int cnt); -static void fill_dma(struct BCState *bcs) +void netjet_fill_dma(struct BCState *bcs) { register u_int *p, *sp; register int cnt; @@ -742,7 +630,7 @@ static void write_raw(struct BCState *bcs, u_int *buf, int cnt) { test_and_set_bit(BC_FLG_NOFRAME, &bcs->Flag); } if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { - fill_dma(bcs); + netjet_fill_dma(bcs); } else { mask ^= 0xffffffff; if (s_cnt < cnt) { @@ -774,7 +662,7 @@ static void write_raw(struct BCState *bcs, u_int *buf, int cnt) { } } -static void write_tiger(struct IsdnCardState *cs) { +void write_tiger(struct IsdnCardState *cs) { u_int *p, cnt = NETJET_DMA_TXSIZE/2; if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_WRITE) { @@ -990,88 +878,6 @@ releasetiger(struct IsdnCardState *cs) } } -static void -netjet_interrupt(int intno, void *dev_id, struct pt_regs *regs) -{ - struct IsdnCardState *cs = dev_id; - u_char val, sval; - long flags; - - if (!cs) { - printk(KERN_WARNING "NETjet: Spurious interrupt!\n"); - return; - } - if (!((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT1)) & - NETJET_ISACIRQ)) { - val = ReadISAC(cs, ISAC_ISTA); - if (cs->debug & L1_DEB_ISAC) - debugl1(cs, "tiger: i1 %x %x", sval, val); - if (val) { - isac_interrupt(cs, val); - WriteISAC(cs, ISAC_MASK, 0xFF); - WriteISAC(cs, ISAC_MASK, 0x0); - } - } - save_flags(flags); - cli(); - if ((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT0))) { - if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { - restore_flags(flags); - return; - } - cs->hw.njet.irqstat0 = sval; - restore_flags(flags); -/* debugl1(cs, "tiger: ist0 %x %x %x %x/%x pulse=%d", - sval, - bytein(cs->hw.njet.base + NETJET_DMACTRL), - bytein(cs->hw.njet.base + NETJET_IRQMASK0), - inl(cs->hw.njet.base + NETJET_DMA_READ_ADR), - inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR), - bytein(cs->hw.njet.base + NETJET_PULSE_CNT)); -*/ -/* cs->hw.njet.irqmask0 = ((0x0f & cs->hw.njet.irqstat0) ^ 0x0f) | 0x30; -*/ byteout(cs->hw.njet.base + NETJET_IRQSTAT0, cs->hw.njet.irqstat0); -/* byteout(cs->hw.njet.base + NETJET_IRQMASK0, cs->hw.njet.irqmask0); -*/ if (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ) - read_tiger(cs); - if (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE) - write_tiger(cs); - test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); - } else - restore_flags(flags); - -/* if (!testcnt--) { - cs->hw.njet.dmactrl = 0; - byteout(cs->hw.njet.base + NETJET_DMACTRL, - cs->hw.njet.dmactrl); - byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0); - } -*/ -} - -static void -reset_netjet(struct IsdnCardState *cs) -{ - long flags; - - save_flags(flags); - sti(); - cs->hw.njet.ctrl_reg = 0xff; /* Reset On */ - byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg); - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ - cs->hw.njet.ctrl_reg = 0x00; /* Reset Off and status read clear */ - byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg); - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ - restore_flags(flags); - cs->hw.njet.auxd = 0; - cs->hw.njet.dmactrl = 0; - byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ); - byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ); - byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); -} - void release_io_netjet(struct IsdnCardState *cs) { @@ -1081,99 +887,3 @@ release_io_netjet(struct IsdnCardState *cs) release_region(cs->hw.njet.base, 256); } - -static int -NETjet_card_msg(struct IsdnCardState *cs, int mt, void *arg) -{ - switch (mt) { - case CARD_RESET: - reset_netjet(cs); - return(0); - case CARD_RELEASE: - release_io_netjet(cs); - return(0); - case CARD_INIT: - inittiger(cs); - clear_pending_isac_ints(cs); - initisac(cs); - /* Reenable all IRQ */ - cs->writeisac(cs, ISAC_MASK, 0); - return(0); - case CARD_TEST: - return(0); - } - return(0); -} - -static struct pci_dev *dev_netjet __initdata = NULL; - -__initfunc(int -setup_netjet(struct IsdnCard *card)) -{ - int bytecnt; - struct IsdnCardState *cs = card->cs; - char tmp[64]; -#if CONFIG_PCI -#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 - if (!pci_present()) { - printk(KERN_ERR "Netjet: no PCI bus present\n"); - return(0); - } - if ((dev_netjet = pci_find_device(PCI_VENDOR_TRAVERSE_TECH, - 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 = dev_netjet->base_address[ 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); - } - 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_BIOS\n"); - printk(KERN_WARNING "NETjet: unable to config NETJET PCI\n"); - return (0); -#endif /* CONFIG_PCI */ - printk(KERN_INFO - "NETjet: PCI card configured at 0x%x IRQ %d\n", - cs->hw.njet.base, cs->irq); - if (check_region(cs->hw.njet.base, bytecnt)) { - printk(KERN_WARNING - "HiSax: %s config port %x-%x already in use\n", - CardType[card->typ], - cs->hw.njet.base, - cs->hw.njet.base + bytecnt); - return (0); - } else { - request_region(cs->hw.njet.base, bytecnt, "netjet isdn"); - } - reset_netjet(cs); - cs->readisac = &ReadISAC; - cs->writeisac = &WriteISAC; - cs->readisacfifo = &ReadISACfifo; - cs->writeisacfifo = &WriteISACfifo; - cs->BC_Read_Reg = &dummyrr; - cs->BC_Write_Reg = &dummywr; - cs->BC_Send_Data = &fill_dma; - cs->cardmsg = &NETjet_card_msg; - cs->irq_func = &netjet_interrupt; - cs->irq_flags |= SA_SHIRQ; - ISACVersion(cs, "NETjet:"); - return (1); -} diff --git a/drivers/isdn/hisax/netjet.h b/drivers/isdn/hisax/netjet.h new file mode 100644 index 000000000000..d7dede4ff6a2 --- /dev/null +++ b/drivers/isdn/hisax/netjet.h @@ -0,0 +1,77 @@ +// $Id: netjet.h,v 2.3 2000/06/26 08:59:14 keil Exp $ +//----------------------------------------------------------------------------- +// +// NETjet common header file +// +// Author Kerstern Keil repackaged by +// Matt Henderson - Traverse Technologies P/L www.traverse.com.au +// +// This file is (c) under GNU PUBLIC LICENSE +// +//----------------------------------------------------------------------------- + +extern const char *CardType[]; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +/* PCI stuff */ +#ifndef PCI_VENDOR_ID_TIGERJET +#define PCI_VENDOR_ID_TIGERJET 0xe159 +#endif +#ifndef PCI_DEVICE_ID_TIGERJET_300 +#define PCI_DEVICE_ID_TIGERJET_300 0x0001 +#endif +#define NETJET_CTRL 0x00 +#define NETJET_DMACTRL 0x01 +#define NETJET_AUXCTRL 0x02 +#define NETJET_AUXDATA 0x03 +#define NETJET_IRQMASK0 0x04 +#define NETJET_IRQMASK1 0x05 +#define NETJET_IRQSTAT0 0x06 +#define NETJET_IRQSTAT1 0x07 +#define NETJET_DMA_READ_START 0x08 +#define NETJET_DMA_READ_IRQ 0x0c +#define NETJET_DMA_READ_END 0x10 +#define NETJET_DMA_READ_ADR 0x14 +#define NETJET_DMA_WRITE_START 0x18 +#define NETJET_DMA_WRITE_IRQ 0x1c +#define NETJET_DMA_WRITE_END 0x20 +#define NETJET_DMA_WRITE_ADR 0x24 +#define NETJET_PULSE_CNT 0x28 + +#define NETJET_ISAC_OFF 0xc0 +#define NETJET_ISACIRQ 0x10 +#define NETJET_IRQM0_READ 0x0c +#define NETJET_IRQM0_READ_1 0x04 +#define NETJET_IRQM0_READ_2 0x08 +#define NETJET_IRQM0_WRITE 0x03 +#define NETJET_IRQM0_WRITE_1 0x01 +#define NETJET_IRQM0_WRITE_2 0x02 + +#define NETJET_DMA_TXSIZE 512 +#define NETJET_DMA_RXSIZE 128 + +#define HDLC_ZERO_SEARCH 0 +#define HDLC_FLAG_SEARCH 1 +#define HDLC_FLAG_FOUND 2 +#define HDLC_FRAME_FOUND 3 +#define HDLC_NULL 4 +#define HDLC_PART 5 +#define HDLC_FULL 6 + +#define HDLC_FLAG_VALUE 0x7e + +u_char NETjet_ReadIC(struct IsdnCardState *cs, u_char offset); +void NETjet_WriteIC(struct IsdnCardState *cs, u_char offset, u_char value); +void NETjet_ReadICfifo(struct IsdnCardState *cs, u_char *data, int size); +void NETjet_WriteICfifo(struct IsdnCardState *cs, u_char *data, int size); + +void read_tiger(struct IsdnCardState *cs); +void write_tiger(struct IsdnCardState *cs); + +void netjet_fill_dma(struct BCState *bcs); +void netjet_interrupt(int intno, void *dev_id, struct pt_regs *regs); +__initfunc(void inittiger(struct IsdnCardState *cs)); +void release_io_netjet(struct IsdnCardState *cs); + diff --git a/drivers/isdn/hisax/niccy.c b/drivers/isdn/hisax/niccy.c index aec259e86795..49b07e85dbdc 100644 --- a/drivers/isdn/hisax/niccy.c +++ b/drivers/isdn/hisax/niccy.c @@ -1,5 +1,5 @@ -/* $Id: niccy.c,v 1.8 1999/08/11 21:01:33 keil Exp $ - +/* $Id: niccy.c,v 1.12 2000/06/26 08:59:14 keil Exp $ + * * niccy.c low level stuff for Dr. Neuhaus NICCY PnP and NICCY PCI and * compatible (SAGEM cybermodem) * @@ -7,28 +7,7 @@ * * 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 - * - * Revision 1.3 1998/04/15 16:42:59 keil - * new init code - * - * Revision 1.2 1998/02/11 17:31:04 keil - * new file + * This file is (c) under GNU PUBLIC LICENSE * */ @@ -42,7 +21,7 @@ #include extern const char *CardType[]; -const char *niccy_revision = "$Revision: 1.8 $"; +const char *niccy_revision = "$Revision: 1.12 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -59,8 +38,12 @@ const char *niccy_revision = "$Revision: 1.8 $"; #define NICCY_PCI 2 /* PCI stuff */ -#define PCI_VENDOR_DR_NEUHAUS 0x1267 -#define PCI_NICCY_ID 0x1016 +#ifndef PCI_VENDOR_ID_SATSAGEM +#define PCI_VENDOR_ID_SATSAGEM 0x1267 +#endif +#ifndef PCI_DEVICE_ID_SATSAGEM_NICCY +#define PCI_DEVICE_ID_SATSAGEM_NICCY 0x1016 +#endif #define PCI_IRQ_CTRL_REG 0x38 #define PCI_IRQ_ENABLE 0x1f00 #define PCI_IRQ_DISABLE 0xff0000 @@ -228,12 +211,13 @@ release_io_niccy(struct IsdnCardState *cs) static void niccy_reset(struct IsdnCardState *cs) { - int val, nval; - - val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); - nval = val | PCI_IRQ_ENABLE; - outl(nval, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); + if (cs->subtyp == NICCY_PCI) { + int val; + val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); + val |= PCI_IRQ_ENABLE; + outl(val, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); + } inithscxisac(cs, 3); } @@ -304,24 +288,26 @@ setup_niccy(struct IsdnCard *card)) return(0); } cs->subtyp = 0; - if ((niccy_dev = pci_find_device(PCI_VENDOR_DR_NEUHAUS, - PCI_NICCY_ID, niccy_dev))) { + if ((niccy_dev = pci_find_device(PCI_VENDOR_ID_SATSAGEM, + PCI_DEVICE_ID_SATSAGEM_NICCY, niccy_dev))) { + if (pci_enable_device(niccy_dev)) + return(0); /* 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 (!niccy_dev->base_address[ 0]) { + cs->hw.niccy.cfg_reg = niccy_dev->base_address[ 0] & PCI_BASE_ADDRESS_IO_MASK; + if (!cs->hw.niccy.cfg_reg) { printk(KERN_WARNING "Niccy: No IO-Adr for PCI cfg found\n"); return(0); } - cs->hw.niccy.cfg_reg = niccy_dev->base_address[ 0] & PCI_BASE_ADDRESS_IO_MASK; - if (!niccy_dev->base_address[ 1]) { + pci_ioaddr = niccy_dev->base_address[ 1] & PCI_BASE_ADDRESS_IO_MASK; + if (!pci_ioaddr) { printk(KERN_WARNING "Niccy: No IO-Adr for PCI card found\n"); return(0); } - pci_ioaddr = niccy_dev->base_address[ 1] & PCI_BASE_ADDRESS_IO_MASK; cs->subtyp = NICCY_PCI; } else { printk(KERN_WARNING "Niccy: No PCI card found\n"); diff --git a/drivers/isdn/hisax/nj_s.c b/drivers/isdn/hisax/nj_s.c new file mode 100644 index 000000000000..ce58ec1d08d8 --- /dev/null +++ b/drivers/isdn/hisax/nj_s.c @@ -0,0 +1,257 @@ +// $Id: nj_s.c,v 2.3 2000/06/26 08:59:14 keil Exp $ +// +// This file is (c) under GNU PUBLIC LICENSE +// +#define __NO_VERSION__ +#include +#include "hisax.h" +#include "isac.h" +#include "isdnl1.h" +#include +#include +#include +#include "netjet.h" + +const char *NETjet_S_revision = "$Revision: 2.3 $"; + +static u_char dummyrr(struct IsdnCardState *cs, int chan, u_char off) +{ + return(5); +} + +static void dummywr(struct IsdnCardState *cs, int chan, u_char off, u_char value) +{ +} + +static void +netjet_s_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, sval; + long flags; + + if (!cs) { + printk(KERN_WARNING "NETjet-S: Spurious interrupt!\n"); + return; + } + if (!((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT1)) & + NETJET_ISACIRQ)) { + val = NETjet_ReadIC(cs, ISAC_ISTA); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "tiger: i1 %x %x", sval, val); + if (val) { + isac_interrupt(cs, val); + NETjet_WriteIC(cs, ISAC_MASK, 0xFF); + NETjet_WriteIC(cs, ISAC_MASK, 0x0); + } + } + save_flags(flags); + cli(); + if ((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT0))) { + if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + restore_flags(flags); + return; + } + cs->hw.njet.irqstat0 = sval; + restore_flags(flags); +/* debugl1(cs, "tiger: ist0 %x %x %x %x/%x pulse=%d", + sval, + bytein(cs->hw.njet.base + NETJET_DMACTRL), + bytein(cs->hw.njet.base + NETJET_IRQMASK0), + inl(cs->hw.njet.base + NETJET_DMA_READ_ADR), + inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR), + bytein(cs->hw.njet.base + NETJET_PULSE_CNT)); +*/ +/* cs->hw.njet.irqmask0 = ((0x0f & cs->hw.njet.irqstat0) ^ 0x0f) | 0x30; +*/ byteout(cs->hw.njet.base + NETJET_IRQSTAT0, cs->hw.njet.irqstat0); +/* byteout(cs->hw.njet.base + NETJET_IRQMASK0, cs->hw.njet.irqmask0); +*/ if (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ) + read_tiger(cs); + if (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE) + write_tiger(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + restore_flags(flags); + +/* if (!testcnt--) { + cs->hw.njet.dmactrl = 0; + byteout(cs->hw.njet.base + NETJET_DMACTRL, + cs->hw.njet.dmactrl); + byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0); + } +*/ +} + +static void +reset_netjet_s(struct IsdnCardState *cs) +{ + long flags; + + save_flags(flags); + sti(); + cs->hw.njet.ctrl_reg = 0xff; /* Reset On */ + byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ + cs->hw.njet.ctrl_reg = 0x00; /* Reset Off and status read clear */ + byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ + restore_flags(flags); + cs->hw.njet.auxd = 0; + cs->hw.njet.dmactrl = 0; + byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ); + byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ); + byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); +} + +static int +NETjet_S_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_netjet_s(cs); + return(0); + case CARD_RELEASE: + release_io_netjet(cs); + return(0); + case CARD_INIT: + inittiger(cs); + clear_pending_isac_ints(cs); + initisac(cs); + /* Reenable all IRQ */ + cs->writeisac(cs, ISAC_MASK, 0); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +static struct pci_dev *dev_netjet __initdata = NULL; + +__initfunc(int +setup_netjet_s(struct IsdnCard *card)) +{ + int bytecnt; + struct IsdnCardState *cs = card->cs; + char tmp[64]; + long flags; +#if CONFIG_PCI +#endif +#ifdef __BIG_ENDIAN +#error "not running on big endian machines now" +#endif + strcpy(tmp, NETjet_S_revision); + printk(KERN_INFO "HiSax: Traverse Tech. NETjet-S driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_NETJET_S) + return(0); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + + for ( ;; ) + { + +#if CONFIG_PCI + + if (!pci_present()) { + printk(KERN_ERR "Netjet: no PCI bus present\n"); + return(0); + } + if ((dev_netjet = pci_find_device(PCI_VENDOR_ID_TIGERJET, + PCI_DEVICE_ID_TIGERJET_300, dev_netjet))) { + if (pci_enable_device(dev_netjet)) + return(0); + cs->irq = dev_netjet->irq; + if (!cs->irq) { + printk(KERN_WARNING "NETjet-S: No IRQ for PCI card found\n"); + return(0); + } + cs->hw.njet.base = dev_netjet->base_address[ 0] & PCI_BASE_ADDRESS_IO_MASK; + if (!cs->hw.njet.base) { + printk(KERN_WARNING "NETjet-S: No IO-Adr for PCI card found\n"); + return(0); + } + } else { + printk(KERN_WARNING "NETjet-S: No 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; + + save_flags(flags); + sti(); + + cs->hw.njet.ctrl_reg = 0xff; /* Reset On */ + byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg); + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ + + cs->hw.njet.ctrl_reg = 0x00; /* Reset Off and status read clear */ + byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg); + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ + + restore_flags(flags); + + cs->hw.njet.auxd = 0xC0; + cs->hw.njet.dmactrl = 0; + + byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ); + byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ); + byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); + + switch ( ( ( NETjet_ReadIC( cs, ISAC_RBCH ) >> 5 ) & 3 ) ) + { + case 0 : + break; + + case 3 : + printk( KERN_WARNING "NETjet-S: NETspider-U PCI card found\n" ); + continue; + + default : + printk( KERN_WARNING "NETjet-S: No PCI card found\n" ); + return 0; + } + break; + } +#else + + printk(KERN_WARNING "NETjet-S: NO_PCI_BIOS\n"); + printk(KERN_WARNING "NETjet-S: unable to config NETJET-S PCI\n"); + return (0); + +#endif /* CONFIG_PCI */ + + bytecnt = 256; + + printk(KERN_INFO + "NETjet-S: PCI card configured at 0x%x IRQ %d\n", + cs->hw.njet.base, cs->irq); + if (check_region(cs->hw.njet.base, bytecnt)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.njet.base, + cs->hw.njet.base + bytecnt); + return (0); + } else { + request_region(cs->hw.njet.base, bytecnt, "netjet-s isdn"); + } + reset_netjet_s(cs); + cs->readisac = &NETjet_ReadIC; + cs->writeisac = &NETjet_WriteIC; + cs->readisacfifo = &NETjet_ReadICfifo; + cs->writeisacfifo = &NETjet_WriteICfifo; + cs->BC_Read_Reg = &dummyrr; + cs->BC_Write_Reg = &dummywr; + cs->BC_Send_Data = &netjet_fill_dma; + cs->cardmsg = &NETjet_S_card_msg; + cs->irq_func = &netjet_s_interrupt; + cs->irq_flags |= SA_SHIRQ; + ISACVersion(cs, "NETjet-S:"); + return (1); +} diff --git a/drivers/isdn/hisax/nj_u.c b/drivers/isdn/hisax/nj_u.c new file mode 100644 index 000000000000..cb7ce68f991c --- /dev/null +++ b/drivers/isdn/hisax/nj_u.c @@ -0,0 +1,260 @@ +/* $Id: nj_u.c,v 2.4 2000/06/26 11:42:16 keil Exp $ + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ +#define __NO_VERSION__ +#include +#include "hisax.h" +#include "icc.h" +#include "isdnl1.h" +#include +#include +#include +#include "netjet.h" + +const char *NETjet_U_revision = "$Revision: 2.4 $"; + +static u_char dummyrr(struct IsdnCardState *cs, int chan, u_char off) +{ + return(5); +} + +static void dummywr(struct IsdnCardState *cs, int chan, u_char off, u_char value) +{ +} + +static void +netjet_u_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, sval; + long flags; + + if (!cs) { + printk(KERN_WARNING "NETspider-U: Spurious interrupt!\n"); + return; + } + if (!((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT1)) & + NETJET_ISACIRQ)) { + val = NETjet_ReadIC(cs, ICC_ISTA); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "tiger: i1 %x %x", sval, val); + if (val) { + icc_interrupt(cs, val); + NETjet_WriteIC(cs, ICC_MASK, 0xFF); + NETjet_WriteIC(cs, ICC_MASK, 0x0); + } + } + save_flags(flags); + cli(); + if ((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT0))) { + if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + restore_flags(flags); + return; + } + cs->hw.njet.irqstat0 = sval; + restore_flags(flags); +/* debugl1(cs, "tiger: ist0 %x %x %x %x/%x pulse=%d", + sval, + bytein(cs->hw.njet.base + NETJET_DMACTRL), + bytein(cs->hw.njet.base + NETJET_IRQMASK0), + inl(cs->hw.njet.base + NETJET_DMA_READ_ADR), + inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR), + bytein(cs->hw.njet.base + NETJET_PULSE_CNT)); +*/ +/* cs->hw.njet.irqmask0 = ((0x0f & cs->hw.njet.irqstat0) ^ 0x0f) | 0x30; +*/ byteout(cs->hw.njet.base + NETJET_IRQSTAT0, cs->hw.njet.irqstat0); +/* byteout(cs->hw.njet.base + NETJET_IRQMASK0, cs->hw.njet.irqmask0); +*/ if (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ) + read_tiger(cs); + if (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE) + write_tiger(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + restore_flags(flags); + +/* if (!testcnt--) { + cs->hw.njet.dmactrl = 0; + byteout(cs->hw.njet.base + NETJET_DMACTRL, + cs->hw.njet.dmactrl); + byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0); + } +*/ +} + +static void +reset_netjet_u(struct IsdnCardState *cs) +{ + long flags; + + save_flags(flags); + sti(); + cs->hw.njet.ctrl_reg = 0xff; /* Reset On */ + byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ + cs->hw.njet.ctrl_reg = 0x00; /* Reset Off and status read clear */ + byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ + restore_flags(flags); + cs->hw.njet.auxd = 0xC0; + cs->hw.njet.dmactrl = 0; + byteout(cs->hw.njet.auxa, 0); + byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ); + byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ); + byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); +} + +static int +NETjet_U_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_netjet_u(cs); + return(0); + case CARD_RELEASE: + release_io_netjet(cs); + return(0); + case CARD_INIT: + inittiger(cs); + clear_pending_icc_ints(cs); + initicc(cs); + /* Reenable all IRQ */ + cs->writeisac(cs, ICC_MASK, 0); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +static struct pci_dev *dev_netjet __initdata = NULL; + +__initfunc(int +setup_netjet_u(struct IsdnCard *card)) +{ + int bytecnt; + struct IsdnCardState *cs = card->cs; + char tmp[64]; + long flags; +#if CONFIG_PCI +#endif +#ifdef __BIG_ENDIAN +#error "not running on big endian machines now" +#endif + strcpy(tmp, NETjet_U_revision); + printk(KERN_INFO "HiSax: Traverse Tech. NETspider-U driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_NETJET_U) + return(0); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + + for ( ;; ) + { + +#if CONFIG_PCI + + if (!pci_present()) { + printk(KERN_ERR "NETspider-U: no PCI bus present\n"); + return(0); + } + if ((dev_netjet = pci_find_device(PCI_VENDOR_ID_TIGERJET, + PCI_DEVICE_ID_TIGERJET_300, dev_netjet))) { + if (pci_enable_device(dev_netjet)) + return(0); + cs->irq = dev_netjet->irq; + if (!cs->irq) { + printk(KERN_WARNING "NETspider-U: No IRQ for PCI card found\n"); + return(0); + } + cs->hw.njet.base = dev_netjet->base_address[ 0] & PCI_BASE_ADDRESS_IO_MASK; + if (!cs->hw.njet.base) { + printk(KERN_WARNING "NETspider-U: No IO-Adr for PCI card found\n"); + return(0); + } + } else { + printk(KERN_WARNING "NETspider-U: No 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; + + save_flags(flags); + sti(); + + cs->hw.njet.ctrl_reg = 0xff; /* Reset On */ + byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg); + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ + + cs->hw.njet.ctrl_reg = 0x00; /* Reset Off and status read clear */ + byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg); + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ + + restore_flags(flags); + + cs->hw.njet.auxd = 0xC0; + cs->hw.njet.dmactrl = 0; + + byteout(cs->hw.njet.auxa, 0); + byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ); + byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ); + byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); + + switch ( ( ( NETjet_ReadIC( cs, ICC_RBCH ) >> 5 ) & 3 ) ) + { + case 3 : + break; + + case 0 : + printk( KERN_WARNING "NETspider-U: NETjet-S PCI card found\n" ); + continue; + + default : + printk( KERN_WARNING "NETspider-U: No PCI card found\n" ); + return 0; + } + break; + } +#else + + printk(KERN_WARNING "NETspider-U: NO_PCI_BIOS\n"); + printk(KERN_WARNING "NETspider-U: unable to config NETspider-U PCI\n"); + return (0); + +#endif /* CONFIG_PCI */ + + bytecnt = 256; + + printk(KERN_INFO + "NETspider-U: PCI card configured at 0x%x IRQ %d\n", + cs->hw.njet.base, cs->irq); + if (check_region(cs->hw.njet.base, bytecnt)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.njet.base, + cs->hw.njet.base + bytecnt); + return (0); + } else { + request_region(cs->hw.njet.base, bytecnt, "netjet-u isdn"); + } + reset_netjet_u(cs); + cs->readisac = &NETjet_ReadIC; + cs->writeisac = &NETjet_WriteIC; + cs->readisacfifo = &NETjet_ReadICfifo; + cs->writeisacfifo = &NETjet_WriteICfifo; + cs->BC_Read_Reg = &dummyrr; + cs->BC_Write_Reg = &dummywr; + cs->BC_Send_Data = &netjet_fill_dma; + cs->cardmsg = &NETjet_U_card_msg; + cs->irq_func = &netjet_u_interrupt; + cs->irq_flags |= SA_SHIRQ; + ICCVersion(cs, "NETspider-U:"); + return (1); +} diff --git a/drivers/isdn/hisax/q931.c b/drivers/isdn/hisax/q931.c index d3c269a04db1..3f461883d00c 100644 --- a/drivers/isdn/hisax/q931.c +++ b/drivers/isdn/hisax/q931.c @@ -1,9 +1,11 @@ -/* $Id: q931.c,v 1.7 1998/11/15 23:55:17 keil Exp $ - +/* $Id: q931.c,v 1.10 2000/06/26 08:59:14 keil Exp $ + * * q931.c code to decode ITU Q.931 call control messages * * Author Jan den Ouden * + * This file is (c) under GNU PUBLIC LICENSE + * * Changelog * * Pauline Middelink general improvements @@ -12,30 +14,6 @@ * * Karsten Keil cause texts, display information element for 1TR6 * - * - * $Log: q931.c,v $ - * Revision 1.7 1998/11/15 23:55:17 keil - * changes from 2.0 - * - * Revision 1.6 1997/07/27 21:09:44 keil - * move functions to isdnl3.c - * - * Revision 1.5 1997/04/06 22:56:43 keil - * Some cosmetic changes - * - * Revision 1.4 1997/02/09 00:29:11 keil - * new interface handling, one interface per card - * - * Revision 1.3 1997/01/21 22:24:59 keil - * cleanups - * - * Revision 1.2 1996/10/27 22:12:45 keil - * reporting unknown level 3 protocol ids - * - * Revision 1.1 1996/10/13 20:04:56 keil - * Initial revision - * - * */ @@ -86,6 +64,24 @@ struct MessageType { { 0xd, "SETUP ACKNOWLEDGE" }, + { + 0x24, "HOLD" + }, + { + 0x28, "HOLD ACKNOWLEDGE" + }, + { + 0x30, "HOLD REJECT" + }, + { + 0x31, "RETRIEVE" + }, + { + 0x33, "RETRIEVE ACKNOWLEDGE" + }, + { + 0x37, "RETRIEVE REJECT" + }, { 0x26, "RESUME" }, @@ -201,29 +197,6 @@ struct MessageType mt_n1[] = #define MT_N1_LEN (sizeof(mt_n1) / sizeof(struct MessageType)) -static struct MessageType fac_1tr6[] = -{ - {FAC_Sperre, "Sperre"}, - {FAC_Forward1, "Forward 1"}, - {FAC_Forward2, "Forward 2"}, - {FAC_Konferenz, "Konferenz"}, - {FAC_GrabBchan, "Grab Bchannel"}, - {FAC_Reactivate, "Reactivate"}, - {FAC_Konferenz3, "Dreier Konferenz"}, - {FAC_Dienstwechsel1, "Einseitiger Dienstwechsel"}, - {FAC_Dienstwechsel2, "Zweiseitiger Dienstwechsel"}, - {FAC_NummernIdent, "Rufnummer-Identifizierung"}, - {FAC_GBG, "GBG"}, - {FAC_DisplayUebergeben, "Display Uebergeben"}, - {FAC_DisplayUmgeleitet, "Display Umgeleitet"}, - {FAC_Unterdruecke, "Unterdruecke Rufnummer"}, - {FAC_Deactivate, "Deactivate"}, - {FAC_Activate, "Activate"}, - {FAC_SPV, "SPV"}, - {FAC_Rueckwechsel, "Rueckwechsel"}, - {FAC_Umleitung, "Umleitung"} -}; -#define FAC_1TR6_LEN (sizeof(fac_1tr6) / sizeof(struct MessageType)) static int prbits(char *dest, u_char b, int start, int len) @@ -683,6 +656,65 @@ prbearer(char *dest, u_char * p) return (dp - dest); } + +static +int +prbearer_ni1(char *dest, u_char * p) +{ + char *dp = dest; + u_char len; + + p++; + len = *p++; + dp += sprintf(dp, " octet 3 "); + dp += prbits(dp, *p, 8, 8); + switch (*p++) { + case 0x80: + dp += sprintf(dp, " Speech"); + break; + case 0x88: + dp += sprintf(dp, " Unrestricted digital information"); + break; + case 0x90: + dp += sprintf(dp, " 3.1 kHz audio"); + break; + default: + dp += sprintf(dp, " Unknown information-transfer capability"); + } + *dp++ = '\n'; + dp += sprintf(dp, " octet 4 "); + dp += prbits(dp, *p, 8, 8); + switch (*p++) { + case 0x90: + dp += sprintf(dp, " 64 kbps, circuit mode"); + break; + case 0xc0: + dp += sprintf(dp, " Packet mode"); + break; + default: + dp += sprintf(dp, " Unknown transfer mode"); + } + *dp++ = '\n'; + if (len > 2) { + dp += sprintf(dp, " octet 5 "); + dp += prbits(dp, *p, 8, 8); + switch (*p++) { + case 0x21: + dp += sprintf(dp, " Rate adaption\n"); + dp += sprintf(dp, " octet 5a "); + dp += prbits(dp, *p, 8, 8); + break; + case 0xa2: + dp += sprintf(dp, " u-law"); + break; + default: + dp += sprintf(dp, " Unknown UI layer 1 protocol"); + } + *dp++ = '\n'; + } + return (dp - dest); +} + static int general(char *dest, u_char * p) { @@ -710,6 +742,33 @@ general(char *dest, u_char * p) return (dp - dest); } +static int +general_ni1(char *dest, u_char * p) +{ + char *dp = dest; + char ch = ' '; + int l, octet = 3; + + p++; + l = *p++; + /* Iterate over all octets in the information element */ + while (l--) { + dp += sprintf(dp, " octet %d%c ", octet, ch); + dp += prbits(dp, *p, 8, 8); + *dp++ = '\n'; + + /* last octet in group? */ + if (*p++ & 0x80) { + octet++; + ch = ' '; + } else if (ch == ' ') + ch = 'a'; + else + ch++; + } + return (dp - dest); +} + static int prcharge(char *dest, u_char * p) { @@ -742,6 +801,112 @@ prtext(char *dest, u_char * p) *dp++ = '\n'; return (dp - dest); } + +static int +prfeatureind(char *dest, u_char * p) +{ + char *dp = dest; + + p += 2; /* skip id, len */ + dp += sprintf(dp, " octet 3 "); + dp += prbits(dp, *p, 8, 8); + *dp++ = '\n'; + if (!(*p++ & 80)) { + dp += sprintf(dp, " octet 4 "); + dp += prbits(dp, *p++, 8, 8); + *dp++ = '\n'; + } + dp += sprintf(dp, " Status: "); + switch (*p) { + case 0: + dp += sprintf(dp, "Idle"); + break; + case 1: + dp += sprintf(dp, "Active"); + break; + case 2: + dp += sprintf(dp, "Prompt"); + break; + case 3: + dp += sprintf(dp, "Pending"); + break; + default: + dp += sprintf(dp, "(Reserved)"); + break; + } + *dp++ = '\n'; + return (dp - dest); +} + +static +struct DTag { /* Display tags */ + u_char nr; + char *descr; +} dtaglist[] = { + { 0x82, "Continuation" }, + { 0x83, "Called address" }, + { 0x84, "Cause" }, + { 0x85, "Progress indicator" }, + { 0x86, "Notification indicator" }, + { 0x87, "Prompt" }, + { 0x88, "Accumlated digits" }, + { 0x89, "Status" }, + { 0x8a, "Inband" }, + { 0x8b, "Calling address" }, + { 0x8c, "Reason" }, + { 0x8d, "Calling party name" }, + { 0x8e, "Called party name" }, + { 0x8f, "Orignal called name" }, + { 0x90, "Redirecting name" }, + { 0x91, "Connected name" }, + { 0x92, "Originating restrictions" }, + { 0x93, "Date & time of day" }, + { 0x94, "Call Appearance ID" }, + { 0x95, "Feature address" }, + { 0x96, "Redirection name" }, + { 0x9e, "Text" }, +}; +#define DTAGSIZE sizeof(dtaglist)/sizeof(struct DTag) + +static int +disptext_ni1(char *dest, u_char * p) +{ + char *dp = dest; + int l, tag, len, i; + + p++; + l = *p++ - 1; + if (*p++ != 0x80) { + dp += sprintf(dp, " Unknown display type\n"); + return (dp - dest); + } + /* Iterate over all tag,length,text fields */ + while (l > 0) { + tag = *p++; + len = *p++; + l -= len + 2; + /* Don't space or skip */ + if ((tag == 0x80) || (tag == 0x81)) p++; + else { + for (i = 0; i < DTAGSIZE; i++) + if (tag == dtaglist[i].nr) + break; + + /* When not found, give appropriate msg */ + if (i != DTAGSIZE) { + dp += sprintf(dp, " %s: ", dtaglist[i].descr); + while (len--) + *dp++ = *p++; + } else { + dp += sprintf(dp, " (unknown display tag %2x): ", tag); + while (len--) + *dp++ = *p++; + } + dp += sprintf(dp, "\n"); + } + } + return (dp - dest); +} static int display(char *dest, u_char * p) { @@ -912,6 +1077,49 @@ struct InformationElement { #define IESIZE sizeof(ielist)/sizeof(struct InformationElement) +static +struct InformationElement ielist_ni1[] = { + { 0x04, "Bearer Capability", prbearer_ni1 }, + { 0x08, "Cause", prcause }, + { 0x14, "Call State", general_ni1 }, + { 0x18, "Channel Identification", prchident }, + { 0x1e, "Progress Indicator", general_ni1 }, + { 0x27, "Notification Indicator", general_ni1 }, + { 0x2c, "Keypad Facility", prtext }, + { 0x32, "Information Request", general_ni1 }, + { 0x34, "Signal", general_ni1 }, + { 0x38, "Feature Activation", general_ni1 }, + { 0x39, "Feature Indication", prfeatureind }, + { 0x3a, "Service Profile Identification (SPID)", prtext }, + { 0x3b, "Endpoint Identifier", general_ni1 }, + { 0x6c, "Calling Party Number", prcalling }, + { 0x6d, "Calling Party Subaddress", general_ni1 }, + { 0x70, "Called Party Number", prcalled }, + { 0x71, "Called Party Subaddress", general_ni1 }, + { 0x74, "Redirecting Number", general_ni1 }, + { 0x78, "Transit Network Selection", general_ni1 }, + { 0x7c, "Low Layer Compatibility", general_ni1 }, + { 0x7d, "High Layer Compatibility", general_ni1 }, +}; + + +#define IESIZE_NI1 sizeof(ielist_ni1)/sizeof(struct InformationElement) + +static +struct InformationElement ielist_ni1_cs5[] = { + { 0x1d, "Operator system access", general_ni1 }, + { 0x2a, "Display text", disptext_ni1 }, +}; + +#define IESIZE_NI1_CS5 sizeof(ielist_ni1_cs5)/sizeof(struct InformationElement) + +static +struct InformationElement ielist_ni1_cs6[] = { + { 0x7b, "Call appearance", general_ni1 }, +}; + +#define IESIZE_NI1_CS6 sizeof(ielist_ni1_cs6)/sizeof(struct InformationElement) + static struct InformationElement we_0[] = { {WE0_cause, "Cause", prcause_1tr6}, @@ -1152,7 +1360,93 @@ dlogframe(struct IsdnCardState *cs, struct sk_buff *skb, int dir) } buf += buf[1] + 2; } - } else if (buf[0] == 8) { /* EURO */ + } else if ((buf[0] == 8) && (cs->protocol == ISDN_PTYPE_NI1)) { /* NI-1 */ + /* locate message type */ + buf++; + cr_l = *buf++; + if (cr_l) + cr = *buf++; + else + cr = 0; + mt = *buf++; + for (i = 0; i < MTSIZE; i++) + if (mtlist[i].nr == mt) + break; + + /* display message type if it exists */ + if (i == MTSIZE) + dp += sprintf(dp, "callref %d %s size %d unknown message type %x!\n", + cr & 0x7f, (cr & 0x80) ? "called" : "caller", + size, mt); + else + dp += sprintf(dp, "callref %d %s size %d message type %s\n", + cr & 0x7f, (cr & 0x80) ? "called" : "caller", + size, mtlist[i].descr); + + /* display each information element */ + while (buf < bend) { + /* Is it a single octet information element? */ + if (*buf & 0x80) { + switch ((*buf >> 4) & 7) { + case 1: + dp += sprintf(dp, " Shift %x\n", *buf & 0xf); + cs_old = cset; + cset = *buf & 7; + cs_fest = *buf & 8; + break; + default: + dp += sprintf(dp, " Unknown single-octet IE %x\n", *buf); + break; + } + buf++; + continue; + } + /* No, locate it in the table */ + if (cset == 0) { + for (i = 0; i < IESIZE; i++) + if (*buf == ielist_ni1[i].nr) + break; + + /* When not found, give appropriate msg */ + if (i != IESIZE) { + dp += sprintf(dp, " %s\n", ielist_ni1[i].descr); + dp += ielist_ni1[i].f(dp, buf); + } else + dp += sprintf(dp, " attribute %x attribute size %d\n", *buf, buf[1]); + } else if (cset == 5) { + for (i = 0; i < IESIZE_NI1_CS5; i++) + if (*buf == ielist_ni1_cs5[i].nr) + break; + + /* When not found, give appropriate msg */ + if (i != IESIZE_NI1_CS5) { + dp += sprintf(dp, " %s\n", ielist_ni1_cs5[i].descr); + dp += ielist_ni1_cs5[i].f(dp, buf); + } else + dp += sprintf(dp, " attribute %x attribute size %d\n", *buf, buf[1]); + } else if (cset == 6) { + for (i = 0; i < IESIZE_NI1_CS6; i++) + if (*buf == ielist_ni1_cs6[i].nr) + break; + + /* When not found, give appropriate msg */ + if (i != IESIZE_NI1_CS6) { + dp += sprintf(dp, " %s\n", ielist_ni1_cs6[i].descr); + dp += ielist_ni1_cs6[i].f(dp, buf); + } else + dp += sprintf(dp, " attribute %x attribute size %d\n", *buf, buf[1]); + } else + dp += sprintf(dp, " Unknown Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]); + + /* Skip to next element */ + if (cs_fest == 8) { + cset = cs_old; + cs_old = 0; + cs_fest = 0; + } + buf += buf[1] + 2; + } + } else if ((buf[0] == 8) && (cs->protocol == ISDN_PTYPE_EURO)) { /* EURO */ /* locate message type */ buf++; cr_l = *buf++; diff --git a/drivers/isdn/hisax/rawhdlc.c b/drivers/isdn/hisax/rawhdlc.c index 5a8b0591e178..1f4dc443628f 100644 --- a/drivers/isdn/hisax/rawhdlc.c +++ b/drivers/isdn/hisax/rawhdlc.c @@ -1,10 +1,11 @@ -/* $Id: rawhdlc.c,v 1.4 1999/12/23 15:09:32 keil Exp $ - +/* $Id: rawhdlc.c,v 1.5 2000/06/26 08:59:14 keil Exp $ + * * rawhdlc.c support routines for cards that don't support HDLC * * Author Karsten Keil (keil@isdn4linux.de) * Brent Baccala * + * This file is (c) under GNU PUBLIC LICENSE * * Some passive ISDN cards, such as the Traverse NETJet and the AMD 7930, * don't perform HDLC encapsulation over the B channel. Drivers for diff --git a/drivers/isdn/hisax/rawhdlc.h b/drivers/isdn/hisax/rawhdlc.h index d946fa0b3d78..12ca6ab8c2cb 100644 --- a/drivers/isdn/hisax/rawhdlc.h +++ b/drivers/isdn/hisax/rawhdlc.h @@ -1,9 +1,11 @@ -/* $Id: rawhdlc.h,v 1.2 1998/02/09 10:53:53 keil Exp $ - +/* $Id: rawhdlc.h,v 1.3 2000/06/26 08:59:14 keil Exp $ + * * rawhdlc.h support routines for cards that don't support HDLC * * Author Brent Baccala * + * This file is (c) under GNU PUBLIC LICENSE + * */ #ifndef RAWHDLC_H diff --git a/drivers/isdn/hisax/s0box.c b/drivers/isdn/hisax/s0box.c index 51bc11658416..6b102bcec549 100644 --- a/drivers/isdn/hisax/s0box.c +++ b/drivers/isdn/hisax/s0box.c @@ -1,9 +1,10 @@ -/* $Id: s0box.c,v 2.2 1999/07/12 21:05:25 keil Exp $ - +/* $Id: s0box.c,v 2.3 2000/06/26 08:59:14 keil Exp $ + * * s0box.c low level stuff for Creatix S0BOX * * Author S0BOX specific stuff: Enrik Berkhan (enrik@starfleet.inka.de) * + * This file is (c) under GNU PUBLIC LICENSE * */ #define __NO_VERSION__ @@ -13,7 +14,7 @@ #include "isdnl1.h" extern const char *CardType[]; -const char *s0box_revision = "$Revision: 2.2 $"; +const char *s0box_revision = "$Revision: 2.3 $"; static inline void writereg(unsigned int padr, signed int addr, u_char off, u_char val) { diff --git a/drivers/isdn/hisax/saphir.c b/drivers/isdn/hisax/saphir.c index 7b021dd95e8e..af2023b81807 100644 --- a/drivers/isdn/hisax/saphir.c +++ b/drivers/isdn/hisax/saphir.c @@ -1,27 +1,12 @@ -/* $Id: saphir.c,v 1.5 1999/12/19 13:09:42 keil Exp $ - +/* $Id: saphir.c,v 1.7 2000/06/26 08:59:14 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.5 1999/12/19 13:09:42 keil - * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for - * signal proof delays - * - * Revision 1.4 1999/09/04 06:20:06 keil - * Changes from kernel set_current_state() - * - * 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 - * + * This file is (c) under GNU PUBLIC LICENSE * */ @@ -33,7 +18,7 @@ #include "isdnl1.h" extern const char *CardType[]; -static char *saphir_rev = "$Revision: 1.5 $"; +static char *saphir_rev = "$Revision: 1.7 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -173,11 +158,9 @@ saphir_interrupt(int intno, void *dev_id, struct pt_regs *regs) 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 + if (cs->hw.saphir.timer.function) + mod_timer(&cs->hw.saphir.timer, jiffies+1*HZ); + 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); @@ -192,9 +175,7 @@ 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); + mod_timer(&cs->hw.saphir.timer, jiffies+1*HZ); } void @@ -241,10 +222,10 @@ saphir_reset(struct IsdnCardState *cs) save_flags(flags); sti(); byteout(cs->hw.saphir.cfg_reg + RESET_REG, 1); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((30*HZ)/1000); /* Timeout 30ms */ byteout(cs->hw.saphir.cfg_reg + RESET_REG, 0); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((30*HZ)/1000); /* Timeout 30ms */ restore_flags(flags); byteout(cs->hw.saphir.cfg_reg + IRQ_REG, irq_val); diff --git a/drivers/isdn/hisax/sedlbauer.c b/drivers/isdn/hisax/sedlbauer.c index 7b73021b62e0..f9ffa7f1d37e 100644 --- a/drivers/isdn/hisax/sedlbauer.c +++ b/drivers/isdn/hisax/sedlbauer.c @@ -1,5 +1,5 @@ -/* $Id: sedlbauer.c,v 1.20 2000/01/20 19:47:45 keil Exp $ - +/* $Id: sedlbauer.c,v 1.23 2000/06/26 08:59:14 keil Exp $ + * * sedlbauer.c low level stuff for Sedlbauer cards * includes support for the Sedlbauer speed star (speed star II), * support for the Sedlbauer speed fax+, @@ -16,71 +16,7 @@ * Sedlbauer AG for informations * Edgar Toernig * - * $Log: sedlbauer.c,v $ - * Revision 1.20 2000/01/20 19:47:45 keil - * Add Fax Class 1 support - * - * Revision 1.19 1999/12/19 13:09:42 keil - * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for - * signal proof delays - * - * Revision 1.18 1999/11/13 21:25:03 keil - * Support for Speedfax+ PCI - * - * Revision 1.17 1999/09/04 06:20:06 keil - * Changes from kernel set_current_state() - * - * Revision 1.16 1999/08/29 18:23:01 niemann - * Fixed typo in errormsg - * - * Revision 1.15 1999/08/25 17:00:00 keil - * Make ISAR V32bis modem running - * Make LL->HL interface open for additional commands - * - * 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 - * - * Revision 1.8 1998/08/13 23:34:51 keil - * starting speedfax+ (ISAR) support - * - * Revision 1.7 1998/04/15 16:44:33 keil - * new init code - * - * Revision 1.6 1998/02/09 18:46:06 keil - * Support for Sedlbauer PCMCIA (Marcus Niemann) - * - * Revision 1.5 1998/02/02 13:29:45 keil - * fast io - * - * Revision 1.4 1997/11/08 21:35:52 keil - * new l1 init - * - * Revision 1.3 1997/11/06 17:09:28 keil - * New 2.1 init code - * - * Revision 1.2 1997/10/29 18:55:52 keil - * changes for 2.1.60 (irq2dev_map) - * - * Revision 1.1 1997/09/11 17:32:04 keil - * new - * + * This file is (c) under GNU PUBLIC LICENSE * */ @@ -103,8 +39,6 @@ * For example: hisaxctrl 9 ISAR.BIN */ -#define SEDLBAUER_PCI 1 - #define __NO_VERSION__ #include #include "hisax.h" @@ -117,19 +51,23 @@ extern const char *CardType[]; -const char *Sedlbauer_revision = "$Revision: 1.20 $"; +const char *Sedlbauer_revision = "$Revision: 1.23 $"; const char *Sedlbauer_Types[] = {"None", "speed card/win", "speed star", "speed fax+", "speed win II / ISDN PC/104", "speed star II", "speed pci", - "speed fax+ pci"}; + "speed fax+ pyramid", "speed fax+ pci"}; -#ifdef SEDLBAUER_PCI -#define PCI_VENDOR_SEDLBAUER 0xe159 -#define PCI_SPEEDPCI_ID 0x02 -#define PCI_SUBVENDOR_SEDLBAUER 0x51 -#define PCI_SUB_ID_SPEEDFAXP 0x01 +#ifndef PCI_VENDOR_ID_TIGERJET +#define PCI_VENDOR_ID_TIGERJET 0xe159 #endif +#ifndef PCI_DEVICE_ID_TIGERJET_100 +#define PCI_DEVICE_ID_TIGERJET_100 0x0002 +#endif +#define PCI_SUBVENDOR_SPEEDFAX_PYRAMID 0x51 +#define PCI_SUBVENDOR_SEDLBAUER_PCI 0x53 +#define PCI_SUBVENDOR_SPEEDFAX_PCI 0x54 +#define PCI_SUB_ID_SEDLBAUER 0x01 #define SEDL_SPEED_CARD_WIN 1 #define SEDL_SPEED_STAR 2 @@ -137,7 +75,8 @@ const char *Sedlbauer_Types[] = #define SEDL_SPEED_WIN2_PC104 4 #define SEDL_SPEED_STAR2 5 #define SEDL_SPEED_PCI 6 -#define SEDL_SPEEDFAX_PCI 7 +#define SEDL_SPEEDFAX_PYRAMID 7 +#define SEDL_SPEEDFAX_PCI 8 #define SEDL_CHIP_TEST 0 #define SEDL_CHIP_ISAC_HSCX 1 @@ -349,10 +288,10 @@ sedlbauer_interrupt(int intno, void *dev_id, struct pt_regs *regs) } if ((cs->hw.sedl.bus == SEDL_BUS_PCMCIA) && (*cs->busy_flag == 1)) { - /* The card tends to generate interrupts while being removed - causing us to just crash the kernel. bad. */ - printk(KERN_WARNING "Sedlbauer: card not available!\n"); - return; + /* The card tends to generate interrupts while being removed + causing us to just crash the kernel. bad. */ + printk(KERN_WARNING "Sedlbauer: card not available!\n"); + return; } val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40); @@ -424,7 +363,8 @@ Start_IPAC: goto Start_IPAC; } if (!icnt) - printk(KERN_WARNING "Sedlbauer IRQ LOOP\n"); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "Sedlbauer IRQ LOOP"); writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xFF); writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xC0); } @@ -462,7 +402,8 @@ sedlbauer_interrupt_isar(int intno, void *dev_id, struct pt_regs *regs) goto Start_ISAC; } if (!cnt) - printk(KERN_WARNING "Sedlbauer IRQ LOOP\n"); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "Sedlbauer IRQ LOOP"); writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT, 0); writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0xFF); @@ -497,10 +438,10 @@ reset_sedlbauer(struct IsdnCardState *cs) writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x20); save_flags(flags); sti(); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x0); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); 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); @@ -523,10 +464,10 @@ reset_sedlbauer(struct IsdnCardState *cs) byteout(cs->hw.sedl.reset_on, SEDL_RESET); /* Reset On */ save_flags(flags); sti(); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); byteout(cs->hw.sedl.reset_off, 0); /* Reset Off */ - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); restore_flags(flags); } @@ -572,7 +513,7 @@ Sedl_card_msg(struct IsdnCardState *cs, int mt, void *arg) case CARD_TEST: return(0); case MDL_INFO_CONN: - if (cs->subtyp != SEDL_SPEEDFAX_PCI) + if (cs->subtyp != SEDL_SPEEDFAX_PYRAMID) return(0); if ((long) arg) cs->hw.sedl.reset_off &= ~SEDL_ISAR_PCI_LED2; @@ -581,7 +522,7 @@ Sedl_card_msg(struct IsdnCardState *cs, int mt, void *arg) byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off); break; case MDL_INFO_REL: - if (cs->subtyp != SEDL_SPEEDFAX_PCI) + if (cs->subtyp != SEDL_SPEEDFAX_PYRAMID) return(0); if ((long) arg) cs->hw.sedl.reset_off |= SEDL_ISAR_PCI_LED2; @@ -593,9 +534,7 @@ Sedl_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -#ifdef SEDLBAUER_PCI static struct pci_dev *dev_sedl __initdata = NULL; -#endif __initfunc(int setup_sedlbauer(struct IsdnCard *card)) @@ -633,42 +572,50 @@ setup_sedlbauer(struct IsdnCard *card)) } } else { /* Probe for Sedlbauer speed pci */ -#if SEDLBAUER_PCI #if CONFIG_PCI if (!pci_present()) { printk(KERN_ERR "Sedlbauer: no PCI bus present\n"); return(0); } - if ((dev_sedl = pci_find_device(PCI_VENDOR_SEDLBAUER, - PCI_SPEEDPCI_ID, dev_sedl))) { + if ((dev_sedl = pci_find_device(PCI_VENDOR_ID_TIGERJET, + PCI_DEVICE_ID_TIGERJET_100, dev_sedl))) { + if (pci_enable_device(dev_sedl)) + return(0); 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 = dev_sedl->base_address[ 0] & - PCI_BASE_ADDRESS_IO_MASK; + cs->hw.sedl.cfg_reg = dev_sedl->base_address[ 0] & PCI_BASE_ADDRESS_IO_MASK; } else { printk(KERN_WARNING "Sedlbauer: No PCI card found\n"); return(0); } cs->irq_flags |= SA_SHIRQ; cs->hw.sedl.bus = SEDL_BUS_PCI; - pci_read_config_word(dev_sedl, PCI_SUBSYSTEM_VENDOR_ID, - &sub_vendor_id); - pci_read_config_word(dev_sedl, PCI_SUBSYSTEM_ID, - &sub_id); + pci_read_config_word(dev_sedl, PCI_SUBSYSTEM_VENDOR_ID, &sub_vendor_id); + pci_read_config_word(dev_sedl, PCI_SUBSYSTEM_ID, &sub_id); printk(KERN_INFO "Sedlbauer: PCI subvendor:%x subid %x\n", sub_vendor_id, sub_id); printk(KERN_INFO "Sedlbauer: PCI base adr %#x\n", cs->hw.sedl.cfg_reg); - if ((sub_vendor_id == PCI_SUBVENDOR_SEDLBAUER) && - (sub_id == PCI_SUB_ID_SPEEDFAXP)) { + if (sub_id != PCI_SUB_ID_SEDLBAUER) { + printk(KERN_ERR "Sedlbauer: unknown sub id %#x\n", sub_id); + return(0); + } + if (sub_vendor_id == PCI_SUBVENDOR_SPEEDFAX_PYRAMID) { + cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR; + cs->subtyp = SEDL_SPEEDFAX_PYRAMID; + } else if (sub_vendor_id == PCI_SUBVENDOR_SPEEDFAX_PCI) { cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR; cs->subtyp = SEDL_SPEEDFAX_PCI; - } else { + } else if (sub_vendor_id == PCI_SUBVENDOR_SEDLBAUER_PCI) { cs->hw.sedl.chip = SEDL_CHIP_IPAC; cs->subtyp = SEDL_SPEED_PCI; + } else { + printk(KERN_ERR "Sedlbauer: unknown sub vendor id %#x\n", + sub_vendor_id); + return(0); } bytecnt = 256; cs->hw.sedl.reset_on = SEDL_ISAR_PCI_ISAR_RESET_ON; @@ -688,7 +635,6 @@ setup_sedlbauer(struct IsdnCard *card)) printk(KERN_WARNING "Sedlbauer: NO_PCI_BIOS\n"); return (0); #endif /* CONFIG_PCI */ -#endif /* SEDLBAUER_PCI */ } /* In case of the sedlbauer pcmcia card, this region is in use, @@ -748,7 +694,6 @@ setup_sedlbauer(struct IsdnCard *card)) if (cs->hw.sedl.chip == SEDL_CHIP_IPAC) { - /* IPAC */ if (cs->hw.sedl.bus == SEDL_BUS_PCI) { cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_ADR; cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_IPAC; diff --git a/drivers/isdn/hisax/sportster.c b/drivers/isdn/hisax/sportster.c index 5e13981c5d5f..6b6407796d6b 100644 --- a/drivers/isdn/hisax/sportster.c +++ b/drivers/isdn/hisax/sportster.c @@ -1,49 +1,12 @@ -/* $Id: sportster.c,v 1.12 1999/12/23 15:09:32 keil Exp $ - +/* $Id: sportster.c,v 1.13 2000/06/26 08:59:14 keil Exp $ + * * sportster.c low level stuff for USR Sportster internal TA * * Author Karsten Keil (keil@isdn4linux.de) * * Thanks to Christian "naddy" Weisgerber (3Com, US Robotics) for documentation * - * $Log: sportster.c,v $ - * Revision 1.12 1999/12/23 15:09:32 keil - * change email - * - * Revision 1.11 1999/12/19 13:09:42 keil - * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for - * signal proof delays - * - * Revision 1.10 1999/09/04 06:20:06 keil - * Changes from kernel set_current_state() - * - * 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 - * - * Revision 1.6 1998/04/15 16:44:35 keil - * new init code - * - * Revision 1.5 1998/02/02 13:29:46 keil - * fast io - * - * Revision 1.4 1997/11/08 21:35:52 keil - * new l1 init - * - * Revision 1.3 1997/11/06 17:09:29 keil - * New 2.1 init code - * - * Revision 1.2 1997/10/29 18:51:18 keil - * New files - * - * Revision 1.1.2.1 1997/10/17 22:10:58 keil - * new files on 2.0 + * This file is (c) under GNU PUBLIC LICENSE * */ #define __NO_VERSION__ @@ -53,7 +16,7 @@ #include "isdnl1.h" extern const char *CardType[]; -const char *sportster_revision = "$Revision: 1.12 $"; +const char *sportster_revision = "$Revision: 1.13 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -187,11 +150,11 @@ reset_sportster(struct IsdnCardState *cs) byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq); save_flags(flags); sti(); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); 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_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); restore_flags(flags); } diff --git a/drivers/isdn/hisax/tei.c b/drivers/isdn/hisax/tei.c index 8454f1554ac5..2a84aa2d2bbe 100644 --- a/drivers/isdn/hisax/tei.c +++ b/drivers/isdn/hisax/tei.c @@ -1,5 +1,5 @@ -/* $Id: tei.c,v 2.13 1999/07/21 14:46:28 keil Exp $ - +/* $Id: tei.c,v 2.15 2000/06/26 08:59:14 keil Exp $ + * * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden * @@ -10,75 +10,13 @@ * Thanks to Jan den Ouden * 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 - * - * Revision 2.10 1998/05/25 14:08:10 keil - * HiSax 3.0 - * fixed X.75 and leased line to work again - * Point2Point and fixed TEI are runtime options now: - * hisaxctrl 7 1 set PTP - * hisaxctrl 8 - * set fixed TEI to TEIVALUE (0-63) - * - * Revision 2.9 1998/05/25 12:58:23 keil - * HiSax golden code from certification, Don't use !!! - * No leased lines, no X75, but many changes. - * - * Revision 2.8 1998/03/07 22:57:07 tsbogend - * made HiSax working on Linux/Alpha - * - * Revision 2.7 1998/02/12 23:08:11 keil - * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() - * - * Revision 2.6 1998/02/02 13:41:42 keil - * fix MDL_ASSIGN for PtP - * - * Revision 2.5 1997/11/06 17:09:12 keil - * New 2.1 init code - * - * Revision 2.4 1997/10/29 19:04:46 keil - * changes for 2.1 - * - * Revision 2.3 1997/10/01 09:21:43 fritz - * Removed old compatibility stuff for 2.0.X kernels. - * From now on, this code is for 2.1.X ONLY! - * Old stuff is still in the separate branch. - * - * Revision 2.2 1997/07/31 19:24:39 keil - * fixed a warning - * - * Revision 2.1 1997/07/31 11:50:16 keil - * ONE TEI and FIXED TEI handling - * - * Revision 2.0 1997/07/27 21:13:30 keil - * New TEI managment - * - * Revision 1.9 1997/06/26 11:18:02 keil - * New managment - * - * Revision 1.8 1997/04/07 22:59:08 keil - * GFP_KERNEL --> GFP_ATOMIC - * - * Revision 1.7 1997/04/06 22:54:03 keil - * Using SKB's - * - * Old log removed/ KKe - * */ #define __NO_VERSION__ #include "hisax.h" #include "isdnl2.h" #include -const char *tei_revision = "$Revision: 2.13 $"; +const char *tei_revision = "$Revision: 2.15 $"; #define ID_REQUEST 1 #define ID_ASSIGNED 2 @@ -216,7 +154,7 @@ tei_id_assign(struct FsmInst *fi, int event, void *arg) if (st->ma.debug) st->ma.tei_m.printdebug(&st->ma.tei_m, "identity assign ri %d tei %d", ri, tei); - if ((ost = findtei(st, tei))) { /* same tei is in use */ + if ((ost = findtei(st, tei))) { /* same tei is in use */ if (ri != ost->ma.ri) { st->ma.tei_m.printdebug(&st->ma.tei_m, "possible duplicate assignment tei %d", tei); @@ -243,10 +181,12 @@ tei_id_test_dup(struct FsmInst *fi, int event, void *arg) 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); + if ((ost = findtei(st, tei))) { /* same tei is in use */ + if (ri != ost->ma.ri) { /* and it wasn't our request */ + st->ma.tei_m.printdebug(&st->ma.tei_m, + "possible duplicate assignment tei %d", tei); + FsmEvent(&ost->ma.tei_m, EV_VERIFY, NULL); + } } } diff --git a/drivers/isdn/hisax/teleint.c b/drivers/isdn/hisax/teleint.c index f741571b1eaf..90e0e2ec5dac 100644 --- a/drivers/isdn/hisax/teleint.c +++ b/drivers/isdn/hisax/teleint.c @@ -1,49 +1,10 @@ -/* $Id: teleint.c,v 1.12 1999/12/19 13:09:42 keil Exp $ - +/* $Id: teleint.c,v 1.13 2000/06/26 08:59:14 keil Exp $ + * * teleint.c low level stuff for TeleInt isdn cards * * Author Karsten Keil (keil@isdn4linux.de) * - * - * $Log: teleint.c,v $ - * Revision 1.12 1999/12/19 13:09:42 keil - * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for - * signal proof delays - * - * Revision 1.11 1999/09/04 06:20:06 keil - * Changes from kernel set_current_state() - * - * Revision 1.10 1999/08/31 11:20:27 paul - * various spelling corrections (new checksums may be needed, Karsten!) - * - * 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 - * - * Revision 1.6 1998/04/15 16:45:31 keil - * new init code - * - * Revision 1.5 1998/02/02 13:40:47 keil - * fast io - * - * Revision 1.4 1997/11/08 21:35:53 keil - * new l1 init - * - * Revision 1.3 1997/11/06 17:09:30 keil - * New 2.1 init code - * - * Revision 1.2 1997/10/29 18:55:53 keil - * changes for 2.1.60 (irq2dev_map) - * - * Revision 1.1 1997/09/11 17:32:32 keil - * new - * + * This file is (c) under GNU PUBLIC LICENSE * */ @@ -55,7 +16,7 @@ extern const char *CardType[]; -const char *TeleInt_revision = "$Revision: 1.12 $"; +const char *TeleInt_revision = "$Revision: 1.13 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -264,11 +225,11 @@ reset_TeleInt(struct IsdnCardState *cs) byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm); /* Reset On */ save_flags(flags); sti(); - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); 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_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); restore_flags(flags); } diff --git a/drivers/isdn/hisax/teles0.c b/drivers/isdn/hisax/teles0.c index 710f30009167..1a90172c5e23 100644 --- a/drivers/isdn/hisax/teles0.c +++ b/drivers/isdn/hisax/teles0.c @@ -1,5 +1,5 @@ -/* $Id: teles0.c,v 2.11 1999/12/23 15:09:32 keil Exp $ - +/* $Id: teles0.c,v 2.12 2000/06/26 08:59:14 keil Exp $ + * * teles0.c low level stuff for Teles Memory IO isdn cards * based on the teles driver from Jan den Ouden * @@ -9,51 +9,7 @@ * Fritz Elfert * Beat Doebeli * - * $Log: teles0.c,v $ - * Revision 2.11 1999/12/23 15:09:32 keil - * change email - * - * Revision 2.10 1999/11/14 23:37:03 keil - * new ISA memory mapped IO - * - * 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 - * - * Revision 2.7 1998/03/07 22:57:08 tsbogend - * made HiSax working on Linux/Alpha - * - * Revision 2.6 1998/02/03 23:27:47 keil - * IRQ 9 - * - * Revision 2.5 1998/02/02 13:29:47 keil - * fast io - * - * Revision 2.4 1997/11/08 21:35:54 keil - * new l1 init - * - * Revision 2.3 1997/11/06 17:09:31 keil - * New 2.1 init code - * - * Revision 2.2 1997/10/29 18:55:57 keil - * changes for 2.1.60 (irq2dev_map) - * - * Revision 2.1 1997/07/27 21:47:10 keil - * new interface structures - * - * Revision 2.0 1997/06/26 11:02:43 keil - * New Layer and card interface - * - * Revision 1.8 1997/04/13 19:54:04 keil - * Change in IRQ check delay for SMP - * - * Revision 1.7 1997/04/06 22:54:04 keil - * Using SKB's - * - * removed old log info /KKe + * This file is (c) under GNU PUBLIC LICENSE * */ #define __NO_VERSION__ @@ -64,7 +20,7 @@ extern const char *CardType[]; -const char *teles0_revision = "$Revision: 2.11 $"; +const char *teles0_revision = "$Revision: 2.12 $"; #define TELES_IOMEM_SIZE 0x400 #define byteout(addr,val) outb(val,addr) diff --git a/drivers/isdn/hisax/teles3.c b/drivers/isdn/hisax/teles3.c index 171aeb07d72f..3cdbfbeb5565 100644 --- a/drivers/isdn/hisax/teles3.c +++ b/drivers/isdn/hisax/teles3.c @@ -1,5 +1,5 @@ -/* $Id: teles3.c,v 2.15 2000/02/03 16:40:10 keil Exp $ - +/* $Id: teles3.c,v 2.16 2000/06/26 08:59:15 keil Exp $ + * * teles3.c low level stuff for Teles 16.3 & PNP isdn cards * * based on the teles driver from Jan den Ouden @@ -10,81 +10,7 @@ * Fritz Elfert * Beat Doebeli * - * $Log: teles3.c,v $ - * Revision 2.15 2000/02/03 16:40:10 keil - * Fix teles pcmcia - * - * Revision 2.14 1999/12/23 15:09:32 keil - * change email - * - * Revision 2.13 1999/08/30 12:01:28 keil - * HW version v1.3 support - * - * 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 - * - * Revision 2.9 1999/02/15 14:11:02 cpetig - * fixed a bug with Teles PCMCIA, it doesn't have a config register - * - * Revision 2.8 1998/04/15 16:44:30 keil - * new init code - * - * Revision 2.7 1998/02/02 13:29:48 keil - * fast io - * - * Revision 2.6 1997/11/13 16:22:44 keil - * COMPAQ_ISA reset - * - * Revision 2.5 1997/11/12 15:01:25 keil - * COMPAQ_ISA changes - * - * Revision 2.4 1997/11/08 21:35:56 keil - * new l1 init - * - * Revision 2.3 1997/11/06 17:09:33 keil - * New 2.1 init code - * - * Revision 2.2 1997/10/29 18:55:59 keil - * changes for 2.1.60 (irq2dev_map) - * - * Revision 2.1 1997/07/27 21:47:12 keil - * new interface structures - * - * Revision 2.0 1997/06/26 11:02:46 keil - * New Layer and card interface - * - * Revision 1.11 1997/04/13 19:54:05 keil - * Change in IRQ check delay for SMP - * - * Revision 1.10 1997/04/06 22:54:05 keil - * Using SKB's - * - * Revision 1.9 1997/03/22 02:01:07 fritz - * -Reworked toplevel Makefile. From now on, no different Makefiles - * for standalone- and in-kernel-compilation are needed any more. - * -Added local Rules.make for above reason. - * -Experimental changes in teles3.c for enhanced IRQ-checking with - * 2.1.X and SMP kernels. - * -Removed diffstd-script, same functionality is in stddiff -r. - * -Enhanced scripts std2kern and stddiff. - * - * Revision 1.8 1997/02/23 18:43:55 fritz - * Added support for Teles-Vision. - * - * Revision 1.7 1997/01/28 22:48:33 keil - * fixes for Teles PCMCIA (Christof Petig) - * - * Revision 1.6 1997/01/27 15:52:55 keil - * SMP proof,cosmetics, PCMCIA added - * - * removed old log info /KKe + * This file is (c) under GNU PUBLIC LICENSE * */ #define __NO_VERSION__ @@ -94,7 +20,7 @@ #include "isdnl1.h" extern const char *CardType[]; -const char *teles3_revision = "$Revision: 2.15 $"; +const char *teles3_revision = "$Revision: 2.16 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) diff --git a/drivers/isdn/hisax/telespci.c b/drivers/isdn/hisax/telespci.c index 1a961e4e5db9..9b63695691d7 100644 --- a/drivers/isdn/hisax/telespci.c +++ b/drivers/isdn/hisax/telespci.c @@ -1,44 +1,11 @@ -/* $Id: telespci.c,v 2.11 1999/12/23 15:09:32 keil Exp $ - +/* $Id: telespci.c,v 2.13 2000/06/26 08:59:15 keil Exp $ + * * telespci.c low level stuff for Teles PCI isdn cards * * Author Ton van Rosmalen * Karsten Keil (keil@isdn4linux.de) * - * - * $Log: telespci.c,v $ - * Revision 2.11 1999/12/23 15:09:32 keil - * change email - * - * Revision 2.10 1999/11/15 14:20:05 keil - * 64Bit compatibility - * - * 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 - * - * Revision 2.4 1998/10/05 09:38:08 keil - * Fix register addressing - * - * Revision 2.3 1998/05/25 12:58:26 keil - * HiSax golden code from certification, Don't use !!! - * No leased lines, no X75, but many changes. - * - * Revision 2.1 1998/04/15 16:38:23 keil - * Add S0Box and Teles PCI support - * + * This file is (c) under GNU PUBLIC LICENSE * */ #define __NO_VERSION__ @@ -50,7 +17,7 @@ #include extern const char *CardType[]; -const char *telespci_revision = "$Revision: 2.11 $"; +const char *telespci_revision = "$Revision: 2.13 $"; #define ZORAN_PO_RQ_PEN 0x02000000 #define ZORAN_PO_WR 0x00800000 @@ -60,6 +27,12 @@ const char *telespci_revision = "$Revision: 2.11 $"; #define ZORAN_PO_GREG1 0x00010000 #define ZORAN_PO_DMASK 0xFF +#ifndef PCI_VENDOR_ID_ZORAN +#define PCI_VENDOR_ID_ZORAN 0x11DE +#endif +#ifndef PCI_DEVICE_ID_ZORAN_36120 +#define PCI_DEVICE_ID_ZORAN_36120 0x6120 +#endif #define WRITE_ADDR_ISAC (ZORAN_PO_WR | ZORAN_PO_GID0 | ZORAN_PO_GREG0) #define READ_DATA_ISAC (ZORAN_PO_GID0 | ZORAN_PO_GREG1) #define WRITE_DATA_ISAC (ZORAN_PO_WR | ZORAN_PO_GID0 | ZORAN_PO_GREG1) @@ -315,6 +288,9 @@ setup_telespci(struct IsdnCard *card)) struct IsdnCardState *cs = card->cs; char tmp[64]; +#ifdef __BIG_ENDIAN +#error "not running on big endian machines now" +#endif strcpy(tmp, telespci_revision); printk(KERN_INFO "HiSax: Teles/PCI driver Rev. %s\n", HiSax_getrev(tmp)); if (cs->typ != ISDN_CTYPE_TELESPCI) @@ -324,16 +300,18 @@ setup_telespci(struct IsdnCard *card)) printk(KERN_ERR "TelesPCI: no PCI bus present\n"); return(0); } - if ((dev_tel = pci_find_device (0x11DE, 0x6120, dev_tel))) { + if ((dev_tel = pci_find_device (PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36120, dev_tel))) { + if (pci_enable_device(dev_tel)) + return(0); cs->irq = dev_tel->irq; if (!cs->irq) { printk(KERN_WARNING "Teles: No IRQ for PCI card found\n"); return(0); } - cs->hw.teles0.membase = (u_long) ioremap(dev_tel->base_address[ 0], + cs->hw.teles0.membase = (u_long) ioremap(dev_tel->base_address[ 0] & PCI_BASE_ADDRESS_MEM_MASK, PAGE_SIZE); printk(KERN_INFO "Found: Zoran, base-address: 0x%lx, irq: 0x%x\n", - dev_tel->base_address[ 0], dev_tel->irq); + dev_tel->base_address[ 0] & PCI_BASE_ADDRESS_MEM_MASK, dev_tel->irq); } else { printk(KERN_WARNING "TelesPCI: No PCI card found\n"); return(0); diff --git a/drivers/isdn/hisax/w6692.c b/drivers/isdn/hisax/w6692.c index 210ed8b2b408..ef6c1918ea53 100644 --- a/drivers/isdn/hisax/w6692.c +++ b/drivers/isdn/hisax/w6692.c @@ -1,5 +1,5 @@ -/* $Id: w6692.c,v 1.4 2000/03/16 23:24:11 werner Exp $ - +/* $Id: w6692.c,v 1.8 2000/09/07 20:33:30 werner Exp $ + * * w6692.c Winbond W6692 specific routines * * Author Petr Novak @@ -7,23 +7,6 @@ * * This file is (c) under GNU PUBLIC LICENSE * - * $Log: w6692.c,v $ - * Revision 1.4 2000/03/16 23:24:11 werner - * - * Fixed an additional location - * - * Revision 1.3 2000/03/16 22:41:36 werner - * - * Tried to fix second B-channel problem (still not tested) - * - * Revision 1.2 2000/02/26 00:35:13 keil - * Fix skb freeing in interrupt context - * - * Revision 1.1 1999/09/04 06:28:58 keil - * first revision - * - * - * */ #include @@ -34,13 +17,18 @@ #include #include -#define PCI_VEND_ASUSCOM 0x675 -#define PCI_DEV_ASUSCOMPCI1 0x1702 +#ifndef PCI_VENDOR_ID_ASUSCOM +#define PCI_VENDOR_ID_ASUSCOM 0x675 +#endif +#ifndef PCI_DEVICE_ID_ASUSCOM_TA1 +#define PCI_DEVICE_ID_ASUSCOM_TA1 0x1702 +#endif #ifndef PCI_VENDOR_ID_WINBOND2 #define PCI_VENDOR_ID_WINBOND2 0x1050 #endif -#define PCI_DEVICE_W6692 0x6692 - +#ifndef PCI_DEVICE_ID_WINBOND_6692 +#define PCI_DEVICE_ID_WINBOND_6692 0x6692 +#endif /* table entry in the PCI devices list */ typedef struct { int vendor_id; @@ -51,14 +39,14 @@ typedef struct { static const PCI_ENTRY id_list[] = { - {PCI_VEND_ASUSCOM, PCI_DEV_ASUSCOMPCI1, "AsusCom", "TA XXX"}, - {PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_W6692, "Winbond", "W6692"}, + {PCI_VENDOR_ID_ASUSCOM, PCI_DEVICE_ID_ASUSCOM_TA1, "AsusCom", "TA XXX"}, + {PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND_6692, "Winbond", "W6692"}, {0, 0, NULL, NULL} }; extern const char *CardType[]; -const char *w6692_revision = "$Revision: 1.4 $"; +const char *w6692_revision = "$Revision: 1.8 $"; #define DBUSY_TIMER_VALUE 80 @@ -256,7 +244,7 @@ W6692B_empty_fifo(struct BCState *bcs, int count) if (bcs->hw.w6692.rcvidx + count > HSCX_BUFMAX) { if (cs->debug & L1_DEB_WARN) debugl1(cs, "W6692B_empty_fifo: incoming packet too large"); - cs->BC_Write_Reg(cs, bcs->hw.w6692.bchan, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); + cs->BC_Write_Reg(cs, bcs->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); bcs->hw.w6692.rcvidx = 0; return; } @@ -264,14 +252,14 @@ W6692B_empty_fifo(struct BCState *bcs, int count) bcs->hw.w6692.rcvidx += count; save_flags(flags); cli(); - READW6692BFIFO(cs, bcs->hw.w6692.bchan, ptr, count); - cs->BC_Write_Reg(cs, bcs->hw.w6692.bchan, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); + READW6692BFIFO(cs, bcs->channel, ptr, count); + cs->BC_Write_Reg(cs, bcs->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); restore_flags(flags); if (cs->debug & L1_DEB_HSCX_FIFO) { char *t = bcs->blog; t += sprintf(t, "W6692B_empty_fifo %c cnt %d", - bcs->hw.w6692.bchan ? 'B' : 'A', count); + bcs->channel + '1', count); QuickHex(t, ptr, count); debugl1(cs, bcs->blog); } @@ -307,14 +295,14 @@ W6692B_fill_fifo(struct BCState *bcs) skb_pull(bcs->tx_skb, count); bcs->tx_cnt -= count; bcs->hw.w6692.count += count; - WRITEW6692BFIFO(cs, bcs->hw.w6692.bchan, ptr, count); - cs->BC_Write_Reg(cs, bcs->hw.w6692.bchan, W_B_CMDR, W_B_CMDR_RACT | W_B_CMDR_XMS | (more ? 0 : W_B_CMDR_XME)); + WRITEW6692BFIFO(cs, bcs->channel, ptr, count); + cs->BC_Write_Reg(cs, bcs->channel, W_B_CMDR, W_B_CMDR_RACT | W_B_CMDR_XMS | (more ? 0 : W_B_CMDR_XME)); restore_flags(flags); if (cs->debug & L1_DEB_HSCX_FIFO) { char *t = bcs->blog; t += sprintf(t, "W6692B_fill_fifo %c cnt %d", - bcs->hw.w6692.bchan ? 'B' : 'A', count); + bcs->channel + '1', count); QuickHex(t, ptr, count); debugl1(cs, bcs->blog); } @@ -325,13 +313,11 @@ W6692B_interrupt(struct IsdnCardState *cs, u_char bchan) { u_char val; u_char r; - struct BCState *bcs = cs->bcs; + struct BCState *bcs; struct sk_buff *skb; int count; - if (bcs->channel != bchan) - bcs++; /* hardware bchan must match ! */ - + bcs = (cs->bcs->channel == bchan) ? cs->bcs : (cs->bcs+1); val = cs->BC_Read_Reg(cs, bchan, W_B_EXIR); debugl1(cs, "W6692B chan %d B_EXIR 0x%02X", bchan, val); @@ -418,7 +404,7 @@ W6692B_interrupt(struct IsdnCardState *cs, u_char bchan) bcs->tx_cnt += bcs->hw.w6692.count; bcs->hw.w6692.count = 0; } - cs->BC_Write_Reg(cs, bcs->hw.w6692.bchan, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT); + cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT); if (cs->debug & L1_DEB_WARN) debugl1(cs, "W6692 B EXIR %x Lost TX", val); } @@ -732,18 +718,16 @@ dbusy_timer_handler(struct IsdnCardState *cs) } static void -W6692Bmode(struct BCState *bcs, int mode, int bc) +W6692Bmode(struct BCState *bcs, int mode, int bchan) { struct IsdnCardState *cs = bcs->cs; - int bchan = bc; - - bcs->hw.w6692.bchan = bc; if (cs->debug & L1_DEB_HSCX) debugl1(cs, "w6692 %c mode %d ichan %d", - '1' + bchan, mode, bc); + '1' + bchan, mode, bchan); bcs->mode = mode; - bcs->channel = bc; + bcs->channel = bchan; + bcs->hw.w6692.bchan = bchan; switch (mode) { case (L1_MODE_NULL): @@ -912,8 +896,6 @@ HISAX_INITFUNC(void initW6692(struct IsdnCardState *cs, int part)) cs->bcs[1].BC_SetStack = setstack_w6692; cs->bcs[0].BC_Close = close_w6692state; cs->bcs[1].BC_Close = close_w6692state; - cs->bcs[0].hw.w6692.bchan = 0; - cs->bcs[1].hw.w6692.bchan = 1; W6692Bmode(cs->bcs, 0, 0); W6692Bmode(cs->bcs + 1, 0, 0); } @@ -996,6 +978,9 @@ __initfunc(int setup_w6692(struct IsdnCard *card)) u_char pci_irq = 0; u_int pci_ioaddr = 0; +#ifdef __BIG_ENDIAN +#error "not running on big endian machines now" +#endif strcpy(tmp, w6692_revision); printk(KERN_INFO "HiSax: W6692 driver Rev. %s\n", HiSax_getrev(tmp)); if (cs->typ != ISDN_CTYPE_W6692) @@ -1009,8 +994,11 @@ __initfunc(int setup_w6692(struct IsdnCard *card)) dev_w6692 = pci_find_device(id_list[id_idx].vendor_id, id_list[id_idx].device_id, dev_w6692); - if (dev_w6692) + if (dev_w6692) { + if (pci_enable_device(dev_w6692)) + continue; break; + } id_idx++; } if (dev_w6692) { @@ -1018,7 +1006,7 @@ __initfunc(int setup_w6692(struct IsdnCard *card)) pci_irq = dev_w6692->irq; /* I think address 0 is allways the configuration area */ /* and address 1 is the real IO space KKe 03.09.99 */ - pci_ioaddr = dev_w6692->base_address[ 1]; + pci_ioaddr = dev_w6692->base_address[ 1] & PCI_BASE_ADDRESS_IO_MASK; } if (!found) { printk(KERN_WARNING "W6692: No PCI card found\n"); @@ -1029,7 +1017,6 @@ __initfunc(int setup_w6692(struct IsdnCard *card)) printk(KERN_WARNING "W6692: No IRQ for PCI card found\n"); return (0); } - pci_ioaddr &= PCI_BASE_ADDRESS_IO_MASK; if (!pci_ioaddr) { printk(KERN_WARNING "W6692: NO I/O Base Address found\n"); return (0); @@ -1069,6 +1056,7 @@ __initfunc(int setup_w6692(struct IsdnCard *card)) cs->BC_Send_Data = &W6692B_fill_fifo; cs->cardmsg = &w6692_card_msg; cs->irq_func = &W6692_interrupt; + cs->irq_flags |= SA_SHIRQ; W6692Version(cs, "W6692:"); printk(KERN_INFO "W6692 ISTA=0x%X\n", ReadW6692(cs, W_ISTA)); printk(KERN_INFO "W6692 IMASK=0x%X\n", ReadW6692(cs, W_IMASK)); diff --git a/drivers/isdn/hisax/w6692.h b/drivers/isdn/hisax/w6692.h index 4990b008263a..5c28d3bac0fa 100644 --- a/drivers/isdn/hisax/w6692.h +++ b/drivers/isdn/hisax/w6692.h @@ -1,18 +1,10 @@ -/* $Id: w6692.h,v 1.1 1999/09/04 06:28:58 keil Exp $ - +/* $Id: w6692.h,v 1.2 2000/06/26 08:59:15 keil Exp $ + * * w6692.h Winbond W6692 specific defines * * Author Petr Novak * - * - * $Log: w6692.h,v $ - * Revision 1.1 1999/09/04 06:28:58 keil - * first revision - * - * - * Revision 1.0 1999/08/28 21:58:00 pnovak - * first version - * + * This file is (c) under GNU PUBLIC LICENSE * */ diff --git a/drivers/isdn/isdn_common.c b/drivers/isdn/isdn_common.c index b366dbb6c23c..e4d034789111 100644 --- a/drivers/isdn/isdn_common.c +++ b/drivers/isdn/isdn_common.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "isdn_common.h" #include "isdn_tty.h" #include "isdn_net.h" @@ -437,6 +438,7 @@ isdn_status_callback(isdn_ctrl * c) int r; int retval = 0; isdn_ctrl cmd; + isdn_net_dev *p; di = c->driver; i = isdn_dc2minor(di, c->arg); @@ -515,9 +517,16 @@ isdn_status_callback(isdn_ctrl * c) cmd.driver = di; cmd.arg = c->arg; cmd.command = ISDN_CMD_ACCEPTD; - isdn_command(&cmd); - retval = 1; + for ( p = dev->netdev; p; p = p->next ) + if ( p->local->isdn_channel == cmd.arg ) + { + strcpy( cmd.parm.setup.eazmsn, p->local->msn ); + isdn_command(&cmd); + retval = 1; + break; + } break; + case 2: /* For calling back, first reject incoming call ... */ case 3: /* Interface found, but down, reject call actively */ retval = 2; @@ -966,6 +975,7 @@ isdn_read(struct file *file, char *buf, size_t count, loff_t * off) ulong flags; int drvidx; int chidx; + int retval; char *p; if (off != &file->f_pos) @@ -973,47 +983,69 @@ isdn_read(struct file *file, char *buf, size_t count, loff_t * off) if (minor == ISDN_MINOR_STATUS) { if (!file->private_data) { - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto out; + } interruptible_sleep_on(&(dev->info_waitq)); } p = isdn_statstr(); file->private_data = 0; if ((len = strlen(p)) <= count) { - if (copy_to_user(buf, p, len)) - return -EFAULT; + if (copy_to_user(buf, p, len)) { + retval = -EFAULT; + goto out; + } *off += len; - return len; + retval = len; + goto out; } - return 0; + retval = 0; + goto out; + } + if (!dev->drivers) { + retval = -ENODEV; + goto out; } - if (!dev->drivers) - return -ENODEV; if (minor < ISDN_MINOR_CTRL) { + printk(KERN_WARNING "isdn_read minor %d obsolete!\n", minor); drvidx = isdn_minor2drv(minor); - if (drvidx < 0) - return -ENODEV; - if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) - return -ENODEV; + if (drvidx < 0) { + retval = -ENODEV; + goto out; + } + if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) { + retval = -ENODEV; + goto out; + } chidx = isdn_minor2chan(minor); - if( ! (p = kmalloc(count,GFP_KERNEL)) ) return -ENOMEM; + if (!(p = kmalloc(count, GFP_KERNEL))) { + retval = -ENOMEM; + goto out; + } save_flags(flags); cli(); len = isdn_readbchan(drvidx, chidx, p, 0, count, &dev->drv[drvidx]->rcv_waitq[chidx]); *off += len; restore_flags(flags); - if( copy_to_user(buf,p,len) ) len = -EFAULT; + if (copy_to_user(buf,p,len)) + len = -EFAULT; kfree(p); - return len; + retval = len; + goto out; } if (minor <= ISDN_MINOR_CTRLMAX) { drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); - if (drvidx < 0) - return -ENODEV; + if (drvidx < 0) { + retval = -ENODEV; + goto out; + } if (!dev->drv[drvidx]->stavail) { - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto out; + } interruptible_sleep_on(&(dev->drv[drvidx]->st_waitq)); } if (dev->drv[drvidx]->interface->readstat) @@ -1030,13 +1062,18 @@ isdn_read(struct file *file, char *buf, size_t count, loff_t * off) dev->drv[drvidx]->stavail = 0; restore_flags(flags); *off += len; - return len; + retval = len; + goto out; } #ifdef CONFIG_ISDN_PPP - if (minor <= ISDN_MINOR_PPPMAX) - return (isdn_ppp_read(minor - ISDN_MINOR_PPP, file, buf, count)); + if (minor <= ISDN_MINOR_PPPMAX) { + retval = isdn_ppp_read(minor - ISDN_MINOR_PPP, file, buf, count); + goto out; + } #endif - return -ENODEV; + retval = -ENODEV; +out: + return retval; } static loff_t @@ -1051,6 +1088,7 @@ isdn_write(struct file *file, const char *buf, size_t count, loff_t * off) uint minor = MINOR(file->f_dentry->d_inode->i_rdev); int drvidx; int chidx; + int retval; if (off != &file->f_pos) return -ESPIPE; @@ -1059,21 +1097,30 @@ isdn_write(struct file *file, const char *buf, size_t count, loff_t * off) return -EPERM; if (!dev->drivers) return -ENODEV; + if (minor < ISDN_MINOR_CTRL) { + printk(KERN_WARNING "isdn_write minor %d obsolete!\n", minor); drvidx = isdn_minor2drv(minor); - if (drvidx < 0) - return -ENODEV; - if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) - return -ENODEV; + if (drvidx < 0) { + retval = -ENODEV; + goto out; + } + if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) { + retval = -ENODEV; + goto out; + } chidx = isdn_minor2chan(minor); while (isdn_writebuf_stub(drvidx, chidx, buf, count, 1) != count) interruptible_sleep_on(&dev->drv[drvidx]->snd_waitq[chidx]); - return count; + retval = count; + goto out; } if (minor <= ISDN_MINOR_CTRLMAX) { drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); - if (drvidx < 0) - return -ENODEV; + if (drvidx < 0) { + retval = -ENODEV; + goto out; + } /* * We want to use the isdnctrl device to load the firmware * @@ -1081,16 +1128,21 @@ isdn_write(struct file *file, const char *buf, size_t count, loff_t * off) return -ENODEV; */ if (dev->drv[drvidx]->interface->writecmd) - return (dev->drv[drvidx]->interface-> - writecmd(buf, count, 1, drvidx, isdn_minor2chan(minor))); + retval = dev->drv[drvidx]->interface-> + writecmd(buf, count, 1, drvidx, isdn_minor2chan(minor)); else - return count; + retval = count; + goto out; } #ifdef CONFIG_ISDN_PPP - if (minor <= ISDN_MINOR_PPPMAX) - return (isdn_ppp_write(minor - ISDN_MINOR_PPP, file, buf, count)); + if (minor <= ISDN_MINOR_PPPMAX) { + retval = isdn_ppp_write(minor - ISDN_MINOR_PPP, file, buf, count); + goto out; + } #endif - return -ENODEV; + retval = -ENODEV; + out: + return retval; } static unsigned int @@ -1106,26 +1158,30 @@ isdn_poll(struct file *file, poll_table * wait) if (file->private_data) { mask |= POLLIN | POLLRDNORM; } - return mask; + goto out; } if (minor >= ISDN_MINOR_CTRL && minor <= ISDN_MINOR_CTRLMAX) { if (drvidx < 0) { /* driver deregistered while file open */ - return POLLHUP; + mask = POLLHUP; + goto out; } poll_wait(file, &(dev->drv[drvidx]->st_waitq), wait); mask = POLLOUT | POLLWRNORM; if (dev->drv[drvidx]->stavail) { mask |= POLLIN | POLLRDNORM; } - return mask; + goto out; } #ifdef CONFIG_ISDN_PPP - if (minor <= ISDN_MINOR_PPPMAX) - return (isdn_ppp_poll(file, wait)); + if (minor <= ISDN_MINOR_PPPMAX) { + mask = isdn_ppp_poll(file, wait); + goto out; + } #endif - printk(KERN_ERR "isdn_common: isdn_poll 2 -> what the hell\n"); - return POLLERR; + mask = POLLERR; + out: + return mask; } @@ -1603,6 +1659,7 @@ isdn_open(struct inode *ino, struct file *filep) if (!dev->channels) return -ENODEV; if (minor < ISDN_MINOR_CTRL) { + printk(KERN_WARNING "isdn_open minor %d obsolete!\n", minor); drvidx = isdn_minor2drv(minor); if (drvidx < 0) return -ENODEV; @@ -2293,11 +2350,13 @@ cleanup_module(void) } if (unregister_chrdev(ISDN_MAJOR, "isdn") != 0) { printk(KERN_WARNING "isdn: controldevice busy, remove cancelled\n"); + restore_flags(flags); } else { del_timer(&dev->timer); + restore_flags(flags); + /* call vfree with interrupts enabled, else it will hang */ vfree(dev); printk(KERN_NOTICE "ISDN-subsystem unloaded\n"); } - restore_flags(flags); } #endif diff --git a/drivers/isdn/isdn_concap.c b/drivers/isdn/isdn_concap.c index 70a2feadaa85..8b971a7700be 100644 --- a/drivers/isdn/isdn_concap.c +++ b/drivers/isdn/isdn_concap.c @@ -52,15 +52,19 @@ int isdn_concap_dl_data_req(struct concap_proto *concap, struct sk_buff *skb) { - int tmp; struct device *ndev = concap -> net_dev; - isdn_net_local *lp = (isdn_net_local *) ndev->priv; + isdn_net_dev *nd = ((isdn_net_local *) ndev->priv)->netdev; + isdn_net_local *lp = isdn_net_get_locked_lp(nd); IX25DEBUG( "isdn_concap_dl_data_req: %s \n", concap->net_dev->name); + if (!lp) { + IX25DEBUG( "isdn_concap_dl_data_req: %s : isdn_net_send_skb returned %d\n", concap -> net_dev -> name, 1); + return 1; + } lp->huptimer = 0; - tmp=isdn_net_send_skb(ndev, lp, skb); - IX25DEBUG( "isdn_concap_dl_data_req: %s : isdn_net_send_skb returned %d\n", concap -> net_dev -> name, tmp); - return tmp; + isdn_net_writebuf_skb(lp, skb); + IX25DEBUG( "isdn_concap_dl_data_req: %s : isdn_net_send_skb returned %d\n", concap -> net_dev -> name, 0); + return 0; } diff --git a/drivers/isdn/isdn_net.c b/drivers/isdn/isdn_net.c index 8787e34a7a2a..2266659317b5 100644 --- a/drivers/isdn/isdn_net.c +++ b/drivers/isdn/isdn_net.c @@ -1,4 +1,4 @@ -/* $Id: isdn_net.c,v 1.137 2000/09/12 19:43:47 kai Exp $ +/* $Id: isdn_net.c,v 1.140 2000/11/01 17:54:01 detabc Exp $ * Linux ISDN subsystem, network interfaces and related functions (linklevel). * @@ -72,7 +72,7 @@ * Find out if the netdevice has been ifup-ed yet. * For slaves, look at the corresponding master. */ -static int __inline__ isdn_net_started(isdn_net_dev *n) +static __inline__ int isdn_net_device_started(isdn_net_dev *n) { isdn_net_local *lp = n->local; struct device *dev; @@ -88,7 +88,7 @@ static int __inline__ isdn_net_started(isdn_net_dev *n) * wake up the network -> net_device queue. * For slaves, wake the corresponding master interface. */ -static void __inline__ isdn_net_lp_xon(isdn_net_local * lp) +static __inline__ void isdn_net_device_wake_queue(isdn_net_local *lp) { if (lp->master) netif_wake_queue(lp->master); @@ -96,6 +96,75 @@ static void __inline__ isdn_net_lp_xon(isdn_net_local * lp) netif_wake_queue(&lp->netdev->dev); } +/* + * stop the network -> net_device queue. + * For slaves, stop the corresponding master interface. + */ +static __inline__ void isdn_net_device_stop_queue(isdn_net_local *lp) +{ + if (lp->master) + netif_stop_queue(lp->master); + else + netif_stop_queue(&lp->netdev->dev); +} + +/* + * find out if the net_device which this lp belongs to (lp can be + * master or slave) is busy. It's busy iff all (master and slave) + * queues are busy + */ +static __inline__ int isdn_net_device_busy(isdn_net_local *lp) +{ + isdn_net_local *nlp; + isdn_net_dev *nd; + unsigned long flags; + + if (!isdn_net_lp_busy(lp)) + return 0; + + if (lp->master) + nd = ((isdn_net_local *) lp->master->priv)->netdev; + else + nd = lp->netdev; + + spin_lock_irqsave(&nd->queue_lock, flags); + nlp = lp->next; + while (nlp != lp) { + if (!isdn_net_lp_busy(nlp)) { + spin_unlock_irqrestore(&nd->queue_lock, flags); + return 0; + } + nlp = nlp->next; + } + spin_unlock_irqrestore(&nd->queue_lock, flags); + return 1; +} + +static __inline__ void isdn_net_inc_frame_cnt(isdn_net_local *lp) +{ + atomic_inc(&lp->frame_cnt); + if (isdn_net_device_busy(lp)) + isdn_net_device_stop_queue(lp); +} + +static __inline__ void isdn_net_dec_frame_cnt(isdn_net_local *lp) +{ + atomic_dec(&lp->frame_cnt); + + if (!(isdn_net_device_busy(lp))) { + if (!skb_queue_empty(&lp->super_tx_queue)) { + queue_task(&lp->tqueue, &tq_immediate); + } else { + isdn_net_device_wake_queue(lp); + } + } +} + +static __inline__ void isdn_net_zero_frame_cnt(isdn_net_local *lp) +{ + atomic_set(&lp->frame_cnt, 0); +} + /* For 2.2.x we leave the transmitter busy timeout at 2 secs, just * to be safe. * For 2.3.x we push it up to 20 secs, because call establishment @@ -111,9 +180,8 @@ static void __inline__ isdn_net_lp_xon(isdn_net_local * lp) 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.114 $"; +char *isdn_net_revision = "$Revision: 1.140 $"; /* * Code for raw-networking over ISDN @@ -122,7 +190,6 @@ char *isdn_net_revision = "$Revision: 1.114 $"; static void isdn_net_unreachable(struct device *dev, struct sk_buff *skb, char *reason) { - if(skb) { u_short proto = ntohs(skb->protocol); @@ -225,16 +292,12 @@ static void isdn_net_unbind_channel(isdn_net_local * lp) { ulong flags; + struct sk_buff *skb; save_flags(flags); cli(); - if (lp->first_skb) { - dev_kfree_skb(lp->first_skb); - lp->first_skb = NULL; - } - if (lp->sav_skb) { - dev_kfree_skb(lp->sav_skb); - lp->sav_skb = NULL; + while ((skb = skb_dequeue(&lp->super_tx_queue))) { + kfree_skb(skb); } if (!lp->master) { /* reset only master device */ /* Moral equivalent of dev_purge_queues(): @@ -331,6 +394,11 @@ isdn_net_autohup() isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, anymore); } +static void isdn_net_lp_disconnected(isdn_net_local *lp) +{ + isdn_net_rm_from_bundle(lp); +} + /* * Handle status-messages from ISDN-interfacecard. * This function is called from within the main-status-dispatcher @@ -354,28 +422,9 @@ isdn_net_stat_callback(int idx, isdn_ctrl *c) /* A packet has successfully been sent out */ if ((lp->flags & ISDN_NET_CONNECTED) && (!lp->dialstate)) { + isdn_net_dec_frame_cnt(lp); lp->stats.tx_packets++; lp->stats.tx_bytes += c->parm.length; - /* some HL drivers deliver - ISDN_STAT_BSENT from hw interrupt. - Output routines in isdn_ppp are now - called with irq disabled such that - dequeueing the sav_skb while another - frame is sent will not occur. - */ - if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && lp->sav_skb) { - struct device *mdev; - if (lp->master) - mdev = lp->master; - else - mdev = &lp->netdev->dev; - if (!isdn_net_send_skb(mdev, lp, lp->sav_skb)) { - lp->sav_skb = NULL; - } else { - return 1; - } - } - isdn_net_lp_xon(lp); } return 1; case ISDN_STAT_DCONN: @@ -405,8 +454,10 @@ isdn_net_stat_callback(int idx, isdn_ctrl *c) #endif /* CONFIG_ISDN_X25 */ if ((!lp->dialstate) && (lp->flags & ISDN_NET_CONNECTED)) { #ifdef CONFIG_ISDN_PPP - isdn_ppp_free(lp); + if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) + isdn_ppp_free(lp); #endif + isdn_net_lp_disconnected(lp); isdn_all_eaz(lp->isdn_device, lp->isdn_channel); printk(KERN_INFO "%s: remote hangup\n", lp->name); printk(KERN_INFO "%s: Chargesum is %d\n", lp->name, @@ -429,6 +480,7 @@ isdn_net_stat_callback(int idx, isdn_ctrl *c) #endif /* CONFIG_ISDN_X25 */ case ISDN_STAT_BCONN: /* B-Channel is up */ + isdn_net_zero_frame_cnt(lp); switch (lp->dialstate) { case 5: case 6: @@ -446,13 +498,17 @@ isdn_net_stat_callback(int idx, isdn_ctrl *c) isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 1); if (lp->p_encap == ISDN_NET_ENCAP_CISCOHDLCK) isdn_timer_ctrl(ISDN_TIMER_KEEPALIVE, 1); + if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP) { + if (lp->master) { /* is lp a slave? */ + isdn_net_dev *nd = ((isdn_net_local *)lp->master->priv)->netdev; + isdn_net_add_to_bundle(nd, lp); + } + } printk(KERN_INFO "isdn_net: %s connected\n", lp->name); /* If first Chargeinfo comes before B-Channel connect, * we correct the timestamp here. */ lp->chargetime = jiffies; - printk(KERN_DEBUG "isdn_net: chargetime of %s now %lu\n", - lp->name, lp->chargetime); /* reset dial-timeout */ lp->dialstarted = 0; @@ -468,13 +524,9 @@ isdn_net_stat_callback(int idx, isdn_ctrl *c) if( pops->connect_ind) pops->connect_ind(cprot); #endif /* CONFIG_ISDN_X25 */ - /* Immediately send first skb to speed up arp */ - if (lp->first_skb) { - - if (!(isdn_net_xmit(&p->dev, lp, lp->first_skb))) - lp->first_skb = NULL; - } - if(! lp->first_skb) isdn_net_lp_xon(lp); + /* ppp needs to do negotiations first */ + if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP) + isdn_net_device_wake_queue(lp); return 1; } break; @@ -584,7 +636,7 @@ isdn_net_dial(void) s = "dial suppressed: isdn system stopped"; else s = "dial suppressed: dialmode `off'"; - isdn_net_unreachable(&p->dev, lp->first_skb, s); + isdn_net_unreachable(&p->dev, 0, s); isdn_net_hangup(&p->dev); break; } @@ -617,7 +669,7 @@ isdn_net_dial(void) restore_flags(flags); lp->dialwait_timer = jiffies + lp->dialwait; lp->dialstarted = 0; - isdn_net_unreachable(&p->dev, lp->first_skb, "dial: timed out"); + isdn_net_unreachable(&p->dev, 0, "dial: timed out"); isdn_net_hangup(&p->dev); break; } @@ -635,7 +687,7 @@ isdn_net_dial(void) if (lp->dialtimeout == 0) { lp->dialwait_timer = jiffies + lp->dialwait; lp->dialstarted = 0; - isdn_net_unreachable(&p->dev, lp->first_skb, "dial: tried all numbers dialmax times"); + isdn_net_unreachable(&p->dev, 0, "dial: tried all numbers dialmax times"); } isdn_net_hangup(&p->dev); break; @@ -651,6 +703,7 @@ isdn_net_dial(void) i = isdn_dc2minor(lp->isdn_device, lp->isdn_channel); if (i >= 0) { strcpy(dev->num[i], cmd.parm.setup.phone); + dev->usage[i] |= ISDN_USAGE_OUTGOING; isdn_info_update(); } printk(KERN_INFO "%s: dialing %d %s...\n", lp->name, @@ -795,10 +848,21 @@ isdn_net_hangup(struct device *d) #endif if (lp->flags & ISDN_NET_CONNECTED) { + if (lp->slave != NULL) { + isdn_net_local *slp = (isdn_net_local *)lp->slave->priv; + if (slp->flags & ISDN_NET_CONNECTED) { + printk(KERN_INFO + "isdn_net: hang up slave %s before %s\n", + slp->name, lp->name); + isdn_net_hangup(lp->slave); + } + } printk(KERN_INFO "isdn_net: local hangup %s\n", lp->name); #ifdef CONFIG_ISDN_PPP - isdn_ppp_free(lp); + if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) + isdn_ppp_free(lp); #endif + isdn_net_lp_disconnected(lp); #ifdef CONFIG_ISDN_X25 /* try if there are generic encap protocol receiver routines and signal the closure of @@ -911,31 +975,81 @@ isdn_net_log_skb(struct sk_buff * skb, isdn_net_local * lp) } /* - * Generic routine to send out an skbuf. - * If lowlevel-device does not support support skbufs, use - * standard send-routine, else send directly. - * - * Return: 0 on success, !0 on failure. + * this function is used to send supervisory data, i.e. data which was + * not received from the network layer, but e.g. frames from ipppd, CCP + * reset frames etc. + */ +void isdn_net_write_super(isdn_net_local *lp, struct sk_buff *skb) +{ + if (in_interrupt()) { + // we can't grab the lock from irq context, + // so we just queue the packet + skb_queue_tail(&lp->super_tx_queue, skb); + queue_task(&lp->tqueue, &tq_immediate); + return; + } + + if (!isdn_net_lp_busy(lp)) { + isdn_net_writebuf_skb(lp, skb); + } else { + skb_queue_tail(&lp->super_tx_queue, skb); + } +} + +/* + * called from tq_immediate */ -int isdn_net_send_skb - (struct device *ndev, isdn_net_local * lp,struct sk_buff *skb) +static void isdn_net_softint(void *private) +{ + isdn_net_local *lp = private; + struct sk_buff *skb; + + while (!isdn_net_lp_busy(lp)) { + skb = skb_dequeue(&lp->super_tx_queue); + if (!skb) + break; + isdn_net_writebuf_skb(lp, skb); + } +} + +/* + * all frames sent from the (net) LL to a HL driver should go via this function + * it's serialized by the caller holding the lp->xmit_lock spinlock + */ +void isdn_net_writebuf_skb(isdn_net_local *lp, struct sk_buff *skb) { int ret; int len = skb->len; /* save len */ - ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb); - if (ret == len) { - lp->transcount += len; - return 0; + /* before obtaining the lock the caller should have checked that + the lp isn't busy */ + if (isdn_net_lp_busy(lp)) { + printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__); + goto error; } - if (ret < 0) { - dev_kfree_skb(skb); - lp->stats.tx_errors++; - return 0; + + if (!(lp->flags & ISDN_NET_CONNECTED)) { + printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__); + goto error; } - return 1; + ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb); + if (ret != len) { + /* we should never get here */ + printk(KERN_WARNING "%s: HL driver queue full\n", lp->name); + goto error; + } + + lp->transcount += len; + isdn_net_inc_frame_cnt(lp); + return; + + error: + dev_kfree_skb(skb); + lp->stats.tx_errors++; + } + /* * Helper function for isdn_net_start_xmit. * When called, the connection is already established. @@ -948,9 +1062,18 @@ int isdn_net_send_skb */ static int -isdn_net_xmit(struct device *ndev, isdn_net_local * lp, struct sk_buff *skb) +isdn_net_xmit(struct device *ndev, struct sk_buff *skb) { - int ret; + isdn_net_dev *nd; + isdn_net_local *slp; + isdn_net_local *lp = (isdn_net_local *) ndev->priv; + int retv = 0; + + if (((isdn_net_local *) (ndev->priv))->master) { + printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__); + dev_kfree_skb(skb); + return 0; + } /* For the other encaps the header has already been built */ #ifdef CONFIG_ISDN_PPP @@ -958,31 +1081,24 @@ isdn_net_xmit(struct device *ndev, isdn_net_local * lp, struct sk_buff *skb) return isdn_ppp_xmit(skb, ndev); } #endif + nd = ((isdn_net_local *) ndev->priv)->netdev; + lp = isdn_net_get_locked_lp(nd); + if (!lp) { + printk(KERN_WARNING "%s: all channels busy - requeuing!\n", ndev->name); + return 1; + } + /* we have our lp locked from now on */ + /* Reset hangup-timeout */ - lp->huptimer = 0; - if (lp->cps > lp->triggercps) { - /* Device overloaded */ + lp->huptimer = 0; // FIXME? + isdn_net_writebuf_skb(lp, skb); - /* - * Packet-delivery via round-robin over master - * and all connected slaves. - */ - if (lp->master) - /* Slaves always deliver themselves */ - ret = isdn_net_send_skb(ndev, lp, skb); - else { - isdn_net_local *slp = (isdn_net_local *) (lp->srobin->priv); - /* Master delivers via srobin and maintains srobin */ - if (lp->srobin == ndev) - ret = isdn_net_send_skb(ndev, lp, skb); - else - ret = isdn_net_start_xmit(skb, lp->srobin); - lp->srobin = (slp->slave) ? slp->slave : ndev; - slp = (isdn_net_local *) (lp->srobin->priv); - if (!((slp->flags & ISDN_NET_CONNECTED) && (slp->dialstate == 0))) - lp->srobin = ndev; - } - /* Slave-startup using delay-variable */ + /* the following stuff is here for backwards compatibility. + * in future, start-up and hangup of slaves (based on current load) + * should move to userspace and get based on an overall cps + * calculation + */ + if (lp->cps > lp->triggercps) { if (lp->slave) { if (!lp->sqfull) { /* First time overload: set timestamp only */ @@ -990,17 +1106,24 @@ isdn_net_xmit(struct device *ndev, isdn_net_local * lp, struct sk_buff *skb) lp->sqfull_stamp = jiffies; } else { /* subsequent overload: if slavedelay exceeded, start dialing */ - if ((jiffies - lp->sqfull_stamp) > lp->slavedelay) - isdn_net_force_dial_lp((isdn_net_local *) lp->slave->priv); + if ((jiffies - lp->sqfull_stamp) > lp->slavedelay) { + slp = lp->slave->priv; + if (!(slp->flags & ISDN_NET_CONNECTED)) { + isdn_net_force_dial_lp((isdn_net_local *) lp->slave->priv); + } + } } } } else { - /* Not overloaded, deliver locally */ - ret = isdn_net_send_skb(ndev, lp, skb); - if (lp->sqfull && ((jiffies - lp->sqfull_stamp) > (lp->slavedelay + (10 * HZ)))) + if (lp->sqfull && ((jiffies - lp->sqfull_stamp) > (lp->slavedelay + (10 * HZ)))) { lp->sqfull = 0; + } + /* this is a hack to allow auto-hangup for slaves on moderate loads */ + nd->queue = nd->local; } - return ret; + + return retv; + } static void @@ -1078,6 +1201,7 @@ isdn_net_start_xmit(struct sk_buff *skb, struct device *ndev) buf = skb->data; isdn_dumppkt("S:", buf, skb->len, 40); #endif + if (!(lp->flags & ISDN_NET_CONNECTED)) { int chi; /* only do autodial if allowed by config */ @@ -1150,19 +1274,11 @@ isdn_net_start_xmit(struct sk_buff *skb, struct device *ndev) return 1; /* let upper layer requeue skb packet */ } #endif - /* remember first skb to speed up arp - * when using encap ETHER - */ - if (lp->first_skb) { - printk(KERN_WARNING "isdn_net_start_xmit: First skb already set!\n"); - dev_kfree_skb(lp->first_skb); - lp->first_skb = NULL; - } - lp->first_skb = skb; /* Initiate dialing */ restore_flags(flags); isdn_net_dial(); - return 0; + isdn_net_device_stop_queue(lp); + return 1; } else { isdn_net_unreachable(ndev, skb, "No phone number"); @@ -1175,14 +1291,7 @@ isdn_net_start_xmit(struct sk_buff *skb, struct device *ndev) if (!lp->dialstate) { /* ISDN connection is established, try sending */ int ret; - if (lp->first_skb) { - if (isdn_net_xmit(ndev, lp, lp->first_skb)){ - netif_stop_queue(ndev); - return 1; -} - lp->first_skb = NULL; - } - ret = (isdn_net_xmit(ndev, lp, skb)); + ret = (isdn_net_xmit(ndev, skb)); if(ret) netif_stop_queue(ndev); return ret; } else @@ -1297,7 +1406,6 @@ 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 = alloc_skb(hl + sizeof(cisco_hdr) + sizeof(cisco_slarp), GFP_ATOMIC); unsigned long t = (jiffies / HZ * 1000000); - int len; cisco_hdr *ch; cisco_slarp *s; @@ -1325,9 +1433,7 @@ isdn_net_slarp_send(isdn_net_local *lp, int is_reply) s->rel = 0xffff; s->t1 = t >> 16; s->t0 = t & 0xffff; - len = skb->len; - if (isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 0, skb) != len) - dev_kfree_skb(skb); + isdn_net_write_super(lp, skb); } static void @@ -1411,7 +1517,6 @@ isdn_net_receive(struct device *ndev, struct sk_buff *skb) lp->stats.rx_packets++; lp->stats.rx_bytes += skb->len; } - skb->dev = ndev; skb->pkt_type = PACKET_HOST; skb->mac.raw = skb->data; @@ -1494,6 +1599,7 @@ isdn_net_receive(struct device *ndev, struct sk_buff *skb) isdn_ppp_receive(lp->netdev, olp, skb); return; #endif + default: #ifdef CONFIG_ISDN_X25 /* try if there are generic sync_device receiver routines */ @@ -1960,7 +2066,7 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup) * Is the interface up? * If not, reject the call actively. */ - if (!isdn_net_started(p)) { + if (!isdn_net_device_started(p)) { restore_flags(flags); printk(KERN_INFO "%s: incoming call, interface down -> rejected\n", lp->name); @@ -2051,6 +2157,7 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup) if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) isdn_ppp_free(lp); #endif + isdn_net_lp_disconnected(lp); isdn_free_channel(lp->isdn_device, lp->isdn_channel, ISDN_USAGE_NET); } @@ -2240,10 +2347,17 @@ isdn_net_new(char *name, struct device *master) netdev->local->magic = ISDN_NET_MAGIC; netdev->queue = netdev->local; + spin_lock_init(&netdev->queue_lock); + netdev->local->last = netdev->local; netdev->local->netdev = netdev; netdev->local->next = netdev->local; + memset(&netdev->local->tqueue, 0, sizeof(struct tq_struct)); + netdev->local->tqueue.routine = isdn_net_softint; + netdev->local->tqueue.data = netdev->local; + spin_lock_init(&netdev->local->xmit_lock); + netdev->local->isdn_device = -1; netdev->local->isdn_channel = -1; netdev->local->pre_device = -1; @@ -2251,13 +2365,11 @@ isdn_net_new(char *name, struct device *master) netdev->local->exclusive = -1; netdev->local->ppp_slot = -1; netdev->local->pppbind = -1; - netdev->local->sav_skb = NULL; - netdev->local->first_skb = NULL; + skb_queue_head_init(&netdev->local->super_tx_queue); netdev->local->l2_proto = ISDN_PROTO_L2_X75I; netdev->local->l3_proto = ISDN_PROTO_L3_TRANS; netdev->local->triggercps = 6000; netdev->local->slavedelay = 10 * HZ; - netdev->local->srobin = &netdev->dev; netdev->local->hupflags = ISDN_INHUP; /* Do hangup even on incoming calls */ netdev->local->onhtime = 10; /* Default hangup-time for saving costs of those who forget configuring this */ @@ -2295,7 +2407,7 @@ isdn_net_newslave(char *parm) if (n->local->master) return NULL; /* Master must not be started yet */ - if (isdn_net_started(n)) + if (isdn_net_device_started(n)) return NULL; return (isdn_net_new(newname, &(n->dev))); } @@ -2338,7 +2450,7 @@ isdn_net_setcfg(isdn_net_ioctl_cfg * cfg) #ifdef CONFIG_ISDN_X25 struct concap_proto * cprot = p -> cprot; #endif - if (isdn_net_started(p)) { + if (isdn_net_device_started(p)) { printk(KERN_WARNING "%s: cannot change encap when if is up\n", lp->name); return -EBUSY; @@ -2771,8 +2883,7 @@ isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q) save_flags(flags); cli(); - - if (isdn_net_started(p)) { + if (isdn_net_device_started(p)) { restore_flags(flags); return -EBUSY; } diff --git a/drivers/isdn/isdn_net.h b/drivers/isdn/isdn_net.h index 88cacea633e4..3f42a46d7f3a 100644 --- a/drivers/isdn/isdn_net.h +++ b/drivers/isdn/isdn_net.h @@ -82,8 +82,87 @@ extern void isdn_net_autohup(void); extern int isdn_net_force_hangup(char *); extern int isdn_net_force_dial(char *); extern isdn_net_dev *isdn_net_findif(char *); -extern int isdn_net_send_skb(struct device *, isdn_net_local *, - struct sk_buff *); extern int isdn_net_rcv_skb(int, struct sk_buff *); extern void isdn_net_slarp_out(void); extern int isdn_net_dial_req(isdn_net_local *); +extern void isdn_net_writebuf_skb(isdn_net_local *lp, struct sk_buff *skb); +extern void isdn_net_write_super(isdn_net_local *lp, struct sk_buff *skb); + +#define ISDN_NET_MAX_QUEUE_LENGTH 2 + +/* + * is this particular channel busy? + */ +static __inline__ int isdn_net_lp_busy(isdn_net_local *lp) +{ + if (atomic_read(&lp->frame_cnt) < ISDN_NET_MAX_QUEUE_LENGTH) + return 0; + else + return 1; +} + +/* + * For the given net device, this will get a non-busy channel out of the + * corresponding bundle. The returned channel is locked. + */ +static __inline__ isdn_net_local * isdn_net_get_locked_lp(isdn_net_dev *nd) +{ + unsigned long flags; + isdn_net_local *lp; + + spin_lock_irqsave(&nd->queue_lock, flags); + lp = nd->queue; /* get lp on top of queue */ + while (isdn_net_lp_busy(nd->queue)) { + nd->queue = nd->queue->next; + if (nd->queue == lp) { /* not found -- should never happen */ + lp = NULL; + goto errout; + } + } + lp = nd->queue; + nd->queue = nd->queue->next; +errout: + spin_unlock_irqrestore(&nd->queue_lock, flags); + return lp; +} + +/* + * add a channel to a bundle + */ +static __inline__ void isdn_net_add_to_bundle(isdn_net_dev *nd, isdn_net_local *nlp) +{ + isdn_net_local *lp; + unsigned long flags; + + spin_lock_irqsave(&nd->queue_lock, flags); + + lp = nd->queue; + nlp->last = lp->last; + lp->last->next = nlp; + lp->last = nlp; + nlp->next = lp; + nd->queue = nlp; + + spin_unlock_irqrestore(&nd->queue_lock, flags); +} +/* + * remove a channel from the bundle it belongs to + */ +static __inline__ void isdn_net_rm_from_bundle(isdn_net_local *lp) +{ + isdn_net_local *master_lp = lp; + unsigned long flags; + + if (lp->master) + master_lp = (isdn_net_local *) lp->master->priv; + + spin_lock_irqsave(&master_lp->netdev->queue_lock, flags); + lp->last->next = lp->next; + lp->next->last = lp->last; + if (master_lp->netdev->queue == lp) + master_lp->netdev->queue = lp->next; + lp->next = lp->last = lp; /* (re)set own pointers */ + spin_unlock_irqrestore(&master_lp->netdev->queue_lock, flags); +} + + diff --git a/drivers/isdn/isdn_ppp.c b/drivers/isdn/isdn_ppp.c index 24e073d6e146..eeaa9524694f 100644 --- a/drivers/isdn/isdn_ppp.c +++ b/drivers/isdn/isdn_ppp.c @@ -1,4 +1,4 @@ -/* $Id: isdn_ppp.c,v 1.63 2000/03/16 15:46:37 kai Exp $ +/* $Id: isdn_ppp.c,v 1.77 2000/06/12 16:46:34 keil Exp $ * * Linux ISDN subsystem, functions for synchronous PPP (linklevel). * @@ -20,8 +20,6 @@ * */ -#define CONFIG_ISDN_CCP 1 - #include #define __NO_VERSION__ #include @@ -85,11 +83,9 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, static void isdn_ppp_mp_cleanup( isdn_net_local * lp ); static int isdn_ppp_bundle(struct ippp_struct *, int unit); - -#define MP_UNLOCK(b) up(&(b)->lock) #endif /* CONFIG_ISDN_MPP */ -char *isdn_ppp_revision = "$Revision: 1.63 $"; +char *isdn_ppp_revision = "$Revision: 1.77 $"; static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS]; @@ -124,55 +120,28 @@ isdn_ppp_frame_log(char *info, char *data, int len, int maxlen,int unit,int slot int isdn_ppp_free(isdn_net_local * lp) { -#ifdef CONFIG_ISDN_MPP - isdn_net_local *master_lp = NULL; -#endif unsigned long flags; struct ippp_struct *is; - isdn_net_local * nlp; if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS) return 0; - is = ippp_table[lp->ppp_slot]; - save_flags(flags); cli(); -#ifdef CONFIG_ISDN_MPP - spin_lock(&lp->netdev->pb->lock); // irq_save not necessary - if (lp->master) - master_lp = (isdn_net_local *) lp->master->priv; - - - /* make sure none of the queue pointers will point to the - * interface being removed */ - for (nlp=lp->next; nlp != lp; nlp=nlp->next) - if (nlp->netdev->queue == lp ) - nlp->netdev->queue = lp->next; - - lp->last->next = lp->next; - lp->next->last = lp->last; - - if (master_lp && master_lp->netdev->queue == lp) - master_lp->netdev->queue = lp->next; -/* - if (lp->next->netdev->queue == lp) - lp->next->netdev->queue = lp->next; - if (lp->last->netdev->queue == lp) - lp->last->netdev->queue = lp->last; -*/ +#ifdef CONFIG_ISDN_MPP + spin_lock(&lp->netdev->pb->lock); +#endif + isdn_net_rm_from_bundle(lp); +#ifdef CONFIG_ISDN_MPP if (lp->netdev->pb->ref_ct == 1) /* last link in queue? */ - isdn_ppp_mp_cleanup(lp); - - lp->next = lp->last = lp; /* (re)set own pointers */ - lp->netdev->queue = lp; - - spin_unlock(&lp->netdev->pb->lock); + isdn_ppp_mp_cleanup(lp); + lp->netdev->pb->ref_ct--; - + spin_unlock(&lp->netdev->pb->lock); #endif /* CONFIG_ISDN_MPP */ + is = ippp_table[lp->ppp_slot]; if ((is->state & IPPP_CONNECT)) isdn_ppp_closewait(lp->ppp_slot); /* force wakeup on ippp device */ else if (is->state & IPPP_ASSIGNED) @@ -183,8 +152,8 @@ isdn_ppp_free(isdn_net_local * lp) is->lp = NULL; /* link is down .. set lp to NULL */ lp->ppp_slot = -1; /* is this OK ?? */ - restore_flags(flags); + restore_flags(flags); return 0; } @@ -199,12 +168,8 @@ isdn_ppp_bind(isdn_net_local * lp) long flags; struct ippp_struct *is; - if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP) - return -1; - save_flags(flags); cli(); - if (lp->pppbind < 0) { /* device bounded to ippp device ? */ isdn_net_dev *net_dev = dev->netdev; char exclusive[ISDN_MAX_CHANNELS]; /* exclusive flags */ @@ -243,7 +208,6 @@ isdn_ppp_bind(isdn_net_local * lp) } lp->ppp_slot = i; - is = ippp_table[i]; is->lp = lp; is->unit = unit; @@ -270,7 +234,7 @@ isdn_ppp_wakeup_daemon(isdn_net_local * lp) return; ippp_table[lp->ppp_slot]->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK; - + if (ippp_table[lp->ppp_slot]->wq) wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq); } @@ -805,8 +769,6 @@ isdn_ppp_write(int min, struct file *file, const char *buf, int count) lp->dialstate == 0 && (lp->flags & ISDN_NET_CONNECTED)) { unsigned short hl; - unsigned long flags; - int cnt; struct sk_buff *skb; /* * we need to reserve enought space in front of @@ -829,17 +791,7 @@ isdn_ppp_write(int min, struct file *file, const char *buf, int count) isdn_ppp_send_ccp(lp->netdev,lp,skb); /* keeps CCP/compression states in sync */ - save_flags(flags); - cli(); - if ((cnt = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb)) != count) { - if (lp->sav_skb) { - dev_kfree_skb(lp->sav_skb); - printk(KERN_INFO "isdn_ppp_write: freeing sav_skb (%d,%d)!\n", cnt, count); - } else - printk(KERN_INFO "isdn_ppp_write: Can't write PPP frame to LL (%d,%d)!\n", cnt, count); - lp->sav_skb = skb; - } - restore_flags(flags); + isdn_net_write_super(lp, skb); } } return count; @@ -954,7 +906,7 @@ void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buf if (!(is->mpppcfg & SC_REJ_MP_PROT)) { if(is->compflags & SC_LINK_DECOMP_ON) { - if(proto == PPP_LINK_COMP) { + if(proto == PPP_COMPFRAG) { if(is->debug & 0x10) printk(KERN_DEBUG "received single link compressed frame\n"); skb = isdn_ppp_decompress(skb,is,NULL,proto); @@ -1000,7 +952,6 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff return; } is = ippp_table[slot]; - if (is->debug & 0x10) { printk(KERN_DEBUG "push, skb %d %04x\n", (int) skb->len, proto); isdn_ppp_frame_log("rpush", skb->data, skb->len, 32,is->unit,lp->ppp_slot); @@ -1100,7 +1051,7 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff #endif break; case PPP_CCP: - case PPP_LINK_CCP: + case PPP_CCPFRAG: isdn_ppp_receive_ccp(net_dev,lp,skb,proto); /* Dont pop up ResetReq/Ack stuff to the daemon any longer - the job is done already */ @@ -1159,20 +1110,13 @@ static unsigned char *isdn_ppp_skb_push(struct sk_buff **skb_p,int len) int isdn_ppp_xmit(struct sk_buff *skb, struct device *netdev) { - 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 */ struct ippp_struct *ipt,*ipts; - unsigned long flags; int slot; - if (mdev) - mlp = (isdn_net_local *) (mdev->priv); - else { - mdev = netdev; - mlp = (isdn_net_local *) (netdev->priv); - } + mlp = (isdn_net_local *) (netdev->priv); nd = mlp->netdev; /* get master lp */ slot = mlp->ppp_slot; @@ -1198,21 +1142,18 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *netdev) break; default: dev_kfree_skb(skb); - printk(KERN_ERR "isdn_ppp: skipped frame with unsupported protocol: %#x.\n", skb->protocol); + printk(KERN_ERR "isdn_ppp: skipped unsupported protocol: %#x.\n", + skb->protocol); return 0; } - lp = nd->queue; /* get lp on top of queue */ - - if (lp->sav_skb) { /* find a non-busy device */ - isdn_net_local *nlp = lp->next; - while (nlp->sav_skb) { - if (lp == nlp) - return 1; - nlp = nd->queue = nd->queue->next; - } - lp = nlp; + lp = isdn_net_get_locked_lp(nd); + if (!lp) { + printk(KERN_WARNING "%s: all channels busy - requeuing!\n", netdev->name); + return 1; } + /* we have our lp locked from now on */ + slot = lp->ppp_slot; if (slot < 0 || slot > ISDN_MAX_CHANNELS) { printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot %d\n", lp->ppp_slot); @@ -1227,8 +1168,8 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *netdev) */ /* Pull off the fake header we stuck on earlier to keep - * the fragemntation code happy. - */ + * the fragmentation code happy. + */ skb_pull(skb,IPPP_MAX_HEADER); if (ipt->debug & 0x4) @@ -1301,11 +1242,10 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *netdev) /* we get mp_seqno from static isdn_net_local */ long mp_seqno = ipts->mp_seqno; ipts->mp_seqno++; - nd->queue = nd->queue->next; if (ipt->mpppcfg & SC_OUT_SHORT_SEQ) { unsigned char *data = isdn_ppp_skb_push(&skb, 3); if(!data) - return 0; + goto unlock; mp_seqno &= 0xfff; data[0] = MP_BEGIN_FRAG | MP_END_FRAG | ((mp_seqno >> 8) & 0xf); /* (B)egin & (E)ndbit .. */ data[1] = mp_seqno & 0xff; @@ -1313,7 +1253,7 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *netdev) } else { unsigned char *data = isdn_ppp_skb_push(&skb, 5); if(!data) - return 0; + goto unlock; data[0] = MP_BEGIN_FRAG | MP_END_FRAG; /* (B)egin & (E)ndbit .. */ data[1] = (mp_seqno >> 16) & 0xff; /* sequence number: 24bit */ data[2] = (mp_seqno >> 8) & 0xff; @@ -1333,20 +1273,20 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *netdev) if( (ipt->pppcfg & SC_COMP_PROT) && (proto <= 0xff) ) { unsigned char *data = isdn_ppp_skb_push(&skb,1); if(!data) - return 0; + goto unlock; data[0] = proto & 0xff; } else { unsigned char *data = isdn_ppp_skb_push(&skb,2); if(!data) - return 0; + goto unlock; data[0] = (proto >> 8) & 0xff; data[1] = proto & 0xff; } if(!(ipt->pppcfg & SC_COMP_AC)) { unsigned char *data = isdn_ppp_skb_push(&skb,2); if(!data) - return 0; + goto unlock; data[0] = 0xff; /* All Stations */ data[1] = 0x03; /* Unnumbered information */ } @@ -1357,16 +1297,10 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *netdev) 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); } - save_flags(flags); - cli(); - if (isdn_net_send_skb(netdev, lp, skb)) { - if (lp->sav_skb) { /* should never happen as sav_skb are sent with disabled IRQs) */ - printk(KERN_ERR "%s: whoops .. there is another stored skb!\n", netdev->name); - dev_kfree_skb(skb); - } else - lp->sav_skb = skb; - } - restore_flags(flags); + + isdn_net_writebuf_skb(lp, skb); + + unlock: return 0; } @@ -1397,8 +1331,8 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *netdev) static int isdn_ppp_mp_bundle_array_init(void) { - int i; - int sz = ISDN_MAX_CHANNELS*sizeof(ippp_bundle); + int i; + int sz = ISDN_MAX_CHANNELS*sizeof(ippp_bundle); if( (isdn_ppp_bundle_arr = (ippp_bundle*)kmalloc(sz, GFP_KERNEL)) == NULL ) return -ENOMEM; @@ -1410,7 +1344,7 @@ static int isdn_ppp_mp_bundle_array_init(void) static ippp_bundle * isdn_ppp_mp_bundle_alloc(void) { - int i; + int i; for( i = 0; i < ISDN_MAX_CHANNELS; i++ ) if (isdn_ppp_bundle_arr[i].ref_ct <= 0) return (isdn_ppp_bundle_arr + i); @@ -1419,7 +1353,7 @@ static ippp_bundle * isdn_ppp_mp_bundle_alloc(void) static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to ) { - struct ippp_struct * is = ippp_table[lp->ppp_slot]; + struct ippp_struct * is = ippp_table[lp->ppp_slot]; if (add_to) { if( lp->netdev->pb ) @@ -1452,18 +1386,27 @@ static void isdn_ppp_mp_print_recv_pkt( int slot, struct sk_buff * skb ); static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb) { - struct ippp_struct *is = ippp_table[lp->ppp_slot]; + struct ippp_struct *is; isdn_net_local * lpq; ippp_bundle * mp; isdn_mppp_stats * stats; struct sk_buff * newfrag, * frag, * start, *nextf; u32 newseq, minseq, thisseq; unsigned long flags; - - spin_lock_irqsave(&net_dev->pb->lock, flags); + int slot; + + spin_lock_irqsave(&net_dev->pb->lock, flags); mp = net_dev->pb; stats = &mp->stats; - + slot = lp->ppp_slot; + if (slot < 0 || slot > ISDN_MAX_CHANNELS) { + printk(KERN_ERR "isdn_ppp_mp_receive: lp->ppp_slot %d\n", lp->ppp_slot); + stats->frame_drops++; + dev_kfree_skb(skb); + spin_unlock_irqrestore(&mp->lock, flags); + return; + } + is = ippp_table[slot]; if( ++mp->frames > stats->max_queue_len ) stats->max_queue_len = mp->frames; @@ -1491,9 +1434,14 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, /* find the minimum received sequence number over all links */ is->last_link_seqno = minseq = newseq; for (lpq = net_dev->queue;;) { - u32 lls = ippp_table[lpq->ppp_slot]->last_link_seqno; - if (MP_LT(lls, minseq)) - minseq = lls; + slot = lpq->ppp_slot; + if (slot < 0 || slot > ISDN_MAX_CHANNELS) { + printk(KERN_ERR "isdn_ppp_mp_receive: lpq->ppp_slot %d\n", lpq->ppp_slot); + } else { + u32 lls = ippp_table[slot]->last_link_seqno; + if (MP_LT(lls, minseq)) + minseq = lls; + } if ((lpq = lpq->next) == net_dev->queue) break; } @@ -1645,20 +1593,19 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, * queue overflow */ if (mp->frames > MP_MAX_QUEUE_LEN) { stats->overflows++; - while (mp->frames < MP_MAX_QUEUE_LEN) { + while (mp->frames > MP_MAX_QUEUE_LEN) { frag = mp->frags->next; isdn_ppp_mp_free_skb(mp, mp->frags); mp->frags = frag; } } - spin_unlock_irqrestore(&mp->lock, flags); } static void isdn_ppp_mp_cleanup( isdn_net_local * lp ) { - struct sk_buff * frag = lp->netdev->pb->frags; - struct sk_buff * nextfrag; + struct sk_buff * frag = lp->netdev->pb->frags; + struct sk_buff * nextfrag; while( frag ) { nextfrag = frag->next; isdn_ppp_mp_free_skb(lp->netdev->pb, frag); @@ -1670,8 +1617,8 @@ static void isdn_ppp_mp_cleanup( isdn_net_local * lp ) static u32 isdn_ppp_mp_get_seq( int short_seq, struct sk_buff * skb, u32 last_seq ) { - u32 seq; - int flags = skb->data[0] & (MP_BEGIN_FRAG | MP_END_FRAG); + u32 seq; + int flags = skb->data[0] & (MP_BEGIN_FRAG | MP_END_FRAG); if( !short_seq ) { @@ -1716,46 +1663,40 @@ struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp, void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff * from, struct sk_buff * to ) { - ippp_bundle * mp = net_dev->pb; - int proto; - struct sk_buff * skb; - unsigned int tot_len; - - if( MP_FLAGS(from) == (MP_BEGIN_FRAG | MP_END_FRAG) ) - { + ippp_bundle * mp = net_dev->pb; + int proto; + struct sk_buff * skb; + unsigned int tot_len; + + if( MP_FLAGS(from) == (MP_BEGIN_FRAG | MP_END_FRAG) ) { if( ippp_table[lp->ppp_slot]->debug & 0x40 ) printk(KERN_DEBUG"isdn_mppp: reassembly: frame %d, " "len %d\n", MP_SEQ(from), from->len ); skb = from; skb_pull(skb, MP_HEADER_LEN); mp->frames--; - } - else - { - struct sk_buff * frag; - int n; - - for( tot_len=0, frag=from, n = 0; frag != to; - frag=frag->next, n++ ) + } else { + struct sk_buff * frag; + int n; + + for(tot_len=n=0, frag=from; frag != to; frag=frag->next, n++) tot_len += frag->len - MP_HEADER_LEN; - + if( ippp_table[lp->ppp_slot]->debug & 0x40 ) printk(KERN_DEBUG"isdn_mppp: reassembling frames %d " "to %d, len %d\n", MP_SEQ(from), (MP_SEQ(from)+n-1) & MP_LONGSEQ_MASK, tot_len ); - - if( (skb = dev_alloc_skb(tot_len)) == NULL ) - { - printk(KERN_ERR"isdn_mppp: cannot allocate sk buff " + if( (skb = dev_alloc_skb(tot_len)) == NULL ) { + printk(KERN_ERR "isdn_mppp: cannot allocate sk buff " "of size %d\n", tot_len); isdn_ppp_mp_discard(mp, from, to); return; } - - while( from != to ) - { - unsigned int len = from->len - MP_HEADER_LEN; - memcpy(skb_put(skb,len), from->data+MP_HEADER_LEN, len); + + while( from != to ) { + unsigned int len = from->len - MP_HEADER_LEN; + + memcpy(skb_put(skb,len), from->data+MP_HEADER_LEN, len); frag = from->next; isdn_ppp_mp_free_skb(mp, from); from = frag; @@ -1784,15 +1725,13 @@ isdn_ppp_bundle(struct ippp_struct *is, int unit) { char ifn[IFNAMSIZ + 1]; isdn_net_dev *p; - isdn_net_local *lp, - *nlp; + isdn_net_local *lp, *nlp; int rc; unsigned long flags; sprintf(ifn, "ippp%d", unit); p = isdn_net_findif(ifn); - if (!p) - { + if (!p) { printk(KERN_ERR "ippp_bundle: cannot find %s\n", ifn); return -EINVAL; } @@ -1800,38 +1739,29 @@ isdn_ppp_bundle(struct ippp_struct *is, int unit) spin_lock_irqsave(&p->pb->lock, flags); nlp = is->lp; - lp = p->queue; - if( nlp->ppp_slot < 0 || nlp->ppp_slot >= ISDN_MAX_CHANNELS || - lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS ) - { - spin_unlock_irqrestore(&p->pb->lock, flags); + lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS ) { printk(KERN_ERR "ippp_bundle: binding to invalid slot %d\n", - nlp->ppp_slot < 0 || nlp->ppp_slot >= ISDN_MAX_CHANNELS ? - nlp->ppp_slot : lp->ppp_slot ); - return -EINVAL; - } - - nlp->last = lp->last; - lp->last->next = nlp; - lp->last = nlp; - nlp->next = lp; - p->queue = nlp; + nlp->ppp_slot < 0 || nlp->ppp_slot >= ISDN_MAX_CHANNELS ? + nlp->ppp_slot : lp->ppp_slot ); + rc = -EINVAL; + goto out; + } + + isdn_net_add_to_bundle(p, nlp); ippp_table[nlp->ppp_slot]->unit = ippp_table[lp->ppp_slot]->unit; -/* maybe also SC_CCP stuff */ - ippp_table[nlp->ppp_slot]->pppcfg |= ippp_table[lp->ppp_slot]->pppcfg & - (SC_ENABLE_IP | SC_NO_TCP_CCID | SC_REJ_COMP_TCP); + /* maybe also SC_CCP stuff */ + ippp_table[nlp->ppp_slot]->pppcfg |= ippp_table[lp->ppp_slot]->pppcfg & + (SC_ENABLE_IP | SC_NO_TCP_CCID | SC_REJ_COMP_TCP); ippp_table[nlp->ppp_slot]->mpppcfg |= ippp_table[lp->ppp_slot]->mpppcfg & - (SC_MP_PROT | SC_REJ_MP_PROT | SC_OUT_SHORT_SEQ | SC_IN_SHORT_SEQ); - - rc = isdn_ppp_mp_init(nlp, p->pb); - + (SC_MP_PROT | SC_REJ_MP_PROT | SC_OUT_SHORT_SEQ | SC_IN_SHORT_SEQ); + rc = isdn_ppp_mp_init(nlp, p->pb); +out: spin_unlock_irqrestore(&p->pb->lock, flags); - - return rc; + return rc; } #endif /* CONFIG_ISDN_MPP */ @@ -2051,8 +1981,7 @@ static void isdn_ppp_ccp_xmit_reset(struct ippp_struct *is, int proto, { struct sk_buff *skb; unsigned char *p; - int count, hl; - unsigned long flags; + int hl; int cnt = 0; isdn_net_local *lp = is->lp; @@ -2093,26 +2022,7 @@ static void isdn_ppp_ccp_xmit_reset(struct ippp_struct *is, int proto, printk(KERN_DEBUG "Sending CCP Frame:\n"); isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,lp->ppp_slot); - /* Just ripped from isdn_ppp_write. Dunno whether it makes sense, - especially dunno what the sav_skb stuff is good for. */ - - count = skb->len; - save_flags(flags); - cli(); - if ((cnt = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, - 1, skb)) != count) { - if (lp->sav_skb) { - dev_kfree_skb(lp->sav_skb); - printk(KERN_INFO - "isdn_ppp_write: freeing sav_skb (%d,%d)!\n", - cnt, count); - } else - printk(KERN_INFO - "isdn_ppp_write: Can't write PPP frame to LL (%d,%d)!\n", - cnt, count); - lp->sav_skb = skb; - } - restore_flags(flags); + isdn_net_write_super(lp, skb); } /* Allocate the reset state vector */ @@ -2362,14 +2272,6 @@ static void isdn_ppp_ccp_reset_ack_rcvd(struct ippp_struct *is, static struct sk_buff *isdn_ppp_decompress(struct sk_buff *skb,struct ippp_struct *is,struct ippp_struct *master, int proto) { -#ifndef CONFIG_ISDN_CCP - if(proto == PPP_COMP || proto == PPP_LINK_COMP) { - printk(KERN_ERR "isdn_ppp: Ouch! Compression not included!\n"); - dev_kfree_skb(skb); - return NULL; - } - return skb; -#else void *stat = NULL; struct isdn_ppp_compressor *ipc = NULL; struct sk_buff *skb_out; @@ -2415,7 +2317,7 @@ static struct sk_buff *isdn_ppp_decompress(struct sk_buff *skb,struct ippp_struc printk(KERN_DEBUG "ippp: Decompress valid!\n"); */ - if((master && proto == PPP_COMP) || (!master && proto == PPP_LINK_COMP) ) { + if((master && proto == PPP_COMP) || (!master && proto == PPP_COMPFRAG) ) { /* Set up reset params for the decompressor */ memset(&rsparm, 0, sizeof(rsparm)); rsparm.data = rsdata; @@ -2455,7 +2357,6 @@ static struct sk_buff *isdn_ppp_decompress(struct sk_buff *skb,struct ippp_struc ipc->incomp(stat,skb,proto); return skb; } -#endif } /* @@ -2474,13 +2375,9 @@ static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto, void *stat; struct sk_buff *skb_out; -#ifdef CONFIG_ISDN_CCP /* we do not compress control protocols */ if(*proto < 0 || *proto > 0x3fff) { -#else - { -#endif - return skb_in; + return skb_in; } if(type) { /* type=1 => Link compression */ @@ -2681,7 +2578,7 @@ static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, struct } proto = ((int)data[0]<<8)+data[1]; - if(proto != PPP_CCP && proto != PPP_LINK_CCP) + if(proto != PPP_CCP && proto != PPP_CCPFRAG) return; printk(KERN_DEBUG "Received CCP frame from daemon:\n"); diff --git a/drivers/isdn/isdn_tty.c b/drivers/isdn/isdn_tty.c index 2ec672ab02ad..b4a90a7ef1e8 100644 --- a/drivers/isdn/isdn_tty.c +++ b/drivers/isdn/isdn_tty.c @@ -53,14 +53,20 @@ static int isdn_tty_countDLE(unsigned char *, int); #define MODEM_PARANOIA_CHECK #define MODEM_DO_RESTART +#ifdef CONFIG_DEVFS_FS +static char *isdn_ttyname_ttyI = "isdn/ttyI%d"; +static char *isdn_ttyname_cui = "isdn/cui%d"; +#else static char *isdn_ttyname_ttyI = "ttyI"; static char *isdn_ttyname_cui = "cui"; +#endif + static int bit2si[8] = {1, 5, 7, 7, 7, 7, 7, 7}; static int si2bit[8] = {4, 1, 4, 4, 4, 4, 4, 4}; -char *isdn_tty_revision = "$Revision: 1.84 $"; +char *isdn_tty_revision = "$Revision: 1.93 $"; /* isdn_tty_try_read() is called from within isdn_tty_rcv_skb() @@ -1177,6 +1183,7 @@ isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int co int c; int total = 0; modem_info *info = (modem_info *) tty->driver_data; + atemu *m = &info->emu; if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_write")) return 0; @@ -1197,8 +1204,6 @@ isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int co || (info->vonline & 3) #endif ) { - atemu *m = &info->emu; - #ifdef CONFIG_ISDN_AUDIO if (!info->vonline) #endif @@ -1256,7 +1261,9 @@ isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int co isdn_command(&c); } info->vonline = 0; - printk(KERN_DEBUG "fax dle cc/c %d/%d\n", cc,c); +#ifdef ISDN_DEBUG_MODEM_VOICE + printk(KERN_DEBUG "fax dle cc/c %d/%d\n", cc, c); +#endif info->xmit_count += cc; } else #endif @@ -1278,9 +1285,14 @@ isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int co count -= c; total += c; } - if ((info->xmit_count) || (skb_queue_len(&info->xmit_queue))) - isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1); atomic_dec(&info->xmit_lock); + if ((info->xmit_count) || (skb_queue_len(&info->xmit_queue))) { + if (m->mdmreg[REG_DXMT] & BIT_DXMT) { + isdn_tty_senddown(info); + isdn_tty_tint(info); + } + isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1); + } if (from_user) up(&info->write_sem); return total; @@ -1700,7 +1712,7 @@ isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * restore_flags(flags); info->blocked_open++; while (1) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(info->flags & ISDN_ASYNC_INITIALIZED)) { #ifdef MODEM_DO_RESTART @@ -1873,7 +1885,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) */ timeout = jiffies + HZ; while (!(info->lsr & UART_LSR_TEMT)) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(20); if (time_after(jiffies,timeout)) break; @@ -1889,7 +1901,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) info->ncarrier = 0; tty->closing = 0; if (info->blocked_open) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(50); wake_up_interruptible(&info->open_wait); } @@ -2364,11 +2376,21 @@ isdn_tty_stat_callback(int i, isdn_ctrl *c) #ifdef ISDN_TTY_STAT_DEBUG printk(KERN_DEBUG "tty_STAT_BCONN ttyI%d\n", info->line); #endif + /* Wake up any processes waiting + * for incoming call of this device when + * DCD follow the state of incoming carrier + */ + if (info->blocked_open && + (info->emu.mdmreg[REG_DCD] & BIT_DCD)) { + wake_up_interruptible(&info->open_wait); + } + /* Schedule CONNECT-Message to any tty * waiting for it and * set DCD-bit of its modem-status. */ - if (TTY_IS_ACTIVE(info)) { + if (TTY_IS_ACTIVE(info) || + (info->blocked_open && (info->emu.mdmreg[REG_DCD] & BIT_DCD))) { info->msr |= UART_MSR_DCD; info->emu.charge = 0; if (info->dialing & 0xf) @@ -2609,7 +2631,7 @@ isdn_tty_check_esc(const u_char * p, u_char plus, int count, int *pluscount, if ((jiffies - *lastplus) < PLUSWAIT2) *pluscount = 0; } - if ((*pluscount == 3) && (count = 1)) + if ((*pluscount == 3) && (count == 1)) isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, 1); if (*pluscount > 3) *pluscount = 1; @@ -2822,8 +2844,8 @@ isdn_tty_get_msnstr(char *n, char **p) int limit = ISDN_MSNLEN - 1; while (((*p[0] >= '0' && *p[0] <= '9') || - /* Why a comma ??? */ - (*p[0] == ',')) && + /* Why a comma ??? */ + (*p[0] == ',') || (*p[0] == ':')) && (limit--)) *n++ = *p[0]++; *n = '\0'; diff --git a/drivers/isdn/isdn_tty.h b/drivers/isdn/isdn_tty.h index ca1030262c1b..4553cf5687b2 100644 --- a/drivers/isdn/isdn_tty.h +++ b/drivers/isdn/isdn_tty.h @@ -57,6 +57,8 @@ #define REG_CPPP 12 #define BIT_CPPP 128 +#define REG_DXMT 13 +#define BIT_DXMT 1 #define REG_T70 13 #define BIT_T70 2 #define BIT_T70_EXT 32 diff --git a/drivers/isdn/sc/debug.c b/drivers/isdn/sc/debug.c index c5312cd83e63..09c11f57da03 100644 --- a/drivers/isdn/sc/debug.c +++ b/drivers/isdn/sc/debug.c @@ -1,5 +1,5 @@ /* - * $Id: debug.c,v 1.3 1997/10/01 09:22:20 fritz Exp $ + * $Id: debug.c,v 1.5 2000/11/12 15:29:37 kai Exp $ * Copyright (C) 1996 SpellCaster Telecommunications Inc. * * This program is free software; you can redistribute it and/or modify @@ -27,11 +27,6 @@ */ #include -#define NULL 0x0 - -#define REQUEST_IRQ(a,b,c,d,e) request_irq(a,b,c,d,e) -#define FREE_IRQ(a,b) free_irq(a,b) - inline char *strcpy(char *, const char *); int dbg_level = 0; @@ -61,7 +56,7 @@ inline char *strcpy(char *dest, const char *src) *i = *j; i++; j++; } - *(++i) = NULL; + *(++i) = 0; return dest; } @@ -70,6 +65,6 @@ inline void pullphone(char *dn, char *str) int i = 0; while(dn[i] != ',') - str[i] = dn[i++]; + str[i] = dn[i], i++; str[i] = 0x0; } diff --git a/drivers/isdn/sc/debug.h b/drivers/isdn/sc/debug.h index bb5553514eae..ba100543d36f 100644 --- a/drivers/isdn/sc/debug.h +++ b/drivers/isdn/sc/debug.h @@ -1,5 +1,5 @@ /* - * $Id: debug.h,v 1.1 1996/11/07 13:07:42 fritz Exp $ + * $Id: debug.h,v 1.2 2000/02/26 01:00:53 keil Exp $ * Copyright (C) 1996 SpellCaster Telecommunications Inc. * * This program is free software; you can redistribute it and/or modify @@ -26,10 +26,5 @@ * +1 (416) 297-6433 Facsimile */ -#if LINUX_VERSION_CODE < 131072 - #error You cant use this driver on kernels older than 2.0 -#else - #define REQUEST_IRQ(a,b,c,d,e) request_irq(a,b,c,d,e) - #define FREE_IRQ(a,b) free_irq(a,b) -#endif - +#define REQUEST_IRQ(a,b,c,d,e) request_irq(a,b,c,d,e) +#define FREE_IRQ(a,b) free_irq(a,b) diff --git a/drivers/isdn/sc/init.c b/drivers/isdn/sc/init.c index d4a64e43df07..e379ae44255d 100644 --- a/drivers/isdn/sc/init.c +++ b/drivers/isdn/sc/init.c @@ -164,7 +164,7 @@ int init_sc(void) if(do_reset) { pr_debug("Doing a SAFE probe reset\n"); outb(0xFF, io[b] + RESET_OFFSET); - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(milliseconds(10000)); } pr_debug("RAM Base for board %d is 0x%x, %s probe\n", b, ram[b], @@ -302,7 +302,7 @@ int init_sc(void) /* * No interrupt could be used */ - pr_debug("Failed to aquire an IRQ line\n"); + pr_debug("Failed to acquire an IRQ line\n"); continue; } @@ -512,7 +512,7 @@ int identify_board(unsigned long rambase, unsigned int iobase) * Try to identify a PRI card */ outb(PRI_BASEPG_VAL, pgport); - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(HZ); sig = readl(rambase + SIG_OFFSET); pr_debug("Looking for a signature, got 0x%x\n", sig); @@ -523,7 +523,7 @@ int identify_board(unsigned long rambase, unsigned int iobase) * Try to identify a PRI card */ outb(BRI_BASEPG_VAL, pgport); - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(HZ); sig = readl(rambase + SIG_OFFSET); pr_debug("Looking for a signature, got 0x%x\n", sig); @@ -555,7 +555,7 @@ int identify_board(unsigned long rambase, unsigned int iobase) */ x = 0; while((inb(iobase + FIFOSTAT_OFFSET) & RF_HAS_DATA) && x < 100) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); x++; } diff --git a/drivers/isdn/sc/message.c b/drivers/isdn/sc/message.c index a00b892692de..871a69643479 100644 --- a/drivers/isdn/sc/message.c +++ b/drivers/isdn/sc/message.c @@ -266,7 +266,7 @@ int send_and_receive(int card, tries = 0; /* wait for the response */ while (tries < timeout) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); pr_debug("SAR waiting..\n"); diff --git a/drivers/isdn/sc/timer.c b/drivers/isdn/sc/timer.c index 42abb3a42208..1c355dc28ef8 100644 --- a/drivers/isdn/sc/timer.c +++ b/drivers/isdn/sc/timer.c @@ -1,5 +1,5 @@ /* - * $Id: timer.c,v 1.2 1996/11/20 17:49:57 fritz Exp $ + * $Id: timer.c,v 1.3 2000/05/06 00:52:39 kai Exp $ * Copyright (C) 1996 SpellCaster Telecommunications Inc. * * This program is free software; you can redistribute it and/or modify @@ -91,9 +91,7 @@ void check_reset(unsigned long data) else { pr_debug("%s: No signature yet, waiting another %d jiffies.\n", adapter[card]->devicename, CHECKRESET_TIME); - del_timer(&adapter[card]->reset_timer); - adapter[card]->reset_timer.expires = jiffies + CHECKRESET_TIME; - add_timer(&adapter[card]->reset_timer); + mod_timer(&adapter[card]->reset_timer, jiffies+CHECKRESET_TIME); } restore_flags(flags); @@ -138,9 +136,7 @@ void check_phystat(unsigned long data) /* Reinitialize the timer */ save_flags(flags); cli(); - del_timer(&adapter[card]->stat_timer); - adapter[card]->stat_timer.expires = jiffies + CHECKSTAT_TIME; - add_timer(&adapter[card]->stat_timer); + mod_timer(&adapter[card]->stat_timer, jiffies+CHECKSTAT_TIME); restore_flags(flags); /* Send a new cePhyStatus message */ diff --git a/drivers/net/3c527.c b/drivers/net/3c527.c index 6feeccf89d4b..5ff404b72b18 100644 --- a/drivers/net/3c527.c +++ b/drivers/net/3c527.c @@ -1,9 +1,7 @@ - /* 3c527.c: 3Com Etherlink/MC32 driver for Linux * * (c) Copyright 1998 Red Hat Software Inc * Written by Alan Cox. - * Further debugging by Carl Drougge. * * Based on skeleton.c written 1993-94 by Donald Becker and ne2.c * (for the MCA stuff) written by Wim Dumon. @@ -17,7 +15,9 @@ */ static const char *version = - "3c527.c:v0.08 2000/02/22 Alan Cox (alan@redhat.com)\n"; + "3c527.c:v0.06 1999/09/16 Alan Cox (alan@redhat.com)\n"; + +/* Modified by Richard Procter (rprocter@mcs.vuw.ac.nz, rnp@netlink.co.nz) */ /* * Things you need @@ -31,6 +31,11 @@ static const char *version = * The documentation in places seems to miss things. In actual fact * I've always eventually found everything is documented, it just * requires careful study. + * + * The manual contradicts itself when describing the minimum number + * buffers in the 'configure lists' command. + * My card accepts a buffer config of 4/4. + * */ #include @@ -57,6 +62,8 @@ static const char *version = #include #include +#include + #include "3c527.h" /* @@ -69,23 +76,31 @@ static const char* cardname = "3c527"; #ifndef NET_DEBUG #define NET_DEBUG 2 #endif + +#undef DEBUG_IRQ + static unsigned int mc32_debug = NET_DEBUG; /* The number of low I/O ports used by the ethercard. */ #define NETCARD_IO_EXTENT 8 +/* As implemented, values must be a power-of-2 -- 4/8/16/32 */ + +#define TX_RING_LEN 16 /* Typically the card supports 37 */ +#define RX_RING_LEN 8 /* " " " */ -struct mc32_mailbox +#define RX_COPYBREAK 200 /* Value from 3c59x.c */ + + +/* Pointers to buffers and their on-card records */ +struct mc32_ring_desc { - u16 mbox __attribute((packed)); - u16 data[1] __attribute((packed)); + volatile struct skb_header *p; + struct sk_buff *skb; }; -/* Information that need to be kept for each board. */ - -#define TX_RING_MAX 16 /* Typically the card supports 37 */ -#define RX_RING_MAX 32 /* " " " */ +/* Information that needs to be kept for each board. */ struct mc32_local { struct net_device_stats net_stats; @@ -93,25 +108,28 @@ struct mc32_local volatile struct mc32_mailbox *rx_box; volatile struct mc32_mailbox *tx_box; volatile struct mc32_mailbox *exec_box; - volatile u16 *stats; - u16 tx_chain; - u16 rx_chain; - u16 tx_len; - u16 rx_len; + volatile struct mc32_stats *stats; /* Start of on-card statistics */ + u16 tx_chain; /* Transmit list start offset */ + u16 rx_chain; /* Receive list start offset */ + u16 tx_len; /* Transmit list count */ + u16 rx_len; /* Receive list count */ u32 base; u16 rx_halted; u16 tx_halted; - u16 rx_pending; /* ring due a service */ u16 exec_pending; u16 mc_reload_wait; /* a multicast load request is pending */ - atomic_t tx_count; /* buffers left */ + atomic_t tx_count; /* buffers left */ struct wait_queue *event; - struct sk_buff *tx_skb[TX_RING_MAX]; /* Transmit ring */ - u16 tx_skb_top; - u16 tx_skb_end; - struct sk_buff *rx_skb[RX_RING_MAX]; /* Receive ring */ - void *rx_ptr[RX_RING_MAX]; /* Data pointers */ - u32 mc_list_valid; /* True when the mclist is set */ + + struct mc32_ring_desc tx_ring[TX_RING_LEN]; /* Host Transmit ring */ + struct mc32_ring_desc rx_ring[RX_RING_LEN]; /* Host Receive ring */ + + u16 tx_ring_tail; /* index to tx de-queue end */ + u16 tx_ring_head; /* index to tx en-queue end */ + + u16 rx_ring_tail; /* index to rx de-queue end */ + + u32 mc_list_valid; /* True when the mclist is set */ }; /* The station (ethernet) address prefix, used for a sanity check. */ @@ -124,7 +142,7 @@ struct mca_adapters_t { char *name; }; -static struct mca_adapters_t mc32_adapters[] __initdata = { +const struct mca_adapters_t mc32_adapters[] = { { 0x0041, "3COM EtherLink MC/32" }, { 0x8EF5, "IBM High Performance Lan Adapter" }, { 0x0000, NULL } @@ -136,6 +154,7 @@ static struct mca_adapters_t mc32_adapters[] __initdata = { extern int mc32_probe(struct device *dev); static int mc32_probe1(struct device *dev, int ioaddr); +static int mc32_command(struct device *dev, u16 cmd, void *data, int len); static int mc32_open(struct device *dev); static int mc32_send_packet(struct sk_buff *skb, struct device *dev); static void mc32_interrupt(int irq, void *dev_id, struct pt_regs *regs); @@ -143,6 +162,7 @@ static int mc32_close(struct device *dev); static struct net_device_stats *mc32_get_stats(struct device *dev); static void mc32_set_multicast_list(struct device *dev); static void mc32_reset_multicast_list(struct device *dev); +static void mc32_flush_tx_ring(struct mc32_local *lp); /* * Check for a network adaptor of this type, and return '0' iff one exists. @@ -224,7 +244,7 @@ __initfunc(static int mc32_probe1(struct device *dev, int slot)) "82586 initialisation failure", "Adapter list configuration error" }; - + /* Time to play MCA games */ if (mc32_debug && version_printed++ == 0) @@ -399,11 +419,11 @@ __initfunc(static int mc32_probe1(struct device *dev, int slot)) lp->exec_box=bus_to_virt(dev->mem_start+base); - base=lp->exec_box->data[1]<<16|lp->exec_box->data[0]; + base=lp->exec_box->data[1]<<16|lp->exec_box->data[0]; lp->base = dev->mem_start+base; - lp->rx_box=bus_to_virt(lp->base + lp->exec_box->data[2]); + lp->rx_box=bus_to_virt(lp->base + lp->exec_box->data[2]); lp->tx_box=bus_to_virt(lp->base + lp->exec_box->data[3]); lp->stats = bus_to_virt(lp->base + lp->exec_box->data[5]); @@ -412,17 +432,14 @@ __initfunc(static int mc32_probe1(struct device *dev, int slot)) * Descriptor chains (card relative) */ - lp->tx_chain = lp->exec_box->data[8]; - lp->rx_chain = lp->exec_box->data[10]; - lp->tx_len = lp->exec_box->data[9]; - lp->rx_len = lp->exec_box->data[11]; + lp->tx_chain = lp->exec_box->data[8]; /* Transmit list start offset */ + lp->rx_chain = lp->exec_box->data[10]; /* Receive list start offset */ + lp->tx_len = lp->exec_box->data[9]; /* Transmit list count */ + lp->rx_len = lp->exec_box->data[11]; /* Receive list count */ - printk("%s: %d RX buffers, %d TX buffers. Base of 0x%08X.\n", - dev->name, lp->rx_len, lp->tx_len, lp->base); - - if(lp->tx_len >TX_RING_MAX) - lp->tx_len = TX_RING_MAX; - + printk("%s: Firmware Rev %d. %d RX buffers, %d TX buffers. Base of 0x%08X.\n", + dev->name, lp->exec_box->data[12], lp->rx_len, lp->tx_len, lp->base); + dev->open = mc32_open; dev->stop = mc32_close; dev->hard_start_xmit = mc32_send_packet; @@ -431,14 +448,13 @@ __initfunc(static int mc32_probe1(struct device *dev, int slot)) lp->rx_halted = 1; lp->tx_halted = 1; - lp->rx_pending = 0; /* Fill in the fields of the device structure with ethernet values. */ ether_setup(dev); + return 0; } - /* * Polled command stuff */ @@ -531,17 +547,18 @@ static int mc32_command(struct device *dev, u16 cmd, void *data, int len) /* Send the command */ while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)); outb(1<<6, ioaddr+HOST_CMD); - + save_flags(flags); cli(); + while(lp->exec_pending!=2) - sleep_on(&lp->event); + sleep_on(&lp->event); lp->exec_pending=0; restore_flags(flags); - - if(lp->exec_box->data[0]&(1<<13)) + if(lp->exec_box->mbox&(1<<13)) ret = -1; + /* * A multicast set got blocked - do it now */ @@ -562,10 +579,12 @@ static void mc32_rx_abort(struct device *dev) struct mc32_local *lp = (struct mc32_local *)dev->priv; int ioaddr = dev->base_addr; - while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)); + mc32_ring_poll(dev); lp->rx_box->mbox=0; - outb(3<<3, ioaddr+HOST_CMD); /* Suspend reception */ + outb(HOST_CMD_SUSPND_RX, ioaddr+HOST_CMD); /* Suspend reception */ + + mc32_ring_poll(dev); } @@ -577,58 +596,32 @@ static void mc32_rx_begin(struct device *dev) { struct mc32_local *lp = (struct mc32_local *)dev->priv; int ioaddr = dev->base_addr; - - while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)); - - lp->rx_box->mbox=0; - outb(1<<3, ioaddr+HOST_CMD); /* GO */ + + /* Tell the card start reception at the first descriptor */ + lp->rx_box->data[0]=lp->rx_chain; + mc32_ring_poll(dev); + lp->rx_box->mbox=0; + outb(HOST_CMD_START_RX, ioaddr+HOST_CMD); /* GO */ + + mc32_ring_poll(dev); lp->rx_halted=0; - lp->rx_pending=0; } static void mc32_tx_abort(struct device *dev) { struct mc32_local *lp = (struct mc32_local *)dev->priv; int ioaddr = dev->base_addr; - - while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)); + + mc32_ring_poll(dev); lp->tx_box->mbox=0; - outb(3, ioaddr+HOST_CMD); /* Suspend */ - - /* Ring empty */ - - atomic_set(&lp->tx_count, lp->tx_len); - - /* Flush */ - if(lp->tx_skb_top!=lp->tx_skb_end) - { - int i; - if(lp->tx_skb_top<=lp->tx_skb_end) - { - for(i=lp->tx_skb_top;itx_skb_end;i++) - { - dev_kfree_skb(lp->tx_skb[i]); - lp->tx_skb[i]=NULL; - } - } - else - { - for(i=lp->tx_skb_end;itx_skb[i]); - lp->tx_skb[i]=NULL; - } - for(i=0;itx_skb_top;i++) - { - dev_kfree_skb(lp->tx_skb[i]); - lp->tx_skb[i]=NULL; - } - } - } - lp->tx_skb_top=lp->tx_skb_end=0; + outb(HOST_CMD_SUSPND_TX, ioaddr+HOST_CMD); /* Suspend */ + + mc32_flush_tx_ring(lp); + + mc32_ring_poll(dev); } /* @@ -640,81 +633,132 @@ static void mc32_tx_begin(struct device *dev) struct mc32_local *lp = (struct mc32_local *)dev->priv; int ioaddr = dev->base_addr; - while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)); - + mc32_ring_poll(dev); + lp->tx_box->mbox=0; -#if 0 - outb(5, ioaddr+HOST_CMD); /* GO */ - printk("TX=>5\n"); + outb(HOST_CMD_RESTRT_TX, ioaddr+HOST_CMD); /* GO */ + mc32_ring_poll(dev); - if(lp->tx_box->mbox&(1<<13)) - printk("TX begin error!\n"); -#endif lp->tx_halted=0; } /* - * Load the rx ring + * Load the rx ring. */ static int mc32_load_rx_ring(struct device *dev) { struct mc32_local *lp = (struct mc32_local *)dev->priv; int i; - u16 base; + u16 rx_base; volatile struct skb_header *p; - base = lp->rx_box->data[0]; - - /* Fix me - should use card size - also fix flush ! */ + rx_base=lp->rx_chain; - for(i=0;irx_skb[i]=alloc_skb(1532, GFP_KERNEL); - if(lp->rx_skb[i]==NULL) + lp->rx_ring[i].skb=alloc_skb(1532, GFP_KERNEL); + skb_reserve(lp->rx_ring[i].skb, 18); + + if(lp->rx_ring[i].skb==NULL) { for(;i>=0;i--) - kfree_skb(lp->rx_skb[i]); + kfree_skb(lp->rx_ring[i].skb); return -ENOBUFS; } - lp->rx_ptr[i]=lp->rx_skb[i]->data+18; - p=bus_to_virt(lp->base+base); + p=bus_to_virt(lp->base+rx_base); + p->control=0; - p->data = virt_to_bus(lp->rx_ptr[i]); + p->data=virt_to_bus(lp->rx_ring[i].skb->data); p->status=0; - p->length = 1532; - base = p->next; + p->length=1532; + + lp->rx_ring[i].p=p; + rx_base=p->next; } - p->control = (1<<6); - lp->rx_box->mbox = 0; + + lp->rx_ring[i-1].p->control |= CONTROL_EOL; + + lp->rx_ring_tail=0; + + lp->rx_box->mbox=0; /* check: needed ? */ return 0; } static void mc32_flush_rx_ring(struct mc32_local *lp) { - int i; - for(i=0;irx_skb[i]); + int i; + for(i=0; i < RX_RING_LEN; i++) { + kfree_skb(lp->rx_ring[i].skb); + lp->rx_ring[i].p=NULL; + } } + +/* Load the addresses of the on-card buffer descriptors into main memory */ +static void mc32_load_tx_ring(struct device *dev) +{ + struct mc32_local *lp = (struct mc32_local *)dev->priv; + volatile struct skb_header *p; + int i; + u16 tx_base; + + tx_base=lp->tx_box->data[0]; + + /* Read the 'next' pointers from the on-card list into */ + /* our tx_ring array so we can reduce slow shared-mem reads */ + + for(i=0;itx_len;i++) + { + p=bus_to_virt(lp->base+tx_base); + lp->tx_ring[i].p=p; + lp->tx_ring[i].skb=NULL; + + tx_base=p->next; + } + + lp->tx_ring_head=lp->tx_ring_tail=0; +} + static void mc32_flush_tx_ring(struct mc32_local *lp) { - int i; - if(lp->tx_skb_top <= lp->tx_skb_end) - { - for(i=lp->tx_skb_top;itx_skb_end;i++) - dev_kfree_skb(lp->tx_skb[i]); - } - else + if(lp->tx_ring_tail!=lp->tx_ring_head) { - for(i=0;itx_skb_end;i++) - dev_kfree_skb(lp->tx_skb[i]); - for(i=lp->tx_skb_top;itx_skb[i]); + int i; + + if(lp->tx_ring_tail < lp->tx_ring_head) + { + for(i=lp->tx_ring_tail;itx_ring_head;i++) + { + dev_kfree_skb(lp->tx_ring[i].skb); + lp->tx_ring[i].skb=NULL; + lp->tx_ring[i].p=NULL; + } + } + else + { + for(i=lp->tx_ring_tail; itx_ring[i].skb); + lp->tx_ring[i].skb=NULL; + lp->tx_ring[i].p=NULL; + } + for(i=0; itx_ring_head; i++) + { + dev_kfree_skb(lp->tx_ring[i].skb); + lp->tx_ring[i].skb=NULL; + lp->tx_ring[i].p=NULL; + } + } } + + /* -1 so that tx_ring_head cannot "lap" tx_ring_tail, */ + /* which would be bad news for mc32_tx_ring as cur. implemented */ + atomic_set(&lp->tx_count, TX_RING_LEN-1); + lp->tx_ring_tail=lp->tx_ring_head=0; } /* @@ -725,9 +769,11 @@ static void mc32_flush_tx_ring(struct mc32_local *lp) static int mc32_open(struct device *dev) { int ioaddr = dev->base_addr; + struct mc32_local *lp = (struct mc32_local *)dev->priv; u16 zero_word=0; u8 one=1; u8 regs; + u16 descnumbuffs[2] = {TX_RING_LEN, RX_RING_LEN}; dev->tbusy = 0; dev->interrupt = 0; @@ -748,7 +794,6 @@ static int mc32_open(struct device *dev) mc32_command(dev, 4, &one, 2); - /* * Send the command sequence "abort, resume" for RX and TX. * The abort cleans up the buffer chains if needed. @@ -757,6 +802,22 @@ static int mc32_open(struct device *dev) mc32_rx_abort(dev); mc32_tx_abort(dev); + /* Ask card to set up on-card descriptors to our spec */ + + if(mc32_command(dev, 8, descnumbuffs, 4)) { + printk("%s: %s rejected our buffer configuration!\n", + dev->name, cardname); + return -ENOBUFS; + } + + /* Report new configuration */ + mc32_command(dev, 6, NULL, 0); + + lp->tx_chain = lp->exec_box->data[8]; /* Transmit list start offset */ + lp->rx_chain = lp->exec_box->data[10]; /* Receive list start offset */ + lp->tx_len = lp->exec_box->data[9]; /* Transmit list count */ + lp->rx_len = lp->exec_box->data[11]; /* Receive list count */ + /* Set Network Address */ mc32_command(dev, 1, dev->dev_addr, 6); @@ -767,9 +828,11 @@ static int mc32_open(struct device *dev) but basically means for all lans now days - has a performance cost but best set */ - mc32_command(dev, 0x0D, &zero_word, 2); /* 82586 bug workaround on */ - + /* mc32_command(dev, 0x0D, &zero_word, 2); */ /* 82586 bug workaround on */ + /* Load the ring we just initialised */ + + mc32_load_tx_ring(dev); if(mc32_load_rx_ring(dev)) { @@ -778,7 +841,7 @@ static int mc32_open(struct device *dev) } /* And the resume command goes last */ - + mc32_rx_begin(dev); mc32_tx_begin(dev); @@ -787,10 +850,10 @@ static int mc32_open(struct device *dev) return 0; } + static int mc32_send_packet(struct sk_buff *skb, struct device *dev) { struct mc32_local *lp = (struct mc32_local *)dev->priv; - int ioaddr = dev->base_addr; if (dev->tbusy) { /* @@ -817,9 +880,7 @@ static int mc32_send_packet(struct sk_buff *skb, struct device *dev) } else { - unsigned long flags; - - u16 tx_head; + unsigned long flags; volatile struct skb_header *p, *np; save_flags(flags); @@ -832,130 +893,230 @@ static int mc32_send_packet(struct sk_buff *skb, struct device *dev) return 1; } - tx_head = lp->tx_box->data[0]; - atomic_dec(&lp->tx_count); + atomic_dec(&lp->tx_count); - /* We will need this to flush the buffer out */ - - lp->tx_skb[lp->tx_skb_end] = skb; - lp->tx_skb_end++; - lp->tx_skb_end&=(TX_RING_MAX-1); - - /* TX suspend - shouldnt be needed but apparently is. - This is a research item ... */ - - while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)); - lp->tx_box->mbox=0; - outb(3, ioaddr+HOST_CMD); - - /* Transmit now stopped */ /* P is the last sending/sent buffer as a pointer */ - p=(struct skb_header *)bus_to_virt(lp->base+tx_head); + p=lp->tx_ring[lp->tx_ring_head].p; + lp->tx_ring_head++; + lp->tx_ring_head&=(TX_RING_LEN-1); + /* NP is the buffer we will be loading */ - np=(struct skb_header *)bus_to_virt(lp->base+p->next); - - np->control |= (1<<6); /* EOL */ - wmb(); - - np->length = skb->len; + np=lp->tx_ring[lp->tx_ring_head].p; - if(np->length < 60) - np->length = 60; + /* We will need this to flush the buffer out */ + lp->tx_ring[lp->tx_ring_head].skb=skb; + + np->length = (skb->len < 60) ? 60 : skb->len; np->data = virt_to_bus(skb->data); - np->status = 0; - np->control = (1<<7)|(1<<6); /* EOP EOL */ - wmb(); - - p->status = 0; - p->control &= ~(1<<6); + np->status = 0; + np->control = CONTROL_EOP | CONTROL_EOL; + wmb(); - dev->tbusy = 0; /* Keep feeding me */ - - while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)); - lp->tx_box->mbox=0; - outb(5, ioaddr+HOST_CMD); /* Restart TX */ - restore_flags(flags); + p->control &= ~CONTROL_EOL; /* Clear EOL on p */ + + dev->tbusy = 0; /* Keep feeding me */ + + restore_flags(flags); } return 0; } static void mc32_update_stats(struct device *dev) { + struct mc32_local *lp = (struct mc32_local *)dev->priv; + volatile struct mc32_stats *st = lp->stats; + + u32 rx_errors=0; + + /* The databook isn't at all clear about whether */ + /* the host should suspend rx/tx here to avoid races. */ + /* Assuming that it should, my code here is probably */ + /* a bit dodgey - RP */ + + /* TO DO: count rx_errors, tx_errors, figure out how to measure colisions */ + + /* RX */ + + rx_errors += lp->net_stats.rx_crc_errors += st->rx_crc_errors; + st->rx_crc_errors = 0; + + rx_errors += lp->net_stats.rx_length_errors += st->rx_tooshort_errors; + st->rx_tooshort_errors = 0; + + rx_errors += lp->net_stats.rx_length_errors += st->rx_toolong_errors; + st->rx_toolong_errors = 0; + + rx_errors += lp->net_stats.rx_fifo_errors += st->rx_overrun_errors; + st->rx_overrun_errors = 0; + + rx_errors += lp->net_stats.rx_frame_errors += st->rx_alignment_errors; + st->rx_alignment_errors = 0; + + rx_errors += lp->net_stats.rx_missed_errors += st->rx_outofresource_errors; + st->rx_outofresource_errors = 0; + + lp->net_stats.rx_errors = rx_errors; + + /* TX */ + + /* How to count collisions when you're only + told frames which had 1 or 2-15? - RP */ + + lp->net_stats.collisions += (st->tx_max_collisions * 16); + st->tx_max_collisions = 0; + + lp->net_stats.tx_carrier_errors += st->tx_carrier_errors; + st->tx_carrier_errors = 0; + + lp->net_stats.tx_fifo_errors += st->tx_underrun_errors; + st->tx_underrun_errors = 0; + } static void mc32_rx_ring(struct device *dev) { - struct mc32_local *lp=dev->priv; - int ioaddr = dev->base_addr; - int x=0; + struct mc32_local *lp=dev->priv; volatile struct skb_header *p; - u16 base; - u16 top; + u16 rx_ring_tail = lp->rx_ring_tail; + u16 rx_old_tail = rx_ring_tail; + + int x=0; - top = base = lp->rx_box->data[0]; do - { - p=(struct skb_header *)bus_to_virt(base+lp->base); - if(!(p->status & (1<<7))) + { + p=lp->rx_ring[rx_ring_tail].p; + + if(!(p->status & (1<<7))) { /* Not COMPLETED */ break; - if(p->status & (1<<6)) - { - u16 length = p->length; - struct sk_buff *skb=dev_alloc_skb(length+2); - if(skb!=NULL) + } + if(p->status & (1<<6)) /* COMPLETED_OK */ + { + + u16 length=p->length; + struct sk_buff *skb; + struct sk_buff *newskb; + +#ifdef DEBUG_IRQ + printk("skb_header %p has frame, x==%d\n", p, x); +#endif + + /* Try to save time by avoiding a copy on big frames */ + + if ((length > RX_COPYBREAK) + && ((newskb=dev_alloc_skb(1532)) != NULL)) + { + skb=lp->rx_ring[rx_ring_tail].skb; + skb_put(skb, length); + + skb_reserve(newskb,18); + lp->rx_ring[rx_ring_tail].skb=newskb; + p->data=virt_to_bus(newskb->data); + } + else { + skb=dev_alloc_skb(length+2); + + if(skb==NULL) + { + lp->net_stats.rx_dropped++; + + p->length = 1532; + p->status = 0; + p->control = 0; + + rx_ring_tail++; + rx_ring_tail&=(RX_RING_LEN-1); + + continue; /* better to use a goto? */ + } + skb_reserve(skb,2); - /*printk("Frame at %p\n", bus_to_virt(p->data)); */ memcpy(skb_put(skb, length), - bus_to_virt(p->data), length); - skb->protocol=eth_type_trans(skb,dev); - skb->dev=dev; - lp->net_stats.rx_packets++; - lp->net_stats.rx_bytes+=skb->len; - netif_rx(skb); + lp->rx_ring[rx_ring_tail].skb->data, length); } - else - lp->net_stats.rx_dropped++; + + skb->protocol=eth_type_trans(skb,dev); + skb->dev=dev; + lp->net_stats.rx_packets++; + lp->net_stats.rx_bytes+=skb->len; + netif_rx(skb); } - else + else /* NOT COMPLETED_OK */ { - lp->net_stats.rx_errors++; - switch(p->status&0x0F) - { - case 1: - lp->net_stats.rx_crc_errors++;break; - case 2: - lp->net_stats.rx_fifo_errors++;break; - case 3: - lp->net_stats.rx_frame_errors++;break; - case 4: - lp->net_stats.rx_missed_errors++;break; - case 5: - lp->net_stats.rx_length_errors++;break; - } + /* There was some sort of reception error. */ + /* This case should never occur unless */ + /* the card is asked to upload damaged frames. */ + + /* do nothing */ } - p->length = 1532; - p->control &= ~(1<<6); + + p->length = 1532; p->status = 0; - base = p->next; + p->control = 0; + + rx_ring_tail++; + rx_ring_tail&=(RX_RING_LEN-1); + } + while(x++<48); + + /* If there was actually a frame to be processed, */ + /* place the EL bit at the descriptor prior to the one to be filled next */ + + if (rx_ring_tail != rx_old_tail) { + lp->rx_ring[(rx_ring_tail-1)&(RX_RING_LEN-1)].p->control |= CONTROL_EOL; + lp->rx_ring[(rx_old_tail-1)&(RX_RING_LEN-1)].p->control &= ~CONTROL_EOL; + + lp->rx_ring_tail=rx_ring_tail; } - while(x++<48); - - /* - * Restart ring processing - */ - - while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)); - lp->rx_box->mbox=0; - lp->rx_box->data[0] = top; - outb(1<<3, ioaddr+HOST_CMD); - lp->rx_halted = 0; } +static void mc32_tx_ring(struct device *dev) { + + struct mc32_local *lp=(struct mc32_local *)dev->priv; + volatile struct skb_header *np; + + /* NB: lp->tx_count=TX_RING_LEN-1 so that tx_ring_head cannot "lap" tail here */ + + while (lp->tx_ring_tail != lp->tx_ring_head) + { + u16 t; + + t=(lp->tx_ring_tail+1)&(TX_RING_LEN-1); + np=lp->tx_ring[t].p; + + if(!(np->status & (1<<7))) { /* Not COMPLETED */ + break; + } + + lp->net_stats.tx_packets++; + + if(!(np->status & (1<<6))) /* Not COMPLETED_OK */ + { + lp->net_stats.tx_errors++; + + /* Error stats are stored on-card to be picked up by + mc32_update_stats() - RP */ + } + + + /* Packets are sent in order - this is + basically a FIFO queue of buffers matching + the card ring */ + lp->net_stats.tx_bytes+=lp->tx_ring[t].skb->len; + dev_kfree_skb(lp->tx_ring[t].skb); + lp->tx_ring[t].skb=NULL; + atomic_inc(&lp->tx_count); + dev->tbusy=0; + mark_bh(NET_BH); + + lp->tx_ring_tail=t; + } +} + /* * The typical workload of the driver: * Handle the network interface interrupts. @@ -965,26 +1126,29 @@ static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs) struct device *dev = dev_id; struct mc32_local *lp; int ioaddr, status, boguscount = 0; + int rx_event = 0; + int tx_event = 0; + int must_restart = 0; if (dev == NULL) { printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq); return; } dev->interrupt = 1; - + ioaddr = dev->base_addr; lp = (struct mc32_local *)dev->priv; /* See whats cooking */ - - while((inb(ioaddr+2)&(1<<5)) && boguscount++<2000) + + while((inb(ioaddr+HOST_STATUS)&HOST_STATUS_CWR) && boguscount++<2000) { status=inb(ioaddr+HOST_CMD); #ifdef DEBUG_IRQ - printk("Status TX%d RX%d EX%d OV%d\n", + printk("Status TX%d RX%d EX%d OV%d BC%d\n", (status&7), (status>>3)&7, (status>>6)&1, - (status>>7)&1); + (status>>7)&1, boguscount); #endif switch(status&7) @@ -992,20 +1156,8 @@ static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs) case 0: break; case 6: /* TX fail */ - lp->net_stats.tx_errors++; case 2: /* TX ok */ - lp->net_stats.tx_packets++; - /* Packets are sent in order - this is - basically a FIFO queue of buffers matching - the card ring */ - lp->net_stats.tx_bytes+=lp->tx_skb[lp->tx_skb_top]->len; - dev_kfree_skb(lp->tx_skb[lp->tx_skb_top]); - lp->tx_skb[lp->tx_skb_top]=NULL; - lp->tx_skb_top++; - lp->tx_skb_top&=(TX_RING_MAX-1); - atomic_inc(&lp->tx_count); - dev->tbusy=0; - mark_bh(NET_BH); + tx_event = 1; break; case 3: /* Halt */ case 4: /* Abort */ @@ -1017,8 +1169,7 @@ static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs) wake_up(&lp->event); break; default: - printk("%s: strange tx ack %d\n", - dev->name, status&7); + printk("%s: strange tx ack %d\n", dev->name, status&7); } status>>=3; switch(status&7) @@ -1026,15 +1177,7 @@ static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs) case 0: break; case 2: /* RX */ - lp->rx_pending=1; - if(!lp->rx_halted) - { - /* - * Halt ring receive - */ - while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)); - outb(3<<3, ioaddr+HOST_CMD); - } + rx_event=1; break; case 3: case 4: @@ -1047,25 +1190,32 @@ static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs) break; case 6: /* Out of RX buffers stat */ - lp->net_stats.rx_dropped++; - lp->rx_pending=1; /* Must restart */ - lp->rx_halted=1; + lp->net_stats.rx_dropped++; + must_restart = 1; /* To restart */ + rx_event = 1; + break; default: printk("%s: strange rx ack %d\n", - dev->name, status&7); - + dev->name, status&7); } status>>=3; if(status&1) { + /* 0=no 1=yes 2=replied, get cmd, 3 = wait reply & dump it */ - if(lp->exec_pending!=3) + + if(lp->exec_pending!=3) + { lp->exec_pending=2; - else + wake_up(&lp->event); + } + else + { lp->exec_pending=0; - wake_up(&lp->event); + wake_up(&lp->event); + } } if(status&2) { @@ -1074,28 +1224,42 @@ static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs) * we have it flagged and can * send an immediate reply (CRR set) */ - + if(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR) { - mc32_update_stats(dev); - outb(0, ioaddr+HOST_CMD); + mc32_update_stats(dev); + outb(0, ioaddr+HOST_CMD); } + } } - + + + if(tx_event) { + mc32_tx_ring(dev); + } + /* - * Process and restart the receive ring. This has some state - * as we must halt the ring to process it and halting the ring - * might not occur in the same IRQ handling loop as we issue - * the halt. - */ + * Process the receive ring and restart if the card + * stopped due to a shortage of free buffers. + */ - if(lp->rx_pending && lp->rx_halted) - { + if(rx_event) { mc32_rx_ring(dev); - lp->rx_pending = 0; + if (must_restart) { + u16 rx_prev_tail=(lp->rx_ring_tail-1)&(RX_RING_LEN-1); + + /* Restart at the current rx_ring_tail */ + lp->rx_box->data[0]=lp->rx_ring[rx_prev_tail].p->next; + lp->rx_box->mbox=0; + + while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)); + outb(HOST_CMD_START_RX, ioaddr+HOST_CMD); /* Re-enable receives */ + } } - dev->interrupt = 0; + + dev->interrupt = 0; + return; } @@ -1152,7 +1316,12 @@ static int mc32_close(struct device *dev) static struct net_device_stats *mc32_get_stats(struct device *dev) { - struct mc32_local *lp = (struct mc32_local *)dev->priv; + struct mc32_local *lp; + + mc32_update_stats(dev); + + lp = (struct mc32_local *)dev->priv; + return &lp->net_stats; } diff --git a/drivers/net/3c527.h b/drivers/net/3c527.h index dfe2738cb15a..e517345974ac 100644 --- a/drivers/net/3c527.h +++ b/drivers/net/3c527.h @@ -7,11 +7,19 @@ */ #define HOST_CMD 0 +#define HOST_CMD_START_RX (1<<3) +#define HOST_CMD_SUSPND_RX (3<<3) +#define HOST_CMD_RESTRT_RX (5<<3) + +#define HOST_CMD_SUSPND_TX 3 +#define HOST_CMD_RESTRT_TX 5 + #define HOST_STATUS 2 #define HOST_STATUS_CRR (1<<6) #define HOST_STATUS_CWR (1<<5) + #define HOST_CTRL 6 #define HOST_CTRL_ATTN (1<<7) #define HOST_CTRL_RESET (1<<6) @@ -19,6 +27,13 @@ #define HOST_RAMPAGE 8 + +struct mc32_mailbox +{ + u16 mbox __attribute((packed)); + u16 data[1] __attribute((packed)); +}; + struct skb_header { u8 status __attribute((packed)); @@ -28,13 +43,37 @@ struct skb_header u32 data __attribute((packed)); }; -#define STATUS_MASK 0x0F -#define COMPLETED 0x80 -#define COMPLETED_OK 0x40 -#define BUFFER_BUSY 0x20 +struct mc32_stats +{ + /* RX Errors */ + u32 rx_crc_errors __attribute((packed)); + u32 rx_alignment_errors __attribute((packed)); + u32 rx_overrun_errors __attribute((packed)); + u32 rx_tooshort_errors __attribute((packed)); + u32 rx_toolong_errors __attribute((packed)); + u32 rx_outofresource_errors __attribute((packed)); + + u32 rx_discarded __attribute((packed)); // via card pattern match filter -#define CONTROL_EOP 0x80 /* End Of Packet */ -#define CONTROL_EL 0x40 /* End of List */ + /* TX Errors */ + u32 tx_max_collisions __attribute((packed)); + u32 tx_carrier_errors __attribute((packed)); + u32 tx_underrun_errors __attribute((packed)); + u32 tx_cts_errors __attribute((packed)); + u32 tx_timeout_errors __attribute((packed)) ; + + /* various cruft */ + u32 dataA[6] __attribute((packed)); + u16 dataB[5] __attribute((packed)); + u32 dataC[14] __attribute((packed)); +}; + +#define STATUS_MASK 0x0F +#define COMPLETED (1<<7) +#define COMPLETED_OK (1<<6) +#define BUFFER_BUSY (1<<5) +#define CONTROL_EOP (1<<7) /* End Of Packet */ +#define CONTROL_EOL (1<<6) /* End of List */ -#define MCA_MC32_ID 0x0041 /* Our MCA ident */ \ No newline at end of file +#define MCA_MC32_ID 0x0041 /* Our MCA ident */ diff --git a/drivers/net/Config.in b/drivers/net/Config.in index 0e068813d874..75e7b08328ba 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -135,6 +135,7 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then tristate 'Ansel Communications EISA 3200 support (EXPERIMENTAL)' CONFIG_AC3200 fi tristate 'Apricot Xen-II on board Ethernet' CONFIG_APRICOT + tristate 'LP486E on board Ethernet' CONFIG_LP486E tristate 'CS89x0 support' CONFIG_CS89x0 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'DM9102 PCI Fast Ethernet Adapter support (EXPERIMENTAL)' CONFIG_DM9102 diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 85e41fc3c81c..1f55e8df8f50 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -776,6 +776,11 @@ else endif endif +# This is also a 82596 and should probably be merged +ifeq ($(CONFIG_LP486E),y) +L_OBJS += lp486e.o +endif + ifeq ($(CONFIG_DEC_ELCP),y) L_OBJS += tulip.o else diff --git a/drivers/net/Space.c b/drivers/net/Space.c index 8d41896bf976..5faa19d58fab 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -64,6 +64,7 @@ extern int at1700_probe(struct device *); extern int fmv18x_probe(struct device *); extern int eth16i_probe(struct device *); extern int depca_probe(struct device *); +extern int lp486e_probe(struct device *); extern int i82596_probe(struct device *); extern int ewrk3_probe(struct device *); extern int de4x5_probe(struct device *); @@ -420,6 +421,9 @@ struct devprobe isa_probes[] __initdata = { #if defined(CONFIG_APRICOT) || defined(CONFIG_MVME16x_NET) || defined(CONFIG_BVME6000_NET) /* Intel I82596 */ {i82596_probe, 0}, #endif +#if defined(CONFIG_LP486E) + {lp486e_probe, 0}, +#endif #ifdef CONFIG_EL1 /* 3c501 */ {el1_probe, 0}, #endif diff --git a/drivers/net/lance.c b/drivers/net/lance.c index 7dacd42096da..0f9453e610d8 100644 --- a/drivers/net/lance.c +++ b/drivers/net/lance.c @@ -926,6 +926,8 @@ static int lance_start_xmit(struct sk_buff *skb, struct device *dev) lp->tx_ring[entry].misc = 0x0000; + lp->stats.tx_bytes += skb->len; + /* If any part of this buffer is >16M we must copy it to a low-memory buffer. */ if ((u32)virt_to_bus(skb->data) + skb->len > 0x01000000) { @@ -941,7 +943,6 @@ static int lance_start_xmit(struct sk_buff *skb, struct device *dev) lp->tx_ring[entry].base = ((u32)virt_to_bus(skb->data) & 0xffffff) | 0x83000000; } lp->cur_tx++; - lp->stats.tx_bytes += skb->len; /* Trigger an immediate send poll. */ outw(0x0000, ioaddr+LANCE_ADDR); diff --git a/drivers/net/lp486e.c b/drivers/net/lp486e.c new file mode 100644 index 000000000000..5daaf80b0e48 --- /dev/null +++ b/drivers/net/lp486e.c @@ -0,0 +1,1401 @@ +/* Intel Professional Workstation/panther ethernet driver */ +/* lp486e.c: A panther 82596 ethernet driver for linux. */ +/* + History and copyrights: + + Driver skeleton + Written 1993 by Donald Becker. + Copyright 1993 United States Government as represented by the Director, + National Security Agency. This software may only be used and + distributed according to the terms of the GNU Public License + as modified by SRC, incorporated herein by reference. + + The author may be reached as becker@super.org or + C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715 + + Apricot + Written 1994 by Mark Evans. + This driver is for the Apricot 82596 bus-master interface + + Modularised 12/94 Mark Evans + + Professional Workstation + Derived from apricot.c by Ard van Breemen + || + + Credits: + Thanks to Murphy Software BV for letting me write this in their time. + Well, actually, I get payed doing this... + (Also: see http://www.murphy.nl for murphy, and my homepage ~ard for + more information on the Professional Workstation) + + Present version + aeb@cwi.nl +*/ +/* + There are currently two motherboards that I know of in the + professional workstation. The only one that I know is the + intel panther motherboard. -- ard +*/ +/* +The pws is equipped with an intel 82596. This is a very intelligent controller +which runs its own micro-code. Communication with the hostprocessor is done +through linked lists of commands and buffers in the hostprocessors memory. +A complete description of the 82596 is available from intel. Search for +a file called "29021806.pdf". It is a complete description of the chip itself. +To use it for the pws some additions are needed regarding generation of +the PORT and CA signal, and the interrupt glue needed for a pc. +I/O map: +PORT SIZE ACTION MEANING +0xCB0 2 WRITE Lower 16 bits for PORT command +0xCB2 2 WRITE Upper 16 bits for PORT command, and issue of PORT command +0xCB4 1 WRITE Generation of CA signal +0xCB8 1 WRITE Clear interrupt glue +All other communication is through memory! +*/ + +#define SLOW_DOWN_IO udelay(5); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define kkmalloc(typ, prio) (typ *)(kmalloc(sizeof(typ), prio)) + +#ifdef REALLY_OLD +#ifndef HAVE_PORTRESERVE +#define check_region(addr, size) 0 +#define request_region(addr, size,name) do ; while(0) +#endif + +#ifndef HAVE_ALLOC_SKB +#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority) +#define kfree_skbmem(buff, size) kfree_s(buff,size) +#endif +#endif /* REALLY_OLD */ + +#define LOG_SRCDST 0x80000000 +#define LOG_STATINT 0x40000000 +#define LOG_STARTINT 0x20000000 + +#define i596_debug debug + +static int i596_debug = 0; + +static const char * const medianame[] = { + "10baseT", "AUI", + "10baseT-FD", "AUI-FD", +}; + +#define LP486E_TOTAL_SIZE 16 + +#define I596_NULL (0xffffffff) + +#define CMD_EOL 0x8000 /* The last command of the list, stop. */ +#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */ +#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */ + +#define CMD_FLEX 0x0008 /* Enable flexible memory model */ + +enum commands { + CmdNOP = 0, + CmdIASetup = 1, + CmdConfigure = 2, + CmdMulticastList = 3, + CmdTx = 4, + CmdTDR = 5, + CmdDump = 6, + CmdDiagnose = 7 +}; + +char *CUcmdnames[8] = { "NOP", "IASetup", "Configure", "MulticastList", + "Tx", "TDR", "Dump", "Diagnose" }; + +/* Status word bits */ +#define STAT_CX 0x8000 /* The CU finished executing a command + with the Interrupt bit set */ +#define STAT_FR 0x4000 /* The RU finished receiving a frame */ +#define STAT_CNA 0x2000 /* The CU left the active state */ +#define STAT_RNR 0x1000 /* The RU left the active state */ +#define STAT_ACK (STAT_CX | STAT_FR | STAT_CNA | STAT_RNR) +#define STAT_CUS 0x0700 /* Status of CU: 0: idle, 1: suspended, + 2: active, 3-7: unused */ +#define STAT_RUS 0x00f0 /* Status of RU: 0: idle, 1: suspended, + 2: no resources, 4: ready, + 10: no resources due to no more RBDs, + 12: no more RBDs, other: unused */ +#define STAT_T 0x0008 /* Bus throttle timers loaded */ +#define STAT_ZERO 0x0807 /* Always zero */ + +char *CUstates[8] = { "idle", "suspended", "active", 0, 0, 0, 0, 0 }; +char *RUstates[16] = { "idle", "suspended", "no resources", 0, "ready", 0, + 0, 0, 0, 0, "no RBDs", 0, "out of RBDs", 0, 0, 0 }; + +#if 0 +static void +i596_out_status(int status) { + int bad = 0; + char *s; + + printk("status %4.4x:", status); + if (status == 0xffff) + printk(" strange..\n"); + else { + if (status & STAT_CX) + printk(" CU done"); + if (status & STAT_CNA) + printk(" CU stopped"); + if (status & STAT_FR) + printk(" got a frame"); + if (status & STAT_RNR) + printk(" RU stopped"); + if (status & STAT_T) + printk(" throttled"); + if (status & STAT_ZERO) + bad = 1; + s = CUstates[(status & STAT_CUS) >> 8]; + if (!s) + bad = 1; + else + printk(" CU(%s)", s); + s = RUstates[(status & STAT_RUS) >> 4]; + if (!s) + bad = 1; + else + printk(" RU(%s)", s); + if (bad) + printk(" bad status"); + printk("\n"); + } +} +#endif + +/* Command word bits */ +#define ACK_CX 0x8000 +#define ACK_FR 0x4000 +#define ACK_CNA 0x2000 +#define ACK_RNR 0x1000 + +#define CUC_START 0x0100 +#define CUC_RESUME 0x0200 +#define CUC_SUSPEND 0x0300 +#define CUC_ABORT 0x0400 + +#define RX_START 0x0010 +#define RX_RESUME 0x0020 +#define RX_SUSPEND 0x0030 +#define RX_ABORT 0x0040 + +typedef u32 phys_addr; + +static inline phys_addr +va_to_pa(volatile void *x) { + return x ? virt_to_bus(x) : I596_NULL; +} + +static inline void * +pa_to_va(phys_addr x) { + return (x == I596_NULL) ? NULL : bus_to_virt(x); +} + +/* status bits for cmd */ +#define CMD_STAT_C 0x8000 /* CU command complete */ +#define CMD_STAT_B 0x4000 /* CU command in progress */ +#define CMD_STAT_OK 0x2000 /* CU command completed without errors */ +#define CMD_STAT_A 0x1000 /* CU command abnormally terminated */ + +struct i596_cmd { /* 8 bytes */ + unsigned short status; + unsigned short command; + phys_addr pa_next; /* va_to_pa(struct i596_cmd *next) */ +}; + +#define EOF 0x8000 +#define SIZE_MASK 0x3fff + +struct i596_tbd { + unsigned short size; + unsigned short pad; + phys_addr pa_next; /* va_to_pa(struct i596_tbd *next) */ + phys_addr pa_data; /* va_to_pa(char *data) */ + struct sk_buff *skb; +}; + +struct tx_cmd { + struct i596_cmd cmd; + phys_addr pa_tbd; /* va_to_pa(struct i596_tbd *tbd) */ + unsigned short size; + unsigned short pad; +}; + +/* status bits for rfd */ +#define RFD_STAT_C 0x8000 /* Frame reception complete */ +#define RFD_STAT_B 0x4000 /* Frame reception in progress */ +#define RFD_STAT_OK 0x2000 /* Frame received without errors */ +#define RFD_STATUS 0x1fff +#define RFD_LENGTH_ERR 0x1000 +#define RFD_CRC_ERR 0x0800 +#define RFD_ALIGN_ERR 0x0400 +#define RFD_NOBUFS_ERR 0x0200 +#define RFD_DMA_ERR 0x0100 /* DMA overrun failure to acquire system bus */ +#define RFD_SHORT_FRAME_ERR 0x0080 +#define RFD_NOEOP_ERR 0x0040 +#define RFD_TRUNC_ERR 0x0020 +#define RFD_MULTICAST 0x0002 /* 0: destination had our address + 1: destination was broadcast/multicast */ +#define RFD_COLLISION 0x0001 + +/* receive frame descriptor */ +struct i596_rfd { + unsigned short stat; + unsigned short cmd; + phys_addr pa_next; /* va_to_pa(struct i596_rfd *next) */ + phys_addr pa_rbd; /* va_to_pa(struct i596_rbd *rbd) */ + unsigned short count; + unsigned short size; + char data[1532]; +}; + +#define RBD_EL 0x8000 +#define RBD_P 0x4000 +#define RBD_SIZEMASK 0x3fff +#define RBD_EOF 0x8000 +#define RBD_F 0x4000 + +/* receive buffer descriptor */ +struct i596_rbd { + unsigned short size; + unsigned short pad; + phys_addr pa_next; /* va_to_pa(struct i596_tbd *next) */ + phys_addr pa_data; /* va_to_pa(char *data) */ + phys_addr pa_prev; /* va_to_pa(struct i596_tbd *prev) */ + + /* Driver private part */ + struct sk_buff *skb; +}; + +#define RX_RING_SIZE 64 +#define RX_SKBSIZE (ETH_FRAME_LEN+10) +#define RX_RBD_SIZE 32 + +/* System Control Block - 40 bytes */ +struct i596_scb { + u16 status; /* 0 */ + u16 command; /* 2 */ + phys_addr pa_cmd; /* 4 - va_to_pa(struct i596_cmd *cmd) */ + phys_addr pa_rfd; /* 8 - va_to_pa(struct i596_rfd *rfd) */ + u32 crc_err; /* 12 */ + u32 align_err; /* 16 */ + u32 resource_err; /* 20 */ + u32 over_err; /* 24 */ + u32 rcvdt_err; /* 28 */ + u32 short_err; /* 32 */ + u16 t_on; /* 36 */ + u16 t_off; /* 38 */ +}; + +/* Intermediate System Configuration Pointer - 8 bytes */ +struct i596_iscp { + u32 busy; /* 0 */ + phys_addr pa_scb; /* 4 - va_to_pa(struct i596_scb *scb) */ +}; + +/* System Configuration Pointer - 12 bytes */ +struct i596_scp { + u32 sysbus; /* 0 */ + u32 pad; /* 4 */ + phys_addr pa_iscp; /* 8 - va_to_pa(struct i596_iscp *iscp) */ +}; + +/* Selftest and dump results - needs 16-byte alignment */ +/* + * The size of the dump area is 304 bytes. When the dump is executed + * by the Port command an extra word will be appended to the dump area. + * The extra word is a copy of the Dump status word (containing the + * C, B, OK bits). [I find 0xa006, with a0 for C+OK and 6 for dump] + */ +struct i596_dump { + u16 dump[153]; /* (304 = 130h) + 2 bytes */ +}; + +struct i596_private { /* aligned to a 16-byte boundary */ + struct i596_scp scp; /* 0 - needs 16-byte alignment */ + struct i596_iscp iscp; /* 12 */ + struct i596_scb scb; /* 20 */ + u32 dummy; /* 60 */ + struct i596_dump dump; /* 64 - needs 16-byte alignment */ + + struct i596_cmd set_add; + char eth_addr[8]; /* directly follows set_add */ + + struct i596_cmd set_conf; + char i596_config[16]; /* directly follows set_conf */ + + struct i596_cmd tdr; + unsigned long tdr_stat; /* directly follows tdr */ + + int last_restart; + volatile struct i596_rbd *rbd_list; + volatile struct i596_rbd *rbd_tail; + volatile struct i596_rfd *rx_tail; + volatile struct i596_cmd *cmd_tail; + volatile struct i596_cmd *cmd_head; + int cmd_backlog; + unsigned long last_cmd; + struct enet_statistics stats; +}; + +static char init_setup[14] = { + 0x8E, /* length 14 bytes, prefetch on */ + 0xC8, /* default: fifo to 8, monitor off */ + 0x40, /* default: don't save bad frames (apricot.c had 0x80) */ + 0x2E, /* (default is 0x26) + No source address insertion, 8 byte preamble */ + 0x00, /* default priority and backoff */ + 0x60, /* default interframe spacing */ + 0x00, /* default slot time LSB */ + 0xf2, /* default slot time and nr of retries */ + 0x00, /* default various bits + (0: promiscuous mode, 1: broadcast disable, + 2: encoding mode, 3: transmit on no CRS, + 4: no CRC insertion, 5: CRC type, + 6: bit stuffing, 7: padding) */ + 0x00, /* default carrier sense and collision detect */ + 0x40, /* default minimum frame length */ + 0xff, /* (default is 0xff, and that is what apricot.c has; + elp486.c has 0xfb: Enable crc append in memory.) */ + 0x00, /* default: not full duplex */ + 0x7f /* (default is 0x3f) multi IA */ +}; + +static int i596_open(struct device *dev); +static int i596_start_xmit(struct sk_buff *skb, struct device *dev); +static void i596_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static int i596_close(struct device *dev); +static struct enet_statistics *i596_get_stats(struct device *dev); +static void i596_add_cmd(struct device *dev, struct i596_cmd *cmd); +static void print_eth(char *); +static void set_multicast_list(struct device *dev); + +static int +i596_timeout(struct device *dev, char *msg, int ct) { + volatile struct i596_private *lp; + int boguscnt = ct; + + lp = (struct i596_private *) dev->priv; + while (lp->scb.command) { + if (--boguscnt == 0) { + printk("%s: %s timed out - stat %4.4x, cmd %4.4x\n", + dev->name, msg, + lp->scb.status, lp->scb.command); + return 1; + } + udelay(5); + } + return 0; +} + +static inline int +init_rx_bufs(struct device *dev, int num) { + volatile struct i596_private *lp; + struct i596_rfd *rfd; + int i; + // struct i596_rbd *rbd; + + lp = (struct i596_private *) dev->priv; + lp->scb.pa_rfd = I596_NULL; + + for (i = 0; i < num; i++) { + rfd = kkmalloc(struct i596_rfd, GFP_KERNEL); + if (rfd == NULL) + break; + + rfd->stat = 0; + rfd->pa_rbd = I596_NULL; + rfd->count = 0; + rfd->size = 1532; + if (i == 0) { + rfd->cmd = CMD_EOL; + lp->rx_tail = rfd; + } else { + rfd->cmd = 0; + } + rfd->pa_next = lp->scb.pa_rfd; + lp->scb.pa_rfd = va_to_pa(rfd); + lp->rx_tail->pa_next = lp->scb.pa_rfd; + } + +#if 0 + for (i = 0; ipad = 0; + rbd->count = 0; + rbd->skb = dev_alloc_skb(RX_SKB_SIZE); + if (!rbd->skb) { + printk("dev_alloc_skb failed"); + } + rbd->next = rfd->rbd; + if (i) { + rfd->rbd->prev = rbd; + rbd->size = RX_SKB_SIZE; + } else { + rbd->size = (RX_SKB_SIZE | RBD_EL); + lp->rbd_tail = rbd; + } + + rfd->rbd = rbd; + } else { + printk("Could not kmalloc rbd\n"); + } + } + lp->rbd_tail->next = rfd->rbd; +#endif + return (i); +} + +static inline void +remove_rx_bufs(struct device *dev) { + struct i596_private *lp; + struct i596_rfd *rfd; + + lp = (struct i596_private *) dev->priv; + lp->rx_tail->pa_next = I596_NULL; + + do { + rfd = pa_to_va(lp->scb.pa_rfd); + lp->scb.pa_rfd = rfd->pa_next; + kfree(rfd); + } while (rfd != lp->rx_tail); + + lp->rx_tail = 0; + +#if 0 + for (lp->rbd_list) { + } +#endif +} + +#define PORT_RESET 0x00 /* reset 82596 */ +#define PORT_SELFTEST 0x01 /* selftest */ +#define PORT_ALTSCP 0x02 /* alternate SCB address */ +#define PORT_DUMP 0x03 /* dump */ + +#define IOADDR 0xcb0 +#define IRQ 10 /* default IRQ - can be changed by ECU */ + +/* The 82596 requires two 16-bit write cycles for a port command */ +static inline void +PORT(phys_addr a, unsigned int cmd) { + if (a & 0xf) + printk("lp486e.c: PORT: address not aligned\n"); + outw(((a & 0xffff) | cmd), IOADDR); + outw(((a>>16) & 0xffff), IOADDR+2); +} + +static inline void +CA(void) { + outb(0, IOADDR+4); + udelay(8); +} + +static inline void +CLEAR_INT(void) { + outb(0, IOADDR+8); +} + +#define SIZE(x) (sizeof(x)/sizeof((x)[0])) + +#if 0 +/* selftest or dump */ +static void +i596_port_do(struct device *dev, int portcmd, char *cmdname) { + volatile struct i596_private *lp = (struct i596_private *)dev->priv; + volatile u16 *outp; + int i, m; + + memset((void *)&(lp->dump), 0, sizeof(struct i596_dump)); + outp = &(lp->dump.dump[0]); + + PORT(va_to_pa(outp), portcmd); + mdelay(30); /* random, unmotivated */ + + printk("lp486e i82596 %s result:\n", cmdname); + for (m = SIZE(lp->dump.dump); m && lp->dump.dump[m-1] == 0; m--) + ; + for (i = 0; i < m; i++) { + printk(" %04x", lp->dump.dump[i]); + if (i%8 == 7) + printk("\n"); + } + printk("\n"); +} +#endif + +static int +i596_scp_setup(struct device *dev) { + volatile struct i596_private *lp = (struct i596_private *)dev->priv; + int boguscnt; + + /* Setup SCP, ISCP, SCB */ + /* + * sysbus bits: + * only a single byte is significant - here 0x44 + * 0x80: big endian mode (details depend on stepping) + * 0x40: 1 + * 0x20: interrupt pin is active low + * 0x10: lock function disabled + * 0x08: external triggering of bus throttle timers + * 0x06: 00: 82586 compat mode, 01: segmented mode, 10: linear mode + * 0x01: unused + */ + lp->scp.sysbus = 0x00440000; /* linear mode */ + lp->scp.pad = 0; /* must be zero */ + lp->scp.pa_iscp = va_to_pa(&(lp->iscp)); + + /* + * The CPU sets the ISCP to 1 before it gives the first CA() + */ + lp->iscp.busy = 0x0001; + lp->iscp.pa_scb = va_to_pa(&(lp->scb)); + + lp->scb.command = 0; + lp->scb.status = 0; + lp->scb.pa_cmd = I596_NULL; + /* lp->scb.pa_rfd has been initialised already */ + + lp->last_cmd = jiffies; + lp->cmd_backlog = 0; + lp->cmd_head = NULL; + + /* + * Reset the 82596. + * We need to wait 10 systemclock cycles, and + * 5 serial clock cycles. + */ + PORT(0, PORT_RESET); /* address part ignored */ + udelay(100); + + /* + * Before the CA signal is asserted, the default SCP address + * (0x00fffff4) can be changed to a 16-byte aligned value + */ + PORT(va_to_pa(&lp->scp), PORT_ALTSCP); /* change the scp address */ + + /* + * The initialization procedure begins when a + * Channel Attention signal is asserted after a reset. + */ + CA(); + + CLEAR_INT(); + + /* + * The ISCP busy is cleared by the 82596 after the SCB base + * and offset are read. + */ + boguscnt = 100; + while (lp->iscp.busy) { + if (--boguscnt == 0) { + /* No i82596 present? */ + printk("%s: i82596 initialization timed out\n", + dev->name); + return 1; + } + udelay(5); + } + /* I find here boguscnt==100, so no delay was required. */ + + return 0; +} + +static int +init_i596(struct device *dev) { + volatile struct i596_private *lp; + + if (i596_scp_setup(dev)) + return 1; + + lp = (struct i596_private *) dev->priv; + lp->scb.command = 0; + + memcpy ((void *)lp->i596_config, init_setup, 14); + lp->set_conf.command = CmdConfigure; + i596_add_cmd(dev, (void *)&lp->set_conf); + + memcpy ((void *)lp->eth_addr, dev->dev_addr, 6); + lp->set_add.command = CmdIASetup; + i596_add_cmd(dev, (struct i596_cmd *)&lp->set_add); + + lp->tdr.command = CmdTDR; + i596_add_cmd(dev, (struct i596_cmd *)&lp->tdr); + + if (lp->scb.command && i596_timeout(dev, "i82596 init", 200)) + return 1; + + lp->scb.command = RX_START; + CA(); + + if (lp->scb.command && i596_timeout(dev, "Receive Unit start", 100)) + return 1; + + return 0; +} + +/* Receive a single frame */ +static inline int +i596_rx_one(struct device *dev, volatile struct i596_private *lp, + struct i596_rfd *rfd, int *frames) { + + if (rfd->stat & RFD_STAT_OK) { + /* a good frame */ + int pkt_len = (rfd->count & 0x3fff); + struct sk_buff *skb = dev_alloc_skb(pkt_len); + + (*frames)++; + + if (rfd->cmd & CMD_EOL) + printk("Received on EOL\n"); + + if (skb == NULL) { + printk ("%s: i596_rx Memory squeeze, " + "dropping packet.\n", dev->name); + lp->stats.rx_dropped++; + return 1; + } + + skb->dev = dev; + memcpy(skb_put(skb,pkt_len), rfd->data, pkt_len); + + skb->protocol = eth_type_trans(skb,dev); + netif_rx(skb); + lp->stats.rx_packets++; + } else { +#if 0 + printk("Frame reception error status %04x\n", + rfd->stat); +#endif + lp->stats.rx_errors++; + if (rfd->stat & RFD_COLLISION) + lp->stats.collisions++; + if (rfd->stat & RFD_SHORT_FRAME_ERR) + lp->stats.rx_length_errors++; + if (rfd->stat & RFD_DMA_ERR) + lp->stats.rx_over_errors++; + if (rfd->stat & RFD_NOBUFS_ERR) + lp->stats.rx_fifo_errors++; + if (rfd->stat & RFD_ALIGN_ERR) + lp->stats.rx_frame_errors++; + if (rfd->stat & RFD_CRC_ERR) + lp->stats.rx_crc_errors++; + if (rfd->stat & RFD_LENGTH_ERR) + lp->stats.rx_length_errors++; + } + rfd->stat = rfd->count = 0; + return 0; +} + +static int +i596_rx(struct device *dev) { + volatile struct i596_private *lp = (struct i596_private *) dev->priv; + struct i596_rfd *rfd; + int frames = 0; + + while (1) { + rfd = pa_to_va(lp->scb.pa_rfd); + if (!rfd) { + printk("i596_rx: NULL rfd?\n"); + return 0; + } +#if 1 + if (rfd->stat && !(rfd->stat & (RFD_STAT_C | RFD_STAT_B))) + printk("SF:%p-%04x\n", rfd, rfd->stat); +#endif + if (!(rfd->stat & RFD_STAT_C)) + break; /* next one not ready */ + if (i596_rx_one(dev, lp, rfd, &frames)) + break; /* out of memory */ + rfd->cmd = CMD_EOL; + lp->rx_tail->cmd = 0; + lp->rx_tail = rfd; + lp->scb.pa_rfd = rfd->pa_next; + } + + return frames; +} + +static void +i596_cleanup_cmd(struct device *dev) { + volatile struct i596_private *lp; + struct i596_cmd *cmd; + + lp = (struct i596_private *) dev->priv; + while (lp->cmd_head) { + cmd = (struct i596_cmd *)lp->cmd_head; + + lp->cmd_head = pa_to_va(lp->cmd_head->pa_next); + lp->cmd_backlog--; + + switch ((cmd->command) & 0x7) { + case CmdTx: { + struct tx_cmd *tx_cmd = (struct tx_cmd *) cmd; + struct i596_tbd * tx_cmd_tbd; + tx_cmd_tbd = pa_to_va(tx_cmd->pa_tbd); + + dev_kfree_skb(tx_cmd_tbd->skb); + + lp->stats.tx_errors++; + lp->stats.tx_aborted_errors++; + + cmd->pa_next = I596_NULL; + kfree((unsigned char *)tx_cmd); + break; + } + case CmdMulticastList: { + // unsigned short count = *((unsigned short *) (ptr + 1)); + + cmd->pa_next = I596_NULL; + kfree((unsigned char *)cmd); + break; + } + default: { + cmd->pa_next = I596_NULL; + break; + } + } + } + + if (lp->scb.command && i596_timeout(dev, "i596_cleanup_cmd", 100)) + ; + + lp->scb.pa_cmd = va_to_pa(lp->cmd_head); +} + +static inline void +i596_reset(struct device *dev, volatile struct i596_private *lp, int ioaddr) { + + if (lp->scb.command && i596_timeout(dev, "i596_reset", 100)) + ; + + dev->start = 0; + dev->tbusy = 1; + + lp->scb.command = CUC_ABORT | RX_ABORT; + CA(); + + /* wait for shutdown */ + if (lp->scb.command && i596_timeout(dev, "i596_reset(2)", 400)) + ; + + i596_cleanup_cmd(dev); + i596_rx(dev); + + dev->start = 1; + dev->tbusy = 0; + /*dev_kfree_skb(skb, FREE_WRITE);*/ + dev->interrupt = 0; + init_i596(dev); +} + +static void i596_add_cmd(struct device *dev, struct i596_cmd *cmd) { + volatile struct i596_private *lp = (struct i596_private *)dev->priv; + int ioaddr = dev->base_addr; + unsigned long flags; + + cmd->status = 0; + cmd->command |= (CMD_EOL | CMD_INTR); + cmd->pa_next = I596_NULL; + + save_flags(flags); + cli(); + if (lp->cmd_head) { + lp->cmd_tail->pa_next = va_to_pa(cmd); + } else { + lp->cmd_head = cmd; + if (lp->scb.command && i596_timeout(dev, "i596_add_cmd", 100)) + ; + lp->scb.pa_cmd = va_to_pa(cmd); + lp->scb.command = CUC_START; + CA(); + } + lp->cmd_tail = cmd; + lp->cmd_backlog++; + + lp->cmd_head = pa_to_va(lp->scb.pa_cmd); + restore_flags(flags); + + if (lp->cmd_backlog > 16) { + int tickssofar = jiffies - lp->last_cmd; + if (tickssofar < 25) return; + + printk("%s: command unit timed out, status resetting.\n", + dev->name); + i596_reset(dev, lp, ioaddr); + } +} + +static int +i596_open(struct device *dev) { + int i; + + if (request_irq(dev->irq, &i596_interrupt, SA_SHIRQ, dev->name, dev)) { + printk("%s: IRQ %d not free\n", dev->name, dev->irq); + return -EAGAIN; + } + +// irq2dev_map[dev->irq] = dev; + + if ((i = init_rx_bufs(dev, RX_RING_SIZE)) < RX_RING_SIZE) + printk("%s: only able to allocate %d receive buffers\n", + dev->name, i); + + if (i < 4) { +// release buffers + free_irq(dev->irq, dev); +// irq2dev_map[dev->irq] = 0; + return -EAGAIN; + } + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + MOD_INC_USE_COUNT; + + init_i596(dev); + + return 0; /* Always succeed */ +} + +static int +i596_start_xmit(struct sk_buff *skb, struct device *dev) { + volatile struct i596_private *lp = (struct i596_private *)dev->priv; + int ioaddr = dev->base_addr; + struct tx_cmd *tx_cmd; + + /* Transmitter timeout, serious problems. */ + if (dev->tbusy) { + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < 5) { + return 1; + } + printk("%s: transmit timed out, status resetting.\n", + dev->name); + lp->stats.tx_errors++; + /* Try to restart the adaptor */ + if (lp->last_restart == lp->stats.tx_packets) { + printk ("Resetting board.\n"); + + /* Shutdown and restart */ + i596_reset(dev,lp, ioaddr); + } else { + /* Issue a channel attention signal */ + printk ("Kicking board.\n"); + + lp->scb.command = (CUC_START | RX_START); + CA(); /*outw(0, ioaddr+4);*/ + + lp->last_restart = lp->stats.tx_packets; + } + dev->tbusy = 0; + dev->trans_start = jiffies; + } + + /* If some higher level thinks we've missed a tx-done interrupt + we are passed NULL. n.b. dev_tint handles the cli()/sti() + itself. */ + if (skb == NULL) { + printk("What about dev_tint\n"); + /*dev_tint(dev);*/ + return 0; + } + + /* shouldn't happen */ + if (skb->len <= 0) return 0; + + /* Block a timer-based transmit from overlapping. This could better be + done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { + printk("%s: Transmitter access conflict.\n", dev->name); + } else { + short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + dev->trans_start = jiffies; + + tx_cmd = (struct tx_cmd *) + kmalloc ((sizeof (struct tx_cmd) + + sizeof (struct i596_tbd)), GFP_ATOMIC); + if (tx_cmd == NULL) { + printk ("%s: i596_xmit Memory squeeze, dropping packet.\n", + dev->name); + lp->stats.tx_dropped++; + + dev_kfree_skb(skb); + } else { + struct i596_tbd * tx_cmd_tbd; + tx_cmd_tbd = (struct i596_tbd *) (tx_cmd+1); + tx_cmd->pa_tbd = va_to_pa(tx_cmd_tbd); + tx_cmd_tbd->pa_next = I596_NULL; + + tx_cmd->cmd.command = (CMD_FLEX | CmdTx); + + tx_cmd->pad = 0; + tx_cmd->size = 0; + tx_cmd_tbd->pad = 0; + tx_cmd_tbd->size = (EOF | length); + + tx_cmd_tbd->pa_data = va_to_pa(skb->data); + tx_cmd_tbd->skb = skb; + + if (i596_debug & LOG_SRCDST) + print_eth(skb->data); + + i596_add_cmd(dev, (struct i596_cmd *)tx_cmd); + + lp->stats.tx_packets++; + } + } + + dev->tbusy = 0; + + return 0; +} + +static void +print_eth(char *add) { + int i; + + printk ("Dest "); + for (i = 0; i < 6; i++) + printk(" %2.2X", (unsigned char) add[i]); + printk ("\n"); + + printk ("Source"); + for (i = 0; i < 6; i++) + printk(" %2.2X", (unsigned char) add[i+6]); + printk ("\n"); + + printk ("type %2.2X%2.2X\n", + (unsigned char) add[12], (unsigned char) add[13]); +} + +int __init +lp486e_probe(struct device *dev) { + volatile struct i596_private *lp; + unsigned char eth_addr[6] = { 0, 0xaa, 0, 0, 0, 0 }; + unsigned char *bios; + int i,j; + static int probed = 0; + + if (probed) + return -ENODEV; + probed++; + + if (check_region(IOADDR, LP486E_TOTAL_SIZE)) { + printk("lp486e: IO address 0x%x in use\n", IOADDR); + return -ENODEV; + } + + /* + * Allocate working memory, 16-byte aligned + */ + dev->mem_start = (unsigned long) + kmalloc(sizeof(struct i596_private) + 0x0f, GFP_KERNEL); + if (!dev->mem_start) + return -ENOMEM; + dev->priv = (void *)((dev->mem_start + 0xf) & 0xfffffff0); + lp = (struct i596_private *) dev->priv; + memset((void *)lp, 0, sizeof(struct i596_private)); + + /* + * Do we really have this thing? + */ + if (i596_scp_setup(dev)) { + kfree ((void *) dev->mem_start); + return -ENODEV; + } + + request_region(IOADDR, LP486E_TOTAL_SIZE, "lp486e"); + + dev->base_addr = IOADDR; + dev->irq = IRQ; + + ether_setup(dev); + + /* + * How do we find the ethernet address? I don't know. + * One possibility is to look at the EISA configuration area + * [0xe8000-0xe9fff]. This contains the ethernet address + * but not at a fixed address - things depend on setup options. + * + * If we find no address, or the wrong address, use + * ifconfig eth0 hw ether a1:a2:a3:a4:a5:a6 + * with the value found in the BIOS setup. + */ + bios = bus_to_virt(0xe8000); + for (j = 0; j < 0x2000; j++) { + if (bios[j] == 0 && bios[j+1] == 0xaa && bios[j+2] == 0) { + printk("%s: maybe address at BIOS 0x%x:", + dev->name, 0xe8000+j); + for (i = 0; i < 6; i++) { + eth_addr[i] = bios[i+j]; + printk(" %2.2X", eth_addr[i]); + } + printk("\n"); + } + } + + printk("%s: lp486e 82596 at %#3x, IRQ %d,", dev->name, IOADDR, IRQ); + for (i = 0; i < 6; i++) + printk(" %2.2X", dev->dev_addr[i] = eth_addr[i]); + printk("\n"); + + /* The LP486E-specific entries in the device structure. */ + dev->open = &i596_open; + dev->stop = &i596_close; + dev->hard_start_xmit = &i596_start_xmit; + dev->get_stats = &i596_get_stats; + dev->set_multicast_list = &set_multicast_list; + +#if 0 + /* selftest reports 0x320925ae - don't know what that means */ + i596_port_do(dev, PORT_SELFTEST, "selftest"); + i596_port_do(dev, PORT_DUMP, "dump"); +#endif + return 0; +} + +static void inline +i596_handle_CU_completion(struct device *dev, + volatile struct i596_private *lp, + unsigned short status, + unsigned short *ack_cmdp) { + volatile struct i596_cmd *cmd; + int frames_out = 0; + int commands_done = 0; + int cmd_val; + + cmd = lp->cmd_head; + + while (lp->cmd_head && (lp->cmd_head->status & CMD_STAT_C)) { + cmd = lp->cmd_head; + + lp->cmd_head = pa_to_va(lp->cmd_head->pa_next); + lp->cmd_backlog--; + + commands_done++; + cmd_val = cmd->command & 0x7; +#if 0 + printk("finished CU %s command (%d)\n", + CUcmdnames[cmd_val], cmd_val); +#endif + switch (cmd_val) { + case CmdTx: + { + struct tx_cmd *tx_cmd; + struct i596_tbd *tx_cmd_tbd; + + tx_cmd = (struct tx_cmd *) cmd; + tx_cmd_tbd = pa_to_va(tx_cmd->pa_tbd); + + frames_out++; + if (cmd->status & CMD_STAT_OK) { + if (i596_debug) + print_eth(pa_to_va(tx_cmd_tbd->pa_data)); + } else { + lp->stats.tx_errors++; + if (i596_debug) + printk("transmission failure:%04x\n", + cmd->status); + if (cmd->status & 0x0020) + lp->stats.collisions++; + if (!(cmd->status & 0x0040)) + lp->stats.tx_heartbeat_errors++; + if (cmd->status & 0x0400) + lp->stats.tx_carrier_errors++; + if (cmd->status & 0x0800) + lp->stats.collisions++; + if (cmd->status & 0x1000) + lp->stats.tx_aborted_errors++; + } + dev_kfree_skb(tx_cmd_tbd->skb); + + cmd->pa_next = I596_NULL; + kfree((unsigned char *)tx_cmd); + break; + } + + case CmdMulticastList: + cmd->pa_next = I596_NULL; + kfree((unsigned char *)cmd); + break; + + case CmdTDR: + { + unsigned long status = *((unsigned long *) (cmd + 1)); + if (status & 0x8000) { + if (i596_debug) + printk("%s: link ok.\n", dev->name); + } else { + if (status & 0x4000) + printk("%s: Transceiver problem.\n", + dev->name); + if (status & 0x2000) + printk("%s: Termination problem.\n", + dev->name); + if (status & 0x1000) + printk("%s: Short circuit.\n", + dev->name); + printk("%s: Time %ld.\n", + dev->name, status & 0x07ff); + } + } + default: + cmd->pa_next = I596_NULL; + lp->last_cmd = jiffies; + + } + } + + cmd = lp->cmd_head; + while (cmd && (cmd != lp->cmd_tail)) { + cmd->command &= 0x1fff; + cmd = pa_to_va(cmd->pa_next); + } + + if (lp->cmd_head && dev->start) + *ack_cmdp |= CUC_START; + lp->scb.pa_cmd = va_to_pa(lp->cmd_head); +} + +static void +i596_interrupt (int irq, void *dev_instance, struct pt_regs *regs) { + struct device *dev = (struct device *) dev_instance; + volatile struct i596_private *lp; + unsigned short status, ack_cmd = 0; + int frames_in = 0; + + if (dev == NULL) { + printk ("i596_interrupt(): irq %d for unknown device.\n", irq); + return; + } + + if (dev->interrupt) + printk("%s: Re-entering the interrupt handler.\n", dev->name); + dev->interrupt = 1; + + lp = (struct i596_private *) dev->priv; + + /* + * The 82596 examines the command, performs the required action, + * and then clears the SCB command word. + */ + if (lp->scb.command && i596_timeout(dev, "interrupt", 40)) + ; + + /* + * The status word indicates the status of the 82596. + * It is modified only by the 82596. + * + * [So, we must not clear it. I find often status 0xffff, + * which is not one of the values allowed by the docs.] + */ + status = lp->scb.status; +#if 0 + if (i596_debug) { + printk("%s: i596 interrupt, ", dev->name); + i596_out_status(status); + } +#endif + /* Impossible, but it happens - perhaps when we get + a receive interrupt but scb.pa_rfd is I596_NULL. */ + if (status == 0xffff) { + printk("%s: i596_interrupt: got status 0xffff\n", dev->name); + goto out; + } + + ack_cmd = (status & STAT_ACK); + + if (status & (STAT_CX | STAT_CNA)) + i596_handle_CU_completion(dev, lp, status, &ack_cmd); + + if (status & (STAT_FR | STAT_RNR)) { + /* Restart the receive unit when it got inactive somehow */ + if ((status & STAT_RNR) && dev->start) + ack_cmd |= RX_START; + + if (status & STAT_FR) { + frames_in = i596_rx(dev); + if (!frames_in) + printk("receive frame reported, but no frames\n"); + } + } + + /* acknowledge the interrupt */ + /* + if ((lp->scb.pa_cmd != I596_NULL) && dev->start) + ack_cmd |= CUC_START; + */ + + if (lp->scb.command && i596_timeout(dev, "i596 interrupt", 100)) + ; + + lp->scb.command = ack_cmd; + + CLEAR_INT(); + CA(); + + out: + dev->interrupt = 0; + return; +} + +static int i596_close(struct device *dev) { + volatile struct i596_private *lp = (struct i596_private *)dev->priv; + + dev->start = 0; + dev->tbusy = 1; + + if (i596_debug) + printk("%s: Shutting down ethercard, status was %4.4x.\n", + dev->name, lp->scb.status); + + lp->scb.command = (CUC_ABORT | RX_ABORT); + CA(); + + i596_cleanup_cmd(dev); + + if (lp->scb.command && i596_timeout(dev, "i596_close", 200)) + ; + + free_irq(dev->irq, dev); + remove_rx_bufs(dev); + MOD_DEC_USE_COUNT; + + return 0; +} + +static struct enet_statistics * i596_get_stats(struct device *dev) { + struct i596_private *lp = (struct i596_private *)dev->priv; + + return &lp->stats; +} + +/* +* Set or clear the multicast filter for this adaptor. +*/ + +static void set_multicast_list(struct device *dev) { + volatile struct i596_private *lp = (struct i596_private *)dev->priv; + struct i596_cmd *cmd; + + if (i596_debug > 1) + printk ("%s: set multicast list %d\n", + dev->name, dev->mc_count); + + if (dev->mc_count > 0) { + struct dev_mc_list *dmi; + char *cp; + cmd = (struct i596_cmd *) + kmalloc(sizeof(struct i596_cmd)+2+dev->mc_count*6, + GFP_ATOMIC); + if (cmd == NULL) { + printk ("%s: set_multicast Memory squeeze.\n", + dev->name); + return; + } + cmd->command = CmdMulticastList; + *((unsigned short *) (cmd + 1)) = dev->mc_count * 6; + cp = ((char *)(cmd + 1))+2; + for (dmi = dev->mc_list; dmi != NULL; dmi = dmi->next) { + memcpy(cp, dmi,6); + cp += 6; + } + if (i596_debug & LOG_SRCDST) + print_eth (((char *)(cmd + 1)) + 2); + i596_add_cmd(dev, cmd); + } else { + if (lp->set_conf.pa_next != I596_NULL) { + return; + } + if (dev->mc_count == 0 && + !(dev->flags & (IFF_PROMISC | IFF_ALLMULTI))) { + if (dev->flags & IFF_ALLMULTI) + dev->flags |= IFF_PROMISC; + lp->i596_config[8] &= ~0x01; + } else { + lp->i596_config[8] |= 0x01; + } + + i596_add_cmd(dev, (struct i596_cmd *) &lp->set_conf); + } +} + +#ifdef HAVE_DEVLIST +static unsigned int lp486e_portlist[] = {0xcb0, 0}; +struct netdev_entry lp486e_drv = { + "lp486e", + lp486e_probe, + LP486E_TOTAL_SIZE, + lp486e_portlist +}; +#endif + +#ifdef MODULE +MODULE_AUTHOR("Ard van Breemen "); +MODULE_DESCRIPTION("Intel Panther onboard i82596 driver"); +MODULE_PARM(debug, "i"); +//MODULE_PARM(max_interrupt_work, "i"); +//MODULE_PARM(reverse_probe, "i"); +//MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); + +static char devicename[9] = { 0, }; +static struct device dev_lp486e = { + devicename, /* device name inserted by /linux/drivers/net/net_init.c */ + 0, 0, 0, 0, + IOADDR, IRQ, + 0, 0, 0, NULL, NULL +}; + +static int full_duplex; +static int options; +static int io = IOADDR; +static int irq = IRQ; + +int init_module(void) { + struct device *dev = &dev_lp486e; + dev->name = devicename; + dev->irq = irq; + dev->base_addr = io; + dev->init = lp486e_probe; + if (register_netdev(dev) != 0) { + return -EIO; + } + full_duplex = 0; + options = 0; + return 0; +} + +void cleanup_module(void) { + unregister_netdev(&dev_lp486e); + kfree((void *)dev_lp486e.mem_start); + dev_lp486e.priv = NULL; + /* If we don't do this, we can't re-insmod it later. */ + release_region(dev_lp486e.base_addr, LP486E_TOTAL_SIZE); +} +#endif /* MODULE */ diff --git a/drivers/net/strip.c b/drivers/net/strip.c index af0282e627d1..571ac88bef82 100644 --- a/drivers/net/strip.c +++ b/drivers/net/strip.c @@ -14,7 +14,7 @@ * for kernel-based devices like TTY. It interfaces between a * raw TTY, and the kernel's INET protocol layers (via DDI). * - * Version: @(#)strip.c 1.3 July 1997 + * Version: @(#)strip.c 1.4 September 2000 * * Author: Stuart Cheshire * @@ -66,12 +66,15 @@ * It is no longer necessarily to manually set the radio's * rate permanently to 115200 -- the driver handles setting * the rate automatically. + * + * v1.4 September 2000 (AB) + * Added support for long serial numbers. */ #ifdef MODULE -static const char StripVersion[] = "1.3-STUART.CHESHIRE-MODULAR"; +static const char StripVersion[] = "1.4-STUART.CHESHIRE-MODULAR"; #else -static const char StripVersion[] = "1.3-STUART.CHESHIRE"; +static const char StripVersion[] = "1.4-STUART.CHESHIRE"; #endif #define TICKLE_TIMERS 0 @@ -897,20 +900,37 @@ static void set_baud(struct tty_struct *tty, unsigned int baudcode) * Convert a string to a Metricom Address. */ -#define IS_RADIO_ADDRESS(p) ( \ +#define IS_RADIO_ADDRESS_1(p) ( \ isdigit((p)[0]) && isdigit((p)[1]) && isdigit((p)[2]) && isdigit((p)[3]) && \ (p)[4] == '-' && \ isdigit((p)[5]) && isdigit((p)[6]) && isdigit((p)[7]) && isdigit((p)[8]) ) +#define IS_RADIO_ADDRESS_2(p) ( \ + isdigit((p)[0]) && isdigit((p)[1]) && \ + (p)[2] == '-' && \ + isdigit((p)[3]) && isdigit((p)[4]) && isdigit((p)[5]) && isdigit((p)[6]) && \ + (p)[7] == '-' && \ + isdigit((p)[8]) && isdigit((p)[9]) && isdigit((p)[10]) && isdigit((p)[11]) ) + static int string_to_radio_address(MetricomAddress *addr, __u8 *p) { - if (!IS_RADIO_ADDRESS(p)) return(1); + if (IS_RADIO_ADDRESS_2(p)) + { + addr->c[0] = 0; + addr->c[1] = (READHEX(p[0]) << 4 | READHEX(p[1])) ^ 0xFF; + addr->c[2] = READHEX(p[3]) << 4 | READHEX(p[4]); + addr->c[3] = READHEX(p[5]) << 4 | READHEX(p[6]); + addr->c[4] = READHEX(p[8]) << 4 | READHEX(p[9]); + addr->c[5] = READHEX(p[10]) << 4 | READHEX(p[11]); + }else{ + if(!IS_RADIO_ADDRESS_1(p)) return(1); addr->c[0] = 0; addr->c[1] = 0; addr->c[2] = READHEX(p[0]) << 4 | READHEX(p[1]); addr->c[3] = READHEX(p[2]) << 4 | READHEX(p[3]); addr->c[4] = READHEX(p[5]) << 4 | READHEX(p[6]); addr->c[5] = READHEX(p[7]) << 4 | READHEX(p[8]); + } return(0); } @@ -920,6 +940,9 @@ static int string_to_radio_address(MetricomAddress *addr, __u8 *p) static __u8 *radio_address_to_string(const MetricomAddress *addr, MetricomAddressString *p) { + if(addr->c[1]) + sprintf(p->c, "%02X-%02X%02X-%02X%02X", addr->c[1] ^ 0xFF, addr->c[2], addr->c[3], addr->c[4], addr->c[5]); + else sprintf(p->c, "%02X%02X-%02X%02X", addr->c[2], addr->c[3], addr->c[4], addr->c[5]); return(p->c); } @@ -1481,6 +1504,12 @@ static unsigned char *strip_make_packet(unsigned char *buffer, struct strip *str *ptr++ = 0x0D; *ptr++ = '*'; + if(haddr.c[1]) + { + *ptr++ = hextable[(haddr.c[1] >> 4) ^ 0xF]; + *ptr++ = hextable[(haddr.c[1] & 0xF) ^ 0xF]; + *ptr++ = '-'; + } *ptr++ = hextable[haddr.c[2] >> 4]; *ptr++ = hextable[haddr.c[2] & 0xF]; *ptr++ = hextable[haddr.c[3] >> 4]; diff --git a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c index 2a8e01800147..63bf3e7d1a21 100644 --- a/drivers/scsi/3w-xxxx.c +++ b/drivers/scsi/3w-xxxx.c @@ -3,6 +3,8 @@ Written By: Adam Radford Modifications By: Joel Jacobson + Arnaldo Carvalho de Melo + Copyright (C) 1999-2000 3ware Inc. @@ -64,6 +66,8 @@ Bug fix so hot spare drives don't show up. 1.02.00.002 - Fix bug with tw_setfeature() call that caused oops on some systems. + 12/09/2000 - release previously allocated resources on failure at + tw_allocate_memory (acme) */ #include @@ -416,37 +420,29 @@ int tw_aen_read_queue(TW_Device_Extension *tw_dev, int request_id) /* This function will allocate memory and check if it is 16 d-word aligned */ int tw_allocate_memory(TW_Device_Extension *tw_dev, int request_id, int size, int which) { - u32 *virt_addr; + u32 *virt_addr = kmalloc(size, GFP_ATOMIC); dprintk(KERN_NOTICE "3w-xxxx: tw_allocate_memory()\n"); + if (!virt_addr) { + printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): kmalloc() failed.\n"); + return 1; + } + + if ((u32)virt_addr % TW_ALIGNMENT) { + kfree(virt_addr); + printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): Found unaligned address.\n"); + return 1; + } + if (which == 0) { - /* Allocate command packet memory */ - virt_addr = kmalloc(size, GFP_ATOMIC); - if (virt_addr == NULL) { - printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): kmalloc() failed.\n"); - return 1; - } - if ((u32)virt_addr % TW_ALIGNMENT) { - printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): Found unaligned address.\n"); - return 1; - } tw_dev->command_packet_virtual_address[request_id] = virt_addr; tw_dev->command_packet_physical_address[request_id] = - virt_to_bus(virt_addr); + virt_to_bus(virt_addr); } else { - /* Allocate generic buffer */ - virt_addr = kmalloc(size, GFP_ATOMIC); - if (virt_addr == NULL) { - printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): kmalloc() failed.\n"); - return 1; - } - if ((u32)virt_addr % TW_ALIGNMENT) { - printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): Found unaligned address.\n"); - return 1; - } tw_dev->alignment_virtual_address[request_id] = virt_addr; - tw_dev->alignment_physical_address[request_id] = virt_to_bus(virt_addr); + tw_dev->alignment_physical_address[request_id] = + virt_to_bus(virt_addr); } return 0; } /* End tw_allocate_memory() */ diff --git a/drivers/scsi/Config.in b/drivers/scsi/Config.in index 89ec87c27cb1..7d4109c1f307 100644 --- a/drivers/scsi/Config.in +++ b/drivers/scsi/Config.in @@ -2,6 +2,7 @@ comment 'SCSI support type (disk, tape, CD-ROM)' dep_tristate 'SCSI disk support' CONFIG_BLK_DEV_SD $CONFIG_SCSI dep_tristate 'SCSI tape support' CONFIG_CHR_DEV_ST $CONFIG_SCSI +dep_tristate 'OnStream SC-x0 SCSI tape support' CONFIG_CHR_DEV_OSST $CONFIG_SCSI dep_tristate 'SCSI CD-ROM support' CONFIG_BLK_DEV_SR $CONFIG_SCSI if [ "$CONFIG_BLK_DEV_SR" != "n" ]; then bool ' Enable vendor-specific extensions (for SCSI CDROM)' CONFIG_BLK_DEV_SR_VENDOR diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 276fba42a837..fa22c259c82f 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -61,6 +61,14 @@ else endif endif +ifeq ($(CONFIG_CHR_DEV_OSST),y) +L_OBJS += osst.o +else + ifeq ($(CONFIG_CHR_DEV_OSST),m) + M_OBJS += osst.o + endif +endif + ifeq ($(CONFIG_BLK_DEV_SD),y) L_OBJS += sd.o sd_ioctl.o else diff --git a/drivers/scsi/README.osst b/drivers/scsi/README.osst new file mode 100644 index 000000000000..32d99afdd4e3 --- /dev/null +++ b/drivers/scsi/README.osst @@ -0,0 +1,259 @@ +README file for the osst driver +=============================== +(w) Kurt Garloff 10/2000 + +This file describes the osst driver as of version 0.8.x/0.9.x, the released +version of the osst driver. +It is intended to help advanced users to understand the role of osst and to +get them started using (and maybe debugging) it. +It won't address issues like "How do I compile a kernel?" or "How do I load +a module?", as these are too basic. +Once the OnStream got merged into the official kernel, the distro makers +will provide the OnStream support for those who are not familiar with +hacking their kernels. + + +Purpose +------- +The osst driver was developed, because the standard SCSI tape driver in +Linux, st, does not support the OnStream SC-x0 SCSI tape. The st is not to +blame for that, as the OnStream tape drives do not support the standard SCSI +command set for Serial Access Storage Devices (SASDs), which basically +corresponds to the QIC-157 spec. +Nevertheless, the OnStream tapes are nice pieces of hardware and therefore +the osst driver has been written to make these tape devs supported by Linux. +The driver is free software. It's released under the GNU GPL and planned to +be integrated into the mainstream kernel. + + +Implementation +-------------- +The osst is a new high-level SCSI driver, just like st, sr, sd and sg. It +can be compiled into the kernel or loaded as a module. +As it represents a new device, it got assigned a new device node: /dev/osstX +are character devices with major no 206 and minor numbers like the /dev/stX +devices. If those are not present, you may create them by calling +Makedevs.sh as root (see below). +The driver started beig a copy of st and as such, the osst devices' +behaviour looks very much the same as st to the userspace applications. + + +History +------- +In the first place, osst shared it's identity very much with st. That meant +that it used the same kernel structures and the same device node as st. +So you could only have either of them being present in the kernel. This has +been fixed by registering an own device, now. +st and osst can coexist, each only accessing the devices it can support by +their own device. + + +Installation +------------ +There is an easy and a correct approach to install the osst driver +(a) Without patching your kernel +(b) Full integration into your kernel +(c) The mainstream kernel does already include the osst driver + +While it is still possible to use possibility (a), i.e. to compile the osst +driver as a module without patching your kernel, this is not recommended, as +you then have only very limited possibility to take advantage of the possible +coexistence of st and osst. This possibility will be removed later. +Anyway here is a description of how installation works. + +Make sure you have a configured 2.2.1x Linux kernel in /usr/src/linux by +installing the approriate packages from your distro or by fetching the +source tarball from ftp.kernel.org or mirror and unpacking it in /usr/src/ +If your kernel is not yet configure, get a reasonable config file by doing +cd /usr/src/linux +make oldconfig # You may use make menuconfig if starting + # from scratch +make dep + +(a) Without patching your kernel + +cd /path/to/onstream/driver/ # REPLACE THIS WITH THE REAL PATH +make +make install + +That's all. You will get a couple of warnings on compilation, which you may +ignore. The osst.o module is now in your module directory ready for loading +and the device nodes have been created. + +(b) Full integration into the kernel. You need to have a reasonable kernel + config, otherwise you may be unable to use your system, as the produced + kernel may not support your hardware! IF YOU NEVER COMPILED YOUR OWN + KERNEL BEFORE, DON'T PROCEED, UNLESS YOU SUCEEDED USING YOUR SELF- + COMPILED KERNELS. + +cd /usr/src/linux +patch -p1 < /path/to/onstream/driver/osst-2X.diff # USE REAL PATH! X = 2,3,4 +cp -p /path/to/onstream/driver/osst.? drivers/scsi/ # Install drv srcs +cp -p /path/to/onstream/driver/osst_options.h drivers/scsi/ + +(b) and (c) + +make oldconfig # or menuconfig / xconfig if you prefer + # enable the OSST driver! +make dep +make bzlilo # to recopmile your kernel and have it + # installed +make modules # to compile the kernel modules +make modules_install # to install your modules +depmod -a # append kernel version if necessary + +Now, your osst driver is inside the kernel or available as a module, +depending on your choice during kernel config. You may still need to create +the device nodes by calling the Makedevs.sh script (see below) manually, +unless you use a devfs kernel, where this won't be needed. + +To load your module, you may use the command +modprobe osst +as root. dmesg should show you, whether your OnStream tapes have been +recognized. + +If you want to have the module autoloaded on access to /dev/osst, you may +add somethind like +alias char-major-206 osst +to your /etc/modules.conf (old name: conf.modules). + +You may find it convenient to create a symbolic link +ln -s nosst0 /dev/tape +to make programs assuming a default name of /dev/tape more convenient to +use. + + +Using it +-------- +You may use the OnStream tape driver with your standard backup software, +which may be tar, cpio, amanda, arkeia, BRU, Lone Tar, ... +by specifying /dev/(n)osst0 as the tape device to use or using the above +symlink trick. The IOCTLs to control tape operation are also mostly +supported and you may try the mt (or mt_st) program to jump between +filemarks, eject the tape, ... +There's one limitation: You need to use a blocksize of 32kB. + +If you just want to get started with standard software, here is an example +for creating and restoring a full backup: +# Backup +tar cvf - / --exclude /proc | buffer -s 32k -m 24M -B -t -o /dev/nosst0 +# Restore +buffer -s 32k -m 8M -B -t -i /dev/osst0 | tar xvf - -C / + +The buffer command has been used to buffer the data before it goes to the +tape (or the filesystem) in order to smooth out the data stream and prevent +the tape from needing to stop and rewind. The OnStream does have an internal +buffer and a variable speed which help this, but especially on writing, the +buffering still proves useful in most cases. It also pads the data to +guarantees the blocksize of 32k. (Otherwise you may pass the -b64 option to +tar.) +Expect something like 1.8MB/s for the SC-x0 drives and 0.9MB/s for the DI-30. +The USB drive will give you about 0.7MB/s. +On a fast machine, you may profit from software data compression (z flag for +tar). + + +USB and IDE +----------- +Via the SCSI emulation layers usb-storage and ide-scsi, you can also use the +osst driver to drive the USB-30 and the DI-30 drives. (Unfortunately, there +is no such layer for the parallel port, otherwise the DP-30 would work as +well.) For the USB support, you need the latest 2.4.0-test kernels and the +latest usb-storage driver from +http://www.linux-usb.org/ +http://sourceforge.net/cvs/?group_id=3581 + +Note that the ide-tape driver as of 1.16f uses a slightly outdated on-tape +format and therefore is not compeletely interoperable with osst tapes. + +The ADR-x0 line is fully SCSI-2 compliant and is spported by st, not osst. +The on-tape format is supposed to be compatible with the one used by osst. + + +Feedback and updates +-------------------- +The driver development is coordinated through a mailing list + +a CVS repository and some web pages. +The tester's pages which contain recent news and updated drivers to download +can be found on +http://linux1.onstream.nl/test/ + +If you find any problems, please have a look at the tester's page in order +to see whether the problem is already known and solved. Otherwise, please +report it to the mailing list. Your feedback is welcome. (This holds also +for reports of successful usage, of course.) +In case of trouble, please do always provide the following infos: +* driver and kernel version used (see syslog) +* driver messages (syslog) +* SCSI config and OnStream Firmware (/proc/scsi/scsi) +* description of error. Is it reproducible? +* software and commands used + +You may subscribe to the mailing list, BTW, it's a majordomo list. + + +Status +------ +0.8.0 was the first widespread BETA release. Since then a lot of reports +have been sent, but mostly reported success or only minor trouble. +All the issues have been addressed. +Check the web pages for more info about the current developments. + + +Acknowledgements +---------------- +The driver has been started by making a copy of Kai Makisara's st driver. +Most of the development has been done by Willem Riede. The presence of the +userspace program osg (onstreamsg) from Terry Hardie has been rather +helpful. The same holds for Gadi Oxman's ide-tape support for the DI-30. +I did add some patches to those drivers as well and coordinated things a +little bit. +Note that most of them did mostly spend their spare time for the creation of +this driver. +The people from OnStream, especially Jack Bombeeck did support this project +and always tried to answer HW or FW related questions. Furthermore, he +pushed the FW developers to do the right things. +SuSE did support this project by allowing me to work on it during my working +time for them and by integrating the driver into their distro. + +More people did help by sending useful comments. Sorry to those who have +been forgotten. Thanks to all the GNU/FSF and Linux developers who made this +platform such an interesting, nice and stable platform. +Thanks go to those who tested the drivers and did send useful reports. Your +help is needed! + + +Makedevs.sh +----------- +#!/bin/sh +# Script to create OnStream SC-x0 device nodes (major 206) +# Usage: Makedevs.sh [nos [path to dev]] +# $Id: README.osst,v 1.5.6.1 2000/10/10 13:45:37 garloff Exp $ +major=206 +nrs=4 +dir=/dev +test -z "$1" || nrs=$1 +test -z "$2" || dir=$2 +declare -i nr +nr=0 +test -d $dir || mkdir -p $dir +while test $nr -lt $nrs; do + mknod $dir/osst$nr c $major $nr + chown 0.disk $dir/osst$nr; chmod 660 $dir/osst$nr; + mknod $dir/nosst$nr c $major $[nr+128] + chown 0.disk $dir/nosst$nr; chmod 660 $dir/nosst$nr; + mknod $dir/osst${nr}l c $major $[nr+32] + chown 0.disk $dir/osst${nr}l; chmod 660 $dir/osst${nr}l; + mknod $dir/nosst${nr}l c $major $[nr+160] + chown 0.disk $dir/nosst${nr}l; chmod 660 $dir/nosst${nr}l; + mknod $dir/osst${nr}m c $major $[nr+64] + chown 0.disk $dir/osst${nr}m; chmod 660 $dir/osst${nr}m; + mknod $dir/nosst${nr}m c $major $[nr+192] + chown 0.disk $dir/nosst${nr}m; chmod 660 $dir/nosst${nr}m; + mknod $dir/osst${nr}a c $major $[nr+96] + chown 0.disk $dir/osst${nr}a; chmod 660 $dir/osst${nr}a; + mknod $dir/nosst${nr}a c $major $[nr+224] + chown 0.disk $dir/nosst${nr}a; chmod 660 $dir/nosst${nr}a; + let nr+=1 +done diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index fdf83232924a..2f0d88462dfd 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -898,6 +898,9 @@ __initfunc(unsigned int scsi_init(void)) #ifdef CONFIG_CHR_DEV_ST scsi_register_device(&st_template); #endif +#ifdef CONFIG_CHR_DEV_OSST + scsi_register_device(&osst_template); +#endif #ifdef CONFIG_CHR_DEV_SG scsi_register_device(&sg_template); #endif diff --git a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h index 799f00c3a4a8..149008af08ea 100644 --- a/drivers/scsi/hosts.h +++ b/drivers/scsi/hosts.h @@ -458,6 +458,7 @@ struct Scsi_Device_Template extern struct Scsi_Device_Template sd_template; extern struct Scsi_Device_Template st_template; +extern struct Scsi_Device_Template osst_template; extern struct Scsi_Device_Template sr_template; extern struct Scsi_Device_Template sg_template; diff --git a/drivers/scsi/i60uscsi.c b/drivers/scsi/i60uscsi.c index 6e8e650b0339..a5a7df4aa681 100644 --- a/drivers/scsi/i60uscsi.c +++ b/drivers/scsi/i60uscsi.c @@ -765,10 +765,10 @@ int Addinia100_into_Adapter_table(WORD wBIOS, WORD wBASE, BYTE bInterrupt, if (inia100_adpt[i].ADPT_BIOS < wBIOS) continue; if (inia100_adpt[i].ADPT_BIOS == wBIOS) { - if (inia100_adpt[i].ADPT_BASE == wBASE) + if (inia100_adpt[i].ADPT_BASE == wBASE) { if (inia100_adpt[i].ADPT_Bus != 0xFF) return (FAILURE); - else if (inia100_adpt[i].ADPT_BASE < wBASE) + } else if (inia100_adpt[i].ADPT_BASE < wBASE) continue; } for (j = MAX_SUPPORTED_ADAPTERS - 1; j > i; j--) { diff --git a/drivers/scsi/osst.c b/drivers/scsi/osst.c new file mode 100644 index 000000000000..b73788b39c6f --- /dev/null +++ b/drivers/scsi/osst.c @@ -0,0 +1,5112 @@ +/* + SCSI Tape Driver for Linux version 1.1 and newer. See the accompanying + file README.st for more information. + + History: + + OnStream SCSI Tape support (osst) cloned from st.c by + Willem Riede (osst@riede.org) Feb 2000 + Fixes ... Kurt Garloff Mar 2000 + + Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara. + Contribution and ideas from several people including (in alphabetical + order) Klaus Ehrenfried, Wolfgang Denk, Steve Hirsch, Andreas Koppenh"ofer, + Michael Leodolter, Eyal Lebedinsky, J"org Weule, and Eric Youngdale. + + Copyright 1992 - 2000 Kai Makisara + email Kai.Makisara@metla.fi + + $Header: /home/cvsroot/Driver/osst.c,v 1.28.2.7 2000/10/05 00:45:52 riede Exp $ + + Last modified: Wed Feb 2 22:04:05 2000 by makisara@kai.makisara.local + Some small formal changes - aeb, 950809 +*/ + +static const char * cvsid = "$Id: osst.c,v 1.28.2.7 2000/10/05 00:45:52 riede Exp $"; +const char * osst_version = "0.8.5"; + +/* The "failure to reconnect" firmware bug */ +#define OSST_FW_NEED_POLL_MIN 10602 /*(107A)*/ +#define OSST_FW_NEED_POLL_MAX 10708 /*(108D)*/ +#define OSST_FW_NEED_POLL(x,d) ((x) >= OSST_FW_NEED_POLL_MIN && (x) <= OSST_FW_NEED_POLL_MAX && d->host->this_id != 7) + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* The driver prints some debugging information on the console if DEBUG + is defined and non-zero. */ +#define DEBUG 0 + +/* The message level for the debug messages is currently set to KERN_NOTICE + so that people can easily see the messages. Later when the debugging messages + in the drivers are more widely classified, this may be changed to KERN_DEBUG. */ +#define OSST_DEB_MSG KERN_NOTICE + +#define MAJOR_NR OSST_MAJOR +#include + +#include "scsi.h" +#include "hosts.h" +#include + +#define ST_KILOBYTE 1024 + +#include "st.h" +#include "osst.h" +#include "osst_options.h" +#include "osst_detect.h" + +#include "constants.h" + +#ifdef MODULE +MODULE_PARM(buffer_kbs, "i"); +MODULE_PARM(write_threshold_kbs, "i"); +MODULE_PARM(max_buffers, "i"); +MODULE_PARM(max_sg_segs, "i"); +static int buffer_kbs = 0; +static int write_threshold_kbs = 0; +static int max_buffers = 0; +static int max_sg_segs = 0; +#endif + +/* Some default definitions have been moved to osst_options.h */ +#define OSST_BUFFER_SIZE (OSST_BUFFER_BLOCKS * ST_KILOBYTE) +#define OSST_WRITE_THRESHOLD (OSST_WRITE_THRESHOLD_BLOCKS * ST_KILOBYTE) + +/* The buffer size should fit into the 24 bits for length in the + 6-byte SCSI read and write commands. */ +#if OSST_BUFFER_SIZE >= (2 << 24 - 1) +#error "Buffer size should not exceed (2 << 24 - 1) bytes!" +#endif + +#if DEBUG +static int debugging = 1; +#endif + +#define MAX_RETRIES 0 +#define MAX_WRITE_RETRIES 0 +#define MAX_READY_RETRIES 5 +#define NO_TAPE NOT_READY + +#define OSST_TIMEOUT (200 * HZ) +#define OSST_LONG_TIMEOUT (1800 * HZ) + +#define TAPE_NR(x) (MINOR(x) & ~(128 | ST_MODE_MASK)) +#define TAPE_MODE(x) ((MINOR(x) & ST_MODE_MASK) >> ST_MODE_SHIFT) + +/* Internal ioctl to set both density (uppermost 8 bits) and blocksize (lower + 24 bits) */ +#define SET_DENS_AND_BLK 0x10001 + +static int osst_nbr_buffers; +static OSST_buffer **osst_buffers; +static int osst_buffer_size = OSST_BUFFER_SIZE; +static int osst_write_threshold = OSST_WRITE_THRESHOLD; +static int osst_max_buffers = OSST_MAX_BUFFERS; +static int osst_max_sg_segs = OSST_MAX_SG; + +static OS_Scsi_Tape ** os_scsi_tapes = NULL; + +static int modes_defined = FALSE; + +static OSST_buffer *new_tape_buffer(int, int); +static int enlarge_buffer(OSST_buffer *, int, int); +static void normalize_buffer(OSST_buffer *); +static int append_to_buffer(const char *, OSST_buffer *, int); +static int from_buffer(OSST_buffer *, char *, int); +static int osst_zero_buffer_tail(OSST_buffer *); +static int osst_copy_to_buffer(OSST_buffer *, unsigned char *); +static int osst_copy_from_buffer(OSST_buffer *, unsigned char *); + +static int osst_init(void); +static int osst_attach(Scsi_Device *); +static int osst_detect(Scsi_Device *); +static void osst_detach(Scsi_Device *); + +struct Scsi_Device_Template osst_template = {NULL, "OnStream tape", "osst", NULL, TYPE_TAPE, + OSST_MAJOR, 0, 0, 0, 0, + osst_detect, osst_init, + NULL, osst_attach, osst_detach}; + +static int osst_int_ioctl(OS_Scsi_Tape *STp, Scsi_Cmnd ** aSCpnt, unsigned int cmd_in,unsigned long arg); + +static int osst_set_frame_position(OS_Scsi_Tape *STp, Scsi_Cmnd ** aSCpnt, int frame, int skip); + +static int osst_get_frame_position(OS_Scsi_Tape *STp, Scsi_Cmnd ** aSCpnt); + +static int osst_flush_write_buffer(OS_Scsi_Tape *STp, Scsi_Cmnd ** aSCpnt, int file_blk); + +static int osst_write_error_recovery(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int pending); + + +/* Routines that handle the interaction with mid-layer SCSI routines */ + +/* Convert the result to success code */ +static int osst_chk_result(Scsi_Cmnd * SCpnt) +{ + int dev = TAPE_NR(SCpnt->request.rq_dev); + int result = SCpnt->result; + unsigned char * sense = SCpnt->sense_buffer, scode; +#if DEBUG + const char *stp; +#endif + + if (!result /* && SCpnt->sense_buffer[0] == 0 */ ) + return 0; + + if (driver_byte(result) & DRIVER_SENSE) + scode = sense[2] & 0x0f; + else { + sense[0] = 0; /* We don't have sense data if this byte is zero */ + scode = 0; + if ((sense[2] & 0x0f) == 1) + return 0; /* FIXME -- is this safe? */ + } + +#if DEBUG + if (debugging) { + printk(OSST_DEB_MSG "osst%d: Error: %x, cmd: %x %x %x %x %x %x Len: %d\n", + dev, result, + SCpnt->data_cmnd[0], SCpnt->data_cmnd[1], SCpnt->data_cmnd[2], + SCpnt->data_cmnd[3], SCpnt->data_cmnd[4], SCpnt->data_cmnd[5], + SCpnt->request_bufflen); + if (driver_byte(result) & DRIVER_SENSE) + print_sense("osst", SCpnt); + } + else +#endif + if (!(driver_byte(result) & DRIVER_SENSE) || + ((sense[0] & 0x70) == 0x70 && + scode != NO_SENSE && + scode != RECOVERED_ERROR && +/* scode != UNIT_ATTENTION && */ + scode != BLANK_CHECK && + scode != VOLUME_OVERFLOW && + SCpnt->data_cmnd[0] != MODE_SENSE && + SCpnt->data_cmnd[0] != TEST_UNIT_READY)) { /* Abnormal conditions for tape */ + if (driver_byte(result) & DRIVER_SENSE) { + printk(KERN_WARNING "osst%d: Error with sense data: ", dev); + print_sense("osst", SCpnt); + } + else + printk(KERN_WARNING + "osst%d: Error %x (sugg. bt 0x%x, driver bt 0x%x, host bt 0x%x).\n", + dev, result, suggestion(result), driver_byte(result), + host_byte(result)); + } + + if ((sense[0] & 0x70) == 0x70 && + scode == RECOVERED_ERROR + ) { + os_scsi_tapes[dev]->recover_count++; + os_scsi_tapes[dev]->recover_erreg++; +#if DEBUG + if (debugging) { + if (SCpnt->data_cmnd[0] == READ_6) + stp = "read"; + else if (SCpnt->data_cmnd[0] == WRITE_6) + stp = "write"; + else + stp = "ioctl"; + printk(OSST_DEB_MSG "osst%d: Recovered %s error (%d).\n", dev, stp, + os_scsi_tapes[dev]->recover_count); + } +#endif + if ((sense[2] & 0xe0) == 0) + return 0; + } + return (-EIO); +} + + +/* Wakeup from interrupt */ +static void osst_sleep_done (Scsi_Cmnd * SCpnt) +{ + unsigned int st_nbr; + int remainder; + OS_Scsi_Tape * STp; + + if ((st_nbr = TAPE_NR(SCpnt->request.rq_dev)) < osst_template.nr_dev) { + STp = os_scsi_tapes[st_nbr]; + if ((STp->buffer)->writing && + (SCpnt->sense_buffer[0] & 0x70) == 0x70 && + (SCpnt->sense_buffer[2] & 0x40)) { + /* EOM at write-behind, has all been written? */ + if ((SCpnt->sense_buffer[0] & 0x80) != 0) + remainder = (SCpnt->sense_buffer[3] << 24) | + (SCpnt->sense_buffer[4] << 16) | + (SCpnt->sense_buffer[5] << 8) | SCpnt->sense_buffer[6]; + else + remainder = 0; + if ((SCpnt->sense_buffer[2] & 0x0f) == VOLUME_OVERFLOW || + remainder > 0) + (STp->buffer)->last_result = SCpnt->result; /* Error */ + else + (STp->buffer)->last_result = INT_MAX; /* OK */ + } + else + (STp->buffer)->last_result = SCpnt->result; + SCpnt->request.rq_status = RQ_SCSI_DONE; + (STp->buffer)->last_SCpnt = SCpnt; + +#if DEBUG + STp->write_pending = 0; +#endif + up(SCpnt->request.sem); + } +#if DEBUG + else if (debugging) + printk(KERN_ERR "osst?: Illegal interrupt device %x\n", st_nbr); +#endif +} + + +/* Do the scsi command. Waits until command performed if do_wait is true. + Otherwise osst_write_behind_check() is used to check that the command + has finished. */ +static Scsi_Cmnd * osst_do_scsi(Scsi_Cmnd *SCpnt, OS_Scsi_Tape *STp, + unsigned char *cmd, int bytes, int timeout, int retries, int do_wait) +{ + unsigned long flags; + unsigned char *bp; + + spin_lock_irqsave(&io_request_lock, flags); + if (SCpnt == NULL) + if ((SCpnt = scsi_allocate_device(NULL, STp->device, 1)) == NULL) { + printk(KERN_ERR "osst%d: Can't get SCSI request.\n", TAPE_NR(STp->devt)); + spin_unlock_irqrestore(&io_request_lock, flags); + return NULL; + } + + cmd[1] |= (SCpnt->lun << 5) & 0xe0; + STp->sem = MUTEX_LOCKED; + SCpnt->use_sg = (bytes > (STp->buffer)->sg[0].length) ? + (STp->buffer)->use_sg : 0; + if (SCpnt->use_sg) { + bp = (char *)&((STp->buffer)->sg[0]); + if ((STp->buffer)->sg_segs < SCpnt->use_sg) + SCpnt->use_sg = (STp->buffer)->sg_segs; + } + else + bp = (STp->buffer)->b_data; + SCpnt->cmd_len = 0; + SCpnt->request.sem = &(STp->sem); + SCpnt->request.rq_status = RQ_SCSI_BUSY; + SCpnt->request.rq_dev = STp->devt; + + scsi_do_cmd(SCpnt, (void *)cmd, bp, bytes, + osst_sleep_done, timeout, retries); + spin_unlock_irqrestore(&io_request_lock, flags); + + if (do_wait) { + down(SCpnt->request.sem); + + (STp->buffer)->last_result_fatal = osst_chk_result(SCpnt); + } + + return SCpnt; +} + + +/* Handle the write-behind checking (downs the semaphore) */ +static void osst_write_behind_check(OS_Scsi_Tape *STp) +{ + OSST_buffer * STbuffer; + ST_partstat * STps; + + STbuffer = STp->buffer; + +#if DEBUG + if (STp->write_pending) + STp->nbr_waits++; + else + STp->nbr_finished++; +#endif + + down(&(STp->sem)); + + (STp->buffer)->last_result_fatal = osst_chk_result((STp->buffer)->last_SCpnt); + + if ((STp->buffer)->last_result_fatal) + (STp->buffer)->last_result_fatal = + osst_write_error_recovery(STp, &((STp->buffer)->last_SCpnt), 1); + else + STp->first_frame_position++; + + scsi_release_command((STp->buffer)->last_SCpnt); + + if (STbuffer->writing < STbuffer->buffer_bytes) +#if 0 + memcpy(STbuffer->b_data, + STbuffer->b_data + STbuffer->writing, + STbuffer->buffer_bytes - STbuffer->writing); +#else + printk(KERN_WARNING "osst: write_behind_check: something left in buffer!\n"); +#endif + STbuffer->buffer_bytes -= STbuffer->writing; + STps = &(STp->ps[STp->partition]); + if (STps->drv_block >= 0) { + if (STp->block_size == 0) + STps->drv_block++; + else + STps->drv_block += STbuffer->writing / STp->block_size; + } + STbuffer->writing = 0; + + return; +} + + + +/* Onstream specific Routines */ +/* + * Initialize the OnStream AUX + */ +static void osst_init_aux(OS_Scsi_Tape * STp, int frame_type, int logical_blk_num) +{ + os_aux_t *aux = STp->buffer->aux; + os_partition_t *par = &aux->partition; + os_dat_t *dat = &aux->dat; + + if (STp->raw) return; + + memset(aux, 0, sizeof(*aux)); + aux->format_id = htonl(0); + memcpy(aux->application_sig, "LIN4", 4); + aux->hdwr = htonl(0); + aux->frame_type = frame_type; + + switch (frame_type) { + case OS_FRAME_TYPE_HEADER: + aux->update_frame_cntr = htonl(STp->update_frame_cntr); + par->partition_num = OS_CONFIG_PARTITION; + par->par_desc_ver = OS_PARTITION_VERSION; + par->wrt_pass_cntr = htons(0xffff); + /* 0-4 = reserved, 5-9 = header, 2990-2994 = header, 2995-2999 = reserved */ + par->first_frame_ppos = htonl(0); + par->last_frame_ppos = htonl(0xbb7); + aux->frame_seq_num = htonl(0); + aux->logical_blk_num_high = htonl(0); + aux->logical_blk_num = htonl(0); + aux->next_mark_ppos = htonl(STp->first_mark_ppos); + break; + case OS_FRAME_TYPE_DATA: + case OS_FRAME_TYPE_MARKER: + dat->dat_sz = 8; + dat->reserved1 = 0; + dat->entry_cnt = 1; + dat->reserved3 = 0; + dat->dat_list[0].blk_sz = htonl(frame_type==OS_FRAME_TYPE_DATA?STp->block_size:0); + dat->dat_list[0].blk_cnt = htons(1); + dat->dat_list[0].flags = frame_type==OS_FRAME_TYPE_MARKER?OS_DAT_FLAGS_MARK:OS_DAT_FLAGS_DATA; + dat->dat_list[0].reserved = 0; + case OS_FRAME_TYPE_EOD: + aux->update_frame_cntr = htonl(0); + par->partition_num = OS_DATA_PARTITION; + par->par_desc_ver = OS_PARTITION_VERSION; + par->wrt_pass_cntr = htons(STp->wrt_pass_cntr); + par->first_frame_ppos = htonl(STp->first_data_ppos); + par->last_frame_ppos = htonl(STp->capacity); + aux->frame_seq_num = htonl(logical_blk_num); + aux->logical_blk_num_high = htonl(0); + aux->logical_blk_num = htonl(logical_blk_num); + break; + default: ; /* probably FILL */ + } + aux->filemark_cnt = ntohl(STp->filemark_cnt); /* FIXME -- violates ADR spec */ + aux->phys_fm = ntohl(0xffffffff); + aux->last_mark_ppos = ntohl(STp->last_mark_ppos); +} + +/* + * Verify that we have the correct tape frame + */ +static int osst_verify_frame(OS_Scsi_Tape * STp, int logical_blk_num, int quiet) +{ + os_aux_t * aux = STp->buffer->aux; + os_partition_t * par = &(aux->partition); + ST_partstat * STps = &(STp->ps[STp->partition]); + int i; + int dev = TAPE_NR(STp->devt); + + if (STp->raw) { + if (STp->buffer->last_result_fatal) { + for (i=0; i < STp->buffer->sg_segs; i++) + memset(STp->buffer->sg[i].address, 0, STp->buffer->sg[i].length); + strcpy(STp->buffer->b_data, "READ ERROR ON FRAME"); + } + return 1; + } + if (STp->buffer->last_result_fatal) { + printk(KERN_INFO "osst%d: Skipping frame, read error\n", dev); + return 0; + } + if (ntohl(aux->format_id) != 0) { + printk(KERN_INFO "osst%d: Skipping frame, format_id %u\n", dev, ntohl(aux->format_id)); + return 0; + } + if (memcmp(aux->application_sig, STp->application_sig, 4) != 0 && + (memcmp(aux->application_sig, "LIN3", 4) != 0 || STp->linux_media_version != 4)) { + printk(KERN_INFO "osst%d: Skipping frame, incorrect application signature\n", dev); + return 0; + } + if (par->partition_num != OS_DATA_PARTITION) { + if (!STp->linux_media || STp->linux_media_version != 2) { + printk(KERN_INFO "osst%d: Skipping frame, partition num %d\n", dev, par->partition_num); return 0; + } + } + if (par->par_desc_ver != OS_PARTITION_VERSION) { + printk(KERN_INFO "osst%d: Skipping frame, partition version %d\n", dev, par->par_desc_ver); + return 0; + } + if (ntohs(par->wrt_pass_cntr) != STp->wrt_pass_cntr) { + printk(KERN_INFO "osst%d: Skipping frame, wrt_pass_cntr %d (expected %d)\n", + dev, ntohs(par->wrt_pass_cntr), STp->wrt_pass_cntr); + return 0; + } + if (aux->frame_seq_num != aux->logical_blk_num) { + printk(KERN_INFO "osst%d: Skipping frame, seq != logical\n", dev); + return 0; + } + if (aux->frame_type != OS_FRAME_TYPE_DATA && + aux->frame_type != OS_FRAME_TYPE_EOD && + aux->frame_type != OS_FRAME_TYPE_MARKER) { + if (!quiet) + printk(KERN_INFO "osst%d: Skipping frame, frame type %x\n", dev, aux->frame_type); + return 0; + } + if (aux->frame_type == OS_FRAME_TYPE_EOD && + STp->first_frame_position < STp->eod_frame_ppos) { + printk(KERN_INFO "osst%d: skipping premature EOD frame %d\n", dev, STp->first_frame_position); + return 0; + } + STp->logical_blk_in_buffer = 1; + + if (logical_blk_num != -1 && ntohl(aux->logical_blk_num) != logical_blk_num) { + if (!quiet) + printk(KERN_INFO "osst%d: Skipping frame, logical_blk_num %u (expected %d)\n", + dev, ntohl(aux->logical_blk_num), logical_blk_num); + return 0; + } + if (aux->frame_type == OS_FRAME_TYPE_MARKER) { + STps->eof = ST_FM_HIT; + + i = ntohl(aux->filemark_cnt); + if (STp->header_cache != NULL && i < OS_FM_TAB_MAX && + STp->first_frame_position - 1 != ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[i])) { +#if 1 //DEBUG + printk(OSST_DEB_MSG "osst%i: %s filemark %d at frame %d\n", dev, + STp->header_cache->dat_fm_tab.fm_tab_ent[i] == 0?"Learned":"Corrected", + i, STp->first_frame_position - 1); +#endif + STp->header_cache->dat_fm_tab.fm_tab_ent[i] = htonl(STp->first_frame_position - 1); + } + } + if (aux->frame_type == OS_FRAME_TYPE_EOD) { + STps->eof = ST_EOD_1; + } + if (aux->frame_type == OS_FRAME_TYPE_DATA) { + STps->eof = ST_NOEOF; + } + return 1; +} + +/* + * Wait for the unit to become Ready + */ +static int osst_wait_ready(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, unsigned timeout) +{ + unsigned char cmd[MAX_COMMAND_SIZE]; + Scsi_Cmnd * SCpnt; + long startwait = jiffies; +#if DEBUG + int dbg = debugging; + int dev = TAPE_NR(STp->devt); + + printk(OSST_DEB_MSG "osst%d: reached onstream wait ready\n", dev); +#endif + + memset(cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = TEST_UNIT_READY; + + SCpnt = osst_do_scsi(*aSCpnt, STp, cmd, 0, STp->timeout, MAX_READY_RETRIES, TRUE); + *aSCpnt = SCpnt; + if (!SCpnt) return (-EBUSY); + + while ( SCpnt->sense_buffer[2] == 2 && SCpnt->sense_buffer[12] == 4 && + ( SCpnt->sense_buffer[13] == 1 || SCpnt->sense_buffer[13] == 8 ) && + time_before(jiffies, startwait + timeout*HZ) ) { +#if DEBUG + if (debugging) { + printk(OSST_DEB_MSG "osst%d: Sleeping in onstream wait ready\n", dev); + printk(OSST_DEB_MSG "osst%d: Turning off debugging for a while\n", dev); + debugging = 0; + } +#endif + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ / 10); + + memset(cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = TEST_UNIT_READY; + + SCpnt = osst_do_scsi(SCpnt, STp, cmd, 0, STp->timeout, MAX_READY_RETRIES, TRUE); + } + *aSCpnt = SCpnt; +#if DEBUG + debugging = dbg; +#endif + if ( SCpnt->sense_buffer[2] && + osst_write_error_recovery(STp, aSCpnt, 0) ) { +#if DEBUG + printk(OSST_DEB_MSG "osst%d: Abnormal exit from onstream wait ready\n", dev); +#endif + return (-EIO); + } +#if DEBUG + printk(OSST_DEB_MSG "osst%d: Normal exit from onstream wait ready\n", dev); +#endif + return 0; +} + +static int osst_position_tape_and_confirm(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int frame) +{ + int retval; + + osst_wait_ready(STp, aSCpnt, 15 * 60); /* TODO - can this catch a write error? */ + retval = osst_set_frame_position(STp, aSCpnt, frame, 0); + if (retval) return (retval); + osst_wait_ready(STp, aSCpnt, 15 * 60); + return (osst_get_frame_position(STp, aSCpnt)); +} + +/* + * Wait for write(s) to complete + */ +static int osst_flush_drive_buffer(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt) +{ + unsigned char cmd[MAX_COMMAND_SIZE]; + Scsi_Cmnd * SCpnt; + + int result = 0; +#if DEBUG + int dev = TAPE_NR(STp->devt); + + printk(OSST_DEB_MSG "osst%d: Reached onstream flush drive buffer (write filemark)\n", dev); +#endif + + memset(cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = WRITE_FILEMARKS; + cmd[1] = 1; + + SCpnt = osst_do_scsi(*aSCpnt, STp, cmd, 0, STp->timeout, MAX_WRITE_RETRIES, TRUE); + *aSCpnt = SCpnt; + if (!SCpnt) return (-EBUSY); + + if ((STp->buffer)->last_result_fatal) + result = osst_write_error_recovery(STp, aSCpnt, 0); + + result |= osst_wait_ready(STp, aSCpnt, 5 * 60); + STp->ps[STp->partition].rw = ST_IDLE; + return (result); +} + +#define OSST_POLL_PER_SEC 10 +static int osst_wait_frame(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int curr, int minlast, int to) +{ + long startwait = jiffies; + int dev = TAPE_NR(STp->devt); +#if DEBUG + char notyetprinted = 1; +#endif + if (STp->ps[STp->partition].rw != ST_READING) + printk(KERN_ERR "osst%i: waiting for frame without having initialized read!\n", dev); + + while (time_before (jiffies, startwait + to*HZ)) + { + int result; + result = osst_get_frame_position (STp, aSCpnt); + if (result == -EIO) + result = osst_write_error_recovery(STp, aSCpnt, 0); + if (result < 0) break; + if (STp->first_frame_position == curr && + ((minlast < 0 && + (signed)STp->last_frame_position > (signed)curr + minlast) || + (minlast >= 0 && STp->cur_frames > minlast) + ) && result >= 0) + { +#if DEBUG + if (jiffies - startwait >= 2*HZ/OSST_POLL_PER_SEC) + printk ("osst%i: Succ wait f fr %i (>%i): %i-%i %i (%i): %3li.%li s\n", + dev, curr, curr+minlast, STp->first_frame_position, + STp->last_frame_position, STp->cur_frames, + result, (jiffies-startwait)/HZ, + (((jiffies-startwait)%HZ)*10)/HZ); +#endif + return 0; + } +#if DEBUG + if (jiffies - startwait >= 2*HZ/OSST_POLL_PER_SEC && notyetprinted) + { + printk ("osst%i: Wait for frame %i (>%i): %i-%i %i (%i)\n", + dev, curr, curr+minlast, STp->first_frame_position, + STp->last_frame_position, STp->cur_frames, result); + notyetprinted--; + } +#endif + current->state = TASK_INTERRUPTIBLE; + schedule_timeout (HZ / OSST_POLL_PER_SEC); + } +#if DEBUG + printk ("osst%i: Fail wait f fr %i (>%i): %i-%i %i: %3li.%li s\n", + dev, curr, curr+minlast, STp->first_frame_position, + STp->last_frame_position, STp->cur_frames, + (jiffies-startwait)/HZ, (((jiffies-startwait)%HZ)*10)/HZ); +#endif + return -EBUSY; +} + +/* + * Read the next OnStream tape block at the current location + */ +static int osst_read_block(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int timeout) +{ + unsigned char cmd[MAX_COMMAND_SIZE]; + Scsi_Cmnd * SCpnt; + int retval = 0; +#if DEBUG + os_aux_t * aux = STp->buffer->aux; + int dev = TAPE_NR(STp->devt); +#endif + + /* TODO: Error handling */ + if (STp->poll) + retval = osst_wait_frame (STp, aSCpnt, STp->first_frame_position, 0, timeout); +#if 0// DEBUG + printk ("osst_read: wait for frame returned %i\n", retval); +#endif + + memset(cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = READ_6; + cmd[1] = 1; + cmd[4] = 1; + +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%i: Reading block from OnStream tape\n", dev); +#endif + SCpnt = osst_do_scsi(*aSCpnt, STp, cmd, OS_FRAME_SIZE, STp->timeout, MAX_RETRIES, TRUE); + *aSCpnt = SCpnt; + if (!SCpnt) + return (-EBUSY); + + if ((STp->buffer)->last_result_fatal) { + retval = 1; +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n", + dev, + SCpnt->sense_buffer[0], SCpnt->sense_buffer[1], + SCpnt->sense_buffer[2], SCpnt->sense_buffer[3], + SCpnt->sense_buffer[4], SCpnt->sense_buffer[5], + SCpnt->sense_buffer[6], SCpnt->sense_buffer[7]); +#endif + } + else + STp->first_frame_position++; +#if DEBUG + if (debugging) { + printk(OSST_DEB_MSG "osst%i: AUX: %c%c%c%c UpdFrCt#%d %s FrSeq#%d LogBlk#%d\n", dev, + aux->application_sig[0], aux->application_sig[1], + aux->application_sig[2], aux->application_sig[3], ntohl(aux->update_frame_cntr), + aux->frame_type==1?"EOD":aux->frame_type==2?"MARK": + aux->frame_type==8?"HEADR":aux->frame_type==0x80?"DATA":"FILL", + ntohl(aux->frame_seq_num), ntohl(aux->logical_blk_num) ); + if (aux->frame_type==2) + printk(OSST_DEB_MSG "osst%i: mark_cnt=%d, last_mark=%d, next_mark=%d\n", dev, + ntohl(aux->filemark_cnt), ntohl(aux->last_mark_ppos), ntohl(aux->next_mark_ppos)); + printk(OSST_DEB_MSG "osst%i: Exit read block from OnStream tape with code %d\n", dev, retval); + } +#endif + return (retval); +} + +static int osst_initiate_read(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt) +{ + ST_partstat * STps = &(STp->ps[STp->partition]); + Scsi_Cmnd * SCpnt ; + unsigned char cmd[MAX_COMMAND_SIZE]; + int retval = 0; +#if DEBUG + int dev = TAPE_NR(STp->devt); +#endif + + if (STps->rw != ST_READING) { /* Initialize read operation */ + if (STps->rw == ST_WRITING) { + osst_flush_write_buffer(STp, aSCpnt, 1); + osst_flush_drive_buffer(STp, aSCpnt); + } + STps->rw = ST_READING; + STp->logical_blk_in_buffer = 0; + + /* + * Issue a read 0 command to get the OnStream drive + * read blocks into its buffer. + */ + memset(cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = READ_6; + cmd[1] = 1; + +#if DEBUG + printk(OSST_DEB_MSG "osst%i: Start Read Ahead on OnStream tape\n", dev); +#endif + SCpnt = osst_do_scsi(*aSCpnt, STp, cmd, 0, STp->timeout, MAX_RETRIES, TRUE); + *aSCpnt = SCpnt; + retval = STp->buffer->last_result_fatal; + } + + return retval; +} + +static int osst_get_logical_blk(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int logical_blk_num, int quiet) +{ + ST_partstat * STps = &(STp->ps[STp->partition]); + int dev = TAPE_NR(STp->devt); + int cnt = 0, + x, + position; + + /* + * Search and wait for the next logical tape block + */ + while (1) { + if (cnt++ > 200) { + printk(KERN_WARNING "osst%d: Couldn't find logical block %d, aborting\n", + dev, logical_blk_num); + return (-EIO); + } +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: Looking for block %d, attempt %d\n", + dev, logical_blk_num, cnt); +#endif + if ( osst_initiate_read(STp, aSCpnt) + || ( (!STp->logical_blk_in_buffer) && osst_read_block(STp, aSCpnt, 30) ) ) { + position = osst_get_frame_position(STp, aSCpnt); + if (position >= 0xbae && position < 0xbb8) + position = 0xbb8; + else { + position += 39; + cnt += 30; + } +#if DEBUG + printk(OSST_DEB_MSG "osst%d: Blank block detected, positioning tape to block %d\n", + dev, position); +#endif + osst_set_frame_position(STp, aSCpnt, position, 0); + continue; + } + if (osst_verify_frame(STp, logical_blk_num, quiet)) + break; + if (osst_verify_frame(STp, -1, quiet)) { + x = ntohl(STp->buffer->aux->logical_blk_num); + if (STp->fast_open) { +#if 1 //DEBUG + printk(OSST_DEB_MSG "osst%d: Found logical block %d instead of %d after fast open\n", + dev, x, logical_blk_num); +#endif + STp->header_ok = 0; + return (-EIO); + } + if (x > logical_blk_num) { +#if DEBUG + printk(OSST_DEB_MSG + "osst%d: Found logical block %d while looking for %d: back up %d\n", + dev, x, logical_blk_num, x - logical_blk_num + 1); +#endif + position = osst_get_frame_position(STp, aSCpnt); + osst_set_frame_position(STp, aSCpnt, position + logical_blk_num - x - 1, 0); + cnt += 10; + } + } + if (osst_get_frame_position(STp, aSCpnt) == 0xbaf) { +#if DEBUG + printk(OSST_DEB_MSG "osst%d: Skipping config partition\n", dev); +#endif + osst_set_frame_position(STp, aSCpnt, 0xbb8, 0); + cnt--; + } + STp->logical_blk_in_buffer = 0; + } + if (cnt > 1) { + STp->recover_count++; + STp->recover_erreg++; + } + STp->logical_blk_num = ntohl(STp->buffer->aux->logical_blk_num); + +#if DEBUG + if (debugging || STps->eof) + printk(OSST_DEB_MSG "osst%i: Exit get logical block (%d=>%d) from OnStream tape with code %d\n", dev, logical_blk_num, STp->logical_blk_num, STps->eof); +#endif + STp->fast_open = FALSE; + return (STps->eof); +} + +static int osst_seek_logical_blk(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int logical_blk_num) +{ + int estimate; + int retries = 0; + int dev = TAPE_NR(STp->devt); + + if (logical_blk_num < 0) logical_blk_num = 0; + /* FIXME -- this may not be valid for foreign formats */ + if (logical_blk_num < 2980) estimate = logical_blk_num + 10; + else estimate = logical_blk_num + 20; + +#if DEBUG + printk(OSST_DEB_MSG "osst%d: Seeking logical block %d (now at %d)\n", + dev, logical_blk_num, STp->logical_blk_num); +#endif + while (++retries < 10) { + osst_set_frame_position(STp, aSCpnt, estimate, 0); + if (osst_get_logical_blk(STp, aSCpnt, logical_blk_num, 1) >= 0) + return 0; + if (osst_get_logical_blk(STp, aSCpnt, -1, 1) < 0) + goto error; + if (STp->logical_blk_num != logical_blk_num) + estimate += logical_blk_num - STp->logical_blk_num; + else + break; + } +error: + printk(KERN_WARNING "osst%d: Couldn't seek to logical block %d (at %d), %d retries\n", + dev, logical_blk_num, STp->logical_blk_num, retries); + return (-EIO); +} + +static int osst_seek_frame(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int frame) +{ + ST_partstat * STps = &(STp->ps[STp->partition]); + int r; + + if (frame < 0 || frame >= STp->capacity) return (-ENXIO); + + if (frame <= STp->first_data_ppos) { + STp->logical_blk_num = STps->drv_file = STps->drv_block = 0; + return (osst_set_frame_position(STp, aSCpnt, frame, 0)); + } + r = osst_set_frame_position(STp, aSCpnt, frame-1, 0); + if (r < 0) return r; + + r = osst_get_logical_blk(STp, aSCpnt, -1, 1); + if (r < 0) return r; + + if (osst_get_frame_position(STp, aSCpnt) != frame) return (-EIO); + + STp->logical_blk_num++; + STp->logical_blk_in_buffer = 0; + STps->drv_file = htonl(STp->buffer->aux->filemark_cnt); + STps->drv_block = -1; + STps->eof = ST_NOEOF; + return 0; +} + +/* + * Read back the drive's internal buffer contents, as a part + * of the write error recovery mechanism for old OnStream + * firmware revisions. + */ +static int osst_read_back_buffer_and_rewrite(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, + unsigned int block, unsigned int skip, int pending) +{ + Scsi_Cmnd * SCpnt = * aSCpnt; + unsigned char * buffer, * p; + unsigned char cmd[MAX_COMMAND_SIZE]; + int frames, flag, new_block, i, logical_blk_num; + int dev = TAPE_NR(STp->devt); + long startwait = jiffies; +#if DEBUG + int dbg = debugging; +#endif + + frames = STp->cur_frames; + if ((buffer = (unsigned char *)vmalloc((frames + pending) * OS_DATA_SIZE)) == NULL) + return (-EIO); + + logical_blk_num = STp->logical_blk_num - frames - pending; + printk(KERN_INFO "osst%d: Reading back %d frames from drive buffer%s\n", + dev, frames, pending?" and one that was pending":""); + + if (pending) { + osst_copy_from_buffer(STp->buffer, (p = &buffer[frames * OS_DATA_SIZE])); +// memcpy((p = &buffer[frames * OS_DATA_SIZE]), STp->buffer->b_data, OS_DATA_SIZE); +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: Pending logical block %d, data %x %x %x %x\n", + dev, logical_blk_num + frames, p[0], p[1], p[2], p[3]); +#endif + } + for (i = 0, p = buffer; i < frames; i++, p += OS_DATA_SIZE) { + + memset(cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = 0x3C; /* Buffer Read */ + cmd[1] = 6; /* Retrieve Faulty Block */ + cmd[7] = 32768 >> 8; + cmd[8] = 32768 & 0xff; + + SCpnt = osst_do_scsi(SCpnt, STp, cmd, OS_FRAME_SIZE, STp->timeout, MAX_RETRIES, TRUE); + + if ((STp->buffer)->last_result_fatal) { + printk(KERN_ERR "osst%d: Failed to read block back from OnStream buffer\n", dev); + vfree((void *)buffer); + *aSCpnt = SCpnt; + return (-EIO); + } + osst_copy_from_buffer(STp->buffer, p); +// memcpy(p, STp->buffer->b_data, OS_DATA_SIZE); +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: Read back logical block %d, data %x %x %x %x\n", + dev, logical_blk_num + i, p[0], p[1], p[2], p[3]); +#endif + } + *aSCpnt = SCpnt; + osst_get_frame_position(STp, aSCpnt); + +#if DEBUG + printk(OSST_DEB_MSG "osst%d: Frames left in buffer: %d\n", dev, STp->cur_frames); +#endif + /* Write synchronously so we can be sure we're OK again and don't have to recover recursively */ + /* In the header we don't actually re-write the blocks that fail, just the ones after them */ + + for (flag=1, new_block=block, p=buffer, i=0; i < frames + pending; ) { + + if (flag) { + if (STp->write_type == OS_WRITE_HEADER) { + i += skip; + p += skip * OS_DATA_SIZE; + } + else if (new_block < 2990 && new_block+skip+frames+pending >= 2990) + new_block = 3000-i; + else + new_block += skip; +#if DEBUG + printk(OSST_DEB_MSG "osst%d: Position to frame %d, write lblk %d\n", + dev, new_block+i, logical_blk_num+i); /* FIXME var blk sz */ +#endif + osst_set_frame_position(STp, aSCpnt, new_block + i, 0); + osst_wait_ready(STp, aSCpnt, 60); + osst_get_frame_position(STp, aSCpnt); + SCpnt = * aSCpnt; + + if (new_block > block + 1000) { + printk(KERN_ERR "osst%d: Failed to find valid tape media\n", dev); + vfree((void *)buffer); + return (-EIO); + } + flag = 0; + if ( i >= frames + pending ) break; + } + osst_copy_to_buffer(STp->buffer, p); +// memcpy(STp->buffer->b_data, p, OS_DATA_SIZE); + /* + * IMPORTANT: for error recovery to work, _never_ queue frames with mixed frame type! + */ + osst_init_aux(STp, STp->buffer->aux->frame_type, logical_blk_num+i ); + memset(cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = WRITE_6; + cmd[1] = 1; + cmd[4] = 1; + +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: About to attempt to write to frame %d\n", dev, new_block+i); +#endif + SCpnt = osst_do_scsi(SCpnt, STp, cmd, OS_FRAME_SIZE, STp->timeout, MAX_WRITE_RETRIES, TRUE); + + if (STp->buffer->last_result_fatal) + flag = 1; + else { + p += OS_DATA_SIZE; i++; + + /* if we just sent the last frame, wait till all successfully written */ + if ( i == frames + pending ) { +#if DEBUG + printk(OSST_DEB_MSG "osst%d: Check re-write successful\n", dev); +#endif + memset(cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = WRITE_FILEMARKS; + cmd[1] = 1; + SCpnt = osst_do_scsi(SCpnt, STp, cmd, 0, STp->timeout, MAX_WRITE_RETRIES, TRUE); + +#if DEBUG + if (debugging) { + printk(OSST_DEB_MSG "osst%d: Sleeping in re-write wait ready\n", dev); + printk(OSST_DEB_MSG "osst%d: Turning off debugging for a while\n", dev); + debugging = 0; + } +#endif + flag = STp->buffer->last_result_fatal; + while ( !flag && time_before(jiffies, startwait + 60*HZ) ) { + + memset(cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = TEST_UNIT_READY; + + SCpnt = osst_do_scsi(SCpnt, STp, cmd, 0, STp->timeout, + MAX_READY_RETRIES, TRUE); + + if (SCpnt->sense_buffer[2] == 2 && SCpnt->sense_buffer[12] == 4 && + (SCpnt->sense_buffer[13] == 1 || SCpnt->sense_buffer[13] == 8)) { + /* in the process of becoming ready */ + schedule_timeout(HZ / 10); + continue; + } + if (STp->buffer->last_result_fatal) + flag = 1; + break; + } +#if DEBUG + debugging = dbg; + printk(OSST_DEB_MSG "osst%d: Wait re-write finished\n", dev); +#endif + } + } + if (flag) { + if ((SCpnt->sense_buffer[ 2] & 0x0f) == 13 && + SCpnt->sense_buffer[12] == 0 && + SCpnt->sense_buffer[13] == 2) { + printk(KERN_ERR "osst%d: Volume overflow in write error recovery\n", dev); + vfree((void *)buffer); + return (-EIO); /* hit end of tape = fail */ + } + i = ((SCpnt->sense_buffer[3] << 24) | + (SCpnt->sense_buffer[4] << 16) | + (SCpnt->sense_buffer[5] << 8) | + SCpnt->sense_buffer[6] ) - new_block; + p = &buffer[i * OS_DATA_SIZE]; +#if 1 //DEBUG + printk(OSST_DEB_MSG "osst%d: Additional write error at %d\n", dev, new_block+i); +#endif + osst_get_frame_position(STp, aSCpnt); +#if DEBUG + printk(OSST_DEB_MSG "osst%d: reported frame positions: host = %d, tape = %d\n", + dev, STp->first_frame_position, STp->last_frame_position); +#endif + } + *aSCpnt = SCpnt; + } + vfree((void *)buffer); + return 0; +} + +static int osst_reposition_and_retry(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, + unsigned int block, unsigned int skip, int pending) +{ + unsigned char cmd[MAX_COMMAND_SIZE]; + Scsi_Cmnd * SCpnt = * aSCpnt; + int dev = TAPE_NR(STp->devt); + int attempts = 1000 / skip; + int flag = 1; + long startwait = jiffies; +#if DEBUG + int dbg = debugging; +#endif + + while (attempts && time_before(jiffies, startwait + 60*HZ)) { + if (flag) { +#if DEBUG + debugging = dbg; +#endif + if (block < 2990 && block+skip+STp->cur_frames+pending >= 2990) + block = 3000-skip; +#if 1 //DEBUG + printk(OSST_DEB_MSG "osst%d: Position to frame %d, re-write from lblk %d\n", + dev, block+skip, STp->logical_blk_num-STp->cur_frames-pending); +#endif + osst_set_frame_position(STp, aSCpnt, block + skip, 1); + flag = 0; + attempts--; + } + if (osst_get_frame_position(STp, aSCpnt) < 0) { /* additional write error */ +#if 1 //DEBUG + printk(OSST_DEB_MSG "osst%d: Addl error, host %d, tape %d, buffer %d\n", + dev, STp->first_frame_position, + STp->last_frame_position, STp->cur_frames); +#endif + block = STp->last_frame_position; + flag = 1; + continue; + } + if (pending && STp->cur_frames < 50) { + + memset(cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = WRITE_6; + cmd[1] = 1; + cmd[4] = 1; +#if 1 //DEBUG + printk(OSST_DEB_MSG "osst%d: About to write pending lblk %d at frame %d\n", + dev, STp->logical_blk_num-1, STp->first_frame_position); +#endif + SCpnt = osst_do_scsi(SCpnt, STp, cmd, OS_FRAME_SIZE, STp->timeout, + MAX_WRITE_RETRIES, TRUE); + *aSCpnt = SCpnt; + + if (STp->buffer->last_result_fatal) { /* additional write error */ + if ((SCpnt->sense_buffer[ 2] & 0x0f) == 13 && + SCpnt->sense_buffer[12] == 0 && + SCpnt->sense_buffer[13] == 2) { + printk(OSST_DEB_MSG + "osst%d: Volume overflow in write error recovery\n", + dev); + break; /* hit end of tape = fail */ + } + flag = 1; + } + else + pending = 0; + + continue; + } + if (STp->cur_frames == 0) { +#if DEBUG + debugging = dbg; + printk(OSST_DEB_MSG "osst%d: Wait re-write finished\n", dev); +#endif + return 0; + } +#if DEBUG + if (debugging) { + printk(OSST_DEB_MSG "osst%d: Sleeping in re-write wait ready\n", dev); + printk(OSST_DEB_MSG "osst%d: Turning off debugging for a while\n", dev); + debugging = 0; + } +#endif + schedule_timeout(HZ / 10); + } + printk(KERN_ERR "osst%d: Failed to find valid tape media\n", dev); +#if DEBUG + debugging = dbg; +#endif + return (-EIO); +} + +/* + * Error recovery algorithm for the OnStream tape. + */ + +static int osst_write_error_recovery(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int pending) +{ + Scsi_Cmnd * SCpnt = * aSCpnt; + ST_partstat * STps = & STp->ps[STp->partition]; + int dev = TAPE_NR(STp->devt); + int retval = 0; + int rw_state; + unsigned int block, skip; + + rw_state = STps->rw; + + if ((SCpnt->sense_buffer[ 2] & 0x0f) != 3 + || SCpnt->sense_buffer[12] != 12 + || SCpnt->sense_buffer[13] != 0) { +#if 1 //DEBUG + printk(OSST_DEB_MSG "osst%d: Write error recovery cannot handle %02x:%02x:%02x\n", + dev, SCpnt->sense_buffer[ 2], SCpnt->sense_buffer[12], SCpnt->sense_buffer[13]); +#endif + return (-EIO); + } + block = (SCpnt->sense_buffer[3] << 24) | + (SCpnt->sense_buffer[4] << 16) | + (SCpnt->sense_buffer[5] << 8) | + SCpnt->sense_buffer[6]; + skip = SCpnt->sense_buffer[9]; + +#if 1 //DEBUG + printk(OSST_DEB_MSG "osst%d: Detected physical bad block at %u, advised to skip %d\n", dev, block, skip); +#endif + osst_get_frame_position(STp, aSCpnt); +#if 1 //DEBUG + printk(OSST_DEB_MSG "osst%d: reported frame positions: host = %d, tape = %d\n", + dev, STp->first_frame_position, STp->last_frame_position); +#endif + switch (STp->write_type) { + case OS_WRITE_DATA: + case OS_WRITE_EOD: + case OS_WRITE_NEW_MARK: + printk(KERN_WARNING "osst%d: Relocating %d buffered logical blocks to physical block %u\n", + dev, STp->cur_frames, block + skip); + if (STp->os_fw_rev >= 10600) + retval = osst_reposition_and_retry(STp, aSCpnt, block, skip, pending); + else + retval = osst_read_back_buffer_and_rewrite(STp, aSCpnt, block, skip, pending); + break; + case OS_WRITE_LAST_MARK: + printk(KERN_ERR "osst%d: Bad block in update last marker, fatal\n", dev); + osst_set_frame_position(STp, aSCpnt, block + STp->cur_frames + pending, 0); + retval = -EIO; + break; + case OS_WRITE_HEADER: + printk(KERN_WARNING "osst%d: Bad block in header partition, skipped\n", dev); + retval = osst_read_back_buffer_and_rewrite(STp, aSCpnt, block, 1, pending); + break; + default: + printk(KERN_WARNING "osst%d: Bad block in filler, ignored\n", dev); + osst_set_frame_position(STp, aSCpnt, block + STp->cur_frames + pending, 0); + } + osst_get_frame_position(STp, aSCpnt); +#if 1 //DEBUG + printk(KERN_ERR "osst%d: Positioning complete, cur_frames %d, pos %d, tape pos %d\n", + dev, STp->cur_frames, STp->first_frame_position, STp->last_frame_position); + printk(OSST_DEB_MSG "osst%d: next logical block to write: %d\n", dev, STp->logical_blk_num); +#endif + if (retval == 0) { + STp->recover_count++; + STp->recover_erreg++; + } + STps->rw = rw_state; + return retval; +} + +static int osst_space_over_filemarks_backward(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int mt_op, int mt_count) +{ + int dev = TAPE_NR(STp->devt); + int cnt; + int last_mark_ppos = -1; + +#if DEBUG + printk(OSST_DEB_MSG "osst%d: Reached space_over_filemarks_backwards %d %d\n", dev, mt_op, mt_count); +#endif + if (osst_get_logical_blk(STp, aSCpnt, -1, 0) < 0) { + printk(KERN_INFO "osst%i: Couldn't get logical blk num in space_filemarks_bwd\n", dev); + return -EIO; + } + if (STp->linux_media_version >= 4) { + /* + * direct lookup in header filemark list + */ + cnt = ntohl(STp->buffer->aux->filemark_cnt); + if (STp->header_ok && + STp->header_cache != NULL && + (cnt - mt_count) >= 0 && + (cnt - mt_count) < OS_FM_TAB_MAX && + (cnt - mt_count) < STp->filemark_cnt && + STp->header_cache->dat_fm_tab.fm_tab_ent[cnt-1] == STp->buffer->aux->last_mark_ppos) + + last_mark_ppos = ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[cnt - mt_count]); +#if 1 //DEBUG + if (STp->header_cache == NULL || (cnt - mt_count) < 0 || (cnt - mt_count) >= OS_FM_TAB_MAX) + printk(OSST_DEB_MSG "osst%i: Filemark lookup fail due to %s\n", dev, + STp->header_cache == NULL?"lack of header cache":"count out of range"); + else + printk(OSST_DEB_MSG "osst%i: Filemark lookup: prev mark %d (%s), skip %d to %d\n", dev, cnt, + ((cnt == -1 && ntohl(STp->buffer->aux->last_mark_ppos) == -1) || + (STp->header_cache->dat_fm_tab.fm_tab_ent[cnt-1] == + STp->buffer->aux->last_mark_ppos))?"match":"error", + mt_count, last_mark_ppos); +#endif + if (last_mark_ppos > 10 && last_mark_ppos < STp->eod_frame_ppos) { + osst_set_frame_position(STp, aSCpnt, last_mark_ppos, 0); + if (osst_get_logical_blk(STp, aSCpnt, -1, 0) < 0) { + printk(KERN_INFO "osst%i: Couldn't get logical blk num in space_filemarks\n", dev); + return (-EIO); + } + if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) { + printk(KERN_INFO "osst%i: Expected to find marker at block %d, not found\n", + dev, last_mark_ppos); + return (-EIO); + } + if (mt_op == MTBSFM) { + STp->logical_blk_num++; + STp->logical_blk_in_buffer = 0; + } + return 0; + } + printk(KERN_INFO "osst%i: Reverting to scan filemark backwards\n", dev); + } + cnt = 0; + while (cnt != mt_count) { + last_mark_ppos = ntohl(STp->buffer->aux->last_mark_ppos); + if (last_mark_ppos == -1) + return (-EIO); +#if DEBUG + printk(OSST_DEB_MSG "osst%i: Positioning to last mark at %d\n", dev, last_mark_ppos); +#endif + osst_set_frame_position(STp, aSCpnt, last_mark_ppos, 0); + cnt++; + if (osst_get_logical_blk(STp, aSCpnt, -1, 0) < 0) { + printk(KERN_INFO "osst%i: Couldn't get logical blk num in space_filemarks\n", dev); + return (-EIO); + } + if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) { + printk(KERN_INFO "osst%i: expected to find marker at block %d, not found\n", dev, last_mark_ppos); + return (-EIO); + } + } + if (mt_op == MTBSFM) { + STp->logical_blk_num++; + STp->logical_blk_in_buffer = 0; + } + return 0; +} + +/* + * ADRL 1.1 compatible "slow" space filemarks fwd version + * + * Just scans for the filemark sequentially. + */ +static int osst_space_over_filemarks_forward_slow(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int mt_op, int mt_count) +{ + int dev = TAPE_NR(STp->devt); + int cnt = 0; + +#if DEBUG + printk(OSST_DEB_MSG "osst%d: Reached space_over_filemarks_forward_slow %d %d\n", dev, mt_op, mt_count); +#endif + if (osst_get_logical_blk(STp, aSCpnt, -1, 0) < 0) { + printk(KERN_INFO "osst%i: Couldn't get logical blk num in space_filemarks_fwd\n", dev); + return (-EIO); + } + while (1) { + if (osst_get_logical_blk(STp, aSCpnt, -1, 0) < 0) { + printk(KERN_INFO "osst%i: Couldn't get logical blk num in space_filemarks\n", dev); + return (-EIO); + } + if (STp->buffer->aux->frame_type == OS_FRAME_TYPE_MARKER) + cnt++; + if (STp->buffer->aux->frame_type == OS_FRAME_TYPE_EOD) { +#if DEBUG + printk(OSST_DEB_MSG "osst%i: space_fwd: EOD reached\n", dev); +#endif + return (-EIO); + } + if (cnt == mt_count) + break; + STp->logical_blk_in_buffer = 0; + } + if (mt_op == MTFSF) { + STp->logical_blk_num++; + STp->logical_blk_in_buffer = 0; + } + return 0; +} + +/* + * Fast linux specific version of OnStream FSF + */ +static int osst_space_over_filemarks_forward_fast(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int mt_op, int mt_count) +{ + int dev = TAPE_NR(STp->devt); + int cnt = 0, + next_mark_ppos = -1; + +#if DEBUG + printk(OSST_DEB_MSG "osst%d: Reached space_over_filemarks_forward_fast %d %d\n", dev, mt_op, mt_count); +#endif + if (osst_get_logical_blk(STp, aSCpnt, -1, 0) < 0) { + printk(KERN_INFO "osst%i: Couldn't get logical blk num in space_filemarks_fwd\n", dev); + return (-EIO); + } + + if (STp->linux_media_version >= 4) { + /* + * direct lookup in header filemark list + */ + cnt = ntohl(STp->buffer->aux->filemark_cnt) - 1; + if (STp->header_ok && + STp->header_cache != NULL && + (cnt + mt_count) < OS_FM_TAB_MAX && + (cnt + mt_count) < STp->filemark_cnt && + ((cnt == -1 && ntohl(STp->buffer->aux->last_mark_ppos) == -1) || + (STp->header_cache->dat_fm_tab.fm_tab_ent[cnt] == STp->buffer->aux->last_mark_ppos))) + + next_mark_ppos = ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[cnt + mt_count]); +#if 1 //DEBUG + if (STp->header_cache == NULL || (cnt + mt_count) >= OS_FM_TAB_MAX) + printk(OSST_DEB_MSG "osst%i: Filemark lookup fail due to %s\n", dev, + STp->header_cache == NULL?"lack of header cache":"count out of range"); + else + printk(OSST_DEB_MSG "osst%i: Filemark lookup: prev mark %d (%s), skip %d to %d\n", dev, cnt, + ((cnt == -1 && ntohl(STp->buffer->aux->last_mark_ppos) == -1) || + (STp->header_cache->dat_fm_tab.fm_tab_ent[cnt] == + STp->buffer->aux->last_mark_ppos))?"match":"error", + mt_count, next_mark_ppos); +#endif + if (next_mark_ppos <= 10 || next_mark_ppos > STp->eod_frame_ppos) { + printk(KERN_INFO "osst%i: Reverting to slow filemark space\n", dev); + return osst_space_over_filemarks_forward_slow(STp, aSCpnt, mt_op, mt_count); + } else { + osst_set_frame_position(STp, aSCpnt, next_mark_ppos, 0); + if (osst_get_logical_blk(STp, aSCpnt, -1, 0) < 0) { + printk(KERN_INFO "osst%i: Couldn't get logical blk num in space_filemarks\n", dev); + return (-EIO); + } + if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) { + printk(KERN_INFO "osst%i: Expected to find marker at block %d, not found\n", + dev, next_mark_ppos); + return (-EIO); + } + } + } else { + /* + * Find nearest (usually previous) marker, then jump from marker to marker + */ + while (1) { + if (STp->buffer->aux->frame_type == OS_FRAME_TYPE_MARKER) + break; + if (STp->buffer->aux->frame_type == OS_FRAME_TYPE_EOD) { +#if DEBUG + printk(OSST_DEB_MSG "osst%i: space_fwd: EOD reached\n", dev); +#endif + return (-EIO); + } + if (ntohl(STp->buffer->aux->filemark_cnt) == 0) { + if (STp->first_mark_ppos == -1) { + printk(KERN_INFO "osst%i: Reverting to slow filemark space\n", dev); + return osst_space_over_filemarks_forward_slow(STp, aSCpnt, mt_op, mt_count); + } + osst_set_frame_position(STp, aSCpnt, STp->first_mark_ppos, 0); + if (osst_get_logical_blk(STp, aSCpnt, -1, 0) < 0) { + printk(KERN_INFO + "osst%i: Couldn't get logical blk num in space_filemarks_fwd_fast\n", + dev); + return (-EIO); + } + if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) { + printk(KERN_INFO "osst%i: Expected to find filemark at %d\n", + dev, STp->first_mark_ppos); + return (-EIO); + } + } else { + if (osst_space_over_filemarks_backward(STp, aSCpnt, MTBSF, 1) < 0) + return (-EIO); + mt_count++; + } + } + cnt++; + while (cnt != mt_count) { + next_mark_ppos = ntohl(STp->buffer->aux->next_mark_ppos); + if (!next_mark_ppos || next_mark_ppos > STp->eod_frame_ppos) { + printk(KERN_INFO "osst%i: Reverting to slow filemark space\n", dev); + return osst_space_over_filemarks_forward_slow(STp, aSCpnt, mt_op, mt_count - cnt); + } +#if DEBUG + else printk(OSST_DEB_MSG "osst%i: Positioning to next mark at %d\n", dev, next_mark_ppos); +#endif + osst_set_frame_position(STp, aSCpnt, next_mark_ppos, 0); + cnt++; + if (osst_get_logical_blk(STp, aSCpnt, -1, 0) < 0) { + printk(KERN_INFO "osst%i: Couldn't get logical blk num in space_filemarks\n", dev); + return (-EIO); + } + if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) { + printk(KERN_INFO "osst%i: Expected to find marker at block %d, not found\n", + dev, next_mark_ppos); + return (-EIO); + } + } + } + if (mt_op == MTFSF) + STp->logical_blk_num++; + STp->logical_blk_in_buffer = 0; + return 0; +} + +/* + * In debug mode, we want to see as many errors as possible + * to test the error recovery mechanism. + */ +#if DEBUG +static void osst_set_retries(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int retries) +{ + unsigned char cmd[MAX_COMMAND_SIZE]; + Scsi_Cmnd * SCpnt = * aSCpnt; + int dev = TAPE_NR(STp->devt); + + memset(cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = MODE_SELECT; + cmd[1] = 0x10; + cmd[4] = NUMBER_RETRIES_PAGE_LENGTH + MODE_HEADER_LENGTH; + + (STp->buffer)->b_data[0] = cmd[4] - 1; + (STp->buffer)->b_data[1] = 0; /* Medium Type - ignoring */ + (STp->buffer)->b_data[2] = 0; /* Reserved */ + (STp->buffer)->b_data[3] = 0; /* Block Descriptor Length */ + (STp->buffer)->b_data[MODE_HEADER_LENGTH + 0] = NUMBER_RETRIES_PAGE | (1 << 7); + (STp->buffer)->b_data[MODE_HEADER_LENGTH + 1] = 2; + (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] = 4; + (STp->buffer)->b_data[MODE_HEADER_LENGTH + 3] = retries; + + if (debugging) + printk(OSST_DEB_MSG "osst%i: Setting number of retries on OnStream tape to %d\n", dev, retries); + + SCpnt = osst_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE); + *aSCpnt = SCpnt; + + if ((STp->buffer)->last_result_fatal) + printk (KERN_ERR "osst%d: Couldn't set retries to %d\n", dev, retries); +} +#endif + +#if 0 +static void osst_update_markers(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int last_mark_ppos, int this_mark_ppos) +{ + int dev = TAPE_NR(STp->devt); + int frame, + reslt; + + if (STp->raw) return; + + STp->last_mark_ppos = this_mark_ppos; + if (STp->header_cache != NULL && STp->filemark_cnt < OS_FM_TAB_MAX) + STp->header_cache->dat_fm_tab.fm_tab_ent[STp->filemark_cnt] = htonl(this_mark_ppos); + if (STp->filemark_cnt++ == 0) + STp->first_mark_ppos = this_mark_ppos; + + if (STp->linux_media_version >= 4) return; + if (last_mark_ppos == -1) return; + + STp->write_type = OS_WRITE_LAST_MARK; + frame = osst_get_frame_position(STp, aSCpnt); +#if DEBUG + printk(OSST_DEB_MSG "osst%i: Update last_marker at frame %d\n", dev, last_mark_ppos); + printk(OSST_DEB_MSG "osst%i: current position %d, lblk %d, tape blk %d\n", + dev, frame, STp->logical_blk_num, STp->last_frame_position); +#endif + osst_set_frame_position(STp, aSCpnt, last_mark_ppos, 0); + osst_initiate_read (STp, aSCpnt); + reslt = osst_read_block(STp, aSCpnt, 180); + + if (reslt) { + printk(KERN_WARNING "osst%i: couldn't read last marker\n", dev); + osst_set_frame_position(STp, aSCpnt, frame, 0); + return; + } + if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) { + printk(KERN_WARNING "osst%i: expected marker at addr %d\n", dev, last_mark_ppos); + osst_set_frame_position(STp, aSCpnt, frame, 0); + return; + } +#if DEBUG + printk(OSST_DEB_MSG "osst%i: writing back marker\n", dev); +#endif + STp->buffer->aux->next_mark_ppos = htonl(this_mark_ppos); + osst_set_frame_position(STp, aSCpnt, last_mark_ppos, 0); + STp->dirty = 1; + if (osst_flush_write_buffer(STp, aSCpnt, 0) || + osst_flush_drive_buffer(STp, aSCpnt) ) { + printk(KERN_WARNING "osst%i: couldn't write marker back at addr %d\n", dev, last_mark_ppos); + } + osst_set_frame_position(STp, aSCpnt, frame, 0); + + return; /* FIXME -- errors should go back to user space */ +} +#endif + +static int osst_write_filemark(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt) +{ + int result; + int this_mark_ppos; +#if DEBUG + int dev = TAPE_NR(STp->devt); +#endif + + if (STp->raw) return 0; + + STp->write_type = OS_WRITE_NEW_MARK; + this_mark_ppos = osst_get_frame_position(STp, aSCpnt); +#if DEBUG + printk(OSST_DEB_MSG "osst%i: Writing Filemark %i at frame %d (lblk %d)\n", + dev, STp->filemark_cnt, this_mark_ppos, STp->logical_blk_num); +#endif + osst_init_aux(STp, OS_FRAME_TYPE_MARKER, STp->logical_blk_num++); + STp->dirty = 1; + result = osst_flush_write_buffer(STp, aSCpnt, 0); + result |= osst_flush_drive_buffer(STp, aSCpnt); + STp->last_mark_ppos = this_mark_ppos; + if (STp->header_cache != NULL && STp->filemark_cnt < OS_FM_TAB_MAX) + STp->header_cache->dat_fm_tab.fm_tab_ent[STp->filemark_cnt] = htonl(this_mark_ppos); + if (STp->filemark_cnt++ == 0) + STp->first_mark_ppos = this_mark_ppos; +// osst_update_markers(STp, aSCpnt, STp->last_mark_ppos, this_mark_ppos); + return result; +} + +static int osst_write_eod(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt) +{ + int result; +#if DEBUG + int dev = TAPE_NR(STp->devt); +#endif + + if (STp->raw) return 0; + + STp->write_type = OS_WRITE_EOD; + STp->eod_frame_ppos = osst_get_frame_position(STp, aSCpnt); +#if DEBUG + printk(OSST_DEB_MSG "osst%i: Writing EOD at %d=>%d\n", dev, STp->logical_blk_num, STp->eod_frame_ppos); +#endif + osst_init_aux(STp, OS_FRAME_TYPE_EOD, STp->logical_blk_num++); + STp->dirty = 1; + + result = osst_flush_write_buffer(STp, aSCpnt, 0); + result |= osst_flush_drive_buffer(STp, aSCpnt); + STp->eod_frame_lfa = --(STp->logical_blk_num); + return result; +} + +static int osst_write_filler(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int block, int count) +{ + int dev = TAPE_NR(STp->devt); + +#if DEBUG + printk(OSST_DEB_MSG "osst%i: Reached onstream write filler group %d\n", dev, block); +#endif + osst_wait_ready(STp, aSCpnt, 60 * 5); + osst_set_frame_position(STp, aSCpnt, block, 0); + STp->write_type = OS_WRITE_FILLER; + osst_init_aux(STp, OS_FRAME_TYPE_FILL, 0); + while (count--) { + memcpy(STp->buffer->b_data, "Filler", 6); + STp->buffer->buffer_bytes = 6; + STp->dirty = 1; + if (osst_flush_write_buffer(STp, aSCpnt, 0)) { + printk(KERN_INFO "osst%i: Couldn't write filler frame\n", dev); + return (-EIO); + } + } +#if DEBUG + printk(OSST_DEB_MSG "osst%i: Exiting onstream write filler group\n", dev); +#endif + return osst_flush_drive_buffer(STp, aSCpnt); +} + +static int __osst_write_header(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int block, int count) +{ + int dev = TAPE_NR(STp->devt); + int result; + +#if DEBUG + printk(OSST_DEB_MSG "osst%i: Reached onstream write header group %d\n", dev, block); +#endif + osst_wait_ready(STp, aSCpnt, 60 * 5); + osst_set_frame_position(STp, aSCpnt, block, 0); + STp->write_type = OS_WRITE_HEADER; + osst_init_aux(STp, OS_FRAME_TYPE_HEADER, STp->logical_blk_num); + while (count--) { + osst_copy_to_buffer(STp->buffer, (unsigned char *)STp->header_cache); +// memcpy(STp->buffer->b_data, STp->header_cache, sizeof(os_header_t)); + STp->buffer->buffer_bytes = sizeof(os_header_t); + STp->dirty = 1; + if (osst_flush_write_buffer(STp, aSCpnt, 0)) { + printk(KERN_INFO "osst%i: Couldn't write header frame\n", dev); + return (-EIO); + } + } + result = osst_flush_drive_buffer(STp, aSCpnt); +#if DEBUG + printk(OSST_DEB_MSG "osst%i: Write onstream header group %s\n", dev, result?"failed":"done"); +#endif + return result; +} + +static int osst_write_header(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int locate_eod) +{ + os_header_t * header; + int reslt ; + int dev = TAPE_NR(STp->devt); + +#if DEBUG + printk(OSST_DEB_MSG "osst%i: Writing tape header\n", dev); +#endif + if (STp->raw) return 0; + + if (STp->header_cache == NULL) { + if ((STp->header_cache = (os_header_t *)vmalloc(sizeof(os_header_t))) == NULL) { + printk(KERN_ERR "osst%i: Failed to allocate header cache\n", dev); + return (-ENOMEM); + } + memset(STp->header_cache, 0, sizeof(os_header_t)); +#if DEBUG + printk(OSST_DEB_MSG "osst%d: Allocated and cleared memory for header cache\n", dev); +#endif + } + if (STp->header_ok) STp->update_frame_cntr++; + else STp->update_frame_cntr = 0; + + header = STp->header_cache; + strcpy(header->ident_str, "ADR_SEQ"); + header->major_rev = 1; + header->minor_rev = 4; + header->ext_trk_tb_off = htons(17192); + header->pt_par_num = 1; + header->partition[0].partition_num = OS_DATA_PARTITION; + header->partition[0].par_desc_ver = OS_PARTITION_VERSION; + header->partition[0].wrt_pass_cntr = htons(STp->wrt_pass_cntr); + header->partition[0].first_frame_ppos = htonl(STp->first_data_ppos); + header->partition[0].last_frame_ppos = htonl(STp->capacity); + header->partition[0].eod_frame_ppos = htonl(STp->eod_frame_ppos); + header->cfg_col_width = htonl(20); + header->dat_col_width = htonl(1500); + header->qfa_col_width = htonl(0); + header->ext_track_tb.nr_stream_part = 1; + header->ext_track_tb.et_ent_sz = 32; + header->ext_track_tb.dat_ext_trk_ey.et_part_num = 0; + header->ext_track_tb.dat_ext_trk_ey.fmt = 1; + header->ext_track_tb.dat_ext_trk_ey.fm_tab_off = htons(17736); + header->ext_track_tb.dat_ext_trk_ey.last_hlb_hi = 0; + header->ext_track_tb.dat_ext_trk_ey.last_hlb = htonl(STp->eod_frame_lfa); + header->ext_track_tb.dat_ext_trk_ey.last_pp = htonl(STp->eod_frame_ppos); + header->dat_fm_tab.fm_part_num = 0; + header->dat_fm_tab.fm_tab_ent_sz = 4; + header->dat_fm_tab.fm_tab_ent_cnt = htons(STp->filemark_cntfilemark_cnt:OS_FM_TAB_MAX); + + reslt = __osst_write_header(STp, aSCpnt, 0xbae, 5); + if (STp->update_frame_cntr == 0) + osst_write_filler(STp, aSCpnt, 0xbb3, 5); + reslt &= __osst_write_header(STp, aSCpnt, 5, 5); + + if (locate_eod) { +#if DEBUG + printk(OSST_DEB_MSG "osst%i: locating back to eod frame addr %d\n", dev, STp->eod_frame_ppos); +#endif + osst_set_frame_position(STp, aSCpnt, STp->eod_frame_ppos, 0); + } + if (reslt) + printk(KERN_WARNING "osst%i: write header failed\n", dev); + else { + memcpy(STp->application_sig, "LIN4", 4); + STp->linux_media = 1; + STp->linux_media_version = 4; + STp->header_ok = 1; + } + return reslt; +} + +static int osst_reset_header(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt) +{ + if (STp->header_cache != NULL) + memset(STp->header_cache, 0, sizeof(os_header_t)); + + STp->logical_blk_num = 0; + STp->logical_blk_in_buffer = 0; + STp->eod_frame_ppos = STp->first_data_ppos = 0x0000000A; + STp->filemark_cnt = 0; + STp->first_mark_ppos = STp->last_mark_ppos = -1; + return osst_write_header(STp, aSCpnt, 1); +} + +static int __osst_analyze_headers(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int block) +{ + int dev = TAPE_NR(STp->devt); + os_header_t * header; + os_aux_t * aux; + char id_string[8]; + int linux_media_version, + update_frame_cntr; + + if (STp->raw) + return 1; + + if (block == 5 || block == 0xbae || STp->buffer->last_result_fatal) { + if (osst_set_frame_position(STp, aSCpnt, block, 0)) + printk(KERN_WARNING "osst%i: Couldn't position tape\n", dev); + if (osst_initiate_read (STp, aSCpnt)) { + printk(KERN_WARNING "osst%i: Couldn't initiate read\n", dev); + return 0; + } + } + if (osst_read_block(STp, aSCpnt, 180)) { +#if DEBUG + printk(OSST_DEB_MSG "osst%i: Couldn't read header frame\n", dev); +#endif + return 0; + } + header = (os_header_t *) STp->buffer->b_data; /* warning: only first segment addressable */ + aux = STp->buffer->aux; + if (aux->frame_type != OS_FRAME_TYPE_HEADER) { +#if DEBUG + printk(OSST_DEB_MSG "osst%i: Skipping non-header frame (%d)\n", dev, block); +#endif + return 0; + } + if (strncmp(header->ident_str, "ADR_SEQ", 7) != 0 && + strncmp(header->ident_str, "ADR-SEQ", 7) != 0) { + strncpy(id_string, header->ident_str, 7); + id_string[7] = 0; + printk(KERN_INFO "osst%i: Invalid header identification string %s\n", dev, id_string); + return 0; + } + update_frame_cntr = ntohl(aux->update_frame_cntr); + if (update_frame_cntr < STp->update_frame_cntr) { +#if DEBUG + printk(OSST_DEB_MSG "osst%i: Skipping frame %d with update_frame_counter %d<%d\n", + dev, block, update_frame_cntr, STp->update_frame_cntr); +#endif + return 0; + } + if (header->major_rev != 1 || header->minor_rev != 4 ) { + printk(KERN_INFO "osst%i: %s revision %d.%d detected (1.4 supported)\n", + dev, (header->major_rev != 1 || header->minor_rev < 2 || + header->minor_rev > 4 )? "Invalid" : "Warning:", + header->major_rev, header->minor_rev); + if (header->major_rev != 1 || header->minor_rev < 2 || header->minor_rev > 4) + return 0; + } + if (header->pt_par_num != 1) + printk(KERN_INFO "osst%i: Warning: %d partitions defined, only one supported\n", + dev, header->pt_par_num); + memcpy(id_string, aux->application_sig, 4); + id_string[4] = 0; + if (memcmp(id_string, "LIN", 3) == 0) { + STp->linux_media = 1; + linux_media_version = id_string[3] - '0'; + if (linux_media_version != 4) + printk(KERN_INFO "osst%i: Linux media version %d detected (current 4)\n", + dev, linux_media_version); + } else { + printk(KERN_WARNING "osst%i: non Linux media detected (%s)\n", dev, id_string); + return 0; + } + if (linux_media_version < STp->linux_media_version) { +#if DEBUG + printk(OSST_DEB_MSG "osst%i: Skipping frame %d with linux_media_version %d\n", + dev, block, linux_media_version); +#endif + return 0; + } + if (linux_media_version > STp->linux_media_version) { +#if DEBUG + printk(OSST_DEB_MSG "osst%i: Frame %d sets linux_media_version to %d\n", + dev, block, linux_media_version); +#endif + memcpy(STp->application_sig, id_string, 5); + STp->linux_media_version = linux_media_version; + STp->update_frame_cntr = -1; + } + if (update_frame_cntr > STp->update_frame_cntr) { +#if DEBUG + printk(OSST_DEB_MSG "osst%i: Frame %d sets update_frame_counter to %d\n", + dev, block, update_frame_cntr); +#endif + if (STp->header_cache == NULL) { + if ((STp->header_cache = (os_header_t *)vmalloc(sizeof(os_header_t))) == NULL) { + printk(KERN_ERR "osst%i: Failed to allocate header cache\n", dev); + return 0; + } +#if DEBUG + printk(OSST_DEB_MSG "osst%d: Allocated memory for header cache\n", dev); +#endif + } + osst_copy_from_buffer(STp->buffer, (unsigned char *)STp->header_cache); + header = STp->header_cache; /* further accesses from cached (full) copy */ + + STp->wrt_pass_cntr = ntohs(header->partition[0].wrt_pass_cntr); + STp->first_data_ppos = ntohl(header->partition[0].first_frame_ppos); + STp->eod_frame_ppos = ntohl(header->partition[0].eod_frame_ppos); + STp->eod_frame_lfa = ntohl(header->ext_track_tb.dat_ext_trk_ey.last_hlb); + STp->filemark_cnt = ntohl(aux->filemark_cnt); + STp->first_mark_ppos = ntohl(aux->next_mark_ppos); + STp->last_mark_ppos = ntohl(aux->last_mark_ppos); + STp->update_frame_cntr = update_frame_cntr; +#if DEBUG + printk(OSST_DEB_MSG "osst%i: detected write pass %d, update frame counter %d, filemark counter %d\n", + dev, STp->wrt_pass_cntr, STp->update_frame_cntr, STp->filemark_cnt); + printk(OSST_DEB_MSG "osst%i: first data frame on tape = %d, last = %d, eod frame = %d\n", dev, + STp->first_data_ppos, + ntohl(header->partition[0].last_frame_ppos), + ntohl(header->partition[0].eod_frame_ppos)); + printk(OSST_DEB_MSG "osst%i: first mark on tape = %d, last = %d, eod frame = %d\n", + dev, STp->first_mark_ppos, STp->last_mark_ppos, STp->eod_frame_ppos); +#endif + if (header->minor_rev < 4 && STp->linux_media_version == 4) { + printk(OSST_DEB_MSG "osst%i: Moving filemark list to ADR 1.4 location\n", dev); + memcpy((void *)header->dat_fm_tab.fm_tab_ent, + (void *)header->old_filemark_list, sizeof(header->dat_fm_tab.fm_tab_ent)); + memset((void *)header->old_filemark_list, 0, sizeof(header->old_filemark_list)); + } + if (header->minor_rev == 4 && + (header->ext_trk_tb_off != htons(17192) || + header->partition[0].partition_num != OS_DATA_PARTITION || + header->partition[0].par_desc_ver != OS_PARTITION_VERSION || + header->partition[0].last_frame_ppos != htonl(STp->capacity) || + header->cfg_col_width != htonl(20) || + header->dat_col_width != htonl(1500) || + header->qfa_col_width != htonl(0) || + header->ext_track_tb.nr_stream_part != 1 || + header->ext_track_tb.et_ent_sz != 32 || + header->ext_track_tb.dat_ext_trk_ey.et_part_num != OS_DATA_PARTITION || + header->ext_track_tb.dat_ext_trk_ey.fmt != 1 || + header->ext_track_tb.dat_ext_trk_ey.fm_tab_off != htons(17736) || + header->ext_track_tb.dat_ext_trk_ey.last_hlb_hi != 0 || + header->ext_track_tb.dat_ext_trk_ey.last_pp != htonl(STp->eod_frame_ppos) || + header->dat_fm_tab.fm_part_num != OS_DATA_PARTITION || + header->dat_fm_tab.fm_tab_ent_sz != 4 || + header->dat_fm_tab.fm_tab_ent_cnt != + htons(STp->filemark_cntfilemark_cnt:OS_FM_TAB_MAX))) + printk(KERN_WARNING "osst%i: Failed consistency check ADR 1.4 format\n", dev); + +// memcpy(STp->header_cache, header, sizeof(os_header_t)); + } + + return 1; +} + +static int osst_analyze_headers(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt) +{ + int position, block; + int first, last; + int valid = 0; + int dev = TAPE_NR(STp->devt); + + position = osst_get_frame_position(STp, aSCpnt); + + if (STp->raw) { + STp->header_ok = STp->linux_media = 1; + STp->linux_media_version = 0; + return 1; + } + STp->header_ok = STp->linux_media = STp->linux_media_version = 0; + STp->wrt_pass_cntr = STp->update_frame_cntr = -1; + STp->eod_frame_ppos = STp->first_data_ppos = -1; + STp->first_mark_ppos = STp->last_mark_ppos = -1; +#if DEBUG + printk(OSST_DEB_MSG "osst%i: Reading header\n", dev); +#endif + + /* optimization for speed - if we are positioned at block 10, read second group first */ + /* TODO try the ADR 1.1 locations for the second group if we have no valid one yet... */ + + first = position==10?0xbae: 5; + last = position==10?0xbb3:10; + + for (block = first; block < last; block++) + if (__osst_analyze_headers(STp, aSCpnt, block)) + valid = 1; + + first = position==10? 5:0xbae; + last = position==10?10:0xbb3; + + for (block = first; block < last; block++) + if (__osst_analyze_headers(STp, aSCpnt, block)) + valid = 1; + + if (!valid) { + printk(KERN_ERR "osst%i: Failed to find valid ADRL header, new media?\n", dev); + STp->eod_frame_ppos = STp->first_data_ppos = 0; + osst_set_frame_position(STp, aSCpnt, 10, 0); + return 0; + } + if (position <= STp->first_data_ppos) { + position = STp->first_data_ppos; + STp->ps[0].drv_file = STp->ps[0].drv_block = STp->logical_blk_num = 0; + } + osst_set_frame_position(STp, aSCpnt, position, 0); + STp->header_ok = 1; + + return 1; +} + +static int osst_verify_position(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt) +{ + int frame_position = STp->first_frame_position; + int logical_blk_num = STp->logical_blk_num; + int prev_mark_ppos = -1; + int actual_mark_ppos, i, n; +#if 1 //DEBUG + int dev = TAPE_NR(STp->devt); + + printk(OSST_DEB_MSG "osst%i: Verify that the tape is really the one we think before writing\n", dev); +#endif + osst_set_frame_position(STp, aSCpnt, frame_position - 1, 0); + if (osst_get_logical_blk(STp, aSCpnt, -1, 0) < 0) { +#if 1 //DEBUG + printk(OSST_DEB_MSG "osst%i: Couldn't get logical blk num in verify_position\n", dev); +#endif + return (-EIO); + } + if (STp->linux_media_version >= 4) { + for (i=0; ifilemark_cnt; i++) + if ((n=ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[i])) < frame_position) + prev_mark_ppos = n; + } else + prev_mark_ppos = frame_position - 1; /* usually - we don't really know */ + actual_mark_ppos = STp->buffer->aux->frame_type == OS_FRAME_TYPE_MARKER ? + frame_position - 1 : ntohl(STp->buffer->aux->last_mark_ppos); + if (frame_position != STp->first_frame_position || + logical_blk_num != STp->logical_blk_num + 1 || + prev_mark_ppos != actual_mark_ppos ) { +#if 1 //DEBUG + printk(OSST_DEB_MSG "osst%i: Block mismatch: frame %d-%d, lblk %d-%d, mark %d-%d\n", dev, + STp->first_frame_position, frame_position, STp->logical_blk_num + 1, + logical_blk_num, actual_mark_ppos, prev_mark_ppos); +#endif + return (-EIO); + } + STp->logical_blk_in_buffer = 0; + STp->logical_blk_num = logical_blk_num; + return 0; +} + +/* Acc. to OnStream, the vers. numbering is the following: + * X.XX for released versions (X=digit), + * XXXY for unreleased versions (Y=letter) + * Ordering 1.05 < 106A < 106a < 106B < ... < 1.06 + * This fn makes monoton numbers out of this scheme ... + */ +static unsigned int osst_parse_firmware_rev (const char * str) +{ + unsigned int rev; + if (str[1] == '.') { + rev = (str[0]-0x30)*10000 + +(str[2]-0x30)*1000 + +(str[3]-0x30)*100; + } else { + rev = (str[0]-0x30)*10000 + +(str[1]-0x30)*1000 + +(str[2]-0x30)*100 - 100; + rev += 2*(str[3] & 0x1f) + +(str[3] >= 0x60? 1: 0); + } + return rev; +} + +/* + * Configure the OnStream SCII tape drive for default operation + */ +static int osst_configure_onstream(OS_Scsi_Tape *STp, Scsi_Cmnd ** aSCpnt) +{ + int dev = TAPE_NR(STp->devt); + unsigned char cmd[MAX_COMMAND_SIZE]; + Scsi_Cmnd * SCpnt = * aSCpnt; + osst_mode_parameter_header_t * header; + osst_block_size_page_t * bs; + osst_capabilities_page_t * cp; + osst_tape_paramtr_page_t * prm; + int drive_buffer_size; + + if (STp->ready != ST_READY) { +#if DEBUG + printk(OSST_DEB_MSG "osst%i: Not Ready\n", dev); +#endif + return (-EIO); + } + + if (STp->os_fw_rev < 10600) { + printk("osst%i: Old OnStream firmware revision detected (%s)\n", + dev, STp->device->rev); + printk("osst%i: An upgrade to version 1.06 or above is recommended\n", + dev); + } + + /* + * Configure 32.5KB (data+aux) frame size. + * Get the current block size from the block size mode page + */ + memset(cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = MODE_SENSE; + cmd[1] = 8; + cmd[2] = BLOCK_SIZE_PAGE; + cmd[4] = BLOCK_SIZE_PAGE_LENGTH + MODE_HEADER_LENGTH; + + SCpnt = osst_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE); + if (SCpnt == NULL) { +#if DEBUG + printk(OSST_DEB_MSG "osst: Busy\n"); +#endif + return (-EBUSY); + } + *aSCpnt = SCpnt; + if ((STp->buffer)->last_result_fatal != 0) { + printk (KERN_ERR "osst%i: Can't get tape block size mode page\n", dev); + return (-EIO); + } + + header = (osst_mode_parameter_header_t *) (STp->buffer)->b_data; + bs = (osst_block_size_page_t *) ((STp->buffer)->b_data + sizeof(osst_mode_parameter_header_t) + header->bdl); + +#if DEBUG + printk(KERN_INFO "osst%i: 32KB play back: %s\n", dev, bs->play32 ? "Yes" : "No"); + printk(KERN_INFO "osst%i: 32.5KB play back: %s\n", dev, bs->play32_5 ? "Yes" : "No"); + printk(KERN_INFO "osst%i: 32KB record: %s\n", dev, bs->record32 ? "Yes" : "No"); + printk(KERN_INFO "osst%i: 32.5KB record: %s\n", dev, bs->record32_5 ? "Yes" : "No"); +#endif + + /* + * Configure default auto columns mode, 32.5KB transfer mode + */ + bs->one = 1; + bs->play32 = 0; + bs->play32_5 = 1; + bs->record32 = 0; + bs->record32_5 = 1; + + memset(cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = MODE_SELECT; + cmd[1] = 0x10; + cmd[4] = BLOCK_SIZE_PAGE_LENGTH + MODE_HEADER_LENGTH; + + SCpnt = osst_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE); + *aSCpnt = SCpnt; + if ((STp->buffer)->last_result_fatal != 0) { + printk (KERN_ERR "osst%i: Couldn't set tape block size mode page\n", dev); + return (-EIO); + } + + STp->block_size = (STp->raw) ? OS_FRAME_SIZE : OS_DATA_SIZE; + STp->min_block = OS_FRAME_SIZE; /* FIXME */ + STp->max_block = STp->block_size; + +#if DEBUG + printk(KERN_INFO "osst%i: Block Size changed to 32.5K\n", dev); + /* + * In debug mode, we want to see as many errors as possible + * to test the error recovery mechanism. + */ + osst_set_retries(STp, aSCpnt, 0); + SCpnt = * aSCpnt; +#endif + + /* + * Set vendor name to 'LIN4' for "Linux support version 4". + */ + + memset(cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = MODE_SELECT; + cmd[1] = 0x10; + cmd[4] = VENDOR_IDENT_PAGE_LENGTH + MODE_HEADER_LENGTH; + + header->mode_data_length = VENDOR_IDENT_PAGE_LENGTH + MODE_HEADER_LENGTH - 1; + header->medium_type = 0; /* Medium Type - ignoring */ + header->dsp = 0; /* Reserved */ + header->bdl = 0; /* Block Descriptor Length */ + + (STp->buffer)->b_data[MODE_HEADER_LENGTH + 0] = VENDOR_IDENT_PAGE | (1 << 7); + (STp->buffer)->b_data[MODE_HEADER_LENGTH + 1] = 6; + (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] = 'L'; + (STp->buffer)->b_data[MODE_HEADER_LENGTH + 3] = 'I'; + (STp->buffer)->b_data[MODE_HEADER_LENGTH + 4] = 'N'; + (STp->buffer)->b_data[MODE_HEADER_LENGTH + 5] = '4'; + (STp->buffer)->b_data[MODE_HEADER_LENGTH + 6] = 0; + (STp->buffer)->b_data[MODE_HEADER_LENGTH + 7] = 0; + + SCpnt = osst_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE); + *aSCpnt = SCpnt; + + if ((STp->buffer)->last_result_fatal != 0) { + printk (KERN_ERR "osst%i: Couldn't set vendor name to %s\n", dev, + (char *) ((STp->buffer)->b_data + MODE_HEADER_LENGTH + 2)); + return (-EIO); + } + + memset(cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = MODE_SENSE; + cmd[1] = 8; + cmd[2] = CAPABILITIES_PAGE; + cmd[4] = CAPABILITIES_PAGE_LENGTH + MODE_HEADER_LENGTH; + + SCpnt = osst_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE); + *aSCpnt = SCpnt; + + if ((STp->buffer)->last_result_fatal != 0) { + printk (KERN_ERR "osst%i: can't get capabilities page\n", dev); + return (-EIO); + } + + header = (osst_mode_parameter_header_t *) (STp->buffer)->b_data; + cp = (osst_capabilities_page_t *) ((STp->buffer)->b_data + + sizeof(osst_mode_parameter_header_t) + header->bdl); + + drive_buffer_size = ntohs(cp->buffer_size) / 2; + + memset(cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = MODE_SENSE; + cmd[1] = 8; + cmd[2] = TAPE_PARAMTR_PAGE; + cmd[4] = TAPE_PARAMTR_PAGE_LENGTH + MODE_HEADER_LENGTH; + + SCpnt = osst_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE); + *aSCpnt = SCpnt; + + if ((STp->buffer)->last_result_fatal != 0) { + printk (KERN_ERR "osst%i: can't get tape parameter page\n", dev); + return (-EIO); + } + + header = (osst_mode_parameter_header_t *) (STp->buffer)->b_data; + prm = (osst_tape_paramtr_page_t *) ((STp->buffer)->b_data + + sizeof(osst_mode_parameter_header_t) + header->bdl); + + STp->density = prm->density; + STp->capacity = ntohs(prm->segtrk) * ntohs(prm->trks); +#if DEBUG + printk(OSST_DEB_MSG "osst%d: Density %d, tape length: %dMB, drive buffer size: %dKB\n", + dev, STp->density, STp->capacity / 32, drive_buffer_size); +#endif + + return 0; + +} + + +/* Step over EOF if it has been inadvertently crossed (ioctl not used because + it messes up the block number). */ +static int cross_eof(OS_Scsi_Tape *STp, Scsi_Cmnd ** aSCpnt, int forward) +{ + int result; + int dev = TAPE_NR(STp->devt); + +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: Stepping over filemark %s.\n", + dev, forward ? "forward" : "backward"); +#endif + + if (forward) { + /* assumes that the filemark is already read by the drive, so this is low cost */ + result = osst_space_over_filemarks_forward_slow(STp, aSCpnt, MTFSF, 1); + } + else + /* assumes this is only called if we just read the filemark! */ + result = osst_seek_logical_blk(STp, aSCpnt, STp->logical_blk_num - 1); + + if (result < 0) + printk(KERN_ERR "osst%d: Stepping over filemark %s failed.\n", + dev, forward ? "forward" : "backward"); + + return result; +} + + +/* Get the tape position. */ + +static int osst_get_frame_position(OS_Scsi_Tape *STp, Scsi_Cmnd ** aSCpnt) +{ + int result = 0; + unsigned char scmd[MAX_COMMAND_SIZE]; + Scsi_Cmnd * SCpnt; + + /* KG: We want to be able to use it for checking Write Buffer availability + * and thus don't want to risk to overwrite anything. Exchange buffers ... */ + char mybuf[24]; + char * olddata = STp->buffer->b_data; + int oldsize = STp->buffer->buffer_size; + int dev = TAPE_NR(STp->devt); + + if (STp->ready != ST_READY) + return (-EIO); + + memset (scmd, 0, MAX_COMMAND_SIZE); + scmd[0] = READ_POSITION; + + STp->buffer->b_data = mybuf; STp->buffer->buffer_size = 24; + SCpnt = osst_do_scsi(*aSCpnt, STp, scmd, 20, STp->timeout, MAX_READY_RETRIES, TRUE); + if (!SCpnt) { + STp->buffer->b_data = olddata; STp->buffer->buffer_size = oldsize; + return (-EBUSY); + } + *aSCpnt = SCpnt; + + if (STp->buffer->last_result_fatal) + result = ((SCpnt->sense_buffer[2] & 0x0f) == 3) ? -EIO : -EINVAL; + + if (result == -EINVAL) + printk(KERN_ERR "osst%d: Can't read tape position.\n", dev); + else { + + if (result == -EIO) { /* re-read position */ + memset (scmd, 0, MAX_COMMAND_SIZE); + scmd[0] = READ_POSITION; + STp->buffer->b_data = mybuf; STp->buffer->buffer_size = 24; + SCpnt = osst_do_scsi(SCpnt, STp, scmd, 20, STp->timeout, MAX_READY_RETRIES, TRUE); + } + STp->first_frame_position = ((STp->buffer)->b_data[4] << 24) + + ((STp->buffer)->b_data[5] << 16) + + ((STp->buffer)->b_data[6] << 8) + + (STp->buffer)->b_data[7]; + STp->last_frame_position = ((STp->buffer)->b_data[ 8] << 24) + + ((STp->buffer)->b_data[ 9] << 16) + + ((STp->buffer)->b_data[10] << 8) + + (STp->buffer)->b_data[11]; + STp->cur_frames = (STp->buffer)->b_data[15]; +#if DEBUG + if (debugging) { + printk(OSST_DEB_MSG "osst%d: Drive Positions: host %d, tape %d%s, buffer %d\n", dev, + STp->first_frame_position, STp->last_frame_position, + ((STp->buffer)->b_data[0]&0x80)?" (BOP)": + ((STp->buffer)->b_data[0]&0x40)?" (EOP)":"", + STp->cur_frames); + } +#endif + if (STp->cur_frames == 0 && STp->first_frame_position != STp->last_frame_position) { +#if DEBUG + printk(KERN_WARNING "osst%d: Correcting read position %d, %d, %d\n", dev, + STp->first_frame_position, STp->last_frame_position, STp->cur_frames); +#endif + STp->first_frame_position = STp->last_frame_position; + } + } + STp->buffer->b_data = olddata; STp->buffer->buffer_size = oldsize; + + return (result == 0 ? STp->first_frame_position : result); +} + + +/* Set the tape block */ +static int osst_set_frame_position(OS_Scsi_Tape *STp, Scsi_Cmnd ** aSCpnt, int block, int skip) +{ + ST_partstat *STps; + int result = 0; + int timeout; + unsigned char scmd[MAX_COMMAND_SIZE]; + Scsi_Cmnd * SCpnt; + int dev = TAPE_NR(STp->devt); + + if (STp->ready != ST_READY) + return (-EIO); + timeout = STp->long_timeout; + STps = &(STp->ps[STp->partition]); + + if (block < 0 || block > STp->capacity) { + printk(KERN_ERR "osst%d: Reposition request %d out of range\n", dev, block); + block = block < 0 ? 0 : (STp->capacity - 1); + result = (-EINVAL); + } +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: Setting block to %d.\n", dev, block); +#endif + memset (scmd, 0, MAX_COMMAND_SIZE); + scmd[0] = SEEK_10; + scmd[1] = 1; + scmd[3] = (block >> 24); + scmd[4] = (block >> 16); + scmd[5] = (block >> 8); + scmd[6] = block; + if (skip) + scmd[9] = 0x80; + + SCpnt = osst_do_scsi(*aSCpnt, STp, scmd, 20, timeout, MAX_READY_RETRIES, TRUE); + if (!SCpnt) { +#if DEBUG + printk(OSST_DEB_MSG "osst%d: Busy\n", dev); +#endif + return (-EBUSY); + } + *aSCpnt = SCpnt; + + STp->first_frame_position = STp->last_frame_position = block; + STps->eof = ST_NOEOF; + if ((STp->buffer)->last_result_fatal != 0) { +#if DEBUG + printk(OSST_DEB_MSG "osst%d: SEEK command failed.\n", dev); +#endif + result = (-EIO); + } + STps->at_sm = 0; + STps->rw = ST_IDLE; + STp->logical_blk_in_buffer = 0; + return result; +} + + + +/* osst versions of st functions - augmented and stripped to suit OnStream only */ + +/* Flush the write buffer (never need to write if variable blocksize). */ +static int osst_flush_write_buffer(OS_Scsi_Tape *STp, Scsi_Cmnd ** aSCpnt, int file_blk) +{ + int offset, transfer, blks = 0; + int result = 0; + unsigned char cmd[MAX_COMMAND_SIZE]; + Scsi_Cmnd * SCpnt = *aSCpnt; + ST_partstat * STps; + int dev = TAPE_NR(STp->devt); + + if ((STp->buffer)->writing) { + if (SCpnt == (STp->buffer)->last_SCpnt) +#if DEBUG + { printk(OSST_DEB_MSG + "osst%d: aSCpnt points to Scsi_Cmnd that write_behind_check will release -- cleared\n", dev); +#endif + *aSCpnt = SCpnt = NULL; +#if DEBUG + } else if (SCpnt) + printk(OSST_DEB_MSG + "osst%d: aSCpnt does not point to Scsi_Cmnd that write_behind_check will release -- strange\n", dev); +#endif + osst_write_behind_check(STp); + if ((STp->buffer)->last_result_fatal) { +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: Async write error (flush) %x.\n", + dev, (STp->buffer)->last_result); +#endif + if ((STp->buffer)->last_result == INT_MAX) + return (-ENOSPC); + return (-EIO); + } + } + + result = 0; + if (STp->dirty == 1) { + + offset = STp->buffer->buffer_bytes; + transfer = OS_FRAME_SIZE; + blks = 1; + +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: Flushing %d bytes, Tranfering %d bytes in %d blocks.\n", + dev, offset, transfer, blks); +#endif + if (offset < OS_DATA_SIZE) + osst_zero_buffer_tail(STp->buffer); + + /* TODO: Error handling! */ + if (STp->poll) + result = osst_wait_frame (STp, aSCpnt, STp->first_frame_position, -50, 120); + + memset(cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = WRITE_6; + cmd[1] = 1; + cmd[4] = blks; + + SCpnt = osst_do_scsi(*aSCpnt, STp, cmd, transfer, STp->timeout, MAX_WRITE_RETRIES, TRUE); + *aSCpnt = SCpnt; + if (!SCpnt) + return (-EBUSY); + + STps = &(STp->ps[STp->partition]); + if ((STp->buffer)->last_result_fatal != 0) { + printk(OSST_DEB_MSG "osst%d: write sense [0]=0x%02x [2]=%02x [12]=%02x [13]=%02x\n", TAPE_NR(STp->devt), + SCpnt->sense_buffer[0], SCpnt->sense_buffer[2], SCpnt->sense_buffer[12], SCpnt->sense_buffer[13]); + if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && + (SCpnt->sense_buffer[2] & 0x40) && /* FIXME - SC-30 drive doesn't assert EOM bit */ + (SCpnt->sense_buffer[2] & 0x0f) == NO_SENSE) { + STp->dirty = 0; + (STp->buffer)->buffer_bytes = 0; + result = (-ENOSPC); + } + else { + if (osst_write_error_recovery(STp, aSCpnt, 1)) { + printk(KERN_ERR "osst%d: Error on flush.\n", dev); + result = (-EIO); + } + } + STps->drv_block = (-1); + } + else { + STp->first_frame_position++; + if (file_blk && STps->drv_block >= 0) + STps->drv_block += blks; + STp->first_frame_position += blks; + STp->dirty = 0; + (STp->buffer)->buffer_bytes = 0; + } + } +#if DEBUG + printk(OSST_DEB_MSG "osst%d: Exit flush write buffer with code %d\n", dev, result); +#endif + return result; +} + + +/* Flush the tape buffer. The tape will be positioned correctly unless + seek_next is true. */ +static int osst_flush_buffer(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, int seek_next) +{ + int backspace, result; + OSST_buffer * STbuffer; + ST_partstat * STps; +#if DEBUG + int dev = TAPE_NR(STp->devt); +#endif + + STbuffer = STp->buffer; + + /* + * If there was a bus reset, block further access + * to this device. + */ + if( STp->device->was_reset ) + return (-EIO); + + if (STp->ready != ST_READY) + return 0; + + STps = &(STp->ps[STp->partition]); + if (STps->rw == ST_WRITING) /* Writing */ + return osst_flush_write_buffer(STp, aSCpnt, 1); + + if (STp->block_size == 0) + return 0; + +#if DEBUG + printk(OSST_DEB_MSG "osst%i: Reached flush (read) buffer\n", dev); +#endif + backspace = ((STp->buffer)->buffer_bytes + + (STp->buffer)->read_pointer) / STp->block_size - + ((STp->buffer)->read_pointer + STp->block_size - 1) / + STp->block_size; + (STp->buffer)->buffer_bytes = 0; + (STp->buffer)->read_pointer = 0; + result = 0; + if (!seek_next) { + if (STps->eof == ST_FM_HIT) { + result = cross_eof(STp, aSCpnt, FALSE); /* Back over the EOF hit */ + if (!result) + STps->eof = ST_NOEOF; + else { + if (STps->drv_file >= 0) + STps->drv_file++; + STps->drv_block = 0; + } + } + if (!result && backspace > 0) /* TODO -- design and run a test case for this */ + result = osst_seek_logical_blk(STp, aSCpnt, STp->logical_blk_num - backspace); + } + else if (STps->eof == ST_FM_HIT) { + if (STps->drv_file >= 0) + STps->drv_file++; + STps->drv_block = 0; + STps->eof = ST_NOEOF; + } + + return result; + +} + + +/* Entry points to osst */ + +/* Write command */ +static ssize_t osst_write(struct file * filp, const char * buf, size_t count, loff_t *ppos) +{ + struct inode *inode = filp->f_dentry->d_inode; + ssize_t total; + ssize_t i, do_count, blks, retval, transfer; + int write_threshold; + int doing_write = 0; + unsigned char cmd[MAX_COMMAND_SIZE]; + const char *b_point; + Scsi_Cmnd * SCpnt = NULL; + OS_Scsi_Tape * STp; + ST_mode * STm; + ST_partstat * STps; + int dev = TAPE_NR(inode->i_rdev); + + STp = os_scsi_tapes[dev]; + + /* + * If we are in the middle of error recovery, don't let anyone + * else try and use this device. Also, if error recovery fails, it + * may try and take the device offline, in which case all further + * access to the device is prohibited. + */ + if( !scsi_block_when_processing_errors(STp->device) ) { + return -ENXIO; + } + + if (ppos != &filp->f_pos) { + /* "A request was outside the capabilities of the device." */ + return -ENXIO; + } + + if (STp->ready != ST_READY) { + if (STp->ready == ST_NO_TAPE) + return (-ENOMEDIUM); + else + return (-EIO); + } + STm = &(STp->modes[STp->current_mode]); + if (!STm->defined) + return (-ENXIO); + if (count == 0) + return 0; + + /* + * If there was a bus reset, block further access + * to this device. + */ + if( STp->device->was_reset ) + return (-EIO); + +#if DEBUG + if (!STp->in_use) { + printk(OSST_DEB_MSG "osst%d: Incorrect device.\n", dev); + return (-EIO); + } +#endif + + if (STp->write_prot) + return (-EACCES); + + /* Write must be integral number of blocks */ + if (STp->block_size != 0 && (count % STp->block_size) != 0) { + printk(KERN_WARNING "osst%d: Write (%d bytes) not multiple of tape block size (32k).\n", + dev, count); + return (-EIO); + } + + if (STp->first_frame_position >= STp->capacity - 164) { + printk(KERN_WARNING "osst%d: Write truncated at EOM early warning (frame %d).\n", + dev, STp->first_frame_position); + return (-ENOSPC); + } + + STps = &(STp->ps[STp->partition]); + + if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED && + !osst_int_ioctl(STp, &SCpnt, MTLOCK, 0)) + STp->door_locked = ST_LOCKED_AUTO; + + if (STps->rw == ST_READING) { + retval = osst_flush_buffer(STp, &SCpnt, 0); + if (retval) { + if (SCpnt) scsi_release_command(SCpnt); + return retval; + } + STps->rw = ST_IDLE; + } + else if (STps->rw != ST_WRITING) { + if (!STp->header_ok || (STps->drv_file == 0 && STps->drv_block == 0)) { + STp->wrt_pass_cntr++; +#if DEBUG + printk(OSST_DEB_MSG "osst%d: Allocating next write pass counter: %d\n", + dev, STp->wrt_pass_cntr); +#endif + osst_reset_header(STp, &SCpnt); + STps->drv_file = STps->drv_block = STp->logical_blk_num = 0; + } else if (STp->fast_open && osst_verify_position(STp, &SCpnt)) { + if (SCpnt) scsi_release_command(SCpnt); + return (-EIO); + } + STp->fast_open = FALSE; + } + if (!STp->header_ok) { +#if DEBUG + printk(OSST_DEB_MSG "osst%d: Write cannot proceed without valid headers\n", dev); +#endif + if (SCpnt) scsi_release_command(SCpnt); + return (-EIO); + } + if ((STp->buffer)->writing) { +if (SCpnt) printk(KERN_ERR "osst%d: Not supposed to have SCpnt at line %d\n", dev, __LINE__); + osst_write_behind_check(STp); + if ((STp->buffer)->last_result_fatal) { +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: Async write error (write) %x.\n", dev, + (STp->buffer)->last_result); +#endif + if ((STp->buffer)->last_result == INT_MAX) + STps->eof = ST_EOM_OK; + else + STps->eof = ST_EOM_ERROR; + } + } + if (SCpnt) scsi_release_command(SCpnt); + SCpnt = NULL; + if (STps->eof == ST_EOM_OK) + return (-ENOSPC); + else if (STps->eof == ST_EOM_ERROR) + return (-EIO); + + /* Check the buffer readability in cases where copy_user might catch + the problems after some tape movement. */ + if ((copy_from_user(&i, buf, 1) != 0 || + copy_from_user(&i, buf + count - 1, 1) != 0)) + return (-EFAULT); + + if (!STm->do_buffer_writes) { +#if 0 + if (STp->block_size != 0 && (count % STp->block_size) != 0) + return (-EIO); /* Write must be integral number of blocks */ +#endif + write_threshold = 1; + } + else + write_threshold = (STp->buffer)->buffer_blocks * STp->block_size; + if (!STm->do_async_writes) + write_threshold--; + + total = count; + + if ((!STp-> raw) && (STp->first_frame_position == 0xbae)) { +#if DEBUG + printk(OSST_DEB_MSG "osst%d: Skipping over config partition.\n", dev); +#endif + if (osst_flush_drive_buffer(STp, &SCpnt) < 0) { + if (SCpnt) scsi_release_command(SCpnt); + return (-EIO); + } + /* error recovery may have bumped us past the header partition */ + if (osst_get_frame_position(STp, &SCpnt) < 0xbb8) + osst_position_tape_and_confirm(STp, &SCpnt, 0xbb8); + } + + if (STp->poll) + retval = osst_wait_frame (STp, &SCpnt, STp->first_frame_position, -50, 60); + /* TODO: Check for an error ! */ + + memset(cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = WRITE_6; + cmd[1] = 1; + + STps->rw = ST_WRITING; + STp->write_type = OS_WRITE_DATA; + +#if DEBUG + printk(OSST_DEB_MSG "osst%d: Writing %d bytes to file %d block %d lblk %d frame %d\n", + dev, count, STps->drv_file, STps->drv_block, + STp->logical_blk_num, STp->first_frame_position); +#endif + + b_point = buf; + while ((STp->buffer)->buffer_bytes + count > write_threshold) + { + doing_write = 1; + do_count = (STp->buffer)->buffer_blocks * STp->block_size - + (STp->buffer)->buffer_bytes; + if (do_count > count) + do_count = count; + + i = append_to_buffer(b_point, STp->buffer, do_count); + if (i) { + if (SCpnt != NULL) { + scsi_release_command(SCpnt); + SCpnt = NULL; + } + return i; + } + + transfer = OS_FRAME_SIZE; + blks = 1; + + osst_init_aux(STp, OS_FRAME_TYPE_DATA, STp->logical_blk_num++ ); + + cmd[2] = blks >> 16; + cmd[3] = blks >> 8; + cmd[4] = blks; + + SCpnt = osst_do_scsi(SCpnt, STp, cmd, transfer, STp->timeout, MAX_WRITE_RETRIES, TRUE); + if (!SCpnt) + return (-EBUSY); + + if ((STp->buffer)->last_result_fatal != 0) { +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: Error on write:\n", dev); +#endif + if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && + (SCpnt->sense_buffer[2] & 0x40)) { + if ((SCpnt->sense_buffer[0] & 0x80) != 0) + transfer = (SCpnt->sense_buffer[3] << 24) | + (SCpnt->sense_buffer[4] << 16) | + (SCpnt->sense_buffer[5] << 8) | SCpnt->sense_buffer[6]; + else + transfer = 0; + transfer *= STp->block_size; + if (transfer <= do_count) { + filp->f_pos += do_count - transfer; + count -= do_count - transfer; + if (STps->drv_block >= 0) { + STps->drv_block += (do_count - transfer) / STp->block_size; + } + STps->eof = ST_EOM_OK; + retval = (-ENOSPC); /* EOM within current request */ +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: EOM with %d bytes unwritten.\n", + dev, transfer); +#endif + } + else { + STps->eof = ST_EOM_ERROR; + STps->drv_block = (-1); /* Too cautious? */ + retval = (-EIO); /* EOM for old data */ +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: EOM with lost data.\n", dev); +#endif + } + } + else { + if (osst_write_error_recovery(STp, &SCpnt, 1) == 0) goto ok; + STps->drv_block = (-1); /* Too cautious? */ + retval = (-EIO); + } + + scsi_release_command(SCpnt); + SCpnt = NULL; + (STp->buffer)->buffer_bytes = 0; + STp->dirty = 0; + if (count < total) + return total - count; + else + return retval; + } + STp->first_frame_position++; +ok: + filp->f_pos += do_count; + b_point += do_count; + count -= do_count; + if (STps->drv_block >= 0) { + STps->drv_block += blks; + } + STp->first_frame_position += blks; + (STp->buffer)->buffer_bytes = 0; + STp->dirty = 0; + } + if (count != 0) { + STp->dirty = 1; + i = append_to_buffer(b_point, STp->buffer, count); + if (i) { + if (SCpnt != NULL) { + scsi_release_command(SCpnt); + SCpnt = NULL; + } + return i; + } + filp->f_pos += count; + count = 0; + } + + if (doing_write && (STp->buffer)->last_result_fatal != 0) { + scsi_release_command(SCpnt); + SCpnt = NULL; + return (STp->buffer)->last_result_fatal; + } + + if (STm->do_async_writes && + ((STp->buffer)->buffer_bytes >= STp->write_threshold && + (STp->buffer)->buffer_bytes >= OS_DATA_SIZE) ) { + /* Schedule an asynchronous write */ + (STp->buffer)->writing = ((STp->buffer)->buffer_bytes / + STp->block_size) * STp->block_size; + STp->dirty = !((STp->buffer)->writing == + (STp->buffer)->buffer_bytes); + + transfer = OS_FRAME_SIZE; + blks = 1; + + osst_init_aux(STp, OS_FRAME_TYPE_DATA, STp->logical_blk_num++ ); + + cmd[2] = blks >> 16; + cmd[3] = blks >> 8; + cmd[4] = blks; +#if DEBUG + STp->write_pending = 1; +#endif + + SCpnt = osst_do_scsi(SCpnt, STp, cmd, transfer, STp->timeout, MAX_WRITE_RETRIES, FALSE); + if (SCpnt == NULL) + return (-EIO); + } + else if (SCpnt != NULL) { + scsi_release_command(SCpnt); + SCpnt = NULL; + } + STps->at_sm &= (total == 0); + if (total > 0) + STps->eof = ST_NOEOF; + + return( total); +} + + +/* Read command */ +static ssize_t osst_read(struct file * filp, char * buf, size_t count, loff_t *ppos) +{ + struct inode * inode = filp->f_dentry->d_inode; + ssize_t total; + ssize_t i, transfer; + int special; + OS_Scsi_Tape * STp; + ST_mode * STm; + ST_partstat * STps; + Scsi_Cmnd *SCpnt = NULL; + int dev = TAPE_NR(inode->i_rdev); + + STp = os_scsi_tapes[dev]; + + /* + * If we are in the middle of error recovery, don't let anyone + * else try and use this device. Also, if error recovery fails, it + * may try and take the device offline, in which case all further + * access to the device is prohibited. + */ + if( !scsi_block_when_processing_errors(STp->device) ) { + return -ENXIO; + } + + if (ppos != &filp->f_pos) { + /* "A request was outside the capabilities of the device." */ + return -ENXIO; + } + + if (STp->ready != ST_READY) { + if (STp->ready == ST_NO_TAPE) + return (-ENOMEDIUM); + else + return (-EIO); + } + STm = &(STp->modes[STp->current_mode]); + if (!STm->defined) + return (-ENXIO); +#if DEBUG + if (!STp->in_use) { + printk(OSST_DEB_MSG "osst%d: Incorrect device.\n", dev); + return (-EIO); + } +#endif + /* Must have initialized medium */ + if (!STp->header_ok) return (-EIO); + + if ((count % STp->block_size) != 0) { + printk(KERN_WARNING "osst%d: Use multiple of %d bytes as block size (%d requested)\n", + dev, STp->block_size, count); + return (-EIO); /* Read must be integral number of blocks */ + } + + if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED && + !osst_int_ioctl(STp, &SCpnt, MTLOCK, 0)) + STp->door_locked = ST_LOCKED_AUTO; + + STps = &(STp->ps[STp->partition]); + if (STps->rw == ST_WRITING) { + transfer = osst_flush_buffer(STp, &SCpnt, 0); + if (SCpnt) scsi_release_command(SCpnt); + SCpnt = NULL; + if (transfer) + return transfer; + STps->rw = ST_IDLE; + } + +#if DEBUG + if (debugging && STps->eof != ST_NOEOF) + printk(OSST_DEB_MSG "osst%d: EOF/EOM flag up (%d). Bytes %d\n", dev, + STps->eof, (STp->buffer)->buffer_bytes); +#endif + if ((STp->buffer)->buffer_bytes == 0 && + STps->eof >= ST_EOD_1) { + if (SCpnt) scsi_release_command(SCpnt); + if (STps->eof < ST_EOD) { + STps->eof += 1; + return 0; + } + return (-EIO); /* EOM or Blank Check */ + } + + /* Check the buffer writability before any tape movement. Don't alter + buffer data. */ + if (copy_from_user(&i, buf, 1) != 0 || + copy_to_user(buf, &i, 1) != 0 || + copy_from_user(&i, buf + count - 1, 1) != 0 || + copy_to_user(buf + count - 1, &i, 1) != 0) { + if (SCpnt) scsi_release_command(SCpnt); + return (-EFAULT); + } + + /* Loop until enough data in buffer or a special condition found */ + for (total = 0, special = 0; total < count && !special; ) { + + /* Get new data if the buffer is empty */ + if ((STp->buffer)->buffer_bytes == 0) { + special = osst_get_logical_blk(STp, &SCpnt, STp->logical_blk_num, 0); + STp->buffer->buffer_bytes = special ? 0 : OS_DATA_SIZE; + STp->buffer->read_pointer = 0; + STp->logical_blk_num++; /* block to look for next time */ + STp->logical_blk_in_buffer = 0; + if (special < 0) { /* No need to continue read */ + if (SCpnt) scsi_release_command(SCpnt); + return special; + } + STps->drv_block++; + } + + /* Move the data from driver buffer to user buffer */ + if ((STp->buffer)->buffer_bytes > 0) { +#if DEBUG + if (debugging && STps->eof != ST_NOEOF) + printk(OSST_DEB_MSG "osst%d: EOF up (%d). Left %d, needed %d.\n", dev, + STps->eof, (STp->buffer)->buffer_bytes, count - total); +#endif + transfer = (STp->buffer)->buffer_bytes < count - total ? + (STp->buffer)->buffer_bytes : count - total; + i = from_buffer(STp->buffer, buf, transfer); + if (i) { + if (SCpnt) scsi_release_command(SCpnt); + return i; + } + filp->f_pos += transfer; + buf += transfer; + total += transfer; + } + + } /* for (total = 0, special = 0; total < count && !special; ) */ + if (SCpnt) scsi_release_command(SCpnt); + + /* Change the eof state if no data from tape or buffer */ + if (total == 0) { + if (STps->eof == ST_FM_HIT) { + STps->eof = ST_FM; + STps->drv_block = 0; + if (STps->drv_file >= 0) + STps->drv_file++; + } + else if (STps->eof == ST_EOD_1) { + STps->eof = ST_EOD_2; + STps->drv_block = 0; + if (STps->drv_file >= 0) + STps->drv_file++; + } + else if (STps->eof == ST_EOD_2) + STps->eof = ST_EOD; + } + else if (STps->eof == ST_FM) + STps->eof = ST_NOEOF; + + return total; +} + + +/* Set the driver options */ +static void osst_log_options(OS_Scsi_Tape *STp, ST_mode *STm, int dev) +{ + printk(KERN_INFO +"osst%d: Mode %d options: buffer writes: %d, async writes: %d, read ahead: %d\n", + dev, STp->current_mode, STm->do_buffer_writes, STm->do_async_writes, + STm->do_read_ahead); + printk(KERN_INFO +"osst%d: can bsr: %d, two FMs: %d, fast mteom: %d, auto lock: %d,\n", + dev, STp->can_bsr, STp->two_fm, STp->fast_mteom, STp->do_auto_lock); + printk(KERN_INFO +"osst%d: defs for wr: %d, no block limits: %d, partitions: %d, s2 log: %d\n", + dev, STm->defaults_for_writes, STp->omit_blklims, STp->can_partitions, + STp->scsi2_logical); + printk(KERN_INFO +"osst%d: sysv: %d\n", dev, STm->sysv); +#if DEBUG + printk(KERN_INFO + "osst%d: debugging: %d\n", + dev, debugging); +#endif +} + + +static int osst_set_options(OS_Scsi_Tape *STp, long options) +{ + int value; + long code; + ST_mode *STm; + int dev = TAPE_NR(STp->devt); + + STm = &(STp->modes[STp->current_mode]); + if (!STm->defined) { + memcpy(STm, &(STp->modes[0]), sizeof(ST_mode)); + modes_defined = TRUE; +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: Initialized mode %d definition from mode 0\n", + dev, STp->current_mode); +#endif + } + + code = options & MT_ST_OPTIONS; + if (code == MT_ST_BOOLEANS) { + STm->do_buffer_writes = (options & MT_ST_BUFFER_WRITES) != 0; + STm->do_async_writes = (options & MT_ST_ASYNC_WRITES) != 0; + STm->defaults_for_writes = (options & MT_ST_DEF_WRITES) != 0; + STm->do_read_ahead = (options & MT_ST_READ_AHEAD) != 0; + STp->two_fm = (options & MT_ST_TWO_FM) != 0; + STp->fast_mteom = (options & MT_ST_FAST_MTEOM) != 0; + STp->do_auto_lock = (options & MT_ST_AUTO_LOCK) != 0; + STp->can_bsr = (options & MT_ST_CAN_BSR) != 0; + STp->omit_blklims = (options & MT_ST_NO_BLKLIMS) != 0; + if ((STp->device)->scsi_level >= SCSI_2) + STp->can_partitions = (options & MT_ST_CAN_PARTITIONS) != 0; + STp->scsi2_logical = (options & MT_ST_SCSI2LOGICAL) != 0; + STm->sysv = (options & MT_ST_SYSV) != 0; +#if DEBUG + debugging = (options & MT_ST_DEBUGGING) != 0; +#endif + osst_log_options(STp, STm, dev); + } + else if (code == MT_ST_SETBOOLEANS || code == MT_ST_CLEARBOOLEANS) { + value = (code == MT_ST_SETBOOLEANS); + if ((options & MT_ST_BUFFER_WRITES) != 0) + STm->do_buffer_writes = value; + if ((options & MT_ST_ASYNC_WRITES) != 0) + STm->do_async_writes = value; + if ((options & MT_ST_DEF_WRITES) != 0) + STm->defaults_for_writes = value; + if ((options & MT_ST_READ_AHEAD) != 0) + STm->do_read_ahead = value; + if ((options & MT_ST_TWO_FM) != 0) + STp->two_fm = value; + if ((options & MT_ST_FAST_MTEOM) != 0) + STp->fast_mteom = value; + if ((options & MT_ST_AUTO_LOCK) != 0) + STp->do_auto_lock = value; + if ((options & MT_ST_CAN_BSR) != 0) + STp->can_bsr = value; + if ((options & MT_ST_NO_BLKLIMS) != 0) + STp->omit_blklims = value; + if ((STp->device)->scsi_level >= SCSI_2 && + (options & MT_ST_CAN_PARTITIONS) != 0) + STp->can_partitions = value; + if ((options & MT_ST_SCSI2LOGICAL) != 0) + STp->scsi2_logical = value; + if ((options & MT_ST_SYSV) != 0) + STm->sysv = value; +#if DEBUG + if ((options & MT_ST_DEBUGGING) != 0) + debugging = value; +#endif + osst_log_options(STp, STm, dev); + } + else if (code == MT_ST_WRITE_THRESHOLD) { + value = (options & ~MT_ST_OPTIONS) * ST_KILOBYTE; + if (value < 1 || value > osst_buffer_size) { + printk(KERN_WARNING "osst%d: Write threshold %d too small or too large.\n", + dev, value); + return (-EIO); + } + STp->write_threshold = value; + printk(KERN_INFO "osst%d: Write threshold set to %d bytes.\n", + dev, value); + } + else if (code == MT_ST_DEF_BLKSIZE) { + value = (options & ~MT_ST_OPTIONS); + if (value == ~MT_ST_OPTIONS) { + STm->default_blksize = (-1); + printk(KERN_INFO "osst%d: Default block size disabled.\n", dev); + } + else { + STm->default_blksize = value; + printk(KERN_INFO "osst%d: Default block size set to %d bytes.\n", + dev, STm->default_blksize); + } + } + else if (code == MT_ST_TIMEOUTS) { + value = (options & ~MT_ST_OPTIONS); + if ((value & MT_ST_SET_LONG_TIMEOUT) != 0) { + STp->long_timeout = (value & ~MT_ST_SET_LONG_TIMEOUT) * HZ; + printk(KERN_INFO "osst%d: Long timeout set to %d seconds.\n", dev, + (value & ~MT_ST_SET_LONG_TIMEOUT)); + } + else { + STp->timeout = value * HZ; + printk(KERN_INFO "osst%d: Normal timeout set to %d seconds.\n", dev, + value); + } + } + else if (code == MT_ST_DEF_OPTIONS) { + code = (options & ~MT_ST_CLEAR_DEFAULT); + value = (options & MT_ST_CLEAR_DEFAULT); + if (code == MT_ST_DEF_DENSITY) { + if (value == MT_ST_CLEAR_DEFAULT) { + STm->default_density = (-1); + printk(KERN_INFO "osst%d: Density default disabled.\n", dev); + } + else { + STm->default_density = value & 0xff; + printk(KERN_INFO "osst%d: Density default set to %x\n", + dev, STm->default_density); + } + } + else if (code == MT_ST_DEF_DRVBUFFER) { + if (value == MT_ST_CLEAR_DEFAULT) { + STp->default_drvbuffer = 0xff; + printk(KERN_INFO "osst%d: Drive buffer default disabled.\n", dev); + } + else { + STp->default_drvbuffer = value & 7; + printk(KERN_INFO "osst%d: Drive buffer default set to %x\n", + dev, STp->default_drvbuffer); + } + } + else if (code == MT_ST_DEF_COMPRESSION) { + if (value == MT_ST_CLEAR_DEFAULT) { + STm->default_compression = ST_DONT_TOUCH; + printk(KERN_INFO "osst%d: Compression default disabled.\n", dev); + } + else { + STm->default_compression = (value & 1 ? ST_YES : ST_NO); + printk(KERN_INFO "osst%d: Compression default set to %x\n", + dev, (value & 1)); + } + } + } + else + return (-EIO); + + return 0; +} + + +/* Internal ioctl function */ +static int osst_int_ioctl(OS_Scsi_Tape * STp, Scsi_Cmnd ** aSCpnt, unsigned int cmd_in, unsigned long arg) +{ + int timeout; + long ltmp; + int i, ioctl_result; + int chg_eof = TRUE; + unsigned char cmd[MAX_COMMAND_SIZE]; + Scsi_Cmnd * SCpnt = *aSCpnt; + ST_partstat * STps; + int fileno, blkno, at_sm, undone, datalen; + int logical_blk_num; + int dev = TAPE_NR(STp->devt); + + if (STp->ready != ST_READY && cmd_in != MTLOAD) { + if (STp->ready == ST_NO_TAPE) + return (-ENOMEDIUM); + else + return (-EIO); + } + timeout = STp->long_timeout; + STps = &(STp->ps[STp->partition]); + fileno = STps->drv_file; + blkno = STps->drv_block; + at_sm = STps->at_sm; + logical_blk_num = STp->logical_blk_num; + + memset(cmd, 0, MAX_COMMAND_SIZE); + datalen = 0; + switch (cmd_in) { + case MTFSFM: + chg_eof = FALSE; /* Changed from the FSF after this */ + case MTFSF: + if (STp->raw) + return (-EIO); + if (STp->linux_media) + ioctl_result = osst_space_over_filemarks_forward_fast(STp, &SCpnt, cmd_in, arg); + else + ioctl_result = osst_space_over_filemarks_forward_slow(STp, &SCpnt, cmd_in, arg); + logical_blk_num = STp->logical_blk_num; + if (fileno >= 0) + fileno += arg; + blkno = 0; + at_sm &= (arg == 0); + goto os_bypass; + + case MTBSF: + chg_eof = FALSE; /* Changed from the FSF after this */ + case MTBSFM: + if (STp->raw) + return (-EIO); + ioctl_result = osst_space_over_filemarks_backward(STp, &SCpnt, cmd_in, arg); + logical_blk_num = STp->logical_blk_num; + if (fileno >= 0) + fileno -= arg; + blkno = (-1); /* We can't know the block number */ + at_sm &= (arg == 0); + goto os_bypass; + + case MTFSR: + case MTBSR: +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%i: Skipping %lu blocks %s from logical block %d\n", + dev, arg, cmd_in==MTFSR?"forward":"backward", logical_blk_num); +#endif + if (cmd_in == MTFSR) { + logical_blk_num += arg; + if (blkno >= 0) blkno += arg; + } + else { + logical_blk_num -= arg; + if (blkno >= 0) blkno -= arg; + } + ioctl_result = osst_seek_logical_blk(STp, &SCpnt, logical_blk_num-1); + STp->logical_blk_in_buffer = 0; + at_sm &= (arg == 0); + goto os_bypass; + + case MTFSS: + cmd[0] = SPACE; + cmd[1] = 0x04; /* Space Setmarks */ /* FIXME -- OS can't do this? */ + cmd[2] = (arg >> 16); + cmd[3] = (arg >> 8); + cmd[4] = arg; +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: Spacing tape forward %d setmarks.\n", dev, + cmd[2] * 65536 + cmd[3] * 256 + cmd[4]); +#endif + if (arg != 0) { + blkno = fileno = (-1); + at_sm = 1; + } + break; + case MTBSS: + cmd[0] = SPACE; + cmd[1] = 0x04; /* Space Setmarks */ /* FIXME -- OS can't do this? */ + ltmp = (-arg); + cmd[2] = (ltmp >> 16); + cmd[3] = (ltmp >> 8); + cmd[4] = ltmp; +#if DEBUG + if (debugging) { + if (cmd[2] & 0x80) + ltmp = 0xff000000; + ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4]; + printk(OSST_DEB_MSG "osst%d: Spacing tape backward %ld setmarks.\n", + dev, (-ltmp)); + } +#endif + if (arg != 0) { + blkno = fileno = (-1); + at_sm = 1; + } + break; + case MTWEOF: + if ( STps->rw == ST_WRITING && !(STp->device)->was_reset) + ioctl_result = osst_flush_write_buffer(STp, &SCpnt, 1); + else + ioctl_result = 0; + for (i=0; ilogical_blk_num; + if (fileno >= 0) fileno += arg; + if (blkno >= 0) blkno = 0; + goto os_bypass; + + case MTWSM: + if (STp->write_prot) + return (-EACCES); + if (!STp->raw) + return 0; + cmd[0] = WRITE_FILEMARKS; /* FIXME -- need OS version */ + if (cmd_in == MTWSM) + cmd[1] = 2; + cmd[2] = (arg >> 16); + cmd[3] = (arg >> 8); + cmd[4] = arg; + timeout = STp->timeout; +#if DEBUG + if (debugging) { + if (cmd_in == MTWEOF) + printk(OSST_DEB_MSG "osst%d: Writing %d filemarks.\n", dev, + cmd[2] * 65536 + cmd[3] * 256 + cmd[4]); + else + printk(OSST_DEB_MSG "osst%d: Writing %d setmarks.\n", dev, + cmd[2] * 65536 + cmd[3] * 256 + cmd[4]); + } +#endif + if (fileno >= 0) + fileno += arg; + blkno = 0; + at_sm = (cmd_in == MTWSM); + break; + case MTOFFL: + case MTLOAD: + case MTUNLOAD: + case MTRETEN: + cmd[0] = START_STOP; + cmd[1] = 1; /* Don't wait for completion */ + if (cmd_in == MTLOAD) + cmd[4] = 1; /* load */ + if (cmd_in == MTRETEN) + cmd[4] = 3; /* retension then mount */ + if (cmd_in == MTOFFL) + cmd[4] = 4; /* rewind then eject */ + timeout = STp->timeout; +#if DEBUG + if (debugging) { + switch (cmd_in) { + case MTUNLOAD: + printk(OSST_DEB_MSG "osst%d: Unloading tape.\n", dev); + break; + case MTLOAD: + printk(OSST_DEB_MSG "osst%d: Loading tape.\n", dev); + break; + case MTRETEN: + printk(OSST_DEB_MSG "osst%d: Retensioning tape.\n", dev); + break; + case MTOFFL: + printk(OSST_DEB_MSG "osst%d: Ejecting tape.\n", dev); + break; + } + } +#endif + fileno = blkno = at_sm = logical_blk_num = 0 ; + break; + case MTNOP: +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: No op on tape.\n", dev); +#endif + return 0; /* Should do something ? */ + break; + case MTEOM: +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: Spacing to end of recorded medium.\n", dev); +#endif + osst_set_frame_position(STp, &SCpnt, STp->eod_frame_ppos, 0); + if (osst_get_logical_blk(STp, &SCpnt, -1, 0) < 0) + return (-EIO); + if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_EOD) + return (-EIO); + ioctl_result = osst_set_frame_position(STp, &SCpnt, STp->eod_frame_ppos, 0); + logical_blk_num = STp->logical_blk_num; + fileno = STp->filemark_cnt; + blkno = at_sm = 0; + goto os_bypass; + + case MTERASE: + if (STp->write_prot) + return (-EACCES); + ioctl_result = osst_reset_header(STp, &SCpnt); + i = osst_write_eod(STp, &SCpnt); + if (i < ioctl_result) ioctl_result = i; + i = osst_position_tape_and_confirm(STp, &SCpnt, STp->eod_frame_ppos); + if (i < ioctl_result) ioctl_result = i; + fileno = blkno = at_sm = logical_blk_num = 0 ; + goto os_bypass; + + case MTREW: + cmd[0] = REZERO_UNIT; /* rewind */ + cmd[1] = 1; +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: Rewinding tape, Immed=%d.\n", dev, cmd[1]); +#endif + fileno = blkno = at_sm = logical_blk_num = 0 ; + break; + + case MTLOCK: + chg_eof = FALSE; + cmd[0] = ALLOW_MEDIUM_REMOVAL; + cmd[4] = SCSI_REMOVAL_PREVENT; +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: Locking drive door.\n", dev); +#endif; + break; + + case MTUNLOCK: + chg_eof = FALSE; + cmd[0] = ALLOW_MEDIUM_REMOVAL; + cmd[4] = SCSI_REMOVAL_ALLOW; +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: Unlocking drive door.\n", dev); +#endif; + break; + + case MTSETBLK: /* Set block length */ + case MTSETDENSITY: /* Set tape density */ + case MTSETDRVBUFFER: /* Set drive buffering */ + case SET_DENS_AND_BLK: /* Set density and block size */ + chg_eof = FALSE; + if (STp->dirty || (STp->buffer)->buffer_bytes != 0) + return (-EIO); /* Not allowed if data in buffer */ + if ((cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) && + (arg & MT_ST_BLKSIZE_MASK) != 0 && + ((arg & MT_ST_BLKSIZE_MASK) < STp->min_block || + (arg & MT_ST_BLKSIZE_MASK) > STp->max_block || + (arg & MT_ST_BLKSIZE_MASK) > osst_buffer_size)) { + printk(KERN_WARNING "osst%d: Illegal block size.\n", dev); + return (-EINVAL); + } + return 0; /* silently ignore if block size didn't change */ + + default: + return (-ENOSYS); + } + + SCpnt = osst_do_scsi(SCpnt, STp, cmd, datalen, timeout, MAX_RETRIES, TRUE); + if (!SCpnt) { +#if DEBUG + printk(OSST_DEB_MSG "osst%d: Couldn't exec scsi cmd for IOCTL\n", dev); +#endif + return (-EBUSY); + } + + ioctl_result = (STp->buffer)->last_result_fatal; + +os_bypass: +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: IOCTL (%d) Result=%d\n", dev, cmd_in, ioctl_result); +#endif + + if (!ioctl_result) { /* SCSI command successful */ + + if (cmd_in == MTFSFM) { + fileno--; + blkno--; + } + if (cmd_in == MTBSFM) { + fileno++; + blkno++; + } + STps->drv_block = blkno; + STps->drv_file = fileno; + STps->at_sm = at_sm; + STp->logical_blk_num = logical_blk_num; + + if (cmd_in == MTLOCK) + STp->door_locked = ST_LOCKED_EXPLICIT; + else if (cmd_in == MTUNLOCK) + STp->door_locked = ST_UNLOCKED; + + if (cmd_in == MTEOM) + STps->eof = ST_EOD; + else if (cmd_in == MTFSF) + STps->eof = ST_FM; + else if (chg_eof) + STps->eof = ST_NOEOF; + + if (cmd_in == MTOFFL || cmd_in == MTUNLOAD) + STp->rew_at_close = 0; + else if (cmd_in == MTLOAD) { +/* STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0; FIXME */ + for (i=0; i < ST_NBR_PARTITIONS; i++) { + STp->ps[i].rw = ST_IDLE; + STp->ps[i].last_block_valid = FALSE; + } + STp->partition = 0; + } + + if (cmd_in == MTREW) { + ioctl_result = osst_position_tape_and_confirm(STp, &SCpnt, STp->first_data_ppos); + if (ioctl_result > 0) + ioctl_result = 0; + } + + } else if (SCpnt) { /* SCSI command was not completely successful. */ + if (SCpnt->sense_buffer[2] & 0x40) { + if (cmd_in != MTBSF && cmd_in != MTBSFM && + cmd_in != MTBSR && cmd_in != MTBSS) + STps->eof = ST_EOM_OK; + STps->drv_block = 0; + } + + undone = ( + (SCpnt->sense_buffer[3] << 24) + + (SCpnt->sense_buffer[4] << 16) + + (SCpnt->sense_buffer[5] << 8) + + SCpnt->sense_buffer[6] ); + if (cmd_in == MTWEOF && + (SCpnt->sense_buffer[0] & 0x70) == 0x70 && + (SCpnt->sense_buffer[2] & 0x4f) == 0x40 && + ((SCpnt->sense_buffer[0] & 0x80) == 0 || undone == 0)) { + ioctl_result = 0; /* EOF written succesfully at EOM */ + if (fileno >= 0) + fileno++; + STps->drv_file = fileno; + STps->eof = ST_NOEOF; + } + else if ( (cmd_in == MTFSF) || (cmd_in == MTFSFM) ) { + if (fileno >= 0) + STps->drv_file = fileno - undone ; + else + STps->drv_file = fileno; + STps->drv_block = 0; + STps->eof = ST_NOEOF; + } + else if ( (cmd_in == MTBSF) || (cmd_in == MTBSFM) ) { + if (fileno >= 0) + STps->drv_file = fileno + undone ; + else + STps->drv_file = fileno; + STps->drv_block = 0; + STps->eof = ST_NOEOF; + } + else if (cmd_in == MTFSR) { + if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */ + if (STps->drv_file >= 0) + STps->drv_file++; + STps->drv_block = 0; + STps->eof = ST_FM; + } + else { + if (blkno >= undone) + STps->drv_block = blkno - undone; + else + STps->drv_block = (-1); + STps->eof = ST_NOEOF; + } + } + else if (cmd_in == MTBSR) { + if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */ + STps->drv_file--; + STps->drv_block = (-1); + } + else { + if (blkno >= 0) + STps->drv_block = blkno + undone; + else + STps->drv_block = (-1); + } + STps->eof = ST_NOEOF; + } + else if (cmd_in == MTEOM) { + STps->drv_file = (-1); + STps->drv_block = (-1); + STps->eof = ST_EOD; + } + else if (chg_eof) + STps->eof = ST_NOEOF; + + if ((SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK) + STps->eof = ST_EOD; + + if (cmd_in == MTLOCK) + STp->door_locked = ST_LOCK_FAILS; + + } + *aSCpnt = SCpnt; + + return ioctl_result; +} + + +/* Open the device */ +static int os_scsi_tape_open(struct inode * inode, struct file * filp) +{ + unsigned short flags; + int i, b_size, need_dma_buffer, new_session = FALSE; + unsigned char cmd[MAX_COMMAND_SIZE]; + Scsi_Cmnd * SCpnt; + OS_Scsi_Tape * STp; + ST_mode * STm; + ST_partstat * STps; + int dev = TAPE_NR(inode->i_rdev); + int mode = TAPE_MODE(inode->i_rdev); + + if (dev >= osst_template.dev_max || (STp = os_scsi_tapes[dev]) == NULL || !STp->device) + return (-ENXIO); + + if( !scsi_block_when_processing_errors(STp->device) ) { + return -ENXIO; + } + + if (STp->in_use) { +#if DEBUG + printk(OSST_DEB_MSG "osst%d: Device already in use.\n", dev); +#endif + return (-EBUSY); + } + STp->in_use = 1; + STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0; + + if (STp->device->host->hostt->module) + __MOD_INC_USE_COUNT(STp->device->host->hostt->module); + if (osst_template.module) + __MOD_INC_USE_COUNT(osst_template.module); + + if (mode != STp->current_mode) { +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: Mode change from %d to %d.\n", + dev, STp->current_mode, mode); +#endif + new_session = TRUE; + STp->current_mode = mode; + } + STm = &(STp->modes[STp->current_mode]); + + flags = filp->f_flags; + STp->write_prot = ((flags & O_ACCMODE) == O_RDONLY); + + STp->raw = (MINOR(inode->i_rdev) & 0x40) != 0; + + /* Allocate a buffer for this user */ + need_dma_buffer = STp->restr_dma; + for (i=0; i < osst_nbr_buffers; i++) + if (!osst_buffers[i]->in_use && + (!need_dma_buffer || osst_buffers[i]->dma)) + break; + if (i >= osst_nbr_buffers) { + STp->buffer = new_tape_buffer(FALSE, need_dma_buffer); + if (STp->buffer == NULL) { + printk(KERN_WARNING "osst%d: Can't allocate tape buffer.\n", dev); + STp->in_use = 0; + if (STp->device->host->hostt->module) + __MOD_DEC_USE_COUNT(STp->device->host->hostt->module); + if(osst_template.module) + __MOD_DEC_USE_COUNT(osst_template.module); + return (-EBUSY); + } + } + else + STp->buffer = osst_buffers[i]; + (STp->buffer)->in_use = 1; + (STp->buffer)->writing = 0; + (STp->buffer)->last_result_fatal = 0; + (STp->buffer)->use_sg = STp->device->host->sg_tablesize; + + /* Compute the usable buffer size for this SCSI adapter */ + if (!(STp->buffer)->use_sg) + (STp->buffer)->buffer_size = (STp->buffer)->sg[0].length; + else { + for (i=0, (STp->buffer)->buffer_size = 0; i < (STp->buffer)->use_sg && + i < (STp->buffer)->sg_segs; i++) + (STp->buffer)->buffer_size += (STp->buffer)->sg[i].length; + } + + STp->dirty = 0; + for (i=0; i < ST_NBR_PARTITIONS; i++) { + STps = &(STp->ps[i]); + STps->rw = ST_IDLE; + } + STp->ready = ST_READY; + STp->recover_count = 0; +#if DEBUG + STp->nbr_waits = STp->nbr_finished = 0; +#endif + memset (cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = TEST_UNIT_READY; + + SCpnt = osst_do_scsi(NULL, STp, cmd, 0, STp->timeout, MAX_READY_RETRIES, TRUE); + if (!SCpnt) { + STp->buffer->in_use = 0; + STp->buffer = NULL; + STp->in_use = 0; + + if (STp->device->host->hostt->module) + __MOD_DEC_USE_COUNT(STp->device->host->hostt->module); + if (osst_template.module) + __MOD_DEC_USE_COUNT(osst_template.module); + return (-EBUSY); + } + if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && + (SCpnt->sense_buffer[2] & 0x0f) == NOT_READY && + SCpnt->sense_buffer[12] == 4 ) { +#if DEBUG + printk(OSST_DEB_MSG "osst%d: Unit not ready, cause %x\n", dev, SCpnt->sense_buffer[13]); +#endif + if (SCpnt->sense_buffer[13] == 2) { /* initialize command required (LOAD) */ + memset (cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = START_STOP; + cmd[1] = 1; + cmd[4] = 1; + SCpnt = osst_do_scsi(SCpnt, STp, cmd, 0, STp->timeout, MAX_READY_RETRIES, TRUE); + } + osst_wait_ready(STp, &SCpnt, (SCpnt->sense_buffer[13]==1?15:3) * 60); + } + if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && + (SCpnt->sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */ +#if DEBUG + printk(OSST_DEB_MSG "osst%d: Unit wants attention\n", dev); +#endif + STp->header_ok = 0; + + for (i=0; i < 10; i++) { + + memset (cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = TEST_UNIT_READY; + + SCpnt = osst_do_scsi(SCpnt, STp, cmd, 0, STp->timeout, MAX_READY_RETRIES, TRUE); + if ((SCpnt->sense_buffer[0] & 0x70) != 0x70 || + (SCpnt->sense_buffer[2] & 0x0f) != UNIT_ATTENTION) + break; + } + + STp->device->was_reset = 0; + STp->partition = STp->new_partition = 0; + if (STp->can_partitions) + STp->nbr_partitions = 1; /* This guess will be updated later if necessary */ + for (i=0; i < ST_NBR_PARTITIONS; i++) { + STps = &(STp->ps[i]); + STps->rw = ST_IDLE; + STps->eof = ST_NOEOF; + STps->at_sm = 0; + STps->last_block_valid = FALSE; + STps->drv_block = 0; + STps->drv_file = 0 ; + } + new_session = TRUE; + } + /* + * if we have valid headers from before, and the drive/tape seem untouched, + * open without reconfiguring and re-reading the headers + */ + if (!STp->buffer->last_result_fatal && STp->header_ok && + !SCpnt->result && SCpnt->sense_buffer[0] == 0 ) { + + memset(cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = MODE_SENSE; + cmd[1] = 8; + cmd[2] = VENDOR_IDENT_PAGE; + cmd[4] = VENDOR_IDENT_PAGE_LENGTH + MODE_HEADER_LENGTH; + + SCpnt = osst_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE); + + if (STp->buffer->last_result_fatal || + STp->buffer->b_data[MODE_HEADER_LENGTH + 2] != 'L' || + STp->buffer->b_data[MODE_HEADER_LENGTH + 3] != 'I' || + STp->buffer->b_data[MODE_HEADER_LENGTH + 4] != 'N' || + STp->buffer->b_data[MODE_HEADER_LENGTH + 5] != '4' ) { +#if DEBUG + printk(OSST_DEB_MSG "osst%d: signature was changed to %c%c%c%c\n", dev, + STp->buffer->b_data[MODE_HEADER_LENGTH + 2], + STp->buffer->b_data[MODE_HEADER_LENGTH + 3], + STp->buffer->b_data[MODE_HEADER_LENGTH + 4], + STp->buffer->b_data[MODE_HEADER_LENGTH + 5]); +#endif + STp->header_ok = 0; + } + i = STp->first_frame_position; + if (STp->header_ok && i == osst_get_frame_position(STp, &SCpnt)) { + if (STp->door_locked == ST_UNLOCKED) { + if (osst_int_ioctl(STp, &SCpnt, MTLOCK, 0)) + printk(KERN_WARNING "osst%d: Can't lock drive door\n", dev); + else + STp->door_locked = ST_LOCKED_AUTO; + } + STp->fast_open = TRUE; + scsi_release_command(SCpnt); + return 0; + } +#if DEBUG + if (i != STp->first_frame_position) + printk(OSST_DEB_MSG "osst%d: tape position changed from %d to %d\n", dev, i, STp->first_frame_position); +#endif + STp->header_ok = 0; + } + STp->fast_open = FALSE; + + if ((STp->buffer)->last_result_fatal != 0 && /* in all error conditions except no medium */ + (SCpnt->sense_buffer[2] != 2 || SCpnt->sense_buffer[12] != 0x3A) ) { + + memset(cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = MODE_SELECT; + cmd[1] = 0x10; + cmd[4] = 4 + MODE_HEADER_LENGTH; + + (STp->buffer)->b_data[0] = cmd[4] - 1; + (STp->buffer)->b_data[1] = 0; /* Medium Type - ignoring */ + (STp->buffer)->b_data[2] = 0; /* Reserved */ + (STp->buffer)->b_data[3] = 0; /* Block Descriptor Length */ + (STp->buffer)->b_data[MODE_HEADER_LENGTH + 0] = 0x3f; + (STp->buffer)->b_data[MODE_HEADER_LENGTH + 1] = 1; + (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] = 2; + (STp->buffer)->b_data[MODE_HEADER_LENGTH + 3] = 3; + +#if 1 //DEBUG + printk(OSST_DEB_MSG "osst%i: Applying soft reset\n", dev); +#endif + SCpnt = osst_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE); + + STp->header_ok = 0; + + for (i=0; i < 10; i++) { + + memset (cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = TEST_UNIT_READY; + + SCpnt = osst_do_scsi(SCpnt, STp, cmd, 0, STp->timeout, MAX_READY_RETRIES, TRUE); + if ((SCpnt->sense_buffer[0] & 0x70) != 0x70 || + (SCpnt->sense_buffer[2] & 0x0f) == NOT_READY) + break; + + if ((SCpnt->sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { + STp->device->was_reset = 0; + STp->partition = STp->new_partition = 0; + if (STp->can_partitions) + STp->nbr_partitions = 1; /* This guess will be updated later if necessary */ + for (i=0; i < ST_NBR_PARTITIONS; i++) { + STps = &(STp->ps[i]); + STps->rw = ST_IDLE; + STps->eof = ST_NOEOF; + STps->at_sm = 0; + STps->last_block_valid = FALSE; + STps->drv_block = 0; + STps->drv_file = 0 ; + } + new_session = TRUE; + } + } + } + + if (osst_wait_ready(STp, &SCpnt, 3 * 60)) /* FIXME - not allowed with NOBLOCK */ + printk(KERN_WARNING "osst%i: Device did not become Ready in open\n",dev); + + if ((STp->buffer)->last_result_fatal != 0) { + if ((STp->device)->scsi_level >= SCSI_2 && + (SCpnt->sense_buffer[0] & 0x70) == 0x70 && + (SCpnt->sense_buffer[2] & 0x0f) == NOT_READY && + SCpnt->sense_buffer[12] == 0x3a) { /* Check ASC */ + STp->ready = ST_NO_TAPE; + } else + STp->ready = ST_NOT_READY; + scsi_release_command(SCpnt); + SCpnt = NULL; + STp->density = 0; /* Clear the erroneous "residue" */ + STp->write_prot = 0; + STp->block_size = 0; + STp->ps[0].drv_file = STp->ps[0].drv_block = (-1); + STp->partition = STp->new_partition = 0; + STp->door_locked = ST_UNLOCKED; + return 0; + } + + STp->min_block = STp->max_block = (-1); + + osst_configure_onstream(STp, &SCpnt); + +/* STp->drv_write_prot = ((STp->buffer)->b_data[2] & 0x80) != 0; FIXME */ + + if (OS_FRAME_SIZE > (STp->buffer)->buffer_size && + !enlarge_buffer(STp->buffer, OS_FRAME_SIZE, STp->restr_dma)) { + printk(KERN_NOTICE "osst%d: Framesize %d too large for buffer.\n", dev, + OS_FRAME_SIZE); + scsi_release_command(SCpnt); + SCpnt = NULL; + STp->buffer->in_use = 0; + STp->buffer = NULL; + if (STp->device->host->hostt->module) + __MOD_DEC_USE_COUNT(STp->device->host->hostt->module); + if (osst_template.module) + __MOD_DEC_USE_COUNT(osst_template.module); + return (-EIO); + } + + if ((STp->buffer)->buffer_size >= OS_FRAME_SIZE) + { + for (i = 0, b_size = 0; + i < STp->buffer->sg_segs && (b_size + STp->buffer->sg[i].length) <= OS_DATA_SIZE; + b_size += STp->buffer->sg[i++].length); + STp->buffer->aux = (os_aux_t *) (STp->buffer->sg[i].address + OS_DATA_SIZE - b_size); +#if DEBUG + printk(OSST_DEB_MSG "osst%d: b_data points to %p in segment 0 at %p\n", dev, + STp->buffer->b_data, STp->buffer->sg[0].address); + printk(OSST_DEB_MSG "osst%d: AUX points to %p in segment %d at %p\n", dev, + STp->buffer->aux, i, STp->buffer->sg[i].address); +#endif + } else + STp->buffer->aux = NULL; /* this had better never happen! */ + + (STp->buffer)->buffer_blocks = 1; + (STp->buffer)->buffer_bytes = + (STp->buffer)->read_pointer = + STp->logical_blk_in_buffer = 0; + +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: Block size: %d, frame size: %d, buffer size: %d (%d blocks).\n", + dev, STp->block_size, OS_FRAME_SIZE, (STp->buffer)->buffer_size, + (STp->buffer)->buffer_blocks); +#endif + + if (STp->drv_write_prot) { + STp->write_prot = 1; +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: Write protected\n", dev); +#endif + if ((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR) { + (STp->buffer)->in_use = 0; + STp->buffer = NULL; + STp->in_use = 0; + STp->in_use = 0; + if (STp->device->host->hostt->module) + __MOD_DEC_USE_COUNT(STp->device->host->hostt->module); + if(osst_template.module) + __MOD_DEC_USE_COUNT(osst_template.module); + scsi_release_command(SCpnt); + SCpnt = NULL; + return (-EROFS); + } + } + + if (new_session) { /* Change the drive parameters for the new mode */ +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: New Session\n", dev); +#endif + STp->density_changed = STp->blksize_changed = FALSE; + STp->compression_changed = FALSE; + } + + /* + * properly position the tape and check the ADR headers + */ + if (STp->door_locked == ST_UNLOCKED) { + if (osst_int_ioctl(STp, &SCpnt, MTLOCK, 0)) + printk(KERN_WARNING "osst%d: Can't lock drive door\n", dev); + else + STp->door_locked = ST_LOCKED_AUTO; + } + + osst_analyze_headers(STp, &SCpnt); + + scsi_release_command(SCpnt); + SCpnt = NULL; + + return 0; +} + + +/* Flush the tape buffer before close */ +static int os_scsi_tape_flush(struct file * filp) +{ + int result = 0, result2; + OS_Scsi_Tape * STp; + ST_mode * STm; + ST_partstat * STps; + Scsi_Cmnd *SCpnt = NULL; + + struct inode *inode = filp->f_dentry->d_inode; + kdev_t devt = inode->i_rdev; + int dev; + + if (filp->f_count > 1) + return 0; + + dev = TAPE_NR(devt); + STp = os_scsi_tapes[dev]; + STm = &(STp->modes[STp->current_mode]); + STps = &(STp->ps[STp->partition]); + + if ( STps->rw == ST_WRITING && !(STp->device)->was_reset) { + result = osst_flush_write_buffer(STp, &SCpnt, 1); + if (result != 0 && result != (-ENOSPC)) + goto out; + } + + if ( STps->rw == ST_WRITING && !(STp->device)->was_reset) { + +#if DEBUG + if (debugging) { + printk(OSST_DEB_MSG "osst%d: File length %ld bytes.\n", + dev, (long)(filp->f_pos)); + printk(OSST_DEB_MSG "osst%d: Async write waits %d, finished %d.\n", + dev, STp->nbr_waits, STp->nbr_finished); + } +#endif + + result = osst_flush_drive_buffer(STp, &SCpnt); + if (result < 0) goto out; + result = osst_write_filemark(STp, &SCpnt); + if (result < 0) goto out; + + if (STps->drv_file >= 0) + STps->drv_file++ ; + STps->drv_block = 0; + + result = osst_write_eod(STp, &SCpnt); + osst_write_header(STp, &SCpnt, !(STp->rew_at_close)); + + STps->eof = ST_FM; + +#if DEBUG + if (debugging) + printk(OSST_DEB_MSG "osst%d: Buffer flushed, %d EOF(s) written\n", + dev, 1+STp->two_fm); +#endif + } + else if (!STp->rew_at_close) { + STps = &(STp->ps[STp->partition]); + if (!STm->sysv || STps->rw != ST_READING) { + if (STp->can_bsr) + result = osst_flush_buffer(STp, &SCpnt, 0); + else if (STps->eof == ST_FM_HIT) { + result = cross_eof(STp, &SCpnt, FALSE); + if (result) { + if (STps->drv_file >= 0) + STps->drv_file++; + STps->drv_block = 0; + STps->eof = ST_FM; + } + else + STps->eof = ST_NOEOF; + } + } + else if ((STps->eof == ST_NOEOF && + !(result = cross_eof(STp, &SCpnt, TRUE))) || + STps->eof == ST_FM_HIT) { + if (STps->drv_file >= 0) + STps->drv_file++; + STps->drv_block = 0; + STps->eof = ST_FM; + } + } + +out: + if (STp->rew_at_close) { + result2 = osst_position_tape_and_confirm(STp, &SCpnt, STp->first_data_ppos); + STps->drv_file = STps->drv_block = STp->logical_blk_num = 0; + if (result == 0) + result = result2; + } + if (SCpnt) scsi_release_command(SCpnt); + + return result; +} + + +/* Close the device and release it */ +static int os_scsi_tape_close(struct inode * inode, struct file * filp) +{ + int result = 0; + OS_Scsi_Tape * STp; + Scsi_Cmnd *SCpnt = NULL; + + kdev_t devt = inode->i_rdev; + int dev; + + dev = TAPE_NR(devt); + STp = os_scsi_tapes[dev]; + + if (STp->door_locked == ST_LOCKED_AUTO) + osst_int_ioctl(STp, &SCpnt, MTUNLOCK, 0); + if (SCpnt) scsi_release_command(SCpnt); + + if (STp->buffer != 0) + STp->buffer->in_use = 0; + + STp->in_use = 0; + if (STp->device->host->hostt->module) + __MOD_DEC_USE_COUNT(STp->device->host->hostt->module); + if(osst_template.module) + __MOD_DEC_USE_COUNT(osst_template.module); + + return result; +} + + +/* The ioctl command */ +static int osst_ioctl(struct inode * inode,struct file * file, + unsigned int cmd_in, unsigned long arg) +{ + int i, cmd_nr, cmd_type; + unsigned int blk; + struct mtop mtc; + struct mtpos mt_pos; + OS_Scsi_Tape *STp; + ST_mode *STm; + ST_partstat *STps; + Scsi_Cmnd *SCpnt = NULL; + int dev = TAPE_NR(inode->i_rdev); + + STp = os_scsi_tapes[dev]; +#if DEBUG + if (debugging && !STp->in_use) { + printk(OSST_DEB_MSG "osst%d: Incorrect device.\n", dev); + return (-EIO); + } +#endif + STm = &(STp->modes[STp->current_mode]); + STps = &(STp->ps[STp->partition]); + + /* + * If we are in the middle of error recovery, don't let anyone + * else try and use this device. Also, if error recovery fails, it + * may try and take the device offline, in which case all further + * access to the device is prohibited. + */ + if( !scsi_block_when_processing_errors(STp->device) ) { + return -ENXIO; + } + + cmd_type = _IOC_TYPE(cmd_in); + cmd_nr = _IOC_NR(cmd_in); + + if (cmd_type == _IOC_TYPE(MTIOCTOP) && cmd_nr == _IOC_NR(MTIOCTOP)) { + if (_IOC_SIZE(cmd_in) != sizeof(mtc)) + return (-EINVAL); + + i = copy_from_user((char *) &mtc, (char *)arg, sizeof(struct mtop)); + if (i) + return (-EFAULT); + + if (mtc.mt_op == MTSETDRVBUFFER && !capable(CAP_SYS_ADMIN)) { + printk(KERN_WARNING "osst%d: MTSETDRVBUFFER only allowed for root.\n", dev); + return (-EPERM); + } + if (!STm->defined && + (mtc.mt_op != MTSETDRVBUFFER && (mtc.mt_count & MT_ST_OPTIONS) == 0)) + return (-ENXIO); + + if (!(STp->device)->was_reset) { + + if (STps->eof == ST_FM_HIT) { + if (mtc.mt_op == MTFSF || mtc.mt_op == MTFSFM|| mtc.mt_op == MTEOM) { + mtc.mt_count -= 1; + if (STps->drv_file >= 0) + STps->drv_file += 1; + } + else if (mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM) { + mtc.mt_count += 1; + if (STps->drv_file >= 0) + STps->drv_file += 1; + } + } + + if (mtc.mt_op == MTSEEK) { + /* Old position must be restored if partition will be changed */ + i = !STp->can_partitions || + (STp->new_partition != STp->partition); + } + else { + i = mtc.mt_op == MTREW || mtc.mt_op == MTOFFL || + mtc.mt_op == MTRETEN || mtc.mt_op == MTEOM || + mtc.mt_op == MTLOCK || mtc.mt_op == MTLOAD || + mtc.mt_op == MTCOMPRESSION; + } + i = osst_flush_buffer(STp, &SCpnt, i); + if (SCpnt) scsi_release_command(SCpnt); + if (i < 0) + return i; + } + else { + /* + * If there was a bus reset, block further access + * to this device. If the user wants to rewind the tape, + * then reset the flag and allow access again. + */ + if(mtc.mt_op != MTREW && + mtc.mt_op != MTOFFL && + mtc.mt_op != MTRETEN && + mtc.mt_op != MTERASE && + mtc.mt_op != MTSEEK && + mtc.mt_op != MTEOM) + return (-EIO); + STp->device->was_reset = 0; + if (STp->door_locked != ST_UNLOCKED && + STp->door_locked != ST_LOCK_FAILS) { + if (osst_int_ioctl(STp, &SCpnt, MTLOCK, 0)) { + printk(KERN_NOTICE "osst%d: Could not relock door after bus reset.\n", + dev); + STp->door_locked = ST_UNLOCKED; + } + } + } + + if (mtc.mt_op != MTNOP && mtc.mt_op != MTSETBLK && + mtc.mt_op != MTSETDENSITY && mtc.mt_op != MTWSM && + mtc.mt_op != MTSETDRVBUFFER && mtc.mt_op != MTSETPART) + STps->rw = ST_IDLE; /* Prevent automatic WEOF and fsf */ + + if (mtc.mt_op == MTOFFL && STp->door_locked != ST_UNLOCKED) + osst_int_ioctl(STp, &SCpnt, MTUNLOCK, 0); /* Ignore result! */ + if (SCpnt) scsi_release_command(SCpnt); + + if (mtc.mt_op == MTSETDRVBUFFER && + (mtc.mt_count & MT_ST_OPTIONS) != 0) + return osst_set_options(STp, mtc.mt_count); + if (mtc.mt_op == MTSETPART) { +/* if (!STp->can_partitions || + mtc.mt_count < 0 || mtc.mt_count >= ST_NBR_PARTITIONS) + return (-EINVAL); + if (mtc.mt_count >= STp->nbr_partitions && + (STp->nbr_partitions = nbr_partitions(inode)) < 0) + return (-EIO);*/ + if (mtc.mt_count >= STp->nbr_partitions) + return (-EINVAL); + STp->new_partition = mtc.mt_count; + return 0; + } + if (mtc.mt_op == MTMKPART) { + if (!STp->can_partitions) + return (-EINVAL); + if ((i = osst_int_ioctl(STp, &SCpnt, MTREW, 0)) < 0 /*|| + (i = partition_tape(inode, mtc.mt_count)) < 0*/) { + if (SCpnt) scsi_release_command(SCpnt); + return i; + } + for (i=0; i < ST_NBR_PARTITIONS; i++) { + STp->ps[i].rw = ST_IDLE; + STp->ps[i].at_sm = 0; + STp->ps[i].last_block_valid = FALSE; + } + STp->partition = STp->new_partition = 0; + STp->nbr_partitions = 1; /* Bad guess ?-) */ + STps->drv_block = STps->drv_file = 0; + return 0; + } + if (mtc.mt_op == MTSEEK) { + i = osst_seek_frame(STp, &SCpnt, mtc.mt_count); + if (SCpnt) scsi_release_command(SCpnt); + if (!STp->can_partitions) + STp->ps[0].rw = ST_IDLE; + return i; + } +/* if (STp->can_partitions && STp->ready == ST_READY && + (i = update_partition(inode)) < 0) + return i;*/ + if (mtc.mt_op == MTCOMPRESSION) + return (-EINVAL)/*osst_compression(STp, (mtc.mt_count & 1))*/; + else { + i = osst_int_ioctl(STp, &SCpnt, mtc.mt_op, mtc.mt_count); + if (SCpnt) scsi_release_command(SCpnt); + return i; + } + } + + if (!STm->defined) + return (-ENXIO); + + i = osst_flush_buffer(STp, &SCpnt, FALSE); + if (SCpnt) scsi_release_command(SCpnt); + if (i < 0) + return i; +/* if (STp->can_partitions && + (i = update_partition(inode)) < 0) + return i;*/ + + if (cmd_type == _IOC_TYPE(MTIOCGET) && cmd_nr == _IOC_NR(MTIOCGET)) { + struct mtget mt_status; + + if (_IOC_SIZE(cmd_in) != sizeof(struct mtget)) + return (-EINVAL); + + mt_status.mt_type = MT_ISONSTREAM_SC; + mt_status.mt_erreg = STp->recover_erreg << MT_ST_SOFTERR_SHIFT; + mt_status.mt_dsreg = + ((STp->block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK) | + ((STp->density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK); + mt_status.mt_blkno = STps->drv_block; + mt_status.mt_fileno = STps->drv_file; + if (STp->block_size != 0) { + if (STps->rw == ST_WRITING) + mt_status.mt_blkno += + (STp->buffer)->buffer_bytes / STp->block_size; + else if (STps->rw == ST_READING) + mt_status.mt_blkno -= ((STp->buffer)->buffer_bytes + + STp->block_size - 1) / STp->block_size; + } + + mt_status.mt_gstat = 0; + if (STp->drv_write_prot) + mt_status.mt_gstat |= GMT_WR_PROT(0xffffffff); + if (mt_status.mt_blkno == 0) { + if (mt_status.mt_fileno == 0) + mt_status.mt_gstat |= GMT_BOT(0xffffffff); + else + mt_status.mt_gstat |= GMT_EOF(0xffffffff); + } + mt_status.mt_resid = STp->partition; + if (STps->eof == ST_EOM_OK || STps->eof == ST_EOM_ERROR) + mt_status.mt_gstat |= GMT_EOT(0xffffffff); + else if (STps->eof >= ST_EOM_OK) + mt_status.mt_gstat |= GMT_EOD(0xffffffff); + if (STp->density == 1) + mt_status.mt_gstat |= GMT_D_800(0xffffffff); + else if (STp->density == 2) + mt_status.mt_gstat |= GMT_D_1600(0xffffffff); + else if (STp->density == 3) + mt_status.mt_gstat |= GMT_D_6250(0xffffffff); + if (STp->ready == ST_READY) + mt_status.mt_gstat |= GMT_ONLINE(0xffffffff); + if (STp->ready == ST_NO_TAPE) + mt_status.mt_gstat |= GMT_DR_OPEN(0xffffffff); + if (STps->at_sm) + mt_status.mt_gstat |= GMT_SM(0xffffffff); + if (STm->do_async_writes || (STm->do_buffer_writes && STp->block_size != 0) || + STp->drv_buffer != 0) + mt_status.mt_gstat |= GMT_IM_REP_EN(0xffffffff); + + i = copy_to_user((char *)arg, (char *)&mt_status, + sizeof(struct mtget)); + if (i) + return (-EFAULT); + + STp->recover_erreg = 0; /* Clear after read */ + return 0; + } /* End of MTIOCGET */ + + if (cmd_type == _IOC_TYPE(MTIOCPOS) && cmd_nr == _IOC_NR(MTIOCPOS)) { + if (_IOC_SIZE(cmd_in) != sizeof(struct mtpos)) + return (-EINVAL); + blk = osst_get_frame_position(STp, &SCpnt); + if (SCpnt) scsi_release_command(SCpnt); + if (blk < 0) + return blk; + mt_pos.mt_blkno = blk; + i = copy_to_user((char *)arg, (char *) (&mt_pos), sizeof(struct mtpos)); + if (i) + return (-EFAULT); + return 0; + } + + return scsi_ioctl(STp->device, cmd_in, (void *) arg); +} + + +/* Memory handling routines */ + +/* Try to allocate a new tape buffer */ +static OSST_buffer * new_tape_buffer( int from_initialization, int need_dma ) +{ + int i, priority, b_size, order, got = 0, segs = 0; + OSST_buffer *tb; + + if (osst_nbr_buffers >= osst_template.dev_max) + return NULL; /* Should never happen */ + + if (from_initialization) + priority = GFP_ATOMIC; + else + priority = GFP_KERNEL; + + i = sizeof(OSST_buffer) + (osst_max_sg_segs - 1) * sizeof(struct scatterlist); + tb = (OSST_buffer *)kmalloc(i, priority); + if (tb) { +// tb->this_size = i; + if (need_dma) + priority |= GFP_DMA; + + /* Try to allocate the first segment up to OSST_FIRST_ORDER and the + others big enough to reach the goal */ + for (b_size = PAGE_SIZE, order = 0; + b_size < osst_buffer_size && order < OSST_FIRST_ORDER; + b_size *= 2, order++ ); + + for ( ; b_size >= PAGE_SIZE; order--, b_size /= 2) { + tb->sg[0].address = + (unsigned char *)__get_free_pages(priority, order); + if (tb->sg[0].address != NULL) { + tb->sg[0].alt_address = NULL; + tb->sg[0].length = b_size; + break; + } + } + if (tb->sg[segs].address == NULL) { + kfree(tb); + tb = NULL; + } + else { /* Got something, continue */ + + for (b_size = PAGE_SIZE, order = 0; + osst_buffer_size > tb->sg[0].length + (OSST_FIRST_SG - 1) * b_size; + b_size *= 2, order++ ); + + for (segs=1, got=tb->sg[0].length; + got < osst_buffer_size && segs < OSST_FIRST_SG; ) { + tb->sg[segs].address = + (unsigned char *)__get_free_pages(priority, order); + if (tb->sg[segs].address == NULL) { + if (osst_buffer_size - got <= + (OSST_FIRST_SG - segs) * b_size / 2) { + b_size /= 2; /* Large enough for the rest of the buffers */ + order--; + continue; + } + tb->sg_segs = segs; + tb->orig_sg_segs = 0; +#if DEBUG + tb->buffer_size = got; +#endif + normalize_buffer(tb); + kfree(tb); + tb = NULL; + break; + } + tb->sg[segs].alt_address = NULL; + tb->sg[segs].length = b_size; + got += b_size; + segs++; + } + } + } + if (!tb) { + printk(KERN_NOTICE "osst: Can't allocate new tape buffer (nbr %d).\n", + osst_nbr_buffers); + return NULL; + } + tb->sg_segs = tb->orig_sg_segs = segs; + tb->b_data = tb->sg[0].address; + +#if DEBUG + if (debugging) { + printk(OSST_DEB_MSG + "osst: Allocated tape buffer %d (%d bytes, %d segments, dma: %d, a: %p).\n", + osst_nbr_buffers, got, tb->sg_segs, need_dma, tb->b_data); + printk(OSST_DEB_MSG + "osst: segment sizes: first %d, last %d bytes.\n", + tb->sg[0].length, tb->sg[segs-1].length); + } +#endif + tb->in_use = 0; + tb->dma = need_dma; + tb->buffer_size = got; + tb->writing = 0; + osst_buffers[osst_nbr_buffers++] = tb; + + return tb; +} + + +/* Try to allocate a temporary enlarged tape buffer */ +static int enlarge_buffer(OSST_buffer *STbuffer, int new_size, int need_dma) +{ + int segs, nbr, max_segs, b_size, priority, order, got; + + normalize_buffer(STbuffer); + + max_segs = STbuffer->use_sg; + if (max_segs > osst_max_sg_segs) + max_segs = osst_max_sg_segs; + nbr = max_segs - STbuffer->sg_segs; + if (nbr <= 0) + return FALSE; + + priority = GFP_KERNEL; + if (need_dma) + priority |= GFP_DMA; + for (b_size = PAGE_SIZE, order = 0; + b_size * nbr < new_size - STbuffer->buffer_size; + b_size *= 2, order++); + + for (segs=STbuffer->sg_segs, got=STbuffer->buffer_size; + segs < max_segs && got < new_size; ) { + STbuffer->sg[segs].address = + (unsigned char *)__get_free_pages(priority, order); + if (STbuffer->sg[segs].address == NULL) { + if (new_size - got <= (max_segs - segs) * b_size / 2) { + b_size /= 2; /* Large enough for the rest of the buffers */ + order--; + continue; + } + printk(KERN_NOTICE "osst: Failed to enlarge buffer to %d bytes.\n", + new_size); +#if DEBUG + STbuffer->buffer_size = got; +#endif + normalize_buffer(STbuffer); + return FALSE; + } + STbuffer->sg[segs].alt_address = NULL; + STbuffer->sg[segs].length = b_size; + STbuffer->sg_segs += 1; + got += b_size; + STbuffer->buffer_size = got; + segs++; + } +#if DEBUG + if (debugging) { + for (nbr=0; osst_buffers[nbr] != STbuffer && nbr < osst_nbr_buffers; nbr++); + printk(OSST_DEB_MSG + "osst: Expanded tape buffer %d (%d bytes, %d->%d segments, dma: %d, a: %p).\n", + nbr, got, STbuffer->orig_sg_segs, STbuffer->sg_segs, need_dma, STbuffer->b_data); + printk(OSST_DEB_MSG + "osst: segment sizes: first %d, last %d bytes.\n", + STbuffer->sg[0].length, STbuffer->sg[segs-1].length); + } +#endif + + return TRUE; +} + + +/* Release the extra buffer */ +static void normalize_buffer(OSST_buffer *STbuffer) +{ + int i, order, b_size; + + for (i=STbuffer->orig_sg_segs; i < STbuffer->sg_segs; i++) { + + for (b_size = PAGE_SIZE, order = 0; + b_size < STbuffer->sg[i].length; + b_size *= 2, order++); + + free_pages((unsigned long)STbuffer->sg[i].address, order); + STbuffer->buffer_size -= STbuffer->sg[i].length; + } +#if DEBUG + if (debugging && STbuffer->orig_sg_segs < STbuffer->sg_segs) + printk(OSST_DEB_MSG "osst: Buffer at %p normalized to %d bytes (segs %d).\n", + STbuffer->b_data, STbuffer->buffer_size, STbuffer->sg_segs); +#endif + STbuffer->sg_segs = STbuffer->orig_sg_segs; +} + + +/* Move data from the user buffer to the tape buffer. Returns zero (success) or + negative error code. */ +static int append_to_buffer(const char *ubp, OSST_buffer *st_bp, int do_count) +{ + int i, cnt, res, offset; + + for (i=0, offset=st_bp->buffer_bytes; + i < st_bp->sg_segs && offset >= st_bp->sg[i].length; i++) + offset -= st_bp->sg[i].length; + if (i == st_bp->sg_segs) { /* Should never happen */ + printk(KERN_WARNING "osst: Append_to_buffer offset overflow.\n"); + return (-EIO); + } + for ( ; i < st_bp->sg_segs && do_count > 0; i++) { + cnt = st_bp->sg[i].length - offset < do_count ? + st_bp->sg[i].length - offset : do_count; + res = copy_from_user(st_bp->sg[i].address + offset, ubp, cnt); + if (res) + return (-EFAULT); + do_count -= cnt; + st_bp->buffer_bytes += cnt; + ubp += cnt; + offset = 0; + } + if (do_count) { /* Should never happen */ + printk(KERN_WARNING "osst: Append_to_buffer overflow (left %d).\n", + do_count); + return (-EIO); + } + return 0; +} + + +/* Move data from the tape buffer to the user buffer. Returns zero (success) or + negative error code. */ +static int from_buffer(OSST_buffer *st_bp, char *ubp, int do_count) +{ + int i, cnt, res, offset; + + for (i=0, offset=st_bp->read_pointer; + i < st_bp->sg_segs && offset >= st_bp->sg[i].length; i++) + offset -= st_bp->sg[i].length; + if (i == st_bp->sg_segs) { /* Should never happen */ + printk(KERN_WARNING "osst: From_buffer offset overflow.\n"); + return (-EIO); + } + for ( ; i < st_bp->sg_segs && do_count > 0; i++) { + cnt = st_bp->sg[i].length - offset < do_count ? + st_bp->sg[i].length - offset : do_count; + res = copy_to_user(ubp, st_bp->sg[i].address + offset, cnt); + if (res) + return (-EFAULT); + do_count -= cnt; + st_bp->buffer_bytes -= cnt; + st_bp->read_pointer += cnt; + ubp += cnt; + offset = 0; + } + if (do_count) { /* Should never happen */ + printk(KERN_WARNING "osst: From_buffer overflow (left %d).\n", + do_count); + return (-EIO); + } + return 0; +} + +/* Sets the tail of the buffer after fill point to zero. + Returns zero (success) or negative error code. */ +static int osst_zero_buffer_tail(OSST_buffer *st_bp) +{ + int i, offset, do_count, cnt; + + for (i = 0, offset = st_bp->buffer_bytes; + i < st_bp->sg_segs && offset >= st_bp->sg[i].length; i++) + offset -= st_bp->sg[i].length; + if (i == st_bp->sg_segs) { /* Should never happen */ + printk(KERN_WARNING "osst: Zero_buffer offset overflow.\n"); + return (-EIO); + } + for (do_count = OS_DATA_SIZE - st_bp->read_pointer; + i < st_bp->sg_segs && do_count > 0; i++) { + cnt = st_bp->sg[i].length - offset < do_count ? + st_bp->sg[i].length - offset : do_count ; + memset(st_bp->sg[i].address + offset, 0, cnt); + do_count -= cnt; + offset = 0; + } + if (do_count) { /* Should never happen */ + printk(KERN_WARNING "osst: Zero_buffer overflow (left %d).\n", do_count); + return (-EIO); + } + return 0; +} + +/* Copy a osst 32K block of memory into the buffer. + Returns zero (success) or negative error code. */ +static int osst_copy_to_buffer(OSST_buffer *st_bp, unsigned char *ptr) +{ + int i, cnt, do_count = OS_DATA_SIZE; + + for (i = 0; i < st_bp->sg_segs && do_count > 0; i++) { + cnt = st_bp->sg[i].length < do_count ? + st_bp->sg[i].length : do_count ; + memcpy(st_bp->sg[i].address, ptr, cnt); + do_count -= cnt; + ptr += cnt; + } + if (do_count || i != st_bp->sg_segs-1) { /* Should never happen */ + printk(KERN_WARNING "osst: Copy_to_buffer overflow (left %d at sg %d).\n", + do_count, i); + return (-EIO); + } + return 0; +} + +/* Copy a osst 32K block of memory from the buffer. + Returns zero (success) or negative error code. */ +static int osst_copy_from_buffer(OSST_buffer *st_bp, unsigned char *ptr) +{ + int i, cnt, do_count = OS_DATA_SIZE; + + for (i = 0; i < st_bp->sg_segs && do_count > 0; i++) { + cnt = st_bp->sg[i].length < do_count ? + st_bp->sg[i].length : do_count ; + memcpy(ptr, st_bp->sg[i].address, cnt); + do_count -= cnt; + ptr += cnt; + } + if (do_count || i != st_bp->sg_segs-1) { /* Should never happen */ + printk(KERN_WARNING "osst: Copy_from_buffer overflow (left %d at sg %d).\n", + do_count, i); + return (-EIO); + } + return 0; +} + + +/* Module housekeeping */ + +#ifndef MODULE +/* Set the boot options. Syntax: st=xxx,yyy + where xxx is buffer size in 1024 byte blocks and yyy is write threshold + in 1024 byte blocks. */ +__initfunc( void osst_setup(char *str, int *ints)) +{ + if (ints[0] > 0 && ints[1] > 0) + osst_buffer_size = ints[1] * ST_KILOBYTE; + if (ints[0] > 1 && ints[2] > 0) { + osst_write_threshold = ints[2] * ST_KILOBYTE; + if (osst_write_threshold > osst_buffer_size) + osst_write_threshold = osst_buffer_size; + } + if (ints[0] > 2 && ints[3] > 0) + osst_max_buffers = ints[3]; +} +#endif + + +static struct file_operations osst_fops = { + NULL, /* lseek - default */ + osst_read, /* read - general block-dev read */ + osst_write, /* write - general block-dev write */ + NULL, /* readdir - bad */ + NULL, /* select */ + osst_ioctl, /* ioctl */ + NULL, /* mmap */ + os_scsi_tape_open, /* open */ + os_scsi_tape_flush, /* flush */ + os_scsi_tape_close, /* release */ + NULL /* fsync */ +}; + + +static int osst_attach(Scsi_Device * SDp){ + OS_Scsi_Tape * tpnt; + ST_mode * STm; + ST_partstat * STps; + int i; +#ifdef CONFIG_DEVFS_FS + int mode; +#endif + + if (SDp->type != TYPE_TAPE || !OSST_SUPPORTS(SDp)) + return 1; + + if (osst_template.nr_dev >= osst_template.dev_max) { + SDp->attached--; + return 1; + } + + /* find a free minor number */ + for (i=0; os_scsi_tapes[i] && i= osst_template.dev_max) panic ("Scsi_devices corrupt (osst)"); + + /* allocate a OS_Scsi_Tape for this device */ + tpnt = (OS_Scsi_Tape *)kmalloc(sizeof(OS_Scsi_Tape), GFP_ATOMIC); + if (tpnt == NULL) { + SDp->attached--; + printk(KERN_ERR "osst: Can't allocate device descriptor.\n"); + return 1; + } + memset(tpnt, 0, sizeof(OS_Scsi_Tape)); + os_scsi_tapes[i] = tpnt; + tpnt->capacity = 0xfffff; + + /* allocate a buffer for this device */ + if (!new_tape_buffer(TRUE, TRUE)) + printk(KERN_ERR "osst: Unable to allocate a tape buffer.\n"); + +#ifdef CONFIG_DEVFS_FS + for (mode = 0; mode < ST_NBR_MODES; ++mode) { + char name[8]; + static char *formats[ST_NBR_MODES] ={"", "l", "m", "a"}; + + /* Rewind entry */ + sprintf (name, "mt%s", formats[mode]); + tpnt->de_r[mode] = + devfs_register (SDp->de, name, 0, DEVFS_FL_DEFAULT, + MAJOR_NR, i + (mode << 5), + S_IFCHR | S_IRUGO | S_IWUGO, + 0, 0, &osst_fops, NULL); + /* No-rewind entry */ + sprintf (name, "mt%sn", formats[mode]); + tpnt->de_n[mode] = + devfs_register (SDp->de, name, 0, DEVFS_FL_DEFAULT, + MAJOR_NR, i + (mode << 5) + 128, + S_IFCHR | S_IRUGO | S_IWUGO, + 0, 0, &osst_fops, NULL); + } + devfs_register_tape (tpnt->de_r[0]); +#endif + + tpnt->device = SDp; + tpnt->devt = MKDEV(MAJOR_NR, i); + tpnt->dirty = 0; + tpnt->in_use = 0; + tpnt->drv_buffer = 1; /* Try buffering if no mode sense */ + tpnt->restr_dma = (SDp->host)->unchecked_isa_dma; + tpnt->density = 0; + tpnt->do_auto_lock = OSST_AUTO_LOCK; + tpnt->can_bsr = OSST_IN_FILE_POS; + tpnt->can_partitions = 0; + tpnt->two_fm = OSST_TWO_FM; + tpnt->fast_mteom = OSST_FAST_MTEOM; + tpnt->scsi2_logical = OSST_SCSI2LOGICAL; /* FIXME */ + tpnt->write_threshold = osst_write_threshold; + tpnt->default_drvbuffer = 0xff; /* No forced buffering */ + tpnt->partition = 0; + tpnt->new_partition = 0; + tpnt->nbr_partitions = 0; + tpnt->timeout = OSST_TIMEOUT; + tpnt->long_timeout = OSST_LONG_TIMEOUT; + + /* Recognize OnStream tapes */ + printk ("osst%i: Tape driver with OnStream support osst %s\nosst%i: %s\n", + i, osst_version, i, cvsid); + /* We don't need to test for OnStream, as this has been done in detect () */ + tpnt->os_fw_rev = osst_parse_firmware_rev (SDp->rev); +#if DEBUG + printk ("osst%i: OnStream tape drive recognized, Model %s\n", i, SDp->model); +#endif + tpnt->omit_blklims = 1; + + tpnt->poll = (strncmp(SDp->model, "DI-", 3) == 0) || OSST_FW_NEED_POLL(tpnt->os_fw_rev,SDp); + tpnt->logical_blk_in_buffer = 0; + tpnt->header_ok = 0; + tpnt->linux_media = 0; + tpnt->header_cache = NULL; + + for (i=0; i < ST_NBR_MODES; i++) { + STm = &(tpnt->modes[i]); + STm->defined = FALSE; + STm->sysv = OSST_SYSV; + STm->defaults_for_writes = 0; + STm->do_async_writes = OSST_ASYNC_WRITES; + STm->do_buffer_writes = OSST_BUFFER_WRITES; + STm->do_read_ahead = OSST_READ_AHEAD; + STm->default_compression = ST_DONT_TOUCH; + STm->default_blksize = 32 * ST_KILOBYTE; /* No forced size */ + STm->default_density = (-1); /* No forced density */ + } + + for (i=0; i < ST_NBR_PARTITIONS; i++) { + STps = &(tpnt->ps[i]); + STps->rw = ST_IDLE; + STps->eof = ST_NOEOF; + STps->at_sm = 0; + STps->last_block_valid = FALSE; + STps->drv_block = (-1); + STps->drv_file = (-1); + } + + tpnt->current_mode = 0; + tpnt->modes[0].defined = TRUE; + + tpnt->density_changed = tpnt->compression_changed = + tpnt->blksize_changed = FALSE; + + osst_template.nr_dev++; + return 0; +}; + +static int osst_detect(Scsi_Device * SDp) +{ + if (SDp->type != TYPE_TAPE) return 0; + /* We are willing to drive OnStream SC-x0 as well as the + * IDE, ParPort, USB variants, if accessible by emulation + * layer (ide-scsi, usb-storage, ...) */ + if (!OSST_SUPPORTS(SDp)) return 0; + + printk(KERN_WARNING + "Detected OnStream scsi tape osst%d at scsi%d, channel %d, id %d, lun %d\n", + osst_template.dev_noticed++, + SDp->host->host_no, SDp->channel, SDp->id, SDp->lun); + + return 1; +} + +static int osst_registered = 0; + +/* Driver initialization (not __initfunc because may be called later) */ +static int osst_init() +{ + int i; + + if (osst_template.dev_noticed == 0) return 0; + + if(!osst_registered) { +#ifdef CONFIG_DEVFS_FS + if (devfs_register_chrdev(MAJOR_NR,"osst",&osst_fops)) { +#else + if (register_chrdev(MAJOR_NR,"osst",&osst_fops)) { +#endif + printk(KERN_ERR "osst: Unable to get major %d for OnStream tapes\n",MAJOR_NR); + return 1; + } + osst_registered++; + } + + if (os_scsi_tapes) return 0; + osst_template.dev_max = OSST_MAX_TAPES; + if (osst_template.dev_max > 128 / ST_NBR_MODES) + printk(KERN_INFO "osst: Only %d tapes accessible.\n", 128 / ST_NBR_MODES); + os_scsi_tapes = + (OS_Scsi_Tape **)kmalloc(osst_template.dev_max * sizeof(OS_Scsi_Tape *), + GFP_ATOMIC); + if (os_scsi_tapes == NULL) { + printk(KERN_ERR "osst: Unable to allocate array for OnStream SCSI tapes.\n"); +#ifdef CONFIG_DEVFS_FS + devfs_unregister_chrdev(MAJOR_NR, "osst"); +#else + unregister_chrdev(MAJOR_NR, "osst"); +#endif + return 1; + } + + for (i=0; i < osst_template.dev_max; ++i) os_scsi_tapes[i] = NULL; + + /* Allocate the buffer pointers */ + osst_buffers = + (OSST_buffer **)kmalloc(osst_template.dev_max * sizeof(OSST_buffer *), + GFP_ATOMIC); + if (osst_buffers == NULL) { + printk(KERN_ERR "osst: Unable to allocate tape buffer pointers.\n"); +#ifdef CONFIG_DEVFS_FS + devfs_unregister_chrdev(MAJOR_NR, "osst"); +#else + unregister_chrdev(MAJOR_NR, "osst"); +#endif + kfree(os_scsi_tapes); + return 1; + } + osst_nbr_buffers = 0; + +#if DEBUG + printk(OSST_DEB_MSG "osst: Buffer size %d bytes, write threshold %d bytes.\n", + osst_buffer_size, osst_write_threshold); +#endif + return 0; +} + + +static void osst_detach(Scsi_Device * SDp) +{ + OS_Scsi_Tape * tpnt; + int i; +#ifdef CONFIG_DEVFS_FS + int mode; +#endif + + for(i=0; idevice == SDp) { + tpnt->device = NULL; +#ifdef CONFIG_DEVFS_FS + for (mode = 0; mode < ST_NBR_MODES; ++mode) { + devfs_unregister (tpnt->de_r[mode]); + tpnt->de_r[mode] = NULL; + devfs_unregister (tpnt->de_n[mode]); + tpnt->de_n[mode] = NULL; + } +#endif + kfree(tpnt); + os_scsi_tapes[i] = NULL; + SDp->attached--; + osst_template.nr_dev--; + osst_template.dev_noticed--; + return; + } + } + return; +} + + +#ifdef MODULE + +int init_module(void) { + int result; + + if (buffer_kbs > 0) + osst_buffer_size = buffer_kbs * ST_KILOBYTE; + if (write_threshold_kbs > 0) + osst_write_threshold = write_threshold_kbs * ST_KILOBYTE; + if (osst_write_threshold > osst_buffer_size) + osst_write_threshold = osst_buffer_size; + if (max_buffers > 0) + osst_max_buffers = max_buffers; + if (max_sg_segs >= OSST_FIRST_SG) + osst_max_sg_segs = max_sg_segs; + printk(KERN_INFO "osst: bufsize %d, wrt %d, max buffers %d, s/g segs %d.\n", + osst_buffer_size, osst_write_threshold, osst_max_buffers, osst_max_sg_segs); +//printk(OSST_DEB_MSG "osst: sizeof(header) = %d (%s)\n",sizeof(os_header_t),sizeof(os_header_t)==OS_DATA_SIZE?"ok":"error"); + osst_template.module = &__this_module; + result = scsi_register_module(MODULE_SCSI_DEV, &osst_template); + if (result) + return result; + + return 0; +} + +void cleanup_module( void) +{ + int i; + OS_Scsi_Tape * STp; + + scsi_unregister_module(MODULE_SCSI_DEV, &osst_template); +#ifdef CONFIG_DEVFS_FS + devfs_unregister_chrdev(MAJOR_NR, "osst"); +#else + unregister_chrdev(MAJOR_NR, "osst"); +#endif + osst_registered--; + if(os_scsi_tapes != NULL) { + for (i=0; i < osst_template.dev_max; ++i) { + if ((STp = os_scsi_tapes[i])) { + if (STp->header_cache != NULL) vfree(STp->header_cache); + kfree(STp); + } + } + kfree(os_scsi_tapes); + + if (osst_buffers != NULL) { + for (i=0; i < osst_nbr_buffers; i++) + if (osst_buffers[i] != NULL) { + osst_buffers[i]->orig_sg_segs = 0; + normalize_buffer(osst_buffers[i]); + kfree(osst_buffers[i]); + } + + kfree(osst_buffers); + } + } + osst_template.dev_max = 0; + printk(KERN_INFO "osst: Unloaded.\n"); +} +#endif /* MODULE */ diff --git a/drivers/scsi/osst.h b/drivers/scsi/osst.h new file mode 100644 index 000000000000..eab7022b022e --- /dev/null +++ b/drivers/scsi/osst.h @@ -0,0 +1,513 @@ +/* + * $Header: /home/cvsroot/Driver/osst.h,v 1.5.2.1 2000/09/24 14:26:02 riede Exp $ + */ + +#include +#ifdef CONFIG_DEVFS_FS +#include +#endif + +/* FIXME - rename and use the following two types or delete them! + * and the types really should go to st.h anyway... + * INQUIRY packet command - Data Format (From Table 6-8 of QIC-157C) + */ +typedef struct { + unsigned device_type :5; /* Peripheral Device Type */ + unsigned reserved0_765 :3; /* Peripheral Qualifier - Reserved */ + unsigned reserved1_6t0 :7; /* Reserved */ + unsigned rmb :1; /* Removable Medium Bit */ + unsigned ansi_version :3; /* ANSI Version */ + unsigned ecma_version :3; /* ECMA Version */ + unsigned iso_version :2; /* ISO Version */ + unsigned response_format :4; /* Response Data Format */ + unsigned reserved3_45 :2; /* Reserved */ + unsigned reserved3_6 :1; /* TrmIOP - Reserved */ + unsigned reserved3_7 :1; /* AENC - Reserved */ + u8 additional_length; /* Additional Length (total_length-4) */ + u8 rsv5, rsv6, rsv7; /* Reserved */ + u8 vendor_id[8]; /* Vendor Identification */ + u8 product_id[16]; /* Product Identification */ + u8 revision_level[4]; /* Revision Level */ + u8 vendor_specific[20]; /* Vendor Specific - Optional */ + u8 reserved56t95[40]; /* Reserved - Optional */ + /* Additional information may be returned */ +} idetape_inquiry_result_t; + +/* + * READ POSITION packet command - Data Format (From Table 6-57) + */ +typedef struct { + unsigned reserved0_10 :2; /* Reserved */ + unsigned bpu :1; /* Block Position Unknown */ + unsigned reserved0_543 :3; /* Reserved */ + unsigned eop :1; /* End Of Partition */ + unsigned bop :1; /* Beginning Of Partition */ + u8 partition; /* Partition Number */ + u8 reserved2, reserved3; /* Reserved */ + u32 first_block; /* First Block Location */ + u32 last_block; /* Last Block Location (Optional) */ + u8 reserved12; /* Reserved */ + u8 blocks_in_buffer[3]; /* Blocks In Buffer - (Optional) */ + u32 bytes_in_buffer; /* Bytes In Buffer (Optional) */ +} idetape_read_position_result_t; + +/* + * Follows structures which are related to the SELECT SENSE / MODE SENSE + * packet commands. + */ +#define COMPRESSION_PAGE 0x0f +#define COMPRESSION_PAGE_LENGTH 16 + +#define CAPABILITIES_PAGE 0x2a +#define CAPABILITIES_PAGE_LENGTH 20 + +#define TAPE_PARAMTR_PAGE 0x2b +#define TAPE_PARAMTR_PAGE_LENGTH 16 + +#define NUMBER_RETRIES_PAGE 0x2f +#define NUMBER_RETRIES_PAGE_LENGTH 4 + +#define BLOCK_SIZE_PAGE 0x30 +#define BLOCK_SIZE_PAGE_LENGTH 4 + +#define BUFFER_FILLING_PAGE 0x33 +#define BUFFER_FILLING_PAGE_LENGTH + +#define VENDOR_IDENT_PAGE 0x36 +#define VENDOR_IDENT_PAGE_LENGTH 8 + +#define LOCATE_STATUS_PAGE 0x37 +#define LOCATE_STATUS_PAGE_LENGTH 0 + +#define MODE_HEADER_LENGTH 4 + + +/* + * REQUEST SENSE packet command result - Data Format. + */ +typedef struct { + unsigned error_code :7; /* Current of deferred errors */ + unsigned valid :1; /* The information field conforms to QIC-157C */ + u8 reserved1 :8; /* Segment Number - Reserved */ + unsigned sense_key :4; /* Sense Key */ + unsigned reserved2_4 :1; /* Reserved */ + unsigned ili :1; /* Incorrect Length Indicator */ + unsigned eom :1; /* End Of Medium */ + unsigned filemark :1; /* Filemark */ + u32 information __attribute__ ((packed)); + u8 asl; /* Additional sense length (n-7) */ + u32 command_specific; /* Additional command specific information */ + u8 asc; /* Additional Sense Code */ + u8 ascq; /* Additional Sense Code Qualifier */ + u8 replaceable_unit_code; /* Field Replaceable Unit Code */ + unsigned sk_specific1 :7; /* Sense Key Specific */ + unsigned sksv :1; /* Sense Key Specific information is valid */ + u8 sk_specific2; /* Sense Key Specific */ + u8 sk_specific3; /* Sense Key Specific */ + u8 pad[2]; /* Padding to 20 bytes */ +} idetape_request_sense_result_t; + +/* + * Mode Parameter Header for the MODE SENSE packet command + */ +typedef struct { + u8 mode_data_length; /* Length of the following data transfer */ + u8 medium_type; /* Medium Type */ + u8 dsp; /* Device Specific Parameter */ + u8 bdl; /* Block Descriptor Length */ +} osst_mode_parameter_header_t; + +/* + * Mode Parameter Block Descriptor the MODE SENSE packet command + * + * Support for block descriptors is optional. + */ +typedef struct { + u8 density_code; /* Medium density code */ + u8 blocks[3]; /* Number of blocks */ + u8 reserved4; /* Reserved */ + u8 length[3]; /* Block Length */ +} osst_parameter_block_descriptor_t; + +/* + * The Data Compression Page, as returned by the MODE SENSE packet command. + */ +typedef struct { + unsigned page_code :6; /* Page Code - Should be 0xf */ + unsigned reserved0 :1; /* Reserved */ + unsigned ps :1; + u8 page_length; /* Page Length - Should be 14 */ + unsigned reserved2 :6; /* Reserved */ + unsigned dcc :1; /* Data Compression Capable */ + unsigned dce :1; /* Data Compression Enable */ + unsigned reserved3 :5; /* Reserved */ + unsigned red :2; /* Report Exception on Decompression */ + unsigned dde :1; /* Data Decompression Enable */ + u32 ca; /* Compression Algorithm */ + u32 da; /* Decompression Algorithm */ + u8 reserved[4]; /* Reserved */ +} osst_data_compression_page_t; + +/* + * The Medium Partition Page, as returned by the MODE SENSE packet command. + */ +typedef struct { + unsigned page_code :6; /* Page Code - Should be 0x11 */ + unsigned reserved1_6 :1; /* Reserved */ + unsigned ps :1; + u8 page_length; /* Page Length - Should be 6 */ + u8 map; /* Maximum Additional Partitions - Should be 0 */ + u8 apd; /* Additional Partitions Defined - Should be 0 */ + unsigned reserved4_012 :3; /* Reserved */ + unsigned psum :2; /* Should be 0 */ + unsigned idp :1; /* Should be 0 */ + unsigned sdp :1; /* Should be 0 */ + unsigned fdp :1; /* Fixed Data Partitions */ + u8 mfr; /* Medium Format Recognition */ + u8 reserved[2]; /* Reserved */ +} osst_medium_partition_page_t; + +/* + * Capabilities and Mechanical Status Page + */ +typedef struct { + unsigned page_code :6; /* Page code - Should be 0x2a */ + unsigned reserved1_67 :2; + u8 page_length; /* Page Length - Should be 0x12 */ + u8 reserved2, reserved3; + unsigned ro :1; /* Read Only Mode */ + unsigned reserved4_1234 :4; + unsigned sprev :1; /* Supports SPACE in the reverse direction */ + unsigned reserved4_67 :2; + unsigned reserved5_012 :3; + unsigned efmt :1; /* Supports ERASE command initiated formatting */ + unsigned reserved5_4 :1; + unsigned qfa :1; /* Supports the QFA two partition formats */ + unsigned reserved5_67 :2; + unsigned lock :1; /* Supports locking the volume */ + unsigned locked :1; /* The volume is locked */ + unsigned prevent :1; /* The device defaults in the prevent state after power up */ + unsigned eject :1; /* The device can eject the volume */ + unsigned reserved6_45 :2; /* Reserved */ + unsigned ecc :1; /* Supports error correction */ + unsigned cmprs :1; /* Supports data compression */ + unsigned reserved7_0 :1; + unsigned blk512 :1; /* Supports 512 bytes block size */ + unsigned blk1024 :1; /* Supports 1024 bytes block size */ + unsigned reserved7_3_6 :4; + unsigned blk32768 :1; /* slowb - the device restricts the byte count for PIO */ + /* transfers for slow buffer memory ??? */ + /* Also 32768 block size in some cases */ + u16 max_speed; /* Maximum speed supported in KBps */ + u8 reserved10, reserved11; + u16 ctl; /* Continuous Transfer Limit in blocks */ + u16 speed; /* Current Speed, in KBps */ + u16 buffer_size; /* Buffer Size, in 512 bytes */ + u8 reserved18, reserved19; +} osst_capabilities_page_t; + +/* + * Block Size Page + */ +typedef struct { + unsigned page_code :6; /* Page code - Should be 0x30 */ + unsigned reserved1_6 :1; + unsigned ps :1; + u8 page_length; /* Page Length - Should be 2 */ + u8 reserved2; + unsigned play32 :1; + unsigned play32_5 :1; + unsigned reserved2_23 :2; + unsigned record32 :1; + unsigned record32_5 :1; + unsigned reserved2_6 :1; + unsigned one :1; +} osst_block_size_page_t; + +/* + * Tape Parameters Page + */ +typedef struct { + unsigned page_code :6; /* Page code - Should be 0x2b */ + unsigned reserved1_6 :1; + unsigned ps :1; + u8 reserved2; + u8 density; + u8 reserved3,reserved4; + u16 segtrk; + u16 trks; + u8 reserved5,reserved6,reserved7,reserved8,reserved9,reserved10; +} osst_tape_paramtr_page_t; + +/* OnStream definitions */ + +#define OS_CONFIG_PARTITION (0xff) +#define OS_DATA_PARTITION (0) +#define OS_PARTITION_VERSION (1) + +/* + * partition + */ +typedef struct os_partition_s { + __u8 partition_num; + __u8 par_desc_ver; + __u16 wrt_pass_cntr; + __u32 first_frame_ppos; + __u32 last_frame_ppos; + __u32 eod_frame_ppos; +} os_partition_t; + +/* + * DAT entry + */ +typedef struct os_dat_entry_s { + __u32 blk_sz; + __u16 blk_cnt; + __u8 flags; + __u8 reserved; +} os_dat_entry_t; + +/* + * DAT + */ +#define OS_DAT_FLAGS_DATA (0xc) +#define OS_DAT_FLAGS_MARK (0x1) + +typedef struct os_dat_s { + __u8 dat_sz; + __u8 reserved1; + __u8 entry_cnt; + __u8 reserved3; + os_dat_entry_t dat_list[16]; +} os_dat_t; + +/* + * Frame types + */ +#define OS_FRAME_TYPE_FILL (0) +#define OS_FRAME_TYPE_EOD (1 << 0) +#define OS_FRAME_TYPE_MARKER (1 << 1) +#define OS_FRAME_TYPE_HEADER (1 << 3) +#define OS_FRAME_TYPE_DATA (1 << 7) + +/* + * AUX + */ +typedef struct os_aux_s { + __u32 format_id; /* hardware compability AUX is based on */ + char application_sig[4]; /* driver used to write this media */ + __u32 hdwr; /* reserved */ + __u32 update_frame_cntr; /* for configuration frame */ + __u8 frame_type; + __u8 frame_type_reserved; + __u8 reserved_18_19[2]; + os_partition_t partition; + __u8 reserved_36_43[8]; + __u32 frame_seq_num; + __u32 logical_blk_num_high; + __u32 logical_blk_num; + os_dat_t dat; + __u8 reserved188_191[4]; + __u32 filemark_cnt; + __u32 phys_fm; + __u32 last_mark_ppos; + __u8 reserved204_223[20]; + + /* + * __u8 app_specific[32]; + * + * Linux specific fields: + */ + __u32 next_mark_ppos; /* when known, points to next marker */ + __u8 linux_specific[28]; + + __u8 reserved_256_511[256]; +} os_aux_t; + +#define OS_FM_TAB_MAX 1024 + +typedef struct os_fm_tab_s { + __u8 fm_part_num; + __u8 reserved_1; + __u8 fm_tab_ent_sz; + __u8 reserved_3; + __u16 fm_tab_ent_cnt; + __u8 reserved6_15[10]; + __u32 fm_tab_ent[OS_FM_TAB_MAX]; +} os_fm_tab_t; + +typedef struct os_ext_trk_ey_s { + __u8 et_part_num; + __u8 fmt; + __u16 fm_tab_off; + __u8 reserved4_7[4]; + __u32 last_hlb_hi; + __u32 last_hlb; + __u32 last_pp; + __u8 reserved20_31[12]; +} os_ext_trk_ey_t; + +typedef struct os_ext_trk_tb_s { + __u8 nr_stream_part; + __u8 reserved_1; + __u8 et_ent_sz; + __u8 reserved3_15[13]; + os_ext_trk_ey_t dat_ext_trk_ey; + os_ext_trk_ey_t qfa_ext_trk_ey; +} os_ext_trk_tb_t; + +typedef struct os_header_s { + char ident_str[8]; + __u8 major_rev; + __u8 minor_rev; + __u16 ext_trk_tb_off; + __u8 reserved12_15[4]; + __u8 pt_par_num; + __u8 pt_reserved1_3[3]; + os_partition_t partition[16]; + __u32 cfg_col_width; + __u32 dat_col_width; + __u32 qfa_col_width; + __u8 cartridge[16]; + __u8 reserved304_511[208]; + __u32 old_filemark_list[16680/4]; /* in ADR 1.4 __u8 track_table[16680] */ + os_ext_trk_tb_t ext_track_tb; + __u8 reserved17272_17735[464]; + os_fm_tab_t dat_fm_tab; + os_fm_tab_t qfa_fm_tab; + __u8 reserved25960_32767[6808]; +} os_header_t; + + +/* + * OnStream ADRL frame + */ +#define OS_FRAME_SIZE (32 * 1024 + 512) +#define OS_DATA_SIZE (32 * 1024) +#define OS_AUX_SIZE (512) +//#define OSST_MAX_SG 2 + +/* The tape buffer descriptor. */ +typedef struct { + unsigned char in_use; + unsigned char dma; /* DMA-able buffer */ + int this_size; /* allocated size of the structure */ + int buffer_size; + int buffer_blocks; + int buffer_bytes; + int read_pointer; + int writing; + int last_result; + int last_result_fatal; + Scsi_Cmnd *last_SCpnt; + unsigned char *b_data; + os_aux_t *aux; /* onstream AUX structure at end of each block */ + unsigned short use_sg; /* zero or number of segments for this adapter */ + unsigned short sg_segs; /* total number of allocated segments */ + unsigned short orig_sg_segs; /* number of segments allocated at first try */ + struct scatterlist sg[1]; /* MUST BE last item */ +} OSST_buffer; + +/* The tape drive descriptor */ +typedef struct { + kdev_t devt; + unsigned capacity; + Scsi_Device* device; + struct semaphore sem; + OSST_buffer * buffer; + + /* Drive characteristics */ + unsigned char omit_blklims; + unsigned char do_auto_lock; + unsigned char can_bsr; + unsigned char can_partitions; + unsigned char two_fm; + unsigned char fast_mteom; + unsigned char restr_dma; + unsigned char scsi2_logical; + unsigned char default_drvbuffer; /* 0xff = don't touch, value 3 bits */ + int write_threshold; + int timeout; /* timeout for normal commands */ + int long_timeout; /* timeout for commands known to take long time*/ + + /* Mode characteristics */ + ST_mode modes[ST_NBR_MODES]; + int current_mode; +#ifdef CONFIG_DEVFS_FS + devfs_handle_t de_r[ST_NBR_MODES]; /* Rewind entries */ + devfs_handle_t de_n[ST_NBR_MODES]; /* No-rewind entries */ +#endif + + /* Status variables */ + int partition; + int new_partition; + int nbr_partitions; /* zero until partition support enabled */ + ST_partstat ps[ST_NBR_PARTITIONS]; + unsigned char dirty; + unsigned char ready; + unsigned char write_prot; + unsigned char drv_write_prot; + unsigned char in_use; + unsigned char blksize_changed; + unsigned char density_changed; + unsigned char compression_changed; + unsigned char drv_buffer; + unsigned char density; + unsigned char door_locked; + unsigned char rew_at_close; + int block_size; + int min_block; + int max_block; + int recover_count; /* from tape opening */ + int recover_erreg; /* from last status call */ + /* + * OnStream specific data + */ + int os_fw_rev; /* the firmware revision * 10000 */ + unsigned char raw; /* flag OnStream raw access (32.5KB block size) */ + unsigned char poll; /* flag that this drive needs polling (IDE|firmware) */ + unsigned char logical_blk_in_buffer; /* flag that the block as per logical_blk_num + * has been read into STp->buffer and is valid */ + int logical_blk_num; /* logical block number */ + unsigned first_frame_position; /* physical frame to be transfered to/from host */ + unsigned last_frame_position; /* physical frame to be transferd to/from tape */ + int cur_frames; /* current number of frames in internal buffer */ + int max_frames; /* max number of frames in internal buffer */ + char application_sig[5]; /* application signature */ + unsigned char fast_open; /* flag that reminds us we didn't check headers at open */ + unsigned short wrt_pass_cntr; /* write pass counter */ + int update_frame_cntr; /* update frame counter */ + int onstream_write_error; /* write error recovery active */ + int header_ok; /* header frame verified ok */ + int linux_media; /* reading linux-specifc media */ + int linux_media_version; + os_header_t * header_cache; /* cache is kept for filemark positions */ + int filemark_cnt; + int first_mark_ppos; + int last_mark_ppos; + int first_data_ppos; + int eod_frame_ppos; + int eod_frame_lfa; + int write_type; /* used in write error recovery */ + unsigned long cmd_start_time; + unsigned long max_cmd_time; + +#if DEBUG + unsigned char write_pending; + int nbr_finished; + int nbr_waits; + unsigned char last_cmnd[6]; + unsigned char last_sense[16]; +#endif +} OS_Scsi_Tape; + +extern Scsi_Tape * scsi_tapes; + +/* Values of write_type */ +#define OS_WRITE_DATA 0 +#define OS_WRITE_EOD 1 +#define OS_WRITE_NEW_MARK 2 +#define OS_WRITE_LAST_MARK 3 +#define OS_WRITE_HEADER 4 +#define OS_WRITE_FILLER 5 + diff --git a/drivers/scsi/osst_detect.h b/drivers/scsi/osst_detect.h new file mode 100644 index 000000000000..be517394b472 --- /dev/null +++ b/drivers/scsi/osst_detect.h @@ -0,0 +1,5 @@ +#define OSST_SUPPORTS(SDp) (! ( memcmp (SDp->vendor, "OnStream", 8) || \ + ( memcmp (SDp->model, "SC-", 3) && \ + memcmp (SDp->model, "DI-", 3) && \ + memcmp (SDp->model, "DP-", 3) && \ + memcmp (SDp->model, "USB", 3) ) ) ) diff --git a/drivers/scsi/osst_options.h b/drivers/scsi/osst_options.h new file mode 100644 index 000000000000..7b48e1019b88 --- /dev/null +++ b/drivers/scsi/osst_options.h @@ -0,0 +1,100 @@ +/* + The compile-time configurable defaults for the Linux SCSI tape driver. + + Copyright 1995 Kai Makisara. + + Last modified: Wed Sep 2 21:24:07 1998 by root@home + + Changed (and renamed) for OnStream SCSI drives garloff@suse.de + 2000-06-21 + + $Header: /home/cvsroot/Driver/osst_options.h,v 1.4 2000/06/26 01:44:01 riede Exp $ +*/ + +#ifndef _OSST_OPTIONS_H +#define _OSST_OPTIONS_H + +/* The minimum limit for the number of SCSI tape devices is determined by + OSST_MAX_TAPES. If the number of tape devices and the "slack" defined by + OSST_EXTRA_DEVS exceeds OSST_MAX_TAPES, the large number is used. */ +#define OSST_MAX_TAPES 4 + +/* If OSST_IN_FILE_POS is nonzero, the driver positions the tape after the + record been read by the user program even if the tape has moved further + because of buffered reads. Should be set to zero to support also drives + that can't space backwards over records. NOTE: The tape will be + spaced backwards over an "accidentally" crossed filemark in any case. */ +#define OSST_IN_FILE_POS 0 + +/* The tape driver buffer size in kilobytes. */ +/* Don't change, as this is the HW blocksize */ +#define OSST_BUFFER_BLOCKS 32 + +/* The number of kilobytes of data in the buffer that triggers an + asynchronous write in fixed block mode. See also OSST_ASYNC_WRITES + below. */ +#define OSST_WRITE_THRESHOLD_BLOCKS 30 + +/* The maximum number of tape buffers the driver allocates. The number + is also constrained by the number of drives detected. Determines the + maximum number of concurrently active tape drives. */ +#define OSST_MAX_BUFFERS OSST_MAX_TAPES + +/* Maximum number of scatter/gather segments */ +/* Fit one buffer in pages and add one for the AUX header */ +#define OSST_MAX_SG (((OSST_BUFFER_BLOCKS*1024) / PAGE_SIZE) + 1) + +/* The number of scatter/gather segments to allocate at first try (must be + smaller or equal to the maximum). */ +#define OSST_FIRST_SG ((OSST_BUFFER_BLOCKS*1024) / PAGE_SIZE) + +/* The size of the first scatter/gather segments (determines the maximum block + size for SCSI adapters not supporting scatter/gather). The default is set + to try to allocate the buffer as one chunk. */ +#define OSST_FIRST_ORDER 5 + + +/* The following lines define defaults for properties that can be set + separately for each drive using the MTSTOPTIONS ioctl. */ + +/* If OSST_TWO_FM is non-zero, the driver writes two filemarks after a + file being written. Some drives can't handle two filemarks at the + end of data. */ +#define OSST_TWO_FM 0 + +/* If OSST_BUFFER_WRITES is non-zero, writes in fixed block mode are + buffered until the driver buffer is full or asynchronous write is + triggered. May make detection of End-Of-Medium early enough fail. */ +#define OSST_BUFFER_WRITES 1 + +/* If OSST_ASYNC_WRITES is non-zero, the SCSI write command may be started + without waiting for it to finish. May cause problems in multiple + tape backups. */ +#define OSST_ASYNC_WRITES 1 + +/* If OSST_READ_AHEAD is non-zero, blocks are read ahead in fixed block + mode. */ +#define OSST_READ_AHEAD 1 + +/* If OSST_AUTO_LOCK is non-zero, the drive door is locked at the first + read or write command after the device is opened. The door is opened + when the device is closed. */ +#define OSST_AUTO_LOCK 0 + +/* If OSST_FAST_MTEOM is non-zero, the MTEOM ioctl is done using the + direct SCSI command. The file number status is lost but this method + is fast with some drives. Otherwise MTEOM is done by spacing over + files and the file number status is retained. */ +#define OSST_FAST_MTEOM 0 + +/* If OSST_SCSI2LOGICAL is nonzero, the logical block addresses are used for + MTIOCPOS and MTSEEK by default. Vendor addresses are used if OSST_SCSI2LOGICAL + is zero. */ +#define OSST_SCSI2LOGICAL 0 + +/* If OSST_SYSV is non-zero, the tape behaves according to the SYS V semantics. + The default is BSD semantics. */ +#define OSST_SYSV 0 + + +#endif diff --git a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h index 21c9e8a5df88..2499719da83b 100644 --- a/drivers/scsi/scsi.h +++ b/drivers/scsi/scsi.h @@ -634,7 +634,7 @@ extern int scsi_mlqueue_insert(Scsi_Cmnd * cmd, int reason); extern int scsi_mlqueue_finish(struct Scsi_Host * host, Scsi_Device * device); -#if defined(MAJOR_NR) && (MAJOR_NR != SCSI_TAPE_MAJOR) +#if defined(MAJOR_NR) && (MAJOR_NR != SCSI_TAPE_MAJOR) && (MAJOR_NR != OSST_MAJOR) #include "hosts.h" static Scsi_Cmnd * end_scsi_request(Scsi_Cmnd * SCpnt, int uptodate, int sectors) diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 15d65f2e5b71..64aad654cdc9 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -26,6 +26,8 @@ * Modified by Jens Axboe - support DVD-RAM * transparently and loose the GHOST hack * + * Modified by Arnaldo Carvalho de Melo + * check resource allocation in sr_init and some cleanups - 2000/12/09 */ #include @@ -70,10 +72,10 @@ struct Scsi_Device_Template sr_template = { sr_finish, sr_attach, sr_detach }; -Scsi_CD *scsi_CDs = NULL; -static int *sr_sizes = NULL; +Scsi_CD *scsi_CDs; +static int *sr_sizes; -static int *sr_blocksizes = NULL; +static int *sr_blocksizes; static int sr_open(struct cdrom_device_info *, int); void get_sectorsize(int); @@ -1113,16 +1115,21 @@ static int sr_init() } if (scsi_CDs) return 0; - sr_template.dev_max = - sr_template.dev_noticed + SR_EXTRA_DEVS; - scsi_CDs = (Scsi_CD *) scsi_init_malloc(sr_template.dev_max * sizeof(Scsi_CD), GFP_ATOMIC); - memset(scsi_CDs, 0, sr_template.dev_max * sizeof(Scsi_CD)); - - sr_sizes = (int *) scsi_init_malloc(sr_template.dev_max * sizeof(int), GFP_ATOMIC); - memset(sr_sizes, 0, sr_template.dev_max * sizeof(int)); - - sr_blocksizes = (int *) scsi_init_malloc(sr_template.dev_max * - sizeof(int), GFP_ATOMIC); + sr_template.dev_max = sr_template.dev_noticed + SR_EXTRA_DEVS; + scsi_CDs = scsi_init_malloc(sr_template.dev_max * sizeof(Scsi_CD), + GFP_ATOMIC); + if (!scsi_CDs) + goto cleanup_register; + + sr_sizes = scsi_init_malloc(sr_template.dev_max * sizeof(int), + GFP_ATOMIC); + if (!sr_sizes) + goto cleanup_cds; + + sr_blocksizes = scsi_init_malloc(sr_template.dev_max * sizeof(int), + GFP_ATOMIC); + if (!sr_blocksizes) + goto cleanup_sizes; /* * These are good guesses for the time being. @@ -1131,6 +1138,18 @@ static int sr_init() sr_blocksizes[i] = 2048; blksize_size[MAJOR_NR] = sr_blocksizes; return 0; +cleanup_sizes: + scsi_init_free((char *) sr_sizes, sr_template.dev_max * sizeof(int)); + sr_sizes = NULL; +cleanup_cds: + scsi_init_free((char *) scsi_CDs, + (sr_template.dev_noticed + SR_EXTRA_DEVS) * + sizeof(Scsi_CD)); + scsi_CDs = NULL; +cleanup_register: + unregister_blkdev(MAJOR_NR, "sr"); + sr_registered--; + return 1; } void sr_finish() @@ -1241,7 +1260,7 @@ void cleanup_module(void) scsi_init_free((char *) scsi_CDs, (sr_template.dev_noticed + SR_EXTRA_DEVS) * sizeof(Scsi_CD)); - + scsi_CDs = NULL; scsi_init_free((char *) sr_sizes, sr_template.dev_max * sizeof(int)); sr_sizes = NULL; diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 87e454d91a3a..e18fa719ebb8 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -55,6 +55,8 @@ #include "constants.h" +#include "osst_detect.h" + #ifdef MODULE MODULE_PARM(buffer_kbs, "i"); MODULE_PARM(write_threshold_kbs, "i"); @@ -3407,6 +3409,18 @@ st_setup(char *str, int *ints)) #endif +/* Returns zero for drives not supported by this driver */ +static int st_supported(Scsi_Device * SDp) +{ + /* These OnStream SC drives require a special driver. The OnStream ADR* drives + are supported by this driver */ + if ( OSST_SUPPORTS(SDp) ) + return 0; + + return 1; +} + + static struct file_operations st_fops = { NULL, /* lseek - default */ st_read, /* read - general block-dev read */ @@ -3429,6 +3443,8 @@ static int st_attach(Scsi_Device * SDp){ if (SDp->type != TYPE_TAPE) return 1; + if (!st_supported(SDp)) + return 1; if (st_template.nr_dev >= st_template.dev_max) { SDp->attached--; @@ -3502,6 +3518,8 @@ static int st_attach(Scsi_Device * SDp){ static int st_detect(Scsi_Device * SDp) { if(SDp->type != TYPE_TAPE) return 0; + if (!st_supported(SDp)) + return 0; printk(KERN_WARNING "Detected scsi tape st%d at scsi%d, channel %d, id %d, lun %d\n", diff --git a/drivers/sound/cs46xx.c b/drivers/sound/cs46xx.c index 78dba73bcd9e..3ea8569fa522 100644 --- a/drivers/sound/cs46xx.c +++ b/drivers/sound/cs46xx.c @@ -2499,7 +2499,7 @@ static struct cs_card_type __initdata cards[]={ {PCI_VENDOR_ID_IBM, 0x0132, "Thinkpad 570", amp_none, clkrun_hack}, {PCI_VENDOR_ID_IBM, 0x0153, "Thinkpad 600X/A20/T20", amp_none, clkrun_hack}, {PCI_VENDOR_ID_IBM, 0x1010, "Thinkpad 600E (unsupported)", NULL, NULL}, - {0, 0, "Card without SSID set", NULL, NULL }, + {0, 0, "Card without SSID set", amp_none, NULL }, {0, 0, NULL, NULL, NULL} }; diff --git a/drivers/usb/serial/usbserial.c b/drivers/usb/serial/usbserial.c index 13944d8d93d6..506a1f702cf5 100644 --- a/drivers/usb/serial/usbserial.c +++ b/drivers/usb/serial/usbserial.c @@ -1259,7 +1259,7 @@ static void usb_serial_disconnect(struct usb_device *dev, void *ptr) static struct tty_driver serial_tty_driver = { magic: TTY_DRIVER_MAGIC, driver_name: "usb-serial", - name: "usb/tts/%d", + name: "ttyUSB", major: SERIAL_TTY_MAJOR, minor_start: 0, num: SERIAL_TTY_MINORS, diff --git a/fs/Config.in b/fs/Config.in index b3b1c4eede8f..84178f92a88f 100644 --- a/fs/Config.in +++ b/fs/Config.in @@ -115,6 +115,7 @@ comment 'Partition Types' bool 'BSD disklabel (BSD partition tables) support' CONFIG_BSD_DISKLABEL bool 'Macintosh partition map support' CONFIG_MAC_PARTITION +bool 'Minix subpartition support' CONFIG_MINIX_SUBPARTITION bool 'SMD disklabel (Sun partition tables) support' CONFIG_SMD_DISKLABEL bool 'Solaris (x86) partition table support' CONFIG_SOLARIS_X86_PARTITION if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then diff --git a/fs/file_table.c b/fs/file_table.c index ca1ed4cc1de4..98e0f52656ce 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -13,9 +13,7 @@ static kmem_cache_t *filp_cache; /* sysctl tunables... */ -int nr_files = 0; /* read only */ -int nr_free_files = 0; /* read only */ -int max_files = NR_FILE;/* tunable */ +struct files_stat_struct files_stat = {0, 0, NR_FILE}; /* Free list management, if you are here you must have f_count == 0 */ static struct file * free_filps = NULL; @@ -26,7 +24,7 @@ static void insert_file_free(struct file *file) free_filps->f_pprev = &file->f_next; free_filps = file; file->f_pprev = &free_filps; - nr_free_files++; + files_stat.nr_free_files++; } /* The list of in-use filp's must be exported (ugh...) */ @@ -72,11 +70,11 @@ struct file * get_empty_filp(void) static int old_max = 0; struct file * f; - if (nr_free_files > NR_RESERVED_FILES) { + if (files_stat.nr_free_files > NR_RESERVED_FILES) { used_one: f = free_filps; remove_filp(f); - nr_free_files--; + files_stat.nr_free_files--; new_one: memset(f, 0, sizeof(*f)); f->f_count = 1; @@ -89,23 +87,23 @@ struct file * get_empty_filp(void) /* * Use a reserved one if we're the superuser */ - if (nr_free_files && !current->euid) + if (files_stat.nr_free_files && !current->euid) goto used_one; /* * Allocate a new one if we're below the limit. */ - if (nr_files < max_files) { + if (files_stat.nr_files < files_stat.max_files) { f = kmem_cache_alloc(filp_cache, SLAB_KERNEL); if (f) { - nr_files++; + files_stat.nr_files++; goto new_one; } /* Big problems... */ printk("VFS: filp allocation failed\n"); - } else if (max_files > old_max) { - printk("VFS: file-max limit %d reached\n", max_files); - old_max = max_files; + } else if (files_stat.max_files > old_max) { + printk("VFS: file-max limit %d reached\n", files_stat.max_files); + old_max = files_stat.max_files; } return NULL; } diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c index 90ad4ee21ee4..b11d1ec8e9f5 100644 --- a/fs/isofs/dir.c +++ b/fs/isofs/dir.c @@ -67,14 +67,18 @@ struct inode_operations isofs_dir_inode_operations = NULL /* permission */ }; -static int isofs_name_translate(char * old, int len, char * new) +int isofs_name_translate(struct iso_directory_record *de, char *new, + struct inode *inode) { - int i, c; + char * old = de->name; + int len = de->name_len[0]; + int i; for (i = 0; i < len; i++) { - c = old[i]; + unsigned char c = old[i]; if (!c) break; + if (c >= 'A' && c <= 'Z') c |= 0x20; /* lower case */ @@ -101,8 +105,7 @@ int get_acorn_filename(struct iso_directory_record * de, { int std; unsigned char * chr; - int retnamlen = isofs_name_translate(de->name, - de->name_len[0], retname); + int retnamlen = isofs_name_translate(de, retname, inode); if (retnamlen == 0) return 0; std = sizeof(struct iso_directory_record) + de->name_len[0]; if (std & 1) std++; @@ -122,7 +125,9 @@ int get_acorn_filename(struct iso_directory_record * de, } /* - * This should _really_ be cleaned up some day.. + * Read directory and call the filldir() argument until either + * all entries were handled, or filldir() returns an error. + * Return 0. */ static int do_isofs_readdir(struct inode *inode, struct file *filp, void *dirent, filldir_t filldir, @@ -132,7 +137,7 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, unsigned char bufbits = ISOFS_BUFFER_BITS(inode); unsigned int block, offset; int inode_number = 0; /* Quiet GCC */ - struct buffer_head *bh; + struct buffer_head *bh = NULL; int len; int map; int high_sierra; @@ -140,78 +145,59 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, char *p = NULL; /* Quiet GCC */ struct iso_directory_record *de; - if (filp->f_pos >= inode->i_size) - return 0; - offset = filp->f_pos & (bufsize - 1); - block = isofs_bmap(inode, filp->f_pos >> bufbits); + block = filp->f_pos >> bufbits; high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra; - if (!block) - return 0; - - if (!(bh = breada(inode->i_dev, block, bufsize, filp->f_pos, inode->i_size))) - return 0; - while (filp->f_pos < inode->i_size) { int de_len; -#ifdef DEBUG - printk("Block, offset, f_pos: %x %x %x\n", - block, offset, filp->f_pos); - printk("inode->i_size = %x\n",inode->i_size); -#endif + + if (!bh) { + bh = isofs_bread(inode, bufsize, block); + if (!bh) + return 0; + } + de = (struct iso_directory_record *) (bh->b_data + offset); - if(first_de) inode_number = (block << bufbits) + (offset & (bufsize - 1)); + if (first_de) + inode_number = (bh->b_blocknr << bufbits) + offset; - /* Check boundaries and get length. by GO! */ - if (offset < bufsize) de_len = *(unsigned char *) de; -#ifdef DEBUG - if (offset < bufsize) printk("de_len = %ld\n", de_len); - else printk("Move to next sector\n"); -#endif - + de_len = *(unsigned char *) de; /* If the length byte is zero, we should move on to the next CDROM sector. If we are at the end of the directory, we kick out of the while loop. */ - if ((offset >= bufsize) || (de_len == 0) ) { + if (de_len == 0) { brelse(bh); - if (offset >= bufsize) { /*Check first. by GO!*/ - offset -= bufsize; - filp->f_pos += offset; - } else { - filp->f_pos = ((filp->f_pos & ~(ISOFS_BLOCK_SIZE - 1)) - + ISOFS_BLOCK_SIZE); - offset = 0; - } - - if (filp->f_pos >= inode->i_size) - return 0; - - block = isofs_bmap(inode, (filp->f_pos) >> bufbits); - if (!block) - return 0; - bh = breada(inode->i_dev, block, bufsize, filp->f_pos, inode->i_size); - if (!bh) - return 0; + bh = NULL; + filp->f_pos = (filp->f_pos & ~(ISOFS_BLOCK_SIZE - 1)) + + ISOFS_BLOCK_SIZE; + block = filp->f_pos >> bufbits; + offset = 0; continue; } - offset += de_len; - if (offset > bufsize) { - /* - * This would only normally happen if we had - * a buggy cdrom image. All directory - * entries should terminate with a null size - * or end exactly at the end of the sector. - */ - printk("next_offset (%x) > bufsize (%lx)\n", - offset,bufsize); - break; + offset += de_len; + + /* Make sure we have a full directory entry */ + if (offset >= bufsize) { + int slop = bufsize - offset + de_len; + memcpy(tmpde, de, slop); + offset &= bufsize - 1; + block++; + brelse(bh); + bh = NULL; + if (offset) { + bh = isofs_bread(inode, bufsize, block); + if (!bh) + return 0; + memcpy((void *) tmpde + slop, bh->b_data, offset); + } + de = tmpde; } - if(de->flags[-high_sierra] & 0x80) { + if (de->flags[-high_sierra] & 0x80) { first_de = 0; filp->f_pos += de_len; continue; @@ -250,7 +236,7 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, map = 1; if (inode->i_sb->u.isofs_sb.s_rock) { len = get_rock_ridge_filename(de, tmpname, inode); - if (len != 0) { + if (len != 0) { /* may be -1 */ p = tmpname; map = 0; } @@ -258,7 +244,7 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, if (map) { #ifdef CONFIG_JOLIET if (inode->i_sb->u.isofs_sb.s_joliet_level) { - len = get_joliet_filename(de, inode, tmpname); + len = get_joliet_filename(de, tmpname, inode); p = tmpname; } else #endif @@ -267,8 +253,7 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, p = tmpname; } else if (inode->i_sb->u.isofs_sb.s_mapping == 'n') { - len = isofs_name_translate(de->name, - de->name_len[0], tmpname); + len = isofs_name_translate(de, tmpname, inode); p = tmpname; } else { p = de->name; @@ -283,7 +268,8 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, continue; } - brelse(bh); + if (bh) + brelse(bh); return 0; } @@ -296,17 +282,17 @@ static int isofs_readdir(struct file *filp, void *dirent, filldir_t filldir) { int result; - char * tmpname; + char * page; struct iso_directory_record * tmpde; struct inode *inode = filp->f_dentry->d_inode; - tmpname = (char *) __get_free_page(GFP_KERNEL); - if (!tmpname) + page = (char *) __get_free_page(GFP_KERNEL); + if (!page) return -ENOMEM; - tmpde = (struct iso_directory_record *) (tmpname+1024); + tmpde = (struct iso_directory_record *) (page+1024); - result = do_isofs_readdir(inode, filp, dirent, filldir, tmpname, tmpde); + result = do_isofs_readdir(inode, filp, dirent, filldir, page, tmpde); - free_page((unsigned long) tmpname); + free_page((unsigned long) page); return result; } diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index e10d808dd044..ccbfeb12c01b 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -54,7 +54,7 @@ static int isofs_cmpi_ms(struct dentry *dentry, struct qstr *a, struct qstr *b); static int isofs_cmp_ms(struct dentry *dentry, struct qstr *a, struct qstr *b); #endif -void isofs_put_super(struct super_block *sb) +static void isofs_put_super(struct super_block *sb) { #ifdef CONFIG_JOLIET if (sb->u.isofs_sb.s_nls_iocharset) { @@ -72,6 +72,9 @@ void isofs_put_super(struct super_block *sb) return; } +static void isofs_read_inode(struct inode *); +static int isofs_statfs (struct super_block *, struct statfs *, int); + static struct super_operations isofs_sops = { isofs_read_inode, NULL, /* write_inode */ @@ -452,7 +455,7 @@ static unsigned int isofs_get_last_session(kdev_t dev) (unsigned long) &ms_info); set_fs(old_fs); #if 0 - printk("isofs.inode: CDROMMULTISESSION: rc=%d\n",i); + printk("isofs.inode: CDROMMULTISESSION: rc=%d\n", i); if (i==0) { printk("isofs.inode: XA disk: %s\n", ms_info.xa_flag ? "yes":"no"); @@ -474,8 +477,8 @@ static unsigned int isofs_get_last_session(kdev_t dev) * Note: a check_disk_change() has been done immediately prior * to this call, so we don't need to check again. */ -struct super_block *isofs_read_super(struct super_block *s, void *data, - int silent) +static struct super_block *isofs_read_super(struct super_block *s, void *data, + int silent) { kdev_t dev = s->s_dev; struct buffer_head * bh = NULL, *pri_bh = NULL; @@ -521,15 +524,13 @@ struct super_block *isofs_read_super(struct super_block *s, void *data, * that value. */ blocksize = get_hardblocksize(dev); - if( (blocksize != 0) - && (blocksize > opt.blocksize) ) - { + if (blocksize > opt.blocksize) { /* * Force the blocksize we are going to use to be the * hardware blocksize. */ opt.blocksize = blocksize; - } + } blocksize_bits = 0; { @@ -625,9 +626,7 @@ struct super_block *isofs_read_super(struct super_block *s, void *data, pri_bh = NULL; root_found: - brelse(pri_bh); - - if (joliet_level && opt.rock == 'n') { + if (joliet_level && (pri == NULL || opt.rock == 'n')) { /* This is the case of Joliet with the norock mount flag. * A disc with both Joliet and Rock Ridge is handled later */ @@ -724,6 +723,7 @@ root_found: * We're all done using the volume descriptor, and may need * to change the device blocksize, so release the buffer now. */ + brelse(pri_bh); brelse(bh); /* @@ -776,8 +776,9 @@ root_found: s->u.isofs_sb.s_gid = opt.gid; s->u.isofs_sb.s_utf8 = opt.utf8; /* - * It would be incredibly stupid to allow people to mark every file on the disk - * as suid, so we merely allow them to set the default permissions. + * It would be incredibly stupid to allow people to mark every file + * on the disk as suid, so we merely allow them to set the default + * permissions. */ s->u.isofs_sb.s_mode = opt.mode & 0777; @@ -880,8 +881,8 @@ out_unlock: return NULL; } -int isofs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz) -{ +static int +isofs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz) { struct statfs tmp; tmp.f_type = ISOFS_SUPER_MAGIC; @@ -896,6 +897,15 @@ int isofs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz) return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } +struct buffer_head *isofs_bread(struct inode *inode, unsigned int bufsize, + unsigned int block) +{ + unsigned int blknr = isofs_bmap(inode, block); + if (!blknr) + return NULL; + return bread(inode->i_dev, blknr, bufsize); +} + int isofs_bmap(struct inode * inode,int block) { off_t b_off, offset, size; @@ -942,10 +952,6 @@ int isofs_bmap(struct inode * inode,int block) firstext = inode->u.isofs_i.i_first_extent; size = inode->u.isofs_i.i_section_size; nextino = inode->u.isofs_i.i_next_section_ino; -#ifdef DEBUG - printk("first inode: inode=%x nextino=%x firstext=%u size=%lu\n", - inode->i_ino, nextino, firstext, size); -#endif i = 0; if (nextino) { while(b_off >= offset + size) { @@ -956,10 +962,6 @@ int isofs_bmap(struct inode * inode,int block) if(!ino) return 0; firstext = ino->u.isofs_i.i_first_extent; size = ino->u.isofs_i.i_section_size; -#ifdef DEBUG - printk("read inode: inode=%lu ino=%lu nextino=%lu firstext=%u size=%lu\n", - inode->i_ino, nextino, ino->u.isofs_i.i_next_section_ino, firstext, size); -#endif nextino = ino->u.isofs_i.i_next_section_ino; iput(ino); @@ -971,10 +973,6 @@ int isofs_bmap(struct inode * inode,int block) } } } -#ifdef DEBUG - printk("isofs_bmap: mapped inode:block %x:%d to block %lu\n", - inode->i_ino, block, (b_off - offset + firstext) >> ISOFS_BUFFER_BITS(inode)); -#endif return firstext + ((b_off - offset) >> ISOFS_BUFFER_BITS(inode)); } @@ -995,118 +993,144 @@ static inline void test_and_set_gid(gid_t *p, gid_t value) static int isofs_read_level3_size(struct inode * inode) { - unsigned long ino = inode->i_ino; + unsigned long f_pos = inode->i_ino; unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); int high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra; struct buffer_head * bh = NULL; - int block = 0; + unsigned long block, offset; int i = 0; int more_entries = 0; - void *cpnt; - struct iso_directory_record * raw_inode; + struct iso_directory_record * tmpde = NULL; inode->i_size = 0; inode->u.isofs_i.i_next_section_ino = 0; + + block = f_pos >> ISOFS_BUFFER_BITS(inode); + offset = f_pos & (bufsize-1); + do { - unsigned char *pnt; - unsigned int reclen; - int offset = (ino & (bufsize - 1)); - - cpnt = NULL; - /* Check whether to update our buffer */ - if (block != ino >> ISOFS_BUFFER_BITS(inode)) { - block = ino >> ISOFS_BUFFER_BITS(inode); + struct iso_directory_record * de; + unsigned int de_len; + + if (!bh) { brelse(bh); bh = bread(inode->i_dev, block, bufsize); if (!bh) goto out_noread; } - pnt = ((unsigned char *) bh->b_data + offset); - /* - * Note: this is invariant even if the record - * spans buffers and must be copied ... - */ - reclen = *pnt; - /* N.B. this test doesn't trigger the i++ code ... */ - if(reclen == 0) { - ino = (ino & ~(ISOFS_BLOCK_SIZE - 1)) + ISOFS_BLOCK_SIZE; + de = (struct iso_directory_record *) (bh->b_data + offset); + de_len = *(unsigned char *) de; + + if (de_len == 0) { + brelse(bh); + bh = NULL; + f_pos = (f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1); + block = f_pos >> ISOFS_BUFFER_BITS(inode); + offset = 0; continue; } - raw_inode = ((struct iso_directory_record *) pnt); - - /* Check whether the raw inode spans the buffer ... */ - if (offset + reclen > bufsize){ - int frag1 = bufsize - offset; - - cpnt = kmalloc(reclen, GFP_KERNEL); - if (cpnt == NULL) - goto out_nomem; - memcpy(cpnt, pnt, frag1); + + offset += de_len; + + /* Make sure we have a full directory entry */ + if (offset >= bufsize) { + int slop = bufsize - offset + de_len; + if (!tmpde) { + tmpde = kmalloc(256, GFP_KERNEL); + if (!tmpde) + goto out_nomem; + } + memcpy(tmpde, de, slop); + offset &= bufsize - 1; + block++; brelse(bh); - bh = bread(inode->i_dev, ++block, bufsize); - if (!bh) - goto out_noread; - offset += reclen - bufsize; - memcpy((char *)cpnt+frag1, bh->b_data, offset); - raw_inode = ((struct iso_directory_record *) cpnt); + bh = NULL; + if (offset) { + bh = bread(inode->i_dev, ++block, bufsize); + if (!bh) + goto out_noread; + memcpy((void *) tmpde + slop, bh->b_data, offset); + } + de = tmpde; } - inode->i_size += isonum_733 (raw_inode->size); - if(i == 1) inode->u.isofs_i.i_next_section_ino = ino; + inode->i_size += isonum_733 (de->size); + if(i == 1) + inode->u.isofs_i.i_next_section_ino = f_pos; + + more_entries = de->flags[-high_sierra] & 0x80; + + f_pos += de_len; - more_entries = raw_inode->flags[-high_sierra] & 0x80; - - ino += reclen; - if (cpnt) - kfree (cpnt); i++; if(i > 100) goto out_toomany; } while(more_entries); out: - brelse(bh); + if (tmpde) + kfree(tmpde); + if (bh) + brelse(bh); return 0; out_nomem: - printk(KERN_INFO "ISOFS: NoMem ISO inode %lu\n", inode->i_ino); - brelse(bh); - return 1; + if (bh) + brelse(bh); + return -ENOMEM; + out_noread: - printk(KERN_INFO "ISOFS: unable to read i-node block %d\n", block); - if (cpnt) - kfree(cpnt); - return 1; + printk(KERN_INFO "ISOFS: unable to read i-node block %lu\n", block); + if (tmpde) + kfree(tmpde); + return -EIO; + out_toomany: printk(KERN_INFO "isofs_read_level3_size: " "More than 100 file sections ?!?, aborting...\n" "isofs_read_level3_size: inode=%lu ino=%lu\n", - inode->i_ino, ino); + inode->i_ino, f_pos); goto out; } -void isofs_read_inode(struct inode * inode) +static void isofs_read_inode(struct inode * inode) { struct super_block *sb = inode->i_sb; unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); int block = inode->i_ino >> ISOFS_BUFFER_BITS(inode); int high_sierra = sb->u.isofs_sb.s_high_sierra; - struct buffer_head * bh; - struct iso_directory_record * raw_inode; - unsigned char *pnt; - int volume_seq_no, i; + struct buffer_head * bh = NULL; + struct iso_directory_record * de; + struct iso_directory_record * tmpde = NULL; + unsigned int de_len; + int volume_seq_no, i, offset; bh = bread(inode->i_dev, block, bufsize); - if (!bh) { - printk(KERN_WARNING "ISOFS: unable to read i-node block\n"); - goto fail; - } + if (!bh) + goto out_badread; - pnt = ((unsigned char *) bh->b_data - + (inode->i_ino & (bufsize - 1))); - raw_inode = ((struct iso_directory_record *) pnt); + offset = (inode->i_ino & (bufsize - 1)); + de = (struct iso_directory_record *) (bh->b_data + offset); + de_len = *(unsigned char *) de; - if (raw_inode->flags[-high_sierra] & 2) { + if (offset + de_len > bufsize){ + int frag1 = bufsize - offset; + + tmpde = kmalloc(de_len, GFP_KERNEL); + if (tmpde == NULL) { + printk(KERN_INFO "isofs_read_inode: out of memory\n"); + goto fail; + } + memcpy(tmpde, bh->b_data + offset, frag1); + brelse(bh); + bh = bread(inode->i_dev, ++block, bufsize); + if (!bh) + goto out_badread; + memcpy((char *)tmpde+frag1, bh->b_data, de_len - frag1); + de = tmpde; + } + + if (de->flags[-high_sierra] & 2) { inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR; inode->i_nlink = 1; /* Set to 1. We know there are 2, but the find utility tries to optimize @@ -1121,10 +1145,10 @@ void isofs_read_inode(struct inode * inode) /* If there are no periods in the name, * then set the execute permission bit */ - for(i=0; i< raw_inode->name_len[0]; i++) - if(raw_inode->name[i]=='.' || raw_inode->name[i]==';') + for(i=0; i< de->name_len[0]; i++) + if(de->name[i]=='.' || de->name[i]==';') break; - if(i == raw_inode->name_len[0] || raw_inode->name[i] == ';') + if(i == de->name_len[0] || de->name[i] == ';') inode->i_mode |= S_IXUGO; /* execute permission */ } inode->i_uid = inode->i_sb->u.isofs_sb.s_uid; @@ -1132,49 +1156,54 @@ void isofs_read_inode(struct inode * inode) inode->i_blocks = inode->i_blksize = 0; - inode->u.isofs_i.i_section_size = isonum_733 (raw_inode->size); - if(raw_inode->flags[-high_sierra] & 0x80) { + inode->u.isofs_i.i_section_size = isonum_733 (de->size); + if(de->flags[-high_sierra] & 0x80) { if(isofs_read_level3_size(inode)) goto fail; } else { - inode->i_size = isonum_733 (raw_inode->size); + inode->i_size = isonum_733 (de->size); } /* There are defective discs out there - we do this to protect ourselves. A cdrom will never contain more than 800Mb .. but a DVD may be up to 1Gig (Ulrich Habel) */ + if((inode->i_size < 0 || inode->i_size > 1073741824) && - inode->i_sb->u.isofs_sb.s_cruft == 'n') { - printk("Warning: defective cdrom. Enabling \"cruft\" mount option.\n"); - inode->i_sb->u.isofs_sb.s_cruft = 'y'; + inode->i_sb->u.isofs_sb.s_cruft == 'n') { + printk(KERN_WARNING "Warning: defective cdrom. " + "Enabling \"cruft\" mount option.\n"); + inode->i_sb->u.isofs_sb.s_cruft = 'y'; } -/* Some dipshit decided to store some other bit of information in the high - byte of the file length. Catch this and holler. WARNING: this will make - it impossible for a file to be > 16Mb on the CDROM!!!*/ + /* + * Some dipshit decided to store some other bit of information + * in the high byte of the file length. Catch this and holler. + * WARNING: this will make it impossible for a file to be > 16MB + * on the CDROM. + */ if(inode->i_sb->u.isofs_sb.s_cruft == 'y' && inode->i_size & 0xff000000){ -/* printk("Illegal format on cdrom. Pester manufacturer.\n"); */ - inode->i_size &= 0x00ffffff; + inode->i_size &= 0x00ffffff; } - if (raw_inode->interleave[0]) { + if (de->interleave[0]) { printk("Interleaved files not (yet) supported.\n"); inode->i_size = 0; } /* I have no idea what file_unit_size is used for, so we will flag it for now */ - if(raw_inode->file_unit_size[0] != 0){ - printk("File unit size != 0 for ISO file (%ld).\n",inode->i_ino); + if (de->file_unit_size[0] != 0){ + printk("File unit size != 0 for ISO file (%ld).\n", + inode->i_ino); } /* I have no idea what other flag bits are used for, so we will flag it for now */ #ifdef DEBUG - if((raw_inode->flags[-high_sierra] & ~2)!= 0){ + if ((de->flags[-high_sierra] & ~2)!= 0){ printk("Unusual flag settings for ISO file (%ld %x).\n", - inode->i_ino, raw_inode->flags[-high_sierra]); + inode->i_ino, de->flags[-high_sierra]); } #endif @@ -1184,32 +1213,25 @@ void isofs_read_inode(struct inode * inode) #endif inode->i_mtime = inode->i_atime = inode->i_ctime = - iso_date(raw_inode->date, high_sierra); + iso_date(de->date, high_sierra); - inode->u.isofs_i.i_first_extent = (isonum_733 (raw_inode->extent) + - isonum_711 (raw_inode->ext_attr_length)); + inode->u.isofs_i.i_first_extent = (isonum_733 (de->extent) + + isonum_711 (de->ext_attr_length)); -/* Now test for possible Rock Ridge extensions which will override some of - these numbers in the inode structure. */ + /* + * Now test for possible Rock Ridge extensions which will override + * some of these numbers in the inode structure. + */ if (!high_sierra) { - parse_rock_ridge_inode(raw_inode, inode); - /* hmm..if we want uid or gid set, override the rock ridge setting */ - test_and_set_uid(&inode->i_uid, inode->i_sb->u.isofs_sb.s_uid); - test_and_set_gid(&inode->i_gid, inode->i_sb->u.isofs_sb.s_gid); + parse_rock_ridge_inode(de, inode); + /* if we want uid/gid set, override the rock ridge setting */ + test_and_set_uid(&inode->i_uid, inode->i_sb->u.isofs_sb.s_uid); + test_and_set_gid(&inode->i_gid, inode->i_sb->u.isofs_sb.s_gid); } -#ifdef DEBUG - printk("Inode: %x extent: %x\n",inode->i_ino, inode->u.isofs_i.i_first_extent); -#endif - /* get the volume sequence number */ - volume_seq_no = isonum_723 (raw_inode->volume_sequence_number) ; - - /* - * All done with buffer ... no more references to buffer memory! - */ - brelse(bh); + volume_seq_no = isonum_723 (de->volume_sequence_number) ; /* * Disable checking if we see any volume number other than 0 or 1. @@ -1219,8 +1241,10 @@ void isofs_read_inode(struct inode * inode) */ if (inode->i_sb->u.isofs_sb.s_cruft == 'n' && (volume_seq_no != 0) && (volume_seq_no != 1)) { - printk("Warning: defective cdrom (volume sequence number). Enabling \"cruft\" mount option.\n"); - inode->i_sb->u.isofs_sb.s_cruft = 'y'; + printk("Warning: defective cdrom " + "(volume sequence number %d). " + "Enabling \"cruft\" mount option.\n", volume_seq_no); + inode->i_sb->u.isofs_sb.s_cruft = 'y'; } /* Install the inode operations vector */ @@ -1228,26 +1252,34 @@ void isofs_read_inode(struct inode * inode) #ifndef IGNORE_WRONG_MULTI_VOLUME_SPECS if (inode->i_sb->u.isofs_sb.s_cruft != 'y' && (volume_seq_no != 0) && (volume_seq_no != 1)) { - printk("Multi volume CD somehow got mounted.\n"); + printk(KERN_WARNING "Multi volume CD somehow got mounted.\n"); } else #endif IGNORE_WRONG_MULTI_VOLUME_SPECS { - if (S_ISREG(inode->i_mode)) - inode->i_op = &isofs_file_inode_operations; - else if (S_ISDIR(inode->i_mode)) - inode->i_op = &isofs_dir_inode_operations; - else if (S_ISLNK(inode->i_mode)) - inode->i_op = &isofs_symlink_inode_operations; - else if (S_ISCHR(inode->i_mode)) - inode->i_op = &chrdev_inode_operations; - else if (S_ISBLK(inode->i_mode)) - inode->i_op = &blkdev_inode_operations; - else if (S_ISFIFO(inode->i_mode)) - init_fifo(inode); + if (S_ISREG(inode->i_mode)) + inode->i_op = &isofs_file_inode_operations; + else if (S_ISDIR(inode->i_mode)) + inode->i_op = &isofs_dir_inode_operations; + else if (S_ISLNK(inode->i_mode)) + inode->i_op = &isofs_symlink_inode_operations; + else if (S_ISCHR(inode->i_mode)) + inode->i_op = &chrdev_inode_operations; + else if (S_ISBLK(inode->i_mode)) + inode->i_op = &blkdev_inode_operations; + else if (S_ISFIFO(inode->i_mode)) + init_fifo(inode); } + + out: + if (tmpde) + kfree(tmpde); + if (bh) + brelse(bh); return; - fail: + out_badread: + printk(KERN_WARNING "ISOFS: unable to read i-node block\n"); + fail: /* With a data error we return this information */ inode->i_mtime = inode->i_atime = inode->i_ctime = 0; inode->u.isofs_i.i_first_extent = 0; @@ -1257,144 +1289,7 @@ void isofs_read_inode(struct inode * inode) inode->i_uid = inode->i_gid = 0; inode->i_mode = S_IFREG; /*Regular file, no one gets to read*/ inode->i_op = NULL; - return; -} - -/* There are times when we need to know the inode number of a parent of - a particular directory. When control passes through a routine that - has access to the parent information, it fills it into the inode structure, - but sometimes the inode gets flushed out of the queue, and someone - remembers the number. When they try to open up again, we have lost - the information. The '..' entry on the disc points to the data area - for a particular inode, so we can follow these links back up, but since - we do not know the inode number, we do not actually know how large the - directory is. The disc is almost always correct, and there is - enough error checking on the drive itself, but an open ended search - makes me a little nervous. - - The BSD iso filesystem uses the extent number for an inode, and this - would work really nicely for us except that the read_inode function - would not have any clean way of finding the actual directory record - that goes with the file. If we had such info, then it would pay - to change the inode numbers and eliminate this function. -*/ - -int isofs_lookup_grandparent(struct inode * parent, int extent) -{ - unsigned long bufsize = ISOFS_BUFFER_SIZE(parent); - unsigned char bufbits = ISOFS_BUFFER_BITS(parent); - unsigned int block,offset; - int parent_dir, inode_number; - int result; - int directory_size; - struct buffer_head * bh; - struct iso_directory_record * de; - - offset = 0; - block = extent << (ISOFS_ZONE_BITS(parent) - bufbits); - if (!(bh = bread(parent->i_dev, block, bufsize))) return -1; - - while (1 == 1) { - de = (struct iso_directory_record *) (bh->b_data + offset); - if (*((unsigned char *) de) == 0) - { - brelse(bh); - printk("Directory .. not found\n"); - return -1; - } - - offset += *((unsigned char *) de); - - if (offset >= bufsize) - { - printk(".. Directory not in first block" - " of directory.\n"); - brelse(bh); - return -1; - } - - if (de->name_len[0] == 1 && de->name[0] == 1) - { - parent_dir = find_rock_ridge_relocation(de, parent); - directory_size = isonum_733 (de->size); - brelse(bh); - break; - } - } -#ifdef DEBUG - printk("Parent dir:%x\n",parent_dir); -#endif - /* Now we know the extent where the parent dir starts on. */ - - result = -1; - - offset = 0; - block = parent_dir << (ISOFS_ZONE_BITS(parent) - bufbits); - if (!block || !(bh = bread(parent->i_dev,block, bufsize))) - { - return -1; - } - - for(;;) - { - de = (struct iso_directory_record *) (bh->b_data + offset); - inode_number = (block << bufbits)+(offset & (bufsize - 1)); - - /* If the length byte is zero, we should move on to the next - CDROM sector. If we are at the end of the directory, we - kick out of the while loop. */ - - if ((*((unsigned char *) de) == 0) || (offset == bufsize) ) - { - brelse(bh); - offset = 0; - block++; - directory_size -= bufsize; - if(directory_size < 0) return -1; - if((block & 1) && (ISOFS_ZONE_BITS(parent) - bufbits) == 1) - { - return -1; - } - if((block & 3) && (ISOFS_ZONE_BITS(parent) - bufbits) == 2) - { - return -1; - } - if (!block - || !(bh = bread(parent->i_dev,block, bufsize))) - { - return -1; - } - continue; - } - - /* Make sure that the entire directory record is in the current - bh block. If not, we malloc a buffer, and put the two - halves together, so that we can cleanly read the block. */ - - offset += *((unsigned char *) de); - - if (offset > bufsize) - { - printk("Directory overrun\n"); - goto out; - } - - if (find_rock_ridge_relocation(de, parent) == extent){ - result = inode_number; - goto out; - } - - } - - /* We go here for any condition we cannot handle. - We also drop through to here at the end of the directory. */ - - out: - brelse(bh); -#ifdef DEBUG - printk("Resultant Inode %d\n",result); -#endif - return result; + goto out; } #ifdef LEAK_CHECK diff --git a/fs/isofs/joliet.c b/fs/isofs/joliet.c index 6599766c0637..00bb431ae689 100644 --- a/fs/isofs/joliet.c +++ b/fs/isofs/joliet.c @@ -19,8 +19,6 @@ uni16_to_x8(unsigned char *ascii, unsigned char *uni, int len, struct nls_table *nls) { unsigned char *ip, *op; - unsigned char ch, cl; - unsigned char *uni_page; ip = uni; op = ascii; @@ -68,8 +66,8 @@ wcsntombs_be(__u8 *s, const __u8 *pwcs, int inlen, int maxlen) } int -get_joliet_filename(struct iso_directory_record * de, struct inode * inode, - unsigned char *outname) +get_joliet_filename(struct iso_directory_record * de, unsigned char *outname, + struct inode * inode) { unsigned char utf8; struct nls_table *nls; diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c index 0efbfe6e9e4b..ea74b6916d74 100644 --- a/fs/isofs/namei.c +++ b/fs/isofs/namei.c @@ -53,200 +53,130 @@ isofs_cmp(struct dentry * dentry, const char * compare, int dlen) * isofs_find_entry() * * finds an entry in the specified directory with the wanted name. It - * returns the cache buffer in which the entry was found, and the entry - * itself (as an inode number). It does NOT read the inode of the - * entry - you'll have to do that yourself if you want to. + * returns the inode number of the found entry, or 0 on error. */ -static struct buffer_head * -isofs_find_entry(struct inode *dir, struct dentry *dentry, unsigned long *ino) +static unsigned long +isofs_find_entry(struct inode *dir, struct dentry *dentry, + char *tmpname, struct iso_directory_record *tmpde) { + unsigned long inode_number; unsigned long bufsize = ISOFS_BUFFER_SIZE(dir); unsigned char bufbits = ISOFS_BUFFER_BITS(dir); - unsigned int block, i, f_pos, offset, - inode_number = 0; /* shut gcc up */ - struct buffer_head * bh , * retval = NULL; - unsigned int old_offset; - int dlen, match; - char * dpnt; - unsigned char *page = NULL; - struct iso_directory_record * de = NULL; /* shut gcc up */ - char de_not_in_buf = 0; /* true if de is in kmalloc'd memory */ - char c; - - *ino = 0; - - if (!(block = dir->u.isofs_i.i_first_extent)) return NULL; + unsigned int block, f_pos, offset; + struct buffer_head * bh = NULL; + + if (!dir->u.isofs_i.i_first_extent) + return 0; f_pos = 0; - - offset = f_pos & (bufsize - 1); - block = isofs_bmap(dir,f_pos >> bufbits); - - if (!block || !(bh = bread(dir->i_dev,block,bufsize))) return NULL; + offset = 0; + block = 0; while (f_pos < dir->i_size) { + struct iso_directory_record * de; + int de_len, match, i, dlen; + char *dpnt; - /* if de is in kmalloc'd memory, do not point to the - next de, instead we will move to the next sector */ - if(!de_not_in_buf) { - de = (struct iso_directory_record *) - (bh->b_data + offset); + if (!bh) { + bh = isofs_bread(dir, bufsize, block); + if (!bh) + return 0; } - inode_number = (block << bufbits) + (offset & (bufsize - 1)); - - /* If byte is zero, or we had to fetch this de past - the end of the buffer, this is the end of file, or - time to move to the next sector. Usually 2048 byte - boundaries. */ - - if (*((unsigned char *) de) == 0 || de_not_in_buf) { - if(de_not_in_buf) { - /* james@bpgc.com: Since we slopped - past the end of the last buffer, we - must start some way into the new - one */ - de_not_in_buf = 0; - kfree(de); - f_pos += offset; - } - else { - offset = 0; - f_pos = ((f_pos & ~(ISOFS_BLOCK_SIZE - 1)) - + ISOFS_BLOCK_SIZE); - } - brelse(bh); - bh = NULL; - - if (f_pos >= dir->i_size) - break; - block = isofs_bmap(dir,f_pos>>bufbits); - if (!block || !(bh = bread(dir->i_dev,block,bufsize))) - break; + de = (struct iso_directory_record *) (bh->b_data + offset); + inode_number = (bh->b_blocknr << bufbits) + offset; - continue; /* Will kick out if past end of directory */ + de_len = *(unsigned char *) de; + if (!de_len) { + brelse(bh); + bh = NULL; + f_pos = (f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1); + block = f_pos >> bufbits; + offset = 0; + continue; } - old_offset = offset; - offset += *((unsigned char *) de); - f_pos += *((unsigned char *) de); + offset += de_len; + f_pos += de_len; - /* james@bpgc.com: new code to handle case where the - directory entry spans two blocks. Usually 1024 - byte boundaries */ + /* Make sure we have a full directory entry */ if (offset >= bufsize) { - struct buffer_head *bh_next; - - /* james@bpgc.com: read the next block, and - copy the split de into a newly kmalloc'd - buffer */ - block = isofs_bmap(dir,f_pos>>bufbits); - if (!block || - !(bh_next = bread(dir->i_dev,block,bufsize))) - break; - - de = (struct iso_directory_record *) - kmalloc(offset - old_offset, GFP_KERNEL); - if(de==NULL) - break; - memcpy((char *)de, bh->b_data + old_offset, - bufsize - old_offset); - memcpy((char *)de + bufsize - old_offset, - bh_next->b_data, offset - bufsize); - brelse(bh_next); - de_not_in_buf = 1; - offset -= bufsize; + int slop = bufsize - offset + de_len; + memcpy(tmpde, de, slop); + offset &= bufsize - 1; + block++; + brelse(bh); + bh = NULL; + if (offset) { + bh = isofs_bread(dir, bufsize, block); + if (!bh) + return 0; + memcpy((void *) tmpde + slop, bh->b_data, offset); + } + de = tmpde; } + dlen = de->name_len[0]; dpnt = de->name; - if (dir->i_sb->u.isofs_sb.s_rock || - dir->i_sb->u.isofs_sb.s_joliet_level || - dir->i_sb->u.isofs_sb.s_mapping == 'n' || - dir->i_sb->u.isofs_sb.s_mapping == 'a') { - if (! page) { - page = (unsigned char *) - __get_free_page(GFP_KERNEL); - if (!page) break; - } - } if (dir->i_sb->u.isofs_sb.s_rock && - ((i = get_rock_ridge_filename(de, page, dir)))) { - dlen = i; - dpnt = page; + ((i = get_rock_ridge_filename(de, tmpname, dir)))) { + dlen = i; /* possibly -1 */ + dpnt = tmpname; #ifdef CONFIG_JOLIET } else if (dir->i_sb->u.isofs_sb.s_joliet_level) { - dlen = get_joliet_filename(de, dir, page); - dpnt = page; + dlen = get_joliet_filename(de, tmpname, dir); + dpnt = tmpname; #endif } else if (dir->i_sb->u.isofs_sb.s_mapping == 'a') { - dlen = get_acorn_filename(de, page, dir); - dpnt = page; + dlen = get_acorn_filename(de, tmpname, dir); + dpnt = tmpname; } else if (dir->i_sb->u.isofs_sb.s_mapping == 'n') { - for (i = 0; i < dlen; i++) { - c = dpnt[i]; - /* lower case */ - if (c >= 'A' && c <= 'Z') c |= 0x20; - if (c == ';' && i == dlen-2 && dpnt[i+1] == '1') { - dlen -= 2; - break; - } - if (c == ';') c = '.'; - page[i] = c; - } - /* This allows us to match with and without - * a trailing period. */ - if(page[dlen-1] == '.' && dentry->d_name.len == dlen-1) - dlen--; - dpnt = page; + dlen = isofs_name_translate(de, tmpname, dir); + dpnt = tmpname; } + /* * Skip hidden or associated files unless unhide is set */ match = 0; - if ((!(de->flags[-dir->i_sb->u.isofs_sb.s_high_sierra] & 5) - || dir->i_sb->u.isofs_sb.s_unhide == 'y') && dlen) + if (dlen > 0 && + (!(de->flags[-dir->i_sb->u.isofs_sb.s_high_sierra] & 5) + || dir->i_sb->u.isofs_sb.s_unhide == 'y')) { match = (isofs_cmp(dentry,dpnt,dlen) == 0); } if (match) { - if(inode_number == -1) { - /* Should only happen for the '..' entry */ - inode_number = - isofs_lookup_grandparent(dir, - find_rock_ridge_relocation(de,dir)); - } - *ino = inode_number; - retval = bh; - bh = NULL; - break; + if (bh) + brelse(bh); + return inode_number; } } - if (page) free_page((unsigned long) page); - if (bh) brelse(bh); - if(de_not_in_buf) - kfree(de); - return retval; + if (bh) + brelse(bh); + return 0; } struct dentry *isofs_lookup(struct inode * dir, struct dentry * dentry) { unsigned long ino; - struct buffer_head * bh; struct inode *inode; + char *page; + struct iso_directory_record * tmpde; -#ifdef DEBUG - printk("lookup: %x %s\n",dir->i_ino, dentry->d_name.name); -#endif dentry->d_op = dir->i_sb->s_root->d_op; - bh = isofs_find_entry(dir, dentry, &ino); + page = (char *) __get_free_page(GFP_USER); + if (!page) + return ERR_PTR(-ENOMEM); + tmpde = (struct iso_directory_record *) (page+1024); - inode = NULL; - if (bh) { - brelse(bh); + ino = isofs_find_entry(dir, dentry, page, tmpde); + free_page((unsigned long) page); - inode = iget(dir->i_sb,ino); + inode = NULL; + if (ino) { + inode = iget(dir->i_sb, ino); if (!inode) return ERR_PTR(-EACCES); } diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c index 03235f22f7cd..53b9a13f4841 100644 --- a/fs/isofs/rock.c +++ b/fs/isofs/rock.c @@ -70,7 +70,7 @@ cont_size = 0; \ cont_offset = 0; \ goto LABEL; \ - }; \ + } \ printk("Unable to read rock-ridge attributes\n"); \ }} @@ -118,22 +118,16 @@ int find_rock_ridge_relocation(struct iso_directory_record * de, CHECK_SP(goto out); break; case SIG('C','L'): -#ifdef DEBUG - printk("RR: CL\n"); -#endif if (flag == 0) { retval = isonum_733(rr->u.CL.location); goto out; - }; + } break; case SIG('P','L'): -#ifdef DEBUG - printk("RR: PL\n"); -#endif if (flag != 0) { retval = isonum_733(rr->u.PL.location); goto out; - }; + } break; case SIG('C','E'): CHECK_CE; /* This tells is if there is a continuation record */ @@ -141,8 +135,8 @@ int find_rock_ridge_relocation(struct iso_directory_record * de, default: break; } - }; - }; + } + } MAYBE_CONTINUE(repeat, inode); return retval; out: @@ -150,6 +144,7 @@ int find_rock_ridge_relocation(struct iso_directory_record * de, return retval; } +/* return length of name field; 0: not found, -1: to be ignored */ int get_rock_ridge_filename(struct iso_directory_record * de, char * retname, struct inode * inode) { @@ -200,24 +195,21 @@ int get_rock_ridge_filename(struct iso_directory_record * de, if (rr->u.NM.flags & ~1) { printk("Unsupported NM flag settings (%d)\n",rr->u.NM.flags); break; - }; + } if((strlen(retname) + rr->len - 5) >= 254) { truncate = 1; break; - }; + } strncat(retname, rr->u.NM.name, rr->len - 5); retnamlen += rr->len - 5; break; case SIG('R','E'): -#ifdef DEBUG - printk("RR: RE (%x)\n", inode->i_ino); -#endif if (buffer) kfree(buffer); return -1; default: break; } - }; + } } MAYBE_CONTINUE(repeat,inode); return retnamlen; /* If 0, this file did not have a NM field */ @@ -263,10 +255,10 @@ int parse_rock_ridge_inode(struct iso_directory_record * de, break; case SIG('E','R'): inode->i_sb->u.isofs_sb.s_rock = 1; - printk(KERN_DEBUG"ISO 9660 Extensions: "); + printk(KERN_DEBUG "ISO 9660 Extensions: "); { int p; for(p=0;pu.ER.len_id;p++) printk("%c",rr->u.ER.data[p]); - }; + } printk("\n"); break; case SIG('P','X'): @@ -337,27 +329,24 @@ int parse_rock_ridge_inode(struct iso_directory_record * de, slp = (struct SL_component *) (((char *) slp) + slp->len + 2); if(slen < 2) { - if( ((rr->u.SL.flags & 1) != 0) - && ((oldslp->flags & 1) == 0) ) inode->i_size += 1; + if((rr->u.SL.flags & 1) != 0 && (oldslp->flags & 1) == 0) + inode->i_size += 1; break; } /* * If this component record isn't continued, then append a '/'. */ - if( (!rootflag) - && ((oldslp->flags & 1) == 0) ) inode->i_size += 1; + if(!rootflag && (oldslp->flags & 1) == 0) + inode->i_size += 1; } } symlink_len = inode->i_size; break; case SIG('R','E'): - printk("Attempt to read inode for relocated directory\n"); + printk(KERN_WARNING "Attempt to read inode for relocated directory\n"); goto out; case SIG('C','L'): -#ifdef DEBUG - printk("RR CL (%x)\n",inode->i_ino); -#endif inode->u.isofs_i.i_first_extent = isonum_733(rr->u.CL.location); reloc = iget(inode->i_sb, (inode->u.isofs_i.i_first_extent << @@ -378,7 +367,7 @@ int parse_rock_ridge_inode(struct iso_directory_record * de, default: break; } - }; + } } MAYBE_CONTINUE(repeat,inode); return 0; diff --git a/fs/isofs/util.c b/fs/isofs/util.c index a8962fc76081..b966c512bc49 100644 --- a/fs/isofs/util.c +++ b/fs/isofs/util.c @@ -98,7 +98,7 @@ isonum_733 (char * p) int iso_date(char * p, int flag) { - int year, month, day, hour ,minute, second, tz; + int year, month, day, hour, minute, second, tz; int crtime, days, i; year = p[0] - 70; @@ -114,7 +114,6 @@ int iso_date(char * p, int flag) crtime = 0; } else { int monlen[12] = {31,28,31,30,31,30,31,31,30,31,30,31}; - extern struct timezone sys_tz; days = year * 365; if (year > 2) @@ -126,8 +125,6 @@ int iso_date(char * p, int flag) days += day - 1; crtime = ((((days * 24) + hour) * 60 + minute) * 60) + second; - if (sys_tz.tz_dsttime) - crtime -= 3600; /* sign extend */ if (tz & 0x80) @@ -149,7 +146,7 @@ int iso_date(char * p, int flag) * NOTE: mkisofs in versions prior to mkisofs-1.10 had * the sign wrong on the timezone offset. This has now * been corrected there too, but if you are getting screwy - * results this may be the explaination. If enough people + * results this may be the explanation. If enough people * complain, a user configuration option could be added * to add the timezone offset in with the wrong sign * for 'compatibility' with older discs, but I cannot see how diff --git a/include/asm-i386/e820.h b/include/asm-i386/e820.h new file mode 100644 index 000000000000..5c285aee7294 --- /dev/null +++ b/include/asm-i386/e820.h @@ -0,0 +1,40 @@ +/* + * structures and definitions for the int 15, ax=e820 memory map + * scheme. + * + * In a nutshell, arch/i386/boot/setup.S populates a scratch table + * in the empty_zero_block that contains a list of usable address/size + * duples. In arch/i386/kernel/setup.c, this information is + * transferred into the e820map, and in arch/i386/mm/init.c, that + * new information is used to mark pages reserved or not. + * + */ +#ifndef __E820_HEADER +#define __E820_HEADER + +#define E820MAP 0x2d0 /* our map */ +#define E820MAX 32 /* number of entries in E820MAP */ +#define E820NR 0x1e8 /* # entries in E820MAP */ + +#define E820_RAM 1 +#define E820_RESERVED 2 +#define E820_ACPI 3 /* usable as RAM once ACPI tables have been read */ +#define E820_NVS 4 + +#define HIGH_MEMORY (1024*1024) + +#ifndef __ASSEMBLY__ + +struct e820map { + int nr_map; + struct e820entry { + unsigned long long addr; /* start of memory segment */ + unsigned long long size; /* size of memory segment */ + unsigned long type; /* type of memory segment */ + } map[E820MAX]; +}; + +extern struct e820map e820; +#endif/*!__ASSEMBLY__*/ + +#endif/*__E820_HEADER*/ diff --git a/include/asm-i386/msr.h b/include/asm-i386/msr.h index 1ed8ea851949..418c03cfdab6 100644 --- a/include/asm-i386/msr.h +++ b/include/asm-i386/msr.h @@ -28,3 +28,7 @@ : "=a" (low), "=d" (high) \ : "c" (counter)) +/* symbolic names for some interesting MSRs */ +#define MSR_IA32_PLATFORM_ID 0x17 +#define MSR_IA32_UCODE_WRITE 0x79 +#define MSR_IA32_UCODE_REV 0x8B diff --git a/include/asm-i386/processor.h b/include/asm-i386/processor.h index 1f22eb3a56b8..06657c582e08 100644 --- a/include/asm-i386/processor.h +++ b/include/asm-i386/processor.h @@ -327,6 +327,23 @@ extern void free_task_struct(struct task_struct *); #define init_task (init_task_union.task) #define init_stack (init_task_union.stack) +/* '6' because it used to be for P6 only, now supports P15 too */ +#define MICROCODE_IOCFREE _IO('6',0) + +/* physical layour of IA32 microcode chunks */ +struct microcode { + unsigned int hdrver; + unsigned int rev; + unsigned int date; + unsigned int sig; + unsigned int cksum; + unsigned int ldrver; + unsigned int pf; + unsigned int reserved[5]; + unsigned int bits[500]; +}; + + /* REP NOP (PAUSE) is a good thing to insert into busy-wait loops. */ extern inline void rep_nop(void) { diff --git a/include/linux/agp_backend.h b/include/linux/agp_backend.h index 22a2c5971cb4..9e8f96e4f90c 100644 --- a/include/linux/agp_backend.h +++ b/include/linux/agp_backend.h @@ -45,6 +45,7 @@ enum chipset_type { INTEL_BX, INTEL_GX, INTEL_I810, + INTEL_I815, INTEL_I840, VIA_GENERIC, VIA_VP3, diff --git a/include/linux/blk.h b/include/linux/blk.h index 8db376063b4c..0a22c3dadc70 100644 --- a/include/linux/blk.h +++ b/include/linux/blk.h @@ -193,6 +193,14 @@ static void floppy_off(unsigned int nr); #define DEVICE_ON(device) #define DEVICE_OFF(device) +#elif (MAJOR_NR == OSST_MAJOR) + +#define DEVICE_NAME "onstream" +#define DEVICE_INTR do_osst +#define DEVICE_NR(device) (MINOR(device) & 0x7f) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + #elif (MAJOR_NR == SCSI_CDROM_MAJOR) #define DEVICE_NAME "CD-ROM" @@ -412,7 +420,7 @@ static void floppy_off(unsigned int nr); #endif /* MAJOR_NR == whatever */ -#if (MAJOR_NR != SCSI_TAPE_MAJOR) +#if (MAJOR_NR != SCSI_TAPE_MAJOR) && (MAJOR_NR != OSST_MAJOR) #if !defined(IDE_DRIVER) #ifndef CURRENT diff --git a/include/linux/capi.h b/include/linux/capi.h index 47ddb3d14f63..0c347e003b21 100644 --- a/include/linux/capi.h +++ b/include/linux/capi.h @@ -114,4 +114,18 @@ typedef union capi_ioctl_struct { __u16 errcode; } capi_ioctl_struct; +/* + * Middleware extension + */ + +#define CAPIFLAG_HIGHJACKING 0x0001 + +#define CAPI_GET_FLAGS _IOR('C',0x23, unsigned) +#define CAPI_SET_FLAGS _IOR('C',0x24, unsigned) +#define CAPI_CLR_FLAGS _IOR('C',0x25, unsigned) + +#define CAPI_NCCI_OPENCOUNT _IOR('C',0x26, unsigned) + +#define CAPI_NCCI_GETUNIT _IOR('C',0x27, unsigned) + #endif /* __LINUX_CAPI_H__ */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 83057fe724c9..e2d39ed8182d 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -47,7 +47,12 @@ struct poll_table_struct; /* And dynamically-tunable limits and defaults: */ extern int max_inodes; -extern int max_files, nr_files, nr_free_files; +struct files_stat_struct { + int nr_files; /* read only */ + int nr_free_files; /* read only */ + int max_files; /* tunable */ +}; +extern struct files_stat_struct files_stat; /* fs/file_table.c */ extern int max_super_blocks, nr_super_blocks; #define NR_FILE 4096 /* this can well be larger on a larger system */ diff --git a/include/linux/isdn.h b/include/linux/isdn.h index 5417126f8f0a..24ad63c00ec8 100644 --- a/include/linux/isdn.h +++ b/include/linux/isdn.h @@ -1,4 +1,4 @@ -/* $Id: isdn.h,v 1.107 2000/09/10 20:29:18 detabc Exp $ +/* $Id: isdn.h,v 1.110 2000/11/01 17:54:01 detabc Exp $ * Main header for the Linux ISDN subsystem (linklevel). * @@ -66,9 +66,7 @@ #undef CONFIG_ISDN_WITH_ABC_CH_EXTINUSE #undef CONFIG_ISDN_WITH_ABC_CONN_ERROR #undef CONFIG_ISDN_WITH_ABC_RAWIPCOMPRESS -#undef CONFIG_ISDN_WITH_ABC_FRAME_LIMIT -#undef CONFIG_ISDN_WITH_ABC_IPV4_RW_SOCKADDR -#undef CONFIG_ISDN_WITH_ABC_IPV4_RWUDP_SOCKADDR +#undef CONFIG_ISDN_WITH_ABC_IPTABLES_NETFILTER /* New ioctl-codes */ @@ -368,7 +366,6 @@ typedef struct isdn_net_local_s { ulong sqfull_stamp; /* Start-Time of overload */ ulong slavedelay; /* Dynamic bundling delaytime */ int triggercps; /* BogoCPS needed for trigger slave */ - struct device *srobin; /* Ptr to Master device for slaves */ isdn_net_phone *phone[2]; /* List of remote-phonenumbers */ /* phone[0] = Incoming Numbers */ /* phone[1] = Outgoing Numbers */ @@ -378,9 +375,15 @@ typedef struct isdn_net_local_s { struct isdn_net_local_s *next; /* Ptr to next link in bundle */ struct isdn_net_local_s *last; /* Ptr to last link in bundle */ struct isdn_net_dev_s *netdev; /* Ptr to netdev */ - struct sk_buff *first_skb; /* Ptr to skb that triggers dialing */ - struct sk_buff *volatile sav_skb; /* Ptr to skb, rejected by LL-driver*/ + struct sk_buff_head super_tx_queue; /* List of supervisory frames to */ + /* be transmitted asap */ + atomic_t frame_cnt; /* number of frames currently */ + /* queued in HL driver */ /* Ptr to orig. hard_header_cache */ + spinlock_t xmit_lock; /* used to protect the xmit path of */ + /* a particular channel (including */ + /* the frame_cnt */ + int (*org_hhc)( struct neighbour *neigh, struct hh_cache *hh); @@ -400,13 +403,17 @@ typedef struct isdn_net_local_s { int cisco_loop; /* Loop counter for Cisco-SLARP */ ulong cisco_myseq; /* Local keepalive seq. for Cisco */ ulong cisco_yourseq; /* Remote keepalive seq. for Cisco */ + struct tq_struct tqueue; } isdn_net_local; /* the interface itself */ typedef struct isdn_net_dev_s { isdn_net_local *local; - isdn_net_local *queue; - void *next; /* Pointer to next isdn-interface */ + isdn_net_local *queue; /* circular list of all bundled + channels, which are currently + online */ + spinlock_t queue_lock; /* lock to protect queue */ + void *next; /* Pointer to next isdn-interface */ struct device dev; /* interface to upper levels */ #ifdef CONFIG_ISDN_PPP ippp_bundle * pb; /* pointer to the common bundle structure @@ -697,5 +704,13 @@ static void __inline__ netif_stop_queue(struct device * dev) { dev->tbusy = 1; } + +struct pci_dev; + +static int __inline__ pci_enable_device(struct pci_dev * pdev) +{ + return 0; +} + #endif /* __KERNEL__ */ #endif /* isdn_h */ diff --git a/include/linux/isdn_ppp.h b/include/linux/isdn_ppp.h index abf4f1dec141..988c9402d7a2 100644 --- a/include/linux/isdn_ppp.h +++ b/include/linux/isdn_ppp.h @@ -30,8 +30,8 @@ struct pppcallinfo #define PPPIOCGIFNAME _IOR('t',136, char [IFNAMSIZ] ) #define PPP_MP 0x003d -#define PPP_LINK_COMP 0x00fb -#define PPP_LINK_CCP 0x80fb +#define PPP_COMPFRAG 0x00fb +#define PPP_CCPFRAG 0x80fb #define SC_MP_PROT 0x00000200 #define SC_REJ_MP_PROT 0x00000400 @@ -68,8 +68,6 @@ struct isdn_ppp_comp_data { #ifdef __KERNEL__ -#include - /* * We need a way for the decompressor to influence the generation of CCP * Reset-Requests in a variety of ways. The decompressor is already returning diff --git a/include/linux/isdnif.h b/include/linux/isdnif.h index 0d51a4a3de5e..15ebaffcba9d 100644 --- a/include/linux/isdnif.h +++ b/include/linux/isdnif.h @@ -135,6 +135,20 @@ /* STAT_INVOKE_BRD callback. The ll_id is set to 0, the other fields */ /* are supplied by the network and not by the HL. */ /*********************************************************************/ + +/*****************/ +/* NI1 commands */ +/*****************/ +#define NI1_CMD_INVOKE ((0x00 << 8) | ISDN_PTYPE_NI1) /* invoke a supplementary service */ +#define NI1_CMD_INVOKE_ABORT ((0x01 << 8) | ISDN_PTYPE_NI1) /* abort a invoke cmd */ + +/*******************************/ +/* NI1 Status callback values */ +/*******************************/ +#define NI1_STAT_INVOKE_RES ((0x80 << 8) | ISDN_PTYPE_NI1) /* Result for invocation */ +#define NI1_STAT_INVOKE_ERR ((0x81 << 8) | ISDN_PTYPE_NI1) /* Error Return for invocation */ +#define NI1_STAT_INVOKE_BRD ((0x82 << 8) | ISDN_PTYPE_NI1) /* Deliver invoke broadcast info */ + typedef struct { ulong ll_id; /* ID supplied by LL when executing */ /* a command and returned by HL for */ @@ -150,7 +164,7 @@ typedef struct /* error value when error callback */ int datalen; /* length of cmd or stat data */ u_char *data;/* pointer to data delivered or send */ - } dss1_cmd_stat; + } isdn_cmd_stat; /* * Commands from linklevel to lowlevel @@ -412,7 +426,7 @@ typedef struct { setup_parm setup;/* For SETUP msg */ capi_msg cmsg; /* For CAPI like messages */ char display[85];/* display message data */ - dss1_cmd_stat dss1_io; /* DSS1 IO-parameter/result */ + isdn_cmd_stat isdn_io; /* ISDN IO-parameter/result */ aux_s aux; /* for modem commands/indications */ #ifdef CONFIG_ISDN_TTY_FAX T30_s *fax; /* Pointer to ttys fax struct */ @@ -420,6 +434,9 @@ typedef struct { } parm; } isdn_ctrl; +#define dss1_io isdn_io +#define ni1_io isdn_io + /* * The interface-struct itself (initialized at load-time of lowlevel-driver) * diff --git a/include/linux/iso_fs.h b/include/linux/iso_fs.h index 0fcb4b822498..67683ebe6f73 100644 --- a/include/linux/iso_fs.h +++ b/include/linux/iso_fs.h @@ -177,41 +177,22 @@ extern int iso_date(char *, int); extern int parse_rock_ridge_inode(struct iso_directory_record *, struct inode *); extern int get_rock_ridge_filename(struct iso_directory_record *, char *, struct inode *); +extern int isofs_name_translate(struct iso_directory_record *, char *, struct inode *); extern char * get_rock_ridge_symlink(struct inode *); -extern int find_rock_ridge_relocation(struct iso_directory_record *, struct inode *); -int get_joliet_filename(struct iso_directory_record *, struct inode *, unsigned char *); +extern int find_rock_ridge_relocation(struct iso_directory_record *, struct inode *); +int get_joliet_filename(struct iso_directory_record *, unsigned char *, struct inode *); int get_acorn_filename(struct iso_directory_record *, char *, struct inode *); -/* The stuff that follows may be totally unneeded. I have not checked to see - which prototypes we are still using. */ - -extern int isofs_open(struct inode * inode, struct file * filp); -extern void isofs_release(struct inode * inode, struct file * filp); extern struct dentry *isofs_lookup(struct inode * dir, struct dentry *); -extern unsigned long isofs_count_free_inodes(struct super_block *sb); -extern int isofs_new_block(int dev); -extern int isofs_free_block(int dev, int block); extern int isofs_bmap(struct inode *,int); - -extern void isofs_put_super(struct super_block *); -extern struct super_block *isofs_read_super(struct super_block *,void *,int); +extern struct buffer_head *isofs_bread(struct inode *, unsigned int, unsigned int); extern int init_iso9660_fs(void); -extern void isofs_read_inode(struct inode *); -extern void isofs_put_inode(struct inode *); -extern int isofs_statfs(struct super_block *, struct statfs *, int); - -extern int isofs_lseek(struct inode *, struct file *, off_t, int); -extern int isofs_read(struct inode *, struct file *, char *, int); -extern int isofs_lookup_grandparent(struct inode *, int); extern struct inode_operations isofs_file_inode_operations; extern struct inode_operations isofs_dir_inode_operations; extern struct inode_operations isofs_symlink_inode_operations; -extern struct inode_operations isofs_chrdev_inode_operations; -extern struct inode_operations isofs_blkdev_inode_operations; -extern struct inode_operations isofs_fifo_inode_operations; /* The following macros are used to check for memory leaks. */ #ifdef LEAK_CHECK diff --git a/include/linux/kernelcapi.h b/include/linux/kernelcapi.h index b10f304a5ac6..40530168ee8f 100644 --- a/include/linux/kernelcapi.h +++ b/include/linux/kernelcapi.h @@ -1,58 +1,17 @@ /* - * $Id: kernelcapi.h,v 1.5 2000/01/28 16:45:40 calle Exp $ + * $Id: kernelcapi.h,v 1.7 2000/06/12 09:20:20 kai Exp $ * * Kernel CAPI 2.0 Interface for Linux * * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) * - * $Log: kernelcapi.h,v $ - * Revision 1.5 2000/01/28 16:45:40 calle - * new manufacturer command KCAPI_CMD_ADDCARD (generic addcard), - * will search named driver and call the add_card function if one exist. - * - * Revision 1.4 1999/09/10 17:24:19 calle - * Changes for proposed standard for CAPI2.0: - * - AK148 "Linux Exention" - * - * Revision 1.3 1999/07/01 15:26:56 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.2 1999/06/21 15:24:26 calle - * extend information in /proc. - * - * Revision 1.1 1997/03/04 21:27:33 calle - * First version in isdn4linux - * - * Revision 2.2 1997/02/12 09:31:39 calle - * new version - * - * Revision 1.1 1997/01/31 10:32:20 calle - * Initial revision - * - * */ + #ifndef __KERNELCAPI_H__ #define __KERNELCAPI_H__ -#define CAPI_MAXAPPL 20 /* - * maximum number of applications - */ -#define CAPI_MAXCONTR 10 /* - * maximum number of controller - */ +#define CAPI_MAXAPPL 20 /* maximum number of applications */ +#define CAPI_MAXCONTR 10 /* maximum number of controller */ #define CAPI_MAXDATAWINDOW 8 @@ -94,8 +53,8 @@ struct capi_interface { __u16 (*capi_put_message) (__u16 applid, struct sk_buff * msg); __u16 (*capi_get_message) (__u16 applid, struct sk_buff ** msgp); __u16 (*capi_set_signal) (__u16 applid, - void (*signal) (__u16 applid, __u32 param), - __u32 param); + void (*signal) (__u16 applid, void *param), + void *param); __u16 (*capi_get_manufacturer) (__u32 contr, __u8 buf[CAPI_MANUFACTURER_LEN]); __u16 (*capi_get_version) (__u32 contr, struct capi_version * verp); __u16(*capi_get_serial) (__u32 contr, __u8 serial[CAPI_SERIAL_LEN]); @@ -108,8 +67,15 @@ struct capi_interface { }; -#define KCI_CONTRUP 0 -#define KCI_CONTRDOWN 1 +struct capi_ncciinfo { + __u16 applid; + __u32 ncci; +}; + +#define KCI_CONTRUP 0 /* struct capi_profile */ +#define KCI_CONTRDOWN 1 /* NULL */ +#define KCI_NCCIUP 2 /* struct capi_ncciinfo */ +#define KCI_NCCIDOWN 3 /* struct capi_ncciinfo */ struct capi_interface_user { char name[20]; @@ -148,6 +114,47 @@ int detach_capi_interface(struct capi_interface_user *); #define CAPI_MSGCTRLERNOTSUPPORTEXTEQUIP 0x110a #define CAPI_MSGCTRLERONLYSUPPORTEXTEQUIP 0x110b +typedef enum { + CapiMessageNotSupportedInCurrentState = 0x2001, + CapiIllContrPlciNcci = 0x2002, + CapiNoPlciAvailable = 0x2003, + CapiNoNcciAvailable = 0x2004, + CapiNoListenResourcesAvailable = 0x2005, + CapiNoFaxResourcesAvailable = 0x2006, + CapiIllMessageParmCoding = 0x2007, +} RESOURCE_CODING_PROBLEM; + +typedef enum { + CapiB1ProtocolNotSupported = 0x3001, + CapiB2ProtocolNotSupported = 0x3002, + CapiB3ProtocolNotSupported = 0x3003, + CapiB1ProtocolParameterNotSupported = 0x3004, + CapiB2ProtocolParameterNotSupported = 0x3005, + CapiB3ProtocolParameterNotSupported = 0x3006, + CapiBProtocolCombinationNotSupported = 0x3007, + CapiNcpiNotSupported = 0x3008, + CapiCipValueUnknown = 0x3009, + CapiFlagsNotSupported = 0x300a, + CapiFacilityNotSupported = 0x300b, + CapiDataLengthNotSupportedByCurrentProtocol = 0x300c, + CapiResetProcedureNotSupportedByCurrentProtocol = 0x300d, + CapiTeiAssignmentFailed = 0x300e, +} REQUESTED_SERVICES_PROBLEM; + +typedef enum { + CapiSuccess = 0x0000, + CapiSupplementaryServiceNotSupported = 0x300e, + CapiRequestNotAllowedInThisState = 0x3010, +} SUPPLEMENTARY_SERVICE_INFO; + +typedef enum { + CapiProtocolErrorLayer1 = 0x3301, + CapiProtocolErrorLayer2 = 0x3302, + CapiProtocolErrorLayer3 = 0x3303, + CapiTimeOut = 0x3303, // SuppServiceReason + CapiCallGivenToOtherApplication = 0x3304, +} CAPI_REASON; + #endif /* __KERNEL__ */ #endif /* __KERNELCAPI_H__ */ diff --git a/include/linux/major.h b/include/linux/major.h index 4336220f2169..b8a6a80559db 100644 --- a/include/linux/major.h +++ b/include/linux/major.h @@ -133,6 +133,8 @@ #define MSR_MAJOR 202 #define CPUID_MAJOR 203 +#define OSST_MAJOR 206 /* OnStream-SCx0 SCSI tape */ + /* * Tests for SCSI devices. */ diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h index e04dc1b423f6..0cb6e38e395e 100644 --- a/include/linux/miscdevice.h +++ b/include/linux/miscdevice.h @@ -17,6 +17,7 @@ #define SUN_OPENPROM_MINOR 139 #define NVRAM_MINOR 144 #define I2O_MINOR 166 +#define MICROCODE_MINOR 184 #define MISC_DYNAMIC_MINOR 255 #define SGI_GRAPHICS_MINOR 146 diff --git a/include/linux/mm.h b/include/linux/mm.h index ad89e46aa2f0..84c587aca99c 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -129,8 +129,12 @@ typedef struct page { struct wait_queue *wait; struct page **pprev_hash; struct buffer_head * buffers; + int age; } mem_map_t; +#define PAGE_AGE_INITIAL 1 /* age for pages just mapped */ +#define PAGE_AGE_YOUNG 2 /* age for pages recently referenced */ + /* Page flag bit values */ #define PG_locked 0 #define PG_error 1 diff --git a/include/linux/mtio.h b/include/linux/mtio.h index c794ed898ac5..6f42dabb6bcb 100644 --- a/include/linux/mtio.h +++ b/include/linux/mtio.h @@ -105,6 +105,8 @@ struct mtget { #define MT_ISEVEREX_FT40A 0x32 /* Everex FT40A (QIC-40) */ #define MT_ISDDS1 0x51 /* DDS device without partitions */ #define MT_ISDDS2 0x52 /* DDS device with partitions */ +#define MT_ISONSTREAM_SC 0x61 /* OnStream SCSI tape drives (SC-x0) + and SCSI emulated (DI, DP, USB) */ #define MT_ISSCSI1 0x71 /* Generic ANSI SCSI-1 tape unit */ #define MT_ISSCSI2 0x72 /* Generic ANSI SCSI-2 tape unit */ @@ -134,6 +136,7 @@ struct mt_tape_info { {MT_ISWT5099EEN24, "Wangtek 5099-een24, 60MB"}, \ {MT_ISTEAC_MT2ST, "Teac MT-2ST 155mb data cassette drive"}, \ {MT_ISEVEREX_FT40A, "Everex FT40A, QIC-40"}, \ + {MT_ISONSTREAM_SC, "OnStream SC-, DI-, DP-, or USB tape drive"}, \ {MT_ISSCSI1, "Generic SCSI-1 tape"}, \ {MT_ISSCSI2, "Generic SCSI-2 tape"}, \ {0, NULL} \ diff --git a/include/linux/sched.h b/include/linux/sched.h index 146ee631fff3..82b10f6ec1d6 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -79,6 +79,7 @@ extern int last_pid; #define TASK_ZOMBIE 4 #define TASK_STOPPED 8 #define TASK_SWAPPING 16 +#define TASK_EXCLUSIVE 32 /* * Scheduling policies @@ -251,6 +252,7 @@ struct task_struct { struct task_struct *next_task, *prev_task; struct task_struct *next_run, *prev_run; + unsigned int task_exclusive; /* task wants wake-one semantics in __wake_up() */ /* task state */ struct linux_binfmt *binfmt; int exit_code, exit_signal; @@ -370,6 +372,7 @@ struct task_struct { /* counter */ DEF_PRIORITY,DEF_PRIORITY,0, \ /* SMP */ 0,0,0,-1, \ /* schedlink */ &init_task,&init_task, &init_task, &init_task, \ +/* task_exclusive */ 0, \ /* binfmt */ NULL, \ /* ec,brk... */ 0,0,0,0,0,0, \ /* pid etc.. */ 0,0,0,0,0, \ @@ -496,8 +499,8 @@ extern long FASTCALL(interruptible_sleep_on_timeout(struct wait_queue ** p, signed long timeout)); extern void FASTCALL(wake_up_process(struct task_struct * tsk)); -#define wake_up(x) __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE) -#define wake_up_interruptible(x) __wake_up((x),TASK_INTERRUPTIBLE) +#define wake_up(x) __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE | TASK_EXCLUSIVE) +#define wake_up_interruptible(x) __wake_up((x),TASK_INTERRUPTIBLE | TASK_EXCLUSIVE) #define __set_current_state(state_value) do { current->state = state_value; } while (0) #ifdef __SMP__ diff --git a/include/net/ip_masq.h b/include/net/ip_masq.h index 1e050359a25c..42dcf50810cc 100644 --- a/include/net/ip_masq.h +++ b/include/net/ip_masq.h @@ -31,6 +31,32 @@ #define MASQUERADE_EXPIRE_TCP 15*60*HZ #define MASQUERADE_EXPIRE_TCP_FIN 2*60*HZ #define MASQUERADE_EXPIRE_UDP 5*60*HZ + +/* + * Debugging stuff + */ + +extern int ip_masq_get_debug_level(void); + +#ifdef CONFIG_IP_MASQ_DEBUG +#define IP_MASQ_DEBUG(level, msg...) do { \ + if (level <= ip_masq_get_debug_level()) \ + printk(KERN_DEBUG "IP_MASQ:" ## msg); \ + } while (0) +#else /* NO DEBUGGING at ALL */ +#define IP_MASQ_DEBUG(level, msg...) do { } while (0) +#endif + +#define IP_MASQ_INFO(msg...) \ + printk(KERN_INFO "IP_MASQ:" ## msg) + +#define IP_MASQ_ERR(msg...) \ + printk(KERN_ERR "IP_MASQ:" ## msg) + +#define IP_MASQ_WARNING(msg...) \ + printk(KERN_WARNING "IP_MASQ:" ## msg) + + /* * ICMP can no longer be modified on the fly using an ioctl - this * define is the only way to change the timeouts @@ -164,7 +190,7 @@ struct ip_masq_app { struct ip_masq_app *next; char *name; /* name of application proxy */ - unsigned type; /* type = proto<<16 | port (host byte order)*/ + unsigned type; /* type = flags | proto<<16 | port (host byte order)*/ int n_attach; int (*masq_init_1) /* ip_masq initializer */ (struct ip_masq_app *, struct ip_masq *); @@ -182,20 +208,60 @@ struct ip_masq_app extern int ip_masq_app_init(void); /* - * ip_masq_app object registration functions (port: host byte order) + * ip_masq_app object registration functions: register_ip_masq_app_type() + * can be used if ip_masq_app has been previously initialized + * (port: host byte order) */ -extern int register_ip_masq_app(struct ip_masq_app *mapp, unsigned short proto, __u16 port); +extern int register_ip_masq_app_type(struct ip_masq_app *mapp); +extern int register_ip_masq_app(struct ip_masq_app *mapp, unsigned proto, __u16 port); extern int unregister_ip_masq_app(struct ip_masq_app *mapp); +/* + * ip_masq_app flags[8bits] (ORed with protocol[8bits] ) + */ +#define IP_MASQ_APP_OUTBOUND 0x01000000 /* dst port matched (in-out) */ +#define IP_MASQ_APP_INBOUND 0x02000000 /* src port matched (in-out) */ +#define IP_MASQ_APP_FWMARK 0x80000000 /* hook by (fwmark & 0xffff) */ +#define IP_MASQ_APP_FLAGS_MASK 0xff000000 /* mask for app flags */ +#define IP_MASQ_APP_PROTO_MASK 0x00ff0000 /* mask for proto value */ +#define IP_MASQ_APP_PORT_MASK 0x0000ffff /* mask for port value */ +#define IP_MASQ_APP_FWMARK_MASK 0x00ffffff /* mask for fwmark value (24bits only) */ + +#define IP_MASQ_APP_TYPE_PP(flags, proto, port) ((flags)|((proto)<<16)|(port)) +#define IP_MASQ_APP_TYPE_FWMARK(flags, fwmark) ((flags)|(fwmark&0x00ffffff)) +#define IP_MASQ_APP_TYPE2PORT(type) ( (type) & 0xffff ) +#define IP_MASQ_APP_TYPE2PROTO(type) ( ((type)>>16) & 0x00ff ) +#define IP_MASQ_APP_TYPE2FWMARK(type) ( (type) & 0x00ffffff ) +#define IP_MASQ_APP_TYPE2FLAGS(type) ( (type) & 0xff000000 ) + +/* Init functions for later register_ip_masq_app_type() */ +static __inline__ int ip_masq_app_init_proto_port(struct ip_masq_app* mapp, unsigned flags, unsigned proto, unsigned port) { + mapp->type = IP_MASQ_APP_TYPE_PP(flags, proto, port); + return 0; +} +static __inline__ int ip_masq_app_init_fwmark(struct ip_masq_app* mapp, unsigned flags, __u32 fwmark) { + if (fwmark&~(IP_MASQ_APP_FWMARK_MASK)) { + IP_MASQ_ERR("ip_masq_app_init_fwmark(): fwmark must be <= %d, sorry (shoot Juanjo)\n", IP_MASQ_APP_FWMARK_MASK); + return -EINVAL; + } + mapp->type = IP_MASQ_APP_TYPE_FWMARK(flags, fwmark); + return 0; +} + /* * get ip_masq_app obj by proto,port(net_byte_order) */ -extern struct ip_masq_app * ip_masq_app_get(unsigned short proto, __u16 port); +extern struct ip_masq_app * ip_masq_app_get_type(unsigned type); +static __inline__ struct ip_masq_app * ip_masq_app_get(unsigned short proto, __u16 port) { + unsigned type = IP_MASQ_APP_TYPE_PP(0, proto, port); + return ip_masq_app_get_type(type); +} /* * ip_masq TO ip_masq_app (un)binding functions. */ extern struct ip_masq_app * ip_masq_bind_app(struct ip_masq *ms); +extern struct ip_masq_app * ip_masq_bind_app_fwmark(struct ip_masq *ms, __u32); extern int ip_masq_unbind_app(struct ip_masq *ms); /* @@ -254,31 +320,6 @@ extern rwlock_t __ip_masq_lock; * */ -/* - * Debugging stuff - */ - -extern int ip_masq_get_debug_level(void); - -#ifdef CONFIG_IP_MASQ_DEBUG -#define IP_MASQ_DEBUG(level, msg...) do { \ - if (level <= ip_masq_get_debug_level()) \ - printk(KERN_DEBUG "IP_MASQ:" ## msg); \ - } while (0) -#else /* NO DEBUGGING at ALL */ -#define IP_MASQ_DEBUG(level, msg...) do { } while (0) -#endif - -#define IP_MASQ_INFO(msg...) \ - printk(KERN_INFO "IP_MASQ:" ## msg) - -#define IP_MASQ_ERR(msg...) \ - printk(KERN_ERR "IP_MASQ:" ## msg) - -#define IP_MASQ_WARNING(msg...) \ - printk(KERN_WARNING "IP_MASQ:" ## msg) - - /* * /proc/net entry */ diff --git a/init/main.c b/init/main.c index cca8e462c171..5a7fe3c8b7bd 100644 --- a/init/main.c +++ b/init/main.c @@ -216,6 +216,7 @@ extern void vmpoff_setup(char *str, int *ints); #endif extern void floppy_setup(char *str, int *ints); extern void st_setup(char *str, int *ints); +extern void osst_setup(char *str, int *ints); extern void st0x_setup(char *str, int *ints); extern void advansys_setup(char *str, int *ints); extern void tmc8xx_setup(char *str, int *ints); @@ -841,6 +842,9 @@ static struct kernel_param cooked_params[] __initdata = { #ifdef CONFIG_CHR_DEV_ST { "st=", st_setup }, #endif +#ifdef CONFIG_CHR_DEV_OSST + { "osst=", osst_setup }, +#endif #ifdef CONFIG_BUSMOUSE { "bmouse=", bmouse_setup }, #endif diff --git a/kernel/sched.c b/kernel/sched.c index d4b30f586b88..cf7e778a1bf7 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -890,8 +890,9 @@ rwlock_t waitqueue_lock = RW_LOCK_UNLOCKED; */ void __wake_up(struct wait_queue **q, unsigned int mode) { - struct task_struct *p; + struct task_struct *p, *best_exclusive; struct wait_queue *head, *next; + unsigned int do_exclusive; if (!q) goto out; @@ -906,22 +907,23 @@ void __wake_up(struct wait_queue **q, unsigned int mode) if (!next) goto out_unlock; + best_exclusive = 0; + do_exclusive = mode & TASK_EXCLUSIVE; while (next != head) { p = next->task; next = next->next; if (p->state & mode) { - /* - * We can drop the read-lock early if this - * is the only/last process. - */ - if (next == head) { - read_unlock(&waitqueue_lock); + if (do_exclusive && p->task_exclusive) { + if (best_exclusive == NULL) + best_exclusive = p; + } + else { wake_up_process(p); - goto out; } - wake_up_process(p); } } + if (best_exclusive) + wake_up_process(best_exclusive); out_unlock: read_unlock(&waitqueue_lock); out: @@ -1943,7 +1945,7 @@ asmlinkage int sys_nanosleep(struct timespec *rqtp, struct timespec *rmtp) delay=(t.tv_nsec + 999) / 1000; if(delay>10000) - mdelay(delay); + mdelay((delay+999)/1000); else udelay(delay); return 0; diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 2cea182b777e..4e1186251dab 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -286,9 +286,9 @@ static ctl_table fs_table[] = { 0444, NULL, &proc_dointvec}, {FS_MAXINODE, "inode-max", &max_inodes, sizeof(int), 0644, NULL, &proc_dointvec}, - {FS_NRFILE, "file-nr", &nr_files, 3*sizeof(int), + {FS_NRFILE, "file-nr", &files_stat, 3*sizeof(int), 0444, NULL, &proc_dointvec}, - {FS_MAXFILE, "file-max", &max_files, sizeof(int), + {FS_MAXFILE, "file-max", &files_stat.max_files, sizeof(int), 0644, NULL, &proc_dointvec}, {FS_NRSUPER, "super-nr", &nr_super_blocks, sizeof(int), 0444, NULL, &proc_dointvec}, diff --git a/mm/filemap.c b/mm/filemap.c index 1acdafe8ba07..384bab05a42e 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -158,8 +158,6 @@ int shrink_mmap(int priority, int gfp_mask) refresh_clock: page = mem_map + clock; do { - int referenced; - /* This works even in the presence of PageSkip because * the first two entries at the beginning of a hole will * be marked, not just the first. @@ -176,33 +174,32 @@ int shrink_mmap(int priority, int gfp_mask) clock = page - mem_map; } + if (test_and_clear_bit(PG_referenced, &page->flags)) { + page->age = PAGE_AGE_YOUNG; + continue; + } + + if (page->age > 0) { + page->age--; + continue; + } + /* We can't free pages unless there's just one user */ if (atomic_read(&page->count) != 1) continue; - referenced = test_and_clear_bit(PG_referenced, &page->flags); - if (PageLocked(page)) continue; if ((gfp_mask & __GFP_DMA) && !PageDMA(page)) continue; - /* - * Is it a page swap page? If so, we want to - * drop it if it is no longer used, even if it - * were to be marked referenced.. - */ + /* Is it a page swap page? Drop it, its old. */ if (PageSwapCache(page)) { - if (referenced && swap_count(page->offset) != 1) - continue; delete_from_swap_cache(page); return 1; } - if (referenced) - continue; - /* Is it a buffer page? */ if (page->buffers) { /* diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 0353366cc2b6..8212c29bb780 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -129,6 +129,7 @@ void __free_pages(struct page *page, unsigned long order) if (PageSwapCache(page)) panic ("Freeing swap cache page"); page->flags &= ~(1 << PG_referenced); + page->age = PAGE_AGE_INITIAL; free_pages_ok(page - mem_map, order, PageDMA(page) ? 1 : 0); return; } diff --git a/net/ipv4/ip_masq.c b/net/ipv4/ip_masq.c index 8d42ea8608c6..3953fbea4ad0 100644 --- a/net/ipv4/ip_masq.c +++ b/net/ipv4/ip_masq.c @@ -236,7 +236,7 @@ static int masq_tcp_state(struct ip_masq *ms, int output, struct tcphdr *th) tcp_state_out: if (new_state!=ms->state) - IP_MASQ_DEBUG(1, "%s %s [%c%c%c%c] %08lX:%04X-%08lX:%04X state: %s->%s\n", + IP_MASQ_DEBUG(1, "%s %s [%c%c%c%c] %08X:%04X-%08X:%04X state: %s->%s\n", masq_proto_name(ms->protocol), output? "output" : "input ", th->syn? 'S' : '.', @@ -798,7 +798,7 @@ static void masq_expire(unsigned long data) */ atomic_inc(&ms->refcnt); - IP_MASQ_DEBUG(1, "Masqueraded %s %08lX:%04X expired\n", + IP_MASQ_DEBUG(1, "Masqueraded %s %08X:%04X expired\n", masq_proto_name(ms->protocol), ntohl(ms->saddr),ntohs(ms->sport)); @@ -846,7 +846,7 @@ static void masq_expire(unsigned long data) } masq_expire_later: - IP_MASQ_DEBUG(0, "masq_expire delayed: %s %08lX:%04X->%08lX:%04X masq.refcnt-1=%d masq.n_control=%d\n", + IP_MASQ_DEBUG(0, "masq_expire delayed: %s %08X:%04X->%08X:%04X masq.refcnt-1=%d masq.n_control=%d\n", masq_proto_name(ms->protocol), ntohl(ms->saddr), ntohs(ms->sport), ntohl(ms->daddr), ntohs(ms->dport), @@ -1223,7 +1223,7 @@ int ip_fw_masquerade(struct sk_buff **skb_p, __u32 maddr) /* h.raw = (char*) iph + iph->ihl * 4; */ - IP_MASQ_DEBUG(2, "Outgoing %s %08lX:%04X -> %08lX:%04X\n", + IP_MASQ_DEBUG(2, "Outgoing %s %08X:%04X -> %08X:%04X\n", masq_proto_name(iph->protocol), ntohl(iph->saddr), ntohs(h.portp[0]), ntohl(iph->daddr), ntohs(h.portp[1])); @@ -1294,6 +1294,8 @@ int ip_fw_masquerade(struct sk_buff **skb_p, __u32 maddr) 0); if (ms == NULL) return -1; + if (!ms->app && skb->fwmark) + ip_masq_bind_app_fwmark(ms, skb->fwmark); } /* @@ -1387,7 +1389,7 @@ int ip_fw_masquerade(struct sk_buff **skb_p, __u32 maddr) } ip_send_check(iph); - IP_MASQ_DEBUG(2, "O-routed from %08lX:%04X with masq.addr %08lX\n", + IP_MASQ_DEBUG(2, "O-routed from %08X:%04X with masq.addr %08X\n", ntohl(ms->maddr),ntohs(ms->mport),ntohl(maddr)); masq_set_state(ms, 1, iph, h.portp); @@ -1486,7 +1488,7 @@ int ip_fw_masq_icmp(struct sk_buff **skb_p, __u32 maddr) struct ip_masq *ms; unsigned short len = ntohs(iph->tot_len) - (iph->ihl * 4); - IP_MASQ_DEBUG(2, "Incoming forward ICMP (%d,%d) %lX -> %lX\n", + IP_MASQ_DEBUG(2, "Incoming forward ICMP (%d,%d) %X -> %X\n", icmph->type, ntohs(icmp_id(icmph)), ntohl(iph->saddr), ntohl(iph->daddr)); @@ -1496,7 +1498,7 @@ int ip_fw_masq_icmp(struct sk_buff **skb_p, __u32 maddr) (icmph->type == ICMP_INFO_REQUEST ) || (icmph->type == ICMP_ADDRESS )) { - IP_MASQ_DEBUG(2, "icmp request rcv %lX->%lX id %d type %d\n", + IP_MASQ_DEBUG(2, "icmp request rcv %X->%X id %d type %d\n", ntohl(iph->saddr), ntohl(iph->daddr), ntohs(icmp_id(icmph)), @@ -1547,7 +1549,7 @@ int ip_fw_masq_icmp(struct sk_buff **skb_p, __u32 maddr) icmph->checksum = 0; icmph->checksum = ip_compute_csum((unsigned char *)icmph, len); - IP_MASQ_DEBUG(2, "icmp request rwt %lX->%lX id %d type %d\n", + IP_MASQ_DEBUG(2, "icmp request rwt %X->%X id %d type %d\n", ntohl(iph->saddr), ntohl(iph->daddr), ntohs(icmp_id(icmph)), @@ -1585,7 +1587,7 @@ int ip_fw_masq_icmp(struct sk_buff **skb_p, __u32 maddr) (ciph->ihl<<2)); - IP_MASQ_DEBUG(2, "fw icmp/icmp rcv %lX->%lX id %d type %d\n", + IP_MASQ_DEBUG(2, "fw icmp/icmp rcv %X->%X id %d type %d\n", ntohl(ciph->saddr), ntohl(ciph->daddr), ntohs(icmp_id(cicmph)), @@ -1621,7 +1623,7 @@ int ip_fw_masq_icmp(struct sk_buff **skb_p, __u32 maddr) icmph->checksum = ip_compute_csum((unsigned char *) icmph, len); - IP_MASQ_DEBUG(2, "fw icmp/icmp rwt %lX->%lX id %d type %d\n", + IP_MASQ_DEBUG(2, "fw icmp/icmp rwt %X->%X id %d type %d\n", ntohl(ciph->saddr), ntohl(ciph->daddr), ntohs(icmp_id(cicmph)), @@ -1657,7 +1659,7 @@ int ip_fw_masq_icmp(struct sk_buff **skb_p, __u32 maddr) } - IP_MASQ_DEBUG(2, "Handling forward ICMP for %08lX:%04X -> %08lX:%04X\n", + IP_MASQ_DEBUG(2, "Handling forward ICMP for %08X:%04X -> %08X:%04X\n", ntohl(ciph->saddr), ntohs(pptr[0]), ntohl(ciph->daddr), ntohs(pptr[1])); @@ -1695,7 +1697,7 @@ int ip_fw_masq_icmp(struct sk_buff **skb_p, __u32 maddr) icmph->checksum = ip_compute_csum((unsigned char *) icmph, len); - IP_MASQ_DEBUG(2, "Rewrote forward ICMP to %08lX:%04X -> %08lX:%04X\n", + IP_MASQ_DEBUG(2, "Rewrote forward ICMP to %08X:%04X -> %08X:%04X\n", ntohl(ciph->saddr), ntohs(pptr[0]), ntohl(ciph->daddr), ntohs(pptr[1])); @@ -1745,7 +1747,7 @@ int ip_fw_demasq_icmp(struct sk_buff **skb_p) unsigned short len = ntohs(iph->tot_len) - (iph->ihl * 4); - IP_MASQ_DEBUG(2, "icmp in/rev (%d,%d) %lX -> %lX\n", + IP_MASQ_DEBUG(2, "icmp in/rev (%d,%d) %X -> %X\n", icmph->type, ntohs(icmp_id(icmph)), ntohl(iph->saddr), ntohl(iph->daddr)); @@ -1756,7 +1758,7 @@ int ip_fw_demasq_icmp(struct sk_buff **skb_p) (icmph->type == ICMP_INFO_REPLY) || (icmph->type == ICMP_ADDRESSREPLY)) { - IP_MASQ_DEBUG(2, "icmp reply rcv %lX->%lX id %d type %d, req %d\n", + IP_MASQ_DEBUG(2, "icmp reply rcv %X->%X id %d type %d, req %d\n", ntohl(iph->saddr), ntohl(iph->daddr), ntohs(icmp_id(icmph)), @@ -1793,7 +1795,7 @@ int ip_fw_demasq_icmp(struct sk_buff **skb_p) - IP_MASQ_DEBUG(2, "icmp reply rwt %lX->%lX id %d type %d\n", + IP_MASQ_DEBUG(2, "icmp reply rwt %X->%X id %d type %d\n", ntohl(iph->saddr), ntohl(iph->daddr), ntohs(icmp_id(icmph)), @@ -1830,7 +1832,7 @@ int ip_fw_demasq_icmp(struct sk_buff **skb_p) (ciph->ihl<<2)); - IP_MASQ_DEBUG(2, "rv icmp/icmp rcv %lX->%lX id %d type %d\n", + IP_MASQ_DEBUG(2, "rv icmp/icmp rcv %X->%X id %d type %d\n", ntohl(ciph->saddr), ntohl(ciph->daddr), ntohs(icmp_id(cicmph)), @@ -1872,7 +1874,7 @@ int ip_fw_demasq_icmp(struct sk_buff **skb_p) icmph->checksum = ip_compute_csum((unsigned char *) icmph, len); - IP_MASQ_DEBUG(2, "rv icmp/icmp rwt %lX->%lX id %d type %d\n", + IP_MASQ_DEBUG(2, "rv icmp/icmp rwt %X->%X id %d type %d\n", ntohl(ciph->saddr), ntohl(ciph->daddr), ntohs(icmp_id(cicmph)), @@ -1906,7 +1908,7 @@ int ip_fw_demasq_icmp(struct sk_buff **skb_p) } - IP_MASQ_DEBUG(2, "Handling reverse ICMP for %08lX:%04X -> %08lX:%04X\n", + IP_MASQ_DEBUG(2, "Handling reverse ICMP for %08X:%04X -> %08X:%04X\n", ntohl(ciph->saddr), ntohs(pptr[0]), ntohl(ciph->daddr), ntohs(pptr[1])); @@ -1948,7 +1950,7 @@ int ip_fw_demasq_icmp(struct sk_buff **skb_p) icmph->checksum = ip_compute_csum((unsigned char *) icmph, len); - IP_MASQ_DEBUG(2, "Rewrote reverse ICMP to %08lX:%04X -> %08lX:%04X\n", + IP_MASQ_DEBUG(2, "Rewrote reverse ICMP to %08X:%04X -> %08X:%04X\n", ntohl(ciph->saddr), ntohs(pptr[0]), ntohl(ciph->daddr), ntohs(pptr[1])); @@ -2067,7 +2069,7 @@ int ip_fw_demasquerade(struct sk_buff **skb_p) - IP_MASQ_DEBUG(2, "Incoming %s %08lX:%04X -> %08lX:%04X\n", + IP_MASQ_DEBUG(2, "Incoming %s %08X:%04X -> %08X:%04X\n", masq_proto_name(iph->protocol), ntohl(iph->saddr), ntohs(h.portp[0]), ntohl(iph->daddr), ntohs(h.portp[1])); @@ -2082,8 +2084,11 @@ int ip_fw_demasquerade(struct sk_buff **skb_p) * Give additional modules a chance to create an entry */ #ifdef CONFIG_IP_MASQUERADE_MOD - if (!ms) + if (!ms) { ms = ip_masq_mod_in_create(skb, iph, maddr); + if (ms && !ms->app && skb->fwmark) + ip_masq_bind_app_fwmark(ms, skb->fwmark); + } /* * Call module's input update hook @@ -2138,7 +2143,7 @@ int ip_fw_demasquerade(struct sk_buff **skb_p) write_unlock(&__ip_masq_lock); - IP_MASQ_DEBUG(1, "ip_fw_demasquerade(): filled daddr=%lX\n", + IP_MASQ_DEBUG(1, "ip_fw_demasquerade(): filled daddr=%X\n", ntohl(ms->daddr)); } @@ -2208,7 +2213,7 @@ int ip_fw_demasquerade(struct sk_buff **skb_p) } ip_send_check(iph); - IP_MASQ_DEBUG(2, "I-routed to %08lX:%04X\n",ntohl(iph->daddr),ntohs(h.portp[1])); + IP_MASQ_DEBUG(2, "I-routed to %08X:%04X\n",ntohl(iph->daddr),ntohs(h.portp[1])); masq_set_state (ms, 0, iph, h.portp); ip_masq_put(ms); diff --git a/net/ipv4/ip_masq_app.c b/net/ipv4/ip_masq_app.c index 84e059fafc5f..6ed16f8990d7 100644 --- a/net/ipv4/ip_masq_app.c +++ b/net/ipv4/ip_masq_app.c @@ -15,7 +15,8 @@ * Fixes: * JJC : Implemented also input pkt hook * Miquel van Smoorenburg : Copy more stuff when resizing skb - * + * Juan Jose Ciarlante : reworked register() (new _init_xxxx() functions for nicer interface) + * Juan Jose Ciarlante : add INBOUND hooks (default to OUTBOUND) * * FIXME: * - ip_masq_skb_replace(): use same skb if space available. @@ -41,13 +42,27 @@ #define IP_MASQ_APP_TAB_SIZE 16 /* must be power of 2 */ -#define IP_MASQ_APP_HASH(proto, port) ((port^proto) & (IP_MASQ_APP_TAB_SIZE-1)) -#define IP_MASQ_APP_TYPE(proto, port) ( proto<<16 | port ) -#define IP_MASQ_APP_PORT(type) ( type & 0xffff ) -#define IP_MASQ_APP_PROTO(type) ( (type>>16) & 0x00ff ) +/* + * ip_masq_app->type holds all the values needed to hook + * passing packet + * flags: (uses only upper 8bits: f_bits) + * INBOUND, OUTBOUND + * FWMARK + * + * if (flags & IP_MASQ_APP_FWMARK) + * [ f_bits][ fwmark (24 bits only) ] + * else + * [ f_bits][ proto ][ port ] + * + * <----------- 32 bits ------------> + * + */ + +#define IP_MASQ_APP_HASH(type) (((type>>16)^(type)) & (IP_MASQ_APP_TAB_SIZE-1)) EXPORT_SYMBOL(register_ip_masq_app); +EXPORT_SYMBOL(register_ip_masq_app_type); EXPORT_SYMBOL(unregister_ip_masq_app); EXPORT_SYMBOL(ip_masq_skb_replace); @@ -58,29 +73,53 @@ EXPORT_SYMBOL(ip_masq_skb_replace); struct ip_masq_app *ip_masq_app_base[IP_MASQ_APP_TAB_SIZE]; /* - * ip_masq_app registration routine - * port: host byte order. + * "raw" ip_masq_app registration routine if you + * did ip_masq_app_init_xxxx() before to initialize (and check) + * type value */ -int register_ip_masq_app(struct ip_masq_app *mapp, unsigned short proto, __u16 port) -{ +int register_ip_masq_app_type(struct ip_masq_app *mapp) { unsigned long flags; unsigned hash; +#ifdef CONFIG_IP_MASQ_DEBUG if (!mapp) { IP_MASQ_ERR("register_ip_masq_app(): NULL arg\n"); return -EINVAL; } - mapp->type = IP_MASQ_APP_TYPE(proto, port); +#endif + if (!mapp->type) { + IP_MASQ_DEBUG(1, "ip_masq_app_register_type(): ZERO type passed\n"); + return -EINVAL; + } + mapp->n_attach = 0; - hash = IP_MASQ_APP_HASH(proto, port); + hash = IP_MASQ_APP_HASH(mapp->type); + IP_MASQ_DEBUG(2, "ip_masq_app_register(): type=0x%x hash=0x%x\n", mapp->type, hash); save_flags(flags); cli(); mapp->next = ip_masq_app_base[hash]; ip_masq_app_base[hash] = mapp; restore_flags(flags); + return 0; +} - return 0; +/* + * ip_masq_app registration routine + * port: host byte order. + * proto: (flags<<8) | ipproto + */ +int register_ip_masq_app(struct ip_masq_app *mapp, unsigned proto, __u16 port) +{ +#ifdef CONFIG_IP_MASQ_DEBUG + if (!mapp) { + IP_MASQ_ERR("register_ip_masq_app(): NULL arg\n"); + return -EINVAL; + } +#endif + + ip_masq_app_init_proto_port(mapp, IP_MASQ_APP_OUTBOUND, proto, port); + return register_ip_masq_app_type(mapp); } /* @@ -104,7 +143,7 @@ int unregister_ip_masq_app(struct ip_masq_app *mapp) mapp->n_attach); return -EINVAL; } - hash = IP_MASQ_APP_HASH(IP_MASQ_APP_PROTO(mapp->type), IP_MASQ_APP_PORT(mapp->type)); + hash = IP_MASQ_APP_HASH(mapp->type); save_flags(flags); cli(); @@ -116,24 +155,26 @@ int unregister_ip_masq_app(struct ip_masq_app *mapp) } restore_flags(flags); - IP_MASQ_ERR("unregister_ip_masq_app(proto=%s,port=%u): not hashed!\n", - masq_proto_name(IP_MASQ_APP_PROTO(mapp->type)), IP_MASQ_APP_PORT(mapp->type)); + IP_MASQ_ERR("unregister_ip_masq_app(flags=0x%x, proto=%s,port=%u,fwmark=%d): not hashed!\n", + + IP_MASQ_APP_TYPE2FLAGS(mapp->type), + masq_proto_name(IP_MASQ_APP_TYPE2PROTO(mapp->type)), + IP_MASQ_APP_TYPE2PORT(mapp->type), + IP_MASQ_APP_TYPE2FWMARK(mapp->type)); return -EINVAL; } /* - * get ip_masq_app object by its proto and port (net byte order). + * get ip_masq_app object by its proto and port (host byte order). */ -struct ip_masq_app * ip_masq_app_get(unsigned short proto, __u16 port) +struct ip_masq_app * ip_masq_app_get_type(unsigned type) { struct ip_masq_app *mapp; unsigned hash; - unsigned type; - port = ntohs(port); - type = IP_MASQ_APP_TYPE(proto,port); - hash = IP_MASQ_APP_HASH(proto,port); + hash = IP_MASQ_APP_HASH(type); + IP_MASQ_DEBUG(2, "ip_masq_app_get(): type=0x%x hash=0x%x\n", type, hash); for(mapp = ip_masq_app_base[hash]; mapp ; mapp = mapp->next) { if (type == mapp->type) return mapp; } @@ -159,8 +200,8 @@ static __inline__ int ip_masq_app_bind_chg(struct ip_masq_app *mapp, int delta) if (n_at < 0) { restore_flags(flags); IP_MASQ_ERR("ip_masq_app: tried to set n_attach < 0 for (proto=%s,port==%d) ip_masq_app object.\n", - masq_proto_name(IP_MASQ_APP_PROTO(mapp->type)), - IP_MASQ_APP_PORT(mapp->type)); + masq_proto_name(IP_MASQ_APP_TYPE2PROTO(mapp->type)), + IP_MASQ_APP_TYPE2PORT(mapp->type)); return -1; } mapp->n_attach = n_at; @@ -168,42 +209,92 @@ static __inline__ int ip_masq_app_bind_chg(struct ip_masq_app *mapp, int delta) return 0; } +/* + * Actually bind pointers and call contructor + */ + +static struct ip_masq_app *do_bind_app(struct ip_masq *ms, struct ip_masq_app *mapp) +{ + /* + * don't allow binding if already bound + */ + if (ms->app != NULL) { + IP_MASQ_ERR("ip_masq_bind_app() called for already bound object.\n"); + goto end; + } + IP_MASQ_DEBUG(1, "ip_masq_bind_app() : bound \"%s\" module\n", mapp->name); + ms->app = mapp; + if (mapp->masq_init_1) mapp->masq_init_1(mapp, ms); + ip_masq_app_bind_chg(mapp, +1); +end: + return ms->app; +} + /* * Bind ip_masq to its ip_masq_app based on proto and dport ALREADY - * set in ip_masq struct. Also calls constructor. + * set in ip_masq struct. + * Only called from ip_masq_new() when creating NEW masq tunnel, */ struct ip_masq_app * ip_masq_bind_app(struct ip_masq *ms) { struct ip_masq_app * mapp; + unsigned type; if (ms->protocol != IPPROTO_TCP && ms->protocol != IPPROTO_UDP) return NULL; - mapp = ip_masq_app_get(ms->protocol, ms->dport); - -#if 0000 -/* #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW */ - if (mapp == NULL) - mapp = ip_masq_app_get(ms->protocol, ms->sport); -/* #endif */ -#endif - - if (mapp != NULL) { - /* - * don't allow binding if already bound - */ + IP_MASQ_DEBUG(1, "ip_masq_bind_app() : called for dst=%d.%d.%d.%d:%d src=%d.%d.%d.%d:%d masq=%d.%d.%d.%d:%d\n", + NIPQUAD(ms->daddr), ntohs(ms->dport), + NIPQUAD(ms->saddr), ntohs(ms->sport), + NIPQUAD(ms->maddr), ntohs(ms->mport)); - if (ms->app != NULL) { - IP_MASQ_ERR("ip_masq_bind_app() called for already bound object.\n"); - return ms->app; - } + /* + * Lookup "normal" case (client's active open inside) + */ + type = IP_MASQ_APP_TYPE_PP(IP_MASQ_APP_OUTBOUND, ms->protocol, ntohs(ms->dport)); + mapp = ip_masq_app_get_type(type); + + if (mapp==NULL) { + /* + * lookup "reverse" case (server waiting inside, + * for portfw and friends) + */ + type = IP_MASQ_APP_TYPE_PP(IP_MASQ_APP_INBOUND, ms->protocol, ntohs(ms->mport)); + mapp = ip_masq_app_get_type(type); + } - ms->app = mapp; - if (mapp->masq_init_1) mapp->masq_init_1(mapp, ms); - ip_masq_app_bind_chg(mapp, +1); - } - return mapp; + if (mapp != NULL) + mapp=do_bind_app(ms, mapp); + return mapp; +} +/* + * Bind ip_masq to its ip_masq_app based on firewall mark + */ +struct ip_masq_app * ip_masq_bind_app_fwmark(struct ip_masq *ms, __u32 fwmark) { + struct ip_masq_app *mapp; + unsigned type; + + IP_MASQ_DEBUG(1, "ip_masq_bind_app_fwmark() : called for fwmark=0x%x, dst=%d.%d.%d.%d:%d src=%d.%d.%d.%d:%d masq=%d.%d.%d.%d:%d\n", + fwmark, + NIPQUAD(ms->daddr), ntohs(ms->dport), + NIPQUAD(ms->saddr), ntohs(ms->sport), + NIPQUAD(ms->maddr), ntohs(ms->mport)); + + type = IP_MASQ_APP_TYPE_FWMARK(IP_MASQ_APP_OUTBOUND, fwmark); + mapp = ip_masq_app_get_type(type); + + if (mapp==NULL) { + /* + * lookup "reverse" case (server waiting inside, + * for portfw and friends) + */ + type = IP_MASQ_APP_TYPE_FWMARK(IP_MASQ_APP_INBOUND, fwmark); + mapp = ip_masq_app_get_type(type); + } + if (mapp != NULL) + mapp=do_bind_app(ms, mapp); + return mapp; } /* @@ -269,10 +360,14 @@ static __inline__ void masq_fix_ack_seq(const struct ip_masq_seq *ms_seq, struct * Adjust ack_seq with delta-offset for * the packets AFTER most recent resized pkt has caused a shift * for packets before most recent resized pkt, use previous_delta + * + * Compare acks against (remote SEQs space is +delta) + * (ms_seq->init_seq + ms_seq->delta), not ms_seq->init_seq + * -- R.R. 27/08/00 */ if (ms_seq->delta || ms_seq->previous_delta) { - if(after(ack_seq,ms_seq->init_seq)) { + if(after(ack_seq,ms_seq->init_seq + ms_seq->delta)) { th->ack_seq = htonl(ack_seq-ms_seq->delta); IP_MASQ_DEBUG(1, "masq_fix_ack_seq() : subtracted delta (%d) from ack_seq\n",ms_seq->delta); @@ -455,8 +550,8 @@ int ip_masq_app_getinfo(char *buffer, char **start, off_t offset, int length, in continue; len += sprintf(buffer+len, "%-3s %-7u %-7d %-17s\n", - masq_proto_name(IP_MASQ_APP_PROTO(mapp->type)), - IP_MASQ_APP_PORT(mapp->type), mapp->n_attach, + masq_proto_name(IP_MASQ_APP_TYPE2PROTO(mapp->type)), + IP_MASQ_APP_TYPE2PORT(mapp->type), mapp->n_attach, mapp->name); if(len >= length) @@ -497,7 +592,7 @@ __initfunc(int ip_masq_app_init(void)) * Replace a segment (of skb->data) with a new one. * FIXME: Should re-use same skb if space available, this could * be done if n_len < o_len, unless some extra space - * were already allocated at driver level :P . + * were already allocated at link level :P . */ static struct sk_buff * skb_replace(struct sk_buff *skb, int pri, char *o_buf, int o_len, char *n_buf, int n_len) diff --git a/net/ipv4/ip_masq_ftp.c b/net/ipv4/ip_masq_ftp.c index da09b6640acd..14c1cd357a8c 100644 --- a/net/ipv4/ip_masq_ftp.c +++ b/net/ipv4/ip_masq_ftp.c @@ -2,7 +2,7 @@ * IP_MASQ_FTP ftp masquerading module * * - * Version: @(#)ip_masq_ftp.c 0.04 02/05/96 + * Version: @(#)ip_masq_ftp.c 0.10 20/09/00 * * Author: Wouter Gadeyne * @@ -17,12 +17,15 @@ * Juan Jose Ciarlante : use ip_masq_listen() * Juan Jose Ciarlante : use private app_data for own flag(s) * Bjarni R. Einarsson : Added protection against "extended FTP ALG attack" - * + * Neil Toronto : portfw FTP support + * Juan Jose Ciarlante : reimplemented parsing logic, merged portfw FTP support for PASV (new "in_ports" module param), use th->doff for data offset + * Juan Jose Ciarlante : safe_mem_eq2() and size adjustments for less CPU + * Juan Jose Ciarlante : fwmark hook-able * * 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. + * 2 of the License. * * Multiple Port Support * The helper can be made to handle up to MAX_MASQ_APP_PORTS (normally 12) @@ -34,6 +37,16 @@ * /etc/conf.modules (or /etc/modules.conf depending on your config) * where modload will pick it up should you use modload to load your * modules. + * Additional portfw Port Support + * Module parameter "in_ports" specifies the list of forwarded ports + * at firewall (portfw and friends) that must be hooked to allow + * PASV connections to inside servers. + * Same as before: + * in_ports=fw1,fw2,... + * Eg: + * ipmasqadm portfw -a -P tcp -L a.b.c.d 2021 -R 192.168.1.1 21 + * ipmasqadm portfw -a -P tcp -L a.b.c.d 8021 -R 192.168.1.1 21 + * modprobe ip_masq_ftp in_ports=2021,8021 * * Protection against the "extended FTP ALG vulnerability". * This vulnerability was reported in: @@ -66,21 +79,51 @@ /* #define IP_MASQ_NDEBUG */ #include +/* + * paranoid, CPU care offset handling + */ +/* #define MASQ_FTP_RELAXED 1 */ +#ifdef MASQ_FTP_RELAXED +#define _N(x) 0 +#else +#define _N(x) (x) +#endif + +#define IP_MASQ_FTP_RPAREN 0x01 /* stream has ')' char */ +/* + * Eat 1 port (last elem) for holding firewall mark instance + */ +#define MAX_MASQ_FTP_PORTS (MAX_MASQ_APP_PORTS-1) +#define MAX_MASQ_FTP_PORTS_MODPARM 11 /* * List of ports (up to MAX_MASQ_APP_PORTS) to be handled by helper * First port is set to the default port. */ -static int ports[MAX_MASQ_APP_PORTS] = {21}; /* I rely on the trailing items being set to zero */ -struct ip_masq_app *masq_incarnations[MAX_MASQ_APP_PORTS]; +static int ports[MAX_MASQ_FTP_PORTS] = {21}; /* I rely on the trailing items being set to zero */ +static struct ip_masq_app *masq_ftp_objs[MAX_MASQ_APP_PORTS]; + +/* + * in (forwarded) ports + */ +static int in_ports[MAX_MASQ_FTP_PORTS] = {0}; +static struct ip_masq_app *masq_in_ftp_objs[MAX_MASQ_APP_PORTS]; + +#define masq_ftp_mark masq_ftp_objs[MAX_MASQ_APP_PORTS-1] +#define masq_in_ftp_mark masq_in_ftp_objs[MAX_MASQ_APP_PORTS-1] /* * List of ports (up to MAX_MASQ_APP_PORTS) we don't allow ftp-data * connections to. Default is to block connections to port 6000 (X servers). * This is in addition to all ports under 1024. */ -static int noport[MAX_MASQ_APP_PORTS] = {6000, 0}; /* I rely on the trailing items being set to zero */ +static int noport[MAX_MASQ_FTP_PORTS] = {6000, 0}; /* I rely on the trailing items being set to zero */ +/* + * Firewall marks for "normal" and "forw" cases + */ +static int mark=0; +static int in_mark=0; /* * Debug level */ @@ -89,12 +132,56 @@ static int debug=0; MODULE_PARM(debug, "i"); #endif -MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i"); -MODULE_PARM(noport, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i"); +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_FTP_PORTS_MODPARM) "i"); +MODULE_PARM(in_ports, "1-" __MODULE_STRING(MAX_MASQ_FTP_PORTS_MODPARM) "i"); +MODULE_PARM(noport, "1-" __MODULE_STRING(MAX_MASQ_FTP_PORTS_MODPARM) "i"); +MODULE_PARM(mark, "i"); +MODULE_PARM(in_mark, "i"); /* Dummy variable */ static int masq_ftp_pasv; +/* + * This function parses the IP address and Port number found in PORT commands + * and PASV responses. This used to be done in-line, but with four cases it + * seemed worth encapsulating. It returns the IP address, or zero if an + * error is detected. + */ +static __u32 parse_ip_port( char **datap, __u16 *portp ) +{ + char *data = *datap; +#if CONFIG_IP_MASQ_DEBUG + char *data0=data; +#endif + unsigned char p1,p2,p3,p4,p5,p6; + + p1 = simple_strtoul(data, &data, 10); + if (*data != ',') + return 0; + p2 = simple_strtoul(data+1, &data, 10); + if (*data != ',') + return 0; + p3 = simple_strtoul(data+1, &data, 10); + if (*data != ',') + return 0; + p4 = simple_strtoul(data+1, &data, 10); + if (*data != ',') + return 0; + p5 = simple_strtoul(data+1, &data, 10); + if (*data != ',') + return 0; + p6 = simple_strtoul(data+1, &data, 10); + + IP_MASQ_DEBUG(2-debug, "FTP: parse_ip_port() Ok: \"%*s\" size=%d\n", + data-data0, + data0, + data-data0); + *datap = data; + *portp = (p5<<8) | p6; + return (p1<<24) | (p2<<16) | (p3<<8) | p4; +} + + static int masq_ftp_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms) { @@ -109,138 +196,221 @@ masq_ftp_done_1 (struct ip_masq_app *mapp, struct ip_masq *ms) return 0; } + +static int masq_ftp_unsafe(__u32 from_ip, __u16 from_port) { + int i; + if (from_port < 1024) + { + IP_MASQ_DEBUG(1-debug, "Unsafe PORT %d.%d.%d.%d:%d detected, ignored\n",NIPQUAD(from_ip),from_port); + return 1; + } + + for (i = 0; (i < MAX_MASQ_FTP_PORTS) && (noport[i]); i++) + if (from_port == noport[i]) + { + IP_MASQ_DEBUG(1-debug, "Unsafe (module parm) PORT %d.%d.%d.%d:%d detected, ignored\n",NIPQUAD(from_ip),from_port); + return 1; + } + return 0; +} +/* + * carefully compare with any of these 2 strings, eating stream pointer + * as it proceeds + * Returns: + * NULL not matched + * !NULL last matched char* + */ +static char* safe_mem_eq2(char *data, const char *data_limit, int size, const char *str1, const char *str2) { +#if CONFIG_IP_MASQ_DEBUG + const char *data0=data; + if (!data) { + IP_MASQ_ERR("FTP: NULL data passed to safe_mem_eq2()!!!"); + return NULL; + } +#endif + IP_MASQ_DEBUG(3-debug, "FTP: safe_mem_equal(): datalimit-data=%d, size=%d\n", data_limit-data, size); + + /* No point in going after data_limit for "size" comparison */ + data_limit -= size; + + while (data <= data_limit) { + if (memcmp(data,str1,size)==0) + goto equal_ok; + if (str2 && memcmp(data,str2,size)==0) + goto equal_ok; + data++; + } + IP_MASQ_DEBUG(2-debug, "FTP: safe_mem_equal(): \"%s\" not matched)\n", str1); + return NULL; +equal_ok: + IP_MASQ_DEBUG(2-debug, "FTP: safe_mem_equal(): \"%s\" matched at offset %d\n", + str1, data-data0); + return data+size; +} int masq_ftp_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr) { struct sk_buff *skb; struct iphdr *iph; struct tcphdr *th; - char *p, *data, *data_limit; - unsigned char p1,p2,p3,p4,p5,p6; - __u32 from; + char *p, *data, *data0, *data_limit; + __u32 from=0; + __u32 from_n; __u16 port; struct ip_masq *n_ms; - char buf[24]; /* xxx.xxx.xxx.xxx,ppp,ppp\000 */ + char buf[25]; /* xxx.xxx.xxx.xxx,ppp,ppp)\000 */ + unsigned flags=0; /* processing flags */ unsigned buf_len; - int diff, i, unsafe; + int diff=0; + + /* Only useful for established sessions */ + if (ms->state != IP_MASQ_S_ESTABLISHED) + return 0; skb = *skb_p; iph = skb->nh.iph; th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); - data = (char *)&th[1]; + data = (char *)th + (((struct tcphdr*)th)->doff << 2); + data0 = data; + data_limit = skb->h.raw + skb->len; - data_limit = skb->h.raw + skb->len - 18; - if (skb->len >= 6 && (memcmp(data, "PASV\r\n", 6) == 0 || memcmp(data, "pasv\r\n", 6) == 0)) - ms->app_data = &masq_ftp_pasv; + IP_MASQ_DEBUG(2-debug, "FTP: called masq_ftp_out() for type=0x%x datasize=%d\n", + mapp->type, + data_limit-data); - while (data < data_limit) - { - if (memcmp(data,"PORT ",5) && memcmp(data,"port ",5)) - { - data ++; - continue; - } - p = data+5; - p1 = simple_strtoul(data+5,&data,10); - if (*data!=',') - continue; - p2 = simple_strtoul(data+1,&data,10); - if (*data!=',') - continue; - p3 = simple_strtoul(data+1,&data,10); - if (*data!=',') - continue; - p4 = simple_strtoul(data+1,&data,10); - if (*data!=',') - continue; - p5 = simple_strtoul(data+1,&data,10); - if (*data!=',') - continue; - p6 = simple_strtoul(data+1,&data,10); - if (*data!='\r' && *data!='\n') - continue; - - from = (p1<<24) | (p2<<16) | (p3<<8) | p4; - port = (p5<<8) | p6; - - if (port < 1024) - { - IP_MASQ_DEBUG(1-debug, "Unsafe PORT %X:%X detected, ignored\n",from,port); - continue; - } + /* Only useful for actual data */ + if (data_limit<=data) + return 0; - for (unsafe = i = 0; (i < MAX_MASQ_APP_PORTS) && (noport[i]); i++) - if (port == noport[i]) - { - IP_MASQ_DEBUG(1-debug, "Unsafe PORT %X:%X detected, ignored\n",from,port); - unsafe = 1; + /* + * We are about to hack an OUT (from firewall) packet, + * check if + * OUTBOUND: internal client stream + * INBOUND: internal server stream + */ + + if (IP_MASQ_APP_TYPE2FLAGS(mapp->type)&IP_MASQ_APP_OUTBOUND) { + IP_MASQ_DEBUG(1-debug, "FTP: in->out client stream\n"); + + /* + * Minimum sizes ... + * PORT x,x,x,x,y,y+... + \r\n + * <---------- 11 + 2 = 13 + */ + if (!(data=safe_mem_eq2(data, data_limit-_N(13), + 5, "PORT ", "port ")) ) { + if (safe_mem_eq2(data0, data_limit, 6, "PASV\r\n", "pasv\r\n")) { + /* Flags this tunnel as pasv, return */ + ms->app_data = &masq_ftp_pasv; + } + return 0; } - if (unsafe) continue; + p = data; + from = parse_ip_port(&data, &port); + if (masq_ftp_unsafe(from,port)) + return 0; + IP_MASQ_DEBUG(1-debug,"FTP: out: PORT %d.%d.%d.%d:%d detected\n", + NIPQUAD(from), port); + + } else if (IP_MASQ_APP_TYPE2FLAGS(mapp->type)&IP_MASQ_APP_INBOUND) { + IP_MASQ_DEBUG(1-debug, "FTP: in->out server stream\n"); + + /* + * Minimum sizes... + * Entering Passive Mode (x,x,x,x,y,y)+... + \r\n + * <------------------------- 26 + 2 = 28 + * <----------------- 18 + 2 = 20 + * <------------ 13 + 2 = 15 + */ + if (!(data=safe_mem_eq2(data, data_limit-_N(28), 8, "ntering ", "NTERING "))) + return 0; + if (!(data=safe_mem_eq2(data+_N(1), data_limit-_N(20), 7, "assive ", "ASSIVE "))) + return 0; + if (!(data=safe_mem_eq2(data+_N(1), data_limit-_N(15), 4, "ode ", "ODE "))) + return 0; + do { + if (data >= data_limit) + return 0; + } while (*data++ != '('); - IP_MASQ_DEBUG(1-debug, "PORT %X:%X detected\n",from,port); + p = data; + from = parse_ip_port(&data, &port); + if ((from == 0) || (*data++ !=')')) + return 0; - /* - * Now update or create an masquerade entry for it - */ + flags |= IP_MASQ_FTP_RPAREN; - IP_MASQ_DEBUG(1-debug, "protocol %d %lX:%X %X:%X\n", iph->protocol, htonl(from), htons(port), iph->daddr, 0); + } else + return 0; - n_ms = ip_masq_out_get(iph->protocol, - htonl(from), htons(port), - iph->daddr, 0); - if (!n_ms) { - n_ms = ip_masq_new(IPPROTO_TCP, - maddr, 0, - htonl(from), htons(port), - iph->daddr, 0, - IP_MASQ_F_NO_DPORT); + /* no from detected, give up */ + if (from == 0) + return 0; - if (n_ms==NULL) - return 0; - ip_masq_control_add(n_ms, ms); - } + /* store from in network byte order */ + from_n = htonl(from); + /* + * Now update or create an masquerade entry for it + */ - /* - * Replace the old PORT with the new one - */ - from = ntohl(n_ms->maddr); - port = ntohs(n_ms->mport); - sprintf(buf,"%d,%d,%d,%d,%d,%d", - from>>24&255,from>>16&255,from>>8&255,from&255, - port>>8&255,port&255); - buf_len = strlen(buf); + IP_MASQ_DEBUG(1-debug, "FTP: out: %d.%d.%d.%d:%d -> %d.%d.%d.%d:%d\n", + NIPQUAD(from_n), htons(port), + NIPQUAD(iph->daddr), 0); - IP_MASQ_DEBUG(1-debug, "new PORT %X:%X\n",from,port); + n_ms = ip_masq_out_get(iph->protocol, + from_n, htons(port), + iph->daddr, 0); + if (!n_ms) { + n_ms = ip_masq_new(IPPROTO_TCP, + maddr, 0, + from_n, htons(port), + iph->daddr, 0, + IP_MASQ_F_NO_DPORT); + if (n_ms==NULL) + return 0; + ip_masq_control_add(n_ms, ms); + } + + /* + * Replace the old PORT with the new one + */ + from = ntohl(n_ms->maddr); + port = ntohs(n_ms->mport); + sprintf(buf,"%d,%d,%d,%d,%d,%d%c", + from>>24&255,from>>16&255,from>>8&255,from&255, + port>>8&255,port&255, + (flags&IP_MASQ_FTP_RPAREN)? ')':0); + buf_len = strlen(buf); + + IP_MASQ_DEBUG(1-debug, "FTP: new PORT %d.%d.%d.%d:%d\n",NIPQUAD(maddr),port); + + /* + * Calculate required delta-offset to keep TCP happy + */ + + diff = buf_len - (data-p); + + /* + * No shift. + */ + + if (diff==0) { /* - * Calculate required delta-offset to keep TCP happy - */ - - diff = buf_len - (data-p); - - /* - * No shift. + * simple case, just replace the old PORT cmd */ - - if (diff==0) { - /* - * simple case, just replace the old PORT cmd - */ - memcpy(p,buf,buf_len); - } else { - - *skb_p = ip_masq_skb_replace(skb, GFP_ATOMIC, p, data-p, buf, buf_len); - } - /* - * Move tunnel to listen state - */ - ip_masq_listen(n_ms); - ip_masq_put(n_ms); - - return diff; + memcpy(p,buf,buf_len); + } else { + *skb_p = ip_masq_skb_replace(skb, GFP_ATOMIC, p, data-p, buf, buf_len); } - return 0; + /* + * Move tunnel to listen state + */ + ip_masq_listen(n_ms); + ip_masq_put(n_ms); + + return diff; } @@ -269,53 +439,92 @@ masq_ftp_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_ struct iphdr *iph; struct tcphdr *th; char *data, *data_limit; - unsigned char p1,p2,p3,p4,p5,p6; __u32 to; + __u32 from_n; __u16 port; struct ip_masq *n_ms; - if (ms->app_data != &masq_ftp_pasv) - return 0; /* quick exit if no outstanding PASV */ + /* Only useful for established sessions */ + if (ms->state != IP_MASQ_S_ESTABLISHED) + return 0; skb = *skb_p; iph = skb->nh.iph; th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); - data = (char *)&th[1]; + data = (char *)th + (((struct tcphdr*)th)->doff << 2); data_limit = skb->h.raw + skb->len; - while (data < data_limit && *data != ' ') - ++data; - while (data < data_limit && *data == ' ') - ++data; - data += 22; - if (data >= data_limit || *data != '(') - return 0; - p1 = simple_strtoul(data+1, &data, 10); - if (data >= data_limit || *data != ',') - return 0; - p2 = simple_strtoul(data+1, &data, 10); - if (data >= data_limit || *data != ',') - return 0; - p3 = simple_strtoul(data+1, &data, 10); - if (data >= data_limit || *data != ',') - return 0; - p4 = simple_strtoul(data+1, &data, 10); - if (data >= data_limit || *data != ',') - return 0; - p5 = simple_strtoul(data+1, &data, 10); - if (data >= data_limit || *data != ',') - return 0; - p6 = simple_strtoul(data+1, &data, 10); - if (data >= data_limit || *data != ')') + IP_MASQ_DEBUG(2-debug, "FTP: called masq_ftp_in() for type=0x%x datasize=%d\n", + mapp->type, + data_limit-data); + + /* Only useful for actual data */ + if (data_limit<=data) return 0; - to = (p1<<24) | (p2<<16) | (p3<<8) | p4; - port = (p5<<8) | p6; + /* + * We are about to hack an IN (to firewall) packet, + * check if + * OUTBOUND: internal client stream + * INBOUND: internal server stream + */ + + if (IP_MASQ_APP_TYPE2FLAGS(mapp->type)&IP_MASQ_APP_OUTBOUND) { + IP_MASQ_DEBUG(1-debug, "FTP: out->in client stream\n"); + /* + * For OUTBOUND only parse on input for linking PASV + * data tunnel with control. + * Exit quickly if no outstanding PASV + */ + if (ms->app_data != &masq_ftp_pasv) + return 0; + + /* + * Minimum sizes... + * Entering Passive Mode (x,x,x,x,y,y)+... + \r\n + * <------------------------- 26 + 2 = 28 + * <----------------- 18 + 2 = 20 + * <------------ 13 + 2 = 15 + */ + if (!(data=safe_mem_eq2(data, data_limit-_N(28), 8, "ntering ", "NTERING "))) + return 0; + if (!(data=safe_mem_eq2(data+_N(1), data_limit-_N(20), 6, "ssive ", "SSIVE "))) + return 0; + if (!(data=safe_mem_eq2(data+_N(1), data_limit-_N(15), 4, "ode ", "ODE "))) + return 0; + do { + if (data >= data_limit) + return 0; + } while (*data++ != '('); + + to = parse_ip_port(&data, &port); + if (to == 0 || *data != ')') + return 0; + + from_n = ntohl(ms->saddr); + IP_MASQ_DEBUG(1-debug, "FTP: PASV response %d.%d.%d.%d:%d -> %d.%d.%d.%d:%d detected\n", + NIPQUAD(from_n), 0, + NIPQUAD(to), port); + } else if (IP_MASQ_APP_TYPE2FLAGS(mapp->type)&IP_MASQ_APP_INBOUND) { + IP_MASQ_DEBUG(1-debug, "FTP: out->in server stream\n"); + + if (!(data=safe_mem_eq2(data, data_limit-_N(13), + 5, "PORT ", "port ")) ) + return 0; + to = parse_ip_port(&data, &port); + if (to == 0 || (*data != '\r' && *data != '\n')) + return 0; + + from_n = ntohl(ms->saddr); + IP_MASQ_DEBUG(1-debug, "FTP: PORT %d.%d.%d.%d:%d -> %d.%d.%d.%d:%d detected\n", + NIPQUAD(from_n), 0, + NIPQUAD(to), port); + } else + return 0; /* * Now update or create an masquerade entry for it */ - IP_MASQ_DEBUG(1-debug, "PASV response %lX:%X %X:%X detected\n", ntohl(ms->saddr), 0, to, port); n_ms = ip_masq_out_get(iph->protocol, ms->saddr, 0, @@ -356,60 +565,122 @@ struct ip_masq_app ip_masq_ftp = { masq_ftp_out, /* pkt_out */ masq_ftp_in, /* pkt_in */ }; - -/* - * ip_masq_ftp initialization - */ - -__initfunc(int ip_masq_ftp_init(void)) +static struct ip_masq_app *make_instance(const struct ip_masq_app *mapp_class, int *err) { - int i, j; + struct ip_masq_app *mapp = NULL; + if ((mapp = kmalloc(sizeof(struct ip_masq_app), GFP_KERNEL))) + memcpy(mapp , mapp_class, sizeof(struct ip_masq_app)); + else + *err= -ENOMEM; + return mapp; +} - for (i=0; (itype); } else { /* To be safe, force the incarnation table entry to NULL */ - masq_incarnations[i] = NULL; + mapp_instances[i] = NULL; } } return 0; +end_kfree: + kfree_s(mapp, sizeof(struct ip_masq_app)); +end: + IP_MASQ_ERR("FTP: registration error, quitting\n"); + return err; } - /* - * ip_masq_ftp fin. + * Unregister ALL ports, _including_ the (possible) firewall + * mark. */ - -int ip_masq_ftp_done(void) -{ - int i, j, k; - - k=0; +static int unregister_ports(struct ip_masq_app *mapp_instances[]) { + int i, j, k=0; for (i=0; (itype); + kfree(mapp_instances[i]); + mapp_instances[i] = NULL; } } } return k; } +/* + * ip_masq_ftp initialization + */ + +__initfunc(int ip_masq_ftp_init(void)) +{ + int ret=0; + struct ip_masq_app *mapp; + if ( + (ret=register_ports(masq_ftp_objs, ports, IP_MASQ_APP_OUTBOUND)) || + (ret=register_ports(masq_in_ftp_objs, in_ports, IP_MASQ_APP_INBOUND)) + ) + goto end; + + if (mark) { + if (!(mapp=make_instance(&ip_masq_ftp, &ret))) + goto end; + if ((ret=ip_masq_app_init_fwmark(mapp, IP_MASQ_APP_OUTBOUND, mark))) { + kfree_s(mapp, sizeof (struct ip_masq_app)); + goto end; + } + if ((ret=register_ip_masq_app_type(mapp))) + goto end; + masq_ftp_mark=mapp; + } + if (in_mark) { + if (!(mapp=make_instance(&ip_masq_ftp, &ret))) + goto end; + if ((ret=ip_masq_app_init_fwmark(mapp, IP_MASQ_APP_INBOUND, in_mark))) { + kfree_s(mapp, sizeof (struct ip_masq_app)); + goto end; + } + if ((ret=register_ip_masq_app_type(mapp))) + goto end; + masq_in_ftp_mark=mapp; + } +end: + return ret; +} + +/* + * ip_masq_ftp fin. + */ + +int ip_masq_ftp_done(void) +{ + int ret; + ret = unregister_ports(masq_ftp_objs); + ret += unregister_ports(masq_in_ftp_objs); + return ret; +} + #ifdef MODULE EXPORT_NO_SYMBOLS; diff --git a/net/ipv4/ip_masq_user.c b/net/ipv4/ip_masq_user.c index eb56019420bb..8a2935d48b9c 100644 --- a/net/ipv4/ip_masq_user.c +++ b/net/ipv4/ip_masq_user.c @@ -35,7 +35,6 @@ */ static int debug=0; -MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i"); MODULE_PARM(debug, "i"); /* diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 3eb6c3b60442..1c1d0a3ab47c 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1619,6 +1619,7 @@ static struct open_request * wait_for_connect(struct sock * sk, struct wait_queue wait = { current, NULL }; struct open_request *req; + current->task_exclusive = 1; add_wait_queue(sk->sleep, &wait); for (;;) { current->state = TASK_INTERRUPTIBLE; @@ -1632,6 +1633,8 @@ static struct open_request * wait_for_connect(struct sock * sk, break; } current->state = TASK_RUNNING; + wmb(); + current->task_exclusive = 0; remove_wait_queue(sk->sleep, &wait); return req; } diff --git a/net/netsyms.c b/net/netsyms.c index b6832191020c..58150e1a8564 100644 --- a/net/netsyms.c +++ b/net/netsyms.c @@ -208,7 +208,7 @@ EXPORT_SYMBOL(__scm_send); /* Needed by unix.o */ EXPORT_SYMBOL(scm_fp_dup); -EXPORT_SYMBOL(max_files); +EXPORT_SYMBOL(files_stat); EXPORT_SYMBOL(do_mknod); EXPORT_SYMBOL(memcpy_toiovec); EXPORT_SYMBOL(csum_partial); diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 387192fa97b5..9e7c531fffef 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -417,7 +417,7 @@ static struct sock * unix_create1(struct socket *sock, int stream) { struct sock *sk; - if (atomic_read(&unix_nr_socks) >= 2*max_files) + if (atomic_read(&unix_nr_socks) >= 2*files_stat.max_files) return NULL; MOD_INC_USE_COUNT; -- 2.39.5