diff -u --recursive --new-file v2.2.13/linux/CREDITS linux/CREDITS --- v2.2.13/linux/CREDITS Tue Jan 4 11:24:09 2000 +++ linux/CREDITS Tue Jan 4 10:12:10 2000 @@ -137,12 +137,9 @@ S: USA N: James Banks -E: james.banks@caldera.com +E: james@sovereign.org D: TLAN network driver -S: Caldera, Inc. -S: 633 South 550 East -S: Provo, Utah 84606 -S: USA +D: Logitech Busmouse driver N: Paul Barton-Davis E: pbd@op.net @@ -300,6 +297,10 @@ S: Amsterdam S: The Netherlands +N: Zach Brown +E: zab@zabbo.net +D: maestro pci sound + N: Ray Burr E: ryb@nightmare.com D: Original author of Amiga FFS filesystem @@ -789,12 +790,12 @@ D: Selection mechanism N: Jochen Hein -E: jochen.hein@delphi.central.de +E: jochen@jochen.org P: 1024/4A27F015 25 72 FB E3 85 9F DE 3B CB 0A DA DA 40 77 05 6C D: National Language Support D: Linux Internationalization Project D: German Localization for Linux and GNU software -S: Frankenstraße +S: Frankenstraße 33 S: 34131 Kassel S: Germany @@ -866,7 +867,7 @@ S: United Kingdom N: Ron Holt -E: ron@caldera.com +E: ron@sovereign.org W: http://www.holt.org/ P: 1024/1FD44539 DF 4B EB 9F 5B 68 38 9A 40 E3 FB 71 D1 C8 0B 56 D: Kernel development @@ -942,12 +943,12 @@ S: United Kingdom N: Jakub Jelinek -E: jj@sunsite.mff.cuni.cz +E: jakub@redhat.com W: http://sunsite.mff.cuni.cz/~jj P: 1024/0F7623C5 53 95 71 3C EB 73 99 97 02 49 40 47 F9 19 68 20 D: Sparc hacker, SILO, mc D: Maintain sunsite.mff.cuni.cz -S: Na Orechovce 7 +S: K osmidomkum 723 S: 160 00 Praha 6 S: Czech Republic @@ -977,12 +978,14 @@ N: Dave Jones E: dave@powertweak.com +E: djones2@glam.ac.uk W: http://linux.powertweak.com -D: Centaur/IDT Winchip/Winchip 2 tweaks +D: Userspace PCI bridge tuning (Powertweak). +D: Centaur/IDT Winchip/Winchip 2 tweaks. D: Misc clean ups and other random hacking. -S: 40, Heol Edward Lewis, -S: Gelligaer, Hengoed, -S: Mid Glamorgan, CF82 8EJ, +S: 28 Laura Street, +S: Treforest, Pontypridd, +S: Mid Glamorgan, CF37 1NW, S: Wales, United Kingdom N: Bernhard Kaindl @@ -997,6 +1000,7 @@ N: Jan Kara E: jack@atrey.karlin.mff.cuni.cz +E: jack@suse.cz D: Quota fixes for 2.2 kernel D: Few other fixes in filesystem area (isofs, loopback) W: http://atrey.karlin.mff.cuni.cz/~jack/ @@ -1282,11 +1286,11 @@ S: Czech Republic N: Paul Mackerras -E: paulus@cs.anu.edu.au +E: paulus@linuxcare.com D: Linux port for PCI Power Macintosh -S: Dept. of Computer Science -S: Australian National University -S: Canberra ACT 0200 +S: Linuxcare, Inc. +S: 24 Marcus Clarke Street +S: Canberra ACT 2601 S: Australia N: Pat Mackinlay @@ -1535,7 +1539,7 @@ S: Australia N: Greg Page -E: greg@caldera.com +E: gpage@sovereign.org D: IPX development and support N: David Parsons @@ -1543,10 +1547,11 @@ D: improved memory detection code. N: Vojtech Pavlik -E: vojtech@ucw.cz +E: vojtech@suse.cz D: Joystick driver D: arcnet-hardware readme D: Minor ARCnet hacking +D: USB development S: Ucitelska 1576 S: Prague 8 S: 182 00 Czech Republic @@ -1713,8 +1718,8 @@ S: Germany N: Stephen Rothwell -E: Stephen.Rothwell@canb.auug.org.au -W: http://www.canb.auug.org.au/~sfr +E: sfr@linuxcare.com +W: http://linuxcare.com.au/sfr P: 1024/BD8C7805 CD A4 9D 01 10 6E 7E 3B 91 88 FA D9 C8 40 AA 02 D: Boot/setup/build work for setup > 2K D: Author, APM driver @@ -1963,7 +1968,8 @@ S: Canada K2L 1S2 N: Andrew Tridgell -E: Andrew.Tridgell@anu.edu.au +E: tridge@samba.org +W: http://linuxcare.com.au/tridge/ D: dosemu, networking, samba S: 3 Ballow Crescent S: MacGregor A.C.T 2615 @@ -2122,9 +2128,10 @@ N: Tim Waugh E: tim@cyberelk.demon.co.uk D: Co-architect of the parallel-port sharing system -S: 4 Fox Close -S: Bishopstoke -S: SO50 8NB +S: 34 Bladon Close +S: GUILDFORD +S: Surrey +S: GU1 1TY S: United Kingdom N: Juergen Weigert diff -u --recursive --new-file v2.2.13/linux/Documentation/Changes linux/Documentation/Changes --- v2.2.13/linux/Documentation/Changes Mon Aug 9 16:05:54 1999 +++ linux/Documentation/Changes Tue Jan 4 10:12:10 2000 @@ -43,7 +43,7 @@ for all your Linux kernel needs. -Last updated: July 13, 1999 +Last updated: October 13, 1999 Current Author: Chris Ricker (kaboom@gatech.edu or chris.ricker@genetics.utah.edu). @@ -61,19 +61,20 @@ - Linux libc6 C Library 2.0.7pre6 ; ls -l /lib/libc* - Dynamic Linker (ld.so) 1.9.9 ; ldd --version or ldd -v - Linux C++ Library 2.7.2.8 ; ls -l /usr/lib/libg++.so.* -- Procps 1.2.9 ; ps --version +- Procps 2.0.3 ; ps --version - Procinfo 16 ; procinfo -v - Psmisc 17 ; pstree -V - Net-tools 1.52 ; hostname -V - Loadlin 1.6a - Sh-utils 1.16 ; basename --v -- Autofs 3.1.1 ; automount --version +- Autofs 3.1.3 ; automount --version - NFS 2.2beta40 ; showmount --version - Bash 1.14.7 ; bash -version - Ncpfs 2.2.0 ; ncpmount -v -- Pcmcia-cs 3.0.13 ; cardmgr -V -- PPP 2.3.8 ; pppd --version -- Util-linux 2.9t ; chsh -v +- Pcmcia-cs 3.0.14 ; cardmgr -V +- PPP 2.3.10 ; pppd --version +- Util-linux 2.9z ; chsh -v +- isdn4k-utils v3.1beta7 ; isdnctrl 2>&1|grep version Upgrade notes ************* @@ -226,7 +227,7 @@ Note that the latest compilers (egcs, pgcc, gcc 2.8) may do Bad Things while compiling your kernel, particularly if absurd optimizations (like -O9) are used. Caveat emptor. Currently, the only -C compiler available in a binary distribution is egcs. Version 1.0.3 +C compiler available in a binary distribution is egcs. Version 1.1.2 seems okay; if you have to have a binary, you may be successful using that. In general, however, gcc-2.7.2.3 is known to be stable, while egcs and others have not been as thoroughly tested yet. @@ -272,9 +273,6 @@ DHCP clients for 2.0 do not work with the new networking code in the 2.2 kernel. You will need to upgrade your dhcpcd / dhcpclient. - The ISDN code in the stock 2.2 kernel may not work for you. If it -doesn't, look in ftp://ftp.suse.com/pub/isdn4linux for updated versions. - In 2.0.x the kernel could be configured to drop source routed IP packets via a compile time configuration option. In 2.2.x, this has been replaced by a sysctl. See Documentation/networking/ip-sysctl.txt @@ -284,8 +282,8 @@ ====== As of 2.1.41, the format of /proc/meminfo has changed. This broke -many memory utils, which have to be upgraded. Get the new procps-1.2 -and you should be set. +many memory utils, which have to be upgraded. Get the new procps and +you should be set. Network File System =================== @@ -513,29 +511,26 @@ so be sure to check that when you recompile. +ISDN4Linux +========== +Older isdn4k-utils versions don't support EXTRAVERSION into kernel version +string. A upgrade to isdn4k-utils.v3.1beta7 or later is recomented. + Where to get the files ********************** Binutils ======== -The 2.8.1.0.23 release: -ftp://tsx-11.mit.edu/pub/linux/packages/GCC/binutils-2.8.1.0.23.bin.tar.gz -ftp://metalab.unc.edu/pub/Linux/GCC/binutils-2.8.1.0.23.bin.tar.gz +The 2.9.1.0.25 release: +ftp://ftp.varesearch.com/pub/support/hjl/binutils/2.9.1/binutils-2.9.1.0.25.tar.gz Installation notes: -ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.binutils-2.8.1.0.23 -ftp://metalab.unc.edu/pub/Linux/GCC/release.binutils-2.8.1.0.23 +ftp://ftp.varesearch.com/pub/support/hjl/binutils/2.9.1/release.binutils-2.9.1.0.25 -The 2.9.1.0.15 release: -ftp://tsx-11.mit.edu/pub/linux/packages/GCC/binutils-2.9.1.0.15-glibc.x86.tar.gz -ftp://tsx-11.mit.edu/pub/linux/packages/GCC/binutils-2.9.1.0.15-libc5.x86.tar.gz -ftp://tsx-11.mit.edu/pub/linux/packages/GCC/binutils-2.9.1.0.15.tar.gz -ftp://metalab.unc.edu/pub/Linux/GCC/binutils-2.9.1.0.15-glibc.x86.tar.gz -ftp://metalab.unc.edu/pub/Linux/GCC/binutils-2.9.1.0.15-libc5.x86.tar.gz -ftp://metalab.unc.edu/pub/Linux/GCC/binutils-2.9.1.0.15.tar.gz +The 2.9.5.0.16 release: +ftp://ftp.varesearch.com/pub/support/hjl/binutils/binutils-2.9.5.0.16.tar.bz2 Installation notes: -ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.binutils-2.9.1.0.15 -ftp://metalab.unc.edu/pub/Linux/GCC/release.binutils-2.9.1.0.15 +ftp://ftp.varesearch.com/pub/support/hjl/binutils/release.binutils-2.9.5.0.16 Bin86 ===== @@ -547,14 +542,12 @@ Gnu C ===== -The egcs-1.0.3 release: -ftp://tsx-11.mit.edu/pub/linux/packages/GCC/egcs-1.0.3-glibc.x86.tar.bz2 -ftp://tsx-11.mit.edu/pub/linux/packages/GCC/egcs-1.0.3-libc5.x86.tar.bz2 -ftp://metalab.unc.edu/pub/Linux/GCC/egcs-1.0.3-glibc.x86.tar.bz2 -ftp://metalab.unc.edu/pub/Linux/GCC/egcs-1.0.3-libc5.x86.tar.bz2 +The egcs-1.1.2 release: +ftp://ftp.varesearch.com/pub/support/hjl/gcc/egcs-1.1.2/egcs-1.1.2-glibc.x86.tar.bz2 +ftp://ftp.varesearch.com/pub/support/hjl/gcc/egcs-1.1.2/egcs-1.1.2-libc5.x86.tar.bz2 +ftp://ftp.varesearch.com/pub/support/hjl/gcc/egcs-1.1.2/egcs-1.1.2-alpha.x86.tar.bz2 Installation notes: -ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.egcs-1.0.3 -ftp://metalab.unc.edu/pub/Linux/GCC/release.egcs-1.0.3 +ftp://ftp.varesearch.com/pub/support/hjl/gcc/egcs-1.1.2/release.egcs-1.1.2 Gnu C 2.7.2.3 source: ftp://ftp.gnu.org/gnu/gcc/gcc-2.7.2.3.tar.gz @@ -596,15 +589,14 @@ Procps utilities ================ -The 1.2 release: -ftp://tsx-11.mit.edu/pub/linux/sources/usr.bin/procps-1.2.9.tar.gz -ftp://metalab.unc.edu/pub/Linux/system/status/ps/procps-1.2.9.tgz +The 2.0.3 release: +ftp://tsx-11.mit.edu/pub/linux/sources/usr.bin/procps-2.0.3.tar.gz Procinfo utilities ================== -The 16 release: -ftp://ftp.cistron.nl/pub/people/svm/procinfo-16.tar.gz +The 17 release: +ftp://ftp.cistron.nl/pub/people/svm/procinfo-17.tar.gz Psmisc utilities ================ @@ -624,7 +616,6 @@ ====== The 0.98.6 release: -ftp://tsx-11.mit.edu/pub/linux/ALPHA/dosemu/dosemu-0.98.6.tgz ftp://ftp.dosemu.org/dosemu/dosemu-0.98.6.tgz Loadlin @@ -645,7 +636,7 @@ ========== The 2.9 release: -ftp://ftp.win.tue.nl/pub/linux/utils/util-linux/util-linux-2.9t.tar.gz +ftp://ftp.win.tue.nl/pub/linux/utils/util-linux/util-linux-2.9z.tar.gz Autofs ====== @@ -660,9 +651,9 @@ ftp://ftp.mathematik.th-darmstadt.de/pub/linux/okir/dontuse/nfs-server-2.2beta40.tar.gz ftp://linux.nrao.edu/mirrors/fb0429.mathematik.th-darmstadt.de/pub/linux/okir/dontuse/nfs-server-2.2beta40.tar.gz -The kernel-level 1.4.4 release: -ftp://ftp.varesearch.com/pub/support/hjl/knfsd/knfsd-1.4.4.tar.gz -ftp://ftp.kernel.org/pub/linux/devel/gcc/knfsd-1.4.4.tar.gz +The kernel-level 1.4.7 release: +ftp://ftp.varesearch.com/pub/support/hjl/knfsd/knfsd-1.4.7.tar.gz +ftp://ftp.kernel.org/pub/linux/devel/gcc/knfsd-1.4.7.tar.gz Net-tools ========= @@ -707,8 +698,8 @@ Pcmcia-cs ========= -The 3.0.13 release: -ftp://hyper.stanford.edu/pub/pcmcia/pcmcia-cs.3.0.13.tar.gz +The 3.0.14 release: +ftp://sourceforge.org/pcmcia/pcmcia-cs.3.0.14.tar.gz Setserial ========= @@ -720,15 +711,15 @@ PPP === -The 2.3.8 release: -ftp://cs.anu.edu.au/pub/software/ppp/ppp-2.3.8.tar.gz +The 2.3.10 release: +ftp://cs.anu.edu.au/pub/software/ppp/ppp-2.3.10.tar.gz IP Chains ========= -The 1.3.8 release: -ftp://ftp.rustcorp.com/ipchains/ipchains-1.3.8.tar.gz -ftp://ftp.rustcorp.com/ipchains/ipchains-1.3.8.tar.bz2 +The 1.3.9 release: +ftp://ftp.rustcorp.com/ipchains/ipchains-1.3.9.tar.gz +ftp://ftp.rustcorp.com/ipchains/ipchains-1.3.9.tar.bz2 IP Masq Adm =========== @@ -739,11 +730,11 @@ DHCP clients ============ -The 2.0b1pl27 ISC dhcpclient release: -ftp://ftp.isc.org/isc/dhcp/test/dhcp-2.0b1pl27.tar.gz +The 2.0 ISC dhcpclient release: +ftp://ftp.isc.org/isc/dhcp/test/dhcp-2.0.tar.gz -The 1.3.17-pl2 PhysTech dhcpcd release: -ftp://ftp.phystech.com/pub/dhcpcd-1.3.17-pl2.tar.gz +The 1.3.18-pl1 PhysTech dhcpcd release: +ftp://ftp.phystech.com/pub/dhcpcd-1.3.18-pl1.tar.gz iBCS ==== @@ -754,8 +745,8 @@ Asun netatalk ============= -The 2.0a18.2 release: -ftp://ftp.u.washington.edu/pub/user-supported/asun/netatalk-1.4b2+asun2.0a18.2.tar.gz +The 2.1.3 release: +ftp://ftp.u.washington.edu/pub/user-supported/asun/netatalk-1.4b2+asun2.1.3.tar.gz Fbset ===== @@ -792,15 +783,20 @@ IP utils ======== -The June 1999 release: -ftp://ftp.inr.ac.ru/ip-routing/iproute2-2.2.4-now-ss990630.tar.gz -ftp://ftp.inr.ac.ru/ip-routing/iputils-ss990610.tar.gz +The latest releases: +ftp://ftp.inr.ac.ru/ip-routing/iproute2-2.2.4-now-ss990824.tar.gz +ftp://ftp.inr.ac.ru/ip-routing/iputils-ss990915.tar.gz Patch ===== The 2.5 release: ftp://ftp.gnu.org/gnu/patch/patch-2.5.tar.gz + +ISDN4Linux +========== +The v3.1beta7 release: +ftp://ftp.isdn4linux.de/pub/isdn4linux/utils/testing/isdn4k-utils.v3.1beta7.tar.gz Other Info ========== diff -u --recursive --new-file v2.2.13/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v2.2.13/linux/Documentation/Configure.help Tue Jan 4 11:10:31 2000 +++ linux/Documentation/Configure.help Tue Jan 4 10:12:10 2000 @@ -1199,7 +1199,7 @@ is no need for the legitimate users to change their TCP/IP software; SYN cookies work transparently to them. For technical information about SYN cookies, check out - ftp://koobera.math.uic.edu/pub/docs/syncookies-archive. + ftp://koobera.math.uic.edu/syncookies.html . If you are SYN flooded, the source address reported by the kernel is likely to have been forged by the attacker; it is only reported as @@ -2532,6 +2532,8 @@ from a boot time script after the /proc filesystem has been mounted. + Enabling masquerading automagically enables ip_always_defrag too. + Details on how to set things up are contained in the IP Masquerade mini-HOWTO, available via FTP (user: anonymous) from ftp://metalab.unc.edu/pub/Linux/docs/HOWTO/mini; there's also some @@ -2643,25 +2645,6 @@ The module will be called ip_masq_markfw.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. -IP: always defragment (required for masquerading) -CONFIG_IP_ALWAYS_DEFRAG - If you say Y here, then all incoming fragments (parts of IP packets - that arose when some host between origin and destination decided - that the packets were too large and cut them into pieces) will be - reassembled (defragmented) before being processed, even if they are - about to be forwarded. - - You must say Y here if you want to enable "IP: masquerading" or "IP: - transparent proxying". - - When using "IP: firewalling" support, you might also want to say Y - here, to have a more reliable firewall (otherwise second and further - fragments must be dealt with by the firewall, which can be tricky). - - Only say Y here if running either a firewall that is the sole link - to your network or a transparent proxy; never ever say Y here for a - normal router or host. - IP: aliasing support CONFIG_IP_ALIAS Sometimes it is useful to give several IP addresses to a single @@ -3515,8 +3498,9 @@ to work, choose Y. This driver is also available as a module called af_packet.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a - module, say M here and read Documentation/modules.txt. If unsure, - say Y. + module, say M here and read Documentation/modules.txt. You will + need to add 'alias net-pf-17 af_packet' to your /etc/conf.modules + file for the module version to function automatically. If unsure, say Y. Kernel/User network link driver CONFIG_NETLINK @@ -5266,7 +5250,7 @@ ftp://ftp.sangoma.com. Read Documentation/networking/wan-router.txt for more information. - The WAN routing support is also available as a module called + The WAN routing support is only available as a module called wanrouter.o ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt. @@ -5290,6 +5274,7 @@ If unsure, say N. + Forwarding between high speed interfaces CONFIG_NET_HW_FLOWCONTROL This option enables NIC (Network Interface Card) hardware throttling @@ -5548,9 +5533,12 @@ that has a program like lynx or netscape) is a family of intelligent multiprotocol WAN adapters with data transfer rates up to T1 (1.544 Mbps). They are also known as Synchronous Data Link Adapters (SDLA) - and designated S502E(A), S503 or S508. These cards support the X.25, - Frame Relay, and PPP protocols. If you have one or more of these - cards, say Y to this option; you may then also want to read the file + and designated S503 or S508. These cards support the X.25, Frame + Relay, PPP, Cisco HDLC protocols. The driver also offers API support + for protocols like HDLC (LAPB), HDLC Streaming and BiSync. + + If you have one or more of these cards, say M to this option; you + may then also want to read the file Documentation/networking/wanpipe.txt. The next questions will ask you about the protocols you want the driver to support. @@ -5570,26 +5558,103 @@ WANPIPE X.25 support CONFIG_WANPIPE_X25 Say Y to this option if you are planning to connect a WANPIPE card - to an X.25 network. You should then also have said Y to "CCITT X.25 - Packet Layer" and "LAPB Data Link Driver", above. If you say N, the - X.25 support will not be included in the driver (saves about 16 KB - of kernel memory). + to an X.25 network. If you say N, the X.25 support will not be + included in the driver. The X.25 option is ONLY supported on S508 + cards. WANPIPE Frame Relay support CONFIG_WANPIPE_FR Say Y to this option if you are planning to connect a WANPIPE card - to a frame relay network. You should then also have said Y to "Frame - Relay (DLCI) support", above. If you say N, the frame relay - support will not be included in the driver (saves about 16 KB of - kernel memory). + to a frame relay network. If you say N, the frame relay support will + not be included in the driver. The Frame Relay option is ONLY + supported on S508 cards. WANPIPE PPP support CONFIG_WANPIPE_PPP Say Y to this option if you are planning to connect a WANPIPE card - to a leased line using Point-to-Point protocol (PPP). You should - then also have said Y to "PPP (point-to-point) support", above. If - you say N, the PPP support will not be included in the driver (saves - about 16 KB of kernel memory). + to a leased line using Point-to-Point protocol (PPP). If you say N, + the PPP support will not be included in the driver. The PPP option + is ONLY supported on S508 cards. + +WANPIPE Cisco HDLC support +CONFIG_WANPIPE_CHDLC + Say Y to this option if you are planning to connect a WANPIPE card + to a leased line using the Cisco HDLC protocol. This now supports + Dual Port Cisco HDLC on the S508 card ONLY. This support also allows + user to build applications using the HDLC streaming API. If you say + N, the Cisco HDLC support and HDLC streaming API will not be + included in the driver. + +MultiGate/COMX support +CONFIG_COMX + Say Y if you want to use any board from the MultiGate (COMX) family. + These boards are synchronous serial adapters for the PC, manufactured + by ITConsult-Pro Co, Hungary. + + Read linux/Documentation/networking/comx.txt for help on configuring + and using COMX interfaces. Further info on these cards can be found at + http://www.itc.hu or . + + If you want to compile this as a module, say M and read + Documentation/modules.txt. The module will be called comx.o. + +COMX/CMX/HiCOMX board support +CONFIG_COMX_HW_COMX + Hardware driver for the 'CMX', 'COMX' and 'HiCOMX' boards from the + MultiGate family. Say Y if you have one of these. + + You will need additional firmware to use these cards, which are + downloadable from ftp://ftp.itc.hu/. + + If you want to compile this as a module, say M and read + Documentation/modules.txt. The module will be called comx-hw-comx.o. + +LoCOMX board support +CONFIG_COMX_HW_LOCOMX + Hardware driver for the 'LoCOMX' board from the MultiGate family. Say Y + if you have a board like this. + + If you want to compile this as a module, say M and read + Documentation/modules.txt. The module will be called comx-hw-locomx.o. + +MixCOM board support +CONFIG_COMX_HW_MIXCOM + Hardware driver for the 'MixCOM' board from the MultiGate family. Say Y + if you have a board like this. + + If you want to use the watchdog device on this card, you should + select it in the Watchdog Cards section of the Character Devices + configuration. The ISDN interface of this card is Teles 16.3 compatible, + you should enable it in the ISDN configuration menu. The driver for the + flash ROM of this card is available separately on ftp://ftp.itc.hu/. + + If you want to compile this as a module, say M and read + Documentation/modules.txt. The module will be called comx-hw-mixcom.o. + +MultiGate Cisco-HDLC and synchronous PPP protocol support +CONFIG_COMX_PROTO_PPP + Cisco-HDLC and synchronous PPP protocol driver for all MultiGate boards. + Say Y if you want to use either protocol on your MultiGate boards. + + If you want to compile this as a module, say M and read + Documentation/modules.txt. The module will be called + comx-proto-ppp.o. + +MultiGate LAPB protocol support +CONFIG_COMX_PROTO_LAPB + LAPB protocol driver for all MultiGate boards. Say Y if you + want to use this protocol on your MultiGate boards. + + If you want to compile this as a module, say M and read + Documentation/modules.txt. The module will be called comx-proto-lapb.o. + +MultiGate Frame Relay protocol support +CONFIG_COMX_PROTO_FR + Frame Relay protocol driver for all MultiGate boards. Say Y if you + want to use this protocol on your MultiGate boards. + + If you want to compile this as a module, say M and read + Documentation/modules.txt. The module will be called comx-proto-fr.o. Ethernet (10 or 100Mbit) CONFIG_NET_ETHERNET @@ -5802,6 +5867,24 @@ say M here and read Documentation/modules.txt. This is recommended. The module will be called rtl8139.o. +SiS 900/7016 support +CONFIG_SIS900 + This is a driver for the Fast Ethernet PCI network cards based on + the SiS 900 and SiS 7016 chips. The SiS 900 core is also embedded in + SiS 630 and SiS 540 chipsets. If you have one of those, say Y and + read the Ethernet-HOWTO, available via FTP (user: anonymous) in + ftp://metalab.unc.edu/pub/Linux/docs/HOWTO. Please read + Documentation/networking/sis900.txt and comments at the beginning + of drivers/net/sis900.c for more information. + + This driver also supports AMD 79C901 HomePNA such that you can use + your phone line as network cable. + + If you want to compile this driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read Documentation/modules.txt. This is recommended. + The module will be called sis900.o. + Packet Engines Yellowfin Gigabit-NIC support CONFIG_YELLOWFIN Say Y here if you have a Packet Engines G-NIC PCI Gigabit Ethernet @@ -5848,6 +5931,27 @@ say M here and read Documentation/modules.txt. This is recommended. The module will be called acenic.o. +SysKonnect SK-98xx support +CONFIG_SK98LIN + Say Y here if you have a SysKonnect SK-98xx Gigabit Ethernet Server + Adapter. The following adapters are supported by this driver: + - SK-9841 (single link 1000Base-LX) + - SK-9842 (dual link 1000Base-LX) + - SK-9843 (single link 1000Base-SX) + - SK-9844 (dual link 1000Base-SX) + - SK-9821 (single link 1000Base-T) + - SK-9822 (dual link 1000Base-T) + The dual link adapters support a link-failover feature. + Read Documentation/networking/sk98lin.txt for information about + optional driver parameters. + Questions concerning this driver may be addresse to: + linux@syskonnect.de + + If you want to compile this driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read Documentation/modules.txt. This is recommended. + The module will be called sk98lin.o. + AMD LANCE and PCnet (AT1500 and NE2100) support CONFIG_LANCE If you have a network (Ethernet) card of this type, say Y and read @@ -6392,6 +6496,32 @@ More specific information is contained in Documentation/networking/tulip.txt. + This is the new version of this driver. If it does not work for + you please try older version which is also available. + + 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 tulip.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt as well as + Documentation/networking/net-modules.txt. + +Old DECchip Tulip (dc21x4x) PCI support +CONFIG_DEC_ELCP_OLD + This driver is developed for the SMC EtherPower series Ethernet + cards and also works with cards based on the DECchip + 21040/21041/21140 (Tulip series) chips. Some LinkSys PCI cards are + of this type. (If your card is NOT SMC EtherPower 10/100 PCI + (smc9332dst), you can also try the driver for "Generic DECchip" + cards, above. However, most people with a network card of this type + will say Y here.) Do read the Ethernet-HOWTO, available via FTP + (user: anonymous) in ftp://metalab.unc.edu/pub/Linux/docs/HOWTO. + More specific information is contained in + Documentation/networking/tulip.txt. + + This an older version of the driver which supports some cards the + new version does not (yet) support. Use it if the new driver does + not work for you. + 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 tulip.o. If you want to compile it as a @@ -8884,9 +9014,15 @@ APM is a BIOS specification for saving power using several different techniques. This is mostly useful for battery powered laptops with APM compliant BIOSes. If you say Y here, the system time will be - reset after a USER RESUME operation, the /proc/apm device will - provide battery status information, and user-space programs will - receive notification of APM "events" (e.g., battery status change). + reset after a RESUME operation, the /proc/apm device will provide + battery status information, and user-space programs will receive + notification of APM "events" (e.g. battery status change). + + If you select "Y" here, you can disable actual use of the APM + BIOS by passing the "apm=off" option to the kernel at boot time. + + Note that the APM support is almost completely disabled for + machines with more than one CPU. Supporting software is available; for more information, read the Battery Powered Linux mini-HOWTO, available via FTP (user: @@ -8899,9 +9035,7 @@ This driver does not support the TI 4000M TravelMate and the ACER 486/DX4/75 because they don't have compliant BIOSes. Many "green" desktop machines also don't have compliant BIOSes, and this driver - will cause those machines to panic during the boot phase (typically, - these machines are using a data segment of 0040, which is reserved - for the Linux kernel). + may cause those machines to panic during the boot phase. If you are running Linux on a laptop, you may also want to read the Linux Laptop home page on the WWW at @@ -9191,83 +9325,117 @@ Joystick support CONFIG_JOYSTICK - If you have a joystick, you can say Y here to enable generic - joystick support. You will also need to say Y or M to at least one - of the hardware specific joystick drivers. This will make the - joysticks available as /dev/jsX devices. Please read the file - Documentation/joystick.txt which contains more information and the - location of the joystick package that you'll need. + If you have a joystick, 6dof controller, gamepad, steering wheel, + weapon control system or something like that you can say Y here to + enable generic support for these controllers. You will also need to + say Y or M to at least one of the hardware specific drivers. This + will make the controllers available as /dev/jsX devices. Please read + the file Documentation/joystick.txt which contains more information + and the location of the joystick package that you'll need. - 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 joystick.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. - -Classic PC analog joysticks and gamepads +Classic PC analog CONFIG_JOY_ANALOG - Say Y here if you have an analog joystick or gamepad that connects - to the PC gameport. This supports many different types, including - joysticks with throttle control, with rudders, or with extensions - like additional hats and buttons compatible with CH Flightstick Pro, + Say Y here if you have a controller that connects to the PC + gameport. This supports many different types, including joysticks + with throttle control, with rudders, or with extensions like + additional hats and buttons compatible with CH Flightstick Pro, ThrustMaster FCS or 6 and 8 button gamepads. For more information on how to use the driver please read Documentation/joystick.txt -FPGaming and MadCatz A3D controllers -CONFIG_JOY_ASSASIN - Say Y here if you have an FPGaming Assasin 3D, MadCatz Panther or - MadCatz Panther XL. For more information on how to use the driver - please read Documentation/joystick.txt +FPGaming and MadCatz A3D +CONFIG_JOY_ASSASSIN + Say Y here if you have an FPGaming or MadCatz controller using the + A3D protocol over the PC gameport. For more information on how to + use the driver please read Documentation/joystick.txt -Gravis GrIP joysticks and gamepads +Gravis GrIP CONFIG_JOY_GRAVIS - Say Y here if you have a Gravis GamePad Pro, Gravis Xterminator or - Gravis Blackhawk Digital. For more information on how to use the - driver please read Documentation/joystick.txt + Say Y here if you have a Gravis controller using the GrIP protocol + over the PC gameport. For more information on how to use the driver + please read Documentation/joystick.txt + +Logitech ADI +CONFIG_JOY_LOGITECH + Say Y here if you have a Logitech controller using the ADI + protocol over the PC gameport. For more information on how to use + the driver please read Documentation/joystick.txt -PDPI Lightning 4 gamecards +Microsoft SideWinder +CONFIG_JOY_SIDEWINDER + Say Y here if you have a Microsoft controller using the Digital + Overdrive protocol over PC gameport. For more information on how to + use the driver please read Documentation/joystick.txt + +ThrustMaster DirectConnect +CONFIG_JOY_THRUSTMASTER + Say Y here if you have a ThrustMaster controller using the + DirectConnect (BSP) protocol over the PC gameport. For more + information on how to use the driver please read + Documentation/joystick.txt + +Creative Labs Blaster +CONFIG_JOY_CREATIVE + Say Y here if you have a Creative Labs controller using the + Blaster protocol over the PC gameport. For more information on how + to use the driver please read Documentation/joystick.txt + +PDPI Lightning 4 card CONFIG_JOY_LIGHTNING Say Y here if you have a PDPI Lightning 4 gamecard and an analog joystick or gamepad connected to it. For more information on how to use the driver please read Documentation/joystick.txt -Logitech Digital joysticks and gamepads -CONFIG_JOY_LOGITECH - Say Y here if you have a Logitech WingMan Extreme Digital, - Logitech ThunderPad Digital or Logitech CyberMan 2. For more +Trident 4DWave and Aureal Vortex gameport +CONFIG_JOY_PCI + Say Y here if you have a Trident 4DWave DX/NX or Aureal Vortex 1/2 + card and want to use its gameport in its enhanced digital mode + with and ordinary analog joystick. For more information on how to + use the driver please read Documentation/joystick.txt + +Magellan and Space Mouse +CONFIG_JOY_MAGELLAN + Say Y here if you have a Magellan or Space Mouse 6DOF controller + connected to your computer's serial port. For more information on + how to use the driver please read Documentation/joystick.txt + +SpaceTec SpaceOrb 360 and SpaceBall Avenger +CONFIG_JOY_SPACEORB + Say Y here if you have a SpaceOrb 360 or SpaceBall Avenger 6DOF + controller connected to your computer's serial port. For more information on how to use the driver please read Documentation/joystick.txt -Microsoft SideWinder, Genius Digital joysticks and gamepads -CONFIG_JOY_SIDEWINDER - Say Y here if you have a Microsoft SideWinder 3d Pro, Microsoft - SideWinder Precision Pro, Microsoft SideWinder Force Feedback Pro, - Microsoft Sidewinder GamePad or Genius Flight2000 F-23 Digital. For - more information on how to use the driver please read +SpaceTec SpaceBall 4000 FLX +CONFIG_JOY_SPACEBALL + Say Y here if you have a SpaceTec SpaceBall 4000 FLX + controller connected to your computer's serial port. For more + information on how to use the driver please read Documentation/joystick.txt -ThrustMaster DirectConnect joysticks and gamepads -CONFIG_JOY_THRUSTMASTER - Say Y here if you have a ThrustMaster Millenium 3D Inceptor or a - ThrustMaster 3D Rage Pad. For more information on how to use the - driver please read Documentation/joystick.txt +Logitech WingMan Warrior +CONFIG_JOY_WARRIOR + Say Y here if you have a Logitech WingMan Warrior controller + connected to your computer's serial port. For more information on + how to use the driver please read Documentation/joystick.txt -NES, SNES, PSX, Multisystem joysticks and gamepads +NES, SNES, N64, PSX, Multi CONFIG_JOY_CONSOLE Say Y here if you have a Nintendo Entertainment System gamepad, - Super Nintendo Entertainment System gamepad, Sony PlayStation - gamepad or a Multisystem -- Atari, Amiga, Commodore, Amstrad CPC - joystick. For more information on how to use the driver please read + Super Nintendo Entertainment System gamepad, Nintendo 64 gamepad, + Sony PlayStation gamepad or a Multisystem -- Atari, Amiga, + Commodore, Amstrad CPC joystick connected to your parallel port. + For more information on how to use the driver please read Documentation/joystick.txt and Documentation/joystick-parport.txt -Sega, Multisystem joysticks and gamepads +Sega, Multi CONFIG_JOY_DB9 Say Y here if you have a Sega Master System gamepad, Sega Genesis gamepad, Sega Saturn gamepad, or a Multisystem -- Atari, Amiga, - Commodore, Amstrad CPC joystick. For more information on how to use - the driver please read Documentation/joystick.txt and - Documentation/joystick-parport.txt + Commodore, Amstrad CPC joystick connected to your parallel port. For + more information on how to use the driver please read + Documentation/joystick.txt and Documentation/joystick-parport.txt -TurboGraFX Multisystem joystick interface +TurboGraFX interface CONFIG_JOY_TURBOGRAFX Say Y here if you have the TurboGraFX interface by Steffen Schwenke, and want to use it with Multiststem -- Atari, Amiga, Commodore, @@ -9422,20 +9590,6 @@ You can say M here to compile this driver as a module; the module is called sb.o. -Generic OPL2/OPL3 FM synthesizer support -CONFIG_SOUND_ADLIB - Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). - Answering Y is usually a safe and recommended choice, however some - cards may have software (TSR) FM emulation. Enabling FM support with - these cards may cause trouble (I don't currently know of any such - cards, however). - - Please read the file Documentation/sound/OPL3 if your card has an - OPL3 chip. - - If unsure, say Y. - - #Loopback MIDI device support #CONFIG_SOUND_VMIDI ### @@ -9715,7 +9869,15 @@ FM synthesizer (YM3812/OPL-3) support CONFIG_SOUND_YM3812 - Answer Y here, unless you know you will not need the option. + Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). + Answering Y is usually a safe and recommended choice, however some + cards may have software (TSR) FM emulation. Enabling FM support with + these cards may cause trouble (I don't currently know of any such + cards, however). + Please read the file Documentation/sound/OPL3 if your card has an + OPL3 chip. + + If unsure, say Y. Sun Audio support CONFIG_SUN_AUDIO @@ -9922,6 +10084,12 @@ See Documentation/sound/NM256 for further information. +ESS Maestro sound chipsets +CONFIG_SOUND_MAESTRO + Say Y or M if you have a sound system driven by ESS's Maestro line + of PCI sound chips. These include the Maestro 1, Maestro 2, and + Maestro 2E. See Documentation/sound/Maestro for more details. + Are you using a crosscompiler CONFIG_CROSSCOMPILE Say Y here if you are compiling the kernel on a different @@ -9953,11 +10121,6 @@ ISDN subsystem CONFIG_ISDN - CAUTION: the ISDN driver shipped with this kernel distribution - is outdated and might not work without problems. An updated driver - is available for download. Please read http://www.isdn4linux.de - on the WWW for a list of servers. - ISDN ("Integrated Services Digital Networks", called RNIS in France) is a special type of fully digital telephone service; it's mostly used to connect to your Internet service provider (with SLIP or @@ -9966,9 +10129,7 @@ conversations while downloading stuff. It only works if your computer is equipped with an ISDN card and both you and your service provider purchased an ISDN line from the phone company. For details, - read http://alumni.caltech.edu/~dank/isdn/ on the WWW. (To browse - the WWW, you need to have access to a machine on the Internet that - has a program like lynx or netscape.) + read http://alumni.caltech.edu/~dank/isdn/ on the WWW. This driver allows you to use an ISDN-card for networking connections and as dialin/out device. The isdn-tty's have a built in @@ -10018,25 +10179,28 @@ is the only voice-supporting driver. See Documentation/isdn/README.audio for more information. -X.25 PLP on top of ISDN (EXPERIMENTAL) +X.25 PLP on top of ISDN CONFIG_ISDN_X25 - This experimental feature provides the X.25 protocol over ISDN - connections. See Documentation/isdn/README.x25 for more information + This feature provides the X.25 protocol over ISDN connections. + See Documentation/isdn/README.x25 for more information if you are thinking about using this. ISDN diversion services support CONFIG_ISDN_DIVERSION This option allows you to use some supplementary diversion services in conjunction with the HiSax driver on an EURO/DSS1 - line. Supported options are CD (call deflection), CFU (Call - forward unconditional), CFB (Call forward when busy) and CFNR - (call forward not reachable). - Additionally the actual CFU, CFB and CFNR state may be - interrogated. The use of CFU, CFB, CFNR and interrogation may - be limited to some countries. The keypad protocol is still not - implemented. - CD should work in all countries if this service has been sub- - scribed. + line. + + Supported options are CD (call deflection), CFU (Call forward + unconditional), CFB (Call forward when busy) and CFNR (call forward + not reachable). Additionally the actual CFU, CFB and CFNR state may + be interrogated. + + The use of CFU, CFB, CFNR and interrogation may be limited to some + countries. The keypad protocol is still not implemented. CD should + work in all countries if the service has been subscribed to. + + Please read the file Documentation/isdn/README.diversion. ICN 2B and 4B support CONFIG_ISDN_DRV_ICN @@ -10278,7 +10442,7 @@ For more informations see under Documentation/isdn/README.hfc-pci. -HiSax Support for Winbond W6692 based cards (EXPERIMENTAL) +HiSax Support for Winbond W6692 based cards CONFIG_HISAX_W6692 This enables HiSax support for Winbond W6692 based PCI ISDN cards. @@ -10311,35 +10475,33 @@ can be inserted in and removed from the running kernel whenever you want, details in Documentation/modules.txt); the module will be called sc.o. See Documentation/isdn/README.sc and - http://www.spellcast.com for more information (to browse the WWW, - you need to have access to a machine on the Internet that has a - program like lynx or netscape). + http://www.spellcast.com for more information. Eicon.Diehl active card support CONFIG_ISDN_DRV_EICON Say Y here if you have an Eicon active ISDN card. In order to use this card, additional firmware is necessary, which has to be loaded - into the card using the eiconctrl utility which is part of the latest - isdn4k-utils package. Please read the file + into the card using the eiconctrl utility which is part of the + latest isdn4k-utils package. Please read the file Documentation/isdn/README.eicon for more information. Eicon old-type card support CONFIG_ISDN_DRV_EICON_ISA - Say Y here if you have an old-type Eicon active ISDN card. In order to - use this card, additional firmware is necessary, which has to be loaded - into the card using the eiconctrl utility which is part of the latest - isdn4k-utils package. Please read the file + Say Y here if you have an old-type Eicon active ISDN card. In order + to use this card, additional firmware is necessary, which has to be + loaded into the card using the eiconctrl utility which is part of + the latest isdn4k-utils package. Please read the file Documentation/isdn/README.eicon for more information. Support AT-Fax Class 2 commands CONFIG_ISDN_TTY_FAX If you say Y here, the modem-emulator will support a subset of the Fax Class 2 commands. Using a getty with fax-support - (mgetty+sendfax, hylafax), you will be able to use your Linux box - as an ISDN-fax-machine. This must be supported by the lowlevel driver + (mgetty+sendfax, hylafax), you will be able to use your Linux box as + an ISDN-fax-machine. This must be supported by the lowlevel driver also. See Documentation/isdn/README.fax for more information. -AVM-B1 with CAPI2.0 support +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 @@ -10350,7 +10512,7 @@ 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 @@ -10364,7 +10526,7 @@ CONFIG_ISDN_DRV_AVMB1_B1PCI Enable support for the PCI version of the AVM B1 card. -AVM T1/T1B ISA support +AVM T1/T1-B ISA support CONFIG_ISDN_DRV_AVMB1_T1ISA Enable support for the AVM T1 T1B card. Note: This is a PRI card and handle 30 B-channels. @@ -10373,12 +10535,18 @@ CONFIG_ISDN_DRV_AVMB1_B1PCMCIA Enable support for the PCMCIA version of the AVM B1 card. +AVM T1/T1-B PCI support +CONFIG_ISDN_DRV_AVMB1_T1PCI + Enable support for the AVM T1 T1B card. + Note: This is a PRI card and handle 30 B-channels. + Verbose reason code reporting (kernel size +=7K) CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON If you say Y here, the AVM B1 driver will give verbose reasons for disconnecting. This will increase the size of the kernel by 7 KB. If unsure, say Y. + IBM Active 2000 support (EXPERIMENTAL) CONFIG_ISDN_DRV_ACT2000 Say Y here if you have an IBM Active 2000 ISDN card. In order to use @@ -10519,6 +10687,7 @@ Note that even if you say N here, you can still use your expansion cards. If in doubt, say Y. +Amiga 1200/600 PCMCIA support CONFIG_AMIGA_PCMCIA Include support in the kernel for pcmcia on Amiga 1200 and Amiga 600. If you intend to use pcmcia cards say Y; otherwise say N. @@ -11783,6 +11952,68 @@ boards from BVM Ltd. Everyone using one of these boards should say Y here. +IBM's S/390 architecture +CONFIG_ARCH_S390 + Select this option, if you want to run the Kernel on one of IBM's + mainframes of the S/390 generation. You should have installed the + s390-compiler released by IBM (based on gcc-2.95.1) before. + +Merge some code into the kernel to make the image IPLable +CONFIG_IPLABLE + If you want to use the produced kernel to IPL directly from a + device, you have to merge a bootsector specific to the device + into the first bytes of the kernel. You will have to select the + IPL device on another question, that pops up, when you select + CONFIG_IPLABE. + +IPL from a S/390 tape unit +CONFIG_IPL_TAPE + Select this option if you want to IPL the image from a tape. + +IPL from a virtual card reader emulated by VM/ESA +CONFIG_IPL_RDR_VM + Select this option if you are running under VM/ESA and want + to IPL the image from the emulated card reader. + +IPL from a real card reader +CONFIG_IPL_RDR + Select this option if you want to IPL the image from a real + card reader. Maybe you still got one and want to try. We didn't + test. + +IBMs S/390 Harddisks (DASDs) +CONFIG_DASD + Enable this option if you want to access DASDs directly utilizing + S/390s channel subsystem commands. This is necessary for running + natively on a single image or an LPAR. + +Enable DASD fast write +CONFIG_DASD_FAST_IO + Enable fast I/O for DASDs. That means that the next I/O command + is already issued at interrupt time, if an I/O request is pending. + This option gives significant speedup of I/O, because we don't + schedule the bottom-halves as often as Intel. + +Support for IBM-style disk-labels (S/390) +CONFIG_S390_PARTITION + Enable this option to assure standard IBM labels on the DASDs. + You must enable it, if you are planning to access DASDs also + attached to another IBM mainframe operation system (OS/390, + VM/ESA, VSE/ESA). + +ECKD devices +CONFIG_DASD_ECKD + ECKD devices are the most commonly used devices. you should enable + this option unless you are very sure to have no ECKD device. + +CKD devices +CONFIG_DASD_CKD + CKD devices are currently unsupported. + +FBA devices +CONFIG_DASD_FBA + FBA devices are currently unsupported. + Compaq SMART2 support CONFIG_BLK_CPQ_DA This is the driver for Compaq Smart Array controllers. @@ -11790,6 +12021,24 @@ See "linux/Documentation/cpqarray.txt" for the current list of boards supported by this driver, and for further information on the use of this driver. + +QuickNet Internet LineJack/PhoneJack support +CONFIG_PHONE_IXJ + Say M if you have a telephony card manufactured by Quicknet + Technologies, Inc. These include the Internet PhoneJACK and + Internet LineJACK Telephony Cards. + + For the ISA versions of these products, you can configure the + cards using the isapnp tools (pnpdump/isapnp) or you can use the + backported isapnp module. Please read: + + /usr/src/linux/Documentation/telephony/ixj.txt. + + For more information on these cards, see Quicknet's website at: + http://www.quicknet.net/ + + If you do not have any Quicknet telephony cards, you can safely + ignore this option. # # A couple of things I keep forgetting: diff -u --recursive --new-file v2.2.13/linux/Documentation/README.moxa linux/Documentation/README.moxa --- v2.2.13/linux/Documentation/README.moxa Wed Dec 31 16:00:00 1969 +++ linux/Documentation/README.moxa Tue Jan 4 10:12:10 2000 @@ -0,0 +1,18 @@ + =================================================================== + Release Note of Linux Driver for Moxa's C104/C168/CI-104J + =================================================================== + + ------------------------------------------------------------------- + Ver. 1.1 Sep. 1, 1999 + ------------------------------------------------------------------- + 1. Improved: + a. Static driver (kernel) and dynamic driver (loadable module) + modes are supported. + b. Multiple Smartio PCI series boards sharing the same IRQ + supported. + + ------------------------------------------------------------------- + Ver. 1.0 Feb 17, 1997 + ------------------------------------------------------------------- + 1. Newly release. + diff -u --recursive --new-file v2.2.13/linux/Documentation/computone.txt linux/Documentation/computone.txt --- v2.2.13/linux/Documentation/computone.txt Tue Oct 19 17:10:36 1999 +++ linux/Documentation/computone.txt Tue Jan 4 10:12:10 2000 @@ -7,7 +7,7 @@ kernel and have been tested on Linux kernels 2.0, 2.2, and 2.3. Version: 1.2.4 -Date: 08/04/99 +Date: 12/15/99 Author: Andrew Manison Testing: larryg@computone.com Support: support@computone.com @@ -28,7 +28,7 @@ products previous to the Intelliport II. This driver was developed on the v2.0.x Linux tree and has been tested up -to v2.2.9; it will probably not work with earlier v1.X kernels,. +to v2.2.13; it will probably not work with earlier v1.X kernels,. 2. QUICK INSTALLATION @@ -204,9 +204,94 @@ 7. ip2mkdev shell script -===== Cut Here ===== +Previously, this script was simply attached here. It is now attached as a +shar archive to make it easier to extract the script from the documentation. +To create the ip2mkdev shell script change to a convenient directory (/tmp +works just fine) and run the following command: + + unshar /usr/src/linux/Documentation/computone.txt + (This file) + +You should now have a file ip2mkdev in your current working directory with +permissions set to execute. Running that script with then create the +necessary devices for the Computone boards, interfaces, and ports which +are present on you system at the time it is run. + + +#!/bin/sh +# This is a shell archive (produced by GNU sharutils 4.2). +# To extract the files from this archive, save it to some FILE, remove +# everything before the `!/bin/sh' line above, then type `sh FILE'. +# +# Made on 1999-12-17 16:06 EST by . +# Source directory was `/mnt2/src/linux-2.3.33/Documentation'. +# +# Existing files will *not* be overwritten unless `-c' is specified. +# +# This shar contains: +# length mode name +# ------ ---------- ------------------------------------------ +# 3300 -rwxr-xr-x ip2mkdev +# +save_IFS="${IFS}" +IFS="${IFS}:" +gettext_dir=FAILED +locale_dir=FAILED +first_param="$1" +for dir in $PATH +do + if test "$gettext_dir" = FAILED && test -f $dir/gettext \ + && ($dir/gettext --version >/dev/null 2>&1) + then + set `$dir/gettext --version 2>&1` + if test "$3" = GNU + then + gettext_dir=$dir + fi + fi + if test "$locale_dir" = FAILED && test -f $dir/shar \ + && ($dir/shar --print-text-domain-dir >/dev/null 2>&1) + then + locale_dir=`$dir/shar --print-text-domain-dir` + fi +done +IFS="$save_IFS" +if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED +then + echo=echo +else + TEXTDOMAINDIR=$locale_dir + export TEXTDOMAINDIR + TEXTDOMAIN=sharutils + export TEXTDOMAIN + echo="$gettext_dir/gettext -s" +fi +touch -am 1231235999 $$.touch >/dev/null 2>&1 +if test ! -f 1231235999 && test -f $$.touch; then + shar_touch=touch +else + shar_touch=: + echo + $echo 'WARNING: not restoring timestamps. Consider getting and' + $echo "installing GNU \`touch', distributed in GNU File Utilities..." + echo +fi +rm -f 1231235999 $$.touch +# +if mkdir _sh06360; then + $echo 'x -' 'creating lock directory' +else + $echo 'failed to create lock directory' + exit 1 +fi +# ============= ip2mkdev ============== +if test -f 'ip2mkdev' && test "$first_param" != -c; then + $echo 'x -' SKIPPING 'ip2mkdev' '(file already exists)' +else + $echo 'x -' extracting 'ip2mkdev' '(text)' + sed 's/^X//' << 'SHAR_EOF' > 'ip2mkdev' && #!/bin/sh - - +X # ip2mkdev # # Make or remove devices as needed for Computone Intelliport drivers @@ -227,117 +312,134 @@ # if test ! -f /proc/tty/drivers then - echo "\ +X echo "\ Unable to check driver status. Make sure proc file system is mounted." - - exit 255 +X +X exit 255 fi - +X if test ! -f /proc/tty/driver/ip2 then - echo "\ +X echo "\ Unable to locate ip2 proc file. Attempting to load driver" - - if insmod ip2 - then - if test ! -f /proc/tty/driver/ip2 - then - echo "\ +X +X if /sbin/insmod ip2 +X then +X if test ! -f /proc/tty/driver/ip2 +X then +X echo "\ Unable to locate ip2 proc file after loading driver. Driver initialization failure or driver version error. " - exit 255 - fi - else - echo "Unable to load ip2 driver." - exit 255 - fi +X exit 255 +X fi +X else +X echo "Unable to load ip2 driver." +X exit 255 +X fi fi - +X # Ok... So we got the driver loaded and we can locate the procfs files. # Next we need our major numbers. - +X TTYMAJOR=`sed -e '/^ip2/!d' -e '/\/dev\/tty/!d' -e 's/.*tty.[ ]*\([0-9]*\)[ ]*.*/\1/' < /proc/tty/drivers` CUAMAJOR=`sed -e '/^ip2/!d' -e '/\/dev\/cu/!d' -e 's/.*cu.[ ]*\([0-9]*\)[ ]*.*/\1/' < /proc/tty/drivers` BRDMAJOR=`sed -e '/^Driver: /!d' -e 's/.*IMajor=\([0-9]*\)[ ]*.*/\1/' < /proc/tty/driver/ip2` - +X echo "\ TTYMAJOR = $TTYMAJOR CUAMAJOR = $CUAMAJOR BRDMAJOR = $BRDMAJOR " - +X # Ok... Now we should know our major numbers, if appropriate... # Now we need our boards and start the device loops. - +X grep '^Board [0-9]:' /proc/tty/driver/ip2 | while read token number type alltherest do - # The test for blank "type" will catch the stats lead-in lines - # if they exist in the file - if test "$type" = "vacant" -o "$type" = "Vacant" -o "$type" = "" - then - continue - fi - - BOARDNO=`expr "$number" : '\([0-9]\):'` - PORTS=`expr "$alltherest" : '.*ports=\([0-9]*\)' | tr ',' ' '` - MINORS=`expr "$alltherest" : '.*minors=\([0-9,]*\)' | tr ',' ' '` - - if test "$BOARDNO" = "" -o "$PORTS" = "" - then +X # The test for blank "type" will catch the stats lead-in lines +X # if they exist in the file +X if test "$type" = "vacant" -o "$type" = "Vacant" -o "$type" = "" +X then +X continue +X fi +X +X BOARDNO=`expr "$number" : '\([0-9]\):'` +X PORTS=`expr "$alltherest" : '.*ports=\([0-9]*\)' | tr ',' ' '` +X MINORS=`expr "$alltherest" : '.*minors=\([0-9,]*\)' | tr ',' ' '` +X +X if test "$BOARDNO" = "" -o "$PORTS" = "" +X then # This may be a bug. We should at least get this much information - echo "Unable to process board line" - continue - fi - - if test "$MINORS" = "" - then +X echo "Unable to process board line" +X continue +X fi +X +X if test "$MINORS" = "" +X then # Silently skip this one. This board seems to have no boxes - continue - fi - - echo "board $BOARDNO: $type ports = $PORTS; port numbers = $MINORS" - - if test "$BRDMAJOR" != "" - then - BRDMINOR=`expr $BOARDNO \* 4` - STSMINOR=`expr $BRDMINOR + 1` - if test ! -c /dev/ip2ipl$BOARDNO ; then - mknod /dev/ip2ipl$BOARDNO c $BRDMAJOR $BRDMINOR - fi - if test ! -c /dev/ip2stat$BOARDNO ; then - mknod /dev/ip2stat$BOARDNO c $BRDMAJOR $STSMINOR - fi - fi - - if test "$TTYMAJOR" != "" - then - PORTNO=$BOARDBASE - - for PORTNO in $MINORS - do - if test ! -c /dev/ttyF$PORTNO ; then - # We got the harware but no device - make it - mknod /dev/ttyF$PORTNO c $TTYMAJOR $PORTNO - fi - done - fi - - if test "$CUAMAJOR" != "" - then - PORTNO=$BOARDBASE - - for PORTNO in $MINORS - do - if test ! -c /dev/cuf$PORTNO ; then - # We got the harware but no device - make it - mknod /dev/cuf$PORTNO c $CUAMAJOR $PORTNO - fi - done - fi +X continue +X fi +X +X echo "board $BOARDNO: $type ports = $PORTS; port numbers = $MINORS" +X +X if test "$BRDMAJOR" != "" +X then +X BRDMINOR=`expr $BOARDNO \* 4` +X STSMINOR=`expr $BRDMINOR + 1` +X if test ! -c /dev/ip2ipl$BOARDNO ; then +X mknod /dev/ip2ipl$BOARDNO c $BRDMAJOR $BRDMINOR +X fi +X if test ! -c /dev/ip2stat$BOARDNO ; then +X mknod /dev/ip2stat$BOARDNO c $BRDMAJOR $STSMINOR +X fi +X fi +X +X if test "$TTYMAJOR" != "" +X then +X PORTNO=$BOARDBASE +X +X for PORTNO in $MINORS +X do +X if test ! -c /dev/ttyF$PORTNO ; then +X # We got the harware but no device - make it +X mknod /dev/ttyF$PORTNO c $TTYMAJOR $PORTNO +X fi +X done +X fi +X +X if test "$CUAMAJOR" != "" +X then +X PORTNO=$BOARDBASE +X +X for PORTNO in $MINORS +X do +X if test ! -c /dev/cuf$PORTNO ; then +X # We got the harware but no device - make it +X mknod /dev/cuf$PORTNO c $CUAMAJOR $PORTNO +X fi +X done +X fi done - +X +Xexit 0 +SHAR_EOF + $shar_touch -am 1217160599 'ip2mkdev' && + chmod 0755 'ip2mkdev' || + $echo 'restore of' 'ip2mkdev' 'failed' + if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ + && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then + md5sum -c << SHAR_EOF >/dev/null 2>&1 \ + || $echo 'ip2mkdev:' 'MD5 check failed' +eccd181f4a2005e47a969fc83885df61 ip2mkdev +SHAR_EOF + else + shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'ip2mkdev'`" + test 3300 -eq "$shar_count" || + $echo 'ip2mkdev:' 'original size' '3300,' 'current size' "$shar_count!" + fi +fi +rm -fr _sh06360 exit 0 -===== Cut Here ===== diff -u --recursive --new-file v2.2.13/linux/Documentation/devices.tex linux/Documentation/devices.tex --- v2.2.13/linux/Documentation/devices.tex Sun Aug 16 11:35:51 1998 +++ linux/Documentation/devices.tex Wed Dec 31 16:00:00 1969 @@ -1,2165 +0,0 @@ -\documentstyle{article} -% $Id: devices.tex,v 1.14 1998/08/10 22:39:24 hpa Exp $ -% --------------------------------------------------------------------------- -% Adopt somewhat reasonable margins, so it doesn't take a million -% pages to print... :-) If you're actually putting this in print, you -% may wish to change these. -\oddsidemargin=0in -\textwidth=6.5in -\topmargin=0in -\headheight=0.5in -\headsep=0.25in -\textheight=7.5in -\footskip=0.75in -\footheight=0.5in -% -\begin{document} -\newcommand{\file}{\tt} % Style to use for a filename -\newcommand{\url}{\it} % Style to use for an URL -\newcommand{\hex}{\tt} % Style to use for a hex number -\newcommand{\ud}{(Under development)} % Abbreviation -\newcommand{\1}{\({}^1\)} -\newcommand{\2}{\({}^2\)} -\newcommand{\3}{\({}^3\)} -\newcommand{\4}{\({}^4\)} -\newlength{\dig} -\settowidth{\dig}{0} % Get width of digits -\newcommand{\num}[2]{\makebox[#1\dig][r]{#2}} -\newcommand{\major}[4]{\num{3}{#1}#2 \> #3 \> #4 \\} -\newcommand{\minor}[3]{\> \> \num{3}{#1} \> {\file #2} \> #3 \\} -\newcommand{\minordots}{\> \> \> \dots \\} -\newenvironment{devicelist}% - {\begin{tabbing}% -000--000 \= blockxxx \= 000 \= {\file /dev/input/keyboardxxx} \= foo \kill}% - {\end{tabbing}} -\newcommand{\link}[4]{{\file #1} \> {\file #2} \> #3 \> #4 \\} -\newcommand{\vlink}[4]{{\file #1} \> {\em #2 \/} \> #3 \> #4 \\} -\newcommand{\node}[3]{{\file #1} \> #2 \> #3 \\} -\newcommand{\tum}{$''$} -\newenvironment{nodelist}% - {\begin{tabbing}% -{\file /dev/crambamboli} \= {\file /proc/self/fd/99} \= symbolicxxx \= -foo \kill}% - {\end{tabbing}} -% -% If you reformat this document, *please* make sure this information -% gets included! This list changes frequently, so it is crucial to -% know the date of the revision. -% -\title{{\bf Linux Allocated Devices}} -\author{Maintained by H. Peter Anvin $<$hpa@zytor.com$>$} -\date{Last revised: August 10, 1998} -\maketitle -% -\noindent -This list is the Linux Device List, the official registry of allocated -device numbers and {\file /dev} directory nodes for the Linux -operating system. - -The latest version of this list is included with the Linux kernel -sources in \LaTeX\ and ASCII form. It is also available separately -from {\url ftp://ftp.kernel.org/pub/linux/docs/device-list/}. In case -of discrepancy between the text and \LaTeX\ versions, the \LaTeX\ -version is authoritative. - -This document is included by reference into the Linux Filesystem -Standard (FSSTND). The FSSTND is available from -{\url ftp://tsx-11.mit.edu/pub/linux/docs/linux-standards/fsstnd/}. - -Allocations marked (68k/Amiga) apply to Linux/68k on the Amiga -platform only. Allocations marked (68k/Atari) apply to Linux/68k on -the Atari platform only. - -This document is in the public domain. The author requests, however, -that semantically altered versions are not distributed without -permission of the author, assuming the author can be contacted without -an unreasonable effort. - -In particular, please don't sent patches for this list to Linus, at -least not without contacting me first. - -I do not have any information about these devices beyond what appears -on this list. Any such information requests will be deleted without -reply. - -\section{How to submit a device entry} - -To have a major number allocated, or a minor number in situations -where that applies (e.g. busmice), please contact me with the -appropriate device information. Also, if you have additional -information regarding any of the devices listed below, or if I have -made a mistake, I would greatly appreciate a note. - -I do, however, make two requests about the nature of your report. -This is necessary for me to be able to keep this list up to date and -correct in a timely manner. First of all, {\em please\/} include the -word ``device'' in the subject so your mail won't accidentally get -buried! I receive hundreds of email messages a day, so mail sent with -other subjects may very well get lost in the avalanche. - -Second, please include a description of the device {\em in the same -format as this list\/}. The reason for this is that it is the only -way I have found to ensure I have all the requisite information to -publish your device and avoid conflicts. - -Your cooperation is appreciated. - -\section{Major numbers} - -\begin{devicelist} -\major{ 0}{}{ }{Unnamed devices (e.g. non-device mounts)} -\major{ 1}{}{char }{Memory devices} -\major{ }{}{block}{RAM disk} -\major{ 2}{}{char }{Pseudo-TTY masters} -\major{ }{}{block}{Floppy disks} -\major{ 3}{}{char }{Pseudo-TTY slaves} -\major{ }{}{block}{First MFM, RLL or IDE hard disk/CD-ROM interface} -\major{ 4}{}{char }{TTY devices} -\major{ 5}{}{char }{Alternate TTY devices} -\major{ 6}{}{char }{Parallel printer devices} -\major{ 7}{}{char }{Virtual console access devices} -\major{ }{}{block}{Loopback devices} -\major{ 8}{}{block}{SCSI disk devices (0-15)} -\major{ 9}{}{char }{SCSI tape devices} -\major{ }{}{block}{Metadisk (RAID) devices} -\major{10}{}{char }{Non-serial mice, misc features} -\major{11}{}{char }{Raw keyboard device} -\major{ }{}{block}{SCSI CD-ROM devices} -\major{12}{}{char }{QIC-02 tape} -\major{ }{}{block}{MSCDEX CD-ROM callback support} -\major{13}{}{char }{PC speaker} -\major{ }{}{block}{8-bit MFM/RLL/IDE controller} -\major{14}{}{char }{Sound card} -\major{ }{}{block}{BIOS harddrive callback support} -\major{15}{}{char }{Joystick} -\major{ }{}{block}{Sony CDU-31A/CDU-33A CD-ROM} -\major{16}{}{char }{Non-SCSI scanners} -\major{ }{}{block}{GoldStar CD-ROM} -\major{17}{}{char }{Chase serial card} -\major{ }{}{block}{Optics Storage CD-ROM} -\major{18}{}{char }{Chase serial card -- alternate devices} -\major{ }{}{block}{Sanyo CD-ROM} -\major{19}{}{char }{Cyclades serial card} -\major{ }{}{block}{``Double'' compressed disk} -\major{20}{}{char }{Cyclades serial card -- alternate devices} -\major{ }{}{block}{Hitachi CD-ROM} -\major{21}{}{char }{Generic SCSI access} -\major{ }{}{block }{Acorn MFM hard drive interface} -\major{22}{}{char }{Digiboard serial card} -\major{ }{}{block}{Second IDE hard disk/CD-ROM interface} -\major{23}{}{char }{Digiboard serial card -- alternate devices} -\major{ }{}{block}{Mitsumi proprietary CD-ROM} -\major{24}{}{char }{Stallion serial card} -\major{ }{}{block}{Sony CDU-535 CD-ROM} -\major{25}{}{char }{Stallion serial card -- alternate devices} -\major{ }{}{block}{First Matsushita (Panasonic/SoundBlaster) CD-ROM} -\major{26}{}{char }{Quanta WinVision frame grabber} -\major{ }{}{block}{Second Matsushita (Panasonic/SoundBlaster) CD-ROM} -\major{27}{}{char }{QIC-117 tape} -\major{ }{}{block}{Third Matsushita (Panasonic/SoundBlaster) CD-ROM} -\major{28}{}{char }{Stallion serial card -- card programming} -\major{ }{}{char }{Atari SLM ACSI laser printer (68k/Atari)} -\major{ }{}{block}{Fourth Matsushita (Panasonic/SoundBlaster) CD-ROM} -\major{ }{}{block}{ACSI disk/CD-ROM (68k/Atari)} -\major{29}{}{char }{Universal frame buffer} -\major{ }{}{block}{Aztech/Orchid/Okano/Wearnes CD-ROM} -\major{30}{}{char }{iBCS-2 compatibility devices} -\major{ }{}{block}{Philips LMS CM-205 CD-ROM} -\major{31}{}{char }{MPU-401 MIDI} -\major{ }{}{block}{ROM/flash memory card} -\major{32}{}{char }{Specialix serial card} -\major{ }{}{block}{Philips LMS CM-206 CD-ROM} -\major{33}{}{char }{Specialix serial card -- alternate devices} -\major{ }{}{block}{Third IDE hard disk/CD-ROM interface} -\major{34}{}{char }{Z8530 HDLC driver} -\major{ }{}{block}{Fourth IDE hard disk/CD-ROM interface} -\major{35}{}{char }{tclmidi MIDI driver} -\major{ }{}{block}{Slow memory ramdisk} -\major{36}{}{char }{Netlink support} -\major{ }{}{block}{MCA ESDI hard disk} -\major{37}{}{char }{IDE tape} -\major{ }{}{block}{Zorro II ramdisk} -\major{38}{}{char }{Myricom PCI Myrinet board} -\major{ }{}{block}{Reserved for Linux/AP+} -\major{39}{}{char }{ML-16P experimental I/O board} -\major{ }{}{block}{Reserved for Linux/AP+} -\major{40}{}{char }{Matrox Meteor frame grabber} -\major{ }{}{block}{Syquest EZ135 parallel port removable drive} -\major{41}{}{char }{Yet Another Micro Monitor} -\major{ }{}{block}{MicroSolutions BackPack parallel port CD-ROM} -\major{42}{}{}{Demo/sample use} -\major{43}{}{char }{isdn4linux virtual modem} -\major{ }{}{block}{Network block devices} -\major{44}{}{char }{isdn4linux virtual modem -- alternate devices} -\major{ }{}{block}{Flash Translation Layer (FTL) filesystems} -\major{45}{}{char }{isdn4linux ISDN BRI driver} -\major{ }{}{block}{Parallel port IDE disk devices} -\major{46}{}{char }{Comtrol Rocketport serial card} -\major{ }{}{block}{Parallel port ATAPI CD-ROM devices} -\major{47}{}{char }{Comtrol Rocketport serial card -- alternate devices} -\major{ }{}{block}{Parallel port ATAPI disk devices} -\major{48}{}{char }{SDL RISCom serial card} -\major{ }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller} -\major{49}{}{char }{SDL RISCom serial card -- alternate devices} -\major{ }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller} -\major{50}{}{char}{Reserved for GLINT} -\major{ }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller} -\major{51}{}{char }{Baycom radio modem} -\major{ }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller} -\major{52}{}{char }{Spellcaster DataComm/BRI ISDN card} -\major{ }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller} -\major{53}{}{char }{BDM interface for remote debugging MC683xx microcontrollers} -\major{ }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller} -\major{54}{}{char }{Electrocardiognosis Holter serial card} -\major{ }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller} -\major{55}{}{char }{DSP56001 digital signal processor} -\major{ }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller} -\major{56}{}{char }{Apple Desktop Bus} -\major{ }{}{block}{Fifth IDE hard disk/CD-ROM interface} -\major{57}{}{char }{Hayes ESP serial card} -\major{ }{}{block}{Sixth IDE hard disk/CD-ROM interface} -\major{58}{}{char }{Hayes ESP serial card -- alternate devices} -\major{ }{}{block}{Reserved for logical volume manager} -\major{59}{}{char }{sf firewall package} -\major{60}{--63}{}{Local/experimental use} -\major{64}{}{char }{ENskip kernel encryption package} -\major{65}{}{char }{Sundance ``plink'' Transputer boards} -\major{ }{}{block}{SCSI disk devices (16-31)} -\major{66}{}{char }{YARC PowerPC PCI coprocessor card} -\major{ }{}{block}{SCSI disk devices (32-47)} -\major{67}{}{char }{Coda network filesystem} -\major{ }{}{block}{SCSI disk devices (48-63)} -\major{68}{}{char }{CAPI 2.0 interface} -\major{ }{}{block}{SCSI disk devices (64-79)} -\major{69}{}{char }{MA16 numeric accelerator card} -\major{ }{}{block}{SCSI disk devices (80-95)} -\major{70}{}{char }{SpellCaster Protocol Services Interface} -\major{ }{}{block}{SCSI disk devices (96-111)} -\major{71}{}{char }{Computone IntelliPort II serial card} -\major{ }{}{block}{SCSI disk devices (112-127)} -\major{72}{}{char }{Computone IntelliPort II serial card -- alternate devices} -\major{73}{}{char }{Computone IntelliPort II serial card -- control devices} -\major{74}{}{char }{SCI bridge} -\major{75}{}{char }{Specialix IO8+ serial card} -\major{76}{}{char }{Specialix IO8+ serial card -- alternate devices} -\major{77}{}{char }{ComScire Quantum Noise Generator} -\major{78}{}{char }{PAM Software's multimodem boards} -\major{79}{}{char }{PAM Software's multimodem boards -- alternate devices} -\major{80}{}{char }{Photometrics AT200 CCD camera} -\major{81}{}{char }{video4linux} -\major{82}{}{char }{WiNRADiO communications receiver card} -\major{83}{}{char }{Teletext/videotext interfaces} -\major{84}{}{char }{Ikon 1011[57] Versatec Greensheet Interface} -\major{85}{}{char }{Linux/SGI shared memory input queue} -\major{86}{}{char }{SCSI media changer} -\major{87}{}{char }{Sony Control-A1 stereo control bus} -\major{88}{}{char }{COMX synchronous serial card} -\major{89}{}{char }{I$^2$C bus interface} -\major{90}{}{char }{Memory Technology Device (RAM, ROM, Flash)} -\major{91}{}{char }{CAN-Bus controller} -\major{92}{}{char }{Reserved for ith Kommunikationstechnik MIC ISDN card} -\major{93}{}{char }{IBM Smart Capture Card frame grabber} -\major{94}{}{char }{miroVIDEO DC10/30 capture/playback device} -\major{95}{}{char }{IP filter} -\major{96}{}{char }{Parallel port ATAPI tape devices} -\major{97}{}{char }{Parallel port generic ATAPI interface} -\major{98}{}{char }{Control and Mesurement Device (comedi)} -\major{99}{}{char }{Raw parallel ports} -\major{100}{}{char }{POTS (analogue telephone) A/B port} -\major{101}{}{char }{Motorola DSP 56xxx board} -\major{102}{}{char }{Philips SAA5249 Teletext signal decoder} -\major{103}{}{char }{Arla network file system} -\major{104}{}{char }{Flash BIOS support} -\major{105}{}{char }{Comtrol VS-1000 serial card} -\major{106}{}{char }{Comtrol VS-1000 serial card -- alternate devices} -\major{107}{}{char }{3Dfx Voodoo Graphics device} -\major{108}{}{char }{Device independent PPP interface} -\major{109}{}{char }{Reserved for logical volume manager} -\major{110}{}{char }{miroMEDIA Surround board} -\major{111}{--119}{}{Unallocated} -\major{120}{--127}{}{Local/experimental use} -\major{128}{--135}{char }{Unix98 PTY masters} -\major{136}{--143}{char }{Unix98 PTY slaves} -\major{144}{--239}{}{Unallocated} -\major{240}{--254}{}{Local/experimental use} -\major{255}{}{}{Reserved} -\end{devicelist} - -\section{Minor numbers} - -\begin{devicelist} -\major{ 0}{}{}{Unnamed devices (e.g. non-device mounts)} - \minor{0}{}{reserved as null device number} -\end{devicelist} - -\begin{devicelist} -\major{ 1}{}{char}{Memory devices} - \minor{1}{/dev/mem}{Physical memory access} - \minor{2}{/dev/kmem}{Kernel virtual memory access} - \minor{3}{/dev/null}{Null device} - \minor{4}{/dev/port}{I/O port access} - \minor{5}{/dev/zero}{Null byte source} - \minor{6}{/dev/core}{OBSOLETE -- should be a link to {\file /proc/kcore}} - \minor{7}{/dev/full}{Returns ENOSPC on write} - \minor{8}{/dev/random}{Nondeterministic random number generator} - \minor{9}{/dev/urandom}{Less secure, but faster random number generator} -\\ -\major{}{}{block}{RAM disk} - \minor{0}{/dev/ram0}{First RAM disk} - \minordots - \minor{7}{/dev/ram7}{Eighth RAM disk} - \minor{250}{/dev/initrd}{Initial RAM disk} -\end{devicelist} - -\noindent -Earlier kernels had {\file /dev/ramdisk} (1, 1) here. {\file /dev/initrd} -refers to a RAM disk which was preloaded by the boot loader. - -\begin{devicelist} -\major{ 2}{}{char}{Pseudo-TTY masters} - \minor{0}{/dev/ptyp0}{First PTY master} - \minor{1}{/dev/ptyp1}{Second PTY master} - \minordots - \minor{255}{/dev/ptyef}{256th PTY master} -\end{devicelist} - -\noindent -Pseudo-TTY's are named as follows: -\begin{itemize} -\item Masters are {\file pty}, slaves are {\file tty}; -\item the fourth letter is one of {\file pqrstuvwxyzabcde} indicating -the 1st through 16th series of 16 pseudo-ttys each, and -\item the fifth letter is one of {\file 0123456789abcdef} indicating -the position within the series. -\end{itemize} - -\noindent -These are the old-style (BSD) PTY devices; Unix98 devices are on major -128 and above and use the PTY master multiplex ({\file /dev/ptmx}) to -acquire a PTY on demand. - -\begin{devicelist} -\major{}{}{block}{Floppy disks} - \minor{0}{/dev/fd0}{Controller 1, drive 1 autodetect} - \minor{1}{/dev/fd1}{Controller 1, drive 2 autodetect} - \minor{2}{/dev/fd2}{Controller 1, drive 3 autodetect} - \minor{3}{/dev/fd3}{Controller 1, drive 4 autodetect} - \minor{128}{/dev/fd4}{Controller 2, drive 1 autodetect} - \minor{129}{/dev/fd5}{Controller 2, drive 2 autodetect} - \minor{130}{/dev/fd6}{Controller 2, drive 3 autodetect} - \minor{131}{/dev/fd7}{Controller 2, drive 4 autodetect} -\\ -\major{}{}{}{To specify format, add to the autodetect device number} - \minor{ 0}{/dev/fd?}{Autodetect format} - \minor{}{}{} - \minor{ 4}{/dev/fd?d360}{5.25\tum\ \num{4}{360}K in a \num{4}{360}K drive\1} - \minor{ 20}{/dev/fd?h360}{5.25\tum\ \num{4}{360}K in a 1200K drive\1} - \minor{ 48}{/dev/fd?h410}{5.25\tum\ \num{4}{410}K in a 1200K drive} - \minor{ 64}{/dev/fd?h420}{5.25\tum\ \num{4}{420}K in a 1200K drive} - \minor{ 24}{/dev/fd?h720}{5.25\tum\ \num{4}{720}K in a 1200K drive} - \minor{ 80}{/dev/fd?h880}{5.25\tum\ \num{4}{880}K in a 1200K drive\1} - \minor{ 8}{/dev/fd?h1200}{5.25\tum\ 1200K in a 1200K drive\1} - \minor{ 40}{/dev/fd?h1440}{5.25\tum\ 1440K in a 1200K drive\1} - \minor{ 56}{/dev/fd?h1476}{5.25\tum\ 1476K in a 1200K drive} - \minor{ 72}{/dev/fd?h1494}{5.25\tum\ 1494K in a 1200K drive} - \minor{ 92}{/dev/fd?h1600}{5.25\tum\ 1600K in a 1200K drive\1} - \minor{}{}{} - \minor{ 12}{/dev/fd?u360}{3.5\tum\ \num{4}{360}K Double Density\2} - \minor{ 16}{/dev/fd?u720}{3.5\tum\ \num{4}{720}K Double Density\1} - \minor{120}{/dev/fd?u800}{3.5\tum\ \num{4}{800}K Double Density\2} - \minor{ 52}{/dev/fd?u820}{3.5\tum\ \num{4}{820}K Double Density} - \minor{ 68}{/dev/fd?u830}{3.5\tum\ \num{4}{830}K Double Density} - \minor{ 84}{/dev/fd?u1040}{3.5\tum\ 1040K Double Density\1} - \minor{ 88}{/dev/fd?u1120}{3.5\tum\ 1120K Double Density\1} - \minor{ 28}{/dev/fd?u1440}{3.5\tum\ 1440K High Density\1} - \minor{124}{/dev/fd?u1600}{3.5\tum\ 1600K High Density\1} - \minor{ 44}{/dev/fd?u1680}{3.5\tum\ 1680K High Density\3} - \minor{ 60}{/dev/fd?u1722}{3.5\tum\ 1722K High Density} - \minor{ 76}{/dev/fd?u1743}{3.5\tum\ 1743K High Density} - \minor{ 96}{/dev/fd?u1760}{3.5\tum\ 1760K High Density} - \minor{116}{/dev/fd?u1840}{3.5\tum\ 1840K High Density\3} - \minor{100}{/dev/fd?u1920}{3.5\tum\ 1920K High Density\1} - \minor{ 32}{/dev/fd?u2880}{3.5\tum\ 2880K Extra Density\1} - \minor{104}{/dev/fd?u3200}{3.5\tum\ 3200K Extra Density} - \minor{108}{/dev/fd?u3520}{3.5\tum\ 3520K Extra Density} - \minor{112}{/dev/fd?u3840}{3.5\tum\ 3840K Extra Density\1} - \minor{}{}{} - \minor{36}{/dev/fd?CompaQ}{Compaq 2880K drive; probably obsolete} -\\ -\major{}{}{}{\1 Autodetectable format} -\major{}{}{}{\2 Autodetectable format in a Double Density (720K) drive only} -\major{}{}{}{\3 Autodetectable format in a High Density (1440K) drive only} -\end{devicelist} - -NOTE: The letter in the device name ({\file d}, {\file q}, {\file h} -or {\file u}) signifies the type of drive supported: 5.25\tum\ Double -Density ({\file d}), 5.25\tum\ Quad Density ({\file q}), 5.25\tum\ -High Density ({\file h}) or 3.5\tum\ (any type, {\file u}). The -capital letters {\file D}, {\file H}, or {\file E} for the 3.5\tum\ -models have been deprecated, since the drive type is insignificant for -these devices. - -\begin{devicelist} -\major{ 3}{}{char}{Pseudo-TTY slaves} - \minor{0}{/dev/ttyp0}{First PTY slave} - \minor{1}{/dev/ttyp1}{Second PTY slave} - \minordots - \minor{255}{/dev/ttyef}{256th PTY slave} -\end{devicelist} - -\noindent -These are the old-style (BSD) PTY devices; Unix98 devices are on major -136 and above. - -\begin{devicelist} -\major{}{}{block}{First MFM, RLL and IDE hard disk/CD-ROM interface} - \minor{0}{/dev/hda}{Master: whole disk (or CD-ROM)} - \minor{64}{/dev/hdb}{Slave: whole disk (or CD-ROM)} -\\ -\major{}{}{}{For partitions, add to the whole disk device number} - \minor{0}{/dev/hd?}{Whole disk} - \minor{1}{/dev/hd?1}{First partition} - \minor{2}{/dev/hd?2}{Second partition} - \minordots - \minor{63}{/dev/hd?63}{63rd partition} -\end{devicelist} - -\noindent -For MS-DOS style partition tables (typically used by Linux/i386 and -sometimes on Linux/Alpha), partitions 1-4 are the primary partitions, -partitions 5 and up are logical partitions. For other partitioning -schemes, the meaning of the numbers vary. - -\begin{devicelist} -\major{ 4}{}{char }{TTY devices} - \minor{0}{/dev/tty0}{Current virtual console} - \minor{1}{/dev/tty1}{First virtual console} - \minordots - \minor{63}{/dev/tty63}{63rd virtual console} - \minor{64}{/dev/ttyS0}{First serial port} - \minordots - \minor{127}{/dev/ttyS63}{64th serial port} - \minor{128}{/dev/ptyp0}{OBSOLETE} - \minordots - \minor{191}{/dev/ptysf}{OBSOLETE} - \minor{192}{/dev/ttyp0}{OBSOLETE} - \minordots - \minor{255}{/dev/ttysf}{OBSOLETE} -\end{devicelist} - -\noindent -Older versions of the Linux kernel used this major number for BSD PTY -devices. As of Linux 2.1.115, this is no longer supported. Use major -numbers 2 and 3. - -\begin{devicelist} -\major{ 5}{}{char }{Alternate TTY devices} - \minor{0}{/dev/tty}{Current TTY device} - \minor{1}{/dev/console}{System console} - \minor{2}{/dev/ptmx}{PTY master multiplex} - \minor{64}{/dev/cua0}{Callout device corresponding to {\file ttyS0}} - \minordots - \minor{127}{/dev/cua63}{Callout device corresponding to {\file ttyS63}} -\end{devicelist} - -\noindent -(5,1) is {\file /dev/console} starting with Linux 2.1.71. See the -section on terminal devices for more information on {\file /dev/console}. - -\begin{devicelist} -\major{ 6}{}{char }{Parallel printer devices} - \minor{0}{/dev/lp0}{First parallel printer ({\hex 0x3bc})} - \minor{1}{/dev/lp1}{Second parallel printer ({\hex 0x378})} - \minor{2}{/dev/lp2}{Third parallel printer ({\hex 0x278})} -\end{devicelist} - -\noindent -Not all computers have the {\hex 0x3bc} parallel port, hence the -"first" printer may be either {\file /dev/lp0} or {\file /dev/lp1}. - -\begin{devicelist} -\major{ 7}{}{char }{Virtual console access devices} - \minor{0}{/dev/vcs}{Current vc text access} - \minor{1}{/dev/vcs1}{tty1 text access} - \minordots - \minor{63}{/dev/vcs63}{tty63 text access} - \minor{128}{/dev/vcsa}{Current vc text/attribute access} - \minor{129}{/dev/vcsa1}{tty1 text/attribute access} - \minordots - \minor{191}{/dev/vcsa63}{tty63 text/attribute access} -\end{devicelist} - -\noindent -NOTE: These devices permit both read and write access. - -\begin{devicelist} -\major{ }{}{block}{Loopback devices} - \minor{0}{/dev/loop0}{First loopback device} - \minor{1}{/dev/loop1}{Second loopback device} - \minordots -\end{devicelist} - -\noindent -The loopback devices are used to mount filesystems not associated with -block devices. The binding to the loopback devices is handled by -{\bf mount}(8) or {\bf losetup}(8). - -\begin{devicelist} -\major{ 8}{}{block}{SCSI disk devices (0-15)} - \minor{0}{/dev/sda}{First SCSI disk whole disk} - \minor{16}{/dev/sdb}{Second SCSI disk whole disk} - \minor{32}{/dev/sdc}{Third SCSI disk whole disk} - \minordots - \minor{240}{/dev/sdp}{Sixteenth SCSI disk whole disk} -\end{devicelist} - -\noindent -Partitions are handled in the same way as for IDE disks (see major -number 3) except that the partition limit is 15 rather than 63 per -disk. - -\begin{devicelist} -\major{ 9}{}{char }{SCSI tape devices} - \minor{0}{/dev/st0}{First SCSI tape, mode 0} - \minor{1}{/dev/st1}{Second SCSI tape, mode 0} - \minordots - \minor{32}{/dev/st0l}{First SCSI tape, mode 1} - \minor{33}{/dev/st1l}{Second SCSI tape, mode 1} - \minordots - \minor{64}{/dev/st0m}{First SCSI tape, mode 2} - \minor{65}{/dev/st1m}{Second SCSI tape, mode 2} - \minordots - \minor{96}{/dev/st0a}{First SCSI tape, mode 3} - \minor{97}{/dev/st1a}{Second SCSI tape, mode 4} - \minordots - \minor{128}{/dev/nst0}{First SCSI tape, mode 0, no rewind} - \minor{129}{/dev/nst1}{Second SCSI tape, mode 0, no rewind} - \minordots - \minor{160}{/dev/nst0l}{First SCSI tape, mode 1, no rewind} - \minor{161}{/dev/nst1l}{Second SCSI tape, mode 1, no rewind} - \minordots - \minor{192}{/dev/nst0m}{First SCSI tape, mode 2, no rewind} - \minor{193}{/dev/nst1m}{Second SCSI tape, mode 2, no rewind} - \minordots - \minor{224}{/dev/nst0a}{First SCSI tape, mode 3, no rewind} - \minor{225}{/dev/nst1a}{Second SCSI tape, mode 3, no rewind} - \minordots -\end{devicelist} - -\noindent -``No rewind'' refers to the omission of the default automatic rewind -on device close. The {\file MTREW} or {\file MTOFFL} ioctl()s can be -used to rewind the tape regardless of the device used to access it. - -\begin{devicelist} -\major{ }{}{block}{Metadisk (RAID) devices} - \minor{0}{/dev/md0}{First metadisk group} - \minor{1}{/dev/md1}{Second metadisk group} - \minordots -\end{devicelist} - -\noindent -The metadisk driver is used to span a filesystem across multiple -physical disks. - -\begin{devicelist} -\major{10}{}{char }{Non-serial mice, misc features} - \minor{0}{/dev/logibm}{Logitech bus mouse} - \minor{1}{/dev/psaux}{PS/2-style mouse port} - \minor{2}{/dev/inportbm}{Microsoft Inport bus mouse} - \minor{3}{/dev/atibm}{ATI XL bus mouse} - \minor{4}{/dev/jbm}{J-mouse} - \minor{4}{/dev/amigamouse}{Amiga mouse (68k/Amiga)} - \minor{5}{/dev/atarimouse}{Atari mouse} - \minor{6}{/dev/sunmouse}{Sun mouse} - \minor{7}{/dev/amigamouse1}{Second Amiga mouse} - \minor{8}{/dev/smouse}{Simple serial mouse driver} - \minor{9}{/dev/pc110pad}{IBM PC-110 digitizer pad} - \minor{128}{/dev/beep}{Fancy beep device} - \minor{129}{/dev/modreq}{Kernel module load request} - \minor{130}{/dev/watchdog}{Watchdog timer port} - \minor{131}{/dev/temperature}{Machine internal temperature} - \minor{132}{/dev/hwtrap}{Hardware fault trap} - \minor{133}{/dev/exttrp}{External device trap} - \minor{134}{/dev/apm\_bios}{Advanced Power Management BIOS} - \minor{135}{/dev/rtc}{Real Time Clock} - \minor{139}{/dev/openprom}{SPARC OpenBoot PROM} - \minor{140}{/dev/relay8}{Berkshire Products Octal relay card} - \minor{141}{/dev/relay16}{Berkshire Products ISO-16 relay card} - \minor{142}{/dev/msr}{x86 model specific registers} - \minor{143}{/dev/pciconf}{PCI configuration space} - \minor{144}{/dev/nvram}{Non-volatile configuration RAM} - \minor{145}{/dev/hfmodem}{Soundcard shortwave modem control} - \minor{146}{/dev/graphics}{Linux/SGI graphics device} - \minor{147}{/dev/opengl}{Linux/SGI OpenGL pipe} - \minor{148}{/dev/gfx}{Linux/SGI graphics effects device} - \minor{149}{/dev/input/mouse}{Linux/SGI Irix emulation mouse} - \minor{150}{/dev/input/keyboard}{Linux/SGI Irix emulation keyboard} - \minor{151}{/dev/led}{Front panel LEDs} - \minor{153}{/dev/mergemem}{Memory merge device} - \minor{154}{/dev/pmu}{Macintosh PowerBook power manager} -\end{devicelist} - -\begin{devicelist} -\major{11}{}{char }{Raw keyboard device} - \minor{0}{/dev/kbd}{Raw keyboard device} -\end{devicelist} - -\noindent -The raw keyboard device is used on Linux/SPARC only. - -\begin{devicelist} -\major{ }{}{block}{SCSI CD-ROM devices} - \minor{0}{/dev/sr0}{First SCSI CD-ROM} - \minor{1}{/dev/sr1}{Second SCSI CD-ROM} - \minordots -\end{devicelist} - -\noindent -The prefix {\file /dev/scd} instead of {\file /dev/sr} has been used -as well, and might make more sense. - -\begin{devicelist} -\major{12}{}{char }{QIC-02 tape} - \minor{2}{/dev/ntpqic11}{QIC-11, no rewind-on-close} - \minor{3}{/dev/tpqic11}{QIC-11, rewind-on-close} - \minor{4}{/dev/ntpqic24}{QIC-24, no rewind-on-close} - \minor{5}{/dev/tpqic24}{QIC-24, rewind-on-close} - \minor{6}{/dev/ntpqic120}{QIC-120, no rewind-on-close} - \minor{7}{/dev/tpqic120}{QIC-120, rewind-on-close} - \minor{8}{/dev/ntpqic150}{QIC-150, no rewind-on-close} - \minor{9}{/dev/tpqic150}{QIC-150, rewind-on-close} -\end{devicelist} - -\noindent -The device names specified are proposed -- if there are ``standard'' -names for these devices, please let me know. - -\begin{devicelist} -\major{ }{}{block}{MSCDEX CD-ROM callback support} - \minor{0}{/dev/dos\_cd0}{First MSCDEX CD-ROM} - \minor{1}{/dev/dos\_cd1}{Second MSCDEX CD-ROM} - \minordots -\end{devicelist} - -\begin{devicelist} -\major{13}{}{char }{PC speaker} - \minor{0}{/dev/pcmixer}{Emulates {\file /dev/mixer}} - \minor{3}{/dev/pcsp}{Emulates {\file /dev/dsp} (8-bit)} - \minor{4}{/dev/pcaudio}{Emulates {\file /dev/audio}} - \minor{5}{/dev/pcsp16}{Emulates {\file /dev/dsp} (16-bit)} -\\ -\major{ }{}{block}{8-bit MFM/RLL/IDE controller} - \minor{0}{/dev/xda}{First XT disk whole disk} - \minor{64}{/dev/xdb}{Second XT disk whole disk} -\end{devicelist} - -\noindent -Partitions are handled in the same way as for IDE disks (see major -number 3). - -\begin{devicelist} -\major{14}{}{char }{Sound card} - \minor{0}{/dev/mixer}{Mixer control} - \minor{1}{/dev/sequencer}{Audio sequencer} - \minor{2}{/dev/midi00}{First MIDI port} - \minor{3}{/dev/dsp}{Digital audio} - \minor{4}{/dev/audio}{Sun-compatible digital audio} - \minor{6}{/dev/sndstat}{Sound card status information} - \minor{8}{/dev/sequencer2}{Sequencer -- alternate device} - \minor{16}{/dev/mixer1}{Second soundcard mixer control} - \minor{17}{/dev/patmgr0}{Sequencer patch manager} - \minor{18}{/dev/midi01}{Second MIDI port} - \minor{19}{/dev/dsp1}{Second soundcard digital audio} - \minor{20}{/dev/audio1}{Second soundcard Sun digital audio} - \minor{33}{/dev/patmgr1}{Sequencer patch manager} - \minor{34}{/dev/midi02}{Third MIDI port} - \minor{50}{/dev/midi03}{Fourth MIDI port} -\\ -\major{ }{}{block}{BIOS harddrive callback support} - \minor{0}{/dev/dos\_hda}{First BIOS harddrive whole disk} - \minor{64}{/dev/dos\_hdb}{Second BIOS harddrive whole disk} - \minor{128}{/dev/dos\_hdc}{Third BIOS harddrive whole disk} - \minor{192}{/dev/dos\_hdd}{Fourth BIOS harddrive whole disk} -\end{devicelist} - -\noindent -Partitions are handled in the same way as for IDE disks (see major -number 3). - -\begin{devicelist} -\major{15}{}{char }{Joystick} - \minor{0}{/dev/js0}{First analog joystick} - \minor{1}{/dev/js1}{Second analog joystick} - \minordots - \minor{128}{/dev/djs0}{First digital joystick} - \minor{129}{/dev/djs1}{Second digital joystick} - \minordots -\\ -\major{ }{}{block}{Sony CDU-31A/CDU-33A CD-ROM} - \minor{0}{/dev/sonycd}{Sony CDU-31A CD-ROM} -\end{devicelist} - -\begin{devicelist} -\major{16}{}{char }{Non-SCSI scanners} - \minor{0}{/dev/gs4500}{Genius 4500 handheld scanner} -\\ -\major{ }{}{block}{GoldStar CD-ROM} - \minor{0}{/dev/gscd}{GoldStar CD-ROM} -\end{devicelist} - -\begin{devicelist} -\major{17}{}{char }{Chase serial card} - \minor{0}{/dev/ttyH0}{First Chase port} - \minor{1}{/dev/ttyH1}{Second Chase port} - \minordots -\\ -\major{ }{}{block}{Optics Storage CD-ROM} - \minor{0}{/dev/optcd}{Optics Storage CD-ROM} -\end{devicelist} - -\begin{devicelist} -\major{18}{}{char }{Chase serial card -- alternate devices} - \minor{0}{/dev/cuh0}{Callout device corresponding to {\file ttyH0}} - \minor{1}{/dev/cuh1}{Callout device corresponding to {\file ttyH1}} - \minordots -\\ -\major{ }{}{block}{Sanyo CD-ROM} - \minor{0}{/dev/sjcd}{Sanyo CD-ROM} -\end{devicelist} - -\begin{devicelist} -\major{19}{}{char }{Cyclades serial card} - \minor{0}{/dev/ttyC0}{First Cyclades port} - \minordots - \minor{31}{/dev/ttyC31}{32nd Cyclades port} -\\ -\major{ }{}{block}{``Double'' compressed disk} - \minor{0}{/dev/double0}{First compressed disk} - \minordots - \minor{7}{/dev/double7}{Eighth compressed disk} - \minor{128}{/dev/cdouble0}{Mirror of first compressed disk} - \minordots - \minor{135}{/dev/cdouble7}{Mirror of eighth compressed disk} -\end{devicelist} - -\noindent -See the Double documentation for an explanation of the ``mirror'' devices. - -\begin{devicelist} -\major{20}{}{char }{Cyclades serial card -- alternate devices} - \minor{0}{/dev/cub0}{Callout device corresponding to {\file ttyC0}} - \minordots - \minor{31}{/dev/cub31}{Callout device corresponding to {\file ttyC31}} -\\ -\major{ }{}{block}{Hitachi CD-ROM} - \minor{0}{/dev/hitcd}{Hitachi CD-ROM} -\end{devicelist} - -\begin{devicelist} -\major{21}{}{char }{Generic SCSI access} - \minor{0}{/dev/sg0}{First generic SCSI device} - \minor{1}{/dev/sg1}{Second generic SCSI device} - \minordots -\end{devicelist} - -\noindent -Most distributions name these {\file /dev/sga}, {\file /dev/sgb}... -This sets an unneccesary limit of 26 SCSI devices in the system, and -is counter to standard Linux device-naming practice. - -\begin{devicelist} -\major{ }{}{block }{Acorn MFM hard drive interface} - \minor{0}{/dev/mfma}{First MFM drive whole disk} - \minor{64}{/dev/mfmb}{Second MFM drive whole disk} -\end{devicelist} - -\noindent -This device is used on the ARM-based Acorn RiscPC. Partitions are -handled the same way as for IDE disks (see major number 3). - -\begin{devicelist} -\major{22}{}{char }{Digiboard serial card} - \minor{0}{/dev/ttyD0}{First Digiboard port} - \minor{1}{/dev/ttyD1}{Second Digiboard port} - \minordots -\\ -\major{ }{}{block}{Second IDE hard disk/CD-ROM interface} - \minor{0}{/dev/hdc}{Master: whole disk (or CD-ROM)} - \minor{64}{/dev/hdd}{Slave: whole disk (or CD-ROM)} -\end{devicelist} - -\noindent -Partitions are handled the same way as for the first interface (see -major number 3). - -\begin{devicelist} -\major{23}{}{char }{Digiboard serial card -- alternate devices} - \minor{0}{/dev/cud0}{Callout device corresponding to {\file ttyD0}} - \minor{1}{/dev/cud1}{Callout device corresponding to {\file ttyD1}} - \minordots -\major{ }{}{block}{Mitsumi proprietary CD-ROM} - \minor{0}{/dev/mcd}{Mitsumi CD-ROM} -\end{devicelist} - -\begin{devicelist}\ -\major{24}{}{char }{Stallion serial card} - \minor{0}{/dev/ttyE0}{Stallion port 0 board 0} - \minor{1}{/dev/ttyE1}{Stallion port 1 board 0} - \minordots - \minor{64}{/dev/ttyE64}{Stallion port 0 board 1} - \minor{65}{/dev/ttyE65}{Stallion port 1 board 1} - \minordots - \minor{128}{/dev/ttyE128}{Stallion port 0 board 2} - \minor{129}{/dev/ttyE129}{Stallion port 1 board 2} - \minordots - \minor{192}{/dev/ttyE192}{Stallion port 0 board 3} - \minor{193}{/dev/ttyE193}{Stallion port 1 board 3} - \minordots -\\ -\major{ }{}{block}{Sony CDU-535 CD-ROM} - \minor{0}{/dev/cdu535}{Sony CDU-535 CD-ROM} -\end{devicelist} - -\begin{devicelist} -\major{25}{}{char }{Stallion serial card -- alternate devices} - \minor{0}{/dev/cue0}{Callout device corresponding to {\file ttyE0}} - \minor{1}{/dev/cue1}{Callout device corresponding to {\file ttyE1}} - \minordots - \minor{64}{/dev/cue64}{Callout device corresponding to {\file ttyE64}} - \minor{65}{/dev/cue65}{Callout device corresponding to {\file ttyE65}} - \minordots - \minor{128}{/dev/cue128}{Callout device corresponding to {\file ttyE128}} - \minor{129}{/dev/cue129}{Callout device corresponding to {\file ttyE129}} - \minordots - \minor{192}{/dev/cue192}{Callout device corresponding to {\file ttyE192}} - \minor{193}{/dev/cue193}{Callout device corresponding to {\file ttyE193}} - \minordots -\\ -\major{ }{}{block}{First Matsushita (Panasonic/SoundBlaster) CD-ROM} - \minor{0}{/dev/sbpcd0}{Panasonic CD-ROM controller 0 unit 0} - \minor{1}{/dev/sbpcd1}{Panasonic CD-ROM controller 0 unit 1} - \minor{2}{/dev/sbpcd2}{Panasonic CD-ROM controller 0 unit 2} - \minor{3}{/dev/sbpcd3}{Panasonic CD-ROM controller 0 unit 3} -\end{devicelist} - -\begin{devicelist} -\major{26}{}{char }{Quanta WinVision frame grabber} - \minor{0}{/dev/wvisfgrab}{Quanta WinVision frame grabber} -\\ -\major{ }{}{block}{Second Matsushita (Panasonic/SoundBlaster) CD-ROM} - \minor{0}{/dev/sbpcd4}{Panasonic CD-ROM controller 1 unit 0} - \minor{1}{/dev/sbpcd5}{Panasonic CD-ROM controller 1 unit 1} - \minor{2}{/dev/sbpcd6}{Panasonic CD-ROM controller 1 unit 2} - \minor{3}{/dev/sbpcd7}{Panasonic CD-ROM controller 1 unit 3} -\end{devicelist} - -\begin{devicelist} -\major{27}{}{char }{QIC-117 tape} - \minor{0}{/dev/qft0}{Unit 0, rewind-on-close} - \minor{1}{/dev/qft1}{Unit 1, rewind-on-close} - \minor{2}{/dev/qft2}{Unit 2, rewind-on-close} - \minor{3}{/dev/qft3}{Unit 3, rewind-on-close} - \minor{4}{/dev/nqft0}{Unit 0, no rewind-on-close} - \minor{5}{/dev/nqft1}{Unit 1, no rewind-on-close} - \minor{6}{/dev/nqft2}{Unit 2, no rewind-on-close} - \minor{7}{/dev/nqft3}{Unit 3, no rewind-on-close} - \minor{16}{/dev/zqft0}{Unit 0, rewind-on-close, compression} - \minor{17}{/dev/zqft1}{Unit 1, rewind-on-close, compression} - \minor{18}{/dev/zqft2}{Unit 2, rewind-on-close, compression} - \minor{19}{/dev/zqft3}{Unit 3, rewind-on-close, compression} - \minor{20}{/dev/nzqft0}{Unit 0, no rewind-on-close, compression} - \minor{21}{/dev/nzqft1}{Unit 1, no rewind-on-close, compression} - \minor{22}{/dev/nzqft2}{Unit 2, no rewind-on-close, compression} - \minor{23}{/dev/nzqft3}{Unit 3, no rewind-on-close, compression} - \minor{32}{/dev/rawqft0}{Unit 0, rewind-on-close, no file marks} - \minor{33}{/dev/rawqft1}{Unit 1, rewind-on-close, no file marks} - \minor{34}{/dev/rawqft2}{Unit 2, rewind-on-close, no file marks} - \minor{35}{/dev/rawqft3}{Unit 3, rewind-on-close, no file marks} - \minor{36}{/dev/nrawqft0}{Unit 0, no rewind-on-close, no file marks} - \minor{37}{/dev/nrawqft1}{Unit 1, no rewind-on-close, no file marks} - \minor{38}{/dev/nrawqft2}{Unit 2, no rewind-on-close, no file marks} - \minor{39}{/dev/nrawqft3}{Unit 3, no rewind-on-close, no file marks} -\\ -\major{ }{}{block}{Third Matsushita (Panasonic/SoundBlaster) CD-ROM} - \minor{0}{/dev/sbpcd8}{Panasonic CD-ROM controller 2 unit 0} - \minor{1}{/dev/sbpcd9}{Panasonic CD-ROM controller 2 unit 1} - \minor{2}{/dev/sbpcd10}{Panasonic CD-ROM controller 2 unit 2} - \minor{3}{/dev/sbpcd11}{Panasonic CD-ROM controller 2 unit 3} -\end{devicelist} - -\begin{devicelist} -\major{28}{}{char }{Stallion serial card -- card programming} - \minor{0}{/dev/staliomem0}{First Stallion I/O card memory} - \minor{1}{/dev/staliomem1}{Second Stallion I/O card memory} - \minor{2}{/dev/staliomem2}{Third Stallion I/O card memory} - \minor{3}{/dev/staliomem3}{Fourth Stallion I/O card memory} -\\ -\major{ }{}{char }{Atari SLM ACSI laser printer (68k/Atari)} - \minor{0}{/dev/slm0}{First SLM laser printer} - \minor{1}{/dev/slm1}{Second SLM laser printer} - \minordots -\\ -\major{ }{}{block}{Fourth Matsushita (Panasonic/SoundBlaster) CD-ROM} - \minor{0}{/dev/sbpcd12}{Panasonic CD-ROM controller 3 unit 0} - \minor{1}{/dev/sbpcd13}{Panasonic CD-ROM controller 3 unit 1} - \minor{2}{/dev/sbpcd14}{Panasonic CD-ROM controller 3 unit 2} - \minor{3}{/dev/sbpcd15}{Panasonic CD-ROM controller 3 unit 3} -\\ -\major{ }{}{block}{ACSI disk/CD-ROM (68k/Atari)} - \minor{0}{/dev/ada}{First ACSI disk whole disk} - \minor{16}{/dev/adb}{Second ACSI disk whole disk} - \minor{32}{/dev/adc}{Third ACSI disk whole disk} - \minordots - \minor{240}{/dev/adp}{Sixteenth ACSI disk whole disk} -\end{devicelist} - -\noindent -Partitions are handled in the same way as for IDE disks (see major -number 3) except that the partition limit is 15 rather than 63 per -disk (same as SCSI.) - -\begin{devicelist} -\major{29}{}{char }{Universal frame buffer} - \minor{0}{/dev/fb0}{First frame buffer} - \minor{32}{/dev/fb1}{Second frame buffer} - \minor{64}{/dev/fb2}{Third frame buffer} - \minordots - \minor{224}{/dev/fb7}{Eighth frame buffer} -\end{devicelist} - -\noindent -All additional minor device numbers are reserved. - -\begin{devicelist} -\major{ }{}{block}{Aztech/Orchid/Okano/Wearnes CD-ROM} - \minor{0}{/dev/aztcd}{Aztech CD-ROM} -\end{devicelist} - -\begin{devicelist} -\major{30}{}{char }{iBCS-2 compatibility devices} - \minor{0}{/dev/socksys}{Socket access} - \minor{1}{/dev/spx}{SVR3 local X interface} - \minor{2}{/dev/inet/arp}{Network access} - \minor{2}{/dev/inet/icmp}{Network access} - \minor{2}{/dev/inet/ip}{Network access} - \minor{2}{/dev/inet/udp}{Network access} - \minor{2}{/dev/inet/tcp}{Network access} -\end{devicelist} - -\noindent -Additionally, iBCS-2 requires {\file /dev/nfsd} to be a link to {\file -/dev/socksys} and {\file /dev/X0R} to be a link to {\file /dev/null}. - -\begin{devicelist} -\major{ }{}{block}{Philips LMS CM-205 CD-ROM} - \minor{0}{/dev/cm205cd}{Philips LMS CM-205 CD-ROM} -\end{devicelist} - -\noindent -{\file /dev/lmscd} is an older name for this drive. This driver does -not work with the CM-205MS CD-ROM. - -\begin{devicelist} -\major{31}{}{char }{MPU-401 MIDI} - \minor{0}{/dev/mpu401data}{MPU-401 data port} - \minor{1}{/dev/mpu401stat}{MPU-401 status port} -\\ -\major{ }{}{block}{ROM/flash memory card} - \minor{0}{/dev/rom0}{First ROM card (rw)} - \minordots - \minor{7}{/dev/rom7}{Eighth ROM card (rw)} - \minor{8}{/dev/rrom0}{First ROM card (ro)} - \minordots - \minor{15}{/dev/rrom0}{Eighth ROM card (ro)} - \minor{16}{/dev/flash0}{First flash memory card (rw)} - \minordots - \minor{23}{/dev/flash7}{Eighth flash memory card (rw)} - \minor{24}{/dev/rflash0}{First flash memory card (ro)} - \minordots - \minor{31}{/dev/rflash7}{Eighth flash memory card (ro)} -\end{devicelist} - -\noindent -The read-write (rw) devices support back-caching written data in RAM, -as well as writing to flash RAM devices. The read-only devices (ro) -support reading only. - -\begin{devicelist} -\major{32}{}{char }{Specialix serial card} - \minor{0}{/dev/ttyX0}{First Specialix port} - \minor{1}{/dev/ttyX1}{Second Specialix port} - \minordots -\\ -\major{ }{}{block}{Philips LMS CM-206 CD-ROM} - \minor{0}{/dev/cm206cd}{Philips LMS CM-206 CD-ROM} -\end{devicelist} - -\begin{devicelist} -\major{33}{}{char }{Specialix serial card -- alternate devices} - \minor{0}{/dev/cux0}{Callout device corresponding to {\file ttyX0}} - \minor{1}{/dev/cux1}{Callout device corresponding to {\file ttyX1}} - \minordots -\\ -\major{ }{}{block}{Third IDE hard disk/CD-ROM interface} - \minor{0}{/dev/hde}{Master: whole disk (or CD-ROM)} - \minor{64}{/dev/hdf}{Slave: whole disk (or CD-ROM)} -\end{devicelist} - -\noindent -Partitions are handled the same way as for the first interface (see -major number 3). - -\begin{devicelist} -\major{34}{}{char }{Z8530 HDLC driver} - \minor{0}{/dev/scc0}{First Z8530, first port} - \minor{1}{/dev/scc1}{First Z8530, second port} - \minor{2}{/dev/scc2}{Second Z8530, first port} - \minor{3}{/dev/scc3}{Second Z8530, second port} - \minordots -\end{devicelist} - -\noindent -In a previous version these devices were named {\file /dev/sc1} for -{\file /dev/scc0}, {\file /dev/sc2} for {\file /dev/scc1}, and so on. - -\begin{devicelist} -\major{ }{}{block}{Fourth IDE hard disk/CD-ROM interface} - \minor{0}{/dev/hdg}{Master: whole disk (or CD-ROM)} - \minor{64}{/dev/hdh}{Slave: whole disk (or CD-ROM)} -\end{devicelist} - -\noindent -Partitions are handled the same way as for the first interface (see -major number 3). - -\begin{devicelist} -\major{35}{}{char }{tclmidi MIDI driver} - \minor{0}{/dev/midi0}{First MIDI port, kernel timed} - \minor{1}{/dev/midi1}{Second MIDI port, kernel timed} - \minor{2}{/dev/midi2}{Third MIDI port, kernel timed} - \minor{3}{/dev/midi3}{Fourth MIDI port, kernel timed} - \minor{64}{/dev/rmidi0}{First MIDI port, untimed} - \minor{65}{/dev/rmidi1}{Second MIDI port, untimed} - \minor{66}{/dev/rmidi2}{Third MIDI port, untimed} - \minor{67}{/dev/rmidi3}{Fourth MIDI port, untimed} - \minor{128}{/dev/smpte0}{First MIDI port, SMPTE timed} - \minor{129}{/dev/smpte1}{Second MIDI port, SMPTE timed} - \minor{130}{/dev/smpte2}{Third MIDI port, SMPTE timed} - \minor{131}{/dev/smpte3}{Fourth MIDI port, SMPTE timed} -\\ -\major{ }{}{block}{Slow memory ramdisk} - \minor{0}{/dev/slram}{Slow memory ramdisk} -\end{devicelist} - -\begin{devicelist} -\major{36}{}{char }{Netlink support} - \minor{0}{/dev/route}{Routing, device updates (kernel to user)} - \minor{1}{/dev/skip}{enSKIP security cache control} -\\ -\major{ }{}{block}{MCA ESDI hard disk} - \minor{0}{/dev/eda}{First ESDI disk whole disk} - \minor{64}{/dev/edb}{Second ESDI disk whole disk} - \minordots -\end{devicelist} - -\noindent -Partitions are handled the same way as for IDE disks (see major number -3). - -\begin{devicelist} -\major{37}{}{char }{IDE tape} - \minor{0}{/dev/ht0}{First IDE tape} - \minor{128}{/dev/nht0}{First IDE tape, no rewind-on-close} -\end{devicelist} - -\noindent -Currently, only one IDE tape drive is supported. - -\begin{devicelist} -\major{ }{}{block}{Zorro II ramdisk} - \minor{0}{/dev/z2ram}{Zorro II ramdisk} -\end{devicelist} - -\begin{devicelist} -\major{38}{}{char }{Myricom PCI Myrinet board} - \minor{0}{/dev/mlanai0}{First Myrinet board} - \minor{1}{/dev/mlanai1}{Second Myrinet board} - \minordots -\end{devicelist} - -\noindent -This device is used for board control, status query and ``user level -packet I/O''. The board is also accessible as a regular {\file eth} -networking device. - -\begin{devicelist} -\major{ }{}{block}{Reserved for Linux/AP+} -\end{devicelist} - -\begin{devicelist} -\major{39}{}{char }{ML-16P experimental I/O board} - \minor{0}{/dev/ml16pa-a0}{First card, first analog channel} - \minor{1}{/dev/ml16pa-a1}{First card, second analog channel} - \minordots - \minor{15}{/dev/ml16pa-a15}{First card, 16th analog channel} - \minor{16}{/dev/ml16pa-d}{First card, digital lines} - \minor{17}{/dev/ml16pa-c0}{First card, first counter/timer} - \minor{18}{/dev/ml16pa-c1}{First card, second counter/timer} - \minor{19}{/dev/ml16pa-c2}{First card, third counter/timer} - \minor{32}{/dev/ml16pb-a0}{Second card, first analog channel} - \minor{33}{/dev/ml16pb-a1}{Second card, second analog channel} - \minordots - \minor{47}{/dev/ml16pb-a15}{Second card, 16th analog channel} - \minor{48}{/dev/ml16pb-d}{Second card, digital lines} - \minor{49}{/dev/ml16pb-c0}{Second card, first counter/timer} - \minor{50}{/dev/ml16pb-c1}{Second card, second counter/timer} - \minor{51}{/dev/ml16pb-c2}{Second card, third counter/timer} - \minordots -\\ -\major{ }{}{block}{Reserved for Linux/AP+} -\end{devicelist} - -\begin{devicelist} -\major{40}{}{char }{Matrox Meteor frame grabber} - \minor{0}{/dev/mmetfgrab}{Matrox Meteor frame grabber} -\\ -\major{ }{}{block}{Syquest EZ135 parallel port removable drive} - \minor{0}{/dev/eza}{Parallel EZ135 drive whole disk} -\end{devicelist} - -\noindent -This device is obsolete and will be removed in a future version of -Linux. It has been replaced with the parallel port IDE disk driver at -major number 45. Partitions are handled the same way as for IDE disks -(see major number 3). - -\begin{devicelist} -\major{41}{}{char }{Yet Another Micro Monitor} - \minor{0}{/dev/yamm}{Yet Another Micro Monitor} -\\ -\major{ }{}{block}{MicroSolutions BackPack parallel port CD-ROM} - \minor{0}{/dev/bpcd}{BackPack CD-ROM} -\end{devicelist} - -\noindent -This device is obsolete and will be removed in a future version of -Linux. It has been replaced with the parallel port ATAPI CD-ROM -driver at major number 46. - -\begin{devicelist} -\major{42}{}{}{Demo/sample use} -\end{devicelist} - -\noindent -This number is intended for use in sample code, as well as a general -``example'' device number. It should never be used for a device -driver that is being distributed; either obtain an official number or -use the local/experimental range. The sudden addition or removal of a -driver with this number should not cause ill effects to the system -(bugs excepted.) - -IN PARTICULAR, ANY DISTRIBUTION WHICH CONTAINS A DEVICE DRIVER USING -MAJOR NUMBER 42 IS NONCOMPLIANT. - -\begin{devicelist} -\major{43}{}{char }{isdn4linux virtual modem} - \minor{0}{/dev/ttyI0}{First virtual modem} - \minordots - \minor{63}{/dev/ttyI63}{64th virtual modem} -\\ -\major{ }{}{block}{Network block devices} - \minor{0}{/dev/nd0}{First network block device} - \minor{1}{/dev/nd1}{Second network block device} - \minordots -\end{devicelist} - -\noindent -Network Block Device is somehow similar to loopback devices: If you -read from it, it sends packet accross network asking server for -data. If you write to it, it sends packet telling server to write. It -could be used to mounting filesystems over the net, swapping over the -net, implementing block device in userland etc. - -\begin{devicelist} -\major{44}{}{char }{isdn4linux virtual modem -- alternate devices} - \minor{0}{/dev/cui0}{Callout device corresponding to {\file ttyI0}} - \minordots - \minor{63}{/dev/cui63}{Callout device corresponding to {\file ttyI63}} -\\ -\major{ }{}{block}{Flash Translation Layer (FTL) filesystems} - \minor{0}{/dev/ftla}{FTL on first Memory Technology Device} - \minor{16}{/dev/ftlb}{FTL on second Memory Technology Device} - \minor{32}{/dev/ftlc}{FTL on third Memory Technology Device} - \minordots - \minor{240}{/dev/ftlp}{FTL on 16th Memory Technology Device} -\end{devicelist} - -\noindent -Partitions are handled in the same way as for IDE disks (see major -number 3) expect that the partition limit is 15 rather than 63 per -disk (same as SCSI.) - -\begin{devicelist} -\major{45}{}{char }{isdn4linux ISDN BRI driver} - \minor{0}{/dev/isdn0}{First virtual B channel raw data} - \minordots - \minor{63}{/dev/isdn63}{64th virtual B channel raw data} - \minor{64}{/dev/isdnctrl0}{First channel control/debug} - \minordots - \minor{127}{/dev/isdnctrl63}{64th channel control/debug} - \minor{128}{/dev/ippp0}{First SyncPPP device} - \minordots - \minor{191}{/dev/ippp63}{64th SyncPPP device} - \minor{255}{/dev/isdninfo}{ISDN monitor interface} -\\ -\major{ }{}{block}{Parallel port IDE disk devices} - \minor{0}{/dev/pda}{First parallel port IDE disk} - \minor{16}{/dev/pdb}{Second parallel port IDE disk} - \minor{32}{/dev/pdc}{Third parallel port IDE disk} - \minor{48}{/dev/pdd}{Fourth parallel port IDE disk} -\end{devicelist} - -\noindent -Partitions are handled in the same way as for IDE disks (see major -number 3) except that the partition limit is 15 rather than 63 per -disk. - -\begin{devicelist} -\major{46}{}{char }{Comtrol Rocketport serial card} - \minor{0}{/dev/ttyR0}{First Rocketport port} - \minor{1}{/dev/ttyR1}{Second Rocketport port} - \minordots -\\ -\major{ }{}{block}{Parallel port ATAPI CD-ROM devices} - \minor{0}{/dev/pcd0}{First parallel port ATAPI CD-ROM} - \minor{1}{/dev/pcd1}{Second parallel port ATAPI CD-ROM} - \minor{2}{/dev/pcd2}{Third parallel port ATAPI CD-ROM} - \minor{3}{/dev/pcd3}{Fourth parallel port ATAPI CD-ROM} -\end{devicelist} - -\begin{devicelist} -\major{47}{}{char }{Comtrol Rocketport serial card -- alternate devices} - \minor{0}{/dev/cur0}{Callout device corresponding to {\file ttyR0}} - \minor{1}{/dev/cur1}{Callout device corresponding to {\file ttyR1}} - \minordots -\\ -\major{ }{}{block}{Parallel port ATAPI disk devices} - \minor{0}{/dev/pf0}{First parallel port ATAPI disk} - \minor{1}{/dev/pf1}{Second parallel port ATAPI disk} - \minor{2}{/dev/pf2}{Third parallel port ATAPI disk} - \minor{3}{/dev/pf3}{Fourth parallel port ATAPI disk} -\end{devicelist} - -\noindent -This driver is intended for floppy disks and similar devices and hence -does not support partitioning. - -\begin{devicelist} -\major{48}{}{char }{SDL RISCom serial card} - \minor{0}{/dev/ttyL0}{First RISCom port} - \minor{1}{/dev/ttyL1}{Second RISCom port} - \minordots -\\ -\major{ }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller} -\end{devicelist} - -\begin{devicelist} -\major{49}{}{char }{SDL RISCom serial card -- alternate devices} - \minor{0}{/dev/cul0}{Callout device corresponding to {\file ttyL0}} - \minor{1}{/dev/cul1}{Callout device corresponding to {\file ttyL1}} - \minordots -\\ -\major{ }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller} -\end{devicelist} - -\begin{devicelist} -\major{50}{}{char}{Reserved for GLINT} -\\ -\major{ }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller} -\end{devicelist} - -\begin{devicelist} -\major{51}{}{char }{Baycom radio modem} - \minor{0}{/dev/bc0}{First Baycom radio modem} - \minor{1}{/dev/bc1}{Second Baycom radio modem} - \minordots -\\ -\major{ }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller} -\end{devicelist} - -\begin{devicelist} -\major{52}{}{char }{Spellcaster DataComm/BRI ISDN card} - \minor{0}{/dev/dcbri0}{First DataComm card} - \minor{1}{/dev/dcbri1}{Second DataComm card} - \minor{2}{/dev/dcbri2}{Third DataComm card} - \minor{3}{/dev/dcbri3}{Fourth DataComm card} -\\ -\major{ }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller} -\end{devicelist} - -\begin{devicelist} -\major{53}{}{char }{BDM interface for remote debugging MC683xx microcontrollers} - \minor{0}{/dev/pd\_bdm0}{PD BDM interface on {\file lp0}} - \minor{1}{/dev/pd\_bdm1}{PD BDM interface on {\file lp1}} - \minor{2}{/dev/pd\_bdm2}{PD BDM interface on {\file lp2}} - \minor{4}{/dev/icd\_bdm0}{ICD BDM interface on {\file lp0}} - \minor{5}{/dev/icd\_bdm1}{ICD BDM interface on {\file lp1}} - \minor{6}{/dev/icd\_bdm2}{ICD BDM interface on {\file lp2}} -\end{devicelist} - -\noindent -This device is used for the interfacing to the MC683xx -microcontrollers via Background Debug Mode by use of a Parallel Port -interface. PD is the Motorola Public Domain Interface and ICD is the -commercial interface by P\&E. - -\begin{devicelist} -\major{ }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller} -\end{devicelist} - -\begin{devicelist} -\major{54}{}{char }{Electrocardiognosis Holter serial card} - \minor{0}{/dev/holter0}{First Holter port} - \minor{1}{/dev/holter1}{Second Holter port} - \minor{2}{/dev/holter2}{Third Holter port} -\end{devicelist} - -\noindent -A custom serial card used by Electrocardiognosis SRL -$<$mseritan@ottonel.pub.ro$>$ to transfer data from Holter 24-hour -heart monitoring equipment. - -\begin{devicelist} -\major{ }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller} -\end{devicelist} - -\begin{devicelist} -\major{55}{}{char }{DSP56001 digital signal processor} - \minor{0}{/dev/dsp56k}{First DSP56001} -\\ -\major{ }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller} -\end{devicelist} - -\begin{devicelist} -\major{56}{}{char }{Apple Desktop Bus} - \minor{0}{/dev/adb}{ADB bus control} -\end{devicelist} - -\noindent -Additional devices will be added to this number, all starting with -{\file /dev/adb}. - -\begin{devicelist} -\major{ }{}{block}{Fifth IDE hard disk/CD-ROM interface} - \minor{0}{/dev/hdi}{Master: whole disk (or CD-ROM)} - \minor{64}{/dev/hdj}{Slave: whole disk (or CD-ROM)} -\end{devicelist} - -\noindent -Partitions are handled the same way as for the first interface (see -major number 3). - -\begin{devicelist} -\major{57}{}{char }{Hayes ESP serial card} - \minor{0}{/dev/ttyP0}{First ESP port} - \minor{1}{/dev/ttyP1}{Second ESP port} - \minordots -\\ -\major{ }{}{block}{Sixth IDE hard disk/CD-ROM interface} - \minor{0}{/dev/hdk}{Master: whole disk (or CD-ROM)} - \minor{64}{/dev/hdl}{Slave: whole disk (or CD-ROM)} -\end{devicelist} - -\noindent -Partitions are handled the same way as for the first interface (see -major number 3). - -\begin{devicelist} -\major{58}{}{char }{Hayes ESP serial card -- alternate devices} - \minor{0}{/dev/cup0}{Callout device corresponding to {\file ttyP0}} - \minor{1}{/dev/cup1}{Callout device corresponding to {\file ttyP1}} - \minordots -\\ -\major{ }{}{block}{Reserved for logical volume manager} -\end{devicelist} - -\begin{devicelist} -\major{59}{}{char }{sf firewall package} - \minor{0}{/dev/firewall}{Communication with sf kernel module} -\end{devicelist} - -\begin{devicelist} -\major{60}{--63}{}{Local/experimental use} -\end{devicelist} - -\noindent -For devices not assigned official numbers, these ranges should be -used, in order to avoid conflict with future assignments. - -\begin{devicelist} -\major{64}{}{char }{ENskip kernel encryption package} - \minor{0}{/dev/enskip}{Communication with ENskip kernel - module} -\end{devicelist} - -\begin{devicelist} -\major{65}{}{char }{Sundance ``plink'' Transputer boards} - \minor{0}{/dev/plink0}{First plink device} - \minor{1}{/dev/plink1}{Second plink device} - \minor{2}{/dev/plink2}{Third plink device} - \minor{3}{/dev/plink3}{Fourth plink device} - \minor{64}{/dev/rplink0}{First plink device, raw} - \minor{65}{/dev/rplink1}{Second plink device, raw} - \minor{66}{/dev/rplink2}{Third plink device, raw} - \minor{67}{/dev/rplink3}{Fourth plink device, raw} - \minor{128}{/dev/plink0d}{First plink device, debug} - \minor{129}{/dev/plink1d}{Second plink device, debug} - \minor{130}{/dev/plink2d}{Third plink device, debug} - \minor{131}{/dev/plink3d}{Fourth plink device, debug} - \minor{192}{/dev/rplink0d}{First plink device, raw, debug} - \minor{193}{/dev/rplink1d}{Second plink device, raw, debug} - \minor{194}{/dev/rplink2d}{Third plink device, raw, debug} - \minor{195}{/dev/rplink3d}{Fourth plink device, raw, debug} -\end{devicelist} - -\noindent -This is a commercial driver; contact James Howes -$<$jth@prosig.demon.co.uk$>$ for information. - -\begin{devicelist} -\major{ }{}{block}{SCSI disk devices (16-31)} - \minor{0}{/dev/sdq}{17th SCSI disk whole disk} - \minor{16}{/dev/sdr}{18th SCSI disk whole disk} - \minor{32}{/dev/sds}{19th SCSI disk whole disk} - \minordots - \minor{240}{/dev/sdaf}{32nd SCSI disk whole disk} -\end{devicelist} - -\noindent -Partitions are handled in the same way as for IDE disks (see major -number 3) except that the partition limit is 15 rather than 63 per -disk. - -\begin{devicelist} -\major{66}{}{char }{YARC PowerPC PCI coprocessor card} - \minor{0}{/dev/yppcpci0}{First YARC card} - \minor{1}{/dev/yppcpci1}{Second YARC card} - \minordots -\end{devicelist} - -\begin{devicelist} -\major{ }{}{block}{SCSI disk devices (32-47)} - \minor{0}{/dev/sdag}{33rd SCSI disk whole disk} - \minor{16}{/dev/sdah}{34th SCSI disk whole disk} - \minor{32}{/dev/sdai}{35th SCSI disk whole disk} - \minordots - \minor{240}{/dev/sdav}{48th SCSI disk whole disk} -\end{devicelist} - -\noindent -Partitions are handled in the same way as for IDE disks (see major -number 3) except that the partition limit is 15 rather than 63 per -disk. - -\begin{devicelist} -\major{67}{}{char }{Coda network filesystem} - \minor{0}{/dev/cfs0}{Coda cache manager} -\end{devicelist} - -\noindent -See {\url http://www.coda.cs.cmu.edu\/} for information about Coda. - -\begin{devicelist} -\major{ }{}{block}{SCSI disk devices (48-63)} - \minor{0}{/dev/sdaw}{49th SCSI disk whole disk} - \minor{16}{/dev/sdax}{50th SCSI disk whole disk} - \minor{32}{/dev/sday}{51st SCSI disk whole disk} - \minordots - \minor{240}{/dev/sdbl}{64th SCSI disk whole disk} -\end{devicelist} - -\noindent -Partitions are handled in the same way as for IDE disks (see major -number 3) except that the partition limit is 15 rather than 63 per -disk. - -\begin{devicelist} -\major{68}{}{char }{CAPI 2.0 interface} - \minor{0}{/dev/capi20}{Control device} - \minor{1}{/dev/capi20.00}{First CAPI 2.0 application} - \minor{2}{/dev/capi20.01}{Second CAPI 2.0 application} - \minordots - \minor{20}{/dev/capi20.19}{19th CAPI 2.0 application} -\end{devicelist} - -\noindent -ISDN CAPI 2.0 driver for use with CAPI 2.0 applications; currently -supports the AVM B1 card. - -\begin{devicelist} -\major{ }{}{block}{SCSI disk devices (64-79)} - \minor{0}{/dev/sdbm}{65th SCSI disk whole disk} - \minor{16}{/dev/sdbn}{66th SCSI disk whole disk} - \minor{32}{/dev/sdbo}{67th SCSI disk whole disk} - \minordots - \minor{240}{/dev/sdcb}{80th SCSI disk whole disk} -\end{devicelist} - -\noindent -Partitions are handled in the same way as for IDE disks (see major -number 3) except that the partition limit is 15 rather than 63 per -disk. - -\begin{devicelist} -\major{69}{}{char }{MA16 numeric accelerator card} - \minor{0}{/dev/ma16}{Board memory access} -\end{devicelist} - -\begin{devicelist} -\major{ }{}{block}{SCSI disk devices (80-95)} - \minor{0}{/dev/sdcc}{81st SCSI disk whole disk} - \minor{16}{/dev/sdcd}{82nd SCSI disk whole disk} - \minor{32}{/dev/sdce}{83th SCSI disk whole disk} - \minordots - \minor{240}{/dev/sdcr}{96th SCSI disk whole disk} -\end{devicelist} - -\noindent -Partitions are handled in the same way as for IDE disks (see major -number 3) except that the partition limit is 15 rather than 63 per -disk. - -\begin{devicelist} -\major{70}{}{char }{SpellCaster Protocol Services Interface} - \minor{0}{/dev/apscfg}{Configuration interface} - \minor{1}{/dev/apsauth}{Authentication interface} - \minor{2}{/dev/apslog}{Logging interface} - \minor{3}{/dev/apsdbg}{Debugging interface} - \minor{64}{/dev/apsisdn}{ISDN command interface} - \minor{65}{/dev/apsasync}{Async command interface} - \minor{128}{/dev/apsmon}{Monitor interface} -\end{devicelist} - -\begin{devicelist} -\major{ }{}{block}{SCSI disk devices (96-111)} - \minor{0}{/dev/sdcs}{97th SCSI disk whole disk} - \minor{16}{/dev/sdct}{98th SCSI disk whole disk} - \minor{32}{/dev/sdcu}{99th SCSI disk whole disk} - \minordots - \minor{240}{/dev/sddh}{112th SCSI disk whole disk} -\end{devicelist} - -\noindent -Partitions are handled in the same way as for IDE disks (see major -number 3) except that the partition limit is 15 rather than 63 per -disk. - -\begin{devicelist} -\major{71}{}{char }{Computone IntelliPort II serial card} - \minor{0}{/dev/ttyF0}{IntelliPort II board 0, port 0} - \minor{1}{/dev/ttyF1}{IntelliPort II board 0, port 1} - \minordots - \minor{63}{/dev/ttyF63}{IntelliPort II board 0, port 63} - \minor{64}{/dev/ttyF64}{IntelliPort II board 1, port 0} - \minor{65}{/dev/ttyF65}{IntelliPort II board 1, port 1} - \minordots - \minor{127}{/dev/ttyF127}{IntelliPort II board 1, port 63} - \minor{128}{/dev/ttyF128}{IntelliPort II board 2, port 0} - \minor{129}{/dev/ttyF129}{IntelliPort II board 2, port 1} - \minordots - \minor{191}{/dev/ttyF191}{IntelliPort II board 2, port 63} - \minor{192}{/dev/ttyF192}{IntelliPort II board 3, port 0} - \minor{193}{/dev/ttyF193}{IntelliPort II board 3, port 1} - \minordots - \minor{255}{/dev/ttyF255}{IntelliPort II board 3, port 63} -\end{devicelist} - -\begin{devicelist} -\major{ }{}{block}{SCSI disk devices (112-127)} - \minor{0}{/dev/sddi}{113th SCSI disk whole disk} - \minor{16}{/dev/sddj}{114th SCSI disk whole disk} - \minor{32}{/dev/sddk}{115th SCSI disk whole disk} - \minordots - \minor{240}{/dev/sddx}{128th SCSI disk whole disk} -\end{devicelist} - -\noindent -Partitions are handled in the same way as for IDE disks (see major -number 3) except that the partition limit is 15 rather than 63 per -disk. - -\begin{devicelist} -\major{72}{}{char }{Computone IntelliPort II serial card -- alternate devices} - \minor{0}{/dev/cuf0}{Callout device corresponding to {\file ttyF0}} - \minor{1}{/dev/cuf1}{Callout device corresponding to {\file ttyF1}} - \minordots - \minor{63}{/dev/cuf63}{Callout device corresponding to {\file ttyF63}} - \minor{64}{/dev/cuf64}{Callout device corresponding to {\file ttyF64}} - \minor{65}{/dev/cuf65}{Callout device corresponding to {\file ttyF65}} - \minordots - \minor{127}{/dev/cuf127}{Callout device corresponding to {\file ttyF127}} - \minor{128}{/dev/cuf128}{Callout device corresponding to {\file ttyF128}} - \minor{129}{/dev/cuf129}{Callout device corresponding to {\file ttyF129}} - \minordots - \minor{191}{/dev/cuf191}{Callout device corresponding to {\file ttyF191}} - \minor{192}{/dev/cuf192}{Callout device corresponding to {\file ttyF192}} - \minor{193}{/dev/cuf193}{Callout device corresponding to {\file ttyF193}} - \minordots - \minor{255}{/dev/cuf255}{Callout device corresponding to {\file ttyF255}} -\end{devicelist} - -\begin{devicelist} -\major{73}{}{char }{Computone IntelliPort II serial card -- control devices} - \minor{0}{/dev/ip2ipl0}{Loadware device for board 0} - \minor{1}{/dev/ip2stat0}{Status device for board 0} - \minor{4}{/dev/ip2ipl1}{Loadware device for board 1} - \minor{5}{/dev/ip2stat1}{Status device for board 1} - \minor{8}{/dev/ip2ipl2}{Loadware device for board 2} - \minor{9}{/dev/ip2stat2}{Status device for board 2} - \minor{12}{/dev/ip2ipl3}{Loadware device for board 3} - \minor{13}{/dev/ip2stat3}{Status device for board 3} -\end{devicelist} - -\begin{devicelist} -\major{74}{}{char }{SCI bridge} - \minor{0}{/dev/SCI/0}{SCI device 0} - \minor{1}{/dev/SCI/1}{SCI device 1} - \minordots -\end{devicelist} - -\noindent -Currently for Dolphin Interconnect Solutions' PCI-SCI bridge. - -\begin{devicelist} -\major{75}{}{char }{Specialix IO8+ serial card} - \minor{0}{/dev/ttyW0}{First IO8+ port, first card} - \minor{1}{/dev/ttyW1}{Second IO8+ port, first card} - \minordots - \minor{8}{/dev/ttyW8}{First IO8+ port, second card} - \minordots -\end{devicelist} - -\begin{devicelist} -\major{76}{}{char }{Specialix IO8+ serial card -- alternate devices} - \minor{0}{/dev/cuw0}{Callout device corresponding to {\file ttyW0}} - \minor{1}{/dev/cuw1}{Callout device corresponding to {\file ttyW1}} - \minordots - \minor{8}{/dev/cuw8}{Callout device corresponding to {\file ttyW8}} - \minordots -\end{devicelist} - -\begin{devicelist} -\major{77}{}{char }{ComScire Quantum Noise Generator} - \minor{0}{/dev/qng}{ComScire Quantum Noise Generator} -\end{devicelist} - -\begin{devicelist} -\major{78}{}{char }{PAM Software's multimodem boards} - \minor{0}{/dev/ttyM0}{First PAM modem} - \minor{1}{/dev/ttyM1}{Second PAM modem} - \minordots -\end{devicelist} - -\begin{devicelist} -\major{79}{}{char }{PAM Software's multimodem boards -- alternate devices} - \minor{0}{/dev/cum0}{Callout device corresponding to {\file ttyM0}} - \minor{1}{/dev/cum1}{Callout device corresponding to {\file ttyM1}} - \minordots -\end{devicelist} - -\begin{devicelist} -\major{80}{}{char }{Photometrics AT200 CCD camera} - \minor{0}{/dev/at200}{Photometrics AT200 CCD camera} -\end{devicelist} - -\begin{devicelist} -\major{81}{}{char }{video4linux} - \minor{0}{/dev/video0}{Video capture/overlay device} - \minordots - \minor{63}{/dev/video63}{Video capture/overlay device} - \minor{64}{/dev/radio0}{Radio device} - \minordots - \minor{127}{/dev/radio63}{Radio device} - \minor{192}{/dev/vtx0}{Teletext device} - \minordots - \minor{223}{/dev/vtx31}{Teletext device} - \minor{224}{/dev/vbi0}{Vertical blank interupt} - \minordots - \minor{255}{/dev/vbi31}{Vertical blank interupt} -\end{devicelist} - -\begin{devicelist} -\major{82}{}{char }{WiNRADiO communications receiver card} - \minor{0}{/dev/winradio0}{First WiNRADiO card} - \minor{1}{/dev/winradio1}{Second WiNRADiO card} - \minordots -\end{devicelist} - -\noindent -The driver and documentation may be obtained from -{\url http://www.proximity.com.au/~brian/winradio/\/}. - -\begin{devicelist} -\major{83}{}{char }{Teletext/videotext interfaces} - \minor{0}{/dev/vtx}{Teletext decoder} - \minor{16}{/dev/vttuner}{TV tuner on teletext interface} -\end{devicelist} - -\noindent -Devices for the driver contained in the VideoteXt package. More information -on {\url http://home.pages.de/~videotext/\/}. - -\begin{devicelist} -\major{84}{}{char }{Ikon 1011[57] Versatec Greensheet Interface} - \minor{0}{/dev/ihcp0}{First Greensheet port} - \minor{1}{/dev/ihcp1}{Second Greensheet port} -\end{devicelist} - -\begin{devicelist} -\major{85}{}{char }{Linux/SGI shared memory input queue} - \minor{0}{/dev/shmiq}{Master shared input queue} - \minor{1}{/dev/qcntl0}{First device pushed} - \minor{2}{/dev/qcntl1}{Second device pushed} - \minordots -\end{devicelist} - -\begin{devicelist} -\major{86}{}{char }{SCSI media changer} - \minor{0}{/dev/sch0}{First SCSI media changer} - \minor{1}{/dev/sch1}{Second SCSI media changer} - \minordots -\end{devicelist} - -\begin{devicelist} -\major{87}{}{char }{Sony Control-A1 stereo control bus} - \minor{0}{/dev/controla0}{First device on chain} - \minor{1}{/dev/controla1}{Second device on chain} - \minordots -\end{devicelist} - -\begin{devicelist} -\major{88}{}{char }{COMX synchronous serial card} - \minor{0}{/dev/comx0}{Channel 0} - \minor{1}{/dev/comx1}{Channel 1} - \minordots -\end{devicelist} - -\begin{devicelist} -\major{89}{}{char }{I$^2$C bus interface} - \minor{0}{/dev/i2c0}{First I$^2$C adapter} - \minor{1}{/dev/i2c1}{Second I$^2$C adapter} - \minordots -\end{devicelist} - -\begin{devicelist} -\major{90}{}{char }{Memory Technology Device (RAM, ROM, Flash)} - \minor{0}{/dev/mtd0}{First MTD (rw)} - \minor{1}{/dev/mtdr0}{First MTD (ro)} - \minordots - \minor{30}{/dev/mtd15}{16th MTD (rw)} - \minor{31}{/dev/mtdr15}{16th MTD (ro)} -\end{devicelist} - -\begin{devicelist} -\major{91}{}{char }{CAN-Bus controller} - \minor{0}{/dev/can0}{First CAN-Bus controller} - \minor{1}{/dev/can1}{Second CAN-Bus controller} - \minordots -\end{devicelist} - -\begin{devicelist} -\major{92}{}{char }{Reserved for ith Kommunikationstechnik MIC ISDN card} -\end{devicelist} - -\begin{devicelist} -\major{93}{}{char }{IBM Smart Capture Card frame grabber} - \minor{0}{/dev/iscc0}{First Smart Capture Card} - \minor{1}{/dev/iscc1}{Second Smart Capture Card} - \minordots - \minor{128}{/dev/isccctl0}{First Smart Capture Card control} - \minor{129}{/dev/isccctl1}{Second Smart Capture Card control} - \minordots -\end{devicelist} - -\begin{devicelist} -\major{94}{}{char }{miroVIDEO DC10/30 capture/playback device} - \minor{0}{/dev/dcxx0}{First capture card} - \minor{1}{/dev/dcxx1}{Second capture card} - \minordots -\end{devicelist} - -\begin{devicelist} -\major{95}{}{char }{IP filter} - \minor{0}{/dev/ipl}{Filter control device/log file} - \minor{1}{/dev/ipnat}{NAT control device/log file} - \minor{2}{/dev/ipstate}{State information log file} - \minor{3}{/dev/ipauth}{Authentication control device/log file} -\end{devicelist} - -\begin{devicelist} -\major{96}{}{char }{Parallel port ATAPI tape devices} - \minor{0}{/dev/pt0}{First parallel port ATAPI tape} - \minor{1}{/dev/pt1}{Second parallel port ATAPI tape} - \minor{2}{/dev/pt2}{Third parallel port ATAPI tape} - \minor{3}{/dev/pt3}{Fourth parallel port ATAPI tape} - \minor{128}{/dev/npt0}{First parallel port ATAPI tape, no rewind} - \minor{129}{/dev/npt1}{Second parallel port ATAPI tape, no rewind} - \minor{130}{/dev/npt2}{Third parallel port ATAPI tape, no rewind} - \minor{131}{/dev/npt3}{Fourth parallel port ATAPI tape, no rewind} -\end{devicelist} - -\begin{devicelist} -\major{97}{}{char }{Parallel port generic ATAPI interface} - \minor{0}{/dev/pg0}{First parallel port ATAPI device} - \minor{1}{/dev/pg1}{Second parallel port ATAPI device} - \minor{2}{/dev/pg2}{Third parallel port ATAPI device} - \minor{3}{/dev/pg3}{Fourth parallel port ATAPI device} -\end{devicelist} - -\noindent -These devices support the same API as the generic SCSI devices. - -\begin{devicelist} -\major{98}{}{char }{Control and Mesurement Device (comedi)} - \minor{0}{/dev/comedi0}{First comedi device} - \minor{1}{/dev/comedi1}{Second comedi device} - \minordots -\end{devicelist} - -\noindent -See {\url http://stm.lbl.gov/comedi/} or {\url -http://www.llp.fu-berlin.de/}. - -\begin{devicelist} -\major{99}{}{char }{Raw parallel ports} - \minor{0}{/dev/parport0}{First parallel port} - \minor{1}{/dev/parport1}{Second parallel port} - \minordots -\end{devicelist} - -\noindent -These devices can be used to drive parallel port devices from -user-space while interacting with the parport sharing code. - -\begin{devicelist} -\major{100}{}{char }{POTS (analogue telephone) A/B port} - \minor{0}{/dev/phone0}{First telephone port} - \minor{1}{/dev/phone1}{Second telephone port} - \minordots -\end{devicelist} - -\begin{devicelist} -\major{101}{}{char }{Motorola DSP 56xxx board} - \minor{0}{/dev/mdspstat}{Status information} - \minor{1}{/dev/mdsp1}{First DSP board I/O and controls} - \minordots - \minor{16}{/dev/mdsp16}{16th DSP board I/O and controls} -\end{devicelist} - -\begin{devicelist} -\major{102}{}{char }{Philips SAA5249 Teletext signal decoder} - \minor{0}{/dev/tlk0}{First Teletext decoder} - \minor{1}{/dev/tlk1}{Second Teletext decoder} - \minor{2}{/dev/tlk2}{Third Teletext decoder} - \minor{3}{/dev/tlk3}{Fourth Teletext decoder} -\end{devicelist} - -\begin{devicelist} -\major{103}{}{char }{Arla network file system} - \minor{0}{/dev/xfs0}{Arla XFS} -\end{devicelist} - -\noindent -Arla is a free clone of the Andrew File System, AFS. Any resemblance -with the Swedish milk producer is coincidental. For more information -about the project, write to $<$arla-drinkers@stacken.kth.se$>$ or -subscribe to the arla-announce mailing list by sending a mail to -$<$arla-announce-request@stacken.kth.se$>$. - -\begin{devicelist} -\major{104}{}{char }{Flash BIOS support} -\end{devicelist} - -\begin{devicelist} -\major{105}{}{char }{Comtrol VS-1000 serial card} - \minor{0}{/dev/ttyV0}{First VS-1000 port} - \minor{1}{/dev/ttyV1}{Second VS-1000 port} - \minordots -\end{devicelist} - -\begin{devicelist} -\major{106}{}{char }{Comtrol VS-1000 serial card -- alternate devices} - \minor{0}{/dev/cuv0}{Callout device corresponding to {\file ttyV0}} - \minor{1}{/dev/cuv1}{Callout device corresponding to {\file ttyV1}} - \minordots -\end{devicelist} - -\begin{devicelist} -\major{107}{}{char }{3Dfx Voodoo Graphics device} - \minor{0}{/dev/3dfx}{Primary 3Dfx graphics device} -\end{devicelist} - -\begin{devicelist} -\major{108}{}{char }{Device independent PPP interface} - \minor{0}{/dev/ppp}{Device independent PPP interface} -\end{devicelist} - -\begin{devicelist} -\major{109}{}{char }{Reserved for logical volume manager} -\end{devicelist} - -\begin{devicelist} -\major{110}{}{char }{miroMEDIA Surround board} - \minor{0}{/dev/srnd0}{First miroMEDIA Surround board} - \minor{1}{/dev/srnd1}{First miroMEDIA Surround board} - \minordots -\end{devicelist} - -\begin{devicelist} -\major{111}{--119}{}{Unallocated} -\end{devicelist} - -\begin{devicelist} -\major{120}{--127}{}{Local/experimental use} -\end{devicelist} - -\begin{devicelist} -\major{128}{--135}{char }{Unix98 PTY masters} -\end{devicelist} - -\noindent -These devices should not have corresponding device nodes; instead they -should be accessed through the {\file /dev/ptmx} cloning device. - -\begin{devicelist} -\major{136}{--143}{char }{Unix98 PTY slaves} - \minor{0}{/dev/pts/0}{First Unix98 pseudo-TTY} - \minor{1}{/dev/pts/1}{Second Unix98 pseudo-TTY} - \minordots -\end{devicelist} - -\noindent -These device nodes are automatically generated with the proper -permissions and modes by mounting the {\file devpts} filesystem onto -{\file /dev/pts} with the appropriate mount options (distribution -dependent.) - -\begin{devicelist} -\major{144}{--239}{}{Unallocated} -\end{devicelist} - -\begin{devicelist} -\major{240}{--254}{}{Local/experimental use} -\end{devicelist} - -\begin{devicelist} -\major{255}{}{}{Reserved} -\end{devicelist} - -\noindent -This major is reserved to assist the expansion to a larger number -space. No device nodes with this major should ever be created on any -filesystem. - -\section{Additional /dev directory entries} - -This section details additional entries that should or may exist in the -{\file /dev} directory. It is preferred that symbolic links use the -same form (absolute or relative) as is indicated here. Links are -classified as {\em hard\/} or {\em symbolic\/} depending on the -preferred type of link; if possible, the indicated type of link should -be used. - -\subsection{Compulsory links} - -These links should exist on all systems: - -\begin{nodelist} -\link{/dev/fd}{/proc/self/fd}{symbolic}{File descriptors} -\link{/dev/stdin}{fd/0}{symbolic}{Standard input file descriptor} -\link{/dev/stdout}{fd/1}{symbolic}{Standard output file descriptor} -\link{/dev/stderr}{fd/2}{symbolic}{Standard error file descriptor} -\link{/dev/nfsd}{socksys}{symbolic}{Required by iBCS-2} -\link{/dev/X0R}{null}{symbolic}{Required by iBCS-2} -\end{nodelist} - -\noindent -Note: The last device is: $<$letter {\tt X}$>$-$<$digit {\tt -0}$>$-$<$letter {\tt R}$>$. - -\subsection{Recommended links} - -It is recommended that these links exist on all systems: - -\begin{nodelist} -\link{/dev/core}{/proc/kcore}{symbolic}{Backward compatibility} -\link{/dev/ramdisk}{ram0}{symbolic}{Backward compatibility} -\link{/dev/ftape}{qft0}{symbolic}{Backward compatibility} -\link{/dev/bttv0}{video0}{symbolic}{Backward compatibility} -\link{/dev/radio}{radio0}{symbolic}{Backward compatibility} -\link{/dev/scd?}{sr?}{hard}{Alternate name for CD-ROMs} -\end{nodelist} - -\subsection{Locally defined links} - -The following links may be established locally to conform to the -configuration of the system. This is merely a tabulation of existing -practice, and does not constitute a recommendation. However, if they -exist, they should have the following uses. - -\begin{nodelist} -\vlink{/dev/mouse}{mouse port}{symbolic}{Current mouse device} -\vlink{/dev/tape}{tape device}{symbolic}{Current tape device} -\vlink{/dev/cdrom}{CD-ROM device}{symbolic}{Current CD-ROM device} -\vlink{/dev/cdwriter}{CD-writer}{symbolic}{Current CD-writer device} -\vlink{/dev/scanner}{scanner device}{symbolic}{Current scanner device} -\vlink{/dev/modem}{modem port}{symbolic}{Current dialout device} -\vlink{/dev/root}{root device}{symbolic}{Current root filesystem} -\vlink{/dev/swap}{swap device}{symbolic}{Current swap device} -\end{nodelist} - -\noindent -{\file /dev/modem} should not be used for a modem which supports -dialin as well as dialout, as it tends to cause lock file problems. -If it exists, {\file /dev/modem} should point to the appropriate -primary TTY device (the use of the alternate callout devices is -deprecated.) - -For SCSI devices, {\file /dev/tape} and {\file /dev/cdrom} should -point to the ``cooked'' devices ({\file /dev/st*} and {\file -/dev/sr*}, respectively), whereas {\file /dev/cdwriter} and {\file -/dev/scanner} should point to the appropriate generic SCSI devices -({\file /dev/sg*}). - -{\file /dev/mouse} may point to a primary serial TTY device, a -hardware mouse device, or a socket for a mouse driver program -(e.g. {\file /dev/gpmdata}). - -\subsection{Sockets and pipes} - -Non-transient sockets or named pipes may exist in {\file /dev}. -Common entries are: - -\begin{nodelist} -\node{/dev/printer}{socket}{{\file lpd} local socket} -\node{/dev/log}{socket}{{\file syslog} local socket} -\node{/dev/gpmdata}{socket}{{\file gpm} mouse multiplexer} -\end{nodelist} - -\section{Terminal devices} - -Terminal, or TTY devices are a special class of character devices. A -terminal device is any device that could act as a controlling terminal -for a session; this includes virtual consoles, serial ports, and -pseudoterminals (PTYs). - -All terminal devices share a common set of capabilities known as line -diciplines; these include the common terminal line dicipline as well -as SLIP and PPP modes. - -All terminal devices are named similarly; this section explains the -naming and use of the various types of TTYs. Note that the naming -conventions include several historical warts; some of these are -Linux-specific, some were inherited from other systems, and some -reflect Linux outgrowing a borrowed convention. - -A hash mark ($\#$) in a device name is in all cases used here to -indicate a decimal number without leading zeroes. - -\subsection{Virtual consoles and the console device} - -Virtual consoles are full-screen terminal displays on the system video -monitor. Virtual consoles are named {\file /dev/tty$\#$}, with -numbering starting at {\file /dev/tty1}; {\file /dev/tty0} is the -current virtual console. {\file /dev/tty0} is the device that should -be used to access the system video card on those architectures for -which the frame buffer devices ({\file /dev/fb*}) are not applicable. -Do not use {\file /dev/console} for this purpose. - -The {\em console device\/}, {\file /dev/console}, is the device to -which system messages should be sent, and on which logins should be -permitted in single-user mode. Starting with Linux 2.1.71, {\file -/dev/console} is managed by the kernel; for previous versions it -should be a symbolic link to either {\file /dev/tty0}, a specific -virtual console such as {\file /dev/tty1}, or to a serial port primary -({\file tty*}, not {\file cu*}) device, depending on the configuration -of the system. - -\subsection{Serial ports} - -Serial ports are RS-232 serial ports and any device which simulates -one, either in hardware (such as internal modems) or in software (such -as the ISDN driver.) Under Linux, each serial ports has two device -names, the primary or callin device and the alternate or callout one. -Each kind of device is indicated by a different letter. For any -letter $X$, the names of the devices are {\file /dev/tty${X\#}$} and -{\file /dev/cu${x\#}$}, respectively; for historical reasons, {\file -/dev/ttyS$\#$} and {\file /dev/ttyC$\#$} correspond to {\file -/dev/cua$\#$} and {\file /dev/cub$\#$}. In the future, it should be -expected that multiple letters will be used; all letters will be upper -case for the {\file tty} device (e.g. {\file /dev/ttyDP$\#$} and lower -case for the {\file cu} device (e.g. {\file /dev/cudp$\#$}. - -The use of the callout devices is deprecated. - -The names {\file /dev/ttyQ$\#$} and {\file /dev/cuq$\#$} are reserved -for local use. - -The alternate devices provide for kernel-based exclusion and somewhat -different defaults than the primary devices. Their main purpose is to -allow the use of serial ports with programs with no inherent or broken -support for serial ports. Their use is deprecated, and they may be -removed from a future version of Linux. - -Arbitration of serial ports is provided by the use of lock files with -the names {\file /var/lock/LCK..tty${X\#}$}. The contents of the lock -file should be the PID of the locking process as an ASCII number. - -It is common practice to install links such as {\file /dev/modem\/} -which point to serial ports. In order to ensure proper locking in the -presence of these links, it is recommended that software chase -symlinks and lock all possible names; additionally, it is recommended -that a lock file be installed with the corresponding alternate -device. In order to avoid deadlocks, it is recommended that the locks -are acquired in the following order, and released in the reverse: -\begin{itemize} -\item{The symbolic link name, if any ({\file /var/lock/LCK..modem})} -\item{The {\file tty} name ({\file /var/lock/LCK..ttyS2})} -\item{The alternate device name ({\file /var/lock/LCK..cua2})} -\end{itemize} -In the case of nested symbolic links, the lock files should be -installed in the order the symlinks are resolved. - -Under no circumstances should an application hold a lock while waiting -for another to be released. In addition, applications which attempt -to create lock files for the corresponding alternate device names -should take into account the possibility of being used on a non-serial -port TTY, for which no alternate device would exist. - -\subsection{Pseudoterminals (PTYs)} - -Pseudoterminals, or PTYs, are used to create login sessions or provide -other capabilities requiring a TTY line dicipline (including SLIP or -PPP capability) to arbitrary data-generation processes. Each PTY has -a {\em master\/} side, named {\file /dev/pty[p-za-e][0-9a-f]\/}, and a -{\em slave\/} side, named {\file /dev/tty[p-za-e][0-9a-f]\/}. The -kernel arbitrates the use of PTYs by allowing each master side to be -opened only once. - -Once the master side has been opened, the corresponding slave device -can be used in the same manner as any TTY device. The master and -slave devices are connected by the kernel, generating the equivalent -of a bidirectional pipe with TTY capabilities. - -Recent versions of the Linux kernels and GNU libc contain support for -the System V/Unix98 naming scheme for PTYs, which assigns a common -device {\file /dev/ptmx\/} to all the masters (opening it will -automatically give you a previously unassigned PTY) and a subdirectory -{\file /dev/pts\/} for the slaves; the slaves are named with decimal -integers ({\file /dev/pts/$\#$\/} in our notation). This removes the -problem of exhausting the namespace and enables the kernel to -automatically create the device nodes for the slaves on demand using -the {\file devpts\/} filesystem. - -\end{document} diff -u --recursive --new-file v2.2.13/linux/Documentation/devices.txt linux/Documentation/devices.txt --- v2.2.13/linux/Documentation/devices.txt Wed Dec 16 12:52:00 1998 +++ linux/Documentation/devices.txt Tue Jan 4 10:12:10 2000 @@ -1,21 +1,20 @@ LINUX ALLOCATED DEVICES Maintained by H. Peter Anvin - Last revised: August 10, 1998 + Last revised: December 21, 1999 This list is the Linux Device List, the official registry of allocated device numbers and /dev directory nodes for the Linux operating system. The latest version of this list is included with the Linux kernel -sources in LaTeX and ASCII form. It is also available separately from -ftp://ftp.kernel.org/pub/linux/docs/device-list/. In case of -discrepancy between the text and LaTeX versions, the LaTeX version is -authoritative. - -This document is included by reference into the Linux Filesystem -Standard (FSSTND). The FSSTND is available from -ftp://tsx-11.mit.edu/pub/linux/docs/linux-standards/fsstnd/. +sources. It is also available separately from +http://www.kernel.org/pub/linux/docs/device-list/ or +ftp://ftp.kernel.org/pub/linux/docs/device-list/. The LaTeX version +of this document is no longer maintained. + +This document is included by reference into the Filesystem Hierarchy +Standard (FHS). The FHS is available from http://www.pathname.com/fhs/. Allocations marked (68k/Amiga) apply to Linux/68k on the Amiga platform only. Allocations marked (68k/Atari) apply to Linux/68k on @@ -33,6 +32,7 @@ on this list. Any such information requests will be deleted without reply. + **** PLEASE READ THIS BEFORE SUBMITTING A DEVICE ENTRY **** To have a major number allocated, or a minor number in situations @@ -53,6 +53,13 @@ found to ensure I have all the requisite information to publish your device and avoid conflicts. +Finally, sometimes I have to play "namespace police." Please don't be +offended. I often get submissions for /dev names that would be bound +to cause conflicts down the road. I am trying to avoid getting in a +situation where we would have to suffer an incompatible forward +change. + + Your cooperation is appreciated. @@ -98,14 +105,14 @@ demand. block Floppy disks - 0 = /dev/fd0 Controller 1, drive 1 autodetect - 1 = /dev/fd1 Controller 1, drive 2 autodetect - 2 = /dev/fd2 Controller 1, drive 3 autodetect - 3 = /dev/fd3 Controller 1, drive 4 autodetect - 128 = /dev/fd4 Controller 2, drive 1 autodetect - 129 = /dev/fd5 Controller 2, drive 2 autodetect - 130 = /dev/fd6 Controller 2, drive 3 autodetect - 131 = /dev/fd7 Controller 2, drive 4 autodetect + 0 = /dev/fd0 Controller 0, drive 0, autodetect + 1 = /dev/fd1 Controller 0, drive 1, autodetect + 2 = /dev/fd2 Controller 0, drive 2, autodetect + 3 = /dev/fd3 Controller 0, drive 3, autodetect + 128 = /dev/fd4 Controller 1, drive 0, autodetect + 129 = /dev/fd5 Controller 1, drive 1, autodetect + 130 = /dev/fd6 Controller 1, drive 2, autodetect + 131 = /dev/fd7 Controller 1, drive 3, autodetect To specify format, add to the autodetect device number: 0 = /dev/fd? Autodetect format @@ -183,51 +190,45 @@ 0 = /dev/tty0 Current virtual console 1 = /dev/tty1 First virtual console - ... + ... 63 = /dev/tty63 63rd virtual console - 64 = /dev/ttyS0 First serial port - ... - 127 = /dev/ttyS63 64th serial port - 128 = /dev/ptyp0 OBSOLETE - ... - 191 = /dev/ptysf OBSOLETE - 192 = /dev/ttyp0 OBSOLETE - ... - 255 = /dev/ttysf OBSOLETE + 64 = /dev/ttyS0 First UART serial port + ... + 255 = /dev/ttyS191 192nd UART serial port Older versions of the Linux kernel used this major number for BSD PTY devices. As of Linux 2.1.115, this - is no longer supported. Use major numbers 2 and 3. + is no longer supported. Use major numbers 2 and 3. 5 char Alternate TTY devices 0 = /dev/tty Current TTY device 1 = /dev/console System console 2 = /dev/ptmx PTY master multiplex - 64 = /dev/cua0 Callout device corresponding to ttyS0 - ... - 127 = /dev/cua63 Callout device corresponding to ttyS63 + 64 = /dev/cua0 Callout device for ttyS0 + ... + 255 = /dev/cua191 Callout device for ttyS191 (5,1) is /dev/console starting with Linux 2.1.71. See the section on terminal devices for more information on /dev/console. 6 char Parallel printer devices - 0 = /dev/lp0 First parallel printer (0x3bc) - 1 = /dev/lp1 Second parallel printer (0x378) - 2 = /dev/lp2 Third parallel printer (0x278) - - Not all computers have the 0x3bc parallel port; hence - the "first" printer may be either /dev/lp0 or - /dev/lp1. + 0 = /dev/lp0 Parallel printer on parport0 + 1 = /dev/lp1 Parallel printer on parport1 + ... + + Current Linux kernels no longer have a fixed mapping + between parallel ports and I/O addresses. Instead, + they are redirected through the parport multiplex layer. 7 char Virtual console capture devices 0 = /dev/vcs Current vc text contents 1 = /dev/vcs1 tty1 text contents - ... + ... 63 = /dev/vcs63 tty63 text contents 128 = /dev/vcsa Current vc text/attribute contents 129 = /dev/vcsa1 tty1 text/attribute contents - ... + ... 191 = /dev/vcsa63 tty63 text/attribute contents NOTE: These devices permit both read and write access. @@ -235,7 +236,7 @@ block Loopback devices 0 = /dev/loop0 First loopback device 1 = /dev/loop1 Second loopback device - ... + ... The loopback devices are used to mount filesystems not associated with block devices. The binding to the @@ -245,7 +246,7 @@ 0 = /dev/sda First SCSI disk whole disk 16 = /dev/sdb Second SCSI disk whole disk 32 = /dev/sdc Third SCSI disk whole disk - ... + ... 240 = /dev/sdp Sixteenth SCSI disk whole disk Partitions are handled in the same way as for IDE @@ -255,28 +256,28 @@ 9 char SCSI tape devices 0 = /dev/st0 First SCSI tape, mode 0 1 = /dev/st1 Second SCSI tape, mode 0 - ... + ... 32 = /dev/st0l First SCSI tape, mode 1 33 = /dev/st1l Second SCSI tape, mode 1 - ... + ... 64 = /dev/st0m First SCSI tape, mode 2 65 = /dev/st1m Second SCSI tape, mode 2 - ... + ... 96 = /dev/st0a First SCSI tape, mode 3 97 = /dev/st1a Second SCSI tape, mode 3 ... 128 = /dev/nst0 First SCSI tape, mode 0, no rewind 129 = /dev/nst1 Second SCSI tape, mode 0, no rewind - ... + ... 160 = /dev/nst0l First SCSI tape, mode 1, no rewind 161 = /dev/nst1l Second SCSI tape, mode 1, no rewind - ... + ... 192 = /dev/nst0m First SCSI tape, mode 2, no rewind 193 = /dev/nst1m Second SCSI tape, mode 2, no rewind - ... + ... 224 = /dev/nst0a First SCSI tape, mode 3, no rewind 225 = /dev/nst1a Second SCSI tape, mode 3, no rewind - ... + ... "No rewind" refers to the omission of the default automatic rewind on device close. The MTREW or MTOFFL @@ -286,7 +287,7 @@ block Metadisk (RAID) devices 0 = /dev/md0 First metadisk group 1 = /dev/md1 Second metadisk group - ... + ... The metadisk driver is used to span a filesystem across multiple physical disks. @@ -303,6 +304,8 @@ 7 = /dev/amigamouse1 Second Amiga mouse 8 = /dev/smouse Simple serial mouse driver 9 = /dev/pc110pad IBM PC-110 digitizer pad + 10 = /dev/adbmouse Apple Desktop Bus mouse + 11 = /dev/vrtpanel Vr41xx embedded touch panel 128 = /dev/beep Fancy beep device 129 = /dev/modreq Kernel module load request 130 = /dev/watchdog Watchdog timer port @@ -326,6 +329,33 @@ 151 = /dev/led Front panel LEDs 153 = /dev/mergemem Memory merge device 154 = /dev/pmu Macintosh PowerBook power manager + 155 = /dev/isictl MultiTech ISICom serial control + 156 = /dev/lcd Front panel LCD display + 157 = /dev/ac Applicom Intl Profibus card + 158 = /dev/nwbutton Netwinder external button + 159 = /dev/nwdebug Netwinder debug interface + 160 = /dev/nwflash Netwinder flash memory + 161 = /dev/userdma User-space DMA access + 162 = /dev/smbus System Management Bus + 163 = /dev/lik Logitech Internet Keyboard + 164 = /dev/ipmo Intel Intelligent Platform Management + 165 = /dev/vmmon VMWare virtual machine monitor + 166 = /dev/i2o/ctl I2O configuration manager + 167 = /dev/specialix_sxctl Specialix serial control + 168 = /dev/tcldrv Technology Concepts serial control + 169 = /dev/specialix_rioctl Specialix RIO serial control + 170 = /dev/smapi IBM Thinkpad SMAPI + 171 = /dev/srripc QNX4 API IPC manager + 172 = /dev/usemaclone Semaphore clone device + 173 = /dev/ipmikcs Intelligent Platform Management + 174 = /dev/uctrl SPARCbook 3 microcontroller + 175 = /dev/agpgart AGP Graphics Address Remapping Table + 176 = /dev/gtrsc Gorgy Timing radio clock + 177 = /dev/cbm Serial CBM bus + 178 = /dev/jsflash JavaStation OS flash SIMM + 179 = /dev/xsvc High-speed shared-mem/semaphore service + 180 = /dev/vrbuttons Vr41xx button input device + 240-255 Reserved for local use 11 char Raw keyboard device 0 = /dev/kbd Raw keyboard device @@ -335,7 +365,7 @@ block SCSI CD-ROM devices 0 = /dev/sr0 First SCSI CD-ROM 1 = /dev/sr1 Second SCSI CD-ROM - ... + ... The prefix /dev/scd instead of /dev/sr has been used as well, and might make more sense. @@ -356,13 +386,17 @@ block MSCDEX CD-ROM callback support 0 = /dev/dos_cd0 First MSCDEX CD-ROM 1 = /dev/dos_cd1 Second MSCDEX CD-ROM - ... + ... - 13 char PC speaker + 13 char PC speaker (OBSOLETE) 0 = /dev/pcmixer Emulates /dev/mixer 1 = /dev/pcsp Emulates /dev/dsp (8-bit) 4 = /dev/pcaudio Emulates /dev/audio 5 = /dev/pcsp16 Emulates /dev/dsp (16-bit) + + The current PC speaker driver uses the Open Sound + System interface, and these devices are obsolete. + block 8-bit MFM/RLL/IDE controller 0 = /dev/xda First XT disk whole disk 64 = /dev/xdb Second XT disk whole disk @@ -370,13 +404,14 @@ Partitions are handled in the same way as IDE disks (see major number 3). - 14 char Sound card + 14 char Open Sound System (OSS) 0 = /dev/mixer Mixer control 1 = /dev/sequencer Audio sequencer 2 = /dev/midi00 First MIDI port 3 = /dev/dsp Digital audio 4 = /dev/audio Sun-compatible digital audio 6 = /dev/sndstat Sound card status information + 7 = /dev/audioctl SPARC audio control device 8 = /dev/sequencer2 Sequencer -- alternate device 16 = /dev/mixer1 Second soundcard mixer control 17 = /dev/patmgr0 Sequencer patch manager @@ -413,43 +448,43 @@ 17 char Chase serial card 0 = /dev/ttyH0 First Chase port 1 = /dev/ttyH1 Second Chase port - ... + ... block Optics Storage CD-ROM 0 = /dev/optcd Optics Storage CD-ROM 18 char Chase serial card - alternate devices - 0 = /dev/cuh0 Callout device corresponding to ttyH0 - 1 = /dev/cuh1 Callout device corresponding to ttyH1 - ... + 0 = /dev/cuh0 Callout device for ttyH0 + 1 = /dev/cuh1 Callout device for ttyH1 + ... block Sanyo CD-ROM 0 = /dev/sjcd Sanyo CD-ROM 19 char Cyclades serial card 0 = /dev/ttyC0 First Cyclades port - ... + ... 31 = /dev/ttyC31 32nd Cyclades port block "Double" compressed disk 0 = /dev/double0 First compressed disk - ... + ... 7 = /dev/double7 Eighth compressed disk 128 = /dev/cdouble0 Mirror of first compressed disk - ... + ... 135 = /dev/cdouble7 Mirror of eighth compressed disk See the Double documentation for the meaning of the mirror devices. 20 char Cyclades serial card - alternate devices - 0 = /dev/cub0 Callout device corresponding to ttyC0 - ... - 31 = /dev/cub31 Callout device corresponding to ttyC31 + 0 = /dev/cub0 Callout device for ttyC0 + ... + 31 = /dev/cub31 Callout device for ttyC31 block Hitachi CD-ROM (under development) 0 = /dev/hitcd Hitachi CD-ROM 21 char Generic SCSI access 0 = /dev/sg0 First generic SCSI device 1 = /dev/sg1 Second generic SCSI device - ... + ... Most distributions name these /dev/sga, /dev/sgb...; this sets an unnecessary limit of 26 SCSI devices in @@ -467,7 +502,7 @@ 22 char Digiboard serial card 0 = /dev/ttyD0 First Digiboard port 1 = /dev/ttyD1 Second Digiboard port - ... + ... block Second IDE hard disk/CD-ROM interface 0 = /dev/hdc Master: whole disk (or CD-ROM) 64 = /dev/hdd Slave: whole disk (or CD-ROM) @@ -476,8 +511,8 @@ interface (see major number 3). 23 char Digiboard serial card - alternate devices - 0 = /dev/cud0 Callout device corresponding to ttyD0 - 1 = /dev/cud1 Callout device corresponding to ttyD1 + 0 = /dev/cud0 Callout device for ttyD0 + 1 = /dev/cud1 Callout device for ttyD1 ... block Mitsumi proprietary CD-ROM 0 = /dev/mcd Mitsumi CD-ROM @@ -485,31 +520,31 @@ 24 char Stallion serial card 0 = /dev/ttyE0 Stallion port 0 card 0 1 = /dev/ttyE1 Stallion port 1 card 0 - ... + ... 64 = /dev/ttyE64 Stallion port 0 card 1 65 = /dev/ttyE65 Stallion port 1 card 1 ... 128 = /dev/ttyE128 Stallion port 0 card 2 129 = /dev/ttyE129 Stallion port 1 card 2 - ... + ... 192 = /dev/ttyE192 Stallion port 0 card 3 193 = /dev/ttyE193 Stallion port 1 card 3 - ... + ... block Sony CDU-535 CD-ROM 0 = /dev/cdu535 Sony CDU-535 CD-ROM 25 char Stallion serial card - alternate devices - 0 = /dev/cue0 Callout device corresponding to ttyE0 - 1 = /dev/cue1 Callout device corresponding to ttyE1 - ... - 64 = /dev/cue64 Callout device corresponding to ttyE64 - 65 = /dev/cue65 Callout device corresponding to ttyE65 - ... - 128 = /dev/cue128 Callout device corresponding to ttyE128 - 129 = /dev/cue129 Callout device corresponding to ttyE129 - ... - 192 = /dev/cue192 Callout device corresponding to ttyE192 - 193 = /dev/cue193 Callout device corresponding to ttyE193 + 0 = /dev/cue0 Callout device for ttyE0 + 1 = /dev/cue1 Callout device for ttyE1 + ... + 64 = /dev/cue64 Callout device for ttyE64 + 65 = /dev/cue65 Callout device for ttyE65 + ... + 128 = /dev/cue128 Callout device for ttyE128 + 129 = /dev/cue129 Callout device for ttyE129 + ... + 192 = /dev/cue192 Callout device for ttyE192 + 193 = /dev/cue193 Callout device for ttyE193 ... block First Matsushita (Panasonic/SoundBlaster) CD-ROM 0 = /dev/sbpcd0 Panasonic CD-ROM controller 0 unit 0 @@ -537,7 +572,7 @@ 16 = /dev/zqft0 Unit 0, rewind-on-close, compression 17 = /dev/zqft1 Unit 1, rewind-on-close, compression 18 = /dev/zqft2 Unit 2, rewind-on-close, compression - 19 = /dev/zqt3 Unit 3, rewind-on-close, compression + 19 = /dev/zqft3 Unit 3, rewind-on-close, compression 20 = /dev/nzqft0 Unit 0, no rewind-on-close, compression 21 = /dev/nzqft1 Unit 1, no rewind-on-close, compression 22 = /dev/nzqft2 Unit 2, no rewind-on-close, compression @@ -546,10 +581,10 @@ 33 = /dev/rawqft1 Unit 1, rewind-on-close, no file marks 34 = /dev/rawqft2 Unit 2, rewind-on-close, no file marks 35 = /dev/rawqft3 Unit 3, rewind-on-close, no file marks - 32 = /dev/nrawqft0 Unit 0, no rewind-on-close, no file marks - 33 = /dev/nrawqft1 Unit 1, no rewind-on-close, no file marks - 34 = /dev/nrawqft2 Unit 2, no rewind-on-close, no file marks - 35 = /dev/nrawqft3 Unit 3, no rewind-on-close, no file marks + 36 = /dev/nrawqft0 Unit 0, no rewind-on-close, no file marks + 37 = /dev/nrawqft1 Unit 1, no rewind-on-close, no file marks + 38 = /dev/nrawqft2 Unit 2, no rewind-on-close, no file marks + 39 = /dev/nrawqft3 Unit 3, no rewind-on-close, no file marks block Third Matsushita (Panasonic/SoundBlaster) CD-ROM 0 = /dev/sbpcd8 Panasonic CD-ROM controller 2 unit 0 1 = /dev/sbpcd9 Panasonic CD-ROM controller 2 unit 1 @@ -564,7 +599,7 @@ char Atari SLM ACSI laser printer (68k/Atari) 0 = /dev/slm0 First SLM laser printer 1 = /dev/slm1 Second SLM laser printer - ... + ... block Fourth Matsushita (Panasonic/SoundBlaster) CD-ROM 0 = /dev/sbpcd12 Panasonic CD-ROM controller 3 unit 0 1 = /dev/sbpcd13 Panasonic CD-ROM controller 3 unit 1 @@ -574,7 +609,7 @@ 0 = /dev/ada First ACSI disk whole disk 16 = /dev/adb Second ACSI disk whole disk 32 = /dev/adc Third ACSI disk whole disk - ... + ... 240 = /dev/adp 16th ACSI disk whole disk Partitions are handled in the same way as for IDE @@ -584,8 +619,8 @@ 29 char Universal frame buffer 0 = /dev/fb0 First frame buffer 32 = /dev/fb1 Second frame buffer - ... - 240 = /dev/fb7 Eighth frame buffer + ... + 224 = /dev/fb7 Eighth frame buffer All additional minor numbers are reserved. @@ -619,13 +654,13 @@ ... 7 = /dev/rom7 Eighth ROM card (rw) 8 = /dev/rrom0 First ROM card (ro) - ... + ... 15 = /dev/rrom7 Eighth ROM card (ro) 16 = /dev/flash0 First flash memory card (rw) - ... + ... 23 = /dev/flash7 Eighth flash memory card (rw) 24 = /dev/rflash0 First flash memory card (ro) - ... + ... 31 = /dev/rflash7 Eighth flash memory card (ro) The read-write (rw) devices support back-caching @@ -636,14 +671,14 @@ 32 char Specialix serial card 0 = /dev/ttyX0 First Specialix port 1 = /dev/ttyX1 Second Specialix port - ... + ... block Philips LMS CM-206 CD-ROM 0 = /dev/cm206cd Philips LMS CM-206 CD-ROM 33 char Specialix serial card - alternate devices - 0 = /dev/cux0 Callout device corresponding to ttyX0 - 1 = /dev/cux1 Callout device corresponding to ttyX1 - ... + 0 = /dev/cux0 Callout device for ttyX0 + 1 = /dev/cux1 Callout device for ttyX1 + ... block Third IDE hard disk/CD-ROM interface 0 = /dev/hde Master: whole disk (or CD-ROM) 64 = /dev/hdf Slave: whole disk (or CD-ROM) @@ -656,7 +691,7 @@ 1 = /dev/scc1 First Z8530, second port 2 = /dev/scc2 Second Z8530, first port 3 = /dev/scc3 Second Z8530, second port - ... + ... In a previous version these devices were named /dev/sc1 for /dev/scc0, /dev/sc2 for /dev/scc1, and so @@ -688,17 +723,25 @@ 36 char Netlink support 0 = /dev/route Routing, device updates, kernel to user 1 = /dev/skip enSKIP security cache control + 3 = /dec/fwmonitor Firewall packet copies + 16 = /dev/tap0 First Ethertap device + ... + 31 = /dev/tap15 16th Ethertap device block MCA ESDI hard disk 0 = /dev/eda First ESDI disk whole disk 64 = /dev/edb Second ESDI disk whole disk - ... + ... Partitions are handled in the same way as IDE disks (see major number 3). 37 char IDE tape 0 = /dev/ht0 First IDE tape + 1 = /dev/ht1 Second IDE tape + ... 128 = /dev/nht0 First IDE tape, no rewind-on-close + 129 = /dev/nht1 Second IDE tape, no rewind-on-close + ... Currently, only one IDE tape drive is supported. @@ -708,7 +751,7 @@ 38 char Myricom PCI Myrinet board 0 = /dev/mlanai0 First Myrinet board 1 = /dev/mlanai1 Second Myrinet board - ... + ... This device is used for status query, board control and "user level packet I/O." This board is also @@ -719,7 +762,7 @@ 39 char ML-16P experimental I/O board 0 = /dev/ml16pa-a0 First card, first analog channel 1 = /dev/ml16pa-a1 First card, second analog channel - ... + ... 15 = /dev/ml16pa-a15 First card, 16th analog channel 16 = /dev/ml16pa-d First card, digital lines 17 = /dev/ml16pa-c0 First card, first counter/timer @@ -727,7 +770,7 @@ 19 = /dev/ml16pa-c2 First card, third counter/timer 32 = /dev/ml16pb-a0 Second card, first analog channel 33 = /dev/ml16pb-a1 Second card, second analog channel - ... + ... 47 = /dev/ml16pb-a15 Second card, 16th analog channel 48 = /dev/ml16pb-d Second card, digital lines 49 = /dev/ml16pb-c0 Second card, first counter/timer @@ -771,12 +814,12 @@ 43 char isdn4linux virtual modem 0 = /dev/ttyI0 First virtual modem - ... + ... 63 = /dev/ttyI63 64th virtual modem block Network block devices 0 = /dev/nb0 First network block device 1 = /dev/nb1 Second network block device - ... + ... Network Block Device is somehow similar to loopback devices: If you read from it, it sends packet accross @@ -786,14 +829,14 @@ the net, implementing block device in userland etc. 44 char isdn4linux virtual modem - alternate devices - 0 = /dev/cui0 Callout device corresponding to ttyI0 - ... - 63 = /dev/cui63 Callout device corresponding to ttyI63 + 0 = /dev/cui0 Callout device for ttyI0 + ... + 63 = /dev/cui63 Callout device for ttyI63 block Flash Translatio Layer (FTL) filesystems 0 = /dev/ftla FTL on first Memory Technology Device 16 = /dev/ftlb FTL on second Memory Technology Device 32 = /dev/ftlc FTL on third Memory Technology Device - ... + ... 240 = /dev/ftlp FTL on 16th Memory Technology Device Partitions are handled in the same way as for IDE @@ -802,14 +845,14 @@ 45 char isdn4linux ISDN BRI driver 0 = /dev/isdn0 First virtual B channel raw data - ... + ... 63 = /dev/isdn63 64th virtual B channel raw data 64 = /dev/isdnctrl0 First channel control/debug - ... + ... 127 = /dev/isdnctrl63 64th channel control/debug 128 = /dev/ippp0 First SyncPPP device - ... + ... 191 = /dev/ippp63 64th SyncPPP device 255 = /dev/isdninfo ISDN monitor interface @@ -826,7 +869,7 @@ 46 char Comtrol Rocketport serial card 0 = /dev/ttyR0 First Rocketport port 1 = /dev/ttyR1 Second Rocketport port - ... + ... block Parallel port ATAPI CD-ROM devices 0 = /dev/pcd0 First parallel port ATAPI CD-ROM 1 = /dev/pcd1 Second parallel port ATAPI CD-ROM @@ -834,9 +877,9 @@ 3 = /dev/pcd3 Fourth parallel port ATAPI CD-ROM 47 char Comtrol Rocketport serial card - alternate devices - 0 = /dev/cur0 Callout device corresponding to ttyR0 - 1 = /dev/cur1 Callout device corresponding to ttyR1 - ... + 0 = /dev/cur0 Callout device for ttyR0 + 1 = /dev/cur1 Callout device for ttyR1 + ... block Parallel port ATAPI disk devices 0 = /dev/pf0 First parallel port ATAPI disk 1 = /dev/pf1 Second parallel port ATAPI disk @@ -849,13 +892,13 @@ 48 char SDL RISCom serial card 0 = /dev/ttyL0 First RISCom port 1 = /dev/ttyL1 Second RISCom port - ... + ... block Reserved for Mylex DAC960 PCI RAID controller 49 char SDL RISCom serial card - alternate devices - 0 = /dev/cul0 Callout device corresponding to ttyL0 - 1 = /dev/cul1 Callout device corresponding to ttyL1 - ... + 0 = /dev/cul0 Callout device for ttyL0 + 1 = /dev/cul1 Callout device for ttyL1 + ... block Reserved for Mylex DAC960 PCI RAID controller 50 char Reserved for GLINT @@ -864,7 +907,7 @@ 51 char Baycom radio modem 0 = /dev/bc0 First Baycom radio modem 1 = /dev/bc1 Second Baycom radio modem - ... + ... block Reserved for Mylex DAC960 PCI RAID controller 52 char Spellcaster DataComm/BRI ISDN card @@ -921,7 +964,7 @@ 57 char Hayes ESP serial card 0 = /dev/ttyP0 First ESP port 1 = /dev/ttyP1 Second ESP port - ... + ... block Sixth IDE hard disk/CD-ROM interface 0 = /dev/hdk Master: whole disk (or CD-ROM) @@ -931,14 +974,25 @@ interface (see major number 3). 58 char Hayes ESP serial card - alternate devices - 0 = /dev/cup0 Callout device corresponding to ttyP0 - 1 = /dev/cup1 Callout device corresponding to ttyP1 - ... + 0 = /dev/cup0 Callout device for ttyP0 + 1 = /dev/cup1 Callout device for ttyP1 + ... block Reserved for logical volume manager 59 char sf firewall package 0 = /dev/firewall Communication with sf kernel module + block Generic PDA filesystem device + 0 = /dev/pda0 First PDA device + 1 = /dev/pda1 Second PDA device + ... + + The pda devices are used to mount filesystems on + remote pda's (basically slow handheld machines with + proprietary OS's and limited memory and storage + running small fs translation drivers) through serial / + IRDA / parallel links. + 60-63 LOCAL/EXPERIMENTAL USE Allocated for local/experimental use. For devices not assigned official numbers, these ranges should be @@ -972,7 +1026,7 @@ 0 = /dev/sdq 16th SCSI disk whole disk 16 = /dev/sdr 17th SCSI disk whole disk 32 = /dev/sds 18th SCSI disk whole disk - ... + ... 240 = /dev/sdaf 32nd SCSI disk whole disk Partitions are handled in the same way as for IDE @@ -982,13 +1036,13 @@ 66 char YARC PowerPC PCI coprocessor card 0 = /dev/yppcpci0 First YARC card 1 = /dev/yppcpci1 Second YARC card - ... + ... block SCSI disk devices (32-47) 0 = /dev/sdag 33th SCSI disk whole disk 16 = /dev/sdah 34th SCSI disk whole disk 32 = /dev/sdai 35th SCSI disk whole disk - ... + ... 240 = /dev/sdav 48nd SCSI disk whole disk Partitions are handled in the same way as for IDE @@ -1004,7 +1058,7 @@ 0 = /dev/sdaw 49th SCSI disk whole disk 16 = /dev/sdax 50th SCSI disk whole disk 32 = /dev/sday 51st SCSI disk whole disk - ... + ... 240 = /dev/sdbl 64th SCSI disk whole disk Partitions are handled in the same way as for IDE @@ -1015,7 +1069,7 @@ 0 = /dev/capi20 Control device 1 = /dev/capi20.00 First CAPI 2.0 application 2 = /dev/capi20.01 Second CAPI 2.0 application - ... + ... 20 = /dev/capi20.19 19th CAPI 2.0 application ISDN CAPI 2.0 driver for use with CAPI 2.0 @@ -1025,7 +1079,7 @@ 0 = /dev/sdbm 64th SCSI disk whole disk 16 = /dev/sdbn 65th SCSI disk whole disk 32 = /dev/sdbo 66th SCSI disk whole disk - ... + ... 240 = /dev/sdcb 80th SCSI disk whole disk Partitions are handled in the same way as for IDE @@ -1039,7 +1093,7 @@ 0 = /dev/sdcc 81st SCSI disk whole disk 16 = /dev/sdcd 82nd SCSI disk whole disk 32 = /dev/sdce 83th SCSI disk whole disk - ... + ... 240 = /dev/sdcr 96th SCSI disk whole disk Partitions are handled in the same way as for IDE @@ -1059,7 +1113,7 @@ 0 = /dev/sdcs 97th SCSI disk whole disk 16 = /dev/sdct 98th SCSI disk whole disk 32 = /dev/sdcu 99th SCSI disk whole disk - ... + ... 240 = /dev/sddh 112nd SCSI disk whole disk Partitions are handled in the same way as for IDE @@ -1069,26 +1123,26 @@ 71 char Computone IntelliPort II serial card 0 = /dev/ttyF0 IntelliPort II board 0, port 0 1 = /dev/ttyF1 IntelliPort II board 0, port 1 - ... + ... 63 = /dev/ttyF63 IntelliPort II board 0, port 63 64 = /dev/ttyF64 IntelliPort II board 1, port 0 65 = /dev/ttyF65 IntelliPort II board 1, port 1 - ... + ... 127 = /dev/ttyF127 IntelliPort II board 1, port 63 128 = /dev/ttyF128 IntelliPort II board 2, port 0 129 = /dev/ttyF129 IntelliPort II board 2, port 1 - ... + ... 191 = /dev/ttyF191 IntelliPort II board 2, port 63 192 = /dev/ttyF192 IntelliPort II board 3, port 0 193 = /dev/ttyF193 IntelliPort II board 3, port 1 - ... + ... 255 = /dev/ttyF255 IntelliPort II board 3, port 63 block SCSI disk devices (112-127) 0 = /dev/sddi 113th SCSI disk whole disk 16 = /dev/sddj 114th SCSI disk whole disk 32 = /dev/sddk 115th SCSI disk whole disk - ... + ... 240 = /dev/sddx 128th SCSI disk whole disk Partitions are handled in the same way as for IDE @@ -1096,22 +1150,22 @@ partitions is 15. 72 char Computone IntelliPort II serial card - alternate devices - 0 = /dev/cuf0 Callout device corresponding to ttyF0 - 1 = /dev/cuf1 Callout device corresponding to ttyF1 - ... - 63 = /dev/cuf63 Callout device corresponding to ttyF63 - 64 = /dev/cuf64 Callout device corresponding to ttyF64 - 65 = /dev/cuf65 Callout device corresponding to ttyF65 - ... - 127 = /dev/cuf127 Callout device corresponding to ttyF127 - 128 = /dev/cuf128 Callout device corresponding to ttyF128 - 129 = /dev/cuf129 Callout device corresponding to ttyF129 - ... - 191 = /dev/cuf191 Callout device corresponding to ttyF191 - 192 = /dev/cuf192 Callout device corresponding to ttyF192 - 193 = /dev/cuf193 Callout device corresponding to ttyF193 - ... - 255 = /dev/cuf255 Callout device corresponding to ttyF255 + 0 = /dev/cuf0 Callout device for ttyF0 + 1 = /dev/cuf1 Callout device for ttyF1 + ... + 63 = /dev/cuf63 Callout device for ttyF63 + 64 = /dev/cuf64 Callout device for ttyF64 + 65 = /dev/cuf65 Callout device for ttyF65 + ... + 127 = /dev/cuf127 Callout device for ttyF127 + 128 = /dev/cuf128 Callout device for ttyF128 + 129 = /dev/cuf129 Callout device for ttyF129 + ... + 191 = /dev/cuf191 Callout device for ttyF191 + 192 = /dev/cuf192 Callout device for ttyF192 + 193 = /dev/cuf193 Callout device for ttyF193 + ... + 255 = /dev/cuf255 Callout device for ttyF255 73 char Computone IntelliPort II serial card - control devices 0 = /dev/ip2ipl0 Loadware device for board 0 @@ -1126,7 +1180,7 @@ 74 char SCI bridge 0 = /dev/SCI/0 SCI device 0 1 = /dev/SCI/1 SCI device 1 - ... + ... Currently for Dolphin Interconnect Solutions' PCI-SCI bridge. @@ -1134,16 +1188,16 @@ 75 char Specialix IO8+ serial card 0 = /dev/ttyW0 First IO8+ port, first card 1 = /dev/ttyW1 Second IO8+ port, first card - ... + ... 8 = /dev/ttyW8 First IO8+ port, second card - ... + ... 76 char Specialix IO8+ serial card - alternate devices - 0 = /dev/cuw0 Callout device corresponding to ttyW0 - 1 = /dev/cuw1 Callout device corresponding to ttyW1 - ... - 8 = /dev/cuw8 Callout device corresponding to ttyW8 - ... + 0 = /dev/cuw0 Callout device for ttyW0 + 1 = /dev/cuw1 Callout device for ttyW1 + ... + 8 = /dev/cuw8 Callout device for ttyW8 + ... 77 char ComScire Quantum Noise Generator 0 = /dev/qng ComScire Quantum Noise Generator @@ -1151,38 +1205,68 @@ 78 char PAM Software's multimodem boards 0 = /dev/ttyM0 First PAM modem 1 = /dev/ttyM1 Second PAM modem - ... + ... 79 char PAM Software's multimodem boards - alternate devices - 0 = /dev/cum0 Callout device corresponding to ttyM0 - 1 = /dev/cum1 Callout device corresponding to ttyM1 - ... + 0 = /dev/cum0 Callout device for ttyM0 + 1 = /dev/cum1 Callout device for ttyM1 + ... 80 char Photometrics AT200 CCD camera 0 = /dev/at200 Photometrics AT200 CCD camera + block I2O hard disk + 0 = /dev/i2o/hda First I2O hard disk, whole disk + 16 = /dev/i2o/hdb Second I2O hard disk, whole disk + ... + 240 = /dev/i2o/hdp 16th I2O hard disk, whole disk + + Partitions are handled in the same way as for IDE + disks (see major number 3) except that the limit on + partitions is 15. + 81 char video4linux 0 = /dev/video0 Video capture/overlay device - ... + ... 63 = /dev/video63 Video capture/overlay device 64 = /dev/radio0 Radio device - ... + ... 127 = /dev/radio63 Radio device 192 = /dev/vtx0 Teletext device - ... + ... 223 = /dev/vtx31 Teletext device 224 = /dev/vbi0 Vertical blank interrupt - ... + ... 255 = /dev/vbi31 Vertical blank interrupt + block I2O hard disk + 0 = /dev/i2o/hdq 17th I2O hard disk, whole disk + 16 = /dev/i2o/hdr 18th I2O hard disk, whole disk + ... + 240 = /dev/i2o/hdaf 32nd I2O hard disk, whole disk + + Partitions are handled in the same way as for IDE + disks (see major number 3) except that the limit on + partitions is 15. + 82 char WiNRADiO communications receiver card 0 = /dev/winradio0 First WiNRADiO card 1 = /dev/winradio1 Second WiNRADiO card - ... + ... The driver and documentation may be obtained from http://www.proximity.com.au/~brian/winradio/ + block I2O hard disk + 0 = /dev/i2o/hdag 33rd I2O hard disk, whole disk + 16 = /dev/i2o/hdah 34th I2O hard disk, whole disk + ... + 240 = /dev/i2o/hdav 48th I2O hard disk, whole disk + + Partitions are handled in the same way as for IDE + disks (see major number 3) except that the limit on + partitions is 15. + 83 char Teletext/videotext interfaces 0 = /dev/vtx Teletext decoder 16 = /dev/vttuner TV tuner on teletext interface @@ -1190,36 +1274,101 @@ Devices for the driver contained in the VideoteXt package. More information on http://home.pages.de/~videotext/ + block I2O hard disk + 0 = /dev/i2o/hdaw 49th I2O hard disk, whole disk + 16 = /dev/i2o/hdax 50th I2O hard disk, whole disk + ... + 240 = /dev/i2o/hdbl 64th I2O hard disk, whole disk + + Partitions are handled in the same way as for IDE + disks (see major number 3) except that the limit on + partitions is 15. + 84 char Ikon 1011[57] Versatec Greensheet Interface 0 = /dev/ihcp0 First Greensheet port 1 = /dev/ihcp1 Second Greensheet port + block I2O hard disk + 0 = /dev/i2o/hdbm 65th I2O hard disk, whole disk + 16 = /dev/i2o/hdbn 66th I2O hard disk, whole disk + ... + 240 = /dev/i2o/hdcb 80th I2O hard disk, whole disk + + Partitions are handled in the same way as for IDE + disks (see major number 3) except that the limit on + partitions is 15. + 85 char Linux/SGI shared memory input queue 0 = /dev/shmiq Master shared input queue 1 = /dev/qcntl0 First device pushed 2 = /dev/qcntl1 Second device pushed ... + block I2O hard disk + 0 = /dev/i2o/hdcc 81st I2O hard disk, whole disk + 16 = /dev/i2o/hdcd 82nd I2O hard disk, whole disk + ... + 240 = /dev/i2o/hdcr 96th I2O hard disk, whole disk + + Partitions are handled in the same way as for IDE + disks (see major number 3) except that the limit on + partitions is 15. + 86 char SCSI media changer 0 = /dev/sch0 First SCSI media changer 1 = /dev/sch1 Second SCSI media changer ... + block I2O hard disk + 0 = /dev/i2o/hdcs 97th I2O hard disk, whole disk + 16 = /dev/i2o/hdct 98th I2O hard disk, whole disk + ... + 240 = /dev/i2o/hddh 112th I2O hard disk, whole disk + + Partitions are handled in the same way as for IDE + disks (see major number 3) except that the limit on + partitions is 15. + 87 char Sony Control-A1 stereo control bus 0 = /dev/controla0 First device on chain 1 = /dev/controla1 Second device on chain ... + block I2O hard disk + 0 = /dev/i2o/hddi 113rd I2O hard disk, whole disk + 16 = /dev/i2o/hddj 114th I2O hard disk, whole disk + ... + 240 = /dev/i2o/hddx 128th I2O hard disk, whole disk + + Partitions are handled in the same way as for IDE + disks (see major number 3) except that the limit on + partitions is 15. + 88 char COMX synchronous serial card 0 = /dev/comx0 COMX channel 0 1 = /dev/comx1 COMX channel 1 ... + block Seventh IDE hard disk/CD-ROM interface + 0 = /dev/hdm Master: whole disk (or CD-ROM) + 64 = /dev/hdn Slave: whole disk (or CD-ROM) + + Partitions are handled the same way as for the first + interface (see major number 3). + + 89 char I2C bus interface - 0 = /dev/i2c0 First I2C adapter - 1 = /dev/i2c1 Second I2C adapter + 0 = /dev/i2c-0 First I2C adapter + 1 = /dev/i2c-1 Second I2C adapter ... + block Eighth IDE hard disk/CD-ROM interface + 0 = /dev/hdo Master: whole disk (or CD-ROM) + 64 = /dev/hdp Slave: whole disk (or CD-ROM) + + Partitions are handled the same way as for the first + interface (see major number 3). + 90 char Memory Technology Device (RAM, ROM, Flash) 0 = /dev/mtd0 First MTD (rw) 1 = /dev/mtdr0 First MTD (ro) @@ -1227,13 +1376,36 @@ 30 = /dev/mtd15 16th MTD (rw) 31 = /dev/mtdr15 16th MTD (ro) + block Ninth IDE hard disk/CD-ROM interface + 0 = /dev/hdq Master: whole disk (or CD-ROM) + 64 = /dev/hdr Slave: whole disk (or CD-ROM) + + Partitions are handled the same way as for the first + interface (see major number 3). + 91 char CAN-Bus devices 0 = /dev/can0 First CAN-Bus controller 1 = /dev/can1 Second CAN-Bus controller ... + block Tenth IDE hard disk/CD-ROM interface + 0 = /dev/hds Master: whole disk (or CD-ROM) + 64 = /dev/hdt Slave: whole disk (or CD-ROM) + + Partitions are handled the same way as for the first + interface (see major number 3). + 92 char Reserved for ith Kommunikationstechnik MIC ISDN card + block PPDD encrypted disk driver + 0 = /dev/ppdd0 First encrypted disk + 1 = /dev/ppdd1 Second encrypted disk + ... + + Partitions are handled in the same way as for IDE + disks (see major number 3) except that the limit on + partitions is 15. + 93 char IBM Smart Capture Card frame grabber 0 = /dev/iscc0 First Smart Capture Card 1 = /dev/iscc1 Second Smart Capture Card @@ -1242,6 +1414,12 @@ 129 = /dev/isccctl1 Second Smart Capture Card control ... + block NAND Flash Translation Layer filesystem + 0 = /dev/nftla First NFTL layer + 16 = /dev/nftlb Second NFTL layer + ... + 240 = /dev/nftlp 16th NTFL layer + 94 char miroVIDEO DC10/30 capture/playback device 0 = /dev/dcxx0 First capture card 1 = /dev/dcxx1 Second capture card @@ -1253,15 +1431,29 @@ 2 = /dev/ipstate State information log file 3 = /dev/ipauth Authentication control device/log file + block IBM S/390 DASD block storage + 0 = /dev/dasd0 First DASD device, major + 1 = /dev/dasd0a First DASD device, block 1 + 2 = /dev/dasd0b First DASD device, block 2 + 3 = /dev/dasd0c First DASD device, block 3 + 4 = /dev/dasd1 Second DASD device, major + 5 = /dev/dasd1a Second DASD device, block 1 + 6 = /dev/dasd1b Second DASD device, block 2 + 7 = /dev/dasd1c Second DASD device, block 3 + ... + 96 char Parallel port ATAPI tape devices 0 = /dev/pt0 First parallel port ATAPI tape 1 = /dev/pt1 Second parallel port ATAPI tape - 2 = /dev/pt2 Third parallel port ATAPI tape - 3 = /dev/pt3 Fourth parallel port ATAPI tape + ... 128 = /dev/npt0 First p.p. ATAPI tape, no rewind 129 = /dev/npt1 Second p.p. ATAPI tape, no rewind - 130 = /dev/npt2 Third p.p. ATAPI tape, no rewind - 131 = /dev/npt3 Fourth p.p. ATAPI tape, no rewind + ... + + block IBM S/390 VM/ESA minidisk + 0 = /dev/msd0 First VM/ESA minidisk + 1 = /dev/msd1 Second VM/ESA minidisk + ... 97 char Parallel port generic ATAPI interface 0 = /dev/pg0 First parallel port ATAPI device @@ -1336,7 +1528,64 @@ 1 = /dev/srnd1 Second miroMEDIA Surround board ... -111-119 UNALLOCATED +111 char Philips SAA7146-based audio/video card + 0 = /dev/av0 First A/V card + 1 = /dev/av1 Second A/V card + ... + +112 char ISI serial card + 0 = /dev/ttyM0 First ISI port + 1 = /dev/ttyM1 Second ISI port + ... + + There is currently a device-naming conflict between + these and PAM multimodems (major 78). + +113 char ISI serial card - alternate devices + 0 = /dev/cum0 Callout device for ttyM0 + 1 = /dev/cum1 Callout device for ttyM1 + ... + +114 char Picture Elements ISE board + 0 = /dev/ise0 First ISE board + 1 = /dev/ise1 Second ISE board + ... + 128 = /dev/isex0 Control node for first ISE board + 129 = /dev/isex1 Control node for second ISE board + ... + + The ISE board is an embedded computer, optimized for + image processing. The /dev/iseN nodes are the general + I/O access to the board, the /dev/isex0 nodes command + nodes used to control the board. + +115 char Console driver speaker + 0 = /dev/speaker Speaker device file + + Plays music using IBM BASIC style strings. + +116 char Advanced Linux System Driver (ALSA) + +117 char COSA/SRP synchronous serial card + 0 = /dev/cosa0c0 1st board, 1st channel + 1 = /dev/cosa0c1 1st board, 2nd channel + ... + 16 = /dev/cosa1c0 2nd board, 1st channel + 17 = /dev/cosa1c1 2nd board, 2nd channel + ... + +118 char Solidum ??? + 0 = /dev/solnp0 + 1 = /dev/solnp1 + ... + 128 = /dev/solnpctl0 + 129 = /dev/solnpctl1 + ... + +119 char VMware virtual network control + 0 = /dev/vnet0 1st virtual network + 1 = /dev/vnet1 2nd virtual network + ... 120-127 LOCAL/EXPERIMENTAL USE @@ -1349,13 +1598,290 @@ 136-143 char Unix98 PTY slaves 0 = /dev/pts/0 First Unix98 pseudo-TTY 1 = /dev/pts/1 Second Unix98 pesudo-TTY + ... These device nodes are automatically generated with the proper permissions and modes by mounting the devpts filesystem onto /dev/pts with the appropriate - mount options (distribution dependent). + mount options (distribution dependent, however, on + *most* distributions the appropriate options are + "mode=0620,gid=".) + +144 char Encapsulated PPP + 0 = /dev/pppox0 First PPP over Ethernet + ... + 63 = /dev/pppox63 64th PPP over Ethernet + + This is primarily used for ADSL. + + The SST 5136-DN DeviceNet interface driver has been + relocated to major 183 due to an unfortunate conflict. + +145 char SAM9407-based soundcard + 0 = /dev/sam0_mixer + 1 = /dev/sam0_sequencer + 2 = /dev/sam0_midi00 + 3 = /dev/sam0_dsp + 4 = /dev/sam0_audio + 6 = /dev/sam0_sndstat + 18 = /dev/sam0_midi01 + 34 = /dev/sam0_midi02 + 50 = /dev/sam0_midi03 + 64 = /dev/sam1_mixer + ... + 128 = /dev/sam2_mixer + ... + 192 = /dev/sam3_mixer + ... + + Device functions match OSS, but offer a number of + addons, which are sam9407 specific. OSS can be + operated simultaneously, taking care of the codec. + +146 char SYSTRAM SCRAMNet mirrored-memory network + 0 = /dev/scramnet0 First SCRAMNet device + 1 = /dev/scramnet1 Second SCRAMNet device + ... + +147 char Aueral Semiconductor Vortex Audio device + 0 = /dev/aureal0 First Aureal Vortex + 1 = /dev/aureal1 Second Aureal Vortex + ... + +148 char Technology Concepts serial card + 0 = /dev/ttyT0 First TCL port + 1 = /dev/ttyT1 Second TCL port + ... + +149 char Technology Concepts serial card - alternate devices + 0 = /dev/cut0 Callout device for ttyT0 + 1 = /dev/cut0 Callout device for ttyT1 + ... + +150 char Real-Time Linux FIFOs + 0 = /dev/rtf0 First RTLinux FIFO + 1 = /dev/rtf1 Second RTLinux FIFO + ... + +151 char DPT I2O SmartRaid V controller + 0 = /dev/dpti0 First DPT I2O adapter + 1 = /dev/dpti1 Second DPT I2O adapter + ... + +154 char Specialix RIO serial card + 0 = /dev/ttySR0 First RIO port + ... + 255 = /dev/ttySR255 256th RIO port + +155 char Specialix RIO serial card - alternate devices + 0 = /dev/cusr0 Callout device for ttySR0 + ... + 255 = /dev/cusr255 Callout device for ttySR255 + +156 char Specialix RIO serial card + 0 = /dev/ttySR256 257th RIO port + ... + 255 = /dev/ttySR511 512th RIO port + +157 char Specialix RIO serial card - alternate devices + 0 = /dev/cusr256 Callout device for ttySR256 + ... + 255 = /dev/cusr511 Callout device for ttySR511 + +158 char Dialogic GammaLink fax driver + 0 = /dev/gfax0 GammaLink channel 0 + 1 = /dev/gfax1 GammaLink channel 1 + ... + +159 char Quicknet Technologies Internet PhoneJack/LineJack + 0 = /dev/ixj0 First device + 1 = /dev/ixj1 Second device + ... + +160 char General Purpose Instrument Bus (GPIB) + 0 = /dev/gpib0 First GPIB bus + 1 = /dev/gpib1 Second GPIB bus + ... + +161 char IrCOMM devices (IrDA serial/parallel emulation) + 0 = /dev/ircomm0 First IrCOMM device + 1 = /dev/ircomm1 Second IrCOMM device + ... + 16 = /dev/irlpt0 First IrLPT device + 17 = /dev/irlpt1 Second IrLPT device + ... + +162 char Raw block device interface + 0 = /dev/raw Raw I/O control device + 1 = /dev/raw1 First raw I/O device + 2 = /dev/raw2 Second raw I/O device + ... + +163 char Radio Tech BIM-XXX-RS232 radio modem + 0 = /dev/bimrt0 First BIM radio modem + 1 = /dev/bimrt1 Second BIM radio modem + ... + +164 char Chase Research AT/PCI-Fast serial card + 0 = /dev/ttyCH0 AT/PCI-Fast board 0, port 0 + ... + 15 = /dev/ttyCH15 AT/PCI-Fast board 0, port 15 + 16 = /dev/ttyCH16 AT/PCI-Fast board 1, port 0 + ... + 31 = /dev/ttyCH31 AT/PCI-Fast board 1, port 15 + 32 = /dev/ttyCH32 AT/PCI-Fast board 2, port 0 + ... + 47 = /dev/ttyCH47 AT/PCI-Fast board 2, port 15 + 48 = /dev/ttyCH48 AT/PCI-Fast board 3, port 0 + ... + 63 = /dev/ttyCH63 AT/PCI-Fast board 3, port 15 + +165 char Chase Research AT/PCI-Fast serial card - alternate devices + 0 = /dev/cuch0 Callout device for ttyCH0 + ... + 63 = /dev/cuch63 Callout device for ttyCH63 + +166 char ACM USB modems + 0 = /dev/ttyACM0 First ACM modem + 1 = /dev/ttyACM1 Second ACM modem + ... + +167 char ACM USB modems - alternate devices + 0 = /dev/cuacm0 Callout device for ttyACM0 + 1 = /dev/cuacm1 Callout device for ttyACM1 + ... + +168 char Eracom CSA7000 PCI encryption adaptor + 0 = /dev/ecsa0 First CSA7000 + 1 = /dev/ecsa1 Second CSA7000 + ... + +169 char Eracom CSA8000 PCI encryption adaptor + 0 = /dev/ecsa8-0 First CSA8000 + 1 = /dev/ecsa8-1 Second CSA8000 + ... + +170 char AMI MegaRAC remote access controller + 0 = /dev/megarac0 First MegaRAC card + 1 = /dev/megarac1 Second MegaRAC card + ... + +171 char Reserved for IEEE 1394 (Firewire) -144-239 UNALLOCATED + +172 char Moxa Intellio serial card + 0 = /dev/ttyMX0 First Moxa port + 1 = /dev/ttyMX1 Second Moxa port + ... + 127 = /dev/ttyMX127 128th Moxa port + 128 = /dev/moxactl Moxa control port + +173 char Moxa Intellio serial card - alternate devices + 0 = /dev/cumx0 Callout device for ttyMX0 + 1 = /dev/cumx1 Callout device for ttyMX1 + ... + 127 = /dev/cumx127 Callout device for ttyMX127 + +174 char SmartIO serial card + 0 = /dev/ttySI0 First SmartIO port + 1 = /dev/ttySI1 Second SmartIO port + ... + +175 char SmartIO serial card - alternate devices + 0 = /dev/cusi0 Callout device for ttySI0 + 1 = /dev/cusi1 Callout device for ttySI1 + ... + +176 char nCipher nFast PCI crypto accelerator + 0 = /dev/nfastpci0 First nFast PCI device + 1 = /dev/nfastpci1 First nFast PCI device + ... + +177 char TI PCILynx memory spaces + 0 = /dev/pcilynx/aux0 AUX space of first PCILynx card + ... + 15 = /dev/pcilynx/aux15 AUX space of 16th PCILynx card + 16 = /dev/pcilynx/rom0 ROM space of first PCILynx card + ... + 31 = /dev/pcilynx/rom15 ROM space of 16th PCILynx card + 32 = /dev/pcilynx/ram0 RAM space of first PCILynx card + ... + 47 = /dev/pcilynx/ram15 RAM space of 16th PCILynx card + +178 char Giganet cLAN1xxx virtual interface adapter + 0 = /dev/clanvi0 First cLAN adapter + 1 = /dev/clanvi1 Second cLAN adapter + ... + +179 char CCube DVXChip-based PCI products + 0 = /dev/dvxirq0 First DVX device + 1 = /dev/dvxirq1 Second DVX device + ... + +180 char USB devices + 0 = /dev/usb/lp0 First USB printer + ... + 15 = /dev/usb/lp15 16th USB printer + 16 = /dev/usb/mouse0 First USB mouse + ... + 31 = /dev/usb/mouse15 16th USB mouse + 32 = /dev/usb/ez0 First USB firmware loader + ... + 47 = /dev/usb/ez15 16th USB firmware loader + 48 = /dev/usb/scanner0 First USB scanner + ... + 63 = /dev/usb/scanner15 16th USB scanner + +181 char Conrad Electronic parallel port radio clocks + 0 = /dev/pcfclock0 First Conrad radio clock + 1 = /dev/pcfclock1 Second Conrad radio clock + ... + +182 char Picture Elements THR2 binarizer + 0 = /dev/pethr0 First THR2 board + 1 = /dev/pethr1 Second THR2 board + ... + +183 char SST 5136-DN DeviceNet interface + 0 = /dev/ss5136dn0 First DeviceNet interface + 1 = /dev/ss5136dn1 Second DeviceNet interface + ... + + This device used to be assigned to major number 144. + It had to be moved due to an unfortunate conflict. + +184 char Picture Elements' video simulator/sender + 0 = /dev/pevss0 First sender board + 1 = /dev/pevss1 Second sender board + ... + +185 char Reserved for InterMezzo high availability file system + +186 char Object-based storage control device + 0 = /dev/obd0 First obd control device + 1 = /dev/obd1 Second obd control device + ... + + See ftp://ftp.lustre.org/pub/obd for code and information. + +187 char UNALLOCATED + +188 char USB serial converters + 0 = /dev/ttyUSB0 First USB serial converter + 1 = /dev/ttyUSB1 Second USB serial converter + ... + +189 char USB serial converters - alternate devices + 0 = /dev/cuusb0 Callout device for ttyUSB0 + 1 = /dev/cuusb1 Callout device for ttyUSB1 + ... + +190 char Kansas City tracker/tuner card + 0 = /dev/kctt0 First KCT/T card + 1 = /dev/kctt1 Second KCT/T card + ... + +191-239 UNALLOCATED 240-254 LOCAL/EXPERIMENTAL USE @@ -1384,8 +1910,9 @@ /dev/stderr fd/2 symbolic stderr file descriptor /dev/nfsd socksys symbolic Required by iBCS-2 /dev/X0R null symbolic Required by iBCS-2 +/dev/i2o* /dev/i2o/* symbolic Backward compatibility -Note: the last device is --. +Note: /dev/X0R is --. Recommended links @@ -1463,7 +1990,7 @@ starting at /dev/tty1; /dev/tty0 is the current virtual console. /dev/tty0 is the device that should be used to access the system video card on those architectures for which the frame buffer devices -(/dev/fb*) are not applicable. Do not use /dev/console +(/dev/fb*) are not applicable. Do not use /dev/console for this purpose. The console device, /dev/console, is the device to which system @@ -1480,10 +2007,10 @@ one, either in hardware (such as internal modems) or in software (such as the ISDN driver.) Under Linux, each serial ports has two device names, the primary or callin device and the alternate or callout one. -Each kind of device is indicated by a different letter. For any +Each kind of device is indicated by a different letter. For any letter X, the names of the devices are /dev/ttyX# and /dev/cux#, respectively; for historical reasons, /dev/ttyS# and /dev/ttyC# -correspond to /dev/cua# and /dev/cub#. In the future, it should be +correspond to /dev/cua# and /dev/cub#. In the future, it should be expected that multiple letters will be used; all letters will be upper case for the "tty" device (e.g. /dev/ttyDP#) and lower case for the "cu" device (e.g. /dev/cudp#). @@ -1497,7 +2024,7 @@ removed from a future version of Linux. Arbitration of serial ports is provided by the use of lock files with -the names /var/lock/LCK..ttyX#. The contents of the lock file should +the names /var/lock/LCK..ttyX#. The contents of the lock file should be the PID of the locking process as an ASCII number. It is common practice to install links such as /dev/modem @@ -1505,7 +2032,7 @@ presence of these links, it is recommended that software chase symlinks and lock all possible names; additionally, it is recommended that a lock file be installed with the corresponding alternate -device. In order to avoid deadlocks, it is recommended that the locks +device. In order to avoid deadlocks, it is recommended that the locks are acquired in the following order, and released in the reverse: 1. The symbolic link name, if any (/var/lock/LCK..modem) @@ -1525,7 +2052,7 @@ Pseudoterminals, or PTYs, are used to create login sessions or provide other capabilities requiring a TTY line dicipline (including SLIP or -PPP capability) to arbitrary data-generation processes. Each PTY has +PPP capability) to arbitrary data-generation processes. Each PTY has a master side, named /dev/pty[p-za-e][0-9a-f], and a slave side, named /dev/tty[p-za-e][0-9a-f]. The kernel arbitrates the use of PTYs by allowing each master side to be opened only once. @@ -1543,3 +2070,4 @@ in our notation). This removes the problem of exhausting the namespace and enables the kernel to automatically create the device nodes for the slaves on demand using the "devpts" filesystem. + diff -u --recursive --new-file v2.2.13/linux/Documentation/isdn/00-INDEX linux/Documentation/isdn/00-INDEX --- v2.2.13/linux/Documentation/isdn/00-INDEX Wed Apr 1 16:20:56 1998 +++ linux/Documentation/isdn/00-INDEX Tue Jan 4 10:12:10 2000 @@ -6,12 +6,18 @@ - description of Linklevel and Hardwarelevel ISDN interface. README - general info on what you need and what to do for Linux ISDN. +README.FAQ + - general info for FAQ. README.audio - info for running audio over ISDN. +README.fax + - info for using Fax over ISDN. README.icn - info on the ICN-ISDN-card and its driver. README.HiSax - info on the HiSax driver which replaces the old teles. +README.hfc-pci + - info on hfc-pci based cards. README.pcbit - info on the PCBIT-D ISDN adapter and driver. README.syncppp @@ -22,8 +28,12 @@ - info on driver for AVM-B1 ISDN card. README.act2000 - info on driver for IBM ACT-2000 card. +README.eicon + - info on driver for Eicon active cards. README.concap - info on "CONCAP" ecapsulation protocol interface used for X.25. +README.diversion + - info on module for isdn diversion services. README.sc - info on driver for Spellcaster cards. README.x25 diff -u --recursive --new-file v2.2.13/linux/Documentation/isdn/HiSax.cert linux/Documentation/isdn/HiSax.cert --- v2.2.13/linux/Documentation/isdn/HiSax.cert Tue Oct 19 17:10:36 1999 +++ linux/Documentation/isdn/HiSax.cert Tue Jan 4 10:12:10 2000 @@ -30,7 +30,7 @@ files are protected by md5 checksums and the md5sum file is pgp signed by myself: -KeyID 1024/FF992F6D 1997/01/16 Karsten Keil +KeyID 1024/FF992F6D 1997/01/16 Karsten Keil Key fingerprint = 92 6B F7 58 EE 86 28 C8 C4 1A E6 DC 39 89 F2 AA Only if the checksums are OK, and the signature of the file @@ -70,9 +70,9 @@ Version: 2.6.3i Charset: noconv -iQCVAwUBN6xoKTpxHvX/mS9tAQF4DAP/efRWym6jvNOND1O9eaEFdP5fd2xKB3XD -Ifh6Iv0DvARcIuxXtEjT+z3FjjQk35eo/wX4C4tpRhYQYdgCxl+iv+5DzhVDpB95 -3QS9E5m0E1eIK3t8XiQTRgb+1JPCMYUThCrakYsX25o3ndGKyDipsCTfkyR38XwC -bUyTfcOYKAk= -=VKyL +iQCVAwUBOFAwqTpxHvX/mS9tAQFI2QP9GLDK2iy/KBhwReE3F7LeO+tVhffTVZ3a +20q5/z/WcIg/pnH0uTkl2UgDXBFXYl45zJyDGNpAposIFmT+Edd14o7Vj1w/BBdn +Y+5rBmJf+gyBu61da5d6bv0lpymwRa/um+ri+ilYnZ/XPfg5JKhdjGSBCJuJAElM +d2jFbTrsMYw= +=LNf9 -----END PGP SIGNATURE----- diff -u --recursive --new-file v2.2.13/linux/Documentation/isdn/README linux/Documentation/isdn/README --- v2.2.13/linux/Documentation/isdn/README Tue Jan 4 11:10:31 2000 +++ linux/Documentation/isdn/README Tue Jan 4 10:12:10 2000 @@ -11,7 +11,7 @@ necessary. Those programs and some contributed utilities are available at - ftp.franken.de + ftp.isdn4linux.de /pub/isdn4linux/isdn4k-utils-.tar.gz @@ -22,11 +22,11 @@ reasons, the mailing-list's primary language is german. However mails written in english have been welcome all the time. - to subscribe: write a email to majordomo@hub-wue.franken.de, + to subscribe: write a email to majordomo@listserv.isdn4linux.de, Subject irrelevant, in the message body: subscribe isdn4linux - To write to the mailing-list, write to isdn4linux@hub-wue.franken.de + To write to the mailing-list, write to isdn4linux@listserv.isdn4linux.de This mailinglist is bidirectionally gated to the newsgroup diff -u --recursive --new-file v2.2.13/linux/Documentation/isdn/README.FAQ linux/Documentation/isdn/README.FAQ --- v2.2.13/linux/Documentation/isdn/README.FAQ Wed Dec 31 16:00:00 1969 +++ linux/Documentation/isdn/README.FAQ Tue Jan 4 10:12:10 2000 @@ -0,0 +1,26 @@ + +The FAQ for isdn4linux +====================== + +Please note that there is a big FAQ available in the isdn4k-utils. +You find it in: + isdn4k-utils/FAQ/i4lfaq.sgml + +In case you just want to see the FAQ online, or download the newest version, +you can have a look at my website: +http://www.mhessler.de/i4lfaq/ (view + download) +or: +http://www.isdn4linux.de/faq/ (view) + +As the extension tells, the FAQ is in SGML format, and you can convert it +into text/html/... format by using the sgml2txt/sgml2html/... tools. +Alternatively, you can also do a 'configure; make all' in the FAQ directory. + + +Please have a look at the FAQ before posting anything in the Mailinglist, +or the newsgroup! + + +Matthias Hessler +hessler@isdn4linux.de + diff -u --recursive --new-file v2.2.13/linux/Documentation/isdn/README.avmb1 linux/Documentation/isdn/README.avmb1 --- v2.2.13/linux/Documentation/isdn/README.avmb1 Mon Aug 9 16:05:54 1999 +++ linux/Documentation/isdn/README.avmb1 Tue Jan 4 10:12:10 2000 @@ -169,7 +169,7 @@ Questions --------- -Check out the FAQ (ftp.franken.de) or subscribe to the +Check out the FAQ (ftp.isdn4linux.de) or subscribe to the linux-avmb1@calle.in-berlin.de mailing list by sending a mail to majordomo@calle.in-berlin.de with subscribe linux-avmb1 diff -u --recursive --new-file v2.2.13/linux/Documentation/isdn/README.eicon linux/Documentation/isdn/README.eicon --- v2.2.13/linux/Documentation/isdn/README.eicon Mon Aug 9 16:05:54 1999 +++ linux/Documentation/isdn/README.eicon Tue Jan 4 10:12:10 2000 @@ -1,6 +1,6 @@ -$Id: README.eicon,v 1.4 1999/07/11 17:17:30 armin Exp $ +$Id: README.eicon,v 1.5 1999/10/11 18:13:25 armin Exp $ -(c) 1999 Cytronics & Melware +(c) 1999 Cytronics & Melware (info@melware.de) This document describes the eicon driver for the Eicon.Diehl active ISDN cards. @@ -24,17 +24,26 @@ Supported Cards ---------------- +=============== +Old ISA type +------------ - S-Card ISA - SX-Card ISA - SXn-Card ISA - SCOM-Card ISA - Quadro-Card ISA - S2M-Card ISA + +DIVA Server family +------------------ - DIVA Server BRI/PCI 2M - DIVA Server PRI/PCI 2M (9M 23M 30M) - (Only analog modem functions of the DSPs are currently implemented) + supported functions of onboard DSPs: + - analog modem + - fax group 2/3 (Fax Class 2 commands) + - DTMF detection + ISDN D-Channel Protocols ------------------------ @@ -76,6 +85,10 @@ Details about using the eiconctrl utility are in 'man eiconctrl' or will be printed by starting eiconctrl without any parameters. +Thanks to + Deutsche Mailbox Saar-Lor-Lux GmbH + for sponsoring and testing fax + capabilities with Diva Server cards. Any reports about bugs, errors and even wishes are welcome. diff -u --recursive --new-file v2.2.13/linux/Documentation/isdn/README.fax linux/Documentation/isdn/README.fax --- v2.2.13/linux/Documentation/isdn/README.fax Tue Oct 19 17:10:36 1999 +++ linux/Documentation/isdn/README.fax Tue Jan 4 10:12:10 2000 @@ -9,7 +9,7 @@ - You need the commands as dummy, because you are using hylafax (with patch) for AVM capi. -- You want to use the fax capabillities of your isdn-card. +- You want to use the fax capabilities of your isdn-card. (supported cards are listed below) @@ -21,8 +21,12 @@ Supported ISDN-Cards -------------------- -Eicon DIVA Server BRI/PCI (will be ready soon) -Eicon DIVA Server PRI/PCI (will be ready soon) +Eicon DIVA Server BRI/PCI + - full support with both B-channels. + +Eicon DIVA Server PRI/PCI + - full support on amount of B-channels + depending on DSPs on board. diff -u --recursive --new-file v2.2.13/linux/Documentation/isdn/README.pcbit linux/Documentation/isdn/README.pcbit --- v2.2.13/linux/Documentation/isdn/README.pcbit Tue Apr 28 14:22:04 1998 +++ linux/Documentation/isdn/README.pcbit Tue Jan 4 10:12:10 2000 @@ -32,7 +32,7 @@ the manufacturer in order to solve this problem. Information/hints/help can be obtained in the linux isdn -mailing list (isdn4linux@hub-wue.franken.de) or directly from me. +mailing list (isdn4linux@listserv.isdn4linux.de) or directly from me. regards, Pedro. diff -u --recursive --new-file v2.2.13/linux/Documentation/joystick-api.txt linux/Documentation/joystick-api.txt --- v2.2.13/linux/Documentation/joystick-api.txt Wed Oct 21 08:43:33 1998 +++ linux/Documentation/joystick-api.txt Tue Jan 4 10:12:10 2000 @@ -1,7 +1,7 @@ Joystick API Documentation -*-Text-*- Ragnar Hojland Espinosa - + 7 Aug 1998 @@ -75,7 +75,7 @@ ...and so on Hats vary from one joystick type to another. Some can be moved in 8 -directions, some only in 4, however, the driver always reports a hat as two +directions, some only in 4, The driver, however, always reports a hat as two independent axis, even if the hardware doesn't allow independent movement. @@ -85,7 +85,7 @@ For an axis, ``value'' is a signed integer between -32767 and +32767 representing the position of the joystick along that axis. If you don't read a 0 when the joystick is `dead', or if it doesn't span the -full range, you should recalibrate (with, for example, jscal). +full range, you should recalibrate it (with, for example, jscal). For a button, ``value'' for a press button event is 1 and for a release button event is 0. @@ -93,16 +93,16 @@ Though this if (js_event.type == JS_EVENT_BUTTON) { - buttons_state ^= (1 << js_event.number); + buttons_state ^= (1 << js_event.number); } may work well if you handle JS_EVENT_INIT events separately, if ((js_event.type & ~JS_EVENT_INIT) == JS_EVENT_BUTTON) { - if (js_event.value) - buttons_state |= (1 << js_event.number); - else - buttons_state &= ~(1 << js_event.number); + if (js_event.value) + buttons_state |= (1 << js_event.number); + else + buttons_state &= ~(1 << js_event.number); } is much safer since it can't lose sync with the driver. As you would @@ -112,6 +112,7 @@ 2.4 js_event.time ~~~~~~~~~~~~~~~~~ + The time an event was generated is stored in ``js_event.time''. It's a time in miliseconds since ... well, since sometime in the past. This eases the task of detecting double clicks, figuring out if movement of axis and button @@ -144,14 +145,14 @@ For example, while (1) { - while (read (fd, &e, sizeof(struct js_event)) > 0) { - process_event (e); - } - /* EAGAIN is returned when the queue is empty */ - if (errno != EAGAIN) { - /* error */ - } - /* do something interesting with processed events */ + while (read (fd, &e, sizeof(struct js_event)) > 0) { + process_event (e); + } + /* EAGAIN is returned when the queue is empty */ + if (errno != EAGAIN) { + /* error */ + } + /* do something interesting with processed events */ } One reason for emptying the queue is that if it gets full you'll start @@ -219,6 +220,7 @@ #ifdef JS_VERSION #if JS_VERSION > 0xsomething + 4.2 JSIOCGNAME ~~~~~~~~~~~~~~ @@ -232,6 +234,7 @@ strncpy(name, "Unknown", sizeof(name)); printf("Name: %s\n", name); + 4.3 JSIOC[SG]CORR ~~~~~~~~~~~~~~~~~ @@ -266,10 +269,10 @@ struct JS_DATA_TYPE js; while (1) { - if (read (fd, &js, JS_RETURN) != JS_RETURN) { - /* error */ - } - usleep (1000); + if (read (fd, &js, JS_RETURN) != JS_RETURN) { + /* error */ + } + usleep (1000); } As you can figure out from the example, the read returns immediately, @@ -296,8 +299,9 @@ center, and 255 maximum value. The v0.8.0.2 driver also had an interface for 'digital joysticks', (now -called Multisystem joystick in this driver), under /dev/djsX. This driver +called Multisystem joysticks in this driver), under /dev/djsX. This driver doesn't try to be compatible with that interface. + 6. Final Notes ~~~~~~~~~~~~~~ diff -u --recursive --new-file v2.2.13/linux/Documentation/joystick-parport.txt linux/Documentation/joystick-parport.txt --- v2.2.13/linux/Documentation/joystick-parport.txt Tue Dec 1 19:05:05 1998 +++ linux/Documentation/joystick-parport.txt Tue Jan 4 10:12:10 2000 @@ -1,6 +1,7 @@ Linux Joystick parport drivers v1.2 BETA - (c) 1998 Vojtech Pavlik + (c) 1998-1999 Vojtech Pavlik (c) 1998 Andree Borrmann + Sponsored by SuSE ---------------------------------------------------------------------------- 0. Disclaimer @@ -44,7 +45,8 @@ The main problem with PC parallel ports is that they don't have +5V power source on any of their pins. So, if you want a reliable source of power for your pads, use either keyboard or joystick port, and make a pass-through -cable. +cable. You can also pull the power directly from the power supply (the red +wire is +5V). If you want to use the parallel port only, you can take the power is from some data pin. For most gamepad and parport implementations only one pin is @@ -298,27 +300,29 @@ The PSX controller is supported by the joy-console.c. -Pinout of the PSX controller: +Pinout of the PSX controller (compatible with DirectPadPro): +---------+---------+---------+ 9 | o o o | o o o | o o o | 1 parallel \________|_________|________/ port pins | | | | | | - | | | | | +--------> Clock --- (1) - | | | | +------------> Select --- (17) - | | | +---------------> Power --- (16) + | | | | | +--------> Clock --- (4) + | | | | +------------> Select --- (3) + | | | +---------------> Power --- (5-9) | | +------------------> Ground --- (18-25) - | +-------------------------> Command --- (14) + | +-------------------------> Command --- (2) +----------------------------> Data --- (10,11,12,13,15) one only... You may have to add pull up/down resistors. Maybe your pad also won't like the 5V (PSX uses 3.7V). - Currently the driver supports only ONE psx pad and only one type of -controller: The normal PSX controller. NEGCON support is planned for the -next release. ANALOG controller may be too (I do not recommend to connect -the "force feedback"/"rumble pack" version... it (may) use too much -power...) + Currently the driver supports only one psx pad per parallel port, and these +controllers: + + * Standard PSX Pad + * NegCon PSX Pad + * Analog PSX Pad (red mode) + * Analog PSX Pad (green mode) 2.4 Sega ~~~~~~~~ @@ -437,13 +441,15 @@ 4 | Multisystem 1-button joystick 5 | Multisystem 2-button joystick 6 | Sony PSX controller + 7 | N64 pad + 8 | N64 pad with direction pad as buttons (DirectPadPro style) The exact type of the PSX controller type is autoprobed, so you must have your controller plugged in before initializing. Should you want to use more than one of parallel ports at once, you can -use js_console_2 and js_db9_3 as additional command line parameters for two -more parallel ports. +use js_console_2 and js_console_3 as additional command line parameters for +two more parallel ports. Changes: v0.1 : First version (SNES only) @@ -463,6 +469,9 @@ v0.10 : Fixed PSX buttons 8 and 9 v0.11V: Switched to EXCL mode Removed wakeup + v0.12V: Added N64 support + v0.13V: Updated N64 support + v0.14V: Fixed N64 axis/button counts 3.2 joy-db9.c ~~~~~~~~~~~~~ @@ -475,6 +484,10 @@ is connected to (eg. 0x378), or, if you are using the parport driver of 2.1+ Linux kernels, the number of the parport interface (eg. 0 for parport0). + Caveat here: This driver only works on bidirectional parallel ports. If +your parallel port is recent enough, you should have no trouble with this. +Old parallel ports may not have this feature. + 'Type' is the type of joystick or pad attached: Type | Joystick/Pad @@ -484,9 +497,10 @@ 2 | Multisystem 2-button joystick 3 | Genesis pad (3+1 buttons) 5 | Genesis pad (5+1 buttons) - 6 | Genesis pad (6+1 buttons) - 7 | Saturn pad + 6 | Genesis pad (6+2 buttons) + 7 | Saturn pad (8 buttons) 8 | Multisystem 1-button joystick (v0.8.0.2 pin-out) + 9 | Two Multiststem 1-button joysticks (v0.8.0.2 pin-out) Should you want to use more than one of these joysticks/pads at once, you can use js_db9_2 and js_db9_3 as additional command line parameters for two @@ -501,6 +515,8 @@ v0.4V: Switched to EXCL mode Removed wakeup v0.5V: Added 0.8.0.2 HW compatibility for Multi sticks + v0.6V: Better timing for Genesis 6 + v0.7V: Added 0.8.0.2 second joystick support 3.3 joy-turbografx.c ~~~~~~~~~~~~~~~~~~~~ @@ -519,6 +535,27 @@ use js_tg_2 and js_tg_3 as additional command line parameters for two more interfaces. -3.4 End +3.4 PC parallel port pinout +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + .----------------------------------------. + At the PC: \ 13 12 11 10 9 8 7 6 5 4 3 2 1 / + \ 25 24 23 22 21 20 19 18 17 16 15 14 / + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Pin | Name | Description + ~~~~~~|~~~~~~~~~|~~~~~~~~~~ + 1 | /STROBE | Strobe + 2-9 | D0-D7 | Data Bit 0-7 + 10 | /ACK | Acknowledge + 11 | BUSY | Busy + 12 | PE | Paper End + 13 | SELIN | Select In + 14 | /AUTOFD | Autofeed + 15 | /ERROR | Error + 16 | /INIT | Initialize + 17 | /SEL | Select + 18-25 | GND | Signal Ground + +3.5 End ~~~~~~~ That's all, folks! Have fun! diff -u --recursive --new-file v2.2.13/linux/Documentation/joystick.txt linux/Documentation/joystick.txt --- v2.2.13/linux/Documentation/joystick.txt Tue Dec 1 19:05:05 1998 +++ linux/Documentation/joystick.txt Tue Jan 4 10:12:10 2000 @@ -1,5 +1,6 @@ - Linux Joystick driver v1.2.13 - (c) 1996-1998 Vojtech Pavlik + Linux Joystick driver v1.2.15 + (c) 1996-1999 Vojtech Pavlik + Sponsored by SuSE ---------------------------------------------------------------------------- 0. Disclaimer @@ -19,7 +20,7 @@ Temple Place, Suite 330, Boston, MA 02111-1307 USA Should you need to contact me, the author, you can do so either by e-mail -- mail your message to , or by paper mail: Vojtech Pavlik, +- mail your message to , or by paper mail: Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic For your convenience, the GNU General Public License version 2 is included @@ -38,18 +39,18 @@ In addition to these it also supports some of the new PC joysticks that use proprietary digital protocols to communicate over the gameport, -currently by FPGaming, Genius, Gravis, Logitech, MadCatz, Microsoft and -ThrustMaster. Creative Labs protocol support is still to be done. +currently by FPGaming, Gravis, Logitech, MadCatz, Microsoft, Creative and +ThrustMaster. Saitek protocol support is still to be done. The driver also includes support for many gamepads and joysticks that were used by various non-PC computers and game consoles. These include Multi system joysticks (Atari, Amiga, Commodore, Amstrad), Sega gamepads (Master -System, Genesis, Saturn), Nintendo gamepads (NES, SNES), Sony gamepads (PSX). -Support for N64, Atari Jaguar, Atari 2600, NES FourScore, SNES MultiTap, -PSX NegCon and others might be added later. +System, Genesis, Saturn), Nintendo gamepads (NES, SNES, N64), Sony gamepads +(PSX). Support for Atari Jaguar, Atari 2600, NES FourScore, SNES MultiTap +and others might be added later. Last, but not least there is also native Amiga joystick support for the -Amiga linux port. +Amiga Linux port. Should you encounter any problems while using the driver, or joysticks this driver can't make complete use of, I'm very interested in hearing about @@ -57,21 +58,16 @@ The joystick package is available at the following FTP sites: + ftp://ftp.suse.cz/pub/development/joystick/ ftp://atrey.karlin.mff.cuni.cz/pub/linux/joystick/ - ftp://artax.karlin.mff.cuni.cz/pub/linux/joystick/ - - The joystick driver is also included in the Linux 2.1 kernels: - - ftp://linux.kernel.org/pub/linux/kernel/v2.1/ + ftp://ftp.gts.cz/pub/linux/joystick/ And a homepage of the driver is at: + http://www.suse.cz/development/joystick/ http://atrey.karlin.mff.cuni.cz/~vojtech/joystick/ - http://artax.karlin.mff.cuni.cz/~vojtech/joystick/ - - A mirror of the homepage is at: - http://www.trylinux.com/projects/joystick/ + http://www.linuxgames.com/joystick/ There is also a mailing list for the driver at: @@ -92,7 +88,21 @@ To compile the utilities in the joystick package, and the driver itself, as a standalone module, you first unpack the package, and then edit the Makefile to meet your needs (namely whether are you using versioned -modules). Then you compile it +modules). You will also need an unpacked and configured + + make config + +kernel in + + /usr/src/linux + +Furthermore, if you're using versioned modules, you'll also need + + make dep + +done on the kernel, to create some needed files. + +After that, you compile the joystick driver make @@ -163,6 +173,14 @@ where 'something' is the type of your joystick. See below for more precise explanation. + Alternately you can add the lines + + alias char-major-15 joy-something + options joy-something js_xx=x,x,x,x,... + + to the /etc/conf.modules file, so that the joystick module will be loaded +automatically when the /dev/js devices are accessed. + 2.5 Verifying that it works ~~~~~~~~~~~~~~~~~~~~~~~~~~~ For testing the joystick driver functionality, there is the jstest @@ -197,14 +215,14 @@ calibration using the jstest command, and if you do, you then can save the correction coefficients into a file - jscal -s /dev/js0 > /etc/joystick.cal + jscal -p /dev/js0 > /etc/joystick.cal And add a line to your rc script executing that file source /etc/joystick.cal This way, after the next reboot your joystick will remain calibrated. You -can also add the jscal -s line to your shutdown script. +can also add the jscal -p line to your shutdown script. 3. HW specific driver information @@ -222,7 +240,7 @@ * 2-axis, 4-button joystick * 3-axis, 4-button joystick -* Two 2-axis, 2-button joysticks on an Y-cable +* 4-axis, 4-button joystick For other joystick types (more/less axes, hats, and buttons) support you'll need to specify the types either on the kernel command line or on the @@ -261,6 +279,8 @@ Joystick | 'm' value ---------------------------------------------------- + Simple 2-button 2-axis joystick | 0x0033 + Second simple joystick on Y-cable | 0x00cc Genius Flight2000 F-12 | 0x00f3 Genius Flight2000 F-21 | 0x08f7 Genius Flight2000 F-22 | 0x02ff @@ -268,9 +288,11 @@ Genius MaxFire G-07 | 0xf0f3 Genius PowerStation | 0xf0f3 Laing #1 PC SuperPad | 0xf0f3 + Logitech Wingman | 0x003b Microsoft SideWinder Standard | 0x003b QuickShot QS-201 SuperWarrior | 0x00fb Saitek Megapad XII | 0x30f3 + PC Powerpad Pro | 0x30f3 In case you have one of the joystick in the table below, and it doesn't work with a specific driver in digital mode for some reason, you can use @@ -280,8 +302,7 @@ Joystick | 'm' value ---------------------------------------------------- Gravis GamePad Pro - analog mode | 0x00f3 - Genius Flight2000 F-23 - CHF mode | 0x02ff - Genius Flight2000 F-23 - FCS mode | 0x08f7 + Genius Flight2000 F-23 | 0x02ff Microsoft SideWinder 3D Pro - CHF mode | 0x02ff Microsoft SideWinder 3D Pro - FCS mode | 0x08f7 @@ -302,70 +323,85 @@ And it would do the same as the above explained command line. Use whichever way you like best. -3.2 Microsoft SideWinder and Genius Digital joysticks -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - SideWinder and Genius Digital joysticks are supported by the +3.2 Microsoft SideWinder joysticks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Microsoft 'Digital Overdrive' protocol is supported by the joy-sidewinder.c module. All currently supported joysticks: -* SideWinder 3d Pro -* SideWinder Precision Pro +* SideWinder 3D Pro * SideWinder Force Feedback Pro -* SideWinder Game Pad (up to four, chained together) -* Genius Flight2000 Digital F-23 +* SideWinder Force Feedback Wheel +* SideWinder FreeStyle Pro +* SideWinder GamePad (up to four, chained together) +* SideWinder Precision Pro are autodetected, and thus no module parameters are needed. + There is one caveat with the 3D Pro. There are 9 buttons reported, +although the joystick has only 8. The 9th button is the mode switch on the +rear side of the joystick. However, moving it, you'll reset the joystick, +and make it unresponsive for about a one third of a second. Furthermore, the +joystick will also re-center itself, taking the position it was in during +this time as a new center position. Use it if you want, but think first. + The SideWinder Standard is not a digital joystick, and thus is supported -by the analog driver described above. SideWinder FreeStyle Pro and -SideWinder Force Feedback Wheel are not supported yet. +by the analog driver described above. -3.3 Logitech Digital joysticks -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Logitech Digital joysticks are supported by the joy-logitech.c module. It -currently supports these devices: +3.3 Logitech ADI devices +~~~~~~~~~~~~~~~~~~~~~~~~ + Logitech ADI protocol is supported by the joy-logitech.c module. It should +support any Logitech device using this protocol. This includes, but is not +limited to: -* Logitech Wingman Extreme Digital * Logitech CyberMan 2 * Logitech ThunderPad Digital - - All three are autodetected, and thus no parameters to the module are -needed. - - Logitech Wingman is not a digital joystick and is handled by the analog -driver described above. Logitech Wingman Warrior communicates through a -serial port and is not supported yet. Logitech Wingman Force, Wingman -Formula, Wingman Formula Force, Wingman Gamepad, Wingman Interceptor are USB -joysticks, with optional serial port connection, and are not supported yet. +* Logitech WingMan Extreme Digital +* Logitech WingMan Formula +* Logitech WingMan Interceptor +* Logitech WingMan GamePad +* Logitech WingMan GamePad USB +* Logitech WingMan GamePad Extreme +* Logitech WingMan Extreme Digital 3D + + ADI devices are autodetected, and the driver supports up to two (any +combination of) devices on a single gameport, using an Y-cable or chained +together. + + Logitech WingMan Joystick, Logitech WingMan Attack, Logitech WingMan +Extreme and Logitech WingMan ThunderPad are not digital joysticks and are +handled by the analog driver described above. Logitech WingMan Warrior and +Logitech Magellan are supported by serial drivers described below. Logitech +CyberMan, Logitech WingMan Force and Logitech WingMan Formula Force are not +supported yet. 3.4 Gravis GrIP ~~~~~~~~~~~~~~~ - Gravis GrIP gamepads are supported by the joy-gravis.c module. It + Gravis GrIP protocol is supported by the joy-gravis.c module. It currently supports: * Gravis GamePad Pro * Gravis Xterminator -* Gravis Blackhawk Digital +* Gravis BlackHawk Digital - All these pads are autodetected, and you can even use any combination of -up to two of these pads either chained together or using an Y-cable on a single -gameport. - -GrIP MultiPort support is in the works. Gravis Xcalibur, ArcadeXtreme, -GamePad Pro/M are joysticks/pads that probably never reached mass -production. Gravis Stinger is a serial device and hopefully will be -supported in the future. + All these devices are autodetected, and you can even use any combination +of up to two of these pads either chained together or using an Y-cable on a +single gameport. + +GrIP MultiPort and Gravis Xterminator DualControl aren't supported yet. +Gravis Stinger is a serial device and hopefully will be supported in the +future. Other Gravis joysticks are supported by the joy-analog driver. 3.5 FPGaming A3D and MadCatz A3D ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - The Assasin 3D protocol created by FPGaming, is used both by FPGaming + The Assassin 3D protocol created by FPGaming, is used both by FPGaming themselves and is licensed to MadCatz. A3D devices are supported by the -joy-assasin.c module. It currently supports: +joy-assassin.c module. It currently supports: -* FPGaming Assasin 3D +* FPGaming Assassin 3D * MadCatz Panther * MadCatz Panther XL - All these devices are autodetected. Because the Assasin 3D and the Panther + All these devices are autodetected. Because the Assassin 3D and the Panther allow connecting analog joysticks to them, these are supported in this driver, too. The driver uses the js_as parameter for the analog joysticks, which has the same syntax as js_an for the analog driver. @@ -376,20 +412,33 @@ 3.6 ThrustMaster DirectConnect (BSP) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The TM DirectConnect (BSP) protocol is supported by the joy-thrustmaster.c -module. It currently supports: +module. This includes, but is not limited to: * ThrustMaster Millenium 3D Inceptor * ThrustMaster 3D Rage Pad +* ThrustMaster Fusion Digital Game Pad - Both these drvices are autodetected, and thus no parameters to the module + Devices not directly supported, but hopefully working are: + +* ThrustMaster FragMaster +* ThrustMaster Attack Throttle + + If you have one of these, contact me. + + BSP devices are autodetected, and thus no parameters to the module are needed. - The Millenium and Rage Pad should work fine now. TM WCS III won't work, -because important parts of code for that are missing. I'm not sure if it was -ever mass produced. +3.7 Creative Labs Blaster +~~~~~~~~~~~~~~~~~~~~~~~~~ + The Blaster protocol is supported by the joy-creative.c module. It +currently supports only the: + +* Creative Blaster GamePad Cobra + + Up to two of these can be used on a single gameport, using an Y-cable. -3.7 PDPI Lightning 4 gamecards -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +3.8 PDPI Lightning 4 gamecards +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PDPI Lightning 4 gamecards are supported by the joy-lightning.c module. This driver is only for analog joysticks connected to the card - if you want to use some of the digital devices, you need to use its specific driver. The @@ -420,8 +469,49 @@ See the description of analog joystick driver for explanations of m0 and n0 values. -3.8 Amiga -~~~~~~~~~ +3.9 Trident 4DWave / Aureal Vortex +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Soundcards with a Trident 4DWave DX/NX or Aureal Vortex chipset provide an +"Enhanced Game Port" mode where the soundcard handles polling the joystick. +This mode is supported by the joy-pci module. + + If no module parameters are given, the joy-pci module will set all the +soundcards it finds to "enhanced" mode, and will try to autodetect the type +of attached joystick. It can only detect the same types of joysticks that +the joy-analog module can. + + This module accepts parameters in the form: + + js_pci=t0,i0,m0,n0,t1,i1,m1,n1,.... + + The "t" value specifies the type of card, as follows: + + t | Card Type + ---------------------------- + 0 | Trident 4DWave DX + 1 | Trident 4DWave NX + 2 | Aureal Vortex1 (Au8820 chipset) + 3 | Aureal Vortex2 (Au8830 chipset) + + If you have more than one card of the same type, the "i" parameter lets +you choose which card to apply the "m" and "n" values to. It counts from +"0". (The driver detects cards in the order listed in the above table.) + + The "m" and "n" values have the same meaning as for the analog module, +with the exception that the value m=0, n=0 indicates that joy-pci should +completely ignore that port. This can be useful to reserve a certain port +for purely MIDI operation. + + For example, let's say you have 3 sound cards - a 4Dwave DX, a 4DWave NX, +and a Vortex 2. You have a three-axis, four-button, one-hat CHF- compatible +joystick on the DX. You use the NX to interface to an external MIDI device. +Finally, you have two two-axis, two-button joysticks on the Vortex. Your +command line might look like: + + js_pci=0,0,0x207,0,1,1,0,0,3,0,0x33,0xcc + +3.10 Amiga +~~~~~~~~~~ Amiga joysticks, connected to an Amiga, are supported by the joy-amiga.c driver. Since they can't be autodetected, the driver has a command line. @@ -438,10 +528,75 @@ No more joystick types are supported now, but that should change in the future if I get an Amiga in the reach of my fingers. -3.9 Game console and 8-bit pads and joysticks -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +3.11 Game console and 8-bit pads and joysticks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ See joystick-parport.txt for more info. +3.12 SpaceTec/LabTec devices +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + SpaceTec serial devices communicate using the SpaceWare protocol. It is +supported by the joy-spaceorb and joy-spaceball drivers. The devices currently +supported by joy-spaceorb are: + +* SpaceTec SpaceBall Avenger +* SpaceTec SpaceOrb 360 + +Devices currently supported by joy-spaceball are: + +* SpaceTec SpaceBall 4000 FLX + + In addition to having the joy-spaceorb/spaceball module in the kernel, you +also need to attach a serial port to it. to do that, run the jsattach +program: + + jsattach --spaceorb /dev/ttySx & +or + jsattach --sball4 /dev/ttySx & + +where /dev/ttySx is the serial port which the device is connected to. After +doing this, the device will be reported and will start working. + + There is one caveat with the SpaceOrb. The button #6, the on the bottom +side of the orb, although reported as an ordinary button, causes internal +recentering of the spaceorb, moving the zero point to the position in which +the ball is at the moment of pressing the button. So, think first before +you bind it to some other function. + +SpaceTec SpaceBall 2003 FLX and 3003 FLX are not supported yet. + +3.13 Logitech SWIFT devices +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + The SWIFT serial protocol is supported by the joy-warrior module. It +currently supports only the: + +* Logitech WingMan Warrior + +but in the future, Logitech CyberMan (the original one, not CM2) could be +supported as well. To use the module, you need to run jsattach after you +insert/compile the module into your kernel: + + jsattach --warrior /dev/ttySx & + +ttySx is the serial port your Warrior is attached to. + +3.14 Magellan / Space Mouse +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + The Magellan (or Space Mouse), manufactured by LogiCad3d (formerly Space +Systems), for many other companies (Logitech, HP, ...) is supported by the +joy-magellan module. It currently supports only the: + +* Magellan 3D +* Space Mouse + +models, the additional buttons on the 'Plus' versions are not supported yet. + + To use it, you need to attach the serial port to the driver using the + + jsattach --magellan /dev/ttySx & + +command. After that the Magellan will be detected, initialized, will beep, +and the /dev/jsX device should become useable. + 4. Troubleshooting ~~~~~~~~~~~~~~~~~~ There is quite a high probability that you run into some problems. For @@ -452,9 +607,10 @@ jstest --normal /dev/js0 jstest --old /dev/js0 - If your trouble stems from the fact the drivers can't detect the attached -joystick, and/or you decide you need my help (which I will gladly provide), -please use the joydump utility first. It's created just by typing + If your trouble stems from the fact the drivers can't detect the joystick +attached to your gameport, and you decide you need my help (which I will +gladly provide), please use the joydump utility first. It's created just by +typing make joydump.o @@ -465,64 +621,73 @@ in the same directory. It will return a 'device busy' or 'initialization failed' error. This is perfectly okay. It has already done it's job. The -results can be found in the system log. Please send me the results along -with your problem report. +results can be found in the system log or in the output of the + + dmesg + +command. Please send me the results along with your problem report. Oh, and read the FAQ! :) 5. FAQ ~~~~~~ - Q: The driver doesn't find any joysticks connected to my soundcard with the -message "joy-something: no joysticks found" and "joy-something.o: -init_module: Device or resource busy." or "Initialization of joy-something -failed" What could be the cause? - A: The most common cause is that the joystick port on your soundcard is -not enabled. If it is an ISA PnP card, you'll need isapnptools to configure -the gameport. Non-PnP cards usually use some option to the sound driver - -see the sound driver docs and source and enable the port. - - Q: Any access to the joystick devices gives me "Operation not supported by -device". What am I doing wrong? - A: You're running a 2.0 kernel and you forgot to insmod the hardware -specific module. You not only need the joystick.o, but also one of the other -joy-*.o files (most usually joy-analog.o), as described in this document, -section 2. If you are not using modules, then you didn't say 'Y' to any of -the hardware-specific questions. Again, see section 2. If you did select -the specific support, and you still get this message, check that you -selected the right one, and if it still doesn't work, go to the previous -FAQ. - - Q: Everything is fine, except I get "No such device" error when I try to -do anything with /dev/js0. What's the cause? - A: You're running a 2.1 kernel and you want to read the previous FAQ. - - Q: Upon 'insmod joystick.o' I get a LOT of unresolved symbols, including -printk and others. Why? - A: You either don't have your kernel compiled with module support. If -that's the cause, re-compile your kernel with module support switched on. -Or, you use versioned symbols, and don't have -DMODVERSIONS in the joystick -driver Makefile, or vice versa. Correct the situation by either removing or -adding -DMODVERSIONS to the Makefile. - - Q: Running 'jstest 1' or 'jscal 1' doesn't work, and returns with "File -not found" error. What is the problem? - A: The command line interface for these tools is different from what -version 0.8.0 used. You have to specify the whole device name, eg. 'jstest -/dev/js0'. - - Q: Running 'jstest /dev/js0' results in "File not found" error. What's the -cause? - A: The device files don't exist. Run 'make devs'. - - Q: Is it possible to connect my old Atari/Commodore/Amiga/console joystick -or pad that uses a 9-pin D-type cannon connector to the serial port of my -PC? - A: Yes, it is possible, but it'll burn your serial port or the pad. It -won't work, of course. - - Q: My joystick doesnt work with Quake / Quake 2. What's the cause? - A: Quake / Quake 2 don't support joystick. Use joy2key to simulate keypresses -for them. +Q: The driver doesn't find any joysticks connected to my soundcard with the + message "joy-something: no joysticks found" and "joy-something.o: + init_module: Device or resource busy." or "Initialization of joy-something + failed" What could be the cause? +A: The most common cause is that the joystick port on your soundcard is + not enabled. If it is an ISA PnP card, you'll need isapnptools to configure + the gameport. Non-PnP cards usually use some option to the sound driver - + see the sound driver docs and source and enable the port. Note that in case + of a PnP card you have to load the joystick driver as a module after running + the isapnp command, it will not work in the opposite order. + +Q: Any access to the joystick devices gives me "Operation not supported by + device". What am I doing wrong? +A: You're running a 2.0 kernel and you forgot to insmod the hardware + specific module. You not only need the joystick.o, but also one of the other + joy-*.o files (most usually joy-analog.o), as described in this document, + section 2. If you are not using modules, then you didn't say 'Y' to any of + the hardware-specific questions. Again, see section 2. If you did select + the specific support, and you still get this message, check that you + selected the right one, and if it still doesn't work, go to the previous + FAQ. + +Q: Everything is fine, except I get "No such device" error when I try to + do anything with /dev/js0. What's the cause? +A: You're running a 2.1 or 2.2. kernel and you want to read the previous FAQ. + +Q: Upon 'insmod joystick.o' I get a LOT of unresolved symbols, including + 'printk' and others. Why? +A: You either don't have your kernel compiled with module support. If + that's the cause, re-compile your kernel with module support switched on. + Or, you use versioned symbols, and don't have -DMODVERSIONS in the joystick + driver Makefile, or vice versa. Correct the situation by either removing or + adding -DMODVERSIONS to the Makefile. + +Q: Upon 'insmod joy-something' I get a bunch of unresolved symbols, like + 'js_register_port, js_unregister device' and others. What's wrong? +A: You need to 'insmod joystick.o' first. + +Q: Running 'jstest 1' or 'jscal 1' doesn't work, and returns with "File + not found" error. What is the problem? +A: The command line interface for these tools is different from what + version 0.8.0 used. You have to specify the whole device name, eg. 'jstest + /dev/js0'. + +Q: Running 'jstest /dev/js0' results in "File not found" error. What's the + cause? +A: The device files don't exist. Run 'make devs'. + +Q: Is it possible to connect my old Atari/Commodore/Amiga/console joystick + or pad that uses a 9-pin D-type cannon connector to the serial port of my + PC? +A: Yes, it is possible, but it'll burn your serial port or the pad. It + won't work, of course. + +Q: My joystick doesn't work with Quake / Quake 2. What's the cause? +A: Quake / Quake 2 don't support joystick. Use joy2key to simulate keypresses + for them. 6. Programming Interface ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -565,23 +730,31 @@ Brian Gerst Andree Borrmann Martin Giguere + David Thompson + Justin Wake + Benoit Triquet + John Dahlstrom + Dan Gohman + Joe Krahn + David Kuder + Raymond Ingles If you think you should be in this list and are not, it's possible that I forgot to include you - contact me and I'll correct the error. :) Thanks to KYE Systems Europe, who provided me with driver sources for the -Genius Flight2000 Digital F-23, which happens to be compatible with -Microsoft SideWinder 3d Pro. +Genius Flight2000 Digital F-23, which happens to be identical (in software) +to Microsoft SideWinder 3D Pro. Thanks to ThrustMaster Inc. who provided me with docs for their digital -protocol, and to Trystan A Larey-williams , who wrote an -attempt of a driver for them. +protocol specifications, and to Trystan A Larey-Williams , +who wrote an attempt of a driver for them. Thanks to Creative Labs Europe, and Ifor Powell , -who provided me with docs for their first generation gamepad. +who provided me with docs for their first generation Blaster GamePad. Special thanks go to FP-Gaming, Inc. and James C Barnes , -who provided me with help and detailed information about the Assasin 3D +who provided me with help and detailed information about the Assassin 3D protocol and devices, and even sent me a Panther and Panther XL for testing, along with cool T-shirts. @@ -590,10 +763,20 @@ code for their L4 gamecard, and sending me the card to test my driver with it. + Thanks to LogiCad3D for their support, for having the specifications +online and for the nice music on their telephone. + + Special thanks to Logitech, Jerry de Raad , +Thomas Burgel , Avinash Shinde + for providing me with a lot of documentation +for their devices, and also for a big box, containing a CyberMan2, Wingman +Extreme, Magellan, Wingman Warrior, two MouseMan mice, and a NewTouch +keyboard. + Thanks to everyone else who helped me develop this package of drivers! - No thanks to Microsoft, Logitech, and Gravis, who don't release a word -about their hardware .... :( + No thanks to Microsoft and Gravis, who don't release a word about their +hardware .... :( 8. ChangeLog ~~~~~~~~~~~~ @@ -602,50 +785,3 @@ 9. To do ~~~~~~~~ See the TODO file for the list of things planned. - -10. Current driver status -~~~~~~~~~~~~~~~~~~~~~~~~~ - OK means tested and not touched till this driver revision, unknown means -that the driver was changed since last test, broken means doesn't work, -incomplete means can't work, because vital parts of support are missing. - -joystick.c: 2.1.x kernel interface - OK -joy-amiga.c: Multi1 stick - unknown -joy-analog.c: standard joysticks - OK - FCS hats - OK - CHF hats & buttons - OK - XY buttons - OK - UV buttons - OK -joy-assasin.c: MadCatz Panther XL - OK - MadCatz PXL rudder - OK - MadCatz Panther - OK - FPG Assasin 3D - OK -joy-console.c: NES pad - OK - SNES pad - OK - Multi1 stick - OK - Multi2 stick - OK - PSX - SW OK, HW unreliable -joy-db9.c: Multi1 stick - OK - Multi2 stick - OK - Sega Genesis pad - OK - Sega Genesis 5 pad - OK - Sega Genesis 6 pad - OK - Sega Saturn pad - unknown -joy-gravis.c Gravis GamePad Pro - OK - Gravis Xterminator - OK - Gravis Blackhawk Digital- OK -joy-lightning.c PDPI Lightning 4 - OK -joy-logitech.c WingMan Extreme Digital - OK - CyberMan 2 - OK - Thunder Pad Digital - unknown -joy-sidewinder.c SW 3D Pro - OK - Genius F-23 - OK - SW GP - OK - SW PP - OK - SW FFP - OK -joy-thrustmaster.c Millenium 3D Inceptor - OK - 3D-Rage Pad - OK -joy-turbografx.c Multi1 stick - OK - -Please help me and send me success / failure reports for the drivers, -I need to know what works, and what needs to be debugged. Thank you. diff -u --recursive --new-file v2.2.13/linux/Documentation/kernel-docs.txt linux/Documentation/kernel-docs.txt --- v2.2.13/linux/Documentation/kernel-docs.txt Mon May 10 13:00:10 1999 +++ linux/Documentation/kernel-docs.txt Tue Jan 4 10:12:10 2000 @@ -1,15 +1,14 @@ - - INDEX OF DOCUMENTATION FOR PEOPLE INTERESTED IN WRITING AND/OR UNDERSTANDING - THE LINUX KERNEL. - - Juan-Mariano de Goyeneche - - - /* - * The latest version of this document may be found at: - * http://www.dit.upm.es/~jmseyas/linux/kernel/hackers-docs.html - */ + Index of Documentation for People Interested in Writing and/or + + Understanding the Linux Kernel. + + Juan-Mariano de Goyeneche < jmseyas@dit.upm.es> + +/* + * The latest version of this document may be found at: + * http://www.dit.upm.es/~jmseyas/linux/kernel/hackers-docs.html + */ The need for a document like this one became apparent in the linux-kernel mailing list as the same questions, asking for pointers @@ -31,301 +30,409 @@ corrections, ideas or comments are also welcomed. The papers that follow are listed in no particular order. All are - catalogued with the following fields: the document's "Title", the - "Author"/s, the "URL" where they can be found, some "Keywords" - helpful when searching for specific topics, and a brief "Description" - of the Document. + cataloged with the following fields: the document's "Title", the + "Author"/s, the "URL" where they can be found, some "Keywords" helpful + when searching for specific topics, and a brief "Description" of the + Document. Enjoy! - - ON-LINE DOCS: - - + Title: "The Linux Kernel" - Author: David A. Rusling. - URL: http://sunsite.unc.edu/linux/LDP/tlk/tlk.html - Keywords: everything!, book. - Description: On line, 200 pages book describing most - aspects of the Linux Kernel. Probably, the first reference - for beginners. Lots of illustrations explaining data - structures use and relationships in the purest Richard W. - Stevens' style. Contents: "1.-Hardware Basics, 2.-Software - Basics, 3.-Memory Management, 4.-Processes, 5.-Interprocess - Communication Mechanisms, 6.-PCI, 7.-Interrupts and Interrupt - Handling, 8.-Device Drivers, 9.-The File system, - 10.-Networks, 11.-Kernel Mechanisms, 12.-Modules, 13.-The - Linux Kernel Sources, A.-Linux Data Structures, B.-The Alpha - AXP Processor, C.-Useful Web and FTP Sites, D.-The GNU - General Public License, Glossary". In short: a must have. - - + Title: "The Linux Kernel Hackers' Guide" - Author: Michael K.Johnson and others. - URL: http://www.redhat.com:8080/HyperNews/get/khg.html - Keywords: everything! - Description: No more Postscript book-like version. Only - HTML now. Many people have contributed. The interface is - similar to web available mailing lists archives. You can find - some articles and then some mails asking questions about them - and/or complementing previous contributions. A little bit - anarchic in this aspect, but with some valuable information - in some cases. - - + Title: "Tour Of the Linux Kernel Source" - Author: Vijo Cherian. - URL: http://www.svrec.ernet.in/~vijo/tolks/tolks.html - Keywords: - Description: The name says it all. A tour of the sources, - describing directories, files, variables, data structures... - It covers general stuff, device drivers, filesystems, IPC and - Network Code. - - + Title: "Overview of the Virtual File System" - Author: Richard Gooch. - URL: http://www.atnf.csiro.au/~rgooch/linux/vfs.txt - Keywords: VFS, File System, mounting filesystems, opening - files, dentries, - dcache. Description: Brief introduction to the Linux - Virtual File System. What is it, how it works, operations - taken when opening a file or mounting a file system and - description of important data structures explaining the - purpose of each of their entries. - - + Title: "The Linux RAID-1, 4, 5 Code" - Author: Ingo Molnar, Gadi Oxman and Miguel de Icaza. - URL: http://www.ssc.com/lj/issue44/2391.html - Keywords: RAID, MD driver. - Description: Linux Journal Kernel Korner article. Here is - it's abstract: "A description of the implementation of the - RAID-1, RAID-4 and RAID-5 personalities of the MD device - driver in the Linux kernel, providing users with high - performance and reliable, secondary-storage capability using - software". - - + Title: "Dynamic Kernels: Modularized Device Drivers" - Author: Alessandro Rubini. - URL: http://www.ssc.com/lj/issue23/1219.html - Keywords: device driver, module, loading/unloading modules, - allocating - resources. Description: Linux Journal Kernel Korner - article. Here is it's abstract: "This is the first of a - series of four articles co-authored by Alessandro Rubini and - Georg Zezchwitz which present a practical approach to writing - Linux device drivers as kernel loadable modules. This - installment presents an introduction to the topic, preparing - the reader to understand next month's installment". - - + Title: "Dynamic Kernels: Discovery" - Author: Alessandro Rubini. - URL: http://www.ssc.com/lj/issue24/kk24.html - Keywords: character driver, init_module, clean_up module, - autodetection, - mayor number, minor number, file operations, open(), close(). - Description: Linux Journal Kernel Korner article. Here is - it's abstract: "This article, the second of four, introduces - part of the actual code to create custom module implementing - a character device driver. It describes the code for module - initialization and cleanup, as well as the open() and close() - system calls". - - + Title: "The Devil's in the Details" - Author: Georg v. Zezschwitz and Alessandro Rubini. - URL: http://www.ssc.com/lj/issue25/kk25.html - Keywords: read(), write(), select(), ioctl(), blocking/non - blocking mode, - interrupt handler. Description: Linux Journal Kernel Korner - article. Here is it's abstract: "This article, the third of - four on writing character device drivers, introduces concepts - of reading, writing, and using ioctl-calls". - - + Title: "Dissecting Interrupts and Browsing DMA" - Author: Alessandro Rubini and Georg v. Zezschwitz. - URL: http://www.ssc.com/lj/issue26/interrupt.html - Keywords: interrupts, irqs, DMA, bottom halves, task - queues. - Description: Linux Journal Kernel Korner article. Here is - it's abstract: "This is the fourth in a series of articles - about writing character device drivers as loadable kernel - modules. This month, we further investigate the field of - interrupt handling. Though it is conceptually simple, - practical limitations and constraints make this an - ``interesting'' part of device driver writing, and several - different facilities have been provided for different - situations. We also investigate the complex topic of DMA". - - + Title: "Network Buffers And Memory Management" - Author: Alan Cox. - URL: http://www.ssc.com/lj/issue30/kk30.html - Keywords: sk_buffs, network devices, protocol/link layer - variables, network - devices flags, transmit, receive, configuration, multicast. - Description: Linux Journal Kernel Korner. Here is the - abstract: "Writing a network device driver for Linux is - fundamentally simple---most of the complexity (other than - talking to the hardware) involves managing network packets in - memory". - - + Title: "An Introduction to the Linux 1.3.x Networking Code" - Author: Vipul Gupta. - URL: - http://anchor.cs.binghamton.edu/courses/cs628/linux-net.html - Keywords: files, sk_buffs. - Description: A short description of files under the net/ - directory. Each file has a one- or two-line paragraph to - describe it. Also, sk_buffs is explained with some - beautiful pictures. A little bit outdated. - - + Title: "Linux ioctl() Primer" - Author: Vipul Gupta. - URL: - http://anchor.cs.binghamton.edu/courses/cs628/ioctl.html - Keywords: ioctl, socket. - Description: Little description and examples on the use and - implementation of the ioctl() system call. A little bit - biased towards sockets. - - + Title: "Writing Linux Device Drivers" - Author: Michael K. Johnson. - URL: http://www.redhat.com/~johnsonm/devices.html - Keywords: files, VFS, file operations, kernel interface, - character vs - block devices, I/O access, hardware interrupts, DMA, access - to user memory, memory allocation, timers. Description: - Introductory 50-minutes (sic) tutorial on writing device - drivers. 12 pages written by the same author of the "Kernel - Hackers' Guide" which give a very good overview of the topic. - - + Title: "The Venus kernel interface" - Author: Peter J. Braam. - URL: - http://www.coda.cs.cmu.edu/doc/html/kernel-venus-protocol.html - Keywords: coda, filesystem, venus, cache manager. - Description: "This document describes the communication - between Venus and kernel level file system code needed for - the operation of the Coda filesystem. This version document - is meant to describe the current interface (version 1.0) as - well as improvements we envisage". - - + Title: "Programming PCI-Devices under Linux" - Author: Claus Schroeter. - URL: - ftp://ftp.llp.fu-berlin.de/pub/linux/LINUX-LAB/whitepapers/pc - ip.ps.gz - Keywords: PCI, device, busmastering. - Description: 6 pages tutorial on PCI programming under - Linux. Gives the basic concepts on the architecture of the - PCI subsystem, as long as basic functions and macros to - read/write the devices and perform busmastering. - - + Title: "Writing Character Device Driver for Linux" - Author: R. Baruch and C. Schroeter. - URL: - ftp://ftp.llp.fu-berlin.de/pub/linux/LINUX-LAB/whitepapers/dr - ivers.ps.gz - Keywords: character device drivers, I/O, signals, DMA, - accessing ports in user space, kernel environment. - Description: 68 pages paper on writing character drivers. A - little bit old (1.993, 1.994) although still useful. - - - - * BOOKS: (Not on-line) - - + Title: "Linux Device Drivers" - Author: Alessandro Rubini. - Publisher: O'Reilly &Associates. - Date: 1998. - ISBN: 1-56592-292-1 - - + Title: "Linux Kernel Internals" - Author: Michael Beck. - Publisher: Addison-Wesley. - Date: 1997. - ISBN: 0-201-33143-8 (second edition) - - + Title: "The Design of the UNIX Operating System" - Author: Maurice J. Bach. - Publisher: Prentice Hall. - Date: 1986. - ISBN: ??? - - + Title: "The Design and Implementation of the 4.3 BSD UNIX - Operating System" - Author: Samuel J. Leffler, Marshall Kirk McKusick, Michael - J. Karels, John S. Quarterman. - Publisher: Addison-Wesley. - Date: 1989 (reprinted with corrections on October, 1990). - ISBN: 0-201-06196-1 - - + Title: "The Design and Implementation of the 4.4 BSD UNIX - Operating System" - Author: Marshall Kirk McKusick, Keith Bostic, Michael J. - Karels, John S. Quarterman. - Publisher: Addison-Wesley. - Date: 1996. - ISBN: 0-201-54979-4 - - + Title: "Programmation Linux 2.0 API systeme et - fonctionnement du noyau" - Author: Remy Card, Eric Dumas, Franck Mevel. - Publisher: Eyrolles. - Date: 1997. - Pages: 520. ISBN: 2-212-08932-5 - - + Title: "Unix internals -- the new frontiers" - Author: Uresh Vahalia. - Publisher: Prentice Hall. - Date: 1996. - Pages: 600. ISBN: 0-13-101908-2 - - - * MISCELLANEOUS: + ON-LINE DOCS: - + Name: Linux Source Driver. - URL: http://lsd.linux.cz - Keywords: Browsing. - Description: "Linux Source Driver (LSD) is an application, - which can make browsing source codes of Linux kernel easier - than you can imagine. You can select between multiple - versions of kernel (e.g. 0.01, 1.0.0, 2.0.33, 2.0.34pre13, - 2.0.0, 2.1.101 etc.). With LSD you can search Linux kernel - (fulltext, macros, types, functions and variables) and LSD - can generate patches for you on the fly (files, directories - or kernel)". - - + Name: Linux Weekly News. - URL: http://lwn.net - Keywords: last kernel news. - Description: The title says it all. There's a fixed kernel - section summarizing developers' work, bug fixes, new features - and versions produced during the week. Published every - Thursday. - - + Name: CuTTiNG.eDGe.LiNuX. - URL: http://edge.linuxhq.com - Keywords: changelist. - Description: Site which provides the changelist for every - kernel release. What's new, what's better, what's changed. - Myrdraal reads the patchs and describes them. Pointers to the - patches are there, too. + * Title: "The Linux Kernel" + Author: David A. Rusling. + URL: http://sunsite.unc.edu/linux/LDP/tlk/tlk.html + Keywords: everything!, book. + Description: On line, 200 pages book describing most aspects of + the Linux Kernel. Probably, the first reference for beginners. + Lots of illustrations explaining data structures use and + relationships in the purest Richard W. Stevens' style. Contents: + "1.-Hardware Basics, 2.-Software Basics, 3.-Memory Management, + 4.-Processes, 5.-Interprocess Communication Mechanisms, 6.-PCI, + 7.-Interrupts and Interrupt Handling, 8.-Device Drivers, 9.-The + File system, 10.-Networks, 11.-Kernel Mechanisms, 12.-Modules, + 13.-The Linux Kernel Sources, A.-Linux Data Structures, B.-The + Alpha AXP Processor, C.-Useful Web and FTP Sites, D.-The GNU + General Public License, Glossary". In short: a must have. + + * Title: "The Linux Kernel Hackers' Guide" + Author: Michael K.Johnson and others. + URL: http://khg.redhat.com/HyperNews/get/khg.html + Keywords: everything! + Description: No more Postscript book-like version. Only HTML now. + Many people have contributed. The interface is similar to web + available mailing lists archives. You can find some articles and + then some mails asking questions about them and/or complementing + previous contributions. A little bit anarchic in this aspect, but + with some valuable information in some cases. + + * Title: "Conceptual Architecture of the Linux Kernel" + Author: Ivan T. Bowman. + URL: http://plg.uwaterloo.ca/~itbowman/papers/CS746G-a1.html + Keywords: conceptual software arquitecture, extracted design, + reverse engineering, system structure. + Description: Conceptual software arquitecture of the Linux kernel, + automatically extracted from the source code. Very detailed. Good + figures. Gives good overall kernel understanding. + + * Title: "Concrete Architecture of the Linux Kernel" + Author: Ivan T. Bowman, Saheem Siddiqi, and Meyer C. Tanuan. + URL: http://plg.uwaterloo.ca/~itbowman/papers/CS746G-a2.html + Keywords: concrete arquitecture, extracted design, reverse + engineering, system structure, dependencies. + Description: Concrete arquitecture of the Linux kernel, + automatically extracted from the source code. Very detailed. Good + figures. Gives good overall kernel understanding. This papers + focus on lower details than its predecessor (files, variables...). + + * Title: "Linux as a Case Study: Its Extracted Software + Architecture" + Author: Ivan T. Bowman, Richard C. Holt and Neil V. Brewster. + URL: http://plg.uwaterloo.ca/~itbowman/papers/linuxcase.html + Keywords: software architecture, architecture recovery, + redocumentation. + Description: Paper appeared at ICSE'99, Los Angeles, May 16-22, + 1999. A mixture of the previous two documents from the same + author. + + * Title: "Overview of the Virtual File System" + Author: Richard Gooch. + URL: http://www.atnf.csiro.au/~rgooch/linux/vfs.txt + Keywords: VFS, File System, mounting filesystems, opening files, + dentries, + dcache. Description: Brief introduction to the Linux Virtual File + System. What is it, how it works, operations taken when opening a + file or mounting a file system and description of important data + structures explaining the purpose of each of their entries. + + * Title: "The Linux RAID-1, 4, 5 Code" + Author: Ingo Molnar, Gadi Oxman and Miguel de Icaza. + URL: http://www.ssc.com/lj/issue44/2391.html + Keywords: RAID, MD driver. + Description: Linux Journal Kernel Korner article. Here is it's + abstract: "A description of the implementation of the RAID-1, + RAID-4 and RAID-5 personalities of the MD device driver in the + Linux kernel, providing users with high performance and reliable, + secondary-storage capability using software". + + * Title: "Dynamic Kernels: Modularized Device Drivers" + Author: Alessandro Rubini. + URL: http://www.ssc.com/lj/issue23/1219.html + Keywords: device driver, module, loading/unloading modules, + allocating resources. + Description: Linux Journal Kernel Korner article. Here is it's + abstract: "This is the first of a series of four articles + co-authored by Alessandro Rubini and Georg Zezchwitz which present + a practical approach to writing Linux device drivers as kernel + loadable modules. This installment presents an introduction to the + topic, preparing the reader to understand next month's + installment". + + * Title: "Dynamic Kernels: Discovery" + Author: Alessandro Rubini. + URL: http://www.ssc.com/lj/issue24/kk24.html + Keywords: character driver, init_module, clean_up module, + autodetection, + mayor number, minor number, file operations, open(), close(). + Description: Linux Journal Kernel Korner article. Here is it's + abstract: "This article, the second of four, introduces part of + the actual code to create custom module implementing a character + device driver. It describes the code for module initialization and + cleanup, as well as the open() and close() system calls". + + * Title: "The Devil's in the Details" + Author: Georg v. Zezschwitz and Alessandro Rubini. + URL: http://www.ssc.com/lj/issue25/kk25.html + Keywords: read(), write(), select(), ioctl(), blocking/non + blocking mode, interrupt handler. + Description: Linux Journal Kernel Korner article. Here is it's + abstract: "This article, the third of four on writing character + device drivers, introduces concepts of reading, writing, and using + ioctl-calls". + + * Title: "Dissecting Interrupts and Browsing DMA" + Author: Alessandro Rubini and Georg v. Zezschwitz. + URL: http://www.ssc.com/lj/issue26/interrupt.html + Keywords: interrupts, irqs, DMA, bottom halves, task queues. + Description: Linux Journal Kernel Korner article. Here is it's + abstract: "This is the fourth in a series of articles about + writing character device drivers as loadable kernel modules. This + month, we further investigate the field of interrupt handling. + Though it is conceptually simple, practical limitations and + constraints make this an ``interesting'' part of device driver + writing, and several different facilities have been provided for + different situations. We also investigate the complex topic of + DMA". + + * Title: "Network Buffers And Memory Management" + Author: Alan Cox. + URL: http://www.ssc.com/lj/issue30/kk30.html + Keywords: sk_buffs, network devices, protocol/link layer + variables, network devices flags, transmit, receive, + configuration, multicast. + Description: Linux Journal Kernel Korner. Here is the abstract: + "Writing a network device driver for Linux is fundamentally + simple---most of the complexity (other than talking to the + hardware) involves managing network packets in memory". + + * Title: "An Introduction to the Linux 1.3.x Networking Code" + Author: Vipul Gupta. + URL: http://anchor.cs.binghamton.edu/courses/cs628/linux-net.html + Keywords: files, sk_buffs. + Description: A short description of files under the net/ + directory. Each file has a one or two lines paragraph description. + sk_buffs explained, too, with some beautiful pictures. A little + bit outdated. + + * Title: "Linux ioctl() Primer" + Author: Vipul Gupta. + URL: http://anchor.cs.binghamton.edu/courses/cs628/ioctl.html + Keywords: ioctl, socket. + Description: Little description and examples on the use and + implementation of the ioctl() system call. A little bit biased + towards sockets. + + * Title: "Writing Linux Device Drivers" + Author: Michael K. Johnson. + URL: http://www.redhat.com/~johnsonm/devices.html + Keywords: files, VFS, file operations, kernel interface, character + vs block devices, I/O access, hardware interrupts, DMA, access to + user memory, memory allocation, timers. + Description: Introductory 50-minutes (sic) tutorial on writing + device drivers. 12 pages written by the same author of the "Kernel + Hackers' Guide" which give a very good overview of the topic. + + * Title: "The Venus kernel interface" + Author: Peter J. Braam. + URL: + http://www.coda.cs.cmu.edu/doc/html/kernel-venus-protocol.html + Keywords: coda, filesystem, venus, cache manager. + Description: "This document describes the communication between + Venus and kernel level file system code needed for the operation + of the Coda filesystem. This version document is meant to describe + the current interface (version 1.0) as well as improvements we + envisage". + + * Title: "Programming PCI-Devices under Linux" + Author: Claus Schroeter. + URL: + ftp://ftp.llp.fu-berlin.de/pub/linux/LINUX-LAB/whitepapers/pcip.ps + .gz + Keywords: PCI, device, busmastering. + Description: 6 pages tutorial on PCI programming under Linux. + Gives the basic concepts on the architecture of the PCI subsystem, + as long as basic functions and macros to read/write the devices + and perform busmastering. + + * Title: "Writing Character Device Driver for Linux" + Author: R. Baruch and C. Schroeter. + URL: + ftp://ftp.llp.fu-berlin.de/pub/linux/LINUX-LAB/whitepapers/drivers + .ps.gz + Keywords: character device drivers, I/O, signals, DMA, accesing + ports in user space, kernel environment. + Description: 68 pages paper on writing character drivers. A little + bit old (1.993, 1.994) although still useful. + + * Title: "The Second Extended Filesystem" + Author: Matthew Wilcox. + URL: http://pocket.fluff.org/~mrw/linux/ext2.txt + Keywords: ext2, filesystem. + Description: Description of ext2's blocks, directories, inodes ... + + * Title: "Analysis of the Ext2fs structure" + Author: Louis-Dominique Dubeau. + URL: http://step.polymtl.ca/~ldd/ext2fs/ext2fs_toc.html + Keywords: ext2, filesystem, ext2fs. + Description: Description of ext2's blocks, directories, inodes, + bitmaps, invariants ... + + * Title: "Kernel API changes from 2.0 to 2.2" + Author: Richard Gooch. + URL: + http://www.atnf.csiro.au/~rgooch/linux/docs/porting-to-2.2.html + Keywords: 2.2, changes. + Description: Kernel functions/structures/variables which changed + from 2.0.x to 2.2.x. + + * Title: "Kernel API changes from 2.2 to 2.3" + Author: Richard Gooch. + URL: + http://www.atnf.csiro.au/~rgooch/linux/docs/porting-to-2.2.html + Keywords: 2.3, changes. + Description: Kernel functions/structures/variables which changed + from 2.2.x to 2.3.x. + + * Title: "Linux Kernel Module Programming Guide" + Author: Ori Pomerantz. + URL: http://www.linuxdoc.org/LDP/lkmpg/mpg.html + Keywords: modules, GPL book, /proc, ioctls, system calls, + interrupt handlers . + Description: Very nice 92 pages GPL book on the topic of modules + programming. Lots of examples. + + * Title: "Device File System (devfs) Overview" + Author: Richard Gooch. + URL: http://www.atnf.csiro.au/~rgooch/linux/docs/devfs.txt + Keywords: filesystem, /dev, devfs, dynamic devices, major/minor + allocation, device management. + Description: Document describing Richard Gooch's controversial + devfs, which allows for dynamic devices, only shows present + devices in /dev, gets rid of major/minor numbers allocation + problems, and allows for hundreds of identical devices (which some + USB systems might demand soon). + + * Title: "I/O Event Handling Under Linux" + Author: Richard Gooch. + URL: http://www.atnf.csiro.au/~rgooch/linux/docs/io-events.html + Keywords: IO, I/O, select(2), poll(2), FDs, aio_read(2), readiness + event queues. + Description: From the Introduction: "I/O Event handling is about + how your Operating System allows you to manage a large number of + open files (file descriptors in UNIX/POSIX, or FDs) in your + application. You want the OS to notify you when FDs become active + (have data ready to be read or are ready for writing). Ideally you + want a mechanism that is scalable. This means a large number of + inactive FDs cost very little in memory and CPU time to manage". + + BOOKS: (Not on-line) + + * Title: "Linux Device Drivers" + Author: Alessandro Rubini. + Publisher: O'Reilly &Associates. + Date: 1998. + ISBN: 1-56592-292-1 + + * Title: "Linux Kernel Internals" + Author: Michael Beck. + Publisher: Addison-Wesley. + Date: 1997. + ISBN: 0-201-33143-8 (second edition) + + * Title: "The Design of the UNIX Operating System" + Author: Maurice J. Bach. + Publisher: Prentice Hall. + Date: 1986. + Pages: 471. + ISBN: 0-13-201757-1 + + * Title: "The Design and Implementation of the 4.3 BSD UNIX + Operating System" + Author: Samuel J. Leffler, Marshall Kirk McKusick, Michael J. + Karels, John S. Quarterman. + Publisher: Addison-Wesley. + Date: 1989 (reprinted with corrections on October, 1990). + ISBN: 0-201-06196-1 + + * Title: "The Design and Implementation of the 4.4 BSD UNIX + Operating System" + Author: Marshall Kirk McKusick, Keith Bostic, Michael J. Karels, + John S. Quarterman. + Publisher: Addison-Wesley. + Date: 1996. + ISBN: 0-201-54979-4 + + * Title: "Programmation Linux 2.0 API systeme et fonctionnement du + noyau" + Author: Remy Card, Eric Dumas, Franck Mevel. + Publisher: Eyrolles. + Date: 1997. + Pages: 520. + ISBN: 2-212-08932-5 + Notes: French. + + * Title: "The Linux Kernel Book" + Author: Remy Card, Eric Dumas, Franck Mevel. + Publisher: John Wiley & Sons. + Date: 1998. + ISBN: 0-471-98141-9 + Notes: English translation. + + * Title: "Linux 2.0" + Author: Remy Card, Eric Dumas, Franck Mevel. + Publisher: Gestión 2000. + Date: 1997. + Pages: 501. + ISBN: 8-480-88208-5 + Notes: Spanish translation. + + * Title: "Unix internals -- the new frontiers" + Author: Uresh Vahalia. + Publisher: Prentice Hall. + Date: 1996. + Pages: 600. + ISBN: 0-13-101908-2 + + * Title: "Linux Core Kernel Commentary. Guide to Insider's Knowledge + on the Core Kernel od the Linux Code" + Author: Scott Maxwell. + Publisher: ???. + Date: 1999. + Pages: 592. + ISBN: 1-57610-469-9 + Notes: CD-ROM included. + + MISCELLANEOUS: + + * Name: Linux Source Driver. + URL: http://lsd.linux.cz + Keywords: Browsing source code. + Description: "Linux Source Driver (LSD) is an application, which + can make browsing source codes of Linux kernel easier than you can + imagine. You can select between multiple versions of kernel (e.g. + 0.01, 1.0.0, 2.0.33, 2.0.34pre13, 2.0.0, 2.1.101 etc.). With LSD + you can search Linux kernel (fulltext, macros, types, functions + and variables) and LSD can generate patches for you on the fly + (files, directories or kernel)". + + * Name: Cross-Referencing Linux. + URL: http://lxr.linux.no/source/ + Keywords: Browsing source code. + Description: Another web-based Linux kernel source code browser. + Lots of cross references to variables and functions. You can see + where they are defined and where they are used. + + * Name: Linux Weekly News. + URL: http://lwn.net + Keywords: latest kernel news. + Description: The title says it all. There's a fixed kernel section + summarizing developers' work, bug fixes, new features and versions + produced during the week. Published every Thursday. + + * Name: Kernel Traffic. + URL: http://kt.linuxcare.com + Keywords: linux-kernel mailing list, weekly kernel news. + Description: Weekly newsletter covering the most relevant + discussions of the linux-kernel mailing list. + + * Name: CuTTiNG.eDGe.LiNuX. + URL: http://edge.kernelnotes.org + Keywords: changelist. + Description: Site which provides the changelist for every kernel + release. What's new, what's better, what's changed. Myrdraal reads + the patches and describes them. Pointers to the patches are there, + too. + + * Name: New linux-kernel Mailing List FAQ. + URL: Original site: + http://www.altern.org/andrebalsa/doc/lkml-faq.html + URL: U.S. mirror site: + http://www.ececs.uc.edu/~rreilova/linux/lkml-faq.html + Keywords: linux-kernel mailing list FAQ. + Description: linux-kernel is a mailing list for developers to + communicate. This FAQ builds on the previous linux-kernel mailing + list FAQ maintained by Frohwalt Egerer, who no longer maintains + it. Read it to see how to join the mailing list. Dozens of + interesting questions regarding the list, Linux, developers (who + is ...?), terms (what is...?) are answered here too. Just read it. + + * Name: "Linux Virtual File System" + Author: Peter J. Braam. + URL: http://www.coda.cs.cmu.edu/doc/talks/linuxvfs + Keywords: slides, VFS, inode, superblock, dentry, dcache. + Description: Set of slides, presumably from a presentation on the + Linux VFS layer. Covers version 2.1.x, with dentries and the + dcache. + _________________________________________________________________ - + Name: New linux-kernel Mailing List FAQ. - URL: Original site: - http://www.altern.org/andrebalsa/doc/lkml-faq.html - URL: U.S. mirror site: - http://www.ececs.uc.edu/~rreilova/linux/lkml-faq.html - Keywords: linux-kernel mailing list FAQ. - Description: linux-kernel is a mailing list for developers - to communicate. This FAQ builds on the previous linux-kernel - mailing list FAQ maintained by Frohwalt Egerer, who no longer - maintains it. Read it to see how to join the mailing list. - Dozens of interesting questions regarding the list, Linux, - developers (who is ...?), terms (what is...?) are answered - here too. Just read it. - - + Name: "Linux Virtual File System" - Author: Peter J. Braam. - URL: http://www.coda.cs.cmu.edu/doc/talks/linuxvfs - Keywords: slides, VFS, inode, superblock, dentry, dcache. - Description: Set of slides, presumably from a presentation - on the Linux VFS layer. Covers version 2.1.x, with dentries - and the dcache. + Document last updated on Tue Nov 30 11:20:00 CET 1999 diff -u --recursive --new-file v2.2.13/linux/Documentation/kernel-parameters.txt linux/Documentation/kernel-parameters.txt --- v2.2.13/linux/Documentation/kernel-parameters.txt Mon Jun 7 16:13:07 1999 +++ linux/Documentation/kernel-parameters.txt Tue Jan 4 10:12:10 2000 @@ -11,7 +11,7 @@ restrictions referred to are that the relevant option is valid if: APIC APIC support is enabled. - APM Automatic Power Management support is enabled. + APM Advanced Power Management support is enabled. AX25 Appropriate AX.25 support is enabled. CD Appropriate CD support is enabled. EIDE EIDE/ATAPI support is enabled. @@ -50,7 +50,7 @@ it will appear as a kernel argument readable via /proc/cmdline by programs running once the system is up. - 53c7xx= [HW,SCSI] + 53c7xx= [HW,SCSI] Amiga SCSI controllers adb_buttons= [HW,MOUSE] @@ -64,10 +64,12 @@ AM53C974= [HW,SCSI] - apm= [APM] Automatic Power Management. + apm= [APM] Advanced Power Management. arcrimi= [HW,NET] + ataflop= [HW, M68k] + atamouse= [HW,MOUSE] Atari Mouse. atascsi= [HW,SCSI] Atari SCSI. @@ -96,22 +98,22 @@ com90xx= [HW,NET] - console= + console= [KNL] output console + comm spec (speed, control, parity) cyclades= [HW,SERIAL] Cyclades multi-serial port adapter. - debug [KNL] Enable kernel debugging. + debug [KNL] Enable kernel debugging (events log level). decnet= [HW,NET] - digi= [HW,SERIAL] + digi= [HW,SERIAL] io parameters + enable/disable command digiepca= [HW,SERIAL] dmascc= [HW,AX25,SERIAL] AX.25 Z80SCC driver with DMA support available. - dmasound= [HW,SOUND] + dmasound= [HW,SOUND] (sound subsystem buffers) dtc3181e= [HW,SCSI] @@ -121,7 +123,7 @@ edb= [HW,PS2] - ether= [HW,NET] Ethernet. + ether= [HW,NET] Ethernet cards parameters (iomem,irq,dev_name). fd_mcs= [HW,SCSI] @@ -129,7 +131,7 @@ floppy= [HW] - ftape= [HW] Floppy Tape subsystem. + ftape= [HW] Floppy Tape subsystem debugging options. gdth= [HW,SCSI] @@ -137,7 +139,8 @@ gvp11= [HW,SCSI] - hd= [EIDE] IDE and EIDE hard drive subsystem. + hd= [EIDE] (E)IDE hard drive subsystem + geometry (Cyl/heads/sectors) or tune parameters. hfmodem= [HW,AX25] @@ -149,10 +152,18 @@ icn= [HW,ISDN] + ide?= [HW] (E)IDE subsystem : config (iomem/irq), tuning or + debugging (serialize,reset,no{dma,tune,probe}) or + chipset specific parameters + + idebus= [HW] (E)IDE subsystem : VLB/PCI bus speed + in2000= [HW,SCSI] init= [KNL] + initrd= [KNL] initial ramdisk path + ip= [PNP] isp16= [HW,CD] @@ -185,7 +196,7 @@ kbd-reset [VT] - load_ramdisk= [RAM] + load_ramdisk= [RAM] initrd loading boolean lp= [LPT] Parallel Printer. @@ -204,7 +215,7 @@ mcdx= [HW,CD] - md= [HW] + md= [HW] RAID subsystems devices and level mdacon= [MDA] @@ -224,6 +235,8 @@ nfsroot= [NFS] + nmi_watchdog= [KNL, BUGS=ix86] debugging features for SMP kernels + no387 [BUGS=ix86] Tells the kernel to use the 387 maths emulation library even if a 387 maths coprocessor is present. @@ -231,6 +244,10 @@ noapic [SMP,APIC] Tells the kernel not to make use of any APIC that may be present on the system. + noasync [HW, M68K] Disables async and sync negotiation for all devices. + + nodisconnect [HW,SCSI, M68K] Disables SCSI disconnects. + no-halt [BUGS=ix86] noinitrd [RAM] Tells the kernel not to load any configured @@ -240,6 +257,8 @@ nosmp [SMP] Tells an SMP kernel to act as a UP kernel. + nosync [HW, M68K] Disables sync negotiation for all devices. + optcd= [HW,CD] panic= @@ -260,7 +279,7 @@ pg. [PARIDE] - pirq= [SMP,APIC] + pirq= [SMP,APIC] mp-table plip= [LP,NET] Parallel port network link. @@ -275,13 +294,14 @@ ramdisk_size= [RAM] - ramdisk_start= [RAM] + ramdisk_start= [RAM] offset of the initrd image when cohabiting with + a kernel image on a floppy reboot= [BUGS=ix86] reserve= - riscom8= [HW,SERIAL] + riscom8= [HW,SERIAL] multi-port serial driver (io parameters) ro [KNL] Mount root device read-only on boot. @@ -303,12 +323,14 @@ specialix= [HW,SERIAL] Specialix multi-serial port adapter. - st= [HW] + st= [HW] SCSI tape parameters (buffers, ..) st0x= [HW,SCSI] stram_swap= [HW] + switches= [HW, M68K] + sym53c416= [HW,SCSI] sym53c8xx= [HW,SCSI] @@ -331,6 +353,6 @@ wdt= [HW] - xd= [HW,XT] + xd= [HW,XT] Original XT 8bit disk controllers xd_geo= [HW,XT] diff -u --recursive --new-file v2.2.13/linux/Documentation/knfsd.txt linux/Documentation/knfsd.txt --- v2.2.13/linux/Documentation/knfsd.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/knfsd.txt Tue Jan 4 10:12:10 2000 @@ -0,0 +1,98 @@ +This is the Linux NFS utility package version 0.1.5. It is based on +knfsd 1.4.7. + +WARNING: The NFS servers in Linux 2.2 to 2.2.13 are not compatible with +other NFS client implemenations. If you plan to use Linux 2.2.x as an +NFS server for non-Linux NFS clients, you should get the Linux NFS +kernel from the Linux NFS CVS server: + +1. Set the environment variable, CVS_RSH, to ssh. +2. Login to the Linux NFS CVS server: + +# cvs -z 3 -d:pserver:anonymous@cvs.linuxnfs.sourceforge.org:/cvsroot/nfs login + +without password if it is your first time. + +3. Check out the current Linux 2.2 NFS kernel: + +a. From the NFS V2 branch: + +# cvs -z 3 -d:pserver:anonymous@cvs.linuxnfs.sourceforge.org:/cvsroot/nfs co -r linux-2-2-nfsv2 linux-2.2 + +b. From the main trunk: + +# cvs -z 3 -d:pserver:anonymous@cvs.linuxnfs.sourceforge.org:/cvsroot/nfs co linux-2.2 + +4. If you don't want to use the current NFS kernel, you can find out +for which kernels the NFS patch is available: + +# cd linux-2.2 +# cvs -z 9 -d:pserver:anonymous@cvs.linuxnfs.sourceforge.org:/cvsroot/nfs status -v Makefile + +Then generate the kernel patch: + +# cvs -z 3 -d:pserver:anonymous@cvs.linuxnfs.sourceforge.org:/cvsroot/nfs rdiff -ko -u -r linux-2-2-xx -r linux-2-2-xx-nfsv2-xxxxx linux-2.2 + +If there is no NFS patch for the kernel you are interested in, you have +to make a patch closest to your kernel version and apply it by hand. + +There is a Linux NFS kernel source tree for Linux 2.3, linux-2.3, on +the Linux NFS CVS server. We will need all the help we can get. To +contribute to the Linux NFS project, please go to + +http://www.linuxnfs.sourceforge.org + +You register yourself. Please send an email to +nfs-admin@linuxnfs.sourceforge.org with + +1. Your user id on www.linuxnfs.sourceforge.org. +2. The area in NFS you'd like to work on. + +You will be notified when it is done. + +There is a Linux NFS mailing list at + +http://lists.sourceforge.net/mailman/listinfo/nfs/ +You can subscribe it and search the mailing list archive via a web +browser. + +The nfs-utils package is available from the CVS server: + +# cvs -z 3 -d:pserver:anonymous@cvs.linuxnfs.sourceforge.org:/cvsroot/nfs co nfs-utils + +will get the latest version. + +The files are + +ftp://ftp.linuxnfs.sourceforge.org/pub/nfs/nfs-utils-0.1.5.tar.gz +ftp://ftp.linuxnfs.sourceforge.org/pub/nfs/nfs-utils-0.1.4-0.1.5.diff.gz + +To compile, just do + +# ./configure +# make + +# make install + +will install the nfs-utils binaries. You have to install the NFS +service scripts. There are 2 in etc/redhat provided for RedHat 6.x. +They are tested on RedHat 6.1. + +On RedHat 6.1, you can use + +# rpm -ta nfs-utils-0.1.5.tar.gz + +to build the source and binary RPMs. + +If your mount from util-linux is too old, you will need 2 patches: + +ftp://ftp.linuxnfs.sourceforge.org/pub/nfs/util-linux-2.9o-mount-nfsv3.patch +ftp://ftp.linuxnfs.sourceforge.org/pub/nfs/util-linux-2.9w-mount-nfsv3try.patch + +Thanks. + + +H.J. +hjl@lucon.org +12/19/99 + diff -u --recursive --new-file v2.2.13/linux/Documentation/moxa-smartio linux/Documentation/moxa-smartio --- v2.2.13/linux/Documentation/moxa-smartio Wed Dec 31 16:00:00 1969 +++ linux/Documentation/moxa-smartio Tue Jan 4 10:12:10 2000 @@ -0,0 +1,412 @@ +============================================================================= + + MOXA Smartio Family Device Driver Ver 1.1 Installation Guide + for Linux Kernel 2.2.x and 2.0.3x + Copyright (C) 1999, Moxa Technologies Co, Ltd. +============================================================================= +Content + +1. Introduction +2. System Requirement +3. Installation +4. Utilities +5. Setserial +6. Troubleshooting + +----------------------------------------------------------------------------- +1. Introduction + + The Smartio family Linux driver, Ver. 1.1, supports following multiport + boards. + + -C104P/H/HS, C104H/PCI, C104HS/PCI, CI-104J 4 port multiport board. + -C168P/H/HS, C168H/PCI 8 port multiport board. + + This driver has been modified a little and cleaned up from the Moxa + contributed driver code and merged into Linux 2.2.14pre. In paticular + official major/minor numbers have been assigned which are different to + those the original Moxa supplied driver used. + + This driver and installation procedure have been developed upon Linux Kernel + 2.2.5 and backward compatible to 2.0.3x. This driver supports Intel x86 and + Alpha hardware platform. In order to maintain compatibility, this version + has also been properly tested with RedHat, OpenLinux, TurboLinux and + S.u.S.E Linux. However, if compatibility problem occurs, please contact + Moxa at support@moxa.com.tw. + + In addition to device driver, useful utilities are also provided in this + version. They are + - msdiag Diagnostic program for detecting installed Moxa Smartio boards. + - msmon Monitor program to observe data count and line status signals. + - msterm A simple terminal program which is useful in testing serial + ports. + - io-irq.exe Configuration program to setup ISA boards. Please note that + this program can only be executed under DOS. + + All the drivers and utilities are published in form of source code under + GNU General Public License in this version. Please refer to GNU General + Public License announcement in each source code file for more detail. + + In Moxa's ftp sites, you may always find latest driver at + ftp://ftp.moxa.com or ftp://ftp.moxa.com.tw. + + This version of driver can be installed as Loadable Module (Module driver) + or built-in into kernel (Static driver). You may refer to following + installation procedure for suitable one. Before you install the driver, + please refer to hardware installation procedure in the User's Manual. + + We assume the user should be familiar with following documents. + - Serial-HOWTO + - Kernel-HOWTO + +----------------------------------------------------------------------------- +2. System Requirement + - Hardware platform: Intel x86 or Alpha machine + - Kernel version: 2.0.3x or 2.2.x + - gcc version 2.72 or later + - Maximum 4 boards can be installed in combination + +----------------------------------------------------------------------------- +3. Installation + + 3.1 Hardware installation + + There are two types of buses, ISA and PCI, for Smartio family multiport + board. + + ISA board + --------- + You'll have to configure CAP address, I/O address, Interrupt Vector + as well as IRQ before installing this driver. Please refer to hardware + installation procedure in User's Manual before proceed any further. + Please make sure the JP1 is open after the ISA board is set properly. + + PCI board + --------- + You may need to adjust IRQ useage in BIOS to avoid from IRQ conflict + with other ISA devices. Please refer to hardware installation + procedure in User's Manual in advance. + + IRQ Sharing + ----------- + Each port within the same multiport board shares the same IRQ. Up to + 4 Moxa Smartio Family multiport boards can be installed together on + one system and they can share the same IRQ. + + 3.2 Driver files and device naming convention + + The driver file may be obtained from ftp, CD-ROM or floppy disk. The + first step, anyway, is to copy driver file "mxser.tgz" into specified + directory. e.g. /moxa. The execute commands as below. + + # cd /moxa + # tar xvf /dev/fd0 + or + # cd /moxa + # cp /mnt/cdrom//mxser.tgz . + # tar xvfz mxser.tgz + + You may find all the driver and utilities files in /moxa/mxser. + Following installation procedure depends on the model you'd like to + run the driver. If you prefer module driver, please refer to 3.3. + If static driver is required, please refer to 3.4. + + Dialin and callout port + ----------------------- + This driver remains traditional serial device properties. There're + two special file name for each serial port. One is dial-in port + which is named "ttyMxx". For callout port, the naming convention + is "cumxx". + + Device naming when more than 2 boards installed + ----------------------------------------------- + Naming convention for each Smartio multiport board is pre-defined + as below. + + Board Num. Dial-in Port Callout port + 1st board ttyM0 - ttyM7 cum0 - cum7 + 2nd board ttyM8 - ttyM15 cum8 - cum15 + 3rd board ttyM16 - ttyM23 cum16 - cum23 + 4th board ttyM24 - ttym31 cum24 - cum31 + + Board sequence + -------------- + This driver will activate ISA boards according to the parameter set + in the driver. After all specified ISA board activated, PCI board + will be installed in the system automatically driven. + Therefore the board number is sorted by the CAP address of ISA boards. + For PCI boards, their sequence will be after ISA boards and C168H/PCI + has higher priority than C104H/PCI boards. + + 3.3 Module driver configuration + Module driver is easiest way to install. If you prefer static driver + installation, please skip this paragraph. + 1. Find "Makefile" in /moxa/mxser, then run + + # make install + + The driver files "mxser.o" and utilities will be properly compiled + and copied to system directories respectively.Then run + + # insmod mxser + + to activate the moduler driver. You may run "lsmod" to check + if "mxser.o" is activated. + + 2. Create special files by executing "msmknod". + # cd /moxa/mxser/driver + # ./msmknod + + Default major numbers for dial-in device and callout device are + 174, 175. Msmknod will delete any special files occuping the same + device naming. + + 3. Up to now, you may manually execute "insmod mxser" to activate + this driver and run "rmmod mxser" to remove it. However, it's + better to have a boot time configuration to eliminate manual + operation. + Boot time configuration can be achieved by rc file. Run following + command for setting rc files. + + # cd /moxa/mxser/driver + # cp ./rc.mxser /etc/rc.d + # cd /etc/rc.d + + You may have to modify part of the content in rc.mxser to specify + parameters for ISA board. Please refer to rc.mxser for more detail. + Find "rc.serial". If "rc.serial" doesn't exist, create it by vi. + Add "rc.mxser" in last line. Next, open rc.local by vi + and append following content. + + if [ -f /etc/rc.d/rc.serial ]; then + sh /etc/rc.d/rc.serial + fi + + 4. Reboot and check if mxser.o activated by "lsmod" command. + 5. If you'd like to drive Smartio ISA boards in the system, you'll + have to add parameter to specify CAP address of given board while + activating "mxser.o". The format for parameters are as follows. + + insmod mxser ioaddr=0x???,0x???,0x???,0x??? + | | | | + | | | +- 4th ISA board + | | +------ 3rd ISA board + | +------------ 2nd ISA board + +------------------- 1st ISA board + + 3.4 Static driver configuration + + 1. Create link + # cd /usr/src/linux/drivers/char + # ln -s /moxa/mxser/driver/mxser.c mxser.c + + 2. Add CAP address list for ISA boards + In module mode, the CAP address for ISA board is given by + parameter. In static driver configuration, you'll have to + assign it within driver's source code. If you will not + install any ISA boards, you may skip to next portion. + The instructions to modify driver source code are as + below. + a. # cd /moxa/mxser/driver + # vi mxser.c + b. Find the array mxserBoardCAP[] as belows. + + static int mxserBoardCAP[] + = {0x00, 0x00, 0x00, 0x00}; + + c. Change the address within this array using vi. For + example, to driver 2 ISA boards with CAP address + 0x280 and 0x180 as 1st and 2nd board. Just to change + the source code as follows. + + static int mxserBoardCAP[] + = {0x280, 0x180, 0x00, 0x00}; + + 3. Modify tty_io.c + # cd /usr/src/linux/drivers/char/ + # vi tty_io.c + Find pty_init(), insert "mxser_init()" as + + pty_init(); + mxser_init(); + + 4. Modify tty.h + # cd /usr/src/linux/include/linux + # vi tty.h + Find extern int tty_init(void), insert "mxser_init()" as + + extern int tty_init(void); + extern int mxser_init(void); + + 5. Modify Makefile + # cd /usr/src/linux/drivers/char + # vi Makefile + Find L_OBJS := tty_io.o ...... random.o, add + "mxser.o" at last of this line as + L_OBJS := tty_io.o ....... mxser.o + + 6. Rebuild kernel + The following are for Linux kernel rebuilding,for your reference only. + For appropriate details, please refer to the Linux document. + + If 'lilo' utility is installed, please use 'make zlilo' to rebuild + kernel. If 'lilo' is not installed, please follow the following steps. + + a. cd /usr/src/linux + b. make clean /* take a few minutes */ + c. make dep /* take a few minutes */ + d. make bzImage /* take probably 10-20 minutes */ + e. Backup original boot kernel. /* optional step */ + f. cp /usr/src/linux/arch/i386/boot/bzImage /boot/vmlinuz + g. Please make sure the boot kernel (vmlinuz) is in the + correct position. If you use 'lilo' utility, you should + check /etc/lilo.conf 'image' item specifiedd the path + which is the 'vmlinuz' path, or you will load wrong + (or old) boot kernel image (vmlinuz). + h. chmod 400 /vmlinuz + i. lilo + j. rdev -R /vmlinuz 1 + k. sync + + Note that if the result of "make zImage" is ERROR, then you have to + go back to Linux configuration Setup. Type "make config" in directory + /usr/src/linux or "setup". + + Since system include file, /usr/src/linux/include/linux/interrupt.h, + is modified each time the MOXA driver is installed, kernel rebuilding + is inevitable. And it takes about 10 to 20 minutes depends on the + machine. + + 7. Make utility + # cd /moxa/mxser/utility + # make install + + 8. Make special file + # cd /moxa/mxser/driver + # ./msmknod + + 9. Reboot + + 3.5 Custom configuration + Although this driver already provides you default configuration, you + still can change the device name and major number.The instruction to + change these parameters are shown as below. + + Change Device name + ------------------ + If you'd like to use other device names instead of default naming + convention, all you have to do is to modify the internal code + within the shell script "msmknod". First, you have to open "msmknod" + by vi. Locate each line contains "ttyM" and "cum" and change them + to the device name you desired. "msmknod" creates the device names + you need next time executed. + + Change Major number + ------------------- + If major number 30 and 35 had been occupied, you may have to select + 2 free major numbers for this driver. There are 3 steps to change + major numbers. + + 1. Find free major numbers + In /proc/devices, you may find all the major numbers occupied + in the system. Please select 2 major numbers that are available. + e.g. 40, 45. + 2. Create special files + Run /moxa/mxser/driver/msmknod to create special files with + specified major numbers. + 3. Modify driver with new major number + Run vi to open /moxa/mxser/driver/mxser.c. Locate the line + contains "MXSERMAJOR". Change the content as below. + #define MXSERMAJOR 40 + #define MXSERCUMAJOR 45 + 4. Run # make install in /moxa/mxser/driver. + + 3.6 Verify driver installation + You may refer to /var/log/messages to check the latest status + log reported by this driver whenever it's activated. +----------------------------------------------------------------------------- +4. Utilities + There are 3 utilities contained in this driver. They are msdiag, msmon and + msterm. These 3 utilities are released in form of source code. They should + be compiled into executable file and copied into /usr/bin. + + msdiag - Diagnostic + -------------------- + This utility provides the function to detect what Moxa Smartio multiport + board exists in the system. + + msmon - Port Monitoring + ----------------------- + This utility gives the user a quick view about all the MOXA ports' + activities. One can easily learn each port's total received/transmitted + (Rx/Tx) character count since the time when the monitoring is started. + Rx/Tx throughputs per second are also reported in interval basis (e.g. + the last 5 seconds) and in average basis (since the time the monitoring + is started). You can reset all ports' count by key. <+> <-> + (plus/minus) keys to change the displaying time interval. Press + on the port, that cursor stay, to view the port's communication + parameters, signal status, and input/output queue. + + msterm - Terminal Emulation + --------------------------- + This utility provides data sending and receiving ability of all tty ports, + especially for MOXA ports. It is quite useful for testing simple + application, for example, sending AT command to a modem connected to the + port or used as a terminal for login purpose. Note that this is only a + dumb terminal emulation without handling full screen operation. +----------------------------------------------------------------------------- +5. Setserial + + Supported Setserial parameters are listed as below. + + uart set UART type(16450-->disable FIFO, 16550A-->enable FIFO) + close_delay set the amount of time(in 1/100 of a second) that DTR + should be kept low while being closed. + closing_wait set the amount of time(in 1/100 of a second) that the + serial port should wait for data to be drained while + being closed, before the receiver is disable. + spd_hi Use 57.6kb when the application requests 38.4kb. + spd_vhi Use 115.2kb when the application requests 38.4kb. + spd_normal Use 38.4kb when the application requests 38.4kb. + +----------------------------------------------------------------------------- +6. Troubleshooting + + The boot time error mesages and solutions are stated as clearly as + possible. If all the possible solutions fail, please contact our technical + support team to get more help. + + Error msg: More than 4 Moxa Smartio family boards found. Fifth board and + after are ignored. + Solution: + To avoid this problem, please unplug fifth and after board, because Moxa + driver supports up to 4 boards. + + Error msg: Request_irq fail, IRQ(?) may be conflict with another device. + Solution: + Other PCI or ISA devices occupy the assigned IRQ. If you are not sure + which device causes the situation,please check /proc/interrupts to find + free IRQ and simply change another free IRQ for Moxa board. + + Error msg: Board #: C1xx Series(CAP=xxx) interupt number invalid. + Solution: + Each port within the same multiport board shares the same IRQ. Please set + one IRQ (IRQ doesn't equal to zero) for one Moxa board. + + Error msg: No interrupt vector be set for Moxa ISA board(CAP=xxx). + Solution: + Moxa ISA board needs an interrupt vector.Please refer to user's manual + "Hardware Installation" chapter to set interrupt vector. + + Error msg: Couldn't install MOXA Smartio family driver! + Solution: + Load Moxa driver fail, the major number may conflict with other devices. + Please refer to previous section 3.5 to change a free major number for + Moxa driver. + + Error msg: Couldn't install MOXA Smartio family callout driver! + Solution: + Load Moxa callout driver fail, the callout device major number may + conflict with other devices. Please refer to previous section 3.5 to + change a free callout device major number for Moxa driver. +----------------------------------------------------------------------------- diff -u --recursive --new-file v2.2.13/linux/Documentation/networking/comx.txt linux/Documentation/networking/comx.txt --- v2.2.13/linux/Documentation/networking/comx.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/networking/comx.txt Tue Jan 4 10:12:10 2000 @@ -0,0 +1,248 @@ + + COMX drivers for the 2.2 kernel + +Originally written by: Tivadar Szemethy, +Currently maintained by: Gergely Madarasz + +Last change: 21/06/1999. + +INTRODUCTION + +This document describes the software drivers and their use for the +COMX line of synchronous serial adapters for Linux version 2.2.0 and +above. +The cards are produced and sold by ITC-Pro Ltd. Budapest, Hungary +For further info contact +or http://www.itc.hu (mostly in Hungarian). +The firmware files and software are available from ftp://ftp.itc.hu + +Currently, the drivers support the following cards and protocols: + +COMX (2x64 kbps intelligent board) +CMX (1x256 + 1x128 kbps intelligent board) +HiCOMX (2x2Mbps intelligent board) +LoCOMX (1x512 kbps passive board) +MixCOM (1x512 or 2x512kbps passive board with a hardware watchdog an + optional BRI interface and optional flashROM (1-32M)) + +At the moment of writing this document, the (Cisco)-HDLC, LAPB, SyncPPP and +Frame Relay (DTE, rfc1294 IP encapsulation with partially implemented Q933a +LMI) protocols are available as link-level protocol. +X.25 support is being worked on. + +USAGE + +Load the comx.o module and the hardware-specific and protocol-specific +modules you'll need into the running kernel using the insmod utility. +This creates the /proc/comx directory. +See the example scripts in the 'etc' directory. + +/proc INTERFACE INTRO + +The COMX driver set has a new type of user interface based on the /proc +filesystem which eliminates the need for external user-land software doing +IOCTL calls. +Each network interface or device (i.e. those ones you configure with 'ifconfig' +and 'route' etc.) has a corresponding directory under /proc/comx. You can +dynamically create a new interface by saying 'mkdir /proc/comx/comx0' (or you +can name it whatever you want up to 8 characters long, comx[n] is just a +convention). +Generally the files contained in these directories are text files, which can +be viewed by 'cat filename' and you can write a string to such a file by +saying 'echo _string_ >filename'. This is very similar to the sysctl interface. +Don't use a text editor to edit these files, always use 'echo' (or 'cat' +where appropriate). +When you've created the comx[n] directory, two files are created automagically +in it: 'boardtype' and 'protocol'. You have to fill in these files correctly +for your board and protocol you intend to use (see the board and protocol +descriptions in this file below or the example scripts in the 'etc' directory). +After filling in these files, other files will appear in the directory for +setting the various hardware- and protocol-related informations (for example +irq and io addresses, keepalive values etc.) These files are set to default +values upon creation, so you don't necessarily have to change all of them. + +When you're ready with filling in the files in the comx[n] directory, you can +configure the corresponding network interface with the standard network +configuration utilites. If you're unble to bring the interfaces up, look up +the various kernel log files on your system, and consult the messages for +a probable reason. + +EXAMPLE + +To create the interface 'comx0' which is the first channel of a COMX card: + +insmod comx +# insmod comx-hw-comx ; insmod comx-proto-hdlc (these are usually +autoloaded if you use the kernel module loader) + +mkdir /proc/comx/comx0 +echo comx >/proc/comx/comx0/boardtype +echo 0x360 >/proc/comx/comx0/io <- jumper-selectable I/O port +echo 0x0a >/proc/comx/comx0/irq <- jumper-selectable IRQ line +echo 0xd000 >/proc/comx/comx0/memaddr <- software-configurable memory + address. COMX uses 64 KB, and this + can be: 0xa000, 0xb000, 0xc000, + 0xd000, 0xe000. Avoid conflicts + with other hardware. +cat /proc/comx/comx0/firmware <- the firmware for the card +echo HDLC >/proc/comx/comx0/protocol <- the data-link protocol +echo 10 >/proc/comx/comx0/keepalive <- the keepalive for the protocol +ifconfig comx0 1.2.3.4 pointopoint 5.6.7.8 netmask 255.255.255.255 <- + finally configure it with ifconfig +Check its status: +cat /proc/comx/comx0/status + +If you want to use the second channel of this board: + +mkdir /proc/comx/comx1 +echo comx >/proc/comx/comx1/boardtype +echo 0x360 >/proc/comx/comx1/io +echo 10 >/proc/comx/comx1/irq +echo 0xd000 >/proc/comx/comx1/memaddr +echo 1 >/proc/comx/comx1/channel <- channels are numbered + as 0 (default) and 1 + +Now, check if the driver recognized that you're going to use the other +channel of the same adapter: + +cat /proc/comx/comx0/twin +comx1 +cat /proc/comx/comx1/twin +comx0 + +You don't have to load the firmware twice, if you use both channels of +an adapter, just write it into the channel 0's /proc firmware file. + +Default values: io 0x360 for COMX, 0x320 (HICOMX), irq 10, memaddr 0xd0000 + +THE LOCOMX HARDWARE DRIVER + +The LoCOMX driver doesn't require firmware, and it doesn't use memory either, +but it uses DMA channels 1 and 3. You can set the clock rate (if enabled by +jumpers on the board) by writing the kbps value into the file named 'clock'. +Set it to 'external' (it is the default) if you have external clock source. + +(Note: currently the LoCOMX driver does not support the internal clock) + +THE COMX, CMX AND HICOMX DRIVERS + +On the HICOMX, COMX and CMX, you have to load the firmware (it is different for +the three cards!). All these adapters can share the same memory +address (we usually use 0xd0000). On the CMX you can set the internal +clock rate (if enabled by jumpers on the small adapter boards) by writing +the kbps value into the 'clock' file. You have to do this before initializing +the card. If you use both HICOMX and CMX/COMX cards, initialize the HICOMX +first. The I/O address of the HICOMX board is not configurable by any +method available to the user: it is hardwired to 0x320, and if you have to +change it, consult ITC-Pro Ltd. + +THE MIXCOM DRIVER + +The MixCOM board doesn't require firmware, the driver communicates with +it through I/O ports. You can have three of these cards in one machine. + +THE HDLC LINE PROTOCOL DRIVER + +There's only one configurable parameter with this protocol: the 'keepalive' +value. You can set this in seconds or set to 'off'. Agree with the administrator +of your peer router on this setting. The default is 10 (seconds). + +EXAMPLE + +(setting up hw parameters, see above) +echo hdlc >/proc/comx/comx0/protocol +echo 10 >/proc/comx/comx0/keepalive <- not necessary, 10 is the default +ifconfig comx0 1.2.3.4 pointopoint 5.6.7.8 netmask 255.255.255.255 + + +THE PPP LINE PROTOCOL DRIVER + +To use this driver, you have to have ppp-2.3.4, and have a modified version of +pppd (this pppd will work as async pppd to, the modifiactions merely relax +some restricions in order to be able to use non-async lines too. +If configured, this driver can use Van Jacobson TCP header compression (you'll +need the slhc.o module for this). +Additionaly to use this protocol, enable async ppp in your kernel config, and +create the comx device special files in /dev. They're character special files +with major 88, and their names must be the same as their network interface +counterparts (i.e /dev/comx0 with minor 0 corresponds interface comx0 and so +on). + +EXAMPLE + +(setting up hw parameters, see above) +echo ppp >/proc/comx/comx0/protocol +ifconfig comx0 up +pppd comx0 1.2.3.4:5.6.7.8 persist <- with this option pppd won't exit + when the line goes down + +THE LAPB LINE PROTOCOL DRIVER + +For this, you'll need to configure LAPB support (See 'LAPB Data Link Driver' in +'Network options' section) into your kernel (thanks to Jonathan Naylor for his +excellent implementation). +comxlapb.o provides the following files in the appropriate directory +(the default values in parens): t1 (5), t2 (1), n2 (20), mode (DTE, STD) and +window (7). Agree with the administrator of your peer router on these +settings (most people use defaults, but you have to know if you are DTE or +DCE). + +EXAMPLE + +(setting up hw parameters, see above) +echo lapb >/proc/comx/comx0/protocol +echo dce >/proc/comx/comx0/mode <- DCE interface in this example +ifconfig comx0 1.2.3.4 pointopoint 5.6.7.8 netmask 255.255.255.255 + + +THE FRAME RELAY PROTOCOL DRIVER + +You DON'T need any other frame relay related modules from the kernel to use +COMX-Frame Relay. This protocol is a bit more complicated than the others, +because it allows to use 'subinterfaces' or DLCIs within one physical device. +First you have to create the 'master' device (the actual physical interface) +as you would do for other protocols. Specify 'frad' as protocol type. +Now you can bring this interface up by saying 'ifconfig comx0 up' (or whatever +you've named the interface). Do not assign any IP address to this interface +and do not set any routes through it. +Then, set up your DLCIs the following way: create a comx interface for each +DLCI you intend to use (with mkdir), and write 'dlci' to the 'boardtype' file, +and 'ietf-ip' to the 'protocol' file. Currently, the only supported +encapsulation type is this (also called as RFC1294/1490 IP encapsulation). +Write the DLCI number to the 'dlci' file, and write the name of the physical +COMX device to the file called 'master'. +Now you can assign an IP address to this interface and set routes using it. +See the example file for further info and example config script. +Notes: this driver implements a DTE interface with partially implemented +Q933a LMI. +You can find an extensively commented example in the 'etc' directory. + +FURTHER /proc FILES + +boardtype: +Type of the hardware. Valid values are: + 'comx', 'hicomx', 'locomx', 'cmx'. + +protocol: +Data-link protocol on this channel. Can be: HDLC, LAPB, PPP, FRAD + +status: +You can read the channel's actual status from the 'status' file, for example +'cat /proc/comx/comx3/status'. + +lineup_delay: +Interpreted in seconds (default is 1). Used to avoid line jitter: the system +will consider the line status 'UP' only if it is up for at least this number +of seconds. + +debug: +You can set various debug options through this file. Valid options are: +'comx_events', 'comx_tx', 'comx_rx', 'hw_events', 'hw_tx', 'hw_rx'. +You can enable a debug options by writing its name prepended by a '+' into +the debug file, for example 'echo +comx_rx >comx0/debug'. +Disabling an option happens similarly, use the '-' prefix +(e.g. 'echo -hw_rx >debug'). +Debug results can be read from the debug file, for example: +tail -f /proc/comx/comx2/debug + + diff -u --recursive --new-file v2.2.13/linux/Documentation/networking/ip-sysctl.txt linux/Documentation/networking/ip-sysctl.txt --- v2.2.13/linux/Documentation/networking/ip-sysctl.txt Mon May 10 09:55:25 1999 +++ linux/Documentation/networking/ip-sysctl.txt Tue Jan 4 10:12:10 2000 @@ -156,6 +156,14 @@ proxy_arp - BOOLEAN Do proxy arp. +hidden - BOOLEAN + Hide addresses attached to this device from another devices. + Such addresses will never be selected by source address autoselection + mechanism, host does not answer broadcast ARP requests for them, + does not announce it as source address of ARP requests, but they + are still reachable via IP. This flag is activated only if it is + enabled both in specific device section and in "all" section. + shared_media - BOOLEAN undocumented. diff -u --recursive --new-file v2.2.13/linux/Documentation/networking/sis900.txt linux/Documentation/networking/sis900.txt --- v2.2.13/linux/Documentation/networking/sis900.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/networking/sis900.txt Tue Jan 4 10:12:10 2000 @@ -0,0 +1,468 @@ + SiS 900/7016 Fast Ethernet Device Driver + by Ollie Lho (ollie@sis.com.tw) + November 4, 1999. Document Revision: 0.2 + + This document gives some information on installation and usage of SiS + 900/7016 device driver under Linux. + ______________________________________________________________________ + + Table of Contents + + + 1. Introduction + + 2. License + + 3. Changes + + 4. Tested Environment + + 5. Files in This Package + + 6. Installation + + 6.1 Kernel version later than 2.2.11 and 2.3.15 + 6.1.1 Building the driver as loadable module + 6.1.2 Building the driver into kernel + 6.2 Earlier Kernel Version in 2.2.x and 2.3.x Series + + 7. Known Problems and Bugs + + 8. Revision History + + 9. Acknowledgements + + + + ______________________________________________________________________ + + 1. Introduction + + This document describes the revision 1.06 of SiS 900/7016 Fast + Ethernet device driver under Linux. The driver is developed by Silicon + Integrated System Corp. and distributed freely under the GNU General + Public License (GPL). The driver can be compiled as a loadable module + and used under Linux kernel version 2.2.x. With minimal changes, the + driver can also be used under 2.3.x kernel, please see section + ``Installation''. If you are intended to use the driver for earlier + kernels, you are on your own. + + The driver is tested with usual TCP/IP applications including FTP, + Telnet, Netscape etc. and is used constantly by the developers. + + Please send all comments/fixes/questions to Ollie Lho. + + + 2. License + + + + + + + + + + + Copyright (C) 1999 Silicon Integrated System Corp. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + + + + 3. Changes + + Changes made in Revision 1.06 + + 1. Separation of sis900.c and sis900.h in order to move most constant + definition to sis900.h (many of those constants were corrected) + + 2. Clean up PCI detection, the pci-scan from Donald Becker were not + used, just simple pci_find_*. + + 3. MII detection is modified to support multiple mii transceiver. + + 4. Bugs in read_eeprom, mdio_* were removed. + + 5. Lot of sis900 irrelevant comments were removed/changed and more + comments were added to reflect the real situation. + + 6. Clean up of physical/virtual address space mess in buffer + descriptors. + + 7. Better transmit/receive error handling. + + 8. The driver now uses zero-copy single buffer management scheme to + improve performance. + + 9. Names of variables were changed to be more consistent. + + 10. + Clean up of auo-negotiation and timer code. + + 11. + Automatic detection and change of PHY on the fly. + + + 4. Tested Environment + + This driver is developed on the following hardware + + o Intel Celeron 336 with SiS 620 (rev 02) chipset + + o SiS 900 (rev 01) and SiS 7016/7014 Fast Ethernet Card + + and tested with these software environments + + o Red Hat Linux version 6.0 + + o Linux kernel version 2.2.13 + + o Netscape version 4.6 + + o NcFTP 3.0.0 beta 18 + + o Samba version 2.0.3 + + + 5. Files in This Package + + In the package you can find these files: + + + sis900-2.2.x.c + Driver source for kernel 2.2.x + + sis900-2.3.x.c + Driver source for kernel 2.3.x + + sis900.h + Header file for both 2.2.x and 2.3.x kernel + + sis900.sgml + Linux-Doc SGML source of the document + + + 6. Installation + + Silicon Integrated System Corp. is cooperating closely with core Linux + Kernel developers. The revisions of SiS 900 driver are distributed by + the usuall channels for kernel tar files and patches. Those kernel tar + files for official kernel and patches for kernel pre-release can be + download at official kernel ftp site + and its mirrors. The 1.06 + revision can be found in kernel version later than 2.3.15 and + pre-2.2.14. If you have no prior experience in networking under + Linux, please read Ethernet HOWTO and Networking HOWTO available from + Linux Documentation Project (LDP). + + The installation procedure are different according to your kernel + versions. + + + 6.1. Kernel version later than 2.2.11 and 2.3.15 + + The driver is bundled in release later than 2.2.11 and 2.3.15 so this + is the most easy case. Be sure you have the appropriate packages for + compiling kernel source. Those packages are listed in + Document/Changes in kernel source distribution. There are two + alternative ways to install the driver + + + 6.1.1. Building the driver as loadable module + + To build the driver as a loadable kernel module you have to + reconfigure the kernel to activate network support by + + + + make config + + + + + Choose "Network Device Support" to "Y" and "Ethernet Support" to "Y". + Then you have to choose "SiS 900 Fast Ethernet Adapter Support" to + "M". + + After reconfiguring the kernel, you can make the driver module by + + + make modules + + + + + The driver should be compiled with no errors. After compiling the + driver, the driver can be installed to proper place by + + + + make modules_install + + + + + Load the driver into kernel by + + + + insmod sis900 + + + + + When loading the driver into memory, some information message can be + view by + + + + dmesg + + + + + or + + + cat /var/log/message + + + + + If the driver is loaded properly you will have messages similar to + this: + + + + sis900.c: v1.06 11/04/99 + eth0: SiS 900 PCI Fast Ethernet at 0xd000, IRQ 10, 00:00:e8:83:7f:a4. + eth0: SiS 900 Internal MII PHY transceiver found at address 1. + eth0: Using SiS 900 Internal MII PHY as default + + + + + showing the version of the driver and the results of probing routine. + + Once the driver is loaded, network can be brought up by + + + + /sbin/ifconfig eth0 IPADDR broadcast BROADCAST netmask NETMASK + + + + + + where IPADDR, BROADCAST, NETMASK are your IP address, broadcast + address and netmask respectively. For more information on how to + configure network interface, please refer to Networking HOWTO. + + The link status is also shown by kernel messages. For example, after + the network interface is activated, you may have the message: + + + + eth0: Media Link On 100mbps full-duplex + + + + + If you try to unplug the twist pair (TP) cable you will get + + + + eth0: Media Link Off + + + + + indicating that the link is failed. + + + 6.1.2. Building the driver into kernel + + If you want to make the driver into kernel, choose "Y" rather than "M" + on "SiS 900 Fast Ethernet Adapter Support" when configuring the + kernel. Build the kernel image in the usual way + + + + make dep + + make clean + + make bzlilo + + + + + Next time the system reboot, you have the driver in memory. + + + 6.2. Earlier Kernel Version in 2.2.x and 2.3.x Series + + Installing the driver for earlier kernels in 2.2.x and 2.3.x series + requires a little bit more work. First you have to copy sis900-2.x.x.c + to /usr/src/linux/drivers/net/ and you have to modify some files + manually (sorry !! no patch available !!) + + in Space.c, add + + + extern int sis900_probe(struct device *dev); + + ... + + #ifdef CONFIG_SIS900 + {sis900_probe,0}, + #endif + + + in Config.in add + + + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + ... //other adapter drivers + tristate 'SiS 900 PCI Fast Ethernet Adapter Support' CONFIG_SIS900 + ... //other adapter drivers + fi + + + + + in Makefile add + + + ifeq ($(CONFIG_SIS900),y) + L_OBJS += sis900.o + else + ifeq ($(CONFIG_SIS900),m) + M_OBJS += sis900.o + endif + endif + + + + + After modifying these files, the driver can be build as described in + the previous section. + + + 7. Known Problems and Bugs + + There are some known problems and bugs. If you find any other bugs + please mail to ollie@sis.com.tw + + 1. AM79C901 HomePNA PHY is not thoroughly tested, there may be some + bugs in the "on the fly" change of transceiver. + + 2. A bug is hidden somewhere in the receive buffer management code, + the bug causes NULL pointer reference in the kernel. This fault is + caught before bad things happen and reported with the message: + + + eth0: NULL pointer encountered in Rx ring, skipping + + + + + which can be viewed with dmesg or cat /var/log/message. + + + 8. Revision History + + + o November 4, 1999, Revision 1.06, Second release, lots of clean up + and optimization. + + o August 8, 1999, Revision 1.05, Initial Public Release + + + 9. Acknowledgements + + This driver was originally derived form Donald Becker's pci-skeleton + and rtl8139 drivers. Donald also provided various suggestion regarded + with improvements made in revision 1.06. + + The 1.05 revision was created by Jim Huang, AMD 79c901 support was + added by Chin-Shan Li. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -u --recursive --new-file v2.2.13/linux/Documentation/networking/sk98lin.txt linux/Documentation/networking/sk98lin.txt --- v2.2.13/linux/Documentation/networking/sk98lin.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/networking/sk98lin.txt Tue Jan 4 10:12:10 2000 @@ -0,0 +1,480 @@ +(C)Copyright 1999 SysKonnect. +=========================================================================== + +sk98lin.txt created 11-Nov-1999 + +Readme File for sk98lin.o v3.02 +SK-NET Gigabit Ethernet Adapter SK-98xx Driver for Linux + +This file contains +(1) OVERVIEW +(2) REQUIRED FILES +(3) INSTALLATION +(4) INCLUSION OF THE ADAPTER AT SYSTEM START +(5) DRIVER PARAMETERS +(6) LARGE FRAME SUPPORT +(7) TROUBLESHOOTING +(8) HISTORY + +=========================================================================== + + + +(1) OVERVIEW +============ + +The sk98lin driver supports the SysKonnect SK-NET Gigabit Ethernet +Adapter SK-98xx family on Linux 2.2.x. +It has been tested with Linux on Intel/x86 and ALPHA machines. +From v3.02 on, the driver is integrated in the linux kernel source. +*** + + +(2) REQUIRED FILES +================== + +The linux kernel source. +No additional files required. +*** + + +(3) INSTALLATION +================ + +The following steps describe the actions that are required to install +the driver and to start it manually. These steps should be carried +out for the initial driver setup. Once confirmed to be ok, they can +be included in the system start which is described in the next +chapter. + +NOTE 1: You must have 'root' access to the system to perform + the following tasks. +NOTE 2: IMPORTANT: In case of problems, please read the section + "Troubleshooting" below. + +1) The driver can either be integrated into the kernel or it can + be compiled as a module. + Select the appropriate option during the kernel configuration. + For use as a module, your kernel must have + 'loadable module support' enabled. + For automatic driver start, you also need 'Kernel module loader' + enabled. + Configure those options, build and install the new kernel. If you + choose to use the driver as a module, do "make modules" and + "make modules_install". + Reboot your system. + +4) Load the module manually by entering: + insmod sk98lin.o + If the SysKonnect SK-98xx adapter is installed in your + computer and you have a /proc filesystem, running the command + 'more /proc/net/dev' should produce an output containing a + line with the following format: + eth0: 0 0 ... + which means that your adapter has been found and initialized. + + NOTE 1: If you have more than one SysKonnect SK-98xx adapter, the + adapters will be listed as 'eth0', 'eth1', 'eth2', etc. + For each adapter, repeat the steps 5) and 6). + NOTE 2: If you have other Ethernet adapters installed, + your SysKonnect SK-98xx adapter can be mapped to 'eth1' or + 'eth2' ... + The module installation message (in system logfile or + on console, depending on /etc/syslog.conf) prints a line + for each adapter that is found, containing the + corresponding 'ethX'. + +5) Select an IP address and assign it to the respective adapter by + entering: + ifconfig eth0 + This causes the adapter to connect to the ethernet. The solitary + yellow LED at the adapter is now active, the link status LED of + the primary port is on and the link status LED of the secondary + port (on dual port adapters) is blinking (only if the laters are + connected to a switch or hub). + You will also get a status message on the console saying + "ethX: network connection up using port Y" and indicating + the selected connection parameters. + + NOTE: If you are in doubt about IP addresses, ask your network + administrator for assistance. + +6) Your adapter should now be fully operational. + Use 'ping ' to verify the connection to other + computers on your network. + By entering 'ifconfig', you can check the number of packets sent + and received by your adapter and additional some other information + regarding the adapter configuration. + +7) The driver module can be stopped and unloaded using the following + commands: + ifconfig eth0 down + rmmod sk98lin +*** + + +(4) INCLUSION OF THE ADAPTER AT SYSTEM START +============================================ + +Since a large number of different Linux distributions are +available, we are unable to describe a general installation procedure +for the driver module. +Because the driver is now integrated in the kernel, installation should +be easy, using the standard mechanism of your distribution. +Refer to the distribution's manual for installation of ethernet adapters. +*** + + +(5) DRIVER PARAMETERS +===================== + +Parameters can be set at the command line while loading the +module with 'insmod'. The configuration tools of some distributions +can also give parameters to the driver module. +If you use the kernel module loader, you can set driver parameters +in the file /etc/conf.modules. Insert a line of the form: + +options sk98lin ... + +For "...", use the same syntax as described below for the command +line paramaters of insmod. +You either have to reboot your computer or unload and reload +the driver to activate the new parameters. +The syntax of the driver parameters is: + +insmod sk98lin parameter=value1[,value2[,value3...]] + +value1 is for the first adapter, value2 for the second one etc. +All Parameters are case sensitive, so write them exactly as +shown below. + +Sample: Suppose you have two adapters. You want to set AutoNegotiation + on Port A of the first adapter to ON and on Port A of the + second adapter to OFF. + You also want to set DuplexCapabilities on Port A of the first + adapter to FULL and on Port A of the second adapter to HALF. + You must enter: + + insmod sk98lin.o AutoNeg_A=On,Off DupCap_A=Full,Half + +NOTE: The number of adapters that can be configured this way is + limited in the driver (file skge.c, constant SK_MAX_CARD_PARAM). + The current limit is 16. If you happen to install + more adapters, adjust this and recompile. + + +5.1 Per-Port Parameters +----------------------- +Those setting are available for each port on the adapter. +In the following description, '?' stands for the port for +which you set the parameter (A or B). + +- Auto Negotiation + Parameter: AutoNeg_? + Values: On, Off, Sense + Default: Sense + + The "Sense"-mode finds out automatically whether the link + partner supports autonegotiation or not. + +- Duplex Capabilities + Parameter: DupCap_? + Values: Half, Full, Both + Default: Both + + This parameters is relevant only if autonegotiation for + this port is not "Sense". If autonegotiation is "On", all + three values are possible. If it is "Off", only "Full" and + "Half" are allowed. + It is usefull if your link partner does not support all + possible combinations. + +- Flow Control + Parameter: FlowCtrl_? + Values: Sym, SymOrRem, LocSend, None + Default: SymOrRem + + This parameter can be used to set the flow control capabilities + that the port reports during autonegotiation. + The meaning of the different modes is: +-- Sym = Symetric: both link partners are allowed to send PAUSE frames +-- SymOrRem = SymetricOrRemote: both or only remote partner are allowed + to send PAUSE frames +-- LocSend = LocalSend: only local link partner is allowed to send + PAUSE frames +-- None: no link partner is allowed to send PAUSE frames + + NOTE: This parameter is ignored if autonegotiation is set to "Off". + +- Role in Master-Slave-Negotiation (1000Base-T only). + Parameter: Role_? + Values: Auto, Master, Slave + Default: Auto + + This parameter is only valid for the SK-9821 and SK-9822 adapters. + For two 1000Base-T ports to communicate, one must take the role as + master (providing timing information), while the other must be slave. + Normally, this is negotiated between the two ports during link + establishment. If this should ever fail, you can force a port to a + specific setting with this parameter. + + +5.2 Per-Adapter Parameters +-------------------------- + +- Preferred Port + Parameter: PrefPort + Values: A, B + Default: A + + This is used to force the preferred port to A or B (on two-port NICs). + The preferred port is the one that is used if both are detected as + fully functional. + +- RLMT (Redundant Link Management Technology) Mode + Parameter: RlmtMode + Values: CheckLinkState,CheckLocalPort, CheckSeg + Default: CheckLinkState + + RLMT (the driver part that decides which port to use) knows three + ways of checking if a port is available for use: + +-- CheckLinkState = Check link state only: RLMT uses the link state + reported by the adapter hardware for each individual port to determine + whether a port can be used for all network traffic or not. + +-- CheckLocalPort - Check other port on adapter: RLMT sends test frames + from each port to each other port and checks if they are received by + the other port, respectively. Thus, the ports must be connected to the + network such that LLC test frames can be exchanged between them + (i.e. there must be no routers between the ports). + +-- CheckSeg - Check other port and segmentation: RLMT checks the other port + and in addition requests information from the Gigabit Ethernet + switch next to each port to see if the network is segmented between + the ports. Thus, this mode is only to be used if you have Gigabit + Ethernet switches installed in your network that have been configured + to use the Spanning Tree protocol. + + NOTE: The modes CheckLocalPort and CheckSeg are meant to operate in + configurations where a network path between the ports on one + adapter exists. Especially, they are not designed to work where + adapters are connected back-to-back. +*** + + +(6) LARGE FRAME SUPPORT +======================= + +Large frames (also called jumbo frames) are now supported by the +driver. This can result in a greatly improved throughput if +transfering large amounts of data. +To enable large frames, set the MTU (maximum transfer unit) +of the interface to the value you wish (up to 9000). The command +for this is: + ifconfig eth0 mtu 9000 +This will only work if you have two adapters connected back-to-back +or if you use a switch that supports large frames. When using a +switch, it should be configured to allow large frames, without +autonegotiating for them. +The setting must be done on all adapters that can be reached by +the large frames. If one adapter is not set to receive large frames, +it will simply drop them. + +NOTE: If you look at the statistics (with netstat) in large frame + mode while there is traffic on the net, you will see the + RX error counter go up. This is because the adapter hardware + counts received large frames as errors, although they are + received correctly. So ignore this counter in that case. + +You can switch back to the standard ethernet frame size with: + ifconfig eth0 mtu 1500 +*** + + +(7) TROUBLESHOOTING +=================== + +If you run into problems during installation, check those items: + +Problem: The SK-98xx adapter can not be found by the driver. +Reason: Look in /proc/pci for the following entry: + 'Ethernet controller: SysKonnect SK-98xx ...' + If this entry exists, then the SK-98xx adapter has been + found by the system and should be able to be used. + If this entry does not exist or if the file '/proc/pci' + is not there, then you may have a hardware problem or PCI + support may not be enabled in your kernel. + The adapter can be checked using the diagnostic program + which is available from the SysKonnect web site: + www.syskonnect.de + Some COMPAQ machines have a problem with PCI under + Linux. This is described in the 'PCI howto' document + (included in some distributions or available from the + www, e.g. at 'www.linux.org'). This might be fixed in the + 2.2.x kernel series (I've not tested it). + +Problem: Programs such as 'ifconfig' or 'route' can not be found or + you get an error message 'Operation not permitted'. +Reason: You are not logged in as user 'root'. Logout and + login as root or change to root via 'su'. + +Problem: Using the command 'ping
', you get a message + "ping: sendto: Network is unreachable". +Reason: Your route is not set up correct. + If you are using RedHat, you probably forgot + to set up the route in 'network configuration'. + Check the existing routes with the 'route' command + and check if there is an entry for 'eth0' and if + it is correct. + +Problem: The driver can be started, the adapter is connected + to the network, but you can not receive or transmit + any packet; e.g. 'ping' does not work. +Reason: You have an incorrect route in your routing table. + Check the routing table with the command 'route' and + read the manual pages about route ('man route'). +NOTE: Although the 2.2.x kernel versions generate the routing + entry automatically, you may have problems of this kind + here, too. We found a case where the driver started correct + at system boot, but after removing and reloading the driver, + the route of the adapter's network pointed to the 'dummy0' + device and had to be corrected manually. + +Problem: You want to use your computer as a router between + multiple IP subnetworks (using multiple adapters), but + you can not reach computers in other subnetworks. +Reason: Either the router's kernel is not configured for IP + forwarding or there is a problem with the routing table + and gateway configuration in at least one of the + computers. + +Problem: At the start of the driver, you get an error message: + "eth0: -- ERROR -- + Class: internal Software error + Nr: 0xcc + Msg: SkGeInitPort() cannot init running ports" +Reason: You are using a driver compiled for single processor + machines on an multiprocessor machine with SMP (Symetric + MultiProcessor) kernel. + Configure your kernel appropriate and recompile the kernel or + the modules. + +If your problem is not listed here, please contact SysKonnect's technical +support for help (linux@syskonnect.de). +When contacting our technical support, please ensure that the +following information is available: +- System Manufacturer and Model +- Boards in your system +- Distribution +- Kernel version +*** + + +(8) HISTORY +=========== + +VERSION 3.02 +Problems fixed: +- None +New Features: +- Integration in linux kernel source. +Known limitations: +- None + +VERSION 3.02 +Problems fixed: +- None +New Features: +- Full source release +Known limitations: +- None + +VERSION 3.00 +Problems fixed: +- None +New Features: +- Support for 1000Base-T adapters (SK-9821 and SK-9822) +Known limitations: +- None + +VERSION 1.07 +Problems fixed: +- RlmtMode parameter value strings were wrong (#10437) +- Driver sent too many RLMT frames (#10439) +- Driver did not recognize network segmentation (#10440) +- RLMT switched too often on segmented network (#10441) +Known limitations: +- None + +VERSION 1.06 +Problems fixed: +- System panic'ed after some time when running with + RlmtMode=CheckOtherLink or RlmtMode=CheckSeg (#10421) + Panic message: "Kernel panic: skput: over ... dev: eth0" +- Driver did not switch back to default port when connected + back-to-back (#10422). +Changes: +- RlmtMode parameter names have changed +New features: +- There is now a version for ALPHA processors +Known limitations: +- None + +VERSION 1.05 +Problems fixed: +- Driver failed to load on kernels with version information + for module symbols enabled +Known limitations: +- None + +VERSION 1.04 +Problems fixed: +- Large frame support does work now (no autonegotiation + support for large frames, just manually selectable) +New Features: +- Receive checksumming in hardware +- Performance optimizations + Some numbers (on two PII-400 machines, back-to-back): + netpipe: 300 MBit/sec, with large frames: 470 MBit/sec + ttcp: 38 MByte/sec, with large frames: 60 MByte/sec + ttcp (UDP send): 66 MByte/sec, with large frames: 106 MByte/sec +Known limitations: +- None + +VERSION 1.03 +Problems fixed: +- Unloading with "rmmod" caused segmentation fault (#10415) +- The link LED flickered from time to time, if no link was + established (#10402) +- Installation problems with RedHat 6.0 (#10409) +New Features: +- Connection state ouput at "network connection up" +Known limitations: +- None + +VERSION 1.02 +Problems fixed: +- Failed with multiple adapters +- Failed with Single Port adapters +- Startup string was only displayed if adapter found +- No link could be established on certain switches when the switches were + rebooted. (#10377) +Known limitations: +- Segmentation fault at "rmmod" with kernel 2.2.3 on some machines + +VERSION 1.01 +Problems fixed: +- Sensor status was not set back to 'ok' after 'warning/error'. (#10386) +Changes: +- improved parallelism in driver + +VERSION 1.00 +Known limitations: +- not tested with all kernel versions (I don't have that much time :-) +- only x86 version available (if you need others, ask for it) +- source code not completely available + +***End of Readme File*** + + diff -u --recursive --new-file v2.2.13/linux/Documentation/networking/tlan.txt linux/Documentation/networking/tlan.txt --- v2.2.13/linux/Documentation/networking/tlan.txt Fri Jul 24 11:15:05 1998 +++ linux/Documentation/networking/tlan.txt Tue Jan 4 10:12:10 2000 @@ -1,15 +1,26 @@ -TLAN driver for Linux, version 1.0 -README -Well, I'm back. The TLAN driver seems pretty stable, so I'm -declaring this cycle of development finished, and calling the -driver 1.0. I will, of course continue to work on improving -the driver, and work towards a 2.0 release. +I haven't had any time to do anything for a long time, and this isn't +likely to change. So there's a driver here for anyone looking to +carry forward a project :) + +For those who are looking for help, I can't. I haven't looked at +a kernel since the early 2.0 series, so I won't know what's going on. +Your best chance at help would be joining the TLAN mailing list and +posting your question there. + +You can join by sending "subscribe tlan" in the body of an email to +majordomo@vuser.vu.union.edu. + +Thanks to those who have (and who will ;) put work in to keep the TLAN +driver working as the kernel moves on. James james@sovereign.org + +TLAN driver for Linux, version 1.0 +README I. Supported Devices. diff -u --recursive --new-file v2.2.13/linux/Documentation/networking/wan-router.txt linux/Documentation/networking/wan-router.txt --- v2.2.13/linux/Documentation/networking/wan-router.txt Wed May 20 18:54:34 1998 +++ linux/Documentation/networking/wan-router.txt Tue Jan 4 10:12:10 2000 @@ -1,19 +1,26 @@ ------------------------------------------------------------------------------ WAN Router for Linux Operating System ------------------------------------------------------------------------------ +Version 2.1.1 - Nov 08, 1999 +Version 2.0.8 - Nov 02, 1999 +Version 2.0.7 - Aug 26, 1999 +Version 2.0.6 - Aug 17, 1999 +Version 2.0.5 - Aug 12, 1999 +Version 2.0.4 - Nov 26, 1998 +Version 2.0.3 - Aug 25, 1998 +Version 2.0.2 - Dec 09, 1997 Version 2.0.1 - Nov 28, 1997 Version 2.0.0 - Nov 06, 1997 Version 1.0.3 - June 3, 1997 Version 1.0.1 - January 30, 1997 -Author: Jaspreet Singh - Gene Kozin -Copyright (c) 1995-1997 Sangoma Technologies Inc. +Author: Nenad Corbic +Copyright (c) 1995-1999 Sangoma Technologies Inc. ------------------------------------------------------------------------------ WARNING: This Version of WANPIPE supports only the S508 and S508/FT1 cards. IF YOU OWN A S502E OR A S508 CARD THEN PLEASE CONTACT SANGOMA TECHNOLOGIES FOR -AN UPGRADE. +AN UPGRADE. ONLY THE BiSYNC STREAMING CODE IS SUPPORTED ON S502E/S503 cards. INTRODUCTION @@ -129,11 +136,55 @@ REVISION HISTORY +2.1.1 Nov 09, 1999 - New code for S514PCI card + - Completely redesigned drivers + fully tested and optimized. + +2.0.8 Nov 02, 1999 - Fixed up the X25API code. + - Clear call bug fixed.i + - Eanbled driver for multi-card + operation. + +2.0.7 Aug 26, 1999 - Merged X25API code into WANPIPE. + - Fixed a memeory leak for X25API + - Updated the X25API code for 2.2.X kernels. + - Improved NEM handling. + +2.0.6 Aug 17, 1999 - Kernel patch works for both 2.2.10 and 2.2.11 kernels + - Fixed up 2.0.5 installation bugs + - No functional difference between 2.0.6 and 2.0.5 + +2.0.5 Aug 12, 1999 - NEW PPP, interrupt drive code + - NEW X25 Xpipmon debugger + - Comments added to setup scripts + - Numerous bug fixes + +2.0.4 Nov 26, 1998 - NEW Cisco Dual Port support. + - NEW support for BiSync Streaming API. + - NEW support for HDLC (LAPB) API. + - WANPIPE provides an API for application + development using the BSD socket interface. + +2.0.3 Aug 25, 1998 - NEW support for Cisco HDLC, with cpipemon + utility for monitoring + - CIR support for Frame-relay + - Support for PAP and CHAP for ppp has been + implemented + - Dynamic IP assignment for PPP + - Multiple channel IPX support for Frame-relay + and X25 + - Inverse Arp support for Frame-relay + - FT1 Configuration utility for linux + - Man Pages for router.conf, router, sdladump, + cfgft1, fpipemon, ppipemon and cpipemon + +2.0.2 Dev 09, 1997 - Implemented PAP and CHAP for ppp. + 2.0.1 Nov 28, 1997 - Protection of "enable_irq()" while "disable_irq()" has been enabled from any other routine (for Frame Relay, PPP and X25). - - Added additional Stats for Fpipemon and Ppipemon - Improved Load Sharing for multiple boards. - + - Added additional Stats for Fpipemon and Ppipemon + - Improved Load Sharing for multiple boards. 2.0.0 Nov 07, 1997 - Implemented protection of RACE conditions by critical flags for FRAME RELAY and PPP. @@ -173,7 +224,7 @@ 1.0.1 January 30, 1997 - Implemented user-readable status and statistics - via /proc file system + via /proc filesystem 1.0.0 December 31, 1996 diff -u --recursive --new-file v2.2.13/linux/Documentation/networking/wanpipe.txt linux/Documentation/networking/wanpipe.txt --- v2.2.13/linux/Documentation/networking/wanpipe.txt Tue Apr 28 14:22:04 1998 +++ linux/Documentation/networking/wanpipe.txt Tue Jan 4 10:12:10 2000 @@ -1,36 +1,23 @@ ------------------------------------------------------------------------------ -WANPIPE(tm) Multiprotocol WAN Driver for Linux WAN Router +Linux WAN Router Utilities Package ------------------------------------------------------------------------------ -Release 4.1 -November 17, 1997 -Author: Jaspreet Singh -Copyright (c) 1995-1997 Sangoma Technologies Inc. +Version 2.1.1 +Nov 08, 1999 +Author: Nenad Corbic +Copyright (c) 1995-1999 Sangoma Technologies Inc. ------------------------------------------------------------------------------ INTRODUCTION -WANPIPE(tm) is a family of intelligent multiprotocol WAN communication adapters -for personal computers (ISA bus) designed to provide PC connectivity to -various communication links, such as leased lines and public data networks, at -speeds up to T1/E1 using a variety of synchronous communications protocols, -including frame relay, PPP, X.25, SDLC, etc. - -WANPIPE driver together with Linux WAN Router module allows you to build a -relatively inexpensive, yet high-performance multiprotocol WAN router. For -more information about the Linux WAN Router please read the file -Documentation/networking/wan-router.txt. You must also obtain the WAN Tools -package to be able to use the Linux WAN Router and WANPIPE driver. The package -is available via the Internet from Sangoma Technologies' anonymous FTP server: - - ftp.sangoma.com/pub/linux/wantools-X.Y.Z.tgz - or - ftp.sangoma.com/pub/linux/wanpipe-X.Y.Z.tgz - -The names of the packages differ only due to naming convention. The -functionality of wantools and wanpipe packages are the same. The latest -version of the WAN Drivers is wanpipe-2.0.0. +This is a set of utilities and shell scripts you need in order to be able to +use Linux kernel-level WAN Router. Please read WAN Router User's manual +(router.txt) and WANPIPE driver documentation found in /usr/lib/router/doc +directory for installation and configuration instructions. -For technical questions and/or comments please e-mail to jaspreet@sangoma.com. +You can find the latest version of this software in /pub/linux directory on +Sangoma Technologies' anonymous FTP server (ftp.sangoma.com). + +For technical questions and/or comments please e-mail to ncorbic@sangoma.com. For general inquiries please contact Sangoma Technologies Inc. by Hotline: 1-800-388-2475 (USA and Canada, toll free) @@ -57,117 +44,214 @@ -NEW IN THIS RELEASE - - o This Version of WANPIPE supports only the S508 and S508/FT1 cards. IF YOU - OWN A S502E OR A S508 CARD THEN PLEASE CONTACT SANGOMA TECHNOLOGIES FOR AN - UPGRADE. - o Protection of "enable_irq()" while "disable_irq()" has been enabled from - any other routine (for Frame Relay, PPP and X25). - o Added additional Stats for Fpipemon and Ppipemon. - o Improved Load Sharing for multiple boards - - -FILE LIST - -drivers/net: - README.wanpipe This file - sdladrv.c SDLA support module source code - sdla_fr.c SDLA Frame Relay source code - sdla_ppp.c SDLA PPP source code - sdla_x25.c SDLA X.25 source code - sdlamain.c SDLA support source code - -include/linux: - sdla_x25.h SDLA X.25 firmware API definitions - sdla_fr.h SDLA frame relay firmware API definitions - sdla_ppp.h SDLA PPP firmware API definitions - wanpipe.h WANPIPE API definitions - sdladrv.h SDLA support module API definitions - sdlasfm.h SDLA firmware module definitions - router.h - - -REVISION HISTORY - -4.1 November 28, 1997 - o Protection of "enable_irq()" while "disable_irq()" has been enabled - from any other routine (for Frame Relay, PPP and X25). - o Added additional Stats for Fpipemon and Ppipemon - o Improved Load Sharing for multiple boards - - -4.0 November 06, 1997 - o Implemented better protection of RACE conditions by critical flags for - FRAME RELAY, PPP and X25. - o DLCI List interrupt mode implemented for DLCI specific CIR. - o IPX support for FRAME RELAY, PPP and X25. - o IPX Server Support (MARS) for FRAME RELAY, PPP and X25. - o More driver specific stats included. - o MULTICAST for FRAME RELAY and PPP. - -3.1.0 January 30, 1997 - - o Implemented IOCTL for executing adapter commands. - o Fixed a bug in frame relay code causing driver configured as a FR - switch to be stuck in WAN_DISCONNECTED mode. - -3.0.0 December 31, 1996 - - o Uses Linux WAN Router interface - o Added support for X.25 routing - o Miscellaneous bug fixes and performance improvements - -2.4.1 December 18, 1996 +ACKNOWLEDGEMENTS - o Added support for LMI and Q.933 frame relay link management +This product is based on the WANPIPE(tm) Multiprotocol WAN Router developed +by Sangoma Technologies Inc. for Linux 2.2.x. Success of the WANPIPE +together with the next major release of Linux kernel in summer 1996 commanded +adequate changes to the WANPIPE code to take full advantage of new Linux +features. + +Instead of continuing developing proprietary interface tied to Sangoma WAN +cards, we decided to separate all hardware-independent code into a separate +module and defined two levels of interfaces - one for user-level applications +and another for kernel-level WAN drivers. WANPIPE is now implemented as a +WAN driver compliant with the WAN Link Driver interface. Also a general +purpose WAN configuration utility and a set of shell scripts was developed to +support WAN router at the user level. + +Many useful ideas concerning hardware-independent interface implementation +were given by Mike McLagan and his implementation +of the Frame Relay router and drivers for Sangoma cards (dlci/sdla). + +With the new implementation of the APIs being incorporated into the WANPIPE, +a special thank goes to Alan Cox in providing insight into BSD sockets. + +Special thanks to all the WANPIPE users who performed field-testing, reported +bugs and made valuable comments and suggestions that help us to improve this +product. -2.3.0 October 17, 1996 - o All shell scripts use meta-configuration file - o Miscellaneous bug fixes -2.2.0 July 16, 1996 +NEW IN THIS RELEASE - o Compatible with Linux 2.0 - o Added uninstall script - o User's Manual is available in HTML format +o Renamed startup script to wanrouter +o Option to turn off/on each router + separately +o New source directory /usr/lib/wanrouter +o New PPP driver +o X25 is not supported in this release + + +PRODUCT COMPONENTS AND RELATED FILES + +/etc: + wanpipe1.conf default router configuration file + wanrouter.rc meta-configuration file (used by the Setup script) + +/lib/modules/X.Y.Z/misc: + wanrouter.o router kernel loadable module + +/lib/modules/X.Y.Z/net: + sdladrv.o Sangoma SDLA support module + wanpipe.o Sangoma WANPIPE(tm) driver module + +/proc/net/wanrouter + Config reads current router configuration + Status reads current router status + {name} reads WAN driver statistics + +/usr/sbin: + wanrouter router start-up script + wanconfig router configuration utility + sdladump WANPIPE adapter memory dump utility + fpipemon Monitor for Frame Relay + cpipemon Monitor for Cisco HDLC + +/usr/lib/wanrouter: + README this file + COPYING GNU General Public License + Setup installation script + Configure configuration script + Filelist distribution definition file + +/usr/lib/wanrouter/doc: + WANPIPE_USER_MANUAL.txt WAN Router User's Manual + WANPIPE_CONFIG.txt WAN Configuration Manual + +/usr/lib/wanrouter/interfaces: + * interface configuration files (TCP/IP configuration) + +/usr/lib/wanrouter/patches: + wanrouter-22.gz patch for Linux kernel 2.2.10 and 2.2.11 + (compatible for all 2.2.X kernels) + wanrouter-20.gz patch for Linux kernel 2.0.36 + + Fix_2.2.11.gz patch to fix the 2.2.11 kernel so other patches + can be applied properly. + +/usr/lib/wanrouter/samples: + interface sample interface configuration file + wanpipe1.cpri CHDLC primary port + wanpipe2.csec CHDLC secondary port + wanpipe1.fr Frame Relay protocol + wanpipe1.ppp PPP protocol ) + wanrouter.rc sample meta-configuration file -2.1.0 June 20, 1996 +/usr/lib/wanrouter/src: + * wan-tools source code - o Added support for synchronous PPP - o Added support for S503 adapter - o Added API for executing adapter commands - o Fixed a re-entrancy problem in frame relay driver - o Changed interface between SDLA driver and protocol support modules - o Updated frame relay firmware +/usr/include/linux: + wanrouter.h router API definitions + wanpipe.h WANPIPE API definitions + sdladrv.h SDLA support module API definitions + sdlasfm.h SDLA firmware module definitions -2.0.0 May 1, 1996 +/usr/src/linux/net/router: + * router source code - o Added interactive installation and configuration scripts - o Added System V-style start-up script - o Added dynamic memory window address selection in SDLA driver - o Miscellaneous bug fixes in SDLA driver - o Updated S508 frame relay firmware - o Changed SFM file format +/var/log: + wanrouter router start-up log (created by the Setup script) -1.0.0 February 12, 1996 +/var/lock: + wanrouter router lock file (created by the Setup script) - o Final release - o Added support for Linux 1.3 - o Updated S508 frame relay firmware +/usr/lib/wanrouter/wanpipe: + fr514.sfm Frame relay firmware for Sangoma S508/S514 card + cdual514.sfm Dual Port Cisco HDLC firmware for Sangoma S508/S514 card + ppp514.sfm PPP Firmware for Sangoma S508 and S514 cards -0.9.0 December 21, 1995 - o Added SNAP encapsulation for routed frames - o Added support for the frame relay switch emulation mode - o Added support for S508 adapter - o Added capability to autodetect adapter type - o Miscellaneous bug fixes in SDLA and frame relay drivers +REVISION HISTORY -0.1.0 October 12, 1995 +1.0.0 December 31, 1996 Initial version - o Initial version +1.0.1 January 30, 1997 Status and statistics can be read via /proc + filesystem entries. ->>>>>>> END OF README <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +1.0.2 April 30, 1997 Added UDP management via monitors. +1.0.3 June 3, 1997 UDP management for multiple boards using Frame + Relay and PPP + Enabled continuous transmission of Configure + Request Packet for PPP (for 508 only) + Connection Timeout for PPP changed from 900 to 0 + Flow Control Problem fixed for Frame Relay + +1.0.4 July 10, 1997 S508/FT1 monitoring capability in fpipemon and + ppipemon utilities. + Configurable TTL for UDP packets. + Multicast and Broadcast IP source addresses are + silently discarded. + +1.0.5 July 28, 1997 Configurable T391,T392,N391,N392,N393 for Frame + Relay in router.conf. + Configurable Memory Address through router.conf + for Frame Relay, PPP and X.25. (commenting this + out enables auto-detection). + Fixed freeing up received buffers using kfree() + for Frame Relay and X.25. + Protect sdla_peek() by calling save_flags(), + cli() and restore_flags(). + Changed number of Trace elements from 32 to 20 + Added DLCI specific data monitoring in FPIPEMON. +2.0.0 Nov 07, 1997 Implemented protection of RACE conditions by + critical flags for FRAME RELAY and PPP. + DLCI List interrupt mode implemented. + IPX support in FRAME RELAY and PPP. + IPX Server Support (MARS) + More driver specific stats included in FPIPEMON + and PIPEMON. + +2.0.1 Nov 28, 1997 Bug Fixes for version 2.0.0. + Protection of "enable_irq()" while + "disable_irq()" has been enabled from any other + routine (for Frame Relay, PPP and X25). + Added additional Stats for Fpipemon and Ppipemon + Improved Load Sharing for multiple boards + +2.0.2 Dec 09, 1997 Support for PAP and CHAP for ppp has been + implemented. + +2.0.3 Aug 15, 1998 New release supporting Cisco HDLC, CIR for Frame + relay, Dynamic IP assignment for PPP and Inverse + Arp support for Frame-relay. Man Pages are + included for better support and a new utility + for configuring FT1 cards. + +2.0.4 Dec 09, 1998 Dual Port support for Cisco HDLC. + Support for HDLC (LAPB) API. + Supports BiSync Streaming code for S502E + and S503 cards. + Support for Streaming HDLC API. + Provides a BSD socket interface for + creating applications using BiSync + streaming. + +2.0.5 Aug 04, 1999 CHDLC initializatin bug fix. + PPP interrupt driven driver: + Fix to the PPP line hangup problem. + New PPP firmware + Added comments to the startup SYSTEM ERROR messages + Xpipemon debugging application for the X25 protocol + New USER_MANUAL.txt + Fixed the odd boundary 4byte writes to the board. + BiSync Streaming code has been taken out. + Available as a patch. + Streaming HDLC API has been taken out. + Available as a patch. + +2.0.6 Aug 17, 1999 Increased debugging in statup scripts + Fixed insallation bugs from 2.0.5 + Kernel patch works for both 2.2.10 and 2.2.11 kernels. + There is no functional difference between the two packages + +2.0.7 Aug 26, 1999 o Merged X25API code into WANPIPE. + o Fixed a memeory leak for X25API + o Updated the X25API code for 2.2.X kernels. + o Improved NEM handling. + +2.1.0 Oct 25, 1999 o New code for S514 PCI Card + o New CHDLC and Frame Relay drivers + o PPP and X25 are not supported in this release +>>>>>> END OF README <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< diff -u --recursive --new-file v2.2.13/linux/Documentation/oops-tracing.txt linux/Documentation/oops-tracing.txt --- v2.2.13/linux/Documentation/oops-tracing.txt Tue Jan 5 11:14:24 1999 +++ linux/Documentation/oops-tracing.txt Tue Jan 4 10:12:10 2000 @@ -15,6 +15,37 @@ linux-kernel@vger.rutgers.edu. Thanks for your help in making Linux as stable as humanly possible. +Where is the_oops.txt? +---------------------- + +Normally the Oops text is read from the kernel buffers by klogd and +handed to syslogd which writes it to a syslog file, typically +/var/log/messages (depends on /etc/syslog.conf). Sometimes klogd dies, +in which case you can run dmesg > file to read the data from the kernel +buffers and save it. Or you can cat /proc/kmsg > file, however you +have to break in to stop the transfer, kmsg is a "never ending file". +If the machine has crashed so badly that you cannot enter commands or +the disk is not available then you have three options :- + +(1) Hand copy the text from the screen and type it in after the machine + has restarted. Messy but it is the only option if you have not + planned for a crash. + +(2) Boot with a serial console (see Documentation/serial-console.txt), + run a null modem to a second machine and capture the output there + using your favourite communication program. Minicom works well. + +(3) Patch the kernel with one of the crash dump patches. These save + data to a floppy disk or video rom or a swap partition. None of + these are standard kernel patches so you have to find and apply + them yourself. Search kernel archives for kmsgdump, lkcd and + oops+smram. + +No matter how you capture the log output, feed the resulting file to +ksymoops along with /proc/ksyms and /proc/modules that applied at the +time of the crash. /var/log/ksymoops can be useful to capture the +latter, man ksymoops for details. + Full Information ---------------- diff -u --recursive --new-file v2.2.13/linux/Documentation/pci.txt linux/Documentation/pci.txt --- v2.2.13/linux/Documentation/pci.txt Tue Apr 28 14:22:04 1998 +++ linux/Documentation/pci.txt Tue Jan 4 10:12:10 2000 @@ -51,6 +51,30 @@ config space. You should use the values in the pci_dev structure as they might have been remapped by the kernel. + If your PCI device uses PCI I/O space, you need to use the check_region(), +request_region() and release_region() routines. These prevent devices from +having conflicting I/O regions. You access your registers using the inb(), +inw(), inl(), outb(), outw(), or outl() routines passing the value of +(struct pci_dev *) dev->base_address[] masked by PCI_BASE_ADDRESS_IO_MASK +as the base address of your registers. + + If your PCI device uses PCI memory space, use ioremap() to create a cookie +mapping to your PCI device. The mask (struct pci_dev *) dev->base_address[] +with PCI_BASE_ADDRESS_MEM_MASK before passing it into ioremap(). This cookie +is passed to the readb(), readw(), readl(), writeb(), writew(), and writel() +routines when accessing PCI space. You must always use these routines when +accessing PCI space from the kernel. Not all architectures allow direct access +to PCI memory space from the kernel. + + The IO-mapping.txt file has information about converting between the +various address spaces. People writing DMA device drivers should pay special +attention to this information. + + PCI interrupt routines are always SA_SHIRQ and should use the value from +(struct pci_dev *) dev->irq field for the interrupt number passed into +request_irq(). Since it is a shared interrupt, you must also always pass a +unique dev_id to request_irq(). + 4. Obsolete functions ~~~~~~~~~~~~~~~~~~~~~ is obsolete and should not be included in new code. diff -u --recursive --new-file v2.2.13/linux/Documentation/proc.txt linux/Documentation/proc.txt --- v2.2.13/linux/Documentation/proc.txt Sat Feb 6 12:46:20 1999 +++ linux/Documentation/proc.txt Tue Jan 4 10:12:10 2000 @@ -1041,6 +1041,21 @@ IP fragmentation settings +ip_always_defrag + Replaces the former Kernel-Configuration option: + CONFIG_IP_ALWAYS_DEFRAG + All incoming fragments (parts of IP packets + that arose when some host between origin and destination decided + that the packets were too large and cut them into pieces) will be + reassembled (defragmented) before being processed, even if they are + about to be forwarded. + + Only say Y here if running either a firewall that is the sole link + to your network or a transparent proxy; never ever say Y here for a + normal router or host. + + This is automagically enabled when enabling masquerading. + ipfrag_high_trash and ipfrag_low_trash Maximum memory used to reassemble IP fragments. When ipfrag_high_thresh bytes of memory is allocated for this purpose, @@ -1176,6 +1191,14 @@ secure_redirects Accept ICMP redirect messages only for gateways, listed in default gateway list. Enabled by default. + +hidden + Hide addresses attached to this device from another devices. + Such addresses will never be selected by source address autoselection + mechanism, host does not answer broadcast ARP requests for them, + does not announce it as source address of ARP requests, but they + are still reachable via IP. This flag is activated only if it is + enabled both in specific device section and in "all" section. shared_media If it is not set the kernel does not assume that different subnets diff -u --recursive --new-file v2.2.13/linux/Documentation/s390/DASD linux/Documentation/s390/DASD --- v2.2.13/linux/Documentation/s390/DASD Wed Dec 31 16:00:00 1969 +++ linux/Documentation/s390/DASD Tue Jan 4 10:12:10 2000 @@ -0,0 +1,73 @@ +DASD device driver + +S/390's disk devices (DASDs) are managed by Linux via the DASD device +driver. It is valid for all types of DASDs and represents them to +Linux as block devices, namely "dasd". Currently the DASD driver uses a +single major number (94) and 4 minor numbers per volume (1 for the +physical volume and 3 for partitions). With respect to partitions see +below. Thus you may have up to 64 DASD devices in your system. + +The kernel parameter 'dasd=from-to,...' may be issued arbitrary times +in the kernel's parameter line or not at all. The 'from' and 'to' +parameters are to be given in hexadecimal notation without a leading +0x. +If you supply kernel parameters the different instances are processed +in order of appearance and a minor number is reserved for any device +covered by the supplied range up to 64 volumes. Additional DASDs are +ignored. If you do not supply the 'dasd=' kernel parameter at all, the +DASD driver registers all supported DASDs of your system to a minor +number in ascending order of the subchannel number. + +The driver currently supports ECKD-devices and there are stubs for +support of the FBA and CKD architectures. For the FBA architecture +only some smart data structures are missing to make the support +complete. +We performed our testing on 3380 and 3390 type disks of different +sizes, under VM and on the bare hardware (LPAR), using internal disks +of the multiprise as well as a RAMAC virtual array. Disks exported by +an Enterprise Storage Server (Seascape) should work fine as well. + +We currently implement one partition per volume, which is the whole +volume, skipping the first blocks up to the volume label. These are +reserved for IPL records and IBM's volume label to assure +accessability of the DASD from other OSs. In a later stage we will +provide support of partitions, maybe VTOC oriented or using a kind of +partition table in the label record. + +USAGE + +-Low-level format (?CKD only) +For using an ECKD-DASD as a Linux harddisk you have to low-level +format the tracks by issueing the BLKDASDFORMAT-ioctl on that +device. This will erase any data on that volume including IBM volume +labels, VTOCs etc. The ioctl may take a 'struct format_data *' or +'NULL' as an argument. +typedef struct { + int start_unit; + int stop_unit; + int blksize; +} format_data_t; +When a NULL argument is passed to the BLKDASDFORMAT ioctl the whole +disk is formatted to a blocksize of 1024 bytes. Otherwise start_unit +and stop_unit are the first and last track to be formatted. If +stop_unit is -1 it implies that the DASD is formatted from start_unit +up to the last track. blksize can be any power of two between 512 and +4096. We recommend no blksize lower than 1024 because the ext2fs uses +1kB blocks anyway and you gain approx. 50% of capacity increasing your +blksize from 512 byte to 1kB. + +-Make a filesystem +Then you can mk??fs the filesystem of your choice on that volume or +partition. For reasons of sanity you should build your filesystem on +the partition /dev/dd?1 instead of the whole volume. You only lose 3kB +but may be sure that you can reuse your data after introduction of a +real partition table. + +BUGS: +- Performance sometimes is rather low because we don't fully exploit clustering + +TODO-List: +- Add IBM'S Disk layout to genhd +- Enhance driver to use more than one major number +- Enable usage as a module +- Support Cache fast write and DASD fast write (ECKD) diff -u --recursive --new-file v2.2.13/linux/Documentation/s390/cds.txt linux/Documentation/s390/cds.txt --- v2.2.13/linux/Documentation/s390/cds.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/s390/cds.txt Tue Jan 4 10:12:10 2000 @@ -0,0 +1,912 @@ +Linux/390 + +Common Device Support (CDS) +Device Driver I/O Support Routines + +Author : Ingo Adlung + +Copyright, IBM Corp. 1999 + +Introduction + +This document describes the common device support routines for Linux/390. +Different than other hardware architectures, ESA/390 has defined a unified +I/O access method. This gives relief to the device drivers as they don't +have to deal with different bus types, polling versus interrupt +processing, shared versus non-shared interrupt processing, DMA versus port +I/O (PIO), and other hardware features more. However, this implies that +either every single device driver needs to implement the hardware I/O +attachment functionality itself, or the operating system provides for a +unified method to access the hardware, providing all the functionality that +every single device driver would have to provide itself. + +The document does not intend to explain the ESA/390 hardware architecture in +every detail.This information can be obtained from the ESA/390 Principles of +Operation manual (IBM Form. No. SA22-7201). + +In order to build common device support for ESA/390 I/O interfaces, a +functional layer was introduced that provides generic I/O access methods to +the hardware. The following figure shows the usage of the common device support +of Linux/390 using a TbCP/IP driven device access an example. Similar figures +could be drawn for other access methods, e.g. file system access to disk +devices. + +The common device support layer shown above comprises the I/O support routines +defined below. Some of them implement common Linux device driver interfaces, +while some of them are ESA/390 platform specific. + +get_dev_info_by_irq() / get_dev_info_by_devno() + allow a device driver to determine the devices attached (visible) to the + system and their current status. + +get_irq_by_devno() / get_devno_by_irq() + get irq (subchannel) from device number and vice versa. + +read_dev_chars() + read device characteristics + +request_irq() + obtain ownership for a specific device. + +free_irq() + release ownership for a specific device. + +disable_irq() + disable a device from presenting interrupts. + +enable_irq() + enable a device, allowing for I/O interrupts. + +do_IO() + initiate an I/O request. + +halt_IO() + terminate the current I/O request processed on the device. + +do_IRQ() + generic interrupt routine. This function is called by the interrupt entry + routine whenever an I/O interrupt is presented to the system. The do_IRQ() + routine determines the interrupt status and calls the device specific + interrupt handler according to the rules (flags) defined during I/O request + initiation with do_IO(). + +The next chapters describe the functions, other than do_IRQ() in more details. +The do_IRQ() interface is not described, as it is called from the Linux/390 +first level interrupt handler only and does not comprise a device driver +callable interface. Instead, the functional description of do_IO() also +describes the input to the device specific interrupt handler. + + +Common Device Support (CDS) for Linux/390 Device Drivers + +General Information + +The following chapters describe the I/O related interface routines the +Linux/390 common device support (CDS) provides to allow for device specific +driver implementations on the IBM ESA/390 hardware platform. Those interfaces +intend to provide the functionality required by every device driver +implementaion to allow to drive a specific hardware device on the ESA/390 +platform. Some of the interface routines are specific to Linux/390 and some +of them can be found on other Linux platforms implementations too. +Miscellaneous function prototypes, data declarations, and macro definitions +can be found in the architecture specific C header file +linux/arch/s390/kernel/irq.h. + +Overview of CDS interface concepts + +Different to other hardware platforms, the ESA/390 architecture doesn't define +interrupt lines managed by a specific interrupt controller and bus systems +that may or may not allow for shared interrupts, DMA processing, etc.. Instead, +the ESA/390 architecture has implemented a so called channel subsystem, that +provides a unified view of the devices physically attached to the systems. +Though the ESA/390 hardware platform knows about a huge variety of different +peripheral attachments like disk devices (aka. DASDs), tapes, communication +controllers, etc. they can all by accessed by a well defined access method and +they are presenting I/O completion a unified way : I/O interruptions. Every +single device is uniquely identified to the system by a so called subchannel, +where the ESA/390 architecture allows for 64k devices be attached. + +Linux, however was first built on the Intel PC architecture, with its two +cascaded 8259 programmable interrupt controllers (PICs), that allow for a +maximum of 15 different interrupt lines. All devices attached to such a system +share those 15 interrupt levels. Devices attached to the ISA bus system must +not share interrupt levels (aka. IRQs), as the ISA bus bases on edge triggered +interrupts. MCA, EISA, PCI and other bus systems base on level triggered +interrupts, and therewith allow for shared IRQs. However, if multiple devices +present their hardware status by the same (shared) IRQ, the operating system +has to call every single device driver registered on this IRQ in order to +determine the device driver owning the device that raised the interrupt. + +In order to not introduce a new I/O concept to the common Linux code, +Linux/390 preserves the IRQ concept and semantically maps the ESA/390 +subchannels to Linux as IRQs. This allows Linux/390 to support up to 64k +different IRQs, uniquely representig a single device each. + +During its startup the Linux/390 system checks for peripheral devices. Each +of those devices is uniquely defined by a so called subchannel by the ESA/390 +channel subsystem. While the subchannel numbers are system generated, each +subchannel also takes a user defined attribute, the so called device number. +Both, subchannel number and device number can not exceed 65535. The +init_IRQ() routine gathers the information about control unit type and device +types that imply specific I/O commands (channel command words - CCWs) in +order to operate the device. Device drivers can retrieve this set of hardware +information during their initialization step to recognize the devices they +support using get_dev_info_by_irq() or get_dev_info_by_devno() respectively. +This methods implies that Linux/390 doesn't require to probe for free (not +armed) interrupt request lines (IRQs) to drive its devices with. Where +applicable, the device drivers can use the read_dev_chars() to retrieve device +characteristics. This can be done without having to request device ownership +previously. + +When a device driver has recognized a device it wants to claim ownership for, +it calls request_irq() with the device's subchannel id serving as pseudo irq +line. One of the required parameters it has to specify is dev_id, defining a +device status block, the CDS layer will use to notify the device driver's +interrupt handler about interrupt information observed. It depends on the +device driver to properly handle those interrupts. + +In order to allow for easy I/O initiation the CDS layer provides a do_IO() +interface that takes a device specific channel program (one or more CCWs) as +input sets up the required architecture specific control blocks and initiates +an I/O request on behalf of the device driver. The do_IO() routine allows for +different I/O methods, synchronous and asynchronous, and allows to specify +whether it expects the CDS layer to notify the device driver for every +interrupt it observes, or with final status only. It also provides a scheme +to allow for overlapped I/O processing. See do_IO() for more details. A device +driver must never issue ESA/390 I/O commands itself, but must use the +Linux/390 CDS interfaces instead. + +For long running I/O request to be canceled, the CDS layer provides the +halt_IO() function. Some devices require to initially issue a HALT SUBCHANNEL +(HSCH) command without having pending I/O requests. This function is also +covered by halt_IO(). + +When done with a device, the device driver calls free_irq() to release its +ownership for the device. During free_irq() processing the CDS layer also +disables the device from presenting further interrupts - the device driver +doesn't need to assure it. The device will be reenabled for interrupts with +the next call to request_irq(). + + + +get_dev_info_by_irq() / get_dev_info_by_devno() - Retrieve Device Information + +During system startup - init_IRQ() processing - the generic I/O device support +checks for the devices available. For all devices found it collects the +SenseID information. For those devices supporting the command it also obtains +extended SenseID information. + +int get_dev_info_by_irq( int irq, + dev_info_t *devinfo); + +int get_dev_info_by_devno( unsigned int irq, + dev_info_t *devinfo); + +irq - defines the subchannel, status information is to be + returned for. +devno - device number. +devinfo - pointer to a user buffer of type dev_info_t that should + be filled with device specific information. + +typedef struct { + unsigned int devno; /* device number */ + unsigned int status; /* device status */ + senseid_t sid_data; /* senseID data */ +} dev_info_t; + +devno - device number as configured in the IOCDS. +status - device status +sid_data - data obtained by a SenseID call + +Possible status values are : + +DEVSTAT_NOT_OPER - device was found not-operational. In this case + the caller should disregard the sid_data + buffer content. + +// +// SenseID response buffer layout +// +typedef struct { + /* common part */ + unsigned char reserved; /* always 0x'FF' */ + unsigned short cu_type; /* control unit type */ + unsigned char cu_model; /* control unit model */ + unsigned short dev_type; /* device type */ + unsigned char dev_model; /* device model */ + unsigned char unused; /* padding byte */ + /* extended part */ + ciw_t ciw[62]; /* variable # of CIWs */ +} senseid_t; + +The ESA/390 I/O architecture defines certain device specific I/O functions. +The device returns the device specific command code together with the SenseID +data in so called Command Information Words (CIW) : + +typedef struct _ciw { + unsigned int et : 2; // entry type + unsigned int reserved : 2; // reserved + unsigned int ct : 4; // command type + unsigned int cmd : 8; // command + unsigned int count : 16; // count +} ciw_t; + +Possible CIW entry types are : + +#define CIW_TYPE_RDC 0x0; // read configuration data +#define CIW_TYPE_SII 0x1; // set interface identifier +#define CIW_TYPE_RNI 0x2; // read node identifier + +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 +-ENODEV - irq or devno don't specify a known subchannel or device + number. +-EINVAL - invalid devinfo value. + +Usage Notes : + +In order to scan for known devices a device driver should scan all irqs by +calling get_dev_info() until it returns -ENODEV as there aren't any more +available devices. + +If a device driver wants to request ownership for a specific device it must +call request_irq() prior to be able to issue any I/O request for it, including +above mentioned device dependent commands. + +Please see the "ESA/390 Common I/O-Commandss and Self Description" manual, +with IBM form number SA22-7204 for more details on how to read the Sense-ID +output, CIWs and device independent commands. + + + +get_irq_by_devno() / get_devno_by_irq() - Convert device identifiers + +While some device drivers act on the irq (subchannel) only, others take user +defined device configurations on device number base, according to the device +numbers configured in the IOCDS. The following routines serve the purpose to +convert irq values into device numbers and vice versa. + +int get_irq_by_devno( unsigned int devno ); + +unsigned int get_devno_by_irq( int irq ); + +The functions return : + +the requested irq/devno values +-1 if the requested conversion can't be accomplished. + +This could either be caused by irq/devno be outside the valid range +( value > 0xffff or value < 0 ) or not identifying a known device. + + +read_dev_chars() - Read Device Characteristics + +This routine returns the characteristics for the device specified. + +The function is meant to be called without an irq handler be in place. +However, the irq for the requested device must not be locked or this will +cause a deadlock situation ! Further, the driver must assure that nobody +else has claimed ownership for the requested irq yet or the owning device +driver's internal accounting may be affected. + +In case of a registered interrupt handler, the interrupt handler must be +able to properly react on interrupts related to the read_dev_chars() I/O +commands. While the request is procesed synchronously, the device interrupt +handler is called for final ending status. In case of error situations the +interrupt handler may recover appropriately. The device irq handler can +recognize the corresponding interrupts by the interruption parameter be +0x00524443. If using the function with an existing device interrupt handler +in place, the irq must be locked prior to call read_dev_chars(). + +The function may be called enabled or disabled. + +int read_dev_chars( int irq, void **buffer, int length ); + +irq - specifies the subchannel the device characteristic + retrieval is requested for +buffer - pointer to a buffer pointer. The buffer pointer itself + may be NULL to have the function allocate a buffer or + must contain a valid buffer area. +length - length of the buffer provided or to be allocated. + +The read_dev_chars() function returns : + + 0 - successful completion +-ENODEV - irq doesn't specify a valid subchannel number +-EINVAL - an invalid parameter was detected +-EBUSY - an irrecoverable I/O error occured or the device is not + operational. + +Usage Notes : + +The function can be used in two ways : + +If the caller doesn't provide a data buffer, read_dev_chars() allocates a +data buffer and provides the device characteristics together. It's the +caller's responsability to release the kernel memory if not longer needed. +This behaviour is triggered by specifying a NULL buffer area (*buffer == NULL). + +Alternatively, if the user specifies a buffer area himself, nothing is +allocated. + +In either case the caller must provide the data area length - for the buffer +he specifies, or the buffer he wants to be allocated. + + +request_irq() - Request Device Ownership + +As previously discussed a device driver will scan for the devices its supports +by calling get_dev_info(). Once it has found a device it will call +request_irq() to request ownership for it. This call causes the subchannel to +be enabled for interrupts if it was found operational. + +int request_irq( unsigned int irq, + int (*handler)( int, + void *, + struct pt_regs *), + unsigned long irqflags, + const char *devname, + void *dev_id); + +irq : specifies the subchannel the ownership is requested for +handler : specifies the device driver's interrupt handler to be + called for interrupt processing +irqflags : IRQ flags, must be 0 (zero) or SA_SAMPLE_RANDOM +devname : device name +dev_id : required pointer to a device specific buffer of type + devstat_t + +typedef struct { + unsigned int devno; /* device number from irb */ + unsigned int intparm; /* interrupt parameter */ + unsigned char cstat; /* channel status - accumulated */ + unsigned char dstat; /* device status - accumulated */ + unsigned char lpum; /* last path used mask from irb */ + unsigned char unused; /* not used - reserved */ + unsigned int flag; /* flag : see below */ + unsigned long cpa; /* CCW addr from irb at prim. status */ + unsigned int rescnt; /* count from irb at primary status */ + unsigned int scnt; /* sense count, if available */ + union { + irb_t irb; /* interruption response block */ + sense_t sense; /* sense information */ + } ii; /* interrupt information */ + } devstat_t; + +During request_irq() processing, the devstat_t layout does not matter as it +won't be used during request_irq() processing. See do_IO() for a functional +description of its usage. + +The request_irq() function returns : + + 0 - successful completion +-EINVAL - an invalid parameter was detected +-EBUSY - device (subchannel) already owned +-ENODEV - the device is not-operational +-ENOMEM - not enough kernel memory to process request + +Usage Notes : + +While Linux for Intel defines dev_id as a unique identifier for shared +interrupt lines it has a totally different purpose on Linux/390. Here it +serves as a shared interrupt status area between the generic device support +layer, and the device specific driver. The value passed to request_irq() +must therefore point to a valid devstat_t type buffer area the device driver +must preserve for later usage. I.e. it must not be released prior to a call +to free_irq() + +The only value parameter irqflags supports is SA_SAMPLE_RANDOM if appropriate. +The Linux/390 kernel does neither know about "fast" interrupt handlers, nor +does it allow for interrupt sharing. Remember, the term interrupt level (irq), +device, and subchannel are used interchangeably in Linux/390. + +If request_irq() was called in enabled state, or if multiple CPUs are present, +the device may present an interrupt to the specified handler prior to +request_irq() return to the caller already ! This includes the possibility +of unsolicited interrupts or a pending interrupt status from an earlier +solicited I/O request. The device driver must be able to handle this situation +properly or the device may become unoperational otherwise ! + +Although the interrupt handler is defined to be called with a pointer to a +struct pt_regs buffer area, this is not implemented by the Linux/390 generic +I/O device driver support layer. The device driver's interrupt handler must +therefore not rely on this parameter on function entry. + + +free_irq() - Release Device Ownership + +A device driver may call free_irq() to release ownership of a previously +aquired device. + +void free_irq( unsigned int irq, + void *dev_id); + +irq : specifies the subchannel the ownership is requested for +dev_id : required pointer to a device specific buffer of type + devstat_t. This must be the same as the one specified + during a previous call to request_irq(). + +Usage Notes : + +Unfortunately the free_irq() is defined not to return error codes. I.e. if +called with wrong parameters a device may still be operational although there +is no device driver available to handle its interrupts. Further, during +free_irq() processing we may possibly find pending interrupt conditions. As +those need to be processed, we have to delay free_irq() returning until a +clean device status is found by synchronously handling them. + +The call to free_irq() will also cause the device (subchannel) be disabled for +interrupts. The device driver must not release any data areas required for +interrupt processing prior to free_irq() return to the caller as interrupts +can occur prior to free_irq() returning. This is also true when called in +disabled state if either multiple CPUs are presents or a pending interrupt +status was found during free_irq() processing. + + +disable_irq() - Disable Interrupts for a given Device + +This function may be called at any time to disable interrupt processing for +the specified irq. However, as Linux/390 maps irqs to the device (subchannel) +one-to-one, this may require more extensive I/O processing than anticipated, +especially if an interrupt status is found pending on the subchannel that +requires synchronous error processing. + +int disable_irq( unsigned int irq ); + +irq : specifies the subchannel to be disabled + +The disable-irq() routine may return : + + 0 - successful completion +-EBUSY - device (subchannel) is currently processing an I/O + request +-ENODEV - the device is not-operational or irq doesn't specify a + valid subchannel + +Usage Notes : + +Unlike the Intel based hardware architecture the ESA/390 architecture does +not have a programmable interrupt controller (PIC) where a specific interrupt +line can be disabled. Instead the subchannel logically representing the device +in the channel subsystem must be disabled for interrupts. However, if there +are still inetrrupt conditions pending they must be processed first in order +to allow for proper processing after reenabling the device at a later time. +This may lead to delayed disable processing. + +As described above the disable processing may require extensive processing. +Therefore disabling and re-enabling the device using disable_irq() / +enable_irq() should be avoided and is not suitable for high frequency +operations. + +Linux for Intel defines this function + +void disable_irq( int irq); + +This is suitable for the Intel PC architecture as this only causes to mask +the requested irq line in the PIC which is not applicable for the ESA/390 +architecture. Therefore we allow for returning error codes. + + +enable_irq() - Enable Interrupts for a given Device + +This function is used to enable a previously disabled device (subchannel). +See disable_irq() for more details. + +int enable_irq( unsigned int irq ); + +irq : specifies the subchannel to be enabled + +The enable-irq() routine may return : + + 0 - successful completion +-EBUSY - device (subchannel) is currently processing an I/O + request. This implies the device is already in enabled + state +-ENODEV - the device is not-operational or irq doesn't specify a + valid subchannel + + + +do_IO() - Initiate I/O Request + +The do_IO() routines is the I/O request front-end processor. All device driver +I/O requests must be issued using this routine. A device driver must not issue +ESA/390 I/O commands itself. Instead the do_IO() routine provides all +interfaces required to drive arbitrary devices. + +This description also covers the status information passed to the device +driver's interrupt handler as this is related to the rules (flags) defined +with the associated I/O request when calling do_IO(). + +int do_IO( int irq, + ccw1_t *cpa, + unsigned long intparm, + unsigned int lpm, + unsigned long flag); + +irq : irq (subchannel) the I/O request is destined for +cpa : logical start address of channel program +intparm : user specific interrupt information; will be presented + back to the device driver's interrupt handler. Allows a + device driver to associate the interrupt with a + particular I/O request. +lpm : defines the channel path to be used for a specific I/O + request. Valid with flag value DOIO_VALID_LPM only. +flag : defines the action to e parformed for I/O processing + +Possible flag values are : + +DOIO_EARLY_NOTIFICATION - allow for early interupt notification +DOIO_VALID_LPM - LPM input parameter is valid (see usage + notes below for details) +DOIO_WAIT_FOR_INTERRUPT - wait synchronously for final status +DOIO_REPORT_ALL - report all interrupt conditions + +The cpa parameter points to the first format 1 CCW of a channel program : + +typedef struct { + char cmd_code; /* command code */ + char flags; /* flags, like IDA adressing, etc. */ + unsigned short count; /* byte count */ + void *cda; /* data address */ +} ccw1_t __attribute__ ((aligned(8))); + +with the following CCW flags values defined : + +CCW_FLAG_DC - data chaining +CCW_FLAG_CC - command chaining +CCW_FLAG_SLI - suppress incorrct length +CCW_FLAG_SKIP - skip +CCW_FLAG_PCI - PCI +CCW_FLAG_IDA - indirect addressing +CCW_FLAG_SUSPEND - suspend + + + +The do_IO() function returns : + + 0 - successful completion or request successfuly initiated +-EBUSY - the do_io() function was caled out of sequence. The + device is currently processing a previous I/O request +-ENODEV - irq doesn't specify a valid subchannel, the device is + not operational (check dev_id.flags) or the irq is not + owned. +-EINVAL - both, DOIO_EARLY_NOTIFICATION and DOIO_REORT_ALL flags + have been specified. The usage of those flags is mutual + exclusive. + +When the I/O request completes, the CDS first level interrupt handler will +setup the dev_id buffer of type devstat_t defined during request_irq() +processing. See request_irq() for the devstat_t data layout. The +dev_id->intparm field in the device status area will contain the value the +device driver has associated with a particular I/O request. If a pending +device status was recognized dev_id->intparm will be set to 0 (zero). This +may happen during I/O initiation or delayed by an alert status notification. +In any case this status is not related to the current (last) I/O request. In +case of a delayed status notification no special interrupt will be presented +to indicate I/O completion as the I/O request was never started, even though +do_IO() returned with successful completion. + +Possible dev_id->flag values are : + +DEVSTAT_FLAG_SENSE_AVAIL - sense data is available +DEVSTAT_NOT_OPER - device is not-operational +DEVSTAT_START_FUNCTION - interrupt is presented as result of a + call to do_IO() +DEVSTAT_HALT_FUNCTION - interrupt is presented as result of a + call to halt_IO() +DEVSTAT_STATUS_PENDING - a pending status was found. The I/O + resquest (if any) was not initiated. + This status might have been presented + delayed, after do_IO() or halt_IO() have + successfully be started previously. +DEVSTAT_FINAL_STATUS - This is a final interrupt status for the + I/O requst identified by intparm. + +If device status DEVSTAT_FLAG_SENSE_AVAIL is indicated in field dev_id->flag, +field dev_id->scnt describes the numer of device specific sense bytes +available in the sense area dev_id->ii.sense. No device sensing by the device +driver itself is required. + +typedef struct { + unsigned char res[32]; /* reserved */ + unsigned char data[32]; /* sense data */ +} sense_t; + +The device interrupt handler can use the following definitions to investigate +the primary unit check source coded in sense byte 0 : + +SNS0_CMD_REJECT 0x80 +SNS0_INTERVENTION_REQ 0x40 +SNS0_BUS_OUT_CHECK 0x20 +SNS0_EQUIPMENT_CHECK 0x10 +SNS0_DATA_CHECK 0x08 +SNS0_OVERRUN 0x04 + +Depending on the device status, multiple of those values may be set together. +Please refer to the device specific documentation for details. + +The devi_id->cstat field provides the (accumulated) subchannel status : + +SCHN_STAT_PCI - program controlled interrupt +SCHN_STAT_INCORR_LEN - incorrect length +SCHN_STAT_PROG_CHECK - program check +SCHN_STAT_PROT_CHECK - protection check +SCHN_STAT_CHN_DATA_CHK - channel data check +SCHN_STAT_CHN_CTRL_CHK - channel control check +SCHN_STAT_INTF_CTRL_CHK - interface control check +SCHN_STAT_CHAIN_CHECK - chaining check + +The dev_id->dstat field provides the (accumulated) device status : + +DEV_STAT_ATTENTION - attention +DEV_STAT_STAT_MOD - status modifier +DEV_STAT_CU_END - control unit end +DEV_STAT_BUSY - busy +DEV_STAT_CHN_END - channel end +DEV_STAT_DEV_END - device end +DEV_STAT_UNIT_CHECK - unit check +DEV_STAT_UNIT_EXCEP - unit exception + +Please see the ESA/390 Principles of Operation manual for details on the +individual flag meanings. + +In rare error situations the device driver may require access to the original +hardware interrupt data beyond the scope of above mentioned infromation. For +those situations the Linux/390 common device support provides the interrupt +response block (IRB) as part of the device status block in dev_id->ii.irb. + +Usage Notes : + +Prior to call do_IO() the device driver must + +assure disabled state, i.e. the I/O mask value in the PSW must be disabled. +This can be accomplished by calling __save_flags( flags). The current PSW +flags are preserved and can be restored by __restore_flags( flags) at a +later time. + +If the device driver violates this rule while running in a uni-processor +environment an interrupt might be presented prior to the do_IO() routine +returning to the device driver main path. In this case we will end in a +deadlock situation as the interrupt handler will try to obtain the irq +lock the device driver still owns (see below) ! + +the driver must assure to hold the device specific lock. This can be +accomplished by + +(i) s390irq_spin_lock( irq), or +(ii) s390irq_spin_lock_irqsave(irq, flags) + +Option (i) should be used if the calling routine is running disabled for +I/O interrupts (see above) already. Option (ii) obtains the device gate und +puts the CPU into I/O disabled state by preserving the current PSW flags. +See the descriptions of s390irq_spin_lock() or s390irq_spin_lock_irqsave() +for more details. + +The device driver is allowed to issue the next do_IO() call from within its +interrupt handler already. It is not required to schedule a bottom-half, +unless an non deterministicly long running error recovery procedure or +similar needs to be scheduled. During I/O processing the Linux/390 generic +I/O device driver support has already obtained the IRQ lock, i.e. the handler +must not try to obtain it again when calling do_IO() or we end in a deadlock +situation ! Anyway, the device driver's interrupt handler must only call +do_IO() if the handler itself can be entered recursively if do_IO() e.g. finds +a status pending and needs to all the interrupt handler itself. + +Device drivers shouldn't heavily rely on DOIO_WAIT_FOR_INTERRUPT synchronous +I/O request processing. All I/O devices, but the console device are driven +using a single shared interrupt subclass (ISC). For sync. processing the +device is temporarily mapped to a special ISC while the calling CPU waits for +I/O completion. As this special ISC is gated, all sync. requests in a SMP +environment are serialized which may cause other CPUs to spin. This service +is therewith primarily meant to be used during device driver initialization +for ease of device setup. + +The lpm input parameter might be used for multipath devices shared among +multiple systems as the Linux/390 CDS isn't grouping channel paths. Therefore +its use might be required if multiple access paths to a device are available +and the device was reserved by means of a reserve device command (for devices +supporting this technique). When issuing this command the device driver needs +needs to extract the dev_id->lpum value and restrict all subsequent channel +programs to this channel path until the device is released by a device release +command. Otherwise a deadlock may occur. + +If a device driver relies on an I/O request to be completed prior to start the +next it can reduce I/O processing overhead by chaining a NoOp I/O command +CCW_CMD_NOOP to the end of the submitted CCW chain. This will force Channel-End +and Device-End status to be presented together, with a single interrupt. +However, this should be used with care as it implies the channel will remain +busy, not being able to process I/O requests for other devices on the same +channel. Therefore e.g. read commands should never use this technique, as the +result will be presented by a single interrupt anyway. + +In order to minimize I/O overhead, a device driver should use the +DOIO_REPORT_ALL only if the device can report intermediate interrupt +information prior to device-end the device driver urgently relies on. In this +case all I/O interruptions are presented to the device driver until final +status is recognized. + +If a device is able to recover from asynchronosly presented I/O errors, it can +perform overlapping I/O using the DOIO_EARLY_NOTIFICATION flag. While some +devices always report channel-end and device-end together, with a single +interrupt, others present primary status (channel-end) when the channel is +ready for the next I/O request and secondary status (device-end) when the data +transmission has been completed at the device. + +Above flag allows to exploit this feature, e.g. for communication devices that +can handle lost data on the network to allow for enhanced I/O processing. + +Unless the channel subsystem at any time presents a secondary status interrupt, +exploiting this feature will cause only primary status interrups to be +presented to the device driver while overlapping I/O is performed. When a +secondary status without error (alert status) is presented, this indicates +successful completion for all overlapping do_IO() requests that have been +issued since the last secondary (final) status. + +During interrupt processing the device specific interrupt handler should avoid +basing its processing decisions on the interruption response block (IRB) that +is part of the dev_id buffer area. The IRB area represents the interruption +parameters from the last interrupt received. Unless the device driver has +specified DOIO_REPORT_ALL or is called with a pending status +(DEVSTAT_STATUS_PENDING), the IRB information may or may not show the complete +interruption status, but the last interrupt only. Therefore the device driver +should usually base its processing decisions on the values of dev_id->cstat +and dev_id->dstat that represent the accumulated subchannel and device status +information gathered since do_IO() request initiation. + + +halt_IO() - Halt I/O Request Processing + +Sometimes a device driver might need a possibility to stop the processing of +a long-running channel program or the device might require to initially issue +a halt subchannel (HSCH) I/O command. For those purposes the halt_IO() command +is provided. + +int halt_IO( int irq, /* subchannel number */ + int intparm, /* dummy intparm */ + unsigned int flag); /* operation mode */ + +irq : irq (subchannel) the halt operation is requested for +intparm : interruption parameter; value is only used if no I/O + is outstanding, otherwise the intparm associated with + the I/O request is returned +flag : 0 (zero) or DOIO_WAIT_FOR_INTERRUPT + +The halt_IO() function returns : + + 0 - successful completion or request successfuly initiated +-EBUSY - the device is currently performing a sysnchonous I/O + operation : do_IO() with flag DOIO_WAIT_FOR_INTERRUPT + or an error was encountered and the device is currently + be sensed +-ENODEV - the irq specified doesn't specify a valid subchannel, the + device is not operational (check dev_id.flags) or the irq + is not owned. + +Usage Notes : + +A device driver may write a never-ending channel program by writing a channel +program that at its end loops back to its beginning by means of a transfer in +channel (TIC) command (CCW_CMD_TIC). Usually this is performed by network +device drivers by setting the PCI CCW flag (CCW_FLAG_PCI). Once this CCW is +executed a program controlled interrupt (PCI) is generated. The device driver +can then perform an appropriate action. Prior to interrupt of an outstanding +read to a network device (with or without PCI flag) a halt_IO() is required +to end the pending operation. + +We don't allow to stop sync. I/O requests by means of a halt_IO() call. The +function will return -EBUSY instead. + + +Miscellaneous Support Routines + +This chapter describes various routines to be used in a Linux/390 device +driver programming environment. + +s390irq_spin_lock() / s390irq_spin_unlock() + +Those two macro definitions are required to obtain the device specific IRQ +lock. The lock needs to be obtained if the device driver intends to call +do_IO() or halt_IO() from anywhere but the device interrupt handler (where +the lock is already owned). Those routines must only be used if running +disabled for interrupts already. Otherwise use s390irq_spin_lock_irqsave() +and the corresponding unlock routine instead (see below). + +s390irq_spin_lock( int irq); +s390irq_spin_unlock( int irq); + + +s390irq_spin_lock_irqsave() / s390_irq_spin_unlock_irqrestore() + +Those two macro definitions are required to obtain the device specific IRQ +lock. The lock needs to be obtained if the device driver intends to call +do_IO() or halt_IO() from anywhere but the device interrupt handler (where +the lock is already owned). Those routines should only be used if running +enabled for interrupts. If running disabled already, the driver should use +s390irq_spin_lock() and the corresponding unlock routine instead (see above). + +s390irq_spin_lock_irqsave( int irq, unsigned long flags); +s390irq_spin_unlock_irqrestore( int irq, unsigned long flags); + + + + +Special Console Interface Routines + +This chapter describes the special interface routines required for system +console processing. Though they are an extension to the Linux/390 device +driver interface concept, they base on the same principles. It was necessary +to build those extensions to assure a deterministic behaviour in critical +situations e.g. printk() messages by other device drivers running disabled +for interrupts during I/O interrupt handling or in case of a panic() message +being raised. + +set_cons_dev - Set Console Device + +This routine allows to specify the system console device. This is necessary +as the console isn't driven by the same ESA/390 interrupt subclass as are +other devices, but it is assigned ist own interrupt subclass. Only one device +can act as system console. See wait_cons_dev() for details. + +int set_cons_dev( int irq); + +irq : subchannel identifying the system console device + +The set_cons_dev() function returns + + 0 - successful completion +-EIO - an unhandled interrupt condition is pending for the + specified subchannel (irq) - status pending +-ENODEV - irq doesn't specify a valid subchannel or the devive is + not operational +-EBUSY - the console device is already defined + +reset_cons_dev - Reset Console Device + +This routine allows for resetting the console device specification. See +set_cons_dev() for details. + +int reset_cons_dev( int irq); + +irq : subchannel identifying the system console device + +The reset_cons_dev() function returns + + 0 - successful completion +-EIO - an unhandled interrupt condition is pending for the + specified subchannel (irq) - status pending +-ENODEV - irq doesn't specify a valid subchannel or the devive is + not operational + +wait_cons_dev - Synchronously Wait for Console Processing + +The wait_cons_dev() routine is used by the console device driver when its +buffer pool for intermediate request queuing is exhausted and a new output +request is received. In this case the console driver uses the wait_cons_dev() +routine to synchronously wait until enough buffer space is gained to enqueue +the current request. Any pending interrupt condition for the console device +found during wait_cons_dev() processing causes its interrupt handler to be +called. + +int wait_cons_dev( int irq); + +irq : subchannel identifying the system console device + +The wait_cons_dev() function returns : + + 0 - successful completion +-EINVAL - the irq specified doesn't match the irq configured for + the console device by set_cons_dev() + +Usage Notes : + +The function should be used carefully. Especially in a SMP environment the +wait_cons_dev() processing requires that all but the special console ISC are +disabled. In a SMP system this requires the other CPUs to be signaled to +disable/enable those ISCs. + + + diff -u --recursive --new-file v2.2.13/linux/Documentation/sound/Introduction linux/Documentation/sound/Introduction --- v2.2.13/linux/Documentation/sound/Introduction Mon Aug 9 16:05:54 1999 +++ linux/Documentation/sound/Introduction Tue Jan 4 10:12:10 2000 @@ -25,7 +25,8 @@ added info on multiple sound cards of similar types,] added more diagnostics info, added info about esd. added info on OSS and ALSA. - +1.1.1 19991031 Added notes on sound-slot- and sound-service. + (Alan Cox) Modular Sound Drivers: ====================== @@ -321,6 +322,12 @@ 7) Turn on debug in drivers/sound/sound_config.h (DEB, DDB, MDB). +8) If the system reports insuffcient DMA memory then you may want to + load sound with the "dmabufs=1" option. Or in /etc/conf.modules add + + preinstall sound dmabufs=1 + + This makes the sound system allocate its buffers and hang onto them. Configuring Sound: ================== @@ -335,7 +342,7 @@ 3) In /etc/conf.modules when using modprobe. -4) Via Red Hat's /usr/sbin/sndconfig program (text based). +4) Via Red Hat's GPL'd /usr/sbin/sndconfig program (text based). 5) Via the OSS soundconf program (with the commercial version of the OSS driver. @@ -344,6 +351,28 @@ Anyone want to write a linuxconf module for configuring sound? +Module Loading: +=============== + +When a sound card is first referenced and sound is modular the sound system +will ask for the sound devices to be loaded. Initially it requests that +the driver for the sound system is loaded. It then wwill ask for +sound-slot-0, where 0 is the first sound card. (sound-slot-1 the second and +so on). Thus you can do + +alias sound-slot-0 sb + +To load a soundblaster at this point. If the slot loading does not provide +the desired device - for example a soundblaster does not directly provide +a midi synth in all cases then it will request "sound-service-0-n" where n +is + +0 Mixer + +2 MIDI + +3, 4 DSP audio + For More Information (RTFM): ============================ @@ -373,6 +402,3 @@ Contact Information: ==================== Wade Hampton: (whampton@staffnet.com) - - - diff -u --recursive --new-file v2.2.13/linux/Documentation/sound/Maestro linux/Documentation/sound/Maestro --- v2.2.13/linux/Documentation/sound/Maestro Wed Dec 31 16:00:00 1969 +++ linux/Documentation/sound/Maestro Tue Jan 4 10:12:10 2000 @@ -0,0 +1,98 @@ + An OSS/Lite Driver for the ESS Maestro family of sound cards + + Zach Brown, December 1999 + +Driver Status and Availability +------------------------------ + +The most recent version of this driver will hopefully always be available at + http://people.redhat.com/zab/maestro/ + +I will try and maintain the most recent stable version of the driver +in both the stable and development kernel lines. + +ESS Maestro Chip Family +----------------------- + +There are 3 main variants of the ESS Maestro PCI sound chip. The first +is the Maestro 1. It was originally produced by Platform Tech as the +'AGOGO'. It can be recognized by Platform Tech's PCI ID 0x1285 with +0x0100 as the device ID. It was put on some sound boards and a few laptops. +ESS bought the design and cleaned it up as the Maestro 2. This starts +their marking with the ESS vendor ID 0x125D and the 'year' device IDs. +The Maestro 2 claims 0x1968 while the Maestro 2e has 0x1978. + +The various families of Maestro are mostly identical as far as this +driver is concerned. It doesn't touch the DSP parts that differ (though +it could for FM synthesis) + +Driver OSS Behavior +-------------------- + +This OSS driver exports /dev/mixer and /dev/dsp to applications, which +mostly adhere to the OSS spec. This driver doesn't register itself +with /dev/sndstat, so don't expect information to appear there. + +The /dev/dsp device exported behaves almost as expected. Playback is +supported in all the various lovely formats. 8/16bit stereo/mono from +8khz to 48khz, and mmap()ing for playback behaves. Capture/recording +is limited due to oddities with the Maestro hardware. One can only +record in 16bit stereo. For recording the maestro uses non interleaved +stereo buffers so that mmap()ing the incoming data does not result in +a ring buffer of LRLR data. mmap()ing of the read buffers is therefore +disallowed until this can be cleaned up. + +/dev/mixer is an interface to the AC'97 codec on the Maestro. It is +worth noting that there are a variety of AC'97s that can be wired to +the Maestro. Which is used is entirely up to the hardware implementor. +This should only be visible to the user by the presence, or lack, of +'Bass' and 'Treble' sliders in the mixer. Not all AC'97s have them. + +The driver doesn't support MIDI or FM playback at the moment. Typically +the Maestro is wired to an MPU MIDI chip, but some hardware implementations +don't. We need to assemble a white list of hardware implementations that +have MIDI wired properly before we can claim to support it safely. + +Compiling and Installing +------------------------ + +With the drivers inclusion into the kernel, compiling and installing +is the same as most OSS/Lite modular sound drivers. Compilation +of the driver is enabled through the CONFIG_SOUND_MAESTRO variable +in the config system. + +It may be modular or statically linked. If it is modular it should be +installed with the rest of the modules for the kernel on the system. +Typically this will be in /lib/modules/ somewhere. 'alias sound maestro' +should also be added to your module configs (typically /etc/conf.modules) +if you're using modular OSS/Lite sound and want to default to using a +maestro chip. + +As this is a PCI device, the module does not need to be informed of +any IO or IRQ resources it should use, it devines these from the +system. Somtimes, on sucky PCs, the BIOS fails to allocated resources +for the maestro. This will result in a message like: + maestro: PCI subsystem reports IRQ 0, this might not be correct. +from the kernel. Should this happen the sound chip most likely will +not operate correctly. To solve this one has to dig through their BIOS +(typically entered by hitting a hot key at boot time) and figure out +what magic needs to happen so that the BIOS will reward the maestro with +an IRQ. This operation is incredibly system specific, so you're on your +own. Sometimes the magic lies in 'PNP Capable Operating System' settings. + +There are very few options to the driver. One is 'debug' which will +tell the driver to print minimal debugging information as it runs. This +can be collected with 'dmesg' or through the klogd daemon. + +The other, more interesting option, is 'dsps_order'. Typically at +install time the driver will only register one available /dev/dsp device +for its use. The 'dsps_order' module parameter allows for more devices +to be allocated, as a power of two. Up to 4 devices can be registered +( dsps_order=2 ). These devices act as fully distinct units and use +separate channels in the maestro. + +.. more details .. +----------------- + +drivers/sound/maestro.c contains comments that hopefully explain +the maestro implementation. diff -u --recursive --new-file v2.2.13/linux/Documentation/sound/NM256 linux/Documentation/sound/NM256 --- v2.2.13/linux/Documentation/sound/NM256 Tue Jan 4 11:10:31 2000 +++ linux/Documentation/sound/NM256 Tue Jan 4 10:12:10 2000 @@ -2,10 +2,10 @@ Documentation for the NeoMagic 256AV/256ZX sound driver ======================================================= -You're looking at version 1.0 of the driver. (Woohoo!) It has been +You're looking at version 1.11 of the driver. (Woohoo!) It has been successfully tested against the following laptop models: - Sony Z505S/Z505SX/Z505DX + Sony Z505S/Z505SX/Z505DX/Z505RX Sony F150, F160, F180, F250, F270, F280, PCG-F26 Dell Latitude CPi, CPt (various submodels) @@ -16,9 +16,13 @@ NeoMagic. There is no warranty, expressed, implied, or otherwise. It is free software in the public domain; feel free to use it, sell it, give it to your best friends, even claim that you wrote it (but why?!) -but don't come whining to me, NeoMagic, Sony, Dell, or anyone else +but don't go whining to me, NeoMagic, Sony, Dell, or anyone else when it blows up your computer. +Version 1.11 fixes a bug uncovered by Timidity--the sound would start clicking +after the first MIDI file was finished playing. There are no other changes +over 1.1. + ============ Installation ============ @@ -42,16 +46,19 @@ another device (it normally shares IRQ 9 with the builtin eepro100 ethernet on the Sony Z505 laptops). -It does not run the card in any sort of compatibility mode. Thus it -almost certainly will not work on laptops that have the -SB16-compatible codec/mixer; you will want to use the standard SB16 -OSS driver with these chipsets. I cannot provide any assistance with -machines using the SB-16 compatible version. +It does not run the card in any sort of compatibility mode. It will +not work on laptops that have the SB16-compatible, AD1848-compatible +or CS4232-compatible codec/mixer; you will want to use the appropriate +compatible OSS driver with these chipsets. I cannot provide any +assistance with machines using the SB16, AD1848 or CS4232 compatible +versions. (The driver now attempts to detect the mixer version, and +will refuse to load if it believes the hardware is not not +AC97-compatible.) The sound support is very basic, but it does include simultaneous playback and record capability. The mixer support is also quite simple, although this is in keeping with the rather limited -functionality of the chipset. +functionality of the chipset. There is no hardware synthesizer available, as the Losedows OPL-3 and MIDI support is done via hardware emulation. @@ -63,8 +70,6 @@ so of course the CD-ROM input doesn't work. It does work on laptops with a builtin CD-ROM drive. -Recording is mono 8-bit only. - The mixer device does not appear to have any tone controls, at least on the Z505 series. The mixer module checks for tone controls in the AC97 mixer, and will enable them if they are available. @@ -93,10 +98,11 @@ limitation. It may be possible to support other speeds in the future. * There is no support for the telephone mixer/codec. There is support - for a phonein/phoneout device if your mixer program supports it; - whether or not it does anything is anyone's guess. (Reports on this - would be appreciated.) - + for a phonein/phoneout device in the mixer driver; whether or not + it does anything is anyone's guess. (Reports on this would be + appreciated. You'll have to figure out how to get the phone to + go off-hook before it'll work, tho.) + * This driver was not written with any cooperation or support from NeoMagic. If you have any questions about this, see their website for their official stance on supporting open source drivers. @@ -118,8 +124,8 @@ * Sometimes the NM256 driver has to guess at where the buffer should be placed, especially if the module is loaded after the - X server is started. It's usually correct, but it will fail on - the Sony F250. + X server is started. It's usually correct, but it will consistently + fail on the Sony F250. * Virtual screens greater than 1024x768x16 under XFree86 are problematic on laptops with only 2.5MB of screen RAM. This @@ -139,10 +145,16 @@ On the F250, it is possible to force the driver to load properly even after the XFree86 server is started by doing: - insmod nm256.o buffertop=0x25a800 + insmod nm256 buffertop=0x25a800 This forces the audio buffers to the correct offset in screen RAM. +One user has reported a similar problem on the Sony F270, although +others apparently aren't seeing any problems. His suggested command +is + + insmod nm256 buffertop=0x272800 + ================= Official WWW site ================= @@ -154,6 +166,17 @@ You should always be able to get the latest version of the driver there, and the driver will be supported for the foreseeable future. +============== +Z505RX and IDE +============== + +There appears to be a problem with the IDE chipset on the Z505RX; one +of the symptoms is that sound playback periodically hangs (when the +disk is accessed). The user reporting the problem also reported that +enabling all of the IDE chipset workarounds in the kernel solved the +problem, tho obviously only one of them should be needed--if someone +can give me more details I would appreciate it. + ============================== Z505S/Z505SX on-board Ethernet ============================== @@ -176,11 +199,30 @@ ================================== There is also a known problem with the Sony Z505S and Z505SX hanging -if a PCMCIA card is inserted while the ethernet driver is loaded. -This is caused by tons of spurious IRQ 9s, probably generated from the -PCMCIA or ACPI bridges. There is currently no fix for the problem, -and the only known workaround is to disable the ethernet interface -before inserting or removing a PCMCIA card. +if a PCMCIA card is inserted while the ethernet driver is loaded, or +in some cases if the laptop is suspended. This is caused by tons of +spurious IRQ 9s, probably generated from the PCMCIA or ACPI bridges. + +There is currently no fix for the problem that works in every case. +The only known workarounds are to disable the ethernet interface +before inserting or removing a PCMCIA card, or with some cards +disabling the PCMCIA card before ejecting it will also help the +problem with the laptop hanging when the card is ejected. + +One user has reported that setting the tcic's cs_irq to some value +other than 9 (like 11) fixed the problem. This doesn't work on my +Z505S, however--changing the value causes the cardmgr to stop seeing +card insertions and removals, cards don't seem to work correctly, and +I still get hangs if a card is inserted when the kernel is booted. + +Using the latest ethernet driver and pcmcia package allows me to +insert an Adaptec 1480A SlimScsi card without the laptop hanging, +although I still have to shut down the card before ejecting or +powering down the laptop. However, similar experiments with a DE-660 +ethernet card still result in hangs when the card is inserted. I am +beginning to think that the interrupts are CardBus-related, since the +Adaptec card is a CardBus card, and the DE-660 is not; however, I +don't have any other CardBus cards to test with. ====== Thanks @@ -208,6 +250,11 @@ Jeff Garzik, for various helpful suggestions on the AC97 interface + "Mr. Bumpy" for feedback on the Z505RX + + Bill Nottingham, for generous assistance in getting the mixer ID + code working + ================= Previous versions ================= @@ -227,3 +274,11 @@ Version 0.75 renamed all the functions and files with slightly more generic names. + +Version 1.1 contains a change to try and detect non-AC97 versions of +the hardware, and not install itself appropriately. It should also +reinitialize the hardware on an APM resume event, assuming that APM +was configured into your kernel. + +Note that previous versions of this document claimed that recording was +8-bit only; it actually has been working for 16-bits all along. diff -u --recursive --new-file v2.2.13/linux/Documentation/sound/PSS linux/Documentation/sound/PSS --- v2.2.13/linux/Documentation/sound/PSS Wed Dec 31 16:00:00 1969 +++ linux/Documentation/sound/PSS Tue Jan 4 10:12:10 2000 @@ -0,0 +1,41 @@ +The PSS cards and other ECHO based cards provide an onboard DSP with +downloadable programs and also has an AD1848 "Microsoft Sound System" +device. The PSS driver enables MSS and MPU401 modes of the card. SB +is not enabled since it doesn't work concurrently with MSS. + +If you build this driver as a module then the driver takes the folowing +parameters + +pss_io. The I/O base the PSS card is configured at (normally 0x220 + or 0x240) + +mss_io The base address of the Microsoft Sound System interface. + This is normally 0x530, but may be 0x604 or other addresses. + +mss_irq The interrupt assigned to the Microsoft Sound System + emulation. IRQ's 3,5,7,9,10,11 and 12 are available. If you + get IRQ errors be sure to check the interrupt is set to + "ISA/Legacy" in the BIOS on modern machines. + +mss_dma The DMA channel used by the Microsoft Sound System. + This can be 0, 1, or 3. DMA 0 is not available on older + machines and will cause a crash on them. + +mpu_io The MPU emulation base address. This sets the base of the + synthesizer. It is typically 0x330 but can be altered. + +mpu_irq The interrupt to use for the synthesizer. It must differ + from the IRQ used by the Microsoft Sound System port. + + +The mpu_io/mpu_irq fields are optional. If they are not specified the +synthesizer parts are not configured. + +When the module is loaded it looks for a file called +/etc/sound/pss_synth. This is the firmware file from the DOS install disks. +This fil holds a general MIDI emulation. The file expected is called +genmidi.ld on newer DOS driver install disks and synth.ld on older ones. + +You can also load alternative DSP algorithms into the card if you wish. One +alternative driver can be found at http://www.mpg123.de/ + diff -u --recursive --new-file v2.2.13/linux/Documentation/telephony/ixj.txt linux/Documentation/telephony/ixj.txt --- v2.2.13/linux/Documentation/telephony/ixj.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/telephony/ixj.txt Tue Jan 4 10:12:10 2000 @@ -0,0 +1,406 @@ +Linux Quicknet-Drivers-Howto +Quicknet Technologies, Inc. (www.quicknet.net) +Version 0.3.4 December 18, 1999 + +1.0 Introduction + +This document describes the first GPL release version of the Linux +driver for the Quicknet Internet PhoneJACK and Internet LineJACK +cards. More information about these cards is available at +www.quicknet.net. The driver version discussed in this document is +0.3.4. + +These cards offer nice telco style interfaces to use your standard +telephone/key system/PBX as the user interface for VoIP applications. +The Internet LineJACK also offers PSTN connectivity for a single line +Internet to PSTN gateway. Of course, you can add more than one card +to a system to obtain multi-line functionality. At this time, the +driver supports the POTS port on both the Internet PhoneJACK and the +Internet LineJACK, but the PSTN port on the latter card is not yet +supported. + +This document, and the drivers for the cards, are intended for a +limited audience that includes technically capable programmers who +would like to experiment with Quicknet cards. The drivers are +considered in ALPHA status and are not yet considered stable enough +for general, widespread use in an unlimited audience. + +That's worth saying again: + +THE LINUX DRIVERS FOR QUICKNET CARDS ARE PRESENTLY IN A ALPHA STATE +AND SHOULD NOT BE CONSIDERED AS READY FOR NORMAL WIDESPREAD USE. + +They are released early in the spirit of Internet development and to +make this technology available to innovators who would benefit from +early exposure. + +When we promote the device driver to "beta" level it will be +considered ready for non-programmer, non-technical users. Until then, +please be aware that these drivers may not be stable and may affect +the performance of your system. + + +1.1 Latest Additions/Improvements + +The 0.3.4 version of the driver is the first GPL release. Several +features had to be removed from the prior binary only module, mostly +for reasons of Intellectual Property rights. We can't release +information that is not ours - so certain aspects of the driver had to +be removed to protect the rights of others. + +Specifically, very old Internet PhoneJACK cards have non-standard +G.723.1 codecs (due to the early nature of the DSPs in those days). +The auto-conversion code to bring those cards into compliance with +todays standards is available as a binary only module to those people +needing it. If you bought your card after 1997 or so, you are OK - +it's only the very old cards that are affected. + +Also, the code to download G.728/G.729/G.729a codecs to the DSP is +available as a binary only module as well. This IP is not ours to +release. + +Hooks are built into the GPL driver to allow it to work with other +companion modules that are completely separate from this module. + +1.2 Copyright, Trademarks, Disclaimer, & Credits + +Copyright + +Copyright (c) 1999 Quicknet Technologies, Inc. Permission is granted +to freely copy and distribute this document provided you preserve it +in its original form. For corrections and minor changes contact the +maintainer at linux@quicknet.net. + +Trademarks + +Internet PhoneJACK and Internet LineJACK are registered trademarks of +Quicknet Technologies, Inc. + +Disclaimer + +Much of the info in this HOWTO is early information released by +Quicknet Technologies, Inc. for the express purpose of allowing early +testing and use of the Linux drivers developed for their products. +While every attempt has been made to be thorough, complete and +accurate, the information contained here may be unreliable and there +are likely a number of errors in this document. Please let the +maintainer know about them. Since this is free documentation, it +should be obvious that neither I nor previous authors can be held +legally responsible for any errors. + +Credits + +This HOWTO was written by: + + Greg Herlein + Ed Okerson + +1.3 Future Plans: You Can Help + +Please let the maintainer know of any errors in facts, opinions, +logic, spelling, grammar, clarity, links, etc. But first, if the date +is over a month old, check to see that you have the latest +version. Please send any info that you think belongs in this document. + +You can also contribute code and/or bug-fixes for the sample +applications. + + +1.4 Where to get things + +You can download the latest versions of the driver from: + +http://www.quicknet.net/develop.htm + +You can download the latest version of this document from: + +http://www.quicknet.net/develop.htm + + +1.5 Mailing List + +Quicknet operates a mailing list to provide a public forum on using +these drivers. + +To subscribe to the linux-sdk mailing list, send an email to: + + majordomo@linux.quicknet.net + +In the body of the email, type: + + subscribe linux-sdk + +Please delete any signature block that you would normally add to the +bottom of your email - it tends to confuse majordomo. + +To send mail to the list, address your mail to + + linux-sdk@linux.quicknet.net + +Your message will go out to everyone on the list. + +To unsubscribe to the linux-sdk mailing list, send an email to: + + majordomo@linux.quicknet.net + +In the body of the email, type: + + unsubscribe linux-sdk + + + +2.0 Requirements + +2.1 Quicknet Card(s) + +You will need at least one Internet PhoneJACK or Internet LineJACK +cards. These are ISA or PCI bus devices that use Plug-n-Play for +configuration, and use no IRQs. The driver will support up to 16 +cards in any one system, of any mix between the two types. + +Note that you will need two cards to do any useful testing alone, since +you will need a card on both ends of the connection. Of course, if +you are doing collaborative work, perhaps your friends or coworkers +have cards too. If not, we'll gladly sell them some! + + +2.2 ISAPNP + +Since the Quicknet cards are Plug-n-Play devices, you will need the +isapnp tools package to configure the cards, or you can use the isapnp +module to autoconfigure them. The former package probably came with +your Linux distribution. Documentation on this package is available +online at: + +http://mailer.wiwi.uni-marburg.de/linux/LDP/HOWTO/Plug-and-Play-HOWTO.html + +The isapnp autoconfiguration is available on the Quicknet website at: + + http://www.quicknet.net/develop.htm + +though it may be in the kernel by the time you read this. + + +3.0 Card Configuration + +If you did not get your drivers as part of the linux kernel, do the +following to install them: + + a. untar the distribution file. We use the following command: + tar -xvzf ixj-0.x.x.tgz + +This creates a subdirectory holding all the necessary files. Go to that +subdirectory. + + b. run the "ixj_dev_create" script to remove any stray device +files left in the /dev directory, and to create the new officially +designated device files. Note that the old devices were called +/dev/ixj, and the new method uses /dev/phone. + + c. type "make;make install" - this will compile and install the +module. + + d. type "depmod -av" to rebuild all your kernel version dependencies. + + e. if you are using the isapnp module to configure the cards + automatically, then skip to step f. Otherwise, ensure that you + have run the isapnp configuration utility to properly configure + the cards. + + e1. The Internet PhoneJACK has one configuration register that + requires 16 IO ports. The Internet LineJACK card has two + configuration registers and isapnp reports that IO 0 + requires 16 IO ports and IO 1 requires 8. The Quicknet + driver assumes that these registers are configured to be + contiguous, i.e. if IO 0 is set to 0x340 then IO 1 should + be set to 0x350. + + Make sure that none of the cards overlap if you have + multiple cards in the system. + + If you are new to the isapnp tools, you can jumpstart + yourself by doing the following: + + e2. go to the /etc directory and run pnpdump to get a blank + isapnp.conf file. + + pnpdump > /etc/isapnp.conf + + e3. edit the /etc/isapnp.conf file to set the IO warnings and + the register IO addresses. The IO warnings means that you + should find the line in the file that looks like this: + + (CONFLICT (IO FATAL)(IRQ FATAL)(DMA FATAL)(MEM FATAL)) # or WARNING + + and you should edit the line to look like this: + + (CONFLICT (IO WARNING)(IRQ FATAL)(DMA FATAL)(MEM FATAL)) # + or WARNING + + The next step is to set the IO port addresses. The issue + here is that isapnp does not identify all of the ports out + there. Specifically any device that does not have a driver + or module loaded by Linux will not be registered. This + includes older sound cards and network cards. We have + found that the IO port 0x300 is often used even though + isapnp claims that no-one is using those ports. We + recommend that for a single card installation that port + 0x340 (and 0x350) be used. The IO port line should change + from this: + + (IO 0 (SIZE 16) (BASE 0x0300) (CHECK)) + + to this: + + (IO 0 (SIZE 16) (BASE 0x0340) ) + + e4. if you have multiple Quicknet cards, make sure that you do + not have any overlaps. Be especially careful if you are + mixing Internet PhoneJACK and Internet LineJACK cards in + the same system. In these cases we recommend moving the + IO port addresses to the 0x400 block. Please note that on + a few machines the 0x400 series are used. Feel free to + experiment with other addresses. Our cards have been + proven to work using IO addresses of up to 0xFF0. + + e5. the last step is to uncomment the activation line so the + drivers will be associated with the port. This means the + line (immediately below) the IO line should go from this: + + # (ACT Y) + + to this: + + (ACT Y) + + Once you have finished editing the isapnp.conf file you + must submit it into the pnp driverconfigure the cards. + This is done using the following command: + + isapnp isapnp.conf + + If this works you should see a line that identifies the + Quicknet device, the IO port(s) chosen, and a message + "Enabled OK". + + f. if you are loading the module by hand, use insmod. An example +of this would look like this: + + insmod phonedev + insmod ixj dspio=0x320,0x310 xio=0,0x330 + +Then verify the module loaded by running lsmod. If you are not using a +module that matches your kernel version, you may need to "force" the +load using the -f option in the insmod command. + + insmod phonedev + insmod -f ixj dspio=0x320,0x310 xio=0,0x330 + + +If you are using isapnp to autoconfigure your card, then you do NOT +need any of the above, though you need to use depmod to load the +driver, like this: + + depmod ixj + +which will result in the needed drivers getting loaded automatically. + + g. if you are planning on using kerneld to automatically load the +module for you, then you need to edit /etc/conf.modules and add the +following lines: + + options ixj dspio=0x340 xio=0x330 ixjdebug=0 + +If you do this, then when you execute an application that uses the +module kerneld will load the module for you. Note that to do this, +you need to have your kernel set to support kerneld. You can check +for this by looking at /usr/src/linux/.config and you should see this: + + # Loadable module support + # + + CONFIG_KMOD=y + + h. if you want non-root users to be able to read and write to the +ixj devices (this is a good idea!) you should do the following: + + - decide upon a group name to use and create that group if + needed. Add the user names to that group that you wish to + have access to the device. For example, we typically will + create a group named "ixj" in /etc/group and add all users + to that group that we want to run software that can use the + ixjX devices. + + - change the permissions on the device files, like this: + + chgrp ixj /dev/ixj* + chmod 660 /dev/ixj* + +Once this is done, then non-root users should be able to use the +devices. If you have enabled autoloading of modules, then the user +should be able to open the device and have the module loaded +automatically for them. + + +4.0 Driver Installation problems. + +We have tested these drivers on the 2.2.9, 2.2.10, 2.2.12, and 2.2.13 kernels +and in all cases have eventually been able to get the drivers to load and +run. We have found four types of problems that prevent this from happening. +The problems and solutions are: + + a. A step was missed in the installation. Go back and use section 3 +as a checklist. Many people miss running the ixj_dev_create script and thus +never load the device names into the filesystem. + + b. The kernel is inconsistently linked. We have found this problem in +the Out Of the Box installation of several distributions. The symptoms +are that neither driver will load, and that the unknown symbols include "jiffy" +and "kmalloc". The solution is to recompile both the kernel and the +modules. The command string for the final compile looks like this: + + In the kernel directory: + 1. cp .config /tmp + 2. make mrproper + 3. cp /tmp/.config . + 4. make dep;make clean;make bzImage;make modules;make modules_install + +This rebuilds both the kernel and all the modules and makes sure they all +have the same linkages. This generally solves the problem once the new +kernel is installed and the system rebooted. + + c. The kernel has been patched, then unpatched. This happens when +someone decides to use an earlier kernel after they load a later kernel. +The symptoms are proceeding through all three above steps and still not +being able to load the driver. What has happened is that the generated +header files are out of sync with the kernel itself. The solution is +to recompile (again) using "make mrproper". This will remove and then +regenerate all the necessary header files. Once this is done, then you +need to install and reboot the kernel. We have not seen any problem +loading one of our drivers after this treatment. + +5.0 Known Limitations + +We cannot currently play "dial-tone" and listen for DTMF digits at the +same time using the ISA PhoneJACK. This is a bug in the 8020 DSP chip +used on that product. All other Quicknet products function normally +in this regard. We have a work-around, but it's not done yet. Until +then, if you want dial-tone, you can always play a recorded dial-tone +sound into the audio until you have gathered the DTMF digits. + + + + + + + + + + + + + + + + + diff -u --recursive --new-file v2.2.13/linux/MAINTAINERS linux/MAINTAINERS --- v2.2.13/linux/MAINTAINERS Tue Jan 4 11:10:31 2000 +++ linux/MAINTAINERS Tue Jan 4 10:12:10 2000 @@ -103,7 +103,7 @@ ADVANSYS SCSI DRIVER P: Bob Frey -M: Bob Frey +M: linux@advansys.com W: http://www.advansys.com/linux.html L: linux-scsi@vger.rutgers.edu S: Maintained @@ -121,9 +121,10 @@ APM DRIVER P: Stephen Rothwell -M: Stephen.Rothwell@canb.auug.org.au +M: apm@linuxcare.com.au L: linux-laptop@vger.rutgers.edu -S: Maintained +W: http://linuxcare.com.au/apm/ +S: Supported APPLETALK NETWORK LAYER P: Jay Schulist @@ -203,6 +204,11 @@ W: http://www.fi.muni.cz/~kas/cosa/ S: Maintained +COMX/MULTIGATE SYNC SERIAL DRIVERS +P: Gergely Madarasz +M: Gergely Madarasz +S: Supported + CREDITS FILE P: John A. Martin M: jam@acm.org @@ -233,7 +239,7 @@ M: SteveW@ACM.org W: http://www.sucs.swan.ac.uk/~rohan/ W: http://www-sigproc.eng.cam.ac.uk/~sjw44/ -L: netdev@roxanne.nuclecu.unam.mx +L: netdev@oss.sgi.com S: Maintained DEVICE NUMBER REGISTRY @@ -452,9 +458,9 @@ JOYSTICK DRIVER P: Vojtech Pavlik -M: vojtech@ucw.cz +M: vojtech@suse.cz L: linux-joystick@atrey.karlin.mff.cuni.cz -W: http://atrey.karlin.mff.cuni.cz/~vojtech/joystick/ +W: http://www.suse.cz/development/joystick/ S: Maintained KERNEL AUTOMOUNTER (AUTOFS) @@ -484,8 +490,9 @@ LINUX FOR POWER MACINTOSH P: Paul Mackerras -M: paulus@cs.anu.edu.au -L: linux-pmac@samba.anu.edu.au +M: paulus@linuxcare.com +W: http://www.linuxppc.org/ +L: linuxppc-dev@lists.linuxppc.org S: Maintained LOGICAL VOLUME MANAGER @@ -494,6 +501,12 @@ W: http://linux.msede.com/lvm S: Maintained +MAESTRO PCI SOUND DRIVER +P: Zach Brown +M: zab@redhat.com +W: http://people.redhat.com/zab/maestro/ +S: Supported + M68K P: Jes Sorensen M: Jes.Sorensen@cern.ch @@ -573,7 +586,7 @@ NETWORKING [GENERAL] P: Networking Teak -M: netdev@nuclecu.unam.mx +M: netdev@oss.sgi.com L: linux-net@vger.rutgers.edu W: http://www.uk.linux.org/NetNews.html (2.0 only) S: Maintained @@ -585,7 +598,7 @@ M: ak@muc.de P: Alexey Kuznetsov M: kuznet@ms2.inr.ac.ru -L: netdev@roxanne.nuclecu.unam.mx +L: netdev@oss.sgi.com S: Maintained NFS CLIENT @@ -804,8 +817,8 @@ S: Maintained TLAN NETWORK DRIVER -P: James Banks -M: james@sovereign.org +P: Torben Mathiasen +M: torben.mathiasen@compaq.com L: tlan@vuser.vu.union.edu S: Maintained diff -u --recursive --new-file v2.2.13/linux/Makefile linux/Makefile --- v2.2.13/linux/Makefile Tue Jan 4 11:10:31 2000 +++ linux/Makefile Tue Jan 4 10:12:10 2000 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 2 -SUBLEVEL = 13 -EXTRAVERSION = +SUBLEVEL = 14 +EXTRAVERSION = ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) @@ -202,6 +202,10 @@ DRIVERS := $(DRIVERS) drivers/net/irda/irda_drivers.a endif +ifeq ($(CONFIG_PHONE),y) +DRIVERS := $(DRIVERS) drivers/telephony/telephony.a +endif + include arch/$(ARCH)/Makefile .S.s: @@ -338,8 +342,9 @@ if [ -f VIDEO_MODULES ]; then inst_mod VIDEO_MODULES video; fi; \ if [ -f FC4_MODULES ]; then inst_mod FC4_MODULES fc4; fi; \ if [ -f IRDA_MODULES ]; then inst_mod IRDA_MODULES net; fi; \ + if [ -f SK98LIN_MODULES ]; then inst_mod SK98LIN_MODULES net; fi; \ \ - for f in *.o; do [ -r $$f ] && echo $$f; done > $$MODLIB/.allmods; \ + for f in *.o; do [ -r $$f ] && echo $$f; done | sort > $$MODLIB/.allmods; \ echo $$MODULES | tr ' ' '\n' | sort | comm -23 $$MODLIB/.allmods - > $$MODLIB/.misc; \ if [ -s $$MODLIB/.misc ]; then inst_mod $$MODLIB/.misc misc; fi; \ rm -f $$MODLIB/.misc $$MODLIB/.allmods; \ @@ -381,6 +386,10 @@ rm -f drivers/net/hamradio/soundmodem/gentbl rm -f drivers/char/hfmodem/gentbl drivers/char/hfmodem/tables.h rm -f drivers/sound/*_boot.h drivers/sound/.*.boot + rm -f drivers/sound/msndinit.c + rm -f drivers/sound/msndperm.c + rm -f drivers/sound/pndsperm.c + rm -f drivers/sound/pndspini.c rm -f .version .config* config.in config.old rm -f scripts/tkparse scripts/kconfig.tk scripts/kconfig.tmp rm -f scripts/lxdialog/*.o scripts/lxdialog/lxdialog diff -u --recursive --new-file v2.2.13/linux/arch/alpha/Makefile linux/arch/alpha/Makefile --- v2.2.13/linux/arch/alpha/Makefile Mon Aug 9 16:05:54 1999 +++ linux/arch/alpha/Makefile Tue Jan 4 10:12:10 2000 @@ -60,9 +60,10 @@ # For TSUNAMI, we must have the assembler not emulate our instructions. # The same is true for POLARIS. +# The same is true for IRONGATE. # BWX is most important, but we don't really want any emulation ever. ifeq ($(old_gas),y) - ifneq ($(CONFIG_ALPHA_GENERIC)$(CONFIG_ALPHA_TSUNAMI)$(CONFIG_ALPHA_POLARIS),) + ifneq ($(CONFIG_ALPHA_GENERIC)$(CONFIG_ALPHA_TSUNAMI)$(CONFIG_ALPHA_POLARIS)$(CONFIG_ALPHA_IRONGATE),) # How do we do #error in make? CFLAGS := --error-please-upgrade-your-assembler endif @@ -79,6 +80,9 @@ ifeq ($(CONFIG_ALPHA_TSUNAMI),y) CFLAGS := $(CFLAGS) -Wa,-mev6 endif + ifeq ($(CONFIG_ALPHA_IRONGATE),y) + CFLAGS := $(CFLAGS) -Wa,-mev6 + endif endif HEAD := arch/alpha/kernel/head.o @@ -116,11 +120,9 @@ @$(MAKEBOOT) clean archmrproper: - -$(MAKE) -C arch/alpha/math-emu cleansymlinks archdep: @$(MAKEBOOT) dep - -$(MAKE) -C arch/alpha/math-emu symlinks bootpfile: @$(MAKEBOOT) bootpfile diff -u --recursive --new-file v2.2.13/linux/arch/alpha/config.in linux/arch/alpha/config.in --- v2.2.13/linux/arch/alpha/config.in Sat May 22 13:41:37 1999 +++ linux/arch/alpha/config.in Tue Jan 4 10:12:10 2000 @@ -33,10 +33,12 @@ EB64+ CONFIG_ALPHA_EB64P \ EB66 CONFIG_ALPHA_EB66 \ EB66+ CONFIG_ALPHA_EB66P \ + Eiger CONFIG_ALPHA_EIGER \ Jensen CONFIG_ALPHA_JENSEN \ LX164 CONFIG_ALPHA_LX164 \ Miata CONFIG_ALPHA_MIATA \ Mikasa CONFIG_ALPHA_MIKASA \ + Nautilus CONFIG_ALPHA_NAUTILUS \ Noname CONFIG_ALPHA_NONAME \ Noritake CONFIG_ALPHA_NORITAKE \ PC164 CONFIG_ALPHA_PC164 \ @@ -54,6 +56,7 @@ unset CONFIG_ALPHA_LCA CONFIG_ALPHA_APECS CONFIG_ALPHA_CIA unset CONFIG_ALPHA_T2 CONFIG_ALPHA_PYXIS CONFIG_ALPHA_POLARIS unset CONFIG_ALPHA_TSUNAMI CONFIG_ALPHA_MCPCIA +unset CONFIG_ALPHA_IRONGATE if [ "$CONFIG_ALPHA_GENERIC" = "y" ] then @@ -117,7 +120,7 @@ define_bool CONFIG_ALPHA_EV5 y define_bool CONFIG_ALPHA_PYXIS y fi -if [ "$CONFIG_ALPHA_DP264" = "y" ] +if [ "$CONFIG_ALPHA_DP264" = "y" -o "$CONFIG_ALPHA_EIGER" = "y" ] then define_bool CONFIG_PCI y define_bool CONFIG_ALPHA_EV6 y @@ -139,6 +142,12 @@ then define_bool CONFIG_ALPHA_EV4 y fi +if [ "$CONFIG_ALPHA_NAUTILUS" = "y" ] +then + define_bool CONFIG_PCI y + define_bool CONFIG_ALPHA_EV6 y + define_bool CONFIG_ALPHA_IRONGATE y +fi if [ "$CONFIG_ALPHA_CABRIOLET" = "y" -o "$CONFIG_ALPHA_AVANTI" = "y" \ -o "$CONFIG_ALPHA_EB64P" = "y" -o "$CONFIG_ALPHA_JENSEN" = "y" \ @@ -147,7 +156,8 @@ -o "$CONFIG_ALPHA_SABLE" = "y" -o "$CONFIG_ALPHA_MIATA" = "y" \ -o "$CONFIG_ALPHA_NORITAKE" = "y" -o "$CONFIG_ALPHA_PC164" = "y" \ -o "$CONFIG_ALPHA_LX164" = "y" -o "$CONFIG_ALPHA_SX164" = "y" \ - -o "$CONFIG_ALPHA_DP264" = "y" -o "$CONFIG_ALPHA_RAWHIDE" = "y" ] + -o "$CONFIG_ALPHA_DP264" = "y" -o "$CONFIG_ALPHA_RAWHIDE" = "y" \ + -o "$CONFIG_ALPHA_EIGER" = "y" ] then bool 'Use SRM as bootloader' CONFIG_ALPHA_SRM if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then diff -u --recursive --new-file v2.2.13/linux/arch/alpha/defconfig linux/arch/alpha/defconfig --- v2.2.13/linux/arch/alpha/defconfig Sat Jun 12 11:52:51 1999 +++ linux/arch/alpha/defconfig Tue Jan 4 10:12:10 2000 @@ -32,6 +32,7 @@ # CONFIG_ALPHA_LX164 is not set # CONFIG_ALPHA_MIATA is not set # CONFIG_ALPHA_MIKASA is not set +# CONFIG_ALPHA_NAUTILUS is not set # CONFIG_ALPHA_NONAME is not set # CONFIG_ALPHA_NORITAKE is not set # CONFIG_ALPHA_PC164 is not set diff -u --recursive --new-file v2.2.13/linux/arch/alpha/kernel/Makefile linux/arch/alpha/kernel/Makefile --- v2.2.13/linux/arch/alpha/kernel/Makefile Mon Aug 9 16:05:54 1999 +++ linux/arch/alpha/kernel/Makefile Tue Jan 4 10:12:11 2000 @@ -12,8 +12,6 @@ .S.o: $(CC) -D__ASSEMBLY__ $(AFLAGS) -c -o $*.o $< -all: kernel.o head.o - O_TARGET := kernel.o O_OBJS := entry.o traps.o process.o osf_sys.o irq.o signal.o setup.o \ bios32.o ptrace.o time.o fpreg.o @@ -22,12 +20,12 @@ ifdef CONFIG_ALPHA_GENERIC -O_OBJS += core_apecs.o core_cia.o core_lca.o core_mcpcia.o core_pyxis.o \ - core_t2.o core_tsunami.o core_polaris.o \ - sys_alcor.o sys_cabriolet.o sys_dp264.o sys_eb64p.o \ - sys_jensen.o sys_miata.o sys_mikasa.o sys_noritake.o \ - sys_rawhide.o sys_ruffian.o sys_sable.o sys_sio.o \ - sys_sx164.o sys_takara.o sys_rx164.o \ +O_OBJS += core_apecs.o core_cia.o core_irongate.o core_lca.o core_mcpcia.o \ + core_pyxis.o core_t2.o core_tsunami.o core_polaris.o \ + sys_alcor.o sys_cabriolet.o sys_dp264.o sys_eb64p.o sys_eiger.o \ + sys_jensen.o sys_miata.o sys_mikasa.o sys_nautilus.o \ + sys_noritake.o sys_rawhide.o sys_ruffian.o sys_rx164.o \ + sys_sable.o sys_sio.o sys_sx164.o sys_takara.o \ es1888.o smc37c669.o smc37c93x.o else @@ -38,6 +36,9 @@ ifdef CONFIG_ALPHA_CIA O_OBJS += core_cia.o endif +ifdef CONFIG_ALPHA_IRONGATE +O_OBJS += core_irongate.o +endif ifdef CONFIG_ALPHA_LCA O_OBJS += core_lca.o endif @@ -70,6 +71,9 @@ ifneq ($(CONFIG_ALPHA_EB64P)$(CONFIG_ALPHA_EB66),) O_OBJS += sys_eb64p.o endif +ifdef CONFIG_ALPHA_EIGER +O_OBJS += sys_eiger.o +endif ifdef CONFIG_ALPHA_JENSEN O_OBJS += sys_jensen.o endif @@ -79,6 +83,9 @@ ifdef CONFIG_ALPHA_MIKASA O_OBJS += sys_mikasa.o endif +ifdef CONFIG_ALPHA_NAUTILUS +O_OBJS += sys_nautilus.o +endif ifdef CONFIG_ALPHA_NORITAKE O_OBJS += sys_noritake.o endif @@ -121,7 +128,7 @@ O_OBJS += smp.o endif -all: kernel.o head.o +all: kernel.o head.o asm_offsets asm_offsets: check_asm ./check_asm > $(TOPDIR)/include/asm-alpha/asm_offsets.h diff -u --recursive --new-file v2.2.13/linux/arch/alpha/kernel/alpha_ksyms.c linux/arch/alpha/kernel/alpha_ksyms.c --- v2.2.13/linux/arch/alpha/kernel/alpha_ksyms.c Tue Jan 4 11:10:31 2000 +++ linux/arch/alpha/kernel/alpha_ksyms.c Tue Jan 4 10:12:11 2000 @@ -49,8 +49,6 @@ extern void __remqu (void); EXPORT_SYMBOL(alpha_mv); -EXPORT_SYMBOL(local_bh_count); -EXPORT_SYMBOL(local_irq_count); EXPORT_SYMBOL(enable_irq); EXPORT_SYMBOL(disable_irq); EXPORT_SYMBOL(disable_irq_nosync); @@ -182,6 +180,9 @@ EXPORT_SYMBOL(read_lock); #endif EXPORT_SYMBOL_NOVERS(kernel_flag); +#else /* __SMP__ */ +EXPORT_SYMBOL(local_bh_count); +EXPORT_SYMBOL(local_irq_count); #endif /* __SMP__ */ /* diff -u --recursive --new-file v2.2.13/linux/arch/alpha/kernel/bios32.c linux/arch/alpha/kernel/bios32.c --- v2.2.13/linux/arch/alpha/kernel/bios32.c Tue Dec 1 09:33:59 1998 +++ linux/arch/alpha/kernel/bios32.c Tue Jan 4 10:12:11 2000 @@ -78,6 +78,7 @@ struct linux_hose_info *bus2hose[256]; struct linux_hose_info *hose_head, **hose_tail = &hose_head; +struct linux_hose_info default_hose; int hose_count; int pci_probe_enabled; @@ -639,11 +640,11 @@ /* Bypass hi reg in the loop. */ dev->base_address[++idx] = 0; - printk("bios32 WARNING: " - "handling 64-bit device in " - "slot %d, function %d: \n", - PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn)); + DBG_DEVS(("bios32 WARNING: " + "handling 64-bit device in " + "slot %d, function %d: \n", + PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn))); } DBG_DEVS(("layout_dev: dev 0x%x MEM @ 0x%lx (0x%x)\n", @@ -1345,8 +1346,7 @@ /* For the benefit of single-bus machines, emulate a multi-bus machine to the (limited) extent necessary. Init all bus2hose entries to point to a dummy. */ - hose = kmalloc(sizeof(*hose), GFP_KERNEL); - memset(hose, 0, sizeof(*hose)); + hose = &default_hose; for (i = 0; i < 256; ++i) bus2hose[i] = hose; } diff -u --recursive --new-file v2.2.13/linux/arch/alpha/kernel/core_irongate.c linux/arch/alpha/kernel/core_irongate.c --- v2.2.13/linux/arch/alpha/kernel/core_irongate.c Wed Dec 31 16:00:00 1969 +++ linux/arch/alpha/kernel/core_irongate.c Tue Jan 4 10:12:11 2000 @@ -0,0 +1,368 @@ +/* + * linux/arch/alpha/kernel/core_irongate.c + * + * Code common to all IRONGATE core logic chips. + * + * Based on code written by David A. Rusling (david.rusling@reo.mts.dec.com). + * Copyright (C) 1999 Alpha Processor, Inc., (David Daniel, Stig Telfer, Soohoon Lee) + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define __EXTERN_INLINE inline +#include +#include +#undef __EXTERN_INLINE + +#include "proto.h" +#include "bios32.h" + +/* + * NOTE: Herein lie back-to-back mb instructions. They are magic. + * One plausible explanation is that the I/O controller does not properly + * handle the system transaction. Another involves timing. Ho hum. + */ + +/* + * BIOS32-style PCI interface: + */ + +/* #define DEBUG_CONFIG */ +#define DEBUG_MCHECK + +#ifdef DEBUG_CONFIG +# define DBG_CFG(args) printk args +#else +# define DBG_CFG(args) +#endif + +#ifdef DEBUG_MCHECK +# define DBG_MCK(args) printk args +#define DEBUG_MCHECK_DUMP +#else +# define DBG_MCK(args) +#endif + + +/* + * Given a bus, device, and function number, compute resulting + * configuration space address accordingly. It is therefore not safe + * to have concurrent invocations to configuration space access + * routines, but there really shouldn't be any need for this. + * + * addr[31:24] reserved + * addr[23:16] bus number (8 bits = 128 possible buses) + * addr[15:11] Device number (5 bits) + * addr[10: 8] function number + * addr[ 7: 2] register number + + * For IRONGATE: + * if (bus = addr[23:16]) == 0 + * then + * type 0 config cycle: + * addr_on_pci[31:11] = id selection for device = addr[15:11] + * addr_on_pci[10: 2] = addr[10: 2] ??? + * addr_on_pci[ 1: 0] = 00 + * else + * type 1 config cycle (pass on with no decoding): + * addr_on_pci[31:24] = 0 + * addr_on_pci[23: 2] = addr[23: 2] + * addr_on_pci[ 1: 0] = 01 + * fi + * + * Notes: + * The function number selects which function of a multi-function device + * (e.g., SCSI and Ethernet). + * + * The register selects a DWORD (32 bit) register offset. Hence it + * doesn't get shifted by 2 bits as we want to "drop" the bottom two + * bits. + */ + +static int +mk_conf_addr(u8 bus, u8 device_fn, u8 where, struct linux_hose_info *hose, + unsigned long *pci_addr, unsigned char *type1) +{ + unsigned long addr; + + DBG_CFG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, " + "pci_addr=0x%p, type1=0x%p)\n", + bus, device_fn, where, pci_addr, type1)); + + addr = (bus << 16) | (device_fn << 8) | where; + addr |= IRONGATE_CONF; + + *pci_addr = addr; + DBG_CFG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); + return 0; +} + +int +irongate_hose_read_config_byte (u8 bus, u8 device_fn, u8 where, u8 *value, + struct linux_hose_info *hose) +{ + unsigned long addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, hose, &addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + *value = __kernel_ldbu(*(vucp)addr); + return PCIBIOS_SUCCESSFUL; +} + +int +irongate_hose_read_config_word (u8 bus, u8 device_fn, u8 where, u16 *value, + struct linux_hose_info *hose) +{ + unsigned long addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, hose, &addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + *value = __kernel_ldwu(*(vusp)addr); + return PCIBIOS_SUCCESSFUL; +} + +int +irongate_hose_read_config_dword (u8 bus, u8 device_fn, u8 where, u32 *value, + struct linux_hose_info *hose) +{ + unsigned long addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, hose, &addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + *value = *(vuip)addr; + return PCIBIOS_SUCCESSFUL; +} + +int +irongate_hose_write_config_byte (u8 bus, u8 device_fn, u8 where, u8 value, + struct linux_hose_info *hose) +{ + unsigned long addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, hose, &addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + __kernel_stb(value, *(vucp)addr); + mb(); + __kernel_ldbu(*(vucp)addr); + return PCIBIOS_SUCCESSFUL; +} + +int +irongate_hose_write_config_word (u8 bus, u8 device_fn, u8 where, u16 value, + struct linux_hose_info *hose) +{ + unsigned long addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, hose, &addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + __kernel_stw(value, *(vusp)addr); + mb(); + __kernel_ldwu(*(vusp)addr); + return PCIBIOS_SUCCESSFUL; +} + +int +irongate_hose_write_config_dword (u8 bus, u8 device_fn, u8 where, u32 value, + struct linux_hose_info *hose) +{ + unsigned long addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, hose, &addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + *(vuip)addr = value; + mb(); + *(vuip)addr; + return PCIBIOS_SUCCESSFUL; +} + +#if 0 +static void +irongate_register_dump(const char *function_name) +{ + printk("%s: Irongate registers:\n" + "\tdev_vendor\t0x%08x\n" + "\tstat_cmd\t0x%08x\n" + "\tclass\t\t0x%08x\n" + "\tlatency\t\t0x%08x\n" + "\tbar0\t\t0x%08x\n" + "\tbar1\t\t0x%08x\n" + "\tbar2\t\t0x%08x\n" + "\trsrvd0[0]\t0x%08x\n" + "\trsrvd0[1]\t0x%08x\n" + "\trsrvd0[2]\t0x%08x\n" + "\trsrvd0[3]\t0x%08x\n" + "\trsrvd0[4]\t0x%08x\n" + "\trsrvd0[5]\t0x%08x\n" + "\tcapptr\t\t0x%08x\n" + "\trsrvd1[0]\t0x%08x\n" + "\trsrvd1[1]\t0x%08x\n" + "\tbacsr10\t\t0x%08x\n" + "\tbacsr32\t\t0x%08x\n" + "\tbacsr54\t\t0x%08x\n" + "\trsrvd2[0]\t0x%08x\n" + "\tdrammap\t\t0x%08x\n" + "\tdramtm\t\t0x%08x\n" + "\tdramms\t\t0x%08x\n" + "\trsrvd3[0]\t0x%08x\n" + "\tbiu0\t\t0x%08x\n" + "\tbiusip\t\t0x%08x\n" + "\trsrvd4[0]\t0x%08x\n" + "\trsrvd4[1]\t0x%08x\n" + "\tmro\t\t0x%08x\n" + "\trsrvd5[0]\t0x%08x\n" + "\trsrvd5[1]\t0x%08x\n" + "\trsrvd5[2]\t0x%08x\n" + "\twhami\t\t0x%08x\n" + "\tpciarb\t\t0x%08x\n" + "\tpcicfg\t\t0x%08x\n" + "\trsrvd6[0]\t0x%08x\n" + "\trsrvd6[1]\t0x%08x\n" + "\trsrvd6[2]\t0x%08x\n" + "\trsrvd6[3]\t0x%08x\n" + "\trsrvd6[4]\t0x%08x\n" + "\tagpcap\t\t0x%08x\n" + "\tagpstat\t\t0x%08x\n" + "\tagpcmd\t\t0x%08x\n" + "\tagpva\t\t0x%08x\n" + "\tagpmode\t\t0x%08x\n", + function_name, + IRONGATE0->dev_vendor, + IRONGATE0->stat_cmd, + IRONGATE0->class, + IRONGATE0->latency, + IRONGATE0->bar0, + IRONGATE0->bar1, + IRONGATE0->bar2, + IRONGATE0->rsrvd0[0], + IRONGATE0->rsrvd0[1], + IRONGATE0->rsrvd0[2], + IRONGATE0->rsrvd0[3], + IRONGATE0->rsrvd0[4], + IRONGATE0->rsrvd0[5], + IRONGATE0->capptr, + IRONGATE0->rsrvd1[0], + IRONGATE0->rsrvd1[1], + IRONGATE0->bacsr10, + IRONGATE0->bacsr32, + IRONGATE0->bacsr54, + IRONGATE0->rsrvd2[0], + IRONGATE0->drammap, + IRONGATE0->dramtm, + IRONGATE0->dramms, + IRONGATE0->rsrvd3[0], + IRONGATE0->biu0, + IRONGATE0->biusip, + IRONGATE0->rsrvd4[0], + IRONGATE0->rsrvd4[1], + IRONGATE0->mro, + IRONGATE0->rsrvd5[0], + IRONGATE0->rsrvd5[1], + IRONGATE0->rsrvd5[2], + IRONGATE0->whami, + IRONGATE0->pciarb, + IRONGATE0->pcicfg, + IRONGATE0->rsrvd6[0], + IRONGATE0->rsrvd6[1], + IRONGATE0->rsrvd6[2], + IRONGATE0->rsrvd6[3], + IRONGATE0->rsrvd6[4], + IRONGATE0->agpcap, + IRONGATE0->agpstat, + IRONGATE0->agpcmd, + IRONGATE0->agpva, + IRONGATE0->agpmode); +} +#else +#define irongate_register_dump(x) 1 +#endif + +void __init +irongate_init_arch(unsigned long *mem_start, unsigned long *mem_end) +{ + struct linux_hose_info *hose; + + irongate_register_dump(__FUNCTION__); + + /* Align memory to cache line; we'll be allocating from it. */ + + *mem_start = (*mem_start | 31) + 1; + + /* + * Irongate only supports one PCI bus but do the hose thing anyway. + * Anything to do for AGP??? + */ + + hose = (struct linux_hose_info *) *mem_start; + *mem_start = (unsigned long)(hose + 1); + memset(hose, 0, sizeof(*hose)); + hose->pci_io_space = IRONGATE_IO; + hose->pci_mem_space = IRONGATE_MEM; + hose->pci_config_space = IRONGATE_CONF; + hose->pci_sparse_space = 0; + hose->pci_hose_index = 0; + + /* add it to the hose list for bios32.[ch] */ + + *hose_tail = hose; + hose_tail = &hose->next; + + IRONGATE0->stat_cmd = IRONGATE0->stat_cmd & ~0x100; + irongate_pci_clr_err(); +} + +int +irongate_pci_clr_err(void) +{ + + unsigned int nmi_ctl=0; + unsigned int IRONGATE_jd; + +again: + + IRONGATE_jd = IRONGATE0->stat_cmd; + printk("Iron stat_cmd %x\n", IRONGATE_jd); + IRONGATE0->stat_cmd = IRONGATE_jd; /* write again clears error bits */ + mb(); + IRONGATE_jd = IRONGATE0->stat_cmd; /* re-read to force write */ + + IRONGATE_jd = IRONGATE0->dramms; + printk("Iron dramms %x\n", IRONGATE_jd); + IRONGATE0->dramms = IRONGATE_jd; /* write again clears error bits */ + mb(); + IRONGATE_jd = IRONGATE0->dramms; /* re-read to force write */ + + /* Clear ALI NMI */ + nmi_ctl = inb(0x61); + nmi_ctl |= 0x0c; + outb(nmi_ctl, 0x61); + nmi_ctl &= ~0x0c; + outb(nmi_ctl, 0x61); + + IRONGATE_jd = IRONGATE0->dramms; + if (IRONGATE_jd & 0x300) goto again; + + return 0; +} + diff -u --recursive --new-file v2.2.13/linux/arch/alpha/kernel/core_mcpcia.c linux/arch/alpha/kernel/core_mcpcia.c --- v2.2.13/linux/arch/alpha/kernel/core_mcpcia.c Tue Oct 19 17:10:36 1999 +++ linux/arch/alpha/kernel/core_mcpcia.c Tue Jan 4 10:12:11 2000 @@ -24,6 +24,7 @@ #include #undef __EXTERN_INLINE +#include "machvec.h" #include "proto.h" #include "bios32.h" @@ -51,9 +52,12 @@ static volatile unsigned int MCPCIA_mcheck_taken[NR_CPUS]; static volatile unsigned int MCPCIA_mcheck_hose[NR_CPUS]; static unsigned int MCPCIA_jd[NR_CPUS]; +static unsigned int MCPCIA_mcheck_enable_print = 1; +static unsigned int MCPCIA_mcheck_probing_hose = 0; -#define MCPCIA_MAX_HOSES 2 +#define MCPCIA_MAX_HOSES 4 +static struct linux_hose_info *mcpcia_hoses[MCPCIA_MAX_HOSES]; /* * Given a bus, device, and function number, compute resulting @@ -297,24 +301,65 @@ return PCIBIOS_SUCCESSFUL; } +static void +mcpcia_pci_clr_err(int cpu, int hose) +{ + MCPCIA_jd[cpu] = *(vuip)MCPCIA_CAP_ERR(hose); + *(vuip)MCPCIA_CAP_ERR(hose) = 0xffffffff; mb(); /* clear them all */ + MCPCIA_jd[cpu] = *(vuip)MCPCIA_CAP_ERR(hose); /* read to force write */ +} + void __init mcpcia_init_arch(unsigned long *mem_start, unsigned long *mem_end) { - extern asmlinkage void entInt(void); struct linux_hose_info *hose; - unsigned int mcpcia_err; - unsigned int pci_rev; - int h, cpu; - - /* Ho hum.. init_arch is called before init_IRQ, but we need to be - able to handle machine checks. So install the handler now. */ - wrent(entInt, 0); + int h; /* Align memory to cache line; we'll be allocating from it. */ *mem_start = (*mem_start | 31) + 1; + /* First, allocate for the maximum number of hoses we might have. */ + for (h = 0; h < MCPCIA_MAX_HOSES; h++) { + + hose = (struct linux_hose_info *)*mem_start; + *mem_start = (unsigned long)(hose + 1); + + memset(hose, 0, sizeof(*hose)); + + mcpcia_hoses[h] = hose; + + hose->pci_io_space = MCPCIA_IO(h); + hose->pci_mem_space = MCPCIA_DENSE(h); + hose->pci_config_space = MCPCIA_CONF(h); + hose->pci_sparse_space = MCPCIA_SPARSE(h); + hose->pci_hose_index = h; + hose->pci_first_busno = 255; + hose->pci_last_busno = 0; + } + +#if 1 + printk("mcpcia_init_arch: allocating for %d hoses\n", + MCPCIA_MAX_HOSES); +#endif +} + +/* + * This is called from init_IRQ, since we cannot take interrupts + * before then, so we cannot do this in init_arch. + */ +void __init +mcpcia_init_hoses(void) +{ + struct linux_hose_info *hose; + unsigned int mcpcia_err; + unsigned int pci_rev; + int h, cpu; + cpu = smp_processor_id(); + MCPCIA_mcheck_enable_print = 0; + MCPCIA_mcheck_probing_hose = 1; + /* First, find how many hoses we have. */ for (h = 0; h < MCPCIA_MAX_HOSES; h++) { @@ -324,50 +369,49 @@ mb(); mb(); draina(); + wrmces(7); MCPCIA_mcheck_expected[cpu] = 1; MCPCIA_mcheck_taken[cpu] = 0; + MCPCIA_mcheck_hose[cpu] = h; + pci_rev = 0xffffffff; mb(); /* Access the bus revision word. */ pci_rev = *(vuip)MCPCIA_REV(h); +#if 0 + draina(); /* huh? */ +#endif + mb(); mb(); /* magic */ if (MCPCIA_mcheck_taken[cpu]) { MCPCIA_mcheck_taken[cpu] = 0; pci_rev = 0xffffffff; mb(); + break; } MCPCIA_mcheck_expected[cpu] = 0; mb(); -#if 0 - printk("mcpcia_init_arch: got 0x%x for PCI_REV for hose %d\n", - pci_rev, h); -#endif if ((pci_rev >> 16) == PCI_CLASS_BRIDGE_HOST) { + hose_count++; - hose = (struct linux_hose_info *)*mem_start; - *mem_start = (unsigned long)(hose + 1); + mcpcia_pci_clr_err(cpu, h); - memset(hose, 0, sizeof(*hose)); + hose = mcpcia_hoses[h]; *hose_tail = hose; hose_tail = &hose->next; - - hose->pci_io_space = MCPCIA_IO(h); - hose->pci_mem_space = MCPCIA_DENSE(h); - hose->pci_config_space = MCPCIA_CONF(h); - hose->pci_sparse_space = MCPCIA_SPARSE(h); - hose->pci_hose_index = h; - hose->pci_first_busno = 255; - hose->pci_last_busno = 0; } } + MCPCIA_mcheck_enable_print = 1; + MCPCIA_mcheck_probing_hose = 0; + #if 1 - printk("mcpcia_init_arch: found %d hoses\n", hose_count); + printk("mcpcia_init_hoses: found %d hoses\n", hose_count); #endif /* Now do init for each hose. */ @@ -390,22 +434,16 @@ printk("MCPCIA_HBASE 0x%x\n", *(vuip)MCPCIA_HBASE(h)); #endif +#if 1 /* - * Set up error reporting. Make sure CPU_PE is OFF in the mask. + * Set up error reporting. */ -#if 0 - mcpcia_err = *(vuip)MCPCIA_ERR_MASK(h); - mcpcia_err &= ~4; - *(vuip)MCPCIA_ERR_MASK(h) = mcpcia_err; - mb(); - mcpcia_err = *(vuip)MCPCIA_ERR_MASK; -#endif - mcpcia_err = *(vuip)MCPCIA_CAP_ERR(h); mcpcia_err |= 0x0006; /* master/target abort */ *(vuip)MCPCIA_CAP_ERR(h) = mcpcia_err; mb() ; mcpcia_err = *(vuip)MCPCIA_CAP_ERR(h); +#endif switch (alpha_use_srm_setup) { @@ -486,11 +524,10 @@ case 0: /* * Set up the PCI->physical memory translation windows. - * For now, windows 1,2 and 3 are disabled. In the - * future, we may want to use them to do scatter/ - * gather DMA. + * For now, windows 2 and 3 are disabled. * * Window 0 goes at 2 GB and is 2 GB large. + * Window 1 goes at ? MB and is ? MB large, S/G. */ *(vuip)MCPCIA_W0_BASE(h) = 1U | (MCPCIA_DMA_WIN_BASE_DEFAULT & 0xfff00000U); @@ -551,14 +588,6 @@ } static void -mcpcia_pci_clr_err(int cpu, int hose) -{ - MCPCIA_jd[cpu] = *(vuip)MCPCIA_CAP_ERR(hose); - *(vuip)MCPCIA_CAP_ERR(hose) = 0xffffffff; mb(); /* clear them all */ - MCPCIA_jd[cpu] = *(vuip)MCPCIA_CAP_ERR(hose); /* read to force write */ -} - -static void mcpcia_print_uncorrectable(struct el_MCPCIA_uncorrected_frame_mcheck *logout) { struct el_common_EV5_uncorrectable_mcheck *frame; @@ -568,65 +597,64 @@ /* Print PAL fields */ for (i = 0; i < 24; i += 2) { - printk("\tpal temp[%d-%d]\t\t= %16lx %16lx\n", + printk(" paltmp[%2d-%2d] = %16lx %16lx\n", i, i+1, frame->paltemp[i], frame->paltemp[i+1]); } for (i = 0; i < 8; i += 2) { - printk("\tshadow[%d-%d]\t\t= %16lx %16lx\n", + printk(" shadow[%2d-%2d] = %16lx %16lx\n", i, i+1, frame->shadow[i], frame->shadow[i+1]); } - printk("\tAddr of excepting instruction\t= %16lx\n", + printk(" Addr of excepting instruction = %16lx\n", frame->exc_addr); - printk("\tSummary of arithmetic traps\t= %16lx\n", + printk(" Summary of arithmetic traps = %16lx\n", frame->exc_sum); - printk("\tException mask\t\t\t= %16lx\n", + printk(" Exception mask = %16lx\n", frame->exc_mask); - printk("\tBase address for PALcode\t= %16lx\n", + printk(" Base address for PALcode = %16lx\n", frame->pal_base); - printk("\tInterrupt Status Reg\t\t= %16lx\n", + printk(" Interrupt Status Reg = %16lx\n", frame->isr); - printk("\tCURRENT SETUP OF EV5 IBOX\t= %16lx\n", + printk(" CURRENT SETUP OF EV5 IBOX = %16lx\n", frame->icsr); - printk("\tI-CACHE Reg %s parity error\t= %16lx\n", - (frame->ic_perr_stat & 0x800L) ? - "Data" : "Tag", + printk(" I-CACHE Reg %s parity error = %16lx\n", + (frame->ic_perr_stat & 0x800L) ? "Data" : "Tag", frame->ic_perr_stat); - printk("\tD-CACHE error Reg\t\t= %16lx\n", + printk(" D-CACHE error Reg = %16lx\n", frame->dc_perr_stat); if (frame->dc_perr_stat & 0x2) { switch (frame->dc_perr_stat & 0x03c) { case 8: - printk("\t\tData error in bank 1\n"); + printk(" Data error in bank 1\n"); break; case 4: - printk("\t\tData error in bank 0\n"); + printk(" Data error in bank 0\n"); break; case 20: - printk("\t\tTag error in bank 1\n"); + printk(" Tag error in bank 1\n"); break; case 10: - printk("\t\tTag error in bank 0\n"); + printk(" Tag error in bank 0\n"); break; } } - printk("\tEffective VA\t\t\t= %16lx\n", + printk(" Effective VA = %16lx\n", frame->va); - printk("\tReason for D-stream\t\t= %16lx\n", + printk(" Reason for D-stream = %16lx\n", frame->mm_stat); - printk("\tEV5 SCache address\t\t= %16lx\n", + printk(" EV5 SCache address = %16lx\n", frame->sc_addr); - printk("\tEV5 SCache TAG/Data parity\t= %16lx\n", + printk(" EV5 SCache TAG/Data parity = %16lx\n", frame->sc_stat); - printk("\tEV5 BC_TAG_ADDR\t\t\t= %16lx\n", + printk(" EV5 BC_TAG_ADDR = %16lx\n", frame->bc_tag_addr); - printk("\tEV5 EI_ADDR: Phys addr of Xfer\t= %16lx\n", + printk(" EV5 EI_ADDR: Phys addr of Xfer = %16lx\n", frame->ei_addr); - printk("\tFill Syndrome\t\t\t= %16lx\n", + printk(" Fill Syndrome = %16lx\n", frame->fill_syndrome); - printk("\tEI_STAT reg\t\t\t= %16lx\n", + printk(" EI_STAT reg = %16lx\n", frame->ei_stat); - printk("\tLD_LOCK\t\t\t\t= %16lx\n", + printk(" LD_LOCK = %16lx\n", frame->ld_lock); } @@ -636,32 +664,53 @@ { struct el_common *mchk_header; struct el_MCPCIA_uncorrected_frame_mcheck *mchk_logout; - unsigned int cpu = smp_processor_id(); + unsigned int cpu; +#if 0 +halt(); +#endif + mb(); + mb(); /* magic */ +#if 0 + draina(); +#endif mchk_header = (struct el_common *)la_ptr; mchk_logout = (struct el_MCPCIA_uncorrected_frame_mcheck *)la_ptr; - mb(); - mb(); /* magic */ - draina(); - if (MCPCIA_mcheck_expected[cpu]) + cpu = smp_processor_id(); + + if (!MCPCIA_mcheck_probing_hose) { + + if (MCPCIA_mcheck_expected[cpu]) mcpcia_pci_clr_err(cpu, MCPCIA_mcheck_hose[cpu]); - else { + else { /* FIXME: how do we figure out which hose the error was on? */ mcpcia_pci_clr_err(cpu, 0); mcpcia_pci_clr_err(cpu, 1); + } + + } else { + /* FIXME: clear out known always good hoses */ + mcpcia_pci_clr_err(cpu, 0); + mcpcia_pci_clr_err(cpu, 1); } + wrmces(0x7); mb(); - process_mcheck_info(vector, la_ptr, regs, "MCPCIA", - DEBUG_MCHECK, MCPCIA_mcheck_expected[cpu]); + if (MCPCIA_mcheck_enable_print) { + + process_mcheck_info(vector, la_ptr, regs, "MCPCIA", + DEBUG_MCHECK, MCPCIA_mcheck_expected[cpu]); - if (vector != 0x620 && vector != 0x630 - && ! MCPCIA_mcheck_expected[cpu]) { + if (vector != 0x620 && vector != 0x630 + && ! MCPCIA_mcheck_expected[cpu]) { mcpcia_print_uncorrectable(mchk_logout); + } } +#if 0 MCPCIA_mcheck_expected[cpu] = 0; +#endif MCPCIA_mcheck_taken[cpu] = 1; } diff -u --recursive --new-file v2.2.13/linux/arch/alpha/kernel/core_pyxis.c linux/arch/alpha/kernel/core_pyxis.c --- v2.2.13/linux/arch/alpha/kernel/core_pyxis.c Mon Aug 9 16:05:54 1999 +++ linux/arch/alpha/kernel/core_pyxis.c Tue Jan 4 10:12:11 2000 @@ -43,8 +43,6 @@ static volatile unsigned int PYXIS_mcheck_expected = 0; static volatile unsigned int PYXIS_mcheck_taken = 0; -static unsigned int PYXIS_jd; - /* * Given a bus, device, and function number, compute resulting @@ -526,23 +524,17 @@ pyxis_machine_check(unsigned long vector, unsigned long la_ptr, struct pt_regs * regs) { - struct el_common *mchk_header; - struct el_PYXIS_sysdata_mcheck *mchk_sysdata; - - mchk_header = (struct el_common *)la_ptr; - - mchk_sysdata = (struct el_PYXIS_sysdata_mcheck *) - (la_ptr + mchk_header->sys_offset); + unsigned int tmp; /* Clear the error before reporting anything. */ mb(); mb(); /* magic */ draina(); - PYXIS_jd = *(vuip)PYXIS_ERR; - *(vuip)PYXIS_ERR = PYXIS_jd; + tmp = *(vuip)PYXIS_ERR; + *(vuip)PYXIS_ERR = tmp; mb(); - PYXIS_jd = *(vuip)PYXIS_ERR; /* re-read to force write */ + *(vuip)PYXIS_ERR; /* re-read to force write */ wrmces(0x7); mb(); diff -u --recursive --new-file v2.2.13/linux/arch/alpha/kernel/core_tsunami.c linux/arch/alpha/kernel/core_tsunami.c --- v2.2.13/linux/arch/alpha/kernel/core_tsunami.c Mon Aug 9 16:05:54 1999 +++ linux/arch/alpha/kernel/core_tsunami.c Tue Jan 4 10:12:11 2000 @@ -164,6 +164,8 @@ return PCIBIOS_DEVICE_NOT_FOUND; __kernel_stb(value, *(vucp)addr); + mb(); + __kernel_ldbu(*(vucp)addr); return PCIBIOS_SUCCESSFUL; } @@ -178,6 +180,8 @@ return PCIBIOS_DEVICE_NOT_FOUND; __kernel_stw(value, *(vusp)addr); + mb(); + __kernel_ldwu(*(vusp)addr); return PCIBIOS_SUCCESSFUL; } @@ -192,6 +196,8 @@ return PCIBIOS_DEVICE_NOT_FOUND; *(vuip)addr = value; + mb(); + *(vuip)addr; return PCIBIOS_SUCCESSFUL; } diff -u --recursive --new-file v2.2.13/linux/arch/alpha/kernel/entry.S linux/arch/alpha/kernel/entry.S --- v2.2.13/linux/arch/alpha/kernel/entry.S Mon May 10 09:55:21 1999 +++ linux/arch/alpha/kernel/entry.S Tue Jan 4 10:12:11 2000 @@ -82,8 +82,8 @@ ldq $1,8($30); \ ldq $2,16($30); \ ldq $3,24($30); \ - ldq $20,152($30); \ - ldq $21,HAE_CACHE($19); \ + ldq $20,HAE_CACHE($19); \ + ldq $21,152($30); \ ldq $4,32($30); \ ldq $5,40($30); \ ldq $6,48($30); \ diff -u --recursive --new-file v2.2.13/linux/arch/alpha/kernel/irq.c linux/arch/alpha/kernel/irq.c --- v2.2.13/linux/arch/alpha/kernel/irq.c Mon Aug 9 16:05:54 1999 +++ linux/arch/alpha/kernel/irq.c Tue Jan 4 10:12:11 2000 @@ -35,12 +35,16 @@ #define vulp volatile unsigned long * #define vuip volatile unsigned int * -unsigned int local_irq_count[NR_CPUS]; -unsigned int local_bh_count[NR_CPUS]; -unsigned long hardirq_no[NR_CPUS]; +/* Only uniprocessor needs this IRQ/BH locking depth, on SMP it + * lives in the per-cpu structure for cache reasons. + */ +#ifndef __SMP__ +unsigned long local_bh_count; +unsigned long local_irq_count; +#endif -#if NR_IRQS > 64 -# error Unable to handle more than 64 irq levels. +#if NR_IRQS > 128 +# error Unable to handle more than 128 irq levels. #endif #ifdef CONFIG_ALPHA_GENERIC @@ -57,7 +61,7 @@ /* * Shadow-copy of masked interrupts. */ -unsigned long alpha_irq_mask = ~0UL; +unsigned long _alpha_irq_masks[2] = {~0UL, ~0UL }; /* * The ack_irq routine used by 80% of the systems. @@ -107,6 +111,8 @@ # define IACK_SC TSUNAMI_IACK_SC #elif defined(CONFIG_ALPHA_POLARIS) # define IACK_SC POLARIS_IACK_SC +#elif defined(CONFIG_ALPHA_IRONGATE) +# define IACK_SC IRONGATE_IACK_SC #else /* This is bogus but necessary to get it to compile on all platforms. */ # define IACK_SC 1L @@ -182,13 +188,15 @@ static inline void mask_irq(unsigned long irq) { - alpha_mv.update_irq_hw(irq, alpha_irq_mask |= 1UL << irq, 0); + set_bit(irq, _alpha_irq_masks); + alpha_mv.update_irq_hw(irq, alpha_irq_mask, 0); } static inline void unmask_irq(unsigned long irq) { - alpha_mv.update_irq_hw(irq, alpha_irq_mask &= ~(1UL << irq), 1); + clear_bit(irq, _alpha_irq_masks); + alpha_mv.update_irq_hw(irq, alpha_irq_mask, 1); } void @@ -205,7 +213,7 @@ disable_irq(unsigned int irq_nr) { /* This works non-SMP, and SMP until we write code to distribute - interrupts to more that cpu 0. */ + interrupts to more than CPU 0. */ disable_irq_nosync(irq_nr); } @@ -384,6 +392,8 @@ static void show(char * str, void *where); +#define SYNC_OTHER_CPUS(x) udelay((x)+1) + static inline void wait_on_irq(int cpu, void *where) { @@ -397,8 +407,7 @@ * already executing in one.. */ if (!atomic_read(&global_irq_count)) { - if (local_bh_count[cpu] || - !atomic_read(&global_bh_count)) + if (local_bh_count || !atomic_read(&global_bh_count)) break; } @@ -412,18 +421,14 @@ count = MAXCOUNT; } __sti(); -#if 0 - SYNC_OTHER_CORES(cpu); -#else - udelay(cpu+1); -#endif + SYNC_OTHER_CPUS(cpu); __cli(); if (atomic_read(&global_irq_count)) continue; - if (global_irq_lock.lock) + if (spin_is_locked(&global_irq_lock)) continue; - if (!local_bh_count[cpu] && + if (!local_bh_count && atomic_read(&global_bh_count)) continue; if (spin_trylock(&global_irq_lock)) @@ -437,13 +442,8 @@ { if (!spin_trylock(&global_irq_lock)) { /* do we already hold the lock? */ - if (cpu == global_irq_holder) { -#if 0 - printk("get_irqlock: already held at %08lx\n", - previous_irqholder); -#endif + if (cpu == global_irq_holder) return; - } /* Uhhuh.. Somebody else got it. Wait.. */ spin_lock(&global_irq_lock); } @@ -469,25 +469,19 @@ void __global_cli(void) { - int cpu; - void *where = __builtin_return_address(0); - /* * Maximize ipl. If ipl was previously 0 and if this thread * is not in an irq, then take global_irq_lock. */ - if ((swpipl(7) == 0) && !local_irq_count[cpu = smp_processor_id()]) - get_irqlock(cpu, where); + if ((swpipl(7) == 0) && !local_irq_count) + get_irqlock(smp_processor_id(), __builtin_return_address(0)); } void __global_sti(void) { - int cpu = smp_processor_id(); - - if (!local_irq_count[cpu]) { - release_irqlock(cpu); - } + if (!local_irq_count) + release_irqlock(smp_processor_id()); __sti(); } @@ -512,7 +506,7 @@ retval = 2 + local_enabled; /* Check for global flags if we're not in an interrupt. */ - if (!local_irq_count[cpu]) { + if (!local_irq_count) { if (local_enabled) retval = 1; if (global_irq_holder == cpu) @@ -566,11 +560,10 @@ hardirq_enter(cpu, irq); barrier(); - while (global_irq_lock.lock) { + while (spin_is_locked(&global_irq_lock)) { if (cpu == global_irq_holder) { - int globl_locked = global_irq_lock.lock; + int globl_locked = spin_is_locked(&global_irq_lock); int globl_icount = atomic_read(&global_irq_count); - int local_count = local_irq_count[cpu]; /* It is very important that we load the state variables before we do the first call to @@ -578,9 +571,9 @@ them... */ printk("CPU[%d]: where [%p] glocked[%d] gicnt[%d]" - " licnt[%d]\n", + " licnt[%ld]\n", cpu, previous_irqholder, globl_locked, - globl_icount, local_count); + globl_icount, local_irq_count); #ifdef VERBOSE_IRQLOCK_DEBUGGING printk("Performing backtrace on all CPUs," " write this down!\n"); @@ -609,19 +602,17 @@ #endif int cpu = smp_processor_id(); - int global_count = atomic_read(&global_irq_count); - int local_count0 = local_irq_count[0]; - int local_count1 = local_irq_count[1]; - long hardirq_no0 = hardirq_no[0]; - long hardirq_no1 = hardirq_no[1]; - printk("\n%s, CPU %d: %p\n", str, cpu, where); - printk("irq: %d [%d(0x%016lx) %d(0x%016lx)]\n", global_count, - local_count0, hardirq_no0, local_count1, hardirq_no1); + printk("irq: %d [%ld %ld]\n", + atomic_read(&global_irq_count), + cpu_data[0].irq_count, + cpu_data[1].irq_count); + + printk("bh: %d [%ld %ld]\n", + atomic_read(&global_bh_count), + cpu_data[0].bh_count, + cpu_data[1].bh_count); - printk("bh: %d [%d %d]\n", - atomic_read(&global_bh_count), local_bh_count[0], - local_bh_count[1]); #if 0 stack = (unsigned long *) &str; for (i = 40; i ; i--) { @@ -644,6 +635,7 @@ count = ~0; } /* nothing .. wait for the other bh's to go away */ + barrier(); } while (atomic_read(&global_bh_count) != 0); } @@ -658,12 +650,8 @@ void synchronize_bh(void) { - if (atomic_read(&global_bh_count)) { - int cpu = smp_processor_id(); - if (!local_irq_count[cpu] && !local_bh_count[cpu]) { + if (atomic_read(&global_bh_count) && !in_interrupt()) wait_on_bh(); - } - } } /* @@ -680,6 +668,7 @@ void synchronize_irq(void) { +#ifdef JOES_ORIGINAL_VERSION int cpu = smp_processor_id(); int local_count; int global_count; @@ -688,7 +677,7 @@ mb(); do { - local_count = local_irq_count[cpu]; + local_count = local_irq_count; global_count = atomic_read(&global_irq_count); if (DEBUG_SYNCHRONIZE_IRQ && (--countdown == 0)) { printk("%d:%d/%d\n", cpu, local_count, global_count); @@ -696,12 +685,19 @@ break; } } while (global_count != local_count); +#else + if (atomic_read(&global_irq_count)) { + /* Stupid approach */ + cli(); + sti(); + } +#endif } #else /* !__SMP__ */ -#define irq_enter(cpu, irq) (++local_irq_count[cpu]) -#define irq_exit(cpu, irq) (--local_irq_count[cpu]) +#define irq_enter(cpu, irq) (++local_irq_count) +#define irq_exit(cpu, irq) (--local_irq_count) #endif /* __SMP__ */ @@ -816,7 +812,8 @@ unsigned long delay; unsigned int i; - for (i = ACTUAL_NR_IRQS - 1; i > 0; i--) { + /* Handle only the first 64 IRQs here. */ + for (i = (ACTUAL_NR_IRQS - 1) & 63; i > 0; i--) { if (!(PROBE_MASK & (1UL << i))) { continue; } @@ -849,6 +846,7 @@ { int i; + /* Handle only the first 64 IRQs here. */ irqs &= alpha_irq_mask; if (!irqs) return 0; @@ -933,6 +931,19 @@ (expected?"expected.":"NOT expected!!!")); if (expected) return; + + /* Just in case we get some incomplete arguments... */ + if (!la_ptr) { + if (!regs) + printk(KERN_CRIT "%s machine check: vector=0x%lx\n", + machine, vector); + else + printk(KERN_CRIT "%s machine check: vector=0x%lx" + " pc=0x%lx ra=0x%lx args=0x%lx/0x%lx/0x%lx\n", + machine, vector, regs->pc, regs->r26, + regs->r16, regs->r17, regs->r18); + return; + } mchk_header = (struct el_common *)la_ptr; diff -u --recursive --new-file v2.2.13/linux/arch/alpha/kernel/irq.h linux/arch/alpha/kernel/irq.h --- v2.2.13/linux/arch/alpha/kernel/irq.h Mon Aug 9 16:05:54 1999 +++ linux/arch/alpha/kernel/irq.h Tue Jan 4 10:12:11 2000 @@ -9,6 +9,7 @@ */ #include +#include #define STANDARD_INIT_IRQ_PROLOG \ outb(0, DMA1_RESET_REG); \ @@ -16,7 +17,8 @@ outb(0, DMA1_CLR_MASK_REG); \ outb(0, DMA2_CLR_MASK_REG) -extern unsigned long alpha_irq_mask; +extern unsigned long _alpha_irq_masks[2]; +#define alpha_irq_mask _alpha_irq_masks[0] extern void generic_ack_irq(unsigned long irq); extern void isa_device_interrupt(unsigned long vector, struct pt_regs * regs); @@ -47,4 +49,41 @@ #else #define TIMER_IRQ RTC_IRQ /* timer is the rtc */ #endif + +/* + * PROBE_MASK is the bitset of irqs that we consider for autoprobing. + */ + +/* NOTE: we only handle the first 64 IRQs in this code. */ + +/* The normal mask includes all the IRQs except timer IRQ 0. */ +#define _PROBE_MASK(nr_irqs) \ + (((nr_irqs > 63) ? ~0UL : ((1UL << (nr_irqs & 63)) - 1)) & ~1UL) + +/* Mask out unused timer irq 0 and RTC irq 8. */ +#define P2K_PROBE_MASK (_PROBE_MASK(16) & ~0x101UL) + +/* Mask out unused timer irq 0, "irqs" 20-30, and the EISA cascade. */ +#define ALCOR_PROBE_MASK (_PROBE_MASK(48) & ~0xfff000000001UL) + +/* Leave timer IRQ 0 in the mask. */ +#define RUFFIAN_PROBE_MASK (_PROBE_MASK(48) | 1UL) + +/* Do not probe/enable beyond the PCI devices. */ +#define TSUNAMI_PROBE_MASK _PROBE_MASK(48) + +#if defined(CONFIG_ALPHA_GENERIC) +# define PROBE_MASK alpha_mv.irq_probe_mask +#elif defined(CONFIG_ALPHA_P2K) +# define PROBE_MASK P2K_PROBE_MASK +#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT) +# define PROBE_MASK ALCOR_PROBE_MASK +#elif defined(CONFIG_ALPHA_RUFFIAN) +# define PROBE_MASK RUFFIAN_PROBE_MASK +#elif defined(CONFIG_ALPHA_DP264) +# define PROBE_MASK TSUNAMI_PROBE_MASK +#else +# define PROBE_MASK _PROBE_MASK(NR_IRQS) +#endif + diff -u --recursive --new-file v2.2.13/linux/arch/alpha/kernel/machvec.h linux/arch/alpha/kernel/machvec.h --- v2.2.13/linux/arch/alpha/kernel/machvec.h Mon Aug 9 16:05:54 1999 +++ linux/arch/alpha/kernel/machvec.h Tue Jan 4 10:12:11 2000 @@ -18,6 +18,11 @@ we can read and write it as we like. ;-) */ #define POLARIS_HAE_ADDRESS (&alpha_mv.hae_cache) +/* Whee. IRONGATE doesn't have an HAE. Fix things up for the GENERIC + kernel by defining the HAE address to be that of the cache. Now + we can read and write it as we like. ;-) */ +#define IRONGATE_HAE_ADDRESS (&alpha_mv.hae_cache) + /* Only a few systems don't define IACK_SC, handling all interrupts through the SRM console. But splitting out that one case from IO() below seems like such a pain. Define this to get things to compile. */ @@ -90,6 +95,7 @@ #define DO_APECS_IO IO(APECS,apecs,apecs) #define DO_CIA_IO IO(CIA,cia,cia) +#define DO_IRONGATE_IO IO(IRONGATE,irongate,irongate) #define DO_LCA_IO IO(LCA,lca,lca) #define DO_MCPCIA_IO IO(MCPCIA,mcpcia,mcpcia) #define DO_PYXIS_IO IO(PYXIS,pyxis_bw,pyxis) @@ -103,6 +109,7 @@ #define DO_APECS_BUS BUS(apecs) #define DO_CIA_BUS BUS(cia) +#define DO_IRONGATE_BUS BUS(irongate) #define DO_LCA_BUS BUS(lca) #define DO_MCPCIA_BUS BUS(mcpcia) #define DO_PYXIS_BUS BUS(pyxis) diff -u --recursive --new-file v2.2.13/linux/arch/alpha/kernel/process.c linux/arch/alpha/kernel/process.c --- v2.2.13/linux/arch/alpha/kernel/process.c Tue Oct 19 17:10:36 1999 +++ linux/arch/alpha/kernel/process.c Tue Jan 4 10:12:11 2000 @@ -117,58 +117,44 @@ } } -void -generic_kill_arch (int mode, char *restart_cmd) -{ - /* The following currently only has any effect on SRM. We should - fix MILO to understand it. Should be pretty easy. Also we can - support RESTART2 via the ipc_buffer machinations pictured below, - which SRM ignores. */ - - if (alpha_using_srm) { - struct percpu_struct *cpup; - unsigned long flags; - - cpup = (struct percpu_struct *) - ((unsigned long)hwrpb + hwrpb->processor_offset); - - flags = cpup->flags; - - /* Clear reason to "default"; clear "bootstrap in progress". */ - flags &= ~0x00ff0001UL; - - if (mode == LINUX_REBOOT_CMD_RESTART) { - if (!restart_cmd) { - flags |= 0x00020000UL; /* "cold bootstrap" */ - cpup->ipc_buffer[0] = 0; - } else { - flags |= 0x00030000UL; /* "warm bootstrap" */ - strncpy((char *)cpup->ipc_buffer, restart_cmd, - sizeof(cpup->ipc_buffer)); - } - } else { - flags |= 0x00040000UL; /* "remain halted" */ - } - - cpup->flags = flags; - mb(); - - reset_for_srm(); - set_hae(srm_hae); +struct halt_info { + int mode; + char * restart_cmd; +}; -#ifdef CONFIG_DUMMY_CONSOLE - /* This has the effect of reseting the VGA video origin. */ - take_over_console(&dummy_con, 0, MAX_NR_CONSOLES-1, 1); -#endif +static void +halt_processor(void * generic_ptr) +{ + struct percpu_struct * cpup; + struct halt_info * how = (struct halt_info *)generic_ptr; + unsigned long *flags; + int cpuid = smp_processor_id(); + + /* No point in taking interrupts anymore. */ + __cli(); + + cpup = (struct percpu_struct *) + ((unsigned long)hwrpb + hwrpb->processor_offset + + hwrpb->processor_size * cpuid); + flags = &cpup->flags; + + /* Clear reason to "default"; clear "bootstrap in progress". */ + *flags &= ~0x00ff0001UL; + +#ifdef __SMP__ + /* Secondaries halt here. */ + if (cpuid != smp_boot_cpuid) { + *flags |= 0x00040000UL; /* "remain halted" */ + clear_bit(cpuid, &cpu_present_mask); + halt(); } +#endif /* __SMP__ */ #ifdef CONFIG_RTC /* Reset rtc to defaults. */ { unsigned char control; - cli(); - /* Reset periodic interrupt frequency. */ CMOS_WRITE(0x26, RTC_FREQ_SELECT); @@ -177,22 +163,73 @@ control |= RTC_PIE; CMOS_WRITE(control, RTC_CONTROL); CMOS_READ(RTC_INTR_FLAGS); - - sti(); } -#endif +#endif /* CONFIG_RTC */ - if (!alpha_using_srm && mode != LINUX_REBOOT_CMD_RESTART) { + if (how->mode == LINUX_REBOOT_CMD_RESTART) { + if (!how->restart_cmd) { + *flags |= 0x00020000UL; /* "cold bootstrap" */ + cpup->ipc_buffer[0] = 0; + } else { + /* NOTE: this could really only work when returning + into MILO, rather than SRM console. The latter + does NOT look at the ipc_buffer to get a new + boot command. It could be done by using callbacks + to change some of the SRM environment variables, + but that is beyond our capabilities at this time. + At the moment, SRM will use the last boot device, + but the file and flags will be the defaults, when + doing a "warm" bootstrap. + */ + *flags |= 0x00030000UL; /* "warm bootstrap" */ + strncpy((char *)cpup->ipc_buffer, + how->restart_cmd, + sizeof(cpup->ipc_buffer)); + } + } else + *flags |= 0x00040000UL; /* "remain halted" */ + +#ifdef __SMP__ + /* Wait for the secondaries to halt. */ + clear_bit(smp_boot_cpuid, &cpu_present_mask); + while (cpu_present_mask) + /* Make sure we sample memory and not a register. */ + barrier(); +#endif /* __SMP__ */ + + /* If booted from SRM, reset some of the original environment. */ + if (alpha_using_srm) { +#ifdef CONFIG_DUMMY_CONSOLE + /* This has the effect of resetting the VGA video origin. */ + take_over_console(&dummy_con, 0, MAX_NR_CONSOLES-1, 1); +#endif + reset_for_srm(); + set_hae(srm_hae); + } + else if (how->mode != LINUX_REBOOT_CMD_RESTART && + how->mode != LINUX_REBOOT_CMD_RESTART2) { /* Unfortunately, since MILO doesn't currently understand the hwrpb bits above, we can't reliably halt the processor and keep it halted. So just loop. */ return; } - if (alpha_using_srm) - srm_paging_stop(); - + /* PRIMARY */ halt(); +} + +void +generic_kill_arch(int mode, char * restart_cmd) +{ + struct halt_info copy_of_args; + + copy_of_args.mode = mode; + copy_of_args.restart_cmd = restart_cmd; +#ifdef __SMP__ + /* A secondary can't wait here for the primary to finish, can it now? */ + smp_call_function(halt_processor, (void *)©_of_args, 1, 0); +#endif /* __SMP__ */ + halt_processor(©_of_args); } void diff -u --recursive --new-file v2.2.13/linux/arch/alpha/kernel/proto.h linux/arch/alpha/kernel/proto.h --- v2.2.13/linux/arch/alpha/kernel/proto.h Mon Aug 9 16:05:54 1999 +++ linux/arch/alpha/kernel/proto.h Tue Jan 4 10:12:11 2000 @@ -1,4 +1,5 @@ /* Prototypes of functions used across modules here in this directory. */ +#include #define vucp volatile unsigned char * #define vusp volatile unsigned short * @@ -44,6 +45,22 @@ extern void cia_init_arch(unsigned long *, unsigned long *); extern void cia_machine_check(u64, u64, struct pt_regs *); +/* core_irongate.c */ +extern int irongate_hose_read_config_byte (u8, u8, u8, u8 *value, + struct linux_hose_info *hose); +extern int irongate_hose_read_config_word (u8, u8, u8, u16 *value, + struct linux_hose_info *hose); +extern int irongate_hose_read_config_dword (u8, u8, u8, u32 *value, + struct linux_hose_info *hose); +extern int irongate_hose_write_config_byte (u8, u8, u8, u8 value, + struct linux_hose_info *hose); +extern int irongate_hose_write_config_word (u8, u8, u8, u16 value, + struct linux_hose_info *hose); +extern int irongate_hose_write_config_dword (u8, u8, u8, u32 value, + struct linux_hose_info *hose); +extern void irongate_init_arch(unsigned long *, unsigned long *); +extern void irongate_machine_check(u64, u64, struct pt_regs *); + /* core_lca.c */ extern int lca_hose_read_config_byte (u8, u8, u8, u8 *value, struct linux_hose_info *hose); @@ -74,6 +91,7 @@ extern int mcpcia_hose_write_config_dword (u8, u8, u8, u32 value, struct linux_hose_info *hose); extern void mcpcia_init_arch(unsigned long *, unsigned long *); +extern void mcpcia_init_hoses(void); extern void mcpcia_machine_check(u64, u64, struct pt_regs *); /* core_polaris.c */ @@ -153,6 +171,7 @@ extern void handle_ipi(struct pt_regs *); extern void smp_percpu_timer_interrupt(struct pt_regs *); extern int smp_boot_cpuid; +extern unsigned long cpu_present_mask; /* bios32.c */ extern void reset_for_srm(void); diff -u --recursive --new-file v2.2.13/linux/arch/alpha/kernel/ptrace.c linux/arch/alpha/kernel/ptrace.c --- v2.2.13/linux/arch/alpha/kernel/ptrace.c Tue Dec 29 16:17:00 1998 +++ linux/arch/alpha/kernel/ptrace.c Tue Jan 4 10:12:11 2000 @@ -149,13 +149,18 @@ pmd_t * pgmiddle; pte_t * pgtable; unsigned long page; + int fault; DBG(DBG_MEM_ALL, ("getting long at 0x%lx\n", addr)); repeat: pgdir = pgd_offset(vma->vm_mm, addr); if (pgd_none(*pgdir)) { - handle_mm_fault(tsk, vma, addr, 0); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 0); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return 0; } if (pgd_bad(*pgdir)) { printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir)); @@ -164,8 +169,12 @@ } pgmiddle = pmd_offset(pgdir, addr); if (pmd_none(*pgmiddle)) { - handle_mm_fault(tsk, vma, addr, 0); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 0); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return 0; } if (pmd_bad(*pgmiddle)) { printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle)); @@ -174,8 +183,12 @@ } pgtable = pte_offset(pgmiddle, addr); if (!pte_present(*pgtable)) { - handle_mm_fault(tsk, vma, addr, 0); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 0); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return 0; } page = pte_page(*pgtable); /* this is a hack for non-kernel-mapped video buffers and similar */ @@ -202,12 +215,17 @@ pmd_t *pgmiddle; pte_t *pgtable; unsigned long page; + int fault; repeat: pgdir = pgd_offset(vma->vm_mm, addr); if (!pgd_present(*pgdir)) { - handle_mm_fault(tsk, vma, addr, 1); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 1); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return; } if (pgd_bad(*pgdir)) { printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir)); @@ -216,8 +234,12 @@ } pgmiddle = pmd_offset(pgdir, addr); if (pmd_none(*pgmiddle)) { - handle_mm_fault(tsk, vma, addr, 1); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 1); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return; } if (pmd_bad(*pgmiddle)) { printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle)); @@ -226,13 +248,21 @@ } pgtable = pte_offset(pgmiddle, addr); if (!pte_present(*pgtable)) { - handle_mm_fault(tsk, vma, addr, 1); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 1); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return; } page = pte_page(*pgtable); if (!pte_write(*pgtable)) { - handle_mm_fault(tsk, vma, addr, 1); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 1); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return; } /* This is a hack for non-kernel-mapped video buffers and similar. */ diff -u --recursive --new-file v2.2.13/linux/arch/alpha/kernel/setup.c linux/arch/alpha/kernel/setup.c --- v2.2.13/linux/arch/alpha/kernel/setup.c Tue Oct 19 17:10:36 1999 +++ linux/arch/alpha/kernel/setup.c Tue Jan 4 10:12:11 2000 @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -56,6 +57,7 @@ #define N(a) (sizeof(a)/sizeof(a[0])) static unsigned long find_end_memory(void); +static unsigned long get_memory_end_override(char *); static struct alpha_machine_vector *get_sysvec(long, long, long); static struct alpha_machine_vector *get_sysvec_byname(const char *); static void get_sysnames(long, long, char **, char **); @@ -112,12 +114,14 @@ WEAK(eb64p_mv); WEAK(eb66_mv); WEAK(eb66p_mv); +WEAK(eiger_mv); WEAK(jensen_mv); WEAK(lx164_mv); WEAK(miata_mv); WEAK(mikasa_mv); WEAK(mikasa_primo_mv); WEAK(monet_mv); +WEAK(nautilus_mv); WEAK(noname_mv); WEAK(noritake_mv); WEAK(noritake_primo_mv); @@ -136,7 +140,6 @@ #undef WEAK - void __init setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * memory_end_p) @@ -146,8 +149,9 @@ struct alpha_machine_vector *vec = NULL; struct percpu_struct *cpu; char *type_name, *var_name, *p; + unsigned long memory_end_override = 0; - hwrpb = (struct hwrpb_struct*)(IDENT_ADDR + INIT_HWRPB->phys_addr); + hwrpb = (struct hwrpb_struct*) __va(INIT_HWRPB->phys_addr); /* * Locate the command line. @@ -189,6 +193,11 @@ est_cycle_freq = simple_strtol(p+6, NULL, 0); continue; } + + if (strncmp(p, "mem=", 4) == 0) { + memory_end_override = get_memory_end_override(p+4); + continue; + } } /* Replace the command line, not that we've killed it with strtok. */ @@ -215,9 +224,10 @@ type_name, (*var_name ? " variation " : ""), var_name, hwrpb->sys_type, hwrpb->sys_variation); } - if (vec != &alpha_mv) + if (vec != &alpha_mv) { alpha_mv = *vec; - + } + #ifdef CONFIG_ALPHA_GENERIC /* Assume that we've booted from SRM if we havn't booted from MILO. Detect the later by looking for "MILO" in the system serial nr. */ @@ -248,8 +258,14 @@ wrmces(0x7); /* Find our memory. */ - *memory_end_p = find_end_memory(); *memory_start_p = (unsigned long) _end; + *memory_end_p = find_end_memory(); + if (memory_end_override && memory_end_override < *memory_end_p) { + printk("Overriding memory size from %luMB to %luMB\n", + __pa(*memory_end_p) >> 20, + __pa(memory_end_override) >> 20); + *memory_end_p = memory_end_override; + } #ifdef CONFIG_BLK_DEV_INITRD initrd_start = INITRD_START; @@ -339,13 +355,35 @@ /* Round it up to an even number of pages. */ high = (high + PAGE_SIZE) & (PAGE_MASK*2); - /* Enforce maximum of 2GB even if there is more. Blah. */ + /* Enforce maximum of 2GB even if there is more, + * but only if the platform (support) cannot handle it. + */ if (high > 0x80000000UL) { - printk("Cropping memory from %luMB to 2048MB\n", high); + printk("Cropping memory from %luMB to 2048MB\n", high >> 20); high = 0x80000000UL; } - return PAGE_OFFSET + high; + return (unsigned long) __va(high); +} + +static unsigned long __init +get_memory_end_override(char *s) +{ + unsigned long end = 0; + char *from = s; + + end = simple_strtoul(from, &from, 0); + if ( *from == 'K' || *from == 'k' ) { + end = end << 10; + from++; + } else if ( *from == 'M' || *from == 'm' ) { + end = end << 20; + from++; + } else if ( *from == 'G' || *from == 'g' ) { + end = end << 30; + from++; + } + return (unsigned long) __va(end); } @@ -358,11 +396,13 @@ "Mikasa", "EB64", "EB66", "EB64+", "AlphaBook1", "Rawhide", "K2", "Lynx", "XL", "EB164", "Noritake", "Cortex", "29", "Miata", "XXM", "Takara", "Yukon", - "Tsunami", "Wildfire", "CUSCO" + "Tsunami", "Wildfire", "CUSCO", "Eiger" }; static char unofficial_names[][8] = {"100", "Ruffian"}; +static char api_names[][16] = {"200", "Nautilus"}; + static char eb164_names[][8] = {"EB164", "PC164", "LX164", "SX164", "RX164"}; static int eb164_indices[] = {0,0,0,1,1,1,1,1,2,2,2,2,3,3,3,3,4}; @@ -386,7 +426,6 @@ }; static int tsunami_indices[] = {0,1,2,3,4,5,6,7,8}; - static struct alpha_machine_vector * __init get_sysvec(long type, long variation, long cpu) { @@ -429,6 +468,7 @@ NULL, /* Tsunami -- see variation. */ NULL, /* Wildfire */ NULL, /* CUSCO */ + &eiger_mv, /* Eiger */ }; static struct alpha_machine_vector *unofficial_vecs[] __initlocaldata = @@ -437,6 +477,12 @@ &ruffian_mv, }; + static struct alpha_machine_vector *api_vecs[] __initlocaldata = + { + NULL, /* 200 */ + &nautilus_mv, + }; + static struct alpha_machine_vector *alcor_vecs[] __initlocaldata = { &alcor_mv, &xlt_mv, &xlt_mv @@ -485,6 +531,9 @@ vec = NULL; if (type < N(systype_vecs)) { vec = systype_vecs[type]; + } else if ((type > ST_API_BIAS) && + (type - ST_API_BIAS) < N(api_vecs)) { + vec = api_vecs[type - ST_API_BIAS]; } else if ((type > ST_UNOFFICIAL_BIAS) && (type - ST_UNOFFICIAL_BIAS) < N(unofficial_vecs)) { vec = unofficial_vecs[type - ST_UNOFFICIAL_BIAS]; @@ -558,12 +607,14 @@ &eb64p_mv, &eb66_mv, &eb66p_mv, + &eiger_mv, &jensen_mv, &lx164_mv, &miata_mv, &mikasa_mv, &mikasa_primo_mv, &monet_mv, + &nautilus_mv, &noname_mv, &noritake_mv, &noritake_primo_mv, @@ -604,6 +655,9 @@ else set type name to family */ if (type < N(systype_names)) { *type_name = systype_names[type]; + } else if ((type > ST_API_BIAS) && + (type - ST_API_BIAS) < N(api_names)) { + *type_name = api_names[type - ST_API_BIAS]; } else if ((type > ST_UNOFFICIAL_BIAS) && (type - ST_UNOFFICIAL_BIAS) < N(unofficial_names)) { *type_name = unofficial_names[type - ST_UNOFFICIAL_BIAS]; @@ -689,6 +743,21 @@ } } +static int +get_nr_processors(struct percpu_struct *cpubase, unsigned long num) +{ + struct percpu_struct *cpu; + int i, count = 0; + + for (i = 0; i < num; i++) { + cpu = (struct percpu_struct *) + ((char *)cpubase + i*hwrpb->processor_size); + if ((cpu->flags & 0x1cc) == 0x1cc) + count++; + } + return count; +} + /* * BUFFER is PAGE_SIZE bytes long. */ @@ -708,7 +777,7 @@ char *cpu_name; char *systype_name; char *sysvariation_name; - int len; + int len, nr_processors; cpu = (struct percpu_struct*)((char*)hwrpb + hwrpb->processor_offset); cpu_index = (unsigned) (cpu->type - 1); @@ -719,6 +788,8 @@ get_sysnames(hwrpb->sys_type, hwrpb->sys_variation, &systype_name, &sysvariation_name); + nr_processors = get_nr_processors(cpu, hwrpb->nr_processors); + len = sprintf(buffer, "cpu\t\t\t: Alpha\n" "cpu model\t\t: %s\n" @@ -738,7 +809,7 @@ "kernel unaligned acc\t: %ld (pc=%lx,va=%lx)\n" "user unaligned acc\t: %ld (pc=%lx,va=%lx)\n" "platform string\t\t: %s\n" - "cpus detected\t\t: %ld\n", + "cpus detected\t\t: %d\n", cpu_name, cpu->variation, cpu->revision, (char*)cpu->serial_no, systype_name, sysvariation_name, hwrpb->sys_revision, @@ -753,7 +824,7 @@ loops_per_sec / 500000, (loops_per_sec / 5000) % 100, unaligned[0].count, unaligned[0].pc, unaligned[0].va, unaligned[1].count, unaligned[1].pc, unaligned[1].va, - platform_string(), hwrpb->nr_processors); + platform_string(), nr_processors); #ifdef __SMP__ len += smp_info(buffer+len); diff -u --recursive --new-file v2.2.13/linux/arch/alpha/kernel/signal.c linux/arch/alpha/kernel/signal.c --- v2.2.13/linux/arch/alpha/kernel/signal.c Mon Aug 9 16:05:54 1999 +++ linux/arch/alpha/kernel/signal.c Tue Jan 4 10:12:11 2000 @@ -437,6 +437,8 @@ err |= __copy_to_user(frame->extramask, &set->sig[1], sizeof(frame->extramask)); } + if (err) + goto give_sigsegv; /* Set up to return from userspace. If provided, use a stub already in userspace. */ @@ -499,6 +501,8 @@ err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, sw, set->sig[0], oldsp); err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + if (err) + goto give_sigsegv; /* Set up to return from userspace. If provided, use a stub already in userspace. */ diff -u --recursive --new-file v2.2.13/linux/arch/alpha/kernel/smp.c linux/arch/alpha/kernel/smp.c --- v2.2.13/linux/arch/alpha/kernel/smp.c Mon Aug 9 16:05:54 1999 +++ linux/arch/alpha/kernel/smp.c Tue Jan 4 10:12:11 2000 @@ -97,6 +97,9 @@ cpu_data[cpuid].loops_per_sec = loops_per_sec; cpu_data[cpuid].last_asn = (cpuid << WIDTH_HARDWARE_ASN) + ASN_FIRST_VERSION; + + cpu_data[cpuid].irq_count = 0; + cpu_data[cpuid].bh_count = 0; } /* @@ -580,7 +583,7 @@ smp_percpu_timer_interrupt(struct pt_regs *regs) { int cpu = smp_processor_id(); - int user = user_mode(regs); + unsigned long user = user_mode(regs); struct cpuinfo_alpha *data = &cpu_data[cpu]; /* Record kernel PC */ @@ -902,7 +905,7 @@ return; } else flush_tlb_other(mm); - + data.vma = vma; data.mm = mm; data.addr = addr; diff -u --recursive --new-file v2.2.13/linux/arch/alpha/kernel/sys_dp264.c linux/arch/alpha/kernel/sys_dp264.c --- v2.2.13/linux/arch/alpha/kernel/sys_dp264.c Mon Aug 9 16:05:54 1999 +++ linux/arch/alpha/kernel/sys_dp264.c Tue Jan 4 10:12:11 2000 @@ -203,7 +203,6 @@ enable_irq(2); } - /* * PCI Fixup configuration. * @@ -402,7 +401,6 @@ { layout_all_busses(DEFAULT_IO_BASE, DEFAULT_MEM_BASE); common_pci_fixup(webbrick_map_irq, common_swizzle); - SMC669_Init(0); } static void __init @@ -412,7 +410,6 @@ common_pci_fixup(clipper_map_irq, common_swizzle); } - /* * The System Vectors */ @@ -427,7 +424,7 @@ max_dma_address: ALPHA_MAX_DMA_ADDRESS, nr_irqs: 64, - irq_probe_mask: _PROBE_MASK(64), + irq_probe_mask: TSUNAMI_PROBE_MASK, update_irq_hw: dp264_update_irq_hw, ack_irq: generic_ack_irq, device_interrupt: dp264_device_interrupt, @@ -450,7 +447,7 @@ max_dma_address: ALPHA_MAX_DMA_ADDRESS, nr_irqs: 64, - irq_probe_mask: _PROBE_MASK(64), + irq_probe_mask: TSUNAMI_PROBE_MASK, update_irq_hw: dp264_update_irq_hw, ack_irq: generic_ack_irq, device_interrupt: dp264_device_interrupt, @@ -472,7 +469,7 @@ max_dma_address: ALPHA_MAX_DMA_ADDRESS, nr_irqs: 64, - irq_probe_mask: _PROBE_MASK(64), + irq_probe_mask: TSUNAMI_PROBE_MASK, update_irq_hw: dp264_update_irq_hw, ack_irq: generic_ack_irq, device_interrupt: dp264_device_interrupt, @@ -494,7 +491,7 @@ max_dma_address: ALPHA_MAX_DMA_ADDRESS, nr_irqs: 64, - irq_probe_mask: _PROBE_MASK(64), + irq_probe_mask: TSUNAMI_PROBE_MASK, update_irq_hw: clipper_update_irq_hw, ack_irq: generic_ack_irq, device_interrupt: dp264_device_interrupt, @@ -505,6 +502,6 @@ pci_fixup: clipper_pci_fixup, kill_arch: generic_kill_arch, }; - /* No alpha_mv alias for webbrick/monet/clipper, since we compile them in unconditionally with DP264; setup_arch knows how to cope. */ + diff -u --recursive --new-file v2.2.13/linux/arch/alpha/kernel/sys_eiger.c linux/arch/alpha/kernel/sys_eiger.c --- v2.2.13/linux/arch/alpha/kernel/sys_eiger.c Wed Dec 31 16:00:00 1969 +++ linux/arch/alpha/kernel/sys_eiger.c Tue Jan 4 10:12:11 2000 @@ -0,0 +1,237 @@ +/* + * linux/arch/alpha/kernel/sys_eiger.c + * + * Copyright (C) 1995 David A Rusling + * Copyright (C) 1996, 1999 Jay A Estabrook + * Copyright (C) 1998, 1999 Richard Henderson + * Copyright (C) 1999 Iain Grant + * + * Code supporting the EIGER (EV6+TSUNAMI). + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "proto.h" +#include "irq.h" +#include "bios32.h" +#include "machvec.h" + +#define dev2hose(d) (bus2hose[(d)->bus->number]->pci_hose_index) + +/* + * HACK ALERT! only the boot cpu is used for interrupts. + */ + +static void +eiger_update_irq_hw(unsigned long irq, unsigned long unused, int unmask_p) +{ + + unsigned int regaddr; + unsigned long mask; + + if (irq <= 15) { + if (irq <= 7) + outb(alpha_irq_mask, 0x21); /* ISA PIC1 */ + else + outb(alpha_irq_mask >> 8, 0xA1); /* ISA PIC2 */ + } else { + if (irq > 63) + mask = _alpha_irq_masks[1] << 16; + else + mask = _alpha_irq_masks[0] >> ((irq - 16) & 0x30); + + regaddr = 0x510 + (((irq - 16) >> 2) & 0x0c); + + outl(mask & 0xffff0000UL, regaddr); + } + +} + +static void +eiger_device_interrupt(unsigned long vector, struct pt_regs * regs) +{ + unsigned intstatus; + + /* + * The PALcode will have passed us vectors 0x800 or 0x810, + * which are fairly arbitrary values and serve only to tell + * us whether an interrupt has come in on IRQ0 or IRQ1. If + * it's IRQ1 it's a PCI interrupt; if it's IRQ0, it's + * probably ISA, but PCI interrupts can come through IRQ0 + * as well if the interrupt controller isn't in accelerated + * mode. + * + * OTOH, the accelerator thing doesn't seem to be working + * overly well, so what we'll do instead is try directly + * examining the Master Interrupt Register to see if it's a + * PCI interrupt, and if _not_ then we'll pass it on to the + * ISA handler. + */ + + intstatus = inw(0x500) & 15; + if (intstatus) { + /* + * This is a PCI interrupt. Check each bit and + * despatch an interrupt if it's set. + */ + + if (intstatus & 8) handle_irq(16+3, 16+3, regs); + if (intstatus & 4) handle_irq(16+2, 16+2, regs); + if (intstatus & 2) handle_irq(16+1, 16+1, regs); + if (intstatus & 1) handle_irq(16+0, 16+0, regs); + } else + isa_device_interrupt (vector, regs); +} + + +static void +eiger_srm_device_interrupt(unsigned long vector, struct pt_regs * regs) +{ + int irq = (vector - 0x800) >> 4; + + handle_irq(irq, irq, regs); + +} + +static void __init +eiger_init_irq(void) +{ + outb(0, DMA1_RESET_REG); + outb(0, DMA2_RESET_REG); + outb(DMA_MODE_CASCADE, DMA2_MODE_REG); + outb(0, DMA2_MASK_REG); + + if (alpha_using_srm) + alpha_mv.device_interrupt = eiger_srm_device_interrupt; + + eiger_update_irq_hw(16, alpha_irq_mask, 0); + + enable_irq(2); +} + +static int __init +eiger_map_irq(struct pci_dev *dev, int slot, int pin) +{ + u8 irq_orig; + int irq; + + /* The SRM console has already calculated out the IRQ value's for + option cards. As this works lets just read in the value already + set and change it to a useable value by Linux. + + All the IRQ values generated by the console are greater than 90, + so we calculate a value for Linux by the following: + + irq = (card Irq - 80); + + We subtract 80 because it is (90 - allocated ISA IRQ's ). + */ + pcibios_read_config_byte(dev->bus->number, + dev->devfn, + PCI_INTERRUPT_LINE, + &irq_orig); + + irq = ( irq_orig - 0x80); + + return irq; +} + +/* New swizzle routine with code for card based bridges */ +static int __init +eiger_swizzle(struct pci_dev *dev, int *pinp) +{ + int slot, pin = *pinp; + int bridge_count = 0; + + /* Find the number of backplane bridges */ + int backplane = ( inw(0x502) & 0x0f); + + switch (backplane) + { + case 0x00: bridge_count = 0; break; /* No bridges */ + case 0x01: bridge_count = 1; break; /* 1 */ + case 0x03: bridge_count = 2; break; /* 2 */ + case 0x07: bridge_count = 3; break; /* 3 */ + case 0x0f: bridge_count = 4; break; /* 4 */ + }; + + + /* Check first for the built-in bridges on hose 0. */ + if ( dev2hose(dev) == 0 && + ( (PCI_SLOT(dev->bus->self->devfn) > (20-bridge_count)))) + { + slot = PCI_SLOT(dev->devfn); + } else { + /* Must be a card-based bridge. */ + do { + /* Check for built-in bridges on hose 0. */ + if ( dev2hose(dev) == 0 && + (PCI_SLOT(dev->bus->self->devfn) > + (20 - bridge_count))) + { + slot = PCI_SLOT(dev->devfn); + break; + } + pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)) ; + + /* Move up the chain of bridges. */ + dev = dev->bus->self; + /* Slot of the next bridge. */ + slot = PCI_SLOT(dev->devfn); + } while (dev->bus->self); + } + *pinp = pin; + + return slot; +} + +static void __init +eiger_pci_fixup(void) +{ + layout_all_busses(DEFAULT_IO_BASE, DEFAULT_MEM_BASE); + common_pci_fixup(eiger_map_irq, eiger_swizzle); +} + +/* + * The System Vectors + */ + +struct alpha_machine_vector eiger_mv __initmv = { + vector_name: "Eiger", + DO_EV6_MMU, + DO_DEFAULT_RTC, + DO_TSUNAMI_IO, + DO_TSUNAMI_BUS, + machine_check: tsunami_machine_check, + max_dma_address: ALPHA_MAX_DMA_ADDRESS, + + nr_irqs: 128, + irq_probe_mask: TSUNAMI_PROBE_MASK, + update_irq_hw: eiger_update_irq_hw, + ack_irq: generic_ack_irq, + device_interrupt: eiger_device_interrupt, + + init_arch: tsunami_init_arch, + init_irq: eiger_init_irq, + init_pit: generic_init_pit, + pci_fixup: eiger_pci_fixup, + kill_arch: generic_kill_arch, +}; +ALIAS_MV(eiger) diff -u --recursive --new-file v2.2.13/linux/arch/alpha/kernel/sys_nautilus.c linux/arch/alpha/kernel/sys_nautilus.c --- v2.2.13/linux/arch/alpha/kernel/sys_nautilus.c Wed Dec 31 16:00:00 1969 +++ linux/arch/alpha/kernel/sys_nautilus.c Tue Jan 4 10:12:11 2000 @@ -0,0 +1,544 @@ +/* + * linux/arch/alpha/kernel/sys_nautilus.c + * + * Copyright (C) 1995 David A Rusling + * Copyright (C) 1998 Richard Henderson + * Copyright (C) 1999 Alpha Processor, Inc., (David Daniel, Stig Telfer, Soohoon Lee) + * + * Code supporting NAUTILUS systems. + * + * + * NAUTILUS has the following I/O features: + * + * a) Driven by AMD 751 aka IRONGATE (northbridge): + * 4 PCI slots + * 1 AGP slot + * + * b) Driven by ALI M1543C (southbridge) + * 2 ISA slots + * 2 IDE connectors + * 1 dual drive capable FDD controller + * 2 serial ports + * 1 ECP/EPP/SP parallel port + * 2 USB ports + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "proto.h" +#include "irq.h" +#include "bios32.h" +#include "machvec.h" + +#define dev2hose(d) (bus2hose[(d)->bus->number]->pci_hose_index) + +static void +nautilus_update_irq_hw(unsigned long irq, unsigned long mask, int unmask_p) +{ + mask |= 0x100; /*Timer is connected to PIC int. line also on Nautilus*/ + /*And the timer int. handler enables PIC line */ + /*So not to get multiple timer int. sources*/ + /*Mask it all the times*/ + if (irq >= 8) + outb(mask >> 8, 0xA1); + else + outb(mask, 0x21); +} + +static void __init +nautilus_init_irq(void) +{ + STANDARD_INIT_IRQ_PROLOG; + + enable_irq(2); /* enable cascade */ + disable_irq(8); +} + + +static void __init +nautilus_pci_fixup(void) +{ +/* All BIOS setups are preserved */ +} + +void +nautilus_kill_arch (int mode, char *restart_cmd) +{ + +#ifdef CONFIG_RTC + /* Reset rtc to defaults. */ + { + unsigned char control; + + cli(); + + /* Reset periodic interrupt frequency. */ + CMOS_WRITE(0x26, RTC_FREQ_SELECT); + + /* Turn on periodic interrupts. */ + control = CMOS_READ(RTC_CONTROL); + control |= RTC_PIE; + CMOS_WRITE(control, RTC_CONTROL); + CMOS_READ(RTC_INTR_FLAGS); + + sti(); + } +#endif + + switch(mode) { + case LINUX_REBOOT_CMD_HALT: + printk("Press Reset bottun"); + break; + case LINUX_REBOOT_CMD_RESTART: + { + int v; + irongate_hose_write_config_byte(0, 0x07<<3, 0x43, &v, 0); + irongate_hose_write_config_byte(0, 0x07<<3, 0x43, v | 0x80, 0); + outb(1, 0x92); + outb(0, 0x92); + printk("Press Reset button"); + } + break; + case LINUX_REBOOT_CMD_POWER_OFF: + { + int pmuport; + irongate_hose_read_config_dword(0, 0x11<<3, 0x10, &pmuport, 0); + pmuport &= 0xfffe; + outl(0xffff, pmuport); /* clear pending events */ + outw(0x2000, pmuport+4); /* power off */ + printk("Press power button"); + } + } + + while (1) + ; + halt(); +} + +/*----------------------------------------------------------------------*/ +/* Machine check handler code + * + * Perform analysis of a machine check that was triggered by the EV6 CPU's + * fault-detection mechanism. */ + +/* IPR structures for EV6, containing the necessary data for the + * machine check handler to unpick the logout frame + */ + + +/* I_STAT */ + +#define EV6__I_STAT__PAR ( 1 << 29 ) + + +/* MM_STAT */ + +#define EV6__MM_STAT__DC_TAG_PERR ( 1 << 10 ) + + +/* DC_STAT */ + +#define EV6__DC_STAT__SEO ( 1 << 4 ) +#define EV6__DC_STAT__ECC_ERR_LD ( 1 << 3 ) +#define EV6__DC_STAT__ECC_ERR_ST ( 1 << 2 ) +#define EV6__DC_STAT__TPERR_P1 ( 1 << 1 ) +#define EV6__DC_STAT__TPERR_P0 ( 1 ) + + +/* C_STAT */ + +#define EV6__C_STAT__BC_PERR ( 0x01 ) +#define EV6__C_STAT__DC_PERR ( 0x02 ) +#define EV6__C_STAT__DSTREAM_MEM_ERR ( 0x03 ) +#define EV6__C_STAT__DSTREAM_BC_ERR ( 0x04 ) +#define EV6__C_STAT__DSTREAM_DC_ERR ( 0x05 ) +#define EV6__C_STAT__PROBE_BC_ERR0 ( 0x06 ) +#define EV6__C_STAT__PROBE_BC_ERR1 ( 0x07 ) +#define EV6__C_STAT__ISTREAM_MEM_ERR ( 0x0B ) +#define EV6__C_STAT__ISTREAM_BC_ERR ( 0x0C ) +#define EV6__C_STAT__DSTREAM_MEM_DBL ( 0x13 ) +#define EV6__C_STAT__DSTREAM_BC_DBL ( 0x14 ) +#define EV6__C_STAT__ISTREAM_MEM_DBL ( 0x1B ) +#define EV6__C_STAT__ISTREAM_BC_DBL ( 0x1C ) + + +/* take the two syndromes from the CBOX error chain and convert them + * into a bit number */ + +/* NOTE - since I don't know of any difference between C0 and C1 + * I just ignore C1, since in all cases I've seen so far they are identical */ + +static const unsigned char ev6_bit_to_syndrome[72] = +{ + 0xce, 0xcb, 0xd3, 0xd5, 0xd6, 0xd9, 0xda, 0xdc, /* 0 */ + 0x23, 0x25, 0x26, 0x29, 0x2a, 0x2c, 0x31, 0x34, /* 8 */ + 0x0e, 0x0b, 0x13, 0x15, 0x16, 0x19, 0x1a, 0x1c, /* 16 */ + 0xe3, 0xe5, 0xe6, 0xe9, 0xea, 0xec, 0xf1, 0xf4, /* 24 */ + 0x4f, 0x4a, 0x52, 0x54, 0x57, 0x58, 0x5b, 0x5d, /* 32 */ + 0xa2, 0xa4, 0xa7, 0xa8, 0xab, 0xad, 0xb0, 0xb5, /* 40 */ + 0x8f, 0x8a, 0x92, 0x94, 0x97, 0x98, 0x9b, 0x9d, /* 48 */ + 0x62, 0x64, 0x67, 0x68, 0x6b, 0x6d, 0x70, 0x75, /* 56 */ + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 /* 64 */ +}; + + +static int ev6_syn2bit(unsigned long c0, unsigned long c1) +{ + int bit; + + for (bit = 0; bit < 72; bit++) + if (ev6_bit_to_syndrome[bit] == c0) return bit; + for (bit = 0; bit < 72; bit++) + if (ev6_bit_to_syndrome[bit] == c1) return bit + 64; + + return -1; /* not found */ +} + + +/* single bit ECC errors are categorised here */ + +#if 0 +static const char *interr = "CPU internal error"; +static const char *slotb= "Slot-B error"; +static const char *membus= "Memory/EV6-bus error"; +#else +static const char *interr = ""; +static const char *slotb = ""; +static const char *membus = ""; +#endif + +static void ev6_crd_interp(char *interp, struct el_common_EV6_mcheck * L) +{ + + /* Icache data or tag parity error */ + if (L->I_STAT & EV6__I_STAT__PAR) { + sprintf(interp, "%s: I_STAT[PAR]\n Icache data or tag parity error", interr); + return; + } + + /* Dcache tag parity error (on issue) (DFAULT) */ + if (L->MM_STAT & EV6__MM_STAT__DC_TAG_PERR) { + sprintf(interp, "%s: MM_STAT[DC_TAG_PERR]\n Dcache tag parity error(on issue)", interr); + return; + } + + /* Errors relating to D-stream set non-zero DC_STAT - mask CRD bits */ + switch (L->DC_STAT & (EV6__DC_STAT__ECC_ERR_ST | EV6__DC_STAT__ECC_ERR_LD)) + { + + case EV6__DC_STAT__ECC_ERR_ST: + /* Dcache single-bit ECC error on small store */ + sprintf(interp, "%s: DC_STAT[ECC_ERR_ST]\n Dcache single-bit ECC error on small store", interr); + return; + + case EV6__DC_STAT__ECC_ERR_LD: + switch (L->C_STAT) { + case 0: + /* Dcache single-bit error on speculative load */ + /* Bcache victim read on Dcache/Bcache miss */ + sprintf(interp, "%s: DC_STAT[ECC_ERR_LD] C_STAT=0\n Dcache single-bit ECC error on speculative load", slotb); + return; + + case EV6__C_STAT__DSTREAM_DC_ERR: + /* Dcache single bit error on load */ + sprintf(interp, "%s: DC_STAT[ECC_ERR_LD] C_STAT[DSTREAM_DC_ERR]\n Dcache single-bit ECC error on speculative load, bit %d", interr, ev6_syn2bit(L->DC0_SYNDROME, L->DC1_SYNDROME)); + return; + + + case EV6__C_STAT__DSTREAM_BC_ERR: + /* Bcache single-bit error on Dcache fill */ + sprintf(interp, "%s: DC_STAT[ECC_ERR_LD] C_STAT[DSTREAM_BC_ERR]\n Bcache single-bit error on Dcache fill, bit %d", slotb, ev6_syn2bit(L->DC0_SYNDROME, L->DC1_SYNDROME)); + return; + + case EV6__C_STAT__DSTREAM_MEM_ERR: + /* Memory single-bit error on Dcache fill */ + sprintf(interp, "%s (to Dcache): DC_STAT[ECC_ERR_LD] C_STAT[DSTREAM_MEM_ERR]\n Memory single-bit error on Dcache fill, Address 0x%lX, bit %d", membus, L->C_ADDR, ev6_syn2bit(L->DC0_SYNDROME, L->DC1_SYNDROME)); + return; + + default: /* not one of these */ + } + default: /* not one of these */ + } + + /* I-stream, other misc errors go on C_STAT alone */ + switch (L->C_STAT) { + + case EV6__C_STAT__ISTREAM_BC_ERR: + /* Bcache single-bit error on Icache fill (also MCHK) */ + sprintf(interp, "%s: C_STAT[ISTREAM_BC_ERR]\n Bcache single-bit error on Icache fill, bit %d", slotb, + ev6_syn2bit(L->DC0_SYNDROME, L->DC1_SYNDROME)); + return; + + case EV6__C_STAT__ISTREAM_MEM_ERR: + /* Memory single-bit error on Icache fill (also MCHK) */ + sprintf(interp, "%s : C_STATISTREAM_MEM_ERR]\n Memory single-bit error on Icache fill addr 0x%lX, bit %d", membus, L->C_ADDR, ev6_syn2bit(L->DC0_SYNDROME, L->DC1_SYNDROME)); + return; + + case EV6__C_STAT__PROBE_BC_ERR0: + case EV6__C_STAT__PROBE_BC_ERR1: + /* Bcache single-bit error on a probe hit */ + sprintf(interp, "%s: C_STAT[PROBE_BC_ERR]\n Bcache single-bit error on a probe hit, addr 0x%lX, bit %d", slotb, L->C_ADDR, ev6_syn2bit(L->DC0_SYNDROME, L->DC1_SYNDROME)); + return; + + default: /* not one of these */ + } +} + + +/* these are the really nasty ones... */ + +static void ev6_mchk_interp(char *interp, struct el_common_EV6_mcheck * L) +{ + /* Machine check errors described by DC_STAT */ + switch (L->DC_STAT) { + case EV6__DC_STAT__TPERR_P0: + case EV6__DC_STAT__TPERR_P1: + /* Dcache tag parity error (on retry) */ + sprintf(interp, "%s: DC_STAT[TPERR_P0|TPERR_P1]\n Dcache tag parity error(on retry)", interr); + return; + + case EV6__DC_STAT__SEO: + /* Dcache second error on store */ + sprintf(interp, "%s: DC_STAT[SEO]\n Dcache second error during mcheck", interr); + return; + + default: /* some other kind of error */ + } + + /* Machine check errors described by C_STAT */ + switch (L->C_STAT) { + case EV6__C_STAT__DC_PERR: /* Dcache duplicate tag parity error */ + sprintf(interp, "%s: C_STAT[DC_PERR]\n Dcache duplicate tag parity error at 0x%lX", + interr, L->C_ADDR); + return; + + case EV6__C_STAT__BC_PERR: /* Bcache tag parity error */ + sprintf(interp, "%s: C_STAT[BC_PERR]\n Bcache tag parity error at 0x%lX", slotb, L->C_ADDR); + return; + + case EV6__C_STAT__ISTREAM_BC_ERR: + /* Bcache single-bit error on Icache fill (also CRD) */ + sprintf(interp, "%s: C_STAT[ISTREAM_BC_ERR]\n Bcache single-bit error on Icache fill 0x%lX bit %d", + slotb, L->C_ADDR, + ev6_syn2bit(L->DC0_SYNDROME, L->DC1_SYNDROME)); + return; + + + case EV6__C_STAT__ISTREAM_MEM_ERR: + /* Memory single-bit error on Icache fill (also CRD) */ + sprintf(interp, "%s: C_STAT[ISTREAM_MEM_ERR]\n Memory single-bit error on Icache fill 0x%lX, bit %d", + membus, L->C_ADDR, + ev6_syn2bit(L->DC0_SYNDROME, L->DC1_SYNDROME)); + return; + + + case EV6__C_STAT__ISTREAM_BC_DBL: + /* Bcache double-bit error on Icache fill */ + sprintf(interp, "%s: C_STAT[ISTREAM_BC_DBL]\n Bcache double-bit error on Icache fill at 0x%lX", + slotb, L->C_ADDR); + return; + case EV6__C_STAT__DSTREAM_BC_DBL: + /* Bcache double-bit error on Dcache fill */ + sprintf(interp, "%s: C_STAT[DSTREAM_BC_DBL]\n Bcache double-bit error on Dcache fill at 0x%lX", + slotb, L->C_ADDR); + return; + + case EV6__C_STAT__ISTREAM_MEM_DBL: + /* Memory double-bit error on Icache fill */ + sprintf(interp, "%s: C_STAT[ISTREAM_MEM_DBL]\n Memory double-bit error on Icache fill at 0x%lX", + membus, L->C_ADDR); + return; + case EV6__C_STAT__DSTREAM_MEM_DBL: + /* Memory double-bit error on Dcache fill */ + sprintf(interp, "%s: C_STAT[DSTREAM_MEM_DBL]\n Memory double-bit error on Dcache fill at 0x%lX", + membus, L->C_ADDR); + return; + + default: /* something else */ + } +} + + +static void ev6_cpu_machine_check( unsigned long vector, + struct el_common_EV6_mcheck *L, struct pt_regs *regs) +{ + char interp[80]; /* buffer for interpretation details */ + + /* this is verbose and looks intimidating. Should it be printed for + * corrected (CRD) machine checks? */ + + printk(KERN_CRIT "PALcode logout frame: " + "MCHK_Code %d " + "MCHK_Frame_Rev %d\n" + "I_STAT %016lx " + "DC_STAT %016lx " + "C_ADDR %016lx\n" + "SYND1 %016lx " + "SYND0 %016lx " + "C_STAT %016lx\n" + "C_STS %016lx " + "RES %016lx " + "EXC_ADDR%016lx\n" + "IER_CM %016lx " + "ISUM %016lx " + "MM_STAT %016lx\n" + "PALBASE %016lx " + "I_CTL %016lx " + "PCTX %016lx\n" + "CPU registers: " + "PC %016lx " + "Return %016lx\n", + L->MCHK_Code, L->MCHK_Frame_Rev, L->I_STAT, L->DC_STAT, + L->C_ADDR, L->DC1_SYNDROME, L->DC0_SYNDROME, L->C_STAT, + L->C_STS, L->RESERVED0, L->EXC_ADDR, L->IER_CM, L->ISUM, + L->MM_STAT, L->PAL_BASE, L->I_CTL, L->PCTX, + regs->pc, regs->r26 ); + + /* Attempt an interpretation on the meanings of the fields above */ + sprintf(interp, "No interpretation available!" ); + if ( vector == SCB_Q_PROCERR ) ev6_crd_interp( interp, L ); + else if ( vector == SCB_Q_PROCMCHK ) ev6_mchk_interp( interp, L ); + + printk( KERN_CRIT "interpretation: %s\n\n", interp ); +} + + +/* Perform analysis of a machine check that arrived from the system (NMI) */ + +static void naut_sys_machine_check( unsigned long vector, unsigned long la_ptr, + struct pt_regs *regs ) +{ + printk("xtime %lx\n", CURRENT_TIME); + printk("PC %lx RA %lx\n", regs->pc, regs->r26); + irongate_pci_clr_err(); +} + + + +/* Machine checks can come from two sources - those on the CPU and those + * in the system. They are analysed separately but all starts here */ + +void nautilus_machine_check(unsigned long vector, unsigned long la_ptr, + struct pt_regs *regs) +{ + char *mchk_class; + unsigned cpu_analysis=0, sys_analysis=0; /* interpretation flags */ + + /* now for some analysis. Machine checks fall into two classes - + * those picked up by the system, and those picked up by the CPU. + * add to that the two levels of severity - correctable or not + */ + + if (vector == SCB_Q_SYSMCHK && ((IRONGATE0->dramms & 0x300) == 0x300)) { + unsigned long nmi_ctl, temp; + + /* Clear ALI NMI */ + nmi_ctl = inb(0x61); + nmi_ctl |= 0x0c; + outb(nmi_ctl, 0x61); + nmi_ctl &= ~0x0c; + outb(nmi_ctl, 0x61); + + temp = IRONGATE0->stat_cmd; + temp &= ~0x100; + IRONGATE0->stat_cmd = temp; /* write again clears error bits */ + mb(); + temp = IRONGATE0->stat_cmd; /* re-read to force write */ + + temp = IRONGATE0->dramms; + IRONGATE0->dramms = temp; /* write again clears error bits */ + mb(); + temp = IRONGATE0->dramms; /* re-read to force write */ + + draina(); + wrmces(0x7); + mb(); + return; + } + + switch (vector) { + case SCB_Q_SYSERR: + mchk_class = "Correctable System Machine Check (NMI)"; + sys_analysis = 1; + break; + case SCB_Q_SYSMCHK: + mchk_class = "Fatal System Machine Check (NMI)"; + sys_analysis = 1; + break; + + case SCB_Q_PROCERR: + mchk_class = "Correctable Processor Machine Check"; + cpu_analysis = 1; + break; + case SCB_Q_PROCMCHK: + mchk_class = "Fatal Processor Machine Check"; + cpu_analysis = 1; + break; + + default: + mchk_class = "Unknown vector!"; + break; + } + + printk(KERN_CRIT "> NAUTILUS Machine check 0x%x [%s]\n", vector, mchk_class); + + if ( cpu_analysis ) + ev6_cpu_machine_check( vector, + (struct el_common_EV6_mcheck *)la_ptr, + regs ); + if ( sys_analysis ) + naut_sys_machine_check( vector, la_ptr, regs ); + + /* Tell the PALcode to clear the machine check */ + draina(); + wrmces(0x7); + mb(); +} + + + +/* + * The System Vectors + */ + +struct alpha_machine_vector nautilus_mv __initmv = { + vector_name: "NAUTILUS", + DO_EV6_MMU, + DO_DEFAULT_RTC, + DO_IRONGATE_IO, + DO_IRONGATE_BUS, + machine_check: nautilus_machine_check, + max_dma_address: ALPHA_NAUTILUS_MAX_DMA_ADDRESS, + + nr_irqs: 16, + irq_probe_mask: (_PROBE_MASK(16) & ~0x101UL), + + update_irq_hw: nautilus_update_irq_hw, + ack_irq: generic_ack_irq, + device_interrupt: isa_device_interrupt, + + init_arch: irongate_init_arch, + init_irq: nautilus_init_irq, + init_pit: generic_init_pit, + pci_fixup: nautilus_pci_fixup, + kill_arch: nautilus_kill_arch, +}; +ALIAS_MV(nautilus) diff -u --recursive --new-file v2.2.13/linux/arch/alpha/kernel/sys_rawhide.c linux/arch/alpha/kernel/sys_rawhide.c --- v2.2.13/linux/arch/alpha/kernel/sys_rawhide.c Sun Jan 10 09:59:59 1999 +++ linux/arch/alpha/kernel/sys_rawhide.c Tue Jan 4 10:12:11 2000 @@ -30,38 +30,53 @@ #include "bios32.h" #include "machvec.h" +static unsigned int hose_irq_masks[4] = { 0xff0000, 0xfe0000, + 0xff0000, 0xff0000 }; static void -rawhide_update_irq_hw(unsigned long irq, unsigned long mask, int unmask_p) +rawhide_update_irq_hw(unsigned long irq, unsigned long unused, int unmask_p) { - if (irq >= 40) { - /* PCI bus 1 with builtin NCR810 SCSI */ - *(vuip)MCPCIA_INT_MASK0(1) = - (~((mask) >> 40) & 0x00ffffffU) | 0x00fe0000U; - mb(); - /* ... and read it back to make sure it got written. */ - *(vuip)MCPCIA_INT_MASK0(1); + unsigned int saddle, hose, new_irq; + unsigned long mask; + + saddle = (irq > 63); /* Which saddle are we on? */ + mask = _alpha_irq_masks[saddle]; /* Use the correct mask. */ + + if (irq < 16) { + if (irq < 8) + outb(mask, 0x21); /* ISA PIC1 */ + else + outb(mask >> 8, 0xA1); /* ISA PIC2 */ + return; } - else if (irq >= 16) { - /* PCI bus 0 with EISA bridge */ - *(vuip)MCPCIA_INT_MASK0(0) = - (~((mask) >> 16) & 0x00ffffffU) | 0x00ff0000U; - mb(); - /* ... and read it back to make sure it got written. */ - *(vuip)MCPCIA_INT_MASK0(0); + + if (!saddle) { + mask >>= 16; /* Saddle 0 includes EISA interrupts. */ + new_irq = irq - 16; + } else { + new_irq = irq - 64; } - else if (irq >= 8) - outb(mask >> 8, 0xA1); /* ISA PIC2 */ - else - outb(mask, 0x21); /* ISA PIC1 */ + + hose = (saddle << 1); + + if (new_irq >= 24) { + mask >>= 24; + hose++; + } + + *(vuip)MCPCIA_INT_MASK0(hose) = + (~(mask) & 0x00ffffffU) | hose_irq_masks[hose]; + mb(); + /* ... and read it back to make sure it got written. */ + *(vuip)MCPCIA_INT_MASK0(hose); } static void rawhide_srm_device_interrupt(unsigned long vector, struct pt_regs * regs) { - int irq, ack; + int irq; - ack = irq = (vector - 0x800) >> 4; + irq = (vector - 0x800) >> 4; /* * The RAWHIDE SRM console reports PCI interrupts with a vector @@ -73,34 +88,31 @@ * also, PCI #1 interrupts are offset some more... :-( */ if (irq == 52) - ack = irq = 56; /* SCSI on PCI 1 is special */ - else { - if (irq >= 24) /* adjust all PCI interrupts down 8 */ - ack = irq = irq - 8; - if (irq >= 48) /* adjust PCI bus 1 interrupts down another 8 */ - ack = irq = irq - 8; - } + irq = 72; /* SCSI on PCI 1 is special */ + + /* Adjust by which hose it is from. */ + irq -= (((irq + 16) >> 2) & 0x38); - handle_irq(irq, ack, regs); + handle_irq(irq, irq, regs); } static void __init rawhide_init_irq(void) { - STANDARD_INIT_IRQ_PROLOG; + unsigned int hose; - /* HACK ALERT! only PCI busses 0 and 1 are used currently, - and routing is only to CPU #1*/ + mcpcia_init_hoses(); - *(vuip)MCPCIA_INT_MASK0(0) = - (~((alpha_irq_mask) >> 16) & 0x00ffffffU) | 0x00ff0000U; mb(); - /* ... and read it back to make sure it got written. */ - *(vuip)MCPCIA_INT_MASK0(0); + STANDARD_INIT_IRQ_PROLOG; - *(vuip)MCPCIA_INT_MASK0(1) = - (~((alpha_irq_mask) >> 40) & 0x00ffffffU) | 0x00fe0000U; mb(); - /* ... and read it back to make sure it got written. */ - *(vuip)MCPCIA_INT_MASK0(1); + /* HACK ALERT! routing is only to CPU #0. */ + + for (hose = 0; hose < hose_count; hose++) { + *(vuip)MCPCIA_INT_MASK0(hose) = hose_irq_masks[hose]; + mb(); + /* ... and read it back to make sure it got written. */ + *(vuip)MCPCIA_INT_MASK0(hose); + } enable_irq(2); } @@ -177,8 +189,8 @@ machine_check: mcpcia_machine_check, max_dma_address: ALPHA_MAX_DMA_ADDRESS, - nr_irqs: 64, - irq_probe_mask: _PROBE_MASK(64), + nr_irqs: 128, + irq_probe_mask: _PROBE_MASK(128), update_irq_hw: rawhide_update_irq_hw, ack_irq: generic_ack_irq, device_interrupt: rawhide_srm_device_interrupt, diff -u --recursive --new-file v2.2.13/linux/arch/alpha/kernel/sys_takara.c linux/arch/alpha/kernel/sys_takara.c --- v2.2.13/linux/arch/alpha/kernel/sys_takara.c Sat Jun 12 11:52:52 1999 +++ linux/arch/alpha/kernel/sys_takara.c Tue Jan 4 10:12:11 2000 @@ -31,18 +31,25 @@ static void -takara_update_irq_hw(unsigned long irq, unsigned long mask, int unmask_p) +takara_update_irq_hw(unsigned long irq, unsigned long unused, int unmask_p) { unsigned int regaddr; + unsigned long mask; if (irq <= 15) { if (irq <= 7) - outb(mask, 0x21); /* ISA PIC1 */ + outb(alpha_irq_mask, 0x21); /* ISA PIC1 */ else - outb(mask >> 8, 0xA1); /* ISA PIC2 */ - } else if (irq <= 31) { - regaddr = 0x510 + ((irq - 16) & 0x0c); - outl((mask >> ((irq - 16) & 0x0c)) & 0xf0000Ul, regaddr); + outb(alpha_irq_mask >> 8, 0xA1); /* ISA PIC2 */ + } else { + if (irq > 63) + mask = _alpha_irq_masks[1] << 16; + else + mask = _alpha_irq_masks[0] >> ((irq - 16) & 0x30); + + regaddr = 0x510 + (((irq - 16) >> 2) & 0x0c); + + outl(mask & 0xffff0000UL, regaddr); } } @@ -88,9 +95,6 @@ { int irq = (vector - 0x800) >> 4; - if (irq > 15) - irq = ((vector - 0x800) >> 6) + 12; - handle_irq(irq, irq, regs); } @@ -101,8 +105,7 @@ if (alpha_using_srm) alpha_mv.device_interrupt = takara_srm_device_interrupt; - - if (!alpha_using_srm) { + else { unsigned int ctlreg = inl(0x500); /* Return to non-accelerated mode. */ @@ -127,6 +130,43 @@ */ static int __init +takara_map_irq_srm(struct pci_dev *dev, int slot, int pin) +{ + static char irq_tab[15][5] __initlocaldata = { + { 16+3, 16+3, 16+3, 16+3, 16+3}, /* slot 6 == device 3 */ + { 16+2, 16+2, 16+2, 16+2, 16+2}, /* slot 7 == device 2 */ + { 16+1, 16+1, 16+1, 16+1, 16+1}, /* slot 8 == device 1 */ + { -1, -1, -1, -1, -1}, /* slot 9 == nothing */ + { -1, -1, -1, -1, -1}, /* slot 10 == nothing */ + { -1, -1, -1, -1, -1}, /* slot 11 == nothing */ +#if 0 + { -1, -1, -1, -1, -1}, /* slot 12 == nothing */ + { -1, -1, -1, -1, -1}, /* slot 13 == nothing */ + { -1, -1, -1, -1, -1}, /* slot 14 == nothing */ + { -1, -1, -1, -1, -1}, /* slot 15 == nothing */ +#else + /* these are behind the bridges */ + { 12, 12, 13, 14, 15}, /* slot 12 == nothing */ + { 8, 8, 9, 19, 11}, /* slot 13 == nothing */ + { 4, 4, 5, 6, 7}, /* slot 14 == nothing */ + { 0, 0, 1, 2, 3}, /* slot 15 == nothing */ +#endif + { -1, -1, -1, -1, -1}, /* slot 16 == nothing */ + {64+ 0, 64+0, 64+1, 64+2, 64+3}, /* slot 17= device 4 */ + {48+ 0, 48+0, 48+1, 48+2, 48+3}, /* slot 18= device 3 */ + {32+ 0, 32+0, 32+1, 32+2, 32+3}, /* slot 19= device 2 */ + {16+ 0, 16+0, 16+1, 16+2, 16+3}, /* slot 20= device 1 */ + }; + const long min_idsel = 6, max_idsel = 20, irqs_per_slot = 5; + int irq = COMMON_TABLE_LOOKUP; + if (irq >= 0 && irq < 16) { /* guess that we are behind a bridge */ + unsigned int busslot = PCI_SLOT(dev->bus->self->devfn); + irq += irq_tab[busslot-min_idsel][0]; + } + return irq; +} + +static int __init takara_map_irq(struct pci_dev *dev, int slot, int pin) { static char irq_tab[15][5] __initlocaldata = { @@ -147,7 +187,7 @@ { 16+1, 16+1, 16+1, 16+1, 16+1}, /* slot 20 == device 1 */ }; const long min_idsel = 6, max_idsel = 20, irqs_per_slot = 5; - return COMMON_TABLE_LOOKUP; + return COMMON_TABLE_LOOKUP; } static int __init @@ -159,14 +199,21 @@ unsigned int busslot = PCI_SLOT(dev->bus->self->devfn); /* Check first for built-in bridges. */ - if (busslot > 16 && ((1<<(36-busslot)) & ctlreg)) { + if (busslot > 16 && ((1U << (36 - busslot)) & ctlreg)) { +#if 0 if (pin == 1) pin += (20 - busslot); else { - /* Must be a card-based bridge. */ - printk(KERN_WARNING "takara_swizzle: cannot handle " - "card-bridge behind builtin bridge yet.\n"); + /* Can only handle INTA pins currently. */ + printk(KERN_WARNING "takara_swizzle: cannot only " + "handle cards with INTA IRQ pin now.\n"); } +#else +#endif + } else { + /* Must be a card-based bridge. */ + printk(KERN_WARNING "takara_swizzle: cannot handle " + "card-bridge behind builtin bridge yet.\n"); } *pinp = pin; @@ -177,8 +224,11 @@ takara_pci_fixup(void) { layout_all_busses(DEFAULT_IO_BASE, DEFAULT_MEM_BASE); - common_pci_fixup(takara_map_irq, takara_swizzle); - /* enable_ide(0x26e); */ + if (alpha_using_srm) + common_pci_fixup(takara_map_irq_srm, takara_swizzle); + else + common_pci_fixup(takara_map_irq, takara_swizzle); + enable_ide(0x26e); } @@ -195,8 +245,8 @@ machine_check: cia_machine_check, max_dma_address: ALPHA_MAX_DMA_ADDRESS, - nr_irqs: 20, - irq_probe_mask: _PROBE_MASK(20), + nr_irqs: 128, + irq_probe_mask: _PROBE_MASK(48), update_irq_hw: takara_update_irq_hw, ack_irq: generic_ack_irq, device_interrupt: takara_device_interrupt, diff -u --recursive --new-file v2.2.13/linux/arch/alpha/kernel/traps.c linux/arch/alpha/kernel/traps.c --- v2.2.13/linux/arch/alpha/kernel/traps.c Sat May 22 13:42:51 1999 +++ linux/arch/alpha/kernel/traps.c Tue Jan 4 10:12:11 2000 @@ -27,66 +27,270 @@ { printk("pc = [<%016lx>] ra = [<%016lx>] ps = %04lx\n", regs->pc, regs->r26, regs->ps); - printk("r0 = %016lx r1 = %016lx r2 = %016lx\n", + printk("v0 = %016lx t0 = %016lx t1 = %016lx\n", regs->r0, regs->r1, regs->r2); - printk("r3 = %016lx r4 = %016lx r5 = %016lx\n", + printk("t2 = %016lx t3 = %016lx t4 = %016lx\n", regs->r3, regs->r4, regs->r5); - printk("r6 = %016lx r7 = %016lx r8 = %016lx\n", + printk("t5 = %016lx t6 = %016lx t7 = %016lx\n", regs->r6, regs->r7, regs->r8); if (r9_15) { - printk("r9 = %016lx r10= %016lx r11= %016lx\n", + printk("s0 = %016lx s1 = %016lx s2 = %016lx\n", r9_15[9], r9_15[10], r9_15[11]); - printk("r12= %016lx r13= %016lx r14= %016lx\n", + printk("s3 = %016lx s4 = %016lx s5 = %016lx\n", r9_15[12], r9_15[13], r9_15[14]); - printk("r15= %016lx\n", r9_15[15]); + printk("s6 = %016lx", r9_15[15]); } - printk("r16= %016lx r17= %016lx r18= %016lx\n", + printk(" a0 = %016lx a1 = %016lx\na2 = %016lx", regs->r16, regs->r17, regs->r18); - printk("r19= %016lx r20= %016lx r21= %016lx\n", + printk(" a3 = %016lx a4 = %016lx\na5 = %016lx", regs->r19, regs->r20, regs->r21); - printk("r22= %016lx r23= %016lx r24= %016lx\n", + printk(" t8 = %016lx t9 = %016lx\nt10= %016lx", regs->r22, regs->r23, regs->r24); - printk("r25= %016lx r27= %016lx r28= %016lx\n", + printk(" t11= %016lx pv = %016lx\nat = %016lx", regs->r25, regs->r27, regs->r28); - printk("gp = %016lx sp = %p\n", regs->gp, regs+1); + printk(" gp = %016lx sp = %p\n", regs->gp, regs+1); +} + +static char * ireg_name[] = {"v0", "t0", "t1", "t2", "t3", "t4", "t5", "t6", + "t7", "s0", "s1", "s2", "s3", "s4", "s5", "s6", + "a0", "a1", "a2", "a3", "a4", "a5", "t8", "t9", + "t10", "t11", "ra", "pv", "at", "gp", "sp", "zero"}; + +static char * inst_name[] = {"call_pal", "", "", "", "", "", "", "", + "lda", "ldah", "ldbu", "ldq_u", "ldwu", "stw", "stb", "stq_u", + "ALU", "ALU", "ALU", "ALU", "SQRT", "FVAX", "FIEEE", "FLOAT", + "MISC", "PAL19", "JMP", "PAL1B", "GRAPH", "PAL1D", "PAL1E", "PAL1F", + "ldf", "ldg", "lds", "ldt", "stf", "stg", "sts", "stt", + "ldl", "ldq", "ldl_l", "ldq_l", "stl", "stq", "stl_c", "stq_c", + "br", "fbeq", "fblt", "fble", "bsr", "fbne", "fbge", "fbgt" + "blbc", "beq", "blt", "ble", "blbs", "bne", "bge", "bgt" +}; + +static char * jump_name[] = {"jmp", "jsr", "ret", "jsr_coroutine"}; + +typedef struct {int func; char * text;} alist; + +static alist inta_name[] = {{0, "addl"}, {2, "s4addl"}, {9, "subl"}, + {0xb, "s4subl"}, {0xf, "cmpbge"}, {0x12, "s8addl"}, {0x1b, "s8subl"}, + {0x1d, "cmpult"}, {0x20, "addq"}, {0x22, "s4addq"}, {0x29, "subq"}, + {0x2b, "s4subq"}, {0x2d, "cmpeq"}, {0x32, "s8addq"}, {0x3b, "s8subq"}, + {0x3d, "cmpule"}, {0x40, "addl/v"}, {0x49, "subl/v"}, {0x4d, "cmplt"}, + {0x60, "addq/v"}, {0x69, "subq/v"}, {0x6d, "cmple"}, {-1, 0}}; + +static alist intl_name[] = {{0, "and"}, {8, "andnot"}, {0x14, "cmovlbs"}, + {0x16, "cmovlbc"}, {0x20, "or"}, {0x24, "cmoveq"}, {0x26, "cmovne"}, + {0x28, "ornot"}, {0x40, "xor"}, {0x44, "cmovlt"}, {0x46, "cmovge"}, + {0x48, "eqv"}, {0x61, "amask"}, {0x64, "cmovle"}, {0x66, "cmovgt"}, + {0x6c, "implver"}, {-1, 0}}; + +static alist ints_name[] = {{2, "mskbl"}, {6, "extbl"}, {0xb, "insbl"}, + {0x12, "mskwl"}, {0x16, "extwl"}, {0x1b, "inswl"}, {0x22, "mskll"}, + {0x26, "extll"}, {0x2b, "insll"}, {0x30, "zap"}, {0x31, "zapnot"}, + {0x32, "mskql"}, {0x34, "srl"}, {0x36, "extql"}, {0x39, "sll"}, + {0x3b, "insql"}, {0x3c, "sra"}, {0x52, "mskwh"}, {0x57, "inswh"}, + {0x5a, "extwh"}, {0x62, "msklh"}, {0x67, "inslh"}, {0x6a, "extlh"}, + {0x72, "mskqh"}, {0x77, "insqh"}, {0x7a, "extqh"}, {-1, 0}}; + +static alist intm_name[] = {{0, "mull"}, {0x20, "mulq"}, {0x30, "umulh"}, + {0x40, "mull/v"}, {0x60, "mulq/v"}, {-1, 0}}; + +static alist * int_name[] = {inta_name, intl_name, ints_name, intm_name}; + +static char * +assoc(int fcode, alist * a) +{ + while ((fcode != a->func) && (a->func != -1)) + ++a; + return a->text; +} + +static char * +iname(unsigned int instr) +{ + int opcode = instr >> 26; + char * name = inst_name[opcode]; + + switch (opcode) { + default: + break; + + case 0x10: + case 0x11: + case 0x12: + case 0x13: { + char * specific_name + = assoc((instr >> 5) & 0x3f, int_name[opcode - 0x10]); + if (specific_name) + name = specific_name; + break; + } + + case 0x1a: + name = jump_name[(instr >> 14) & 3]; + break; + } + + return name; +} + +static enum {NOT_INST, PAL, BRANCH, MEMORY, JUMP, OPERATE, FOPERATE, MISC} +iformat(int opcode) +{ + if (opcode >= 0x30) + return BRANCH; + if (opcode >= 0x20) + return MEMORY; + if (opcode == 0) + return PAL; + if (opcode < 8) + return NOT_INST; + if (opcode < 0x10) + return MEMORY; + if (opcode < 0x14) + return OPERATE; + if (opcode < 0x18) + return FOPERATE; + switch (opcode) { + case 0x18: + return MISC; + case 0x1A: + return JUMP; + case 0x1C: + return OPERATE; + default: + return NOT_INST; + } +} + +/* + * The purpose here is to provide useful clues about a kernel crash, so + * less likely instructions, e.g. floating point, aren't fully decoded. + */ +static void +disassemble(unsigned int instr) +{ + int optype = instr >> 26; + char buf[40], *s = buf; + + s += sprintf(buf, "%08x %s ", instr, iname(instr)); + switch (iformat(optype)) { + default: + case NOT_INST: + case MISC: + break; + + case PAL: + s += sprintf(s, "%d", instr); + break; + + case BRANCH: { + int reg = (instr >> 21) & 0x1f; + int offset = instr & 0x1fffff; + + if (offset >= 0x100000) + offset -= 0x200000; + if (((optype & 3) == 0) || (optype >= 0x38)) { + if ((optype != 0x30) || (reg != 0x1f)) + s += sprintf(s, "%s,", ireg_name[reg]); + } else + s += sprintf(s, "f%d,", reg); + s += sprintf(s, ".%+d", (offset + 1) << 2); + break; + } + + case MEMORY: { + int addr_reg = (instr >> 16) & 0x1f; + int value_reg = (instr >> 21) & 0x1f; + int offset = instr & 0xffff; + + if (offset >= 0x8000) + offset -= 0x10000; + if ((optype >= 0x20) && (optype < 0x28)) + s += sprintf(s, "f%d", value_reg); + else + s += sprintf(s, "%s", ireg_name[value_reg]); + + s += sprintf(s, ",%d(%s)", offset, ireg_name[addr_reg]); + break; + } + + case JUMP: { + int target_reg = (instr >> 16) & 0x1f; + int return_reg = (instr >> 21) & 0x1f; + + s += sprintf(s, "%s,", ireg_name[return_reg]); + s += sprintf(s, "(%s)", ireg_name[target_reg]); + break; + } + + case OPERATE: { + int areg = (instr >> 21) & 0x1f; + int breg = (instr >> 16) & 0x1f; + int creg = instr & 0x1f; + int litflag = instr & (1<<12); + int lit = (instr >> 13) & 0xff; + + s += sprintf(s, "%s,", ireg_name[areg]); + if (litflag) + s += sprintf(s, "%d", lit); + else + s += sprintf(s, "%s", ireg_name[breg]); + s += sprintf(s, ",%s", ireg_name[creg]); + break; + } + + case FOPERATE: { + int areg = (instr >> 21) & 0x1f; + int breg = (instr >> 16) & 0x1f; + int creg = instr & 0x1f; + + s += sprintf(s, "f%d,f%d,f%d", areg, breg, creg); + break; + } + } + buf[s-buf] = 0; + printk("%s\n", buf); } static void dik_show_code(unsigned int *pc) { - long i; + int i; - printk("Code:"); - for (i = -3; i < 6; i++) { + printk("Code:\n"); + for (i = -6; i < 2; i++) { unsigned int insn; if (__get_user(insn, pc+i)) break; - printk("%c%08x%c",i?' ':'<',insn,i?' ':'>'); + printk("%c", i ? ' ' : '*'); + disassemble(insn); } - printk("\n"); } static void dik_show_trace(unsigned long *sp) { - long i = 0; - printk("Trace:"); - while (0x1ff8 & (unsigned long) sp) { + int i = 1; + + printk("Trace: "); + do { extern unsigned long _stext, _etext; - unsigned long tmp = *sp; - sp++; - if (tmp < (unsigned long) &_stext) + unsigned long kpc = *sp; + + if (kpc < (unsigned long) &_stext) continue; - if (tmp >= (unsigned long) &_etext) + if (kpc >= (unsigned long) &_etext) continue; - printk(" [<%lx>]", tmp); - if (++i > 40) { - printk(" ..."); - break; - } - } + /* + * Assume that only the low 24-bits of a kernel text address + * is interesting. + */ + printk("%6x%c", (int)kpc & 0xffffff, (++i % 11) ? ' ' : '\n'); + } while (((unsigned long)++sp & 0x1ff8) && (i < 33)); + if (i >= 33) + printk(" ..."); printk("\n"); } diff -u --recursive --new-file v2.2.13/linux/arch/alpha/math-emu/Makefile linux/arch/alpha/math-emu/Makefile --- v2.2.13/linux/arch/alpha/math-emu/Makefile Tue Oct 19 17:10:36 1999 +++ linux/arch/alpha/math-emu/Makefile Tue Jan 4 10:12:11 2000 @@ -1,21 +1,18 @@ # -# Makefile for math-emulator files... +# Makefile for the FPU instruction emulation. # +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... -O_TARGET := math-emu.o -O_OBJS := fp-emul.o udivmodti4.o - -LINKS := double.h op-common.h op-1.h op-2.h op-4.h single.h \ - soft-fp.h udivmodti4.c +O_TARGET := math-emu.o +O_OBJS := math.o +CFLAGS += -I. -I$(TOPDIR)/include/math-emu -w ifeq ($(CONFIG_MATHEMU),m) M_OBJS := $(O_TARGET) endif include $(TOPDIR)/Rules.make - -symlinks: - ln -sf $(patsubst %,../../sparc64/math-emu/%,$(LINKS)) . - -cleansymlinks: - rm -f $(LINKS) diff -u --recursive --new-file v2.2.13/linux/arch/alpha/math-emu/fp-emul.c linux/arch/alpha/math-emu/fp-emul.c --- v2.2.13/linux/arch/alpha/math-emu/fp-emul.c Tue Oct 19 17:10:36 1999 +++ linux/arch/alpha/math-emu/fp-emul.c Wed Dec 31 16:00:00 1969 @@ -1,451 +0,0 @@ -#include -#include -#include -#include - -#include - -#include "soft-fp.h" -#include "double.h" -#include "single.h" - -#define OPC_PAL 0x00 -#define OPC_INTA 0x10 -#define OPC_INTL 0x11 -#define OPC_INTS 0x12 -#define OPC_INTM 0x13 -#define OPC_FLTC 0x14 -#define OPC_FLTV 0x15 -#define OPC_FLTI 0x16 -#define OPC_FLTL 0x17 -#define OPC_MISC 0x18 -#define OPC_JSR 0x1a - -#define FOP_SRC_S 0 -#define FOP_SRC_T 2 -#define FOP_SRC_Q 3 - -#define FOP_FNC_ADDx 0 -#define FOP_FNC_CVTQL 0 -#define FOP_FNC_SUBx 1 -#define FOP_FNC_MULx 2 -#define FOP_FNC_DIVx 3 -#define FOP_FNC_CMPxUN 4 -#define FOP_FNC_CMPxEQ 5 -#define FOP_FNC_CMPxLT 6 -#define FOP_FNC_CMPxLE 7 -#define FOP_FNC_SQRTx 11 -#define FOP_FNC_CVTxS 12 -#define FOP_FNC_CVTxT 14 -#define FOP_FNC_CVTxQ 15 - -#define MISC_TRAPB 0x0000 -#define MISC_EXCB 0x0400 - -extern unsigned long alpha_read_fp_reg (unsigned long reg); -extern void alpha_write_fp_reg (unsigned long reg, unsigned long val); -extern unsigned long alpha_read_fp_reg_s (unsigned long reg); -extern void alpha_write_fp_reg_s (unsigned long reg, unsigned long val); - - -#ifdef MODULE - -MODULE_DESCRIPTION("FP Software completion module"); - -extern long (*alpha_fp_emul_imprecise)(struct pt_regs *, unsigned long); -extern long (*alpha_fp_emul) (unsigned long pc); - -static long (*save_emul_imprecise)(struct pt_regs *, unsigned long); -static long (*save_emul) (unsigned long pc); - -long do_alpha_fp_emul_imprecise(struct pt_regs *, unsigned long); -long do_alpha_fp_emul(unsigned long); - -int init_module(void) -{ - save_emul_imprecise = alpha_fp_emul_imprecise; - save_emul = alpha_fp_emul; - alpha_fp_emul_imprecise = do_alpha_fp_emul_imprecise; - alpha_fp_emul = do_alpha_fp_emul; - return 0; -} - -void cleanup_module(void) -{ - alpha_fp_emul_imprecise = save_emul_imprecise; - alpha_fp_emul = save_emul; -} - -#undef alpha_fp_emul_imprecise -#define alpha_fp_emul_imprecise do_alpha_fp_emul_imprecise -#undef alpha_fp_emul -#define alpha_fp_emul do_alpha_fp_emul - -#endif /* MODULE */ - -/* For 128-bit division. */ - -__complex__ unsigned long -udiv128(unsigned long divisor_f0, unsigned long divisor_f1, - unsigned long dividend_f0, unsigned long dividend_f1) -{ - _FP_FRAC_DECL_2(quo); - _FP_FRAC_DECL_2(rem); - _FP_FRAC_DECL_2(tmp); - unsigned long i, num_bits, bit; - __complex__ unsigned long ret; - - _FP_FRAC_SET_2(rem, _FP_ZEROFRAC_2); - _FP_FRAC_SET_2(quo, _FP_ZEROFRAC_2); - - if (_FP_FRAC_ZEROP_2(divisor)) - goto out; - - if (_FP_FRAC_GT_2(divisor, dividend)) { - _FP_FRAC_COPY_2(rem, dividend); - goto out; - } - - if (_FP_FRAC_EQ_2(divisor, dividend)) { - __FP_FRAC_SET_2(quo, 0, 1); - goto out; - } - - num_bits = 128; - while (1) { - bit = _FP_FRAC_NEGP_2(dividend); - _FP_FRAC_COPY_2(tmp, rem); - _FP_FRAC_SLL_2(tmp, 1); - _FP_FRAC_LOW_2(tmp) |= bit; - if (! _FP_FRAC_GE_2(tmp, divisor)) - break; - _FP_FRAC_COPY_2(rem, tmp); - _FP_FRAC_SLL_2(dividend, 1); - num_bits--; - } - - for (i = 0; i < num_bits; i++) { - bit = _FP_FRAC_NEGP_2(dividend); - _FP_FRAC_SLL_2(rem, 1); - _FP_FRAC_LOW_2(rem) |= bit; - _FP_FRAC_SUB_2(tmp, rem, divisor); - bit = _FP_FRAC_NEGP_2(tmp); - _FP_FRAC_SLL_2(dividend, 1); - _FP_FRAC_SLL_2(quo, 1); - if (!bit) { - _FP_FRAC_LOW_2(quo) |= 1; - _FP_FRAC_COPY_2(rem, tmp); - } - } - -out: - __real__ ret = quo_f1; - __imag__ ret = rem_f1; - return ret; -} - -/* - * Emulate the floating point instruction at address PC. Returns 0 if - * emulation fails. Notice that the kernel does not and cannot use FP - * regs. This is good because it means that instead of - * saving/restoring all fp regs, we simply stick the result of the - * operation into the appropriate register. - */ -long -alpha_fp_emul (unsigned long pc) -{ - FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR); - FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR); - - unsigned long fa, fb, fc, func, mode, src; - unsigned long fpcw = current->tss.flags; - unsigned long res, cmptype, va, vb, vc, fpcr; - __u32 insn; - - MOD_INC_USE_COUNT; - - get_user(insn, (__u32*)pc); - fc = (insn >> 0) & 0x1f; /* destination register */ - fb = (insn >> 16) & 0x1f; - fa = (insn >> 21) & 0x1f; - func = (insn >> 5) & 0xf; - src = (insn >> 9) & 0x3; - mode = (insn >> 11) & 0x3; - - fpcr = rdfpcr(); - - if (mode == 3) { - /* Dynamic -- get rounding mode from fpcr. */ - mode = (fpcr >> FPCR_DYN_SHIFT) & 3; - } - - res = 0; - - switch (src) { - case FOP_SRC_S: - va = alpha_read_fp_reg_s(fa); - vb = alpha_read_fp_reg_s(fb); - - __FP_UNPACK_S(SA, &va); - __FP_UNPACK_S(SB, &vb); - - switch (func) { - case FOP_FNC_SUBx: - if (SB_c != FP_CLS_NAN) - SB_s ^= 1; - /* FALLTHRU */ - case FOP_FNC_ADDx: - FP_ADD_S(SR, SA, SB); - goto pack_s; - - case FOP_FNC_MULx: - FP_MUL_S(SR, SA, SB); - goto pack_s; - - case FOP_FNC_DIVx: - if (SB_c == FP_CLS_ZERO && SA_c != FP_CLS_ZERO) { - res |= EFLAG_DIVZERO; - if (__FPU_TRAP_P(EFLAG_DIVZERO)) - goto done; - } - FP_DIV_S(SR, SA, SB); - goto pack_s; - - case FOP_FNC_SQRTx: - FP_SQRT_S(SR, SA); - goto pack_s; - } - goto bad_insn; - - case FOP_SRC_T: - va = alpha_read_fp_reg(fa); - vb = alpha_read_fp_reg(fb); - - __FP_UNPACK_D(DA, &va); - __FP_UNPACK_D(DB, &vb); - - switch (func) { - case FOP_FNC_SUBx: - if (DB_c != FP_CLS_NAN) - DB_s ^= 1; - /* FALLTHRU */ - case FOP_FNC_ADDx: - FP_ADD_D(DR, DA, DB); - goto pack_d; - - case FOP_FNC_MULx: - FP_MUL_D(DR, DA, DB); - goto pack_d; - - case FOP_FNC_DIVx: - if (DB_c == FP_CLS_ZERO && DA_c != FP_CLS_ZERO) { - res |= EFLAG_DIVZERO; - if (__FPU_TRAP_P(EFLAG_DIVZERO)) - goto done; - } - FP_DIV_D(DR, DA, DB); - goto pack_d; - - case FOP_FNC_CMPxUN: - cmptype = CMPTXX_UN; - goto compare; - case FOP_FNC_CMPxEQ: - cmptype = CMPTXX_EQ; - goto compare; - case FOP_FNC_CMPxLT: - cmptype = CMPTXX_LT; - goto compare; - case FOP_FNC_CMPxLE: - cmptype = CMPTXX_LE; - goto compare; - compare: - FP_CMP_D(res, DA, DB, 3); - vc = 0; - if (res == cmptype - || (cmptype == CMPTXX_LE - && (res == CMPTXX_LT || res == CMPTXX_EQ))) { - vc = 0x4000000000000000; - } - goto done_d; - - case FOP_FNC_SQRTx: - FP_SQRT_D(DR, DA); - goto pack_d; - - case FOP_FNC_CVTxS: - /* It is irritating that DEC encoded CVTST with - SRC == T_floating. It is also interesting that - the bit used to tell the two apart is /U... */ - if (insn & 0x2000) { - FP_CONV(S,D,1,1,SR,DA); - goto pack_s; - } else { - /* CVTST need do nothing else but copy the - bits and repack. */ - DR_c = DA_c; - DR_s = DA_s; - DR_e = DA_e; - DR_r = DA_r; - DR_f = DA_f; - goto pack_d; - } - - case FOP_FNC_CVTxQ: - FP_TO_INT_D(vc, DA, 64, 1); - res = _FTOI_RESULT(DA); - goto done_d; - } - goto bad_insn; - - case FOP_SRC_Q: - vb = alpha_read_fp_reg(fb); - - switch (func) { - case FOP_FNC_CVTQL: - /* Notice: We can get here only due to an integer - overflow. Such overflows are reported as invalid - ops. We return the result the hw would have - computed. */ - vc = ((vb & 0xc0000000) << 32 | /* sign and msb */ - (vb & 0x3fffffff) << 29); /* rest of the int */ - res = EFLAG_INVALID; - goto done_d; - - case FOP_FNC_CVTxS: - FP_FROM_INT_S(SR, ((long)vb), 64, long); - goto pack_s; - - case FOP_FNC_CVTxT: - FP_FROM_INT_D(DR, ((long)vb), 64, long); - goto pack_d; - } - goto bad_insn; - } - goto bad_insn; - -pack_s: - res |= __FP_PACK_S(&vc, SR); - alpha_write_fp_reg_s(fc, vc); - goto done; - -pack_d: - res |= __FP_PACK_D(&vc, DR); -done_d: - alpha_write_fp_reg(fc, vc); - goto done; - - /* - * Take the appropriate action for each possible - * floating-point result: - * - * - Set the appropriate bits in the FPCR - * - If the specified exception is enabled in the FPCR, - * return. The caller (entArith) will dispatch - * the appropriate signal to the translated program. - * - * In addition, properly track the exception state in software - * as described in the Alpha Architectre Handbook section 4.7.7.3. - */ -done: - if (res) { - /* Record exceptions in software control word. */ - current->tss.flags - = fpcw |= (res << IEEE_STATUS_TO_EXCSUM_SHIFT); - - /* Update hardware control register */ - fpcr &= (~FPCR_MASK | FPCR_DYN_MASK); - fpcr |= ieee_swcr_to_fpcr(fpcw); - wrfpcr(fpcr); - - /* Do we generate a signal? */ - if (res & fpcw & IEEE_TRAP_ENABLE_MASK) { - MOD_DEC_USE_COUNT; - return 0; - } - } - - /* We used to write the destination register here, but DEC FORTRAN - requires that the result *always* be written... so we do the write - immediately after the operations above. */ - - MOD_DEC_USE_COUNT; - return 1; - -bad_insn: - printk(KERN_ERR "alpha_fp_emul: Invalid FP insn %#x at %#lx\n", - insn, pc); - MOD_DEC_USE_COUNT; - return 0; -} - -long -alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask) -{ - unsigned long trigger_pc = regs->pc - 4; - unsigned long insn, opcode, rc; - - MOD_INC_USE_COUNT; - - /* - * Turn off the bits corresponding to registers that are the - * target of instructions that set bits in the exception - * summary register. We have some slack doing this because a - * register that is the target of a trapping instruction can - * be written at most once in the trap shadow. - * - * Branches, jumps, TRAPBs, EXCBs and calls to PALcode all - * bound the trap shadow, so we need not look any further than - * up to the first occurrence of such an instruction. - */ - while (write_mask) { - get_user(insn, (__u32*)(trigger_pc)); - opcode = insn >> 26; - rc = insn & 0x1f; - - switch (opcode) { - case OPC_PAL: - case OPC_JSR: - case 0x30 ... 0x3f: /* branches */ - MOD_DEC_USE_COUNT; - return 0; - - case OPC_MISC: - switch (insn & 0xffff) { - case MISC_TRAPB: - case MISC_EXCB: - MOD_DEC_USE_COUNT; - return 0; - - default: - break; - } - break; - - case OPC_INTA: - case OPC_INTL: - case OPC_INTS: - case OPC_INTM: - write_mask &= ~(1UL << rc); - break; - - case OPC_FLTC: - case OPC_FLTV: - case OPC_FLTI: - case OPC_FLTL: - write_mask &= ~(1UL << (rc + 32)); - break; - } - if (!write_mask) { - if (alpha_fp_emul(trigger_pc)) { - /* re-execute insns in trap-shadow: */ - regs->pc = trigger_pc + 4; - MOD_DEC_USE_COUNT; - return 1; - } - break; - } - trigger_pc -= 4; - } - MOD_DEC_USE_COUNT; - return 0; -} diff -u --recursive --new-file v2.2.13/linux/arch/alpha/math-emu/fp-emul.h linux/arch/alpha/math-emu/fp-emul.h --- v2.2.13/linux/arch/alpha/math-emu/fp-emul.h Thu Apr 11 23:49:30 1996 +++ linux/arch/alpha/math-emu/fp-emul.h Wed Dec 31 16:00:00 1969 @@ -1,10 +0,0 @@ -/* - * These defines correspond to the dynamic rounding mode bits in the - * Floating Point Control Register. They also happen to correspond to - * the instruction encodings except that 0x03 signifies dynamic - * rounding mode in that case. - */ -#define ROUND_CHOP 0x00 /* chopped (aka round towards zero) */ -#define ROUND_NINF 0x01 /* round towards negative infinity */ -#define ROUND_NEAR 0x02 /* round towards nearest number */ -#define ROUND_PINF 0x03 /* round towards positive infinity */ diff -u --recursive --new-file v2.2.13/linux/arch/alpha/math-emu/math.c linux/arch/alpha/math-emu/math.c --- v2.2.13/linux/arch/alpha/math-emu/math.c Wed Dec 31 16:00:00 1969 +++ linux/arch/alpha/math-emu/math.c Tue Jan 4 10:12:11 2000 @@ -0,0 +1,447 @@ +#include +#include +#include +#include + +#include + +#include "sfp-util.h" +#include "soft-fp.h" +#include "single.h" +#include "double.h" + +#define OPC_PAL 0x00 +#define OPC_INTA 0x10 +#define OPC_INTL 0x11 +#define OPC_INTS 0x12 +#define OPC_INTM 0x13 +#define OPC_FLTC 0x14 +#define OPC_FLTV 0x15 +#define OPC_FLTI 0x16 +#define OPC_FLTL 0x17 +#define OPC_MISC 0x18 +#define OPC_JSR 0x1a + +#define FOP_SRC_S 0 +#define FOP_SRC_T 2 +#define FOP_SRC_Q 3 + +#define FOP_FNC_ADDx 0 +#define FOP_FNC_CVTQL 0 +#define FOP_FNC_SUBx 1 +#define FOP_FNC_MULx 2 +#define FOP_FNC_DIVx 3 +#define FOP_FNC_CMPxUN 4 +#define FOP_FNC_CMPxEQ 5 +#define FOP_FNC_CMPxLT 6 +#define FOP_FNC_CMPxLE 7 +#define FOP_FNC_SQRTx 11 +#define FOP_FNC_CVTxS 12 +#define FOP_FNC_CVTxT 14 +#define FOP_FNC_CVTxQ 15 + +#define MISC_TRAPB 0x0000 +#define MISC_EXCB 0x0400 + +extern unsigned long alpha_read_fp_reg (unsigned long reg); +extern void alpha_write_fp_reg (unsigned long reg, unsigned long val); +extern unsigned long alpha_read_fp_reg_s (unsigned long reg); +extern void alpha_write_fp_reg_s (unsigned long reg, unsigned long val); + + +#ifdef MODULE + +MODULE_DESCRIPTION("FP Software completion module"); + +extern long (*alpha_fp_emul_imprecise)(struct pt_regs *, unsigned long); +extern long (*alpha_fp_emul) (unsigned long pc); + +static long (*save_emul_imprecise)(struct pt_regs *, unsigned long); +static long (*save_emul) (unsigned long pc); + +long do_alpha_fp_emul_imprecise(struct pt_regs *, unsigned long); +long do_alpha_fp_emul(unsigned long); + +int init_module(void) +{ + save_emul_imprecise = alpha_fp_emul_imprecise; + save_emul = alpha_fp_emul; + alpha_fp_emul_imprecise = do_alpha_fp_emul_imprecise; + alpha_fp_emul = do_alpha_fp_emul; + return 0; +} + +void cleanup_module(void) +{ + alpha_fp_emul_imprecise = save_emul_imprecise; + alpha_fp_emul = save_emul; +} + +#undef alpha_fp_emul_imprecise +#define alpha_fp_emul_imprecise do_alpha_fp_emul_imprecise +#undef alpha_fp_emul +#define alpha_fp_emul do_alpha_fp_emul + +#endif /* MODULE */ + +/* For 128-bit division. */ + +void +udiv128(unsigned long divisor_f0, unsigned long divisor_f1, + unsigned long dividend_f0, unsigned long dividend_f1, + unsigned long *quot, unsigned long *remd) +{ + _FP_FRAC_DECL_2(quo); + _FP_FRAC_DECL_2(rem); + _FP_FRAC_DECL_2(tmp); + unsigned long i, num_bits, bit; + + _FP_FRAC_SET_2(rem, _FP_ZEROFRAC_2); + _FP_FRAC_SET_2(quo, _FP_ZEROFRAC_2); + + if (_FP_FRAC_ZEROP_2(divisor)) + goto out; + + if (_FP_FRAC_GT_2(divisor, dividend)) { + _FP_FRAC_COPY_2(rem, dividend); + goto out; + } + + if (_FP_FRAC_EQ_2(divisor, dividend)) { + __FP_FRAC_SET_2(quo, 0, 1); + goto out; + } + + num_bits = 128; + while (1) { + bit = _FP_FRAC_NEGP_2(dividend); + _FP_FRAC_COPY_2(tmp, rem); + _FP_FRAC_SLL_2(tmp, 1); + _FP_FRAC_LOW_2(tmp) |= bit; + if (! _FP_FRAC_GE_2(tmp, divisor)) + break; + _FP_FRAC_COPY_2(rem, tmp); + _FP_FRAC_SLL_2(dividend, 1); + num_bits--; + } + + for (i = 0; i < num_bits; i++) { + bit = _FP_FRAC_NEGP_2(dividend); + _FP_FRAC_SLL_2(rem, 1); + _FP_FRAC_LOW_2(rem) |= bit; + _FP_FRAC_SUB_2(tmp, rem, divisor); + bit = _FP_FRAC_NEGP_2(tmp); + _FP_FRAC_SLL_2(dividend, 1); + _FP_FRAC_SLL_2(quo, 1); + if (!bit) { + _FP_FRAC_LOW_2(quo) |= 1; + _FP_FRAC_COPY_2(rem, tmp); + } + } + +out: + *quot = quo_f1; + *remd = rem_f1; + return; +} + +/* + * Emulate the floating point instruction at address PC. Returns 0 if + * emulation fails. Notice that the kernel does not and cannot use FP + * regs. This is good because it means that instead of + * saving/restoring all fp regs, we simply stick the result of the + * operation into the appropriate register. + */ +long +alpha_fp_emul (unsigned long pc) +{ + FP_DECL_EX; + FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR); + FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR); + + unsigned long fa, fb, fc, func, mode, src; + unsigned long fpcw = current->tss.flags; + unsigned long res, va, vb, vc, fpcr; + __u32 insn; + + MOD_INC_USE_COUNT; + + get_user(insn, (__u32*)pc); + fc = (insn >> 0) & 0x1f; /* destination register */ + fb = (insn >> 16) & 0x1f; + fa = (insn >> 21) & 0x1f; + func = (insn >> 5) & 0xf; + src = (insn >> 9) & 0x3; + mode = (insn >> 11) & 0x3; + + fpcr = rdfpcr(); + + if (mode == 3) { + /* Dynamic -- get rounding mode from fpcr. */ + mode = (fpcr >> FPCR_DYN_SHIFT) & 3; + } + + switch (src) { + case FOP_SRC_S: + va = alpha_read_fp_reg_s(fa); + vb = alpha_read_fp_reg_s(fb); + + FP_UNPACK_SP(SA, &va); + FP_UNPACK_SP(SB, &vb); + + switch (func) { + case FOP_FNC_SUBx: + FP_SUB_S(SR, SA, SB); + goto pack_s; + + case FOP_FNC_ADDx: + FP_ADD_S(SR, SA, SB); + goto pack_s; + + case FOP_FNC_MULx: + FP_MUL_S(SR, SA, SB); + goto pack_s; + + case FOP_FNC_DIVx: + FP_DIV_S(SR, SA, SB); + goto pack_s; + + case FOP_FNC_SQRTx: + FP_SQRT_S(SR, SB); + goto pack_s; + } + goto bad_insn; + + case FOP_SRC_T: + va = alpha_read_fp_reg(fa); + vb = alpha_read_fp_reg(fb); + + if ((func & ~3) == FOP_FNC_CMPxUN) { + FP_UNPACK_RAW_DP(DA, &va); + FP_UNPACK_RAW_DP(DB, &vb); + if (!DA_e && !_FP_FRAC_ZEROP_1(DA)) { + FP_SET_EXCEPTION(FP_EX_DENORM); + if (FP_DENORM_ZERO) + _FP_FRAC_SET_1(DA, _FP_ZEROFRAC_1); + } + if (!DB_e && !_FP_FRAC_ZEROP_1(DB)) { + FP_SET_EXCEPTION(FP_EX_DENORM); + if (FP_DENORM_ZERO) + _FP_FRAC_SET_1(DB, _FP_ZEROFRAC_1); + } + FP_CMP_D(res, DA, DB, 3); + vc = 0x4000000000000000; + /* CMPTEQ, CMPTUN don't trap on QNaN, while CMPTLT and CMPTLE do */ + if (res == 3 && ((func & 3) >= 2 || FP_ISSIGNAN_D(DA) || FP_ISSIGNAN_D(DB))) + FP_SET_EXCEPTION(FP_EX_INVALID); + switch (func) { + case FOP_FNC_CMPxUN: if (res != 3) vc = 0; break; + case FOP_FNC_CMPxEQ: if (res) vc = 0; break; + case FOP_FNC_CMPxLT: if (res != -1) vc = 0; break; + case FOP_FNC_CMPxLE: if ((long)res > 0) vc = 0; break; + } + goto done_d; + } + + FP_UNPACK_DP(DA, &va); + FP_UNPACK_DP(DB, &vb); + + switch (func) { + case FOP_FNC_SUBx: + FP_SUB_D(DR, DA, DB); + goto pack_d; + + case FOP_FNC_ADDx: + FP_ADD_D(DR, DA, DB); + goto pack_d; + + case FOP_FNC_MULx: + FP_MUL_D(DR, DA, DB); + goto pack_d; + + case FOP_FNC_DIVx: + FP_DIV_D(DR, DA, DB); + goto pack_d; + + case FOP_FNC_SQRTx: + FP_SQRT_D(DR, DB); + goto pack_d; + + case FOP_FNC_CVTxS: + /* It is irritating that DEC encoded CVTST with + SRC == T_floating. It is also interesting that + the bit used to tell the two apart is /U... */ + if (insn & 0x2000) { + FP_CONV(S,D,1,1,SR,DB); + goto pack_s; + } else { + /* CVTST need do nothing else but copy the + bits and repack. */ + DR_c = DB_c; + DR_s = DB_s; + DR_e = DB_e; + DR_f = DB_f; + goto pack_d; + } + + case FOP_FNC_CVTxQ: + if (DB_c == FP_CLS_NAN && (_FP_FRAC_HIGH_RAW_D(DB) & _FP_QNANBIT_D)) + vc = 0; /* AAHB Table B-2 sais QNaN should not trigger INV */ + else + FP_TO_INT_ROUND_D(vc, DB, 64, 2); + goto done_d; + } + goto bad_insn; + + case FOP_SRC_Q: + vb = alpha_read_fp_reg(fb); + + switch (func) { + case FOP_FNC_CVTQL: + /* Notice: We can get here only due to an integer + overflow. Such overflows are reported as invalid + ops. We return the result the hw would have + computed. */ + vc = ((vb & 0xc0000000) << 32 | /* sign and msb */ + (vb & 0x3fffffff) << 29); /* rest of the int */ + FP_SET_EXCEPTION (FP_EX_INVALID); + goto done_d; + + case FOP_FNC_CVTxS: + FP_FROM_INT_S(SR, ((long)vb), 64, long); + goto pack_s; + + case FOP_FNC_CVTxT: + FP_FROM_INT_D(DR, ((long)vb), 64, long); + goto pack_d; + } + goto bad_insn; + } + goto bad_insn; + +pack_s: + FP_PACK_SP(&vc, SR); + alpha_write_fp_reg_s(fc, vc); + goto done; + +pack_d: + FP_PACK_DP(&vc, DR); +done_d: + alpha_write_fp_reg(fc, vc); + goto done; + + /* + * Take the appropriate action for each possible + * floating-point result: + * + * - Set the appropriate bits in the FPCR + * - If the specified exception is enabled in the FPCR, + * return. The caller (entArith) will dispatch + * the appropriate signal to the translated program. + * + * In addition, properly track the exception state in software + * as described in the Alpha Architectre Handbook section 4.7.7.3. + */ +done: + if (_fex) { + /* Record exceptions in software control word. */ + current->tss.flags + = fpcw |= (_fex << IEEE_STATUS_TO_EXCSUM_SHIFT); + + /* Update hardware control register */ + fpcr &= (~FPCR_MASK | FPCR_DYN_MASK); + fpcr |= ieee_swcr_to_fpcr(fpcw); + wrfpcr(fpcr); + + /* Do we generate a signal? */ + if (_fex & fpcw & IEEE_TRAP_ENABLE_MASK) { + MOD_DEC_USE_COUNT; + return 0; + } + } + + /* We used to write the destination register here, but DEC FORTRAN + requires that the result *always* be written... so we do the write + immediately after the operations above. */ + + MOD_DEC_USE_COUNT; + return 1; + +bad_insn: + printk(KERN_ERR "alpha_fp_emul: Invalid FP insn %#x at %#lx\n", + insn, pc); + MOD_DEC_USE_COUNT; + return 0; +} + +long +alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask) +{ + unsigned long trigger_pc = regs->pc - 4; + unsigned long insn, opcode, rc; + + MOD_INC_USE_COUNT; + + /* + * Turn off the bits corresponding to registers that are the + * target of instructions that set bits in the exception + * summary register. We have some slack doing this because a + * register that is the target of a trapping instruction can + * be written at most once in the trap shadow. + * + * Branches, jumps, TRAPBs, EXCBs and calls to PALcode all + * bound the trap shadow, so we need not look any further than + * up to the first occurrence of such an instruction. + */ + while (write_mask) { + get_user(insn, (__u32*)(trigger_pc)); + opcode = insn >> 26; + rc = insn & 0x1f; + + switch (opcode) { + case OPC_PAL: + case OPC_JSR: + case 0x30 ... 0x3f: /* branches */ + MOD_DEC_USE_COUNT; + return 0; + + case OPC_MISC: + switch (insn & 0xffff) { + case MISC_TRAPB: + case MISC_EXCB: + MOD_DEC_USE_COUNT; + return 0; + + default: + break; + } + break; + + case OPC_INTA: + case OPC_INTL: + case OPC_INTS: + case OPC_INTM: + write_mask &= ~(1UL << rc); + break; + + case OPC_FLTC: + case OPC_FLTV: + case OPC_FLTI: + case OPC_FLTL: + write_mask &= ~(1UL << (rc + 32)); + break; + } + if (!write_mask) { + if (alpha_fp_emul(trigger_pc)) { + /* re-execute insns in trap-shadow: */ + regs->pc = trigger_pc + 4; + MOD_DEC_USE_COUNT; + return 1; + } + break; + } + trigger_pc -= 4; + } + MOD_DEC_USE_COUNT; + return 0; +} diff -u --recursive --new-file v2.2.13/linux/arch/alpha/math-emu/sfp-machine.h linux/arch/alpha/math-emu/sfp-machine.h --- v2.2.13/linux/arch/alpha/math-emu/sfp-machine.h Tue Oct 19 17:10:36 1999 +++ linux/arch/alpha/math-emu/sfp-machine.h Tue Jan 4 10:12:11 2000 @@ -1,6 +1,10 @@ -/* Machine-dependent software floating-point definitions. Sparc64 version. - Copyright (C) 1997 Free Software Foundation, Inc. +/* Machine-dependent software floating-point definitions. + Alpha kernel version. + Copyright (C) 1997,1998,1999 Free Software Foundation, Inc. This file is part of the GNU C Library. + Contributed by Richard Henderson (rth@cygnus.com), + Jakub Jelinek (jakub@redhat.com) and + David S. Miller (davem@redhat.com). The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as @@ -17,575 +21,64 @@ not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include -#include - +#ifndef _SFP_MACHINE_H +#define _SFP_MACHINE_H + #define _FP_W_TYPE_SIZE 64 #define _FP_W_TYPE unsigned long #define _FP_WS_TYPE signed long #define _FP_I_TYPE long -#define _FP_MUL_MEAT_S(R,X,Y) _FP_MUL_MEAT_1_imm(S,R,X,Y) -#define _FP_MUL_MEAT_D(R,X,Y) _FP_MUL_MEAT_1_wide(D,R,X,Y,umul_ppmm) -#define _FP_MUL_MEAT_Q(R,X,Y) _FP_MUL_MEAT_2_wide(Q,R,X,Y,umul_ppmm) +#define _FP_MUL_MEAT_S(R,X,Y) \ + _FP_MUL_MEAT_1_imm(_FP_WFRACBITS_S,R,X,Y) +#define _FP_MUL_MEAT_D(R,X,Y) \ + _FP_MUL_MEAT_1_wide(_FP_WFRACBITS_D,R,X,Y,umul_ppmm) +#define _FP_MUL_MEAT_Q(R,X,Y) \ + _FP_MUL_MEAT_2_wide(_FP_WFRACBITS_Q,R,X,Y,umul_ppmm) #define _FP_DIV_MEAT_S(R,X,Y) _FP_DIV_MEAT_1_imm(S,R,X,Y,_FP_DIV_HELP_imm) #define _FP_DIV_MEAT_D(R,X,Y) _FP_DIV_MEAT_1_udiv(D,R,X,Y) -#define _FP_DIV_MEAT_Q(R,X,Y) _FP_DIV_MEAT_2_udiv_64(Q,R,X,Y) +#define _FP_DIV_MEAT_Q(R,X,Y) _FP_DIV_MEAT_2_udiv(Q,R,X,Y) #define _FP_NANFRAC_S _FP_QNANBIT_S #define _FP_NANFRAC_D _FP_QNANBIT_D -#define _FP_NANFRAC_Q _FP_QNANBIT_Q, 0 - -/* On some architectures float-to-int conversions return a result - code. On others (e.g. Sparc) they return 0. */ -#define _FTOI_RESULT(X) X##_r - -#define _FP_KEEPNANFRACP 1 - -/* - * Alpha Architecture Manual Section 4.7.10.4: Propagating NaN Values, - * summary: - * - * The first of the following rules that is applicable governs the - * value returned: - * 1: If X is a quiet NaN, copy X to the result. - * 2: If X is a signaling NaN, the result is the canonical quiet NaN - * with the same sign as X - * 3: If Y is a quiet NaN, copy Y to the result. - * 4: If Y is a signaling NaN, the result is the canonical quiet NaN - * with the same sign as Y - * 5: The result is the canonical quiet NaN with a sign bit of 1 - * - * In addition, in cases (2) and (4) above we set EFLAG_INVALID. - */ +#define _FP_NANFRAC_Q _FP_QNANBIT_Q +#define _FP_NANSIGN_S 1 +#define _FP_NANSIGN_D 1 +#define _FP_NANSIGN_Q 1 -#define _FP_IS_NAN(fs, Z) (Z##_c == FP_CLS_NAN) -#define _FP_IS_QNAN(fs, Z) (Z##_f & _FP_QNANBIT_##fs) +#define _FP_KEEPNANFRACP 1 -#define _FP_CHOOSENAN(fs, wc, R, X, Y) \ - do { \ - R##_r |= (X##_r | Y##_r); \ - if (_FP_IS_NAN(fs, Y)) { \ - R##_s = Y##_s; \ - R##_c = FP_CLS_NAN; \ - if (_FP_IS_QNAN(fs, Y)) { /* Rule 1 */ \ - _FP_FRAC_COPY_##wc(R,Y); \ - } else { /* Rule 2 */ \ - _FP_FRAC_SET_##wc(R,Y##_f | _FP_QNANBIT_##fs); \ - R##_r = EFLAG_INVALID; \ - } \ - } else if (_FP_IS_NAN(fs, X)) { \ - R##_s = X##_s; \ - R##_c = FP_CLS_NAN; \ - if (_FP_IS_QNAN(fs, X)) { /* Rule 3 */ \ - _FP_FRAC_COPY_##wc(R,X); \ - } else { /* Rule 4 */ \ - _FP_FRAC_SET_##wc(R,X##_f | _FP_QNANBIT_##fs); \ - R##_r |= EFLAG_INVALID; \ - } \ - } else { /* Rule 5 */ \ - R##_s = 1; \ - R##_c = FP_CLS_NAN; \ - _FP_FRAC_SET_##wc(R,_FP_QNANBIT_##fs | EFLAG_MASK); \ - } \ - } while(0) - -/* Rules 3 and 4 don't apply to functions of only one argument */ -#define _FP_CHOOSENAN_1(fs, wc, R, X) \ +/* Alpha Architecture Handbook, 4.7.10.4 sais that + * we should prefer any type of NaN in Fb, then Fa. + */ +#define _FP_CHOOSENAN(fs, wc, R, X, Y, OP) \ do { \ - if (_FP_IS_NAN(fs, X)) { \ - R##_s = X##_s; \ - R##_c = FP_CLS_NAN; \ - if (_FP_IS_QNAN(fs, X)) { /* Rule 1 */ \ - _FP_FRAC_COPY_##wc(R,X); \ - } else { /* Rule 2 */ \ - _FP_FRAC_SET_##wc(R,X##_f | _FP_QNANBIT_##fs); \ - R##_r |= EFLAG_INVALID; \ - } \ - } else { /* Rule 5 */ \ - R##_s = 1; \ - R##_c = FP_CLS_NAN; \ - _FP_FRAC_SET_##wc(R,_FP_QNANBIT_##fs | EFLAG_MASK); \ - } \ - } while(0) - -#define _FP_CHOOSENAN_SQRT _FP_CHOOSENAN_1 - - -#define __FP_UNPACK_DENORM(fs, wc, X) \ - do { \ - _FP_I_TYPE _shift; \ - X##_r |= EFLAG_DENORM; \ - if (_FP_DENORM_TO_ZERO) { \ - /* Crunching a nonzero denorm to zero necessarily makes */ \ - /* the result inexact */ \ - X##_r |= EFLAG_INEXACT; \ - _FP_FRAC_SET_##wc(X, 0); \ - X##_c = FP_CLS_ZERO; \ - } else { \ - _FP_FRAC_CLZ_##wc(_shift, X); \ - _shift -= _FP_FRACXBITS_##fs; \ - _FP_FRAC_SLL_##wc(X, (_shift+_FP_WORKBITS)); \ - X##_e -= _FP_EXPBIAS_##fs - 1 + _shift; \ - X##_c = FP_CLS_NORMAL; \ - } \ - } while (0) - -#define __FP_UNPACK_RAW_1(fs, X, val) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - X##_f = _flo->bits.frac; \ - X##_e = _flo->bits.exp; \ - X##_s = _flo->bits.sign; \ - } while (0) - -#define __FP_UNPACK_RAW_2(fs, X, val) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - X##_f0 = _flo->bits.frac0; \ - X##_f1 = _flo->bits.frac1; \ - X##_e = _flo->bits.exp; \ - X##_s = _flo->bits.sign; \ - } while (0) - -#define __FP_UNPACK_S(X,val) \ - do { \ - __FP_UNPACK_RAW_1(S,X,val); \ - _FP_UNPACK_CANONICAL(S,1,X); \ - } while (0) - -#define __FP_UNPACK_D(X,val) \ - do { \ - __FP_UNPACK_RAW_1(D,X,val); \ - _FP_UNPACK_CANONICAL(D,1,X); \ + R##_s = Y##_s; \ + _FP_FRAC_COPY_##wc(R,X); \ + R##_c = FP_CLS_NAN; \ } while (0) -#define __FP_UNPACK_Q(X,val) \ - do { \ - __FP_UNPACK_RAW_2(Q,X,val); \ - _FP_UNPACK_CANONICAL(Q,2,X); \ - } while (0) - -#define __FP_PACK_RAW_1(fs, val, X) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - _flo->bits.frac = X##_f; \ - _flo->bits.exp = X##_e; \ - _flo->bits.sign = X##_s; \ - } while (0) - -#define __FP_PACK_RAW_2(fs, val, X) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - _flo->bits.frac0 = X##_f0; \ - _flo->bits.frac1 = X##_f1; \ - _flo->bits.exp = X##_e; \ - _flo->bits.sign = X##_s; \ - } while (0) - - -/* Alpha rules for handling certain exceptional cases are different - * enough that we simply define our own versions here to override - * the ones in op-common.h - */ +/* Obtain the current rounding mode. */ +#define FP_ROUNDMODE mode +#define FP_RND_NEAREST (FPCR_DYN_NORMAL >> FPCR_DYN_SHIFT) +#define FP_RND_ZERO (FPCR_DYN_CHOPPED >> FPCR_DYN_SHIFT) +#define FP_RND_PINF (FPCR_DYN_PLUS >> FPCR_DYN_SHIFT) +#define FP_RND_MINF (FPCR_DYN_MINUS >> FPCR_DYN_SHIFT) -#define _FP_ADD(fs, wc, R, X, Y) \ -do { \ - /* Propagate any flags that may have been set during unpacking */ \ - R##_r |= (X##_r | Y##_r); \ - switch (_FP_CLS_COMBINE(X##_c, Y##_c)) \ - { \ - case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NORMAL): \ - { \ - /* shift the smaller number so that its exponent matches the larger */ \ - _FP_I_TYPE diff = X##_e - Y##_e; \ - \ - if (diff < 0) \ - { \ - diff = -diff; \ - if (diff <= _FP_WFRACBITS_##fs) \ - _FP_FRAC_SRS_##wc(X, diff, _FP_WFRACBITS_##fs); \ - else if (!_FP_FRAC_ZEROP_##wc(X)) { \ - _FP_FRAC_SET_##wc(X, _FP_MINFRAC_##wc); \ - R##_r |= EFLAG_INEXACT; \ - } \ - else \ - _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ - R##_e = Y##_e; \ - } \ - else \ - { \ - if (diff > 0) \ - { \ - if (diff <= _FP_WFRACBITS_##fs) \ - _FP_FRAC_SRS_##wc(Y, diff, _FP_WFRACBITS_##fs); \ - else if (!_FP_FRAC_ZEROP_##wc(Y)) { \ - _FP_FRAC_SET_##wc(Y, _FP_MINFRAC_##wc); \ - R##_r |= EFLAG_INEXACT; \ - } \ - else \ - _FP_FRAC_SET_##wc(Y, _FP_ZEROFRAC_##wc); \ - } \ - R##_e = X##_e; \ - } \ - \ - R##_c = FP_CLS_NORMAL; \ - \ - if (X##_s == Y##_s) \ - { \ - R##_s = X##_s; \ - _FP_FRAC_ADD_##wc(R, X, Y); \ - if (_FP_FRAC_OVERP_##wc(fs, R)) \ - { \ - _FP_FRAC_SRS_##wc(R, 1, _FP_WFRACBITS_##fs); \ - R##_e++; \ - } \ - } \ - else \ - { \ - R##_s = X##_s; \ - _FP_FRAC_SUB_##wc(R, X, Y); \ - if (_FP_FRAC_ZEROP_##wc(R)) \ - { \ - /* return an exact zero */ \ - if (FP_ROUNDMODE == FP_RND_MINF) \ - R##_s |= Y##_s; \ - else \ - R##_s &= Y##_s; \ - R##_c = FP_CLS_ZERO; \ - } \ - else \ - { \ - if (_FP_FRAC_NEGP_##wc(R)) \ - { \ - _FP_FRAC_SUB_##wc(R, Y, X); \ - R##_s = Y##_s; \ - } \ - \ - /* renormalize after subtraction */ \ - _FP_FRAC_CLZ_##wc(diff, R); \ - diff -= _FP_WFRACXBITS_##fs; \ - if (diff) \ - { \ - R##_e -= diff; \ - _FP_FRAC_SLL_##wc(R, diff); \ - } \ - } \ - } \ - break; \ - } \ - \ - case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NAN): \ - case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NORMAL): \ - case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_INF): \ - case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_ZERO): \ - case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NAN): \ - case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NAN): \ - case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NAN): \ - _FP_CHOOSENAN(fs, wc, R, X, Y); \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_ZERO): \ - R##_e = X##_e; \ - _FP_FRAC_COPY_##wc(R, X); \ - R##_s = X##_s; \ - R##_c = X##_c; \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NORMAL): \ - R##_e = Y##_e; \ - _FP_FRAC_COPY_##wc(R, Y); \ - R##_s = Y##_s; \ - R##_c = Y##_c; \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_INF): \ - if (X##_s != Y##_s) \ - { \ - /* +INF + -INF => NAN */ \ - _FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs); \ - R##_s = X##_s ^ Y##_s; \ - R##_c = FP_CLS_NAN; \ - R##_r |= EFLAG_INVALID; \ - break; \ - } \ - /* FALLTHRU */ \ - \ - case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NORMAL): \ - case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_ZERO): \ - R##_s = X##_s; \ - R##_c = FP_CLS_INF; \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_INF): \ - case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_INF): \ - R##_s = Y##_s; \ - R##_c = FP_CLS_INF; \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_ZERO): \ - /* make sure the sign is correct */ \ - if (FP_ROUNDMODE == FP_RND_MINF) \ - R##_s = X##_s | Y##_s; \ - else \ - R##_s = X##_s & Y##_s; \ - R##_c = FP_CLS_ZERO; \ - break; \ - \ - default: \ - abort(); \ - } \ -} while (0) - - -#define _FP_MUL(fs, wc, R, X, Y) \ -do { \ - /* Propagate any flags that may have been set during unpacking */ \ - R##_r |= (X##_r | Y##_r); \ - R##_s = X##_s ^ Y##_s; \ - switch (_FP_CLS_COMBINE(X##_c, Y##_c)) \ - { \ - case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NORMAL): \ - R##_c = FP_CLS_NORMAL; \ - R##_e = X##_e + Y##_e + 1; \ - \ - _FP_MUL_MEAT_##fs(R,X,Y); \ - \ - if (_FP_FRAC_OVERP_##wc(fs, R)) \ - _FP_FRAC_SRS_##wc(R, 1, _FP_WFRACBITS_##fs); \ - else \ - R##_e--; \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NORMAL): \ - case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_INF): \ - case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_ZERO): \ - case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NAN): \ - case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NAN): \ - case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NAN): \ - case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NAN): \ - _FP_CHOOSENAN(fs, wc, R, X, Y); \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_INF): \ - case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NORMAL): \ - case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NORMAL): \ - case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_ZERO): \ - _FP_FRAC_COPY_##wc(R, X); \ - R##_c = X##_c; \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_INF): \ - case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_ZERO): \ - _FP_FRAC_COPY_##wc(R, Y); \ - R##_c = Y##_c; \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_ZERO): \ - case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_INF): \ - R##_c = FP_CLS_NAN; \ - _FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs); \ - R##_s = 1; /* Alpha SRM rule */ \ - break; \ - \ - default: \ - abort(); \ - } \ -} while (0) - - -#define _FP_DIV(fs, wc, R, X, Y) \ -do { \ - /* Propagate any flags that may have been set during unpacking */ \ - R##_r |= (X##_r | Y##_r); \ - R##_s = X##_s ^ Y##_s; \ - switch (_FP_CLS_COMBINE(X##_c, Y##_c)) \ - { \ - case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NORMAL): \ - R##_c = FP_CLS_NORMAL; \ - R##_e = X##_e - Y##_e; \ - \ - _FP_DIV_MEAT_##fs(R,X,Y); \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NORMAL): \ - case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_INF): \ - case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_ZERO): \ - case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NAN): \ - case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NAN): \ - case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NAN): \ - case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NAN): \ - _FP_CHOOSENAN(fs, wc, R, X, Y); \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_INF): \ - case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_INF): \ - case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NORMAL): \ - R##_c = FP_CLS_ZERO; \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_ZERO): \ - case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_ZERO): \ - case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NORMAL): \ - R##_c = FP_CLS_INF; \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_INF): \ - case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_ZERO): \ - R##_c = FP_CLS_NAN; \ - _FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs); \ - R##_s = 1; /* Alpha SRM rule */ \ - break; \ - \ - default: \ - abort(); \ - } \ -} while (0) - - -#define _FP_TO_INT(fs, wc, r, X, rsize, rsigned) \ - ({ \ - switch (X##_c) \ - { \ - case FP_CLS_ZERO: \ - r = 0; \ - break; \ - case FP_CLS_NAN: \ - r = 0; \ - X##_r |= EFLAG_INVALID; \ - break; \ - case FP_CLS_INF: \ - r = 0; \ - X##_r |= (EFLAG_INVALID | EFLAG_INEXACT); \ - break; \ - case FP_CLS_NORMAL: \ - if (X##_e < 0) \ - { \ - r = 0; \ - X##_r |= EFLAG_INEXACT; \ - } \ - else \ - { \ - if (X##_e >= rsize - (rsigned != 0)) { \ - /* Overflow. On alpha, set the INV bit and proceed */ \ - /* JRP - I *believe* the proper behavior is to set */ \ - /* INV and write a true zero... need to check */ \ - X##_r |= EFLAG_INVALID; \ - r = 0; \ - break; \ - } \ - if (_FP_W_TYPE_SIZE*wc < rsize) \ - { \ - _FP_FRAC_ASSEMBLE_##wc(r, X, rsize); \ - r <<= X##_e - _FP_WFRACBITS_##fs; \ - } \ - else \ - { \ - if (X##_e >= _FP_WFRACBITS_##fs) \ - _FP_FRAC_SLL_##wc(X, (X##_e - _FP_WFRACBITS_##fs + 1)); \ - else \ - _FP_FRAC_SRL_##wc(X, (_FP_WFRACBITS_##fs - X##_e - 1)); \ - _FP_FRAC_ASSEMBLE_##wc(r, X, rsize); \ - } \ - if (rsigned && X##_s) \ - r = -r; \ - } \ - break; \ - } \ - X##_r; \ - }) - - -/* We only actually write to the destination register if exceptions - signalled (if any) will not trap. */ -#define __FPU_TEM (current->tss.flags & IEEE_TRAP_ENABLE_MASK) -#define __FPU_TRAP_P(bits) ((__FPU_TEM & (bits)) != 0) - -#define __FP_PACK_S(val,X) \ -({ int __exc = _FP_PACK_CANONICAL(S,1,X); \ - if (!__exc || !__FPU_TRAP_P(__exc)) \ - __FP_PACK_RAW_1(S,val,X); \ - __exc; \ -}) - -#define __FP_PACK_D(val,X) \ -({ int __exc = _FP_PACK_CANONICAL(D,1,X); \ - if(!__exc || !__FPU_TRAP_P(__exc)) \ - __FP_PACK_RAW_1(D,val,X); \ - __exc; \ -}) - -#define __FP_PACK_Q(val,X) \ -({ int __exc = _FP_PACK_CANONICAL(Q,2,X); \ - if(!__exc || !__FPU_TRAP_P(__exc)) \ - __FP_PACK_RAW_2(Q,val,X); \ - __exc; \ -}) +/* Exception flags. */ +#define FP_EX_INVALID IEEE_TRAP_ENABLE_INV +#define FP_EX_OVERFLOW IEEE_TRAP_ENABLE_OVF +#define FP_EX_UNDERFLOW IEEE_TRAP_ENABLE_UNF +#define FP_EX_DIVZERO IEEE_TRAP_ENABLE_DZE +#define FP_EX_INEXACT IEEE_TRAP_ENABLE_INE +#define FP_EX_DENORM IEEE_TRAP_ENABLE_DNO -/* Obtain the current rounding mode. */ -#define FP_ROUNDMODE mode -#define FP_RND_NEAREST (FPCR_DYN_NORMAL >> FPCR_DYN_SHIFT) -#define FP_RND_ZERO (FPCR_DYN_CHOPPED >> FPCR_DYN_SHIFT) -#define FP_RND_PINF (FPCR_DYN_PLUS >> FPCR_DYN_SHIFT) -#define FP_RND_MINF (FPCR_DYN_MINUS >> FPCR_DYN_SHIFT) - - -#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ - ((sl) = (al) + (bl), (sh) = (ah) + (bh) + ((sl) < (al))) - -#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ - ((sl) = (al) - (bl), (sh) = (ah) - (bh) - ((al) < (bl))) - -#define umul_ppmm(wh, wl, u, v) \ - __asm__ ("mulq %2,%3,%1; umulh %2,%3,%0" \ - : "=r" ((UDItype)(wh)), \ - "=&r" ((UDItype)(wl)) \ - : "r" ((UDItype)(u)), \ - "r" ((UDItype)(v))) - -extern __complex__ unsigned long udiv128(unsigned long, unsigned long, - unsigned long, unsigned long); - -#define udiv_qrnnd(q, r, n1, n0, d) \ - do { \ - __complex__ unsigned long x_; \ - x_ = udiv128((n0), (n1), 0, (d)); \ - (q) = __real__ x_; \ - (r) = __imag__ x_; \ - } while (0) +#define FP_DENORM_ZERO (fpcw & IEEE_MAP_DMZ) -#define UDIV_NEEDS_NORMALIZATION 1 +#define FP_HANDLE_EXCEPTIONS return _fex -#define abort() goto bad_insn +/* We write the results always */ +#define FP_INHIBIT_RESULTS 0 -#ifndef __LITTLE_ENDIAN -#define __LITTLE_ENDIAN -1 #endif -#define __BYTE_ORDER __LITTLE_ENDIAN - -/* Exception flags. */ -#define EFLAG_INVALID IEEE_TRAP_ENABLE_INV -#define EFLAG_OVERFLOW IEEE_TRAP_ENABLE_OVF -#define EFLAG_UNDERFLOW IEEE_TRAP_ENABLE_UNF -#define EFLAG_DIVZERO IEEE_TRAP_ENABLE_DZE -#define EFLAG_INEXACT IEEE_TRAP_ENABLE_INE -#define EFLAG_DENORM IEEE_TRAP_ENABLE_DNO -#define EFLAG_MASK IEEE_TRAP_ENABLE_MASK - -#define _FP_DENORM_TO_ZERO ((current->tss.flags) & IEEE_MAP_DMZ) - -/* Comparison operations */ -#define CMPTXX_EQ 0 -#define CMPTXX_LT -1 -#define CMPTXX_GT 1 -#define CMPTXX_LE 2 -#define CMPTXX_UN 3 diff -u --recursive --new-file v2.2.13/linux/arch/alpha/math-emu/sfp-util.h linux/arch/alpha/math-emu/sfp-util.h --- v2.2.13/linux/arch/alpha/math-emu/sfp-util.h Wed Dec 31 16:00:00 1969 +++ linux/arch/alpha/math-emu/sfp-util.h Tue Jan 4 10:12:11 2000 @@ -0,0 +1,40 @@ +#include +#include +#include +#include +#include + +#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ + ((sl) = (al) + (bl), (sh) = (ah) + (bh) + ((sl) < (al))) + +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + ((sl) = (al) - (bl), (sh) = (ah) - (bh) - ((al) < (bl))) + +#define umul_ppmm(wh, wl, u, v) \ + __asm__ ("mulq %2,%3,%1; umulh %2,%3,%0" \ + : "=r" ((UDItype)(wh)), \ + "=&r" ((UDItype)(wl)) \ + : "r" ((UDItype)(u)), \ + "r" ((UDItype)(v))) + +extern void udiv128(unsigned long, unsigned long, + unsigned long, unsigned long, + unsigned long *, + unsigned long *); + +#define udiv_qrnnd(q, r, n1, n0, d) \ + do { \ + unsigned long xr, xi; \ + udiv128((n0), (n1), 0, (d), &xr, &xi); \ + (q) = xr; \ + (r) = xi; \ + } while (0) + +#define UDIV_NEEDS_NORMALIZATION 1 + +#define abort() goto bad_insn + +#ifndef __LITTLE_ENDIAN +#define __LITTLE_ENDIAN -1 +#endif +#define __BYTE_ORDER __LITTLE_ENDIAN diff -u --recursive --new-file v2.2.13/linux/arch/alpha/mm/fault.c linux/arch/alpha/mm/fault.c --- v2.2.13/linux/arch/alpha/mm/fault.c Mon Aug 9 16:05:54 1999 +++ linux/arch/alpha/mm/fault.c Tue Jan 4 10:12:11 2000 @@ -120,9 +120,18 @@ if (!(vma->vm_flags & VM_WRITE)) goto bad_area; } - handle_mm_fault(current, vma, address, cause > 0); +survive: + { + int fault = handle_mm_fault(current, vma, address, cause > 0); + if (!fault) + goto do_sigbus; + if (fault < 0) + goto out_of_memory; + } up(&mm->mmap_sem); - goto out; + out_unlock: + unlock_kernel(); + return; /* * Something tried to access memory that isn't in our memory map.. @@ -133,9 +142,10 @@ if (user_mode(regs)) { force_sig(SIGSEGV, current); - goto out; + goto out_unlock; } +no_context: /* Are we prepared to handle this fault as an exception? */ if ((fixup = search_exception_table(regs->pc)) != 0) { unsigned long newpc; @@ -143,7 +153,7 @@ printk("%s: Exception at [<%lx>] (%lx)\n", current->comm, regs->pc, newpc); regs->pc = newpc; - goto out; + goto out_unlock; } /* @@ -154,7 +164,37 @@ "virtual address %016lx\n", address); die_if_kernel("Oops", regs, cause, (unsigned long*)regs - 16); do_exit(SIGKILL); - out: - unlock_kernel(); -} +/* + * We ran out of memory, or some other thing happened to us that made + * us unable to handle the page fault gracefully. + */ +out_of_memory: + if (current->pid == 1) + { + current->policy |= SCHED_YIELD; + schedule(); + goto survive; + } + up(&mm->mmap_sem); + if (user_mode(regs)) + { + printk("VM: killing process %s\n", current->comm); + do_exit(SIGKILL); + } + goto no_context; + +do_sigbus: + up(&mm->mmap_sem); + + /* + * Send a sigbus, regardless of whether we were in kernel + * or user mode. + */ + force_sig(SIGBUS, current); + + /* Kernel mode? Handle exceptions or die */ + if (!user_mode(regs)) + goto no_context; + goto out_unlock; +} diff -u --recursive --new-file v2.2.13/linux/arch/alpha/mm/init.c linux/arch/alpha/mm/init.c --- v2.2.13/linux/arch/alpha/mm/init.c Mon Aug 9 16:05:54 1999 +++ linux/arch/alpha/mm/init.c Tue Jan 4 10:12:11 2000 @@ -179,7 +179,7 @@ { register unsigned long sp __asm__("$30"); pcb->ksp = sp; - return __reload_tss(pcb); + return (struct thread_struct *) __reload_tss(pcb); } /* @@ -211,7 +211,11 @@ if (cluster->usage & 3) continue; pfn = cluster->start_pfn; + if (pfn >= MAP_NR(end_mem)) /* if we overrode mem size */ + continue; nr = cluster->numpages; + if ((pfn + nr) > MAP_NR(end_mem)) /* if override in cluster */ + nr = MAP_NR(end_mem) - pfn; while (nr--) clear_bit(PG_reserved, &mem_map[pfn++].flags); @@ -221,7 +225,7 @@ the last slot of the L1 page table. */ memset((void *) ZERO_PAGE(0), 0, PAGE_SIZE); memset(swapper_pg_dir, 0, PAGE_SIZE); - newptbr = ((unsigned long) swapper_pg_dir - PAGE_OFFSET) >> PAGE_SHIFT; + newptbr = MAP_NR(swapper_pg_dir); pgd_val(swapper_pg_dir[1023]) = (newptbr << 32) | pgprot_val(PAGE_KERNEL); @@ -328,8 +332,8 @@ kill_page(tmp); free_page(tmp); } - tmp = nr_free_pages << PAGE_SHIFT; - printk("Memory: %luk available\n", tmp >> 10); + tmp = nr_free_pages << (PAGE_SHIFT - 10); + printk("Memory: %luk available\n", tmp); return; } @@ -358,7 +362,7 @@ i = max_mapnr; val->totalram = 0; val->sharedram = 0; - val->freeram = nr_free_pages << PAGE_SHIFT; + val->freeram = ((unsigned long)nr_free_pages) << PAGE_SHIFT; val->bufferram = buffermem; while (i-- > 0) { if (PageReserved(mem_map+i)) diff -u --recursive --new-file v2.2.13/linux/arch/i386/boot/compressed/Makefile linux/arch/i386/boot/compressed/Makefile --- v2.2.13/linux/arch/i386/boot/compressed/Makefile Tue Jan 4 11:10:31 2000 +++ linux/arch/i386/boot/compressed/Makefile Tue Jan 4 10:12:11 2000 @@ -37,7 +37,7 @@ tmppiggy=_tmp_$$$$piggy; \ rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk; \ $(OBJCOPY) $(SYSTEM) $$tmppiggy; \ - gzip -f -3 < $$tmppiggy > $$tmppiggy.gz; \ + gzip -f -9 < $$tmppiggy > $$tmppiggy.gz; \ echo "SECTIONS { .data : { input_len = .; LONG(input_data_end - input_data) input_data = .; *(.data) input_data_end = .; }}" > $$tmppiggy.lnk; \ $(LD) -m elf_i386 -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-i386 -T $$tmppiggy.lnk; \ rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk diff -u --recursive --new-file v2.2.13/linux/arch/i386/boot/compressed/misc.c linux/arch/i386/boot/compressed/misc.c --- v2.2.13/linux/arch/i386/boot/compressed/misc.c Wed Jun 24 14:30:08 1998 +++ linux/arch/i386/boot/compressed/misc.c Tue Jan 4 10:12:11 2000 @@ -104,7 +104,7 @@ #define LOW_BUFFER_START 0x2000 #define LOW_BUFFER_END 0x90000 #define LOW_BUFFER_SIZE ( LOW_BUFFER_END - LOW_BUFFER_START ) -#define HEAP_SIZE 0x2000 +#define HEAP_SIZE 0x2400 static int high_loaded =0; static uch *high_buffer_start /* = (uch *)(((ulg)&end) + HEAP_SIZE)*/; diff -u --recursive --new-file v2.2.13/linux/arch/i386/config.in linux/arch/i386/config.in --- v2.2.13/linux/arch/i386/config.in Mon Aug 9 16:05:54 1999 +++ linux/arch/i386/config.in Tue Jan 4 10:12:11 2000 @@ -126,6 +126,8 @@ source net/Config.in fi +source drivers/telephony/Config.in + mainmenu_option next_comment comment 'SCSI support' diff -u --recursive --new-file v2.2.13/linux/arch/i386/kernel/apm.c linux/arch/i386/kernel/apm.c --- v2.2.13/linux/arch/i386/kernel/apm.c Tue Jan 4 11:10:31 2000 +++ linux/arch/i386/kernel/apm.c Tue Jan 4 10:12:11 2000 @@ -1,8 +1,8 @@ /* -*- linux-c -*- * APM BIOS driver for Linux - * Copyright 1994-1998 Stephen Rothwell - * (Stephen.Rothwell@canb.auug.org.au) - * Development of this driver was funded by NEC Australia P/L + * Copyright 1994-1999 Stephen Rothwell (sfr@linuxcare.com) + * + * Initial development of this driver was funded by NEC Australia P/L * and NEC Corporation * * This program is free software; you can redistribute it and/or modify it diff -u --recursive --new-file v2.2.13/linux/arch/i386/kernel/bios32.c linux/arch/i386/kernel/bios32.c --- v2.2.13/linux/arch/i386/kernel/bios32.c Sat Apr 24 17:49:37 1999 +++ linux/arch/i386/kernel/bios32.c Tue Jan 4 10:12:11 2000 @@ -1095,7 +1095,7 @@ * for buggy PCI BIOS'es :-[). */ -extern int skip_ioapic_setup; +extern int skip_ioapic_setup; /* defined in arch/i386/kernel/smp.c */ static void __init pcibios_fixup_devices(void) { diff -u --recursive --new-file v2.2.13/linux/arch/i386/kernel/io_apic.c linux/arch/i386/kernel/io_apic.c --- v2.2.13/linux/arch/i386/kernel/io_apic.c Tue Jan 4 11:10:31 2000 +++ linux/arch/i386/kernel/io_apic.c Tue Jan 4 10:12:11 2000 @@ -19,7 +19,7 @@ * volatile is justified in this case, IO-APIC register contents * might change spontaneously, GCC should not cache it */ -#define IO_APIC_BASE ((volatile int *)fix_to_virt(FIX_IO_APIC_BASE)) +#define IO_APIC_BASE(idx) ((volatile int *)__fix_to_virt(FIX_IO_APIC_BASE_0 + idx)) /* * The structure of the IO-APIC: @@ -47,7 +47,7 @@ /* * # of IRQ routing registers */ -int nr_ioapic_registers = 0; +int nr_ioapic_registers[MAX_IO_APICS]; enum ioapic_irq_destination_types { dest_Fixed = 0, @@ -94,6 +94,8 @@ mp_ExtINT = 3 }; +int mp_apic_entries = 0; /* # of I/O APIC entries */ +struct mpc_config_ioapic mp_apics[MAX_IO_APICS];/* I/O APIC entries */ int mp_irq_entries = 0; /* # of MP IRQ source entries */ struct mpc_config_intsrc mp_irqs[MAX_IRQ_SOURCES]; /* MP IRQ source entries */ @@ -108,34 +110,34 @@ * between pins and IRQs. */ -static inline unsigned int io_apic_read(unsigned int reg) +static inline unsigned int io_apic_read(unsigned int apic, unsigned int reg) { - *IO_APIC_BASE = reg; - return *(IO_APIC_BASE+4); + *IO_APIC_BASE(apic) = reg; + return *(IO_APIC_BASE(apic)+4); } -static inline void io_apic_write(unsigned int reg, unsigned int value) +static inline void io_apic_write(unsigned int apic, unsigned int reg, unsigned int value) { - *IO_APIC_BASE = reg; - *(IO_APIC_BASE+4) = value; + *IO_APIC_BASE(apic) = reg; + *(IO_APIC_BASE(apic)+4) = value; } /* * Re-write a value: to be used for read-modify-write * cycles where the read already set up the index register. */ -static inline void io_apic_modify(unsigned int value) +static inline void io_apic_modify(unsigned int apic, unsigned int value) { - *(IO_APIC_BASE+4) = value; + *(IO_APIC_BASE(apic)+4) = value; } /* * Synchronize the IO-APIC and the CPU by doing * a dummy read from the IO-APIC */ -static inline void io_apic_sync(void) +static inline void io_apic_sync(unsigned int apic) { - (void) *(IO_APIC_BASE+4); + (void) *(IO_APIC_BASE(apic)+4); } /* @@ -146,7 +148,7 @@ #define PIN_MAP_SIZE (MAX_PLUS_SHARED_IRQS + NR_IRQS) static struct irq_pin_list { - int pin, next; + int apic, pin, next; } irq_2_pin[PIN_MAP_SIZE]; /* @@ -154,7 +156,7 @@ * shared ISA-space IRQs, so we have to support them. We are super * fast in the common case, and fast for shared ISA-space IRQs. */ -static void add_pin_to_irq(unsigned int irq, int pin) +static void add_pin_to_irq(unsigned int irq, int apic, int pin) { static int first_free_entry = NR_IRQS; struct irq_pin_list *entry = irq_2_pin + irq; @@ -168,6 +170,7 @@ if (++first_free_entry >= PIN_MAP_SIZE) panic("io_apic.c: whoops"); } + entry->apic = apic; entry->pin = pin; } @@ -183,9 +186,9 @@ pin = entry->pin; \ if (pin == -1) \ break; \ - reg = io_apic_read(0x10 + R + pin*2); \ + reg = io_apic_read(entry->apic, 0x10 + R + pin*2); \ reg ACTION; \ - io_apic_modify(reg); \ + io_apic_modify(entry->apic, reg); \ if (!entry->next) \ break; \ entry = irq_2_pin + entry->next; \ @@ -197,12 +200,12 @@ * We disable IO-APIC IRQs by setting their 'destination CPU mask' to * zero. Trick by Ramesh Nalluri. */ -DO_ACTION( disable, 1, &= 0x00ffffff, io_apic_sync()) /* destination = 0x00 */ +DO_ACTION( disable, 1, &= 0x00ffffff, io_apic_sync(entry->apic))/* destination = 0x00 */ DO_ACTION( enable, 1, |= 0xff000000, ) /* destination = 0xff */ -DO_ACTION( mask, 0, |= 0x00010000, io_apic_sync()) /* mask = 1 */ +DO_ACTION( mask, 0, |= 0x00010000, io_apic_sync(entry->apic))/* mask = 1 */ DO_ACTION( unmask, 0, &= 0xfffeffff, ) /* mask = 0 */ -static void clear_IO_APIC_pin(unsigned int pin) +static void clear_IO_APIC_pin(unsigned int apic, unsigned int pin) { struct IO_APIC_route_entry entry; @@ -211,16 +214,17 @@ */ memset(&entry, 0, sizeof(entry)); entry.mask = 1; - io_apic_write(0x10 + 2 * pin, *(((int *)&entry) + 0)); - io_apic_write(0x11 + 2 * pin, *(((int *)&entry) + 1)); + io_apic_write(apic, 0x10 + 2 * pin, *(((int *)&entry) + 0)); + io_apic_write(apic, 0x11 + 2 * pin, *(((int *)&entry) + 1)); } static void clear_IO_APIC (void) { - int pin; + int apic, pin; - for (pin = 0; pin < nr_ioapic_registers; pin++) - clear_IO_APIC_pin(pin); + for (apic = 0; apic < mp_apic_entries; apic++) + for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) + clear_IO_APIC_pin(apic, pin); } /* @@ -270,12 +274,13 @@ /* * Find the IRQ entry number of a certain pin. */ -static int __init find_irq_entry(int pin, int type) +static int __init find_irq_entry(int apic, int pin, int type) { int i; for (i = 0; i < mp_irq_entries; i++) if ( (mp_irqs[i].mpc_irqtype == type) && + (mp_irqs[i].mpc_dstapic == mp_apics[apic].mpc_apicid) && (mp_irqs[i].mpc_dstirq == pin)) return i; @@ -307,21 +312,26 @@ * Find a specific PCI IRQ entry. * Not an initfunc, possibly needed by modules */ +static int __init pin_2_irq(int idx, int apic, int pin); int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pci_pin) { - int i; + int apic, i; for (i = 0; i < mp_irq_entries; i++) { int lbus = mp_irqs[i].mpc_srcbus; - if (IO_APIC_IRQ(mp_irqs[i].mpc_dstirq) && + for (apic = 0; apic < mp_apic_entries; apic++) + if (mp_apics[apic].mpc_apicid == mp_irqs[i].mpc_dstapic) + break; + + if ((apic || IO_APIC_IRQ(mp_irqs[i].mpc_dstirq)) && (mp_bus_id_to_type[lbus] == MP_BUS_PCI) && !mp_irqs[i].mpc_irqtype && (bus == mp_bus_id_to_pci_bus[mp_irqs[i].mpc_srcbus]) && (slot == ((mp_irqs[i].mpc_srcbusirq >> 2) & 0x1f)) && (pci_pin == (mp_irqs[i].mpc_srcbusirq & 3))) - return mp_irqs[i].mpc_dstirq; + return pin_2_irq(i,apic,mp_irqs[i].mpc_dstirq); } return -1; } @@ -491,9 +501,9 @@ return MPBIOS_trigger(idx); } -static int __init pin_2_irq(int idx, int pin) +static int __init pin_2_irq(int idx, int apic, int pin) { - int irq; + int irq, i; int bus = mp_irqs[idx].mpc_srcbus; /* @@ -513,9 +523,12 @@ case MP_BUS_PCI: /* PCI pin */ { /* - * PCI IRQs are 'directly mapped' + * PCI IRQs are mapped in order */ - irq = pin; + i = irq = 0; + while (i < apic) + irq += nr_ioapic_registers[i++]; + irq += pin; break; } default: @@ -545,12 +558,14 @@ static inline int IO_APIC_irq_trigger(int irq) { - int idx, pin; + int apic, idx, pin; - for (pin = 0; pin < nr_ioapic_registers; pin++) { - idx = find_irq_entry(pin,mp_INT); - if ((idx != -1) && (irq == pin_2_irq(idx,pin))) - return irq_trigger(idx); + for (apic = 0; apic < mp_apic_entries; apic++) { + for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) { + idx = find_irq_entry(apic,pin,mp_INT); + if ((idx != -1) && (irq == pin_2_irq(idx,apic,pin))) + return irq_trigger(idx); + } } /* * nonexistent IRQs are edge default @@ -582,11 +597,12 @@ void __init setup_IO_APIC_irqs(void) { struct IO_APIC_route_entry entry; - int pin, idx, bus, irq, first_notcon = 1; + int apic, pin, idx, irq, first_notcon = 1; printk("init IO_APIC IRQs\n"); - for (pin = 0; pin < nr_ioapic_registers; pin++) { + for (apic = 0; apic < mp_apic_entries; apic++) { + for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) { /* * add it to the IO-APIC irq-routing table: @@ -598,13 +614,13 @@ entry.mask = 0; /* enable IRQ */ entry.dest.logical.logical_dest = 0; /* but no route */ - idx = find_irq_entry(pin,mp_INT); + idx = find_irq_entry(apic,pin,mp_INT); if (idx == -1) { if (first_notcon) { - printk(" IO-APIC pin %d", pin); + printk(" IO-APIC (apicid-pin) %d-%d", mp_apics[apic].mpc_apicid, pin); first_notcon = 0; } else - printk(", %d", pin); + printk(", %d-%d", mp_apics[apic].mpc_apicid, pin); continue; } @@ -617,18 +633,17 @@ entry.dest.logical.logical_dest = 0xff; } - irq = pin_2_irq(idx,pin); - add_pin_to_irq(irq, pin); + irq = pin_2_irq(idx,apic,pin); + add_pin_to_irq(irq, apic, pin); - if (!IO_APIC_IRQ(irq)) + if (!apic && !IO_APIC_IRQ(irq)) continue; entry.vector = assign_irq_vector(irq); - bus = mp_irqs[idx].mpc_srcbus; - - io_apic_write(0x11+2*pin, *(((int *)&entry)+1)); - io_apic_write(0x10+2*pin, *(((int *)&entry)+0)); + io_apic_write(apic, 0x11+2*pin, *(((int *)&entry)+1)); + io_apic_write(apic, 0x10+2*pin, *(((int *)&entry)+0)); + } } if (!first_notcon) @@ -638,7 +653,7 @@ /* * Set up a certain pin as ExtINT delivered interrupt */ -void __init setup_ExtINT_pin(unsigned int pin, int irq) +void __init setup_ExtINT_pin(unsigned int apic, unsigned int pin, int irq) { struct IO_APIC_route_entry entry; @@ -662,8 +677,8 @@ entry.polarity = 0; entry.trigger = 0; - io_apic_write(0x10+2*pin, *(((int *)&entry)+0)); - io_apic_write(0x11+2*pin, *(((int *)&entry)+1)); + io_apic_write(apic, 0x10+2*pin, *(((int *)&entry)+0)); + io_apic_write(apic, 0x11+2*pin, *(((int *)&entry)+1)); } void __init UNEXPECTED_IO_APIC(void) @@ -674,17 +689,14 @@ void __init print_IO_APIC(void) { - int i; + int apic, i; struct IO_APIC_reg_00 reg_00; struct IO_APIC_reg_01 reg_01; struct IO_APIC_reg_02 reg_02; printk("number of MP IRQ sources: %d.\n", mp_irq_entries); - printk("number of IO-APIC registers: %d.\n", nr_ioapic_registers); - - *(int *)®_00 = io_apic_read(0); - *(int *)®_01 = io_apic_read(1); - *(int *)®_02 = io_apic_read(2); + for (i = 0; i < mp_apic_entries; i++) + printk("number of IO-APIC #%d registers: %d.\n", mp_apics[i].mpc_apicid, nr_ioapic_registers[i]); /* * We are a bit conservative about what we expect. We have to @@ -692,6 +704,12 @@ */ printk("testing the IO APIC.......................\n"); + for (apic = 0; apic < mp_apic_entries; apic++) { + + *(int *)®_00 = io_apic_read(apic, 0); + *(int *)®_01 = io_apic_read(apic, 1); + *(int *)®_02 = io_apic_read(apic, 2); + printk("\nIO APIC #%d......\n", mp_apics[apic].mpc_apicid); printk(".... register #00: %08X\n", *(int *)®_00); printk("....... : physical APIC id: %02X\n", reg_00.ID); if (reg_00.__reserved_1 || reg_00.__reserved_2) @@ -731,8 +749,8 @@ for (i = 0; i <= reg_01.entries; i++) { struct IO_APIC_route_entry entry; - *(((int *)&entry)+0) = io_apic_read(0x10+i*2); - *(((int *)&entry)+1) = io_apic_read(0x11+i*2); + *(((int *)&entry)+0) = io_apic_read(apic, 0x10+i*2); + *(((int *)&entry)+1) = io_apic_read(apic, 0x11+i*2); printk(" %02x %03X %02X ", i, @@ -751,7 +769,7 @@ entry.vector ); } - + } printk(KERN_DEBUG "IRQ to pin mappings:\n"); for (i = 0; i < NR_IRQS; i++) { struct irq_pin_list *entry = irq_2_pin + i; @@ -796,9 +814,12 @@ */ { struct IO_APIC_reg_01 reg_01; + int i; - *(int *)®_01 = io_apic_read(1); - nr_ioapic_registers = reg_01.entries+1; + for (i = 0; i < mp_apic_entries; i++) { + *(int *)®_01 = io_apic_read(i, 1); + nr_ioapic_registers[i] = reg_01.entries+1; + } } /* @@ -897,15 +918,15 @@ /* * Set the ID */ - *(int *)®_00 = io_apic_read(0); + *(int *)®_00 = io_apic_read(0, 0); printk("...changing IO-APIC physical APIC ID to 2...\n"); reg_00.ID = 0x2; - io_apic_write(0, *(int *)®_00); + io_apic_write(0, 0, *(int *)®_00); /* * Sanity check */ - *(int *)®_00 = io_apic_read(0); + *(int *)®_00 = io_apic_read(0, 0); if (reg_00.ID != 0x2) panic("could not set ID"); } @@ -1227,7 +1248,10 @@ if (pin2 != -1) { printk(".. (found pin %d) ...", pin2); - setup_ExtINT_pin(pin2, 0); + /* + * legacy devices should be connected to IO APIC #0 + */ + setup_ExtINT_pin(0, pin2, 0); make_8259A_irq(0); } @@ -1238,9 +1262,9 @@ * Just in case ... */ if (pin1 != -1) - clear_IO_APIC_pin(pin1); + clear_IO_APIC_pin(0, pin1); if (pin2 != -1) - clear_IO_APIC_pin(pin2); + clear_IO_APIC_pin(0, pin2); make_8259A_irq(0); @@ -1282,7 +1306,8 @@ * - those for which the user has specified a pirq= parameter */ if ( ioapic_whitelisted() || - (nr_ioapic_registers == 16) || + (mp_apic_entries == 1 && nr_ioapic_registers[0] == 16) || + (mp_apic_entries > 1) || pirqs_enabled) { printk("ENABLING IO-APIC IRQs\n"); diff -u --recursive --new-file v2.2.13/linux/arch/i386/kernel/irq.h linux/arch/i386/kernel/irq.h --- v2.2.13/linux/arch/i386/kernel/irq.h Mon Aug 9 16:05:54 1999 +++ linux/arch/i386/kernel/irq.h Tue Jan 4 10:12:11 2000 @@ -112,6 +112,11 @@ extern char _stext, _etext; +/* + * IF YOU CHANGE THIS, PLEASE ALSO CHANGE + * FIX_IO_APIC_BASE_* in fixmap.h + */ +#define MAX_IO_APICS 4 #define MAX_IRQ_SOURCES 128 #define MAX_MP_BUSSES 32 enum mp_bustype { diff -u --recursive --new-file v2.2.13/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c --- v2.2.13/linux/arch/i386/kernel/ptrace.c Wed Mar 24 13:18:46 1999 +++ linux/arch/i386/kernel/ptrace.c Tue Jan 4 10:12:11 2000 @@ -80,12 +80,17 @@ pmd_t * pgmiddle; pte_t * pgtable; unsigned long page; + int fault; repeat: pgdir = pgd_offset(vma->vm_mm, addr); if (pgd_none(*pgdir)) { - handle_mm_fault(tsk, vma, addr, 0); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 0); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return 0; } if (pgd_bad(*pgdir)) { printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir)); @@ -94,8 +99,12 @@ } pgmiddle = pmd_offset(pgdir, addr); if (pmd_none(*pgmiddle)) { - handle_mm_fault(tsk, vma, addr, 0); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 0); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return 0; } if (pmd_bad(*pgmiddle)) { printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle)); @@ -104,8 +113,12 @@ } pgtable = pte_offset(pgmiddle, addr); if (!pte_present(*pgtable)) { - handle_mm_fault(tsk, vma, addr, 0); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 0); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return 0; } page = pte_page(*pgtable); /* this is a hack for non-kernel-mapped video buffers and similar */ @@ -131,12 +144,17 @@ pmd_t *pgmiddle; pte_t *pgtable; unsigned long page; + int fault; repeat: pgdir = pgd_offset(vma->vm_mm, addr); if (!pgd_present(*pgdir)) { - handle_mm_fault(tsk, vma, addr, 1); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 1); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return; } if (pgd_bad(*pgdir)) { printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir)); @@ -145,8 +163,12 @@ } pgmiddle = pmd_offset(pgdir, addr); if (pmd_none(*pgmiddle)) { - handle_mm_fault(tsk, vma, addr, 1); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 1); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return; } if (pmd_bad(*pgmiddle)) { printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle)); @@ -155,13 +177,21 @@ } pgtable = pte_offset(pgmiddle, addr); if (!pte_present(*pgtable)) { - handle_mm_fault(tsk, vma, addr, 1); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 1); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return; } page = pte_page(*pgtable); if (!pte_write(*pgtable)) { - handle_mm_fault(tsk, vma, addr, 1); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 1); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return; } /* this is a hack for non-kernel-mapped video buffers and similar */ if (MAP_NR(page) < max_mapnr) diff -u --recursive --new-file v2.2.13/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c --- v2.2.13/linux/arch/i386/kernel/setup.c Tue Jan 4 11:10:31 2000 +++ linux/arch/i386/kernel/setup.c Tue Jan 4 10:12:11 2000 @@ -17,6 +17,12 @@ * * IDT Winchip tweaks, misc clean ups. * Dave Jones , August 1999 + * + * Added proper L2 cache detection for Coppermine + * Dragan Stancevic , October 1999 + * + * Improved Intel cache detection. + * Dave Jones , October 1999 */ /* @@ -366,7 +372,7 @@ /* Zero is valid according to the BIOS weenies */ if(i386_endbase) { - printk(KERN_NOTICE "Ignoring bogus EBDA pointer %X\n", + printk(KERN_NOTICE "Ignoring bogus EBDA pointer %lX\n", i386_endbase); } i386_endbase = BIOS_ENDBASE; @@ -755,8 +761,8 @@ { X86_VENDOR_INTEL, 6, { "Pentium Pro A-step", "Pentium Pro", NULL, "Pentium II (Klamath)", NULL, "Pentium II (Deschutes)", "Mobile Pentium II", - "Pentium III (Katmai)", "Pentium III (Coppermine)", NULL, NULL, - NULL, NULL, NULL, NULL }}, + "Pentium III (Katmai)", "Pentium III (Coppermine)", NULL, NULL, + NULL, NULL, NULL, NULL, NULL }}, { X86_VENDOR_AMD, 4, { NULL, NULL, NULL, "486 DX/2", NULL, NULL, NULL, "486 DX/2-WB", "486 DX/4", "486 DX/4-WB", NULL, NULL, NULL, NULL, "Am5x86-WT", @@ -818,49 +824,48 @@ } } - for (i = 0; i < sizeof(cpu_models)/sizeof(struct cpu_model_info); i++) { - if (c->cpuid_level > 1) { - /* supports eax=2 call */ - int edx, cache_size, dummy; + if (c->cpuid_level > 1) { + /* supports eax=2 call */ + int edx, dummy; - cpuid(2, &dummy, &dummy, &dummy, &edx); + cpuid(2, &dummy, &dummy, &dummy, &edx); - /* We need only the LSB */ - edx &= 0xff; + /* We need only the LSB */ + edx &= 0xff; - switch (edx) { - case 0x40: - cache_size = 0; - break; - - case 0x41: - cache_size = 128; - break; - - case 0x42: - cache_size = 256; - break; - - case 0x43: - cache_size = 512; - break; - - case 0x44: - cache_size = 1024; - break; - - case 0x45: - cache_size = 2048; - break; - - default: - cache_size = 0; - break; - } + switch (edx) { + case 0x40: + c->x86_cache_size = 0; + break; - c->x86_cache_size = cache_size; + case 0x41: + c->x86_cache_size = 128; + break; + + case 0x42: + case 0x82: /*Detect 256-Kbyte cache on Coppermine*/ + c->x86_cache_size = 256; + break; + + case 0x43: + c->x86_cache_size = 512; + break; + + case 0x44: + c->x86_cache_size = 1024; + break; + + case 0x45: + c->x86_cache_size = 2048; + break; + + default: + c->x86_cache_size = 0; + break; } + } + for (i = 0; i < sizeof(cpu_models)/sizeof(struct cpu_model_info); i++) { if (cpu_models[i].vendor == c->x86_vendor && cpu_models[i].x86 == c->x86) { if (c->x86_model <= 16) @@ -1019,8 +1024,9 @@ x86_cap_flags[14] = "mca"; x86_cap_flags[16] = "pat"; x86_cap_flags[17] = "pse36"; - x86_cap_flags[18] = "psn"; - x86_cap_flags[24] = "osfxsr"; + x86_cap_flags[18] = "pn"; + x86_cap_flags[24] = "fxsr"; + x86_cap_flags[25] = "xmm"; break; case X86_VENDOR_CENTAUR: diff -u --recursive --new-file v2.2.13/linux/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c --- v2.2.13/linux/arch/i386/kernel/signal.c Mon Jun 7 16:14:20 1999 +++ linux/arch/i386/kernel/signal.c Tue Jan 4 10:12:11 2000 @@ -419,13 +419,19 @@ ? current->exec_domain->signal_invmap[sig] : sig), &frame->sig); + if (err) + goto give_sigsegv; err |= setup_sigcontext(&frame->sc, &frame->fpstate, regs, set->sig[0]); + if (err) + goto give_sigsegv; if (_NSIG_WORDS > 1) { err |= __copy_to_user(frame->extramask, &set->sig[1], sizeof(frame->extramask)); } + if (err) + goto give_sigsegv; /* Set up to return from userspace. If provided, use a stub already in userspace. */ @@ -486,6 +492,8 @@ err |= __put_user(&frame->info, &frame->pinfo); err |= __put_user(&frame->uc, &frame->puc); err |= __copy_to_user(&frame->info, info, sizeof(*info)); + if (err) + goto give_sigsegv; /* Create the ucontext. */ err |= __put_user(0, &frame->uc.uc_flags); @@ -497,6 +505,8 @@ err |= setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpstate, regs, set->sig[0]); err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + if (err) + goto give_sigsegv; /* Set up to return from userspace. If provided, use a stub already in userspace. */ diff -u --recursive --new-file v2.2.13/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c --- v2.2.13/linux/arch/i386/kernel/smp.c Tue Jan 4 11:10:31 2000 +++ linux/arch/i386/kernel/smp.c Tue Jan 4 10:12:11 2000 @@ -114,7 +114,6 @@ volatile unsigned long kstack_ptr; /* Stack vector for booting CPUs */ struct cpuinfo_x86 cpu_data[NR_CPUS]; /* Per CPU bogomips and other parameters */ static unsigned int num_processors = 1; /* Internal processor count */ -unsigned long mp_ioapic_addr = 0xFEC00000; /* Address of the I/O apic (not yet used) */ unsigned char boot_cpu_id = 0; /* Processor that is doing the boot up */ static int smp_activated = 0; /* Tripped once we need to start cross invalidating */ int apic_version[NR_CPUS]; /* APIC version number */ @@ -128,6 +127,8 @@ const char lk_lockmsg[] = "lock from interrupt context at %p\n"; int mp_bus_id_to_type [MAX_MP_BUSSES] = { -1, }; +extern int mp_apic_entries; +extern struct mpc_config_ioapic mp_apics [MAX_IO_APICS]; extern int mp_irq_entries; extern struct mpc_config_intsrc mp_irqs [MAX_IRQ_SOURCES]; extern int mpc_default_type; @@ -367,11 +368,9 @@ printk("I/O APIC #%d Version %d at 0x%lX.\n", m->mpc_apicid,m->mpc_apicver, m->mpc_apicaddr); - /* - * we use the first one only currently - */ - if (ioapics == 1) - mp_ioapic_addr = m->mpc_apicaddr; + mp_apics [mp_apic_entries] = *m; + if (++mp_apic_entries > MAX_IO_APICS) + --mp_apic_entries; } mpt+=sizeof(*m); count+=sizeof(*m); @@ -403,9 +402,9 @@ } } } - if (ioapics > 1) + if (ioapics > MAX_IO_APICS) { - printk("Warning: Multiple IO-APICs not yet supported.\n"); + printk("Warning: Max I/O APICs exceeded (max %d, found %d).\n", MAX_IO_APICS, ioapics); printk("Warning: switching to non APIC mode.\n"); skip_ioapic_setup=1; } @@ -493,6 +492,8 @@ cpu_present_map=3; num_processors=2; printk("I/O APIC at 0xFEC00000.\n"); + mp_apics[0].mpc_apicaddr = 0xFEC00000; + mp_apic_entries = 1; /* * Save the default type number, we @@ -772,18 +773,22 @@ #ifdef CONFIG_X86_IO_APIC { - unsigned long ioapic_phys; + unsigned long ioapic_phys, idx = FIX_IO_APIC_BASE_0; + int i; - if (smp_found_config) { - ioapic_phys = mp_ioapic_addr; - } else { - ioapic_phys = __pa(memory_start); - memset((void *)memory_start, 0, PAGE_SIZE); - memory_start += PAGE_SIZE; + for (i = 0; i < mp_apic_entries; i++) { + if (smp_found_config) { + ioapic_phys = mp_apics[i].mpc_apicaddr; + } else { + ioapic_phys = __pa(memory_start); + memset((void *)memory_start, 0, PAGE_SIZE); + memory_start += PAGE_SIZE; + } + set_fixmap(idx,ioapic_phys); + printk("mapped IOAPIC to %08lx (%08lx)\n", + __fix_to_virt(idx), ioapic_phys); + idx++; } - set_fixmap(FIX_IO_APIC_BASE,ioapic_phys); - printk("mapped IOAPIC to %08lx (%08lx)\n", - fix_to_virt(FIX_IO_APIC_BASE), ioapic_phys); } #endif diff -u --recursive --new-file v2.2.13/linux/arch/i386/math-emu/README linux/arch/i386/math-emu/README --- v2.2.13/linux/arch/i386/math-emu/README Wed Jun 24 14:30:08 1998 +++ linux/arch/i386/math-emu/README Tue Jan 4 10:12:11 2000 @@ -1,9 +1,9 @@ +---------------------------------------------------------------------------+ | wm-FPU-emu an FPU emulator for 80386 and 80486SX microprocessors. | | | - | Copyright (C) 1992,1993,1994,1995,1996,1997 | + | Copyright (C) 1992,1993,1994,1995,1996,1997,1999 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@suburbia.net | + | Australia. E-mail billm@melbpc.org.au | | | | This program is free software; you can redistribute it and/or modify | | it under the terms of the GNU General Public License version 2 as | @@ -42,14 +42,15 @@ some differences. Please report bugs, etc to me at: - billm@suburbia.net + billm@melbpc.org.au +or b.metzenthen@medoto.unimelb.edu.au For more information on the emulator and on floating point topics, see my web pages, currently at http://www.suburbia.net/~billm/ --Bill Metzenthen - December 1997 + December 1999 ----------------------- Internals of wm-FPU-emu ----------------------- @@ -98,7 +99,7 @@ ----------------------- Limitations of wm-FPU-emu ----------------------- There are a number of differences between the current wm-FPU-emu -(version 2.00) and the 80486 FPU (apart from bugs). The differences +(version 2.01) and the 80486 FPU (apart from bugs). The differences are fewer than those which applied to the 1.xx series of the emulator. Some of the more important differences are listed below: diff -u --recursive --new-file v2.2.13/linux/arch/i386/math-emu/fpu_trig.c linux/arch/i386/math-emu/fpu_trig.c --- v2.2.13/linux/arch/i386/math-emu/fpu_trig.c Tue Dec 9 17:57:09 1997 +++ linux/arch/i386/math-emu/fpu_trig.c Tue Jan 4 10:12:11 2000 @@ -3,9 +3,9 @@ | | | Implementation of the FPU "transcendental" functions. | | | - | Copyright (C) 1992,1993,1994,1997 | + | Copyright (C) 1992,1993,1994,1997,1999 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@suburbia.net | + | Australia. E-mail billm@melbpc.org.au | | | | | +---------------------------------------------------------------------------*/ @@ -85,7 +85,8 @@ FPU_normalize(&tmp); tmptag = FPU_u_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION, SIGN_POS, - exponent16(&CONST_PI2extra) + exponent16(&tmp)); + exponent(&CONST_PI2extra) + exponent(&tmp)); + setsign(&tmp, getsign(&CONST_PI2extra)); st0_tag = FPU_add(&tmp, tmptag, 0, FULL_PRECISION); if ( signnegative(st0_ptr) ) { @@ -117,7 +118,8 @@ FPU_normalize(&tmp); /* This must return TAG_Valid */ tmptag = FPU_u_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION, SIGN_POS, - exponent16(&CONST_PI2extra) + exponent16(&tmp)); + exponent(&CONST_PI2extra) + exponent(&tmp)); + setsign(&tmp, getsign(&CONST_PI2extra)); st0_tag = FPU_sub(LOADED|(tmptag & 0x0f), (int)&tmp, FULL_PRECISION); if ( (exponent(st0_ptr) == exponent(&CONST_PI2)) && @@ -827,18 +829,29 @@ unsigned long long st1, unsigned long long q, int n) { + int dummy; unsigned long long x; x = st0 << n; /* Do the required multiplication and subtraction in the one operation */ - asm volatile ("movl %2,%%eax; mull %4; subl %%eax,%0; sbbl %%edx,%1; - movl %3,%%eax; mull %4; subl %%eax,%1; - movl %2,%%eax; mull %5; subl %%eax,%1;" - :"=m" (x), "=m" (((unsigned *)&x)[1]) - :"m" (st1),"m" (((unsigned *)&st1)[1]), - "m" (q),"m" (((unsigned *)&q)[1]) - :"%ax","%dx"); + + /* lsw x -= lsw st1 * lsw q */ + asm volatile ("mull %4; subl %%eax,%0; sbbl %%edx,%1" + :"=m" (((unsigned *)&x)[0]), "=m" (((unsigned *)&x)[1]), + "=a" (dummy) + :"2" (((unsigned *)&st1)[0]), "m" (((unsigned *)&q)[0]) + :"%dx"); + /* msw x -= msw st1 * lsw q */ + asm volatile ("mull %3; subl %%eax,%0" + :"=m" (((unsigned *)&x)[1]), "=a" (dummy) + :"1" (((unsigned *)&st1)[1]), "m" (((unsigned *)&q)[0]) + :"%dx"); + /* msw x -= lsw st1 * msw q */ + asm volatile ("mull %3; subl %%eax,%0" + :"=m" (((unsigned *)&x)[1]), "=a" (dummy) + :"1" (((unsigned *)&st1)[0]), "m" (((unsigned *)&q)[1]) + :"%dx"); *y = x; } diff -u --recursive --new-file v2.2.13/linux/arch/i386/math-emu/poly.h linux/arch/i386/math-emu/poly.h --- v2.2.13/linux/arch/i386/math-emu/poly.h Tue Apr 7 07:48:54 1998 +++ linux/arch/i386/math-emu/poly.h Tue Jan 4 10:12:11 2000 @@ -3,9 +3,9 @@ | | | Header file for the FPU-emu poly*.c source files. | | | - | Copyright (C) 1994 | + | Copyright (C) 1994,1999 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Australia. E-mail billm@melbpc.org.au | | | | Declarations and definitions for functions operating on Xsig (12-byte | | extended-significand) quantities. | @@ -55,15 +55,20 @@ actually be in-line. */ -/* Multiply two fixed-point 32 bit numbers. */ -extern inline void mul_32_32(const unsigned long arg1, - const unsigned long arg2, - unsigned long *out) +/* Multiply two fixed-point 32 bit numbers, producing a 32 bit result. + The answer is the ms word of the product. */ +/* Some versions of gcc make it difficult to stop eax from being clobbered. + Merely specifying that it is used doesn't work... + */ +extern inline unsigned long mul_32_32(const unsigned long arg1, + const unsigned long arg2) { - asm volatile ("movl %1,%%eax; mull %2; movl %%edx,%0" \ - :"=g" (*out) \ - :"g" (arg1), "g" (arg2) \ - :"ax","dx"); + int retval; + asm volatile ("mull %2; movl %%edx,%%eax" \ + :"=a" (retval) \ + :"0" (arg1), "g" (arg2) \ + :"dx"); + return retval; } diff -u --recursive --new-file v2.2.13/linux/arch/i386/math-emu/poly_sin.c linux/arch/i386/math-emu/poly_sin.c --- v2.2.13/linux/arch/i386/math-emu/poly_sin.c Tue Dec 9 17:57:09 1997 +++ linux/arch/i386/math-emu/poly_sin.c Tue Jan 4 10:12:11 2000 @@ -4,9 +4,9 @@ | Computation of an approximation of the sin function and the cosine | | function by a polynomial. | | | - | Copyright (C) 1992,1993,1994,1997 | + | Copyright (C) 1992,1993,1994,1997,1999 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | - | E-mail billm@suburbia.net | + | E-mail billm@melbpc.org.au | | | | | +---------------------------------------------------------------------------*/ @@ -132,6 +132,9 @@ } /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */ fixed_arg = 0x921fb54442d18469LL - fixed_arg; + /* There is a special case which arises due to rounding, to fix here. */ + if ( fixed_arg == 0xffffffffffffffffLL ) + fixed_arg = 0; XSIG_LL(argSqrd) = fixed_arg; argSqrd.lsw = 0; mul64_Xsig(&argSqrd, &fixed_arg); @@ -172,10 +175,9 @@ if ( argSqrd.msw & 0xffc00000 ) { /* Get about 32 bit precision in these: */ - mul_32_32(0x898cc517, argSqrd.msw, &adj); - fix_up -= adj/6; + fix_up -= mul_32_32(0x898cc517, argSqrd.msw) / 6; } - mul_32_32(fix_up, LL_MSW(fixed_arg), &fix_up); + fix_up = mul_32_32(fix_up, LL_MSW(fixed_arg)); adj = accumulator.lsw; /* temp save */ accumulator.lsw -= fix_up; @@ -211,7 +213,6 @@ FPU_REG result; long int exponent, exp2, echange; Xsig accumulator, argSqrd, fix_up, argTo4; - unsigned long adj; unsigned long long fixed_arg; #ifdef PARANOID @@ -300,6 +301,9 @@ } /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */ fixed_arg = 0x921fb54442d18469LL - fixed_arg; + /* There is a special case which arises due to rounding, to fix here. */ + if ( fixed_arg == 0xffffffffffffffffLL ) + fixed_arg = 0; exponent = -1; exp2 = -1; @@ -363,10 +367,8 @@ if ( argSqrd.msw & 0xffc00000 ) { /* Get about 32 bit precision in these: */ - mul_32_32(0x898cc517, argSqrd.msw, &adj); - fix_up.msw -= adj/2; - mul_32_32(0x898cc517, argTo4.msw, &adj); - fix_up.msw += adj/24; + fix_up.msw -= mul_32_32(0x898cc517, argSqrd.msw) / 2; + fix_up.msw += mul_32_32(0x898cc517, argTo4.msw) / 24; } exp2 += norm_Xsig(&accumulator); diff -u --recursive --new-file v2.2.13/linux/arch/i386/math-emu/poly_tan.c linux/arch/i386/math-emu/poly_tan.c --- v2.2.13/linux/arch/i386/math-emu/poly_tan.c Tue Dec 9 17:57:09 1997 +++ linux/arch/i386/math-emu/poly_tan.c Tue Jan 4 10:12:11 2000 @@ -3,9 +3,9 @@ | | | Compute the tan of a FPU_REG, using a polynomial approximation. | | | - | Copyright (C) 1992,1993,1994,1997 | + | Copyright (C) 1992,1993,1994,1997,1999 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@suburbia.net | + | Australia. E-mail billm@melbpc.org.au | | | | | +---------------------------------------------------------------------------*/ @@ -84,6 +84,14 @@ } /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */ XSIG_LL(accum) = 0x921fb54442d18469LL - XSIG_LL(accum); + /* This is a special case which arises due to rounding. */ + if ( XSIG_LL(accum) == 0xffffffffffffffffLL ) + { + FPU_settag0(TAG_Valid); + significand(st0_ptr) = 0x8a51e04daabda360LL; + setexponent16(st0_ptr, 0x41 + EXTENDED_Ebias | SIGN_Negative); + return; + } argSignif.lsw = accum.lsw; XSIG_LL(argSignif) = XSIG_LL(accum); @@ -177,11 +185,11 @@ else if ( exponent > -30 ) { adj = accum.msw >> -(exponent+1); /* tan */ - mul_32_32(adj, adj, &adj); /* tan^2 */ + adj = mul_32_32(adj, adj); /* tan^2 */ } else adj = 0; - mul_32_32(0x898cc517, adj, &adj); /* delta * tan^2 */ + adj = mul_32_32(0x898cc517, adj); /* delta * tan^2 */ fix_up.msw += adj; if ( !(fix_up.msw & 0x80000000) ) /* did fix_up overflow ? */ diff -u --recursive --new-file v2.2.13/linux/arch/i386/math-emu/reg_round.S linux/arch/i386/math-emu/reg_round.S --- v2.2.13/linux/arch/i386/math-emu/reg_round.S Tue Dec 9 17:57:09 1997 +++ linux/arch/i386/math-emu/reg_round.S Tue Jan 4 10:12:11 2000 @@ -365,7 +365,7 @@ jne LDo_64_round_up /* Now test for round-to-even */ - testb $1,%ebx + testb $1,%bl jz LCheck_truncate_64 LDo_64_round_up: diff -u --recursive --new-file v2.2.13/linux/arch/i386/mm/fault.c linux/arch/i386/mm/fault.c --- v2.2.13/linux/arch/i386/mm/fault.c Sun Nov 22 09:38:19 1998 +++ linux/arch/i386/mm/fault.c Tue Jan 4 10:12:11 2000 @@ -50,7 +50,14 @@ start &= PAGE_MASK; for (;;) { - handle_mm_fault(current,vma, start, 1); + survive: + { + int fault = handle_mm_fault(current,vma, start, 1); + if (!fault) + goto do_sigbus; + if (fault < 0) + goto out_of_memory; + } if (!size) break; size--; @@ -73,6 +80,19 @@ bad_area: return 0; + +do_sigbus: + force_sig(SIGBUS, current); + goto bad_area; + +out_of_memory: + if (current->pid == 1) + { + current->policy |= SCHED_YIELD; + schedule(); + goto survive; + } + goto bad_area; } asmlinkage void do_invalid_op(struct pt_regs *, unsigned long); @@ -162,8 +182,14 @@ * make sure we exit gracefully rather than endlessly redo * the fault. */ - if (!handle_mm_fault(tsk, vma, address, write)) - goto do_sigbus; +survive: + { + int fault = handle_mm_fault(tsk, vma, address, write); + if (!fault) + goto do_sigbus; + if (fault < 0) + goto out_of_memory; + } /* * Did it hit the DOS screen memory VA from vm86 mode? @@ -255,6 +281,34 @@ * We ran out of memory, or some other thing happened to us that made * us unable to handle the page fault gracefully. */ +out_of_memory: + if (tsk->pid == 1) + { + tsk->policy |= SCHED_YIELD; + schedule(); + goto survive; + } + up(&mm->mmap_sem); + if (error_code & 4) + { + if (!((regs->eflags >> 12) & 3)) + { + printk("VM: killing process %s\n", tsk->comm); + do_exit(SIGKILL); + } + else + { + /* + * The task is running with privilegies and so we + * trust it and we give it a chance to die gracefully. + */ + printk("VM: terminating process %s\n", tsk->comm); + force_sig(SIGTERM, current); + return; + } + } + goto no_context; + do_sigbus: up(&mm->mmap_sem); diff -u --recursive --new-file v2.2.13/linux/arch/ppc/Makefile linux/arch/ppc/Makefile --- v2.2.13/linux/arch/ppc/Makefile Mon Aug 9 16:05:55 1999 +++ linux/arch/ppc/Makefile Tue Jan 4 10:12:11 2000 @@ -112,6 +112,10 @@ rm -f .config arch/ppc/defconfig ln -s apus_defconfig arch/ppc/defconfig +gemini_config: + rm -f .config arch/ppc/defconfig + ln -s gemini_defconfig arch/ppc/defconfig + tags: etags */*.c include/{asm,linux}/*.h arch/ppc/kernel/*.{c,h} diff -u --recursive --new-file v2.2.13/linux/arch/ppc/boot/Makefile linux/arch/ppc/boot/Makefile --- v2.2.13/linux/arch/ppc/boot/Makefile Tue Oct 19 17:10:36 1999 +++ linux/arch/ppc/boot/Makefile Tue Jan 4 10:12:11 2000 @@ -25,12 +25,22 @@ IOFF = 0 ISZ = 0 +ifeq ($(CONFIG_ALL_PPC),y) +CONFIG_PREP=y +endif + ifeq ($(CONFIG_SMP),y) TFTPIMAGE=/tftpboot/zImage.prep.smp$(MSIZE) else TFTPIMAGE=/tftpboot/zImage.prep$(MSIZE) endif +ifeq ($(CONFIG_SMP),y) +TFTPSIMAGE=/tftpboot/sImage.smp +else +TFTPSIMAGE=/tftpboot/sImage +endif + ifeq ($(CONFIG_PPC64),y) MSIZE=.64 else @@ -46,14 +56,10 @@ OBJCOPY = $(CROSS_COMPILE)objcopy OBJCOPY_ARGS = -O elf32-powerpc -ifeq ($(CONFIG_SMP),y) -CFLAGS += -D__SMP__ -endif - OBJECTS += vreset.o kbd.o of1275.o - ifeq ($(CONFIG_SERIAL_CONSOLE),y) - OBJECTS += ns16550.o - endif +ifeq ($(CONFIG_SERIAL_CONSOLE),y) +OBJECTS += ns16550.o +endif all: zImage @@ -75,11 +81,20 @@ zvmlinux.initrd.tmp $@ rm zvmlinux.initrd.tmp -zImage: zvmlinux mkprep +zImage: zvmlinux mkprep sImage +ifdef CONFIG_PREP ./mkprep -pbp zvmlinux zImage +endif + +sImage: ../../../vmlinux +ifdef CONFIG_GEMINI + $(OBJCOPY) -I elf32-powerpc -O binary ../../../vmlinux sImage +endif zImage.initrd: zvmlinux.initrd mkprep +ifdef CONFIG_PREP ./mkprep -pbp zvmlinux.initrd zImage.initrd +endif zvmlinux: $(OBJECTS) ../coffboot/vmlinux.gz # @@ -108,13 +123,18 @@ $(HOSTCC) -o mkprep mkprep.c znetboot : zImage +ifdef CONFIG_PREP cp zImage $(TFTPIMAGE) +endif +ifdef CONFIG_GEMINI + cp sImage $(TFTPSIMAGE) +endif znetboot.initrd : zImage.initrd cp zImage.initrd $(TFTPIMAGE) clean: - rm -f vmlinux* zvmlinux* mkprep zImage* + rm -f vmlinux* zvmlinux* mkprep zImage* sImage* fastdep: $(TOPDIR)/scripts/mkdep *.[Sch] > .depend diff -u --recursive --new-file v2.2.13/linux/arch/ppc/boot/head.S linux/arch/ppc/boot/head.S --- v2.2.13/linux/arch/ppc/boot/head.S Thu Apr 29 12:39:01 1999 +++ linux/arch/ppc/boot/head.S Tue Jan 4 10:12:11 2000 @@ -6,7 +6,7 @@ .text /* - * $Id: head.S,v 1.31 1999/04/22 06:32:00 davem Exp $ + * $Id: head.S,v 1.33 1999/09/08 01:06:58 cort Exp $ * * Boot loader philosophy: * ROM loads us to some arbitrary location @@ -125,12 +125,11 @@ * get start address of kernel code which is stored as a coff * entry. see boot/head.S -- Cort */ - li r9,0x0 - lwz r9,0(r9) + li r9,0x4 mtlr r9 - li r9,0 lis r10,0xdeadc0de@h ori r10,r10,0xdeadc0de@l + li r9,0 stw r10,0(r9) /* * The Radstone firmware maps PCI memory at 0xc0000000 using BAT2 diff -u --recursive --new-file v2.2.13/linux/arch/ppc/chrpboot/main.c linux/arch/ppc/chrpboot/main.c --- v2.2.13/linux/arch/ppc/chrpboot/main.c Mon Aug 9 16:05:55 1999 +++ linux/arch/ppc/chrpboot/main.c Tue Jan 4 10:12:12 2000 @@ -68,7 +68,7 @@ flush_cache(dst, len); - sa = *(unsigned long *)PROG_START+PROG_START; + sa = (unsigned long)PROG_START; printf("start address = 0x%x\n\r", sa); (*(void (*)())sa)(0, 0, prom, a1, a2); diff -u --recursive --new-file v2.2.13/linux/arch/ppc/chrpboot/string.S linux/arch/ppc/chrpboot/string.S --- v2.2.13/linux/arch/ppc/chrpboot/string.S Mon Jan 12 15:18:13 1998 +++ linux/arch/ppc/chrpboot/string.S Wed Dec 31 16:00:00 1969 @@ -1,206 +0,0 @@ -/* - * String handling functions for PowerPC. - * - * Copyright (C) 1996 Paul Mackerras. - * - * 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. - */ -#define r0 0 -#define r3 3 -#define r4 4 -#define r5 5 -#define r6 6 -#define r7 7 -#define r8 8 - - .globl strcpy -strcpy: - addi r5,r3,-1 - addi r4,r4,-1 -1: lbzu r0,1(r4) - cmpwi 0,r0,0 - stbu r0,1(r5) - bne 1b - blr - - .globl strncpy -strncpy: - cmpwi 0,r5,0 - beqlr - mtctr r5 - addi r6,r3,-1 - addi r4,r4,-1 -1: lbzu r0,1(r4) - cmpwi 0,r0,0 - stbu r0,1(r6) - bdnzf 2,1b /* dec ctr, branch if ctr != 0 && !cr0.eq */ - blr - - .globl strcat -strcat: - addi r5,r3,-1 - addi r4,r4,-1 -1: lbzu r0,1(r5) - cmpwi 0,r0,0 - bne 1b - addi r5,r5,-1 -1: lbzu r0,1(r4) - cmpwi 0,r0,0 - stbu r0,1(r5) - bne 1b - blr - - .globl strcmp -strcmp: - addi r5,r3,-1 - addi r4,r4,-1 -1: lbzu r3,1(r5) - cmpwi 1,r3,0 - lbzu r0,1(r4) - subf. r3,r0,r3 - beqlr 1 - beq 1b - blr - - .globl strlen -strlen: - addi r4,r3,-1 -1: lbzu r0,1(r4) - cmpwi 0,r0,0 - bne 1b - subf r3,r3,r4 - blr - - .globl memset -memset: - rlwimi r4,r4,8,16,23 - rlwimi r4,r4,16,0,15 - addi r6,r3,-4 - cmplwi 0,r5,4 - blt 7f - stwu r4,4(r6) - beqlr - andi. r0,r6,3 - add r5,r0,r5 - subf r6,r0,r6 - rlwinm r0,r5,32-2,2,31 - mtctr r0 - bdz 6f -1: stwu r4,4(r6) - bdnz 1b -6: andi. r5,r5,3 -7: cmpwi 0,r5,0 - beqlr - mtctr r5 - addi r6,r6,3 -8: stbu r4,1(r6) - bdnz 8b - blr - - .globl bcopy -bcopy: - mr r6,r3 - mr r3,r4 - mr r4,r6 - b memcpy - - .globl memmove -memmove: - cmplw 0,r3,r4 - bgt backwards_memcpy - /* fall through */ - - .globl memcpy -memcpy: - rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ - addi r6,r3,-4 - addi r4,r4,-4 - beq 2f /* if less than 8 bytes to do */ - andi. r0,r6,3 /* get dest word aligned */ - mtctr r7 - bne 5f -1: lwz r7,4(r4) - lwzu r8,8(r4) - stw r7,4(r6) - stwu r8,8(r6) - bdnz 1b - andi. r5,r5,7 -2: cmplwi 0,r5,4 - blt 3f - lwzu r0,4(r4) - addi r5,r5,-4 - stwu r0,4(r6) -3: cmpwi 0,r5,0 - beqlr - mtctr r5 - addi r4,r4,3 - addi r6,r6,3 -4: lbzu r0,1(r4) - stbu r0,1(r6) - bdnz 4b - blr -5: subfic r0,r0,4 - mtctr r0 -6: lbz r7,4(r4) - addi r4,r4,1 - stb r7,4(r6) - addi r6,r6,1 - bdnz 6b - subf r5,r0,r5 - rlwinm. r7,r5,32-3,3,31 - beq 2b - mtctr r7 - b 1b - - .globl backwards_memcpy -backwards_memcpy: - rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ - add r6,r3,r5 - add r4,r4,r5 - beq 2f - andi. r0,r6,3 - mtctr r7 - bne 5f -1: lwz r7,-4(r4) - lwzu r8,-8(r4) - stw r7,-4(r6) - stwu r8,-8(r6) - bdnz 1b - andi. r5,r5,7 -2: cmplwi 0,r5,4 - blt 3f - lwzu r0,-4(r4) - subi r5,r5,4 - stwu r0,-4(r6) -3: cmpwi 0,r5,0 - beqlr - mtctr r5 -4: lbzu r0,-1(r4) - stbu r0,-1(r6) - bdnz 4b - blr -5: mtctr r0 -6: lbzu r7,-1(r4) - stbu r7,-1(r6) - bdnz 6b - subf r5,r0,r5 - rlwinm. r7,r5,32-3,3,31 - beq 2b - mtctr r7 - b 1b - - .globl memcmp -memcmp: - cmpwi 0,r5,0 - blelr - mtctr r5 - addi r6,r3,-1 - addi r4,r4,-1 -1: lbzu r3,1(r6) - lbzu r0,1(r4) - subf. r3,r0,r3 - bdnzt 2,1b - blr diff -u --recursive --new-file v2.2.13/linux/arch/ppc/coffboot/main.c linux/arch/ppc/coffboot/main.c --- v2.2.13/linux/arch/ppc/coffboot/main.c Tue Aug 4 16:06:36 1998 +++ linux/arch/ppc/coffboot/main.c Tue Jan 4 10:12:12 2000 @@ -100,7 +100,7 @@ flush_cache(dst, len); - sa = *(unsigned *)dst + PROG_START; + sa = (unsigned long)dst; printf("start address = 0x%x\n", sa); #if 0 @@ -165,7 +165,6 @@ printf("gunzip: ran out of data in header\n"); exit(); } - s.zalloc = zalloc; s.zfree = zfree; r = inflateInit2(&s, -MAX_WBITS); diff -u --recursive --new-file v2.2.13/linux/arch/ppc/common_defconfig linux/arch/ppc/common_defconfig --- v2.2.13/linux/arch/ppc/common_defconfig Mon Aug 9 16:05:55 1999 +++ linux/arch/ppc/common_defconfig Tue Jan 4 10:12:12 2000 @@ -1,5 +1,5 @@ # -# Automatically generated make config: don't edit +# Automatically generated by make menuconfig: don't edit # # @@ -7,13 +7,13 @@ # CONFIG_PPC=y CONFIG_6xx=y -# CONFIG_PPC64 is not set # CONFIG_8xx is not set # CONFIG_PMAC is not set # CONFIG_PREP is not set # CONFIG_CHRP is not set CONFIG_ALL_PPC=y # CONFIG_APUS is not set +# CONFIG_GEMINI is not set # CONFIG_MBX is not set # CONFIG_SMP is not set CONFIG_6xx=y @@ -62,10 +62,6 @@ # CONFIG_BLK_DEV_FD=y CONFIG_BLK_DEV_IDE=y - -# -# Please see Documentation/ide.txt for help/info on IDE drives -# # CONFIG_BLK_DEV_HD_IDE is not set CONFIG_BLK_DEV_IDEDISK=y CONFIG_BLK_DEV_IDECD=y @@ -81,18 +77,16 @@ CONFIG_BLK_DEV_IDEDMA=y CONFIG_PMAC_IDEDMA_AUTO=y # CONFIG_IDE_CHIPSETS is not set - -# -# Additional Block Devices -# CONFIG_BLK_DEV_LOOP=y # CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_MD is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_INITRD=y # CONFIG_BLK_DEV_XD is not set +# CONFIG_BLK_DEV_DAC960 is not set CONFIG_PARIDE_PARPORT=y # CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set # CONFIG_BLK_DEV_HD is not set # @@ -115,17 +109,9 @@ # CONFIG_IP_MROUTE is not set CONFIG_IP_ALIAS=y CONFIG_SYN_COOKIES=y - -# -# (it is safe to leave these untouched) -# CONFIG_INET_RARP=y CONFIG_SKB_LARGE=y # CONFIG_IPV6 is not set - -# -# -# # CONFIG_IPX is not set CONFIG_ATALK=m # CONFIG_X25 is not set @@ -147,19 +133,11 @@ # SCSI support # CONFIG_SCSI=y - -# -# SCSI support type (disk, tape, CD-ROM) -# CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_ST=y CONFIG_BLK_DEV_SR=y CONFIG_BLK_DEV_SR_VENDOR=y -# CONFIG_CHR_DEV_SG is not set - -# -# Some SCSI devices (e.g. CD jukebox) support multiple LUNs -# +CONFIG_CHR_DEV_SG=y # CONFIG_SCSI_MULTI_LUN is not set CONFIG_SCSI_CONSTANTS=y # CONFIG_SCSI_LOGGING is not set @@ -177,6 +155,7 @@ CONFIG_AIC7XXX_CMDS_PER_DEVICE=8 CONFIG_AIC7XXX_PROC_STATS=y CONFIG_AIC7XXX_RESET_DELAY=15 +# CONFIG_SCSI_IPS is not set # CONFIG_SCSI_ADVANSYS is not set # CONFIG_SCSI_IN2000 is not set # CONFIG_SCSI_AM53C974 is not set @@ -189,12 +168,11 @@ # CONFIG_SCSI_FUTURE_DOMAIN is not set # CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_GENERIC_NCR5380 is not set -# CONFIG_SCSI_G_NCR5380_PORT is not set -# CONFIG_SCSI_G_NCR5380_MEM is not set # CONFIG_SCSI_INITIO is not set # CONFIG_SCSI_INIA100 is not set # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_SIM710 is not set # CONFIG_SCSI_NCR53C7xx is not set # CONFIG_SCSI_NCR53C8XX is not set CONFIG_SCSI_SYM53C8XX=y @@ -226,26 +204,37 @@ # Network device support # CONFIG_NETDEVICES=y + +# +# ARCnet devices +# # CONFIG_ARCNET is not set # CONFIG_DUMMY is not set # CONFIG_EQUALIZER is not set # CONFIG_ETHERTAP is not set +# CONFIG_NET_SB1000 is not set + +# +# Ethernet (10 or 100Mbit) +# CONFIG_NET_ETHERNET=y CONFIG_MACE=y CONFIG_BMAC=y +# CONFIG_NCR885E is not set # CONFIG_NET_VENDOR_3COM is not set # CONFIG_LANCE is not set # CONFIG_NET_VENDOR_SMC is not set # CONFIG_NET_VENDOR_RACAL is not set # CONFIG_RTL8139 is not set # CONFIG_YELLOWFIN is not set -# CONFIG_ACENIC is not set # CONFIG_NET_ISA is not set CONFIG_NET_EISA=y CONFIG_PCNET32=y +# CONFIG_ACENIC is not set # CONFIG_AC3200 is not set # CONFIG_APRICOT is not set # CONFIG_CS89x0 is not set +# CONFIG_DM9102 is not set CONFIG_DE4X5=y # CONFIG_DEC_ELCP is not set # CONFIG_DGRS is not set @@ -255,28 +244,41 @@ # CONFIG_NE2K_PCI is not set # CONFIG_TLAN is not set # CONFIG_VIA_RHINE is not set +# CONFIG_SIS900 is not set # CONFIG_ES3210 is not set # CONFIG_EPIC100 is not set # CONFIG_ZNET is not set # CONFIG_NET_POCKET is not set # CONFIG_FDDI is not set # CONFIG_HIPPI is not set -# CONFIG_DLCI is not set + +# +# Appletalk devices +# # CONFIG_LTPC is not set # CONFIG_COPS is not set # CONFIG_IPDDP is not set CONFIG_PPP=y +# CONFIG_SLIP is not set +# CONFIG_NET_RADIO is not set # -# CCP compressors for PPP are only built as modules. +# Token ring devices # -# CONFIG_SLIP is not set -# CONFIG_NET_RADIO is not set # CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set # CONFIG_SHAPER is not set + +# +# Wan interfaces +# # CONFIG_HOSTESS_SV11 is not set # CONFIG_COSA is not set -# CONFIG_RCPCI is not set +# CONFIG_SEALEVEL_4021 is not set +# CONFIG_COMX is not set +# CONFIG_DLCI is not set +# CONFIG_SBNI is not set # # Amateur Radio support @@ -302,7 +304,6 @@ CONFIG_FB_CONTROL=y CONFIG_FB_PLATINUM=y CONFIG_FB_VALKYRIE=y -# CONFIG_FB_ATY is not set CONFIG_FB_IMSTT=y CONFIG_FB_CT65550=y # CONFIG_FB_S3TRIO is not set @@ -311,7 +312,7 @@ CONFIG_FB_MATROX_MYSTIQUE=y CONFIG_FB_MATROX_G100=y # CONFIG_FB_MATROX_MULTIHEAD is not set -# CONFIG_FB_ATY is not set +CONFIG_FB_ATY=y # CONFIG_FB_VIRTUAL is not set # CONFIG_FBCON_ADVANCED is not set CONFIG_FBCON_CFB8=y @@ -369,14 +370,6 @@ # Ftape, the floppy tape device driver # # CONFIG_FTAPE is not set -# CONFIG_FT_NORMAL_DEBUG is not set -# CONFIG_FT_FULL_DEBUG is not set -# CONFIG_FT_NO_TRACE is not set -# CONFIG_FT_NO_TRACE_AT_ALL is not set -# CONFIG_FT_STD_FDC is not set -# CONFIG_FT_MACH2 is not set -# CONFIG_FT_PROBE_FC10 is not set -# CONFIG_FT_ALT_FDC is not set # # USB drivers - not for the faint of heart @@ -407,6 +400,7 @@ CONFIG_EXT2_FS=y # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set +# CONFIG_EFS_FS is not set # # Network File Systems @@ -458,6 +452,7 @@ # CONFIG_NLS_ISO8859_7 is not set # CONFIG_NLS_ISO8859_8 is not set # CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_14 is not set # CONFIG_NLS_ISO8859_15 is not set # CONFIG_NLS_KOI8_R is not set @@ -466,8 +461,11 @@ # CONFIG_SOUND=y CONFIG_DMASOUND=y +# CONFIG_SOUND_CMPCI is not set # CONFIG_SOUND_ES1370 is not set # CONFIG_SOUND_ES1371 is not set +# CONFIG_SOUND_MAESTRO is not set +# CONFIG_SOUND_ESSSOLO1 is not set # CONFIG_SOUND_SONICVIBES is not set # CONFIG_SOUND_MSNDCLAS is not set # CONFIG_SOUND_MSNDPIN is not set @@ -475,13 +473,13 @@ # CONFIG_SOUND_DMAP is not set # CONFIG_SOUND_PAS is not set # CONFIG_SOUND_SB is not set -# CONFIG_SOUND_ADLIB is not set # CONFIG_SOUND_GUS is not set # CONFIG_SOUND_MPU401 is not set # CONFIG_SOUND_PSS is not set # CONFIG_SOUND_MSS is not set # CONFIG_SOUND_SSCAPE is not set # CONFIG_SOUND_TRIX is not set +# CONFIG_SOUND_VIA82CXXX is not set # CONFIG_SOUND_MAD16 is not set # CONFIG_SOUND_WAVEFRONT is not set CONFIG_SOUND_CS4232=m @@ -494,6 +492,7 @@ # CONFIG_SOUND_YM3812 is not set # CONFIG_SOUND_VMIDI is not set # CONFIG_SOUND_UART6850 is not set +# CONFIG_SOUND_NM256 is not set # # Additional low level sound drivers diff -u --recursive --new-file v2.2.13/linux/arch/ppc/config.in linux/arch/ppc/config.in --- v2.2.13/linux/arch/ppc/config.in Tue Jan 4 11:10:32 2000 +++ linux/arch/ppc/config.in Tue Jan 4 10:12:12 2000 @@ -9,7 +9,6 @@ define_bool CONFIG_PPC y choice 'Processor type' \ "6xx/7xx CONFIG_6xx \ - 630/Power3(64-Bit) CONFIG_PPC64 \ 860/821 CONFIG_8xx" 6xx/7xx choice 'Machine Type' \ @@ -18,6 +17,7 @@ CHRP CONFIG_CHRP \ PowerMac/PReP/CHRP CONFIG_ALL_PPC \ APUS CONFIG_APUS \ + Gemini CONFIG_GEMINI \ MBX CONFIG_MBX" PowerMac bool 'Symmetric multi-processing support' CONFIG_SMP diff -u --recursive --new-file v2.2.13/linux/arch/ppc/defconfig linux/arch/ppc/defconfig --- v2.2.13/linux/arch/ppc/defconfig Tue Jan 4 11:10:32 2000 +++ linux/arch/ppc/defconfig Tue Jan 4 10:12:12 2000 @@ -1,5 +1,5 @@ # -# Automatically generated make config: don't edit +# Automatically generated by make menuconfig: don't edit # # @@ -7,16 +7,15 @@ # CONFIG_PPC=y CONFIG_6xx=y -# CONFIG_PPC64 is not set # CONFIG_8xx is not set -CONFIG_PMAC=y +# CONFIG_PMAC is not set # CONFIG_PREP is not set # CONFIG_CHRP is not set -# CONFIG_ALL_PPC is not set +CONFIG_ALL_PPC=y # CONFIG_APUS is not set +# CONFIG_GEMINI is not set # CONFIG_MBX is not set # CONFIG_SMP is not set -CONFIG_MACH_SPECIFIC=y CONFIG_6xx=y # @@ -24,7 +23,7 @@ # CONFIG_EXPERIMENTAL=y CONFIG_MODULES=y -# CONFIG_MODVERSIONS is not set +CONFIG_MODVERSIONS=y CONFIG_KMOD=y CONFIG_PCI=y # CONFIG_PCI_QUIRKS is not set @@ -35,11 +34,10 @@ # CONFIG_BSD_PROCESS_ACCT is not set CONFIG_BINFMT_ELF=y CONFIG_KERNEL_ELF=y -CONFIG_BINFMT_MISC=m +# CONFIG_BINFMT_MISC is not set # CONFIG_BINFMT_JAVA is not set -CONFIG_PARPORT=m -# CONFIG_PARPORT_PC is not set -# CONFIG_VGA_CONSOLE is not set +# CONFIG_PARPORT is not set +CONFIG_VGA_CONSOLE=y CONFIG_FB=y CONFIG_FB_COMPAT_XPMAC=y CONFIG_PMAC_PBOOK=y @@ -52,6 +50,7 @@ # CONFIG_TOTALMP is not set CONFIG_BOOTX_TEXT=y # CONFIG_MOTOROLA_HOTSWAP is not set +# CONFIG_CMDLINE_BOOL is not set # # Plug and Play support @@ -61,12 +60,8 @@ # # Block devices # -# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_FD=y CONFIG_BLK_DEV_IDE=y - -# -# Please see Documentation/ide.txt for help/info on IDE drives -# # CONFIG_BLK_DEV_HD_IDE is not set CONFIG_BLK_DEV_IDEDISK=y CONFIG_BLK_DEV_IDECD=y @@ -75,25 +70,13 @@ # CONFIG_BLK_DEV_IDESCSI is not set # CONFIG_BLK_DEV_CMD640 is not set # CONFIG_BLK_DEV_RZ1000 is not set -CONFIG_BLK_DEV_IDEPCI=y -CONFIG_BLK_DEV_IDEDMA=y -# CONFIG_BLK_DEV_OFFBOARD is not set -CONFIG_IDEDMA_AUTO=y -# CONFIG_BLK_DEV_OPTI621 is not set -# CONFIG_BLK_DEV_TRM290 is not set -# CONFIG_BLK_DEV_NS87415 is not set -# CONFIG_BLK_DEV_VIA82C586 is not set -CONFIG_BLK_DEV_CMD646=y -# CONFIG_BLK_DEV_SL82C105 is not set +# CONFIG_BLK_DEV_IDEPCI is not set +CONFIG_BLK_DEV_SL82C105=y CONFIG_BLK_DEV_IDE_PMAC=y CONFIG_BLK_DEV_IDEDMA_PMAC=y CONFIG_BLK_DEV_IDEDMA=y CONFIG_PMAC_IDEDMA_AUTO=y # CONFIG_IDE_CHIPSETS is not set - -# -# Additional Block Devices -# CONFIG_BLK_DEV_LOOP=y # CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_MD is not set @@ -101,7 +84,7 @@ CONFIG_BLK_DEV_INITRD=y # CONFIG_BLK_DEV_XD is not set # CONFIG_BLK_DEV_DAC960 is not set -CONFIG_PARIDE_PARPORT=m +CONFIG_PARIDE_PARPORT=y # CONFIG_PARIDE is not set # CONFIG_BLK_CPQ_DA is not set # CONFIG_BLK_DEV_HD is not set @@ -125,18 +108,10 @@ # CONFIG_NET_IPGRE is not set # CONFIG_IP_MROUTE is not set CONFIG_IP_ALIAS=y -# CONFIG_SYN_COOKIES is not set - -# -# (it is safe to leave these untouched) -# +CONFIG_SYN_COOKIES=y CONFIG_INET_RARP=y CONFIG_SKB_LARGE=y # CONFIG_IPV6 is not set - -# -# -# # CONFIG_IPX is not set CONFIG_ATALK=m # CONFIG_X25 is not set @@ -158,19 +133,11 @@ # SCSI support # CONFIG_SCSI=y - -# -# SCSI support type (disk, tape, CD-ROM) -# CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_ST=y CONFIG_BLK_DEV_SR=y CONFIG_BLK_DEV_SR_VENDOR=y -# CONFIG_CHR_DEV_SG is not set - -# -# Some SCSI devices (e.g. CD jukebox) support multiple LUNs -# +CONFIG_CHR_DEV_SG=y # CONFIG_SCSI_MULTI_LUN is not set CONFIG_SCSI_CONSTANTS=y # CONFIG_SCSI_LOGGING is not set @@ -188,6 +155,7 @@ CONFIG_AIC7XXX_CMDS_PER_DEVICE=8 CONFIG_AIC7XXX_PROC_STATS=y CONFIG_AIC7XXX_RESET_DELAY=15 +# CONFIG_SCSI_IPS is not set # CONFIG_SCSI_ADVANSYS is not set # CONFIG_SCSI_IN2000 is not set # CONFIG_SCSI_AM53C974 is not set @@ -202,12 +170,11 @@ # CONFIG_SCSI_GENERIC_NCR5380 is not set # CONFIG_SCSI_INITIO is not set # CONFIG_SCSI_INIA100 is not set -# CONFIG_SCSI_PPA is not set -# CONFIG_SCSI_IMM is not set # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_SIM710 is not set # CONFIG_SCSI_NCR53C7xx is not set -CONFIG_SCSI_NCR53C8XX=y +# CONFIG_SCSI_NCR53C8XX is not set CONFIG_SCSI_SYM53C8XX=y CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8 CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32 @@ -215,7 +182,7 @@ # CONFIG_SCSI_NCR53C8XX_PROFILE is not set # CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set # CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set -CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT=y +# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set # CONFIG_SCSI_PAS16 is not set # CONFIG_SCSI_PCI2000 is not set # CONFIG_SCSI_PCI2220I is not set @@ -253,20 +220,21 @@ CONFIG_NET_ETHERNET=y CONFIG_MACE=y CONFIG_BMAC=y +# CONFIG_NCR885E is not set # CONFIG_NET_VENDOR_3COM is not set # CONFIG_LANCE is not set # CONFIG_NET_VENDOR_SMC is not set # CONFIG_NET_VENDOR_RACAL is not set # CONFIG_RTL8139 is not set -# CONFIG_SIS900 is not set # CONFIG_YELLOWFIN is not set # CONFIG_NET_ISA is not set CONFIG_NET_EISA=y -# CONFIG_PCNET32 is not set +CONFIG_PCNET32=y # CONFIG_ACENIC is not set # CONFIG_AC3200 is not set # CONFIG_APRICOT is not set # CONFIG_CS89x0 is not set +# CONFIG_DM9102 is not set CONFIG_DE4X5=y # CONFIG_DEC_ELCP is not set # CONFIG_DGRS is not set @@ -276,6 +244,7 @@ # CONFIG_NE2K_PCI is not set # CONFIG_TLAN is not set # CONFIG_VIA_RHINE is not set +# CONFIG_SIS900 is not set # CONFIG_ES3210 is not set # CONFIG_EPIC100 is not set # CONFIG_ZNET is not set @@ -289,12 +258,7 @@ # CONFIG_LTPC is not set # CONFIG_COPS is not set # CONFIG_IPDDP is not set -# CONFIG_PLIP is not set CONFIG_PPP=y - -# -# CCP compressors for PPP are only built as modules. -# # CONFIG_SLIP is not set # CONFIG_NET_RADIO is not set @@ -312,7 +276,9 @@ # CONFIG_HOSTESS_SV11 is not set # CONFIG_COSA is not set # CONFIG_SEALEVEL_4021 is not set +# CONFIG_COMX is not set # CONFIG_DLCI is not set +# CONFIG_SBNI is not set # # Amateur Radio support @@ -338,11 +304,14 @@ CONFIG_FB_CONTROL=y CONFIG_FB_PLATINUM=y CONFIG_FB_VALKYRIE=y -CONFIG_FB_ATY=y CONFIG_FB_IMSTT=y CONFIG_FB_CT65550=y # CONFIG_FB_S3TRIO is not set -# CONFIG_FB_MATROX is not set +CONFIG_FB_MATROX=y +# CONFIG_FB_MATROX_MILLENIUM is not set +CONFIG_FB_MATROX_MYSTIQUE=y +CONFIG_FB_MATROX_G100=y +# CONFIG_FB_MATROX_MULTIHEAD is not set CONFIG_FB_ATY=y # CONFIG_FB_VIRTUAL is not set # CONFIG_FBCON_ADVANCED is not set @@ -365,13 +334,22 @@ # CONFIG_VT=y CONFIG_VT_CONSOLE=y -# CONFIG_SERIAL is not set +CONFIG_SERIAL=m # CONFIG_SERIAL_EXTENDED is not set # CONFIG_SERIAL_NONSTANDARD is not set CONFIG_UNIX98_PTYS=y CONFIG_UNIX98_PTY_COUNT=256 -# CONFIG_PRINTER is not set -# CONFIG_MOUSE is not set +CONFIG_MOUSE=y + +# +# Mice +# +# CONFIG_ATIXL_BUSMOUSE is not set +# CONFIG_BUSMOUSE is not set +# CONFIG_MS_BUSMOUSE is not set +CONFIG_PSMOUSE=y +# CONFIG_82C710_MOUSE is not set +# CONFIG_PC110_PAD is not set # CONFIG_QIC02_TAPE is not set # CONFIG_WATCHDOG is not set CONFIG_NVRAM=y @@ -406,10 +384,10 @@ # CONFIG_ADFS_FS is not set # CONFIG_AFFS_FS is not set CONFIG_HFS_FS=y -CONFIG_FAT_FS=m -CONFIG_MSDOS_FS=m +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y # CONFIG_UMSDOS_FS is not set -CONFIG_VFAT_FS=m +CONFIG_VFAT_FS=y CONFIG_ISO9660_FS=y # CONFIG_JOLIET is not set # CONFIG_MINIX_FS is not set @@ -475,7 +453,7 @@ # CONFIG_NLS_ISO8859_8 is not set # CONFIG_NLS_ISO8859_9 is not set # CONFIG_NLS_ISO8859_14 is not set -CONFIG_NLS_ISO8859_15=y +# CONFIG_NLS_ISO8859_15 is not set # CONFIG_NLS_KOI8_R is not set # @@ -483,13 +461,43 @@ # CONFIG_SOUND=y CONFIG_DMASOUND=y +# CONFIG_SOUND_CMPCI is not set # CONFIG_SOUND_ES1370 is not set # CONFIG_SOUND_ES1371 is not set +# CONFIG_SOUND_MAESTRO is not set # CONFIG_SOUND_ESSSOLO1 is not set # CONFIG_SOUND_SONICVIBES is not set # CONFIG_SOUND_MSNDCLAS is not set # CONFIG_SOUND_MSNDPIN is not set -# CONFIG_SOUND_OSS is not set +CONFIG_SOUND_OSS=y +# CONFIG_SOUND_DMAP is not set +# CONFIG_SOUND_PAS is not set +# CONFIG_SOUND_SB is not set +# CONFIG_SOUND_GUS is not set +# CONFIG_SOUND_MPU401 is not set +# CONFIG_SOUND_PSS is not set +# CONFIG_SOUND_MSS is not set +# CONFIG_SOUND_SSCAPE is not set +# CONFIG_SOUND_TRIX is not set +# CONFIG_SOUND_VIA82CXXX is not set +# CONFIG_SOUND_MAD16 is not set +# CONFIG_SOUND_WAVEFRONT is not set +CONFIG_SOUND_CS4232=m +# CONFIG_SOUND_OPL3SA2 is not set +# CONFIG_SOUND_MAUI is not set +# CONFIG_SOUND_SGALAXY is not set +# CONFIG_SOUND_AD1816 is not set +# CONFIG_SOUND_OPL3SA1 is not set +# CONFIG_SOUND_SOFTOSS is not set +# CONFIG_SOUND_YM3812 is not set +# CONFIG_SOUND_VMIDI is not set +# CONFIG_SOUND_UART6850 is not set +# CONFIG_SOUND_NM256 is not set + +# +# Additional low level sound drivers +# +# CONFIG_LOWLEVEL_SOUND is not set # # Kernel hacking diff -u --recursive --new-file v2.2.13/linux/arch/ppc/gemini_defconfig linux/arch/ppc/gemini_defconfig --- v2.2.13/linux/arch/ppc/gemini_defconfig Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/gemini_defconfig Tue Jan 4 10:12:12 2000 @@ -0,0 +1,379 @@ +# +# Automatically generated make config: don't edit +# + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_6xx=y +# CONFIG_8xx is not set +# CONFIG_PMAC is not set +# CONFIG_PREP is not set +# CONFIG_CHRP is not set +# CONFIG_ALL_PPC is not set +# CONFIG_APUS is not set +CONFIG_GEMINI=y +# CONFIG_MBX is not set +# CONFIG_SMP is not set +CONFIG_MACH_SPECIFIC=y +CONFIG_6xx=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y +CONFIG_PCI=y +# CONFIG_PCI_QUIRKS is not set +# CONFIG_PCI_OLD_PROC is not set +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_BINFMT_JAVA is not set +# CONFIG_PARPORT is not set +# CONFIG_VGA_CONSOLE is not set +# CONFIG_FB is not set +# CONFIG_PMAC_PBOOK is not set +# CONFIG_MAC_KEYBOARD is not set +# CONFIG_MAC_FLOPPY is not set +# CONFIG_MAC_SERIAL is not set +# CONFIG_ADBMOUSE is not set +# CONFIG_PROC_DEVICETREE is not set +# CONFIG_TOTALMP is not set +# CONFIG_BOOTX_TEXT is not set +# CONFIG_MOTOROLA_HOTSWAP is not set + +# +# Plug and Play support +# +# CONFIG_PNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_IDE is not set + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_HD_ONLY is not set + +# +# Additional Block Devices +# +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_BLK_DEV_DAC960 is not set +CONFIG_PARIDE_PARPORT=y +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_DEV_HD is not set + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_NETLINK=y +# CONFIG_RTNETLINK is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_FIREWALL is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_IP_ROUTER is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +CONFIG_IP_ALIAS=y +CONFIG_SYN_COOKIES=y + +# +# (it is safe to leave these untouched) +# +# CONFIG_INET_RARP is not set +CONFIG_SKB_LARGE=y +# CONFIG_IPV6 is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_BRIDGE is not set +# CONFIG_LLC is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set +# CONFIG_CPU_IS_SLOW is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# SCSI support +# +CONFIG_SCSI=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +CONFIG_BLK_DEV_SR=y +CONFIG_BLK_DEV_SR_VENDOR=y +# CONFIG_CHR_DEV_SG is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AHA1740 is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_IPS is not set +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_MEGARAID is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_EATA_DMA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_G_NCR5380_PORT is not set +# CONFIG_SCSI_G_NCR5380_MEM is not set +# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_SIM710 is not set +# CONFIG_SCSI_NCR53C7xx is not set +# CONFIG_SCSI_NCR53C8XX is not set +CONFIG_SCSI_SYM53C8XX=y +CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8 +CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32 +CONFIG_SCSI_NCR53C8XX_SYNC=20 +# CONFIG_SCSI_NCR53C8XX_PROFILE is not set +# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set +# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set +# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PCI2000 is not set +# CONFIG_SCSI_PCI2220I is not set +# CONFIG_SCSI_PSI240I is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_QLOGIC_FC is not set +# CONFIG_SCSI_SEAGATE is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_MESH is not set +# CONFIG_SCSI_MAC53C94 is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +# CONFIG_ETHERTAP is not set +# CONFIG_NET_SB1000 is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_MACE is not set +# CONFIG_BMAC is not set +CONFIG_NCR885E=y +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_RTL8139 is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_NET_ISA is not set +# CONFIG_NET_EISA is not set +# CONFIG_NET_POCKET is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_NET_RADIO is not set + +# +# Token ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_HOSTESS_SV11 is not set +# CONFIG_COSA is not set +# CONFIG_SEALEVEL_4021 is not set +# CONFIG_COMX is not set +# CONFIG_DLCI is not set +# CONFIG_SBNI is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 +# CONFIG_MOUSE is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_WATCHDOG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set + +# +# Video For Linux +# +# CONFIG_VIDEO_DEV is not set + +# +# Joystick support +# +# CONFIG_JOYSTICK is not set +# CONFIG_DTLK is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_FT_NORMAL_DEBUG is not set +# CONFIG_FT_FULL_DEBUG is not set +# CONFIG_FT_NO_TRACE is not set +# CONFIG_FT_NO_TRACE_AT_ALL is not set +# CONFIG_FT_STD_FDC is not set +# CONFIG_FT_MACH2 is not set +# CONFIG_FT_PROBE_FC10 is not set +# CONFIG_FT_ALT_FDC is not set + +# +# USB drivers - not for the faint of heart +# +# CONFIG_USB is not set + +# +# Filesystems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_FAT_FS is not set +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +# CONFIG_EFS_FS is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_NFS_FS is not set +# CONFIG_NFSD is not set +# CONFIG_SUNRPC is not set +# CONFIG_LOCKD is not set +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set + +# +# Partition Types +# +# CONFIG_BSD_DISKLABEL is not set +CONFIG_MAC_PARTITION=y +# CONFIG_SMD_DISKLABEL is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# Kernel hacking +# +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_KGDB is not set +# CONFIG_XMON is not set diff -u --recursive --new-file v2.2.13/linux/arch/ppc/kernel/Makefile linux/arch/ppc/kernel/Makefile --- v2.2.13/linux/arch/ppc/kernel/Makefile Tue Oct 19 17:10:36 1999 +++ linux/arch/ppc/kernel/Makefile Tue Jan 4 10:12:12 2000 @@ -40,7 +40,8 @@ pmac_setup.o pmac_support.o \ prep_pci.o pmac_pci.o chrp_pci.o \ residual.o prom.o openpic.o feature.o \ - prep_nvram.o open_pic.o i8259.o pmac_pic.o indirect_pci.o + prep_nvram.o open_pic.o i8259.o pmac_pic.o indirect_pci.o \ + gemini_pci.o gemini_prom.o gemini_setup.o OX_OBJS += chrp_setup.o prep_setup.o endif endif diff -u --recursive --new-file v2.2.13/linux/arch/ppc/kernel/chrp_pci.c linux/arch/ppc/kernel/chrp_pci.c --- v2.2.13/linux/arch/ppc/kernel/chrp_pci.c Tue Jan 4 11:10:32 2000 +++ linux/arch/ppc/kernel/chrp_pci.c Tue Jan 4 10:12:12 2000 @@ -287,9 +287,6 @@ { if ( dev->irq ) dev->irq = openpic_to_irq( dev->irq ); - /* adjust the io_port for the NCR cards for busses other than 0 -- Cort */ - if ( (dev->bus->number > 0) && (dev->vendor == PCI_VENDOR_ID_NCR) ) - dev->base_address[0] += (dev->bus->number*0x08000000); /* these need to be absolute addrs for OF and Matrox FB -- Cort */ if ( dev->vendor == PCI_VENDOR_ID_MATROX ) { @@ -306,6 +303,10 @@ pcibios_write_config_word(dev->bus->number, dev->devfn, PCI_VENDOR_ID, PCI_VENDOR_ID_AMD); } + if ( (dev->bus->number > 0) && + ((dev->vendor == PCI_VENDOR_ID_NCR) || + (dev->vendor == PCI_VENDOR_ID_AMD))) + dev->base_address[0] += (dev->bus->number*0x08000000); } } diff -u --recursive --new-file v2.2.13/linux/arch/ppc/kernel/chrp_setup.c linux/arch/ppc/kernel/chrp_setup.c --- v2.2.13/linux/arch/ppc/kernel/chrp_setup.c Tue Oct 19 17:10:36 1999 +++ linux/arch/ppc/kernel/chrp_setup.c Tue Jan 4 10:12:12 2000 @@ -386,8 +386,8 @@ } } #endif /* __SMP__ */ - - irq = openpic_irq(0); + + irq = openpic_irq(smp_processor_id()); if (irq == IRQ_8259_CASCADE) { /* @@ -402,7 +402,7 @@ /* * Acknowledge as soon as possible to allow i8259 * interrupt nesting */ - openpic_eoi(0); + openpic_eoi(smp_processor_id()); openpic_eoi_done = 1; } if (irq == OPENPIC_VEC_SPURIOUS) @@ -429,7 +429,7 @@ } out: if (!openpic_eoi_done) - openpic_eoi(0); + openpic_eoi(smp_processor_id()); } __initfunc(void @@ -446,12 +446,9 @@ (*(unsigned long *)get_property(np, "8259-interrupt-acknowledge", NULL)); } + open_pic.irq_offset = 16; for ( i = 16 ; i < NR_IRQS ; i++ ) irq_desc[i].ctl = &open_pic; - /* openpic knows that it's at irq 16 offset - * so we don't need to set it in the pic structure - * -- Cort - */ openpic_init(1); for ( i = 0 ; i < 16 ; i++ ) irq_desc[i].ctl = &i8259_pic; @@ -461,8 +458,14 @@ xmon_irq, 0, "NMI", 0); #endif /* CONFIG_XMON */ #ifdef __SMP__ - request_irq(openpic_to_irq(OPENPIC_VEC_IPI), - openpic_ipi_action, 0, "IPI0", 0); + request_irq(OPENPIC_VEC_IPI, openpic_ipi_action, + 0, "IPI0", 0); + request_irq(OPENPIC_VEC_IPI+1, openpic_ipi_action, + 0, "IPI1 (invalidate TLB)", 0); + request_irq(OPENPIC_VEC_IPI+2, openpic_ipi_action, + 0, "IPI2 (stop CPU)", 0); + request_irq(OPENPIC_VEC_IPI+3, openpic_ipi_action, + 0, "IPI3 (reschedule)", 0); #endif /* __SMP__ */ } @@ -471,7 +474,6 @@ { adb_init(); - /* Should this be here? - Corey */ pmac_nvram_init(); #ifdef CONFIG_VT diff -u --recursive --new-file v2.2.13/linux/arch/ppc/kernel/gemini_pci.c linux/arch/ppc/kernel/gemini_pci.c --- v2.2.13/linux/arch/ppc/kernel/gemini_pci.c Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/kernel/gemini_pci.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,273 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "pci.h" + +#define pci_config_addr(bus,dev,offset) \ + (0x80000000 | (bus<<16) | (dev<<8) | offset) + + +int +gemini_pcibios_read_config_byte(unsigned char bus, unsigned char dev, + unsigned char offset, unsigned char *val) +{ + unsigned long reg; + reg = grackle_read( pci_config_addr( bus, dev, (offset & ~(0x3)))); + *val = ((reg >> ((offset & 0x3) << 3)) & 0xff); + return PCIBIOS_SUCCESSFUL; +} + +int +gemini_pcibios_read_config_word(unsigned char bus, unsigned char dev, + unsigned char offset, unsigned short *val) +{ + unsigned long reg; + reg = grackle_read( pci_config_addr( bus, dev, (offset & ~(0x3)))); + *val = ((reg >> ((offset & 0x3) << 3)) & 0xffff); + return PCIBIOS_SUCCESSFUL; +} + +int +gemini_pcibios_read_config_dword(unsigned char bus, unsigned char dev, + unsigned char offset, unsigned int *val) +{ + *val = grackle_read( pci_config_addr( bus, dev, (offset & ~(0x3)))); + return PCIBIOS_SUCCESSFUL; +} + +int +gemini_pcibios_write_config_byte(unsigned char bus, unsigned char dev, + unsigned char offset, unsigned char val) +{ + unsigned long reg; + int shifts = offset & 0x3; + + reg = grackle_read( pci_config_addr( bus, dev, (offset & ~(0x3)))); + reg = (reg & ~(0xff << (shifts << 3))) | (val << (shifts << 3)); + grackle_write( pci_config_addr( bus, dev, (offset & ~(0x3))), reg ); + return PCIBIOS_SUCCESSFUL; +} + +int +gemini_pcibios_write_config_word(unsigned char bus, unsigned char dev, + unsigned char offset, unsigned short val) +{ + unsigned long reg; + int shifts = offset & 0x3; + + reg = grackle_read( pci_config_addr( bus, dev, (offset & ~(0x3)))); + reg = (reg & ~(0xffff << (shifts << 3))) | (val << (shifts << 3)); + grackle_write( pci_config_addr( bus, dev, (offset & ~(0x3))), reg ); + return PCIBIOS_SUCCESSFUL; +} + +int +gemini_pcibios_write_config_dword(unsigned char bus, unsigned char dev, + unsigned char offset, unsigned int val) +{ + grackle_write( pci_config_addr( bus, dev, (offset & ~(0x3))), val ); + return PCIBIOS_SUCCESSFUL; +} + +struct gemini_device { + unsigned short vendor, device; + unsigned char irq; + unsigned short cmd; + unsigned char cache_line, latency; + void (*init)(struct pci_dev *dev); +}; + +static struct gemini_device gemini_map[] = { + { PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C885, 11, 0x15, 32, 248, NULL }, + { PCI_VENDOR_ID_NCR, 0x701, 10, 0, 0, 0, NULL }, + { PCI_VENDOR_ID_TUNDRA, PCI_DEVICE_ID_TUNDRA_CA91C042, 3, 0, 0, 0, NULL }, + { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_MPIC, 0xff, 0, 0, 0, NULL }, + { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_670, 0xff, 0, 0, 0, NULL }, + { PCI_VENDOR_ID_MOTOROLA, PCI_DEVICE_ID_MOTOROLA_MPC106, 0xff, 0, 0, 0, NULL }, +}; + +static int gemini_map_count = (sizeof( gemini_map ) / + sizeof( gemini_map[0] )); + + + +/* This just sets up the known devices on the board. */ +__initfunc(static void mapin_device( struct pci_dev *dev )) +{ + struct gemini_device *p; + unsigned short cmd; + int i; + + + for( i=0; i < gemini_map_count; i++ ) { + p = &(gemini_map[i]); + + if ( p->vendor == dev->vendor && + p->device == dev->device ) { + + if (p->irq != 0xff) { + pci_write_config_byte( dev, PCI_INTERRUPT_LINE, p->irq ); + dev->irq = p->irq; + } + + if (p->cmd) { + pci_read_config_word( dev, PCI_COMMAND, &cmd ); + pci_write_config_word( dev, PCI_COMMAND, (p->cmd|cmd)); + } + + if (p->cache_line) + pci_write_config_byte( dev, PCI_CACHE_LINE_SIZE, p->cache_line ); + + if (p->latency) + pci_write_config_byte( dev, PCI_LATENCY_TIMER, p->latency ); + } + } +} + +#define KB 1024 +#define MB (KB*KB) + +#define ALIGN(val,align) (((val) + ((align) -1))&(~((align) -1))) +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) + +#define FIRST_IO_ADDR 0x10000 +#define FIRST_MEM_ADDR 0x02000000 + +#define GEMINI_PCI_MEM_BASE (0xf0000000) +#define GEMINI_PCI_IO_BASE (0xfe800000) + +static unsigned long pci_mem_base = GEMINI_PCI_MEM_BASE; +static unsigned long pci_io_base = GEMINI_PCI_IO_BASE; + +static unsigned int io_base = FIRST_IO_ADDR; +static unsigned int mem_base = FIRST_MEM_ADDR; + + + +__initfunc(static void layout_dev( struct pci_dev *dev )) +{ + int i; + struct pci_bus *bus; + unsigned short cmd; + unsigned int reg, base, mask, size, alignto, type; + + bus = dev->bus; + + /* make any known settings happen */ + mapin_device( dev ); + + gemini_pcibios_read_config_word( bus->number, dev->devfn, PCI_COMMAND, &cmd ); + + for( reg = PCI_BASE_ADDRESS_0, i=0; reg <= PCI_BASE_ADDRESS_5; reg += 4, i++ ) { + + /* MPIC already done */ + if (dev->vendor == PCI_VENDOR_ID_IBM && + dev->device == PCI_DEVICE_ID_IBM_MPIC) + return; + + gemini_pcibios_write_config_dword( bus->number, dev->devfn, reg, 0xffffffff ); + gemini_pcibios_read_config_dword( bus->number, dev->devfn, reg, &base ); + if (!base) { + dev->base_address[i] = 0; + continue; + } + + if (base & PCI_BASE_ADDRESS_SPACE_IO) { + cmd |= PCI_COMMAND_IO; + base &= PCI_BASE_ADDRESS_IO_MASK; + mask = (~base << 1) | 0x1; + size = (mask & base) & 0xffffffff; + alignto = MAX(0x400, size); + base = ALIGN(io_base, alignto); + io_base = base + size; + gemini_pcibios_write_config_dword( bus->number, dev->devfn, reg, + ((pci_io_base + base) & 0x00ffffff) | 0x1); + dev->base_address[i] = (pci_io_base + base) | 0x1; + } + + else { + cmd |= PCI_COMMAND_MEMORY; + type = base & PCI_BASE_ADDRESS_MEM_TYPE_MASK; + mask = (~base << 1) | 0x1; + size = (mask & base) & 0xffffffff; + switch( type ) { + + case PCI_BASE_ADDRESS_MEM_TYPE_32: + break; + case PCI_BASE_ADDRESS_MEM_TYPE_64: + printk("Warning: Ignoring 64-bit device; slot %d, function %d.\n", + PCI_SLOT( dev->devfn ), PCI_FUNC( dev->devfn )); + reg += 4; + continue; + } + + alignto = MAX(0x1000, size); + base = ALIGN(mem_base, alignto); + mem_base = base + size; + gemini_pcibios_write_config_dword( bus->number, dev->devfn, + reg, (pci_mem_base + base)); + dev->base_address[i] = pci_mem_base + base; + } + } + + if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) + cmd |= PCI_COMMAND_IO; + + gemini_pcibios_write_config_word( bus->number, dev->devfn, PCI_COMMAND, + (cmd|PCI_COMMAND_MASTER)); + +} + +__initfunc(static void layout_bus( struct pci_bus *bus )) +{ + struct pci_dev *dev; + + if (!bus->devices && !bus->children) + return; + + io_base = ALIGN(io_base, 4*KB); + mem_base = ALIGN(mem_base, 4*KB); + + for( dev = bus->devices; dev; dev = dev->sibling ) { + if (((dev->class >> 16) != PCI_BASE_CLASS_BRIDGE) || + ((dev->class >> 8) == PCI_CLASS_BRIDGE_OTHER)) + layout_dev( dev ); + } +} + + +__initfunc(void gemini_pcibios_fixup(void)) +{ + struct pci_bus *bus; + unsigned long orig_mem_base, orig_io_base; + + orig_mem_base = pci_mem_base; + orig_io_base = pci_io_base; + + for( bus = &pci_root; bus; bus = bus->children ) + layout_bus( bus ); + + pci_mem_base = orig_mem_base; + pci_io_base = orig_io_base; + + +} + +decl_config_access_method(gemini); + +/* The "bootloader" for Synergy boards does none of this for us, so we need to + lay it all out ourselves... --Dan */ +__initfunc(void gemini_setup_pci_ptrs(void)) +{ + + set_config_access_method(gemini); + ppc_md.pcibios_fixup = gemini_pcibios_fixup; +} diff -u --recursive --new-file v2.2.13/linux/arch/ppc/kernel/gemini_prom.S linux/arch/ppc/kernel/gemini_prom.S --- v2.2.13/linux/arch/ppc/kernel/gemini_prom.S Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/kernel/gemini_prom.S Tue Jan 4 10:12:12 2000 @@ -0,0 +1,94 @@ +/* + * arch/ppc/kernel/gemini_prom.S + * + * Not really prom support code (yet), but sort of anti-prom code. The current + * bootloader does a number of things it shouldn't and doesn't do things that it + * should. The stuff in here is mainly a hodge-podge collection of setup code + * to get the board up and running. + * ---Dan + */ + +#include "ppc_asm.tmpl" +#include "ppc_defs.h" +#include +#include +#include + +#define HID0_ABE (1<<3) + +/* + * On 750's the MMU is on when Linux is booted, so we need to clear out the + * bootloader's BAT settings, make sure we're in supervisor state (gotcha!), + * and turn off the MMU. + * + */ + +_GLOBAL(gemini_prom_init) +#ifdef __SMP__ + /* Since the MMU's on, get stuff in rom space that we'll need */ + lis r4,GEMINI_CPUSTAT@h + ori r4,r4,GEMINI_CPUSTAT@l + lbz r5,0(r4) + andi. r5,r5,3 + mr r24,r5 /* cpu # used later on */ +#endif + mfmsr r4 + li r3,MSR_PR /* ensure supervisor! */ + ori r3,r3,MSR_IR|MSR_DR + andc r4,r4,r3 + mtmsr r4 +#if 0 + /* zero out the bats now that the MMU is off */ +prom_no_mmu: + li r3,0 + mtspr IBAT0U,r3 + mtspr IBAT0L,r3 + mtspr IBAT1U,r3 + mtspr IBAT1L,r3 + mtspr IBAT2U,r3 + mtspr IBAT2L,r3 + mtspr IBAT3U,r3 + mtspr IBAT3L,r3 + + mtspr DBAT0U,r3 + mtspr DBAT0L,r3 + mtspr DBAT1U,r3 + mtspr DBAT1L,r3 + mtspr DBAT2U,r3 + mtspr DBAT2L,r3 + mtspr DBAT3U,r3 + mtspr DBAT3L,r3 +#endif + + /* the bootloader (as far as I'm currently aware) doesn't mess with page + tables, but since we're already here, might as well zap these, too */ + li r4,0 + mtspr SDR1,r4 + + li r4,16 + mtctr r4 + li r3,0 + li r4,0 +3: mtsrin r3,r4 + addi r3,r3,1 + bdnz 3b + +#ifdef __SMP__ + /* The 750 book (and Mot/IBM support) says that this will "assist" snooping + when in SMP. Not sure yet whether this should stay or leave... */ + mfspr r4,HID0 + ori r4,r4,HID0_ABE + mtspr HID0,r4 + sync +#endif /* __SMP__ */ + blr + +/* apparently, SMon doesn't pay attention to HID0[SRST]. Disable the MMU and + branch to 0xfff00100 */ +_GLOBAL(_gemini_reboot) + lis r5,GEMINI_BOOT_INIT@h + ori r5,r5,GEMINI_BOOT_INIT@l + li r6,MSR_IP + mtspr SRR0,r5 + mtspr SRR1,r6 + rfi diff -u --recursive --new-file v2.2.13/linux/arch/ppc/kernel/gemini_setup.c linux/arch/ppc/kernel/gemini_setup.c --- v2.2.13/linux/arch/ppc/kernel/gemini_setup.c Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/kernel/gemini_setup.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,576 @@ +/* + * linux/arch/ppc/kernel/setup.c + * + * Copyright (C) 1995 Linus Torvalds + * Adapted from 'alpha' version by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + * Synergy Microsystems board support by Dan Cox (dan@synergymicro.com) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "time.h" +#include "local_irq.h" +#include "open_pic.h" + +void gemini_setup_pci_ptrs(void); + +static int l2_printed = 0; +static unsigned char gemini_switch_map = 0; +static char *gemini_board_families[] = { + "VGM", "VSS", "KGM", "VGR", "KSS" +}; + +static char *gemini_memtypes[] = { + "EDO DRAM, 60nS", "SDRAM, 15nS, CL=2", "SDRAM, 15nS, CL=2 with ECC" +}; + +static unsigned int cpu_7xx[16] = { + 0, 15, 14, 0, 0, 13, 5, 9, 6, 11, 8, 10, 16, 12, 7, 0 +}; +static unsigned int cpu_6xx[16] = { + 0, 0, 14, 0, 0, 13, 5, 9, 6, 11, 8, 10, 0, 12, 7, 0 +}; + +void +gemini_do_IRQ(struct pt_regs *regs, int cpu, int isfake) +{ + int irq, openpic_eoi_done = 0; +#ifdef __SMP__ + { + unsigned int loops = 1000000; + while(test_bit(0, &global_irq_lock)) { + if (smp_processor_id() == global_irq_holder) { + printk("uh oh, it chokes!!! interrupt while we hold irq lock\n"); + break; + } + if (loops-- == 0) { + printk("do_IRQ waiting for irq lock (holder=%d)\n", + global_irq_holder); + } + } + } +#endif + + irq = openpic_irq(cpu); + + if (irq == OPENPIC_VEC_SPURIOUS ) { + ppc_spurious_interrupts++; + openpic_eoi_done = 1; + goto out; + } + + if (irq < 0) { + printk(KERN_DEBUG "Bogus interrupt %d from pc=%lx\n", irq, + regs->nip); + ppc_spurious_interrupts++; + } + + else + ppc_irq_dispatch_handler(regs, irq); + out: + if (!openpic_eoi_done) + openpic_eoi(smp_processor_id()); +} + +static inline unsigned long _get_HID1(void) +{ + unsigned long val; + + __asm__ __volatile__("mfspr %0,1009" : "=r" (val)); + return val; +} + +int +gemini_get_cpuinfo(char *buffer) +{ + int i, len; + unsigned char reg, rev; + char *family; + unsigned int type; + + reg = readb(GEMINI_FEAT); + family = gemini_board_families[((reg>>4) & 0xf)]; + if (((reg>>4) & 0xf) > 2) + printk(KERN_ERR "cpuinfo(): unable to determine board family\n"); + + reg = readb(GEMINI_BREV); + type = (reg>>4) & 0xf; + rev = reg & 0xf; + + reg = readb(GEMINI_BECO); + + len = sprintf( buffer, "machine\t\t: Gemini %s%d, rev %c, eco %d\n", + family, type, (rev + 'A'), (reg & 0xf)); + + len += sprintf( buffer+len, "vendor\t\t: %s\n", + (_get_PVR() & (1<<15)) ? "IBM" : "Motorola"); + + reg = readb(GEMINI_MEMCFG); + len += sprintf( buffer+len, "memory type\t: %s\n", + gemini_memtypes[(reg & 0xc0)>>6]); + len += sprintf( buffer+len, "switches on\t: "); + for( i=0; i < 8; i++ ) { + if ( gemini_switch_map & (1<>16) == 8) + hid1 = cpu_7xx[hid1]; + else + hid1 = cpu_6xx[hid1]; + + reg = readb(GEMINI_BSTAT) & 0xc0; + + switch( reg >> 2 ) { + + case 0: + default: + clock = (hid1*100)/3; + break; + + case 1: + clock = (hid1*125)/3; + break; + + case 2: + clock = (hid1*50)/3; + break; + } + + return clock; +} + + +#define L2CR_PIPE_LATEWR (0x01800000) /* late-write SRAM */ +#define L2CR_L2CTL (0x00100000) /* RAM control */ +#define L2CR_INST_DISABLE (0x00400000) /* disable for insn's */ +#define L2CR_L2I (0x00200000) /* global invalidate */ +#define L2CR_L2E (0x80000000) /* enable */ +#define L2CR_L2WT (0x00080000) /* write-through */ + +void __init gemini_init_l2(void) +{ + unsigned char reg; + unsigned long cache; + int speed; + + reg = readb(GEMINI_L2CFG); + + /* 750's L2 initializes differently from a 604's. Also note that a Grackle + bug will hang a dual-604 board, so make sure that doesn't happen by not + turning on the L2 */ + if ( _get_PVR() >> 16 != 8 ) { + + /* check for dual cpus and cry sadly about the loss of an L2... */ + if ((( readb(GEMINI_CPUSTAT) & 0x0c ) >> 2) != 1) + printk("Sorry. Your dual-604 does not allow the L2 to be enabled due " + "to a Grackle bug.\n"); + else if ( reg & GEMINI_L2_SIZE_MASK ) { + printk("Enabling 604 L2 cache: %dKb\n", + (128<<((reg & GEMINI_L2_SIZE_MASK)>>6))); + writeb( 1, GEMINI_L2CFG ); + } + } + + /* do a 750 */ + else { + /* Synergy's first round of 750 boards had the L2 size stuff into the + board register above. If it's there, it's used; if not, the + standard default is 1Mb. The L2 type, I'm told, is "most likely + probably always going to be late-write". --Dan */ + + if (reg & 0xc0) { + if (!l2_printed) { + printk("Enabling 750 L2 cache: %dKb\n", + (128 << ((reg & 0xc0)>>6))); + l2_printed=1; + } + + /* take the size given */ + cache = (((reg>>6) & 0x3)<<28); + } + else + /* default of 1Mb */ + cache = 0x3<<28; + + reg &= 0x3; + + /* a cache ratio of 1:1 and CPU clock speeds in excess of 300Mhz are bad + things. If found, tune it down to 1:1.5. -- Dan */ + if (!reg) { + + speed = gemini_get_clock_speed(); + + if (speed >= 300) { + printk("Warning: L2 ratio is 1:1 on a %dMhz processor. Dropping to 1:1.5.\n", + speed ); + printk("Contact Synergy Microsystems for an ECO to fix this problem\n"); + reg = 0x1; + } + } + + /* standard stuff */ + cache |= ((1<>8) & 0xf) <= 2) + cache |= L2CR_L2WT; +#endif + cache |= L2CR_PIPE_LATEWR|L2CR_L2CTL|L2CR_INST_DISABLE; + _set_L2CR(0); + _set_L2CR(cache|L2CR_L2I|L2CR_L2E); + } +} + +void +gemini_restart(char *cmd) +{ + __cli(); + /* make a clean restart, not via the MPIC */ + _gemini_reboot(); + for(;;); +} + +void +gemini_power_off(void) +{ + for(;;); +} + +void +gemini_halt(void) +{ + gemini_restart(NULL); +} + +void __init gemini_init_IRQ(void) +{ + int i; + + /* gemini has no 8259 */ + open_pic.irq_offset = 0; + for( i=0; i < NR_IRQS; i++ ) + irq_desc[i].ctl = &open_pic; + openpic_init(1); +#ifdef __SMP__ + request_irq(OPENPIC_VEC_IPI, openpic_ipi_action, + 0, "IPI0", 0); + request_irq(OPENPIC_VEC_IPI+1, openpic_ipi_action, + 0, "IPI1 (invalidate TLB)", 0); + request_irq(OPENPIC_VEC_IPI+2, openpic_ipi_action, + 0, "IPI2 (stop CPU)", 0); + request_irq(OPENPIC_VEC_IPI+3, openpic_ipi_action, + 0, "IPI3 (reschedule)", 0); +#endif /* __SMP__ */ +} + +#define gemini_rtc_read(x) (readb(GEMINI_RTC+(x))) +#define gemini_rtc_write(val,x) (writeb((val),(GEMINI_RTC+(x)))) + +/* ensure that the RTC is up and running */ +void __init gemini_time_init(void) +{ + unsigned char reg; + + reg = gemini_rtc_read(M48T35_RTC_CONTROL); + + if ( reg & M48T35_RTC_STOPPED ) { + printk(KERN_INFO "M48T35 real-time-clock was stopped. Now starting...\n"); + gemini_rtc_write((reg & ~(M48T35_RTC_STOPPED)), M48T35_RTC_CONTROL); + gemini_rtc_write((reg | M48T35_RTC_SET), M48T35_RTC_CONTROL); + } +} + +#undef DEBUG_RTC + +unsigned long +gemini_get_rtc_time(void) +{ + unsigned int year, mon, day, hour, min, sec; + unsigned char reg; + + reg = gemini_rtc_read(M48T35_RTC_CONTROL); + gemini_rtc_write((reg|M48T35_RTC_READ), M48T35_RTC_CONTROL); +#ifdef DEBUG_RTC + printk("get rtc: reg = %x\n", reg); +#endif + + do { + sec = gemini_rtc_read(M48T35_RTC_SECONDS); + min = gemini_rtc_read(M48T35_RTC_MINUTES); + hour = gemini_rtc_read(M48T35_RTC_HOURS); + day = gemini_rtc_read(M48T35_RTC_DOM); + mon = gemini_rtc_read(M48T35_RTC_MONTH); + year = gemini_rtc_read(M48T35_RTC_YEAR); + } while( sec != gemini_rtc_read(M48T35_RTC_SECONDS)); +#ifdef DEBUG_RTC + printk("get rtc: sec=%x, min=%x, hour=%x, day=%x, mon=%x, year=%x\n", + sec, min, hour, day, mon, year); +#endif + + gemini_rtc_write(reg, M48T35_RTC_CONTROL); + + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + + if ((year += 1900) < 1970) + year += 100; +#ifdef DEBUG_RTC + printk("get rtc: sec=%x, min=%x, hour=%x, day=%x, mon=%x, year=%x\n", + sec, min, hour, day, mon, year); +#endif + + return mktime( year, mon, day, hour, min, sec ); +} + + +int +gemini_set_rtc_time( unsigned long now ) +{ + unsigned char reg; + struct rtc_time tm; + + to_tm( now, &tm ); + + reg = gemini_rtc_read(M48T35_RTC_CONTROL); +#if DEBUG_RTC + printk("set rtc: reg = %x\n", reg); +#endif + + gemini_rtc_write((reg|M48T35_RTC_SET), M48T35_RTC_CONTROL); +#if DEBUG_RTC + printk("set rtc: tm vals - sec=%x, min=%x, hour=%x, mon=%x, mday=%x, year=%x\n", + tm.tm_sec, tm.tm_min, tm.tm_hour, tm.tm_mon, tm.tm_mday, tm.tm_year); +#endif + + tm.tm_year -= 1900; + BIN_TO_BCD(tm.tm_sec); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_mon); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_year); +#ifdef DEBUG_RTC + printk("set rtc: tm vals - sec=%x, min=%x, hour=%x, mon=%x, mday=%x, year=%x\n", + tm.tm_sec, tm.tm_min, tm.tm_hour, tm.tm_mon, tm.tm_mday, tm.tm_year); +#endif + + gemini_rtc_write(tm.tm_sec, M48T35_RTC_SECONDS); + gemini_rtc_write(tm.tm_min, M48T35_RTC_MINUTES); + gemini_rtc_write(tm.tm_hour, M48T35_RTC_HOURS); + gemini_rtc_write(tm.tm_mday, M48T35_RTC_DOM); + gemini_rtc_write(tm.tm_mon, M48T35_RTC_MONTH); + gemini_rtc_write(tm.tm_year, M48T35_RTC_YEAR); + + /* done writing */ + gemini_rtc_write(reg, M48T35_RTC_CONTROL); + + if ((time_state == TIME_ERROR) || (time_state == TIME_BAD)) + time_state = TIME_OK; + + return 0; +} + +/* use the RTC to determine the decrementer count */ +void __init gemini_calibrate_decr(void) +{ + int freq, divisor; + unsigned char reg; + + /* determine processor bus speed */ + reg = readb(GEMINI_BSTAT); + + switch(((reg & 0x0c)>>2)&0x3) { + case 0: + default: + freq = 66; + break; + case 1: + freq = 83; + break; + case 2: + freq = 100; + break; + } + + freq *= 1000000; + divisor = 4; + decrementer_count = freq / HZ / divisor; + count_period_num = divisor; + count_period_den = freq / 1000000; +} + + +void __init gemini_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + void layout_bus( struct pci_bus * ); + + gemini_setup_pci_ptrs(); + + ISA_DMA_THRESHOLD = 0; + DMA_MODE_READ = 0; + DMA_MODE_WRITE = 0; + +#ifdef CONFIG_BLK_DEV_INITRD + if ( r4 ) + { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif + + ppc_md.setup_arch = gemini_setup_arch; + ppc_md.setup_residual = NULL; + ppc_md.get_cpuinfo = gemini_get_cpuinfo; + ppc_md.irq_cannonicalize = NULL; + ppc_md.init_IRQ = gemini_init_IRQ; + ppc_md.do_IRQ = gemini_do_IRQ; + ppc_md.init = NULL; + + ppc_md.restart = gemini_restart; + ppc_md.power_off = gemini_power_off; + ppc_md.halt = gemini_halt; + + ppc_md.time_init = gemini_time_init; + ppc_md.set_rtc_time = gemini_set_rtc_time; + ppc_md.get_rtc_time = gemini_get_rtc_time; + ppc_md.calibrate_decr = gemini_calibrate_decr; + + /* no keyboard/mouse/video stuff yet.. */ + ppc_md.kbd_setkeycode = NULL; + ppc_md.kbd_getkeycode = NULL; + ppc_md.kbd_translate = NULL; + ppc_md.kbd_unexpected_up = NULL; + ppc_md.kbd_leds = NULL; + ppc_md.kbd_init_hw = NULL; +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.ppc_kbd_sysrq_xlate = NULL; +#endif +} diff -u --recursive --new-file v2.2.13/linux/arch/ppc/kernel/head.S linux/arch/ppc/kernel/head.S --- v2.2.13/linux/arch/ppc/kernel/head.S Tue Oct 19 17:10:36 1999 +++ linux/arch/ppc/kernel/head.S Tue Jan 4 10:12:12 2000 @@ -1,7 +1,7 @@ /* * arch/ppc/kernel/head.S * - * $Id: head.S,v 1.130.2.3 1999/08/10 21:36:48 cort Exp $ + * $Id: head.S,v 1.130.2.6 1999/10/12 01:03:34 cort Exp $ * * PowerPC version * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) @@ -97,21 +97,12 @@ bdnz 0b #endif -#ifdef CONFIG_PPC64 +/* 601 only have IBAT cr0.eq is set on 601 when using this macro */ #define LOAD_BAT(n, offset, reg, RA, RB) \ - ld RA,offset+0(reg); \ - ld RB,offset+8(reg); \ + /* see the comment for clear_bats() -- Cort */ \ + li RA,0; \ mtspr IBAT##n##U,RA; \ - mtspr IBAT##n##L,RB; \ - ld RA,offset+16(reg); \ - ld RB,offset+24(reg); \ mtspr DBAT##n##U,RA; \ - mtspr DBAT##n##L,RB; \ - -#else /* CONFIG_PPC64 */ - -/* 601 only have IBAT cr0.eq is set on 601 when using this macro */ -#define LOAD_BAT(n, offset, reg, RA, RB) \ lwz RA,offset+0(reg); \ lwz RB,offset+4(reg); \ mtspr IBAT##n##U,RA; \ @@ -122,7 +113,6 @@ mtspr DBAT##n##U,RA; \ mtspr DBAT##n##L,RB; \ 1: -#endif /* CONFIG_PPC64 */ #ifndef CONFIG_APUS #define tophys(rd,rs,rt) addis rd,rs,-KERNELBASE@h @@ -149,7 +139,15 @@ .text .globl _start _start: - .long TOPHYS(__start),0,0 + /* + * These are here for legacy reasons, the kernel used to + * need to look like a coff function entry for the pmac + * but we're always started by some kind of bootloader now. + * -- Cort + */ + nop + nop + nop /* PMAC * Enter here with the kernel text, data and bss loaded starting at @@ -220,16 +218,6 @@ .globl __start __start: -#ifdef CONFIG_PPC64 -/* - * Go into 32-bit mode to boot. OF should do this for - * us already but just in case... - * -- Cort - */ - mfmsr r10 - clrldi r10,r10,3 - mtmsr r10 -#endif /* * We have to do any OF calls before we map ourselves to KERNELBASE, * because OF may have I/O devices mapped in in that area @@ -245,13 +233,16 @@ bl prom_init .globl __secondary_start __secondary_start: -/* - * Use the first pair of BAT registers to map the 1st 16MB +/* Switch MMU off, clear BATs and flush TLB */ + bl mmu_off + bl clear_bats + bl flush_tlbs + +/* Use the first pair of BAT registers to map the 1st 16MB * of RAM to KERNELBASE. From this point on we can't safely * call OF any more. */ lis r11,KERNELBASE@h -#ifndef CONFIG_PPC64 mfspr r9,PVR rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */ cmpi 0,r9,1 @@ -265,7 +256,6 @@ mtspr IBAT1U,r9 mtspr IBAT1L,r10 b 5f -#endif /* CONFIG_PPC64 */ 4: #ifdef CONFIG_APUS ori r11,r11,BL_8M<<2|0x2 /* set up an 8MB mapping */ @@ -277,14 +267,10 @@ #else ori r11,r11,BL_256M<<2|0x2 /* set up BAT registers for 604 */ li r8,2 /* R/W access */ -#ifdef CONFIG_PPC64 - /* clear out the high 32 bits in the BAT */ - clrldi r11,r11,32 - clrldi r8,r8,32 - /* turn off the pagetable mappings just in case */ - clrldi r16,r16,63 - mtsdr1 r16 -#else /* CONFIG_PPC64 */ + /* + * If the MMU is off clear the bats. See clear_bat() -- Cort + */ +#ifndef CONFIG_GEMINI /* * allow secondary cpus to get at all of ram in early bootup * since their init_task may be up there -- Cort @@ -302,14 +288,13 @@ mtspr DBAT2U,r21 /* bit in upper BAT register */ mtspr IBAT2L,r18 mtspr IBAT2U,r21 -#endif /* CONFIG_PPC64 */ +#endif /* ndef CONFIG_GEMINI */ #endif mtspr DBAT0L,r8 /* N.B. 6xx (not 601) have valid */ mtspr DBAT0U,r11 /* bit in upper BAT register */ mtspr IBAT0L,r8 mtspr IBAT0U,r11 5: isync - #ifdef CONFIG_APUS /* Unfortunately the APUS specific instructions bloat the * code so it cannot fit in the 0x100 bytes available. We have @@ -499,7 +484,12 @@ /* System reset */ #ifdef CONFIG_SMP /* MVME/MTX start the secondary here */ +#ifdef CONFIG_GEMINI + . = 0x100 + b __secondary_start_gemini +#else /* CONFIG_GEMINI */ STD_EXCEPTION(0x100, Reset, __secondary_start_psurge) +#endif /* CONFIG_GEMINI */ #else STD_EXCEPTION(0x100, Reset, UnknownException) #endif @@ -1269,6 +1259,7 @@ .globl hash_page hash_page: #ifdef __SMP__ + SAVE_2GPRS(7,r21) eieio lis r2,hash_table_lock@h ori r2,r2,hash_table_lock@l @@ -1282,10 +1273,18 @@ bne- 12f stwcx. r0,0,r2 beq+ 11f -12: cmpw r6,r0 + /* spin here a bit */ +12: mfctr r7 + li r8,1000 + mtctr r8 +13: + bdnz 13b + mtctr r7 + cmpw r6,r0 bdnzf 2,10b tw 31,31,31 11: eieio + REST_2GPRS(7, r21) #endif /* Get PTE (linux-style) and check access */ lwz r5,PG_TABLES(r5) @@ -1312,7 +1311,6 @@ #else bnelr- #endif - ori r6,r6,0x100 /* set _PAGE_ACCESSED in pte */ rlwinm r5,r4,5,24,24 /* _PAGE_RW access -> _PAGE_DIRTY */ rlwimi r5,r4,7,22,22 /* _PAGE_RW -> _PAGE_HWWRITE */ @@ -1320,11 +1318,6 @@ stw r6,0(r2) /* update PTE (accessed/dirty bits) */ /* Convert linux-style PTE to low word of PPC-style PTE */ -#ifdef CONFIG_PPC64 - /* clear the high 32 bits just in case */ - clrldi r6,r6,32 - clrldi r4,r4,32 -#endif /* CONFIG_PPC64 */ rlwinm r4,r6,32-9,31,31 /* _PAGE_HWWRITE -> PP lsb */ rlwimi r6,r6,32-1,31,31 /* _PAGE_USER -> PP (both bits now) */ ori r4,r4,0xe04 /* clear out reserved bits */ @@ -1332,34 +1325,17 @@ /* Construct the high word of the PPC-style PTE */ mfsrin r5,r3 /* get segment reg for segment */ -#ifdef CONFIG_PPC64 - sldi r5,r5,12 -#else /* CONFIG_PPC64 */ rlwinm r5,r5,7,1,24 /* put VSID in 0x7fffff80 bits */ -#endif /* CONFIG_PPC64 */ #ifndef __SMP__ /* do this later for SMP */ -#ifdef CONFIG_PPC64 - ori r5,r5,1 /* set V (valid) bit */ -#else /* CONFIG_PPC64 */ oris r5,r5,0x8000 /* set V (valid) bit */ -#endif /* CONFIG_PPC64 */ #endif -#ifdef CONFIG_PPC64 -/* XXX: does this insert the api correctly? -- Cort */ - rlwimi r5,r3,17,21,25 /* put in API (abbrev page index) */ -#else /* CONFIG_PPC64 */ rlwimi r5,r3,10,26,31 /* put in API (abbrev page index) */ -#endif /* CONFIG_PPC64 */ /* Get the address of the primary PTE group in the hash table */ .globl hash_page_patch_A hash_page_patch_A: lis r4,Hash_base@h /* base address of hash table */ -#ifdef CONFIG_PPC64 - /* just in case */ - clrldi r4,r4,32 -#endif rlwimi r4,r5,32-1,26-Hash_bits,25 /* (VSID & hash_mask) << 6 */ rlwinm r0,r3,32-6,26-Hash_bits,25 /* (PI & hash_mask) << 6 */ xor r4,r4,r0 /* make primary hash */ @@ -1662,6 +1638,19 @@ blr #endif /* CONFIG_8xx */ +mmu_off: + addi r4, r3, __secondary_start - _start + mfmsr r3 + andi. r0,r3,MSR_DR|MSR_IR /* MMU enabled? */ + beq 1f + ori r3,r3,MSR_DR|MSR_IR + xori r3,r3,MSR_DR|MSR_IR + mtspr SRR0,r4 + mtspr SRR1,r3 + sync + rfi +1: blr + /* * This code is jumped to from the startup code to copy * the kernel image to physical address 0. @@ -1671,8 +1660,10 @@ addi r9,r9,0x6f58 /* translate source addr */ cmpw r31,r9 /* (we have to on chrp) */ beq 7f +#if 0 // still needed ? breaks on me if I don't disable this rlwinm r4,r4,0,8,31 /* translate source address */ add r4,r4,r3 /* to region mapped with BATs */ +#endif 7: addis r9,r26,klimit@ha /* fetch klimit */ lwz r25,klimit@l(r9) addis r25,r25,-KERNELBASE@h @@ -1724,33 +1715,26 @@ mfmsr r0 ori r1,r0,MSR_DR|MSR_IR mtspr SRR1,r1 -#ifdef CONFIG_SMP - /* see the function start_here_ibm_hack for explanation -- Cort */ - andi. 0,r1,MSR_DR /* check if the MMU is already on */ - bne 10f - lis r5,smp_ibm_chrp_hack@h - ori r5,r5,smp_ibm_chrp_hack@l - tophys(r5,r5,r6) - lwz r5,0(r5) - cmpi 0,r5,0 - beq 10f - lis r5,first_cpu_booted@h - ori r5,r5,first_cpu_booted@l - tophys(r5,r5,r6) - lwz r5,0(r5) - cmpi 0,r5,0 - beq 10f - lis r0,start_here_ibm_hack@h - ori r0,r0,start_here_ibm_hack@l - b 1010f -10: -#endif /* CONFIG_SMP */ lis r0,start_here@h ori r0,r0,start_here@l -1010: mtspr SRR0,r0 + mtspr SRR0,r0 SYNC rfi /* enables MMU */ +#ifdef CONFIG_GEMINI + .globl __secondary_start_gemini +__secondary_start_gemini: + mfspr r4,HID0 + ori r4,r4,HID0_ICFI + li r3,0 + ori r3,r3,HID0_ICE + andc r4,r4,r3 + mtspr HID0,r4 + sync + bl prom_init + b __secondary_start +#endif /* CONFIG_GEMINI */ + #ifdef CONFIG_SMP .globl __secondary_start_psurge __secondary_start_psurge: @@ -1780,25 +1764,6 @@ blr #endif /* CONFIG_SMP */ -/* - * We get _strange_ behavior on the new IBM chrp firmware. - * We end up here with the MMU disabled, even though we enable - * it with the rfi that takes us here. Somehow, a physical address - * access fixes this by enabling the MMU. - * - * The IBM engineers can't explain this behavior and AIX doesn't - * seem to find it. This hack gets around it for now. - * -- Cort - */ -start_here_ibm_hack: - mfmsr r1 - lis r5,smp_ibm_chrp_hack@h - ori r5,r5,smp_ibm_chrp_hack@l - tophys(r5,r5,r6) - mfmsr r1 - stw r1,_MSR-16(r5) - addi r5,r5,_MSR-16 - dcbf 0,r5 #ifndef CONFIG_8xx start_here: /* @@ -1895,11 +1860,7 @@ */ #ifndef CONFIG_8xx lis r6,_SDR1@ha -#ifdef CONFIG_PPC64 - ld r6,_SDR1@l(r6) -#else lwz r6,_SDR1@l(r6) -#endif #else /* The right way to do this would be to track it down through * init's TSS like the context switch code does, but this is @@ -1928,14 +1889,6 @@ #endif #ifndef CONFIG_8xx mtspr SDR1,r6 -#ifdef CONFIG_PPC64 - /* clear the v bit in the ASR so we can - * behave as if we have segment registers - * -- Cort - */ - clrldi r6,r6,63 - mtasr r6 -#endif /* CONFIG_PPC64 */ li r0,16 /* load up segment register values */ mtctr r0 /* for context 0 */ lis r3,0x2000 /* Ku = 1, VSID = 0 */ @@ -1952,17 +1905,10 @@ lis r3,BATS@ha addi r3,r3,BATS@l tophys(r3,r3,r4) -#ifdef CONFIG_PPC64 - LOAD_BAT(0,0,r3,r4,r5) - LOAD_BAT(1,32,r3,r4,r5) - LOAD_BAT(2,64,r3,r4,r5) - LOAD_BAT(3,96,r3,r4,r5) -#else /* CONFIG_PPC64 */ LOAD_BAT(0,0,r3,r4,r5) LOAD_BAT(1,16,r3,r4,r5) LOAD_BAT(2,32,r3,r4,r5) LOAD_BAT(3,48,r3,r4,r5) -#endif /* CONFIG_PPC64 */ #endif /* CONFIG_8xx */ /* Set up for using our exception vectors */ /* ptr to phys current tss */ @@ -2733,3 +2679,48 @@ .globl cmd_line cmd_line: .space 512 + +/* + * An undocumented "feature" of 604e requires that the v bit + * be cleared before changing BAT values. + * + * Also, newer IBM firmware does not clear bat3 and 4 so + * this makes sure it's done. + * -- Cort + */ +clear_bats: +#if !defined(CONFIG_GEMINI) + li r20,0 + mfspr r9,PVR + rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */ + cmpwi r9, 1 + beq 1f + + mtspr DBAT0U,r20 + mtspr DBAT0L,r20 + mtspr DBAT1U,r20 + mtspr DBAT1L,r20 + mtspr DBAT2U,r20 + mtspr DBAT2L,r20 + mtspr DBAT3U,r20 + mtspr DBAT3L,r20 +1: + mtspr IBAT0U,r20 + mtspr IBAT0L,r20 + mtspr IBAT1U,r20 + mtspr IBAT1L,r20 + mtspr IBAT2U,r20 + mtspr IBAT2L,r20 + mtspr IBAT3U,r20 + mtspr IBAT3L,r20 +#endif /* !defined(CONFIG_GEMINI) */ + blr + +flush_tlbs: + lis r20, 0x1000 +1: addic. r20, r20, -0x1000 + tlbie r20 + blt 1b + sync + blr + diff -u --recursive --new-file v2.2.13/linux/arch/ppc/kernel/irq.c linux/arch/ppc/kernel/irq.c --- v2.2.13/linux/arch/ppc/kernel/irq.c Tue Oct 19 17:10:36 1999 +++ linux/arch/ppc/kernel/irq.c Tue Jan 4 10:12:12 2000 @@ -94,8 +94,8 @@ * this needs to be removed. * -- Cort */ -static char cache_bitmask = 0; -static struct irqaction malloc_cache[8]; +static unsigned long cache_bitmask[1]; +static struct irqaction malloc_cache[32]; extern int mem_init_done; void *irq_kmalloc(size_t size, int pri) @@ -103,10 +103,10 @@ unsigned int i; if ( mem_init_done ) return kmalloc(size,pri); - for ( i = 0; i <= 3 ; i++ ) - if ( ! ( cache_bitmask & (1< + * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive * for more details. */ - -/* - * Note: Interprocessor Interrupt (IPI) and Timer support is incomplete - */ - - #include #include #include @@ -23,12 +20,11 @@ #include #include #include - +#include "open_pic.h" #define REGISTER_DEBUG #undef REGISTER_DEBUG - volatile struct OpenPIC *OpenPIC = NULL; u_int OpenPIC_NumInitSenses __initdata = 0; u_char *OpenPIC_InitSenses __initdata = NULL; @@ -36,11 +32,6 @@ static u_int NumProcessors; static u_int NumSources; - - /* - * Accesses to the current processor's registers - */ - #ifndef __powerpc__ #define THIS_CPU Private #define CHECK_THIS_CPU do {} while (0) @@ -49,11 +40,6 @@ #define CHECK_THIS_CPU check_arg_cpu(cpu) #endif - - /* - * Sanity checks - */ - #if 1 #define check_arg_ipi(ipi) \ if (ipi < 0 || ipi >= OPENPIC_NUM_IPI) \ @@ -67,9 +53,14 @@ #define check_arg_pri(pri) \ if (pri < 0 || pri >= OPENPIC_NUM_PRI) \ printk("openpic.c:%d: illegal priority %d\n", __LINE__, pri); +/* + * I changed this to return to keep us from from trying to use irq #'s + * that we're using for IPI's. + * -- Cort + */ #define check_arg_irq(irq) \ if (irq < 0 || irq >= NumSources) \ - printk("openpic.c:%d: illegal irq %d\n", __LINE__, irq); + /*printk("openpic.c:%d: illegal irq %d\n", __LINE__, irq);*/return; #define check_arg_cpu(cpu) \ if (cpu < 0 || cpu >= NumProcessors) \ printk("openpic.c:%d: illegal cpu %d\n", __LINE__, cpu); @@ -82,252 +73,229 @@ #define check_arg_cpu(cpu) do {} while (0) #endif - - /* - * Dummy interrupt handler - */ - static void no_action(int ir1, void *dev, struct pt_regs *regs) {} - - /* - * I/O functions - */ - +/* + * I/O functions + */ #ifdef __i386__ static inline u_int ld_le32(volatile u_int *addr) { - return *addr; + return *addr; } static inline void out_le32(volatile u_int *addr, u_int val) { - *addr = val; + *addr = val; } #endif u_int openpic_read(volatile u_int *addr) { - u_int val; + u_int val; - val = ld_le32(addr); + val = ld_le32(addr); #ifdef REGISTER_DEBUG - printk("openpic_read(0x%08x) = 0x%08x\n", (u_int)addr, val); + printk("openpic_read(0x%08x) = 0x%08x\n", (u_int)addr, val); #endif - return val; + return val; } static inline void openpic_write(volatile u_int *addr, u_int val) { #ifdef REGISTER_DEBUG - printk("openpic_write(0x%08x, 0x%08x)\n", (u_int)addr, val); + printk("openpic_write(0x%08x, 0x%08x)\n", (u_int)addr, val); #endif - out_le32(addr, val); + out_le32(addr, val); } - static inline u_int openpic_readfield(volatile u_int *addr, u_int mask) { - u_int val = openpic_read(addr); - return val & mask; + u_int val = openpic_read(addr); + return val & mask; } inline void openpic_writefield(volatile u_int *addr, u_int mask, - u_int field) + u_int field) { - u_int val = openpic_read(addr); - openpic_write(addr, (val & ~mask) | (field & mask)); + u_int val = openpic_read(addr); + openpic_write(addr, (val & ~mask) | (field & mask)); } static inline void openpic_clearfield(volatile u_int *addr, u_int mask) { - openpic_writefield(addr, mask, 0); + openpic_writefield(addr, mask, 0); } static inline void openpic_setfield(volatile u_int *addr, u_int mask) { - openpic_writefield(addr, mask, mask); + openpic_writefield(addr, mask, mask); } - - /* - * Update a Vector/Priority register in a safe manner. The interrupt will - * be disabled. - */ - +/* + * Update a Vector/Priority register in a safe manner. The interrupt will + * be disabled. + */ static void openpic_safe_writefield(volatile u_int *addr, u_int mask, u_int field) { - openpic_setfield(addr, OPENPIC_MASK); - /* wait until it's not in use */ - while (openpic_read(addr) & OPENPIC_ACTIVITY); - openpic_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK); + openpic_setfield(addr, OPENPIC_MASK); + /* wait until it's not in use */ + while (openpic_read(addr) & OPENPIC_ACTIVITY); + openpic_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK); } - -/* -------- Global Operations ---------------------------------------------- */ - - - /* - * Initialize the OpenPIC - */ - +/* + * Initialize the OpenPIC + */ __initfunc(void openpic_init(int main_pic)) { - u_int t, i; - u_int timerfreq; - const char *version; + u_int t, i; + u_int timerfreq; + const char *version; - if (!OpenPIC) - panic("No OpenPIC found"); + if (!OpenPIC) + panic("No OpenPIC found"); - t = openpic_read(&OpenPIC->Global.Feature_Reporting0); - switch (t & OPENPIC_FEATURE_VERSION_MASK) { + t = openpic_read(&OpenPIC->Global.Feature_Reporting0); + switch (t & OPENPIC_FEATURE_VERSION_MASK) { case 1: - version = "1.0"; - break; + version = "1.0"; + break; case 2: - version = "1.2"; - break; + version = "1.2"; + break; case 3: - version = "1.3"; - break; + version = "1.3"; + break; default: - version = "?"; - break; - } - NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> - OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1; - NumSources = ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >> - OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1; - printk("OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n", version, - NumProcessors, NumSources, OpenPIC); - timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency); - printk("OpenPIC timer frequency is "); - if (timerfreq) - printk("%d Hz\n", timerfreq); - else - printk("not set\n"); - - if ( main_pic ) - { - /* Initialize timer interrupts */ - for (i = 0; i < OPENPIC_NUM_TIMERS; i++) { - /* Disabled, Priority 0 */ - openpic_inittimer(i, 0, OPENPIC_VEC_TIMER+i); - /* No processor */ - openpic_maptimer(i, 0); - } - - /* Initialize IPI interrupts */ - for (i = 0; i < OPENPIC_NUM_IPI; i++) { - /* Disabled, Priority 0 */ - openpic_initipi(i, 0, OPENPIC_VEC_IPI+i); - } + version = "?"; + break; + } + NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> + OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1; + NumSources = ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >> + OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1; + printk("OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n", version, + NumProcessors, NumSources, OpenPIC); + timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency); + printk("OpenPIC timer frequency is "); + if (timerfreq) + printk("%d Hz\n", timerfreq); + else + printk("not set\n"); + + if ( main_pic ) + { + /* Initialize timer interrupts */ + for (i = 0; i < OPENPIC_NUM_TIMERS; i++) { + /* Disabled, Priority 0 */ + openpic_inittimer(i, 0, OPENPIC_VEC_TIMER+i); + /* No processor */ + openpic_maptimer(i, 0); + } - /* Initialize external interrupts */ - /* SIOint (8259 cascade) is special */ - openpic_initirq(0, 8, OPENPIC_VEC_SOURCE, 1, 1); - /* Processor 0 */ - openpic_mapirq(0, 1<<0); - for (i = 1; i < NumSources; i++) { - /* Enabled, Priority 8 */ - openpic_initirq(i, 8, OPENPIC_VEC_SOURCE+i, 0, - i < OpenPIC_NumInitSenses ? OpenPIC_InitSenses[i] : 1); - /* Processor 0 */ - openpic_mapirq(i, 1<<0); - } + /* Initialize IPI interrupts */ + for (i = 0; i < OPENPIC_NUM_IPI; i++) { + openpic_initipi(i, 10, OPENPIC_VEC_IPI+i); + } - /* Initialize the spurious interrupt */ - openpic_set_spurious(OPENPIC_VEC_SPURIOUS); + /* Initialize external interrupts */ + for (i = 0; i < NumSources; i++) { + /* Enabled, Priority 8 */ + openpic_initirq(i, 8, open_pic.irq_offset+i, 0, + i < OpenPIC_NumInitSenses ? OpenPIC_InitSenses[i] : 1); + /* Processor 0 */ + openpic_mapirq(i, 1<<0); + } - if (request_irq(IRQ_8259_CASCADE, no_action, SA_INTERRUPT, - "82c59 cascade", NULL)) - printk("Unable to get OpenPIC IRQ 0 for cascade\n"); - openpic_set_priority(0, 0); - openpic_disable_8259_pass_through(); - } -} + /* Initialize the spurious interrupt */ + openpic_set_spurious(OPENPIC_VEC_SPURIOUS); + /* Gemini has no i8259 */ + if ( _machine != _MACH_gemini ) + { + /* SIOint (8259 cascade) is special */ + openpic_initirq(0, 8, open_pic.irq_offset, 1, 1); + openpic_mapirq(0, 1<<0); + if (request_irq(IRQ_8259_CASCADE, no_action, SA_INTERRUPT, + "82c59 cascade", NULL)) + printk("Unable to get OpenPIC IRQ 0 for cascade\n"); + } + openpic_set_priority(0, 0); + openpic_disable_8259_pass_through(); + } +} - /* - * Reset the OpenPIC - */ +/* + * Reset the OpenPIC + */ void openpic_reset(void) { - openpic_setfield(&OpenPIC->Global.Global_Configuration0, - OPENPIC_CONFIG_RESET); + openpic_setfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_RESET); } - /* - * Enable/disable 8259 Pass Through Mode - */ - +/* + * Enable/disable 8259 Pass Through Mode + */ void openpic_enable_8259_pass_through(void) { - openpic_clearfield(&OpenPIC->Global.Global_Configuration0, - OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); + openpic_clearfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); } void openpic_disable_8259_pass_through(void) { - openpic_setfield(&OpenPIC->Global.Global_Configuration0, - OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); + openpic_setfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); } - #ifndef __i386__ - /* - * Find out the current interrupt - */ - +/* + * Find out the current interrupt + */ u_int openpic_irq(u_int cpu) { - u_int vec; + u_int vec; - check_arg_cpu(cpu); - vec = openpic_readfield(&OpenPIC->THIS_CPU.Interrupt_Acknowledge, - OPENPIC_VECTOR_MASK); -#if 0 -if (vec != 22 /* SCSI */) -printk("++openpic_irq: %d\n", vec); -#endif - return vec; + check_arg_cpu(cpu); + vec = openpic_readfield(&OpenPIC->THIS_CPU.Interrupt_Acknowledge, + OPENPIC_VECTOR_MASK); + return vec; } #endif - /* - * Signal end of interrupt (EOI) processing - */ - +/* + * Signal end of interrupt (EOI) processing + */ #ifndef __powerpc__ void openpic_eoi(void) #else void openpic_eoi(u_int cpu) #endif { - check_arg_cpu(cpu); - openpic_write(&OpenPIC->THIS_CPU.EOI, 0); + check_arg_cpu(cpu); + openpic_write(&OpenPIC->THIS_CPU.EOI, 0); } - /* - * Get/set the current task priority - */ - +/* + * Get/set the current task priority + */ #ifndef __powerpc__ u_int openpic_get_priority(void) #else u_int openpic_get_priority(u_int cpu) #endif { - CHECK_THIS_CPU; - return openpic_readfield(&OpenPIC->THIS_CPU.Current_Task_Priority, - OPENPIC_CURRENT_TASK_PRIORITY_MASK); + CHECK_THIS_CPU; + return openpic_readfield(&OpenPIC->THIS_CPU.Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK); } #ifndef __powerpc__ @@ -336,176 +304,178 @@ void openpic_set_priority(u_int cpu, u_int pri) #endif { - CHECK_THIS_CPU; - check_arg_pri(pri); - openpic_writefield(&OpenPIC->THIS_CPU.Current_Task_Priority, - OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri); + CHECK_THIS_CPU; + check_arg_pri(pri); + openpic_writefield(&OpenPIC->THIS_CPU.Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri); } - /* - * Get/set the spurious vector - */ - +/* + * Get/set the spurious vector + */ u_int openpic_get_spurious(void) { - return openpic_readfield(&OpenPIC->Global.Spurious_Vector, - OPENPIC_VECTOR_MASK); + return openpic_readfield(&OpenPIC->Global.Spurious_Vector, + OPENPIC_VECTOR_MASK); } void openpic_set_spurious(u_int vec) { - check_arg_vec(vec); - openpic_writefield(&OpenPIC->Global.Spurious_Vector, OPENPIC_VECTOR_MASK, - vec); + check_arg_vec(vec); + openpic_writefield(&OpenPIC->Global.Spurious_Vector, OPENPIC_VECTOR_MASK, + vec); } - - /* - * Initialize one or more CPUs - */ - +/* + * Initialize one or more CPUs + */ void openpic_init_processor(u_int cpumask) { - openpic_write(&OpenPIC->Global.Processor_Initialization, cpumask); + openpic_write(&OpenPIC->Global.Processor_Initialization, cpumask); } -/* -------- Interprocessor Interrupts -------------------------------------- */ - - - /* - * Initialize an interprocessor interrupt (and disable it) - * - * ipi: OpenPIC interprocessor interrupt number - * pri: interrupt source priority - * vec: the vector it will produce - */ - +/* + * Initialize an interprocessor interrupt (and disable it) + * + * ipi: OpenPIC interprocessor interrupt number + * pri: interrupt source priority + * vec: the vector it will produce + */ void openpic_initipi(u_int ipi, u_int pri, u_int vec) { - check_arg_timer(ipi); - check_arg_pri(pri); - check_arg_vec(vec); - openpic_safe_writefield(&OpenPIC->Global.IPI_Vector_Priority(ipi), - OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, - (pri << OPENPIC_PRIORITY_SHIFT) | vec); + check_arg_timer(ipi); + check_arg_pri(pri); + check_arg_vec(vec); + openpic_safe_writefield(&OpenPIC->Global.IPI_Vector_Priority(ipi), + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec); } - - /* - * Send an IPI to one or more CPUs - */ - +/* + * Send an IPI to one or more CPUs + */ #ifndef __powerpc__ void openpic_cause_IPI(u_int ipi, u_int cpumask) #else void openpic_cause_IPI(u_int cpu, u_int ipi, u_int cpumask) #endif { - CHECK_THIS_CPU; - check_arg_ipi(ipi); - openpic_write(&OpenPIC->THIS_CPU.IPI_Dispatch(ipi), cpumask); + CHECK_THIS_CPU; + check_arg_ipi(ipi); + openpic_write(&OpenPIC->THIS_CPU.IPI_Dispatch(ipi), cpumask); } +void openpic_enable_IPI(u_int ipi) +{ + check_arg_ipi(ipi); + openpic_clearfield(&OpenPIC->Global.IPI_Vector_Priority(ipi), + OPENPIC_MASK); +} -/* -------- Timer Interrupts ----------------------------------------------- */ - - - /* - * Initialize a timer interrupt (and disable it) - * - * timer: OpenPIC timer number - * pri: interrupt source priority - * vec: the vector it will produce - */ +/* + * Do per-cpu setup for SMP systems. + * + * Get IPI's working and start taking interrupts. + * -- Cort + */ +void do_openpic_setup_cpu(void) +{ + int i; + + for ( i = 0; i < OPENPIC_NUM_IPI ; i++ ) + openpic_enable_IPI(i); +#if 0 + /* let the openpic know we want intrs */ + for ( i = 0; i < NumSources ; i++ ) + openpic_mapirq(i, openpic_read(&OpenPIC->Source[i].Destination) + | (1<Global.Timer[timer].Vector_Priority, - OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, - (pri << OPENPIC_PRIORITY_SHIFT) | vec); + check_arg_timer(timer); + check_arg_pri(pri); + check_arg_vec(vec); + openpic_safe_writefield(&OpenPIC->Global.Timer[timer].Vector_Priority, + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec); } - /* - * Map a timer interrupt to one or more CPUs - */ - +/* + * Map a timer interrupt to one or more CPUs + */ void openpic_maptimer(u_int timer, u_int cpumask) { - check_arg_timer(timer); - openpic_write(&OpenPIC->Global.Timer[timer].Destination, cpumask); + check_arg_timer(timer); + openpic_write(&OpenPIC->Global.Timer[timer].Destination, cpumask); } - -/* -------- Interrupt Sources ---------------------------------------------- */ - - - /* - * Enable/disable an interrupt source - */ - +/* + * Enable/disable an interrupt source + */ void openpic_enable_irq(u_int irq) { - check_arg_irq(irq); - openpic_clearfield(&OpenPIC->Source[irq].Vector_Priority, OPENPIC_MASK); + check_arg_irq(irq); + openpic_clearfield(&OpenPIC->Source[irq].Vector_Priority, OPENPIC_MASK); } void openpic_disable_irq(u_int irq) { - check_arg_irq(irq); - openpic_setfield(&OpenPIC->Source[irq].Vector_Priority, OPENPIC_MASK); + check_arg_irq(irq); + openpic_setfield(&OpenPIC->Source[irq].Vector_Priority, OPENPIC_MASK); } - - /* - * Initialize an interrupt source (and disable it!) - * - * irq: OpenPIC interrupt number - * pri: interrupt source priority - * vec: the vector it will produce - * pol: polarity (1 for positive, 0 for negative) - * sense: 1 for level, 0 for edge - */ - +/* + * Initialize an interrupt source (and disable it!) + * + * irq: OpenPIC interrupt number + * pri: interrupt source priority + * vec: the vector it will produce + * pol: polarity (1 for positive, 0 for negative) + * sense: 1 for level, 0 for edge + */ void openpic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense) { - check_arg_irq(irq); - check_arg_pri(pri); - check_arg_vec(vec); - openpic_safe_writefield(&OpenPIC->Source[irq].Vector_Priority, - OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | - OPENPIC_SENSE_POLARITY | OPENPIC_SENSE_LEVEL, - (pri << OPENPIC_PRIORITY_SHIFT) | vec | - (pol ? OPENPIC_SENSE_POLARITY : 0) | - (sense ? OPENPIC_SENSE_LEVEL : 0)); + check_arg_irq(irq); + check_arg_pri(pri); + check_arg_vec(vec); + openpic_safe_writefield(&OpenPIC->Source[irq].Vector_Priority, + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | + OPENPIC_SENSE_POLARITY | OPENPIC_SENSE_LEVEL, + (pri << OPENPIC_PRIORITY_SHIFT) | vec | + (pol ? OPENPIC_SENSE_POLARITY : 0) | + (sense ? OPENPIC_SENSE_LEVEL : 0)); } - - /* - * Map an interrupt source to one or more CPUs - */ - +/* + * Map an interrupt source to one or more CPUs + */ void openpic_mapirq(u_int irq, u_int cpumask) { - check_arg_irq(irq); - openpic_write(&OpenPIC->Source[irq].Destination, cpumask); + check_arg_irq(irq); + openpic_write(&OpenPIC->Source[irq].Destination, cpumask); } - - /* - * Set the sense for an interrupt source (and disable it!) - * - * sense: 1 for level, 0 for edge - */ - +/* + * Set the sense for an interrupt source (and disable it!) + * + * sense: 1 for level, 0 for edge + */ void openpic_set_sense(u_int irq, int sense) { - check_arg_irq(irq); - openpic_safe_writefield(&OpenPIC->Source[irq].Vector_Priority, - OPENPIC_SENSE_LEVEL, - (sense ? OPENPIC_SENSE_LEVEL : 0)); + check_arg_irq(irq); + openpic_safe_writefield(&OpenPIC->Source[irq].Vector_Priority, + OPENPIC_SENSE_LEVEL, + (sense ? OPENPIC_SENSE_LEVEL : 0)); } diff -u --recursive --new-file v2.2.13/linux/arch/ppc/kernel/pmac_pic.c linux/arch/ppc/kernel/pmac_pic.c --- v2.2.13/linux/arch/ppc/kernel/pmac_pic.c Tue Oct 19 17:10:36 1999 +++ linux/arch/ppc/kernel/pmac_pic.c Tue Jan 4 10:12:12 2000 @@ -3,12 +3,14 @@ #include #include #include +#include +#include #include #include #include #include "pmac_pic.h" -/* pmac */struct pmac_irq_hw { +struct pmac_irq_hw { unsigned int flag; unsigned int enable; unsigned int ack; @@ -31,6 +33,12 @@ #define GATWICK_IRQ_POOL_SIZE 10 static struct interrupt_info gatwick_int_pool[GATWICK_IRQ_POOL_SIZE]; +extern int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val); +extern int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val); + + static void __pmac pmac_mask_and_ack_irq(unsigned int irq_nr) { unsigned long bit = 1UL << (irq_nr & 0x1f); @@ -108,7 +116,7 @@ }; struct hw_interrupt_type gatwick_pic = { - " GATWICK ", + " PMAC-PI2 ", NULL, NULL, NULL, @@ -160,7 +168,7 @@ if (xmon_2nd) xmon(regs); #endif - smp_message_recv(); + pmac_smp_message_recv(); goto out; } /* could be here due to a do_fake_interrupt call but we don't @@ -290,17 +298,67 @@ } } +/* + * The PowerBook 3400/2400/3500 can have a combo ethernet/modem + * card which includes an ohare chip that acts as a second interrupt + * controller. If we find this second ohare, set it up and fix the + * interrupt value in the device tree for the ethernet chip. + */ +static void __init enable_second_ohare(void) +{ + unsigned char bus, devfn; + unsigned short cmd; + unsigned long addr; + int second_irq; + struct device_node *irqctrler = find_devices("pci106b,7"); + struct device_node *ether; + + if (irqctrler == NULL || irqctrler->n_addrs <= 0) + return; + addr = (unsigned long) ioremap(irqctrler->addrs[0].address, 0x40); + pmac_irq_hw[1] = (volatile struct pmac_irq_hw *)(addr + 0x20); + max_irqs = 64; + if (pci_device_loc(irqctrler, &bus, &devfn) == 0) { + pmac_pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; + cmd &= ~PCI_COMMAND_IO; + pmac_pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd); + } + + second_irq = irqctrler->intrs[0].line; + printk(KERN_INFO "irq: secondary controller on irq %d\n", second_irq); + request_irq(second_irq, gatwick_action, SA_INTERRUPT, + "interrupt cascade", 0 ); + + /* Fix interrupt for the modem/ethernet combo controller. The number + in the device tree (27) is bogus (correct for the ethernet-only + board but not the combo ethernet/modem board). + The real interrupt is 28 on the second controller -> 28+32 = 60. + */ + ether = find_devices("pci1011,14"); + if (ether && ether->n_intrs > 0) { + ether->intrs[0].line = 60; + printk(KERN_INFO "irq: Fixed ethernet IRQ to %d\n", + ether->intrs[0].line); + } +} + __initfunc(void pmac_pic_init(void)) { int i; struct device_node *irqctrler; - unsigned long addr; - int second_irq = -999; + volatile struct pmac_irq_hw *addr; + int second_irq; - - /* G3 powermacs have 64 interrupts, G3 Series PowerBook have 128, - others have 32 */ + /* + * G3 powermacs and 1999 G3 PowerBooks have 64 interrupts, + * 1998 G3 Series PowerBooks have 128, + * other powermacs have 32. + * The combo ethernet/modem card for the Powerstar powerbooks + * (2400/3400/3500, ohare based) has a second ohare chip + * effectively making a total of 64. + */ max_irqs = max_real_irqs = 32; irqctrler = find_devices("mac-io"); if (irqctrler) @@ -317,24 +375,28 @@ /* get addresses of first controller */ if (irqctrler) { if (irqctrler->n_addrs > 0) { - addr = (unsigned long) - ioremap(irqctrler->addrs[0].address, 0x40); - for (i = 0; i < 2; ++i) - pmac_irq_hw[i] = (volatile struct pmac_irq_hw*) - (addr + (2 - i) * 0x10); + addr = ioremap(irqctrler->addrs[0].address, 0x40); + addr += 2; + for (i = 0; i < 2; ++i, --addr) + pmac_irq_hw[i] = addr; } /* get addresses of second controller */ - irqctrler = (irqctrler->next) ? irqctrler->next : NULL; + irqctrler = irqctrler->next; if (irqctrler && irqctrler->n_addrs > 0) { - addr = (unsigned long) - ioremap(irqctrler->addrs[0].address, 0x40); - for (i = 2; i < 4; ++i) - pmac_irq_hw[i] = (volatile struct pmac_irq_hw*) - (addr + (4 - i) * 0x10); + addr = ioremap(irqctrler->addrs[0].address, 0x40); + addr += 2; + for (i = 2; i < 4; ++i, --addr) + pmac_irq_hw[i] = addr; } } + /* PowerBooks 3400 and 3500 can have a second controller in a second + ohare chip, on the combo ethernet/modem card */ + if (machine_is_compatible("AAPL,3400/2400") + || machine_is_compatible("AAPL,3500")) + enable_second_ohare(); + /* disable all interrupts in all controllers */ for (i = 0; i * 32 < max_irqs; ++i) out_le32(&pmac_irq_hw[i]->enable, 0); @@ -346,11 +408,11 @@ (int)second_irq); if (device_is_compatible(irqctrler, "gatwick")) pmac_fix_gatwick_interrupts(irqctrler, max_real_irqs); - for ( i = max_real_irqs ; i < max_irqs ; i++ ) - irq_desc[i].ctl = &gatwick_pic; request_irq( second_irq, gatwick_action, SA_INTERRUPT, - "gatwick cascade", 0 ); + "interrupt cascade", 0 ); } + for (i = max_real_irqs; i < max_irqs; i++) + irq_desc[i].ctl = &gatwick_pic; printk("System has %d possible interrupts\n", max_irqs); if (max_irqs != max_real_irqs) printk(KERN_DEBUG "%d interrupts on main controller\n", @@ -381,6 +443,7 @@ out_le32(&pmac_irq_hw[0]->enable, ppc_cached_irq_mask[0]); if (max_real_irqs > 32) out_le32(&pmac_irq_hw[1]->enable, ppc_cached_irq_mask[1]); + (void)in_le32(&pmac_irq_hw[0]->flag); mb(); } @@ -392,6 +455,7 @@ out_le32(&pmac_irq_hw[0]->enable, 0); if (max_real_irqs > 32) out_le32(&pmac_irq_hw[1]->enable, 0); + (void)in_le32(&pmac_irq_hw[0]->flag); mb(); for (i = 0; i < max_real_irqs; ++i) if (test_bit(i, sleep_save_mask)) diff -u --recursive --new-file v2.2.13/linux/arch/ppc/kernel/pmac_setup.c linux/arch/ppc/kernel/pmac_setup.c --- v2.2.13/linux/arch/ppc/kernel/pmac_setup.c Tue Oct 19 17:10:36 1999 +++ linux/arch/ppc/kernel/pmac_setup.c Tue Jan 4 10:12:12 2000 @@ -190,7 +190,7 @@ return len; } -#ifdef CONFIG_SCSI +#if defined(CONFIG_SCSI) && defined(CONFIG_BLK_DEV_SD) /* Find the device number for the disk (if any) at target tgt on host adaptor host. XXX this really really should be in drivers/scsi/sd.c. */ @@ -217,7 +217,7 @@ return MKDEV_SD(i); return 0; } -#endif +#endif /* SCSI and BLK_DEV_SD */ /* * Dummy mksound function that does nothing. @@ -434,7 +434,7 @@ __initfunc(void find_boot_device(void)) { -#ifdef CONFIG_SCSI +#if defined(CONFIG_SCSI) && defined(CONFIG_BLK_DEV_SD) if (boot_host != NULL) { boot_dev = sd_find_target(boot_host, boot_target); if (boot_dev != 0) @@ -464,7 +464,7 @@ find_boot_device(); found_boot = 1; } - if (dev == boot_dev) { + if (boot_dev == 0 || dev == boot_dev) { ROOT_DEV = MKDEV(MAJOR(dev), MINOR(dev) + part); boot_dev = NODEV; printk(" (root)"); diff -u --recursive --new-file v2.2.13/linux/arch/ppc/kernel/ppc_ksyms.c linux/arch/ppc/kernel/ppc_ksyms.c --- v2.2.13/linux/arch/ppc/kernel/ppc_ksyms.c Tue Jan 4 11:10:32 2000 +++ linux/arch/ppc/kernel/ppc_ksyms.c Tue Jan 4 10:12:12 2000 @@ -198,6 +198,8 @@ EXPORT_SYMBOL(find_compatible_devices); EXPORT_SYMBOL(find_path_device); EXPORT_SYMBOL(find_phandle); +EXPORT_SYMBOL(device_is_compatible); +EXPORT_SYMBOL(machine_is_compatible); EXPORT_SYMBOL(get_property); EXPORT_SYMBOL(pci_io_base); EXPORT_SYMBOL(pci_device_loc); @@ -214,7 +216,6 @@ #endif /* CONFIG_PMAC */ EXPORT_SYMBOL(abs); -EXPORT_SYMBOL(device_is_compatible); EXPORT_SYMBOL_NOVERS(__ashrdi3); EXPORT_SYMBOL_NOVERS(__lshrdi3); diff -u --recursive --new-file v2.2.13/linux/arch/ppc/kernel/prep_pci.c linux/arch/ppc/kernel/prep_pci.c --- v2.2.13/linux/arch/ppc/kernel/prep_pci.c Tue Oct 19 17:10:36 1999 +++ linux/arch/ppc/kernel/prep_pci.c Tue Jan 4 10:12:12 2000 @@ -170,7 +170,7 @@ 0, /* Slot 8 - unused */ 0, /* Slot 9 - unused */ 0, /* Slot 10 - unxued */ - 0, /* Slot 11 - unused */ + 0x1e, /* Slot 11 - PCI-ISA/IDE/USB */ 0, /* Slot 12 - unused */ 0, /* Slot 13 - unused */ 2, /* Slot 14 - Ethernet */ @@ -198,7 +198,7 @@ 0, /* Slot 8 - unused */ 0, /* Slot 9 - unused */ 0, /* Slot 10 - unxued */ - 0, /* Slot 11 - unused */ + 0x1e, /* Slot 11 - PCI-ISA/IDE/USB */ 0, /* Slot 12 - unused */ 0, /* Slot 13 - unused */ 2, /* Slot 14 - Ethernet */ @@ -224,7 +224,7 @@ 0, /* Slot 8 - unused */ 0, /* Slot 9 - unused */ 0, /* Slot 10 - unused */ - 0, /* Slot 11 - unused */ + 0x1e, /* Slot 11 - PCI-ISA/IDE/USB */ 3, /* Slot 12 - SCSI */ 0, /* Slot 13 - unused */ 2, /* Slot 14 - Ethernet */ @@ -253,7 +253,7 @@ 0, /* Slot 8 - unused */ 0, /* Slot 9 - unused */ 0, /* Slot 10 - unused */ - 0, /* Slot 11 - unused */ + 0x1e, /* Slot 11 - PCI-ISA/IDE/USB */ 3, /* Slot 12 - SCSI */ 0, /* Slot 13 - unused */ 2, /* Slot 14 - Ethernet 1 */ @@ -1076,40 +1076,91 @@ return; } +int motopenpic_to_irq(int n) +{ + if (n & 0xF0) { + return (n & 0x0F); + } else { + return(openpic_to_irq(n)); + } +} + +void prep_pib_init(void) +{ +unsigned char reg; +unsigned short short_reg; + +struct pci_dev *dev = NULL; + + if (( _prep_type == _PREP_Motorola) && (OpenPIC)) { + /* + * Perform specific configuration for the Via Tech or + * or Winbond PCI-ISA-Bridge part. + */ + if ((dev = pci_find_device(PCI_VENDOR_ID_VIA, + PCI_DEVICE_ID_VIA_82C586_1, dev))) { + /* + * PPCBUG does not set the enable bits + * for the IDE device. Force them on here. + */ + pcibios_read_config_byte(dev->bus->number, + dev->devfn, 0x40, ®); + + reg |= 0x03; /* IDE: Chip Enable Bits */ + pcibios_write_config_byte(dev->bus->number, + dev->devfn, 0x40, reg); + + } else if ((dev = pci_find_device(PCI_VENDOR_ID_WINBOND, + PCI_DEVICE_ID_WINBOND_83C553, dev))) { + /* + * Clear the PCI Interrupt Routing Control Register. + */ + short_reg = 0x0000; + pcibios_write_config_word(dev->bus->number, + dev->devfn, 0x44, short_reg); + } + } +} __initfunc( void prep_pcibios_fixup(void)) { - struct pci_dev *dev; - extern unsigned char *Motherboard_map; - extern unsigned char *Motherboard_routes; - unsigned char i; - - if ( _prep_type == _PREP_Radstone ) - { - printk("Radstone boards require no PCI fixups\n"); +extern unsigned char *Motherboard_map; +extern unsigned char *Motherboard_routes; +unsigned char i; +struct pci_dev *dev; + + if (_prep_type == _PREP_Radstone) { + printk("Radstone boards require no PCI fixups\n"); return; - } + } prep_route_pci_interrupts(); + prep_pib_init(); + printk("Setting PCI interrupts for a \"%s\"\n", Motherboard_map_name); + if (OpenPIC) { - /* PCI interrupts are controlled by the OpenPIC */ - for(dev=pci_devices; dev; dev=dev->next) { - if (dev->bus->number == 0) { - dev->irq = openpic_to_irq(Motherboard_map[PCI_SLOT(dev->devfn)]); - pcibios_write_config_byte(dev->bus->number, dev->devfn, PCI_INTERRUPT_PIN, dev->irq); - } else { - if (Motherboard_non0 != NULL) - Motherboard_non0(dev); - } + + /* PCI interrupts are controlled by the OpenPIC */ + for(dev=pci_devices; dev; dev=dev->next) { + if (dev->bus->number == 0) { + dev->irq = + motopenpic_to_irq(Motherboard_map[PCI_SLOT(dev->devfn)]); + + pcibios_write_config_byte(dev->bus->number, dev->devfn, + PCI_INTERRUPT_PIN, dev->irq); + } else { + if (Motherboard_non0 != NULL) + Motherboard_non0(dev); } - return; + + } + return; } - for(dev=pci_devices; dev; dev=dev->next) - { + for(dev=pci_devices; dev; dev=dev->next) { /* * Use our old hard-coded kludge to figure out what * irq this device uses. This is necessary on things @@ -1118,29 +1169,26 @@ unsigned char d = PCI_SLOT(dev->devfn); dev->irq = Motherboard_routes[Motherboard_map[d]]; - for ( i = 0 ; i <= 5 ; i++ ) - { - if ( dev->base_address[i] > 0x10000000 ) - { - printk("Relocating PCI address %lx -> %lx\n", - dev->base_address[i], - (dev->base_address[i] & 0x00FFFFFF) - | 0x01000000); - dev->base_address[i] = - (dev->base_address[i] & 0x00FFFFFF) | 0x01000000; - pci_write_config_dword(dev, - PCI_BASE_ADDRESS_0+(i*0x4), - dev->base_address[i] ); - } + for ( i = 0 ; i <= 5 ; i++ ) { + if ( dev->base_address[i] > 0x10000000 ) { + printk("Relocating PCI address %lx -> %lx\n", + dev->base_address[i], + (dev->base_address[i] & 0x00FFFFFF)|0x01000000); + + dev->base_address[i] = (dev->base_address[i] & + 0x00FFFFFF)|0x01000000; + pci_write_config_dword(dev, + PCI_BASE_ADDRESS_0+(i*0x4), + dev->base_address[i] ); + } } #if 0 - /* - * If we have residual data and if it knows about this - * device ask it what the irq is. - * -- Cort - */ - ppcd = residual_find_device_id( ~0L, dev->device, - -1,-1,-1, 0); + /* + * If we have residual data and if it knows about this + * device ask it what the irq is. + * -- Cort + */ + ppcd = residual_find_device_id( ~0L, dev->device, -1,-1,-1, 0); #endif } } diff -u --recursive --new-file v2.2.13/linux/arch/ppc/kernel/prep_setup.c linux/arch/ppc/kernel/prep_setup.c --- v2.2.13/linux/arch/ppc/kernel/prep_setup.c Tue Oct 19 17:10:36 1999 +++ linux/arch/ppc/kernel/prep_setup.c Tue Jan 4 10:12:12 2000 @@ -628,25 +628,36 @@ void prep_ide_insw(ide_ioreg_t port, void *buf, int ns) { - _insw((unsigned short *)((port)+_IO_BASE), buf, ns); + ide_insw(((port)+(_IO_BASE)), buf, ns); } void prep_ide_outsw(ide_ioreg_t port, void *buf, int ns) { - _outsw((unsigned short *)((port)+_IO_BASE), buf, ns); + ide_outsw(((port)+_IO_BASE), buf, ns); } int prep_ide_default_irq(ide_ioreg_t base) { - switch (base) { - case 0x1f0: return 13; - case 0x170: return 13; - case 0x1e8: return 11; - case 0x168: return 10; - default: - return 0; + if ( _prep_type == _PREP_IBM ) { + switch (base) { + case 0x1f0: return 13; + case 0x170: return 13; + case 0x1e8: return 11; + case 0x168: return 10; + default: + return 0; + } + } else { + switch (base) { + case 0x1f0: return 14; + case 0x170: return 14; + case 0x1e8: return 15; + case 0x168: return 15; + default: + return 0; + } } } @@ -687,6 +698,7 @@ void prep_ide_fix_driveid(struct hd_driveid *id) { + ppc_generic_ide_fix_driveid(id); } __initfunc(void diff -u --recursive --new-file v2.2.13/linux/arch/ppc/kernel/process.c linux/arch/ppc/kernel/process.c --- v2.2.13/linux/arch/ppc/kernel/process.c Tue Oct 19 17:10:36 1999 +++ linux/arch/ppc/kernel/process.c Tue Jan 4 10:12:12 2000 @@ -40,6 +40,7 @@ #include #include #include +#include int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs); extern unsigned long _get_SP(void); diff -u --recursive --new-file v2.2.13/linux/arch/ppc/kernel/prom.c linux/arch/ppc/kernel/prom.c --- v2.2.13/linux/arch/ppc/kernel/prom.c Tue Jan 4 11:10:32 2000 +++ linux/arch/ppc/kernel/prom.c Tue Jan 4 10:12:12 2000 @@ -25,6 +25,7 @@ #include #include #include +#include /* * Properties whose value is longer than this get excluded from our @@ -263,7 +264,7 @@ * handling exceptions and the MMU hash table for us. */ __init -void +unsigned long prom_init(int r3, int r4, prom_entry pp) { #ifdef CONFIG_SMP @@ -272,14 +273,23 @@ char type[16], *path; #endif unsigned long mem; - ihandle prom_rtas; + ihandle prom_rtas, prom_mmu; unsigned long offset = reloc_offset(); int l; char *p, *d; + unsigned long phys; + + /* Default */ + phys = offset + KERNELBASE; + +#ifdef CONFIG_GEMINI + gemini_prom_init(); + return phys; +#endif /* CONFIG_GEMINI */ /* check if we're apus, return if we are */ if ( r3 == 0x61707573 ) - return; + return phys; /* If we came here from BootX, clear the screen, * set up some pointers and return. */ @@ -375,12 +385,12 @@ prom_print(RELOC("booting...\n")); flushscreen(); #endif - return; + return phys; } /* check if we're prep, return if we are */ if ( *(unsigned long *)(0) == 0xdeadc0de ) - return; + return phys; /* First get a handle for the stdout device */ RELOC(prom) = pp; @@ -472,6 +482,29 @@ prom_print(RELOC(" done\n")); } + if ((int) call_prom(RELOC("getprop"), 4, 1, RELOC(prom_chosen), + RELOC("mmu"), &prom_mmu, sizeof(prom_mmu)) <= 0) { + prom_print(RELOC(" no MMU found\n")); + } else { + int nargs; + struct prom_args prom_args; + nargs = 4; + prom_args.service = RELOC("call-method"); + prom_args.nargs = nargs; + prom_args.nret = 4; + prom_args.args[0] = RELOC("translate"); + prom_args.args[1] = prom_mmu; + prom_args.args[2] = (void *)(offset + KERNELBASE); + prom_args.args[3] = (void *)1; + RELOC(prom)(&prom_args); + + /* We assume the phys. address size is 3 cells */ + if (prom_args.args[nargs] != 0) + prom_print(RELOC(" (translate failed) ")); + else + phys = (unsigned long)prom_args.args[nargs+3]; + } + #ifdef CONFIG_SMP /* * With CHRP SMP we need to use the OF to start the other @@ -506,7 +539,7 @@ node = call_prom(RELOC("finddevice"), 1, 1, RELOC("/")); if ( (int)call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"),type, sizeof(type)) <= 0) - return; + return phys; /* copy the holding pattern code to someplace safe (8M) */ memcpy( (void *)(8<<20), RELOC(__secondary_hold), 0x100 ); @@ -548,6 +581,7 @@ prom_print(RELOC("...failed\n")); } #endif + return phys; } /* @@ -1428,6 +1462,21 @@ } } +/* Indicates whether the root node has a given value in its + * compatible property. + */ +__openfirmware +int +machine_is_compatible(const char *compat) +{ + struct device_node *root; + + root = find_path_device("/"); + if (root == 0) + return 0; + return device_is_compatible(root, compat); +} + #ifdef CONFIG_BOOTX_TEXT __pmac @@ -1986,20 +2035,5 @@ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; - -/* Indicates whether the root node has a given value in its - * compatible property. - */ -__openfirmware -int -machine_is_compatible(const char *compat) -{ - struct device_node *root; - - root = find_path_device("/"); - if (root == 0) - return 0; - return device_is_compatible(root, compat); -} #endif /* CONFIG_BOOTX_TEXT */ diff -u --recursive --new-file v2.2.13/linux/arch/ppc/kernel/ptrace.c linux/arch/ppc/kernel/ptrace.c --- v2.2.13/linux/arch/ppc/kernel/ptrace.c Fri Jun 4 13:30:47 1999 +++ linux/arch/ppc/kernel/ptrace.c Tue Jan 4 10:12:12 2000 @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include #include @@ -86,7 +86,6 @@ * tables. NOTE! You should check that the long isn't on a page boundary, * and that it is in the task area before calling this: this routine does * no checking. - * */ static unsigned long get_long(struct task_struct * tsk, struct vm_area_struct * vma, unsigned long addr) @@ -95,22 +94,31 @@ pmd_t * pgmiddle; pte_t * pgtable; unsigned long page; + int fault; repeat: pgdir = pgd_offset(vma->vm_mm, addr); if (pgd_none(*pgdir)) { - handle_mm_fault(tsk, vma, addr, 0); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 0); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return 0; } if (pgd_bad(*pgdir)) { printk("ptrace[1]: bad page directory %lx\n", pgd_val(*pgdir)); pgd_clear(pgdir); return 0; } - pgmiddle = pmd_offset(pgdir,addr); + pgmiddle = pmd_offset(pgdir, addr); if (pmd_none(*pgmiddle)) { - handle_mm_fault(tsk, vma, addr, 0); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 0); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return 0; } if (pmd_bad(*pgmiddle)) { printk("ptrace[3]: bad pmd %lx\n", pmd_val(*pgmiddle)); @@ -119,8 +127,12 @@ } pgtable = pte_offset(pgmiddle, addr); if (!pte_present(*pgtable)) { - handle_mm_fault(tsk, vma, addr, 0); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 0); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return 0; } page = pte_page(*pgtable); /* this is a hack for non-kernel-mapped video buffers and similar */ @@ -146,22 +158,31 @@ pmd_t *pgmiddle; pte_t *pgtable; unsigned long page; - + int fault; + repeat: pgdir = pgd_offset(vma->vm_mm, addr); if (!pgd_present(*pgdir)) { - handle_mm_fault(tsk, vma, addr, 1); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 1); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return; } if (pgd_bad(*pgdir)) { printk("ptrace[2]: bad page directory %lx\n", pgd_val(*pgdir)); pgd_clear(pgdir); return; } - pgmiddle = pmd_offset(pgdir,addr); + pgmiddle = pmd_offset(pgdir, addr); if (pmd_none(*pgmiddle)) { - handle_mm_fault(tsk, vma, addr, 1); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 1); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return; } if (pmd_bad(*pgmiddle)) { printk("ptrace[4]: bad pmd %lx\n", pmd_val(*pgmiddle)); @@ -170,13 +191,21 @@ } pgtable = pte_offset(pgmiddle, addr); if (!pte_present(*pgtable)) { - handle_mm_fault(tsk, vma, addr, 1); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 1); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return; } page = pte_page(*pgtable); if (!pte_write(*pgtable)) { - handle_mm_fault(tsk, vma, addr, 1); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 1); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return; } /* this is a hack for non-kernel-mapped video buffers and similar */ if (MAP_NR(page) < max_mapnr) { @@ -247,7 +276,7 @@ } *result = low; } else - *result = get_long(tsk, vma,addr); + *result = get_long(tsk, vma, addr); return 0; } @@ -299,7 +328,7 @@ put_long(tsk, vma,addr & ~(sizeof(long)-1),high); put_long(tsk, vma_low,(addr+sizeof(long)) & ~(sizeof(long)-1),low); } else - put_long(tsk, vma,addr,data); + put_long(tsk, vma, addr, data); return 0; } @@ -307,6 +336,7 @@ { struct task_struct *child; int ret = -EPERM; + unsigned long flags; lock_kernel(); if (request == PTRACE_TRACEME) { @@ -318,34 +348,40 @@ ret = 0; goto out; } - if (pid == 1) /* you may not mess with init */ - goto out; ret = -ESRCH; - if (!(child = find_task_by_pid(pid))) + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + read_unlock(&tasklist_lock); /* FIXME!!! */ + if (!child) goto out; ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out; if (request == PTRACE_ATTACH) { if (child == current) goto out; if ((!child->dumpable || (current->uid != child->euid) || - (current->uid != child->uid) || (current->uid != child->suid) || + (current->uid != child->uid) || (current->gid != child->egid) || - (current->gid != child->gid) || - (current->gid != child->sgid) || - (!cap_issubset(child->cap_permitted, current->cap_permitted))) - && !capable(CAP_SYS_PTRACE)) + (current->gid != child->sgid) || + (!cap_issubset(child->cap_permitted, current->cap_permitted)) || + (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) goto out; /* the same process cannot be attached many times */ if (child->flags & PF_PTRACED) goto out; child->flags |= PF_PTRACED; + + write_lock_irqsave(&tasklist_lock, flags); if (child->p_pptr != current) { REMOVE_LINKS(child); child->p_pptr = current; SET_LINKS(child); } + write_unlock_irqrestore(&tasklist_lock, flags); + send_sig(SIGSTOP, child, 1); ret = 0; goto out; @@ -369,27 +405,19 @@ down(&child->mm->mmap_sem); ret = read_long(child, addr, &tmp); up(&child->mm->mmap_sem); - if (ret < 0) - goto out; - ret = verify_area(VERIFY_WRITE, (void *) data, sizeof(long)); - if (!ret) - put_user(tmp, (unsigned long *) data); + if (ret >= 0) + ret = put_user(tmp,(unsigned long *) data); goto out; } /* read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: { unsigned long tmp; - - if ((addr & 3) || addr < 0 || addr > (PT_FPSCR << 2)) { - ret = -EIO; + + ret = -EIO; + if ((addr & 3) || addr < 0 || addr > (PT_FPSCR << 2)) goto out; - } - ret = verify_area(VERIFY_WRITE, (void *) data, - sizeof(long)); - if (ret) - goto out; tmp = 0; /* Default return condition */ addr = addr >> 2; /* temporary hack. */ if (addr < PT_FPR0) { @@ -401,9 +429,8 @@ tmp = ((long *)child->tss.fpr)[addr - PT_FPR0]; } else - ret = -EIO; - if (!ret) - put_user(tmp,(unsigned long *) data); + goto out; + ret = put_user(tmp, (unsigned long *) data); goto out; } @@ -442,16 +469,16 @@ case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ case PTRACE_CONT: { /* restart after signal. */ ret = -EIO; - if ((unsigned long) data >= _NSIG) + if ((unsigned long) data > _NSIG) goto out; if (request == PTRACE_SYSCALL) child->flags |= PF_TRACESYS; else child->flags &= ~PF_TRACESYS; child->exit_code = data; - wake_up_process(child); /* make sure the single step bit is not set. */ clear_single_step(child); + wake_up_process(child); ret = 0; goto out; } @@ -465,38 +492,40 @@ ret = 0; if (child->state == TASK_ZOMBIE) /* already dead */ goto out; - wake_up_process(child); child->exit_code = SIGKILL; /* make sure the single step bit is not set. */ clear_single_step(child); + wake_up_process(child); goto out; } case PTRACE_SINGLESTEP: { /* set the trap flag. */ ret = -EIO; - if ((unsigned long) data >= _NSIG) + if ((unsigned long) data > _NSIG) goto out; child->flags &= ~PF_TRACESYS; set_single_step(child); - wake_up_process(child); child->exit_code = data; /* give it a chance to run. */ + wake_up_process(child); ret = 0; goto out; } case PTRACE_DETACH: { /* detach a process that was attached. */ ret = -EIO; - if ((unsigned long) data >= _NSIG) + if ((unsigned long) data > _NSIG) goto out; child->flags &= ~(PF_PTRACED|PF_TRACESYS); - wake_up_process(child); child->exit_code = data; + write_lock_irqsave(&tasklist_lock, flags); REMOVE_LINKS(child); child->p_pptr = child->p_opptr; SET_LINKS(child); + write_unlock_irqrestore(&tasklist_lock, flags); /* make sure the single step bit is not set. */ clear_single_step(child); + wake_up_process(child); ret = 0; goto out; } @@ -512,10 +541,9 @@ asmlinkage void syscall_trace(void) { - lock_kernel(); if ((current->flags & (PF_PTRACED|PF_TRACESYS)) != (PF_PTRACED|PF_TRACESYS)) - goto out; + return; current->exit_code = SIGTRAP; current->state = TASK_STOPPED; notify_parent(current, SIGCHLD); @@ -529,6 +557,4 @@ send_sig(current->exit_code, current, 1); current->exit_code = 0; } -out: - unlock_kernel(); } diff -u --recursive --new-file v2.2.13/linux/arch/ppc/kernel/setup.c linux/arch/ppc/kernel/setup.c --- v2.2.13/linux/arch/ppc/kernel/setup.c Tue Jan 4 11:10:32 2000 +++ linux/arch/ppc/kernel/setup.c Tue Jan 4 10:12:12 2000 @@ -1,5 +1,5 @@ /* - * $Id: setup.c,v 1.132.2.5 1999/09/11 03:32:50 paulus Exp $ + * $Id: setup.c,v 1.132.2.6 1999/10/19 04:32:33 paulus Exp $ * Common prep/pmac/chrp boot and setup code. */ @@ -62,6 +62,12 @@ unsigned long r6, unsigned long r7); +extern void gemini_init(unsigned long r3, + unsigned long r4, + unsigned long r5, + unsigned long r6, + unsigned long r7); + extern boot_infos_t *boot_infos; extern char cmd_line[512]; char saved_command_line[256]; @@ -342,7 +348,6 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { - #ifdef __SMP__ if ( first_cpu_booted ) return 0; #endif /* __SMP__ */ @@ -403,6 +408,8 @@ _machine = _MACH_fads; #elif defined(CONFIG_APUS) _machine = _MACH_apus; +#elif defined(CONFIG_GEMINI) + _machine = _MACH_gemini; #else #error "Machine not defined correctly" #endif /* CONFIG_APUS */ @@ -488,6 +495,11 @@ mbx_init(r3, r4, r5, r6, r7); break; #endif +#ifdef CONFIG_GEMINI + case _MACH_gemini: + gemini_init(r3, r4, r5, r6, r7); + break; +#endif default: printk("Unknown machine type in identify_machine!\n"); } @@ -497,6 +509,28 @@ extern int __map_without_bats; __map_without_bats = 1; } + + /* Look for mem= option on command line */ + if (strstr(cmd_line, "mem=")) { + char *p, *q; + unsigned long maxmem = 0; + extern unsigned long __max_memory; + + for (q = cmd_line; (p = strstr(q, "mem=")) != 0; ) { + q = p + 4; + if (p > cmd_line && p[-1] != ' ') + continue; + maxmem = simple_strtoul(q, &q, 0); + if (*q == 'k' || *q == 'K') { + maxmem <<= 10; + ++q; + } else if (*q == 'm' || *q == 'M') { + maxmem <<= 20; + ++q; + } + } + __max_memory = maxmem; + } return 0; } @@ -522,7 +556,7 @@ } __initfunc(void setup_arch(char **cmdline_p, - unsigned long * memory_start_p, unsigned long * memory_end_p)) + unsigned long * memory_start_p, unsigned long * memory_end_p)) { extern int panic_timeout; extern char _etext[], _edata[]; @@ -532,8 +566,11 @@ #ifdef CONFIG_XMON extern void xmon_map_scc(void); + char *p; + xmon_map_scc(); - if (strstr(cmd_line, "xmon")) + p = strstr(cmd_line, "xmon"); + if (p != NULL && (p == cmd_line || p[-1] == ' ')) xmon(0); #endif /* CONFIG_XMON */ @@ -547,6 +584,7 @@ /* Save unparsed command line copy for /proc/cmdline */ strcpy(saved_command_line, cmd_line); + *cmdline_p = cmd_line; *memory_start_p = find_available_memory(); diff -u --recursive --new-file v2.2.13/linux/arch/ppc/kernel/signal.c linux/arch/ppc/kernel/signal.c --- v2.2.13/linux/arch/ppc/kernel/signal.c Mon Aug 9 16:05:55 1999 +++ linux/arch/ppc/kernel/signal.c Tue Jan 4 10:12:12 2000 @@ -266,8 +266,8 @@ if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) goto badframe; - if (regs->msr & MSR_FP) - giveup_fpu(current); + if (regs->msr & MSR_FP) + giveup_fpu(current); if (__copy_to_user(&frame->gp_regs, regs, GP_REGS_SIZE) || __copy_to_user(&frame->fp_regs, current->tss.fpr, ELF_NFPREG * sizeof(double)) @@ -471,6 +471,7 @@ /* Whee! Actually deliver the signal. */ handle_signal(signr, ka, &info, oldset, regs, &newsp, frame); + break; } if (regs->trap == 0x0C00 /* System Call! */ && @@ -487,5 +488,6 @@ setup_frame(regs, (struct sigregs *) frame, newsp); return 1; + } diff -u --recursive --new-file v2.2.13/linux/arch/ppc/kernel/sleep.S linux/arch/ppc/kernel/sleep.S --- v2.2.13/linux/arch/ppc/kernel/sleep.S Tue Oct 19 17:10:36 1999 +++ linux/arch/ppc/kernel/sleep.S Tue Jan 4 10:12:12 2000 @@ -171,6 +171,11 @@ */ wake_up: + /* Flash inval the instruction cache */ + mfspr r3,HID0 + ori r3,r3, HID0_ICFI + mtspr HID0,r3 + isync /* Restore the HID0 register. This turns on the L1 caches. */ subi r1,r1,SL_PC lwz r3,SL_HID0(r1) diff -u --recursive --new-file v2.2.13/linux/arch/ppc/kernel/smp.c linux/arch/ppc/kernel/smp.c --- v2.2.13/linux/arch/ppc/kernel/smp.c Tue Oct 19 17:10:36 1999 +++ linux/arch/ppc/kernel/smp.c Tue Jan 4 10:12:12 2000 @@ -34,8 +34,11 @@ #include #include #include +#include #include "time.h" +#include "open_pic.h" + int first_cpu_booted = 0; int smp_threads_ready = 0; volatile int smp_commenced = 0; @@ -109,34 +112,8 @@ } } -/* - * Dirty hack to get smp message passing working. - * - * As it is now, if we're sending two message at the same time - * we have race conditions. The PowerSurge doesn't easily - * allow us to send IPI messages so we put the messages in - * smp_message[]. - * - * This is because don't have several IPI's on the PowerSurge even though - * we do on the chrp. It would be nice to use the actual IPI's on the chrp - * rather than this but having two methods of doing IPI isn't a good idea - * right now. - * -- Cort - */ -int smp_message[NR_CPUS]; -void smp_message_recv(void) +void smp_message_recv(int msg) { - int msg = smp_message[smp_processor_id()]; - - if ( _machine == _MACH_Pmac ) - { - /* clear interrupt */ - out_be32(PSURGE_INTR, ~0); - } - - /* make sure msg is for us */ - if ( msg == -1 ) return; - ipi_count++; switch( msg ) @@ -145,25 +122,72 @@ __cli(); while (1) ; break; - case MSG_RESCHEDULE: + case MSG_RESCHEDULE: current->need_resched = 1; break; - case 0xf0f0: /* syncing time bases - just return */ + case MSG_INVALIDATE_TLB: + _tlbia(); + case 0xf0f0: /* pmac syncing time bases - just return */ break; default: printk("SMP %d: smp_message_recv(): unknown msg %d\n", smp_processor_id(), msg); break; } +} + +/* + * As it is now, if we're sending two message at the same time + * we have race conditions on Pmac. The PowerSurge doesn't easily + * allow us to send IPI messages so we put the messages in + * smp_message[]. + * + * This is because don't have several IPI's on the PowerSurge even though + * we do on the chrp. It would be nice to use actual IPI's such as with openpic + * rather than this. + * -- Cort + */ +int pmac_smp_message[NR_CPUS]; +void pmac_smp_message_recv(void) +{ + int msg = pmac_smp_message[smp_processor_id()]; + + /* clear interrupt */ + out_be32(PSURGE_INTR, ~0); + + /* make sure msg is for us */ + if ( msg == -1 ) return; + + smp_message_recv(msg); + /* reset message */ - smp_message[smp_processor_id()] = -1; + pmac_smp_message[smp_processor_id()] = -1; +} + + +/* + * 750's don't broadcast tlb invalidates so + * we have to emulate that behavior. + * -- Cort + */ +void smp_send_tlb_invalidate(int cpu) +{ + if ( (_get_PVR()>>16) == 8 ) + smp_message_pass(MSG_ALL_BUT_SELF, MSG_INVALIDATE_TLB, 0, 0); } void smp_send_reschedule(int cpu) { - /* This is only used if `cpu' is running an idle task, - so it will reschedule itself anyway... */ - /*smp_message_pass(cpu, MSG_RESCHEDULE, 0, 0);*/ + /* + * This is only used if `cpu' is running an idle task, + * so it will reschedule itself anyway... + * + * This isn't the case anymore since the other CPU could be + * sleeping and won't reschedule until the next interrupt (such + * as the timer). + * -- Cort + */ + smp_message_pass(cpu, MSG_RESCHEDULE, 0, 0); } void smp_send_stop(void) @@ -171,38 +195,39 @@ smp_message_pass(MSG_ALL_BUT_SELF, MSG_STOP_CPU, 0, 0); } -spinlock_t mesg_pass_lock = SPIN_LOCK_UNLOCKED; void smp_message_pass(int target, int msg, unsigned long data, int wait) { int i; - if ( !(_machine & (_MACH_Pmac|_MACH_chrp|_MACH_prep)) ) + + if ( !(_machine & (_MACH_Pmac|_MACH_chrp|_MACH_prep|_MACH_gemini)) ) return; - spin_lock(&mesg_pass_lock); - - /* - * We assume here that the msg is not -1. If it is, - * the recipient won't know the message was destined - * for it. -- Cort - */ - - switch( target ) - { - case MSG_ALL: - smp_message[smp_processor_id()] = msg; - /* fall through */ - case MSG_ALL_BUT_SELF: - for ( i = 0 ; i < smp_num_cpus ; i++ ) - if ( i != smp_processor_id () ) - smp_message[i] = msg; - break; - default: - smp_message[target] = msg; - break; - } - switch (_machine) { case _MACH_Pmac: + /* + * IPI's on the Pmac are a hack but without reasonable + * IPI hardware SMP on Pmac is a hack. + * + * We assume here that the msg is not -1. If it is, + * the recipient won't know the message was destined + * for it. -- Cort + */ + for ( i = 0; i <= smp_num_cpus ; i++ ) + pmac_smp_message[i] = -1; + switch( target ) + { + case MSG_ALL: + pmac_smp_message[smp_processor_id()] = msg; + /* fall through */ + case MSG_ALL_BUT_SELF: + for ( i = 0 ; i < smp_num_cpus ; i++ ) + if ( i != smp_processor_id () ) + pmac_smp_message[i] = msg; + break; + default: + pmac_smp_message[target] = msg; + break; + } /* interrupt secondary processor */ out_be32(PSURGE_INTR, ~0); out_be32(PSURGE_INTR, 0); @@ -213,35 +238,27 @@ /* interrupt primary */ /**(volatile unsigned long *)(0xf3019000);*/ break; - case _MACH_chrp: case _MACH_prep: - /* - * There has to be some way of doing this better - - * perhaps a sent-to-all or send-to-all-but-self - * in the openpic. This gets us going for now, though. - * -- Cort - */ + case _MACH_gemini: + /* make sure we're sending something that translates to an IPI */ + if ( msg > 0x3 ) + break; switch ( target ) { case MSG_ALL: - for ( i = 0 ; i < smp_num_cpus ; i++ ) - openpic_cause_IPI(i, 0, 0xffffffff ); + openpic_cause_IPI(smp_processor_id(), msg, 0xffffffff); break; case MSG_ALL_BUT_SELF: - for ( i = 0 ; i < smp_num_cpus ; i++ ) - if ( i != smp_processor_id () ) - openpic_cause_IPI(i, 0, - 0xffffffff & ~(1 << smp_processor_id())); + openpic_cause_IPI(smp_processor_id(), msg, + 0xffffffff & ~(1 << smp_processor_id())); break; default: - openpic_cause_IPI(target, 0, 1U << target); + openpic_cause_IPI(smp_processor_id(), msg, 1<>2; + cpu_nr = (cpu_nr == 0) ? 4 : cpu_nr; + break; default: printk("SMP not supported on this machine.\n"); return; @@ -338,25 +359,16 @@ case _MACH_chrp: *(unsigned long *)KERNELBASE = i; asm volatile("dcbf 0,%0"::"r"(KERNELBASE):"memory"); -#if 0 - device = find_type_devices("cpu"); - /* assume cpu device list is in order, find the ith cpu */ - for ( a = i; device && a; device = device->next, a-- ) - ; - if ( !device ) - break; - printk( "Starting %s (%lu): ", device->full_name, - *(ulong *)get_property(device, "reg", NULL) ); - call_rtas( "start-cpu", 3, 1, NULL, - *(ulong *)get_property(device, "reg", NULL), - __pa(__secondary_start_chrp), i); -#endif break; case _MACH_prep: *MotSave_SmpIar = (unsigned long)__secondary_start_psurge - KERNELBASE; *MotSave_CpusState[1] = CPU_GOOD; printk("CPU1 reset, waiting\n"); break; + case _MACH_gemini: + openpic_init_processor( 1<processor] = 1; + /* + * Each processor has to do this and this is the best + * place to stick it for now. + * -- Cort + */ + if ( _machine & (_MACH_gemini|_MACH_chrp|_MACH_prep) ) + do_openpic_setup_cpu(); while(!smp_commenced) barrier(); __sti(); @@ -437,8 +458,8 @@ void __init smp_store_cpu_info(int id) { struct cpuinfo_PPC *c = &cpu_data[id]; - /* assume bogomips are same for everything */ c->loops_per_sec = loops_per_sec; c->pvr = _get_PVR(); } + diff -u --recursive --new-file v2.2.13/linux/arch/ppc/mm/init.c linux/arch/ppc/mm/init.c --- v2.2.13/linux/arch/ppc/mm/init.c Tue Jan 4 11:10:32 2000 +++ linux/arch/ppc/mm/init.c Tue Jan 4 10:12:12 2000 @@ -1,5 +1,5 @@ /* - * $Id: init.c,v 1.164.2.5 1999/09/07 00:59:22 paulus Exp $ + * $Id: init.c,v 1.164.2.7 1999/10/19 04:32:39 paulus Exp $ * * PowerPC version * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) @@ -54,6 +54,7 @@ #include #include /* END APUS includes */ +#include int prom_trashed; atomic_t next_mmu_context; @@ -83,6 +84,7 @@ unsigned long *prep_find_end_of_memory(void); unsigned long *pmac_find_end_of_memory(void); unsigned long *apus_find_end_of_memory(void); +unsigned long *gemini_find_end_of_memory(void); extern unsigned long *find_end_of_memory(void); #ifdef CONFIG_MBX unsigned long *mbx_find_end_of_memory(void); @@ -114,6 +116,7 @@ static void append_mem_piece(struct mem_pieces *, unsigned, unsigned); extern struct task_struct *current_set[NR_CPUS]; +unsigned long cpu_invalidate_tlb[NR_CPUS]; PTE *Hash, *Hash_end; unsigned long Hash_size, Hash_mask; @@ -149,6 +152,9 @@ */ int __map_without_bats = 0; +/* max amount of RAM to use */ +unsigned long __max_memory; + /* optimization for 603 to load the tlb directly from the linux table -- Cort */ #define NO_RELOAD_HTAB 1 /* change in kernel/head.S too! */ @@ -492,6 +498,9 @@ #ifndef CONFIG_8xx __clear_user(Hash, Hash_size); _tlbia(); +#ifdef __SMP__ + smp_send_tlb_invalidate(0); +#endif #else asm volatile ("tlbia" : : ); #endif @@ -509,6 +518,9 @@ mm->context = NO_CONTEXT; if (mm == current->mm) activate_context(current); +#ifdef __SMP__ + smp_send_tlb_invalidate(0); +#endif #else asm volatile ("tlbia" : : ); #endif @@ -522,6 +534,9 @@ flush_hash_page(vma->vm_mm->context, vmaddr); else flush_hash_page(0, vmaddr); +#ifdef __SMP__ + smp_send_tlb_invalidate(0); +#endif #else asm volatile ("tlbia" : : ); #endif @@ -551,6 +566,9 @@ { flush_hash_page(mm->context, start); } +#ifdef __SMP__ + smp_send_tlb_invalidate(0); +#endif #else asm volatile ("tlbia" : : ); #endif @@ -576,6 +594,9 @@ } read_unlock(&tasklist_lock); flush_hash_segments(0x10, 0xffffff); +#ifdef __SMP__ + smp_send_tlb_invalidate(0); +#endif atomic_set(&next_mmu_context, 0); /* make sure current always has a context */ current->mm->context = MUNGE_CONTEXT(atomic_inc_return(&next_mmu_context)); @@ -984,6 +1005,10 @@ FREESEC(__pmac_begin,__pmac_end,num_pmac_pages); FREESEC(__prep_begin,__prep_end,num_prep_pages); break; + case _MACH_gemini: + FREESEC(__pmac_begin,__pmac_end,num_pmac_pages); + FREESEC(__prep_begin,__prep_end,num_prep_pages); + break; } if ( !have_of ) @@ -1021,9 +1046,13 @@ else if (_machine == _MACH_apus ) end_of_DRAM = apus_find_end_of_memory(); #endif +#ifdef CONFIG_GEMINI + else if ( _machine == _MACH_gemini ) + end_of_DRAM = gemini_find_end_of_memory(); +#endif /* CONFIG_GEMINI */ else /* prep */ end_of_DRAM = prep_find_end_of_memory(); - +*(unsigned long *)(KERNELBASE) = 0xdeadbeef; hash_init(); _SDR1 = __pa(Hash) | (Hash_mask >> 10); ioremap_base = 0xf8000000; @@ -1066,6 +1095,10 @@ (kernel_map) remaps individual IO regions to 0x90000000. */ break; + case _MACH_gemini: + setbat(0, 0xf0000000, 0xf0000000, 0x10000000, IO_PAGE); + setbat(1, 0x80000000, 0x80000000, 0x10000000, IO_PAGE); + break; } ioremap_bot = ioremap_base; #else /* CONFIG_8xx */ @@ -1296,7 +1329,7 @@ int i; /* max amount of RAM we allow -- Cort */ -#define RAM_LIMIT (256<<20) +#define RAM_LIMIT (768<<20) memory_node = find_devices("memory"); if (memory_node == NULL) { @@ -1329,8 +1362,12 @@ * to our nearest IO area. * -- Cort */ - if ( phys_mem.regions[0].size >= RAM_LIMIT ) - phys_mem.regions[0].size = RAM_LIMIT; + if (__max_memory == 0 || __max_memory > RAM_LIMIT) + __max_memory = RAM_LIMIT; + if (phys_mem.regions[0].size >= __max_memory) { + phys_mem.regions[0].size = __max_memory; + phys_mem.n_regions = 1; + } total = phys_mem.regions[0].size; if (phys_mem.n_regions > 1) { @@ -1343,20 +1380,15 @@ if (boot_infos == 0) { /* record which bits the prom is using */ get_mem_prop("available", &phys_avail); + prom_mem = phys_mem; + for (i = 0; i < phys_avail.n_regions; ++i) + remove_mem_piece(&prom_mem, + phys_avail.regions[i].address, + phys_avail.regions[i].size, 0); } else { /* booted from BootX - it's all available (after klimit) */ phys_avail = phys_mem; - } - prom_mem = phys_mem; - for (i = 0; i < phys_avail.n_regions; ++i) - { - if ( phys_avail.regions[i].address >= RAM_LIMIT ) - continue; - if ( (phys_avail.regions[i].address+phys_avail.regions[i].size) - >= RAM_LIMIT ) - phys_avail.regions[i].size = RAM_LIMIT - phys_avail.regions[i].address; - remove_mem_piece(&prom_mem, phys_avail.regions[i].address, - phys_avail.regions[i].size, 1); + prom_mem.n_regions = 0; } /* @@ -1406,6 +1438,32 @@ return (__va(total)); } + +#if defined(CONFIG_GEMINI) +unsigned long __init *gemini_find_end_of_memory(void) +{ + unsigned long total, kstart, ksize, *ret; + unsigned char reg; +*(unsigned long *)(KERNELBASE) = 0x1; + reg = readb(GEMINI_MEMCFG); +*(unsigned long *)(KERNELBASE) = 0x2; + total = ((1<<((reg & 0x7) - 1)) * + (8<<((reg >> 3) & 0x7))); + total *= (1024*1024); + phys_mem.regions[0].address = 0; + phys_mem.regions[0].size = total; + phys_mem.n_regions = 1; + + ret = __va(phys_mem.regions[0].size); + phys_avail = phys_mem; + kstart = __pa(_stext); + ksize = PAGE_ALIGN( _end - _stext ); +*(unsigned long *)(KERNELBASE) = 0x3; + remove_mem_piece( &phys_avail, kstart, ksize, 0 ); +*(unsigned long *)(KERNELBASE) = 0x4; + return ret; +} +#endif /* defined(CONFIG_GEMINI) */ #ifdef CONFIG_APUS #define HARDWARE_MAPPED_SIZE (512*1024) diff -u --recursive --new-file v2.2.13/linux/arch/ppc/xmon/xmon.c linux/arch/ppc/xmon/xmon.c --- v2.2.13/linux/arch/ppc/xmon/xmon.c Tue Oct 19 17:10:36 1999 +++ linux/arch/ppc/xmon/xmon.c Tue Jan 4 10:12:12 2000 @@ -115,9 +115,13 @@ { struct pt_regs regs; int msr, cmd; + static int entered = 0; + + if (!entered) { + entered = 1; + printk("Entering xmon kernel debugger.\n"); + } - printk("Entering xmon kernel debugger.\n"); - if (excp == NULL) { asm volatile ("stw 0,0(%0)\n\ lwz 0,0(1)\n\ @@ -230,7 +234,7 @@ if (dabr.enabled && pc == dabr.instr) return &dabr; - if (iabr.enabled && pc == iabr.address) + if (iabr.enabled && ((pc ^ iabr.address) & ~3) == 0) return &iabr; bp = bpts; for (i = 0; i < NBPTS; ++i, ++bp) @@ -254,7 +258,9 @@ printf("Couldn't insert breakpoint at %x, disabling\n", bp->address); bp->enabled = 0; + continue; } + store_inst((void *) bp->address); } if (dabr.enabled) set_dabr(dabr.address); @@ -277,9 +283,12 @@ continue; if (mread(bp->address, &instr, 4) == 4 && instr == bpinstr - && mwrite(bp->address, &bp->instr, 4) != 4) + && mwrite(bp->address, &bp->instr, 4) != 4) { printf("Couldn't remove breakpoint at %x\n", bp->address); + continue; + } + store_inst((void *) bp->address); } } diff -u --recursive --new-file v2.2.13/linux/arch/s390/Makefile linux/arch/s390/Makefile --- v2.2.13/linux/arch/s390/Makefile Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/Makefile Tue Jan 4 10:12:12 2000 @@ -0,0 +1,70 @@ + +# i386/Makefile +# +# This file is included by the global makefile so that you can add your own +# architecture-specific flags and dependencies. Remember to do have actions +# for "archclean" and "archdep" for cleaning up and making dependencies for +# this architecture +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1994 by Linus Torvalds +# + +LD=$(CROSS_COMPILE)ld -m elf_s390 +CPP=$(CC) -E +OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S +LDFLAGS=-e start +LINKFLAGS =-T $(TOPDIR)/arch/s390/vmlinux.lds $(LDFLAGS) + +CFLAGS_PIPE := -pipe +CFLAGS_NSR := -fno-strength-reduce +CFLAGS := $(CFLAGS) $(CFLAGS_PIPE) $(CFLAGS_NSR) + + +ifdef SMP +CFLAGS := $(CFLAGS) -D__SMP__ +endif + +HEAD := arch/s390/kernel/head.o arch/s390/kernel/init_task.o + +SUBDIRS := $(SUBDIRS) arch/s390/mm arch/s390/kernel arch/s390/lib \ + drivers/s390 +CORE_FILES := arch/s390/mm/mm.o arch/s390/kernel/kernel.o $(CORE_FILES) \ + drivers/s390/io.o +LIBS := $(TOPDIR)/arch/s390/lib/lib.a $(LIBS) $(TOPDIR)/arch/s390/lib/lib.a + +all: image listing + +listing: vmlinux + @$(MAKEBOOT) listing + +arch/s390/kernel: dummy + $(MAKE) linuxsubdirs SUBDIRS=arch/s390/kernel + +arch/s390/mm: dummy + $(MAKE) linuxsubdirs SUBDIRS=arch/s390/mm + +drivers/s390: dummy + $(MAKE) linuxsubdirs SUBDIRS=drivers/s390 + +MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot + +silo: + @$(MAKEBOOT) silo + +dasdfmt: + @$(MAKEBOOT) dasdfmt + +image: vmlinux + @$(MAKEBOOT) image + +archclean: + @$(MAKEBOOT) clean + +archmrproper: + +archdep: + @$(MAKEBOOT) dep diff -u --recursive --new-file v2.2.13/linux/arch/s390/boot/Makefile linux/arch/s390/boot/Makefile --- v2.2.13/linux/arch/s390/boot/Makefile Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/boot/Makefile Tue Jan 4 10:12:12 2000 @@ -0,0 +1,40 @@ +# +# Makefile for the linux i386-specific parts of the memory manager. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +OBJCOPY = $(CROSS_COMPILE)objcopy + +O_TARGET := +O_OBJS := + +include $(TOPDIR)/Rules.make + +%.lnk: %.o + $(LD) -Ttext 0x0 -o $@ $< + +%.boot: %.lnk + $(OBJCOPY) -O binary $< $@ + +silo: silo.c + $(CROSS_COMPILE)gcc $< -o $@ + +dasdfmt: dasdfmt.c + $(CROSS_COMPILE)gcc $< -o $@ + +image: $(CONFIGURE) $(TOPDIR)/vmlinux \ + iplfba.boot ipleckd.boot + $(OBJCOPY) -O binary $(TOPDIR)/vmlinux image + +listing: ../../../vmlinux + $(OBJDUMP) --disassemble --disassemble-all --disassemble-zeroes --reloc $(TOPDIR)/vmlinux > listing + +dep: + +clean: + rm -f image listing silo dasdfmt iplfba.boot ipleckd.boot + diff -u --recursive --new-file v2.2.13/linux/arch/s390/boot/dasdfmt.c linux/arch/s390/boot/dasdfmt.c --- v2.2.13/linux/arch/s390/boot/dasdfmt.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/boot/dasdfmt.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,559 @@ +/* + * + * dasdfmt.c + * + * S390 version + * Copyright (C) 1999 IBM Corporation + * Author(s): Utz Bacher (utz.bacher@de.ibm.com) + * + * Still to do: + * detect non-switch parameters and complain about them + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define EXIT_MISUSE 1 +#define TEMPFILENAME "/tmp/ddfXXXXXX" +#define TEMPFILENAMECHARS 8 /* 8 characters are fixed in all temp filenames */ +#define IOCTL_COMMAND 'D' << 8 +#define SLASHDEV "/dev/" +#define PROC_DASD_DEVICES "/proc/dasd/devices" +#define DASD_DRIVER_NAME "dasd" +#define PROC_LINE_LENGTH 80 +#define ERR_LENGTH 80 + +#define MAX_FILELEN NAME_MAX+PATH_MAX + +#define GIVEN_DEVNO 1 +#define GIVEN_MAJOR 2 +#define GIVEN_MINOR 4 + +#define CHECK_START 1 +#define CHECK_END 2 +#define CHECK_BLKSIZE 4 +#define CHECK_ALL ~0 + +#define ERRMSG(x...) {fflush(stdout);fprintf(stderr,x);} +#define ERRMSG_EXIT(ec,x...) {fflush(stdout);fprintf(stderr,x);exit(ec);} + +#define CHECK_SPEC_MAX_ONCE(i,str) \ + {if (i>1) \ + ERRMSG_EXIT(EXIT_MISUSE,"%s: " str " " \ + "can only be specified once\n",prog_name);} + +#define PARSE_PARAM_INTO(x,param,base,str) \ + {x=(int)strtol(param,&endptr,base); \ + if (*endptr) \ + ERRMSG_EXIT(EXIT_MISUSE,"%s: " str " " \ + "is in invalid format\n",prog_name);} + +typedef struct { + int start_unit; + int stop_unit; + int blksize; +} format_data_t; + +char prog_name[]="dasd_format"; +char tempfilename[]=TEMPFILENAME; + +void +exit_usage(int exitcode) +{ + printf("Usage: %s [-tvV] [-s start_track] [-e end_track] \n",prog_name); + printf(" [-b blocksize] -f dev_filename | -n 390_devno\n"); + exit(exitcode); +} + +void +get_xno_from_xno(int *devno,int *major_no,int *minor_no,int mode) +{ + FILE *file; + int d,mi,ma,rc; + char line[PROC_LINE_LENGTH]; + + file=fopen(PROC_DASD_DEVICES,"r"); + if (file==NULL) + ERRMSG_EXIT(EXIT_FAILURE,"%s: failed to open " \ + PROC_DASD_DEVICES ": %s (do you have the /proc " \ + "filesystem enabled?)\n",prog_name,strerror(errno)); + + fgets(line,sizeof(line),file); /* omit first line */ + while (fgets(line,sizeof(line),file)!=NULL) { + rc=sscanf(line,"%X%d%d",&d,&ma,&mi); + if ( (rc==3) && + !((d!=*devno)&&(mode&GIVEN_DEVNO)) && + !((ma!=*major_no)&&(mode&GIVEN_MAJOR)) && + !((mi!=*minor_no)&&(mode&GIVEN_MINOR)) ) { + *devno=d; + *major_no=ma; + *minor_no=mi; + /* yes, this is a quick exit, but the easiest way */ + fclose(file); + return; + break; + } + } + fclose(file); + + ERRMSG_EXIT(EXIT_FAILURE,"%s: failed to find device in the /proc " \ + "filesystem (are you sure to have the right param line?)\n", + prog_name); +} + +char * +get_devname_from_devno(int devno,int verbosity) +{ + int major_no,minor_no; + int file_major,file_minor; + struct stat stat_buf; + int rc; + int found; + char *devname; + char tmpname[MAX_FILELEN]; + + DIR *dp; + struct dirent *direntp; + + /**** get minor number ****/ + get_xno_from_xno(&devno,&major_no,&minor_no,GIVEN_DEVNO); + + /**** get device file ****/ + if ((dp=opendir(SLASHDEV)) == NULL) + ERRMSG_EXIT(EXIT_FAILURE,"%s: unable to read " SLASHDEV \ + "\n",prog_name); + found=0; + while ((direntp=readdir(dp)) != NULL) { + strcpy(tmpname,SLASHDEV); + strcat(tmpname,direntp->d_name); + rc=stat(tmpname,&stat_buf); + if (!rc) { + file_major=(stat_buf.st_rdev>>8)&0xff; + file_minor=stat_buf.st_rdev&0xff; + if ((file_major==major_no) && (file_minor==minor_no)) { + found=1; + break; + } + } + } + if (found) { + devname=malloc(strlen(direntp->d_name)); + strcpy(devname,tmpname); + } + rc=closedir(dp); + if (rc<0) ERRMSG("%s: unable to close directory " SLASHDEV \ + "; continuing\n",prog_name); + if (found) + return devname; + + if (verbosity>=1) + printf("I didn't find device node in " SLASHDEV \ + "; trying to create a temporary node\n"); + + /**** get temp file and create device node *****/ + rc=mkstemp(tempfilename); + if (rc==-1) + ERRMSG_EXIT(EXIT_FAILURE,"%s: failed to get temporary " \ + "filename: %s\n",prog_name,strerror(errno)); + close(rc); + rc=unlink(tempfilename); + + rc=mknod(tempfilename,S_IFBLK|0600,(major_no<<8)+minor_no); + if (rc) + ERRMSG_EXIT(EXIT_FAILURE,"%s: failed to create temporary " \ + "device node %s: %s\n",prog_name,tempfilename, + strerror(errno)); + return tempfilename; +} + +char * +check_param(int mode,format_data_t data) +{ + char *s; + + if (NULL==(s=malloc(ERR_LENGTH))) + ERRMSG_EXIT(EXIT_FAILURE,"%s: not enough memory.\n",prog_name); + + if ((mode&CHECK_START)&&(data.start_unit<0)) { + strcpy(s,"start track must be greater than zero"); + goto exit; + } + if ((mode&CHECK_END)&&(data.stop_unit<-1)) { + strcpy(s,"end track must be -1 or greater than zero"); + goto exit; + } + if ((mode&CHECK_END)&&(data.start_unit>data.stop_unit)&& + (data.stop_unit!=-1)) { + strcpy(s,"end track must be higher than start track"); + goto exit; + } + + if ((mode&CHECK_BLKSIZE)&&(data.blksize<1)) { + strcpy(s,"blocksize must be a positive integer"); + goto exit; + } + if (mode&CHECK_BLKSIZE) while (data.blksize>0) { + if ((data.blksize%2)&&(data.blksize!=1)) { + strcpy(s,"blocksize must be a power of 2"); + goto exit; + } + data.blksize/=2; + } + + free(s); + return NULL; +exit: + return s; +} + +#define ASK_PRINTOUT printf("Please enter %s",output) +#define ASK_GETBUFFER fgets(buffer,sizeof(buffer),stdin) +#define ASK_SCANFORNUMBER(var) rc=sscanf(buffer,"%d%c",&var,&c) +#define ASK_COMPLAIN_FORMAT if ((rc==2)&&(c=='\n')) rc=1; \ + if (rc==-1) rc=1; /* this happens, if enter is pressed */ \ + if (rc!=1) printf(" -- wrong input, try again.\n") +#define ASK_CHECK_PARAM(mode) str=check_param(mode,params); \ + if (str!=NULL) { printf(" -- %s\n",str); rc=0; free(str); } + +format_data_t +ask_user_for_data(format_data_t params) +{ + char buffer[20]; /* should be enough for inputing track numbers */ + char c; + int i,rc; + char *str; + char output[60],o2[12]; + + i=params.start_unit; + do { + params.start_unit=i; + sprintf(output,"the start track of the range to format " \ + "[%d]: ",i); + ASK_PRINTOUT; + ASK_GETBUFFER; + ASK_SCANFORNUMBER(params.start_unit); + ASK_COMPLAIN_FORMAT; + ASK_CHECK_PARAM(CHECK_START); + } while (rc!=1); + + i=params.stop_unit; + do { + params.stop_unit=i; + sprintf(output,"the end track of the range to format ["); + if (i==-1) sprintf(o2,"END]: "); else + sprintf(o2,"%d]: ",i); + strcat(output,o2); + ASK_PRINTOUT; + ASK_GETBUFFER; + if ( (!strcasecmp(buffer,"end")) || + (!strcasecmp(buffer,"end\n")) ) { + rc=1; + params.stop_unit=-1; + } else { + ASK_SCANFORNUMBER(params.stop_unit); + ASK_COMPLAIN_FORMAT; + ASK_CHECK_PARAM(CHECK_END); + } + } while (rc!=1); + + i=params.blksize; + do { + params.blksize=i; + sprintf(output,"the blocksize of the formatting [%d]: ",i); + ASK_PRINTOUT; + ASK_GETBUFFER; + ASK_SCANFORNUMBER(params.blksize); + ASK_COMPLAIN_FORMAT; + ASK_CHECK_PARAM(CHECK_BLKSIZE); + } while (rc!=1); + + return params; +} + +void +do_format_dasd(char *dev_name,format_data_t format_params,int testmode, + int verbosity,int withoutprompt) +{ + int fd,rc; + struct stat stat_buf; + int minor_no,major_no,devno; + char inp_buffer[5]; /* to contain yes */ + + fd=open(dev_name,O_RDWR); + if (fd==-1) + ERRMSG_EXIT(EXIT_FAILURE,"%s: error opening device %s: " \ + "%s\n",prog_name,dev_name,strerror(errno)); + + if (verbosity>=1) { + } + + rc=stat(dev_name,&stat_buf); + if (rc) { + ERRMSG_EXIT(EXIT_FAILURE,"%s: error occured during stat: " \ + "%s\n",prog_name,strerror(errno)); + } else { + if (!S_ISBLK(stat_buf.st_mode)) + ERRMSG_EXIT(EXIT_FAILURE,"%s: file is not a " \ + "blockdevice.\n",prog_name); + major_no=(stat_buf.st_rdev>>8)&0xff; + minor_no=stat_buf.st_rdev&0xff; + } + + if ( ((withoutprompt)&&(verbosity>=1)) || + (!withoutprompt) ) { + get_xno_from_xno(&devno,&major_no,&minor_no, + GIVEN_MAJOR|GIVEN_MINOR); + printf("\nI am going to format the device %s in the " \ + "following way:\n",dev_name); + printf(" Device number of device : 0x%x\n",devno); + printf(" Major number of device : %u\n",major_no); + printf(" Minor number of device : %u\n",minor_no); + printf(" Start track : %d\n" \ + ,format_params.start_unit); + printf(" End track : "); + if (format_params.stop_unit==-1) + printf("last track of disk\n"); + else + printf("%d\n",format_params.stop_unit); + printf(" Blocksize : %d\n" \ + ,format_params.blksize); + if (testmode) printf("Test mode active, omitting ioctl.\n"); + } + + while (!testmode) { + if (!withoutprompt) { + printf("\n--->> ATTENTION! <<---\n"); + printf("All data in the specified range of that " \ + "device will be lost.\nType yes to continue" \ + ", no will leave the disk untouched: "); + fgets(inp_buffer,sizeof(inp_buffer),stdin); + if (strcasecmp(inp_buffer,"yes") && + strcasecmp(inp_buffer,"yes\n")) { + printf("Omitting ioctl call (disk will " \ + "NOT be formatted).\n"); + break; + } + } + + if ( !( (withoutprompt)&&(verbosity<1) )) + printf("Formatting the device. This may take a " \ + "while (get yourself a coffee).\n"); + rc=ioctl(fd,IOCTL_COMMAND,format_params); + if (rc) + ERRMSG_EXIT(EXIT_FAILURE,"%s: the dasd driver " \ + "returned with the following error " \ + "message:\n%s\n",prog_name,strerror(errno)); + printf("Finished formatting the device.\n"); + + break; + } + + rc=close(fd); + if (rc) + ERRMSG("%s: error during close: " \ + "%s; continuing.\n",prog_name,strerror(errno)); +} + + + +int main(int argc,char *argv[]) { + int verbosity; + int testmode; + int withoutprompt; + + char *dev_name; + int devno; + char *dev_filename,*devno_param_str,*range_param_str; + char *start_param_str,*end_param_str,*blksize_param_str; + + format_data_t format_params; + + int rc; + int oc; + char *endptr; + + char c1,c2,cbuffer[6]; /* should be able to contain -end plus 1 char */ + int i1,i2; + char *str; + + int start_specified,end_specified,blksize_specified; + int devfile_specified,devno_specified,range_specified; + + /******************* initialization ********************/ + + endptr=NULL; + + /* set default values */ + format_params.start_unit=0; + format_params.stop_unit=-1; + format_params.blksize=1024; + testmode=0; + verbosity=0; + withoutprompt=0; + start_specified=end_specified=blksize_specified=0; + devfile_specified=devno_specified=range_specified=0; + + /*************** parse parameters **********************/ + + /* avoid error message generated by getopt */ + opterr=0; + + while ( (oc=getopt(argc,argv,"r:s:e:b:n:f:hty?vV")) !=EOF) { + switch (oc) { + case 'y': + withoutprompt=1; + break; + + case 't': + testmode=1; + break; + + case 'v': + verbosity++; + break; + + case '?': /* fall-through */ + case ':': + exit_usage(EXIT_MISUSE); + + case 'h': + exit_usage(0); + + case 'V': + printf("%s version 0.99\n",prog_name); + exit(0); + + case 's' : + start_param_str=optarg; + start_specified++; + break; + + case 'e' : + end_param_str=optarg; + end_specified++; + break; + + case 'b' : + blksize_param_str=optarg; + blksize_specified++; + break; + + case 'n' : + devno_param_str=optarg; + devno_specified++; + break; + + case 'f' : + dev_filename=optarg; + devfile_specified++; + break; + case 'r' : + range_param_str=optarg; + range_specified++; + break; + } + } + + /* set default values */ + + /******************** checking of parameters **************/ + + /* convert range into -s and -e */ + CHECK_SPEC_MAX_ONCE(range_specified,"formatting range"); + + while (range_specified) { + start_specified++; + end_specified++; + + /* scan for 1 or 2 integers, separated by a dash */ + rc=sscanf(range_param_str,"%d%c%d%c",&i1,&c1,&i2,&c2); + if ((rc==3)&&(c1=='-')) { + format_params.start_unit=i1; + format_params.stop_unit=i2; + break; + } + if (rc==1) { + format_params.start_unit=i1; + break; + } + + /* scan for integer and -END */ + rc=sscanf(range_param_str,"%d%s",&i1,cbuffer); + if ((rc==2)&&(!strcasecmp(cbuffer,"-END"))) { + format_params.start_unit=i1; + format_params.stop_unit=-1; + break; + } + ERRMSG_EXIT(EXIT_MISUSE,"%s: specified range " \ + "is in invalid format\n",prog_name); + } + + if ((!devfile_specified)&&(!devno_specified)) + ERRMSG_EXIT(EXIT_MISUSE,"%s: device to format " \ + "not specified\n",prog_name); + + if ((devfile_specified+devno_specified)>1) + ERRMSG_EXIT(EXIT_MISUSE,"%s: device to format " \ + "can only be specified once\n",prog_name); + + if ((!start_specified)&&(!end_specified)&&(!range_specified)&& + (!blksize_specified)) { + format_params=ask_user_for_data(format_params); + } + + CHECK_SPEC_MAX_ONCE(start_specified,"start track"); + CHECK_SPEC_MAX_ONCE(end_specified,"end track"); + CHECK_SPEC_MAX_ONCE(blksize_specified,"blocksize"); + + if (devno_specified) + PARSE_PARAM_INTO(devno,devno_param_str,16,"device number"); + if (start_specified&&!range_specified) + PARSE_PARAM_INTO(format_params.start_unit,start_param_str,10, + "start track"); + if (end_specified&&!range_specified) + PARSE_PARAM_INTO(format_params.stop_unit,end_param_str,10, + "end track"); + if (blksize_specified) + PARSE_PARAM_INTO(format_params.blksize,blksize_param_str,10, + "blocksize"); + + /***********get dev_name *********************/ + dev_name=(devno_specified)? + get_devname_from_devno(devno,verbosity): + dev_filename; + + /*** range checking *********/ + str=check_param(CHECK_ALL,format_params); + if (str!=NULL) ERRMSG_EXIT(EXIT_MISUSE,"%s: %s\n",prog_name,str); + + /*************** issue the real command *****************/ + do_format_dasd(dev_name,format_params,testmode,verbosity, + withoutprompt); + + /*************** cleanup ********************************/ + if (strncmp(dev_name,TEMPFILENAME,TEMPFILENAMECHARS)==0) { + rc=unlink(dev_name); + if ((rc)&&(verbosity>=1)) + ERRMSG("%s: temporary device node %s could not be " \ + "removed: %s\n",prog_name,dev_name, + strerror(errno)); + } else { + if (devno_specified) { + /* so we have allocated space for the filename */ + free(dev_name); + } + } + + return 0; +} diff -u --recursive --new-file v2.2.13/linux/arch/s390/boot/ipleckd.S linux/arch/s390/boot/ipleckd.S --- v2.2.13/linux/arch/s390/boot/ipleckd.S Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/boot/ipleckd.S Tue Jan 4 10:12:12 2000 @@ -0,0 +1,289 @@ +# +# arch/s390/boot/ipleckd.S +# IPL record for 3380/3390 DASD +# +# S390 version +# Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation +# Author(s): Holger Smolinski +# +# +# FIXME: should use the countarea to determine the blocksize +# FIXME: should insert zeroes into memory when filling holes +# FIXME: calculate blkpertrack from rdc data and blksize + +# Usage of registers +# r1: ipl subchannel ( general use, dont overload without save/restore !) +# r10: +# r13: base register index to 0x0000 +# r14: callers address +# r15: temporary save register (we have no stack!) + .org 0 +.psw: .long 0x00080000,0x80000000+_start +.ccw1: .long 0x06000000,0x00001000 # Re-Read enough of bootsector to start +.ccw2: .long 0x00000000,0x00000000 # read countarea of record 1 to s/w. + + .org 0x58 +.Lextn: .long 0x000a0000,0x00000000+.Lextn +.Lsvcn: .long 0x000a0000,0x00000000+.Lsvcn +.Lprgn: .long 0x000a0000,0x00000000+.Lprgn +.Lmcn: .long 0x000a0000,0x00000000+.Lmcn +.Lion: .long 0x00080000,0x80000000+.Lionewaddr + + .org 0xd0 +.Lnull: + .long 0x00000000,0x00000000 + .org 0xe0 +.Llstad:.long 0x00000000,0x00000000 # sectorno + ct of bootlist + + .org 0xf0 # Lets start now... +_start: .globl _start + l %r1,0xb8 # get IPL-subchannel from lowcore + stsch .Lrdcdata + oi .Lrdcdata+5,0x80 # enable ssch + oi .Lrdcdata+27,0x01 # enalbe concurrent sense + msch .Lrdcdata + l %r2,.Lparm + mvc 0x0(8,%r2),.Lnull # set parmarea to null + lctl %c6,%c6,.Lc6 # enable all interrupts +.Lrdc: # read device characteristics + la %r6,.Lrdcccw + st %r6,.Lorb+8 # store cp-address to orb + bras %r15,.Lssch # start I/O + oi .Llodata+1,0x80 + lh %r5,.Lcountarea+6 # init r5 from countarea + stcm %r5,3,.Lrdccw+2 # and store into rd template *FIXME* + stcm %r5,3,.Llodata+14 # and store into lodata *FIXME* +.Lbootlist: + l %r2,.Llstad + l %r3,.Lblklst + lhi %r4,1 + bras %r14,.Lreadblks +.Lloader: + l %r10,.Lblklst # r10 is index to bootlist + lhi %r5,4 # r5: skip 4 blocks = firstpage.... +.Lkloop: + clc .Lnull(8),0(%r10) # test blocklist + jz .Lchkparm # end of list? + l %r2,0(%r10) # get startblock to r2 + slr %r4,%r4 # erase r4 + icm %r4,1,7(%r10) # get blockcount + slr %r3,%r3 # get address to r3 + icm %r3,0xe,4(%r10) + chi %r5,0 # still blocks to skip? + jz .Ldoread # no: start reading + cr %r5,%r4 # #skipblocks >= blockct? + jm .L007 # no: skip the blocks one by one +.L006: + sr %r5,%r4 # decrease number of blocks to skip + j .Lkcont # advance to next entry +.L007: + ahi %r2,1 # skip 1 block... + bctr %r4,0 # update blockct + ah %r3,.Lcountarea+6 # increment address + bct %r5,.L007 # 4 blocks skipped? +.Ldoread: + ltr %r2,%r2 # test startblock + jz .Lzeroes # startblocks is zero (hole) +.Ldiskread: + bras %r14,.Lreadblks + j .Lkcont +.Lzeroes: + lr %r2,%r3 + slr %r3,%r3 + icm %r3,3,.Lcountarea+6 # get blocksize +.L008: slr %r5,%r5 # no bytes to move + mvcle %r2,%r4,0(%r10) # fill zeroes to storage + jo .L008 # until block is filled + bctr %r4,0 # skip to next block + a %r2,.Lcountarea+6 # proceed loadaddress + jnz .Lzeroes # proceed for additional blocks +.Lkcont: + ahi %r10,8 + j .Lkloop +.Lchkparm: + lm %r3,%r4,.Lstart # load .Lstart and .Lparm + clc 0x0(4,%r4),.Lnull + je .Lrunkern + mvc 0x480(128,%r3),0(%r4) # move 1k-0x80 to parmarea + mvc 0x500(256,%r3),0x80(%r4) + mvc 0x600(256,%r3),0x180(%r4) + mvc 0x700(256,%r3),0x280(%r4) +.Lrunkern: + lhi %r2,17 + sll %r2,12 + st %r1,0xc6c(%r2) # store iplsubchannel to lowcore + st %r1,0xc6c # store iplsubchannel to lowcore + br %r3 +# This function does the start IO +# r2: number of first block to read ( input by caller ) +# r3: address to read data to ( input by caller ) +# r4: number of blocks to read ( input by caller ) +# r5: destroyed +# r6: blocks per track ( input by caller ) +# r7: number of heads +# r8: +# r9: +# r10: +# r11: temporary register +# r12: local use for base address +# r13: base address for module +# r14: address of caller for subroutine +# r15: temporary save register (since we have no stack) +.Lreadblks: + la %r12,.Ldeccw + st %r12,8+.Lorb # store cpaddr to orb + ahi %r12,0x10 # increment r12 to point to rdccw + oi 1(%r12),0x40 # set CC in rd template + # first setup the read CCWs + lr %r15,%r4 # save number or blocks + slr %r7,%r7 + icm %r7,3,.Lrdcdata+14 # load heads to r7 + clc .Lrdcdata+3(2),.L3390 + jne .L010 # 3380 or 3390 ? + lhi %r6,12 # setup r6 correct! + j .L011 +.L010: + lhi %r6,10 +.L011: + # loop for nbl times +.Lrdloop: + mvc 0(8,%r12),.Lrdccw # copy template to this ccw + st %r3,4(%r12) # store target address to this ccw + bct %r4,.L005 # decrement no of blks still to do + ni 1(%r12),0x3f # delete CC from last ccw + lr %r4,%r15 # restore number of blocks + # read CCWs are setup now + stcm %r4,3,.Llodata+2 # store blockno to lodata clears r4 + ar %r4,%r2 # r4 (clear): ebl = blk + nbl + bctr %r4,0 # decrement r4 ( last blk touched + srda %r2,32 # trk = blk / bpt, bot = blk % bpt + dr %r2,%r6 # r3: trk, r2: bot + ahi %r2,1 # bot++ ( we start counting at 1 ) + stcm %r2,1,.Llodata+12 # store bot to lodata + xr %r2,%r2 # cy = trk / heads, hd = trk % heads + dr %r2,%r7 # r3: cy, r2: hd + sll %r3,16 # combine to CCHH in r3 + or %r3,%r2 + st %r3,.Ldedata+8 # store cchh to dedata + st %r3,.Llodata+4 # store cchh to lodata + st %r3,.Llodata+8 # store cchh to lodata + lr %r15,%r5 # save r5 + srda %r4,32 # tr2 = ebl / bpt + dr %r4,%r6 # r5: tr2, r4: bot2 + xr %r4,%r4 # cy2 = tr2 / heads, hd2 = hd2 % heads + dr %r4,%r7 # r5: cy2, r4: hd2 + stcm %r5,3,.Ldedata+12 # store cy2,hd2 to dedata + stcm %r4,3,.Ldedata+14 # store cy2,hd2 to dedata + lr %r5,%r15 # restore r5 + # CCWs are setup now, arent they? + bras %r15,.Lssch # start I/O + br %r14 # return to caller +.L005: + ah %r3,.Lcountarea+6 # add blocksize to target address + ahi %r12,8 # add sizeof(ccw) to base address + j .Lrdloop +# end of function +# This function does the start IO +# r1: Subchannel number +# r8: ORB address +# r9: IRB address +.Lssch: + lhi %r13,10 # initialize retries +.L012: + ssch .Lorb # start I/O + jz .Ltpi # ok? + bras %r14,.Ldisab # error +.Ltpi: + lpsw .Lwaitpsw # load wait-PSW +.Lionewaddr: + c %r1,0xb8 # compare to ipl subhchannel + jnz .Ltpi # not equal: loop + clc 0xbc(4),.Lorb # cross check the intparm + jnz .Ltpi # not equal: loop + tsch .Lirb # get status + tm .Lirb+9,0xff # channel status ? + jz .L003 # CS == 0x00 + bras %r14,.Ldisab # error +.L003: + tm .Lirb+8,0xf3 # DS different from CE/DE + jz .L004 # ok ? + bct %r13,.L012 # retries <= 5 ? + bras %r14,.Ldisab # error +.L004: + tm .Lirb+8,0x04 # DE set? + jz .Ltpi # DE not set, loop +.Lsschend: + br %r15 # return to caller +# end of function +# In case of error goto disabled wait with %r14 containing the caller +.Ldisab: + st %r14,.Ldisabpsw+4 + lpsw .Ldisabpsw + +# FIXME pre-initialized data should be listed first +# NULLed storage can be taken from anywhere ;) +.L3390: + .word 0x3390 +.Lblklst: + .long 0x00002000 + .align 8 +.Ldisabpsw: + .long 0x000a0000,0x00000000 +.Lwaitpsw: + .long 0x020a0000,0x00000000+.Ltpi +.Lorb: + .long 0x0049504c,0x0080ff00 # intparm is " IPL" + .long 0x00000000 # CCW address +.Lc6: .long 0xff000000 +.Lstart: + .long 0x00010000 # do not separate .Lstart and .Lparm +.Lparm: + .long 0x00008000 # they are loaded with a LM +.Lrdcdata: + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 +.Lirb: + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 +.Lcountarea: + .word 0x0000 # cyl; + .word 0x0000 # head; + .byte 0x00 # record; + .byte 0x00 # key length; + .word 0x0000 # data length == blocksize; +.Ldedata: + .long 0x40c00000,0x00000000 + .long 0x00000000,0x00000000 +.Llodata: + .long 0x06000001,0x00000000 + .long 0x00000000,0x01000000 + .long 0x12345678 + .org 0x3c8 +.Lrdcccw: # CCW read device characteristics + .long 0x64400040,0x00000000+.Lrdcdata + .long 0x63400010,0x00000000+.Ldedata + .long 0x47400010,0x00000000+.Llodata + .long 0x12000008,0x00000000+.Lcountarea +.Ldeccw: + .long 0x63400010,0x00000000+.Ldedata +.Lloccw: + .long 0x47400010,0x00000000+.Llodata +.Lrdccw: + .long 0x86400000,0x00000000 + .org 0x400 +# end of pre initialized data is here CCWarea follows +# from here we load 1k blocklist +# end of function + diff -u --recursive --new-file v2.2.13/linux/arch/s390/boot/iplfba.S linux/arch/s390/boot/iplfba.S --- v2.2.13/linux/arch/s390/boot/iplfba.S Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/boot/iplfba.S Tue Jan 4 10:12:12 2000 @@ -0,0 +1,131 @@ +# +# Ipl block for fba devices +# Copyright (C) 1998 IBM Corporation +# Author(s): Martin Schwidefsky +# +# startup for ipl at address 0 +# start with restart + +# The first 24 byes are loaded by ipl to addresses 0-23 (a PSW and two CCWs). +# The CCWs on 8-23 are used as a continuation of the implicit ipl channel +# program. The fba ipl loader only uses the CCW on 8-15 to load the first 512 +# byte block to location 0-511 (the reading starts again at block 0, byte 0). +# The second CCW is used to store the location of the load list. + .org 0 + .long 0x00080000,0x80000000+_start # The first 24 byte are loaded + .long 0x02000000,0x20000200 # by ipl to addresses 0-23. + .long 0x00000001,0x00000001 # (PSW, one CCW & loadlist info). + + .globl _start +_start: + basr %r13,0 +.LPG0: + l %r1,0xb8 # load ipl subchannel number + lhi %r2,0x200 # location for the loadlist + lm %r3,%r4,0x10 # blocknr and length of loadlist + bras %r14,.Lloader # load loadlist + + lhi %r11,0x400 + lhi %r12,0x200 # load address of loadlist + l %r3,0(%r12) # get first block number + l %r4,4(%r12) # get first block count + la %r12,8(%r12) + j .Llistloop + .org 0x50 +.Llistloop: + lr %r2,%r11 # load address + lr %r5,%r4 # block count + mhi %r5,512 + la %r11,0(%r5,%r11) # update load address + bras %r14,.Lloader # load chunk of the image + l %r3,0(%r12) # get next block number + icm %r4,15,4(%r12) # get next block count + la %r12,8(%r12) + jnz .Llistloop + +# +# everything loaded, go for it +# + l %r1,.Lstart-.LPG0(%r13) + br %r1 + +# +# subroutine for loading a sequence of block from fba +# %r2: load address (24 bit address) +# %r3: number of first block (unsigned long) +# %r4: number of blocks to load (unsigned short) +# + .org 0xC0 +.Lloader: + la %r5,.Llo-.LPG0(%r13) + sth %r4,2(%r5) # initialize block count + st %r3,4(%r5) # initialize block number + la %r5,.Lccws-.LPG0(%r13) + mhi %r4,512 + sth %r4,22(%r5) # initialize byte count + icm %r2,8,16(%r5) + st %r2,16(%r5) # initialize CCW data address + + slr %r2,%r2 + la %r3,.Lorb-.LPG0(%r13) # r2 = address of orb into r2 + la %r4,.Ltinfo-.LPG0(%r13) # r3 = address of tpi info block + la %r5,.Lirb-.LPG0(%r13) # r4 = address of irb + + lctl %c6,%c6,.Lc6-.LPG0(%r13) +.Lldlp: + ssch 0(%r3) # read blocks +.Ltpi: + tpi 0(%r4) # test pending interrupt + jz .Ltpi + c %r1,0(%r4) # compare subchannel number + jne .Ltpi + tsch 0(%r5) + slr %r0,%r0 + tm 8(%r5),0x82 # do we have a problem ? + jnz .Ldwpsw + tm 8(%r5),0x04 # got device end ? + jz .Ltpi +.Lexit: + br %r14 + + .align 8 +.Ldwpsw:.long 0x000a0000,0x00000000 +.Lorb: .long 0x00000000,0x0000ff00,.Lccws +.Ltinfo:.long 0 +.Lirb: .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +.Lc6: .long 0xff000000 +.Lloadp:.long 0,0 +.Lparm: .long 0x10400 +.Lstart:.long 0x10000 + .align 8 +.Lccws: .long 0x63000000+.Lde,0x60000010 # define extent + .long 0x43000000+.Llo,0x60000008 # locate +# offset 1 in read CCW: data address (24 bit) +# offset 6 in read CCW: number of bytes (16 bit) + .long 0x42000000,0x20000000 # read +.Lde: .long 0x40000200,0x00000000 + .long 0x00000000,0x00001000 +# offset 2 in .Llo: block count (unsigned short) +# offset 4 in .Llo: block number (unsigned long) +.Llo: .long 0x06000000,0x00000000 + + .org 0x200 + .long 0x00000002,0x0000007f + .long 0x00000081,0x0000007f + .long 0x00000100,0x0000007f + .long 0x0000017f,0x0000007f + .long 0x000001fe,0x0000007f + .long 0x0000027d,0x0000007f + .long 0x000002fc,0x0000007f + .long 0x0000037b,0x0000007f + .long 0x000003fa,0x0000007f + .long 0x00000479,0x0000007f + .long 0x000004f8,0x0000007f + .long 0x00000577,0x0000007f + .long 0x000005f6,0x0000007f + .long 0x00000675,0x0000007f + .long 0x000006f4,0x0000007f + .long 0x00000773,0x0000003f + .long 0x00000000,0x00000000 + .org 0x400 + diff -u --recursive --new-file v2.2.13/linux/arch/s390/boot/silo.c linux/arch/s390/boot/silo.c --- v2.2.13/linux/arch/s390/boot/silo.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/boot/silo.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,408 @@ +/* + * arch/s390/boot/silo.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Holger Smolinski + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* from dasd.h */ +#define DASD_PARTN_BITS 2 +#define BIODASDRWTB _IOWR('D',5,int) +/* end */ + +#define PRINT_LEVEL(x,y...) if ( silo_options.verbosity >= x ) printf(y) +#define ERROR_LEVEL(x,y...) if ( silo_options.verbosity >= x ) fprintf(stderr,y) +#define TOGGLE(x) ((x)=((x)?(0):(1))) +#define GETARG(x) {int len=strlen(optarg);x=malloc(len);strncpy(x,optarg,len);PRINT_LEVEL(1,"%s set to %s\n",#x,optarg);} + +#define ITRY(x) if ( (x) == -1 ) { ERROR_LEVEL(0,"%s (line:%d) '%s' returned %d='%s'\n", __FILE__,__LINE__,#x,errno,strerror(errno)); usage(); exit(1); } +#define NTRY(x) if ( (x) == 0 ) { ERROR_LEVEL(0,"%s (line:%d) '%s' returned %d='%s'\n", __FILE__,__LINE__,#x,errno,strerror(errno)); usage(); exit(1); } + +#define MAX_CLUSTERS 256 +#define PARTN_MASK ((1 << DASD_PARTN_BITS) - 1) + +struct silo_options + { + short int verbosity; + struct + { + unsigned char testonly; + } + flags; + char *image; + char *ipldevice; + char *parmfile; + char *ramdisk; + char *bootsect; + } +silo_options = +{ + 1, /* verbosity */ + { + 0, /* testonly */ + } + , + "./image", /* image */ + NULL, /* ipldevice */ + NULL, /* parmfile */ + NULL, /* initrd */ + "ipleckd.boot", /* bootsector */ +}; + +struct blockdesc + { + unsigned long off; + unsigned short ct; + unsigned long addr; + }; + +struct blocklist + { + struct blockdesc blk[MAX_CLUSTERS]; + unsigned short ix; + }; + +void +usage (void) +{ + printf ("Usage:\n"); + printf ("silo -d ipldevice [additional options]\n"); + printf ("-d /dev/node : set ipldevice to /dev/node\n"); + printf ("-f image : set image to image\n"); + printf ("-p parmfile : set parameter file to parmfile\n"); + printf ("-b bootsect : set bootsector to bootsect\n"); + printf ("Additional options\n"); + printf ("-v: increase verbosity level\n"); + printf ("-v#: set verbosity level to #\n"); + printf ("-t: toggle testonly flag\n"); + printf ("-h: print this message\n"); + printf ("-?: print this message\n"); +} + +int +parse_options (struct silo_options *o, int argc, char *argv[]) +{ + int rc = 0; + int oc; + if (argc == 1) + { + usage (); + exit (0); + } + while ((oc = getopt (argc, argv, "f:d:p:r:b:B:h?v::t")) != -1) + { + switch (oc) + { + case 't': + TOGGLE (o->flags.testonly); + PRINT_LEVEL (1, "Testonly flag is now %sactive\n", o->flags.testonly ? "" : "in"); + break; + case 'v': + { + unsigned short v; + if (optarg && sscanf (optarg, "%hu", &v)) + o->verbosity = v; + else + o->verbosity++; + PRINT_LEVEL (1, "Verbosity value is now %hu\n", o->verbosity); + break; + } + case 'h': + case '?': + usage (); + break; + case 'd': + GETARG (o->ipldevice); + break; + case 'f': + GETARG (o->image); + break; + case 'p': + GETARG (o->parmfile); + break; + case 'r': + GETARG (o->ramdisk); + break; + case 'b': + GETARG (o->bootsect); + break; + default: + rc = EINVAL; + break; + } + } + return rc; +} + +int +verify_device (char *name) +{ + int rc = 0; + struct stat dst; + struct stat st; + ITRY (stat (name, &dst)); + if (S_ISBLK (dst.st_mode)) + { + if (!(MINOR (dst.st_rdev) & PARTN_MASK)) + { + rc = dst.st_rdev; + } + else + /* invalid MINOR & PARTN_MASK */ + { + ERROR_LEVEL (1, "Cannot boot from partition %d %d %d", + (int) PARTN_MASK, (int) MINOR (dst.st_rdev), (int) (PARTN_MASK & MINOR (dst.st_rdev))); + rc = -1; + errno = EINVAL; + } + } + else + /* error S_ISBLK */ + { + ERROR_LEVEL (1, "%s is no block device\n", name); + rc = -1; + errno = EINVAL; + } + return rc; +} + +int +verify_file (char *name, int dev) +{ + int rc = 0; + struct stat dst; + struct stat st; + int bs = 1024; + int l; + ITRY (stat (name, &dst)); + if (S_ISREG (dst.st_mode)) + { + if ((unsigned) MAJOR (dev) == (unsigned) MAJOR (dst.st_dev) && (unsigned) MINOR (dev) == (unsigned) (MINOR (dst.st_dev) & ~PARTN_MASK)) + { + /* whatever to do if all is ok... */ + } + else + /* devicenumber doesn't match */ + { + ERROR_LEVEL (1, "%s is not on device (%d/%d) but on (%d/%d)\n", name, (unsigned) MAJOR (dev), (unsigned) MINOR (dev), (unsigned) MAJOR (dst.st_dev), (unsigned) (MINOR (dst.st_dev) & ~PARTN_MASK)); + rc = -1; + errno = EINVAL; + } + } + else + /* error S_ISREG */ + { + ERROR_LEVEL (1, "%s is neither regular file nor linkto one\n", name); + rc = -1; + errno = EINVAL; + } + return rc; +} + +int +verify_options (struct silo_options *o) +{ + int rc = 0; + int dev = 0; + int crc = 0; + if (!o->ipldevice || !o->image || !o->bootsect) + { + usage (); + exit (1); + } + PRINT_LEVEL (1, "IPL device is: '%s'", o->ipldevice); + + ITRY (dev = verify_device (o->ipldevice)); + PRINT_LEVEL (2, "...ok...(%d/%d)", (unsigned short) MAJOR (dev), (unsigned short) MINOR (dev)); + PRINT_LEVEL (1, "\n"); + + PRINT_LEVEL (0, "bootsector is: '%s'", o->bootsect); + ITRY (verify_file (o->bootsect, dev)); + PRINT_LEVEL (1, "...ok..."); + PRINT_LEVEL (0, "\n"); + + PRINT_LEVEL (0, "Kernel image is: '%s'", o->image); + ITRY (verify_file (o->image, dev)); + PRINT_LEVEL (1, "...ok..."); + PRINT_LEVEL (0, "\n"); + + if (o->parmfile) + { + PRINT_LEVEL (0, "parameterfile is: '%s'", o->parmfile); + ITRY (verify_file (o->parmfile, dev)); + PRINT_LEVEL (1, "...ok..."); + PRINT_LEVEL (0, "\n"); + } + + if (o->ramdisk) + { + PRINT_LEVEL (0, "initialramdisk is: '%s'", o->ramdisk); + ITRY (verify_file (o->ramdisk, dev)); + PRINT_LEVEL (1, "...ok..."); + PRINT_LEVEL (0, "\n"); + } + + return crc; +} + + +int +add_file_to_blocklist (char *name, struct blocklist *lst, long addr) +{ + int fd; + int devfd; + struct stat fst; + int i; + int blk; + int bs; + int blocks; + + int rc = 0; + + ITRY (fd = open (name, O_RDONLY)); + ITRY (fstat (fd, &fst)); + ITRY (mknod ("/tmp/silodev", S_IFBLK | S_IRUSR | S_IWUSR, fst.st_dev)); + ITRY (devfd = open ("/tmp/silodev", O_RDONLY)); + ITRY (ioctl (fd, FIGETBSZ, &bs)); + blocks = (fst.st_size + bs - 1) / bs; + for (i = 0; i < blocks; i++) + { + blk = i; + ITRY (ioctl (fd, FIBMAP, &blk)); + if (blk) + { + int oldblk = blk; + ITRY (ioctl (devfd, BIODASDRWTB, &blk)); + if (blk <= 0) + { + ERROR_LEVEL (0, "BIODASDRWTB on blk %d returned %d\n", oldblk, blk); + break; + } + } + else + { + PRINT_LEVEL (1, "Filled hole on blk %d\n", i); + } + if (lst->ix == 0 || i == 0 || + lst->blk[lst->ix - 1].ct >= 128 || + (lst->blk[lst->ix - 1].off + lst->blk[lst->ix - 1].ct != blk && + !(lst->blk[lst->ix - 1].off == 0 && blk == 0))) + { + if (lst->ix >= MAX_CLUSTERS) + { + rc = 1; + errno = ENOMEM; + break; + } + lst->blk[lst->ix].off = blk; + lst->blk[lst->ix].ct = 1; + lst->blk[lst->ix].addr = addr + i * bs; + lst->ix++; + } + else + { + lst->blk[lst->ix - 1].ct++; + } + } + ITRY(unlink("/tmp/silodev")); + return rc; +} + +int +write_bootsect (char *ipldevice, char *bootsect, struct blocklist *blklst) +{ + int i; + int s_fd, d_fd, b_fd, bd_fd; + struct stat s_st, d_st, b_st; + int rc=0; + int bs, boots; + char buffer[4096]; + ITRY (d_fd = open (ipldevice, O_RDWR | O_SYNC)); + ITRY (fstat (d_fd, &d_st)); + ITRY (s_fd = open ("boot.map", O_RDWR | O_TRUNC | O_CREAT | O_SYNC)); + ITRY (verify_file (bootsect, d_st.st_rdev)); + for (i = 0; i < blklst->ix; i++) + { + int offset = blklst->blk[i].off; + int addrct = blklst->blk[i].addr | (blklst->blk[i].ct & 0xff); + PRINT_LEVEL (1, "ix %i: offset: %06x count: %02x address: 0x%08x\n", i, offset, blklst->blk[i].ct & 0xff, blklst->blk[i].addr); + NTRY (write (s_fd, &offset, sizeof (int))); + NTRY (write (s_fd, &addrct, sizeof (int))); + } + ITRY (ioctl (s_fd,FIGETBSZ, &bs)); + ITRY (stat ("boot.map", &s_st)); + if (s_st.st_size > bs ) + { + ERROR_LEVEL (0,"boot.map is larger than one block\n"); + rc = -1; + errno = EINVAL; + } + boots=0; + ITRY (mknod ("/tmp/silodev", S_IFBLK | S_IRUSR | S_IWUSR, s_st.st_dev)); + ITRY (bd_fd = open ("/tmp/silodev", O_RDONLY)); + ITRY ( ioctl(s_fd,FIBMAP,&boots)); + ITRY (ioctl (bd_fd, BIODASDRWTB, &boots)); + PRINT_LEVEL (1, "Bootmap is in block no: 0x%08x\n", boots); + close (bd_fd); + close(s_fd); + ITRY (unlink("/tmp/silodev")); + /* Now patch the bootsector */ + ITRY (b_fd = open (bootsect, O_RDONLY)); + NTRY (read (b_fd, buffer, 4096)); + memset (buffer + 0xe0, 0, 8); + *(int *) (buffer + 0xe0) = boots; + NTRY (write (d_fd, buffer, 4096)); + NTRY (write (d_fd, buffer, 4096)); + close (b_fd); + close (d_fd); + return rc; +} + +int +do_silo (struct silo_options *o) +{ + int rc = 0; + + int device_fd; + int image_fd; + struct blocklist blklist; + memset (&blklist, 0, sizeof (struct blocklist)); + ITRY (add_file_to_blocklist (o->image, &blklist, 0x00000000)); + if (o->parmfile) + { + ITRY (add_file_to_blocklist (o->parmfile, &blklist, 0x00008000)); + } + if (o->ramdisk) + { + ITRY (add_file_to_blocklist (o->ramdisk, &blklist, 0x00800000)); + } + ITRY (write_bootsect (o->ipldevice, o->bootsect, &blklist)); + + return rc; +} + +int +main (int argct, char *argv[]) +{ + int rc = 0; + ITRY (parse_options (&silo_options, argct, argv)); + ITRY (verify_options (&silo_options)); + ITRY (do_silo (&silo_options)); + return rc; +} diff -u --recursive --new-file v2.2.13/linux/arch/s390/config.in linux/arch/s390/config.in --- v2.2.13/linux/arch/s390/config.in Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/config.in Tue Jan 4 10:12:12 2000 @@ -0,0 +1,68 @@ + +# For a description of the syntax of this configuration file, +# see the Configure script. +# +mainmenu_name "Linux Kernel Configuration" +define_bool CONFIG_ARCH_S390 y + +mainmenu_option next_comment +comment 'Code maturity level options' +bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL +endmenu + +mainmenu_option next_comment +comment 'Processor type and features' +bool 'Symmetric multi-processing support' CONFIG_SMP +bool 'IEEE FPU emulation' CONFIG_IEEEFPU_EMULATION +endmenu + +mainmenu_option next_comment +comment 'Loadable module support' +bool 'Enable loadable module support' CONFIG_MODULES +if [ "$CONFIG_MODULES" = "y" ]; then + bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS + bool 'Kernel module loader' CONFIG_KMOD +fi +endmenu + +mainmenu_option next_comment +comment 'General setup' +bool 'Fast IRQ handling' CONFIG_FAST_IRQ +bool 'Builtin IPL record support' CONFIG_IPL +if [ "$CONFIG_IPL" = "y" ]; then + choice 'IPL method generated into head.S' \ + "tape CONFIG_IPL_TAPE \ + vm_reader CONFIG_IPL_VM" tape +fi +bool 'Networking support' CONFIG_NET +bool 'System V IPC' CONFIG_SYSVIPC +bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT +bool 'Sysctl support' CONFIG_SYSCTL +tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF + +endmenu + +source drivers/s390/Config.in + +if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in +fi + +source fs/Config.in + +# source drivers/char/Config.in + +mainmenu_option next_comment +comment 'Kernel hacking' + +#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC +bool 'Kernel profiling support' CONFIG_PROFILE +if [ "$CONFIG_PROFILE" = "y" ]; then + int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 +fi +if [ "$CONFIG_CTC" = "y" ]; then + bool 'Remote GDB kernel debugging' CONFIG_REMOTE_DEBUG +fi +# this does not work. bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ +endmenu + diff -u --recursive --new-file v2.2.13/linux/arch/s390/defconfig linux/arch/s390/defconfig --- v2.2.13/linux/arch/s390/defconfig Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/defconfig Tue Jan 4 10:12:12 2000 @@ -0,0 +1,157 @@ +# +# Automatically generated by make menuconfig: don't edit +# +CONFIG_ARCH_S390=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Processor type and features +# +CONFIG_SMP=y +CONFIG_IEEEFPU_EMULATION=y + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_MODVERSIONS is not set +# CONFIG_KMOD is not set + +# +# General setup +# +CONFIG_FAST_IRQ=y +CONFIG_IPL=y +# CONFIG_IPL_TAPE is not set +CONFIG_IPL_VM=y +CONFIG_NET=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +CONFIG_BINFMT_ELF=y + +# +# S/390 block device drivers +# +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_MDISK=y +# CONFIG_MDISK_SYNC is not set +CONFIG_DASD=y +CONFIG_DASD_ECKD=y + +# +# S/390 Network device support +# +CONFIG_NETDEVICES=y +CONFIG_CTC=y +# CONFIG_IUCV is not set +# CONFIG_DUMMY is not set +CONFIG_NET_ETHERNET=y +# CONFIG_TR is not set + +# +# S/390 Terminal and Console options +# +CONFIG_3215=y +CONFIG_3215_CONSOLE=y +CONFIG_HWC=y +CONFIG_HWC_CONSOLE=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_NETLINK=y +# CONFIG_RTNETLINK is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_FIREWALL is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_IP_ROUTER is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_ALIAS is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_RARP is not set +CONFIG_SKB_LARGE=y +# CONFIG_IPV6 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_BRIDGE is not set +# CONFIG_LLC is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set +# CONFIG_CPU_IS_SLOW is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Filesystems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFSD is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set + +# +# Partition Types +# +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MAC_PARTITION is not set +# CONFIG_SMD_DISKLABEL is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_NLS is not set + +# +# Kernel hacking +# +# CONFIG_PROFILE is not set +# CONFIG_REMOTE_DEBUG is not set +# CONFIG_MAGIC_SYSRQ is not set diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/Makefile linux/arch/s390/kernel/Makefile --- v2.2.13/linux/arch/s390/kernel/Makefile Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/Makefile Tue Jan 4 10:12:12 2000 @@ -0,0 +1,51 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +ifdef CONFIG_SMP +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o +else +.S.o: + $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o +endif + +all: kernel.o head.o init_task.o + +O_TARGET := kernel.o +O_OBJS := lowcore.o entry.o bitmap.o traps.o time.o process.o irq.o \ + setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ + s390fpu.o s390io.o + +OX_OBJS := s390ksyms.o +MX_OBJS := + +ifdef CONFIG_SMP +O_OBJS += smp.o +endif + +ifeq ($(CONFIG_IEEEFPU_EMULATION),y) + O_OBJS += mathemu.o floatlib.o +endif + +# +# Kernel debugging +# +ifdef CONFIG_REMOTE_DEBUG +O_OBJS += gdb-stub.o #gdb-low.o +endif + + + +include $(TOPDIR)/Rules.make + + + + + + diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/bitmap.S linux/arch/s390/kernel/bitmap.S --- v2.2.13/linux/arch/s390/kernel/bitmap.S Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/bitmap.S Tue Jan 4 10:12:12 2000 @@ -0,0 +1,37 @@ +/* + * arch/s390/kernel/bitmap.S + * Bitmaps for set_bit, clear_bit, test_and_set_bit, ... + * See include/asm-s390/{bitops.h|posix_types.h} for details + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + + .globl _oi_bitmap +_oi_bitmap: + .byte 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80 + + .globl _ni_bitmap +_ni_bitmap: + .byte 0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F + + .globl _zb_findmap +_zb_findmap: + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,7 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,8 + diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/cpcmd.c linux/arch/s390/kernel/cpcmd.c --- v2.2.13/linux/arch/s390/kernel/cpcmd.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/cpcmd.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,53 @@ +/* + * arch/s390/kernel/cpcmd.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +#include +#include +#include + +void cpcmd(char *cmd, char *response, int rlen) +{ + const int mask = 0x40000000L; + char obuffer[128]; + int olen; + + olen = strlen(cmd); + strcpy(obuffer, cmd); + ASCEBC(obuffer,olen); + + if (response != NULL && rlen > 0) { + asm volatile ("LRA 2,0(0,%0)\n\t" + "LR 4,%1\n\t" + "O 4,%4\n\t" + "LRA 3,0(0,%2)\n\t" + "LR 5,%3\n\t" + ".long 0x83240008 # Diagnose 83\n\t" + : /* no output */ + : "a" (obuffer), "d" (olen), + "a" (response), "d" (rlen), "m" (mask) + : "2", "3", "4", "5" ); + EBCASC(response, rlen); + } else { + asm volatile ("LRA 2,0(0,%0)\n\t" + "LR 3,%1\n\t" + ".long 0x83230008 # Diagnose 83\n\t" + : /* no output */ + : "a" (obuffer), "d" (olen) + : "2", "3" ); + } +} + +int sys_msgcp(char *str) +{ + char buffer[256]; + + sprintf(buffer, "MSG * %s", str); + cpcmd(buffer, NULL, 0); + cpcmd("STOP", NULL, 0); +} + diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/cpcmd.h linux/arch/s390/kernel/cpcmd.h --- v2.2.13/linux/arch/s390/kernel/cpcmd.h Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/cpcmd.h Tue Jan 4 10:12:12 2000 @@ -0,0 +1,14 @@ +/* + * arch/s390/kernel/cpcmd.h + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +#ifndef __CPCMD__ +#define __CPCMD__ + +extern void cpcmd(char *cmd, char *response, int rlen); + +#endif diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/ebcdic.c linux/arch/s390/kernel/ebcdic.c --- v2.2.13/linux/arch/s390/kernel/ebcdic.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/ebcdic.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,242 @@ +/* + * arch/s390/kernel/ebcdic.c + * ECBDIC -> ASCII, ASCII -> ECBDIC, + * upper to lower case (EBCDIC) conversion tables. + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky + * Martin Peschke + */ + +#include + +/* + * ASCII (IBM PC 437) -> EBCDIC 037 + */ +__u8 _ascebc[256] = +{ + /*00 NUL SOH STX ETX EOT ENQ ACK BEL */ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, + /*08 BS HT LF VT FF CR SO SI */ + /* ->NL */ + 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /*10 DLE DC1 DC2 DC3 DC4 NAK SYN ETB */ + 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, + /*18 CAN EM SUB ESC FS GS RS US */ + /* ->IGS ->IRS ->IUS */ + 0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F, + /*20 SP ! " # $ % & ' */ + 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, + /*28 ( ) * + , - . / */ + 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, + /*30 0 1 2 3 4 5 6 7 */ + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + /*38 8 9 : ; < = > ? */ + 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, + /*40 @ A B C D E F G */ + 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + /*48 H I J K L M N O */ + 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + /*50 P Q R S T U V W */ + 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, + /*58 X Y Z [ \ ] ^ _ */ + 0xE7, 0xE8, 0xE9, 0xBA, 0xE0, 0xBB, 0xB0, 0x6D, + /*60 ` a b c d e f g */ + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + /*68 h i j k l m n o */ + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + /*70 p q r s t u v w */ + 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + /*78 x y z { | } ~ DL */ + 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, + /*80*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*88*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*90*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*98*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*A0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*A8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*B0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*B8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*C0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*C8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*D0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*D8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*E0 sz */ + 0x3F, 0x59, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*E8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*F0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*F8*/ + 0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0xEA, 0x3F, 0xFF +}; + +/* + * EBCDIC 037 -> ASCII (IBM PC 437) + */ +__u8 _ebcasc[256] = +{ + /* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */ + 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, + /* 0x08 -GE -SPS -RPT VT FF CR SO SI */ + 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC + -ENP ->LF */ + 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, + /* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB + -IUS */ + 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + /* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC + -INP */ + 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, + /* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL + -SW */ + 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, + /* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */ + 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, + /* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */ + 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, + /* 0x40 SP RSP ä ---- */ + 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, + /* 0x48 . < ( + | */ + 0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, + /* 0x50 & ---- */ + 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, + /* 0x58 ß ! $ * ) ; */ + 0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA, + /* 0x60 - / ---- Ä ---- ---- ---- */ + 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, + /* 0x68 ---- , % _ > ? */ + 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, + /* 0x70 ---- ---- ---- ---- ---- ---- ---- */ + 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + /* 0x78 * ` : # @ ' = " */ + 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, + /* 0x80 * a b c d e f g */ + 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + /* 0x88 h i ---- ---- ---- */ + 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, + /* 0x90 ° j k l m n o p */ + 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, + /* 0x98 q r ---- ---- */ + 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, + /* 0xA0 ~ s t u v w x */ + 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + /* 0xA8 y z ---- ---- ---- ---- */ + 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, + /* 0xB0 ^ ---- § ---- */ + 0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, + /* 0xB8 ---- [ ] ---- ---- ---- ---- */ + 0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07, + /* 0xC0 { A B C D E F G */ + 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + /* 0xC8 H I ---- ö ---- */ + 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, + /* 0xD0 } J K L M N O P */ + 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + /* 0xD8 Q R ---- ü */ + 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, + /* 0xE0 \ S T U V W X */ + 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + /* 0xE8 Y Z ---- Ö ---- ---- ---- */ + 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, + /* 0xF0 0 1 2 3 4 5 6 7 */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + /* 0xF8 8 9 ---- ---- Ü ---- ---- ---- */ + 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07 +}; + + +/* + * EBCDIC 037 conversion table: + * from upper to lower case + */ +__u8 _ebc_tolower[256] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + 0x60, 0x61, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9C, 0x9F, + 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, + 0xA8, 0xA9, 0xAA, 0xAB, 0x8C, 0x8D, 0x8E, 0xAF, + 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, + 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, + 0xC0, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, + 0xD0, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, + 0xE0, 0xE1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, + 0xA8, 0xA9, 0xEA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xDB, 0xDC, 0xDD, 0xDE, 0xFF +}; + + +/* + * EBCDIC 037 conversion table: + * from lower to upper case + */ +__u8 _ebc_toupper[256] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x40, 0x41, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0x50, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, + 0x80, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0x8A, 0x8B, 0xAC, 0xAD, 0xAE, 0x8F, + 0x90, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, + 0xD8, 0xD9, 0x9A, 0x9B, 0x9E, 0x9D, 0x9E, 0x9F, + 0xA0, 0xA1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, + 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, + 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, + 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0xCA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, + 0xD8, 0xD9, 0xDA, 0xFB, 0xFC, 0xFD, 0xFE, 0xDF, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF +}; diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/entry.S linux/arch/s390/kernel/entry.S --- v2.2.13/linux/arch/s390/kernel/entry.S Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/entry.S Tue Jan 4 10:12:12 2000 @@ -0,0 +1,961 @@ +/* + * arch/s390/kernel/entry.S + * S390 low-level entry points. + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * Hartmut Penner (hp@de.ibm.com), + * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + */ + +#include +#include +#include +#include +#include +#define ASSEMBLY +#include +#include + + +/* + * stack layout for the system_call stack entry + * Martin please don't modify these back to hard coded values + * You know how bad I'm at mental arithmetic DJB & it gives + * me grief when I modify the pt_regs + */ +SP_PTREGS = STACK_FRAME_OVERHEAD +SP_PSW = SP_PTREGS +SP_R0 = (SP_PSW+PSW_MASK_SIZE+PSW_ADDR_SIZE) +SP_R1 = (SP_R0+GPR_SIZE) +SP_R2 = (SP_R1+GPR_SIZE) +SP_R3 = (SP_R2+GPR_SIZE) +SP_R4 = (SP_R3+GPR_SIZE) +SP_R5 = (SP_R4+GPR_SIZE) +SP_R6 = (SP_R5+GPR_SIZE) +SP_R7 = (SP_R6+GPR_SIZE) +SP_R8 = (SP_R7+GPR_SIZE) +SP_R9 = (SP_R8+GPR_SIZE) +SP_RA = (SP_R9+GPR_SIZE) +SP_RB = (SP_RA+GPR_SIZE) +SP_RC = (SP_RB+GPR_SIZE) +SP_RD = (SP_RC+GPR_SIZE) +SP_RE = (SP_RD+GPR_SIZE) +SP_RF = (SP_RE+GPR_SIZE) +SP_AREGS = (SP_RF+GPR_SIZE) +SP_ORIG_R2 = (SP_AREGS+(NUM_ACRS*ACR_SIZE)) +SP_TRAP = (SP_ORIG_R2+GPR_SIZE) +#if CONFIG_REMOTE_DEBUG +SP_CRREGS = (SP_TRAP+4) +/* fpu registers are saved & restored by the gdb stub itself */ +SP_FPC = (SP_CRREGS+(NUM_CRS*CR_SIZE)) +SP_FPRS = (SP_FPC+FPC_SIZE+FPC_PAD_SIZE) +SP_SIZE = (SP_FPRS+(NUM_FPRS*FPR_SIZE)) +#else +SP_SIZE = (SP_TRAP+4) +#endif +/* + * these defines are offsets into the thread_struct + */ +_TSS_PTREGS = 0 +_TSS_FPRS = (_TSS_PTREGS+8) +_TSS_AR2 = (_TSS_FPRS+136) +_TSS_AR4 = (_TSS_AR2+4) +_TSS_KSP = (_TSS_AR4+4) +_TSS_USERSEG = (_TSS_KSP+4) +_TSS_ERROR = (_TSS_USERSEG+4) +_TSS_PROT = (_TSS_ERROR+4) +_TSS_TRAP = (_TSS_PROT+4) +_TSS_MM = (_TSS_TRAP+4) +_TSS_PER = (_TSS_MM+8) + +/* + * these are offsets into the task-struct. + */ +state = 0 +flags = 4 +sigpending = 8 +addr_limit = 12 +exec_domain = 20 +need_resched = 24 + +/* PSW related defines */ +disable = 0xFC +enable = 0x03 +daton = 0x04 + + +#if 0 +/* some code left lying around in case we need a + * printk for debugging purposes + */ + sysc_printk: .long printk + sysc_msg: .string "<2>r15 %X\n" + .align 4 + +# basr %r13,0 + l %r0,SP_PSW+4(%r15) + sll %r0,1 + chi %r0,0 + jnz sysc_dn + l %r9,sysc_printk-sysc_lit(%r13) + la %r2,sysc_msg-sysc_lit(%r13) + lr %r3,%r15 + basr %r14,%r9 +sysc_dn: +#endif + +/* + * Register usage in interrupt handlers: + * R9 - pointer to current task structure + * R13 - pointer to literal pool + * R14 - return register for function calls + * R15 - kernel stack pointer + */ + +#define SAVE_ALL1(psworg) \ + st %r15,__LC_SAVE_AREA ; \ + tm psworg+1,0x01 ; /* test problem state bit */ \ + jz 0f ; /* skip stack setup save */ \ + l %r15,__LC_KERNEL_STACK ; /* problem state -> load ksp */ \ +0: ahi %r15,-SP_SIZE ; /* make room for registers & psw */ \ + srl %r15,3 ; \ + sll %r15,3 ; /* align stack pointer to 8 */ \ + stm %r0,%r14,SP_R0(%r15) ; /* store gprs 0-14 to kernel stack */ \ + st %r2,SP_ORIG_R2(%r15) ; /* store original content of gpr 2 */ \ + mvc SP_RF(4,%r15),__LC_SAVE_AREA ; /* move R15 to stack */ \ + stam %a0,%a15,SP_AREGS(%r15) ; /* store access registers to kst. */ \ + mvc SP_PSW(8,%r15),psworg ; /* move user PSW to stack */ \ + xc 0(7,%r15),0(%r15) ; /* clear back chain & trap ind. */ \ + mvi SP_TRAP+3(%r15),psworg ; /* store trap indication in pt_regs */ \ + slr %r0,%r0 ; \ + sar %a2,%r0 ; /* set ac.reg. 2 to primary space */ \ + lhi %r0,1 ; \ + sar %a4,%r0 ; /* set access reg. 4 to home space */ + +#define RESTORE_ALL1 \ + mvc 0x50(8,0),SP_PSW(%r15) ; /* move user PSW to lowcore */ \ + lam %a0,%a15,SP_AREGS(%r15) ; /* load the access registers */ \ + lm %r0,%r15,SP_R0(%r15) ; /* load gprs 0-15 of user */ \ + ni 0x51(0),0xfd ; /* clear wait state bit */ \ + lpsw 0x50 /* back to caller */ + +#if CONFIG_REMOTE_DEBUG +#define SAVE_ALL(psworg) \ + SAVE_ALL1(psworg) \ + tm psworg+1,0x01 ; /* test problem state bit */ \ + jz 0f ; /* skip stack setup save */ \ + stctl %c0,%c15,SP_CRREGS(%r15) ; /* save control regs for remote debugging */ \ +0: + +#define RESTORE_ALL \ + tm SP_PSW+1(%r15),0x01 ; /* test problem state bit */ \ + jz 0f ; /* skip cr restore */ \ + lctl %c0,%c15,SP_CRREGS(%r15) ; /* store control regs for remote debugging */ \ +0: RESTORE_ALL1 +#else + +#define SAVE_ALL(psworg) \ + SAVE_ALL1(psworg) + +#define RESTORE_ALL \ + RESTORE_ALL1 +#endif + +#define GET_CURRENT /* load pointer to task_struct to R9 */ \ + lhi %r9,-8192 ; \ + nr %r9,15 + + +/* + * Scheduler resume function, called by switch_to + * grp2 = (thread_struct *) prev->tss + * grp3 = (thread_struct *) next->tss + * Returns: + * gpr2 = prev + */ + .globl resume +resume: + l %r4,_TSS_PTREGS(%r3) + tm SP_PSW-SP_PTREGS(%r4),0x40 # is the new process using per ? + jz RES_DN1 # if not we're fine + stctl %r9,%r11,24(%r15) # We are using per stuff + clc _TSS_PER(12,%r3),24(%r15) + je RES_DN1 # we got away without bashing TLB's + lctl %c9,%c11,_TSS_PER(%r3) # Nope we didn't +RES_DN1: + stm %r6,%r15,24(%r15) # store resume registers of prev task + iac %r1 # get address space control bits + sacf 0 + st %r1,4(%r15) + st %r15,_TSS_KSP(%r2) # store kernel stack ptr to prev->tss.ksp + lhi %r0,-8192 + nr %r0,%r15 + l %r15,_TSS_KSP(%r3) # load kernel stack ptr from next->tss.ksp + lhi %r1,8191 + or %r1,%r15 + ahi %r1,1 + st %r1,__LC_KERNEL_STACK # __LC_KERNEL_STACK = new kernel stack + stam %a2,%a2,_TSS_AR2(%r2) # store kernel access reg. 2 + stam %a4,%a4,_TSS_AR4(%r2) # store kernel access reg. 4 + lam %a2,%a2,_TSS_AR2(%r3) # load kernel access reg. 2 + lam %a4,%a4,_TSS_AR4(%r3) # load kernel access reg. 4 + lctl %c7,%c7,_TSS_USERSEG(%r3) # load secondary-space for new task + lctl %c13,%c13,_TSS_USERSEG(%r3) # load home-space for new task + lr %r2,%r0 # return task_struct of last task + l %r1,4(%r15) + sacf 0(%r1) # set address space control bits + lm %r6,%r15,24(%r15) # load resume registers of next task + br %r14 + +/* + * SVC interrupt handler routine. System calls are synchronous events and + * are executed with interrupts enabled. + */ + +sysc_lit: + sysc_bhmask: .long bh_mask + sysc_bhactive: .long bh_active + sysc_do_signal: .long do_signal + sysc_do_bottom_half:.long do_bottom_half + sysc_schedule: .long schedule + sysc_trace: .long syscall_trace +#ifdef __SMP__ + sysc_schedtail: .long schedule_tail +#endif + sysc_clone: .long sys_clone + sysc_fork: .long sys_fork + sysc_vfork: .long sys_vfork + sysc_sigreturn: .long sys_sigreturn + sysc_execve: .long sys_execve + sysc_sigsuspend: .long sys_sigsuspend + sysc_rt_sigsuspend: .long sys_rt_sigsuspend + + .globl system_call +system_call: + SAVE_ALL(0x20) + basr %r13,0 + ahi %r13,sysc_lit-. # setup base pointer R13 to sysc_lit + slr %r8,%r8 # gpr 8 is call save (-> tracesys) + ic %r8,0x8B # get svc number from lowcore + stosm 24(%r15),0x03 # reenable interrupts + GET_CURRENT # load pointer to task_struct to R9 + sll %r8,2 + l %r8,sys_call_table-sysc_lit(8,%r13) # get address of system call + tm flags+3(%r9),0x20 # PF_TRACESYS + jnz sysc_tracesys + basr %r14,%r8 # call sys_xxxx + st %r2,SP_R2(%r15) # store return value (change R2 on stack) + # ATTENTION: check sys_execve_glue before + # changing anything here !! + +sysc_return: + GET_CURRENT # load pointer to task_struct to R9 + tm SP_PSW+1(%r15),0x01 # returning to user ? +# tm SP_PSW+2(%r15),0xc0 # returning to user or kernel thread ? + jno sysc_leave # no-> skip bottom half, resched & signal +# +# check, if bottom-half has to be done +# + l %r1,sysc_bhmask-sysc_lit(%r13) + l %r0,0(%r1) + l %r1,sysc_bhactive-sysc_lit(%r13) + n %r0,0(%r1) + jnz sysc_handle_bottom_half +# +# check, if reschedule is needed +# +sysc_return_bh: + icm %r0,15,need_resched(%r9) # get need_resched from task_struct + jnz sysc_reschedule + icm %r0,15,sigpending(%r9) # get sigpending from task_struct + jnz sysc_signal_return +sysc_leave: + + stnsm 24(%r15),disable # disable I/O and ext. interrupts + RESTORE_ALL + +# +# call do_signal before return +# +sysc_signal_return: + la %r2,SP_PTREGS(%r15) # load pt_regs + sr %r3,%r3 # clear *oldset + l %r1,sysc_do_signal-sysc_lit(%r13) + la %r14,sysc_leave-sysc_lit(%r13) + br %r1 # return point is sysc_leave + +# +# call trace before and after sys_call +# +sysc_tracesys: + l %r1,sysc_trace-sysc_lit(%r13) + lhi %r2,-ENOSYS + st %r2,SP_R2(%r15) # give sysc_trace an -ENOSYS retval + basr %r14,%r1 + lm %r3,%r6,SP_R3(%r15) + l %r2,SP_ORIG_R2(%r15) + basr %r14,%r8 # call sys_xxx + st %r2,SP_R2(%r15) # store return value + l %r1,sysc_trace-sysc_lit(%r13) + la %r14,sysc_return-sysc_lit(%r13) + br %r1 # return point is sysc_return + + +# +# call do_bottom_half and return from syscall, if interrupt-level +# is zero +# +sysc_handle_bottom_half: + l %r1,sysc_do_bottom_half-sysc_lit(%r13) + la %r14,sysc_return_bh-sysc_lit(%r13) + br %r1 # call do_bottom_half + +# +# call schedule with sysc_return as return-address +# +sysc_reschedule: + l %r1,sysc_schedule-sysc_lit(%r13) + la %r14,sysc_return-sysc_lit(%r13) + br %r1 # call scheduler, return to sysc_return + +# +# a new process exits the kernel with ret_from_fork +# + .globl ret_from_fork +ret_from_fork: + basr %r13,0 + ahi %r13,sysc_lit-. # setup base pointer R13 to $SYSCDAT + GET_CURRENT # load pointer to task_struct to R9 + stosm 24(%r15),0x03 # reenable interrupts + sr %r0,%r0 # child returns 0 + st %r0,SP_R2(%r15) # store return value (change R2 on stack) +#ifdef __SMP__ + l %r1,sysc_schedtail-sysc_lit(%r13) + la %r14,sysc_return-sysc_lit(%r13) + br %r1 # call schedule_tail, return to sysc_return +#else + j sysc_return +#endif + +# +# clone, fork, vfork, exec and sigreturn need glue, +# because they all expect pt_regs as parameter, +# but are called with different parameter. +# return-address is set up above +# +sys_clone_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs + l %r1,sysc_clone-sysc_lit(%r13) + br %r1 # branch to sys_clone + +sys_fork_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs + l %r1,sysc_fork-sysc_lit(%r13) + br %r1 # branch to sys_fork + +sys_vfork_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs + l %r1,sysc_vfork-sysc_lit(%r13) + br %r1 # branch to sys_vfork + +sys_execve_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs + l %r1,sysc_execve-sysc_lit(%r13) + basr %r14,%r1 # call sys_execve + ltr %r2,%r2 # check if execve failed + jnz sysc_return-4 # it did fail -> store result in gpr2 + j sysc_return # SKIP ST 2,SP_R2(15) after BASR 14,8 + # in system_call + +sys_sigreturn_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs as parameter + l %r1,sysc_sigreturn-sysc_lit(%r13) + br %r1 # branch to sys_sigreturn + +# +# sigsuspend and rt_sigsuspend need pt_regs as an additional +# parameter and they have to skip the store of %r2 into the +# user register %r2 because the return value was set in +# sigsuspend and rt_sigsuspend already and must not be overwritten! +# + +sys_sigsuspend_glue: + lr %r5,%r4 # move mask back + lr %r4,%r3 # move history1 parameter + lr %r3,%r2 # move history0 parameter + la %r2,SP_PTREGS(%r15) # load pt_regs as first parameter + l %r1,sysc_sigsuspend-sysc_lit(%r13) + la %r14,sysc_return-sysc_lit(%r13) # skip store of return value + br %r1 # branch to sys_sigsuspend + +sys_rt_sigsuspend_glue: + lr %r4,%r3 # move sigsetsize parameter + lr %r3,%r2 # move unewset parameter + la %r2,SP_PTREGS(%r15) # load pt_regs as first parameter + l %r1,sysc_rt_sigsuspend-sysc_lit(%r13) + la %r14,sysc_return-sysc_lit(%r13) # skip store of return value + br %r1 # branch to sys_rt_sigsuspend + + .globl sys_call_table +sys_call_table: + .long sys_ni_syscall /* 0 */ + .long sys_exit + .long sys_fork_glue + .long sys_read + .long sys_write + .long sys_open /* 5 */ + .long sys_close + .long sys_waitpid + .long sys_creat + .long sys_link + .long sys_unlink /* 10 */ + .long sys_execve_glue + .long sys_chdir + .long sys_time + .long sys_mknod + .long sys_chmod /* 15 */ + .long sys_lchown + .long sys_ni_syscall /* old break syscall holder */ + .long sys_stat + .long sys_lseek + .long sys_getpid /* 20 */ + .long sys_mount + .long sys_umount + .long sys_setuid + .long sys_getuid + .long sys_stime /* 25 */ + .long sys_ptrace + .long sys_alarm + .long sys_fstat + .long sys_pause + .long sys_utime /* 30 */ + .long sys_ni_syscall /* old stty syscall holder */ + .long sys_ni_syscall /* old gtty syscall holder */ + .long sys_access + .long sys_nice + .long sys_ni_syscall /* 35 */ /* old ftime syscall holder */ + .long sys_sync + .long sys_kill + .long sys_rename + .long sys_mkdir + .long sys_rmdir /* 40 */ + .long sys_dup + .long sys_pipe + .long sys_times + .long sys_ni_syscall /* old prof syscall holder */ + .long sys_brk /* 45 */ + .long sys_setgid + .long sys_getgid + .long sys_signal + .long sys_geteuid + .long sys_getegid /* 50 */ + .long sys_acct + .long sys_ni_syscall /* old phys syscall holder */ + .long sys_ni_syscall /* old lock syscall holder */ + .long sys_ioctl + .long sys_fcntl /* 55 */ + .long sys_ni_syscall /* old mpx syscall holder */ + .long sys_setpgid + .long sys_ni_syscall /* old ulimit syscall holder */ + .long sys_olduname + .long sys_umask /* 60 */ + .long sys_chroot + .long sys_ustat + .long sys_dup2 + .long sys_getppid + .long sys_getpgrp /* 65 */ + .long sys_setsid + .long sys_sigaction + .long sys_sgetmask + .long sys_ssetmask + .long sys_setreuid /* 70 */ + .long sys_setregid + .long sys_sigsuspend_glue + .long sys_sigpending + .long sys_sethostname + .long sys_setrlimit /* 75 */ + .long sys_getrlimit + .long sys_getrusage + .long sys_gettimeofday + .long sys_settimeofday + .long sys_getgroups /* 80 */ + .long sys_setgroups + .long old_select + .long sys_symlink + .long sys_lstat + .long sys_readlink /* 85 */ + .long sys_uselib + .long sys_swapon + .long sys_reboot + .long old_readdir + .long old_mmap /* 90 */ + .long sys_munmap + .long sys_truncate + .long sys_ftruncate + .long sys_fchmod + .long sys_fchown /* 95 */ + .long sys_getpriority + .long sys_setpriority + .long sys_ni_syscall /* old profil syscall holder */ + .long sys_statfs + .long sys_fstatfs /* 100 */ + .long sys_ioperm + .long sys_socketcall + .long sys_syslog + .long sys_setitimer + .long sys_getitimer /* 105 */ + .long sys_newstat + .long sys_newlstat + .long sys_newfstat + .long sys_uname + .long sys_ni_syscall /* 110 */ /* iopl for i386 */ + .long sys_vhangup + .long sys_idle + .long sys_ni_syscall /* vm86old for i386 */ + .long sys_wait4 + .long sys_swapoff /* 115 */ + .long sys_sysinfo + .long sys_ipc + .long sys_fsync + .long sys_sigreturn_glue + .long sys_clone_glue /* 120 */ + .long sys_setdomainname + .long sys_newuname + .long sys_ni_syscall /* modify_ldt for i386 */ + .long sys_adjtimex + .long sys_mprotect /* 125 */ + .long sys_sigprocmask + .long sys_create_module + .long sys_init_module + .long sys_delete_module + .long sys_get_kernel_syms /* 130 */ + .long sys_quotactl + .long sys_getpgid + .long sys_fchdir + .long sys_bdflush + .long sys_sysfs /* 135 */ + .long sys_personality + .long sys_ni_syscall /* for afs_syscall */ + .long sys_setfsuid + .long sys_setfsgid + .long sys_llseek /* 140 */ + .long sys_getdents + .long sys_select + .long sys_flock + .long sys_msync + .long sys_readv /* 145 */ + .long sys_writev + .long sys_getsid + .long sys_fdatasync + .long sys_sysctl + .long sys_mlock /* 150 */ + .long sys_munlock + .long sys_mlockall + .long sys_munlockall + .long sys_sched_setparam + .long sys_sched_getparam /* 155 */ + .long sys_sched_setscheduler + .long sys_sched_getscheduler + .long sys_sched_yield + .long sys_sched_get_priority_max + .long sys_sched_get_priority_min /* 160 */ + .long sys_sched_rr_get_interval + .long sys_nanosleep + .long sys_mremap + .long sys_setresuid + .long sys_getresuid /* 165 */ + .long sys_ni_syscall /* for vm86 */ + .long sys_query_module + .long sys_poll + .long sys_nfsservctl + .long sys_setresgid /* 170 */ + .long sys_getresgid + .long sys_prctl + .long sys_rt_sigreturn + .long sys_rt_sigaction + .long sys_rt_sigprocmask /* 175 */ + .long sys_rt_sigpending + .long sys_rt_sigtimedwait + .long sys_rt_sigqueueinfo + .long sys_rt_sigsuspend_glue + .long sys_pread /* 180 */ + .long sys_pwrite + .long sys_chown + .long sys_getcwd + .long sys_capget + .long sys_capset /* 185 */ + .long sys_sigaltstack + .long sys_sendfile + .long sys_ni_syscall /* streams1 */ + .long sys_ni_syscall /* streams2 */ + .long sys_vfork_glue /* 190 */ + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall /* 195 */ + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall /* 205 */ + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall /* 215 */ + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall /* 225 */ + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall /* 235 */ + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall /* 245 */ + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_msgcp /* 255 */ + +/* + * Program check handler routine + */ + +pgm_lit: + pgm_handle_per: .long handle_per_exception + pgm_jump_table: .long pgm_check_table + pgm_sysc_ret: .long sysc_return + pgm_sysc_lit: .long sysc_lit + pgm_do_signal: .long do_signal + + .globl pgm_check_handler +pgm_check_handler: +/* + * First we need to check for a special case: + * Single stepping an instruction that disables the PER event mask will + * cause a PER event AFTER the mask has been set. Example: SVC or LPSW. + * For a single stepped SVC the program check handler gets control after + * the SVC new PSW has been loaded. But we want to execute the SVC first and + * then handle the PER event. Therefore we update the SVC old PSW to point + * to the pgm_check_handler and branch to the SVC handler after we checked + * if we have to load the kernel stack register. + * For every other possible cause for PER event without the PER mask set + * we just ignore the PER event (FIXME: is there anything we have to do + * for LPSW?). + */ + tm __LC_PGM_INT_CODE+1,0x80 # check whether we got a per exception + jz pgm_sv # skip if not + tm __LC_PGM_OLD_PSW,0x40 # test if per event recording is on + jnz pgm_sv # skip if it is +# ok its one of the special cases, now we need to find out which one + clc __LC_PGM_OLD_PSW(8),__LC_SVC_NEW_PSW + je pgm_svcper +# no interesting special case, ignore PER event + lpsw 0x28 +# it was a single stepped SVC that is causing all the trouble +pgm_svcper: + st %r15,__LC_SAVE_AREA + l %r15,__LC_PGM_NEW_PSW+4 # prepare return psw + ahi %r15,pgm_svcret-pgm_check_handler + st %r15,__LC_PGM_OLD_PSW+4 # use pgm old psw as temp storage + tm __LC_SVC_OLD_PSW,0x01 # test problem state of svc old psw + jz .+8 + l %r15,__LC_KERNEL_STACK # load ksp + ahi %r15,-16 + srl %r15,3 # align stack pointer to 8 + sll %r15,3 + mvc 0(4,%r15),__LC_SAVE_AREA # save user r15 + mvc 4(4,%r15),__LC_PGM_ILC # save program check information + mvc 8(8,%r15),__LC_SVC_OLD_PSW # save return point + mvc __LC_SVC_OLD_PSW(8),__LC_PGM_OLD_PSW # move prepared return pt. + j system_call # now do the svc +pgm_svcret: + mvc __LC_PGM_OLD_PSW(8),8(%r15) # return point is svc return point + mvc __LC_PGM_ILC(4),4(%r15) # restore program check info + l %r15,0(%r15) # restore user r15 +pgm_sv: + SAVE_ALL(0x28) + basr %r13,0 + ahi %r13,pgm_lit-. # setup base pointer R13 to $PGMDAT + lh %r7,__LC_PGM_ILC # load instruction length + lh %r8,__LC_PGM_INT_CODE # N.B. saved int code used later KEEP it + stosm 24(%r15),0x03 # reenable interrupts + lr %r3,%r8 + lhi %r0,0x7f + nr %r3,%r0 # clear per-event-bit + je pgm_dn # none of Martins exceptions occured bypass + l %r9,pgm_jump_table-pgm_lit(%r13) + sll %r3,2 + l %r9,0(%r3,%r9) # load address of handler routine + la %r2,SP_PTREGS(%r15) # address of register-save area + srl %r3,2 + chi %r3,0x4 # protection-exception ? + jne pgm_go # if not, + l %r5,SP_PSW+4(15) # load psw addr + sr %r5,%r7 # substract ilc from psw + st %r5,SP_PSW+4(15) # store corrected psw addr +pgm_go: basr %r14,%r9 # branch to interrupt-handler +pgm_dn: lhi %r0,0x80 + nr %r8,%r0 # check for per exception + je pgm_return + la %r2,SP_PTREGS(15) # address of register-save area + l %r9,pgm_handle_per-pgm_lit(%r13) # load adr. of per handler + l %r14,pgm_sysc_ret-pgm_lit(%r13) # load adr. of system return + l %r13,pgm_sysc_lit-pgm_lit(%r13) + br %r9 # branch to handle_per_exception +# +# the backend code is the same as for sys-call +# +pgm_return: + l %r14,pgm_sysc_ret-pgm_lit(%r13) + l %r13,pgm_sysc_lit-pgm_lit(%r13) + br %r14 + +default_trap_handler: + .globl default_trap_handler + lpsw 112 + +/* + * IO interrupt handler routine + */ + +io_lit: + io_do_IRQ: .long do_IRQ + io_schedule: .long schedule + io_do_signal: .long do_signal + io_bhmask: .long bh_mask + io_bhactive: .long bh_active + io_do_bottom_half:.long do_bottom_half + + .globl io_int_handler +io_int_handler: + SAVE_ALL(0x38) + basr %r13,0 + ahi %r13,io_lit-. # setup base pointer R13 to $IODAT + la %r2,SP_PTREGS(%r15) # address of register-save area + sr %r3,%r3 + icm %r3,%r3,__LC_SUBCHANNEL_NR # load subchannel nr & extend to int + l %r4,__LC_IO_INT_PARM # load interuption parm + l %r9,io_do_IRQ-io_lit(%r13) # load address of do_IRQ + basr %r14,%r9 # branch to standard irq handler + +io_return: + GET_CURRENT # load pointer to task_struct to R9 + tm SP_PSW+1(%r15),0x01 # returning to user ? +# tm SP_PSW+2(%r15),0x80 # returning to user or kernel thread ? + jz io_leave # no-> skip resched & signal + stosm 24(%r15),0x03 # reenable interrupts +# +# check, if bottom-half has to be done +# + l %r1,io_bhmask-io_lit(%r13) + l %r0,0(%r1) + l %r1,io_bhactive-io_lit(%r13) + n %r0,0(%r1) + jnz io_handle_bottom_half +io_return_bh: +# +# check, if reschedule is needed +# + icm %r0,15,need_resched(%r9) # get need_resched from task_struct + jnz io_reschedule + icm %r0,15,sigpending(%r9) # get sigpending from task_struct + jnz io_signal_return +io_leave: + stnsm 24(%r15),disable # disable I/O and ext. interrupts + RESTORE_ALL + +# +# call do_bottom_half and return from syscall, if interrupt-level +# is zero +# +io_handle_bottom_half: + l %r1,io_do_bottom_half-io_lit(%r13) + la %r14,io_return_bh-io_lit(%r13) + br %r1 # call do_bottom_half + +# +# call schedule with io_return as return-address +# +io_reschedule: + l %r1,io_schedule-io_lit(%r13) + la %r14,io_return-io_lit(%r13) + br %r1 # call scheduler, return to io_return + +# +# call do_signal before return +# +io_signal_return: + la %r2,SP_PTREGS(%r15) # load pt_regs + sr %r3,%r3 # clear *oldset + l %r1,io_do_signal-io_lit(%r13) + la %r14,io_leave-io_lit(%r13) + br %r1 # return point is io_leave + +/* + * External interrupt handler routine + */ + +ext_lit: + ext_timer_int: .long do_timer_interrupt +#ifdef __SMP__ + ext_call_int: .long do_ext_call_interrupt +#endif +#ifdef CONFIG_HWC + ext_hwc_int: .long do_hwc_interrupt +#endif +#ifdef CONFIG_MDISK + ext_mdisk_int: .long do_mdisk_interrupt +#endif +#ifdef CONFIG_IUCV + ext_iucv_int: .long do_iucv_interrupt +#endif + ext_io_lit: .long io_lit + ext_io_return: .long io_return + + .globl ext_int_handler +ext_int_handler: + SAVE_ALL(0x18) + basr %r13,0 + ahi %r13,ext_lit-. # setup base pointer R13 to $EXTDAT + la %r2,SP_PTREGS(%r15) # address of register-save area + lh %r3,__LC_EXT_INT_CODE # error code +#ifdef __SMP__ + chi %r3,0x1202 # EXTERNAL_CALL + jne ext_no_extcall + l %r9,ext_call_int-ext_lit(%r13) # load ext_call_interrupt + l %r14,ext_io_return-ext_lit(%r13) + l %r13,ext_io_lit-ext_lit(%r13) + br %r9 # branch to ext call handler +ext_no_extcall: +#endif + chi %r3,0x1004 # CPU_TIMER + jne ext_no_timer + l %r9,ext_timer_int-ext_lit(%r13) # load timer_interrupt + l %r14,ext_io_return-ext_lit(%r13) + l %r13,ext_io_lit-ext_lit(%r13) + br %r9 # branch to ext call handler +ext_no_timer: +#ifdef CONFIG_HWC + chi %r3,0x2401 # HWC interrupt + jne ext_no_hwc + l %r9,ext_hwc_int-ext_lit(%r13) # load addr. of hwc routine + l %r14,ext_io_return-ext_lit(%r13) + l %r13,ext_io_lit-ext_lit(%r13) + br %r9 # branch to ext call handler +ext_no_hwc: +#endif +#ifdef CONFIG_MDISK + chi %r3,0x2603 # diag 250 (VM) interrupt + jne ext_no_mdisk + l %r9,ext_mdisk_int-ext_lit(%r13) + l %r14,ext_io_return-ext_lit(%r13) + l %r13,ext_io_lit-ext_lit(%r13) + br %r9 # branch to ext call handler +ext_no_mdisk: +#endif +#ifdef CONFIG_IUCV + chi %r3,0x4000 # diag 250 (VM) interrupt + jne ext_no_iucv + l %r9,ext_iucv_int-ext_lit(%r13) + l %r14,ext_io_return-ext_lit(%r13) + l %r13,ext_io_lit-ext_lit(%r13) + br %r9 # branch to ext call handler +ext_no_iucv: +#endif + + l %r14,ext_io_return-ext_lit(%r13) + l %r13,ext_io_lit-ext_lit(%r13) + br %r14 # use backend code of io_int_handler + +/* + * Machine check handler routines + */ +mcck_lit: + mcck_crw_pending: .long do_crw_pending + + + .globl mcck_int_handler +mcck_int_handler: + SAVE_ALL(0x30) + basr %r13,0 + ahi %r13,mcck_lit-. # setup base pointer R13 to $MCCKDAT + tm __LC_MCCK_CODE+1,0x40 + jno mcck_no_crw + l %r1,mcck_crw_pending-mcck_lit(%r13) + basr %r14,%r1 # call do_crw_pending +mcck_no_crw: +mcck_return: + RESTORE_ALL + +#ifdef __SMP__ +/* + * Restart interruption handler, kick starter for additional CPUs + */ + .globl restart_int_handler +restart_int_handler: + l %r15,__LC_KERNEL_STACK # load ksp + lctl %c0,%c15,__LC_CREGS_SAVE_AREA # get new ctl regs + lam %a0,%a15,__LC_AREGS_SAVE_AREA + stosm 0(%r15),daton # now we can turn dat on + lm %r6,%r15,24(%r15) # load registers from clone + bras %r14,restart_go + .long start_secondary +restart_go: + l %r14,0(%r14) + br %r14 # branch to start_secondary +#else +/* + * If we do not run with SMP enabled, let the new CPU crash ... + */ + .globl restart_int_handler +restart_int_handler: + basr %r1,0 +restart_base: + lpsw restart_crash-restart_base(%r1) + .align 8 +restart_crash: + .long 0x000a0000,0x00000000 +restart_go: +#endif + diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/floatlib.c linux/arch/s390/kernel/floatlib.c --- v2.2.13/linux/arch/s390/kernel/floatlib.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/floatlib.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,1021 @@ +/* +** libgcc support for software floating point. +** Copyright (C) 1991 by Pipeline Associates, Inc. All rights reserved. +** Permission is granted to do *anything* you want with this file, +** commercial or otherwise, provided this message remains intact. So there! +** I would appreciate receiving any updates/patches/changes that anyone +** makes, and am willing to be the repository for said changes (am I +** making a big mistake?). + +Warning! Only single-precision is actually implemented. This file +won't really be much use until double-precision is supported. + +However, once that is done, this file might eventually become a +replacement for libgcc1.c. It might also make possible +cross-compilation for an IEEE target machine from a non-IEEE +host such as a VAX. + +If you'd like to work on completing this, please talk to rms@gnu.ai.mit.edu. + +--> Double precision floating support added by James Carlson on 20 April 1998. + +** +** Pat Wood +** Pipeline Associates, Inc. +** pipeline!phw@motown.com or +** sun!pipeline!phw or +** uunet!motown!pipeline!phw +** +** 05/01/91 -- V1.0 -- first release to gcc mailing lists +** 05/04/91 -- V1.1 -- added float and double prototypes and return values +** -- fixed problems with adding and subtracting zero +** -- fixed rounding in truncdfsf2 +** -- fixed SWAP define and tested on 386 +*/ + +/* +** The following are routines that replace the libgcc soft floating point +** routines that are called automatically when -msoft-float is selected. +** The support single and double precision IEEE format, with provisions +** for byte-swapped machines (tested on 386). Some of the double-precision +** routines work at full precision, but most of the hard ones simply punt +** and call the single precision routines, producing a loss of accuracy. +** long long support is not assumed or included. +** Overall accuracy is close to IEEE (actually 68882) for single-precision +** arithmetic. I think there may still be a 1 in 1000 chance of a bit +** being rounded the wrong way during a multiply. I'm not fussy enough to +** bother with it, but if anyone is, knock yourself out. +** +** Efficiency has only been addressed where it was obvious that something +** would make a big difference. Anyone who wants to do this right for +** best speed should go in and rewrite in assembler. +** +** I have tested this only on a 68030 workstation and 386/ix integrated +** in with -msoft-float. +*/ + +#define float long +#define double long long + +/* the following deal with IEEE single-precision numbers */ +#define EXCESS 126 +#define SIGNBIT 0x80000000 +#define HIDDEN (1 << 23) +#define SIGN(fp) ((fp) & SIGNBIT) +#define EXP(fp) (((fp) >> 23) & 0xFF) +#define MANT(fp) (((fp) & 0x7FFFFF) | HIDDEN) +#define PACK(s,e,m) ((s) | ((e) << 23) | (m)) + +/* the following deal with IEEE double-precision numbers */ +#define EXCESSD 1022 +#define HIDDEND (1 << 20) +#define EXPD(fp) (((fp.l.upper) >> 20) & 0x7FF) +#define SIGND(fp) ((fp.l.upper) & SIGNBIT) +#define MANTD(fp) (((((fp.l.upper) & 0xFFFFF) | HIDDEND) << 10) |(fp.l.lower >> 22)) +#define HIDDEND_LL ((long long)1 << 52) +#define MANTD_LL(fp) ((fp.ll & (HIDDEND_LL-1)) | HIDDEND_LL) +#define PACKD_LL(s,e,m) (((long long)((s)+((e)<<20))<<32)|(m)) + +/* define SWAP for 386/960 reverse-byte-order brain-damaged CPUs */ +union double_long { + double d; +#ifdef SWAP + struct { + unsigned long lower; + long upper; + } l; +#else + struct { + long upper; + unsigned long lower; + } l; +#endif + long long ll; +}; + +union float_long + { + float f; + long l; + }; + +long long +__negdi2 (long long u) +{ + + union lll { + long long ll; + long s[2]; + }; + + union lll w,uu; + + uu.ll = u; + + w.s[1] = -uu.s[1]; + w.s[0] = -uu.s[0] - ((int) w.s[1] != 0); + + return w.ll; +} + + + +/* add two floats */ +float +__addsf3 (float a1, float a2) +{ + register long mant1, mant2; + register union float_long fl1, fl2; + register int exp1, exp2; + int sign = 0; + + fl1.f = a1; + fl2.f = a2; + + /* check for zero args */ + if (!fl1.l) { + fl1.f = fl2.f; + goto test_done; + } + if (!fl2.l) + goto test_done; + + exp1 = EXP (fl1.l); + exp2 = EXP (fl2.l); + + if (exp1 > exp2 + 25) + goto test_done; + if (exp2 > exp1 + 25) { + fl1.f = fl2.f; + goto test_done; + } + + /* do everything in excess precision so's we can round later */ + mant1 = MANT (fl1.l) << 6; + mant2 = MANT (fl2.l) << 6; + + if (SIGN (fl1.l)) + mant1 = -mant1; + if (SIGN (fl2.l)) + mant2 = -mant2; + + if (exp1 > exp2) + { + mant2 >>= exp1 - exp2; + } + else + { + mant1 >>= exp2 - exp1; + exp1 = exp2; + } + mant1 += mant2; + + if (mant1 < 0) + { + mant1 = -mant1; + sign = SIGNBIT; + } + else if (!mant1) { + fl1.f = 0; + goto test_done; + } + + /* normalize up */ + while (!(mant1 & 0xE0000000)) + { + mant1 <<= 1; + exp1--; + } + + /* normalize down? */ + if (mant1 & (1 << 30)) + { + mant1 >>= 1; + exp1++; + } + + /* round to even */ + mant1 += (mant1 & 0x40) ? 0x20 : 0x1F; + + /* normalize down? */ + if (mant1 & (1 << 30)) + { + mant1 >>= 1; + exp1++; + } + + /* lose extra precision */ + mant1 >>= 6; + + /* turn off hidden bit */ + mant1 &= ~HIDDEN; + + /* pack up and go home */ + fl1.l = PACK (sign, exp1, mant1); +test_done: + return (fl1.f); +} + +/* subtract two floats */ +float +__subsf3 (float a1, float a2) +{ + register union float_long fl1, fl2; + + fl1.f = a1; + fl2.f = a2; + + /* check for second arg zero */ + if (!fl2.l) + return (fl1.f); + /* twiddle sign bit */ + fl2.l ^= SIGNBIT; + /* check for first arg zero */ + if (!fl1.l) + return (fl2.f); + /* add values */ + return __addsf3 (a1, fl2.f); +} + +/* compare two floats */ +long +__cmpsf2 (float a1, float a2) +{ + register union float_long fl1, fl2; + + fl1.f = a1; + fl2.f = a2; + + if (SIGN (fl1.l) && SIGN (fl2.l)) + { + fl1.l ^= SIGNBIT; + fl2.l ^= SIGNBIT; + if (fl1.l < fl2.l) + return (-1); + if (fl1.l > fl2.l) + return (1); + return 0; + } else { + if (fl1.l < fl2.l) + return (-1); + if (fl1.l > fl2.l) + return (1); + return (0); + } +} + +/* multiply two floats */ +float +__mulsf3 (float a1, float a2) +{ + register union float_long fl1, fl2; + register unsigned long result; + register int exp; + int sign; + + fl1.f = a1; + fl2.f = a2; + + if (!fl1.l || !fl2.l) { + fl1.f = 0; + goto test_done; + } + + /* compute sign and exponent */ + sign = SIGN (fl1.l) ^ SIGN (fl2.l); + exp = EXP (fl1.l) - EXCESS; + exp += EXP (fl2.l); + + fl1.l = MANT (fl1.l); + fl2.l = MANT (fl2.l); + + /* the multiply is done as one 16x16 multiply and two 16x8 multiples */ + result = (fl1.l >> 8) * (fl2.l >> 8); + result += ((fl1.l & 0xFF) * (fl2.l >> 8)) >> 8; + result += ((fl2.l & 0xFF) * (fl1.l >> 8)) >> 8; + + result >>= 2; + if (result & 0x20000000) + { + /* round */ + result += 0x20; + result >>= 6; + } + else + { + /* round */ + result += 0x10; + result >>= 5; + exp--; + } + if (result & (HIDDEN<<1)) { + result >>= 1; + exp++; + } + + result &= ~HIDDEN; + + /* pack up and go home */ + fl1.l = PACK (sign, exp, result); +test_done: + return (fl1.f); +} + +/* divide two floats */ +float +__divsf3 (float a1, float a2) +{ + register union float_long fl1, fl2; + register int result; + register int mask; + register int exp, sign; + + fl1.f = a1; + fl2.f = a2; + + /* subtract exponents */ + exp = EXP (fl1.l) - EXP (fl2.l) + EXCESS; + + /* compute sign */ + sign = SIGN (fl1.l) ^ SIGN (fl2.l); + + /* divide by zero??? */ + if (!fl2.l) + /* return NaN or -NaN */ + return (sign ? 0xFFFFFFFF : 0x7FFFFFFF); + + /* numerator zero??? */ + if (!fl1.l) + return (0); + + /* now get mantissas */ + fl1.l = MANT (fl1.l); + fl2.l = MANT (fl2.l); + + /* this assures we have 25 bits of precision in the end */ + if (fl1.l < fl2.l) + { + fl1.l <<= 1; + exp--; + } + + /* now we perform repeated subtraction of fl2.l from fl1.l */ + mask = 0x1000000; + result = 0; + while (mask) + { + if (fl1.l >= fl2.l) + { + result |= mask; + fl1.l -= fl2.l; + } + fl1.l <<= 1; + mask >>= 1; + } + + /* round */ + result += 1; + + /* normalize down */ + exp++; + result >>= 1; + + result &= ~HIDDEN; + + /* pack up and go home */ + fl1.l = PACK (sign, exp, result); + return (fl1.f); +} + +/* convert double to float */ +float +__truncdfsf2 (double a1) +{ + register int exp; + register long mant; + register union float_long fl; + register union double_long dl1; + + dl1.d = a1; + + if (!dl1.l.upper && !dl1.l.lower) + return (float)(0); + + exp = EXPD (dl1) - EXCESSD + EXCESS; + + /* shift double mantissa 6 bits so we can round */ + mant = MANTD (dl1) >> 6; + + /* now round and shift down */ + mant += 1; + mant >>= 1; + + /* did the round overflow? */ + if (mant & 0xFF000000) + { + mant >>= 1; + exp++; + } + + mant &= ~HIDDEN; + + /* pack up and go home */ + fl.l = PACK (SIGND (dl1), exp, mant); + return (fl.f); +} + +/* convert int to double */ +double +__floatsidf (register long a1) +{ + register int sign = 0, exp = 31 + EXCESSD; + union double_long dl; + + if (a1 == 0x80000000) + { + /* + * -a1 would be 0 ! + */ + dl.l.upper = 0xc1e00000; + dl.l.lower = 0x0; + return (dl.d); + } + + if (!a1) + { + dl.l.upper = dl.l.lower = 0; + return (dl.d); + } + + if (a1 < 0) + { + sign = SIGNBIT; + a1 = -a1; + } + + while (a1 < 0x1000000) + { + a1 <<= 4; + exp -= 4; + } + + while (a1 < 0x40000000) + { + a1 <<= 1; + exp--; + } + + /* pack up and go home */ + dl.l.upper = sign; + dl.l.upper |= exp << 20; + dl.l.upper |= (a1 >> 10) & ~HIDDEND; + dl.l.lower = a1 << 22; + + return (dl.d); +} + +double +__floatdidf (register long long a1) +{ + register int exp = 63 + EXCESSD; + union double_long dl; + + dl.l.upper = dl.l.lower = 0; + if (a1 == 0) + return (dl.d); + + if (a1 < 0) { + dl.l.upper = SIGNBIT; + a1 = -a1; + } + + while (a1 < (long long)1<<54) { + a1 <<= 8; + exp -= 8; + } + while (a1 < (long long)1<<62) { + a1 <<= 1; + exp -= 1; + } + /* pack up and go home */ + dl.ll |= (a1 >> 10) & ~HIDDEND_LL; + dl.l.upper |= exp << 20; + + return (dl.d); +} + +float +__floatsisf (register long a1) +{ + return __truncdfsf2(__floatsidf(a1)); +} + +float +__floatdisf (register long long a1) +{ + return (float)__floatdidf(a1); +} +/* negate a float */ +float +__negsf2 (float a1) +{ + register union float_long fl1; + + fl1.f = a1; + if (!fl1.l) + return (0); + + fl1.l ^= SIGNBIT; + return (fl1.f); +} + +/* negate a double */ +double +__negdf2 (double a1) +{ + register union double_long dl1; + + dl1.d = a1; + + if (!dl1.l.upper && !dl1.l.lower) + return (dl1.d); + + dl1.l.upper ^= SIGNBIT; + return (dl1.d); +} + +/* convert float to double */ +double +__extendsfdf2 (float a1) +{ + register union float_long fl1; + register union double_long dl; + register int exp; + + fl1.f = a1; + + if (!fl1.l) + { + dl.l.upper = dl.l.lower = 0; + return (dl.d); + } + + dl.l.upper = SIGN (fl1.l); + exp = EXP (fl1.l) - EXCESS + EXCESSD; + dl.l.upper |= exp << 20; + dl.l.upper |= (MANT (fl1.l) & ~HIDDEN) >> 3; + dl.l.lower = MANT (fl1.l) << 29; + + return (dl.d); +} + + +/* compare two doubles */ +long +__cmpdf2 (double a1, double a2) +{ + register union double_long dl1, dl2; + + dl1.d = a1; + dl2.d = a2; + + if (SIGND (dl1) && SIGND (dl2)) + { + dl1.l.upper ^= SIGNBIT; + dl2.l.upper ^= SIGNBIT; + if (dl1.l.upper < dl2.l.upper) + return (1); + if (dl1.l.upper > dl2.l.upper) + return (-1); + if (dl1.l.lower < dl2.l.lower) + return (1); + if (dl1.l.lower > dl2.l.lower) + return (-1); + return (0); + } else { + if (dl1.l.upper < dl2.l.upper) + return (-1); + if (dl1.l.upper > dl2.l.upper) + return (1); + if (dl1.l.lower < dl2.l.lower) + return (-1); + if (dl1.l.lower > dl2.l.lower) + return (1); + return (0); + } +} + +/* convert double to int */ +long +__fixdfsi (double a1) +{ + register union double_long dl1; + register int exp; + register long l; + + dl1.d = a1; + + if (!dl1.l.upper && !dl1.l.lower) + return (0); + + exp = EXPD (dl1) - EXCESSD - 31; + l = MANTD (dl1); + + if (exp > 0) + return SIGND(dl1) ? (1<<31) : ((1ul<<31)-1); + + /* shift down until exp = 0 or l = 0 */ + if (exp <= 0 && exp > -32 && l) + l >>= -exp; + else + return (0); + + return (SIGND (dl1) ? -l : l); +} + +/* convert float to int */ +long +__fixsfsi (float a1) +{ + return __fixdfsi(__extendsfdf2(a1)); +} + +/* convert double to int */ +long long +__fixdfdi (double a1) +{ + register union double_long dl1; + register int exp; + register long long l; + + dl1.d = a1; + + if (!dl1.l.upper && !dl1.l.lower) + return (0); + + exp = EXPD (dl1) - EXCESSD - 64; + l = MANTD_LL(dl1); + + if (exp > 0) { + l = (long long)1<<63; + if (!SIGND(dl1)) + l--; + return l; + } + + /* shift down until exp = 0 or l = 0 */ + if (exp <= 0 && exp > -64 && l) + l >>= -exp; + else + return (0); + + return (SIGND (dl1) ? -l : l); +} + +/* convert double to unsigned int */ +unsigned long +__fixunsdfsi (double a1) +{ + register union double_long dl1; + register int exp; + register unsigned long l; + + dl1.d = a1; + + if (!dl1.l.upper && !dl1.l.lower) + return (0); + + exp = EXPD (dl1) - EXCESSD - 32; + l = (((((dl1.l.upper) & 0xFFFFF) | HIDDEND) << 11) | (dl1.l.lower >> 21)); + + if (exp > 0) + return (0xFFFFFFFFul); /* largest integer */ + + /* shift down until exp = 0 or l = 0 */ + if (exp < 0 && exp > -32 && l) + l >>= -exp; + else + return (0); + + return (l); +} + +/* convert double to unsigned int */ +unsigned long long +__fixunsdfdi (double a1) +{ + register union double_long dl1; + register int exp; + register unsigned long long l; + + dl1.d = a1; + + if (dl1.ll == 0) + return (0); + + exp = EXPD (dl1) - EXCESSD - 64; + + l = dl1.ll; + + if (exp > 0) + return (unsigned long long)-1; + + /* shift down until exp = 0 or l = 0 */ + if (exp < 0 && exp > -64 && l) + l >>= -exp; + else + return (0); + + return (l); +} + +/* addtwo doubles */ +double +__adddf3 (double a1, double a2) +{ + register long long mant1, mant2; + register union double_long fl1, fl2; + register int exp1, exp2; + int sign = 0; + + fl1.d = a1; + fl2.d = a2; + + /* check for zero args */ + if (!fl2.ll) + goto test_done; + if (!fl1.ll) { + fl1.d = fl2.d; + goto test_done; + } + + exp1 = EXPD(fl1); + exp2 = EXPD(fl2); + + if (exp1 > exp2 + 54) + goto test_done; + if (exp2 > exp1 + 54) { + fl1.d = fl2.d; + goto test_done; + } + + /* do everything in excess precision so's we can round later */ + mant1 = MANTD_LL(fl1) << 9; + mant2 = MANTD_LL(fl2) << 9; + + if (SIGND(fl1)) + mant1 = -mant1; + if (SIGND(fl2)) + mant2 = -mant2; + + if (exp1 > exp2) + mant2 >>= exp1 - exp2; + else { + mant1 >>= exp2 - exp1; + exp1 = exp2; + } + mant1 += mant2; + + if (mant1 < 0) { + mant1 = -mant1; + sign = SIGNBIT; + } else if (!mant1) { + fl1.d = 0; + goto test_done; + } + + /* normalize up */ + while (!(mant1 & ((long long)7<<61))) { + mant1 <<= 1; + exp1--; + } + + /* normalize down? */ + if (mant1 & ((long long)3<<62)) { + mant1 >>= 1; + exp1++; + } + + /* round to even */ + mant1 += (mant1 & (1<<9)) ? (1<<8) : ((1<<8)-1); + + /* normalize down? */ + if (mant1 & ((long long)3<<62)) { + mant1 >>= 1; + exp1++; + } + + /* lose extra precision */ + mant1 >>= 9; + + /* turn off hidden bit */ + mant1 &= ~HIDDEND_LL; + + /* pack up and go home */ + fl1.ll = PACKD_LL(sign,exp1,mant1); + +test_done: + return (fl1.d); +} + +/* subtract two doubles */ +double +__subdf3 (double a1, double a2) +{ + register union double_long fl1, fl2; + + fl1.d = a1; + fl2.d = a2; + + /* check for zero args */ + if (!fl2.ll) + return (fl1.d); + /* twiddle sign bit and add */ + fl2.l.upper ^= SIGNBIT; + if (!fl1.ll) + return (fl2.d); + return __adddf3 (a1, fl2.d); +} + +/* multiply two doubles */ +double +__muldf3 (double a1, double a2) +{ + register union double_long fl1, fl2; + register unsigned long long result=0ULL; + register int exp; + int sign; + + fl1.d = a1; + fl2.d = a2; + + if (!fl1.ll || !fl2.ll) { + fl1.d = 0; + goto test_done; + } + + /* compute sign and exponent */ + sign = SIGND(fl1) ^ SIGND(fl2); + exp = EXPD(fl1) - EXCESSD; + exp += EXPD(fl2); + + fl1.ll = MANTD_LL(fl1); + fl2.ll = MANTD_LL(fl2); + + /* the multiply is done as one 31x31 multiply and two 31x21 multiples */ + result = (fl1.ll >> 21) * (fl2.ll >> 21); + result += ((fl1.ll & 0x1FFFFF) * (fl2.ll >> 21)) >> 21; + result += ((fl2.ll & 0x1FFFFF) * (fl1.ll >> 21)) >> 21; + + result >>= 2; + if (result & ((long long)1<<61)) { + /* round */ + result += 1<<8; + result >>= 9; + } else { + /* round */ + result += 1<<7; + result >>= 8; + exp--; + } + if (result & (HIDDEND_LL<<1)) { + result >>= 1; + exp++; + } + + result &= ~HIDDEND_LL; + + /* pack up and go home */ + fl1.ll = PACKD_LL(sign,exp,result); +test_done: + return (fl1.d); +} + +/* divide two doubles */ +double +__divdf3 (double a1, double a2) +{ + register union double_long fl1, fl2; + register long long mask,result; + register int exp, sign; + + fl1.d = a1; + fl2.d = a2; + + /* subtract exponents */ + exp = EXPD(fl1) - EXPD(fl2) + EXCESSD; + + /* compute sign */ + sign = SIGND(fl1) ^ SIGND(fl2); + + /* numerator zero??? */ + if (fl1.ll == 0) { + /* divide by zero??? */ + if (fl2.ll == 0) + fl1.ll = ((unsigned long long)1<<63)-1; /* NaN */ + else + fl1.ll = 0; + goto test_done; + } + + /* return +Inf or -Inf */ + if (fl2.ll == 0) { + fl1.ll = PACKD_LL(SIGND(fl1),2047,0); + goto test_done; + } + + + /* now get mantissas */ + fl1.ll = MANTD_LL(fl1); + fl2.ll = MANTD_LL(fl2); + + /* this assures we have 54 bits of precision in the end */ + if (fl1.ll < fl2.ll) { + fl1.ll <<= 1; + exp--; + } + + /* now we perform repeated subtraction of fl2.ll from fl1.ll */ + mask = (long long)1<<53; + result = 0; + while (mask) { + if (fl1.ll >= fl2.ll) + { + result |= mask; + fl1.ll -= fl2.ll; + } + fl1.ll <<= 1; + mask >>= 1; + } + + /* round */ + result += 1; + + /* normalize down */ + exp++; + result >>= 1; + + result &= ~HIDDEND_LL; + + /* pack up and go home */ + fl1.ll = PACKD_LL(sign, exp, result); + +test_done: + return (fl1.d); +} + +int +__gtdf2 (double a1, double a2) +{ + return __cmpdf2 ((float) a1, (float) a2) > 0; +} + +int +__gedf2 (double a1, double a2) +{ + return (__cmpdf2 ((float) a1, (float) a2) >= 0) - 1; +} + +int +__ltdf2 (double a1, double a2) +{ + return - (__cmpdf2 ((float) a1, (float) a2) < 0); +} + +int +__ledf2 (double a1, double a2) +{ + return __cmpdf2 ((float) a1, (float) a2) > 0; +} + +int +__eqdf2 (double a1, double a2) +{ + return *(long long *) &a1 == *(long long *) &a2; +} + +int +__nedf2 (double a1, double a2) +{ + return *(long long *) &a1 != *(long long *) &a2; +} + +/* absolute value of double */ +double +__absdf2(double a1) +{ + if (__cmpdf2(a1,0.0) < 0) + return __negdf2(a1); + else + return a1; +} + +/* absolute value of float */ +float +__abssf2(float a1) +{ + if (__cmpsf2(a1,0.0) < 0) + return __negsf2(a1); + else + return a1; +} diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/gdb-stub.c linux/arch/s390/kernel/gdb-stub.c --- v2.2.13/linux/arch/s390/kernel/gdb-stub.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/gdb-stub.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,575 @@ +/* + * arch/s390/kernel/gdb-stub.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + * + * Originally written by Glenn Engel, Lake Stevens Instrument Division + * + * Contributed by HP Systems + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * + * Modified for Linux/MIPS (and MIPS in general) by Andreas Busse + * Send complaints, suggestions etc. to + * + * Copyright (C) 1995 Andreas Busse + */ + +/* + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing a BREAK instruction. + * + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $#. + * + * where + * :: + * :: < two hex digits computed as modulo 256 sum of > + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * external low-level support routines + */ + +extern int putDebugChar(char c); /* write a single character */ +extern char getDebugChar(void); /* read and return a single char */ +extern void fltr_set_mem_err(void); +extern void trap_low(void); + +/* + * breakpoint and test functions + */ +extern void breakpoint(void); +extern void breakinst(void); + +/* + * local prototypes + */ + +static void getpacket(char *buffer); +static void putpacket(char *buffer); +static int hex(unsigned char ch); +static int hexToInt(char **ptr, int *intValue); +static unsigned char *mem2hex(char *mem, char *buf, int count, int may_fault); + + +/* + * BUFMAX defines the maximum number of characters in inbound/outbound buffers + * at least NUMREGBYTES*2 are needed for register packets + */ +#define BUFMAX 2048 + +static char input_buffer[BUFMAX]; +static char output_buffer[BUFMAX]; +int gdb_stub_initialised = FALSE; +static const char hexchars[]="0123456789abcdef"; + + +/* + * Convert ch from a hex digit to an int + */ +static int hex(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch-'a'+10; + if (ch >= '0' && ch <= '9') + return ch-'0'; + if (ch >= 'A' && ch <= 'F') + return ch-'A'+10; + return -1; +} + +/* + * scan for the sequence $# + */ +static void getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + unsigned char ch; + + do { + /* + * wait around for the start character, + * ignore all other characters + */ + while ((ch = (getDebugChar() & 0x7f)) != '$') ; + + checksum = 0; + xmitcsum = -1; + count = 0; + + /* + * now, read until a # or end of buffer is found + */ + while (count < BUFMAX) { + ch = getDebugChar() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + + if (count >= BUFMAX) + continue; + + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(getDebugChar() & 0x7f) << 4; + xmitcsum |= hex(getDebugChar() & 0x7f); + + if (checksum != xmitcsum) + putDebugChar('-'); /* failed checksum */ + else { + putDebugChar('+'); /* successful transfer */ + + /* + * if a sequence char is present, + * reply the sequence ID + */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + + /* + * remove sequence chars from buffer + */ + count = strlen(buffer); + for (i=3; i <= count; i++) + buffer[i-3] = buffer[i]; + } + } + } + } + while (checksum != xmitcsum); +} + +/* + * send the packet in buffer. + */ +static void putpacket(char *buffer) +{ + unsigned char checksum; + int count; + unsigned char ch; + + /* + * $#. + */ + + do { + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count]) != 0) { + if (!(putDebugChar(ch))) + return; + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum & 0xf]); + + } + while ((getDebugChar() & 0x7f) != '+'); +} + + + +/* + * Convert the memory pointed to by mem into hex, placing result in buf. + * Return a pointer to the last char put in buf (null), in case of mem fault, + * return 0. + * If MAY_FAULT is non-zero, then we will handle memory faults by returning + * a 0, else treat a fault like any other fault in the stub. + */ +static unsigned char *mem2hex(char *mem, char *buf, int count, int may_fault) +{ + unsigned char ch; + +/* set_mem_fault_trap(may_fault); */ + + while (count-- > 0) { + ch = *(mem++); + if (mem_err) + return 0; + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch & 0xf]; + } + + *buf = 0; + +/* set_mem_fault_trap(0); */ + + return buf; +} + +/* + * convert the hex array pointed to by buf into binary to be placed in mem + * return a pointer to the character AFTER the last byte written + */ +static char *hex2mem(char *buf, char *mem, int count, int may_fault) +{ + int i; + unsigned char ch; + +/* set_mem_fault_trap(may_fault); */ + + for (i=0; ifp_regs; + int has_ieee=save_fp_regs1(fpregs); + + if(!has_ieee) + { + fpregs->fpc=0; + fpregs->fprs[1].d= + fpregs->fprs[3].d= + fpregs->fprs[5].d= + fpregs->fprs[7].d=0; + memset(&fpregs->fprs[8].d,0,sizeof(freg_t)*8); + } +} + +void gdb_stub_set_non_pt_regs(gdb_pt_regs *regs) +{ + restore_fp_regs1(®s->fp_regs); +} + +void gdb_stub_send_signal(int sigval) +{ + char *ptr; + ptr = output_buffer; + + /* + * Send trap type (converted to signal) + */ + *ptr++ = 'S'; + *ptr++ = hexchars[sigval >> 4]; + *ptr++ = hexchars[sigval & 0xf]; + *ptr++ = 0; + putpacket(output_buffer); /* send it off... */ +} + +/* + * This function does all command processing for interfacing to gdb. It + * returns 1 if you should skip the instruction at the trap address, 0 + * otherwise. + */ +void gdb_stub_handle_exception(gdb_pt_regs *regs,int sigval) +{ + int trap; /* Trap type */ + int addr; + int length; + char *ptr; + unsigned long *stack; + + + /* + * reply to host that an exception has occurred + */ + send_signal(sigval); + + /* + * Wait for input from remote GDB + */ + while (1) { + output_buffer[0] = 0; + getpacket(input_buffer); + + switch (input_buffer[0]) + { + case '?': + send_signal(sigval); + continue; + + case 'd': + /* toggle debug flag */ + break; + + /* + * Return the value of the CPU registers + */ + case 'g': + gdb_stub_get_non_pt_regs(regs); + ptr = output_buffer; + ptr= mem2hex((char *)regs,ptr,sizeof(s390_regs_common),FALSE); + ptr= mem2hex((char *)®s->crs[0],ptr,NUM_CRS*CR_SIZE,FALSE); + ptr = mem2hex((char *)®s->fp_regs, ptr,sizeof(s390_fp_regs)); + break; + + /* + * set the value of the CPU registers - return OK + * FIXME: Needs to be written + */ + case 'G': + ptr=input_buffer; + hex2mem (ptr, (char *)regs,sizeof(s390_regs_common), FALSE); + ptr+=sizeof(s390_regs_common)*2; + hex2mem (ptr, (char *)regs->crs[0],NUM_CRS*CR_SIZE, FALSE); + ptr+=NUM_CRS*CR_SIZE*2; + hex2mem (ptr, (char *)regs->fp_regs,sizeof(s390_fp_regs), FALSE); + gdb_stub_set_non_pt_regs(regs); + strcpy(output_buffer,"OK"); + break; + + /* + * mAA..AA,LLLL Read LLLL bytes at address AA..AA + */ + case 'm': + ptr = &input_buffer[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length)) { + if (mem2hex((char *)addr, output_buffer, length, 1)) + break; + strcpy (output_buffer, "E03"); + } else + strcpy(output_buffer,"E01"); + break; + + /* + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK + */ + case 'M': + ptr = &input_buffer[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length) + && *ptr++ == ':') { + if (hex2mem(ptr, (char *)addr, length, 1)) + strcpy(output_buffer, "OK"); + else + strcpy(output_buffer, "E03"); + } + else + strcpy(output_buffer, "E02"); + break; + + /* + * cAA..AA Continue at address AA..AA(optional) + */ + case 'c': + /* try to read optional parameter, pc unchanged if no parm */ + + ptr = &input_buffer[1]; + if (hexToInt(&ptr, &addr)) + regs->cp0_epc = addr; + + /* + * Need to flush the instruction cache here, as we may + * have deposited a breakpoint, and the icache probably + * has no way of knowing that a data ref to some location + * may have changed something that is in the instruction + * cache. + * NB: We flush both caches, just to be sure... + */ + + flush_cache_all(); + return; + /* NOTREACHED */ + break; + + + /* + * kill the program + */ + case 'k' : + break; /* do nothing */ + + + /* + * Reset the whole machine (FIXME: system dependent) + */ + case 'r': + break; + + + /* + * Step to next instruction + */ + case 's': + /* + * There is no single step insn in the MIPS ISA, so we + * use breakpoints and continue, instead. + */ + single_step(regs); + flush_cache_all(); + return; + /* NOTREACHED */ + + } + break; + + } /* switch */ + + /* + * reply to the request + */ + + putpacket(output_buffer); + + } /* while */ +} + +/* + * This function will generate a breakpoint exception. It is used at the + * beginning of a program to sync up with a debugger and can be used + * otherwise as a quick means to stop program execution and "break" into + * the debugger. + */ +void breakpoint(void) +{ + if (!gdb_stub_initialised) + return; + __asm__ __volatile__( + ".globl breakinst\n" + "breakinst:\t.word %0\n\t" + : + : "i" (BREAKPOINT_U16) + : + ); +} + + + + + + + diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/head.S linux/arch/s390/kernel/head.S --- v2.2.13/linux/arch/s390/kernel/head.S Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/head.S Tue Jan 4 10:12:12 2000 @@ -0,0 +1,581 @@ +/* + * arch/s390/kernel/head.S + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hp@de.ibm.com), + * Martin Schwidefsky (schwidefsky@de.ibm.com), + * + * There are 4 different IPL methods + * 1) load the image directly into ram at address 0 and do an PSW restart + * 2) linload will load the image from address 0x10000 to memory 0x10000 + * and start the code thru LPSW 0x0008000080010000 (VM only, deprecated) + * 3) generate the tape ipl header, store the generated image on a tape + * and ipl from it + * 4) generate the vm reader ipl header, move the generated image to the + * VM reader (use option NOH!) and do a ipl from reader (VM only) + * We use the cpuid to distinguish between VM and native ipl + * params for kernel are pushed to 0x10400 (see setup.h) + */ + +#include +#include +#include + +#ifndef CONFIG_IPL + .org 0 + .long 0x00080000,0x80000000+start # Just a restart PSW +#else +#ifdef CONFIG_IPL_TAPE +#define IPL_BS 1024 + .org 0 + .long 0x00080000,0x80000000+iplstart # The first 24 bytes are loaded + .long 0x07000000,0x60000001 # by ipl to addresses 0-23. + .long 0x02000000,0x20000000+IPL_BS # (a PSW and two CCWs). + .long 0x00000000,0x00000000 # external old psw + .long 0x00000000,0x00000000 # svc old psw + .long 0x00000000,0x00000000 # program check old psw + .long 0x00000000,0x00000000 # machine check old psw + .long 0x00000000,0x00000000 # io old psw + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x000a0000,0x00000058 # external new psw + .long 0x000a0000,0x00000060 # svc new psw + .long 0x000a0000,0x00000068 # program check new psw + .long 0x000a0000,0x00000070 # machine check new psw + .long 0x00080000,0x80000000+.Lioint # io new psw + + .org 0x100 +iplstart: + l %r1,0xb8 # load ipl subchannel number + lhi %r2,IPL_BS # load start address + bras %r14,.Lloader # load rest of ipl image + l %r12,.Lparm # pointer to parameter area +# +# load parameter file from tape +# + l %r2,INITRD_START-PARMAREA(%r12) # use ramdisk location as temp + bras %r14,.Lloader # load parameter file + ltr %r2,%r2 # got anything ? + jz .Lnopf + chi %r2,896 + jnh .Lnotrunc + lhi %r2,896 +.Lnotrunc: + l %r4,INITRD_START-PARMAREA(%r12) + clc 0(6,%r4),.Lebcdic # prefix EBCDIC in parm file ? + jne .Lnocv + ahi %r4,6 # skip EBCDIC + ahi %r2,-6 + jnp .Lnopf + l %r3,.Lcvtab + tr 0(256,%r4),0(%r3) # convert parameters to ascii + tr 256(256,%r4),0(%r3) + tr 512(256,%r4),0(%r3) + tr 768(128,%r4),0(%r3) +.Lnocv: la %r3,COMMAND_LINE-PARMAREA(%r12) # load adr. of command line + mvc 0(256,%r3),0(%r4) + mvc 256(256,%r3),256(%r4) + mvc 512(256,%r3),512(%r4) + mvc 768(128,%r3),768(%r4) + slr %r0,%r0 + j .Lcntlp +.Ldelspc: + ic %r0,0(%r2,%r3) + chi %r0,0x20 # is it a space ? + je .Lcntlp + ahi %r2,1 + j .Leolp +.Lcntlp: + brct %r2,.Ldelspc +.Leolp: + slr %r0,%r0 + stc %r0,0(%r2,%r3) # terminate buffer +.Lnopf: + +# +# load ramdisk from tape +# + l %r2,INITRD_START-PARMAREA(%r12) # load adr. of ramdisk + bras %r14,.Lloader # load ramdisk + st %r2,INITRD_SIZE-PARMAREA(%r12) # store size of ramdisk + ltr %r2,%r2 + jnz .Lrdcont + st %r2,INITRD_START-PARMAREA(%r12) # no ramdisk found, null it +.Lrdcont: +# +# everything loaded, go for it +# + j start +# +# subroutine for loading from tape +# Paramters: +# R1 = device number +# R2 = load address +.Lloader: + st %r14,.Lldret + la %r3,.Lorbread # r3 = address of orb + la %r5,.Lirb # r5 = address of irb + st %r2,.Lccwread+4 # initialize CCW data addresses + lctl %c6,%c6,.Lcr6 + slr %r2,%r2 +.Lldlp: + lhi %r6,3 # 3 retries +.Lssch: + ssch 0(%r3) # load chunk of IPL_BS bytes + jnz .Llderr +.Lw4end: + bras %r14,.Lwait4io + tm 8(%r5),0x82 # do we have a problem ? + jnz .Lrecov + slr %r7,%r7 + icm %r7,3,10(%r5) # get residual count + lcr %r7,%r7 + ahi %r7,IPL_BS # IPL_BS-residual=#bytes read + ar %r2,%r7 # add to total size + tm 8(%r5),0x01 # found a tape mark ? + jnz .Ldone + l %r0,.Lccwread+4 # update CCW data addresses + ar %r0,%r7 + st %r0,.Lccwread+4 + j .Lldlp +.Ldone: + l %r14,.Lldret + br %r14 # r2 contains the total size +.Lrecov: + bras %r14,.Lsense # do the sensing + brct %r6,.Lssch # dec. retry count & branch + j .Llderr +# +# Sense subroutine +# +.Lsense: + st %r14,.Lsnsret + la %r7,.Lorbsense + ssch 0(%r7) # start sense command + jnz .Llderr + bras %r14,.Lwait4io + l %r14,.Lsnsret + tm 8(%r5),0x82 # do we have a problem ? + jnz .Llderr + br %r14 +# +# Wait for interrupt subroutine +# +.Lwait4io: + lpsw .Lwaitpsw +.Lioint: + c %r1,0xb8 # compare subchannel number + jne .Lwait4io + tsch 0(%r5) + slr %r0,%r0 + tm 8(%r5),0x82 # do we have a problem ? + jnz .Lwtexit + tm 8(%r5),0x04 # got device end ? + jz .Lwait4io +.Lwtexit: + br %r14 +.Llderr: + lpsw .Lcrash + + .align 8 +.Lorbread: + .long 0x00000000,0x0080ff00,.Lccwread + .align 8 +.Lorbsense: + .long 0x00000000,0x0080ff00,.Lccwsense + .align 8 +.Lccwread: + .long 0x02200000+IPL_BS,0x00000000 +.Lccwsense: + .long 0x04200001,0x00000000 +.Lwaitpsw: + .long 0x020a0000,0x80000000+.Lioint + +.Lirb: .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +.Lcr6: .long 0xff000000 + .align 8 +.Lcrash:.long 0x000a0000,0x00000000 +.Lparm: .long PARMAREA +.Lldret:.long 0 +.Lsnsret: .long 0 +.Lebcdic:.long 0xc5c2c3c4,0xc9c30000 +.Lcvtab:.long _ebcasc # ebcdic to ascii table + +#endif /* CONFIG_IPL_TAPE */ + +#ifdef CONFIG_IPL_VM + .org 0 + .long 0x00080000,0x80000000+iplstart # The first 24 bytes are loaded + .long 0x02000018,0x60000050 # by ipl to addresses 0-23. + .long 0x02000068,0x60000050 # (a PSW and two CCWs). + .fill 80-24,1,0x40 # bytes 24-79 are discarded !! + .long 0x020000f0,0x60000050 # The next 160 byte are loaded + .long 0x02000140,0x60000050 # to addresses 0x18-0xb7 + .long 0x02000190,0x60000050 # They form the continuation + .long 0x020001e0,0x60000050 # of the CCW program started + .long 0x02000230,0x60000050 # by ipl and load the range + .long 0x02000280,0x60000050 # 0x0f0-0x730 from the image + .long 0x020002d0,0x60000050 # to the range 0x0f0-0x730 + .long 0x02000320,0x60000050 # in memory. At the end of + .long 0x02000370,0x60000050 # the channel program the PSW + .long 0x020003c0,0x60000050 # at location 0 is loaded. + .long 0x02000410,0x60000050 # Initial processing starts + .long 0x02000460,0x60000050 # at 0xf0 = iplstart. + .long 0x020004b0,0x60000050 + .long 0x02000500,0x60000050 + .long 0x02000550,0x60000050 + .long 0x020005a0,0x60000050 + .long 0x020005f0,0x60000050 + .long 0x02000640,0x60000050 + .long 0x02000690,0x60000050 + .long 0x020006e0,0x20000050 + + + .org 0xf0 +iplstart: + stidp __LC_CPUID # store cpuid + tm __LC_CPUID,0xff # running under VM ? + jno start # no -> skip loader part + l %r1,0xb8 # load ipl subchannel number + lhi %r2,0x730 # load start address + bras %r14,.Lloader # load rest of ipl image + + l %r12,.Lparm # pointer to parameter area + +# +# load parameter file from reader +# + la %r2,COMMAND_LINE-PARMAREA(%r12) # load adr. of command line + bras %r14,.Lloader # load parameter file + ltr %r2,%r2 # got anything ? + jz .Lnopf + la %r3,COMMAND_LINE-PARMAREA(%r12) # load adr. of command line + l %r4,.Lcvtab + tr 0(256,3),0(4) # convert parameters to ascii + l %r4,.Lcvtab + tr 256(256,3),0(4) + l %r4,.Lcvtab + tr 512(256,3),0(4) + l %r4,.Lcvtab + tr 768(128,3),0(4) + slr %r0,%r0 + j .Lcntlp +.Ldelspc: + ic %r0,0(%r2,%r3) + chi %r0,0x20 # is it a space ? + je .Lcntlp + ahi %r2,1 + j .Leolp +.Lcntlp: + brct %r2,.Ldelspc +.Leolp: + slr %r0,%r0 + stc %r0,0(%r2,%r3) # terminate buffer +.Lnopf: + +# +# load ramdisk from reader +# + l %r2,INITRD_START-PARMAREA(%r12) # load adr. of ramdisk + bras %r14,.Lloader # load ramdisk + st %r2,INITRD_SIZE-PARMAREA(%r12) # store size of ramdisk + ltr %r2,%r2 + jnz .Lrdcont + st %r2,INITRD_START-PARMAREA(%r12) # no ramdisk found, null it +.Lrdcont: + +# +# everything loaded, reset files in reader, then go for it +# + la %r2,.Lreset + lhi %r3,26 + .long 0x83230008 + j start + +# +# subroutine for loading cards from the reader +# +.Lloader: + la %r3,.Lorb # r2 = address of orb into r2 + la %r5,.Lirb # r4 = address of irb + la %r6,.Lccws + la %r7,20 +.Linit: + st %r2,4(%r6) # initialize CCW data addresses + ahi %r2,0x50 + ahi %r6,8 + brct 7,.Linit + + lctl %c6,%c6,.Lcr6 # set IO subclass mask + slr %r2,%r2 +.Lldlp: + ssch 0(%r3) # load chunk of 1600 bytes + jnz .Llderr +.Lwait4irq: + mvc __LC_IO_NEW_PSW(8),.Lnewpsw # set up IO interrupt psw + lpsw .Lwaitpsw +.Lioint: + c %r1,0xb8 # compare subchannel number + jne .Lwait4irq + tsch 0(%r5) + + slr %r0,%r0 + ic %r0,8(%r5) # get device status + chi %r0,8 # channel end ? + je .Lcont + chi %r0,12 # channel end + device end ? + je .Lcont + + l %r0,4(%r5) + s %r0,8(%r3) # r0/8 = number of ccws executed + mhi %r0,10 # *10 = number of bytes in ccws + lh %r3,10(%r5) # get residual count + sr %r0,%r3 # #ccws*80-residual=#bytes read + ar %r2,%r0 + + br %r14 # r2 contains the total size + +.Lcont: + ahi %r2,0x640 # add 0x640 to total size + la %r6,.Lccws + la %r7,20 +.Lincr: + l %r0,4(%r6) # update CCW data addresses + ahi %r0,0x640 + st %r0,4(%r6) + ahi %r6,8 + brct 7,.Lincr + + j .Lldlp +.Llderr: + lpsw .Lcrash + + .align 8 +.Lorb: .long 0x00000000,0x0080ff00,.Lccws +.Lirb: .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +.Lcr6: .long 0xff000000 +.Lloadp:.long 0,0 +.Lparm: .long PARMAREA +.Lcvtab:.long _ebcasc # ebcdic to ascii table +.Lreset:.byte 0xc3,0xc8,0xc1,0xd5,0xc7,0xc5,0x40,0xd9,0xc4,0xd9,0x40 + .byte 0xc1,0xd3,0xd3,0x40,0xd2,0xc5,0xc5,0xd7,0x40,0xd5,0xd6 + .byte 0xc8,0xd6,0xd3,0xc4 # "change rdr all keep nohold" + .align 8 +.Lcrash: + .long 0x000a0000,0x00000000 +.Lnewpsw: + .long 0x00080000,0x80000000+.Lioint +.Lwaitpsw: + .long 0x020a0000,0x80000000+.Lioint + + .align 8 +.Lccws: .rept 19 + .long 0x02600050,0x00000000 + .endr + .long 0x02200050,0x00000000 + + .org 0x730 # end of the area loaded by the ipl channel program +#endif /* CONFIG_IPL_VM */ + +#endif /* CONFIG_IPL */ + +# +# startup-code at 0x10000, running in real mode +# this is called either by the ipl loader or directly by PSW restart or linload +# + .org 0x10000 + .globl start +start: basr %r13,0 # get base +.LPG1: lctl %c1,%c1,.Lpstd-.LPG1(%r13) # load pstd + lctl %c7,%c7,.Lpstd-.LPG1(%r13) # load sstd + lctl %c13,%c13,.Lpstd-.LPG1(%r13) # load hstd + lctl %c0,%c0,.Lcr0-.LPG1(%r13) # set CR0 + l %r12,.Lparm1-.LPG1(%r13) # pointer to parameter area + +# +# find out memory size +# + mvc 104(8,0),.Lpcmem-.LPG1(%r13) # setup program check handler + slr %r1,%r1 + lhi %r2,1 + sll %r2,20 +.Lloop: + l %r0,0(%r1) # test page + ar %r1,%r2 # add 1M + jnm .Lloop # r1 < 0x80000000 -> loop +.Lchkmem: + n %r1,.L4malign-.LPG1(%r13) # align to multiples of 4M + st %r1,MEMORY_SIZE-PARMAREA(%r12) # store memory size + +# +# find out if we are running under VM +# + stidp __LC_CPUID # store cpuid + tm __LC_CPUID,0xff # running under VM ? + jno .Lnovm + oi MACHINE_FLAGS+3-PARMAREA(%r12),1 # set VM flag +.Lnovm: + +# +# find out if we have an IEEE fpu +# + mvc 104(8,0),.Lpcfpu-.LPG1(%r13) # setup program check handler + ld %f0,.Lflt0-.LPG1(%r13) # load (float) 0.0 + ldr %f2,%f0 + adbr %f0,%f2 # test IEEE add instruction + oi MACHINE_FLAGS+3-PARMAREA(%r12),2 # set IEEE fpu flag +.Lchkfpu: + +# +# move ramdisk to the end of storage, out of the way of the paging tables +# + icm %r2,15,INITRD_START-PARMAREA(%r12) # load adr. of ramdisk + jz .Lnocprd + icm %r3,15,INITRD_SIZE-PARMAREA(%r12) # store size of ramdisk + jz .Lnocprd + l %r4,MEMORY_SIZE-PARMAREA(%r12) + sr %r4,%r3 + srl %r4,12 + sll %r4,12 + st %r4,INITRD_START-PARMAREA(%r12) + lr %r5,%r3 +.Lcprd: + mvcle %r4,%r2,0 + jo .Lcprd +.Lnocprd: + lpsw .Lentry-.LPG1(13) # jump to _stext in primary-space, + # virtual and never return ... + .align 8 +.Lentry:.long 0x0408C000,0x80000000 + _stext +.Lpstd: .long .Lpgd+0x7F # segment-table +.Lcr0: .long 0x04b50002 +.Lpcmem:.long 0x00080000,0x80000000 + .Lchkmem +.Lpcfpu:.long 0x00080000,0x80000000 + .Lchkfpu +.Lflt0: .double 0 +.Lparm1:.long PARMAREA +.L4malign:.long 0xffc00000 + +# +# params at 10400 (setup.h) +# + .org PARMAREA + .long 0x0100 # ORIG_ROOT_DEV: ramdisk major/minor + .word 0 # MOUNT_ROOT_RDONLY: no + .long 0 # MEMORY_SIZE + .long 0 # MACHINE_FLAGS (bit 0:VM, bit 1:IEEE) + .long RAMDISK_ORIGIN # INITRD_START + .long 0x800000 # INITRD_SIZE + .word 0 # RAMDISK_FLAGS + + .org COMMAND_LINE +# .byte "root=/dev/nfs rw nfsroot=9.164.160.7:/home/mschwide/nfsboot " +# .byte "ip=9.164.147.12:9.164.160.7:9.164.147.1:255.255.255.0:vmlinux:tr0:off" +# .byte "root=/dev/nfs nfsroot=9.164.160.7:/home/mschwide/nfsboot " +# .byte "ip=9.164.181.228:9.164.160.7:9.164.181.1:255.255.224.0:vmlinux:tr0:off" +# .byte "root=/dev/nfs nfsroot=9.164.160.7:/home/pasch/nfsboot " +# .byte "ip=9.164.185.120:9.164.160.7:9.164.181.1:255.255.224.0:vmlinux:tr0:off" +# .byte "mdisk=402:65536:1229,403:131072:2780 root=/dev/mnda ro" +# .byte "root=/dev/nfs rw nfsroot=9.164.160.209:/usr/local/nfsboot " +# .byte "ip=9.164.181.228:9.164.160.209:9.164.181.1:255.255.224.0:vmlinux:tr0:off" + .byte "root=/dev/ram0 ro" +# .byte 0 + +# +# startup-code, running in virtual mode +# + .org 0x10800 + .globl _stext +_stext: basr %r13,0 # get base +.LPG2: +# +# Setup lowcore +# + spx .Lprefix-.LPG2(%r13) # set prefix to linux lowcore + l %r15,.Linittu-.LPG2(%r13) + ahi %r15,8192 # init_task_union + 8191 + st %r15,__LC_KERNEL_STACK # set end of kernel stack + ahi %r15,-96 + lhi %r0,-1 + st %r0,__LC_KERNEL_LEVEL # set interrupt count to -1 +# +# clear bss memory +# + l %r2,.Lbss_bgn-.LPG2(%r13) # start of bss + l %r3,.Lbss_end-.LPG2(%r13) # end of bss + sr %r3,%r2 # length of bss + sr %r4,%r4 # + sr %r5,%r5 # set src,length and pad to zero + sr %r0,%r0 # + mvcle %r2,%r4,0 # clear mem + jo .-4 # branch back, if not finish +# check control registers + stctl %c0,%c15,0(%r15) + l %r0,0(%r15) + o %r0,.Lcr0or-.LPG2(%r13) # enable sigp external ints. + st %r0,0(%r15) + lctl %c0,%c15,0(%r15) + +# + lam 0,15,.Laregs-.LPG2(%r13) # load access regs needed by uaccess + l %r14,.Lstart-.LPG2(%r13) + basr %r14,%r14 # call start_kernel +# +# We returned from start_kernel ?!? PANIK +# + basr %r13,0 + lpsw .Ldw-.(%r13) # load disabled wait psw +# +.Lstart: .long start_kernel + .align 8 +.Lprefix: .long init_S390_lowcore +.Linittu: .long init_task_union +.Lbss_bgn: .long __bss_start +.Lbss_end: .long _end +.Lcr0or: .long 0x00002000 + .align 4 +.Laregs: .long 0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 + .align 8 +.Ldw: .long 0x000a0000,0x00000000 + +# +# tempory segment-table at 0x11000 +# + .org 0x11000 +.Lpgd: .long .Lpt0+0x1f # 00000000-000fffff + .long .Lpt1+0x1f # 00100000-001fffff + .long .Lpt2+0x1f # 00200000-002fffff + .long .Lpt3+0x1f # 00300000-003fffff + .fill 2044,4,0x20 # 00400000-7fffffff + +# +# tempory page-tables at 0x12000-0x15fff +# + .macro mktable from,to + .long \from*0x10000 + .long \from*0x10000+0x1000 + .long \from*0x10000+0x2000 + .long \from*0x10000+0x3000 + .long \from*0x10000+0x4000 + .long \from*0x10000+0x5000 + .long \from*0x10000+0x6000 + .long \from*0x10000+0x7000 + .long \from*0x10000+0x8000 + .long \from*0x10000+0x9000 + .long \from*0x10000+0xa000 + .long \from*0x10000+0xb000 + .long \from*0x10000+0xc000 + .long \from*0x10000+0xd000 + .long \from*0x10000+0xe000 + .long \from*0x10000+0xf000 + .if \to-\from + mktable "(\from+1)",\to + .endif + .endm + +.Lpt0: mktable 0,15 +.Lpt1: mktable 16,31 +.Lpt2: mktable 32,47 +.Lpt3: mktable 48,63 + diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/ieee.h linux/arch/s390/kernel/ieee.h --- v2.2.13/linux/arch/s390/kernel/ieee.h Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/ieee.h Tue Jan 4 10:12:12 2000 @@ -0,0 +1,90 @@ +/* + * arch/s390/kernel/ieee.h + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +#include + +static inline void _adddf(int R1,int R2) +{ + current->tss.fprs[R1].fd = current->tss.fprs[R1].fd + + current->tss.fprs[R2].fd; +} + +static inline void _subdf(int R1,int R2) +{ + current->tss.fprs[R1].fd = current->tss.fprs[R1].fd - + current->tss.fprs[R2].fd; +} + +static inline void _muldf(int R1,int R2) +{ + current->tss.fprs[R1].fd = current->tss.fprs[R1].fd * + current->tss.fprs[R2].fd; +} + +static inline void _divdf(int R1,int R2) +{ + current->tss.fprs[R1].fd = current->tss.fprs[R1].fd / + current->tss.fprs[R2].fd; +} + +static inline void _negdf(int R1,int R2) +{ + current->tss.fprs[R1].fd = -current->tss.fprs[R1].fd; +} + +static inline void _fixdfsi(int R1,int R2) +{ + current->tss.regs->gprs[R1] = (__u32) current->tss.fprs[R2].fd; +} + +static inline void _extendsidf(int R1,int R2) +{ + current->tss.fprs[R1].fd = (double) current->tss.regs->gprs[R2]; +} + + +static inline void _addsf(int R1,int R2) +{ + current->tss.fprs[R1].ff = current->tss.fprs[R1].ff + + current->tss.fprs[R2].ff; +} + +static inline void _subsf(int R1,int R2) +{ + current->tss.fprs[R1].ff = current->tss.fprs[R1].ff - + current->tss.fprs[R2].ff; +} + +static inline void _mulsf(int R1,int R2) +{ + current->tss.fprs[R1].ff = current->tss.fprs[R1].ff * + current->tss.fprs[R2].ff; +} + +static inline void _divsf(int R1,int R2) +{ + current->tss.fprs[R1].ff = current->tss.fprs[R1].ff / + current->tss.fprs[R2].ff; +} + +static inline void _negsf(int R1,int R2) +{ + current->tss.fprs[R1].ff = -current->tss.fprs[R1].ff; +} + +static inline void _fixsfsi(int R1,int R2) +{ + current->tss.regs->gprs[R1] = (__u32) current->tss.fprs[R2].ff; +} + +static inline void _extendsisf(int R1,int R2) +{ + current->tss.fprs[R1].ff = (double) current->tss.regs->gprs[R2]; +} + + diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/init_task.c linux/arch/s390/kernel/init_task.c --- v2.2.13/linux/arch/s390/kernel/init_task.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/init_task.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,31 @@ +/* + * arch/s390/kernel/init_task.c + * + * S390 version + * + * Derived from "arch/i386/kernel/init_task.c" + */ + +#include +#include + +#include +#include + +static struct vm_area_struct init_mmap = INIT_MMAP; +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS; +struct mm_struct init_mm = INIT_MM; + +/* + * Initial task structure. + * + * We need to make sure that this is 8192-byte aligned due to the + * way process stacks are handled. This is done by making sure + * the linker maps this in the .text segment right after head.S, + * and making head.S ensure the proper alignment. + * + * The things we do for performance.. + */ +union task_union init_task_union __attribute__((aligned(8192))) = { INIT_TASK }; diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/irq.c linux/arch/s390/kernel/irq.c --- v2.2.13/linux/arch/s390/kernel/irq.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/irq.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,428 @@ +/* + * arch/s390/kernel/irq.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Ingo Adlung (adlung@de.ibm.com) + * + * Derived from "arch/i386/kernel/irq.c" + * Copyright (C) 1992, 1999 Linus Torvalds, Ingo Molnar + * + * S/390 I/O interrupt processing and I/O request processing is + * implemented in linux/arch/s390/do_io.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "irq.h" + + +void s390_init_IRQ(void); +void s390_free_irq(unsigned int irq, void *dev_id); +int s390_request_irq( unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, + const char *devname, + void *dev_id); + +atomic_t nmi_counter; +spinlock_t s390_bh_lock = SPIN_LOCK_UNLOCKED; +/* + * Dummy controller type for unused interrupts + */ +int do_none(unsigned int irq, int cpu, struct pt_regs * regs) { return 0;} +int enable_none(unsigned int irq) { return(-ENODEV); } +int disable_none(unsigned int irq) { return(-ENODEV); } + +struct hw_interrupt_type no_irq_type = { + "none", + do_none, + enable_none, + disable_none +}; + +irq_desc_t irq_desc[NR_IRQS] = { + [0 ... (NR_IRQS-1)] = { 0, &no_irq_type, }, +}; + +#if 0 +/* + * The following vectors are part of the Linux architecture, there + * is no hardware IRQ pin equivalent for them, they are triggered + * through the ICC by us (IPIs), via smp_message_pass(): + */ +BUILD_SMP_INTERRUPT(reschedule_interrupt) +BUILD_SMP_INTERRUPT(invalidate_interrupt) +BUILD_SMP_INTERRUPT(stop_cpu_interrupt) +BUILD_SMP_INTERRUPT(mtrr_interrupt) +BUILD_SMP_INTERRUPT(spurious_interrupt) +#endif + +#if 0 +static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { } +#endif + +int get_irq_list(char *buf) +{ + int i, j; + struct irqaction * action; + char *p = buf; + + p += sprintf(p, " "); + for (j=0; jtypename); + p += sprintf(p, " %s", action->name); + + for (action=action->next; action; action = action->next) { + p += sprintf(p, ", %s", action->name); + } + *p++ = '\n'; + } + p += sprintf(p, "NMI: %10u\n", atomic_read(&nmi_counter)); +#ifdef __SMP__ + p += sprintf(p, "IPI: %10u\n", atomic_read(&ipi_count)); +#endif + return p - buf; +} + +/* + * Global interrupt locks for SMP. Allow interrupts to come in on any + * CPU, yet make cli/sti act globally to protect critical regions.. + */ +#ifdef __SMP__ +atomic_t global_irq_holder = ATOMIC_INIT(NO_PROC_ID); +atomic_t global_irq_lock; +atomic_t global_irq_count = ATOMIC_INIT(0); + +atomic_t global_bh_count; +atomic_t global_bh_lock; + +/* + * "global_cli()" is a special case, in that it can hold the + * interrupts disabled for a longish time, and also because + * we may be doing TLB invalidates when holding the global + * IRQ lock for historical reasons. Thus we may need to check + * SMP invalidate events specially by hand here (but not in + * any normal spinlocks) + * + * Thankfully we don't need this as we can deliver flush tlbs with + * interrupts disabled DJB :-) + */ +#define check_smp_invalidate(cpu) + +static void show(char * str) +{ + int i; + unsigned long *stack; + int cpu = smp_processor_id(); + + printk("\n%s, CPU %d:\n", str, cpu); + printk("irq: %d [%d]\n", + atomic_read(&global_irq_count),atomic_read(&S390_lowcore.local_irq_count)); + printk("bh: %d [%d]\n", + atomic_read(&global_bh_count),atomic_read(&S390_lowcore.local_bh_count)); + stack = (unsigned long *) &str; + for (i = 40; i ; i--) { + unsigned long x = *++stack; + if (x > (unsigned long) &init_task_union && x < (unsigned long) &vsprintf) { + printk("<[%08lx]> ", x); + } + } +} + +#define MAXCOUNT 100000000 + +static inline void wait_on_bh(void) +{ + int count = MAXCOUNT; + do { + if (!--count) { + show("wait_on_bh"); + count = ~0; + } + /* nothing .. wait for the other bh's to go away */ + } while (atomic_read(&global_bh_count) != 0); +} + +static inline void wait_on_irq(int cpu) +{ + int count = MAXCOUNT; + + for (;;) { + + /* + * Wait until all interrupts are gone. Wait + * for bottom half handlers unless we're + * already executing in one.. + */ + if (!atomic_read(&global_irq_count)) { + if (atomic_read(&safe_get_cpu_lowcore(cpu).local_bh_count)|| + !atomic_read(&global_bh_count)) + break; + } + + /* Duh, we have to loop. Release the lock to avoid deadlocks */ + clear_bit(0,&global_irq_lock); + + for (;;) { + if (!--count) { + show("wait_on_irq"); + count = ~0; + } + __sti(); + SYNC_OTHER_CORES(cpu); + __cli(); + check_smp_invalidate(cpu); + if (atomic_read(&global_irq_count)) + continue; + if (atomic_read(&global_irq_lock)) + continue; + if (!(atomic_read(&safe_get_cpu_lowcore(cpu).local_bh_count)) + && atomic_read(&global_bh_count)) + continue; + if (!test_and_set_bit(0,&global_irq_lock)) + break; + } + } +} + +/* + * This is called when we want to synchronize with + * bottom half handlers. We need to wait until + * no other CPU is executing any bottom half handler. + * + * Don't wait if we're already running in an interrupt + * context or are inside a bh handler. + */ +void synchronize_bh(void) +{ + if (atomic_read(&global_bh_count) && !in_interrupt()) + wait_on_bh(); +} + +/* + * This is called when we want to synchronize with + * interrupts. We may for example tell a device to + * stop sending interrupts: but to make sure there + * are no interrupts that are executing on another + * CPU we need to call this function. + */ +void synchronize_irq(void) +{ + if (atomic_read(&global_irq_count)) { + /* Stupid approach */ + cli(); + sti(); + } +} + +static inline void get_irqlock(int cpu) +{ + if (test_and_set_bit(0,&global_irq_lock)) { + /* do we already hold the lock? */ + if ( cpu == atomic_read(&global_irq_holder)) + return; + /* Uhhuh.. Somebody else got it. Wait.. */ + do { + do { + check_smp_invalidate(cpu); + } while (test_bit(0,&global_irq_lock)); + } while (test_and_set_bit(0,&global_irq_lock)); + } + /* + * We also to make sure that nobody else is running + * in an interrupt context. + */ + wait_on_irq(cpu); + + /* + * Ok, finally.. + */ + atomic_set(&global_irq_holder,cpu); +} + +#define EFLAGS_I_SHIFT 25 + +/* + * A global "cli()" while in an interrupt context + * turns into just a local cli(). Interrupts + * should use spinlocks for the (very unlikely) + * case that they ever want to protect against + * each other. + * + * If we already have local interrupts disabled, + * this will not turn a local disable into a + * global one (problems with spinlocks: this makes + * save_flags+cli+sti usable inside a spinlock). + */ +void __global_cli(void) +{ + unsigned int flags; + + __save_flags(flags); + if (flags & (1 << EFLAGS_I_SHIFT)) { + int cpu = smp_processor_id(); + __cli(); + if (!atomic_read(&S390_lowcore.local_irq_count)) + get_irqlock(cpu); + } +} + +void __global_sti(void) +{ + + if (!atomic_read(&S390_lowcore.local_irq_count)) + release_irqlock(smp_processor_id()); + __sti(); +} + +/* + * SMP flags value to restore to: + * 0 - global cli + * 1 - global sti + * 2 - local cli + * 3 - local sti + */ +unsigned long __global_save_flags(void) +{ + int retval; + int local_enabled; + unsigned long flags; + + __save_flags(flags); + local_enabled = (flags >> EFLAGS_I_SHIFT) & 1; + /* default to local */ + retval = 2 + local_enabled; + + /* check for global flags if we're not in an interrupt */ + if (!atomic_read(&S390_lowcore.local_irq_count)) + { + if (local_enabled) + retval = 1; + if (atomic_read(&global_irq_holder)== smp_processor_id()) + retval = 0; + } + return retval; +} + +void __global_restore_flags(unsigned long flags) +{ + switch (flags) { + case 0: + __global_cli(); + break; + case 1: + __global_sti(); + break; + case 2: + __cli(); + break; + case 3: + __sti(); + break; + default: + printk("global_restore_flags: %08lx (%08lx)\n", + flags, (&flags)[-1]); + } +} + +#endif + +/* + * Note : This fuction should be eliminated as it doesn't comply with the + * S/390 irq scheme we have implemented ... + */ +int handle_IRQ_event(unsigned int irq, int cpu, struct pt_regs * regs) +{ + struct irqaction * action; + int status; + + status = 0; + action = irq_desc[irq].action; + + if (action) { + status |= 1; + + if (!(action->flags & SA_INTERRUPT)) + __sti(); + + do { + status |= action->flags; + action->handler(irq, action->dev_id, regs); + action = action->next; + } while (action); + if (status & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + __cli(); + } + + return status; +} + + +void enable_nop(int irq) +{ +} + +__initfunc(void init_IRQ(void)) +{ + + s390_init_IRQ(); + +} + + +void free_irq(unsigned int irq, void *dev_id) +{ + s390_free_irq( irq, dev_id); +} + + +int request_irq( unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, + const char *devname, + void *dev_id) +{ + return( s390_request_irq( irq, handler, irqflags, devname, dev_id ) ); + +} + diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/irq.h linux/arch/s390/kernel/irq.h --- v2.2.13/linux/arch/s390/kernel/irq.h Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/irq.h Tue Jan 4 10:12:12 2000 @@ -0,0 +1,723 @@ +#ifndef __irq_h +#define __irq_h + +#include +#include + +/* + * Interrupt controller descriptor. This is all we need + * to describe about the low-level hardware. + */ +struct hw_interrupt_type { + const char *typename; + int (*handle)(unsigned int irq, + int cpu, + struct pt_regs * regs); + int (*enable) (unsigned int irq); + int (*disable)(unsigned int irq); +}; + + +/* + * Status: reason for being disabled: somebody has + * done a "disable_irq()" or we must not re-enter the + * already executing irq.. + */ +#define IRQ_INPROGRESS 1 +#define IRQ_DISABLED 2 +#define IRQ_PENDING 4 + +/* + * path management control word + */ +typedef struct { + unsigned long intparm; /* interruption parameter */ + unsigned int res0 : 2; /* reserved zeros */ + unsigned int isc : 3; /* interruption sublass */ + unsigned int res5 : 3; /* reserved zeros */ + unsigned int ena : 1; /* enabled */ + unsigned int lm : 2; /* limit mode */ + unsigned int mme : 2; /* measurement-mode enable */ + unsigned int mp : 1; /* multipath mode */ + unsigned int tf : 1; /* timing facility */ + unsigned int dnv : 1; /* device number valid */ + unsigned int dev : 16; /* device number */ + unsigned char lpm; /* logical path mask */ + unsigned char pnom; /* path not operational mask */ + unsigned char lpum; /* last path used mask */ + unsigned char pim; /* path installed mask */ + unsigned short mbi; /* measurement-block index */ + unsigned char pom; /* path operational mask */ + unsigned char pam; /* path available mask */ + unsigned char chpid[8]; /* CHPID 0-7 (if available) */ + unsigned int unused1 : 8; /* reserved zeros */ + unsigned int st : 3; /* subchannel type */ + unsigned int unused2 : 20; /* reserved zeros */ + unsigned int csense : 1; /* concurrent sense; can be enabled ...*/ + /* ... per MSCH, however, if facility */ + /* ... is not installed, this results */ + /* ... in an operand exception. */ + } pmcw_t; + +/* + * subchannel status word + */ +typedef struct { + unsigned int key : 4; /* subchannel key */ + unsigned int sctl : 1; /* suspend control */ + unsigned int eswf : 1; /* ESW format */ + unsigned int cc : 2; /* deferred condition code */ + unsigned int fmt : 1; /* format */ + unsigned int pfch : 1; /* prefetch */ + unsigned int isic : 1; /* initial-status interruption control */ + unsigned int alcc : 1; /* address-limit checking control */ + unsigned int ssi : 1; /* supress-suspended interruption */ + unsigned int zcc : 1; /* zero condition code */ + unsigned int ectl : 1; /* extended control */ + unsigned int pno : 1; /* path not operational */ + unsigned int res : 1; /* reserved */ + unsigned int fctl : 3; /* function control */ + unsigned int actl : 7; /* activity control */ + unsigned int stctl : 5; /* status control */ + unsigned long cpa; /* channel program address */ + unsigned int dstat : 8; /* device status */ + unsigned int cstat : 8; /* subchannel status */ + unsigned int count : 16; /* residual count */ + } scsw_t; + +#define SCSW_FCTL_CLEAR_FUNC 0x1 +#define SCSW_FCTL_HALT_FUNC 0x2 +#define SCSW_FCTL_START_FUNC 0x4 + +#define SCSW_ACTL_SUSPENDED 0x1 +#define SCSW_ACTL_DEVACT 0x2 +#define SCSW_ACTL_SCHACT 0x4 +#define SCSW_ACTL_CLEAR_PEND 0x8 +#define SCSW_ACTL_HALT_PEND 0x10 +#define SCSW_ACTL_START_PEND 0x20 +#define SCSW_ACTL_RESUME_PEND 0x40 + +#define SCSW_STCTL_STATUS_PEND 0x1 +#define SCSW_STCTL_SEC_STATUS 0x2 +#define SCSW_STCTL_PRIM_STATUS 0x4 +#define SCSW_STCTL_INTER_STATUS 0x8 +#define SCSW_STCTL_ALERT_STATUS 0x10 + +#define DEV_STAT_ATTENTION 0x80 +#define DEV_STAT_STAT_MOD 0x40 +#define DEV_STAT_CU_END 0x20 +#define DEV_STAT_BUSY 0x10 +#define DEV_STAT_CHN_END 0x08 +#define DEV_STAT_DEV_END 0x04 +#define DEV_STAT_UNIT_CHECK 0x02 +#define DEV_STAT_UNIT_EXCEP 0x01 + +#define SCHN_STAT_PCI 0x80 +#define SCHN_STAT_INCORR_LEN 0x40 +#define SCHN_STAT_PROG_CHECK 0x20 +#define SCHN_STAT_PROT_CHECK 0x10 +#define SCHN_STAT_CHN_DATA_CHK 0x08 +#define SCHN_STAT_CHN_CTRL_CHK 0x04 +#define SCHN_STAT_INTF_CTRL_CHK 0x02 +#define SCHN_STAT_CHAIN_CHECK 0x01 + +/* + * subchannel information block + */ +typedef struct { + pmcw_t pmcw; /* path management control word */ + scsw_t scsw; /* subchannel status word */ + char mda[12]; /* model dependent area */ + } schib_t; + +typedef struct { + char cmd_code;/* command code */ + char flags; /* flags, like IDA adressing, etc. */ + unsigned short count; /* byte count */ + void *cda; /* data address */ + } ccw1_t __attribute__ ((aligned(8))); + +#define CCW_FLAG_DC 0x80 +#define CCW_FLAG_CC 0x40 +#define CCW_FLAG_SLI 0x20 +#define CCW_FLAG_SKIP 0x10 +#define CCW_FLAG_PCI 0x08 +#define CCW_FLAG_IDA 0x04 +#define CCW_FLAG_SUSPEND 0x02 + +#define CCW_CMD_BASIC_SENSE 0x04 +#define CCW_CMD_TIC 0x08 +#define CCW_CMD_SENSE_ID 0xE4 +#define CCW_CMD_NOOP 0x03 +#define CCW_CMD_RDC 0x64 + +#define SENSE_MAX_COUNT 0x20 + +/* + * architectured values for first sense byte + */ +#define SNS0_CMD_REJECT 0x80 +#define SNS_CMD_REJECT SNS0_CMD_REJECT +#define SNS0_INTERVENTION_REQ 0x40 +#define SNS0_BUS_OUT_CHECK 0x20 +#define SNS0_EQUIPMENT_CHECK 0x10 +#define SNS0_DATA_CHECK 0x08 +#define SNS0_OVERRUN 0x04 + +/* + * operation request block + */ +typedef struct { + unsigned long intparm; /* interruption parameter */ + unsigned int key : 4; /* flags, like key, suspend control, etc. */ + unsigned int spnd : 1; /* suspend control */ + unsigned int res1 : 3; /* reserved */ + unsigned int fmt : 1; /* format control */ + unsigned int pfch : 1; /* prefetch control */ + unsigned int isic : 1; /* initial-status-interruption control */ + unsigned int alcc : 1; /* address-limit-checking control */ + unsigned int ssic : 1; /* suppress-suspended-interr. control */ + unsigned int res2 : 3; /* reserved */ + unsigned int lpm : 8; /* logical path mask */ + unsigned int ils : 1; /* incorrect length */ + unsigned int zero : 7; /* reserved zeros */ + ccw1_t *cpa; /* channel program address */ + } __attribute__ ((packed,aligned(4))) orb_t; + +typedef struct { + unsigned int res0 : 4; /* reserved */ + unsigned int pvrf : 1; /* path-verification-required flag */ + unsigned int cpt : 1; /* channel-path timeout */ + unsigned int fsavf : 1; /* Failing storage address validity flag */ + unsigned int cons : 1; /* concurrent-sense */ + unsigned int res8 : 2; /* reserved */ + unsigned int scnt : 6; /* sense count if cons == 1 */ + unsigned int res16 : 16; /* reserved */ + } erw_t; + +/* + * subchannel logout area + */ +typedef struct { + unsigned int res0 : 1; /* reserved */ + unsigned int esf : 7; /* extended status flags */ + unsigned int lpum : 8; /* last path used mask */ + unsigned int res16 : 1; /* reserved */ + unsigned int fvf : 5; /* field-validity flags */ + unsigned int sacc : 2; /* storage access code */ + unsigned int termc : 2; /* termination code */ + unsigned int devsc : 1; /* device-status check */ + unsigned int serr : 1; /* secondary error */ + unsigned int ioerr : 1; /* i/o-error alert */ + unsigned int seqc : 3; /* sequence code */ + } sublog_t ; + +/* + * Format 0 Extended Status Word (ESW) + */ +typedef struct { + sublog_t sublog; /* subchannel logout */ + erw_t erw; /* extended report word */ + void *faddr; /* failing address */ + unsigned int zeros[2]; /* 2 fullwords of zeros */ + } esw0_t; + +/* + * Format 1 Extended Status Word (ESW) + */ +typedef struct { + unsigned char zero0; /* reserved zeros */ + unsigned char lpum; /* last path used mask */ + unsigned short zero16; /* reserved zeros */ + erw_t erw; /* extended report word */ + unsigned int zeros[3]; /* 2 fullwords of zeros */ + } esw1_t; + +/* + * Format 2 Extended Status Word (ESW) + */ +typedef struct { + unsigned char zero0; /* reserved zeros */ + unsigned char lpum; /* last path used mask */ + unsigned short dcti; /* device-connect-time interval */ + erw_t erw; /* extended report word */ + unsigned int zeros[3]; /* 2 fullwords of zeros */ + } esw2_t; + +/* + * Format 3 Extended Status Word (ESW) + */ +typedef struct { + unsigned char zero0; /* reserved zeros */ + unsigned char lpum; /* last path used mask */ + unsigned short res; /* reserved */ + erw_t erw; /* extended report word */ + unsigned int zeros[3]; /* 2 fullwords of zeros */ + } esw3_t; + +typedef union { + esw0_t esw0; + esw1_t esw1; + esw2_t esw2; + esw3_t esw3; + } esw_t; + +/* + * interruption response block + */ +typedef struct { + scsw_t scsw; /* subchannel status word */ + esw_t esw; /* extended status word */ + char ecw[32]; /* extended control word */ + } irb_t __attribute__ ((aligned(4))); + +/* + * TPI info structure + */ +typedef struct { + unsigned int res : 16; /* reserved 0x00000001 */ + unsigned int irq : 16; /* aka. subchannel number */ + unsigned int intparm; /* interruption parameter */ + } tpi_info_t; + + +/* + * This is the "IRQ descriptor", which contains various information + * about the irq, including what kind of hardware handling it has, + * whether it is disabled etc etc. + * + * Pad this out to 32 bytes for cache and indexing reasons. + */ +typedef struct { + unsigned int status; /* IRQ status - IRQ_INPROGRESS, IRQ_DISABLED */ + struct hw_interrupt_type *handler; /* handle/enable/disable functions */ + struct irqaction *action; /* IRQ action list */ + unsigned int unused[3]; + spinlock_t irq_lock; + } irq_desc_t; + +// +// command information word (CIW) layout +// +typedef struct _ciw { + unsigned int et : 2; // entry type + unsigned int reserved : 2; // reserved + unsigned int ct : 4; // command type + unsigned int cmd : 8; // command + unsigned int count : 16; // count + } ciw_t; + +#define CIW_TYPE_RCD 0x0 // read configuration data +#define CIW_TYPE_SII 0x1 // set interface identifier +#define CIW_TYPE_RNI 0x2 // read node identifier + +// +// sense-id response buffer layout +// +typedef struct { + /* common part */ + unsigned char reserved; /* always 0x'FF' */ + unsigned short cu_type; /* control unit type */ + unsigned char cu_model; /* control unit model */ + unsigned short dev_type; /* device type */ + unsigned char dev_model; /* device model */ + unsigned char unused; /* padding byte */ + /* extended part */ + ciw_t ciw[62]; /* variable # of CIWs */ + } __attribute__ ((packed,aligned(4))) senseid_t; + +/* + * sense data + */ +typedef struct { + unsigned char res[32]; /* reserved */ + unsigned char data[32]; /* sense data */ + } sense_t; + +/* + * device status area, to be provided by the device driver + * when calling request_irq() as parameter "dev_id", later + * tied to the "action" control block. + * + * Note : No data area must be added after union ii or the + * effective devstat size calculation will fail ! + */ +typedef struct { + unsigned int devno; /* device number, aka. "cuu" from irb */ + unsigned int intparm; /* interrupt parameter */ + unsigned char cstat; /* channel status - accumulated */ + unsigned char dstat; /* device status - accumulated */ + unsigned char lpum; /* last path used mask from irb */ + unsigned char unused; /* not used - reserved */ + unsigned int flag; /* flag : see below */ + unsigned long cpa; /* CCW address from irb at primary status */ + unsigned int rescnt; /* res. count from irb at primary status */ + unsigned int scnt; /* sense count, if DEVSTAT_FLAG_SENSE_AVAIL */ + union { + irb_t irb; /* interruption response block */ + sense_t sense; /* sense information */ + } ii; /* interrupt information */ + } devstat_t; + +#define DEVSTAT_FLAG_SENSE_AVAIL 0x00000001 +#define DEVSTAT_NOT_OPER 0x00000002 +#define DEVSTAT_START_FUNCTION 0x00000004 +#define DEVSTAT_HALT_FUNCTION 0x00000008 +#define DEVSTAT_STATUS_PENDING 0x00000010 +#define DEVSTAT_FINAL_STATUS 0x80000000 + +/* + * Flags used as input parameters for do_IO() + */ +#define DOIO_EARLY_NOTIFICATION 0x01 /* allow for I/O completion ... */ + /* ... notification after ... */ + /* ... primary interrupt status */ +#define DOIO_RETURN_CHAN_END DOIO_EARLY_NOTIFICATION +#define DOIO_VALID_LPM 0x02 /* LPM input parameter is valid */ +#define DOIO_WAIT_FOR_INTERRUPT 0x04 /* wait synchronously for interrupt */ +#define DOIO_REPORT_ALL 0x08 /* report all interrupt conditions */ +#define DOIO_ALLOW_SUSPEND 0x10 /* allow for channel prog. suspend */ +#define DOIO_DENY_PREFETCH 0x20 /* don't allow for CCW prefetch */ +#define DOIO_SUPPRESS_INTER 0x40 /* suppress intermediate inter. */ + /* ... for suspended CCWs */ + +/* + * do_IO() + * + * Start a S/390 channel program. When the interrupt arrives + * handle_IRQ_event() is called, which eventually calls the + * IRQ handler, either immediately, delayed (dev-end missing, + * or sense required) or never (no IRQ handler registered - + * should never occur, as the IRQ (subchannel ID) should be + * disabled if no handler is present. Depending on the action + * taken, do_IO() returns : 0 - Success + * -EIO - Status pending + * see : action->dev_id->cstat + * action->dev_id->dstat + * -EBUSY - Device busy + * -ENODEV - Device not operational + */ +int do_IO( int irq, /* IRQ aka. subchannel number */ + ccw1_t *cpa, /* logical channel program address */ + unsigned long initparm, /* interruption parameter */ + unsigned char lpm, /* logical path mask */ + unsigned long flag); /* flags : see above */ + +int start_IO( int irq, /* IRQ aka. subchannel number */ + ccw1_t *cpa, /* logical channel program address */ + unsigned long intparm, /* interruption parameter */ + unsigned char lpm, /* logical path mask */ + unsigned long flag); /* flags : see above */ + +int resume_IO( int irq); /* IRQ aka. subchannel number */ + +int halt_IO( int irq, /* IRQ aka. subchannel number */ + int intparm, /* dummy intparm */ + unsigned int flag); /* possible DOIO_WAIT_FOR_INTERRUPT */ + + +int process_IRQ( struct pt_regs regs, + unsigned int irq, + unsigned int intparm); + + +int enable_cpu_sync_isc ( int irq ); +int disable_cpu_sync_isc( int irq ); + +typedef struct { + int irq; /* irq, aka. subchannel */ + unsigned int devno; /* device number */ + unsigned int status; /* device status */ + senseid_t sid_data; /* senseID data */ + } dev_info_t; + +int get_dev_info( int irq, dev_info_t *); /* to be eliminated - don't use */ + +int get_dev_info_by_irq ( int irq, dev_info_t *pdi); +int get_dev_info_by_devno( unsigned int devno, dev_info_t *pdi); + +int get_irq_by_devno( unsigned int devno ); +unsigned int get_devno_by_irq( int irq ); + +int read_dev_chars( int irq, void **buffer, int length ); +int read_conf_data( int irq, void **buffer, int *length ); + +extern senseid_t senseid[NR_IRQS]; + +extern irq_desc_t irq_desc[NR_IRQS]; + +extern int handle_IRQ_event(unsigned int, int cpu, struct pt_regs *); + +extern int set_cons_dev(int irq); +extern int reset_cons_dev(int irq); +extern int wait_cons_dev(int irq); + +/* + * Some S390 specific IO instructions as inline + */ + +extern __inline__ int stsch(int irq, volatile schib_t *addr) +{ + int ccode; + + __asm__ __volatile__( + "LR 1,%1\n\t" + "STSCH 0(%2)\n\t" + "IPM %0\n\t" + "SRL %0,28\n\t" + : "=d" (ccode) : "r" (irq | 0x10000L), "a" (addr) + : "cc", "1" ); + return ccode; +} + +extern __inline__ int msch(int irq, volatile schib_t *addr) +{ + int ccode; + + __asm__ __volatile__( + "LR 1,%1\n\t" + "MSCH 0(%2)\n\t" + "IPM %0\n\t" + "SRL %0,28\n\t" + : "=d" (ccode) : "r" (irq | 0x10000L), "a" (addr) + : "cc", "1" ); + return ccode; +} + +extern __inline__ int msch_err(int irq, volatile schib_t *addr) +{ + int ccode; + + __asm__ __volatile__( + " lr 1,%1\n" + " msch 0(%2)\n" + "0: ipm %0\n" + " srl %0,28\n" + "1:\n" + ".section .fixup,\"ax\"\n" + "2: l %0,%3\n" + " bras 1,3f\n" + " .long 1b\n" + "3: l 1,0(1)\n" + " br 1\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .long 0b,2b\n" + ".previous" + : "=d" (ccode) + : "r" (irq | 0x10000L), "a" (addr), "i" (__LC_PGM_ILC) + : "cc", "1" ); + return ccode; +} + +extern __inline__ int tsch(int irq, volatile irb_t *addr) +{ + int ccode; + + __asm__ __volatile__( + "LR 1,%1\n\t" + "TSCH 0(%2)\n\t" + "IPM %0\n\t" + "SRL %0,28\n\t" + : "=d" (ccode) : "r" (irq | 0x10000L), "a" (addr) + : "cc", "1" ); + return ccode; +} + +extern __inline__ int tpi( volatile tpi_info_t *addr) +{ + int ccode; + + __asm__ __volatile__( + "TPI 0(%1)\n\t" + "IPM %0\n\t" + "SRL %0,28\n\t" + : "=d" (ccode) : "a" (addr) + : "cc", "1" ); + return ccode; +} + +extern __inline__ int ssch(int irq, volatile orb_t *addr) +{ + int ccode; + + __asm__ __volatile__( + "LR 1,%1\n\t" + "SSCH 0(%2)\n\t" + "IPM %0\n\t" + "SRL %0,28\n\t" + : "=d" (ccode) : "r" (irq | 0x10000L), "a" (addr) + : "cc", "1" ); + return ccode; +} + +extern __inline__ int rsch(int irq) +{ + int ccode; + + __asm__ __volatile__( + "LR 1,%1\n\t" + "RSCH\n\t" + "IPM %0\n\t" + "SRL %0,28\n\t" + : "=d" (ccode) : "r" (irq | 0x10000L) + : "cc", "1" ); + return ccode; +} + +extern __inline__ int csch(int irq) +{ + int ccode; + + __asm__ __volatile__( + "LR 1,%1\n\t" + "CSCH\n\t" + "IPM %0\n\t" + "SRL %0,28\n\t" + : "=d" (ccode) : "r" (irq | 0x10000L) + : "cc", "1" ); + return ccode; +} + +extern __inline__ int hsch(int irq) +{ + int ccode; + + __asm__ __volatile__( + "LR 1,%1\n\t" + "HSCH\n\t" + "IPM %0\n\t" + "SRL %0,28\n\t" + : "=d" (ccode) : "r" (irq | 0x10000L) + : "cc", "1" ); + return ccode; +} + +extern __inline__ int iac( void) +{ + int ccode; + + __asm__ __volatile__( + "IAC %0\n\t" + "IPM %0\n\t" + "SRL %0,28\n\t" + : "=d" (ccode) : : "cc"); + return ccode; +} + +typedef struct { + unsigned int vrdcdvno : 16; /* device number (input) */ + unsigned int vrdclen : 16; /* data block length (input) */ + unsigned int vrdcvcla : 8; /* virtual device class (output) */ + unsigned int vrdcvtyp : 8; /* virtual device type (output) */ + unsigned int vrdcvsta : 8; /* virtual device status (output) */ + unsigned int vrdcvfla : 8; /* virtual device flags (output) */ + unsigned int vrdcrccl : 8; /* real device class (output) */ + unsigned int vrdccrty : 8; /* real device type (output) */ + unsigned int vrdccrmd : 8; /* real device model (output) */ + unsigned int vrdccrft : 8; /* real device feature (output) */ + } __attribute__ ((packed,aligned(4))) diag210_t; + +void VM_virtual_device_info( int devno, /* device number */ + senseid_t *ps ); /* ptr to senseID data */ + +extern __inline__ int diag210( diag210_t * addr) +{ + int ccode; + + __asm__ __volatile__( + "LR 1,%1\n\t" + ".long 0x83110210\n\t" + "IPM %0\n\t" + "SRL %0,28\n\t" + : "=d" (ccode) : "a" (addr) + : "cc", "1" ); + return ccode; +} + +/* + * Various low-level irq details needed by irq.c, process.c, + * time.c, io_apic.c and smp.c + * + * Interrupt entry/exit code at both C and assembly level + */ + +void mask_irq(unsigned int irq); +void unmask_irq(unsigned int irq); + +#define MAX_IRQ_SOURCES 128 + +extern spinlock_t irq_controller_lock; + +#ifdef __SMP__ + +#include + +static inline void irq_enter(int cpu, unsigned int irq) +{ + hardirq_enter(cpu); + while (test_bit(0,&global_irq_lock)) { + eieio(); + } +} + +static inline void irq_exit(int cpu, unsigned int irq) +{ + hardirq_exit(cpu); + release_irqlock(cpu); +} + + +#else + +#define irq_enter(cpu, irq) (++local_irq_count[cpu]) +#define irq_exit(cpu, irq) (--local_irq_count[cpu]) + +#endif + +#define __STR(x) #x +#define STR(x) __STR(x) + +#ifdef __SMP__ + +/* + * SMP has a few special interrupts for IPI messages + */ + +#endif /* __SMP__ */ + +/* + * x86 profiling function, SMP safe. We might want to do this in + * assembly totally? + */ +static inline void s390_do_profile (unsigned long addr) +{ +#if 0 + if (prof_buffer && current->pid) { + addr -= (unsigned long) &_stext; + addr >>= prof_shift; + /* + * Don't ignore out-of-bounds EIP values silently, + * put them into the last histogram slot, so if + * present, they will show up as a sharp peak. + */ + if (addr > prof_len-1) + addr = prof_len-1; + atomic_inc((atomic_t *)&prof_buffer[addr]); + } +#endif +} + +#define s390irq_spin_lock(irq) \ + spin_lock(&(irq_desc[irq].irq_lock)) + +#define s390irq_spin_unlock(irq) \ + spin_unlock(&(irq_desc[irq].irq_lock)) + +#define s390irq_spin_lock_irqsave(irq,flags) \ + spin_lock_irqsave(&(irq_desc[irq].irq_lock), flags) +#define s390irq_spin_unlock_irqrestore(irq,flags) \ + spin_unlock_irqrestore(&(irq_desc[irq].irq_lock), flags) +#endif + diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/irqextras390.c linux/arch/s390/kernel/irqextras390.c --- v2.2.13/linux/arch/s390/kernel/irqextras390.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/irqextras390.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,35 @@ +/* + * arch/s390/kernel/irqextras390.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + * + * Some channel code by D.J. Barrow + */ + +/* + +*/ +#include +#include + +#if 0 +// fixchannelprogram is now obselete +void fixchannelprogram(orb_bits_t *orbptr) +{ + __u32 newAddress=orbptr->ccw_program_address; + fixccws(orbptr->ccw_program_address); + orbptr->ccw_program_address=newAddress; + orbptr->ccw_program_address=(ccw1_t *)(((__u32)orbptr->ccw_program_address)); +} +#endif + +void fixccws(ccw1_bits_t *ccwptr) +{ + for(;;ccwptr++) + { // Just hope nobody starts doing prefixing + if(!ccwptr->cc) + break; + } +} diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/lowcore.S linux/arch/s390/kernel/lowcore.S --- v2.2.13/linux/arch/s390/kernel/lowcore.S Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/lowcore.S Tue Jan 4 10:12:12 2000 @@ -0,0 +1,60 @@ +/* + * arch/s390/kernel/lowcore.S + * S390 lowcore definition. + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hp@de.ibm.com) + * Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +#include + .align 4096 + .globl init_S390_lowcore +init_S390_lowcore: + .long _RESTART_PSW_MASK + .long restart_int_handler + _ADDR_31 + .long 0,0 + .long 0,0 +EXT_OLD: .long 0,0 +SVC_OLD: .long 0,0 +PGM_OLD: .long 0,0 +MCCK_OLD:.long 0,0 +IO_OLD: .long 0,0 + .long 0,0,0,0,0,0 +# +# new psws need all to be physical +# because we start with dat off +# +EXT_PSW: .long _EXT_PSW_MASK + .long ext_int_handler + _ADDR_31 +# +SVC_PSW: .long _SVC_PSW_MASK + .long system_call + _ADDR_31 +# +PGM_PSW: .long _PGM_PSW_MASK + .long pgm_check_handler + _ADDR_31 +# +MCCK_PSW:.long _MCCK_PSW_MASK + .long mcck_int_handler + _ADDR_31 +# +IO_PSW: .long _IO_PSW_MASK + .long io_int_handler + _ADDR_31 +# +# +# +EXTERNAL_PARAMETER: .long 0 +CPU_ADDRESS: .word 0 +EXT_INTERRUPT_CODE: .word 0 +SVC_ILC: .word 0 +SVC_CODE: .word 0 +PGM_ILC: .word 0 +PGM_CODE: .word 0 +TRANS_EXC_ADDR: .long 0 # 090 + .fill 0xC00-0x094,1,0 +SAVE_AREA: .fill 0x40,1,0 # C00 +KERNEL_STACK: .long 0 # C40 +KERNEL_LEVEL: .long 0 # C44 +CPUID: .long 0,0 # C48 + .fill 0x1000-0xC50,1,0 + diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/mathemu.c linux/arch/s390/kernel/mathemu.c --- v2.2.13/linux/arch/s390/kernel/mathemu.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/mathemu.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,895 @@ +/* + * arch/s390/kernel/mathemu.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * + * 'mathemu.c' handles IEEE instructions on a S390 processor + * that does not have the IEEE fpu + */ + +#include +#include +#include +#include + +#include +#include + +static void set_CC_df(__u64 val1,__u64 val2) { + int rc; + rc = __cmpdf2(val1,val2); + current->tss.regs->psw.mask &= 0xFFFFCFFF; + switch (rc) { + case -1: + current->tss.regs->psw.mask |= 0x00001000; + break; + case 1: + current->tss.regs->psw.mask |= 0x00002000; + break; + } +} + +static void set_CC_sf(__u32 val1,__u32 val2) { + int rc; + rc = __cmpsf2(val1,val2); + current->tss.regs->psw.mask &= 0xFFFFCFFF; + switch (rc) { + case -1: + current->tss.regs->psw.mask |= 0x00001000; + break; + case 1: + current->tss.regs->psw.mask |= 0x00002000; + break; + } +} + + +static void emu_adb (int rx, __u64 val) { + current->tss.fp_regs.fprs[rx].d = __adddf3(current->tss.fp_regs.fprs[rx].d,val); + set_CC_df(current->tss.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_adbr (int rx, int ry) { + current->tss.fp_regs.fprs[rx].d = __adddf3(current->tss.fp_regs.fprs[rx].d, + current->tss.fp_regs.fprs[ry].d); + set_CC_df(current->tss.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_aeb (int rx, __u32 val) { + current->tss.fp_regs.fprs[rx].f = __addsf3(current->tss.fp_regs.fprs[rx].f,val); + set_CC_sf(current->tss.fp_regs.fprs[rx].f,0); +} + +static void emu_aebr (int rx, int ry) { + current->tss.fp_regs.fprs[rx].f = __addsf3(current->tss.fp_regs.fprs[rx].f, + current->tss.fp_regs.fprs[ry].f); + set_CC_sf(current->tss.fp_regs.fprs[rx].f,0); +} + +static void emu_axbr (int rx, int ry) { + printk("axbr emulation not implemented!\n"); +} + +static void emu_cdb (int rx, __u64 val) { + set_CC_df(current->tss.fp_regs.fprs[rx].d,val); +} + +static void emu_cdbr (int rx, int ry) { + set_CC_df(current->tss.fp_regs.fprs[rx].d,current->tss.fp_regs.fprs[ry].d); +} + +static void emu_cdfbr (int rx, int ry) { + current->tss.fp_regs.fprs[rx].d = + __floatsidf(current->tss.regs->gprs[ry]); +} + +static void emu_ceb (int rx, __u32 val) { + set_CC_sf(current->tss.fp_regs.fprs[rx].f,val); +} + +static void emu_cebr (int rx, int ry) { + set_CC_sf(current->tss.fp_regs.fprs[rx].f,current->tss.fp_regs.fprs[ry].f); +} + +static void emu_cefbr (int rx, int ry) { + current->tss.fp_regs.fprs[rx].f = + __floatsisf(current->tss.regs->gprs[ry]); +} + +static void emu_cfdbr (int rx, int ry, int mask) { + current->tss.regs->gprs[rx] = + __fixdfsi(current->tss.fp_regs.fprs[ry].d); +} + +static void emu_cfebr (int rx, int ry, int mask) { + current->tss.regs->gprs[rx] = + __fixsfsi(current->tss.fp_regs.fprs[ry].f); +} + +static void emu_cfxbr (int rx, int ry, int mask) { + printk("cfxbr emulation not implemented!\n"); +} + +static void emu_cxbr (int rx, int ry) { + printk("cxbr emulation not implemented!\n"); +} + +static void emu_cxfbr (int rx, int ry) { + printk("cxfbr emulation not implemented!\n"); +} + +static void emu_ddb (int rx, __u64 val) { + current->tss.fp_regs.fprs[rx].d = __divdf3(current->tss.fp_regs.fprs[rx].d,val); + set_CC_df(current->tss.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_ddbr (int rx, int ry) { + current->tss.fp_regs.fprs[rx].d = __divdf3(current->tss.fp_regs.fprs[rx].d, + current->tss.fp_regs.fprs[ry].d); + set_CC_df(current->tss.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_deb (int rx, __u32 val) { + current->tss.fp_regs.fprs[rx].f = __divsf3(current->tss.fp_regs.fprs[rx].f,val); + set_CC_sf(current->tss.fp_regs.fprs[rx].f,0); +} + +static void emu_debr (int rx, int ry) { + current->tss.fp_regs.fprs[rx].f = __divsf3(current->tss.fp_regs.fprs[rx].f, + current->tss.fp_regs.fprs[ry].f); + set_CC_sf(current->tss.fp_regs.fprs[rx].f,0); +} + +static void emu_didbr (int rx, int ry, int mask) { + printk("didbr emulation not implemented!\n"); +} + +static void emu_diebr (int rx, int ry, int mask) { + printk("diebr emulation not implemented!\n"); +} + +static void emu_dxbr (int rx, int ry) { + printk("dxbr emulation not implemented!\n"); +} + +static void emu_efpc (int rx, int ry) { + printk("efpc emulation not implemented!\n"); +} + +static void emu_fidbr (int rx, int ry, int mask) { + printk("fidbr emulation not implemented!\n"); +} + +static void emu_fiebr (int rx, int ry, int mask) { + printk("fiebr emulation not implemented!\n"); +} + +static void emu_fixbr (int rx, int ry, int mask) { + printk("fixbr emulation not implemented!\n"); +} + +static void emu_kdb (int rx, __u64 val) { + printk("kdb emulation not implemented!\n"); +} + +static void emu_kdbr (int rx, int ry) { + printk("kdbr emulation not implemented!\n"); +} + +static void emu_keb (int rx, __u32 val) { + printk("keb emulation not implemented!\n"); +} + +static void emu_kebr (int rx, int ry) { + printk("kebr emulation not implemented!\n"); +} + +static void emu_kxbr (int rx, int ry) { + printk("kxbr emulation not implemented!\n"); +} + +static void emu_lcdbr (int rx, int ry) { + current->tss.fp_regs.fprs[rx].d = + __negdf2(current->tss.fp_regs.fprs[ry].d); + set_CC_df(current->tss.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_lcebr (int rx, int ry) { + current->tss.fp_regs.fprs[rx].f = + __negsf2(current->tss.fp_regs.fprs[ry].f); + set_CC_sf(current->tss.fp_regs.fprs[rx].f,0); +} + +static void emu_lcxbr (int rx, int ry) { + printk("lcxbr emulation not implemented!\n"); +} + +static void emu_ldeb (int rx, __u32 val) { + current->tss.fp_regs.fprs[rx].d = __extendsfdf2(val); +} + +static void emu_ldebr (int rx, int ry) { + current->tss.fp_regs.fprs[rx].d = + __extendsfdf2(current->tss.fp_regs.fprs[ry].f); +} + +static void emu_ldxbr (int rx, int ry) { + printk("ldxbr emulation not implemented!\n"); +} + +static void emu_ledbr (int rx, int ry) { + current->tss.fp_regs.fprs[rx].f = __truncdfsf2(current->tss.fp_regs.fprs[ry].d); + set_CC_sf(current->tss.fp_regs.fprs[rx].f,0); +} + +static void emu_lexbr (int rx, int ry) { + printk("lexbr emulation not implemented!\n"); +} + +static void emu_lndbr (int rx, int ry) { + printk("lndbr emulation not implemented!\n"); +} + +static void emu_lnebr (int rx, int ry) { + printk("lnebr emulation not implemented!\n"); +} + +static void emu_lnxbr (int rx, int ry) { + printk("lnxbr emulation not implemented!\n"); +} + +static void emu_lpdbr (int rx, int ry) { + current->tss.fp_regs.fprs[rx].d = __absdf2(current->tss.fp_regs.fprs[ry].d); + set_CC_df(current->tss.fp_regs.fprs[rx].d,0); +} + +static void emu_lpebr (int rx, int ry) { + current->tss.fp_regs.fprs[rx].f = __abssf2(current->tss.fp_regs.fprs[ry].f); + set_CC_sf(current->tss.fp_regs.fprs[rx].f,0); +} + +static void emu_lpxbr (int rx, int ry) { + printk("lpxbr emulation not implemented!\n"); +} + +static void emu_ltdbr (int rx, int ry) { + current->tss.fp_regs.fprs[rx].d = current->tss.fp_regs.fprs[ry].d; + set_CC_df(current->tss.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_ltebr (int rx, int ry) { + current->tss.fp_regs.fprs[rx].f = current->tss.fp_regs.fprs[ry].f; + set_CC_sf(current->tss.fp_regs.fprs[rx].f,0); +} + +static void emu_ltxbr (int rx, int ry) { + printk("ltxbr emulation not implemented!\n"); +} + +static void emu_lxdb (int rx, __u64 val) { + printk("lxdb emulation not implemented!\n"); +} + +static void emu_lxdbr (int rx, int ry) { + printk("lxdbr emulation not implemented!\n"); +} + +static void emu_lxeb (int rx, __u32 val) { + printk("lxeb emulation not implemented!\n"); +} + +static void emu_lxebr (int rx, int ry) { + printk("lxebr emulation not implemented!\n"); +} + +static void emu_madb (int rx, __u64 val, int mask) { + printk("madb emulation not implemented!\n"); +} + +static void emu_madbr (int rx, int ry, int mask) { + printk(" emulation not implemented!\n"); +} + +static void emu_maeb (int rx, __u32 val, int mask) { + printk("maeb emulation not implemented!\n"); +} + +static void emu_maebr (int rx, int ry, int mask) { + printk("maebr emulation not implemented!\n"); +} + +static void emu_mdb (int rx, __u64 val) { + current->tss.fp_regs.fprs[rx].d = __muldf3(current->tss.fp_regs.fprs[rx].d,val); + set_CC_df(current->tss.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_mdbr (int rx, int ry) { + current->tss.fp_regs.fprs[rx].d = __muldf3(current->tss.fp_regs.fprs[rx].d, + current->tss.fp_regs.fprs[ry].d); + set_CC_df(current->tss.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_mdeb (int rx, __u32 val) { + printk("mdeb emulation not implemented!\n"); +} + +static void emu_mdebr (int rx, int ry) { + printk("mdebr emulation not implemented!\n"); +} + +static void emu_meeb (int rx, __u32 val) { + current->tss.fp_regs.fprs[rx].f = __mulsf3(current->tss.fp_regs.fprs[rx].f, + val); + set_CC_sf(current->tss.fp_regs.fprs[rx].f,0); +} + +static void emu_meebr (int rx, int ry) { + current->tss.fp_regs.fprs[rx].f = __mulsf3(current->tss.fp_regs.fprs[rx].f, + current->tss.fp_regs.fprs[ry].f); + set_CC_sf(current->tss.fp_regs.fprs[rx].f,0); +} + +static void emu_msdb (int rx, __u64 val, int mask) { + printk("msdb emulation not implemented!\n"); +} + +static void emu_msdbr (int rx, int ry, int mask) { + printk("msdbr emulation not implemented!\n"); +} + +static void emu_mseb (int rx, __u32 val, int mask) { + printk("mseb emulation not implemented!\n"); +} + +static void emu_msebr (int rx, int ry, int mask) { + printk("msebr emulation not implemented!\n"); +} + +static void emu_mxbr (int rx, int ry) { + printk("mxbr emulation not implemented!\n"); +} + +static void emu_mxdb (int rx, __u64 val) { + printk("mxdb emulation not implemented!\n"); +} + +static void emu_mxdbr (int rx, int ry) { + printk("mxdbr emulation not implemented!\n"); +} + +static void emu_sdb (int rx, __u64 val) { + current->tss.fp_regs.fprs[rx].d = __subdf3(current->tss.fp_regs.fprs[rx].d, + val); + set_CC_sf(current->tss.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_sdbr (int rx, int ry) { + current->tss.fp_regs.fprs[rx].d = __subdf3(current->tss.fp_regs.fprs[rx].d, + current->tss.fp_regs.fprs[ry].d); + set_CC_sf(current->tss.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_seb (int rx, __u32 val) { + current->tss.fp_regs.fprs[rx].f = __subsf3(current->tss.fp_regs.fprs[rx].f, + val); + set_CC_sf(current->tss.fp_regs.fprs[rx].f,0); +} + +static void emu_sebr (int rx, int ry) { + current->tss.fp_regs.fprs[rx].f = __subsf3(current->tss.fp_regs.fprs[rx].f, + current->tss.fp_regs.fprs[ry].f); + set_CC_sf(current->tss.fp_regs.fprs[rx].f,0); +} + +static void emu_sfpc (int rx, int ry) { + printk("sfpc emulation not implemented!\n"); +} + +static void emu_sqdb (int rx, __u64 val) { + printk("sqdb emulation not implemented!\n"); +} + +static void emu_sqdbr (int rx, int ry) { + printk("sqdbr emulation not implemented!\n"); +} + +static void emu_sqeb (int rx, __u32 val) { + printk("sqeb emulation not implemented!\n"); +} + +static void emu_sqebr (int rx, int ry) { + printk("sqebr emulation not implemented!\n"); +} + +static void emu_sqxbr (int rx, int ry) { + printk("sqxbr emulation not implemented!\n"); +} + +static void emu_sxbr (int rx, int ry) { + printk("sxbr emulation not implemented!\n"); +} + +static void emu_tcdb (int rx, __u64 val) { + printk("tcdb emulation not implemented!\n"); +} + +static void emu_tceb (int rx, __u32 val) { + printk("tceb emulation not implemented!\n"); +} + +static void emu_tcxb (int rx, __u64 val) { + printk("tcxb emulation not implemented!\n"); +} + + +static inline void emu_load_regd(int reg) { + if ((reg&9) == 0) { /* test if reg in {0,2,4,6} */ + __asm__ __volatile ( /* load reg from fp_regs.fprs[reg] */ + " bras 1,0f\n" + " ld 0,0(%1)\n" + "0: ex %0,0(1)" + : /* no output */ + : "a" (reg<<4), "a" (¤t->tss.fp_regs.fprs[reg].d) + : "1" ); + } +} + +static inline void emu_load_rege(int reg) { + if ((reg&9) == 0) { /* test if reg in {0,2,4,6} */ + __asm__ __volatile ( /* load reg from fp_regs.fprs[reg] */ + " bras 1,0f\n" + " le 0,0(%1)\n" + "0: ex %0,0(1)" + : /* no output */ + : "a" (reg<<4), "a" (¤t->tss.fp_regs.fprs[reg].f) + : "1" ); + } +} + +static inline void emu_store_regd(int reg) { + if ((reg&9) == 0) { /* test if reg in {0,2,4,6} */ + __asm__ __volatile ( /* store reg to fp_regs.fprs[reg] */ + " bras 1,0f\n" + " std 0,0(%1)\n" + "0: ex %0,0(1)" + : /* no output */ + : "a" (reg<<4), "a" (¤t->tss.fp_regs.fprs[reg].d) + : "1" ); + } +} + + +static inline void emu_store_rege(int reg) { + if ((reg&9) == 0) { /* test if reg in {0,2,4,6} */ + __asm__ __volatile ( /* store reg to fp_regs.fprs[reg] */ + " bras 1,0f\n" + " ste 0,0(%1)\n" + "0: ex %0,0(1)" + : /* no output */ + : "a" (reg<<4), "a" (¤t->tss.fp_regs.fprs[reg].f) + : "1" ); + } +} + +int math_emu_b3(__u8 *opcode, struct pt_regs * regs) { + static const __u8 format_table[] = { + 2, 2, 2, 2, 9, 1, 2, 1, 2, 2, 2, 2, 9, 2, 4, 4, + 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1,10, 1, 1, 3, 1, 1, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 3, 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 5, 6, 6, 0, 7, 8, 8, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + static const void *jump_table[]= { + emu_lpebr, emu_lnebr, emu_ltebr, emu_lcebr, + emu_ldebr, emu_lxdbr, emu_lxebr, emu_mxdbr, + emu_kebr, emu_cebr, emu_aebr, emu_sebr, + emu_mdebr, emu_debr, emu_maebr, emu_msebr, + emu_lpdbr, emu_lndbr, emu_ltdbr, emu_lcdbr, + emu_sqebr, emu_sqdbr, emu_sqxbr, emu_meebr, + emu_kdbr, emu_cdbr, emu_adbr, emu_sdbr, + emu_mdbr, emu_ddbr, emu_madbr, emu_msdbr, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + emu_lpxbr, emu_lnxbr, emu_ltxbr, emu_lcxbr, + emu_ledbr, emu_ldxbr, emu_lexbr, emu_fixbr, + emu_kxbr, emu_cxbr, emu_axbr, emu_sxbr, + emu_mxbr, emu_dxbr, NULL, NULL, + NULL, NULL, NULL, emu_diebr, + NULL, NULL, NULL, emu_fiebr, + NULL, NULL, NULL, emu_didbr, + NULL, NULL, NULL, emu_fidbr, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + emu_sfpc, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + emu_efpc, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + emu_cefbr, emu_cdfbr, emu_cxfbr, NULL, + emu_cfebr, emu_cfdbr, emu_cfxbr + }; + + switch (format_table[opcode[1]]) { + case 1: /* RRE format, double operation */ + emu_store_regd((opcode[3]>>4)&15); + emu_store_regd(opcode[3]&15); + /* call the emulation function */ + ((void (*)(int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15); + emu_load_regd((opcode[3]>>4)&15); + emu_load_regd(opcode[3]&15); + return 0; + case 2: /* RRE format, float operation */ + emu_store_rege((opcode[3]>>4)&15); + emu_store_rege(opcode[3]&15); + /* call the emulation function */ + ((void (*)(int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15); + emu_load_rege((opcode[3]>>4)&15); + emu_load_rege(opcode[3]&15); + return 0; + case 3: /* RRF format, double operation */ + emu_store_regd((opcode[3]>>4)&15); + emu_store_regd(opcode[3]&15); + /* call the emulation function */ + ((void (*)(int, int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15,opcode[2]>>4); + emu_load_regd((opcode[3]>>4)&15); + emu_load_regd(opcode[3]&15); + return 0; + case 4: /* RRF format, float operation */ + emu_store_rege((opcode[3]>>4)&15); + emu_store_rege(opcode[3]&15); + /* call the emulation function */ + ((void (*)(int, int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15,opcode[2]>>4); + emu_load_rege((opcode[3]>>4)&15); + emu_load_rege(opcode[3]&15); + return 0; + case 5: /* RRE format, cefbr instruction */ + emu_store_rege((opcode[3]>>4)&15); + /* call the emulation function */ + ((void (*)(int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15); + emu_load_rege((opcode[3]>>4)&15); + return 0; + case 6: /* RRE format, cdfbr & cxfbr instruction */ + emu_store_regd((opcode[3]>>4)&15); + /* call the emulation function */ + ((void (*)(int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15); + emu_load_regd((opcode[3]>>4)&15); + return 0; + /* FIXME !! */ + return 0; + case 7: /* RRF format, cfebr instruction */ + emu_store_rege(opcode[3]&15); + /* call the emulation function */ + ((void (*)(int, int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15,opcode[2]>>4); + return 0; + case 8: /* RRF format, cfdbr & cfxbr instruction */ + emu_store_regd(opcode[3]&15); + /* call the emulation function */ + ((void (*)(int, int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15,opcode[2]>>4); + return 0; + case 9: /* RRE format, ldebr & mdebr instruction */ + /* float store but double load */ + emu_store_rege((opcode[3]>>4)&15); + emu_store_rege(opcode[3]&15); + /* call the emulation function */ + ((void (*)(int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15); + emu_load_regd((opcode[3]>>4)&15); + return 0; + case 10: /* RRE format, ledbr instruction */ + /* double store but float load */ + emu_store_regd((opcode[3]>>4)&15); + emu_store_regd(opcode[3]&15); + /* call the emulation function */ + ((void (*)(int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15); + emu_load_rege((opcode[3]>>4)&15); + return 0; + default: + return 1; + } +} + +static void* calc_addr(struct pt_regs *regs,int rx,int rb,int disp) +{ + rx &= 0xf; + rb &= 0xf; + disp &= 0xfff; + return (void*) ((rx != 0 ? regs->gprs[rx] : 0) + /* index */ + (rb != 0 ? regs->gprs[rb] : 0) + /* base */ + disp); +} + +int math_emu_ed(__u8 *opcode, struct pt_regs * regs) { + static const __u8 format_table[] = { + 0, 0, 0, 0, 5, 1, 2, 1, 2, 2, 2, 2, 5, 2, 4, 4, + 2, 1, 1, 0, 2, 1, 0, 2, 1, 1, 1, 1, 1, 1, 3, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + static const void *jump_table[]= { + NULL, NULL, NULL, NULL, + emu_ldeb, emu_lxdb, emu_lxeb, emu_mxdb, + emu_keb, emu_ceb, emu_aeb, emu_seb, + emu_mdeb, emu_deb, emu_maeb, emu_mseb, + emu_tceb, emu_tcdb, emu_tcxb, NULL, + emu_sqeb, emu_sqdb, NULL, emu_meeb, + emu_kdb, emu_cdb, emu_adb, emu_sdb, + emu_mdb, emu_ddb, emu_madb, emu_msdb + }; + + switch (format_table[opcode[5]]) { + case 1: /* RXE format, __u64 constant */ { + __u64 *dxb, temp; + __u32 opc; + + emu_store_regd((opcode[1]>>4)&15); + opc = *((__u32 *) opcode); + dxb = (__u64 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if copy_from_user fails ? */ + copy_from_user(&temp, dxb, 8); + /* call the emulation function */ + ((void (*)(int, __u64))jump_table[opcode[5]]) + (opcode[1]>>4,temp); + emu_load_regd((opcode[1]>>4)&15); + return 0; + } + case 2: /* RXE format, __u32 constant */ { + __u32 *dxb, temp; + __u32 opc; + + emu_store_rege((opcode[1]>>4)&15); + opc = *((__u32 *) opcode); + dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if get_user fails ? */ + get_user(temp, dxb); + /* call the emulation function */ + ((void (*)(int, __u32))jump_table[opcode[5]]) + (opcode[1]>>4,temp); + emu_load_rege((opcode[1]>>4)&15); + return 0; + } + case 3: /* RXF format, __u64 constant */ { + __u32 *dxb, temp; + __u32 opc; + + emu_store_regd((opcode[1]>>4)&15); + opc = *((__u32 *) opcode); + dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if copy_from_user fails ? */ + copy_from_user(&temp, dxb, 8); + /* call the emulation function */ + ((void (*)(int, __u32, int))jump_table[opcode[5]]) + (opcode[1]>>4,temp,opcode[4]>>4); + emu_load_regd((opcode[1]>>4)&15); + return 0; + } + case 4: /* RXF format, __u32 constant */ { + __u32 *dxb, temp; + __u32 opc; + + emu_store_rege((opcode[1]>>4)&15); + opc = *((__u32 *) opcode); + dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if get_user fails ? */ + get_user(temp, dxb); + /* call the emulation function */ + ((void (*)(int, __u32, int))jump_table[opcode[5]]) + (opcode[1]>>4,temp,opcode[4]>>4); + emu_load_rege((opcode[1]>>4)&15); + return 0; + } + case 5: /* RXE format, __u32 constant */ + /* store_rege and load_regd */ + { + __u32 *dxb, temp; + __u32 opc; + emu_store_rege((opcode[1]>>4)&15); + opc = *((__u32 *) opcode); + dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if get_user fails ? */ + get_user(temp, dxb); + /* call the emulation function */ + ((void (*)(int, __u32))jump_table[opcode[5]]) + (opcode[1]>>4,temp); + emu_load_regd((opcode[1]>>4)&15); + return 0; + } + default: + return 1; + } +} + +/* + * Emulate LDR Rx,Ry with Rx or Ry not in {0, 2, 4, 6} + */ +void math_emu_ldr(__u8 *opcode) { + __u16 opc = *((__u16 *) opcode); + + if ((opc & 0x0090) == 0) { /* test if rx in {0,2,4,6} */ + /* we got an exception therfore ry can't be in {0,2,4,6} */ + __asm__ __volatile ( /* load rx from fp_regs.fprs[ry] */ + " bras 1,0f\n" + " ld 0,0(%1)\n" + "0: ex %0,0(1)" + : /* no output */ + : "a" (opc&0x00f0), + "a" (¤t->tss.fp_regs.fprs[opc&0x000f].d) + : "1" ); + } else if ((opc & 0x0009) == 0) { /* test if ry in {0,2,4,6} */ + __asm__ __volatile ( /* store ry to fp_regs.fprs[rx] */ + " bras 1,0f\n" + " std 0,0(%1)\n" + "0: ex %0,0(1)" + : /* no output */ + : "a" ((opc&0x000f)<<4), + "a" (¤t->tss.fp_regs.fprs[(opc&0x00f0)>>4].d) + : "1" ); + } else { /* move fp_regs.fprs[ry] to fp_regs.fprs[rx] */ + current->tss.fp_regs.fprs[(opc&0x00f0)>>4] = + current->tss.fp_regs.fprs[opc&0x000f]; + } +} + +/* + * Emulate LER Rx,Ry with Rx or Ry not in {0, 2, 4, 6} + */ +void math_emu_ler(__u8 *opcode) { + __u16 opc = *((__u16 *) opcode); + + if ((opc & 0x0090) == 0) { /* test if rx in {0,2,4,6} */ + /* we got an exception therfore ry can't be in {0,2,4,6} */ + __asm__ __volatile ( /* load rx from fp_regs.fprs[ry] */ + " bras 1,0f\n" + " le 0,0(%1)\n" + "0: ex %0,0(1)" + : /* no output */ + : "a" (opc&0x00f0), + "a" (¤t->tss.fp_regs.fprs[opc&0x000f].f) + : "1" ); + } else if ((opc & 0x0009) == 0) { /* test if ry in {0,2,4,6} */ + __asm__ __volatile ( /* store ry to fp_regs.fprs[rx] */ + " bras 1,0f\n" + " ste 0,0(%1)\n" + "0: ex %0,0(1)" + : /* no output */ + : "a" ((opc&0x000f)<<4), + "a" (¤t->tss.fp_regs.fprs[(opc&0x00f0)>>4].f) + : "1" ); + } else { /* move fp_regs.fprs[ry] to fp_regs.fprs[rx] */ + current->tss.fp_regs.fprs[(opc&0x00f0)>>4] = + current->tss.fp_regs.fprs[opc&0x000f]; + } +} + +/* + * Emulate LD R,D(X,B) with R not in {0, 2, 4, 6} + */ +void math_emu_ld(__u8 *opcode, struct pt_regs * regs) { + __u32 opc = *((__u32 *) opcode); + __u64 *dxb; + + dxb = (__u64 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if copy_from_user fails ? */ + copy_from_user(¤t->tss.fp_regs.fprs[(opc>>20)&15].d, dxb, 8); +} + +/* + * Emulate LE R,D(X,B) with R not in {0, 2, 4, 6} + */ +void math_emu_le(__u8 *opcode, struct pt_regs * regs) { + __u32 opc = *((__u32 *) opcode); + __u32 *mem, *dxb; + + dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if get_user fails ? */ + mem = (__u32 *) (¤t->tss.fp_regs.fprs[(opc>>20)&15].f); + get_user(mem[0], dxb); +} + +/* + * Emulate STD R,D(X,B) with R not in {0, 2, 4, 6} + */ +void math_emu_std(__u8 *opcode, struct pt_regs * regs) { + __u32 opc = *((__u32 *) opcode); + __u64 *dxb; + dxb = (__u64 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if copy_to_user fails ? */ + copy_to_user(dxb, ¤t->tss.fp_regs.fprs[(opc>>20)&15].d, 8); +} + +/* + * Emulate STE R,D(X,B) with R not in {0, 2, 4, 6} + */ +void math_emu_ste(__u8 *opcode, struct pt_regs * regs) { + __u32 opc = *((__u32 *) opcode); + __u32 *mem, *dxb; + dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if put_user fails ? */ + mem = (__u32 *) (¤t->tss.fp_regs.fprs[(opc>>20)&15].f); + put_user(mem[0], dxb); +} + +/* + * Emulate LFPC D(B) + */ +int math_emu_lfpc(__u8 *opcode, struct pt_regs *regs) { + /* FIXME: how to do that ?!? */ + return 0; +} + +/* + * Emulate STFPC D(B) + */ +int math_emu_stfpc(__u8 *opcode, struct pt_regs *regs) { + /* FIXME: how to do that ?!? */ + return 0; +} + +/* + * Emulate SRNM D(B) + */ +int math_emu_srnm(__u8 *opcode, struct pt_regs *regs) { + /* FIXME: how to do that ?!? */ + return 0; +} + + + + + + + + + + + + + + + + diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/process.c linux/arch/s390/kernel/process.c --- v2.2.13/linux/arch/s390/kernel/process.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/process.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,415 @@ +/* + * arch/s390/kernel/process.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * Hartmut Penner (hp@de.ibm.com), + * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + * + * Derived from "arch/i386/kernel/process.c" + * Copyright (C) 1995, Linus Torvalds + */ + +/* + * This file handles the architecture-dependent parts of process handling.. + */ + +#define __KERNEL_SYSCALLS__ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "irq.h" + +spinlock_t semaphore_wake_lock = SPIN_LOCK_UNLOCKED; + +asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); + +/* + * The idle loop on a S390... + */ + +static psw_t wait_psw; + +#ifndef __SMP__ +static +#endif +int cpu_idle(void *unused) +{ + /* endless idle loop with no priority at all */ + init_idle(); + current->priority = 0; + current->counter = -100; + wait_psw.mask = _WAIT_PSW_MASK; + wait_psw.addr = (unsigned long) &&idle_wakeup | 0x80000000L; + while(1) { + if (bh_mask & bh_active) { + do_bottom_half(); + continue; + } + if (current->need_resched) { + schedule(); + check_pgt_cache(); + continue; + } + + /* load wait psw */ + asm volatile ( + "lpsw %0" + : : "m" (wait_psw) ); +idle_wakeup: + } +} + +asmlinkage int sys_idle(void) +{ + if (current->pid != 0) + return -EPERM; + cpu_idle(NULL); + return 0; +} + +/* + As all the register will only be made displayable to the root + user ( via printk ) or checking if the uid of the user is 0 from + the /proc filesystem please god this will be secure enough DJB. + The lines are given one at a time so as not to chew stack space in + printk on a crash & also for the proc filesystem when you get + 0 returned you know you've got all the lines + */ + +int sprintf_regs(int line,char *buff,struct task_struct * task,struct thread_struct *tss,struct pt_regs * regs) +{ + int linelen=0; + int regno,chaincnt; + u32 backchain,prev_backchain,endchain; + + enum + { + sp_linefeed, + sp_psw, + sp_ksp, + sp_gprs, + sp_gprs1, + sp_gprs2, + sp_gprs3, + sp_gprs4, + sp_acrs, + sp_acrs1, + sp_acrs2, + sp_acrs3, + sp_acrs4, + sp_kern_backchain, + sp_kern_backchain1 + }; + + if(task) + tss=&task->tss; + if(tss) + regs=tss->regs; + switch(line) + { + case sp_linefeed: linelen=sprintf(buff,"\n"); + break; + case sp_psw: + if(regs) + linelen=sprintf(buff,"User PSW: %08lx %08lx\n", + (unsigned long) regs->psw.mask, + (unsigned long) regs->psw.addr); + else + linelen=sprintf(buff,"pt_regs=NULL some info unavailable\n"); + break; + case sp_ksp: + if(task) + linelen+=sprintf(&buff[linelen],"task: %08x ",(addr_t)task); + if(tss) + linelen+=sprintf(&buff[linelen],"tss: %08x ksp: %08x ", + (addr_t)tss,(addr_t)tss->ksp); + if(regs) + linelen+=sprintf(&buff[linelen],"pt_regs: %08x\n",(addr_t)regs); + break; + case sp_gprs: + if(regs) + linelen=sprintf(buff,"User GPRS:\n"); + break; + case sp_gprs1 ... sp_gprs4: + if(regs) + { + regno=(line-sp_gprs1)*4; + linelen=sprintf(buff,"%08x %08x %08x %08x\n", + regs->gprs[regno], regs->gprs[regno+1], + regs->gprs[regno+2], regs->gprs[regno+3]); + } + break; + case sp_acrs: + if(regs) + linelen=sprintf(buff,"User ACRS:\n"); + break; + case sp_acrs1 ... sp_acrs4: + if(regs) + { + regno=(line-sp_acrs1)*4; + linelen=sprintf(buff,"%08x %08x %08x %08x\n", + regs->acrs[regno], regs->acrs[regno+1], + regs->acrs[regno+2], regs->acrs[regno+3]); + } + break; + case sp_kern_backchain: + if(tss&&tss->ksp&®s) + linelen=sprintf(buff,"Kernel BackChain CallChain BackChain CallChain\n"); + break; + default: + if(tss&&tss->ksp&®s) + { + + backchain=(tss->ksp&PSW_ADDR_MASK); + endchain=((backchain&(-8192))+8192); + prev_backchain=backchain-1; + line-=sp_kern_backchain1; + for(chaincnt=0;;chaincnt++) + { + if((backchain==0)||(backchain>=endchain) + ||(chaincnt>=8)||(prev_backchain>=backchain)) + break; + if((chaincnt>>1)==line) + { + linelen+=sprintf(&buff[linelen],"%s%08x %08x ", + (chaincnt&1) ? "":" ", + backchain,*(u32 *)(backchain+56)); + } + if((chaincnt>>1)>line) + break; + prev_backchain=backchain; + backchain=(*((u32 *)backchain))&PSW_ADDR_MASK; + } + if(linelen) + linelen+=sprintf(&buff[linelen],"\n"); + } + } + return(linelen); +} + + +void show_regs(struct task_struct * task,struct thread_struct *tss,struct pt_regs *regs) +{ + char buff[80]; + int line; + + for(line=0;sprintf_regs(line,buff,task,tss,regs);line++) + printk(buff); +} + +int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +{ + int clone_arg = flags | CLONE_VM; + int retval; + + __asm__ __volatile__( + " sr 2,2\n" + " lr 3,%1\n" + " l 4,%6\n" /* load kernel stack ptr of parent */ + " svc %b2\n" /* Linux system call*/ + " cl 4,%6\n" /* compare ksp's: child or parent ? */ + " je 0f\n" /* parent - jump*/ + " l 15,%6\n" /* fix kernel stack pointer*/ + " ahi 15,%7\n" + " xc 0(96,15),0(15)\n" /* clear save area */ + " lr 2,%4\n" /* load argument*/ + " lr 14,%5\n" /* get fn-pointer*/ + " basr 14,14\n" /* call fn*/ + " svc %b3\n" /* Linux system call*/ + "0: lr %0,2" + : "=a" (retval) + : "d" (clone_arg), "i" (__NR_clone), "i" (__NR_exit), + "d" (arg), "d" (fn), "i" (__LC_KERNEL_STACK) , "i" (-STACK_FRAME_OVERHEAD) + : "2", "3", "4" ); + return retval; +} + +/* + * Free current thread data structures etc.. + */ +void exit_thread(void) +{ +} + +void flush_thread(void) +{ + + current->used_math = 0; + current->flags &= ~PF_USEDFPU; +} + +void release_thread(struct task_struct *dead_task) +{ +} + +int copy_thread(int nr, unsigned long clone_flags, unsigned long new_stackp, + struct task_struct * p, struct pt_regs * regs) +{ + struct stack_frame + { + unsigned long back_chain; + unsigned long eos; + unsigned long glue1; + unsigned long glue2; + unsigned long scratch[2]; + unsigned long gprs[10]; /* gprs 6 -15 */ + unsigned long fprs[4]; /* fpr 4 and 6 */ + unsigned long empty[4]; + struct pt_regs childregs; + } *frame; + + frame = (struct stack_frame *) (2*PAGE_SIZE + (unsigned long) p) -1; + frame = (struct stack_frame *) (((unsigned long) frame)&-8L); + p->tss.regs = &frame->childregs; + p->tss.ksp = (unsigned long) frame; + frame->childregs = *regs; + frame->childregs.gprs[15] = new_stackp; + frame->eos = 0; + + /* new return point is ret_from_sys_call */ + frame->gprs[8] = ((unsigned long) &ret_from_fork) | 0x80000000; + + /* fake return stack for resume(), don't go back to schedule */ + frame->gprs[9] = (unsigned long) frame; + + /* save fprs, if used in last task */ + save_fp_regs(&p->tss.fp_regs); + p->tss.user_seg = __pa((unsigned long) p->mm->pgd) | _USER_SEG_TABLE_LEN; + p->tss.fs = USER_DS; + /* Don't copy debug registers */ + memset(&p->tss.per_info,0,sizeof(p->tss.per_info)); + return 0; +} + + + +asmlinkage int sys_fork(struct pt_regs regs) +{ + int ret; + + lock_kernel(); + ret = do_fork(SIGCHLD, regs.gprs[15], ®s); + unlock_kernel(); + return ret; +} + +asmlinkage int sys_clone(struct pt_regs regs) +{ + unsigned long clone_flags; + unsigned long newsp; + int ret; + + lock_kernel(); + clone_flags = regs.gprs[3]; + newsp = regs.gprs[2]; + if (!newsp) + newsp = regs.gprs[15]; + ret = do_fork(clone_flags, newsp, ®s); + unlock_kernel(); + return ret; +} + +/* + * This is trivial, and on the face of it looks like it + * could equally well be done in user mode. + * + * Not so, for quite unobvious reasons - register pressure. + * In user mode vfork() cannot have a stack frame, and if + * done by calling the "clone()" system call directly, you + * do not have enough call-clobbered registers to hold all + * the information you need. + */ +asmlinkage int sys_vfork(struct pt_regs regs) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, + regs.gprs[15], ®s); +} + +/* + * sys_execve() executes a new program. + */ +asmlinkage int sys_execve(struct pt_regs regs) +{ + int error; + char * filename; + + lock_kernel(); + filename = getname((char *) regs.orig_gpr2); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + error = do_execve(filename, (char **) regs.gprs[3], (char **) regs.gprs[4], ®s); + if (error == 0) + current->flags &= ~PF_DTRACE; + putname(filename); +out: + unlock_kernel(); + return error; +} + + +/* + * fill in the FPU structure for a core dump. + */ +int dump_fpu (struct pt_regs * regs, s390_fp_regs *fpregs) +{ + save_fp_regs(fpregs); + return(TRUE); +} + +/* + * fill in the user structure for a core dump.. + */ +void dump_thread(struct pt_regs * regs, struct user * dump) +{ + +/* changed the size calculations - should hopefully work better. lbt */ + dump->magic = CMAGIC; + dump->start_code = 0; + dump->start_stack = regs->gprs[15] & ~(PAGE_SIZE - 1); + dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT; + dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT; + dump->u_dsize -= dump->u_tsize; + dump->u_ssize = 0; + if (dump->start_stack < TASK_SIZE) + dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT; + memcpy(&dump->regs.gprs[0],regs,sizeof(s390_regs)); + dump_fpu (regs, &dump->regs.fp_regs); + memcpy(&dump->regs.per_info,¤t->tss.per_info,sizeof(per_struct)); +} + + + + + + + + diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/ptrace.c linux/arch/s390/kernel/ptrace.c --- v2.2.13/linux/arch/s390/kernel/ptrace.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/ptrace.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,605 @@ +/* + * arch/s390/kernel/ptrace.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + * + * Based on PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/m68k/kernel/ptrace.c" + * Copyright (C) 1994 by Hamish Macdonald + * Taken from linux/kernel/ptrace.c and modified for M680x0. + * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file README.legal in the main directory of + * this archive for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +void FixPerRegisters(struct task_struct *task) +{ + struct pt_regs *regs = task->tss.regs; + per_struct *per_info= + (per_struct *)&task->tss.per_info; + + per_info->control_regs.bits.em_instruction_fetch= + per_info->single_step|per_info->instruction_fetch; + + if(per_info->single_step) + { + per_info->control_regs.bits.starting_addr=0; + per_info->control_regs.bits.ending_addr=0x7fffffffUL; + } + else + { + per_info->control_regs.bits.starting_addr= + per_info->starting_addr; + per_info->control_regs.bits.ending_addr= + per_info->ending_addr; + } + /* if any of the control reg tracing bits are on + we switch on per in the psw */ + if(per_info->control_regs.words.cr[0]&PER_EM_MASK) + regs->psw.mask |=PSW_PER_MASK; + else + regs->psw.mask &= ~PSW_PER_MASK; + if(per_info->control_regs.bits.storage_alt_space_ctl) + task->tss.user_seg|=USER_STD_MASK; + else + task->tss.user_seg&=~USER_STD_MASK; +} + +void set_single_step(struct task_struct *task) +{ + per_struct *per_info= + (per_struct *)&task->tss.per_info; + + per_info->single_step=1; /* Single step */ + FixPerRegisters(task); +} + +void clear_single_step(struct task_struct *task) +{ + per_struct *per_info= + (per_struct *)&task->tss.per_info; + + per_info->single_step=0; + FixPerRegisters(task); +} + + + +/* + * This routine gets a long from any process space by following the page + * tables. NOTE! You should check that the long isn't on a page boundary, + * and that it is in the task area before calling this: this routine does + * no checking. + * + */ +static unsigned long get_long(struct task_struct * tsk, + struct vm_area_struct * vma, unsigned long addr) +{ + pgd_t * pgdir; + pmd_t * pgmiddle; + pte_t * pgtable; + unsigned long page; + +repeat: + pgdir = pgd_offset(vma->vm_mm, addr); + if (pgd_none(*pgdir)) { + handle_mm_fault(tsk, vma, addr, 0); + goto repeat; + } + if (pgd_bad(*pgdir)) { + printk("ptrace[1]: bad page directory %lx\n", pgd_val(*pgdir)); + pgd_clear(pgdir); + return 0; + } + pgmiddle = pmd_offset(pgdir,addr); + if (pmd_none(*pgmiddle)) { + handle_mm_fault(tsk, vma, addr, 0); + goto repeat; + } + if (pmd_bad(*pgmiddle)) { + printk("ptrace[3]: bad pmd %lx\n", pmd_val(*pgmiddle)); + pmd_clear(pgmiddle); + return 0; + } + pgtable = pte_offset(pgmiddle, addr); + if (!pte_present(*pgtable)) { + handle_mm_fault(tsk, vma, addr, 0); + goto repeat; + } + page = pte_page(*pgtable); +/* this is a hack for non-kernel-mapped video buffers and similar */ + if (MAP_NR(page) >= max_mapnr) + return 0; + page += addr & ~PAGE_MASK; + return *(unsigned long *) page; +} + +/* + * This routine puts a long into any process space by following the page + * tables. NOTE! You should check that the long isn't on a page boundary, + * and that it is in the task area before calling this: this routine does + * no checking. + * + * Now keeps R/W state of page so that a text page stays readonly + * even if a debugger scribbles breakpoints into it. -M.U- + */ +static void put_long(struct task_struct * tsk, struct vm_area_struct * vma, + unsigned long addr, unsigned long data) +{ + pgd_t *pgdir; + pmd_t *pgmiddle; + pte_t *pgtable; + unsigned long page; + +repeat: + pgdir = pgd_offset(vma->vm_mm, addr); + if (!pgd_present(*pgdir)) { + handle_mm_fault(tsk, vma, addr, 1); + goto repeat; + } + if (pgd_bad(*pgdir)) { + printk("ptrace[2]: bad page directory %lx\n", pgd_val(*pgdir)); + pgd_clear(pgdir); + return; + } + pgmiddle = pmd_offset(pgdir,addr); + if (pmd_none(*pgmiddle)) { + handle_mm_fault(tsk, vma, addr, 1); + goto repeat; + } + if (pmd_bad(*pgmiddle)) { + printk("ptrace[4]: bad pmd %lx\n", pmd_val(*pgmiddle)); + pmd_clear(pgmiddle); + return; + } + pgtable = pte_offset(pgmiddle, addr); + if (!pte_present(*pgtable)) { + handle_mm_fault(tsk, vma, addr, 1); + goto repeat; + } + page = pte_page(*pgtable); + if (!pte_write(*pgtable)) { + handle_mm_fault(tsk, vma, addr, 1); + goto repeat; + } +/* this is a hack for non-kernel-mapped video buffers and similar */ + if (MAP_NR(page) < max_mapnr) { + unsigned long phys_addr = page + (addr & ~PAGE_MASK); + *(unsigned long *) phys_addr = data; + flush_icache_range(phys_addr, phys_addr+4); + } +/* we're bypassing pagetables, so we have to set the dirty bit ourselves */ +/* this should also re-instate whatever read-only mode there was before */ + set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot))); + flush_tlb_all(); +} + +static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr) +{ + struct vm_area_struct * vma; + + addr &= PAGE_MASK; + vma = find_vma(tsk->mm,addr); + if (!vma) + return NULL; + if (vma->vm_start <= addr) + return vma; + if (!(vma->vm_flags & VM_GROWSDOWN)) + return NULL; + if (vma->vm_end - addr > tsk->rlim[RLIMIT_STACK].rlim_cur) + return NULL; + vma->vm_offset -= vma->vm_start - addr; + vma->vm_start = addr; + return vma; +} + +/* + * This routine checks the page boundaries, and that the offset is + * within the task area. It then calls get_long() to read a long. + */ +static int read_long(struct task_struct * tsk, unsigned long addr, + unsigned long * result) +{ + struct vm_area_struct * vma = find_extend_vma(tsk, addr); + + if (!vma) + return -EIO; + if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) { + unsigned long low,high; + struct vm_area_struct * vma_low = vma; + + if (addr + sizeof(long) >= vma->vm_end) { + vma_low = vma->vm_next; + if (!vma_low || vma_low->vm_start != vma->vm_end) + return -EIO; + } + high = get_long(tsk, vma,addr & ~(sizeof(long)-1)); + low = get_long(tsk, vma_low,(addr+sizeof(long)) & ~(sizeof(long)-1)); + switch (addr & (sizeof(long)-1)) { + case 3: + low >>= 8; + low |= high << 24; + break; + case 2: + low >>= 16; + low |= high << 16; + break; + case 1: + low >>= 24; + low |= high << 8; + break; + } + *result = low; + } else + *result = get_long(tsk, vma,addr); + return 0; +} + +/* + * This routine checks the page boundaries, and that the offset is + * within the task area. It then calls put_long() to write a long. + */ +static int write_long(struct task_struct * tsk, unsigned long addr, + unsigned long data) +{ + struct vm_area_struct * vma = find_extend_vma(tsk, addr); + + if (!vma) + return -EIO; + if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) { + unsigned long low,high; + struct vm_area_struct * vma_low = vma; + + if (addr + sizeof(long) >= vma->vm_end) { + vma_low = vma->vm_next; + if (!vma_low || vma_low->vm_start != vma->vm_end) + return -EIO; + } + high = get_long(tsk, vma,addr & ~(sizeof(long)-1)); + low = get_long(tsk, vma_low,(addr+sizeof(long)) & ~(sizeof(long)-1)); + switch (addr & (sizeof(long)-1)) { + case 0: /* shouldn't happen, but safety first */ + high = data; + break; + case 3: + low &= 0x000000ff; + low |= data << 8; + high &= ~0xff; + high |= data >> 24; + break; + case 2: + low &= 0x0000ffff; + low |= data << 16; + high &= ~0xffff; + high |= data >> 16; + break; + case 1: + low &= 0x00ffffff; + low |= data << 24; + high &= ~0xffffff; + high |= data >> 8; + break; + } + put_long(tsk, vma,addr & ~(sizeof(long)-1),high); + put_long(tsk, vma_low,(addr+sizeof(long)) & ~(sizeof(long)-1),low); + } else + put_long(tsk, vma,addr,data); + return 0; +} + + +int ptrace_usercopy(addr_t realuseraddr,addr_t copyaddr,int len,int tofromuser,int writeuser,u32 mask) +{ + u32 tempuser; + int retval=0; + + if(writeuser&&realuseraddr==(addr_t)NULL) + return(0); + if(mask!=0xffffffff) + { + tempuser=*((u32 *)realuseraddr); + if(!writeuser) + { + tempuser&=mask; + realuseraddr=(addr_t)&tempuser; + } + } + if(tofromuser) + { + if(writeuser) + { + retval=copy_from_user((void *)realuseraddr,(void *)copyaddr,len); + } + else + { + if(realuseraddr==(addr_t)NULL) + retval=(clear_user((void *)copyaddr,len)==-EFAULT ? -EIO:0); + else + retval=(copy_to_user((void *)copyaddr,(void *)realuseraddr,len)==-EFAULT ? -EIO:0); + } + } + else + { + if(writeuser) + memcpy((void *)realuseraddr,(void *)copyaddr,len); + else + memcpy((void *)copyaddr,(void *)realuseraddr,len); + } + if(mask!=0xffffffff&&writeuser) + (*((u32 *)realuseraddr))=(((*((u32 *)realuseraddr))&mask)|(tempuser&~mask)); + return(retval); +} + +int copy_user(struct task_struct *task,saddr_t useraddr,addr_t copyaddr,int len,int tofromuser,int writingtouser) +{ + int copylen=0,copymax; + addr_t realuseraddr; + saddr_t enduseraddr=useraddr+len; + + u32 mask; + + if (useraddr < 0 || enduseraddr > sizeof(struct user)|| + (useraddr < PT_ENDREGS && (useraddr&3))|| + (enduseraddr < PT_ENDREGS && (enduseraddr&3))) + return (-EIO); + while(len>0) + { + mask=0xffffffff; + if(useraddrtss.regs)[useraddr]); + if(useraddrtss.fp_regs)[useraddr-PT_FPC]); + } + else if(useraddrtss.per_info)[useraddr-PT_CR_9]); + } + else + { + copymax=sizeof(struct user); + realuseraddr=(addr_t)NULL; + } + copylen=copymax-useraddr; + copylen=(copylen>len ? len:copylen); + if(ptrace_usercopy(realuseraddr,copyaddr,copylen,tofromuser,writingtouser,mask)) + return (-EIO); + copyaddr+=copylen; + len-=copylen; + useraddr+=copylen; + } + FixPerRegisters(task); + return(0); +} + +asmlinkage int sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + int ret = -EPERM; + unsigned long tmp; + ptrace_area parea; + lock_kernel(); + if (request == PTRACE_TRACEME) + { + /* are we already being traced? */ + if (current->flags & PF_PTRACED) + goto out; + /* set the ptrace bit in the process flags. */ + current->flags |= PF_PTRACED; + ret = 0; + goto out; + } + ret = -ESRCH; + if (!(child = find_task_by_pid(pid))) + goto out; + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out; + if (request == PTRACE_ATTACH) + { + if (child == current) + goto out; + if ((!child->dumpable || + (current->uid != child->euid) || + (current->uid != child->uid) || + (current->gid != child->egid) || + (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) + goto out; + /* the same process cannot be attached many times */ + if (child->flags & PF_PTRACED) + goto out; + child->flags |= PF_PTRACED; + if (child->p_pptr != current) + { + REMOVE_LINKS(child); + child->p_pptr = current; + SET_LINKS(child); + } + send_sig(SIGSTOP, child, 1); + ret = 0; + goto out; + } + ret = -ESRCH; + // printk("child=%lX child->flags=%lX",child,child->flags); + if (!(child->flags & PF_PTRACED)) + goto out; + if (child->state != TASK_STOPPED) + { + if (request != PTRACE_KILL) + goto out; + } + if (child->p_pptr != current) + goto out; + + switch (request) + { + /* If I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: + down(&child->mm->mmap_sem); + ret = read_long(child, addr, &tmp); + up(&child->mm->mmap_sem); + if (ret < 0) + break; + ret=put_user(tmp, (unsigned long *) data); + break; + + /* read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: + ret=copy_user(child,addr,data,sizeof(unsigned long),TRUE,FALSE); + break; + + /* If I and D space are separate, this will have to be fixed. */ + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: + down(&child->mm->mmap_sem); + ret = write_long(child,addr,data); + up(&child->mm->mmap_sem); + break; + + case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ + ret=copy_user(child,addr,(addr_t)&data,sizeof(unsigned long),FALSE,TRUE); + break; + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: /* restart after signal. */ + ret = -EIO; + if ((unsigned long) data >= _NSIG) + break; + if (request == PTRACE_SYSCALL) + child->flags |= PF_TRACESYS; + else + child->flags &= ~PF_TRACESYS; + child->exit_code = data; + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + ret = 0; + break; + +/* + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to + * exit. + */ + case PTRACE_KILL: + ret = 0; + if (child->state == TASK_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + clear_single_step(child); + wake_up_process(child); + /* make sure the single step bit is not set. */ + break; + + case PTRACE_SINGLESTEP: /* set the trap flag. */ + ret = -EIO; + if ((unsigned long) data >= _NSIG) + break; + child->flags &= ~PF_TRACESYS; + child->exit_code = data; + set_single_step(child); + /* give it a chance to run. */ + wake_up_process(child); + ret = 0; + break; + + case PTRACE_DETACH: /* detach a process that was attached. */ + ret = -EIO; + if ((unsigned long) data >= _NSIG) + break; + child->flags &= ~(PF_PTRACED|PF_TRACESYS); + wake_up_process(child); + child->exit_code = data; + REMOVE_LINKS(child); + child->p_pptr = child->p_opptr; + SET_LINKS(child); + /* make sure the single step bit is not set. */ + clear_single_step(child); + ret = 0; + break; + case PTRACE_PEEKUSR_AREA: + case PTRACE_POKEUSR_AREA: + if((ret=copy_from_user(&parea,(void *)addr,sizeof(parea)))==0) + ret=copy_user(child,parea.kernel_addr,parea.process_addr, + parea.len,TRUE,(request==PTRACE_POKEUSR_AREA)); + break; + default: + ret = -EIO; + break; + } + out: + unlock_kernel(); + return ret; +} + +asmlinkage void syscall_trace(void) +{ + lock_kernel(); + if ((current->flags & (PF_PTRACED|PF_TRACESYS)) + != (PF_PTRACED|PF_TRACESYS)) + goto out; + current->exit_code = SIGTRAP; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } + out: + unlock_kernel(); +} diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/s390fpu.c linux/arch/s390/kernel/s390fpu.c --- v2.2.13/linux/arch/s390/kernel/s390fpu.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/s390fpu.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,146 @@ +/* + * arch/s390/kernel/s390fpu.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) + * + * s390fpu.h functions for saving & restoring the fpu state. + * + * I couldn't inline these as linux/sched.h included half the world + * & was required to at the task structure. + * & the functions were too complex to make macros from. + * ( & as usual I didn't feel like debugging inline code ). + */ + +#include + +int save_fp_regs1(s390_fp_regs *fpregs) +{ + int has_ieee=MACHINE_HAS_IEEE; +/* + I don't think we can use STE here as this would load + fp registers 0 & 2 into memory locations 0 & 1 etc. + */ + asm volatile ("STD 0,8(%0)\n\t" + "STD 2,24(%0)\n\t" + "STD 4,40(%0)\n\t" + "STD 6,56(%0)" + : + : "a" (fpregs) + : "memory" + ); + if(has_ieee) + { + asm volatile ("STFPC 0(%0)\n\t" + "STD 1,16(%0)\n\t" + "STD 3,32(%0)\n\t" + "STD 5,48(%0)\n\t" + "STD 7,64(%0)\n\t" + "STD 8,72(%0)\n\t" + "STD 9,80(%0)\n\t" + "STD 10,88(%0)\n\t" + "STD 11,96(%0)\n\t" + "STD 12,104(%0)\n\t" + "STD 13,112(%0)\n\t" + "STD 14,120(%0)\n\t" + "STD 15,128(%0)\n\t" + : + : "a" (fpregs) + : "memory" + ); + } + return(has_ieee); +} + + +void save_fp_regs(s390_fp_regs *fpregs) +{ +#if CONFIG_IEEEFPU_EMULATION + s390_fp_regs *currentfprs; +#endif +#if CONFIG_IEEEFPU_EMULATION + if(!save_fp_regs1(fpregs)) + { + currentfprs=¤t->tss.fp_regs; + fpregs->fpc=currentfprs->fpc; + fpregs->fprs[1].d=currentfprs->fprs[1].d; + fpregs->fprs[3].d=currentfprs->fprs[3].d; + fpregs->fprs[5].d=currentfprs->fprs[5].d; + fpregs->fprs[7].d=currentfprs->fprs[7].d; + memcpy(&fpregs->fprs[8].d,¤tfprs->fprs[8].d,sizeof(freg_t)*8); + } +#else + save_fp_regs1(fpregs); +#endif +} + + +int restore_fp_regs1(s390_fp_regs *fpregs) +{ + int has_ieee=MACHINE_HAS_IEEE; + + asm volatile ("LD 0,8(%0)\n\t" + "LD 2,24(%0)\n\t" + "LD 4,40(%0)\n\t" + "LD 6,56(%0)" + : + : "a" (fpregs) + : "memory" + ); + if(has_ieee) + { + asm volatile ("LFPC 0(%0)\n\t" + "LD 1,16(%0)\n\t" + "LD 3,32(%0)\n\t" + "LD 5,48(%0)\n\t" + "LD 7,64(%0)\n\t" + "LD 8,72(%0)\n\t" + "LD 9,80(%0)\n\t" + "LD 10,88(%0)\n\t" + "LD 11,96(%0)\n\t" + "LD 12,104(%0)\n\t" + "LD 13,112(%0)\n\t" + "LD 14,120(%0)\n\t" + "LD 15,128(%0)\n\t" + : + : "a" (fpregs) + : "memory" + ); + } + return(has_ieee); +} + +void restore_fp_regs(s390_fp_regs *fpregs) +{ +#if CONFIG_IEEEFPU_EMULATION + s390_fp_regs *currentfprs; +#endif + +#if CONFIG_IEEEFPU_EMULATION + if(!restore_fp_regs1(fpregs)) + { + currentfprs=¤t->tss.fp_regs; + currentfprs->fpc=fpregs->fpc; + currentfprs->fprs[1].d=fpregs->fprs[1].d; + currentfprs->fprs[3].d=fpregs->fprs[3].d; + currentfprs->fprs[5].d=fpregs->fprs[5].d; + currentfprs->fprs[7].d=fpregs->fprs[7].d; + memcpy(¤tfprs->fprs[8].d,&fpregs->fprs[8].d,sizeof(freg_t)*8); + } +#else + restore_fp_regs1(fpregs); +#endif +} + + + + + + + + + + + + diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/s390io.c linux/arch/s390/kernel/s390io.c --- v2.2.13/linux/arch/s390/kernel/s390io.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/s390io.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,3386 @@ +/* + * arch/s390/kernel/s390io.c + * S/390 common I/O routines + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Ingo Adlung (adlung@de.ibm.com) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "irq.h" +#include "s390io.h" + +#undef CONFIG_DEBUG_IO + +#define REIPL_DEVID_MAGIC 0x87654321 + +struct irqaction init_IRQ_action; +unsigned int highest_subchannel; +senseid_t senseid[NR_IRQS]; +schib_t schiblock[NR_IRQS]; +ioinfo_t ioinfo[NR_IRQS]; +spinlock_t sync_isc; // synchronous irq processing lock +psw_t io_sync_wait; // wait PSW for sync IO, prot. by sync_isc +psw_t io_new_psw; // save I/O new PSW, prot. by sync_isc +int cons_dev = -1; // identify console device +int init_IRQ_complete = 0; + +extern struct hw_interrupt_type no_irq_type; + +static void init_IRQ_handler( int irq, void *dev_id, struct pt_regs *regs); +static int s390_setup_irq(unsigned int irq, struct irqaction * new); +static void s390_process_subchannels( void); +static void s390_device_recognition( void); +static int s390_validate_subchannel( int irq); +static int s390_SenseID( int irq, senseid_t *sid); +static int s390_process_IRQ( unsigned int irq, unsigned int intparm); + +extern int do_none(unsigned int irq, int cpu, struct pt_regs * regs); +extern int enable_none(unsigned int irq); +extern int disable_none(unsigned int irq); +extern void tod_wait(unsigned long usecs); + +asmlinkage void do_IRQ( struct pt_regs regs, + unsigned int irq, + unsigned int intparm ); + +void s390_displayhex(char *str,void *ptr,s32 cnt); + +void s390_displayhex(char *str,void *ptr,s32 cnt) +{ + s32 cnt1,cnt2,maxcnt2; + u32 *currptr=(u32 *)ptr; + + printk("\n%s\n",str); + for(cnt1=0;cnt116) + maxcnt2=16; + for(cnt2=0;cnt2= NR_IRQS) + return -EINVAL; + + if ( !handler || !dev_id ) + return -EINVAL; + + /* + * during init_IRQ() processing we don't have memory + * management yet, thus need to use a statically + * allocated irqaction control block + */ + if ( init_IRQ_complete ) + { + action = (struct irqaction *) + kmalloc(sizeof(struct irqaction), GFP_KERNEL); + } + else + { + action = &init_IRQ_action; + + } /* endif */ + + if (!action) + { + return -ENOMEM; + + } /* endif */ + + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + action->next = NULL; + action->dev_id = dev_id; + + retval = s390_setup_irq(irq, action); + + if ( retval && init_IRQ_complete ) + { + kfree(action); + + } /* endif */ + + return retval; +} + +void s390_free_irq(unsigned int irq, void *dev_id) +{ + unsigned int flags; + int ret; + + unsigned int count = 0; + + if ( irq >= NR_IRQS ) + { + return; + + } /* endif */ + + s390irq_spin_lock_irqsave(irq,flags); + +#ifdef CONFIG_KERNEL_DEBUG + if ( irq != cons_dev ) + { + printk("Trying to free IRQ%d\n",irq); + + } /* endif */ +#endif + + /* + * disable the device and reset all IRQ info if + * the IRQ is actually owned by the handler ... + */ + if ( irq_desc[irq].action ) + { + if ( irq_desc[irq].action->dev_id == dev_id || + dev_id == REIPL_DEVID_MAGIC ) + { + ioinfo[irq].ui.flags.unready = 1; /* start deregister */ + + do + { + ret = irq_desc[irq].handler->disable(irq); + + count++; + + if ( count == 3 ) + { + panic( "free_irq() - device busy, retry count exceeded\n"); + + } /* endif */ + + } while ( ret == -EBUSY ); + + if ( init_IRQ_complete ) + kfree( irq_desc[irq].action ); + + irq_desc[irq].action = NULL; + ioinfo[irq].ui.flags.ready = 0; + + irq_desc[irq].handler->enable = &enable_none; + irq_desc[irq].handler->disable = &disable_none; + + ioinfo[irq].ui.flags.unready = 0; /* deregister ended */ + + s390irq_spin_unlock_irqrestore( irq, flags); + } + else + { + s390irq_spin_unlock_irqrestore( irq, flags); + + printk("free_irq() : error, dev_id does not match !"); + + } /* endif */ + + } + else + { + s390irq_spin_unlock_irqrestore( irq, flags); + + printk("free_irq() : error, no action block ... !"); + + } /* endif */ + +} + +/* + * Generic enable/disable code + */ +int disable_irq(unsigned int irq) +{ + unsigned long flags; + int ret; + + s390irq_spin_lock_irqsave(irq, flags); + + /* + * At this point we may actually have a pending interrupt being active + * on another CPU. So don't touch the IRQ_INPROGRESS bit.. + */ + irq_desc[irq].status |= IRQ_DISABLED; + ret = irq_desc[irq].handler->disable(irq); + s390irq_spin_unlock_irqrestore(irq, flags); + + synchronize_irq(); + + return( ret); +} + +int enable_irq(unsigned int irq) +{ + unsigned long flags; + int ret; + + s390irq_spin_lock_irqsave(irq, flags); + + irq_desc[irq].status = 0; + ret = irq_desc[irq].handler->enable(irq); + + s390irq_spin_unlock_irqrestore(irq, flags); + + return(ret); +} + +/* + * Enable IRQ by modifying the subchannel + */ +static int enable_subchannel( unsigned int irq) +{ + int ret; + int ccode; + int retry = 5; + + if ( irq > highest_subchannel || irq < 0 ) + { + return( -ENODEV ); + + } /* endif */ + + /* + * If a previous disable request is pending we reset it. However, this + * status implies that the device may (still) be not-operational. + */ + if ( ioinfo[irq].ui.flags.d_disable ) + { + ioinfo[irq].ui.flags.d_disable = 0; + ret = 0; + } + else + { + ccode = stsch(irq, &(ioinfo[irq].schib) ); + + if ( ccode ) + { + ret = -ENODEV; + } + else + { + ioinfo[irq].schib.pmcw.ena = 1; + + do + { + ccode = msch( irq, &(ioinfo[irq].schib) ); + + switch (ccode) { + case 0: + ret = 0; + break; + + case 1: + /* + * very bad, requires interrupt alike processing, where + * "rbh" is a dummy parameter for interface compatibility + * only. Bottom-half handling cannot be required as + * this must be an unsolicited interrupt (!busy). + */ + + ioinfo[irq].ui.flags.s_pend = 1; + + s390_process_IRQ( irq, 0 ); + + ioinfo[irq].ui.flags.s_pend = 0; + + ret = -EIO; /* might be overwritten on ... */ + /* ... re-driving the msch() */ + retry--; + break; + + case 3: + ioinfo[irq].ui.flags.oper = 0; + ret = -ENODEV; + break; + + default: + panic( "enable_subchannel() : ccode 2 on msch() received !\n"); + ret = -ENODEV; // never reached + } + + } while ( (ccode == 1) && retry ); + + } /* endif */ + + } /* endif */ + + return( ret ); +} + + +/* + * Disable IRQ by modifying the subchannel + */ +static int disable_subchannel( unsigned int irq) +{ + int cc; /* condition code */ + int ret; /* function return value */ + int retry = 5; + + if ( irq > highest_subchannel ) + { + ret = -ENODEV; + } + else if ( ioinfo[irq].ui.flags.busy ) + { + /* + * the disable function must not be called while there are + * requests pending for completion ! + */ + ret = -EBUSY; + } + else + { + /* + * If device isn't operational we have to perform delayed + * disabling when the next interrupt occurs - unless the + * irq is re-requested prior to the interrupt to occur. + */ + cc = stsch(irq, &(ioinfo[irq].schib) ); + + if ( cc == 3 ) + { + ioinfo[irq].ui.flags.oper = 0; + ioinfo[irq].ui.flags.d_disable = 1; + + ret = 0; + } + else // cc == 0 + { + ioinfo[irq].schib.pmcw.ena = 0; + + do + { + cc = msch( irq, &(ioinfo[irq].schib) ); + + switch (cc) { + case 0 : + ret = 0; /* done */ + break; + + case 1 : + /* + * very bad, requires interrupt alike processing, where + * "rbh" is a dummy parameter for interface compatibility + * only. Bottom-half handling cannot be required as + * this must be an unsolicited interrupt (!busy). + */ + ioinfo[irq].ui.flags.s_pend = 1; + + s390_process_IRQ( irq, 0 ); + + ioinfo[irq].ui.flags.s_pend = 0; + + ret = -EBUSY; /* might be overwritten on ... */ + /* ... re-driving the msch() */ + retry--; + break; + + case 2 : + /* + * *** must not occur ! *** + * *** *** + * *** indicates our internal interrupt accounting is out *** + * *** of sync ===> panic() *** + */ + panic( "disable_subchannel() : unexpected busy condition !\n"); + ret = -ENODEV; // never reached + break; + + case 3 : + /* + * should hardly occur but not impossible ... + */ + ioinfo[irq].ui.flags.oper = 0; + ioinfo[irq].ui.flags.d_disable = 1; + + ret = 0; /* if the device has gone we don't ... */ + /* ... need to disable it anymore ! */ + break; + + default : + ret = -ENODEV; // never reached ... + break; + + } /* endswitch */ + + } while ( (cc == 1) && retry ); + + } /* endif */ + + } /* endif */ + + return( ret); +} + + + +int s390_setup_irq(unsigned int irq, struct irqaction * new) +{ + unsigned long flags; + int rc = 0; + + /* + * The following block of code has to be executed atomically + */ + s390irq_spin_lock_irqsave( irq, flags); + + if ( irq_desc[irq].action == NULL ) + { + irq_desc[irq].action = new; + irq_desc[irq].status = 0; + irq_desc[irq].handler->enable = &enable_subchannel; + irq_desc[irq].handler->disable = &disable_subchannel; + irq_desc[irq].handler->handle = &handle_IRQ_event; + + ioinfo[irq].ui.flags.ready = 1; + + irq_desc[irq].handler->enable(irq); + } + else + { + /* + * interrupt already owned, and shared interrupts + * aren't supported on S/390. + */ + rc = -EBUSY; + + } /* endif */ + + s390irq_spin_unlock_irqrestore(irq,flags); + + return( rc); +} + + +void s390_init_IRQ( void ) +{ + unsigned long flags; /* PSW flags */ + long cr6 __attribute__ ((aligned (8))); + int irq; /* counter for I/O subchannels */ + + for (irq=0; irqdev_id)->ii.irb, + '\0', sizeof( irb_t) ); + } /* endif */ + + /* + * initialize device status information + */ + ioinfo[irq].ui.flags.busy = 1; + ioinfo[irq].ui.flags.doio = 1; + + ioinfo[irq].devstat.intparm = intparm; + ioinfo[irq].devstat.cstat = 0; + ioinfo[irq].devstat.dstat = 0; + ioinfo[irq].devstat.lpum = 0; + ioinfo[irq].devstat.flag = DEVSTAT_START_FUNCTION; + ioinfo[irq].devstat.scnt = 0; + + ioinfo[irq].ui.flags.fast = 0; + ioinfo[irq].ui.flags.repall = 0; + + /* + * Check for either early (FAST) notification requests + * or if we are to return all interrupt info. + * Default is to call IRQ handler at secondary status only + */ + if ( flag & DOIO_RETURN_CHAN_END ) + { + ioinfo[irq].ui.flags.fast = 1; + } + else if ( flag & DOIO_REPORT_ALL ) + { + ioinfo[irq].ui.flags.repall = 1; + + } /* endif */ + + if ( flag & DOIO_VALID_LPM ) + { + ioinfo[irq].lpm = lpm; /* specific path */ + } + else + { + ioinfo[irq].lpm = 0xff; /* any path */ + + } /* endif */ + + /* + * If synchronous I/O processing is requested, we have + * to wait for the corresponding interrupt to occur by + * polling the interrupt condition. However, as multiple + * interrupts may be outstanding, we must not just wait + * for the first interrupt, but must poll until ours + * pops up. + */ + if ( flag & DOIO_WAIT_FOR_INTERRUPT ) + { + int io_sub; + int io_parm; + psw_t io_new_psw; + int ccode; + + int ready = 0; + struct _lowcore *lc = NULL; + + /* + * We shouldn't perform a TPI loop, waiting for an interrupt + * to occur, but should load a WAIT PSW instead. Otherwise + * we may keep the channel subsystem busy, not able to present + * the interrupt. When our sync. interrupt arrived we reset + * the I/O old PSW to its original value. + */ + memcpy( &io_new_psw, &lc->io_new_psw, sizeof(psw_t)); + + ccode = iac(); + + switch (ccode) { + case 0: // primary-space + io_sync_wait.mask = _IO_PSW_MASK + | _PSW_PRIM_SPACE_MODE + | _PSW_IO_WAIT; + break; + case 1: // secondary-space + io_sync_wait.mask = _IO_PSW_MASK + | _PSW_SEC_SPACE_MODE + | _PSW_IO_WAIT; + break; + case 2: // access-register + io_sync_wait.mask = _IO_PSW_MASK + | _PSW_ACC_REG_MODE + | _PSW_IO_WAIT; + break; + case 3: // home-space + io_sync_wait.mask = _IO_PSW_MASK + | _PSW_HOME_SPACE_MODE + | _PSW_IO_WAIT; + break; + default: + panic( "start_IO() : unexpected " + "address-space-control %d\n", + ccode); + break; + } /* endswitch */ + + io_sync_wait.addr = (unsigned long) &&io_wakeup | 0x80000000L; + + /* + * Martin didn't like modifying the new PSW, now we take + * a fast exit in do_IRQ() instead + */ + *(int *)__LC_SYNC_IO_WORD = 1; + + do + { + + asm volatile ( "lpsw %0" : : "m" (io_sync_wait) ); +io_wakeup: + io_parm = *(int *)__LC_IO_INT_PARM; + io_sub = (int)*(short *)__LC_SUBCHANNEL_NR; + + ready = s390_process_IRQ( io_sub, io_parm); + + } while ( !((io_sub == irq) && (ready == 1)) ); + + *(int *)__LC_SYNC_IO_WORD = 0; + + } /* endif */ + + break; + + case 1 : /* status pending */ + + ioinfo[irq].devstat.flag |= DEVSTAT_STATUS_PENDING; + + /* + * initialize the device driver specific devstat irb area + */ + memset( &((devstat_t *) irq_desc[irq].action->dev_id)->ii.irb, + '\0', sizeof( irb_t) ); + + /* + * Let the common interrupt handler process the pending status. + * However, we must avoid calling the user action handler, as + * it won't be prepared to handle a pending status during do_IO() + * processing inline. This also implies that process_IRQ must + * terminate synchronously - especially if device sensing is + * required. + */ + ioinfo[irq].ui.flags.s_pend = 1; + ioinfo[irq].ui.flags.busy = 1; + ioinfo[irq].ui.flags.doio = 1; + + s390_process_IRQ( irq, intparm ); + + ioinfo[irq].ui.flags.s_pend = 0; + ioinfo[irq].ui.flags.busy = 0; + ioinfo[irq].ui.flags.doio = 0; + ioinfo[irq].ui.flags.repall = 0; + ioinfo[irq].ui.flags.w4final = 0; + + ioinfo[irq].devstat.flag |= DEVSTAT_FINAL_STATUS; + + /* + * In multipath mode a condition code 3 implies the last path + * has gone, except we have previously restricted the I/O to + * a particular path. A condition code 1 (0 won't occur) + * results in return code EIO as well as 3 with another path + * than the one used (i.e. path available mask is non-zero). + */ + if ( ioinfo[irq].devstat.ii.irb.scsw.cc == 3 ) + { + ret = -ENODEV; + ioinfo[irq].devstat.flag |= DEVSTAT_NOT_OPER; + ioinfo[irq].ui.flags.oper = 0; + +#if CONFIG_DEBUG_IO + { + char buffer[80]; + + stsch(irq, &(ioinfo[irq].schib) ); + + sprintf( buffer, "s390_start_IO(%04X) - irb for " + "device %04X, after status pending\n", + irq, + ioinfo[irq].devstat.devno ); + + s390_displayhex( buffer, + &(ioinfo[irq].devstat.ii.irb) , + sizeof(irb_t)); + + sprintf( buffer, "s390_start_IO(%04X) - schib for " + "device %04X, after status pending\n", + irq, + ioinfo[irq].devstat.devno ); + + s390_displayhex( buffer, + &(ioinfo[irq].schib) , + sizeof(schib_t)); + + + if (ioinfo[irq].devstat.flag & DEVSTAT_FLAG_SENSE_AVAIL) + { + sprintf( buffer, "s390_start_IO(%04X) - sense data for " + "device %04X, after status pending\n", + irq, + ioinfo[irq].devstat.devno ); + + s390_displayhex( buffer, + ((devstat_t *)(irq_desc[irq].action->dev_id))->ii.sense.data, + ((devstat_t *)(irq_desc[irq].action->dev_id))->rescnt); + + } + } +#endif + } + else + { + ret = -EIO; + ioinfo[irq].devstat.flag &= ~DEVSTAT_NOT_OPER; + ioinfo[irq].ui.flags.oper = 1; + + } /* endif */ + + break; + + case 2 : /* busy */ + + ret = -EBUSY; + break; + + default: /* device not operational */ + + ret = -ENODEV; + ioinfo[irq].ui.flags.oper = 0; + + ioinfo[irq].devstat.flag |= DEVSTAT_NOT_OPER; + ioinfo[irq].devstat.intparm = intparm; + + memcpy( irq_desc[irq].action->dev_id, + &(ioinfo[irq].devstat), + sizeof( devstat_t) ); + +#if CONFIG_DEBUG_IO + { + char buffer[80]; + + stsch(irq, &(ioinfo[irq].schib) ); + + sprintf( buffer, "s390_start_IO(%04X) - schib for " + "device %04X, after 'not oper' status\n", + irq, + ioinfo[irq].devstat.devno ); + + s390_displayhex( buffer, + &(ioinfo[irq].schib), + sizeof(schib_t)); + } +#endif + break; + + } /* endswitch */ + + if ( ( flag & DOIO_WAIT_FOR_INTERRUPT ) + && ( sync_isc_locked ) ) + { + disable_cpu_sync_isc( irq ); + + spin_unlock_irqrestore( &sync_isc, psw_flags); + + sync_isc_locked = 0; // local setting + ioinfo[irq].ui.flags.syncio = 0; // global setting + + } /* endif */ + + return( ret); +} + +int do_IO( int irq, /* IRQ */ + ccw1_t *cpa, /* channel program address */ + unsigned long intparm, /* interruption parameter */ + unsigned char lpm, /* logical path mask */ + unsigned long flag) /* flags : see above */ +{ + int ret = 0; + + if ( irq > highest_subchannel || irq < 0 ) + { + return( -ENODEV ); + + } /* endif */ + + /* handler registered ? */ + if ( !ioinfo[irq].ui.flags.ready ) + { + return( -ENODEV ); + + } /* endif */ + + /* + * Note: We ignore the device operational status - if not operational, + * the SSCH will lead to an -ENODEV condition ... + */ + if ( !ioinfo[irq].ui.flags.busy ) /* last I/O completed ? */ + { + ret = s390_start_IO( irq, cpa, intparm, lpm, flag); + } + else if ( ioinfo[irq].ui.flags.fast ) + { + /* + * If primary status was received and ending status is missing, the + * device driver won't be notified on the ending status if early + * (fast) interrupt notification was requested. Therefore we have + * to queue the next incoming request. If halt_IO() is issued while + * there is a request queued, a HSCH needs to be issued and the queued + * request must be deleted but its intparm must be returned (see + * halt_IO() processing) + */ + if ( ioinfo[irq].ui.flags.w4final && !ioinfo[irq].ui.flags.doio_q ) + { + ioinfo[irq].qflag = flag; + ioinfo[irq].qcpa = cpa; + ioinfo[irq].qintparm = intparm; + ioinfo[irq].qlpm = lpm; + } + else + { + ret = -EBUSY; + + } /* endif */ + } + else + { + ret = -EBUSY; + + } /* endif */ + + return( ret ); + +} + +/* + * resume suspended I/O operation + */ +int resume_IO( int irq) +{ + int ret = 0; + + if ( irq > highest_subchannel || irq < 0 ) + { + return( -ENODEV ); + + } /* endif */ + + /* + * We allow for 'resume' requests only for active I/O operations + */ + if ( ioinfo[irq].ui.flags.busy ) + { + int ccode; + + ccode = rsch( irq); + + switch (ccode) { + case 0 : + break; + + case 1 : + s390_process_IRQ( irq, + ioinfo[irq].devstat.intparm ); + ret = -EBUSY; + break; + + case 2 : + ret = -EINVAL; + break; + + case 3 : + /* + * useless to wait for request completion + * as device is no longer operational ! + */ + ioinfo[irq].ui.flags.oper = 0; + ioinfo[irq].ui.flags.busy = 0; + ret = -ENODEV; + break; + + } /* endswitch */ + + } + else + { + ret = -ENOTCONN; + + } /* endif */ + + return( ret); +} + +/* + * Note: The "intparm" parameter is not used by the halt_IO() function + * itself, as no ORB is built for the HSCH instruction. However, + * it allows the device interrupt handler to associate the upcoming + * interrupt with the halt_IO() request. + */ +int halt_IO( int irq, + int intparm, + unsigned int flag) /* possible DOIO_WAIT_FOR_INTERRUPT */ +{ + int ret; + int ccode; + unsigned long psw_flags; + + int sync_isc_locked = 0; + + if ( irq > highest_subchannel || irq < 0 ) + { + ret = -ENODEV; + } + /* + * we only allow for halt_IO if the device has an I/O handler associated + */ + else if ( !ioinfo[irq].ui.flags.ready ) + { + ret = -ENODEV; + } + /* + * we ignore the halt_io() request if ending_status was received but + * a SENSE operation is waiting for completion. + */ + else if ( ioinfo[irq].ui.flags.w4sense ) + { + ret = 0; + } + /* + * We don't allow for halt_io with a sync do_IO() requests pending. + */ + else if ( ioinfo[irq].ui.flags.syncio ) + { + ret = -EBUSY; + } + else + { + /* + * If sync processing was requested we lock the sync ISC, modify the + * device to present interrupts for this ISC only and switch the + * CPU to handle this ISC + the console ISC exclusively. + */ + if ( flag & DOIO_WAIT_FOR_INTERRUPT ) + { + // + // check whether we run recursively (sense processing) + // + if ( !ioinfo[irq].ui.flags.syncio ) + { + spin_lock_irqsave( &sync_isc, psw_flags); + + ret = enable_cpu_sync_isc( irq); + + if ( ret ) + { + spin_unlock_irqrestore( &sync_isc, psw_flags); + + // sigh, there should be a single exit point only ... + return( ret); + } + else + { + sync_isc_locked = 1; // local setting + ioinfo[irq].ui.flags.syncio = 1; // global setting + + } /* endif */ + + } /* endif */ + + } /* endif */ + + /* + * Issue "Halt subchannel" and process condition code + */ + ccode = hsch( irq ); + + switch ( ccode ) { + case 0: + + ioinfo[irq].ui.flags.haltio = 1; + + if ( !ioinfo[irq].ui.flags.doio ) + { + ioinfo[irq].ui.flags.busy = 1; + ioinfo[irq].devstat.intparm = intparm; + ioinfo[irq].devstat.cstat = 0; + ioinfo[irq].devstat.dstat = 0; + ioinfo[irq].devstat.lpum = 0; + ioinfo[irq].devstat.flag = DEVSTAT_HALT_FUNCTION; + ioinfo[irq].devstat.scnt = 0; + + } + else + { + ioinfo[irq].devstat.flag |= DEVSTAT_HALT_FUNCTION; + + } /* endif */ + + /* + * If synchronous I/O processing is requested, we have + * to wait for the corresponding interrupt to occur by + * polling the interrupt condition. However, as multiple + * interrupts may be outstanding, we must not just wait + * for the first interrupt, but must poll until ours + * pops up. + */ + if ( flag & DOIO_WAIT_FOR_INTERRUPT ) + { + int io_sub; + int io_parm; + psw_t io_new_psw; + int ccode; + + int ready = 0; + struct _lowcore *lc = NULL; + + /* + * We shouldn't perform a TPI loop, waiting for an interrupt + * to occur, but should load a WAIT PSW instead. Otherwise + * we may keep the channel subsystem busy, not able to present + * the interrupt. When our sync. interrupt arrived we reset + * the I/O old PSW to its original value. + */ + memcpy( &io_new_psw, &lc->io_new_psw, sizeof(psw_t)); + + ccode = iac(); + + switch (ccode) { + case 0: // primary-space + io_sync_wait.mask = _IO_PSW_MASK + | _PSW_PRIM_SPACE_MODE + | _PSW_IO_WAIT; + break; + case 1: // secondary-space + io_sync_wait.mask = _IO_PSW_MASK + | _PSW_SEC_SPACE_MODE + | _PSW_IO_WAIT; + break; + case 2: // access-register + io_sync_wait.mask = _IO_PSW_MASK + | _PSW_ACC_REG_MODE + | _PSW_IO_WAIT; + break; + case 3: // home-space + io_sync_wait.mask = _IO_PSW_MASK + | _PSW_HOME_SPACE_MODE + | _PSW_IO_WAIT; + break; + default: + panic( "halt_IO() : unexpected " + "address-space-control %d\n", + ccode); + break; + } /* endswitch */ + + io_sync_wait.addr = (unsigned long) &&hio_wakeup | 0x80000000L; + + /* + * Martin didn't like modifying the new PSW, now we take + * a fast exit in do_IRQ() instead + */ + *(int *)__LC_SYNC_IO_WORD = 1; + + do + { + + asm volatile ( "lpsw %0" : : "m" (io_sync_wait) ); +hio_wakeup: + io_parm = *(int *)__LC_IO_INT_PARM; + io_sub = (int)*(__u16 *)__LC_SUBCHANNEL_NR; + + ready = s390_process_IRQ( io_sub, io_parm); + + } while ( !((io_sub == irq) && (ready == 1)) ); + + *(int *)__LC_SYNC_IO_WORD = 0; + + } /* endif */ + + ret = 0; + break; + + case 1 : /* status pending */ + + ioinfo[irq].devstat.flag |= DEVSTAT_STATUS_PENDING; + + /* + * initialize the device driver specific devstat irb area + */ + memset( &((devstat_t *) irq_desc[irq].action->dev_id)->ii.irb, + '\0', sizeof( irb_t) ); + + /* + * Let the common interrupt handler process the pending status. + * However, we must avoid calling the user action handler, as + * it won't be prepared to handle a pending status during do_IO() + * processing inline. This also implies that s390_process_IRQ must + * terminate synchronously - especially if device sensing is + * required. + */ + ioinfo[irq].ui.flags.s_pend = 1; + ioinfo[irq].ui.flags.busy = 1; + ioinfo[irq].ui.flags.doio = 1; + + s390_process_IRQ( irq, intparm ); + + ioinfo[irq].ui.flags.s_pend = 0; + ioinfo[irq].ui.flags.busy = 0; + ioinfo[irq].ui.flags.doio = 0; + ioinfo[irq].ui.flags.repall = 0; + ioinfo[irq].ui.flags.w4final = 0; + + ioinfo[irq].devstat.flag |= DEVSTAT_FINAL_STATUS; + + /* + * In multipath mode a condition code 3 implies the last path + * has gone, except we have previously restricted the I/O to + * a particular path. A condition code 1 (0 won't occur) + * results in return code EIO as well as 3 with another path + * than the one used (i.e. path available mask is non-zero). + */ + if ( ioinfo[irq].devstat.ii.irb.scsw.cc == 3 ) + { + ret = -ENODEV; + ioinfo[irq].devstat.flag |= DEVSTAT_NOT_OPER; + ioinfo[irq].ui.flags.oper = 0; + } + else + { + ret = -EIO; + ioinfo[irq].devstat.flag &= ~DEVSTAT_NOT_OPER; + ioinfo[irq].ui.flags.oper = 1; + + } /* endif */ + + break; + + case 2 : /* busy */ + + ret = -EBUSY; + break; + + default: /* device not operational */ + + ret = -ENODEV; + break; + + } /* endswitch */ + + if ( ( flag & DOIO_WAIT_FOR_INTERRUPT ) + && ( sync_isc_locked ) ) + { + sync_isc_locked = 0; // local setting + ioinfo[irq].ui.flags.syncio = 0; // global setting + + disable_cpu_sync_isc( irq ); + + spin_unlock_irqrestore( &sync_isc, psw_flags); + + } /* endif */ + + } /* endif */ + + return( ret ); +} + + +/* + * do_IRQ() handles all normal I/O device IRQ's (the special + * SMP cross-CPU interrupts have their own specific + * handlers). + * + * Returns: 0 - no ending status received, no further action taken + * 1 - interrupt handler was called with ending status + */ +asmlinkage void do_IRQ( struct pt_regs regs, + unsigned int irq, + unsigned int intparm ) +{ +#ifdef CONFIG_FAST_IRQ + int ccode; + tpi_info_t tpi_info; + int new_irq; +#endif + int use_irq = irq; + long use_intparm = intparm; + + /* + * take fast exit if CPU is in sync. I/O state + * + * Note: we have to turn off the WAIT bit and re-disable + * interrupts prior to return as this was the initial + * entry condition to synchronous I/O. + */ + if ( *(int *)__LC_SYNC_IO_WORD ) + { + regs.psw.mask &= ~(_PSW_WAIT_MASK_BIT | _PSW_IO_MASK_BIT); + + return; + + } /* endif */ + + s390irq_spin_lock(use_irq); + +#ifdef CONFIG_FAST_IRQ + do { +#endif /* CONFIG_FAST_IRQ */ + + s390_process_IRQ( use_irq, use_intparm); + +#ifdef CONFIG_FAST_IRQ + + /* + * more interrupts pending ? + */ + ccode = tpi( &tpi_info ); + + if ( ! ccode ) + break; // no, leave ... + + new_irq = tpi_info.irq; + use_intparm = tpi_info.intparm; + + /* + * if the interrupt is for a different irq we + * release the current irq lock and obtain + * a new one ... + */ + if ( new_irq != use_irq ) + { + s390irq_spin_unlock(use_irq); + use_irq = new_irq; + s390irq_spin_lock(use_irq); + + } /* endif */ + + } while ( 1 ); + +#endif /* CONFIG_FAST_IRQ */ + + s390irq_spin_unlock(use_irq); + + return; +} + +/* + * s390_process_IRQ() handles status pending situations and interrupts + * + * Called by : do_IRQ() - for "real" interrupts + * s390_start_IO, halt_IO() + * - status pending cond. after SSCH, or HSCH + * disable_subchannel() - status pending conditions (after MSCH) + * + * Returns: 0 - no ending status received, no further action taken + * 1 - interrupt handler was called with ending status + */ +int s390_process_IRQ( unsigned int irq, + unsigned int intparm) +{ + int ccode; /* condition code from tsch() operation */ + int irb_cc; /* condition code from irb */ + int sdevstat; /* effective struct devstat size to copy */ + unsigned int fctl; /* function control */ + unsigned int stctl; /* status control */ + unsigned int actl; /* activity control */ + struct irqaction *action; + struct pt_regs regs; /* for interface compatibility only */ + + int issense = 0; + int ending_status = 0; + int allow4handler = 1; + int cpu = smp_processor_id(); + + kstat.irqs[cpu][irq]++; + + action = irq_desc[irq].action; + + /* + * It might be possible that a device was not-oper. at the time + * of free_irq() processing. This means the handler is no longer + * available when the device possibly becomes ready again. In + * this case we perform delayed disable_subchannel() processing. + */ + if ( action == NULL ) + { + if ( !ioinfo[irq].ui.flags.d_disable ) + { + panic( "do_IRQ() : error, no irq_action available !\n"); + + } /* endif */ + + } /* endif */ + + /* + * retrieve the i/o interrupt information (irb), + * update the device specific status information + * and possibly call the interrupt handler. + * + * Note 1: At this time we don't process the resulting + * condition code (ccode) from tsch(), although + * we probably should. + * + * Note 2: Here we will have to check for channel + * check conditions and call a channel check + * handler. + * + * Note 3: If a start function was issued, the interruption + * parameter relates to it. If a halt function was + * issued for an idle device, the intparm must not + * be taken from lowcore, but from the devstat area. + */ + ccode = tsch( irq, &(ioinfo[irq].devstat.ii.irb) ); + + // + // We must only accumulate the status if initiated by do_IO() or halt_IO() + // + if ( ioinfo[irq].ui.flags.busy ) + { + ioinfo[irq].devstat.dstat |= ioinfo[irq].devstat.ii.irb.scsw.dstat; + ioinfo[irq].devstat.cstat |= ioinfo[irq].devstat.ii.irb.scsw.cstat; + } + else + { + ioinfo[irq].devstat.dstat = ioinfo[irq].devstat.ii.irb.scsw.dstat; + ioinfo[irq].devstat.cstat = ioinfo[irq].devstat.ii.irb.scsw.cstat; + + ioinfo[irq].devstat.flag = 0; // reset status flags + + } /* endif */ + + ioinfo[irq].devstat.lpum = ioinfo[irq].devstat.ii.irb.esw.esw1.lpum; + + if ( ioinfo[irq].ui.flags.doio) + { + ioinfo[irq].devstat.intparm = intparm; + + } /* endif */ + + /* + * reset device-busy bit if no longer set in irb + */ + if ( (ioinfo[irq].devstat.dstat & DEV_STAT_BUSY ) + && ((ioinfo[irq].devstat.ii.irb.scsw.dstat & DEV_STAT_BUSY) == 0)) + { + ioinfo[irq].devstat.dstat &= ~DEV_STAT_BUSY; + + } /* endif */ + + /* + * Save residual count and CCW information in case primary and + * secondary status are presented with different interrupts. + */ + if ( ioinfo[irq].devstat.ii.irb.scsw.stctl & SCSW_STCTL_PRIM_STATUS ) + { + ioinfo[irq].devstat.rescnt = ioinfo[irq].devstat.ii.irb.scsw.count; + +#if CONFIG_DEBUG_IO + if ( irq != cons_dev ) + printk( "s390_process_IRQ( %04X ) : " + "residual count from irb after tsch() %d\n", + irq, ioinfo[irq].devstat.rescnt ); +#endif + } /* endif */ + + if ( ioinfo[irq].devstat.ii.irb.scsw.cpa != 0 ) + { + ioinfo[irq].devstat.cpa = ioinfo[irq].devstat.ii.irb.scsw.cpa; + + } /* endif */ + + irb_cc = ioinfo[irq].devstat.ii.irb.scsw.cc; + + // + // check for any kind of channel or interface control check but don't + // issue the message for the console device + // + if ( (ioinfo[irq].devstat.ii.irb.scsw.cstat + & ( SCHN_STAT_CHN_DATA_CHK + | SCHN_STAT_CHN_CTRL_CHK + | SCHN_STAT_INTF_CTRL_CHK ) ) + && (irq != cons_dev ) ) + { + printk( "Channel-Check or Interface-Control-Check " + "received\n" + " ... device %X on subchannel %X, dev_stat " + ": %X sch_stat : %X\n", + ioinfo[irq].devstat.devno, + irq, + ioinfo[irq].devstat.dstat, + ioinfo[irq].devstat.cstat); + + } /* endif */ + + issense = ioinfo[irq].devstat.ii.irb.esw.esw0.erw.cons; + + if ( issense ) + { + ioinfo[irq].devstat.scnt = + ioinfo[irq].devstat.ii.irb.esw.esw0.erw.scnt; + ioinfo[irq].devstat.flag |= + DEVSTAT_FLAG_SENSE_AVAIL; + + sdevstat = sizeof( devstat_t); + +#if CONFIG_DEBUG_IO + if ( irq != cons_dev ) + printk( "s390_process_IRQ( %04X ) : " + "concurrent sense bytes avail %d\n", + irq, ioinfo[irq].devstat.scnt ); +#endif + } + else + { + /* don't copy the sense data area ! */ + sdevstat = sizeof( devstat_t) - SENSE_MAX_COUNT; + + } /* endif */ + + switch ( irb_cc ) { + case 1: /* status pending */ + + ioinfo[irq].devstat.flag |= DEVSTAT_STATUS_PENDING; + + case 0: /* normal i/o interruption */ + + fctl = ioinfo[irq].devstat.ii.irb.scsw.fctl; + stctl = ioinfo[irq].devstat.ii.irb.scsw.stctl; + actl = ioinfo[irq].devstat.ii.irb.scsw.actl; + + + ioinfo[irq].stctl |= stctl; + + ending_status = ( stctl & SCSW_STCTL_SEC_STATUS ) + || ( stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND) ) + || ( (fctl == SCSW_FCTL_HALT_FUNC) && (stctl == SCSW_STCTL_STATUS_PEND) ); + + /* + * Check for unsolicited interrupts - for debug purposes only + * + * We only consider an interrupt as unsolicited, if the device was not + * actively in use (busy) and an interrupt other than an ALERT status + * was received. + * + * Note: We must not issue a message to the console, if the + * unsolicited interrupt applies to the console device + * itself ! + */ +#if CONFIG_DEBUG_IO + if ( ( irq != cons_dev ) + && !( stctl & SCSW_STCTL_ALERT_STATUS ) + && ( ioinfo[irq].ui.flags.busy == 0 ) ) + { + char buffer[80]; + + printk( "Unsolicited interrupt received for device %04X on subchannel %04X\n" + " ... device status : %02X subchannel status : %02X\n", + ioinfo[irq].devstat.devno, + irq, + ioinfo[irq].devstat.dstat, + ioinfo[irq].devstat.cstat); + + sprintf( buffer, "s390_process_IRQ(%04X) - irb for " + "device %04X, ending_status %d\n", + irq, + ioinfo[irq].devstat.devno, + ending_status); + + s390_displayhex( buffer, + &(ioinfo[irq].devstat.ii.irb) , + sizeof(irb_t)); + + } /* endif */ +#endif + /* + * Check whether we must issue a SENSE CCW ourselves if there is no + * concurrent sense facility installed for the subchannel. + * + * Note: We should check for ioinfo[irq].ui.flags.consns but VM + * violates the ESA/390 architecture and doesn't present an + * operand exception for virtual devices without concurrent + * sense facility available/supported when enabling the + * concurrent sense facility. + */ + if ( ( ( ioinfo[irq].devstat.ii.irb.scsw.dstat & DEV_STAT_UNIT_CHECK ) + && ( !issense ) ) + || ( ioinfo[irq].ui.flags.delsense && ending_status ) ) + { + int ret_io; + ccw1_t *s_ccw = &ioinfo[irq].senseccw; + unsigned long s_flag = 0; + + if (ending_status) + { + /* + * We copy the current status information into the device driver + * status area. Then we can use the local devstat area for device + * sensing. When finally calling the IRQ handler we must not overlay + * the original device status but copy the sense data only. + */ + memcpy( irq_desc[irq].action->dev_id, + &(ioinfo[irq].devstat), + sizeof( devstat_t) ); + + s_ccw->cmd_code = CCW_CMD_BASIC_SENSE; + s_ccw->cda = (char *)virt_to_phys( ioinfo[irq].devstat.ii.sense.data); + s_ccw->count = SENSE_MAX_COUNT; + s_ccw->flags = CCW_FLAG_SLI; + + /* + * If free_irq() or a sync do_IO/s390_start_IO() is in + * process we have to sense synchronously + */ + if ( ioinfo[irq].ui.flags.unready || ioinfo[irq].ui.flags.syncio ) + { + s_flag = DOIO_WAIT_FOR_INTERRUPT; + + } /* endif */ + + /* + * Reset status info + * + * It does not matter whether this is a sync. or async. + * SENSE request, but we have to assure we don't call + * the irq handler now, but keep the irq in busy state. + * In sync. mode s390_process_IRQ() is called recursively, + * while in async. mode we re-enter do_IRQ() with the + * next interrupt. + * + * Note : this may be a delayed sense request ! + */ + allow4handler = 0; + + ioinfo[irq].ui.flags.fast = 0; + ioinfo[irq].ui.flags.repall = 0; + ioinfo[irq].ui.flags.w4final = 0; + ioinfo[irq].ui.flags.delsense = 0; + + ioinfo[irq].devstat.cstat = 0; + ioinfo[irq].devstat.dstat = 0; + ioinfo[irq].devstat.rescnt = SENSE_MAX_COUNT; + + ioinfo[irq].ui.flags.w4sense = 1; + + ret_io = s390_start_IO( irq, + s_ccw, + 0xE2C5D5E2, // = SENSe + 0, // n/a + s_flag); + } + else + { + /* + * we received an Unit Check but we have no final + * status yet, therefore we must delay the SENSE + * processing. However, we must not report this + * intermediate status to the device interrupt + * handler. + */ + ioinfo[irq].ui.flags.fast = 0; + ioinfo[irq].ui.flags.repall = 0; + + ioinfo[irq].ui.flags.delsense = 1; + allow4handler = 0; + + } /* endif */ + + } /* endif */ + + /* + * we allow for the device action handler if . + * - we received ending status + * - the action handler requested to see all interrupts + * - we received a PCI + * - fast notification was requested (primary status) + * - unsollicited interrupts + * + */ + if ( allow4handler ) + { + allow4handler = ending_status + || ( ioinfo[irq].ui.flags.repall ) + || ( ioinfo[irq].devstat.ii.irb.scsw.cstat & SCHN_STAT_PCI ) + || ( (ioinfo[irq].ui.flags.fast ) && (stctl & SCSW_STCTL_PRIM_STATUS) ) + || ( ioinfo[irq].ui.flags.oper == 0 ); + } + + /* + * We used to copy the device status information right before + * calling the device action handler. However, in status + * pending situations during do_IO() or halt_IO(), as well as + * enable_subchannel/disable_subchannel processing we must + * synchronously return the status information and must not + * call the device action handler. + * + */ + if ( allow4handler ) + { + /* + * if we were waiting for sense data we copy the sense + * bytes only as the original status information was + * saved prior to sense already. + */ + if ( ioinfo[irq].ui.flags.w4sense ) + { + int sense_count = SENSE_MAX_COUNT-ioinfo[irq].devstat.rescnt; + +#if CONFIG_DEBUG_IO + if ( irq != cons_dev ) + printk( "s390_process_IRQ( %04X ) : " + "BASIC SENSE bytes avail %d\n", + irq, sense_count ); +#endif + ioinfo[irq].ui.flags.w4sense = 0; + ((devstat_t *)(action->dev_id))->flag |= DEVSTAT_FLAG_SENSE_AVAIL; + ((devstat_t *)(action->dev_id))->scnt = sense_count; + + if (sense_count >= 0) + memcpy( ((devstat_t *)(action->dev_id))->ii.sense.data, + &(ioinfo[irq].devstat.ii.sense.data), sense_count); + else +#if 0 + panic( "s390_process_IRQ(%04x) encountered " + "negative sense count\n", + irq); +#else + printk( "s390_process_IRQ(%04x) encountered " + "negative sense count\n", + irq); +#endif + } + else + { + memcpy( action->dev_id, &(ioinfo[irq].devstat), sdevstat ); + + } /* endif */ + + } /* endif */ + + /* + * for status pending situations other than deferred interrupt + * conditions detected by s390_process_IRQ() itself we must not + * call the handler. + */ + if ( ioinfo[irq].ui.flags.s_pend ) + allow4handler = 0; + + /* + * Call device action handler if applicable + */ + if ( allow4handler ) + { + + /* + * We only reset the busy condition when we are sure that no further + * interrupt is pending for the current I/O request (ending_status). + */ + if ( ending_status || !ioinfo[irq].ui.flags.oper ) + { + ioinfo[irq].ui.flags.oper = 1; /* dev IS oper */ + + ioinfo[irq].ui.flags.busy = 0; + ioinfo[irq].ui.flags.doio = 0; + ioinfo[irq].ui.flags.haltio = 0; + ioinfo[irq].ui.flags.fast = 0; + ioinfo[irq].ui.flags.repall = 0; + ioinfo[irq].ui.flags.w4final = 0; + + ioinfo[irq].devstat.flag |= DEVSTAT_FINAL_STATUS; + ((devstat_t *)(action->dev_id))->flag |= DEVSTAT_FINAL_STATUS; + + action->handler( irq, action->dev_id, ®s); + + // + // reset intparm after final status or we will badly present unsolicited + // interrupts with a intparm value possibly no longer valid. + // + ioinfo[irq].devstat.intparm = 0; + + // + // Was there anything queued ? Start the pending channel program + // if there is one. + // + if ( ioinfo[irq].ui.flags.doio_q ) + { + int ret; + + ret = s390_start_IO( irq, + ioinfo[irq].qcpa, + ioinfo[irq].qintparm, + ioinfo[irq].qlpm, + ioinfo[irq].qflag); + + ioinfo[irq].ui.flags.doio_q = 0; + + /* + * If s390_start_IO() failed call the device's interrupt + * handler, the IRQ related devstat area was setup by + * s390_start_IO() accordingly already (status pending + * condition). + */ + if ( ret ) + { + action->handler( irq, action->dev_id, ®s); + + } /* endif */ + + } /* endif */ + + } + else + { + ioinfo[irq].ui.flags.w4final = 1; + action->handler( irq, action->dev_id, ®s); + + } /* endif */ + + } /* endif */ + + break; + + case 3: /* device not operational */ + + ioinfo[irq].ui.flags.oper = 0; + + ioinfo[irq].ui.flags.busy = 0; + ioinfo[irq].ui.flags.doio = 0; + ioinfo[irq].ui.flags.haltio = 0; + + ioinfo[irq].devstat.cstat = 0; + ioinfo[irq].devstat.dstat = 0; + ioinfo[irq].devstat.flag |= DEVSTAT_NOT_OPER; + ioinfo[irq].devstat.flag |= DEVSTAT_FINAL_STATUS; + + /* + * When we find a device "not oper" we save the status + * information into the device status area and call the + * device specific interrupt handler. + * + * Note: currently we don't have any way to reenable + * the device unless an unsolicited interrupt + * is presented. We don't check for spurious + * interrupts on "not oper" conditions. + */ + + if ( ioinfo[irq].ui.flags.fast && ioinfo[irq].ui.flags.w4final ) + { + /* + * If a new request was queued already, we have + * to simulate the "not oper" status for the + * queued request by switching the "intparm" value + * and notify the interrupt handler. + */ + if ( ioinfo[irq].ui.flags.doio_q ) + { + ioinfo[irq].devstat.intparm = ioinfo[irq].qintparm; + + } /* endif */ + + } /* endif */ + + ioinfo[irq].ui.flags.fast = 0; + ioinfo[irq].ui.flags.repall = 0; + ioinfo[irq].ui.flags.w4final = 0; + + memcpy( action->dev_id, &(ioinfo[irq].devstat), sdevstat ); + + ioinfo[irq].devstat.intparm = 0; + + if ( !ioinfo[irq].ui.flags.s_pend ) + action->handler( irq, action->dev_id, ®s); + + ending_status = 1; + + break; + + } /* endswitch */ + + return( ending_status ); +} + +/* + * Set the special i/o-interruption sublass 7 for the + * device specified by parameter irq. There can only + * be a single device been operated on this special + * isc. This function is aimed being able to check + * on special device interrupts in disabled state, + * without having to delay I/O processing (by queueing) + * for non-console devices. + * + * Setting of this isc is done by set_cons_dev(), while + * reset_cons_dev() resets this isc and re-enables the + * default isc3 for this device. wait_cons_dev() allows + * to actively wait on an interrupt for this device in + * disabed state. When the interrupt condition is + * encountered, wait_cons_dev(9 calls do_IRQ() to have + * the console device driver processing the interrupt. + */ +int set_cons_dev( int irq ) +{ + int ccode; + unsigned long cr6 __attribute__ ((aligned (8))); + int rc = 0; + + if ( cons_dev != -1 ) + { + rc = -EBUSY; + } + else if ( (irq > highest_subchannel) || (irq < 0) ) + { + rc = -ENODEV; + } + else + { + /* + * modify the indicated console device to operate + * on special console interrupt sublass 7 + */ + ccode = stsch( irq, &(ioinfo[irq].schib) ); + + if (ccode) + { + rc = -ENODEV; + ioinfo[irq].devstat.flag |= DEVSTAT_NOT_OPER; + } + else + { + ioinfo[irq].schib.pmcw.isc = 7; + + ccode = msch( irq, &(ioinfo[irq].schib) ); + + if (ccode) + { + rc = -EIO; + } + else + { + cons_dev = irq; + + /* + * enable console I/O-interrupt sublass 7 + */ + asm volatile ("STCTL 6,6,%0": "=m" (cr6)); + cr6 |= 0x01000000; + asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory"); + + } /* endif */ + + } /* endif */ + + } /* endif */ + + return( rc); +} + +int reset_cons_dev( int irq) +{ + int rc = 0; + int ccode; + long cr6 __attribute__ ((aligned (8))); + + if ( cons_dev != -1 ) + { + rc = -EBUSY; + } + else if ( (irq > highest_subchannel) || (irq < 0) ) + { + rc = -ENODEV; + } + else + { + /* + * reset the indicated console device to operate + * on default console interrupt sublass 3 + */ + ccode = stsch( irq, &(ioinfo[irq].schib) ); + + if (ccode) + { + rc = -ENODEV; + ioinfo[irq].devstat.flag |= DEVSTAT_NOT_OPER; + } + else + { + + ioinfo[irq].schib.pmcw.isc = 3; + + ccode = msch( irq, &(ioinfo[irq].schib) ); + + if (ccode) + { + rc = -EIO; + } + else + { + cons_dev = -1; + + /* + * disable special console I/O-interrupt sublass 7 + */ + asm volatile ("STCTL 6,6,%0": "=m" (cr6)); + cr6 &= 0xFEFFFFFF; + asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory"); + + } /* endif */ + + } /* endif */ + + } /* endif */ + + return( rc); +} + +int wait_cons_dev( int irq ) +{ + int rc = 0; + long save_cr6; + + if ( irq == cons_dev ) + { + + /* + * before entering the spinlock we may already have + * processed the interrupt on a different CPU ... + */ + if ( ioinfo[irq].ui.flags.busy == 1 ) + { + long cr6 __attribute__ ((aligned (8))); + + /* + * disable all, but isc 7 (console device) + */ + asm volatile ("STCTL 6,6,%0": "=m" (cr6)); + save_cr6 = cr6; + cr6 &= 0x01FFFFFF; + asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory"); + + do { + tpi_info_t tpi_info; + if (tpi(&tpi_info) == 1) { + s390_process_IRQ(tpi_info.irq, + tpi_info.intparm); + } else { + s390irq_spin_unlock(irq); + tod_wait(100); + s390irq_spin_lock(irq); + } + eieio(); + } while (ioinfo[irq].ui.flags.busy == 1); + + /* + * restore previous isc value + */ + asm volatile ("STCTL 6,6,%0": "=m" (cr6)); + cr6 = save_cr6; + asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory"); + + } /* endif */ + + } + else + { + rc = EINVAL; + + } /* endif */ + + + return(rc); +} + + +int enable_cpu_sync_isc( int irq ) +{ + int ccode; + long cr6 __attribute__ ((aligned (8))); + + int count = 0; + int rc = 0; + + if ( irq <= highest_subchannel ) + { + ccode = stsch( irq, &(ioinfo[irq].schib) ); + + if ( !ccode ) + { + ioinfo[irq].schib.pmcw.isc = 5; + + do + { + ccode = msch( irq, &(ioinfo[irq].schib) ); + + if (ccode == 0 ) + { + /* + * enable interrupt subclass in CPU + */ + asm volatile ("STCTL 6,6,%0": "=m" (cr6)); + cr6 |= 0x04000000; // enable sync isc 5 + cr6 &= 0xEFFFFFFF; // disable standard isc 3 + asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory"); + } + else if (ccode == 3) + { + rc = -ENODEV; // device not-oper - very unlikely + + } + else if (ccode == 2) + { + rc = -EBUSY; // device busy - should not happen + + } + else if (ccode == 1) + { + // + // process pending status + // + ioinfo[irq].ui.flags.s_pend = 1; + + s390_process_IRQ( irq, 0 ); + + ioinfo[irq].ui.flags.s_pend = 0; + + count++; + + } /* endif */ + + } while ( ccode == 1 && count < 3 ); + + if ( count == 3) + { + rc = -EIO; + + } /* endif */ + } + else + { + rc = -ENODEV; // device is not-operational + + } /* endif */ + } + else + { + rc = -EINVAL; + + } /* endif */ + + return( rc); +} + +int disable_cpu_sync_isc( int irq) +{ + int rc = 0; + int ccode; + long cr6 __attribute__ ((aligned (8))); + + if ( irq <= highest_subchannel ) + { + ccode = stsch( irq, &(ioinfo[irq].schib) ); + + ioinfo[irq].schib.pmcw.isc = 3; + + ccode = msch( irq, &(ioinfo[irq].schib) ); + + if (ccode) + { + rc = -EIO; + } + else + { + + /* + * enable interrupt subclass in CPU + */ + asm volatile ("STCTL 6,6,%0": "=m" (cr6)); + cr6 &= 0xFBFFFFFF; // disable sync isc 5 + cr6 |= 0x10000000; // enable standard isc 3 + asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory"); + + } /* endif */ + + } + else + { + rc = -EINVAL; + + } /* endif */ + + return( rc); +} + +void VM_virtual_device_info( int devno, /* device number */ + senseid_t *ps ) /* pointer to sense ID data area */ +{ + diag210_t diag_data; + int ccode; + + int error = 0; + + diag_data.vrdcdvno = devno; + diag_data.vrdclen = sizeof( diag210_t); + ccode = diag210( (diag210_t *)virt_to_phys( &diag_data ) ); + ps->reserved = 0xff; + + switch (diag_data.vrdcvcla) { + case 0x80: + + switch (diag_data.vrdcvtyp) { + case 00: + + ps->cu_type = 0x3215; + + break; + + default: + + error = 1; + + break; + + } /* endswitch */ + + break; + + case 40: + + switch (diag_data.vrdcvtyp) { + case 0xC0: + + ps->cu_type = 0x5080; + + break; + + case 0x80: + + ps->cu_type = 0x2250; + + break; + + case 0x04: + + ps->cu_type = 0x3277; + + break; + + case 0x01: + + ps->cu_type = 0x3278; + + break; + + default: + + error = 1; + + break; + + } /* endswitch */ + + break; + + case 0x20: + + switch (diag_data.vrdcvtyp) { + case 0x84: + + ps->cu_type = 0x3505; + + break; + + case 0x82: + + ps->cu_type = 0x2540; + + break; + + case 0x81: + + ps->cu_type = 0x2501; + + break; + + default: + + error = 1; + + break; + + } /* endswitch */ + + break; + + case 0x10: + + switch (diag_data.vrdcvtyp) { + case 0x84: + + ps->cu_type = 0x3525; + + break; + + case 0x82: + + ps->cu_type = 0x2540; + + break; + + case 0x4F: + case 0x4E: + case 0x48: + + ps->cu_type = 0x3820; + + break; + + case 0x4D: + case 0x49: + case 0x45: + + ps->cu_type = 0x3800; + + break; + + case 0x4B: + + ps->cu_type = 0x4248; + + break; + + case 0x4A: + + ps->cu_type = 0x4245; + + break; + + case 0x47: + + ps->cu_type = 0x3262; + + break; + + case 0x43: + + ps->cu_type = 0x3203; + + break; + + case 0x42: + + ps->cu_type = 0x3211; + + break; + + case 0x41: + + ps->cu_type = 0x1403; + + break; + + default: + + error = 1; + + break; + + } /* endswitch */ + + break; + + case 0x08: + + switch (diag_data.vrdcvtyp) { + case 0x82: + + ps->cu_type = 0x3422; + + break; + + case 0x81: + + ps->cu_type = 0x3490; + + break; + + case 0x10: + + ps->cu_type = 0x3420; + + break; + + case 0x02: + + ps->cu_type = 0x3430; + + break; + + case 0x01: + + ps->cu_type = 0x3480; + + break; + + case 0x42: + + ps->cu_type = 0x3424; + + break; + + case 0x44: + + ps->cu_type = 0x9348; + + break; + + default: + + error = 1; + + break; + + } /* endswitch */ + + break; + + default: + + error = 1; + + break; + + } /* endswitch */ + + if ( error ) + {printk( "DIAG X'210' for device %04X returned (cc = %d): vdev class : %02X, " + "vdev type : %04X \n ... rdev class : %02X, rdev type : %04X, rdev model: %02X\n", + devno, + ccode, + diag_data.vrdcvcla, + diag_data.vrdcvtyp, + diag_data.vrdcrccl, + diag_data.vrdccrty, + diag_data.vrdccrmd ); + + } /* endif */ + +} + +/* + * This routine returns the characteristics for the device + * specified. Some old devices might not provide the necessary + * command code information during SenseID processing. In this + * case the function returns -EINVAL. Otherwise the function + * allocates a decice specific data buffer and provides the + * device characteristics together with the buffer size. Its + * the callers responability to release the kernel memory if + * not longer needed. In case of persistent I/O problems -EBUSY + * is returned. + * + * The function may be called enabled or disabled. However, the + * caller must have locked the irq it is requesting data for. + * + * Note : It would have been nice to collect this information + * during init_IRQ() processing but this is not possible + * + * a) without statically pre-allocation fixed size buffers + * as virtual memory management isn't available yet. + * + * b) without unnecessarily increase system startup by + * evaluating devices eventually not used at all. + */ +int read_dev_chars( int irq, void **buffer, int length ) +{ + unsigned int flags; + ccw1_t *rdc_ccw; + devstat_t devstat; + char *rdc_buf; + int devflag; + + int ret = 0; + int emulated = 0; + int retry = 5; + + if ( !buffer || !length ) + { + return( -EINVAL ); + + } /* endif */ + + if ( (irq > highest_subchannel) || (irq < 0 ) ) + { + return( -ENODEV ); + + } /* endif */ + + if ( ioinfo[irq].ui.flags.oper == 0 ) + { + return( -ENODEV ); + + } /* endif */ + + /* + * Before playing around with irq locks we should assure + * running disabled on (just) our CPU. Sync. I/O requests + * also require to run disabled. + * + * Note : as no global lock is required, we must not use + * cli(), but __cli() instead. + */ + __save_flags(flags); + __cli(); + + rdc_ccw = &ioinfo[irq].senseccw; + + if ( !ioinfo[irq].ui.flags.ready ) + { + ret = request_irq( irq, init_IRQ_handler, 0, "RDC", &devstat ); + + if ( !ret ) + { + emulated = 1; + + } /* endif */ + + } /* endif */ + + if ( !ret ) + { + if ( ! *buffer ) + { + rdc_buf = kmalloc( length, GFP_KERNEL); + } + else + { + rdc_buf = *buffer; + + } /* endif */ + + if ( !rdc_buf ) + { + ret = -ENOMEM; + } + else + { + do + { + rdc_ccw->cmd_code = CCW_CMD_RDC; + rdc_ccw->cda = (char *)virt_to_phys( rdc_buf ); + rdc_ccw->count = length; + rdc_ccw->flags = CCW_FLAG_SLI; + + ret = s390_start_IO( irq, + rdc_ccw, + 0x00524443, // RDC + 0, // n/a + DOIO_WAIT_FOR_INTERRUPT ); + retry--; + devflag = ((devstat_t *)(irq_desc[irq].action->dev_id))->flag; + + } while ( ( retry ) + && ( ret || (devflag & DEVSTAT_STATUS_PENDING) ) ); + + } /* endif */ + + if ( !retry ) + { + ret = -EBUSY; + + } /* endif */ + + __restore_flags(flags); + + /* + * on success we update the user input parms + */ + if ( !ret ) + { + *buffer = rdc_buf; + + } /* endif */ + + if ( emulated ) + { + free_irq( irq, &devstat); + + } /* endif */ + + } /* endif */ + + return( ret ); +} + +/* + * Read Configuration data + */ +int read_conf_data( int irq, void **buffer, int *length ) +{ + int found = 0; + int ciw_cnt = 0; + unsigned int flags; + + int ret = 0; + + if ( (irq > highest_subchannel) || (irq < 0 ) ) + { + return( -ENODEV ); + + } /* endif */ + + if ( ioinfo[irq].ui.flags.oper == 0 ) + { + return( -ENODEV ); + + } /* endif */ + + /* + * scan for RCD command in extended SenseID data + */ + for ( ; (found == 0) && (ciw_cnt < 62); ciw_cnt++ ) + { + if ( senseid[irq].ciw[ciw_cnt].ct == CIW_TYPE_RCD ) + { + found = 1; + break; + } /* endif */ + + } /* endfor */ + + if ( found ) + { + ccw1_t *rcd_ccw = &ioinfo[irq].senseccw; + devstat_t devstat; + char *rcd_buf; + int devflag; + + int emulated = 0; + int retry = 5; + + __save_flags(flags); + __cli(); + + if ( !ioinfo[irq].ui.flags.ready ) + { + ret = request_irq( irq, init_IRQ_handler, 0, "RCD", &devstat ); + + if ( !ret ) + { + emulated = 1; + + } /* endif */ + + } /* endif */ + + if ( !ret ) + { + rcd_buf = kmalloc( senseid[irq].ciw[ciw_cnt].count, GFP_KERNEL); + + do + { + rcd_ccw->cmd_code = senseid[irq].ciw[ciw_cnt].cmd; + rcd_ccw->cda = (char *)virt_to_phys( rcd_buf ); + rcd_ccw->count = senseid[irq].ciw[ciw_cnt].count; + rcd_ccw->flags = CCW_FLAG_SLI; + + ret = s390_start_IO( irq, + rcd_ccw, + 0x00524344, // == RCD + 0, // n/a + DOIO_WAIT_FOR_INTERRUPT ); + + retry--; + + devflag = ((devstat_t *)(irq_desc[irq].action->dev_id))->flag; + + } while ( ( retry ) + && ( ret || (devflag & DEVSTAT_STATUS_PENDING) ) ); + + if ( !retry ) + ret = -EBUSY; + + __restore_flags(flags); + + } /* endif */ + + /* + * on success we update the user input parms + */ + if ( !ret ) + { + *length = senseid[irq].ciw[ciw_cnt].count; + *buffer = rcd_buf; + + } /* endif */ + + if ( emulated ) + free_irq( irq, &devstat); + } + else + { + ret = -EINVAL; + + } /* endif */ + + return( ret ); + +} + + +int get_dev_info( int irq, dev_info_t *pdi) +{ + return( get_dev_info_by_irq( irq, pdi) ); +} + +int get_dev_info_by_irq( int irq, dev_info_t *pdi) +{ + + if ( irq > highest_subchannel ) + { + return -ENODEV; + } + else if ( pdi == NULL ) + { + return -EINVAL; + } + else + { + pdi->devno = ioinfo[irq].schib.pmcw.dev; + pdi->irq = irq; + + if ( ioinfo[irq].ui.flags.oper ) + { + pdi->status = 0; + memcpy( &(pdi->sid_data), &senseid[irq], sizeof( senseid_t)); + } + else + { + pdi->status = DEVSTAT_NOT_OPER; + memcpy( &(pdi->sid_data), '\0', sizeof( senseid_t)); + pdi->sid_data.cu_type = 0xFFFF; + + } /* endif */ + + return 0; + + } /* endif */ + +} + + +int get_dev_info_by_devno( unsigned int devno, dev_info_t *pdi) +{ + int i; + int rc = -ENODEV; + + if ( devno > 0x0000ffff ) + { + return -ENODEV; + } + else if ( pdi == NULL ) + { + return -EINVAL; + } + else + { + + for ( i=0; i <= highest_subchannel; i++ ) + { + + if ( ioinfo[i].schib.pmcw.dev == devno ) + { + if ( ioinfo[i].ui.flags.oper ) + { + pdi->status = 0; + pdi->irq = i; + pdi->devno = devno; + memcpy( &(pdi->sid_data), + &senseid[i], + sizeof( senseid_t)); + } + else + { + pdi->status = DEVSTAT_NOT_OPER; + pdi->irq = i; + pdi->devno = devno; + memcpy( &(pdi->sid_data), '\0', sizeof( senseid_t)); + pdi->sid_data.cu_type = 0xFFFF; + + } /* endif */ + + rc = 0; /* found */ + break; + + } /* endif */ + + } /* endfor */ + + return( rc); + + } /* endif */ + +} + +int get_irq_by_devno( unsigned int devno ) +{ + int i; + int rc = -1; + + if ( devno <= 0x0000ffff ) + { + for ( i=0; i <= highest_subchannel; i++ ) + { + if ( devno == ioinfo[i].schib.pmcw.dev ) + { + rc = i; + break; + + } /* endif */ + + } /* endfor */ + + } /* endif */ + + return( rc); + +} + +unsigned int get_devno_by_irq( int irq ) +{ + + if ( irq > highest_subchannel ) + { + return -1; + + } /* endif */ + + /* + * we don't need to check for the device be operational + * as the initial STSCH will always present the device + * number defined by the IOCDS regardless of the device + * existing or not. + */ + return( ioinfo[irq].schib.pmcw.dev ); + +} + +/* + * s390_device_recognition + * + * Used for system wide device recognition. Issues the device + * independant SenseID command to obtain info the device type. + * + */ +void s390_device_recognition( void) +{ + + int irq = 0; /* let's start with subchannel 0 ... */ + + do + { + /* + * We issue the SenseID command on I/O subchannels we think are + * operational only. + */ + if ( ( schiblock[irq].pmcw.st == 0 ) + && ( ioinfo[irq].ui.flags.oper == 1 ) ) + { + s390_SenseID( irq, &senseid[irq] ); + + } /* endif */ + + irq ++; + + } while ( irq <= highest_subchannel ); + +} + + +/* + * s390_search_devices + * + * Determines all subchannels available to the system. + * + */ +void s390_process_subchannels( void) +{ + int isValid; + + int irq = 0; /* Evaluate all subchannels starting with 0 ... */ + + do + { + isValid = s390_validate_subchannel( irq); + + irq++; + + } while ( isValid && irq < NR_IRQS ); + + highest_subchannel = --irq; + + printk( "\nHighest subchannel number detected: %u\n", + highest_subchannel); +} + +/* + * s390_validate_subchannel() + * + * Process the subchannel for the requested irq. Returns 1 for valid + * subchannels, otherwise 0. + */ +int s390_validate_subchannel( int irq ) +{ + + int retry; /* retry count for status pending conditions */ + int ccode; /* condition code for stsch() only */ + int ccode2; /* condition code for other I/O routines */ + + /* + * The first subchannel that is not-operational (ccode==3) + * indicates that there aren't any more devices available. + */ + ccode = stsch( irq, &schiblock[irq]); + + if ( ccode == 0) + { + /* + * ... just being curious we check for non I/O subchannels + */ + if ( schiblock[irq].pmcw.st ) + { + printk( "Subchannel %04X reports " + "non-I/O subchannel type %04X\n", + irq, + schiblock[irq].pmcw.st); + + ioinfo[irq].ui.flags.oper = 0; + + } /* endif */ + + if ( !schiblock[irq].pmcw.dnv ) + { + /* + * don't process invalid device numbers ... + */ + ioinfo[irq].ui.flags.oper = 0; + } + else + { + printk( "Detected device %04X on subchannel %04X" + " - PIM = %02X, PAM = %02X, POM = %02X\n", + schiblock[irq].pmcw.dev, + irq, + schiblock[irq].pmcw.pim, + schiblock[irq].pmcw.pam, + schiblock[irq].pmcw.pom); + + /* + * We now have to initially ... + * ... set "interruption sublass" + * ... enable "concurrent sense" + * ... enable "multipath mode" if more than + * one CHPID is available + * + * Note : we don't enable the device here, this is temporarily + * done during device sensing below. + */ + schiblock[irq].pmcw.isc = 3; /* could be smth. else ... */ + schiblock[irq].pmcw.csense = 1; /* concurrent sense */ + schiblock[irq].pmcw.ena = 0; /* force disable it */ + + if ( ( schiblock[irq].pmcw.pim != 0 ) + && ( schiblock[irq].pmcw.pim != 0x80 ) ) + { + schiblock[irq].pmcw.mp = 1; /* multipath mode */ + + } /* endif */ + + /* + * initialize ioinfo structure + */ + ioinfo[irq].irq = irq; + ioinfo[irq].ui.flags.busy = 0; + ioinfo[irq].ui.flags.ready = 0; + ioinfo[irq].ui.flags.oper = 1; + ioinfo[irq].devstat.intparm = irq; + ioinfo[irq].devstat.devno = schiblock[irq].pmcw.dev; + + memcpy( &(ioinfo[irq].schib), &(schiblock[irq]), sizeof( schib_t)); + + retry = 5; + + do + { + ccode2 = msch_err( irq, &schiblock[irq]); + + switch (ccode2) { + case 0: // successful completion + // + // concurrent sense facility available ... + // + ioinfo[irq].ui.flags.consns = 1; + break; + + case 1: // status pending + // + // How can we have a pending status as device is + // disabled for interrupts ? Anyway, clear it ... + // + tsch( irq, &(ioinfo[irq].devstat.ii.irb) ); + retry--; + break; + + case 2: // busy + retry--; + break; + + case 3: // not operational + ioinfo[irq].ui.flags.oper = 0; + retry = 0; + break; + + default: +#define PGMCHK_OPERAND_EXC 0x15 + + if ( (ccode2 & PGMCHK_OPERAND_EXC) == PGMCHK_OPERAND_EXC ) + { + /* + * re-issue the modify subchannel without trying to + * enable the concurrent sense facility + */ + schiblock[irq].pmcw.csense = 0; + + memcpy( &(ioinfo[irq].schib), + &(schiblock[irq]), + sizeof( schib_t)); + + ccode2 = msch_err( irq, &schiblock[irq]); + + if ( ccode2 != 0 ) + { + printk( " ... modify subchannel (2) failed with CC = %X\n", + ccode2 ); + ioinfo[irq].ui.flags.oper = 0; + } + else + { + ioinfo[irq].ui.flags.consns = 0; + + } /* endif */ + } + else + { + printk( " ... modify subchannel (1) failed with CC = %X\n", + ccode2); + ioinfo[irq].ui.flags.oper = 0; + + } /* endif */ + + retry = 0; + break; + + } /* endswitch */ + + } while ( ccode2 && retry ); + + if ( (ccode2 < 3) && (!retry) ) + { + printk( " ... msch() retry count for " + "subchannel %04X exceeded, CC = %d\n", + irq, + ccode2); + + } /* endif */ + + } /* endif */ + + } /* endif */ + + /* + * indicate whether the subchannel is valid + */ + if ( ccode == 3) + return(0); + else + return(1); +} + +/* + * s390_SenseID + * + * Try to obtain the 'control unit'/'device type' information + * associated with the subchannel. + * + * The function is primarily meant to be called without irq + * action handler in place. However, it also allows for + * use with an action handler in place. If there is already + * an action handler registered assure it can handle the + * s390_SenseID() related device interrupts - interruption + * parameter used is 0x00E2C9C4 ( SID ). + */ +int s390_SenseID( int irq, senseid_t *sid ) +{ + ccw1_t sense_ccw; /* ccw area for SenseID command */ + devstat_t devstat; /* required by request_irq() */ + + int irq_ret = 0; /* return code */ + int retry = 5; /* retry count */ + int inlreq = 0; /* inline request_irq() */ + + if ( (irq > highest_subchannel) || (irq < 0 ) ) + { + return( -ENODEV ); + + } /* endif */ + + if ( ioinfo[irq].ui.flags.oper == 0 ) + { + return( -ENODEV ); + + } /* endif */ + + if ( !ioinfo[irq].ui.flags.ready ) + { + /* + * Perform SENSE ID command processing. We have to request device + * ownership and provide a dummy I/O handler. We issue sync. I/O + * requests and evaluate the devstat area on return therefore + * we don't need a real I/O handler in place. + */ + irq_ret = request_irq( irq, init_IRQ_handler, 0, "SID", &devstat); + + if ( irq_ret == 0 ) + inlreq = 1; + + } /* endif */ + + if ( irq_ret == 0 ) + { + s390irq_spin_lock( irq); + + sense_ccw.cmd_code = CCW_CMD_SENSE_ID; + sense_ccw.cda = (char *)virt_to_phys( sid ); + sense_ccw.count = sizeof( senseid_t); + sense_ccw.flags = CCW_FLAG_SLI; + + senseid[irq].cu_type = 0xFFFF; /* initialize fields ... */ + senseid[irq].cu_model = 0; + senseid[irq].dev_type = 0; + senseid[irq].dev_model = 0; + + /* + * We now issue a SenseID request. In case of BUSY + * or STATUS PENDING conditions we retry 5 times. + */ + do + { + memset( &devstat, '\0', sizeof( devstat_t) ); + + irq_ret = s390_start_IO( irq, + &sense_ccw, + 0x00E2C9C4, // == SID + 0, // n/a + DOIO_WAIT_FOR_INTERRUPT ); + + if ( sid->cu_type == 0xFFFF ) + { + if ( devstat.flag & DEVSTAT_STATUS_PENDING ) + { +#if CONFIG_DEBUG_IO + printk( "Device %04X on Subchannel %04X " + "reports pending status, retry : %d\n", + schiblock[irq].pmcw.dev, + irq, + retry); +#endif + } /* endif */ + + if ( devstat.flag & DEVSTAT_FLAG_SENSE_AVAIL ) + { + /* + * if the device doesn't support the SenseID + * command further retries wouldn't help ... + */ + if ( devstat.ii.sense.data[0] == SNS0_CMD_REJECT ) + { + retry = 0; + } +#if CONFIG_DEBUG_IO + else + { + printk( "Device %04X," + " UC/SenseID," + " retry %d, cnt %02d," + " sns :" + " %02X%02X%02X%02X %02X%02X%02X%02X ...\n", + schiblock[irq].pmcw.dev, + retry, + devstat.scnt, + devstat.ii.sense.data[0], + devstat.ii.sense.data[1], + devstat.ii.sense.data[2], + devstat.ii.sense.data[3], + devstat.ii.sense.data[4], + devstat.ii.sense.data[5], + devstat.ii.sense.data[6], + devstat.ii.sense.data[7]); + + } /* endif */ +#endif + } + else if ( devstat.flag & DEVSTAT_NOT_OPER ) + { + printk( "Device %04X on Subchannel %04X " + "became 'not operational'\n", + schiblock[irq].pmcw.dev, + irq); + + retry = 0; + + } /* endif */ + } + else // we got it ... + { + retry = 0; + + } /* endif */ + + retry--; + + } while ( retry > 0 ); + + s390irq_spin_unlock( irq); + + /* + * If we installed the irq action handler we have to + * release it too. + */ + if ( inlreq ) + free_irq( irq, &devstat); + + /* + * if running under VM check there ... perhaps we should do + * only if we suffered a command reject, but it doesn't harm + */ + if ( ( sid->cu_type == 0xFFFF ) + && ( MACHINE_IS_VM ) ) + { + VM_virtual_device_info( schiblock[irq].pmcw.dev, + sid ); + } /* endif */ + + if ( sid->cu_type == 0xFFFF ) + { + /* + * SenseID CU-type of 0xffff indicates that no device + * information could be retrieved (pre-init value). + * + * If we can't couldn't identify the device type we + * consider the device "not operational". + */ + printk( "Unknown device %04X on subchannel %04X\n", + schiblock[irq].pmcw.dev, + irq); + ioinfo[irq].ui.flags.oper = 0; + + } /* endif */ + + /* + * Issue device info message if unit was operational . + */ + if ( ioinfo[irq].ui.flags.oper ) + { + if ( sid->dev_type != 0 ) + { + printk( "Device %04X reports: CU Type/Mod = %04X/%02X," + " Dev Type/Mod = %04X/%02X\n", + schiblock[irq].pmcw.dev, + sid->cu_type, + sid->cu_model, + sid->dev_type, + sid->dev_model); + } + else + { + printk( "Device %04X reports:" + " Dev Type/Mod = %04X/%02X\n", + schiblock[irq].pmcw.dev, + sid->cu_type, + sid->cu_model); + + } /* endif */ + + } /* endif */ + + if ( ioinfo[irq].ui.flags.oper ) + irq_ret = 0; + else + irq_ret = -ENODEV; + + } /* endif */ + + return( irq_ret ); +} + +void do_crw_pending(void) +{ +} +#ifdef CONFIG_READIPL_ENABLED +void +do_reipl ( int sch ) +{ + static ccw1_t iplccw[2] = { + { CCW_CMD_READ_IPL, CCW_FLAG_CC, 24, 0x00000000 }, + { CCW_CMD_TIC , CCW_FLAG_CC, 0, 0x00000008 } + }; + static orb_t iplorb = { + 0, + }; + static psw_t psw_0 = {0,}; + static long cr6_0 = 0x0; + static psw_t psw_1 = {0,}; + static long cr6_1 = 0x0; + int i; + + /* First disable all Devices/IRQs */ + for ( i = 0; i < highest_subchannel; i ++ ) { + free_irq ( i, (void*)REIPL_DEVID_MAGIC ); + } + /* re enable the one device */ + enable_subchannel (sch); + __asm__ __volatile__ ( "spx 0" ); + __asm__ __volatile__ ( "lpsw %0" :: "m" (psw_0) ); /* disable all */ + __asm__ __volatile__ ( "lctl 6,6,%0" : : "m" (cr6_0) ); + __asm__ __volatile__ ( "lr 1,%0\n" + "ssch %1\n" + :: "d" (sch), "m" (iplorb) : "1" ); + __asm__ __volatile__ ( "lctl 6,6,%0" :: "m" (cr6_1) ); + __asm__ __volatile__ ( "lpsw %0" :: "m" (psw_1) ); + __asm__ __volatile__ ( "lpsw 0" ); /* restart */ +} +#endif diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/s390io.h linux/arch/s390/kernel/s390io.h --- v2.2.13/linux/arch/s390/kernel/s390io.h Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/s390io.h Tue Jan 4 10:12:12 2000 @@ -0,0 +1,72 @@ +/* + * arch/s390/kernel/s390io.h + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Ingo Adlung (adlung@de.ibm.com) + */ + +#ifndef __s390io_h +#define __s390io_h + +/* + * Private data structure used by do_IO() + * + * Note : If bit flags are added, the "unused" value must be + * decremented accordingly ! + */ +typedef struct { + unsigned int irq; /* aka. subchannel number */ + union { + unsigned int info; + struct { + unsigned int busy : 1; /* device currently in use */ + unsigned int oper : 1; /* device is operational */ + unsigned int fast : 1; /* post with "channel end", ... */ + /* ... don't wait for "device end" */ + /* ... from do_IO() parameters */ + unsigned int ready : 1; /* interrupt handler registered */ + unsigned int haltio : 1; /* halt_IO in process */ + unsigned int doio : 1; /* do_IO in process */ + unsigned int doio_q : 1; /* do_IO queued - only possible ... */ + /* ... if 'fast' is set too */ + unsigned int w4final : 1; /* wait for final status, internally */ + /* ... used with 'fast' setting only */ + unsigned int repall : 1; /* report every interrupt status */ + unsigned int unready : 1; /* deregister irq handler in process */ + unsigned int d_disable : 1; /* delayed disabling required */ + unsigned int w4sense : 1; /* SENSE status pending */ + unsigned int syncio : 1; /* synchronous I/O requested */ + unsigned int consns : 1; /* concurrent sense is available */ + unsigned int delsense : 1; /* delayed SENSE required */ + unsigned int s_pend : 1; /* status pending condition */ + unsigned int unused : 16; /* unused */ + } __attribute__ ((packed)) flags; + } ui; + unsigned int lpm; /* logical path mask to be used ... */ + /* ... from do_IO() parms. Only ... */ + /* ... valid if vlpm is set too. */ + schib_t schib; /* subchannel information block */ + orb_t orb; /* operation request block */ + devstat_t devstat; /* device status */ + ccw1_t *qcpa; /* queued channel program */ + ccw1_t senseccw; /* ccw for sense command */ + unsigned int stctl; /* accumulated status control from irb */ + unsigned int qintparm; /* queued interruption parameter */ + unsigned long qflag; /* queued flags */ + unsigned char qlpm; /* queued logical path mask */ + + } __attribute__ ((aligned(8))) ioinfo_t; + +#define IOINFO_FLAGS_BUSY 0x80000000 +#define IOINFO_FLAGS_OPER 0x40000000 +#define IOINFO_FLAGS_FAST 0x20000000 +#define IOINFO_FLAGS_READY 0x10000000 +#define IOINFO_FLAGS_HALTIO 0x08000000 +#define IOINFO_FLAGS_DOIO 0x04000000 +#define IOINFO_FLAGS_DOIO_Q 0x02000000 +#define IOINFO_FLAGS_W4FINAL 0x01000000 +#define IOINFO_FLAGS_REPALL 0x00800000 + +#endif /* __s390io_h */ + diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/s390ksyms.c linux/arch/s390/kernel/s390ksyms.c --- v2.2.13/linux/arch/s390/kernel/s390ksyms.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/s390ksyms.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,22 @@ +/* + * arch/s390/kernel/s390ksyms.c + * + * S390 version + */ + +#include "irq.h" +#include "asm/string.h" + +EXPORT_SYMBOL(get_dev_info_by_irq); +EXPORT_SYMBOL(get_dev_info_by_devno); +EXPORT_SYMBOL(get_irq_by_devno); +EXPORT_SYMBOL(get_devno_by_irq); +EXPORT_SYMBOL(halt_IO); +EXPORT_SYMBOL(irq_desc); +EXPORT_SYMBOL(do_IO); +EXPORT_SYMBOL(memset); +EXPORT_SYMBOL(init_mm); +EXPORT_SYMBOL(_oi_bitmap); +EXPORT_SYMBOL(_ni_bitmap); +EXPORT_SYMBOL(_zb_findmap); + diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/setup.c linux/arch/s390/kernel/setup.c --- v2.2.13/linux/arch/s390/kernel/setup.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/setup.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,308 @@ +/* + * arch/s390/kernel/setup.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hp@de.ibm.com), + * Martin Schwidefsky (schwidefsky@de.ibm.com) + * + * Derived from "arch/i386/kernel/setup.c" + * Copyright (C) 1995, Linus Torvalds + */ + +/* + * This file handles the architecture-dependent parts of initialization + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_BLK_DEV_RAM +#include +#endif +#include +#include +#include +#include + +/* + * Machine setup.. + */ +__u16 boot_cpu_addr; +int cpus_initialized = 0; +unsigned long cpu_initialized = 0; + +/* + * Setup options + */ + +#ifdef CONFIG_BLK_DEV_RAM +extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ +extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt*/ +extern int rd_image_start; /* starting block # of image */ +#endif + +extern int root_mountflags; +extern int _etext, _edata, _end; + + +/* + * This is set up by the setup-routine at boot-time + * for S390 need to find out, what we have to setup + * using address 0x10400 ... + */ + +#include + +static char command_line[COMMAND_LINE_SIZE] = { 0, }; + char saved_command_line[COMMAND_LINE_SIZE]; + +/* + * cpu_init() initializes state that is per-CPU. + */ +void cpu_init (void) +{ + int nr = smp_processor_id(); + + if (test_and_set_bit(nr,&cpu_initialized)) { + printk("CPU#%d ALREADY INITIALIZED!!!!!!!!!\n", nr); + for (;;) __sti(); + } + cpus_initialized++; + + /* + * Store processor id in lowcore (used e.g. in timer_interrupt) + */ + asm volatile ("stidp %0": "=m" (S390_lowcore.cpu_data.cpu_id)); + S390_lowcore.cpu_data.cpu_addr = hard_smp_processor_id(); + S390_lowcore.cpu_data.cpu_nr = nr; + + /* + * Force FPU initialization: + */ + current->flags &= ~PF_USEDFPU; + current->used_math = 0; +} + +/* + * Reboot, halt and power_off routines for non SMP. + */ +#ifndef __SMP__ +void machine_restart(char * __unused) +{ + if (MACHINE_IS_VM) { + cpcmd("IPL", NULL, 0); + } else { + /* FIXME: how to reipl ? */ + disabled_wait(2); + } +} + +void machine_halt(void) +{ + if (MACHINE_IS_VM) { + cpcmd("IPL 200 STOP", NULL, 0); + } else { + disabled_wait(0); + } +} + +void machine_power_off(void) +{ + if (MACHINE_IS_VM) { + cpcmd("IPL CMS", NULL, 0); + } else { + disabled_wait(0); + } +} +#endif + +/* + * Waits for 'delay' microseconds using the tod clock + */ +void tod_wait(unsigned long delay) +{ + uint64_t start_cc, end_cc; + + if (delay == 0) + return; + asm volatile ("STCK %0" : "=m" (start_cc)); + do { + asm volatile ("STCK %0" : "=m" (end_cc)); + } while (((end_cc - start_cc)/4096) < delay); +} + +/* + * Setup function called from init/main.c just after the banner + * was printed. + */ +__initfunc(void setup_arch(char **cmdline_p, + unsigned long * memory_start_p, unsigned long * memory_end_p)) +{ + unsigned long memory_start, memory_end; + char c = ' ', *to = command_line, *from = COMMAND_LINE; + static unsigned int smptrap=0; + unsigned long delay = 0; + int len = 0; + + if (smptrap) + return; + smptrap=1; + + printk("Command line is: %s\n", COMMAND_LINE); + + /* + * Setup lowcore information for boot cpu + */ + cpu_init(); + boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr; + + /* + * print what head.S has found out about the machine + */ + if (MACHINE_IS_VM) + printk("We are running under VM\n"); + else + printk("We are running native\n"); + if (MACHINE_HAS_IEEE) + printk("This machine has an IEEE fpu\n"); + else + printk("This machine has no IEEE fpu\n"); + + ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV); +#ifdef CONFIG_BLK_DEV_RAM + rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK; + rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0); + rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0); +#endif + /* nasty stuff with PARMAREAs. we use head.S or parameterline + if (!MOUNT_ROOT_RDONLY) + root_mountflags &= ~MS_RDONLY; + */ + memory_start = (unsigned long) &_end; /* fixit if use $CODELO etc*/ + memory_end = MEMORY_SIZE; + init_task.mm->start_code = PAGE_OFFSET; + init_task.mm->end_code = (unsigned long) &_etext; + init_task.mm->end_data = (unsigned long) &_edata; + init_task.mm->brk = (unsigned long) &_end; + + /* Save unparsed command line copy for /proc/cmdline */ + memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE); + saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; + + for (;;) { + /* + * "mem=XXX[kKmM]" sets memsize != 32M + * memory size + */ + if (c == ' ' && strncmp(from, "mem=", 4) == 0) { + if (to != command_line) to--; + memory_end = simple_strtoul(from+4, &from, 0); + if ( *from == 'K' || *from == 'k' ) { + memory_end = memory_end << 10; + from++; + } else if ( *from == 'M' || *from == 'm' ) { + memory_end = memory_end << 20; + from++; + } + } + if (c == ' ' && strncmp(from, "ipldelay=", 9) == 0) { + if (to != command_line) to--; + delay = simple_strtoul(from+9, &from, 0); + if (*from == 's' || *from == 'S') { + delay = delay*1000000; + from++; + } else if (*from == 'm' || *from == 'M') { + delay = delay*60*1000000; + from++; + } + tod_wait(delay); + } + c = *(from++); + if (!c) + break; + if (COMMAND_LINE_SIZE <= ++len) + break; + *(to++) = c; + } + *to = '\0'; + *cmdline_p = command_line; + memory_end += PAGE_OFFSET; + *memory_start_p = memory_start; + *memory_end_p = memory_end; + +#ifdef CONFIG_BLK_DEV_INITRD + initrd_start = INITRD_START; + if (initrd_start) { + initrd_end = initrd_start+INITRD_SIZE; + printk("Initial ramdisk at: 0x%p (%lu bytes)\n", + (void *) initrd_start, INITRD_SIZE); + initrd_below_start_ok = 1; + if (initrd_end > *memory_end_p) { + printk("initrd extends beyond end of memory " + "(0x%08lx > 0x%08lx)\ndisabling initrd\n", + initrd_end, (unsigned long) memory_end_p); + initrd_start = initrd_end = 0; + } + } +#endif + +} + +void print_cpu_info(struct cpuinfo_S390 *cpuinfo) +{ + printk("cpu %d " +#ifdef __SMP__ + "phys_idx=%d " +#endif + "vers=%02X ident=%06X machine=%04X unused=%04X\n", + cpuinfo->cpu_nr, +#ifdef __SMP__ + cpuinfo->cpu_addr, +#endif + cpuinfo->cpu_id.version, + cpuinfo->cpu_id.ident, + cpuinfo->cpu_id.machine, + cpuinfo->cpu_id.unused); +} + +/* + * Get CPU information for use by the procfs. + */ + +int get_cpuinfo(char * buffer) +{ + struct cpuinfo_S390 *cpuinfo; + char *p = buffer; + int i; + + p += sprintf(p,"vendor_id : IBM/S390\n" + "# processors : %i\n" + "bogomips per cpu: %lu.%02lu\n", + smp_num_cpus, loops_per_sec/500000, + (loops_per_sec/5000)%100); + for (i = 0; i < smp_num_cpus; i++) { + cpuinfo = &safe_get_cpu_lowcore(i).cpu_data; + p += sprintf(p,"processor %i: " + "version = %02X, " + "identification = %06X, " + "machine = %04X\n", + i, cpuinfo->cpu_id.version, + cpuinfo->cpu_id.ident, + cpuinfo->cpu_id.machine); + } + return p - buffer; +} + diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/signal.c linux/arch/s390/kernel/signal.c --- v2.2.13/linux/arch/s390/kernel/signal.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/signal.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,519 @@ +/* + * arch/s390/kernel/signal.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) + * + * based on PPC & i386 signal handling by Linus Torvalds & Gary Thomas + * + * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if CONFIG_BINFMT_TOC +#include +#endif +/* + * These are the flags in the MSR that the user is allowed to change + * by modifying the saved value of the MSR on the stack. SE and BE + * should not be in this list since gdb may want to change these. I.e, + * you should be able to step out of a signal handler to see what + * instruction executes next after the signal handler completes. + * Alternately, if you stepped into a signal handler, you should be + * able to continue 'til the next breakpoint from within the signal + * handler, even if the handler returns. + */ + + +#define __390_TO_DO__ 0 +#define __390_NEEDED__ 0 +#define DEBUG_SIG 0 + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +int do_signal(struct pt_regs *reg,sigset_t *oldset); +extern int sys_wait4(pid_t pid, unsigned long *stat_addr, + int options, unsigned long *ru); + + + + + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ +asmlinkage int sys_sigsuspend(struct pt_regs * regs,int history0, int history1, old_sigset_t mask) +{ + sigset_t saveset; + + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + regs->gprs[2] = -EINTR; + + while (1) + { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(regs, &saveset)) + return -EINTR; + } +} + + +// +// N.B. This hasn't been tested it just compiles easily +// +asmlinkage int sys_rt_sigsuspend(struct pt_regs * regs,sigset_t *unewset, size_t sigsetsize) +{ + sigset_t saveset, newset; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + regs->gprs[2] = -EINTR; + while (1) + { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(regs, &saveset)) + return -EINTR; + } +} + +asmlinkage int sys_rt_sigreturn(unsigned long __unused) +{ + printk("sys_rt_sigreturn(): %s/%d not yet implemented.\n", + current->comm,current->pid); + do_exit(SIGSEGV); +} + +asmlinkage int +sys_sigaltstack(const stack_t *uss, stack_t *uoss) +{ + struct pt_regs *regs = (struct pt_regs *) &uss; + return do_sigaltstack(uss, uoss, regs->gprs[15]); +} + + +asmlinkage int sys_sigaction(int sig, const struct old_sigaction *act,struct old_sigaction *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if (act) + { + old_sigset_t mask; + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) + { + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + + + __put_user((u32)old_ka.sa.sa_handler,&oact->sa_handler) || + __put_user((u32)old_ka.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + return ret; +} + +/* + * When we have signals to deliver, we set up on the + * user stack, going down from the original stack pointer: + * a sigregs struct + * one or more sigcontext structs + * a gap of __SIGNAL_FRAMESIZE bytes + * + * Each of these things must be a multiple of 16 bytes in size. + * + * XXX ultimately we will have to stack up a siginfo and ucontext + * for each rt signal. + */ + +struct sigregs { + user_regs_struct user_regs; + + unsigned short tramp[1]; + /* Programs using the rs6000/xcoff abi can save up to 19 gp regs + and 18 fp regs below sp before decrementing it. */ +}; + + +int sys_sigreturn(struct pt_regs *regs) +{ + struct sigcontext_struct *sc, sigctx; + struct sigregs *sr; + int ret; + user_regs_struct saved_regs; + sigset_t set; + unsigned long prevsp; + routine_descriptor tempdes; + + sc = (struct sigcontext_struct *)(regs->gprs[15] + __SIGNAL_FRAMESIZE); + if (copy_from_user(&sigctx, sc, sizeof(sigctx))) + goto badframe; + memcpy(&set.sig[0],&sigctx.oldmask[0],SIGMASK_COPY_SIZE); + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + sc++; /* Look at next sigcontext */ + if (sc == (struct sigcontext_struct *)(sigctx.regs)) { + /* Last stacked signal - restore registers */ + sr = (struct sigregs *) sigctx.regs; + if (copy_from_user(&saved_regs, &sr->user_regs, + sizeof(sr->user_regs))) + goto badframe; + saved_regs.psw.mask=(regs->psw.mask&~PSW_MASK_DEBUGCHANGE) + |(saved_regs.psw.mask&PSW_MASK_DEBUGCHANGE); + saved_regs.psw.addr=(regs->psw.addr&~PSW_ADDR_DEBUGCHANGE) + |(saved_regs.psw.addr&PSW_ADDR_DEBUGCHANGE); + memcpy(regs,&saved_regs,sizeof(s390_regs)); + restore_fp_regs(&saved_regs.fp_regs); + ret = regs->gprs[2]; + + } else { + /* More signals to go */ + regs->gprs[15] = (unsigned long)sc - __SIGNAL_FRAMESIZE; + if (copy_from_user(&sigctx, sc, sizeof(sigctx))) + goto badframe; + sr = (struct sigregs *) sigctx.regs; + regs->gprs[2] = ret = sigctx.signal; +#if DEBUG_SIG + regs->gprs[3] = (unsigned long) sr; // My best guess says this is used for debugging only DJB +#endif + regs->gprs[14] = (unsigned long) sr->tramp; // return address + + if(uses_routine_descriptors()) + { + if(copy_from_user(&tempdes,(char *)sigctx.handler,sizeof(tempdes))) + goto badframe; + fix_routine_descriptor_regs(&tempdes,regs); + } + else + regs->psw.addr = FIX_PSW(sigctx.handler); + + if (get_user(prevsp, &sr->user_regs.gprs[15]) + || put_user(prevsp, (unsigned long *) regs->gprs[15])) + goto badframe; + } + return ret; + +badframe: + lock_kernel(); + do_exit(SIGSEGV); +} + + +/* + * Set up a signal frame. + */ +void setup_frame(struct pt_regs *regs, struct sigregs *frame,unsigned long newsp) +{ + struct sigcontext_struct *sc = (struct sigcontext_struct *) newsp; + s390_fp_regs fpregs; + + routine_descriptor tempdes,*tempdesptr; + + if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) + goto badframe; + save_fp_regs(&fpregs); + if (__copy_to_user(&frame->user_regs, regs, sizeof(s390_regs)) + || __copy_to_user(&frame->user_regs.fp_regs,&fpregs,sizeof(fpregs)) + || __put_user(0x0A77, &frame->tramp[0])) /* SVC 0x77 */ + goto badframe; + newsp -= __SIGNAL_FRAMESIZE; + if (put_user(regs->gprs[15], (unsigned long *)newsp) || get_user(regs->gprs[2], &sc->signal)) + goto badframe; + + if(uses_routine_descriptors()) + { + if(get_user(tempdesptr, (routine_descriptor **)& sc->handler) + || copy_from_user(&tempdes,tempdesptr,sizeof(tempdes))) + goto badframe; + fix_routine_descriptor_regs(&tempdes,regs); + } + else + { + if(get_user(regs->psw.addr, (void **) & sc->handler)) + goto badframe; + regs->psw.addr=FIX_PSW(regs->psw.addr); + } + regs->gprs[15] = newsp; +#if DEBUG_SIG + regs->gprs[3] = (unsigned long) frame; +#endif + regs->gprs[14] = (unsigned long) frame->tramp; + + return; + +badframe: +#if DEBUG_SIG + printk("badframe in setup_frame, regs=%p frame=%p newsp=%lx\n", + regs, frame, newsp); +#endif + lock_kernel(); + do_exit(SIGSEGV); +} + +/* + * OK, we're invoking a handler + */ +void +handle_signal(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs, + unsigned long *newspp, unsigned long frame) +{ + struct sigcontext_struct *sc; + + if (regs->trap == 0x20) /* System Call! */ + { + switch(regs->gprs[2]) + { + case -ERESTARTNOHAND: + regs->gprs[2] = -EINTR; + break; + case -ERESTARTSYS: + if (!(ka->sa.sa_flags & SA_RESTART)) + { + regs->gprs[2] = -EINTR; + break; + } + /* fallthrough */ + case -ERESTARTNOINTR: + regs->gprs[2] = regs->orig_gpr2; + regs->psw.addr -= 2; + /* Back up & retry system call */ + } + } + /* Put another sigcontext on the stack */ + *newspp -= sizeof(*sc); + sc = (struct sigcontext_struct *) *newspp; + if (verify_area(VERIFY_WRITE, sc, sizeof(*sc))) + goto badframe; + + if (__put_user((unsigned long) ka->sa.sa_handler, &sc->handler) + || copy_to_user(&sc->oldmask[0],&oldset->sig[0],SIGMASK_COPY_SIZE) + || __put_user((u32)((struct pt_regs *)frame), &sc->regs) + || __put_user(sig, &sc->signal)) + goto badframe; + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sigmask_lock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + } + return; + +badframe: +#if DEBUG_SIG + printk("badframe in handle_signal, regs=%p frame=%lx newsp=%lx\n", + regs, frame, *newspp); + printk("sc=%p sig=%d ka=%p info=%p oldset=%p\n", sc, sig, ka, info, oldset); +#endif + lock_kernel(); + do_exit(SIGSEGV); +} + + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + */ +int do_signal(struct pt_regs *regs,sigset_t *oldset) +{ + siginfo_t info; + struct k_sigaction *ka; + unsigned long frame, newsp; + + if (!oldset) + oldset = ¤t->blocked; + + newsp = frame = regs->gprs[15] - sizeof(struct sigregs); + + for (;;) { + unsigned long signr; + + spin_lock_irq(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + + if (!signr) + break; + + if ((current->flags & PF_PTRACED) && signr != SIGKILL) { + /* Let the debugger run. */ + current->exit_code = signr; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + + /* We're back. Did the debugger cancel the sig? */ + if (!(signr = current->exit_code)) + continue; + current->exit_code = 0; + + /* The debugger continued. Ignore SIGSTOP. */ + if (signr == SIGSTOP) + continue; + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = current->p_pptr->pid; + info.si_uid = current->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(¤t->blocked, signr)) { + send_sig_info(signr, &info, current); + continue; + } + } + + ka = ¤t->sig->action[signr-1]; + if (ka->sa.sa_handler == SIG_IGN) { + if (signr != SIGCHLD) + continue; + /* Check for SIGCHLD: it's special. */ + while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) + /* nothing */; + continue; + } + + if (ka->sa.sa_handler == SIG_DFL) { + int exit_code = signr; + + /* Init gets no signals it doesn't want. */ + if (current->pid == 1) + continue; + + switch (signr) { + case SIGCONT: case SIGCHLD: case SIGWINCH: + continue; + + case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (is_orphaned_pgrp(current->pgrp)) + continue; + /* FALLTHRU */ + + case SIGSTOP: + current->state = TASK_STOPPED; + current->exit_code = signr; + if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) + notify_parent(current, SIGCHLD); + schedule(); + continue; + + case SIGQUIT: case SIGILL: case SIGTRAP: + case SIGABRT: case SIGFPE: case SIGSEGV: + lock_kernel(); + if (current->binfmt + && current->binfmt->core_dump + && current->binfmt->core_dump(signr, regs)) + exit_code |= 0x80; + unlock_kernel(); + /* FALLTHRU */ + + default: + lock_kernel(); + sigaddset(¤t->signal, signr); + recalc_sigpending(current); + current->flags |= PF_SIGNALED; + do_exit(exit_code); + /* NOTREACHED */ + } + } + + /* Whee! Actually deliver the signal. */ + handle_signal(signr, ka, &info, oldset, regs, &newsp, frame); + } + + if ( + regs->trap == 0x20 /* System Call! */ && + ((int)regs->gprs[2] == -ERESTARTNOHAND || + (int)regs->gprs[2] == -ERESTARTSYS || + (int)regs->gprs[2] == -ERESTARTNOINTR)) { + regs->gprs[2] = regs->orig_gpr2; + regs->psw.addr -= 2; /* Back up & retry system call */ + } + + if (newsp == frame) + return 0; /* no signals delivered */ + + setup_frame(regs, (struct sigregs *) frame, newsp); + return 1; +} + + + + + + + + + + + + + + + + + + + + diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/smp.c linux/arch/s390/kernel/smp.c --- v2.2.13/linux/arch/s390/kernel/smp.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/smp.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,693 @@ +/* + * arch/s390/kernel/smp.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + * Martin Schwidefsky (schwidefsky@de.ibm.com) + * + * based on other smp stuff by + * (c) 1995 Alan Cox, CymruNET Ltd + * (c) 1998 Ingo Molnar + * + * We work with logical cpu numbering everywhere we can. The only + * functions using the real cpu address (got from STAP) are the sigp + * functions. For all other functions we use the identity mapping. + * That means that cpu_number_map[i] == i for every cpu. cpu_number_map is + * used e.g. to find the idle task belonging to a logical cpu. Every array + * in the kernel is sorted by the logical cpu number and not by the physical + * one which is causing all the confusion with __cpu_logical_map and + * cpu_number_map in other architectures. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cpcmd.h" +#include "irq.h" + +/* prototypes */ +extern void update_one_process( struct task_struct *p, + unsigned long ticks, unsigned long user, + unsigned long system, int cpu); +extern int cpu_idle(void * unused); + +extern __u16 boot_cpu_addr; + +/* + * An array with a pointer the lowcore of every CPU. + */ +static int max_cpus = NR_CPUS; /* Setup configured maximum number of CPUs to activate */ +int smp_num_cpus; +struct _lowcore *lowcore_ptr[NR_CPUS]; +unsigned int prof_multiplier[NR_CPUS]; +unsigned int prof_counter[NR_CPUS]; +volatile int cpu_number_map[NR_CPUS]; +volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */ +cycles_t cacheflush_time=0; +int smp_threads_ready=0; /* Set when the idlers are all forked. */ +unsigned long ipi_count=0; /* Number of IPIs delivered. */ +static atomic_t smp_commenced = ATOMIC_INIT(0); + +spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; + +/* + * Setup routine for controlling SMP activation + * + * Command-line option of "nosmp" or "maxcpus=0" will disable SMP + * activation entirely (the MPS table probe still happens, though). + * + * Command-line option of "maxcpus=", where is an integer + * greater than 0, limits the maximum number of CPUs activated in + * SMP mode to . + */ + +void __init smp_setup(char *str, int *ints) +{ + if (ints && ints[0] > 0) + max_cpus = ints[1]; + else + max_cpus = 0; +} + +/* + * Reboot, halt and power_off routines for SMP. + */ +void do_machine_restart(void) +{ + smp_send_stop(); + if (MACHINE_IS_VM) { + cpcmd("IPL", NULL, 0); + } else { + /* FIXME: how to reipl ? */ + disabled_wait(2); + } +} + +void machine_restart(char * __unused) +{ + if (smp_processor_id() != 0) { + smp_ext_call_async(0, ec_restart); + for (;;); + } else + do_machine_restart(); +} + +void do_machine_halt(void) +{ + smp_send_stop(); + if (MACHINE_IS_VM) { + cpcmd("IPL CMS", NULL, 0); + } else { + disabled_wait(0); + } +} + +void machine_halt(void) +{ + if (smp_processor_id() != 0) { + smp_ext_call_async(0, ec_halt); + for (;;); + } else + do_machine_halt(); +} + +void do_machine_power_off(void) +{ + smp_send_stop(); + if (MACHINE_IS_VM) { + cpcmd("IPL CMS", NULL, 0); + } else { + disabled_wait(0); + } +} + +void machine_power_off(void) +{ + if (smp_processor_id() != 0) { + smp_ext_call_async(0, ec_power_off); + for (;;); + } else + do_machine_power_off(); +} + +/* + * This is the main routine where commands issued by other + * cpus are handled. + */ + +void do_ext_call_interrupt(__u16 source_cpu_addr) +{ + ec_ext_call *ec, *next; + int bits; + + /* + * handle bit signal external calls + * + * For the ec_schedule signal we have to do nothing. All the work + * is done automatically when we return from the interrupt. + * For the ec_restart, ec_halt and ec_power_off we call the + * appropriate routine. + */ + do { + bits = atomic_read(&S390_lowcore.ext_call_fast); + } while (atomic_compare_and_swap(bits,0,&S390_lowcore.ext_call_fast)); + + if (test_bit(ec_restart, &bits)) + do_machine_restart(); + if (test_bit(ec_halt, &bits)) + do_machine_halt(); + if (test_bit(ec_power_off, &bits)) + do_machine_power_off(); + + /* + * Handle external call commands with a parameter area + */ + do { + ec = (ec_ext_call *) atomic_read(&S390_lowcore.ext_call_queue); + } while (atomic_compare_and_swap((int) ec, 0, + &S390_lowcore.ext_call_queue)); + if (ec == NULL) + return; /* no command signals */ + + /* Make a fifo out of the lifo */ + next = ec; + ec->next = NULL; + while (next != NULL) { + ec_ext_call *tmp = next->next; + next->next = ec; + ec = next; + next = tmp; + } + + /* Execute every sigp command on the queue */ + while (ec != NULL) { + switch (ec->cmd) { + case ec_get_ctl: { + ec_creg_parms *pp; + pp = (ec_creg_parms *) ec->parms; + atomic_set(&ec->status,ec_executing); + asm volatile ( + " bras 1,0f\n" + " stctl 0,0,0(%0)\n" + "0: ex %1,0(1)\n" + : : "a" (pp->cregs+pp->start_ctl), + "a" ((pp->start_ctl<<4) + pp->end_ctl) + : "memory", "1" ); + atomic_set(&ec->status,ec_done); + return; + } + case ec_set_ctl: { + ec_creg_parms *pp; + pp = (ec_creg_parms *) ec->parms; + atomic_set(&ec->status,ec_executing); + asm volatile ( + " bras 1,0f\n" + " lctl 0,0,0(%0)\n" + "0: ex %1,0(1)\n" + : : "a" (pp->cregs+pp->start_ctl), + "a" ((pp->start_ctl<<4) + pp->end_ctl) + : "memory", "1" ); + atomic_set(&ec->status,ec_done); + return; + } + case ec_set_ctl_masked: { + ec_creg_mask_parms *pp; + u32 cregs[16]; + int i; + + pp = (ec_creg_mask_parms *) ec->parms; + atomic_set(&ec->status,ec_executing); + asm volatile ( + " bras 1,0f\n" + " stctl 0,0,0(%0)\n" + "0: ex %1,0(1)\n" + : : "a" (cregs+pp->start_ctl), + "a" ((pp->start_ctl<<4) + pp->end_ctl) + : "memory", "1" ); + for (i = pp->start_ctl; i <= pp->end_ctl; i++) + cregs[i] = (cregs[i] & pp->andvals[i]) + | pp->orvals[i]; + asm volatile ( + " bras 1,0f\n" + " lctl 0,0,0(%0)\n" + "0: ex %1,0(1)\n" + : : "a" (cregs+pp->start_ctl), + "a" ((pp->start_ctl<<4) + pp->end_ctl) + : "memory", "1" ); + atomic_set(&ec->status,ec_done); + return; + } + default: + } + ec = ec->next; + } +} + +/* + * Send an external call sigp to another cpu and wait for its completion. + */ +sigp_ccode smp_ext_call_sync(int cpu, ec_cmd_sig cmd, void *parms) +{ + struct _lowcore *lowcore = &get_cpu_lowcore(cpu); + sigp_ccode ccode; + ec_ext_call ec; + + ec.cmd = cmd; + atomic_set(&ec.status, ec_pending); + ec.parms = parms; + do { + ec.next = (ec_ext_call*) atomic_read(&lowcore->ext_call_queue); + } while (atomic_compare_and_swap((int) ec.next, (int)(&ec), + &lowcore->ext_call_queue)); + /* + * We try once to deliver the signal. There are four possible + * return codes: + * 0) Order code accepted - can't show up on an external call + * 1) Status stored - fine, wait for completion. + * 2) Busy - there is another signal pending. Thats fine too, because + * do_ext_call from the pending signal will execute all signals on + * the queue. We wait for completion. + * 3) Not operational - something very bad has happened to the cpu. + * do not wait for completion. + */ + ccode = signal_processor(cpu, sigp_external_call); + + if (ccode != sigp_not_operational) + /* wait for completion, FIXME: possible seed of a deadlock */ + while (atomic_read(&ec.status) != ec_done); + + return ccode; +} + +/* + * Send an external call sigp to another cpu and return without waiting + * for its completion. Currently we do not support parameters with + * asynchronous sigps. + */ +sigp_ccode smp_ext_call_async(int cpu, ec_bit_sig sig) +{ + struct _lowcore *lowcore = &get_cpu_lowcore(cpu); + sigp_ccode ccode; + + /* + * Set signaling bit in lowcore of target cpu and kick it + */ + atomic_set_mask(1<ext_call_fast); + ccode = signal_processor(cpu, sigp_external_call); + return ccode; +} + +/* + * Send an external call sigp to every other cpu in the system and + * wait for the completion of the sigps. + */ +void smp_ext_call_sync_others(ec_cmd_sig cmd, void *parms) +{ + struct _lowcore *lowcore; + ec_ext_call ec[NR_CPUS]; + sigp_ccode ccode; + int i; + + for (i = 0; i < smp_num_cpus; i++) { + if (smp_processor_id() == i) + continue; + lowcore = &get_cpu_lowcore(i); + ec[i].cmd = cmd; + atomic_set(&ec[i].status, ec_pending); + ec[i].parms = parms; + do { + ec[i].next = (ec_ext_call *) + atomic_read(&lowcore->ext_call_queue); + } while (atomic_compare_and_swap((int) ec[i].next, (int)(ec+i), + &lowcore->ext_call_queue)); + ccode = signal_processor(i, sigp_external_call); + } + + /* wait for completion, FIXME: possible seed of a deadlock */ + for (i = 0; i < smp_num_cpus; i++) { + if (smp_processor_id() == i) + continue; + while (atomic_read(&ec[i].status) != ec_done); + } +} + +/* + * Send an external call sigp to every other cpu in the system and + * return without waiting for the completion of the sigps. Currently + * we do not support parameters with asynchronous sigps. + */ +void smp_ext_call_async_others(ec_bit_sig sig) +{ + struct _lowcore *lowcore; + sigp_ccode ccode; + int i; + + for (i = 0; i < smp_num_cpus; i++) { + if (smp_processor_id() == i) + continue; + lowcore = &get_cpu_lowcore(i); + /* + * Set signaling bit in lowcore of target cpu and kick it + */ + atomic_set_mask(1<ext_call_fast); + ccode = signal_processor(i, sigp_external_call); + } +} + +/* + * cycles through all the cpus, + * returns early if info is not NULL & the processor has something + * of intrest to report in the info structure. + * it returns the next cpu to check if it returns early. + * i.e. it should be used as follows if you wish to receive info. + * next_cpu=0; + * do + * { + * info->cpu=next_cpu; + * next_cpu=smp_signal_others(order_code,parameter,TRUE,info); + * ... check info here + * } while(next_cpu<=smp_num_cpus) + * + * if you are lazy just use it like + * smp_signal_others(order_code,parameter,0,TRUE,NULL); + */ +int smp_signal_others(sigp_order_code order_code, u32 parameter, + int spin, sigp_info *info) +{ + sigp_ccode ccode; + u32 dummy; + u16 i; + + if (info) + info->intresting = FALSE; + for (i = (info ? info->cpu : 0); i < smp_num_cpus; i++) { + if (smp_processor_id() != i) { + do { + ccode = signal_processor_ps( + (info ? &info->status : &dummy), + parameter, i, order_code); + } while(spin && ccode == sigp_busy); + if (info && ccode != sigp_order_code_accepted) { + info->intresting = TRUE; + info->cpu = i; + info->ccode = ccode; + i++; + break; + } + } + } + return i; +} + +/* + * this function sends a 'stop' sigp to all other CPUs in the system. + * it goes straight through. + */ + +void smp_send_stop(void) +{ + smp_signal_others(sigp_stop, 0, TRUE, NULL); +} + +/* + * this function sends a 'reschedule' IPI to another CPU. + * it goes straight through and wastes no time serializing + * anything. Worst case is that we lose a reschedule ... + */ + +void smp_send_reschedule(int cpu) +{ + smp_ext_call_async(cpu, ec_schedule); +} + +/* + * Set a bit in a control register of all cpus + */ +void smp_ctl_set_bit(int cr, int bit) { + ec_creg_mask_parms parms; + + if (atomic_read(&smp_commenced) != 0) { + parms.start_ctl = cr; + parms.end_ctl = cr; + parms.orvals[cr] = 1 << bit; + parms.andvals[cr] = 0xFFFFFFFF; + smp_ext_call_sync_others(ec_set_ctl_masked,&parms); + } + __ctl_set_bit(cr, bit); +} + +/* + * Clear a bit in a control register of all cpus + */ +void smp_ctl_clear_bit(int cr, int bit) { + ec_creg_mask_parms parms; + + if (atomic_read(&smp_commenced) != 0) { + parms.start_ctl = cr; + parms.end_ctl = cr; + parms.orvals[cr] = 0x00000000; + parms.andvals[cr] = ~(1 << bit); + smp_ext_call_sync_others(ec_set_ctl_masked,&parms); + } + __ctl_clear_bit(cr, bit); +} + + +/* + * Lets check how many CPUs we have. + */ + +void smp_count_cpus(void) +{ + int curr_cpu; + + __cpu_logical_map[0] = boot_cpu_addr; + current->processor = 0; + smp_num_cpus = 1; + for (curr_cpu = 0; + curr_cpu <= 65535 && smp_num_cpus < max_cpus; curr_cpu++) { + if ((__u16) curr_cpu == boot_cpu_addr) + continue; + __cpu_logical_map[smp_num_cpus] = (__u16) curr_cpu; + if (signal_processor(smp_num_cpus, sigp_sense) == + sigp_not_operational) + continue; + smp_num_cpus++; + } + printk("Detected %d CPU's\n",(int) smp_num_cpus); + printk("Boot cpu address %2X\n", boot_cpu_addr); +} + + +/* + * Activate a secondary processor. + */ +extern void init_100hz_timer(void); + +int __init start_secondary(void *cpuvoid) +{ + cpu_init(); + print_cpu_info(&safe_get_cpu_lowcore(smp_processor_id()).cpu_data); + while (!atomic_read(&smp_commenced)) + /* nothing */ ; + init_100hz_timer(); + return cpu_idle(NULL); +} + +/* + * The restart interrupt handler jumps to start_secondary directly + * without the detour over initialize_secondary. We defined it here + * so that the linker doesn't complain. + */ +void __init initialize_secondary(void) +{ +} + +static void __init do_boot_cpu(int cpu) +{ + struct task_struct *idle; + struct _lowcore *cpu_lowcore; + /* + * We need an idle process for each processor. + */ + + kernel_thread(start_secondary,(void *)cpu, CLONE_PID); + idle = task[cpu]; + if (!idle) + panic("No idle process for CPU %d",cpu); + idle->processor = cpu; + cpu_number_map[cpu] = cpu; + cpu_lowcore=&get_cpu_lowcore(cpu); + cpu_lowcore->kernel_stack=idle->tss.ksp; + __asm__ __volatile__("stctl 0,15,%0\n\t" + "stam 0,15,%1" + : "=m" (cpu_lowcore->cregs_save_area[0]), + "=m" (cpu_lowcore->access_regs_save_area[0]) + : : "memory"); + + eieio(); + signal_processor(cpu,sigp_restart); +} + +/* + * Architecture specific routine called by the kernel just before init is + * fired off. This allows the BP to have everything in order [we hope]. + * At the end of this all the APs will hit the system scheduling and off + * we go. Each AP will load the system gdt's and jump through the kernel + * init into idle(). At this point the scheduler will one day take over + * and give them jobs to do. smp_callin is a standard routine + * we use to track CPUs as they power up. + */ + +void __init smp_commence(void) +{ + /* + * Lets the callins below out of their loop. + */ + atomic_set(&smp_commenced,1); +} + +/* + * Cycle through the processors sending APIC IPIs to boot each. + */ + +void __init smp_boot_cpus(void) +{ + struct _lowcore *curr_lowcore; + sigp_ccode ccode; + int curr_cpu; + int i; + + smp_count_cpus(); + memset(lowcore_ptr,0,sizeof(lowcore_ptr)); + + /* + * Initialize the logical to physical CPU number mapping + * and the per-CPU profiling counter/multiplier + */ + + for (i = 0; i < NR_CPUS; i++) { + cpu_number_map[i] = -1; + prof_counter[i] = 1; + prof_multiplier[i] = 1; + } + + cpu_number_map[0] = 0; + print_cpu_info(&safe_get_cpu_lowcore(0).cpu_data); + + for(curr_cpu = 0; curr_cpu < smp_num_cpus; curr_cpu++) + { + curr_lowcore = (struct _lowcore *) + __get_free_page(GFP_KERNEL|GFP_DMA); + if (curr_lowcore == NULL) { + printk("smp_boot_cpus failed to allocate prefix memory\n"); + break; + } + lowcore_ptr[curr_cpu] = curr_lowcore; + memcpy(curr_lowcore, &S390_lowcore, sizeof(struct _lowcore)); + /* + * Most of the parameters are set up when the cpu is + * started up. + */ + if(smp_processor_id()==curr_cpu) + set_prefix((u32)curr_lowcore); + else { + ccode=signal_processor_p((u32)(curr_lowcore), + curr_cpu,sigp_set_prefix); + if(ccode) { + /* if this gets troublesome I'll have to do + * something about it. */ + printk("ccode %d for cpu %d returned when " + "setting prefix in smp_boot_cpus not good.\n", + (int)ccode,(int)curr_cpu); + } + else + do_boot_cpu(curr_cpu); + } + } +} + +/* + * the frequency of the profiling timer can be changed + * by writing a multiplier value into /proc/profile. + * + * usually you want to run this on all CPUs ;) + */ +int setup_profiling_timer(unsigned int multiplier) +{ + return 0; +} + +/* + * Local timer interrupt handler. It does both profiling and + * process statistics/rescheduling. + * + * We do profiling in every local tick, statistics/rescheduling + * happen only every 'profiling multiplier' ticks. The default + * multiplier is 1 and it can be changed by writing the new multiplier + * value into /proc/profile. + */ + +void smp_local_timer_interrupt(struct pt_regs * regs) +{ + int cpu = smp_processor_id(); + + /* + * The profiling function is SMP safe. (nothing can mess + * around with "current", and the profiling counters are + * updated with atomic operations). This is especially + * useful with a profiling multiplier != 1 + */ + if (!user_mode(regs)) + s390_do_profile(regs->psw.addr); + + if (!--prof_counter[cpu]) { + int user=0,system=0; + struct task_struct * p = current; + + /* + * After doing the above, we need to make like + * a normal interrupt - otherwise timer interrupts + * ignore the global interrupt lock, which is the + * WrongThing (tm) to do. + */ + + if (user_mode(regs)) + user=1; + else + system=1; + + irq_enter(cpu, 0); + update_one_process(p, 1, user, system, cpu); + if (p->pid) { + p->counter -= 1; + if (p->counter < 0) { + p->counter = 0; + p->need_resched = 1; + } + if (p->priority < DEF_PRIORITY) { + kstat.cpu_nice += user; + kstat.per_cpu_nice[cpu] += user; + } else { + kstat.cpu_user += user; + kstat.per_cpu_user[cpu] += user; + } + kstat.cpu_system += system; + kstat.per_cpu_system[cpu] += system; + + } + prof_counter[cpu]=prof_multiplier[cpu]; + irq_exit(cpu, 0); + } +} + diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/sys_s390.c linux/arch/s390/kernel/sys_s390.c --- v2.2.13/linux/arch/s390/kernel/sys_s390.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/sys_s390.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,225 @@ +/* + * arch/s390/kernel/sys_s390.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * + * Derived from "arch/i386/kernel/sys_i386.c" + * + * This file contains various random system calls that + * have a non-standard calling sequence on the Linux/s390 + * platform. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * sys_pipe() is the normal C calling standard for creating + * a pipe. It's not the way Unix traditionally does this, though. + */ +asmlinkage int sys_pipe(unsigned long * fildes) +{ + int fd[2]; + int error; + + lock_kernel(); + error = do_pipe(fd); + unlock_kernel(); + if (!error) { + if (copy_to_user(fildes, fd, 2*sizeof(int))) + error = -EFAULT; + } + return error; +} + +/* + * Perform the select(nd, in, out, ex, tv) and mmap() system + * calls. Linux/i386 didn't use to be able to handle more than + * 4 system call parameters, so these system calls used a memory + * block for parameter passing.. + */ + +struct mmap_arg_struct { + unsigned long addr; + unsigned long len; + unsigned long prot; + unsigned long flags; + unsigned long fd; + unsigned long offset; +}; + +asmlinkage int old_mmap(struct mmap_arg_struct *arg) +{ + int error = -EFAULT; + struct file * file = NULL; + struct mmap_arg_struct a; + + if (copy_from_user(&a, arg, sizeof(a))) + return -EFAULT; + + down(¤t->mm->mmap_sem); + lock_kernel(); + if (!(a.flags & MAP_ANONYMOUS)) { + error = -EBADF; + file = fget(a.fd); + if (!file) + goto out; + } + a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + + error = do_mmap(file, a.addr, a.len, a.prot, a.flags, a.offset); + if (file) + fput(file); +out: + unlock_kernel(); + up(¤t->mm->mmap_sem); + return error; +} + +extern asmlinkage int sys_select(int, fd_set *, fd_set *, fd_set *, struct timeval *); + +struct sel_arg_struct { + unsigned long n; + fd_set *inp, *outp, *exp; + struct timeval *tvp; +}; + +asmlinkage int old_select(struct sel_arg_struct *arg) +{ + struct sel_arg_struct a; + + if (copy_from_user(&a, arg, sizeof(a))) + return -EFAULT; + /* sys_select() does the appropriate kernel locking */ + return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp); +} + +/* + * sys_ipc() is the de-multiplexer for the SysV IPC calls.. + * + * This is really horribly ugly. + */ +asmlinkage int sys_ipc (uint call, int first, int second, + int third, void *ptr, long fifth) +{ + int ret; + + switch (call) { + case SEMOP: + return sys_semop (first, (struct sembuf *)ptr, second); + case SEMGET: + return sys_semget (first, second, third); + case SEMCTL: { + union semun fourth; + if (!ptr) + return -EINVAL; + if (get_user(fourth.__pad, (void **) ptr)) + return -EFAULT; + return sys_semctl (first, second, third, fourth); + } + case MSGSND: + return sys_msgsnd (first, (struct msgbuf *) ptr, + second, third); + break; + case MSGRCV: + return sys_msgrcv (first, + (struct msgbuf *) ptr, + second, fifth, third); + case MSGGET: + return sys_msgget ((key_t) first, second); + case MSGCTL: + return sys_msgctl (first, second, (struct msqid_ds *) ptr); + + case SHMAT: { + ulong raddr; + ret = sys_shmat (first, (char *) ptr, second, &raddr); + if (ret) + return ret; + return put_user (raddr, (ulong *) third); + break; + } + case SHMDT: + return sys_shmdt ((char *)ptr); + case SHMGET: + return sys_shmget (first, second, third); + case SHMCTL: + return sys_shmctl (first, second, + (struct shmid_ds *) ptr); + default: + return -EINVAL; + + } + + return -EINVAL; +} + +/* + * Old cruft + */ +asmlinkage int sys_uname(struct old_utsname * name) +{ + int err; + if (!name) + return -EFAULT; + down(&uts_sem); + err=copy_to_user(name, &system_utsname, sizeof (*name)); + up(&uts_sem); + return err?-EFAULT:0; +} + +asmlinkage int sys_olduname(struct oldold_utsname * name) +{ + int error; + + if (!name) + return -EFAULT; + if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname))) + return -EFAULT; + + down(&uts_sem); + + error = __copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN); + error |= __put_user(0,name->sysname+__OLD_UTS_LEN); + error |= __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN); + error |= __put_user(0,name->nodename+__OLD_UTS_LEN); + error |= __copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN); + error |= __put_user(0,name->release+__OLD_UTS_LEN); + error |= __copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN); + error |= __put_user(0,name->version+__OLD_UTS_LEN); + error |= __copy_to_user(&name->machine,&system_utsname.machine,__OLD_UTS_LEN); + error |= __put_user(0,name->machine+__OLD_UTS_LEN); + + up(&uts_sem); + + error = error ? -EFAULT : 0; + + return error; +} + +asmlinkage int sys_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + return -ERESTARTNOHAND; +} + +asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on) +{ + return -ENOSYS; +} + diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/time.c linux/arch/s390/kernel/time.c --- v2.2.13/linux/arch/s390/kernel/time.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/time.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,250 @@ +/* + * arch/s390/kernel/time.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hp@de.ibm.com), + * Martin Schwidefsky (schwidefsky@de.ibm.com), + * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) + * + * Derived from "arch/i386/kernel/time.c" + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "irq.h" + + +extern volatile unsigned long lost_ticks; + +/* change this if you have some constant time drift */ +#define USECS_PER_JIFFY ((signed long)1000000/HZ) +#define CLK_TICKS_PER_JIFFY ((signed long)USECS_PER_JIFFY<<12) + +#define TICK_SIZE tick + +static uint64_t init_timer_cc, last_timer_cc; + +extern rwlock_t xtime_lock; + +void tod_to_timeval(uint64_t todval, struct timeval *xtime) +{ + const int high_bit = 0x80000000L; + const int c_f4240 = 0xf4240L; + const int c_7a120 = 0x7a120; + /* We have to divide the 64 bit value todval by 4096 + * (because the 2^12 bit is the one that changes every + * microsecond) and then split it into seconds and + * microseconds. A value of max (2^52-1) divided by + * the value 0xF4240 can yield a max result of approx + * (2^32.068). Thats to big to fit into a signed int + * ... hacking time! + */ + asm volatile ("L 2,%1\n\t" + "LR 3,2\n\t" + "SRL 2,12\n\t" + "SLL 3,20\n\t" + "L 4,%O1+4(%R1)\n\t" + "SRL 4,12\n\t" + "OR 3,4\n\t" /* now R2/R3 contain (todval >> 12) */ + "SR 4,4\n\t" + "CL 2,%2\n\t" + "JL .+12\n\t" + "S 2,%2\n\t" + "L 4,%3\n\t" + "D 2,%4\n\t" + "OR 3,4\n\t" + "ST 2,%O0+4(%R0)\n\t" + "ST 3,%0" + : "=m" (*xtime) : "m" (todval), + "m" (c_7a120), "m" (high_bit), "m" (c_f4240) + : "cc", "memory", "2", "3", "4" ); +} + +unsigned long do_gettimeoffset(void) +{ + __u64 timer_cc; + + asm volatile ("STCK %0" : "=m" (timer_cc)); + /* We require the offset from the previous interrupt */ + return ((unsigned long)((timer_cc - last_timer_cc)>>12)); +} + +/* + * This version of gettimeofday has microsecond resolution. + */ +void do_gettimeofday(struct timeval *tv) +{ + extern volatile unsigned long lost_ticks; + unsigned long flags; + unsigned long usec, sec; + + read_lock_irqsave(&xtime_lock, flags); + usec = do_gettimeoffset(); + if (lost_ticks) + usec +=(USECS_PER_JIFFY*lost_ticks); + sec = xtime.tv_sec; + usec += xtime.tv_usec; + read_unlock_irqrestore(&xtime_lock, flags); + + while (usec >= 1000000) { + usec -= 1000000; + sec++; + } + + tv->tv_sec = sec; + tv->tv_usec = usec; +} + +void do_settimeofday(struct timeval *tv) +{ + + write_lock_irq(&xtime_lock); + /* This is revolting. We need to set the xtime.tv_usec + * correctly. However, the value in this location is + * is value at the last tick. + * Discover what correction gettimeofday + * would have done, and then undo it! + */ + tv->tv_usec -= do_gettimeoffset(); + + while (tv->tv_usec < 0) { + tv->tv_usec += 1000000; + tv->tv_sec--; + } + + xtime = *tv; + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; + write_unlock_irq(&xtime_lock); +} + +/* + * timer_interrupt() needs to keep up the real-time clock, + * as well as call the "do_timer()" routine every clocktick + */ + +#ifdef __SMP__ +extern __u16 boot_cpu_addr; +#endif + +void do_timer_interrupt(struct pt_regs *regs,int error_code) +{ + unsigned long flags; + + /* + * reset timer to 10ms minus time already elapsed + * since timer-interrupt pending + */ + + save_flags(flags); + cli(); +#ifdef __SMP__ + if(S390_lowcore.cpu_data.cpu_addr==boot_cpu_addr) { + write_lock(&xtime_lock); + last_timer_cc = S390_lowcore.jiffy_timer_cc; + } +#else + last_timer_cc = S390_lowcore.jiffy_timer_cc; +#endif + /* set clock comparator */ + S390_lowcore.jiffy_timer_cc += CLK_TICKS_PER_JIFFY; + asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer_cc)); + +/* + * In the SMP case we use the local timer interrupt to do the + * profiling, except when we simulate SMP mode on a uniprocessor + * system, in that case we have to call the local interrupt handler. + */ +#ifdef __SMP__ + /* when SMP, do smp_local_timer_interrupt for *all* CPUs, + but only do the rest for the boot CPU */ + smp_local_timer_interrupt(regs); +#else + if (!user_mode(regs)) + s390_do_profile(regs->psw.addr); +#endif + +#ifdef __SMP__ + if(S390_lowcore.cpu_data.cpu_addr==boot_cpu_addr) +#endif + { + do_timer(regs); +#ifdef __SMP__ + write_unlock(&xtime_lock); +#endif + } + restore_flags(flags); + +} + +/* + * Start the clock comparator on the current CPU + */ +static long cr0 __attribute__ ((aligned (8))); + +void init_100hz_timer(void) +{ + /* allow clock comparator timer interrupt */ + asm volatile ("STCTL 0,0,%0" : "=m" (cr0) : : "memory"); + cr0 |= 0x800; + asm volatile ("LCTL 0,0,%0" : : "m" (cr0) : "memory"); + /* set clock comparator */ + /* read the TOD clock */ + asm volatile ("STCK %0" : "=m" (S390_lowcore.jiffy_timer_cc)); + S390_lowcore.jiffy_timer_cc += CLK_TICKS_PER_JIFFY; + asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer_cc)); +} + +/* + * Initialize the TOD clock and the CPU timer of + * the boot cpu. + */ +__initfunc(void time_init(void)) +{ + int cc; + + /* kick the TOD clock */ + asm volatile ("STCK %1\n\t" + "IPM %0\n\t" + "SRL %0,28" : "=r" (cc), "=m" (init_timer_cc)); + switch (cc) { + case 0: /* clock in set state: all is fine */ + break; + case 1: /* clock in non-set state: FIXME */ + printk("time_init: TOD clock in non-set state\n"); + break; + case 2: /* clock in error state: FIXME */ + printk("time_init: TOD clock in error state\n"); + break; + case 3: /* clock in stopped or not-operational state: FIXME */ + printk("time_init: TOD clock stopped/non-operational\n"); + break; + } + init_100hz_timer(); + init_timer_cc = S390_lowcore.jiffy_timer_cc; + init_timer_cc -= 0x8126d60e46000000LL - + (0x3c26700LL*1000000*4096); + tod_to_timeval(init_timer_cc, &xtime); +} diff -u --recursive --new-file v2.2.13/linux/arch/s390/kernel/traps.c linux/arch/s390/kernel/traps.c --- v2.2.13/linux/arch/s390/kernel/traps.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/kernel/traps.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,461 @@ +/* + * arch/s390/kernel/traps.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + * + * Derived from "arch/i386/kernel/traps.c" + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* + * 'Traps.c' handles hardware traps and faults after we have saved some + * state in 'asm.s'. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#if CONFIG_REMOTE_DEBUG +#include +#endif + +/* Called from entry.S only */ +extern void handle_per_exception(struct pt_regs *regs); + +typedef void pgm_check_handler_t(struct pt_regs *, long); +pgm_check_handler_t *pgm_check_table[128]; + +extern pgm_check_handler_t default_trap_handler; +extern pgm_check_handler_t do_page_fault; + +asmlinkage int system_call(void); + +static inline void console_verbose(void) +{ + extern int console_loglevel; + console_loglevel = 15; +} + +#define DO_ERROR(trapnr, signr, str, name, tsk) \ +asmlinkage void name(struct pt_regs * regs, long error_code) \ +{ \ + if (check_for_fixup(regs) == 0) { \ + tsk->tss.error_code = error_code; \ + tsk->tss.trap_no = trapnr; \ + force_sig(signr, tsk); \ + die(str,regs,error_code); \ + } \ +} + + +void page_exception(void); + +/* TODO: define these as 'pgm_check_handler_t xxx;' +asmlinkage void divide_error(void); +asmlinkage void debug(void); +asmlinkage void nmi(void); +asmlinkage void int3(void); +asmlinkage void overflow(void); +asmlinkage void bounds(void); +asmlinkage void invalid_op(void); +asmlinkage void device_not_available(void); +asmlinkage void double_fault(void); +asmlinkage void coprocessor_segment_overrun(void); +asmlinkage void invalid_TSS(void); +asmlinkage void segment_not_present(void); +asmlinkage void stack_segment(void); +asmlinkage void general_protection(void); +asmlinkage void coprocessor_error(void); +asmlinkage void reserved(void); +asmlinkage void alignment_check(void); +asmlinkage void spurious_interrupt_bug(void); +*/ + +int kstack_depth_to_print = 24; + +/* + * These constants are for searching for possible module text + * segments. VMALLOC_OFFSET comes from mm/vmalloc.c; MODULE_RANGE is + * a guess of how much space is likely to be vmalloced. + */ +#define VMALLOC_OFFSET (8*1024*1024) +#define MODULE_RANGE (8*1024*1024) + +void show_crashed_task_info(void) +{ + printk("CPU: %d\n",smp_processor_id()); + printk("Process %s (pid: %d, stackpage=%08X)\n", + current->comm, current->pid, 4096+(addr_t)current); + show_regs(current,NULL,NULL); +} +#if 0 +static void show_registers(struct pt_regs *regs) +{ + printk("CPU: %d\nPSW: %08lx %08lx\n", + smp_processor_id(), (unsigned long) regs->psw.mask, + (unsigned long) regs->psw.addr); + printk("GPRS:\n"); + + printk("%08lx %08lx %08lx %08lx\n", + regs->gprs[0], regs->gprs[1], + regs->gprs[2], regs->gprs[3]); + printk("%08lx %08lx %08lx %08lx\n", + regs->gprs[4], regs->gprs[5], + regs->gprs[6], regs->gprs[7]); + printk("%08lx %08lx %08lx %08lx\n", + regs->gprs[8], regs->gprs[9], + regs->gprs[10], regs->gprs[11]); + printk("%08lx %08lx %08lx %08lx\n", + regs->gprs[12], regs->gprs[13], + regs->gprs[14], regs->gprs[15]); + printk("Process %s (pid: %d, stackpage=%08lx)\nStack: ", + current->comm, current->pid, 4096+(unsigned long)current); +/* + stack = (unsigned long *) esp; + for(i=0; i < kstack_depth_to_print; i++) { + if (((long) stack & 4095) == 0) + break; + if (i && ((i % 8) == 0)) + printk("\n "); + printk("%08lx ", get_seg_long(ss,stack++)); + } + printk("\nCall Trace: "); + stack = (unsigned long *) esp; + i = 1; + module_start = PAGE_OFFSET + (max_mapnr << PAGE_SHIFT); + module_start = ((module_start + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)); + module_end = module_start + MODULE_RANGE; + while (((long) stack & 4095) != 0) { + addr = get_seg_long(ss, stack++); */ + /* + * If the address is either in the text segment of the + * kernel, or in the region which contains vmalloc'ed + * memory, it *may* be the address of a calling + * routine; if so, print it so that someone tracing + * down the cause of the crash will be able to figure + * out the call path that was taken. + */ +/* if (((addr >= (unsigned long) &_stext) && + (addr <= (unsigned long) &_etext)) || + ((addr >= module_start) && (addr <= module_end))) { + if (i && ((i % 8) == 0)) + printk("\n "); + printk("[<%08lx>] ", addr); + i++; + } + } + printk("\nCode: "); + for(i=0;i<20;i++) + printk("%02x ",0xff & get_seg_byte(regs->xcs & 0xffff,(i+(char *)regs->eip))); + printk("\n"); +*/ +} +#endif + + +spinlock_t die_lock; + +void die(const char * str, struct pt_regs * regs, long err) +{ + console_verbose(); + spin_lock_irq(&die_lock); + printk("%s: %04lx\n", str, err & 0xffff); + show_crashed_task_info(); + spin_unlock_irq(&die_lock); + do_exit(SIGSEGV); +} + +int check_for_fixup(struct pt_regs * regs) +{ + if (!(regs->psw.mask & PSW_PROBLEM_STATE)) { + unsigned long fixup; + fixup = search_exception_table(regs->psw.addr); + if (fixup) { + regs->psw.addr = fixup; + return 1; + } + } + return 0; +} + +int do_debugger_trap(struct pt_regs *regs,int signal) +{ + if(regs->psw.mask&PSW_PROBLEM_STATE) + { + if(current->flags & PF_PTRACED) + force_sig(signal,current); + else + return(TRUE); + } + else + { +#if CONFIG_REMOTE_DEBUG + if(gdb_stub_initialised) + { + gdb_stub_handle_exception((gdb_pt_regs *)regs,signal); + return(FALSE); + } +#endif + return(TRUE); + } + return(FALSE); +} + + +DO_ERROR(2, SIGILL, "privileged operation", privileged_op, current) +DO_ERROR(3, SIGILL, "execute exception", execute_exception, current) +DO_ERROR(5, SIGSEGV, "addressing exception", addressing_exception, current) +DO_ERROR(9, SIGFPE, "fixpoint divide exception", divide_exception, current) +DO_ERROR(0x12, SIGILL, "translation exception", translation_exception, current) +DO_ERROR(0x13, SIGILL, "special operand exception", special_op_exception, current) +DO_ERROR(0x15, SIGILL, "operand exception", operand_exception, current) + +/* need to define +DO_ERROR( 6, SIGILL, "invalid operand", invalid_op, current) +DO_ERROR( 8, SIGSEGV, "double fault", double_fault, current) +DO_ERROR( 9, SIGFPE, "coprocessor segment overrun", coprocessor_segment_overrun, last_task_used_math) +DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS, current) +DO_ERROR(11, SIGBUS, "segment not present", segment_not_present, current) +DO_ERROR(12, SIGBUS, "stack segment", stack_segment, current) +DO_ERROR(17, SIGSEGV, "alignment check", alignment_check, current) +DO_ERROR(18, SIGSEGV, "reserved", reserved, current) +DO_ERROR(19, SIGSEGV, "cache flush denied", cache_flush_denied, current) +*/ + +#ifdef CONFIG_IEEEFPU_EMULATION + +asmlinkage void illegal_op(struct pt_regs * regs, long error_code) +{ + __u8 opcode[6]; + __u16 *location; + int do_sig = 0; + int problem_state=(regs->psw.mask & PSW_PROBLEM_STATE); + + lock_kernel(); + location = (__u16 *)(regs->psw.addr-S390_lowcore.pgm_ilc); + if(problem_state) + get_user(*((__u16 *) opcode), location); + else + *((__u16 *)opcode)=*((__u16 *)location); + if(*((__u16 *)opcode)==BREAKPOINT_U16) + { + if(do_debugger_trap(regs,SIGTRAP)) + do_sig=1; + } + else if (problem_state ) + { + if (opcode[0] == 0xb3) { + get_user(*((__u16 *) (opcode+2)), location+1); + do_sig = math_emu_b3(opcode, regs); + } else if (opcode[0] == 0xed) { + get_user(*((__u32 *) (opcode+2)), + (__u32 *)(location+1)); + do_sig = math_emu_ed(opcode, regs); + } else if (*((__u16 *) opcode) == 0xb299) { + get_user(*((__u16 *) (opcode+2)), location+1); + do_sig = math_emu_srnm(opcode, regs); + } else if (*((__u16 *) opcode) == 0xb29c) { + get_user(*((__u16 *) (opcode+2)), location+1); + do_sig = math_emu_stfpc(opcode, regs); + } else if (*((__u16 *) opcode) == 0xb29d) { + get_user(*((__u16 *) (opcode+2)), location+1); + do_sig = math_emu_lfpc(opcode, regs); + } else + do_sig = 1; + } else + do_sig = 1; + if (do_sig) { + if (check_for_fixup(regs) == 0) { + current->tss.error_code = error_code; + current->tss.trap_no = 1; + force_sig(SIGILL, current); + die("illegal operation", regs, error_code); + } + } + unlock_kernel(); +} + +asmlinkage void specification_exception(struct pt_regs * regs, long error_code) +{ + __u8 opcode[6]; + __u16 *location; + int do_sig = 0; + + lock_kernel(); + if (regs->psw.mask & 0x00010000L) { + location = (__u16 *)(regs->psw.addr-S390_lowcore.pgm_ilc); + get_user(*((__u16 *) opcode), location); + switch (opcode[0]) { + case 0x28: /* LDR Rx,Ry */ + math_emu_ldr(opcode); + break; + case 0x38: /* LER Rx,Ry */ + math_emu_ler(opcode); + break; + case 0x60: /* STD R,D(X,B) */ + get_user(*((__u16 *) (opcode+2)), location+1); + math_emu_std(opcode, regs); + break; + case 0x68: /* LD R,D(X,B) */ + get_user(*((__u16 *) (opcode+2)), location+1); + math_emu_ld(opcode, regs); + break; + case 0x70: /* STE R,D(X,B) */ + get_user(*((__u16 *) (opcode+2)), location+1); + math_emu_ste(opcode, regs); + break; + case 0x78: /* LE R,D(X,B) */ + get_user(*((__u16 *) (opcode+2)), location+1); + math_emu_le(opcode, regs); + break; + default: + do_sig = 1; + break; + } + } else + do_sig = 1; + if (do_sig) { + if (check_for_fixup(regs) == 0) { + current->tss.error_code = error_code; + current->tss.trap_no = 1; + force_sig(SIGILL, current); + die("illegal operation", regs, error_code); + } + } + unlock_kernel(); +} + +asmlinkage void data_exception(struct pt_regs * regs, long error_code) +{ + __u8 opcode[6]; + __u16 *location; + int do_sig = 0; + + lock_kernel(); + if (regs->psw.mask & 0x00010000L) { + location = (__u16 *)(regs->psw.addr-S390_lowcore.pgm_ilc); + get_user(*((__u16 *) opcode), location); + switch (opcode[0]) { + case 0x28: /* LDR Rx,Ry */ + math_emu_ldr(opcode); + break; + case 0x38: /* LER Rx,Ry */ + math_emu_ler(opcode); + break; + case 0x60: /* STD R,D(X,B) */ + get_user(*((__u16 *) (opcode+2)), location+1); + math_emu_std(opcode, regs); + break; + case 0x68: /* LD R,D(X,B) */ + get_user(*((__u16 *) (opcode+2)), location+1); + math_emu_ld(opcode, regs); + break; + case 0x70: /* STE R,D(X,B) */ + get_user(*((__u16 *) (opcode+2)), location+1); + math_emu_ste(opcode, regs); + break; + case 0x78: /* LE R,D(X,B) */ + get_user(*((__u16 *) (opcode+2)), location+1); + math_emu_le(opcode, regs); + break; + case 0xb3: + get_user(*((__u16 *) (opcode+2)), location+1); + do_sig = math_emu_b3(opcode, regs); + break; + case 0xed: + get_user(*((__u32 *) (opcode+2)), + (__u32 *)(location+1)); + do_sig = math_emu_ed(opcode, regs); + break; + case 0xb2: + if (opcode[1] == 0x99) { + get_user(*((__u16 *) (opcode+2)), location+1); + do_sig = math_emu_srnm(opcode, regs); + } else if (opcode[1] == 0x9c) { + get_user(*((__u16 *) (opcode+2)), location+1); + do_sig = math_emu_stfpc(opcode, regs); + } else if (opcode[1] == 0x9d) { + get_user(*((__u16 *) (opcode+2)), location+1); + do_sig = math_emu_lfpc(opcode, regs); + } else + do_sig = 1; + break; + default: + do_sig = 1; + break; + } + } else + do_sig = 1; + if (do_sig) { + if (check_for_fixup(regs) == 0) { + current->tss.error_code = error_code; + current->tss.trap_no = 7; + force_sig(SIGILL, current); + die("data exception", regs, error_code); + } + } + unlock_kernel(); +} + +#else +DO_ERROR(1, SIGILL, "illegal operation", illegal_op, current) +DO_ERROR(6, SIGILL, "specification exception", specification_exception, current) +DO_ERROR(7, SIGILL, "data exception", data_exception, current) +#endif /* CONFIG_IEEEFPU_EMULATION */ + + +/* init is done in lowcore.S and head.S */ + +__initfunc(void trap_init(void)) +{ + int i; + + for (i = 0; i < 128; i++) + pgm_check_table[i] = &default_trap_handler; + pgm_check_table[1] = &illegal_op; + pgm_check_table[2] = &privileged_op; + pgm_check_table[3] = &execute_exception; + pgm_check_table[5] = &addressing_exception; + pgm_check_table[6] = &specification_exception; + pgm_check_table[7] = &data_exception; + pgm_check_table[9] = ÷_exception; + pgm_check_table[0x12] = &translation_exception; + pgm_check_table[0x13] = &special_op_exception; + pgm_check_table[0x15] = &operand_exception; + pgm_check_table[4] = &do_page_fault; + pgm_check_table[0x10] = &do_page_fault; + pgm_check_table[0x11] = &do_page_fault; +} + + +void handle_per_exception(struct pt_regs *regs) +{ + if(regs->psw.mask&PSW_PROBLEM_STATE) + { + per_struct *per_info=¤t->tss.per_info; + per_info->lowcore.words.perc_atmid=S390_lowcore.per_perc_atmid; + per_info->lowcore.words.address=S390_lowcore.per_address; + per_info->lowcore.words.access_id=S390_lowcore.per_access_id; + } + if(do_debugger_trap(regs,SIGTRAP)) + printk("Spurious per exception detected\n"); +} + diff -u --recursive --new-file v2.2.13/linux/arch/s390/lib/Makefile linux/arch/s390/lib/Makefile --- v2.2.13/linux/arch/s390/lib/Makefile Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/lib/Makefile Tue Jan 4 10:12:12 2000 @@ -0,0 +1,17 @@ +# +# Makefile for s390-specific library files.. +# + +ifdef SMP +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o +else +.S.o: + $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o +endif + +L_TARGET = lib.a +L_OBJS = checksum.o delay.o memset.o strcmp.o strncpy.o + +include $(TOPDIR)/Rules.make + diff -u --recursive --new-file v2.2.13/linux/arch/s390/lib/checksum.c linux/arch/s390/lib/checksum.c --- v2.2.13/linux/arch/s390/lib/checksum.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/lib/checksum.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,56 @@ +/* + * arch/s390/lib/checksum.c + * S390 fast network checksum routines + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Ulrich Hild (first version), + * Martin Schwidefsky (schwidefsky@de.ibm.com), + * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + * + * This file contains network checksum routines + */ + +#include +#include +#include +#include +#include + +/* + * computes a partial checksum, e.g. for TCP/UDP fragments + */ +unsigned int +csum_partial (const unsigned char *buff, int len, unsigned int sum) +{ + /* + * Experiments with ethernet and slip connections show that buff + * is aligned on either a 2-byte or 4-byte boundary. + */ + __asm__ __volatile__ ( + " lr 2,%1\n" /* address in gpr 2 */ + " lr 3,%2\n" /* length in gpr 3 */ + "0: cksm %0,2\n" /* do checksum on longs */ + " jo 0b\n" + : "+&d" (sum) + : "d" (buff), "d" (len) + : "cc", "2", "3" ); + return sum; +} + +/* + * Fold a partial checksum without adding pseudo headers + */ +unsigned short csum_fold(unsigned int sum) +{ + __asm__ __volatile__ ( + " sr 3,3\n" /* %0 = H*65536 + L */ + " lr 2,%0\n" /* %0 = H L, R2/R3 = H L / 0 0 */ + " srdl 2,16\n" /* %0 = H L, R2/R3 = 0 H / L 0 */ + " alr 2,3\n" /* %0 = H L, R2/R3 = L H / L 0 */ + " alr %0,2\n" /* %0 = H+L+C L+H */ + " srl %0,16\n" /* %0 = H+L+C */ + : "+d" (sum) : : "cc", "2", "3"); + return ((unsigned short) ~sum); +} + diff -u --recursive --new-file v2.2.13/linux/arch/s390/lib/delay.c linux/arch/s390/lib/delay.c --- v2.2.13/linux/arch/s390/lib/delay.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/lib/delay.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,44 @@ +/* + * arch/s390/kernel/delay.c + * Precise Delay Loops for S390 + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * + * Derived from "arch/i386/lib/delay.c" + * Copyright (C) 1993 Linus Torvalds + * Copyright (C) 1997 Martin Mares + */ + +#include +#include + +#ifdef __SMP__ +#include +#endif + +void __delay(unsigned long loops) +{ + __asm__ __volatile__( + "0: ahi %0,-1\n" + " jnm 0b" + : /* no outputs */ : "r" (loops) ); +} + +inline void __const_udelay(unsigned long xloops) +{ + + __asm__("LR 3,%1\n\t" + "MR 2,%2\n\t" + "LR %0,2\n\t" + : "=r" (xloops) + : "r" (xloops) , "r" (loops_per_sec) + : "2" , "3"); + __delay(xloops); +} + +void __udelay(unsigned long usecs) +{ + __const_udelay(usecs * 0x000010c6); /* 2**32 / 1000000 */ +} diff -u --recursive --new-file v2.2.13/linux/arch/s390/lib/memset.S linux/arch/s390/lib/memset.S --- v2.2.13/linux/arch/s390/lib/memset.S Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/lib/memset.S Tue Jan 4 10:12:12 2000 @@ -0,0 +1,30 @@ +/* + * arch/s390/lib/memset.S + * S390 fast memset routine + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +/* + * R2 = address to memory area + * R3 = byte to fill memory with + * R4 = number of bytes to fill + */ + .globl memset +memset: + LTR 4,4 + JZ memset_end + LR 0,2 # save pointer to memory area + LR 1,3 # move pad byte to R1 + LR 3,4 + SR 4,4 # no source for MVCLE, only a pad byte + SR 5,5 + MVCLE 2,4,0(1) # thats it, MVCLE is your friend + JO .-4 + LR 2,0 # return pointer to mem. +memset_end: + BR 14 + + diff -u --recursive --new-file v2.2.13/linux/arch/s390/lib/strcmp.S linux/arch/s390/lib/strcmp.S --- v2.2.13/linux/arch/s390/lib/strcmp.S Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/lib/strcmp.S Tue Jan 4 10:12:12 2000 @@ -0,0 +1,27 @@ +/* + * arch/s390/lib/strcmp.S + * S390 strcmp routine + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +/* + * R2 = address of compare string + * R3 = address of test string + */ + .globl strcmp +strcmp: + SR 0,0 + SR 1,1 + CLST 2,3 + JO .-4 + JE strcmp_equal + IC 0,0(0,3) + IC 1,0(0,2) + SR 1,0 +strcmp_equal: + LR 2,1 + BR 14 + diff -u --recursive --new-file v2.2.13/linux/arch/s390/lib/strncpy.S linux/arch/s390/lib/strncpy.S --- v2.2.13/linux/arch/s390/lib/strncpy.S Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/lib/strncpy.S Tue Jan 4 10:12:12 2000 @@ -0,0 +1,32 @@ +/* + * arch/s390/kernel/strncpy.S + * S390 strncpy routine + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +/* + * R2 = address of destination + * R3 = address of source string + * R4 = max number of bytes to copy + */ + .globl strncpy +strncpy: + LR 1,2 # don't touch address in R2 + LTR 4,4 + JZ strncpy_exit # 0 bytes -> nothing to do + AHI 4,-1 + SR 0,0 + BASR 5,0 +strncpy_loop: + ICM 0,1,0(3) # ICM sets the cc, IC does not + LA 3,1(0,3) + STC 0,0(0,1) + LA 1,1(0,1) + JZ strncpy_exit # ICM inserted a 0x00 + BCTR 4,5 # R4 -= 1, jump to strncpy_loop if >= 0 +strncpy_exit: + BR 14 + diff -u --recursive --new-file v2.2.13/linux/arch/s390/mm/Makefile linux/arch/s390/mm/Makefile --- v2.2.13/linux/arch/s390/mm/Makefile Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/mm/Makefile Tue Jan 4 10:12:12 2000 @@ -0,0 +1,13 @@ +# +# Makefile for the linux i386-specific parts of the memory manager. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +O_TARGET := mm.o +O_OBJS := init.o fault.o ioremap.o extable.o + +include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.2.13/linux/arch/s390/mm/extable.c linux/arch/s390/mm/extable.c --- v2.2.13/linux/arch/s390/mm/extable.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/mm/extable.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,63 @@ +/* + * arch/s390/mm/extable.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hp@de.ibm.com) + * + * Derived from "arch/i386/mm/extable.c" + */ + +#include +#include +#include + +extern const struct exception_table_entry __start___ex_table[]; +extern const struct exception_table_entry __stop___ex_table[]; + +static inline unsigned long +search_one_table(const struct exception_table_entry *first, + const struct exception_table_entry *last, + unsigned long value) +{ + while (first <= last) { + const struct exception_table_entry *mid; + long diff; + + mid = (last - first) / 2 + first; + diff = mid->insn - value; + if (diff == 0) + return mid->fixup; + else if (diff < 0) + first = mid+1; + else + last = mid-1; + } + return 0; +} + +unsigned long +search_exception_table(unsigned long addr) +{ + unsigned long ret; + +#ifndef CONFIG_MODULES + addr &= 0x7fffffff; /* remove amode bit from address */ + /* There is only the kernel to search. */ + ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr); + if (ret) return FIX_PSW(ret); +#else + /* The kernel is the last "module" -- no need to treat it special. */ + struct module *mp; + addr &= 0x7fffffff; /* remove amode bit from address */ + for (mp = module_list; mp != NULL; mp = mp->next) { + if (mp->ex_table_start == NULL) + continue; + ret = search_one_table(mp->ex_table_start, + mp->ex_table_end - 1, addr); + if (ret) return FIX_PSW(ret); + } +#endif + + return 0; +} diff -u --recursive --new-file v2.2.13/linux/arch/s390/mm/fault.c linux/arch/s390/mm/fault.c --- v2.2.13/linux/arch/s390/mm/fault.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/mm/fault.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,203 @@ +/* + * arch/s390/mm/fault.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hp@de.ibm.com) + * + * Derived from "arch/i386/mm/fault.c" + * Copyright (C) 1995 Linus Torvalds + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +extern void die(const char *,struct pt_regs *,long); + +/* + * This routine handles page faults. It determines the address, + * and the problem, and then passes it off to one of the appropriate + * routines. + * + * error_code: + * ****0004 Protection -> Write-Protection (suprression) + * ****0010 Segment translation -> Not present (nullification) + * ****0011 Page translation -> Not present (nullification) + */ +asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) +{ + struct task_struct *tsk; + struct mm_struct *mm; + struct vm_area_struct * vma; + unsigned long address; + unsigned long fixup; + int write; + unsigned long psw_mask; + unsigned long psw_addr; + + /* + * get psw mask of Program old psw to find out, + * if user or kernel mode + */ + + psw_mask = S390_lowcore.program_old_psw.mask; + psw_addr = S390_lowcore.program_old_psw.addr; + + /* + * get the failing address + * more specific the segment and page table portion of + * the address + */ + + address = S390_lowcore.trans_exc_code&0x7ffff000; + + if (atomic_read(&S390_lowcore.local_irq_count)) + die("page fault from irq handler",regs,error_code); + + tsk = current; + mm = tsk->mm; + + down(&mm->mmap_sem); + + vma = find_vma(mm, address); + if (!vma) { + printk("no vma for address %lX\n",address); + goto bad_area; + } + if (vma->vm_start <= address) + goto good_area; + if (!(vma->vm_flags & VM_GROWSDOWN)) { + printk("VM_GROWSDOWN not set, but address %lX \n",address); + printk("not in vma %p (start %lX end %lX)\n",vma, + vma->vm_start,vma->vm_end); + goto bad_area; + } + if (expand_stack(vma, address)) { + printk("expand of vma failed address %lX\n",address); + printk("vma %p (start %lX end %lX)\n",vma, + vma->vm_start,vma->vm_end); + goto bad_area; + } +/* + * Ok, we have a good vm_area for this memory access, so + * we can handle it.. + */ +good_area: + write = 0; + switch (error_code & 0xFF) { + case 0x04: /* write, present*/ + write = 1; + break; + case 0x10: /* not present*/ + case 0x11: /* not present*/ + if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE))) { + printk("flags %X of vma for address %lX wrong \n", + vma->vm_flags,address); + printk("vma %p (start %lX end %lX)\n",vma, + vma->vm_start,vma->vm_end); + goto bad_area; + } + break; + default: + printk("code should be 4, 10 or 11 (%lX) \n",error_code&0xFF); + goto bad_area; + } + handle_mm_fault(tsk, vma, address, write); + + up(&mm->mmap_sem); + return; + +/* + * Something tried to access memory that isn't in our memory map.. + * Fix it, but check if it's kernel or user first.. + */ +bad_area: + up(&mm->mmap_sem); + + /* User mode accesses just cause a SIGSEGV */ + if (psw_mask & PSW_PROBLEM_STATE) { + tsk->tss.prot_addr = address; + tsk->tss.error_code = error_code; + tsk->tss.trap_no = 14; + + printk("User process fault: interruption code 0x%lX\n",error_code); + printk("failing address: %lX\n",address); + show_crashed_task_info(); + force_sig(SIGSEGV, tsk); + return; + } + + /* Are we prepared to handle this kernel fault? */ + + if ((fixup = search_exception_table(regs->psw.addr)) != 0) { + regs->psw.addr = fixup; + return; + } + +/* + * Oops. The kernel tried to access some bad page. We'll have to + * terminate things with extreme prejudice. + * + * First we check if it was the bootup rw-test, though.. + */ + if (address < PAGE_SIZE) + printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); + else + printk(KERN_ALERT "Unable to handle kernel paging request"); + printk(" at virtual address %08lx\n",address); +/* + * need to define, which information is useful here + */ + + lock_kernel(); + die("Oops", regs, error_code); + do_exit(SIGKILL); + unlock_kernel(); +} + +/* + { + char c; + int i,j; + char *addr; + addr = ((char*) psw_addr)-0x20; + for (i=0;i<16;i++) { + if (i == 2) + printk("\n"); + printk ("%08X: ",(unsigned long) addr); + for (j=0;j<4;j++) { + printk("%08X ",*(unsigned long*)addr); + addr += 4; + } + addr -=0x10; + printk(" | "); + for (j=0;j<16;j++) { + printk("%c",(c=*addr++) < 0x20 ? '.' : c ); + } + + printk("\n"); + } + printk("\n"); + } + +*/ + + + + + + diff -u --recursive --new-file v2.2.13/linux/arch/s390/mm/init.c linux/arch/s390/mm/init.c --- v2.2.13/linux/arch/s390/mm/init.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/mm/init.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,430 @@ +/* + * arch/s390/mm/init.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hp@de.ibm.com) + * + * Derived from "arch/i386/mm/init.c" + * Copyright (C) 1995 Linus Torvalds + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static int test_access(unsigned long loc) +{ + static const int ssm_mask = 0x07000000L; + int rc, i; + + rc = 0; + for (i=0; i<4; i++) { + __asm__ __volatile__( + " slr %0,%0\n" + " ssm %1\n" + " tprot 0(%2),0\n" + "0: jne 1f\n" + " lhi %0,1\n" + "1: ssm %3\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .long 0b,1b\n" + ".previous" + : "+&d" (rc) : "i" (0), "a" (loc), "m" (ssm_mask) + : "cc"); + if (rc == 0) + break; + loc += 0x100000; + } + return rc; +} + +extern void show_net_buffers(void); + +extern unsigned long initrd_start,initrd_end; + +static inline void invalidate_page(pte_t *pte) +{ + int i; + for (i=0;i high) { + do { + if(pgd_quicklist) + free_pgd_slow(get_pgd_fast()), freed++; + if(pmd_quicklist) + free_pmd_slow(get_pmd_fast()), freed++; + if(pte_quicklist) + free_pte_slow(get_pte_fast()), freed++; + } while(pgtable_cache_size > low); + } + return freed; +} + +/* + * BAD_PAGE is the page that is used for page faults when linux + * is out-of-memory. Older versions of linux just did a + * do_exit(), but using this instead means there is less risk + * for a process dying in kernel mode, possibly leaving an inode + * unused etc.. + * + * BAD_PAGETABLE is the accompanying page-table: it is initialized + * to point to BAD_PAGE entries. + * + * ZERO_PAGE is a special page that is used for zero-initialized + * data and COW. + */ +pte_t * __bad_pagetable(void) +{ + extern char empty_bad_page_table[PAGE_SIZE]; + + memset((void *)empty_bad_page_table, 0, PAGE_SIZE); + return (pte_t *) empty_bad_page_table; +} + +pte_t __bad_page(void) +{ + extern char empty_bad_page[PAGE_SIZE]; + + memset((void *)empty_bad_page, 0, PAGE_SIZE); + return pte_mkdirty(mk_pte((unsigned long) empty_bad_page, PAGE_SHARED)); +} + +void show_mem(void) +{ + int i,free = 0,total = 0,reserved = 0; + int shared = 0, cached = 0; + + printk("Mem-info:\n"); + show_free_areas(); + printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); + i = max_mapnr; + while (i-- > 0) { + total++; + if (PageReserved(mem_map+i)) + reserved++; + else if (PageSwapCache(mem_map+i)) + cached++; + else if (!atomic_read(&mem_map[i].count)) + free++; + else + shared += atomic_read(&mem_map[i].count) - 1; + } + printk("%d pages of RAM\n",total); + printk("%d reserved pages\n",reserved); + printk("%d pages shared\n",shared); + printk("%d pages swap cached\n",cached); + printk("%ld pages in page table cache\n",pgtable_cache_size); + show_buffers(); +#ifdef CONFIG_NET + show_net_buffers(); +#endif +} + +extern unsigned long free_area_init(unsigned long, unsigned long); + +/* References to section boundaries */ + +extern unsigned long _text; +extern unsigned long _etext; +extern unsigned long _edata; +extern unsigned long __bss_start; +extern unsigned long _end; + +extern unsigned long __init_begin; +extern unsigned long __init_end; + +/* + * the initial mapping is set up by linload to address of 4 MB + * to enable virtual addressing to the first 4 MB + * paging_init will erase this initial mapping + */ + +pgd_t swapper_pg_dir[512] __attribute__ ((__aligned__ (4096))); +unsigned long empty_bad_page[1024] __attribute__ ((__aligned__ (4096))); +unsigned long empty_zero_page[1024] __attribute__ ((__aligned__ (4096))); +pte_t empty_bad_page_table[1024] __attribute__ ((__aligned__ (4096))); + +/* + * paging_init() sets up the page tables - note that the first 4MB are + * already mapped by head.S. + * + * This routines also unmaps the page at virtual kernel address 0, so + * that we can trap those pesky NULL-reference errors in the kernel. + */ +__initfunc(unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)) +{ + pgd_t * pg_dir; + pte_t * pg_table; + pte_t pte; + int i; + unsigned long tmp; + unsigned long address=0; + unsigned long pgdir_k = (__pa(swapper_pg_dir) & PAGE_MASK) | _KERNSEG_TABLE; + + /* unmap whole virtual address space */ + + pg_dir = swapper_pg_dir; + + for (i=0;ipgd0 = (_PAGE_TABLE | ((unsigned long) pg_table)); + pg_dir->pgd1 = (_PAGE_TABLE | ((unsigned long) pg_table+1024)); + pg_dir->pgd2 = (_PAGE_TABLE | ((unsigned long) pg_table+2048)); + pg_dir->pgd3 = (_PAGE_TABLE | ((unsigned long) pg_table+3072)); + pg_dir++; + + /* now change pg_table to kernel virtual addresses */ + pg_table = (pte_t *) start_mem; + start_mem += PAGE_SIZE; + + for (tmp = 0 ; tmp < PTRS_PER_PTE ; tmp++,pg_table++) { + pte = mk_pte(address, PAGE_KERNEL); + if (address >= end_mem) + pte_clear(&pte); + set_pte(pg_table, pte); + address += PAGE_SIZE; + } + } + + /* enable virtual mapping in kernel mode */ + __asm__ __volatile__(" LCTL 1,1,%0\n" + " LCTL 7,7,%0\n" + " LCTL 13,13,%0" : :"m" (pgdir_k)); + + local_flush_tlb(); + + return free_area_init(start_mem, end_mem); +} + +__initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) +{ + unsigned long start_low_mem = PAGE_SIZE; + int codepages = 0; + int reservedpages = 0; + int datapages = 0; + int initpages = 0; + unsigned long tmp; + + end_mem &= PAGE_MASK; + high_memory = (void *) end_mem; + max_mapnr = num_physpages = MAP_NR(end_mem); + + /* clear the zero-page */ + memset(empty_zero_page, 0, PAGE_SIZE); + + /* mark usable pages in the mem_map[] */ + start_low_mem = PAGE_ALIGN(start_low_mem)+PAGE_OFFSET; + +#if 0 /* FIXME: WHATS THAT FOR ?!?! */ +#ifdef __SMP__ + /* + * But first pinch a few for the stack/trampoline stuff + * FIXME: Don't need the extra page at 4K, but need to fix + * trampoline before removing it. (see the GDT stuff) + * + */ + start_low_mem += PAGE_SIZE; /* 32bit startup code*/ + start_low_mem = smp_alloc_memory(start_low_mem);/* AP processor stacks*/ +#endif +#endif + start_mem = PAGE_ALIGN(start_mem); + + tmp = start_mem; + while (tmp < end_mem) { + if ((tmp & 0x3ff000) == 0 && test_access(tmp) == 0) { + int i; + printk("4M Segment %lX not available\n",tmp); + for (i = 0;i<0x400;i++) { + set_bit(PG_reserved, + &mem_map[MAP_NR(tmp)].flags); + tmp += PAGE_SIZE; + } + } else { + clear_bit(PG_reserved, &mem_map[MAP_NR(tmp)].flags); + tmp += PAGE_SIZE; + } + } + for (tmp = PAGE_OFFSET ; tmp < end_mem ; tmp += PAGE_SIZE) { + if (PageReserved(mem_map+MAP_NR(tmp))) { + if (tmp >= (unsigned long) &_text && tmp < (unsigned long) &_edata) { + if (tmp < (unsigned long) &_etext) { +#if 0 + if (tmp >= 0x00001000UL) { + pgd_t *pgd = pgd_offset_k(tmp); + pmd_t *pmd = pmd_offset(pgd, tmp); + pte_t *pte = pte_offset(pmd, tmp); + + *pte = pte_wrprotect(*pte); + } +#endif + codepages++; + } else + datapages++; + } else if (tmp >= (unsigned long) __init_begin + && tmp < (unsigned long) __init_end) + initpages++; + else if (tmp >= (unsigned long) &__bss_start + && tmp < (unsigned long) start_mem) + datapages++; + else + reservedpages++; + continue; + } + atomic_set(&mem_map[MAP_NR(tmp)].count, 1); + +#ifdef CONFIG_BLK_DEV_INITRD + if (!initrd_start || (tmp < initrd_start || tmp >= + initrd_end)) +#endif + free_page(tmp); + } + printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init)\n", + (unsigned long) nr_free_pages << (PAGE_SHIFT-10), + max_mapnr << (PAGE_SHIFT-10), + codepages << (PAGE_SHIFT-10), + reservedpages << (PAGE_SHIFT-10), + datapages << (PAGE_SHIFT-10), + initpages << (PAGE_SHIFT-10)); + +} + +void free_initmem(void) +{ + unsigned long addr; + + addr = (unsigned long)(&__init_begin); + for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { + mem_map[MAP_NR(addr)].flags &= ~(1 << PG_reserved); + atomic_set(&mem_map[MAP_NR(addr)].count, 1); + free_page(addr); + } + printk ("Freeing unused kernel memory: %ldk freed\n", (&__init_end - &__init_begin) >> 10); +} + +void si_meminfo(struct sysinfo *val) +{ + int i; + + i = max_mapnr; + val->totalram = 0; + val->sharedram = 0; + val->freeram = nr_free_pages << PAGE_SHIFT; + val->bufferram = buffermem; + while (i-- > 0) { + if (PageReserved(mem_map+i)) + continue; + val->totalram++; + if (!atomic_read(&mem_map[i].count)) + continue; + val->sharedram += atomic_read(&mem_map[i].count) - 1; + } + val->totalram <<= PAGE_SHIFT; + val->sharedram <<= PAGE_SHIFT; + return; +} diff -u --recursive --new-file v2.2.13/linux/arch/s390/mm/ioremap.c linux/arch/s390/mm/ioremap.c --- v2.2.13/linux/arch/s390/mm/ioremap.c Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/mm/ioremap.c Tue Jan 4 10:12:12 2000 @@ -0,0 +1,120 @@ +/* + * arch/s390/mm/ioremap.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hp@de.ibm.com) + * + * Derived from "arch/i386/mm/extable.c" + * (C) Copyright 1995 1996 Linus Torvalds + * + * Re-map IO memory to kernel address space so that we can access it. + * This is needed for high PCI addresses that aren't mapped in the + * 640k-1MB IO memory area on PC's + */ + +#include +#include + +static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, + unsigned long phys_addr, unsigned long flags) +{ + unsigned long end; + + address &= ~PMD_MASK; + end = address + size; + if (end > PMD_SIZE) + end = PMD_SIZE; + do { + if (!pte_none(*pte)) + printk("remap_area_pte: page already exists\n"); + set_pte(pte, mk_pte_phys(phys_addr, __pgprot(_PAGE_PRESENT | + _PAGE_DIRTY | _PAGE_ACCESSED | flags))); + address += PAGE_SIZE; + phys_addr += PAGE_SIZE; + pte++; + } while (address < end); +} + +static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, + unsigned long phys_addr, unsigned long flags) +{ + unsigned long end; + + address &= ~PGDIR_MASK; + end = address + size; + if (end > PGDIR_SIZE) + end = PGDIR_SIZE; + phys_addr -= address; + do { + pte_t * pte = pte_alloc_kernel(pmd, address); + if (!pte) + return -ENOMEM; + remap_area_pte(pte, address, end - address, address + phys_addr, flags); + address = (address + PMD_SIZE) & PMD_MASK; + pmd++; + } while (address < end); + return 0; +} + +static int remap_area_pages(unsigned long address, unsigned long phys_addr, + unsigned long size, unsigned long flags) +{ + pgd_t * dir; + unsigned long end = address + size; + + phys_addr -= address; + dir = pgd_offset(&init_mm, address); + flush_cache_all(); + while (address < end) { + pmd_t *pmd = pmd_alloc_kernel(dir, address); + if (!pmd) + return -ENOMEM; + if (remap_area_pmd(pmd, address, end - address, + phys_addr + address, flags)) + return -ENOMEM; + set_pgdir(address, *dir); + address = (address + PGDIR_SIZE) & PGDIR_MASK; + dir++; + } + flush_tlb_all(); + return 0; +} + +/* + * Generic mapping function (not visible outside): + */ + +/* + * Remap an arbitrary physical address space into the kernel virtual + * address space. Needed when the kernel wants to access high addresses + * directly. + */ +void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) +{ + void * addr; + struct vm_struct * area; + + if (phys_addr < virt_to_phys(high_memory)) + return phys_to_virt(phys_addr); + if (phys_addr & ~PAGE_MASK) + return NULL; + size = PAGE_ALIGN(size); + if (!size || size > phys_addr + size) + return NULL; + area = get_vm_area(size); + if (!area) + return NULL; + addr = area->addr; + if (remap_area_pages(VMALLOC_VMADDR(addr), phys_addr, size, flags)) { + vfree(addr); + return NULL; + } + return addr; +} + +void iounmap(void *addr) +{ + if (addr > high_memory) + return vfree(addr); +} diff -u --recursive --new-file v2.2.13/linux/arch/s390/vmlinux.lds linux/arch/s390/vmlinux.lds --- v2.2.13/linux/arch/s390/vmlinux.lds Wed Dec 31 16:00:00 1969 +++ linux/arch/s390/vmlinux.lds Tue Jan 4 10:12:12 2000 @@ -0,0 +1,69 @@ +/* ld script to make s390 Linux kernel + * Written by Martin Schwidefsky (schwidefsky@de.ibm.com) + */ +OUTPUT_FORMAT("elf32-s390", "elf32-s390", "elf32-s390") +OUTPUT_ARCH(s390) +ENTRY(_start) +SECTIONS +{ + . = 0x00000000; + _text = .; /* Text and read-only data */ + .text : { + *(.text) + *(.fixup) + *(.gnu.warning) + } = 0x0700 + .text.lock : { *(.text.lock) } /* out-of-line lock text */ + .rodata : { *(.rodata) } + .kstrtab : { *(.kstrtab) } + + . = ALIGN(16); /* Exception table */ + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + __start___ksymtab = .; /* Kernel symbol table */ + __ksymtab : { *(__ksymtab) } + __stop___ksymtab = .; + + _etext = .; /* End of text section */ + + .data : { /* Data */ + *(.data) + CONSTRUCTORS + } + + _edata = .; /* End of data section */ + + . = ALIGN(8192); /* init_task */ + .data.init_task : { *(.data.init_task) } + + . = ALIGN(4096); /* Init code and data */ + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(4096); + __init_end = .; + + . = ALIGN(32); + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + + . = ALIGN(4096); + .data.page_aligned : { *(.data.idt) } + + + __bss_start = .; /* BSS */ + .bss : { + *(.bss) + } + _end = . ; + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } +} diff -u --recursive --new-file v2.2.13/linux/arch/sparc/Makefile linux/arch/sparc/Makefile --- v2.2.13/linux/arch/sparc/Makefile Sun Oct 4 10:22:42 1998 +++ linux/arch/sparc/Makefile Tue Jan 4 10:12:12 2000 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.39 1998/09/16 12:31:31 jj Exp $ +# $Id: Makefile,v 1.39.2.1 1999/09/20 12:05:00 jj Exp $ # sparc/Makefile # # Makefile for the architecture dependent flags and dependencies on the @@ -62,10 +62,8 @@ -$(MAKE) -C arch/sparc/boot clean archmrproper: - -$(MAKE) -C arch/sparc/math-emu cleansymlinks archdep: - -$(MAKE) -C arch/sparc/math-emu symlinks check_asm: $(MAKE) -C arch/sparc/kernel check_asm diff -u --recursive --new-file v2.2.13/linux/arch/sparc/kernel/entry.S linux/arch/sparc/kernel/entry.S --- v2.2.13/linux/arch/sparc/kernel/entry.S Tue Jan 4 11:10:32 2000 +++ linux/arch/sparc/kernel/entry.S Tue Jan 4 10:12:12 2000 @@ -1,4 +1,4 @@ -/* $Id: entry.S,v 1.159.2.2 1999/09/22 11:37:29 jj Exp $ +/* $Id: entry.S,v 1.159.2.6 1999/10/11 08:24:35 davem Exp $ * arch/sparc/kernel/entry.S: Sparc trap low-level entry points. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -829,13 +829,13 @@ .globl C_LABEL(invalid_segment_patch1_ff) .globl C_LABEL(invalid_segment_patch2_ff) C_LABEL(invalid_segment_patch1_ff): cmp %l4, 0xff -C_LABEL(invalid_segment_patch2_ff): mov 0xff, %l4 +C_LABEL(invalid_segment_patch2_ff): mov 0xff, %l3 .align 4 .globl C_LABEL(invalid_segment_patch1_1ff) .globl C_LABEL(invalid_segment_patch2_1ff) C_LABEL(invalid_segment_patch1_1ff): cmp %l4, 0x1ff -C_LABEL(invalid_segment_patch2_1ff): mov 0x1ff, %l4 +C_LABEL(invalid_segment_patch2_1ff): mov 0x1ff, %l3 .align 4 .globl C_LABEL(num_context_patch1_16), C_LABEL(num_context_patch2_16) @@ -856,7 +856,7 @@ #ifdef CONFIG_SUN4 C_LABEL(vac_hwflush_patch1_on): nop #else -C_LABEL(vac_hwflush_patch1_on): subcc %l7, (PAGE_SIZE - 4), %l7 +C_LABEL(vac_hwflush_patch1_on): addcc %l7, -PAGE_SIZE, %l7 #endif C_LABEL(vac_hwflush_patch2_on): sta %g0, [%l3 + %l7] ASI_HWFLUSHSEG @@ -972,12 +972,12 @@ bne 1f sethi %hi(C_LABEL(sun4c_kfree_ring)), %l4 or %l4, %lo(C_LABEL(sun4c_kfree_ring)), %l4 - ld [%l4 + 0x10], %l3 + ld [%l4 + 0x18], %l3 deccc %l3 ! do we have a free entry? bcs,a 2f ! no, unmap one. sethi %hi(C_LABEL(sun4c_kernel_ring)), %l4 - st %l3, [%l4 + 0x10] ! sun4c_kfree_ring.num_entries-- + st %l3, [%l4 + 0x18] ! sun4c_kfree_ring.num_entries-- ld [%l4 + 0x00], %l6 ! entry = sun4c_kfree_ring.ringhd.next st %l5, [%l6 + 0x08] ! entry->vaddr = address @@ -1000,10 +1000,11 @@ st %l6, [%l4 + 0x00] ! head->next = entry - ld [%l4 + 0x10], %l3 + ld [%l4 + 0x18], %l3 inc %l3 ! sun4c_kernel_ring.num_entries++ + st %l3, [%l4 + 0x18] b 4f - st %l3, [%l4 + 0x10] + ld [%l6 + 0x08], %l5 2: or %l4, %lo(C_LABEL(sun4c_kernel_ring)), %l4 @@ -1023,7 +1024,7 @@ C_LABEL(vac_hwflush_patch1): C_LABEL(vac_linesize_patch): subcc %l7, 16, %l7 - bg 9b + bne 9b C_LABEL(vac_hwflush_patch2): sta %g0, [%l3 + %l7] ASI_FLUSHSEG @@ -1044,47 +1045,36 @@ mov %l3, %l5 ! address = tmp +4: C_LABEL(num_context_patch1): mov 0x08, %l7 -C_LABEL(invalid_segment_patch2): - mov 0x7f, %l4 + ld [%l6 + 0x08], %l4 + ldub [%l6 + 0x0c], %l3 + or %l4, %l3, %l4 ! encode new vaddr/pseg into l4 sethi %hi(AC_CONTEXT), %l3 lduba [%l3] ASI_CONTROL, %l6 -3: - deccc %l7 - stba %l7, [%l3] ASI_CONTROL - bne 3b - stXa %l4, [%l5] ASI_SEGMAP - - stba %l6, [%l3] ASI_CONTROL - - ! reload the entry - - sethi %hi(C_LABEL(sun4c_kernel_ring)), %l4 - ld [%l4 + %lo(C_LABEL(sun4c_kernel_ring))], %l6 - - ld [%l6 + 0x08], %l5 ! restore address from entry->vaddr - -4: -C_LABEL(num_context_patch2): - mov 0x08, %l7 - - ldub [%l6 + 0x0c], %l4 ! entry->pseg - + /* Invalidate old mapping, instantiate new mapping, + * for each context. Registers l6/l7 are live across + * this loop. + */ +3: deccc %l7 sethi %hi(AC_CONTEXT), %l3 - lduba [%l3] ASI_CONTROL, %l6 - -3: - deccc %l7 stba %l7, [%l3] ASI_CONTROL +C_LABEL(invalid_segment_patch2): + mov 0x7f, %l3 + stXa %l3, [%l5] ASI_SEGMAP + andn %l4, 0x1ff, %l3 bne 3b - stXa %l4, [%l5] ASI_SEGMAP + stXa %l4, [%l3] ASI_SEGMAP + sethi %hi(AC_CONTEXT), %l3 stba %l6, [%l3] ASI_CONTROL + andn %l4, 0x1ff, %l5 + 1: sethi %hi(SUN4C_VMALLOC_START), %l4 cmp %l5, %l4 @@ -1152,6 +1142,7 @@ sun4c_fault_fromuser: SAVE_ALL + nop mov %l7, %o1 ! Decode the info from %l7 mov %l7, %o2 diff -u --recursive --new-file v2.2.13/linux/arch/sparc/kernel/head.S linux/arch/sparc/kernel/head.S --- v2.2.13/linux/arch/sparc/kernel/head.S Tue Jan 4 11:10:32 2000 +++ linux/arch/sparc/kernel/head.S Tue Jan 4 10:12:12 2000 @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.95.2.2 1999/09/23 09:53:18 anton Exp $ +/* $Id: head.S,v 1.95.2.5 1999/12/02 11:51:56 davem Exp $ * head.S: The initial boot code for the Sparc port of Linux. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -173,7 +173,8 @@ t_bad9b:BAD_TRAP(0x9b) BAD_TRAP(0x9c) BAD_TRAP(0x9d) BAD_TRAP(0x9e) BAD_TRAP(0x9f) t_getcc:GETCC_TRAP /* Get Condition Codes */ t_setcc:SETCC_TRAP /* Set Condition Codes */ -t_bada2:BAD_TRAP(0xa2) BAD_TRAP(0xa3) BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6) +t_getpsr:GETPSR_TRAP /* Get PSR Register */ +t_bada3:BAD_TRAP(0xa3) BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6) t_slowi:INDIRECT_SOLARIS_SYSCALL(156) t_bada8:BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab) t_badac:BAD_TRAP(0xac) BAD_TRAP(0xad) BAD_TRAP(0xae) BAD_TRAP(0xaf) BAD_TRAP(0xb0) @@ -253,8 +254,8 @@ LINUX_SYSCALL_TRAP BAD_TRAP(0x91) BAD_TRAP(0x92) BAD_TRAP(0x93) BAD_TRAP(0x94) BAD_TRAP(0x95) BAD_TRAP(0x96) BAD_TRAP(0x97) BAD_TRAP(0x98) BAD_TRAP(0x99) BAD_TRAP(0x9a) BAD_TRAP(0x9b) BAD_TRAP(0x9c) BAD_TRAP(0x9d) BAD_TRAP(0x9e) - BAD_TRAP(0x9f) GETCC_TRAP SETCC_TRAP - BAD_TRAP(0xa2) BAD_TRAP(0xa3) BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6) + BAD_TRAP(0x9f) GETCC_TRAP SETCC_TRAP GETPSR_TRAP + BAD_TRAP(0xa3) BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6) INDIRECT_SOLARIS_SYSCALL(156) BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab) BAD_TRAP(0xac) BAD_TRAP(0xad) BAD_TRAP(0xae) BAD_TRAP(0xaf) BAD_TRAP(0xb0) BAD_TRAP(0xb1) BAD_TRAP(0xb2) BAD_TRAP(0xb3) BAD_TRAP(0xb4) BAD_TRAP(0xb5) @@ -325,8 +326,8 @@ LINUX_SYSCALL_TRAP BAD_TRAP(0x91) BAD_TRAP(0x92) BAD_TRAP(0x93) BAD_TRAP(0x94) BAD_TRAP(0x95) BAD_TRAP(0x96) BAD_TRAP(0x97) BAD_TRAP(0x98) BAD_TRAP(0x99) BAD_TRAP(0x9a) BAD_TRAP(0x9b) BAD_TRAP(0x9c) BAD_TRAP(0x9d) BAD_TRAP(0x9e) - BAD_TRAP(0x9f) GETCC_TRAP SETCC_TRAP - BAD_TRAP(0xa2) BAD_TRAP(0xa3) BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6) + BAD_TRAP(0x9f) GETCC_TRAP SETCC_TRAP GETPSR_TRAP + BAD_TRAP(0xa3) BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6) INDIRECT_SOLARIS_SYSCALL(156) BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab) BAD_TRAP(0xac) BAD_TRAP(0xad) BAD_TRAP(0xae) BAD_TRAP(0xaf) BAD_TRAP(0xb0) BAD_TRAP(0xb1) BAD_TRAP(0xb2) BAD_TRAP(0xb3) BAD_TRAP(0xb4) BAD_TRAP(0xb5) @@ -397,8 +398,8 @@ LINUX_SYSCALL_TRAP BAD_TRAP(0x91) BAD_TRAP(0x92) BAD_TRAP(0x93) BAD_TRAP(0x94) BAD_TRAP(0x95) BAD_TRAP(0x96) BAD_TRAP(0x97) BAD_TRAP(0x98) BAD_TRAP(0x99) BAD_TRAP(0x9a) BAD_TRAP(0x9b) BAD_TRAP(0x9c) BAD_TRAP(0x9d) BAD_TRAP(0x9e) - BAD_TRAP(0x9f) GETCC_TRAP SETCC_TRAP - BAD_TRAP(0xa2) BAD_TRAP(0xa3) BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6) + BAD_TRAP(0x9f) GETCC_TRAP SETCC_TRAP GETPSR_TRAP + BAD_TRAP(0xa3) BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6) INDIRECT_SOLARIS_SYSCALL(156) BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab) BAD_TRAP(0xac) BAD_TRAP(0xad) BAD_TRAP(0xae) BAD_TRAP(0xaf) BAD_TRAP(0xb0) BAD_TRAP(0xb1) BAD_TRAP(0xb2) BAD_TRAP(0xb3) BAD_TRAP(0xb4) BAD_TRAP(0xb5) @@ -450,7 +451,7 @@ */ .ascii "HdrS" .word LINUX_VERSION_CODE - .half 0x0201 /* HdrS version */ + .half 0x0203 /* HdrS version */ C_LABEL(root_flags): .half 1 C_LABEL(root_dev): @@ -462,6 +463,8 @@ C_LABEL(sparc_ramdisk_size): .word 0 .word C_LABEL(reboot_command) + .word 0, 0, 0 + .word _end /* Cool, here we go. Pick up the romvec pointer in %o0 and stash it in * %g7 and at prom_vector_p. And also quickly check whether we are on diff -u --recursive --new-file v2.2.13/linux/arch/sparc/kernel/process.c linux/arch/sparc/kernel/process.c --- v2.2.13/linux/arch/sparc/kernel/process.c Mon Aug 9 16:05:55 1999 +++ linux/arch/sparc/kernel/process.c Tue Jan 4 10:12:12 2000 @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.137.2.1 1999/08/07 10:42:45 davem Exp $ +/* $Id: process.c,v 1.137.2.2 1999/10/01 01:32:53 anton Exp $ * linux/arch/sparc/kernel/process.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -55,7 +55,6 @@ { int ret = -EPERM; - lock_kernel(); if (current->pid != 0) goto out; @@ -100,7 +99,6 @@ } ret = 0; out: - unlock_kernel(); return ret; } diff -u --recursive --new-file v2.2.13/linux/arch/sparc/kernel/ptrace.c linux/arch/sparc/kernel/ptrace.c --- v2.2.13/linux/arch/sparc/kernel/ptrace.c Wed Mar 24 15:10:40 1999 +++ linux/arch/sparc/kernel/ptrace.c Tue Jan 4 10:12:12 2000 @@ -37,12 +37,17 @@ pmd_t * pgmiddle; pte_t * pgtable; unsigned long page, retval; + int fault; repeat: pgdir = pgd_offset(vma->vm_mm, addr); if (pgd_none(*pgdir)) { - handle_mm_fault(tsk, vma, addr, 0); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 0); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return 0; } if (pgd_bad(*pgdir)) { printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir)); @@ -51,8 +56,12 @@ } pgmiddle = pmd_offset(pgdir, addr); if (pmd_none(*pgmiddle)) { - handle_mm_fault(tsk, vma, addr, 0); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 0); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return 0; } if (pmd_bad(*pgmiddle)) { printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle)); @@ -61,8 +70,12 @@ } pgtable = pte_offset(pgmiddle, addr); if (!pte_present(*pgtable)) { - handle_mm_fault(tsk, vma, addr, 0); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 0); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return 0; } page = pte_page(*pgtable); /* this is a hack for non-kernel-mapped video buffers and similar */ @@ -90,12 +103,17 @@ pmd_t *pgmiddle; pte_t *pgtable; unsigned long page; + int fault; repeat: pgdir = pgd_offset(vma->vm_mm, addr); if (!pgd_present(*pgdir)) { - handle_mm_fault(tsk, vma, addr, 1); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 1); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return; } if (pgd_bad(*pgdir)) { printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir)); @@ -104,8 +122,12 @@ } pgmiddle = pmd_offset(pgdir, addr); if (pmd_none(*pgmiddle)) { - handle_mm_fault(tsk, vma, addr, 1); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 1); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return; } if (pmd_bad(*pgmiddle)) { printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle)); @@ -114,13 +136,21 @@ } pgtable = pte_offset(pgmiddle, addr); if (!pte_present(*pgtable)) { - handle_mm_fault(tsk, vma, addr, 1); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 1); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return; } page = pte_page(*pgtable); if (!pte_write(*pgtable)) { - handle_mm_fault(tsk, vma, addr, 1); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 1); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return; } /* this is a hack for non-kernel-mapped video buffers and similar */ flush_cache_page(vma, addr); diff -u --recursive --new-file v2.2.13/linux/arch/sparc/kernel/setup.c linux/arch/sparc/kernel/setup.c --- v2.2.13/linux/arch/sparc/kernel/setup.c Thu Apr 22 19:24:51 1999 +++ linux/arch/sparc/kernel/setup.c Tue Jan 4 10:12:12 2000 @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.105 1999/04/13 14:17:08 jj Exp $ +/* $Id: setup.c,v 1.105.2.1 1999/11/16 06:29:31 davem Exp $ * linux/arch/sparc/kernel/setup.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -424,7 +424,9 @@ initrd_start -= KERNBASE; initrd_end -= KERNBASE; break; - } + default: + break; + }; } } #endif diff -u --recursive --new-file v2.2.13/linux/arch/sparc/kernel/sparc_ksyms.c linux/arch/sparc/kernel/sparc_ksyms.c --- v2.2.13/linux/arch/sparc/kernel/sparc_ksyms.c Tue Jan 4 11:10:32 2000 +++ linux/arch/sparc/kernel/sparc_ksyms.c Tue Jan 4 10:12:12 2000 @@ -1,4 +1,4 @@ -/* $Id: sparc_ksyms.c,v 1.77.2.3 1999/09/22 17:06:50 jj Exp $ +/* $Id: sparc_ksyms.c,v 1.77.2.4 1999/09/28 16:47:30 davem Exp $ * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -63,6 +63,7 @@ extern void bcopy (const char *, char *, int); extern int __ashrdi3(int, int); +extern int __ashldi3(int, int); extern int __lshrdi3(int, int); extern void dump_thread(struct pt_regs *, struct user *); @@ -269,6 +270,7 @@ EXPORT_SYMBOL_NOVERS(memset); EXPORT_SYMBOL_NOVERS(memmove); EXPORT_SYMBOL_NOVERS(__ashrdi3); +EXPORT_SYMBOL_NOVERS(__ashldi3); EXPORT_SYMBOL_NOVERS(__lshrdi3); EXPORT_SYMBOL_DOT(rem); diff -u --recursive --new-file v2.2.13/linux/arch/sparc/kernel/sun4d_smp.c linux/arch/sparc/kernel/sun4d_smp.c --- v2.2.13/linux/arch/sparc/kernel/sun4d_smp.c Tue May 11 08:24:31 1999 +++ linux/arch/sparc/kernel/sun4d_smp.c Tue Jan 4 10:12:12 2000 @@ -99,6 +99,14 @@ local_flush_cache_all(); local_flush_tlb_all(); + /* + * Unblock the master CPU _only_ when the scheduler state + * of all secondary CPUs will be up-to-date, so after + * the SMP initialization the master will be just allowed + * to call the scheduler code. + */ + init_idle(); + /* Get our local ticker going. */ smp_setup_percpu_timer(); diff -u --recursive --new-file v2.2.13/linux/arch/sparc/kernel/sun4m_smp.c linux/arch/sparc/kernel/sun4m_smp.c --- v2.2.13/linux/arch/sparc/kernel/sun4m_smp.c Tue May 11 08:24:31 1999 +++ linux/arch/sparc/kernel/sun4m_smp.c Tue Jan 4 10:12:12 2000 @@ -93,6 +93,14 @@ local_flush_cache_all(); local_flush_tlb_all(); + /* + * Unblock the master CPU _only_ when the scheduler state + * of all secondary CPUs will be up-to-date, so after + * the SMP initialization the master will be just allowed + * to call the scheduler code. + */ + init_idle(); + /* Allow master to continue. */ swap((unsigned long *)&cpu_callin_map[cpuid], 1); local_flush_cache_all(); diff -u --recursive --new-file v2.2.13/linux/arch/sparc/kernel/sys_sparc.c linux/arch/sparc/kernel/sys_sparc.c --- v2.2.13/linux/arch/sparc/kernel/sys_sparc.c Tue May 11 08:24:31 1999 +++ linux/arch/sparc/kernel/sys_sparc.c Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: sys_sparc.c,v 1.52 1999/05/08 08:09:48 anton Exp $ +/* $Id: sys_sparc.c,v 1.52.2.1 1999/10/04 10:36:15 davem Exp $ * linux/arch/sparc/kernel/sys_sparc.c * * This file contains various random system calls that @@ -192,24 +192,23 @@ } retval = -ENOMEM; len = PAGE_ALIGN(len); - if(!(flags & MAP_FIXED) && !addr) { - addr = get_unmapped_area(addr, len); + if(!(flags & MAP_FIXED) && + (!addr || (ARCH_SUN4C_SUN4 && + (addr >= 0x20000000 && addr < 0xe0000000)))) { + addr = get_unmapped_area(0, len); if(!addr) goto out_putf; + if (ARCH_SUN4C_SUN4 && + (addr >= 0x20000000 && addr < 0xe0000000)) { + retval = -EINVAL; + goto out_putf; + } } /* See asm-sparc/uaccess.h */ retval = -EINVAL; if((len > (TASK_SIZE - PAGE_SIZE)) || (addr > (TASK_SIZE-len-PAGE_SIZE))) goto out_putf; - - if(ARCH_SUN4C_SUN4) { - if(((addr >= 0x20000000) && (addr < 0xe0000000))) { - /* VM hole */ - retval = current->mm->brk; - goto out_putf; - } - } flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); retval = do_mmap(file, addr, len, prot, flags, off); diff -u --recursive --new-file v2.2.13/linux/arch/sparc/kernel/sys_sunos.c linux/arch/sparc/kernel/sys_sunos.c --- v2.2.13/linux/arch/sparc/kernel/sys_sunos.c Mon Aug 9 16:05:55 1999 +++ linux/arch/sparc/kernel/sys_sunos.c Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: sys_sunos.c,v 1.94.2.2 1999/08/07 10:42:49 davem Exp $ +/* $Id: sys_sunos.c,v 1.94.2.3 1999/10/04 10:36:20 davem Exp $ * sys_sunos.c: SunOS specific syscall compatibility support. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -85,10 +85,17 @@ } retval = -ENOMEM; - if(!(flags & MAP_FIXED) && !addr) { - addr = get_unmapped_area(addr, len); + if(!(flags & MAP_FIXED) && + (!addr || (ARCH_SUN4C_SUN4 && + (addr >= 0x20000000 && addr < 0xe0000000)))) { + addr = get_unmapped_area(0, len); if(!addr) goto out_putf; + if (ARCH_SUN4C_SUN4 && + (addr >= 0x20000000 && addr < 0xe0000000)) { + retval = -EINVAL; + goto out_putf; + } } /* If this is ld.so or a shared library doing an mmap * of /dev/zero, transform it into an anonymous mapping. @@ -110,13 +117,6 @@ retval = -EINVAL; if((len > (TASK_SIZE - PAGE_SIZE)) || (addr > (TASK_SIZE-len-PAGE_SIZE))) goto out_putf; - - if(ARCH_SUN4C_SUN4) { - if(((addr >= 0x20000000) && (addr < 0xe0000000))) { - retval = current->mm->brk; - goto out_putf; - } - } flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); retval = do_mmap(file, addr, len, prot, flags, off); diff -u --recursive --new-file v2.2.13/linux/arch/sparc/lib/Makefile linux/arch/sparc/lib/Makefile --- v2.2.13/linux/arch/sparc/lib/Makefile Sun Mar 21 07:23:38 1999 +++ linux/arch/sparc/lib/Makefile Tue Jan 4 10:12:13 2000 @@ -1,11 +1,12 @@ -# $Id: Makefile,v 1.28 1999/03/21 06:37:44 davem Exp $ +# $Id: Makefile,v 1.28.2.1 1999/09/28 16:47:36 davem Exp $ # Makefile for Sparc library files.. # OBJS = mul.o rem.o sdiv.o udiv.o umul.o urem.o ashrdi3.o memcpy.o memset.o \ strlen.o checksum.o blockops.o memscan.o memcmp.o strncmp.o \ strncpy_from_user.o divdi3.o udivdi3.o strlen_user.o \ - copy_user.o locks.o atomic.o bitops.o debuglocks.o lshrdi3.o + copy_user.o locks.o atomic.o bitops.o debuglocks.o lshrdi3.o \ + ashldi3.o ifdef CONFIG_SMP OBJS += irqlock.o @@ -88,6 +89,9 @@ ashrdi3.o: ashrdi3.S $(CC) -D__ASSEMBLY__ -c -o ashrdi3.o ashrdi3.S + +ashldi3.o: ashldi3.S + $(CC) -D__ASSEMBLY__ -c -o ashldi3.o ashldi3.S lshrdi3.o: lshrdi3.S $(CC) -D__ASSEMBLY__ -c -o lshrdi3.o lshrdi3.S diff -u --recursive --new-file v2.2.13/linux/arch/sparc/lib/ashldi3.S linux/arch/sparc/lib/ashldi3.S --- v2.2.13/linux/arch/sparc/lib/ashldi3.S Wed Dec 31 16:00:00 1969 +++ linux/arch/sparc/lib/ashldi3.S Tue Jan 4 10:12:13 2000 @@ -0,0 +1,36 @@ +/* $Id: ashldi3.S,v 1.1.2.1 1999/09/28 16:47:39 davem Exp $ + * ashldi3.S: GCC emits these for certain drivers playing + * with long longs. + * + * Copyright (C) 1999 David S. Miller (davem@redhat.com) + */ + +#include + + .text + .align 4 + .globl C_LABEL(__ashldi3) +C_LABEL(__ashldi3): + cmp %o2, 0 + be 9f + mov 0x20, %g2 + + sub %g2, %o2, %g2 + cmp %g2, 0 + bg 7f + sll %o0, %o2, %g3 + + neg %g2 + clr %o5 + b 8f + sll %o1, %g2, %o4 +7: + srl %o1, %g2, %g2 + sll %o1, %o2, %o5 + or %g3, %g2, %o4 +8: + mov %o4, %o0 + mov %o5, %o1 +9: + retl + nop diff -u --recursive --new-file v2.2.13/linux/arch/sparc/lib/ashrdi3.S linux/arch/sparc/lib/ashrdi3.S --- v2.2.13/linux/arch/sparc/lib/ashrdi3.S Sat Nov 9 00:12:00 1996 +++ linux/arch/sparc/lib/ashrdi3.S Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: ashrdi3.S,v 1.3 1996/09/07 23:18:10 davem Exp $ +/* $Id: ashrdi3.S,v 1.3.12.1 1999/09/28 16:47:42 davem Exp $ * ashrdi3.S: The filesystem code creates all kinds of references to * this little routine on the sparc with gcc. * @@ -7,7 +7,9 @@ #include - .globl C_LABEL(__ashrdi3) + .text + .align 4 + .globl C_LABEL(__ashrdi3) C_LABEL(__ashrdi3): tst %o2 be 3f diff -u --recursive --new-file v2.2.13/linux/arch/sparc/math-emu/Makefile linux/arch/sparc/math-emu/Makefile --- v2.2.13/linux/arch/sparc/math-emu/Makefile Tue Apr 14 17:44:19 1998 +++ linux/arch/sparc/math-emu/Makefile Tue Jan 4 10:12:13 2000 @@ -8,19 +8,7 @@ # Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := math-emu.o -O_OBJS := math.o ashldi3.o fabss.o faddd.o faddq.o fadds.o \ - fcmpd.o fcmped.o fcmpeq.o fcmpes.o fcmpq.o fcmps.o \ - fdivd.o fdivq.o fdivs.o fdmulq.o fdtoi.o fdtoq.o \ - fdtos.o fitoq.o fmovs.o fmuld.o fmulq.o fmuls.o \ - fnegs.o fqtod.o fqtoi.o fqtos.o fsmuld.o fsqrtd.o \ - fsqrtq.o fsqrts.o fstod.o fstoi.o fstoq.o fsubd.o \ - fsubq.o fsubs.o udivmodti4.o - -LINKS := double.h faddd.c faddq.c fadds.c fdivd.c fdivq.c fdivs.c \ - fdtoi.c fitoq.c fmuld.c fmulq.c fmuls.c fqtoi.c \ - fsqrtd.c fsqrtq.c fsqrts.c fstoi.c fsubd.c \ - fsubq.c fsubs.c op-1.h op-2.h op-4.h op-common.h quad.h \ - single.h soft-fp.h udivmodti4.c +O_OBJS := math.o ashldi3.o .S.s: $(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s @@ -28,10 +16,6 @@ .S.o: $(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o -include $(TOPDIR)/Rules.make - -symlinks: - ln -sf $(patsubst %,../../sparc64/math-emu/%,$(LINKS)) . +CFLAGS += -I. -I$(TOPDIR)/include/math-emu -w -cleansymlinks: - rm -f $(LINKS) +include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.2.13/linux/arch/sparc/math-emu/fabss.c linux/arch/sparc/math-emu/fabss.c --- v2.2.13/linux/arch/sparc/math-emu/fabss.c Wed Mar 10 16:53:36 1999 +++ linux/arch/sparc/math-emu/fabss.c Wed Dec 31 16:00:00 1969 @@ -1,6 +0,0 @@ -int FABSS(unsigned long *rd, unsigned long *rs2) -{ - /* Clear the sign bit (high bit of word 0) */ - rd[0] = rs2[0] & 0x7fffffffUL; - return 0; -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc/math-emu/fcmpd.c linux/arch/sparc/math-emu/fcmpd.c --- v2.2.13/linux/arch/sparc/math-emu/fcmpd.c Wed Mar 10 16:53:36 1999 +++ linux/arch/sparc/math-emu/fcmpd.c Wed Dec 31 16:00:00 1969 @@ -1,18 +0,0 @@ -#include "soft-fp.h" -#include "double.h" - -int FCMPD(void *rd, void *rs2, void *rs1) -{ - FP_DECL_D(A); FP_DECL_D(B); - long ret; - unsigned long *fsr = rd; - - __FP_UNPACK_D(A, rs1); - __FP_UNPACK_D(B, rs2); - FP_CMP_D(ret, B, A, 2); - if (ret == -1) - ret = 2; - - *fsr = (*fsr & ~0xc00) | (ret << 10); - return 0; -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc/math-emu/fcmped.c linux/arch/sparc/math-emu/fcmped.c --- v2.2.13/linux/arch/sparc/math-emu/fcmped.c Wed Mar 10 16:53:36 1999 +++ linux/arch/sparc/math-emu/fcmped.c Wed Dec 31 16:00:00 1969 @@ -1,18 +0,0 @@ -#include "soft-fp.h" -#include "double.h" - -int FCMPED(void *rd, void *rs2, void *rs1) -{ - FP_DECL_D(A); FP_DECL_D(B); - long ret; - unsigned long *fsr = rd; - - __FP_UNPACK_D(A, rs1); - __FP_UNPACK_D(B, rs2); - FP_CMP_D(ret, B, A, 2); - if (ret == -1) - ret = 2; - - *fsr = (*fsr & ~0xc00) | (ret << 10); - return 0; -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc/math-emu/fcmpeq.c linux/arch/sparc/math-emu/fcmpeq.c --- v2.2.13/linux/arch/sparc/math-emu/fcmpeq.c Wed Mar 10 16:53:36 1999 +++ linux/arch/sparc/math-emu/fcmpeq.c Wed Dec 31 16:00:00 1969 @@ -1,18 +0,0 @@ -#include "soft-fp.h" -#include "quad.h" - -int FCMPEQ(void *rd, void *rs2, void *rs1) -{ - FP_DECL_Q(A); FP_DECL_Q(B); - long ret; - unsigned long fsr; - - __FP_UNPACK_Q(A, rs1); - __FP_UNPACK_Q(B, rs2); - FP_CMP_Q(ret, B, A, 3); - if (ret == -1) ret = 2; - fsr = *(unsigned long *)rd; - fsr &= ~0xc00; fsr |= (ret << 10); - *(unsigned long *)rd = fsr; - return 0; -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc/math-emu/fcmpes.c linux/arch/sparc/math-emu/fcmpes.c --- v2.2.13/linux/arch/sparc/math-emu/fcmpes.c Wed Mar 10 16:53:36 1999 +++ linux/arch/sparc/math-emu/fcmpes.c Wed Dec 31 16:00:00 1969 @@ -1,18 +0,0 @@ -#include "soft-fp.h" -#include "single.h" - -int FCMPES(void *rd, void *rs2, void *rs1) -{ - FP_DECL_S(A); FP_DECL_S(B); - long ret; - unsigned long *fsr = rd; - - __FP_UNPACK_S(A, rs1); - __FP_UNPACK_S(B, rs2); - FP_CMP_S(ret, B, A, 1); - if (ret == -1) - ret = 2; - - *fsr = (*fsr & ~0xc00) | (ret << 10); - return 0; -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc/math-emu/fcmpq.c linux/arch/sparc/math-emu/fcmpq.c --- v2.2.13/linux/arch/sparc/math-emu/fcmpq.c Wed Mar 10 16:53:36 1999 +++ linux/arch/sparc/math-emu/fcmpq.c Wed Dec 31 16:00:00 1969 @@ -1,18 +0,0 @@ -#include "soft-fp.h" -#include "quad.h" - -int FCMPQ(void *rd, void *rs2, void *rs1) -{ - FP_DECL_Q(A); FP_DECL_Q(B); - long ret; - unsigned long fsr; - - __FP_UNPACK_Q(A, rs1); - __FP_UNPACK_Q(B, rs2); - FP_CMP_Q(ret, B, A, 3); - if (ret == -1) ret = 2; - fsr = *(unsigned long *)rd; - fsr &= ~0xc00; fsr |= (ret << 10); - *(unsigned long *)rd = fsr; - return 0; -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc/math-emu/fcmps.c linux/arch/sparc/math-emu/fcmps.c --- v2.2.13/linux/arch/sparc/math-emu/fcmps.c Wed Mar 10 16:53:36 1999 +++ linux/arch/sparc/math-emu/fcmps.c Wed Dec 31 16:00:00 1969 @@ -1,18 +0,0 @@ -#include "soft-fp.h" -#include "single.h" - -int FCMPS(void *rd, void *rs2, void *rs1) -{ - FP_DECL_S(A); FP_DECL_S(B); - long ret; - unsigned long *fsr = rd; - - __FP_UNPACK_S(A, rs1); - __FP_UNPACK_S(B, rs2); - FP_CMP_S(ret, B, A, 1); - if (ret == -1) - ret = 2; - - *fsr = (*fsr & ~0xc00) | (ret << 10); - return 0; -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc/math-emu/fdmulq.c linux/arch/sparc/math-emu/fdmulq.c --- v2.2.13/linux/arch/sparc/math-emu/fdmulq.c Wed Mar 10 16:53:36 1999 +++ linux/arch/sparc/math-emu/fdmulq.c Wed Dec 31 16:00:00 1969 @@ -1,15 +0,0 @@ -#include "soft-fp.h" -#include "quad.h" -#include "double.h" - -int FDMULQ(void *rd, void *rs2, void *rs1) -{ - FP_DECL_D(IN); FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R); - - __FP_UNPACK_D(IN, rs1); - FP_CONV(Q,D,4,2,A,IN); - __FP_UNPACK_D(IN, rs2); - FP_CONV(Q,D,4,2,B,IN); - FP_MUL_Q(R, A, B); - return __FP_PACK_Q(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc/math-emu/fdtoq.c linux/arch/sparc/math-emu/fdtoq.c --- v2.2.13/linux/arch/sparc/math-emu/fdtoq.c Wed Mar 10 16:53:36 1999 +++ linux/arch/sparc/math-emu/fdtoq.c Wed Dec 31 16:00:00 1969 @@ -1,12 +0,0 @@ -#include "soft-fp.h" -#include "quad.h" -#include "double.h" - -int FDTOQ(void *rd, void *rs2) -{ - FP_DECL_D(A); FP_DECL_Q(R); - - __FP_UNPACK_D(A, rs2); - FP_CONV(Q,D,4,2,R,A); - return __FP_PACK_Q(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc/math-emu/fdtos.c linux/arch/sparc/math-emu/fdtos.c --- v2.2.13/linux/arch/sparc/math-emu/fdtos.c Wed Mar 10 16:53:36 1999 +++ linux/arch/sparc/math-emu/fdtos.c Wed Dec 31 16:00:00 1969 @@ -1,12 +0,0 @@ -#include "soft-fp.h" -#include "double.h" -#include "single.h" - -int FDTOS(void *rd, void *rs2) -{ - FP_DECL_D(A); FP_DECL_S(R); - - __FP_UNPACK_D(A, rs2); - FP_CONV(S,D,1,2,R,A); - return __FP_PACK_S(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc/math-emu/fmovs.c linux/arch/sparc/math-emu/fmovs.c --- v2.2.13/linux/arch/sparc/math-emu/fmovs.c Tue Apr 14 17:44:19 1998 +++ linux/arch/sparc/math-emu/fmovs.c Wed Dec 31 16:00:00 1969 @@ -1,5 +0,0 @@ -int FMOVS(unsigned long *rd, unsigned long *rs2) -{ - rd[0] = rs2[0]; - return 0; -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc/math-emu/fnegs.c linux/arch/sparc/math-emu/fnegs.c --- v2.2.13/linux/arch/sparc/math-emu/fnegs.c Wed Mar 10 16:53:36 1999 +++ linux/arch/sparc/math-emu/fnegs.c Wed Dec 31 16:00:00 1969 @@ -1,6 +0,0 @@ -int FNEGS(unsigned long *rd, unsigned long *rs2) -{ - /* just change the sign bit */ - rd[0] = rs2[0] ^ 0x80000000UL; - return 0; -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc/math-emu/fqtod.c linux/arch/sparc/math-emu/fqtod.c --- v2.2.13/linux/arch/sparc/math-emu/fqtod.c Wed Mar 10 16:53:36 1999 +++ linux/arch/sparc/math-emu/fqtod.c Wed Dec 31 16:00:00 1969 @@ -1,12 +0,0 @@ -#include "soft-fp.h" -#include "quad.h" -#include "double.h" - -int FQTOD(void *rd, void *rs2) -{ - FP_DECL_Q(A); FP_DECL_D(R); - - __FP_UNPACK_Q(A, rs2); - FP_CONV(D,Q,2,4,R,A); - return __FP_PACK_D(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc/math-emu/fqtos.c linux/arch/sparc/math-emu/fqtos.c --- v2.2.13/linux/arch/sparc/math-emu/fqtos.c Wed Mar 10 16:53:36 1999 +++ linux/arch/sparc/math-emu/fqtos.c Wed Dec 31 16:00:00 1969 @@ -1,12 +0,0 @@ -#include "soft-fp.h" -#include "quad.h" -#include "single.h" - -int FQTOS(void *rd, void *rs2) -{ - FP_DECL_Q(A); FP_DECL_S(R); - - __FP_UNPACK_Q(A, rs2); - FP_CONV(S,Q,1,4,R,A); - return __FP_PACK_S(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc/math-emu/fsmuld.c linux/arch/sparc/math-emu/fsmuld.c --- v2.2.13/linux/arch/sparc/math-emu/fsmuld.c Wed Mar 10 16:53:36 1999 +++ linux/arch/sparc/math-emu/fsmuld.c Wed Dec 31 16:00:00 1969 @@ -1,15 +0,0 @@ -#include "soft-fp.h" -#include "double.h" -#include "single.h" - -int FSMULD(void *rd, void *rs2, void *rs1) -{ - FP_DECL_S(IN); FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R); - - __FP_UNPACK_S(IN, rs1); - FP_CONV(D,S,2,1,A,IN); - __FP_UNPACK_S(IN, rs2); - FP_CONV(D,S,2,1,B,IN); - FP_MUL_D(R, A, B); - return __FP_PACK_D(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc/math-emu/fstod.c linux/arch/sparc/math-emu/fstod.c --- v2.2.13/linux/arch/sparc/math-emu/fstod.c Wed Mar 10 16:53:36 1999 +++ linux/arch/sparc/math-emu/fstod.c Wed Dec 31 16:00:00 1969 @@ -1,12 +0,0 @@ -#include "soft-fp.h" -#include "double.h" -#include "single.h" - -int FSTOD(void *rd, void *rs2) -{ - FP_DECL_S(A); FP_DECL_D(R); - - __FP_UNPACK_S(A, rs2); - FP_CONV(D,S,2,1,R,A); - return __FP_PACK_D(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc/math-emu/fstoq.c linux/arch/sparc/math-emu/fstoq.c --- v2.2.13/linux/arch/sparc/math-emu/fstoq.c Wed Mar 10 16:53:36 1999 +++ linux/arch/sparc/math-emu/fstoq.c Wed Dec 31 16:00:00 1969 @@ -1,12 +0,0 @@ -#include "soft-fp.h" -#include "quad.h" -#include "single.h" - -int FSTOQ(void *rd, void *rs2) -{ - FP_DECL_S(A); FP_DECL_Q(R); - - __FP_UNPACK_S(A, rs2); - FP_CONV(Q,S,4,1,R,A); - return __FP_PACK_Q(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc/math-emu/math.c linux/arch/sparc/math-emu/math.c --- v2.2.13/linux/arch/sparc/math-emu/math.c Wed Mar 10 16:53:36 1999 +++ linux/arch/sparc/math-emu/math.c Tue Jan 4 10:12:13 2000 @@ -1,26 +1,19 @@ -/* +/* * arch/sparc/math-emu/math.c * * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk) - * Based on the sparc64 code by Jakub Jelinek. + * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz) + * Copyright (C) 1999 David S. Miller (davem@redhat.com) * * This is a good place to start if you're trying to understand the - * emulation code, because it's pretty simple. What we do is + * emulation code, because it's pretty simple. What we do is * essentially analyse the instruction to work out what the operation * is and which registers are involved. We then execute the appropriate * FXXXX function. [The floating point queue introduces a minor wrinkle; * see below...] * The fxxxxx.c files each emulate a single insn. They look relatively * simple because the complexity is hidden away in an unholy tangle - * of preprocessor macros. - * - * WARNING : don't look at the macro definitions unless you - * absolutely have to! They're extremely ugly, rather complicated - * and a single line in an fxxxx.c file can expand to the equivalent - * of 30 lines or more of C. Of course, any error in those 30 lines - * is reported by the compiler as an error in the single line with the - * macro usage... - * Question: should we replace them with inline functions? + * of preprocessor macros. * * The first layer of macros is single.h, double.h, quad.h. Generally * these files define macros for working with floating point numbers @@ -29,11 +22,11 @@ * generic macros (in this case _FP_ADD(D,2,R,X,Y) where the number * of machine words required to store the given IEEE format is passed * as a parameter. [double.h and co check the number of bits in a word - * and define FP_ADD_D & co appropriately]. + * and define FP_ADD_D & co appropriately]. * The generic macros are defined in op-common.h. This is where all * the grotty stuff like handling NaNs is coded. To handle the possible * word sizes macros in op-common.h use macros like _FP_FRAC_SLL_##wc() - * where wc is the 'number of machine words' parameter (here 2). + * where wc is the 'number of machine words' parameter (here 2). * These are defined in the third layer of macros: op-1.h, op-2.h * and op-4.h. These handle operations on floating point numbers composed * of 1,2 and 4 machine words respectively. [For example, on sparc64 @@ -41,7 +34,7 @@ * constructs in op-1.h, but on sparc32 they use op-2.h definitions.] * soft-fp.h is on the same level as op-common.h, and defines some * macros which are independent of both word size and FP format. - * Finally, sfp-machine.h is the machine dependent part of the + * Finally, sfp-machine.h is the machine dependent part of the * code: it defines the word size and what type a word is. It also * defines how _FP_MUL_MEAT_t() maps to _FP_MUL_MEAT_n_* : op-n.h * provide several possible flavours of multiply algorithm, most @@ -64,59 +57,11 @@ * so we follow that practice... */ -/* WISHLIST: - * - * + Replace all the macros with inline functions. These should - * have the same effect but be much easier to work with. - * - * + Emulate the IEEE exception flags. We don't currently do this - * because a) it would require significant alterations to - * the emulation macros [see the comments about _FP_NEG() - * in op-common.c and note that we'd need to invent a convention - * for passing in the flags to FXXXX fns and returning them] and - * b) SPARClinux doesn't let users access the flags anyway - * [contrast Solaris, which allows you to examine, clear or set - * the flags, and request that exceptions cause SIGFPE - * [which you then set up a signal handler for, obviously...]]. - * Erm, (b) may quite possibly be garbage. %fsr is user-writable - * so you don't need a syscall. There may or may not be library - * support. - * - * + Emulation of FMULQ, FDIVQ, FSQRTQ, FDMULQ needs to be - * written! - * - * + reindent code to conform to Linux kernel standard :-> - * - * + work out whether all the compile-time warnings are bogus - * - * + check that conversion to/from integers works - * - * + check with the SPARC architecture manual to see if we resolve - * the implementation-dependent bits of the IEEE spec in the - * same manner as the hardware. - * - * + more test cases for the test script always welcome! - * - * + illegal opcodes currently cause SIGFPEs. We should arrange - * to tell the traps.c code to SIGILL instead. Currently, - * everywhere that we return 0 should cause SIGILL, I think. - * SIGFPE should only be caused if we set an IEEE exception bit - * and the relevant trap bit is also set. (this means that - * traps.c should do this; also it should handle the case of - * IEEE exception generated directly by the hardware.) - * Should illegal_fp_register (which is a flavour of fp exception) - * cause SIGFPE or SIGILL? - * - * + the test script needs to be extended to handle the quadword - * and comparison insns. - * - * + _FP_DIV_MEAT_2_udiv_64() appears to work but it should be - * checked by somebody who understands the algorithm :-> - * - * + fpsave() saves the FP queue but fpload() doesn't reload it. +/* TODO: + * fpsave() saves the FP queue but fpload() doesn't reload it. * Therefore when we context switch or change FPU ownership * we have to check to see if the queue had anything in it and - * emulate it if it did. This is going to be a pain. + * emulate it if it did. This is going to be a pain. */ #include @@ -124,71 +69,57 @@ #include #include +#include "sfp-util.h" #include "soft-fp.h" +#include "single.h" +#include "double.h" +#include "quad.h" #define FLOATFUNC(x) extern int x(void *,void *,void *) -/* Current status: we don't properly emulate the difficult quadword - * insns (MUL, DIV, SQRT). - * There are also some ops involving the FP registers which we don't - * emulate: the branch on FP condition flags and the load/store to - * FP regs or FSR. I'm assuming that these will never generate traps - * (not unreasonable if there's an FPU at all; comments in the NetBSD - * kernel source agree on this point). If we wanted to allow - * purely software-emulation of the FPU with FPU totally disabled - * or non-existent, we'd have to emulate these as well. We'd also - * need to alter the fp_disabled trap handler to call the math-emu - * code appropriately. The structure of do_one_mathemu() is also - * inappropriate for these ops (as it has no way to alter the pc, - * for a start) and it might be better to special-case them in do_mathemu(). - * Oh, and you'd need to alter the traps.c code so it didn't try to - * fpsave() and fpload(). If there's genuinely no FPU then there's - * probably bits of kernel stuff that just won't work anyway... - */ - /* The Vn labels indicate what version of the SPARC architecture gas thinks - * each insn is. This is from the binutils source :-> + * each insn is. This is from the binutils source :-> */ /* quadword instructions */ -FLOATFUNC(FSQRTQ); /* v8 NYI */ -FLOATFUNC(FADDQ); /* v8 */ -FLOATFUNC(FSUBQ); /* v8 */ -FLOATFUNC(FMULQ); /* v8 NYI */ -FLOATFUNC(FDIVQ); /* v8 NYI */ -FLOATFUNC(FDMULQ); /* v8 NYI */ -FLOATFUNC(FQTOS); /* v8 */ -FLOATFUNC(FQTOD); /* v8 */ -FLOATFUNC(FITOQ); /* v8 */ -FLOATFUNC(FSTOQ); /* v8 */ -FLOATFUNC(FDTOQ); /* v8 */ -FLOATFUNC(FQTOI); /* v8 */ -FLOATFUNC(FCMPQ); /* v8 */ -FLOATFUNC(FCMPEQ); /* v8 */ +#define FSQRTQ 0x02b /* v8 */ +#define FADDQ 0x043 /* v8 */ +#define FSUBQ 0x047 /* v8 */ +#define FMULQ 0x04b /* v8 */ +#define FDIVQ 0x04f /* v8 */ +#define FDMULQ 0x06e /* v8 */ +#define FQTOS 0x0c7 /* v8 */ +#define FQTOD 0x0cb /* v8 */ +#define FITOQ 0x0cc /* v8 */ +#define FSTOQ 0x0cd /* v8 */ +#define FDTOQ 0x0ce /* v8 */ +#define FQTOI 0x0d3 /* v8 */ +#define FCMPQ 0x053 /* v8 */ +#define FCMPEQ 0x057 /* v8 */ /* single/double instructions (subnormal): should all work */ -FLOATFUNC(FSQRTS); /* v7 */ -FLOATFUNC(FSQRTD); /* v7 */ -FLOATFUNC(FADDS); /* v6 */ -FLOATFUNC(FADDD); /* v6 */ -FLOATFUNC(FSUBS); /* v6 */ -FLOATFUNC(FSUBD); /* v6 */ -FLOATFUNC(FMULS); /* v6 */ -FLOATFUNC(FMULD); /* v6 */ -FLOATFUNC(FDIVS); /* v6 */ -FLOATFUNC(FDIVD); /* v6 */ -FLOATFUNC(FSMULD); /* v8 */ -FLOATFUNC(FDTOS); /* v6 */ -FLOATFUNC(FSTOD); /* v6 */ -FLOATFUNC(FSTOI); /* v6 */ -FLOATFUNC(FDTOI); /* v6 */ -FLOATFUNC(FABSS); /* v6 */ -FLOATFUNC(FCMPS); /* v6 */ -FLOATFUNC(FCMPES); /* v6 */ -FLOATFUNC(FCMPD); /* v6 */ -FLOATFUNC(FCMPED); /* v6 */ -FLOATFUNC(FMOVS); /* v6 */ -FLOATFUNC(FNEGS); /* v6 */ -FLOATFUNC(FITOS); /* v6 */ -FLOATFUNC(FITOD); /* v6 */ +#define FSQRTS 0x029 /* v7 */ +#define FSQRTD 0x02a /* v7 */ +#define FADDS 0x041 /* v6 */ +#define FADDD 0x042 /* v6 */ +#define FSUBS 0x045 /* v6 */ +#define FSUBD 0x046 /* v6 */ +#define FMULS 0x049 /* v6 */ +#define FMULD 0x04a /* v6 */ +#define FDIVS 0x04d /* v6 */ +#define FDIVD 0x04e /* v6 */ +#define FSMULD 0x069 /* v6 */ +#define FDTOS 0x0c6 /* v6 */ +#define FSTOD 0x0c9 /* v6 */ +#define FSTOI 0x0d1 /* v6 */ +#define FDTOI 0x0d2 /* v6 */ +#define FABSS 0x009 /* v6 */ +#define FCMPS 0x051 /* v6 */ +#define FCMPES 0x055 /* v6 */ +#define FCMPD 0x052 /* v6 */ +#define FCMPED 0x056 /* v6 */ +#define FMOVS 0x001 /* v6 */ +#define FNEGS 0x005 /* v6 */ +#define FITOS 0x0c4 /* v6 */ +#define FITOD 0x0c8 /* v6 */ #define FSR_TEM_SHIFT 23UL #define FSR_TEM_MASK (0x1fUL << FSR_TEM_SHIFT) @@ -197,7 +128,7 @@ #define FSR_CEXC_SHIFT 0UL #define FSR_CEXC_MASK (0x1fUL << FSR_CEXC_SHIFT) -static int do_one_mathemu(u32 insn, unsigned long *fsr, unsigned long *fregs); +static int do_one_mathemu(u32 insn, unsigned long *fsr, unsigned long *fregs); /* Unlike the Sparc64 version (which has a struct fpustate), we * pass the taskstruct corresponding to the task which currently owns the @@ -210,65 +141,65 @@ */ int do_mathemu(struct pt_regs *regs, struct task_struct *fpt) { - /* regs->pc isn't necessarily the PC at which the offending insn is sitting. - * The FPU maintains a queue of FPops which cause traps. - * When it hits an instruction that requires that the trapped op succeeded - * (usually because it reads a reg. that the trapped op wrote) then it - * causes this exception. We need to emulate all the insns on the queue - * and then allow the op to proceed. - * This code should also handle the case where the trap was precise, - * in which case the queue length is zero and regs->pc points at the - * single FPop to be emulated. (this case is untested, though :->) - * You'll need this case if you want to be able to emulate all FPops - * because the FPU either doesn't exist or has been software-disabled. - * [The UltraSPARC makes FP a precise trap; this isn't as stupid as it - * might sound because the Ultra does funky things with a superscalar - * architecture.] - */ - - /* You wouldn't believe how often I typed 'ftp' when I meant 'fpt' :-> */ - - int i; - int retcode = 0; /* assume all succeed */ - unsigned long insn; - -#ifdef DEBUG_MATHEMU - printk("In do_mathemu()... pc is %08lx\n", regs->pc); - printk("fpqdepth is %ld\n",fpt->tss.fpqdepth); - for (i = 0; i < fpt->tss.fpqdepth; i++) - printk("%d: %08lx at %08lx\n",i,fpt->tss.fpqueue[i].insn, (unsigned long)fpt->tss.fpqueue[i].insn_addr); -#endif - - if (fpt->tss.fpqdepth == 0) { /* no queue, guilty insn is at regs->pc */ -#ifdef DEBUG_MATHEMU - printk("precise trap at %08lx\n", regs->pc); + /* regs->pc isn't necessarily the PC at which the offending insn is sitting. + * The FPU maintains a queue of FPops which cause traps. + * When it hits an instruction that requires that the trapped op succeeded + * (usually because it reads a reg. that the trapped op wrote) then it + * causes this exception. We need to emulate all the insns on the queue + * and then allow the op to proceed. + * This code should also handle the case where the trap was precise, + * in which case the queue length is zero and regs->pc points at the + * single FPop to be emulated. (this case is untested, though :->) + * You'll need this case if you want to be able to emulate all FPops + * because the FPU either doesn't exist or has been software-disabled. + * [The UltraSPARC makes FP a precise trap; this isn't as stupid as it + * might sound because the Ultra does funky things with a superscalar + * architecture.] + */ + + /* You wouldn't believe how often I typed 'ftp' when I meant 'fpt' :-> */ + + int i; + int retcode = 0; /* assume all succeed */ + unsigned long insn; + +#ifdef DEBUG_MATHEMU + printk("In do_mathemu()... pc is %08lx\n", regs->pc); + printk("fpqdepth is %ld\n", fpt->tss.fpqdepth); + for (i = 0; i < fpt->tss.fpqdepth; i++) + printk("%d: %08lx at %08lx\n", i, fpt->tss.fpqueue[i].insn, + (unsigned long)fpt->tss.fpqueue[i].insn_addr); +#endif + + if (fpt->tss.fpqdepth == 0) { /* no queue, guilty insn is at regs->pc */ +#ifdef DEBUG_MATHEMU + printk("precise trap at %08lx\n", regs->pc); #endif - if (!get_user(insn, (u32 *)regs->pc)) { - retcode = do_one_mathemu(insn, &fpt->tss.fsr, fpt->tss.float_regs); - if (retcode) { - /* in this case we need to fix up PC & nPC */ - regs->pc = regs->npc; - regs->npc += 4; - } - } - return retcode; - } - - /* Normal case: need to empty the queue... */ - for (i = 0; i < fpt->tss.fpqdepth; i++) - { - retcode = do_one_mathemu(fpt->tss.fpqueue[i].insn, &(fpt->tss.fsr), fpt->tss.float_regs); - if (!retcode) /* insn failed, no point doing any more */ - break; - } - /* Now empty the queue and clear the queue_not_empty flag */ - if(retcode) - fpt->tss.fsr &= ~(0x3000 | FSR_CEXC_MASK); - else - fpt->tss.fsr &= ~0x3000; - fpt->tss.fpqdepth = 0; - - return retcode; + if (!get_user(insn, (u32 *)regs->pc)) { + retcode = do_one_mathemu(insn, &fpt->tss.fsr, fpt->tss.float_regs); + if (retcode) { + /* in this case we need to fix up PC & nPC */ + regs->pc = regs->npc; + regs->npc += 4; + } + } + return retcode; + } + + /* Normal case: need to empty the queue... */ + for (i = 0; i < fpt->tss.fpqdepth; i++) { + retcode = do_one_mathemu(fpt->tss.fpqueue[i].insn, &(fpt->tss.fsr), fpt->tss.float_regs); + if (!retcode) /* insn failed, no point doing any more */ + break; + } + /* Now empty the queue and clear the queue_not_empty flag */ + if(retcode) + fpt->tss.fsr &= ~(0x3000 | FSR_CEXC_MASK); + else + fpt->tss.fsr &= ~0x3000; + fpt->tss.fpqdepth = 0; + + return retcode; } /* All routines returning an exception to raise should detect @@ -279,7 +210,7 @@ * * We return 0 if a SIGFPE should be sent, 1 otherwise. */ -static int record_exception(unsigned long *pfsr, int eflag) +static inline int record_exception(unsigned long *pfsr, int eflag) { unsigned long fsr = *pfsr; int would_trap; @@ -291,46 +222,36 @@ if(would_trap != 0) { eflag &= ((fsr & FSR_TEM_MASK) >> FSR_TEM_SHIFT); if((eflag & (eflag - 1)) != 0) { - if(eflag & EFLAG_INVALID) - eflag = EFLAG_INVALID; - else if(eflag & EFLAG_DIVZERO) - eflag = EFLAG_DIVZERO; - else if(eflag & EFLAG_INEXACT) - eflag = EFLAG_INEXACT; + if(eflag & FP_EX_INVALID) + eflag = FP_EX_INVALID; + else if(eflag & FP_EX_OVERFLOW) + eflag = FP_EX_OVERFLOW; + else if(eflag & FP_EX_UNDERFLOW) + eflag = FP_EX_UNDERFLOW; + else if(eflag & FP_EX_DIVZERO) + eflag = FP_EX_DIVZERO; + else if(eflag & FP_EX_INEXACT) + eflag = FP_EX_INEXACT; } } - /* Set CEXC, here are the rules: + /* Set CEXC, here is the rule: * - * 1) In general all FPU ops will set one and only one + * In general all FPU ops will set one and only one * bit in the CEXC field, this is always the case * when the IEEE exception trap is enabled in TEM. - * - * 2) As a special case, if an overflow or underflow - * is being signalled, AND the trap is not enabled - * in TEM, then the inexact field shall also be set. */ fsr &= ~(FSR_CEXC_MASK); - if(would_trap || - (eflag & (EFLAG_OVERFLOW | EFLAG_UNDERFLOW)) == 0) { - fsr |= ((long)eflag << FSR_CEXC_SHIFT); - } else { - fsr |= (((long)eflag << FSR_CEXC_SHIFT) | - (EFLAG_INEXACT << FSR_CEXC_SHIFT)); - } + fsr |= ((long)eflag << FSR_CEXC_SHIFT); - /* Set the AEXC field, rules are: + /* Set the AEXC field, rule is: * - * 1) If a trap would not be generated, the + * If a trap would not be generated, the * CEXC just generated is OR'd into the * existing value of AEXC. - * - * 2) When a trap is generated, AEXC is cleared. */ if(would_trap == 0) fsr |= ((long)eflag << FSR_AEXC_SHIFT); - else - fsr &= ~(FSR_AEXC_MASK); /* If trapping, indicate fault trap type IEEE. */ if(would_trap != 0) @@ -341,159 +262,264 @@ return (would_trap ? 0 : 1); } -static int do_one_mathemu(u32 insn, unsigned long *fsr, unsigned long *fregs) +typedef union { + u32 s; + u64 d; + u64 q[2]; +} *argp; + +static int do_one_mathemu(u32 insn, unsigned long *pfsr, unsigned long *fregs) { - /* Emulate the given insn, updating fsr and fregs appropriately. */ - int type = 0; - /* 01 is single, 10 is double, 11 is quad, - * 000011 is rs1, 001100 is rs2, 110000 is rd (00 in rd is fcc) - * 111100000000 tells which ftt that may happen in - * (this field not used on sparc32 code, as we can't - * extract trap type info for ops on the FP queue) - */ - int freg, eflag; - int (*func)(void *,void *,void *) = NULL; - void *rs1 = NULL, *rs2 = NULL, *rd = NULL; + /* Emulate the given insn, updating fsr and fregs appropriately. */ + int type = 0; + /* r is rd, b is rs2 and a is rs1. The *u arg tells + whether the argument should be packed/unpacked (0 - do not unpack/pack, 1 - unpack/pack) + non-u args tells the size of the argument (0 - no argument, 1 - single, 2 - double, 3 - quad */ +#define TYPE(dummy, r, ru, b, bu, a, au) type = (au << 2) | (a << 0) | (bu << 5) | (b << 3) | (ru << 8) | (r << 6) + int freg; + argp rs1 = NULL, rs2 = NULL, rd = NULL; + FP_DECL_EX; + FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR); + FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR); + FP_DECL_Q(QA); FP_DECL_Q(QB); FP_DECL_Q(QR); + int IR; + long fsr; #ifdef DEBUG_MATHEMU - printk("In do_mathemu(), emulating %08lx\n", insn); -#endif - - if ((insn & 0xc1f80000) == 0x81a00000) /* FPOP1 */ { - switch ((insn >> 5) & 0x1ff) { - /* QUAD - ftt == 3 */ - case 0x001: type = 0x314; func = FMOVS; break; - case 0x005: type = 0x314; func = FNEGS; break; - case 0x009: type = 0x314; func = FABSS; break; - case 0x02b: type = 0x33c; func = FSQRTQ; break; - case 0x043: type = 0x33f; func = FADDQ; break; - case 0x047: type = 0x33f; func = FSUBQ; break; - case 0x04b: type = 0x33f; func = FMULQ; break; - case 0x04f: type = 0x33f; func = FDIVQ; break; - case 0x06e: type = 0x33a; func = FDMULQ; break; - case 0x0c7: type = 0x31c; func = FQTOS; break; - case 0x0cb: type = 0x32c; func = FQTOD; break; - case 0x0cc: type = 0x334; func = FITOQ; break; - case 0x0cd: type = 0x334; func = FSTOQ; break; - case 0x0ce: type = 0x338; func = FDTOQ; break; - case 0x0d3: type = 0x31c; func = FQTOI; break; - /* SUBNORMAL - ftt == 2 */ - case 0x029: type = 0x214; func = FSQRTS; break; - case 0x02a: type = 0x228; func = FSQRTD; break; - case 0x041: type = 0x215; func = FADDS; break; - case 0x042: type = 0x22a; func = FADDD; break; - case 0x045: type = 0x215; func = FSUBS; break; - case 0x046: type = 0x22a; func = FSUBD; break; - case 0x049: type = 0x215; func = FMULS; break; - case 0x04a: type = 0x22a; func = FMULD; break; - case 0x04d: type = 0x215; func = FDIVS; break; - case 0x04e: type = 0x22a; func = FDIVD; break; - case 0x069: type = 0x225; func = FSMULD; break; - case 0x0c6: type = 0x218; func = FDTOS; break; - case 0x0c9: type = 0x224; func = FSTOD; break; - case 0x0d1: type = 0x214; func = FSTOI; break; - case 0x0d2: type = 0x218; func = FDTOI; break; - default: -#ifdef DEBUG_MATHEMU - printk("unknown FPop1: %03lx\n",(insn>>5)&0x1ff); -#endif - } - } - else if ((insn & 0xc1f80000) == 0x81a80000) /* FPOP2 */ { - switch ((insn >> 5) & 0x1ff) { - case 0x051: type = 0x305; func = FCMPS; break; - case 0x052: type = 0x30a; func = FCMPD; break; - case 0x053: type = 0x30f; func = FCMPQ; break; - case 0x055: type = 0x305; func = FCMPES; break; - case 0x056: type = 0x30a; func = FCMPED; break; - case 0x057: type = 0x30f; func = FCMPEQ; break; - default: -#ifdef DEBUG_MATHEMU - printk("unknown FPop2: %03lx\n",(insn>>5)&0x1ff); -#endif - } - } - - if (!type) { /* oops, didn't recognise that FPop */ - printk("attempt to emulate unrecognised FPop!\n"); - return 0; - } - - /* Decode the registers to be used */ - freg = (*fsr >> 14) & 0xf; - - *fsr &= ~0x1c000; /* clear the traptype bits */ - - freg = ((insn >> 14) & 0x1f); - switch (type & 0x3) /* is rs1 single, double or quad? */ - { - case 3: - if (freg & 3) /* quadwords must have bits 4&5 of the */ - { /* encoded reg. number set to zero. */ - *fsr |= (6 << 14); - return 0; /* simulate invalid_fp_register exception */ - } - /* fall through */ - case 2: - if (freg & 1) /* doublewords must have bit 5 zeroed */ - { - *fsr |= (6 << 14); - return 0; - } - } - rs1 = (void *)&fregs[freg]; - freg = (insn & 0x1f); - switch ((type >> 2) & 0x3) - { /* same again for rs2 */ - case 3: - if (freg & 3) /* quadwords must have bits 4&5 of the */ - { /* encoded reg. number set to zero. */ - *fsr |= (6 << 14); - return 0; /* simulate invalid_fp_register exception */ - } - /* fall through */ - case 2: - if (freg & 1) /* doublewords must have bit 5 zeroed */ - { - *fsr |= (6 << 14); - return 0; - } - } - rs2 = (void *)&fregs[freg]; - freg = ((insn >> 25) & 0x1f); - switch ((type >> 4) & 0x3) /* and finally rd. This one's a bit different */ - { - case 0: /* dest is fcc. (this must be FCMPQ or FCMPEQ) */ - if (freg) /* V8 has only one set of condition codes, so */ - { /* anything but 0 in the rd field is an error */ - *fsr |= (6 << 14); /* (should probably flag as invalid opcode */ - return 0; /* but SIGFPE will do :-> ) */ - } - rd = (void *)(fsr); /* FCMPQ and FCMPEQ are special and only */ - break; /* set bits they're supposed to :-> */ - case 3: - if (freg & 3) /* quadwords must have bits 4&5 of the */ - { /* encoded reg. number set to zero. */ - *fsr |= (6 << 14); - return 0; /* simulate invalid_fp_register exception */ - } - /* fall through */ - case 2: - if (freg & 1) /* doublewords must have bit 5 zeroed */ - { - *fsr |= (6 << 14); - return 0; - } - /* fall through */ - case 1: - rd = (void *)&fregs[freg]; - break; - } -#ifdef DEBUG_MATHEMU - printk("executing insn...\n"); -#endif - eflag = func(rd, rs2, rs1); /* do the Right Thing */ - if(eflag == 0) - return 1; /* success! */ - return record_exception(fsr, eflag); + printk("In do_mathemu(), emulating %08lx\n", insn); +#endif + + if ((insn & 0xc1f80000) == 0x81a00000) /* FPOP1 */ { + switch ((insn >> 5) & 0x1ff) { + case FSQRTQ: TYPE(3,3,1,3,1,0,0); break; + case FADDQ: + case FSUBQ: + case FMULQ: + case FDIVQ: TYPE(3,3,1,3,1,3,1); break; + case FDMULQ: TYPE(3,3,1,2,1,2,1); break; + case FQTOS: TYPE(3,1,1,3,1,0,0); break; + case FQTOD: TYPE(3,2,1,3,1,0,0); break; + case FITOQ: TYPE(3,3,1,1,0,0,0); break; + case FSTOQ: TYPE(3,3,1,1,1,0,0); break; + case FDTOQ: TYPE(3,3,1,2,1,0,0); break; + case FQTOI: TYPE(3,1,0,3,1,0,0); break; + case FSQRTS: TYPE(2,1,1,1,1,0,0); break; + case FSQRTD: TYPE(2,2,1,2,1,0,0); break; + case FADDD: + case FSUBD: + case FMULD: + case FDIVD: TYPE(2,2,1,2,1,2,1); break; + case FADDS: + case FSUBS: + case FMULS: + case FDIVS: TYPE(2,1,1,1,1,1,1); break; + case FSMULD: TYPE(2,2,1,1,1,1,1); break; + case FDTOS: TYPE(2,1,1,2,1,0,0); break; + case FSTOD: TYPE(2,2,1,1,1,0,0); break; + case FSTOI: TYPE(2,1,0,1,1,0,0); break; + case FDTOI: TYPE(2,1,0,2,1,0,0); break; + case FITOS: TYPE(2,1,1,1,0,0,0); break; + case FITOD: TYPE(2,2,1,1,0,0,0); break; + case FMOVS: + case FABSS: + case FNEGS: TYPE(2,1,0,1,0,0,0); break; + default: +#ifdef DEBUG_MATHEMU + printk("unknown FPop1: %03lx\n",(insn>>5)&0x1ff); +#endif + } + } else if ((insn & 0xc1f80000) == 0x81a80000) /* FPOP2 */ { + switch ((insn >> 5) & 0x1ff) { + case FCMPS: TYPE(3,0,0,1,0,1,0); break; + case FCMPES: TYPE(3,0,0,1,0,1,0); break; + case FCMPD: TYPE(3,0,0,2,0,2,0); break; + case FCMPED: TYPE(3,0,0,2,0,2,0); break; + case FCMPQ: TYPE(3,0,0,3,0,3,0); break; + case FCMPEQ: TYPE(3,0,0,3,0,3,0); break; + default: +#ifdef DEBUG_MATHEMU + printk("unknown FPop2: %03lx\n",(insn>>5)&0x1ff); +#endif + } + } + + if (!type) { /* oops, didn't recognise that FPop */ +#ifdef DEBUG_MATHEMU + printk("attempt to emulate unrecognised FPop!\n"); +#endif + return 0; + } + + /* Decode the registers to be used */ + freg = (*pfsr >> 14) & 0xf; + + *pfsr &= ~0x1c000; /* clear the traptype bits */ + + freg = ((insn >> 14) & 0x1f); + switch (type & 0x3) { /* is rs1 single, double or quad? */ + case 3: + if (freg & 3) { /* quadwords must have bits 4&5 of the */ + /* encoded reg. number set to zero. */ + *pfsr |= (6 << 14); + return 0; /* simulate invalid_fp_register exception */ + } + /* fall through */ + case 2: + if (freg & 1) { /* doublewords must have bit 5 zeroed */ + *pfsr |= (6 << 14); + return 0; + } + } + rs1 = (argp)&fregs[freg]; + switch (type & 0x7) { + case 7: FP_UNPACK_QP (QA, rs1); break; + case 6: FP_UNPACK_DP (DA, rs1); break; + case 5: FP_UNPACK_SP (SA, rs1); break; + } + freg = (insn & 0x1f); + switch ((type >> 3) & 0x3) { /* same again for rs2 */ + case 3: + if (freg & 3) { /* quadwords must have bits 4&5 of the */ + /* encoded reg. number set to zero. */ + *pfsr |= (6 << 14); + return 0; /* simulate invalid_fp_register exception */ + } + /* fall through */ + case 2: + if (freg & 1) { /* doublewords must have bit 5 zeroed */ + *pfsr |= (6 << 14); + return 0; + } + } + rs2 = (argp)&fregs[freg]; + switch ((type >> 3) & 0x7) { + case 7: FP_UNPACK_QP (QB, rs2); break; + case 6: FP_UNPACK_DP (DB, rs2); break; + case 5: FP_UNPACK_SP (SB, rs2); break; + } + freg = ((insn >> 25) & 0x1f); + switch ((type >> 6) & 0x3) { /* and finally rd. This one's a bit different */ + case 0: /* dest is fcc. (this must be FCMPQ or FCMPEQ) */ + if (freg) { /* V8 has only one set of condition codes, so */ + /* anything but 0 in the rd field is an error */ + *pfsr |= (6 << 14); /* (should probably flag as invalid opcode */ + return 0; /* but SIGFPE will do :-> ) */ + } + break; + case 3: + if (freg & 3) { /* quadwords must have bits 4&5 of the */ + /* encoded reg. number set to zero. */ + *pfsr |= (6 << 14); + return 0; /* simulate invalid_fp_register exception */ + } + /* fall through */ + case 2: + if (freg & 1) { /* doublewords must have bit 5 zeroed */ + *pfsr |= (6 << 14); + return 0; + } + /* fall through */ + case 1: + rd = (void *)&fregs[freg]; + break; + } +#ifdef DEBUG_MATHEMU + printk("executing insn...\n"); +#endif + /* do the Right Thing */ + switch ((insn >> 5) & 0x1ff) { + /* + */ + case FADDS: FP_ADD_S (SR, SA, SB); break; + case FADDD: FP_ADD_D (DR, DA, DB); break; + case FADDQ: FP_ADD_Q (QR, QA, QB); break; + /* - */ + case FSUBS: FP_SUB_S (SR, SA, SB); break; + case FSUBD: FP_SUB_D (DR, DA, DB); break; + case FSUBQ: FP_SUB_Q (QR, QA, QB); break; + /* * */ + case FMULS: FP_MUL_S (SR, SA, SB); break; + case FSMULD: FP_CONV (D, S, 2, 1, DA, SA); + FP_CONV (D, S, 2, 1, DB, SB); + case FMULD: FP_MUL_D (DR, DA, DB); break; + case FDMULQ: FP_CONV (Q, D, 4, 2, QA, DA); + FP_CONV (Q, D, 4, 2, QB, DB); + case FMULQ: FP_MUL_Q (QR, QA, QB); break; + /* / */ + case FDIVS: FP_DIV_S (SR, SA, SB); break; + case FDIVD: FP_DIV_D (DR, DA, DB); break; + case FDIVQ: FP_DIV_Q (QR, QA, QB); break; + /* sqrt */ + case FSQRTS: FP_SQRT_S (SR, SB); break; + case FSQRTD: FP_SQRT_D (DR, DB); break; + case FSQRTQ: FP_SQRT_Q (QR, QB); break; + /* mov */ + case FMOVS: rd->s = rs2->s; break; + case FABSS: rd->s = rs2->s & 0x7fffffff; break; + case FNEGS: rd->s = rs2->s ^ 0x80000000; break; + /* float to int */ + case FSTOI: FP_TO_INT_S (IR, SB, 32, 1); break; + case FDTOI: FP_TO_INT_D (IR, DB, 32, 1); break; + case FQTOI: FP_TO_INT_Q (IR, QB, 32, 1); break; + /* int to float */ + case FITOS: IR = rs2->s; FP_FROM_INT_S (SR, IR, 32, int); break; + case FITOD: IR = rs2->s; FP_FROM_INT_D (DR, IR, 32, int); break; + case FITOQ: IR = rs2->s; FP_FROM_INT_Q (QR, IR, 32, int); break; + /* float to float */ + case FSTOD: FP_CONV (D, S, 2, 1, DR, SB); break; + case FSTOQ: FP_CONV (Q, S, 4, 1, QR, SB); break; + case FDTOQ: FP_CONV (Q, D, 4, 2, QR, DB); break; + case FDTOS: FP_CONV (S, D, 1, 2, SR, DB); break; + case FQTOS: FP_CONV (S, Q, 1, 4, SR, QB); break; + case FQTOD: FP_CONV (D, Q, 2, 4, DR, QB); break; + /* comparison */ + case FCMPS: + case FCMPES: + FP_UNPACK_RAW_SP (SA, rs1); + FP_UNPACK_RAW_SP (SB, rs2); + FP_CMP_S(IR, SB, SA, 3); + if (IR == 3 && + (((insn >> 5) & 0x1ff) == FCMPES || + FP_ISSIGNAN_S(SA) || + FP_ISSIGNAN_S(SB))) + FP_SET_EXCEPTION (FP_EX_INVALID); + break; + case FCMPD: + case FCMPED: + FP_UNPACK_RAW_DP (DA, rs1); + FP_UNPACK_RAW_DP (DB, rs2); + FP_CMP_D(IR, DB, DA, 3); + if (IR == 3 && + (((insn >> 5) & 0x1ff) == FCMPED || + FP_ISSIGNAN_D(DA) || + FP_ISSIGNAN_D(DB))) + FP_SET_EXCEPTION (FP_EX_INVALID); + break; + case FCMPQ: + case FCMPEQ: + FP_UNPACK_RAW_QP (QA, rs1); + FP_UNPACK_RAW_QP (QB, rs2); + FP_CMP_Q(IR, QB, QA, 3); + if (IR == 3 && + (((insn >> 5) & 0x1ff) == FCMPEQ || + FP_ISSIGNAN_Q(QA) || + FP_ISSIGNAN_Q(QB))) + FP_SET_EXCEPTION (FP_EX_INVALID); + } + if (!FP_INHIBIT_RESULTS) { + switch ((type >> 6) & 0x7) { + case 0: fsr = *pfsr; + if (IR == -1) IR = 2; + /* fcc is always fcc0 */ + fsr &= ~0xc00; fsr |= (IR << 10); break; + *pfsr = fsr; + break; + case 1: rd->s = IR; break; + case 5: FP_PACK_SP (rd, SR); break; + case 6: FP_PACK_DP (rd, DR); break; + case 7: FP_PACK_QP (rd, QR); break; + } + } + if(_fex == 0) + return 1; /* success! */ + return record_exception(pfsr, _fex); } diff -u --recursive --new-file v2.2.13/linux/arch/sparc/math-emu/sfp-machine.h linux/arch/sparc/math-emu/sfp-machine.h --- v2.2.13/linux/arch/sparc/math-emu/sfp-machine.h Mon Aug 9 16:05:55 1999 +++ linux/arch/sparc/math-emu/sfp-machine.h Tue Jan 4 10:12:13 2000 @@ -1,6 +1,11 @@ -/* Machine-dependent software floating-point definitions. Sparc version. - Copyright (C) 1997 Free Software Foundation, Inc. +/* Machine-dependent software floating-point definitions. + Sparc userland (_Q_*) version. + Copyright (C) 1997,1998,1999 Free Software Foundation, Inc. This file is part of the GNU C Library. + Contributed by Richard Henderson (rth@cygnus.com), + Jakub Jelinek (jj@ultra.linux.cz), + David S. Miller (davem@redhat.com) and + Peter Maydell (pmaydell@chiark.greenend.org.uk). The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as @@ -15,405 +20,186 @@ You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - Actually, this is a sparc (32bit) version, written based on the - i386 and sparc64 versions, by me, - Peter Maydell (pmaydell@chiark.greenend.org.uk). - Comments are by and large also mine, although they may be inaccurate. - - In picking out asm fragments I've gone with the lowest common - denominator, which also happens to be the hardware I have :-> - That is, a SPARC without hardware multiply and divide. - */ - - -/* basic word size definitions */ +#ifndef _SFP_MACHINE_H +#define _SFP_MACHINE_H + #define _FP_W_TYPE_SIZE 32 #define _FP_W_TYPE unsigned long #define _FP_WS_TYPE signed long #define _FP_I_TYPE long -/* You can optionally code some things like addition in asm. For - * example, i386 defines __FP_FRAC_ADD_2 as asm. If you don't - * then you get a fragment of C code [if you change an #ifdef 0 - * in op-2.h] or a call to add_ssaaaa (see below). - * Good places to look for asm fragments to use are gcc and glibc. - * gcc's longlong.h is useful. - */ - -/* We need to know how to multiply and divide. If the host word size - * is >= 2*fracbits you can use FP_MUL_MEAT_n_imm(t,R,X,Y) which - * codes the multiply with whatever gcc does to 'a * b'. - * _FP_MUL_MEAT_n_wide(t,R,X,Y,f) is used when you have an asm - * function that can multiply two 1W values and get a 2W result. - * Otherwise you're stuck with _FP_MUL_MEAT_n_hard(t,R,X,Y) which - * does bitshifting to avoid overflow. - * For division there is FP_DIV_MEAT_n_imm(t,R,X,Y,f) for word size - * >= 2*fracbits, where f is either _FP_DIV_HELP_imm or - * _FP_DIV_HELP_ldiv (see op-1.h). - * _FP_DIV_MEAT_udiv() is if you have asm to do 2W/1W => (1W, 1W). - * [GCC and glibc have longlong.h which has the asm macro udiv_qrnnd - * to do this.] - * In general, 'n' is the number of words required to hold the type, - * and 't' is either S, D or Q for single/double/quad. - * -- PMM - */ -/* Example: SPARC64: - * #define _FP_MUL_MEAT_S(R,X,Y) _FP_MUL_MEAT_1_imm(S,R,X,Y) - * #define _FP_MUL_MEAT_D(R,X,Y) _FP_MUL_MEAT_1_wide(D,R,X,Y,umul_ppmm) - * #define _FP_MUL_MEAT_Q(R,X,Y) _FP_MUL_MEAT_2_wide(Q,R,X,Y,umul_ppmm) - * - * #define _FP_DIV_MEAT_S(R,X,Y) _FP_DIV_MEAT_1_imm(S,R,X,Y,_FP_DIV_HELP_imm) - * #define _FP_DIV_MEAT_D(R,X,Y) _FP_DIV_MEAT_1_udiv(D,R,X,Y) - * #define _FP_DIV_MEAT_Q(R,X,Y) _FP_DIV_MEAT_2_udiv_64(Q,R,X,Y) - * - * Example: i386: - * #define _FP_MUL_MEAT_S(R,X,Y) _FP_MUL_MEAT_1_wide(S,R,X,Y,_i386_mul_32_64) - * #define _FP_MUL_MEAT_D(R,X,Y) _FP_MUL_MEAT_2_wide(D,R,X,Y,_i386_mul_32_64) - * - * #define _FP_DIV_MEAT_S(R,X,Y) _FP_DIV_MEAT_1_udiv(S,R,X,Y,_i386_div_64_32) - * #define _FP_DIV_MEAT_D(R,X,Y) _FP_DIV_MEAT_2_udiv_64(D,R,X,Y) - */ -#define _FP_MUL_MEAT_S(R,X,Y) _FP_MUL_MEAT_1_wide(S,R,X,Y,umul_ppmm) -#define _FP_MUL_MEAT_D(R,X,Y) _FP_MUL_MEAT_2_wide(D,R,X,Y,umul_ppmm) -/* FIXME: This is not implemented, but should be soon */ -#define _FP_MUL_MEAT_Q(R,X,Y) _FP_FRAC_SET_4(R, _FP_ZEROFRAC_4) -#define _FP_DIV_MEAT_S(R,X,Y) _FP_DIV_MEAT_1_udiv(S,R,X,Y) -#define _FP_DIV_MEAT_D(R,X,Y) _FP_DIV_MEAT_2_udiv_64(D,R,X,Y) -/* FIXME: This is not implemented, but should be soon */ -#define _FP_DIV_MEAT_Q(R,X,Y) _FP_FRAC_SET_4(R, _FP_ZEROFRAC_4) - -/* These macros define what NaN looks like. They're supposed to expand to - * a comma-separated set of 32bit unsigned ints that encode NaN. - */ -#define _FP_NANFRAC_S _FP_QNANBIT_S -#define _FP_NANFRAC_D _FP_QNANBIT_D, 0 -#define _FP_NANFRAC_Q _FP_QNANBIT_Q, 0, 0, 0 - -/* On some architectures float-to-int conversions return a result - * code. On others (e.g. Sparc) they return 0 - */ -#define _FTOI_RESULT 0 +#define _FP_MUL_MEAT_S(R,X,Y) \ + _FP_MUL_MEAT_1_wide(_FP_WFRACBITS_S,R,X,Y,umul_ppmm) +#define _FP_MUL_MEAT_D(R,X,Y) \ + _FP_MUL_MEAT_2_wide(_FP_WFRACBITS_D,R,X,Y,umul_ppmm) +#define _FP_MUL_MEAT_Q(R,X,Y) \ + _FP_MUL_MEAT_4_wide(_FP_WFRACBITS_Q,R,X,Y,umul_ppmm) + +#define _FP_DIV_MEAT_S(R,X,Y) _FP_DIV_MEAT_1_udiv(S,R,X,Y) +#define _FP_DIV_MEAT_D(R,X,Y) _FP_DIV_MEAT_2_udiv(D,R,X,Y) +#define _FP_DIV_MEAT_Q(R,X,Y) _FP_DIV_MEAT_4_udiv(Q,R,X,Y) + +#define _FP_NANFRAC_S ((_FP_QNANBIT_S << 1) - 1) +#define _FP_NANFRAC_D ((_FP_QNANBIT_D << 1) - 1), -1 +#define _FP_NANFRAC_Q ((_FP_QNANBIT_Q << 1) - 1), -1, -1, -1 +#define _FP_NANSIGN_S 0 +#define _FP_NANSIGN_D 0 +#define _FP_NANSIGN_Q 0 #define _FP_KEEPNANFRACP 1 -/* This macro appears to be called when both X and Y are NaNs, and - * has to choose one and copy it to R. i386 goes for the larger of the - * two, sparc64 just picks Y. I don't understand this at all so I'll - * go with sparc64 because it's shorter :-> -- PMM +/* If one NaN is signaling and the other is not, + * we choose that one, otherwise we choose X. */ -#define _FP_CHOOSENAN(fs, wc, R, X, Y) \ +/* For _Qp_* and _Q_*, this should prefer X, for + * CPU instruction emulation this should prefer Y. + * (see SPAMv9 B.2.2 section). + */ +#define _FP_CHOOSENAN(fs, wc, R, X, Y, OP) \ do { \ - R##_s = Y##_s; \ - _FP_FRAC_COPY_##wc(R,Y); \ + if ((_FP_FRAC_HIGH_RAW_##fs(Y) & _FP_QNANBIT_##fs) \ + && !(_FP_FRAC_HIGH_RAW_##fs(X) & _FP_QNANBIT_##fs)) \ + { \ + R##_s = X##_s; \ + _FP_FRAC_COPY_##wc(R,X); \ + } \ + else \ + { \ + R##_s = Y##_s; \ + _FP_FRAC_COPY_##wc(R,Y); \ + } \ R##_c = FP_CLS_NAN; \ } while (0) -#define _FP_CHOOSENAN_SQRT(fs, wc, R, X) \ - do { \ - R##_s = 0; \ - R##_c = FP_CLS_NAN; \ - _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ - } while (0) +/* Some assembly to speed things up. */ +#define __FP_FRAC_ADD_3(r2,r1,r0,x2,x1,x0,y2,y1,y0) \ + __asm__ ("addcc %r7,%8,%2 + addxcc %r5,%6,%1 + addx %r3,%4,%0" \ + : "=r" ((USItype)(r2)), \ + "=&r" ((USItype)(r1)), \ + "=&r" ((USItype)(r0)) \ + : "%rJ" ((USItype)(x2)), \ + "rI" ((USItype)(y2)), \ + "%rJ" ((USItype)(x1)), \ + "rI" ((USItype)(y1)), \ + "%rJ" ((USItype)(x0)), \ + "rI" ((USItype)(y0)) \ + : "cc") + +#define __FP_FRAC_SUB_3(r2,r1,r0,x2,x1,x0,y2,y1,y0) \ + __asm__ ("subcc %r7,%8,%2 + subxcc %r5,%6,%1 + subx %r3,%4,%0" \ + : "=r" ((USItype)(r2)), \ + "=&r" ((USItype)(r1)), \ + "=&r" ((USItype)(r0)) \ + : "%rJ" ((USItype)(x2)), \ + "rI" ((USItype)(y2)), \ + "%rJ" ((USItype)(x1)), \ + "rI" ((USItype)(y1)), \ + "%rJ" ((USItype)(x0)), \ + "rI" ((USItype)(y0)) \ + : "cc") + +#define __FP_FRAC_ADD_4(r3,r2,r1,r0,x3,x2,x1,x0,y3,y2,y1,y0) \ + do { \ + /* We need to fool gcc, as we need to pass more than 10 \ + input/outputs. */ \ + register USItype _t1 __asm__ ("g1"), _t2 __asm__ ("g2"); \ + __asm__ __volatile__ (" + addcc %r8,%9,%1 + addxcc %r6,%7,%0 + addxcc %r4,%5,%%g2 + addx %r2,%3,%%g1" \ + : "=&r" ((USItype)(r1)), \ + "=&r" ((USItype)(r0)) \ + : "%rJ" ((USItype)(x3)), \ + "rI" ((USItype)(y3)), \ + "%rJ" ((USItype)(x2)), \ + "rI" ((USItype)(y2)), \ + "%rJ" ((USItype)(x1)), \ + "rI" ((USItype)(y1)), \ + "%rJ" ((USItype)(x0)), \ + "rI" ((USItype)(y0)) \ + : "cc", "g1", "g2"); \ + __asm__ __volatile__ ("" : "=r" (_t1), "=r" (_t2)); \ + r3 = _t1; r2 = _t2; \ + } while (0) + +#define __FP_FRAC_SUB_4(r3,r2,r1,r0,x3,x2,x1,x0,y3,y2,y1,y0) \ + do { \ + /* We need to fool gcc, as we need to pass more than 10 \ + input/outputs. */ \ + register USItype _t1 __asm__ ("g1"), _t2 __asm__ ("g2"); \ + __asm__ __volatile__ (" + subcc %r8,%9,%1 + subxcc %r6,%7,%0 + subxcc %r4,%5,%%g2 + subx %r2,%3,%%g1" \ + : "=&r" ((USItype)(r1)), \ + "=&r" ((USItype)(r0)) \ + : "%rJ" ((USItype)(x3)), \ + "rI" ((USItype)(y3)), \ + "%rJ" ((USItype)(x2)), \ + "rI" ((USItype)(y2)), \ + "%rJ" ((USItype)(x1)), \ + "rI" ((USItype)(y1)), \ + "%rJ" ((USItype)(x0)), \ + "rI" ((USItype)(y0)) \ + : "cc", "g1", "g2"); \ + __asm__ __volatile__ ("" : "=r" (_t1), "=r" (_t2)); \ + r3 = _t1; r2 = _t2; \ + } while (0) + +#define __FP_FRAC_DEC_3(x2,x1,x0,y2,y1,y0) __FP_FRAC_SUB_3(x2,x1,x0,x2,x1,x0,y2,y1,y0) + +#define __FP_FRAC_DEC_4(x3,x2,x1,x0,y3,y2,y1,y0) __FP_FRAC_SUB_4(x3,x2,x1,x0,x3,x2,x1,x0,y3,y2,y1,y0) + +#define __FP_FRAC_ADDI_4(x3,x2,x1,x0,i) \ + __asm__ ("addcc %3,%4,%3 + addxcc %2,%%g0,%2 + addxcc %1,%%g0,%1 + addx %0,%%g0,%0" \ + : "=&r" ((USItype)(x3)), \ + "=&r" ((USItype)(x2)), \ + "=&r" ((USItype)(x1)), \ + "=&r" ((USItype)(x0)) \ + : "rI" ((USItype)(i)), \ + "0" ((USItype)(x3)), \ + "1" ((USItype)(x2)), \ + "2" ((USItype)(x1)), \ + "3" ((USItype)(x0)) \ + : "cc") - -#define __FP_UNPACK_DENORM(fs, wc, X) \ - { \ - _FP_I_TYPE _shift; \ - _FP_FRAC_CLZ_##wc(_shift, X); \ - _shift -= _FP_FRACXBITS_##fs; \ - _FP_FRAC_SLL_##wc(X, (_shift+_FP_WORKBITS)); \ - X##_e -= _FP_EXPBIAS_##fs - 1 + _shift; \ - X##_c = FP_CLS_NORMAL; \ - } - -#define __FP_UNPACK_RAW_1(fs, X, val) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - X##_f = _flo->bits.frac; \ - X##_e = _flo->bits.exp; \ - X##_s = _flo->bits.sign; \ - } while (0) - -#define __FP_UNPACK_RAW_2(fs, X, val) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - X##_f0 = _flo->bits.frac0; \ - X##_f1 = _flo->bits.frac1; \ - X##_e = _flo->bits.exp; \ - X##_s = _flo->bits.sign; \ - } while (0) - -#define __FP_UNPACK_RAW_4(fs, X, val) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - X##_f[0] = _flo->bits.frac0; \ - X##_f[1] = _flo->bits.frac1; \ - X##_f[2] = _flo->bits.frac2; \ - X##_f[3] = _flo->bits.frac3; \ - X##_e = _flo->bits.exp; \ - X##_s = _flo->bits.sign; \ - } while (0) - -#define __FP_UNPACK_S(X,val) \ - do { \ - __FP_UNPACK_RAW_1(S,X,val); \ - _FP_UNPACK_CANONICAL(S,1,X); \ - } while (0) - -#define __FP_UNPACK_D(X,val) \ - do { \ - __FP_UNPACK_RAW_2(D,X,val); \ - _FP_UNPACK_CANONICAL(D,2,X); \ - } while (0) - -#define __FP_UNPACK_Q(X,val) \ - do { \ - __FP_UNPACK_RAW_4(Q,X,val); \ - _FP_UNPACK_CANONICAL(Q,4,X); \ - } while (0) - -#define __FP_PACK_RAW_1(fs, val, X) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - _flo->bits.frac = X##_f; \ - _flo->bits.exp = X##_e; \ - _flo->bits.sign = X##_s; \ - } while (0) - -#define __FP_PACK_RAW_2(fs, val, X) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - _flo->bits.frac0 = X##_f0; \ - _flo->bits.frac1 = X##_f1; \ - _flo->bits.exp = X##_e; \ - _flo->bits.sign = X##_s; \ - } while (0) - -#define __FP_PACK_RAW_4(fs, val, X) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - _flo->bits.frac0 = X##_f[0]; \ - _flo->bits.frac1 = X##_f[1]; \ - _flo->bits.frac2 = X##_f[2]; \ - _flo->bits.frac3 = X##_f[3]; \ - _flo->bits.exp = X##_e; \ - _flo->bits.sign = X##_s; \ - } while (0) - -#include -#include - -/* We only actually write to the destination register - * if exceptions signalled (if any) will not trap. - */ -#ifdef __SMP__ -#define __FPU_TEM \ - (((current->tss.fsr)>>23)&0x1f) -#else +#ifndef __SMP__ extern struct task_struct *last_task_used_math; -#define __FPU_TEM \ - (((last_task_used_math->tss.fsr)>>23)&0x1f) #endif -#define __FPU_TRAP_P(bits) \ - ((__FPU_TEM & (bits)) != 0) - -#define __FP_PACK_S(val,X) \ -({ int __exc = _FP_PACK_CANONICAL(S,1,X); \ - if(!__exc || !__FPU_TRAP_P(__exc)) \ - __FP_PACK_RAW_1(S,val,X); \ - __exc; \ -}) - -#define __FP_PACK_D(val,X) \ -({ int __exc = _FP_PACK_CANONICAL(D,2,X); \ - if(!__exc || !__FPU_TRAP_P(__exc)) \ - __FP_PACK_RAW_2(D,val,X); \ - __exc; \ -}) - -#define __FP_PACK_Q(val,X) \ -({ int __exc = _FP_PACK_CANONICAL(Q,4,X); \ - if(!__exc || !__FPU_TRAP_P(__exc)) \ - __FP_PACK_RAW_4(Q,val,X); \ - __exc; \ -}) /* Obtain the current rounding mode. */ +#ifndef FP_ROUNDMODE #ifdef __SMP__ #define FP_ROUNDMODE ((current->tss.fsr >> 30) & 0x3) #else #define FP_ROUNDMODE ((last_task_used_math->tss.fsr >> 30) & 0x3) #endif +#endif -/* the asm fragments go here: all these are taken from glibc-2.0.5's stdlib/longlong.h */ - -#include -#include - -/* add_ssaaaa is used in op-2.h and should be equivalent to - * #define add_ssaaaa(sh,sl,ah,al,bh,bl) (sh = ah+bh+ (( sl = al+bl) < al)) - * add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1, - * high_addend_2, low_addend_2) adds two UWtype integers, composed by - * HIGH_ADDEND_1 and LOW_ADDEND_1, and HIGH_ADDEND_2 and LOW_ADDEND_2 - * respectively. The result is placed in HIGH_SUM and LOW_SUM. Overflow - * (i.e. carry out) is not stored anywhere, and is lost. - */ -#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ - __asm__ ("addcc %r4,%5,%1 - addx %r2,%3,%0" \ - : "=r" ((USItype)(sh)), \ - "=&r" ((USItype)(sl)) \ - : "%rJ" ((USItype)(ah)), \ - "rI" ((USItype)(bh)), \ - "%rJ" ((USItype)(al)), \ - "rI" ((USItype)(bl)) \ - : "cc") - - -/* sub_ddmmss is used in op-2.h and udivmodti4.c and should be equivalent to - * #define sub_ddmmss(sh, sl, ah, al, bh, bl) (sh = ah-bh - ((sl = al-bl) > al)) - * sub_ddmmss(high_difference, low_difference, high_minuend, low_minuend, - * high_subtrahend, low_subtrahend) subtracts two two-word UWtype integers, - * composed by HIGH_MINUEND_1 and LOW_MINUEND_1, and HIGH_SUBTRAHEND_2 and - * LOW_SUBTRAHEND_2 respectively. The result is placed in HIGH_DIFFERENCE - * and LOW_DIFFERENCE. Overflow (i.e. carry out) is not stored anywhere, - * and is lost. - */ - -#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ - __asm__ ("subcc %r4,%5,%1 - subx %r2,%3,%0" \ - : "=r" ((USItype)(sh)), \ - "=&r" ((USItype)(sl)) \ - : "rJ" ((USItype)(ah)), \ - "rI" ((USItype)(bh)), \ - "rJ" ((USItype)(al)), \ - "rI" ((USItype)(bl)) \ - : "cc") - - -/* asm fragments for mul and div */ -/* umul_ppmm(high_prod, low_prod, multipler, multiplicand) multiplies two - * UWtype integers MULTIPLER and MULTIPLICAND, and generates a two UWtype - * word product in HIGH_PROD and LOW_PROD. - * These look ugly because the sun4/4c don't have umul/udiv/smul/sdiv in - * hardware. - */ -#define umul_ppmm(w1, w0, u, v) \ - __asm__ ("! Inlined umul_ppmm - wr %%g0,%2,%%y ! SPARC has 0-3 delay insn after a wr - sra %3,31,%%g2 ! Don't move this insn - and %2,%%g2,%%g2 ! Don't move this insn - andcc %%g0,0,%%g1 ! Don't move this insn - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,%3,%%g1 - mulscc %%g1,0,%%g1 - add %%g1,%%g2,%0 - rd %%y,%1" \ - : "=r" ((USItype)(w1)), \ - "=r" ((USItype)(w0)) \ - : "%rI" ((USItype)(u)), \ - "r" ((USItype)(v)) \ - : "%g1", "%g2", "cc") - -/* udiv_qrnnd(quotient, remainder, high_numerator, low_numerator, - * denominator) divides a UDWtype, composed by the UWtype integers - * HIGH_NUMERATOR and LOW_NUMERATOR, by DENOMINATOR and places the quotient - * in QUOTIENT and the remainder in REMAINDER. HIGH_NUMERATOR must be less - * than DENOMINATOR for correct operation. If, in addition, the most - * significant bit of DENOMINATOR must be 1, then the pre-processor symbol - * UDIV_NEEDS_NORMALIZATION is defined to 1. - */ - -#define udiv_qrnnd(q, r, n1, n0, d) \ - __asm__ ("! Inlined udiv_qrnnd - mov 32,%%g1 - subcc %1,%2,%%g0 -1: bcs 5f - addxcc %0,%0,%0 ! shift n1n0 and a q-bit in lsb - sub %1,%2,%1 ! this kills msb of n - addx %1,%1,%1 ! so this can't give carry - subcc %%g1,1,%%g1 -2: bne 1b - subcc %1,%2,%%g0 - bcs 3f - addxcc %0,%0,%0 ! shift n1n0 and a q-bit in lsb - b 3f - sub %1,%2,%1 ! this kills msb of n -4: sub %1,%2,%1 -5: addxcc %1,%1,%1 - bcc 2b - subcc %%g1,1,%%g1 -! Got carry from n. Subtract next step to cancel this carry. - bne 4b - addcc %0,%0,%0 ! shift n1n0 and a 0-bit in lsb - sub %1,%2,%1 -3: xnor %0,0,%0 - ! End of inline udiv_qrnnd" \ - : "=&r" ((USItype) (q)), \ - "=&r" ((USItype) (r)) \ - : "r" ((USItype) (d)), \ - "1" ((USItype) (n1)), \ - "0" ((USItype) (n0)) : "%g1", "cc") - -#define UDIV_NEEDS_NORMALIZATION 0 +/* Exception flags. */ +#define FP_EX_INVALID (1 << 4) +#define FP_EX_OVERFLOW (1 << 3) +#define FP_EX_UNDERFLOW (1 << 2) +#define FP_EX_DIVZERO (1 << 1) +#define FP_EX_INEXACT (1 << 0) -#define abort() \ - return 0 +#define FP_HANDLE_EXCEPTIONS return _fex -#ifdef __BIG_ENDIAN -#define __BYTE_ORDER __BIG_ENDIAN +#ifdef __SMP__ +#define FP_INHIBIT_RESULTS ((current->tss.fsr >> 23) & _fex) #else -#define __BYTE_ORDER __LITTLE_ENDIAN +#define FP_INHIBIT_RESULTS ((last_task_used_math->tss.fsr >> 23) & _fex) #endif -/* Exception flags. */ -#define EFLAG_INVALID (1 << 4) -#define EFLAG_OVERFLOW (1 << 3) -#define EFLAG_UNDERFLOW (1 << 2) -#define EFLAG_DIVZERO (1 << 1) -#define EFLAG_INEXACT (1 << 0) +#endif diff -u --recursive --new-file v2.2.13/linux/arch/sparc/math-emu/sfp-util.h linux/arch/sparc/math-emu/sfp-util.h --- v2.2.13/linux/arch/sparc/math-emu/sfp-util.h Wed Dec 31 16:00:00 1969 +++ linux/arch/sparc/math-emu/sfp-util.h Tue Jan 4 10:12:13 2000 @@ -0,0 +1,115 @@ +#include +#include +#include +#include + +#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ + __asm__ ("addcc %r4,%5,%1 + addx %r2,%3,%0" \ + : "=r" ((USItype)(sh)), \ + "=&r" ((USItype)(sl)) \ + : "%rJ" ((USItype)(ah)), \ + "rI" ((USItype)(bh)), \ + "%rJ" ((USItype)(al)), \ + "rI" ((USItype)(bl)) \ + : "cc") +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + __asm__ ("subcc %r4,%5,%1 + subx %r2,%3,%0" \ + : "=r" ((USItype)(sh)), \ + "=&r" ((USItype)(sl)) \ + : "rJ" ((USItype)(ah)), \ + "rI" ((USItype)(bh)), \ + "rJ" ((USItype)(al)), \ + "rI" ((USItype)(bl)) \ + : "cc") + +#define umul_ppmm(w1, w0, u, v) \ + __asm__ ("! Inlined umul_ppmm + wr %%g0,%2,%%y ! SPARC has 0-3 delay insn after a wr + sra %3,31,%%g2 ! Don't move this insn + and %2,%%g2,%%g2 ! Don't move this insn + andcc %%g0,0,%%g1 ! Don't move this insn + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,%3,%%g1 + mulscc %%g1,0,%%g1 + add %%g1,%%g2,%0 + rd %%y,%1" \ + : "=r" ((USItype)(w1)), \ + "=r" ((USItype)(w0)) \ + : "%rI" ((USItype)(u)), \ + "r" ((USItype)(v)) \ + : "%g1", "%g2", "cc") + +/* It's quite necessary to add this much assembler for the sparc. + The default udiv_qrnnd (in C) is more than 10 times slower! */ +#define udiv_qrnnd(q, r, n1, n0, d) \ + __asm__ ("! Inlined udiv_qrnnd + mov 32,%%g1 + subcc %1,%2,%%g0 +1: bcs 5f + addxcc %0,%0,%0 ! shift n1n0 and a q-bit in lsb + sub %1,%2,%1 ! this kills msb of n + addx %1,%1,%1 ! so this can't give carry + subcc %%g1,1,%%g1 +2: bne 1b + subcc %1,%2,%%g0 + bcs 3f + addxcc %0,%0,%0 ! shift n1n0 and a q-bit in lsb + b 3f + sub %1,%2,%1 ! this kills msb of n +4: sub %1,%2,%1 +5: addxcc %1,%1,%1 + bcc 2b + subcc %%g1,1,%%g1 +! Got carry from n. Subtract next step to cancel this carry. + bne 4b + addcc %0,%0,%0 ! shift n1n0 and a 0-bit in lsb + sub %1,%2,%1 +3: xnor %0,0,%0 + ! End of inline udiv_qrnnd" \ + : "=&r" ((USItype)(q)), \ + "=&r" ((USItype)(r)) \ + : "r" ((USItype)(d)), \ + "1" ((USItype)(n1)), \ + "0" ((USItype)(n0)) : "%g1", "cc") +#define UDIV_NEEDS_NORMALIZATION 0 + +#define abort() \ + return 0 + +#ifdef __BIG_ENDIAN +#define __BYTE_ORDER __BIG_ENDIAN +#else +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif diff -u --recursive --new-file v2.2.13/linux/arch/sparc/mm/Makefile linux/arch/sparc/mm/Makefile --- v2.2.13/linux/arch/sparc/mm/Makefile Wed Mar 10 16:53:36 1999 +++ linux/arch/sparc/mm/Makefile Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.33 1999/01/02 16:45:47 davem Exp $ +# $Id: Makefile,v 1.33.2.1 1999/10/06 15:36:20 davem Exp $ # Makefile for the linux Sparc-specific parts of the memory manager. # # Note! Dependencies are done automagically by 'make dep', which also @@ -12,7 +12,7 @@ ifeq ($(CONFIG_SUN4),y) O_OBJS += nosrmmu.o else -O_OBJS += srmmu.o iommu.o io-unit.o hypersparc.o viking.o tsunami.o +O_OBJS += srmmu.o iommu.o io-unit.o hypersparc.o viking.o tsunami.o swift.o endif ifdef CONFIG_SMP O_OBJS += nosun4c.o @@ -30,3 +30,6 @@ tsunami.o: tsunami.S $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o tsunami.o tsunami.S + +swift.o: swift.S + $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o swift.o swift.S diff -u --recursive --new-file v2.2.13/linux/arch/sparc/mm/asyncd.c linux/arch/sparc/mm/asyncd.c --- v2.2.13/linux/arch/sparc/mm/asyncd.c Sun Oct 4 10:22:42 1998 +++ linux/arch/sparc/mm/asyncd.c Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: asyncd.c,v 1.12 1998/09/13 04:30:30 davem Exp $ +/* $Id: asyncd.c,v 1.12.2.1 1999/11/16 06:29:36 davem Exp $ * The asyncd kernel daemon. This handles paging on behalf of * processes that receive page faults due to remote (async) memory * accesses. @@ -152,7 +152,9 @@ if(!pte) goto no_memory; if(!pte_present(*pte)) { - handle_mm_fault(tsk, vma, address, write); + int fault = handle_mm_fault(tsk, vma, address, write); + if (fault < 0) + goto no_memory; goto finish_up; } set_pte(pte, pte_mkyoung(*pte)); @@ -164,7 +166,11 @@ flush_tlb_page(vma, address); goto finish_up; } - handle_mm_fault(tsk, vma, address, write); + { + int fault = handle_mm_fault(tsk, vma, address, write); + if (fault < 0) + goto no_memory; + } /* Fall through for do_wp_page */ finish_up: @@ -173,7 +179,7 @@ no_memory: stats.failure++; - oom(tsk); + force_sig(SIGKILL, tsk); return 1; bad_area: diff -u --recursive --new-file v2.2.13/linux/arch/sparc/mm/fault.c linux/arch/sparc/mm/fault.c --- v2.2.13/linux/arch/sparc/mm/fault.c Mon Aug 9 16:05:55 1999 +++ linux/arch/sparc/mm/fault.c Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: fault.c,v 1.101.2.2 1999/08/07 10:42:53 davem Exp $ +/* $Id: fault.c,v 1.101.2.5 1999/11/16 06:29:39 davem Exp $ * fault.c: Page fault handlers for the Sparc. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -237,8 +237,14 @@ if(!(vma->vm_flags & (VM_READ | VM_EXEC))) goto bad_area; } - if (!handle_mm_fault(current, vma, address, write)) - goto do_sigbus; +survive: + { + int fault = handle_mm_fault(current, vma, address, write); + if (!fault) + goto do_sigbus; + if (fault < 0) + goto out_of_memory; + } up(&mm->mmap_sem); return; /* @@ -288,6 +294,17 @@ unhandled_fault (address, tsk, regs); return; +out_of_memory: + if (tsk->pid == 1) { + tsk->policy |= SCHED_YIELD; + schedule(); + goto survive; + } + up(&mm->mmap_sem); + printk("VM: killing process %s\n", tsk->comm); + do_exit(SIGKILL); + return; + do_sigbus: up(&mm->mmap_sem); tsk->tss.sig_address = address; @@ -309,8 +326,18 @@ pgd_t *pgdp; pte_t *ptep; - if (text_fault) + if (text_fault) { address = regs->pc; + } else if (!write && + !(regs->psr & PSR_PS)) { + unsigned int insn, *ip; + + ip = (unsigned int *)regs->pc; + if (! get_user(insn, ip)) { + if ((insn & 0xc1680000) == 0xc0680000) + write = 1; + } + } pgdp = sun4c_pgd_offset(mm, address); ptep = sun4c_pte_offset((pmd_t *) pgdp, address); @@ -319,28 +346,36 @@ if (write) { if ((pte_val(*ptep) & (_SUN4C_PAGE_WRITE|_SUN4C_PAGE_PRESENT)) == (_SUN4C_PAGE_WRITE|_SUN4C_PAGE_PRESENT)) { + unsigned long flags; *ptep = __pte(pte_val(*ptep) | _SUN4C_PAGE_ACCESSED | _SUN4C_PAGE_MODIFIED | _SUN4C_PAGE_VALID | _SUN4C_PAGE_DIRTY); + save_and_cli(flags); if (sun4c_get_segmap(address) != invalid_segment) { sun4c_put_pte(address, pte_val(*ptep)); + restore_flags(flags); return; } + restore_flags(flags); } } else { if ((pte_val(*ptep) & (_SUN4C_PAGE_READ|_SUN4C_PAGE_PRESENT)) == (_SUN4C_PAGE_READ|_SUN4C_PAGE_PRESENT)) { + unsigned long flags; *ptep = __pte(pte_val(*ptep) | _SUN4C_PAGE_ACCESSED | _SUN4C_PAGE_VALID); + save_and_cli(flags); if (sun4c_get_segmap(address) != invalid_segment) { sun4c_put_pte(address, pte_val(*ptep)); + restore_flags(flags); return; } + restore_flags(flags); } } } @@ -389,8 +424,14 @@ if(!(vma->vm_flags & (VM_READ | VM_EXEC))) goto bad_area; } - if (!handle_mm_fault(current, vma, address, write)) - goto do_sigbus; +survive: + { + int fault = handle_mm_fault(current, vma, address, write); + if (!fault) + goto do_sigbus; + if (fault < 0) + goto out_of_memory; + } up(&mm->mmap_sem); return; bad_area: @@ -404,6 +445,17 @@ send_sig(SIGSEGV, tsk, 1); return; +out_of_memory: + if (tsk->pid == 1) { + tsk->policy |= SCHED_YIELD; + schedule(); + goto survive; + } + up(&mm->mmap_sem); + printk("VM: killing process %s\n", tsk->comm); + do_exit(SIGKILL); + return; + do_sigbus: up(&mm->mmap_sem); tsk->tss.sig_address = address; @@ -415,31 +467,25 @@ { unsigned long sp; - lock_kernel(); sp = current->tss.rwbuf_stkptrs[0]; if(((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK)) force_user_fault(sp + 0x38, 1); force_user_fault(sp, 1); - unlock_kernel(); } void window_underflow_fault(unsigned long sp) { - lock_kernel(); if(((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK)) force_user_fault(sp + 0x38, 0); force_user_fault(sp, 0); - unlock_kernel(); } void window_ret_fault(struct pt_regs *regs) { unsigned long sp; - lock_kernel(); sp = regs->u_regs[UREG_FP]; if(((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK)) force_user_fault(sp + 0x38, 0); force_user_fault(sp, 0); - unlock_kernel(); } diff -u --recursive --new-file v2.2.13/linux/arch/sparc/mm/iommu.c linux/arch/sparc/mm/iommu.c --- v2.2.13/linux/arch/sparc/mm/iommu.c Tue Oct 19 17:10:36 1999 +++ linux/arch/sparc/mm/iommu.c Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: iommu.c,v 1.10.2.1 1999/08/13 12:35:35 davem Exp $ +/* $Id: iommu.c,v 1.10.2.2 1999/11/03 03:05:55 davem Exp $ * iommu.c: IOMMU specific routines for memory management. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -16,6 +16,7 @@ #include #include #include +#include /* srmmu.c */ extern int viking_mxcc_present; @@ -201,16 +202,19 @@ #ifdef CONFIG_SBUS static void iommu_map_dma_area(unsigned long addr, int len) { - unsigned long page, end; + unsigned long page, end, ipte_cache; pgprot_t dvma_prot; struct iommu_struct *iommu = SBus_chain->iommu; iopte_t *iopte = iommu->page_table; iopte_t *first; - if(viking_mxcc_present) + if(viking_mxcc_present || srmmu_modtype == HyperSparc) { dvma_prot = __pgprot(SRMMU_CACHE | SRMMU_ET_PTE | SRMMU_PRIV); - else + ipte_cache = 1; + } else { dvma_prot = __pgprot(SRMMU_ET_PTE | SRMMU_PRIV); + ipte_cache = 0; + } iopte += ((addr - iommu->start) >> PAGE_SHIFT); first = iopte; @@ -229,13 +233,20 @@ viking_mxcc_flush_page(page); else if (viking_flush) viking_flush_page(page); + else + flush_page_to_ram(page); pgdp = pgd_offset(init_task.mm, addr); pmdp = pmd_offset(pgdp, addr); ptep = pte_offset(pmdp, addr); set_pte(ptep, pte_val(mk_pte(page, dvma_prot))); - iopte_val(*iopte++) = MKIOPTE(mmu_v2p(page)); + if (ipte_cache != 0) { + iopte_val(*iopte++) = MKIOPTE(mmu_v2p(page)); + } else { + iopte_val(*iopte++) = + MKIOPTE(mmu_v2p(page)) & ~IOPTE_CACHE; + } } addr += PAGE_SIZE; } diff -u --recursive --new-file v2.2.13/linux/arch/sparc/mm/nosrmmu.c linux/arch/sparc/mm/nosrmmu.c --- v2.2.13/linux/arch/sparc/mm/nosrmmu.c Thu Apr 22 19:24:51 1999 +++ linux/arch/sparc/mm/nosrmmu.c Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: nosrmmu.c,v 1.2 1999/03/30 10:17:39 jj Exp $ +/* $Id: nosrmmu.c,v 1.2.2.1 1999/10/06 10:52:37 anton Exp $ * nosrmmu.c: This file is a bunch of dummies for sun4 compiles, * so that it does not need srmmu and avoid ifdefs. * @@ -13,6 +13,8 @@ static char shouldnothappen[] __initdata = "SUN4 kernel can only run on SUN4\n"; enum mbus_module srmmu_modtype; + +int vac_cache_size = 0; __initfunc(static void should_not_happen(void)) { diff -u --recursive --new-file v2.2.13/linux/arch/sparc/mm/srmmu.c linux/arch/sparc/mm/srmmu.c --- v2.2.13/linux/arch/sparc/mm/srmmu.c Tue Jan 4 11:10:32 2000 +++ linux/arch/sparc/mm/srmmu.c Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: srmmu.c,v 1.187.2.2 1999/09/21 11:24:15 anton Exp $ +/* $Id: srmmu.c,v 1.187.2.7 1999/11/16 06:29:44 davem Exp $ * srmmu.c: SRMMU specific routines for memory management. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -114,7 +114,9 @@ #define srmmu_ahashfn(addr) ((addr) >> 24) int viking_mxcc_present = 0; +#ifdef __SMP__ static spinlock_t srmmu_context_spinlock = SPIN_LOCK_UNLOCKED; +#endif /* Physical memory can be _very_ non-contiguous on the sun4m, especially * the SS10/20 class machines and with the latest openprom revisions. @@ -725,6 +727,17 @@ srmmu_set_entry(ptep, pte_val(pteval)); } +extern void swift_flush_chunk(unsigned long chunk); + +static void srmmu_set_pte_nocache_swift(pte_t *ptep, pte_t pteval) +{ + unsigned long page; + + srmmu_set_entry(ptep, pte_val(pteval)); + page = ((unsigned long)ptep) & PAGE_MASK; + swift_flush_chunk(page); +} + static void srmmu_set_pte_nocache_cypress(pte_t *ptep, pte_t pteval) { register unsigned long a, b, c, d, e, f, g; @@ -909,7 +922,8 @@ /* tsunami.S */ extern void tsunami_flush_cache_all(void); extern void tsunami_flush_cache_mm(struct mm_struct *mm); -extern void tsunami_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end); +extern void tsunami_flush_cache_range(struct mm_struct *mm, + unsigned long start, unsigned long end); extern void tsunami_flush_cache_page(struct vm_area_struct *vma, unsigned long page); extern void tsunami_flush_page_to_ram(unsigned long page); extern void tsunami_flush_page_for_dma(unsigned long page); @@ -917,111 +931,54 @@ extern void tsunami_flush_chunk(unsigned long chunk); extern void tsunami_flush_tlb_all(void); extern void tsunami_flush_tlb_mm(struct mm_struct *mm); -extern void tsunami_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end); +extern void tsunami_flush_tlb_range(struct mm_struct *mm, + unsigned long start, unsigned long end); extern void tsunami_flush_tlb_page(struct vm_area_struct *vma, unsigned long page); +extern void tsunami_setup_blockops(void); -/* Workaround, until we find what's going on with Swift. When low on memory, it sometimes - * loops in fault/handle_mm_fault incl. flush_tlb_page to find out it is already in page tables/ - * fault again on the same instruction. I really don't understand it, have checked it and contexts - * are right, flush_tlb_all is done as well, and it faults again... Strange. -jj - */ -static void swift_update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t pte) -{ - static unsigned long last; - - if (last == address) viking_hwprobe(address); - last = address; -} - -/* Swift flushes. It has the recommended SRMMU specification flushing - * facilities, so we can do things in a more fine grained fashion than we - * could on the tsunami. Let's watch out for HARDWARE BUGS... - */ +/* swift.S */ +extern void swift_flush_cache_all(void); +extern void swift_flush_cache_mm(struct mm_struct *mm); +extern void swift_flush_cache_range(struct mm_struct *mm, + unsigned long start, unsigned long end); +extern void swift_flush_cache_page(struct vm_area_struct *vma, unsigned long page); +extern void swift_flush_page_to_ram(unsigned long page); +extern void swift_flush_page_for_dma(unsigned long page); +extern void swift_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr); +extern void swift_flush_tlb_all(void); +extern void swift_flush_tlb_mm(struct mm_struct *mm); +extern void swift_flush_tlb_range(struct mm_struct *mm, + unsigned long start, unsigned long end); +extern void swift_flush_tlb_page(struct vm_area_struct *vma, unsigned long page); -static void swift_flush_cache_all(void) -{ - flush_user_windows(); - swift_idflash_clear(); -} - -static void swift_flush_cache_mm(struct mm_struct *mm) -{ - FLUSH_BEGIN(mm) - flush_user_windows(); - swift_idflash_clear(); - FLUSH_END -} - -static void swift_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end) -{ - FLUSH_BEGIN(mm) - flush_user_windows(); - swift_idflash_clear(); - FLUSH_END -} - -static void swift_flush_cache_page(struct vm_area_struct *vma, unsigned long page) -{ - FLUSH_BEGIN(vma->vm_mm) - flush_user_windows(); - if(vma->vm_flags & VM_EXEC) - swift_flush_icache(); - swift_flush_dcache(); - FLUSH_END -} - -/* Not copy-back on swift. */ -static void swift_flush_page_to_ram(unsigned long page) -{ -} - -/* But not IO coherent either. */ -static void swift_flush_page_for_dma(unsigned long page) -{ - swift_flush_dcache(); -} - -/* Again, Swift is non-snooping split I/D cache'd just like tsunami, - * so have to punt the icache for on-stack signal insns. Only the - * icache need be flushed since the dcache is write-through. - */ -static void swift_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr) -{ - swift_flush_icache(); -} - -static void swift_flush_chunk(unsigned long chunk) +static void swift_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) { + if(pgdp != swapper_pg_dir) + swift_flush_chunk((unsigned long)pgdp); + if(tsk->mm->context != NO_CONTEXT && + tsk->mm->pgd != pgdp) { + swift_flush_cache_mm(tsk->mm); + ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp); + swift_flush_tlb_mm(tsk->mm); + } } -static void swift_flush_tlb_all(void) +static void swift_init_new_context(struct mm_struct *mm) { - srmmu_flush_whole_tlb(); - module_stats.invall++; -} + ctxd_t *ctxp; -static void swift_flush_tlb_mm(struct mm_struct *mm) -{ - FLUSH_BEGIN(mm) - srmmu_flush_whole_tlb(); - module_stats.invmm++; - FLUSH_END -} + spin_lock(&srmmu_context_spinlock); + alloc_context(mm); + spin_unlock(&srmmu_context_spinlock); -static void swift_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) -{ - FLUSH_BEGIN(mm) - srmmu_flush_whole_tlb(); - module_stats.invrnge++; - FLUSH_END -} + ctxp = &srmmu_context_table[mm->context]; + srmmu_set_entry((pte_t *)ctxp, + __pte((SRMMU_ET_PTD | + (srmmu_v2p((unsigned long) mm->pgd) >> 4)))); + swift_flush_chunk(((unsigned long)ctxp) & PAGE_MASK); -static void swift_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) -{ - FLUSH_BEGIN(vma->vm_mm) - srmmu_flush_whole_tlb(); - module_stats.invpg++; - FLUSH_END + if(mm == current->mm) + srmmu_set_context(mm->context); } /* The following are all MBUS based SRMMU modules, and therefore could @@ -1306,7 +1263,8 @@ /* hypersparc.S */ extern void hypersparc_flush_cache_all(void); extern void hypersparc_flush_cache_mm(struct mm_struct *mm); -extern void hypersparc_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end); +extern void hypersparc_flush_cache_range(struct mm_struct *mm, + unsigned long start, unsigned long end); extern void hypersparc_flush_cache_page(struct vm_area_struct *vma, unsigned long page); extern void hypersparc_flush_page_to_ram(unsigned long page); extern void hypersparc_flush_chunk(unsigned long chunk); @@ -1314,7 +1272,8 @@ extern void hypersparc_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr); extern void hypersparc_flush_tlb_all(void); extern void hypersparc_flush_tlb_mm(struct mm_struct *mm); -extern void hypersparc_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end); +extern void hypersparc_flush_tlb_range(struct mm_struct *mm, + unsigned long start, unsigned long end); extern void hypersparc_flush_tlb_page(struct vm_area_struct *vma, unsigned long page); extern void hypersparc_setup_blockops(void); @@ -1328,7 +1287,8 @@ static void hypersparc_ctxd_set(ctxd_t *ctxp, pgd_t *pgdp) { - srmmu_set_entry((pte_t *)ctxp, __pte((SRMMU_ET_PTD | (srmmu_v2p((unsigned long) pgdp) >> 4)))); + srmmu_set_entry((pte_t *)ctxp, __pte((SRMMU_ET_PTD | + (srmmu_v2p((unsigned long) pgdp) >> 4)))); hypersparc_flush_page_to_ram((unsigned long)ctxp); hyper_flush_whole_icache(); } @@ -1407,7 +1367,9 @@ alloc_context(tsk->mm); spin_unlock(&srmmu_context_spinlock); ctxp = &srmmu_context_table[tsk->mm->context]; - srmmu_set_entry((pte_t *)ctxp, __pte((SRMMU_ET_PTD | (srmmu_v2p((unsigned long) tsk->mm->pgd) >> 4)))); + srmmu_set_entry((pte_t *)ctxp, + __pte((SRMMU_ET_PTD | + (srmmu_v2p((unsigned long) tsk->mm->pgd) >> 4)))); hypersparc_flush_page_to_ram((unsigned long)ctxp); } hyper_flush_whole_icache(); @@ -1423,7 +1385,9 @@ spin_unlock(&srmmu_context_spinlock); ctxp = &srmmu_context_table[mm->context]; - srmmu_set_entry((pte_t *)ctxp, __pte((SRMMU_ET_PTD | (srmmu_v2p((unsigned long) mm->pgd) >> 4)))); + srmmu_set_entry((pte_t *)ctxp, + __pte((SRMMU_ET_PTD | + (srmmu_v2p((unsigned long) mm->pgd) >> 4)))); hypersparc_flush_page_to_ram((unsigned long)ctxp); if(mm == current->mm) { @@ -1450,12 +1414,14 @@ static inline void srmmu_early_pgd_set(pgd_t *pgdp, pmd_t *pmdp) { - set_pte((pte_t *)pgdp, __pte((SRMMU_ET_PTD | (srmmu_early_paddr((unsigned long) pmdp) >> 4)))); + set_pte((pte_t *)pgdp, __pte((SRMMU_ET_PTD | + (srmmu_early_paddr((unsigned long) pmdp) >> 4)))); } static inline void srmmu_early_pmd_set(pmd_t *pmdp, pte_t *ptep) { - set_pte((pte_t *)pmdp, __pte((SRMMU_ET_PTD | (srmmu_early_paddr((unsigned long) ptep) >> 4)))); + set_pte((pte_t *)pmdp, __pte((SRMMU_ET_PTD | + (srmmu_early_paddr((unsigned long) ptep) >> 4)))); } static inline unsigned long srmmu_early_pgd_page(pgd_t pgd) @@ -1470,12 +1436,14 @@ static inline pmd_t *srmmu_early_pmd_offset(pgd_t *dir, unsigned long address) { - return (pmd_t *) srmmu_early_pgd_page(*dir) + ((address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1)); + return (pmd_t *) srmmu_early_pgd_page(*dir) + + ((address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1)); } static inline pte_t *srmmu_early_pte_offset(pmd_t *dir, unsigned long address) { - return (pte_t *) srmmu_early_pmd_page(*dir) + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1)); + return (pte_t *) srmmu_early_pmd_page(*dir) + + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1)); } static inline void srmmu_allocate_ptable_skeleton(unsigned long start, unsigned long end) @@ -2245,6 +2213,7 @@ __initfunc(static void init_hypersparc(void)) { srmmu_name = "ROSS HyperSparc"; + srmmu_modtype = HyperSparc; init_vac_layout(); @@ -2373,21 +2342,14 @@ __initfunc(static void poke_swift(void)) { - unsigned long mreg = srmmu_get_mmureg(); + unsigned long mreg; /* Clear any crap from the cache or else... */ - swift_idflash_clear(); - mreg |= (SWIFT_IE | SWIFT_DE); /* I & D caches on */ + swift_flush_cache_all(); - /* The Swift branch folding logic is completely broken. At - * trap time, if things are just right, if can mistakenly - * think that a trap is coming from kernel mode when in fact - * it is coming from user mode (it mis-executes the branch in - * the trap code). So you see things like crashme completely - * hosing your machine which is completely unacceptable. Turn - * this shit off... nice job Fujitsu. - */ - mreg &= ~(SWIFT_BF); + /* Enable I & D caches */ + mreg = srmmu_get_mmureg(); + mreg |= (SWIFT_IE | SWIFT_DE); srmmu_set_mmureg(mreg); } @@ -2444,18 +2406,24 @@ BTFIXUPSET_CALL(flush_cache_page, swift_flush_cache_page, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(flush_cache_range, swift_flush_cache_range, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(flush_chunk, swift_flush_chunk, BTFIXUPCALL_NOP); /* local flush _only_ */ + BTFIXUPSET_CALL(flush_chunk, swift_flush_chunk, BTFIXUPCALL_NORM); /* local flush _only_ */ BTFIXUPSET_CALL(flush_tlb_all, swift_flush_tlb_all, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(flush_tlb_mm, swift_flush_tlb_mm, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(flush_tlb_page, swift_flush_tlb_page, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(flush_tlb_range, swift_flush_tlb_range, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(flush_page_to_ram, swift_flush_page_to_ram, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(flush_page_to_ram, swift_flush_page_to_ram, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(flush_sig_insns, swift_flush_sig_insns, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(flush_page_for_dma, swift_flush_page_for_dma, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(update_mmu_cache, swift_update_mmu_cache, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(update_mmu_cache, srmmu_update_mmu_cache, BTFIXUPCALL_NOP); + + BTFIXUPSET_CALL(sparc_update_rootmmu_dir, swift_update_rootmmu_dir, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(set_pte, srmmu_set_pte_nocache_swift, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(init_new_context, swift_init_new_context, BTFIXUPCALL_NORM); + + flush_page_for_dma_global = 0; /* Are you now convinced that the Swift is one of the * biggest VLSI abortions of all time? Bravo Fujitsu! @@ -2613,7 +2581,7 @@ BTFIXUPSET_CALL(flush_chunk, turbosparc_flush_chunk, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(flush_sig_insns, turbosparc_flush_sig_insns, BTFIXUPCALL_NOP); - BTFIXUPSET_CALL(flush_page_for_dma, turbosparc_flush_page_for_dma, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(flush_page_for_dma, turbosparc_flush_page_for_dma, BTFIXUPCALL_NORM); poke_srmmu = poke_turbosparc; } @@ -2644,7 +2612,7 @@ BTFIXUPSET_CALL(flush_cache_page, tsunami_flush_cache_page, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(flush_cache_range, tsunami_flush_cache_range, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(flush_chunk, tsunami_flush_chunk, BTFIXUPCALL_NOP); /* local flush _only_ */ + BTFIXUPSET_CALL(flush_chunk, tsunami_flush_chunk, BTFIXUPCALL_NORM); /* local flush _only_ */ BTFIXUPSET_CALL(flush_tlb_all, tsunami_flush_tlb_all, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(flush_tlb_mm, tsunami_flush_tlb_mm, BTFIXUPCALL_NORM); @@ -2656,6 +2624,8 @@ BTFIXUPSET_CALL(flush_page_for_dma, tsunami_flush_page_for_dma, BTFIXUPCALL_NORM); poke_srmmu = poke_tsunami; + + tsunami_setup_blockops(); } __initfunc(static void poke_viking(void)) @@ -2738,8 +2708,7 @@ * which we use the IOMMU. */ BTFIXUPSET_CALL(flush_page_for_dma, viking_flush_page, BTFIXUPCALL_NORM); - /* Also, this is so far the only chip which actually uses - the page argument to flush_page_for_dma */ + flush_page_for_dma_global = 0; } else { srmmu_name = "TI Viking/MXCC"; diff -u --recursive --new-file v2.2.13/linux/arch/sparc/mm/sun4c.c linux/arch/sparc/mm/sun4c.c --- v2.2.13/linux/arch/sparc/mm/sun4c.c Tue Jan 4 11:10:32 2000 +++ linux/arch/sparc/mm/sun4c.c Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: sun4c.c,v 1.173.2.1 1999/09/08 00:32:02 davem Exp $ +/* $Id: sun4c.c,v 1.173.2.5 1999/10/11 08:24:44 davem Exp $ * sun4c.c: Doing in software what should be done in hardware. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -27,42 +27,19 @@ #include #include -/* TODO: Make it such that interrupt handlers cannot dick with - * the user segment lists, most of the cli/sti pairs can - * disappear once that is taken care of. - */ - -/* XXX Ok the real performance win, I figure, will be to use a combined hashing - * XXX and bitmap scheme to keep track of what we have mapped where. The whole - * XXX incentive is to make it such that the range flushes can be serviced - * XXX always in near constant time. --DaveM +/* Because of our dynamic kernel TLB miss strategy, and how + * our DVMA mapping allocation works, you _MUST_: + * + * 1) Disable interrupts _and_ not touch any dynamic kernel + * memory while messing with kernel MMU state. By + * dynamic memory I mean any object which is not in + * the kernel image itself or a task_struct (both of + * which are locked into the MMU). + * 2) Disable interrupts while messing with user MMU state. */ extern int num_segmaps, num_contexts; -/* Define this to get extremely anal debugging, undefine for performance. */ -/* #define DEBUG_SUN4C_MM */ - -#define UWINMASK_OFFSET (const unsigned long)(&(((struct task_struct *)0)->tss.uwinmask)) - -/* This is used in many routines below. */ -#define FUW_INLINE do { \ - register int ctr asm("g5"); \ - ctr = 0; \ - __asm__ __volatile__("\n" \ - "1: ld [%%g6 + %2], %%g4 ! flush user windows\n" \ - " orcc %%g0, %%g4, %%g0\n" \ - " add %0, 1, %0\n" \ - " bne 1b\n" \ - " save %%sp, -64, %%sp\n" \ - "2: subcc %0, 1, %0\n" \ - " bne 2b\n" \ - " restore %%g0, %%g0, %%g0\n" \ - : "=&r" (ctr) \ - : "0" (ctr), "i" (UWINMASK_OFFSET) \ - : "g4", "cc"); \ -} while(0); - #ifdef CONFIG_SUN4 #define SUN4C_VAC_SIZE sun4c_vacinfo.num_bytes #else @@ -82,58 +59,21 @@ #define MIN(a,b) ((a)<(b)?(a):(b)) #endif - -#define KGPROF_PROFILING 0 -#if KGPROF_PROFILING -#define KGPROF_DEPTH 3 /* this needs to match the code below */ -#define KGPROF_SIZE 100 -static struct { - unsigned addr[KGPROF_DEPTH]; - unsigned count; -} kgprof_counters[KGPROF_SIZE]; - -/* just call this function from whatever function you think needs it then - look at /proc/cpuinfo to see where the function is being called from - and how often. This gives a type of "kernel gprof" */ -#define NEXT_PROF(prev,lvl) (prev>PAGE_OFFSET?__builtin_return_address(lvl):0) -static inline void kgprof_profile(void) -{ - unsigned ret[KGPROF_DEPTH]; - int i,j; - /* you can't use a variable argument to __builtin_return_address() */ - ret[0] = (unsigned)__builtin_return_address(0); - ret[1] = (unsigned)NEXT_PROF(ret[0],1); - ret[2] = (unsigned)NEXT_PROF(ret[1],2); - - for (i=0;iid_machtype) { + switch (idprom->id_machtype) { case (SM_SUN4|SM_4_110): sun4c_vacinfo.type = NONE; @@ -477,12 +362,12 @@ default: prom_printf("Cannot initialize VAC - wierd sun4 model idprom->id_machtype = %d", idprom->id_machtype); prom_halt(); - } + }; } else { sun4c_vacinfo.type = WRITE_THROUGH; - if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) || - (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) { + if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) || + (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) { /* PROM on SS1 lacks this info, to be super safe we * hard code it here since this arch is cast in stone. */ @@ -497,7 +382,7 @@ sun4c_vacinfo.do_hwflushes = prom_getintdefault(prom_root_node, "vac-hwflush", 0); - if(sun4c_vacinfo.do_hwflushes == 0) + if (sun4c_vacinfo.do_hwflushes == 0) sun4c_vacinfo.do_hwflushes = prom_getintdefault(prom_root_node, "vac_hwflush", 0); @@ -509,7 +394,7 @@ sun4c_vacinfo.num_lines = (sun4c_vacinfo.num_bytes / sun4c_vacinfo.linesize); - switch(sun4c_vacinfo.linesize) { + switch (sun4c_vacinfo.linesize) { case 16: sun4c_vacinfo.log2lsize = 4; break; @@ -566,7 +451,7 @@ prom_printf("Unhandled number of segmaps: %d\n", num_segmaps); prom_halt(); - } + }; switch (num_contexts) { case 8: /* Default, nothing to do. */ @@ -574,19 +459,22 @@ case 16: PATCH_INSN(num_context_patch1_16, num_context_patch1); +#if 0 PATCH_INSN(num_context_patch2_16, num_context_patch2); +#endif break; default: prom_printf("Unhandled number of contexts: %d\n", num_contexts); prom_halt(); - } - if(sun4c_vacinfo.do_hwflushes != 0) { + }; + + if (sun4c_vacinfo.do_hwflushes != 0) { PATCH_INSN(vac_hwflush_patch1_on, vac_hwflush_patch1); PATCH_INSN(vac_hwflush_patch2_on, vac_hwflush_patch2); } else { - switch(sun4c_vacinfo.linesize) { + switch (sun4c_vacinfo.linesize) { case 16: /* Default, nothing to do. */ break; @@ -604,7 +492,7 @@ __initfunc(static void sun4c_probe_mmu(void)) { if (ARCH_SUN4) { - switch(idprom->id_machtype) { + switch (idprom->id_machtype) { case (SM_SUN4|SM_4_110): prom_printf("No support for 4100 yet\n"); prom_halt(); @@ -631,10 +519,10 @@ default: prom_printf("Invalid SUN4 model\n"); prom_halt(); - } + }; } else { - if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) || - (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) { + if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) || + (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) { /* Hardcode these just to be safe, PROM on SS1 does * not have this info available in the root node. */ @@ -679,10 +567,10 @@ { extern unsigned long start; - if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS2)) || - (idprom->id_machtype == (SM_SUN4C | SM_4C_IPX)) || - (idprom->id_machtype == (SM_SUN4 | SM_4_330)) || - (idprom->id_machtype == (SM_SUN4C | SM_4C_ELC))) { + if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS2)) || + (idprom->id_machtype == (SM_SUN4C | SM_4C_IPX)) || + (idprom->id_machtype == (SM_SUN4 | SM_4_330)) || + (idprom->id_machtype == (SM_SUN4C | SM_4C_ELC))) { /* Whee.. */ printk("SS2 cache bug detected, uncaching trap table page\n"); sun4c_flush_page((unsigned int) &start); @@ -697,9 +585,9 @@ unsigned long page, end; end = PAGE_ALIGN((addr + len)); - while(addr < end) { + while (addr < end) { page = get_free_page(GFP_KERNEL); - if(!page) { + if (!page) { prom_printf("alloc_dvma: Cannot get a dvma page\n"); prom_halt(); } @@ -726,6 +614,13 @@ unsigned long vaddr; unsigned char pseg; unsigned char locked; + + /* For user mappings only, and completely hidden from kernel + * TLB miss code. + */ + unsigned char ctx; + struct sun4c_mmu_entry *lru_next; + struct sun4c_mmu_entry *lru_prev; }; static struct sun4c_mmu_entry mmu_entry_pool[SUN4C_MAX_SEGMAPS]; @@ -734,12 +629,15 @@ { int i; - for(i=0; i < SUN4C_MAX_SEGMAPS; i++) { + for (i = 0; i < SUN4C_MAX_SEGMAPS; i++) { mmu_entry_pool[i].pseg = i; mmu_entry_pool[i].next = 0; mmu_entry_pool[i].prev = 0; mmu_entry_pool[i].vaddr = 0; mmu_entry_pool[i].locked = 0; + mmu_entry_pool[i].ctx = 0; + mmu_entry_pool[i].lru_next = 0; + mmu_entry_pool[i].lru_prev = 0; } mmu_entry_pool[invalid_segment].locked = 1; } @@ -750,8 +648,8 @@ unsigned long start, end; end = vaddr + SUN4C_REAL_PGDIR_SIZE; - for(start = vaddr; start < end; start += PAGE_SIZE) - if(sun4c_get_pte(start) & _SUN4C_PAGE_VALID) + for (start = vaddr; start < end; start += PAGE_SIZE) + if (sun4c_get_pte(start) & _SUN4C_PAGE_VALID) sun4c_put_pte(start, (sun4c_get_pte(start) | bits_on) & ~bits_off); } @@ -762,16 +660,16 @@ unsigned char pseg, ctx; #ifdef CONFIG_SUN4 /* sun4/110 and 260 have no kadb. */ - if((idprom->id_machtype != (SM_SUN4 | SM_4_260)) && - (idprom->id_machtype != (SM_SUN4 | SM_4_110))) { + if ((idprom->id_machtype != (SM_SUN4 | SM_4_260)) && + (idprom->id_machtype != (SM_SUN4 | SM_4_110))) { #endif - for(vaddr = KADB_DEBUGGER_BEGVM; - vaddr < LINUX_OPPROM_ENDVM; - vaddr += SUN4C_REAL_PGDIR_SIZE) { + for (vaddr = KADB_DEBUGGER_BEGVM; + vaddr < LINUX_OPPROM_ENDVM; + vaddr += SUN4C_REAL_PGDIR_SIZE) { pseg = sun4c_get_segmap(vaddr); - if(pseg != invalid_segment) { + if (pseg != invalid_segment) { mmu_entry_pool[pseg].locked = 1; - for(ctx = 0; ctx < num_contexts; ctx++) + for (ctx = 0; ctx < num_contexts; ctx++) prom_putsegment(ctx, vaddr, pseg); fix_permissions(vaddr, _SUN4C_PAGE_PRIV, 0); } @@ -779,10 +677,10 @@ #ifdef CONFIG_SUN4 } #endif - for(vaddr = KERNBASE; vaddr < kernel_end; vaddr += SUN4C_REAL_PGDIR_SIZE) { + for (vaddr = KERNBASE; vaddr < kernel_end; vaddr += SUN4C_REAL_PGDIR_SIZE) { pseg = sun4c_get_segmap(vaddr); mmu_entry_pool[pseg].locked = 1; - for(ctx = 0; ctx < num_contexts; ctx++) + for (ctx = 0; ctx < num_contexts; ctx++) prom_putsegment(ctx, vaddr, pseg); fix_permissions(vaddr, _SUN4C_PAGE_PRIV, _SUN4C_PAGE_NOCACHE); } @@ -792,13 +690,13 @@ { int i, ctx; - while(start < end) { - for(i=0; i < invalid_segment; i++) - if(!mmu_entry_pool[i].locked) + while (start < end) { + for (i = 0; i < invalid_segment; i++) + if (!mmu_entry_pool[i].locked) break; mmu_entry_pool[i].locked = 1; sun4c_init_clean_segmap(i); - for(ctx = 0; ctx < num_contexts; ctx++) + for (ctx = 0; ctx < num_contexts; ctx++) prom_putsegment(ctx, start, mmu_entry_pool[i].pseg); start += SUN4C_REAL_PGDIR_SIZE; } @@ -815,13 +713,15 @@ static struct sun4c_mmu_ring sun4c_context_ring[SUN4C_MAX_CONTEXTS]; /* used user entries */ static struct sun4c_mmu_ring sun4c_ufree_ring; /* free user entries */ +static struct sun4c_mmu_ring sun4c_ulru_ring; /* LRU user entries */ struct sun4c_mmu_ring sun4c_kernel_ring; /* used kernel entries */ struct sun4c_mmu_ring sun4c_kfree_ring; /* free kernel entries */ static inline void sun4c_init_rings(unsigned long *mempool) { int i; - for(i=0; iringhd; @@ -849,49 +752,58 @@ ring->num_entries++; } -static inline void add_ring_ordered(struct sun4c_mmu_ring *ring, - struct sun4c_mmu_entry *entry) +static __inline__ void add_lru(struct sun4c_mmu_entry *entry) +{ + struct sun4c_mmu_ring *ring = &sun4c_ulru_ring; + struct sun4c_mmu_entry *head = &ring->ringhd; + + entry->lru_next = head; + (entry->lru_prev = head->lru_prev)->lru_next = entry; + head->lru_prev = entry; +} + +static void add_ring_ordered(struct sun4c_mmu_ring *ring, + struct sun4c_mmu_entry *entry) { struct sun4c_mmu_entry *head = &ring->ringhd; unsigned long addr = entry->vaddr; - if(head->next != &ring->ringhd) { - while((head->next != &ring->ringhd) && (head->next->vaddr < addr)) - head = head->next; - } + while ((head->next != &ring->ringhd) && (head->next->vaddr < addr)) + head = head->next; + entry->prev = head; (entry->next = head->next)->prev = entry; head->next = entry; ring->num_entries++; + + add_lru(entry); } -static inline void remove_ring(struct sun4c_mmu_ring *ring, - struct sun4c_mmu_entry *entry) +static __inline__ void remove_ring(struct sun4c_mmu_ring *ring, + struct sun4c_mmu_entry *entry) { struct sun4c_mmu_entry *next = entry->next; (next->prev = entry->prev)->next = next; ring->num_entries--; -#ifdef DEBUG_SUN4C_MM - if(ring->num_entries < 0) - panic("sun4c: Ring num_entries < 0!"); -#endif } -static inline void free_user_entry(int ctx, struct sun4c_mmu_entry *entry) +static void remove_lru(struct sun4c_mmu_entry *entry) { - remove_ring(sun4c_context_ring+ctx, entry); - add_ring(&sun4c_ufree_ring, entry); + struct sun4c_mmu_entry *next = entry->lru_next; + + (next->lru_prev = entry->lru_prev)->lru_next = next; } -static inline void assign_user_entry(int ctx, struct sun4c_mmu_entry *entry) +static void free_user_entry(int ctx, struct sun4c_mmu_entry *entry) { - remove_ring(&sun4c_ufree_ring, entry); - add_ring_ordered(sun4c_context_ring+ctx, entry); + remove_ring(sun4c_context_ring+ctx, entry); + remove_lru(entry); + add_ring(&sun4c_ufree_ring, entry); } -static inline void free_kernel_entry(struct sun4c_mmu_entry *entry, - struct sun4c_mmu_ring *ring) +static void free_kernel_entry(struct sun4c_mmu_entry *entry, + struct sun4c_mmu_ring *ring) { remove_ring(ring, entry); add_ring(&sun4c_kfree_ring, entry); @@ -901,9 +813,9 @@ { int i; - while(howmany) { - for(i=0; i < invalid_segment; i++) - if(!mmu_entry_pool[i].locked) + while (howmany) { + for (i = 0; i < invalid_segment; i++) + if (!mmu_entry_pool[i].locked) break; mmu_entry_pool[i].locked = 1; sun4c_init_clean_segmap(i); @@ -916,54 +828,40 @@ { int i; - for(i=0; i < invalid_segment; i++) { - if(mmu_entry_pool[i].locked) + for (i = 0; i < invalid_segment; i++) { + if (mmu_entry_pool[i].locked) continue; sun4c_init_clean_segmap(i); add_ring(&sun4c_ufree_ring, &mmu_entry_pool[i]); } } -static inline void sun4c_kernel_unmap(struct sun4c_mmu_entry *kentry) +static void sun4c_kernel_unmap(struct sun4c_mmu_entry *kentry) { int savectx, ctx; savectx = sun4c_get_context(); - for(ctx = 0; ctx < num_contexts; ctx++) { + for (ctx = 0; ctx < num_contexts; ctx++) { sun4c_set_context(ctx); sun4c_put_segmap(kentry->vaddr, invalid_segment); } sun4c_set_context(savectx); } -static inline void sun4c_kernel_map(struct sun4c_mmu_entry *kentry) +static void sun4c_kernel_map(struct sun4c_mmu_entry *kentry) { int savectx, ctx; savectx = sun4c_get_context(); - for(ctx = 0; ctx < num_contexts; ctx++) { + for (ctx = 0; ctx < num_contexts; ctx++) { sun4c_set_context(ctx); sun4c_put_segmap(kentry->vaddr, kentry->pseg); } sun4c_set_context(savectx); } -static inline void sun4c_user_unmap(struct sun4c_mmu_entry *uentry) -{ - sun4c_put_segmap(uentry->vaddr, invalid_segment); -} - -static inline void sun4c_user_map(struct sun4c_mmu_entry *uentry) -{ - unsigned long start = uentry->vaddr; - unsigned long end = start + SUN4C_REAL_PGDIR_SIZE; - - sun4c_put_segmap(uentry->vaddr, uentry->pseg); - while(start < end) { - sun4c_put_pte(start, 0); - start += PAGE_SIZE; - } -} +#define sun4c_user_unmap(__entry) \ + sun4c_put_segmap((__entry)->vaddr, invalid_segment) static void sun4c_demap_context_hw(struct sun4c_mmu_ring *crp, unsigned char ctx) { @@ -971,11 +869,11 @@ unsigned long flags; save_and_cli(flags); - if(head->next != head) { + if (head->next != head) { struct sun4c_mmu_entry *entry = head->next; int savectx = sun4c_get_context(); - FUW_INLINE + flush_user_windows(); sun4c_set_context(ctx); sun4c_flush_context_hw(); do { @@ -985,7 +883,7 @@ free_user_entry(ctx, entry); entry = next; - } while(entry != head); + } while (entry != head); sun4c_set_context(savectx); } restore_flags(flags); @@ -997,11 +895,11 @@ unsigned long flags; save_and_cli(flags); - if(head->next != head) { + if (head->next != head) { struct sun4c_mmu_entry *entry = head->next; int savectx = sun4c_get_context(); - FUW_INLINE + flush_user_windows(); sun4c_set_context(ctx); sun4c_flush_context_sw(); do { @@ -1011,49 +909,31 @@ free_user_entry(ctx, entry); entry = next; - } while(entry != head); + } while (entry != head); sun4c_set_context(savectx); } restore_flags(flags); } -static inline void sun4c_demap_one(struct sun4c_mmu_ring *crp, unsigned char ctx) -{ - /* by using .prev we get a kind of "lru" algorithm */ - struct sun4c_mmu_entry *entry = crp->ringhd.prev; - unsigned long flags; - int savectx = sun4c_get_context(); - -#ifdef DEBUG_SUN4C_MM - if(entry == &crp->ringhd) - panic("sun4c_demap_one: Freeing from empty ctx ring."); -#endif - FUW_INLINE - save_and_cli(flags); - sun4c_set_context(ctx); - sun4c_flush_segment(entry->vaddr); - sun4c_user_unmap(entry); - free_user_entry(ctx, entry); - sun4c_set_context(savectx); - restore_flags(flags); -} - static int sun4c_user_taken_entries = 0; /* This is how much we have. */ static int max_user_taken_entries = 0; /* This limits us and prevents deadlock. */ -static inline struct sun4c_mmu_entry *sun4c_kernel_strategy(void) +static struct sun4c_mmu_entry *sun4c_kernel_strategy(void) { struct sun4c_mmu_entry *this_entry; /* If some are free, return first one. */ - if(sun4c_kfree_ring.num_entries) { + if (sun4c_kfree_ring.num_entries) { this_entry = sun4c_kfree_ring.ringhd.next; return this_entry; } /* Else free one up. */ this_entry = sun4c_kernel_ring.ringhd.prev; - sun4c_flush_segment(this_entry->vaddr); + if (sun4c_vacinfo.do_hwflushes) + sun4c_flush_segment_hw(this_entry->vaddr); + else + sun4c_flush_segment_sw(this_entry->vaddr); sun4c_kernel_unmap(this_entry); free_kernel_entry(this_entry, &sun4c_kernel_ring); this_entry = sun4c_kfree_ring.ringhd.next; @@ -1061,141 +941,73 @@ return this_entry; } -void sun4c_shrink_kernel_ring(void) -{ - struct sun4c_mmu_entry *entry; - unsigned long flags; - - /* If an interrupt comes in here, we die... */ - save_and_cli(flags); - - if (sun4c_user_taken_entries) { - entry = sun4c_kernel_strategy(); - remove_ring(&sun4c_kfree_ring, entry); - add_ring(&sun4c_ufree_ring, entry); - sun4c_user_taken_entries--; -#if 0 - printk("shrink: ufree= %d, kfree= %d, kernel= %d\n", - sun4c_ufree_ring.num_entries, - sun4c_kfree_ring.num_entries, - sun4c_kernel_ring.num_entries); -#endif -#ifdef DEBUG_SUN4C_MM - if(sun4c_user_taken_entries < 0) - panic("sun4c_shrink_kernel_ring: taken < 0."); -#endif - } - restore_flags(flags); -} - /* Using this method to free up mmu entries eliminates a lot of * potential races since we have a kernel that incurs tlb * replacement faults. There may be performance penalties. + * + * NOTE: Must be called with interrupts disabled. */ -static inline struct sun4c_mmu_entry *sun4c_user_strategy(void) +static struct sun4c_mmu_entry *sun4c_user_strategy(void) { - struct ctx_list *next_one; - struct sun4c_mmu_ring *rp = 0; + struct sun4c_mmu_entry *entry; unsigned char ctx; -#ifdef DEBUG_SUN4C_MM - int lim = num_contexts; -#endif + int savectx; /* If some are free, return first one. */ - if(sun4c_ufree_ring.num_entries) { -#ifdef DEBUG_SUN4C_MM - if(sun4c_ufree_ring.ringhd.next == &sun4c_ufree_ring.ringhd) - panic("sun4c_user_strategy: num_entries!=0 but ring empty."); -#endif - return sun4c_ufree_ring.ringhd.next; + if (sun4c_ufree_ring.num_entries) { + entry = sun4c_ufree_ring.ringhd.next; + goto unlink_out; } if (sun4c_user_taken_entries) { - sun4c_shrink_kernel_ring(); -#ifdef DEBUG_SUN4C_MM - if(sun4c_ufree_ring.ringhd.next == &sun4c_ufree_ring.ringhd) - panic("sun4c_user_strategy: kernel shrunk but ufree empty."); -#endif - return sun4c_ufree_ring.ringhd.next; + entry = sun4c_kernel_strategy(); + sun4c_user_taken_entries--; + goto kunlink_out; } - /* Grab one from the LRU context. */ - next_one = ctx_used.next; - while ((sun4c_context_ring[next_one->ctx_number].num_entries == 0) -#ifdef DEBUG_SUN4C_MM - && (--lim >= 0) -#endif - ) - next_one = next_one->next; + /* Grab from the beginning of the LRU list. */ + entry = sun4c_ulru_ring.ringhd.lru_next; + ctx = entry->ctx; -#ifdef DEBUG_SUN4C_MM - if(lim < 0) - panic("No user segmaps!"); -#endif + savectx = sun4c_get_context(); + flush_user_windows(); + sun4c_set_context(ctx); + if (sun4c_vacinfo.do_hwflushes) + sun4c_flush_segment_hw(entry->vaddr); + else + sun4c_flush_segment_sw(entry->vaddr); + sun4c_user_unmap(entry); + remove_ring(sun4c_context_ring + ctx, entry); + remove_lru(entry); + sun4c_set_context(savectx); - ctx = next_one->ctx_number; - rp = &sun4c_context_ring[ctx]; + return entry; - sun4c_demap_one(rp, ctx); -#ifdef DEBUG_SUN4C_MM - if(sun4c_ufree_ring.ringhd.next == &sun4c_ufree_ring.ringhd) - panic("sun4c_user_strategy: demapped one but ufree empty."); -#endif - return sun4c_ufree_ring.ringhd.next; +unlink_out: + remove_ring(&sun4c_ufree_ring, entry); + return entry; +kunlink_out: + remove_ring(&sun4c_kfree_ring, entry); + return entry; } +/* NOTE: Must be called with interrupts disabled. */ void sun4c_grow_kernel_ring(void) { struct sun4c_mmu_entry *entry; -#if 0 - printk("grow: "); -#endif - /* Prevent deadlock condition. */ - if(sun4c_user_taken_entries >= max_user_taken_entries) { -#if 0 - printk("deadlock avoidance, taken= %d max= %d\n", - sun4c_user_taken_entries, max_user_taken_entries); -#endif + if (sun4c_user_taken_entries >= max_user_taken_entries) return; - } if (sun4c_ufree_ring.num_entries) { entry = sun4c_ufree_ring.ringhd.next; -#ifdef DEBUG_SUN4C_MM - if(entry == &sun4c_ufree_ring.ringhd) - panic("\nsun4c_grow_kernel_ring: num_entries!=0, ring empty."); -#endif remove_ring(&sun4c_ufree_ring, entry); add_ring(&sun4c_kfree_ring, entry); -#ifdef DEBUG_SUN4C_MM - if(sun4c_user_taken_entries < 0) - panic("\nsun4c_grow_kernel_ring: taken < 0."); -#endif sun4c_user_taken_entries++; -#if 0 - printk("ufree= %d, kfree= %d, kernel= %d\n", - sun4c_ufree_ring.num_entries, - sun4c_kfree_ring.num_entries, - sun4c_kernel_ring.num_entries); -#endif } } -static inline void alloc_user_segment(unsigned long address, unsigned char ctx) -{ - struct sun4c_mmu_entry *entry; - unsigned long flags; - - save_and_cli(flags); - entry = sun4c_user_strategy(); - entry->vaddr = (address & SUN4C_REAL_PGDIR_MASK); - assign_user_entry(ctx, entry); - sun4c_user_map(entry); - restore_flags(flags); -} - /* This is now a fast in-window trap handler to avoid any and all races. */ static void sun4c_quick_kernel_fault(unsigned long address) { @@ -1234,7 +1046,7 @@ #define BUCKET_PTE_PAGE(pte) \ (PAGE_OFFSET + (((pte) & SUN4C_PFN_MASK) << PAGE_SHIFT)) -static inline void get_locked_segment(unsigned long addr) +static void get_locked_segment(unsigned long addr) { struct sun4c_mmu_entry *stolen; unsigned long flags; @@ -1242,19 +1054,14 @@ save_and_cli(flags); addr &= SUN4C_REAL_PGDIR_MASK; stolen = sun4c_user_strategy(); - remove_ring(&sun4c_ufree_ring, stolen); max_user_taken_entries--; -#ifdef DEBUG_SUN4C_MM - if(max_user_taken_entries < 0) - panic("get_locked_segment: max_user_taken < 0."); -#endif stolen->vaddr = addr; - FUW_INLINE + flush_user_windows(); sun4c_kernel_map(stolen); restore_flags(flags); } -static inline void free_locked_segment(unsigned long addr) +static void free_locked_segment(unsigned long addr) { struct sun4c_mmu_entry *entry; unsigned long flags; @@ -1265,14 +1072,13 @@ pseg = sun4c_get_segmap(addr); entry = &mmu_entry_pool[pseg]; - FUW_INLINE - sun4c_flush_segment(addr); + flush_user_windows(); + if (sun4c_vacinfo.do_hwflushes) + sun4c_flush_segment_hw(addr); + else + sun4c_flush_segment_sw(addr); sun4c_kernel_unmap(entry); add_ring(&sun4c_ufree_ring, entry); -#ifdef DEBUG_SUN4C_MM - if(max_user_taken_entries < 0) - panic("free_locked_segment: max_user_taken < 0."); -#endif max_user_taken_entries++; restore_flags(flags); } @@ -1284,8 +1090,8 @@ /* 32 buckets per segment... */ entry &= ~31; start = entry; - for(end = (start + 32); start < end; start++) - if(sun4c_bucket[start] != BUCKET_EMPTY) + for (end = (start + 32); start < end; start++) + if (sun4c_bucket[start] != BUCKET_EMPTY) return; /* Entire segment empty, release it. */ @@ -1304,23 +1110,39 @@ int entry; pages = __get_free_pages(GFP_KERNEL, TASK_STRUCT_ORDER); - if(!pages) + if (!pages) return (struct task_struct *) 0; - for(entry = sun4c_lowbucket_avail; entry < NR_TASK_BUCKETS; entry++) - if(sun4c_bucket[entry] == BUCKET_EMPTY) + for (entry = sun4c_lowbucket_avail; entry < NR_TASK_BUCKETS; entry++) + if (sun4c_bucket[entry] == BUCKET_EMPTY) break; - if(entry == NR_TASK_BUCKETS) { + if (entry == NR_TASK_BUCKETS) { free_pages(pages, TASK_STRUCT_ORDER); return (struct task_struct *) 0; } - if(entry >= sun4c_lowbucket_avail) + if (entry >= sun4c_lowbucket_avail) sun4c_lowbucket_avail = entry + 1; addr = BUCKET_ADDR(entry); sun4c_bucket[entry] = (union task_union *) addr; - if(sun4c_get_segmap(addr) == invalid_segment) + if (sun4c_get_segmap(addr) == invalid_segment) get_locked_segment(addr); + + /* We are changing the virtual color of the page(s) + * so we must flush the cache to guarentee consistancy. + */ + if (sun4c_vacinfo.do_hwflushes) { + sun4c_flush_page_hw(pages); +#ifndef CONFIG_SUN4 + sun4c_flush_page_hw(pages + PAGE_SIZE); +#endif + } else { + sun4c_flush_page_sw(pages); +#ifndef CONFIG_SUN4 + sun4c_flush_page_sw(pages + PAGE_SIZE); +#endif + } + sun4c_put_pte(addr, BUCKET_PTE(pages)); #ifndef CONFIG_SUN4 sun4c_put_pte(addr + PAGE_SIZE, BUCKET_PTE(pages + PAGE_SIZE)); @@ -1344,7 +1166,7 @@ sun4c_put_pte(tsaddr + PAGE_SIZE, 0); #endif sun4c_bucket[entry] = BUCKET_EMPTY; - if(entry < sun4c_lowbucket_avail) + if (entry < sun4c_lowbucket_avail) sun4c_lowbucket_avail = entry; free_pages(pages, TASK_STRUCT_ORDER); @@ -1367,7 +1189,7 @@ sun4c_put_pte(tsaddr + PAGE_SIZE, 0); #endif sun4c_bucket[entry] = BUCKET_EMPTY; - if(entry < sun4c_lowbucket_avail) + if (entry < sun4c_lowbucket_avail) sun4c_lowbucket_avail = entry; free_pages(pages, TASK_STRUCT_ORDER); @@ -1378,10 +1200,10 @@ { int entry; - if(sizeof(union task_union) != (PAGE_SIZE << TASK_STRUCT_ORDER)) { + if (sizeof(union task_union) != (PAGE_SIZE << TASK_STRUCT_ORDER)) { prom_printf("task union not %d page(s)!\n", 1 << TASK_STRUCT_ORDER); } - for(entry = 0; entry < NR_TASK_BUCKETS; entry++) + for (entry = 0; entry < NR_TASK_BUCKETS; entry++) sun4c_bucket[entry] = BUCKET_EMPTY; sun4c_lowbucket_avail = 0; } @@ -1501,7 +1323,7 @@ unsigned long page; page = ((unsigned long)bufptr) & PAGE_MASK; - if(MAP_NR(page) > max_mapnr) { + if (MAP_NR(page) > max_mapnr) { sun4c_flush_page(page); return (__u32)bufptr; /* already locked */ } @@ -1510,7 +1332,7 @@ static void sun4c_get_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus) { - while(sz >= 0) { + while (sz >= 0) { sg[sz].dvma_addr = (__u32)sun4c_lockarea(sg[sz].addr, sg[sz].len); sz--; } @@ -1518,14 +1340,14 @@ static void sun4c_release_scsi_one(__u32 bufptr, unsigned long len, struct linux_sbus *sbus) { - if(bufptr < sun4c_iobuffer_start) + if (bufptr < sun4c_iobuffer_start) return; /* On kernel stack or similar, see above */ sun4c_unlockarea((char *)bufptr, len); } static void sun4c_release_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus) { - while(sz >= 0) { + while (sz >= 0) { sun4c_unlockarea((char *)sg[sz].dvma_addr, sg[sz].len); sz--; } @@ -1546,7 +1368,7 @@ sun4c_taskstack_start = SUN4C_LOCK_VADDR; sun4c_taskstack_end = (sun4c_taskstack_start + (TASK_ENTRY_SIZE * NR_TASK_BUCKETS)); - if(sun4c_taskstack_end >= SUN4C_LOCK_END) { + if (sun4c_taskstack_end >= SUN4C_LOCK_END) { prom_printf("Too many tasks, decrease NR_TASK_BUCKETS please.\n"); prom_halt(); } @@ -1576,12 +1398,12 @@ { unsigned long begin, end; - FUW_INLINE + flush_user_windows(); begin = (KERNBASE + SUN4C_REAL_PGDIR_SIZE); end = (begin + SUN4C_VAC_SIZE); - if(sun4c_vacinfo.linesize == 32) { - while(begin < end) { + if (sun4c_vacinfo.linesize == 32) { + while (begin < end) { __asm__ __volatile__(" ld [%0 + 0x00], %%g0 ld [%0 + 0x20], %%g0 @@ -1603,7 +1425,7 @@ begin += 512; } } else { - while(begin < end) { + while (begin < end) { __asm__ __volatile__(" ld [%0 + 0x00], %%g0 ld [%0 + 0x10], %%g0 @@ -1631,29 +1453,31 @@ { int new_ctx = mm->context; - if(new_ctx != NO_CONTEXT && sun4c_context_ring[new_ctx].num_entries) { - struct sun4c_mmu_entry *head = &sun4c_context_ring[new_ctx].ringhd; - unsigned long flags; + if (new_ctx != NO_CONTEXT) { + flush_user_windows(); + if (sun4c_context_ring[new_ctx].num_entries) { + struct sun4c_mmu_entry *head = &sun4c_context_ring[new_ctx].ringhd; + unsigned long flags; + + save_and_cli(flags); + if (head->next != head) { + struct sun4c_mmu_entry *entry = head->next; + int savectx = sun4c_get_context(); + + sun4c_set_context(new_ctx); + sun4c_flush_context_hw(); + do { + struct sun4c_mmu_entry *next = entry->next; - save_and_cli(flags); - if(head->next != head) { - struct sun4c_mmu_entry *entry = head->next; - int savectx = sun4c_get_context(); - - FUW_INLINE - sun4c_set_context(new_ctx); - sun4c_flush_context_hw(); - do { - struct sun4c_mmu_entry *next = entry->next; - - sun4c_user_unmap(entry); - free_user_entry(new_ctx, entry); + sun4c_user_unmap(entry); + free_user_entry(new_ctx, entry); - entry = next; - } while(entry != head); - sun4c_set_context(savectx); + entry = next; + } while (entry != head); + sun4c_set_context(savectx); + } + restore_flags(flags); } - restore_flags(flags); } } @@ -1661,29 +1485,28 @@ { int new_ctx = mm->context; -#if KGPROF_PROFILING - kgprof_profile(); -#endif - if(new_ctx != NO_CONTEXT) { + if (new_ctx != NO_CONTEXT) { struct sun4c_mmu_entry *head = &sun4c_context_ring[new_ctx].ringhd; struct sun4c_mmu_entry *entry; unsigned long flags; - FUW_INLINE + flush_user_windows(); + save_and_cli(flags); /* All user segmap chains are ordered on entry->vaddr. */ - for(entry = head->next; - (entry != head) && ((entry->vaddr+SUN4C_REAL_PGDIR_SIZE) < start); - entry = entry->next) + for (entry = head->next; + (entry != head) && ((entry->vaddr+SUN4C_REAL_PGDIR_SIZE) < start); + entry = entry->next) ; /* Tracing various job mixtures showed that this conditional * only passes ~35% of the time for most worse case situations, * therefore we avoid all of this gross overhead ~65% of the time. */ - if((entry != head) && (entry->vaddr < end)) { + if ((entry != head) && (entry->vaddr < end)) { int octx = sun4c_get_context(); + sun4c_set_context(new_ctx); /* At this point, always, (start >= entry->vaddr) and @@ -1698,11 +1521,11 @@ /* "realstart" is always >= entry->vaddr */ realend = entry->vaddr + SUN4C_REAL_PGDIR_SIZE; - if(end < realend) + if (end < realend) realend = end; - if((realend - entry->vaddr) <= (PAGE_SIZE << 3)) { + if ((realend - entry->vaddr) <= (PAGE_SIZE << 3)) { unsigned long page = entry->vaddr; - while(page < realend) { + while (page < realend) { sun4c_flush_page_hw(page); page += PAGE_SIZE; } @@ -1712,14 +1535,13 @@ free_user_entry(new_ctx, entry); } entry = next; - } while((entry != head) && (entry->vaddr < end)); + } while ((entry != head) && (entry->vaddr < end)); sun4c_set_context(octx); } restore_flags(flags); } } -/* XXX no save_and_cli/restore_flags needed, but put here if darkside still crashes */ static void sun4c_flush_cache_page_hw(struct vm_area_struct *vma, unsigned long page) { struct mm_struct *mm = vma->vm_mm; @@ -1728,76 +1550,84 @@ /* Sun4c has no separate I/D caches so cannot optimize for non * text page flushes. */ - if(new_ctx != NO_CONTEXT) { + if (new_ctx != NO_CONTEXT) { int octx = sun4c_get_context(); + unsigned long flags; - FUW_INLINE + flush_user_windows(); + save_and_cli(flags); sun4c_set_context(new_ctx); sun4c_flush_page_hw(page); sun4c_set_context(octx); + restore_flags(flags); } } static void sun4c_flush_page_to_ram_hw(unsigned long page) { + unsigned long flags; + + save_and_cli(flags); sun4c_flush_page_hw(page); + restore_flags(flags); } static void sun4c_flush_cache_mm_sw(struct mm_struct *mm) { int new_ctx = mm->context; - if(new_ctx != NO_CONTEXT && sun4c_context_ring[new_ctx].num_entries) { - struct sun4c_mmu_entry *head = &sun4c_context_ring[new_ctx].ringhd; - unsigned long flags; + if (new_ctx != NO_CONTEXT) { + flush_user_windows(); - save_and_cli(flags); - if(head->next != head) { - struct sun4c_mmu_entry *entry = head->next; - int savectx = sun4c_get_context(); - - FUW_INLINE - sun4c_set_context(new_ctx); - sun4c_flush_context_sw(); - do { - struct sun4c_mmu_entry *next = entry->next; + if (sun4c_context_ring[new_ctx].num_entries) { + struct sun4c_mmu_entry *head = &sun4c_context_ring[new_ctx].ringhd; + unsigned long flags; + + save_and_cli(flags); + if (head->next != head) { + struct sun4c_mmu_entry *entry = head->next; + int savectx = sun4c_get_context(); + + sun4c_set_context(new_ctx); + sun4c_flush_context_sw(); + do { + struct sun4c_mmu_entry *next = entry->next; - sun4c_user_unmap(entry); - free_user_entry(new_ctx, entry); + sun4c_user_unmap(entry); + free_user_entry(new_ctx, entry); - entry = next; - } while(entry != head); - sun4c_set_context(savectx); + entry = next; + } while (entry != head); + sun4c_set_context(savectx); + } + restore_flags(flags); } - restore_flags(flags); } } static void sun4c_flush_cache_range_sw(struct mm_struct *mm, unsigned long start, unsigned long end) { int new_ctx = mm->context; - -#if KGPROF_PROFILING - kgprof_profile(); -#endif - if(new_ctx != NO_CONTEXT) { + + if (new_ctx != NO_CONTEXT) { struct sun4c_mmu_entry *head = &sun4c_context_ring[new_ctx].ringhd; struct sun4c_mmu_entry *entry; unsigned long flags; - FUW_INLINE + flush_user_windows(); + save_and_cli(flags); /* All user segmap chains are ordered on entry->vaddr. */ - for(entry = head->next; - (entry != head) && ((entry->vaddr+SUN4C_REAL_PGDIR_SIZE) < start); - entry = entry->next) + for (entry = head->next; + (entry != head) && ((entry->vaddr+SUN4C_REAL_PGDIR_SIZE) < start); + entry = entry->next) ; /* Tracing various job mixtures showed that this conditional * only passes ~35% of the time for most worse case situations, * therefore we avoid all of this gross overhead ~65% of the time. */ - if((entry != head) && (entry->vaddr < end)) { + if ((entry != head) && (entry->vaddr < end)) { int octx = sun4c_get_context(); sun4c_set_context(new_ctx); @@ -1813,11 +1643,11 @@ /* "realstart" is always >= entry->vaddr */ realend = entry->vaddr + SUN4C_REAL_PGDIR_SIZE; - if(end < realend) + if (end < realend) realend = end; - if((realend - entry->vaddr) <= (PAGE_SIZE << 3)) { + if ((realend - entry->vaddr) <= (PAGE_SIZE << 3)) { unsigned long page = entry->vaddr; - while(page < realend) { + while (page < realend) { sun4c_flush_page_sw(page); page += PAGE_SIZE; } @@ -1827,7 +1657,7 @@ free_user_entry(new_ctx, entry); } entry = next; - } while((entry != head) && (entry->vaddr < end)); + } while ((entry != head) && (entry->vaddr < end)); sun4c_set_context(octx); } restore_flags(flags); @@ -1842,19 +1672,26 @@ /* Sun4c has no separate I/D caches so cannot optimize for non * text page flushes. */ - if(new_ctx != NO_CONTEXT) { + if (new_ctx != NO_CONTEXT) { int octx = sun4c_get_context(); + unsigned long flags; - FUW_INLINE + flush_user_windows(); + save_and_cli(flags); sun4c_set_context(new_ctx); sun4c_flush_page_sw(page); sun4c_set_context(octx); + restore_flags(flags); } } static void sun4c_flush_page_to_ram_sw(unsigned long page) { + unsigned long flags; + + save_and_cli(flags); sun4c_flush_page_sw(page); + restore_flags(flags); } /* Sun4c cache is unified, both instructions and data live there, so @@ -1881,8 +1718,11 @@ flush_user_windows(); while (sun4c_kernel_ring.num_entries) { next_entry = this_entry->next; - sun4c_flush_segment(this_entry->vaddr); - for(ctx = 0; ctx < num_contexts; ctx++) { + if (sun4c_vacinfo.do_hwflushes) + sun4c_flush_segment_hw(this_entry->vaddr); + else + sun4c_flush_segment_sw(this_entry->vaddr); + for (ctx = 0; ctx < num_contexts; ctx++) { sun4c_set_context(ctx); sun4c_put_segmap(this_entry->vaddr, invalid_segment); } @@ -1897,16 +1737,15 @@ { int new_ctx = mm->context; - if(new_ctx != NO_CONTEXT) { + if (new_ctx != NO_CONTEXT) { struct sun4c_mmu_entry *head = &sun4c_context_ring[new_ctx].ringhd; unsigned long flags; save_and_cli(flags); - if(head->next != head) { + if (head->next != head) { struct sun4c_mmu_entry *entry = head->next; int savectx = sun4c_get_context(); - FUW_INLINE sun4c_set_context(new_ctx); sun4c_flush_context_hw(); do { @@ -1916,7 +1755,7 @@ free_user_entry(new_ctx, entry); entry = next; - } while(entry != head); + } while (entry != head); sun4c_set_context(savectx); } restore_flags(flags); @@ -1927,26 +1766,21 @@ { int new_ctx = mm->context; - if(new_ctx != NO_CONTEXT) { + if (new_ctx != NO_CONTEXT) { struct sun4c_mmu_entry *head = &sun4c_context_ring[new_ctx].ringhd; struct sun4c_mmu_entry *entry; unsigned long flags; -#if KGPROF_PROFILING - kgprof_profile(); -#endif save_and_cli(flags); /* See commentary in sun4c_flush_cache_range_*(). */ - for(entry = head->next; - (entry != head) && ((entry->vaddr+SUN4C_REAL_PGDIR_SIZE) < start); - entry = entry->next) + for (entry = head->next; + (entry != head) && ((entry->vaddr+SUN4C_REAL_PGDIR_SIZE) < start); + entry = entry->next) ; - if((entry != head) && (entry->vaddr < end)) { + if ((entry != head) && (entry->vaddr < end)) { int octx = sun4c_get_context(); - /* This window flush is paranoid I think... -DaveM */ - FUW_INLINE sun4c_set_context(new_ctx); do { struct sun4c_mmu_entry *next = entry->next; @@ -1956,7 +1790,7 @@ free_user_entry(new_ctx, entry); entry = next; - } while((entry != head) && (entry->vaddr < end)); + } while ((entry != head) && (entry->vaddr < end)); sun4c_set_context(octx); } restore_flags(flags); @@ -1968,15 +1802,17 @@ struct mm_struct *mm = vma->vm_mm; int new_ctx = mm->context; - if(new_ctx != NO_CONTEXT) { + if (new_ctx != NO_CONTEXT) { int savectx = sun4c_get_context(); + unsigned long flags; - FUW_INLINE + save_and_cli(flags); sun4c_set_context(new_ctx); page &= PAGE_MASK; sun4c_flush_page_hw(page); sun4c_put_pte(page, 0); sun4c_set_context(savectx); + restore_flags(flags); } } @@ -1984,16 +1820,15 @@ { int new_ctx = mm->context; - if(new_ctx != NO_CONTEXT) { + if (new_ctx != NO_CONTEXT) { struct sun4c_mmu_entry *head = &sun4c_context_ring[new_ctx].ringhd; unsigned long flags; save_and_cli(flags); - if(head->next != head) { + if (head->next != head) { struct sun4c_mmu_entry *entry = head->next; int savectx = sun4c_get_context(); - FUW_INLINE sun4c_set_context(new_ctx); sun4c_flush_context_sw(); do { @@ -2003,7 +1838,7 @@ free_user_entry(new_ctx, entry); entry = next; - } while(entry != head); + } while (entry != head); sun4c_set_context(savectx); } restore_flags(flags); @@ -2014,27 +1849,21 @@ { int new_ctx = mm->context; - if(new_ctx != NO_CONTEXT) { + if (new_ctx != NO_CONTEXT) { struct sun4c_mmu_entry *head = &sun4c_context_ring[new_ctx].ringhd; struct sun4c_mmu_entry *entry; unsigned long flags; -#if KGPROF_PROFILING - kgprof_profile(); -#endif - save_and_cli(flags); /* See commentary in sun4c_flush_cache_range_*(). */ - for(entry = head->next; - (entry != head) && ((entry->vaddr+SUN4C_REAL_PGDIR_SIZE) < start); - entry = entry->next) + for (entry = head->next; + (entry != head) && ((entry->vaddr+SUN4C_REAL_PGDIR_SIZE) < start); + entry = entry->next) ; - if((entry != head) && (entry->vaddr < end)) { + if ((entry != head) && (entry->vaddr < end)) { int octx = sun4c_get_context(); - /* This window flush is paranoid I think... -DaveM */ - FUW_INLINE sun4c_set_context(new_ctx); do { struct sun4c_mmu_entry *next = entry->next; @@ -2044,7 +1873,7 @@ free_user_entry(new_ctx, entry); entry = next; - } while((entry != head) && (entry->vaddr < end)); + } while ((entry != head) && (entry->vaddr < end)); sun4c_set_context(octx); } restore_flags(flags); @@ -2056,15 +1885,17 @@ struct mm_struct *mm = vma->vm_mm; int new_ctx = mm->context; - if(new_ctx != NO_CONTEXT) { + if (new_ctx != NO_CONTEXT) { int savectx = sun4c_get_context(); + unsigned long flags; - FUW_INLINE + save_and_cli(flags); sun4c_set_context(new_ctx); page &= PAGE_MASK; sun4c_flush_page_sw(page); sun4c_put_pte(page, 0); sun4c_set_context(savectx); + restore_flags(flags); } } @@ -2077,7 +1908,6 @@ { } - void sun4c_mapioaddr(unsigned long physaddr, unsigned long virt_addr, int bus_type, int rdonly) { @@ -2085,7 +1915,7 @@ page_entry = ((physaddr >> PAGE_SHIFT) & SUN4C_PFN_MASK); page_entry |= ((pg_iobits | _SUN4C_PAGE_PRIV) & ~(_SUN4C_PAGE_PRESENT)); - if(rdonly) + if (rdonly) page_entry &= ~_SUN4C_WRITEABLE; sun4c_put_pte(virt_addr, page_entry); } @@ -2100,7 +1930,7 @@ struct ctx_list *ctxp; ctxp = ctx_free.next; - if(ctxp != &ctx_free) { + if (ctxp != &ctx_free) { remove_from_ctx_list(ctxp); add_to_used_ctxlist(ctxp); mm->context = ctxp->ctx_number; @@ -2108,26 +1938,22 @@ return; } ctxp = ctx_used.next; - if(ctxp->ctx_mm == current->mm) + if (ctxp->ctx_mm == current->mm) ctxp = ctxp->next; -#ifdef DEBUG_SUN4C_MM - if(ctxp == &ctx_used) - panic("out of mmu contexts"); -#endif remove_from_ctx_list(ctxp); add_to_used_ctxlist(ctxp); ctxp->ctx_mm->context = NO_CONTEXT; ctxp->ctx_mm = mm; mm->context = ctxp->ctx_number; sun4c_demap_context_hw(&sun4c_context_ring[ctxp->ctx_number], - ctxp->ctx_number); + ctxp->ctx_number); } static void sun4c_switch_to_context_hw(struct task_struct *tsk) { struct ctx_list *ctx; - if(tsk->mm->context == NO_CONTEXT) { + if (tsk->mm->context == NO_CONTEXT) { sun4c_alloc_context_hw(tsk->mm); } else { /* Update the LRU ring of contexts. */ @@ -2141,7 +1967,7 @@ static void sun4c_init_new_context_hw(struct mm_struct *mm) { sun4c_alloc_context_hw(mm); - if(mm == current->mm) + if (mm == current->mm) sun4c_set_context(mm->context); } @@ -2149,7 +1975,7 @@ { struct ctx_list *ctx_old; - if(mm->context != NO_CONTEXT && atomic_read(&mm->count) == 1) { + if (mm->context != NO_CONTEXT && atomic_read(&mm->count) == 1) { sun4c_demap_context_hw(&sun4c_context_ring[mm->context], mm->context); ctx_old = ctx_list_pool + mm->context; remove_from_ctx_list(ctx_old); @@ -2163,7 +1989,7 @@ struct ctx_list *ctxp; ctxp = ctx_free.next; - if(ctxp != &ctx_free) { + if (ctxp != &ctx_free) { remove_from_ctx_list(ctxp); add_to_used_ctxlist(ctxp); mm->context = ctxp->ctx_number; @@ -2171,26 +1997,22 @@ return; } ctxp = ctx_used.next; - if(ctxp->ctx_mm == current->mm) + if (ctxp->ctx_mm == current->mm) ctxp = ctxp->next; -#ifdef DEBUG_SUN4C_MM - if(ctxp == &ctx_used) - panic("out of mmu contexts"); -#endif remove_from_ctx_list(ctxp); add_to_used_ctxlist(ctxp); ctxp->ctx_mm->context = NO_CONTEXT; ctxp->ctx_mm = mm; mm->context = ctxp->ctx_number; sun4c_demap_context_sw(&sun4c_context_ring[ctxp->ctx_number], - ctxp->ctx_number); + ctxp->ctx_number); } static void sun4c_switch_to_context_sw(struct task_struct *tsk) { struct ctx_list *ctx; - if(tsk->mm->context == NO_CONTEXT) { + if (tsk->mm->context == NO_CONTEXT) { sun4c_alloc_context_sw(tsk->mm); } else { /* Update the LRU ring of contexts. */ @@ -2204,7 +2026,7 @@ static void sun4c_init_new_context_sw(struct mm_struct *mm) { sun4c_alloc_context_sw(mm); - if(mm == current->mm) + if (mm == current->mm) sun4c_set_context(mm->context); } @@ -2212,7 +2034,7 @@ { struct ctx_list *ctx_old; - if(mm->context != NO_CONTEXT && atomic_read(&mm->count) == 1) { + if (mm->context != NO_CONTEXT && atomic_read(&mm->count) == 1) { sun4c_demap_context_sw(&sun4c_context_ring[mm->context], mm->context); ctx_old = ctx_list_pool + mm->context; remove_from_ctx_list(ctx_old); @@ -2227,7 +2049,7 @@ int len; used_user_entries = 0; - for(i=0; i < num_contexts; i++) + for (i = 0; i < num_contexts; i++) used_user_entries += sun4c_context_ring[i].num_entries; len = sprintf(buf, @@ -2241,10 +2063,7 @@ "usedpsegs\t: %d\n" "ufreepsegs\t: %d\n" "user_taken\t: %d\n" - "max_taken\t: %d\n" - "context\t\t: %d flushes\n" - "segment\t\t: %d flushes\n" - "page\t\t: %d flushes\n", + "max_taken\t: %d\n", sun4c_vacinfo.num_bytes, (sun4c_vacinfo.do_hwflushes ? "yes" : "no"), sun4c_vacinfo.linesize, @@ -2255,22 +2074,7 @@ used_user_entries, sun4c_ufree_ring.num_entries, sun4c_user_taken_entries, - max_user_taken_entries, - ctxflushes, segflushes, pageflushes); - -#if KGPROF_PROFILING - { - int i,j; - len += sprintf(buf + len,"kgprof profiling:\n"); - for (i=0;i> PAGE_SHIFT) | pgprot_val(pgprot)); } -#if 0 /* Not used due to BTFIXUPs */ -static pte_t sun4c_pte_modify(pte_t pte, pgprot_t newprot) -{ - return __pte((pte_val(pte) & _SUN4C_PAGE_CHG_MASK) | - pgprot_val(newprot)); -} -#endif - static unsigned long sun4c_pte_page(pte_t pte) { return (PAGE_OFFSET + ((pte_val(pte) & SUN4C_PFN_MASK) << (PAGE_SHIFT))); @@ -2489,7 +2225,7 @@ static pte_t *sun4c_pte_alloc_kernel(pmd_t *pmd, unsigned long address) { - if(address >= SUN4C_LOCK_VADDR) + if (address >= SUN4C_LOCK_VADDR) return NULL; address = (address >> PAGE_SHIFT) & (SUN4C_PTRS_PER_PTE - 1); if (sun4c_pmd_none(*pmd)) @@ -2529,7 +2265,7 @@ { unsigned long *ret; - if((ret = pgd_quicklist) != NULL) { + if ((ret = pgd_quicklist) != NULL) { pgd_quicklist = (unsigned long *)(*ret); ret[0] = ret[1]; pgtable_cache_size--; @@ -2548,15 +2284,15 @@ static int sun4c_check_pgt_cache(int low, int high) { int freed = 0; - if(pgtable_cache_size > high) { + if (pgtable_cache_size > high) { do { - if(pgd_quicklist) + if (pgd_quicklist) free_pgd_slow(get_pgd_fast()), freed++; - if(pmd_quicklist) + if (pmd_quicklist) free_pmd_slow(get_pmd_fast()), freed++; - if(pte_quicklist) + if (pte_quicklist) free_pte_slow(get_pte_fast()), freed++; - } while(pgtable_cache_size > low); + } while (pgtable_cache_size > low); } return freed; } @@ -2577,7 +2313,7 @@ { unsigned long *ret; - if((ret = (unsigned long *)pte_quicklist) != NULL) { + if ((ret = (unsigned long *)pte_quicklist) != NULL) { pte_quicklist = (unsigned long *)(*ret); ret[0] = ret[1]; pgtable_cache_size--; @@ -2680,9 +2416,9 @@ if (vma->vm_file) dentry = vma->vm_file->f_dentry; - if(dentry) + if (dentry) inode = dentry->d_inode; - if(inode) { + if (inode) { unsigned long offset = (address & PAGE_MASK) - vma->vm_start; struct vm_area_struct *vmaring = inode->i_mmap; int alias_found = 0; @@ -2691,19 +2427,21 @@ unsigned long start; /* Do not mistake ourselves as another mapping. */ - if(vmaring == vma) + if (vmaring == vma) continue; if (S4CVAC_BADALIAS(vaddr, address)) { alias_found++; start = vmaring->vm_start; - while(start < vmaring->vm_end) { + while (start < vmaring->vm_end) { pgdp = sun4c_pgd_offset(vmaring->vm_mm, start); - if(!pgdp) goto next; + if (!pgdp) + goto next; ptep = sun4c_pte_offset((pmd_t *) pgdp, start); - if(!ptep) goto next; + if (!ptep) + goto next; - if(pte_val(*ptep) & _SUN4C_PAGE_PRESENT) { + if (pte_val(*ptep) & _SUN4C_PAGE_PRESENT) { flush_cache_page(vmaring, start); *ptep = __pte(pte_val(*ptep) | _SUN4C_PAGE_NOCACHE); @@ -2715,7 +2453,7 @@ } } while ((vmaring = vmaring->vm_next_share) != NULL); - if(alias_found && !(pte_val(pte) & _SUN4C_PAGE_NOCACHE)) { + if (alias_found && !(pte_val(pte) & _SUN4C_PAGE_NOCACHE)) { pgdp = sun4c_pgd_offset(vma->vm_mm, address); ptep = sun4c_pte_offset((pmd_t *) pgdp, address); *ptep = __pte(pte_val(*ptep) | _SUN4C_PAGE_NOCACHE); @@ -2724,16 +2462,62 @@ } } +/* An experiment, turn off by default for now... -DaveM */ +#define SUN4C_PRELOAD_PSEG + void sun4c_update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte) { unsigned long flags; + int pseg; save_and_cli(flags); address &= PAGE_MASK; - if(sun4c_get_segmap(address) == invalid_segment) - alloc_user_segment(address, sun4c_get_context()); + if ((pseg = sun4c_get_segmap(address)) == invalid_segment) { + struct sun4c_mmu_entry *entry = sun4c_user_strategy(); + struct mm_struct *mm = vma->vm_mm; + unsigned long start, end; + + entry->vaddr = start = (address & SUN4C_REAL_PGDIR_MASK); + entry->ctx = mm->context; + add_ring_ordered(sun4c_context_ring + mm->context, entry); + sun4c_put_segmap(entry->vaddr, entry->pseg); + end = start + SUN4C_REAL_PGDIR_SIZE; + while (start < end) { +#ifdef SUN4C_PRELOAD_PSEG + pgd_t *pgdp = sun4c_pgd_offset(mm, start); + pte_t *ptep; + + if (!pgdp) + goto no_mapping; + ptep = sun4c_pte_offset((pmd_t *) pgdp, start); + if (!ptep || !(pte_val(*ptep) & _SUN4C_PAGE_PRESENT)) + goto no_mapping; + sun4c_put_pte(start, pte_val(*ptep)); + goto next; + + no_mapping: +#endif + sun4c_put_pte(start, 0); +#ifdef SUN4C_PRELOAD_PSEG + next: +#endif + start += PAGE_SIZE; + } + if ((vma->vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED)) + sun4c_vac_alias_fixup(vma, address, pte); +#ifndef SUN4C_PRELOAD_PSEG + sun4c_put_pte(address, pte_val(pte)); +#endif + restore_flags(flags); + return; + } else { + struct sun4c_mmu_entry *entry = &mmu_entry_pool[pseg]; + + remove_lru(entry); + add_lru(entry); + } - if((vma->vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED)) + if ((vma->vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED)) sun4c_vac_alias_fixup(vma, address, pte); sun4c_put_pte(address, pte_val(pte)); @@ -2786,8 +2570,8 @@ start_mem = sparc_context_init(start_mem, num_contexts); start_mem = free_area_init(start_mem, end_mem); cnt = 0; - for(i = 0; i < num_segmaps; i++) - if(mmu_entry_pool[i].locked) + for (i = 0; i < num_segmaps; i++) + if (mmu_entry_pool[i].locked) cnt++; max_user_taken_entries = num_segmaps - cnt - 40 - 1; @@ -2839,7 +2623,7 @@ BTFIXUPSET_CALL(flush_cache_all, sun4c_flush_cache_all, BTFIXUPCALL_NORM); - if(sun4c_vacinfo.do_hwflushes) { + if (sun4c_vacinfo.do_hwflushes) { BTFIXUPSET_CALL(flush_cache_mm, sun4c_flush_cache_mm_hw, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(flush_cache_range, sun4c_flush_cache_range_hw, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(flush_cache_page, sun4c_flush_cache_page_hw, BTFIXUPCALL_NORM); diff -u --recursive --new-file v2.2.13/linux/arch/sparc/mm/swift.S linux/arch/sparc/mm/swift.S --- v2.2.13/linux/arch/sparc/mm/swift.S Wed Dec 31 16:00:00 1969 +++ linux/arch/sparc/mm/swift.S Tue Jan 4 10:12:13 2000 @@ -0,0 +1,274 @@ +/* $Id: swift.S,v 1.1.2.3 1999/10/14 01:00:17 davem Exp $ + * swift.S: MicroSparc-II mmu/cache operations. + * + * Copyright (C) 1999 David S. Miller (davem@redhat.com) + */ + +#include +#include +#include +#include +#include + +#define WINDOW_FLUSH(tmp1, tmp2) \ + mov 0, tmp1; \ +98: ld [%g6 + AOFF_task_tss + AOFF_thread_uwinmask], tmp2; \ + orcc %g0, tmp2, %g0; \ + add tmp1, 1, tmp1; \ + bne 98b; \ + save %sp, -64, %sp; \ +99: subcc tmp1, 1, tmp1; \ + bne 99b; \ + restore %g0, %g0, %g0; + + .text + .align 4 + +#if 1 /* XXX screw this, I can't get the VAC flushes working + * XXX reliably... -DaveM + */ + .globl swift_flush_cache_all, swift_flush_cache_mm + .globl swift_flush_cache_range, swift_flush_cache_page + .globl swift_flush_page_for_dma, swift_flush_chunk + .globl swift_flush_page_to_ram + +swift_flush_cache_all: +swift_flush_cache_mm: +swift_flush_cache_range: +swift_flush_cache_page: +swift_flush_page_for_dma: +swift_flush_chunk: +swift_flush_page_to_ram: + sethi %hi(0x2000), %o0 +1: subcc %o0, 0x10, %o0 + sta %g0, [%o0] ASI_M_TXTC_TAG + sta %g0, [%o0] ASI_M_DATAC_TAG + bne 1b + nop + retl + nop +#else + + .globl swift_flush_cache_all +swift_flush_cache_all: + WINDOW_FLUSH(%g4, %g5) + + /* Just clear out all the tags. */ + sethi %hi(16 * 1024), %o0 +1: subcc %o0, 16, %o0 + sta %g0, [%o0] ASI_M_TXTC_TAG + bne 1b + sta %g0, [%o0] ASI_M_DATAC_TAG + retl + nop + + .globl swift_flush_cache_mm +swift_flush_cache_mm: +#ifndef __SMP__ + ld [%o0 + AOFF_mm_context], %g2 + cmp %g2, -1 + be swift_flush_cache_mm_out +#endif + WINDOW_FLUSH(%g4, %g5) + rd %psr, %g1 + andn %g1, PSR_ET, %g3 + wr %g3, 0x0, %psr + nop + nop + mov SRMMU_CTX_REG, %g7 + lda [%g7] ASI_M_MMUREGS, %g5 + sta %g2, [%g7] ASI_M_MMUREGS + +#if 1 + sethi %hi(0x2000), %o0 +1: subcc %o0, 0x10, %o0 + sta %g0, [%o0] ASI_M_FLUSH_CTX + bne 1b + nop +#else + clr %o0 + or %g0, 2048, %g7 + or %g0, 2048, %o1 + add %o1, 2048, %o2 + add %o2, 2048, %o3 + mov 16, %o4 + add %o4, 2048, %o5 + add %o5, 2048, %g2 + add %g2, 2048, %g3 +1: sta %g0, [%o0 ] ASI_M_FLUSH_CTX + sta %g0, [%o0 + %o1] ASI_M_FLUSH_CTX + sta %g0, [%o0 + %o2] ASI_M_FLUSH_CTX + sta %g0, [%o0 + %o3] ASI_M_FLUSH_CTX + sta %g0, [%o0 + %o4] ASI_M_FLUSH_CTX + sta %g0, [%o0 + %o5] ASI_M_FLUSH_CTX + sta %g0, [%o0 + %g2] ASI_M_FLUSH_CTX + sta %g0, [%o0 + %g3] ASI_M_FLUSH_CTX + subcc %g7, 32, %g7 + bne 1b + add %o0, 32, %o0 +#endif + + mov SRMMU_CTX_REG, %g7 + sta %g5, [%g7] ASI_M_MMUREGS + wr %g1, 0x0, %psr + nop + nop +swift_flush_cache_mm_out: + retl + nop + + .globl swift_flush_cache_range +swift_flush_cache_range: + sub %o2, %o1, %o2 + sethi %hi(4096), %o3 + cmp %o2, %o3 + bgu swift_flush_cache_mm + nop + b 70f + nop + + .globl swift_flush_cache_page +swift_flush_cache_page: + ld [%o0 + 0x0], %o0 /* XXX vma->vm_mm, GROSS XXX */ +70: +#ifndef __SMP__ + ld [%o0 + AOFF_mm_context], %g2 + cmp %g2, -1 + be swift_flush_cache_page_out +#endif + WINDOW_FLUSH(%g4, %g5) + rd %psr, %g1 + andn %g1, PSR_ET, %g3 + wr %g3, 0x0, %psr + nop + nop + mov SRMMU_CTX_REG, %g7 + lda [%g7] ASI_M_MMUREGS, %g5 + sta %g2, [%g7] ASI_M_MMUREGS + + andn %o1, (PAGE_SIZE - 1), %o1 +#if 1 + sethi %hi(0x1000), %o0 +1: subcc %o0, 0x10, %o0 + sta %g0, [%o1 + %o0] ASI_M_FLUSH_PAGE + bne 1b + nop +#else + or %g0, 512, %g7 + or %g0, 512, %o0 + add %o0, 512, %o2 + add %o2, 512, %o3 + add %o3, 512, %o4 + add %o4, 512, %o5 + add %o5, 512, %g3 + add %g3, 512, %g4 +1: sta %g0, [%o1 ] ASI_M_FLUSH_PAGE + sta %g0, [%o1 + %o0] ASI_M_FLUSH_PAGE + sta %g0, [%o1 + %o2] ASI_M_FLUSH_PAGE + sta %g0, [%o1 + %o3] ASI_M_FLUSH_PAGE + sta %g0, [%o1 + %o4] ASI_M_FLUSH_PAGE + sta %g0, [%o1 + %o5] ASI_M_FLUSH_PAGE + sta %g0, [%o1 + %g3] ASI_M_FLUSH_PAGE + sta %g0, [%o1 + %g4] ASI_M_FLUSH_PAGE + subcc %g7, 16, %g7 + bne 1b + add %o1, 16, %o1 +#endif + + mov SRMMU_CTX_REG, %g7 + sta %g5, [%g7] ASI_M_MMUREGS + wr %g1, 0x0, %psr + nop + nop +swift_flush_cache_page_out: + retl + nop + + /* Swift is write-thru, however it is not + * I/O nor TLB-walk coherent. Also it has + * caches which are virtually indexed and tagged. + */ + .globl swift_flush_page_for_dma + .globl swift_flush_chunk + .globl swift_flush_page_to_ram +swift_flush_page_for_dma: +swift_flush_chunk: +swift_flush_page_to_ram: + andn %o0, (PAGE_SIZE - 1), %o1 +#if 1 + sethi %hi(0x1000), %o0 +1: subcc %o0, 0x10, %o0 + sta %g0, [%o1 + %o0] ASI_M_FLUSH_PAGE + bne 1b + nop +#else + or %g0, 512, %g7 + or %g0, 512, %o0 + add %o0, 512, %o2 + add %o2, 512, %o3 + add %o3, 512, %o4 + add %o4, 512, %o5 + add %o5, 512, %g3 + add %g3, 512, %g4 +1: sta %g0, [%o1 ] ASI_M_FLUSH_PAGE + sta %g0, [%o1 + %o0] ASI_M_FLUSH_PAGE + sta %g0, [%o1 + %o2] ASI_M_FLUSH_PAGE + sta %g0, [%o1 + %o3] ASI_M_FLUSH_PAGE + sta %g0, [%o1 + %o4] ASI_M_FLUSH_PAGE + sta %g0, [%o1 + %o5] ASI_M_FLUSH_PAGE + sta %g0, [%o1 + %g3] ASI_M_FLUSH_PAGE + sta %g0, [%o1 + %g4] ASI_M_FLUSH_PAGE + subcc %g7, 16, %g7 + bne 1b + add %o1, 16, %o1 +#endif + retl + nop +#endif + + .globl swift_flush_sig_insns +swift_flush_sig_insns: + flush %o1 + retl + flush %o1 + 4 + + .globl swift_flush_tlb_mm + .globl swift_flush_tlb_range + .globl swift_flush_tlb_all +swift_flush_tlb_mm: +swift_flush_tlb_range: +#ifndef __SMP__ + ld [%o0 + AOFF_mm_context], %g2 + cmp %g2, -1 + be swift_flush_tlb_all_out +#endif +swift_flush_tlb_all: + mov 0x400, %o1 + sta %g0, [%o1] ASI_M_FLUSH_PROBE +swift_flush_tlb_all_out: + retl + nop + + .globl swift_flush_tlb_page +swift_flush_tlb_page: + ld [%o0 + 0x00], %o0 /* XXX vma->vm_mm GROSS XXX */ + mov SRMMU_CTX_REG, %g1 + ld [%o0 + AOFF_mm_context], %o3 + andn %o1, (PAGE_SIZE - 1), %o1 +#ifndef __SMP__ + cmp %o3, -1 + be swift_flush_tlb_page_out + nop +#endif +#if 1 + mov 0x400, %o1 + sta %g0, [%o1] ASI_M_FLUSH_PROBE +#else + lda [%g1] ASI_M_MMUREGS, %g5 + sta %o3, [%g1] ASI_M_MMUREGS + sta %g0, [%o1] ASI_M_FLUSH_PROBE + sta %g5, [%g1] ASI_M_MMUREGS +#endif +swift_flush_tlb_page_out: + retl + nop diff -u --recursive --new-file v2.2.13/linux/arch/sparc/mm/tsunami.S linux/arch/sparc/mm/tsunami.S --- v2.2.13/linux/arch/sparc/mm/tsunami.S Mon Aug 9 16:05:55 1999 +++ linux/arch/sparc/mm/tsunami.S Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: tsunami.S,v 1.1.6.1 1999/08/09 13:00:16 davem Exp $ +/* $Id: tsunami.S,v 1.1.6.3 1999/10/06 15:36:38 davem Exp $ * tsunami.S: High speed MicroSparc-I mmu/cache operations. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -44,11 +44,11 @@ tsunami_flush_cache_all: WINDOW_FLUSH(%g4, %g5) tsunami_flush_page_for_dma: - sta %g0, [%g0] ASI_M_DC_FLCLEAR sta %g0, [%g0] ASI_M_IC_FLCLEAR +tsunami_flush_chunk: + sta %g0, [%g0] ASI_M_DC_FLCLEAR tsunami_flush_cache_out: tsunami_flush_page_to_ram: -tsunami_flush_chunk: retl nop @@ -98,3 +98,51 @@ tsunami_flush_tlb_page_out: retl sta %g5, [%g1] ASI_M_MMUREGS + +#define MIRROR_BLOCK(dst, src, offset, t0, t1, t2, t3) \ + ldd [src + offset + 0x18], t0; \ + std t0, [dst + offset + 0x18]; \ + ldd [src + offset + 0x10], t2; \ + std t2, [dst + offset + 0x10]; \ + ldd [src + offset + 0x08], t0; \ + std t0, [dst + offset + 0x08]; \ + ldd [src + offset + 0x00], t2; \ + std t2, [dst + offset + 0x00]; + + .globl tsunami_copy_1page +tsunami_copy_1page: +/* NOTE: This routine has to be shorter than 70insns --jj */ + or %g0, (PAGE_SIZE >> 8), %g1 +1: + MIRROR_BLOCK(%o0, %o1, 0x00, %o2, %o3, %o4, %o5) + MIRROR_BLOCK(%o0, %o1, 0x20, %o2, %o3, %o4, %o5) + MIRROR_BLOCK(%o0, %o1, 0x40, %o2, %o3, %o4, %o5) + MIRROR_BLOCK(%o0, %o1, 0x60, %o2, %o3, %o4, %o5) + MIRROR_BLOCK(%o0, %o1, 0x80, %o2, %o3, %o4, %o5) + MIRROR_BLOCK(%o0, %o1, 0xa0, %o2, %o3, %o4, %o5) + MIRROR_BLOCK(%o0, %o1, 0xc0, %o2, %o3, %o4, %o5) + MIRROR_BLOCK(%o0, %o1, 0xe0, %o2, %o3, %o4, %o5) + subcc %g1, 1, %g1 + add %o0, 0x100, %o0 + bne 1b + add %o1, 0x100, %o1 + + .globl tsunami_setup_blockops +tsunami_setup_blockops: + sethi %hi(__copy_1page), %o0 + or %o0, %lo(__copy_1page), %o0 + sethi %hi(tsunami_copy_1page), %o1 + or %o1, %lo(tsunami_copy_1page), %o1 + sethi %hi(tsunami_setup_blockops), %o2 + or %o2, %lo(tsunami_setup_blockops), %o2 + ld [%o1], %o4 +1: add %o1, 4, %o1 + st %o4, [%o0] + add %o0, 4, %o0 + cmp %o1, %o2 + bne 1b + ld [%o1], %o4 + sta %g0, [%g0] ASI_M_IC_FLCLEAR + sta %g0, [%g0] ASI_M_DC_FLCLEAR + retl + nop diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/config.in linux/arch/sparc64/config.in --- v2.2.13/linux/arch/sparc64/config.in Tue Jan 4 11:10:32 2000 +++ linux/arch/sparc64/config.in Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -# $Id: config.in,v 1.67.2.2 1999/09/22 11:37:42 jj Exp $ +# $Id: config.in,v 1.67.2.5 1999/10/19 16:49:37 davem Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # @@ -169,7 +169,7 @@ bool ' Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS N int ' Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 5 fi - dep_tristate 'NCR53C8XX SCSI support' CONFIG_SCSI_NCR53C8XX $CONFIG_SCSI + dep_tristate 'SYM53C8XX SCSI support' CONFIG_SCSI_SYM53C8XX $CONFIG_SCSI if [ "$CONFIG_SCSI_NCR53C8XX" != "n" ]; then int ' default tagged command queue depth' CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS 8 int ' maximum number of queued commands' CONFIG_SCSI_NCR53C8XX_MAX_TAGS 32 diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/defconfig linux/arch/sparc64/defconfig --- v2.2.13/linux/arch/sparc64/defconfig Tue Jan 4 11:10:32 2000 +++ linux/arch/sparc64/defconfig Tue Jan 4 10:12:13 2000 @@ -27,7 +27,9 @@ CONFIG_PROM_CONSOLE=y CONFIG_FB=y CONFIG_DUMMY_CONSOLE=y -# CONFIG_FB_PM2 is not set +CONFIG_FB_PM2=y +# CONFIG_FB_PM2_FIFO_DISCONNECT is not set +CONFIG_FB_PM2_PCI=y # CONFIG_FB_MATROX is not set CONFIG_FB_ATY=y CONFIG_FB_SBUS=y @@ -200,10 +202,10 @@ CONFIG_SCSI_QLOGICPTI=m CONFIG_SCSI_AIC7XXX=y # CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT is not set -# CONFIG_CMDS_PER_DEVICE is not set +CONFIG_AIC7XXX_CMDS_PER_DEVICE=8 CONFIG_AIC7XXX_PROC_STATS=y CONFIG_AIC7XXX_RESET_DELAY=5 -CONFIG_SCSI_NCR53C8XX=y +CONFIG_SCSI_SYM53C8XX=y CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=4 CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32 CONFIG_SCSI_NCR53C8XX_SYNC=10 diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/kernel/dtlb_backend.S linux/arch/sparc64/kernel/dtlb_backend.S --- v2.2.13/linux/arch/sparc64/kernel/dtlb_backend.S Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/kernel/dtlb_backend.S Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: dtlb_backend.S,v 1.7 1998/12/16 04:33:28 davem Exp $ +/* $Id: dtlb_backend.S,v 1.7.2.1 1999/12/05 10:41:57 davem Exp $ * dtlb_backend.S: Back end to DTLB miss replacement strategy. * This is included directly into the trap table. * @@ -10,7 +10,7 @@ #define VPTE_SHIFT (PAGE_SHIFT - 3) #define PMD_SHIFT (23 - PAGE_SHIFT + 3) #define PGD_SHIFT (34 - PAGE_SHIFT + 3) -#define VPTE_BITS (_PAGE_CP | _PAGE_P | _PAGE_W) +#define VPTE_BITS (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W) /* Ways we can get here: * diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/kernel/ebus.c linux/arch/sparc64/kernel/ebus.c --- v2.2.13/linux/arch/sparc64/kernel/ebus.c Tue Jan 4 11:10:32 2000 +++ linux/arch/sparc64/kernel/ebus.c Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: ebus.c,v 1.36.2.3 1999/09/21 15:45:37 davem Exp $ +/* $Id: ebus.c,v 1.36.2.4 1999/11/08 23:25:45 davem Exp $ * ebus.c: PCI to EBus bridge device. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -56,15 +56,15 @@ return mem; } -__initfunc(void ebus_intmap_match(struct linux_ebus *ebus, - struct linux_prom_registers *reg, - int *interrupt)) +__initfunc(int ebus_intmap_match(struct linux_ebus *ebus, + struct linux_prom_registers *reg, + int *interrupt)) { unsigned int hi, lo, irq; int i; if (!ebus->num_ebus_intmap) - return; + return 0; hi = reg->which_io & ebus->ebus_intmask.phys_hi; lo = reg->phys_addr & ebus->ebus_intmask.phys_lo; @@ -74,13 +74,10 @@ (ebus->ebus_intmap[i].phys_lo == lo) && (ebus->ebus_intmap[i].interrupt == irq)) { *interrupt = ebus->ebus_intmap[i].cinterrupt; - return; + return 0; } } - - prom_printf("ebus: IRQ [%08x.%08x.%08x] not found in interrupt-map\n", - reg->which_io, reg->phys_addr, *interrupt); - prom_halt(); + return -1; } __initfunc(void fill_ebus_child(int node, struct linux_prom_registers *preg, @@ -132,9 +129,16 @@ } else { dev->num_irqs = len / sizeof(irqs[0]); for (i = 0; i < dev->num_irqs; i++) { - ebus_intmap_match(dev->bus, preg, &irqs[i]); - dev->irqs[i] = psycho_irq_build(dev->bus->parent, - dev->bus->self, irqs[i]); + if (ebus_intmap_match(dev->bus, preg, &irqs[i]) != -1) { + dev->irqs[i] = psycho_irq_build(dev->bus->parent, + dev->bus->self, + irqs[i]); + } else { + /* If we get a bogus interrupt property, just + * record the raw value instead of punting. + */ + dev->irqs[i] = irqs[i]; + } } } @@ -186,9 +190,16 @@ } else { dev->num_irqs = len / sizeof(irqs[0]); for (i = 0; i < dev->num_irqs; i++) { - ebus_intmap_match(dev->bus, ®s[0], &irqs[i]); - dev->irqs[i] = psycho_irq_build(dev->bus->parent, - dev->bus->self, irqs[i]); + if (ebus_intmap_match(dev->bus, ®s[0], &irqs[i]) != -1) { + dev->irqs[i] = psycho_irq_build(dev->bus->parent, + dev->bus->self, + irqs[i]); + } else { + /* If we get a bogus interrupt property, just + * record the raw value instead of punting. + */ + dev->irqs[i] = irqs[i]; + } } } diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/kernel/entry.S linux/arch/sparc64/kernel/entry.S --- v2.2.13/linux/arch/sparc64/kernel/entry.S Tue Jan 4 11:10:32 2000 +++ linux/arch/sparc64/kernel/entry.S Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: entry.S,v 1.103.2.2 1999/09/22 11:37:37 jj Exp $ +/* $Id: entry.S,v 1.103.2.4 1999/10/24 17:29:13 davem Exp $ * arch/sparc64/kernel/entry.S: Sparc64 trap low-level entry points. * * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) @@ -214,8 +214,8 @@ .align 32 .globl do_ivec do_ivec: - wr %g0, ASI_UDB_INTR_R, %asi - ldxa [%g0 + 0x40] %asi, %g3 + mov 0x40, %g3 + ldxa [%g3 + %g0] ASI_UDB_INTR_R, %g3 sethi %hi(KERNBASE), %g4 cmp %g3, %g4 bgeu,pn %xcc, do_ivec_xcall @@ -234,22 +234,25 @@ sllx %g2, %g4, %g2 sllx %g4, 2, %g4 - lduw [%g1 + %g4], %g5 /* g5 = irq_work(cpu, pil) */ + lduw [%g6 + %g4], %g5 /* g5 = irq_work(cpu, pil) */ stw %g5, [%g3 + 0x00] /* bucket->irq_chain = g5 */ - stw %g3, [%g1 + %g4] /* irq_work(cpu, pil) = bucket */ + stw %g3, [%g6 + %g4] /* irq_work(cpu, pil) = bucket */ wr %g2, 0x0, %set_softint retry do_ivec_xcall: - ldxa [%g0 + 0x50] %asi, %g6 + mov 0x50, %g1 + ldxa [%g1 + %g0] ASI_UDB_INTR_R, %g1 srl %g3, 0, %g3 - ldxa [%g0 + 0x60] %asi, %g7 + mov 0x60, %g7 + ldxa [%g7 + %g0] ASI_UDB_INTR_R, %g7 stxa %g0, [%g0] ASI_INTR_RECEIVE membar #Sync jmpl %g3, %g0 nop + do_ivec_spurious: - stw %g3, [%g1 + 0x00] /* irq_work(cpu, 0) = bucket */ + stw %g3, [%g6 + 0x00] /* irq_work(cpu, 0) = bucket */ rdpr %pstate, %g5 wrpr %g5, PSTATE_IG | PSTATE_AG, %pstate @@ -260,6 +263,76 @@ add %sp, STACK_BIAS + REGWIN_SZ, %o0 ba,pt %xcc, rtrap clr %l6 + + .globl save_alternate_globals +save_alternate_globals: /* %o0 = save_area */ + rdpr %pstate, %o5 + andn %o5, PSTATE_IE, %o1 + wrpr %o1, PSTATE_AG, %pstate + stx %g0, [%o0 + 0x00] + stx %g1, [%o0 + 0x08] + stx %g2, [%o0 + 0x10] + stx %g3, [%o0 + 0x18] + stx %g4, [%o0 + 0x20] + stx %g5, [%o0 + 0x28] + stx %g6, [%o0 + 0x30] + stx %g7, [%o0 + 0x38] + wrpr %o1, PSTATE_IG, %pstate + stx %g0, [%o0 + 0x40] + stx %g1, [%o0 + 0x48] + stx %g2, [%o0 + 0x50] + stx %g3, [%o0 + 0x58] + stx %g4, [%o0 + 0x60] + stx %g5, [%o0 + 0x68] + stx %g6, [%o0 + 0x70] + stx %g7, [%o0 + 0x78] + wrpr %o1, PSTATE_MG, %pstate + stx %g0, [%o0 + 0x80] + stx %g1, [%o0 + 0x88] + stx %g2, [%o0 + 0x90] + stx %g3, [%o0 + 0x98] + stx %g4, [%o0 + 0xa0] + stx %g5, [%o0 + 0xa8] + stx %g6, [%o0 + 0xb0] + stx %g7, [%o0 + 0xb8] + wrpr %o5, 0x0, %pstate + retl + nop + + .globl restore_alternate_globals +restore_alternate_globals: /* %o0 = save_area */ + rdpr %pstate, %o5 + andn %o5, PSTATE_IE, %o1 + wrpr %o1, PSTATE_AG, %pstate + ldx [%o0 + 0x00], %g0 + ldx [%o0 + 0x08], %g1 + ldx [%o0 + 0x10], %g2 + ldx [%o0 + 0x18], %g3 + ldx [%o0 + 0x20], %g4 + ldx [%o0 + 0x28], %g5 + ldx [%o0 + 0x30], %g6 + ldx [%o0 + 0x38], %g7 + wrpr %o1, PSTATE_IG, %pstate + ldx [%o0 + 0x40], %g0 + ldx [%o0 + 0x48], %g1 + ldx [%o0 + 0x50], %g2 + ldx [%o0 + 0x58], %g3 + ldx [%o0 + 0x60], %g4 + ldx [%o0 + 0x68], %g5 + ldx [%o0 + 0x70], %g6 + ldx [%o0 + 0x78], %g7 + wrpr %o1, PSTATE_MG, %pstate + ldx [%o0 + 0x80], %g0 + ldx [%o0 + 0x88], %g1 + ldx [%o0 + 0x90], %g2 + ldx [%o0 + 0x98], %g3 + ldx [%o0 + 0xa0], %g4 + ldx [%o0 + 0xa8], %g5 + ldx [%o0 + 0xb0], %g6 + ldx [%o0 + 0xb8], %g7 + wrpr %o5, 0x0, %pstate + retl + nop .globl getcc, setcc getcc: diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/kernel/head.S linux/arch/sparc64/kernel/head.S --- v2.2.13/linux/arch/sparc64/kernel/head.S Tue Jan 4 11:10:32 2000 +++ linux/arch/sparc64/kernel/head.S Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.60.2.2 1999/08/19 01:11:12 davem Exp $ +/* $Id: head.S,v 1.60.2.4 1999/11/12 15:45:47 davem Exp $ * head.S: Initial boot code for the Sparc64 port of Linux. * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) @@ -52,7 +52,7 @@ .ascii "HdrS" .word LINUX_VERSION_CODE - .half 0x0202 /* HdrS version */ + .half 0x0203 /* HdrS version */ root_flags: .half 1 root_dev: @@ -65,6 +65,7 @@ .word 0 .xword reboot_command .xword bootstr_len + .word _end /* We must be careful, 32-bit OpenBOOT will get confused if it * tries to save away a register window to a 64-bit kernel @@ -92,28 +93,6 @@ wrpr %g0, (PSTATE_PRIV|PSTATE_PEF|PSTATE_IE), %pstate wr %g0, 0, %fprs -#ifdef __SMP__ - /* Ugly but necessary... */ - sethi %hi(KERNBASE), %g7 - sethi %hi(sparc64_cpu_startup), %g5 - or %g5, %lo(sparc64_cpu_startup), %g5 - sub %g5, %g7, %g5 - sethi %hi(sparc64_cpu_startup_end), %g6 - or %g6, %lo(sparc64_cpu_startup_end), %g6 - sub %g6, %g7, %g6 - sethi %hi(smp_trampoline), %g3 - or %g3, %lo(smp_trampoline), %g3 - sub %g3, %g7, %g3 -1: ldx [%g5], %g1 - stx %g1, [%g3] - membar #StoreStore - flush %g3 - add %g5, 8, %g5 - cmp %g5, %g6 - blu,pt %xcc, 1b - add %g3, 8, %g3 -#endif - create_mappings: /* %g5 holds the tlb data */ sethi %uhi(_PAGE_VALID | _PAGE_SZ4MB), %g5 @@ -379,7 +358,7 @@ wrpr %o1, (PSTATE_IG|PSTATE_IE), %pstate #ifndef __SMP__ sethi %hi(__up_workvec), %g5 - or %g5, %lo(__up_workvec), %g1 + or %g5, %lo(__up_workvec), %g6 #else /* By definition of where we are, this is boot_cpu. */ sethi %hi(cpu_data), %g5 @@ -403,7 +382,7 @@ set_worklist: sllx %g1, 7, %g1 add %g5, %g1, %g5 - add %g5, 64, %g1 + add %g5, 64, %g6 #endif /* Kill PROM timer */ diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/kernel/ioctl32.c linux/arch/sparc64/kernel/ioctl32.c --- v2.2.13/linux/arch/sparc64/kernel/ioctl32.c Tue Jan 4 11:10:32 2000 +++ linux/arch/sparc64/kernel/ioctl32.c Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: ioctl32.c,v 1.62.2.4 1999/09/22 17:06:56 jj Exp $ +/* $Id: ioctl32.c,v 1.62.2.8 1999/11/16 23:59:31 davem Exp $ * ioctl32.c: Conversion between 32bit and 64bit native ioctls. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -1699,9 +1698,9 @@ int error = -EBADF; lock_kernel(); - filp = fcheck(fd); + filp = fget(fd); if(!filp) - goto out; + goto out2; if (!filp->f_op || !filp->f_op->ioctl) { error = sys_ioctl (fd, cmd, arg); @@ -1920,6 +1919,9 @@ case TIOCSCTTY: case TIOCGPTN: case TIOCSPTLCK: + case TIOCGSERIAL: + case TIOCSSERIAL: + case TIOCSERGETLSR: /* Big F */ case FBIOGTYPE: @@ -1995,23 +1997,23 @@ case BLKRASET: /* 0x09 */ - case RAID_VERSION: - case GET_ARRAY_INFO: - case GET_DISK_INFO: - case CLEAR_ARRAY: - case ADD_NEW_DISK: - case HOT_REMOVE_DISK: - case SET_ARRAY_INFO: - case SET_DISK_INFO: - case WRITE_RAID_INFO: - case UNPROTECT_ARRAY: - case PROTECT_ARRAY: - case HOT_ADD_DISK: - case RUN_ARRAY: - case START_ARRAY: - case STOP_ARRAY: - case STOP_ARRAY_RO: - case RESTART_ARRAY_RW: + case /* RAID_VERSION */ _IOR (MD_MAJOR, 0x10, char[12]): + case /* GET_ARRAY_INFO */ _IOR (MD_MAJOR, 0x11, char[72]): + case /* GET_DISK_INFO */ _IOR (MD_MAJOR, 0x12, char[20]): + case /* CLEAR_ARRAY */ _IO (MD_MAJOR, 0x20): + case /* ADD_NEW_DISK */ _IOW (MD_MAJOR, 0x21, char[20]): + case /* HOT_REMOVE_DISK */ _IO (MD_MAJOR, 0x22): + case /* SET_ARRAY_INFO */ _IOW (MD_MAJOR, 0x23, char[72]): + case /* SET_DISK_INFO */ _IO (MD_MAJOR, 0x24): + case /* WRITE_RAID_INFO */ _IO (MD_MAJOR, 0x25): + case /* UNPROTECT_ARRAY */ _IO (MD_MAJOR, 0x26): + case /* PROTECT_ARRAY */ _IO (MD_MAJOR, 0x27): + case /* HOT_ADD_DISK */ _IO (MD_MAJOR, 0x28): + case /* RUN_ARRAY */ _IOW (MD_MAJOR, 0x30, char[12]): + case /* START_ARRAY */ _IO (MD_MAJOR, 0x31): + case /* STOP_ARRAY */ _IO (MD_MAJOR, 0x32): + case /* STOP_ARRAY_RO */ _IO (MD_MAJOR, 0x33): + case /* RESTART_ARRAY_RW */ _IO (MD_MAJOR, 0x34): /* Big K */ case PIO_FONT: @@ -2219,6 +2221,9 @@ case CDROM_DRIVE_STATUS: case CDROM_DISC_STATUS: case CDROM_CHANGER_NSLOTS: + case CDROM_LOCKDOOR: + case CDROM_DEBUG: + case CDROM_GET_CAPABILITY: /* Big L */ case LOOP_SET_FD: @@ -2385,6 +2390,10 @@ case AUTOFS_IOC_PROTOVER: case AUTOFS_IOC_EXPIRE: + /* Raw devices */ + case _IO(0xac, 0): /* RAW_SETBIND */ + case _IO(0xac, 1): /* RAW_GETBIND */ + error = sys_ioctl (fd, cmd, arg); goto out; @@ -2397,9 +2406,11 @@ (int)fd, (unsigned int)cmd, (unsigned int)arg); } while(0); error = -EINVAL; - break; + goto out; } out: + fput(filp); +out2: unlock_kernel(); return error; } diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/kernel/irq.c linux/arch/sparc64/kernel/irq.c --- v2.2.13/linux/arch/sparc64/kernel/irq.c Thu Apr 22 19:24:51 1999 +++ linux/arch/sparc64/kernel/irq.c Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.76 1999/04/02 14:54:30 davem Exp $ +/* $Id: irq.c,v 1.76.2.1 1999/12/05 07:24:37 davem Exp $ * irq.c: UltraSparc IRQ handling/init/registry. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -825,10 +825,10 @@ int cpu = smp_processor_id(); printk("\n%s, CPU %d:\n", str, cpu); - printk("irq: %d [%ld %ld]\n", + printk("irq: %d [%u %u]\n", atomic_read(&global_irq_count), cpu_data[0].irq_count, cpu_data[1].irq_count); - printk("bh: %d [%ld %ld]\n", + printk("bh: %d [%u %u]\n", (spin_is_locked(&global_bh_count) ? 1 : 0), cpu_data[0].bh_count, cpu_data[1].bh_count); } diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/kernel/process.c linux/arch/sparc64/kernel/process.c --- v2.2.13/linux/arch/sparc64/kernel/process.c Tue Jan 4 11:10:32 2000 +++ linux/arch/sparc64/kernel/process.c Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.92.2.2 1999/08/31 18:21:27 davem Exp $ +/* $Id: process.c,v 1.92.2.3 1999/12/05 07:24:38 davem Exp $ * arch/sparc64/kernel/process.c * * Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu) @@ -277,7 +277,7 @@ unsigned long flags; spin_lock_irqsave(®dump_lock, flags); - printk("CPU[%d]: local_irq_count[%ld] global_irq_count[%d]\n", + printk("CPU[%d]: local_irq_count[%u] global_irq_count[%d]\n", smp_processor_id(), local_irq_count, atomic_read(&global_irq_count)); #endif diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/kernel/psycho.c linux/arch/sparc64/kernel/psycho.c --- v2.2.13/linux/arch/sparc64/kernel/psycho.c Mon Aug 9 16:05:55 1999 +++ linux/arch/sparc64/kernel/psycho.c Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: psycho.c,v 1.85.2.2 1999/08/09 13:00:21 davem Exp $ +/* $Id: psycho.c,v 1.85.2.5 1999/10/28 02:28:38 davem Exp $ * psycho.c: Ultra/AX U2P PCI controller support. * * Copyright (C) 1997 David S. Miller (davem@caipfs.rutgers.edu) @@ -221,15 +221,12 @@ control |= (IOMMU_CTRL_TBWSZ | IOMMU_CTRL_ENAB); switch(tsbsize) { case 8: - pci_dvma_mask = 0x1fffffffUL; control |= IOMMU_TSBSZ_8K; break; case 16: - pci_dvma_mask = 0x3fffffffUL; control |= IOMMU_TSBSZ_16K; break; case 32: - pci_dvma_mask = 0x7fffffffUL; control |= IOMMU_TSBSZ_32K; break; default: @@ -461,8 +458,19 @@ err = prom_getproperty(node, "model", namebuf, sizeof(namebuf)); if ((err > 0) && !strncmp(namebuf, "SUNW,sabre", err)) { + /* SABRE/APB is composed of a 4GB aligned 4GB + * total PCI memory space. + */ + pci_dvma_mask = 0xffffffff; + sabre_init(node); goto next_pci; + } else { + /* PSYCHO has two independant 4GB, 2GB aligned, + * memory spaces, and the lower 2GB is the region + * for all PCI memory space device mappings. + */ + pci_dvma_mask = 0x7fffffff; } portid = prom_getintdefault(node, "upa-portid", 0xff); @@ -755,17 +763,18 @@ { struct pci_dev *pdev; unsigned short stmp; - unsigned int itmp; - unsigned char btmp; +#if 0 + unsigned char sabre_latency_timer = 32; for(pdev = pci_devices; pdev; pdev = pdev->next) { if(pdev->vendor == PCI_VENDOR_ID_SUN && pdev->device == PCI_DEVICE_ID_SUN_SABRE) { - pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64); + pci_read_config_byte(pdev, PCI_LATENCY_TIMER, + &sabre_latency_timer); break; } } - +#endif for (pdev = sabre->pci_bus->devices; pdev; pdev = pdev->sibling) { if (pdev->vendor == PCI_VENDOR_ID_SUN && pdev->device == PCI_DEVICE_ID_SUN_SIMBA) { @@ -778,52 +787,11 @@ /* Status register bits are "write 1 to clear". */ pci_write_config_word(pdev, PCI_STATUS, 0xffff); pci_write_config_word(pdev, PCI_SEC_STATUS, 0xffff); - - pci_read_config_word(pdev, PCI_BRIDGE_CONTROL, &stmp); - stmp = PCI_BRIDGE_CTL_MASTER_ABORT | - PCI_BRIDGE_CTL_SERR | - PCI_BRIDGE_CTL_PARITY; - pci_write_config_word(pdev, PCI_BRIDGE_CONTROL, stmp); - - pci_read_config_dword(pdev, APB_PCI_CONTROL_HIGH, &itmp); - itmp = APB_PCI_CTL_HIGH_SERR | - APB_PCI_CTL_HIGH_ARBITER_EN; - pci_write_config_dword(pdev, APB_PCI_CONTROL_HIGH, itmp); - - /* Systems with SIMBA are usually workstations, so - * we configure to park to SIMBA not to the previous - * bus owner. - */ - pci_read_config_dword(pdev, APB_PCI_CONTROL_LOW, &itmp); - itmp = APB_PCI_CTL_LOW_ERRINT_EN | 0x0f; - pci_write_config_dword(pdev, APB_PCI_CONTROL_LOW, itmp); - - /* Don't mess with the retry limit and PIO/DMA latency - * timer settings. But do set primary and secondary - * latency timers. - */ - pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64); - pci_write_config_byte(pdev, PCI_SEC_LATENCY_TIMER, 64); - - /* Here is an overview of the behavior of various - * revisions of APB wrt. write buffer full conditions: - * - * Revision 1.0: pre-FCS, always stalls - * Revision 1.1: pre-FCS, always disconnects - * Revision 1.2: same behavior as rev 1.1 - * Revision 1.3: behavior is determined by bit 4 of - * secondary control register - * 0: stall initially, but disconnect - * if PCI latency timer expires - * 1: always disconnect - * - * By setting the bit, since it is reserved in previous - * revisions of APB, we get all FCS hardware to have - * identical behavior when APB's write buffer fills up. - */ - pci_read_config_byte(pdev, APB_SECONDARY_CONTROL, &btmp); - btmp |= APB_SECONDARY_CTL_DISCON_FULL; - pci_write_config_byte(pdev, APB_SECONDARY_CONTROL, btmp); +#if 0 + /* Propagate Sabre latency timer value into APB. */ + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, + sabre_latency_timer); +#endif } } } @@ -1958,6 +1926,12 @@ } node = pcp->prom_node; + + /* No need to crash if the PCI device lacks PROM + * information. + */ + if (node == -1) + return; err = prom_getproperty(node, "reg", (char *)&pregs[0], sizeof(pregs)); if(err == 0 || err == -1) { diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/kernel/ptrace.c linux/arch/sparc64/kernel/ptrace.c --- v2.2.13/linux/arch/sparc64/kernel/ptrace.c Wed Mar 24 15:10:28 1999 +++ linux/arch/sparc64/kernel/ptrace.c Tue Jan 4 10:12:13 2000 @@ -40,6 +40,7 @@ pgd_t * pgdir; pmd_t * pgmiddle; pte_t * pgtable; + int fault; repeat: pgdir = pgd_offset(vma->vm_mm, addr); @@ -50,8 +51,12 @@ current->mm->segments = (void *) (addr & PAGE_SIZE); if (pgd_none(*pgdir)) { - handle_mm_fault(tsk, vma, addr, write); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, write); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return 0; } if (pgd_bad(*pgdir)) { printk("ptrace: bad page directory %016lx\n", pgd_val(*pgdir)); @@ -60,8 +65,12 @@ } pgmiddle = pmd_offset(pgdir, addr); if (pmd_none(*pgmiddle)) { - handle_mm_fault(tsk, vma, addr, write); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, write); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return 0; } if (pmd_bad(*pgmiddle)) { printk("ptrace: bad page middle %016lx\n", pmd_val(*pgmiddle)); @@ -70,12 +79,20 @@ } pgtable = pte_offset(pgmiddle, addr); if (!pte_present(*pgtable)) { - handle_mm_fault(tsk, vma, addr, write); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, write); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return 0; } if (write && !pte_write(*pgtable)) { - handle_mm_fault(tsk, vma, addr, write); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, write); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, tsk); + return 0; } return pgtable; } diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/kernel/setup.c linux/arch/sparc64/kernel/setup.c --- v2.2.13/linux/arch/sparc64/kernel/setup.c Sat May 29 11:10:15 1999 +++ linux/arch/sparc64/kernel/setup.c Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.43.2.1 1999/05/28 02:18:13 davem Exp $ +/* $Id: setup.c,v 1.43.2.2 1999/10/24 17:29:20 davem Exp $ * linux/arch/sparc64/kernel/setup.c * * Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu) @@ -434,6 +434,17 @@ extern struct consw sun_serial_con; +void register_prom_callbacks(void) +{ + prom_setcallback(prom_callback); + prom_feval(": linux-va>tte-data 2 \" va>tte-data\" $callback drop ; " + "' linux-va>tte-data to va>tte-data"); + prom_feval(": linux-.soft1 1 \" .soft1\" $callback 2drop ; " + "' linux-.soft1 to .soft1"); + prom_feval(": linux-.soft2 1 \" .soft2\" $callback 2drop ; " + "' linux-.soft2 to .soft2"); +} + __initfunc(void setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * memory_end_p)) { @@ -478,13 +489,6 @@ } } } - prom_setcallback(prom_callback); - prom_feval(": linux-va>tte-data 2 \" va>tte-data\" $callback drop ; " - "' linux-va>tte-data to va>tte-data"); - prom_feval(": linux-.soft1 1 \" .soft1\" $callback 2drop ; " - "' linux-.soft1 to .soft1"); - prom_feval(": linux-.soft2 1 \" .soft2\" $callback 2drop ; " - "' linux-.soft2 to .soft2"); /* In paging_init() we tip off this value to see if we need * to change init_mm.pgd to point to the real alias mapping. diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/kernel/smp.c linux/arch/sparc64/kernel/smp.c --- v2.2.13/linux/arch/sparc64/kernel/smp.c Tue May 11 08:24:32 1999 +++ linux/arch/sparc64/kernel/smp.c Tue Jan 4 10:12:13 2000 @@ -94,7 +94,8 @@ cpu_data[id].udelay_val = loops_per_sec; cpu_data[id].pgcache_size = 0; - cpu_data[id].pte_cache = NULL; + cpu_data[id].pte_cache[0] = NULL; + cpu_data[id].pte_cache[1] = NULL; cpu_data[id].pgdcache_size = 0; cpu_data[id].pgd_cache = NULL; cpu_data[id].idle_volume = 1; @@ -180,7 +181,7 @@ extern struct prom_cpuinfo linux_cpus[64]; -extern unsigned long smp_trampoline; +extern unsigned long sparc64_cpu_startup; /* The OBP cpu startup callback truncates the 3rd arg cookie to * 32-bits (I think) so to be safe we have it read the pointer @@ -206,15 +207,13 @@ continue; if(cpu_present_map & (1UL << i)) { - unsigned long entry = (unsigned long)(&smp_trampoline); + unsigned long entry = (unsigned long)(&sparc64_cpu_startup); unsigned long cookie = (unsigned long)(&cpu_new_task); struct task_struct *p; int timeout; int no; - extern unsigned long phys_base; - entry += phys_base - KERNBASE; - cookie += phys_base - KERNBASE; + prom_printf("Starting CPU %d... ", i); kernel_thread(start_secondary, NULL, CLONE_PID); p = task[++cpucount]; p->processor = i; @@ -235,9 +234,11 @@ cpu_number_map[i] = cpucount; __cpu_logical_map[cpucount] = i; prom_cpu_nodes[i] = linux_cpus[no].prom_node; + prom_printf("OK\n"); } else { cpucount--; printk("Processor %d is stuck.\n", i); + prom_printf("FAILED\n"); } } if(!callin_flag) { @@ -536,14 +537,31 @@ /* Imprisoned penguins run with %pil == 15, but PSTATE_IE set, so they * can service tlb flush xcalls... */ +extern void prom_world(int); +extern void save_alternate_globals(unsigned long *); +extern void restore_alternate_globals(unsigned long *); void smp_penguin_jailcell(void) { - flushw_user(); + unsigned long global_save[24]; + + __asm__ __volatile__("flushw"); + save_alternate_globals(global_save); + prom_world(1); atomic_inc(&smp_capture_registry); membar("#StoreLoad | #StoreStore"); while(penguins_are_doing_time) membar("#LoadLoad"); + restore_alternate_globals(global_save); atomic_dec(&smp_capture_registry); + prom_world(0); +} + +extern unsigned long xcall_promstop; + +void smp_promstop_others(void) +{ + if (smp_processors_ready) + smp_cross_call(&xcall_promstop, 0, 0, 0); } static inline void sparc64_do_profile(unsigned long pc) @@ -774,7 +792,8 @@ (int) cacheflush_time); } -int __init setup_profiling_timer(unsigned int multiplier) +/* /proc/profile writes can call this, don't __init it please. */ +int setup_profiling_timer(unsigned int multiplier) { unsigned long flags; int i; diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/kernel/sys_sparc32.c linux/arch/sparc64/kernel/sys_sparc32.c --- v2.2.13/linux/arch/sparc64/kernel/sys_sparc32.c Wed Jun 2 09:55:38 1999 +++ linux/arch/sparc64/kernel/sys_sparc32.c Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: sys_sparc32.c,v 1.107.2.1 1999/05/16 10:48:44 davem Exp $ +/* $Id: sys_sparc32.c,v 1.107.2.5 1999/11/12 11:17:47 davem Exp $ * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls. * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -1136,8 +1137,10 @@ } ret = -EINVAL; - if (n < 0 || n > KFDS_NR) + if (n < 0) goto out_nofds; + if (n > current->files->max_fdset) + n = current->files->max_fdset; /* * We need 6 bitmaps (in/out/ex for both incoming and outgoing), @@ -1197,84 +1200,157 @@ return ret; } -static inline int putstat(struct stat32 *ubuf, struct stat *kbuf) +static int cp_new_stat32(struct inode *inode, struct stat32 *statbuf) { + unsigned long ino, blksize, blocks; + kdev_t dev, rdev; + umode_t mode; + nlink_t nlink; + uid_t uid; + gid_t gid; + off_t size; + time_t atime, mtime, ctime; int err; - - err = put_user (kbuf->st_dev, &ubuf->st_dev); - err |= __put_user (kbuf->st_ino, &ubuf->st_ino); - err |= __put_user (kbuf->st_mode, &ubuf->st_mode); - err |= __put_user (kbuf->st_nlink, &ubuf->st_nlink); - err |= __put_user (kbuf->st_uid, &ubuf->st_uid); - err |= __put_user (kbuf->st_gid, &ubuf->st_gid); - err |= __put_user (kbuf->st_rdev, &ubuf->st_rdev); - err |= __put_user (kbuf->st_size, &ubuf->st_size); - err |= __put_user (kbuf->st_atime, &ubuf->st_atime); - err |= __put_user (kbuf->st_mtime, &ubuf->st_mtime); - err |= __put_user (kbuf->st_ctime, &ubuf->st_ctime); - err |= __put_user (kbuf->st_blksize, &ubuf->st_blksize); - err |= __put_user (kbuf->st_blocks, &ubuf->st_blocks); + + /* Stream the loads of inode data into the load buffer, + * then we push it all into the store buffer below. This + * should give optimal cache performance. + */ + ino = inode->i_ino; + dev = inode->i_dev; + mode = inode->i_mode; + nlink = inode->i_nlink; + uid = inode->i_uid; + gid = inode->i_gid; + rdev = inode->i_rdev; + size = inode->i_size; + atime = inode->i_atime; + mtime = inode->i_mtime; + ctime = inode->i_ctime; + blksize = inode->i_blksize; + blocks = inode->i_blocks; + + err = put_user(kdev_t_to_nr(dev), &statbuf->st_dev); + err |= put_user(ino, &statbuf->st_ino); + err |= put_user(mode, &statbuf->st_mode); + err |= put_user(nlink, &statbuf->st_nlink); + err |= put_user(uid, &statbuf->st_uid); + err |= put_user(gid, &statbuf->st_gid); + err |= put_user(kdev_t_to_nr(rdev), &statbuf->st_rdev); + err |= put_user(size, &statbuf->st_size); + err |= put_user(atime, &statbuf->st_atime); + err |= put_user(0, &statbuf->__unused1); + err |= put_user(mtime, &statbuf->st_mtime); + err |= put_user(0, &statbuf->__unused2); + err |= put_user(ctime, &statbuf->st_ctime); + err |= put_user(0, &statbuf->__unused3); + if (blksize) { + err |= put_user(blksize, &statbuf->st_blksize); + err |= put_user(blocks, &statbuf->st_blocks); + } else { + unsigned int tmp_blocks; + +#define D_B 7 +#define I_B (BLOCK_SIZE / sizeof(unsigned short)) + tmp_blocks = (size + BLOCK_SIZE - 1) / BLOCK_SIZE; + if (tmp_blocks > D_B) { + unsigned int indirect; + + indirect = (tmp_blocks - D_B + I_B - 1) / I_B; + tmp_blocks += indirect; + if (indirect > 1) { + indirect = (indirect - 1 + I_B - 1) / I_B; + tmp_blocks += indirect; + if (indirect > 1) + tmp_blocks++; + } + } + err |= put_user(BLOCK_SIZE, &statbuf->st_blksize); + err |= put_user((BLOCK_SIZE / 512) * tmp_blocks, &statbuf->st_blocks); +#undef D_B +#undef I_B + } + err |= put_user(0, &statbuf->__unused4[0]); + err |= put_user(0, &statbuf->__unused4[1]); + return err; } -extern asmlinkage int sys_newstat(char * filename, struct stat * statbuf); - asmlinkage int sys32_newstat(char * filename, struct stat32 *statbuf) { - int ret; - struct stat s; - char *filenam; - mm_segment_t old_fs = get_fs(); - - filenam = getname32 (filename); - ret = PTR_ERR(filenam); - if (!IS_ERR(filenam)) { - set_fs (KERNEL_DS); - ret = sys_newstat(filenam, &s); - set_fs (old_fs); - putname (filenam); - if (putstat (statbuf, &s)) - return -EFAULT; + struct dentry *dentry; + int error; + + lock_kernel(); + dentry = namei(filename); + + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + struct inode *inode = dentry->d_inode; + + if (inode->i_op && + inode->i_op->revalidate) + error = inode->i_op->revalidate(dentry); + else + error = 0; + if (!error) + error = cp_new_stat32(inode, statbuf); + + dput(dentry); } - return ret; + unlock_kernel(); + return error; } -extern asmlinkage int sys_newlstat(char * filename, struct stat * statbuf); - asmlinkage int sys32_newlstat(char * filename, struct stat32 *statbuf) { - int ret; - struct stat s; - char *filenam; - mm_segment_t old_fs = get_fs(); - - filenam = getname32 (filename); - ret = PTR_ERR(filenam); - if (!IS_ERR(filenam)) { - set_fs (KERNEL_DS); - ret = sys_newlstat(filenam, &s); - set_fs (old_fs); - putname (filenam); - if (putstat (statbuf, &s)) - return -EFAULT; + struct dentry *dentry; + int error; + + lock_kernel(); + dentry = lnamei(filename); + + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + struct inode *inode = dentry->d_inode; + + if (inode->i_op && + inode->i_op->revalidate) + error = inode->i_op->revalidate(dentry); + else + error = 0; + if (!error) + error = cp_new_stat32(inode, statbuf); + + dput(dentry); } - return ret; + unlock_kernel(); + return error; } -extern asmlinkage int sys_newfstat(unsigned int fd, struct stat * statbuf); - asmlinkage int sys32_newfstat(unsigned int fd, struct stat32 *statbuf) { - int ret; - struct stat s; - mm_segment_t old_fs = get_fs(); - - set_fs (KERNEL_DS); - ret = sys_newfstat(fd, &s); - set_fs (old_fs); - if (putstat (statbuf, &s)) - return -EFAULT; - return ret; + struct file *f; + int err = -EBADF; + + lock_kernel(); + f = fget(fd); + if (f) { + struct dentry *dentry = f->f_dentry; + struct inode *inode = dentry->d_inode; + + if (inode->i_op && + inode->i_op->revalidate) + err = inode->i_op->revalidate(dentry); + else + err = 0; + if (!err) + err = cp_new_stat32(inode, statbuf); + + fput(f); + } + unlock_kernel(); + return err; } extern asmlinkage int sys_sysfs(int option, unsigned long arg1, unsigned long arg2); @@ -1677,7 +1753,7 @@ siginfo_t32 * siginfo64to32(siginfo_t32 *d, siginfo_t *s) { - memset (&d, 0, sizeof(siginfo_t32)); + memset (d, 0, sizeof(siginfo_t32)); d->si_signo = s->si_signo; d->si_errno = s->si_errno; d->si_code = s->si_code; @@ -2581,6 +2657,48 @@ return len; } +extern asmlinkage int sys_setsockopt(int fd, int level, int optname, + char *optval, int optlen); + +asmlinkage int sys32_setsockopt(int fd, int level, int optname, + char *optval, int optlen) +{ + if (optname == SO_ATTACH_FILTER) { + struct sock_fprog32 { + __u16 len; + __u32 filter; + } *fprog32 = (struct sock_fprog32 *)optval; + struct sock_fprog kfprog; + struct sock_filter *kfilter; + unsigned int fsize; + mm_segment_t old_fs; + __u32 uptr; + int ret; + + if (get_user(kfprog.len, &fprog32->len) || + __get_user(uptr, &fprog32->filter)) + return -EFAULT; + kfprog.filter = (struct sock_filter *)A(uptr); + fsize = kfprog.len * sizeof(struct sock_filter); + kfilter = (struct sock_filter *)kmalloc(fsize, GFP_KERNEL); + if (kfilter == NULL) + return -ENOMEM; + if (copy_from_user(kfilter, kfprog.filter, fsize)) { + kfree(kfilter); + return -EFAULT; + } + kfprog.filter = kfilter; + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_setsockopt(fd, level, optname, + (char *)&kfprog, sizeof(kfprog)); + set_fs(old_fs); + kfree(kfilter); + return ret; + } + return sys_setsockopt(fd, level, optname, optval, optlen); +} + /* Argument list sizes for sys_socketcall */ #define AL(x) ((x) * sizeof(u32)) static unsigned char nargs[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), @@ -2599,8 +2717,6 @@ extern asmlinkage int sys_recv(int fd, void *ubuf, size_t size, unsigned flags); extern asmlinkage int sys32_recvfrom(int fd, u32 ubuf, __kernel_size_t32 size, unsigned flags, u32 addr, u32 addr_len); -extern asmlinkage int sys_setsockopt(int fd, int level, int optname, - char *optval, int optlen); extern asmlinkage int sys32_getsockopt(int fd, int level, int optname, u32 optval, u32 optlen); @@ -2651,7 +2767,7 @@ case SYS_SHUTDOWN: return sys_shutdown(a0,a1); case SYS_SETSOCKOPT: - return sys_setsockopt(a0, a1, a[2], (char *)A(a[3]), a[4]); + return sys32_setsockopt(a0, a1, a[2], (char *)A(a[3]), a[4]); case SYS_GETSOCKOPT: return sys32_getsockopt(a0, a1, a[2], a[3], a[4]); case SYS_SENDMSG: diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/kernel/time.c linux/arch/sparc64/kernel/time.c --- v2.2.13/linux/arch/sparc64/kernel/time.c Mon Mar 15 16:10:43 1999 +++ linux/arch/sparc64/kernel/time.c Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.20 1999/03/15 22:13:40 davem Exp $ +/* $Id: time.c,v 1.20.2.1 1999/10/09 06:03:23 davem Exp $ * time.c: UltraSparc timer and TOD clock support. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -47,7 +47,7 @@ */ unsigned long timer_tick_offset; static unsigned long timer_tick_compare; -static unsigned long timer_ticks_per_usec; +static unsigned long timer_ticks_per_usec_quotient; static __inline__ void timer_check_rtc(void) { @@ -392,7 +392,7 @@ init_timers(timer_interrupt, &clock); timer_tick_offset = clock / HZ; - timer_ticks_per_usec = clock / 1000000; + timer_ticks_per_usec_quotient = ((1UL<<32) / (clock / 1000020UL)); } static __inline__ unsigned long do_gettimeoffset(void) @@ -408,7 +408,7 @@ : "r" (timer_tick_offset), "r" (timer_tick_compare) : "g1", "g2"); - return ticks / timer_ticks_per_usec; + return (ticks * timer_ticks_per_usec_quotient) >> 32UL; } /* This need not obtain the xtime_lock as it is coded in @@ -431,24 +431,22 @@ or %g2, %lo(xtime), %g2 or %g1, %lo(timer_tick_compare), %g1 1: ldda [%g2] 0x24, %o4 - membar #LoadLoad | #MemIssue rd %tick, %o1 ldx [%g1], %g7 - membar #LoadLoad | #MemIssue ldda [%g2] 0x24, %o2 - membar #LoadLoad xor %o4, %o2, %o2 xor %o5, %o3, %o3 orcc %o2, %o3, %g0 bne,pn %xcc, 1b sethi %hi(lost_ticks), %o2 - sethi %hi(timer_ticks_per_usec), %o3 + sethi %hi(timer_ticks_per_usec_quotient), %o3 ldx [%o2 + %lo(lost_ticks)], %o2 add %g3, %o1, %o1 - ldx [%o3 + %lo(timer_ticks_per_usec)], %o3 + ldx [%o3 + %lo(timer_ticks_per_usec_quotient)], %o3 sub %o1, %g7, %o1 + mulx %o3, %o1, %o1 brz,pt %o2, 1f - udivx %o1, %o3, %o1 + srlx %o1, 32, %o1 sethi %hi(10000), %g2 or %g2, %lo(10000), %g2 add %o1, %g2, %o1 diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/kernel/trampoline.S linux/arch/sparc64/kernel/trampoline.S --- v2.2.13/linux/arch/sparc64/kernel/trampoline.S Tue Jan 4 11:10:33 2000 +++ linux/arch/sparc64/kernel/trampoline.S Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: trampoline.S,v 1.8.2.2 1999/08/19 01:11:14 davem Exp $ +/* $Id: trampoline.S,v 1.8.2.4 1999/10/27 00:22:24 davem Exp $ * trampoline.S: Jump start slave processors on sparc64. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -14,21 +14,107 @@ #include .data - .align 8 - .globl smp_trampoline -smp_trampoline: .skip 0x300 + .align 8 +call_method: + .asciz "call-method" + .align 8 +itlb_load: + .asciz "SUNW,itlb-load" + .align 8 +dtlb_load: + .asciz "SUNW,dtlb-load" .text .align 8 .globl sparc64_cpu_startup, sparc64_cpu_startup_end sparc64_cpu_startup: flushw + mov (LSU_CONTROL_IC | LSU_CONTROL_DC | LSU_CONTROL_IM | LSU_CONTROL_DM), %g1 stxa %g1, [%g0] ASI_LSU_CONTROL membar #Sync - wrpr %g0, (PSTATE_PRIV | PSTATE_PEF | PSTATE_IE), %pstate - wr %g0, 0, %fprs + wrpr %g0, 15, %pil + wr %g0, 0, %tick_cmpr + + /* Call OBP by hand to lock KERNBASE into i/d tlbs. */ + mov %o0, %l0 + + sethi %hi(prom_entry_lock), %g2 +1: ldstub [%g2 + %lo(prom_entry_lock)], %g1 + brnz,pn %g1, 1b + membar #StoreLoad | #StoreStore + + sethi %hi(p1275buf), %g2 + or %g2, %lo(p1275buf), %g2 + ldx [%g2 + 0x10], %l2 + mov %sp, %l1 + add %l2, -(192 + 128), %sp + flushw + + sethi %hi(call_method), %g2 + or %g2, %lo(call_method), %g2 + stx %g2, [%sp + 2047 + 128 + 0x00] + mov 5, %g2 + stx %g2, [%sp + 2047 + 128 + 0x08] + mov 1, %g2 + stx %g2, [%sp + 2047 + 128 + 0x10] + sethi %hi(itlb_load), %g2 + or %g2, %lo(itlb_load), %g2 + stx %g2, [%sp + 2047 + 128 + 0x18] + sethi %hi(mmu_ihandle_cache), %g2 + lduw [%g2 + %lo(mmu_ihandle_cache)], %g2 + stx %g2, [%sp + 2047 + 128 + 0x20] + sethi %hi(KERNBASE), %g2 + stx %g2, [%sp + 2047 + 128 + 0x28] + sethi %hi(kern_locked_tte_data), %g2 + ldx [%g2 + %lo(kern_locked_tte_data)], %g2 + stx %g2, [%sp + 2047 + 128 + 0x30] + mov 63, %g2 + stx %g2, [%sp + 2047 + 128 + 0x38] + sethi %hi(p1275buf), %g2 + or %g2, %lo(p1275buf), %g2 + ldx [%g2 + 0x08], %o1 + call %o1 + add %sp, (2047 + 128), %o0 + + sethi %hi(call_method), %g2 + or %g2, %lo(call_method), %g2 + stx %g2, [%sp + 2047 + 128 + 0x00] + mov 5, %g2 + stx %g2, [%sp + 2047 + 128 + 0x08] + mov 1, %g2 + stx %g2, [%sp + 2047 + 128 + 0x10] + sethi %hi(dtlb_load), %g2 + or %g2, %lo(dtlb_load), %g2 + stx %g2, [%sp + 2047 + 128 + 0x18] + sethi %hi(mmu_ihandle_cache), %g2 + lduw [%g2 + %lo(mmu_ihandle_cache)], %g2 + stx %g2, [%sp + 2047 + 128 + 0x20] + sethi %hi(KERNBASE), %g2 + stx %g2, [%sp + 2047 + 128 + 0x28] + sethi %hi(kern_locked_tte_data), %g2 + ldx [%g2 + %lo(kern_locked_tte_data)], %g2 + stx %g2, [%sp + 2047 + 128 + 0x30] + mov 63, %g2 + stx %g2, [%sp + 2047 + 128 + 0x38] + sethi %hi(p1275buf), %g2 + or %g2, %lo(p1275buf), %g2 + ldx [%g2 + 0x08], %o1 + call %o1 + add %sp, (2047 + 128), %o0 + + sethi %hi(prom_entry_lock), %g2 + stb %g0, [%g2 + %lo(prom_entry_lock)] + membar #StoreStore | #StoreLoad + + mov %l1, %sp + flushw + + mov %l0, %o0 + + wrpr %g0, (PSTATE_PRIV | PSTATE_PEF), %pstate + wr %g0, 0, %fprs sethi %uhi(PAGE_OFFSET), %g4 sllx %g4, 32, %g4 @@ -37,99 +123,6 @@ srl %o0, 0, %o0 ldx [%o0], %g6 - sethi %uhi(_PAGE_VALID | _PAGE_SZ4MB), %g5 - sllx %g5, 32, %g5 - or %g5, (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_L | _PAGE_W | _PAGE_G), %g5 - - sethi %uhi(_PAGE_PADDR), %g3 - or %g3, %ulo(_PAGE_PADDR), %g3 - sllx %g3, 32, %g3 - sethi %hi(_PAGE_PADDR), %g7 - or %g7, %lo(_PAGE_PADDR), %g7 - or %g3, %g7, %g3 - - clr %l0 - set 0x1fff, %l2 - rd %pc, %l3 - andn %l3, %l2, %g2 -1: ldxa [%l0] ASI_ITLB_TAG_READ, %g1 - nop - nop - nop - andn %g1, %l2, %g1 - cmp %g1, %g2 - be,a,pn %xcc, 2f - ldxa [%l0] ASI_ITLB_DATA_ACCESS, %g1 - cmp %l0, (63 << 3) - blu,pt %xcc, 1b - add %l0, (1 << 3), %l0 - -2: nop - nop - nop - and %g1, %g3, %g1 - sub %g1, %g2, %g1 - or %g5, %g1, %g5 - clr %l0 - sethi %hi(KERNBASE), %g3 - sethi %hi(KERNBASE<<1), %g7 - mov TLB_TAG_ACCESS, %l7 -1: ldxa [%l0] ASI_ITLB_TAG_READ, %g1 - nop - nop - nop - andn %g1, %l2, %g1 - cmp %g1, %g3 - blu,pn %xcc, 2f - cmp %g1, %g7 - bgeu,pn %xcc, 2f - nop - stxa %g0, [%l7] ASI_IMMU - stxa %g0, [%l0] ASI_ITLB_DATA_ACCESS -2: cmp %l0, (63 << 3) - blu,pt %xcc, 1b - add %l0, (1 << 3), %l0 - - nop - nop - nop - clr %l0 -1: ldxa [%l0] ASI_DTLB_TAG_READ, %g1 - nop - nop - nop - andn %g1, %l2, %g1 - cmp %g1, %g3 - blu,pn %xcc, 2f - cmp %g1, %g7 - bgeu,pn %xcc, 2f - nop - stxa %g0, [%l7] ASI_DMMU - stxa %g0, [%l0] ASI_DTLB_DATA_ACCESS -2: cmp %l0, (63 << 3) - blu,pt %xcc, 1b - add %l0, (1 << 3), %l0 - - nop - nop - nop - sethi %hi(KERNBASE), %g3 - mov (63 << 3), %g7 - stxa %g3, [%l7] ASI_DMMU - stxa %g5, [%g7] ASI_DTLB_DATA_ACCESS - membar #Sync - stxa %g3, [%l7] ASI_IMMU - stxa %g5, [%g7] ASI_ITLB_DATA_ACCESS - membar #Sync - flush %g3 - membar #Sync - b,pt %xcc, 1f - nop -1: set bounce, %g2 - jmpl %g2 + %g0, %g0 - nop - -bounce: mov PRIMARY_CONTEXT, %g7 stxa %g0, [%g7] ASI_DMMU membar #Sync @@ -137,24 +130,6 @@ stxa %g0, [%g7] ASI_DMMU membar #Sync - mov TLB_TAG_ACCESS, %g2 - stxa %g3, [%g2] ASI_IMMU - stxa %g3, [%g2] ASI_DMMU - - mov (63 << 3), %g7 - ldxa [%g7] ASI_ITLB_DATA_ACCESS, %g1 - andn %g1, (_PAGE_G), %g1 - stxa %g1, [%g7] ASI_ITLB_DATA_ACCESS - membar #Sync - - ldxa [%g7] ASI_DTLB_DATA_ACCESS, %g1 - andn %g1, (_PAGE_G), %g1 - stxa %g1, [%g7] ASI_DTLB_DATA_ACCESS - membar #Sync - - flush %g3 - membar #Sync - mov 1, %g5 sllx %g5, (PAGE_SHIFT + 1), %g5 sub %g5, (REGWIN_SZ + STACK_BIAS), %g5 @@ -167,12 +142,12 @@ /* Setup the trap globals, then we can resurface. */ rdpr %pstate, %o1 mov %g6, %o2 - wrpr %o1, (PSTATE_AG | PSTATE_IE), %pstate + wrpr %o1, PSTATE_AG, %pstate sethi %hi(sparc64_ttable_tl0), %g5 wrpr %g5, %tba mov %o2, %g6 - wrpr %o1, (PSTATE_MG | PSTATE_IE), %pstate + wrpr %o1, PSTATE_MG, %pstate #define KERN_HIGHBITS ((_PAGE_VALID | _PAGE_SZ4MB) ^ 0xfffff80000000000) #define KERN_LOWBITS (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W) #ifdef THIS_IS_CHEETAH @@ -198,7 +173,7 @@ #undef VPTE_BASE /* Setup interrupt globals, we are always SMP. */ - wrpr %o1, (PSTATE_IG | PSTATE_IE), %pstate + wrpr %o1, PSTATE_IG, %pstate /* Get our UPA MID. */ lduw [%o2 + AOFF_task_processor], %g1 @@ -208,11 +183,14 @@ /* In theory this is: &(cpu_data[this_upamid].irq_worklists[0]) */ sllx %g1, 7, %g1 add %g5, %g1, %g1 - add %g1, 64, %g1 + add %g1, 64, %g6 wrpr %g0, 0, %wstate or %o1, PSTATE_IE, %o1 wrpr %o1, 0, %pstate + + call prom_set_trap_table + sethi %hi(sparc64_ttable_tl0), %o0 call smp_callin nop diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/kernel/traps.c linux/arch/sparc64/kernel/traps.c --- v2.2.13/linux/arch/sparc64/kernel/traps.c Tue Jan 4 11:10:33 2000 +++ linux/arch/sparc64/kernel/traps.c Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: traps.c,v 1.58.2.1 1999/08/19 01:11:16 davem Exp $ +/* $Id: traps.c,v 1.58.2.2 1999/12/01 23:55:43 davem Exp $ * arch/sparc64/kernel/traps.c * * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) @@ -26,6 +26,7 @@ #include #include #include +#include #ifdef CONFIG_KMOD #include #endif @@ -814,6 +815,13 @@ #endif } #endif + +void do_getpsr(struct pt_regs *regs) +{ + regs->u_regs[UREG_I0] = tstate_to_psr(regs->tstate); + regs->tpc = regs->tnpc; + regs->tnpc += 4; +} void trap_init(void) { diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/kernel/ttable.S linux/arch/sparc64/kernel/ttable.S --- v2.2.13/linux/arch/sparc64/kernel/ttable.S Tue Jan 4 11:10:33 2000 +++ linux/arch/sparc64/kernel/ttable.S Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: ttable.S,v 1.28.2.1 1999/08/19 01:11:14 davem Exp $ +/* $Id: ttable.S,v 1.28.2.2 1999/12/01 23:55:44 davem Exp $ * ttable.S: Sparc V9 Trap Table(s) with SpitFire extensions. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -120,7 +120,8 @@ tl0_resv11e: TRAP_UTRAP(UT_TRAP_INSTRUCTION_30,0x11e) TRAP_UTRAP(UT_TRAP_INSTRUCTION_31,0x11f) tl0_getcc: GETCC_TRAP tl0_setcc: SETCC_TRAP -tl0_resv122: BTRAP(0x122) BTRAP(0x123) BTRAP(0x124) BTRAP(0x125) BTRAP(0x126) +tl0_getpsr: TRAP(do_getpsr) +tl0_resv123: BTRAP(0x123) BTRAP(0x124) BTRAP(0x125) BTRAP(0x126) tl0_solindir: INDIRECT_SOLARIS_SYSCALL(156) tl0_resv128: BTRAP(0x128) BTRAP(0x129) BTRAP(0x12a) BTRAP(0x12b) BTRAP(0x12c) tl0_resv12d: BTRAP(0x12d) BTRAP(0x12e) BTRAP(0x12f) BTRAP(0x130) BTRAP(0x131) diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/lib/blockops.S linux/arch/sparc64/lib/blockops.S --- v2.2.13/linux/arch/sparc64/lib/blockops.S Tue Oct 27 09:52:20 1998 +++ linux/arch/sparc64/lib/blockops.S Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: blockops.S,v 1.16 1998/10/20 03:09:04 jj Exp $ +/* $Id: blockops.S,v 1.16.2.1 1999/10/07 20:48:14 davem Exp $ * blockops.S: UltraSparc block zero optimized routines. * * Copyright (C) 1996,1998 David S. Miller (davem@caip.rutgers.edu) @@ -136,16 +136,18 @@ faddd %f0, %f2, %f12 ! FPA Group fmuld %f0, %f2, %f14 ! FPM - wr %g0, ASI_BLK_P, %asi ! LSU Group membar #StoreLoad | #StoreStore | #LoadStore ! LSU Group -1: stda %f0, [%o0 + 0x00] %asi ! Store Group - stda %f0, [%o0 + 0x40] %asi ! Store Group - stda %f0, [%o0 + 0x80] %asi ! Store Group - stda %f0, [%o0 + 0xc0] %asi ! Store Group +1: stda %f0, [%o0 + %g0] ASI_BLK_P ! Store Group + add %o0, 0x40, %o0 ! IEU0 + stda %f0, [%o0 + %g0] ASI_BLK_P ! Store Group + add %o0, 0x40, %o0 ! IEU0 + stda %f0, [%o0 + %g0] ASI_BLK_P ! Store Group + add %o0, 0x40, %o0 ! IEU0 Group + stda %f0, [%o0 + %g0] ASI_BLK_P ! Store Group subcc %o1, 1, %o1 ! IEU1 bne,pt %icc, 1b ! CTI - add %o0, 0x100, %o0 ! IEU0 Group + add %o0, 0x40, %o0 ! IEU0 Group membar #Sync ! LSU Group VISExitHalf diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/Makefile linux/arch/sparc64/math-emu/Makefile --- v2.2.13/linux/arch/sparc64/math-emu/Makefile Tue Oct 27 09:52:20 1998 +++ linux/arch/sparc64/math-emu/Makefile Tue Jan 4 10:12:13 2000 @@ -8,12 +8,7 @@ # Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := math-emu.o -O_OBJS := math.o fabsq.o faddq.o fdivq.o fdmulq.o fitoq.o \ - fmovq.o fmulq.o fnegq.o fqtoi.o fqtox.o fsubq.o \ - fxtoq.o fdtoq.o fstoq.o fqtos.o fqtod.o fsqrtq.o \ - fcmpq.o fcmpeq.o udivmodti4.o \ - fsqrts.o fsqrtd.o fadds.o faddd.o fsubs.o fsubd.o \ - fmuls.o fmuld.o fdivs.o fdivd.o fsmuld.o \ - fstoi.o fdtoi.o fstox.o fdtox.o fstod.o fdtos.o +O_OBJS := math.o +CFLAGS += -I. -I$(TOPDIR)/include/math-emu -w include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/double.h linux/arch/sparc64/math-emu/double.h --- v2.2.13/linux/arch/sparc64/math-emu/double.h Tue Apr 14 17:44:21 1998 +++ linux/arch/sparc64/math-emu/double.h Wed Dec 31 16:00:00 1969 @@ -1,129 +0,0 @@ -/* - * Definitions for IEEE Double Precision - */ - -#if _FP_W_TYPE_SIZE < 32 -#error "Here's a nickel kid. Go buy yourself a real computer." -#endif - -#if _FP_W_TYPE_SIZE < 64 -#define _FP_FRACTBITS_D (2 * _FP_W_TYPE_SIZE) -#else -#define _FP_FRACTBITS_D _FP_W_TYPE_SIZE -#endif - -#define _FP_FRACBITS_D 53 -#define _FP_FRACXBITS_D (_FP_FRACTBITS_D - _FP_FRACBITS_D) -#define _FP_WFRACBITS_D (_FP_WORKBITS + _FP_FRACBITS_D) -#define _FP_WFRACXBITS_D (_FP_FRACTBITS_D - _FP_WFRACBITS_D) -#define _FP_EXPBITS_D 11 -#define _FP_EXPBIAS_D 1023 -#define _FP_EXPMAX_D 2047 - -#define _FP_QNANBIT_D \ - ((_FP_W_TYPE)1 << (_FP_FRACBITS_D-2) % _FP_W_TYPE_SIZE) -#define _FP_IMPLBIT_D \ - ((_FP_W_TYPE)1 << (_FP_FRACBITS_D-1) % _FP_W_TYPE_SIZE) -#define _FP_OVERFLOW_D \ - ((_FP_W_TYPE)1 << _FP_WFRACBITS_D % _FP_W_TYPE_SIZE) - -#if _FP_W_TYPE_SIZE < 64 - -union _FP_UNION_D -{ - double flt; - struct { -#if __BYTE_ORDER == __BIG_ENDIAN - unsigned sign : 1; - unsigned exp : _FP_EXPBITS_D; - unsigned frac1 : _FP_FRACBITS_D - (_FP_IMPLBIT_D != 0) - _FP_W_TYPE_SIZE; - unsigned frac0 : _FP_W_TYPE_SIZE; -#else - unsigned frac0 : _FP_W_TYPE_SIZE; - unsigned frac1 : _FP_FRACBITS_D - (_FP_IMPLBIT_D != 0) - _FP_W_TYPE_SIZE; - unsigned exp : _FP_EXPBITS_D; - unsigned sign : 1; -#endif - } bits __attribute__((packed)); -}; - -#define FP_DECL_D(X) _FP_DECL(2,X) -#define FP_UNPACK_RAW_D(X,val) _FP_UNPACK_RAW_2(D,X,val) -#define FP_PACK_RAW_D(val,X) _FP_PACK_RAW_2(D,val,X) - -#define FP_UNPACK_D(X,val) \ - do { \ - _FP_UNPACK_RAW_2(D,X,val); \ - _FP_UNPACK_CANONICAL(D,2,X); \ - } while (0) - -#define FP_PACK_D(val,X) \ - do { \ - _FP_PACK_CANONICAL(D,2,X); \ - _FP_PACK_RAW_2(D,val,X); \ - } while (0) - -#define FP_NEG_D(R,X) _FP_NEG(D,2,R,X) -#define FP_ADD_D(R,X,Y) _FP_ADD(D,2,R,X,Y) -#define FP_SUB_D(R,X,Y) _FP_SUB(D,2,R,X,Y) -#define FP_MUL_D(R,X,Y) _FP_MUL(D,2,R,X,Y) -#define FP_DIV_D(R,X,Y) _FP_DIV(D,2,R,X,Y) -#define FP_SQRT_D(R,X) _FP_SQRT(D,2,R,X) - -#define FP_CMP_D(r,X,Y,un) _FP_CMP(D,2,r,X,Y,un) -#define FP_CMP_EQ_D(r,X,Y) _FP_CMP_EQ(D,2,r,X,Y) - -#define FP_TO_INT_D(r,X,rsz,rsg) _FP_TO_INT(D,2,r,X,rsz,rsg) -#define FP_FROM_INT_D(X,r,rs,rt) _FP_FROM_INT(D,2,X,r,rs,rt) - -#else - -union _FP_UNION_D -{ - double flt; - struct { -#if __BYTE_ORDER == __BIG_ENDIAN - unsigned sign : 1; - unsigned exp : _FP_EXPBITS_D; - unsigned long frac : _FP_FRACBITS_D - (_FP_IMPLBIT_D != 0); -#else - unsigned long frac : _FP_FRACBITS_D - (_FP_IMPLBIT_D != 0); - unsigned exp : _FP_EXPBITS_D; - unsigned sign : 1; -#endif - } bits __attribute__((packed)); -}; - -#define FP_DECL_D(X) _FP_DECL(1,X) -#define FP_UNPACK_RAW_D(X,val) _FP_UNPACK_RAW_1(D,X,val) -#define FP_PACK_RAW_D(val,X) _FP_PACK_RAW_1(D,val,X) - -#define FP_UNPACK_D(X,val) \ - do { \ - _FP_UNPACK_RAW_1(D,X,val); \ - _FP_UNPACK_CANONICAL(D,1,X); \ - } while (0) - -#define FP_PACK_D(val,X) \ - do { \ - _FP_PACK_CANONICAL(D,1,X); \ - _FP_PACK_RAW_1(D,val,X); \ - } while (0) - -#define FP_NEG_D(R,X) _FP_NEG(D,1,R,X) -#define FP_ADD_D(R,X,Y) _FP_ADD(D,1,R,X,Y) -#define FP_SUB_D(R,X,Y) _FP_SUB(D,1,R,X,Y) -#define FP_MUL_D(R,X,Y) _FP_MUL(D,1,R,X,Y) -#define FP_DIV_D(R,X,Y) _FP_DIV(D,1,R,X,Y) -#define FP_SQRT_D(R,X) _FP_SQRT(D,1,R,X) - -/* The implementation of _FP_MUL_D and _FP_DIV_D should be chosen by - the target machine. */ - -#define FP_CMP_D(r,X,Y,un) _FP_CMP(D,1,r,X,Y,un) -#define FP_CMP_EQ_D(r,X,Y) _FP_CMP_EQ(D,1,r,X,Y) - -#define FP_TO_INT_D(r,X,rsz,rsg) _FP_TO_INT(D,1,r,X,rsz,rsg) -#define FP_FROM_INT_D(X,r,rs,rt) _FP_FROM_INT(D,1,X,r,rs,rt) - -#endif /* W_TYPE_SIZE < 64 */ diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fabsq.c linux/arch/sparc64/math-emu/fabsq.c --- v2.2.13/linux/arch/sparc64/math-emu/fabsq.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fabsq.c Wed Dec 31 16:00:00 1969 @@ -1,6 +0,0 @@ -int FABSQ(unsigned long *rd, unsigned long *rs2) -{ - rd[0] = rs2[0] & 0x7fffffffffffffffUL; - rd[1] = rs2[1]; - return 0; -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/faddd.c linux/arch/sparc64/math-emu/faddd.c --- v2.2.13/linux/arch/sparc64/math-emu/faddd.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/faddd.c Wed Dec 31 16:00:00 1969 @@ -1,12 +0,0 @@ -#include "soft-fp.h" -#include "double.h" - -int FADDD(void *rd, void *rs2, void *rs1) -{ - FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R); - - __FP_UNPACK_D(A, rs1); - __FP_UNPACK_D(B, rs2); - FP_ADD_D(R, A, B); - return __FP_PACK_D(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/faddq.c linux/arch/sparc64/math-emu/faddq.c --- v2.2.13/linux/arch/sparc64/math-emu/faddq.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/faddq.c Wed Dec 31 16:00:00 1969 @@ -1,12 +0,0 @@ -#include "soft-fp.h" -#include "quad.h" - -int FADDQ(void *rd, void *rs2, void *rs1) -{ - FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R); - - __FP_UNPACK_Q(A, rs1); - __FP_UNPACK_Q(B, rs2); - FP_ADD_Q(R, A, B); - return __FP_PACK_Q(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fadds.c linux/arch/sparc64/math-emu/fadds.c --- v2.2.13/linux/arch/sparc64/math-emu/fadds.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fadds.c Wed Dec 31 16:00:00 1969 @@ -1,12 +0,0 @@ -#include "soft-fp.h" -#include "single.h" - -int FADDS(void *rd, void *rs2, void *rs1) -{ - FP_DECL_S(A); FP_DECL_S(B); FP_DECL_S(R); - - __FP_UNPACK_S(A, rs1); - __FP_UNPACK_S(B, rs2); - FP_ADD_S(R, A, B); - return __FP_PACK_S(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fcmpeq.c linux/arch/sparc64/math-emu/fcmpeq.c --- v2.2.13/linux/arch/sparc64/math-emu/fcmpeq.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fcmpeq.c Wed Dec 31 16:00:00 1969 @@ -1,25 +0,0 @@ -#include "soft-fp.h" -#include "quad.h" - -int FCMPEQ(void *rd, void *rs2, void *rs1) -{ - FP_DECL_Q(A); FP_DECL_Q(B); - long ret; - int fccno = ((long)rd) & 3; - unsigned long fsr; - - rd = (void *)(((long)rd)&~3); - __FP_UNPACK_Q(A, rs1); - __FP_UNPACK_Q(B, rs2); - FP_CMP_Q(ret, B, A, 3); - if (ret == -1) ret = 2; - fsr = *(unsigned long *)rd; - switch (fccno) { - case 0: fsr &= ~0xc00; fsr |= (ret << 10); break; - case 1: fsr &= ~0x300000000UL; fsr |= (ret << 32); break; - case 2: fsr &= ~0xc00000000UL; fsr |= (ret << 34); break; - case 3: fsr &= ~0x3000000000UL; fsr |= (ret << 36); break; - } - *(unsigned long *)rd = fsr; - return 0; -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fcmpq.c linux/arch/sparc64/math-emu/fcmpq.c --- v2.2.13/linux/arch/sparc64/math-emu/fcmpq.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fcmpq.c Wed Dec 31 16:00:00 1969 @@ -1,25 +0,0 @@ -#include "soft-fp.h" -#include "quad.h" - -int FCMPQ(void *rd, void *rs2, void *rs1) -{ - FP_DECL_Q(A); FP_DECL_Q(B); - long ret; - int fccno = ((long)rd) & 3; - unsigned long fsr; - - rd = (void *)(((long)rd)&~3); - __FP_UNPACK_Q(A, rs1); - __FP_UNPACK_Q(B, rs2); - FP_CMP_Q(ret, B, A, 3); - if (ret == -1) ret = 2; - fsr = *(unsigned long *)rd; - switch (fccno) { - case 0: fsr &= ~0xc00; fsr |= (ret << 10); break; - case 1: fsr &= ~0x300000000UL; fsr |= (ret << 32); break; - case 2: fsr &= ~0xc00000000UL; fsr |= (ret << 34); break; - case 3: fsr &= ~0x3000000000UL; fsr |= (ret << 36); break; - } - *(unsigned long *)rd = fsr; - return 0; -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fdivd.c linux/arch/sparc64/math-emu/fdivd.c --- v2.2.13/linux/arch/sparc64/math-emu/fdivd.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fdivd.c Wed Dec 31 16:00:00 1969 @@ -1,19 +0,0 @@ -#include "soft-fp.h" -#include "double.h" - -int FDIVD(void *rd, void *rs2, void *rs1) -{ - FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R); - int ret = 0; - - __FP_UNPACK_D(A, rs1); - __FP_UNPACK_D(B, rs2); - if(B_c == FP_CLS_ZERO && - A_c != FP_CLS_ZERO) { - ret |= EFLAG_DIVZERO; - if(__FPU_TRAP_P(EFLAG_DIVZERO)) - return ret; - } - FP_DIV_D(R, A, B); - return (ret | __FP_PACK_D(rd, R)); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fdivq.c linux/arch/sparc64/math-emu/fdivq.c --- v2.2.13/linux/arch/sparc64/math-emu/fdivq.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fdivq.c Wed Dec 31 16:00:00 1969 @@ -1,19 +0,0 @@ -#include "soft-fp.h" -#include "quad.h" - -int FDIVQ(void *rd, void *rs2, void *rs1) -{ - FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R); - int ret; - - __FP_UNPACK_Q(A, rs1); - __FP_UNPACK_Q(B, rs2); - if(B_c == FP_CLS_ZERO && - A_c != FP_CLS_ZERO) { - ret |= EFLAG_DIVZERO; - if(__FPU_TRAP_P(EFLAG_DIVZERO)) - return ret; - } - FP_DIV_Q(R, A, B); - return (ret | __FP_PACK_Q(rd, R)); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fdivs.c linux/arch/sparc64/math-emu/fdivs.c --- v2.2.13/linux/arch/sparc64/math-emu/fdivs.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fdivs.c Wed Dec 31 16:00:00 1969 @@ -1,20 +0,0 @@ -#include "soft-fp.h" -#include "single.h" - -int FDIVS(void *rd, void *rs2, void *rs1) -{ - FP_DECL_S(A); FP_DECL_S(B); FP_DECL_S(R); - int ret = 0; - - __FP_UNPACK_S(A, rs1); - __FP_UNPACK_S(B, rs2); - if(B_c == FP_CLS_ZERO && - A_c != FP_CLS_ZERO) { - ret |= EFLAG_DIVZERO; - if(__FPU_TRAP_P(EFLAG_DIVZERO)) - return ret; - } - FP_DIV_S(R, A, B); - return (ret | __FP_PACK_S(rd, R)); -} - diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fdmulq.c linux/arch/sparc64/math-emu/fdmulq.c --- v2.2.13/linux/arch/sparc64/math-emu/fdmulq.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fdmulq.c Wed Dec 31 16:00:00 1969 @@ -1,15 +0,0 @@ -#include "soft-fp.h" -#include "quad.h" -#include "double.h" - -int FDMULQ(void *rd, void *rs2, void *rs1) -{ - FP_DECL_D(IN); FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R); - - __FP_UNPACK_D(IN, rs1); - FP_CONV(Q,D,2,1,A,IN); - __FP_UNPACK_D(IN, rs2); - FP_CONV(Q,D,2,1,B,IN); - FP_MUL_Q(R, A, B); - return __FP_PACK_Q(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fdtoi.c linux/arch/sparc64/math-emu/fdtoi.c --- v2.2.13/linux/arch/sparc64/math-emu/fdtoi.c Mon Aug 9 16:05:55 1999 +++ linux/arch/sparc64/math-emu/fdtoi.c Wed Dec 31 16:00:00 1969 @@ -1,13 +0,0 @@ -#include "soft-fp.h" -#include "double.h" - -int FDTOI(unsigned *rd, void *rs2) -{ - FP_DECL_D(A); - unsigned r; - - __FP_UNPACK_D(A, rs2); - FP_TO_INT_D(r, A, 32, 1); - *rd = r; - return _FTOI_RESULT; -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fdtoq.c linux/arch/sparc64/math-emu/fdtoq.c --- v2.2.13/linux/arch/sparc64/math-emu/fdtoq.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fdtoq.c Wed Dec 31 16:00:00 1969 @@ -1,12 +0,0 @@ -#include "soft-fp.h" -#include "quad.h" -#include "double.h" - -int FDTOQ(void *rd, void *rs2) -{ - FP_DECL_D(A); FP_DECL_Q(R); - - __FP_UNPACK_D(A, rs2); - FP_CONV(Q,D,2,1,R,A); - return __FP_PACK_Q(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fdtos.c linux/arch/sparc64/math-emu/fdtos.c --- v2.2.13/linux/arch/sparc64/math-emu/fdtos.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fdtos.c Wed Dec 31 16:00:00 1969 @@ -1,12 +0,0 @@ -#include "soft-fp.h" -#include "double.h" -#include "single.h" - -int FDTOS(void *rd, void *rs2) -{ - FP_DECL_D(A); FP_DECL_S(R); - - __FP_UNPACK_D(A, rs2); - FP_CONV(S,D,1,1,R,A); - return __FP_PACK_S(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fdtox.c linux/arch/sparc64/math-emu/fdtox.c --- v2.2.13/linux/arch/sparc64/math-emu/fdtox.c Mon Aug 9 16:05:55 1999 +++ linux/arch/sparc64/math-emu/fdtox.c Wed Dec 31 16:00:00 1969 @@ -1,13 +0,0 @@ -#include "soft-fp.h" -#include "double.h" - -int FDTOX(unsigned long *rd, void *rs2) -{ - FP_DECL_D(A); - unsigned long r; - - __FP_UNPACK_D(A, rs2); - FP_TO_INT_D(r, A, 64, 1); - *rd = r; - return _FTOI_RESULT; -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fitoq.c linux/arch/sparc64/math-emu/fitoq.c --- v2.2.13/linux/arch/sparc64/math-emu/fitoq.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fitoq.c Wed Dec 31 16:00:00 1969 @@ -1,11 +0,0 @@ -#include "soft-fp.h" -#include "quad.h" - -int FITOQ(void *rd, void *rs2) -{ - FP_DECL_Q(R); - int a = *(int *)rs2; - - FP_FROM_INT_Q(R, a, 32, int); - return __FP_PACK_Q(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fmovq.c linux/arch/sparc64/math-emu/fmovq.c --- v2.2.13/linux/arch/sparc64/math-emu/fmovq.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fmovq.c Wed Dec 31 16:00:00 1969 @@ -1,6 +0,0 @@ -int FMOVQ(unsigned long *rd, unsigned long *rs2) -{ - rd[0] = rs2[0]; - rd[1] = rs2[1]; - return 0; -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fmuld.c linux/arch/sparc64/math-emu/fmuld.c --- v2.2.13/linux/arch/sparc64/math-emu/fmuld.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fmuld.c Wed Dec 31 16:00:00 1969 @@ -1,12 +0,0 @@ -#include "soft-fp.h" -#include "double.h" - -int FMULD(void *rd, void *rs2, void *rs1) -{ - FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R); - - __FP_UNPACK_D(A, rs1); - __FP_UNPACK_D(B, rs2); - FP_MUL_D(R, A, B); - return __FP_PACK_D(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fmulq.c linux/arch/sparc64/math-emu/fmulq.c --- v2.2.13/linux/arch/sparc64/math-emu/fmulq.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fmulq.c Wed Dec 31 16:00:00 1969 @@ -1,12 +0,0 @@ -#include "soft-fp.h" -#include "quad.h" - -int FMULQ(void *rd, void *rs2, void *rs1) -{ - FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R); - - __FP_UNPACK_Q(A, rs1); - __FP_UNPACK_Q(B, rs2); - FP_MUL_Q(R, A, B); - return __FP_PACK_Q(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fmuls.c linux/arch/sparc64/math-emu/fmuls.c --- v2.2.13/linux/arch/sparc64/math-emu/fmuls.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fmuls.c Wed Dec 31 16:00:00 1969 @@ -1,12 +0,0 @@ -#include "soft-fp.h" -#include "single.h" - -int FMULS(void *rd, void *rs2, void *rs1) -{ - FP_DECL_S(A); FP_DECL_S(B); FP_DECL_S(R); - - __FP_UNPACK_S(A, rs1); - __FP_UNPACK_S(B, rs2); - FP_MUL_S(R, A, B); - return __FP_PACK_S(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fnegq.c linux/arch/sparc64/math-emu/fnegq.c --- v2.2.13/linux/arch/sparc64/math-emu/fnegq.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fnegq.c Wed Dec 31 16:00:00 1969 @@ -1,7 +0,0 @@ -int FNEGQ(unsigned long *rd, unsigned long *rs2) -{ - rd[0] = rs2[0] ^ 0x8000000000000000UL; - rd[1] = rs2[1]; - return 0; -} - diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fqtod.c linux/arch/sparc64/math-emu/fqtod.c --- v2.2.13/linux/arch/sparc64/math-emu/fqtod.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fqtod.c Wed Dec 31 16:00:00 1969 @@ -1,12 +0,0 @@ -#include "soft-fp.h" -#include "quad.h" -#include "double.h" - -int FQTOD(void *rd, void *rs2) -{ - FP_DECL_Q(A); FP_DECL_D(R); - - __FP_UNPACK_Q(A, rs2); - FP_CONV(D,Q,1,2,R,A); - return __FP_PACK_D(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fqtoi.c linux/arch/sparc64/math-emu/fqtoi.c --- v2.2.13/linux/arch/sparc64/math-emu/fqtoi.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fqtoi.c Wed Dec 31 16:00:00 1969 @@ -1,13 +0,0 @@ -#include "soft-fp.h" -#include "quad.h" - -int FQTOI(unsigned *rd, void *rs2) -{ - FP_DECL_Q(A); - unsigned r; - - __FP_UNPACK_Q(A, rs2); - FP_TO_INT_Q(r, A, 32, 1); - *rd = r; - return 0; -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fqtos.c linux/arch/sparc64/math-emu/fqtos.c --- v2.2.13/linux/arch/sparc64/math-emu/fqtos.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fqtos.c Wed Dec 31 16:00:00 1969 @@ -1,12 +0,0 @@ -#include "soft-fp.h" -#include "quad.h" -#include "single.h" - -int FQTOS(void *rd, void *rs2) -{ - FP_DECL_Q(A); FP_DECL_S(R); - - __FP_UNPACK_Q(A, rs2); - FP_CONV(S,Q,1,2,R,A); - return __FP_PACK_S(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fqtox.c linux/arch/sparc64/math-emu/fqtox.c --- v2.2.13/linux/arch/sparc64/math-emu/fqtox.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fqtox.c Wed Dec 31 16:00:00 1969 @@ -1,13 +0,0 @@ -#include "soft-fp.h" -#include "quad.h" - -int FQTOX(unsigned long *rd, void *rs2) -{ - FP_DECL_Q(A); - unsigned long r; - - __FP_UNPACK_Q(A, rs2); - FP_TO_INT_Q(r, A, 64, 1); - *rd = r; - return 0; -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fsmuld.c linux/arch/sparc64/math-emu/fsmuld.c --- v2.2.13/linux/arch/sparc64/math-emu/fsmuld.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fsmuld.c Wed Dec 31 16:00:00 1969 @@ -1,15 +0,0 @@ -#include "soft-fp.h" -#include "double.h" -#include "single.h" - -int FSMULD(void *rd, void *rs2, void *rs1) -{ - FP_DECL_S(IN); FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R); - - __FP_UNPACK_S(IN, rs1); - FP_CONV(D,S,1,1,A,IN); - __FP_UNPACK_S(IN, rs2); - FP_CONV(D,S,1,1,B,IN); - FP_MUL_D(R, A, B); - return __FP_PACK_D(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fsqrtd.c linux/arch/sparc64/math-emu/fsqrtd.c --- v2.2.13/linux/arch/sparc64/math-emu/fsqrtd.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fsqrtd.c Wed Dec 31 16:00:00 1969 @@ -1,11 +0,0 @@ -#include "soft-fp.h" -#include "double.h" - -int FSQRTD(void *rd, void *rs2) -{ - FP_DECL_D(A); FP_DECL_D(R); - - __FP_UNPACK_D(A, rs2); - FP_SQRT_D(R, A); - return __FP_PACK_D(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fsqrtq.c linux/arch/sparc64/math-emu/fsqrtq.c --- v2.2.13/linux/arch/sparc64/math-emu/fsqrtq.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fsqrtq.c Wed Dec 31 16:00:00 1969 @@ -1,11 +0,0 @@ -#include "soft-fp.h" -#include "quad.h" - -int FSQRTQ(void *rd, void *rs2) -{ - FP_DECL_Q(A); FP_DECL_Q(R); - - __FP_UNPACK_Q(A, rs2); - FP_SQRT_Q(R, A); - return __FP_PACK_Q(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fsqrts.c linux/arch/sparc64/math-emu/fsqrts.c --- v2.2.13/linux/arch/sparc64/math-emu/fsqrts.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fsqrts.c Wed Dec 31 16:00:00 1969 @@ -1,11 +0,0 @@ -#include "soft-fp.h" -#include "single.h" - -int FSQRTS(void *rd, void *rs2) -{ - FP_DECL_S(A); FP_DECL_S(R); - - __FP_UNPACK_S(A, rs2); - FP_SQRT_S(R, A); - return __FP_PACK_S(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fstod.c linux/arch/sparc64/math-emu/fstod.c --- v2.2.13/linux/arch/sparc64/math-emu/fstod.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fstod.c Wed Dec 31 16:00:00 1969 @@ -1,12 +0,0 @@ -#include "soft-fp.h" -#include "double.h" -#include "single.h" - -int FSTOD(void *rd, void *rs2) -{ - FP_DECL_S(A); FP_DECL_D(R); - - __FP_UNPACK_S(A, rs2); - FP_CONV(D,S,1,1,R,A); - return __FP_PACK_D(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fstoi.c linux/arch/sparc64/math-emu/fstoi.c --- v2.2.13/linux/arch/sparc64/math-emu/fstoi.c Mon Aug 9 16:05:55 1999 +++ linux/arch/sparc64/math-emu/fstoi.c Wed Dec 31 16:00:00 1969 @@ -1,13 +0,0 @@ -#include "soft-fp.h" -#include "single.h" - -int FSTOI(unsigned *rd, void *rs2) -{ - FP_DECL_S(A); - unsigned r; - - __FP_UNPACK_S(A, rs2); - FP_TO_INT_S(r, A, 32, 1); - *rd = r; - return _FTOI_RESULT; -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fstoq.c linux/arch/sparc64/math-emu/fstoq.c --- v2.2.13/linux/arch/sparc64/math-emu/fstoq.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fstoq.c Wed Dec 31 16:00:00 1969 @@ -1,12 +0,0 @@ -#include "soft-fp.h" -#include "quad.h" -#include "single.h" - -int FSTOQ(void *rd, void *rs2) -{ - FP_DECL_S(A); FP_DECL_Q(R); - - __FP_UNPACK_S(A, rs2); - FP_CONV(Q,S,2,1,R,A); - return __FP_PACK_Q(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fstox.c linux/arch/sparc64/math-emu/fstox.c --- v2.2.13/linux/arch/sparc64/math-emu/fstox.c Mon Aug 9 16:05:55 1999 +++ linux/arch/sparc64/math-emu/fstox.c Wed Dec 31 16:00:00 1969 @@ -1,13 +0,0 @@ -#include "soft-fp.h" -#include "single.h" - -int FSTOX(unsigned long *rd, void *rs2) -{ - FP_DECL_S(A); - unsigned long r; - - __FP_UNPACK_S(A, rs2); - FP_TO_INT_S(r, A, 64, 1); - *rd = r; - return _FTOI_RESULT; -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fsubd.c linux/arch/sparc64/math-emu/fsubd.c --- v2.2.13/linux/arch/sparc64/math-emu/fsubd.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fsubd.c Wed Dec 31 16:00:00 1969 @@ -1,14 +0,0 @@ -#include "soft-fp.h" -#include "double.h" - -int FSUBD(void *rd, void *rs2, void *rs1) -{ - FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R); - - __FP_UNPACK_D(A, rs1); - __FP_UNPACK_D(B, rs2); - if (B_c != FP_CLS_NAN) - B_s ^= 1; - FP_ADD_D(R, A, B); - return __FP_PACK_D(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fsubq.c linux/arch/sparc64/math-emu/fsubq.c --- v2.2.13/linux/arch/sparc64/math-emu/fsubq.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fsubq.c Wed Dec 31 16:00:00 1969 @@ -1,14 +0,0 @@ -#include "soft-fp.h" -#include "quad.h" - -int FSUBQ(void *rd, void *rs2, void *rs1) -{ - FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R); - - __FP_UNPACK_Q(A, rs1); - __FP_UNPACK_Q(B, rs2); - if (B_c != FP_CLS_NAN) - B_s ^= 1; - FP_ADD_Q(R, A, B); - return __FP_PACK_Q(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fsubs.c linux/arch/sparc64/math-emu/fsubs.c --- v2.2.13/linux/arch/sparc64/math-emu/fsubs.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fsubs.c Wed Dec 31 16:00:00 1969 @@ -1,14 +0,0 @@ -#include "soft-fp.h" -#include "single.h" - -int FSUBS(void *rd, void *rs2, void *rs1) -{ - FP_DECL_S(A); FP_DECL_S(B); FP_DECL_S(R); - - __FP_UNPACK_S(A, rs1); - __FP_UNPACK_S(B, rs2); - if (B_c != FP_CLS_NAN) - B_s ^= 1; - FP_ADD_S(R, A, B); - return __FP_PACK_S(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/fxtoq.c linux/arch/sparc64/math-emu/fxtoq.c --- v2.2.13/linux/arch/sparc64/math-emu/fxtoq.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/fxtoq.c Wed Dec 31 16:00:00 1969 @@ -1,11 +0,0 @@ -#include "soft-fp.h" -#include "quad.h" - -int FXTOQ(void *rd, void *rs2) -{ - FP_DECL_Q(R); - long a = *(long *)rs2; - - FP_FROM_INT_Q(R, a, 64, long); - return __FP_PACK_Q(rd, R); -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/math.c linux/arch/sparc64/math-emu/math.c --- v2.2.13/linux/arch/sparc64/math-emu/math.c Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/math.c Tue Jan 4 10:12:13 2000 @@ -1,7 +1,7 @@ -/* $Id: math.c,v 1.7 1999/02/10 14:16:26 davem Exp $ +/* $Id: math.c,v 1.7.2.2 1999/11/19 08:24:37 jj Exp $ * arch/sparc64/math-emu/math.c * - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1999 Jakub Jelinek (jj@ultra.linux.cz) * Copyright (C) 1999 David S. Miller (davem@redhat.com) * * Emulation routines originate from soft-fp package, which is part @@ -15,47 +15,63 @@ #include #include +#include "sfp-util.h" #include "soft-fp.h" - -#define FLOATFUNC(x) extern int x(void *,void *,void *); - -FLOATFUNC(FMOVQ) -FLOATFUNC(FNEGQ) -FLOATFUNC(FABSQ) -FLOATFUNC(FSQRTQ) -FLOATFUNC(FADDQ) -FLOATFUNC(FSUBQ) -FLOATFUNC(FMULQ) -FLOATFUNC(FDIVQ) -FLOATFUNC(FDMULQ) -FLOATFUNC(FQTOX) -FLOATFUNC(FXTOQ) -FLOATFUNC(FQTOS) -FLOATFUNC(FQTOD) -FLOATFUNC(FITOQ) -FLOATFUNC(FSTOQ) -FLOATFUNC(FDTOQ) -FLOATFUNC(FQTOI) -FLOATFUNC(FCMPQ) -FLOATFUNC(FCMPEQ) - -FLOATFUNC(FSQRTS) -FLOATFUNC(FSQRTD) -FLOATFUNC(FADDS) -FLOATFUNC(FADDD) -FLOATFUNC(FSUBS) -FLOATFUNC(FSUBD) -FLOATFUNC(FMULS) -FLOATFUNC(FMULD) -FLOATFUNC(FDIVS) -FLOATFUNC(FDIVD) -FLOATFUNC(FSMULD) -FLOATFUNC(FSTOX) -FLOATFUNC(FDTOX) -FLOATFUNC(FDTOS) -FLOATFUNC(FSTOD) -FLOATFUNC(FSTOI) -FLOATFUNC(FDTOI) +#include "single.h" +#include "double.h" +#include "quad.h" + +/* QUAD - ftt == 3 */ +#define FMOVQ 0x003 +#define FNEGQ 0x007 +#define FABSQ 0x00b +#define FSQRTQ 0x02b +#define FADDQ 0x043 +#define FSUBQ 0x047 +#define FMULQ 0x04b +#define FDIVQ 0x04f +#define FDMULQ 0x06e +#define FQTOX 0x083 +#define FXTOQ 0x08c +#define FQTOS 0x0c7 +#define FQTOD 0x0cb +#define FITOQ 0x0cc +#define FSTOQ 0x0cd +#define FDTOQ 0x0ce +#define FQTOI 0x0d3 +/* SUBNORMAL - ftt == 2 */ +#define FSQRTS 0x029 +#define FSQRTD 0x02a +#define FADDS 0x041 +#define FADDD 0x042 +#define FSUBS 0x045 +#define FSUBD 0x046 +#define FMULS 0x049 +#define FMULD 0x04a +#define FDIVS 0x04d +#define FDIVD 0x04e +#define FSMULD 0x069 +#define FSTOX 0x081 +#define FDTOX 0x082 +#define FDTOS 0x0c6 +#define FSTOD 0x0c9 +#define FSTOI 0x0d1 +#define FDTOI 0x0d2 +/* FPOP2 */ +#define FCMPQ 0x053 +#define FCMPEQ 0x057 +#define FMOVQ0 0x003 +#define FMOVQ1 0x043 +#define FMOVQ2 0x083 +#define FMOVQ3 0x0c3 +#define FMOVQI 0x103 +#define FMOVQX 0x183 +#define FMOVQZ 0x027 +#define FMOVQLE 0x047 +#define FMOVQLZ 0x067 +#define FMOVQNZ 0x0a7 +#define FMOVQGZ 0x0c7 +#define FMOVQGE 0x0e7 #define FSR_TEM_SHIFT 23UL #define FSR_TEM_MASK (0x1fUL << FSR_TEM_SHIFT) @@ -72,7 +88,7 @@ * * We return 0 if a SIGFPE should be sent, 1 otherwise. */ -static int record_exception(struct pt_regs *regs, int eflag) +static inline int record_exception(struct pt_regs *regs, int eflag) { u64 fsr = current->tss.xfsr[0]; int would_trap; @@ -84,46 +100,36 @@ if(would_trap != 0) { eflag &= ((fsr & FSR_TEM_MASK) >> FSR_TEM_SHIFT); if((eflag & (eflag - 1)) != 0) { - if(eflag & EFLAG_INVALID) - eflag = EFLAG_INVALID; - else if(eflag & EFLAG_DIVZERO) - eflag = EFLAG_DIVZERO; - else if(eflag & EFLAG_INEXACT) - eflag = EFLAG_INEXACT; + if(eflag & FP_EX_INVALID) + eflag = FP_EX_INVALID; + else if(eflag & FP_EX_OVERFLOW) + eflag = FP_EX_OVERFLOW; + else if(eflag & FP_EX_UNDERFLOW) + eflag = FP_EX_UNDERFLOW; + else if(eflag & FP_EX_DIVZERO) + eflag = FP_EX_DIVZERO; + else if(eflag & FP_EX_INEXACT) + eflag = FP_EX_INEXACT; } } - /* Set CEXC, here are the rules: + /* Set CEXC, here is the rule: * - * 1) In general all FPU ops will set one and only one + * In general all FPU ops will set one and only one * bit in the CEXC field, this is always the case * when the IEEE exception trap is enabled in TEM. - * - * 2) As a special case, if an overflow or underflow - * is being signalled, AND the trap is not enabled - * in TEM, then the inexact field shall also be set. */ fsr &= ~(FSR_CEXC_MASK); - if(would_trap || - (eflag & (EFLAG_OVERFLOW | EFLAG_UNDERFLOW)) == 0) { - fsr |= ((long)eflag << FSR_CEXC_SHIFT); - } else { - fsr |= (((long)eflag << FSR_CEXC_SHIFT) | - (EFLAG_INEXACT << FSR_CEXC_SHIFT)); - } + fsr |= ((long)eflag << FSR_CEXC_SHIFT); - /* Set the AEXC field, rules are: + /* Set the AEXC field, rule is: * - * 1) If a trap would not be generated, the + * If a trap would not be generated, the * CEXC just generated is OR'd into the * existing value of AEXC. - * - * 2) When a trap is generated, AEXC is cleared. */ if(would_trap == 0) fsr |= ((long)eflag << FSR_AEXC_SHIFT); - else - fsr &= ~(FSR_AEXC_MASK); /* If trapping, indicate fault trap type IEEE. */ if(would_trap != 0) @@ -142,18 +148,31 @@ return (would_trap ? 0 : 1); } +typedef union { + u32 s; + u64 d; + u64 q[2]; +} *argp; + int do_mathemu(struct pt_regs *regs, struct fpustate *f) { unsigned long pc = regs->tpc; unsigned long tstate = regs->tstate; u32 insn = 0; - int type = 0; /* 01 is single, 10 is double, 11 is quad, - 000011 is rs1, 001100 is rs2, 110000 is rd (00 in rd is fcc) - 111100000000 tells which ftt may that happen in */ + int type = 0; + /* ftt tells which ftt it may happen in, r is rd, b is rs2 and a is rs1. The *u arg tells + whether the argument should be packed/unpacked (0 - do not unpack/pack, 1 - unpack/pack) + non-u args tells the size of the argument (0 - no argument, 1 - single, 2 - double, 3 - quad */ +#define TYPE(ftt, r, ru, b, bu, a, au) type = (au << 2) | (a << 0) | (bu << 5) | (b << 3) | (ru << 8) | (r << 6) | (ftt << 9) int freg; static u64 zero[2] = { 0L, 0L }; int flags; - int (*func)(void *,void *,void *) = NULL; + FP_DECL_EX; + FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR); + FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR); + FP_DECL_Q(QA); FP_DECL_Q(QB); FP_DECL_Q(QR); + int IR; + long XR, xfsr; if(tstate & TSTATE_PRIV) die_if_kernel("FPQuad from kernel", regs); @@ -163,55 +182,145 @@ if ((insn & 0xc1f80000) == 0x81a00000) /* FPOP1 */ { switch ((insn >> 5) & 0x1ff) { /* QUAD - ftt == 3 */ - case 0x003: type = 0x33c; func = FMOVQ; break; - case 0x007: type = 0x33c; func = FNEGQ; break; - case 0x00b: type = 0x33c; func = FABSQ; break; - case 0x02b: type = 0x33c; func = FSQRTQ; break; - case 0x043: type = 0x33f; func = FADDQ; break; - case 0x047: type = 0x33f; func = FSUBQ; break; - case 0x04b: type = 0x33f; func = FMULQ; break; - case 0x04f: type = 0x33f; func = FDIVQ; break; - case 0x06e: type = 0x33a; func = FDMULQ; break; - case 0x083: type = 0x32c; func = FQTOX; break; - case 0x08c: type = 0x338; func = FXTOQ; break; - case 0x0c7: type = 0x31c; func = FQTOS; break; - case 0x0cb: type = 0x32c; func = FQTOD; break; - case 0x0cc: type = 0x334; func = FITOQ; break; - case 0x0cd: type = 0x334; func = FSTOQ; break; - case 0x0ce: type = 0x338; func = FDTOQ; break; - case 0x0d3: type = 0x31c; func = FQTOI; break; + case FMOVQ: + case FNEGQ: + case FABSQ: TYPE(3,3,0,3,0,0,0); break; + case FSQRTQ: TYPE(3,3,1,3,1,0,0); break; + case FADDQ: + case FSUBQ: + case FMULQ: + case FDIVQ: TYPE(3,3,1,3,1,3,1); break; + case FDMULQ: TYPE(3,3,1,2,1,2,1); break; + case FQTOX: TYPE(3,2,0,3,1,0,0); break; + case FXTOQ: TYPE(3,3,1,2,0,0,0); break; + case FQTOS: TYPE(3,1,1,3,1,0,0); break; + case FQTOD: TYPE(3,2,1,3,1,0,0); break; + case FITOQ: TYPE(3,3,1,1,0,0,0); break; + case FSTOQ: TYPE(3,3,1,1,1,0,0); break; + case FDTOQ: TYPE(3,3,1,2,1,0,0); break; + case FQTOI: TYPE(3,1,0,3,1,0,0); break; /* SUBNORMAL - ftt == 2 */ - case 0x029: type = 0x214; func = FSQRTS; break; - case 0x02a: type = 0x228; func = FSQRTD; break; - case 0x041: type = 0x215; func = FADDS; break; - case 0x042: type = 0x22a; func = FADDD; break; - case 0x045: type = 0x215; func = FSUBS; break; - case 0x046: type = 0x22a; func = FSUBD; break; - case 0x049: type = 0x215; func = FMULS; break; - case 0x04a: type = 0x22a; func = FMULD; break; - case 0x04d: type = 0x215; func = FDIVS; break; - case 0x04e: type = 0x22a; func = FDIVD; break; - case 0x069: type = 0x225; func = FSMULD; break; - case 0x081: type = 0x224; func = FSTOX; break; - case 0x082: type = 0x228; func = FDTOX; break; - case 0x0c6: type = 0x218; func = FDTOS; break; - case 0x0c9: type = 0x224; func = FSTOD; break; - case 0x0d1: type = 0x214; func = FSTOI; break; - case 0x0d2: type = 0x218; func = FDTOI; break; + case FSQRTS: TYPE(2,1,1,1,1,0,0); break; + case FSQRTD: TYPE(2,2,1,2,1,0,0); break; + case FADDD: + case FSUBD: + case FMULD: + case FDIVD: TYPE(2,2,1,2,1,2,1); break; + case FADDS: + case FSUBS: + case FMULS: + case FDIVS: TYPE(2,1,1,1,1,1,1); break; + case FSMULD: TYPE(2,2,1,1,1,1,1); break; + case FSTOX: TYPE(2,2,0,1,1,0,0); break; + case FDTOX: TYPE(2,2,0,2,1,0,0); break; + case FDTOS: TYPE(2,1,1,2,1,0,0); break; + case FSTOD: TYPE(2,2,1,1,1,0,0); break; + case FSTOI: TYPE(2,1,0,1,1,0,0); break; + case FDTOI: TYPE(2,1,0,2,1,0,0); break; } } else if ((insn & 0xc1f80000) == 0x81a80000) /* FPOP2 */ { + IR = 2; switch ((insn >> 5) & 0x1ff) { - case 0x053: type = 0x30f; func = FCMPQ; break; - case 0x057: type = 0x30f; func = FCMPEQ; break; + case FCMPQ: TYPE(3,0,0,3,0,3,0); break; + case FCMPEQ: TYPE(3,0,0,3,0,3,0); break; + /* Now the conditional fmovq support */ + case FMOVQ0: + case FMOVQ1: + case FMOVQ2: + case FMOVQ3: + /* fmovq %fccX, %fY, %fZ */ + if (!((insn >> 11) & 3)) + XR = current->tss.xfsr[0] >> 10; + else + XR = current->tss.xfsr[0] >> (30 + ((insn >> 10) & 0x6)); + XR &= 3; + IR = 0; + switch ((insn >> 14) & 0x7) { + /* case 0: IR = 0; break; */ /* Never */ + case 1: if (XR) IR = 1; break; /* Not Equal */ + case 2: if (XR == 1 || XR == 2) IR = 1; break; /* Less or Greater */ + case 3: if (XR & 1) IR = 1; break; /* Unordered or Less */ + case 4: if (XR == 1) IR = 1; break; /* Less */ + case 5: if (XR & 2) IR = 1; break; /* Unordered or Greater */ + case 6: if (XR == 2) IR = 1; break; /* Greater */ + case 7: if (XR == 3) IR = 1; break; /* Unordered */ + } + if ((insn >> 14) & 8) + IR ^= 1; + break; + case FMOVQI: + case FMOVQX: + /* fmovq %[ix]cc, %fY, %fZ */ + XR = regs->tstate >> 32; + if ((insn >> 5) & 0x80) + XR >>= 4; + XR &= 0xf; + IR = 0; + freg = ((XR >> 2) ^ XR) & 2; + switch ((insn >> 14) & 0x7) { + /* case 0: IR = 0; break; */ /* Never */ + case 1: if (XR & 4) IR = 1; break; /* Equal */ + case 2: if ((XR & 4) || freg) IR = 1; break; /* Less or Equal */ + case 3: if (freg) IR = 1; break; /* Less */ + case 4: if (XR & 5) IR = 1; break; /* Less or Equal Unsigned */ + case 5: if (XR & 1) IR = 1; break; /* Carry Set */ + case 6: if (XR & 8) IR = 1; break; /* Negative */ + case 7: if (XR & 2) IR = 1; break; /* Overflow Set */ + } + if ((insn >> 14) & 8) + IR ^= 1; + break; + case FMOVQZ: + case FMOVQLE: + case FMOVQLZ: + case FMOVQNZ: + case FMOVQGZ: + case FMOVQGE: + freg = (insn >> 14) & 0x1f; + if (!freg) + XR = 0; + else if (freg < 16) + XR = regs->u_regs[freg]; + else if (current->tss.flags & SPARC_FLAG_32BIT) { + struct reg_window32 *win32; + flushw_user (); + win32 = (struct reg_window32 *)((unsigned long)((u32)regs->u_regs[UREG_FP])); + get_user(XR, &win32->locals[freg - 16]); + } else { + struct reg_window *win; + flushw_user (); + win = (struct reg_window *)(regs->u_regs[UREG_FP] + STACK_BIAS); + get_user(XR, &win->locals[freg - 16]); + } + IR = 0; + switch ((insn >> 10) & 3) { + case 1: if (!XR) IR = 1; break; /* Register Zero */ + case 2: if (XR <= 0) IR = 1; break; /* Register Less Than or Equal to Zero */ + case 3: if (XR < 0) IR = 1; break; /* Register Less Than Zero */ + } + if ((insn >> 10) & 4) + IR ^= 1; + break; + } + if (IR == 0) { + /* The fmov test was false. Do a nop instead */ + current->tss.xfsr[0] &= ~(FSR_CEXC_MASK); + regs->tpc = regs->tnpc; + regs->tnpc += 4; + return 1; + } else if (IR == 1) { + /* Change the instruction into plain fmovq */ + insn = (insn & 0x3e00001f) | 0x81a00060; + TYPE(3,3,0,3,0,0,0); } } } if (type) { - void *rs1 = NULL, *rs2 = NULL, *rd = NULL; + argp rs1 = NULL, rs2 = NULL, rd = NULL; freg = (current->tss.xfsr[0] >> 14) & 0xf; - if (freg != (type >> 8)) + if (freg != (type >> 9)) goto err; current->tss.xfsr[0] &= ~0x1c000; freg = ((insn >> 14) & 0x1f); @@ -221,34 +330,43 @@ goto err; } case 2: freg = ((freg & 1) << 5) | (freg & 0x1e); - case 1: rs1 = (void *)&f->regs[freg]; + case 1: rs1 = (argp)&f->regs[freg]; flags = (freg < 32) ? FPRS_DL : FPRS_DU; if (!(current->tss.fpsaved[0] & flags)) - rs1 = (void *)&zero; + rs1 = (argp)&zero; break; } + switch (type & 0x7) { + case 7: FP_UNPACK_QP (QA, rs1); break; + case 6: FP_UNPACK_DP (DA, rs1); break; + case 5: FP_UNPACK_SP (SA, rs1); break; + } freg = (insn & 0x1f); - switch ((type >> 2) & 0x3) { + switch ((type >> 3) & 0x3) { case 3: if (freg & 2) { current->tss.xfsr[0] |= (6 << 14) /* invalid_fp_register */; goto err; } case 2: freg = ((freg & 1) << 5) | (freg & 0x1e); - case 1: rs2 = (void *)&f->regs[freg]; + case 1: rs2 = (argp)&f->regs[freg]; flags = (freg < 32) ? FPRS_DL : FPRS_DU; if (!(current->tss.fpsaved[0] & flags)) - rs2 = (void *)&zero; + rs2 = (argp)&zero; break; } + switch ((type >> 3) & 0x7) { + case 7: FP_UNPACK_QP (QB, rs2); break; + case 6: FP_UNPACK_DP (DB, rs2); break; + case 5: FP_UNPACK_SP (SB, rs2); break; + } freg = ((insn >> 25) & 0x1f); - switch ((type >> 4) & 0x3) { - case 0: rd = (void *)(((long)¤t->tss.xfsr[0]) | (freg & 3)); break; + switch ((type >> 6) & 0x3) { case 3: if (freg & 2) { current->tss.xfsr[0] |= (6 << 14) /* invalid_fp_register */; goto err; } case 2: freg = ((freg & 1) << 5) | (freg & 0x1e); - case 1: rd = (void *)&f->regs[freg]; + case 1: rd = (argp)&f->regs[freg]; flags = (freg < 32) ? FPRS_DL : FPRS_DU; if (!(current->tss.fpsaved[0] & FPRS_FEF)) { current->tss.fpsaved[0] = FPRS_FEF; @@ -263,9 +381,87 @@ current->tss.fpsaved[0] |= flags; break; } - flags = func(rd, rs2, rs1); - if(flags != 0) - return record_exception(regs, flags); + switch ((insn >> 5) & 0x1ff) { + /* + */ + case FADDS: FP_ADD_S (SR, SA, SB); break; + case FADDD: FP_ADD_D (DR, DA, DB); break; + case FADDQ: FP_ADD_Q (QR, QA, QB); break; + /* - */ + case FSUBS: FP_SUB_S (SR, SA, SB); break; + case FSUBD: FP_SUB_D (DR, DA, DB); break; + case FSUBQ: FP_SUB_Q (QR, QA, QB); break; + /* * */ + case FMULS: FP_MUL_S (SR, SA, SB); break; + case FSMULD: FP_CONV (D, S, 1, 1, DA, SA); + FP_CONV (D, S, 1, 1, DB, SB); + case FMULD: FP_MUL_D (DR, DA, DB); break; + case FDMULQ: FP_CONV (Q, D, 2, 1, QA, DA); + FP_CONV (Q, D, 2, 1, QB, DB); + case FMULQ: FP_MUL_Q (QR, QA, QB); break; + /* / */ + case FDIVS: FP_DIV_S (SR, SA, SB); break; + case FDIVD: FP_DIV_D (DR, DA, DB); break; + case FDIVQ: FP_DIV_Q (QR, QA, QB); break; + /* sqrt */ + case FSQRTS: FP_SQRT_S (SR, SB); break; + case FSQRTD: FP_SQRT_D (DR, DB); break; + case FSQRTQ: FP_SQRT_Q (QR, QB); break; + /* mov */ + case FMOVQ: rd->q[0] = rs2->q[0]; rd->q[1] = rs2->q[1]; break; + case FABSQ: rd->q[0] = rs2->q[0] & 0x7fffffffffffffffUL; rd->q[1] = rs2->q[1]; break; + case FNEGQ: rd->q[0] = rs2->q[0] ^ 0x8000000000000000UL; rd->q[1] = rs2->q[1]; break; + /* float to int */ + case FSTOI: FP_TO_INT_S (IR, SB, 32, 1); break; + case FDTOI: FP_TO_INT_D (IR, DB, 32, 1); break; + case FQTOI: FP_TO_INT_Q (IR, QB, 32, 1); break; + case FSTOX: FP_TO_INT_S (XR, SB, 64, 1); break; + case FDTOX: FP_TO_INT_D (XR, DB, 64, 1); break; + case FQTOX: FP_TO_INT_Q (XR, QB, 64, 1); break; + /* int to float */ + case FITOQ: IR = rs2->s; FP_FROM_INT_Q (QR, IR, 32, int); break; + case FXTOQ: XR = rs2->d; FP_FROM_INT_Q (QR, XR, 64, long); break; + /* float to float */ + case FSTOD: FP_CONV (D, S, 1, 1, DR, SB); break; + case FSTOQ: FP_CONV (Q, S, 2, 1, QR, SB); break; + case FDTOQ: FP_CONV (Q, D, 2, 1, QR, DB); break; + case FDTOS: FP_CONV (S, D, 1, 1, SR, DB); break; + case FQTOS: FP_CONV (S, Q, 1, 2, SR, QB); break; + case FQTOD: FP_CONV (D, Q, 1, 2, DR, QB); break; + /* comparison */ + case FCMPQ: + case FCMPEQ: + FP_UNPACK_RAW_QP (QA, rs1); + FP_UNPACK_RAW_QP (QB, rs2); + FP_CMP_Q(XR, QB, QA, 3); + if (XR == 3 && + (((insn >> 5) & 0x1ff) == FCMPEQ || + FP_ISSIGNAN_Q(QA) || + FP_ISSIGNAN_Q(QB))) + FP_SET_EXCEPTION (FP_EX_INVALID); + } + if (!FP_INHIBIT_RESULTS) { + switch ((type >> 6) & 0x7) { + case 0: xfsr = current->tss.xfsr[0]; + if (XR == -1) XR = 2; + switch (freg & 3) { + /* fcc0, 1, 2, 3 */ + case 0: xfsr &= ~0xc00; xfsr |= (XR << 10); break; + case 1: xfsr &= ~0x300000000UL; xfsr |= (XR << 32); break; + case 2: xfsr &= ~0xc00000000UL; xfsr |= (XR << 34); break; + case 3: xfsr &= ~0x3000000000UL; xfsr |= (XR << 36); break; + } + current->tss.xfsr[0] = xfsr; + break; + case 1: rd->s = IR; break; + case 2: rd->d = XR; break; + case 5: FP_PACK_SP (rd, SR); break; + case 6: FP_PACK_DP (rd, DR); break; + case 7: FP_PACK_QP (rd, QR); break; + } + } + + if(_fex != 0) + return record_exception(regs, _fex); /* Success and no exceptions detected. */ current->tss.xfsr[0] &= ~(FSR_CEXC_MASK); diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/op-1.h linux/arch/sparc64/math-emu/op-1.h --- v2.2.13/linux/arch/sparc64/math-emu/op-1.h Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/op-1.h Wed Dec 31 16:00:00 1969 @@ -1,245 +0,0 @@ -/* - * Basic one-word fraction declaration and manipulation. - */ - -#define _FP_FRAC_DECL_1(X) _FP_W_TYPE X##_f -#define _FP_FRAC_COPY_1(D,S) (D##_f = S##_f) -#define _FP_FRAC_SET_1(X,I) (X##_f = I) -#define _FP_FRAC_HIGH_1(X) (X##_f) -#define _FP_FRAC_LOW_1(X) (X##_f) -#define _FP_FRAC_WORD_1(X,w) (X##_f) - -#define _FP_FRAC_ADDI_1(X,I) (X##_f += I) -#define _FP_FRAC_SLL_1(X,N) \ - do { \ - if (__builtin_constant_p(N) && (N) == 1) \ - X##_f += X##_f; \ - else \ - X##_f <<= (N); \ - } while (0) -#define _FP_FRAC_SRL_1(X,N) (X##_f >>= N) - -/* Right shift with sticky-lsb. */ -#define _FP_FRAC_SRS_1(X,N,sz) __FP_FRAC_SRS_1(X##_f, N, sz) - -#define __FP_FRAC_SRS_1(X,N,sz) \ - (X = (X >> (N) | (__builtin_constant_p(N) && (N) == 1 \ - ? X & 1 : (X << (_FP_W_TYPE_SIZE - (N))) != 0))) - -#define _FP_FRAC_ADD_1(R,X,Y) (R##_f = X##_f + Y##_f) -#define _FP_FRAC_SUB_1(R,X,Y) (R##_f = X##_f - Y##_f) -#define _FP_FRAC_CLZ_1(z, X) __FP_CLZ(z, X##_f) - -/* Predicates */ -#define _FP_FRAC_NEGP_1(X) ((_FP_WS_TYPE)X##_f < 0) -#define _FP_FRAC_ZEROP_1(X) (X##_f == 0) -#define _FP_FRAC_OVERP_1(fs,X) (X##_f & _FP_OVERFLOW_##fs) -#define _FP_FRAC_EQ_1(X, Y) (X##_f == Y##_f) -#define _FP_FRAC_GE_1(X, Y) (X##_f >= Y##_f) -#define _FP_FRAC_GT_1(X, Y) (X##_f > Y##_f) - -#define _FP_ZEROFRAC_1 0 -#define _FP_MINFRAC_1 1 - -/* - * Unpack the raw bits of a native fp value. Do not classify or - * normalize the data. - */ - -#define _FP_UNPACK_RAW_1(fs, X, val) \ - do { \ - union _FP_UNION_##fs _flo; _flo.flt = (val); \ - \ - X##_f = _flo.bits.frac; \ - X##_e = _flo.bits.exp; \ - X##_s = _flo.bits.sign; \ - } while (0) - - -/* - * Repack the raw bits of a native fp value. - */ - -#define _FP_PACK_RAW_1(fs, val, X) \ - do { \ - union _FP_UNION_##fs _flo; \ - \ - _flo.bits.frac = X##_f; \ - _flo.bits.exp = X##_e; \ - _flo.bits.sign = X##_s; \ - \ - (val) = _flo.flt; \ - } while (0) - - -/* - * Multiplication algorithms: - */ - -/* Basic. Assuming the host word size is >= 2*FRACBITS, we can do the - multiplication immediately. */ - -#define _FP_MUL_MEAT_1_imm(fs, R, X, Y) \ - do { \ - R##_f = X##_f * Y##_f; \ - /* Normalize since we know where the msb of the multiplicands \ - were (bit B), we know that the msb of the of the product is \ - at either 2B or 2B-1. */ \ - _FP_FRAC_SRS_1(R, _FP_WFRACBITS_##fs-1, 2*_FP_WFRACBITS_##fs); \ - } while (0) - -/* Given a 1W * 1W => 2W primitive, do the extended multiplication. */ - -#define _FP_MUL_MEAT_1_wide(fs, R, X, Y, doit) \ - do { \ - _FP_W_TYPE _Z_f0, _Z_f1; \ - doit(_Z_f1, _Z_f0, X##_f, Y##_f); \ - /* Normalize since we know where the msb of the multiplicands \ - were (bit B), we know that the msb of the of the product is \ - at either 2B or 2B-1. */ \ - _FP_FRAC_SRS_2(_Z, _FP_WFRACBITS_##fs-1, 2*_FP_WFRACBITS_##fs); \ - R##_f = _Z_f0; \ - } while (0) - -/* Finally, a simple widening multiply algorithm. What fun! */ - -#define _FP_MUL_MEAT_1_hard(fs, R, X, Y) \ - do { \ - _FP_W_TYPE _xh, _xl, _yh, _yl, _z_f0, _z_f1, _a_f0, _a_f1; \ - \ - /* split the words in half */ \ - _xh = X##_f >> (_FP_W_TYPE_SIZE/2); \ - _xl = X##_f & (((_FP_W_TYPE)1 << (_FP_W_TYPE_SIZE/2)) - 1); \ - _yh = Y##_f >> (_FP_W_TYPE_SIZE/2); \ - _yl = Y##_f & (((_FP_W_TYPE)1 << (_FP_W_TYPE_SIZE/2)) - 1); \ - \ - /* multiply the pieces */ \ - _z_f0 = _xl * _yl; \ - _a_f0 = _xh * _yl; \ - _a_f1 = _xl * _yh; \ - _z_f1 = _xh * _yh; \ - \ - /* reassemble into two full words */ \ - if ((_a_f0 += _a_f1) < _a_f1) \ - _z_f1 += (_FP_W_TYPE)1 << (_FP_W_TYPE_SIZE/2); \ - _a_f1 = _a_f0 >> (_FP_W_TYPE_SIZE/2); \ - _a_f0 = _a_f0 << (_FP_W_TYPE_SIZE/2); \ - _FP_FRAC_ADD_2(_z, _z, _a); \ - \ - /* normalize */ \ - _FP_FRAC_SRS_2(_z, _FP_WFRACBITS_##fs - 1, 2*_FP_WFRACBITS_##fs); \ - R##_f = _z_f0; \ - } while (0) - - -/* - * Division algorithms: - */ - -/* Basic. Assuming the host word size is >= 2*FRACBITS, we can do the - division immediately. Give this macro either _FP_DIV_HELP_imm for - C primitives or _FP_DIV_HELP_ldiv for the ISO function. Which you - choose will depend on what the compiler does with divrem4. */ - -#define _FP_DIV_MEAT_1_imm(fs, R, X, Y, doit) \ - do { \ - _FP_W_TYPE _q, _r; \ - X##_f <<= (X##_f < Y##_f \ - ? R##_e--, _FP_WFRACBITS_##fs \ - : _FP_WFRACBITS_##fs - 1); \ - doit(_q, _r, X##_f, Y##_f); \ - R##_f = _q | (_r != 0); \ - } while (0) - -/* GCC's longlong.h defines a 2W / 1W => (1W,1W) primitive udiv_qrnnd - that may be useful in this situation. This first is for a primitive - that requires normalization, the second for one that does not. Look - for UDIV_NEEDS_NORMALIZATION to tell which your machine needs. */ - -#define _FP_DIV_MEAT_1_udiv_norm(fs, R, X, Y) \ - do { \ - _FP_W_TYPE _nh, _nl, _q, _r; \ - \ - /* Normalize Y -- i.e. make the most significant bit set. */ \ - Y##_f <<= _FP_WFRACXBITS_##fs - 1; \ - \ - /* Shift X op correspondingly high, that is, up one full word. */ \ - if (X##_f <= Y##_f) \ - { \ - _nl = 0; \ - _nh = X##_f; \ - } \ - else \ - { \ - R##_e++; \ - _nl = X##_f << (_FP_W_TYPE_SIZE-1); \ - _nh = X##_f >> 1; \ - } \ - \ - udiv_qrnnd(_q, _r, _nh, _nl, Y##_f); \ - R##_f = _q | (_r != 0); \ - } while (0) - -#define _FP_DIV_MEAT_1_udiv(fs, R, X, Y) \ - do { \ - _FP_W_TYPE _nh, _nl, _q, _r; \ - if (X##_f < Y##_f) \ - { \ - R##_e--; \ - _nl = X##_f << _FP_WFRACBITS_##fs; \ - _nh = X##_f >> _FP_WFRACXBITS_##fs; \ - } \ - else \ - { \ - _nl = X##_f << (_FP_WFRACBITS_##fs - 1); \ - _nh = X##_f >> (_FP_WFRACXBITS_##fs + 1); \ - } \ - udiv_qrnnd(_q, _r, _nh, _nl, Y##_f); \ - R##_f = _q | (_r != 0); \ - } while (0) - - -/* - * Square root algorithms: - * We have just one right now, maybe Newton approximation - * should be added for those machines where division is fast. - */ - -#define _FP_SQRT_MEAT_1(R, S, T, X, q) \ - do { \ - while (q) \ - { \ - T##_f = S##_f + q; \ - if (T##_f <= X##_f) \ - { \ - S##_f = T##_f + q; \ - X##_f -= T##_f; \ - R##_f += q; \ - } \ - _FP_FRAC_SLL_1(X, 1); \ - q >>= 1; \ - } \ - } while (0) - -/* - * Assembly/disassembly for converting to/from integral types. - * No shifting or overflow handled here. - */ - -#define _FP_FRAC_ASSEMBLE_1(r, X, rsize) (r = X##_f) -#define _FP_FRAC_DISASSEMBLE_1(X, r, rsize) (X##_f = r) - - -/* - * Convert FP values between word sizes - */ - -#define _FP_FRAC_CONV_1_1(dfs, sfs, D, S) \ - do { \ - D##_f = S##_f; \ - if (_FP_WFRACBITS_##sfs > _FP_WFRACBITS_##dfs) \ - _FP_FRAC_SRS_1(D, (_FP_WFRACBITS_##sfs-_FP_WFRACBITS_##dfs), \ - _FP_WFRACBITS_##sfs); \ - else \ - D##_f <<= _FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs; \ - } while (0) diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/op-2.h linux/arch/sparc64/math-emu/op-2.h --- v2.2.13/linux/arch/sparc64/math-emu/op-2.h Tue Oct 19 17:10:36 1999 +++ linux/arch/sparc64/math-emu/op-2.h Wed Dec 31 16:00:00 1969 @@ -1,433 +0,0 @@ -/* - * Basic two-word fraction declaration and manipulation. - */ - -#define _FP_FRAC_DECL_2(X) _FP_W_TYPE X##_f0, X##_f1 -#define _FP_FRAC_COPY_2(D,S) (D##_f0 = S##_f0, D##_f1 = S##_f1) -#define _FP_FRAC_SET_2(X,I) __FP_FRAC_SET_2(X, I) -#define _FP_FRAC_HIGH_2(X) (X##_f1) -#define _FP_FRAC_LOW_2(X) (X##_f0) -#define _FP_FRAC_WORD_2(X,w) (X##_f##w) - -#define _FP_FRAC_SLL_2(X,N) \ - do { \ - if ((N) < _FP_W_TYPE_SIZE) \ - { \ - if (__builtin_constant_p(N) && (N) == 1) \ - { \ - X##_f1 = X##_f1 + X##_f1 + (((_FP_WS_TYPE)(X##_f0)) < 0); \ - X##_f0 += X##_f0; \ - } \ - else \ - { \ - X##_f1 = X##_f1 << (N) | X##_f0 >> (_FP_W_TYPE_SIZE - (N)); \ - X##_f0 <<= (N); \ - } \ - } \ - else \ - { \ - X##_f1 = X##_f0 << ((N) - _FP_W_TYPE_SIZE); \ - X##_f0 = 0; \ - } \ - } while (0) - -#define _FP_FRAC_SRL_2(X,N) \ - do { \ - if ((N) < _FP_W_TYPE_SIZE) \ - { \ - X##_f0 = X##_f0 >> (N) | X##_f1 << (_FP_W_TYPE_SIZE - (N)); \ - X##_f1 >>= (N); \ - } \ - else \ - { \ - X##_f0 = X##_f1 >> ((N) - _FP_W_TYPE_SIZE); \ - X##_f1 = 0; \ - } \ - } while (0) - -/* Right shift with sticky-lsb. */ -#define _FP_FRAC_SRS_2(X,N,sz) \ - do { \ - if ((N) < _FP_W_TYPE_SIZE) \ - { \ - X##_f0 = (X##_f1 << (_FP_W_TYPE_SIZE - (N)) | X##_f0 >> (N) | \ - (__builtin_constant_p(N) && (N) == 1 \ - ? X##_f0 & 1 \ - : (X##_f0 << (_FP_W_TYPE_SIZE - (N))) != 0)); \ - X##_f1 >>= (N); \ - } \ - else \ - { \ - X##_f0 = (X##_f1 >> ((N) - _FP_W_TYPE_SIZE) | \ - (((X##_f1 << (sz - (N))) | X##_f0) != 0)); \ - X##_f1 = 0; \ - } \ - } while (0) - -#define _FP_FRAC_ADDI_2(X,I) \ - __FP_FRAC_ADDI_2(X##_f1, X##_f0, I) - -#define _FP_FRAC_ADD_2(R,X,Y) \ - __FP_FRAC_ADD_2(R##_f1, R##_f0, X##_f1, X##_f0, Y##_f1, Y##_f0) - -#define _FP_FRAC_SUB_2(R,X,Y) \ - __FP_FRAC_SUB_2(R##_f1, R##_f0, X##_f1, X##_f0, Y##_f1, Y##_f0) - -#define _FP_FRAC_CLZ_2(R,X) \ - do { \ - if (X##_f1) \ - __FP_CLZ(R,X##_f1); \ - else \ - { \ - __FP_CLZ(R,X##_f0); \ - R += _FP_W_TYPE_SIZE; \ - } \ - } while(0) - -/* Predicates */ -#define _FP_FRAC_NEGP_2(X) ((_FP_WS_TYPE)X##_f1 < 0) -#define _FP_FRAC_ZEROP_2(X) ((X##_f1 | X##_f0) == 0) -#define _FP_FRAC_OVERP_2(fs,X) (X##_f1 & _FP_OVERFLOW_##fs) -#define _FP_FRAC_EQ_2(X, Y) (X##_f1 == Y##_f1 && X##_f0 == Y##_f0) -#define _FP_FRAC_GT_2(X, Y) \ - (X##_f1 > Y##_f1 || (X##_f1 == Y##_f1 && X##_f0 > Y##_f0)) -#define _FP_FRAC_GE_2(X, Y) \ - (X##_f1 > Y##_f1 || (X##_f1 == Y##_f1 && X##_f0 >= Y##_f0)) - -#define _FP_ZEROFRAC_2 0, 0 -#define _FP_MINFRAC_2 0, 1 - -/* - * Internals - */ - -#define __FP_FRAC_SET_2(X,I1,I0) (X##_f0 = I0, X##_f1 = I1) - -#define __FP_CLZ_2(R, xh, xl) \ - do { \ - if (xh) \ - __FP_CLZ(R,xl); \ - else \ - { \ - __FP_CLZ(R,xl); \ - R += _FP_W_TYPE_SIZE; \ - } \ - } while(0) - -#if 0 - -#ifndef __FP_FRAC_ADDI_2 -#define __FP_FRAC_ADDI_2(xh, xl, i) \ - (xh += ((xl += i) < i)) -#endif -#ifndef __FP_FRAC_ADD_2 -#define __FP_FRAC_ADD_2(rh, rl, xh, xl, yh, yl) \ - (rh = xh + yh + ((rl = xl + yl) < xl)) -#endif -#ifndef __FP_FRAC_SUB_2 -#define __FP_FRAC_SUB_2(rh, rl, xh, xl, yh, yl) \ - (rh = xh - yh - ((rl = xl - yl) > xl)) -#endif - -#else - -#undef __FP_FRAC_ADDI_2 -#define __FP_FRAC_ADDI_2(xh, xl, i) add_ssaaaa(xh, xl, xh, xl, 0, i) -#undef __FP_FRAC_ADD_2 -#define __FP_FRAC_ADD_2 add_ssaaaa -#undef __FP_FRAC_SUB_2 -#define __FP_FRAC_SUB_2 sub_ddmmss - -#endif - -/* - * Unpack the raw bits of a native fp value. Do not classify or - * normalize the data. - */ - -#define _FP_UNPACK_RAW_2(fs, X, val) \ - do { \ - union _FP_UNION_##fs _flo; _flo.flt = (val); \ - \ - X##_f0 = _flo.bits.frac0; \ - X##_f1 = _flo.bits.frac1; \ - X##_e = _flo.bits.exp; \ - X##_s = _flo.bits.sign; \ - } while (0) - - -/* - * Repack the raw bits of a native fp value. - */ - -#define _FP_PACK_RAW_2(fs, val, X) \ - do { \ - union _FP_UNION_##fs _flo; \ - \ - _flo.bits.frac0 = X##_f0; \ - _flo.bits.frac1 = X##_f1; \ - _flo.bits.exp = X##_e; \ - _flo.bits.sign = X##_s; \ - \ - (val) = _flo.flt; \ - } while (0) - - -/* - * Multiplication algorithms: - */ - -/* Given a 1W * 1W => 2W primitive, do the extended multiplication. */ - -#define _FP_MUL_MEAT_2_wide(fs, R, X, Y, doit) \ - do { \ - _FP_FRAC_DECL_4(_z); _FP_FRAC_DECL_2(_b); _FP_FRAC_DECL_2(_c); \ - \ - doit(_FP_FRAC_WORD_4(_z,1), _FP_FRAC_WORD_4(_z,0), X##_f0, Y##_f0); \ - doit(_b_f1, _b_f0, X##_f0, Y##_f1); \ - doit(_c_f1, _c_f0, X##_f1, Y##_f0); \ - doit(_FP_FRAC_WORD_4(_z,3), _FP_FRAC_WORD_4(_z,2), X##_f1, Y##_f1); \ - \ - __FP_FRAC_ADD_4(_FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2), \ - _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0), \ - 0, _b_f1, _b_f0, 0, \ - _FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2), \ - _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0)); \ - __FP_FRAC_ADD_4(_FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2), \ - _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0), \ - 0, _c_f1, _c_f0, 0, \ - _FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2), \ - _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0)); \ - \ - /* Normalize since we know where the msb of the multiplicands \ - were (bit B), we know that the msb of the of the product is \ - at either 2B or 2B-1. */ \ - _FP_FRAC_SRS_4(_z, _FP_WFRACBITS_##fs-1, 2*_FP_WFRACBITS_##fs); \ - R##_f0 = _FP_FRAC_WORD_4(_z,0); \ - R##_f1 = _FP_FRAC_WORD_4(_z,1); \ - } while (0) - -/* This next macro appears to be totally broken. Fortunately nowhere - * seems to use it :-> The problem is that we define _z[4] but - * then use it in _FP_FRAC_SRS_4, which will attempt to access - * _z_f[n] which will cause an error. The fix probably involves - * declaring it with _FP_FRAC_DECL_4, see previous macro. -- PMM 02/1998 - */ -#define _FP_MUL_MEAT_2_gmp(fs, R, X, Y) \ - do { \ - _FP_W_TYPE _x[2], _y[2], _z[4]; \ - _x[0] = X##_f0; _x[1] = X##_f1; \ - _y[0] = Y##_f0; _y[1] = Y##_f1; \ - \ - mpn_mul_n(_z, _x, _y, 2); \ - \ - /* Normalize since we know where the msb of the multiplicands \ - were (bit B), we know that the msb of the of the product is \ - at either 2B or 2B-1. */ \ - _FP_FRAC_SRS_4(_z, _FP_WFRACBITS##_fs-1, 2*_FP_WFRACBITS_##fs); \ - R##_f0 = _z[0]; \ - R##_f1 = _z[1]; \ - } while (0) - - -/* - * Division algorithms: - * This seems to be giving me difficulties -- PMM - * Look, NetBSD seems to be able to comment algorithms. Can't you? - * I've thrown printks at the problem. - * This now appears to work, but I still don't really know why. - * Also, I don't think the result is properly normalised... - */ - -#define _FP_DIV_MEAT_2_udiv_64(fs, R, X, Y) \ - do { \ - extern void _fp_udivmodti4(_FP_W_TYPE q[2], _FP_W_TYPE r[2], \ - _FP_W_TYPE n1, _FP_W_TYPE n0, \ - _FP_W_TYPE d1, _FP_W_TYPE d0); \ - _FP_W_TYPE _n_f3, _n_f2, _n_f1, _n_f0, _r_f1, _r_f0; \ - _FP_W_TYPE _q_f1, _q_f0, _m_f1, _m_f0; \ - _FP_W_TYPE _rmem[2], _qmem[2]; \ - /* I think this check is to ensure that the result is normalised. \ - * Assuming X,Y normalised (ie in [1.0,2.0)) X/Y will be in \ - * [0.5,2.0). Furthermore, it will be less than 1.0 iff X < Y. \ - * In this case we tweak things. (this is based on comments in \ - * the NetBSD FPU emulation code. ) \ - * We know X,Y are normalised because we ensure this as part of \ - * the unpacking process. -- PMM \ - */ \ - if (_FP_FRAC_GT_2(X, Y)) \ - { \ -/* R##_e++; */ \ - _n_f3 = X##_f1 >> 1; \ - _n_f2 = X##_f1 << (_FP_W_TYPE_SIZE - 1) | X##_f0 >> 1; \ - _n_f1 = X##_f0 << (_FP_W_TYPE_SIZE - 1); \ - _n_f0 = 0; \ - } \ - else \ - { \ - R##_e--; \ - _n_f3 = X##_f1; \ - _n_f2 = X##_f0; \ - _n_f1 = _n_f0 = 0; \ - } \ - \ - /* Normalize, i.e. make the most significant bit of the \ - denominator set. CHANGED: - 1 to nothing -- PMM */ \ - _FP_FRAC_SLL_2(Y, _FP_WFRACXBITS_##fs /* -1 */); \ - \ - /* Do the 256/128 bit division given the 128-bit _fp_udivmodtf4 \ - primitive snagged from libgcc2.c. */ \ - \ - _fp_udivmodti4(_qmem, _rmem, _n_f3, _n_f2, 0, Y##_f1); \ - _q_f1 = _qmem[0]; \ - umul_ppmm(_m_f1, _m_f0, _q_f1, Y##_f0); \ - _r_f1 = _rmem[0]; \ - _r_f0 = _n_f1; \ - if (_FP_FRAC_GT_2(_m, _r)) \ - { \ - _q_f1--; \ - _FP_FRAC_ADD_2(_r, _r, Y); \ - if (_FP_FRAC_GE_2(_r, Y) && _FP_FRAC_GT_2(_m, _r)) \ - { \ - _q_f1--; \ - _FP_FRAC_ADD_2(_r, _r, Y); \ - } \ - } \ - _FP_FRAC_SUB_2(_r, _r, _m); \ - \ - _fp_udivmodti4(_qmem, _rmem, _r_f1, _r_f0, 0, Y##_f1); \ - _q_f0 = _qmem[0]; \ - umul_ppmm(_m_f1, _m_f0, _q_f0, Y##_f0); \ - _r_f1 = _rmem[0]; \ - _r_f0 = _n_f0; \ - if (_FP_FRAC_GT_2(_m, _r)) \ - { \ - _q_f0--; \ - _FP_FRAC_ADD_2(_r, _r, Y); \ - if (_FP_FRAC_GE_2(_r, Y) && _FP_FRAC_GT_2(_m, _r)) \ - { \ - _q_f0--; \ - _FP_FRAC_ADD_2(_r, _r, Y); \ - } \ - } \ - _FP_FRAC_SUB_2(_r, _r, _m); \ - \ - R##_f1 = _q_f1; \ - R##_f0 = _q_f0 | ((_r_f1 | _r_f0) != 0); \ - /* adjust so answer is normalized again. I'm not sure what the \ - * final sz param should be. In practice it's never used since \ - * N is 1 which is always going to be < _FP_W_TYPE_SIZE... \ - */ \ - /* _FP_FRAC_SRS_2(R,1,_FP_WFRACBITS_##fs); */ \ - } while (0) - - -#define _FP_DIV_MEAT_2_gmp(fs, R, X, Y) \ - do { \ - _FP_W_TYPE _x[4], _y[2], _z[4]; \ - _y[0] = Y##_f0; _y[1] = Y##_f1; \ - _x[0] = _x[3] = 0; \ - if (_FP_FRAC_GT_2(X, Y)) \ - { \ - R##_e++; \ - _x[1] = (X##_f0 << (_FP_WFRACBITS-1 - _FP_W_TYPE_SIZE) | \ - X##_f1 >> (_FP_W_TYPE_SIZE - \ - (_FP_WFRACBITS-1 - _FP_W_TYPE_SIZE))); \ - _x[2] = X##_f1 << (_FP_WFRACBITS-1 - _FP_W_TYPE_SIZE); \ - } \ - else \ - { \ - _x[1] = (X##_f0 << (_FP_WFRACBITS - _FP_W_TYPE_SIZE) | \ - X##_f1 >> (_FP_W_TYPE_SIZE - \ - (_FP_WFRACBITS - _FP_W_TYPE_SIZE))); \ - _x[2] = X##_f1 << (_FP_WFRACBITS - _FP_W_TYPE_SIZE); \ - } \ - \ - (void) mpn_divrem (_z, 0, _x, 4, _y, 2); \ - R##_f1 = _z[1]; \ - R##_f0 = _z[0] | ((_x[0] | _x[1]) != 0); \ - } while (0) - - -/* - * Square root algorithms: - * We have just one right now, maybe Newton approximation - * should be added for those machines where division is fast. - */ - -#define _FP_SQRT_MEAT_2(R, S, T, X, q) \ - do { \ - while (q) \ - { \ - T##_f1 = S##_f1 + q; \ - if (T##_f1 <= X##_f1) \ - { \ - S##_f1 = T##_f1 + q; \ - X##_f1 -= T##_f1; \ - R##_f1 += q; \ - } \ - _FP_FRAC_SLL_2(X, 1); \ - q >>= 1; \ - } \ - q = (_FP_W_TYPE)1 << (_FP_W_TYPE_SIZE - 1); \ - while (q) \ - { \ - T##_f0 = S##_f0 + q; \ - T##_f1 = S##_f1; \ - if (T##_f1 < X##_f1 || \ - (T##_f1 == X##_f1 && T##_f0 < X##_f0)) \ - { \ - S##_f0 = T##_f0 + q; \ - if (((_FP_WS_TYPE)T##_f0) < 0 && \ - ((_FP_WS_TYPE)S##_f0) >= 0) \ - S##_f1++; \ - _FP_FRAC_SUB_2(X, X, T); \ - R##_f0 += q; \ - } \ - _FP_FRAC_SLL_2(X, 1); \ - q >>= 1; \ - } \ - } while (0) - - -/* - * Assembly/disassembly for converting to/from integral types. - * No shifting or overflow handled here. - */ - -#define _FP_FRAC_ASSEMBLE_2(r, X, rsize) \ - do { \ - if (rsize <= _FP_W_TYPE_SIZE) \ - r = X##_f0; \ - else \ - { \ - r = X##_f1; \ - r <<= _FP_W_TYPE_SIZE; \ - r += X##_f0; \ - } \ - } while (0) - -#define _FP_FRAC_DISASSEMBLE_2(X, r, rsize) \ - do { \ - X##_f0 = r; \ - X##_f1 = (rsize <= _FP_W_TYPE_SIZE ? 0 : r >> _FP_W_TYPE_SIZE); \ - } while (0) - -/* - * Convert FP values between word sizes - */ - -#define _FP_FRAC_CONV_1_2(dfs, sfs, D, S) \ - do { \ - _FP_FRAC_SRS_2(S, (_FP_WFRACBITS_##sfs - _FP_WFRACBITS_##dfs), \ - _FP_WFRACBITS_##sfs); \ - D##_f = S##_f0; \ - } while (0) - -#define _FP_FRAC_CONV_2_1(dfs, sfs, D, S) \ - do { \ - D##_f0 = S##_f; \ - D##_f1 = 0; \ - _FP_FRAC_SLL_2(D, (_FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs)); \ - } while (0) - diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/op-4.h linux/arch/sparc64/math-emu/op-4.h --- v2.2.13/linux/arch/sparc64/math-emu/op-4.h Tue Apr 14 17:44:21 1998 +++ linux/arch/sparc64/math-emu/op-4.h Wed Dec 31 16:00:00 1969 @@ -1,297 +0,0 @@ -/* - * Basic four-word fraction declaration and manipulation. - * - * When adding quadword support for 32 bit machines, we need - * to be a little careful as double multiply uses some of these - * macros: (in op-2.h) - * _FP_MUL_MEAT_2_wide() uses _FP_FRAC_DECL_4, _FP_FRAC_WORD_4, - * _FP_FRAC_ADD_4, _FP_FRAC_SRS_4 - * _FP_MUL_MEAT_2_gmp() uses _FP_FRAC_SRS_4 (and should use - * _FP_FRAC_DECL_4: it appears to be broken and is not used - * anywhere anyway. ) - * - * I've now fixed all the macros that were here from the sparc64 code. - * [*none* of the shift macros were correct!] -- PMM 02/1998 - * - * The only quadword stuff that remains to be coded is: - * 1) the conversion to/from ints, which requires - * that we check (in op-common.h) that the following do the right thing - * for quadwords: _FP_TO_INT(Q,4,r,X,rsz,rsg), _FP_FROM_INT(Q,4,X,r,rs,rt) - * 2) multiply, divide and sqrt, which require: - * _FP_MUL_MEAT_4_*(R,X,Y), _FP_DIV_MEAT_4_*(R,X,Y), _FP_SQRT_MEAT_4(R,S,T,X,q), - * This also needs _FP_MUL_MEAT_Q and _FP_DIV_MEAT_Q to be defined to - * some suitable _FP_MUL_MEAT_4_* macros in sfp-machine.h. - * [we're free to choose whatever FP_MUL_MEAT_4_* macros we need for - * these; they are used nowhere else. ] - */ - -#define _FP_FRAC_DECL_4(X) _FP_W_TYPE X##_f[4] -#define _FP_FRAC_COPY_4(D,S) \ - (D##_f[0] = S##_f[0], D##_f[1] = S##_f[1], \ - D##_f[2] = S##_f[2], D##_f[3] = S##_f[3]) -/* The _FP_FRAC_SET_n(X,I) macro is intended for use with another - * macro such as _FP_ZEROFRAC_n which returns n comma separated values. - * The result is that we get an expansion of __FP_FRAC_SET_n(X,I0,I1,I2,I3) - * which just assigns the In values to the array X##_f[]. - * This is why the number of parameters doesn't appear to match - * at first glance... -- PMM - */ -#define _FP_FRAC_SET_4(X,I) __FP_FRAC_SET_4(X, I) -#define _FP_FRAC_HIGH_4(X) (X##_f[3]) -#define _FP_FRAC_LOW_4(X) (X##_f[0]) -#define _FP_FRAC_WORD_4(X,w) (X##_f[w]) - -#define _FP_FRAC_SLL_4(X,N) \ - do { \ - _FP_I_TYPE _up, _down, _skip, _i; \ - _skip = (N) / _FP_W_TYPE_SIZE; \ - _up = (N) % _FP_W_TYPE_SIZE; \ - _down = _FP_W_TYPE_SIZE - _up; \ - for (_i = 3; _i > _skip; --_i) \ - X##_f[_i] = X##_f[_i-_skip] << _up | X##_f[_i-_skip-1] >> _down; \ -/* bugfixed: was X##_f[_i] <<= _up; -- PMM 02/1998 */ \ - X##_f[_i] = X##_f[0] << _up; \ - for (--_i; _i >= 0; --_i) \ - X##_f[_i] = 0; \ - } while (0) - -/* This one was broken too */ -#define _FP_FRAC_SRL_4(X,N) \ - do { \ - _FP_I_TYPE _up, _down, _skip, _i; \ - _skip = (N) / _FP_W_TYPE_SIZE; \ - _down = (N) % _FP_W_TYPE_SIZE; \ - _up = _FP_W_TYPE_SIZE - _down; \ - for (_i = 0; _i < 3-_skip; ++_i) \ - X##_f[_i] = X##_f[_i+_skip] >> _down | X##_f[_i+_skip+1] << _up; \ - X##_f[_i] = X##_f[3] >> _down; \ - for (++_i; _i < 4; ++_i) \ - X##_f[_i] = 0; \ - } while (0) - - -/* Right shift with sticky-lsb. - * What this actually means is that we do a standard right-shift, - * but that if any of the bits that fall off the right hand side - * were one then we always set the LSbit. - */ -#define _FP_FRAC_SRS_4(X,N,size) \ - do { \ - _FP_I_TYPE _up, _down, _skip, _i; \ - _FP_W_TYPE _s; \ - _skip = (N) / _FP_W_TYPE_SIZE; \ - _down = (N) % _FP_W_TYPE_SIZE; \ - _up = _FP_W_TYPE_SIZE - _down; \ - for (_s = _i = 0; _i < _skip; ++_i) \ - _s |= X##_f[_i]; \ - _s |= X##_f[_i] << _up; \ -/* s is now != 0 if we want to set the LSbit */ \ - for (_i = 0; _i < 3-_skip; ++_i) \ - X##_f[_i] = X##_f[_i+_skip] >> _down | X##_f[_i+_skip+1] << _up; \ - X##_f[_i] = X##_f[3] >> _down; \ - for (++_i; _i < 4; ++_i) \ - X##_f[_i] = 0; \ - /* don't fix the LSB until the very end when we're sure f[0] is stable */ \ - X##_f[0] |= (_s != 0); \ - } while (0) - -#define _FP_FRAC_ADD_4(R,X,Y) \ - __FP_FRAC_ADD_4(R##_f[3], R##_f[2], R##_f[1], R##_f[0], \ - X##_f[3], X##_f[2], X##_f[1], X##_f[0], \ - Y##_f[3], Y##_f[2], Y##_f[1], Y##_f[0]) - -#define _FP_FRAC_SUB_4(R,X,Y) \ - __FP_FRAC_SUB_4(R##_f[3], R##_f[2], R##_f[1], R##_f[0], \ - X##_f[3], X##_f[2], X##_f[1], X##_f[0], \ - Y##_f[3], Y##_f[2], Y##_f[1], Y##_f[0]) - -#define _FP_FRAC_ADDI_4(X,I) \ - __FP_FRAC_ADDI_4(X##_f[3], X##_f[2], X##_f[1], X##_f[0], I) - -#define _FP_ZEROFRAC_4 0,0,0,0 -#define _FP_MINFRAC_4 0,0,0,1 - -#define _FP_FRAC_ZEROP_4(X) ((X##_f[0] | X##_f[1] | X##_f[2] | X##_f[3]) == 0) -#define _FP_FRAC_NEGP_4(X) ((_FP_WS_TYPE)X##_f[3] < 0) -#define _FP_FRAC_OVERP_4(fs,X) (X##_f[0] & _FP_OVERFLOW_##fs) - -#define _FP_FRAC_EQ_4(X,Y) \ - (X##_f[0] == Y##_f[0] && X##_f[1] == Y##_f[1] \ - && X##_f[2] == Y##_f[2] && X##_f[3] == Y##_f[3]) - -#define _FP_FRAC_GT_4(X,Y) \ - (X##_f[3] > Y##_f[3] || \ - (X##_f[3] == Y##_f[3] && (X##_f[2] > Y##_f[2] || \ - (X##_f[2] == Y##_f[2] && (X##_f[1] > Y##_f[1] || \ - (X##_f[1] == Y##_f[1] && X##_f[0] > Y##_f[0]) \ - )) \ - )) \ - ) - -#define _FP_FRAC_GE_4(X,Y) \ - (X##_f[3] > Y##_f[3] || \ - (X##_f[3] == Y##_f[3] && (X##_f[2] > Y##_f[2] || \ - (X##_f[2] == Y##_f[2] && (X##_f[1] > Y##_f[1] || \ - (X##_f[1] == Y##_f[1] && X##_f[0] >= Y##_f[0]) \ - )) \ - )) \ - ) - - -#define _FP_FRAC_CLZ_4(R,X) \ - do { \ - if (X##_f[3]) \ - { \ - __FP_CLZ(R,X##_f[3]); \ - } \ - else if (X##_f[2]) \ - { \ - __FP_CLZ(R,X##_f[2]); \ - R += _FP_W_TYPE_SIZE; \ - } \ - else if (X##_f[1]) \ - { \ - __FP_CLZ(R,X##_f[2]); \ - R += _FP_W_TYPE_SIZE*2; \ - } \ - else \ - { \ - __FP_CLZ(R,X##_f[0]); \ - R += _FP_W_TYPE_SIZE*3; \ - } \ - } while(0) - - -#define _FP_UNPACK_RAW_4(fs, X, val) \ - do { \ - union _FP_UNION_##fs _flo; _flo.flt = (val); \ - X##_f[0] = _flo.bits.frac0; \ - X##_f[1] = _flo.bits.frac1; \ - X##_f[2] = _flo.bits.frac2; \ - X##_f[3] = _flo.bits.frac3; \ - X##_e = _flo.bits.exp; \ - X##_s = _flo.bits.sign; \ - } while (0) - -#define _FP_PACK_RAW_4(fs, val, X) \ - do { \ - union _FP_UNION_##fs _flo; \ - _flo.bits.frac0 = X##_f[0]; \ - _flo.bits.frac1 = X##_f[1]; \ - _flo.bits.frac2 = X##_f[2]; \ - _flo.bits.frac3 = X##_f[3]; \ - _flo.bits.exp = X##_e; \ - _flo.bits.sign = X##_s; \ - (val) = _flo.flt; \ - } while (0) - - -/* - * Internals - */ - -#define __FP_FRAC_SET_4(X,I3,I2,I1,I0) \ - (X##_f[3] = I3, X##_f[2] = I2, X##_f[1] = I1, X##_f[0] = I0) - -#ifndef __FP_FRAC_ADD_4 -#define __FP_FRAC_ADD_4(r3,r2,r1,r0,x3,x2,x1,x0,y3,y2,y1,y0) \ - (r0 = x0 + y0, \ - r1 = x1 + y1 + (r0 < x0), \ - r2 = x2 + y2 + (r1 < x1), \ - r3 = x3 + y3 + (r2 < x2)) -#endif - -#ifndef __FP_FRAC_SUB_4 -#define __FP_FRAC_SUB_4(r3,r2,r1,r0,x3,x2,x1,x0,y3,y2,y1,y0) \ - (r0 = x0 - y0, \ - r1 = x1 - y1 - (r0 > x0), \ - r2 = x2 - y2 - (r1 > x1), \ - r3 = x3 - y3 - (r2 > x2)) -#endif - -#ifndef __FP_FRAC_ADDI_4 -/* I always wanted to be a lisp programmer :-> */ -#define __FP_FRAC_ADDI_4(x3,x2,x1,x0,i) \ - (x3 += ((x2 += ((x1 += ((x0 += i) < x0)) < x1) < x2))) -#endif - -/* Convert FP values between word sizes. This appears to be more - * complicated than I'd have expected it to be, so these might be - * wrong... These macros are in any case somewhat bogus because they - * use information about what various FRAC_n variables look like - * internally [eg, that 2 word vars are X_f0 and x_f1]. But so do - * the ones in op-2.h and op-1.h. - */ -#define _FP_FRAC_CONV_1_4(dfs, sfs, D, S) \ - do { \ - _FP_FRAC_SRS_4(S, (_FP_WFRACBITS_##sfs - _FP_WFRACBITS_##dfs), \ - _FP_WFRACBITS_##sfs); \ - D##_f = S##_f[0]; \ - } while (0) - -#define _FP_FRAC_CONV_2_4(dfs, sfs, D, S) \ - do { \ - _FP_FRAC_SRS_4(S, (_FP_WFRACBITS_##sfs - _FP_WFRACBITS_##dfs), \ - _FP_WFRACBITS_##sfs); \ - D##_f0 = S##_f[0]; \ - D##_f1 = S##_f[1]; \ - } while (0) - -/* Assembly/disassembly for converting to/from integral types. - * No shifting or overflow handled here. - */ -/* Put the FP value X into r, which is an integer of size rsize. */ -#define _FP_FRAC_ASSEMBLE_4(r, X, rsize) \ - do { \ - if (rsize <= _FP_W_TYPE_SIZE) \ - r = X##_f[0]; \ - else if (rsize <= 2*_FP_W_TYPE_SIZE) \ - { \ - r = X##_f[1]; \ - r <<= _FP_W_TYPE_SIZE; \ - r += X##_f[0]; \ - } \ - else \ - { \ - /* I'm feeling lazy so we deal with int == 3words (implausible)*/ \ - /* and int == 4words as a single case. */ \ - r = X##_f[3]; \ - r <<= _FP_W_TYPE_SIZE; \ - r += X##_f[2]; \ - r <<= _FP_W_TYPE_SIZE; \ - r += X##_f[1]; \ - r <<= _FP_W_TYPE_SIZE; \ - r += X##_f[0]; \ - } \ - } while (0) - -/* "No disassemble Number Five!" */ -/* move an integer of size rsize into X's fractional part. We rely on - * the _f[] array consisting of words of size _FP_W_TYPE_SIZE to avoid - * having to mask the values we store into it. - */ -#define _FP_FRAC_DISASSEMBLE_4(X, r, rsize) \ - do { \ - X##_f[0] = r; \ - X##_f[1] = (rsize <= _FP_W_TYPE_SIZE ? 0 : r >> _FP_W_TYPE_SIZE); \ - X##_f[2] = (rsize <= 2*_FP_W_TYPE_SIZE ? 0 : r >> 2*_FP_W_TYPE_SIZE); \ - X##_f[3] = (rsize <= 3*_FP_W_TYPE_SIZE ? 0 : r >> 3*_FP_W_TYPE_SIZE); \ - } while (0); - -#define _FP_FRAC_CONV_4_1(dfs, sfs, D, S) \ - do { \ - D##_f[0] = S##_f; \ - D##_f[1] = D##_f[2] = D##_f[3] = 0; \ - _FP_FRAC_SLL_4(D, (_FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs)); \ - } while (0) - -#define _FP_FRAC_CONV_4_2(dfs, sfs, D, S) \ - do { \ - D##_f[0] = S##_f0; \ - D##_f[1] = S##_f1; \ - D##_f[2] = D##_f[3] = 0; \ - _FP_FRAC_SLL_4(D, (_FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs)); \ - } while (0) - -/* FIXME! This has to be written */ -#define _FP_SQRT_MEAT_4(R, S, T, X, q) diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/op-common.h linux/arch/sparc64/math-emu/op-common.h --- v2.2.13/linux/arch/sparc64/math-emu/op-common.h Tue Oct 19 17:10:36 1999 +++ linux/arch/sparc64/math-emu/op-common.h Wed Dec 31 16:00:00 1969 @@ -1,726 +0,0 @@ -#define _FP_DECL(wc, X) \ - _FP_I_TYPE X##_c, X##_s, X##_e, X##_r=0; \ - _FP_FRAC_DECL_##wc(X) - -/* - * Finish truely unpacking a native fp value by classifying the kind - * of fp value and normalizing both the exponent and the fraction. - */ - -#ifndef _FP_UNPACK_CANONICAL -#define _FP_UNPACK_CANONICAL(fs, wc, X) \ -do { \ - switch (X##_e) \ - { \ - default: \ - _FP_FRAC_HIGH_##wc(X) |= _FP_IMPLBIT_##fs; \ - _FP_FRAC_SLL_##wc(X, _FP_WORKBITS); \ - X##_e -= _FP_EXPBIAS_##fs; \ - X##_c = FP_CLS_NORMAL; \ - break; \ - \ - case 0: \ - if (_FP_FRAC_ZEROP_##wc(X)) \ - X##_c = FP_CLS_ZERO; \ - else \ - /* A denormalized number. */ \ - __FP_UNPACK_DENORM(fs, wc, X); \ - break; \ - \ - case _FP_EXPMAX_##fs: \ - if (_FP_FRAC_ZEROP_##wc(X)) \ - X##_c = FP_CLS_INF; \ - else \ - /* We don't differentiate between signaling and quiet nans. */ \ - X##_c = FP_CLS_NAN; \ - break; \ - } \ -} while (0) -#endif /* _FP_UNPACK_CANONICAL */ - - -/* - * Before packing the bits back into the native fp result, take care - * of such mundane things as rounding and overflow. Also, for some - * kinds of fp values, the original parts may not have been fully - * extracted -- but that is ok, we can regenerate them now. - */ - -#ifndef _FP_PACK_CANONICAL -#define _FP_PACK_CANONICAL(fs, wc, X) \ -({int __pk__ret = X##_r; \ - switch (X##_c) \ - { \ - case FP_CLS_NORMAL: \ - X##_e += _FP_EXPBIAS_##fs; \ - if (X##_e > 0) \ - { \ - __pk__ret |= _FP_ROUND(wc, X); \ - if (_FP_FRAC_OVERP_##wc(fs, X)) \ - { \ - _FP_FRAC_SRL_##wc(X, (_FP_WORKBITS+1)); \ - X##_e++; \ - } \ - else \ - _FP_FRAC_SRL_##wc(X, _FP_WORKBITS); \ - if (X##_e >= _FP_EXPMAX_##fs) \ - { \ - /* overflow to infinity */ \ - X##_e = _FP_EXPMAX_##fs; \ - _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ - __pk__ret |= EFLAG_OVERFLOW; \ - } \ - } \ - else \ - { \ - /* we've got a denormalized number */ \ - X##_e = -X##_e + 1; \ - if (X##_e <= _FP_WFRACBITS_##fs) \ - { \ - _FP_FRAC_SRS_##wc(X, X##_e, _FP_WFRACBITS_##fs); \ - __pk__ret |= _FP_ROUND(wc, X); \ - _FP_FRAC_SLL_##wc(X, 1); \ - if (_FP_FRAC_OVERP_##wc(fs, X)) \ - { \ - X##_e = 1; \ - _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ - } \ - else \ - { \ - X##_e = 0; \ - _FP_FRAC_SRL_##wc(X, _FP_WORKBITS+1); \ - __pk__ret |= EFLAG_UNDERFLOW; \ - } \ - } \ - else \ - { \ - /* underflow to zero */ \ - X##_e = 0; \ - _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ - __pk__ret |= EFLAG_UNDERFLOW; \ - } \ - } \ - break; \ - \ - case FP_CLS_ZERO: \ - X##_e = 0; \ - _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ - break; \ - \ - case FP_CLS_INF: \ - X##_e = _FP_EXPMAX_##fs; \ - _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ - break; \ - \ - case FP_CLS_NAN: \ - X##_e = _FP_EXPMAX_##fs; \ - if (!_FP_KEEPNANFRACP) \ - { \ - _FP_FRAC_SET_##wc(X, _FP_NANFRAC_##fs); \ - X##_s = 0; \ - } \ - else \ - _FP_FRAC_HIGH_##wc(X) |= _FP_QNANBIT_##fs; \ - break; \ - } \ - __pk__ret; \ -}) -#endif /* _FP_PACK_CANONICAL */ - -/* - * Main addition routine. The input values should be cooked. - */ -#ifndef _FP_ADD -#define _FP_ADD(fs, wc, R, X, Y) \ -do { \ - /* Propagate any flags that may have been set during unpacking */ \ - R##_r |= (X##_r | Y##_r); \ - switch (_FP_CLS_COMBINE(X##_c, Y##_c)) \ - { \ - case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NORMAL): \ - { \ - /* shift the smaller number so that its exponent matches the larger */ \ - _FP_I_TYPE diff = X##_e - Y##_e; \ - \ - if (diff < 0) \ - { \ - diff = -diff; \ - if (diff <= _FP_WFRACBITS_##fs) \ - _FP_FRAC_SRS_##wc(X, diff, _FP_WFRACBITS_##fs); \ - else if (!_FP_FRAC_ZEROP_##wc(X)) { \ - _FP_FRAC_SET_##wc(X, _FP_MINFRAC_##wc); \ - R_r |= EFLAG_INEXACT; \ - } \ - else \ - _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ - R##_e = Y##_e; \ - } \ - else \ - { \ - if (diff > 0) \ - { \ - if (diff <= _FP_WFRACBITS_##fs) \ - _FP_FRAC_SRS_##wc(Y, diff, _FP_WFRACBITS_##fs); \ - else if (!_FP_FRAC_ZEROP_##wc(Y)) { \ - _FP_FRAC_SET_##wc(Y, _FP_MINFRAC_##wc); \ - R_r |= EFLAG_INEXACT; \ - } \ - else \ - _FP_FRAC_SET_##wc(Y, _FP_ZEROFRAC_##wc); \ - } \ - R##_e = X##_e; \ - } \ - \ - R##_c = FP_CLS_NORMAL; \ - \ - if (X##_s == Y##_s) \ - { \ - R##_s = X##_s; \ - _FP_FRAC_ADD_##wc(R, X, Y); \ - if (_FP_FRAC_OVERP_##wc(fs, R)) \ - { \ - _FP_FRAC_SRS_##wc(R, 1, _FP_WFRACBITS_##fs); \ - R##_e++; \ - } \ - } \ - else \ - { \ - R##_s = X##_s; \ - _FP_FRAC_SUB_##wc(R, X, Y); \ - if (_FP_FRAC_ZEROP_##wc(R)) \ - { \ - /* return an exact zero */ \ - if (FP_ROUNDMODE == FP_RND_MINF) \ - R##_s |= Y##_s; \ - else \ - R##_s &= Y##_s; \ - R##_c = FP_CLS_ZERO; \ - } \ - else \ - { \ - if (_FP_FRAC_NEGP_##wc(R)) \ - { \ - _FP_FRAC_SUB_##wc(R, Y, X); \ - R##_s = Y##_s; \ - } \ - \ - /* renormalize after subtraction */ \ - _FP_FRAC_CLZ_##wc(diff, R); \ - diff -= _FP_WFRACXBITS_##fs; \ - if (diff) \ - { \ - R##_e -= diff; \ - _FP_FRAC_SLL_##wc(R, diff); \ - } \ - } \ - } \ - break; \ - } \ - \ - case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NAN): \ - _FP_CHOOSENAN(fs, wc, R, X, Y); \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_ZERO): \ - R##_e = X##_e; \ - case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NORMAL): \ - case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_INF): \ - case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_ZERO): \ - _FP_FRAC_COPY_##wc(R, X); \ - R##_s = X##_s; \ - R##_c = X##_c; \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NORMAL): \ - R##_e = Y##_e; \ - case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NAN): \ - case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NAN): \ - case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NAN): \ - _FP_FRAC_COPY_##wc(R, Y); \ - R##_s = Y##_s; \ - R##_c = Y##_c; \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_INF): \ - if (X##_s != Y##_s) \ - { \ - /* +INF + -INF => NAN */ \ - _FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs); \ - R##_s = X##_s ^ Y##_s; \ - R##_c = FP_CLS_NAN; \ - R##_r |= EFLAG_INVALID; \ - break; \ - } \ - /* FALLTHRU */ \ - \ - case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NORMAL): \ - case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_ZERO): \ - R##_s = X##_s; \ - R##_c = FP_CLS_INF; \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_INF): \ - case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_INF): \ - R##_s = Y##_s; \ - R##_c = FP_CLS_INF; \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_ZERO): \ - /* make sure the sign is correct */ \ - if (FP_ROUNDMODE == FP_RND_MINF) \ - R##_s = X##_s | Y##_s; \ - else \ - R##_s = X##_s & Y##_s; \ - R##_c = FP_CLS_ZERO; \ - break; \ - \ - default: \ - abort(); \ - } \ -} while (0) -#endif /* _FP_ADD */ - - -/* - * Main negation routine. FIXME -- when we care about setting exception - * bits reliably, this will not do. We should examine all of the fp classes. - */ -#ifndef _FP_NEG -#define _FP_NEG(fs, wc, R, X) \ - do { \ - R##_r |= X##_r; \ - _FP_FRAC_COPY_##wc(R, X); \ - R##_c = X##_c; \ - R##_e = X##_e; \ - R##_s = 1 ^ X##_s; \ - } while (0) -#endif /* _FP_NEG */ - - -/* - * Main multiplication routine. The input values should be cooked. - */ -#ifndef _FP_MUL -#define _FP_MUL(fs, wc, R, X, Y) \ -do { \ - /* Propagate any flags that may have been set during unpacking */ \ - R##_r |= (X##_r | Y##_r); \ - R##_s = X##_s ^ Y##_s; \ - switch (_FP_CLS_COMBINE(X##_c, Y##_c)) \ - { \ - case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NORMAL): \ - R##_c = FP_CLS_NORMAL; \ - R##_e = X##_e + Y##_e + 1; \ - \ - _FP_MUL_MEAT_##fs(R,X,Y); \ - \ - if (_FP_FRAC_OVERP_##wc(fs, R)) \ - _FP_FRAC_SRS_##wc(R, 1, _FP_WFRACBITS_##fs); \ - else \ - R##_e--; \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NAN): \ - _FP_CHOOSENAN(fs, wc, R, X, Y); \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NORMAL): \ - case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_INF): \ - case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_ZERO): \ - R##_s = X##_s; \ - \ - case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_INF): \ - case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NORMAL): \ - case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NORMAL): \ - case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_ZERO): \ - _FP_FRAC_COPY_##wc(R, X); \ - R##_c = X##_c; \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NAN): \ - case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NAN): \ - case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NAN): \ - R##_s = Y##_s; \ - \ - case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_INF): \ - case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_ZERO): \ - _FP_FRAC_COPY_##wc(R, Y); \ - R##_c = Y##_c; \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_ZERO): \ - case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_INF): \ - R##_c = FP_CLS_NAN; \ - _FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs); \ - break; \ - \ - default: \ - abort(); \ - } \ -} while (0) -#endif /* _FP_MUL */ - - -/* - * Main division routine. The input values should be cooked. - */ -#ifndef _FP_DIV -#define _FP_DIV(fs, wc, R, X, Y) \ -do { \ - /* Propagate any flags that may have been set during unpacking */ \ - R##_r |= (X##_r | Y##_r); \ - R##_s = X##_s ^ Y##_s; \ - switch (_FP_CLS_COMBINE(X##_c, Y##_c)) \ - { \ - case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NORMAL): \ - R##_c = FP_CLS_NORMAL; \ - R##_e = X##_e - Y##_e; \ - \ - _FP_DIV_MEAT_##fs(R,X,Y); \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NAN): \ - _FP_CHOOSENAN(fs, wc, R, X, Y); \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NORMAL): \ - case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_INF): \ - case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_ZERO): \ - R##_s = X##_s; \ - _FP_FRAC_COPY_##wc(R, X); \ - R##_c = X##_c; \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NAN): \ - case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NAN): \ - case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NAN): \ - R##_s = Y##_s; \ - _FP_FRAC_COPY_##wc(R, Y); \ - R##_c = Y##_c; \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_INF): \ - case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_INF): \ - case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NORMAL): \ - R##_c = FP_CLS_ZERO; \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_ZERO): \ - case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_ZERO): \ - case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NORMAL): \ - R##_c = FP_CLS_INF; \ - break; \ - \ - case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_INF): \ - case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_ZERO): \ - R##_c = FP_CLS_NAN; \ - _FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs); \ - break; \ - \ - default: \ - abort(); \ - } \ -} while (0) -#endif _FP_DIV - - -/* - * Main differential comparison routine. The inputs should be raw not - * cooked. The return is -1,0,1 for normal values, 2 otherwise. - */ -#ifndef _FP_CMP -#define _FP_CMP(fs, wc, ret, X, Y, un) \ - do { \ - /* NANs are unordered */ \ - if ((X##_e == _FP_EXPMAX_##fs && !_FP_FRAC_ZEROP_##wc(X)) \ - || (Y##_e == _FP_EXPMAX_##fs && !_FP_FRAC_ZEROP_##wc(Y))) \ - { \ - ret = un; \ - } \ - /* Deal with infinities */ \ - else if (X##_c == FP_CLS_INF) { \ - if(Y##_c == FP_CLS_INF) { \ - ret = Y##_s - X##_s; \ - } \ - else { \ - ret = X##_s ? -1 : 1; \ - } \ - } \ - else if(Y##_c == FP_CLS_INF) { \ - ret = Y##_s ? 1 : -1; \ - } \ - else \ - { \ - int __is_zero_x; \ - int __is_zero_y; \ - \ - __is_zero_x = (!X##_e && _FP_FRAC_ZEROP_##wc(X)) ? 1 : 0; \ - __is_zero_y = (!Y##_e && _FP_FRAC_ZEROP_##wc(Y)) ? 1 : 0; \ - \ - if (__is_zero_x && __is_zero_y) \ - ret = 0; \ - else if (__is_zero_x) \ - ret = Y##_s ? 1 : -1; \ - else if (__is_zero_y) \ - ret = X##_s ? -1 : 1; \ - else if (X##_s != Y##_s) \ - ret = X##_s ? -1 : 1; \ - else if (X##_e > Y##_e) \ - ret = X##_s ? -1 : 1; \ - else if (X##_e < Y##_e) \ - ret = X##_s ? 1 : -1; \ - else if (_FP_FRAC_GT_##wc(X, Y)) \ - ret = X##_s ? -1 : 1; \ - else if (_FP_FRAC_GT_##wc(Y, X)) \ - ret = X##_s ? 1 : -1; \ - else \ - ret = 0; \ - } \ - } while (0) -#endif /* _FP_CMP */ - - -/* Simplification for strict equality. */ - -#ifndef _FP_CMP_EQ -#define _FP_CMP_EQ(fs, wc, ret, X, Y) \ - do { \ - /* NANs are unordered */ \ - if ((X##_e == _FP_EXPMAX_##fs && !_FP_FRAC_ZEROP_##wc(X)) \ - || (Y##_e == _FP_EXPMAX_##fs && !_FP_FRAC_ZEROP_##wc(Y))) \ - { \ - ret = 1; \ - } \ - else \ - { \ - ret = !(X##_e == Y##_e \ - && _FP_FRAC_EQ_##wc(X, Y) \ - && (X##_s == Y##_s || !X##_e && _FP_FRAC_ZEROP_##wc(X))); \ - } \ - } while (0) -#endif /* _FP_CMP_EQ */ - -/* - * Main square root routine. The input value should be cooked. - */ -#ifndef _FP_SQRT -#define _FP_SQRT(fs, wc, R, X) \ -do { \ - _FP_FRAC_DECL_##wc(T); _FP_FRAC_DECL_##wc(S); \ - _FP_W_TYPE q; \ - R##_r |= X##_r; \ - switch (X##_c) \ - { \ - case FP_CLS_NAN: \ - _FP_CHOOSENAN_SQRT(fs, wc, R, X); \ - break; \ - case FP_CLS_INF: \ - if (X##_s) \ - { \ - R##_s = 0; \ - R##_c = FP_CLS_NAN; /* sNAN */ \ - } \ - else \ - { \ - R##_s = 0; \ - R##_c = FP_CLS_INF; /* sqrt(+inf) = +inf */ \ - } \ - break; \ - case FP_CLS_ZERO: \ - R##_s = X##_s; \ - R##_c = FP_CLS_ZERO; /* sqrt(+-0) = +-0 */ \ - break; \ - case FP_CLS_NORMAL: \ - R##_s = 0; \ - if (X##_s) \ - { \ - R##_c = FP_CLS_NAN; /* sNAN */ \ - break; \ - } \ - R##_c = FP_CLS_NORMAL; \ - if (X##_e & 1) \ - _FP_FRAC_SLL_##wc(X, 1); \ - R##_e = X##_e >> 1; \ - _FP_FRAC_SET_##wc(S, _FP_ZEROFRAC_##wc); \ - _FP_FRAC_SET_##wc(R, _FP_ZEROFRAC_##wc); \ - q = _FP_OVERFLOW_##fs; \ - _FP_FRAC_SLL_##wc(X, 1); \ - _FP_SQRT_MEAT_##wc(R, S, T, X, q); \ - _FP_FRAC_SRL_##wc(R, 1); \ - } \ - } while (0) -#endif /* FP_SQRT */ - -/* - * Convert from FP to integer - */ - -/* "When a NaN, infinity, large positive argument >= 2147483648.0, or - * large negative argument <= -2147483649.0 is converted to an integer, - * the invalid_current bit...should be set and fp_exception_IEEE_754 should - * be raised. If the floating point invalid trap is disabled, no trap occurs - * and a numerical result is generated: if the sign bit of the operand - * is 0, the result is 2147483647; if the sign bit of the operand is 1, - * the result is -2147483648." - * Similarly for conversion to extended ints, except that the boundaries - * are >= 2^63, <= -(2^63 + 1), and the results are 2^63 + 1 for s=0 and - * -2^63 for s=1. - * -- SPARC Architecture Manual V9, Appendix B, which specifies how - * SPARCs resolve implementation dependencies in the IEEE-754 spec. - * I don't believe that the code below follows this. I'm not even sure - * it's right! - * It doesn't cope with needing to convert to an n bit integer when there - * is no n bit integer type. Fortunately gcc provides long long so this - * isn't a problem for sparc32. - * I have, however, fixed its NaN handling to conform as above. - * -- PMM 02/1998 - * NB: rsigned is not 'is r declared signed?' but 'should the value stored - * in r be signed or unsigned?'. r is always(?) declared unsigned. - * Comments below are mine, BTW -- PMM - */ -#ifndef _FP_TO_INT -#define _FP_TO_INT(fs, wc, r, X, rsize, rsigned) \ - do { \ - switch (X##_c) \ - { \ - case FP_CLS_NORMAL: \ - if (X##_e < 0) \ - { \ - /* case FP_CLS_NAN: see above! */ \ - case FP_CLS_ZERO: \ - r = 0; \ - } \ - else if (X##_e >= rsize - (rsigned != 0)) \ - { /* overflow */ \ - case FP_CLS_NAN: \ - case FP_CLS_INF: \ - if (rsigned) \ - { \ - r = 1; \ - r <<= rsize - 1; \ - r -= 1 - X##_s; \ - } \ - else \ - { \ - r = 0; \ - if (!X##_s) \ - r = ~r; \ - } \ - } \ - else \ - { \ - if (_FP_W_TYPE_SIZE*wc < rsize) \ - { \ - _FP_FRAC_ASSEMBLE_##wc(r, X, rsize); \ - r <<= X##_e - _FP_WFRACBITS_##fs; \ - } \ - else \ - { \ - if (X##_e >= _FP_WFRACBITS_##fs) \ - _FP_FRAC_SLL_##wc(X, (X##_e - _FP_WFRACBITS_##fs + 1)); \ - else \ - _FP_FRAC_SRL_##wc(X, (_FP_WFRACBITS_##fs - X##_e - 1)); \ - _FP_FRAC_ASSEMBLE_##wc(r, X, rsize); \ - } \ - if (rsigned && X##_s) \ - r = -r; \ - } \ - break; \ - } \ - } while (0) - -#endif /* _FP_TO_INT */ - -#ifndef _FP_FROM_INT -#define _FP_FROM_INT(fs, wc, X, r, rsize, rtype) \ - do { \ - if (r) \ - { \ - X##_c = FP_CLS_NORMAL; \ - \ - if ((X##_s = (r < 0))) \ - r = -r; \ - /* Note that `r' is now considered unsigned, so we don't have \ - to worry about the single signed overflow case. */ \ - \ - if (rsize <= _FP_W_TYPE_SIZE) \ - __FP_CLZ(X##_e, r); \ - else \ - __FP_CLZ_2(X##_e, (_FP_W_TYPE)(r >> _FP_W_TYPE_SIZE), \ - (_FP_W_TYPE)r); \ - if (rsize < _FP_W_TYPE_SIZE) \ - X##_e -= (_FP_W_TYPE_SIZE - rsize); \ - X##_e = rsize - X##_e - 1; \ - \ - if (_FP_FRACBITS_##fs < rsize && _FP_WFRACBITS_##fs < X##_e) \ - __FP_FRAC_SRS_1(r, (X##_e - _FP_WFRACBITS_##fs), rsize); \ - r &= ~((_FP_W_TYPE)1 << X##_e); \ - _FP_FRAC_DISASSEMBLE_##wc(X, ((unsigned rtype)r), rsize); \ - _FP_FRAC_SLL_##wc(X, (_FP_WFRACBITS_##fs - X##_e - 1)); \ - } \ - else \ - { \ - X##_c = FP_CLS_ZERO, X##_s = 0; \ - } \ - } while (0) -#endif /* FP_FROM_INT */ - -#ifndef FP_CONV -#define FP_CONV(dfs,sfs,dwc,swc,D,S) \ - do { \ - _FP_FRAC_CONV_##dwc##_##swc(dfs, sfs, D, S); \ - D##_e = S##_e; \ - D##_c = S##_c; \ - D##_s = S##_s; \ - D##_r |= S##_r; \ - } while (0) -#endif FP_CONV - -/* - * Helper primitives. - */ - -/* Count leading zeros in a word. */ - -#ifndef __FP_CLZ -#if _FP_W_TYPE_SIZE < 64 -/* this is just to shut the compiler up about shifts > word length -- PMM 02/1998 */ -#define __FP_CLZ(r, x) \ - do { \ - _FP_W_TYPE _t = (x); \ - r = _FP_W_TYPE_SIZE - 1; \ - if (_t > 0xffff) r -= 16; \ - if (_t > 0xffff) _t >>= 16; \ - if (_t > 0xff) r -= 8; \ - if (_t > 0xff) _t >>= 8; \ - if (_t & 0xf0) r -= 4; \ - if (_t & 0xf0) _t >>= 4; \ - if (_t & 0xc) r -= 2; \ - if (_t & 0xc) _t >>= 2; \ - if (_t & 0x2) r -= 1; \ - } while (0) -#else /* not _FP_W_TYPE_SIZE < 64 */ -#define __FP_CLZ(r, x) \ - do { \ - _FP_W_TYPE _t = (x); \ - r = _FP_W_TYPE_SIZE - 1; \ - if (_t > 0xffffffff) r -= 32; \ - if (_t > 0xffffffff) _t >>= 32; \ - if (_t > 0xffff) r -= 16; \ - if (_t > 0xffff) _t >>= 16; \ - if (_t > 0xff) r -= 8; \ - if (_t > 0xff) _t >>= 8; \ - if (_t & 0xf0) r -= 4; \ - if (_t & 0xf0) _t >>= 4; \ - if (_t & 0xc) r -= 2; \ - if (_t & 0xc) _t >>= 2; \ - if (_t & 0x2) r -= 1; \ - } while (0) -#endif /* not _FP_W_TYPE_SIZE < 64 */ -#endif /* ndef __FP_CLZ */ - -#define _FP_DIV_HELP_imm(q, r, n, d) \ - do { \ - q = n / d, r = n % d; \ - } while (0) - diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/quad.h linux/arch/sparc64/math-emu/quad.h --- v2.2.13/linux/arch/sparc64/math-emu/quad.h Tue Apr 14 17:44:21 1998 +++ linux/arch/sparc64/math-emu/quad.h Wed Dec 31 16:00:00 1969 @@ -1,138 +0,0 @@ -/* - * Definitions for IEEE Quad Precision - */ -#if _FP_W_TYPE_SIZE < 32 -/* It appears to be traditional to abuse 16bitters in these header files... */ -#error "Here's a nickel, kid. Go buy yourself a real computer." -#endif - -#if _FP_W_TYPE_SIZE < 64 -/* This is all terribly experimental and I don't know if it'll work properly -- PMM 02/1998 */ -#define _FP_FRACTBITS_Q (4*_FP_W_TYPE_SIZE) -#else -#define _FP_FRACTBITS_Q (2*_FP_W_TYPE_SIZE) -#endif - -#define _FP_FRACBITS_Q 113 -#define _FP_FRACXBITS_Q (_FP_FRACTBITS_Q - _FP_FRACBITS_Q) -#define _FP_WFRACBITS_Q (_FP_WORKBITS + _FP_FRACBITS_Q) -#define _FP_WFRACXBITS_Q (_FP_FRACTBITS_Q - _FP_WFRACBITS_Q) -#define _FP_EXPBITS_Q 15 -#define _FP_EXPBIAS_Q 16383 -#define _FP_EXPMAX_Q 32767 - -#define _FP_QNANBIT_Q \ - ((_FP_W_TYPE)1 << (_FP_FRACBITS_Q-2) % _FP_W_TYPE_SIZE) -#define _FP_IMPLBIT_Q \ - ((_FP_W_TYPE)1 << (_FP_FRACBITS_Q-1) % _FP_W_TYPE_SIZE) -#define _FP_OVERFLOW_Q \ - ((_FP_W_TYPE)1 << (_FP_WFRACBITS_Q % _FP_W_TYPE_SIZE)) - -#if _FP_W_TYPE_SIZE < 64 - -union _FP_UNION_Q -{ - long double flt; - struct - { -#if __BYTE_ORDER == __BIG_ENDIAN - unsigned sign : 1; - unsigned exp : _FP_EXPBITS_Q; - unsigned long frac3 : _FP_FRACBITS_Q - (_FP_IMPLBIT_Q != 0)-(_FP_W_TYPE_SIZE * 3); - unsigned long frac2 : _FP_W_TYPE_SIZE; - unsigned long frac1 : _FP_W_TYPE_SIZE; - unsigned long frac0 : _FP_W_TYPE_SIZE; -#else - unsigned long frac0 : _FP_W_TYPE_SIZE; - unsigned long frac1 : _FP_W_TYPE_SIZE; - unsigned long frac2 : _FP_W_TYPE_SIZE; - unsigned long frac3 : _FP_FRACBITS_Q - (_FP_IMPLBIT_Q != 0)-(_FP_W_TYPE_SIZE * 3); - unsigned exp : _FP_EXPBITS_Q; - unsigned sign : 1; -#endif /* not bigendian */ - } bits __attribute__((packed)); -}; - - -#define FP_DECL_Q(X) _FP_DECL(4,X) -#define FP_UNPACK_RAW_Q(X,val) _FP_UNPACK_RAW_4(Q,X,val) -#define FP_PACK_RAW_Q(val,X) _FP_PACK_RAW_4(Q,val,X) - -#define FP_UNPACK_Q(X,val) \ - do { \ - _FP_UNPACK_RAW_4(Q,X,val); \ - _FP_UNPACK_CANONICAL(Q,4,X); \ - } while (0) - -#define FP_PACK_Q(val,X) \ - do { \ - _FP_PACK_CANONICAL(Q,4,X); \ - _FP_PACK_RAW_4(Q,val,X); \ - } while (0) - -#define FP_NEG_Q(R,X) _FP_NEG(Q,4,R,X) -#define FP_ADD_Q(R,X,Y) _FP_ADD(Q,4,R,X,Y) -/* single.h and double.h define FP_SUB_t this way too. However, _FP_SUB is - * never defined in op-common.h! Fortunately nobody seems to use the FP_SUB_t - * macros: I suggest a combination of FP_NEG and FP_ADD :-> -- PMM 02/1998 - */ -#define FP_SUB_Q(R,X,Y) _FP_SUB(Q,4,R,X,Y) -#define FP_MUL_Q(R,X,Y) _FP_MUL(Q,4,R,X,Y) -#define FP_DIV_Q(R,X,Y) _FP_DIV(Q,4,R,X,Y) -#define FP_SQRT_Q(R,X) _FP_SQRT(Q,4,R,X) - -#define FP_CMP_Q(r,X,Y,un) _FP_CMP(Q,4,r,X,Y,un) -#define FP_CMP_EQ_Q(r,X,Y) _FP_CMP_EQ(Q,4,r,X,Y) - -#define FP_TO_INT_Q(r,X,rsz,rsg) _FP_TO_INT(Q,4,r,X,rsz,rsg) -#define FP_FROM_INT_Q(X,r,rs,rt) _FP_FROM_INT(Q,4,X,r,rs,rt) - -#else /* not _FP_W_TYPE_SIZE < 64 */ -union _FP_UNION_Q -{ - long double flt /* __attribute__((mode(TF))) */ ; - struct { -#if __BYTE_ORDER == __BIG_ENDIAN - unsigned sign : 1; - unsigned exp : _FP_EXPBITS_Q; - unsigned long frac1 : _FP_FRACBITS_Q-(_FP_IMPLBIT_Q != 0)-_FP_W_TYPE_SIZE; - unsigned long frac0 : _FP_W_TYPE_SIZE; -#else - unsigned long frac0 : _FP_W_TYPE_SIZE; - unsigned long frac1 : _FP_FRACBITS_Q-(_FP_IMPLBIT_Q != 0)-_FP_W_TYPE_SIZE; - unsigned exp : _FP_EXPBITS_Q; - unsigned sign : 1; -#endif - } bits; -}; - -#define FP_DECL_Q(X) _FP_DECL(2,X) -#define FP_UNPACK_RAW_Q(X,val) _FP_UNPACK_RAW_2(Q,X,val) -#define FP_PACK_RAW_Q(val,X) _FP_PACK_RAW_2(Q,val,X) - -#define FP_UNPACK_Q(X,val) \ - do { \ - _FP_UNPACK_RAW_2(Q,X,val); \ - _FP_UNPACK_CANONICAL(Q,2,X); \ - } while (0) - -#define FP_PACK_Q(val,X) \ - do { \ - _FP_PACK_CANONICAL(Q,2,X); \ - _FP_PACK_RAW_2(Q,val,X); \ - } while (0) - -#define FP_NEG_Q(R,X) _FP_NEG(Q,2,R,X) -#define FP_ADD_Q(R,X,Y) _FP_ADD(Q,2,R,X,Y) -#define FP_SUB_Q(R,X,Y) _FP_SUB(Q,2,R,X,Y) -#define FP_MUL_Q(R,X,Y) _FP_MUL(Q,2,R,X,Y) -#define FP_DIV_Q(R,X,Y) _FP_DIV(Q,2,R,X,Y) -#define FP_SQRT_Q(R,X) _FP_SQRT(Q,2,R,X) - -#define FP_CMP_Q(r,X,Y,un) _FP_CMP(Q,2,r,X,Y,un) -#define FP_CMP_EQ_Q(r,X,Y) _FP_CMP_EQ(Q,2,r,X,Y) - -#define FP_TO_INT_Q(r,X,rsz,rsg) _FP_TO_INT(Q,2,r,X,rsz,rsg) -#define FP_FROM_INT_Q(X,r,rs,rt) _FP_FROM_INT(Q,2,X,r,rs,rt) - -#endif /* not _FP_W_TYPE_SIZE < 64 */ diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/sfp-machine.h linux/arch/sparc64/math-emu/sfp-machine.h --- v2.2.13/linux/arch/sparc64/math-emu/sfp-machine.h Mon Aug 9 16:05:55 1999 +++ linux/arch/sparc64/math-emu/sfp-machine.h Tue Jan 4 10:12:13 2000 @@ -1,6 +1,10 @@ -/* Machine-dependent software floating-point definitions. Sparc64 version. - Copyright (C) 1997 Free Software Foundation, Inc. +/* Machine-dependent software floating-point definitions. + Sparc64 kernel version. + Copyright (C) 1997,1998,1999 Free Software Foundation, Inc. This file is part of the GNU C Library. + Contributed by Richard Henderson (rth@cygnus.com), + Jakub Jelinek (jj@ultra.linux.cz) and + David S. Miller (davem@redhat.com). The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as @@ -17,253 +21,71 @@ not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#ifndef _SFP_MACHINE_H +#define _SFP_MACHINE_H + #define _FP_W_TYPE_SIZE 64 #define _FP_W_TYPE unsigned long #define _FP_WS_TYPE signed long #define _FP_I_TYPE long -#define _FP_MUL_MEAT_S(R,X,Y) _FP_MUL_MEAT_1_imm(S,R,X,Y) -#define _FP_MUL_MEAT_D(R,X,Y) _FP_MUL_MEAT_1_wide(D,R,X,Y,umul_ppmm) -#define _FP_MUL_MEAT_Q(R,X,Y) _FP_MUL_MEAT_2_wide(Q,R,X,Y,umul_ppmm) +#define _FP_MUL_MEAT_S(R,X,Y) \ + _FP_MUL_MEAT_1_imm(_FP_WFRACBITS_S,R,X,Y) +#define _FP_MUL_MEAT_D(R,X,Y) \ + _FP_MUL_MEAT_1_wide(_FP_WFRACBITS_D,R,X,Y,umul_ppmm) +#define _FP_MUL_MEAT_Q(R,X,Y) \ + _FP_MUL_MEAT_2_wide_3mul(_FP_WFRACBITS_Q,R,X,Y,umul_ppmm) #define _FP_DIV_MEAT_S(R,X,Y) _FP_DIV_MEAT_1_imm(S,R,X,Y,_FP_DIV_HELP_imm) #define _FP_DIV_MEAT_D(R,X,Y) _FP_DIV_MEAT_1_udiv(D,R,X,Y) -#define _FP_DIV_MEAT_Q(R,X,Y) _FP_DIV_MEAT_2_udiv_64(Q,R,X,Y) +#define _FP_DIV_MEAT_Q(R,X,Y) _FP_DIV_MEAT_2_udiv(Q,R,X,Y) -#define _FP_NANFRAC_S _FP_QNANBIT_S -#define _FP_NANFRAC_D _FP_QNANBIT_D -#define _FP_NANFRAC_Q _FP_QNANBIT_Q, 0 - -/* On some architectures float-to-int conversions return a result - * code. On others (e.g. Sparc) they return 0 - */ -#define _FTOI_RESULT 0 +#define _FP_NANFRAC_S ((_FP_QNANBIT_S << 1) - 1) +#define _FP_NANFRAC_D ((_FP_QNANBIT_D << 1) - 1) +#define _FP_NANFRAC_Q ((_FP_QNANBIT_Q << 1) - 1), -1 +#define _FP_NANSIGN_S 0 +#define _FP_NANSIGN_D 0 +#define _FP_NANSIGN_Q 0 #define _FP_KEEPNANFRACP 1 -#define _FP_CHOOSENAN(fs, wc, R, X, Y) \ - do { \ - R##_s = Y##_s; \ - _FP_FRAC_COPY_##wc(R,Y); \ - R##_c = FP_CLS_NAN; \ - } while (0) - -#define _FP_CHOOSENAN_SQRT(fs, wc, R, X) \ - do { \ - R##_s = 0; \ - R##_c = FP_CLS_NAN; \ - _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ - } while (0) - - -#define __FP_UNPACK_DENORM(fs, wc, X) \ - { \ - _FP_I_TYPE _shift; \ - _FP_FRAC_CLZ_##wc(_shift, X); \ - _shift -= _FP_FRACXBITS_##fs; \ - _FP_FRAC_SLL_##wc(X, (_shift+_FP_WORKBITS)); \ - X##_e -= _FP_EXPBIAS_##fs - 1 + _shift; \ - X##_c = FP_CLS_NORMAL; \ - } - - -#define __FP_UNPACK_RAW_1(fs, X, val) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - X##_f = _flo->bits.frac; \ - X##_e = _flo->bits.exp; \ - X##_s = _flo->bits.sign; \ - } while (0) - -#define __FP_UNPACK_RAW_2(fs, X, val) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - X##_f0 = _flo->bits.frac0; \ - X##_f1 = _flo->bits.frac1; \ - X##_e = _flo->bits.exp; \ - X##_s = _flo->bits.sign; \ - } while (0) - -#define __FP_UNPACK_S(X,val) \ - do { \ - __FP_UNPACK_RAW_1(S,X,val); \ - _FP_UNPACK_CANONICAL(S,1,X); \ - } while (0) -#define __FP_UNPACK_D(X,val) \ - do { \ - __FP_UNPACK_RAW_1(D,X,val); \ - _FP_UNPACK_CANONICAL(D,1,X); \ - } while (0) - -#define __FP_UNPACK_Q(X,val) \ - do { \ - __FP_UNPACK_RAW_2(Q,X,val); \ - _FP_UNPACK_CANONICAL(Q,2,X); \ - } while (0) - -#define __FP_PACK_RAW_1(fs, val, X) \ +/* If one NaN is signaling and the other is not, + * we choose that one, otherwise we choose X. + */ +/* For _Qp_* and _Q_*, this should prefer X, for + * CPU instruction emulation this should prefer Y. + * (see SPAMv9 B.2.2 section). + */ +#define _FP_CHOOSENAN(fs, wc, R, X, Y, OP) \ do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - _flo->bits.frac = X##_f; \ - _flo->bits.exp = X##_e; \ - _flo->bits.sign = X##_s; \ - } while (0) - -#define __FP_PACK_RAW_2(fs, val, X) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - _flo->bits.frac0 = X##_f0; \ - _flo->bits.frac1 = X##_f1; \ - _flo->bits.exp = X##_e; \ - _flo->bits.sign = X##_s; \ + if ((_FP_FRAC_HIGH_RAW_##fs(Y) & _FP_QNANBIT_##fs) \ + && !(_FP_FRAC_HIGH_RAW_##fs(X) & _FP_QNANBIT_##fs)) \ + { \ + R##_s = X##_s; \ + _FP_FRAC_COPY_##wc(R,X); \ + } \ + else \ + { \ + R##_s = Y##_s; \ + _FP_FRAC_COPY_##wc(R,Y); \ + } \ + R##_c = FP_CLS_NAN; \ } while (0) -#include -#include - -/* We only actually write to the destination register - * if exceptions signalled (if any) will not trap. - */ -#define __FPU_TEM \ - (((current->tss.xfsr[0])>>23)&0x1f) -#define __FPU_TRAP_P(bits) \ - ((__FPU_TEM & (bits)) != 0) - -#define __FP_PACK_S(val,X) \ -({ int __exc = _FP_PACK_CANONICAL(S,1,X); \ - if(!__exc || !__FPU_TRAP_P(__exc)) \ - __FP_PACK_RAW_1(S,val,X); \ - __exc; \ -}) - -#define __FP_PACK_D(val,X) \ -({ int __exc = _FP_PACK_CANONICAL(D,1,X); \ - if(!__exc || !__FPU_TRAP_P(__exc)) \ - __FP_PACK_RAW_1(D,val,X); \ - __exc; \ -}) - -#define __FP_PACK_Q(val,X) \ -({ int __exc = _FP_PACK_CANONICAL(Q,2,X); \ - if(!__exc || !__FPU_TRAP_P(__exc)) \ - __FP_PACK_RAW_2(Q,val,X); \ - __exc; \ -}) - /* Obtain the current rounding mode. */ +#ifndef FP_ROUNDMODE #define FP_ROUNDMODE ((current->tss.xfsr[0] >> 30) & 0x3) +#endif -#include -#include - -#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ - __asm__ ("addcc %4,%5,%1 - add %2,%3,%0 - bcs,a,pn %%xcc, 1f - add %0, 1, %0 - 1:" \ - : "=r" ((UDItype)(sh)), \ - "=&r" ((UDItype)(sl)) \ - : "r" ((UDItype)(ah)), \ - "r" ((UDItype)(bh)), \ - "r" ((UDItype)(al)), \ - "r" ((UDItype)(bl)) \ - : "cc") - -#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ - __asm__ ("subcc %4,%5,%1 - sub %2,%3,%0 - bcs,a,pn %%xcc, 1f - sub %0, 1, %0 - 1:" \ - : "=r" ((UDItype)(sh)), \ - "=&r" ((UDItype)(sl)) \ - : "r" ((UDItype)(ah)), \ - "r" ((UDItype)(bh)), \ - "r" ((UDItype)(al)), \ - "r" ((UDItype)(bl)) \ - : "cc") - -#define umul_ppmm(wh, wl, u, v) \ - do { \ - __asm__ ("mulx %2,%3,%1 - srlx %2,32,%%g1 - srl %3,0,%%g2 - mulx %%g1,%%g2,%%g3 - srlx %3,32,%%g1 - srl %2,0,%%g2 - mulx %%g1,%%g2,%%g2 - srlx %2,32,%%g1 - add %%g2,%%g3,%%g3 - srlx %3,32,%%g2 - mulx %%g1,%%g2,%%g1 - srlx %%g3,32,%%g2 - add %%g1,%%g2,%0" \ - : "=r" ((UDItype)(wh)), \ - "=&r" ((UDItype)(wl)) \ - : "r" ((UDItype)(u)), \ - "r" ((UDItype)(v)) \ - : "g1", "g2", "g3", "cc"); \ - } while (0) - -#define udiv_qrnnd(q, r, n1, n0, d) \ - do { \ - UWtype __d1, __d0, __q1, __q0, __r1, __r0, __m; \ - __d1 = (d >> 32); \ - __d0 = (USItype)d; \ - \ - __r1 = (n1) % __d1; \ - __q1 = (n1) / __d1; \ - __m = (UWtype) __q1 * __d0; \ - __r1 = (__r1 << 32) | (n0 >> 32); \ - if (__r1 < __m) \ - { \ - __q1--, __r1 += (d); \ - if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */ \ - if (__r1 < __m) \ - __q1--, __r1 += (d); \ - } \ - __r1 -= __m; \ - \ - __r0 = __r1 % __d1; \ - __q0 = __r1 / __d1; \ - __m = (UWtype) __q0 * __d0; \ - __r0 = (__r0 << 32) | ((USItype)n0); \ - if (__r0 < __m) \ - { \ - __q0--, __r0 += (d); \ - if (__r0 >= (d)) \ - if (__r0 < __m) \ - __q0--, __r0 += (d); \ - } \ - __r0 -= __m; \ - \ - (q) = (UWtype) (__q1 << 32) | __q0; \ - (r) = __r0; \ - } while (0) +/* Exception flags. */ +#define FP_EX_INVALID (1 << 4) +#define FP_EX_OVERFLOW (1 << 3) +#define FP_EX_UNDERFLOW (1 << 2) +#define FP_EX_DIVZERO (1 << 1) +#define FP_EX_INEXACT (1 << 0) -#define UDIV_NEEDS_NORMALIZATION 1 +#define FP_HANDLE_EXCEPTIONS return _fex -#define abort() \ - return 0 +#define FP_INHIBIT_RESULTS ((current->tss.xfsr[0] >> 23) & _fex) -#ifdef __BIG_ENDIAN -#define __BYTE_ORDER __BIG_ENDIAN -#else -#define __BYTE_ORDER __LITTLE_ENDIAN #endif - -/* Exception flags. */ -#define EFLAG_INVALID (1 << 4) -#define EFLAG_OVERFLOW (1 << 3) -#define EFLAG_UNDERFLOW (1 << 2) -#define EFLAG_DIVZERO (1 << 1) -#define EFLAG_INEXACT (1 << 0) diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/sfp-util.h linux/arch/sparc64/math-emu/sfp-util.h --- v2.2.13/linux/arch/sparc64/math-emu/sfp-util.h Wed Dec 31 16:00:00 1969 +++ linux/arch/sparc64/math-emu/sfp-util.h Tue Jan 4 10:12:13 2000 @@ -0,0 +1,120 @@ +/* $Id: sfp-util.h,v 1.3.2.1 1999/09/20 12:06:23 jj Exp $ + * arch/sparc64/math-emu/sfp-util.h + * + * Copyright (C) 1999 Jakub Jelinek (jj@ultra.linux.cz) + * Copyright (C) 1999 David S. Miller (davem@redhat.com) + * + */ + +#include +#include +#include +#include + +#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ + __asm__ ("addcc %4,%5,%1 + add %2,%3,%0 + bcs,a,pn %%xcc, 1f + add %0, 1, %0 + 1:" \ + : "=r" ((UDItype)(sh)), \ + "=&r" ((UDItype)(sl)) \ + : "r" ((UDItype)(ah)), \ + "r" ((UDItype)(bh)), \ + "r" ((UDItype)(al)), \ + "r" ((UDItype)(bl)) \ + : "cc") + +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + __asm__ ("subcc %4,%5,%1 + sub %2,%3,%0 + bcs,a,pn %%xcc, 1f + sub %0, 1, %0 + 1:" \ + : "=r" ((UDItype)(sh)), \ + "=&r" ((UDItype)(sl)) \ + : "r" ((UDItype)(ah)), \ + "r" ((UDItype)(bh)), \ + "r" ((UDItype)(al)), \ + "r" ((UDItype)(bl)) \ + : "cc") + +#define umul_ppmm(wh, wl, u, v) \ + do { \ + UDItype tmp1, tmp2, tmp3, tmp4; \ + __asm__ __volatile__ ( \ + "srl %7,0,%3 + mulx %3,%6,%1 + srlx %6,32,%2 + mulx %2,%3,%4 + sllx %4,32,%5 + srl %6,0,%3 + sub %1,%5,%5 + srlx %5,32,%5 + addcc %4,%5,%4 + srlx %7,32,%5 + mulx %3,%5,%3 + mulx %2,%5,%5 + sethi %%hi(0x80000000),%2 + addcc %4,%3,%4 + srlx %4,32,%4 + add %2,%2,%2 + movcc %%xcc,%%g0,%2 + addcc %5,%4,%5 + sllx %3,32,%3 + add %1,%3,%1 + add %5,%2,%0" \ + : "=r" ((UDItype)(wh)), \ + "=&r" ((UDItype)(wl)), \ + "=&r" (tmp1), "=&r" (tmp2), "=&r" (tmp3), "=&r" (tmp4) \ + : "r" ((UDItype)(u)), \ + "r" ((UDItype)(v)) \ + : "cc"); \ + } while (0) + +#define udiv_qrnnd(q, r, n1, n0, d) \ + do { \ + UWtype __d1, __d0, __q1, __q0, __r1, __r0, __m; \ + __d1 = (d >> 32); \ + __d0 = (USItype)d; \ + \ + __r1 = (n1) % __d1; \ + __q1 = (n1) / __d1; \ + __m = (UWtype) __q1 * __d0; \ + __r1 = (__r1 << 32) | (n0 >> 32); \ + if (__r1 < __m) \ + { \ + __q1--, __r1 += (d); \ + if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */ \ + if (__r1 < __m) \ + __q1--, __r1 += (d); \ + } \ + __r1 -= __m; \ + \ + __r0 = __r1 % __d1; \ + __q0 = __r1 / __d1; \ + __m = (UWtype) __q0 * __d0; \ + __r0 = (__r0 << 32) | ((USItype)n0); \ + if (__r0 < __m) \ + { \ + __q0--, __r0 += (d); \ + if (__r0 >= (d)) \ + if (__r0 < __m) \ + __q0--, __r0 += (d); \ + } \ + __r0 -= __m; \ + \ + (q) = (UWtype) (__q1 << 32) | __q0; \ + (r) = __r0; \ + } while (0) + +#define UDIV_NEEDS_NORMALIZATION 1 + +#define abort() \ + return 0 + +#ifdef __BIG_ENDIAN +#define __BYTE_ORDER __BIG_ENDIAN +#else +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/single.h linux/arch/sparc64/math-emu/single.h --- v2.2.13/linux/arch/sparc64/math-emu/single.h Tue Apr 14 17:44:21 1998 +++ linux/arch/sparc64/math-emu/single.h Wed Dec 31 16:00:00 1969 @@ -1,66 +0,0 @@ -/* - * Definitions for IEEE Single Precision - */ - -#if _FP_W_TYPE_SIZE < 32 -#error "Here's a nickel kid. Go buy yourself a real computer." -#endif - -#define _FP_FRACBITS_S 24 -#define _FP_FRACXBITS_S (_FP_W_TYPE_SIZE - _FP_FRACBITS_S) -#define _FP_WFRACBITS_S (_FP_WORKBITS + _FP_FRACBITS_S) -#define _FP_WFRACXBITS_S (_FP_W_TYPE_SIZE - _FP_WFRACBITS_S) -#define _FP_EXPBITS_S 8 -#define _FP_EXPBIAS_S 127 -#define _FP_EXPMAX_S 255 -#define _FP_QNANBIT_S ((_FP_W_TYPE)1 << (_FP_FRACBITS_S-2)) -#define _FP_IMPLBIT_S ((_FP_W_TYPE)1 << (_FP_FRACBITS_S-1)) -#define _FP_OVERFLOW_S ((_FP_W_TYPE)1 << (_FP_WFRACBITS_S)) - -/* The implementation of _FP_MUL_MEAT_S and _FP_DIV_MEAT_S should be - chosen by the target machine. */ - -union _FP_UNION_S -{ - float flt; - struct { -#if __BYTE_ORDER == __BIG_ENDIAN - unsigned sign : 1; - unsigned exp : _FP_EXPBITS_S; - unsigned frac : _FP_FRACBITS_S - (_FP_IMPLBIT_S != 0); -#else - unsigned frac : _FP_FRACBITS_S - (_FP_IMPLBIT_S != 0); - unsigned exp : _FP_EXPBITS_S; - unsigned sign : 1; -#endif - } bits __attribute__((packed)); -}; - -#define FP_DECL_S(X) _FP_DECL(1,X) -#define FP_UNPACK_RAW_S(X,val) _FP_UNPACK_RAW_1(S,X,val) -#define FP_PACK_RAW_S(val,X) _FP_PACK_RAW_1(S,val,X) - -#define FP_UNPACK_S(X,val) \ - do { \ - _FP_UNPACK_RAW_1(S,X,val); \ - _FP_UNPACK_CANONICAL(S,1,X); \ - } while (0) - -#define FP_PACK_S(val,X) \ - do { \ - _FP_PACK_CANONICAL(S,1,X); \ - _FP_PACK_RAW_1(S,val,X); \ - } while (0) - -#define FP_NEG_S(R,X) _FP_NEG(S,1,R,X) -#define FP_ADD_S(R,X,Y) _FP_ADD(S,1,R,X,Y) -#define FP_SUB_S(R,X,Y) _FP_SUB(S,1,R,X,Y) -#define FP_MUL_S(R,X,Y) _FP_MUL(S,1,R,X,Y) -#define FP_DIV_S(R,X,Y) _FP_DIV(S,1,R,X,Y) -#define FP_SQRT_S(R,X) _FP_SQRT(S,1,R,X) - -#define FP_CMP_S(r,X,Y,un) _FP_CMP(S,1,r,X,Y,un) -#define FP_CMP_EQ_S(r,X,Y) _FP_CMP_EQ(S,1,r,X,Y) - -#define FP_TO_INT_S(r,X,rsz,rsg) _FP_TO_INT(S,1,r,X,rsz,rsg) -#define FP_FROM_INT_S(X,r,rs,rt) _FP_FROM_INT(S,1,X,r,rs,rt) diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/soft-fp.h linux/arch/sparc64/math-emu/soft-fp.h --- v2.2.13/linux/arch/sparc64/math-emu/soft-fp.h Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/soft-fp.h Wed Dec 31 16:00:00 1969 @@ -1,94 +0,0 @@ -#ifndef SOFT_FP_H -#define SOFT_FP_H - -#include "sfp-machine.h" - -#define _FP_WORKBITS 3 -#define _FP_WORK_LSB ((_FP_W_TYPE)1 << 3) -#define _FP_WORK_ROUND ((_FP_W_TYPE)1 << 2) -#define _FP_WORK_GUARD ((_FP_W_TYPE)1 << 1) -#define _FP_WORK_STICKY ((_FP_W_TYPE)1 << 0) - -#ifndef FP_RND_NEAREST -# define FP_RND_NEAREST 0 -# define FP_RND_ZERO 1 -# define FP_RND_PINF 2 -# define FP_RND_MINF 3 -#ifndef FP_ROUNDMODE -# define FP_ROUNDMODE FP_RND_NEAREST -#endif -#endif - -#define _FP_ROUND_NEAREST(wc, X) \ -({ int __ret = EFLAG_INEXACT; \ - if ((_FP_FRAC_LOW_##wc(X) & 15) != _FP_WORK_ROUND) \ - _FP_FRAC_ADDI_##wc(X, _FP_WORK_ROUND); \ - else __ret = 0; \ - __ret; \ -}) - -#define _FP_ROUND_ZERO(wc, X) 0 /* XXX */ - -#define _FP_ROUND_PINF(wc, X) \ -({ int __ret = EFLAG_INEXACT; \ - if (!X##_s && (_FP_FRAC_LOW_##wc(X) & 7)) \ - _FP_FRAC_ADDI_##wc(X, _FP_WORK_LSB); \ - else __ret = 0; \ - __ret; \ -}) - -#define _FP_ROUND_MINF(wc, X) \ -({ int __ret = EFLAG_INEXACT; \ - if (X##_s && (_FP_FRAC_LOW_##wc(X) & 7)) \ - _FP_FRAC_ADDI_##wc(X, _FP_WORK_LSB); \ - else __ret = 0; \ - __ret; \ -}) - -#define _FP_ROUND(wc, X) \ -({ int __ret = 0; \ - switch (FP_ROUNDMODE) \ - { \ - case FP_RND_NEAREST: \ - __ret |= _FP_ROUND_NEAREST(wc,X); \ - break; \ - case FP_RND_ZERO: \ - __ret |= _FP_ROUND_ZERO(wc,X); \ - break; \ - case FP_RND_PINF: \ - __ret |= _FP_ROUND_PINF(wc,X); \ - break; \ - case FP_RND_MINF: \ - __ret |= _FP_ROUND_MINF(wc,X); \ - break; \ - }; \ - __ret; \ -}) - -#define FP_CLS_NORMAL 0 -#define FP_CLS_ZERO 1 -#define FP_CLS_INF 2 -#define FP_CLS_NAN 3 - -#define _FP_CLS_COMBINE(x,y) (((x) << 2) | (y)) - -#include "op-1.h" -#include "op-2.h" -#include "op-4.h" -#include "op-common.h" - -/* Sigh. Silly things longlong.h needs. */ -#define UWtype _FP_W_TYPE -#define W_TYPE_SIZE _FP_W_TYPE_SIZE - -typedef int SItype __attribute__((mode(SI))); -typedef int DItype __attribute__((mode(DI))); -typedef unsigned int USItype __attribute__((mode(SI))); -typedef unsigned int UDItype __attribute__((mode(DI))); -#if _FP_W_TYPE_SIZE == 32 -typedef unsigned int UHWtype __attribute__((mode(HI))); -#elif _FP_W_TYPE_SIZE == 64 -typedef USItype UHWtype; -#endif - -#endif diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/math-emu/udivmodti4.c linux/arch/sparc64/math-emu/udivmodti4.c --- v2.2.13/linux/arch/sparc64/math-emu/udivmodti4.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/udivmodti4.c Wed Dec 31 16:00:00 1969 @@ -1,191 +0,0 @@ -/* This has so very few changes over libgcc2's __udivmoddi4 it isn't funny. */ - -#include "soft-fp.h" - -#undef count_leading_zeros -#define count_leading_zeros __FP_CLZ - -void -_fp_udivmodti4(_FP_W_TYPE q[2], _FP_W_TYPE r[2], - _FP_W_TYPE n1, _FP_W_TYPE n0, - _FP_W_TYPE d1, _FP_W_TYPE d0) -{ - _FP_W_TYPE q0, q1, r0, r1; - _FP_I_TYPE b, bm; - - if (d1 == 0) - { -#if !UDIV_NEEDS_NORMALIZATION - if (d0 > n1) - { - /* 0q = nn / 0D */ - - udiv_qrnnd (q0, n0, n1, n0, d0); - q1 = 0; - - /* Remainder in n0. */ - } - else - { - /* qq = NN / 0d */ - - if (d0 == 0) - d0 = 1 / d0; /* Divide intentionally by zero. */ - - udiv_qrnnd (q1, n1, 0, n1, d0); - udiv_qrnnd (q0, n0, n1, n0, d0); - - /* Remainder in n0. */ - } - - r0 = n0; - r1 = 0; - -#else /* UDIV_NEEDS_NORMALIZATION */ - - if (d0 > n1) - { - /* 0q = nn / 0D */ - - count_leading_zeros (bm, d0); - - if (bm != 0) - { - /* Normalize, i.e. make the most significant bit of the - denominator set. */ - - d0 = d0 << bm; - n1 = (n1 << bm) | (n0 >> (_FP_W_TYPE_SIZE - bm)); - n0 = n0 << bm; - } - - udiv_qrnnd (q0, n0, n1, n0, d0); - q1 = 0; - - /* Remainder in n0 >> bm. */ - } - else - { - /* qq = NN / 0d */ - - if (d0 == 0) - d0 = 1 / d0; /* Divide intentionally by zero. */ - - count_leading_zeros (bm, d0); - - if (bm == 0) - { - /* From (n1 >= d0) /\ (the most significant bit of d0 is set), - conclude (the most significant bit of n1 is set) /\ (the - leading quotient digit q1 = 1). - - This special case is necessary, not an optimization. - (Shifts counts of SI_TYPE_SIZE are undefined.) */ - - n1 -= d0; - q1 = 1; - } - else - { - _FP_W_TYPE n2; - - /* Normalize. */ - - b = _FP_W_TYPE_SIZE - bm; - - d0 = d0 << bm; - n2 = n1 >> b; - n1 = (n1 << bm) | (n0 >> b); - n0 = n0 << bm; - - udiv_qrnnd (q1, n1, n2, n1, d0); - } - - /* n1 != d0... */ - - udiv_qrnnd (q0, n0, n1, n0, d0); - - /* Remainder in n0 >> bm. */ - } - - r0 = n0 >> bm; - r1 = 0; -#endif /* UDIV_NEEDS_NORMALIZATION */ - } - else - { - if (d1 > n1) - { - /* 00 = nn / DD */ - - q0 = 0; - q1 = 0; - - /* Remainder in n1n0. */ - r0 = n0; - r1 = n1; - } - else - { - /* 0q = NN / dd */ - - count_leading_zeros (bm, d1); - if (bm == 0) - { - /* From (n1 >= d1) /\ (the most significant bit of d1 is set), - conclude (the most significant bit of n1 is set) /\ (the - quotient digit q0 = 0 or 1). - - This special case is necessary, not an optimization. */ - - /* The condition on the next line takes advantage of that - n1 >= d1 (true due to program flow). */ - if (n1 > d1 || n0 >= d0) - { - q0 = 1; - sub_ddmmss (n1, n0, n1, n0, d1, d0); - } - else - q0 = 0; - - q1 = 0; - - r0 = n0; - r1 = n1; - } - else - { - _FP_W_TYPE m1, m0, n2; - - /* Normalize. */ - - b = _FP_W_TYPE_SIZE - bm; - - d1 = (d1 << bm) | (d0 >> b); - d0 = d0 << bm; - n2 = n1 >> b; - n1 = (n1 << bm) | (n0 >> b); - n0 = n0 << bm; - - udiv_qrnnd (q0, n1, n2, n1, d1); - umul_ppmm (m1, m0, q0, d0); - - if (m1 > n1 || (m1 == n1 && m0 > n0)) - { - q0--; - sub_ddmmss (m1, m0, m1, m0, d1, d0); - } - - q1 = 0; - - /* Remainder in (n1n0 - m1m0) >> bm. */ - sub_ddmmss (n1, n0, n1, n0, m1, m0); - r0 = (n1 << b) | (n0 >> bm); - r1 = n1 >> bm; - } - } - } - - q[0] = q0; q[1] = q1; - r[0] = r0, r[1] = r1; -} diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/mm/asyncd.c linux/arch/sparc64/mm/asyncd.c --- v2.2.13/linux/arch/sparc64/mm/asyncd.c Sun Oct 4 10:22:43 1998 +++ linux/arch/sparc64/mm/asyncd.c Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: asyncd.c,v 1.5 1998/09/13 04:30:33 davem Exp $ +/* $Id: asyncd.c,v 1.5.2.1 1999/11/16 06:29:53 davem Exp $ * The asyncd kernel daemon. This handles paging on behalf of * processes that receive page faults due to remote (async) memory * accesses. @@ -152,7 +152,9 @@ if(!pte) goto no_memory; if(!pte_present(*pte)) { - handle_mm_fault(tsk, vma, address, write); + int fault = handle_mm_fault(tsk, vma, address, write); + if (fault < 0) + goto no_memory; goto finish_up; } set_pte(pte, pte_mkyoung(*pte)); @@ -164,7 +166,11 @@ flush_tlb_page(vma, address); goto finish_up; } - handle_mm_fault(tsk, vma, address, write); + { + int fault = handle_mm_fault(tsk, vma, address, write); + if (fault < 0) + goto no_memory; + } /* Fall through for do_wp_page */ finish_up: @@ -173,7 +179,7 @@ no_memory: stats.failure++; - oom(tsk); + force_sig(SIGKILL, tsk); return 1; bad_area: diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/mm/fault.c linux/arch/sparc64/mm/fault.c --- v2.2.13/linux/arch/sparc64/mm/fault.c Tue Mar 16 21:52:06 1999 +++ linux/arch/sparc64/mm/fault.c Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: fault.c,v 1.34 1999/03/16 12:12:28 jj Exp $ +/* $Id: fault.c,v 1.34.2.1 1999/11/16 06:29:56 davem Exp $ * arch/sparc64/mm/fault.c: Page fault handlers for the 64-bit Sparc. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -210,8 +210,15 @@ goto bad_area; } current->mm->segments = (void *) (address & PAGE_SIZE); - if (!handle_mm_fault(current, vma, address, write)) - goto do_sigbus; +survive: + { + int fault = handle_mm_fault(current, vma, address, write); + if (!fault) + goto do_sigbus; + if (fault < 0) + goto out_of_memory; + } + up(&mm->mmap_sem); return; /* @@ -289,6 +296,17 @@ } unhandled_fault (address, current, regs); } + return; + +out_of_memory: + if (current->pid == 1) { + current->policy |= SCHED_YIELD; + schedule(); + goto survive; + } + up(&mm->mmap_sem); + printk("VM: killing process %s\n", current->comm); + do_exit(SIGKILL); return; do_sigbus: diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/mm/init.c linux/arch/sparc64/mm/init.c --- v2.2.13/linux/arch/sparc64/mm/init.c Mon Aug 9 16:05:55 1999 +++ linux/arch/sparc64/mm/init.c Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: init.c,v 1.127.2.1 1999/06/25 10:42:10 davem Exp $ +/* $Id: init.c,v 1.127.2.6 1999/12/05 07:24:42 davem Exp $ * arch/sparc64/mm/init.c * * Copyright (C) 1996-1999 David S. Miller (davem@caip.rutgers.edu) @@ -40,9 +40,6 @@ /* Ugly, but necessary... -DaveM */ unsigned long phys_base; -/* get_new_mmu_context() uses "cache + 1". */ -unsigned long tlb_context_cache = CTX_FIRST_VERSION - 1; - /* References to section boundaries */ extern char __init_begin, __init_end, etext, __bss_start; @@ -56,8 +53,10 @@ if(pgd_quicklist) free_pgd_slow(get_pgd_fast()), freed++; #endif - if(pte_quicklist) - free_pte_slow(get_pte_fast()), freed++; + if(pte_quicklist[0]) + free_pte_slow(get_pte_fast(0)), freed++; + if(pte_quicklist[1]) + free_pte_slow(get_pte_fast(1)), freed++; } while(pgtable_cache_size > low); } #ifndef __SMP__ @@ -646,13 +645,23 @@ unsigned long data; }; -static inline void inherit_prom_mappings(void) +extern unsigned long prom_boot_page; +extern void prom_remap(unsigned long physpage, unsigned long virtpage, int mmu_ihandle); +extern int prom_get_mmu_ihandle(void); +extern void register_prom_callbacks(void); + +/* Exported for SMP bootup purposes. */ +unsigned long kern_locked_tte_data; + +static void inherit_prom_mappings(void) { struct linux_prom_translation *trans; + unsigned long phys_page, tte_vaddr, tte_data; + void (*remap_func)(unsigned long, unsigned long, int); pgd_t *pgdp; pmd_t *pmdp; pte_t *ptep; - int node, n, i; + int node, n, i, tsz; node = prom_finddevice("/virtual-memory"); n = prom_getproplen(node, "translations"); @@ -660,11 +669,12 @@ prom_printf("Couldn't get translation property\n"); prom_halt(); } + n += 5 * sizeof(struct linux_prom_translation); + for (tsz = 1; tsz < n; tsz <<= 1) + /* empty */; + trans = sparc_init_alloc(&mempool, tsz); - for (i = 1; i < n; i <<= 1) /* empty */; - trans = sparc_init_alloc(&mempool, i); - - if (prom_getproperty(node, "translations", (char *)trans, i) == -1) { + if ((n = prom_getproperty(node, "translations", (char *)trans, tsz)) == -1) { prom_printf("Couldn't get translation property\n"); prom_halt(); } @@ -696,6 +706,83 @@ } } } + + /* Now fixup OBP's idea about where we really are mapped. */ + prom_printf("Remapping the kernel... "); + phys_page = spitfire_get_dtlb_data(63) & _PAGE_PADDR; + phys_page += ((unsigned long)&prom_boot_page - + (unsigned long)&empty_zero_page); + + /* Lock this into i/d tlb entry 59 */ + __asm__ __volatile__( + "stxa %%g0, [%2] %3\n\t" + "stxa %0, [%1] %4\n\t" + "membar #Sync\n\t" + "flush %%g6\n\t" + "stxa %%g0, [%2] %5\n\t" + "stxa %0, [%1] %6\n\t" + "membar #Sync\n\t" + "flush %%g6" + : : "r" (phys_page | _PAGE_VALID | _PAGE_SZ8K | _PAGE_CP | + _PAGE_CV | _PAGE_P | _PAGE_L | _PAGE_W), + "r" (59 << 3), "r" (TLB_TAG_ACCESS), + "i" (ASI_DMMU), "i" (ASI_DTLB_DATA_ACCESS), + "i" (ASI_IMMU), "i" (ASI_ITLB_DATA_ACCESS) + : "memory"); + + tte_vaddr = (unsigned long) &empty_zero_page; + kern_locked_tte_data = tte_data = spitfire_get_dtlb_data(63); + + remap_func = (void *) ((unsigned long) &prom_remap - + (unsigned long) &prom_boot_page); + + remap_func(spitfire_get_dtlb_data(63) & _PAGE_PADDR, + (unsigned long) &empty_zero_page, + prom_get_mmu_ihandle()); + + /* Flush out that temporary mapping. */ + spitfire_flush_dtlb_nucleus_page(0x0); + spitfire_flush_itlb_nucleus_page(0x0); + + /* Now lock us back into the TLBs via OBP. */ + prom_dtlb_load(63, tte_data, tte_vaddr); + prom_itlb_load(63, tte_data, tte_vaddr); + + /* Re-read translations property. */ + if ((n = prom_getproperty(node, "translations", (char *)trans, tsz)) == -1) { + prom_printf("Couldn't get translation property\n"); + prom_halt(); + } + n = n / sizeof(*trans); + + for (i = 0; i < n; i++) { + unsigned long vaddr = trans[i].virt; + unsigned long size = trans[i].size; + + if (vaddr < 0xf0000000UL) { + unsigned long avoid_start = (unsigned long) &empty_zero_page; + unsigned long avoid_end = avoid_start + (4 * 1024 * 1024); + + if (vaddr < avoid_start) { + unsigned long top = vaddr + size; + + if (top > avoid_start) + top = avoid_start; + prom_unmap(top - vaddr, vaddr); + } + if ((vaddr + size) > avoid_end) { + unsigned long bottom = vaddr; + + if (bottom < avoid_end) + bottom = avoid_end; + prom_unmap((vaddr + size) - bottom, bottom); + } + } + } + + prom_printf("done.\n"); + + register_prom_callbacks(); } /* The OBP specifications for sun4u mark 0xfffffffc00000000 and @@ -956,6 +1043,8 @@ #define CTX_BMAP_SLOTS (1UL << (CTX_VERSION_SHIFT - 6)) unsigned long mmu_context_bmap[CTX_BMAP_SLOTS]; +spinlock_t ctx_alloc_lock = SPIN_LOCK_UNLOCKED; +unsigned long tlb_context_cache = CTX_FIRST_VERSION - 1; /* Caller does TLB context flushing on local CPU if necessary. * @@ -966,14 +1055,17 @@ */ void get_new_mmu_context(struct mm_struct *mm) { - unsigned long ctx = (tlb_context_cache + 1) & ~(CTX_VERSION_MASK); - unsigned long new_ctx; + unsigned long ctx, new_ctx; + spin_lock(&ctx_alloc_lock); + ctx = (tlb_context_cache + 1) & ~(CTX_VERSION_MASK); if (ctx == 0) ctx = 1; if ((mm->context != NO_CONTEXT) && - !((mm->context ^ tlb_context_cache) & CTX_VERSION_MASK)) - clear_bit(mm->context & ~(CTX_VERSION_MASK), mmu_context_bmap); + !((mm->context ^ tlb_context_cache) & CTX_VERSION_MASK)) { + unsigned long nr = mm->context & ~(CTX_VERSION_MASK); + mmu_context_bmap[nr >> 6] &= ~(1UL << (nr & 63)); + } new_ctx = find_next_zero_bit(mmu_context_bmap, 1UL << CTX_VERSION_SHIFT, ctx); if (new_ctx >= (1UL << CTX_VERSION_SHIFT)) { new_ctx = find_next_zero_bit(mmu_context_bmap, ctx, 1); @@ -1000,10 +1092,12 @@ goto out; } } - set_bit(new_ctx, mmu_context_bmap); + mmu_context_bmap[new_ctx >> 6] |= (1UL << (new_ctx & 63)); new_ctx |= (tlb_context_cache & CTX_VERSION_MASK); out: tlb_context_cache = new_ctx; + spin_unlock(&ctx_alloc_lock); + mm->context = new_ctx; mm->cpu_vm_mask = 0; } @@ -1012,6 +1106,10 @@ struct pgtable_cache_struct pgt_quicklists; #endif +/* For PMDs we don't care about the color, writes are + * only done via Dcache which is write-thru, so non-Dcache + * reads will always see correct data. + */ pmd_t *get_pmd_slow(pgd_t *pgd, unsigned long offset) { pmd_t *pmd; @@ -1025,13 +1123,51 @@ return NULL; } -pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) +/* OK, we have to color these pages because during DTLB + * protection faults we set the dirty bit via a non-Dcache + * enabled mapping in the VPTE area. The kernel can end + * up missing the dirty bit resulting in processes crashing + * _iff_ the VPTE mapping of the ptes have a virtual address + * bit 13 which is different from bit 13 of the physical address. + * + * The sequence is: + * 1) DTLB protection fault, write dirty bit into pte via VPTE + * mappings. + * 2) Swapper checks pte, does not see dirty bit, frees page. + * 3) Process faults back in the page, the old pre-dirtied copy + * is provided and here is the corruption. + */ +pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset, unsigned long color) { - pte_t *pte; + unsigned long paddr = __get_free_pages(GFP_KERNEL, 1); + + if (paddr) { + struct page *page2 = mem_map + MAP_NR(paddr + PAGE_SIZE); + unsigned long *to_free; + pte_t *pte; + + /* Set count of second page, so we can free it + * seperately later on. + */ + atomic_set(&page2->count, 1); + + /* Clear out both pages now. */ + memset((char *)paddr, 0, (PAGE_SIZE << 1)); + + /* Determine which page we give to this request. */ + if (!color) { + pte = (pte_t *) paddr; + to_free = (unsigned long *) (paddr + PAGE_SIZE); + } else { + pte = (pte_t *) (paddr + PAGE_SIZE); + to_free = (unsigned long *) paddr; + } + + /* Now free the other one up, adjust cache size. */ + *to_free = (unsigned long) pte_quicklist[color ^ 0x1]; + pte_quicklist[color ^ 0x1] = to_free; + pgtable_cache_size++; - pte = (pte_t *) __get_free_page(GFP_KERNEL); - if(pte) { - memset(pte, 0, PAGE_SIZE); pmd_set(pmd, pte); return pte + offset; } @@ -1214,7 +1350,7 @@ /* Allocate 64M for dynamic DVMA mapping area. */ allocate_ptable_skeleton(DVMA_VADDR, DVMA_VADDR + 0x4000000); inherit_prom_mappings(); - + /* Ok, we can use our TLB miss and window trap handlers safely. * We need to do a quick peek here to see if we are on StarFire * or not, so setup_tba can setup the IRQ globals correctly (it @@ -1230,24 +1366,12 @@ setup_tba(is_starfire); } - /* Really paranoid. */ - flushi((long)&empty_zero_page); - membar("#Sync"); - - /* Cleanup the extra locked TLB entry we created since we have the - * nice TLB miss handlers of ours installed now. - */ + inherit_locked_prom_mappings(1); + /* We only created DTLB mapping of this stuff. */ spitfire_flush_dtlb_nucleus_page(alias_base); if (second_alias_page) spitfire_flush_dtlb_nucleus_page(second_alias_page); - membar("#Sync"); - - /* Paranoid */ - flushi((long)&empty_zero_page); - membar("#Sync"); - - inherit_locked_prom_mappings(1); flush_tlb_all(); @@ -1256,11 +1380,97 @@ return device_scan (PAGE_ALIGN (start_mem)); } +/* Ok, it seems that the prom can allocate some more memory chunks + * as a side effect of some prom calls we perform during the + * boot sequence. My most likely theory is that it is from the + * prom_set_traptable() call, and OBP is allocating a scratchpad + * for saving client program register state etc. + */ +__initfunc(static void sort_memlist(struct linux_mlist_p1275 *thislist)) +{ + int swapi = 0; + int i, mitr; + unsigned long tmpaddr, tmpsize; + unsigned long lowest; + + for(i=0; thislist[i].theres_more != 0; i++) { + lowest = thislist[i].start_adr; + for(mitr = i+1; thislist[mitr-1].theres_more != 0; mitr++) + if(thislist[mitr].start_adr < lowest) { + lowest = thislist[mitr].start_adr; + swapi = mitr; + } + if(lowest == thislist[i].start_adr) continue; + tmpaddr = thislist[swapi].start_adr; + tmpsize = thislist[swapi].num_bytes; + for(mitr = swapi; mitr > i; mitr--) { + thislist[mitr].start_adr = thislist[mitr-1].start_adr; + thislist[mitr].num_bytes = thislist[mitr-1].num_bytes; + } + thislist[i].start_adr = tmpaddr; + thislist[i].num_bytes = tmpsize; + } +} + +__initfunc(static void rescan_sp_banks(void)) +{ + struct linux_prom64_registers memlist[64]; + struct linux_mlist_p1275 avail[64], *mlist; + unsigned long bytes, base_paddr; + int num_regs, node = prom_finddevice("/memory"); + int i; + + num_regs = prom_getproperty(node, "available", + (char *) memlist, sizeof(memlist)); + num_regs = (num_regs / sizeof(struct linux_prom64_registers)); + for (i = 0; i < num_regs; i++) { + avail[i].start_adr = memlist[i].phys_addr; + avail[i].num_bytes = memlist[i].reg_size; + avail[i].theres_more = &avail[i + 1]; + } + avail[i - 1].theres_more = NULL; + sort_memlist(avail); + + mlist = &avail[0]; + i = 0; + bytes = mlist->num_bytes; + base_paddr = mlist->start_adr; + + sp_banks[0].base_addr = base_paddr; + sp_banks[0].num_bytes = bytes; + + while (mlist->theres_more != NULL){ + i++; + mlist = mlist->theres_more; + bytes = mlist->num_bytes; + if (i >= SPARC_PHYS_BANKS-1) { + printk ("The machine has more banks than " + "this kernel can support\n" + "Increase the SPARC_PHYS_BANKS " + "setting (currently %d)\n", + SPARC_PHYS_BANKS); + i = SPARC_PHYS_BANKS-1; + break; + } + + sp_banks[i].base_addr = mlist->start_adr; + sp_banks[i].num_bytes = mlist->num_bytes; + } + + i++; + sp_banks[i].base_addr = 0xdeadbeefbeefdeadUL; + sp_banks[i].num_bytes = 0; + + for (i = 0; sp_banks[i].num_bytes != 0; i++) + sp_banks[i].num_bytes &= PAGE_MASK; +} + __initfunc(static void taint_real_pages(unsigned long start_mem, unsigned long end_mem)) { unsigned long tmp = 0, paddr, endaddr; unsigned long end = __pa(end_mem); + rescan_sp_banks(); dvmaio_init(); for (paddr = __pa(start_mem); paddr < end; ) { for (; sp_banks[tmp].num_bytes != 0; tmp++) @@ -1316,6 +1526,7 @@ max_mapnr = MAP_NR(end_mem); high_memory = (void *) end_mem; + start_mem = ((start_mem + 7UL) & ~7UL); sparc64_valid_addr_bitmap = (unsigned long *)start_mem; i = max_mapnr >> ((22 - PAGE_SHIFT) + 6); i += 1; diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/mm/ultra.S linux/arch/sparc64/mm/ultra.S --- v2.2.13/linux/arch/sparc64/mm/ultra.S Sun Mar 28 09:07:47 1999 +++ linux/arch/sparc64/mm/ultra.S Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: ultra.S,v 1.32 1999/03/28 08:39:34 davem Exp $ +/* $Id: ultra.S,v 1.32.2.1 1999/10/24 17:29:34 davem Exp $ * ultra.S: Don't expand these all over the place... * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -173,10 +173,10 @@ * * Register usage: * %g5 mm->context (all tlb flushes) - * %g6 address arg 1 (tlb page and range flushes) + * %g1 address arg 1 (tlb page and range flushes) * %g7 address arg 2 (tlb range flush only) * - * %g1 ivector table, don't touch + * %g6 ivector table, don't touch * %g2 scratch 1 * %g3 scratch 2 * %g4 scratch 3 @@ -187,7 +187,7 @@ .globl xcall_flush_tlb_page, xcall_flush_tlb_mm, xcall_flush_tlb_range xcall_flush_tlb_page: mov SECONDARY_CONTEXT, %g2 - or %g6, 0x10, %g4 + or %g1, 0x10, %g4 ldxa [%g2] ASI_DMMU, %g3 stxa %g5, [%g2] ASI_DMMU stxa %g0, [%g4] ASI_DMMU_DEMAP @@ -208,11 +208,11 @@ xcall_flush_tlb_range: sethi %hi(8192 - 1), %g2 or %g2, %lo(8192 - 1), %g2 - andn %g6, %g2, %g6 + andn %g1, %g2, %g1 andn %g7, %g2, %g7 - sub %g7, %g6, %g3 + sub %g7, %g1, %g3 add %g2, 1, %g2 - orcc %g6, 0x10, %g6 + orcc %g1, 0x10, %g1 srlx %g3, 13, %g4 cmp %g4, 96 @@ -224,8 +224,8 @@ nop nop -1: stxa %g0, [%g6 + %g3] ASI_DMMU_DEMAP - stxa %g0, [%g6 + %g3] ASI_IMMU_DEMAP +1: stxa %g0, [%g1 + %g3] ASI_DMMU_DEMAP + stxa %g0, [%g1 + %g3] ASI_IMMU_DEMAP brnz,pt %g3, 1b sub %g3, %g2, %g3 stxa %g7, [%g4] ASI_DMMU @@ -261,6 +261,22 @@ b,pt %xcc, rtrap clr %l6 + .globl xcall_promstop +xcall_promstop: + rdpr %pstate, %g2 + wrpr %g2, PSTATE_IG | PSTATE_AG, %pstate + rdpr %pil, %g2 + wrpr %g0, 15, %pil + sethi %hi(109f), %g7 + b,pt %xcc, etrap_irq +109: or %g7, %lo(109b), %g7 + flushw + call prom_stopself + nop + /* We should not return, just spin if we do... */ +1: b,a,pt %xcc, 1b + nop + .globl xcall_receive_signal xcall_receive_signal: rdpr %pstate, %g2 @@ -302,7 +318,7 @@ cmp %g2, 63 ble,pt %icc, 1b sll %g2, 3, %g3 - flush %g1 + flush %g6 retry .globl xcall_flush_cache_all @@ -315,6 +331,6 @@ cmp %g3, %g2 bleu,pt %xcc, 1b nop - flush %g1 + flush %g6 retry #endif /* __SMP__ */ diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/prom/Makefile linux/arch/sparc64/prom/Makefile --- v2.2.13/linux/arch/sparc64/prom/Makefile Mon Mar 17 14:54:23 1997 +++ linux/arch/sparc64/prom/Makefile Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.2 1997/02/25 12:40:25 jj Exp $ +# $Id: Makefile,v 1.2.6.1 1999/10/24 17:29:02 davem Exp $ # Makefile for the Sun Boot PROM interface library under # Linux. # @@ -9,13 +9,19 @@ # Note 2! The CFLAGS definitions are now in the main makefile... OBJS = bootstr.o devops.o init.o memory.o misc.o \ - ranges.o tree.o console.o printf.o p1275.o + ranges.o tree.o console.o printf.o p1275.o map.o all: promlib.a promlib.a: $(OBJS) $(AR) rcs promlib.a $(OBJS) sync + +.S.s: + $(CPP) -D__ASSEMBLY__ $(AFLAGS) -ansi $< -o $*.s + +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c $< -o $*.o dep: $(CPP) -M *.c > .depend diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/prom/map.S linux/arch/sparc64/prom/map.S --- v2.2.13/linux/arch/sparc64/prom/map.S Wed Dec 31 16:00:00 1969 +++ linux/arch/sparc64/prom/map.S Tue Jan 4 10:12:13 2000 @@ -0,0 +1,70 @@ +/* $Id: map.S,v 1.1.2.1 1999/10/24 17:28:59 davem Exp $ + * map.S: Tricky coding required to fixup the kernel OBP maps + * properly. + * + * Copyright (C) 1999 David S. Miller (davem@redhat.com) + */ + + .text + .align 8192 + .globl prom_boot_page +prom_boot_page: +call_method: + .asciz "call-method" + .align 8 +map: + .asciz "map" + .align 8 + + /* When we are invoked, our caller has remapped us to + * page zero, therefore we must use PC relative addressing + * for everything after we begin performing the unmap/map + * calls. + */ + .globl prom_remap +prom_remap: /* %o0 = physpage, %o1 = virtpage, %o2 = mmu_ihandle */ + rd %pc, %g1 + srl %o2, 0, %o2 ! kill sign extension + sethi %hi(p1275buf), %g2 + or %g2, %lo(p1275buf), %g2 + ldx [%g2 + 0x10], %g3 ! prom_cif_stack + save %g3, -(192 + 128), %sp + ldx [%g2 + 0x08], %l0 ! prom_cif_handler + mov %g6, %i3 + mov %g4, %i4 + flushw + + sethi %hi(prom_remap - call_method), %g7 + or %g7, %lo(prom_remap - call_method), %g7 + sub %g1, %g7, %l2 ! call-method string + sethi %hi(prom_remap - map), %g7 + or %g7, %lo(prom_remap - map), %g7 + sub %g1, %g7, %l4 ! map string + + /* OK, map the 4MB region we really live at. */ + stx %l2, [%sp + 2047 + 128 + 0x00] ! call-method + mov 7, %l5 + stx %l5, [%sp + 2047 + 128 + 0x08] ! num_args + mov 1, %l5 + stx %l5, [%sp + 2047 + 128 + 0x10] ! num_rets + stx %l4, [%sp + 2047 + 128 + 0x18] ! map + stx %i2, [%sp + 2047 + 128 + 0x20] ! mmu_ihandle + mov -1, %l5 + stx %l5, [%sp + 2047 + 128 + 0x28] ! mode == default + sethi %hi(4 * 1024 * 1024), %l5 + stx %l5, [%sp + 2047 + 128 + 0x30] ! size + stx %i1, [%sp + 2047 + 128 + 0x38] ! vaddr + stx %g0, [%sp + 2047 + 128 + 0x40] ! filler + stx %i0, [%sp + 2047 + 128 + 0x48] ! paddr + call %l0 + add %sp, (2047 + 128), %o0 ! argument array + + /* Restore hard-coded globals. */ + mov %i3, %g6 + mov %i4, %g4 + + /* Wheee.... we are done. */ + ret + restore + + .align 8192 diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/prom/misc.c linux/arch/sparc64/prom/misc.c --- v2.2.13/linux/arch/sparc64/prom/misc.c Tue Jan 4 11:10:33 2000 +++ linux/arch/sparc64/prom/misc.c Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: misc.c,v 1.14.2.1 1999/08/19 01:11:18 davem Exp $ +/* $Id: misc.c,v 1.14.2.3 1999/10/27 00:22:26 davem Exp $ * misc.c: Miscellaneous prom functions that don't belong * anywhere else. * @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include @@ -37,6 +39,11 @@ extern int serial_console; #endif +#ifdef __SMP__ +extern void smp_capture(void); +extern void smp_release(void); +#endif + /* Drop into the prom, with the chance to continue with the 'go' * prom command. */ @@ -44,26 +51,57 @@ prom_cmdline(void) { unsigned long flags; - + + __save_and_cli(flags); + #ifdef CONFIG_SUN_CONSOLE if(!serial_console && prom_palette) prom_palette (1); #endif - __save_and_cli(flags); + + /* We always arrive here via a serial interrupt. + * So in order for everything to work reliably, even + * on SMP, we need to drop the IRQ locks we hold. + */ +#ifdef __SMP__ + hardirq_exit(smp_processor_id()); + smp_capture(); +#else + local_irq_count--; +#endif + p1275_cmd ("enter", P1275_INOUT(0,0)); - __restore_flags(flags); + +#ifdef __SMP__ + smp_release(); + hardirq_enter(smp_processor_id()); + spin_unlock_wait(&global_irq_lock); +#else + local_irq_count++; +#endif + #ifdef CONFIG_SUN_CONSOLE if(!serial_console && prom_palette) prom_palette (0); #endif + + __restore_flags(flags); } +#ifdef __SMP__ +extern void smp_promstop_others(void); +#endif + /* Drop into the prom, but completely terminate the program. * No chance of continuing. */ void prom_halt(void) { +#ifdef __SMP__ + smp_promstop_others(); + udelay(8000); +#endif again: p1275_cmd ("exit", P1275_INOUT(0,0)); goto again; /* PROM is out to get me -DaveM */ @@ -122,10 +160,10 @@ p1275_cmd("SUNW,set-trap-table", P1275_INOUT(1, 0), tba); } -/* This is only used internally below. */ -static int prom_get_mmu_ihandle(void) +int mmu_ihandle_cache = 0; + +int prom_get_mmu_ihandle(void) { - static int mmu_ihandle_cache = 0; int node, ret; if (mmu_ihandle_cache != 0) @@ -165,7 +203,10 @@ unsigned long vaddr) { return p1275_cmd("call-method", - (P1275_ARG(0, P1275_ARG_IN_BUF) | P1275_INOUT(5, 1)), + (P1275_ARG(0, P1275_ARG_IN_STRING) | + P1275_ARG(2, P1275_ARG_IN_64B) | + P1275_ARG(3, P1275_ARG_IN_64B) | + P1275_INOUT(5, 1)), "SUNW,itlb-load", prom_get_mmu_ihandle(), /* And then our actual args are pushed backwards. */ @@ -179,7 +220,10 @@ unsigned long vaddr) { return p1275_cmd("call-method", - (P1275_ARG(0, P1275_ARG_IN_BUF) | P1275_INOUT(5, 1)), + (P1275_ARG(0, P1275_ARG_IN_STRING) | + P1275_ARG(2, P1275_ARG_IN_64B) | + P1275_ARG(3, P1275_ARG_IN_64B) | + P1275_INOUT(5, 1)), "SUNW,dtlb-load", prom_get_mmu_ihandle(), /* And then our actual args are pushed backwards. */ @@ -188,6 +232,41 @@ index); } +int prom_map(int mode, unsigned long size, + unsigned long vaddr, unsigned long paddr) +{ + int ret = p1275_cmd("call-method", + (P1275_ARG(0, P1275_ARG_IN_STRING) | + P1275_ARG(3, P1275_ARG_IN_64B) | + P1275_ARG(4, P1275_ARG_IN_64B) | + P1275_ARG(6, P1275_ARG_IN_64B) | + P1275_INOUT(7, 1)), + "map", + prom_get_mmu_ihandle(), + mode, + size, + vaddr, + 0, + paddr); + + if (ret == 0) + ret = -1; + return ret; +} + +void prom_unmap(unsigned long size, unsigned long vaddr) +{ + p1275_cmd("call-method", + (P1275_ARG(0, P1275_ARG_IN_STRING) | + P1275_ARG(2, P1275_ARG_IN_64B) | + P1275_ARG(3, P1275_ARG_IN_64B) | + P1275_INOUT(4, 0)), + "unmap", + prom_get_mmu_ihandle(), + size, + vaddr); +} + /* Set aside physical memory which is not touched or modified * across soft resets. */ @@ -226,7 +305,7 @@ return p1275_cmd("call-method", (P1275_ARG(0, P1275_ARG_IN_STRING) | P1275_ARG(3, P1275_ARG_OUT_BUF) | - P1275_ARG(5, P1275_ARG_IN_64B) | + P1275_ARG(6, P1275_ARG_IN_64B) | P1275_INOUT(8, 2)), "SUNW,get-unumber", prom_get_memory_ihandle(), buflen, buf, P1275_SIZE(buflen), diff -u --recursive --new-file v2.2.13/linux/arch/sparc64/prom/p1275.c linux/arch/sparc64/prom/p1275.c --- v2.2.13/linux/arch/sparc64/prom/p1275.c Tue Jan 4 11:10:33 2000 +++ linux/arch/sparc64/prom/p1275.c Tue Jan 4 10:12:13 2000 @@ -1,4 +1,4 @@ -/* $Id: p1275.c,v 1.15.2.1 1999/08/19 01:11:19 davem Exp $ +/* $Id: p1275.c,v 1.15.2.3 1999/10/27 00:22:27 davem Exp $ * p1275.c: Sun IEEE 1275 PROM low level interface routines * * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -253,9 +253,7 @@ */ static int prom_entry_depth = 0; #ifdef __SMP__ -static spinlock_t prom_entry_lock = SPIN_LOCK_UNLOCKED; -extern void smp_capture(void); -extern void smp_release(void); +spinlock_t prom_entry_lock = SPIN_LOCK_UNLOCKED; #endif static __inline__ unsigned long prom_get_lock(void) @@ -270,9 +268,6 @@ if (prom_entry_depth != 0) panic("prom_get_lock"); #endif -#ifdef __SMP__ - smp_capture(); -#endif } prom_entry_depth++; @@ -281,12 +276,9 @@ static __inline__ void prom_release_lock(unsigned long flags) { - if (--prom_entry_depth == 0) { -#ifdef __SMP__ - smp_release(); -#endif + if (--prom_entry_depth == 0) spin_unlock(&prom_entry_lock); - } + __restore_flags(flags); } diff -u --recursive --new-file v2.2.13/linux/drivers/Makefile linux/drivers/Makefile --- v2.2.13/linux/drivers/Makefile Mon Aug 9 16:05:55 1999 +++ linux/drivers/Makefile Tue Jan 4 10:12:13 2000 @@ -10,7 +10,7 @@ SUB_DIRS := block char net misc sound MOD_SUB_DIRS := $(SUB_DIRS) ALL_SUB_DIRS := $(SUB_DIRS) pci sgi scsi sbus cdrom isdn pnp \ - macintosh video dio zorro fc4 usb + macintosh video dio zorro fc4 usb telephony ifdef CONFIG_DIO SUB_DIRS += dio @@ -109,6 +109,16 @@ MOD_SUB_DIRS += fc4 endif endif + +ifeq ($(CONFIG_PHONE),y) +SUB_DIRS += telephony +MOD_SUB_DIRS += telephony +else + ifeq ($(CONFIG_PHONE),m) + MOD_SUB_DIRS += telephony + endif +endif + # When MOD_LIST_NAME is set, make will try to add $(MOD_SUB_DIRS).o to # modules/MOD_LIST_NAME. We don't have hamradio.o and Linus diff -u --recursive --new-file v2.2.13/linux/drivers/block/Config.in linux/drivers/block/Config.in --- v2.2.13/linux/drivers/block/Config.in Mon Aug 9 16:05:55 1999 +++ linux/drivers/block/Config.in Tue Jan 4 10:12:14 2000 @@ -131,7 +131,9 @@ if [ "$CONFIG_PARIDE" = "y" -o "$CONFIG_PARIDE" = "m" ]; then source drivers/block/paride/Config.in fi -tristate 'Compaq SMART2 support' CONFIG_BLK_CPQ_DA +if [ "$CONFIG_PCI" = "y" ]; then + tristate 'Compaq SMART2 support' CONFIG_BLK_CPQ_DA +fi if [ "$CONFIG_BLK_DEV_HD_IDE" = "y" -o "$CONFIG_BLK_DEV_HD_ONLY" = "y" ]; then define_bool CONFIG_BLK_DEV_HD y diff -u --recursive --new-file v2.2.13/linux/drivers/block/genhd.c linux/drivers/block/genhd.c --- v2.2.13/linux/drivers/block/genhd.c Tue Jan 4 11:10:33 2000 +++ linux/drivers/block/genhd.c Tue Jan 4 10:12:14 2000 @@ -784,8 +784,11 @@ label = (struct sun_disklabel *) bh->b_data; p = label->partitions; if (be16_to_cpu(label->magic) != SUN_LABEL_MAGIC) { +#if 0 + /* There is no error here - it is just not a sunlabel. */ printk("Dev %s Sun disklabel: bad magic %04x\n", kdevname(dev), be16_to_cpu(label->magic)); +#endif brelse(bh); return 0; } @@ -856,8 +859,11 @@ p = &label->partitions[0]; magic = label->magic_mushroom; if(be32_to_cpu(magic) != SGI_LABEL_MAGIC) { +#if 0 + /* There is no error here - it is just not an sgilabel. */ printk("Dev %s SGI disklabel: bad magic %08x\n", kdevname(dev), magic); +#endif brelse(bh); return 0; } @@ -1049,7 +1055,8 @@ int dev_bsize, dev_pos, pos; unsigned secsize; #ifdef CONFIG_PPC - int first_bootable = 1; + int found_root = 0; + int found_root_goodness = 0; #endif struct mac_partition *part; struct mac_driver_desc *md; @@ -1107,16 +1114,36 @@ * If this is the first bootable partition, tell the * setup code, in case it wants to make this the root. */ - if ( (_machine == _MACH_Pmac) && first_bootable - && (be32_to_cpu(part->status) & MAC_STATUS_BOOTABLE) - && strcasecmp(part->processor, "powerpc") == 0) { - note_bootable_part(dev, blk); - first_bootable = 0; + if (_machine == _MACH_Pmac) { + int goodness = 0; + + if ((be32_to_cpu(part->status) & MAC_STATUS_BOOTABLE) + && strcasecmp(part->processor, "powerpc") == 0) + goodness++; + + if (strcasecmp(part->type, "Apple_UNIX_SVR2") == 0) { + goodness++; + if ((strcmp(part->name, "/") == 0) || + (strstr(part->name, "root") != 0)) { + goodness++; + } + if (strncmp(part->name, "swap", 4) == 0) + goodness--; + } + + if (goodness > found_root_goodness) { + found_root = blk; + found_root_goodness = goodness; + } } #endif /* CONFIG_PPC */ ++current_minor; } +#ifdef CONFIG_PPC + if (found_root_goodness) + note_bootable_part(dev, found_root); +#endif brelse(bh); printk("\n"); return 1; @@ -1450,7 +1477,7 @@ #ifdef CONFIG_BLK_CPQ_DA cpqarray_init(); #endif -#ifdef CONFIG_INET +#ifdef CONFIG_NET net_dev_init(); #endif #ifdef CONFIG_VT diff -u --recursive --new-file v2.2.13/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c --- v2.2.13/linux/drivers/block/ide-cd.c Tue Jan 4 11:10:33 2000 +++ linux/drivers/block/ide-cd.c Tue Jan 4 10:12:14 2000 @@ -139,7 +139,7 @@ * 3.15 July 2, 1996 -- Added support for Sanyo 3 CD changers * from Ben Galliart with * special help from Jeff Lightfoot - * + * * 3.15a July 9, 1996 -- Improved Sanyo 3 CD changer identification * 3.16 Jul 28, 1996 -- Fix from Gadi to reduce kernel stack usage for ioctl. * 3.17 Sep 17, 1996 -- Tweak audio reads for some drives. @@ -519,7 +519,7 @@ /* Returns 0 if the request should be continued. Returns 1 if the request was ended. */ -static int cdrom_decode_status (ide_drive_t *drive, int good_stat, +static int cdrom_decode_status (ide_startstop_t *startstop, ide_drive_t *drive, int good_stat, int *stat_ret) { struct request *rq = HWGROUP(drive)->rq; @@ -551,7 +551,7 @@ rq->buffer; pc->stat = 1; cdrom_end_request (1, drive); - ide_error (drive, "request sense failure", stat); + *startstop = ide_error (drive, "request sense failure", stat); return 1; } else if (cmd == PACKET_COMMAND) { @@ -633,7 +633,7 @@ } else if ((err & ~ABRT_ERR) != 0) { /* Go to the default handler for other errors. */ - ide_error (drive, "cdrom_decode_status", stat); + *startstop = ide_error (drive, "cdrom_decode_status", stat); return 1; } else if ((++rq->errors > ERROR_MAX)) { /* We've racked up too many retries. Abort. */ @@ -649,6 +649,7 @@ } /* Retry, or handle the next request. */ + *startstop = ide_stopped; return 1; } @@ -660,13 +661,15 @@ called when the interrupt from the drive arrives. Otherwise, HANDLER will be called immediately after the drive is prepared for the transfer. */ -static int cdrom_start_packet_command (ide_drive_t *drive, int xferlen, +static ide_startstop_t cdrom_start_packet_command (ide_drive_t *drive, int xferlen, ide_handler_t *handler) { + ide_startstop_t startstop; struct cdrom_info *info = drive->driver_data; /* Wait for the controller to be idle. */ - if (ide_wait_stat (drive, 0, BUSY_STAT, WAIT_READY)) return 1; + if (ide_wait_stat (&startstop, drive, 0, BUSY_STAT, WAIT_READY)) + return startstop; if (info->dma) info->dma = !HWIF(drive)->dmaproc(ide_dma_read, drive); @@ -686,12 +689,11 @@ if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) { ide_set_handler (drive, handler, WAIT_CMD); OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */ + return ide_started; } else { OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */ - (*handler) (drive); + return (*handler) (drive); } - - return 0; } @@ -700,7 +702,7 @@ by cdrom_start_packet_command. HANDLER is the interrupt handler to call when the command completes or there's data ready. */ -static int cdrom_transfer_packet_command (ide_drive_t *drive, +static ide_startstop_t cdrom_transfer_packet_command (ide_drive_t *drive, char *cmd_buf, int cmd_len, ide_handler_t *handler) { @@ -708,14 +710,16 @@ /* Here we should have been called after receiving an interrupt from the device. DRQ should how be set. */ int stat_dum; + ide_startstop_t startstop; /* Check for errors. */ - if (cdrom_decode_status (drive, DRQ_STAT, &stat_dum)) - return 1; + if (cdrom_decode_status (&startstop, drive, DRQ_STAT, &stat_dum)) + return startstop; } else { + ide_startstop_t startstop; /* Otherwise, we must wait for DRQ to get set. */ - if (ide_wait_stat (drive, DRQ_STAT, BUSY_STAT, WAIT_READY)) - return 1; + if (ide_wait_stat (&startstop, drive, DRQ_STAT, BUSY_STAT, WAIT_READY)) + return startstop; } /* Arm the interrupt handler. */ @@ -724,7 +728,7 @@ /* Send the command to the device. */ atapi_output_bytes (drive, cmd_buf, cmd_len); - return 0; + return ide_started; } @@ -826,12 +830,13 @@ /* * Interrupt routine. Called when a read request has completed. */ -static void cdrom_read_intr (ide_drive_t *drive) +static ide_startstop_t cdrom_read_intr (ide_drive_t *drive) { int stat; int ireason, len, sectors_to_transfer, nskip; struct cdrom_info *info = drive->driver_data; int i, dma = info->dma, dma_error = 0; + ide_startstop_t startstop; struct request *rq = HWGROUP(drive)->rq; @@ -842,8 +847,8 @@ HWIF(drive)->dmaproc(ide_dma_off, drive); } - if (cdrom_decode_status (drive, 0, &stat)) - return; + if (cdrom_decode_status (&startstop, drive, 0, &stat)) + return startstop; if (dma) { if (!dma_error) { @@ -851,9 +856,9 @@ i -= rq->current_nr_sectors; ide_end_request(1, HWGROUP(drive)); } + return ide_stopped; } else - ide_error (drive, "dma error", stat); - return; + return ide_error (drive, "dma error", stat); } /* Read the interrupt reason and the transfer length. */ @@ -870,11 +875,12 @@ cdrom_end_request (0, drive); } else cdrom_end_request (1, drive); - return; + return ide_stopped; } /* Check that the drive is expecting to do the same thing we are. */ - if (cdrom_read_check_ireason (drive, len, ireason)) return; + if (cdrom_read_check_ireason (drive, len, ireason)) + return ide_stopped; /* Assume that the drive will always provide data in multiples of at least SECTOR_SIZE, as it gets hairy to keep track @@ -889,7 +895,7 @@ CDROM_CONFIG_FLAGS (drive)->limit_nframes = 1; } cdrom_end_request (0, drive); - return; + return ide_stopped; } /* The number of sectors we need to read from the drive. */ @@ -952,6 +958,7 @@ /* Done moving data! Wait for another interrupt. */ ide_set_handler (drive, &cdrom_read_intr, WAIT_CMD); + return ide_started; } @@ -1019,7 +1026,7 @@ * However, for drq_interrupt devices, it is called from an interrupt * when the drive is ready to accept the command. */ -static void cdrom_start_read_continuation (ide_drive_t *drive) +static ide_startstop_t cdrom_start_read_continuation (ide_drive_t *drive) { struct packet_command pc; struct request *rq = HWGROUP(drive)->rq; @@ -1046,7 +1053,7 @@ printk ("%s: cdrom_start_read_continuation: buffer botch (%ld)\n", drive->name, rq->current_nr_sectors); cdrom_end_request (0, drive); - return; + return ide_stopped; } sector -= nskip; @@ -1072,22 +1079,22 @@ put_unaligned(htonl (frame), (unsigned int *) &pc.c[2]); /* Send the command to the drive and return. */ - (void) cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c), - &cdrom_read_intr); + return cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c), &cdrom_read_intr); } #define IDECD_SEEK_THRESHOLD (1000) /* 1000 blocks */ #define IDECD_SEEK_TIMER (2 * WAIT_MIN_SLEEP) /* 40 ms */ #define IDECD_SEEK_TIMEOUT WAIT_CMD /* 10 sec */ -static void cdrom_seek_intr (ide_drive_t *drive) +static ide_startstop_t cdrom_seek_intr (ide_drive_t *drive) { struct cdrom_info *info = drive->driver_data; int stat; static int retry = 10; + ide_startstop_t startstop; - if (cdrom_decode_status (drive, 0, &stat)) - return; + if (cdrom_decode_status (&startstop, drive, 0, &stat)) + return startstop; CDROM_CONFIG_FLAGS(drive)->seeking = 1; if (retry && jiffies - info->start_seek > IDECD_SEEK_TIMER) { @@ -1096,9 +1103,10 @@ drive->dsc_overlap = 0; } } + return ide_stopped; } -static void cdrom_start_seek_continuation (ide_drive_t *drive) +static ide_startstop_t cdrom_start_seek_continuation (ide_drive_t *drive) { struct packet_command pc; struct request *rq = HWGROUP(drive)->rq; @@ -1113,22 +1121,22 @@ memset (&pc.c, 0, sizeof (pc.c)); pc.c[0] = SEEK; put_unaligned(htonl (frame), (unsigned int *) &pc.c[2]); - (void) cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c), &cdrom_seek_intr); + return cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c), &cdrom_seek_intr); } -static void cdrom_start_seek (ide_drive_t *drive, unsigned int block) +static ide_startstop_t cdrom_start_seek (ide_drive_t *drive, unsigned int block) { struct cdrom_info *info = drive->driver_data; info->dma = 0; info->start_seek = jiffies; - cdrom_start_packet_command (drive, 0, cdrom_start_seek_continuation); + return cdrom_start_packet_command (drive, 0, cdrom_start_seek_continuation); } /* * Start a read request from the CD-ROM. */ -static void cdrom_start_read (ide_drive_t *drive, unsigned int block) +static ide_startstop_t cdrom_start_read (ide_drive_t *drive, unsigned int block) { struct cdrom_info *info = drive->driver_data; struct request *rq = HWGROUP(drive)->rq; @@ -1148,7 +1156,7 @@ /* Satisfy whatever we can of this request from our cached sector. */ if (cdrom_read_from_buffer (drive)) - return; + return ide_stopped; /* Clear the local sector buffer. */ info->nsectors_buffered = 0; @@ -1159,8 +1167,7 @@ info->dma = 0; /* Start sending the read request to the drive. */ - cdrom_start_packet_command (drive, 32768, - cdrom_start_read_continuation); + return cdrom_start_packet_command (drive, 32768, cdrom_start_read_continuation); } @@ -1178,15 +1185,16 @@ /* Interrupt routine for packet command completion. */ -static void cdrom_pc_intr (ide_drive_t *drive) +static ide_startstop_t cdrom_pc_intr (ide_drive_t *drive) { int ireason, len, stat, thislen; struct request *rq = HWGROUP(drive)->rq; struct packet_command *pc = (struct packet_command *)rq->buffer; + ide_startstop_t startstop; /* Check for errors. */ - if (cdrom_decode_status (drive, 0, &stat)) - return; + if (cdrom_decode_status (&startstop, drive, 0, &stat)) + return startstop; /* Read the interrupt reason and the transfer length. */ ireason = IN_BYTE (IDE_NSECTOR_REG); @@ -1219,7 +1227,7 @@ pc->stat = 1; cdrom_end_request (1, drive); } - return; + return ide_stopped; } /* Figure out how much data to transfer. */ @@ -1288,21 +1296,21 @@ /* Now we wait for another interrupt. */ ide_set_handler (drive, &cdrom_pc_intr, WAIT_CMD); + return ide_started; } -static void cdrom_do_pc_continuation (ide_drive_t *drive) +static ide_startstop_t cdrom_do_pc_continuation (ide_drive_t *drive) { struct request *rq = HWGROUP(drive)->rq; struct packet_command *pc = (struct packet_command *)rq->buffer; /* Send the command to the drive and return. */ - cdrom_transfer_packet_command (drive, pc->c, - sizeof (pc->c), &cdrom_pc_intr); + return cdrom_transfer_packet_command (drive, pc->c, sizeof (pc->c), &cdrom_pc_intr); } -static void cdrom_do_packet_command (ide_drive_t *drive) +static ide_startstop_t cdrom_do_packet_command (ide_drive_t *drive) { int len; struct request *rq = HWGROUP(drive)->rq; @@ -1317,7 +1325,7 @@ pc->stat = 0; /* Start sending the command to the drive. */ - cdrom_start_packet_command (drive, len, cdrom_do_pc_continuation); + return cdrom_start_packet_command (drive, len, cdrom_do_pc_continuation); } @@ -1401,19 +1409,20 @@ /**************************************************************************** * cdrom driver request routine. */ -static -void ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, unsigned long block) +static ide_startstop_t +ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, unsigned long block) { if (rq -> cmd == PACKET_COMMAND || rq -> cmd == REQUEST_SENSE_COMMAND) - cdrom_do_packet_command (drive); + return cdrom_do_packet_command (drive); else if (rq -> cmd == RESET_DRIVE_COMMAND) { cdrom_end_request (1, drive); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } else if (rq -> cmd != READ) { printk ("ide-cd: bad cmd %d\n", rq -> cmd); cdrom_end_request (0, drive); + return ide_stopped; } else { + ide_startstop_t action; struct cdrom_info *info = drive->driver_data; if (CDROM_CONFIG_FLAGS(drive)->seeking) { @@ -1423,17 +1432,18 @@ if ((stat & SEEK_STAT) != SEEK_STAT) { if (elpased < IDECD_SEEK_TIMEOUT) { ide_stall_queue (drive, IDECD_SEEK_TIMER); - return; + return ide_stopped; } printk ("%s: DSC timeout\n", drive->name); } CDROM_CONFIG_FLAGS(drive)->seeking = 0; } if (IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap) - cdrom_start_seek (drive, block); + action = cdrom_start_seek (drive, block); else - cdrom_start_read (drive, block); + action = cdrom_start_read (drive, block); info->last_block = block; + return action; } } @@ -2890,8 +2900,11 @@ struct atapi_capabilities_page cap; } buf; - if (CDROM_CONFIG_FLAGS (drive)->nec260) + if (CDROM_CONFIG_FLAGS (drive)->nec260) { + CDROM_CONFIG_FLAGS (drive)->no_eject = 0; + CDROM_CONFIG_FLAGS (drive)->audio_play = 1; return nslots; + } do { /* we seem to get stat=0x01,err=0x00 the first time (??) */ if (attempts-- <= 0) diff -u --recursive --new-file v2.2.13/linux/drivers/block/ide-disk.c linux/drivers/block/ide-disk.c --- v2.2.13/linux/drivers/block/ide-disk.c Wed Mar 10 17:49:43 1999 +++ linux/drivers/block/ide-disk.c Tue Jan 4 10:12:14 2000 @@ -88,46 +88,60 @@ */ static int lba_capacity_is_ok (struct hd_driveid *id) { - unsigned long lba_sects = id->lba_capacity; - unsigned long chs_sects = id->cyls * id->heads * id->sectors; - unsigned long _10_percent = chs_sects / 10; + unsigned long lba_sects, chs_sects, head, tail; /* - * very large drives (8GB+) may lie about the number of cylinders - * This is a split test for drives 8 Gig and Bigger only. + * The ATA spec tells large drives to return + * C/H/S = 16383/16/63 independent of their size. + * Some drives can be jumpered to use 15 heads instead of 16. */ - if ((id->lba_capacity >= 16514064) && (id->cyls == 0x3fff) && - (id->heads == 16) && (id->sectors == 63)) { - id->cyls = lba_sects / (16 * 63); /* correct cyls */ - return 1; /* lba_capacity is our only option */ - } + if (id->cyls == 16383 && id->sectors == 63 && + (id->heads == 15 || id->heads == 16) && + id->lba_capacity >= 16383*63*id->heads) + return 1; + + lba_sects = id->lba_capacity; + chs_sects = id->cyls * id->heads * id->sectors; + /* perform a rough sanity check on lba_sects: within 10% is "okay" */ - if ((lba_sects - chs_sects) < _10_percent) { - return 1; /* lba_capacity is good */ - } + if ((lba_sects - chs_sects) < chs_sects/10) + return 1; + /* some drives have the word order reversed */ - lba_sects = (lba_sects << 16) | (lba_sects >> 16); - if ((lba_sects - chs_sects) < _10_percent) { - id->lba_capacity = lba_sects; /* fix it */ + head = ((lba_sects >> 16) & 0xffff); + tail = (lba_sects & 0xffff); + lba_sects = (head | (tail << 16)); + if ((lba_sects - chs_sects) < chs_sects/10) { + id->lba_capacity = lba_sects; return 1; /* lba_capacity is (now) good */ } - return 0; /* lba_capacity value is bad */ + + return 0; /* lba_capacity value may be bad */ } /* * read_intr() is the handler for disk read/multread interrupts */ -static void read_intr (ide_drive_t *drive) +static ide_startstop_t read_intr (ide_drive_t *drive) { byte stat; int i; unsigned int msect, nsect; struct request *rq; - +#if 0 if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) { - ide_error(drive, "read_intr", stat); - return; + return ide_error(drive, "read_intr", stat); + } +#else /* new way for dealing with premature shared PCI interrupts */ + if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) { + if (stat & (ERR_STAT|DRQ_STAT)) { + return ide_error(drive, "read_intr", stat); + } + /* no data yet, so wait for another interrupt */ + ide_set_handler(drive, &read_intr, WAIT_CMD); + return ide_started; } +#endif msect = drive->mult_count; read_next: @@ -138,12 +152,6 @@ msect -= nsect; } else nsect = 1; - /* - * PIO input can take longish times, so we drop the spinlock. - * On SMP, bad things might happen if syscall level code adds - * a new request while we do this PIO, so we just freeze all - * request queue handling while doing the PIO. FIXME - */ idedisk_input_data(drive, rq->buffer, nsect * SECTOR_WORDS); #ifdef DEBUG printk("%s: read: sectors(%ld-%ld), buffer=0x%08lx, remaining=%ld\n", @@ -154,27 +162,30 @@ rq->buffer += nsect<<9; rq->errors = 0; i = (rq->nr_sectors -= nsect); - if ((rq->current_nr_sectors -= nsect) <= 0) + if (((long)(rq->current_nr_sectors -= nsect)) <= 0) ide_end_request(1, HWGROUP(drive)); if (i > 0) { if (msect) goto read_next; ide_set_handler (drive, &read_intr, WAIT_CMD); + return ide_started; } + return ide_stopped; } /* * write_intr() is the handler for disk write interrupts */ -static void write_intr (ide_drive_t *drive) +static ide_startstop_t write_intr (ide_drive_t *drive) { byte stat; int i; ide_hwgroup_t *hwgroup = HWGROUP(drive); struct request *rq = hwgroup->rq; - int error = 0; - if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) { + if (!OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) { + printk("%s: write_intr error1: nr_sectors=%ld, stat=0x%02x\n", drive->name, rq->nr_sectors, stat); + } else { #ifdef DEBUG printk("%s: write: sector %ld, buffer=0x%08lx, remaining=%ld\n", drive->name, rq->sector, (unsigned long) rq->buffer, @@ -186,30 +197,44 @@ rq->errors = 0; i = --rq->nr_sectors; --rq->current_nr_sectors; - if (rq->current_nr_sectors <= 0) + if (((long)rq->current_nr_sectors) <= 0) ide_end_request(1, hwgroup); if (i > 0) { idedisk_output_data (drive, rq->buffer, SECTOR_WORDS); ide_set_handler (drive, &write_intr, WAIT_CMD); + return ide_started; } - goto out; + return ide_stopped; } - } else - error = 1; -out: - if (error) - ide_error(drive, "write_intr", stat); + return ide_stopped; /* the original code did this here (?) */ + } + return ide_error(drive, "write_intr", stat); } /* * ide_multwrite() transfers a block of up to mcount sectors of data * to a drive as part of a disk multiple-sector write operation. + * + * Returns 0 if successful; returns 1 if request had to be aborted due to corrupted buffer list. */ -void ide_multwrite (ide_drive_t *drive, unsigned int mcount) +int ide_multwrite (ide_drive_t *drive, unsigned int mcount) { - struct request *rq = &HWGROUP(drive)->wrq; + ide_hwgroup_t *hwgroup= HWGROUP(drive); + + /* + * This may look a bit odd, but remember wrq is a copy of the + * request not the original. The pointers are real however so the + * bh's are not copies. Remember that or bad stuff will happen + * + * At the point we are called the drive has asked us for the + * data, and its our job to feed it, walking across bh boundaries + * if need be. + */ + + struct request *rq = &hwgroup->wrq; do { + unsigned long flags; unsigned int nsect = rq->current_nr_sectors; if (nsect > mcount) nsect = mcount; @@ -221,61 +246,95 @@ drive->name, rq->sector, (unsigned long) rq->buffer, nsect, rq->nr_sectors - nsect); #endif - if ((rq->nr_sectors -= nsect) <= 0) + spin_lock_irqsave(&io_request_lock, flags); /* Is this really necessary? */ + + /* + * Completed ? + */ + + if (((long)(rq->nr_sectors -= nsect)) <= 0) + { + spin_unlock_irqrestore(&io_request_lock, flags); break; + } + + /* + * Take off the sectors transferred + */ + if ((rq->current_nr_sectors -= nsect) == 0) { + /* + * If there are no sectors left then move on to + * the next buffer. + */ if ((rq->bh = rq->bh->b_reqnext) != NULL) { rq->current_nr_sectors = rq->bh->b_size>>9; rq->buffer = rq->bh->b_data; } else { - panic("%s: buffer list corrupted\n", drive->name); - break; + /* + * Excuse me boss.. nr_sectors doesnt tally + * deep crap + */ + spin_unlock_irqrestore(&io_request_lock, flags); + printk("%s: buffer list corrupted (%ld, %ld, %d)\n", drive->name, + rq->current_nr_sectors, rq->nr_sectors, nsect); + ide_end_request(0, hwgroup); + return 1; } } else { + /* Fix the pointer.. we ate data */ rq->buffer += nsect << 9; } + spin_unlock_irqrestore(&io_request_lock, flags); } while (mcount); + return 0; } /* * multwrite_intr() is the handler for disk multwrite interrupts */ -static void multwrite_intr (ide_drive_t *drive) +static ide_startstop_t multwrite_intr (ide_drive_t *drive) { byte stat; int i; ide_hwgroup_t *hwgroup = HWGROUP(drive); struct request *rq = &hwgroup->wrq; - int error = 0; if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) { if (stat & DRQ_STAT) { + /* + * The drive wants data. Remember rq is the copy + * of the request + */ if (rq->nr_sectors) { - ide_multwrite(drive, drive->mult_count); + if (ide_multwrite(drive, drive->mult_count)) + return ide_stopped; ide_set_handler (drive, &multwrite_intr, WAIT_CMD); - goto out; + return ide_started; } } else { + /* + * If the copy has all the blocks completed then + * we can end the original request. + */ if (!rq->nr_sectors) { /* all done? */ rq = hwgroup->rq; for (i = rq->nr_sectors; i > 0;){ i -= rq->current_nr_sectors; ide_end_request(1, hwgroup); } - goto out; + return ide_stopped; } } - } else - error = 1; -out: - if (error) - ide_error(drive, "multwrite_intr", stat); + return ide_stopped; /* the original code did this here (?) */ + } + return ide_error(drive, "multwrite_intr", stat); } /* * set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd. */ -static void set_multmode_intr (ide_drive_t *drive) +static ide_startstop_t set_multmode_intr (ide_drive_t *drive) { byte stat = GET_STAT(); @@ -286,28 +345,31 @@ drive->special.b.recalibrate = 1; (void) ide_dump_status(drive, "set_multmode", stat); } + return ide_stopped; } /* * set_geometry_intr() is invoked on completion of a WIN_SPECIFY cmd. */ -static void set_geometry_intr (ide_drive_t *drive) +static ide_startstop_t set_geometry_intr (ide_drive_t *drive) { byte stat = GET_STAT(); if (!OK_STAT(stat,READY_STAT,BAD_STAT)) - ide_error(drive, "set_geometry_intr", stat); + return ide_error(drive, "set_geometry_intr", stat); + return ide_stopped; } /* * recal_intr() is invoked on completion of a WIN_RESTORE (recalibrate) cmd. */ -static void recal_intr (ide_drive_t *drive) +static ide_startstop_t recal_intr (ide_drive_t *drive) { byte stat = GET_STAT(); if (!OK_STAT(stat,READY_STAT,BAD_STAT)) - ide_error(drive, "recal_intr", stat); + return ide_error(drive, "recal_intr", stat); + return ide_stopped; } /* @@ -315,7 +377,7 @@ * using LBA if supported, or CHS otherwise, to address sectors. * It also takes care of issuing special DRIVE_CMDs. */ -static void do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block) +static ide_startstop_t do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block) { #ifdef CONFIG_BLK_DEV_PDC4030 ide_hwif_t *hwif = HWIF(drive); @@ -361,45 +423,63 @@ } #ifdef CONFIG_BLK_DEV_PDC4030 if (use_pdc4030_io) { - extern void do_pdc4030_io(ide_drive_t *, struct request *); - do_pdc4030_io (drive, rq); - return; + extern ide_startstop_t do_pdc4030_io(ide_drive_t *, struct request *); + return do_pdc4030_io (drive, rq); } #endif /* CONFIG_BLK_DEV_PDC4030 */ if (rq->cmd == READ) { #ifdef CONFIG_BLK_DEV_IDEDMA if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_read, drive))) - return; + return ide_started; #endif /* CONFIG_BLK_DEV_IDEDMA */ ide_set_handler(drive, &read_intr, WAIT_CMD); OUT_BYTE(drive->mult_count ? WIN_MULTREAD : WIN_READ, IDE_COMMAND_REG); - return; + return ide_started; } if (rq->cmd == WRITE) { + ide_startstop_t startstop; #ifdef CONFIG_BLK_DEV_IDEDMA if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_write, drive))) - return; + return ide_started; #endif /* CONFIG_BLK_DEV_IDEDMA */ OUT_BYTE(drive->mult_count ? WIN_MULTWRITE : WIN_WRITE, IDE_COMMAND_REG); - if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { + if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { printk(KERN_ERR "%s: no DRQ after issuing %s\n", drive->name, drive->mult_count ? "MULTWRITE" : "WRITE"); - return; + return startstop; } if (!drive->unmask) __cli(); /* local CPU only */ if (drive->mult_count) { - HWGROUP(drive)->wrq = *rq; /* scratchpad */ + ide_hwgroup_t *hwgroup = HWGROUP(drive); + /* + * Ugh.. this part looks ugly because we MUST set up + * the interrupt handler before outputting the first block + * of data to be written. If we hit an error (corrupted buffer list) + * in ide_multwrite(), then we need to remove the handler/timer + * before returning. Fortunately, this NEVER happens (right?). + * + * Except when you get an error it seems... + */ + hwgroup->wrq = *rq; /* scratchpad */ ide_set_handler (drive, &multwrite_intr, WAIT_CMD); - ide_multwrite(drive, drive->mult_count); + if (ide_multwrite(drive, drive->mult_count)) { + unsigned long flags; + spin_lock_irqsave(&io_request_lock, flags); + hwgroup->handler = NULL; + del_timer(&hwgroup->timer); + spin_unlock_irqrestore(&io_request_lock, flags); + return ide_stopped; + } } else { ide_set_handler (drive, &write_intr, WAIT_CMD); idedisk_output_data(drive, rq->buffer, SECTOR_WORDS); } - return; + return ide_started; } printk(KERN_ERR "%s: bad command: %d\n", drive->name, rq->cmd); ide_end_request(0, HWGROUP(drive)); + return ide_stopped; } static int idedisk_open (struct inode *inode, struct file *filp, ide_drive_t *drive) @@ -446,7 +526,6 @@ /* Determine capacity, and use LBA if the drive properly supports it */ if (id != NULL && (id->capability & 2) && lba_capacity_is_ok(id)) { if (id->lba_capacity >= capacity) { - drive->cyl = id->lba_capacity / (drive->head * drive->sect); capacity = id->lba_capacity; drive->select.b.lba = 1; } @@ -454,7 +533,7 @@ return (capacity - drive->sect0); } -static void idedisk_special (ide_drive_t *drive) +static ide_startstop_t idedisk_special (ide_drive_t *drive) { special_t *s = &drive->special; @@ -480,7 +559,9 @@ int special = s->all; s->all = 0; printk(KERN_ERR "%s: bad special flag: 0x%02x\n", drive->name, special); + return ide_stopped; } + return IS_PDC4030_DRIVE ? ide_stopped : ide_started; } static void idedisk_pre_reset (ide_drive_t *drive) @@ -602,7 +683,7 @@ return -EBUSY; drive->nowerr = arg; drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT; - spin_unlock_irqrestore(&HWGROUP(drive)->spinlock, flags); + spin_unlock_irqrestore(&io_request_lock, flags); return 0; } @@ -721,9 +802,9 @@ if ((id->lba_capacity > 16514064) || (id->cyls == 0x3fff)) { id->cyls = ((int)(id->lba_capacity/(id->heads * id->sectors))); } - drive->cyl = id->cur_cyls = id->cyls; - drive->head = id->cur_heads = id->heads; - drive->sect = id->cur_sectors = id->sectors; + drive->cyl = id->cyls; + drive->head = id->heads; + drive->sect = id->sectors; } /* calculate drive capacity, and select LBA if possible */ @@ -733,21 +814,19 @@ * if possible, give fdisk access to more of the drive, * by correcting bios_cyls: */ - if ((capacity >= (drive->bios_cyl * drive->bios_sect * drive->bios_head)) && - (!drive->forced_geom) && drive->bios_sect && drive->bios_head) { - drive->bios_cyl = (capacity / drive->bios_sect) / drive->bios_head; -#ifdef DEBUG - printk("Fixing Geometry :: CHS=%d/%d/%d to CHS=%d/%d/%d\n", - drive->id->cur_cyls, - drive->id->cur_heads, - drive->id->cur_sectors, - drive->bios_cyl, - drive->bios_head, - drive->bios_sect); -#endif - drive->id->cur_cyls = drive->bios_cyl; - drive->id->cur_heads = drive->bios_head; - drive->id->cur_sectors = drive->bios_sect; + if (!drive->forced_geom && + capacity > drive->bios_cyl * drive->bios_sect * drive->bios_head) { + unsigned long cylsize; + cylsize = drive->bios_sect * drive->bios_head; + if (cylsize == 0 || capacity/cylsize > 65535) { + drive->bios_sect = 63; + drive->bios_head = 255; + cylsize = 63*255; + } + if (capacity/cylsize > 65535) + drive->bios_cyl = 65535; + else + drive->bios_cyl = capacity/cylsize; } #if 0 /* done instead for entire identify block in arch/ide.h stuff */ @@ -762,7 +841,7 @@ if (drive->using_dma) { if ((id->field_valid & 4) && (id->dma_ultra & (id->dma_ultra >> 8) & 7)) { - printk(", UDMA"); /* UDMA BIOS-enabled! */ + printk(", UDMA"); /* UDMA BIOS-enabled! */ } else if (id->field_valid & 4) { printk(", (U)DMA"); /* Can be BIOS-enabled! */ } else { @@ -770,19 +849,6 @@ } } printk("\n"); - - if (drive->select.b.lba) { - if (*(int *)&id->cur_capacity0 < id->lba_capacity) { -#ifdef DEBUG - printk(" CurSects=%d, LBASects=%d, ", - *(int *)&id->cur_capacity0, id->lba_capacity); -#endif - *(int *)&id->cur_capacity0 = id->lba_capacity; -#ifdef DEBUG - printk( "Fixed CurSects=%d\n", *(int *)&id->cur_capacity0); -#endif - } - } drive->mult_count = 0; if (id->max_multsect) { diff -u --recursive --new-file v2.2.13/linux/drivers/block/ide-dma.c linux/drivers/block/ide-dma.c --- v2.2.13/linux/drivers/block/ide-dma.c Tue May 11 14:58:46 1999 +++ linux/drivers/block/ide-dma.c Tue Jan 4 10:12:14 2000 @@ -136,7 +136,7 @@ /* * dma_intr() is the handler for disk read/write DMA interrupts */ -void ide_dma_intr (ide_drive_t *drive) +ide_startstop_t ide_dma_intr (ide_drive_t *drive) { int i; byte stat, dma_stat; @@ -151,12 +151,11 @@ i -= rq->current_nr_sectors; ide_end_request(1, HWGROUP(drive)); } - return; + return ide_stopped; } printk("%s: dma_intr: bad DMA status\n", drive->name); } - ide__sti(); /* local CPU only */ - ide_error(drive, "dma_intr", stat); + return ide_error(drive, "dma_intr", stat); } /* diff -u --recursive --new-file v2.2.13/linux/drivers/block/ide-floppy.c linux/drivers/block/ide-floppy.c --- v2.2.13/linux/drivers/block/ide-floppy.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/block/ide-floppy.c Tue Jan 4 10:12:14 2000 @@ -834,7 +834,7 @@ * idefloppy_pc_intr is the usual interrupt handler which will be called * during a packet command. */ -static void idefloppy_pc_intr (ide_drive_t *drive) +static ide_startstop_t idefloppy_pc_intr (ide_drive_t *drive) { idefloppy_floppy_t *floppy = drive->driver_data; idefloppy_status_reg_t status; @@ -879,24 +879,22 @@ rq->errors++; if (pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) { printk (KERN_ERR "ide-floppy: I/O error in request sense command\n"); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } idefloppy_retry_pc (drive); /* Retry operation */ - return; + return ide_stopped; /* queued, but not started */ } pc->error = 0; if (floppy->failed_pc == pc) floppy->failed_pc=NULL; pc->callback(drive); /* Command finished - Call the callback function */ - return; + return ide_stopped; } #ifdef CONFIG_BLK_DEV_IDEDMA if (test_and_clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) { printk (KERN_ERR "ide-floppy: The floppy wants to issue more interrupts in DMA mode\n"); (void) HWIF(drive)->dmaproc(ide_dma_off, drive); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } #endif /* CONFIG_BLK_DEV_IDEDMA */ bcount.b.high=IN_BYTE (IDE_BCOUNTH_REG); /* Get the number of bytes to transfer */ @@ -905,14 +903,12 @@ if (ireason.b.cod) { printk (KERN_ERR "ide-floppy: CoD != 0 in idefloppy_pc_intr\n"); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } if (ireason.b.io == test_bit (PC_WRITING, &pc->flags)) { /* Hopefully, we will never get here */ printk (KERN_ERR "ide-floppy: We wanted to %s, ", ireason.b.io ? "Write":"Read"); printk (KERN_ERR "but the floppy wants us to %s !\n",ireason.b.io ? "Read":"Write"); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } if (!test_bit (PC_WRITING, &pc->flags)) { /* Reading - Check that we have enough space */ temp = pc->actually_transferred + bcount.all; @@ -921,7 +917,7 @@ printk (KERN_ERR "ide-floppy: The floppy wants to send us more data than expected - discarding data\n"); idefloppy_discard_data (drive,bcount.all); ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD); - return; + return ide_started; } #if IDEFLOPPY_DEBUG_LOG printk (KERN_NOTICE "ide-floppy: The floppy wants to send us more data than expected - allowing transfer\n"); @@ -943,31 +939,33 @@ pc->current_position+=bcount.all; ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD); /* And set the interrupt handler again */ + return ide_started; } -static void idefloppy_transfer_pc (ide_drive_t *drive) +static ide_startstop_t idefloppy_transfer_pc (ide_drive_t *drive) { + ide_startstop_t startstop; idefloppy_floppy_t *floppy = drive->driver_data; idefloppy_ireason_reg_t ireason; - if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { + if (ide_wait_stat(&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { printk (KERN_ERR "ide-floppy: Strange, packet command initiated yet DRQ isn't asserted\n"); - return; + return startstop; } ireason.all=IN_BYTE (IDE_IREASON_REG); if (!ireason.b.cod || ireason.b.io) { printk (KERN_ERR "ide-floppy: (IO,CoD) != (0,1) while issuing a packet command\n"); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } ide_set_handler (drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD); /* Set the interrupt routine */ atapi_output_bytes (drive, floppy->pc->c, 12); /* Send the actual packet */ + return ide_started; } /* * Issue a packet command */ -static void idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *pc) +static ide_startstop_t idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *pc) { idefloppy_floppy_t *floppy = drive->driver_data; idefloppy_bcount_reg_t bcount; @@ -995,7 +993,7 @@ } floppy->failed_pc=NULL; pc->callback(drive); - return; + return ide_stopped; } #if IDEFLOPPY_DEBUG_LOG printk (KERN_INFO "Retry number - %d\n",pc->retries); @@ -1030,9 +1028,10 @@ if (test_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags)) { ide_set_handler (drive, &idefloppy_transfer_pc, IDEFLOPPY_WAIT_CMD); OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* Issue the packet command */ + return ide_started; } else { OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); - idefloppy_transfer_pc (drive); + return idefloppy_transfer_pc (drive); } } @@ -1138,7 +1137,7 @@ /* * idefloppy_do_request is our request handling function. */ -static void idefloppy_do_request (ide_drive_t *drive, struct request *rq, unsigned long block) +static ide_startstop_t idefloppy_do_request (ide_drive_t *drive, struct request *rq, unsigned long block) { idefloppy_floppy_t *floppy = drive->driver_data; idefloppy_pc_t *pc; @@ -1155,7 +1154,7 @@ else printk (KERN_ERR "ide-floppy: %s: I/O error\n", drive->name); idefloppy_end_request (0, HWGROUP(drive)); - return; + return ide_stopped; } switch (rq->cmd) { case READ: @@ -1163,7 +1162,7 @@ if (rq->sector % floppy->bs_factor || rq->nr_sectors % floppy->bs_factor) { printk ("%s: unsupported r/w request size\n", drive->name); idefloppy_end_request (0, HWGROUP(drive)); - return; + return ide_stopped; } pc = idefloppy_next_pc_storage (drive); idefloppy_create_rw_cmd (floppy, pc, rq, block); @@ -1174,10 +1173,10 @@ default: printk (KERN_ERR "ide-floppy: unsupported command %x in request queue\n", rq->cmd); idefloppy_end_request (0,HWGROUP (drive)); - return; + return ide_stopped; } pc->rq = rq; - idefloppy_issue_pc (drive, pc); + return idefloppy_issue_pc (drive, pc); } /* diff -u --recursive --new-file v2.2.13/linux/drivers/block/ide-probe.c linux/drivers/block/ide-probe.c --- v2.2.13/linux/drivers/block/ide-probe.c Mon Mar 22 12:44:18 1999 +++ linux/drivers/block/ide-probe.c Tue Jan 4 10:12:14 2000 @@ -386,12 +386,20 @@ /* Extract drive geometry from CMOS+BIOS if not already setup */ for (unit = 0; unit < MAX_DRIVES; ++unit) { ide_drive_t *drive = &hwif->drives[unit]; - if ((cmos_disks & (0xf0 >> (unit*4))) && !drive->present && !drive->nobios) { - drive->cyl = drive->bios_cyl = *(unsigned short *)BIOS; - drive->head = drive->bios_head = *(BIOS+2); - drive->sect = drive->bios_sect = *(BIOS+14); - drive->ctl = *(BIOS+8); - drive->present = 1; + if ((cmos_disks & (0xf0 >> (unit*4))) + && !drive->present && !drive->nobios) { + unsigned short cyl = *(unsigned short *)BIOS; + unsigned char head = *(BIOS+2); + unsigned char sect = *(BIOS+14); + if (cyl > 0 && head > 0 && sect > 0 && sect < 64) { + drive->cyl = drive->bios_cyl = cyl; + drive->head = drive->bios_head = head; + drive->sect = drive->bios_sect = sect; + drive->ctl = *(BIOS+8); + drive->present = 1; + } else + printk("hd%d: C/H/S=%d/%d/%d from BIOS ignored\n", + unit, cyl, head, sect); } BIOS += 16; } @@ -560,10 +568,6 @@ hwgroup->handler = NULL; hwgroup->drive = NULL; hwgroup->busy = 0; - hwgroup->spinlock = (spinlock_t)SPIN_LOCK_UNLOCKED; -#if (DEBUG_SPINLOCK > 0) - printk("hwgroup(%s) spinlock is %p\n", hwif->name, &hwgroup->spinlock); /* FIXME */ -#endif init_timer(&hwgroup->timer); hwgroup->timer.function = &ide_timer_expiry; hwgroup->timer.data = (unsigned long) hwgroup; diff -u --recursive --new-file v2.2.13/linux/drivers/block/ide-proc.c linux/drivers/block/ide-proc.c --- v2.2.13/linux/drivers/block/ide-proc.c Wed May 6 14:42:53 1998 +++ linux/drivers/block/ide-proc.c Tue Jan 4 10:12:14 2000 @@ -516,8 +516,10 @@ char *out = page; int len; - out += sprintf(out,"physical %hi/%hi/%hi\n", drive->cyl, drive->head, drive->sect); - out += sprintf(out,"logical %hi/%hi/%hi\n", drive->bios_cyl, drive->bios_head, drive->bios_sect); + out += sprintf(out,"physical %d/%d/%d\n", + drive->cyl, drive->head, drive->sect); + out += sprintf(out,"logical %d/%d/%d\n", + drive->bios_cyl, drive->bios_head, drive->bios_sect); len = out - page; PROC_IDE_READ_RETURN(page,start,off,count,eof,len); } diff -u --recursive --new-file v2.2.13/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c --- v2.2.13/linux/drivers/block/ide-tape.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/block/ide-tape.c Tue Jan 4 10:12:14 2000 @@ -346,6 +346,7 @@ #include #include #include +#include #include #include @@ -522,9 +523,9 @@ int b_count; byte *buffer; /* Data buffer */ byte *current_position; /* Pointer into the above buffer */ - void (*callback) (ide_drive_t *); /* Called when this packet command is completed */ + ide_startstop_t (*callback) (ide_drive_t *); /* Called when this packet command is completed */ byte pc_buffer[IDETAPE_PC_BUFFER_SIZE]; /* Temporary buffer */ - unsigned int flags; /* Status/Action bit flags */ + unsigned long flags; /* Status/Action bit flags */ } idetape_pc_t; /* @@ -541,19 +542,55 @@ * Capabilities and Mechanical Status Page */ typedef struct { - unsigned page_code :6; /* Page code - Should be 0x2a */ - unsigned reserved1_67 :2; +#if defined(__BIG_ENDIAN_BITFIELD) + u8 reserved1_67 :2; + u8 page_code :6; /* Page code - Should be 0x2a */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u8 page_code :6; /* Page code - Should be 0x2a */ + u8 reserved1_67 :2; +#else +#error "Please fix " +#endif u8 page_length; /* Page Length - Should be 0x12 */ u8 reserved2, reserved3; + +#if defined(__BIG_ENDIAN_BITFIELD) + unsigned reserved4_67 :2; + unsigned sprev :1; /* Supports SPACE in the reverse direction */ + unsigned reserved4_1234 :4; + unsigned ro :1; /* Read Only Mode */ + + unsigned reserved5_67 :2; + unsigned qfa :1; /* Supports the QFA two partition formats */ + unsigned reserved5_4 :1; + unsigned efmt :1; /* Supports ERASE command initiated formatting */ + unsigned reserved5_012 :3; + + unsigned cmprs :1; /* Supports data compression */ + unsigned ecc :1; /* Supports error correction */ + unsigned reserved6_45 :2; /* Reserved */ + unsigned eject :1; /* The device can eject the volume */ + unsigned prevent :1; /* The device defaults in the prevent state after power up */ + unsigned locked :1; /* The volume is locked */ + unsigned lock :1; /* Supports locking the volume */ + + unsigned slowb :1; /* The device restricts the byte count for PIO */ + unsigned reserved7_3_6 :4; + unsigned blk1024 :1; /* Supports 1024 bytes block size */ + unsigned blk512 :1; /* Supports 512 bytes block size */ + unsigned reserved7_0 :1; +#elif defined(__LITTLE_ENDIAN_BITFIELD) 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 */ @@ -561,11 +598,13 @@ 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 slowb :1; /* The device restricts the byte count for PIO */ +#endif /* transfers for slow buffer memory ??? */ u16 max_speed; /* Maximum speed supported in KBps */ u8 reserved10, reserved11; @@ -695,7 +734,7 @@ int pages_per_stage; int excess_bh_size; /* Wasted space in each stage */ - unsigned int flags; /* Status/Action flags */ + unsigned long flags; /* Status/Action flags */ spinlock_t spinlock; /* protects the ide-tape queue */ } idetape_tape_t; @@ -787,6 +826,16 @@ typedef union { unsigned all :8; struct { +#if defined(__BIG_ENDIAN_BITFIELD) + unsigned bsy :1; /* The device has access to the command block */ + unsigned drdy :1; /* Ignored for ATAPI commands (ready to accept ATA command) */ + unsigned reserved5 :1; /* Reserved */ + unsigned dsc :1; /* Buffer availability / Media access command finished */ + unsigned drq :1; /* Data is request by the device */ + unsigned corr :1; /* Correctable error occurred */ + unsigned idx :1; /* Reserved */ + unsigned check :1; /* Error occurred */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) unsigned check :1; /* Error occurred */ unsigned idx :1; /* Reserved */ unsigned corr :1; /* Correctable error occurred */ @@ -795,6 +844,7 @@ unsigned reserved5 :1; /* Reserved */ unsigned drdy :1; /* Ignored for ATAPI commands (ready to accept ATA command) */ unsigned bsy :1; /* The device has access to the command block */ +#endif } b; } idetape_status_reg_t; @@ -804,11 +854,19 @@ typedef union { unsigned all :8; struct { +#if defined(__BIG_ENDIAN_BITFIELD) + unsigned sense_key :4; /* Sense key of the last failed packet command */ + unsigned mcr :1; /* Media Change Requested - As defined by ATA */ + unsigned abrt :1; /* Aborted command - As defined by ATA */ + unsigned eom :1; /* End Of Media Detected */ + unsigned ili :1; /* Illegal Length Indication */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) unsigned ili :1; /* Illegal Length Indication */ unsigned eom :1; /* End Of Media Detected */ unsigned abrt :1; /* Aborted command - As defined by ATA */ unsigned mcr :1; /* Media Change Requested - As defined by ATA */ unsigned sense_key :4; /* Sense key of the last failed packet command */ +#endif } b; } idetape_error_reg_t; @@ -818,10 +876,17 @@ typedef union { unsigned all :8; struct { +#if defined(__BIG_ENDIAN_BITFIELD) + unsigned reserved7 :1; /* Reserved */ + unsigned reserved654 :3; /* Reserved (Tag Type) */ + unsigned reserved321 :3; /* Reserved */ + unsigned dma :1; /* Using DMA of PIO */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) unsigned dma :1; /* Using DMA of PIO */ unsigned reserved321 :3; /* Reserved */ unsigned reserved654 :3; /* Reserved (Tag Type) */ unsigned reserved7 :1; /* Reserved */ +#endif } b; } idetape_feature_reg_t; @@ -831,8 +896,13 @@ typedef union { unsigned all :16; struct { +#if defined(__BIG_ENDIAN_BITFIELD) + unsigned high :8; /* MSB */ + unsigned low :8; /* LSB */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) unsigned low :8; /* LSB */ unsigned high :8; /* MSB */ +#endif } b; } idetape_bcount_reg_t; @@ -842,9 +912,15 @@ typedef union { unsigned all :8; struct { +#if defined(__BIG_ENDIAN_BITFIELD) + unsigned reserved :6; /* Reserved */ + unsigned io :1; /* The device requests us to read (1) or write (0) */ + unsigned cod :1; /* Information transferred is command (1) or data (0) */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) unsigned cod :1; /* Information transferred is command (1) or data (0) */ unsigned io :1; /* The device requests us to read (1) or write (0) */ unsigned reserved :6; /* Reserved */ +#endif } b; } idetape_ireason_reg_t; @@ -854,11 +930,19 @@ typedef union { unsigned all :8; struct { +#if defined(__BIG_ENDIAN_BITFIELD) + unsigned one7 :1; /* Should be set to 1 */ + unsigned reserved6 :1; /* Reserved */ + unsigned one5 :1; /* Should be set to 1 */ + unsigned drv :1; /* The responding drive will be drive 0 (0) or drive 1 (1) */ + unsigned sam_lun :4; /* Should be zero with ATAPI (not used) */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) unsigned sam_lun :4; /* Should be zero with ATAPI (not used) */ unsigned drv :1; /* The responding drive will be drive 0 (0) or drive 1 (1) */ unsigned one5 :1; /* Should be set to 1 */ unsigned reserved6 :1; /* Reserved */ unsigned one7 :1; /* Should be set to 1 */ +#endif } b; } idetape_drivesel_reg_t; @@ -868,11 +952,19 @@ typedef union { unsigned all :8; struct { +#if defined(__BIG_ENDIAN_BITFIELD) + unsigned reserved4567 :4; /* Reserved */ + unsigned one3 :1; /* Should be set to 1 */ + unsigned srst :1; /* ATA software reset. ATAPI devices should use the new ATAPI srst. */ + unsigned nien :1; /* Device interrupt is disabled (1) or enabled (0) */ + unsigned zero0 :1; /* Should be set to zero */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) unsigned zero0 :1; /* Should be set to zero */ unsigned nien :1; /* Device interrupt is disabled (1) or enabled (0) */ unsigned srst :1; /* ATA software reset. ATAPI devices should use the new ATAPI srst. */ unsigned one3 :1; /* Should be set to 1 */ unsigned reserved4567 :4; /* Reserved */ +#endif } b; } idetape_control_reg_t; @@ -890,6 +982,15 @@ * the ATAPI IDENTIFY DEVICE command. */ struct idetape_id_gcw { +#if defined(__BIG_ENDIAN_BITFIELD) + unsigned protocol :2; /* Protocol type */ + unsigned reserved13 :1; /* Reserved */ + unsigned device_type :5; /* Device type */ + unsigned removable :1; /* Removable media */ + unsigned drq_type :2; /* Command packet DRQ type */ + unsigned reserved234 :3; /* Reserved */ + unsigned packet_size :2; /* Packet Size */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) unsigned packet_size :2; /* Packet Size */ unsigned reserved234 :3; /* Reserved */ unsigned drq_type :2; /* Command packet DRQ type */ @@ -897,23 +998,50 @@ unsigned device_type :5; /* Device type */ unsigned reserved13 :1; /* Reserved */ unsigned protocol :2; /* Protocol type */ +#endif }; /* * INQUIRY packet command - Data Format (From Table 6-8 of QIC-157C) */ typedef struct { +#if defined(__BIG_ENDIAN_BITFIELD) + unsigned reserved0_765 :3; /* Peripheral Qualifier - Reserved */ + unsigned device_type :5; /* Peripheral Device Type */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) unsigned device_type :5; /* Peripheral Device Type */ unsigned reserved0_765 :3; /* Peripheral Qualifier - Reserved */ +#endif + +#if defined(__BIG_ENDIAN_BITFIELD) + unsigned rmb :1; /* Removable Medium Bit */ + unsigned reserved1_6t0 :7; /* Reserved */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) unsigned reserved1_6t0 :7; /* Reserved */ unsigned rmb :1; /* Removable Medium Bit */ +#endif + +#if defined(__BIG_ENDIAN_BITFIELD) + unsigned iso_version :2; /* ISO Version */ + unsigned ecma_version :3; /* ECMA Version */ + unsigned ansi_version :3; /* ANSI Version */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) unsigned ansi_version :3; /* ANSI Version */ unsigned ecma_version :3; /* ECMA Version */ unsigned iso_version :2; /* ISO Version */ +#endif + +#if defined(__BIG_ENDIAN_BITFIELD) + unsigned reserved3_7 :1; /* AENC - Reserved */ + unsigned reserved3_6 :1; /* TrmIOP - Reserved */ + unsigned reserved3_45 :2; /* Reserved */ + unsigned response_format :4; /* Response Data Format */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) unsigned response_format :4; /* Response Data Format */ unsigned reserved3_45 :2; /* Reserved */ unsigned reserved3_6 :1; /* TrmIOP - Reserved */ unsigned reserved3_7 :1; /* AENC - Reserved */ +#endif u8 additional_length; /* Additional Length (total_length-4) */ u8 rsv5, rsv6, rsv7; /* Reserved */ u8 vendor_id[8]; /* Vendor Identification */ @@ -928,11 +1056,19 @@ * READ POSITION packet command - Data Format (From Table 6-57) */ typedef struct { +#if defined(__BIG_ENDIAN_BITFIELD) + unsigned bop :1; /* Beginning Of Partition */ + unsigned eop :1; /* End Of Partition */ + unsigned reserved0_543 :3; /* Reserved */ + unsigned bpu :1; /* Block Position Unknown */ + unsigned reserved0_10 :2; /* Reserved */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) 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 */ +#endif u8 partition; /* Partition Number */ u8 reserved2, reserved3; /* Reserved */ u32 first_block; /* First Block Location */ @@ -946,22 +1082,41 @@ * REQUEST SENSE packet command result - Data Format. */ typedef struct { +#if defined(__BIG_ENDIAN_BITFIELD) + unsigned valid :1; /* The information field conforms to QIC-157C */ + unsigned error_code :7; /* Current of deferred errors */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) unsigned error_code :7; /* Current of deferred errors */ unsigned valid :1; /* The information field conforms to QIC-157C */ +#endif + u8 reserved1 :8; /* Segment Number - Reserved */ +#if defined(__BIG_ENDIAN_BITFIELD) + unsigned filemark :1; /* Filemark */ + unsigned eom :1; /* End Of Medium */ + unsigned ili :1; /* Incorrect Length Indicator */ + unsigned reserved2_4 :1; /* Reserved */ + unsigned sense_key :4; /* Sense Key */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) 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 */ +#endif 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 */ +#if defined(__BIG_ENDIAN_BITFIELD) + unsigned sksv :1; /* Sense Key Specific information is valid */ + unsigned sk_specific1 :7; /* Sense Key Specific */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) unsigned sk_specific1 :7; /* Sense Key Specific */ unsigned sksv :1; /* Sense Key Specific information is valid */ +#endif u8 sk_specific2; /* Sense Key Specific */ u8 sk_specific3; /* Sense Key Specific */ u8 pad[2]; /* Padding to 20 bytes */ @@ -1000,16 +1155,35 @@ * The Data Compression Page, as returned by the MODE SENSE packet command. */ typedef struct { +#if defined(__BIG_ENDIAN_BITFIELD) + unsigned ps :1; + unsigned reserved0 :1; /* Reserved */ + unsigned page_code :6; /* Page Code - Should be 0xf */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) unsigned page_code :6; /* Page Code - Should be 0xf */ unsigned reserved0 :1; /* Reserved */ unsigned ps :1; +#endif u8 page_length; /* Page Length - Should be 14 */ +#if defined(__BIG_ENDIAN_BITFIELD) + unsigned dce :1; /* Data Compression Enable */ + unsigned dcc :1; /* Data Compression Capable */ + unsigned reserved2 :6; /* Reserved */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) unsigned reserved2 :6; /* Reserved */ unsigned dcc :1; /* Data Compression Capable */ unsigned dce :1; /* Data Compression Enable */ +#endif + +#if defined(__BIG_ENDIAN_BITFIELD) + unsigned dde :1; /* Data Decompression Enable */ + unsigned red :2; /* Report Exception on Decompression */ + unsigned reserved3 :5; /* Reserved */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) unsigned reserved3 :5; /* Reserved */ unsigned red :2; /* Report Exception on Decompression */ unsigned dde :1; /* Data Decompression Enable */ +#endif u32 ca; /* Compression Algorithm */ u32 da; /* Decompression Algorithm */ u8 reserved[4]; /* Reserved */ @@ -1019,17 +1193,31 @@ * The Medium Partition Page, as returned by the MODE SENSE packet command. */ typedef struct { +#if defined(__BIG_ENDIAN_BITFIELD) + unsigned ps :1; + unsigned reserved1_6 :1; /* Reserved */ + unsigned page_code :6; /* Page Code - Should be 0x11 */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) unsigned page_code :6; /* Page Code - Should be 0x11 */ unsigned reserved1_6 :1; /* Reserved */ unsigned ps :1; +#endif u8 page_length; /* Page Length - Should be 6 */ u8 map; /* Maximum Additional Partitions - Should be 0 */ u8 apd; /* Additional Partitions Defined - Should be 0 */ +#if defined(__BIG_ENDIAN_BITFIELD) + unsigned fdp :1; /* Fixed Data Partitions */ + unsigned sdp :1; /* Should be 0 */ + unsigned idp :1; /* Should be 0 */ + unsigned psum :2; /* Should be 0 */ + unsigned reserved4_012 :3; /* Reserved */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) 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 */ +#endif u8 mfr; /* Medium Format Recognition */ u8 reserved[2]; /* Reserved */ } idetape_medium_partition_page_t; @@ -1656,7 +1844,7 @@ } } -static void idetape_request_sense_callback (ide_drive_t *drive) +static ide_startstop_t idetape_request_sense_callback (ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; @@ -1670,6 +1858,7 @@ printk (KERN_ERR "Error in REQUEST SENSE itself - Aborting request!\n"); idetape_end_request (0,HWGROUP (drive)); } + return ide_stopped; } /* @@ -1701,7 +1890,7 @@ * last packet command. We queue a request sense packet command in * the head of the request list. */ -static void idetape_retry_pc (ide_drive_t *drive) +static ide_startstop_t idetape_retry_pc (ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; idetape_pc_t *pc; @@ -1714,6 +1903,7 @@ idetape_create_request_sense_cmd (pc); set_bit (IDETAPE_IGNORE_DSC, &tape->flags); idetape_queue_pc_head (drive, pc, rq); + return ide_stopped; } /* @@ -1724,7 +1914,7 @@ * algorithm described before idetape_issue_packet_command. * */ -static void idetape_pc_intr (ide_drive_t *drive) +static ide_startstop_t idetape_pc_intr (ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; idetape_status_reg_t status; @@ -1780,11 +1970,9 @@ #endif /* IDETAPE_DEBUG_LOG */ if (pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) { printk (KERN_ERR "ide-tape: I/O error in request sense command\n"); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } - idetape_retry_pc (drive); /* Retry operation */ - return; + return idetape_retry_pc (drive); /* Retry operation */ } pc->error = 0; if (test_bit (PC_WAIT_FOR_DSC, &pc->flags) && !status.b.dsc) { /* Media access command */ @@ -1792,20 +1980,18 @@ tape->dsc_polling_frequency = IDETAPE_DSC_MA_FAST; tape->dsc_timeout = jiffies + IDETAPE_DSC_MA_TIMEOUT; idetape_postpone_request (drive); /* Allow ide.c to handle other requests */ - return; + return ide_stopped; } if (tape->failed_pc == pc) tape->failed_pc=NULL; - pc->callback(drive); /* Command finished - Call the callback function */ - return; + return pc->callback(drive); /* Command finished - Call the callback function */ } #ifdef CONFIG_BLK_DEV_IDEDMA if (test_and_clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) { printk (KERN_ERR "ide-tape: The tape wants to issue more interrupts in DMA mode\n"); printk (KERN_ERR "ide-tape: DMA disabled, reverting to PIO\n"); (void) HWIF(drive)->dmaproc(ide_dma_off, drive); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } #endif /* CONFIG_BLK_DEV_IDEDMA */ bcount.b.high=IN_BYTE (IDE_BCOUNTH_REG); /* Get the number of bytes to transfer */ @@ -1814,14 +2000,12 @@ if (ireason.b.cod) { printk (KERN_ERR "ide-tape: CoD != 0 in idetape_pc_intr\n"); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } if (ireason.b.io == test_bit (PC_WRITING, &pc->flags)) { /* Hopefully, we will never get here */ printk (KERN_ERR "ide-tape: We wanted to %s, ", ireason.b.io ? "Write":"Read"); printk (KERN_ERR "but the tape wants us to %s !\n",ireason.b.io ? "Read":"Write"); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } if (!test_bit (PC_WRITING, &pc->flags)) { /* Reading - Check that we have enough space */ temp = pc->actually_transferred + bcount.all; @@ -1830,7 +2014,7 @@ printk (KERN_ERR "ide-tape: The tape wants to send us more data than expected - discarding data\n"); idetape_discard_data (drive,bcount.all); ide_set_handler (drive,&idetape_pc_intr,IDETAPE_WAIT_CMD); - return; + return ide_started; } #if IDETAPE_DEBUG_LOG printk (KERN_NOTICE "ide-tape: The tape wants to send us more data than expected - allowing transfer\n"); @@ -1852,6 +2036,7 @@ pc->current_position+=bcount.all; ide_set_handler (drive,&idetape_pc_intr,IDETAPE_WAIT_CMD); /* And set the interrupt handler again */ + return ide_started; } /* @@ -1897,16 +2082,17 @@ * */ -static void idetape_transfer_pc(ide_drive_t *drive) +static ide_startstop_t idetape_transfer_pc(ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; idetape_pc_t *pc = tape->pc; idetape_ireason_reg_t ireason; int retries = 100; + ide_startstop_t startstop; - if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { + if (ide_wait_stat(&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { printk (KERN_ERR "ide-tape: Strange, packet command initiated yet DRQ isn't asserted\n"); - return; + return startstop; } ireason.all=IN_BYTE (IDE_IREASON_REG); while (retries-- && (!ireason.b.cod || ireason.b.io)) { @@ -1921,14 +2107,14 @@ } if (!ireason.b.cod || ireason.b.io) { printk (KERN_ERR "ide-tape: (IO,CoD) != (0,1) while issuing a packet command\n"); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } ide_set_handler(drive, &idetape_pc_intr, IDETAPE_WAIT_CMD); /* Set the interrupt routine */ atapi_output_bytes (drive,pc->c,12); /* Send the actual packet */ + return ide_started; } -static void idetape_issue_packet_command (ide_drive_t *drive, idetape_pc_t *pc) +static ide_startstop_t idetape_issue_packet_command (ide_drive_t *drive, idetape_pc_t *pc) { idetape_tape_t *tape = drive->driver_data; idetape_bcount_reg_t bcount; @@ -1957,8 +2143,7 @@ pc->error = IDETAPE_ERROR_GENERAL; /* Giving up */ } tape->failed_pc=NULL; - pc->callback(drive); - return; + return pc->callback(drive); } #if IDETAPE_DEBUG_LOG printk (KERN_INFO "Retry number - %d\n",pc->retries); @@ -1992,13 +2177,14 @@ if (test_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags)) { ide_set_handler(drive, &idetape_transfer_pc, IDETAPE_WAIT_CMD); OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG); + return ide_started; } else { OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG); - idetape_transfer_pc(drive); + return idetape_transfer_pc(drive); } } -static void idetape_media_access_finished (ide_drive_t *drive) +static ide_startstop_t idetape_media_access_finished (ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; idetape_pc_t *pc = tape->pc; @@ -2008,8 +2194,7 @@ if (status.b.dsc) { if (status.b.check) { /* Error detected */ printk (KERN_ERR "ide-tape: %s: I/O error, ",tape->name); - idetape_retry_pc (drive); /* Retry operation */ - return; + return idetape_retry_pc (drive); /* Retry operation */ } pc->error = 0; if (tape->failed_pc == pc) @@ -2018,13 +2203,13 @@ pc->error = IDETAPE_ERROR_GENERAL; tape->failed_pc = NULL; } - pc->callback (drive); + return pc->callback (drive); } /* * General packet command callback function. */ -static void idetape_pc_callback (ide_drive_t *drive) +static ide_startstop_t idetape_pc_callback (ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; @@ -2033,9 +2218,10 @@ #endif /* IDETAPE_DEBUG_LOG */ idetape_end_request (tape->pc->error ? 0:1, HWGROUP(drive)); + return ide_stopped; } -static void idetape_rw_callback (ide_drive_t *drive) +static ide_startstop_t idetape_rw_callback (ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; struct request *rq = HWGROUP(drive)->rq; @@ -2052,6 +2238,7 @@ idetape_end_request (1, HWGROUP (drive)); else idetape_end_request (tape->pc->error, HWGROUP (drive)); + return ide_stopped; } static void idetape_create_locate_cmd (idetape_pc_t *pc, unsigned int block, byte partition) @@ -2170,7 +2357,7 @@ set_bit (PC_DMA_RECOMMENDED, &pc->flags); } -static void idetape_read_position_callback (ide_drive_t *drive) +static ide_startstop_t idetape_read_position_callback (ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; idetape_read_position_result_t *result; @@ -2200,6 +2387,7 @@ } } else idetape_end_request (0,HWGROUP (drive)); + return ide_stopped; } static void idetape_create_read_position_cmd (idetape_pc_t *pc) @@ -2213,7 +2401,7 @@ /* * idetape_do_request is our request handling function. */ -static void idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned long block) +static ide_startstop_t idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned long block) { idetape_tape_t *tape = drive->driver_data; idetape_pc_t *pc; @@ -2229,24 +2417,23 @@ /* * We do not support buffer cache originated requests. */ - printk (KERN_NOTICE "ide-tape: %s: Unsupported command in request queue\n", drive->name); + printk (KERN_NOTICE "ide-tape: %s: Unsupported command in request queue (%d)\n", drive->name, rq->cmd); ide_end_request (0,HWGROUP (drive)); /* Let the common code handle it */ - return; + return ide_stopped; } /* * Retry a failed packet command */ if (tape->failed_pc != NULL && tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) { - idetape_issue_packet_command (drive, tape->failed_pc); - return; + return idetape_issue_packet_command (drive, tape->failed_pc); } #if IDETAPE_DEBUG_BUGS if (postponed_rq != NULL) if (rq != postponed_rq) { printk (KERN_ERR "ide-tape: ide-tape.c bug - Two DSC requests were queued\n"); idetape_end_request (0,HWGROUP (drive)); - return; + return ide_stopped; } #endif /* IDETAPE_DEBUG_BUGS */ @@ -2266,15 +2453,16 @@ tape->dsc_timeout = jiffies + IDETAPE_DSC_RW_TIMEOUT; } else if ((signed long) (jiffies - tape->dsc_timeout) > 0) { printk (KERN_ERR "ide-tape: %s: DSC timeout\n", tape->name); - if (rq->cmd == IDETAPE_PC_RQ2) + if (rq->cmd == IDETAPE_PC_RQ2) { idetape_media_access_finished (drive); - else - ide_do_reset (drive); - return; + return ide_stopped; + } else { + return ide_do_reset (drive); + } } else if (jiffies - tape->dsc_polling_start > IDETAPE_DSC_MA_THRESHOLD) tape->dsc_polling_frequency = IDETAPE_DSC_MA_SLOW; idetape_postpone_request (drive); - return; + return ide_stopped; } switch (rq->cmd) { case IDETAPE_READ_RQ: @@ -2289,20 +2477,20 @@ rq->cmd = IDETAPE_WRITE_RQ; rq->errors = IDETAPE_ERROR_EOD; idetape_end_request (1, HWGROUP(drive)); - return; + return ide_stopped; case IDETAPE_PC_RQ1: pc=(idetape_pc_t *) rq->buffer; rq->cmd = IDETAPE_PC_RQ2; break; case IDETAPE_PC_RQ2: idetape_media_access_finished (drive); - return; + return ide_stopped; default: printk (KERN_ERR "ide-tape: bug in IDETAPE_RQ_CMD macro\n"); idetape_end_request (0,HWGROUP (drive)); - return; + return ide_stopped; } - idetape_issue_packet_command (drive, pc); + return idetape_issue_packet_command (drive, pc); } /* diff -u --recursive --new-file v2.2.13/linux/drivers/block/ide.c linux/drivers/block/ide.c --- v2.2.13/linux/drivers/block/ide.c Tue Jan 4 11:10:33 2000 +++ linux/drivers/block/ide.c Tue Jan 4 10:12:14 2000 @@ -1,5 +1,5 @@ /* - * linux/drivers/block/ide.c Version 6.18 August 16, 1998 + * linux/drivers/block/ide.c Version 6.19 December 28, 1999 * * Copyright (C) 1994-1998 Linus Torvalds & authors (see below) */ @@ -92,6 +92,7 @@ * Version 6.16 fixed various bugs; even more SMP friendly * Version 6.17 fix for newest EZ-Drive problem * Version 6.18 default unpartitioned-disk translation now "BIOS LBA" + * Version 6.19 Added SMP support; fixed multmode issues. -ml * * Some additional driver compile-time options are in ide.h * @@ -433,7 +434,7 @@ #if 0 udelay(1); /* need to guarantee 400ns since last command was issued */ #endif - if (GET_STAT() & BUSY_STAT) + if (GET_STAT() & BUSY_STAT) /* Note: this may clear a pending IRQ!! */ return 0; /* drive busy: definitely not interrupting */ return 1; /* drive ready: *might* be interrupting */ } @@ -471,17 +472,15 @@ unsigned long flags; ide_hwgroup_t *hwgroup = HWGROUP(drive); - spin_lock_irqsave(&hwgroup->spinlock, flags); -#ifdef DEBUG + spin_lock_irqsave(&io_request_lock, flags); if (hwgroup->handler != NULL) { printk("%s: ide_set_handler: handler not null; old=%p, new=%p\n", drive->name, hwgroup->handler, handler); } -#endif hwgroup->handler = handler; hwgroup->timer.expires = jiffies + timeout; - add_timer(&(hwgroup->timer)); - spin_unlock_irqrestore(&hwgroup->spinlock, flags); + add_timer(&hwgroup->timer); + spin_unlock_irqrestore(&io_request_lock, flags); } /* @@ -516,7 +515,7 @@ } } -static void do_reset1 (ide_drive_t *, int); /* needed below */ +static ide_startstop_t do_reset1 (ide_drive_t *, int); /* needed below */ /* * atapi_reset_pollfunc() gets invoked to poll the interface for completion every 50ms @@ -524,7 +523,7 @@ * and we have not yet hit our maximum waiting time, then the timer is restarted * for another 50ms. */ -static void atapi_reset_pollfunc (ide_drive_t *drive) +static ide_startstop_t atapi_reset_pollfunc (ide_drive_t *drive) { ide_hwgroup_t *hwgroup = HWGROUP(drive); byte stat; @@ -537,14 +536,14 @@ } else { if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) { ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20); - return; /* continue polling */ + return ide_started; /* continue polling */ } hwgroup->poll_timeout = 0; /* end of polling */ printk("%s: ATAPI reset timed-out, status=0x%02x\n", drive->name, stat); - do_reset1 (drive, 1); /* do it the old fashioned way */ - return; + return do_reset1 (drive, 1); /* do it the old fashioned way */ } hwgroup->poll_timeout = 0; /* done polling */ + return ide_stopped; } /* @@ -553,7 +552,7 @@ * and we have not yet hit our maximum waiting time, then the timer is restarted * for another 50ms. */ -static void reset_pollfunc (ide_drive_t *drive) +static ide_startstop_t reset_pollfunc (ide_drive_t *drive) { ide_hwgroup_t *hwgroup = HWGROUP(drive); ide_hwif_t *hwif = HWIF(drive); @@ -562,7 +561,7 @@ if (!OK_STAT(tmp=GET_STAT(), 0, BUSY_STAT)) { if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) { ide_set_handler (drive, &reset_pollfunc, HZ/20); - return; /* continue polling */ + return ide_started; /* continue polling */ } printk("%s: reset timed-out, status=0x%02x\n", hwif->name, tmp); } else { @@ -594,6 +593,7 @@ } } hwgroup->poll_timeout = 0; /* done polling */ + return ide_stopped; } static void pre_reset (ide_drive_t *drive) @@ -623,7 +623,7 @@ * (up to 30 seconds worstcase). So, instead of busy-waiting here for it, * we set a timer to poll at 50ms intervals. */ -static void do_reset1 (ide_drive_t *drive, int do_not_try_atapi) +static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) { unsigned int unit; unsigned long flags; @@ -642,7 +642,7 @@ hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20); __restore_flags (flags); /* local CPU only */ - return; + return ide_started; } /* @@ -670,14 +670,15 @@ #endif /* OK_TO_RESET_CONTROLLER */ __restore_flags (flags); /* local CPU only */ + return ide_started; } /* * ide_do_reset() is the entry point to the drive/interface reset code. */ -void ide_do_reset (ide_drive_t *drive) +ide_startstop_t ide_do_reset (ide_drive_t *drive) { - do_reset1 (drive, 0); + return do_reset1 (drive, 0); } /* @@ -703,11 +704,8 @@ HWGROUP(drive)->rq = NULL; rq->rq_status = RQ_INACTIVE; spin_unlock_irqrestore(&io_request_lock, flags); - save_flags(flags); /* all CPUs; overkill? */ - cli(); /* all CPUs; overkill? */ if (rq->sem != NULL) up(rq->sem); /* inform originator that rq has been serviced */ - restore_flags(flags); /* all CPUs; overkill? */ } /* @@ -800,19 +798,19 @@ /* * ide_error() takes action based on the error returned by the drive. */ -void ide_error (ide_drive_t *drive, const char *msg, byte stat) +ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, byte stat) { struct request *rq; byte err; err = ide_dump_status(drive, msg, stat); if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL) - return; + return ide_stopped; /* retry only "normal" I/O: */ if (rq->cmd == IDE_DRIVE_CMD) { rq->errors = 1; ide_end_drive_cmd(drive, stat, err); - return; + return ide_stopped; } if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) { /* other bits are useless when BUSY */ rq->errors |= ERROR_RESET; @@ -821,7 +819,7 @@ /* err has different meaning on cdrom and tape */ if (err == ABRT_ERR) { if (drive->select.b.lba && IN_BYTE(IDE_COMMAND_REG) == WIN_SPECIFY) - return; /* some newer drives don't support WIN_SPECIFY */ + return ide_stopped; /* some newer drives don't support WIN_SPECIFY */ } else if ((err & (ABRT_ERR | ICRC_ERR)) == (ABRT_ERR | ICRC_ERR)) ; /* UDMA crc error -- just retry the operation */ else if (err & (BBD_ERR | ECC_ERR)) /* retries won't help these */ @@ -843,12 +841,13 @@ } else { if ((rq->errors & ERROR_RESET) == ERROR_RESET) { ++rq->errors; - ide_do_reset(drive); - return; - } else if ((rq->errors & ERROR_RECAL) == ERROR_RECAL) + return ide_do_reset(drive); + } + if ((rq->errors & ERROR_RECAL) == ERROR_RECAL) drive->special.b.recalibrate = 1; ++rq->errors; } + return ide_stopped; } /* @@ -866,7 +865,7 @@ /* * drive_cmd_intr() is invoked on completion of a special DRIVE_CMD. */ -static void drive_cmd_intr (ide_drive_t *drive) +static ide_startstop_t drive_cmd_intr (ide_drive_t *drive) { struct request *rq = HWGROUP(drive)->rq; byte *args = (byte *) rq->buffer; @@ -883,17 +882,17 @@ udelay(100); } - if (OK_STAT(stat, READY_STAT, BAD_STAT)) - ide_end_drive_cmd (drive, stat, GET_ERR()); - else - ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */ + if (!OK_STAT(stat, READY_STAT, BAD_STAT)) + return ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */ + ide_end_drive_cmd (drive, stat, GET_ERR()); + return ide_stopped; } /* * do_special() is used to issue WIN_SPECIFY, WIN_RESTORE, and WIN_SETMULT * commands to a drive. It used to do much more, but has been scaled back. */ -static inline void do_special (ide_drive_t *drive) +static ide_startstop_t do_special (ide_drive_t *drive) { special_t *s = &drive->special; @@ -906,11 +905,12 @@ if (tuneproc != NULL) tuneproc(drive, drive->tune_req); } else if (drive->driver != NULL) { - DRIVER(drive)->special(drive); + return DRIVER(drive)->special(drive); } else if (s->all) { printk("%s: bad special flag: 0x%02x\n", drive->name, s->all); s->all = 0; } + return ide_stopped; } /* @@ -924,12 +924,12 @@ * setting a timer to wake up at half second intervals thereafter, * until timeout is achieved, before timing out. */ -int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout) +int ide_wait_stat (ide_startstop_t *startstop, ide_drive_t *drive, byte good, byte bad, unsigned long timeout) { byte stat; int i; unsigned long flags; - + udelay(1); /* spec allows drive 400ns to assert "BUSY" */ if ((stat = GET_STAT()) & BUSY_STAT) { __save_flags(flags); /* local CPU only */ @@ -938,7 +938,7 @@ while ((stat = GET_STAT()) & BUSY_STAT) { if (0 < (signed long)(jiffies - timeout)) { __restore_flags(flags); /* local CPU only */ - ide_error(drive, "status timeout", stat); + *startstop = ide_error(drive, "status timeout", stat); return 1; } } @@ -956,7 +956,7 @@ if (OK_STAT((stat = GET_STAT()), good, bad)) return 0; } - ide_error(drive, "status error", stat); + *startstop = ide_error(drive, "status error", stat); return 1; } @@ -964,7 +964,7 @@ * execute_drive_cmd() issues a special drive command, * usually initiated by ioctl() from the external hdparm program. */ -static void execute_drive_cmd (ide_drive_t *drive, struct request *rq) +static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, struct request *rq) { byte *args = rq->buffer; if (args) { @@ -978,7 +978,7 @@ } OUT_BYTE(args[2],IDE_FEATURE_REG); ide_cmd(drive, args[0], args[1], &drive_cmd_intr); - return; + return ide_started; } else { /* * NULL is actually a valid way of waiting for @@ -988,21 +988,21 @@ printk("%s: DRIVE_CMD (null)\n", drive->name); #endif ide_end_drive_cmd(drive, GET_STAT(), GET_ERR()); - return; + return ide_stopped; } } /* * start_request() initiates handling of a new I/O request */ -static inline void start_request (ide_drive_t *drive) +static ide_startstop_t start_request (ide_drive_t *drive) { + ide_startstop_t startstop; unsigned long block, blockend; struct request *rq = drive->queue; unsigned int minor = MINOR(rq->rq_dev), unit = minor >> PARTN_BITS; ide_hwif_t *hwif = HWIF(drive); - ide__sti(); /* local CPU only */ #ifdef DEBUG printk("%s: start_request: current=0x%08lx\n", hwif->name, (unsigned long) rq); #endif @@ -1032,29 +1032,27 @@ while ((read_timer() - hwif->last_time) < DISK_RECOVERY_TIME); #endif SELECT_DRIVE(hwif, drive); - if (ide_wait_stat(drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) { + if (ide_wait_stat(&startstop, drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) { printk("%s: drive not ready for command\n", drive->name); - return; + return startstop; } if (!drive->special.all) { if (rq->cmd == IDE_DRIVE_CMD) { - execute_drive_cmd(drive, rq); - return; + return execute_drive_cmd(drive, rq); } if (drive->driver != NULL) { - DRIVER(drive)->do_request(drive, rq, block); - return; + return (DRIVER(drive)->do_request(drive, rq, block)); } printk("%s: media type %d not supported\n", drive->name, drive->media); goto kill_rq; } - do_special(drive); - return; + return do_special(drive); kill_rq: if (drive->driver != NULL) DRIVER(drive)->end_request(0, HWGROUP(drive)); else ide_end_request(0, HWGROUP(drive)); + return ide_stopped; } /* @@ -1115,22 +1113,54 @@ } /* - * Caller must have already acquired spinlock using *spinflags + * Issue a new request to a drive from hwgroup + * Caller must have already done spin_lock_irqsave(&io_request_lock, ..); + * + * A hwgroup is a serialized group of IDE interfaces. Usually there is + * exactly one hwif (interface) per hwgroup, but buggy controllers (eg. CMD640) + * may have both interfaces in a single hwgroup to "serialize" access. + * Or possibly multiple ISA interfaces can share a common IRQ by being grouped + * together into one hwgroup for serialized access. + * + * Note also that several hwgroups can end up sharing a single IRQ, + * possibly along with many other devices. This is especially common in + * PCI-based systems with off-board IDE controller cards. + * + * The IDE driver uses the single global io_request_lock spinlock to protect + * access to the request queues, and to protect the hwgroup->busy flag. + * + * The first thread into the driver for a particular hwgroup sets the + * hwgroup->busy flag to indicate that this hwgroup is now active, + * and then initiates processing of the top request from the request queue. + * + * Other threads attempting entry notice the busy setting, and will simply + * queue their new requests and exit immediately. Note that hwgroup->busy + * remains set even when the driver is merely awaiting the next interrupt. + * Thus, the meaning is "this hwgroup is busy processing a request". + * + * When processing of a request completes, the completing thread or IRQ-handler + * will start the next request from the queue. If no more work remains, + * the driver will clear the hwgroup->busy flag and exit. + * + * The io_request_lock (spinlock) is used to protect all access to the + * hwgroup->busy flag, but is otherwise not needed for most processing in + * the driver. This makes the driver much more friendlier to shared IRQs + * than previous designs, while remaining 100% (?) SMP safe and capable. */ -static void ide_do_request (ide_hwgroup_t *hwgroup, unsigned long *hwgroup_flags, int masked_irq) +static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq) { struct blk_dev_struct *bdev; ide_drive_t *drive; ide_hwif_t *hwif; - unsigned long io_flags; + ide_startstop_t startstop; + + ide_get_lock(&ide_lock, ide_intr, hwgroup); /* for atari only: POSSIBLY BROKEN HERE(?) */ - hwgroup->busy = 1; - while (hwgroup->handler == NULL) { - spin_lock_irqsave(&io_request_lock, io_flags); + while (!hwgroup->busy) { + hwgroup->busy = 1; drive = choose_drive(hwgroup); if (drive == NULL) { unsigned long sleep = 0; - hwgroup->rq = NULL; drive = hwgroup->drive; do { @@ -1140,24 +1170,31 @@ if (drive->sleep && (!sleep || 0 < (signed long)(sleep - drive->sleep))) sleep = drive->sleep; } while ((drive = drive->next) != hwgroup->drive); - spin_unlock_irqrestore(&io_request_lock, io_flags); if (sleep) { + /* + * Take a short snooze, and then wake up this hwgroup again. + * This gives other hwgroups on the same a chance to + * play fairly with us, just in case there are big differences + * in relative throughputs.. don't want to hog the cpu too much. + */ if (0 < (signed long)(jiffies + WAIT_MIN_SLEEP - sleep)) sleep = jiffies + WAIT_MIN_SLEEP; -#if 1 + hwgroup->sleeping = 1; /* so that ide_timer_expiry knows what to do */ +#if 1 /* paranoia */ if (hwgroup->timer.next || hwgroup->timer.prev) - printk("ide_set_handler: timer already active\n"); + printk("ide_set_handler: timer was already active\n"); #endif mod_timer(&hwgroup->timer, sleep); + /* we purposely leave hwgroup->busy==1 while sleeping */ } else { /* Ugly, but how can we sleep for the lock otherwise? perhaps from tq_scheduler? */ ide_release_lock(&ide_lock); /* for atari only */ + hwgroup->busy = 0; } - hwgroup->busy = 0; - return; + return; /* no more work for this hwgroup (for now) */ } hwif = HWIF(drive); - if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif) /* set nIEN for previous hwif */ + if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif) /* set nIEN for previous hwif */ OUT_BYTE(hwgroup->drive->ctl|2, hwgroup->hwif->io_ports[IDE_CONTROL_OFFSET]); hwgroup->hwif = hwif; hwgroup->drive = drive; @@ -1168,15 +1205,24 @@ if (bdev->current_request == &bdev->plug) /* FIXME: paranoia */ printk("%s: Huh? nuking plugged queue\n", drive->name); bdev->current_request = hwgroup->rq = drive->queue; - spin_unlock_irqrestore(&io_request_lock, io_flags); - + /* + * Some systems have trouble with IDE IRQs arriving while + * the driver is still setting things up. So, here we disable + * the IRQ used by this interface while the request is being started. + * This may look bad at first, but pretty much the same thing + * happens anyway when any interrupt comes in, IDE or otherwise + * -- the kernel masks the IRQ while it is being handled. + */ if (hwif->irq != masked_irq) disable_irq_nosync(hwif->irq); - spin_unlock_irqrestore(&hwgroup->spinlock, *hwgroup_flags); - start_request(drive); - spin_lock_irqsave(&hwgroup->spinlock, *hwgroup_flags); + spin_unlock(&io_request_lock); + ide__sti(); /* allow other IRQs while we start this request */ + startstop = start_request(drive); + spin_lock_irq(&io_request_lock); if (hwif->irq != masked_irq) enable_irq(hwif->irq); + if (startstop == ide_stopped) + hwgroup->busy = 0; } } @@ -1190,134 +1236,112 @@ return &hwif->drives[DEVICE_NR(dev) & 1].queue; } -/* - * do_hwgroup_request() invokes ide_do_request() after claiming hwgroup->busy. - */ -static void do_hwgroup_request (ide_hwgroup_t *hwgroup) -{ - unsigned long flags; - - spin_lock_irqsave(&hwgroup->spinlock, flags); - if (hwgroup->busy) { - spin_unlock_irqrestore(&hwgroup->spinlock, flags); - return; - } - del_timer(&hwgroup->timer); - ide_get_lock(&ide_lock, ide_intr, hwgroup); /* for atari only */ - ide_do_request(hwgroup, &flags, 0); - spin_unlock_irqrestore(&hwgroup->spinlock, flags); -} - -/* - * ll_rw_blk.c invokes our do_idex_request() function - * with the io_request_spinlock already grabbed. - * Since we need to do our own spinlock's internally, - * on paths that don't necessarily originate through the - * do_idex_request() path, we have to undo the spinlock on entry, - * and restore it again on exit. - * Fortunately, this is mostly a nop for non-SMP kernels. - */ -static inline void unlock_do_hwgroup_request (ide_hwgroup_t *hwgroup) -{ - spin_unlock(&io_request_lock); - do_hwgroup_request (hwgroup); - spin_lock_irq(&io_request_lock); -} - void do_ide0_request (void) { - unlock_do_hwgroup_request (ide_hwifs[0].hwgroup); + ide_do_request (ide_hwifs[0].hwgroup, 0); } #if MAX_HWIFS > 1 void do_ide1_request (void) { - unlock_do_hwgroup_request (ide_hwifs[1].hwgroup); + ide_do_request (ide_hwifs[1].hwgroup, 0); } #endif /* MAX_HWIFS > 1 */ #if MAX_HWIFS > 2 void do_ide2_request (void) { - unlock_do_hwgroup_request (ide_hwifs[2].hwgroup); + ide_do_request (ide_hwifs[2].hwgroup, 0); } #endif /* MAX_HWIFS > 2 */ #if MAX_HWIFS > 3 void do_ide3_request (void) { - unlock_do_hwgroup_request (ide_hwifs[3].hwgroup); + ide_do_request (ide_hwifs[3].hwgroup, 0); } #endif /* MAX_HWIFS > 3 */ #if MAX_HWIFS > 4 void do_ide4_request (void) { - unlock_do_hwgroup_request (ide_hwifs[4].hwgroup); + ide_do_request (ide_hwifs[4].hwgroup, 0); } #endif /* MAX_HWIFS > 4 */ #if MAX_HWIFS > 5 void do_ide5_request (void) { - unlock_do_hwgroup_request (ide_hwifs[5].hwgroup); + ide_do_request (ide_hwifs[5].hwgroup, 0); } #endif /* MAX_HWIFS > 5 */ -static void start_next_request (ide_hwgroup_t *hwgroup, int masked_irq) -{ - unsigned long flags; - ide_drive_t *drive; - - spin_lock_irqsave(&hwgroup->spinlock, flags); - if (hwgroup->handler != NULL) { - spin_unlock_irqrestore(&hwgroup->spinlock, flags); - return; - } - drive = hwgroup->drive; - set_recovery_timer(HWIF(drive)); - drive->service_time = jiffies - drive->service_start; - ide_do_request(hwgroup, &flags, masked_irq); - spin_unlock_irqrestore(&hwgroup->spinlock, flags); -} - +/* + * ide_timer_expiry() is our timeout function for all drive operations. + * But note that it can also be invoked as a result of a "sleep" operation + * triggered by the mod_timer() call in ide_do_request. + */ void ide_timer_expiry (unsigned long data) { - ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data; - ide_drive_t *drive; - ide_handler_t *handler; - unsigned long flags; + ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data; + ide_handler_t *handler; + unsigned long flags; - spin_lock_irqsave(&hwgroup->spinlock, flags); - drive = hwgroup->drive; + spin_lock_irqsave(&io_request_lock, flags); + del_timer(&hwgroup->timer); if ((handler = hwgroup->handler) == NULL) { - spin_unlock_irqrestore(&hwgroup->spinlock, flags); - do_hwgroup_request(hwgroup); - return; - } - hwgroup->busy = 1; /* should already be "1" */ - hwgroup->handler = NULL; - del_timer(&hwgroup->timer); /* Is this needed?? */ - if (hwgroup->poll_timeout != 0) { /* polling in progress? */ - spin_unlock_irqrestore(&hwgroup->spinlock, flags); - handler(drive); - } else if (drive_is_ready(drive)) { - printk("%s: lost interrupt\n", drive->name); - spin_unlock_irqrestore(&hwgroup->spinlock, flags); - handler(drive); + /* + * Either a marginal timeout occured (got the interrupt just as timer expired), + * or we were "sleeping" to give other devices a chance. Either way, we don't + * really want to complain about anything. + */ + if (hwgroup->sleeping) { + hwgroup->sleeping = 0; + hwgroup->busy = 0; + } } else { - if (drive->waiting_for_dma) { - (void) hwgroup->hwif->dmaproc(ide_dma_end, drive); - printk("%s: timeout waiting for DMA\n", drive->name); - /* - * need something here for HX PIIX3 UDMA and HPT343.......AMH - * irq timeout: status=0x58 { DriveReady SeekComplete DataRequest } - */ + ide_drive_t *drive = hwgroup->drive; + hwgroup->handler = NULL; + if (!drive) { + printk("ide_timer_expiry: hwgroup->drive was NULL\n"); + } else { + ide_hwif_t *hwif; + ide_startstop_t startstop; + if (!hwgroup->busy) { + hwgroup->busy = 1; /* paranoia */ + printk("%s: ide_timer_expiry: hwgroup->busy was 0 ??\n", drive->name); + } + /* + * We need to simulate a real interrupt when invoking the handler() + * function, which means we need to globally mask the specific IRQ: + */ + spin_unlock(&io_request_lock); + hwif = HWIF(drive); + disable_irq(hwif->irq); + __cli(); /* local CPU only, as if we were handling an interrupt */ + if (hwgroup->poll_timeout != 0 || drive_is_ready(drive)) { + if (hwgroup->poll_timeout == 0) { + printk("%s: lost interrupt\n", drive->name); + (void)ide_ack_intr(hwif->io_ports[IDE_STATUS_OFFSET], hwif->io_ports[IDE_IRQ_OFFSET]); + } + startstop = handler(drive); + } else { + if (drive->waiting_for_dma) { + (void) hwgroup->hwif->dmaproc(ide_dma_end, drive); + printk("%s: timeout waiting for DMA\n", drive->name); + } + startstop = ide_error(drive, "irq timeout", GET_STAT()); + } + set_recovery_timer(hwif); + drive->service_time = jiffies - drive->service_start; + enable_irq(hwif->irq); + spin_lock_irq(&io_request_lock); + if (startstop == ide_stopped) + hwgroup->busy = 0; } - spin_unlock_irqrestore(&hwgroup->spinlock, flags); - ide_error(drive, "irq timeout", GET_STAT()); } - start_next_request(hwgroup, 0); + ide_do_request(hwgroup, 0); + spin_unlock_irqrestore(&io_request_lock, flags); } /* @@ -1377,9 +1401,9 @@ ide_hwif_t *hwif; ide_drive_t *drive; ide_handler_t *handler; + ide_startstop_t startstop; - __cli(); /* local CPU only */ - spin_lock_irqsave(&hwgroup->spinlock, flags); + spin_lock_irqsave(&io_request_lock, flags); hwif = hwgroup->hwif; if ((handler = hwgroup->handler) == NULL || hwgroup->poll_timeout != 0) { /* @@ -1404,41 +1428,67 @@ */ (void)ide_ack_intr(hwif->io_ports[IDE_STATUS_OFFSET], hwif->io_ports[IDE_IRQ_OFFSET]); unexpected_intr(irq, hwgroup); + } +#ifdef CONFIG_BLK_DEV_IDEPCI + else + { + /* + * Whack the status register, just in case we have a leftover pending IRQ. + */ + (void)IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]); } - spin_unlock_irqrestore(&hwgroup->spinlock, flags); +#endif + spin_unlock_irqrestore(&io_request_lock, flags); return; } drive = hwgroup->drive; - if (!drive || !drive_is_ready(drive)) { - spin_unlock_irqrestore(&hwgroup->spinlock, flags); + if (!drive) { + /* + * This should NEVER happen, and there isn't much we could do about it here. + */ + spin_unlock_irqrestore(&io_request_lock, flags); + return; + } + if (!drive_is_ready(drive)) { + /* + * This happens regularly when we share a PCI IRQ with another device. + * Unfortunately, it can also happen with some buggy drives that trigger + * the IRQ before their status register is up to date. Hopefully we have + * enough advance overhead that the latter isn't a problem. + */ + spin_unlock_irqrestore(&io_request_lock, flags); return; } + if (!hwgroup->busy) { + hwgroup->busy = 1; /* paranoia */ + printk("%s: ide_intr: hwgroup->busy was 0 ??\n", drive->name); + } hwgroup->handler = NULL; + del_timer(&hwgroup->timer); (void)ide_ack_intr(hwif->io_ports[IDE_STATUS_OFFSET], hwif->io_ports[IDE_IRQ_OFFSET]); - del_timer(&(hwgroup->timer)); - { - struct request *rq; - unsigned long block, sectors; - - if ((rq = hwgroup->rq) != NULL) { - block = rq->sector; - block += drive->part[MINOR(rq->rq_dev)&PARTN_MASK].start_sect + drive->sect0; - sectors = drive->using_dma ? rq->nr_sectors : drive->mult_count ? drive->mult_count : 1; - } - } - - spin_unlock_irqrestore(&hwgroup->spinlock, flags); + spin_unlock(&io_request_lock); if (drive->unmask) ide__sti(); /* local CPU only */ - handler(drive); /* service this interrupt, may set handler for next interrupt */ + startstop = handler(drive); /* service this interrupt, may set handler for next interrupt */ + spin_lock_irq(&io_request_lock); /* * Note that handler() may have set things up for another * interrupt to occur soon, but it cannot happen until * we exit from this routine, because it will be the - * same irq as is currently being serviced here, - * and Linux won't allow another (on any CPU) until we return. + * same irq as is currently being serviced here, and Linux + * won't allow another of the same (on any CPU) until we return. */ - start_next_request(hwgroup, hwif->irq); + set_recovery_timer(HWIF(drive)); + drive->service_time = jiffies - drive->service_start; + if (startstop == ide_stopped) { + if (hwgroup->handler == NULL) { /* paranoia */ + hwgroup->busy = 0; + ide_do_request(hwgroup, hwif->irq); + } else { + printk("%s: ide_intr: huh? expected NULL handler on exit\n", drive->name); + } + } + spin_unlock_irqrestore(&io_request_lock, flags); } /* @@ -1521,7 +1571,6 @@ rq->rq_dev = MKDEV(major,(drive->select.b.unit)<sem = &sem; - spin_lock_irqsave(&io_request_lock, flags); cur_rq = drive->queue; if (cur_rq == NULL || action == ide_preempt) { @@ -1537,16 +1586,13 @@ rq->next = cur_rq->next; cur_rq->next = rq; } + ide_do_request(hwgroup, 0); spin_unlock_irqrestore(&io_request_lock, flags); - do_hwgroup_request(hwgroup); - save_flags(flags); /* all CPUs; overkill? */ - cli(); /* all CPUs; overkill? */ - if (action == ide_wait) - { - down(&sem); /* wait for it to be serviced */ + if (action == ide_wait) { + down(&sem); /* wait for it to be serviced */ + return rq->errors ? -EIO : 0; /* return -EIO if errors */ } - restore_flags(flags); /* all CPUs; overkill? */ - return rq->errors ? -EIO : 0; /* return -EIO if errors */ + return 0; } /* @@ -1569,14 +1615,14 @@ major = MAJOR(i_rdev); minor = drive->select.b.unit << PARTN_BITS; hwgroup = HWGROUP(drive); - spin_lock_irqsave(&hwgroup->spinlock, flags); + spin_lock_irqsave(&io_request_lock, flags); if (drive->busy || (drive->usage > 1)) { - spin_unlock_irqrestore(&hwgroup->spinlock, flags); + spin_unlock_irqrestore(&io_request_lock, flags); return -EBUSY; }; drive->busy = 1; MOD_INC_USE_COUNT; - spin_unlock_irqrestore(&hwgroup->spinlock, flags); + spin_unlock_irqrestore(&io_request_lock, flags); for (p = 0; p < (1<part[p].nr_sects > 0) { @@ -1962,7 +2008,7 @@ unsigned long flags; if ((setting->rw & SETTING_READ)) { - spin_lock_irqsave(&HWGROUP(drive)->spinlock, flags); + spin_lock_irqsave(&io_request_lock, flags); switch(setting->data_type) { case TYPE_BYTE: val = *((u8 *) setting->data); @@ -1975,7 +2021,7 @@ val = *((u32 *) setting->data); break; } - spin_unlock_irqrestore(&HWGROUP(drive)->spinlock, flags); + spin_unlock_irqrestore(&io_request_lock, flags); } return val; } @@ -1985,19 +2031,28 @@ ide_hwgroup_t *hwgroup = HWGROUP(drive); unsigned long timeout = jiffies + (3 * HZ); - spin_lock_irqsave(&hwgroup->spinlock, *flags); + spin_lock_irqsave(&io_request_lock, *flags); while (hwgroup->busy) { - spin_unlock_irqrestore(&hwgroup->spinlock, *flags); - __sti(); /* local CPU only; needed for jiffies */ + unsigned long lflags; + spin_unlock_irqrestore(&io_request_lock, *flags); + __save_flags(lflags); /* local CPU only */ + __sti(); /* local CPU only; needed for jiffies */ if (0 < (signed long)(jiffies - timeout)) { + __restore_flags(lflags); /* local CPU only */ printk("%s: channel busy\n", drive->name); return -EBUSY; } - spin_lock_irqsave(&hwgroup->spinlock, *flags); + __restore_flags(lflags); /* local CPU only */ + spin_lock_irqsave(&io_request_lock, *flags); } return 0; } +/* + * FIXME: This should be changed to enqueue a special request + * to the driver to change settings, and then wait on a sema for completion. + * The current scheme of polling is kludgey, though safe enough. + */ int ide_write_setting(ide_drive_t *drive, ide_settings_t *setting, int val) { unsigned long flags; @@ -2030,7 +2085,7 @@ *p = val; break; } - spin_unlock_irqrestore(&HWGROUP(drive)->spinlock, flags); + spin_unlock_irqrestore(&io_request_lock, flags); return 0; } @@ -2668,47 +2723,16 @@ if (!drive) return 0; - if (drive->forced_geom) { - /* - * Update the current 3D drive values. - */ - drive->id->cur_cyls = drive->bios_cyl; - drive->id->cur_heads = drive->bios_head; - drive->id->cur_sectors = drive->bios_sect; + if (drive->forced_geom) return 0; - } - if (xparm > 1 && xparm <= drive->bios_head && drive->bios_sect == 63) { - /* - * Update the current 3D drive values. - */ - drive->id->cur_cyls = drive->bios_cyl; - drive->id->cur_heads = drive->bios_head; - drive->id->cur_sectors = drive->bios_sect; + if (xparm > 1 && xparm <= drive->bios_head && drive->bios_sect == 63) return 0; /* we already have a translation */ - } printk("%s ", msg); - if (xparm < 0 && (drive->bios_cyl * drive->bios_head * drive->bios_sect) < (1024 * 16 * 63)) { - /* - * Update the current 3D drive values. - */ - drive->id->cur_cyls = drive->bios_cyl; - drive->id->cur_heads = drive->bios_head; - drive->id->cur_sectors = drive->bios_sect; + if (xparm < 0 && (drive->bios_cyl * drive->bios_head * drive->bios_sect) < (1024 * 16 * 63)) return 0; /* small disk: no translation needed */ - } - - if (drive->id) { - drive->cyl = drive->id->cyls; - drive->head = drive->id->heads; - drive->sect = drive->id->sectors; - } - drive->bios_cyl = drive->cyl; - drive->bios_head = drive->head; - drive->bios_sect = drive->sect; - drive->special.b.set_geometry = 1; tracks = drive->bios_cyl * drive->bios_head * drive->bios_sect / 63; drive->bios_sect = 63; @@ -2736,12 +2760,7 @@ } drive->part[0].nr_sects = current_capacity(drive); printk("[%d/%d/%d]", drive->bios_cyl, drive->bios_head, drive->bios_sect); - /* - * Update the current 3D drive values. - */ - drive->id->cur_cyls = drive->bios_cyl; - drive->id->cur_heads = drive->bios_head; - drive->id->cur_sectors = drive->bios_sect; + return 1; } @@ -2844,9 +2863,10 @@ return ide_unregister_subdriver(drive); } -static void default_do_request(ide_drive_t *drive, struct request *rq, unsigned long block) +static ide_startstop_t default_do_request(ide_drive_t *drive, struct request *rq, unsigned long block) { ide_end_request(0, HWGROUP(drive)); + return ide_stopped; } static void default_end_request (byte uptodate, ide_hwgroup_t *hwgroup) @@ -2884,12 +2904,13 @@ return 0x7fffffff; /* cdrom or tape */ } -static void default_special (ide_drive_t *drive) +static ide_startstop_t default_special (ide_drive_t *drive) { special_t *s = &drive->special; s->all = 0; drive->mult_req = 0; + return ide_stopped; } static void setup_driver_defaults (ide_drive_t *drive) diff -u --recursive --new-file v2.2.13/linux/drivers/block/ide.h linux/drivers/block/ide.h --- v2.2.13/linux/drivers/block/ide.h Tue May 11 10:35:52 1999 +++ linux/drivers/block/ide.h Tue Jan 4 10:12:14 2000 @@ -353,14 +353,22 @@ } ide_hwif_t; /* + * Status returned from various ide_ functions + */ +typedef enum { + ide_stopped, /* no drive operation was started */ + ide_started /* a drive operation was started, and a handler was set up */ +} ide_startstop_t; + +/* * internal ide interrupt handler type */ -typedef void (ide_handler_t)(ide_drive_t *); +typedef ide_startstop_t (ide_handler_t)(ide_drive_t *); typedef struct hwgroup_s { - spinlock_t spinlock; /* protects "busy" and "handler" */ ide_handler_t *handler;/* irq handler, if active */ - int busy; /* BOOL: protects all fields below */ + volatile int busy; /* BOOL: protects all fields below */ + int sleeping; /* BOOL: wake us up on timer expiry */ ide_drive_t *drive; /* current drive */ ide_hwif_t *hwif; /* ptr to current hwif in linked-list */ struct request *rq; /* current request */ @@ -443,13 +451,14 @@ #define PROC_IDE_READ_RETURN(page,start,off,count,eof,len) return 0; #endif + /* * Subdrivers support. */ #define IDE_SUBDRIVER_VERSION 1 typedef int (ide_cleanup_proc)(ide_drive_t *); -typedef void (ide_do_request_proc)(ide_drive_t *, struct request *, unsigned long); +typedef ide_startstop_t (ide_do_request_proc)(ide_drive_t *, struct request *, unsigned long); typedef void (ide_end_request_proc)(byte, ide_hwgroup_t *); typedef int (ide_ioctl_proc)(ide_drive_t *, struct inode *, struct file *, unsigned int, unsigned long); typedef int (ide_open_proc)(struct inode *, struct file *, ide_drive_t *); @@ -457,7 +466,7 @@ typedef int (ide_check_media_change_proc)(ide_drive_t *); typedef void (ide_pre_reset_proc)(ide_drive_t *); typedef unsigned long (ide_capacity_proc)(ide_drive_t *); -typedef void (ide_special_proc)(ide_drive_t *); +typedef ide_startstop_t (ide_special_proc)(ide_drive_t *); typedef void (ide_setting_proc)(ide_drive_t *); typedef struct ide_driver_s { @@ -545,9 +554,9 @@ /* * ide_error() takes action based on the error returned by the controller. - * The calling function must return afterwards, to restart the request. + * The caller should return immediately after invoking this. */ -void ide_error (ide_drive_t *drive, const char *msg, byte stat); +ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, byte stat); /* * Issue a simple drive command @@ -567,10 +576,11 @@ * This routine busy-waits for the drive status to be not "busy". * It then checks the status for all of the "good" bits and none * of the "bad" bits, and if all is okay it returns 0. All other - * cases return 1 after invoking ide_error() -- caller should return. - * + * cases return 1 after doing "*startstop = ide_error()", and the + * caller should return the updated value of "startstop" in this case. + * "startstop" is unchanged when the function returns 0; */ -int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout); +int ide_wait_stat (ide_startstop_t *startstop, ide_drive_t *drive, byte good, byte bad, unsigned long timeout); /* * This routine is called from the partition-table code in genhd.c @@ -594,7 +604,7 @@ * Start a reset operation for an IDE interface. * The caller should return immediately after invoking this. */ -void ide_do_reset (ide_drive_t *); +ide_startstop_t ide_do_reset (ide_drive_t *); /* * This function is intended to be used prior to invoking ide_do_drive_cmd(). @@ -661,7 +671,7 @@ * ide_multwrite() transfers a block of up to mcount sectors of data * to a drive as part of a disk multwrite operation. */ -void ide_multwrite (ide_drive_t *drive, unsigned int mcount); +int ide_multwrite (ide_drive_t *drive, unsigned int mcount); /* * ide_stall_queue() can be used by a drive to give excess bandwidth back @@ -751,7 +761,7 @@ #define BAD_DMA_DRIVE 0 #define GOOD_DMA_DRIVE 1 int ide_build_dmatable (ide_drive_t *drive); -void ide_dma_intr (ide_drive_t *drive); +ide_startstop_t ide_dma_intr (ide_drive_t *drive); int check_drive_lists (ide_drive_t *drive, int good_bad); int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive); int ide_release_dma (ide_hwif_t *hwif); diff -u --recursive --new-file v2.2.13/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c --- v2.2.13/linux/drivers/block/ll_rw_blk.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/block/ll_rw_blk.c Tue Jan 4 10:12:14 2000 @@ -422,7 +422,6 @@ if (buffer_locked(bh)) return; /* Maybe the above fixes it, and maybe it doesn't boot. Life is interesting */ - lock_buffer(bh); if (blk_size[major]) { @@ -512,6 +511,8 @@ case IDE5_MAJOR: case ACSI_MAJOR: case MFM_ACORN_MAJOR: + case MDISK_MAJOR: + case DASD_MAJOR: /* * The scsi disk and cdrom drivers completely remove the request * from the queue when they start processing an entry. For this @@ -657,7 +658,6 @@ kdevname(bh[0]->b_dev), bh[0]->b_blocknr); goto sorry; } - /* Determine correct block size for this device. */ correct_size = BLOCK_SIZE; if (blksize_size[major]) { @@ -896,6 +896,12 @@ #endif #ifdef CONFIG_BLK_DEV_NBD nbd_init(); +#endif +#ifdef CONFIG_MDISK + mdisk_init(); +#endif +#ifdef CONFIG_DASD + dasd_init(); #endif return 0; }; diff -u --recursive --new-file v2.2.13/linux/drivers/block/loop.c linux/drivers/block/loop.c --- v2.2.13/linux/drivers/block/loop.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/block/loop.c Tue Jan 4 10:12:14 2000 @@ -315,7 +315,7 @@ /* Do what the default llseek() code would have done */ file->f_pos = new_offset; file->f_reada = 0; - file->f_version = ++event; + file->f_version = ++global_event; } if (file->f_op->write == NULL) { diff -u --recursive --new-file v2.2.13/linux/drivers/block/ns87415.c linux/drivers/block/ns87415.c --- v2.2.13/linux/drivers/block/ns87415.c Sat May 29 11:10:15 1999 +++ linux/drivers/block/ns87415.c Tue Jan 4 10:12:14 2000 @@ -97,6 +97,10 @@ return 0; ns87415_prepare_drive(drive, 0); /* DMA failed: select PIO xfer */ return 1; + case ide_dma_check: + if (drive->media != ide_disk) + return ide_dmaproc(ide_dma_off_quietly, drive); + /* Fallthrough... */ default: return ide_dmaproc(func, drive); /* use standard DMA stuff */ } diff -u --recursive --new-file v2.2.13/linux/drivers/block/pdc4030.c linux/drivers/block/pdc4030.c --- v2.2.13/linux/drivers/block/pdc4030.c Tue Dec 29 11:24:57 1998 +++ linux/drivers/block/pdc4030.c Tue Jan 4 10:12:14 2000 @@ -122,6 +122,7 @@ */ int init_pdc4030 (void) { + ide_startstop_t startstop; ide_hwif_t *hwif = hwif_required; ide_drive_t *drive; ide_hwif_t *second_hwif; @@ -143,7 +144,7 @@ if(pdc4030_cmd(drive,PROMISE_GET_CONFIG)) { return 0; } - if(ide_wait_stat(drive,DATA_READY,BAD_W_STAT,WAIT_DRQ)) { + if(ide_wait_stat(&startstop,drive,DATA_READY,BAD_W_STAT,WAIT_DRQ)) { printk("%s: Failed Promise read config!\n",hwif->name); return 0; } @@ -195,7 +196,7 @@ /* * promise_read_intr() is the handler for disk read/multread interrupts */ -static void promise_read_intr (ide_drive_t *drive) +static ide_startstop_t promise_read_intr (ide_drive_t *drive) { byte stat; int i; @@ -203,8 +204,7 @@ struct request *rq; if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) { - ide_error(drive, "promise_read_intr", stat); - return; + return ide_error(drive, "promise_read_intr", stat); } read_again: @@ -240,17 +240,18 @@ goto read_again; if(stat & BUSY_STAT) { ide_set_handler (drive, &promise_read_intr, WAIT_CMD); - return; + return ide_started;; } printk("Ah! promise read intr: sectors left !DRQ !BUSY\n"); - ide_error(drive, "promise read intr", stat); + return ide_error(drive, "promise read intr", stat); } + return ide_stopped; } /* * promise_write_pollfunc() is the handler for disk write completion polling. */ -static void promise_write_pollfunc (ide_drive_t *drive) +static ide_startstop_t promise_write_pollfunc (ide_drive_t *drive) { int i; ide_hwgroup_t *hwgroup = HWGROUP(drive); @@ -259,20 +260,20 @@ if (IN_BYTE(IDE_NSECTOR_REG) != 0) { if (time_before(jiffies, hwgroup->poll_timeout)) { ide_set_handler (drive, &promise_write_pollfunc, 1); - return; /* continue polling... */ + return ide_started; /* continue polling... */ } printk("%s: write timed-out!\n",drive->name); - ide_error (drive, "write timeout", GET_STAT()); - return; + return ide_error (drive, "write timeout", GET_STAT()); } - ide_multwrite(drive, 4); + if (ide_multwrite(drive, 4)) + return ide_stopped; rq = hwgroup->rq; for (i = rq->nr_sectors; i > 0;) { i -= rq->current_nr_sectors; ide_end_request(1, hwgroup); } - return; + return ide_stopped; } /* @@ -283,24 +284,27 @@ * how it's done in the drivers for other O/Ses. There is no interrupt * generated on writes, which is why we have to do it like this. */ -static void promise_write (ide_drive_t *drive) +static ide_startstop_t promise_write (ide_drive_t *drive) { ide_hwgroup_t *hwgroup = HWGROUP(drive); struct request *rq = &hwgroup->wrq; int i; if (rq->nr_sectors > 4) { - ide_multwrite(drive, rq->nr_sectors - 4); + if (ide_multwrite(drive, rq->nr_sectors - 4)) + return ide_stopped; hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; ide_set_handler (drive, &promise_write_pollfunc, 1); - return; + return ide_started; } else { - ide_multwrite(drive, rq->nr_sectors); + if (ide_multwrite(drive, rq->nr_sectors)) + return ide_stopped; rq = hwgroup->rq; for (i = rq->nr_sectors; i > 0;) { i -= rq->current_nr_sectors; ide_end_request(1, hwgroup); } + return ide_stopped; } } @@ -309,7 +313,7 @@ * already set up. It issues a READ or WRITE command to the Promise * controller, assuming LBA has been used to set up the block number. */ -void do_pdc4030_io (ide_drive_t *drive, struct request *rq) +ide_startstop_t do_pdc4030_io (ide_drive_t *drive, struct request *rq) { unsigned long timeout; byte stat; @@ -330,28 +334,29 @@ disable_irq(HWIF(drive)->irq); ide_intr(HWIF(drive)->irq,HWGROUP(drive),NULL); enable_irq(HWIF(drive)->irq); - return; + return ide_stopped; } if(IN_BYTE(IDE_SELECT_REG) & 0x01) - return; + return ide_started; udelay(1); } while (time_before(jiffies, timeout)); printk("%s: reading: No DRQ and not waiting - Odd!\n", drive->name); - return; + return ide_started; } if (rq->cmd == WRITE) { + ide_startstop_t startstop; OUT_BYTE(PROMISE_WRITE, IDE_COMMAND_REG); - if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { + if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { printk("%s: no DRQ after issuing PROMISE_WRITE\n", drive->name); - return; + return startstop; } if (!drive->unmask) __cli(); /* local CPU only */ HWGROUP(drive)->wrq = *rq; /* scratchpad */ - promise_write(drive); - return; + return promise_write(drive); } printk("%s: bad command: %d\n", drive->name, rq->cmd); ide_end_request(0, HWGROUP(drive)); + return ide_stopped; } diff -u --recursive --new-file v2.2.13/linux/drivers/block/raid0.c linux/drivers/block/raid0.c --- v2.2.13/linux/drivers/block/raid0.c Fri May 8 00:17:13 1998 +++ linux/drivers/block/raid0.c Tue Jan 4 10:12:14 2000 @@ -182,6 +182,12 @@ block=*rsector >> 1; hash=data->hash_table+(block/data->smallest->size); + if (hash - data->hash_table > data->nr_zones) + { + printk(KERN_DEBUG "raid0_map: invalid block %ul\n", block); + return -1; + } + /* Sanity check */ if ((chunk_size*2)<(*rsector % (chunk_size*2))+size) { diff -u --recursive --new-file v2.2.13/linux/drivers/block/rd.c linux/drivers/block/rd.c --- v2.2.13/linux/drivers/block/rd.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/block/rd.c Tue Jan 4 10:12:14 2000 @@ -112,7 +112,11 @@ * architecture-specific setup routine (from the stored boot sector * information). */ +#ifdef CONFIG_ARCH_S390 +int rd_size = 8192; /* Size of the RAM disks */ +#else int rd_size = 4096; /* Size of the RAM disks */ +#endif #ifndef MODULE int rd_doload = 0; /* 1 = load RAM disk, 0 = don't load */ @@ -191,7 +195,10 @@ switch (cmd) { case BLKFLSBUF: if (!capable(CAP_SYS_ADMIN)) return -EACCES; - invalidate_buffers(inode->i_rdev); + /* special: we want to release the ramdisk memory, + it's not like with the other blockdevices where + this ioctl only flushes away the buffer cache. */ + destroy_buffers(inode->i_rdev); break; case BLKGETSIZE: /* Return device size */ @@ -347,7 +354,7 @@ int i; for (i = 0 ; i < NUM_RAMDISKS; i++) - invalidate_buffers(MKDEV(MAJOR_NR, i)); + destroy_buffers(MKDEV(MAJOR_NR, i)); unregister_blkdev( MAJOR_NR, "ramdisk" ); blk_dev[MAJOR_NR].request_fn = 0; @@ -564,10 +571,12 @@ } infile.f_op->read(&infile, buf, BLOCK_SIZE, &infile.f_pos); outfile.f_op->write(&outfile, buf, BLOCK_SIZE, &outfile.f_pos); +#ifndef CONFIG_ARCH_S390 if (!(i % 16)) { printk("%c\b", rotator[rotate & 0x3]); rotate++; } +#endif } printk("done.\n"); kfree(buf); diff -u --recursive --new-file v2.2.13/linux/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c --- v2.2.13/linux/drivers/cdrom/cdrom.c Tue Jan 4 11:10:33 2000 +++ linux/drivers/cdrom/cdrom.c Tue Jan 4 10:12:14 2000 @@ -842,12 +842,13 @@ case CDROM_LOCKDOOR: { cdinfo(CD_DO_IOCTL, "%socking door.\n",arg?"L":"Unl"); - if (!CDROM_CAN(CDC_LOCK)) { + if (!CDROM_CAN(CDC_LOCK)) return -EDRIVE_CANT_DO_THIS; - } else { - keeplocked = arg ? 1 : 0; - return cdo->lock_door(cdi, arg); - } + keeplocked = arg ? 1 : 0; + /* don't unlock the door on multiple opens */ + if ((cdi->use_count != 1) && !arg) + return -EBUSY; + return cdo->lock_door(cdi, arg); } case CDROM_DEBUG: { diff -u --recursive --new-file v2.2.13/linux/drivers/char/Config.in linux/drivers/char/Config.in --- v2.2.13/linux/drivers/char/Config.in Tue Jan 4 11:10:33 2000 +++ linux/drivers/char/Config.in Tue Jan 4 10:12:14 2000 @@ -22,30 +22,32 @@ fi bool 'Non-standard serial port support' CONFIG_SERIAL_NONSTANDARD if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then + tristate 'Computone IntelliPort Plus serial support' CONFIG_COMPUTONE tristate 'Comtrol Rocketport support' CONFIG_ROCKETPORT - tristate 'Digiboard Intelligent Async Support' CONFIG_DIGIEPCA - if [ "$CONFIG_DIGIEPCA" = "n" ]; then - tristate 'Digiboard PC/Xx Support' CONFIG_DIGI - fi tristate 'Cyclades async mux support' CONFIG_CYCLADES if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_CYCLADES" != "n" ]; then bool ' Cyclades-Z interrupt mode operation (EXPERIMENTAL)' CONFIG_CYZ_INTR fi - bool 'Stallion multiport serial support' CONFIG_STALDRV - if [ "$CONFIG_STALDRV" = "y" ]; then - tristate ' Stallion EasyIO or EC8/32 support' CONFIG_STALLION - tristate ' Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION + tristate 'Digiboard Intelligent Async Support' CONFIG_DIGIEPCA + if [ "$CONFIG_DIGIEPCA" = "n" ]; then + tristate 'Digiboard PC/Xx Support' CONFIG_DIGI + fi + tristate 'Hayes ESP serial port support' CONFIG_ESPSERIAL + tristate 'Moxa Intellio support' CONFIG_MOXA_INTELLIO + tristate 'Moxa SmartIO support' CONFIG_MOXA_SMARTIO + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate 'Multi-Tech multiport card support' CONFIG_ISI m fi tristate 'SDL RISCom/8 card support' CONFIG_RISCOM8 - tristate 'Computone IntelliPort Plus serial support' CONFIG_COMPUTONE tristate 'Specialix IO8+ card support' CONFIG_SPECIALIX if [ "$CONFIG_SPECIALIX" != "n" ]; then bool 'Specialix DTR/RTS pin is RTS' CONFIG_SPECIALIX_RTSCTS fi tristate 'Specialix SX (and SI) card support' CONFIG_SX - tristate 'Hayes ESP serial port support' CONFIG_ESPSERIAL - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - dep_tristate 'Multi-Tech multiport card support' CONFIG_ISI m + bool 'Stallion multiport serial support' CONFIG_STALDRV + if [ "$CONFIG_STALDRV" = "y" ]; then + tristate ' Stallion EasyIO or EC8/32 support' CONFIG_STALLION + tristate ' Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION fi dep_tristate 'Microgate SyncLink card support' CONFIG_SYNCLINK m dep_tristate 'HDLC line discipline support' CONFIG_N_HDLC m @@ -74,6 +76,8 @@ endmenu fi +source drivers/char/joystick/Config.in + tristate 'QIC-02 tape support' CONFIG_QIC02_TAPE if [ "$CONFIG_QIC02_TAPE" != "n" ]; then bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF @@ -101,6 +105,7 @@ tristate ' Software Watchdog' CONFIG_SOFT_WATCHDOG tristate ' Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG tristate ' Acquire SBC Watchdog Timer' CONFIG_ACQUIRE_WDT + tristate ' Mixcom Watchdog' CONFIG_MIXCOMWD endmenu fi @@ -178,15 +183,6 @@ #dep_tristate ' Include support for LML33' CONFIG_VIDEO_LML33 $CONFIG_VIDEO_ZORAN fi -endmenu - -mainmenu_option next_comment -comment 'Joystick support' - -tristate 'Joystick support' CONFIG_JOYSTICK -if [ "$CONFIG_JOYSTICK" != "n" ]; then - source drivers/char/joystick/Config.in -fi endmenu tristate 'Double Talk PC internal speech card support' CONFIG_DTLK diff -u --recursive --new-file v2.2.13/linux/drivers/char/Makefile linux/drivers/char/Makefile --- v2.2.13/linux/drivers/char/Makefile Tue Jan 4 11:10:33 2000 +++ linux/drivers/char/Makefile Tue Jan 4 10:12:14 2000 @@ -59,8 +59,10 @@ LX_OBJS += keyboard.o endif ifneq ($(ARCH),m68k) + ifneq ($(ARCH),s390) L_OBJS += pc_keyb.o defkeymap.o endif + endif else ifdef CONFIG_PCI L_OBJS += defkeymap.o @@ -92,6 +94,22 @@ endif endif +ifeq ($(CONFIG_MOXA_SMARTIO),y) +L_OBJS += mxser.o +else + ifeq ($(CONFIG_MOXA_SMARTIO),m) + M_OBJS += mxser.o + endif +endif + +ifeq ($(CONFIG_MOXA_INTELLIO),y) +L_OBJS += moxa.o +else + ifeq ($(CONFIG_MOXA_INTELLIO),m) + M_OBJS += moxa.o + endif +endif + ifeq ($(CONFIG_DIGI),y) L_OBJS += pcxx.o else @@ -270,6 +288,14 @@ endif endif +ifeq ($(CONFIG_MIXCOMWD),y) +L_OBJS += mixcomwd.o +else + ifeq ($(CONFIG_MIXCOMWD),m) + M_OBJS += mixcomwd.o + endif +endif + ifeq ($(CONFIG_AMIGAMOUSE),y) L_OBJS += amigamouse.o else @@ -343,11 +369,21 @@ endif ifeq ($(CONFIG_VIDEO_BT848),y) -L_OBJS += bttv.o msp3400.o tuner.o +L_OBJS += bttv.o tuner.o L_I2C=y else ifeq ($(CONFIG_VIDEO_BT848),m) - M_OBJS += bttv.o msp3400.o tuner.o + M_OBJS += bttv.o tuner.o + M_I2C=y + endif +endif + +ifeq ($(CONFIG_VIDEO_MSP3400),y) +L_OBJS += msp3400.o +L_I2C=y +else + ifeq ($(CONFIG_VIDEO_MSP3400),m) + M_OBJS += msp3400.o M_I2C=y endif endif @@ -380,9 +416,11 @@ ifeq ($(CONFIG_VIDEO_ZORAN),y) L_OBJS += buz.o +L_I2C=y else ifeq ($(CONFIG_VIDEO_ZORAN),m) M_OBJS += buz.o + M_I2C=y endif endif diff -u --recursive --new-file v2.2.13/linux/drivers/char/acquirewdt.c linux/drivers/char/acquirewdt.c --- v2.2.13/linux/drivers/char/acquirewdt.c Wed Jun 2 11:29:28 1999 +++ linux/drivers/char/acquirewdt.c Tue Jan 4 10:12:14 2000 @@ -219,7 +219,7 @@ misc_register(&acq_miscdev); request_region(WDT_STOP, 1, "Acquire WDT"); request_region(WDT_START, 1, "Acquire WDT"); - unregister_reboot_notifier(&acq_notifier); + register_reboot_notifier(&acq_notifier); return 0; } diff -u --recursive --new-file v2.2.13/linux/drivers/char/bttv.c linux/drivers/char/bttv.c --- v2.2.13/linux/drivers/char/bttv.c Tue Jan 4 11:10:33 2000 +++ linux/drivers/char/bttv.c Tue Jan 4 10:12:14 2000 @@ -403,7 +403,7 @@ } } -void attach_inform(struct i2c_bus *bus, int id) +static void attach_inform(struct i2c_bus *bus, int id) { struct bttv *btv = (struct bttv*)bus->data; @@ -422,7 +422,7 @@ } } -void detach_inform(struct i2c_bus *bus, int id) +static void detach_inform(struct i2c_bus *bus, int id) { struct bttv *btv = (struct bttv*)bus->data; diff -u --recursive --new-file v2.2.13/linux/drivers/char/busmouse.c linux/drivers/char/busmouse.c --- v2.2.13/linux/drivers/char/busmouse.c Wed Dec 16 13:38:18 1998 +++ linux/drivers/char/busmouse.c Tue Jan 4 10:12:14 2000 @@ -210,7 +210,8 @@ buttons = mouse.buttons; mouse.dx -= dx; mouse.dy -= dy; - mouse.ready = 0; + if(mouse.dx==0 && mouse.dy==0) + mouse.ready = 0; enable_irq(mouse_irq); /* restore_flags(flags); */ diff -u --recursive --new-file v2.2.13/linux/drivers/char/buz.c linux/drivers/char/buz.c --- v2.2.13/linux/drivers/char/buz.c Tue Jan 4 11:10:33 2000 +++ linux/drivers/char/buz.c Tue Jan 4 10:12:14 2000 @@ -374,13 +374,13 @@ return (btread(ZR36057_I2CBR) >> 1) & 1; } -void attach_inform(struct i2c_bus *bus, int id) +static void attach_inform(struct i2c_bus *bus, int id) { DEBUG(struct zoran *zr = (struct zoran *) bus->data); DEBUG(printk(BUZ_DEBUG "-%u: i2c attach %02x\n", zr->id, id)); } -void detach_inform(struct i2c_bus *bus, int id) +static void detach_inform(struct i2c_bus *bus, int id) { DEBUG(struct zoran *zr = (struct zoran *) bus->data); DEBUG(printk(BUZ_DEBUG "-%u: i2c detach %02x\n", zr->id, id)); diff -u --recursive --new-file v2.2.13/linux/drivers/char/console.c linux/drivers/char/console.c --- v2.2.13/linux/drivers/char/console.c Tue Jan 4 11:10:33 2000 +++ linux/drivers/char/console.c Tue Jan 4 10:12:14 2000 @@ -2820,6 +2820,7 @@ EXPORT_SYMBOL(video_font_height); EXPORT_SYMBOL(video_scan_lines); EXPORT_SYMBOL(vc_resize); +EXPORT_SYMBOL(fg_console); #ifndef VT_SINGLE_DRIVER EXPORT_SYMBOL(take_over_console); diff -u --recursive --new-file v2.2.13/linux/drivers/char/hfmodem/refclock.c linux/drivers/char/hfmodem/refclock.c --- v2.2.13/linux/drivers/char/hfmodem/refclock.c Sat Jul 18 11:47:49 1998 +++ linux/drivers/char/hfmodem/refclock.c Tue Jan 4 10:12:14 2000 @@ -127,14 +127,13 @@ #ifdef __i386__ if (rdtsc_ok) { - unsigned int tmp0, tmp1; - unsigned int tmp2, tmp3; + unsigned int tmp0, tmp1, tmp2, tmp3, tmp4; __asm__("rdtsc;\n\t" "subl %2,%%eax\n\t" "sbbl %3,%%edx\n\t" : "=&a" (tmp0), "=&d" (tmp1) - : "m" (dev->clk.starttime_lo), "m" (dev->clk.starttime_hi) : "ax", "dx"); - __asm__("mull %1" : "=d" (tmp2) : "m" (scale_rdtsc), "a" (tmp0) : "ax"); + : "m" (dev->clk.starttime_lo), "m" (dev->clk.starttime_hi)); + __asm__("mull %2" : "=d" (tmp2), "=a" (tmp4) : "m" (scale_rdtsc), "1" (tmp0) : "ax"); __asm__("mull %1" : "=a" (tmp3) : "m" (scale_rdtsc), "a" (tmp1) : "dx"); curtime = tmp2 + tmp3; goto time_known; diff -u --recursive --new-file v2.2.13/linux/drivers/char/i2c.c linux/drivers/char/i2c.c --- v2.2.13/linux/drivers/char/i2c.c Tue Oct 19 17:10:37 1999 +++ linux/drivers/char/i2c.c Tue Jan 4 10:12:14 2000 @@ -56,6 +56,8 @@ /* anything to do here ? */ #ifdef CONFIG_VIDEO_BT848 i2c_tuner_init(); +#endif +#ifdef CONFIG_VIDEO_MSP3400 msp3400c_init(); #endif #ifdef CONFIG_VIDEO_BUZ diff -u --recursive --new-file v2.2.13/linux/drivers/char/joystick/Config.in linux/drivers/char/joystick/Config.in --- v2.2.13/linux/drivers/char/joystick/Config.in Wed Dec 16 13:38:18 1998 +++ linux/drivers/char/joystick/Config.in Tue Jan 4 10:12:14 2000 @@ -1,19 +1,34 @@ # -# Joystick lowlevel driver configuration +# Joystick driver # -dep_tristate ' Classic PC analog joysticks and gamepads' CONFIG_JOY_ANALOG $CONFIG_JOYSTICK -dep_tristate ' FPGaming and MadCatz A3D controllers' CONFIG_JOY_ASSASIN $CONFIG_JOYSTICK -dep_tristate ' Gravis GrIP joysticks and gamepads' CONFIG_JOY_GRAVIS $CONFIG_JOYSTICK -dep_tristate ' Logitech Digital joysticks and gamepads' CONFIG_JOY_LOGITECH $CONFIG_JOYSTICK -dep_tristate ' Microsoft SideWinder, Genius Digital joysticks and gamepads' CONFIG_JOY_SIDEWINDER $CONFIG_JOYSTICK -dep_tristate ' ThrustMaster DirectConnect joysticks and gamepads' CONFIG_JOY_THRUSTMASTER $CONFIG_JOYSTICK -dep_tristate ' PDPI Lightning 4 gamecards' CONFIG_JOY_LIGHTNING $CONFIG_JOYSTICK -if [ "$CONFIG_PARPORT" != "n" ]; then - dep_tristate ' NES, SNES, PSX, Multisystem joysticks and gamepads' CONFIG_JOY_CONSOLE $CONFIG_JOYSTICK $CONFIG_PARPORT - dep_tristate ' Sega, Multisystem joysticks and gamepads' CONFIG_JOY_DB9 $CONFIG_JOYSTICK $CONFIG_PARPORT - dep_tristate ' TurboGraFX Multisystem joystick interface' CONFIG_JOY_TURBOGRAFX $CONFIG_JOYSTICK $CONFIG_PARPORT -fi -if [ "$CONFIG_AMIGA" = "y" ]; then - dep_tristate ' Amiga joysticks' CONFIG_JOY_AMIGA $CONFIG_JOYSTICK +mainmenu_option next_comment +comment 'Joysticks' + +tristate 'Joystick support' CONFIG_JOYSTICK + +if [ "$CONFIG_JOYSTICK" != "n" ]; then + dep_tristate ' Classic PC analog' CONFIG_JOY_ANALOG $CONFIG_JOYSTICK + dep_tristate ' FPGaming and MadCatz A3D' CONFIG_JOY_ASSASSIN $CONFIG_JOYSTICK + dep_tristate ' Gravis GrIP' CONFIG_JOY_GRAVIS $CONFIG_JOYSTICK + dep_tristate ' Logitech ADI' CONFIG_JOY_LOGITECH $CONFIG_JOYSTICK + dep_tristate ' Microsoft SideWinder' CONFIG_JOY_SIDEWINDER $CONFIG_JOYSTICK + dep_tristate ' ThrustMaster DirectConnect' CONFIG_JOY_THRUSTMASTER $CONFIG_JOYSTICK + dep_tristate ' Creative Labs Blaster' CONFIG_JOY_CREATIVE $CONFIG_JOYSTICK + dep_tristate ' PDPI Lightning 4 card' CONFIG_JOY_LIGHTNING $CONFIG_JOYSTICK + dep_tristate ' Trident 4DWave and Aureal Vortex gameport' CONFIG_JOY_PCI $CONFIG_JOYSTICK + dep_tristate ' Magellan and Space Mouse' CONFIG_JOY_MAGELLAN $CONFIG_JOYSTICK + dep_tristate ' SpaceTec SpaceOrb 360 and SpaceBall Avenger' CONFIG_JOY_SPACEORB $CONFIG_JOYSTICK + dep_tristate ' SpaceTec SpaceBall 4000 FLX' CONFIG_JOY_SPACEBALL $CONFIG_JOYSTICK + dep_tristate ' Logitech WingMan Warrior' CONFIG_JOY_WARRIOR $CONFIG_JOYSTICK + if [ "$CONFIG_PARPORT" != "n" ]; then + dep_tristate ' NES, SNES, PSX, N64, Multi' CONFIG_JOY_CONSOLE $CONFIG_JOYSTICK $CONFIG_PARPORT + dep_tristate ' Sega, Multi' CONFIG_JOY_DB9 $CONFIG_JOYSTICK $CONFIG_PARPORT + dep_tristate ' TurboGraFX interface' CONFIG_JOY_TURBOGRAFX $CONFIG_JOYSTICK $CONFIG_PARPORT + fi + if [ "$CONFIG_AMIGA" = "y" ]; then + dep_tristate ' Amiga joysticks' CONFIG_JOY_AMIGA $CONFIG_JOYSTICK + fi fi + +endmenu diff -u --recursive --new-file v2.2.13/linux/drivers/char/joystick/Makefile linux/drivers/char/joystick/Makefile --- v2.2.13/linux/drivers/char/joystick/Makefile Thu Oct 8 17:25:16 1998 +++ linux/drivers/char/joystick/Makefile Tue Jan 4 10:12:14 2000 @@ -1,23 +1,18 @@ # # Makefile for the joystick drivers. # -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). -# -# Note 2! The CFLAGS definitions are now inherited from the -# parent makes.. -# O_TARGET := js.o +OX_OBJS := O_OBJS := +MX_OBJS := M_OBJS := ifeq ($(CONFIG_JOYSTICK),y) -O_OBJS += joystick.o +OX_OBJS += joystick.o else ifeq ($(CONFIG_JOYSTICK),m) - M_OBJS += joystick.o + MX_OBJS += joystick.o endif endif @@ -37,11 +32,11 @@ endif endif -ifeq ($(CONFIG_JOY_ASSASIN),y) -O_OBJS += joy-assasin.o +ifeq ($(CONFIG_JOY_ASSASSIN),y) +O_OBJS += joy-assassin.o else - ifeq ($(CONFIG_JOY_ASSASIN),m) - M_OBJS += joy-assasin.o + ifeq ($(CONFIG_JOY_ASSASSIN),m) + M_OBJS += joy-assassin.o endif endif @@ -53,6 +48,14 @@ endif endif +ifeq ($(CONFIG_JOY_CREATIVE),y) +O_OBJS += joy-creative.o +else + ifeq ($(CONFIG_JOY_CREATIVE),m) + M_OBJS += joy-creative.o + endif +endif + ifeq ($(CONFIG_JOY_DB9),y) O_OBJS += joy-db9.o else @@ -85,6 +88,22 @@ endif endif +ifeq ($(CONFIG_JOY_MAGELLAN),y) +O_OBJS += joy-magellan.o +else + ifeq ($(CONFIG_JOY_MAGELLAN),m) + M_OBJS += joy-magellan.o + endif +endif + +ifeq ($(CONFIG_JOY_PCI),y) +O_OBJS += joy-pci.o +else + ifeq ($(CONFIG_JOY_PCI),m) + M_OBJS += joy-pci.o + endif +endif + ifeq ($(CONFIG_JOY_SIDEWINDER),y) O_OBJS += joy-sidewinder.o else @@ -93,6 +112,22 @@ endif endif +ifeq ($(CONFIG_JOY_SPACEORB),y) +O_OBJS += joy-spaceorb.o +else + ifeq ($(CONFIG_JOY_SPACEORB),m) + M_OBJS += joy-spaceorb.o + endif +endif + +ifeq ($(CONFIG_JOY_SPACEBALL),y) +O_OBJS += joy-spaceball.o +else + ifeq ($(CONFIG_JOY_SPACEBALL),m) + M_OBJS += joy-spaceball.o + endif +endif + ifeq ($(CONFIG_JOY_THRUSTMASTER),y) O_OBJS += joy-thrustmaster.o else @@ -106,6 +141,14 @@ else ifeq ($(CONFIG_JOY_TURBOGRAFX),m) M_OBJS += joy-turbografx.o + endif +endif + +ifeq ($(CONFIG_JOY_WARRIOR),y) +O_OBJS += joy-warrior.o +else + ifeq ($(CONFIG_JOY_WARRIOR),m) + M_OBJS += joy-warrior.o endif endif diff -u --recursive --new-file v2.2.13/linux/drivers/char/joystick/joy-amiga.c linux/drivers/char/joystick/joy-amiga.c --- v2.2.13/linux/drivers/char/joystick/joy-amiga.c Tue Dec 1 19:05:05 1998 +++ linux/drivers/char/joystick/joy-amiga.c Tue Jan 4 10:12:14 2000 @@ -1,7 +1,9 @@ /* * joy-amiga.c Version 1.2 * - * Copyright (c) 1998 Vojtech Pavlik + * Copyright (c) 1998-1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* @@ -25,7 +27,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: + * e-mail - mail your message to , or by paper mail: * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic */ @@ -36,13 +38,14 @@ #include #include #include +#include static struct js_port* js_am_port __initdata = NULL; -MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_AUTHOR("Vojtech Pavlik "); MODULE_PARM(js_am, "1-2i"); -static int js_am[]={0,0}; +static int __initdata js_am[] = { 0, 0 }; /* * js_am_read() reads and Amiga joystick data. @@ -69,7 +72,7 @@ axes[0][0] = ((data >> 1) & 1) - ((data >> 9) & 1); data = ~(data ^ (data << 1)); - axes[0][0] = ((data >> 1) & 1) - ((data >> 9) & 1); + axes[0][1] = ((data >> 1) & 1) - ((data >> 9) & 1); return 0; } @@ -114,11 +117,14 @@ } #ifndef MODULE -void __init js_am_setup(char *str, int *ints) +int __init js_am_setup(SETUP_PARAM) { int i; + SETUP_PARSE(2); for (i = 0; i <= ints[0] && i < 2; i++) js_am[i] = ints[i+1]; + return 1; } +__setup("js_am=", js_am_setup); #endif #ifdef MODULE @@ -148,8 +154,8 @@ #ifdef MODULE void cleanup_module(void) { - while (js_am_port != NULL) { - if (js_am_port->devs[0] != NULL) + while (js_am_port) { + if (js_am_port->devs[0]) js_unregister_device(js_am_port->devs[0]); js_am_port = js_unregister_port(js_am_port); } diff -u --recursive --new-file v2.2.13/linux/drivers/char/joystick/joy-analog.c linux/drivers/char/joystick/joy-analog.c --- v2.2.13/linux/drivers/char/joystick/joy-analog.c Tue Dec 1 19:05:05 1998 +++ linux/drivers/char/joystick/joy-analog.c Tue Jan 4 10:12:14 2000 @@ -1,7 +1,9 @@ /* * joy-analog.c Version 1.2 * - * Copyright (c) 1996-1998 Vojtech Pavlik + * Copyright (c) 1996-1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* @@ -25,74 +27,129 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: + * e-mail - mail your message to , or by paper mail: * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic */ #include +#include +#include +#include #include #include #include #include #include #include +#include #include +#include -#define JS_AN_MAX_TIME 3000 +#define JS_AN_MAX_TIME 3000 /* 3 ms */ +#define JS_AN_LOOP_TIME 2000 /* 2 t */ static int js_an_port_list[] __initdata = {0x201, 0}; static struct js_port* js_an_port __initdata = NULL; -MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_AUTHOR("Vojtech Pavlik "); MODULE_PARM(js_an, "2-24i"); -static int js_an[]={-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0}; +static int __initdata js_an[] = { -1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0 }; #include "joy-analog.h" +struct js_ax_info { + int io; + int speed; + int loop; + int timeout; + struct js_an_info an; +}; + +/* + * Time macros. + */ + +#ifdef __i386__ +#ifdef CONFIG_X86_TSC +#define GET_TIME(x) __asm__ __volatile__ ( "rdtsc" : "=a" (x) : : "dx" ) +#define DELTA(x,y) ((x)-(y)) +#define TIME_NAME "TSC" +#else +#define GET_TIME(x) do { outb(0, 0x43); x = inb(0x40); x |= inb(0x40) << 8; } while (0) +#define DELTA(x,y) ((y)-(x)+((y)<(x)?1193180L/HZ:0)) +#define TIME_NAME "PIT" +#endif +#elif __alpha__ +#define GET_TIME(x) __asm__ __volatile__ ( "rpcc %0" : "=r" (x) ) +#define DELTA(x,y) ((x)-(y)) +#define TIME_NAME "PCC" +#endif + +#ifndef GET_TIME +#define FAKE_TIME +static unsigned long js_an_faketime = 0; +#define GET_TIME(x) do { x = js_an_faketime++; } while(0) +#define DELTA(x,y) ((x)-(y)) +#define TIME_NAME "Unreliable" +#endif + /* * js_an_read() reads analog joystick data. */ static int js_an_read(void *xinfo, int **axes, int **buttons) { - struct js_an_info *info = xinfo; + struct js_ax_info *info = xinfo; + struct js_an_info *an = &info->an; + int io = info->io; + unsigned long flags; unsigned char buf[4]; - int time[4]; - unsigned char u, v, a; - unsigned int t, t1; + unsigned int time[4]; + unsigned char u, v, w; + unsigned int p, q, r, s, t; int i, j; - int timeout; - int io = info->io; - timeout = (JS_AN_MAX_TIME * js_time_speed_a) >> 10; - - info->buttons = (~inb(io) & JS_AN_BUTTONS_STD) >> 4; + an->buttons = ~inb(io) >> 4; i = 0; - u = a = ((info->mask[0] | info->mask[1]) & JS_AN_AXES_STD) | (info->extensions & JS_AN_HAT_FCS) - | ((info->extensions & JS_AN_BUTTONS_PXY_XY) >> 2) | ((info->extensions & JS_AN_BUTTONS_PXY_UV) >> 4); - + w = ((an->mask[0] | an->mask[1]) & JS_AN_AXES_STD) | (an->extensions & JS_AN_HAT_FCS) + | ((an->extensions & JS_AN_BUTTONS_PXY_XY) >> 2) | ((an->extensions & JS_AN_BUTTONS_PXY_UV) >> 4); + p = info->loop; + q = info->timeout; + + __save_flags(flags); + __cli(); outb(0xff,io); - t = js_get_time_a(); + GET_TIME(r); + __restore_flags(flags); + t = r; + v = w; do { - v = inb(io) & a; - t1 = js_get_time_a(); - if (u ^ v) { - time[i] = js_delta_a(t1,t); + s = t; + u = v; + __cli(); + v = inb(io) & w; + GET_TIME(t); + __restore_flags(flags); + if ((u ^ v) && (DELTA(t,s) < p)) { + time[i] = t; buf[i] = u ^ v; - u = v; i++; } - } while (v && js_delta_a(t1,t) < timeout); + } while (v && (i < 4) && (DELTA(t,r) < q)); - for (--i; i >= 0; i--) + v <<= 4; + + for (--i; i >= 0; i--) { + v |= buf[i]; for (j = 0; j < 4; j++) - if (buf[i] & (1 << j)) info->axes[j] = (time[i] << 10) / js_time_speed_a; + if (buf[i] & (1 << j)) an->axes[j] = (DELTA(time[i],r) << 10) / info->speed; + } - js_an_decode(info, axes, buttons); + js_an_decode(an, axes, buttons); - return 0; + return -(v != w); } /* @@ -116,12 +173,53 @@ } /* + * js_an_calibrate_timer() calibrates the timer and computes loop + * and timeout values for a joystick port. + */ + +static void __init js_an_calibrate_timer(struct js_ax_info *info) +{ + unsigned int i, t, tx, t1, t2, t3; + unsigned long flags; + int io = info->io; + + save_flags(flags); + cli(); + GET_TIME(t1); +#ifdef FAKE_TIME + js_an_faketime += 830; +#endif + udelay(1000); + GET_TIME(t2); + GET_TIME(t3); + restore_flags(flags); + + info->speed = DELTA(t2, t1) - DELTA(t3, t2); + + tx = 1 << 30; + + for(i = 0; i < 50; i++) { + save_flags(flags); + cli(); + GET_TIME(t1); + for(t = 0; t < 50; t++) { inb(io); GET_TIME(t2); } + GET_TIME(t3); + restore_flags(flags); + udelay(i); + if ((t = DELTA(t2,t1) - DELTA(t3,t2)) < tx) tx = t; + } + + info->loop = (JS_AN_LOOP_TIME * t) / 50000; + info->timeout = (JS_AN_MAX_TIME * info->speed) / 1000; +} + +/* * js_an_probe() probes for analog joysticks. */ static struct js_port __init *js_an_probe(int io, int mask0, int mask1, struct js_port *port) { - struct js_an_info info; + struct js_ax_info info, *ax; int i, numdev; unsigned char u; @@ -129,7 +227,6 @@ if (check_region(io, 1)) return port; - if (((u = inb(io)) & 3) == 3) return port; outb(0xff,io); u = inb(io); udelay(JS_AN_MAX_TIME); @@ -138,31 +235,41 @@ if (!u) return port; if (u & 0xf0) return port; - if ((numdev = js_an_probe_devs(&info, u, mask0, mask1, port)) <= 0) + if ((numdev = js_an_probe_devs(&info.an, u, mask0, mask1, port)) <= 0) return port; info.io = io; + js_an_calibrate_timer(&info); + request_region(info.io, 1, "joystick (analog)"); - port = js_register_port(port, &info, numdev, sizeof(struct js_an_info), js_an_read); + port = js_register_port(port, &info, numdev, sizeof(struct js_ax_info), js_an_read); + ax = port->info; for (i = 0; i < numdev; i++) - printk(KERN_INFO "js%d: %s at %#x\n", - js_register_device(port, i, js_an_axes(i, &info), js_an_buttons(i, &info), - js_an_name(i, &info), js_an_open, js_an_close), - js_an_name(i, &info), info.io); + printk(KERN_INFO "js%d: %s at %#x ["TIME_NAME" timer, %d %sHz clock, %d ns res]\n", + js_register_device(port, i, js_an_axes(i, &ax->an), js_an_buttons(i, &ax->an), + js_an_name(i, &ax->an), js_an_open, js_an_close), + js_an_name(i, &ax->an), + ax->io, + ax->speed > 10000 ? (ax->speed + 800) / 1000 : ax->speed, + ax->speed > 10000 ? "M" : "k", + ax->loop * 1000000000 / JS_AN_LOOP_TIME / ax->speed); - js_an_read(port->info, port->axes, port->buttons); - js_an_init_corr(port->info, port->axes, port->corr, 8); + js_an_read(ax, port->axes, port->buttons); + js_an_init_corr(&ax->an, port->axes, port->corr, 8); return port; } #ifndef MODULE -void __init js_an_setup(char *str, int *ints) +int __init js_an_setup(SETUP_PARAM) { int i; + SETUP_PARSE(24); for (i = 0; i <= ints[0] && i < 24; i++) js_an[i] = ints[i+1]; + return 1; } +__setup("js_an=", js_an_setup); #endif #ifdef MODULE @@ -193,11 +300,11 @@ void cleanup_module(void) { int i; - struct js_an_info *info; + struct js_ax_info *info; - while (js_an_port != NULL) { + while (js_an_port) { for (i = 0; i < js_an_port->ndevs; i++) - if (js_an_port->devs[i] != NULL) + if (js_an_port->devs[i]) js_unregister_device(js_an_port->devs[i]); info = js_an_port->info; release_region(info->io, 1); diff -u --recursive --new-file v2.2.13/linux/drivers/char/joystick/joy-analog.h linux/drivers/char/joystick/joy-analog.h --- v2.2.13/linux/drivers/char/joystick/joy-analog.h Thu Oct 8 17:25:16 1998 +++ linux/drivers/char/joystick/joy-analog.h Tue Jan 4 10:12:14 2000 @@ -1,13 +1,15 @@ /* * joy-analog.h Version 1.2 * - * Copyright (c) 1996-1998 Vojtech Pavlik + * Copyright (c) 1996-1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* * This file is designed to be included in any joystick driver * that communicates with standard analog joysticks. This currently - * is: joy-analog.c, joy-assasin.c, and joy-lightning.c + * is: joy-analog.c, joy-assassin.c, and joy-lightning.c */ /* @@ -26,10 +28,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: + * e-mail - mail your message to , or by paper mail: * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic */ +#include + #define JS_AN_AXES_STD 0x0f #define JS_AN_BUTTONS_STD 0xf0 @@ -53,7 +57,6 @@ } js_an_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1, 0}, { 0, 1}, {-1, 0}}; struct js_an_info { - int io; unsigned char mask[2]; unsigned int extensions; int axes[4]; @@ -75,7 +78,7 @@ if (info->mask[1] & JS_AN_BUTTONS_STD) buttons[1][0] = 0; if (info->extensions & JS_AN_ANY_CHF) { - switch (info->buttons) { + switch (info->buttons & 0xf) { case 0x1: buttons[0][0] = 0x01; break; case 0x2: buttons[0][0] = 0x02; break; case 0x4: buttons[0][0] = 0x04; break; @@ -134,19 +137,6 @@ } } -/* - * js_an_count_bits() counts set bits in a byte. - */ - -static inline int js_an_count_bits(unsigned long c) -{ - int i = 0; - while (c) { - i += c & 1; - c >>= 1; - } - return i; -} /* * js_an_init_corr() initializes the correction values for @@ -158,7 +148,7 @@ int i, j, t; for (i = 0; i < 2; i++) - for (j = 0; j < js_an_count_bits(info->mask[i] & 0xf); j++) { + for (j = 0; j < hweight8(info->mask[i] & 0xf); j++) { if ((j == 2 && (info->mask[i] & 0xb) == 0xb) || (j == 3 && (info->mask[i] & 0xf) == 0xf)) { @@ -175,9 +165,9 @@ corr[i][j].coef[3] = (1 << 29) / (t - (t >> 2) + 1); } - i = js_an_count_bits(info->mask[0] & 0xf); + i = hweight8(info->mask[0] & 0xf); - for (j = i; j < i + (js_an_count_bits(info->extensions & JS_AN_HATS_ALL) << 1); j++) { + for (j = i; j < i + (hweight8(info->extensions & JS_AN_HATS_ALL) << 1); j++) { corr[0][j].type = JS_CORR_BROKEN; corr[0][j].prec = 0; corr[0][j].coef[0] = 0; @@ -204,6 +194,7 @@ info->mask[1] = mask1 & (exist | 0xf0) & ~info->mask[0]; info->extensions = (mask0 >> 8) & ((exist & JS_AN_HAT_FCS) | ((exist << 2) & JS_AN_BUTTONS_PXY_XY) | ((exist << 4) & JS_AN_BUTTONS_PXY_UV) | JS_AN_ANY_CHF); + if (info->extensions & JS_AN_BUTTONS_PXY) { info->mask[0] &= ~((info->extensions & JS_AN_BUTTONS_PXY_XY) >> 2); info->mask[0] &= ~((info->extensions & JS_AN_BUTTONS_PXY_UV) >> 4); @@ -212,7 +203,7 @@ if (info->extensions & JS_AN_HAT_FCS) { info->mask[0] &= ~JS_AN_HAT_FCS; info->mask[1] = 0; - info->extensions &= ~(JS_AN_BUTTON_PXY_Y | JS_AN_BUTTON_PXY_U); + info->extensions &= ~(JS_AN_BUTTON_PXY_Y | JS_AN_BUTTON_PXY_V); } if (info->extensions & JS_AN_ANY_CHF) { info->mask[0] |= 0xf0; @@ -233,12 +224,11 @@ info->mask[0] = 0xcc; /* joystick 1 */ break; case 0xf: - info->mask[0] = 0x33; /* joysticks 0 and 1 */ - info->mask[1] = 0xcc; + info->mask[0] = 0xff; /* 4-axis 4-button joystick */ break; default: printk(KERN_WARNING "joy-analog: Unknown joystick device detected " - "(data=%#x), contact \n", exist); + "(data=%#x), contact \n", exist); return -1; } } @@ -252,7 +242,7 @@ static inline int js_an_axes(int i, struct js_an_info *info) { - return js_an_count_bits(info->mask[i] & 0x0f) + js_an_count_bits(info->extensions & JS_AN_HATS_ALL) * 2; + return hweight8(info->mask[i] & 0x0f) + hweight8(info->extensions & JS_AN_HATS_ALL) * 2; } /* @@ -261,9 +251,9 @@ static inline int js_an_buttons(int i, struct js_an_info *info) { - return js_an_count_bits(info->mask[i] & 0xf0) + + return hweight8(info->mask[i] & 0xf0) + (info->extensions & JS_AN_BUTTONS_CHF) * 2 + - js_an_count_bits(info->extensions & JS_AN_BUTTONS_PXY); + hweight8(info->extensions & JS_AN_BUTTONS_PXY); } /* @@ -276,13 +266,13 @@ { sprintf(js_an_name_buf, "Analog %d-axis %d-button", - js_an_count_bits(info->mask[i] & 0x0f), + hweight8(info->mask[i] & 0x0f), js_an_buttons(i, info)); if (info->extensions & JS_AN_HATS_ALL) sprintf(js_an_name_buf, "%s %d-hat", js_an_name_buf, - js_an_count_bits(info->extensions & JS_AN_HATS_ALL)); + hweight8(info->extensions & JS_AN_HATS_ALL)); strcat(js_an_name_buf, " joystick"); @@ -291,7 +281,7 @@ js_an_name_buf, info->extensions & JS_AN_ANY_CHF ? " CHF" : "", info->extensions & JS_AN_HAT_FCS ? " FCS" : "", - info->extensions & JS_AN_BUTTONS_PXY ? " XY-button" : ""); + info->extensions & JS_AN_BUTTONS_PXY ? " XY/UV" : ""); return js_an_name_buf; } diff -u --recursive --new-file v2.2.13/linux/drivers/char/joystick/joy-assasin.c linux/drivers/char/joystick/joy-assasin.c --- v2.2.13/linux/drivers/char/joystick/joy-assasin.c Tue Dec 1 19:05:05 1998 +++ linux/drivers/char/joystick/joy-assasin.c Wed Dec 31 16:00:00 1969 @@ -1,423 +0,0 @@ -/* - * joy-assasin.c Version 1.2 - * - * Copyright (c) 1998 Vojtech Pavlik - */ - -/* - * This is a module for the Linux joystick driver, supporting - * joysticks using FP-Gaming's Assasin 3D protocol. - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define JS_AS_MAX_START 250 -#define JS_AS_MAX_STROBE 50 -#define JS_AS_MAX_TIME 2400 -#define JS_AS_MAX_LENGTH 40 - -#define JS_AS_MODE_A3D 1 /* Assasin 3D */ -#define JS_AS_MODE_PAN 2 /* Panther */ -#define JS_AS_MODE_OEM 3 /* Panther OEM version */ -#define JS_AS_MODE_PXL 4 /* Panther XL */ - -MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_PARM(js_as, "2-24i"); - -static int js_as[]={-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0}; - -static int js_as_port_list[] __initdata = {0x201, 0}; -static struct js_port* js_as_port __initdata = NULL; - -#include "joy-analog.h" - -struct js_as_info { - int io; - char mode; - char rudder; - struct js_an_info an; -}; - -/* - * js_as_read_packet() reads an Assasin 3D packet. - */ - -static int js_as_read_packet(int io, int length, char *data) -{ - unsigned char u, v; - int i; - unsigned int t, t1; - unsigned long flags; - - int start = (js_time_speed * JS_AS_MAX_START) >> 10; - int strobe = (js_time_speed * JS_AS_MAX_STROBE) >> 10; - - i = 0; - - __save_flags(flags); - __cli(); - outb(0xff,io); - - u = inb(io); - t = js_get_time(); - - do { - v = inb(io); - t1 = js_get_time(); - } while (u == v && js_delta(t1, t) < start); - - t = t1; - - do { - v = inb(io); - t1 = js_get_time(); - if ((u ^ v) & u & 0x10) { - data[i++] = v >> 5; - t = t1; - } - u = v; - } while (i < length && js_delta(t1,t) < strobe); - - __restore_flags(flags); - - return i; -} - -/* - * js_as_csum() computes checksum of triplet packet - */ - -static int js_as_csum(char *data, int count) -{ - int i, csum = 0; - for (i = 0; i < count - 2; i++) csum += data[i]; - return (csum & 0x3f) != ((data[count - 2] << 3) | data[count - 1]); -} - -/* - * js_as_read() reads and analyzes A3D joystick data. - */ - -static int js_as_read(void *xinfo, int **axes, int **buttons) -{ - struct js_as_info *info = xinfo; - char data[JS_AS_MAX_LENGTH]; - - switch (info->mode) { - - case JS_AS_MODE_A3D: - case JS_AS_MODE_OEM: - case JS_AS_MODE_PAN: - - if (js_as_read_packet(info->io, 29, data) != 29) return -1; - if (data[0] != info->mode) return -1; - if (js_as_csum(data, 29)) return -1; - - axes[0][0] = ((data[5] << 6) | (data[6] << 3) | data[ 7]) - ((data[5] & 4) << 7); - axes[0][1] = ((data[8] << 6) | (data[9] << 3) | data[10]) - ((data[8] & 4) << 7); - - buttons[0][0] = (data[2] << 2) | (data[3] >> 1); - - info->an.axes[0] = ((char)((data[11] << 6) | (data[12] << 3) | (data[13]))) + 128; - info->an.axes[1] = ((char)((data[14] << 6) | (data[15] << 3) | (data[16]))) + 128; - info->an.axes[2] = ((char)((data[17] << 6) | (data[18] << 3) | (data[19]))) + 128; - info->an.axes[3] = ((char)((data[20] << 6) | (data[21] << 3) | (data[22]))) + 128; - - info->an.buttons = ((data[3] << 3) | data[4]) & 0xf; - - js_an_decode(&info->an, axes + 1, buttons + 1); - - return 0; - - case JS_AS_MODE_PXL: - - if (js_as_read_packet(info->io, 33, data) != 33) return -1; - if (data[0] != info->mode) return -1; - if (js_as_csum(data, 33)) return -1; - - axes[0][0] = ((char)((data[15] << 6) | (data[16] << 3) | (data[17]))) + 128; - axes[0][1] = ((char)((data[18] << 6) | (data[19] << 3) | (data[20]))) + 128; - info->an.axes[0] = ((char)((data[21] << 6) | (data[22] << 3) | (data[23]))) + 128; - axes[0][2] = ((char)((data[24] << 6) | (data[25] << 3) | (data[26]))) + 128; - - axes[0][3] = ( data[5] & 1) - ((data[5] >> 2) & 1); - axes[0][4] = ((data[5] >> 1) & 1) - ((data[6] >> 2) & 1); - axes[0][5] = ((data[4] >> 1) & 1) - ( data[3] & 1); - axes[0][6] = ((data[4] >> 2) & 1) - ( data[4] & 1); - - axes[0][7] = ((data[ 9] << 6) | (data[10] << 3) | data[11]) - ((data[ 9] & 4) << 7); - axes[0][8] = ((data[12] << 6) | (data[13] << 3) | data[14]) - ((data[12] & 4) << 7); - - buttons[0][0] = (data[2] << 8) | ((data[3] & 6) << 5) | (data[7] << 3) | data[8]; - - if (info->rudder) axes[1][0] = info->an.axes[0]; - - return 0; - - default: - printk("Error.\n"); - return -1; - } -} - -/* - * js_as_open() is a callback from the file open routine. - */ - -static int js_as_open(struct js_dev *jd) -{ - MOD_INC_USE_COUNT; - return 0; -} - -/* - * js_as_close() is a callback from the file release routine. - */ - -static int js_as_close(struct js_dev *jd) -{ - MOD_DEC_USE_COUNT; - return 0; -} - -/* - * js_as_pxl_init_corr() initializes the correction values for - * the Panther XL. - */ - -static void __init js_as_pxl_init_corr(struct js_corr **corr, int **axes) -{ - int i; - - for (i = 0; i < 2; i++) { - corr[0][i].type = JS_CORR_BROKEN; - corr[0][i].prec = 0; - corr[0][i].coef[0] = axes[0][i] - 4; - corr[0][i].coef[1] = axes[0][i] + 4; - corr[0][i].coef[2] = (1 << 29) / (127 - 32); - corr[0][i].coef[3] = (1 << 29) / (127 - 32); - } - - corr[0][2].type = JS_CORR_BROKEN; - corr[0][2].prec = 0; - corr[0][2].coef[0] = 127 - 4; - corr[0][2].coef[1] = 128 + 4; - corr[0][2].coef[2] = (1 << 29) / (127 - 6); - corr[0][2].coef[3] = (1 << 29) / (127 - 6); - - for (i = 3; i < 7; i++) { - corr[0][i].type = JS_CORR_BROKEN; - corr[0][i].prec = 0; - corr[0][i].coef[0] = 0; - corr[0][i].coef[1] = 0; - corr[0][i].coef[2] = (1 << 29); - corr[0][i].coef[3] = (1 << 29); - } - - for (i = 7; i < 9; i++) { - corr[0][i].type = JS_CORR_BROKEN; - corr[0][i].prec = -1; - corr[0][i].coef[0] = 0; - corr[0][i].coef[1] = 0; - corr[0][i].coef[2] = (104 << 14); - corr[0][i].coef[3] = (104 << 14); - } -} - -/* - * js_as_as_init_corr() initializes the correction values for - * the Panther and Assasin. - */ - -static void __init js_as_as_init_corr(struct js_corr **corr) -{ - int i; - - for (i = 0; i < 2; i++) { - corr[0][i].type = JS_CORR_BROKEN; - corr[0][i].prec = -1; - corr[0][i].coef[0] = 0; - corr[0][i].coef[1] = 0; - corr[0][i].coef[2] = (104 << 14); - corr[0][i].coef[3] = (104 << 14); - } -} - -/* - * js_as_rudder_init_corr() initializes the correction values for - * the Panther XL connected rudder. - */ - -static void __init js_as_rudder_init_corr(struct js_corr **corr, int **axes) -{ - corr[1][0].type = JS_CORR_BROKEN; - corr[1][0].prec = 0; - corr[1][0].coef[0] = axes[1][0] - (axes[1][0] >> 3); - corr[1][0].coef[1] = axes[1][0] + (axes[1][0] >> 3); - corr[1][0].coef[2] = (1 << 29) / (axes[1][0] - (axes[1][0] >> 2) + 1); - corr[1][0].coef[3] = (1 << 29) / (axes[1][0] - (axes[1][0] >> 2) + 1); -} - -/* - * js_as_probe() probes for A3D joysticks. - */ - -static struct js_port __init *js_as_probe(int io, int mask0, int mask1, struct js_port *port) -{ - struct js_as_info iniinfo; - struct js_as_info *info = &iniinfo; - char *name; - char data[JS_AS_MAX_LENGTH]; - unsigned char u; - int i; - int numdev; - - memset(info, 0, sizeof(struct js_as_info)); - - if (io < 0) return port; - - if (check_region(io, 1)) return port; - if (((u = inb(io)) & 3) == 3) return port; - outb(0xff,io); - if (!((inb(io) ^ u) & ~u & 0xf)) return port; - - if (js_as_read_packet(io, 1, data) != 1) return port; - - if (data[0] && data[0] <= 4) { - info->mode = data[0]; - info->io = io; - request_region(io, 1, "joystick (assasin)"); - port = js_register_port(port, info, 3, sizeof(struct js_as_info), js_as_read); - info = port->info; - } else { - printk(KERN_WARNING "joy-assasin: unknown joystick device detected " - "(io=%#x, id=%d), contact \n", io, data[0]); - return port; - } - - udelay(JS_AS_MAX_TIME); - - if (info->mode == JS_AS_MODE_PXL) { - printk(KERN_INFO "js%d: MadCatz Panther XL at %#x\n", - js_register_device(port, 0, 9, 9, "MadCatz Panther XL", js_as_open, js_as_close), - info->io); - js_as_read(port->info, port->axes, port->buttons); - js_as_pxl_init_corr(port->corr, port->axes); - if (info->an.axes[0] < 254) { - printk(KERN_INFO "js%d: Analog rudder on MadCatz Panther XL\n", - js_register_device(port, 1, 1, 0, "Analog rudder", js_as_open, js_as_close)); - info->rudder = 1; - port->axes[1][0] = info->an.axes[0]; - js_as_rudder_init_corr(port->corr, port->axes); - } - return port; - } - - switch (info->mode) { - case JS_AS_MODE_A3D: name = "FP-Gaming Assasin 3D"; break; - case JS_AS_MODE_PAN: name = "MadCatz Panther"; break; - case JS_AS_MODE_OEM: name = "OEM Assasin 3D"; break; - default: name = "This cannot happen"; break; - } - - printk(KERN_INFO "js%d: %s at %#x\n", - js_register_device(port, 0, 2, 3, name, js_as_open, js_as_close), - name, info->io); - - js_as_as_init_corr(port->corr); - - js_as_read(port->info, port->axes, port->buttons); - - for (i = u = 0; i < 4; i++) if (info->an.axes[i] < 254) u |= 1 << i; - - if ((numdev = js_an_probe_devs(&info->an, u, mask0, mask1, port)) <= 0) - return port; - - for (i = 0; i < numdev; i++) - printk(KERN_INFO "js%d: %s on %s\n", - js_register_device(port, i + 1, js_an_axes(i, &info->an), js_an_buttons(i, &info->an), - js_an_name(i, &info->an), js_as_open, js_as_close), - js_an_name(i, &info->an), name); - - js_an_decode(&info->an, port->axes + 1, port->buttons + 1); - js_an_init_corr(&info->an, port->axes + 1, port->corr + 1, 0); - - return port; -} - -#ifndef MODULE -void __init js_as_setup(char *str, int *ints) -{ - int i; - for (i = 0; i <= ints[0] && i < 24; i++) js_as[i] = ints[i+1]; -} -#endif - -#ifdef MODULE -int init_module(void) -#else -int __init js_as_init(void) -#endif -{ - int i; - - if (js_as[0] >= 0) { - for (i = 0; (js_as[i*3] >= 0) && i < 8; i++) - js_as_port = js_as_probe(js_as[i*3], js_as[i*3+1], js_as[i*3+2], js_as_port); - } else { - for (i = 0; js_as_port_list[i]; i++) js_as_port = js_as_probe(js_as_port_list[i], 0, 0, js_as_port); - } - if (js_as_port) return 0; - -#ifdef MODULE - printk(KERN_WARNING "joy-assasin: no joysticks found\n"); -#endif - - return -ENODEV; -} - -#ifdef MODULE -void cleanup_module(void) -{ - int i; - struct js_as_info *info; - - while (js_as_port != NULL) { - for (i = 0; i < js_as_port->ndevs; i++) - if (js_as_port->devs[i] != NULL) - js_unregister_device(js_as_port->devs[i]); - info = js_as_port->info; - release_region(info->io, 1); - js_as_port = js_unregister_port(js_as_port); - } - -} -#endif diff -u --recursive --new-file v2.2.13/linux/drivers/char/joystick/joy-assassin.c linux/drivers/char/joystick/joy-assassin.c --- v2.2.13/linux/drivers/char/joystick/joy-assassin.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/joystick/joy-assassin.c Tue Jan 4 10:12:14 2000 @@ -0,0 +1,416 @@ +/* + * joy-assassin.c Version 1.2 + * + * Copyright (c) 1998-1999 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * This is a module for the Linux joystick driver, supporting + * joysticks using FP-Gaming's Assassin 3D protocol. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define JS_AS_MAX_START 1000 +#define JS_AS_DELAY_READ 3000 +#define JS_AS_MAX_LENGTH 40 + +#define JS_AS_MODE_A3D 1 /* Assassin 3D */ +#define JS_AS_MODE_PAN 2 /* Panther */ +#define JS_AS_MODE_OEM 3 /* Panther OEM version */ +#define JS_AS_MODE_PXL 4 /* Panther XL */ + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_PARM(js_as, "2-24i"); + +static int __initdata js_as[] = { -1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0 }; + +static int js_as_port_list[] __initdata = {0x201, 0}; +static struct js_port* js_as_port __initdata = NULL; + +#include "joy-analog.h" + +struct js_as_info { + int io; + char mode; + char rudder; + struct js_an_info an; +}; + +/* + * js_as_read_packet() reads an Assassin 3D packet. + */ + +static int js_as_read_packet(int io, int length, char *data) +{ + unsigned char u, v; + int i; + unsigned int t, p; + unsigned long flags; + + i = 0; + + __save_flags(flags); + __cli(); + + outb(0xff,io); + v = inb(io); + t = p = JS_AS_MAX_START; + + while (t > 0 && i < length) { + t--; + u = v; v = inb(io); + if (~v & u & 0x10) { + data[i++] = v >> 5; + p = t = (p - t) << 3; + } + } + + __restore_flags(flags); + + return i; +} + +/* + * js_as_csum() computes checksum of triplet packet + */ + +static int js_as_csum(char *data, int count) +{ + int i, csum = 0; + for (i = 0; i < count - 2; i++) csum += data[i]; + return (csum & 0x3f) != ((data[count - 2] << 3) | data[count - 1]); +} + +/* + * js_as_read() reads and analyzes A3D joystick data. + */ + +static int js_as_read(void *xinfo, int **axes, int **buttons) +{ + struct js_as_info *info = xinfo; + char data[JS_AS_MAX_LENGTH]; + + switch (info->mode) { + + case JS_AS_MODE_A3D: + case JS_AS_MODE_OEM: + case JS_AS_MODE_PAN: + + if (js_as_read_packet(info->io, 29, data) != 29) return -1; + if (data[0] != info->mode) return -1; + if (js_as_csum(data, 29)) return -1; + + axes[0][0] = ((data[5] << 6) | (data[6] << 3) | data[ 7]) - ((data[5] & 4) << 7); + axes[0][1] = ((data[8] << 6) | (data[9] << 3) | data[10]) - ((data[8] & 4) << 7); + + buttons[0][0] = (data[2] << 2) | (data[3] >> 1); + + info->an.axes[0] = ((char)((data[11] << 6) | (data[12] << 3) | (data[13]))) + 128; + info->an.axes[1] = ((char)((data[14] << 6) | (data[15] << 3) | (data[16]))) + 128; + info->an.axes[2] = ((char)((data[17] << 6) | (data[18] << 3) | (data[19]))) + 128; + info->an.axes[3] = ((char)((data[20] << 6) | (data[21] << 3) | (data[22]))) + 128; + + info->an.buttons = ((data[3] << 3) | data[4]) & 0xf; + + js_an_decode(&info->an, axes + 1, buttons + 1); + + return 0; + + case JS_AS_MODE_PXL: + + if (js_as_read_packet(info->io, 33, data) != 33) return -1; + if (data[0] != info->mode) return -1; + if (js_as_csum(data, 33)) return -1; + + axes[0][0] = ((char)((data[15] << 6) | (data[16] << 3) | (data[17]))) + 128; + axes[0][1] = ((char)((data[18] << 6) | (data[19] << 3) | (data[20]))) + 128; + info->an.axes[0] = ((char)((data[21] << 6) | (data[22] << 3) | (data[23]))) + 128; + axes[0][2] = ((char)((data[24] << 6) | (data[25] << 3) | (data[26]))) + 128; + + axes[0][3] = ( data[5] & 1) - ((data[5] >> 2) & 1); + axes[0][4] = ((data[5] >> 1) & 1) - ((data[6] >> 2) & 1); + axes[0][5] = ((data[4] >> 1) & 1) - ( data[3] & 1); + axes[0][6] = ((data[4] >> 2) & 1) - ( data[4] & 1); + + axes[0][7] = ((data[ 9] << 6) | (data[10] << 3) | data[11]) - ((data[ 9] & 4) << 7); + axes[0][8] = ((data[12] << 6) | (data[13] << 3) | data[14]) - ((data[12] & 4) << 7); + + buttons[0][0] = (data[2] << 8) | ((data[3] & 6) << 5) | (data[7] << 3) | data[8]; + + if (info->rudder) axes[1][0] = info->an.axes[0]; + + return 0; + } + return -1; +} + +/* + * js_as_open() is a callback from the file open routine. + */ + +static int js_as_open(struct js_dev *jd) +{ + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_as_close() is a callback from the file release routine. + */ + +static int js_as_close(struct js_dev *jd) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_as_pxl_init_corr() initializes the correction values for + * the Panther XL. + */ + +static void __init js_as_pxl_init_corr(struct js_corr **corr, int **axes) +{ + int i; + + for (i = 0; i < 2; i++) { + corr[0][i].type = JS_CORR_BROKEN; + corr[0][i].prec = 0; + corr[0][i].coef[0] = axes[0][i] - 4; + corr[0][i].coef[1] = axes[0][i] + 4; + corr[0][i].coef[2] = (1 << 29) / (127 - 32); + corr[0][i].coef[3] = (1 << 29) / (127 - 32); + } + + corr[0][2].type = JS_CORR_BROKEN; + corr[0][2].prec = 0; + corr[0][2].coef[0] = 127 - 4; + corr[0][2].coef[1] = 128 + 4; + corr[0][2].coef[2] = (1 << 29) / (127 - 6); + corr[0][2].coef[3] = (1 << 29) / (127 - 6); + + for (i = 3; i < 7; i++) { + corr[0][i].type = JS_CORR_BROKEN; + corr[0][i].prec = 0; + corr[0][i].coef[0] = 0; + corr[0][i].coef[1] = 0; + corr[0][i].coef[2] = (1 << 29); + corr[0][i].coef[3] = (1 << 29); + } + + for (i = 7; i < 9; i++) { + corr[0][i].type = JS_CORR_BROKEN; + corr[0][i].prec = -1; + corr[0][i].coef[0] = 0; + corr[0][i].coef[1] = 0; + corr[0][i].coef[2] = (104 << 14); + corr[0][i].coef[3] = (104 << 14); + } +} + +/* + * js_as_as_init_corr() initializes the correction values for + * the Panther and Assassin. + */ + +static void __init js_as_as_init_corr(struct js_corr **corr) +{ + int i; + + for (i = 0; i < 2; i++) { + corr[0][i].type = JS_CORR_BROKEN; + corr[0][i].prec = -1; + corr[0][i].coef[0] = 0; + corr[0][i].coef[1] = 0; + corr[0][i].coef[2] = (104 << 14); + corr[0][i].coef[3] = (104 << 14); + } +} + +/* + * js_as_rudder_init_corr() initializes the correction values for + * the Panther XL connected rudder. + */ + +static void __init js_as_rudder_init_corr(struct js_corr **corr, int **axes) +{ + corr[1][0].type = JS_CORR_BROKEN; + corr[1][0].prec = 0; + corr[1][0].coef[0] = axes[1][0] - (axes[1][0] >> 3); + corr[1][0].coef[1] = axes[1][0] + (axes[1][0] >> 3); + corr[1][0].coef[2] = (1 << 29) / (axes[1][0] - (axes[1][0] >> 2) + 1); + corr[1][0].coef[3] = (1 << 29) / (axes[1][0] - (axes[1][0] >> 2) + 1); +} + +/* + * js_as_probe() probes for A3D joysticks. + */ + +static struct js_port __init *js_as_probe(int io, int mask0, int mask1, struct js_port *port) +{ + struct js_as_info iniinfo; + struct js_as_info *info = &iniinfo; + char *name; + char data[JS_AS_MAX_LENGTH]; + unsigned char u; + int i; + int numdev; + + memset(info, 0, sizeof(struct js_as_info)); + + if (io < 0) return port; + + if (check_region(io, 1)) return port; + + i = js_as_read_packet(io, JS_AS_MAX_LENGTH, data); + + printk("%d\n", i); + + if (!i) return port; + if (js_as_csum(data, i)) return port; + + if (data[0] && data[0] <= 4) { + info->mode = data[0]; + info->io = io; + request_region(io, 1, "joystick (assassin)"); + port = js_register_port(port, info, 3, sizeof(struct js_as_info), js_as_read); + info = port->info; + } else { + printk(KERN_WARNING "joy-assassin: unknown joystick device detected " + "(io=%#x, id=%d), contact \n", io, data[0]); + return port; + } + + udelay(JS_AS_DELAY_READ); + + if (info->mode == JS_AS_MODE_PXL) { + printk(KERN_INFO "js%d: MadCatz Panther XL at %#x\n", + js_register_device(port, 0, 9, 9, "MadCatz Panther XL", js_as_open, js_as_close), + info->io); + js_as_read(port->info, port->axes, port->buttons); + js_as_pxl_init_corr(port->corr, port->axes); + if (info->an.axes[0] < 254) { + printk(KERN_INFO "js%d: Analog rudder on MadCatz Panther XL\n", + js_register_device(port, 1, 1, 0, "Analog rudder", js_as_open, js_as_close)); + info->rudder = 1; + port->axes[1][0] = info->an.axes[0]; + js_as_rudder_init_corr(port->corr, port->axes); + } + return port; + } + + switch (info->mode) { + case JS_AS_MODE_A3D: name = "FP-Gaming Assassin 3D"; break; + case JS_AS_MODE_PAN: name = "MadCatz Panther"; break; + case JS_AS_MODE_OEM: name = "OEM Assassin 3D"; break; + default: name = "This cannot happen"; break; + } + + printk(KERN_INFO "js%d: %s at %#x\n", + js_register_device(port, 0, 2, 3, name, js_as_open, js_as_close), + name, info->io); + + js_as_as_init_corr(port->corr); + + js_as_read(port->info, port->axes, port->buttons); + + for (i = u = 0; i < 4; i++) if (info->an.axes[i] < 254) u |= 1 << i; + + if ((numdev = js_an_probe_devs(&info->an, u, mask0, mask1, port)) <= 0) + return port; + + for (i = 0; i < numdev; i++) + printk(KERN_INFO "js%d: %s on %s\n", + js_register_device(port, i + 1, js_an_axes(i, &info->an), js_an_buttons(i, &info->an), + js_an_name(i, &info->an), js_as_open, js_as_close), + js_an_name(i, &info->an), name); + + js_an_decode(&info->an, port->axes + 1, port->buttons + 1); + js_an_init_corr(&info->an, port->axes + 1, port->corr + 1, 0); + + return port; +} + +#ifndef MODULE +int __init js_as_setup(SETUP_PARAM) +{ + int i; + SETUP_PARSE(24); + for (i = 0; i <= ints[0] && i < 24; i++) js_as[i] = ints[i+1]; + return 1; +} +__setup("js_as=", js_as_setup); +#endif + +#ifdef MODULE +int init_module(void) +#else +int __init js_as_init(void) +#endif +{ + int i; + + if (js_as[0] >= 0) { + for (i = 0; (js_as[i*3] >= 0) && i < 8; i++) + js_as_port = js_as_probe(js_as[i*3], js_as[i*3+1], js_as[i*3+2], js_as_port); + } else { + for (i = 0; js_as_port_list[i]; i++) js_as_port = js_as_probe(js_as_port_list[i], 0, 0, js_as_port); + } + if (js_as_port) return 0; + +#ifdef MODULE + printk(KERN_WARNING "joy-assassin: no joysticks found\n"); +#endif + + return -ENODEV; +} + +#ifdef MODULE +void cleanup_module(void) +{ + int i; + struct js_as_info *info; + + while (js_as_port) { + for (i = 0; i < js_as_port->ndevs; i++) + if (js_as_port->devs[i]) + js_unregister_device(js_as_port->devs[i]); + info = js_as_port->info; + release_region(info->io, 1); + js_as_port = js_unregister_port(js_as_port); + } + +} +#endif diff -u --recursive --new-file v2.2.13/linux/drivers/char/joystick/joy-console.c linux/drivers/char/joystick/joy-console.c --- v2.2.13/linux/drivers/char/joystick/joy-console.c Tue Dec 1 19:05:05 1998 +++ linux/drivers/char/joystick/joy-console.c Tue Jan 4 10:12:14 2000 @@ -1,14 +1,19 @@ /* - * joy-console.c Version 0.11V + * joy-console.c Version 0.14V * - * Copyright (c) 1998 Andree Borrmann + * Copyright (c) 1998 Andree Borrmann + * Copyright (c) 1999 John Dahlstrom + * Copyright (c) 1999 David Kuder + * Copyright (c) 1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* * This is a module for the Linux joystick driver, supporting - * console (NES, SNES, Multi1, Multi2, PSX) gamepads connected - * via parallel port. Up to five such controllers can be - * connected to one parallel port. + * console (NES, SNES, N64, Multi1, Multi2, PSX) gamepads + * connected via parallel port. Up to five such controllers + * can be connected to one parallel port. */ /* @@ -25,6 +30,10 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic */ #include @@ -36,12 +45,13 @@ #include #include #include +#include -MODULE_AUTHOR("Andree Borrmann "); +MODULE_AUTHOR("Vojtech Pavlik "); MODULE_PARM(js_console, "2-6i"); -MODULE_PARM(js_console2,"2-6i"); -MODULE_PARM(js_console3,"2-6i"); +MODULE_PARM(js_console_2,"2-6i"); +MODULE_PARM(js_console_3,"2-6i"); #define JS_NO_PAD 0 @@ -51,29 +61,29 @@ #define JS_MULTI_STICK 4 #define JS_MULTI2_STICK 5 #define JS_PSX_PAD 6 - -#define JS_MAX_PAD JS_PSX_PAD +#define JS_N64_PAD 7 +#define JS_N64_PAD_DPP 8 /* DirectPad Pro compatible layout */ + +#define JS_MAX_PAD JS_N64_PAD_DPP struct js_console_info { -#ifdef USE_PARPORT struct pardevice *port; /* parport device */ -#else - int port; /* hw port */ -#endif int pads; /* total number of pads */ + int pad_to_device[5]; /* pad to js device mapping (js0, js1, etc.) */ int snes; /* SNES pads */ int nes; /* NES pads */ + int n64; /* N64 pads */ + int n64_dpp; /* bits indicate N64 pads treated 14 button, 2 axis */ int multi; /* Multi joysticks */ int multi2; /* Multi joysticks with 2 buttons */ - int psx; /* Normal PSX controllers */ - int negcon; /* PSX NEGCON controllers */ + int psx; /* PSX controllers */ }; static struct js_port* js_console_port = NULL; static int js_console[] __initdata = { -1, 0, 0, 0, 0, 0 }; -static int js_console2[] __initdata = { -1, 0, 0, 0, 0, 0 }; -static int js_console3[] __initdata = { -1, 0, 0, 0, 0, 0 }; +static int js_console_2[] __initdata = { -1, 0, 0, 0, 0, 0 }; +static int js_console_3[] __initdata = { -1, 0, 0, 0, 0, 0 }; static int status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 }; @@ -109,7 +119,7 @@ #define JS_SNES_L 10 #define JS_SNES_R 11 -#define JS_NES_POWER 0xf8 +#define JS_NES_POWER 0xfc #define JS_NES_CLOCK 0x01 #define JS_NES_LATCH 0x02 @@ -123,17 +133,99 @@ { int i; - JS_PAR_DATA_OUT(JS_NES_POWER + JS_NES_CLOCK + JS_NES_LATCH, info->port); + JS_PAR_DATA_OUT(JS_NES_POWER | JS_NES_CLOCK | JS_NES_LATCH, info->port); udelay(JS_NES_DELAY * 2); - JS_PAR_DATA_OUT(JS_NES_POWER + JS_NES_CLOCK, info->port); + JS_PAR_DATA_OUT(JS_NES_POWER | JS_NES_CLOCK, info->port); for (i = 0; i < length; i++) { udelay(JS_NES_DELAY); JS_PAR_DATA_OUT(JS_NES_POWER, info->port); data[i] = JS_PAR_STATUS(info->port) ^ ~JS_PAR_STATUS_INVERT; udelay(JS_NES_DELAY); - JS_PAR_DATA_OUT(JS_NES_POWER + JS_NES_CLOCK, info->port); + JS_PAR_DATA_OUT(JS_NES_POWER | JS_NES_CLOCK, info->port); + } +} + +/* + * N64 support. + */ + +#define JS_N64_A 0 +#define JS_N64_B 1 +#define JS_N64_Z 2 +#define JS_N64_START 3 +#define JS_N64_UP 4 +#define JS_N64_DOWN 5 +#define JS_N64_LEFT 6 +#define JS_N64_RIGHT 7 +#define JS_N64_UNUSED1 8 +#define JS_N64_UNUSED2 9 +#define JS_N64_L 10 +#define JS_N64_R 11 +#define JS_N64_CU 12 +#define JS_N64_CD 13 +#define JS_N64_CL 14 +#define JS_N64_CR 15 +#define JS_N64_X 23 /* 16 - 23, signed 8-bit int */ +#define JS_N64_Y 31 /* 24 - 31, signed 8-bit int */ + +#define JS_N64_LENGTH 32 /* N64 bit length, not including stop bit */ +#define JS_N64_REQUEST_LENGTH 37 /* transmit request sequence is 9 bits long */ +#define JS_N64_DELAY 133 /* delay between transmit request, and response ready (us) */ +#define JS_N64_REQUEST 0x1dd1111111ULL /* the request data command (encoded for 000000011) */ +#define JS_N64_DWS 3 /* delay between write segments (required for sound playback because of ISA DMA) */ + /* JS_N64_DWS > 24 is known to fail */ +#define JS_N64_POWER_W 0xe2 /* power during write (transmit request) */ +#define JS_N64_POWER_R 0xfd /* power during read */ +#define JS_N64_OUT 0x1d /* output bits to the 4 pads */ + /* Reading the main axes of any N64 pad is known to fail if the corresponding bit */ + /* in JS_N64_OUT is pulled low on the output port (by any routine) for more */ + /* than 0.123 consecutive ms */ +#define JS_N64_CLOCK 0x02 /* clock bits for read */ + +/* + * js_n64_read_packet() reads an N64 packet. + * Each pad uses one bit per byte. So all pads connected to this port are read in parallel. + */ + +static void js_n64_read_packet(struct js_console_info *info, unsigned char *data) +{ + int i; + unsigned long flags; + +/* + * Request the pad to transmit data + */ + + save_flags(flags); + cli(); + for (i = 0; i < JS_N64_REQUEST_LENGTH; i++) { + JS_PAR_DATA_OUT(JS_N64_POWER_W | ((JS_N64_REQUEST >> i) & 1 ? JS_N64_OUT : 0), info->port); + udelay(JS_N64_DWS); } + restore_flags(flags); + +/* + * Wait for the pad response to be loaded into the 33-bit register of the adapter + */ + + udelay(JS_N64_DELAY); + +/* + * Grab data (ignoring the last bit, which is a stop bit) + */ + + for (i = 0; i < JS_N64_LENGTH; i++) { + JS_PAR_DATA_OUT(JS_N64_POWER_R, info->port); + data[i] = JS_PAR_STATUS(info->port); + JS_PAR_DATA_OUT(JS_N64_POWER_R | JS_N64_CLOCK, info->port); + } + +/* + * We must wait ~0.2 ms here for the controller to reinitialize before the next read request. + * No worries as long as js_console_read is polled less frequently than this. + */ + } /* @@ -161,7 +253,9 @@ for (i = 0; i < length; i++) { JS_PAR_DATA_OUT(~(1 << i), info->port); data[i] = JS_PAR_STATUS(info->port) ^ ~JS_PAR_STATUS_INVERT; + printk(" %d", data[i]); } + printk("\n"); } /* @@ -169,27 +263,28 @@ */ #define JS_PSX_DELAY 10 +#define JS_PSX_LENGTH 8 /* talk to the controller in bytes */ -#define JS_PSX_LENGTH 8 - -#define JS_PSX_NORMAL 0x41 -#define JS_PSX_NEGCON 0x23 -#define JS_PSX_MOUSE 0x12 - -#define JS_PSX_SELBUT 0x01 +#define JS_PSX_NORMAL 0x41 /* Standard Digital controller */ +#define JS_PSX_NEGCON 0x23 /* NegCon pad */ +#define JS_PSX_MOUSE 0x12 /* PSX Mouse */ +#define JS_PSX_ANALOGR 0x73 /* Analog controller in Red mode */ +#define JS_PSX_ANALOGG 0x53 /* Analog controller in Green mode */ + +#define JS_PSX_JOYR 0x02 /* These are for the Analog/Dual Shock controller in RED mode */ +#define JS_PSX_JOYL 0x04 /* I'm not sure the exact purpose of these but its in the docs */ +#define JS_PSX_SELBUT 0x01 /* Standard buttons on almost all PSX controllers. */ #define JS_PSX_START 0x08 -#define JS_PSX_UP 0x10 +#define JS_PSX_UP 0x10 /* Digital direction pad */ #define JS_PSX_RIGHT 0x20 #define JS_PSX_DOWN 0x40 #define JS_PSX_LEFT 0x80 -#define JS_PSX_CLOCK 0x01 -#define JS_PSX_COMMAND 0x02 -#define JS_PSX_POWER 0xf8 -#define JS_PSX_NOPOWER 0x04 -#define JS_PSX_SELECT 0x08 - -#define JS_PSX_CTRL_OUT(X,Y) JS_PAR_CTRL_OUT((X)^0x0f, Y) +#define JS_PSX_CLOCK 0x04 /* Pin 3 */ +#define JS_PSX_COMMAND 0x01 /* Pin 1 */ +#define JS_PSX_POWER 0xf8 /* Pins 5-9 */ +#define JS_PSX_SELECT 0x02 /* Pin 2 */ +#define JS_PSX_NOPOWER 0x04 /* * js_psx_command() writes 8bit command and reads 8bit data from @@ -202,11 +297,11 @@ cmd = (b&1)?JS_PSX_COMMAND:0; for (i=0; i<8; i++) { - JS_PSX_CTRL_OUT(cmd, info->port); + JS_PAR_DATA_OUT(cmd | JS_PSX_POWER, info->port); udelay(JS_PSX_DELAY); ret |= ((JS_PAR_STATUS(info->port) ^ JS_PAR_STATUS_INVERT ) & info->psx) ? (1<port); + JS_PAR_DATA_OUT(cmd | JS_PSX_CLOCK | JS_PSX_POWER, info->port); udelay(JS_PSX_DELAY); b >>= 1; } @@ -228,7 +323,7 @@ JS_PAR_DATA_OUT(JS_PSX_POWER, info->port); - JS_PSX_CTRL_OUT(JS_PSX_CLOCK | JS_PSX_SELECT, info->port); /* Select pad */ + JS_PAR_DATA_OUT(JS_PSX_CLOCK | JS_PSX_SELECT | JS_PSX_POWER, info->port); /* Select pad */ udelay(JS_PSX_DELAY*2); js_psx_command(info, 0x01); /* Access pad */ ret = js_psx_command(info, 0x42); /* Get device id */ @@ -237,7 +332,7 @@ data[i]=js_psx_command(info, 0); else ret = -1; - JS_PSX_CTRL_OUT(JS_PSX_SELECT | JS_PSX_CLOCK, info->port); + JS_PAR_DATA_OUT(JS_PSX_SELECT | JS_PSX_CLOCK | JS_PSX_POWER, info->port); __restore_flags(flags); return ret; @@ -248,14 +343,14 @@ * js_console_read() reads and analyzes console pads data. */ -#define JS_MAX_LENGTH JS_SNES_LENGTH +#define JS_MAX_LENGTH JS_N64_LENGTH static int js_console_read(void *xinfo, int **axes, int **buttons) { struct js_console_info *info = xinfo; unsigned char data[JS_MAX_LENGTH]; - int i, s; + int i, j, s; int n = 0; /* @@ -268,24 +363,68 @@ for (i = 0; i < 5; i++) { s = status_bit[i]; + n = info->pad_to_device[i]; if (info->nes & s) { axes[n][0] = (data[JS_SNES_RIGHT]&s?1:0) - (data[JS_SNES_LEFT]&s?1:0); axes[n][1] = (data[JS_SNES_DOWN] &s?1:0) - (data[JS_SNES_UP] &s?1:0); - buttons[n][0] = ((data[JS_NES_A] &s)?1:0) | ((data[JS_NES_B] &s)?2:0) - | ((data[JS_NES_START]&s)?4:0) | ((data[JS_NES_SELECT]&s)?8:0); - - n++; + buttons[n][0] = (data[JS_NES_A] &s?1:0) | (data[JS_NES_B] &s?2:0) + | (data[JS_NES_START]&s?4:0) | (data[JS_NES_SELECT]&s?8:0); } else if (info->snes & s) { axes[n][0] = (data[JS_SNES_RIGHT]&s?1:0) - (data[JS_SNES_LEFT]&s?1:0); axes[n][1] = (data[JS_SNES_DOWN] &s?1:0) - (data[JS_SNES_UP] &s?1:0); - buttons[n][0] = ((data[JS_SNES_A] &s)?0x01:0) | ((data[JS_SNES_B] &s)?0x02:0) - | ((data[JS_SNES_X] &s)?0x04:0) | ((data[JS_SNES_Y] &s)?0x08:0) - | ((data[JS_SNES_L] &s)?0x10:0) | ((data[JS_SNES_R] &s)?0x20:0) - | ((data[JS_SNES_START]&s)?0x40:0) | ((data[JS_SNES_SELECT]&s)?0x80:0); - n++; + buttons[n][0] = (data[JS_SNES_A] &s?0x01:0) | (data[JS_SNES_B] &s?0x02:0) + | (data[JS_SNES_X] &s?0x04:0) | (data[JS_SNES_Y] &s?0x08:0) + | (data[JS_SNES_L] &s?0x10:0) | (data[JS_SNES_R] &s?0x20:0) + | (data[JS_SNES_START]&s?0x40:0) | (data[JS_SNES_SELECT]&s?0x80:0); + } + } + } + +/* + * N64 pads + */ + + if (info->n64) { + if ( (info->nes || info->snes) && (info->n64 & status_bit[0]) ) { + /* SNES/NES compatibility */ + udelay(240); /* 200 us delay + 20% tolerance */ + } + + js_n64_read_packet(info, data); + + for (i = 0; i < 5; i++) { + s = status_bit[i]; + n = info->pad_to_device[i]; + if (info->n64 & s & ~(data[JS_N64_UNUSED1] | data[JS_N64_UNUSED2])) { + + buttons[n][0] = ( ((data[JS_N64_A]&s) ? 0x01:0) | ((data[JS_N64_B] & s ) ? 0x02:0) + | ((data[JS_N64_Z]&s) ? 0x04:0) | ((data[JS_N64_L] & s ) ? 0x08:0) + | ((data[JS_N64_R]&s) ? 0x10:0) | ((data[JS_N64_START]&s)? 0x20:0) + | ((data[JS_N64_CU]&s)? 0x40:0) | ((data[JS_N64_CR]&s) ? 0x80:0) + | ((data[JS_N64_CD]&s)?0x100:0) | ((data[JS_N64_CL]&s) ?0x200:0) ); + + if (info->n64_dpp & s) { + buttons[n][0] |= ((data[JS_N64_LEFT]&s) ? 0x400:0) | ((data[JS_N64_UP] & s)? 0x800:0) + |((data[JS_N64_RIGHT]&s)?0x1000:0) | ((data[JS_N64_DOWN]&s)?0x2000:0); + } else { + axes[n][2] = (data[JS_N64_RIGHT]&s?1:0) - (data[JS_N64_LEFT]&s?1:0); + axes[n][3] = (data[JS_N64_DOWN] &s?1:0) - (data[JS_N64_UP] &s?1:0); + } + + /* build int from bits of signed 8-bit int's */ + j = 7; + axes[n][0] = (data[JS_N64_X - j] & s) ? ~0x7f : 0; + axes[n][1] = (data[JS_N64_Y - j] & s) ? ~0x7f : 0; + while ( j-- > 0 ) { + axes[n][0] |= (data[JS_N64_X - j] & s) ? (1 << j) : 0; + axes[n][1] |= (data[JS_N64_Y - j] & s) ? (1 << j) : 0; + } + /* flip Y-axis for conformity */ + axes[n][1] = -axes[n][1]; + } } } @@ -300,21 +439,18 @@ for (i = 0; i < 5; i++) { s = status_bit[i]; + n = info->pad_to_device[i]; if (info->multi & s) { axes[n][0] = (data[JS_MULTI_RIGHT]&s?1:0) - (data[JS_MULTI_LEFT]&s?1:0); axes[n][1] = (data[JS_MULTI_DOWN] &s?1:0) - (data[JS_MULTI_UP] &s?1:0); buttons[n][0] = (data[JS_MULTI_BUTTON]&s)?1:0; - - n++; } else if (info->multi2 & s) { axes[n][0] = (data[JS_MULTI_RIGHT]&s?1:0) - (data[JS_MULTI_LEFT]&s?1:0); axes[n][1] = (data[JS_MULTI_DOWN] &s?1:0) - (data[JS_MULTI_UP] &s?1:0); buttons[n][0] = (data[JS_MULTI_BUTTON]&s)?1:0 | (data[JS_MULTI_BUTTON2]&s)?2:0; - - n++; } } } @@ -323,18 +459,44 @@ * PSX controllers */ - if (info->psx && (js_psx_read_packet(info, 2, data) == JS_PSX_NORMAL)) { /* FIXME? >1 PSX pads? */ + if (info->psx) { + + for ( i = 0; i < 5; i++ ) + if ( info->psx & status_bit[i] ) { + n = info->pad_to_device[i]; + break; + } + + buttons[n][0] = 0; + + switch (js_psx_read_packet(info, 6, data)) { - axes[n][0] = (data[0]&JS_PSX_RIGHT?0:1) - (data[0]&JS_PSX_LEFT?0:1); - axes[n][1] = (data[0]&JS_PSX_DOWN ?0:1) - (data[0]&JS_PSX_UP ?0:1); + case JS_PSX_ANALOGR: - buttons[n][0] = ((~data[1]&0xf)<<4) | ((~data[1]&0xf0)>>4) | - (data[0]&JS_PSX_START?0:0x200) | (data[0]&JS_PSX_SELBUT?0:0x100); + buttons[n][0] |= (data[0]&JS_PSX_JOYL?0:0x800) | (data[0]&JS_PSX_JOYR?0:0x400); - n++; + case JS_PSX_ANALOGG: + + axes[n][2] = data[2]; + axes[n][3] = data[3]; + axes[n][4] = data[4]; + axes[n][5] = data[5]; + + case JS_PSX_NORMAL: + case JS_PSX_NEGCON: + + axes[n][0] = (data[0]&JS_PSX_RIGHT?0:1) - (data[0]&JS_PSX_LEFT?0:1); + axes[n][1] = (data[0]&JS_PSX_DOWN ?0:1) - (data[0]&JS_PSX_UP ?0:1); + + buttons[n][0] |= ((~data[1]&0xf)<<4) | ((~data[1]&0xf0)>>4) | + (data[0]&JS_PSX_START?0:0x200) | (data[0]&JS_PSX_SELBUT?0:0x100); + + break; + + } } - return -(n != info->pads); + return 0; } /* @@ -343,10 +505,8 @@ int js_console_open(struct js_dev *dev) { -#ifdef USE_PARPORT struct js_console_info *info = dev->port->info; if (!MOD_IN_USE && parport_claim(info->port)) return -EBUSY; -#endif MOD_INC_USE_COUNT; return 0; } @@ -357,13 +517,9 @@ int js_console_close(struct js_dev *dev) { -#ifdef USE_PARPORT struct js_console_info *info = dev->port->info; -#endif MOD_DEC_USE_COUNT; -#ifdef USE_PARPORT if (!MOD_IN_USE) parport_release(info->port); -#endif return 0; } @@ -373,16 +529,12 @@ struct js_console_info *info; int i; - while (js_console_port != NULL) { + while (js_console_port) { for (i = 0; i < js_console_port->ndevs; i++) - if (js_console_port->devs[i] != NULL) + if (js_console_port->devs[i]) js_unregister_device(js_console_port->devs[i]); info = js_console_port->info; -#ifdef USE_PARPORT parport_unregister_device(info->port); -#else - release_region(info->port, 3); -#endif js_console_port = js_unregister_port(js_console_port); } } @@ -393,7 +545,7 @@ * console gamepads. */ -static void __init js_console_init_corr(int num_axes, struct js_corr *corr) +static void __init js_console_init_corr(int num_axes, int type, struct js_corr *corr) { int i; @@ -405,6 +557,28 @@ corr[i].coef[2] = (1 << 29); corr[i].coef[3] = (1 << 29); } + + if (type == JS_N64_PAD || type == JS_N64_PAD_DPP) { + for (i = 0; i < 2; i++) { + corr[i].type = JS_CORR_BROKEN; + corr[i].prec = 0; + corr[i].coef[0] = 0; + corr[i].coef[1] = 0; + corr[i].coef[2] = (1 << 22); + corr[i].coef[3] = (1 << 22); + } + } + + if (type == JS_PSX_ANALOGG || type == JS_PSX_ANALOGR) { + for (i = 2; i < 6; i++) { + corr[i].type = JS_CORR_BROKEN; + corr[i].prec = 0; + corr[i].coef[0] = 127 - 2; + corr[i].coef[1] = 128 + 2; + corr[i].coef[2] = (1 << 29) / (127 - 4); + corr[i].coef[3] = (1 << 29) / (127 - 4); + } + } } /* @@ -415,45 +589,40 @@ static struct js_port __init *js_console_probe(int *config, struct js_port *port) { char *name[5]; - int i, psx, axes[5], buttons[5]; + int i, psx, axes[5], buttons[5], type[5]; unsigned char data[2]; /* used for PSX probe */ struct js_console_info info; + struct parport *pp; memset(&info, 0, sizeof(struct js_console_info)); if (config[0] < 0) return port; -#ifdef USE_PARPORT - { - struct parport *pp; - - if (config[0] > 0x10) - for (pp=parport_enumerate(); pp != NULL && (pp->base!=config[0]); pp=pp->next); - else - for (pp=parport_enumerate(); pp != NULL && (config[0]>0); pp=pp->next) config[0]--; - - if (pp == NULL) { - printk(KERN_ERR "joy-console: no such parport\n"); - return port; - } + if (config[0] > 0x10) + for (pp=parport_enumerate(); pp && (pp->base!=config[0]); pp=pp->next); + else + for (pp=parport_enumerate(); pp && (config[0]>0); pp=pp->next) config[0]--; - info.port = parport_register_device(pp, "joystick (console)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); - if (!info.port) - return port; + if (!pp) { + printk(KERN_ERR "joy-console: no such parport\n"); + return port; } + info.port = parport_register_device(pp, "joystick (console)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); + if (!info.port) + return port; + if (parport_claim(info.port)) { parport_unregister_device(info.port); /* port currently not available ... */ return port; } -#else - info.port = config[0]; - if (check_region(info.port, 3)) return port; - request_region(info.port, 3, "joystick (console pad)"); -#endif - for (i = 0; i < 5; i++) + for (i = 0; i < 5; i++) { + + type[info.pads] = config[i+1]; + info.pad_to_device[i] = info.pads; + switch(config[i+1]) { case JS_NO_PAD: @@ -478,6 +647,23 @@ info.pads++; break; + case JS_N64_PAD: + axes[info.pads] = 4; + buttons[info.pads] = 10; + name[info.pads] = "N64 pad"; + info.n64 |= status_bit[i]; + info.pads++; + break; + + case JS_N64_PAD_DPP: + axes[info.pads] = 2; + buttons[info.pads] = 14; + name[info.pads] = "N64 pad (DPP mode)"; + info.n64 |= status_bit[i]; + info.n64_dpp |= status_bit[i]; + info.pads++; + break; + case JS_MULTI_STICK: axes[info.pads] = 2; @@ -497,31 +683,58 @@ break; case JS_PSX_PAD: - + info.psx |= status_bit[i]; psx = js_psx_read_packet(&info, 2, data); psx = js_psx_read_packet(&info, 2, data); info.psx &= ~status_bit[i]; + type[i] = psx; + switch(psx) { case JS_PSX_NORMAL: axes[info.pads] = 2; buttons[info.pads] = 10; - name[info.pads] = "PSX controller"; + name[info.pads] = "PSX pad"; info.psx |= status_bit[i]; info.pads++; break; + + case JS_PSX_ANALOGR: + axes[info.pads] = 6; + buttons[info.pads] = 12; + name[info.pads] = "Analog Red PSX pad"; + info.psx |= status_bit[i]; + info.pads++; + break; + + case JS_PSX_ANALOGG: + axes[info.pads] = 6; + buttons[info.pads] = 10; + name[info.pads] = "Analog Green PSX pad"; + info.psx |= status_bit[i]; + info.pads++; + break; + case JS_PSX_NEGCON: - printk(KERN_WARNING "joy-console: NegCon not yet supported...\n"); + axes[info.pads] = 2; + buttons[info.pads] = 10; + name[info.pads] = "NegCon PSX pad"; + info.psx |= status_bit[i]; + info.pads++; break; + case JS_PSX_MOUSE: - printk(KERN_WARNING "joy-console: PSX mouse not supported...\n"); + printk(KERN_WARNING "joy-psx: PSX mouse not supported...\n"); break; + case -1: - printk(KERN_ERR "joy-console: no PSX controller found...\n"); + printk(KERN_ERR "joy-psx: no PSX controller found...\n"); break; + default: - printk(KERN_WARNING "joy-console: unknown PSX controller 0x%x\n", psx); + printk(KERN_WARNING "joy-psx: PSX controller unknown: 0x%x," + " please report to .\n", psx); } break; @@ -529,52 +742,53 @@ printk(KERN_WARNING "joy-console: pad type %d unknown\n", config[i+1]); } + } if (!info.pads) { -#ifdef USE_PARPORT parport_release(info.port); parport_unregister_device(info.port); -#else - release_region(info.port, 3); -#endif return port; } port = js_register_port(port, &info, info.pads, sizeof(struct js_console_info), js_console_read); for (i = 0; i < info.pads; i++) { -#ifdef USE_PARPORT printk(KERN_INFO "js%d: %s on %s\n", js_register_device(port, i, axes[i], buttons[i], name[i], js_console_open, js_console_close), name[i], info.port->port->name); -#else - printk(KERN_INFO "js%d: %s at %#x\n", - js_register_device(port, i, axes[i], buttons[i], name[i], js_console_open, js_console_close), - name[i], info.port); -#endif - js_console_init_corr(axes[i], port->corr[i]); + js_console_init_corr(axes[i], type[i], port->corr[i]); } -#ifdef USE_PARPORT parport_release(info.port); -#endif return port; } #ifndef MODULE -void __init js_console_setup(char *str, int *ints) +int __init js_console_setup(SETUP_PARAM) { int i; - - if (!strcmp(str,"js_console")) - for (i = 0; i <= ints[0] && i < 6; i++) js_console[i] = ints[i+1]; - if (!strcmp(str,"js_console2")) - for (i = 0; i <= ints[0] && i < 6; i++) js_console2[i] = ints[i+1]; - if (!strcmp(str,"js_console3")) - for (i = 0; i <= ints[0] && i < 6; i++) js_console3[i] = ints[i+1]; - + SETUP_PARSE(6); + for (i = 0; i <= ints[0] && i < 6; i++) js_console[i] = ints[i+1]; + return 1; } +int __init js_console_setup_2(SETUP_PARAM) +{ + int i; + SETUP_PARSE(6); + for (i = 0; i <= ints[0] && i < 6; i++) js_console_2[i] = ints[i+1]; + return 1; +} +int __init js_console_setup_3(SETUP_PARAM) +{ + int i; + SETUP_PARSE(6); + for (i = 0; i <= ints[0] && i < 6; i++) js_console_3[i] = ints[i+1]; + return 1; +} +__setup("js_console=", js_console_setup); +__setup("js_console_2=", js_console_setup_2); +__setup("js_console_3=", js_console_setup_3); #endif #ifdef MODULE @@ -584,8 +798,8 @@ #endif { js_console_port = js_console_probe(js_console, js_console_port); - js_console_port = js_console_probe(js_console2, js_console_port); - js_console_port = js_console_probe(js_console3, js_console_port); + js_console_port = js_console_probe(js_console_2, js_console_port); + js_console_port = js_console_probe(js_console_3, js_console_port); if (js_console_port) return 0; diff -u --recursive --new-file v2.2.13/linux/drivers/char/joystick/joy-creative.c linux/drivers/char/joystick/joy-creative.c --- v2.2.13/linux/drivers/char/joystick/joy-creative.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/joystick/joy-creative.c Tue Jan 4 10:12:14 2000 @@ -0,0 +1,287 @@ +/* + * joy-creative.c Version 1.2 + * + * Copyright (c) 1999 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * This is a module for the Linux joystick driver, supporting + * Creative Labs Blaster gamepad family. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define JS_CR_MAX_STROBE 100 /* 100 us max wait for first strobe */ +#define JS_CR_LENGTH 36 + +#define JS_CR_MODE_BGPC 8 + +static int js_cr_port_list[] __initdata = {0x201, 0}; +static struct js_port* js_cr_port __initdata = NULL; + +struct js_cr_info { + int io; + unsigned char mode[2]; +}; + +/* + * js_cr_read_packet() reads a Blaster gamepad packet. + */ + +static int js_cr_read_packet(int io, unsigned int *data) +{ + unsigned long flags; + unsigned char u, v, w; + __u64 buf[2]; + int r[2], t[2], p[2]; + int i, j, ret; + + for (i = 0; i < 2; i++); { + r[i] = buf[i] = 0; + p[i] = t[i] = JS_CR_MAX_STROBE; + p[i] += JS_CR_MAX_STROBE; + } + + __save_flags(flags); + __cli(); + + u = inb(io); + + do { + t[0]--; t[1]--; + v = inb(io); + for (i = 0, w = u ^ v; i < 2 && w; i++, w >>= 2) + if (w & 0x30) { + if ((w & 0x30) < 0x30 && r[i] < JS_CR_LENGTH && t[i] > 0) { + buf[i] |= (__u64)((w >> 5) & 1) << r[i]++; + p[i] = t[i] = (p[i] - t[i]) << 1; + u = v; + } else t[i] = 0; + } + } while (t[0] > 0 || t[1] > 0); + + __restore_flags(flags); + + + ret = 0; + + for (i = 0; i < 2; i++) { + + if (r[i] != JS_CR_LENGTH) continue; + + for (j = 0; j < JS_CR_LENGTH && (buf[i] & 0x04104107f) ^ 0x041041040; j++) + buf[i] = (buf[i] >> 1) | ((__u64)(buf[i] & 1) << (JS_CR_LENGTH - 1)); + + if (j < JS_CR_LENGTH) ret |= (1 << i); + + data[i] = ((buf[i] >> 7) & 0x000001f) | ((buf[i] >> 8) & 0x00003e0) + | ((buf[i] >> 9) & 0x0007c00) | ((buf[i] >> 10) & 0x00f8000) + | ((buf[i] >> 11) & 0x1f00000); + + } + + return ret; +} + +/* + * js_cr_read() reads and analyzes Blaster gamepad data. + */ + +static int js_cr_read(void *xinfo, int **axes, int **buttons) +{ + struct js_cr_info *info = xinfo; + unsigned int data[2]; + int i, r; + + if (!(r = js_cr_read_packet(info->io, data))) + return -1; + + for (i = 0; i < 2; i++) + if (r & (1 << i)) { + switch (info->mode[i]) { + + case JS_CR_MODE_BGPC: + + axes[i][0] = ((data[i] >> 4) & 1) - ((data[i] >> 3) & 1); + axes[i][1] = ((data[i] >> 2) & 1) - ((data[i] >> 1) & 1); + + buttons[i][0] = ((data[i] >> 12) & 0x007) | ((data[i] >> 6) & 0x038) + | ((data[i] >> 1) & 0x0c0) | ((data[i] >> 7) & 0x300) + | ((data[i] << 5) & 0xc00); + + break; + + default: + break; + + } + } + + return 0; +} + +/* + * js_cr_open() is a callback from the file open routine. + */ + +static int js_cr_open(struct js_dev *jd) +{ + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_cr_close() is a callback from the file release routine. + */ + +static int js_cr_close(struct js_dev *jd) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_cr_init_corr() initializes correction values of + * Blaster gamepads. + */ + +static void __init js_cr_init_corr(int mode, struct js_corr *corr) +{ + int i; + + switch (mode) { + + case JS_CR_MODE_BGPC: + + for (i = 0; i < 2; i++) { + corr[i].type = JS_CORR_BROKEN; + corr[i].prec = 0; + corr[i].coef[0] = 0; + corr[i].coef[1] = 0; + corr[i].coef[2] = (1 << 29); + corr[i].coef[3] = (1 << 29); + } + + break; + + } +} + +/* + * js_cr_probe() probes for Blaster gamepads. + */ + +static struct js_port __init *js_cr_probe(int io, struct js_port *port) +{ + struct js_cr_info info; + char *names[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "Blaster GamePad Cobra" }; + char axes[] = { 0, 0, 0, 0, 0, 0, 0, 0, 2 }; + char buttons[] = { 0, 0, 0, 0, 0, 0, 0, 0, 12 }; + unsigned int data[2]; + int i, r; + + if (check_region(io, 1)) return port; + + info.mode[0] = info.mode[1] = 0; + + if (!(r = js_cr_read_packet(io, data))) + return port; + + for (i = 0; i < 2; i++) { + if (r & (1 << i)) { + if (~data[i] & 1) { + info.mode[i] = JS_CR_MODE_BGPC; + } else { + info.mode[i] = (data[i] >> 2) & 7; + } + if (!names[info.mode[i]]) { + printk(KERN_WARNING "joy-creative: Unknown Creative device %d at %#x\n", + info.mode[i], io); + info.mode[i] = 0; + } + } + } + + if (!info.mode[0] && !info.mode[1]) return port; + + info.io = io; + + request_region(io, 1, "joystick (creative)"); + port = js_register_port(port, &info, 2, sizeof(struct js_cr_info), js_cr_read); + + for (i = 0; i < 2; i++) + if (info.mode[i]) { + printk(KERN_INFO "js%d: %s at %#x\n", + js_register_device(port, i, axes[info.mode[i]], buttons[info.mode[i]], + names[info.mode[i]], js_cr_open, js_cr_close), + names[info.mode[i]], io); + js_cr_init_corr(info.mode[i], port->corr[i]); + } + + return port; +} + +#ifdef MODULE +int init_module(void) +#else +int __init js_cr_init(void) +#endif +{ + int *p; + + for (p = js_cr_port_list; *p; p++) js_cr_port = js_cr_probe(*p, js_cr_port); + if (js_cr_port) return 0; + +#ifdef MODULE + printk(KERN_WARNING "joy-creative: no joysticks found\n"); +#endif + + return -ENODEV; +} + +#ifdef MODULE +void cleanup_module(void) +{ + int i; + struct js_cr_info *info; + + while (js_cr_port) { + for (i = 0; i < js_cr_port->ndevs; i++) + if (js_cr_port->devs[i]) + js_unregister_device(js_cr_port->devs[i]); + info = js_cr_port->info; + release_region(info->io, 1); + js_cr_port = js_unregister_port(js_cr_port); + } +} +#endif diff -u --recursive --new-file v2.2.13/linux/drivers/char/joystick/joy-db9.c linux/drivers/char/joystick/joy-db9.c --- v2.2.13/linux/drivers/char/joystick/joy-db9.c Tue Dec 1 19:05:05 1998 +++ linux/drivers/char/joystick/joy-db9.c Tue Jan 4 10:12:14 2000 @@ -1,7 +1,10 @@ /* - * joy-db9.c Version 0.5V + * joy-db9.c Version 0.6V * - * Copyright (c) 1998 Andree Borrmann + * Copyright (c) 1998 Andree Borrmann + * Copyright (c) 1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* @@ -24,6 +27,10 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic */ #include @@ -35,8 +42,9 @@ #include #include #include +#include -MODULE_AUTHOR("Andree Borrmann "); +MODULE_AUTHOR("Vojtech Pavlik "); MODULE_PARM(js_db9, "2i"); MODULE_PARM(js_db9_2, "2i"); MODULE_PARM(js_db9_3, "2i"); @@ -48,7 +56,8 @@ #define JS_GENESIS6_PAD 0x06 #define JS_SATURN_PAD 0x07 #define JS_MULTI_0802 0x08 -#define JS_MAX_PAD 0x09 +#define JS_MULTI_0802_2 0x09 +#define JS_MAX_PAD 0x0A #define JS_DB9_UP 0x01 #define JS_DB9_DOWN 0x02 @@ -59,8 +68,8 @@ #define JS_DB9_FIRE3 0x40 #define JS_DB9_FIRE4 0x80 -#define JS_DB9_NORMAL 0x22 -#define JS_DB9_NOSELECT 0x20 +#define JS_DB9_NORMAL 0x2a +#define JS_DB9_NOSELECT 0x28 #define JS_DB9_SATURN0 0x20 #define JS_DB9_SATURN1 0x22 @@ -76,11 +85,7 @@ static int js_db9_3[] __initdata = { -1, 0 }; struct js_db9_info { -#ifdef USE_PARPORT struct pardevice *port; /* parport device */ -#else - int port; /* hw port */ -#endif int mode; /* pad mode */ }; @@ -95,6 +100,15 @@ switch(info->mode) { + case JS_MULTI_0802_2: + + data = JS_PAR_DATA_IN(info->port) >> 3; + + axes[1][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); + axes[1][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); + + buttons[1][0] = (data&JS_DB9_FIRE1?0:1); + case JS_MULTI_0802: data = JS_PAR_STATUS(info->port) >> 3; @@ -185,11 +199,12 @@ udelay(JS_GENESIS6_DELAY); JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); /* 3 */ udelay(JS_GENESIS6_DELAY); - JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); data=JS_PAR_DATA_IN(info->port); - buttons[0][0] |= (data&JS_DB9_LEFT?0:0x10) | (data&JS_DB9_DOWN?0:0x20) | (data&JS_DB9_UP?0:0x40); + buttons[0][0] |= (data&JS_DB9_LEFT?0:0x10) | (data&JS_DB9_DOWN ?0:0x20) | + (data&JS_DB9_UP ?0:0x40) | (data&JS_DB9_RIGHT?0:0x80); + JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); udelay(JS_GENESIS6_DELAY); JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); /* 4 */ udelay(JS_GENESIS6_DELAY); @@ -236,11 +251,11 @@ struct js_db9_info *info = dev->port->info; if (!MOD_IN_USE) { -#ifdef USE_PARPORT if (parport_claim(info->port)) return -EBUSY; -#endif - JS_PAR_ECTRL_OUT(0x35,info->port); /* enable PS/2 mode: */ + JS_PAR_DATA_OUT(0xff, info->port); + if (info->mode != JS_MULTI_0802) + JS_PAR_ECTRL_OUT(0x35,info->port); /* enable PS/2 mode: */ JS_PAR_CTRL_OUT(JS_DB9_NORMAL,info->port); /* reverse direction, enable Select signal */ } @@ -260,12 +275,11 @@ if (!MOD_IN_USE) { - JS_PAR_CTRL_OUT(0x00,info->port); /* normal direction */ - JS_PAR_ECTRL_OUT(0x15,info->port); /* enable normal mode */ + JS_PAR_CTRL_OUT(0x00,info->port); /* normal direction */ + if (info->mode != JS_MULTI_0802) + JS_PAR_ECTRL_OUT(0x15,info->port); /* enable normal mode */ -#ifdef USE_PARPORT parport_release(info->port); -#endif } return 0; } @@ -274,16 +288,15 @@ void cleanup_module(void) { struct js_db9_info *info; + int i; - while (js_db9_port != NULL) { - js_unregister_device(js_db9_port->devs[0]); + while (js_db9_port) { info = js_db9_port->info; -#ifdef USE_PARPORT + + for (i = 0; i < js_db9_port->ndevs; i++) + if (js_db9_port->devs[i]) + js_unregister_device(js_db9_port->devs[i]); parport_unregister_device(info->port); -#else - release_region(info->port, 3); - release_region(info->port+0x402, 1); -#endif js_db9_port = js_unregister_port(js_db9_port); } @@ -295,17 +308,17 @@ * db9 gamepads. */ -static void __init js_db9_init_corr(struct js_corr **corr) +static void __init js_db9_init_corr(struct js_corr *corr) { int i; for (i = 0; i < 2; i++) { - corr[0][i].type = JS_CORR_BROKEN; - corr[0][i].prec = 0; - corr[0][i].coef[0] = 0; - corr[0][i].coef[1] = 0; - corr[0][i].coef[2] = (1 << 29); - corr[0][i].coef[3] = (1 << 29); + corr[i].type = JS_CORR_BROKEN; + corr[i].prec = 0; + corr[i].coef[0] = 0; + corr[i].coef[1] = 0; + corr[i].coef[2] = (1 << 29); + corr[i].coef[3] = (1 << 29); } } @@ -316,75 +329,76 @@ static struct js_port __init *js_db9_probe(int *config, struct js_port *port) { struct js_db9_info info; - char buttons[JS_MAX_PAD] = {0,1,2,4,0,6,7,8,1}; + struct parport *pp; + int i; + char buttons[JS_MAX_PAD] = {0,1,2,4,0,6,8,8,1,1}; char *name[JS_MAX_PAD] = {NULL, "Multisystem joystick", "Multisystem joystick (2 fire)", "Genesis pad", - NULL, "Genesis 5 pad", "Genesis 6 pad", "Saturn pad", "Multisystem (0.8.0.2) joystick"}; + NULL, "Genesis 5 pad", "Genesis 6 pad", "Saturn pad", "Multisystem (0.8.0.2) joystick", + "Multisystem (0.8.0.2-dual) joystick"}; if (config[0] < 0) return port; if (config[1] < 0 || config[1] >= JS_MAX_PAD || !name[config[1]]) return port; -#ifdef USE_PARPORT - { - struct parport *pp; + info.mode = config[1]; - if (config[0] > 0x10) - for (pp=parport_enumerate(); pp != NULL && (pp->base!=config[0]); pp=pp->next); - else - for (pp=parport_enumerate(); pp != NULL && (config[0]>0); pp=pp->next) config[0]--; - - if (pp == NULL) { - printk(KERN_ERR "joy-db9: no such parport\n"); - return port; - } - - if (!(pp->modes & (PARPORT_MODE_PCPS2 | PARPORT_MODE_PCECPPS2))) { - printk(KERN_ERR "js-db9: specified parport is not bidirectional\n"); - return port; - } - - info.port = parport_register_device(pp, "joystick (db9)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); - if (!info.port) - return port; + if (config[0] > 0x10) + for (pp=parport_enumerate(); pp && (pp->base!=config[0]); pp=pp->next); + else + for (pp=parport_enumerate(); pp && (config[0]>0); pp=pp->next) config[0]--; + + if (!pp) { + printk(KERN_ERR "joy-db9: no such parport\n"); + return port; } -#else - info.port = config[0]; - if (check_region(info.port, 3) || check_region(info.port+0x402,1)) return port; - request_region(info.port, 3, "joystick (db9)"); - request_region(info.port+0x402, 1, "joystick (db9)"); -#endif - info.mode = config[1]; + if (!(pp->modes & (PARPORT_MODE_PCPS2 | PARPORT_MODE_PCECPPS2)) && info.mode != JS_MULTI_0802) { + printk(KERN_ERR "js-db9: specified parport is not bidirectional\n"); + return port; + } - port = js_register_port(port, &info, 1, sizeof(struct js_db9_info), js_db9_read); + info.port = parport_register_device(pp, "joystick (db9)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); + if (!info.port) + return port; + + port = js_register_port(port, &info, 1 + (info.mode == JS_MULTI_0802_2), sizeof(struct js_db9_info), js_db9_read); + + for (i = 0; i < 1 + (info.mode == JS_MULTI_0802_2); i++) { + printk(KERN_INFO "js%d: %s on %s\n", + js_register_device(port, i, 2, buttons[info.mode], name[info.mode], js_db9_open, js_db9_close), + name[info.mode], info.port->port->name); -#ifdef USE_PARPORT - printk(KERN_INFO "js%d: %s on %s\n", - js_register_device(port, 0, 2, buttons[info.mode], name[info.mode], js_db9_open, js_db9_close), - name[info.mode], info.port->port->name); -#else - printk(KERN_INFO "js%d: %s at %#x\n", - js_register_device(port, 0, 2, buttons[info.mode], name[info.mode], js_db9_open, js_db9_close), - name[info.mode], info.port); -#endif + js_db9_init_corr(port->corr[i]); + } - js_db9_init_corr(port->corr); return port; } #ifndef MODULE -void __init js_db9_setup(char *str, int *ints) +int __init js_db9_setup(SETUP_PARAM) { int i; - - if (!strcmp(str,"js_db9")) - for (i = 0; i <= ints[0] && i < 2; i++) js_db9[i] = ints[i+1]; - if (!strcmp(str,"js_db9_2")) - for (i = 0; i <= ints[0] && i < 2; i++) js_db9_2[i] = ints[i+1]; - if (!strcmp(str,"js_db9_3")) - for (i = 0; i <= ints[0] && i < 2; i++) js_db9_3[i] = ints[i+1]; - + SETUP_PARSE(2); + for (i = 0; i <= ints[0] && i < 2; i++) js_db9[i] = ints[i+1]; + return 1; +} +int __init js_db9_setup_2(SETUP_PARAM) +{ + int i; + SETUP_PARSE(2); + for (i = 0; i <= ints[0] && i < 2; i++) js_db9_2[i] = ints[i+1]; + return 1; +} +int __init js_db9_setup_3(SETUP_PARAM) +{ + int i; + SETUP_PARSE(2); + for (i = 0; i <= ints[0] && i < 2; i++) js_db9_3[i] = ints[i+1]; + return 1; } +__setup("js_db9=", js_db9_setup); +__setup("js_db9_2=", js_db9_setup_2); +__setup("js_db9_3=", js_db9_setup_3); #endif #ifdef MODULE diff -u --recursive --new-file v2.2.13/linux/drivers/char/joystick/joy-gravis.c linux/drivers/char/joystick/joy-gravis.c --- v2.2.13/linux/drivers/char/joystick/joy-gravis.c Tue Dec 1 19:05:05 1998 +++ linux/drivers/char/joystick/joy-gravis.c Tue Jan 4 10:12:14 2000 @@ -1,7 +1,9 @@ /* * joy-gravis.c Version 1.2 * - * Copyright (c) 1998 Vojtech Pavlik + * Copyright (c) 1998-1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* @@ -25,7 +27,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: + * e-mail - mail your message to , or by paper mail: * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic */ @@ -37,15 +39,16 @@ #include #include #include +#include #define JS_GR_MODE_GPP 1 #define JS_GR_LENGTH_GPP 24 -#define JS_GR_STROBE_GPP 75 +#define JS_GR_STROBE_GPP 400 #define JS_GR_MODE_XT 2 #define JS_GR_MODE_BD 3 #define JS_GR_LENGTH_XT 4 -#define JS_GR_STROBE_XT 30 +#define JS_GR_STROBE_XT 200 #define JS_GR_MAX_CHUNKS_XT 10 #define JS_GR_MAX_BITS_XT 30 @@ -63,37 +66,36 @@ static int js_gr_gpp_read_packet(int io, int shift, unsigned int *data) { - unsigned int t, t1; + unsigned long flags; unsigned char u, v; + unsigned int t, p; int i; - unsigned long flags; - - int strobe = (js_time_speed * JS_GR_STROBE_GPP) >> 10; i = 0; data[0] = 0; + p = t = JS_GR_STROBE_GPP; + p += JS_GR_STROBE_GPP; __save_flags(flags); __cli(); - u = inb(io) >> shift; - t = js_get_time(); + + v = inb(io) >> shift; do { - v = (inb(io) >> shift) & 3; - t1 = js_get_time(); - if ((u ^ v) & u & 1) { + t--; + u = v; v = (inb(io) >> shift) & 3; + if (~v & u & 1) { data[0] |= (v >> 1) << i++; - t = t1; + p = t = (p - t) << 1; } - u = v; - } while (i < JS_GR_LENGTH_GPP && js_delta(t1,t) < strobe); + } while (i < JS_GR_LENGTH_GPP && t > 0); __restore_flags(flags); if (i < JS_GR_LENGTH_GPP) return -1; for (i = 0; i < JS_GR_LENGTH_GPP && (data[0] & 0xfe4210) ^ 0x7c0000; i++) - data[0] = data[0] >> 1 | (data[0] & 1) << 23; + data[0] = data[0] >> 1 | (data[0] & 1) << (JS_GR_LENGTH_GPP - 1); return -(i == JS_GR_LENGTH_GPP); } @@ -104,35 +106,36 @@ static int js_gr_xt_read_packet(int io, int shift, unsigned int *data) { - unsigned int t, t1; - unsigned char u, v, w; unsigned int i, j, buf, crc; + unsigned char u, v, w; unsigned long flags; + unsigned int t, p; char status; - int strobe = (js_time_speed * JS_GR_STROBE_XT) >> 10; - data[0] = data[1] = data[2] = data[3] = 0; status = buf = i = j = 0; + p = t = JS_GR_STROBE_XT; + p += JS_GR_STROBE_XT; __save_flags(flags); __cli(); v = w = (inb(io) >> shift) & 3; - t = js_get_time(); do { + t--; u = (inb(io) >> shift) & 3; - t1 = js_get_time(); if (u ^ v) { if ((u ^ v) & 1) { + p = t = (p - t) << 2; buf = (buf << 1) | (u >> 1); i++; } else if ((((u ^ v) & (v ^ w)) >> 1) & ~(u | v | w) & 1) { + p = t = (p - t) << 2; if (i == 20) { crc = buf ^ (buf >> 7) ^ (buf >> 14); if (!((crc ^ (0x25cb9e70 >> ((crc >> 2) & 0x1c))) & 0xf)) { @@ -145,12 +148,11 @@ i = 0; } - t = t1; w = v; v = u; } - } while (status != 0xf && i < JS_GR_MAX_BITS_XT && j < JS_GR_MAX_CHUNKS_XT && js_delta(t1,t) < strobe); + } while (status != 0xf && i < JS_GR_MAX_BITS_XT && j < JS_GR_MAX_CHUNKS_XT && t > 0); __restore_flags(flags); @@ -320,7 +322,7 @@ } /* - * js_gr_probe() probes fro GrIP joysticks. + * js_gr_probe() probes for GrIP joysticks. */ static struct js_port __init *js_gr_probe(int io, struct js_port *port) @@ -389,9 +391,9 @@ int i; struct js_gr_info *info; - while (js_gr_port != NULL) { + while (js_gr_port) { for (i = 0; i < js_gr_port->ndevs; i++) - if (js_gr_port->devs[i] != NULL) + if (js_gr_port->devs[i]) js_unregister_device(js_gr_port->devs[i]); info = js_gr_port->info; release_region(info->io, 1); diff -u --recursive --new-file v2.2.13/linux/drivers/char/joystick/joy-lightning.c linux/drivers/char/joystick/joy-lightning.c --- v2.2.13/linux/drivers/char/joystick/joy-lightning.c Tue Dec 1 19:05:05 1998 +++ linux/drivers/char/joystick/joy-lightning.c Tue Jan 4 10:12:14 2000 @@ -1,7 +1,9 @@ /* * joy-lightning.c Version 1.2 * - * Copyright (c) 1998 Vojtech Pavlik + * Copyright (c) 1998-1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* @@ -26,7 +28,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: + * e-mail - mail your message to , or by paper mail: * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic */ @@ -38,6 +40,7 @@ #include #include #include +#include #define JS_L4_PORT 0x201 #define JS_L4_SELECT_ANALOG 0xa4 @@ -52,10 +55,10 @@ static struct js_port* __initdata js_l4_port = NULL; -MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_AUTHOR("Vojtech Pavlik "); MODULE_PARM(js_l4, "2-24i"); -static int js_l4[]={-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0}; +static int __initdata js_l4[] = { -1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0 }; #include "joy-analog.h" @@ -70,11 +73,10 @@ static int js_l4_wait_ready(void) { - unsigned int t, t1, timeout; - timeout = (JS_L4_TIMEOUT * js_time_speed) >> 10; - t = t1 = js_get_time(); - while ((inb(JS_L4_PORT) & JS_L4_BUSY) && (js_delta(t1 = js_get_time(), t) < timeout)); - return -(js_delta(t1, t) >= timeout); + unsigned int t; + t = JS_L4_TIMEOUT; + while ((inb(JS_L4_PORT) & JS_L4_BUSY) && t > 0) t--; + return -(t<=0); } /* @@ -255,14 +257,14 @@ port = js_register_port(port, info, numdev, sizeof(struct js_l4_info), js_l4_read); + info = port->info; + for (i = 0; i < numdev; i++) printk(KERN_INFO "js%d: %s on L4 port %d\n", js_register_device(port, i, js_an_axes(i, &info->an), js_an_buttons(i, &info->an), js_an_name(i, &info->an), js_l4_open, js_l4_close), js_an_name(i, &info->an), info->port); - info = port->info; - js_l4_calibrate(info); js_l4_read(info, port->axes, port->buttons); js_an_init_corr(&info->an, port->axes, port->corr, 0); @@ -300,18 +302,21 @@ cards[i] = rev; - printk(KERN_INFO "js: PDPI Lightning 4 %s card (ports %d-%d) firmware v%d.%d found at %#x\n", + printk(KERN_INFO "js: PDPI Lightning 4 %s card (ports %d-%d) firmware v%d.%d at %#x\n", i ? "secondary" : "primary", (i << 2), (i << 2) + 3, rev >> 4, rev & 0xf, JS_L4_PORT); } } #ifndef MODULE -void __init js_l4_setup(char *str, int *ints) +int __init js_l4_setup(SETUP_PARAM) { int i; + SETUP_PARSE(24); for (i = 0; i <= ints[0] && i < 24; i++) js_l4[i] = ints[i+1]; + return 1; } +__setup("js_l4=", js_l4_setup); #endif #ifdef MODULE @@ -333,7 +338,7 @@ js_l4_port = js_l4_probe(cards, i, 0, 0, js_l4_port); } - if (js_l4_port == NULL) { + if (!js_l4_port) { #ifdef MODULE printk(KERN_WARNING "joy-lightning: no joysticks found\n"); #endif @@ -352,9 +357,9 @@ int cal[4] = {59, 59, 59, 59}; struct js_l4_info *info; - while (js_l4_port != NULL) { + while (js_l4_port) { for (i = 0; i < js_l4_port->ndevs; i++) - if (js_l4_port->devs[i] != NULL) + if (js_l4_port->devs[i]) js_unregister_device(js_l4_port->devs[i]); info = js_l4_port->info; js_l4_setcal(info->port, cal); diff -u --recursive --new-file v2.2.13/linux/drivers/char/joystick/joy-logitech.c linux/drivers/char/joystick/joy-logitech.c --- v2.2.13/linux/drivers/char/joystick/joy-logitech.c Tue Dec 1 19:05:05 1998 +++ linux/drivers/char/joystick/joy-logitech.c Tue Jan 4 10:12:14 2000 @@ -1,12 +1,14 @@ /* * joy-logitech.c Version 1.2 * - * Copyright (c) 1998 Vojtech Pavlik + * Copyright (c) 1998-1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* * This is a module for the Linux joystick driver, supporting - * Logitech Digital joystick family. + * Logitech ADI joystick family. */ /* @@ -25,7 +27,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: + * e-mail - mail your message to , or by paper mail: * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic */ @@ -38,113 +40,175 @@ #include #include #include +#include +#include -#define JS_LT_MAX_START 250 -#define JS_LT_MAX_STROBE 25 -#define JS_LT_MAX_LENGTH 72 +/* + * Times array sizes, flags, ids. + */ + +#undef JS_LT_DEBUG + +#define JS_LT_MAX_START 400 /* Trigger to packet timeout [400us] */ -#define JS_LT_MAX_DELAY 12000 +#define JS_LT_MAX_LENGTH 256 +#define JS_LT_MIN_LENGTH 8 +#define JS_LT_MIN_LEN_LENGTH 10 +#define JS_LT_MIN_ID_LENGTH 66 +#define JS_LT_MAX_NAME_LENGTH 16 -#define JS_LT_MODE_WMED 1 -#define JS_LT_MODE_CM2 2 -#define JS_LT_MODE_TPD 3 +#define JS_LT_INIT_DELAY 10 /* Delay after init packet [10ms] */ +#define JS_LT_DATA_DELAY 4 /* Delay after data packet [4ms] */ -static int js_lt_seq_init[] __initdata = { 6000, 11000, 7000, 9000, 6000, 11000, 7000, 9000, 0 }; -static int js_lt_seq_reset[] __initdata = { 2000, 3000, 2000, 3000, 0 }; +#define JS_LT_FLAG_HAT 0x04 +#define JS_LT_FLAG_10BIT 0x08 -static int js_lt_port_list[] __initdata = {0x201, 0}; +#define JS_LT_ID_WMED 0x00 +#define JS_LT_ID_TPD 0x01 +#define JS_LT_ID_WMI 0x04 +#define JS_LT_ID_WGP 0x06 +#define JS_LT_ID_WM3D 0x07 +#define JS_LT_ID_WGPE 0x08 + +#define JS_LT_BUG_BUTTONS 0x01 +#define JS_LT_BUG_LONGID 0x02 +#define JS_LT_BUG_LONGDATA 0x04 +#define JS_LT_BUG_IGNTRIG 0x08 + +/* + * Port probing variables. + */ + +static int js_lt_port_list[] __initdata = { 0x201, 0 }; static struct js_port* js_lt_port __initdata = NULL; +/* + * Device names. + */ + +#define JS_LT_MAX_ID 10 + +static char *js_lt_names[] = { "WingMan Extreme Digital", "ThunderPad Digital", "SideCar", "CyberMan 2", + "WingMan Interceptor", "WingMan Formula", "WingMan GamePad", + "WingMan Extreme Digital 3D", "WingMan GamePad Extreme", + "WingMan GamePad USB", "Unknown Device %#x"}; + +/* + * Hat to axis conversion arrays. + */ + static struct { int x; int y; } js_lt_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; +/* + * Per-port information. + */ + struct js_lt_info { - int io; - unsigned char mode; + int io; + int length[2]; + int ret[2]; + int idx[2]; + unsigned char id[2]; + char buttons[2]; + char axes10[2]; + char axes8[2]; + char pad[2]; + char hats[2]; + char name[2][JS_LT_MAX_NAME_LENGTH]; + unsigned char data[2][JS_LT_MAX_LENGTH]; + char bugs[2]; }; /* - * js_lt_read_packet() reads a Logitech packet. + * js_lt_read_packet() reads a Logitech ADI packet. */ -static int js_lt_read_packet(int io, __u64 *data) +static void js_lt_read_packet(struct js_lt_info *info) { - - static unsigned char buf[JS_LT_MAX_LENGTH]; - unsigned char u, v, w, mask = 0; - int i; + unsigned char u, v, w, x, z; + int t[2], s[2], p[2], i; unsigned long flags; - unsigned int t, t1; - - int start = (js_time_speed * JS_LT_MAX_START) >> 10; - int strobe = (js_time_speed * JS_LT_MAX_STROBE) >> 10; - u = inb(io) >> 4; - - if (u == 0xc) mask = 0x10; - if (u == 0x0) mask = 0x50; - if (!mask) return 0; - - i = 0; + for (i = 0; i < 2; i++) { + info->ret[i] = -1; + p[i] = t[i] = JS_LT_MAX_START; + s[i] = 0; + } __save_flags(flags); __cli(); - outb(0xff,io); + outb(0xff, info->io); + v = z = inb(info->io); - u = inb(io); - t = js_get_time(); + do { + u = v; + w = u ^ (v = x = inb(info->io)); + for (i = 0; i < 2; i++, w >>= 2, x >>= 2) { + t[i]--; + if ((w & 0x30) && s[i]) { + if ((w & 0x30) < 0x30 && info->ret[i] < JS_LT_MAX_LENGTH && t[i] > 0) { + info->data[i][++info->ret[i]] = w; + p[i] = t[i] = (p[i] - t[i]) << 1; + } else t[i] = 0; + } else if (!(x & 0x30)) s[i] = 1; + } + } while (t[0] > 0 || t[1] > 0); - if ((u & 0xc) != 0xc) mask = 0x10; + __restore_flags(flags); - do { - u = inb(io); - t1 = js_get_time(); - } while ((((u >> 1) ^ u) & mask) != mask && js_delta(t1,t) < start); + for (i = 0; i < 2; i++, z >>= 2) + if ((z & 0x30) && info->ret[i] > 0) + info->bugs[i] |= JS_LT_BUG_BUTTONS; + +#ifdef JS_LT_DEBUG + printk(KERN_DEBUG "joy-logitech: read %d %d bits\n", info->ret[0], info->ret[1]); + printk(KERN_DEBUG "joy-logitech: stream0:"); + for (i = 0; i <= info->ret[0]; i++) printk("%d", (info->data[0][i] >> 5) & 1); + printk("\n"); + printk(KERN_DEBUG "joy-logitech: stream1:"); + for (i = 0; i <= info->ret[1]; i++) printk("%d", (info->data[1][i] >> 5) & 1); + printk("\n"); +#endif - t = t1; + return; +} - do { - v = inb(io); - t1 = js_get_time(); - w = u ^ v; - if ((((w >> 1) ^ w) & mask) == mask) { - buf[i++] = w; - t = t1; - u = v; - } - } while (i < JS_LT_MAX_LENGTH && js_delta(t1,t) < strobe); +/* + * js_lt_move_bits() detects a possible 2-stream mode, and moves + * the bits accordingly. + */ - __restore_flags(flags); +static void js_lt_move_bits(struct js_lt_info *info, int length) +{ + int i; - t = i; - *data = 0; + info->idx[0] = info->idx[1] = 0; - if (mask == 0x10) { - for (i = 0; i < t; i++) - *data = ((buf[i] >> 5) & 1) | (*data << 1); - return t; - } - if (mask == 0x50) { - for (i = 0; i < t; i++) - *data = ((__u64)(buf[i] & 0x20) << (t - 5)) | (buf[i] >> 7) | (*data << 1); - return t << 1; - } - return 0; + if (info->ret[0] <= 0 || info->ret[1] <= 0) return; + if (info->data[0][0] & 0x20 || ~info->data[1][0] & 0x20) return; + + for (i = 1; i <= info->ret[1]; i++) + info->data[0][((length - 1) >> 1) + i + 1] = info->data[1][i]; + + info->ret[0] += info->ret[1]; + info->ret[1] = -1; } /* - * js_lt_reverse() reverses the order of bits in a byte. + * js_lt_get_bits() gathers bits from the data packet. */ -static unsigned char js_lt_reverse(unsigned char u) +static inline int js_lt_get_bits(struct js_lt_info *info, int device, int count) { - u = ((u & 0x0f) << 4) | ((u >> 4) & 0x0f); - u = ((u & 0x33) << 2) | ((u >> 2) & 0x33); - u = ((u & 0x55) << 1) | ((u >> 1) & 0x55); - return u; + int bits = 0; + int i; + if ((info->idx[device] += count) > info->ret[device]) return 0; + for (i = 0; i < count; i++) bits |= ((info->data[device][info->idx[device] - i] >> 5) & 1) << i; + return bits; } /* @@ -154,54 +218,61 @@ static int js_lt_read(void *xinfo, int **axes, int **buttons) { struct js_lt_info *info = xinfo; - __u64 data; - int hat; - - switch (info->mode) { - - case JS_LT_MODE_TPD: - - if (js_lt_read_packet(info->io, &data) != 20) return -1; - - axes[0][0] = ((data >> 6) & 1) - ((data >> 4) & 1); - axes[0][1] = ((data >> 5) & 1) - ((data >> 7) & 1); - - buttons[0][0] = js_lt_reverse((data & 0x0f) | ((data >> 4) & 0xf0)); - - return 0; + int i, j, k, l, t; + int ret = 0; - case JS_LT_MODE_WMED: + js_lt_read_packet(info); + js_lt_move_bits(info, info->length[0]); - if (js_lt_read_packet(info->io, &data) != 42) return -1; - if ((hat = data & 0xf) > 8) return -1; + for (i = 0; i < 2; i++) { - axes[0][0] = (data >> 26) & 0xff; - axes[0][1] = (data >> 18) & 0xff; - axes[0][2] = (data >> 10) & 0xff; - axes[0][3] = js_lt_hat_to_axis[hat].x; - axes[0][4] = js_lt_hat_to_axis[hat].y; - - buttons[0][0] = js_lt_reverse((data >> 2) & 0xfc); - - return 0; - - case JS_LT_MODE_CM2: - - if (js_lt_read_packet(info->io, &data) != 64) return -1; + if (!info->length[i]) continue; + + if (info->length[i] > info->ret[i] || + info->id[i] != (js_lt_get_bits(info, i, 4) | (js_lt_get_bits(info, i, 4) << 4))) { + ret = -1; + continue; + } - axes[0][0] = (data >> 48) & 0xff; - axes[0][1] = (data >> 40) & 0xff; - axes[0][2] = (data >> 32) & 0xff; - axes[0][3] = (data >> 24) & 0xff; - axes[0][4] = (data >> 16) & 0xff; - axes[0][5] = (data >> 8) & 0xff; + if (info->length[i] < info->ret[i]) + info->bugs[i] |= JS_LT_BUG_LONGDATA; + + k = l = 0; + + for (j = 0; j < info->axes10[i]; j++) + axes[i][k++] = js_lt_get_bits(info, i, 10); + + for (j = 0; j < info->axes8[i]; j++) + axes[i][k++] = js_lt_get_bits(info, i, 8); + + for (j = 0; j <= (info->buttons[i] - 1) >> 5; j++) buttons[i][j] = 0; + + for (j = 0; j < info->buttons[i] && j < 63; j++) { + if (j == info->pad[i]) { + t = js_lt_get_bits(info, i, 4); + axes[i][k++] = ((t >> 2) & 1) - ( t & 1); + axes[i][k++] = ((t >> 1) & 1) - ((t >> 3) & 1); + } + buttons[i][l >> 5] |= js_lt_get_bits(info, i, 1) << (l & 0x1f); + l++; + } - buttons[0][0] = js_lt_reverse(data & 0xff); + for (j = 0; j < info->hats[i]; j++) { + if((t = js_lt_get_bits(info, i, 4)) > 8) { + if (t != 15) ret = -1; /* Hat press */ + t = 0; + } + axes[i][k++] = js_lt_hat_to_axis[t].x; + axes[i][k++] = js_lt_hat_to_axis[t].y; + } - return 0; + for (j = 63; j < info->buttons[i]; j++) { + buttons[i][l >> 5] |= js_lt_get_bits(info, i, 1) << (l & 0x1f); + l++; + } } - return -1; + return ret; } /* @@ -225,15 +296,18 @@ } /* - * js_lt_trigger_sequence() sends a trigger & delay sequence - * to reset/initialize a Logitech joystick. + * js_lt_init_digital() sends a trigger & delay sequence + * to reset and initialize a Logitech joystick into digital mode. */ -static void __init js_lt_trigger_sequence(int io, int *seq) +static void __init js_lt_init_digital(int io) { - while (*seq) { + int seq[] = { 3, 2, 3, 10, 6, 11, 7, 9, 11, 0 }; + int i; + + for (i = 0; seq[i]; i++) { outb(0xff,io); - udelay(*seq++); + mdelay(seq[i]); } } @@ -242,35 +316,45 @@ * Logitech joysticks. */ -static void __init js_lt_init_corr(int num_axes, int mode, int **axes, struct js_corr **corr) +static void __init js_lt_init_corr(int id, int naxes10, int naxes8, int naxes1, int *axes, struct js_corr *corr) { int j; - - for (j = 0; j < num_axes; j++) { - corr[0][j].type = JS_CORR_BROKEN; - corr[0][j].prec = 2; - corr[0][j].coef[0] = axes[0][j] - 8; - corr[0][j].coef[1] = axes[0][j] + 8; - corr[0][j].coef[2] = (1 << 29) / (127 - 32); - corr[0][j].coef[3] = (1 << 29) / (127 - 32); + + if (id == JS_LT_ID_WMED) axes[2] = 128; /* Throttle fixup */ + if (id == JS_LT_ID_WMI) axes[2] = 512; + if (id == JS_LT_ID_WM3D) axes[3] = 128; + + if (id == JS_LT_ID_WGPE) { /* Tilt fixup */ + axes[0] = 512; + axes[1] = 512; } - switch (mode) { - case JS_LT_MODE_TPD: j = 0; break; - case JS_LT_MODE_WMED: j = 3; break; - case JS_LT_MODE_CM2: j = 6; break; - default: j = 0; break; + for (j = 0; j < naxes10; j++) { + corr[j].type = JS_CORR_BROKEN; + corr[j].prec = 2; + corr[j].coef[0] = axes[j] - 16; + corr[j].coef[1] = axes[j] + 16; + corr[j].coef[2] = (1 << 29) / (256 - 32); + corr[j].coef[3] = (1 << 29) / (256 - 32); } - for (; j < num_axes; j++) { - corr[0][j].type = JS_CORR_BROKEN; - corr[0][j].prec = 0; - corr[0][j].coef[0] = 0; - corr[0][j].coef[1] = 0; - corr[0][j].coef[2] = (1 << 29); - corr[0][j].coef[3] = (1 << 29); + for (; j < naxes8 + naxes10; j++) { + corr[j].type = JS_CORR_BROKEN; + corr[j].prec = 1; + corr[j].coef[0] = axes[j] - 2; + corr[j].coef[1] = axes[j] + 2; + corr[j].coef[2] = (1 << 29) / (64 - 16); + corr[j].coef[3] = (1 << 29) / (64 - 16); } + for (; j < naxes1 + naxes8 + naxes10; j++) { + corr[j].type = JS_CORR_BROKEN; + corr[j].prec = 0; + corr[j].coef[0] = 0; + corr[j].coef[1] = 0; + corr[j].coef[2] = (1 << 29); + corr[j].coef[3] = (1 << 29); + } } /* @@ -279,61 +363,157 @@ static struct js_port __init *js_lt_probe(int io, struct js_port *port) { - struct js_lt_info info; - char *name; - int axes, buttons, i; - __u64 data; - unsigned char u; + struct js_lt_info iniinfo; + struct js_lt_info *info = &iniinfo; + char name[32]; + int i, j, t; if (check_region(io, 1)) return port; - if (((u = inb(io)) & 3) == 3) return port; - outb(0xff,io); - if (!((inb(io) ^ u) & ~u & 0xf)) return port; - - if (!(i = js_lt_read_packet(io, &data))) { - udelay(JS_LT_MAX_DELAY); - js_lt_trigger_sequence(io, js_lt_seq_reset); - js_lt_trigger_sequence(io, js_lt_seq_init); - i = js_lt_read_packet(io, &data); - } + js_lt_init_digital(io); + + memset(info, 0, sizeof(struct js_lt_info)); + + info->length[0] = info->length[1] = JS_LT_MAX_LENGTH; + + info->io = io; + js_lt_read_packet(info); + + if (info->ret[0] >= JS_LT_MIN_LEN_LENGTH) + js_lt_move_bits(info, js_lt_get_bits(info, 0, 10)); + + info->length[0] = info->length[1] = 0; + + for (i = 0; i < 2; i++) { + + if (info->ret[i] < JS_LT_MIN_ID_LENGTH) continue; /* Minimum ID packet length */ + + if (info->ret[i] < (t = js_lt_get_bits(info, i, 10))) { + printk(KERN_WARNING "joy-logitech: Short ID packet: reported: %d != read: %d\n", + t, info->ret[i]); + continue; + } + + if (info->ret[i] > t) + info->bugs[i] |= JS_LT_BUG_LONGID; + +#ifdef JS_LT_DEBUG + printk(KERN_DEBUG "joy-logitech: id %d length %d", i, t); +#endif + + info->id[i] = js_lt_get_bits(info, i, 4) | (js_lt_get_bits(info, i, 4) << 4); + + if ((t = js_lt_get_bits(info, i, 4)) & JS_LT_FLAG_HAT) info->hats[i]++; + +#ifdef JS_LT_DEBUG + printk(KERN_DEBUG "joy-logitech: id %d flags %d", i, t); +#endif + + if ((info->length[i] = js_lt_get_bits(info, i, 10)) >= JS_LT_MAX_LENGTH) { + printk(KERN_WARNING "joy-logitech: Expected packet length too long (%d).\n", + info->length[i]); + continue; + } + + if (info->length[i] < JS_LT_MIN_LENGTH) { + printk(KERN_WARNING "joy-logitech: Expected packet length too short (%d).\n", + info->length[i]); + continue; + } + + info->axes8[i] = js_lt_get_bits(info, i, 4); + info->buttons[i] = js_lt_get_bits(info, i, 6); + + if (js_lt_get_bits(info, i, 6) != 8 && info->hats[i]) { + printk(KERN_WARNING "joy-logitech: Other than 8-dir POVs not supported yet.\n"); + continue; + } + + info->buttons[i] += js_lt_get_bits(info, i, 6); + info->hats[i] += js_lt_get_bits(info, i, 4); + + j = js_lt_get_bits(info, i, 4); + + if (t & JS_LT_FLAG_10BIT) { + info->axes10[i] = info->axes8[i] - j; + info->axes8[i] = j; + } + + t = js_lt_get_bits(info, i, 4); + + for (j = 0; j < t; j++) + info->name[i][j] = js_lt_get_bits(info, i, 8); + info->name[i][j] = 0; + + switch (info->id[i]) { + case JS_LT_ID_TPD: + info->pad[i] = 4; + info->buttons[i] -= 4; + break; + case JS_LT_ID_WGP: + info->pad[i] = 0; + info->buttons[i] -= 4; + break; + default: + info->pad[i] = -1; + break; + } + + if (info->length[i] != + (t = 8 + info->buttons[i] + info->axes10[i] * 10 + info->axes8[i] * 8 + + info->hats[i] * 4 + (info->pad[i] != -1) * 4)) { + printk(KERN_WARNING "js%d: Expected lenght %d != data length %d\n", i, t, info->length[i]); + } - switch (i) { - case 0: - return port; - case 20: - info.mode = JS_LT_MODE_TPD; - axes = 2; buttons = 8; name = "Logitech ThunderPad Digital"; - break; - case 42: - info.mode = JS_LT_MODE_WMED; - axes = 5; buttons = 6; name = "Logitech WingMan Extreme Digital"; - break; - case 64: - info.mode = JS_LT_MODE_CM2; - axes = 6; buttons = 8; name = "Logitech CyberMan 2"; - break; - case 72: - case 144: - return port; - default: - printk(KERN_WARNING "joy-logitech: unknown joystick device detected " - "(io=%#x, count=%d, data=0x%08x%08x), contact \n", - io, i, (int)(data >> 32), (int)(data & 0xffffffff)); - return port; } - info.io = io; + if (!info->length[0] && !info->length[1]) + return port; request_region(io, 1, "joystick (logitech)"); - port = js_register_port(port, &info, 1, sizeof(struct js_lt_info), js_lt_read); - printk(KERN_INFO "js%d: %s at %#x\n", - js_register_device(port, 0, axes, buttons, name, js_lt_open, js_lt_close), name, io); - udelay(JS_LT_MAX_DELAY); + port = js_register_port(port, info, 2, sizeof(struct js_lt_info), js_lt_read); + info = port->info; + + for (i = 0; i < 2; i++) + if (info->length[i] > 0) { + sprintf(name, info->id[i] < JS_LT_MAX_ID ? + js_lt_names[info->id[i]] : js_lt_names[JS_LT_MAX_ID], info->id[i]); + printk(KERN_INFO "js%d: %s [%s] at %#x\n", + js_register_device(port, i, + info->axes10[i] + info->axes8[i] + ((info->hats[i] + (info->pad[i] >= 0)) << 1), + info->buttons[i], name, js_lt_open, js_lt_close), name, info->name[i], io); + } + + mdelay(JS_LT_INIT_DELAY); + if (js_lt_read(info, port->axes, port->buttons)) { + if (info->ret[0] < 1) info->bugs[0] |= JS_LT_BUG_IGNTRIG; + if (info->ret[1] < 1) info->bugs[1] |= JS_LT_BUG_IGNTRIG; + mdelay(JS_LT_DATA_DELAY); + js_lt_read(info, port->axes, port->buttons); + } - js_lt_read(port->info, port->axes, port->buttons); - js_lt_init_corr(axes, info.mode, port->axes, port->corr); + for (i = 0; i < 2; i++) + if (info->length[i] > 0) { +#ifdef JS_LT_DEBUG + printk(KERN_DEBUG "js%d: length %d ret %d id %d buttons %d axes10 %d axes8 %d " + "pad %d hats %d name %s explen %d\n", + i, info->length[i], info->ret[i], info->id[i], + info->buttons[i], info->axes10[i], info->axes8[i], + info->pad[i], info->hats[i], info->name[i], + 8 + info->buttons[i] + info->axes10[i] * 10 + info->axes8[i] * 8 + + info->hats[i] * 4 + (info->pad[i] != -1) * 4); +#endif + if (info->bugs[i]) { + printk(KERN_WARNING "js%d: Firmware bugs detected:%s%s%s%s\n", i, + info->bugs[i] & JS_LT_BUG_BUTTONS ? " init_buttons" : "", + info->bugs[i] & JS_LT_BUG_LONGID ? " long_id" : "", + info->bugs[i] & JS_LT_BUG_LONGDATA ? " long_data" : "", + info->bugs[i] & JS_LT_BUG_IGNTRIG ? " ignore_trigger" : ""); + } + js_lt_init_corr(info->id[i], info->axes10[i], info->axes8[i], + ((info->pad[i] >= 0) + info->hats[i]) << 1, port->axes[i], port->corr[i]); + } return port; } @@ -359,10 +539,13 @@ #ifdef MODULE void cleanup_module(void) { + int i; struct js_lt_info *info; - while (js_lt_port != NULL) { - js_unregister_device(js_lt_port->devs[0]); + while (js_lt_port) { + for (i = 0; i < js_lt_port->ndevs; i++) + if (js_lt_port->devs[i]) + js_unregister_device(js_lt_port->devs[i]); info = js_lt_port->info; release_region(info->io, 1); js_lt_port = js_unregister_port(js_lt_port); diff -u --recursive --new-file v2.2.13/linux/drivers/char/joystick/joy-magellan.c linux/drivers/char/joystick/joy-magellan.c --- v2.2.13/linux/drivers/char/joystick/joy-magellan.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/joystick/joy-magellan.c Tue Jan 4 10:12:14 2000 @@ -0,0 +1,397 @@ +/* + * joy-magellan.c Version 0.1 + * + * Copyright (c) 1999 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * This is a module for the Linux joystick driver, supporting + * the Magellan and Space Mouse 6dof controllers. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Constants. + */ + +#define N_JOYSTICK_MAG 14 +#define JS_MAG_MAX_LENGTH 64 + +/* + * List of Magellans. + */ + +static struct js_port* js_mag_port = NULL; + +/* + * Per-Magellan data. + */ + +struct js_mag_info { + struct tty_struct* tty; + struct js_port* port; + int idx; + unsigned char data[JS_MAG_MAX_LENGTH]; + unsigned char name[JS_MAG_MAX_LENGTH]; + char ack; + char used; +}; + +/* + * js_mag_crunch_nibbles() verifies that the bytes sent from the Magellan + * have correct upper nibbles for the lower ones, if not, the packet will + * be thrown away. It also strips these upper halves to simplify further + * processing. + */ + +static int js_mag_crunch_nibbles(unsigned char *data, int count) +{ + static unsigned char nibbles[16] = "0AB3D56GH9:Kidx) return; + + switch (info->data[0]) { + + case 'd': /* Axis data */ + if (info->idx != 25) return; + if (js_mag_crunch_nibbles(info->data, 24)) return; + if (!info->port->devs[0]) return; + for (i = 0; i < 6; i++) { + info->port->axes[0][i] = + ( info->data[(i << 2) + 1] << 12 | info->data[(i << 2) + 2] << 8 | + info->data[(i << 2) + 3] << 4 | info->data[(i << 2) + 4] ) + - 32768; + } + break; + + case 'e': /* Error packet */ + if (info->idx != 4) return; + if (js_mag_crunch_nibbles(info->data, 3)) return; + switch (info->data[1]) { + case 1: + printk(KERN_ERR "joy-magellan: Received command error packet. Failing command byte: %c\n", + info->data[2] | (info->data[3] << 4)); + break; + case 2: + printk(KERN_ERR "joy-magellan: Received framing error packet.\n"); + break; + default: + printk(KERN_ERR "joy-magellan: Received unknown error packet.\n"); + } + break; + + case 'k': /* Button data */ + if (info->idx != 4) return; + if (js_mag_crunch_nibbles(info->data, 3)) return; + if (!info->port->devs[0]) return; + info->port->buttons[0][0] = (info->data[1] << 1) | (info->data[2] << 5) | info->data[3]; + break; + + case 'm': /* Mode */ + if (info->idx != 2) return; + if (js_mag_crunch_nibbles(info->data, 1)) return; + break; + + case 'n': /* Null radius */ + if (info->idx != 2) return; + if (js_mag_crunch_nibbles(info->data, 1)) return; + break; + + case 'p': /* Data rate */ + if (info->idx != 3) return; + if (js_mag_crunch_nibbles(info->data, 2)) return; + break; + + case 'q': /* Sensitivity */ + if (info->idx != 3) return; + if (js_mag_crunch_nibbles(info->data, 2)) return; + break; + + case 'v': /* Version string */ + info->data[info->idx] = 0; + for (i = 1; i < info->idx && info->data[i] == ' '; i++); + memcpy(info->name, info->data + i, info->idx - i); + break; + + case 'z': /* Zero position */ + break; + + default: + printk("joy-magellan: Unknown packet %d length %d:", info->data[0], info->idx); + for (i = 0; i < info->idx; i++) printk(" %02x", info->data[i]); + printk("\n"); + return; + } + + info->ack = info->data[0]; +} + +/* + * js_mag_command() sends a command to the Magellan, and waits for + * acknowledge. + */ + +static int js_mag_command(struct js_mag_info *info, char *command, int timeout) +{ + info->ack = 0; + if (info->tty->driver.write(info->tty, 0, command, strlen(command)) != strlen(command)) return -1; + while (!info->ack && timeout--) mdelay(1); + return -(info->ack != command[0]); +} + +/* + * js_mag_setup() initializes the Magellan to sane state. Also works as + * a probe for Magellan existence. + */ + +static int js_mag_setup(struct js_mag_info *info) +{ + + if (js_mag_command(info, "vQ\r", 800)) /* Read version */ + return -1; + if (js_mag_command(info, "m3\r", 50)) /* Set full 3d mode */ + return -1; + if (js_mag_command(info, "pBB\r", 50)) /* Set 16 reports/second (max) */ + return -1; + if (js_mag_command(info, "z\r", 50)) /* Set zero position */ + return -1; + + return 0; +} + +/* + * js_mag_read() updates the axis and button data upon startup. + */ + +static int js_mag_read(struct js_mag_info *info) +{ + memset(info->port->axes[0],0, sizeof(int) * 6); /* Axes are 0 after zero postition cmd */ + + if (js_mag_command(info, "kQ\r", 50)) /* Read buttons */ + return -1; + + return 0; +} + +/* + * js_mag_open() is a callback from the joystick device open routine. + */ + +static int js_mag_open(struct js_dev *jd) +{ + struct js_mag_info *info = jd->port->info; + info->used++; + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_mag_close() is a callback from the joystick device release routine. + */ + +static int js_mag_close(struct js_dev *jd) +{ + struct js_mag_info *info = jd->port->info; + if (!--info->used) { + js_unregister_device(jd->port->devs[0]); + js_mag_port = js_unregister_port(jd->port); + } + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_mag_init_corr() initializes the correction values for the Magellan. + * It asumes gain setting of 0, question is, what we should do for higher + * gain settings ... + */ + +static void js_mag_init_corr(struct js_corr **corr) +{ + int i; + + for (i = 0; i < 6; i++) { + corr[0][i].type = JS_CORR_BROKEN; + corr[0][i].prec = 0; + corr[0][i].coef[0] = 0; + corr[0][i].coef[1] = 0; + corr[0][i].coef[2] = (1 << 29) / 256; + corr[0][i].coef[3] = (1 << 29) / 256; + } +} + +/* + * js_mag_ldisc_open() is the routine that is called upon setting our line + * discipline on a tty. It looks for the Magellan, and if found, registers + * it as a joystick device. + */ + +static int js_mag_ldisc_open(struct tty_struct *tty) +{ + struct js_mag_info iniinfo; + struct js_mag_info *info = &iniinfo; + + info->tty = tty; + info->idx = 0; + info->used = 1; + + js_mag_port = js_register_port(js_mag_port, info, 1, sizeof(struct js_mag_info), NULL); + + info = js_mag_port->info; + info->port = js_mag_port; + tty->disc_data = info; + + if (js_mag_setup(info)) { + js_mag_port = js_unregister_port(info->port); + return -ENODEV; + } + + printk(KERN_INFO "js%d: Magellan [%s] on %s%d\n", + js_register_device(js_mag_port, 0, 6, 9, "Magellan", js_mag_open, js_mag_close), + info->name, tty->driver.name, MINOR(tty->device) - tty->driver.minor_start); + + + js_mag_read(info); + js_mag_init_corr(js_mag_port->corr); + + MOD_INC_USE_COUNT; + + return 0; +} + +/* + * js_mag_ldisc_close() is the opposite of js_mag_ldisc_open() + */ + +static void js_mag_ldisc_close(struct tty_struct *tty) +{ + struct js_mag_info* info = (struct js_mag_info*) tty->disc_data; + if (!--info->used) { + js_unregister_device(info->port->devs[0]); + js_mag_port = js_unregister_port(info->port); + } + MOD_DEC_USE_COUNT; +} + +/* + * js_mag_ldisc_receive() is called by the low level driver when characters + * are ready for us. We then buffer them for further processing, or call the + * packet processing routine. + */ + +static void js_mag_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) +{ + struct js_mag_info* info = (struct js_mag_info*) tty->disc_data; + int i; + + for (i = 0; i < count; i++) + if (cp[i] == '\r') { + js_mag_process_packet(info); + info->idx = 0; + } else { + if (info->idx < JS_MAG_MAX_LENGTH) + info->data[info->idx++] = cp[i]; + } +} + +/* + * js_mag_ldisc_room() reports how much room we do have for receiving data. + * Although we in fact have infinite room, we need to specify some value + * here, so why not the size of our packet buffer. It's big anyway. + */ + +static int js_mag_ldisc_room(struct tty_struct *tty) +{ + return JS_MAG_MAX_LENGTH; +} + +/* + * The line discipline structure. + */ + +static struct tty_ldisc js_mag_ldisc = { + magic: TTY_LDISC_MAGIC, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) + name: "magellan", +#endif + open: js_mag_ldisc_open, + close: js_mag_ldisc_close, + receive_buf: js_mag_ldisc_receive, + receive_room: js_mag_ldisc_room, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +#ifdef MODULE +int init_module(void) +#else +int __init js_mag_init(void) +#endif +{ + if (tty_register_ldisc(N_JOYSTICK_MAG, &js_mag_ldisc)) { + printk(KERN_ERR "joy-magellan: Error registering line discipline.\n"); + return -ENODEV; + } + + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + tty_register_ldisc(N_JOYSTICK_MAG, NULL); +} +#endif diff -u --recursive --new-file v2.2.13/linux/drivers/char/joystick/joy-pci.c linux/drivers/char/joystick/joy-pci.c --- v2.2.13/linux/drivers/char/joystick/joy-pci.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/joystick/joy-pci.c Tue Jan 4 10:12:14 2000 @@ -0,0 +1,272 @@ +/* + * joy-pci.c Version 0.4.0 + * + * Copyright (c) 1999 Raymond Ingles + * Copyright (c) 1999 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * This is a module for the Linux joystick driver, supporting the + * gameports on Trident 4DWave and Aureal Vortex soundcards, and + * analog joysticks connected to them. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Raymond Ingles "); +MODULE_PARM(js_pci, "3-32i"); + +#define NUM_CARDS 8 +static int js_pci[NUM_CARDS * 4] __initdata = { -1,0,0,0,-1,0,0,0,-1,0,0,0,-1,0,0,0, + -1,0,0,0,-1,0,0,0,-1,0,0,0,-1,0,0,0 }; + +static struct js_port * js_pci_port __initdata = NULL; + +#include "joy-analog.h" + +struct js_pci_info; +typedef void (*js_pci_func)(struct js_pci_info *); + +struct js_pci_data { + int vendor; /* PCI Vendor ID */ + int model; /* PCI Model ID */ + int size; /* Memory / IO region size */ + int lcr; /* Aureal Legacy Control Register */ + int gcr; /* Gameport control register */ + int buttons; /* Buttons location */ + int axes; /* Axes start */ + int axsize; /* Axis field size */ + int axmax; /* Axis field max value */ + js_pci_func init; + js_pci_func cleanup; + char *name; +}; + +struct js_pci_info { + unsigned char *base; + struct pci_dev *pci_p; + __u32 lcr; + struct js_pci_data *data; + struct js_an_info an; +}; + +/* + * js_pci_*_init() sets the info->base field, disables legacy gameports, + * and enables the enhanced ones. + */ + +static void js_pci_4dwave_init(struct js_pci_info *info) +{ + info->base = ioremap(BASE_ADDRESS(info->pci_p, 1), info->data->size); + pci_read_config_word(info->pci_p, info->data->lcr, (unsigned short *)&info->lcr); + pci_write_config_word(info->pci_p, info->data->lcr, info->lcr & ~0x20); + writeb(0x80, info->base + info->data->gcr); +} + +static void js_pci_vortex_init(struct js_pci_info *info) +{ + info->base = ioremap(BASE_ADDRESS(info->pci_p, 0), info->data->size); + info->lcr = readl(info->base + info->data->lcr); + writel(info->lcr & ~0x8, info->base + info->data->lcr); + writel(0x40, info->base + info->data->gcr); +} + +/* + * js_pci_*_cleanup does the opposite of the above functions. + */ + +static void js_pci_4dwave_cleanup(struct js_pci_info *info) +{ + pci_write_config_word(info->pci_p, info->data->lcr, info->lcr); + writeb(0x00, info->base + info->data->gcr); + iounmap(info->base); +} + +static void js_pci_vortex_cleanup(struct js_pci_info *info) +{ + writel(info->lcr, info->base + info->data->lcr); + writel(0x00, info->base + info->data->gcr); + iounmap(info->base); +} + +static struct js_pci_data js_pci_data[] = +{{ PCI_VENDOR_ID_TRIDENT, 0x2000, 0x10000, 0x00044 ,0x00030, 0x00031, 0x00034, 2, 0xffff, + js_pci_4dwave_init, js_pci_4dwave_cleanup, "Trident 4DWave DX" }, + { PCI_VENDOR_ID_TRIDENT, 0x2001, 0x10000, 0x00044, 0x00030, 0x00031, 0x00034, 2, 0xffff, + js_pci_4dwave_init, js_pci_4dwave_cleanup, "Trident 4DWave NX" }, + { PCI_VENDOR_ID_AUREAL, 0x0001, 0x40000, 0x1280c, 0x1100c, 0x11008, 0x11010, 4, 0x1fff, + js_pci_vortex_init, js_pci_vortex_cleanup, "Aureal Vortex1" }, + { PCI_VENDOR_ID_AUREAL, 0x0002, 0x40000, 0x2a00c, 0x2880c, 0x28808, 0x28810, 4, 0x1fff, + js_pci_vortex_init, js_pci_vortex_cleanup, "Aureal Vortex2" }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL }}; + +/* + * js_pci_read() reads data from a PCI gameport. + */ + +static int js_pci_read(void *xinfo, int **axes, int **buttons) +{ + struct js_pci_info *info = xinfo; + int i; + + info->an.buttons = ~readb(info->base + info->data->buttons) >> 4; + + for (i = 0; i < 4; i++) + info->an.axes[i] = readw(info->base + info->data->axes + i * info->data->axsize); + + js_an_decode(&info->an, axes, buttons); + + return 0; +} + +/* + * js_pci_open() is a callback from the file open routine. + */ + +static int js_pci_open(struct js_dev *jd) +{ + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_pci_close() is a callback from the file release routine. + */ + +static int js_pci_close(struct js_dev *jd) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +static struct js_port * __init js_pci_probe(struct js_port *port, int type, int number, + struct pci_dev *pci_p, struct js_pci_data *data) +{ + int i; + unsigned char u; + int mask0, mask1, numdev; + struct js_pci_info iniinfo; + struct js_pci_info *info = &iniinfo; + + mask0 = mask1 = 0; + + for (i = 0; i < NUM_CARDS; i++) + if (js_pci[i * 4] == type && js_pci[i * 4 + 1] == number) { + mask0 = js_pci[i * 4 + 2]; + mask1 = js_pci[i * 4 + 3]; + if (!mask0 && !mask1) return port; + break; + } + + memset(info, 0, sizeof(struct js_pci_info)); + + info->data = data; + info->pci_p = pci_p; + data->init(info); + + mdelay(10); + js_pci_read(info, NULL, NULL); + + for (i = u = 0; i < 4; i++) + if (info->an.axes[i] < info->data->axmax) u |= 1 << i; + + if ((numdev = js_an_probe_devs(&info->an, u, mask0, mask1, port)) <= 0) + return port; + + port = js_register_port(port, info, numdev, sizeof(struct js_pci_info), js_pci_read); + + info = port->info; + + for (i = 0; i < numdev; i++) + printk(KERN_WARNING "js%d: %s on %s #%d\n", + js_register_device(port, i, js_an_axes(i, &info->an), js_an_buttons(i, &info->an), + js_an_name(i, &info->an), js_pci_open, js_pci_close), js_an_name(i, &info->an), data->name, number); + + js_pci_read(info, port->axes, port->buttons); + js_an_init_corr(&info->an, port->axes, port->corr, 32); + + return port; +} + +#ifndef MODULE +int __init js_pci_setup(SETUP_PARAM) +{ + int i; + SETUP_PARSE(NUM_CARDS*4); + for (i = 0; i <= ints[0] && i < NUM_CARDS*4; i++) + js_pci[i] = ints[i+1]; + return 1; +} +__setup("js_pci=", js_pci_setup); +#endif + +#ifdef MODULE +int init_module(void) +#else +int __init js_pci_init(void) +#endif +{ + struct pci_dev *pci_p = NULL; + int i, j; + + for (i = 0; js_pci_data[i].vendor; i++) + for (j = 0; (pci_p = pci_find_device(js_pci_data[i].vendor, js_pci_data[i].model, pci_p)); j++) + js_pci_port = js_pci_probe(js_pci_port, i, j, pci_p, js_pci_data + i); + + if (!js_pci_port) { +#ifdef MODULE + printk(KERN_WARNING "joy-pci: no joysticks found\n"); +#endif + return -ENODEV; + } + + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + int i; + struct js_pci_info *info; + + while (js_pci_port) { + for (i = 0; i < js_pci_port->ndevs; i++) + if (js_pci_port->devs[i]) + js_unregister_device(js_pci_port->devs[i]); + info = js_pci_port->info; + info->data->cleanup(info); + js_pci_port = js_unregister_port(js_pci_port); + } +} +#endif diff -u --recursive --new-file v2.2.13/linux/drivers/char/joystick/joy-sidewinder.c linux/drivers/char/joystick/joy-sidewinder.c --- v2.2.13/linux/drivers/char/joystick/joy-sidewinder.c Tue Dec 1 19:05:05 1998 +++ linux/drivers/char/joystick/joy-sidewinder.c Tue Jan 4 10:12:14 2000 @@ -1,7 +1,9 @@ /* * joy-sidewinder.c Version 1.2 * - * Copyright (c) 1998 Vojtech Pavlik + * Copyright (c) 1998-1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* @@ -25,7 +27,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: + * e-mail - mail your message to , or by paper mail: * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic */ @@ -38,18 +40,36 @@ #include #include #include +#include -#define JS_SW_MAX_START 250 -#define JS_SW_MIN_STROBE 25 -#define JS_SW_EXT_STROBE 45 -#define JS_SW_MIN_TIME 1500 -#define JS_SW_MAX_TIME 4000 - -#define JS_SW_MAX_LENGTH 72 - -#define JS_SW_MODE_3DP 1 -#define JS_SW_MODE_PP 2 -#define JS_SW_MODE_GP 3 +/* + * These are really magic values. Changing them can make a problem go away, + * as well as break everything. + */ + +#undef JS_SW_DEBUG + +#define JS_SW_START 400 /* The time we wait for the first bit [400 us] */ +#define JS_SW_STROBE 45 /* Max time per bit [45 us] */ +#define JS_SW_TIMEOUT 4000 /* Wait for everything to settle [4 ms] */ +#define JS_SW_KICK 45 /* Wait after A0 fall till kick [45 us] */ +#define JS_SW_END 8 /* Number of bits before end of packet to kick */ +#define JS_SW_FAIL 16 /* Number of packet read errors to fail and reinitialize */ +#define JS_SW_BAD 2 /* Number of packet read errors to switch off 3d Pro optimization */ +#define JS_SW_OK 64 /* Number of packet read successes to switch optimization back on */ +#define JS_SW_LENGTH 512 /* Max number of bits in a packet */ + +/* + * SideWinder joystick types ... + */ + +#define JS_SW_TYPE_3DP 1 +#define JS_SW_TYPE_F23 2 +#define JS_SW_TYPE_GP 3 +#define JS_SW_TYPE_PP 4 +#define JS_SW_TYPE_FFP 5 +#define JS_SW_TYPE_FSP 6 +#define JS_SW_TYPE_FFW 7 static int js_sw_port_list[] __initdata = {0x201, 0}; static struct js_port* js_sw_port __initdata = NULL; @@ -61,208 +81,398 @@ struct js_sw_info { int io; - unsigned char mode; + int length; + int speed; + unsigned char type; + unsigned char bits; unsigned char number; - unsigned char optimize; + unsigned char fail; + unsigned char ok; }; /* - * js_sw_init_digital() switches a SideWinder into digital mode. + * Gameport speed. + */ + +unsigned int js_sw_io_speed = 0; + +/* + * js_sw_measure_speed() measures the gameport i/o speed. */ -static void __init js_sw_init_digital(int io) +static int __init js_sw_measure_speed(int io) { - unsigned int t; - unsigned int timeout = (js_time_speed * JS_SW_MAX_TIME) >> 10; - int delays[] = {140, 140+726, 140+300, 0}; - int i = 0; +#ifdef __i386__ + +#define GET_TIME(x) do { outb(0, 0x43); x = inb(0x40); x |= inb(0x40) << 8; } while (0) +#define DELTA(x,y) ((y)-(x)+((y)<(x)?1193180L/HZ:0)) + + unsigned int i, t, t1, t2, t3, tx; unsigned long flags; - __save_flags(flags); - __cli(); - do { - outb(0xff,io); - t = js_get_time(); - while ((inb(io) & 1) && (js_delta(js_get_time(),t) < timeout)); - udelay(delays[i]); - } while (delays[i++]); - __restore_flags(flags); + tx = 1 << 30; - for (i = 0; i < 4; i++) { - udelay(300); - outb(0xff, io); + for(i = 0; i < 50; i++) { + save_flags(flags); /* Yes, all CPUs */ + cli(); + GET_TIME(t1); + for(t = 0; t < 50; t++) inb(io); + GET_TIME(t2); + GET_TIME(t3); + restore_flags(flags); + udelay(i * 10); + if ((t = DELTA(t2,t1) - DELTA(t3,t2)) < tx) tx = t; } - return; + return 59659 / t; + +#else + + unsigned int j, t = 0; + + j = jiffies; while (j == jiffies); + j = jiffies; while (j == jiffies) { t++; inb(0x201); } + + return t * HZ / 1000; + +#endif } /* - * js_sw_read_packet() reads a SideWinder packet. + * js_sw_read_packet() is a function which reads either a data packet, or an + * identification packet from a SideWinder joystick. Better don't try to + * understand this, since all the ugliness of the Microsoft Digital + * Overdrive protocol is concentrated in this function. If you really want + * to know how this works, first go watch a couple horror movies, so that + * you are well prepared, read US patent #5628686 and then e-mail me, + * and I'll send you an explanation. + * Vojtech */ -static int js_sw_read_packet(int io, int l1, int l2, int strobe, __u64 *data) +static int js_sw_read_packet(int io, int speed, unsigned char *buf, int length, int id) { - static unsigned char buf[JS_SW_MAX_LENGTH]; - unsigned char u, v; - int i; unsigned long flags; - unsigned int t, t1; - - int length = l1 < l2 ? l2 : l1; - int start = (js_time_speed * JS_SW_MAX_START) >> 10; - strobe = (js_time_speed * strobe) >> 10; + int timeout, bitout, sched, i, kick, start, strobe; + unsigned char pending, u, v; - i = 0; + i = -id; /* Don't care about data, only want ID */ + timeout = id ? (JS_SW_TIMEOUT * speed) >> 10 : 0; /* Set up global timeout for ID packet */ + kick = id ? (JS_SW_KICK * speed) >> 10 : 0; /* Set up kick timeout for ID packet */ + start = (JS_SW_START * speed) >> 10; + strobe = (JS_SW_STROBE * speed) >> 10; + bitout = start; + pending = 0; + sched = 0; - __save_flags(flags); - __cli(); - outb(0xff,io); + __save_flags(flags); /* Quiet, please */ + __cli(); + outb(0xff, io); /* Trigger */ v = inb(io); - t = js_get_time(); do { + bitout--; u = v; v = inb(io); - t1 = js_get_time(); - } while (!((u ^ v) & u & 0x10) && js_delta(t1, t) < start); + } while (!(~v & u & 0x10) && (bitout > 0)); /* Wait for first falling edge on clock */ - t = t1; + if (bitout > 0) bitout = strobe; /* Extend time if not timed out */ + + while ((timeout > 0 || bitout > 0) && (i < length)) { + + timeout--; + bitout--; /* Decrement timers */ + sched--; - do { - v = inb(io); - t1 = js_get_time(); - if ((u ^ v) & v & 0x10) { - buf[i++] = v >> 5; - t = t1; - } u = v; - } while (i < length && js_delta(t1,t) < strobe); + v = inb(io); - __restore_flags(flags); + if ((~u & v & 0x10) && (bitout > 0)) { /* Rising edge on clock - data bit */ + if (i >= 0) /* Want this data */ + buf[i] = v >> 5; /* Store it */ + i++; /* Advance index */ + bitout = strobe; /* Extend timeout for next bit */ + } + + if (kick && (~v & u & 0x01)) { /* Falling edge on axis 0 */ + sched = kick; /* Schedule second trigger */ + kick = 0; /* Don't schedule next time on falling edge */ + pending = 1; /* Mark schedule */ + } + + if (pending && sched < 0 && (i > -JS_SW_END)) { /* Second trigger time */ + outb(0xff, io); /* Trigger */ + bitout = start; /* Long bit timeout */ + pending = 0; /* Unmark schedule */ + timeout = 0; /* Switch from global to bit timeouts */ + } + } - *data = 0; + __restore_flags(flags); /* Done - relax */ - if (i == l1) { - t = i > 64 ? 64 : i; - for (i = 0; i < t; i++) - *data |= (__u64) (buf[i] & 1) << i; - return t; - } - if (i == l2) { - t = i > 22 ? 22 : i; - for (i = 0; i < t; i++) - *data |= (__u64) buf[i] << (3 * i); - return t * 3; +#ifdef JS_SW_DEBUG + { + int j; + printk(KERN_DEBUG "joy-sidewinder: Read %d triplets. [", i); + for (j = 0; j < i; j++) printk("%d", buf[j]); + printk("]\n"); } +#endif return i; } /* - * js_sw_parity computes parity of __u64 + * js_sw_get_bits() and GB() compose bits from the triplet buffer into a __u64. + * Parameter 'pos' is bit number inside packet where to start at, 'num' is number + * of bits to be read, 'shift' is offset in the resulting __u64 to start at, bits + * is number of bits per triplet. + */ + +#define GB(pos,num,shift) js_sw_get_bits(buf, pos, num, shift, info->bits) + +static __u64 js_sw_get_bits(unsigned char *buf, int pos, int num, char shift, char bits) +{ + __u64 data = 0; + int tri = pos % bits; /* Start position */ + int i = pos / bits; + int bit = shift; + + while (num--) { + data |= (__u64)((buf[i] >> tri++) & 1) << bit++; /* Transfer bit */ + if (tri == bits) { + i++; /* Next triplet */ + tri = 0; + } + } + + return data; +} + +/* + * js_sw_init_digital() initializes a SideWinder 3D Pro joystick + * into digital mode. + */ + +static void js_sw_init_digital(int io, int speed) +{ + int seq[] = { 140, 140+725, 140+300, 0 }; + unsigned long flags; + int i, t; + + __save_flags(flags); + __cli(); + + i = 0; + do { + outb(0xff, io); /* Trigger */ + t = (JS_SW_TIMEOUT * speed) >> 10; + while ((inb(io) & 1) && t) t--; /* Wait for axis to fall back to 0 */ + udelay(seq[i]); /* Delay magic time */ + } while (seq[++i]); + + outb(0xff, io); /* Last trigger */ + + __restore_flags(flags); +} + +/* + * js_sw_parity() computes parity of __u64 */ static int js_sw_parity(__u64 t) { - t ^= t >> 32; - t ^= t >> 16; - t ^= t >> 8; - t ^= t >> 4; - t ^= t >> 2; - t ^= t >> 1; - return t & 1; + int x = t ^ (t >> 32); + x ^= x >> 16; + x ^= x >> 8; + x ^= x >> 4; + x ^= x >> 2; + x ^= x >> 1; + return x & 1; } /* - * js_sw_csum() computes checksum of nibbles in __u64 + * js_sw_ccheck() checks synchronization bits and computes checksum of nibbles. */ -static int js_sw_csum(__u64 t) +static int js_sw_check(__u64 t) { char sum = 0; - while (t) { + + if ((t & 0x8080808080808080ULL) ^ 0x80) /* Sync */ + return -1; + + while (t) { /* Sum */ sum += t & 0xf; t >>= 4; } + return sum & 0xf; } /* - * js_sw_read() reads and analyzes SideWinder joystick data. + * js_sw_parse() analyzes SideWinder joystick data, and writes the results into + * the axes and buttons arrays. */ -static int js_sw_read(void *xinfo, int **axes, int **buttons) +static int js_sw_parse(unsigned char *buf, struct js_sw_info *info, int **axes, int **buttons) { - struct js_sw_info *info = xinfo; - __u64 data; int hat, i; - switch (info->mode) { - - case JS_SW_MODE_3DP: + switch (info->type) { - if (info->optimize) { - i = js_sw_read_packet(info->io, -1, 22, JS_SW_EXT_STROBE, &data); - } else { - i = js_sw_read_packet(info->io, 64, 66, JS_SW_EXT_STROBE, &data); - if (i == 198) info->optimize = 1; - } + case JS_SW_TYPE_3DP: + case JS_SW_TYPE_F23: - if (i < 60) { - js_sw_init_digital(info->io); - info->optimize = 0; - return -1; - } + if (js_sw_check(GB(0,64,0)) || (hat = GB(6,1,3) | GB(60,3,0)) > 8) return -1; - if (((data & 0x8080808080808080ULL) ^ 0x80) || js_sw_csum(data) || - (hat = ((data >> 3) & 0x08) | ((data >> 60) & 0x07)) > 8) { - info->optimize = 0; - return -1; - } - axes[0][0] = ((data << 4) & 0x380) | ((data >> 16) & 0x07f); - axes[0][1] = ((data << 7) & 0x380) | ((data >> 24) & 0x07f); - axes[0][2] = ((data >> 28) & 0x180) | ((data >> 40) & 0x07f); - axes[0][3] = ((data >> 25) & 0x380) | ((data >> 48) & 0x07f); + axes[0][0] = GB( 3,3,7) | GB(16,7,0); + axes[0][1] = GB( 0,3,7) | GB(24,7,0); + axes[0][2] = GB(35,2,7) | GB(40,7,0); + axes[0][3] = GB(32,3,7) | GB(48,7,0); axes[0][4] = js_sw_hat_to_axis[hat].x; axes[0][5] = js_sw_hat_to_axis[hat].y; - buttons[0][0] = ((~data >> 31) & 0x80) | ((~data >> 8) & 0x7f); + buttons[0][0] = ~(GB(37,1,8) | GB(38,1,7) | GB(8,7,0)); + + return 0; + + case JS_SW_TYPE_GP: + + for (i = 0; i < info->number * 15; i += 15) { + + if (js_sw_parity(GB(i,15,0))) return -1; + + axes[i][0] = GB(i+3,1,0) - GB(i+2,1,0); + axes[i][1] = GB(i+0,1,0) - GB(i+1,1,0); + buttons[i][0] = ~GB(i+4,10,0); + + } return 0; - case JS_SW_MODE_PP: + case JS_SW_TYPE_PP: + case JS_SW_TYPE_FFP: - if (js_sw_read_packet(info->io, 48, 16, JS_SW_EXT_STROBE, &data) != 48) return -1; - if (!js_sw_parity(data) || (hat = (data >> 42) & 0xf) > 8) return -1; + if (!js_sw_parity(GB(0,48,0)) || (hat = GB(42,4,0)) > 8) return -1; - axes[0][0] = (data >> 9) & 0x3ff; - axes[0][1] = (data >> 19) & 0x3ff; - axes[0][2] = (data >> 29) & 0x07f; - axes[0][3] = (data >> 36) & 0x03f; + axes[0][0] = GB( 9,10,0); + axes[0][1] = GB(19,10,0); + axes[0][2] = GB(36, 6,0); + axes[0][3] = GB(29, 7,0); axes[0][4] = js_sw_hat_to_axis[hat].x; axes[0][5] = js_sw_hat_to_axis[hat].y; - buttons[0][0] = ~data & 0x1ff; + buttons[0][0] = ~GB(0,9,0); return 0; - case JS_SW_MODE_GP: + case JS_SW_TYPE_FSP: - if (js_sw_read_packet(info->io, 15 * info->number, 5 * info->number, - JS_SW_EXT_STROBE, &data) != 15 * info->number) return -1; - if (js_sw_parity(data)) return -1; - - for (i = 0; i < info->number; i++) { - axes[i][0] = ((data >> 3) & 1) - ((data >> 2) & 1); - axes[i][1] = ( data & 1) - ((data >> 1) & 1); - buttons[i][0] = (~data >> 4) & 0x3ff; - data >>= 15; - } + if (!js_sw_parity(GB(0,43,0)) || (hat = GB(28,4,0)) > 8) return -1; + + axes[0][0] = GB( 0,10,0); + axes[0][1] = GB(16,10,0); + axes[0][2] = GB(32, 6,0); + axes[0][3] = js_sw_hat_to_axis[hat].x; + axes[0][4] = js_sw_hat_to_axis[hat].y; + buttons[0][0] = ~(GB(10,6,0) | GB(26,2,6) | GB(38,2,8)); + + return 0; + + case JS_SW_TYPE_FFW: + + if (!js_sw_parity(GB(0,33,0))) return -1; + + axes[0][0] = GB( 0,10,0); + axes[0][1] = GB(10, 6,0); + axes[0][2] = GB(16, 6,0); + buttons[0][0] = ~GB(22,8,0); return 0; + } + + return -1; +} + +/* + * js_sw_read() reads SideWinder joystick data, and reinitializes + * the joystick in case of persistent problems. This is the function that is + * called from the generic code to poll the joystick. + */ + +static int js_sw_read(void *xinfo, int **axes, int **buttons) +{ + struct js_sw_info *info = xinfo; + unsigned char buf[JS_SW_LENGTH]; + int i; + + i = js_sw_read_packet(info->io, info->speed, buf, info->length, 0); + + if (info->type <= JS_SW_TYPE_F23 && info->length == 66 && i != 66) { /* Broken packet, try to fix */ - default: - return -1; + if (i == 64 && !js_sw_check(js_sw_get_bits(buf,0,64,0,1))) { /* Last init failed, 1 bit mode */ + printk(KERN_WARNING "joy-sidewinder: Joystick in wrong mode on %#x" + " - going to reinitialize.\n", info->io); + info->fail = JS_SW_FAIL; /* Reinitialize */ + i = 128; /* Bogus value */ + } + + if (i < 66 && GB(0,64,0) == GB(i*3-66,64,0)) /* 1 == 3 */ + i = 66; /* Everything is fine */ + + if (i < 66 && GB(0,64,0) == GB(66,64,0)) /* 1 == 2 */ + i = 66; /* Everything is fine */ + + if (i < 66 && GB(i*3-132,64,0) == GB(i*3-66,64,0)) { /* 2 == 3 */ + memmove(buf, buf + i - 22, 22); /* Move data */ + i = 66; /* Carry on */ + } + } + + if (i == info->length && !js_sw_parse(buf, info, axes, buttons)) { /* Parse data */ + + info->fail = 0; + info->ok++; + + if (info->type <= JS_SW_TYPE_F23 && info->length == 66 /* Many packets OK */ + && info->ok > JS_SW_OK) { + + printk(KERN_INFO "joy-sidewinder: No more trouble on %#x" + " - enabling optimization again.\n", info->io); + info->length = 22; + } + + return 0; } + + info->ok = 0; + info->fail++; + + if (info->type <= JS_SW_TYPE_F23 && info->length == 22 /* Consecutive bad packets */ + && info->fail > JS_SW_BAD) { + + printk(KERN_INFO "joy-sidewinder: Many bit errors on %#x" + " - disabling optimization.\n", info->io); + info->length = 66; + } + + if (info->fail < JS_SW_FAIL) return -1; /* Not enough, don't reinitialize yet */ + + printk(KERN_WARNING "joy-sidewinder: Too many bit errors on %#x" + " - reinitializing joystick.\n", info->io); + + if (!i && info->type <= JS_SW_TYPE_F23) { /* 3D Pro can be in analog mode */ + udelay(3 * JS_SW_TIMEOUT); + js_sw_init_digital(info->io, info->speed); + } + + udelay(JS_SW_TIMEOUT); + i = js_sw_read_packet(info->io, info->speed, buf, JS_SW_LENGTH, 0); /* Read normal data packet */ + udelay(JS_SW_TIMEOUT); + js_sw_read_packet(info->io, info->speed, buf, JS_SW_LENGTH, i); /* Read ID packet, this initializes the stick */ + + info->fail = JS_SW_FAIL; + + return -1; } /* @@ -290,7 +500,7 @@ * SideWinders. */ -static void __init js_sw_init_corr(int num_axes, int mode, int number, struct js_corr **corr) +static void __init js_sw_init_corr(int num_axes, int type, int number, struct js_corr **corr) { int i, j; @@ -305,9 +515,10 @@ corr[i][j].coef[3] = (1 << 29) / (511 - 32); } - switch (mode) { + switch (type) { - case JS_SW_MODE_3DP: + case JS_SW_TYPE_3DP: + case JS_SW_TYPE_F23: corr[i][2].type = JS_CORR_BROKEN; corr[i][2].prec = 4; @@ -320,33 +531,73 @@ break; - case JS_SW_MODE_PP: + case JS_SW_TYPE_PP: + case JS_SW_TYPE_FFP: corr[i][2].type = JS_CORR_BROKEN; - corr[i][2].prec = 1; - corr[i][2].coef[0] = 63 - 4; - corr[i][2].coef[1] = 64 + 4; - corr[i][2].coef[2] = (1 << 29) / (63 - 4); - corr[i][2].coef[3] = (1 << 29) / (63 - 4); + corr[i][2].prec = 0; + corr[i][2].coef[0] = 31 - 2; + corr[i][2].coef[1] = 32 + 2; + corr[i][2].coef[2] = (1 << 29) / (31 - 2); + corr[i][2].coef[3] = (1 << 29) / (31 - 2); corr[i][3].type = JS_CORR_BROKEN; - corr[i][3].prec = 0; - corr[i][3].coef[0] = 31 - 2; - corr[i][3].coef[1] = 32 + 2; - corr[i][3].coef[2] = (1 << 29) / (31 - 2); - corr[i][3].coef[3] = (1 << 29) / (31 - 2); + corr[i][3].prec = 1; + corr[i][3].coef[0] = 63 - 4; + corr[i][3].coef[1] = 64 + 4; + corr[i][3].coef[2] = (1 << 29) / (63 - 4); + corr[i][3].coef[3] = (1 << 29) / (63 - 4); j = 4; break; + case JS_SW_TYPE_FFW: + + corr[i][0].type = JS_CORR_BROKEN; + corr[i][0].prec = 2; + corr[i][0].coef[0] = 511 - 8; + corr[i][0].coef[1] = 512 + 8; + corr[i][0].coef[2] = (1 << 29) / (511 - 8); + corr[i][0].coef[3] = (1 << 29) / (511 - 8); + + corr[i][1].type = JS_CORR_BROKEN; + corr[i][1].prec = 1; + corr[i][1].coef[0] = 63; + corr[i][1].coef[1] = 63; + corr[i][1].coef[2] = (1 << 29) / -63; + corr[i][1].coef[3] = (1 << 29) / -63; + + corr[i][2].type = JS_CORR_BROKEN; + corr[i][2].prec = 1; + corr[i][2].coef[0] = 63; + corr[i][2].coef[1] = 63; + corr[i][2].coef[2] = (1 << 29) / -63; + corr[i][2].coef[3] = (1 << 29) / -63; + + j = 3; + + break; + + case JS_SW_TYPE_FSP: + + corr[i][2].type = JS_CORR_BROKEN; + corr[i][2].prec = 0; + corr[i][2].coef[0] = 31 - 2; + corr[i][2].coef[1] = 32 + 2; + corr[i][2].coef[2] = (1 << 29) / (31 - 2); + corr[i][2].coef[3] = (1 << 29) / (31 - 2); + + j = 3; + + break; + default: j = 0; - } - for (; j < num_axes; j++) { + for (; j < num_axes; j++) { /* Hats & other binary axes */ corr[i][j].type = JS_CORR_BROKEN; corr[i][j].prec = 0; corr[i][j].coef[0] = 0; @@ -358,83 +609,211 @@ } /* + * js_sw_print_packet() prints the contents of a SideWinder packet. + */ + +static void js_sw_print_packet(char *name, int length, unsigned char *buf, char bits) +{ + int i; + + printk("joy-sidewinder: %s packet, %d bits. [", name, length); + for (i = (((length + 3) >> 2) - 1); i >= 0; i--) + printk("%x", (int)js_sw_get_bits(buf, i << 2, 4, 0, bits)); + printk("]\n"); +} + +/* + * js_sw_3dp_id() translates the 3DP id into a human legible string. + * Unfortunately I don't know how to do this for the other SW types. + */ + +static void js_sw_3dp_id(unsigned char *buf, char *comment) +{ + int i; + char pnp[8], rev[9]; + + for (i = 0; i < 7; i++) /* ASCII PnP ID */ + pnp[i] = js_sw_get_bits(buf, 24+8*i, 8, 0, 1); + + for (i = 0; i < 8; i++) /* ASCII firmware revision */ + rev[i] = js_sw_get_bits(buf, 88+8*i, 8, 0, 1); + + pnp[7] = rev[8] = 0; + + sprintf(comment, " [PnP %d.%02d id %s rev %s]", + (int) (js_sw_get_bits(buf, 8, 6, 6, 1) | /* Two 6-bit values */ + js_sw_get_bits(buf, 16, 6, 0, 1)) / 100, + (int) (js_sw_get_bits(buf, 8, 6, 6, 1) | + js_sw_get_bits(buf, 16, 6, 0, 1)) % 100, + pnp, rev); +} + +/* + * js_sw_guess_mode() checks the upper two button bits for toggling - + * indication of that the joystick is in 3-bit mode. This is documented + * behavior for 3DP ID packet, and for example the FSP does this in + * normal packets instead. Fun ... + */ + +static int js_sw_guess_mode(unsigned char *buf, int len) +{ + int i; + unsigned char xor = 0; + for (i = 1; i < len; i++) xor |= (buf[i - 1] ^ buf[i]) & 6; + return !!xor * 2 + 1; +} + +/* * js_sw_probe() probes for SideWinder type joysticks. */ static struct js_port __init *js_sw_probe(int io, struct js_port *port) { struct js_sw_info info; - char *name; - int i, j, axes, buttons; - __u64 data; - unsigned char u; + char *names[] = {NULL, "SideWinder 3D Pro", "Flight2000 F-23", "SideWinder GamePad", "SideWinder Precision Pro", + "SideWinder Force Feedback Pro", "SideWinder FreeStyle Pro", "SideWinder Force Feedback Wheel" }; + char axes[] = { 0, 6, 6, 2, 6, 6, 5, 3 }; + char buttons[] = { 0, 9, 9, 10, 9, 9, 10, 8 }; + int i, j, k, l, speed; + unsigned char buf[JS_SW_LENGTH]; + unsigned char idbuf[JS_SW_LENGTH]; + unsigned char m = 1; + char comment[40]; + comment[0] = 0; if (check_region(io, 1)) return port; - if (((u = inb(io)) & 3) == 3) return port; - outb(0xff,io); - if (!((inb(io) ^ u) & ~u & 0xf)) return port; - - i = js_sw_read_packet(io, JS_SW_MAX_LENGTH, -1, JS_SW_EXT_STROBE, &data); - - if (!i) { - udelay(JS_SW_MIN_TIME); - js_sw_init_digital(io); - udelay(JS_SW_MAX_TIME); - i = js_sw_read_packet(io, JS_SW_MAX_LENGTH, -1, JS_SW_EXT_STROBE, &data); - } - - switch (i) { - case 0: - return port; - case 5: - case 10: - case 15: - case 20: - case 30: - case 45: - case 60: - info.mode = JS_SW_MODE_GP; - outb(0xff,io); /* Kick into 3-bit mode */ - udelay(JS_SW_MAX_TIME); - i = js_sw_read_packet(io, 60, -1, JS_SW_EXT_STROBE, &data); /* Get total length */ - udelay(JS_SW_MIN_TIME); - j = js_sw_read_packet(io, 15, -1, JS_SW_MIN_STROBE, &data); /* Get subpacket length */ - if (!i || !j) { - printk(KERN_WARNING "joy-sidewinder: SideWinder GamePad detected (%d,%d)," - " but not idenfitied.\n", i, j); - return port; + + speed = js_sw_measure_speed(io); + + i = js_sw_read_packet(io, speed, buf, JS_SW_LENGTH, 0); /* Read normal packet */ + m |= js_sw_guess_mode(buf, i); /* Data packet (1-bit) can carry mode info [FSP] */ + udelay(JS_SW_TIMEOUT); + +#ifdef JS_SW_DEBUG + printk(KERN_DEBUG "joy-sidewinder: Init 1: Mode %d. Length %d.\n", m , i); +#endif + + if (!i) { /* No data. 3d Pro analog mode? */ + js_sw_init_digital(io, speed); /* Switch to digital */ + udelay(JS_SW_TIMEOUT); + i = js_sw_read_packet(io, speed, buf, JS_SW_LENGTH, 0); /* Retry reading packet */ + udelay(JS_SW_TIMEOUT); +#ifdef JS_SW_DEBUG + printk(KERN_DEBUG "joy-sidewinder: Init 1b: Length %d.\n", i); +#endif + if (!i) return port; /* No data -> FAIL */ + } + + j = js_sw_read_packet(io, speed, idbuf, JS_SW_LENGTH, i); /* Read ID. This initializes the stick */ + m |= js_sw_guess_mode(idbuf, j); /* ID packet should carry mode info [3DP] */ + +#ifdef JS_SW_DEBUG + printk(KERN_DEBUG "joy-sidewinder: Init 2: Mode %d. ID Length %d.\n", m , j); +#endif + + if (!j) { /* Read ID failed. Happens in 1-bit mode on PP */ + udelay(JS_SW_TIMEOUT); + i = js_sw_read_packet(io, speed, buf, JS_SW_LENGTH, 0); /* Retry reading packet */ +#ifdef JS_SW_DEBUG + printk(KERN_DEBUG "joy-sidewinder: Init 2b: Mode %d. Length %d.\n", m , i); +#endif + if (!i) return port; + udelay(JS_SW_TIMEOUT); + j = js_sw_read_packet(io, speed, idbuf, JS_SW_LENGTH, i);/* Retry reading ID */ +#ifdef JS_SW_DEBUG + printk(KERN_DEBUG "joy-sidewinder: Init 2c: ID Length %d.\n", j); +#endif + + } + + k = JS_SW_FAIL; /* Try JS_SW_FAIL times */ + l = 0; + + do { + k--; + udelay(JS_SW_TIMEOUT); + i = js_sw_read_packet(io, speed, buf, JS_SW_LENGTH, 0); /* Read data packet */ +#ifdef JS_SW_DEBUG + printk(KERN_DEBUG "joy-sidewinder: Init 3: Length %d.\n", i); +#endif + + if (i > l) { /* Longer? As we can only lose bits, it makes */ + /* no sense to try detection for a packet shorter */ + l = i; /* than the previous one */ + + info.number = 1; + info.io = io; + info.speed = speed; + info.length = i; + info.bits = m; + info.fail = 0; + info.ok = 0; + info.type = 0; + + switch (i * m) { + case 60: + info.number++; + case 45: /* Ambiguous packet length */ + if (j <= 40) { /* ID length less or eq 40 -> FSP */ + case 43: + info.type = JS_SW_TYPE_FSP; + break; + } + info.number++; + case 30: + info.number++; + case 15: + info.type = JS_SW_TYPE_GP; + break; + case 33: + case 31: + info.type = JS_SW_TYPE_FFW; + break; + case 48: /* Ambiguous */ + if (j == 14) { /* ID lenght 14*3 -> FFP */ + info.type = JS_SW_TYPE_FFP; + sprintf(comment, " [AC %s]", js_sw_get_bits(idbuf,38,1,0,3) ? "off" : "on"); + } else + info.type = JS_SW_TYPE_PP; + break; + case 198: + info.length = 22; + case 64: + info.type = JS_SW_TYPE_3DP; + if (j == 160) js_sw_3dp_id(idbuf, comment); + break; } - info.number = i / j; - axes = 2; buttons = 10; name = "SideWinder GamePad"; - break; - case 16: - case 48: - info.mode = JS_SW_MODE_PP; info.number = 1; - axes = 6; buttons = 9; name = "SideWinder Precision Pro"; - break; - case 64: - case 66: - info.mode = JS_SW_MODE_3DP; info.number = 1; info.optimize = 0; - axes = 6; buttons = 8; name = "SideWinder 3D Pro"; - break; - case 72: - return port; - default: - printk(KERN_WARNING "joy-sidewinder: unknown joystick device detected " - "(io=%#x, count=%d, data=0x%08x%08x), contact \n", - io, i, (int)(data >> 32), (int)(data & 0xffffffff)); - return port; + } + + } while (k && !info.type); + + if (!info.type) { + printk(KERN_WARNING "joy-sidewinder: unknown joystick device detected " + "(io=%#x), contact \n", io); + js_sw_print_packet("ID", j * 3, idbuf, 3); + js_sw_print_packet("Data", i * m, buf, m); + return port; } - info.io = io; +#ifdef JS_SW_DEBUG + js_sw_print_packet("ID", j * 3, idbuf, 3); + js_sw_print_packet("Data", i * m, buf, m); +#endif + + k = i; request_region(io, 1, "joystick (sidewinder)"); + port = js_register_port(port, &info, info.number, sizeof(struct js_sw_info), js_sw_read); + for (i = 0; i < info.number; i++) - printk(KERN_INFO "js%d: %s at %#x\n", - js_register_device(port, i, axes, buttons, name, js_sw_open, js_sw_close), name, io); - js_sw_init_corr(axes, info.mode, info.number, port->corr); + printk(KERN_INFO "js%d: %s%s at %#x [%d ns res %d-bit id %d data %d]\n", + js_register_device(port, i, axes[info.type], buttons[info.type], + names[info.type], js_sw_open, js_sw_close), names[info.type], comment, io, + 1000000 / speed, m, j, k); + + js_sw_init_corr(axes[info.type], info.type, info.number, port->corr); return port; } @@ -463,9 +842,9 @@ int i; struct js_sw_info *info; - while (js_sw_port != NULL) { + while (js_sw_port) { for (i = 0; i < js_sw_port->ndevs; i++) - if (js_sw_port->devs[i] != NULL) + if (js_sw_port->devs[i]) js_unregister_device(js_sw_port->devs[i]); info = js_sw_port->info; release_region(info->io, 1); diff -u --recursive --new-file v2.2.13/linux/drivers/char/joystick/joy-spaceball.c linux/drivers/char/joystick/joy-spaceball.c --- v2.2.13/linux/drivers/char/joystick/joy-spaceball.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/joystick/joy-spaceball.c Tue Jan 4 10:12:14 2000 @@ -0,0 +1,347 @@ +/* + * joy-spaceball.c Version 0.1 + * + * Copyright (c) 1998 David Thompson + * Copyright (c) 1999 Vojtech Pavlik + * Copyright (c) 1999 Joseph Krahn + * + * Sponsored by SuSE + */ + +/* + * This is a module for the Linux joystick driver, supporting + * the SpaceTec SpaceBall 4000 FLX. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Constants. + */ + +#define N_JOYSTICK_SBALL 12 +#define JS_SBALL_MAX_LENGTH 128 + +/* + * List of SpaceBalls. + */ + +static struct js_port* js_sball_port = NULL; + +/* + * Per-Ball data. + */ + +struct js_sball_info { + struct tty_struct* tty; + struct js_port* port; + int idx; + unsigned char data[JS_SBALL_MAX_LENGTH]; + int js; + char used; +}; + +/* + * js_sball_process_packet() decodes packets the driver receives from the + * SpaceBall. + */ + +static void js_sball_process_packet(struct js_sball_info* info) +{ + int i,b; + int **axes = info->port->axes; + int **buttons = info->port->buttons; + unsigned char *data = info->data; + + if (info->idx < 2) return; + + switch (info->data[0]) { + + case '@': /* Reset packet */ + info->data[info->idx - 1] = 0; + for (i = 1; i < info->idx && info->data[i] == ' '; i++); + + printk(KERN_INFO "js%d: SpaceBall 4000FLX [%s] on %s%d\n", + info->js, info->data + i, info->tty->driver.name, + MINOR(info->tty->device) - info->tty->driver.minor_start); + + memset(axes[0], 0, sizeof(int) * 6); /* All axes, buttons should be zero */ + buttons[0][0] = 0; + break; + + case 'D': /* Ball data */ + if (info->idx != 16) return; + if (!info->port->devs[0]) return; + axes[0][0] = ((data[3] << 8) | data[4] ); + axes[0][1] = ((data[5] << 8) | data[6] ); + axes[0][2] = ((data[7] << 8) | data[8] ); + axes[0][3] = ((data[9] << 8) | data[10]); + axes[0][4] = ((data[11]<< 8) | data[12]); + axes[0][5] = ((data[13]<< 8) | data[14]); + for(i = 0; i < 6; i ++) if (axes[0][i] & 0x8000) axes[0][i] -= 0x10000; + break; + + case 'K': /* Button data, part1 */ + /* We can ignore this packet for the SB 4000FLX. */ + break; + + case '.': /* Button data, part2 */ + if (info->idx != 4) return; + if (!info->port->devs[0]) return; + b = (data[1] & 0xbf) << 8 | (data[2] & 0xbf); + buttons[0][0] = ((b & 0x1f80) >> 1 | (b & 0x3f)); + break; + + case '?': /* Error packet */ + info->data[info->idx - 1] = 0; + printk(KERN_ERR "joy-spaceball: Device error. [%s]\n",info->data+1); + break; + + case 'A': /* reply to A command (ID# report) */ + case 'B': /* reply to B command (beep) */ + case 'H': /* reply to H command (firmware report) */ + case 'S': /* reply to S command (single beep) */ + case 'Y': /* reply to Y command (scale flag) */ + case '"': /* reply to "n command (report info, part n) */ + break; + + case 'P': /* Pulse (update) speed */ + if (info->idx != 3) return; /* data[2],data[3] = hex digits for speed 00-FF */ + break; + + default: + printk("joy-spaceball: Unknown packet %d length %d:", data[0], info->idx); + for (i = 0; i < info->idx; i++) printk(" %02x", data[i]); + printk("\n"); + return; + } +} + +/* + * js_sball_open() is a callback from the joystick device open routine. + */ + +static int js_sball_open(struct js_dev *jd) +{ + struct js_sball_info *info = jd->port->info; + info->used++; + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_sball_close() is a callback from the joystick device release routine. + */ + +static int js_sball_close(struct js_dev *jd) +{ + struct js_sball_info *info = jd->port->info; + if (!--info->used) { + js_unregister_device(jd->port->devs[0]); + js_sball_port = js_unregister_port(jd->port); + } + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_sball_init_corr() initializes the correction values for the SpaceBall. + */ + +static void __init js_sball_init_corr(struct js_corr **corr) +{ + int j; + + for (j = 0; j < 3; j++) { + corr[0][j].type = JS_CORR_BROKEN; + corr[0][j].prec = 0; + corr[0][j].coef[0] = 0; + corr[0][j].coef[1] = 0; + corr[0][j].coef[2] = 50000; + corr[0][j].coef[3] = 50000; + } + for (j = 3; j < 6; j++) { + corr[0][j].type = JS_CORR_BROKEN; + corr[0][j].prec = 0; + corr[0][j].coef[0] = 0; + corr[0][j].coef[1] = 0; + corr[0][j].coef[2] = 300000; + corr[0][j].coef[3] = 300000; + } +} + +/* + * js_sball_ldisc_open() is the routine that is called upon setting our line + * discipline on a tty. + */ + +static int js_sball_ldisc_open(struct tty_struct *tty) +{ + struct js_sball_info iniinfo; + struct js_sball_info *info = &iniinfo; + + info->tty = tty; + info->idx = 0; + info->used = 1; + + js_sball_port = js_register_port(js_sball_port, info, 1, sizeof(struct js_sball_info), NULL); + + info = js_sball_port->info; + info->port = js_sball_port; + tty->disc_data = info; + + info->js = js_register_device(js_sball_port, 0, 6, 12, "SpaceBall 4000 FLX", js_sball_open, js_sball_close); + + js_sball_init_corr(js_sball_port->corr); + + MOD_INC_USE_COUNT; + + return 0; +} + +/* + * js_sball_ldisc_close() is the opposite of js_sball_ldisc_open() + */ + +static void js_sball_ldisc_close(struct tty_struct *tty) +{ + struct js_sball_info* info = (struct js_sball_info*) tty->disc_data; + if (!--info->used) { + js_unregister_device(info->port->devs[0]); + js_sball_port = js_unregister_port(info->port); + } + MOD_DEC_USE_COUNT; +} + +/* + * js_sball_ldisc_receive() is called by the low level driver when characters + * are ready for us. We then buffer them for further processing, or call the + * packet processing routine. + */ + +static void js_sball_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) +{ + struct js_sball_info* info = (struct js_sball_info*) tty->disc_data; + int i; + int esc_flag = 0; + +/* + * Spaceball 4000 FLX packets all start with a one letter packet-type decriptor, + * and end in 0x0d. It uses '^' as an escape for 0x0d characters which can + * occur in the axis values. ^M, ^Q and ^S all mean 0x0d, depending (I think) + * on whether the axis value is increasing, decreasing, or same as before. + * (I don't see why this is useful). + * + * There may be a nicer whay to handle the escapes, but I wanted to be sure to + * allow for an escape at the end of the buffer. + */ + for (i = 0; i < count; i++) { + if (esc_flag) { /* If the last char was an escape, overwrite it with the escaped value */ + + switch (cp[i]){ + case 'M': + case 'Q': + case 'S': + info->data[info->idx]=0x0d; + break; + case '^': /* escaped escape; leave as is */ + break; + default: + printk("joy-spaceball: Unknown escape character: %02x\n", cp[i]); + } + + esc_flag = 0; + + } else { + + if (info->idx < JS_SBALL_MAX_LENGTH) + info->data[info->idx++] = cp[i]; + + if (cp[i] == 0x0D) { + if (info->idx) + js_sball_process_packet(info); + info->idx = 0; + } else + if (cp[i] == '^') esc_flag = 1; + + } + } +} + +/* + * js_sball_ldisc_room() reports how much room we do have for receiving data. + * Although we in fact have infinite room, we need to specify some value + * here, so why not the size of our packet buffer. It's big anyway. + */ + +static int js_sball_ldisc_room(struct tty_struct *tty) +{ + return JS_SBALL_MAX_LENGTH; +} + +/* + * The line discipline structure. + */ + +static struct tty_ldisc js_sball_ldisc = { + magic: TTY_LDISC_MAGIC, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) + name: "spaceball", +#endif + open: js_sball_ldisc_open, + close: js_sball_ldisc_close, + receive_buf: js_sball_ldisc_receive, + receive_room: js_sball_ldisc_room, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +#ifdef MODULE +int init_module(void) +#else +int __init js_sball_init(void) +#endif +{ + if (tty_register_ldisc(N_JOYSTICK_SBALL, &js_sball_ldisc)) { + printk(KERN_ERR "joy-spaceball: Error registering line discipline.\n"); + return -ENODEV; + } + + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + tty_register_ldisc(N_JOYSTICK_SBALL, NULL); +} +#endif diff -u --recursive --new-file v2.2.13/linux/drivers/char/joystick/joy-spaceorb.c linux/drivers/char/joystick/joy-spaceorb.c --- v2.2.13/linux/drivers/char/joystick/joy-spaceorb.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/joystick/joy-spaceorb.c Tue Jan 4 10:12:14 2000 @@ -0,0 +1,305 @@ +/* + * joy-spaceorb.c Version 0.1 + * + * Copyright (c) 1998 David Thompson + * Copyright (c) 1999 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * This is a module for the Linux joystick driver, supporting + * the SpaceTec SpaceOrb 360 and SpaceBall Avenger 6dof controllers. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Constants. + */ + +#define N_JOYSTICK_ORB 15 +#define JS_ORB_MAX_LENGTH 64 + +/* + * List of SpaceOrbs. + */ + +static struct js_port* js_orb_port = NULL; + +/* + * Per-Orb data. + */ + +struct js_orb_info { + struct tty_struct* tty; + struct js_port* port; + int idx; + unsigned char data[JS_ORB_MAX_LENGTH]; + int js; + char used; +}; + +static unsigned char js_orb_xor[] = "SpaceWare"; + +static unsigned char *js_orb_errors[] = { "EEPROM storing 0 failed", "Receive queue overflow", "Transmit queue timeout", + "Bad packet", "Power brown-out", "EEPROM checksum error", "Hardware fault" }; + +/* + * js_orb_process_packet() decodes packets the driver receives from the + * SpaceOrb. + */ + +static void js_orb_process_packet(struct js_orb_info* info) +{ + int i; + int **axes = info->port->axes; + int **buttons = info->port->buttons; + unsigned char *data = info->data; + unsigned char c = 0; + + if (info->idx < 2) return; + for (i = 0; i < info->idx; i++) c ^= data[i]; + if (c) return; + + switch (info->data[0]) { + + case 'R': /* Reset packet */ + info->data[info->idx - 1] = 0; + for (i = 1; i < info->idx && info->data[i] == ' '; i++); + printk(KERN_INFO "js%d: SpaceOrb 360 [%s] on %s%d\n", + info->js, info->data + i, info->tty->driver.name, + MINOR(info->tty->device) - info->tty->driver.minor_start); + break; + + case 'D': /* Ball + button data */ + if (info->idx != 12) return; + if (!info->port->devs[0]) return; + for (i = 0; i < 9; i++) info->data[i+2] ^= js_orb_xor[i]; + axes[0][0] = ( data[2] << 3) | (data[ 3] >> 4); + axes[0][1] = ((data[3] & 0x0f) << 6) | (data[ 4] >> 1); + axes[0][2] = ((data[4] & 0x01) << 9) | (data[ 5] << 2) | (data[4] >> 5); + axes[0][3] = ((data[6] & 0x1f) << 5) | (data[ 7] >> 2); + axes[0][4] = ((data[7] & 0x03) << 8) | (data[ 8] << 1) | (data[7] >> 6); + axes[0][5] = ((data[9] & 0x3f) << 4) | (data[10] >> 3); + for(i = 0; i < 6; i ++) if (axes[0][i] & 0x200) axes[0][i] -= 1024; + buttons[0][0] = data[1]; + break; + + case 'K': /* Button data */ + if (info->idx != 5) return; + if (!info->port->devs[0]) return; + buttons[0][0] = data[2]; + break; + + case 'E': /* Error packet */ + if (info->idx != 4) return; + printk(KERN_ERR "joy-spaceorb: Device error. [ "); + for (i = 0; i < 7; i++) + if (data[1] & (1 << i)) + printk("%s ", js_orb_errors[i]); + printk("]\n"); + break; + + case 'N': /* Null region */ + if (info->idx != 3) return; + break; + + case 'P': /* Pulse (update) speed */ + if (info->idx != 4) return; + break; + + default: + printk("joy-spaceorb: Unknown packet %d length %d:", data[0], info->idx); + for (i = 0; i < info->idx; i++) printk(" %02x", data[i]); + printk("\n"); + return; + } +} + +/* + * js_orb_open() is a callback from the joystick device open routine. + */ + +static int js_orb_open(struct js_dev *jd) +{ + struct js_orb_info *info = jd->port->info; + info->used++; + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_orb_close() is a callback from the joystick device release routine. + */ + +static int js_orb_close(struct js_dev *jd) +{ + struct js_orb_info *info = jd->port->info; + if (!--info->used) { + js_unregister_device(jd->port->devs[0]); + js_orb_port = js_unregister_port(jd->port); + } + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_orb_init_corr() initializes the correction values for the SpaceOrb. + */ + +static void __init js_orb_init_corr(struct js_corr **corr) +{ + int j; + + for (j = 0; j < 6; j++) { + corr[0][j].type = JS_CORR_BROKEN; + corr[0][j].prec = 0; + corr[0][j].coef[0] = 0 ; + corr[0][j].coef[1] = 0 ; + corr[0][j].coef[2] = (1 << 29) / 511; + corr[0][j].coef[3] = (1 << 29) / 511; + } +} + +/* + * js_orb_ldisc_open() is the routine that is called upon setting our line + * discipline on a tty. + */ + +static int js_orb_ldisc_open(struct tty_struct *tty) +{ + struct js_orb_info iniinfo; + struct js_orb_info *info = &iniinfo; + + info->tty = tty; + info->idx = 0; + info->used = 1; + + js_orb_port = js_register_port(js_orb_port, info, 1, sizeof(struct js_orb_info), NULL); + + info = js_orb_port->info; + info->port = js_orb_port; + tty->disc_data = info; + + info->js = js_register_device(js_orb_port, 0, 6, 7, "SpaceOrb 360", js_orb_open, js_orb_close); + + js_orb_init_corr(js_orb_port->corr); + + MOD_INC_USE_COUNT; + + return 0; +} + +/* + * js_orb_ldisc_close() is the opposite of js_orb_ldisc_open() + */ + +static void js_orb_ldisc_close(struct tty_struct *tty) +{ + struct js_orb_info* info = (struct js_orb_info*) tty->disc_data; + if (!--info->used) { + js_unregister_device(info->port->devs[0]); + js_orb_port = js_unregister_port(info->port); + } + MOD_DEC_USE_COUNT; +} + +/* + * js_orb_ldisc_receive() is called by the low level driver when characters + * are ready for us. We then buffer them for further processing, or call the + * packet processing routine. + */ + +static void js_orb_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) +{ + struct js_orb_info* info = (struct js_orb_info*) tty->disc_data; + int i; + + for (i = 0; i < count; i++) { + if (~cp[i] & 0x80) { + if (info->idx) js_orb_process_packet(info); + info->idx = 0; + } + if (info->idx < JS_ORB_MAX_LENGTH) + info->data[info->idx++] = cp[i] & 0x7f; + } +} + +/* + * js_orb_ldisc_room() reports how much room we do have for receiving data. + * Although we in fact have infinite room, we need to specify some value + * here, so why not the size of our packet buffer. It's big anyway. + */ + +static int js_orb_ldisc_room(struct tty_struct *tty) +{ + return JS_ORB_MAX_LENGTH; +} + +/* + * The line discipline structure. + */ + +static struct tty_ldisc js_orb_ldisc = { + magic: TTY_LDISC_MAGIC, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) + name: "spaceorb", +#endif + open: js_orb_ldisc_open, + close: js_orb_ldisc_close, + receive_buf: js_orb_ldisc_receive, + receive_room: js_orb_ldisc_room, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +#ifdef MODULE +int init_module(void) +#else +int __init js_orb_init(void) +#endif +{ + if (tty_register_ldisc(N_JOYSTICK_ORB, &js_orb_ldisc)) { + printk(KERN_ERR "joy-spaceorb: Error registering line discipline.\n"); + return -ENODEV; + } + + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + tty_register_ldisc(N_JOYSTICK_ORB, NULL); +} +#endif diff -u --recursive --new-file v2.2.13/linux/drivers/char/joystick/joy-thrustmaster.c linux/drivers/char/joystick/joy-thrustmaster.c --- v2.2.13/linux/drivers/char/joystick/joy-thrustmaster.c Tue Dec 1 19:05:05 1998 +++ linux/drivers/char/joystick/joy-thrustmaster.c Tue Jan 4 10:12:14 2000 @@ -1,7 +1,9 @@ /* * joy-thrustmaster.c Version 1.2 * - * Copyright (c) 1998 Vojtech Pavlik + * Copyright (c) 1998-1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* @@ -25,7 +27,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: + * e-mail - mail your message to , or by paper mail: * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic */ @@ -38,28 +40,15 @@ #include #include #include +#include #define JS_TM_MAX_START 400 -#define JS_TM_MAX_STROBE 25 +#define JS_TM_MAX_STROBE 45 #define JS_TM_MAX_LENGTH 13 #define JS_TM_MODE_M3DI 1 #define JS_TM_MODE_3DRP 3 -#define JS_TM_MODE_WCS3 4 - -#define JS_TM_MODE_MAX 5 /* Last mode + 1 */ - -#define JS_TM_BYTE_A0 0 -#define JS_TM_BYTE_A1 1 -#define JS_TM_BYTE_A2 3 -#define JS_TM_BYTE_A3 4 -#define JS_TM_BYTE_A4 6 -#define JS_TM_BYTE_A5 7 - -#define JS_TM_BYTE_D0 2 -#define JS_TM_BYTE_D1 5 -#define JS_TM_BYTE_D2 8 -#define JS_TM_BYTE_D3 9 +#define JS_TM_MODE_FGP 163 #define JS_TM_BYTE_ID 10 #define JS_TM_BYTE_REV 11 @@ -68,48 +57,39 @@ static int js_tm_port_list[] __initdata = {0x201, 0}; static struct js_port* js_tm_port __initdata = NULL; +static unsigned char js_tm_byte_a[16] = { 0, 1, 3, 4, 6, 7 }; +static unsigned char js_tm_byte_d[16] = { 2, 5, 8, 9 }; + struct js_tm_info { int io; unsigned char mode; }; -static int js_tm_id_to_def[JS_TM_MODE_MAX] = {0x00, 0x42, 0x00, 0x22, 0x00}; - /* * js_tm_read_packet() reads a ThrustMaster packet. */ static int js_tm_read_packet(int io, unsigned char *data) { - unsigned int t, t1; + unsigned int t, p; unsigned char u, v, error; int i, j; unsigned long flags; - int start = (js_time_speed * JS_TM_MAX_START) >> 10; - int strobe = (js_time_speed * JS_TM_MAX_STROBE) >> 10; - error = 0; i = j = 0; + p = t = JS_TM_MAX_START; __save_flags(flags); __cli(); outb(0xff,io); - - t = js_get_time(); + + v = inb(io) >> 4; do { - u = inb(io); - t1 = js_get_time(); - } while ((u & 1) && js_delta(t1, t) < start); - - t = t1; - u >>= 4; - - do { - v = inb(io) >> 4; - t1 = js_get_time(); - if ((u ^ v) & u & 2) { + t--; + u = v; v = inb(io) >> 4; + if (~v & u & 2) { if (j) { if (j < 9) { /* Data bit */ data[i] |= (~v & 1) << (j - 1); @@ -124,10 +104,9 @@ error |= ~v & 1; j++; } - t = t1; + p = t = (p - t) << 1; } - u = v; - } while (!error && i < JS_TM_MAX_LENGTH && js_delta(t1,t) < strobe); + } while (!error && i < JS_TM_MAX_LENGTH && t > 0); __restore_flags(flags); @@ -142,46 +121,39 @@ { struct js_tm_info *info = xinfo; unsigned char data[JS_TM_MAX_LENGTH]; + int i; - if (js_tm_read_packet(info->io, data)) { - printk(KERN_WARNING "joy-thrustmaster: failed to read data packet\n"); - return -1; - } - if (data[JS_TM_BYTE_ID] != info->mode) { - printk(KERN_WARNING "joy-thrustmaster: ID (%d) != mode (%d)\n", - data[JS_TM_BYTE_ID], info->mode); - return -1; - } - if (data[JS_TM_BYTE_DEF] != js_tm_id_to_def[info->mode]) { - printk(KERN_WARNING "joy-thrustmaster: DEF (%d) != def(mode) (%d)\n", - data[JS_TM_BYTE_DEF], js_tm_id_to_def[info->mode]); - return -1; - } + if (js_tm_read_packet(info->io, data)) return -1; + if (data[JS_TM_BYTE_ID] != info->mode) return -1; + + for (i = 0; i < data[JS_TM_BYTE_DEF] >> 4; i++) axes[0][i] = data[js_tm_byte_a[i]]; switch (info->mode) { case JS_TM_MODE_M3DI: - axes[0][0] = data[JS_TM_BYTE_A0]; - axes[0][1] = data[JS_TM_BYTE_A1]; - axes[0][2] = data[JS_TM_BYTE_A2]; - axes[0][3] = data[JS_TM_BYTE_A3]; - - axes[0][4] = ((data[JS_TM_BYTE_D0] >> 3) & 1) - ((data[JS_TM_BYTE_D0] >> 1) & 1); - axes[0][5] = ((data[JS_TM_BYTE_D0] >> 2) & 1) - ( data[JS_TM_BYTE_D0] & 1); + axes[0][4] = ((data[js_tm_byte_d[0]] >> 3) & 1) - ((data[js_tm_byte_d[0]] >> 1) & 1); + axes[0][5] = ((data[js_tm_byte_d[0]] >> 2) & 1) - ( data[js_tm_byte_d[0]] & 1); - buttons[0][0] = ((data[JS_TM_BYTE_D0] >> 6) & 0x01) | ((data[JS_TM_BYTE_D0] >> 3) & 0x06) - | ((data[JS_TM_BYTE_D0] >> 4) & 0x08) | ((data[JS_TM_BYTE_D1] >> 2) & 0x30); + buttons[0][0] = ((data[js_tm_byte_d[0]] >> 6) & 0x01) | ((data[js_tm_byte_d[0]] >> 3) & 0x06) + | ((data[js_tm_byte_d[0]] >> 4) & 0x08) | ((data[js_tm_byte_d[1]] >> 2) & 0x30); return 0; case JS_TM_MODE_3DRP: + case JS_TM_MODE_FGP: + + buttons[0][0] = (data[js_tm_byte_d[0]] & 0x3f) | ((data[js_tm_byte_d[1]] << 6) & 0xc0) + | (( ((int) data[js_tm_byte_d[0]]) << 2) & 0x300); + + return 0; + + default: - axes[0][0] = data[JS_TM_BYTE_A0]; - axes[0][1] = data[JS_TM_BYTE_A1]; + buttons[0][0] = 0; - buttons[0][0] = ( data[JS_TM_BYTE_D0] & 0x3f) | ((data[JS_TM_BYTE_D1] << 6) & 0xc0) - | (( ((int) data[JS_TM_BYTE_D0]) << 2) & 0x300); + for (i = 0; i < (data[JS_TM_BYTE_DEF] & 0xf); i++) + buttons[0][0] |= ((int) data[js_tm_byte_d[i]]) << (i << 3); return 0; @@ -217,9 +189,9 @@ static void __init js_tm_init_corr(int num_axes, int mode, int **axes, struct js_corr **corr) { - int j; + int j = 0; - for (j = 0; j < num_axes; j++) { + for (; j < num_axes; j++) { corr[0][j].type = JS_CORR_BROKEN; corr[0][j].prec = 0; corr[0][j].coef[0] = 127 - 2; @@ -230,8 +202,7 @@ switch (mode) { case JS_TM_MODE_M3DI: j = 4; break; - case JS_TM_MODE_3DRP: j = 2; break; - default: j = 0; break; + default: break; } for (; j < num_axes; j++) { @@ -252,48 +223,46 @@ static struct js_port __init *js_tm_probe(int io, struct js_port *port) { struct js_tm_info info; - char *names[JS_TM_MODE_MAX] = { NULL, "ThrustMaster Millenium 3D Inceptor", NULL, - "ThrustMaster Rage 3D Gamepad", "ThrustMaster WCS III" }; - char axes[JS_TM_MODE_MAX] = { 0, 6, 0, 2, 0 }; - char buttons[JS_TM_MODE_MAX] = { 0, 5, 0, 10, 0 }; - + struct js_rm_models { + unsigned char id; + char *name; + char axes; + char buttons; + } models[] = { { 1, "ThrustMaster Millenium 3D Inceptor", 6, 6 }, + { 3, "ThrustMaster Rage 3D Gamepad", 2, 10 }, + { 163, "Thrustmaster Fusion GamePad", 2, 10 }, + { 0, NULL, 0, 0 }}; + char name[64]; unsigned char data[JS_TM_MAX_LENGTH]; - unsigned char u; + unsigned char a, b; + int i; if (check_region(io, 1)) return port; - if (((u = inb(io)) & 3) == 3) return port; - outb(0xff,io); - if (!((inb(io) ^ u) & ~u & 0xf)) return port; - - if(js_tm_read_packet(io, data)) { - printk(KERN_WARNING "joy-thrustmaster: probe - can't read packet\n"); - return port; - } + if (js_tm_read_packet(io, data)) return port; info.io = io; info.mode = data[JS_TM_BYTE_ID]; if (!info.mode) return port; - if (info.mode >= JS_TM_MODE_MAX || !names[info.mode]) { - printk(KERN_WARNING "joy-thrustmaster: unknown device detected " - "(io=%#x, id=%d), contact \n", - io, info.mode); - return port; - } + for (i = 0; models[i].id && models[i].id != info.mode; i++); - if (data[JS_TM_BYTE_DEF] != js_tm_id_to_def[info.mode]) { - printk(KERN_WARNING "joy-thrustmaster: wrong DEF (%d) for ID %d - should be %d\n", - data[JS_TM_BYTE_DEF], info.mode, js_tm_id_to_def[info.mode]); + if (models[i].id != info.mode) { + a = data[JS_TM_BYTE_DEF] >> 4; + b = (data[JS_TM_BYTE_DEF] & 0xf) << 3; + sprintf(name, "Unknown %d-axis, %d-button TM device %d", a, b, info.mode); + } else { + sprintf(name, models[i].name); + a = models[i].axes; + b = models[i].buttons; } request_region(io, 1, "joystick (thrustmaster)"); port = js_register_port(port, &info, 1, sizeof(struct js_tm_info), js_tm_read); printk(KERN_INFO "js%d: %s revision %d at %#x\n", - js_register_device(port, 0, axes[info.mode], buttons[info.mode], - names[info.mode], js_tm_open, js_tm_close), names[info.mode], data[JS_TM_BYTE_REV], io); - js_tm_init_corr(axes[info.mode], info.mode, port->axes, port->corr); + js_register_device(port, 0, a, b, name, js_tm_open, js_tm_close), name, data[JS_TM_BYTE_REV], io); + js_tm_init_corr(a, info.mode, port->axes, port->corr); return port; } @@ -321,7 +290,7 @@ { struct js_tm_info *info; - while (js_tm_port != NULL) { + while (js_tm_port) { js_unregister_device(js_tm_port->devs[0]); info = js_tm_port->info; release_region(info->io, 1); diff -u --recursive --new-file v2.2.13/linux/drivers/char/joystick/joy-turbografx.c linux/drivers/char/joystick/joy-turbografx.c --- v2.2.13/linux/drivers/char/joystick/joy-turbografx.c Tue Dec 1 19:05:05 1998 +++ linux/drivers/char/joystick/joy-turbografx.c Tue Jan 4 10:12:14 2000 @@ -1,7 +1,9 @@ /* * joy-turbografx.c Version 1.2 * - * Copyright (c) 1998 Vojtech Pavlik + * Copyright (c) 1998-1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* @@ -26,7 +28,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: + * e-mail - mail your message to , or by paper mail: * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic */ @@ -39,9 +41,10 @@ #include #include #include +#include -MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_AUTHOR("Vojtech Pavlik "); MODULE_PARM(js_tg, "2-8i"); MODULE_PARM(js_tg_2, "2-8i"); MODULE_PARM(js_tg_3, "2-8i"); @@ -57,18 +60,14 @@ #define JS_TG_BUTTON4 0x01 #define JS_TG_BUTTON5 0x08 -static struct js_port* js_tg_port = NULL; +static struct js_port* js_tg_port __initdata = NULL; static int js_tg[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; static int js_tg_2[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; static int js_tg_3[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; struct js_tg_info { -#ifdef USE_PARPORT struct pardevice *port; /* parport device */ -#else - int port; /* hw port */ -#endif int sticks; /* joysticks connected */ }; @@ -109,9 +108,7 @@ struct js_tg_info *info = dev->port->info; if (!MOD_IN_USE) { -#ifdef USE_PARPORT if (parport_claim(info->port)) return -EBUSY; -#endif JS_PAR_CTRL_OUT(0x04, info->port); } MOD_INC_USE_COUNT; @@ -129,9 +126,7 @@ MOD_DEC_USE_COUNT; if (!MOD_IN_USE) { JS_PAR_CTRL_OUT(0x00, info->port); -#ifdef USE_PARPORT parport_release(info->port); -#endif } return 0; } @@ -142,16 +137,12 @@ struct js_tg_info *info; int i; - while (js_tg_port != NULL) { + while (js_tg_port) { for (i = 0; i < js_tg_port->ndevs; i++) - if (js_tg_port->devs[i] != NULL) + if (js_tg_port->devs[i]) js_unregister_device(js_tg_port->devs[i]); info = js_tg_port->info; -#ifdef USE_PARPORT parport_unregister_device(info->port); -#else - release_region(info->port, 3); -#endif js_tg_port = js_unregister_port(js_tg_port); } } @@ -186,33 +177,25 @@ { struct js_tg_info iniinfo; struct js_tg_info *info = &iniinfo; + struct parport *pp; int i; if (config[0] < 0) return port; -#ifdef USE_PARPORT - { - struct parport *pp; - - if (config[0] > 0x10) - for (pp=parport_enumerate(); pp != NULL && (pp->base!=config[0]); pp=pp->next); - else - for (pp=parport_enumerate(); pp != NULL && (config[0]>0); pp=pp->next) config[0]--; - - if (pp == NULL) { - printk(KERN_ERR "joy-tg: no such parport\n"); - return port; - } - info->port = parport_register_device(pp, "joystick (turbografx)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); - if (!info->port) - return port; + if (config[0] > 0x10) + for (pp=parport_enumerate(); pp && (pp->base!=config[0]); pp=pp->next); + else + for (pp=parport_enumerate(); pp && (config[0]>0); pp=pp->next) config[0]--; + + if (!pp) { + printk(KERN_ERR "joy-tg: no such parport\n"); + return port; } -#else - info->port = config[0]; - if (check_region(info->port, 3)) return port; - request_region(info->port, 3, "joystick (turbografx)"); -#endif + + info->port = parport_register_device(pp, "joystick (turbografx)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); + if (!info->port) + return port; port = js_register_port(port, info, 7, sizeof(struct js_tg_info), js_tg_read); info = port->info; @@ -221,24 +204,14 @@ for (i = 0; i < 7; i++) if (config[i+1] > 0 && config[i+1] < 6) { -#ifdef USE_PARPORT printk(KERN_INFO "js%d: Multisystem joystick on %s\n", js_register_device(port, i, 2, config[i+1], "Multisystem joystick", js_tg_open, js_tg_close), info->port->port->name); -#else - printk(KERN_INFO "js%d: Multisystem joystick at %#x\n", - js_register_device(port, i, 2, config[i+1], "Multisystem joystick", js_tg_open, js_tg_close), - info->port); -#endif info->sticks |= (1 << i); } if (!info->sticks) { -#ifdef USE_PARPORT parport_unregister_device(info->port); -#else - release_region(info->port, 3); -#endif return port; } @@ -248,18 +221,30 @@ } #ifndef MODULE -void __init js_tg_setup(char *str, int *ints) +int __init js_tg_setup(SETUP_PARAM) { int i; - - if (!strcmp(str,"js_tg")) - for (i = 0; i <= ints[0] && i < 2; i++) js_tg[i] = ints[i+1]; - if (!strcmp(str,"js_tg_2")) - for (i = 0; i <= ints[0] && i < 2; i++) js_tg_2[i] = ints[i+1]; - if (!strcmp(str,"js_tg_3")) - for (i = 0; i <= ints[0] && i < 2; i++) js_tg_3[i] = ints[i+1]; - + SETUP_PARSE(2); + for (i = 0; i <= ints[0] && i < 2; i++) js_tg[i] = ints[i+1]; + return 1; } +int __init js_tg_setup_2(SETUP_PARAM) +{ + int i; + SETUP_PARSE(2); + for (i = 0; i <= ints[0] && i < 2; i++) js_tg_2[i] = ints[i+1]; + return 1; +} +int __init js_tg_setup_3(SETUP_PARAM) +{ + int i; + SETUP_PARSE(2); + for (i = 0; i <= ints[0] && i < 2; i++) js_tg_3[i] = ints[i+1]; + return 1; +} +__setup("js_tg=", js_tg_setup); +__setup("js_tg_2=", js_tg_setup_2); +__setup("js_tg_3=", js_tg_setup_3); #endif #ifdef MODULE diff -u --recursive --new-file v2.2.13/linux/drivers/char/joystick/joy-warrior.c linux/drivers/char/joystick/joy-warrior.c --- v2.2.13/linux/drivers/char/joystick/joy-warrior.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/joystick/joy-warrior.c Tue Jan 4 10:12:14 2000 @@ -0,0 +1,318 @@ +/* + * joy-warrior.c Version 0.1 + * + * Copyright (c) 1998 David Thompson + * Copyright (c) 1999 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * This is a module for the Linux joystick driver, supporting + * the Logitech WingMan Warrior joystick. + */ + +/* + * This program is free warftware; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Constants. + */ + +#define N_JOYSTICK_WAR 13 +#define JS_WAR_MAX_LENGTH 16 + +/* + * List of Warriors. + */ + +static struct js_port* js_war_port = NULL; + +static char js_war_lengths[] = { 0, 4, 12, 3, 4, 4, 0, 0 }; + +/* + * Per-Warrior data. + */ + +struct js_war_info { + struct tty_struct* tty; + struct js_port* port; + int idx; + int len; + unsigned char data[JS_WAR_MAX_LENGTH]; + char used; +}; + +/* + * js_war_process_packet() decodes packets the driver receives from the + * Warrior. It updates the data accordingly. + */ + +static void js_war_process_packet(struct js_war_info* info) +{ + int **axes = info->port->axes; + int **buttons = info->port->buttons; + unsigned char *data = info->data; + int i; + + if (!info->idx) return; + + switch ((data[0] >> 4) & 7) { + + case 1: /* Button data */ + if (!info->port->devs[0]) return; + buttons[0][0] = ((data[3] & 0xa) >> 1) | ((data[3] & 0x5) << 1); + return; + case 2: /* Static status (Send !S to get one) */ +#if 0 + printk("joy-warrior: Static status:"); + for (i = 0; i < 12; i++) + printk(" %02x", info->data[i]); + printk("\n"); +#endif + return; + case 3: /* XY-axis info->data */ + if (!info->port->devs[0]) return; + axes[0][0] = ((data[0] & 8) << 5) - (data[2] | ((data[0] & 4) << 5)); + axes[0][1] = (data[1] | ((data[0] & 1) << 7)) - ((data[0] & 2) << 7); + return; + break; + case 4: /* Dynamic status */ +#if 0 + printk("joy-warrior: Dynamic status:"); + for (i = 0; i < 4; i++) + printk(" %02x", info->data[i]); + printk("\n"); +#endif + return; + case 5: /* Throttle, spinner, hat info->data */ + if (!info->port->devs[0]) return; + axes[0][2] = (data[1] | ((data[0] & 1) << 7)) - ((data[0] & 2) << 7); + axes[0][3] = (data[3] & 2 ? 1 : 0) - (info->data[3] & 1 ? 1 : 0); + axes[0][4] = (data[3] & 8 ? 1 : 0) - (info->data[3] & 4 ? 1 : 0); + axes[0][5] = (data[2] | ((data[0] & 4) << 5)) - ((data[0] & 8) << 5); + return; + default: + printk("joy-warrior: Unknown packet %d length %d:", (data[0] >> 4) & 7, info->idx); + for (i = 0; i < info->idx; i++) + printk(" %02x", data[i]); + printk("\n"); + return; + } +} + +/* + * js_war_open() is a callback from the joystick device open routine. + */ + +static int js_war_open(struct js_dev *jd) +{ + struct js_war_info *info = jd->port->info; + info->used++; + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_war_close() is a callback from the joystick device release routine. + */ + +static int js_war_close(struct js_dev *jd) +{ + struct js_war_info *info = jd->port->info; + if (!--info->used) { + js_unregister_device(jd->port->devs[0]); + js_war_port = js_unregister_port(jd->port); + } + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_war_init_corr() initializes the correction values for the Warrior. + */ + +static void __init js_war_init_corr(struct js_corr **corr) +{ + int i; + + for (i = 0; i < 6; i++) { + corr[0][i].type = JS_CORR_BROKEN; + corr[0][i].prec = 0; + corr[0][i].coef[0] = -8; + corr[0][i].coef[1] = 8; + corr[0][i].coef[2] = (1 << 29) / (128 - 64); + corr[0][i].coef[3] = (1 << 29) / (128 - 64); + } + + corr[0][2].coef[2] = (1 << 29) / (128 - 16); + corr[0][2].coef[3] = (1 << 29) / (128 - 16); + + for (i = 3; i < 5; i++) { + corr[0][i].coef[0] = 0; + corr[0][i].coef[1] = 0; + corr[0][i].coef[2] = (1 << 29); + corr[0][i].coef[3] = (1 << 29); + } + + corr[0][5].prec = -1; + corr[0][5].coef[0] = 0; + corr[0][5].coef[1] = 0; + corr[0][5].coef[2] = (1 << 29) / 128; + corr[0][5].coef[3] = (1 << 29) / 128; +} + +/* + * js_war_ldisc_open() is the routine that is called upon setting our line + * discipline on a tty. + */ + +static int js_war_ldisc_open(struct tty_struct *tty) +{ + struct js_war_info iniinfo; + struct js_war_info *info = &iniinfo; + + info->tty = tty; + info->idx = 0; + info->len = 0; + info->used = 1; + + js_war_port = js_register_port(js_war_port, info, 1, sizeof(struct js_war_info), NULL); + + info = js_war_port->info; + info->port = js_war_port; + tty->disc_data = info; + + printk(KERN_INFO "js%d: WingMan Warrior on %s%d\n", + js_register_device(js_war_port, 0, 6, 4, "WingMan Warrior", js_war_open, js_war_close), + tty->driver.name, MINOR(tty->device) - tty->driver.minor_start); + + js_war_init_corr(js_war_port->corr); + + MOD_INC_USE_COUNT; + + return 0; +} + +/* + * js_war_ldisc_close() is the opposite of js_war_ldisc_open() + */ + +static void js_war_ldisc_close(struct tty_struct *tty) +{ + struct js_war_info* info = (struct js_war_info*) tty->disc_data; + if (!--info->used) { + js_unregister_device(info->port->devs[0]); + js_war_port = js_unregister_port(info->port); + } + MOD_DEC_USE_COUNT; +} + +/* + * js_war_ldisc_receive() is called by the low level driver when characters + * are ready for us. We then buffer them for further processing, or call the + * packet processing routine. + */ + +static void js_war_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) +{ + struct js_war_info* info = (struct js_war_info*) tty->disc_data; + int i; + + for (i = 0; i < count; i++) { + if (cp[i] & 0x80) { + if (info->idx) + js_war_process_packet(info); + info->idx = 0; + info->len = js_war_lengths[(cp[i] >> 4) & 7]; + } + + if (info->idx < JS_WAR_MAX_LENGTH) + info->data[info->idx++] = cp[i]; + + if (info->idx == info->len) { + if (info->idx) + js_war_process_packet(info); + info->idx = 0; + info->len = 0; + } + } +} + +/* + * js_war_ldisc_room() reports how much room we do have for receiving data. + * Although we in fact have infinite room, we need to specify some value + * here, so why not the size of our packet buffer. It's big anyway. + */ + +static int js_war_ldisc_room(struct tty_struct *tty) +{ + return JS_WAR_MAX_LENGTH; +} + +/* + * The line discipline structure. + */ + +static struct tty_ldisc js_war_ldisc = { + magic: TTY_LDISC_MAGIC, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) + name: "warrior", +#endif + open: js_war_ldisc_open, + close: js_war_ldisc_close, + receive_buf: js_war_ldisc_receive, + receive_room: js_war_ldisc_room, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +#ifdef MODULE +int init_module(void) +#else +int __init js_war_init(void) +#endif +{ + if (tty_register_ldisc(N_JOYSTICK_WAR, &js_war_ldisc)) { + printk(KERN_ERR "joy-warrior: Error registering line discipline.\n"); + return -ENODEV; + } + + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + tty_register_ldisc(N_JOYSTICK_WAR, NULL); +} +#endif diff -u --recursive --new-file v2.2.13/linux/drivers/char/joystick/joystick.c linux/drivers/char/joystick/joystick.c --- v2.2.13/linux/drivers/char/joystick/joystick.c Tue Dec 1 19:05:05 1998 +++ linux/drivers/char/joystick/joystick.c Tue Jan 4 10:12:14 2000 @@ -1,7 +1,9 @@ /* - * joystick.c Version 1.2 + * joystick.c Version 1.2 * - * Copyright (c) 1996-1998 Vojtech Pavlik + * Copyright (c) 1996-1999 Vojtech Pavlik + * + * Sponsored by SuSE */ /* @@ -26,14 +28,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: + * e-mail - mail your message to , or by paper mail: * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic */ #include #include #include -#include #include #include #include @@ -42,10 +43,9 @@ #include #include #include -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) -#include #include -#endif +#include +#include /* * Configurable parameters. @@ -54,6 +54,15 @@ #define JS_REFRESH_TIME HZ/50 /* Time between two reads of joysticks (20ms) */ /* + * Exported symbols. + */ + +EXPORT_SYMBOL(js_register_port); +EXPORT_SYMBOL(js_unregister_port); +EXPORT_SYMBOL(js_register_device); +EXPORT_SYMBOL(js_unregister_device); + +/* * Buffer macros. */ @@ -74,263 +83,13 @@ static int js_use_count = 0; /* - * Exported variables. - */ - -unsigned int js_time_speed = 0; -js_time_func js_get_time; -js_delta_func js_delta; - -unsigned int js_time_speed_a = 0; -js_time_func js_get_time_a; -js_delta_func js_delta_a; - -/* * Module info. */ -MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_AUTHOR("Vojtech Pavlik "); MODULE_SUPPORTED_DEVICE("js"); /* - * js_get_time_*() are different functions to get current time. - * js_delta_*() are functions to compute time difference. - */ - -#ifdef __i386__ - -static unsigned int js_get_time_rdtsc(void) -{ - unsigned int x; - __asm__ __volatile__ ( "rdtsc" : "=A" (x) ); - return x; -} - -static unsigned int js_get_time_pit(void) -{ - unsigned long flags; - unsigned int x; - - __save_flags(flags); - __cli(); - outb(0, 0x43); - x = inb(0x40); - x |= inb(0x40) << 8; - __restore_flags(flags); - - return x; -} - -static int js_delta_pit(unsigned int x, unsigned int y) -{ - return y - x + ( y < x ? 1193180L / HZ : 0 ); -} - -static unsigned int js_get_time_counter(void) -{ - static int time_counter = 0; - return time_counter++; -} - -#else -#ifdef __alpha__ - -static unsigned int js_get_time_rpcc(void) -{ - unsigned int x; - __asm__ __volatile__ ( "rpcc %0" : "=r" (x) ); - return x; -} - -#else - -#ifndef MODULE -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) -static unsigned int js_get_time_system(void) -{ - static struct timeval js_tv; - get_fast_time(&js_tv); - return js_tv.tv_sec * 1000000L + js_tv.tv_usec; -} -#endif -#endif - -#endif -#endif - -static int js_delta_normal(unsigned int x, unsigned int y) -{ - return x - y; -} - -/* - * js_calibrate_time() calibrates a given timer. - */ - -static int __init js_calibrate_time(js_time_func get_time, js_delta_func delta) -{ - unsigned int t1, t2, t3; - unsigned long flags; - - __save_flags(flags); - __cli(); - t1 = get_time(); - udelay(1000); - t2 = get_time(); - t3 = get_time(); - __restore_flags(flags); - - return delta(t2, t1) - delta(t3, t2); -} - -/* - * js_calibrate_time_counter() calibrates the counter timer, which can't - * be calibrated using the above function. - */ - -#ifdef __i386__ - -static int __init js_calibrate_time_counter(void) -{ - unsigned int i, j, t1, t2, t3; - - j = jiffies; do { inb(0x201); t1 = js_get_time_counter(); } while (j == jiffies); - j = jiffies; do { inb(0x201); t2 = js_get_time_counter(); } while (j == jiffies); - - j = (t2 - t1) * HZ / 1000; - - t1 = js_get_time_pit(); - for (i = 0; i < 1000; i++) { - inb(0x201); - js_get_time_counter(); - } - t2 = js_get_time_pit(); - t3 = js_get_time_pit(); - - i = 1193180L / (js_delta_pit(t2, t1) - js_delta_pit(t3, t2)); - - if (DIFF(i,j) > 5) - printk(KERN_WARNING "js: Counter timer calibration unsure," - " pass1 (0.%d MHz) and pass2 (0.%d MHz) differ.\n", j, i); - - return (i + j) >> 1; -} - -#endif - -/* - * js_setup_time chooses the best available timers - * on the system and calibrates them. - */ - -static int __init js_setup_time(void) -{ - int t; - char *name, *name_a; - - name = ""; - name_a = ""; - js_time_speed = 0; - js_time_speed_a = 0; - -#ifdef __i386__ - - t = js_calibrate_time(js_get_time_pit, js_delta_pit); - - if (DIFF(t, 1193) > 5) - printk(KERN_WARNING "js: Measured PIT speed is %d.%03d MHz, but should be 1.193 MHz.\n" - KERN_WARNING "js: This is probably caused by wrong BogoMIPS value. It is: %ld, should be: %ld.\n", - t / 1000, t % 1000, loops_per_sec / 500000, loops_per_sec / (t * 500000 / 1193)); - - if (JS_HAS_RDTSC && (t = js_calibrate_time(js_get_time_rdtsc, js_delta_normal)) > 0) { - - js_time_speed_a = t; - js_get_time_a = js_get_time_rdtsc; - js_delta_a = js_delta_normal; - js_time_speed = t; - js_get_time = js_get_time_rdtsc; - js_delta = js_delta_normal; - name = "RDTSC"; - - } else { - - js_time_speed_a = t; - js_get_time_a = js_get_time_pit; - js_delta_a = js_delta_pit; - name_a = "PIT"; - - t = js_calibrate_time_counter(); - - js_time_speed = t; - js_get_time = js_get_time_counter; - js_delta = js_delta_normal; - name = "counter"; - - } - -#else -#ifdef __alpha__ - - t = js_calibrate_time(js_get_time_rpcc, js_delta_normal); - - js_time_speed_a = t; - js_get_time_a = js_get_time_rpcc; - js_delta_a = js_delta_normal; - js_time_speed = t; - js_get_time = js_get_time_rpcc; - js_delta = js_delta_normal; - name = "RPCC"; - -#else - -#ifndef MODULE -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) - t = js_calibrate_time(js_get_time_system, js_delta_normal); - - js_time_speed_a = t; - js_get_time_a = js_get_time_system; - js_delta_a = js_delta_normal; - js_time_speed = t; - js_get_time = js_get_time_system; - js_delta = js_delta_normal; - name = "system"; -#endif -#endif - -#endif -#endif - - printk(KERN_INFO "js: Version %d.%d.%d ", - JS_VERSION >> 16 & 0xff, JS_VERSION >> 8 & 0xff, JS_VERSION & 0xff); - - if (js_time_speed_a <= 0 || js_time_speed <= 0) { - printk("\n"); - return -1; - } - - printk("using "); - - if (js_time_speed > 10000) { - t = js_time_speed / 1000 + (js_time_speed % 1000 >= 500); - printk("%d MHz ", t); - } else { - t = js_time_speed / 10 + (js_time_speed % 10 >= 5); - printk("%d.%02d MHz ", t / 100, t % 100); - } - - if (js_get_time_a != js_get_time) { - t = js_time_speed_a / 10 + (js_time_speed_a % 10 >= 5); - printk("%s timer and %d.%02d MHz %s timer.\n", - name, t / 100, t % 100, name_a); - } else { - printk("%s timer.\n", name); - } - - return 0; -} - - -/* * js_correct() performs correction of raw joystick data. */ @@ -364,7 +123,6 @@ return (buttons[i >> 5] >> (i & 0x1f)) & 1; } - /* * js_add_event() adds an event to the buffer. This requires additional * queue post-processing done by js_sync_buff. @@ -457,14 +215,17 @@ struct js_dev *curd = js_dev; unsigned long flags; - while (curp != NULL) { - curp->read(curp->info, curp->axes, curp->buttons); + while (curp) { + if (curp->read) + if (curp->read(curp->info, curp->axes, curp->buttons)) + curp->fail++; + curp->total++; curp = curp->next; } spin_lock_irqsave(&js_lock, flags); - while (curd != NULL) { + while (curd) { if (data) { js_process_data(curd); js_sync_buff(curd); @@ -485,13 +246,9 @@ * space. */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos) -#else -static int js_read(struct inode *inode, struct file *file, char *buf, int count) -#endif { - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); struct js_event *buff = (void *) buf; struct js_list *curl; struct js_dev *jd; @@ -525,8 +282,8 @@ if (GOF(curl->tail) == jd->bhead && curl->startup == jd->num_axes + jd->num_buttons) { + __set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&jd->wait, &wait); - current->state = TASK_INTERRUPTIBLE; while (GOF(curl->tail) == jd->bhead) { @@ -575,13 +332,8 @@ tmpevent.time = jiffies * (1000/HZ); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) if (copy_to_user(&buff[written], &tmpevent, sizeof(struct js_event))) retval = -EFAULT; -#else - if (!(retval = verify_area(VERIFY_WRITE, &buff[written], sizeof(struct js_event)))) - memcpy_tofs(&buff[written], &tmpevent, sizeof(struct js_event)); -#endif curl->startup++; written++; @@ -593,17 +345,11 @@ while ((jd->bhead != (new_tail = GOF(curl->tail))) && (written < blocks) && !retval) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) if (copy_to_user(&buff[written], &jd->buff[new_tail], sizeof(struct js_event))) retval = -EFAULT; if (put_user((__u32)(jd->buff[new_tail].time * (1000/HZ)), &buff[written].time)) retval = -EFAULT; -#else - if (!(retval = verify_area(VERIFY_WRITE, &buff[written], sizeof(struct js_event)))) { - memcpy_tofs(&buff[written], &jd->buff[new_tail], sizeof(struct js_event)); - put_user((__u32)(jd->buff[new_tail].time * (1000/HZ)), &buff[written].time); - } -#endif + curl->tail = new_tail; written++; } @@ -624,15 +370,9 @@ data.y = jd->num_axes < 2 ? 0 : ((js_correct(jd->new.axes[1], &jd->corr[1]) / 256) + 128) >> js_comp_glue.JS_CORR.y; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) retval = copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; -#else - if (!(retval = verify_area(VERIFY_WRITE, buf, sizeof(struct JS_DATA_TYPE)))) { - memcpy_tofs(buf, &data, sizeof(struct JS_DATA_TYPE)); - } -#endif - curl->startup = 0; + curl->startup = jd->num_axes + jd->num_buttons; curl->tail = GOB(jd->bhead); if (!retval) retval = sizeof(struct JS_DATA_TYPE); } @@ -644,12 +384,12 @@ if (orig_tail == jd->tail) { new_tail = curl->tail; curl = jd->list; - while (curl != NULL && curl->tail != jd->tail) { + while (curl && curl->tail != jd->tail) { if (ROT(jd->bhead, new_tail, curl->tail) || (jd->bhead == curl->tail)) new_tail = curl->tail; curl = curl->next; } - if (curl == NULL) jd->tail = new_tail; + if (!curl) jd->tail = new_tail; } spin_unlock_irqrestore(&js_lock, flags); @@ -661,8 +401,6 @@ * js_poll() does select() support. */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) - static unsigned int js_poll(struct file *file, poll_table *wait) { struct js_list *curl = file->private_data; @@ -676,20 +414,6 @@ return retval; } -#else - -static int js_select(struct inode *inode, struct file *file, int sel_type, select_table *wait) -{ - struct js_list *curl = file->private_data; - if (sel_type == SEL_IN) { - if (GOF(curl->tail) != curl->dev->bhead) return 1; - select_wait(&curl->dev->wait, wait); - } - return 0; -} - -#endif - /* * js_ioctl handles misc ioctl calls. */ @@ -703,8 +427,6 @@ curl = file->private_data; jd = curl->dev; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) - switch (cmd) { /* @@ -757,95 +479,6 @@ } } -#else - - switch (cmd) { - -/* - * 0.x compatibility - */ - - case JS_SET_CAL: - if (verify_area(VERIFY_READ, (struct JS_DATA_TYPE *) arg, - sizeof(struct JS_DATA_TYPE))) return -EFAULT; - memcpy_fromfs(&js_comp_glue.JS_CORR, (struct JS_DATA_SAVE_TYPE *) arg, - sizeof(struct JS_DATA_TYPE)); - return 0; - case JS_GET_CAL: - if (verify_area(VERIFY_WRITE, (struct JS_DATA_TYPE *) arg, - sizeof(struct JS_DATA_TYPE))) return -EFAULT; - memcpy_tofs((struct JS_DATA_SAVE_TYPE *) arg, &js_comp_glue.JS_CORR, - sizeof(struct JS_DATA_TYPE)); - return 0; - case JS_SET_TIMEOUT: - if (verify_area(VERIFY_READ, (int *) arg, sizeof(int))) return -EFAULT; - js_comp_glue.JS_TIMEOUT = get_user((int *) arg); - return 0; - case JS_GET_TIMEOUT: - if (verify_area(VERIFY_WRITE, (int *) arg, sizeof(int))) return -EFAULT; - put_user(js_comp_glue.JS_TIMEOUT, (int *) arg); - return 0; - case JS_SET_TIMELIMIT: - if (verify_area(VERIFY_READ, (long *) arg, sizeof(long))) return -EFAULT; - js_comp_glue.JS_TIMELIMIT = get_user((long *) arg); - return 0; - case JS_GET_TIMELIMIT: - if (verify_area(VERIFY_WRITE, (long *) arg, sizeof(long))) return -EFAULT; - put_user(js_comp_glue.JS_TIMELIMIT, (long *) arg); - return 0; - case JS_SET_ALL: - if (verify_area(VERIFY_READ, (struct JS_DATA_SAVE_TYPE *) arg, - sizeof(struct JS_DATA_SAVE_TYPE))) return -EFAULT; - memcpy_fromfs(&js_comp_glue, (struct JS_DATA_SAVE_TYPE *) arg, - sizeof(struct JS_DATA_SAVE_TYPE)); - return 0; - case JS_GET_ALL: - if (verify_area(VERIFY_WRITE, (struct JS_DATA_SAVE_TYPE *) arg, - sizeof(struct JS_DATA_SAVE_TYPE))) return -EFAULT; - memcpy_tofs((struct JS_DATA_SAVE_TYPE *) arg, &js_comp_glue, - sizeof(struct JS_DATA_SAVE_TYPE)); - return 0; - -/* - * 1.x ioctl calls - */ - - case JSIOCGVERSION: - if (verify_area(VERIFY_WRITE, (__u32 *) arg, sizeof(__u32))) return -EFAULT; - put_user(JS_VERSION, (__u32 *) arg); - return 0; - case JSIOCGAXES: - if (verify_area(VERIFY_WRITE, (__u8 *) arg, sizeof(__u8))) return -EFAULT; - put_user(jd->num_axes, (__u8 *) arg); - return 0; - case JSIOCGBUTTONS: - if (verify_area(VERIFY_WRITE, (__u8 *) arg, sizeof(__u8))) return -EFAULT; - put_user(jd->num_buttons, (__u8 *) arg); - return 0; - case JSIOCSCORR: - if (verify_area(VERIFY_READ, (struct js_corr *) arg, - sizeof(struct js_corr) * jd->num_axes)) return -EFAULT; - memcpy_fromfs(jd->corr, (struct js_corr *) arg, - sizeof(struct js_corr) * jd->num_axes); - return 0; - case JSIOCGCORR: - if (verify_area(VERIFY_WRITE, (struct js_corr *) arg, - sizeof(struct js_corr) * jd->num_axes)) return -EFAULT; - memcpy_tofs((struct js_corr *) arg, - jd->corr, sizeof(struct js_corr) * jd->num_axes); - return 0; - default: - if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) { - len = strlen(jd->name) + 1; - if (verify_area(VERIFY_WRITE, (char *) arg, len)) return -EFAULT; - if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - memcpy_tofs((char *) arg, jd->name, len); - return len; - } - } - -#endif - return -EINVAL; } @@ -867,21 +500,20 @@ spin_lock_irqsave(&js_lock, flags); - while (i > 0 && jd != NULL) { + while (i > 0 && jd) { jd = jd->next; i--; } spin_unlock_irqrestore(&js_lock, flags); - if (jd == NULL) return -ENODEV; + if (!jd) return -ENODEV; if ((result = jd->open(jd))) return result; - MOD_INC_USE_COUNT; - if (!js_use_count++) js_do_timer(0); + if ((new = kmalloc(sizeof(struct js_list), GFP_KERNEL))) { - if ((new = kmalloc(sizeof(struct js_list), GFP_KERNEL)) != NULL) { + MOD_INC_USE_COUNT; spin_lock_irqsave(&js_lock, flags); @@ -896,6 +528,8 @@ spin_unlock_irqrestore(&js_lock, flags); + if (!js_use_count++) js_do_timer(0); + } else { result = -ENOMEM; } @@ -908,11 +542,7 @@ * used by it. */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) static int js_release(struct inode *inode, struct file *file) -#else -static void js_release(struct inode *inode, struct file *file) -#endif { struct js_list *curl = file->private_data; struct js_dev *jd = curl->dev; @@ -925,11 +555,11 @@ while (*curp && (*curp != curl)) curp = &((*curp)->next); *curp = (*curp)->next; - if (jd->list != NULL) + if (jd->list) if (curl->tail == jd->tail) { curl = jd->list; new_tail = curl->tail; - while (curl != NULL && curl->tail != jd->tail) { + while (curl && curl->tail != jd->tail) { if (ROT(jd->bhead, new_tail, curl->tail) || (jd->bhead == curl->tail)) new_tail = curl->tail; curl = curl->next; @@ -946,9 +576,7 @@ jd->close(jd); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) return 0; -#endif } /* @@ -967,7 +595,7 @@ printk(",--- Dumping Devices:\n"); printk("| js_dev = %x\n", (int) js_dev); - while (curd != NULL) { + while (curd) { printk("| %s-device %x, next %x axes %d, buttons %d, port %x - %#x\n", curd->next ? "|":"`", (int) curd, (int) curd->next, curd->num_axes, curd->num_buttons, (int) curd->port, curd->port->io); @@ -977,7 +605,7 @@ printk(">--- Dumping ports:\n"); printk("| js_port = %x\n", (int) js_port); - while (curp != NULL) { + while (curp) { printk("| %s-port %x, next %x, io %#x, devices %d\n", curp->next ? "|":"`", (int) curp, (int) curp->next, curp->io, curp->ndevs); @@ -1009,7 +637,7 @@ int i; unsigned long flags; - if ((all = kmalloc(sizeof(struct js_port) + 4 * devs * sizeof(void*) + infos, GFP_KERNEL)) == NULL) + if (!(all = kmalloc(sizeof(struct js_port) + 4 * devs * sizeof(void*) + infos, GFP_KERNEL))) return NULL; curp = all; @@ -1018,6 +646,8 @@ curp->prev = port; curp->read = read; curp->ndevs = devs; + curp->fail = 0; + curp->total = 0; curp->devs = all += sizeof(struct js_port); for (i = 0; i < devs; i++) curp->devs[i] = NULL; @@ -1035,7 +665,7 @@ spin_lock_irqsave(&js_lock, flags); - while (*ptrp != NULL) ptrp=&((*ptrp)->next); + while (*ptrp) ptrp=&((*ptrp)->next); *ptrp = curp; spin_unlock_irqrestore(&js_lock, flags); @@ -1051,7 +681,9 @@ spin_lock_irqsave(&js_lock, flags); - while (*curp != NULL && (*curp != port)) curp = &((*curp)->next); + printk("js: There were %d failures out of %d read attempts.\n", port->fail, port->total); + + while (*curp && (*curp != port)) curp = &((*curp)->next); *curp = (*curp)->next; spin_unlock_irqrestore(&js_lock, flags); @@ -1071,9 +703,9 @@ int i = 0; unsigned long flags; - if ((all = kmalloc(sizeof(struct js_dev) + 2 * axes * sizeof(int) + + if (!(all = kmalloc(sizeof(struct js_dev) + 2 * axes * sizeof(int) + 2 * (((buttons - 1) >> 5) + 1) * sizeof(int) + - axes * sizeof(struct js_corr) + strlen(name) + 1, GFP_KERNEL)) == NULL) + axes * sizeof(struct js_corr) + strlen(name) + 1, GFP_KERNEL))) return -1; curd = all; @@ -1081,10 +713,11 @@ curd->next = NULL; curd->list = NULL; curd->port = port; - curd->wait = NULL; curd->open = open; curd->close = close; + init_waitqueue_head(&curd->wait); + curd->ahead = 0; curd->bhead = 0; curd->tail = JS_BUFF_SIZE - 1; @@ -1107,7 +740,7 @@ spin_lock_irqsave(&js_lock, flags); - while (*ptrd != NULL) { ptrd=&(*ptrd)->next; i++; } + while (*ptrd) { ptrd=&(*ptrd)->next; i++; } *ptrd = curd; spin_unlock_irqrestore(&js_lock, flags); @@ -1122,7 +755,7 @@ spin_lock_irqsave(&js_lock, flags); - while (*curd != NULL && (*curd != dev)) curd = &((*curd)->next); + while (*curd && (*curd != dev)) curd = &((*curd)->next); *curd = (*curd)->next; spin_unlock_irqrestore(&js_lock, flags); @@ -1137,11 +770,7 @@ static struct file_operations js_fops = { read: js_read, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) poll: js_poll, -#else - select: js_select, -#endif ioctl: js_ioctl, open: js_open, release: js_release, @@ -1158,15 +787,15 @@ int __init js_init(void) #endif { - int result; - - js_setup_time(); if (register_chrdev(JOYSTICK_MAJOR, "js", &js_fops)) { printk(KERN_ERR "js: unable to get major %d for joystick\n", JOYSTICK_MAJOR); return -EBUSY; } + printk(KERN_INFO "js: Joystick driver v%d.%d.%d (c) 1999 Vojtech Pavlik \n", + JS_VERSION >> 16 & 0xff, JS_VERSION >> 8 & 0xff, JS_VERSION & 0xff); + spin_lock_init(&js_lock); init_timer(&js_timer); @@ -1177,44 +806,61 @@ js_comp_glue.JS_TIMEOUT = JS_DEF_TIMEOUT; js_comp_glue.JS_TIMELIMIT = JS_DEF_TIMELIMIT; -#ifdef MODULE - result = 0; -#else - result = -ENODEV; +#ifndef MODULE +#ifdef CONFIG_JOY_PCI + js_pci_init(); +#endif #ifdef CONFIG_JOY_LIGHTNING - if (!js_l4_init()) result = 0; + js_l4_init(); #endif #ifdef CONFIG_JOY_SIDEWINDER - if (!js_sw_init()) result = 0; + js_sw_init(); #endif -#ifdef CONFIG_JOY_ASSASIN - if (!js_as_init()) result = 0; +#ifdef CONFIG_JOY_ASSASSIN + js_as_init(); #endif #ifdef CONFIG_JOY_LOGITECH - if (!js_lt_init()) result = 0; + js_lt_init(); #endif #ifdef CONFIG_JOY_THRUSTMASTER - if (!js_tm_init()) result = 0; + js_tm_init(); #endif #ifdef CONFIG_JOY_GRAVIS - if (!js_gr_init()) result = 0; + js_gr_init(); +#endif +#ifdef CONFIG_JOY_CREATIVE + js_cr_init(); #endif #ifdef CONFIG_JOY_ANALOG - if (!js_an_init()) result = 0; + js_an_init(); #endif #ifdef CONFIG_JOY_CONSOLE - if (!js_console_init()) result = 0; + js_console_init(); #endif #ifdef CONFIG_JOY_DB9 - if (!js_db9_init()) result = 0; + js_db9_init(); +#endif +#ifdef CONFIG_JOY_TURBOGRAFX + js_tg_init(); #endif #ifdef CONFIG_JOY_AMIGA - if (!js_am_init()) result = 0; + js_am_init(); +#endif +#ifdef CONFIG_JOY_MAGELLAN + js_mag_init(); +#endif +#ifdef CONFIG_JOY_WARRIOR + js_war_init(); +#endif +#ifdef CONFIG_JOY_SPACEORB + js_orb_init(); +#endif +#ifdef CONFIG_JOY_SPACEBALL + js_sball_init(); #endif - if (result) printk(KERN_ERR "js: no joysticks found\n"); #endif - return result; + return 0; } /* @@ -1229,3 +875,4 @@ printk(KERN_ERR "js: can't unregister device\n"); } #endif + diff -u --recursive --new-file v2.2.13/linux/drivers/char/lp.c linux/drivers/char/lp.c --- v2.2.13/linux/drivers/char/lp.c Tue Jan 4 11:10:33 2000 +++ linux/drivers/char/lp.c Tue Jan 4 10:12:14 2000 @@ -424,7 +424,7 @@ { unsigned int last = lp_table[minor].last_error; unsigned char status = r_str(minor); - if (status & LP_PERRORP) + if ((status & LP_PERRORP) && !(LP_F(minor) & LP_CAREFUL)) /* No error. */ last = 0; else if ((status & LP_POUTPA)) { @@ -437,11 +437,14 @@ last = LP_PSELECD; printk(KERN_INFO "lp%d off-line\n", minor); } - } else { + } else if (!(status & LP_PERRORP)) { if (last != LP_PERRORP) { last = LP_PERRORP; printk(KERN_INFO "lp%d on fire\n", minor); } + } else { + last = 0; /* Come here if LP_CAREFUL is set and no + errors are reported. */ } lp_table[minor].last_error = last; @@ -787,14 +790,12 @@ else LP_F(minor) &= ~LP_ABORTOPEN; break; -#ifdef OBSOLETED case LPCAREFUL: if (arg) LP_F(minor) |= LP_CAREFUL; else LP_F(minor) &= ~LP_CAREFUL; break; -#endif case LPTRUSTIRQ: if (arg) LP_F(minor) |= LP_TRUST_IRQ; @@ -971,7 +972,8 @@ return -EIO; } } else { - printk(KERN_INFO "lp: driver loaded but no devices found\n"); + printk(KERN_INFO "lp: no devices found\n"); + return -ENODEV; } return 0; diff -u --recursive --new-file v2.2.13/linux/drivers/char/mem.c linux/drivers/char/mem.c --- v2.2.13/linux/drivers/char/mem.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/char/mem.c Tue Jan 4 10:12:14 2000 @@ -37,6 +37,9 @@ #ifdef CONFIG_ISDN int isdn_init(void); #endif +#ifdef CONFIG_PHONE +extern int telephony_init(void); +#endif #ifdef CONFIG_VIDEO_DEV extern int videodev_init(void); #endif @@ -681,6 +684,9 @@ #endif #ifdef CONFIG_VIDEO_DEV videodev_init(); +#endif +#ifdef CONFIG_PHONE + telephony_init(); #endif return 0; } diff -u --recursive --new-file v2.2.13/linux/drivers/char/mixcomwd.c linux/drivers/char/mixcomwd.c --- v2.2.13/linux/drivers/char/mixcomwd.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/mixcomwd.c Tue Jan 4 10:12:14 2000 @@ -0,0 +1,250 @@ +/* + * MixCom Watchdog: A Simple Hardware Watchdog Device + * Based on Softdog driver by Alan Cox and PC Watchdog driver by Ken Hollis + * + * Author: Gergely Madarasz + * + * Copyright (c) 1999 ITConsult-Pro Co. + * + * 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. + * + * Version 0.1 (99/04/15): + * - first version + * + * Version 0.2 (99/06/16): + * - added kernel timer watchdog ping after close + * since the hardware does not support watchdog shutdown + * + * Version 0.3 (99/06/21): + * - added WDIOC_GETSTATUS and WDIOC_GETSUPPORT ioctl calls + * + * Version 0.3.1 (99/06/22): + * - allow module removal while internal timer is active, + * print warning about probable reset + * + */ + +#define VERSION "0.3.1" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int mixcomwd_ioports[] = { 0x180, 0x280, 0x380, 0x000 }; + +#define MIXCOM_WATCHDOG_OFFSET 0xc10 +#define MIXCOM_ID1 0x11 +#define MIXCOM_ID2 0x13 + +static int mixcomwd_opened; +static int mixcomwd_port; + +#ifndef CONFIG_WATCHDOG_NOWAYOUT +static int mixcomwd_timer_alive; +static struct timer_list mixcomwd_timer; +#endif + +static void mixcomwd_ping(void) +{ + outb_p(55,mixcomwd_port+MIXCOM_WATCHDOG_OFFSET); + return; +} + +#ifndef CONFIG_WATCHDOG_NOWAYOUT +static void mixcomwd_timerfun(unsigned long d) +{ + mixcomwd_ping(); + + mod_timer(&mixcomwd_timer,jiffies+ 5*HZ); +} +#endif + +/* + * Allow only one person to hold it open + */ + +static int mixcomwd_open(struct inode *inode, struct file *file) +{ + if(test_and_set_bit(0,&mixcomwd_opened)) { + return -EBUSY; + } + mixcomwd_ping(); + +#ifndef CONFIG_WATCHDOG_NOWAYOUT + if(mixcomwd_timer_alive) { + del_timer(&mixcomwd_timer); + mixcomwd_timer_alive=0; + } +#endif + MOD_INC_USE_COUNT; + + return 0; +} + +static int mixcomwd_release(struct inode *inode, struct file *file) +{ + +#ifndef CONFIG_WATCHDOG_NOWAYOUT + if(mixcomwd_timer_alive) { + printk(KERN_ERR "mixcomwd: release called while internal timer alive"); + return -EBUSY; + } + init_timer(&mixcomwd_timer); + mixcomwd_timer.expires=jiffies + 5 * HZ; + mixcomwd_timer.function=mixcomwd_timerfun; + mixcomwd_timer.data=0; + mixcomwd_timer_alive=1; + add_timer(&mixcomwd_timer); +#endif + MOD_DEC_USE_COUNT; + + clear_bit(0,&mixcomwd_opened); + return 0; +} + + +static ssize_t mixcomwd_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + if (ppos != &file->f_pos) { + return -ESPIPE; + } + + if(len) + { + mixcomwd_ping(); + return 1; + } + return 0; +} + +static int mixcomwd_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int status; + static struct watchdog_info ident = { + WDIOF_KEEPALIVEPING, 1, "MixCOM watchdog" + }; + + switch(cmd) + { + case WDIOC_GETSTATUS: + status=mixcomwd_opened; +#ifndef CONFIG_WATCHDOG_NOWAYOUT + status|=mixcomwd_timer_alive; +#endif + if (copy_to_user((int *)arg, &status, sizeof(int))) { + return -EFAULT; + } + break; + case WDIOC_GETSUPPORT: + if (copy_to_user((struct watchdog_info *)arg, &ident, + sizeof(ident))) { + return -EFAULT; + } + break; + case WDIOC_KEEPALIVE: + mixcomwd_ping(); + break; + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static struct file_operations mixcomwd_fops= +{ + NULL, /* Seek */ + NULL, /* Read */ + mixcomwd_write, /* Write */ + NULL, /* Readdir */ + NULL, /* Select */ + mixcomwd_ioctl, /* Ioctl */ + NULL, /* MMap */ + mixcomwd_open, + NULL, /* flush */ + mixcomwd_release, + NULL, + NULL /* Fasync */ +}; + +static struct miscdevice mixcomwd_miscdev= +{ + WATCHDOG_MINOR, + "watchdog", + &mixcomwd_fops +}; + +__initfunc(static int mixcomwd_checkcard(int port)) +{ + int id; + + if(check_region(port,1)) { + return 0; + } + + id=inb_p(port + MIXCOM_WATCHDOG_OFFSET) & 0x3f; + if(id!=MIXCOM_ID1 && id!=MIXCOM_ID2) { + return 0; + } + return 1; +} + + +__initfunc(void mixcomwd_init(void)) +{ + int i; + int found=0; + + for (i = 0; mixcomwd_ioports[i] != 0; i++) { + if (mixcomwd_checkcard(mixcomwd_ioports[i])) { + found = 1; + mixcomwd_port = mixcomwd_ioports[i]; + break; + } + } + + if (!found) { + printk("mixcomwd: No card detected, or port not available.\n"); + return; + } + + request_region(mixcomwd_port+MIXCOM_WATCHDOG_OFFSET,1,"MixCOM watchdog"); + + misc_register(&mixcomwd_miscdev); + printk("MixCOM watchdog driver v%s, MixCOM card at 0x%3x\n",VERSION,mixcomwd_port); +} + +#ifdef MODULE +int init_module(void) +{ + mixcomwd_init(); + return 0; +} + +void cleanup_module(void) +{ +#ifndef CONFIG_WATCHDOG_NOWAYOUT + if(mixcomwd_timer_alive) { + printk(KERN_WARNING "mixcomwd: I quit now, hardware will" + " probably reboot!\n"); + del_timer(&mixcomwd_timer); + mixcomwd_timer_alive=0; + } +#endif + release_region(mixcomwd_port+MIXCOM_WATCHDOG_OFFSET,1); + misc_deregister(&mixcomwd_miscdev); +} +#endif diff -u --recursive --new-file v2.2.13/linux/drivers/char/moxa.c linux/drivers/char/moxa.c --- v2.2.13/linux/drivers/char/moxa.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/moxa.c Tue Jan 4 10:12:14 2000 @@ -0,0 +1,3319 @@ +/*****************************************************************************/ +/* + * moxa.c -- MOXA Intellio family multiport serial driver. + * + * Copyright (C) 1999-2000 Moxa Technologies (support@moxa.com.tw). + * + * This code is loosely based on the Linux serial driver, written by + * Linus Torvalds, Theodore T'so and others. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * MOXA Intellio Series Driver + * for : LINUX + * date : 1999/1/7 + * version : 5.1 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define MOXA_VERSION "5.1k" + +#define MOXAMAJOR 172 +#define MOXACUMAJOR 173 + +#define put_to_user(arg1, arg2) put_user(arg1, (unsigned long *)arg2) +#define get_from_user(arg1, arg2) get_user(arg1, (unsigned int *)arg2) + +#define MAX_BOARDS 4 /* Don't change this value */ +#define MAX_PORTS_PER_BOARD 32 /* Don't change this value */ +#define MAX_PORTS 128 /* Don't change this value */ + +/* + * Define the Moxa PCI vendor and device IDs. + */ +#define MOXA_BUS_TYPE_ISA 0 +#define MOXA_BUS_TYPE_PCI 1 + +#ifndef PCI_VENDOR_ID_MOXA +#define PCI_VENDOR_ID_MOXA 0x1393 +#endif +#ifndef PCI_DEVICE_ID_CP204J +#define PCI_DEVICE_ID_CP204J 0x2040 +#endif +#ifndef PCI_DEVICE_ID_C218 +#define PCI_DEVICE_ID_C218 0x2180 +#endif +#ifndef PCI_DEVICE_ID_C320 +#define PCI_DEVICE_ID_C320 0x3200 +#endif + +enum { + MOXA_BOARD_C218_PCI = 1, + MOXA_BOARD_C218_ISA, + MOXA_BOARD_C320_PCI, + MOXA_BOARD_C320_ISA, + MOXA_BOARD_CP204J, +}; + +static char *moxa_brdname[] = +{ + "C218 Turbo PCI series", + "C218 Turbo ISA series", + "C320 Turbo PCI series", + "C320 Turbo ISA series", + "CP-204J series", +}; + +typedef struct { + unsigned short vendor_id; + unsigned short device_id; + unsigned short board_type; +} moxa_pciinfo; + +static moxa_pciinfo moxa_pcibrds[] = +{ + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_C218, MOXA_BOARD_C218_PCI}, + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_C320, MOXA_BOARD_C320_PCI}, + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_CP204J, MOXA_BOARD_CP204J}, +}; + +typedef struct _moxa_isa_board_conf { + int boardType; + int numPorts; + unsigned long baseAddr; +} moxa_isa_board_conf; + +static moxa_isa_board_conf moxa_isa_boards[] = +{ +/* {MOXA_BOARD_C218_ISA,8,0xDC000}, */ +}; + +typedef struct _moxa_pci_devinfo { + ushort busNum; + ushort devNum; +} moxa_pci_devinfo; + +typedef struct _moxa_board_conf { + int boardType; + int numPorts; + unsigned long baseAddr; + int busType; + moxa_pci_devinfo pciInfo; +} moxa_board_conf; + +static moxa_board_conf moxa_boards[MAX_BOARDS]; +static unsigned long moxaBaseAddr[MAX_BOARDS]; + +struct moxa_str { + int type; + int port; + int close_delay; + unsigned short closing_wait; + int count; + int blocked_open; + int event; + int asyncflags; + long session; + long pgrp; + unsigned long statusflags; + struct tty_struct *tty; + struct termios normal_termios; + struct termios callout_termios; + struct wait_queue *open_wait; + struct wait_queue *close_wait; + struct tq_struct tqueue; +}; + +struct mxser_mstatus { + tcflag_t cflag; + int cts; + int dsr; + int ri; + int dcd; +}; + +static struct mxser_mstatus GMStatus[MAX_PORTS]; + +/* statusflags */ +#define TXSTOPPED 0x1 +#define LOWWAIT 0x2 +#define EMPTYWAIT 0x4 +#define THROTTLE 0x8 + +/* event */ +#define MOXA_EVENT_HANGUP 1 + +#define SERIAL_DO_RESTART + + +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 + +#define WAKEUP_CHARS 256 + +#define PORTNO(x) (MINOR((x)->device) - (x)->driver.minor_start) + +static int verbose = 0; +static int ttymajor = MOXAMAJOR; +static int calloutmajor = MOXACUMAJOR; +#ifdef MODULE +/* Variables for insmod */ +static int baseaddr[] = {0, 0, 0, 0}; +static int type[] = {0, 0, 0, 0}; +static int numports[] = {0, 0, 0, 0}; + +MODULE_AUTHOR("William Chen"); +MODULE_DESCRIPTION("MOXA Intellio Family Multiport Board Device Driver"); +MODULE_PARM(type, "1-4i"); +MODULE_PARM(baseaddr, "1-4i"); +MODULE_PARM(numports, "1-4i"); +MODULE_PARM(ttymajor, "i"); +MODULE_PARM(calloutmajor, "i"); +MODULE_PARM(verbose, "i"); + +#endif //MODULE + +static struct tty_driver moxaDriver; +static struct tty_driver moxaCallout; +static struct tty_struct *moxaTable[MAX_PORTS + 1]; +static struct termios *moxaTermios[MAX_PORTS + 1]; +static struct termios *moxaTermiosLocked[MAX_PORTS + 1]; +static struct moxa_str moxaChannels[MAX_PORTS]; +static int moxaRefcount; +unsigned char *moxaXmitBuff; +static int moxaTimer_on; +struct timer_list moxaTimer; +static int moxaEmptyTimer_on[MAX_PORTS]; +struct timer_list moxaEmptyTimer[MAX_PORTS]; +struct semaphore moxaBuffSem = MUTEX; + +int moxa_init(void); +#ifdef MODULE +int init_module(void); +void cleanup_module(void); +#endif +/* + * static functions: + */ +static int moxa_get_PCI_conf(struct pci_dev *, int, moxa_board_conf *); +static void do_moxa_softint(void *); +static int moxa_open(struct tty_struct *, struct file *); +static void moxa_close(struct tty_struct *, struct file *); +static int moxa_write(struct tty_struct *, int, const unsigned char *, int); +static int moxa_write_room(struct tty_struct *); +static void moxa_flush_buffer(struct tty_struct *); +static int moxa_chars_in_buffer(struct tty_struct *); +static void moxa_flush_chars(struct tty_struct *); +static void moxa_put_char(struct tty_struct *, unsigned char); +static int moxa_ioctl(struct tty_struct *, struct file *, unsigned int, unsigned long); +static void moxa_throttle(struct tty_struct *); +static void moxa_unthrottle(struct tty_struct *); +static void moxa_set_termios(struct tty_struct *, struct termios *); +static void moxa_stop(struct tty_struct *); +static void moxa_start(struct tty_struct *); +static void moxa_hangup(struct tty_struct *); +static void moxa_poll(unsigned long); +static void set_tty_param(struct tty_struct *); +static int block_till_ready(struct tty_struct *, struct file *, + struct moxa_str *); +static void setup_empty_event(struct tty_struct *); +static void check_xmit_empty(unsigned long); +static void shut_down(struct moxa_str *); +static void receive_data(struct moxa_str *); +/* + * moxa board interface functions: + */ +static void MoxaDriverInit(void); +static int MoxaDriverIoctl(unsigned int, unsigned long, int); +static int MoxaDriverPoll(void); +static int MoxaPortsOfCard(int); +static int MoxaPortIsValid(int); +static void MoxaPortEnable(int); +static void MoxaPortDisable(int); +static long MoxaPortGetMaxBaud(int); +static long MoxaPortSetBaud(int, long); +static int MoxaPortSetTermio(int, struct termios *); +static int MoxaPortGetLineOut(int, int *, int *); +static void MoxaPortLineCtrl(int, int, int); +static void MoxaPortFlowCtrl(int, int, int, int, int, int); +static int MoxaPortLineStatus(int); +static int MoxaPortDCDChange(int); +static int MoxaPortDCDON(int); +static void MoxaPortFlushData(int, int); +static int MoxaPortWriteData(int, unsigned char *, int); +static int MoxaPortReadData(int, unsigned char *, int); +static int MoxaPortTxQueue(int); +static int MoxaPortRxQueue(int); +static int MoxaPortTxFree(int); +static void MoxaPortTxDisable(int); +static void MoxaPortTxEnable(int); +static int MoxaPortResetBrkCnt(int); +static void MoxaPortSendBreak(int, int); +static int moxa_get_serial_info(struct moxa_str *, struct serial_struct *); +static int moxa_set_serial_info(struct moxa_str *, struct serial_struct *); +static void MoxaSetFifo(int port, int enable); + +#ifdef MODULE +int init_module(void) +{ + int ret; + + if (verbose) + printk("Loading module moxa ...\n"); + ret = moxa_init(); + if (verbose) + printk("Done\n"); + return (ret); +} + +void cleanup_module(void) +{ + int i; + + if (verbose) + printk("Unloading module moxa ...\n"); + + if (moxaTimer_on) + del_timer(&moxaTimer); + + for (i = 0; i < MAX_PORTS; i++) + if (moxaEmptyTimer_on[i]) + del_timer(&moxaEmptyTimer[i]); + + if (tty_unregister_driver(&moxaCallout)) + printk("Couldn't unregister MOXA Intellio family callout driver\n"); + if (tty_unregister_driver(&moxaDriver)) + printk("Couldn't unregister MOXA Intellio family serial driver\n"); + if (verbose) + printk("Done\n"); + +} +#endif + +int moxa_init(void) +{ + int i, n, numBoards; + struct moxa_str *ch; + int ret1, ret2; + + printk(KERN_INFO "MOXA Intellio family driver version %s\n", MOXA_VERSION); + + memset(&moxaDriver, 0, sizeof(struct tty_driver)); + memset(&moxaCallout, 0, sizeof(struct tty_driver)); + moxaDriver.magic = TTY_DRIVER_MAGIC; + moxaDriver.name = "ttya"; + moxaDriver.major = ttymajor; + moxaDriver.minor_start = 0; + moxaDriver.num = MAX_PORTS + 1; + moxaDriver.type = TTY_DRIVER_TYPE_SERIAL; + moxaDriver.subtype = SERIAL_TYPE_NORMAL; + moxaDriver.init_termios = tty_std_termios; + moxaDriver.init_termios.c_iflag = 0; + moxaDriver.init_termios.c_oflag = 0; + moxaDriver.init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL; + moxaDriver.init_termios.c_lflag = 0; + moxaDriver.flags = TTY_DRIVER_REAL_RAW; + moxaDriver.refcount = &moxaRefcount; + moxaDriver.table = moxaTable; + moxaDriver.termios = moxaTermios; + moxaDriver.termios_locked = moxaTermiosLocked; + + moxaDriver.open = moxa_open; + moxaDriver.close = moxa_close; + moxaDriver.write = moxa_write; + moxaDriver.write_room = moxa_write_room; + moxaDriver.flush_buffer = moxa_flush_buffer; + moxaDriver.chars_in_buffer = moxa_chars_in_buffer; + moxaDriver.flush_chars = moxa_flush_chars; + moxaDriver.put_char = moxa_put_char; + moxaDriver.ioctl = moxa_ioctl; + moxaDriver.throttle = moxa_throttle; + moxaDriver.unthrottle = moxa_unthrottle; + moxaDriver.set_termios = moxa_set_termios; + moxaDriver.stop = moxa_stop; + moxaDriver.start = moxa_start; + moxaDriver.hangup = moxa_hangup; + + moxaCallout = moxaDriver; + moxaCallout.name = "ttyA"; + moxaCallout.major = calloutmajor; + moxaCallout.subtype = SERIAL_TYPE_CALLOUT; + + moxaXmitBuff = 0; + + for (i = 0, ch = moxaChannels; i < MAX_PORTS; i++, ch++) { + ch->type = PORT_16550A; + ch->port = i; + ch->tqueue.routine = do_moxa_softint; + ch->tqueue.data = ch; + ch->tty = 0; + ch->close_delay = 5 * HZ / 10; + ch->closing_wait = 30 * HZ; + ch->count = 0; + ch->blocked_open = 0; + ch->callout_termios = moxaCallout.init_termios; + ch->normal_termios = moxaDriver.init_termios; + ch->open_wait = 0; + ch->close_wait = 0; + } + + for (i = 0; i < MAX_BOARDS; i++) { + moxa_boards[i].boardType = 0; + moxa_boards[i].numPorts = 0; + moxa_boards[i].baseAddr = 0; + moxa_boards[i].busType = 0; + moxa_boards[i].pciInfo.busNum = 0; + moxa_boards[i].pciInfo.devNum = 0; + } + MoxaDriverInit(); + printk("Tty devices major number = %d, callout devices major number = %d\n", ttymajor, calloutmajor); + + ret1 = 0; + ret2 = 0; + if ((ret1 = tty_register_driver(&moxaDriver))) { + printk(KERN_ERR "Couldn't install MOXA Smartio family driver !\n"); + } else if ((ret2 = tty_register_driver(&moxaCallout))) { + tty_unregister_driver(&moxaDriver); + printk(KERN_ERR "Couldn't install MOXA Smartio family callout driver !\n"); + } + if (ret1 || ret2) { + return -1; + } + for (i = 0; i < MAX_PORTS; i++) { + init_timer(&moxaEmptyTimer[i]); + moxaEmptyTimer[i].function = check_xmit_empty; + moxaEmptyTimer[i].data = (unsigned long) & moxaChannels[i]; + moxaEmptyTimer_on[i] = 0; + } + + init_timer(&moxaTimer); + moxaTimer.function = moxa_poll; + moxaTimer.expires = jiffies + (HZ / 50); + moxaTimer_on = 1; + add_timer(&moxaTimer); + + /* Find the boards defined in source code */ + numBoards = 0; + for (i = 0; i < MAX_BOARDS; i++) { + if ((moxa_isa_boards[i].boardType == MOXA_BOARD_C218_ISA) || + (moxa_isa_boards[i].boardType == MOXA_BOARD_C320_ISA)) { + moxa_boards[numBoards].boardType = moxa_isa_boards[i].boardType; + if (moxa_isa_boards[i].boardType == MOXA_BOARD_C218_ISA) + moxa_boards[numBoards].numPorts = 8; + else + moxa_boards[numBoards].numPorts = moxa_isa_boards[i].numPorts; + moxa_boards[numBoards].busType = MOXA_BUS_TYPE_ISA; + moxa_boards[numBoards].baseAddr = moxa_isa_boards[i].baseAddr; + if (verbose) + printk("Board %2d: %s board(baseAddr=%lx)\n", + numBoards + 1, + moxa_brdname[moxa_boards[numBoards].boardType - 1], + moxa_boards[numBoards].baseAddr); + numBoards++; + } + } + /* Find the boards defined form module args. */ +#ifdef MODULE + for (i = 0; i < MAX_BOARDS; i++) { + if ((type[i] == MOXA_BOARD_C218_ISA) || + (type[i] == MOXA_BOARD_C320_ISA)) { + if (verbose) + printk("Board %2d: %s board(baseAddr=%lx)\n", + numBoards + 1, + moxa_brdname[type[i] - 1], + (unsigned long) baseaddr[i]); + if (numBoards >= MAX_BOARDS) { + if (verbose) + printk("More than %d MOXA Intellio family boards found. Board is ignored.", MAX_BOARDS); + continue; + } + moxa_boards[numBoards].boardType = type[i]; + if (moxa_isa_boards[i].boardType == MOXA_BOARD_C218_ISA) + moxa_boards[numBoards].numPorts = 8; + else + moxa_boards[numBoards].numPorts = numports[i]; + moxa_boards[numBoards].busType = MOXA_BUS_TYPE_ISA; + moxa_boards[numBoards].baseAddr = baseaddr[i]; + numBoards++; + } + } +#endif + /* Find PCI boards here */ +#ifdef CONFIG_PCI + if (pci_present()) { + struct pci_dev *p = NULL; + n = sizeof(moxa_pcibrds) / sizeof(moxa_pciinfo); + i = 0; + while (i < n) { + while((p = pci_find_device(moxa_pcibrds[i].vendor_id, moxa_pcibrds[i].device_id, p))!=NULL) + { + if (numBoards >= MAX_BOARDS) { + if (verbose) + printk("More than %d MOXA Intellio family boards found. Board is ignored.", MAX_BOARDS); + } else { + moxa_get_PCI_conf(p, moxa_pcibrds[i].board_type, + &moxa_boards[numBoards]); + numBoards++; + } + } + i++; + } + } +#endif + for (i = 0; i < numBoards; i++) { + moxaBaseAddr[i] = (unsigned long) ioremap((unsigned long) moxa_boards[i].baseAddr, 0x4000); + } + + return (0); +} + +static int moxa_get_PCI_conf(struct pci_dev *p, int board_type, moxa_board_conf * board) +{ + unsigned int val; + + board->baseAddr = p->base_address[2] & PCI_BASE_ADDRESS_MEM_MASK; + board->boardType = board_type; + switch (board_type) { + case MOXA_BOARD_C218_ISA: + case MOXA_BOARD_C218_PCI: + board->numPorts = 8; + break; + + case MOXA_BOARD_CP204J: + board->numPorts = 4; + break; + default: + board->numPorts = 0; + break; + } + board->busType = MOXA_BUS_TYPE_PCI; + board->pciInfo.busNum = p->bus->number; + board->pciInfo.devNum = p->devfn >> 3; + + return (0); +} + +static void do_moxa_softint(void *private_) +{ + struct moxa_str *ch = (struct moxa_str *) private_; + struct tty_struct *tty; + + if (!ch || !(tty = ch->tty)) + return; + if (test_and_clear_bit(MOXA_EVENT_HANGUP, &ch->event)) { + tty_hangup(tty); + wake_up_interruptible(&ch->open_wait); + ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE); + } +} + +static int moxa_open(struct tty_struct *tty, struct file *filp) +{ + struct moxa_str *ch; + int port; + int retval; + unsigned long page; + + port = PORTNO(tty); + if (port == MAX_PORTS) { + MOD_INC_USE_COUNT; + return (0); + } + if (!MoxaPortIsValid(port)) { + tty->driver_data = NULL; + return (-ENODEV); + } + down(&moxaBuffSem); + if (!moxaXmitBuff) { + page = get_free_page(GFP_KERNEL); + if (!page) { + up(&moxaBuffSem); + return (-ENOMEM); + } + if (moxaXmitBuff) + free_page(page); + else + moxaXmitBuff = (unsigned char *) page; + } + up(&moxaBuffSem); + + MOD_INC_USE_COUNT; + ch = &moxaChannels[port]; + ch->count++; + tty->driver_data = ch; + ch->tty = tty; + if (ch->count == 1 && (ch->asyncflags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = ch->normal_termios; + else + *tty->termios = ch->callout_termios; + } + ch->session = current->session; + ch->pgrp = current->pgrp; + if (!(ch->asyncflags & ASYNC_INITIALIZED)) { + ch->statusflags = 0; + set_tty_param(tty); + MoxaPortLineCtrl(ch->port, 1, 1); + MoxaPortEnable(ch->port); + ch->asyncflags |= ASYNC_INITIALIZED; + } + retval = block_till_ready(tty, filp, ch); + + moxa_unthrottle(tty); + + if (ch->type == PORT_16550A) { + MoxaSetFifo(ch->port, 1); + } else { + MoxaSetFifo(ch->port, 0); + } + + return (retval); +} + +static void moxa_close(struct tty_struct *tty, struct file *filp) +{ + struct moxa_str *ch; + int port; + + port = PORTNO(tty); + if (port == MAX_PORTS) { + MOD_DEC_USE_COUNT; + return; + } + if (!MoxaPortIsValid(port)) { +#ifdef SERIAL_DEBUG_CLOSE + printk("Invalid portno in moxa_close\n"); +#endif + tty->driver_data = NULL; + return; + } + if (tty->driver_data == NULL) { + return; + } + if (tty_hung_up_p(filp)) { + MOD_DEC_USE_COUNT; + return; + } + ch = (struct moxa_str *) tty->driver_data; + + if ((tty->count == 1) && (ch->count != 1)) { + printk("moxa_close: bad serial port count; tty->count is 1, " + "ch->count is %d\n", ch->count); + ch->count = 1; + } + if (--ch->count < 0) { + printk("moxa_close: bad serial port count, minor=%d\n", + MINOR(tty->device)); + ch->count = 0; + } + if (ch->count) { + MOD_DEC_USE_COUNT; + return; + } + ch->asyncflags |= ASYNC_CLOSING; + + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (ch->asyncflags & ASYNC_NORMAL_ACTIVE) + ch->normal_termios = *tty->termios; + if (ch->asyncflags & ASYNC_CALLOUT_ACTIVE) + ch->callout_termios = *tty->termios; + if (ch->asyncflags & ASYNC_INITIALIZED) { + setup_empty_event(tty); + tty_wait_until_sent(tty, 30 * HZ); /* 30 seconds timeout */ + moxaEmptyTimer_on[ch->port] = 0; + del_timer(&moxaEmptyTimer[ch->port]); + } + shut_down(ch); + MoxaPortFlushData(port, 2); + + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + ch->event = 0; + ch->tty = 0; + if (ch->blocked_open) { + if (ch->close_delay) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(ch->close_delay); + } + wake_up_interruptible(&ch->open_wait); + } + ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE | + ASYNC_CLOSING); + wake_up_interruptible(&ch->close_wait); + MOD_DEC_USE_COUNT; +} + +static int moxa_write(struct tty_struct *tty, int from_user, + const unsigned char *buf, int count) +{ + struct moxa_str *ch; + int len, port; + unsigned long flags; + unsigned char *temp; + + ch = (struct moxa_str *) tty->driver_data; + if (ch == NULL) + return (0); + port = ch->port; + save_flags(flags); + cli(); + if (from_user) { + copy_from_user(moxaXmitBuff, buf, count); + temp = moxaXmitBuff; + } else + temp = (unsigned char *) buf; + len = MoxaPortWriteData(port, temp, count); + restore_flags(flags); + /********************************************* + if ( !(ch->statusflags & LOWWAIT) && + ((len != count) || (MoxaPortTxFree(port) <= 100)) ) + ************************************************/ + ch->statusflags |= LOWWAIT; + return (len); +} + +static int moxa_write_room(struct tty_struct *tty) +{ + struct moxa_str *ch; + + if (tty->stopped) + return (0); + ch = (struct moxa_str *) tty->driver_data; + if (ch == NULL) + return (0); + return (MoxaPortTxFree(ch->port)); +} + +static void moxa_flush_buffer(struct tty_struct *tty) +{ + struct moxa_str *ch = (struct moxa_str *) tty->driver_data; + + if (ch == NULL) + return; + MoxaPortFlushData(ch->port, 1); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup) (tty); + wake_up_interruptible(&tty->write_wait); +} + +static int moxa_chars_in_buffer(struct tty_struct *tty) +{ + int chars; + struct moxa_str *ch = (struct moxa_str *) tty->driver_data; + + /* + * Sigh...I have to check if driver_data is NULL here, because + * if an open() fails, the TTY subsystem eventually calls + * tty_wait_until_sent(), which calls the driver's chars_in_buffer() + * routine. And since the open() failed, we return 0 here. TDJ + */ + if (ch == NULL) + return (0); + chars = MoxaPortTxQueue(ch->port); + if (chars) { + /* + * Make it possible to wakeup anything waiting for output + * in tty_ioctl.c, etc. + */ + if (!(ch->statusflags & EMPTYWAIT)) + setup_empty_event(tty); + } + return (chars); +} + +static void moxa_flush_chars(struct tty_struct *tty) +{ + /* + * Don't think I need this, because this is called to empty the TX + * buffer for the 16450, 16550, etc. + */ +} + +static void moxa_put_char(struct tty_struct *tty, unsigned char c) +{ + struct moxa_str *ch; + int port; + unsigned long flags; + + ch = (struct moxa_str *) tty->driver_data; + if (ch == NULL) + return; + port = ch->port; + save_flags(flags); + cli(); + moxaXmitBuff[0] = c; + MoxaPortWriteData(port, moxaXmitBuff, 1); + restore_flags(flags); + /************************************************ + if ( !(ch->statusflags & LOWWAIT) && (MoxaPortTxFree(port) <= 100) ) + *************************************************/ + ch->statusflags |= LOWWAIT; +} + +static int moxa_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct moxa_str *ch = (struct moxa_str *) tty->driver_data; + register int port; + int retval, dtr, rts; + unsigned long flag; + + port = PORTNO(tty); + if ((port != MAX_PORTS) && (!ch)) + return (-EINVAL); + + switch (cmd) { + case TCSBRK: /* SVID version: non-zero arg --> no break */ + retval = tty_check_change(tty); + if (retval) + return (retval); + setup_empty_event(tty); + tty_wait_until_sent(tty, 0); + if (!arg) + MoxaPortSendBreak(ch->port, 0); + return (0); + case TCSBRKP: /* support for POSIX tcsendbreak() */ + retval = tty_check_change(tty); + if (retval) + return (retval); + setup_empty_event(tty); + tty_wait_until_sent(tty, 0); + MoxaPortSendBreak(ch->port, arg); + return (0); + case TIOCGSOFTCAR: + return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg); + case TIOCSSOFTCAR: + if(get_user(retval, (unsigned long *) arg)) + return -EFAULT; + arg = retval; + tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0)); + if (C_CLOCAL(tty)) + ch->asyncflags &= ~ASYNC_CHECK_CD; + else + ch->asyncflags |= ASYNC_CHECK_CD; + return (0); + case TIOCMGET: + flag = 0; + MoxaPortGetLineOut(ch->port, &dtr, &rts); + if (dtr) + flag |= TIOCM_DTR; + if (rts) + flag |= TIOCM_RTS; + dtr = MoxaPortLineStatus(ch->port); + if (dtr & 1) + flag |= TIOCM_CTS; + if (dtr & 2) + flag |= TIOCM_DSR; + if (dtr & 4) + flag |= TIOCM_CD; + return put_user(flag, (unsigned int *) arg); + case TIOCMBIS: + if(get_user(retval, (unsigned int *) arg)) + return -EFAULT; + MoxaPortGetLineOut(ch->port, &dtr, &rts); + if (retval & TIOCM_RTS) + rts = 1; + if (retval & TIOCM_DTR) + dtr = 1; + MoxaPortLineCtrl(ch->port, dtr, rts); + return (0); + case TIOCMBIC: + if(get_user(retval, (unsigned int *) arg)) + return -EFAULT; + MoxaPortGetLineOut(ch->port, &dtr, &rts); + if (retval & TIOCM_RTS) + rts = 0; + if (retval & TIOCM_DTR) + dtr = 0; + MoxaPortLineCtrl(ch->port, dtr, rts); + return (0); + case TIOCMSET: + if(get_user(retval, (unsigned long *) arg)) + return -EFAULT; + dtr = rts = 0; + if (retval & TIOCM_RTS) + rts = 1; + if (retval & TIOCM_DTR) + dtr = 1; + MoxaPortLineCtrl(ch->port, dtr, rts); + return (0); + case TIOCGSERIAL: + return (moxa_get_serial_info(ch, (struct serial_struct *) arg)); + + case TIOCSSERIAL: + return (moxa_set_serial_info(ch, (struct serial_struct *) arg)); + default: + retval = MoxaDriverIoctl(cmd, arg, port); + } + return (retval); +} + +static void moxa_throttle(struct tty_struct *tty) +{ + struct moxa_str *ch = (struct moxa_str *) tty->driver_data; + + ch->statusflags |= THROTTLE; +} + +static void moxa_unthrottle(struct tty_struct *tty) +{ + struct moxa_str *ch = (struct moxa_str *) tty->driver_data; + + ch->statusflags &= ~THROTTLE; +} + +static void moxa_set_termios(struct tty_struct *tty, + struct termios *old_termios) +{ + struct moxa_str *ch = (struct moxa_str *) tty->driver_data; + + if (ch == NULL) + return; + set_tty_param(tty); + if (!(old_termios->c_cflag & CLOCAL) && + (tty->termios->c_cflag & CLOCAL)) + wake_up_interruptible(&ch->open_wait); +} + +static void moxa_stop(struct tty_struct *tty) +{ + struct moxa_str *ch = (struct moxa_str *) tty->driver_data; + + if (ch == NULL) + return; + MoxaPortTxDisable(ch->port); + ch->statusflags |= TXSTOPPED; +} + + +static void moxa_start(struct tty_struct *tty) +{ + struct moxa_str *ch = (struct moxa_str *) tty->driver_data; + + if (ch == NULL) + return; + + if (!(ch->statusflags & TXSTOPPED)) + return; + + MoxaPortTxEnable(ch->port); + ch->statusflags &= ~TXSTOPPED; +} + +static void moxa_hangup(struct tty_struct *tty) +{ + struct moxa_str *ch = (struct moxa_str *) tty->driver_data; + + moxa_flush_buffer(tty); + shut_down(ch); + ch->event = 0; + ch->count = 0; + ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE); + ch->tty = 0; + wake_up_interruptible(&ch->open_wait); +} + +static void moxa_poll(unsigned long ignored) +{ + register int card; + struct moxa_str *ch; + struct tty_struct *tp; + int i, ports; + + moxaTimer_on = 0; + del_timer(&moxaTimer); + + if (MoxaDriverPoll() < 0) { + moxaTimer.function = moxa_poll; + moxaTimer.expires = jiffies + (HZ / 50); + moxaTimer_on = 1; + add_timer(&moxaTimer); + return; + } + for (card = 0; card < MAX_BOARDS; card++) { + if ((ports = MoxaPortsOfCard(card)) <= 0) + continue; + ch = &moxaChannels[card * MAX_PORTS_PER_BOARD]; + for (i = 0; i < ports; i++, ch++) { + if ((ch->asyncflags & ASYNC_INITIALIZED) == 0) + continue; + if (!(ch->statusflags & THROTTLE) && + (MoxaPortRxQueue(ch->port) > 0)) + receive_data(ch); + if ((tp = ch->tty) == 0) + continue; + if (ch->statusflags & LOWWAIT) { + if (MoxaPortTxQueue(ch->port) <= WAKEUP_CHARS) { + if (!tp->stopped) { + ch->statusflags &= ~LOWWAIT; + if ((tp->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tp->ldisc.write_wakeup) + (tp->ldisc.write_wakeup) (tp); + wake_up_interruptible(&tp->write_wait); + } + } + } + if (!I_IGNBRK(tp) && (MoxaPortResetBrkCnt(ch->port) > 0)) { + tty_insert_flip_char(tp, 0, TTY_BREAK); + tty_schedule_flip(tp); + } + if (MoxaPortDCDChange(ch->port)) { + if (ch->asyncflags & ASYNC_CHECK_CD) { + if (MoxaPortDCDON(ch->port)) + wake_up_interruptible(&ch->open_wait); + else { + set_bit(MOXA_EVENT_HANGUP, &ch->event); + queue_task(&ch->tqueue, &tq_scheduler); + } + } + } + } + } + + moxaTimer.function = moxa_poll; + moxaTimer.expires = jiffies + (HZ / 50); + moxaTimer_on = 1; + add_timer(&moxaTimer); +} + +/******************************************************************************/ + +static void set_tty_param(struct tty_struct *tty) +{ + register struct termios *ts; + struct moxa_str *ch; + int rts, cts, txflow, rxflow, xany; + + ch = (struct moxa_str *) tty->driver_data; + ts = tty->termios; + if (ts->c_cflag & CLOCAL) + ch->asyncflags &= ~ASYNC_CHECK_CD; + else + ch->asyncflags |= ASYNC_CHECK_CD; + rts = cts = txflow = rxflow = xany = 0; + if (ts->c_cflag & CRTSCTS) + rts = cts = 1; + if (ts->c_iflag & IXON) + txflow = 1; + if (ts->c_iflag & IXOFF) + rxflow = 1; + if (ts->c_iflag & IXANY) + xany = 1; + MoxaPortFlowCtrl(ch->port, rts, cts, txflow, rxflow, xany); + MoxaPortSetTermio(ch->port, ts); +} + +static int block_till_ready(struct tty_struct *tty, struct file *filp, + struct moxa_str *ch) +{ + struct wait_queue wait = {current, NULL}; + unsigned long flags; + int retval; + int do_clocal = C_CLOCAL(tty); + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || (ch->asyncflags & ASYNC_CLOSING)) { + if (ch->asyncflags & ASYNC_CLOSING) + interruptible_sleep_on(&ch->close_wait); +#ifdef SERIAL_DO_RESTART + if (ch->asyncflags & ASYNC_HUP_NOTIFY) + return (-EAGAIN); + else + return (-ERESTARTSYS); +#else + return (-EAGAIN); +#endif + } + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + if (ch->asyncflags & ASYNC_NORMAL_ACTIVE) + return (-EBUSY); + if ((ch->asyncflags & ASYNC_CALLOUT_ACTIVE) && + (ch->asyncflags & ASYNC_SESSION_LOCKOUT) && + (ch->session != current->session)) + return (-EBUSY); + if ((ch->asyncflags & ASYNC_CALLOUT_ACTIVE) && + (ch->asyncflags & ASYNC_PGRP_LOCKOUT) && + (ch->pgrp != current->pgrp)) + return (-EBUSY); + ch->asyncflags |= ASYNC_CALLOUT_ACTIVE; + return (0); + } + /* + * If non-blocking mode is set, then make the check up front + * and then exit. + */ + if (filp->f_flags & O_NONBLOCK) { + if (ch->asyncflags & ASYNC_CALLOUT_ACTIVE) + return (-EBUSY); + ch->asyncflags |= ASYNC_NORMAL_ACTIVE; + return (0); + } + /* + * Block waiting for the carrier detect and the line to become free + */ + retval = 0; + add_wait_queue(&ch->open_wait, &wait); +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready before block: ttys%d, count = %d\n", + ch->line, ch->count); +#endif + save_flags(flags); + cli(); + if (!tty_hung_up_p(filp)) + ch->count--; + restore_flags(flags); + ch->blocked_open++; + while (1) { + current->state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) || + !(ch->asyncflags & ASYNC_INITIALIZED)) { +#ifdef SERIAL_DO_RESTART + if (ch->asyncflags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; +#else + retval = -EAGAIN; +#endif + break; + } + if (!(ch->asyncflags & ASYNC_CALLOUT_ACTIVE) && + !(ch->asyncflags & ASYNC_CLOSING) && (do_clocal || + MoxaPortDCDON(ch->port))) + break; + + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&ch->open_wait, &wait); + if (!tty_hung_up_p(filp)) + ch->count++; + ch->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready after blocking: ttys%d, count = %d\n", + ch->line, ch->count); +#endif + if (retval) + return (retval); + ch->asyncflags |= ASYNC_NORMAL_ACTIVE; + return (0); +} + +static void setup_empty_event(struct tty_struct *tty) +{ + struct moxa_str *ch = tty->driver_data; + unsigned long flags; + + save_flags(flags); + cli(); + ch->statusflags |= EMPTYWAIT; + moxaEmptyTimer_on[ch->port] = 0; + del_timer(&moxaEmptyTimer[ch->port]); + moxaEmptyTimer[ch->port].expires = jiffies + HZ; + moxaEmptyTimer_on[ch->port] = 1; + add_timer(&moxaEmptyTimer[ch->port]); + restore_flags(flags); +} + +static void check_xmit_empty(unsigned long data) +{ + struct moxa_str *ch; + + ch = (struct moxa_str *) data; + moxaEmptyTimer_on[ch->port] = 0; + del_timer(&moxaEmptyTimer[ch->port]); + if (ch->tty && (ch->statusflags & EMPTYWAIT)) { + if (MoxaPortTxQueue(ch->port) == 0) { + ch->statusflags &= ~EMPTYWAIT; + if ((ch->tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + ch->tty->ldisc.write_wakeup) + (ch->tty->ldisc.write_wakeup) (ch->tty); + wake_up_interruptible(&ch->tty->write_wait); + return; + } + moxaEmptyTimer[ch->port].expires = jiffies + HZ; + moxaEmptyTimer_on[ch->port] = 1; + add_timer(&moxaEmptyTimer[ch->port]); + } else + ch->statusflags &= ~EMPTYWAIT; +} + +static void shut_down(struct moxa_str *ch) +{ + struct tty_struct *tp; + + if (!(ch->asyncflags & ASYNC_INITIALIZED)) + return; + + tp = ch->tty; + + MoxaPortDisable(ch->port); + + /* + * If we're a modem control device and HUPCL is on, drop RTS & DTR. + */ + if (tp->termios->c_cflag & HUPCL) + MoxaPortLineCtrl(ch->port, 0, 0); + + ch->asyncflags &= ~ASYNC_INITIALIZED; +} + +static void receive_data(struct moxa_str *ch) +{ + struct tty_struct *tp; + struct termios *ts; + int i, count, rc, space; + unsigned char *charptr, *flagptr; + unsigned long flags; + + ts = 0; + tp = ch->tty; + if (tp) + ts = tp->termios; + /************************************************** + if ( !tp || !ts || !(ts->c_cflag & CREAD) ) { + *****************************************************/ + if (!tp || !ts) { + MoxaPortFlushData(ch->port, 0); + return; + } + space = TTY_FLIPBUF_SIZE - tp->flip.count; + if (space <= 0) + return; + charptr = tp->flip.char_buf_ptr; + flagptr = tp->flip.flag_buf_ptr; + rc = tp->flip.count; + save_flags(flags); + cli(); + count = MoxaPortReadData(ch->port, charptr, space); + restore_flags(flags); + for (i = 0; i < count; i++) + *flagptr++ = 0; + charptr += count; + rc += count; + tp->flip.count = rc; + tp->flip.char_buf_ptr = charptr; + tp->flip.flag_buf_ptr = flagptr; + tty_schedule_flip(ch->tty); +} + +#define Magic_code 0x404 + +/* + * System Configuration + */ +/* + * for C218 BIOS initialization + */ +#define C218_ConfBase 0x800 +#define C218_status (C218_ConfBase + 0) /* BIOS running status */ +#define C218_diag (C218_ConfBase + 2) /* diagnostic status */ +#define C218_key (C218_ConfBase + 4) /* WORD (0x218 for C218) */ +#define C218DLoad_len (C218_ConfBase + 6) /* WORD */ +#define C218check_sum (C218_ConfBase + 8) /* BYTE */ +#define C218chksum_ok (C218_ConfBase + 0x0a) /* BYTE (1:ok) */ +#define C218_TestRx (C218_ConfBase + 0x10) /* 8 bytes for 8 ports */ +#define C218_TestTx (C218_ConfBase + 0x18) /* 8 bytes for 8 ports */ +#define C218_RXerr (C218_ConfBase + 0x20) /* 8 bytes for 8 ports */ +#define C218_ErrFlag (C218_ConfBase + 0x28) /* 8 bytes for 8 ports */ + +#define C218_LoadBuf 0x0F00 +#define C218_KeyCode 0x218 +#define CP204J_KeyCode 0x204 + +/* + * for C320 BIOS initialization + */ +#define C320_ConfBase 0x800 +#define C320_LoadBuf 0x0f00 +#define STS_init 0x05 /* for C320_status */ + +#define C320_status C320_ConfBase + 0 /* BIOS running status */ +#define C320_diag C320_ConfBase + 2 /* diagnostic status */ +#define C320_key C320_ConfBase + 4 /* WORD (0320H for C320) */ +#define C320DLoad_len C320_ConfBase + 6 /* WORD */ +#define C320check_sum C320_ConfBase + 8 /* WORD */ +#define C320chksum_ok C320_ConfBase + 0x0a /* WORD (1:ok) */ +#define C320bapi_len C320_ConfBase + 0x0c /* WORD */ +#define C320UART_no C320_ConfBase + 0x0e /* WORD */ + +#define C320_KeyCode 0x320 + +#define FixPage_addr 0x0000 /* starting addr of static page */ +#define DynPage_addr 0x2000 /* starting addr of dynamic page */ +#define C218_start 0x3000 /* starting addr of C218 BIOS prg */ +#define Control_reg 0x1ff0 /* select page and reset control */ +#define HW_reset 0x80 + +/* + * Function Codes + */ +#define FC_CardReset 0x80 +#define FC_ChannelReset 1 /* C320 firmware not supported */ +#define FC_EnableCH 2 +#define FC_DisableCH 3 +#define FC_SetParam 4 +#define FC_SetMode 5 +#define FC_SetRate 6 +#define FC_LineControl 7 +#define FC_LineStatus 8 +#define FC_XmitControl 9 +#define FC_FlushQueue 10 +#define FC_SendBreak 11 +#define FC_StopBreak 12 +#define FC_LoopbackON 13 +#define FC_LoopbackOFF 14 +#define FC_ClrIrqTable 15 +#define FC_SendXon 16 +#define FC_SetTermIrq 17 /* C320 firmware not supported */ +#define FC_SetCntIrq 18 /* C320 firmware not supported */ +#define FC_SetBreakIrq 19 +#define FC_SetLineIrq 20 +#define FC_SetFlowCtl 21 +#define FC_GenIrq 22 +#define FC_InCD180 23 +#define FC_OutCD180 24 +#define FC_InUARTreg 23 +#define FC_OutUARTreg 24 +#define FC_SetXonXoff 25 +#define FC_OutCD180CCR 26 +#define FC_ExtIQueue 27 +#define FC_ExtOQueue 28 +#define FC_ClrLineIrq 29 +#define FC_HWFlowCtl 30 +#define FC_GetClockRate 35 +#define FC_SetBaud 36 +#define FC_SetDataMode 41 +#define FC_GetCCSR 43 +#define FC_GetDataError 45 +#define FC_RxControl 50 +#define FC_ImmSend 51 +#define FC_SetXonState 52 +#define FC_SetXoffState 53 +#define FC_SetRxFIFOTrig 54 +#define FC_SetTxFIFOCnt 55 +#define FC_UnixRate 56 +#define FC_UnixResetTimer 57 + +#define RxFIFOTrig1 0 +#define RxFIFOTrig4 1 +#define RxFIFOTrig8 2 +#define RxFIFOTrig14 3 + +/* + * Dual-Ported RAM + */ +#define DRAM_global 0 +#define INT_data (DRAM_global + 0) +#define Config_base (DRAM_global + 0x108) + +#define IRQindex (INT_data + 0) +#define IRQpending (INT_data + 4) +#define IRQtable (INT_data + 8) + +/* + * Interrupt Status + */ +#define IntrRx 0x01 /* receiver data O.K. */ +#define IntrTx 0x02 /* transmit buffer empty */ +#define IntrFunc 0x04 /* function complete */ +#define IntrBreak 0x08 /* received break */ +#define IntrLine 0x10 /* line status change + for transmitter */ +#define IntrIntr 0x20 /* received INTR code */ +#define IntrQuit 0x40 /* received QUIT code */ +#define IntrEOF 0x80 /* received EOF code */ + +#define IntrRxTrigger 0x100 /* rx data count reach tigger value */ +#define IntrTxTrigger 0x200 /* tx data count below trigger value */ + +#define Magic_no (Config_base + 0) +#define Card_model_no (Config_base + 2) +#define Total_ports (Config_base + 4) +#define Module_cnt (Config_base + 8) +#define Module_no (Config_base + 10) +#define Timer_10ms (Config_base + 14) +#define Disable_IRQ (Config_base + 20) +#define TMS320_PORT1 (Config_base + 22) +#define TMS320_PORT2 (Config_base + 24) +#define TMS320_CLOCK (Config_base + 26) + +/* + * DATA BUFFER in DRAM + */ +#define Extern_table 0x400 /* Base address of the external table + (24 words * 64) total 3K bytes + (24 words * 128) total 6K bytes */ +#define Extern_size 0x60 /* 96 bytes */ +#define RXrptr 0x00 /* read pointer for RX buffer */ +#define RXwptr 0x02 /* write pointer for RX buffer */ +#define TXrptr 0x04 /* read pointer for TX buffer */ +#define TXwptr 0x06 /* write pointer for TX buffer */ +#define HostStat 0x08 /* IRQ flag and general flag */ +#define FlagStat 0x0A +#define FlowControl 0x0C /* B7 B6 B5 B4 B3 B2 B1 B0 */ + /* x x x x | | | | */ + /* | | | + CTS flow */ + /* | | +--- RTS flow */ + /* | +------ TX Xon/Xoff */ + /* +--------- RX Xon/Xoff */ +#define Break_cnt 0x0E /* received break count */ +#define CD180TXirq 0x10 /* if non-0: enable TX irq */ +#define RX_mask 0x12 +#define TX_mask 0x14 +#define Ofs_rxb 0x16 +#define Ofs_txb 0x18 +#define Page_rxb 0x1A +#define Page_txb 0x1C +#define EndPage_rxb 0x1E +#define EndPage_txb 0x20 +#define Data_error 0x22 +#define RxTrigger 0x28 +#define TxTrigger 0x2a + +#define rRXwptr 0x34 +#define Low_water 0x36 + +#define FuncCode 0x40 +#define FuncArg 0x42 +#define FuncArg1 0x44 + +#define C218rx_size 0x2000 /* 8K bytes */ +#define C218tx_size 0x8000 /* 32K bytes */ + +#define C218rx_mask (C218rx_size - 1) +#define C218tx_mask (C218tx_size - 1) + +#define C320p8rx_size 0x2000 +#define C320p8tx_size 0x8000 +#define C320p8rx_mask (C320p8rx_size - 1) +#define C320p8tx_mask (C320p8tx_size - 1) + +#define C320p16rx_size 0x2000 +#define C320p16tx_size 0x4000 +#define C320p16rx_mask (C320p16rx_size - 1) +#define C320p16tx_mask (C320p16tx_size - 1) + +#define C320p24rx_size 0x2000 +#define C320p24tx_size 0x2000 +#define C320p24rx_mask (C320p24rx_size - 1) +#define C320p24tx_mask (C320p24tx_size - 1) + +#define C320p32rx_size 0x1000 +#define C320p32tx_size 0x1000 +#define C320p32rx_mask (C320p32rx_size - 1) +#define C320p32tx_mask (C320p32tx_size - 1) + +#define Page_size 0x2000 +#define Page_mask (Page_size - 1) +#define C218rx_spage 3 +#define C218tx_spage 4 +#define C218rx_pageno 1 +#define C218tx_pageno 4 +#define C218buf_pageno 5 + +#define C320p8rx_spage 3 +#define C320p8tx_spage 4 +#define C320p8rx_pgno 1 +#define C320p8tx_pgno 4 +#define C320p8buf_pgno 5 + +#define C320p16rx_spage 3 +#define C320p16tx_spage 4 +#define C320p16rx_pgno 1 +#define C320p16tx_pgno 2 +#define C320p16buf_pgno 3 + +#define C320p24rx_spage 3 +#define C320p24tx_spage 4 +#define C320p24rx_pgno 1 +#define C320p24tx_pgno 1 +#define C320p24buf_pgno 2 + +#define C320p32rx_spage 3 +#define C320p32tx_ofs C320p32rx_size +#define C320p32tx_spage 3 +#define C320p32buf_pgno 1 + +/* + * Host Status + */ +#define WakeupRx 0x01 +#define WakeupTx 0x02 +#define WakeupBreak 0x08 +#define WakeupLine 0x10 +#define WakeupIntr 0x20 +#define WakeupQuit 0x40 +#define WakeupEOF 0x80 /* used in VTIME control */ +#define WakeupRxTrigger 0x100 +#define WakeupTxTrigger 0x200 +/* + * Flag status + */ +#define Rx_over 0x01 +#define Xoff_state 0x02 +#define Tx_flowOff 0x04 +#define Tx_enable 0x08 +#define CTS_state 0x10 +#define DSR_state 0x20 +#define DCD_state 0x80 +/* + * FlowControl + */ +#define CTS_FlowCtl 1 +#define RTS_FlowCtl 2 +#define Tx_FlowCtl 4 +#define Rx_FlowCtl 8 +#define IXM_IXANY 0x10 + +#define LowWater 128 + +#define DTR_ON 1 +#define RTS_ON 2 +#define CTS_ON 1 +#define DSR_ON 2 +#define DCD_ON 8 + +/* mode definition */ +#define MX_CS8 0x03 +#define MX_CS7 0x02 +#define MX_CS6 0x01 +#define MX_CS5 0x00 + +#define MX_STOP1 0x00 +#define MX_STOP15 0x04 +#define MX_STOP2 0x08 + +#define MX_PARNONE 0x00 +#define MX_PAREVEN 0x40 +#define MX_PARODD 0xC0 + +/* + * Query + */ +#define QueryPort MAX_PORTS + + + +struct mon_str { + int tick; + int rxcnt[MAX_PORTS]; + int txcnt[MAX_PORTS]; +}; +typedef struct mon_str mon_st; + +#define DCD_changed 0x01 +#define DCD_oldstate 0x80 + +static unsigned char moxaBuff[10240]; +static unsigned long moxaIntNdx[MAX_BOARDS]; +static unsigned long moxaIntPend[MAX_BOARDS]; +static unsigned long moxaIntTable[MAX_BOARDS]; +static char moxaChkPort[MAX_PORTS]; +static char moxaLineCtrl[MAX_PORTS]; +static unsigned long moxaTableAddr[MAX_PORTS]; +static long moxaCurBaud[MAX_PORTS]; +static char moxaDCDState[MAX_PORTS]; +static char moxaLowChkFlag[MAX_PORTS]; +static int moxaLowWaterChk; +static int moxaCard; +static mon_st moxaLog; +static int moxaFuncTout; +static ushort moxaBreakCnt[MAX_PORTS]; + +static void moxadelay(int); +static void moxafunc(unsigned long, int, ushort); +static void wait_finish(unsigned long); +static void low_water_check(unsigned long); +static int moxaloadbios(int, unsigned char *, int); +static int moxafindcard(int); +static int moxaload320b(int, unsigned char *, int); +static int moxaloadcode(int, unsigned char *, int); +static int moxaloadc218(int, unsigned long, int); +static int moxaloadc320(int, unsigned long, int, int *); + +/***************************************************************************** + * Driver level functions: * + * 1. MoxaDriverInit(void); * + * 2. MoxaDriverIoctl(unsigned int cmd, unsigned long arg, int port); * + * 3. MoxaDriverPoll(void); * + *****************************************************************************/ +void MoxaDriverInit(void) +{ + int i; + + moxaFuncTout = HZ / 2; /* 500 mini-seconds */ + moxaCard = 0; + moxaLog.tick = 0; + moxaLowWaterChk = 0; + for (i = 0; i < MAX_PORTS; i++) { + moxaChkPort[i] = 0; + moxaLowChkFlag[i] = 0; + moxaLineCtrl[i] = 0; + moxaLog.rxcnt[i] = 0; + moxaLog.txcnt[i] = 0; + } +} + +#define MOXA 0x400 +#define MOXA_GET_IQUEUE (MOXA + 1) /* get input buffered count */ +#define MOXA_GET_OQUEUE (MOXA + 2) /* get output buffered count */ +#define MOXA_INIT_DRIVER (MOXA + 6) /* moxaCard=0 */ +#define MOXA_LOAD_BIOS (MOXA + 9) /* download BIOS */ +#define MOXA_FIND_BOARD (MOXA + 10) /* Check if MOXA card exist? */ +#define MOXA_LOAD_C320B (MOXA + 11) /* download 320B firmware */ +#define MOXA_LOAD_CODE (MOXA + 12) /* download firmware */ +#define MOXA_GETDATACOUNT (MOXA + 23) +#define MOXA_GET_IOQUEUE (MOXA + 27) +#define MOXA_FLUSH_QUEUE (MOXA + 28) +#define MOXA_GET_CONF (MOXA + 35) /* configuration */ +#define MOXA_GET_MAJOR (MOXA + 63) +#define MOXA_GET_CUMAJOR (MOXA + 64) +#define MOXA_GETMSTATUS (MOXA + 65) + + +struct moxaq_str { + int inq; + int outq; +}; + +struct dl_str { + char *buf; + int len; + int cardno; +}; + +static struct moxaq_str temp_queue[MAX_PORTS]; +static struct dl_str dltmp; + +void MoxaPortFlushData(int port, int mode) +{ + unsigned long ofsAddr; + if ((mode < 0) || (mode > 2)) + return; + ofsAddr = moxaTableAddr[port]; + moxafunc(ofsAddr, FC_FlushQueue, mode); + if (mode != 1) { + moxaLowChkFlag[port] = 0; + low_water_check(ofsAddr); + } +} + +int MoxaDriverIoctl(unsigned int cmd, unsigned long arg, int port) +{ + int i; + int status; + int MoxaPortTxQueue(int), MoxaPortRxQueue(int); + + if (port == QueryPort) { + if ((cmd != MOXA_GET_CONF) && (cmd != MOXA_INIT_DRIVER) && + (cmd != MOXA_LOAD_BIOS) && (cmd != MOXA_FIND_BOARD) && (cmd != MOXA_LOAD_C320B) && + (cmd != MOXA_LOAD_CODE) && (cmd != MOXA_GETDATACOUNT) && + (cmd != MOXA_GET_IOQUEUE) && (cmd != MOXA_GET_MAJOR) && + (cmd != MOXA_GET_CUMAJOR) && (cmd != MOXA_GETMSTATUS)) + return (-EINVAL); + } + switch (cmd) { + case MOXA_GET_CONF: + if(copy_to_user((void *)arg, &moxa_boards, MAX_BOARDS * sizeof(moxa_board_conf))) + return -EFAULT; + return (0); + case MOXA_INIT_DRIVER: + if ((int) arg == 0x404) + MoxaDriverInit(); + return (0); + case MOXA_GETDATACOUNT: + moxaLog.tick = jiffies; + if(copy_to_user((void *)arg, &moxaLog, sizeof(mon_st))) + return -EFAULT; + return (0); + case MOXA_FLUSH_QUEUE: + MoxaPortFlushData(port, arg); + return (0); + case MOXA_GET_IOQUEUE: + for (i = 0; i < MAX_PORTS; i++) { + if (moxaChkPort[i]) { + temp_queue[i].inq = MoxaPortRxQueue(i); + temp_queue[i].outq = MoxaPortTxQueue(i); + } + } + if(copy_to_user((void *)arg, temp_queue, sizeof(struct moxaq_str) * MAX_PORTS)) + return -EFAULT; + return (0); + case MOXA_LOAD_BIOS: + if(copy_from_user(&dltmp, (void *)arg, sizeof(struct dl_str))) + return -EFAULT; + i = moxaloadbios(dltmp.cardno, dltmp.buf, dltmp.len); + return (i); + case MOXA_FIND_BOARD: + if(copy_from_user(&dltmp, (void *)arg, sizeof(struct dl_str))) + return -EFAULT; + return moxafindcard(dltmp.cardno); + case MOXA_LOAD_C320B: + if(copy_from_user(&dltmp, (void *)arg, sizeof(struct dl_str))) + return -EFAULT; + moxaload320b(dltmp.cardno, dltmp.buf, dltmp.len); + return (0); + case MOXA_LOAD_CODE: + if(copy_from_user(&dltmp, (void *)arg, sizeof(struct dl_str))) + return -EFAULT; + i = moxaloadcode(dltmp.cardno, dltmp.buf, dltmp.len); + if (i == -1) + return (-EFAULT); + return (i); + case MOXA_GET_OQUEUE: + i = MoxaPortTxQueue(port); + return put_user(i, (unsigned long *) arg); + case MOXA_GET_IQUEUE: + i = MoxaPortRxQueue(port); + return put_user(i, (unsigned long *) arg); + case MOXA_GET_MAJOR: + if(copy_to_user((void *)arg, &ttymajor, sizeof(int))) + return -EFAULT; + return 0; + case MOXA_GET_CUMAJOR: + if(copy_to_user((void *)arg, &calloutmajor, sizeof(int))) + return -EFAULT; + return 0; + case MOXA_GETMSTATUS: + for (i = 0; i < MAX_PORTS; i++) { + GMStatus[i].ri = 0; + GMStatus[i].dcd = 0; + GMStatus[i].dsr = 0; + GMStatus[i].cts = 0; + if (!moxaChkPort[i]) { + continue; + } else { + status = MoxaPortLineStatus(moxaChannels[i].port); + if (status & 1) + GMStatus[i].cts = 1; + if (status & 2) + GMStatus[i].dsr = 1; + if (status & 4) + GMStatus[i].dcd = 1; + } + + if (!moxaChannels[i].tty || !moxaChannels[i].tty->termios) + GMStatus[i].cflag = moxaChannels[i].normal_termios.c_cflag; + else + GMStatus[i].cflag = moxaChannels[i].tty->termios->c_cflag; + } + if(copy_to_user((void *)arg, GMStatus, sizeof(struct mxser_mstatus) * MAX_PORTS)) + return -EFAULT; + return 0; + + } + return (-ENOIOCTLCMD); +} + +int MoxaDriverPoll(void) +{ + register ushort temp; + register int card; + unsigned long ip, ofsAddr; + int port, p, ports; + + if (moxaCard == 0) + return (-1); + for (card = 0; card < MAX_BOARDS; card++) { + if ((ports = moxa_boards[card].numPorts) == 0) + continue; + if (readb(moxaIntPend[card]) == 0xff) { + ip = moxaIntTable[card] + readb(moxaIntNdx[card]); + p = card * MAX_PORTS_PER_BOARD; + ports <<= 1; + for (port = 0; port < ports; port += 2, p++) { + if ((temp = readw(ip + port)) != 0) { + writew(0, ip + port); + ofsAddr = moxaTableAddr[p]; + if (temp & IntrTx) + writew(readw(ofsAddr + HostStat) & ~WakeupTx, ofsAddr + HostStat); + if (temp & IntrBreak) { + moxaBreakCnt[p]++; + } + if (temp & IntrLine) { + if (readb(ofsAddr + FlagStat) & DCD_state) { + if ((moxaDCDState[p] & DCD_oldstate) == 0) + moxaDCDState[p] = (DCD_oldstate | + DCD_changed); + } else { + if (moxaDCDState[p] & DCD_oldstate) + moxaDCDState[p] = DCD_changed; + } + } + } + } + writeb(0, moxaIntPend[card]); + } + if (moxaLowWaterChk) { + p = card * MAX_PORTS_PER_BOARD; + for (port = 0; port < ports; port++, p++) { + if (moxaLowChkFlag[p]) { + moxaLowChkFlag[p] = 0; + ofsAddr = moxaTableAddr[p]; + low_water_check(ofsAddr); + } + } + } + } + moxaLowWaterChk = 0; + return (0); +} + +/***************************************************************************** + * Card level function: * + * 1. MoxaPortsOfCard(int cardno); * + *****************************************************************************/ +int MoxaPortsOfCard(int cardno) +{ + + if (moxa_boards[cardno].boardType == 0) + return (0); + return (moxa_boards[cardno].numPorts); +} + +/***************************************************************************** + * Port level functions: * + * 1. MoxaPortIsValid(int port); * + * 2. MoxaPortEnable(int port); * + * 3. MoxaPortDisable(int port); * + * 4. MoxaPortGetMaxBaud(int port); * + * 5. MoxaPortGetCurBaud(int port); * + * 6. MoxaPortSetBaud(int port, long baud); * + * 7. MoxaPortSetMode(int port, int databit, int stopbit, int parity); * + * 8. MoxaPortSetTermio(int port, unsigned char *termio); * + * 9. MoxaPortGetLineOut(int port, int *dtrState, int *rtsState); * + * 10. MoxaPortLineCtrl(int port, int dtrState, int rtsState); * + * 11. MoxaPortFlowCtrl(int port, int rts, int cts, int rx, int tx,int xany); * + * 12. MoxaPortLineStatus(int port); * + * 13. MoxaPortDCDChange(int port); * + * 14. MoxaPortDCDON(int port); * + * 15. MoxaPortFlushData(int port, int mode); * + * 16. MoxaPortWriteData(int port, unsigned char * buffer, int length); * + * 17. MoxaPortReadData(int port, unsigned char * buffer, int length); * + * 18. MoxaPortTxBufSize(int port); * + * 19. MoxaPortRxBufSize(int port); * + * 20. MoxaPortTxQueue(int port); * + * 21. MoxaPortTxFree(int port); * + * 22. MoxaPortRxQueue(int port); * + * 23. MoxaPortRxFree(int port); * + * 24. MoxaPortTxDisable(int port); * + * 25. MoxaPortTxEnable(int port); * + * 26. MoxaPortGetBrkCnt(int port); * + * 27. MoxaPortResetBrkCnt(int port); * + * 28. MoxaPortSetXonXoff(int port, int xonValue, int xoffValue); * + * 29. MoxaPortIsTxHold(int port); * + * 30. MoxaPortSendBreak(int port, int ticks); * + *****************************************************************************/ +/* + * Moxa Port Number Description: + * + * MOXA serial driver supports up to 4 MOXA-C218/C320 boards. And, + * the port number using in MOXA driver functions will be 0 to 31 for + * first MOXA board, 32 to 63 for second, 64 to 95 for third and 96 + * to 127 for fourth. For example, if you setup three MOXA boards, + * first board is C218, second board is C320-16 and third board is + * C320-32. The port number of first board (C218 - 8 ports) is from + * 0 to 7. The port number of second board (C320 - 16 ports) is form + * 32 to 47. The port number of third board (C320 - 32 ports) is from + * 64 to 95. And those port numbers form 8 to 31, 48 to 63 and 96 to + * 127 will be invalid. + * + * + * Moxa Functions Description: + * + * Function 1: Driver initialization routine, this routine must be + * called when initialized driver. + * Syntax: + * void MoxaDriverInit(); + * + * + * Function 2: Moxa driver private IOCTL command processing. + * Syntax: + * int MoxaDriverIoctl(unsigned int cmd, unsigned long arg, int port); + * + * unsigned int cmd : IOCTL command + * unsigned long arg : IOCTL argument + * int port : port number (0 - 127) + * + * return: 0 (OK) + * -EINVAL + * -ENOIOCTLCMD + * + * + * Function 3: Moxa driver polling process routine. + * Syntax: + * int MoxaDriverPoll(void); + * + * return: 0 ; polling O.K. + * -1 : no any Moxa card. + * + * + * Function 4: Get the ports of this card. + * Syntax: + * int MoxaPortsOfCard(int cardno); + * + * int cardno : card number (0 - 3) + * + * return: 0 : this card is invalid + * 8/16/24/32 + * + * + * Function 5: Check this port is valid or invalid + * Syntax: + * int MoxaPortIsValid(int port); + * int port : port number (0 - 127, ref port description) + * + * return: 0 : this port is invalid + * 1 : this port is valid + * + * + * Function 6: Enable this port to start Tx/Rx data. + * Syntax: + * void MoxaPortEnable(int port); + * int port : port number (0 - 127) + * + * + * Function 7: Disable this port + * Syntax: + * void MoxaPortDisable(int port); + * int port : port number (0 - 127) + * + * + * Function 8: Get the maximun available baud rate of this port. + * Syntax: + * long MoxaPortGetMaxBaud(int port); + * int port : port number (0 - 127) + * + * return: 0 : this port is invalid + * 38400/57600/115200 bps + * + * + * Function 9: Get the current baud rate of this port. + * Syntax: + * long MoxaPortGetCurBaud(int port); + * int port : port number (0 - 127) + * + * return: 0 : this port is invalid + * 50 - 115200 bps + * + * + * Function 10: Setting baud rate of this port. + * Syntax: + * long MoxaPortSetBaud(int port, long baud); + * int port : port number (0 - 127) + * long baud : baud rate (50 - 115200) + * + * return: 0 : this port is invalid or baud < 50 + * 50 - 115200 : the real baud rate set to the port, if + * the argument baud is large than maximun + * available baud rate, the real setting + * baud rate will be the maximun baud rate. + * + * + * Function 11: Setting the data-bits/stop-bits/parity of this port + * Syntax: + * int MoxaPortSetMode(int port, int databits, int stopbits, int parity); + * int port : port number (0 - 127) + * int databits : data bits (8/7/6/5) + * int stopbits : stop bits (2/1/0, 0 show 1.5 stop bits) + int parity : parity (0:None,1:Odd,2:Even,3:Mark,4:Space) + * + * return: -1 : invalid parameter + * 0 : setting O.K. + * + * + * Function 12: Configure the port. + * Syntax: + * int MoxaPortSetTermio(int port, struct termios *termio); + * int port : port number (0 - 127) + * struct termios * termio : termio structure pointer + * + * return: -1 : this port is invalid or termio == NULL + * 0 : setting O.K. + * + * + * Function 13: Get the DTR/RTS state of this port. + * Syntax: + * int MoxaPortGetLineOut(int port, int *dtrState, int *rtsState); + * int port : port number (0 - 127) + * int * dtrState : pointer to INT to receive the current DTR + * state. (if NULL, this function will not + * write to this address) + * int * rtsState : pointer to INT to receive the current RTS + * state. (if NULL, this function will not + * write to this address) + * + * return: -1 : this port is invalid + * 0 : O.K. + * + * + * Function 14: Setting the DTR/RTS output state of this port. + * Syntax: + * void MoxaPortLineCtrl(int port, int dtrState, int rtsState); + * int port : port number (0 - 127) + * int dtrState : DTR output state (0: off, 1: on) + * int rtsState : RTS output state (0: off, 1: on) + * + * + * Function 15: Setting the flow control of this port. + * Syntax: + * void MoxaPortFlowCtrl(int port, int rtsFlow, int ctsFlow, int rxFlow, + * int txFlow,int xany); + * int port : port number (0 - 127) + * int rtsFlow : H/W RTS flow control (0: no, 1: yes) + * int ctsFlow : H/W CTS flow control (0: no, 1: yes) + * int rxFlow : S/W Rx XON/XOFF flow control (0: no, 1: yes) + * int txFlow : S/W Tx XON/XOFF flow control (0: no, 1: yes) + * int xany : S/W XANY flow control (0: no, 1: yes) + * + * + * Function 16: Get ths line status of this port + * Syntax: + * int MoxaPortLineStatus(int port); + * int port : port number (0 - 127) + * + * return: Bit 0 - CTS state (0: off, 1: on) + * Bit 1 - DSR state (0: off, 1: on) + * Bit 2 - DCD state (0: off, 1: on) + * + * + * Function 17: Check the DCD state has changed since the last read + * of this function. + * Syntax: + * int MoxaPortDCDChange(int port); + * int port : port number (0 - 127) + * + * return: 0 : no changed + * 1 : DCD has changed + * + * + * Function 18: Check ths current DCD state is ON or not. + * Syntax: + * int MoxaPortDCDON(int port); + * int port : port number (0 - 127) + * + * return: 0 : DCD off + * 1 : DCD on + * + * + * Function 19: Flush the Rx/Tx buffer data of this port. + * Syntax: + * void MoxaPortFlushData(int port, int mode); + * int port : port number (0 - 127) + * int mode + * 0 : flush the Rx buffer + * 1 : flush the Tx buffer + * 2 : flush the Rx and Tx buffer + * + * + * Function 20: Write data. + * Syntax: + * int MoxaPortWriteData(int port, unsigned char * buffer, int length); + * int port : port number (0 - 127) + * unsigned char * buffer : pointer to write data buffer. + * int length : write data length + * + * return: 0 - length : real write data length + * + * + * Function 21: Read data. + * Syntax: + * int MoxaPortReadData(int port, unsigned char * buffer, int length); + * int port : port number (0 - 127) + * unsigned char * buffer : pointer to read data buffer. + * int length : read data buffer length + * + * return: 0 - length : real read data length + * + * + * Function 22: Get the Tx buffer size of this port + * Syntax: + * int MoxaPortTxBufSize(int port); + * int port : port number (0 - 127) + * + * return: .. : Tx buffer size + * + * + * Function 23: Get the Rx buffer size of this port + * Syntax: + * int MoxaPortRxBufSize(int port); + * int port : port number (0 - 127) + * + * return: .. : Rx buffer size + * + * + * Function 24: Get the Tx buffer current queued data bytes + * Syntax: + * int MoxaPortTxQueue(int port); + * int port : port number (0 - 127) + * + * return: .. : Tx buffer current queued data bytes + * + * + * Function 25: Get the Tx buffer current free space + * Syntax: + * int MoxaPortTxFree(int port); + * int port : port number (0 - 127) + * + * return: .. : Tx buffer current free space + * + * + * Function 26: Get the Rx buffer current queued data bytes + * Syntax: + * int MoxaPortRxQueue(int port); + * int port : port number (0 - 127) + * + * return: .. : Rx buffer current queued data bytes + * + * + * Function 27: Get the Rx buffer current free space + * Syntax: + * int MoxaPortRxFree(int port); + * int port : port number (0 - 127) + * + * return: .. : Rx buffer current free space + * + * + * Function 28: Disable port data transmission. + * Syntax: + * void MoxaPortTxDisable(int port); + * int port : port number (0 - 127) + * + * + * Function 29: Enable port data transmission. + * Syntax: + * void MoxaPortTxEnable(int port); + * int port : port number (0 - 127) + * + * + * Function 30: Get the received BREAK signal count. + * Syntax: + * int MoxaPortGetBrkCnt(int port); + * int port : port number (0 - 127) + * + * return: 0 - .. : BREAK signal count + * + * + * Function 31: Get the received BREAK signal count and reset it. + * Syntax: + * int MoxaPortResetBrkCnt(int port); + * int port : port number (0 - 127) + * + * return: 0 - .. : BREAK signal count + * + * + * Function 32: Set the S/W flow control new XON/XOFF value, default + * XON is 0x11 & XOFF is 0x13. + * Syntax: + * void MoxaPortSetXonXoff(int port, int xonValue, int xoffValue); + * int port : port number (0 - 127) + * int xonValue : new XON value (0 - 255) + * int xoffValue : new XOFF value (0 - 255) + * + * + * Function 33: Check this port's transmission is hold by remote site + * because the flow control. + * Syntax: + * int MoxaPortIsTxHold(int port); + * int port : port number (0 - 127) + * + * return: 0 : normal + * 1 : hold by remote site + * + * + * Function 34: Send out a BREAK signal. + * Syntax: + * void MoxaPortSendBreak(int port, int ms100); + * int port : port number (0 - 127) + * int ms100 : break signal time interval. + * unit: 100 mini-second. if ms100 == 0, it will + * send out a about 250 ms BREAK signal. + * + */ +int MoxaPortIsValid(int port) +{ + + if (moxaCard == 0) + return (0); + if (moxaChkPort[port] == 0) + return (0); + return (1); +} + +void MoxaPortEnable(int port) +{ + unsigned long ofsAddr; + int MoxaPortLineStatus(int); + short lowwater = 512; + + ofsAddr = moxaTableAddr[port]; + writew(lowwater, ofsAddr + Low_water); + moxaBreakCnt[port] = 0; + if ((moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA) || + (moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI)) { + moxafunc(ofsAddr, FC_SetBreakIrq, 0); + } else { + writew(readw(ofsAddr + HostStat) | WakeupBreak, ofsAddr + HostStat); + } + + moxafunc(ofsAddr, FC_SetLineIrq, Magic_code); + moxafunc(ofsAddr, FC_FlushQueue, 2); + + moxafunc(ofsAddr, FC_EnableCH, Magic_code); + MoxaPortLineStatus(port); +} + +void MoxaPortDisable(int port) +{ + unsigned long ofsAddr; + + ofsAddr = moxaTableAddr[port]; + moxafunc(ofsAddr, FC_SetFlowCtl, 0); /* disable flow control */ + moxafunc(ofsAddr, FC_ClrLineIrq, Magic_code); + writew(0, ofsAddr + HostStat); + moxafunc(ofsAddr, FC_DisableCH, Magic_code); +} + +long MoxaPortGetMaxBaud(int port) +{ + if ((moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA) || + (moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI)) + return (460800L); + else + return (921600L); +} + + +long MoxaPortSetBaud(int port, long baud) +{ + unsigned long ofsAddr; + long max, clock; + unsigned int val; + + if ((baud < 50L) || ((max = MoxaPortGetMaxBaud(port)) == 0)) + return (0); + ofsAddr = moxaTableAddr[port]; + if (baud > max) + baud = max; + if (max == 38400L) + clock = 614400L; /* for 9.8304 Mhz : max. 38400 bps */ + else if (max == 57600L) + clock = 691200L; /* for 11.0592 Mhz : max. 57600 bps */ + else + clock = 921600L; /* for 14.7456 Mhz : max. 115200 bps */ + val = clock / baud; + moxafunc(ofsAddr, FC_SetBaud, val); + baud = clock / val; + moxaCurBaud[port] = baud; + return (baud); +} + +int MoxaPortSetTermio(int port, struct termios *termio) +{ + unsigned long ofsAddr; + tcflag_t cflag; + long baud; + tcflag_t mode = 0; + + if (moxaChkPort[port] == 0 || termio == 0) + return (-1); + ofsAddr = moxaTableAddr[port]; + cflag = termio->c_cflag; /* termio->c_cflag */ + + mode = termio->c_cflag & CSIZE; + if (mode == CS5) + mode = MX_CS5; + else if (mode == CS6) + mode = MX_CS6; + else if (mode == CS7) + mode = MX_CS7; + else if (mode == CS8) + mode = MX_CS8; + + if (termio->c_cflag & CSTOPB) { + if (mode == MX_CS5) + mode |= MX_STOP15; + else + mode |= MX_STOP2; + } else + mode |= MX_STOP1; + + if (termio->c_cflag & PARENB) { + if (termio->c_cflag & PARODD) + mode |= MX_PARODD; + else + mode |= MX_PAREVEN; + } else + mode |= MX_PARNONE; + + moxafunc(ofsAddr, FC_SetDataMode, (ushort) mode); + + cflag &= (CBAUD | CBAUDEX); +#ifndef B921600 +#define B921600 (B460800+1) +#endif + switch (cflag) { + case B921600: + baud = 921600L; + break; + case B460800: + baud = 460800L; + break; + case B230400: + baud = 230400L; + break; + case B115200: + baud = 115200L; + break; + case B57600: + baud = 57600L; + break; + case B38400: + baud = 38400L; + break; + case B19200: + baud = 19200L; + break; + case B9600: + baud = 9600L; + break; + case B4800: + baud = 4800L; + break; + case B2400: + baud = 2400L; + break; + case B1800: + baud = 1800L; + break; + case B1200: + baud = 1200L; + break; + case B600: + baud = 600L; + break; + case B300: + baud = 300L; + break; + case B200: + baud = 200L; + break; + case B150: + baud = 150L; + break; + case B134: + baud = 134L; + break; + case B110: + baud = 110L; + break; + case B75: + baud = 75L; + break; + case B50: + baud = 50L; + break; + default: + baud = 0; + } + if ((moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA) || + (moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI)) { + if (baud == 921600L) + return (-1); + } + MoxaPortSetBaud(port, baud); + + if (termio->c_iflag & (IXON | IXOFF | IXANY)) { + writeb(termio->c_cc[VSTART], ofsAddr + FuncArg); + writeb(termio->c_cc[VSTOP], ofsAddr + FuncArg1); + writeb(FC_SetXonXoff, ofsAddr + FuncCode); + wait_finish(ofsAddr); + + } + return (0); +} + +int MoxaPortGetLineOut(int port, int *dtrState, int *rtsState) +{ + + if (!MoxaPortIsValid(port)) + return (-1); + if (dtrState) { + if (moxaLineCtrl[port] & DTR_ON) + *dtrState = 1; + else + *dtrState = 0; + } + if (rtsState) { + if (moxaLineCtrl[port] & RTS_ON) + *rtsState = 1; + else + *rtsState = 0; + } + return (0); +} + +void MoxaPortLineCtrl(int port, int dtr, int rts) +{ + unsigned long ofsAddr; + int mode; + + ofsAddr = moxaTableAddr[port]; + mode = 0; + if (dtr) + mode |= DTR_ON; + if (rts) + mode |= RTS_ON; + moxaLineCtrl[port] = mode; + moxafunc(ofsAddr, FC_LineControl, mode); +} + +void MoxaPortFlowCtrl(int port, int rts, int cts, int txflow, int rxflow, int txany) +{ + unsigned long ofsAddr; + int mode; + + ofsAddr = moxaTableAddr[port]; + mode = 0; + if (rts) + mode |= RTS_FlowCtl; + if (cts) + mode |= CTS_FlowCtl; + if (txflow) + mode |= Tx_FlowCtl; + if (rxflow) + mode |= Rx_FlowCtl; + if (txany) + mode |= IXM_IXANY; + moxafunc(ofsAddr, FC_SetFlowCtl, mode); +} + +int MoxaPortLineStatus(int port) +{ + unsigned long ofsAddr; + int val; + + ofsAddr = moxaTableAddr[port]; + if ((moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA) || + (moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI)) { + moxafunc(ofsAddr, FC_LineStatus, 0); + val = readw(ofsAddr + FuncArg); + } else { + val = readw(ofsAddr + FlagStat) >> 4; + } + val &= 0x0B; + if (val & 8) { + val |= 4; + if ((moxaDCDState[port] & DCD_oldstate) == 0) + moxaDCDState[port] = (DCD_oldstate | DCD_changed); + } else { + if (moxaDCDState[port] & DCD_oldstate) + moxaDCDState[port] = DCD_changed; + } + val &= 7; + return (val); +} + +int MoxaPortDCDChange(int port) +{ + int n; + + if (moxaChkPort[port] == 0) + return (0); + n = moxaDCDState[port]; + moxaDCDState[port] &= ~DCD_changed; + n &= DCD_changed; + return (n); +} + +int MoxaPortDCDON(int port) +{ + int n; + + if (moxaChkPort[port] == 0) + return (0); + if (moxaDCDState[port] & DCD_oldstate) + n = 1; + else + n = 0; + return (n); +} + + +/* + int MoxaDumpMem(int port, unsigned char * buffer, int len) + { + int i; + unsigned long baseAddr,ofsAddr,ofs; + + baseAddr = moxaBaseAddr[port / MAX_PORTS_PER_BOARD]; + ofs = baseAddr + DynPage_addr + pageofs; + if (len > 0x2000L) + len = 0x2000L; + for (i = 0; i < len; i++) + buffer[i] = readb(ofs+i); + } + */ + + +int MoxaPortWriteData(int port, unsigned char * buffer, int len) +{ + int c, total, i; + ushort tail; + int cnt; + ushort head, tx_mask, spage, epage; + ushort pageno, pageofs, bufhead; + unsigned long baseAddr, ofsAddr, ofs; + + ofsAddr = moxaTableAddr[port]; + baseAddr = moxaBaseAddr[port / MAX_PORTS_PER_BOARD]; + tx_mask = readw(ofsAddr + TX_mask); + spage = readw(ofsAddr + Page_txb); + epage = readw(ofsAddr + EndPage_txb); + tail = readw(ofsAddr + TXwptr); + head = readw(ofsAddr + TXrptr); + c = (head > tail) ? (head - tail - 1) + : (head - tail + tx_mask); + if (c > len) + c = len; + moxaLog.txcnt[port] += c; + total = c; + if (spage == epage) { + bufhead = readw(ofsAddr + Ofs_txb); + writew(spage, baseAddr + Control_reg); + while (c > 0) { + if (head > tail) + len = head - tail - 1; + else + len = tx_mask + 1 - tail; + len = (c > len) ? len : c; + ofs = baseAddr + DynPage_addr + bufhead + tail; + for (i = 0; i < len; i++) + writeb(*buffer++, ofs + i); + tail = (tail + len) & tx_mask; + c -= len; + } + writew(tail, ofsAddr + TXwptr); + } else { + len = c; + pageno = spage + (tail >> 13); + pageofs = tail & Page_mask; + do { + cnt = Page_size - pageofs; + if (cnt > c) + cnt = c; + c -= cnt; + writeb(pageno, baseAddr + Control_reg); + ofs = baseAddr + DynPage_addr + pageofs; + for (i = 0; i < cnt; i++) + writeb(*buffer++, ofs + i); + if (c == 0) { + writew((tail + len) & tx_mask, ofsAddr + TXwptr); + break; + } + if (++pageno == epage) + pageno = spage; + pageofs = 0; + } while (1); + } + writeb(1, ofsAddr + CD180TXirq); /* start to send */ + return (total); +} + +int MoxaPortReadData(int port, unsigned char * buffer, int space) +{ + register ushort head, pageofs; + int i, count, cnt, len, total, remain; + ushort tail, rx_mask, spage, epage; + ushort pageno, bufhead; + unsigned long baseAddr, ofsAddr, ofs; + + ofsAddr = moxaTableAddr[port]; + baseAddr = moxaBaseAddr[port / MAX_PORTS_PER_BOARD]; + head = readw(ofsAddr + RXrptr); + tail = readw(ofsAddr + RXwptr); + rx_mask = readw(ofsAddr + RX_mask); + spage = readw(ofsAddr + Page_rxb); + epage = readw(ofsAddr + EndPage_rxb); + count = (tail >= head) ? (tail - head) + : (tail - head + rx_mask + 1); + if (count == 0) + return (0); + + total = (space > count) ? count : space; + remain = count - total; + moxaLog.rxcnt[port] += total; + count = total; + if (spage == epage) { + bufhead = readw(ofsAddr + Ofs_rxb); + writew(spage, baseAddr + Control_reg); + while (count > 0) { + if (tail >= head) + len = tail - head; + else + len = rx_mask + 1 - head; + len = (count > len) ? len : count; + ofs = baseAddr + DynPage_addr + bufhead + head; + for (i = 0; i < len; i++) + *buffer++ = readb(ofs + i); + head = (head + len) & rx_mask; + count -= len; + } + writew(head, ofsAddr + RXrptr); + } else { + len = count; + pageno = spage + (head >> 13); + pageofs = head & Page_mask; + do { + cnt = Page_size - pageofs; + if (cnt > count) + cnt = count; + count -= cnt; + writew(pageno, baseAddr + Control_reg); + ofs = baseAddr + DynPage_addr + pageofs; + for (i = 0; i < cnt; i++) + *buffer++ = readb(ofs + i); + if (count == 0) { + writew((head + len) & rx_mask, ofsAddr + RXrptr); + break; + } + if (++pageno == epage) + pageno = spage; + pageofs = 0; + } while (1); + } + if ((readb(ofsAddr + FlagStat) & Xoff_state) && (remain < LowWater)) { + moxaLowWaterChk = 1; + moxaLowChkFlag[port] = 1; + } + return (total); +} + + +int MoxaPortTxQueue(int port) +{ + unsigned long ofsAddr; + ushort rptr, wptr, mask; + int len; + + ofsAddr = moxaTableAddr[port]; + rptr = readw(ofsAddr + TXrptr); + wptr = readw(ofsAddr + TXwptr); + mask = readw(ofsAddr + TX_mask); + len = (wptr - rptr) & mask; + return (len); +} + +int MoxaPortTxFree(int port) +{ + unsigned long ofsAddr; + ushort rptr, wptr, mask; + int len; + + ofsAddr = moxaTableAddr[port]; + rptr = readw(ofsAddr + TXrptr); + wptr = readw(ofsAddr + TXwptr); + mask = readw(ofsAddr + TX_mask); + len = mask - ((wptr - rptr) & mask); + return (len); +} + +int MoxaPortRxQueue(int port) +{ + unsigned long ofsAddr; + ushort rptr, wptr, mask; + int len; + + ofsAddr = moxaTableAddr[port]; + rptr = readw(ofsAddr + RXrptr); + wptr = readw(ofsAddr + RXwptr); + mask = readw(ofsAddr + RX_mask); + len = (wptr - rptr) & mask; + return (len); +} + + +void MoxaPortTxDisable(int port) +{ + unsigned long ofsAddr; + + ofsAddr = moxaTableAddr[port]; + moxafunc(ofsAddr, FC_SetXoffState, Magic_code); +} + +void MoxaPortTxEnable(int port) +{ + unsigned long ofsAddr; + + ofsAddr = moxaTableAddr[port]; + moxafunc(ofsAddr, FC_SetXonState, Magic_code); +} + + +int MoxaPortResetBrkCnt(int port) +{ + ushort cnt; + cnt = moxaBreakCnt[port]; + moxaBreakCnt[port] = 0; + return (cnt); +} + + +void MoxaPortSendBreak(int port, int ms100) +{ + unsigned long ofsAddr; + + ofsAddr = moxaTableAddr[port]; + if (ms100) { + moxafunc(ofsAddr, FC_SendBreak, Magic_code); + moxadelay(ms100 * (HZ / 10)); + } else { + moxafunc(ofsAddr, FC_SendBreak, Magic_code); + moxadelay(HZ / 4); /* 250 ms */ + } + moxafunc(ofsAddr, FC_StopBreak, Magic_code); +} + +static int moxa_get_serial_info(struct moxa_str *info, + struct serial_struct *retinfo) +{ + struct serial_struct tmp; + + if (!retinfo) + return (-EFAULT); + memset(&tmp, 0, sizeof(tmp)); + tmp.type = info->type; + tmp.line = info->port; + tmp.port = 0; + tmp.irq = 0; + tmp.flags = info->asyncflags; + tmp.baud_base = 921600; + tmp.close_delay = info->close_delay; + tmp.closing_wait = info->closing_wait; + tmp.custom_divisor = 0; + tmp.hub6 = 0; + if(copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return -EFAULT; + return (0); +} + + +static int moxa_set_serial_info(struct moxa_str *info, + struct serial_struct *new_info) +{ + struct serial_struct new_serial; + + if(copy_from_user(&new_serial, new_info, sizeof(new_serial))) + return -EFAULT; + + if ((new_serial.irq != 0) || + (new_serial.port != 0) || +// (new_serial.type != info->type) || + (new_serial.custom_divisor != 0) || + (new_serial.baud_base != 921600)) + return (-EPERM); + + if (!suser()) { + if (((new_serial.flags & ~ASYNC_USR_MASK) != + (info->asyncflags & ~ASYNC_USR_MASK))) + return (-EPERM); + } else { + info->close_delay = new_serial.close_delay * HZ / 100; + info->closing_wait = new_serial.closing_wait * HZ / 100; + } + + new_serial.flags = (new_serial.flags & ~ASYNC_FLAGS); + new_serial.flags |= (info->asyncflags & ASYNC_FLAGS); + + if (new_serial.type == PORT_16550A) { + MoxaSetFifo(info->port, 1); + } else { + MoxaSetFifo(info->port, 0); + } + + info->type = new_serial.type; + return (0); +} + + + +/***************************************************************************** + * Static local functions: * + *****************************************************************************/ +/* + * moxadelay - delays a specified number ticks + */ +static void moxadelay(int tick) +{ + unsigned long st, et; + + st = jiffies; + et = st + tick; + while (jiffies < et); +} + +static void moxafunc(unsigned long ofsAddr, int cmd, ushort arg) +{ + + writew(arg, ofsAddr + FuncArg); + writew(cmd, ofsAddr + FuncCode); + wait_finish(ofsAddr); +} + +static void wait_finish(unsigned long ofsAddr) +{ + unsigned long i, j; + + i = jiffies; + while (readw(ofsAddr + FuncCode) != 0) { + j = jiffies; + if ((j - i) > moxaFuncTout) { + return; + } + } +} + +static void low_water_check(unsigned long ofsAddr) +{ + int len; + ushort rptr, wptr, mask; + + if (readb(ofsAddr + FlagStat) & Xoff_state) { + rptr = readw(ofsAddr + RXrptr); + wptr = readw(ofsAddr + RXwptr); + mask = readw(ofsAddr + RX_mask); + len = (wptr - rptr) & mask; + if (len <= Low_water) + moxafunc(ofsAddr, FC_SendXon, 0); + } +} + +static int moxaloadbios(int cardno, unsigned char *tmp, int len) +{ + unsigned long baseAddr; + int i; + + if(copy_from_user(moxaBuff, tmp, len)) + return -EFAULT; + baseAddr = moxaBaseAddr[cardno]; + writeb(HW_reset, baseAddr + Control_reg); /* reset */ + moxadelay(1); /* delay 10 ms */ + for (i = 0; i < 4096; i++) + writeb(0, baseAddr + i); /* clear fix page */ + for (i = 0; i < len; i++) + writeb(moxaBuff[i], baseAddr + i); /* download BIOS */ + writeb(0, baseAddr + Control_reg); /* restart */ + return (0); +} + +static int moxafindcard(int cardno) +{ + unsigned long baseAddr; + ushort tmp; + + baseAddr = moxaBaseAddr[cardno]; + switch (moxa_boards[cardno].boardType) { + case MOXA_BOARD_C218_ISA: + case MOXA_BOARD_C218_PCI: + if ((tmp = readw(baseAddr + C218_key)) != C218_KeyCode) { + return (-1); + } + break; + case MOXA_BOARD_CP204J: + if ((tmp = readw(baseAddr + C218_key)) != CP204J_KeyCode) { + return (-1); + } + break; + default: + if ((tmp = readw(baseAddr + C320_key)) != C320_KeyCode) { + return (-1); + } + if ((tmp = readw(baseAddr + C320_status)) != STS_init) { + return (-2); + } + } + return (0); +} + +static int moxaload320b(int cardno, unsigned char * tmp, int len) +{ + unsigned long baseAddr; + int i; + + if(copy_from_user(moxaBuff, tmp, len)) + return -EFAULT; + baseAddr = moxaBaseAddr[cardno]; + writew(len - 7168 - 2, baseAddr + C320bapi_len); + writeb(1, baseAddr + Control_reg); /* Select Page 1 */ + for (i = 0; i < 7168; i++) + writeb(moxaBuff[i], baseAddr + DynPage_addr + i); + writeb(2, baseAddr + Control_reg); /* Select Page 2 */ + for (i = 0; i < (len - 7168); i++) + writeb(moxaBuff[i + 7168], baseAddr + DynPage_addr + i); + return (0); +} + +static int moxaloadcode(int cardno, unsigned char * tmp, int len) +{ + unsigned long baseAddr, ofsAddr; + int retval, port, i; + + if(copy_from_user(moxaBuff, tmp, len)) + return -EFAULT; + baseAddr = moxaBaseAddr[cardno]; + switch (moxa_boards[cardno].boardType) { + case MOXA_BOARD_C218_ISA: + case MOXA_BOARD_C218_PCI: + case MOXA_BOARD_CP204J: + retval = moxaloadc218(cardno, baseAddr, len); + if (retval) + return (retval); + port = cardno * MAX_PORTS_PER_BOARD; + for (i = 0; i < moxa_boards[cardno].numPorts; i++, port++) { + moxaChkPort[port] = 1; + moxaCurBaud[port] = 9600L; + moxaDCDState[port] = 0; + moxaTableAddr[port] = baseAddr + Extern_table + Extern_size * i; + ofsAddr = moxaTableAddr[port]; + writew(C218rx_mask, ofsAddr + RX_mask); + writew(C218tx_mask, ofsAddr + TX_mask); + writew(C218rx_spage + i * C218buf_pageno, ofsAddr + Page_rxb); + writew(readw(ofsAddr + Page_rxb) + C218rx_pageno, ofsAddr + EndPage_rxb); + + writew(C218tx_spage + i * C218buf_pageno, ofsAddr + Page_txb); + writew(readw(ofsAddr + Page_txb) + C218tx_pageno, ofsAddr + EndPage_txb); + + } + break; + default: + retval = moxaloadc320(cardno, baseAddr, len, + &moxa_boards[cardno].numPorts); + if (retval) + return (retval); + port = cardno * MAX_PORTS_PER_BOARD; + for (i = 0; i < moxa_boards[cardno].numPorts; i++, port++) { + moxaChkPort[port] = 1; + moxaCurBaud[port] = 9600L; + moxaDCDState[port] = 0; + moxaTableAddr[port] = baseAddr + Extern_table + Extern_size * i; + ofsAddr = moxaTableAddr[port]; + if (moxa_boards[cardno].numPorts == 8) { + writew(C320p8rx_mask, ofsAddr + RX_mask); + writew(C320p8tx_mask, ofsAddr + TX_mask); + writew(C320p8rx_spage + i * C320p8buf_pgno, ofsAddr + Page_rxb); + writew(readw(ofsAddr + Page_rxb) + C320p8rx_pgno, ofsAddr + EndPage_rxb); + writew(C320p8tx_spage + i * C320p8buf_pgno, ofsAddr + Page_txb); + writew(readw(ofsAddr + Page_txb) + C320p8tx_pgno, ofsAddr + EndPage_txb); + + } else if (moxa_boards[cardno].numPorts == 16) { + writew(C320p16rx_mask, ofsAddr + RX_mask); + writew(C320p16tx_mask, ofsAddr + TX_mask); + writew(C320p16rx_spage + i * C320p16buf_pgno, ofsAddr + Page_rxb); + writew(readw(ofsAddr + Page_rxb) + C320p16rx_pgno, ofsAddr + EndPage_rxb); + writew(C320p16tx_spage + i * C320p16buf_pgno, ofsAddr + Page_txb); + writew(readw(ofsAddr + Page_txb) + C320p16tx_pgno, ofsAddr + EndPage_txb); + + } else if (moxa_boards[cardno].numPorts == 24) { + writew(C320p24rx_mask, ofsAddr + RX_mask); + writew(C320p24tx_mask, ofsAddr + TX_mask); + writew(C320p24rx_spage + i * C320p24buf_pgno, ofsAddr + Page_rxb); + writew(readw(ofsAddr + Page_rxb) + C320p24rx_pgno, ofsAddr + EndPage_rxb); + writew(C320p24tx_spage + i * C320p24buf_pgno, ofsAddr + Page_txb); + writew(readw(ofsAddr + Page_txb), ofsAddr + EndPage_txb); + } else if (moxa_boards[cardno].numPorts == 32) { + writew(C320p32rx_mask, ofsAddr + RX_mask); + writew(C320p32tx_mask, ofsAddr + TX_mask); + writew(C320p32tx_ofs, ofsAddr + Ofs_txb); + writew(C320p32rx_spage + i * C320p32buf_pgno, ofsAddr + Page_rxb); + writew(readb(ofsAddr + Page_rxb), ofsAddr + EndPage_rxb); + writew(C320p32tx_spage + i * C320p32buf_pgno, ofsAddr + Page_txb); + writew(readw(ofsAddr + Page_txb), ofsAddr + EndPage_txb); + } + } + break; + } + return (0); +} + +static int moxaloadc218(int cardno, unsigned long baseAddr, int len) +{ + char retry; + int i, j, len1, len2; + ushort usum, *ptr, keycode; + + if (moxa_boards[cardno].boardType == MOXA_BOARD_CP204J) + keycode = CP204J_KeyCode; + else + keycode = C218_KeyCode; + usum = 0; + len1 = len >> 1; + ptr = (ushort *) moxaBuff; + for (i = 0; i < len1; i++) + usum += *(ptr + i); + retry = 0; + do { + len1 = len >> 1; + j = 0; + while (len1) { + len2 = (len1 > 2048) ? 2048 : len1; + len1 -= len2; + for (i = 0; i < len2 << 1; i++) + writeb(moxaBuff[i + j], baseAddr + C218_LoadBuf + i); + j += i; + + writew(len2, baseAddr + C218DLoad_len); + writew(0, baseAddr + C218_key); + for (i = 0; i < 100; i++) { + if (readw(baseAddr + C218_key) == keycode) + break; + moxadelay(1); /* delay 10 ms */ + } + if (readw(baseAddr + C218_key) != keycode) { + return (-1); + } + } + writew(0, baseAddr + C218DLoad_len); + writew(usum, baseAddr + C218check_sum); + writew(0, baseAddr + C218_key); + for (i = 0; i < 100; i++) { + if (readw(baseAddr + C218_key) == keycode) + break; + moxadelay(1); /* delay 10 ms */ + } + retry++; + } while ((readb(baseAddr + C218chksum_ok) != 1) && (retry < 3)); + if (readb(baseAddr + C218chksum_ok) != 1) { + return (-1); + } + writew(0, baseAddr + C218_key); + for (i = 0; i < 100; i++) { + if (readw(baseAddr + Magic_no) == Magic_code) + break; + moxadelay(1); /* delay 10 ms */ + } + if (readw(baseAddr + Magic_no) != Magic_code) { + return (-1); + } + writew(1, baseAddr + Disable_IRQ); + writew(0, baseAddr + Magic_no); + for (i = 0; i < 100; i++) { + if (readw(baseAddr + Magic_no) == Magic_code) + break; + moxadelay(1); /* delay 10 ms */ + } + if (readw(baseAddr + Magic_no) != Magic_code) { + return (-1); + } + moxaCard = 1; + moxaIntNdx[cardno] = baseAddr + IRQindex; + moxaIntPend[cardno] = baseAddr + IRQpending; + moxaIntTable[cardno] = baseAddr + IRQtable; + return (0); +} + +static int moxaloadc320(int cardno, unsigned long baseAddr, int len, int *numPorts) +{ + ushort usum; + int i, j, wlen, len2, retry; + ushort *uptr; + + usum = 0; + wlen = len >> 1; + uptr = (ushort *) moxaBuff; + for (i = 0; i < wlen; i++) + usum += uptr[i]; + retry = 0; + j = 0; + do { + while (wlen) { + if (wlen > 2048) + len2 = 2048; + else + len2 = wlen; + wlen -= len2; + len2 <<= 1; + for (i = 0; i < len2; i++) + writeb(moxaBuff[j + i], baseAddr + C320_LoadBuf + i); + len2 >>= 1; + j += i; + writew(len2, baseAddr + C320DLoad_len); + writew(0, baseAddr + C320_key); + for (i = 0; i < 10; i++) { + if (readw(baseAddr + C320_key) == C320_KeyCode) + break; + moxadelay(1); + } + if (readw(baseAddr + C320_key) != C320_KeyCode) + return (-1); + } + writew(0, baseAddr + C320DLoad_len); + writew(usum, baseAddr + C320check_sum); + writew(0, baseAddr + C320_key); + for (i = 0; i < 10; i++) { + if (readw(baseAddr + C320_key) == C320_KeyCode) + break; + moxadelay(1); + } + retry++; + } while ((readb(baseAddr + C320chksum_ok) != 1) && (retry < 3)); + if (readb(baseAddr + C320chksum_ok) != 1) + return (-1); + writew(0, baseAddr + C320_key); + for (i = 0; i < 600; i++) { + if (readw(baseAddr + Magic_no) == Magic_code) + break; + moxadelay(1); + } + if (readw(baseAddr + Magic_no) != Magic_code) + return (-100); + + if (moxa_boards[cardno].busType == MOXA_BUS_TYPE_PCI) { /* ASIC board */ + writew(0x3800, baseAddr + TMS320_PORT1); + writew(0x3900, baseAddr + TMS320_PORT2); + writew(28499, baseAddr + TMS320_CLOCK); + } else { + writew(0x3200, baseAddr + TMS320_PORT1); + writew(0x3400, baseAddr + TMS320_PORT2); + writew(19999, baseAddr + TMS320_CLOCK); + } + writew(1, baseAddr + Disable_IRQ); + writew(0, baseAddr + Magic_no); + for (i = 0; i < 500; i++) { + if (readw(baseAddr + Magic_no) == Magic_code) + break; + moxadelay(1); + } + if (readw(baseAddr + Magic_no) != Magic_code) + return (-102); + + j = readw(baseAddr + Module_cnt); + if (j <= 0) + return (-101); + *numPorts = j * 8; + writew(j, baseAddr + Module_no); + writew(0, baseAddr + Magic_no); + for (i = 0; i < 600; i++) { + if (readw(baseAddr + Magic_no) == Magic_code) + break; + moxadelay(1); + } + if (readw(baseAddr + Magic_no) != Magic_code) + return (-102); + moxaCard = 1; + moxaIntNdx[cardno] = baseAddr + IRQindex; + moxaIntPend[cardno] = baseAddr + IRQpending; + moxaIntTable[cardno] = baseAddr + IRQtable; + return (0); +} + +long MoxaPortGetCurBaud(int port) +{ + + if (moxaChkPort[port] == 0) + return (0); + return (moxaCurBaud[port]); +} + +static void MoxaSetFifo(int port, int enable) +{ + unsigned long ofsAddr = moxaTableAddr[port]; + + if (!enable) { + moxafunc(ofsAddr, FC_SetRxFIFOTrig, 0); + moxafunc(ofsAddr, FC_SetTxFIFOCnt, 1); + } else { + moxafunc(ofsAddr, FC_SetRxFIFOTrig, 3); + moxafunc(ofsAddr, FC_SetTxFIFOCnt, 16); + } +} + +#if 0 +int MoxaPortSetMode(int port, int databits, int stopbits, int parity) +{ + unsigned long ofsAddr; + int val; + + val = 0; + switch (databits) { + case 5: + val |= 0; + break; + case 6: + val |= 1; + break; + case 7: + val |= 2; + break; + case 8: + val |= 3; + break; + default: + return (-1); + } + switch (stopbits) { + case 0: + val |= 0; + break; /* stop bits 1.5 */ + case 1: + val |= 0; + break; + case 2: + val |= 4; + break; + default: + return (-1); + } + switch (parity) { + case 0: + val |= 0x00; + break; /* None */ + case 1: + val |= 0x08; + break; /* Odd */ + case 2: + val |= 0x18; + break; /* Even */ + case 3: + val |= 0x28; + break; /* Mark */ + case 4: + val |= 0x38; + break; /* Space */ + default: + return (-1); + } + ofsAddr = moxaTableAddr[port]; + moxafunc(ofsAddr, FC_SetMode, val); + return (0); +} + +int MoxaPortTxBufSize(int port) +{ + unsigned long ofsAddr; + int size; + + ofsAddr = moxaTableAddr[port]; + size = readw(ofsAddr + TX_mask); + return (size); +} + +int MoxaPortRxBufSize(int port) +{ + unsigned long ofsAddr; + int size; + + ofsAddr = moxaTableAddr[port]; + size = readw(ofsAddr + RX_mask); + return (size); +} + +int MoxaPortRxFree(int port) +{ + unsigned long ofsAddr; + ushort rptr, wptr, mask; + int len; + + ofsAddr = moxaTableAddr[port]; + rptr = readw(ofsAddr + RXrptr); + wptr = readw(ofsAddr + RXwptr); + mask = readw(ofsAddr + RX_mask); + len = mask - ((wptr - rptr) & mask); + return (len); +} +int MoxaPortGetBrkCnt(int port) +{ + return (moxaBreakCnt[port]); +} + +void MoxaPortSetXonXoff(int port, int xonValue, int xoffValue) +{ + unsigned long ofsAddr; + + ofsAddr = moxaTableAddr[port]; + writew(xonValue, ofsAddr + FuncArg); + writew(xoffValue, ofsAddr + FuncArg1); + writew(FC_SetXonXoff, ofsAddr + FuncCode); + wait_finish(ofsAddr); +} + +int MoxaPortIsTxHold(int port) +{ + unsigned long ofsAddr; + int val; + + ofsAddr = moxaTableAddr[port]; + if ((moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA) || + (moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI)) { + moxafunc(ofsAddr, FC_GetCCSR, 0); + val = readw(ofsAddr + FuncArg); + if (val & 0x04) + return (1); + } else { + if (readw(ofsAddr + FlagStat) & Tx_flowOff) + return (1); + } + return (0); +} +#endif diff -u --recursive --new-file v2.2.13/linux/drivers/char/msbusmouse.c linux/drivers/char/msbusmouse.c --- v2.2.13/linux/drivers/char/msbusmouse.c Wed Dec 16 13:39:11 1998 +++ linux/drivers/char/msbusmouse.c Tue Jan 4 10:12:14 2000 @@ -157,7 +157,8 @@ put_user(0x00, buffer + i); mouse.dx -= dx; mouse.dy += dy; - mouse.ready = 0; + if(mouse.dx == 0 && mouse.dy == 0) + mouse.ready = 0; return i; } diff -u --recursive --new-file v2.2.13/linux/drivers/char/mxser.c linux/drivers/char/mxser.c --- v2.2.13/linux/drivers/char/mxser.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/mxser.c Tue Jan 4 10:12:14 2000 @@ -0,0 +1,2449 @@ +/*****************************************************************************/ +/* + * mxser.c -- MOXA Smartio family multiport serial driver. + * + * Copyright (C) 1999-2000 Moxa Technologies (support@moxa.com.tw). + * + * This code is loosely based on the Linux serial driver, written by + * Linus Torvalds, Theodore T'so and others. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * MOXA Smartio Family Serial Driver + * + * Copyright (C) 1999,2000 Moxa Technologies Co., LTD. + * + * for : LINUX 2.0.X, 2.2.X + * date : 1999/07/22 + * version : 1.1 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define MXSER_VERSION "1.1kern" + +#define MXSERMAJOR 174 +#define MXSERCUMAJOR 175 + + +#define MXSER_EVENT_TXLOW 1 +#define MXSER_EVENT_HANGUP 2 + + +#define SERIAL_DO_RESTART + +#define MXSER_BOARDS 4 /* Max. boards */ +#define MXSER_PORTS 32 /* Max. ports */ +#define MXSER_PORTS_PER_BOARD 8 /* Max. ports per board */ +#define MXSER_ISR_PASS_LIMIT 256 + +#define MXSER_ERR_IOADDR -1 +#define MXSER_ERR_IRQ -2 +#define MXSER_ERR_IRQ_CONFLIT -3 +#define MXSER_ERR_VECTOR -4 + +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 + +#define WAKEUP_CHARS 256 + +#define UART_MCR_AFE 0x20 +#define UART_LSR_SPECIAL 0x1E + +#define PORTNO(x) (MINOR((x)->device) - (x)->driver.minor_start) + +#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + +#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT) + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +/* + * Define the Moxa PCI vendor and device IDs. + */ + +#ifndef PCI_VENDOR_ID_MOXA +#define PCI_VENDOR_ID_MOXA 0x1393 +#endif +#ifndef PCI_DEVICE_ID_C168 +#define PCI_DEVICE_ID_C168 0x1680 +#endif +#ifndef PCI_DEVICE_ID_C104 +#define PCI_DEVICE_ID_C104 0x1040 +#endif + +#define C168_ASIC_ID 1 +#define C104_ASIC_ID 2 +#define CI104J_ASIC_ID 5 + +enum { + MXSER_BOARD_C168_ISA = 1, + MXSER_BOARD_C104_ISA, + MXSER_BOARD_CI104J, + MXSER_BOARD_C168_PCI, + MXSER_BOARD_C104_PCI, +}; + +static char *mxser_brdname[] = +{ + "C168 series", + "C104 series", + "CI-104J series", + "C168H/PCI series", + "C104H/PCI series", +}; + +static int mxser_numports[] = +{ + 8, + 4, + 4, + 8, + 4, +}; + +/* + * MOXA ioctls + */ +#define MOXA 0x400 +#define MOXA_GETDATACOUNT (MOXA + 23) +#define MOXA_GET_CONF (MOXA + 35) +#define MOXA_DIAGNOSE (MOXA + 50) +#define MOXA_CHKPORTENABLE (MOXA + 60) +#define MOXA_HighSpeedOn (MOXA + 61) +#define MOXA_GET_MAJOR (MOXA + 63) +#define MOXA_GET_CUMAJOR (MOXA + 64) +#define MOXA_GETMSTATUS (MOXA + 65) + +typedef struct { + unsigned short vendor_id; + unsigned short device_id; + unsigned short board_type; +} mxser_pciinfo; + +static mxser_pciinfo mxser_pcibrds[] = +{ + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_C168, MXSER_BOARD_C168_PCI}, + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_C104, MXSER_BOARD_C104_PCI}, +}; + +typedef struct _moxa_pci_info { + unsigned short busNum; + unsigned short devNum; +} moxa_pci_info; + +static int ioaddr[MXSER_BOARDS] = {0, 0, 0, 0}; +static int ttymajor = MXSERMAJOR; +static int calloutmajor = MXSERCUMAJOR; +static int verbose = 0; + +#ifdef MODULE +/* Variables for insmod */ + +MODULE_AUTHOR("William Chen"); +MODULE_DESCRIPTION("MOXA Smartio Family Multiport Board Device Driver"); +MODULE_PARM(ioaddr, "1-4i"); +MODULE_PARM(ttymajor, "i"); +MODULE_PARM(calloutmajor, "i"); +MODULE_PARM(verbose, "i"); + +#endif /* MODULE */ + +struct mxser_hwconf { + int board_type; + int ports; + int irq; + int vector; + int vector_mask; + int uart_type; + int ioaddr[MXSER_PORTS_PER_BOARD]; + int baud_base[MXSER_PORTS_PER_BOARD]; + moxa_pci_info pciInfo; +}; + +struct mxser_struct { + int port; + int base; /* port base address */ + int irq; /* port using irq no. */ + int vector; /* port irq vector */ + int vectormask; /* port vector mask */ + int rx_trigger; /* Rx fifo trigger level */ + int baud_base; /* max. speed */ + int flags; /* defined in tty.h */ + int type; /* UART type */ + struct tty_struct *tty; + int read_status_mask; + int ignore_status_mask; + int xmit_fifo_size; + int custom_divisor; + int x_char; /* xon/xoff character */ + int close_delay; + unsigned short closing_wait; + int IER; /* Interrupt Enable Register */ + int MCR; /* Modem control register */ + unsigned long event; + int count; /* # of fd on device */ + int blocked_open; /* # of blocked opens */ + long session; /* Session of opening process */ + long pgrp; /* pgrp of opening process */ + unsigned char *xmit_buf; + int xmit_head; + int xmit_tail; + int xmit_cnt; + struct tq_struct tqueue; + struct termios normal_termios; + struct termios callout_termios; + struct wait_queue *open_wait; + struct wait_queue *close_wait; + struct wait_queue *delta_msr_wait; + struct async_icount icount; /* kernel counters for the 4 input interrupts */ +}; + +struct mxser_log { + int tick; + int rxcnt[MXSER_PORTS]; + int txcnt[MXSER_PORTS]; +}; + +struct mxser_mstatus { + tcflag_t cflag; + int cts; + int dsr; + int ri; + int dcd; +}; + +static struct mxser_mstatus GMStatus[MXSER_PORTS]; + +static int mxserBoardCAP[MXSER_BOARDS] = +{ + 0, 0, 0, 0 + /* 0x180, 0x280, 0x200, 0x320 */ +}; + + +static struct tty_driver mxvar_sdriver, mxvar_cdriver; +static int mxvar_refcount; +static struct mxser_struct mxvar_table[MXSER_PORTS]; +static struct tty_struct *mxvar_tty[MXSER_PORTS + 1]; +static struct termios *mxvar_termios[MXSER_PORTS + 1]; +static struct termios *mxvar_termios_locked[MXSER_PORTS + 1]; +static struct mxser_log mxvar_log; +static int mxvar_diagflag; +/* + * mxvar_tmp_buf is used as a temporary buffer by serial_write. We need + * to lock it in case the memcpy_fromfs blocks while swapping in a page, + * and some other program tries to do a serial write at the same time. + * Since the lock will only come under contention when the system is + * swapping and available memory is low, it makes sense to share one + * buffer across all the serial ports, since it significantly saves + * memory if large numbers of serial ports are open. + */ +static unsigned char *mxvar_tmp_buf = 0; +static struct semaphore mxvar_tmp_buf_sem = MUTEX; + +/* + * This is used to figure out the divisor speeds and the timeouts + */ +static int mxvar_baud_table[] = +{ + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600, 0}; + +struct mxser_hwconf mxsercfg[MXSER_BOARDS]; + +/* + * static functions: + */ + +#ifdef MODULE +int init_module(void); +void cleanup_module(void); +#endif + +static void mxser_getcfg(int board, struct mxser_hwconf *hwconf); +int mxser_init(void); +static int mxser_get_ISA_conf(int, struct mxser_hwconf *); +static int mxser_get_PCI_conf(int, int, int, struct mxser_hwconf *); +static void mxser_do_softint(void *); +static int mxser_open(struct tty_struct *, struct file *); +static void mxser_close(struct tty_struct *, struct file *); +static int mxser_write(struct tty_struct *, int, const unsigned char *, int); +static int mxser_write_room(struct tty_struct *); +static void mxser_flush_buffer(struct tty_struct *); +static int mxser_chars_in_buffer(struct tty_struct *); +static void mxser_flush_chars(struct tty_struct *); +static void mxser_put_char(struct tty_struct *, unsigned char); +static int mxser_ioctl(struct tty_struct *, struct file *, uint, ulong); +static int mxser_ioctl_special(unsigned int, unsigned long); +static void mxser_throttle(struct tty_struct *); +static void mxser_unthrottle(struct tty_struct *); +static void mxser_set_termios(struct tty_struct *, struct termios *); +static void mxser_stop(struct tty_struct *); +static void mxser_start(struct tty_struct *); +static void mxser_hangup(struct tty_struct *); +static void mxser_interrupt(int, void *, struct pt_regs *); +static inline void mxser_receive_chars(struct mxser_struct *, int *); +static inline void mxser_transmit_chars(struct mxser_struct *); +static inline void mxser_check_modem_status(struct mxser_struct *, int); +static int mxser_block_til_ready(struct tty_struct *, struct file *, struct mxser_struct *); +static int mxser_startup(struct mxser_struct *); +static void mxser_shutdown(struct mxser_struct *); +static int mxser_change_speed(struct mxser_struct *, struct termios *old_termios); +static int mxser_get_serial_info(struct mxser_struct *, struct serial_struct *); +static int mxser_set_serial_info(struct mxser_struct *, struct serial_struct *); +static int mxser_get_lsr_info(struct mxser_struct *, unsigned int *); +static void mxser_send_break(struct mxser_struct *, int); +static int mxser_get_modem_info(struct mxser_struct *, unsigned int *); +static int mxser_set_modem_info(struct mxser_struct *, unsigned int, unsigned int *); + +/* + * The MOXA C168/C104 serial driver boot-time initialization code! + */ + + +#ifdef MODULE +int init_module(void) +{ + int ret; + + if (verbose) + printk("Loading module mxser ...\n"); + ret = mxser_init(); + if (verbose) + printk("Done.\n"); + return (ret); +} + +void cleanup_module(void) +{ + int i, err = 0; + + + if (verbose) + printk("Unloading module mxser ...\n"); + if ((err |= tty_unregister_driver(&mxvar_cdriver))) + printk("Couldn't unregister MOXA Smartio family callout driver\n"); + if ((err |= tty_unregister_driver(&mxvar_sdriver))) + printk("Couldn't unregister MOXA Smartio family serial driver\n"); + + for (i = 0; i < MXSER_BOARDS; i++) { + if (mxsercfg[i].board_type == -1) + continue; + else { + free_irq(mxsercfg[i].irq, &mxvar_table[i * MXSER_PORTS_PER_BOARD]); + } + } + + if (verbose) + printk("Done.\n"); + +} +#endif + + +int mxser_initbrd(int board, struct mxser_hwconf *hwconf) +{ + struct mxser_struct *info; + unsigned long flags; + int retval; + int i, n; + + n = board * MXSER_PORTS_PER_BOARD; + info = &mxvar_table[n]; + for (i = 0; i < hwconf->ports; i++, n++, info++) { + if (verbose) { + printk(" ttyM%d/cum%d at 0x%04x ", n, n, hwconf->ioaddr[i]); + if (hwconf->baud_base[i] == 115200) + printk(" max. baud rate up to 115200 bps.\n"); + else + printk(" max. baud rate up to 921600 bps.\n"); + } + info->port = n; + info->base = hwconf->ioaddr[i]; + info->irq = hwconf->irq; + info->vector = hwconf->vector; + info->vectormask = hwconf->vector_mask; + info->rx_trigger = 14; + info->baud_base = hwconf->baud_base[i]; + info->flags = ASYNC_SHARE_IRQ; + info->type = hwconf->uart_type; + if ((info->type == PORT_16450) || (info->type == PORT_8250)) + info->xmit_fifo_size = 1; + else + info->xmit_fifo_size = 16; + info->custom_divisor = hwconf->baud_base[i] * 16; + info->close_delay = 5 * HZ / 10; + info->closing_wait = 30 * HZ; + info->tqueue.routine = mxser_do_softint; + info->tqueue.data = info; + info->callout_termios = mxvar_cdriver.init_termios; + info->normal_termios = mxvar_sdriver.init_termios; + } + + /* + * Allocate the IRQ if necessary + */ + save_flags(flags); + + n = board * MXSER_PORTS_PER_BOARD; + info = &mxvar_table[n]; + + cli(); + retval = request_irq(hwconf->irq, mxser_interrupt, IRQ_T(info), + "mxser", info); + if (retval) { + restore_flags(flags); + printk("Board %d: %s", board, mxser_brdname[hwconf->board_type - 1]); + printk(" Request irq fail,IRQ (%d) may be conflit with another device.\n", info->irq); + return (retval); + } + restore_flags(flags); + + return 0; +} + + +static void mxser_getcfg(int board, struct mxser_hwconf *hwconf) +{ + mxsercfg[board] = *hwconf; +} + +static int mxser_get_PCI_conf(int busnum, int devnum, int board_type, struct mxser_hwconf *hwconf) +{ + int i; + unsigned int val, ioaddress; + + hwconf->board_type = board_type; + hwconf->ports = mxser_numports[board_type - 1]; + pcibios_read_config_dword(busnum, devnum, PCI_BASE_ADDRESS_2, &val); + if (val == 0xffffffff) + return (MXSER_ERR_IOADDR); + else + ioaddress = val & 0xffffffc; + for (i = 0; i < hwconf->ports; i++) + hwconf->ioaddr[i] = ioaddress + 8 * i; + + pcibios_read_config_dword(busnum, devnum, PCI_BASE_ADDRESS_3, &val); + if (val == 0xffffffff) + return (MXSER_ERR_VECTOR); + else + ioaddress = val & 0xffffffc; + hwconf->vector = ioaddress; + + pcibios_read_config_dword(busnum, devnum, PCI_INTERRUPT_LINE, &val); + if (val == 0xffffffff) + return (MXSER_ERR_IRQ); + else + hwconf->irq = val & 0xff; + + hwconf->uart_type = PORT_16550A; + hwconf->vector_mask = 0; + for (i = 0; i < hwconf->ports; i++) { + hwconf->vector_mask |= (1 << i); + hwconf->baud_base[i] = 921600; + } + return (0); +} + +int mxser_init(void) +{ + int i, m, retval, b; + int n, index; + int ret1, ret2; + unsigned char busnum, devnum; + struct mxser_hwconf hwconf; + + printk("MOXA Smartio family driver version %s\n", MXSER_VERSION); + + /* Initialize the tty_driver structure */ + + memset(&mxvar_sdriver, 0, sizeof(struct tty_driver)); + mxvar_sdriver.magic = TTY_DRIVER_MAGIC; + mxvar_sdriver.name = "ttyM"; + mxvar_sdriver.major = ttymajor; + mxvar_sdriver.minor_start = 0; + mxvar_sdriver.num = MXSER_PORTS + 1; + mxvar_sdriver.type = TTY_DRIVER_TYPE_SERIAL; + mxvar_sdriver.subtype = SERIAL_TYPE_NORMAL; + mxvar_sdriver.init_termios = tty_std_termios; + mxvar_sdriver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + mxvar_sdriver.flags = TTY_DRIVER_REAL_RAW; + mxvar_sdriver.refcount = &mxvar_refcount; + mxvar_sdriver.table = mxvar_tty; + mxvar_sdriver.termios = mxvar_termios; + mxvar_sdriver.termios_locked = mxvar_termios_locked; + + mxvar_sdriver.open = mxser_open; + mxvar_sdriver.close = mxser_close; + mxvar_sdriver.write = mxser_write; + mxvar_sdriver.put_char = mxser_put_char; + mxvar_sdriver.flush_chars = mxser_flush_chars; + mxvar_sdriver.write_room = mxser_write_room; + mxvar_sdriver.chars_in_buffer = mxser_chars_in_buffer; + mxvar_sdriver.flush_buffer = mxser_flush_buffer; + mxvar_sdriver.ioctl = mxser_ioctl; + mxvar_sdriver.throttle = mxser_throttle; + mxvar_sdriver.unthrottle = mxser_unthrottle; + mxvar_sdriver.set_termios = mxser_set_termios; + mxvar_sdriver.stop = mxser_stop; + mxvar_sdriver.start = mxser_start; + mxvar_sdriver.hangup = mxser_hangup; + + /* + * The callout device is just like normal device except for + * major number and the subtype code. + */ + mxvar_cdriver = mxvar_sdriver; + mxvar_cdriver.name = "cum"; + mxvar_cdriver.major = calloutmajor; + mxvar_cdriver.subtype = SERIAL_TYPE_CALLOUT; + + printk("Tty devices major number = %d, callout devices major number = %d\n", ttymajor, calloutmajor); + + mxvar_diagflag = 0; + memset(mxvar_table, 0, MXSER_PORTS * sizeof(struct mxser_struct)); + memset(&mxvar_log, 0, sizeof(struct mxser_log)); + + + m = 0; + /* Start finding ISA boards here */ + for (b = 0; b < MXSER_BOARDS && m < MXSER_BOARDS; b++) { + int cap; + if (!(cap = mxserBoardCAP[b])) + continue; + + retval = mxser_get_ISA_conf(cap, &hwconf); + + if (retval != 0) + printk("Found MOXA %s board (CAP=0x%x)\n", + mxser_brdname[hwconf.board_type - 1], + ioaddr[b]); + + if (retval <= 0) { + if (retval == MXSER_ERR_IRQ) + printk("Invalid interrupt number,board not configured\n"); + else if (retval == MXSER_ERR_IRQ_CONFLIT) + printk("Invalid interrupt number,board not configured\n"); + else if (retval == MXSER_ERR_VECTOR) + printk("Invalid interrupt vector,board not configured\n"); + else if (retval == MXSER_ERR_IOADDR) + printk("Invalid I/O address,board not configured\n"); + + continue; + } + hwconf.pciInfo.busNum = 0; + hwconf.pciInfo.devNum = 0; + + if (mxser_initbrd(m, &hwconf) < 0) + continue; + + mxser_getcfg(m, &hwconf); + + m++; + } + + /* Start finding ISA boards from module arg */ + for (b = 0; b < MXSER_BOARDS && m < MXSER_BOARDS; b++) { + int cap; + if (!(cap = ioaddr[b])) + continue; + + retval = mxser_get_ISA_conf(cap, &hwconf); + + if (retval != 0) + printk("Found MOXA %s board (CAP=0x%x)\n", + mxser_brdname[hwconf.board_type - 1], + ioaddr[b]); + + if (retval <= 0) { + if (retval == MXSER_ERR_IRQ) + printk("Invalid interrupt number,board not configured\n"); + else if (retval == MXSER_ERR_IRQ_CONFLIT) + printk("Invalid interrupt number,board not configured\n"); + else if (retval == MXSER_ERR_VECTOR) + printk("Invalid interrupt vector,board not configured\n"); + else if (retval == MXSER_ERR_IOADDR) + printk("Invalid I/O address,board not configured\n"); + + continue; + } + hwconf.pciInfo.busNum = 0; + hwconf.pciInfo.devNum = 0; + + if (mxser_initbrd(m, &hwconf) < 0) + continue; + + mxser_getcfg(m, &hwconf); + + m++; + } + + /* start finding PCI board here */ + +#ifdef CONFIG_PCI + if (pci_present()) + { + n = sizeof(mxser_pcibrds) / sizeof(mxser_pciinfo); + index = 0; + b = 0; + while (b < n) { + if (pcibios_find_device(mxser_pcibrds[b].vendor_id, + mxser_pcibrds[b].device_id, + index, + &busnum, + &devnum) != 0) { + b++; + index = 0; + continue; + } + hwconf.pciInfo.busNum = busnum; + hwconf.pciInfo.devNum = devnum; + printk("Found MOXA %s board(BusNo=%d,DevNo=%d)\n", mxser_brdname[mxser_pcibrds[b].board_type - 1], busnum, devnum >> 3); + index++; + if (m >= MXSER_BOARDS) { + printk("Too many Smartio family boards find (maximum %d),board not configured\n", MXSER_BOARDS); + } else { + retval = mxser_get_PCI_conf(busnum, devnum, + mxser_pcibrds[b].board_type, &hwconf); + if (retval < 0) { + if (retval == MXSER_ERR_IRQ) + printk("Invalid interrupt number,board not configured\n"); + else if (retval == MXSER_ERR_IRQ_CONFLIT) + printk("Invalid interrupt number,board not configured\n"); + else if (retval == MXSER_ERR_VECTOR) + printk("Invalid interrupt vector,board not configured\n"); + else if (retval == MXSER_ERR_IOADDR) + printk("Invalid I/O address,board not configured\n"); + continue; + + } + if (mxser_initbrd(m, &hwconf) < 0) + continue; + mxser_getcfg(m, &hwconf); + m++; + + } + + } + } +#endif + + for (i = m; i < MXSER_BOARDS; i++) { + mxsercfg[i].board_type = -1; + } + + + ret1 = 0; + ret2 = 0; + if (!(ret1 = tty_register_driver(&mxvar_sdriver))) { + if (!(ret2 = tty_register_driver(&mxvar_cdriver))) { + return 0; + } else { + tty_unregister_driver(&mxvar_sdriver); + printk("Couldn't install MOXA Smartio family callout driver !\n"); + } + } else + printk("Couldn't install MOXA Smartio family driver !\n"); + + + if (ret1 || ret2) { + for (i = 0; i < MXSER_BOARDS; i++) { + if (mxsercfg[i].board_type == -1) + continue; + else { + free_irq(mxsercfg[i].irq, &mxvar_table[i * MXSER_PORTS_PER_BOARD]); + } + } + return -1; + } + return (0); +} + +static void mxser_do_softint(void *private_) +{ + struct mxser_struct *info = (struct mxser_struct *) private_; + struct tty_struct *tty; + + tty = info->tty; + if (!tty) + return; + if (test_and_clear_bit(MXSER_EVENT_TXLOW, &info->event)) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup) (tty); + wake_up_interruptible(&tty->write_wait); + } + if (test_and_clear_bit(MXSER_EVENT_HANGUP, &info->event)) { + tty_hangup(tty); + } +} + +/* + * This routine is called whenever a serial port is opened. It + * enables interrupts for a serial port, linking in its async structure into + * the IRQ chain. It also performs the serial-specific + * initialization for the tty structure. + */ + +static int mxser_open(struct tty_struct *tty, struct file *filp) +{ + struct mxser_struct *info; + int retval, line; + unsigned long page; + + line = PORTNO(tty); + if (line == MXSER_PORTS) + return (0); + if ((line < 0) || (line > MXSER_PORTS)) + return (-ENODEV); + info = mxvar_table + line; + if (!info->base) + return (-ENODEV); + + info->count++; + tty->driver_data = info; + info->tty = tty; + + if (!mxvar_tmp_buf) { + page = get_free_page(GFP_KERNEL); + if (!page) + return (-ENOMEM); + if (mxvar_tmp_buf) + free_page(page); + else + mxvar_tmp_buf = (unsigned char *) page; + } + /* + * Start up serial port + */ + retval = mxser_startup(info); + if (retval) + return (retval); + + retval = mxser_block_til_ready(tty, filp, info); + if (retval) + return (retval); + + if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->normal_termios; + else + *tty->termios = info->callout_termios; + mxser_change_speed(info, 0); + } + info->session = current->session; + info->pgrp = current->pgrp; + + MOD_INC_USE_COUNT; + + return (0); +} + +/* + * This routine is called when the serial port gets closed. First, we + * wait for the last remaining data to be sent. Then, we unlink its + * async structure from the interrupt chain if necessary, and we free + * that IRQ if nothing is left in the chain. + */ + +static void mxser_close(struct tty_struct *tty, struct file *filp) +{ + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + unsigned long flags; + unsigned long timeout; + + if (PORTNO(tty) == MXSER_PORTS) + return; + if (!info) + return; + + save_flags(flags); + cli(); + + if (tty_hung_up_p(filp)) { + restore_flags(flags); + MOD_DEC_USE_COUNT; + return; + } + if ((tty->count == 1) && (info->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. Info->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("mxser_close: bad serial port count; tty->count is 1, " + "info->count is %d\n", info->count); + info->count = 1; + } + if (--info->count < 0) { + printk("mxser_close: bad serial port count for ttys%d: %d\n", + info->port, info->count); + info->count = 0; + } + if (info->count) { + restore_flags(flags); + MOD_DEC_USE_COUNT; + return; + } + info->flags |= ASYNC_CLOSING; + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->normal_termios = *tty->termios; + if (info->flags & ASYNC_CALLOUT_ACTIVE) + info->callout_termios = *tty->termios; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->closing_wait); + /* + * At this point we stop accepting input. To do this, we + * disable the receive line status interrupts, and tell the + * interrupt driver to stop checking the data ready bit in the + * line status register. + */ + info->IER &= ~UART_IER_RLSI; + /* by William + info->read_status_mask &= ~UART_LSR_DR; + */ + if (info->flags & ASYNC_INITIALIZED) { + outb(info->IER, info->base + UART_IER); + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + timeout = jiffies + HZ; + while (!(inb(info->base + UART_LSR) & UART_LSR_TEMT)) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(5); + if (jiffies > timeout) + break; + } + } + mxser_shutdown(info); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + info->event = 0; + info->tty = 0; + if (info->blocked_open) { + if (info->close_delay) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(info->close_delay); + } + wake_up_interruptible(&info->open_wait); + } + info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE | + ASYNC_CLOSING); + wake_up_interruptible(&info->close_wait); + restore_flags(flags); + + MOD_DEC_USE_COUNT; +} + +static int mxser_write(struct tty_struct *tty, int from_user, + const unsigned char *buf, int count) +{ + int c, total = 0; + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + unsigned long flags; + + if (!tty || !info->xmit_buf || !mxvar_tmp_buf) + return (0); + + if (from_user) + down(&mxvar_tmp_buf_sem); + save_flags(flags); + while (1) { + cli(); + c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + if (c <= 0) + break; + + if (from_user) { + copy_from_user(mxvar_tmp_buf, buf, c); + c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + memcpy(info->xmit_buf + info->xmit_head, mxvar_tmp_buf, c); + } else + memcpy(info->xmit_buf + info->xmit_head, buf, c); + info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE - 1); + info->xmit_cnt += c; + restore_flags(flags); + buf += c; + count -= c; + total += c; + } + if (from_user) + up(&mxvar_tmp_buf_sem); + if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped && + !(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + outb(info->IER, info->base + UART_IER); + } + restore_flags(flags); + return (total); +} + +static void mxser_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + unsigned long flags; + + if (!tty || !info->xmit_buf) + return; + + save_flags(flags); + cli(); + if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { + restore_flags(flags); + return; + } + info->xmit_buf[info->xmit_head++] = ch; + info->xmit_head &= SERIAL_XMIT_SIZE - 1; + info->xmit_cnt++; + /********************************************** why ??? *********** + if ( !tty->stopped && !tty->hw_stopped && + !(info->IER & UART_IER_THRI) ) { + info->IER |= UART_IER_THRI; + outb(info->IER, info->base + UART_IER); + } + *****************************************************************/ + restore_flags(flags); +} + +static void mxser_flush_chars(struct tty_struct *tty) +{ + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + unsigned long flags; + + if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || + !info->xmit_buf) + return; + + save_flags(flags); + cli(); + info->IER |= UART_IER_THRI; + outb(info->IER, info->base + UART_IER); + restore_flags(flags); +} + +static int mxser_write_room(struct tty_struct *tty) +{ + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + int ret; + + ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; + if (ret < 0) + ret = 0; + return (ret); +} + +static int mxser_chars_in_buffer(struct tty_struct *tty) +{ + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + + return (info->xmit_cnt); +} + +static void mxser_flush_buffer(struct tty_struct *tty) +{ + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + unsigned long flags; + + save_flags(flags); + cli(); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + restore_flags(flags); + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup) (tty); +} + +static int mxser_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + int retval; + struct async_icount cprev, cnow; /* kernel counter temps */ + struct serial_icounter_struct *p_cuser; /* user space */ + unsigned long templ; + + if (PORTNO(tty) == MXSER_PORTS) + return (mxser_ioctl_special(cmd, arg)); + if ((cmd != TIOCGSERIAL) && (cmd != TIOCMIWAIT) && + (cmd != TIOCGICOUNT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return (-EIO); + } + switch (cmd) { + case TCSBRK: /* SVID version: non-zero arg --> no break */ + retval = tty_check_change(tty); + if (retval) + return (retval); + tty_wait_until_sent(tty, 0); + if (!arg) + mxser_send_break(info, HZ / 4); /* 1/4 second */ + return (0); + case TCSBRKP: /* support for POSIX tcsendbreak() */ + retval = tty_check_change(tty); + if (retval) + return (retval); + tty_wait_until_sent(tty, 0); + mxser_send_break(info, arg ? arg * (HZ / 10) : HZ / 4); + return (0); + case TIOCGSOFTCAR: + return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg); + case TIOCSSOFTCAR: + if(get_user(templ, (unsigned long *) arg)) + return -EFAULT; + arg = templ; + tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0)); + return (0); + case TIOCMGET: + return (mxser_get_modem_info(info, (unsigned int *) arg)); + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + return (mxser_set_modem_info(info, cmd, (unsigned int *) arg)); + case TIOCGSERIAL: + return (mxser_get_serial_info(info, (struct serial_struct *) arg)); + case TIOCSSERIAL: + return (mxser_set_serial_info(info, (struct serial_struct *) arg)); + case TIOCSERGETLSR: /* Get line status register */ + return (mxser_get_lsr_info(info, (unsigned int *) arg)); + /* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + * - mask passed in arg for lines of interest + * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) + * Caller should use TIOCGICOUNT to see which one it was + */ + case TIOCMIWAIT: + save_flags(flags); + cli(); + cprev = info->icount; /* note the counters on entry */ + restore_flags(flags); + while (1) { + interruptible_sleep_on(&info->delta_msr_wait); + /* see if a signal did it */ + if (signal_pending(current)) + return (-ERESTARTSYS); + save_flags(flags); + cli(); + cnow = info->icount; /* atomic copy */ + restore_flags(flags); + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) + return (-EIO); /* no change => error */ + if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { + return (0); + } + cprev = cnow; + } + /* NOTREACHED */ + /* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ + case TIOCGICOUNT: + save_flags(flags); + cli(); + cnow = info->icount; + restore_flags(flags); + p_cuser = (struct serial_icounter_struct *) arg; + if(put_user(cnow.cts, &p_cuser->cts)) + return -EFAULT; + if(put_user(cnow.dsr, &p_cuser->dsr)) + return -EFAULT; + if(put_user(cnow.rng, &p_cuser->rng)) + return -EFAULT; + return put_user(cnow.dcd, &p_cuser->dcd); + case MOXA_HighSpeedOn: + return put_user(info->baud_base != 115200 ? 1 : 0, (int *) arg); + default: + return (-ENOIOCTLCMD); + } + return (0); +} + +static int mxser_ioctl_special(unsigned int cmd, unsigned long arg) +{ + int i, result, status; + + switch (cmd) { + case MOXA_GET_CONF: + if(copy_to_user((struct mxser_hwconf *) arg, mxsercfg, + sizeof(struct mxser_hwconf) * 4)) + return -EFAULT; + return 0; + case MOXA_GET_MAJOR: + if(copy_to_user((int *) arg, &ttymajor, sizeof(int))) + return -EFAULT; + return 0; + + case MOXA_GET_CUMAJOR: + if(copy_to_user((int *) arg, &calloutmajor, sizeof(int))) + return -EFAULT; + return 0; + + case MOXA_CHKPORTENABLE: + result = 0; + for (i = 0; i < MXSER_PORTS; i++) { + if (mxvar_table[i].base) + result |= (1 << i); + } + return put_user(result, (unsigned long *) arg); + case MOXA_GETDATACOUNT: + if(copy_to_user((struct mxser_log *) arg, &mxvar_log, sizeof(mxvar_log))) + return -EFAULT; + return (0); + case MOXA_GETMSTATUS: + for (i = 0; i < MXSER_PORTS; i++) { + GMStatus[i].ri = 0; + if (!mxvar_table[i].base) { + GMStatus[i].dcd = 0; + GMStatus[i].dsr = 0; + GMStatus[i].cts = 0; + continue; + } + if (!mxvar_table[i].tty || !mxvar_table[i].tty->termios) + GMStatus[i].cflag = mxvar_table[i].normal_termios.c_cflag; + else + GMStatus[i].cflag = mxvar_table[i].tty->termios->c_cflag; + + status = inb(mxvar_table[i].base + UART_MSR); + if (status & 0x80 /*UART_MSR_DCD */ ) + GMStatus[i].dcd = 1; + else + GMStatus[i].dcd = 0; + + if (status & 0x20 /*UART_MSR_DSR */ ) + GMStatus[i].dsr = 1; + else + GMStatus[i].dsr = 0; + + + if (status & 0x10 /*UART_MSR_CTS */ ) + GMStatus[i].cts = 1; + else + GMStatus[i].cts = 0; + } + if(copy_to_user((struct mxser_mstatus *) arg, GMStatus, + sizeof(struct mxser_mstatus) * MXSER_PORTS)) + return -EFAULT; + return 0; + default: + return (-ENOIOCTLCMD); + } + return (0); +} + +/* + * This routine is called by the upper-layer tty layer to signal that + * incoming characters should be throttled. + */ +static void mxser_throttle(struct tty_struct *tty) +{ + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + unsigned long flags; + + if (I_IXOFF(tty)) { + info->x_char = STOP_CHAR(tty); + save_flags(flags); + cli(); + outb(info->IER, 0); + info->IER |= UART_IER_THRI; + outb(info->IER, info->base + UART_IER); /* force Tx interrupt */ + restore_flags(flags); + } + if (info->tty->termios->c_cflag & CRTSCTS) { + info->MCR &= ~UART_MCR_RTS; + save_flags(flags); + cli(); + outb(info->MCR, info->base + UART_MCR); + restore_flags(flags); + } +} + +static void mxser_unthrottle(struct tty_struct *tty) +{ + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + unsigned long flags; + + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else { + info->x_char = START_CHAR(tty); + save_flags(flags); + cli(); + outb(info->IER, 0); + info->IER |= UART_IER_THRI; /* force Tx interrupt */ + outb(info->IER, info->base + UART_IER); + restore_flags(flags); + } + } + if (info->tty->termios->c_cflag & CRTSCTS) { + info->MCR |= UART_MCR_RTS; + save_flags(flags); + cli(); + outb(info->MCR, info->base + UART_MCR); + restore_flags(flags); + } +} + +static void mxser_set_termios(struct tty_struct *tty, + struct termios *old_termios) +{ + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + +/* 8-2-99 by William + if ( (tty->termios->c_cflag == old_termios->c_cflag) && + (RELEVANT_IFLAG(tty->termios->c_iflag) == + RELEVANT_IFLAG(old_termios->c_iflag)) ) + return; + + mxser_change_speed(info, old_termios); + + if ( (old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS) ) { + tty->hw_stopped = 0; + mxser_start(tty); + } + */ + if ((tty->termios->c_cflag != old_termios->c_cflag) || + (RELEVANT_IFLAG(tty->termios->c_iflag) != + RELEVANT_IFLAG(old_termios->c_iflag))) { + + mxser_change_speed(info, old_termios); + + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + mxser_start(tty); + } + } +/* Handle sw stopped */ + if ((old_termios->c_iflag & IXON) && + !(tty->termios->c_iflag & IXON)) { + tty->stopped = 0; + mxser_start(tty); + } +} + +/* + * mxser_stop() and mxser_start() + * + * This routines are called before setting or resetting tty->stopped. + * They enable or disable transmitter interrupts, as necessary. + */ +static void mxser_stop(struct tty_struct *tty) +{ + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + unsigned long flags; + + save_flags(flags); + cli(); + if (info->IER & UART_IER_THRI) { + info->IER &= ~UART_IER_THRI; + outb(info->IER, info->base + UART_IER); + } + restore_flags(flags); +} + +static void mxser_start(struct tty_struct *tty) +{ + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + unsigned long flags; + + save_flags(flags); + cli(); + if (info->xmit_cnt && info->xmit_buf && + !(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + outb(info->IER, info->base + UART_IER); + } + restore_flags(flags); +} + +/* + * This routine is called by tty_hangup() when a hangup is signaled. + */ +void mxser_hangup(struct tty_struct *tty) +{ + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + + mxser_flush_buffer(tty); + mxser_shutdown(info); + info->event = 0; + info->count = 0; + info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE); + info->tty = 0; + wake_up_interruptible(&info->open_wait); +} + +/* + * This is the serial driver's generic interrupt routine + */ +static void mxser_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + int status, i; + struct mxser_struct *info; + struct mxser_struct *port; + int max, irqbits, bits, msr; + int pass_counter = 0; + + port = 0; + for (i = 0; i < MXSER_BOARDS; i++) { + if (dev_id == &(mxvar_table[i * MXSER_PORTS_PER_BOARD])) { + port = dev_id; + break; + } + } + + if (i == MXSER_BOARDS) + return; + if (port == 0) + return; + max = mxser_numports[mxsercfg[i].board_type - 1]; + + while (1) { + irqbits = inb(port->vector) & port->vectormask; + if (irqbits == port->vectormask) + break; + for (i = 0, bits = 1; i < max; i++, irqbits |= bits, bits <<= 1) { + if (irqbits == port->vectormask) + break; + if (bits & irqbits) + continue; + info = port + i; + if (!info->tty || + (inb(info->base + UART_IIR) & UART_IIR_NO_INT)) + continue; + status = inb(info->base + UART_LSR) & info->read_status_mask; + if (status & UART_LSR_DR) + mxser_receive_chars(info, &status); + msr = inb(info->base + UART_MSR); + if (msr & UART_MSR_ANY_DELTA) + mxser_check_modem_status(info, msr); + if (status & UART_LSR_THRE) { +/* 8-2-99 by William + if ( info->x_char || (info->xmit_cnt > 0) ) + */ + mxser_transmit_chars(info); + } + } + if (pass_counter++ > MXSER_ISR_PASS_LIMIT) { +#if 0 + printk("MOXA Smartio/Indusrtio family driver interrupt loop break\n"); +#endif + break; /* Prevent infinite loops */ + } + } +} + +static inline void mxser_receive_chars(struct mxser_struct *info, + int *status) +{ + struct tty_struct *tty = info->tty; + unsigned char ch; + int ignored = 0; + int cnt = 0; + + do { + ch = inb(info->base + UART_RX); + if (*status & info->ignore_status_mask) { + if (++ignored > 100) + break; + } else { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + break; + tty->flip.count++; + if (*status & UART_LSR_SPECIAL) { + if (*status & UART_LSR_BI) { + *tty->flip.flag_buf_ptr++ = TTY_BREAK; + if (info->flags & ASYNC_SAK) + do_SAK(tty); + } else if (*status & UART_LSR_PE) { + *tty->flip.flag_buf_ptr++ = TTY_PARITY; + } else if (*status & UART_LSR_FE) { + *tty->flip.flag_buf_ptr++ = TTY_FRAME; + } else if (*status & UART_LSR_OE) { + *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; + } else + *tty->flip.flag_buf_ptr++ = 0; + } else + *tty->flip.flag_buf_ptr++ = 0; + *tty->flip.char_buf_ptr++ = ch; + cnt++; + } + *status = inb(info->base + UART_LSR) & info->read_status_mask; + } while (*status & UART_LSR_DR); + mxvar_log.rxcnt[info->port] += cnt; + queue_task(&tty->flip.tqueue, &tq_timer); + +} + +static inline void mxser_transmit_chars(struct mxser_struct *info) +{ + int count, cnt; + + if (info->x_char) { + outb(info->x_char, info->base + UART_TX); + info->x_char = 0; + mxvar_log.txcnt[info->port]++; + return; + } + if ((info->xmit_cnt <= 0) || info->tty->stopped || + info->tty->hw_stopped) { + info->IER &= ~UART_IER_THRI; + outb(info->IER, info->base + UART_IER); + return; + } + cnt = info->xmit_cnt; + count = info->xmit_fifo_size; + do { + outb(info->xmit_buf[info->xmit_tail++], info->base + UART_TX); + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE - 1); + if (--info->xmit_cnt <= 0) + break; + } while (--count > 0); + mxvar_log.txcnt[info->port] += (cnt - info->xmit_cnt); + + if (info->xmit_cnt < WAKEUP_CHARS) { + set_bit(MXSER_EVENT_TXLOW, &info->event); + queue_task(&info->tqueue, &tq_scheduler); + } + if (info->xmit_cnt <= 0) { + info->IER &= ~UART_IER_THRI; + outb(info->IER, info->base + UART_IER); + } +} + +static inline void mxser_check_modem_status(struct mxser_struct *info, + int status) +{ + + /* update input line counters */ + if (status & UART_MSR_TERI) + info->icount.rng++; + if (status & UART_MSR_DDSR) + info->icount.dsr++; + if (status & UART_MSR_DDCD) + info->icount.dcd++; + if (status & UART_MSR_DCTS) + info->icount.cts++; + wake_up_interruptible(&info->delta_msr_wait); + + if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { + if (status & UART_MSR_DCD) + wake_up_interruptible(&info->open_wait); + else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_CALLOUT_NOHUP))) + set_bit(MXSER_EVENT_HANGUP, &info->event); + queue_task(&info->tqueue, &tq_scheduler); + + } + if (info->flags & ASYNC_CTS_FLOW) { + if (info->tty->hw_stopped) { + if (status & UART_MSR_CTS) { + info->tty->hw_stopped = 0; + info->IER |= UART_IER_THRI; + outb(info->IER, info->base + UART_IER); + + set_bit(MXSER_EVENT_TXLOW, &info->event); + queue_task(&info->tqueue, &tq_scheduler); + } + } else { + if (!(status & UART_MSR_CTS)) { + info->tty->hw_stopped = 1; + info->IER &= ~UART_IER_THRI; + outb(info->IER, info->base + UART_IER); + } + } + } +} + +static int mxser_block_til_ready(struct tty_struct *tty, struct file *filp, + struct mxser_struct *info) +{ + struct wait_queue wait = {current, NULL}; + unsigned long flags; + int retval; + int do_clocal = 0; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); +#ifdef SERIAL_DO_RESTART + if (info->flags & ASYNC_HUP_NOTIFY) + return (-EAGAIN); + else + return (-ERESTARTSYS); +#else + return (-EAGAIN); +#endif + } + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + if (info->flags & ASYNC_NORMAL_ACTIVE) + return (-EBUSY); + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_SESSION_LOCKOUT) && + (info->session != current->session)) + return (-EBUSY); + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_PGRP_LOCKOUT) && + (info->pgrp != current->pgrp)) + return (-EBUSY); + info->flags |= ASYNC_CALLOUT_ACTIVE; + return (0); + } + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + if (info->flags & ASYNC_CALLOUT_ACTIVE) + return (-EBUSY); + info->flags |= ASYNC_NORMAL_ACTIVE; + return (0); + } + if (info->flags & ASYNC_CALLOUT_ACTIVE) { + if (info->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + } else { + if (tty->termios->c_cflag & CLOCAL) + do_clocal = 1; + } + + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, info->count is dropped by one, so that + * mxser_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&info->open_wait, &wait); + save_flags(flags); + cli(); + if (!tty_hung_up_p(filp)) + info->count--; + restore_flags(flags); + info->blocked_open++; + while (1) { + save_flags(flags); + cli(); + if (!(info->flags & ASYNC_CALLOUT_ACTIVE)) + outb(inb(info->base + UART_MCR) | UART_MCR_DTR | UART_MCR_RTS, + info->base + UART_MCR); + restore_flags(flags); + current->state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)) { +#ifdef SERIAL_DO_RESTART + if (info->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; +#else + retval = -EAGAIN; +#endif + break; + } + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + !(info->flags & ASYNC_CLOSING) && + (do_clocal || (inb(info->base + UART_MSR) & UART_MSR_DCD))) + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&info->open_wait, &wait); + if (!tty_hung_up_p(filp)) + info->count++; + info->blocked_open--; + if (retval) + return (retval); + info->flags |= ASYNC_NORMAL_ACTIVE; + return (0); +} + +static int mxser_startup(struct mxser_struct *info) +{ + unsigned long flags; + unsigned long page; + + page = get_free_page(GFP_KERNEL); + if (!page) + return (-ENOMEM); + + save_flags(flags); + cli(); + + if (info->flags & ASYNC_INITIALIZED) { + free_page(page); + restore_flags(flags); + return (0); + } + if (!info->base || !info->type) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + free_page(page); + restore_flags(flags); + return (0); + } + if (info->xmit_buf) + free_page(page); + else + info->xmit_buf = (unsigned char *) page; + + /* + * Clear the FIFO buffers and disable them + * (they will be reenabled in mxser_change_speed()) + */ + if (info->xmit_fifo_size == 16) + outb((UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT), + info->base + UART_FCR); + + /* + * At this point there's no way the LSR could still be 0xFF; + * if it is, then bail out, because there's likely no UART + * here. + */ + if (inb(info->base + UART_LSR) == 0xff) { + restore_flags(flags); + if (suser()) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + return (0); + } else + return (-ENODEV); + } + /* + * Clear the interrupt registers. + */ + (void) inb(info->base + UART_LSR); + (void) inb(info->base + UART_RX); + (void) inb(info->base + UART_IIR); + (void) inb(info->base + UART_MSR); + + /* + * Now, initialize the UART + */ + outb(UART_LCR_WLEN8, info->base + UART_LCR); /* reset DLAB */ + info->MCR = UART_MCR_DTR | UART_MCR_RTS; + outb(info->MCR, info->base + UART_MCR); + + /* + * Finally, enable interrupts + */ + info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; + outb(info->IER, info->base + UART_IER); /* enable interrupts */ + + /* + * And clear the interrupt registers again for luck. + */ + (void) inb(info->base + UART_LSR); + (void) inb(info->base + UART_RX); + (void) inb(info->base + UART_IIR); + (void) inb(info->base + UART_MSR); + + if (info->tty) + test_and_clear_bit(TTY_IO_ERROR, &info->tty->flags); + + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + + /* + * and set the speed of the serial port + */ + mxser_change_speed(info, 0); + + info->flags |= ASYNC_INITIALIZED; + restore_flags(flags); + return (0); +} + +/* + * This routine will shutdown a serial port; interrupts maybe disabled, and + * DTR is dropped if the hangup on close termio flag is on. + */ +static void mxser_shutdown(struct mxser_struct *info) +{ + unsigned long flags; + + if (!(info->flags & ASYNC_INITIALIZED)) + return; + + save_flags(flags); + cli(); /* Disable interrupts */ + + /* + * clear delta_msr_wait queue to avoid mem leaks: we may free the irq + * here so the queue might never be waken up + */ + wake_up_interruptible(&info->delta_msr_wait); + + /* + * Free the IRQ, if necessary + */ + if (info->xmit_buf) { + free_page((unsigned long) info->xmit_buf); + info->xmit_buf = 0; + } + info->IER = 0; + outb(0x00, info->base + UART_IER); /* disable all intrs */ + + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) + info->MCR &= ~(UART_MCR_DTR | UART_MCR_RTS); + outb(info->MCR, info->base + UART_MCR); + + /* clear Rx/Tx FIFO's */ + outb((UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT), info->base + UART_FCR); + /* read data port to reset things */ + (void) inb(info->base + UART_RX); + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + info->flags &= ~ASYNC_INITIALIZED; + restore_flags(flags); +} + +/* + * This routine is called to set the UART divisor registers to match + * the specified baud rate for a serial port. + */ +static int mxser_change_speed(struct mxser_struct *info, + struct termios *old_termios) +{ + int quot = 0; + unsigned cflag, cval, fcr; + int i; + int ret = 0; + unsigned long flags; + + if (!info->tty || !info->tty->termios) + return ret; + cflag = info->tty->termios->c_cflag; + if (!(info->base)) + return ret; + +#ifndef B921600 +#define B921600 (B460800 +1) +#endif + switch (cflag & (CBAUD | CBAUDEX)) { + case B921600: + i = 20; + break; + case B460800: + i = 19; + break; + case B230400: + i = 18; + break; + case B115200: + i = 17; + break; + case B57600: + i = 16; + break; + case B38400: + i = 15; + break; + case B19200: + i = 14; + break; + case B9600: + i = 13; + break; + case B4800: + i = 12; + break; + case B2400: + i = 11; + break; + case B1800: + i = 10; + break; + case B1200: + i = 9; + break; + case B600: + i = 8; + break; + case B300: + i = 7; + break; + case B200: + i = 6; + break; + case B150: + i = 5; + break; + case B134: + i = 4; + break; + case B110: + i = 3; + break; + case B75: + i = 2; + break; + case B50: + i = 1; + break; + default: + i = 0; + break; + } + + if (i == 15) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + i = 16; /* 57600 bps */ + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + i = 17; /* 115200 bps */ + +#ifdef ASYNC_SPD_SHI + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + i = 18; +#endif + +#ifdef ASYNC_SPD_WARP + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + i = 19; +#endif + } + if (mxvar_baud_table[i] == 134) { + quot = (2 * info->baud_base / 269); + } else if (mxvar_baud_table[i]) { + quot = info->baud_base / mxvar_baud_table[i]; + if (!quot && old_termios) { + /* re-calculate */ + info->tty->termios->c_cflag &= ~CBAUD; + info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); + switch (info->tty->termios->c_cflag & (CBAUD | CBAUDEX)) { + case B921600: + i = 20; + break; + case B460800: + i = 19; + break; + case B230400: + i = 18; + break; + case B115200: + i = 17; + break; + case B57600: + i = 16; + break; + case B38400: + i = 15; + break; + case B19200: + i = 14; + break; + case B9600: + i = 13; + break; + case B4800: + i = 12; + break; + case B2400: + i = 11; + break; + case B1800: + i = 10; + break; + case B1200: + i = 9; + break; + case B600: + i = 8; + break; + case B300: + i = 7; + break; + case B200: + i = 6; + break; + case B150: + i = 5; + break; + case B134: + i = 4; + break; + case B110: + i = 3; + break; + case B75: + i = 2; + break; + case B50: + i = 1; + break; + default: + i = 0; + break; + } + if (i == 15) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + i = 16; /* 57600 bps */ + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + i = 17; /* 115200 bps */ +#ifdef ASYNC_SPD_SHI + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + i = 18; +#endif +#ifdef ASYNC_SPD_WARP + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + i = 19; +#endif + } + if (mxvar_baud_table[i] == 134) { + quot = (2 * info->baud_base / 269); + } else if (mxvar_baud_table[i]) { + quot = info->baud_base / mxvar_baud_table[i]; + if (quot == 0) + quot = 1; + } else { + quot = 0; + } + } else if (quot == 0) + quot = 1; + } else { + quot = 0; + } + + if (quot) { + info->MCR |= UART_MCR_DTR; + save_flags(flags); + cli(); + outb(info->MCR, info->base + UART_MCR); + restore_flags(flags); + } else { + info->MCR &= ~UART_MCR_DTR; + save_flags(flags); + cli(); + outb(info->MCR, info->base + UART_MCR); + restore_flags(flags); + return ret; + } + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: + cval = 0x00; + break; + case CS6: + cval = 0x01; + break; + case CS7: + cval = 0x02; + break; + case CS8: + cval = 0x03; + break; + default: + cval = 0x00; + break; /* too keep GCC shut... */ + } + if (cflag & CSTOPB) + cval |= 0x04; + if (cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; + if ((info->type == PORT_8250) || (info->type == PORT_16450)) { + fcr = 0; + } else { + fcr = UART_FCR_ENABLE_FIFO; + switch (info->rx_trigger) { + case 1: + fcr |= UART_FCR_TRIGGER_1; + break; + case 4: + fcr |= UART_FCR_TRIGGER_4; + break; + case 8: + fcr |= UART_FCR_TRIGGER_8; + break; + default: + fcr |= UART_FCR_TRIGGER_14; + } + } + + /* CTS flow control flag and modem status interrupts */ + info->IER &= ~UART_IER_MSI; + info->MCR &= ~UART_MCR_AFE; + if (cflag & CRTSCTS) { + info->flags |= ASYNC_CTS_FLOW; + info->IER |= UART_IER_MSI; + if (info->type == PORT_16550A) + info->MCR |= UART_MCR_AFE; + } else { + info->flags &= ~ASYNC_CTS_FLOW; + } + outb(info->MCR, info->base + UART_MCR); + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else { + info->flags |= ASYNC_CHECK_CD; + info->IER |= UART_IER_MSI; + } + outb(info->IER, info->base + UART_IER); + + /* + * Set up parity check flag + */ + info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (I_INPCK(info->tty)) + info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) + info->read_status_mask |= UART_LSR_BI; + + info->ignore_status_mask = 0; +#if 0 + /* This should be safe, but for some broken bits of hardware... */ + if (I_IGNPAR(info->tty)) { + info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + info->read_status_mask |= UART_LSR_PE | UART_LSR_FE; + } +#endif + if (I_IGNBRK(info->tty)) { + info->ignore_status_mask |= UART_LSR_BI; + info->read_status_mask |= UART_LSR_BI; + /* + * If we're ignore parity and break indicators, ignore + * overruns too. (For real raw support). + */ + if (I_IGNPAR(info->tty)) { + info->ignore_status_mask |= UART_LSR_OE | UART_LSR_PE | UART_LSR_FE; + info->read_status_mask |= UART_LSR_OE | UART_LSR_PE | UART_LSR_FE; + } + } + save_flags(flags); + cli(); + outb(cval | UART_LCR_DLAB, info->base + UART_LCR); /* set DLAB */ + outb(quot & 0xff, info->base + UART_DLL); /* LS of divisor */ + outb(quot >> 8, info->base + UART_DLM); /* MS of divisor */ + outb(cval, info->base + UART_LCR); /* reset DLAB */ + outb(fcr, info->base + UART_FCR); /* set fcr */ + restore_flags(flags); + + return ret; +} + +/* + * ------------------------------------------------------------ + * friends of mxser_ioctl() + * ------------------------------------------------------------ + */ +static int mxser_get_serial_info(struct mxser_struct *info, + struct serial_struct *retinfo) +{ + struct serial_struct tmp; + + if (!retinfo) + return (-EFAULT); + memset(&tmp, 0, sizeof(tmp)); + tmp.type = info->type; + tmp.line = info->port; + tmp.port = info->base; + tmp.irq = info->irq; + tmp.flags = info->flags; + tmp.baud_base = info->baud_base; + tmp.close_delay = info->close_delay; + tmp.closing_wait = info->closing_wait; + tmp.custom_divisor = info->custom_divisor; + tmp.hub6 = 0; + copy_to_user(retinfo, &tmp, sizeof(*retinfo)); + return (0); +} + +static int mxser_set_serial_info(struct mxser_struct *info, + struct serial_struct *new_info) +{ + struct serial_struct new_serial; + unsigned int flags; + int retval = 0; + + if (!new_info || !info->base) + return (-EFAULT); + copy_from_user(&new_serial, new_info, sizeof(new_serial)); + + if ((new_serial.irq != info->irq) || + (new_serial.port != info->base) || + (new_serial.type != info->type) || + (new_serial.custom_divisor != info->custom_divisor) || + (new_serial.baud_base != info->baud_base)) + return (-EPERM); + + flags = info->flags & ASYNC_SPD_MASK; + + if (!suser()) { + if ((new_serial.baud_base != info->baud_base) || + (new_serial.close_delay != info->close_delay) || + ((new_serial.flags & ~ASYNC_USR_MASK) != + (info->flags & ~ASYNC_USR_MASK))) + return (-EPERM); + info->flags = ((info->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + } else { + /* + * OK, past this point, all the error checking has been done. + * At this point, we start making changes..... + */ + info->flags = ((info->flags & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS)); + info->close_delay = new_serial.close_delay * HZ / 100; + info->closing_wait = new_serial.closing_wait * HZ / 100; + } + + if (info->flags & ASYNC_INITIALIZED) { + if (flags != (info->flags & ASYNC_SPD_MASK)) { + mxser_change_speed(info, 0); + } + } else + retval = mxser_startup(info); + return (retval); +} + +/* + * mxser_get_lsr_info - get line status register info + * + * Purpose: Let user call ioctl() to get info when the UART physically + * is emptied. On bus types like RS485, the transmitter must + * release the bus after transmitting. This must be done when + * the transmit shift register is empty, not be done when the + * transmit holding register is empty. This functionality + * allows an RS485 driver to be written in user space. + */ +static int mxser_get_lsr_info(struct mxser_struct *info, unsigned int *value) +{ + unsigned char status; + unsigned int result; + unsigned long flags; + + save_flags(flags); + cli(); + status = inb(info->base + UART_LSR); + restore_flags(flags); + result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); + put_user(result, value); + return (0); +} + +/* + * This routine sends a break character out the serial port. + */ +static void mxser_send_break(struct mxser_struct *info, int duration) +{ + unsigned long flags; + if (!info->base) + return; + current->state = TASK_INTERRUPTIBLE; + save_flags(flags); + cli(); + outb(inb(info->base + UART_LCR) | UART_LCR_SBC, info->base + UART_LCR); + schedule_timeout(duration); + outb(inb(info->base + UART_LCR) & ~UART_LCR_SBC, info->base + UART_LCR); + restore_flags(flags); +} + +static int mxser_get_modem_info(struct mxser_struct *info, + unsigned int *value) +{ + unsigned char control, status; + unsigned int result; + unsigned long flags; + + control = info->MCR; + save_flags(flags); + cli(); + status = inb(info->base + UART_MSR); + if (status & UART_MSR_ANY_DELTA) + mxser_check_modem_status(info, status); + restore_flags(flags); + result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) | + ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) | + ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) | + ((status & UART_MSR_RI) ? TIOCM_RNG : 0) | + ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) | + ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); + put_user(result, value); + return (0); +} + +static int mxser_set_modem_info(struct mxser_struct *info, unsigned int cmd, + unsigned int *value) +{ + unsigned int arg; + unsigned long flags; + + if(get_user(arg, value)) + return -EFAULT; + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS) + info->MCR |= UART_MCR_RTS; + if (arg & TIOCM_DTR) + info->MCR |= UART_MCR_DTR; + break; + case TIOCMBIC: + if (arg & TIOCM_RTS) + info->MCR &= ~UART_MCR_RTS; + if (arg & TIOCM_DTR) + info->MCR &= ~UART_MCR_DTR; + break; + case TIOCMSET: + info->MCR = ((info->MCR & ~(UART_MCR_RTS | UART_MCR_DTR)) | + ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) | + ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); + break; + default: + return (-EINVAL); + } + save_flags(flags); + cli(); + outb(info->MCR, info->base + UART_MCR); + restore_flags(flags); + return (0); +} + +static int mxser_read_register(int, unsigned short *); +static int mxser_program_mode(int); +static void mxser_normal_mode(int); + +static int mxser_get_ISA_conf(int cap, struct mxser_hwconf *hwconf) +{ + int id, i, bits; + unsigned short regs[16], irq; + unsigned char scratch, scratch2; + + id = mxser_read_register(cap, regs); + if (id == C168_ASIC_ID) + hwconf->board_type = MXSER_BOARD_C168_ISA; + else if (id == C104_ASIC_ID) + hwconf->board_type = MXSER_BOARD_C104_ISA; + else if (id == CI104J_ASIC_ID) + hwconf->board_type = MXSER_BOARD_CI104J; + else + return (0); + irq = regs[9] & 0x0F; + irq = irq | (irq << 4); + irq = irq | (irq << 8); + if ((irq != regs[9]) || ((id == 1) && (irq != regs[10]))) { + return (MXSER_ERR_IRQ_CONFLIT); + } + if (!irq) { + return (MXSER_ERR_IRQ); + } + for (i = 0; i < 8; i++) + hwconf->ioaddr[i] = (int) regs[i + 1] & 0xFFF8; + hwconf->irq = (int) (irq & 0x0F); + if ((regs[12] & 0x80) == 0) { + return (MXSER_ERR_VECTOR); + } + hwconf->vector = (int) regs[11]; /* interrupt vector */ + if (id == 1) + hwconf->vector_mask = 0x00FF; + else + hwconf->vector_mask = 0x000F; + for (i = 7, bits = 0x0100; i >= 0; i--, bits <<= 1) { + if (regs[12] & bits) + hwconf->baud_base[i] = 921600; + else + hwconf->baud_base[i] = 115200; + } + scratch2 = inb(cap + UART_LCR) & (~UART_LCR_DLAB); + outb(scratch2 | UART_LCR_DLAB, cap + UART_LCR); + outb(0, cap + UART_EFR); /* EFR is the same as FCR */ + outb(scratch2, cap + UART_LCR); + outb(UART_FCR_ENABLE_FIFO, cap + UART_FCR); + scratch = inb(cap + UART_IIR); + if (scratch & 0xC0) + hwconf->uart_type = PORT_16550A; + else + hwconf->uart_type = PORT_16450; + if (id == 1) + hwconf->ports = 8; + else + hwconf->ports = 4; + return (hwconf->ports); +} + +#define CHIP_SK 0x01 /* Serial Data Clock in Eprom */ +#define CHIP_DO 0x02 /* Serial Data Output in Eprom */ +#define CHIP_CS 0x04 /* Serial Chip Select in Eprom */ +#define CHIP_DI 0x08 /* Serial Data Input in Eprom */ +#define EN_CCMD 0x000 /* Chip's command register */ +#define EN0_RSARLO 0x008 /* Remote start address reg 0 */ +#define EN0_RSARHI 0x009 /* Remote start address reg 1 */ +#define EN0_RCNTLO 0x00A /* Remote byte count reg WR */ +#define EN0_RCNTHI 0x00B /* Remote byte count reg WR */ +#define EN0_DCFG 0x00E /* Data configuration reg WR */ +#define EN0_PORT 0x010 /* Rcv missed frame error counter RD */ +#define ENC_PAGE0 0x000 /* Select page 0 of chip registers */ +#define ENC_PAGE3 0x0C0 /* Select page 3 of chip registers */ +static int mxser_read_register(int port, unsigned short *regs) +{ + int i, k, value, id; + unsigned int j; + + id = mxser_program_mode(port); + if (id < 0) + return (id); + for (i = 0; i < 14; i++) { + k = (i & 0x3F) | 0x180; + for (j = 0x100; j > 0; j >>= 1) { + outb(CHIP_CS, port); + if (k & j) { + outb(CHIP_CS | CHIP_DO, port); + outb(CHIP_CS | CHIP_DO | CHIP_SK, port); /* A? bit of read */ + } else { + outb(CHIP_CS, port); + outb(CHIP_CS | CHIP_SK, port); /* A? bit of read */ + } + } + (void) inb(port); + value = 0; + for (k = 0, j = 0x8000; k < 16; k++, j >>= 1) { + outb(CHIP_CS, port); + outb(CHIP_CS | CHIP_SK, port); + if (inb(port) & CHIP_DI) + value |= j; + } + regs[i] = value; + outb(0, port); + } + mxser_normal_mode(port); + return (id); +} + +static int mxser_program_mode(int port) +{ + int id, i, j, n; + unsigned long flags; + + save_flags(flags); + cli(); + outb(0, port); + outb(0, port); + outb(0, port); + (void) inb(port); + (void) inb(port); + outb(0, port); + (void) inb(port); + restore_flags(flags); + id = inb(port + 1) & 0x1F; + if ((id != C168_ASIC_ID) && (id != C104_ASIC_ID) && (id != CI104J_ASIC_ID)) + return (-1); + for (i = 0, j = 0; i < 4; i++) { + n = inb(port + 2); + if (n == 'M') { + j = 1; + } else if ((j == 1) && (n == 1)) { + j = 2; + break; + } else + j = 0; + } + if (j != 2) + id = -2; + return (id); +} + +static void mxser_normal_mode(int port) +{ + int i, n; + + outb(0xA5, port + 1); + outb(0x80, port + 3); + outb(12, port + 0); /* 9600 bps */ + outb(0, port + 1); + outb(0x03, port + 3); /* 8 data bits */ + outb(0x13, port + 4); /* loop back mode */ + for (i = 0; i < 16; i++) { + n = inb(port + 5); + if ((n & 0x61) == 0x60) + break; + if ((n & 1) == 1) + (void) inb(port); + } + outb(0x00, port + 4); +} diff -u --recursive --new-file v2.2.13/linux/drivers/char/radio-aimslab.c linux/drivers/char/radio-aimslab.c --- v2.2.13/linux/drivers/char/radio-aimslab.c Thu Apr 15 05:42:40 1999 +++ linux/drivers/char/radio-aimslab.c Tue Jan 4 10:12:14 2000 @@ -195,7 +195,7 @@ return 0; } -int rt_getsigstr(struct rt_device *dev) +static int rt_getsigstr(struct rt_device *dev) { if (inb(io) & 2) /* bit set = no signal present */ return 0; diff -u --recursive --new-file v2.2.13/linux/drivers/char/radio-cadet.c linux/drivers/char/radio-cadet.c --- v2.2.13/linux/drivers/char/radio-cadet.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/char/radio-cadet.c Tue Jan 4 10:12:14 2000 @@ -39,6 +39,7 @@ static __u8 rdsin=0,rdsout=0,rdsstat=0; static unsigned char rdsbuf[RDS_BUFFER]; static int cadet_lock=0; +static int cadet_probe(void); /* * Signal Strength Threshold Values diff -u --recursive --new-file v2.2.13/linux/drivers/char/radio-rtrack2.c linux/drivers/char/radio-rtrack2.c --- v2.2.13/linux/drivers/char/radio-rtrack2.c Thu Apr 15 05:42:40 1999 +++ linux/drivers/char/radio-rtrack2.c Tue Jan 4 10:12:14 2000 @@ -89,7 +89,7 @@ return 0; } -int rt_getsigstr(struct rt_device *dev) +static int rt_getsigstr(struct rt_device *dev) { if (inb(io) & 2) /* bit set = no signal present */ return 0; diff -u --recursive --new-file v2.2.13/linux/drivers/char/serial.c linux/drivers/char/serial.c --- v2.2.13/linux/drivers/char/serial.c Tue Jan 4 11:10:33 2000 +++ linux/drivers/char/serial.c Tue Jan 4 10:12:15 2000 @@ -61,6 +61,7 @@ * Check the magic number for the async_structure where * ever possible. */ +#include #undef SERIAL_PARANOIA_CHECK #define CONFIG_SERIAL_NOPAUSE_IO @@ -119,7 +120,6 @@ * End of serial driver configuration section. */ -#include #include #include #include diff -u --recursive --new-file v2.2.13/linux/drivers/char/synclink.c linux/drivers/char/synclink.c --- v2.2.13/linux/drivers/char/synclink.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/char/synclink.c Tue Jan 4 10:12:15 2000 @@ -1,7 +1,7 @@ /* * linux/drivers/char/synclink.c * - * ==FILEDATE 19990901== + * ==FILEDATE 19991207== * * Device driver for Microgate SyncLink ISA and PCI * high speed multiprotocol serial adapters. @@ -925,7 +925,7 @@ #endif static char *driver_name = "SyncLink serial driver"; -static char *driver_version = "1.14"; +static char *driver_version = "1.15"; static struct tty_driver serial_driver, callout_driver; static int serial_refcount; @@ -6981,7 +6981,6 @@ spin_lock_irqsave(&info->irq_spinlock,flags); usc_reset(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); /* Verify the reset state of some registers. */ @@ -7015,7 +7014,6 @@ } } - spin_lock_irqsave(&info->irq_spinlock,flags); usc_reset(info); spin_unlock_irqrestore(&info->irq_spinlock,flags); @@ -7035,7 +7033,6 @@ spin_lock_irqsave(&info->irq_spinlock,flags); usc_reset(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); /* * Setup 16C32 to interrupt on TxC pin (14MHz clock) transition. @@ -7057,6 +7054,8 @@ usc_UnlatchIostatusBits(info, MISCSTATUS_TXC_LATCHED); usc_EnableStatusIrqs(info, SICR_TXC_ACTIVE + SICR_TXC_INACTIVE); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + EndTime=100; while( EndTime-- && !info->irq_occurred ) { set_current_state(TASK_INTERRUPTIBLE); @@ -7359,7 +7358,9 @@ } } + spin_lock_irqsave(&info->irq_spinlock,flags); usc_reset( info ); + spin_unlock_irqrestore(&info->irq_spinlock,flags); /* restore current port options */ memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS)); diff -u --recursive --new-file v2.2.13/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c --- v2.2.13/linux/drivers/char/tty_io.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/char/tty_io.c Tue Jan 4 10:12:15 2000 @@ -129,6 +129,9 @@ extern long console_8xx_init(long, long); extern int rs_8xx_init(void); #endif /* CONFIG_8xx */ +#ifdef CONFIG_3215 +extern long con3215_init(long, long); +#endif /* CONFIG_3215 */ #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) @@ -2070,6 +2073,9 @@ #ifdef CONFIG_VT kmem_start = con_init(kmem_start); #endif +#ifdef CONFIG_HWC + kmem_start = hwc_console_init(kmem_start); +#endif #ifdef CONFIG_SERIAL_CONSOLE #ifdef CONFIG_8xx kmem_start = console_8xx_init(kmem_start, kmem_end); @@ -2077,6 +2083,9 @@ kmem_start = serial_console_init(kmem_start, kmem_end); #endif /* CONFIG_8xx */ #endif +#ifdef CONFIG_3215 + kmem_start = con3215_init(kmem_start, kmem_end); +#endif return kmem_start; } @@ -2200,6 +2209,12 @@ rs_8xx_init(); #endif /* CONFIG_8xx */ pty_init(); +#ifdef CONFIG_MOXA_SMARTIO + mxser_init(); +#endif +#ifdef CONFIG_MOXA_INTELLIO + moxa_init(); +#endif #ifdef CONFIG_VT vcs_init(); #endif diff -u --recursive --new-file v2.2.13/linux/drivers/fc4/Makefile linux/drivers/fc4/Makefile --- v2.2.13/linux/drivers/fc4/Makefile Mon Mar 15 16:11:29 1999 +++ linux/drivers/fc4/Makefile Tue Jan 4 10:12:15 2000 @@ -7,8 +7,6 @@ M_OBJS := MOD_LIST_NAME := FC4_MODULES -include ../../.config - ifeq ($(CONFIG_FC4),y) FC4 = fc.o ifeq ($(CONFIG_MODULES),y) diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/Config.in linux/drivers/isdn/Config.in --- v2.2.13/linux/drivers/isdn/Config.in Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/Config.in Tue Jan 4 10:12:15 2000 @@ -2,77 +2,78 @@ # ISDN device configuration # if [ "$CONFIG_INET" != "n" ]; then - bool 'Support synchronous PPP' CONFIG_ISDN_PPP - if [ "$CONFIG_ISDN_PPP" != "n" ]; then - bool 'Use VJ-compression with synchronous PPP' CONFIG_ISDN_PPP_VJ - bool 'Support generic MP (RFC 1717)' CONFIG_ISDN_MPP - fi + bool ' Support synchronous PPP' CONFIG_ISDN_PPP + if [ "$CONFIG_ISDN_PPP" != "n" ]; then + bool ' Use VJ-compression with synchronous PPP' CONFIG_ISDN_PPP_VJ + bool ' Support generic MP (RFC 1717)' CONFIG_ISDN_MPP + fi fi -bool 'Support audio via ISDN' CONFIG_ISDN_AUDIO +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 2 commands' CONFIG_ISDN_TTY_FAX fi -bool 'Support isdn diversion services' CONFIG_ISDN_DIVERSION +bool ' Support isdn diversion services' CONFIG_ISDN_DIVERSION if [ "$CONFIG_X25" != "n" ]; then - bool 'X.25 PLP on top of ISDN (EXPERIMENTAL)' CONFIG_ISDN_X25 + bool ' X.25 PLP on top of ISDN' CONFIG_ISDN_X25 fi -dep_tristate 'ICN 2B and 4B support' CONFIG_ISDN_DRV_ICN $CONFIG_ISDN -dep_tristate 'isdnloop support' CONFIG_ISDN_DRV_LOOP $CONFIG_ISDN -dep_tristate 'PCBIT-D support' CONFIG_ISDN_DRV_PCBIT $CONFIG_ISDN -dep_tristate 'HiSax SiemensChipSet driver support' CONFIG_ISDN_DRV_HISAX $CONFIG_ISDN +dep_tristate ' ICN 2B and 4B support' CONFIG_ISDN_DRV_ICN $CONFIG_ISDN +dep_tristate ' isdnloop support' CONFIG_ISDN_DRV_LOOP $CONFIG_ISDN +dep_tristate ' PCBIT-D support' CONFIG_ISDN_DRV_PCBIT $CONFIG_ISDN +dep_tristate ' HiSax SiemensChipSet driver support' CONFIG_ISDN_DRV_HISAX $CONFIG_ISDN if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then - bool 'HiSax Support for EURO/DSS1' CONFIG_HISAX_EURO - if [ "$CONFIG_HISAX_EURO" != "n" ]; then - bool 'Support for german chargeinfo' CONFIG_DE_AOC - bool 'Disable sending complete' CONFIG_HISAX_NO_SENDCOMPLETE - bool 'Disable sending low layer compatibility' CONFIG_HISAX_NO_LLC - fi - bool 'HiSax Support for german 1TR6' CONFIG_HISAX_1TR6 - bool 'HiSax Support for Teles 16.0/8.0' CONFIG_HISAX_16_0 - bool 'HiSax Support for Teles 16.3 or PNP or PCMCIA' CONFIG_HISAX_16_3 - bool 'HiSax Support for Teles PCI' CONFIG_HISAX_TELESPCI - bool 'HiSax Support for Teles S0Box' CONFIG_HISAX_S0BOX - bool 'HiSax Support for AVM A1 (Fritz)' CONFIG_HISAX_AVM_A1 - bool 'HiSax Support for AVM PnP/PCI (Fritz!PnP/PCI)' CONFIG_HISAX_FRITZPCI - bool 'HiSax Support for AVM A1 PCMCIA (Fritz)' CONFIG_HISAX_AVM_A1_PCMCIA - bool 'HiSax Support for Elsa cards' CONFIG_HISAX_ELSA - bool 'HiSax Support for ITK ix1-micro Revision 2' CONFIG_HISAX_IX1MICROR2 - bool 'HiSax Support for Eicon.Diehl Diva cards' CONFIG_HISAX_DIEHLDIVA - bool 'HiSax Support for ASUSCOM cards' CONFIG_HISAX_ASUSCOM - bool 'HiSax Support for TELEINT cards' CONFIG_HISAX_TELEINT - bool 'HiSax Support for HFC-S based cards' CONFIG_HISAX_HFCS - bool 'HiSax Support for Sedlbauer cards' CONFIG_HISAX_SEDLBAUER - bool 'HiSax Support for USR Sportster internal TA' CONFIG_HISAX_SPORTSTER - bool 'HiSax Support for MIC card' CONFIG_HISAX_MIC - bool 'HiSax Support for NETjet card' CONFIG_HISAX_NETJET - bool 'HiSax Support for Niccy PnP/PCI card' CONFIG_HISAX_NICCY - bool 'HiSax Support for Siemens I-Surf card' CONFIG_HISAX_ISURF - bool 'HiSax Support for HST Saphir card' CONFIG_HISAX_HSTSAPHIR - bool 'HiSax Support for Telekom A4T card' CONFIG_HISAX_BKM_A4T - bool 'HiSax Support for Scitel Quadro card' CONFIG_HISAX_SCT_QUADRO - bool 'HiSax Support for Gazel cards' CONFIG_HISAX_GAZEL - bool 'HiSax Support for HFC PCI-Bus cards' CONFIG_HISAX_HFC_PCI - if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then - bool 'HiSax Support for Winbond W6692 based cards (EXPERIMENTAL)' CONFIG_HISAX_W6692 -# bool 'HiSax Support for TESTEMULATOR (EXPERIMENTAL)' CONFIG_HISAX_TESTEMU - if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then - bool 'HiSax Support for Am7930' CONFIG_HISAX_AMD7930 - fi - fi + bool ' HiSax Support for EURO/DSS1' CONFIG_HISAX_EURO + if [ "$CONFIG_HISAX_EURO" != "n" ]; then + bool ' Support for german chargeinfo' CONFIG_DE_AOC + bool ' Disable sending complete' CONFIG_HISAX_NO_SENDCOMPLETE + bool ' Disable sending low layer compatibility' CONFIG_HISAX_NO_LLC + fi + bool ' HiSax Support for german 1TR6' CONFIG_HISAX_1TR6 + bool ' HiSax Support for Teles 16.0/8.0' CONFIG_HISAX_16_0 + bool ' HiSax Support for Teles 16.3 or PNP or PCMCIA' CONFIG_HISAX_16_3 + bool ' HiSax Support for Teles PCI' CONFIG_HISAX_TELESPCI + bool ' HiSax Support for Teles S0Box' CONFIG_HISAX_S0BOX + bool ' HiSax Support for AVM A1 (Fritz)' CONFIG_HISAX_AVM_A1 + bool ' HiSax Support for AVM PnP/PCI (Fritz!PnP/PCI)' CONFIG_HISAX_FRITZPCI + bool ' HiSax Support for AVM A1 PCMCIA (Fritz)' CONFIG_HISAX_AVM_A1_PCMCIA + bool ' HiSax Support for Elsa cards' CONFIG_HISAX_ELSA + bool ' HiSax Support for ITK ix1-micro Revision 2' CONFIG_HISAX_IX1MICROR2 + bool ' HiSax Support for Eicon.Diehl Diva cards' CONFIG_HISAX_DIEHLDIVA + bool ' HiSax Support for ASUSCOM cards' CONFIG_HISAX_ASUSCOM + bool ' HiSax Support for TELEINT cards' CONFIG_HISAX_TELEINT + bool ' HiSax Support for HFC-S based cards' CONFIG_HISAX_HFCS + bool ' HiSax Support for Sedlbauer cards' CONFIG_HISAX_SEDLBAUER + bool ' HiSax Support for USR Sportster internal TA' CONFIG_HISAX_SPORTSTER + bool ' HiSax Support for MIC card' CONFIG_HISAX_MIC + bool ' HiSax Support for NETjet card' CONFIG_HISAX_NETJET + bool ' HiSax Support for Niccy PnP/PCI card' CONFIG_HISAX_NICCY + bool ' HiSax Support for Siemens I-Surf card' CONFIG_HISAX_ISURF + bool ' HiSax Support for HST Saphir card' CONFIG_HISAX_HSTSAPHIR + bool ' HiSax Support for Telekom A4T card' CONFIG_HISAX_BKM_A4T + bool ' HiSax Support for Scitel Quadro card' CONFIG_HISAX_SCT_QUADRO + bool ' HiSax Support for Gazel cards' CONFIG_HISAX_GAZEL + bool ' HiSax Support for HFC PCI-Bus cards' CONFIG_HISAX_HFC_PCI + bool ' HiSax Support for Winbond W6692 based cards' CONFIG_HISAX_W6692 + if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then +# bool ' HiSax Support for TESTEMULATOR (EXPERIMENTAL)' CONFIG_HISAX_TESTEMU + if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then + bool ' HiSax Support for Am7930' CONFIG_HISAX_AMD7930 + fi + fi fi if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then - dep_tristate 'Spellcaster support (EXPERIMENTAL)' CONFIG_ISDN_DRV_SC $CONFIG_ISDN - dep_tristate 'IBM Active 2000 support (EXPERIMENTAL)' CONFIG_ISDN_DRV_ACT2000 $CONFIG_ISDN + dep_tristate ' Spellcaster support (EXPERIMENTAL)' CONFIG_ISDN_DRV_SC $CONFIG_ISDN + dep_tristate ' IBM Active 2000 support (EXPERIMENTAL)' CONFIG_ISDN_DRV_ACT2000 $CONFIG_ISDN fi -dep_tristate 'Eicon.Diehl active card support' CONFIG_ISDN_DRV_EICON $CONFIG_ISDN +dep_tristate ' Eicon.Diehl active card support' CONFIG_ISDN_DRV_EICON $CONFIG_ISDN if [ "$CONFIG_ISDN_DRV_EICON" != "n" ]; then - bool 'Eicon S,SX,SCOM,Quadro,S2M support' CONFIG_ISDN_DRV_EICON_ISA + 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 +dep_tristate ' AVM CAPI2.0 support' CONFIG_ISDN_DRV_AVMB1 $CONFIG_ISDN if [ "$CONFIG_ISDN_DRV_AVMB1" != "n" ]; then - bool 'AVM B1 ISA support' CONFIG_ISDN_DRV_AVMB1_B1ISA - bool 'AVM B1 PCI support' CONFIG_ISDN_DRV_AVMB1_B1PCI - bool 'AVM T1/T1B ISA support' CONFIG_ISDN_DRV_AVMB1_T1ISA - bool 'AVM B1/M1/M2 PCMCIA support' CONFIG_ISDN_DRV_AVMB1_B1PCMCIA - bool 'Verbose reason code reporting (kernel size +=7K)' CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON + bool ' AVM B1 ISA support' CONFIG_ISDN_DRV_AVMB1_B1ISA + bool ' AVM B1 PCI support' CONFIG_ISDN_DRV_AVMB1_B1PCI + bool ' AVM T1/T1-B ISA support' CONFIG_ISDN_DRV_AVMB1_T1ISA + bool ' AVM B1/M1/M2 PCMCIA support' CONFIG_ISDN_DRV_AVMB1_B1PCMCIA + bool ' AVM T1/T1-B PCI support' CONFIG_ISDN_DRV_AVMB1_T1PCI + bool ' Verbose reason code reporting (kernel size +=7K)' CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON fi diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/act2000/act2000_isa.c linux/drivers/isdn/act2000/act2000_isa.c --- v2.2.13/linux/drivers/isdn/act2000/act2000_isa.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/act2000/act2000_isa.c Tue Jan 4 10:12:15 2000 @@ -1,4 +1,4 @@ -/* $Id: act2000_isa.c,v 1.9 1999/09/04 06:20:04 keil Exp $ +/* $Id: act2000_isa.c,v 1.10 1999/10/24 18:46:05 fritz Exp $ * * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version). * @@ -20,6 +20,10 @@ * 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() * @@ -64,14 +68,14 @@ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -static int isa_irqs[] = +static int act2000_isa_irqs[] = { 3, 5, 7, 10, 11, 12, 15 }; -#define ISA_NRIRQS (sizeof(isa_irqs)/sizeof(int)) +#define ISA_NRIRQS (sizeof(act2000_isa_irqs)/sizeof(int)) static void -isa_delay(long t) +act2000_isa_delay(long t) { sti(); current->state = TASK_INTERRUPTIBLE; @@ -86,7 +90,7 @@ * 0 = Signature not found. */ static int -isa_reset(unsigned short portbase) +act2000_isa_reset(unsigned short portbase) { unsigned char reg; int i; @@ -112,7 +116,7 @@ } int -isa_detect(unsigned short portbase) +act2000_isa_detect(unsigned short portbase) { int ret = 0; unsigned long flags; @@ -120,13 +124,13 @@ save_flags(flags); cli(); if (!check_region(portbase, ISA_REGION)) - ret = isa_reset(portbase); + ret = act2000_isa_reset(portbase); restore_flags(flags); return ret; } static void -isa_interrupt(int irq, void *dev_id, struct pt_regs *regs) +act2000_isa_interrupt(int irq, void *dev_id, struct pt_regs *regs) { act2000_card *card = irq2card_map[irq]; u_char istatus; @@ -141,7 +145,7 @@ /* RX fifo has data */ istatus &= ISA_ISR_OUT_MASK; outb(0, ISA_PORT_SIS); - isa_receive(card); + act2000_isa_receive(card); outb(ISA_SIS_INT, ISA_PORT_SIS); } if (istatus & ISA_ISR_ERR) { @@ -154,7 +158,7 @@ } static void -isa_select_irq(act2000_card * card) +act2000_isa_select_irq(act2000_card * card) { unsigned char reg; @@ -186,9 +190,9 @@ } static void -isa_enable_irq(act2000_card * card) +act2000_isa_enable_irq(act2000_card * card) { - isa_select_irq(card); + act2000_isa_select_irq(card); /* Enable READ irq */ outb(ISA_SIS_INT, ISA_PORT_SIS); } @@ -198,7 +202,7 @@ * If irq is -1, choose next free irq, else irq is given explicitely. */ int -isa_config_irq(act2000_card * card, short irq) +act2000_isa_config_irq(act2000_card * card, short irq) { int i; unsigned long flags; @@ -216,8 +220,8 @@ if (irq == -1) { /* Auto select */ for (i = 0; i < ISA_NRIRQS; i++) { - if (!request_irq(isa_irqs[i], &isa_interrupt, 0, card->regname, NULL)) { - card->irq = isa_irqs[i]; + if (!request_irq(act2000_isa_irqs[i], &act2000_isa_interrupt, 0, card->regname, NULL)) { + card->irq = act2000_isa_irqs[i]; irq2card_map[card->irq] = card; card->flags |= ACT2000_FLAGS_IVALID; break; @@ -225,7 +229,7 @@ } } else { /* Fixed irq */ - if (!request_irq(irq, &isa_interrupt, 0, card->regname, NULL)) { + if (!request_irq(irq, &act2000_isa_interrupt, 0, card->regname, NULL)) { card->irq = irq; irq2card_map[card->irq] = card; card->flags |= ACT2000_FLAGS_IVALID; @@ -237,7 +241,7 @@ "act2000: Could not request irq\n"); return -EBUSY; } else { - isa_select_irq(card); + act2000_isa_select_irq(card); /* Disable READ and WRITE irq */ outb(0, ISA_PORT_SIS); outb(0, ISA_PORT_SOS); @@ -246,7 +250,7 @@ } int -isa_config_port(act2000_card * card, unsigned short portbase) +act2000_isa_config_port(act2000_card * card, unsigned short portbase) { if (card->flags & ACT2000_FLAGS_PVALID) { release_region(card->port, ISA_REGION); @@ -265,7 +269,7 @@ * Release ressources, used by an adaptor. */ void -isa_release(act2000_card * card) +act2000_isa_release(act2000_card * card) { unsigned long flags; @@ -283,7 +287,7 @@ } static int -isa_writeb(act2000_card * card, u_char data) +act2000_isa_writeb(act2000_card * card, u_char data) { u_char timeout = 40; @@ -300,7 +304,7 @@ } static int -isa_readb(act2000_card * card, u_char * data) +act2000_isa_readb(act2000_card * card, u_char * data) { u_char timeout = 40; @@ -317,13 +321,13 @@ } void -isa_receive(act2000_card *card) +act2000_isa_receive(act2000_card *card) { u_char c; if (test_and_set_bit(ACT2000_LOCK_RX, (void *) &card->ilock) != 0) return; - while (!isa_readb(card, &c)) { + while (!act2000_isa_readb(card, &c)) { if (card->idat.isa.rcvidx < 8) { card->idat.isa.rcvhdr[card->idat.isa.rcvidx++] = c; if (card->idat.isa.rcvidx == 8) { @@ -335,7 +339,7 @@ if (card->idat.isa.rcvskb == NULL) { card->idat.isa.rcvignore = 1; printk(KERN_WARNING - "isa_receive: no memory\n"); + "act2000_isa_receive: no memory\n"); test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock); return; } @@ -344,12 +348,12 @@ } else { card->idat.isa.rcvidx = 0; printk(KERN_WARNING - "isa_receive: Invalid CAPI msg\n"); + "act2000_isa_receive: Invalid CAPI msg\n"); { int i; __u8 *p; __u8 *c; __u8 tmp[30]; for (i = 0, p = (__u8 *)&card->idat.isa.rcvhdr, c = tmp; i < 8; i++) c += sprintf(c, "%02x ", *(p++)); - printk(KERN_WARNING "isa_receive: %s\n", tmp); + printk(KERN_WARNING "act2000_isa_receive: %s\n", tmp); } } } @@ -380,7 +384,7 @@ } void -isa_send(act2000_card * card) +act2000_isa_send(act2000_card * card) { unsigned long flags; struct sk_buff *skb; @@ -413,7 +417,7 @@ skb = card->sbuf; l = 0; while (skb->len) { - if (isa_writeb(card, *(skb->data))) { + if (act2000_isa_writeb(card, *(skb->data))) { /* Fifo is full, but more data to send */ test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock); /* Schedule myself */ @@ -444,7 +448,7 @@ * Get firmware ID, check for 'ISDN' signature. */ static int -isa_getid(act2000_card * card) +act2000_isa_getid(act2000_card * card) { act2000_fwid fid; @@ -454,7 +458,7 @@ while (1) { if (count > 510) return -EPROTO; - if (isa_readb(card, p++)) + if (act2000_isa_readb(card, p++)) break; count++; } @@ -473,7 +477,7 @@ printk(KERN_INFO "act2000: Firmware-ID: %s\n", fid.revision); if (card->flags & ACT2000_FLAGS_IVALID) { printk(KERN_DEBUG "Enabling Interrupts ...\n"); - isa_enable_irq(card); + act2000_isa_enable_irq(card); } return 0; } @@ -482,7 +486,7 @@ * Download microcode into card, check Firmware signature. */ int -isa_download(act2000_card * card, act2000_ddef * cb) +act2000_isa_download(act2000_card * card, act2000_ddef * cb) { int length; int ret; @@ -494,9 +498,9 @@ u_char *buf; act2000_ddef cblock; - if (!isa_reset(card->port)) + if (!act2000_isa_reset(card->port)) return -ENXIO; - isa_delay(HZ / 2); + act2000_isa_delay(HZ / 2); if ((ret = verify_area(VERIFY_READ, (void *) cb, sizeof(cblock)))) return ret; copy_from_user(&cblock, (char *) cb, sizeof(cblock)); @@ -514,7 +518,7 @@ b = buf; copy_from_user(buf, p, l); while (c < l) { - if (isa_writeb(card, *b++)) { + if (act2000_isa_writeb(card, *b++)) { printk(KERN_WARNING "act2000: loader timed out" " len=%d c=%d\n", length, c); @@ -527,6 +531,6 @@ p += l; } kfree(buf); - isa_delay(HZ / 2); - return (isa_getid(card)); + act2000_isa_delay(HZ / 2); + return (act2000_isa_getid(card)); } diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/act2000/act2000_isa.h linux/drivers/isdn/act2000/act2000_isa.h --- v2.2.13/linux/drivers/isdn/act2000/act2000_isa.h Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/act2000/act2000_isa.h Tue Jan 4 10:12:15 2000 @@ -1,4 +1,4 @@ -/* $Id: act2000_isa.h,v 1.2 1998/11/05 22:12:43 fritz Exp $ +/* $Id: act2000_isa.h,v 1.3 1999/10/24 18:46:05 fritz Exp $ * * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version). * @@ -20,6 +20,10 @@ * 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. * @@ -141,12 +145,12 @@ /* Prototypes */ -extern int isa_detect(unsigned short portbase); -extern int isa_config_irq(act2000_card * card, short irq); -extern int isa_config_port(act2000_card * card, unsigned short portbase); -extern int isa_download(act2000_card * card, act2000_ddef * cb); -extern void isa_release(act2000_card * card); -extern void isa_receive(act2000_card *card); -extern void isa_send(act2000_card *card); +extern int act2000_isa_detect(unsigned short portbase); +extern int act2000_isa_config_irq(act2000_card * card, short irq); +extern int act2000_isa_config_port(act2000_card * card, unsigned short portbase); +extern int act2000_isa_download(act2000_card * card, act2000_ddef * cb); +extern void act2000_isa_release(act2000_card * card); +extern void act2000_isa_receive(act2000_card *card); +extern void act2000_isa_send(act2000_card *card); #endif /* act2000_isa_h */ diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/act2000/module.c linux/drivers/isdn/act2000/module.c --- v2.2.13/linux/drivers/isdn/act2000/module.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/act2000/module.c Tue Jan 4 10:12:15 2000 @@ -1,4 +1,4 @@ -/* $Id: module.c,v 1.9 1999/04/12 13:13:56 fritz Exp $ +/* $Id: module.c,v 1.11 1999/10/30 09:48:04 keil Exp $ * * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000. * @@ -20,6 +20,13 @@ * 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. * @@ -56,12 +63,12 @@ #include "act2000_isa.h" #include "capi.h" -static unsigned short isa_ports[] = +static unsigned short act2000_isa_ports[] = { 0x0200, 0x0240, 0x0280, 0x02c0, 0x0300, 0x0340, 0x0380, 0xcfe0, 0xcfa0, 0xcf60, 0xcf20, 0xcee0, 0xcea0, 0xce60, }; -#define ISA_NRPORTS (sizeof(isa_ports)/sizeof(unsigned short)) +#define ISA_NRPORTS (sizeof(act2000_isa_ports)/sizeof(unsigned short)) static act2000_card *cards = (act2000_card *) NULL; @@ -238,7 +245,7 @@ { switch (card->bus) { case ACT2000_BUS_ISA: - isa_send(card); + act2000_isa_send(card); break; case ACT2000_BUS_PCMCIA: case ACT2000_BUS_MCA: @@ -253,7 +260,7 @@ { switch (card->bus) { case ACT2000_BUS_ISA: - isa_receive(card); + act2000_isa_receive(card); break; case ACT2000_BUS_PCMCIA: case ACT2000_BUS_MCA: @@ -296,7 +303,7 @@ case ACT2000_IOCTL_LOADBOOT: switch (card->bus) { case ACT2000_BUS_ISA: - ret = isa_download(card, + ret = act2000_isa_download(card, (act2000_ddef *)a); if (!ret) { card->flags |= ACT2000_FLAGS_LOADED; @@ -758,7 +765,7 @@ card->interface.statcallb(&cmd); switch (card->bus) { case ACT2000_BUS_ISA: - isa_release(card); + act2000_isa_release(card); break; case ACT2000_BUS_MCA: case ACT2000_BUS_PCMCIA: @@ -792,11 +799,11 @@ switch (bus) { case ACT2000_BUS_ISA: for (i = 0; i < ISA_NRPORTS; i++) - if (isa_detect(isa_ports[i])) { + if (act2000_isa_detect(act2000_isa_ports[i])) { printk(KERN_INFO "act2000: Detected ISA card at port 0x%x\n", - isa_ports[i]); - act2000_alloccard(bus, isa_ports[i], irq, id); + act2000_isa_ports[i]); + act2000_alloccard(bus, act2000_isa_ports[i], irq, id); } break; case ACT2000_BUS_MCA: @@ -819,10 +826,10 @@ added++; switch (p->bus) { case ACT2000_BUS_ISA: - if (isa_detect(p->port)) { + if (act2000_isa_detect(p->port)) { if (act2000_registercard(p)) break; - if (isa_config_port(p, p->port)) { + if (act2000_isa_config_port(p, p->port)) { printk(KERN_WARNING "act2000: Could not request port 0x%04x\n", p->port); @@ -830,7 +837,7 @@ p->interface.statcallb = NULL; break; } - if (isa_config_irq(p, p->irq)) { + if (act2000_isa_config_irq(p, p->irq)) { printk(KERN_INFO "act2000: No IRQ available, fallback to polling\n"); /* Fall back to polled operation */ diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/avmb1/avmcard.h linux/drivers/isdn/avmb1/avmcard.h --- v2.2.13/linux/drivers/isdn/avmb1/avmcard.h Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/avmb1/avmcard.h Tue Jan 4 10:12:15 2000 @@ -1,9 +1,16 @@ /* - * $Id: avmcard.h,v 1.5 1999/09/07 09:02:53 calle Exp $ + * $Id: avmcard.h,v 1.6 1999/11/05 16:38:01 calle Exp $ * * Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: avmcard.h,v $ + * Revision 1.6 1999/11/05 16:38:01 calle + * Cleanups before kernel 2.4: + * - Changed all messages to use card->name or driver->name instead of + * constant string. + * - Moved some data from struct avmcard into new struct avmctrl_info. + * Changed all lowlevel capi driver to match the new structur. + * * Revision 1.5 1999/09/07 09:02:53 calle * SETDATA removed. Now inside the kernel the datapart of DATA_B3_REQ and * DATA_B3_IND is always directly after the CAPI message. The "Data" member @@ -78,6 +85,7 @@ __u8 sendbuf[128+2048]; } avmcard_dmainfo; + typedef struct avmcard { char name[32]; unsigned int port; @@ -86,13 +94,6 @@ enum avmcardtype cardtype; int cardnr; /* for t1isa */ - int versionlen; - char versionbuf[1024]; - char *version[AVM_MAXVERSION]; - - char cardname[32]; - - char infobuf[128]; /* for function procinfo */ char msgbuf[128]; /* capimsg msg part */ char databuf[2048]; /* capimsg data part */ @@ -102,9 +103,25 @@ volatile __u32 csr; avmcard_dmainfo *dma; - struct capi_ctr *ctrl; + struct avmctrl_info { + char cardname[32]; + + int versionlen; + char versionbuf[1024]; + char *version[AVM_MAXVERSION]; + + char infobuf[128]; /* for function procinfo */ + + struct avmcard *card; + struct capi_ctr *capi_ctrl; + + } *ctrlinfo; + + int nlogcontr; } avmcard; +typedef struct avmctrl_info avmctrl_info; + extern int b1_irq_table[16]; /* @@ -545,16 +562,16 @@ } int b1_detect(unsigned int base, enum avmcardtype cardtype); -int b1_load_t4file(unsigned int base, capiloaddatapart * t4file); -int b1_load_config(unsigned int base, capiloaddatapart * config); -int b1_loaded(unsigned int base); +int b1_load_t4file(avmcard *card, capiloaddatapart * t4file); +int b1_load_config(avmcard *card, capiloaddatapart * config); +int b1_loaded(avmcard *card); int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data); void b1_reset_ctr(struct capi_ctr *ctrl); void b1_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp); void b1_release_appl(struct capi_ctr *ctrl, __u16 appl); void b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb); -void b1_parse_version(avmcard *card); +void b1_parse_version(avmctrl_info *card); void b1_handle_interrupt(avmcard * card); int b1ctl_read_proc(char *page, char **start, off_t off, diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/avmb1/b1.c linux/drivers/isdn/avmb1/b1.c --- v2.2.13/linux/drivers/isdn/avmb1/b1.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/avmb1/b1.c Tue Jan 4 10:12:15 2000 @@ -1,11 +1,21 @@ /* - * $Id: b1.c,v 1.10 1999/09/15 08:16:03 calle Exp $ + * $Id: b1.c,v 1.12 1999/11/05 16:38:01 calle Exp $ * * Common module for AVM B1 cards. * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: b1.c,v $ + * Revision 1.12 1999/11/05 16:38:01 calle + * Cleanups before kernel 2.4: + * - Changed all messages to use card->name or driver->name instead of + * constant string. + * - Moved some data from struct avmcard into new struct avmctrl_info. + * Changed all lowlevel capi driver to match the new structur. + * + * Revision 1.11 1999/10/11 22:04:12 keil + * COMPAT_NEED_UACCESS (no include in isdn_compat.h) + * * Revision 1.10 1999/09/15 08:16:03 calle * Implementation of 64Bit extention complete. * @@ -76,7 +86,7 @@ #include "capicmd.h" #include "capiutil.h" -static char *revision = "$Revision: 1.10 $"; +static char *revision = "$Revision: 1.12 $"; /* ------------------------------------------------------------- */ @@ -148,11 +158,12 @@ return 0; } -int b1_load_t4file(unsigned int base, capiloaddatapart * t4file) +int b1_load_t4file(avmcard *card, capiloaddatapart * t4file) { unsigned char buf[256]; unsigned char *dp; int i, left, retval; + unsigned int base = card->port; dp = t4file->data; left = t4file->len; @@ -166,7 +177,8 @@ } for (i = 0; i < sizeof(buf); i++) if (b1_save_put_byte(base, buf[i]) < 0) { - printk(KERN_ERR "b1_load_t4file: corrupted t4 file ?\n"); + printk(KERN_ERR "%s: corrupted firmware file ?\n", + card->name); return -EIO; } left -= sizeof(buf); @@ -182,17 +194,19 @@ } for (i = 0; i < left; i++) if (b1_save_put_byte(base, buf[i]) < 0) { - printk(KERN_ERR "b1_load_t4file: corrupted t4 file ?\n"); + printk(KERN_ERR "%s: corrupted firmware file ?\n", + card->name); return -EIO; } } return 0; } -int b1_load_config(unsigned int base, capiloaddatapart * config) +int b1_load_config(avmcard *card, capiloaddatapart * config) { unsigned char buf[256]; unsigned char *dp; + unsigned int base = card->port; int i, j, left, retval; dp = config->data; @@ -241,8 +255,9 @@ return 0; } -int b1_loaded(unsigned int base) +int b1_loaded(avmcard *card) { + unsigned int base = card->port; unsigned long stop; unsigned char ans; unsigned long tout = 2; @@ -252,7 +267,8 @@ break; } if (!b1_tx_empty(base)) { - printk(KERN_ERR "b1_loaded: tx err, corrupted t4 file ?\n"); + printk(KERN_ERR "%s: b1_loaded: tx err, corrupted t4 file ?\n", + card->name); return 0; } b1_put_byte(base, SEND_POLL); @@ -261,11 +277,12 @@ if ((ans = b1_get_byte(base)) == RECEIVE_POLL) { return 1; } - printk(KERN_ERR "b1_loaded: got 0x%x, firmware not running\n", ans); + printk(KERN_ERR "%s: b1_loaded: got 0x%x, firmware not running\n", + card->name, ans); return 0; } } - printk(KERN_ERR "b1_loaded: firmware not running\n"); + printk(KERN_ERR "%s: b1_loaded: firmware not running\n", card->name); return 0; } @@ -273,14 +290,15 @@ int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) { - avmcard *card = (avmcard *)(ctrl->driverdata); + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; unsigned int port = card->port; unsigned long flags; int retval; b1_reset(port); - if ((retval = b1_load_t4file(port, &data->firmware))) { + if ((retval = b1_load_t4file(card, &data->firmware))) { b1_reset(port); printk(KERN_ERR "%s: failed to load t4file!!\n", card->name); @@ -290,7 +308,7 @@ b1_disable_irq(port); if (data->configuration.len > 0 && data->configuration.data) { - if ((retval = b1_load_config(port, &data->configuration))) { + if ((retval = b1_load_config(card, &data->configuration))) { b1_reset(port); printk(KERN_ERR "%s: failed to load config!!\n", card->name); @@ -298,7 +316,7 @@ } } - if (!b1_loaded(port)) { + if (!b1_loaded(card)) { printk(KERN_ERR "%s: failed to load t4file.\n", card->name); return -EIO; } @@ -317,13 +335,14 @@ void b1_reset_ctr(struct capi_ctr *ctrl) { - avmcard *card = (avmcard *)(ctrl->driverdata); + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; unsigned int port = card->port; b1_reset(port); b1_reset(port); - memset(card->version, 0, sizeof(card->version)); + memset(cinfo->version, 0, sizeof(cinfo->version)); ctrl->reseted(ctrl); } @@ -331,7 +350,8 @@ __u16 appl, capi_register_params *rp) { - avmcard *card = (avmcard *)(ctrl->driverdata); + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; unsigned int port = card->port; unsigned long flags; int nconn, want = rp->level3cnt; @@ -355,7 +375,8 @@ void b1_release_appl(struct capi_ctr *ctrl, __u16 appl) { - avmcard *card = (avmcard *)(ctrl->driverdata); + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; unsigned int port = card->port; unsigned long flags; @@ -368,7 +389,8 @@ void b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) { - avmcard *card = (avmcard *)(ctrl->driverdata); + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; unsigned int port = card->port; unsigned long flags; __u16 len = CAPIMSG_LEN(skb->data); @@ -392,25 +414,26 @@ /* ------------------------------------------------------------- */ -void b1_parse_version(avmcard *card) +void b1_parse_version(avmctrl_info *cinfo) { - struct capi_ctr *ctrl = card->ctrl; + struct capi_ctr *ctrl = cinfo->capi_ctrl; + avmcard *card = cinfo->card; capi_profile *profp; __u8 *dversion; __u8 flag; int i, j; for (j = 0; j < AVM_MAXVERSION; j++) - card->version[j] = "\0\0" + 1; + cinfo->version[j] = "\0\0" + 1; for (i = 0, j = 0; - j < AVM_MAXVERSION && i < card->versionlen; - j++, i += card->versionbuf[i] + 1) - card->version[j] = &card->versionbuf[i + 1]; + j < AVM_MAXVERSION && i < cinfo->versionlen; + j++, i += cinfo->versionbuf[i] + 1) + cinfo->version[j] = &cinfo->versionbuf[i + 1]; - strncpy(ctrl->serial, card->version[VER_SERIAL], CAPI_SERIAL_LEN); - memcpy(&ctrl->profile, card->version[VER_PROFILE],sizeof(capi_profile)); + strncpy(ctrl->serial, cinfo->version[VER_SERIAL], CAPI_SERIAL_LEN); + memcpy(&ctrl->profile, cinfo->version[VER_PROFILE],sizeof(capi_profile)); strncpy(ctrl->manu, "AVM GmbH", CAPI_MANUFACTURER_LEN); - dversion = card->version[VER_DRIVER]; + dversion = cinfo->version[VER_DRIVER]; ctrl->version.majorversion = 2; ctrl->version.minorversion = 0; ctrl->version.majormanuversion = (((dversion[0] - '0') & 0xf) << 4); @@ -423,23 +446,24 @@ flag = ((__u8 *)(profp->manu))[1]; switch (flag) { - case 0: if (card->version[VER_CARDTYPE]) - strcpy(card->cardname, card->version[VER_CARDTYPE]); - else strcpy(card->cardname, "B1"); + case 0: if (cinfo->version[VER_CARDTYPE]) + strcpy(cinfo->cardname, cinfo->version[VER_CARDTYPE]); + else strcpy(cinfo->cardname, "B1"); break; - case 3: strcpy(card->cardname,"PCMCIA B"); break; - case 4: strcpy(card->cardname,"PCMCIA M1"); break; - case 5: strcpy(card->cardname,"PCMCIA M2"); break; - case 6: strcpy(card->cardname,"B1 V3.0"); break; - case 7: strcpy(card->cardname,"B1 PCI"); break; - default: sprintf(card->cardname, "AVM?%u", (unsigned int)flag); break; + case 3: strcpy(cinfo->cardname,"PCMCIA B"); break; + case 4: strcpy(cinfo->cardname,"PCMCIA M1"); break; + case 5: strcpy(cinfo->cardname,"PCMCIA M2"); break; + case 6: strcpy(cinfo->cardname,"B1 V3.0"); break; + case 7: strcpy(cinfo->cardname,"B1 PCI"); break; + default: sprintf(cinfo->cardname, "AVM?%u", (unsigned int)flag); break; } printk(KERN_NOTICE "%s: card %d \"%s\" ready.\n", - card->name, ctrl->cnr, card->cardname); + card->name, ctrl->cnr, cinfo->cardname); flag = ((__u8 *)(profp->manu))[3]; if (flag) - printk(KERN_NOTICE "b1capi: card %d Protocol:%s%s%s%s%s%s%s\n", + printk(KERN_NOTICE "%s: card %d Protocol:%s%s%s%s%s%s%s\n", + card->name, ctrl->cnr, (flag & 0x01) ? " DSS1" : "", (flag & 0x02) ? " CT1" : "", @@ -466,7 +490,8 @@ void b1_handle_interrupt(avmcard * card) { - struct capi_ctr *ctrl = card->ctrl; + avmctrl_info *cinfo = &card->ctrlinfo[0]; + struct capi_ctr *ctrl = cinfo->capi_ctrl; unsigned char b1cmd; struct sk_buff *skb; @@ -548,12 +573,12 @@ case RECEIVE_INIT: - card->versionlen = b1_get_slice(card->port, card->versionbuf); - b1_parse_version(card); + cinfo->versionlen = b1_get_slice(card->port, cinfo->versionbuf); + b1_parse_version(cinfo); printk(KERN_INFO "%s: %s-card (%s) now active\n", card->name, - card->version[VER_CARDTYPE], - card->version[VER_DRIVER]); + cinfo->version[VER_CARDTYPE], + cinfo->version[VER_DRIVER]); ctrl->ready(ctrl); break; @@ -593,7 +618,8 @@ int b1ctl_read_proc(char *page, char **start, off_t off, int count, int *eof, struct capi_ctr *ctrl) { - avmcard *card = (avmcard *)(ctrl->driverdata); + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; __u8 flag; int len = 0; char *s; @@ -615,11 +641,11 @@ len += sprintf(page+len, "%-16s %s\n", "type", s); if (card->cardtype == avm_t1isa) len += sprintf(page+len, "%-16s %d\n", "cardnr", card->cardnr); - if ((s = card->version[VER_DRIVER]) != 0) + if ((s = cinfo->version[VER_DRIVER]) != 0) len += sprintf(page+len, "%-16s %s\n", "ver_driver", s); - if ((s = card->version[VER_CARDTYPE]) != 0) + if ((s = cinfo->version[VER_CARDTYPE]) != 0) len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s); - if ((s = card->version[VER_SERIAL]) != 0) + if ((s = cinfo->version[VER_SERIAL]) != 0) len += sprintf(page+len, "%-16s %s\n", "ver_serial", s); if (card->cardtype != avm_m1) { @@ -647,7 +673,7 @@ (flag & 0x04) ? " leased line with D-channel" : "" ); } - len += sprintf(page+len, "%-16s %s\n", "cardname", card->cardname); + len += sprintf(page+len, "%-16s %s\n", "cardname", cinfo->cardname); if (off+count >= len) *eof = 1; diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/avmb1/b1isa.c linux/drivers/isdn/avmb1/b1isa.c --- v2.2.13/linux/drivers/isdn/avmb1/b1isa.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/avmb1/b1isa.c Tue Jan 4 10:12:15 2000 @@ -1,11 +1,18 @@ /* - * $Id: b1isa.c,v 1.4 1999/08/22 20:26:24 calle Exp $ + * $Id: b1isa.c,v 1.5 1999/11/05 16:38:01 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.5 1999/11/05 16:38:01 calle + * Cleanups before kernel 2.4: + * - Changed all messages to use card->name or driver->name instead of + * constant string. + * - Moved some data from struct avmcard into new struct avmctrl_info. + * Changed all lowlevel capi driver to match the new structur. + * * Revision 1.4 1999/08/22 20:26:24 calle * backported changes from kernel 2.3.14: * - several #include "config.h" gone, others come. @@ -54,7 +61,7 @@ #include "capilli.h" #include "avmcard.h" -static char *revision = "$Revision: 1.4 $"; +static char *revision = "$Revision: 1.5 $"; /* ------------------------------------------------------------- */ @@ -91,7 +98,8 @@ static void b1isa_remove_ctr(struct capi_ctr *ctrl) { - avmcard *card = (avmcard *)(ctrl->driverdata); + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; unsigned int port = card->port; b1_reset(port); @@ -100,6 +108,7 @@ di->detach_ctr(ctrl); free_irq(card->irq, card); release_region(card->port, AVMB1_PORTLEN); + kfree(card->ctrlinfo); kfree(card); MOD_DEC_USE_COUNT; @@ -109,6 +118,7 @@ static int b1isa_add_card(struct capi_driver *driver, struct capicardparams *p) { + avmctrl_info *cinfo; avmcard *card; int retval; @@ -119,6 +129,15 @@ return -ENOMEM; } memset(card, 0, sizeof(avmcard)); + cinfo = (avmctrl_info *) kmalloc(sizeof(avmctrl_info), GFP_ATOMIC); + if (!cinfo) { + printk(KERN_WARNING "b1isa: no memory.\n"); + kfree(card); + return -ENOMEM; + } + memset(cinfo, 0, sizeof(avmctrl_info)); + card->ctrlinfo = cinfo; + cinfo->card = card; sprintf(card->name, "b1isa-%x", p->port); card->port = p->port; card->irq = p->irq; @@ -128,17 +147,20 @@ printk(KERN_WARNING "b1isa: ports 0x%03x-0x%03x in use.\n", card->port, card->port + AVMB1_PORTLEN); + kfree(card->ctrlinfo); kfree(card); return -EBUSY; } if (b1_irq_table[card->irq & 0xf] == 0) { printk(KERN_WARNING "b1isa: irq %d not valid.\n", card->irq); + kfree(card->ctrlinfo); kfree(card); return -EINVAL; } if ( card->port != 0x150 && card->port != 0x250 && card->port != 0x300 && card->port != 0x340) { printk(KERN_WARNING "b1isa: illegal port 0x%x.\n", card->port); + kfree(card->ctrlinfo); kfree(card); return -EINVAL; } @@ -146,6 +168,7 @@ if ((retval = b1_detect(card->port, card->cardtype)) != 0) { printk(KERN_NOTICE "b1isa: NO card at 0x%x (%d)\n", card->port, retval); + kfree(card->ctrlinfo); kfree(card); return -EIO; } @@ -157,15 +180,17 @@ if (retval) { printk(KERN_ERR "b1isa: unable to get IRQ %d.\n", card->irq); release_region(card->port, AVMB1_PORTLEN); + kfree(card->ctrlinfo); kfree(card); return -EBUSY; } - card->ctrl = di->attach_ctr(driver, card->name, card); - if (!card->ctrl) { + cinfo->capi_ctrl = di->attach_ctr(driver, card->name, cinfo); + if (!cinfo->capi_ctrl) { printk(KERN_ERR "b1isa: attach controller failed.\n"); free_irq(card->irq, card); release_region(card->port, AVMB1_PORTLEN); + kfree(card->ctrlinfo); kfree(card); return -EBUSY; } @@ -176,15 +201,17 @@ static char *b1isa_procinfo(struct capi_ctr *ctrl) { - avmcard *card = (avmcard *)(ctrl->driverdata); - if (!card) + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + + if (!cinfo) return ""; - sprintf(card->infobuf, "%s %s 0x%x %d", - card->cardname[0] ? card->cardname : "-", - card->version[VER_DRIVER] ? card->version[VER_DRIVER] : "-", - card->port, card->irq + sprintf(cinfo->infobuf, "%s %s 0x%x %d", + cinfo->cardname[0] ? cinfo->cardname : "-", + cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", + cinfo->card ? cinfo->card->port : 0x0, + cinfo->card ? cinfo->card->irq : 0 ); - return card->infobuf; + return cinfo->infobuf; } /* ------------------------------------------------------------- */ diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/avmb1/b1pci.c linux/drivers/isdn/avmb1/b1pci.c --- v2.2.13/linux/drivers/isdn/avmb1/b1pci.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/avmb1/b1pci.c Tue Jan 4 10:12:15 2000 @@ -1,11 +1,21 @@ /* - * $Id: b1pci.c,v 1.16 1999/08/11 21:01:07 keil Exp $ + * $Id: b1pci.c,v 1.18 1999/11/05 16:38:01 calle Exp $ * * Module for AVM B1 PCI-card. * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: b1pci.c,v $ + * Revision 1.18 1999/11/05 16:38:01 calle + * Cleanups before kernel 2.4: + * - Changed all messages to use card->name or driver->name instead of + * constant string. + * - Moved some data from struct avmcard into new struct avmctrl_info. + * Changed all lowlevel capi driver to match the new structur. + * + * Revision 1.17 1999/10/05 06:50:07 calle + * Forgot SA_SHIRQ as argument to request_irq. + * * Revision 1.16 1999/08/11 21:01:07 keil * new PCI codefix * @@ -56,7 +66,7 @@ #include "capilli.h" #include "avmcard.h" -static char *revision = "$Revision: 1.16 $"; +static char *revision = "$Revision: 1.18 $"; /* ------------------------------------------------------------- */ @@ -85,11 +95,11 @@ card = (avmcard *) devptr; if (!card) { - printk(KERN_WARNING "b1_interrupt: wrong device\n"); + printk(KERN_WARNING "b1pci: interrupt: wrong device\n"); return; } if (card->interrupt) { - printk(KERN_ERR "b1_interrupt: reentering interrupt hander (%s)\n", card->name); + printk(KERN_ERR "%s: reentering interrupt hander.\n", card->name); return; } @@ -103,7 +113,8 @@ static void b1pci_remove_ctr(struct capi_ctr *ctrl) { - avmcard *card = (avmcard *)(ctrl->driverdata); + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; unsigned int port = card->port; b1_reset(port); @@ -113,6 +124,7 @@ free_irq(card->irq, card); release_region(card->port, AVMB1_PORTLEN); ctrl->driverdata = 0; + kfree(card->ctrlinfo); kfree(card); MOD_DEC_USE_COUNT; @@ -122,15 +134,17 @@ static char *b1pci_procinfo(struct capi_ctr *ctrl) { - avmcard *card = (avmcard *)(ctrl->driverdata); - if (!card) + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + + if (!cinfo) return ""; - sprintf(card->infobuf, "%s %s 0x%x %d", - card->cardname[0] ? card->cardname : "-", - card->version[VER_DRIVER] ? card->version[VER_DRIVER] : "-", - card->port, card->irq + sprintf(cinfo->infobuf, "%s %s 0x%x %d", + cinfo->cardname[0] ? cinfo->cardname : "-", + cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", + cinfo->card ? cinfo->card->port : 0x0, + cinfo->card ? cinfo->card->irq : 0 ); - return card->infobuf; + return cinfo->infobuf; } /* ------------------------------------------------------------- */ @@ -138,15 +152,25 @@ static int b1pci_add_card(struct capi_driver *driver, struct capicardparams *p) { avmcard *card; + avmctrl_info *cinfo; int retval; card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC); if (!card) { - printk(KERN_WARNING "b1pci: no memory.\n"); + printk(KERN_WARNING "%s: no memory.\n", driver->name); return -ENOMEM; } memset(card, 0, sizeof(avmcard)); + cinfo = (avmctrl_info *) kmalloc(sizeof(avmctrl_info), GFP_ATOMIC); + if (!cinfo) { + printk(KERN_WARNING "%s: no memory.\n", driver->name); + kfree(card); + return -ENOMEM; + } + memset(cinfo, 0, sizeof(avmctrl_info)); + card->ctrlinfo = cinfo; + cinfo->card = card; sprintf(card->name, "b1pci-%x", p->port); card->port = p->port; card->irq = p->irq; @@ -154,15 +178,17 @@ if (check_region(card->port, AVMB1_PORTLEN)) { printk(KERN_WARNING - "b1pci: ports 0x%03x-0x%03x in use.\n", - card->port, card->port + AVMB1_PORTLEN); + "%s: ports 0x%03x-0x%03x in use.\n", + driver->name, card->port, card->port + AVMB1_PORTLEN); + kfree(card->ctrlinfo); kfree(card); return -EBUSY; } b1_reset(card->port); if ((retval = b1_detect(card->port, card->cardtype)) != 0) { - printk(KERN_NOTICE "b1pci: NO card at 0x%x (%d)\n", - card->port, retval); + printk(KERN_NOTICE "%s: NO card at 0x%x (%d)\n", + driver->name, card->port, retval); + kfree(card->ctrlinfo); kfree(card); return -EIO; } @@ -170,19 +196,23 @@ request_region(p->port, AVMB1_PORTLEN, card->name); - retval = request_irq(card->irq, b1pci_interrupt, 0, card->name, card); + retval = request_irq(card->irq, b1pci_interrupt, SA_SHIRQ, card->name, card); if (retval) { - printk(KERN_ERR "b1pci: unable to get IRQ %d.\n", card->irq); + printk(KERN_ERR "%s: unable to get IRQ %d.\n", + driver->name, card->irq); release_region(card->port, AVMB1_PORTLEN); + kfree(card->ctrlinfo); kfree(card); return -EBUSY; } - card->ctrl = di->attach_ctr(driver, card->name, card); - if (!card->ctrl) { - printk(KERN_ERR "b1pci: attach controller failed.\n"); + cinfo->capi_ctrl = di->attach_ctr(driver, card->name, cinfo); + if (!cinfo->capi_ctrl) { + printk(KERN_ERR "%s: attach controller failed.\n", + driver->name); free_irq(card->irq, card); release_region(card->port, AVMB1_PORTLEN); + kfree(card->ctrlinfo); kfree(card); return -EBUSY; } @@ -276,7 +306,7 @@ printk(KERN_ERR "%s: NO B1-PCI card detected\n", driver->name); return -ESRCH; #else - printk(KERN_ERR "b1pci: kernel not compiled with PCI.\n"); + printk(KERN_ERR "%s: kernel not compiled with PCI.\n", driver->name); return -EIO; #endif } diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/avmb1/b1pcmcia.c linux/drivers/isdn/avmb1/b1pcmcia.c --- v2.2.13/linux/drivers/isdn/avmb1/b1pcmcia.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/avmb1/b1pcmcia.c Tue Jan 4 10:12:15 2000 @@ -1,11 +1,18 @@ /* - * $Id: b1pcmcia.c,v 1.4 1999/08/22 20:26:26 calle Exp $ + * $Id: b1pcmcia.c,v 1.5 1999/11/05 16:38:01 calle 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.5 1999/11/05 16:38:01 calle + * Cleanups before kernel 2.4: + * - Changed all messages to use card->name or driver->name instead of + * constant string. + * - Moved some data from struct avmcard into new struct avmctrl_info. + * Changed all lowlevel capi driver to match the new structur. + * * Revision 1.4 1999/08/22 20:26:26 calle * backported changes from kernel 2.3.14: * - several #include "config.h" gone, others come. @@ -55,7 +62,7 @@ #include "capilli.h" #include "avmcard.h" -static char *revision = "$Revision: 1.4 $"; +static char *revision = "$Revision: 1.5 $"; /* ------------------------------------------------------------- */ @@ -74,11 +81,12 @@ card = (avmcard *) devptr; if (!card) { - printk(KERN_WARNING "b1_interrupt: wrong device\n"); + printk(KERN_WARNING "b1pcmcia: interrupt: wrong device\n"); return; } if (card->interrupt) { - printk(KERN_ERR "b1_interrupt: reentering interrupt hander (%s)\n", card->name); + printk(KERN_ERR "%s: reentering interrupt hander.\n", + card->name); return; } @@ -92,7 +100,8 @@ static void b1pcmcia_remove_ctr(struct capi_ctr *ctrl) { - avmcard *card = (avmcard *)(ctrl->driverdata); + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; unsigned int port = card->port; b1_reset(port); @@ -115,16 +124,26 @@ unsigned irq, enum avmcardtype cardtype) { + avmctrl_info *cinfo; avmcard *card; int retval; card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC); if (!card) { - printk(KERN_WARNING "b1pcmcia: no memory.\n"); + printk(KERN_WARNING "%s: no memory.\n", driver->name); return -ENOMEM; } memset(card, 0, sizeof(avmcard)); + cinfo = (avmctrl_info *) kmalloc(sizeof(avmctrl_info), GFP_ATOMIC); + if (!cinfo) { + printk(KERN_WARNING "%s: no memory.\n", driver->name); + kfree(card); + return -ENOMEM; + } + memset(cinfo, 0, sizeof(avmctrl_info)); + card->ctrlinfo = cinfo; + cinfo->card = card; switch (cardtype) { case avm_m1: sprintf(card->name, "m1-%x", port); break; case avm_m2: sprintf(card->name, "m2-%x", port); break; @@ -136,8 +155,9 @@ b1_reset(card->port); if ((retval = b1_detect(card->port, card->cardtype)) != 0) { - printk(KERN_NOTICE "b1pcmcia: NO card at 0x%x (%d)\n", - card->port, retval); + printk(KERN_NOTICE "%s: NO card at 0x%x (%d)\n", + driver->name, card->port, retval); + kfree(card->ctrlinfo); kfree(card); return -EIO; } @@ -145,36 +165,42 @@ retval = request_irq(card->irq, b1pcmcia_interrupt, 0, card->name, card); if (retval) { - printk(KERN_ERR "b1pcmcia: unable to get IRQ %d.\n", card->irq); + printk(KERN_ERR "%s: unable to get IRQ %d.\n", + driver->name, card->irq); + kfree(card->ctrlinfo); kfree(card); return -EBUSY; } - card->ctrl = di->attach_ctr(driver, card->name, card); - if (!card->ctrl) { - printk(KERN_ERR "b1pcmcia: attach controller failed.\n"); + cinfo->capi_ctrl = di->attach_ctr(driver, card->name, cinfo); + if (!cinfo->capi_ctrl) { + printk(KERN_ERR "%s: attach controller failed.\n", + driver->name); free_irq(card->irq, card); + kfree(card->ctrlinfo); kfree(card); return -EBUSY; } MOD_INC_USE_COUNT; - return card->ctrl->cnr; + return cinfo->capi_ctrl->cnr; } /* ------------------------------------------------------------- */ static char *b1pcmcia_procinfo(struct capi_ctr *ctrl) { - avmcard *card = (avmcard *)(ctrl->driverdata); - if (!card) + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + + if (!cinfo) return ""; - sprintf(card->infobuf, "%s %s 0x%x %d", - card->cardname[0] ? card->cardname : "-", - card->version[VER_DRIVER] ? card->version[VER_DRIVER] : "-", - card->port, card->irq + sprintf(cinfo->infobuf, "%s %s 0x%x %d", + cinfo->cardname[0] ? cinfo->cardname : "-", + cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", + cinfo->card ? cinfo->card->port : 0x0, + cinfo->card ? cinfo->card->irq : 0 ); - return card->infobuf; + return cinfo->infobuf; } /* ------------------------------------------------------------- */ @@ -219,7 +245,7 @@ avmcard *card; for (ctrl = b1pcmcia_driver.controller; ctrl; ctrl = ctrl->next) { - card = (avmcard *)(ctrl->driverdata); + card = ((avmctrl_info *)(ctrl->driverdata))->card; if (card->port == port && card->irq == irq) { b1pcmcia_remove_ctr(ctrl); return 0; diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/avmb1/capi.c linux/drivers/isdn/avmb1/capi.c --- v2.2.13/linux/drivers/isdn/avmb1/capi.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/avmb1/capi.c Tue Jan 4 10:12:15 2000 @@ -1,11 +1,14 @@ /* - * $Id: capi.c,v 1.21 1999/09/10 17:24:18 calle Exp $ + * $Id: capi.c,v 1.22 1999/11/13 21:27:16 keil Exp $ * * CAPI 2.0 Interface for Linux * * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capi.c,v $ + * Revision 1.22 1999/11/13 21:27:16 keil + * remove KERNELVERSION + * * Revision 1.21 1999/09/10 17:24:18 calle * Changes for proposed standard for CAPI2.0: * - AK148 "Linux Exention" @@ -282,20 +285,13 @@ capi_poll(struct file *file, poll_table * wait) { unsigned int mask = 0; -#if (LINUX_VERSION_CODE >= 0x02012d) unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); -#else - unsigned int minor = MINOR(file->f_inode->i_rdev); -#endif struct capidev *cdev; if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) return POLLERR; cdev = &capidevs[minor]; -#if (LINUX_VERSION_CODE < 0x020159) /* 2.1.89 */ -#define poll_wait(f,wq,w) poll_wait((wq),(w)) -#endif poll_wait(file, &(cdev->recv_wait), wait); mask = POLLOUT | POLLWRNORM; if (!skb_queue_empty(&cdev->recv_queue)) @@ -523,9 +519,7 @@ capi_ioctl, NULL, /* capi_mmap */ capi_open, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,118) NULL, /* capi_flush */ -#endif capi_release, NULL, /* capi_fsync */ NULL, /* capi_fasync */ diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/avmb1/capidrv.c linux/drivers/isdn/avmb1/capidrv.c --- v2.2.13/linux/drivers/isdn/avmb1/capidrv.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/avmb1/capidrv.c Tue Jan 4 10:12:15 2000 @@ -1,11 +1,17 @@ /* - * $Id: capidrv.c,v 1.27 1999/09/16 15:13:04 calle Exp $ + * $Id: capidrv.c,v 1.29 1999/12/06 17:13:06 calle Exp $ * * ISDN4Linux Driver, using capi20 interface (kernelcapi) * * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capidrv.c,v $ + * Revision 1.29 1999/12/06 17:13:06 calle + * Added controller watchdog. + * + * Revision 1.28 1999/11/05 16:22:37 calle + * Bugfix: Missing break in switch on ISDN_CMD_HANGUP. + * * Revision 1.27 1999/09/16 15:13:04 calle * forgot to change paramter type of contr for lower_callback ... * @@ -165,7 +171,7 @@ #include "capicmd.h" #include "capidrv.h" -static char *revision = "$Revision: 1.27 $"; +static char *revision = "$Revision: 1.29 $"; int debugmode = 0; MODULE_AUTHOR("Carsten Paeth "); @@ -193,6 +199,7 @@ int state; __u32 cipmask; __u32 cipmask2; + struct timer_list listentimer; /* * ID of capi message sent @@ -716,8 +723,6 @@ /* P-0.1 */ {ST_PLCI_OUTGOING, ST_PLCI_NONE, EV_PLCI_CONNECT_CONF_ERROR, p0}, {ST_PLCI_OUTGOING, ST_PLCI_ALLOCATED, EV_PLCI_CONNECT_CONF_OK, 0}, - {ST_PLCI_OUTGOING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, - {ST_PLCI_OUTGOING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, /* P-1 */ {ST_PLCI_ALLOCATED, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, 0}, {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, @@ -1857,14 +1862,19 @@ ); ncci_change_state(card, bchan->nccip, EV_NCCI_DISCONNECT_B3_REQ); send_message(card, &cmdcmsg); + return 0; } else if (bchan->plcip) { - bchan->disconnecting = 1; if (bchan->plcip->state == ST_PLCI_INCOMING) { - /* just ignore, we a called from isdn_status_callback(), - * which will return 0 or 2, this is handled by the - * CONNECT_IND handler + /* + * just ignore, we a called from + * isdn_status_callback(), + * which will return 0 or 2, this is handled + * by the CONNECT_IND handler */ - } else { + bchan->disconnecting = 1; + return 0; + } else if (bchan->plcip->plci) { + bchan->disconnecting = 1; capi_fill_DISCONNECT_REQ(&cmdcmsg, global.appid, card->msgid++, @@ -1876,8 +1886,18 @@ ); plci_change_state(card, bchan->plcip, EV_PLCI_DISCONNECT_REQ); send_message(card, &cmdcmsg); + return 0; + } else { + printk(KERN_ERR "capidrv-%d: chan %ld disconnect request while waiting for CONNECT_CONF\n", + card->contrnr, + c->arg); + return -EINVAL; } } + printk(KERN_ERR "capidrv-%d: chan %ld disconnect request on free channel\n", + card->contrnr, + c->arg); + return -EINVAL; /* ready */ case ISDN_CMD_SETL2: @@ -2172,6 +2192,29 @@ send_message(card, &cmdcmsg); } +static void send_listen(capidrv_contr *card) +{ + capi_fill_LISTEN_REQ(&cmdcmsg, global.appid, + card->msgid++, + card->contrnr, /* controller */ + 1 << 6, /* Infomask */ + card->cipmask, + card->cipmask2, + 0, 0); + send_message(card, &cmdcmsg); + listen_change_state(card, EV_LISTEN_REQ); +} + +static void listentimerfunc(unsigned long x) +{ + capidrv_contr *card = (capidrv_contr *)x; + if (card->state != ST_LISTEN_NONE && card->state != ST_LISTEN_ACTIVE) + printk(KERN_ERR "%s: controller dead ??\n", card->name); + send_listen(card); + mod_timer(&card->listentimer, jiffies + 60*HZ); +} + + static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) { capidrv_contr *card; @@ -2186,6 +2229,7 @@ return -1; } memset(card, 0, sizeof(capidrv_contr)); + init_timer(&card->listentimer); strcpy(card->name, id); card->contrnr = contr; card->nbchan = profp->nbchannel; @@ -2242,15 +2286,11 @@ card->cipmask = 0x1FFF03FF; /* any */ card->cipmask2 = 0; - capi_fill_LISTEN_REQ(&cmdcmsg, global.appid, - card->msgid++, - contr, /* controller */ - 1 << 6, /* Infomask */ - card->cipmask, - card->cipmask2, - 0, 0); - send_message(card, &cmdcmsg); - listen_change_state(card, EV_LISTEN_REQ); + send_listen(card); + + card->listentimer.data = (unsigned long)card; + card->listentimer.function = listentimerfunc; + mod_timer(&card->listentimer, jiffies + 60*HZ); printk(KERN_INFO "%s: now up (%d B channels)\n", card->name, card->nbchan); @@ -2296,6 +2336,7 @@ printk(KERN_ERR "capidrv: bug in free_plci()\n"); } kfree(card->bchans); + del_timer(&card->listentimer); printk(KERN_INFO "%s: now down.\n", card->name); diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/avmb1/kcapi.c linux/drivers/isdn/avmb1/kcapi.c --- v2.2.13/linux/drivers/isdn/avmb1/kcapi.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/avmb1/kcapi.c Tue Jan 4 10:12:15 2000 @@ -1,11 +1,21 @@ /* - * $Id: kcapi.c,v 1.8 1999/09/10 17:24:18 calle Exp $ + * $Id: kcapi.c,v 1.11 1999/11/23 13:29:29 calle Exp $ * * Kernel CAPI 2.0 Module * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: kcapi.c,v $ + * Revision 1.11 1999/11/23 13:29:29 calle + * Bugfix: incoming capi message were never traced. + * + * Revision 1.10 1999/10/26 15:30:32 calle + * Generate error message if user want to add card, but driver module is + * not loaded. + * + * Revision 1.9 1999/10/11 22:04:12 keil + * COMPAT_NEED_UACCESS (no include in isdn_compat.h) + * * Revision 1.8 1999/09/10 17:24:18 calle * Changes for proposed standard for CAPI2.0: * - AK148 "Linux Exention" @@ -72,7 +82,7 @@ #include #endif -static char *revision = "$Revision: 1.8 $"; +static char *revision = "$Revision: 1.11 $"; /* ------------------------------------------------------------- */ @@ -696,9 +706,9 @@ if (cmd == CAPI_DATA_B3 && subcmd == CAPI_IND) { card->nrecvdatapkt++; if (card->traceflag > 2) showctl |= 2; - if (card->traceflag) showctl |= 2; } else { card->nrecvctlpkt++; + if (card->traceflag) showctl |= 2; } showctl |= (card->traceflag & 1); if (showctl & 2) { @@ -1226,7 +1236,12 @@ case AVM_CARDTYPE_T1: driver = t1isa_driver; break; default: driver = 0; } - if (!driver || !driver->add_card) { + if (!driver) { + printk(KERN_ERR "kcapi: driver not loaded.\n"); + return -EIO; + } + if (!driver->add_card) { + printk(KERN_ERR "kcapi: driver has no add card function.\n"); return -EIO; } diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/avmb1/t1isa.c linux/drivers/isdn/avmb1/t1isa.c --- v2.2.13/linux/drivers/isdn/avmb1/t1isa.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/avmb1/t1isa.c Tue Jan 4 10:12:15 2000 @@ -1,11 +1,18 @@ /* - * $Id: t1isa.c,v 1.7 1999/09/15 08:16:03 calle Exp $ + * $Id: t1isa.c,v 1.8 1999/11/05 16:38:01 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.8 1999/11/05 16:38:01 calle + * Cleanups before kernel 2.4: + * - Changed all messages to use card->name or driver->name instead of + * constant string. + * - Moved some data from struct avmcard into new struct avmctrl_info. + * Changed all lowlevel capi driver to match the new structur. + * * Revision 1.7 1999/09/15 08:16:03 calle * Implementation of 64Bit extention complete. * @@ -66,7 +73,7 @@ #include "capilli.h" #include "avmcard.h" -static char *revision = "$Revision: 1.7 $"; +static char *revision = "$Revision: 1.8 $"; /* ------------------------------------------------------------- */ @@ -169,7 +176,8 @@ static void t1_handle_interrupt(avmcard * card) { - struct capi_ctr *ctrl = card->ctrl; + avmctrl_info *cinfo = &card->ctrlinfo[0]; + struct capi_ctr *ctrl = cinfo->capi_ctrl; unsigned char b1cmd; struct sk_buff *skb; @@ -197,7 +205,8 @@ CAPIMSG_SETLEN(card->msgbuf, 30); } if (!(skb = alloc_skb(DataB3Len+MsgLen, GFP_ATOMIC))) { - printk(KERN_ERR "t1isa: incoming packet dropped\n"); + printk(KERN_ERR "%s: incoming packet dropped\n", + card->name); } else { memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len); @@ -210,7 +219,8 @@ ApplId = (unsigned) b1_get_word(card->port); MsgLen = t1_get_slice(card->port, card->msgbuf); if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) { - printk(KERN_ERR "t1isa: incoming packet dropped\n"); + printk(KERN_ERR "%s: incoming packet dropped\n", + card->name); } else { memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); ctrl->handle_capimsg(ctrl, ApplId, skb); @@ -248,12 +258,12 @@ case RECEIVE_INIT: - card->versionlen = t1_get_slice(card->port, card->versionbuf); - b1_parse_version(card); + cinfo->versionlen = t1_get_slice(card->port, cinfo->versionbuf); + b1_parse_version(cinfo); printk(KERN_INFO "%s: %s-card (%s) now active\n", card->name, - card->version[VER_CARDTYPE], - card->version[VER_DRIVER]); + cinfo->version[VER_CARDTYPE], + cinfo->version[VER_DRIVER]); ctrl->ready(ctrl); break; @@ -299,11 +309,12 @@ card = (avmcard *) devptr; if (!card) { - printk(KERN_WARNING "t1_interrupt: wrong device\n"); + printk(KERN_WARNING "t1isa: interrupt: wrong device\n"); return; } if (card->interrupt) { - printk(KERN_ERR "t1_interrupt: reentering interrupt hander (%s)\n", card->name); + printk(KERN_ERR "%s: reentering interrupt hander.\n", + card->name); return; } @@ -317,7 +328,8 @@ static int t1isa_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) { - avmcard *card = (avmcard *)(ctrl->driverdata); + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; unsigned int port = card->port; unsigned long flags; int retval; @@ -325,7 +337,7 @@ t1_disable_irq(port); b1_reset(port); - if ((retval = b1_load_t4file(port, &data->firmware))) { + if ((retval = b1_load_t4file(card, &data->firmware))) { b1_reset(port); printk(KERN_ERR "%s: failed to load t4file!!\n", card->name); @@ -333,7 +345,7 @@ } if (data->configuration.len > 0 && data->configuration.data) { - if ((retval = b1_load_config(port, &data->configuration))) { + if ((retval = b1_load_config(card, &data->configuration))) { b1_reset(port); printk(KERN_ERR "%s: failed to load config!!\n", card->name); @@ -341,7 +353,7 @@ } } - if (!b1_loaded(port)) { + if (!b1_loaded(card)) { printk(KERN_ERR "%s: failed to load t4file.\n", card->name); return -EIO; } @@ -360,20 +372,22 @@ void t1isa_reset_ctr(struct capi_ctr *ctrl) { - avmcard *card = (avmcard *)(ctrl->driverdata); + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; unsigned int port = card->port; t1_disable_irq(port); b1_reset(port); b1_reset(port); - memset(card->version, 0, sizeof(card->version)); + memset(cinfo->version, 0, sizeof(cinfo->version)); ctrl->reseted(ctrl); } static void t1isa_remove_ctr(struct capi_ctr *ctrl) { - avmcard *card = (avmcard *)(ctrl->driverdata); + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; unsigned int port = card->port; t1_disable_irq(port); @@ -384,6 +398,7 @@ di->detach_ctr(ctrl); free_irq(card->irq, card); release_region(card->port, AVMB1_PORTLEN); + kfree(card->ctrlinfo); kfree(card); MOD_DEC_USE_COUNT; @@ -394,16 +409,26 @@ static int t1isa_add_card(struct capi_driver *driver, struct capicardparams *p) { struct capi_ctr *ctrl; + avmctrl_info *cinfo; avmcard *card; int retval; card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC); if (!card) { - printk(KERN_WARNING "t1isa: no memory.\n"); + printk(KERN_WARNING "%s: no memory.\n", driver->name); return -ENOMEM; } memset(card, 0, sizeof(avmcard)); + cinfo = (avmctrl_info *) kmalloc(sizeof(avmctrl_info), GFP_ATOMIC); + if (!cinfo) { + printk(KERN_WARNING "%s: no memory.\n", driver->name); + kfree(card); + return -ENOMEM; + } + memset(cinfo, 0, sizeof(avmctrl_info)); + card->ctrlinfo = cinfo; + cinfo->card = card; sprintf(card->name, "t1isa-%x", p->port); card->port = p->port; card->irq = p->irq; @@ -411,33 +436,42 @@ card->cardnr = p->cardnr; if (!(((card->port & 0x7) == 0) && ((card->port & 0x30) != 0x30))) { - printk(KERN_WARNING "t1isa: illegal port 0x%x.\n", card->port); + printk(KERN_WARNING "%s: illegal port 0x%x.\n", + driver->name, card->port); + kfree(card->ctrlinfo); kfree(card); return -EINVAL; } if (check_region(card->port, AVMB1_PORTLEN)) { printk(KERN_WARNING - "t1isa: ports 0x%03x-0x%03x in use.\n", - card->port, card->port + AVMB1_PORTLEN); + "%s: ports 0x%03x-0x%03x in use.\n", + driver->name, card->port, card->port + AVMB1_PORTLEN); + kfree(card->ctrlinfo); kfree(card); return -EBUSY; } if (hema_irq_table[card->irq & 0xf] == 0) { - printk(KERN_WARNING "t1isa: irq %d not valid.\n", card->irq); + printk(KERN_WARNING "%s: irq %d not valid.\n", + driver->name, card->irq); + kfree(card->ctrlinfo); kfree(card); return -EINVAL; } for (ctrl = driver->controller; ctrl; ctrl = ctrl->next) { - if (((avmcard *)(ctrl->driverdata))->cardnr == card->cardnr) { - printk(KERN_WARNING "t1isa: card with number %d already installed.\n", card->cardnr); + avmcard *cardp = ((avmctrl_info *)(ctrl->driverdata))->card; + if (cardp->cardnr == card->cardnr) { + printk(KERN_WARNING "%s: card with number %d already installed at 0x%x.\n", + driver->name, card->cardnr, cardp->port); + kfree(card->ctrlinfo); kfree(card); return -EBUSY; } } if ((retval = t1_detectandinit(card->port, card->irq, card->cardnr)) != 0) { - printk(KERN_NOTICE "t1isa: NO card at 0x%x (%d)\n", - card->port, retval); + printk(KERN_NOTICE "%s: NO card at 0x%x (%d)\n", + driver->name, card->port, retval); + kfree(card->ctrlinfo); kfree(card); return -EIO; } @@ -448,17 +482,21 @@ retval = request_irq(card->irq, t1isa_interrupt, 0, card->name, card); if (retval) { - printk(KERN_ERR "t1isa: unable to get IRQ %d.\n", card->irq); + printk(KERN_ERR "%s: unable to get IRQ %d.\n", + driver->name, card->irq); release_region(card->port, AVMB1_PORTLEN); + kfree(card->ctrlinfo); kfree(card); return -EBUSY; } - card->ctrl = di->attach_ctr(driver, card->name, card); - if (!card->ctrl) { - printk(KERN_ERR "t1isa: attach controller failed.\n"); + cinfo->capi_ctrl = di->attach_ctr(driver, card->name, cinfo); + if (!cinfo->capi_ctrl) { + printk(KERN_ERR "%s: attach controller failed.\n", + driver->name); free_irq(card->irq, card); release_region(card->port, AVMB1_PORTLEN); + kfree(card->ctrlinfo); kfree(card); return -EBUSY; } @@ -469,7 +507,8 @@ static void t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) { - avmcard *card = (avmcard *)(ctrl->driverdata); + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; unsigned int port = card->port; unsigned long flags; __u16 len = CAPIMSG_LEN(skb->data); @@ -494,15 +533,18 @@ static char *t1isa_procinfo(struct capi_ctr *ctrl) { - avmcard *card = (avmcard *)(ctrl->driverdata); - if (!card) + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + + if (!cinfo) return ""; - sprintf(card->infobuf, "%s %s 0x%x %d %d", - card->cardname[0] ? card->cardname : "-", - card->version[VER_DRIVER] ? card->version[VER_DRIVER] : "-", - card->port, card->irq, card->cardnr + sprintf(cinfo->infobuf, "%s %s 0x%x %d %d", + cinfo->cardname[0] ? cinfo->cardname : "-", + cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", + cinfo->card ? cinfo->card->port : 0x0, + cinfo->card ? cinfo->card->irq : 0, + cinfo->card ? cinfo->card->cardnr : 0 ); - return card->infobuf; + return cinfo->infobuf; } diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/avmb1/t1pci.c linux/drivers/isdn/avmb1/t1pci.c --- v2.2.13/linux/drivers/isdn/avmb1/t1pci.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/avmb1/t1pci.c Tue Jan 4 10:12:15 2000 @@ -0,0 +1,1166 @@ +/* + * $Id: t1pci.c,v 1.3 1999/11/13 21:27:16 keil Exp $ + * + * Module for AVM T1 PCI-card. + * + * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: t1pci.c,v $ + * Revision 1.3 1999/11/13 21:27:16 keil + * remove KERNELVERSION + * + * Revision 1.2 1999/11/05 16:38:02 calle + * Cleanups before kernel 2.4: + * - Changed all messages to use card->name or driver->name instead of + * constant string. + * - Moved some data from struct avmcard into new struct avmctrl_info. + * Changed all lowlevel capi driver to match the new structur. + * + * Revision 1.1 1999/10/26 15:31:42 calle + * Added driver for T1-PCI card. + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "capicmd.h" +#include "capiutil.h" +#include "capilli.h" +#include "avmcard.h" + +static char *revision = "$Revision: 1.3 $"; + +#undef CONFIG_T1PCI_DEBUG +#undef CONFIG_T1PCI_POLLDEBUG + +/* ------------------------------------------------------------- */ + +#ifndef PCI_VENDOR_ID_AVM +#define PCI_VENDOR_ID_AVM 0x1244 +#endif + +#ifndef PCI_DEVICE_ID_AVM_T1 +#define PCI_DEVICE_ID_AVM_T1 0x1200 +#endif + +/* ------------------------------------------------------------- */ + +int suppress_pollack = 0; + +MODULE_AUTHOR("Carsten Paeth "); + +MODULE_PARM(suppress_pollack, "0-1i"); + + +/* ------------------------------------------------------------- */ + +static struct capi_driver_interface *di; + +/* ------------------------------------------------------------- */ + +static void t1pci_dispatch_tx(avmcard *card); + +/* ------------------------------------------------------------- */ + +/* S5933 */ + +#define AMCC_RXPTR 0x24 +#define AMCC_RXLEN 0x28 +#define AMCC_TXPTR 0x2c +#define AMCC_TXLEN 0x30 + +#define AMCC_INTCSR 0x38 +# define EN_READ_TC_INT 0x00008000L +# define EN_WRITE_TC_INT 0x00004000L +# define EN_TX_TC_INT EN_READ_TC_INT +# define EN_RX_TC_INT EN_WRITE_TC_INT +# define AVM_FLAG 0x30000000L + +# define ANY_S5933_INT 0x00800000L +# define READ_TC_INT 0x00080000L +# define WRITE_TC_INT 0x00040000L +# define TX_TC_INT READ_TC_INT +# define RX_TC_INT WRITE_TC_INT +# define MASTER_ABORT_INT 0x00100000L +# define TARGET_ABORT_INT 0x00200000L +# define BUS_MASTER_INT 0x00200000L +# define ALL_INT 0x000C0000L + +#define AMCC_MCSR 0x3c +# define A2P_HI_PRIORITY 0x00000100L +# define EN_A2P_TRANSFERS 0x00000400L +# define P2A_HI_PRIORITY 0x00001000L +# define EN_P2A_TRANSFERS 0x00004000L +# define RESET_A2P_FLAGS 0x04000000L +# define RESET_P2A_FLAGS 0x02000000L + +/* ------------------------------------------------------------- */ + +#define t1outmeml(addr, value) writel(value, addr) +#define t1inmeml(addr) readl(addr) +#define t1outmemw(addr, value) writew(value, addr) +#define t1inmemw(addr) readw(addr) +#define t1outmemb(addr, value) writeb(value, addr) +#define t1inmemb(addr) readb(addr) + +/* ------------------------------------------------------------- */ + +static inline int t1pci_tx_empty(unsigned int port) +{ + return inb(port + 0x03) & 0x1; +} + +static inline int t1pci_rx_full(unsigned int port) +{ + return inb(port + 0x02) & 0x1; +} + +static int t1pci_tolink(avmcard *card, void *buf, unsigned int len) +{ + unsigned long stop = jiffies + 1 * HZ; /* maximum wait time 1 sec */ + unsigned char *s = (unsigned char *)buf; + while (len--) { + while ( !t1pci_tx_empty(card->port) + && time_before(jiffies, stop)); + if (!t1pci_tx_empty(card->port)) + return -1; + t1outp(card->port, 0x01, *s++); + } + return 0; +} + +static int t1pci_fromlink(avmcard *card, void *buf, unsigned int len) +{ + unsigned long stop = jiffies + 1 * HZ; /* maximum wait time 1 sec */ + unsigned char *s = (unsigned char *)buf; + while (len--) { + while ( !t1pci_rx_full(card->port) + && time_before(jiffies, stop)); + if (!t1pci_rx_full(card->port)) + return -1; + *s++ = t1inp(card->port, 0x00); + } + return 0; +} + +static int WriteReg(avmcard *card, __u32 reg, __u8 val) +{ + __u8 cmd = 0x00; + if ( t1pci_tolink(card, &cmd, 1) == 0 + && t1pci_tolink(card, ®, 4) == 0) { + __u32 tmp = val; + return t1pci_tolink(card, &tmp, 4); + } + return -1; +} + +static __u8 ReadReg(avmcard *card, __u32 reg) +{ + __u8 cmd = 0x01; + if ( t1pci_tolink(card, &cmd, 1) == 0 + && t1pci_tolink(card, ®, 4) == 0) { + __u32 tmp; + if (t1pci_fromlink(card, &tmp, 4) == 0) + return (__u8)tmp; + } + return 0xff; +} + +/* ------------------------------------------------------------- */ + +static inline void _put_byte(void **pp, __u8 val) +{ + __u8 *s = *pp; + *s++ = val; + *pp = s; +} + +static inline void _put_word(void **pp, __u32 val) +{ + __u8 *s = *pp; + *s++ = val & 0xff; + *s++ = (val >> 8) & 0xff; + *s++ = (val >> 16) & 0xff; + *s++ = (val >> 24) & 0xff; + *pp = s; +} + +static inline void _put_slice(void **pp, unsigned char *dp, unsigned int len) +{ + unsigned i = len; + _put_word(pp, i); + while (i-- > 0) + _put_byte(pp, *dp++); +} + +static inline __u8 _get_byte(void **pp) +{ + __u8 *s = *pp; + __u8 val; + val = *s++; + *pp = s; + return val; +} + +static inline __u32 _get_word(void **pp) +{ + __u8 *s = *pp; + __u32 val; + val = *s++; + val |= (*s++ << 8); + val |= (*s++ << 16); + val |= (*s++ << 24); + *pp = s; + return val; +} + +static inline __u32 _get_slice(void **pp, unsigned char *dp) +{ + unsigned int len, i; + + len = i = _get_word(pp); + while (i-- > 0) *dp++ = _get_byte(pp); + return len; +} + +/* ------------------------------------------------------------- */ + +static void t1pci_reset(avmcard *card) +{ + unsigned long flags; + + save_flags(flags); + cli(); + card->csr = 0x0; + t1outmeml(card->mbase+AMCC_INTCSR, card->csr); + t1outmeml(card->mbase+AMCC_MCSR, 0); + t1outmeml(card->mbase+AMCC_RXLEN, 0); + t1outmeml(card->mbase+AMCC_TXLEN, 0); + + t1outp(card->port, T1_RESETLINK, 0x00); + t1outp(card->port, 0x07, 0x00); + + restore_flags(flags); + + t1outmeml(card->mbase+AMCC_MCSR, 0); + udelay(10 * 1000); + t1outmeml(card->mbase+AMCC_MCSR, 0x0f000000); /* reset all */ + udelay(10 * 1000); + t1outmeml(card->mbase+AMCC_MCSR, 0); + udelay(42 * 1000); + +} + +/* ------------------------------------------------------------- */ + +static int t1pci_detect(avmcard *card) +{ + t1outmeml(card->mbase+AMCC_MCSR, 0); + udelay(10 * 1000); + t1outmeml(card->mbase+AMCC_MCSR, 0x0f000000); /* reset all */ + udelay(10 * 1000); + t1outmeml(card->mbase+AMCC_MCSR, 0); + udelay(42 * 1000); + + t1outmeml(card->mbase+AMCC_RXLEN, 0); + t1outmeml(card->mbase+AMCC_TXLEN, 0); + card->csr = 0x0; + t1outmeml(card->mbase+AMCC_INTCSR, card->csr); + + if (t1inmeml(card->mbase+AMCC_MCSR) != 0x000000E6) + return 1; + + t1outmeml(card->mbase+AMCC_RXPTR, 0xffffffff); + t1outmeml(card->mbase+AMCC_TXPTR, 0xffffffff); + if ( t1inmeml(card->mbase+AMCC_RXPTR) != 0xfffffffc + || t1inmeml(card->mbase+AMCC_TXPTR) != 0xfffffffc) + return 2; + + t1outmeml(card->mbase+AMCC_RXPTR, 0x0); + t1outmeml(card->mbase+AMCC_TXPTR, 0x0); + if ( t1inmeml(card->mbase+AMCC_RXPTR) != 0x0 + || t1inmeml(card->mbase+AMCC_TXPTR) != 0x0) + return 3; + + t1outp(card->port, T1_RESETLINK, 0x00); + t1outp(card->port, 0x07, 0x00); + + t1outp(card->port, 0x02, 0x02); + t1outp(card->port, 0x03, 0x02); + + if ( (t1inp(card->port, 0x02) & 0xFE) != 0x02 + || t1inp(card->port, 0x3) != 0x03) + return 4; + + t1outp(card->port, 0x02, 0x00); + t1outp(card->port, 0x03, 0x00); + + if ( (t1inp(card->port, 0x02) & 0xFE) != 0x00 + || t1inp(card->port, 0x3) != 0x01) + return 5; + + /* Transputer test */ + + if ( WriteReg(card, 0x80001000, 0x11) != 0 + || WriteReg(card, 0x80101000, 0x22) != 0 + || WriteReg(card, 0x80201000, 0x33) != 0 + || WriteReg(card, 0x80301000, 0x44) != 0) + return 6; + + if ( ReadReg(card, 0x80001000) != 0x11 + || ReadReg(card, 0x80101000) != 0x22 + || ReadReg(card, 0x80201000) != 0x33 + || ReadReg(card, 0x80301000) != 0x44) + return 7; + + if ( WriteReg(card, 0x80001000, 0x55) != 0 + || WriteReg(card, 0x80101000, 0x66) != 0 + || WriteReg(card, 0x80201000, 0x77) != 0 + || WriteReg(card, 0x80301000, 0x88) != 0) + return 8; + + if ( ReadReg(card, 0x80001000) != 0x55 + || ReadReg(card, 0x80101000) != 0x66 + || ReadReg(card, 0x80201000) != 0x77 + || ReadReg(card, 0x80301000) != 0x88) + return 9; + + return 0; +} + +/* ------------------------------------------------------------- */ + +static void t1pci_dispatch_tx(avmcard *card) +{ + avmcard_dmainfo *dma = card->dma; + unsigned long flags; + struct sk_buff *skb; + __u8 cmd, subcmd; + __u16 len; + __u32 txlen; + int inint; + void *p; + + save_flags(flags); + cli(); + + inint = card->interrupt; + + if (card->csr & EN_TX_TC_INT) { /* tx busy */ + restore_flags(flags); + return; + } + + skb = skb_dequeue(&dma->send_queue); + if (!skb) { +#ifdef CONFIG_T1PCI_DEBUG + printk(KERN_DEBUG "tx(%d): underrun\n", inint); +#endif + restore_flags(flags); + return; + } + + len = CAPIMSG_LEN(skb->data); + + if (len) { + cmd = CAPIMSG_COMMAND(skb->data); + subcmd = CAPIMSG_SUBCOMMAND(skb->data); + + p = dma->sendbuf; + + if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) { + __u16 dlen = CAPIMSG_DATALEN(skb->data); + _put_byte(&p, SEND_DATA_B3_REQ); + _put_slice(&p, skb->data, len); + _put_slice(&p, skb->data + len, dlen); + } else { + _put_byte(&p, SEND_MESSAGE); + _put_slice(&p, skb->data, len); + } + txlen = (__u8 *)p - (__u8 *)dma->sendbuf; +#ifdef CONFIG_T1PCI_DEBUG + printk(KERN_DEBUG "tx(%d): put msg len=%d\n", + inint, txlen); +#endif + } else { + txlen = skb->len-2; +#ifdef CONFIG_T1PCI_POLLDEBUG + if (skb->data[2] == SEND_POLLACK) + printk(KERN_INFO "%s: ack to t1\n", card->name); +#endif +#ifdef CONFIG_T1PCI_DEBUG + printk(KERN_DEBUG "tx(%d): put 0x%x len=%d\n", + inint, skb->data[2], txlen); +#endif + memcpy(dma->sendbuf, skb->data+2, skb->len-2); + } + txlen = (txlen + 3) & ~3; + + t1outmeml(card->mbase+AMCC_TXPTR, virt_to_phys(dma->sendbuf)); + t1outmeml(card->mbase+AMCC_TXLEN, txlen); + + card->csr |= EN_TX_TC_INT; + + if (!inint) + t1outmeml(card->mbase+AMCC_INTCSR, card->csr); + + restore_flags(flags); + dev_kfree_skb(skb); +} + +/* ------------------------------------------------------------- */ + +static void queue_pollack(avmcard *card) +{ + struct sk_buff *skb; + void *p; + + skb = alloc_skb(3, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost poll ack\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_POLLACK); + skb_put(skb, (__u8 *)p - (__u8 *)skb->data); + + skb_queue_tail(&card->dma->send_queue, skb); + t1pci_dispatch_tx(card); +} + +/* ------------------------------------------------------------- */ + +static void t1pci_handle_rx(avmcard *card) +{ + avmctrl_info *cinfo = &card->ctrlinfo[0]; + avmcard_dmainfo *dma = card->dma; + struct capi_ctr *ctrl = cinfo->capi_ctrl; + struct sk_buff *skb; + void *p = dma->recvbuf+4; + __u32 ApplId, MsgLen, DataB3Len, NCCI, WindowSize; + __u8 b1cmd = _get_byte(&p); + +#ifdef CONFIG_T1PCI_DEBUG + printk(KERN_DEBUG "rx: 0x%x %lu\n", b1cmd, (unsigned long)dma->recvlen); +#endif + + switch (b1cmd) { + case RECEIVE_DATA_B3_IND: + + ApplId = (unsigned) _get_word(&p); + MsgLen = _get_slice(&p, card->msgbuf); + DataB3Len = _get_slice(&p, card->databuf); + + if (MsgLen < 30) { /* not CAPI 64Bit */ + memset(card->msgbuf+MsgLen, 0, 30-MsgLen); + MsgLen = 30; + CAPIMSG_SETLEN(card->msgbuf, 30); + } + if (!(skb = alloc_skb(DataB3Len+MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "%s: incoming packet dropped\n", + card->name); + } else { + memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); + memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len); + ctrl->handle_capimsg(ctrl, ApplId, skb); + } + break; + + case RECEIVE_MESSAGE: + + ApplId = (unsigned) _get_word(&p); + MsgLen = _get_slice(&p, card->msgbuf); + if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "%s: incoming packet dropped\n", + card->name); + } else { + memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); + ctrl->handle_capimsg(ctrl, ApplId, skb); + } + break; + + case RECEIVE_NEW_NCCI: + + ApplId = _get_word(&p); + NCCI = _get_word(&p); + WindowSize = _get_word(&p); + + ctrl->new_ncci(ctrl, ApplId, NCCI, WindowSize); + + break; + + case RECEIVE_FREE_NCCI: + + ApplId = _get_word(&p); + NCCI = _get_word(&p); + + if (NCCI != 0xffffffff) + ctrl->free_ncci(ctrl, ApplId, NCCI); + else ctrl->appl_released(ctrl, ApplId); + break; + + case RECEIVE_START: +#ifdef CONFIG_T1PCI_POLLDEBUG + printk(KERN_INFO "%s: poll from t1\n", card->name); +#endif + if (!suppress_pollack) + queue_pollack(card); + ctrl->resume_output(ctrl); + break; + + case RECEIVE_STOP: + ctrl->suspend_output(ctrl); + break; + + case RECEIVE_INIT: + + cinfo->versionlen = _get_slice(&p, cinfo->versionbuf); + b1_parse_version(cinfo); + printk(KERN_INFO "%s: %s-card (%s) now active\n", + card->name, + cinfo->version[VER_CARDTYPE], + cinfo->version[VER_DRIVER]); + ctrl->ready(ctrl); + break; + + case RECEIVE_TASK_READY: + ApplId = (unsigned) _get_word(&p); + MsgLen = _get_slice(&p, card->msgbuf); + card->msgbuf[MsgLen--] = 0; + while ( MsgLen >= 0 + && ( card->msgbuf[MsgLen] == '\n' + || card->msgbuf[MsgLen] == '\r')) + card->msgbuf[MsgLen--] = 0; + printk(KERN_INFO "%s: task %d \"%s\" ready.\n", + card->name, ApplId, card->msgbuf); + break; + + case RECEIVE_DEBUGMSG: + MsgLen = _get_slice(&p, card->msgbuf); + card->msgbuf[MsgLen--] = 0; + while ( MsgLen >= 0 + && ( card->msgbuf[MsgLen] == '\n' + || card->msgbuf[MsgLen] == '\r')) + card->msgbuf[MsgLen--] = 0; + printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf); + break; + + default: + printk(KERN_ERR "%s: t1pci_interrupt: 0x%x ???\n", + card->name, b1cmd); + return; + } +} + +/* ------------------------------------------------------------- */ + +static void t1pci_handle_interrupt(avmcard *card) +{ + __u32 status = t1inmeml(card->mbase+AMCC_INTCSR); + __u32 newcsr; + + if ((status & ANY_S5933_INT) == 0) + return; + + newcsr = card->csr | (status & ALL_INT); + if (status & TX_TC_INT) newcsr &= ~EN_TX_TC_INT; + if (status & RX_TC_INT) newcsr &= ~EN_RX_TC_INT; + t1outmeml(card->mbase+AMCC_INTCSR, newcsr); + + if ((status & RX_TC_INT) != 0) { + __u8 *recvbuf = card->dma->recvbuf; + __u32 rxlen; + if (card->dma->recvlen == 0) { + card->dma->recvlen = *((__u32 *)recvbuf); + rxlen = (card->dma->recvlen + 3) & ~3; + t1outmeml(card->mbase+AMCC_RXPTR, + virt_to_phys(recvbuf+4)); + t1outmeml(card->mbase+AMCC_RXLEN, rxlen); + } else { + t1pci_handle_rx(card); + card->dma->recvlen = 0; + t1outmeml(card->mbase+AMCC_RXPTR, virt_to_phys(recvbuf)); + t1outmeml(card->mbase+AMCC_RXLEN, 4); + } + } + + if ((status & TX_TC_INT) != 0) { + card->csr &= ~EN_TX_TC_INT; + t1pci_dispatch_tx(card); + } else if (card->csr & EN_TX_TC_INT) { + if (t1inmeml(card->mbase+AMCC_TXLEN) == 0) { + card->csr &= ~EN_TX_TC_INT; + t1pci_dispatch_tx(card); + } + } + t1outmeml(card->mbase+AMCC_INTCSR, card->csr); +} + +static void t1pci_interrupt(int interrupt, void *devptr, struct pt_regs *regs) +{ + avmcard *card; + + card = (avmcard *) devptr; + + if (!card) { + printk(KERN_WARNING "t1pci: interrupt: wrong device\n"); + return; + } + if (card->interrupt) { + printk(KERN_ERR "%s: reentering interrupt hander\n", card->name); + return; + } + + card->interrupt = 1; + + t1pci_handle_interrupt(card); + + card->interrupt = 0; +} + +/* ------------------------------------------------------------- */ + +static int t1pci_loaded(avmcard *card) +{ + unsigned long stop; + unsigned char ans; + unsigned long tout = 2; + unsigned int base = card->port; + + for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) { + if (b1_tx_empty(base)) + break; + } + if (!b1_tx_empty(base)) { + printk(KERN_ERR "%s: t1pci_loaded: tx err, corrupted t4 file ?\n", + card->name); + return 0; + } + b1_put_byte(base, SEND_POLLACK); + for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) { + if (b1_rx_full(base)) { + if ((ans = b1_get_byte(base)) == RECEIVE_POLLDWORD) { + return 1; + } + printk(KERN_ERR "%s: t1pci_loaded: got 0x%x, firmware not running in dword mode\n", card->name, ans); + return 0; + } + } + printk(KERN_ERR "%s: t1pci_loaded: firmware not running\n", card->name); + return 0; +} + +/* ------------------------------------------------------------- */ + +static void t1pci_send_init(avmcard *card) +{ + struct sk_buff *skb; + void *p; + + skb = alloc_skb(15, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost register appl.\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_INIT); + _put_word(&p, AVM_NAPPS); + _put_word(&p, AVM_NCCI_PER_CHANNEL*30); + _put_word(&p, card->cardnr - 1); + skb_put(skb, (__u8 *)p - (__u8 *)skb->data); + + skb_queue_tail(&card->dma->send_queue, skb); + t1pci_dispatch_tx(card); +} + +static int t1pci_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned long flags; + int retval; + + t1pci_reset(card); + + if ((retval = b1_load_t4file(card, &data->firmware))) { + t1pci_reset(card); + printk(KERN_ERR "%s: failed to load t4file!!\n", + card->name); + return retval; + } + + if (data->configuration.len > 0 && data->configuration.data) { + if ((retval = b1_load_config(card, &data->configuration))) { + t1pci_reset(card); + printk(KERN_ERR "%s: failed to load config!!\n", + card->name); + return retval; + } + } + + if (!t1pci_loaded(card)) { + t1pci_reset(card); + printk(KERN_ERR "%s: failed to load t4file.\n", card->name); + return -EIO; + } + + save_flags(flags); + cli(); + + card->csr = AVM_FLAG; + t1outmeml(card->mbase+AMCC_INTCSR, card->csr); + t1outmeml(card->mbase+AMCC_MCSR, + EN_A2P_TRANSFERS|EN_P2A_TRANSFERS + |A2P_HI_PRIORITY|P2A_HI_PRIORITY + |RESET_A2P_FLAGS|RESET_P2A_FLAGS); + t1outp(card->port, 0x07, 0x30); + t1outp(card->port, 0x10, 0xF0); + + card->dma->recvlen = 0; + t1outmeml(card->mbase+AMCC_RXPTR, virt_to_phys(card->dma->recvbuf)); + t1outmeml(card->mbase+AMCC_RXLEN, 4); + card->csr |= EN_RX_TC_INT; + t1outmeml(card->mbase+AMCC_INTCSR, card->csr); + restore_flags(flags); + + t1pci_send_init(card); + + return 0; +} + +void t1pci_reset_ctr(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + + t1pci_reset(card); + + memset(cinfo->version, 0, sizeof(cinfo->version)); + ctrl->reseted(ctrl); +} + +static void t1pci_remove_ctr(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + + t1pci_reset(card); + + di->detach_ctr(ctrl); + free_irq(card->irq, card); + iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK)); + release_region(card->port, AVMB1_PORTLEN); + ctrl->driverdata = 0; + kfree(card->ctrlinfo); + kfree(card->dma); + kfree(card); + + MOD_DEC_USE_COUNT; +} + +/* ------------------------------------------------------------- */ + + +void t1pci_register_appl(struct capi_ctr *ctrl, + __u16 appl, + capi_register_params *rp) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + struct sk_buff *skb; + int want = rp->level3cnt; + int nconn; + void *p; + + if (want > 0) nconn = want; + else nconn = ctrl->profile.nbchannel * -want; + if (nconn == 0) nconn = ctrl->profile.nbchannel; + + skb = alloc_skb(23, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost register appl.\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_REGISTER); + _put_word(&p, appl); + _put_word(&p, 1024 * (nconn+1)); + _put_word(&p, nconn); + _put_word(&p, rp->datablkcnt); + _put_word(&p, rp->datablklen); + skb_put(skb, (__u8 *)p - (__u8 *)skb->data); + + skb_queue_tail(&card->dma->send_queue, skb); + t1pci_dispatch_tx(card); + + ctrl->appl_registered(ctrl, appl); +} + +/* ------------------------------------------------------------- */ + +void t1pci_release_appl(struct capi_ctr *ctrl, __u16 appl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + struct sk_buff *skb; + void *p; + + skb = alloc_skb(7, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost release appl.\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_RELEASE); + _put_word(&p, appl); + + skb_put(skb, (__u8 *)p - (__u8 *)skb->data); + skb_queue_tail(&card->dma->send_queue, skb); + t1pci_dispatch_tx(card); +} + +/* ------------------------------------------------------------- */ + + +static void t1pci_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + skb_queue_tail(&card->dma->send_queue, skb); + t1pci_dispatch_tx(card); +} + +/* ------------------------------------------------------------- */ + +static char *t1pci_procinfo(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + + if (!cinfo) + return ""; + sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx", + cinfo->cardname[0] ? cinfo->cardname : "-", + cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", + cinfo->card ? cinfo->card->port : 0x0, + cinfo->card ? cinfo->card->irq : 0, + cinfo->card ? cinfo->card->membase : 0 + ); + return cinfo->infobuf; +} + +static int t1pci_read_proc(char *page, char **start, off_t off, + int count, int *eof, struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned long flags; + __u8 flag; + int len = 0; + char *s; + __u32 txaddr, txlen, rxaddr, rxlen, csr; + + len += sprintf(page+len, "%-16s %s\n", "name", card->name); + len += sprintf(page+len, "%-16s 0x%x\n", "io", card->port); + len += sprintf(page+len, "%-16s %d\n", "irq", card->irq); + len += sprintf(page+len, "%-16s 0x%lx\n", "membase", card->membase); + switch (card->cardtype) { + case avm_b1isa: s = "B1 ISA"; break; + case avm_b1pci: s = "B1 PCI"; break; + case avm_b1pcmcia: s = "B1 PCMCIA"; break; + case avm_m1: s = "M1"; break; + case avm_m2: s = "M2"; break; + case avm_t1isa: s = "T1 ISA (HEMA)"; break; + case avm_t1pci: s = "T1 PCI"; break; + case avm_c4: s = "C4"; break; + default: s = "???"; break; + } + len += sprintf(page+len, "%-16s %s\n", "type", s); + if ((s = cinfo->version[VER_DRIVER]) != 0) + len += sprintf(page+len, "%-16s %s\n", "ver_driver", s); + if ((s = cinfo->version[VER_CARDTYPE]) != 0) + len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s); + if ((s = cinfo->version[VER_SERIAL]) != 0) + len += sprintf(page+len, "%-16s %s\n", "ver_serial", s); + + if (card->cardtype != avm_m1) { + flag = ((__u8 *)(ctrl->profile.manu))[3]; + if (flag) + len += sprintf(page+len, "%-16s%s%s%s%s%s%s%s\n", + "protocol", + (flag & 0x01) ? " DSS1" : "", + (flag & 0x02) ? " CT1" : "", + (flag & 0x04) ? " VN3" : "", + (flag & 0x08) ? " NI1" : "", + (flag & 0x10) ? " AUSTEL" : "", + (flag & 0x20) ? " ESS" : "", + (flag & 0x40) ? " 1TR6" : "" + ); + } + if (card->cardtype != avm_m1) { + flag = ((__u8 *)(ctrl->profile.manu))[5]; + if (flag) + len += sprintf(page+len, "%-16s%s%s%s%s\n", + "linetype", + (flag & 0x01) ? " point to point" : "", + (flag & 0x02) ? " point to multipoint" : "", + (flag & 0x08) ? " leased line without D-channel" : "", + (flag & 0x04) ? " leased line with D-channel" : "" + ); + } + len += sprintf(page+len, "%-16s %s\n", "cardname", cinfo->cardname); + + save_flags(flags); + cli(); + + txaddr = (__u32)phys_to_virt(t1inmeml(card->mbase+0x2c)); + txaddr -= (__u32)card->dma->sendbuf; + txlen = t1inmeml(card->mbase+0x30); + + rxaddr = (__u32)phys_to_virt(t1inmeml(card->mbase+0x24)); + rxaddr -= (__u32)card->dma->recvbuf; + rxlen = t1inmeml(card->mbase+0x28); + + csr = t1inmeml(card->mbase+AMCC_INTCSR); + + restore_flags(flags); + + len += sprintf(page+len, "%-16s 0x%lx\n", + "csr (cached)", (unsigned long)card->csr); + len += sprintf(page+len, "%-16s 0x%lx\n", + "csr", (unsigned long)csr); + len += sprintf(page+len, "%-16s %lu\n", + "txoff", (unsigned long)txaddr); + len += sprintf(page+len, "%-16s %lu\n", + "txlen", (unsigned long)txlen); + len += sprintf(page+len, "%-16s %lu\n", + "rxoff", (unsigned long)rxaddr); + len += sprintf(page+len, "%-16s %lu\n", + "rxlen", (unsigned long)rxlen); + + if (off+count >= len) + *eof = 1; + if (len < off) + return 0; + *start = page + off; + return ((count < len-off) ? count : len-off); +} + +/* ------------------------------------------------------------- */ + +static int t1pci_add_card(struct capi_driver *driver, struct capicardparams *p) +{ + unsigned long page_offset, base; + avmcard *card; + avmctrl_info *cinfo; + int retval; + + card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC); + + if (!card) { + printk(KERN_WARNING "%s: no memory.\n", driver->name); + return -ENOMEM; + } + memset(card, 0, sizeof(avmcard)); + card->dma = (avmcard_dmainfo *) kmalloc(sizeof(avmcard_dmainfo), GFP_ATOMIC); + if (!card->dma) { + printk(KERN_WARNING "%s: no memory.\n", driver->name); + kfree(card); + return -ENOMEM; + } + memset(card->dma, 0, sizeof(avmcard_dmainfo)); + cinfo = (avmctrl_info *) kmalloc(sizeof(avmctrl_info), GFP_ATOMIC); + if (!cinfo) { + printk(KERN_WARNING "%s: no memory.\n", driver->name); + kfree(card->dma); + kfree(card); + return -ENOMEM; + } + memset(cinfo, 0, sizeof(avmctrl_info)); + card->ctrlinfo = cinfo; + cinfo->card = card; + sprintf(card->name, "t1pci-%x", p->port); + card->port = p->port; + card->irq = p->irq; + card->membase = p->membase; + card->cardtype = avm_t1pci; + + if (check_region(card->port, AVMB1_PORTLEN)) { + printk(KERN_WARNING + "%s: ports 0x%03x-0x%03x in use.\n", + driver->name, card->port, card->port + AVMB1_PORTLEN); + kfree(card->ctrlinfo); + kfree(card->dma); + kfree(card); + return -EBUSY; + } + + base = card->membase & PAGE_MASK; + page_offset = card->membase - base; + card->mbase = ioremap_nocache(base, page_offset + 64); + + t1pci_reset(card); + + if ((retval = t1pci_detect(card)) != 0) { + printk(KERN_NOTICE "%s: NO card at 0x%x (%d)\n", + driver->name, card->port, retval); + iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK)); + kfree(card->ctrlinfo); + kfree(card->dma); + kfree(card); + return -EIO; + } + t1pci_reset(card); + + request_region(p->port, AVMB1_PORTLEN, card->name); + + retval = request_irq(card->irq, t1pci_interrupt, SA_SHIRQ, card->name, card); + if (retval) { + printk(KERN_ERR "%s: unable to get IRQ %d.\n", + driver->name, card->irq); + iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK)); + release_region(card->port, AVMB1_PORTLEN); + kfree(card->ctrlinfo); + kfree(card->dma); + kfree(card); + return -EBUSY; + } + + cinfo->capi_ctrl = di->attach_ctr(driver, card->name, cinfo); + if (!cinfo->capi_ctrl) { + printk(KERN_ERR "%s: attach controller failed.\n", driver->name); + iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK)); + free_irq(card->irq, card); + release_region(card->port, AVMB1_PORTLEN); + kfree(card->ctrlinfo); + kfree(card->dma); + kfree(card); + return -EBUSY; + } + card->cardnr = cinfo->capi_ctrl->cnr; + + skb_queue_head_init(&card->dma->send_queue); + + MOD_INC_USE_COUNT; + + return 0; +} + +/* ------------------------------------------------------------- */ + +static struct capi_driver t1pci_driver = { + "t1pci", + "0.0", + t1pci_load_firmware, + t1pci_reset_ctr, + t1pci_remove_ctr, + t1pci_register_appl, + t1pci_release_appl, + t1pci_send_message, + + t1pci_procinfo, + t1pci_read_proc, + 0, /* use standard driver_read_proc */ + + 0, /* no add_card function */ +}; + +#ifdef MODULE +#define t1pci_init init_module +void cleanup_module(void); +#endif + +static int ncards = 0; + +int t1pci_init(void) +{ + struct capi_driver *driver = &t1pci_driver; + struct pci_dev *dev = NULL; + char *p; + int retval; + + if ((p = strchr(revision, ':'))) { + strncpy(driver->revision, p + 1, sizeof(driver->revision)); + p = strchr(driver->revision, '$'); + *p = 0; + } + + printk(KERN_INFO "%s: revision %s\n", driver->name, driver->revision); + + di = attach_capi_driver(driver); + + if (!di) { + printk(KERN_ERR "%s: failed to attach capi_driver\n", + driver->name); + return -EIO; + } + +#ifdef CONFIG_PCI + if (!pci_present()) { + printk(KERN_ERR "%s: no PCI bus present\n", driver->name); + detach_capi_driver(driver); + return -EIO; + } + + while ((dev = pci_find_device(PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_T1, dev))) { + struct capicardparams param; + + param.port = dev->base_address[ 1] & PCI_BASE_ADDRESS_IO_MASK; + param.irq = dev->irq; + param.membase = dev->base_address[ 0] & PCI_BASE_ADDRESS_MEM_MASK; + + 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); + retval = t1pci_add_card(driver, ¶m); + if (retval != 0) { + printk(KERN_ERR + "%s: no AVM-T1-PCI at i/o %#x, irq %d detected, mem %#x\n", + driver->name, param.port, param.irq, param.membase); +#ifdef MODULE + cleanup_module(); +#endif + return retval; + } + ncards++; + } + if (ncards) { + printk(KERN_INFO "%s: %d T1-PCI card(s) detected\n", + driver->name, ncards); + return 0; + } + printk(KERN_ERR "%s: NO T1-PCI card detected\n", driver->name); + return -ESRCH; +#else + printk(KERN_ERR "%s: kernel not compiled with PCI.\n", driver->name); + return -EIO; +#endif +} + +#ifdef MODULE +void cleanup_module(void) +{ + detach_capi_driver(&t1pci_driver); +} +#endif diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/eicon/eicon.h linux/drivers/isdn/eicon/eicon.h --- v2.2.13/linux/drivers/isdn/eicon/eicon.h Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/eicon/eicon.h Tue Jan 4 10:12:15 2000 @@ -1,4 +1,4 @@ -/* $Id: eicon.h,v 1.14 1999/09/08 20:17:31 armin Exp $ +/* $Id: eicon.h,v 1.18 1999/11/25 11:43:27 armin Exp $ * * ISDN low-level module for Eicon.Diehl active ISDN-Cards. * @@ -21,6 +21,22 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon.h,v $ + * Revision 1.18 1999/11/25 11:43:27 armin + * Fixed statectrl and connect message. + * X.75 fix and HDLC/transparent with autoconnect. + * Minor cleanup. + * + * Revision 1.17 1999/10/26 21:15:33 armin + * using define for checking phone number len to avoid buffer overflow. + * + * Revision 1.16 1999/10/08 22:09:33 armin + * Some fixes of cards interface handling. + * Bugfix of NULL pointer occurence. + * Changed a few log outputs. + * + * Revision 1.15 1999/09/26 14:17:53 armin + * Improved debug and log via readstat() + * * Revision 1.14 1999/09/08 20:17:31 armin * Added microchannel patch from Erik Weber. * @@ -126,6 +142,7 @@ #define MAX_HEADER_LEN 10 +#define MAX_STATUS_BUFFER 150 /* Struct for adding new cards */ typedef struct eicon_cdef { @@ -233,6 +250,7 @@ #include #include +#include #include @@ -480,14 +498,14 @@ typedef struct { int No; /* Channel Number */ - unsigned short callref; /* Call Reference */ unsigned short fsm_state; /* Current D-Channel state */ + unsigned short statectrl; /* State controling bits */ unsigned short eazmask; /* EAZ-Mask for this Channel */ int queued; /* User-Data Bytes in TX queue */ int waitq; /* User-Data Bytes in wait queue */ int waitpq; /* User-Data Bytes in packet queue */ - unsigned short plci; - unsigned short ncci; + struct sk_buff *tskb1; /* temp skb 1 */ + struct sk_buff *tskb2; /* temp skb 2 */ unsigned char l2prot; /* Layer 2 protocol */ unsigned char l3prot; /* Layer 3 protocol */ #ifdef CONFIG_ISDN_TTY_FAX @@ -497,9 +515,13 @@ entity e; /* Entity */ char cpn[32]; /* remember cpn */ char oad[32]; /* remember oad */ + char dsa[32]; /* remember dsa */ + char osa[32]; /* remember osa */ unsigned char cause[2]; /* Last Cause */ unsigned char si1; unsigned char si2; + unsigned char plan; + unsigned char screen; } eicon_chan; typedef struct { @@ -535,7 +557,7 @@ #define EICON_STATE_LISTEN 15 #define EICON_STATE_WMCONN 16 -#define EICON_MAX_QUEUED 8000 /* 2 * maxbuff */ +#define EICON_MAX_QUEUE 2138 #define EICON_LOCK_TX 0 #define EICON_LOCK_RX 1 @@ -584,19 +606,16 @@ struct eicon_card *next; /* Pointer to next device struct */ int myid; /* Driver-Nr. assigned by linklevel */ unsigned long flags; /* Statusflags */ - unsigned long ilock; /* Semaphores for IRQ-Routines */ struct sk_buff_head rcvq; /* Receive-Message queue */ struct sk_buff_head sndq; /* Send-Message queue */ struct sk_buff_head rackq; /* Req-Ack-Message queue */ struct sk_buff_head sackq; /* Data-Ack-Message queue */ - u_char *ack_msg; /* Ptr to User Data in User skb */ - __u16 need_b3ack; /* Flag: Need ACK for current skb */ - struct sk_buff *sbuf; /* skb which is currently sent */ + struct sk_buff_head statq; /* Status-Message queue */ + int statq_entries; struct tq_struct snd_tq; /* Task struct for xmit bh */ struct tq_struct rcv_tq; /* Task struct for rcv bh */ struct tq_struct ack_tq; /* Task struct for ack bh */ msn_entry *msn_list; - unsigned short msgnum; /* Message number for sending */ eicon_chan* IdTable[256]; /* Table to find entity */ __u16 ref_in; __u16 ref_out; @@ -677,6 +696,7 @@ #endif /* CONFIG_MCA */ extern ulong DebugVar; +extern void eicon_log(eicon_card * card, int level, const char *fmt, ...); #endif /* __KERNEL__ */ diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/eicon/eicon_idi.c linux/drivers/isdn/eicon/eicon_idi.c --- v2.2.13/linux/drivers/isdn/eicon/eicon_idi.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/eicon/eicon_idi.c Tue Jan 4 10:12:15 2000 @@ -1,4 +1,4 @@ -/* $Id: eicon_idi.c,v 1.18 1999/09/07 12:48:05 armin Exp $ +/* $Id: eicon_idi.c,v 1.27 1999/11/29 13:12:03 armin Exp $ * * ISDN lowlevel-module for Eicon.Diehl active cards. * IDI interface @@ -6,6 +6,11 @@ * Copyright 1998,99 by Armin Schindler (mac@melware.de) * Copyright 1999 Cytronics & Melware (info@melware.de) * + * Thanks to Deutsche Mailbox Saar-Lor-Lux GmbH + * for sponsoring and testing fax + * capabilities with Diva Server cards. + * (dor@deutschemailbox.de) + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) @@ -21,6 +26,38 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_idi.c,v $ + * Revision 1.27 1999/11/29 13:12:03 armin + * Autoconnect on L2_TRANS doesn't work with link_level correctly, + * changed back to former mode. + * + * Revision 1.26 1999/11/25 11:43:27 armin + * Fixed statectrl and connect message. + * X.75 fix and HDLC/transparent with autoconnect. + * Minor cleanup. + * + * Revision 1.25 1999/11/18 20:30:55 armin + * removed old workaround for ISA cards. + * + * Revision 1.24 1999/10/26 21:15:33 armin + * using define for checking phone number len to avoid buffer overflow. + * + * Revision 1.23 1999/10/11 18:13:25 armin + * Added fax capabilities for Eicon Diva Server cards. + * + * Revision 1.22 1999/10/08 22:09:33 armin + * Some fixes of cards interface handling. + * Bugfix of NULL pointer occurence. + * Changed a few log outputs. + * + * Revision 1.21 1999/09/26 14:17:53 armin + * Improved debug and log via readstat() + * + * Revision 1.20 1999/09/21 20:35:43 armin + * added more error checking. + * + * Revision 1.19 1999/09/21 20:06:40 armin + * Added pointer checks. + * * Revision 1.18 1999/09/07 12:48:05 armin * Prepared for sub-address usage. * @@ -105,7 +142,7 @@ #undef EICON_FULL_SERVICE_OKTETT -char *eicon_idi_revision = "$Revision: 1.18 $"; +char *eicon_idi_revision = "$Revision: 1.27 $"; eicon_manifbuf *manbuf; @@ -162,16 +199,13 @@ reqbuf->XBuffer.P[l++] = LLC; reqbuf->XBuffer.P[l++] = 2; switch(chan->l2prot) { - case ISDN_PROTO_L2_HDLC: - reqbuf->XBuffer.P[l++] = 2; + case ISDN_PROTO_L2_TRANS: + reqbuf->XBuffer.P[l++] = 2; /* transparent */ break; case ISDN_PROTO_L2_X75I: case ISDN_PROTO_L2_X75UI: case ISDN_PROTO_L2_X75BUI: - reqbuf->XBuffer.P[l++] = 5; - break; - case ISDN_PROTO_L2_TRANS: - reqbuf->XBuffer.P[l++] = 2; + reqbuf->XBuffer.P[l++] = 5; /* X.75 */ break; case ISDN_PROTO_L2_MODEM: if (chan->fsm_state == EICON_STATE_IWAIT) @@ -179,11 +213,12 @@ else reqbuf->XBuffer.P[l++] = 10; /* V.42 */ break; + case ISDN_PROTO_L2_HDLC: case ISDN_PROTO_L2_FAX: if (chan->fsm_state == EICON_STATE_IWAIT) reqbuf->XBuffer.P[l++] = 3; /* autoconnect on incoming */ else - reqbuf->XBuffer.P[l++] = 2; + reqbuf->XBuffer.P[l++] = 2; /* transparent */ break; default: reqbuf->XBuffer.P[l++] = 1; @@ -289,8 +324,7 @@ reqbuf->XBuffer.P[8] = 0; reqbuf->XBuffer.length = l; reqbuf->Reference = 0; /* Sig Entity */ - if (DebugVar & 8) - printk(KERN_DEBUG"idi_req: Ch%d: Call_Res\n", chan->No); + eicon_log(NULL, 8, "idi_req: Ch%d: Call_Res\n", chan->No); return(0); } @@ -306,8 +340,7 @@ skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC); if ((!skb) || (!skb2)) { - if (DebugVar & 1) - printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed in do_req()\n", chan->No); + eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in do_req()\n", chan->No); if (skb) dev_kfree_skb(skb); if (skb2) @@ -319,8 +352,7 @@ chan2->ptr = chan; reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ)); - if (DebugVar & 8) - printk(KERN_DEBUG "idi_req: Ch%d: req %x (%s)\n", chan->No, cmd, (layer)?"Net":"Sig"); + eicon_log(card, 8, "idi_req: Ch%d: req %x (%s)\n", chan->No, cmd, (layer)?"Net":"Sig"); if (layer) cmd |= 0x700; switch(cmd) { case ASSIGN: @@ -359,8 +391,7 @@ idi_put_req(reqbuf, IDI_N_DISC_ACK, 1); break; default: - if (DebugVar & 1) - printk(KERN_ERR "idi_req: Ch%d: Unknown request\n", chan->No); + eicon_log(card, 1, "idi_req: Ch%d: Unknown request\n", chan->No); dev_kfree_skb(skb); dev_kfree_skb(skb2); return(-1); @@ -375,14 +406,18 @@ int eicon_idi_listen_req(eicon_card *card, eicon_chan *chan) { - if (DebugVar & 16) - printk(KERN_DEBUG"idi_req: Ch%d: Listen_Req eazmask=0x%x\n",chan->No, chan->eazmask); + if ((!card) || (!chan)) + return 1; + + eicon_log(card, 16, "idi_req: Ch%d: Listen_Req eazmask=0x%x\n",chan->No, chan->eazmask); if (!chan->e.D3Id) { idi_do_req(card, chan, ASSIGN, 0); } if (chan->fsm_state == EICON_STATE_NULL) { - idi_do_req(card, chan, INDICATE_REQ, 0); - chan->fsm_state = EICON_STATE_LISTEN; + if (!(chan->statectrl & HAVE_CONN_REQ)) { + idi_do_req(card, chan, INDICATE_REQ, 0); + chan->fsm_state = EICON_STATE_LISTEN; + } } return(0); } @@ -430,15 +465,20 @@ int idi_hangup(eicon_card *card, eicon_chan *chan) { + if ((!card) || (!chan)) + return 1; + if ((chan->fsm_state == EICON_STATE_ACTIVE) || (chan->fsm_state == EICON_STATE_WMCONN)) { if (chan->e.B2Id) idi_do_req(card, chan, IDI_N_DISC, 1); } if (chan->e.B2Id) idi_do_req(card, chan, REMOVE, 1); - idi_do_req(card, chan, HANGUP, 0); - chan->fsm_state = EICON_STATE_NULL; - if (DebugVar & 8) - printk(KERN_DEBUG"idi_req: Ch%d: Hangup\n", chan->No); + if (chan->fsm_state != EICON_STATE_NULL) { + chan->statectrl |= WAITING_FOR_HANGUP; + idi_do_req(card, chan, HANGUP, 0); + chan->fsm_state = EICON_STATE_NULL; + } + eicon_log(card, 8, "idi_req: Ch%d: Hangup\n", chan->No); #ifdef CONFIG_ISDN_TTY_FAX chan->fax = 0; #endif @@ -448,18 +488,20 @@ int idi_connect_res(eicon_card *card, eicon_chan *chan) { + if ((!card) || (!chan)) + return 1; + chan->fsm_state = EICON_STATE_IWAIT; - idi_do_req(card, chan, CALL_RES, 0); /* check if old NetID has been removed */ if (chan->e.B2Id) { - if (DebugVar & 1) - printk(KERN_WARNING "idi_err: Ch%d: Old NetID %x was not removed.\n", - chan->No, chan->e.B2Id); + eicon_log(card, 1, "eicon: Ch%d: old net_id %x still exist, removing.\n", + chan->No, chan->e.B2Id); idi_do_req(card, chan, REMOVE, 1); } idi_do_req(card, chan, ASSIGN, 1); + idi_do_req(card, chan, CALL_RES, 0); return(0); } @@ -478,12 +520,14 @@ eicon_REQ *reqbuf; eicon_chan_ptr *chan2; + if ((!card) || (!chan)) + return 1; + skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC); skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC); if ((!skb) || (!skb2)) { - if (DebugVar & 1) - printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed in connect_req()\n", chan->No); + eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in connect_req()\n", chan->No); if (skb) dev_kfree_skb(skb); if (skb2) @@ -625,12 +669,20 @@ reqbuf->XBuffer.length = l; reqbuf->Reference = 0; /* Sig Entity */ - skb_queue_tail(&chan->e.X, skb); - skb_queue_tail(&card->sndq, skb2); - eicon_schedule_tx(card); + if (chan->statectrl & WAITING_FOR_HANGUP) { + /* If the line did not disconnect yet, + we have to delay this command */ + eicon_log(card, 32, "idi_req: Ch%d: delaying conn_req\n", chan->No); + chan->statectrl |= HAVE_CONN_REQ; + chan->tskb1 = skb; + chan->tskb2 = skb2; + } else { + skb_queue_tail(&chan->e.X, skb); + skb_queue_tail(&card->sndq, skb2); + eicon_schedule_tx(card); + } - if (DebugVar & 8) - printk(KERN_DEBUG"idi_req: Ch%d: Conn_Req %s -> %s\n",chan->No, eazmsn, phone); + eicon_log(card, 8, "idi_req: Ch%d: Conn_Req %s -> %s\n",chan->No, eazmsn, phone); return(0); } @@ -647,9 +699,10 @@ __u16 code; isdn_ctrl cmd; - memset(message, 0, sizeof(idi_ind_message)); + memset(message, 0, sizeof(idi_ind_message)); + + if ((!len) || (!buffer[pos])) return; - if ((!len) || (!buffer[pos])) return; while(pos <= len) { w = buffer[pos++]; if (!w) return; @@ -679,8 +732,18 @@ else code = w; code |= (codeset<<8); + if (pos + wlen > len) { + eicon_log(ccard, 1, "idi_err: Ch%d: IElen %d of %x exceeds Ind_Length (+%d)\n", chan->No, + wlen, code, (pos + wlen) - len); + return; + } + switch(code) { case OAD: + if (wlen > sizeof(message->oad)) { + pos += wlen; + break; + } j = 1; if (wlen) { message->plan = buffer[pos++]; @@ -693,11 +756,14 @@ } for(i=0; i < wlen-j; i++) message->oad[i] = buffer[pos++]; - if (DebugVar & 2) - printk(KERN_DEBUG"idi_inf: Ch%d: OAD=(0x%02x,0x%02x) %s\n", chan->No, - message->plan, message->screen, message->oad); + eicon_log(ccard, 2, "idi_inf: Ch%d: OAD=(0x%02x,0x%02x) %s\n", chan->No, + message->plan, message->screen, message->oad); break; case RDN: + if (wlen > sizeof(message->rdn)) { + pos += wlen; + break; + } j = 1; if (wlen) { if (!(buffer[pos++] & 0x80)) { @@ -707,92 +773,116 @@ } for(i=0; i < wlen-j; i++) message->rdn[i] = buffer[pos++]; - if (DebugVar & 2) - printk(KERN_DEBUG"idi_inf: Ch%d: RDN= %s\n", chan->No, + eicon_log(ccard, 2, "idi_inf: Ch%d: RDN= %s\n", chan->No, message->rdn); break; case CPN: + if (wlen > sizeof(message->cpn)) { + pos += wlen; + break; + } for(i=0; i < wlen; i++) message->cpn[i] = buffer[pos++]; - if (DebugVar & 2) - printk(KERN_DEBUG"idi_inf: Ch%d: CPN=(0x%02x) %s\n", chan->No, - (__u8)message->cpn[0], message->cpn + 1); + eicon_log(ccard, 2, "idi_inf: Ch%d: CPN=(0x%02x) %s\n", chan->No, + (__u8)message->cpn[0], message->cpn + 1); break; case DSA: + if (wlen > sizeof(message->dsa)) { + pos += wlen; + break; + } pos += 2; for(i=0; i < wlen-2; i++) message->dsa[i] = buffer[pos++]; - if (DebugVar & 2) - printk(KERN_DEBUG"idi_inf: Ch%d: DSA=%s\n", chan->No, message->dsa); + eicon_log(ccard, 2, "idi_inf: Ch%d: DSA=%s\n", chan->No, message->dsa); break; case OSA: + if (wlen > sizeof(message->osa)) { + pos += wlen; + break; + } pos += 2; for(i=0; i < wlen-2; i++) message->osa[i] = buffer[pos++]; - if (DebugVar & 2) - printk(KERN_DEBUG"idi_inf: Ch%d: OSA=%s\n", chan->No, message->osa); + eicon_log(ccard, 2, "idi_inf: Ch%d: OSA=%s\n", chan->No, message->osa); break; case BC: + if (wlen > sizeof(message->bc)) { + pos += wlen; + break; + } for(i=0; i < wlen; i++) message->bc[i] = buffer[pos++]; - if (DebugVar & 4) - printk(KERN_DEBUG"idi_inf: Ch%d: BC = 0x%02x 0x%02x 0x%02x\n", chan->No, - message->bc[0],message->bc[1],message->bc[2]); + eicon_log(ccard, 4, "idi_inf: Ch%d: BC = 0x%02x 0x%02x 0x%02x\n", chan->No, + message->bc[0],message->bc[1],message->bc[2]); break; case 0x800|BC: + if (wlen > sizeof(message->e_bc)) { + pos += wlen; + break; + } for(i=0; i < wlen; i++) message->e_bc[i] = buffer[pos++]; - if (DebugVar & 4) - printk(KERN_DEBUG"idi_inf: Ch%d: ESC/BC=%d\n", chan->No, message->bc[0]); + eicon_log(ccard, 4, "idi_inf: Ch%d: ESC/BC=%d\n", chan->No, message->bc[0]); break; case LLC: + if (wlen > sizeof(message->llc)) { + pos += wlen; + break; + } for(i=0; i < wlen; i++) message->llc[i] = buffer[pos++]; - if (DebugVar & 4) - printk(KERN_DEBUG"idi_inf: Ch%d: LLC=%d %d %d %d\n", chan->No, message->llc[0], - message->llc[1],message->llc[2],message->llc[3]); + eicon_log(ccard, 4, "idi_inf: Ch%d: LLC=%d %d %d %d\n", chan->No, message->llc[0], + message->llc[1],message->llc[2],message->llc[3]); break; case HLC: + if (wlen > sizeof(message->hlc)) { + pos += wlen; + break; + } for(i=0; i < wlen; i++) message->hlc[i] = buffer[pos++]; - if (DebugVar & 4) - printk(KERN_DEBUG"idi_inf: Ch%d: HLC=%x %x %x %x %x\n", chan->No, - message->hlc[0], message->hlc[1], - message->hlc[2], message->hlc[3], message->hlc[4]); + eicon_log(ccard, 4, "idi_inf: Ch%d: HLC=%x %x %x %x %x\n", chan->No, + message->hlc[0], message->hlc[1], + message->hlc[2], message->hlc[3], message->hlc[4]); break; case DSP: case 0x600|DSP: + if (wlen > sizeof(message->display)) { + pos += wlen; + break; + } for(i=0; i < wlen; i++) message->display[i] = buffer[pos++]; - if (DebugVar & 4) - printk(KERN_DEBUG"idi_inf: Ch%d: Display: %s\n", chan->No, - message->display); + eicon_log(ccard, 4, "idi_inf: Ch%d: Display: %s\n", chan->No, + message->display); break; case 0x600|KEY: + if (wlen > sizeof(message->keypad)) { + pos += wlen; + break; + } for(i=0; i < wlen; i++) message->keypad[i] = buffer[pos++]; - if (DebugVar & 4) - printk(KERN_DEBUG"idi_inf: Ch%d: Keypad: %s\n", chan->No, - message->keypad); + eicon_log(ccard, 4, "idi_inf: Ch%d: Keypad: %s\n", chan->No, + message->keypad); break; case NI: case 0x600|NI: if (wlen) { - if (DebugVar & 4) { - switch(buffer[pos] & 127) { - case 0: - printk(KERN_DEBUG"idi_inf: Ch%d: User suspended.\n", chan->No); - break; - case 1: - printk(KERN_DEBUG"idi_inf: Ch%d: User resumed.\n", chan->No); - break; - case 2: - printk(KERN_DEBUG"idi_inf: Ch%d: Bearer service change.\n", chan->No); - break; - default: - printk(KERN_DEBUG"idi_inf: Ch%d: Unknown Notification %x.\n", - chan->No, buffer[pos] & 127); - } + switch(buffer[pos] & 127) { + case 0: + eicon_log(ccard, 4, "idi_inf: Ch%d: User suspended.\n", chan->No); + break; + case 1: + eicon_log(ccard, 4, "idi_inf: Ch%d: User resumed.\n", chan->No); + break; + case 2: + eicon_log(ccard, 4, "idi_inf: Ch%d: Bearer service change.\n", chan->No); + break; + default: + eicon_log(ccard, 4, "idi_inf: Ch%d: Unknown Notification %x.\n", + chan->No, buffer[pos] & 127); } pos += wlen; } @@ -800,80 +890,91 @@ case PI: case 0x600|PI: if (wlen > 1) { - if (DebugVar & 4) { - switch(buffer[pos+1] & 127) { - case 1: - printk(KERN_DEBUG"idi_inf: Ch%d: Call is not end-to-end ISDN.\n", chan->No); - break; - case 2: - printk(KERN_DEBUG"idi_inf: Ch%d: Destination address is non ISDN.\n", chan->No); - break; - case 3: - printk(KERN_DEBUG"idi_inf: Ch%d: Origination address is non ISDN.\n", chan->No); - break; - case 4: - printk(KERN_DEBUG"idi_inf: Ch%d: Call has returned to the ISDN.\n", chan->No); - break; - case 5: - printk(KERN_DEBUG"idi_inf: Ch%d: Interworking has occurred.\n", chan->No); - break; - case 8: - printk(KERN_DEBUG"idi_inf: Ch%d: In-band information available.\n", chan->No); - break; - default: - printk(KERN_DEBUG"idi_inf: Ch%d: Unknown Progress %x.\n", - chan->No, buffer[pos+1] & 127); - } + switch(buffer[pos+1] & 127) { + case 1: + eicon_log(ccard, 4, "idi_inf: Ch%d: Call is not end-to-end ISDN.\n", chan->No); + break; + case 2: + eicon_log(ccard, 4, "idi_inf: Ch%d: Destination address is non ISDN.\n", chan->No); + break; + case 3: + eicon_log(ccard, 4, "idi_inf: Ch%d: Origination address is non ISDN.\n", chan->No); + break; + case 4: + eicon_log(ccard, 4, "idi_inf: Ch%d: Call has returned to the ISDN.\n", chan->No); + break; + case 5: + eicon_log(ccard, 4, "idi_inf: Ch%d: Interworking has occurred.\n", chan->No); + break; + case 8: + eicon_log(ccard, 4, "idi_inf: Ch%d: In-band information available.\n", chan->No); + break; + default: + eicon_log(ccard, 4, "idi_inf: Ch%d: Unknown Progress %x.\n", + chan->No, buffer[pos+1] & 127); } } pos += wlen; break; case CAU: + if (wlen > sizeof(message->cau)) { + pos += wlen; + break; + } for(i=0; i < wlen; i++) message->cau[i] = buffer[pos++]; memcpy(&chan->cause, &message->cau, 2); - if (DebugVar & 4) - printk(KERN_DEBUG"idi_inf: Ch%d: CAU=%d %d\n", chan->No, - message->cau[0],message->cau[1]); + eicon_log(ccard, 4, "idi_inf: Ch%d: CAU=%d %d\n", chan->No, + message->cau[0],message->cau[1]); break; case 0x800|CAU: + if (wlen > sizeof(message->e_cau)) { + pos += wlen; + break; + } for(i=0; i < wlen; i++) message->e_cau[i] = buffer[pos++]; - if (DebugVar & 4) - printk(KERN_DEBUG"idi_inf: Ch%d: ECAU=%d %d\n", chan->No, - message->e_cau[0],message->e_cau[1]); + eicon_log(ccard, 4, "idi_inf: Ch%d: ECAU=%d %d\n", chan->No, + message->e_cau[0],message->e_cau[1]); break; case 0x800|CHI: + if (wlen > sizeof(message->e_chi)) { + pos += wlen; + break; + } for(i=0; i < wlen; i++) message->e_chi[i] = buffer[pos++]; - if (DebugVar & 4) - printk(KERN_DEBUG"idi_inf: Ch%d: ESC/CHI=%d\n", chan->No, - message->e_cau[0]); + eicon_log(ccard, 4, "idi_inf: Ch%d: ESC/CHI=%d\n", chan->No, + message->e_cau[0]); break; case 0x800|0x7a: pos ++; message->e_mt=buffer[pos++]; - if (DebugVar & 2) - printk(KERN_DEBUG"idi_inf: Ch%d: EMT=0x%x\n", chan->No, message->e_mt); + eicon_log(ccard, 4, "idi_inf: Ch%d: EMT=0x%x\n", chan->No, message->e_mt); break; case DT: + if (wlen > sizeof(message->dt)) { + pos += wlen; + break; + } for(i=0; i < wlen; i++) message->dt[i] = buffer[pos++]; - if (DebugVar & 4) - printk(KERN_DEBUG"idi_inf: Ch%d: DT: %02d.%02d.%02d %02d:%02d:%02d\n", chan->No, - message->dt[2], message->dt[1], message->dt[0], - message->dt[3], message->dt[4], message->dt[5]); + eicon_log(ccard, 4, "idi_inf: Ch%d: DT: %02d.%02d.%02d %02d:%02d:%02d\n", chan->No, + message->dt[2], message->dt[1], message->dt[0], + message->dt[3], message->dt[4], message->dt[5]); break; case 0x600|SIN: + if (wlen > sizeof(message->sin)) { + pos += wlen; + break; + } for(i=0; i < wlen; i++) message->sin[i] = buffer[pos++]; - if (DebugVar & 2) - printk(KERN_DEBUG"idi_inf: Ch%d: SIN=%d %d\n", chan->No, - message->sin[0],message->sin[1]); + eicon_log(ccard, 2, "idi_inf: Ch%d: SIN=%d %d\n", chan->No, + message->sin[0],message->sin[1]); break; case 0x600|CPS: - if (DebugVar & 2) - printk(KERN_DEBUG"idi_inf: Ch%d: Called Party Status in ind\n", chan->No); + eicon_log(ccard, 2, "idi_inf: Ch%d: Called Party Status in ind\n", chan->No); pos += wlen; break; case 0x600|CIF: @@ -881,8 +982,7 @@ if (buffer[pos + i] != '0') break; memcpy(&cmd.parm.num, &buffer[pos + i], wlen - i); cmd.parm.num[wlen - i] = 0; - if (DebugVar & 2) - printk(KERN_DEBUG"idi_inf: Ch%d: CIF=%s\n", chan->No, cmd.parm.num); + eicon_log(ccard, 2, "idi_inf: Ch%d: CIF=%s\n", chan->No, cmd.parm.num); pos += wlen; cmd.driver = ccard->myid; cmd.command = ISDN_STAT_CINF; @@ -890,8 +990,11 @@ ccard->interface.statcallb(&cmd); break; case 0x600|DATE: - if (DebugVar & 2) - printk(KERN_DEBUG"idi_inf: Ch%d: Date in ind\n", chan->No); + eicon_log(ccard, 2, "idi_inf: Ch%d: Date in ind\n", chan->No); + pos += wlen; + break; + case 0xa1: + eicon_log(ccard, 2, "idi_inf: Ch%d: Sending Complete in ind.\n", chan->No); pos += wlen; break; case 0xe08: @@ -911,8 +1014,7 @@ case 0x880: /* Managment Information Element */ if (!manbuf) { - if (DebugVar & 1) - printk(KERN_WARNING"idi_err: manbuf not allocated\n"); + eicon_log(ccard, 1, "idi_err: manbuf not allocated\n"); } else { memcpy(&manbuf->data[manbuf->pos], &buffer[pos], wlen); @@ -924,9 +1026,8 @@ break; default: pos += wlen; - if (DebugVar & 6) - printk(KERN_WARNING"idi_inf: Ch%d: unknown information element 0x%x in ind, len:%x\n", - chan->No, code, wlen); + eicon_log(ccard, 6, "idi_inf: Ch%d: unknown information element 0x%x in ind, len:%x\n", + chan->No, code, wlen); } } } @@ -967,110 +1068,1099 @@ int idi_fill_in_T30(eicon_chan *chan, unsigned char *buffer) { + eicon_t30_s *t30 = (eicon_t30_s *) buffer; - /* TODO , code follows */ + if (!chan->fax) { + eicon_log(NULL, 1,"idi_T30: fill_in with NULL fax struct, ERROR\n"); + return 0; + } + memset(t30, 0, sizeof(eicon_t30_s)); + t30->station_id_len = EICON_FAXID_LEN; + memcpy(&t30->station_id[0], &chan->fax->id[0], EICON_FAXID_LEN); + t30->resolution = chan->fax->resolution; + t30->rate = chan->fax->rate + 1; /* eicon rate starts with 1 */ + t30->format = T30_FORMAT_SFF; + t30->pages_low = 0; + t30->pages_high = 0; + t30->atf = 1; /* optimised for AT+F command set */ + t30->code = 0; + t30->feature_bits_low = 0; + t30->feature_bits_high = 0; + t30->control_bits_low = 0; + t30->control_bits_high = 0; + + if (chan->fax->nbc) { + /* set compression by DCC value */ + switch(chan->fax->compression) { + case (0): /* 1-D modified */ + break; + case (1): /* 2-D modified Read */ + t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_2D_CODING; + t30->feature_bits_low |= T30_FEATURE_BIT_2D_CODING; + break; + case (2): /* 2-D uncompressed */ + t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_UNCOMPR; + t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_2D_CODING; + t30->feature_bits_low |= T30_FEATURE_BIT_UNCOMPR_ENABLED; + t30->feature_bits_low |= T30_FEATURE_BIT_2D_CODING; + break; + case (3): /* 2-D modified Read */ + t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_T6_CODING; + t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_2D_CODING; + t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_UNCOMPR; + t30->feature_bits_low |= T30_FEATURE_BIT_UNCOMPR_ENABLED; + t30->feature_bits_low |= T30_FEATURE_BIT_T6_CODING; + t30->feature_bits_low |= T30_FEATURE_BIT_2D_CODING; + t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_ECM; + t30->feature_bits_low |= T30_FEATURE_BIT_ECM; + break; + } + } else { + /* set compression to best */ + t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_T6_CODING; + t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_2D_CODING; + t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_UNCOMPR; + t30->feature_bits_low |= T30_FEATURE_BIT_UNCOMPR_ENABLED; + t30->feature_bits_low |= T30_FEATURE_BIT_T6_CODING; + t30->feature_bits_low |= T30_FEATURE_BIT_2D_CODING; + t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_ECM; + t30->feature_bits_low |= T30_FEATURE_BIT_ECM; + } + switch(chan->fax->ecm) { + case (0): /* disable ECM */ + break; + case (1): + t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_ECM; + t30->control_bits_low |= T30_CONTROL_BIT_ECM_64_BYTES; + t30->feature_bits_low |= T30_FEATURE_BIT_ECM; + t30->feature_bits_low |= T30_FEATURE_BIT_ECM_64_BYTES; + break; + case (2): + t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_ECM; + t30->feature_bits_low |= T30_FEATURE_BIT_ECM; + break; + } - return(0); + if (DebugVar & 128) { + char st[40]; + eicon_log(NULL, 128, "sT30:code = %x\n", t30->code); + eicon_log(NULL, 128, "sT30:rate = %x\n", t30->rate); + eicon_log(NULL, 128, "sT30:res = %x\n", t30->resolution); + eicon_log(NULL, 128, "sT30:format = %x\n", t30->format); + eicon_log(NULL, 128, "sT30:pages_low = %x\n", t30->pages_low); + eicon_log(NULL, 128, "sT30:pages_high = %x\n", t30->pages_high); + eicon_log(NULL, 128, "sT30:atf = %x\n", t30->atf); + eicon_log(NULL, 128, "sT30:control_bits_low = %x\n", t30->control_bits_low); + eicon_log(NULL, 128, "sT30:control_bits_high = %x\n", t30->control_bits_high); + eicon_log(NULL, 128, "sT30:feature_bits_low = %x\n", t30->feature_bits_low); + eicon_log(NULL, 128, "sT30:feature_bits_high = %x\n", t30->feature_bits_high); + //eicon_log(NULL, 128, "sT30:universal_5 = %x\n", t30->universal_5); + //eicon_log(NULL, 128, "sT30:universal_6 = %x\n", t30->universal_6); + //eicon_log(NULL, 128, "sT30:universal_7 = %x\n", t30->universal_7); + eicon_log(NULL, 128, "sT30:station_id_len = %x\n", t30->station_id_len); + eicon_log(NULL, 128, "sT30:head_line_len = %x\n", t30->head_line_len); + strncpy(st, t30->station_id, t30->station_id_len); + st[t30->station_id_len] = 0; + eicon_log(NULL, 128, "sT30:station_id = <%s>\n", st); + } + return(sizeof(eicon_t30_s)); } /* send fax struct */ int idi_send_edata(eicon_card *card, eicon_chan *chan) { + struct sk_buff *skb; + struct sk_buff *skb2; + eicon_REQ *reqbuf; + eicon_chan_ptr *chan2; + + if ((chan->fsm_state == EICON_STATE_NULL) || (chan->fsm_state == EICON_STATE_LISTEN)) { + eicon_log(card, 1, "idi_snd: Ch%d: send edata on state %d !\n", chan->No, chan->fsm_state); + return -ENODEV; + } + eicon_log(card, 128, "idi_snd: Ch%d: edata (fax)\n", chan->No); + + skb = alloc_skb(sizeof(eicon_REQ) + sizeof(eicon_t30_s), GFP_ATOMIC); + skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC); + + if ((!skb) || (!skb2)) { + eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in send_edata()\n", chan->No); + if (skb) + dev_kfree_skb(skb); + if (skb2) + dev_kfree_skb(skb2); + return -ENOMEM; + } + + chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr)); + chan2->ptr = chan; + + reqbuf = (eicon_REQ *)skb_put(skb, sizeof(eicon_t30_s) + sizeof(eicon_REQ)); - /* TODO , code follows */ + reqbuf->Req = IDI_N_EDATA; + reqbuf->ReqCh = 0; + reqbuf->ReqId = 1; + + reqbuf->XBuffer.length = idi_fill_in_T30(chan, reqbuf->XBuffer.P); + reqbuf->Reference = 1; /* Net Entity */ + skb_queue_tail(&chan->e.X, skb); + skb_queue_tail(&card->sndq, skb2); + eicon_schedule_tx(card); return (0); } void idi_parse_edata(eicon_card *ccard, eicon_chan *chan, unsigned char *buffer, int len) { + eicon_t30_s *p = (eicon_t30_s *)buffer; + int i; - /* TODO , code follows */ + if (DebugVar & 128) { + char st[40]; + eicon_log(ccard, 128, "rT30:len %d , size %d\n", len, sizeof(eicon_t30_s)); + eicon_log(ccard, 128, "rT30:code = %x\n", p->code); + eicon_log(ccard, 128, "rT30:rate = %x\n", p->rate); + eicon_log(ccard, 128, "rT30:res = %x\n", p->resolution); + eicon_log(ccard, 128, "rT30:format = %x\n", p->format); + eicon_log(ccard, 128, "rT30:pages_low = %x\n", p->pages_low); + eicon_log(ccard, 128, "rT30:pages_high = %x\n", p->pages_high); + eicon_log(ccard, 128, "rT30:atf = %x\n", p->atf); + eicon_log(ccard, 128, "rT30:control_bits_low = %x\n", p->control_bits_low); + eicon_log(ccard, 128, "rT30:control_bits_high = %x\n", p->control_bits_high); + eicon_log(ccard, 128, "rT30:feature_bits_low = %x\n", p->feature_bits_low); + eicon_log(ccard, 128, "rT30:feature_bits_high = %x\n", p->feature_bits_high); + //eicon_log(ccard, 128, "rT30:universal_5 = %x\n", p->universal_5); + //eicon_log(ccard, 128, "rT30:universal_6 = %x\n", p->universal_6); + //eicon_log(ccard, 128, "rT30:universal_7 = %x\n", p->universal_7); + eicon_log(ccard, 128, "rT30:station_id_len = %x\n", p->station_id_len); + eicon_log(ccard, 128, "rT30:head_line_len = %x\n", p->head_line_len); + strncpy(st, p->station_id, p->station_id_len); + st[p->station_id_len] = 0; + eicon_log(ccard, 128, "rT30:station_id = <%s>\n", st); + } + if (!chan->fax) { + eicon_log(ccard, 1, "idi_edata: parse to NULL fax struct, ERROR\n"); + return; + } + chan->fax->code = p->code; + i = (p->station_id_len < FAXIDLEN) ? p->station_id_len : (FAXIDLEN - 1); + memcpy(chan->fax->r_id, p->station_id, i); + chan->fax->r_id[i] = 0; + chan->fax->r_resolution = p->resolution; + chan->fax->r_rate = p->rate - 1; + chan->fax->r_binary = 0; /* no binary support */ + chan->fax->r_width = 0; + chan->fax->r_length = 2; + chan->fax->r_scantime = 0; + chan->fax->r_compression = 0; + chan->fax->r_ecm = 0; + if (p->feature_bits_low & T30_FEATURE_BIT_2D_CODING) { + chan->fax->r_compression = 1; + if (p->feature_bits_low & T30_FEATURE_BIT_UNCOMPR_ENABLED) { + chan->fax->r_compression = 2; + } + } + if (p->feature_bits_low & T30_FEATURE_BIT_T6_CODING) { + chan->fax->r_compression = 3; + } + if (p->feature_bits_low & T30_FEATURE_BIT_ECM) { + chan->fax->r_ecm = 2; + if (p->feature_bits_low & T30_FEATURE_BIT_ECM_64_BYTES) + chan->fax->r_ecm = 1; + } } void idi_fax_send_header(eicon_card *card, eicon_chan *chan, int header) { + static __u16 wd2sff[] = { + 1728, 2048, 2432, 1216, 864 + }; + static __u16 ln2sff[2][3] = { + { 1143, 1401, 0 } , { 2287, 2802, 0 } + }; + struct sk_buff *skb; + eicon_sff_dochead *doc; + eicon_sff_pagehead *page; + u_char *docp; + + if (!chan->fax) { + eicon_log(card, 1, "idi_fax: send head with NULL fax struct, ERROR\n"); + return; + } + if (header == 2) { /* DocHeader + PageHeader */ + skb = alloc_skb(sizeof(eicon_sff_dochead) + sizeof(eicon_sff_pagehead), GFP_ATOMIC); + } else { + skb = alloc_skb(sizeof(eicon_sff_pagehead), GFP_ATOMIC); + } + if (!skb) { + eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in fax_send_header()\n", chan->No); + return; + } - /* TODO , code follows */ + if (header == 2) { /* DocHeader + PageHeader */ + docp = skb_put(skb, sizeof(eicon_sff_dochead) + sizeof(eicon_sff_pagehead)); + doc = (eicon_sff_dochead *) docp; + page = (eicon_sff_pagehead *) (docp + sizeof(eicon_sff_dochead)); + memset(docp, 0,sizeof(eicon_sff_dochead) + sizeof(eicon_sff_pagehead)); + doc->id = 0x66666653; + doc->version = 0x01; + doc->off1pagehead = sizeof(eicon_sff_dochead); + } else { + page = (eicon_sff_pagehead *)skb_put(skb, sizeof(eicon_sff_pagehead)); + memset(page, 0, sizeof(eicon_sff_pagehead)); + } + switch(header) { + case 1: /* PageHeaderEnd */ + page->pageheadid = 254; + page->pageheadlen = 0; + break; + case 0: /* PageHeader */ + case 2: /* DocHeader + PageHeader */ + page->pageheadid = 254; + page->pageheadlen = sizeof(eicon_sff_pagehead) - 2; + page->resvert = chan->fax->resolution; + page->reshoriz = 0; /* always 203 dpi */ + page->coding = 0; /* always 1D */ + page->linelength = wd2sff[chan->fax->width]; + page->pagelength = ln2sff[chan->fax->resolution][chan->fax->length]; + eicon_log(card, 128, "sSFF-Head: linelength = %d\n", page->linelength); + eicon_log(card, 128, "sSFF-Head: pagelength = %d\n", page->pagelength); + break; + } + idi_send_data(card, chan, 0, skb, 0); } void idi_fax_cmd(eicon_card *card, eicon_chan *chan) { + isdn_ctrl cmd; + + if ((!card) || (!chan)) + return; + + if (!chan->fax) { + eicon_log(card, 1, "idi_fax: cmd with NULL fax struct, ERROR\n"); + return; + } + switch (chan->fax->code) { + case ISDN_TTY_FAX_DT: + if (chan->fax->phase == ISDN_FAX_PHASE_B) { + idi_send_edata(card, chan); + break; + } + if (chan->fax->phase == ISDN_FAX_PHASE_D) { + idi_send_edata(card, chan); + break; + } + break; + + case ISDN_TTY_FAX_DR: + if (chan->fax->phase == ISDN_FAX_PHASE_B) { + idi_send_edata(card, chan); + + cmd.driver = card->myid; + cmd.command = ISDN_STAT_FAXIND; + cmd.arg = chan->No; + chan->fax->r_code = ISDN_TTY_FAX_CFR; + card->interface.statcallb(&cmd); + + cmd.driver = card->myid; + cmd.command = ISDN_STAT_FAXIND; + cmd.arg = chan->No; + chan->fax->r_code = ISDN_TTY_FAX_RID; + card->interface.statcallb(&cmd); - /* TODO , code follows */ + /* telling 1-D compression */ + chan->fax->r_compression = 0; + cmd.driver = card->myid; + cmd.command = ISDN_STAT_FAXIND; + cmd.arg = chan->No; + chan->fax->r_code = ISDN_TTY_FAX_DCS; + card->interface.statcallb(&cmd); + + chan->fax2.NextObject = FAX_OBJECT_DOCU; + chan->fax2.PrevObject = FAX_OBJECT_DOCU; + + break; + } + if (chan->fax->phase == ISDN_FAX_PHASE_D) { + idi_send_edata(card, chan); + break; + } + break; + case ISDN_TTY_FAX_ET: + switch(chan->fax->fet) { + case 0: + case 1: + idi_fax_send_header(card, chan, 0); + break; + case 2: + idi_fax_send_header(card, chan, 1); + break; + } + break; + } } void idi_edata_rcveop(eicon_card *card, eicon_chan *chan) { + isdn_ctrl cmd; - /* TODO , code follows */ - + if (!chan->fax) { + eicon_log(card, 1, "idi_edata: rcveop with NULL fax struct, ERROR\n"); + return; + } + cmd.driver = card->myid; + cmd.command = ISDN_STAT_FAXIND; + cmd.arg = chan->No; + chan->fax->r_code = ISDN_TTY_FAX_ET; + card->interface.statcallb(&cmd); } void idi_reset_fax_stat(eicon_chan *chan) { - - /* TODO , code follows */ - + chan->fax2.LineLen = 0; + chan->fax2.LineData = 0; + chan->fax2.LineDataLen = 0; + chan->fax2.NullByteExist = 0; + chan->fax2.Dle = 0; + chan->fax2.PageCount = 0; + chan->fax2.Eop = 0; } void idi_edata_action(eicon_card *ccard, eicon_chan *chan, char *buffer, int len) { + isdn_ctrl cmd; + + if (!chan->fax) { + eicon_log(ccard, 1, "idi_edata: action with NULL fax struct, ERROR\n"); + return; + } + if (chan->fax->direction == ISDN_TTY_FAX_CONN_OUT) { + idi_parse_edata(ccard, chan, buffer, len); + + if (chan->fax->phase == ISDN_FAX_PHASE_A) { + idi_reset_fax_stat(chan); + + chan->fsm_state = EICON_STATE_ACTIVE; + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_BCONN; + cmd.arg = chan->No; + strcpy(cmd.parm.num, ""); + ccard->interface.statcallb(&cmd); + + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_FAXIND; + cmd.arg = chan->No; + chan->fax->r_code = ISDN_TTY_FAX_FCON; + ccard->interface.statcallb(&cmd); - /* TODO , code follows */ + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_FAXIND; + cmd.arg = chan->No; + chan->fax->r_code = ISDN_TTY_FAX_RID; + ccard->interface.statcallb(&cmd); + + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_FAXIND; + cmd.arg = chan->No; + chan->fax->r_code = ISDN_TTY_FAX_DIS; + ccard->interface.statcallb(&cmd); + if (chan->fax->r_compression != 0) { + /* telling fake compression in second DIS message */ + chan->fax->r_compression = 0; + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_FAXIND; + cmd.arg = chan->No; + chan->fax->r_code = ISDN_TTY_FAX_DIS; + ccard->interface.statcallb(&cmd); + } + + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_FAXIND; + cmd.arg = chan->No; + chan->fax->r_code = ISDN_TTY_FAX_SENT; /* OK message */ + ccard->interface.statcallb(&cmd); + } else + if (chan->fax->phase == ISDN_FAX_PHASE_D) { + + if ((chan->fax->code == EDATA_T30_MCF) && + (chan->fax->fet != 2)) { + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_FAXIND; + cmd.arg = chan->No; + chan->fax->r_code = ISDN_TTY_FAX_PTS; + ccard->interface.statcallb(&cmd); + } + + switch(chan->fax->fet) { + case 0: /* new page */ + /* stay in phase D , wait on cmd +FDT */ + break; + case 1: /* new document */ + /* link-level switch to phase B */ + break; + case 2: /* session end */ + default: + /* send_edata produces error on some */ + /* fax-machines here, so we don't */ + /* idi_send_edata(ccard, chan); */ + break; + } + } + } + + if (chan->fax->direction == ISDN_TTY_FAX_CONN_IN) { + idi_parse_edata(ccard, chan, buffer, len); + + if ((chan->fax->code == EDATA_T30_DCS) && + (chan->fax->phase == ISDN_FAX_PHASE_A)) { + idi_reset_fax_stat(chan); + + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_BCONN; + cmd.arg = chan->No; + strcpy(cmd.parm.num, ""); + ccard->interface.statcallb(&cmd); + + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_FAXIND; + cmd.arg = chan->No; + chan->fax->r_code = ISDN_TTY_FAX_FCON_I; + ccard->interface.statcallb(&cmd); + } else + if ((chan->fax->code == EDATA_T30_TRAIN_OK) && + (chan->fax->phase == ISDN_FAX_PHASE_A)) { + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_FAXIND; + cmd.arg = chan->No; + chan->fax->r_code = ISDN_TTY_FAX_RID; + ccard->interface.statcallb(&cmd); + + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_FAXIND; + cmd.arg = chan->No; + chan->fax->r_code = ISDN_TTY_FAX_TRAIN_OK; + ccard->interface.statcallb(&cmd); + } else + if ((chan->fax->code == EDATA_T30_TRAIN_OK) && + (chan->fax->phase == ISDN_FAX_PHASE_B)) { + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_FAXIND; + cmd.arg = chan->No; + chan->fax->r_code = ISDN_TTY_FAX_TRAIN_OK; + ccard->interface.statcallb(&cmd); + } else + if (chan->fax->phase == ISDN_FAX_PHASE_C) { + switch(chan->fax->code) { + case EDATA_T30_TRAIN_OK: + idi_send_edata(ccard, chan); + break; + case EDATA_T30_MPS: + chan->fax->fet = 0; + idi_edata_rcveop(ccard, chan); + break; + case EDATA_T30_EOM: + chan->fax->fet = 1; + idi_edata_rcveop(ccard, chan); + break; + case EDATA_T30_EOP: + chan->fax->fet = 2; + idi_edata_rcveop(ccard, chan); + break; + } + } + } } void fax_put_rcv(eicon_card *ccard, eicon_chan *chan, u_char *Data, int len) { - - /* TODO , code follows */ - + struct sk_buff *skb; + + skb = alloc_skb(len + MAX_HEADER_LEN, GFP_ATOMIC); + if (!skb) { + eicon_log(ccard, 1, "idi_err: Ch%d: alloc_skb failed in fax_put_rcv()\n", chan->No); + return; + } + skb_reserve(skb, MAX_HEADER_LEN); + memcpy(skb_put(skb, len), Data, len); + ccard->interface.rcvcallb_skb(ccard->myid, chan->No, skb); } void idi_faxdata_rcv(eicon_card *ccard, eicon_chan *chan, struct sk_buff *skb) { + eicon_OBJBUFFER InBuf; + eicon_OBJBUFFER LineBuf; + unsigned int Length = 0; + unsigned int aLength = 0; + unsigned int ObjectSize = 0; + unsigned int ObjHeadLen = 0; + unsigned int ObjDataLen = 0; + __u8 Recordtype; + __u8 PageHeaderLen; + __u8 Event; + eicon_sff_pagehead *ob_page; + + __u16 Cl2Eol = 0x8000; + +# define EVENT_NONE 0 +# define EVENT_NEEDDATA 1 + + if (!chan->fax) { + eicon_log(ccard, 1, "idi_fax: rcvdata with NULL fax struct, ERROR\n"); + return; + } + + + + if (chan->fax->direction == ISDN_TTY_FAX_CONN_IN) { + InBuf.Data = skb->data; + InBuf.Size = skb->len; + InBuf.Len = 0; + InBuf.Next = InBuf.Data; + LineBuf.Data = chan->fax2.abLine; + LineBuf.Size = sizeof(chan->fax2.abLine); + LineBuf.Len = chan->fax2.LineLen; + LineBuf.Next = LineBuf.Data + LineBuf.Len; + + Event = EVENT_NONE; + while (Event == EVENT_NONE) { + switch(chan->fax2.NextObject) { + case FAX_OBJECT_DOCU: + Length = LineBuf.Len + (InBuf.Size - InBuf.Len); + if (Length < sizeof(eicon_sff_dochead)) { + Event = EVENT_NEEDDATA; + break; + } + ObjectSize = sizeof(eicon_sff_dochead); + Length = ObjectSize; + if (LineBuf.Len < Length) { + Length -= LineBuf.Len; + LineBuf.Len = 0; + LineBuf.Next = LineBuf.Data; + InBuf.Len += Length; + InBuf.Next += Length; + } else { + LineBuf.Len -= Length; + LineBuf.Next = LineBuf.Data + LineBuf.Len; + memmove(LineBuf.Data, LineBuf.Data + Length, LineBuf.Len); + } + chan->fax2.PrevObject = FAX_OBJECT_DOCU; + chan->fax2.NextObject = FAX_OBJECT_PAGE; + break; + + case FAX_OBJECT_PAGE: + Length = LineBuf.Len + (InBuf.Size - InBuf.Len); + if (Length < 2) { + Event = EVENT_NEEDDATA; + break; + } + if (LineBuf.Len == 0) { + *LineBuf.Next++ = *InBuf.Next++; + LineBuf.Len++; + InBuf.Len++; + } + if (LineBuf.Len == 1) { + *LineBuf.Next++ = *InBuf.Next++; + LineBuf.Len++; + InBuf.Len++; + } + PageHeaderLen = *(LineBuf.Data + 1); + ObjectSize = (PageHeaderLen == 0) ? 2 : sizeof(eicon_sff_pagehead); + if (Length < ObjectSize) { + Event = EVENT_NEEDDATA; + break; + } + Length = ObjectSize; + /* extract page dimensions */ + if (LineBuf.Len < Length) { + aLength = Length - LineBuf.Len; + memcpy(LineBuf.Next, InBuf.Next, aLength); + LineBuf.Next += aLength; + InBuf.Next += aLength; + LineBuf.Len += aLength; + InBuf.Len += aLength; + } + if (Length > 2) { + ob_page = (eicon_sff_pagehead *)LineBuf.Data; + switch(ob_page->linelength) { + case 2048: + chan->fax->r_width = 1; + break; + case 2432: + chan->fax->r_width = 2; + break; + case 1216: + chan->fax->r_width = 3; + break; + case 864: + chan->fax->r_width = 4; + break; + case 1728: + default: + chan->fax->r_width = 0; + } + switch(ob_page->pagelength) { + case 1143: + case 2287: + chan->fax->r_length = 0; + break; + case 1401: + case 2802: + chan->fax->r_length = 1; + break; + default: + chan->fax->r_length = 2; + } + eicon_log(ccard, 128, "rSFF-Head: linelength = %d\n", ob_page->linelength); + eicon_log(ccard, 128, "rSFF-Head: pagelength = %d\n", ob_page->pagelength); + } + LineBuf.Len -= Length; + LineBuf.Next = LineBuf.Data + LineBuf.Len; + memmove(LineBuf.Data, LineBuf.Data + Length, LineBuf.Len); + + chan->fax2.PrevObject = FAX_OBJECT_PAGE; + chan->fax2.NextObject = FAX_OBJECT_LINE; + break; + + case FAX_OBJECT_LINE: + Length = LineBuf.Len + (InBuf.Size - InBuf.Len); + if (Length < 1) { + Event = EVENT_NEEDDATA; + break; + } + if (LineBuf.Len == 0) { + *LineBuf.Next++ = *InBuf.Next++; + LineBuf.Len++; + InBuf.Len++; + } + Recordtype = *LineBuf.Data; + if (Recordtype == 0) { + /* recordtype pixel row (2 byte length) */ + ObjHeadLen = 3; + if (Length < ObjHeadLen) { + Event = EVENT_NEEDDATA; + break; + } + while (LineBuf.Len < ObjHeadLen) { + *LineBuf.Next++ = *InBuf.Next++; + LineBuf.Len++; + InBuf.Len++; + } + ObjDataLen = *((__u16*) (LineBuf.Data + 1)); + ObjectSize = ObjHeadLen + ObjDataLen; + if (Length < ObjectSize) { + Event = EVENT_NEEDDATA; + break; + } + } else + if ((Recordtype >= 1) && (Recordtype <= 216)) { + /* recordtype pixel row (1 byte length) */ + ObjHeadLen = 1; + ObjDataLen = Recordtype; + ObjectSize = ObjHeadLen + ObjDataLen; + if (Length < ObjectSize) { + Event = EVENT_NEEDDATA; + break; + } + } else + if ((Recordtype >= 217) && (Recordtype <= 253)) { + /* recordtype empty lines */ + ObjHeadLen = 1; + ObjDataLen = 0; + ObjectSize = ObjHeadLen + ObjDataLen; + LineBuf.Len--; + LineBuf.Next = LineBuf.Data + LineBuf.Len; + memmove(LineBuf.Data, LineBuf.Data + 1, LineBuf.Len); + break; + } else + if (Recordtype == 254) { + /* recordtype page header */ + chan->fax2.PrevObject = FAX_OBJECT_LINE; + chan->fax2.NextObject = FAX_OBJECT_PAGE; + break; + } else { + /* recordtype user information */ + ObjHeadLen = 2; + if (Length < ObjHeadLen) { + Event = EVENT_NEEDDATA; + break; + } + while (LineBuf.Len < ObjHeadLen) { + *LineBuf.Next++ = *InBuf.Next++; + LineBuf.Len++; + InBuf.Len++; + } + ObjDataLen = *(LineBuf.Data + 1); + ObjectSize = ObjHeadLen + ObjDataLen; + if (ObjDataLen == 0) { + /* illegal line coding */ + LineBuf.Len -= ObjHeadLen; + LineBuf.Next = LineBuf.Data + LineBuf.Len; + memmove(LineBuf.Data, LineBuf.Data + ObjHeadLen, LineBuf.Len); + break; + } else { + /* user information */ + if (Length < ObjectSize) { + Event = EVENT_NEEDDATA; + break; + } + Length = ObjectSize; + if (LineBuf.Len < Length) { + Length -= LineBuf.Len; + LineBuf.Len = 0; + LineBuf.Next = LineBuf.Data; + InBuf.Len += Length; + InBuf.Next += Length; + } else { + LineBuf.Len -= Length; + LineBuf.Next = LineBuf.Data + LineBuf.Len; + memmove(LineBuf.Data, LineBuf.Data + Length, LineBuf.Len); + } + } + break; + } + Length = ObjectSize; + if (LineBuf.Len > ObjHeadLen) { + fax_put_rcv(ccard, chan, LineBuf.Data + ObjHeadLen, + (LineBuf.Len - ObjHeadLen)); + } + Length -= LineBuf.Len; + LineBuf.Len = 0; + LineBuf.Next = LineBuf.Data; + if (Length > 0) { + fax_put_rcv(ccard, chan, InBuf.Next, Length); + InBuf.Len += Length; + InBuf.Next += Length; + } + fax_put_rcv(ccard, chan, (__u8 *)&Cl2Eol, sizeof(Cl2Eol)); + break; + } /* end of switch (chan->fax2.NextObject) */ + } /* end of while (Event==EVENT_NONE) */ + if (InBuf.Len < InBuf.Size) { + Length = InBuf.Size - InBuf.Len; + if ((LineBuf.Len + Length) > LineBuf.Size) { + eicon_log(ccard, 1, "idi_fax: Ch%d: %d bytes dropping, small buffer\n", chan->No, + Length); + } else { + memcpy(LineBuf.Next, InBuf.Next, Length); + LineBuf.Len += Length; + } + } + chan->fax2.LineLen = LineBuf.Len; + } else { /* CONN_OUT */ + /* On CONN_OUT we do not need incoming data, drop it */ + /* maybe later for polling */ + } - /* TODO , code follows */ +# undef EVENT_NONE +# undef EVENT_NEEDDATA + return; } int idi_fax_send_outbuf(eicon_card *ccard, eicon_chan *chan, eicon_OBJBUFFER *OutBuf) { + struct sk_buff *skb; - /* TODO , code follows */ + skb = alloc_skb(OutBuf->Len, GFP_ATOMIC); + if (!skb) { + eicon_log(ccard, 1, "idi_err: Ch%d: alloc_skb failed in fax_send_outbuf()\n", chan->No); + return(-1); + } + memcpy(skb_put(skb, OutBuf->Len), OutBuf->Data, OutBuf->Len); - return(0); + OutBuf->Len = 0; + OutBuf->Next = OutBuf->Data; + + return(idi_send_data(ccard, chan, 0, skb, 1)); } int idi_faxdata_send(eicon_card *ccard, eicon_chan *chan, struct sk_buff *skb) { + isdn_ctrl cmd; + eicon_OBJBUFFER InBuf; + __u8 InData; + __u8 InMask; + eicon_OBJBUFFER OutBuf; + eicon_OBJBUFFER LineBuf; + __u32 LineData; + unsigned int LineDataLen; + __u8 Byte; + __u8 Event; + int ret = 1; + +# define EVENT_NONE 0 +# define EVENT_EOD 1 +# define EVENT_EOL 2 +# define EVENT_EOP 3 - /* TODO , code follows */ + if ((!ccard) || (!chan)) + return -1; - return(0); + if (!chan->fax) { + eicon_log(ccard, 1, "idi_fax: senddata with NULL fax struct, ERROR\n"); + return -1; + } + + if (chan->fax->direction == ISDN_TTY_FAX_CONN_IN) { + /* Simply ignore any data written in data mode when receiving a fax. */ + /* This is not completely correct because only XON's should come here. */ + dev_kfree_skb(skb); + return 1; + } + + if (chan->fax->phase != ISDN_FAX_PHASE_C) { + dev_kfree_skb(skb); + return 1; + } + + if (chan->queued + skb->len > 1200) + return 0; + + InBuf.Data = skb->data; + InBuf.Size = skb->len; + InBuf.Len = 0; + InBuf.Next = InBuf.Data; + InData = 0; + InMask = 0; + + LineBuf.Data = chan->fax2.abLine; + LineBuf.Size = sizeof(chan->fax2.abLine); + LineBuf.Len = chan->fax2.LineLen; + LineBuf.Next = LineBuf.Data + LineBuf.Len; + LineData = chan->fax2.LineData; + LineDataLen = chan->fax2.LineDataLen; + + OutBuf.Data = chan->fax2.abFrame; + OutBuf.Size = sizeof(chan->fax2.abFrame); + OutBuf.Len = 0; + OutBuf.Next = OutBuf.Data; + + Event = EVENT_NONE; + + chan->fax2.Eop = 0; + + for (;;) { + for (;;) { + if (InMask == 0) { + if (InBuf.Len >= InBuf.Size) { + Event = EVENT_EOD; + break; + } + if ((chan->fax2.Dle != _DLE_) && *InBuf.Next == _DLE_) { + chan->fax2.Dle = _DLE_; + InBuf.Next++; + InBuf.Len++; + if (InBuf.Len >= InBuf.Size) { + Event = EVENT_EOD; + break; + } + } + if (chan->fax2.Dle == _DLE_) { + chan->fax2.Dle = 0; + if (*InBuf.Next == _ETX_) { + Event = EVENT_EOP; + break; + } else + if (*InBuf.Next == _DLE_) { + /* do nothing */ + } else { + eicon_log(ccard, 1, + "idi_err: Ch%d: unknown DLE escape %02x found\n", + chan->No, *InBuf.Next); + InBuf.Next++; + InBuf.Len++; + if (InBuf.Len >= InBuf.Size) { + Event = EVENT_EOD; + break; + } + } + } + InBuf.Len++; + InData = *InBuf.Next++; + InMask = (chan->fax->bor) ? 0x80 : 0x01; + } + while (InMask) { + LineData >>= 1; + LineDataLen++; + if (InData & InMask) + LineData |= 0x80000000; + if (chan->fax->bor) + InMask >>= 1; + else + InMask <<= 1; + + if ((LineDataLen >= T4_EOL_BITSIZE) && + ((LineData & T4_EOL_MASK_DWORD) == T4_EOL_DWORD)) { + Event = EVENT_EOL; + if (LineDataLen > T4_EOL_BITSIZE) { + Byte = (__u8) + ((LineData & ~T4_EOL_MASK_DWORD) >> + (32 - LineDataLen)); + if (Byte == 0) { + if (! chan->fax2.NullByteExist) { + chan->fax2.NullBytesPos = LineBuf.Len; + chan->fax2.NullByteExist = 1; + } + } else { + chan->fax2.NullByteExist = 0; + } + if (LineBuf.Len < LineBuf.Size) { + *LineBuf.Next++ = Byte; + LineBuf.Len++; + } + } + LineDataLen = 0; + break; + } + if (LineDataLen >= T4_EOL_BITSIZE + 8) { + Byte = (__u8) + ((LineData & ~T4_EOL_MASK_DWORD) >> + (32 - T4_EOL_BITSIZE - 8)); + LineData &= T4_EOL_MASK_DWORD; + LineDataLen = T4_EOL_BITSIZE; + if (Byte == 0) { + if (! chan->fax2.NullByteExist) { + chan->fax2.NullBytesPos = LineBuf.Len; + chan->fax2.NullByteExist = 1; + } + } else { + chan->fax2.NullByteExist = 0; + } + if (LineBuf.Len < LineBuf.Size) { + *LineBuf.Next++ = Byte; + LineBuf.Len++; + } + } + } + if (Event != EVENT_NONE) + break; + } + + if ((Event != EVENT_EOL) && (Event != EVENT_EOP)) + break; + + if ((Event == EVENT_EOP) && (LineDataLen > 0)) { + LineData >>= 32 - LineDataLen; + LineDataLen = 0; + while (LineData != 0) { + Byte = (__u8) LineData; + LineData >>= 8; + if (Byte == 0) { + if (! chan->fax2.NullByteExist) { + chan->fax2.NullBytesPos = LineBuf.Len; + chan->fax2.NullByteExist = 1; + } + } else { + chan->fax2.NullByteExist = 0; + } + if (LineBuf.Len < LineBuf.Size) { + *LineBuf.Next++ = Byte; + LineBuf.Len++; + } + + } + } + if (chan->fax2.NullByteExist) { + if (chan->fax2.NullBytesPos == 0) { + LineBuf.Len = 0; + } else { + LineBuf.Len = chan->fax2.NullBytesPos + 1; + } + } + if (LineBuf.Len > 0) { + if (OutBuf.Len + LineBuf.Len + SFF_LEN_FLD_SIZE > OutBuf.Size) { + ret = idi_fax_send_outbuf(ccard, chan, &OutBuf); + } + if (LineBuf.Len <= 216) { + *OutBuf.Next++ = (__u8) LineBuf.Len; + OutBuf.Len++; + } else { + *OutBuf.Next++ = 0; + *((__u16 *) OutBuf.Next)++ = (__u16) LineBuf.Len; + OutBuf.Len += 3; + } + memcpy(OutBuf.Next, LineBuf.Data, LineBuf.Len); + OutBuf.Next += LineBuf.Len; + OutBuf.Len += LineBuf.Len; + } + LineBuf.Len = 0; + LineBuf.Next = LineBuf.Data; + chan->fax2.NullByteExist = 0; + if (Event == EVENT_EOP) + break; + + Event = EVENT_NONE; + } + + if (Event == EVENT_EOP) { + chan->fax2.Eop = 1; + chan->fax2.PageCount++; + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_FAXIND; + cmd.arg = chan->No; + chan->fax->r_code = ISDN_TTY_FAX_EOP; + ccard->interface.statcallb(&cmd); + } + if (OutBuf.Len > 0) { + ret = idi_fax_send_outbuf(ccard, chan, &OutBuf); + } + + chan->fax2.LineLen = LineBuf.Len; + chan->fax2.LineData = LineData; + chan->fax2.LineDataLen = LineDataLen; + +# undef EVENT_NONE +# undef EVENT_EOD +# undef EVENT_EOL +# undef EVENT_EOP + + if (ret >= 0) + dev_kfree_skb(skb); + if (ret == 0) + ret = 1; + return(ret); } void idi_fax_hangup(eicon_card *ccard, eicon_chan *chan) { + isdn_ctrl cmd; - /* TODO , code follows */ - + if (!chan->fax) { + eicon_log(ccard, 1, "idi_fax: hangup with NULL fax struct, ERROR\n"); + return; + } + if ((chan->fax->direction == ISDN_TTY_FAX_CONN_OUT) && + (chan->fax->code == 0)) { + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_FAXIND; + cmd.arg = chan->No; + chan->fax->r_code = ISDN_TTY_FAX_PTS; + ccard->interface.statcallb(&cmd); + } + if ((chan->fax->code > 1) && (chan->fax->code < 120)) + chan->fax->code += 120; + chan->fax->r_code = ISDN_TTY_FAX_HNG; + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_FAXIND; + cmd.arg = chan->No; + ccard->interface.statcallb(&cmd); } #endif /******** FAX ********/ @@ -1084,20 +2174,17 @@ eicon_chan_ptr *chan2; if ((chan->fsm_state == EICON_STATE_NULL) || (chan->fsm_state == EICON_STATE_LISTEN)) { - if (DebugVar & 1) - printk(KERN_DEBUG"idi_snd: Ch%d: send udata on state %d !\n", chan->No, chan->fsm_state); + eicon_log(card, 1, "idi_snd: Ch%d: send udata on state %d !\n", chan->No, chan->fsm_state); return -ENODEV; } - if (DebugVar & 8) - printk(KERN_DEBUG"idi_snd: Ch%d: udata 0x%x: %d %d %d %d\n", chan->No, - UReq, buffer[0], buffer[1], buffer[2], buffer[3]); + eicon_log(card, 8, "idi_snd: Ch%d: udata 0x%x: %d %d %d %d\n", chan->No, + UReq, buffer[0], buffer[1], buffer[2], buffer[3]); skb = alloc_skb(sizeof(eicon_REQ) + len + 1, GFP_ATOMIC); skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC); if ((!skb) || (!skb2)) { - if (DebugVar & 1) - printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed in send_udata()\n", chan->No); + eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in send_udata()\n", chan->No); if (skb) dev_kfree_skb(skb); if (skb2) @@ -1131,6 +2218,9 @@ u_char buf[6]; struct enable_dtmf_s *dtmf_buf = (struct enable_dtmf_s *)buf; + if ((!ccard) || (!chan)) + return; + memset(buf, 0, 6); switch(cmd) { case ISDN_AUDIO_SETDD: @@ -1162,14 +2252,15 @@ '1','4','7','*','2','5','8','0','3','6','9','#','A','B','C','D' }; + if ((!ccard) || (!chan)) + return; + switch (buffer[0]) { case DSP_UDATA_INDICATION_SYNC: - if (DebugVar & 16) - printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_SYNC time %d\n", chan->No, p->time); + eicon_log(ccard, 16, "idi_ind: Ch%d: UDATA_SYNC time %d\n", chan->No, p->time); break; case DSP_UDATA_INDICATION_DCD_OFF: - if (DebugVar & 8) - printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_DCD_OFF time %d\n", chan->No, p->time); + eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_DCD_OFF time %d\n", chan->No, p->time); break; case DSP_UDATA_INDICATION_DCD_ON: if ((chan->l2prot == ISDN_PROTO_L2_MODEM) && @@ -1181,31 +2272,24 @@ sprintf(cmd.parm.num, "%d/%s", p->speed, connmsg[p->norm]); ccard->interface.statcallb(&cmd); } - if (DebugVar & 8) { - printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_DCD_ON time %d\n", chan->No, p->time); - printk(KERN_DEBUG"idi_ind: Ch%d: %d %d %d %d\n", chan->No, - p->norm, p->options, p->speed, p->delay); - } + eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_DCD_ON time %d\n", chan->No, p->time); + eicon_log(ccard, 8, "idi_ind: Ch%d: %d %d %d %d\n", chan->No, + p->norm, p->options, p->speed, p->delay); break; case DSP_UDATA_INDICATION_CTS_OFF: - if (DebugVar & 8) - printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_CTS_OFF time %d\n", chan->No, p->time); + eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_CTS_OFF time %d\n", chan->No, p->time); break; case DSP_UDATA_INDICATION_CTS_ON: - if (DebugVar & 8) { - printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_CTS_ON time %d\n", chan->No, p->time); - printk(KERN_DEBUG"idi_ind: Ch%d: %d %d %d %d\n", chan->No, - p->norm, p->options, p->speed, p->delay); - } + eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_CTS_ON time %d\n", chan->No, p->time); + eicon_log(ccard, 8, "idi_ind: Ch%d: %d %d %d %d\n", chan->No, + p->norm, p->options, p->speed, p->delay); break; case DSP_UDATA_INDICATION_DISCONNECT: - if (DebugVar & 8) - printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_DISCONNECT cause %d\n", chan->No, buffer[1]); + eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_DISCONNECT cause %d\n", chan->No, buffer[1]); break; case DSP_UDATA_INDICATION_DTMF_DIGITS_RECEIVED: - if (DebugVar & 8) - printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_DTMF_REC '%c'\n", chan->No, - dtmf_code[buffer[1]]); + eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_DTMF_REC '%c'\n", chan->No, + dtmf_code[buffer[1]]); cmd.driver = ccard->myid; cmd.command = ISDN_STAT_AUDIO; cmd.parm.num[0] = ISDN_AUDIO_DTMF; @@ -1214,8 +2298,7 @@ ccard->interface.statcallb(&cmd); break; default: - if (DebugVar & 8) - printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED UDATA Indication 0x%02x\n", chan->No, buffer[0]); + eicon_log(ccard, 8, "idi_ind: Ch%d: UNHANDLED UDATA Indication 0x%02x\n", chan->No, buffer[0]); } } @@ -1223,23 +2306,35 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) { int tmp; + char tnum[64]; + int dlev; int free_buff; + ulong flags; struct sk_buff *skb2; eicon_IND *ind = (eicon_IND *)skb->data; eicon_chan *chan; idi_ind_message message; isdn_ctrl cmd; + if (!ccard) { + eicon_log(ccard, 1, "idi_err: Ch??: null card in handle_ind\n"); + dev_kfree_skb(skb); + return; + } + if ((chan = ccard->IdTable[ind->IndId]) == NULL) { + eicon_log(ccard, 1, "idi_err: Ch??: null chan in handle_ind\n"); dev_kfree_skb(skb); return; } - if ((DebugVar & 128) || - ((DebugVar & 16) && (ind->Ind != 8))) { - printk(KERN_DEBUG "idi_hdl: Ch%d: Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n", chan->No, - ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length); - } + if (ind->Ind != 8) + dlev = 144; + else + dlev = 128; + + eicon_log(ccard, dlev, "idi_hdl: Ch%d: Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n", chan->No, + ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length); free_buff = 1; /* Signal Layer */ @@ -1247,15 +2342,16 @@ idi_IndParse(ccard, chan, &message, ind->RBuffer.P, ind->RBuffer.length); switch(ind->Ind) { case HANGUP: - if (DebugVar & 8) - printk(KERN_DEBUG"idi_ind: Ch%d: Hangup\n", chan->No); + eicon_log(ccard, 8, "idi_ind: Ch%d: Hangup\n", chan->No); while((skb2 = skb_dequeue(&chan->e.X))) { dev_kfree_skb(skb2); } - chan->e.busy = 0; + save_flags(flags); + cli(); chan->queued = 0; chan->waitq = 0; chan->waitpq = 0; + restore_flags(flags); if (message.e_cau[0] & 0x7f) { cmd.driver = ccard->myid; cmd.arg = chan->No; @@ -1269,66 +2365,92 @@ if (!chan->e.B2Id) chan->fax = 0; #endif - if ((chan->fsm_state == EICON_STATE_ACTIVE) || - (chan->fsm_state == EICON_STATE_WMCONN)) { + if (((chan->fsm_state == EICON_STATE_ACTIVE) || + (chan->fsm_state == EICON_STATE_WMCONN)) || + ((chan->l2prot == ISDN_PROTO_L2_FAX) && + (chan->fsm_state == EICON_STATE_OBWAIT))) { chan->fsm_state = EICON_STATE_NULL; } else { if (chan->e.B2Id) idi_do_req(ccard, chan, REMOVE, 1); - chan->fsm_state = EICON_STATE_NULL; - cmd.driver = ccard->myid; - cmd.arg = chan->No; - cmd.command = ISDN_STAT_DHUP; - ccard->interface.statcallb(&cmd); - eicon_idi_listen_req(ccard, chan); + chan->statectrl &= ~WAITING_FOR_HANGUP; + if (chan->statectrl & HAVE_CONN_REQ) { + eicon_log(ccard, 32, "idi_req: Ch%d: queueing delayed conn_req\n", chan->No); + chan->statectrl &= ~HAVE_CONN_REQ; + if ((chan->tskb1) && (chan->tskb2)) { + skb_queue_tail(&chan->e.X, chan->tskb1); + skb_queue_tail(&ccard->sndq, chan->tskb2); + eicon_schedule_tx(ccard); + } + chan->tskb1 = NULL; + chan->tskb2 = NULL; + } else { + chan->fsm_state = EICON_STATE_NULL; + cmd.driver = ccard->myid; + cmd.arg = chan->No; + cmd.command = ISDN_STAT_DHUP; + ccard->interface.statcallb(&cmd); + eicon_idi_listen_req(ccard, chan); + } } break; case INDICATE_IND: - if (DebugVar & 8) - printk(KERN_DEBUG"idi_ind: Ch%d: Indicate_Ind\n", chan->No); + eicon_log(ccard, 8, "idi_ind: Ch%d: Indicate_Ind\n", chan->No); + if (chan->fsm_state != EICON_STATE_LISTEN) { + eicon_log(ccard, 1, "idi_err: Ch%d: Incoming call on wrong state (%d).\n", + chan->No, chan->fsm_state); + idi_do_req(ccard, chan, HANGUP, 0); + break; + } chan->fsm_state = EICON_STATE_ICALL; idi_bc2si(message.bc, message.hlc, &chan->si1, &chan->si2); strcpy(chan->cpn, message.cpn + 1); - if (strlen(message.dsa)) { - strcat(chan->cpn, "."); - strcat(chan->cpn, message.dsa); - } strcpy(chan->oad, message.oad); - if (strlen(message.osa)) { - strcat(chan->oad, "."); - strcat(chan->oad, message.osa); - } + strcpy(chan->dsa, message.dsa); + strcpy(chan->osa, message.osa); + chan->plan = message.plan; + chan->screen = message.screen; try_stat_icall_again: cmd.driver = ccard->myid; cmd.command = ISDN_STAT_ICALL; cmd.arg = chan->No; cmd.parm.setup.si1 = chan->si1; cmd.parm.setup.si2 = chan->si2; - strcpy(cmd.parm.setup.eazmsn, chan->cpn); - strcpy(cmd.parm.setup.phone, chan->oad); - cmd.parm.setup.plan = message.plan; - cmd.parm.setup.screen = message.screen; + strcpy(tnum, chan->cpn); + if (strlen(chan->dsa)) { + strcat(tnum, "."); + strcat(tnum, chan->dsa); + } + tnum[ISDN_MSNLEN - 1] = 0; + strcpy(cmd.parm.setup.eazmsn, tnum); + strcpy(tnum, chan->oad); + if (strlen(chan->osa)) { + strcat(tnum, "."); + strcat(tnum, chan->osa); + } + tnum[ISDN_MSNLEN - 1] = 0; + strcpy(cmd.parm.setup.phone, tnum); + cmd.parm.setup.plan = chan->plan; + cmd.parm.setup.screen = chan->screen; tmp = ccard->interface.statcallb(&cmd); switch(tmp) { case 0: /* no user responding */ idi_do_req(ccard, chan, HANGUP, 0); + chan->fsm_state = EICON_STATE_NULL; break; case 1: /* alert */ - if (DebugVar & 8) - printk(KERN_DEBUG"idi_req: Ch%d: Call Alert\n", chan->No); + eicon_log(ccard, 8, "idi_req: Ch%d: Call Alert\n", chan->No); if ((chan->fsm_state == EICON_STATE_ICALL) || (chan->fsm_state == EICON_STATE_ICALLW)) { chan->fsm_state = EICON_STATE_ICALL; idi_do_req(ccard, chan, CALL_ALERT, 0); } break; case 2: /* reject */ - if (DebugVar & 8) - printk(KERN_DEBUG"idi_req: Ch%d: Call Reject\n", chan->No); + eicon_log(ccard, 8, "idi_req: Ch%d: Call Reject\n", chan->No); idi_do_req(ccard, chan, REJECT, 0); break; case 3: /* incomplete number */ - if (DebugVar & 8) - printk(KERN_DEBUG"idi_req: Ch%d: Incomplete Number\n", chan->No); + eicon_log(ccard, 8, "idi_req: Ch%d: Incomplete Number\n", chan->No); switch(ccard->type) { case EICON_CTYPE_MAESTRAP: case EICON_CTYPE_S2M: @@ -1342,8 +2464,7 @@ } break; case INFO_IND: - if (DebugVar & 8) - printk(KERN_DEBUG"idi_ind: Ch%d: Info_Ind\n", chan->No); + eicon_log(ccard, 8, "idi_ind: Ch%d: Info_Ind\n", chan->No); if ((chan->fsm_state == EICON_STATE_ICALLW) && (message.cpn[0])) { strcat(chan->cpn, message.cpn + 1); @@ -1351,8 +2472,7 @@ } break; case CALL_IND: - if (DebugVar & 8) - printk(KERN_DEBUG"idi_ind: Ch%d: Call_Ind\n", chan->No); + eicon_log(ccard, 8, "idi_ind: Ch%d: Call_Ind\n", chan->No); if ((chan->fsm_state == EICON_STATE_ICALL) || (chan->fsm_state == EICON_STATE_IWAIT)) { chan->fsm_state = EICON_STATE_IBWAIT; cmd.driver = ccard->myid; @@ -1369,15 +2489,18 @@ case ISDN_PROTO_L2_MODEM: /* do nothing, wait for connect */ break; - default: + case ISDN_PROTO_L2_TRANS: idi_do_req(ccard, chan, IDI_N_CONNECT, 1); + break; + default: + /* On most incoming calls we use automatic connect */ + /* idi_do_req(ccard, chan, IDI_N_CONNECT, 1); */ } } else idi_hangup(ccard, chan); break; case CALL_CON: - if (DebugVar & 8) - printk(KERN_DEBUG"idi_ind: Ch%d: Call_Con\n", chan->No); + eicon_log(ccard, 8, "idi_ind: Ch%d: Call_Con\n", chan->No); if (chan->fsm_state == EICON_STATE_OCALL) { chan->fsm_state = EICON_STATE_OBWAIT; cmd.driver = ccard->myid; @@ -1387,9 +2510,8 @@ /* check if old NetID has been removed */ if (chan->e.B2Id) { - if (DebugVar & 1) - printk(KERN_WARNING "idi_err: Ch%d: Old NetID %x was not removed.\n", - chan->No, chan->e.B2Id); + eicon_log(ccard, 1, "eicon: Ch%d: old net_id %x still exist, removing.\n", + chan->No, chan->e.B2Id); idi_do_req(ccard, chan, REMOVE, 1); } @@ -1405,12 +2527,10 @@ idi_hangup(ccard, chan); break; case AOC_IND: - if (DebugVar & 8) - printk(KERN_DEBUG"idi_ind: Ch%d: Advice of Charge\n", chan->No); + eicon_log(ccard, 8, "idi_ind: Ch%d: Advice of Charge\n", chan->No); break; default: - if (DebugVar & 8) - printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED SigIndication 0x%02x\n", chan->No, ind->Ind); + eicon_log(ccard, 8, "idi_ind: Ch%d: UNHANDLED SigIndication 0x%02x\n", chan->No, ind->Ind); } } /* Network Layer */ @@ -1424,8 +2544,7 @@ else switch(ind->Ind) { case IDI_N_CONNECT_ACK: - if (DebugVar & 16) - printk(KERN_DEBUG"idi_ind: Ch%d: N_Connect_Ack\n", chan->No); + eicon_log(ccard, 16, "idi_ind: Ch%d: N_Connect_Ack\n", chan->No); if (chan->l2prot == ISDN_PROTO_L2_MODEM) { chan->fsm_state = EICON_STATE_WMCONN; break; @@ -1445,8 +2564,7 @@ } } else { - if (DebugVar & 1) - printk(KERN_DEBUG "idi_ind: N_CONNECT_ACK with NULL fax struct, ERROR\n"); + eicon_log(ccard, 1, "idi_ind: N_CONNECT_ACK with NULL fax struct, ERROR\n"); } #endif break; @@ -1455,11 +2573,11 @@ cmd.driver = ccard->myid; cmd.command = ISDN_STAT_BCONN; cmd.arg = chan->No; + strcpy(cmd.parm.num, "64000"); ccard->interface.statcallb(&cmd); break; case IDI_N_CONNECT: - if (DebugVar & 16) - printk(KERN_DEBUG"idi_ind: Ch%d: N_Connect\n", chan->No); + eicon_log(ccard, 16,"idi_ind: Ch%d: N_Connect\n", chan->No); if (chan->e.B2Id) idi_do_req(ccard, chan, IDI_N_CONNECT_ACK, 1); if (chan->l2prot == ISDN_PROTO_L2_FAX) { break; @@ -1472,12 +2590,15 @@ cmd.driver = ccard->myid; cmd.command = ISDN_STAT_BCONN; cmd.arg = chan->No; + strcpy(cmd.parm.num, "64000"); ccard->interface.statcallb(&cmd); break; case IDI_N_DISC: - if (DebugVar & 16) - printk(KERN_DEBUG"idi_ind: Ch%d: N_DISC\n", chan->No); + eicon_log(ccard, 16, "idi_ind: Ch%d: N_DISC\n", chan->No); if (chan->e.B2Id) { + while((skb2 = skb_dequeue(&chan->e.X))) { + dev_kfree_skb(skb2); + } idi_do_req(ccard, chan, IDI_N_DISC_ACK, 1); idi_do_req(ccard, chan, REMOVE, 1); } @@ -1487,9 +2608,12 @@ idi_fax_hangup(ccard, chan); } #endif + save_flags(flags); + cli(); chan->queued = 0; chan->waitq = 0; chan->waitpq = 0; + restore_flags(flags); idi_do_req(ccard, chan, HANGUP, 0); if (chan->fsm_state == EICON_STATE_ACTIVE) { cmd.driver = ccard->myid; @@ -1497,14 +2621,14 @@ cmd.arg = chan->No; ccard->interface.statcallb(&cmd); chan->fsm_state = EICON_STATE_NULL; + chan->statectrl |= WAITING_FOR_HANGUP; } #ifdef CONFIG_ISDN_TTY_FAX chan->fax = 0; #endif break; case IDI_N_DISC_ACK: - if (DebugVar & 16) - printk(KERN_DEBUG"idi_ind: Ch%d: N_DISC_ACK\n", chan->No); + eicon_log(ccard, 16, "idi_ind: Ch%d: N_DISC_ACK\n", chan->No); #ifdef CONFIG_ISDN_TTY_FAX if (chan->l2prot == ISDN_PROTO_L2_FAX) { idi_parse_edata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length); @@ -1513,13 +2637,11 @@ #endif break; case IDI_N_DATA_ACK: - if (DebugVar & 16) - printk(KERN_DEBUG"idi_ind: Ch%d: N_DATA_ACK\n", chan->No); + eicon_log(ccard, 16, "idi_ind: Ch%d: N_DATA_ACK\n", chan->No); break; case IDI_N_DATA: skb_pull(skb, sizeof(eicon_IND) - 1); - if (DebugVar & 128) - printk(KERN_DEBUG"idi_rcv: Ch%d: %d bytes\n", chan->No, skb->len); + eicon_log(ccard, 128, "idi_rcv: Ch%d: %d bytes\n", chan->No, skb->len); if (chan->l2prot == ISDN_PROTO_L2_FAX) { #ifdef CONFIG_ISDN_TTY_FAX idi_faxdata_rcv(ccard, chan, skb); @@ -1538,27 +2660,27 @@ break; #endif default: - if (DebugVar & 8) - printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED NetIndication 0x%02x\n", chan->No, ind->Ind); + eicon_log(ccard, 8, "idi_ind: Ch%d: UNHANDLED NetIndication 0x%02x\n", chan->No, ind->Ind); } } else { - if (DebugVar & 1) - printk(KERN_ERR "idi_ind: Ch%d: Ind is neither SIG nor NET !\n", chan->No); + eicon_log(ccard, 1, "idi_ind: Ch%d: Ind is neither SIG nor NET !\n", chan->No); } - if (free_buff) dev_kfree_skb(skb); + if (free_buff) + dev_kfree_skb(skb); } int idi_handle_ack_ok(eicon_card *ccard, eicon_chan *chan, eicon_RC *ack) { + ulong flags; isdn_ctrl cmd; if (ack->RcId != ((chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id)) { - /* I dont know why this happens, just ignoring this RC */ - if (DebugVar & 16) - printk(KERN_DEBUG "idi_ack: Ch%d: RcId %d not equal to last %d\n", chan->No, - ack->RcId, (chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id); + /* I dont know why this happens, should not ! */ + /* just ignoring this RC */ + eicon_log(ccard, 16, "idi_ack: Ch%d: RcId %d not equal to last %d\n", chan->No, + ack->RcId, (chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id); return 1; } @@ -1572,27 +2694,27 @@ /* Remove an Id */ if (chan->e.Req == REMOVE) { if (ack->Reference != chan->e.ref) { - if (DebugVar & 16) - printk(KERN_DEBUG "idi_ack: Ch%d: Rc-Ref %d not equal to stored %d\n", chan->No, - ack->Reference, chan->e.ref); - return 0; + /* This should not happen anymore */ + eicon_log(ccard, 16, "idi_ack: Ch%d: Rc-Ref %d not equal to stored %d\n", chan->No, + ack->Reference, chan->e.ref); } + save_flags(flags); + cli(); ccard->IdTable[ack->RcId] = NULL; - if (DebugVar & 16) - printk(KERN_DEBUG "idi_ack: Ch%d: Removed : Id=%x Ch=%d (%s)\n", chan->No, - ack->RcId, ack->RcCh, (chan->e.ReqCh)? "Net":"Sig"); + eicon_log(ccard, 16, "idi_ack: Ch%d: Removed : Id=%x Ch=%d (%s)\n", chan->No, + ack->RcId, ack->RcCh, (chan->e.ReqCh)? "Net":"Sig"); if (!chan->e.ReqCh) chan->e.D3Id = 0; else chan->e.B2Id = 0; + restore_flags(flags); return 1; } /* Signal layer */ if (!chan->e.ReqCh) { - if (DebugVar & 16) - printk(KERN_DEBUG "idi_ack: Ch%d: RC OK Id=%x Ch=%d (ref:%d)\n", chan->No, - ack->RcId, ack->RcCh, ack->Reference); + eicon_log(ccard, 16, "idi_ack: Ch%d: RC OK Id=%x Ch=%d (ref:%d)\n", chan->No, + ack->RcId, ack->RcCh, ack->Reference); } else { /* Network layer */ switch(chan->e.Req & 0x0f) { @@ -1606,7 +2728,10 @@ cmd.parm.length = chan->waitpq; ccard->interface.statcallb(&cmd); } + save_flags(flags); + cli(); chan->waitpq = 0; + restore_flags(flags); #ifdef CONFIG_ISDN_TTY_FAX if (chan->l2prot == ISDN_PROTO_L2_FAX) { if (((chan->queued - chan->waitq) < 1) && @@ -1620,20 +2745,21 @@ ccard->interface.statcallb(&cmd); } else { - if (DebugVar & 1) - printk(KERN_DEBUG "idi_ack: Sent with NULL fax struct, ERROR\n"); + eicon_log(ccard, 1, "idi_ack: Sent with NULL fax struct, ERROR\n"); } } } #endif } + save_flags(flags); + cli(); chan->queued -= chan->waitq; if (chan->queued < 0) chan->queued = 0; + restore_flags(flags); break; default: - if (DebugVar & 16) - printk(KERN_DEBUG "idi_ack: Ch%d: RC OK Id=%x Ch=%d (ref:%d)\n", chan->No, - ack->RcId, ack->RcCh, ack->Reference); + eicon_log(ccard, 16, "idi_ack: Ch%d: RC OK Id=%x Ch=%d (ref:%d)\n", chan->No, + ack->RcId, ack->RcCh, ack->Reference); } } return 1; @@ -1643,22 +2769,30 @@ idi_handle_ack(eicon_card *ccard, struct sk_buff *skb) { int j; + ulong flags; eicon_RC *ack = (eicon_RC *)skb->data; eicon_chan *chan; isdn_ctrl cmd; int dCh = -1; + if (!ccard) { + eicon_log(ccard, 1, "idi_err: Ch??: null card in handle_ack\n"); + dev_kfree_skb(skb); + return; + } + + save_flags(flags); + cli(); if ((chan = ccard->IdTable[ack->RcId]) != NULL) dCh = chan->No; - + restore_flags(flags); switch (ack->Rc) { case OK_FC: case N_FLOW_CONTROL: case ASSIGN_RC: - if (DebugVar & 1) - printk(KERN_ERR "idi_ack: Ch%d: unhandled RC 0x%x\n", - dCh, ack->Rc); + eicon_log(ccard, 1, "idi_ack: Ch%d: unhandled RC 0x%x\n", + dCh, ack->Rc); break; case READY_INT: case TIMER_INT: @@ -1667,8 +2801,7 @@ case OK: if (!chan) { - if (DebugVar & 1) - printk(KERN_ERR "idi_ack: Ch%d: OK on chan without Id\n", dCh); + eicon_log(ccard, 1, "idi_ack: Ch%d: OK on chan without Id\n", dCh); break; } if (!idi_handle_ack_ok(ccard, chan, ack)) @@ -1677,29 +2810,29 @@ case ASSIGN_OK: if (chan) { - if (DebugVar & 1) - printk(KERN_ERR "idi_ack: Ch%d: ASSIGN-OK on chan already assigned (%x,%x)\n", - chan->No, chan->e.D3Id, chan->e.B2Id); + eicon_log(ccard, 1, "idi_ack: Ch%d: ASSIGN-OK on chan already assigned (%x,%x)\n", + chan->No, chan->e.D3Id, chan->e.B2Id); } + save_flags(flags); + cli(); for(j = 0; j < ccard->nchannels + 1; j++) { - if (ccard->bch[j].e.ref == ack->Reference) { + if ((ccard->bch[j].e.ref == ack->Reference) && + (ccard->bch[j].e.Req == ASSIGN)) { if (!ccard->bch[j].e.ReqCh) ccard->bch[j].e.D3Id = ack->RcId; else ccard->bch[j].e.B2Id = ack->RcId; ccard->IdTable[ack->RcId] = &ccard->bch[j]; - ccard->bch[j].e.busy = 0; - ccard->bch[j].e.ref = 0; - if (DebugVar & 16) - printk(KERN_DEBUG"idi_ack: Ch%d: Id %x assigned (%s)\n", j, - ack->RcId, (ccard->bch[j].e.ReqCh)? "Net":"Sig"); + chan = &ccard->bch[j]; + eicon_log(ccard, 16, "idi_ack: Ch%d: Id %x assigned (%s)\n", j, + ack->RcId, (ccard->bch[j].e.ReqCh)? "Net":"Sig"); break; } } + restore_flags(flags); if (j > ccard->nchannels) { - if (DebugVar & 24) - printk(KERN_DEBUG"idi_ack: Ch??: ref %d not found for Id %d\n", - ack->Reference, ack->RcId); + eicon_log(ccard, 24, "idi_ack: Ch??: ref %d not found for Id %d\n", + ack->Reference, ack->RcId); } break; @@ -1711,9 +2844,19 @@ case UNKNOWN_IE: case WRONG_IE: default: - if (DebugVar & 1) - printk(KERN_ERR "eicon_ack: Ch%d: Not OK !!: Rc=%d Id=%x Ch=%d\n", dCh, - ack->Rc, ack->RcId, ack->RcCh); + if (!chan) { + eicon_log(ccard, 1, "idi_ack: Ch%d: Not OK !! on chan without Id\n", dCh); + break; + } else + switch (chan->e.Req) { + case 12: /* Alert */ + eicon_log(ccard, 2, "eicon_err: Ch%d: Alert Not OK : Rc=%d Id=%x Ch=%d\n", + dCh, ack->Rc, ack->RcId, ack->RcCh); + break; + default: + eicon_log(ccard, 1, "eicon_err: Ch%d: Ack Not OK !!: Rc=%d Id=%x Ch=%d Req=%d\n", + dCh, ack->Rc, ack->RcId, ack->RcCh, chan->e.Req); + } if (dCh == ccard->nchannels) { /* Management */ chan->fsm_state = 2; } else if (dCh >= 0) { @@ -1726,8 +2869,13 @@ ccard->interface.statcallb(&cmd); } } - if (chan) + save_flags(flags); + cli(); + if (chan) { + chan->e.ref = 0; chan->e.busy = 0; + } + restore_flags(flags); dev_kfree_skb(skb); eicon_schedule_tx(ccard); } @@ -1742,21 +2890,25 @@ int len, plen = 0, offset = 0; unsigned long flags; + if ((!card) || (!chan)) { + eicon_log(card, 1, "idi_err: Ch??: null card/chan in send_data\n"); + return -1; + } + if (chan->fsm_state != EICON_STATE_ACTIVE) { - if (DebugVar & 1) - printk(KERN_DEBUG"idi_snd: Ch%d: send bytes on state %d !\n", chan->No, chan->fsm_state); + eicon_log(card, 1, "idi_snd: Ch%d: send bytes on state %d !\n", chan->No, chan->fsm_state); return -ENODEV; } len = skb->len; - if (len > 2138) /* too much for the shared memory */ + if (len > EICON_MAX_QUEUE) /* too much for the shared memory */ return -1; if (!len) return 0; - if (chan->queued + len > ((chan->l2prot == ISDN_PROTO_L2_TRANS) ? 4000 : EICON_MAX_QUEUED)) + if (chan->queued + len > EICON_MAX_QUEUE) return 0; - if (DebugVar & 128) - printk(KERN_DEBUG"idi_snd: Ch%d: %d bytes\n", chan->No, len); + + eicon_log(card, 128, "idi_snd: Ch%d: %d bytes\n", chan->No, len); save_flags(flags); cli(); @@ -1769,8 +2921,7 @@ if ((!xmit_skb) || (!skb2)) { restore_flags(flags); - if (DebugVar & 1) - printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed in send_data()\n", chan->No); + eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in send_data()\n", chan->No); if (xmit_skb) dev_kfree_skb(skb); if (skb2) @@ -1824,8 +2975,7 @@ skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC); if ((!skb) || (!skb2)) { - if (DebugVar & 1) - printk(KERN_WARNING "idi_err: alloc_skb failed in manage_assign()\n"); + eicon_log(card, 1, "idi_err: alloc_skb failed in manage_assign()\n"); if (skb) dev_kfree_skb(skb); if (skb2) @@ -1867,8 +3017,7 @@ skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC); if ((!skb) || (!skb2)) { - if (DebugVar & 1) - printk(KERN_WARNING "idi_err: alloc_skb failed in manage_remove()\n"); + eicon_log(card, 1, "idi_err: alloc_skb failed in manage_remove()\n"); if (skb) dev_kfree_skb(skb); if (skb2) @@ -1934,8 +3083,7 @@ chan->fsm_state = 0; if (!(manbuf = kmalloc(sizeof(eicon_manifbuf), GFP_KERNEL))) { - if (DebugVar & 1) - printk(KERN_WARNING "idi_err: alloc_manifbuf failed\n"); + eicon_log(card, 1, "idi_err: alloc_manifbuf failed\n"); chan->e.D3Id = 0; return -ENOMEM; } @@ -1949,8 +3097,7 @@ skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC); if ((!skb) || (!skb2)) { - if (DebugVar & 1) - printk(KERN_WARNING "idi_err_manif: alloc_skb failed in manage()\n"); + eicon_log(card, 1, "idi_err_manif: alloc_skb failed in manage()\n"); if (skb) dev_kfree_skb(skb); if (skb2) diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/eicon/eicon_idi.h linux/drivers/isdn/eicon/eicon_idi.h --- v2.2.13/linux/drivers/isdn/eicon/eicon_idi.h Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/eicon/eicon_idi.h Tue Jan 4 10:12:15 2000 @@ -1,4 +1,4 @@ -/* $Id: eicon_idi.h,v 1.7 1999/08/22 20:26:46 calle Exp $ +/* $Id: eicon_idi.h,v 1.8 1999/11/25 11:43:27 armin Exp $ * * ISDN lowlevel-module for the Eicon.Diehl active cards. * IDI-Interface @@ -21,6 +21,11 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_idi.h,v $ + * Revision 1.8 1999/11/25 11:43:27 armin + * Fixed statectrl and connect message. + * X.75 fix and HDLC/transparent with autoconnect. + * Minor cleanup. + * * Revision 1.7 1999/08/22 20:26:46 calle * backported changes from kernel 2.3.14: * - several #include "config.h" gone, others come. @@ -169,6 +174,10 @@ #define OK 0xff /* command accepted */ /*------------------------------------------------------------------*/ + +/* defines for statectrl */ +#define WAITING_FOR_HANGUP 0x01 +#define HAVE_CONN_REQ 0x02 typedef struct { char cpn[32]; diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/eicon/eicon_io.c linux/drivers/isdn/eicon/eicon_io.c --- v2.2.13/linux/drivers/isdn/eicon/eicon_io.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/eicon/eicon_io.c Tue Jan 4 10:12:15 2000 @@ -1,4 +1,4 @@ -/* $Id: eicon_io.c,v 1.5 1999/08/31 11:20:11 paul Exp $ +/* $Id: eicon_io.c,v 1.9 1999/11/18 20:55:25 armin Exp $ * * ISDN low-level module for Eicon.Diehl active ISDN-Cards. * Code for communicating with hardware. @@ -24,6 +24,20 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_io.c,v $ + * Revision 1.9 1999/11/18 20:55:25 armin + * Ready_Int fix of ISA cards. + * + * Revision 1.8 1999/10/08 22:09:34 armin + * Some fixes of cards interface handling. + * Bugfix of NULL pointer occurence. + * Changed a few log outputs. + * + * Revision 1.7 1999/09/26 14:17:53 armin + * Improved debug and log via readstat() + * + * Revision 1.6 1999/09/21 20:35:43 armin + * added more error checking. + * * Revision 1.5 1999/08/31 11:20:11 paul * various spelling corrections (new checksums may be needed, Karsten!) * @@ -54,19 +68,21 @@ void eicon_io_rcv_dispatch(eicon_card *ccard) { + ulong flags; struct sk_buff *skb, *skb2, *skb_new; eicon_IND *ind, *ind2, *ind_new; eicon_chan *chan; if (!ccard) { - if (DebugVar & 1) - printk(KERN_WARNING "eicon_io_rcv_dispatch: NULL card!\n"); + eicon_log(ccard, 1, "eicon_err: NULL card in rcv_dispatch !\n"); return; } while((skb = skb_dequeue(&ccard->rcvq))) { ind = (eicon_IND *)skb->data; + save_flags(flags); + cli(); if ((chan = ccard->IdTable[ind->IndId]) == NULL) { if (DebugVar & 1) { switch(ind->Ind) { @@ -74,14 +90,16 @@ /* doesn't matter if this happens */ break; default: - printk(KERN_ERR "idi: Indication for unknown channel Ind=%d Id=%x\n", ind->Ind, ind->IndId); - printk(KERN_DEBUG "idi_hdl: Ch??: Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n", + eicon_log(ccard, 1, "idi: Indication for unknown channel Ind=%d Id=%x\n", ind->Ind, ind->IndId); + eicon_log(ccard, 1, "idi_hdl: Ch??: Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n", ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length); } } + restore_flags(flags); dev_kfree_skb(skb); continue; } + restore_flags(flags); if (chan->e.complete) { /* check for rec-buffer chaining */ if (ind->MLength == ind->RBuffer.length) { @@ -97,10 +115,12 @@ } } else { + save_flags(flags); + cli(); if (!(skb2 = skb_dequeue(&chan->e.R))) { chan->e.complete = 1; - if (DebugVar & 1) - printk(KERN_ERR "eicon: buffer incomplete, but 0 in queue\n"); + eicon_log(ccard, 1, "eicon: buffer incomplete, but 0 in queue\n"); + restore_flags(flags); dev_kfree_skb(skb); continue; } @@ -108,8 +128,8 @@ skb_new = alloc_skb(((sizeof(eicon_IND)-1)+ind->RBuffer.length+ind2->RBuffer.length), GFP_ATOMIC); if (!skb_new) { - if (DebugVar & 1) - printk(KERN_ERR "eicon_io: skb_alloc failed in rcv_dispatch()\n"); + eicon_log(ccard, 1, "eicon_io: skb_alloc failed in rcv_dispatch()\n"); + restore_flags(flags); dev_kfree_skb(skb); dev_kfree_skb(skb2); continue; @@ -128,12 +148,14 @@ dev_kfree_skb(skb2); if (ind->MLength == ind->RBuffer.length) { chan->e.complete = 2; + restore_flags(flags); idi_handle_ind(ccard, skb_new); continue; } else { chan->e.complete = 0; skb_queue_tail(&chan->e.R, skb_new); + restore_flags(flags); continue; } } @@ -145,8 +167,7 @@ struct sk_buff *skb; if (!ccard) { - if (DebugVar & 1) - printk(KERN_WARNING "eicon_io_ack_dispatch: NULL card!\n"); + eicon_log(ccard, 1, "eicon_err: NULL card in ack_dispatch!\n"); return; } while((skb = skb_dequeue(&ccard->rackq))) { @@ -402,13 +423,13 @@ int scom = 0; int tmp = 0; int quloop = 1; + int dlev = 0; pci_card = &ccard->hwif.pci; isa_card = &ccard->hwif.isa; if (!ccard) { - if (DebugVar & 1) - printk(KERN_WARNING "eicon_transmit: NULL card!\n"); + eicon_log(ccard, 1, "eicon_transmit: NULL card!\n"); return; } @@ -441,7 +462,7 @@ prram = 0; break; default: - printk(KERN_WARNING "eicon_transmit: unsupported card-type!\n"); + eicon_log(ccard, 1, "eicon_transmit: unsupported card-type!\n"); return; } @@ -452,7 +473,7 @@ save_flags(flags); cli(); if (scom) { - if (ram_inb(ccard, &com->Req)) { + if ((ram_inb(ccard, &com->Req)) || (ccard->ReadyInt)) { if (!ccard->ReadyInt) { tmp = ram_inb(ccard, &com->ReadyInt) + 1; ram_outb(ccard, &com->ReadyInt, tmp); @@ -460,16 +481,14 @@ } restore_flags(flags); skb_queue_head(&ccard->sndq, skb2); - if (DebugVar & 32) - printk(KERN_INFO "eicon: transmit: Card not ready\n"); + eicon_log(ccard, 32, "eicon: transmit: Card not ready\n"); return; } } else { if (!(ram_inb(ccard, &prram->ReqOutput) - ram_inb(ccard, &prram->ReqInput))) { restore_flags(flags); skb_queue_head(&ccard->sndq, skb2); - if (DebugVar & 32) - printk(KERN_INFO "eicon: transmit: Card not ready\n"); + eicon_log(ccard, 32, "eicon: transmit: Card not ready\n"); return; } } @@ -482,8 +501,7 @@ cli(); reqbuf = (eicon_REQ *)skb->data; if ((reqbuf->Reference) && (chan->e.B2Id == 0) && (reqbuf->ReqId & 0x1f)) { - if (DebugVar & 16) - printk(KERN_WARNING "eicon: transmit: error Id=0 on %d (Net)\n", chan->No); + eicon_log(ccard, 16, "eicon: transmit: error Id=0 on %d (Net)\n", chan->No); } else { if (scom) { ram_outw(ccard, &com->XBuffer.length, reqbuf->XBuffer.length); @@ -498,7 +516,7 @@ ram_outb(ccard, &ReqOut->ReqCh, reqbuf->ReqCh); ram_outb(ccard, &ReqOut->Req, reqbuf->Req); } - + dlev = 160; if (reqbuf->ReqId & 0x1f) { /* if this is no ASSIGN */ if (!reqbuf->Reference) { /* Signal Layer */ @@ -520,6 +538,7 @@ ((reqbuf->Req & 0x0f) == 0x01)) { /* Send Data */ chan->waitq = reqbuf->XBuffer.length; chan->waitpq += reqbuf->XBuffer.length; + dlev = 128; } } @@ -547,13 +566,13 @@ else ram_outw(ccard, &prram->NextReq, ram_inw(ccard, &ReqOut->next)); - chan->e.busy = 1; - if (DebugVar & 32) - printk(KERN_DEBUG "eicon: Req=%d Id=%x Ch=%d Len=%d Ref=%d\n", - reqbuf->Req, - ram_inb(ccard, &ReqOut->ReqId), - reqbuf->ReqCh, reqbuf->XBuffer.length, - chan->e.ref); + chan->e.busy = 1; + eicon_log(ccard, dlev, "eicon: Req=%d Id=%x Ch=%d Len=%d Ref=%d\n", + reqbuf->Req, + (scom) ? ram_inb(ccard, &com->ReqId) : + ram_inb(ccard, &ReqOut->ReqId), + reqbuf->ReqCh, reqbuf->XBuffer.length, + chan->e.ref); } restore_flags(flags); dev_kfree_skb(skb); @@ -562,8 +581,7 @@ } else { skb_queue_tail(&ccard->sackq, skb2); - if (DebugVar & 32) - printk(KERN_INFO "eicon: transmit: busy chan %d\n", chan->No); + eicon_log(ccard, 128, "eicon: transmit: busy chan %d\n", chan->No); } if (scom) @@ -604,10 +622,11 @@ unsigned char *irqprobe = 0; int scom = 0; int tmp = 0; + int dlev = 0; if (!ccard) { - printk(KERN_WARNING "eicon_irq: spurious interrupt %d\n", irq); + eicon_log(ccard, 1, "eicon_irq: spurious interrupt %d\n", irq); return; } @@ -659,7 +678,7 @@ prram = 0; break; default: - printk(KERN_WARNING "eicon_irq: unsupported card-type!\n"); + eicon_log(ccard, 1, "eicon_irq: unsupported card-type!\n"); return; } @@ -709,24 +728,21 @@ case EICON_CTYPE_QUADRO: case EICON_CTYPE_S2M: if (!(readb(isa_card->intack))) { /* card did not interrupt */ - if (DebugVar & 1) - printk(KERN_DEBUG "eicon: IRQ: card reports no interrupt!\n"); + eicon_log(ccard, 1, "eicon: IRQ: card reports no interrupt!\n"); return; } break; #endif case EICON_CTYPE_MAESTRAP: if (!(readb(&ram[0x3fe]))) { /* card did not interrupt */ - if (DebugVar & 1) - printk(KERN_DEBUG "eicon: IRQ: card reports no interrupt!\n"); + eicon_log(ccard, 1, "eicon: IRQ: card reports no interrupt!\n"); return; } break; case EICON_CTYPE_MAESTRA: outw(0x3fe, pci_card->PCIreg + M_ADDR); if (!(inb(pci_card->PCIreg + M_DATA))) { /* card did not interrupt */ - if (DebugVar & 1) - printk(KERN_DEBUG "eicon: IRQ: card reports no interrupt!\n"); + eicon_log(ccard, 1, "eicon: IRQ: card reports no interrupt!\n"); return; } break; @@ -738,26 +754,24 @@ if ((tmp = ram_inb(ccard, &com->Rc))) { eicon_RC *ack; if (tmp == READY_INT) { - if (DebugVar & 64) - printk(KERN_INFO "eicon: IRQ Rc=READY_INT\n"); + eicon_log(ccard, 64, "eicon: IRQ Rc=READY_INT\n"); if (ccard->ReadyInt) { ccard->ReadyInt--; ram_outb(ccard, &com->Rc, 0); + eicon_schedule_tx(ccard); } } else { skb = alloc_skb(sizeof(eicon_RC), GFP_ATOMIC); if (!skb) { - if (DebugVar & 1) - printk(KERN_ERR "eicon_io: skb_alloc failed in _irq()\n"); + eicon_log(ccard, 1, "eicon_io: skb_alloc failed in _irq()\n"); } else { ack = (eicon_RC *)skb_put(skb, sizeof(eicon_RC)); ack->Rc = tmp; ack->RcId = ram_inb(ccard, &com->RcId); ack->RcCh = ram_inb(ccard, &com->RcCh); ack->Reference = ccard->ref_in++; - if (DebugVar & 64) - printk(KERN_INFO "eicon: IRQ Rc=%d Id=%x Ch=%d Ref=%d\n", - tmp,ack->RcId,ack->RcCh,ack->Reference); + eicon_log(ccard, 128, "eicon: IRQ Rc=%d Id=%x Ch=%d Ref=%d\n", + tmp,ack->RcId,ack->RcCh,ack->Reference); skb_queue_tail(&ccard->rackq, skb); eicon_schedule_ack(ccard); } @@ -773,8 +787,7 @@ int len = ram_inw(ccard, &com->RBuffer.length); skb = alloc_skb((sizeof(eicon_IND) + len - 1), GFP_ATOMIC); if (!skb) { - if (DebugVar & 1) - printk(KERN_ERR "eicon_io: skb_alloc failed in _irq()\n"); + eicon_log(ccard, 1, "eicon_io: skb_alloc failed in _irq()\n"); } else { ind = (eicon_IND *)skb_put(skb, (sizeof(eicon_IND) + len - 1)); ind->Ind = tmp; @@ -782,9 +795,12 @@ ind->IndCh = ram_inb(ccard, &com->IndCh); ind->MInd = ram_inb(ccard, &com->MInd); ind->MLength = ram_inw(ccard, &com->MLength); - ind->RBuffer.length = len; - if (DebugVar & 64) - printk(KERN_INFO "eicon: IRQ Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n", + ind->RBuffer.length = len; + if ((tmp == 1) || (tmp == 8)) + dlev = 128; + else + dlev = 192; + eicon_log(ccard, dlev, "eicon: IRQ Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n", tmp,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,len); ram_copyfromcard(ccard, &ind->RBuffer.P, &com->RBuffer.P, len); skb_queue_tail(&ccard->rcvq, skb); @@ -807,17 +823,15 @@ if((Rc=ram_inb(ccard, &RcIn->Rc))) { skb = alloc_skb(sizeof(eicon_RC), GFP_ATOMIC); if (!skb) { - if (DebugVar & 1) - printk(KERN_ERR "eicon_io: skb_alloc failed in _irq()\n"); + eicon_log(ccard, 1, "eicon_io: skb_alloc failed in _irq()\n"); } else { ack = (eicon_RC *)skb_put(skb, sizeof(eicon_RC)); ack->Rc = Rc; ack->RcId = ram_inb(ccard, &RcIn->RcId); ack->RcCh = ram_inb(ccard, &RcIn->RcCh); ack->Reference = ram_inw(ccard, &RcIn->Reference); - if (DebugVar & 64) - printk(KERN_INFO "eicon: IRQ Rc=%d Id=%x Ch=%d Ref=%d\n", - Rc,ack->RcId,ack->RcCh,ack->Reference); + eicon_log(ccard, 128, "eicon: IRQ Rc=%d Id=%x Ch=%d Ref=%d\n", + Rc,ack->RcId,ack->RcCh,ack->Reference); skb_queue_tail(&ccard->rackq, skb); eicon_schedule_ack(ccard); } @@ -842,8 +856,7 @@ int len = ram_inw(ccard, &IndIn->RBuffer.length); skb = alloc_skb((sizeof(eicon_IND) + len - 1), GFP_ATOMIC); if (!skb) { - if (DebugVar & 1) - printk(KERN_ERR "eicon_io: skb_alloc failed in _irq()\n"); + eicon_log(ccard, 1, "eicon_io: skb_alloc failed in _irq()\n"); } else { ind = (eicon_IND *)skb_put(skb, (sizeof(eicon_IND) + len - 1)); ind->Ind = Ind; @@ -852,8 +865,11 @@ ind->MInd = ram_inb(ccard, &IndIn->MInd); ind->MLength = ram_inw(ccard, &IndIn->MLength); ind->RBuffer.length = len; - if (DebugVar & 64) - printk(KERN_INFO "eicon: IRQ Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n", + if ((Ind == 1) || (Ind == 8)) + dlev = 128; + else + dlev = 192; + eicon_log(ccard, dlev, "eicon: IRQ Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n", Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,len); ram_copyfromcard(ccard, &ind->RBuffer.P, &IndIn->RBuffer.P, len); skb_queue_tail(&ccard->rcvq, skb); diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/eicon/eicon_isa.c linux/drivers/isdn/eicon/eicon_isa.c --- v2.2.13/linux/drivers/isdn/eicon/eicon_isa.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/eicon/eicon_isa.c Tue Jan 4 10:12:15 2000 @@ -1,4 +1,4 @@ -/* $Id: eicon_isa.c,v 1.9 1999/09/08 20:17:31 armin Exp $ +/* $Id: eicon_isa.c,v 1.12 1999/11/27 12:56:19 armin Exp $ * * ISDN low-level module for Eicon.Diehl active ISDN-Cards. * Hardware-specific code for old ISA cards. @@ -22,8 +22,17 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_isa.c,v $ + * Revision 1.12 1999/11/27 12:56:19 armin + * Forgot some iomem changes for last ioremap compat. + * + * Revision 1.11 1999/11/25 11:33:09 armin + * Microchannel fix from Erik Weber (exrz73@ibm.net). + * + * Revision 1.10 1999/11/18 21:14:30 armin + * New ISA memory mapped IO + * * Revision 1.9 1999/09/08 20:17:31 armin - * Added microchannel patch from Erik Weber. + * Added microchannel patch from Erik Weber (exrz73@ibm.net). * * Revision 1.8 1999/09/06 07:29:35 fritz * Changed my mail-address. @@ -70,7 +79,7 @@ #define release_shmem release_region #define request_shmem request_region -char *eicon_isa_revision = "$Revision: 1.9 $"; +char *eicon_isa_revision = "$Revision: 1.12 $"; #undef EICON_MCA_DEBUG @@ -87,8 +96,9 @@ static void eicon_isa_release_shmem(eicon_isa_card *card) { - if (card->mvalid) + if (card->mvalid) { release_shmem((unsigned long)card->shmem, card->ramsize); + } card->mvalid = 0; } @@ -117,7 +127,7 @@ case EICON_CTYPE_S2M: printk(KERN_INFO "Eicon %s at 0x%lx, irq %d.\n", eicon_ctype_name[card->type], - (unsigned long)card->shmem, + card->physmem, card->irq); } } @@ -126,6 +136,7 @@ eicon_isa_find_card(int Mem, int Irq, char * Id) { int primary = 1; + unsigned long amem; if (!strlen(Id)) return -1; @@ -143,19 +154,20 @@ return -1; } - writew(0x55aa, Mem + 0x402); - if (readw(Mem + 0x402) != 0x55aa) primary = 0; - writew(0, Mem + 0x402); - if (readw(Mem + 0x402) != 0) primary = 0; + amem = (unsigned long) Mem; + writew(0x55aa, amem + 0x402); + if (readw(amem + 0x402) != 0x55aa) primary = 0; + writew(0, amem + 0x402); + if (readw(amem + 0x402) != 0) primary = 0; printk(KERN_INFO "Eicon: Driver-ID: %s\n", Id); if (primary) { printk(KERN_INFO "Eicon: assuming pri card at 0x%x\n", Mem); - writeb(0, Mem + 0x3ffe); + writeb(0, amem + 0x3ffe); return EICON_CTYPE_ISAPRI; } else { printk(KERN_INFO "Eicon: assuming bri card at 0x%x\n", Mem); - writeb(0, Mem + 0x400); + writeb(0, amem + 0x400); return EICON_CTYPE_ISABRI; } return -1; @@ -187,57 +199,65 @@ return -EFAULT; } + if (card->type == EICON_CTYPE_ISAPRI) + card->ramsize = RAMSIZE_P; + else + card->ramsize = RAMSIZE; + + /* Register shmem */ + if (check_shmem((unsigned long)card->shmem, card->ramsize)) { + printk(KERN_WARNING "eicon_isa_boot: memory at 0x%lx already in use.\n", + (unsigned long)card->shmem); + kfree(code); + return -EBUSY; + } + request_shmem((unsigned long)card->shmem, card->ramsize, "Eicon ISA ISDN"); +#ifdef EICON_MCA_DEBUG + printk(KERN_INFO "eicon_isa_boot: card->ramsize = %d.\n", card->ramsize); +#endif + card->mvalid = 1; + switch(card->type) { case EICON_CTYPE_S: case EICON_CTYPE_SX: case EICON_CTYPE_SCOM: case EICON_CTYPE_QUADRO: case EICON_CTYPE_ISABRI: - card->ramsize = RAMSIZE; card->intack = (__u8 *)card->shmem + INTACK; card->startcpu = (__u8 *)card->shmem + STARTCPU; card->stopcpu = (__u8 *)card->shmem + STOPCPU; break; case EICON_CTYPE_S2M: case EICON_CTYPE_ISAPRI: - card->ramsize = RAMSIZE_P; card->intack = (__u8 *)card->shmem + INTACK_P; card->startcpu = (__u8 *)card->shmem + STARTCPU_P; card->stopcpu = (__u8 *)card->shmem + STOPCPU_P; break; default: printk(KERN_WARNING "eicon_isa_boot: Invalid card type %d\n", card->type); + eicon_isa_release_shmem(card); + kfree(code); return -EINVAL; } - /* Register shmem */ - if (check_shmem((unsigned long)card->shmem, card->ramsize)) { - printk(KERN_WARNING "eicon_isa_boot: memory at 0x%lx already in use.\n", - (unsigned long)card->shmem); - kfree(code); - return -EBUSY; - } - request_shmem((unsigned long)card->shmem, card->ramsize, "Eicon ISA ISDN"); -#ifdef EICON_MCA_DEBUG - printk(KERN_INFO "eicon_isa_boot: card->ramsize = %d.\n", card->ramsize); -#endif - card->mvalid = 1; - /* clear any pending irq's */ readb(card->intack); #ifdef CONFIG_MCA - if (card->type == EICON_CTYPE_SCOM) { - outb_p(0,card->io+1); - } - else { - printk(KERN_WARNING "eicon_isa_boot: Card type yet not supported.\n"); - return -EINVAL; - }; + if (MCA_bus) { + if (card->type == EICON_CTYPE_SCOM) { + outb_p(0,card->io+1); + } + else { + printk(KERN_WARNING "eicon_isa_boot: Card type not supported yet.\n"); + eicon_isa_release_shmem(card); + return -EINVAL; + }; #ifdef EICON_MCA_DEBUG printk(KERN_INFO "eicon_isa_boot: card->io = %x.\n", card->io); printk(KERN_INFO "eicon_isa_boot: card->irq = %d.\n", (int)card->irq); #endif + } #else /* set reset-line active */ writeb(0, card->stopcpu); @@ -269,7 +289,9 @@ /* Start CPU */ writeb(cbuf.boot_opt, &boot->ctrl); #ifdef CONFIG_MCA - outb_p(0, card->io); + if (MCA_bus) { + outb_p(0, card->io); + } #else writeb(0, card->startcpu); #endif /* CONFIG_MCA */ @@ -320,7 +342,7 @@ } printk(KERN_INFO "%s: startup-code loaded\n", eicon_ctype_name[card->type]); if ((card->type == EICON_CTYPE_QUADRO) && (card->master)) { - tmp = eicon_addcard(card->type, (unsigned long)card->shmem, card->irq, + tmp = eicon_addcard(card->type, card->physmem, card->irq, ((eicon_card *)card->card)->regname); printk(KERN_INFO "Eicon: %d adapters added\n", tmp); } diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/eicon/eicon_isa.h linux/drivers/isdn/eicon/eicon_isa.h --- v2.2.13/linux/drivers/isdn/eicon/eicon_isa.h Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/eicon/eicon_isa.h Tue Jan 4 10:12:15 2000 @@ -1,4 +1,4 @@ -/* $Id: eicon_isa.h,v 1.5 1999/09/08 20:17:31 armin Exp $ +/* $Id: eicon_isa.h,v 1.7 1999/11/18 21:14:30 armin Exp $ * * ISDN low-level module for Eicon.Diehl active ISDN-Cards. * @@ -21,6 +21,12 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_isa.h,v $ + * Revision 1.7 1999/11/18 21:14:30 armin + * New ISA memory mapped IO + * + * Revision 1.6 1999/11/15 19:37:04 keil + * need config.h + * * Revision 1.5 1999/09/08 20:17:31 armin * Added microchannel patch from Erik Weber. * @@ -48,6 +54,7 @@ #define eicon_isa_h #ifdef __KERNEL__ +#include /* Factory defaults for ISA-Cards */ #define EICON_ISA_MEMBASE 0xd0000 @@ -112,6 +119,7 @@ typedef struct { int ramsize; int irq; /* IRQ */ + unsigned long physmem; /* physical memory address */ #ifdef CONFIG_MCA int io; /* IO-port for MCA brand */ #endif /* CONFIG_MCA */ diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/eicon/eicon_mod.c linux/drivers/isdn/eicon/eicon_mod.c --- v2.2.13/linux/drivers/isdn/eicon/eicon_mod.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/eicon/eicon_mod.c Tue Jan 4 10:12:15 2000 @@ -1,4 +1,4 @@ -/* $Id: eicon_mod.c,v 1.15 1999/09/08 20:17:31 armin Exp $ +/* $Id: eicon_mod.c,v 1.22 1999/11/27 12:56:19 armin Exp $ * * ISDN lowlevel-module for Eicon.Diehl active cards. * @@ -11,6 +11,11 @@ * * Deutsche Telekom AG for S2M support. * + * Deutsche Mailbox Saar-Lor-Lux GmbH + * for sponsoring and testing fax + * capabilities with Diva Server cards. + * (dor@deutschemailbox.de) + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) @@ -26,8 +31,32 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_mod.c,v $ + * Revision 1.22 1999/11/27 12:56:19 armin + * Forgot some iomem changes for last ioremap compat. + * + * Revision 1.21 1999/11/25 11:35:10 armin + * Microchannel fix from Erik Weber (exrz73@ibm.net). + * Minor cleanup. + * + * Revision 1.20 1999/11/18 21:14:30 armin + * New ISA memory mapped IO + * + * Revision 1.19 1999/11/12 13:21:44 armin + * Bugfix of undefined reference with CONFIG_MCA + * + * Revision 1.18 1999/10/11 18:13:25 armin + * Added fax capabilities for Eicon Diva Server cards. + * + * Revision 1.17 1999/10/08 22:09:34 armin + * Some fixes of cards interface handling. + * Bugfix of NULL pointer occurence. + * Changed a few log outputs. + * + * Revision 1.16 1999/09/26 14:17:53 armin + * Improved debug and log via readstat() + * * Revision 1.15 1999/09/08 20:17:31 armin - * Added microchannel patch from Erik Weber. + * Added microchannel patch from Erik Weber (exrz73@ibm.net). * * Revision 1.14 1999/09/06 07:29:35 fritz * Changed my mail-address. @@ -104,7 +133,7 @@ static eicon_card *cards = (eicon_card *) NULL; /* glob. var , contains start of card-list */ -static char *eicon_revision = "$Revision: 1.15 $"; +static char *eicon_revision = "$Revision: 1.22 $"; extern char *eicon_pci_revision; extern char *eicon_isa_revision; @@ -185,8 +214,7 @@ { if ((channel >= 0) && (channel < card->nchannels)) return &(card->bch[channel]); - if (DebugVar & 1) - printk(KERN_WARNING "eicon: Invalid channel %d\n", channel); + eicon_log(card, 1, "eicon: Invalid channel %d\n", channel); return NULL; } @@ -263,9 +291,8 @@ eicon_io_rcv_dispatch(card); break; default: - if (DebugVar & 1) - printk(KERN_WARNING - "eicon_ack_dispatch: Illegal bustype %d\n", card->bus); + eicon_log(card, 1, + "eicon_ack_dispatch: Illegal bustype %d\n", card->bus); } } @@ -279,9 +306,8 @@ eicon_io_ack_dispatch(card); break; default: - if (DebugVar & 1) - printk(KERN_WARNING - "eicon_ack_dispatch: Illegal bustype %d\n", card->bus); + eicon_log(card, 1, + "eicon_ack_dispatch: Illegal bustype %d\n", card->bus); } } @@ -295,9 +321,8 @@ eicon_io_transmit(card); break; default: - if (DebugVar & 1) - printk(KERN_WARNING - "eicon_transmit: Illegal bustype %d\n", card->bus); + eicon_log(card, 1, + "eicon_transmit: Illegal bustype %d\n", card->bus); } } @@ -307,8 +332,7 @@ int ret_val; if (!(xlr = kmalloc(sizeof(xlogreq_t), GFP_KERNEL))) { - if (DebugVar & 1) - printk(KERN_WARNING "idi_err: alloc_xlogreq_t failed\n"); + eicon_log(card, 1, "idi_err: alloc_xlogreq_t failed\n"); return -ENOMEM; } if (copy_from_user(xlr, xlogreq, sizeof(xlogreq_t))) { @@ -338,9 +362,8 @@ int ret = 0; unsigned long flags; - if (DebugVar & 16) - printk(KERN_WARNING "eicon_cmd 0x%x with arg 0x%lx (0x%lx)\n", - c->command, c->arg, (ulong) *c->parm.num); + eicon_log(card, 16, "eicon_cmd 0x%x with arg 0x%lx (0x%lx)\n", + c->command, c->arg, (ulong) *c->parm.num); switch (c->command) { case ISDN_CMD_IOCTL: @@ -360,9 +383,8 @@ return card->hwif.pci.PCIram; #endif default: - if (DebugVar & 1) - printk(KERN_WARNING - "eicon: Illegal BUS type %d\n", + eicon_log(card, 1, + "eicon: Illegal BUS type %d\n", card->bus); ret = -ENODEV; } @@ -389,9 +411,8 @@ return 0; #endif /* CONFIG_MCA */ default: - if (DebugVar & 1) - printk(KERN_WARNING - "eicon: Illegal BUS type %d\n", + eicon_log(card, 1, + "eicon: Illegal BUS type %d\n", card->bus); ret = -ENODEV; } @@ -406,9 +427,8 @@ return card->hwif.pci.irq; #endif default: - if (DebugVar & 1) - printk(KERN_WARNING - "eicon: Illegal BUS type %d\n", + eicon_log(card, 1, + "eicon: Illegal BUS type %d\n", card->bus); ret = -ENODEV; } @@ -423,9 +443,8 @@ card->hwif.isa.irq = a; return 0; default: - if (DebugVar & 1) - printk(KERN_WARNING - "eicon: Illegal BUS type %d\n", + eicon_log(card, 1, + "eicon: Illegal BUS type %d\n", card->bus); ret = -ENODEV; } @@ -441,9 +460,8 @@ &(((eicon_codebuf *)a)->isa)); break; default: - if (DebugVar & 1) - printk(KERN_WARNING - "eicon: Illegal BUS type %d\n", + eicon_log(card, 1, + "eicon: Illegal BUS type %d\n", card->bus); ret = -ENODEV; } @@ -475,9 +493,8 @@ } break; default: - if (DebugVar & 1) - printk(KERN_WARNING - "eicon: Illegal BUS type %d\n", + eicon_log(card, 1, + "eicon: Illegal BUS type %d\n", card->bus); ret = -ENODEV; } @@ -541,7 +558,7 @@ return 0; case EICON_IOCTL_DEBUGVAR: DebugVar = a; - printk(KERN_DEBUG"Eicon: Debug Value set to %ld\n", DebugVar); + eicon_log(card, 1, "Eicon: Debug Value set to %ld\n", DebugVar); return 0; #ifdef MODULE case EICON_IOCTL_FREEIT: @@ -562,8 +579,7 @@ cli(); if ((chan->fsm_state != EICON_STATE_NULL) && (chan->fsm_state != EICON_STATE_LISTEN)) { restore_flags(flags); - if (DebugVar & 1) - printk(KERN_WARNING "Dial on channel %d with state %d\n", + eicon_log(card, 1, "Dial on channel %d with state %d\n", chan->No, chan->fsm_state); return -EBUSY; } @@ -572,7 +588,6 @@ else tmp[0] = c->parm.setup.eazmsn[0]; chan->fsm_state = EICON_STATE_OCALL; - chan->callref = 0xffff; restore_flags(flags); ret = idi_connect_req(card, chan, c->parm.setup.phone, @@ -667,20 +682,17 @@ case ISDN_CMD_GETEAZ: if (!card->flags & EICON_FLAGS_RUNNING) return -ENODEV; - if (DebugVar & 1) - printk(KERN_DEBUG "eicon CMD_GETEAZ not implemented\n"); + eicon_log(card, 1, "eicon CMD_GETEAZ not implemented\n"); return 0; case ISDN_CMD_SETSIL: if (!card->flags & EICON_FLAGS_RUNNING) return -ENODEV; - if (DebugVar & 1) - printk(KERN_DEBUG "eicon CMD_SETSIL not implemented\n"); + eicon_log(card, 1, "eicon CMD_SETSIL not implemented\n"); return 0; case ISDN_CMD_GETSIL: if (!card->flags & EICON_FLAGS_RUNNING) return -ENODEV; - if (DebugVar & 1) - printk(KERN_DEBUG "eicon CMD_GETSIL not implemented\n"); + eicon_log(card, 1, "eicon CMD_GETSIL not implemented\n"); return 0; case ISDN_CMD_LOCK: MOD_INC_USE_COUNT; @@ -752,6 +764,52 @@ static int if_readstatus(u_char * buf, int len, int user, int id, int channel) { + int count = 0; + int cnt = 0; + ulong flags = 0; + u_char *p = buf; + struct sk_buff *skb; + + eicon_card *card = eicon_findcard(id); + + if (card) { + if (!card->flags & EICON_FLAGS_RUNNING) + return -ENODEV; + + save_flags(flags); + cli(); + while((skb = skb_dequeue(&card->statq))) { + + if ((skb->len + count) > len) + cnt = len - count; + else + cnt = skb->len; + + if (user) + copy_to_user(p, skb->data, cnt); + else + memcpy(p, skb->data, cnt); + + count += cnt; + p += cnt; + + if (cnt == skb->len) { + dev_kfree_skb(skb); + if (card->statq_entries > 0) + card->statq_entries--; + } else { + skb_pull(skb, cnt); + skb_queue_head(&card->statq, skb); + restore_flags(flags); + return count; + } + } + card->statq_entries = 0; + restore_flags(flags); + return count; + } + printk(KERN_ERR + "eicon: if_readstatus called with invalid driverId!\n"); return 0; } @@ -766,14 +824,11 @@ len = skb->len; if (card) { - if (!card->flags & EICON_FLAGS_RUNNING) { - dev_kfree_skb(skb); + if (!card->flags & EICON_FLAGS_RUNNING) return -ENODEV; - } - if (!(chan = find_channel(card, channel))) { - dev_kfree_skb(skb); + if (!(chan = find_channel(card, channel))) return -ENODEV; - } + if (chan->fsm_state == EICON_STATE_ACTIVE) { #ifdef CONFIG_ISDN_TTY_FAX if (chan->l2prot == ISDN_PROTO_L2_FAX) { @@ -785,16 +840,114 @@ ret = idi_send_data(card, chan, ack, skb, 1); return (ret); } else { - dev_kfree_skb(skb); return -ENODEV; } } printk(KERN_ERR "eicon: if_sendbuf called with invalid driverId!\n"); - dev_kfree_skb(skb); return -ENODEV; } +/* jiftime() copied from HiSax */ +static inline int jiftime(char *s, long mark) +{ + s += 8; + + *s-- = '\0'; + *s-- = mark % 10 + '0'; + mark /= 10; + *s-- = mark % 10 + '0'; + mark /= 10; + *s-- = '.'; + *s-- = mark % 10 + '0'; + mark /= 10; + *s-- = mark % 6 + '0'; + mark /= 6; + *s-- = ':'; + *s-- = mark % 10 + '0'; + mark /= 10; + *s-- = mark % 10 + '0'; + return(8); +} + +void +eicon_putstatus(eicon_card * card, char * buf) +{ + ulong flags; + int count; + isdn_ctrl cmd; + u_char *p; + struct sk_buff *skb; + + if (!card) + return; + + save_flags(flags); + cli(); + count = strlen(buf); + skb = alloc_skb(count, GFP_ATOMIC); + if (!skb) { + restore_flags(flags); + printk(KERN_ERR "eicon: could not alloc skb in putstatus\n"); + return; + } + p = skb_put(skb, count); + memcpy(p, buf, count); + + skb_queue_tail(&card->statq, skb); + + if (card->statq_entries >= MAX_STATUS_BUFFER) { + if ((skb = skb_dequeue(&card->statq))) { + count -= skb->len; + dev_kfree_skb(skb); + } else + count = 0; + } else + card->statq_entries++; + + restore_flags(flags); + if (count) { + cmd.command = ISDN_STAT_STAVAIL; + cmd.driver = card->myid; + cmd.arg = count; + card->interface.statcallb(&cmd); + } +} + +/* + * Debug and Log + */ +void +eicon_log(eicon_card * card, int level, const char *fmt, ...) +{ + va_list args; + char Line[160]; + u_char *p; + + + if ((DebugVar & level) || (DebugVar & 256)) { + va_start(args, fmt); + + if (DebugVar & level) { + if (DebugVar & 256) { + /* log-buffer */ + p = Line; + p += jiftime(p, jiffies); + *p++ = 32; + p += vsprintf(p, fmt, args); + *p = 0; + eicon_putstatus(card, Line); + } else { + /* printk, syslogd */ + vsprintf(Line, fmt, args); + printk(KERN_DEBUG "%s", Line); + } + } + + va_end(args); + } +} + /* * Allocate a new card-struct, initialize it @@ -817,7 +970,7 @@ qloop = (Type == EICON_CTYPE_QUADRO)?2:0; for (i = 0; i <= qloop; i++) { if (!(card = (eicon_card *) kmalloc(sizeof(eicon_card), GFP_KERNEL))) { - printk(KERN_WARNING + eicon_log(card, 1, "eicon: (%s) Could not allocate card-struct.\n", id); return; } @@ -826,6 +979,8 @@ skb_queue_head_init(&card->rcvq); skb_queue_head_init(&card->rackq); skb_queue_head_init(&card->sackq); + skb_queue_head_init(&card->statq); + card->statq_entries = 0; card->snd_tq.routine = (void *) (void *) eicon_transmit; card->snd_tq.data = card; card->rcv_tq.routine = (void *) (void *) eicon_rcv_dispatch; @@ -854,19 +1009,28 @@ case EICON_CTYPE_S: case EICON_CTYPE_SX: case EICON_CTYPE_SCOM: - if (membase == -1) - membase = EICON_ISA_MEMBASE; - if (irq == -1) - irq = EICON_ISA_IRQ; - card->bus = EICON_BUS_MCA; - card->hwif.isa.card = (void *)card; - card->hwif.isa.shmem = (eicon_isa_shmem *)membase; - card->hwif.isa.master = 1; - - card->hwif.isa.irq = irq; - card->hwif.isa.type = Type; - card->nchannels = 2; - card->interface.channels = 1; + if (MCA_bus) { + if (membase == -1) + membase = EICON_ISA_MEMBASE; + if (irq == -1) + irq = EICON_ISA_IRQ; + card->bus = EICON_BUS_MCA; + card->hwif.isa.card = (void *)card; + card->hwif.isa.shmem = (eicon_isa_shmem *)membase; + card->hwif.isa.physmem = (unsigned long)membase; + card->hwif.isa.master = 1; + + card->hwif.isa.irq = irq; + card->hwif.isa.type = Type; + card->nchannels = 2; + card->interface.channels = 1; + } else { + printk(KERN_WARNING + "eicon (%s): no MCA bus detected.\n", + card->interface.id); + kfree(card); + return; + } break; #endif /* CONFIG_MCA */ case EICON_CTYPE_QUADRO: @@ -877,6 +1041,7 @@ card->bus = EICON_BUS_ISA; card->hwif.isa.card = (void *)card; card->hwif.isa.shmem = (eicon_isa_shmem *)(membase + (i+1) * EICON_ISA_QOFFSET); + card->hwif.isa.physmem = (unsigned long)(membase + (i+1) * EICON_ISA_QOFFSET); card->hwif.isa.master = 0; strcpy(card->interface.id, id); if (id[strlen(id) - 1] == 'a') { @@ -897,7 +1062,7 @@ p = p->next; } if (!p) { - printk(KERN_WARNING "eicon_alloccard: Quadro Master not found.\n"); + eicon_log(card, 1, "eicon_alloccard: Quadro Master not found.\n"); kfree(card); return; } @@ -919,7 +1084,7 @@ ISDN_FEATURE_L2_V11019 | ISDN_FEATURE_L2_V11038 | ISDN_FEATURE_L2_MODEM | - /* ISDN_FEATURE_L2_FAX | */ + ISDN_FEATURE_L2_FAX | ISDN_FEATURE_L3_TRANSDSP | ISDN_FEATURE_L3_FAX; card->hwif.pci.card = (void *)card; @@ -943,7 +1108,7 @@ ISDN_FEATURE_L2_V11019 | ISDN_FEATURE_L2_V11038 | ISDN_FEATURE_L2_MODEM | - /* ISDN_FEATURE_L2_FAX | */ + ISDN_FEATURE_L2_FAX | ISDN_FEATURE_L3_TRANSDSP | ISDN_FEATURE_L3_FAX; card->hwif.pci.card = (void *)card; @@ -970,6 +1135,7 @@ card->bus = EICON_BUS_ISA; card->hwif.isa.card = (void *)card; card->hwif.isa.shmem = (eicon_isa_shmem *)membase; + card->hwif.isa.physmem = (unsigned long)membase; card->hwif.isa.master = 1; card->hwif.isa.irq = irq; card->hwif.isa.type = Type; @@ -984,6 +1150,7 @@ card->bus = EICON_BUS_ISA; card->hwif.isa.card = (void *)card; card->hwif.isa.shmem = (eicon_isa_shmem *)membase; + card->hwif.isa.physmem = (unsigned long)membase; card->hwif.isa.master = 1; card->hwif.isa.irq = irq; card->hwif.isa.type = Type; @@ -992,27 +1159,28 @@ break; #endif default: - printk(KERN_WARNING "eicon_alloccard: Invalid type %d\n", Type); + eicon_log(card, 1, "eicon_alloccard: Invalid type %d\n", Type); kfree(card); return; } if (!(card->bch = (eicon_chan *) kmalloc(sizeof(eicon_chan) * (card->nchannels + 1) , GFP_KERNEL))) { - printk(KERN_WARNING + eicon_log(card, 1, "eicon: (%s) Could not allocate bch-struct.\n", id); kfree(card); return; } for (j=0; j< (card->nchannels + 1); j++) { memset((char *)&card->bch[j], 0, sizeof(eicon_chan)); - card->bch[j].plci = 0x8000; - card->bch[j].ncci = 0x8000; + card->bch[j].statectrl = 0; card->bch[j].l2prot = ISDN_PROTO_L2_X75I; card->bch[j].l3prot = ISDN_PROTO_L3_TRANS; card->bch[j].e.D3Id = 0; card->bch[j].e.B2Id = 0; card->bch[j].e.Req = 0; card->bch[j].No = j; + card->bch[j].tskb1 = NULL; + card->bch[j].tskb2 = NULL; skb_queue_head_init(&card->bch[j].e.X); skb_queue_head_init(&card->bch[j].e.R); } @@ -1044,9 +1212,8 @@ break; #endif default: - if (DebugVar & 1) - printk(KERN_WARNING - "eicon_registercard: Illegal BUS type %d\n", + eicon_log(card, 1, + "eicon_registercard: Illegal BUS type %d\n", card->bus); return -1; } @@ -1085,9 +1252,8 @@ break; #endif default: - if (DebugVar & 1) - printk(KERN_WARNING - "eicon: Invalid BUS type %d\n", + eicon_log(card, 1, + "eicon: Invalid BUS type %d\n", card->bus); break; } @@ -1096,6 +1262,26 @@ static void eicon_freecard(eicon_card *card) { + int i; + struct sk_buff *skb; + + for(i = 0; i < (card->nchannels + 1); i++) { + while((skb = skb_dequeue(&card->bch[i].e.X))) + dev_kfree_skb(skb); + while((skb = skb_dequeue(&card->bch[i].e.R))) + dev_kfree_skb(skb); + } + while((skb = skb_dequeue(&card->sndq))) + dev_kfree_skb(skb); + while((skb = skb_dequeue(&card->rcvq))) + dev_kfree_skb(skb); + while((skb = skb_dequeue(&card->rackq))) + dev_kfree_skb(skb); + while((skb = skb_dequeue(&card->sackq))) + dev_kfree_skb(skb); + while((skb = skb_dequeue(&card->statq))) + dev_kfree_skb(skb); + eicon_clear_msn(card); kfree(card->bch); kfree(card); @@ -1141,9 +1327,8 @@ break; #endif default: - if (DebugVar & 1) - printk(KERN_WARNING - "eicon: addcard: Invalid BUS type %d\n", + printk(KERN_ERR + "eicon: addcard: Invalid BUS type %d\n", p->bus); } } else @@ -1155,7 +1340,7 @@ p = p->next; } else { /* registering failed, remove card from list, free memory */ - printk(KERN_WARNING + printk(KERN_ERR "eicon: Initialization of %s failed\n", p->interface.id); if (q) { @@ -1222,10 +1407,9 @@ printk(KERN_INFO "eicon: No MCA bus, ISDN-interfaces not probed.\n"); } else { - if (DebugVar & 8) - printk(KERN_DEBUG - "eicon_mca_find_card, irq=%d.\n", - irq); + eicon_log(NULL, 8, + "eicon_mca_find_card, irq=%d.\n", + irq); if (!eicon_mca_find_card(0, membase, irq, id)) card_count++; }; @@ -1346,10 +1530,9 @@ { int j, curr_slot = 0; - if (DebugVar & 8) - printk(KERN_DEBUG - "eicon_mca_find_card type: %d, membase: %#x, irq %d \n", - type, membase, irq); + eicon_log(NULL, 8, + "eicon_mca_find_card type: %d, membase: %#x, irq %d \n", + type, membase, irq); /* find a no-driver-assigned eicon card */ for (j=0; eicon_mca_adapters[j].adf_id != 0; j++) { @@ -1414,11 +1597,10 @@ int irq_array1[]={3,4,0,0,2,10,11,12}; adf_pos0 = mca_read_stored_pos(slot,2); - if (DebugVar & 8) - printk(KERN_DEBUG - "eicon_mca_probe irq=%d, membase=%d\n", - irq, - membase); + eicon_log(NULL, 8, + "eicon_mca_probe irq=%d, membase=%d\n", + irq, + membase); switch (a_idx) { case 0: /* P/2-Adapter (== PRI/S2M ? ) */ cards_membase= 0xC0000+((adf_pos0>>4)*0x4000); @@ -1473,7 +1655,7 @@ default: return ENODEV; }; - /* Uebereinstimmung vorgegebener membase & irq */ + /* matching membase & irq */ if ( 1 == eicon_addcard(type, membase, irq, id)) { mca_set_adapter_name(slot, eicon_mca_adapters[a_idx].name); mca_set_adapter_procfn(slot, (MCA_ProcFn) eicon_info, cards); @@ -1486,10 +1668,9 @@ /* reset card */ outb_p(0,cards_io+1); - if (DebugVar & 8) - printk(KERN_INFO "eicon_addcard: successful for slot # %d.\n", + eicon_log(NULL, 8, "eicon_addcard: successful for slot # %d.\n", cards->mca_slot+1); - return 0 ; /* eicon_addcard hat eine Karte zugefuegt */ + return 0 ; /* eicon_addcard added a card */ } else { return ENODEV; }; diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/arcofi.c linux/drivers/isdn/hisax/arcofi.c --- v2.2.13/linux/drivers/isdn/hisax/arcofi.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/hisax/arcofi.c Tue Jan 4 10:12:15 2000 @@ -1,12 +1,19 @@ -/* $Id: arcofi.c,v 1.8 1999/08/25 16:50:51 keil Exp $ +/* $Id: arcofi.c,v 1.10 1999/12/23 15:09:32 keil Exp $ * arcofi.c Ansteuerung ARCOFI 2165 * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * 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) * @@ -83,7 +90,7 @@ if (event == ARCOFI_TIMEOUT) { cs->dc.isac.arcofi_state = ARCOFI_NOP; test_and_set_bit(FLG_ARCOFI_ERROR, &cs->HW_Flags); - wake_up_interruptible(&cs->dc.isac.arcofi_wait); + wake_up(&cs->dc.isac.arcofi_wait); return(1); } switch (cs->dc.isac.arcofi_state) { @@ -109,7 +116,7 @@ del_timer(&cs->dc.isac.arcofitimer); } cs->dc.isac.arcofi_state = ARCOFI_NOP; - wake_up_interruptible(&cs->dc.isac.arcofi_wait); + wake_up(&cs->dc.isac.arcofi_wait); } } } @@ -126,7 +133,7 @@ del_timer(&cs->dc.isac.arcofitimer); } cs->dc.isac.arcofi_state = ARCOFI_NOP; - wake_up_interruptible(&cs->dc.isac.arcofi_wait); + wake_up(&cs->dc.isac.arcofi_wait); } } break; diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/arcofi.h linux/drivers/isdn/hisax/arcofi.h --- v2.2.13/linux/drivers/isdn/hisax/arcofi.h Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/hisax/arcofi.h Tue Jan 4 10:12:15 2000 @@ -1,12 +1,15 @@ -/* $Id: arcofi.h,v 1.4 1999/07/01 08:11:18 keil Exp $ +/* $Id: arcofi.h,v 1.5 1999/12/23 15:09:32 keil Exp $ * arcofi.h Ansteuerung ARCOFI 2165 * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * 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 * diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/asuscom.c linux/drivers/isdn/hisax/asuscom.c --- v2.2.13/linux/drivers/isdn/hisax/asuscom.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/hisax/asuscom.c Tue Jan 4 10:12:15 2000 @@ -1,4 +1,4 @@ -/* $Id: asuscom.c,v 1.8 1999/09/04 06:20:05 keil Exp $ +/* $Id: asuscom.c,v 1.9 1999/12/19 13:09:41 keil Exp $ * asuscom.c low level stuff for ASUSCOM NETWORK INC. ISDNLink cards * @@ -8,6 +8,10 @@ * * * $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() * @@ -42,7 +46,7 @@ extern const char *CardType[]; -const char *Asuscom_revision = "$Revision: 1.8 $"; +const char *Asuscom_revision = "$Revision: 1.9 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -291,13 +295,13 @@ byteout(cs->hw.asus.adr, ASUS_RESET); /* Reset On */ save_flags(flags); sti(); - current->state = TASK_INTERRUPTIBLE; + 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_INTERRUPTIBLE; + 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 -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/avm_pci.c linux/drivers/isdn/hisax/avm_pci.c --- v2.2.13/linux/drivers/isdn/hisax/avm_pci.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/hisax/avm_pci.c Tue Jan 4 10:12:15 2000 @@ -1,4 +1,4 @@ -/* $Id: avm_pci.c,v 1.12 1999/09/04 06:20:05 keil Exp $ +/* $Id: avm_pci.c,v 1.14 1999/12/19 13:09:41 keil Exp $ * avm_pci.c low level stuff for AVM Fritz!PCI and ISA PnP isdn cards * Thanks to AVM, Berlin for informations @@ -7,6 +7,13 @@ * * * $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() * @@ -56,7 +63,7 @@ #include extern const char *CardType[]; -static const char *avm_pci_rev = "$Revision: 1.12 $"; +static const char *avm_pci_rev = "$Revision: 1.14 $"; #define AVM_FRITZ_PCI 1 #define AVM_FRITZ_PNP 2 @@ -269,18 +276,26 @@ int hdlc = bcs->channel; if (cs->debug & L1_DEB_HSCX) - debugl1(cs, "hdlc %c mode %d ichan %d", - 'A' + hdlc, mode, bc); - bcs->mode = mode; - bcs->channel = bc; + debugl1(cs, "hdlc %c mode %d --> %d ichan %d --> %d", + 'A' + hdlc, bcs->mode, mode, hdlc, bc); bcs->hw.hdlc.ctrl.ctrl = 0; switch (mode) { + case (-1): /* used for init */ + bcs->mode = 1; + bcs->channel = bc; + bc = 0; case (L1_MODE_NULL): + if (bcs->mode == L1_MODE_NULL) + return; bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS; write_ctrl(bcs, 5); + bcs->mode = L1_MODE_NULL; + bcs->channel = bc; break; case (L1_MODE_TRANS): + bcs->mode = mode; + bcs->channel = bc; bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS; write_ctrl(bcs, 5); @@ -290,6 +305,8 @@ hdlc_sched_event(bcs, B_XMTBUFREADY); break; case (L1_MODE_HDLC): + bcs->mode = mode; + bcs->channel = bc; bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_ITF_FLG; write_ctrl(bcs, 5); @@ -695,8 +712,8 @@ cs->bcs[1].BC_SetStack = setstack_hdlc; cs->bcs[0].BC_Close = close_hdlcstate; cs->bcs[1].BC_Close = close_hdlcstate; - modehdlc(cs->bcs, 0, 0); - modehdlc(cs->bcs + 1, 0, 0); + modehdlc(cs->bcs, -1, 0); + modehdlc(cs->bcs + 1, -1, 1); } static void @@ -734,11 +751,11 @@ save_flags(flags); sti(); outb(AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER, cs->hw.avm.cfg_reg + 2); - current->state = TASK_INTERRUPTIBLE; + 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_INTERRUPTIBLE; + 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)); } diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/bkm_a4t.c linux/drivers/isdn/hisax/bkm_a4t.c --- v2.2.13/linux/drivers/isdn/hisax/bkm_a4t.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/hisax/bkm_a4t.c Tue Jan 4 10:12:15 2000 @@ -1,4 +1,4 @@ -/* $Id: bkm_a4t.c,v 1.8 1999/09/04 06:20:05 keil Exp $ +/* $Id: bkm_a4t.c,v 1.9 1999/12/19 13:09:41 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 @@ -7,6 +7,10 @@ * 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() * @@ -48,7 +52,7 @@ extern const char *CardType[]; -const char *bkm_a4t_revision = "$Revision: 1.8 $"; +const char *bkm_a4t_revision = "$Revision: 1.9 $"; static inline u_char @@ -231,11 +235,11 @@ sti(); /* Issue the I20 soft reset */ pI20_Regs->i20SysControl = 0xFF; /* all in */ - current->state = TASK_INTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((10 * HZ) / 1000); /* Remove the soft reset */ pI20_Regs->i20SysControl = sysRESET | 0xFF; - current->state = TASK_INTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((10 * HZ) / 1000); /* Set our configuration */ pI20_Regs->i20SysControl = sysRESET | sysCFG; @@ -246,14 +250,14 @@ g_A4T_ISAC_RES | g_A4T_JADE_BOOTR | g_A4T_ISAR_BOOTR; - current->state = TASK_INTERRUPTIBLE; + 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_INTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((10 * HZ) / 1000); restore_flags(flags); } diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/bkm_a8.c linux/drivers/isdn/hisax/bkm_a8.c --- v2.2.13/linux/drivers/isdn/hisax/bkm_a8.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/hisax/bkm_a8.c Tue Jan 4 10:12:15 2000 @@ -1,4 +1,4 @@ -/* $Id: bkm_a8.c,v 1.8 1999/09/04 06:20:05 keil Exp $ +/* $Id: bkm_a8.c,v 1.9 1999/12/19 13:09:41 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 @@ -7,6 +7,10 @@ * 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() * @@ -49,7 +53,7 @@ extern const char *CardType[]; -const char sct_quadro_revision[] = "$Revision: 1.8 $"; +const char sct_quadro_revision[] = "$Revision: 1.9 $"; /* To survive the startup phase */ typedef struct { @@ -298,13 +302,13 @@ save_flags(flags); sti(); - current->state = TASK_INTERRUPTIBLE; + 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_INTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((10 * HZ) / 1000); restore_flags(flags); } diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/callc.c linux/drivers/isdn/hisax/callc.c --- v2.2.13/linux/drivers/isdn/hisax/callc.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/hisax/callc.c Tue Jan 4 10:12:15 2000 @@ -1,4 +1,4 @@ -/* $Id: callc.c,v 2.37 1999/09/20 19:49:47 keil Exp $ +/* $Id: callc.c,v 2.40 1999/12/19 12:59:56 keil Exp $ * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden @@ -11,6 +11,16 @@ * 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 * @@ -157,7 +167,7 @@ #define MOD_USE_COUNT ( GET_USE_COUNT (&__this_module)) #endif /* MODULE */ -const char *lli_revision = "$Revision: 2.37 $"; +const char *lli_revision = "$Revision: 2.40 $"; extern struct IsdnCard cards[]; extern int nrcards; @@ -193,8 +203,7 @@ /* * Find card with given driverId */ -static inline struct IsdnCardState -* +static inline struct IsdnCardState * hisax_findcard(int driverid) { int i; @@ -233,39 +242,39 @@ } enum { - ST_NULL, /* 0 inactive */ - ST_OUT_DIAL, /* 1 outgoing, SETUP send; awaiting confirm */ - ST_IN_WAIT_LL, /* 2 incoming call received; wait for LL confirm */ - ST_IN_ALERT_SENT, /* 3 incoming call received; ALERT send */ - ST_IN_WAIT_CONN_ACK, /* 4 incoming CONNECT send; awaiting CONN_ACK */ - ST_WAIT_BCONN, /* 5 CONNECT/CONN_ACK received, awaiting b-channel prot. estbl. */ - ST_ACTIVE, /* 6 active, b channel prot. established */ - ST_WAIT_BRELEASE, /* 7 call clear. (initiator), awaiting b channel prot. rel. */ - ST_WAIT_BREL_DISC, /* 8 call clear. (receiver), DISCONNECT req. received */ - ST_WAIT_DCOMMAND, /* 9 call clear. (receiver), awaiting DCHANNEL message */ - ST_WAIT_DRELEASE, /* 10 DISCONNECT sent, awaiting RELEASE */ - ST_WAIT_D_REL_CNF, /* 11 RELEASE sent, awaiting RELEASE confirm */ - ST_IN_PROCEED_SEND, /* 12 incoming call, proceeding send */ + ST_NULL, /* 0 inactive */ + ST_OUT_DIAL, /* 1 outgoing, SETUP send; awaiting confirm */ + ST_IN_WAIT_LL, /* 2 incoming call received; wait for LL confirm */ + ST_IN_ALERT_SENT, /* 3 incoming call received; ALERT send */ + ST_IN_WAIT_CONN_ACK, /* 4 incoming CONNECT send; awaiting CONN_ACK */ + ST_WAIT_BCONN, /* 5 CONNECT/CONN_ACK received, awaiting b-channel prot. estbl. */ + ST_ACTIVE, /* 6 active, b channel prot. established */ + ST_WAIT_BRELEASE, /* 7 call clear. (initiator), awaiting b channel prot. rel. */ + ST_WAIT_BREL_DISC, /* 8 call clear. (receiver), DISCONNECT req. received */ + ST_WAIT_DCOMMAND, /* 9 call clear. (receiver), awaiting DCHANNEL message */ + ST_WAIT_DRELEASE, /* 10 DISCONNECT sent, awaiting RELEASE */ + ST_WAIT_D_REL_CNF, /* 11 RELEASE sent, awaiting RELEASE confirm */ + ST_IN_PROCEED_SEND, /* 12 incoming call, proceeding send */ }; #define STATE_COUNT (ST_IN_PROCEED_SEND + 1) - static char *strState[] = - { - "ST_NULL", - "ST_OUT_DIAL", - "ST_IN_WAIT_LL", - "ST_IN_ALERT_SENT", - "ST_IN_WAIT_CONN_ACK", - "ST_WAIT_BCONN", - "ST_ACTIVE", +static char *strState[] = +{ + "ST_NULL", + "ST_OUT_DIAL", + "ST_IN_WAIT_LL", + "ST_IN_ALERT_SENT", + "ST_IN_WAIT_CONN_ACK", + "ST_WAIT_BCONN", + "ST_ACTIVE", "ST_WAIT_BRELEASE", "ST_WAIT_BREL_DISC", "ST_WAIT_DCOMMAND", "ST_WAIT_DRELEASE", "ST_WAIT_D_REL_CNF", - "ST_IN_PROCEED_SEND", + "ST_IN_PROCEED_SEND", }; enum { @@ -327,19 +336,19 @@ static inline void HL_LL(struct Channel *chanp, int command) { - isdn_ctrl ic; + isdn_ctrl ic; - ic.driver = chanp->cs->myid; - ic.command = command; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); + ic.driver = chanp->cs->myid; + ic.command = command; + ic.arg = chanp->chan; + chanp->cs->iif.statcallb(&ic); } static inline void lli_deliver_cause(struct Channel *chanp) { - isdn_ctrl ic; - + isdn_ctrl ic; + if (chanp->proc->para.cause == NO_CAUSE) return; ic.driver = chanp->cs->myid; @@ -357,42 +366,42 @@ static inline void lli_close(struct FsmInst *fi) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; - FsmChangeState(fi, ST_NULL); - chanp->Flags = 0; - chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); + FsmChangeState(fi, ST_NULL); + chanp->Flags = 0; + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); } - static void +static void lli_leased_in(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; + isdn_ctrl ic; + int ret; - isdn_ctrl ic; - int ret; - - chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan); - FsmChangeState(fi, ST_IN_WAIT_LL); - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_ICALL_LEASED"); - ic.driver = chanp->cs->myid; + if (!chanp->leased) + return; + chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan); + FsmChangeState(fi, ST_IN_WAIT_LL); + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_ICALL_LEASED"); + ic.driver = chanp->cs->myid; ic.command = ((chanp->chan < 2) ? ISDN_STAT_ICALL : ISDN_STAT_ICALLW); - ic.arg = chanp->chan; - ic.parm.setup.si1 = 7; - ic.parm.setup.si2 = 0; - ic.parm.setup.plan = 0; - ic.parm.setup.screen = 0; - sprintf(ic.parm.setup.eazmsn,"%d", chanp->chan + 1); - sprintf(ic.parm.setup.phone,"LEASED%d", chanp->cs->myid); - ret = chanp->cs->iif.statcallb(&ic); - if (chanp->debug & 1) - link_debug(chanp, 1, "statcallb ret=%d", ret); - - if (!ret) { - chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); - FsmChangeState(fi, ST_NULL); - } + ic.arg = chanp->chan; + ic.parm.setup.si1 = 7; + ic.parm.setup.si2 = 0; + ic.parm.setup.plan = 0; + ic.parm.setup.screen = 0; + sprintf(ic.parm.setup.eazmsn,"%d", chanp->chan + 1); + sprintf(ic.parm.setup.phone,"LEASED%d", chanp->cs->myid); + ret = chanp->cs->iif.statcallb(&ic); + if (chanp->debug & 1) + link_debug(chanp, 1, "statcallb ret=%d", ret); + if (!ret) { + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); + FsmChangeState(fi, ST_NULL); + } } @@ -402,14 +411,14 @@ static void lli_init_bchan_out(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - - FsmChangeState(fi, ST_WAIT_BCONN); - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_DCONN"); - HL_LL(chanp, ISDN_STAT_DCONN); - init_b_st(chanp, 0); - chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL); + struct Channel *chanp = fi->userdata; + + FsmChangeState(fi, ST_WAIT_BCONN); + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_DCONN"); + HL_LL(chanp, ISDN_STAT_DCONN); + init_b_st(chanp, 0); + chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL); } static void @@ -421,14 +430,13 @@ FsmDelTimer(&chanp->dial_timer, 73); chanp->l2_active_protocol = chanp->l2_protocol; chanp->incoming = 0; - - chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan); - if (chanp->leased) { - lli_init_bchan_out(fi, event, arg); - } else { - FsmChangeState(fi, ST_OUT_DIAL); - chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | REQUEST, chanp); - } + chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan); + if (chanp->leased) { + lli_init_bchan_out(fi, event, arg); + } else { + FsmChangeState(fi, ST_OUT_DIAL); + chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | REQUEST, chanp); + } } static void @@ -436,18 +444,17 @@ { struct Channel *chanp = fi->userdata; - FsmDelTimer(&chanp->drel_timer, 60); - FsmDelTimer(&chanp->dial_timer, 73); - chanp->l2_active_protocol = chanp->l2_protocol; - chanp->incoming = 0; - - chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan); - if (chanp->leased) { - lli_init_bchan_out(fi, event, arg); - } else { - FsmChangeState(fi, ST_OUT_DIAL); - chanp->d_st->lli.l4l3(chanp->d_st, CC_RESUME | REQUEST, chanp); - } + FsmDelTimer(&chanp->drel_timer, 60); + FsmDelTimer(&chanp->dial_timer, 73); + chanp->l2_active_protocol = chanp->l2_protocol; + chanp->incoming = 0; + chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan); + if (chanp->leased) { + lli_init_bchan_out(fi, event, arg); + } else { + FsmChangeState(fi, ST_OUT_DIAL); + chanp->d_st->lli.l4l3(chanp->d_st, CC_RESUME | REQUEST, chanp); + } } static void @@ -515,15 +522,15 @@ FsmChangeState(fi, ST_IN_ALERT_SENT); chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); break; - case 5: /* direct redirect */ - case 4: /* Proceeding desired */ + case 5: /* direct redirect */ + case 4: /* Proceeding desired */ FsmDelTimer(&chanp->drel_timer, 61); FsmChangeState(fi, ST_IN_PROCEED_SEND); - chanp->d_st->lli.l4l3(chanp->d_st, CC_PROCEED_SEND | REQUEST, chanp->proc); - if (ret == 5) - { chanp->setup = ic.parm.setup; - chanp->d_st->lli.l4l3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc); - } + chanp->d_st->lli.l4l3(chanp->d_st, CC_PROCEED_SEND | REQUEST, chanp->proc); + if (ret == 5) { + chanp->setup = ic.parm.setup; + chanp->d_st->lli.l4l3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc); + } break; case 2: /* Rejecting Call */ break; @@ -584,17 +591,17 @@ static void lli_setup_rsp(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - - if (chanp->leased) { - lli_init_bchan_in(fi, event, arg); - } else { - FsmChangeState(fi, ST_IN_WAIT_CONN_ACK); + struct Channel *chanp = fi->userdata; + + if (chanp->leased) { + lli_init_bchan_in(fi, event, arg); + } else { + FsmChangeState(fi, ST_IN_WAIT_CONN_ACK); #ifdef WANT_ALERT - chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); + chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); #endif - chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc); - } + chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc); + } } /* Call suspend */ @@ -610,51 +617,84 @@ /* Call clearing */ static void +lli_leased_hup(struct FsmInst *fi, struct Channel *chanp) +{ + isdn_ctrl ic; + + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_CAUSE; + ic.arg = chanp->chan; + sprintf(ic.parm.num, "L0010"); + chanp->cs->iif.statcallb(&ic); + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_DHUP"); + HL_LL(chanp, ISDN_STAT_DHUP); + lli_close(fi); +} + +static void lli_disconnect_req(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; - FsmChangeState(fi, ST_WAIT_DRELEASE); - chanp->proc->para.cause = 0x10; /* Normal Call Clearing */ - chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc); + if (chanp->leased) { + lli_leased_hup(fi, chanp); + } else { + FsmChangeState(fi, ST_WAIT_DRELEASE); + chanp->proc->para.cause = 0x10; /* Normal Call Clearing */ + chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, + chanp->proc); + } } static void lli_disconnect_reject(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; - FsmChangeState(fi, ST_WAIT_DRELEASE); - chanp->proc->para.cause = 0x15; /* Call Rejected */ - chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc); + if (chanp->leased) { + lli_leased_hup(fi, chanp); + } else { + FsmChangeState(fi, ST_WAIT_DRELEASE); + chanp->proc->para.cause = 0x15; /* Call Rejected */ + chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, + chanp->proc); + } } static void lli_dhup_close(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_DHUP"); - lli_deliver_cause(chanp); - HL_LL(chanp, ISDN_STAT_DHUP); - - lli_close(fi); + if (chanp->leased) { + lli_leased_hup(fi, chanp); + } else { + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_DHUP"); + lli_deliver_cause(chanp); + HL_LL(chanp, ISDN_STAT_DHUP); + lli_close(fi); + } } static void lli_reject_req(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; + if (chanp->leased) { + lli_leased_hup(fi, chanp); + return; + } #ifndef ALERT_REJECT - chanp->proc->para.cause = 0x15; /* Call Rejected */ - chanp->d_st->lli.l4l3(chanp->d_st, CC_REJECT | REQUEST, chanp->proc); - lli_dhup_close(fi, event, arg); + chanp->proc->para.cause = 0x15; /* Call Rejected */ + chanp->d_st->lli.l4l3(chanp->d_st, CC_REJECT | REQUEST, chanp->proc); + lli_dhup_close(fi, event, arg); #else - FsmRestartTimer(&chanp->drel_timer, 40, EV_HANGUP, NULL, 63); - FsmChangeState(fi, ST_IN_ALERT_SENT); - chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); + FsmRestartTimer(&chanp->drel_timer, 40, EV_HANGUP, NULL, 63); + FsmChangeState(fi, ST_IN_ALERT_SENT); + chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); #endif } @@ -672,54 +712,45 @@ lli_start_disc(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; - isdn_ctrl ic; if (chanp->leased) { - ic.command = ISDN_STAT_CAUSE; - ic.arg = chanp->chan; - sprintf(ic.parm.num, "L0010"); - chanp->cs->iif.statcallb(&ic); - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_DHUP"); - HL_LL(chanp, ISDN_STAT_DHUP); - lli_close(fi); + lli_leased_hup(fi, chanp); } else { - lli_disconnect_req(fi, event, arg); + lli_disconnect_req(fi, event, arg); } } static void lli_rel_b_disc(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - - release_b_st(chanp); - lli_start_disc(fi, event, arg); + struct Channel *chanp = fi->userdata; + + release_b_st(chanp); + lli_start_disc(fi, event, arg); } static void lli_bhup_disc(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_BHUP"); - HL_LL(chanp, ISDN_STAT_BHUP); + struct Channel *chanp = fi->userdata; - lli_rel_b_disc(fi, event, arg); + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_BHUP"); + HL_LL(chanp, ISDN_STAT_BHUP); + lli_rel_b_disc(fi, event, arg); } static void lli_bhup_rel_b(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; - FsmChangeState(fi, ST_WAIT_DCOMMAND); - chanp->data_open = 0; - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_BHUP"); - HL_LL(chanp, ISDN_STAT_BHUP); - release_b_st(chanp); + FsmChangeState(fi, ST_WAIT_DCOMMAND); + chanp->data_open = 0; + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_BHUP"); + HL_LL(chanp, ISDN_STAT_BHUP); + release_b_st(chanp); } static void @@ -736,63 +767,65 @@ static void lli_rel_b_dhup(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; - release_b_st(chanp); - lli_dhup_close(fi, event, arg); + release_b_st(chanp); + lli_dhup_close(fi, event, arg); } static void lli_bhup_dhup(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_BHUP"); - HL_LL(chanp, ISDN_STAT_BHUP); - - lli_rel_b_dhup(fi, event, arg); + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_BHUP"); + HL_LL(chanp, ISDN_STAT_BHUP); + lli_rel_b_dhup(fi, event, arg); } static void lli_abort(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; - chanp->data_open = 0; - chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); - - lli_bhup_dhup(fi, event, arg); + chanp->data_open = 0; + chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); + lli_bhup_dhup(fi, event, arg); } static void lli_release_req(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - - FsmChangeState(fi, ST_WAIT_D_REL_CNF); - chanp->d_st->lli.l4l3(chanp->d_st, CC_RELEASE | REQUEST, chanp->proc); + struct Channel *chanp = fi->userdata; + + if (chanp->leased) { + lli_leased_hup(fi, chanp); + } else { + FsmChangeState(fi, ST_WAIT_D_REL_CNF); + chanp->d_st->lli.l4l3(chanp->d_st, CC_RELEASE | REQUEST, + chanp->proc); + } } static void lli_rel_b_release_req(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - - release_b_st(chanp); - lli_release_req(fi, event, arg); + struct Channel *chanp = fi->userdata; + + release_b_st(chanp); + lli_release_req(fi, event, arg); } static void lli_bhup_release_req(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_BHUP"); - HL_LL(chanp, ISDN_STAT_BHUP); + struct Channel *chanp = fi->userdata; - lli_rel_b_release_req(fi, event, arg); + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_BHUP"); + HL_LL(chanp, ISDN_STAT_BHUP); + lli_rel_b_release_req(fi, event, arg); } @@ -819,7 +852,7 @@ if (chanp->debug & 1) link_debug(chanp, 0, "STAT_DHUP"); - HL_LL(chanp, ISDN_STAT_DHUP); + HL_LL(chanp, ISDN_STAT_DHUP); } static void @@ -830,67 +863,65 @@ if (chanp->debug & 1) link_debug(chanp, 0, "STAT_DHUP"); HL_LL(chanp, ISDN_STAT_DHUP); - lli_close(fi); + lli_close(fi); } static void lli_error(struct FsmInst *fi, int event, void *arg) { - FsmChangeState(fi, ST_WAIT_DRELEASE); + FsmChangeState(fi, ST_WAIT_DRELEASE); } static void lli_failure_l(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; + struct Channel *chanp = fi->userdata; + isdn_ctrl ic; - FsmChangeState(fi, ST_NULL); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_CAUSE; - ic.arg = chanp->chan; - sprintf(ic.parm.num, "L%02X%02X", 0, 0x2f); - chanp->cs->iif.statcallb(&ic); - HL_LL(chanp, ISDN_STAT_DHUP); - chanp->Flags = 0; - chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); + FsmChangeState(fi, ST_NULL); + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_CAUSE; + ic.arg = chanp->chan; + sprintf(ic.parm.num, "L%02X%02X", 0, 0x2f); + chanp->cs->iif.statcallb(&ic); + HL_LL(chanp, ISDN_STAT_DHUP); + chanp->Flags = 0; + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); } static void lli_rel_b_fail(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; - release_b_st(chanp); - lli_failure_l(fi, event, arg); + release_b_st(chanp); + lli_failure_l(fi, event, arg); } static void lli_bhup_fail(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_BHUP"); - HL_LL(chanp, ISDN_STAT_BHUP); - - lli_rel_b_fail(fi, event, arg); + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_BHUP"); + HL_LL(chanp, ISDN_STAT_BHUP); + lli_rel_b_fail(fi, event, arg); } static void lli_failure_a(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; - chanp->data_open = 0; - chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); - - lli_bhup_fail(fi, event, arg); + chanp->data_open = 0; + chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); + lli_bhup_fail(fi, event, arg); } - /* *INDENT-OFF* */ - static struct FsmNode fnlist[] HISAX_INITDATA = - { +/* *INDENT-OFF* */ +static struct FsmNode fnlist[] HISAX_INITDATA = +{ {ST_NULL, EV_DIAL, lli_prep_dialout}, {ST_NULL, EV_RESUME, lli_resume}, {ST_NULL, EV_SETUP_IND, lli_deliver_call}, @@ -953,10 +984,9 @@ {ST_WAIT_D_REL_CNF, EV_RELEASE, lli_dhup_close}, {ST_WAIT_D_REL_CNF, EV_DIAL, lli_dchan_not_ready}, }; - /* *INDENT-ON* */ +/* *INDENT-ON* */ - - #define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode)) +#define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode)) HISAX_INITFUNC(void CallcNew(void)) @@ -979,9 +1009,9 @@ { struct PStack *st = chanp->b_st; - if(test_and_clear_bit(FLG_START_B, &chanp->Flags)) { - chanp->bcs->BC_Close(chanp->bcs); - switch (chanp->l2_active_protocol) { + if(test_and_clear_bit(FLG_START_B, &chanp->Flags)) { + chanp->bcs->BC_Close(chanp->bcs); + switch (chanp->l2_active_protocol) { case (ISDN_PROTO_L2_X75I): releasestack_isdnl2(st); break; @@ -989,10 +1019,10 @@ case (ISDN_PROTO_L2_TRANS): case (ISDN_PROTO_L2_MODEM): case (ISDN_PROTO_L2_FAX): - releasestack_transl2(st); - break; - } - } + releasestack_transl2(st); + break; + } + } } struct Channel @@ -1007,10 +1037,10 @@ else i=0; - if (!bch) - { i = 2; /* virtual channel */ - chanp += 2; - } + if (!bch) { + i = 2; /* virtual channel */ + chanp += 2; + } while (i < ((bch) ? cs->chanlimit : (2 + MAX_WAITING_CALLS))) { if (chanp->fi.state == ST_NULL) @@ -1019,17 +1049,17 @@ i++; } - if (bch) /* number of channels is limited */ - { i = 2; /* virtual channel */ - chanp = st->lli.userdata; - chanp += i; - while (i < (2 + MAX_WAITING_CALLS)) { - if (chanp->fi.state == ST_NULL) - return (chanp); - chanp++; - i++; - } - } + if (bch) /* number of channels is limited */ { + i = 2; /* virtual channel */ + chanp = st->lli.userdata; + chanp += i; + while (i < (2 + MAX_WAITING_CALLS)) { + if (chanp->fi.state == ST_NULL) + return (chanp); + chanp++; + i++; + } + } return (NULL); } @@ -1047,19 +1077,19 @@ dchan_l3l4(struct PStack *st, int pr, void *arg) { struct l3_process *pc = arg; - struct IsdnCardState *cs = st->l1.hardware; + struct IsdnCardState *cs = st->l1.hardware; struct Channel *chanp; - if(!pc) - return; - - if (pr == (CC_SETUP | INDICATION)) { - if (!(chanp = selectfreechannel(pc->st, pc->para.bchannel))) { - pc->para.cause = 0x11; /* User busy */ - pc->st->lli.l4l3(pc->st, CC_REJECT | REQUEST, pc); - } else { - chanp->proc = pc; - pc->chan = chanp; + if(!pc) + return; + + if (pr == (CC_SETUP | INDICATION)) { + if (!(chanp = selectfreechannel(pc->st, pc->para.bchannel))) { + pc->para.cause = 0x11; /* User busy */ + pc->st->lli.l4l3(pc->st, CC_REJECT | REQUEST, pc); + } else { + chanp->proc = pc; + pc->chan = chanp; FsmEvent(&chanp->fi, EV_SETUP_IND, NULL); } return; @@ -1115,8 +1145,8 @@ break; case (CC_REDIR | INDICATION): stat_redir_result(cs, chanp->chan, pc->redir_result); - break; - default: + break; + default: if (chanp->debug & 0x800) { HiSax_putstatus(chanp->cs, "Ch", "%d L3->L4 unknown primitiv %#x", @@ -1141,7 +1171,7 @@ (*stp)->l2.l2l1 = dummy_pstack; (*stp)->l2.l2l3 = dummy_pstack; (*stp)->l3.l3l2 = dummy_pstack; - (*stp)->l3.l3ml3 = dummy_pstack; + (*stp)->l3.l3ml3 = dummy_pstack; (*stp)->l3.l3l4 = dummy_pstack; (*stp)->lli.l4l3 = dummy_pstack; (*stp)->ma.layer = dummy_pstack; @@ -1224,8 +1254,8 @@ } int -CallcNewChan(struct IsdnCardState *csta) -{ int i; +CallcNewChan(struct IsdnCardState *csta) { + int i; chancount += 2; init_chan(0, csta); @@ -1234,8 +1264,7 @@ for (i = 0; i < MAX_WAITING_CALLS; i++) init_chan(i+2,csta); - printk(KERN_INFO "HiSax: MAX_WAITING_CALLS added\n"); - + printk(KERN_INFO "HiSax: MAX_WAITING_CALLS added\n"); if (test_bit(FLG_PTP, &csta->channel->d_st->l2.flag)) { printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n"); csta->channel->d_st->lli.l4l3(csta->channel->d_st, @@ -1266,13 +1295,13 @@ for (i = 0; i < 2; i++) { FsmDelTimer(&csta->channel[i].drel_timer, 74); FsmDelTimer(&csta->channel[i].dial_timer, 75); - if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) - release_d_st(csta->channel + i); - if (csta->channel[i].b_st) { - release_b_st(csta->channel + i); - kfree(csta->channel[i].b_st); - csta->channel[i].b_st = NULL; - } else + if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) + release_d_st(csta->channel + i); + if (csta->channel[i].b_st) { + release_b_st(csta->channel + i); + kfree(csta->channel[i].b_st); + csta->channel[i].b_st = NULL; + } else printk(KERN_WARNING "CallcFreeChan b_st ch%d allready freed\n", i); if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) { release_d_st(csta->channel + i); @@ -1305,7 +1334,7 @@ break; default: printk(KERN_WARNING "lldata_handler unknown primitive %#x\n", - pr); + pr); break; } } @@ -1335,7 +1364,7 @@ break; default: printk(KERN_WARNING "lltrans_handler unknown primitive %#x\n", - pr); + pr); break; } } @@ -1438,7 +1467,7 @@ break; default: printk(KERN_WARNING "transd_l4l3 unknown primitive %#x\n", - pr); + pr); break; } } @@ -1478,11 +1507,6 @@ } static void -channel_report(struct Channel *chanp) -{ -} - -static void distr_debug(struct IsdnCardState *csta, int debugflags) { int i; @@ -1515,7 +1539,6 @@ { char *t = tmpbuf; - t += sprintf(tmpbuf, "%d CAPIMSG", chanp->chan); t += QuickHex(t, (u_char *)cm, (cm->Length>50)? 50: cm->Length); t--; *t= 0; @@ -1532,20 +1555,16 @@ return; switch(cm->para[3]) { case 4: /* Suspend */ - if (cm->para[5]) { - strncpy(chanp->setup.phone, &cm->para[5], cm->para[5] +1); - FsmEvent(&chanp->fi, EV_SUSPEND, cm); - } + strncpy(chanp->setup.phone, &cm->para[5], cm->para[5] +1); + FsmEvent(&chanp->fi, EV_SUSPEND, cm); break; case 5: /* Resume */ - if (cm->para[5]) { - strncpy(chanp->setup.phone, &cm->para[5], cm->para[5] +1); - if (chanp->fi.state == ST_NULL) { - FsmEvent(&chanp->fi, EV_RESUME, cm); - } else { - FsmDelTimer(&chanp->dial_timer, 72); - FsmAddTimer(&chanp->dial_timer, 80, EV_RESUME, cm, 73); - } + strncpy(chanp->setup.phone, &cm->para[5], cm->para[5] +1); + if (chanp->fi.state == ST_NULL) { + FsmEvent(&chanp->fi, EV_RESUME, cm); + } else { + FsmDelTimer(&chanp->dial_timer, 72); + FsmAddTimer(&chanp->dial_timer, 80, EV_RESUME, cm, 73); } break; } @@ -1602,7 +1621,7 @@ if (!csta) { printk(KERN_ERR "HiSax: if_command %d called with invalid driverId %d!\n", - ic->command, ic->driver); + ic->command, ic->driver); return -ENODEV; } switch (ic->command) { @@ -1697,9 +1716,8 @@ case (ISDN_CMD_IOCTL): switch (ic->arg) { case (0): - HiSax_reportcard(csta->cardnr); - for (i = 0; i < 2; i++) - channel_report(&csta->channel[i]); + num = *(unsigned int *) ic->parm.num; + HiSax_reportcard(csta->cardnr, num); break; case (1): num = *(unsigned int *) ic->parm.num; @@ -1776,11 +1794,18 @@ num = *(unsigned int *) ic->parm.num; chanp = csta->channel + (num & 1); num = num >>1; - test_and_set_bit(FLG_FIXED_TEI, &chanp->d_st->l2.flag); - chanp->d_st->l2.tei = num; - HiSax_putstatus(csta, "set card ", "in FIXED TEI (%d) mode", num); - printk(KERN_DEBUG "HiSax: set card in FIXED TEI (%d) mode\n", - num); + if (num == 127) { + test_and_clear_bit(FLG_FIXED_TEI, &chanp->d_st->l2.flag); + chanp->d_st->l2.tei = -1; + HiSax_putstatus(csta, "set card ", "in VAR TEI mode"); + printk(KERN_DEBUG "HiSax: set card in VAR TEI mode\n"); + } else { + test_and_set_bit(FLG_FIXED_TEI, &chanp->d_st->l2.flag); + chanp->d_st->l2.tei = num; + HiSax_putstatus(csta, "set card ", "in FIXED TEI (%d) mode", num); + printk(KERN_DEBUG "HiSax: set card in FIXED TEI (%d) mode\n", + num); + } chanp->d_st->lli.l4l3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL); break; @@ -1816,7 +1841,7 @@ if (csta->auxcmd) return(csta->auxcmd(csta, ic)); printk(KERN_DEBUG "HiSax: invalid ioclt %d\n", - (int) ic->arg); + (int) ic->arg); return (-EINVAL); } break; @@ -1844,11 +1869,11 @@ break; /* protocol specific io commands */ - case (ISDN_CMD_PROT_IO): + case (ISDN_CMD_PROT_IO): for (st = csta->stlist; st; st = st->next) if (st->protocol == (ic->arg & 0xFF)) return(st->lli.l4l3_proto(st, ic)); - return(-EINVAL); + return(-EINVAL); break; default: if (csta->auxcmd) @@ -1870,7 +1895,7 @@ if (!csta) { printk(KERN_ERR - "HiSax: if_sendbuf called with invalid driverId!\n"); + "HiSax: if_sendbuf called with invalid driverId!\n"); return -ENODEV; } chanp = csta->channel + chan; diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/config.c linux/drivers/isdn/hisax/config.c --- v2.2.13/linux/drivers/isdn/hisax/config.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/hisax/config.c Tue Jan 4 10:12:15 2000 @@ -1,10 +1,23 @@ -/* $Id: config.c,v 2.37 1999/09/20 12:11:08 keil Exp $ +/* $Id: config.c,v 2.42 1999/12/19 13:09:41 keil Exp $ * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden * * * $Log: config.c,v $ + * Revision 2.42 1999/12/19 13:09:41 keil + * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for + * signal proof delays + * + * 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 * @@ -483,10 +496,10 @@ MODULE_PARM(irq, "1-8i"); MODULE_PARM(mem, "1-8i"); MODULE_PARM(id, "s"); -#ifdef CONFIG_HISAX_16_3 /* For Creatix/Teles PnP */ +#ifdef IO0_IO1 MODULE_PARM(io0, "1-8i"); MODULE_PARM(io1, "1-8i"); -#endif /* CONFIG_HISAX_16_3 */ +#endif /* IO0_IO1 */ #endif /* MODULE */ int nrcards; @@ -519,9 +532,9 @@ printk(KERN_INFO "HiSax: Linux Driver for passive ISDN cards\n"); #ifdef MODULE - printk(KERN_INFO "HiSax: Version 3.3a (module)\n"); + printk(KERN_INFO "HiSax: Version 3.3c (module)\n"); #else - printk(KERN_INFO "HiSax: Version 3.3a (kernel)\n"); + printk(KERN_INFO "HiSax: Version 3.3c (kernel)\n"); #endif strcpy(tmp, l1_revision); printk(KERN_INFO "HiSax: Layer1 Revision %s\n", HiSax_getrev(tmp)); @@ -976,7 +989,7 @@ while (cnt) { cs->cardmsg(cs, CARD_INIT, NULL); sti(); - current->state = TASK_INTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; /* Timeout 10ms */ schedule_timeout((10*HZ)/1000); restore_flags(flags); @@ -1369,12 +1382,9 @@ } void -HiSax_reportcard(int cardnr) +HiSax_reportcard(int cardnr, int sel) { struct IsdnCardState *cs = cards[cardnr].cs; - struct PStack *stptr; - struct l3_process *pc; - int j, i = 1; printk(KERN_DEBUG "HiSax: reportcard No %d\n", cardnr + 1); printk(KERN_DEBUG "HiSax: Type %s\n", CardType[cs->typ]); @@ -1382,45 +1392,33 @@ printk(KERN_DEBUG "HiSax: HiSax_reportcard address 0x%lX\n", (ulong) & HiSax_reportcard); printk(KERN_DEBUG "HiSax: cs 0x%lX\n", (ulong) cs); - printk(KERN_DEBUG "HiSax: HW_Flags %x bc0 flg %x bc0 flg %x\n", + printk(KERN_DEBUG "HiSax: HW_Flags %x bc0 flg %x bc1 flg %x\n", cs->HW_Flags, cs->bcs[0].Flag, cs->bcs[1].Flag); printk(KERN_DEBUG "HiSax: bcs 0 mode %d ch%d\n", cs->bcs[0].mode, cs->bcs[0].channel); printk(KERN_DEBUG "HiSax: bcs 1 mode %d ch%d\n", cs->bcs[1].mode, cs->bcs[1].channel); - printk(KERN_DEBUG "HiSax: cs setstack_d 0x%lX\n", (ulong) cs->setstack_d); - printk(KERN_DEBUG "HiSax: cs stl 0x%lX\n", (ulong) & (cs->stlist)); - stptr = cs->stlist; - while (stptr != NULL) { - printk(KERN_DEBUG "HiSax: dst%d 0x%lX\n", i, (ulong) stptr); - printk(KERN_DEBUG "HiSax: dst%d stp 0x%lX\n", i, (ulong) stptr->l1.stlistp); - printk(KERN_DEBUG "HiSax: dst%d l1.l1hw 0x%lX\n", i, (ulong) stptr->l1.l1hw); - printk(KERN_DEBUG "HiSax: tei %d sapi %d\n", - stptr->l2.tei, stptr->l2.sap); - printk(KERN_DEBUG "HiSax: man 0x%lX\n", (ulong) stptr->ma.layer); - pc = stptr->l3.proc; - while (pc) { - printk(KERN_DEBUG "HiSax: l3proc %x 0x%lX\n", pc->callref, - (ulong) pc); - printk(KERN_DEBUG "HiSax: state %d st 0x%lX chan 0x%lX\n", - pc->state, (ulong) pc->st, (ulong) pc->chan); - pc = pc->next; - } - stptr = stptr->next; - i++; - } - for (j = 0; j < 2; j++) { - printk(KERN_DEBUG "HiSax: ch%d 0x%lX\n", j, - (ulong) & cs->channel[j]); - stptr = cs->channel[j].b_st; - i = 1; - while (stptr != NULL) { - printk(KERN_DEBUG "HiSax: b_st%d 0x%lX\n", i, (ulong) stptr); - printk(KERN_DEBUG "HiSax: man 0x%lX\n", (ulong) stptr->ma.layer); - stptr = stptr->next; - i++; - } +#ifdef ERROR_STATISTIC + printk(KERN_DEBUG "HiSax: dc errors(rx,crc,tx) %d,%d,%d\n", + cs->err_rx, cs->err_crc, cs->err_tx); + printk(KERN_DEBUG "HiSax: bc0 errors(inv,rdo,crc,tx) %d,%d,%d,%d\n", + cs->bcs[0].err_inv, cs->bcs[0].err_rdo, cs->bcs[0].err_crc, cs->bcs[0].err_tx); + printk(KERN_DEBUG "HiSax: bc1 errors(inv,rdo,crc,tx) %d,%d,%d,%d\n", + cs->bcs[1].err_inv, cs->bcs[1].err_rdo, cs->bcs[1].err_crc, cs->bcs[1].err_tx); + if (sel == 99) { + cs->err_rx = 0; + cs->err_crc = 0; + cs->err_tx = 0; + cs->bcs[0].err_inv = 0; + cs->bcs[0].err_rdo = 0; + cs->bcs[0].err_crc = 0; + cs->bcs[0].err_tx = 0; + cs->bcs[1].err_inv = 0; + cs->bcs[1].err_rdo = 0; + cs->bcs[1].err_crc = 0; + cs->bcs[1].err_tx = 0; } +#endif } @@ -1599,9 +1597,7 @@ nrcards = 0; HiSaxVersion(); - if (id) /* If id= string used */ - HiSax_id = id; - /* Initialize all 8 structs, even though we only accept + /* Initialize all structs, even though we only accept two pcmcia cards */ for (i = 0; i < HISAX_MAX_CARDS; i++) { @@ -1650,9 +1646,7 @@ nrcards = 0; HiSaxVersion(); - if (id) /* If id= string used */ - HiSax_id = id; - /* Initialize all 8 structs, even though we only accept + /* Initialize all structs, even though we only accept two pcmcia cards */ for (i = 0; i < HISAX_MAX_CARDS; i++) { @@ -1701,12 +1695,10 @@ nrcards = 0; HiSaxVersion(); - if (id) /* If id= string used */ - HiSax_id = id; - /* Initialize all 16 structs, even though we only accept + /* Initialize all structs, even though we only accept two pcmcia cards */ - for (i = 0; i < 16; i++) { + 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]; diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/diva.c linux/drivers/isdn/hisax/diva.c --- v2.2.13/linux/drivers/isdn/hisax/diva.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/hisax/diva.c Tue Jan 4 10:12:15 2000 @@ -1,4 +1,4 @@ -/* $Id: diva.c,v 1.17 1999/09/04 06:20:06 keil Exp $ +/* $Id: diva.c,v 1.18 1999/12/19 13:09:41 keil Exp $ * diva.c low level stuff for Eicon.Diehl Diva Family ISDN cards * @@ -12,6 +12,10 @@ * * * $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() * @@ -80,7 +84,7 @@ extern const char *CardType[]; -const char *Diva_revision = "$Revision: 1.17 $"; +const char *Diva_revision = "$Revision: 1.18 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -750,30 +754,30 @@ sti(); if (cs->subtyp == DIVA_IPAC_ISA) { writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x20); - current->state = TASK_INTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((10*HZ)/1000); writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x00); - current->state = TASK_INTERRUPTIBLE; + 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_INTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((10*HZ)/1000); *ireg = PITA_PARA_MPX_MODE; - current->state = TASK_INTERRUPTIBLE; + 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_INTERRUPTIBLE; + 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_INTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((10*HZ)/1000); if (cs->subtyp == DIVA_ISA) cs->hw.diva.ctrl_reg |= DIVA_ISA_LED_A; diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/elsa.c linux/drivers/isdn/hisax/elsa.c --- v2.2.13/linux/drivers/isdn/hisax/elsa.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/hisax/elsa.c Tue Jan 4 10:12:15 2000 @@ -1,4 +1,4 @@ -/* $Id: elsa.c,v 2.19 1999/09/04 06:20:06 keil Exp $ +/* $Id: elsa.c,v 2.20 1999/12/19 13:09:42 keil Exp $ * elsa.c low level stuff for Elsa isdn cards * @@ -14,6 +14,10 @@ * 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() * @@ -99,7 +103,7 @@ extern const char *CardType[]; -const char *Elsa_revision = "$Revision: 2.19 $"; +const char *Elsa_revision = "$Revision: 2.20 $"; const char *Elsa_Types[] = {"None", "PC", "PCC-8", "PCC-16", "PCF", "PCF-Pro", "PCMCIA", "QS 1000", "QS 3000", "QS 1000 PCI", "QS 3000 PCI", @@ -578,10 +582,10 @@ save_flags(flags); sti(); writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x20); - current->state = TASK_INTERRUPTIBLE; + 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_INTERRUPTIBLE; + 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); @@ -785,7 +789,7 @@ cs->hw.elsa.status |= ELSA_TIMER_AKTIV; byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); byteout(cs->hw.elsa.timer, 0); - current->state = TASK_INTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((110*HZ)/1000); restore_flags(flags); cs->hw.elsa.ctrl_reg &= ~ELSA_ENA_TIMER_INT; diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/fsm.c linux/drivers/isdn/hisax/fsm.c --- v2.2.13/linux/drivers/isdn/hisax/fsm.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/hisax/fsm.c Tue Jan 4 10:12:15 2000 @@ -1,12 +1,15 @@ -/* $Id: fsm.c,v 1.10 1998/11/15 23:54:39 keil Exp $ +/* $Id: fsm.c,v 1.11 1999/12/23 15:09:32 keil Exp $ - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * 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 * diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/hfc_2bds0.c linux/drivers/isdn/hisax/hfc_2bds0.c --- v2.2.13/linux/drivers/isdn/hisax/hfc_2bds0.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/hisax/hfc_2bds0.c Tue Jan 4 10:12:15 2000 @@ -1,11 +1,17 @@ -/* $Id: hfc_2bds0.c,v 1.9 1999/07/01 08:11:35 keil Exp $ +/* $Id: hfc_2bds0.c,v 1.11 1999/12/23 15:09:32 keil Exp $ * * specific routines for CCD's HFC 2BDS0 * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * 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 * @@ -260,6 +266,9 @@ if (cs->debug & L1_DEB_WARN) debugl1(cs, "hfc_empty_fifo: incoming packet too small"); cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel); +#ifdef ERROR_STATISTIC + bcs->err_inv++; +#endif cli(); while ((idx++ < count) && WaitNoBusy(cs)) ReadReg(cs, HFCD_DATA_NODEB, cip); @@ -302,6 +311,9 @@ debugl1(cs, "FIFO CRC error"); dev_kfree_skb(skb); skb = NULL; +#ifdef ERROR_STATISTIC + bcs->err_crc++; +#endif } } } @@ -741,6 +753,9 @@ printk(KERN_WARNING "HFC DFIFO channel BUSY Error\n"); dev_kfree_skb(skb); skb = NULL; +#ifdef ERROR_STATISTIC + cs->err_rx++; +#endif } else { cli(); WaitNoBusy(cs); @@ -757,6 +772,9 @@ debugl1(cs, "FIFO CRC error"); dev_kfree_skb(skb); skb = NULL; +#ifdef ERROR_STATISTIC + cs->err_crc++; +#endif } else { skb_queue_tail(&cs->rq, skb); sched_event_D(cs, D_RCVBUFREADY); diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/hfc_2bds0.h linux/drivers/isdn/hisax/hfc_2bds0.h --- v2.2.13/linux/drivers/isdn/hisax/hfc_2bds0.h Wed Apr 1 16:20:58 1998 +++ linux/drivers/isdn/hisax/hfc_2bds0.h Tue Jan 4 10:12:15 2000 @@ -1,11 +1,14 @@ -/* $Id: hfc_2bds0.h,v 1.2 1998/02/02 13:26:15 keil Exp $ +/* $Id: hfc_2bds0.h,v 1.3 1999/12/23 15:09:32 keil Exp $ * specific defines for CCD's HFC 2BDS0 * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * 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 * diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/hfc_2bs0.c linux/drivers/isdn/hisax/hfc_2bs0.c --- v2.2.13/linux/drivers/isdn/hisax/hfc_2bs0.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/hisax/hfc_2bs0.c Tue Jan 4 10:12:15 2000 @@ -1,4 +1,4 @@ -/* $Id: hfc_2bs0.c,v 1.9 1999/07/01 08:11:36 keil Exp $ +/* $Id: hfc_2bs0.c,v 1.12 1999/12/19 14:17:12 keil Exp $ * specific routines for CCD's HFC 2BS0 * @@ -6,6 +6,16 @@ * * * $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 * @@ -207,7 +217,7 @@ WaitForBusy(cs); return (NULL); } - if (count < 4) { + if ((count < 4) && (bcs->mode != L1_MODE_TRANS)) { if (cs->debug & L1_DEB_WARN) debugl1(cs, "hfc_empty_fifo: incoming packet too small"); cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel); @@ -217,46 +227,60 @@ stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC | HFC_CHANNEL(bcs->channel)); WaitForBusy(cs); +#ifdef ERROR_STATISTIC + bcs->err_inv++; +#endif return (NULL); } - if (!(skb = dev_alloc_skb(count - 3))) + if (bcs->mode == L1_MODE_TRANS) + count -= 1; + else + count -= 3; + if (!(skb = dev_alloc_skb(count))) printk(KERN_WARNING "HFC: receive out of memory\n"); else { - ptr = skb_put(skb, count - 3); + 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)) { *ptr++ = cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip); idx++; } - if (idx != count - 3) { + if (idx != count) { debugl1(cs, "RFIFO BUSY error"); printk(KERN_WARNING "HFC FIFO channel %d BUSY Error\n", bcs->channel); dev_kfree_skb(skb); - WaitNoBusy(cs); - stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC | - HFC_CHANNEL(bcs->channel)); - WaitForBusy(cs); + if (bcs->mode != L1_MODE_TRANS) { + WaitNoBusy(cs); + stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC | + HFC_CHANNEL(bcs->channel)); + WaitForBusy(cs); + } return (NULL); } - WaitNoBusy(cs); - chksum = (cs->BC_Read_Reg(cs, HFC_DATA, cip) << 8); - WaitNoBusy(cs); - chksum += cs->BC_Read_Reg(cs, HFC_DATA, cip); - WaitNoBusy(cs); - stat = cs->BC_Read_Reg(cs, HFC_DATA, cip); - if (cs->debug & L1_DEB_HSCX) - debugl1(cs, "hfc_empty_fifo %d chksum %x stat %x", - bcs->channel, chksum, stat); - if (stat) { - debugl1(cs, "FIFO CRC error"); - dev_kfree_skb(skb); - skb = NULL; + if (bcs->mode != L1_MODE_TRANS) { + WaitNoBusy(cs); + chksum = (cs->BC_Read_Reg(cs, HFC_DATA, cip) << 8); + WaitNoBusy(cs); + chksum += cs->BC_Read_Reg(cs, HFC_DATA, cip); + WaitNoBusy(cs); + stat = cs->BC_Read_Reg(cs, HFC_DATA, cip); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_empty_fifo %d chksum %x stat %x", + bcs->channel, chksum, stat); + if (stat) { + debugl1(cs, "FIFO CRC error"); + dev_kfree_skb(skb); + skb = NULL; +#ifdef ERROR_STATISTIC + bcs->err_crc++; +#endif + } + WaitNoBusy(cs); + stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC | + HFC_CHANNEL(bcs->channel)); + WaitForBusy(cs); } - WaitNoBusy(cs); - stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC | - HFC_CHANNEL(bcs->channel)); - WaitForBusy(cs); } return (skb); } @@ -268,6 +292,7 @@ long flags; int idx, fcnt; int count; + int z1, z2; u_char cip; if (!bcs->tx_skb) @@ -279,29 +304,39 @@ cli(); cip = HFC_CIP | HFC_F1 | HFC_SEND | HFC_CHANNEL(bcs->channel); if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) { - cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip); - WaitForBusy(cs); + cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip); + WaitForBusy(cs); } WaitNoBusy(cs); - bcs->hw.hfc.f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip); - cip = HFC_CIP | HFC_F2 | HFC_SEND | HFC_CHANNEL(bcs->channel); - WaitNoBusy(cs); - bcs->hw.hfc.f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip); - bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(bcs, HFC_Z1 | HFC_SEND | HFC_CHANNEL(bcs->channel)); - if (cs->debug & L1_DEB_HSCX) - debugl1(cs, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)", - bcs->channel, bcs->hw.hfc.f1, bcs->hw.hfc.f2, - bcs->hw.hfc.send[bcs->hw.hfc.f1]); - fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2; - if (fcnt < 0) - fcnt += 32; - if (fcnt > 30) { - if (cs->debug & L1_DEB_HSCX) - debugl1(cs, "hfc_fill_fifo more as 30 frames"); - restore_flags(flags); - return; - } - count = GetFreeFifoBytes(bcs); + if (bcs->mode != L1_MODE_TRANS) { + bcs->hw.hfc.f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip); + cip = HFC_CIP | HFC_F2 | HFC_SEND | HFC_CHANNEL(bcs->channel); + WaitNoBusy(cs); + bcs->hw.hfc.f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip); + bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(bcs, HFC_Z1 | HFC_SEND | HFC_CHANNEL(bcs->channel)); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)", + bcs->channel, bcs->hw.hfc.f1, bcs->hw.hfc.f2, + bcs->hw.hfc.send[bcs->hw.hfc.f1]); + fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2; + if (fcnt < 0) + fcnt += 32; + if (fcnt > 30) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_fill_fifo more as 30 frames"); + restore_flags(flags); + return; + } + count = GetFreeFifoBytes(bcs); + } + else { + WaitForBusy(cs); + z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel)); + z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel)); + count = z1 - z2; + if (count < 0) + count += cs->hw.hfc.fifosize; + } /* L1_MODE_TRANS */ if (cs->debug & L1_DEB_HSCX) debugl1(cs, "hfc_fill_fifo %d count(%ld/%d)", bcs->channel, bcs->tx_skb->len, @@ -326,9 +361,11 @@ count = -1; dev_kfree_skb(bcs->tx_skb); bcs->tx_skb = NULL; - WaitForBusy(cs); - WaitNoBusy(cs); - cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F1_INC | HFC_SEND | HFC_CHANNEL(bcs->channel)); + if (bcs->mode != L1_MODE_TRANS) { + WaitForBusy(cs); + WaitNoBusy(cs); + cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F1_INC | HFC_SEND | HFC_CHANNEL(bcs->channel)); + } if (bcs->st->lli.l1writewakeup && (count >= 0)) bcs->st->lli.l1writewakeup(bcs->st, count); test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); @@ -357,32 +394,39 @@ WaitForBusy(cs); } WaitNoBusy(cs); - f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip); - cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel); - WaitNoBusy(cs); - f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip); - if (f1 != f2) { - if (cs->debug & L1_DEB_HSCX) - debugl1(cs, "hfc rec %d f1(%d) f2(%d)", - bcs->channel, f1, f2); + receive = 0; + if (bcs->mode == L1_MODE_HDLC) { + f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip); + cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel); + WaitNoBusy(cs); + f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip); + if (f1 != f2) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc rec %d f1(%d) f2(%d)", + bcs->channel, f1, f2); + receive = 1; + } + } + if (receive || (bcs->mode == L1_MODE_TRANS)) { WaitForBusy(cs); z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel)); z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel)); rcnt = z1 - z2; if (rcnt < 0) rcnt += cs->hw.hfc.fifosize; - rcnt++; - if (cs->debug & L1_DEB_HSCX) - debugl1(cs, "hfc rec %d z1(%x) z2(%x) cnt(%d)", - bcs->channel, z1, z2, rcnt); -/* sti(); */ - if ((skb = hfc_empty_fifo(bcs, rcnt))) { - skb_queue_tail(&bcs->rqueue, skb); - hfc_sched_event(bcs, B_RCVBUFREADY); + if ((bcs->mode == L1_MODE_HDLC) || (rcnt)) { + rcnt++; + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc rec %d z1(%x) z2(%x) cnt(%d)", + bcs->channel, z1, z2, rcnt); + /* sti(); */ + if ((skb = hfc_empty_fifo(bcs, rcnt))) { + skb_queue_tail(&bcs->rqueue, skb); + hfc_sched_event(bcs, B_RCVBUFREADY); + } } receive = 1; - } else - receive = 0; + } restore_flags(flags); udelay(1); cli(); @@ -423,12 +467,19 @@ switch (mode) { case (L1_MODE_NULL): - if (bc) + if (bc) { + cs->hw.hfc.ctmt &= ~1; cs->hw.hfc.isac_spcr &= ~0x03; - else + } + else { + cs->hw.hfc.ctmt &= ~2; cs->hw.hfc.isac_spcr &= ~0x0c; + } break; case (L1_MODE_TRANS): + cs->hw.hfc.ctmt &= ~(1 << bc); /* set HDLC mode */ + cs->BC_Write_Reg(cs, HFC_STATUS, cs->hw.hfc.ctmt, cs->hw.hfc.ctmt); + hfc_clear_fifo(bcs); /* complete fifo clear */ if (bc) { cs->hw.hfc.ctmt |= 1; cs->hw.hfc.isac_spcr &= ~0x03; @@ -453,7 +504,7 @@ } cs->BC_Write_Reg(cs, HFC_STATUS, cs->hw.hfc.ctmt, cs->hw.hfc.ctmt); cs->writeisac(cs, ISAC_SPCR, cs->hw.hfc.isac_spcr); - if (mode) + if (mode == L1_MODE_HDLC) hfc_clear_fifo(bcs); } diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/hfc_2bs0.h linux/drivers/isdn/hisax/hfc_2bs0.h --- v2.2.13/linux/drivers/isdn/hisax/hfc_2bs0.h Wed Apr 1 16:20:58 1998 +++ linux/drivers/isdn/hisax/hfc_2bs0.h Tue Jan 4 10:12:16 2000 @@ -1,11 +1,14 @@ -/* $Id: hfc_2bs0.h,v 1.1 1997/09/11 17:31:34 keil Exp $ +/* $Id: hfc_2bs0.h,v 1.2 1999/12/23 15:09:32 keil Exp $ * specific defines for CCD's HFC 2BS0 * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * 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 * diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/hfc_pci.c linux/drivers/isdn/hisax/hfc_pci.c --- v2.2.13/linux/drivers/isdn/hisax/hfc_pci.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/hisax/hfc_pci.c Tue Jan 4 10:12:16 2000 @@ -1,4 +1,4 @@ -/* $Id: hfc_pci.c,v 1.20 1999/09/07 06:18:55 werner Exp $ +/* $Id: hfc_pci.c,v 1.25 1999/12/19 13:09:42 keil Exp $ * hfc_pci.c low level driver for CCD´s hfc-pci based cards * @@ -23,6 +23,25 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: hfc_pci.c,v $ + * 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 @@ -103,52 +122,40 @@ extern const char *CardType[]; -static const char *hfcpci_revision = "$Revision: 1.20 $"; +static const char *hfcpci_revision = "$Revision: 1.25 $"; /* table entry in the PCI devices list */ typedef struct { - int vendor_id; - int device_id; - char *vendor_name; - char *card_name; - } PCI_ENTRY; - -static const PCI_ENTRY id_list[] = { - {0x1397,0x2BD0,"CCD/Billion/Asuscom","2BD0"}, - {0x1397,0xB000,"Billion","B000"}, - {0x1397,0xB006,"Billion","B006"}, - {0x1397,0xB007,"Billion","B007"}, - {0x1397,0xB008,"Billion","B008"}, - {0x1397,0xB009,"Billion","B009"}, - {0x1397,0xB00A,"Billion","B00A"}, - {0x1397,0xB00B,"Billion","B00B"}, - {0x1397,0xB00C,"Billion","B00C"}, - {0x1043,0x0675,"Asuscom/Askey","675"}, - {0x0871,0xFFA2,"German telekom","T-Concept"}, - {0x0871,0xFFA1,"German telekom","A1T"}, - {0x1051,0x0100,"Motorola MC145575","MC145575"}, - {0x1397,0xB100,"Seyeon","B100"}, - {0x15B0,0x2BD0,"Zoltrix","2BD0"}, - {0,0,NULL,NULL}, + int vendor_id; + int device_id; + char *vendor_name; + char *card_name; +} PCI_ENTRY; + +#define NT_T1_COUNT 20 /* number of 3.125ms interrupts for G2 timeout */ + +static const PCI_ENTRY id_list[] = +{ + {0x1397, 0x2BD0, "CCD/Billion/Asuscom", "2BD0"}, + {0x1397, 0xB000, "Billion", "B000"}, + {0x1397, 0xB006, "Billion", "B006"}, + {0x1397, 0xB007, "Billion", "B007"}, + {0x1397, 0xB008, "Billion", "B008"}, + {0x1397, 0xB009, "Billion", "B009"}, + {0x1397, 0xB00A, "Billion", "B00A"}, + {0x1397, 0xB00B, "Billion", "B00B"}, + {0x1397, 0xB00C, "Billion", "B00C"}, + {0x1043, 0x0675, "Asuscom/Askey", "675"}, + {0x0871, 0xFFA2, "German telekom", "T-Concept"}, + {0x0871, 0xFFA1, "German telekom", "A1T"}, + {0x1051, 0x0100, "Motorola MC145575", "MC145575"}, + {0x1397, 0xB100, "Seyeon", "B100"}, + {0x15B0, 0x2BD0, "Zoltrix", "2BD0"}, + {0, 0, NULL, NULL}, }; #if CONFIG_PCI -/*****************************/ -/* release D- and B-channels */ -/*****************************/ -void -releasehfcpci(struct IsdnCardState *cs) -{ - if (cs->bcs[0].hw.hfc.send) { - kfree(cs->bcs[0].hw.hfc.send); - cs->bcs[0].hw.hfc.send = NULL; - } - if (cs->bcs[1].hw.hfc.send) { - kfree(cs->bcs[1].hw.hfc.send); - cs->bcs[1].hw.hfc.send = NULL; - } -} /******************************************/ /* free hardware resources used by driver */ @@ -156,10 +163,21 @@ void release_io_hfcpci(struct IsdnCardState *cs) { + int flags; + + save_flags(flags); + cli(); + cs->hw.hfcpci.int_m2 = 0; /* interrupt output off ! */ + Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2); + restore_flags(flags); + Write_hfc(cs, HFCPCI_CIRM, HFCPCI_RESET); /* Reset On */ + sti(); + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout((30 * HZ) / 1000); /* Timeout 30ms */ + Write_hfc(cs, HFCPCI_CIRM, 0); /* Reset Off */ #if CONFIG_PCI - pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, 0); /* disabe memory mapped ports + busmaster */ -#endif /* CONFIG_PCI */ - releasehfcpci(cs); + pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, 0); /* disable memory mapped ports + busmaster */ +#endif /* CONFIG_PCI */ del_timer(&cs->hw.hfcpci.timer); kfree(cs->hw.hfcpci.share_start); cs->hw.hfcpci.share_start = NULL; @@ -175,16 +193,20 @@ { long flags; + save_flags(flags); + cli(); pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, PCI_ENA_MEMIO); /* enable memory mapped ports, disable busmaster */ + cs->hw.hfcpci.int_m2 = 0; /* interrupt output off ! */ + Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2); + printk(KERN_INFO "HFC_PCI: resetting card\n"); pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, PCI_ENA_MEMIO + PCI_ENA_MASTER); /* enable memory ports + busmaster */ Write_hfc(cs, HFCPCI_CIRM, HFCPCI_RESET); /* Reset On */ - save_flags(flags); sti(); - current->state = TASK_INTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((30 * HZ) / 1000); /* Timeout 30ms */ Write_hfc(cs, HFCPCI_CIRM, 0); /* Reset Off */ - current->state = TASK_INTERRUPTIBLE; + 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"); @@ -192,25 +214,23 @@ cs->hw.hfcpci.fifo_en = 0x30; /* only D fifos enabled */ Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en); - cs->hw.hfcpci.trm = 0 + HFCPCI_BTRANS_THRESMASK; /* no echo connect , threshold */ + 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 */ cs->hw.hfcpci.sctrl_e = HFCPCI_AUTO_AWAKE; Write_hfc(cs, HFCPCI_SCTRL_E, cs->hw.hfcpci.sctrl_e); /* S/T Auto awake */ - cs->hw.hfcpci.bswapped = 0; /* no exchange */ + cs->hw.hfcpci.bswapped = 0; /* no exchange */ + cs->hw.hfcpci.nt_mode = 0; /* we are in TE mode */ cs->hw.hfcpci.ctmt = HFCPCI_TIM3_125 | HFCPCI_AUTO_TIMER; Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt); - cs->hw.hfcpci.int_m2 = HFCPCI_IRQ_ENABLE; - cs->hw.hfcpci.int_m1 = HFCPCI_INTS_DTRANS | HFCPCI_INTS_DREC | - HFCPCI_INTS_L1STATE | HFCPCI_CLTIMER; + cs->hw.hfcpci.int_m1 = HFCPCI_INTS_DTRANS | HFCPCI_INTS_DREC | + HFCPCI_INTS_L1STATE | HFCPCI_INTS_TIMER; Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1); - Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2); /* Clear already pending ints */ if (Read_hfc(cs, HFCPCI_INT_S1)); - if (Read_hfc(cs, HFCPCI_INT_S2)); Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 2); /* HFC ST 2 */ udelay(10); @@ -218,24 +238,29 @@ cs->hw.hfcpci.mst_m = HFCPCI_MASTER; /* HFC Master Mode */ Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m); - cs->hw.hfcpci.sctrl = 0x40; /* set tx_lo mode, error in datasheet ! */ + cs->hw.hfcpci.sctrl = 0x40; /* set tx_lo mode, error in datasheet ! */ Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl); cs->hw.hfcpci.sctrl_r = 0; Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.sctrl_r); - /* Init GCI/IOM2 in master mode */ + /* Init GCI/IOM2 in master mode */ /* Slots 0 and 1 are set for B-chan 1 and 2 */ /* D- and monitor/CI channel are not enabled */ /* STIO1 is used as output for data, B1+B2 from ST->IOM+HFC */ - /* STIO2 is used as data input, B1+B2 from IOM->ST */ + /* STIO2 is used as data input, B1+B2 from IOM->ST */ /* ST B-channel send disabled -> continous 1s */ /* The IOM slots are always enabled */ - cs->hw.hfcpci.conn = 0x36; /* set data flow directions */ + cs->hw.hfcpci.conn = 0x36; /* set data flow directions */ Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn); - Write_hfc(cs, HFCPCI_B1_SSL, 0x80); /* B1-Slot 0 STIO1 out enabled */ - Write_hfc(cs, HFCPCI_B2_SSL, 0x81); /* B2-Slot 1 STIO1 out enabled */ - Write_hfc(cs, HFCPCI_B1_RSL, 0x80); /* B1-Slot 0 STIO2 in enabled */ - Write_hfc(cs, HFCPCI_B2_RSL, 0x81); /* B2-Slot 1 STIO2 in enabled */ + Write_hfc(cs, HFCPCI_B1_SSL, 0x80); /* B1-Slot 0 STIO1 out enabled */ + Write_hfc(cs, HFCPCI_B2_SSL, 0x81); /* B2-Slot 1 STIO1 out enabled */ + Write_hfc(cs, HFCPCI_B1_RSL, 0x80); /* B1-Slot 0 STIO2 in enabled */ + Write_hfc(cs, HFCPCI_B2_RSL, 0x81); /* B2-Slot 1 STIO2 in enabled */ + + /* Finally enable IRQ output */ + cs->hw.hfcpci.int_m2 = HFCPCI_IRQ_ENABLE; + Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2); + if (Read_hfc(cs, HFCPCI_INT_S2)); restore_flags(flags); } @@ -326,7 +351,7 @@ count -= 3; ptr = skb_put(skb, count); - if (zp->z2 + count <= B_FIFO_SIZE + B_SUB_VAL) + if (zp->z2 + count <= B_FIFO_SIZE + B_SUB_VAL) maxlen = count; /* complete transfer */ else maxlen = B_FIFO_SIZE + B_SUB_VAL - zp->z2; /* maximum */ @@ -418,54 +443,56 @@ /*******************************************************************************/ /* check for transparent receive data and read max one threshold size if avail */ /*******************************************************************************/ -int hfcpci_empty_fifo_trans(struct BCState *bcs, bzfifo_type *bz, u_char *bdata) -{ unsigned short *z1r, *z2r; - int new_z2, fcnt, maxlen; - struct sk_buff *skb; - u_char *ptr, *ptr1; - - z1r = &bz->za[MAX_B_FRAMES].z1; /* pointer to z reg */ - z2r = z1r + 1; - - if (!(fcnt = *z1r - *z2r)) - return(0); /* no data avail */ - - if (fcnt <= 0) - fcnt += B_FIFO_SIZE; /* bytes actually buffered */ - if (fcnt > HFCPCI_BTRANS_THRESHOLD) - fcnt = HFCPCI_BTRANS_THRESHOLD; /* limit size */ - - new_z2 = *z2r + fcnt; /* new position in fifo */ - if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) - new_z2 -= B_FIFO_SIZE; /* buffer wrap */ +int +hfcpci_empty_fifo_trans(struct BCState *bcs, bzfifo_type * bz, u_char * bdata) +{ + unsigned short *z1r, *z2r; + int new_z2, fcnt, maxlen; + struct sk_buff *skb; + u_char *ptr, *ptr1; + + z1r = &bz->za[MAX_B_FRAMES].z1; /* pointer to z reg */ + z2r = z1r + 1; + + if (!(fcnt = *z1r - *z2r)) + return (0); /* no data avail */ + + if (fcnt <= 0) + fcnt += B_FIFO_SIZE; /* bytes actually buffered */ + if (fcnt > HFCPCI_BTRANS_THRESHOLD) + fcnt = HFCPCI_BTRANS_THRESHOLD; /* limit size */ + + new_z2 = *z2r + fcnt; /* new position in fifo */ + if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z2 -= B_FIFO_SIZE; /* buffer wrap */ - if (!(skb = dev_alloc_skb(fcnt))) + if (!(skb = dev_alloc_skb(fcnt))) printk(KERN_WARNING "HFCPCI: receive out of memory\n"); - else { - ptr = skb_put(skb, fcnt); - if (*z2r + fcnt <= B_FIFO_SIZE + B_SUB_VAL) - maxlen = fcnt; /* complete transfer */ - else - maxlen = B_FIFO_SIZE + B_SUB_VAL - *z2r; /* maximum */ - - ptr1 = bdata + (*z2r - B_SUB_VAL); /* start of data */ - memcpy(ptr, ptr1, maxlen); /* copy data */ - fcnt -= maxlen; - - if (fcnt) { /* rest remaining */ - ptr += maxlen; - ptr1 = bdata; /* start of buffer */ - memcpy(ptr, ptr1, fcnt); /* rest */ - } - cli(); - skb_queue_tail(&bcs->rqueue, skb); - sti(); - hfcpci_sched_event(bcs, B_RCVBUFREADY); - } - - *z2r = new_z2; /* new position */ - return(1); -} /* hfcpci_empty_fifo_trans */ + else { + ptr = skb_put(skb, fcnt); + if (*z2r + fcnt <= B_FIFO_SIZE + B_SUB_VAL) + maxlen = fcnt; /* complete transfer */ + else + maxlen = B_FIFO_SIZE + B_SUB_VAL - *z2r; /* maximum */ + + ptr1 = bdata + (*z2r - B_SUB_VAL); /* start of data */ + memcpy(ptr, ptr1, maxlen); /* copy data */ + fcnt -= maxlen; + + if (fcnt) { /* rest remaining */ + ptr += maxlen; + ptr1 = bdata; /* start of buffer */ + memcpy(ptr, ptr1, fcnt); /* rest */ + } + cli(); + skb_queue_tail(&bcs->rqueue, skb); + sti(); + hfcpci_sched_event(bcs, B_RCVBUFREADY); + } + + *z2r = new_z2; /* new position */ + return (1); +} /* hfcpci_empty_fifo_trans */ /**********************************/ /* B-channel main receive routine */ @@ -526,11 +553,10 @@ receive = 1; else receive = 0; - } else - if (bcs->mode == L1_MODE_TRANS) - receive = hfcpci_empty_fifo_trans(bcs, bz, bdata); - else - receive = 0; + } else if (bcs->mode == L1_MODE_TRANS) + receive = hfcpci_empty_fifo_trans(bcs, bz, bdata); + else + receive = 0; test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); if (count && receive) goto Begin; @@ -622,7 +648,7 @@ bzfifo_type *bz; u_char *bdata; u_char new_f1, *src, *dst; - unsigned short *z1t, *z2t; + unsigned short *z1t, *z2t; if (!bcs->tx_skb) return; @@ -641,57 +667,53 @@ } if (bcs->mode == L1_MODE_TRANS) { - z1t = &bz->za[MAX_B_FRAMES].z1; - z2t = z1t + 1; - if (cs->debug & L1_DEB_HSCX) - debugl1(cs, "hfcpci_fill_fifo_trans %d z1(%x) z2(%x)", - bcs->channel, *z1t, *z2t); - fcnt = *z2t - *z1t; - if (fcnt <= 0) - fcnt += B_FIFO_SIZE; /* fcnt contains available bytes in fifo */ - fcnt = B_FIFO_SIZE - fcnt; /* remaining bytes to send */ - - while ((fcnt < 2 * HFCPCI_BTRANS_THRESHOLD) && (bcs->tx_skb)) { - if (bcs->tx_skb->len < B_FIFO_SIZE - fcnt) { - /* data is suitable for fifo */ - count = bcs->tx_skb->len; - - new_z1 = *z1t + count; /* new buffer Position */ - if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) - new_z1 -= B_FIFO_SIZE; /* buffer wrap */ - src = bcs->tx_skb->data; /* source pointer */ - dst = bdata + (*z1t - B_SUB_VAL); - maxlen = (B_FIFO_SIZE + B_SUB_VAL) - *z1t; /* end of fifo */ - if (maxlen > count) - maxlen = count; /* limit size */ - memcpy(dst, src, maxlen); /* first copy */ + z1t = &bz->za[MAX_B_FRAMES].z1; + z2t = z1t + 1; + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfcpci_fill_fifo_trans %d z1(%x) z2(%x)", + bcs->channel, *z1t, *z2t); + fcnt = *z2t - *z1t; + if (fcnt <= 0) + fcnt += B_FIFO_SIZE; /* fcnt contains available bytes in fifo */ + fcnt = B_FIFO_SIZE - fcnt; /* remaining bytes to send */ + + while ((fcnt < 2 * HFCPCI_BTRANS_THRESHOLD) && (bcs->tx_skb)) { + if (bcs->tx_skb->len < B_FIFO_SIZE - fcnt) { + /* data is suitable for fifo */ + count = bcs->tx_skb->len; + + new_z1 = *z1t + count; /* new buffer Position */ + if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z1 -= B_FIFO_SIZE; /* buffer wrap */ + src = bcs->tx_skb->data; /* source pointer */ + dst = bdata + (*z1t - B_SUB_VAL); + maxlen = (B_FIFO_SIZE + B_SUB_VAL) - *z1t; /* end of fifo */ + if (maxlen > count) + maxlen = count; /* limit size */ + memcpy(dst, src, maxlen); /* first copy */ + + count -= maxlen; /* remaining bytes */ + if (count) { + dst = bdata; /* start of buffer */ + src += maxlen; /* new position */ + memcpy(dst, src, count); + } + bcs->tx_cnt -= bcs->tx_skb->len; + fcnt += bcs->tx_skb->len; + *z1t = new_z1; /* now send data */ + } else if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfcpci_fill_fifo_trans %d frame length %d discarded", + bcs->channel, bcs->tx_skb->len); - count -= maxlen; /* remaining bytes */ - if (count) { - dst = bdata; /* start of buffer */ - src += maxlen; /* new position */ - memcpy(dst, src, count); - } - bcs->tx_cnt -= bcs->tx_skb->len; - fcnt += bcs->tx_skb->len; - *z1t = new_z1; /* now send data */ - } - else - if (cs->debug & L1_DEB_HSCX) - debugl1(cs, "hfcpci_fill_fifo_trans %d frame length %d discarded", - bcs->channel, bcs->tx_skb->len); - - dev_kfree_skb(bcs->tx_skb); - cli(); - bcs->tx_skb = skb_dequeue(&bcs->squeue); /* fetch next data */ - sti(); - } - test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); - restore_flags(flags); - return; + dev_kfree_skb(bcs->tx_skb); + cli(); + bcs->tx_skb = skb_dequeue(&bcs->squeue); /* fetch next data */ + sti(); + } + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + restore_flags(flags); + return; } - - if (cs->debug & L1_DEB_HSCX) debugl1(cs, "hfcpci_fill_fifo_hdlc %d f1(%d) f2(%d) z1(f1)(%x)", bcs->channel, bz->f1, bz->f2, @@ -757,51 +779,107 @@ return; } +/**********************************************/ +/* D-channel l1 state call for leased NT-mode */ +/**********************************************/ +static void +dch_nt_l2l1(struct PStack *st, int pr, void *arg) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; + + switch (pr) { + case (PH_DATA | REQUEST): + case (PH_PULL | REQUEST): + case (PH_PULL | INDICATION): + st->l1.l1hw(st, pr, arg); + break; + case (PH_ACTIVATE | REQUEST): + st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL); + break; + case (PH_TESTLOOP | REQUEST): + if (1 & (long) arg) + debugl1(cs, "PH_TEST_LOOP B1"); + if (2 & (long) arg) + debugl1(cs, "PH_TEST_LOOP B2"); + if (!(3 & (long) arg)) + debugl1(cs, "PH_TEST_LOOP DISABLED"); + st->l1.l1hw(st, HW_TESTLOOP | REQUEST, arg); + break; + default: + if (cs->debug) + debugl1(cs, "dch_nt_l2l1 msg %04X unhandled", pr); + break; + } +} + + + /***********************/ /* set/reset echo mode */ -/***********************/ +/***********************/ static int -hfcpci_auxcmd(struct IsdnCardState *cs, isdn_ctrl *ic) +hfcpci_auxcmd(struct IsdnCardState *cs, isdn_ctrl * ic) { - int flags; - int i = *(unsigned int *) ic->parm.num; - - if (cs->chanlimit > 1) - return(-EINVAL); - - save_flags(flags); - cli(); - if (i) { - cs->logecho = 1; - cs->hw.hfcpci.trm |= 0x20; /* enable echo chan */ - cs->hw.hfcpci.int_m1 |= HFCPCI_INTS_B2REC; - cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2RX; - } - else { - cs->logecho = 0; - cs->hw.hfcpci.trm &= ~0x20; /* enable echo chan */ - cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_B2REC; - cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2RX; - } - cs->hw.hfcpci.sctrl_r &= ~SCTRL_B2_ENA; - cs->hw.hfcpci.sctrl &= ~SCTRL_B2_ENA; - cs->hw.hfcpci.conn |= 0x10; /* B2-IOM -> B2-ST */ - cs->hw.hfcpci.ctmt &= ~2; - Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt); - Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.sctrl_r); - Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl); - Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn); - Write_hfc(cs, HFCPCI_TRM, cs->hw.hfcpci.trm); - Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en); - Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1); - restore_flags(flags); - return(0); -} /* hfcpci_auxcmd */ + int flags; + int i = *(unsigned int *) ic->parm.num; + + if ((ic->arg == 98) && + (!(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_STATES, HFCPCI_LOAD_STATE | 0); /* HFC ST G0 */ + udelay(10); + cs->hw.hfcpci.sctrl |= SCTRL_MODE_NT; + Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl); /* set NT-mode */ + udelay(10); + Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 1); /* HFC ST G1 */ + udelay(10); + Write_hfc(cs, HFCPCI_STATES, 1 | HFCPCI_ACTIVATE | HFCPCI_DO_ACTION); + cs->dc.hfcpci.ph_state = 1; + cs->hw.hfcpci.nt_mode = 1; + cs->hw.hfcpci.nt_timer = 0; + cs->stlist->l2.l2l1 = dch_nt_l2l1; + restore_flags(flags); + debugl1(cs, "NT mode activated"); + return (0); + } + if ((cs->chanlimit > 1) || (cs->hw.hfcpci.bswapped) || + (cs->hw.hfcpci.nt_mode) || (ic->arg != 12)) + return (-EINVAL); + + save_flags(flags); + cli(); + if (i) { + cs->logecho = 1; + cs->hw.hfcpci.trm |= 0x20; /* enable echo chan */ + cs->hw.hfcpci.int_m1 |= HFCPCI_INTS_B2REC; + cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2RX; + } else { + cs->logecho = 0; + cs->hw.hfcpci.trm &= ~0x20; /* disable echo chan */ + cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_B2REC; + cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2RX; + } + cs->hw.hfcpci.sctrl_r &= ~SCTRL_B2_ENA; + cs->hw.hfcpci.sctrl &= ~SCTRL_B2_ENA; + cs->hw.hfcpci.conn |= 0x10; /* B2-IOM -> B2-ST */ + cs->hw.hfcpci.ctmt &= ~2; + Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt); + Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.sctrl_r); + Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl); + Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn); + Write_hfc(cs, HFCPCI_TRM, cs->hw.hfcpci.trm); + Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en); + Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1); + restore_flags(flags); + return (0); +} /* hfcpci_auxcmd */ /*****************************/ /* E-channel receive routine */ /*****************************/ -static void receive_emsg(struct IsdnCardState *cs) +static void +receive_emsg(struct IsdnCardState *cs) { long flags; int rcnt; @@ -838,55 +916,54 @@ if (cs->debug & L1_DEB_ISAC) debugl1(cs, "hfcpci e_rec z1(%x) z2(%x) cnt(%d)", zp->z1, zp->z2, rcnt); - new_z2 = zp->z2 + rcnt; /* new position in fifo */ + new_z2 = zp->z2 + rcnt; /* new position in fifo */ if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) - new_z2 -= B_FIFO_SIZE; /* buffer wrap */ + new_z2 -= B_FIFO_SIZE; /* buffer wrap */ new_f2 = (bz->f2 + 1) & MAX_B_FRAMES; - if ((rcnt > 256 + 3) || (count < 4) || + if ((rcnt > 256 + 3) || (count < 4) || (*(bdata + (zp->z1 - B_SUB_VAL)))) { - if (cs->debug & L1_DEB_WARN) - debugl1(cs, "hfcpci_empty_echan: incoming packet invalid length %d or crc", rcnt); - bz->za[new_f2].z2 = new_z2; - bz->f2 = new_f2; /* next buffer */ + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hfcpci_empty_echan: incoming packet invalid length %d or crc", rcnt); + bz->za[new_f2].z2 = new_z2; + bz->f2 = new_f2; /* next buffer */ } else { - total = rcnt; - rcnt -= 3; - ptr = e_buffer; - - if (zp->z2 <= B_FIFO_SIZE + B_SUB_VAL) - maxlen = rcnt; /* complete transfer */ - else - maxlen = B_FIFO_SIZE + B_SUB_VAL - zp->z2; /* maximum */ + total = rcnt; + rcnt -= 3; + ptr = e_buffer; - ptr1 = bdata + (zp->z2 - B_SUB_VAL); /* start of data */ - memcpy(ptr, ptr1, maxlen); /* copy data */ - rcnt -= maxlen; + if (zp->z2 <= B_FIFO_SIZE + B_SUB_VAL) + maxlen = rcnt; /* complete transfer */ + else + maxlen = B_FIFO_SIZE + B_SUB_VAL - zp->z2; /* maximum */ - if (rcnt) { /* rest remaining */ - ptr += maxlen; - ptr1 = bdata; /* start of buffer */ - memcpy(ptr, ptr1, rcnt); /* rest */ - } - bz->za[new_f2].z2 = new_z2; - bz->f2 = new_f2; /* next buffer */ - if (cs->debug & DEB_DLOG_HEX) { - ptr = cs->dlog; - if ((total - 3) < MAX_DLOG_SPACE / 3 - 10) { - *ptr++ = 'E'; - *ptr++ = 'C'; - *ptr++ = 'H'; - *ptr++ = 'O'; - *ptr++ = ':'; - ptr += QuickHex(ptr, e_buffer, total - 3); - ptr--; - *ptr++ = '\n'; - *ptr = 0; - HiSax_putstatus(cs, NULL, cs->dlog); - } else - HiSax_putstatus(cs, "LogEcho: ", "warning Frame too big (%d)", total - 3); - } + ptr1 = bdata + (zp->z2 - B_SUB_VAL); /* start of data */ + memcpy(ptr, ptr1, maxlen); /* copy data */ + rcnt -= maxlen; - } + if (rcnt) { /* rest remaining */ + ptr += maxlen; + ptr1 = bdata; /* start of buffer */ + memcpy(ptr, ptr1, rcnt); /* rest */ + } + bz->za[new_f2].z2 = new_z2; + bz->f2 = new_f2; /* next buffer */ + if (cs->debug & DEB_DLOG_HEX) { + ptr = cs->dlog; + if ((total - 3) < MAX_DLOG_SPACE / 3 - 10) { + *ptr++ = 'E'; + *ptr++ = 'C'; + *ptr++ = 'H'; + *ptr++ = 'O'; + *ptr++ = ':'; + ptr += QuickHex(ptr, e_buffer, total - 3); + ptr--; + *ptr++ = '\n'; + *ptr = 0; + HiSax_putstatus(cs, NULL, cs->dlog); + } else + HiSax_putstatus(cs, "LogEcho: ", "warning Frame too big (%d)", total - 3); + } + } rcnt = bz->f1 - bz->f2; if (rcnt < 0) @@ -902,7 +979,7 @@ goto Begin; restore_flags(flags); return; -} /* receive_emsg */ +} /* receive_emsg */ /*********************/ /* Interrupt handler */ @@ -921,6 +998,9 @@ printk(KERN_WARNING "HFC-PCI: Spurious interrupt!\n"); return; } + if (!(cs->hw.hfcpci.int_m2 & 0x08)) + return; /* not initialised */ + if (HFCPCI_ANYINT & (stat = Read_hfc(cs, HFCPCI_STATUS))) { val = Read_hfc(cs, HFCPCI_INT_S1); if (cs->debug & L1_DEB_ISAC) @@ -933,7 +1013,7 @@ test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags) ? "locked" : "unlocked"); val &= cs->hw.hfcpci.int_m1; - if (val & 0x40) { /* TE state machine irq */ + if (val & 0x40) { /* state machine irq */ exval = Read_hfc(cs, HFCPCI_STATES) & 0xf; if (cs->debug & L1_DEB_ISAC) debugl1(cs, "ph_state chg %d->%d", cs->dc.hfcpci.ph_state, @@ -942,6 +1022,14 @@ sched_event_D_pci(cs, D_L1STATECHANGE); val &= ~0x40; } + if (val & 0x80) { /* timer irq */ + if (cs->hw.hfcpci.nt_mode) { + if ((--cs->hw.hfcpci.nt_timer) < 0) + sched_event_D_pci(cs, D_L1STATECHANGE); + } + val &= ~0x80; + Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt | HFCPCI_CLTIMER); + } while (val) { save_flags(flags); cli(); @@ -956,24 +1044,23 @@ cs->hw.hfcpci.int_s1 = exval; } if (val & 0x08) { - if (!(bcs = Sel_BCS(cs, cs->hw.hfcpci.bswapped ? 1:0))) { + if (!(bcs = Sel_BCS(cs, cs->hw.hfcpci.bswapped ? 1 : 0))) { if (cs->debug) debugl1(cs, "hfcpci spurious 0x08 IRQ"); } else main_rec_hfcpci(bcs); } - if (val & 0x10) { - if (cs->logecho) - receive_emsg(cs); - else - if (!(bcs = Sel_BCS(cs, 1))) { + if (val & 0x10) { + if (cs->logecho) + receive_emsg(cs); + else if (!(bcs = Sel_BCS(cs, 1))) { if (cs->debug) debugl1(cs, "hfcpci spurious 0x10 IRQ"); } else main_rec_hfcpci(bcs); } if (val & 0x01) { - if (!(bcs = Sel_BCS(cs, cs->hw.hfcpci.bswapped ? 1:0))) { + if (!(bcs = Sel_BCS(cs, cs->hw.hfcpci.bswapped ? 1 : 0))) { if (cs->debug) debugl1(cs, "hfcpci spurious 0x01 IRQ"); } else { @@ -1082,6 +1169,7 @@ { struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; struct sk_buff *skb = arg; + int flags; switch (pr) { case (PH_DATA | REQUEST): @@ -1157,13 +1245,46 @@ Write_hfc(cs, HFCPCI_STATES, HFCPCI_ACTIVATE | HFCPCI_DO_ACTION); break; case (HW_DEACTIVATE | REQUEST): - cs->hw.hfcpci.mst_m &= ~HFCPCI_MASTER; + cs->hw.hfcpci.mst_m &= ~HFCPCI_MASTER; Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m); break; case (HW_INFO3 | REQUEST): cs->hw.hfcpci.mst_m |= HFCPCI_MASTER; Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m); break; + case (HW_TESTLOOP | REQUEST): + switch ((int) arg) { + case (1): + Write_hfc(cs, HFCPCI_B1_SSL, 0x80); /* tx slot */ + Write_hfc(cs, HFCPCI_B1_RSL, 0x80); /* rx slot */ + save_flags(flags); + cli(); + cs->hw.hfcpci.conn = (cs->hw.hfcpci.conn & ~7) | 1; + Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn); + restore_flags(flags); + break; + + case (2): + Write_hfc(cs, HFCPCI_B2_SSL, 0x81); /* tx slot */ + Write_hfc(cs, HFCPCI_B2_RSL, 0x81); /* rx slot */ + save_flags(flags); + cli(); + cs->hw.hfcpci.conn = (cs->hw.hfcpci.conn & ~0x38) | 0x08; + Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn); + restore_flags(flags); + break; + + default: + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hfcpci_l1hw loop invalid %4x", (int) arg); + return; + } + save_flags(flags); + cli(); + cs->hw.hfcpci.trm |= 0x80; /* enable IOM-loop */ + Write_hfc(cs, HFCPCI_TRM, cs->hw.hfcpci.trm); + restore_flags(flags); + break; default: if (cs->debug & L1_DEB_WARN) debugl1(cs, "hfcpci_l1hw unknown pr %4x", pr); @@ -1203,113 +1324,127 @@ { struct IsdnCardState *cs = bcs->cs; bzfifo_type *bzr, *bzt; - int flags; + int flags, fifo2; if (cs->debug & L1_DEB_HSCX) debugl1(cs, "HFCPCI bchannel mode %d bchan %d/%d", mode, bc, bcs->channel); bcs->mode = mode; bcs->channel = bc; - if (cs->chanlimit > 1) { - cs->hw.hfcpci.bswapped = 0; /* B1 and B2 normal mode */ - cs->hw.hfcpci.sctrl_e &= ~0x80; - } - else { - if (bc) { - cs->hw.hfcpci.bswapped = 1; /* B1 and B2 exchanged */ - cs->hw.hfcpci.sctrl_e |= 0x80; - bc = 0; /* B1 controller used */ - } - else { - cs->hw.hfcpci.bswapped = 0; /* B1 and B2 normal mode */ - cs->hw.hfcpci.sctrl_e &= ~0x80; - } - } + fifo2 = bc; save_flags(flags); cli(); + if (cs->chanlimit > 1) { + cs->hw.hfcpci.bswapped = 0; /* B1 and B2 normal mode */ + cs->hw.hfcpci.sctrl_e &= ~0x80; + } else { + if (bc) { + if (mode != L1_MODE_NULL) { + cs->hw.hfcpci.bswapped = 1; /* B1 and B2 exchanged */ + cs->hw.hfcpci.sctrl_e |= 0x80; + } else { + cs->hw.hfcpci.bswapped = 0; /* B1 and B2 normal mode */ + cs->hw.hfcpci.sctrl_e &= ~0x80; + } + fifo2 = 0; + } else { + cs->hw.hfcpci.bswapped = 0; /* B1 and B2 normal mode */ + cs->hw.hfcpci.sctrl_e &= ~0x80; + } + } switch (mode) { case (L1_MODE_NULL): if (bc) { cs->hw.hfcpci.sctrl &= ~SCTRL_B2_ENA; cs->hw.hfcpci.sctrl_r &= ~SCTRL_B2_ENA; - cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2; - cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B2TRANS+HFCPCI_INTS_B2REC); } else { cs->hw.hfcpci.sctrl &= ~SCTRL_B1_ENA; cs->hw.hfcpci.sctrl_r &= ~SCTRL_B1_ENA; + } + if (fifo2) { + cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2; + cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC); + } else { cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B1; - cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B1TRANS+HFCPCI_INTS_B1REC); + cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC); } break; case (L1_MODE_TRANS): if (bc) { - cs->hw.hfcpci.ctmt |= 2; - cs->hw.hfcpci.conn &= ~0x18; cs->hw.hfcpci.sctrl |= SCTRL_B2_ENA; cs->hw.hfcpci.sctrl_r |= SCTRL_B2_ENA; - cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2; - cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B2TRANS+HFCPCI_INTS_B2REC); - bzr = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b2; - bzt = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b2; } else { - cs->hw.hfcpci.ctmt |= 1; - cs->hw.hfcpci.conn &= ~0x03; cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA; cs->hw.hfcpci.sctrl_r |= SCTRL_B1_ENA; + } + if (fifo2) { + cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2; + cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC); + cs->hw.hfcpci.ctmt |= 2; + cs->hw.hfcpci.conn &= ~0x18; + bzr = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b2; + bzt = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b2; + } else { cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B1; - cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B1TRANS+HFCPCI_INTS_B1REC); - bzr = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b1; - bzt = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b1; + cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC); + cs->hw.hfcpci.ctmt |= 1; + cs->hw.hfcpci.conn &= ~0x03; + bzr = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b1; + bzt = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b1; } bzr->za[MAX_B_FRAMES].z1 = B_FIFO_SIZE + B_SUB_VAL - 1; - bzr->za[MAX_B_FRAMES].z2 = bzr->za[MAX_B_FRAMES].z1; + bzr->za[MAX_B_FRAMES].z2 = bzr->za[MAX_B_FRAMES].z1; bzr->f1 = MAX_B_FRAMES; - bzr->f2 = bzr->f1; /* init F pointers to remain constant */ + bzr->f2 = bzr->f1; /* init F pointers to remain constant */ bzt->za[MAX_B_FRAMES].z1 = B_FIFO_SIZE + B_SUB_VAL - 1; - bzt->za[MAX_B_FRAMES].z2 = bzt->za[MAX_B_FRAMES].z1; + bzt->za[MAX_B_FRAMES].z2 = bzt->za[MAX_B_FRAMES].z1; bzt->f1 = MAX_B_FRAMES; - bzt->f2 = bzt->f1; /* init F pointers to remain constant */ + bzt->f2 = bzt->f1; /* init F pointers to remain constant */ break; case (L1_MODE_HDLC): if (bc) { - cs->hw.hfcpci.ctmt &= ~2; - cs->hw.hfcpci.conn &= ~0x18; cs->hw.hfcpci.sctrl |= SCTRL_B2_ENA; cs->hw.hfcpci.sctrl_r |= SCTRL_B2_ENA; - cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2; - cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B2TRANS+HFCPCI_INTS_B2REC); } else { - cs->hw.hfcpci.ctmt &= ~1; - cs->hw.hfcpci.conn &= ~0x3; cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA; cs->hw.hfcpci.sctrl_r |= SCTRL_B1_ENA; + } + if (fifo2) { + cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2; + cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC); + cs->hw.hfcpci.ctmt &= ~2; + cs->hw.hfcpci.conn &= ~0x18; + } else { cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B1; - cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B1TRANS+HFCPCI_INTS_B1REC); + cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC); + cs->hw.hfcpci.ctmt &= ~1; + cs->hw.hfcpci.conn &= ~0x03; } break; case (L1_MODE_EXTRN): if (bc) { - cs->hw.hfcpci.conn |= 0x10; + cs->hw.hfcpci.conn |= 0x10; cs->hw.hfcpci.sctrl |= SCTRL_B2_ENA; cs->hw.hfcpci.sctrl_r |= SCTRL_B2_ENA; cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2; - cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B2TRANS+HFCPCI_INTS_B2REC); + cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC); } else { - cs->hw.hfcpci.conn |= 0x02; + cs->hw.hfcpci.conn |= 0x02; cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA; cs->hw.hfcpci.sctrl_r |= SCTRL_B1_ENA; cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B1; - cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B1TRANS+HFCPCI_INTS_B1REC); + cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC); } break; } + Write_hfc(cs, HFCPCI_SCTRL_E, cs->hw.hfcpci.sctrl_e); Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1); - restore_flags(flags); Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en); Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl); Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.sctrl_r); Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt); Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn); + restore_flags(flags); } /******************************/ @@ -1429,29 +1564,72 @@ static void hfcpci_bh(struct IsdnCardState *cs) { + int flags; /* struct PStack *stptr; */ if (!cs) return; if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) { - switch (cs->dc.hfcpci.ph_state) { - case (0): - l1_msg(cs, HW_RESET | INDICATION, NULL); - break; - case (3): - l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL); - break; - case (8): - l1_msg(cs, HW_RSYNC | INDICATION, NULL); - break; - case (6): - l1_msg(cs, HW_INFO2 | INDICATION, NULL); - break; - case (7): - l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL); - break; - default: - break; + if (!cs->hw.hfcpci.nt_mode) + switch (cs->dc.hfcpci.ph_state) { + case (0): + l1_msg(cs, HW_RESET | INDICATION, NULL); + break; + case (3): + l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL); + break; + case (8): + l1_msg(cs, HW_RSYNC | INDICATION, NULL); + break; + case (6): + l1_msg(cs, HW_INFO2 | INDICATION, NULL); + break; + case (7): + l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL); + break; + default: + break; + } else { + switch (cs->dc.hfcpci.ph_state) { + case (2): + save_flags(flags); + cli(); + if (cs->hw.hfcpci.nt_timer < 0) { + cs->hw.hfcpci.nt_timer = 0; + cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1); + /* Clear already pending ints */ + if (Read_hfc(cs, HFCPCI_INT_S1)); + + Write_hfc(cs, HFCPCI_STATES, 4 | HFCPCI_LOAD_STATE); + udelay(10); + Write_hfc(cs, HFCPCI_STATES, 4); + cs->dc.hfcpci.ph_state = 4; + } else { + cs->hw.hfcpci.int_m1 |= HFCPCI_INTS_TIMER; + Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1); + cs->hw.hfcpci.ctmt &= ~HFCPCI_AUTO_TIMER; + cs->hw.hfcpci.ctmt |= HFCPCI_TIM3_125; + Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt | HFCPCI_CLTIMER); + Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt | HFCPCI_CLTIMER); + cs->hw.hfcpci.nt_timer = NT_T1_COUNT; + Write_hfc(cs, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3); /* allow G2 -> G3 transition */ + } + restore_flags(flags); + break; + case (1): + case (3): + case (4): + save_flags(flags); + cli(); + cs->hw.hfcpci.nt_timer = 0; + cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1); + restore_flags(flags); + break; + default: + break; + } } } if (test_and_clear_bit(D_RCVBUFREADY, &cs->event)) @@ -1461,24 +1639,6 @@ } -/*************************************/ -/* Alloc memory send data for queues */ -/*************************************/ -__initfunc(unsigned int - *init_send_hfcpci(int cnt)) -{ - int i, *send; - - if (!(send = kmalloc(cnt * sizeof(unsigned int), GFP_ATOMIC))) { - printk(KERN_WARNING - "HiSax: No memory for hfcpci.send\n"); - return (NULL); - } - for (i = 0; i < cnt; i++) - send[i] = 0x1fff; - return (send); -} - /********************************/ /* called for card init message */ /********************************/ @@ -1490,10 +1650,6 @@ cs->dbusytimer.data = (long) cs; init_timer(&cs->dbusytimer); cs->tqueue.routine = (void *) (void *) hfcpci_bh; - if (!cs->bcs[0].hw.hfc.send) - cs->bcs[0].hw.hfc.send = init_send_hfcpci(32); - if (!cs->bcs[1].hw.hfc.send) - cs->bcs[1].hw.hfc.send = init_send_hfcpci(32); cs->BC_Send_Data = &hfcpci_send_data; cs->bcs[0].BC_SetStack = setstack_2b; cs->bcs[1].BC_SetStack = setstack_2b; @@ -1526,7 +1682,7 @@ inithfcpci(cs); save_flags(flags); sti(); - current->state = TASK_INTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((80 * HZ) / 1000); /* Timeout 80ms */ /* now switch timer interrupt off */ cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_TIMER; @@ -1551,16 +1707,15 @@ setup_hfcpci(struct IsdnCard *card)) { struct IsdnCardState *cs = card->cs; + unsigned short cmd; char tmp[64]; int i; - struct pci_dev *tmp_hfcpci = NULL; + struct pci_dev *tmp_hfcpci = NULL; strcpy(tmp, hfcpci_revision); printk(KERN_INFO "HiSax: HFC-PCI driver Rev. %s\n", HiSax_getrev(tmp)); #if CONFIG_PCI cs->hw.hfcpci.int_s1 = 0; - cs->bcs[0].hw.hfc.send = NULL; - cs->bcs[1].hw.hfc.send = NULL; cs->dc.hfcpci.ph_state = 0; cs->hw.hfcpci.fifo = 255; if (cs->typ == ISDN_CTYPE_HFC_PCI) { @@ -1569,22 +1724,22 @@ return (0); } i = 0; - while (id_list[i].vendor_id) { - tmp_hfcpci = pci_find_device(id_list[i].vendor_id, - id_list[i].device_id, - dev_hfcpci); - i++; - if (tmp_hfcpci) { - if ((card->para[0]) && (card->para[0] != (tmp_hfcpci->base_address[0] & PCI_BASE_ADDRESS_IO_MASK))) - continue; - else - break; - } - } - + while (id_list[i].vendor_id) { + tmp_hfcpci = pci_find_device(id_list[i].vendor_id, + id_list[i].device_id, + dev_hfcpci); + i++; + if (tmp_hfcpci) { + if ((card->para[0]) && (card->para[0] != (tmp_hfcpci->base_address[ 0] & PCI_BASE_ADDRESS_IO_MASK))) + continue; + else + break; + } + } + if (tmp_hfcpci) { - i--; - dev_hfcpci = tmp_hfcpci; /* old device */ + i--; + dev_hfcpci = tmp_hfcpci; /* old device */ cs->hw.hfcpci.pci_bus = dev_hfcpci->bus->number; cs->hw.hfcpci.pci_device_fn = dev_hfcpci->devfn; cs->irq = dev_hfcpci->irq; @@ -1593,11 +1748,44 @@ return (0); } cs->hw.hfcpci.pci_io = (char *) dev_hfcpci->base_address[ 1]; - printk(KERN_INFO "HiSax: HFC-PCI card manufacturer: %s card name: %s\n",id_list[i].vendor_name,id_list[i].card_name); + printk(KERN_INFO "HiSax: HFC-PCI card manufacturer: %s card name: %s\n", id_list[i].vendor_name, id_list[i].card_name); } else { printk(KERN_WARNING "HFC-PCI: No PCI card found\n"); return (0); } + 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, + cs->hw.hfcpci.pci_device_fn, + PCI_COMMAND, + 0x0103); /* set SERR */ + pcibios_read_config_word(cs->hw.hfcpci.pci_bus, + cs->hw.hfcpci.pci_device_fn, + PCI_COMMAND, + &cmd); + pcibios_write_config_word(cs->hw.hfcpci.pci_bus, + cs->hw.hfcpci.pci_device_fn, + PCI_COMMAND, + cmd & ~2); + (int) cs->hw.hfcpci.pci_io &= ~(PAGE_SIZE - 1); + pcibios_write_config_dword(cs->hw.hfcpci.pci_bus, + cs->hw.hfcpci.pci_device_fn, + PCI_BASE_ADDRESS_1, + (int) cs->hw.hfcpci.pci_io); + pcibios_write_config_word(cs->hw.hfcpci.pci_bus, + cs->hw.hfcpci.pci_device_fn, + PCI_COMMAND, + cmd); + pcibios_read_config_dword(cs->hw.hfcpci.pci_bus, + cs->hw.hfcpci.pci_device_fn, + PCI_BASE_ADDRESS_1, + (void *) &cs->hw.hfcpci.pci_io); + if (((int) cs->hw.hfcpci.pci_io & (PAGE_SIZE - 1))) { + printk(KERN_WARNING "HFC-PCI unable to align address %x\n", (unsigned) cs->hw.hfcpci.pci_io); + return (0); + } + dev_hfcpci->base_address[1] = (int) cs->hw.hfcpci.pci_io; + } if (!cs->hw.hfcpci.pci_io) { printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n"); return (0); @@ -1613,16 +1801,16 @@ (((ulong) cs->hw.hfcpci.share_start) & ~0x7FFF) + 0x8000; pcibios_write_config_dword(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, 0x80, - (u_int) virt_to_bus(cs->hw.hfcpci.fifos)); + (u_int) virt_to_bus(cs->hw.hfcpci.fifos)); cs->hw.hfcpci.pci_io = ioremap((ulong) cs->hw.hfcpci.pci_io, 256); printk(KERN_INFO - "HFC-PCI: defined at mem %#x fifo %#x(%#x) IRQ %d HZ %d\n", + "HFC-PCI: defined at mem %#x fifo %#x(%#x) IRQ %d HZ %d\n", (u_int) cs->hw.hfcpci.pci_io, (u_int) cs->hw.hfcpci.fifos, (u_int) virt_to_bus(cs->hw.hfcpci.fifos), cs->irq, HZ); pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, PCI_ENA_MEMIO); /* enable memory mapped ports, disable busmaster */ - cs->hw.hfcpci.int_m2 = 0; /* disable alle interrupts */ + cs->hw.hfcpci.int_m2 = 0; /* disable alle interrupts */ cs->hw.hfcpci.int_m1 = 0; Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1); Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2); @@ -1647,7 +1835,7 @@ reset_hfcpci(cs); cs->cardmsg = &hfcpci_card_msg; - cs->auxcmd = &hfcpci_auxcmd; + cs->auxcmd = &hfcpci_auxcmd; return (1); #else printk(KERN_WARNING "HFC-PCI: NO_PCI_BIOS\n"); diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/hfc_pci.h linux/drivers/isdn/hisax/hfc_pci.h --- v2.2.13/linux/drivers/isdn/hisax/hfc_pci.h Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/hisax/hfc_pci.h Tue Jan 4 10:12:16 2000 @@ -1,4 +1,4 @@ -/* $Id: hfc_pci.h,v 1.6 1999/08/28 21:04:29 werner Exp $ +/* $Id: hfc_pci.h,v 1.7 1999/10/10 20:13:06 werner Exp $ * specific defines for CCD's HFC 2BDS0 PCI chips * @@ -21,6 +21,10 @@ * 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) * @@ -129,7 +133,7 @@ /* bits in CTMT (Write) */ #define HFCPCI_CLTIMER 0x80 -#define HFCPCI_TIM3_125 0x00 +#define HFCPCI_TIM3_125 0x04 #define HFCPCI_TIM25 0x10 #define HFCPCI_TIM50 0x14 #define HFCPCI_TIM400 0x18 diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/hfcscard.c linux/drivers/isdn/hisax/hfcscard.c --- v2.2.13/linux/drivers/isdn/hisax/hfcscard.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/hisax/hfcscard.c Tue Jan 4 10:12:16 2000 @@ -1,4 +1,4 @@ -/* $Id: hfcscard.c,v 1.5 1999/09/04 06:20:06 keil Exp $ +/* $Id: hfcscard.c,v 1.6 1999/12/19 13:09:42 keil Exp $ * hfcscard.c low level stuff for hfcs based cards (Teles3c, ACER P10) * @@ -6,6 +6,10 @@ * * * $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() * @@ -30,7 +34,7 @@ extern const char *CardType[]; -static const char *hfcs_revision = "$Revision: 1.5 $"; +static const char *hfcs_revision = "$Revision: 1.6 $"; static void hfcs_interrupt(int intno, void *dev_id, struct pt_regs *regs) @@ -85,13 +89,13 @@ cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm); /* Reset On */ save_flags(flags); sti(); - current->state = TASK_INTERRUPTIBLE; + 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_INTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((10*HZ)/1000); if (cs->typ == ISDN_CTYPE_TELES3C) cs->hw.hfcD.cirm |= HFCD_INTB; @@ -138,7 +142,7 @@ init2bds0(cs); save_flags(flags); sti(); - current->state = TASK_INTERRUPTIBLE; + 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 -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/hisax.h linux/drivers/isdn/hisax/hisax.h --- v2.2.13/linux/drivers/isdn/hisax/hisax.h Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/hisax/hisax.h Tue Jan 4 10:12:16 2000 @@ -1,8 +1,18 @@ -/* $Id: hisax.h,v 2.35 1999/09/04 06:35:09 keil Exp $ +/* $Id: hisax.h,v 2.38 1999/11/14 23:37:03 keil Exp $ * Basic declarations, defines and prototypes * * $Log: hisax.h,v $ + * 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 * @@ -140,6 +150,8 @@ #include #include +#undef ERROR_STATISTIC + #define REQUEST 0 #define CONFIRM 1 #define INDICATION 2 @@ -581,6 +593,12 @@ int event; int (*BC_SetStack) (struct PStack *, struct BCState *); void (*BC_Close) (struct BCState *); +#ifdef ERROR_STATISTIC + int err_crc; + int err_tx; + int err_rdo; + int err_inv; +#endif union { struct hscx_hw hscx; struct hdlc_hw hdlc; @@ -648,7 +666,8 @@ struct teles0_hw { unsigned int cfg_reg; - unsigned int membase; + unsigned long membase; + unsigned long phymem; }; struct avm_hw { @@ -755,7 +774,8 @@ unsigned char fifo; unsigned char fifo_en; unsigned char bswapped; - /* unsigned int *send; */ + unsigned char nt_mode; + int nt_timer; unsigned char pci_bus; unsigned char pci_device_fn; unsigned char *pci_io; /* start of PCI IO memory */ @@ -787,8 +807,9 @@ struct isurf_hw { unsigned int reset; - unsigned int isac; - unsigned int isar; + unsigned long phymem; + unsigned long isac; + unsigned long isar; struct isar_reg isar_r; }; @@ -961,6 +982,11 @@ int event; struct tq_struct tqueue; struct timer_list dbusytimer; +#ifdef ERROR_STATISTIC + int err_crc; + int err_tx; + int err_rx; +#endif }; #define MON0_RX 1 @@ -1355,7 +1381,7 @@ int HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb); void HiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, ...); void VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, va_list args); -void HiSax_reportcard(int cardnr); +void HiSax_reportcard(int cardnr, int sel); int QuickHex(char *txt, u_char * p, int cnt); void LogFrame(struct IsdnCardState *cs, u_char * p, int size); void dlogframe(struct IsdnCardState *cs, struct sk_buff *skb, int dir); diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/hscx.h linux/drivers/isdn/hisax/hscx.h --- v2.2.13/linux/drivers/isdn/hisax/hscx.h Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/hisax/hscx.h Tue Jan 4 10:12:16 2000 @@ -1,11 +1,14 @@ -/* $Id: hscx.h,v 1.4 1998/04/15 16:45:34 keil Exp $ +/* $Id: hscx.h,v 1.5 1999/12/23 15:09:32 keil Exp $ * hscx.h HSCX specific defines * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * 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 * diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/hscx_irq.c linux/drivers/isdn/hisax/hscx_irq.c --- v2.2.13/linux/drivers/isdn/hisax/hscx_irq.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/hisax/hscx_irq.c Tue Jan 4 10:12:16 2000 @@ -1,4 +1,4 @@ -/* $Id: hscx_irq.c,v 1.12 1999/07/01 08:11:42 keil Exp $ +/* $Id: hscx_irq.c,v 1.13 1999/10/14 20:25:28 keil Exp $ * hscx_irq.c low level b-channel stuff for Siemens HSCX * @@ -7,6 +7,9 @@ * This is an include file for fast inline IRQ stuff * * $Log: hscx_irq.c,v $ + * Revision 1.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 * @@ -181,16 +184,28 @@ if (val & 0x80) { /* RME */ r = READHSCX(cs, hscx, HSCX_RSTA); if ((r & 0xf0) != 0xa0) { - if (!(r & 0x80)) + if (!(r & 0x80)) { if (cs->debug & L1_DEB_WARN) debugl1(cs, "HSCX invalid frame"); - if ((r & 0x40) && bcs->mode) +#ifdef ERROR_STATISTIC + bcs->err_inv++; +#endif + } + if ((r & 0x40) && bcs->mode) { if (cs->debug & L1_DEB_WARN) debugl1(cs, "HSCX RDO mode=%d", bcs->mode); - if (!(r & 0x20)) +#ifdef ERROR_STATISTIC + bcs->err_rdo++; +#endif + } + if (!(r & 0x20)) { if (cs->debug & L1_DEB_WARN) debugl1(cs, "HSCX CRC error"); +#ifdef ERROR_STATISTIC + bcs->err_crc++; +#endif + } WriteHSCXCMDR(cs, hscx, 0x80); } else { count = READHSCX(cs, hscx, HSCX_RBCL) & ( @@ -265,6 +280,9 @@ if (bcs->mode == 1) hscx_fill_fifo(bcs); else { +#ifdef ERROR_STATISTIC + bcs->err_tx++; +#endif /* Here we lost an TX interrupt, so * restart transmitting the whole frame. */ @@ -295,6 +313,9 @@ /* Here we lost an TX interrupt, so * restart transmitting the whole frame. */ +#ifdef ERROR_STATISTIC + bcs->err_tx++; +#endif if (bcs->tx_skb) { skb_push(bcs->tx_skb, bcs->hw.hscx.count); bcs->tx_cnt += bcs->hw.hscx.count; diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/ipac.h linux/drivers/isdn/hisax/ipac.h --- v2.2.13/linux/drivers/isdn/hisax/ipac.h Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/hisax/ipac.h Tue Jan 4 10:12:16 2000 @@ -1,11 +1,14 @@ -/* $Id: ipac.h,v 1.3 1998/04/15 16:48:09 keil Exp $ +/* $Id: ipac.h,v 1.4 1999/12/23 15:09:32 keil Exp $ * ipac.h IPAC specific defines * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * 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 * diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/isac.c linux/drivers/isdn/hisax/isac.c --- v2.2.13/linux/drivers/isdn/hisax/isac.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/hisax/isac.c Tue Jan 4 10:12:16 2000 @@ -1,4 +1,4 @@ -/* $Id: isac.c,v 1.23 1999/08/25 16:50:52 keil Exp $ +/* $Id: isac.c,v 1.24 1999/10/14 20:25:28 keil Exp $ * isac.c ISAC specific routines * @@ -9,6 +9,9 @@ * ../../../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) * @@ -281,12 +284,20 @@ if (val & 0x80) { /* RME */ exval = cs->readisac(cs, ISAC_RSTA); if ((exval & 0x70) != 0x20) { - if (exval & 0x40) + if (exval & 0x40) { if (cs->debug & L1_DEB_WARN) debugl1(cs, "ISAC RDO"); - if (!(exval & 0x20)) +#ifdef ERROR_STATISTIC + cs->err_rx++; +#endif + } + if (!(exval & 0x20)) { if (cs->debug & L1_DEB_WARN) debugl1(cs, "ISAC CRC error"); +#ifdef ERROR_STATISTIC + cs->err_crc++; +#endif + } cs->writeisac(cs, ISAC_CMDR, 0x80); } else { count = cs->readisac(cs, ISAC_RBCL) & 0x1f; @@ -371,6 +382,9 @@ if (exval & 0x40) { /* XDU */ debugl1(cs, "ISAC XDU"); printk(KERN_WARNING "HiSax: ISAC 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)) diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/isac.h linux/drivers/isdn/hisax/isac.h --- v2.2.13/linux/drivers/isdn/hisax/isac.h Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/hisax/isac.h Tue Jan 4 10:12:16 2000 @@ -1,11 +1,14 @@ -/* $Id: isac.h,v 1.5 1998/05/25 12:58:03 keil Exp $ +/* $Id: isac.h,v 1.6 1999/12/23 15:09:32 keil Exp $ * isac.h ISAC specific defines * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * 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. diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/isar.c linux/drivers/isdn/hisax/isar.c --- v2.2.13/linux/drivers/isdn/hisax/isar.c Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/hisax/isar.c Tue Jan 4 10:12:16 2000 @@ -1,4 +1,4 @@ -/* $Id: isar.c,v 1.6 1999/08/31 11:20:20 paul Exp $ +/* $Id: isar.c,v 1.8 1999/12/19 13:00:56 keil Exp $ * isar.c ISAR (Siemens PSB 7110) specific routines * @@ -6,6 +6,12 @@ * * * $Log: isar.c,v $ + * 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!) * @@ -480,6 +486,12 @@ if (cs->debug & L1_DEB_WARN) debugl1(cs, "isar frame error %x len %d", ireg->cmsb, ireg->clsb); +#ifdef ERROR_STATISTIC + if (ireg->cmsb & HDLC_ERR_RER) + bcs->err_inv++; + if (ireg->cmsb & HDLC_ERR_CER) + bcs->err_crc++; +#endif bcs->hw.isar.rcvidx = 0; cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); } else { @@ -790,10 +802,19 @@ check_send(cs, ireg->cmsb); break; case ISAR_IIS_BSTEV: - cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); +#ifdef ERROR_STATISTIC + if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) { + if (ireg->cmsb == BSTEV_TBO) + bcs->err_tx++; + if (ireg->cmsb == BSTEV_RBO) + bcs->err_rdo++; + } +#endif if (cs->debug & L1_DEB_WARN) debugl1(cs, "Buffer STEV dpath%d msb(%x)", ireg->iis>>6, ireg->cmsb); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + break; case ISAR_IIS_PSTEV: if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) { rcv_mbox(cs, ireg, (u_char *)ireg->par); @@ -900,11 +921,9 @@ } break; } - if (!sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL)) { - if (cs->debug) - debugl1(cs, "isar pump status req dp%d failed", - bcs->hw.isar.dpath); - } + udelay(1000); + sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); + udelay(1000); } static void @@ -948,11 +967,9 @@ } break; } - if (!sendmsg(cs, dps | ISAR_HIS_BSTREQ, 0, 0, NULL)) { - if (cs->debug) - debugl1(cs, "isar buf stat req dp%d failed", - bcs->hw.isar.dpath); - } + udelay(1000); + sendmsg(cs, dps | ISAR_HIS_BSTREQ, 0, 0, NULL); + udelay(1000); } static void @@ -977,15 +994,10 @@ cmsb |= IOM_CTRL_ALAW | IOM_CTRL_RCV; break; } - if (!sendmsg(cs, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg)) { - if (cs->debug) - debugl1(cs, "isar iom2 dp%d failed", bcs->hw.isar.dpath); - } - if (!sendmsg(cs, dps | ISAR_HIS_IOM2REQ, 0, 0, NULL)) { - if (cs->debug) - debugl1(cs, "isar IOM2 cfg req dp%d failed", - bcs->hw.isar.dpath); - } + sendmsg(cs, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg); + udelay(1000); + sendmsg(cs, dps | ISAR_HIS_IOM2REQ, 0, 0, NULL); + udelay(1000); } int diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/isar.h linux/drivers/isdn/hisax/isar.h --- v2.2.13/linux/drivers/isdn/hisax/isar.h Tue Jan 4 11:10:34 2000 +++ linux/drivers/isdn/hisax/isar.h Tue Jan 4 10:12:16 2000 @@ -1,10 +1,13 @@ -/* $Id: isar.h,v 1.5 1999/08/25 16:59:59 keil Exp $ +/* $Id: isar.h,v 1.6 1999/10/14 20:25:29 keil Exp $ * isar.h ISAR (Siemens PSB 7110) specific defines * * Author Karsten Keil (keil@isdn4linux.de) * * * $Log: isar.h,v $ + * 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 @@ -178,12 +181,17 @@ #define HDLC_FSD 0x20 #define HDLC_FST 0x20 #define HDLC_ERROR 0x1c +#define HDLC_ERR_FAD 0x10 +#define HDLC_ERR_RER 0x08 +#define HDLC_ERR_CER 0x01 #define SART_NMD 0x01 #define BSTAT_RDM0 0x1 #define BSTAT_RDM1 0x2 #define BSTAT_RDM2 0x4 #define BSTAT_RDM3 0x8 +#define BSTEV_TBO 0x1f +#define BSTEV_RBO 0x2f extern int ISARVersion(struct IsdnCardState *cs, char *s); extern void isar_int_main(struct IsdnCardState *cs); diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/isurf.c linux/drivers/isdn/hisax/isurf.c --- v2.2.13/linux/drivers/isdn/hisax/isurf.c Tue Jan 4 11:10:35 2000 +++ linux/drivers/isdn/hisax/isurf.c Tue Jan 4 10:12:16 2000 @@ -1,10 +1,17 @@ -/* $Id: isurf.c,v 1.6 1999/09/04 06:20:06 keil Exp $ +/* $Id: isurf.c,v 1.8 1999/12/19 13:09:42 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() * @@ -37,7 +44,7 @@ extern const char *CardType[]; -static const char *ISurf_revision = "$Revision: 1.6 $"; +static const char *ISurf_revision = "$Revision: 1.8 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -50,7 +57,7 @@ #define ISURF_ISAR_OFFSET 0 #define ISURF_ISAC_OFFSET 0x100 - +#define ISURF_IOMEM_SIZE 0x400 /* Interface functions */ static u_char @@ -157,10 +164,10 @@ byteout(cs->hw.isurf.reset, chips); /* Reset On */ save_flags(flags); sti(); - current->state = TASK_INTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((10*HZ)/1000); byteout(cs->hw.isurf.reset, ISURF_ISAR_EA); /* Reset Off */ - current->state = TASK_INTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((10*HZ)/1000); restore_flags(flags); } @@ -223,8 +230,7 @@ return(0); if (card->para[1] && card->para[2]) { cs->hw.isurf.reset = card->para[1]; - cs->hw.isurf.isar = card->para[2] + ISURF_ISAR_OFFSET; - cs->hw.isurf.isac = card->para[2] + ISURF_ISAC_OFFSET; + cs->hw.isurf.phymem = card->para[2]; cs->irq = card->para[0]; } else { printk(KERN_WARNING "HiSax: %s port/mem not set\n", @@ -240,11 +246,12 @@ } else { request_region(cs->hw.isurf.reset, 1, "isurf isdn"); } - + cs->hw.isurf.isar = cs->hw.isurf.phymem + ISURF_ISAR_OFFSET; + cs->hw.isurf.isac = cs->hw.isurf.phymem + ISURF_ISAC_OFFSET; printk(KERN_INFO - "ISurf: defined at 0x%x 0x%x IRQ %d\n", + "ISurf: defined at 0x%x 0x%lx IRQ %d\n", cs->hw.isurf.reset, - cs->hw.isurf.isar, + cs->hw.isurf.phymem, cs->irq); cs->cardmsg = &ISurf_card_msg; diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/l3dss1.c linux/drivers/isdn/hisax/l3dss1.c --- v2.2.13/linux/drivers/isdn/hisax/l3dss1.c Tue Jan 4 11:10:35 2000 +++ linux/drivers/isdn/hisax/l3dss1.c Tue Jan 4 10:12:16 2000 @@ -1,4 +1,4 @@ -/* $Id: l3dss1.c,v 2.19 1999/08/25 16:55:23 keil Exp $ +/* $Id: l3dss1.c,v 2.21 1999/12/19 20:25:17 keil Exp $ * EURO/DSS1 D-channel protocol * @@ -13,6 +13,13 @@ * Fritz Elfert * * $Log: l3dss1.c,v $ + * 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 * @@ -90,7 +97,7 @@ #include extern char *HiSax_getrev(const char *revision); -const char *dss1_revision = "$Revision: 2.19 $"; +const char *dss1_revision = "$Revision: 2.21 $"; #define EXT_BEARER_CAPS 1 @@ -665,34 +672,36 @@ } static int ie_ALERTING[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1, - IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_HLC, IE_USER_USER, -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_CONNECT_PN, - IE_CONNECT_SUB, IE_LLC, IE_HLC, IE_USER_USER, -1}; -static int ie_CONNECT_ACKNOWLEDGE[] = {IE_CHANNEL_ID, IE_DISPLAY, -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_USER_USER, -1}; -static int ie_INFORMATION[] = {IE_COMPLETE, IE_DISPLAY, IE_KEYPAD, + 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_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_USER_USER, -1}; +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_CALLING_PN, + IE_NET_FAC, IE_DISPLAY, IE_KEYPAD, IE_SIGNAL, IE_CALLING_PN, IE_CALLING_SUB, IE_CALLED_PN, IE_CALLED_SUB, IE_LLC, IE_HLC, IE_USER_USER, -1}; static int ie_SETUP_ACKNOWLEDGE[] = {IE_CHANNEL_ID | IE_MANDATORY, IE_FACILITY, - IE_PROGRESS, IE_DISPLAY, -1}; + 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}; @@ -1451,10 +1460,23 @@ p = EncodeASyncParams(p, pc->para.setup.si2 - 192); #if HISAX_SEND_STD_LLC_IE } else { - *p++ = 0x7c; - *p++ = 0x02; - *p++ = 0x88; - *p++ = 0x90; + 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 */ + break; + case 5: /* Datatransmission 64k, BTX */ + case 7: /* Datatransmission 64k */ + default: + *p++ = 0x7c; /* BC-IE-code */ + *p++ = 0x2; /* Length */ + *p++ = 0x88; /* Coding Std. CCITT, unrestr. dig. Inform. */ + *p++ = 0x90; /* Circuit-Mode 64kbps */ + break; + } #endif } #endif @@ -2520,14 +2542,13 @@ u_char *msg = pc->chan->setup.phone; MsgHead(p, pc->callref, MT_SUSPEND); - - *p++ = IE_CALL_ID; 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 { + } else if (l) { l3_debug(pc->st, "SUS wrong CALL_ID len %d", l); return; } @@ -2596,13 +2617,13 @@ MsgHead(p, pc->callref, MT_RESUME); - *p++ = IE_CALL_ID; 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 { + } else if (l) { l3_debug(pc->st, "RES wrong CALL_ID len %d", l); return; } diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/md5sums.asc linux/drivers/isdn/hisax/md5sums.asc --- v2.2.13/linux/drivers/isdn/hisax/md5sums.asc Tue Jan 4 11:10:35 2000 +++ linux/drivers/isdn/hisax/md5sums.asc Tue Jan 4 10:12:16 2000 @@ -6,26 +6,26 @@ # Eicon Technology Diva 2.01 PCI cards in the moment. # Read ../../../Documentation/isdn/HiSax.cert for more informations. # -2760872030085b2d36aa203b44e9570e isac.c +3c2b1c96274cba97a8261d1cecc662b8 isac.c a9a15d069dbacb383cc24c238cb5ebbe isdnl1.c bb51bd223040b511c18f091da5ab6456 isdnl2.c b7aa7f97b2374967a4aca7c52991142c isdnl3.c a23fbf8879c1432b04640b8b04bdf419 tei.c -c07fd0729b93ebffc3dbfc88cc334be1 callc.c +ce248e56c2e1326012d0b25f92bbf99b callc.c bf9605b36429898f7be6630034e83230 cert.c -3fe91ac7175e1c15ad5f15b6d71b2756 l3dss1.c +2dbd5fadc33657c654f71385ff84127a l3dss1.c 2d748ced0eea375b21fe7ea91ca7917c l3_1tr6.c -72ee065c70473949a676f06f8ce83ebf elsa.c -1b26dd9b0f132744a7be2ce2ab1eda83 diva.c +8188deeb4a1b34c574cd51638cd214d0 elsa.c +a3c2b8e9d2c623658888b5f1d4317c2a diva.c # end of md5sums -----BEGIN PGP SIGNATURE----- Version: 2.6.3i Charset: noconv -iQCVAwUBN+/NfDpxHvX/mS9tAQEC4gQAlAfRvVnILSrDUtH83RzPd6nCLONiHpKn -nmyE3BkGk2DO1D8azCjx3X/9x7ozEm8pL5UN2kZuu5p9EO0ISzM5ZmcCi0jxAOq5 -c493UJmnWWOw02G0pNw+EsmRH9mUh1t7IyUEL+UpUMNyAEQRqq66k764YyUkKF88 -fup3c64mSKw= -=bmv8 +iQCVAwUBOGUfJzpxHvX/mS9tAQFpwwP/UFR+IP3URpDrRPJgWdzaP+Ho/nu4uOQM ++l2d0MNZlBPIPiRJnP921mcSXUw37OsuXf8cfqihuCgc+2+ExNZe9s9o/Wz5TttA +L1fhoP85OXy6m5Ap77Feh1tzxAQodRwIzzFvrXqUUN82JW32Q931dJzxVbYtgP16 +SkErM8N2GbM= +=QeLI -----END PGP SIGNATURE----- diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/netjet.c linux/drivers/isdn/hisax/netjet.c --- v2.2.13/linux/drivers/isdn/hisax/netjet.c Tue Jan 4 11:10:35 2000 +++ linux/drivers/isdn/hisax/netjet.c Tue Jan 4 10:12:16 2000 @@ -1,4 +1,4 @@ -/* $Id: netjet.c,v 1.15 1999/09/04 06:20:06 keil Exp $ +/* $Id: netjet.c,v 1.17 1999/12/19 13:09:42 keil Exp $ * netjet.c low level stuff for Traverse Technologie NETJet ISDN cards * @@ -7,6 +7,13 @@ * 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() * @@ -78,7 +85,7 @@ extern const char *CardType[]; -const char *NETjet_revision = "$Revision: 1.15 $"; +const char *NETjet_revision = "$Revision: 1.17 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -528,19 +535,26 @@ debugl1(bcs->cs, "tiger: frame not byte aligned"); state=HDLC_FLAG_SEARCH; bcs->hw.tiger.r_err++; +#ifdef ERROR_STATISTIC + bcs->err_inv++; +#endif } else { if (bcs->cs->debug & L1_DEB_HSCX) debugl1(bcs->cs,"tiger frame end(%d,%d): fcs(%x) i %x", i,j,bcs->hw.tiger.r_fcs, bcs->cs->hw.njet.irqstat0); if (bcs->hw.tiger.r_fcs == PPP_GOODFCS) { got_frame(bcs, (bitcnt>>3)-3); - } else + } else { if (bcs->cs->debug) { debugl1(bcs->cs, "tiger FCS error"); printframe(bcs->cs, bcs->hw.tiger.rcvbuf, (bitcnt>>3)-1, "rec"); bcs->hw.tiger.r_err++; } +#ifdef ERROR_STATISTIC + bcs->err_crc++; +#endif + } state=HDLC_FLAG_FOUND; } bitcnt=0; @@ -562,6 +576,9 @@ r_val=0; state=HDLC_FLAG_SEARCH; bcs->hw.tiger.r_err++; +#ifdef ERROR_STATISTIC + bcs->err_inv++; +#endif } else { bcs->hw.tiger.rcvbuf[(bitcnt>>3)-1] = r_val; bcs->hw.tiger.r_fcs = @@ -586,6 +603,12 @@ if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_READ) { debugl1(cs,"tiger warn read double dma %x/%x", cs->hw.njet.irqstat0, cs->hw.njet.last_is0); +#ifdef ERROR_STATISTIC + if (cs->bcs[0].mode) + cs->bcs[0].err_rdo++; + if (cs->bcs[1].mode) + cs->bcs[1].err_rdo++; +#endif return; } else { cs->hw.njet.last_is0 &= ~NETJET_IRQM0_READ; @@ -757,6 +780,12 @@ if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_WRITE) { debugl1(cs,"tiger warn write double dma %x/%x", cs->hw.njet.irqstat0, cs->hw.njet.last_is0); +#ifdef ERROR_STATISTIC + if (cs->bcs[0].mode) + cs->bcs[0].err_tx++; + if (cs->bcs[1].mode) + cs->bcs[1].err_tx++; +#endif return; } else { cs->hw.njet.last_is0 &= ~NETJET_IRQM0_WRITE; @@ -1029,11 +1058,11 @@ sti(); cs->hw.njet.ctrl_reg = 0xff; /* Reset On */ byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg); - current->state = TASK_INTERRUPTIBLE; + current->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_INTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ restore_flags(flags); cs->hw.njet.auxd = 0; diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/rawhdlc.c linux/drivers/isdn/hisax/rawhdlc.c --- v2.2.13/linux/drivers/isdn/hisax/rawhdlc.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/isdn/hisax/rawhdlc.c Tue Jan 4 10:12:16 2000 @@ -1,8 +1,8 @@ -/* $Id: rawhdlc.c,v 1.3 1998/06/17 19:51:21 he Exp $ +/* $Id: rawhdlc.c,v 1.4 1999/12/23 15:09:32 keil Exp $ * rawhdlc.c support routines for cards that don't support HDLC * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Author Karsten Keil (keil@isdn4linux.de) * Brent Baccala * * diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/saphir.c linux/drivers/isdn/hisax/saphir.c --- v2.2.13/linux/drivers/isdn/hisax/saphir.c Tue Jan 4 11:10:35 2000 +++ linux/drivers/isdn/hisax/saphir.c Tue Jan 4 10:12:16 2000 @@ -1,4 +1,4 @@ -/* $Id: saphir.c,v 1.4 1999/09/04 06:20:06 keil Exp $ +/* $Id: saphir.c,v 1.5 1999/12/19 13:09:42 keil Exp $ * saphir.c low level stuff for HST Saphir 1 * @@ -8,6 +8,10 @@ * * * $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() * @@ -29,7 +33,7 @@ #include "isdnl1.h" extern const char *CardType[]; -static char *saphir_rev = "$Revision: 1.4 $"; +static char *saphir_rev = "$Revision: 1.5 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -237,10 +241,10 @@ save_flags(flags); sti(); byteout(cs->hw.saphir.cfg_reg + RESET_REG, 1); - current->state = TASK_INTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((30*HZ)/1000); /* Timeout 30ms */ byteout(cs->hw.saphir.cfg_reg + RESET_REG, 0); - current->state = TASK_INTERRUPTIBLE; + 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 -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/sedlbauer.c linux/drivers/isdn/hisax/sedlbauer.c --- v2.2.13/linux/drivers/isdn/hisax/sedlbauer.c Tue Jan 4 11:10:35 2000 +++ linux/drivers/isdn/hisax/sedlbauer.c Tue Jan 4 10:12:16 2000 @@ -1,4 +1,4 @@ -/* $Id: sedlbauer.c,v 1.17 1999/09/04 06:20:06 keil Exp $ +/* $Id: sedlbauer.c,v 1.19 1999/12/19 13:09:42 keil Exp $ * sedlbauer.c low level stuff for Sedlbauer cards * includes support for the Sedlbauer speed star (speed star II), @@ -17,6 +17,13 @@ * Edgar Toernig * * $Log: sedlbauer.c,v $ + * 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() * @@ -79,12 +86,13 @@ * --------------------------------------------------------------------- * Speed Card ISAC_HSCX DIP-SWITCH * Speed Win ISAC_HSCX ISAPNP - * Speed Fax+ ISAC_ISAR ISAPNP #HDLC works# + * Speed Fax+ ISAC_ISAR ISAPNP Full analog support * Speed Star ISAC_HSCX CARDMGR * Speed Win2 IPAC ISAPNP * ISDN PC/104 IPAC DIP-SWITCH * Speed Star2 IPAC CARDMGR - * Speed PCI IPAC PNP + * Speed PCI IPAC PCI PNP + * Speed Fax+ ISAC_ISAR PCI PNP Full analog support * * Important: * For the sedlbauer speed fax+ to work properly you have to download @@ -106,15 +114,18 @@ extern const char *CardType[]; -const char *Sedlbauer_revision = "$Revision: 1.17 $"; +const char *Sedlbauer_revision = "$Revision: 1.19 $"; const char *Sedlbauer_Types[] = {"None", "speed card/win", "speed star", "speed fax+", - "speed win II / ISDN PC/104", "speed star II", "speed pci"}; + "speed win II / ISDN PC/104", "speed star II", "speed pci", + "speed fax+ pci"}; #ifdef SEDLBAUER_PCI #define PCI_VENDOR_SEDLBAUER 0xe159 -#define PCI_SPEEDPCI_ID 0x02 +#define PCI_SPEEDPCI_ID 0x02 +#define PCI_SUBVENDOR_SEDLBAUER 0x51 +#define PCI_SUB_ID_SPEEDFAXP 0x01 #endif #define SEDL_SPEED_CARD_WIN 1 @@ -123,6 +134,7 @@ #define SEDL_SPEED_WIN2_PC104 4 #define SEDL_SPEED_STAR2 5 #define SEDL_SPEED_PCI 6 +#define SEDL_SPEEDFAX_PCI 7 #define SEDL_CHIP_TEST 0 #define SEDL_CHIP_ISAC_HSCX 1 @@ -153,12 +165,19 @@ #define SEDL_ISAR_ISA_ISAR_RESET_ON 10 #define SEDL_ISAR_ISA_ISAR_RESET_OFF 12 -#define SEDL_IPAC_ANY_ADR 0 -#define SEDL_IPAC_ANY_IPAC 2 +#define SEDL_IPAC_ANY_ADR 0 +#define SEDL_IPAC_ANY_IPAC 2 -#define SEDL_IPAC_PCI_BASE 0 -#define SEDL_IPAC_PCI_ADR 0xc0 -#define SEDL_IPAC_PCI_IPAC 0xc8 +#define SEDL_IPAC_PCI_BASE 0 +#define SEDL_IPAC_PCI_ADR 0xc0 +#define SEDL_IPAC_PCI_IPAC 0xc8 +#define SEDL_ISAR_PCI_ADR 0xc8 +#define SEDL_ISAR_PCI_ISAC 0xd0 +#define SEDL_ISAR_PCI_ISAR 0xe0 +#define SEDL_ISAR_PCI_ISAR_RESET_ON 0x01 +#define SEDL_ISAR_PCI_ISAR_RESET_OFF 0x18 +#define SEDL_ISAR_PCI_LED1 0x08 +#define SEDL_ISAR_PCI_LED2 0x10 #define SEDL_RESET 0x3 /* same as DOS driver */ @@ -235,24 +254,25 @@ static u_char ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset) { - return (readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset|0x80));} + return (readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset|0x80)); +} static void WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value) { - writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset|0x80, value); + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset|0x80, value); } static void ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size) { - readfifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0x80, data, size); + readfifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0x80, data, size); } static void WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size) { - writefifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0x80, data, size); + writefifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0x80, data, size); } static u_char @@ -474,10 +494,10 @@ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x20); save_flags(flags); sti(); - current->state = TASK_INTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((10*HZ)/1000); writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x0); - current->state = TASK_INTERRUPTIBLE; + 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); @@ -485,14 +505,25 @@ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xc0); writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_PCFG, 0x12); restore_flags(flags); + } else if ((cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) && + (cs->hw.sedl.bus == SEDL_BUS_PCI)) { + byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_on); + save_flags(flags); + sti(); + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout((20*HZ)/1000); + byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off); + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout((20*HZ)/1000); + restore_flags(flags); } else { byteout(cs->hw.sedl.reset_on, SEDL_RESET); /* Reset On */ save_flags(flags); sti(); - current->state = TASK_INTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((10*HZ)/1000); byteout(cs->hw.sedl.reset_off, 0); /* Reset Off */ - current->state = TASK_INTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((10*HZ)/1000); restore_flags(flags); } @@ -537,6 +568,24 @@ return(0); case CARD_TEST: return(0); + case MDL_INFO_CONN: + if (cs->subtyp != SEDL_SPEEDFAX_PCI) + return(0); + if ((long) arg) + cs->hw.sedl.reset_off &= ~SEDL_ISAR_PCI_LED2; + else + cs->hw.sedl.reset_off &= ~SEDL_ISAR_PCI_LED1; + byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off); + break; + case MDL_INFO_REL: + if (cs->subtyp != SEDL_SPEEDFAX_PCI) + return(0); + if ((long) arg) + cs->hw.sedl.reset_off |= SEDL_ISAR_PCI_LED2; + else + cs->hw.sedl.reset_off |= SEDL_ISAR_PCI_LED1; + byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off); + break; } return(0); } @@ -551,6 +600,8 @@ int bytecnt, ver, val; struct IsdnCardState *cs = card->cs; char tmp[64]; + u16 sub_vendor_id, sub_id; + long flags; strcpy(tmp, Sedlbauer_revision); printk(KERN_INFO "HiSax: Sedlbauer driver Rev. %s\n", HiSax_getrev(tmp)); @@ -598,15 +649,40 @@ printk(KERN_WARNING "Sedlbauer: No PCI card found\n"); return(0); } - cs->irq_flags |= SA_SHIRQ; + cs->irq_flags |= SA_SHIRQ; cs->hw.sedl.bus = SEDL_BUS_PCI; - cs->hw.sedl.chip = SEDL_CHIP_IPAC; - cs->subtyp = SEDL_SPEED_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); + 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)) { + cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR; + cs->subtyp = SEDL_SPEEDFAX_PCI; + cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + + SEDL_ISAR_PCI_ISAR_RESET_ON; + cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + + SEDL_ISAR_PCI_ISAR_RESET_OFF; + } else { + cs->hw.sedl.chip = SEDL_CHIP_IPAC; + cs->subtyp = SEDL_SPEED_PCI; + } bytecnt = 256; byteout(cs->hw.sedl.cfg_reg, 0xff); byteout(cs->hw.sedl.cfg_reg, 0x00); byteout(cs->hw.sedl.cfg_reg+ 2, 0xdd); byteout(cs->hw.sedl.cfg_reg+ 5, 0x02); + byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_on); + save_flags(flags); + sti(); + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout((10*HZ)/1000); + byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off); + restore_flags(flags); #else printk(KERN_WARNING "Sedlbauer: NO_PCI_BIOS\n"); return (0); @@ -688,7 +764,7 @@ cs->writeisacfifo = &WriteISACfifo_IPAC; cs->irq_func = &sedlbauer_interrupt_ipac; - val = readreg(cs->hw.sedl.adr,cs->hw.sedl.isac, IPAC_ID); + val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ID); printk(KERN_INFO "Sedlbauer: IPAC version %x\n", val); reset_sedlbauer(cs); } else { @@ -698,18 +774,31 @@ cs->readisacfifo = &ReadISACfifo; cs->writeisacfifo = &WriteISACfifo; if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) { - cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ADR; - cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ISAC; - cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ISAR; - cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ISAR_RESET_ON; - cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ISAR_RESET_OFF; + if (cs->hw.sedl.bus == SEDL_BUS_PCI) { + cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + + SEDL_ISAR_PCI_ADR; + cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + + SEDL_ISAR_PCI_ISAC; + cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + + SEDL_ISAR_PCI_ISAR; + } else { + cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + + SEDL_ISAR_ISA_ADR; + cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + + SEDL_ISAR_ISA_ISAC; + cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + + SEDL_ISAR_ISA_ISAR; + cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + + SEDL_ISAR_ISA_ISAR_RESET_ON; + cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + + SEDL_ISAR_ISA_ISAR_RESET_OFF; + } cs->bcs[0].hw.isar.reg = &cs->hw.sedl.isar; cs->bcs[1].hw.isar.reg = &cs->hw.sedl.isar; test_and_set_bit(HW_ISAR, &cs->HW_Flags); cs->irq_func = &sedlbauer_interrupt_isar; cs->auxcmd = &isar_auxcmd; ISACVersion(cs, "Sedlbauer:"); - cs->BC_Read_Reg = &ReadISAR; cs->BC_Write_Reg = &WriteISAR; cs->BC_Send_Data = &isar_fill_fifo; diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/sportster.c linux/drivers/isdn/hisax/sportster.c --- v2.2.13/linux/drivers/isdn/hisax/sportster.c Tue Jan 4 11:10:35 2000 +++ linux/drivers/isdn/hisax/sportster.c Tue Jan 4 10:12:16 2000 @@ -1,12 +1,19 @@ -/* $Id: sportster.c,v 1.10 1999/09/04 06:20:06 keil Exp $ +/* $Id: sportster.c,v 1.12 1999/12/23 15:09:32 keil Exp $ * sportster.c low level stuff for USR Sportster internal TA * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * 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() * @@ -46,7 +53,7 @@ #include "isdnl1.h" extern const char *CardType[]; -const char *sportster_revision = "$Revision: 1.10 $"; +const char *sportster_revision = "$Revision: 1.12 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -180,11 +187,11 @@ byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq); save_flags(flags); sti(); - current->state = TASK_INTERRUPTIBLE; + 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_INTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((10*HZ)/1000); restore_flags(flags); } diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/teleint.c linux/drivers/isdn/hisax/teleint.c --- v2.2.13/linux/drivers/isdn/hisax/teleint.c Tue Jan 4 11:10:35 2000 +++ linux/drivers/isdn/hisax/teleint.c Tue Jan 4 10:12:16 2000 @@ -1,4 +1,4 @@ -/* $Id: teleint.c,v 1.11 1999/09/04 06:20:06 keil Exp $ +/* $Id: teleint.c,v 1.12 1999/12/19 13:09:42 keil Exp $ * teleint.c low level stuff for TeleInt isdn cards * @@ -6,6 +6,10 @@ * * * $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() * @@ -51,7 +55,7 @@ extern const char *CardType[]; -const char *TeleInt_revision = "$Revision: 1.11 $"; +const char *TeleInt_revision = "$Revision: 1.12 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -260,11 +264,11 @@ byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm); /* Reset On */ save_flags(flags); sti(); - current->state = TASK_INTERRUPTIBLE; + 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_INTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((10*HZ)/1000); restore_flags(flags); } diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/teles0.c linux/drivers/isdn/hisax/teles0.c --- v2.2.13/linux/drivers/isdn/hisax/teles0.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/isdn/hisax/teles0.c Tue Jan 4 10:12:16 2000 @@ -1,15 +1,21 @@ -/* $Id: teles0.c,v 2.9 1999/07/12 21:05:31 keil Exp $ +/* $Id: teles0.c,v 2.11 1999/12/23 15:09:32 keil Exp $ * teles0.c low level stuff for Teles Memory IO isdn cards * based on the teles driver from Jan den Ouden * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Author Karsten Keil (keil@isdn4linux.de) * * Thanks to Jan den Ouden * 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 @@ -58,71 +64,72 @@ extern const char *CardType[]; -const char *teles0_revision = "$Revision: 2.9 $"; +const char *teles0_revision = "$Revision: 2.11 $"; +#define TELES_IOMEM_SIZE 0x400 #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) static inline u_char -readisac(unsigned int adr, u_char off) +readisac(unsigned long adr, u_char off) { return readb(adr + ((off & 1) ? 0x2ff : 0x100) + off); } static inline void -writeisac(unsigned int adr, u_char off, u_char data) +writeisac(unsigned long adr, u_char off, u_char data) { writeb(data, adr + ((off & 1) ? 0x2ff : 0x100) + off); mb(); } static inline u_char -readhscx(unsigned int adr, int hscx, u_char off) +readhscx(unsigned long adr, int hscx, u_char off) { return readb(adr + (hscx ? 0x1c0 : 0x180) + ((off & 1) ? 0x1ff : 0) + off); } static inline void -writehscx(unsigned int adr, int hscx, u_char off, u_char data) +writehscx(unsigned long adr, int hscx, u_char off, u_char data) { writeb(data, adr + (hscx ? 0x1c0 : 0x180) + ((off & 1) ? 0x1ff : 0) + off); mb(); } static inline void -read_fifo_isac(unsigned int adr, u_char * data, int size) +read_fifo_isac(unsigned long adr, u_char * data, int size) { register int i; - register u_char *ad = (u_char *) ((long)adr + 0x100); + register u_char *ad = (u_char *)adr + 0x100; for (i = 0; i < size; i++) data[i] = readb(ad); } static inline void -write_fifo_isac(unsigned int adr, u_char * data, int size) +write_fifo_isac(unsigned long adr, u_char * data, int size) { register int i; - register u_char *ad = (u_char *) ((long)adr + 0x100); + register u_char *ad = (u_char *)adr + 0x100; for (i = 0; i < size; i++) { writeb(data[i], ad); mb(); } } static inline void -read_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size) +read_fifo_hscx(unsigned long adr, int hscx, u_char * data, int size) { register int i; - register u_char *ad = (u_char *) ((long)adr + (hscx ? 0x1c0 : 0x180)); + register u_char *ad = (u_char *) (adr + (hscx ? 0x1c0 : 0x180)); for (i = 0; i < size; i++) data[i] = readb(ad); } static inline void -write_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size) +write_fifo_hscx(unsigned long adr, int hscx, u_char * data, int size) { int i; - register u_char *ad = (u_char *) ((long)adr + (hscx ? 0x1c0 : 0x180)); + register u_char *ad = (u_char *) (adr + (hscx ? 0x1c0 : 0x180)); for (i = 0; i < size; i++) { writeb(data[i], ad); mb(); } @@ -262,7 +269,7 @@ default: return(1); } - cfval |= ((cs->hw.teles0.membase >> 9) & 0xF0); + cfval |= ((cs->hw.teles0.phymem >> 9) & 0xF0); byteout(cs->hw.teles0.cfg_reg + 4, cfval); HZDELAY(HZ / 10 + 1); byteout(cs->hw.teles0.cfg_reg + 4, cfval | 1); @@ -318,10 +325,9 @@ "Teles0: membase configured DOSish, assuming 0x%lx\n", (unsigned long) card->para[1]); } - cs->hw.teles0.membase = card->para[1]; cs->irq = card->para[0]; if (cs->hw.teles0.cfg_reg) { - if (check_region((cs->hw.teles0.cfg_reg), 8)) { + if (check_region(cs->hw.teles0.cfg_reg, 8)) { printk(KERN_WARNING "HiSax: %s config port %x-%x already in use\n", CardType[card->typ], @@ -358,8 +364,10 @@ } /* 16.0 and 8.0 designed for IOM1 */ test_and_set_bit(HW_IOM1, &cs->HW_Flags); + cs->hw.teles0.phymem = card->para[1]; + cs->hw.teles0.membase = cs->hw.teles0.phymem; printk(KERN_INFO - "HiSax: %s config irq:%d mem:0x%X cfg:0x%X\n", + "HiSax: %s config irq:%d mem:0x%lX cfg:0x%X\n", CardType[cs->typ], cs->irq, cs->hw.teles0.membase, cs->hw.teles0.cfg_reg); if (reset_teles0(cs)) { diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/teles3.c linux/drivers/isdn/hisax/teles3.c --- v2.2.13/linux/drivers/isdn/hisax/teles3.c Tue Jan 4 11:10:35 2000 +++ linux/drivers/isdn/hisax/teles3.c Tue Jan 4 10:12:16 2000 @@ -1,16 +1,19 @@ -/* $Id: teles3.c,v 2.13 1999/08/30 12:01:28 keil Exp $ +/* $Id: teles3.c,v 2.14 1999/12/23 15:09:32 keil Exp $ * teles3.c low level stuff for Teles 16.3 & PNP isdn cards * * based on the teles driver from Jan den Ouden * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Author Karsten Keil (keil@isdn4linux.de) * * Thanks to Jan den Ouden * Fritz Elfert * Beat Doebeli * * $Log: teles3.c,v $ + * 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 * @@ -88,7 +91,7 @@ #include "isdnl1.h" extern const char *CardType[]; -const char *teles3_revision = "$Revision: 2.13 $"; +const char *teles3_revision = "$Revision: 2.14 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/hisax/telespci.c linux/drivers/isdn/hisax/telespci.c --- v2.2.13/linux/drivers/isdn/hisax/telespci.c Tue Jan 4 11:10:35 2000 +++ linux/drivers/isdn/hisax/telespci.c Tue Jan 4 10:12:16 2000 @@ -1,12 +1,18 @@ -/* $Id: telespci.c,v 2.9 1999/08/11 21:01:34 keil Exp $ +/* $Id: telespci.c,v 2.11 1999/12/23 15:09:32 keil Exp $ * telespci.c low level stuff for Teles PCI isdn cards * * Author Ton van Rosmalen - * Karsten Keil (keil@temic-ech.spacenet.de) + * 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 * @@ -44,7 +50,7 @@ #include extern const char *CardType[]; -const char *telespci_revision = "$Revision: 2.9 $"; +const char *telespci_revision = "$Revision: 2.11 $"; #define ZORAN_PO_RQ_PEN 0x02000000 #define ZORAN_PO_WR 0x00800000 @@ -66,7 +72,7 @@ } while (portdata & ZORAN_PO_RQ_PEN) static inline u_char -readisac(unsigned int adr, u_char off) +readisac(unsigned long adr, u_char off) { register unsigned int portdata; @@ -83,7 +89,7 @@ } static inline void -writeisac(unsigned int adr, u_char off, u_char data) +writeisac(unsigned long adr, u_char off, u_char data) { register unsigned int portdata; @@ -99,7 +105,7 @@ } static inline u_char -readhscx(unsigned int adr, int hscx, u_char off) +readhscx(unsigned long adr, int hscx, u_char off) { register unsigned int portdata; @@ -115,7 +121,7 @@ } static inline void -writehscx(unsigned int adr, int hscx, u_char off, u_char data) +writehscx(unsigned long adr, int hscx, u_char off, u_char data) { register unsigned int portdata; @@ -130,7 +136,7 @@ } static inline void -read_fifo_isac(unsigned int adr, u_char * data, int size) +read_fifo_isac(unsigned long adr, u_char * data, int size) { register unsigned int portdata; register int i; @@ -148,7 +154,7 @@ } static void -write_fifo_isac(unsigned int adr, u_char * data, int size) +write_fifo_isac(unsigned long adr, u_char * data, int size) { register unsigned int portdata; register int i; @@ -165,7 +171,7 @@ } static inline void -read_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size) +read_fifo_hscx(unsigned long adr, int hscx, u_char * data, int size) { register unsigned int portdata; register int i; @@ -183,7 +189,7 @@ } static inline void -write_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size) +write_fifo_hscx(unsigned long adr, int hscx, u_char * data, int size) { unsigned int portdata; register int i; @@ -324,7 +330,7 @@ printk(KERN_WARNING "Teles: No IRQ for PCI card found\n"); return(0); } - cs->hw.teles0.membase = (u_int) ioremap(dev_tel->base_address[ 0], + cs->hw.teles0.membase = (u_long) ioremap(dev_tel->base_address[ 0], PAGE_SIZE); printk(KERN_INFO "Found: Zoran, base-address: 0x%lx, irq: 0x%x\n", dev_tel->base_address[ 0], dev_tel->irq); @@ -348,7 +354,7 @@ /* writel(0x00800000, cs->hw.teles0.membase + 0x200); */ printk(KERN_INFO - "HiSax: %s config irq:%d mem:%x\n", + "HiSax: %s config irq:%d mem:%lx\n", CardType[cs->typ], cs->irq, cs->hw.teles0.membase); diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c --- v2.2.13/linux/drivers/isdn/isdn_common.c Tue Jan 4 11:10:35 2000 +++ linux/drivers/isdn/isdn_common.c Tue Jan 4 10:12:16 2000 @@ -1,4 +1,4 @@ -/* $Id: isdn_common.c,v 1.87 1999/09/12 16:19:39 detabc Exp $ +/* $Id: isdn_common.c,v 1.93 1999/11/04 13:11:36 keil Exp $ * Linux ISDN subsystem, common used functions (linklevel). * @@ -21,6 +21,31 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_common.c,v $ + * Revision 1.93 1999/11/04 13:11:36 keil + * Reinit of v110 structs + * + * Revision 1.92 1999/10/31 15:59:50 he + * more skb headroom checks + * + * Revision 1.91 1999/10/28 22:48:45 armin + * Bugfix: isdn_free_channel() now frees the channel, + * even when the usage of the ttyI has changed. + * + * Revision 1.90 1999/10/27 21:21:17 detabc + * Added support for building logically-bind-group's per interface. + * usefull for outgoing call's with more then one isdn-card. + * + * Switchable support to dont reset the hangup-timeout for + * receive frames. Most part's of the timru-rules for receiving frames + * are now obsolete. If the input- or forwarding-firewall deny + * the frame, the line will be not hold open. + * + * Revision 1.89 1999/10/16 14:46:47 keil + * replace kmalloc with vmalloc for the big dev struct + * + * Revision 1.88 1999/10/02 00:39:26 he + * Fixed a 2.3.x wait queue initialization (was causing panics) + * * Revision 1.87 1999/09/12 16:19:39 detabc * added abc features * low cost routing for net-interfaces (only the HL side). @@ -371,6 +396,7 @@ #include #include #include +#include #include #include "isdn_common.h" #include "isdn_tty.h" @@ -390,7 +416,7 @@ isdn_dev *dev = (isdn_dev *) 0; -static char *isdn_revision = "$Revision: 1.87 $"; +static char *isdn_revision = "$Revision: 1.93 $"; extern char *isdn_net_revision; extern char *isdn_tty_revision; @@ -2098,13 +2124,19 @@ save_flags(flags); cli(); for (i = 0; i < ISDN_MAX_CHANNELS; i++) - if (((dev->usage[i] & ISDN_USAGE_MASK) == usage) && + if (((!usage) || ((dev->usage[i] & ISDN_USAGE_MASK) == usage)) && (dev->drvmap[i] == di) && (dev->chanmap[i] == ch)) { dev->usage[i] &= (ISDN_USAGE_NONE | ISDN_USAGE_EXCLUSIVE); strcpy(dev->num[i], "???"); dev->ibytes[i] = 0; dev->obytes[i] = 0; +// 20.10.99 JIM, try to reinitialize v110 ! + dev->v110emu[i] = 0; + atomic_set(&(dev->v110use[i]), 0); + isdn_v110_close(dev->v110[i]); + dev->v110[i] = NULL; +// 20.10.99 JIM, try to reinitialize v110 ! isdn_info_update(); isdn_free_queue(&dev->drv[di]->rpqueue[ch]); } @@ -2186,8 +2218,33 @@ /* V.110 must always be acknowledged */ ack = 1; ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, nskb); - } else - ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, skb); + } else { + int hl = dev->drv[drvidx]->interface->hl_hdrlen; + + if( skb_headroom(skb) < hl ){ + /* + * This should only occur when new HL driver with + * increased hl_hdrlen was loaded after netdevice + * was created and connected to the new driver. + * + * The V.110 branch (re-allocates on its own) does + * not need this + */ + struct sk_buff * skb_tmp; + + skb_tmp = skb_realloc_headroom(skb, hl); + printk(KERN_DEBUG "isdn_writebuf_skb_stub: reallocating headroom%s\n", skb_tmp ? "" : " failed"); + if (!skb_tmp) return -ENOMEM; /* 0 better? */ + ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, skb_tmp); + if( ret > 0 ){ + dev_kfree_skb(skb); + } else { + dev_kfree_skb(skb_tmp); + } + } else { + ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, skb); + } + } if (ret > 0) { dev->obytes[idx] += ret; if (dev->v110[idx]) { @@ -2470,8 +2527,7 @@ int i; char tmprev[50]; - sti(); - if (!(dev = (isdn_dev *) kmalloc(sizeof(isdn_dev), GFP_KERNEL))) { + if (!(dev = (isdn_dev *) vmalloc(sizeof(isdn_dev)))) { printk(KERN_WARNING "isdn: Could not allocate device-struct.\n"); return -EIO; } @@ -2487,7 +2543,7 @@ } if (register_chrdev(ISDN_MAJOR, "isdn", &isdn_fops)) { printk(KERN_WARNING "isdn: Could not register control devices\n"); - kfree(dev); + vfree(dev); return -EIO; } if ((i = isdn_tty_modem_init()) < 0) { @@ -2496,7 +2552,7 @@ tty_unregister_driver(&dev->mdm.cua_modem); if (i <= -2) tty_unregister_driver(&dev->mdm.tty_modem); - kfree(dev); + vfree(dev); unregister_chrdev(ISDN_MAJOR, "isdn"); return -EIO; } @@ -2508,7 +2564,7 @@ for (i = 0; i < ISDN_MAX_CHANNELS; i++) kfree(dev->mdm.info[i].xmit_buf - 4); unregister_chrdev(ISDN_MAJOR, "isdn"); - kfree(dev); + vfree(dev); return -EIO; } #endif /* CONFIG_ISDN_PPP */ @@ -2577,7 +2633,7 @@ printk(KERN_WARNING "isdn: controldevice busy, remove cancelled\n"); } else { del_timer(&dev->timer); - kfree(dev); + vfree(dev); printk(KERN_NOTICE "ISDN-subsystem unloaded\n"); } restore_flags(flags); diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/isdn_common.h linux/drivers/isdn/isdn_common.h --- v2.2.13/linux/drivers/isdn/isdn_common.h Tue Jan 4 11:10:35 2000 +++ linux/drivers/isdn/isdn_common.h Tue Jan 4 10:12:16 2000 @@ -1,4 +1,4 @@ -/* $Id: isdn_common.h,v 1.16 1999/07/01 08:29:54 keil Exp $ +/* $Id: isdn_common.h,v 1.17 1999/10/27 21:21:17 detabc Exp $ * header for Linux ISDN subsystem, common used functions and debugging-switches (linklevel). * @@ -21,6 +21,15 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_common.h,v $ + * Revision 1.17 1999/10/27 21:21:17 detabc + * Added support for building logically-bind-group's per interface. + * usefull for outgoing call's with more then one isdn-card. + * + * Switchable support to dont reset the hangup-timeout for + * receive frames. Most part's of the timru-rules for receiving frames + * are now obsolete. If the input- or forwarding-firewall deny + * the frame, the line will be not hold open. + * * Revision 1.16 1999/07/01 08:29:54 keil * compatibility to 2.3 kernel * diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/isdn_net.c linux/drivers/isdn/isdn_net.c --- v2.2.13/linux/drivers/isdn/isdn_net.c Tue Jan 4 11:10:35 2000 +++ linux/drivers/isdn/isdn_net.c Tue Jan 4 10:12:16 2000 @@ -1,4 +1,4 @@ -/* $Id: isdn_net.c,v 1.92 1999/09/13 23:25:17 he Exp $ +/* $Id: isdn_net.c,v 1.95 1999/10/27 21:21:17 detabc Exp $ * Linux ISDN subsystem, network interfaces and related functions (linklevel). * @@ -21,6 +21,24 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_net.c,v $ + * Revision 1.95 1999/10/27 21:21:17 detabc + * Added support for building logically-bind-group's per interface. + * usefull for outgoing call's with more then one isdn-card. + * + * Switchable support to dont reset the hangup-timeout for + * receive frames. Most part's of the timru-rules for receiving frames + * are now obsolete. If the input- or forwarding-firewall deny + * the frame, the line will be not hold open. + * + * Revision 1.94 1999/10/02 11:07:02 he + * Changed tbusy logic in indn_net.c + * + * Revision 1.93 1999/09/23 22:22:41 detabc + * added tcp-keepalive-detect with local response (ipv4 only) + * added host-only-interface support + * (source ipaddr == interface ipaddr) (ipv4 only) + * ok with kernel 2.3.18 and 2.2.12 + * * Revision 1.92 1999/09/13 23:25:17 he * serialized xmitting frames from isdn_ppp and BSENT statcallb * @@ -375,6 +393,49 @@ #endif +#ifndef ISDN_NEW_TBUSY +#define ISDN_NEW_TBUSY +#endif +#ifdef ISDN_NEW_TBUSY +/* + * Outline of new tbusy handling: + * + * Old method, roughly spoken, consisted of setting tbusy when entering + * isdn_net_start_xmit() and at several other locations and clearing + * it from isdn_net_start_xmit() thread when sending was successful. + * + * With 2.3.x multithreaded network core, to prevent problems, tbusy should + * only be set by the isdn_net_start_xmit() thread and only when a tx-busy + * condition is detected. Other threads (in particular isdn_net_stat_callb()) + * are only allowed to clear tbusy. + * + * -HE + */ + +/* + * Tell upper layers that the network device is ready to xmit more frames. + */ +static void __inline__ isdn_net_dev_xon(struct device * dev) +{ + dev->tbusy = 0; + mark_bh(NET_BH); +} + +static void __inline__ isdn_net_lp_xon(isdn_net_local * lp) +{ + lp->netdev->dev.tbusy = 0; + if(lp->master) lp->master->tbusy = 0; + mark_bh(NET_BH); +} + +/* + * Ask upper layers to temporarily cease passing us more xmit frames. + */ +static void __inline__ isdn_net_dev_xoff(struct device * dev) +{ + dev->tbusy = 1; +} +#endif /* Prototypes */ @@ -382,7 +443,7 @@ 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.92 $"; +char *isdn_net_revision = "$Revision: 1.95 $"; /* * Code for raw-networking over ISDN @@ -424,7 +485,11 @@ save_flags(flags); cli(); /* Avoid glitch on writes to CMD regs */ dev->interrupt = 0; +#ifdef ISDN_NEW_TBUSY + isdn_net_dev_xon(dev); +#else dev->tbusy = 0; +#endif #ifdef CONFIG_ISDN_X25 if( cprot && cprot -> pops && dops ) cprot -> pops -> restart ( cprot, dev, dops ); @@ -638,13 +703,19 @@ mdev = &lp->netdev->dev; if (!isdn_net_send_skb(mdev, lp, lp->sav_skb)) { lp->sav_skb = NULL; +#ifndef ISDN_NEW_TBUSY mark_bh(NET_BH); +#endif } else { return 1; } } +#ifdef ISDN_NEW_TBUSY + isdn_net_lp_xon(lp); +#else if (test_and_clear_bit(0, (void *) &(p->dev.tbusy))) mark_bh(NET_BH); +#endif } return 1; case ISDN_STAT_DCONN: @@ -727,7 +798,6 @@ lp->dialstarted = 0; lp->dialwait_timer = 0; - /* Immediately send first skb to speed up arp */ #ifdef CONFIG_ISDN_PPP if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) isdn_ppp_wakeup_daemon(lp); @@ -738,11 +808,15 @@ 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; } +#ifdef ISDN_NEW_TBUSY + if(! lp->first_skb) isdn_net_lp_xon(lp); +#else else { /* * dev.tbusy is usually cleared implicitly by isdn_net_xmit(,,lp->first_skb). @@ -751,6 +825,7 @@ lp->netdev->dev.tbusy = 0; mark_bh(NET_BH); } +#endif /* ISDN_NEW_TBUSY */ return 1; } break; @@ -1185,6 +1260,7 @@ break; } printk(KERN_INFO "OPEN: %d.%d.%d.%d -> %d.%d.%d.%d%s\n", + p[12], p[13], p[14], p[15], p[16], p[17], p[18], p[19], addinfo); @@ -1203,8 +1279,12 @@ * standard send-routine, else send directly. * * Return: 0 on success, !0 on failure. + */ +#ifndef ISDN_NEW_TBUSY +/* * Side-effects: ndev->tbusy is cleared on success. */ +#endif int isdn_net_send_skb(struct device *ndev, isdn_net_local * lp, struct sk_buff *skb) @@ -1215,13 +1295,17 @@ ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb); if (ret == len) { lp->transcount += len; +#ifndef ISDN_NEW_TBUSY clear_bit(0, (void *) &(ndev->tbusy)); +#endif return 0; } if (ret < 0) { dev_kfree_skb(skb); lp->stats.tx_errors++; +#ifndef ISDN_NEW_TBUSY clear_bit(0, (void *) &(ndev->tbusy)); +#endif return 0; } return 1; @@ -1267,7 +1351,11 @@ if (lp->srobin == ndev) ret = isdn_net_send_skb(ndev, lp, skb); else +#ifdef ISDN_NEW_TBUSY + ret = isdn_net_start_xmit(skb, lp->srobin); +#else ret = ndev->tbusy = isdn_net_start_xmit(skb, lp->srobin); +#endif lp->srobin = (slp->slave) ? slp->slave : ndev; slp = (isdn_net_local *) (lp->srobin->priv); if (!((slp->flags & ISDN_NET_CONNECTED) && (slp->dialstate == 0))) @@ -1327,8 +1415,13 @@ if (!lp->dialstate) lp->stats.tx_errors++; ndev->trans_start = jiffies; +#ifdef ISDN_NEW_TBUSY + isdn_net_dev_xon(ndev); +#endif } +#ifndef ISDN_NEW_TBUSY ndev->tbusy = 1; /* left instead of obsolete test_and_set_bit() */ +#endif #ifdef CONFIG_ISDN_X25 /* At this point hard_start_xmit() passes control to the encapsulation protocol (if present). @@ -1342,7 +1435,11 @@ when a dl_establish request is received from the upper layer. */ if( cprot ) { - return cprot -> pops -> encap_and_xmit ( cprot , skb); + int ret = cprot -> pops -> encap_and_xmit ( cprot , skb); +#ifdef ISDN_NEW_TBUSY + if(ret) isdn_net_dev_xoff(ndev); +#endif + return ret; } else #endif /* auto-dialing xmit function */ @@ -1361,7 +1458,9 @@ if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) { isdn_net_unreachable(ndev, skb, "dial rejected: interface not in dialmode `auto'"); dev_kfree_skb(skb); +#ifndef ISDN_NEW_TBUSY ndev->tbusy = 0; +#endif return 0; } if (lp->phone[1]) { @@ -1377,7 +1476,9 @@ if(jiffies < lp->dialwait_timer) { isdn_net_unreachable(ndev, skb, "dial rejected: retry-time not reached"); dev_kfree_skb(skb); +#ifndef ISDN_NEW_TBUSY ndev->tbusy = 0; +#endif restore_flags(flags); return 0; } else @@ -1386,22 +1487,28 @@ /* Grab a free ISDN-Channel */ if (((chi = - isdn_get_free_channel(ISDN_USAGE_NET, - lp->l2_proto, - lp->l3_proto, - lp->pre_device, - lp->pre_channel)) < 0) && + isdn_get_free_channel( + ISDN_USAGE_NET, + lp->l2_proto, + lp->l3_proto, + lp->pre_device, + lp->pre_channel) + ) < 0) && ((chi = - isdn_get_free_channel(ISDN_USAGE_NET, - lp->l2_proto, - lp->l3_proto, - lp->pre_device, - lp->pre_channel^1)) < 0)) { + isdn_get_free_channel( + ISDN_USAGE_NET, + lp->l2_proto, + lp->l3_proto, + lp->pre_device, + lp->pre_channel^1) + ) < 0)) { restore_flags(flags); isdn_net_unreachable(ndev, skb, "No channel"); dev_kfree_skb(skb); +#ifndef ISDN_NEW_TBUSY ndev->tbusy = 0; +#endif return 0; } /* Log packet, which triggered dialing */ @@ -1421,6 +1528,9 @@ } restore_flags(flags); isdn_net_dial(); /* Initiate dialing */ +#ifdef ISDN_NEW_TBUSY + isdn_net_dev_xoff(ndev); +#endif return 1; /* let upper layer requeue skb packet */ } #endif @@ -1434,7 +1544,9 @@ } lp->first_skb = skb; /* Initiate dialing */ +#ifndef ISDN_NEW_TBUSY ndev->tbusy = 0; +#endif restore_flags(flags); isdn_net_dial(); return 0; @@ -1442,21 +1554,37 @@ isdn_net_unreachable(ndev, skb, "No phone number"); dev_kfree_skb(skb); +#ifndef ISDN_NEW_TBUSY ndev->tbusy = 0; +#endif return 0; } } else { - /* Connection is established, try sending */ + /* Device is connected to an ISDN channel */ ndev->trans_start = jiffies; if (!lp->dialstate) { + /* ISDN connection is established, try sending */ + int ret; if (lp->first_skb) { - if (isdn_net_xmit(ndev, lp, lp->first_skb)) + if (isdn_net_xmit(ndev, lp, lp->first_skb)){ +#ifdef ISDN_NEW_TBUSY + isdn_net_dev_xoff(ndev); +#endif return 1; +} lp->first_skb = NULL; } - return (isdn_net_xmit(ndev, lp, skb)); + ret = (isdn_net_xmit(ndev, lp, skb)); +#ifdef ISDN_NEW_TBUSY + if(ret) isdn_net_dev_xoff(ndev); +#endif + return ret; } else +#ifdef ISDN_NEW_TBUSY + isdn_net_dev_xoff(ndev); +#else ndev->tbusy = 1; +#endif } } return 1; @@ -2068,7 +2196,6 @@ isdn_net_phone *n; ulong flags; char nr[32]; - /* Search name in netdev-chain */ save_flags(flags); cli(); @@ -2280,10 +2407,15 @@ lp->name, nr, eaz); if (lp->phone[1]) { /* Grab a free ISDN-Channel */ - if ((chi = isdn_get_free_channel(ISDN_USAGE_NET, lp->l2_proto, + if ((chi = + isdn_get_free_channel( + ISDN_USAGE_NET, + lp->l2_proto, lp->l3_proto, - lp->pre_device, - lp->pre_channel)) < 0) { + lp->pre_device, + lp->pre_channel) + ) < 0) { + printk(KERN_WARNING "isdn_net_find_icall: No channel for %s\n", lp->name); restore_flags(flags); return 0; @@ -2390,10 +2522,14 @@ cli(); /* Grab a free ISDN-Channel */ - if ((chi = isdn_get_free_channel(ISDN_USAGE_NET, lp->l2_proto, - lp->l3_proto, - lp->pre_device, - lp->pre_channel)) < 0) { + if ((chi = + isdn_get_free_channel( + ISDN_USAGE_NET, + lp->l2_proto, + lp->l3_proto, + lp->pre_device, + lp->pre_channel) + ) < 0) { printk(KERN_WARNING "isdn_net_force_dial: No channel for %s\n", lp->name); restore_flags(flags); return -EAGAIN; @@ -3153,4 +3289,3 @@ restore_flags(flags); return 0; } - diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/isdn_ppp.c linux/drivers/isdn/isdn_ppp.c --- v2.2.13/linux/drivers/isdn/isdn_ppp.c Tue Jan 4 11:10:35 2000 +++ linux/drivers/isdn/isdn_ppp.c Tue Jan 4 10:12:16 2000 @@ -1,4 +1,4 @@ -/* $Id: isdn_ppp.c,v 1.54 1999/09/13 23:25:17 he Exp $ +/* $Id: isdn_ppp.c,v 1.60 1999/11/04 20:29:55 he Exp $ * * Linux ISDN subsystem, functions for synchronous PPP (linklevel). * @@ -19,6 +19,28 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_ppp.c,v $ + * Revision 1.60 1999/11/04 20:29:55 he + * applied Andre Beck's reset_free fix + * + * Revision 1.59 1999/10/31 15:59:50 he + * more skb headroom checks + * + * Revision 1.58 1999/10/30 13:13:01 keil + * Henners isdn_ppp_skb_push:under fix + * + * Revision 1.57 1999/10/05 22:47:17 he + * Removed dead ISDN_SYNCPPP_READDRESS code (obsoleted by sysctl_ip_dynaddr + * and network address translation) + * + * Revision 1.56 1999/09/29 16:01:06 he + * replaced dev_alloc_skb() for downstream skbs by equivalent alloc_skb() + * + * Revision 1.55 1999/09/23 22:07:51 detabc + * + * make ipc_head common usable (for use compressor with raw-ip) + * add function before netif_rx(). needed for ipv4-tcp-keepalive-detect. + * ~ + * * Revision 1.54 1999/09/13 23:25:17 he * serialized xmitting frames from isdn_ppp and BSENT statcallb * @@ -218,10 +240,6 @@ /* TODO: right tbusy handling when using MP */ -/* - * experimental for dynamic addressing: readdress IP frames - */ -#undef ISDN_SYNCPPP_READDRESS #define CONFIG_ISDN_CCP 1 #include @@ -264,6 +282,7 @@ unsigned char code, unsigned char id, unsigned char *data, int len); static struct ippp_ccp_reset *isdn_ppp_ccp_reset_alloc(struct ippp_struct *is); +static void isdn_ppp_ccp_reset_free(struct ippp_struct *is); static void isdn_ppp_ccp_reset_free_state(struct ippp_struct *is, unsigned char id); static void isdn_ppp_ccp_timer_callback(unsigned long closure); @@ -287,9 +306,10 @@ static void isdn_ppp_free_mpqueue(isdn_net_dev *); #endif -char *isdn_ppp_revision = "$Revision: 1.54 $"; +char *isdn_ppp_revision = "$Revision: 1.60 $"; static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS]; + static struct isdn_ppp_compressor *ipc_head = NULL; /* @@ -360,10 +380,6 @@ printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_slot, (long) lp, (long) is->lp); is->lp = NULL; /* link is down .. set lp to NULL */ -#ifdef ISDN_SYNCPPP_READDRESS - is->old_pa_addr = 0x0; - is->old_pa_dstaddr = 0x0; -#endif lp->ppp_slot = -1; /* is this OK ?? */ restore_flags(flags); @@ -608,9 +624,9 @@ is->comp_stat = is->link_comp_stat = NULL; is->decomp_stat = is->link_decomp_stat = NULL; + /* Clean up if necessary */ if(is->reset) - kfree(is->reset); - is->reset = NULL; + isdn_ppp_ccp_reset_free(is); /* this slot is ready for new connections */ is->state = 0; @@ -1462,12 +1478,6 @@ ipts = ippp_table[mlp->ppp_slot]; if (!(ipts->pppcfg & SC_ENABLE_IP)) { /* PPP connected ? */ -#ifdef ISDN_SYNCPPP_READDRESS - if (!ipts->old_pa_addr) - ipts->old_pa_addr = mdev->pa_addr; - if (!ipts->old_pa_dstaddr) - ipts->old_pa_dstaddr = mdev->pa_dstaddr; -#endif if (ipts->debug & 0x1) printk(KERN_INFO "%s: IP frame delayed.\n", netdev->name); return 1; @@ -1476,21 +1486,6 @@ switch (ntohs(skb->protocol)) { case ETH_P_IP: proto = PPP_IP; -#ifdef ISDN_SYNCPPP_READDRESS - if (ipts->old_pa_addr != mdev->pa_addr) { - struct iphdr *ipfr; - ipfr = (struct iphdr *) skb->data; - if(ipts->debug & 0x4) - printk(KERN_DEBUG "IF-address changed from %lx to %lx\n", ipts->old_pa_addr, mdev->pa_addr); - if (ipfr->version == 4) { - if (ipfr->saddr == ipts->old_pa_addr) { - printk(KERN_DEBUG "readdressing %lx to %lx\n", ipfr->saddr, mdev->pa_addr); - ipfr->saddr = mdev->pa_addr; - } - } - } - /* dstaddr change not so important */ -#endif break; case ETH_P_IPX: proto = PPP_IPX; /* untested */ @@ -1521,8 +1516,6 @@ /* Pull off the fake header we stuck on earlier to keep * the fragemntation code happy. - * this will break the ISDN_SYNCPPP_READDRESS hack a few lines - * above. So, enabling this is no longer allowed */ skb_pull(skb,IPPP_MAX_HEADER); @@ -1540,7 +1533,13 @@ * sk_buff. old call to dev_alloc_skb only reserved * 16 bytes, now we are looking what the driver want. */ - hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen; + hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen + IPPP_MAX_HEADER; + /* + * Note: hl might still be insufficient because the method + * above does not account for a possibible MPPP slave channel + * which had larger HL header space requirements than the + * master. + */ new_skb = alloc_skb(hl+skb->len, GFP_ATOMIC); if (new_skb) { u_char *buf; @@ -2227,18 +2226,20 @@ { struct sk_buff *skb; unsigned char *p; - int count; + int count, hl; unsigned long flags; int cnt = 0; isdn_net_local *lp = is->lp; /* Alloc large enough skb */ - skb = dev_alloc_skb(len + 16); + hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen; + skb = alloc_skb(len + hl + 16,GFP_ATOMIC); if(!skb) { printk(KERN_WARNING "ippp: CCP cannot send reset - out of memory\n"); return; } + skb_reserve(skb, hl); /* We may need to stuff an address and control field first */ if(!(is->pppcfg & SC_COMP_AC)) { @@ -2293,15 +2294,34 @@ static struct ippp_ccp_reset *isdn_ppp_ccp_reset_alloc(struct ippp_struct *is) { struct ippp_ccp_reset *r; - printk(KERN_DEBUG "ippp_ccp: allocating reset data structure\n"); r = kmalloc(sizeof(struct ippp_ccp_reset), GFP_KERNEL); - if(!r) + if(!r) { + printk(KERN_ERR "ippp_ccp: failed to allocate reset data" + " structure - no mem\n"); return NULL; + } memset(r, 0, sizeof(struct ippp_ccp_reset)); + printk(KERN_DEBUG "ippp_ccp: allocated reset data structure %p\n", r); is->reset = r; return r; } +/* Destroy the reset state vector. Kill all pending timers first. */ +static void isdn_ppp_ccp_reset_free(struct ippp_struct *is) +{ + unsigned int id; + + printk(KERN_DEBUG "ippp_ccp: freeing reset data structure %p\n", + is->reset); + for(id = 0; id < 256; id++) { + if(is->reset->rs[id]) { + isdn_ppp_ccp_reset_free_state(is, (unsigned char)id); + } + } + kfree(is->reset); + is->reset = NULL; +} + /* Free a given state and clear everything up for later reallocation */ static void isdn_ppp_ccp_reset_free_state(struct ippp_struct *is, unsigned char id) @@ -2667,9 +2687,11 @@ } /* Allow for at least 150 % expansion (for now) */ - skb_out = dev_alloc_skb(skb_in->len + skb_in->len/2 + 32); + skb_out = alloc_skb(skb_in->len + skb_in->len/2 + 32 + + skb_headroom(skb_in), GFP_ATOMIC); if(!skb_out) return skb_in; + skb_reserve(skb_out, skb_headroom(skb_in)); ret = (compressor->compress)(stat,skb_in,skb_out,*proto); if(!ret) { @@ -2898,7 +2920,6 @@ } } - int isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc) { ipc->next = ipc_head; @@ -2932,6 +2953,16 @@ if(is->debug & 0x10) printk(KERN_DEBUG "[%d] Set %s type %d\n",is->unit, (data->flags&IPPP_COMP_FLAG_XMIT)?"compressor":"decompressor",num); + + /* If is has no valid reset state vector, we cannot allocate a + decompressor. The decompressor would cause reset transactions + sooner or later, and they need that vector. */ + + if(!(data->flags & IPPP_COMP_FLAG_XMIT) && !is->reset) { + printk(KERN_ERR "ippp_ccp: no reset data structure - can't" + " allow decompression.\n"); + return -ENOMEM; + } while(ipc) { if(ipc->num == num) { diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/isdn_tty.c linux/drivers/isdn/isdn_tty.c --- v2.2.13/linux/drivers/isdn/isdn_tty.c Tue Jan 4 11:10:35 2000 +++ linux/drivers/isdn/isdn_tty.c Tue Jan 4 10:12:17 2000 @@ -1,4 +1,4 @@ -/* $Id: isdn_tty.c,v 1.74 1999/09/04 06:20:04 keil Exp $ +/* $Id: isdn_tty.c,v 1.80 1999/11/07 13:34:30 armin Exp $ * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel). * @@ -20,6 +20,27 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_tty.c,v $ + * Revision 1.80 1999/11/07 13:34:30 armin + * Fixed AT command line editor + * + * Revision 1.79 1999/10/29 18:35:08 armin + * Check number len in isdn_get_msnstr() to avoid buffer overflow. + * + * Revision 1.78 1999/10/28 23:03:51 armin + * Bugfix: now freeing channel on modem_hup() even when + * usage on ttyI has changed and error-report for + * AT-commands on wrong channel-state. + * + * Revision 1.77 1999/10/26 21:13:14 armin + * using define for checking phone number len in isdn_tty_getdial() + * + * Revision 1.76 1999/10/11 22:16:26 keil + * Suspend/Resume is possible without explicit ID too + * + * Revision 1.75 1999/10/08 18:59:32 armin + * Bugfix of too small MSN buffer and checking phone number + * in isdn_tty_getdial() + * * Revision 1.74 1999/09/04 06:20:04 keil * Changes from kernel set_current_state() * @@ -351,7 +372,7 @@ static int si2bit[8] = {4, 1, 4, 4, 4, 4, 4, 4}; -char *isdn_tty_revision = "$Revision: 1.74 $"; +char *isdn_tty_revision = "$Revision: 1.80 $"; /* isdn_tty_try_read() is called from within isdn_tty_rcv_skb() @@ -1017,7 +1038,6 @@ isdn_tty_modem_hup(modem_info * info, int local) { isdn_ctrl cmd; - int usage; if (!info) return; @@ -1071,10 +1091,7 @@ } isdn_all_eaz(info->isdn_driver, info->isdn_channel); info->emu.mdmreg[REG_RINGCNT] = 0; - usage = isdn_calc_usage(info->emu.mdmreg[REG_SI1I], - info->emu.mdmreg[REG_L2PROT]); - isdn_free_channel(info->isdn_driver, info->isdn_channel, - usage); + isdn_free_channel(info->isdn_driver, info->isdn_channel, 0); } info->isdn_driver = -1; info->isdn_channel = -1; @@ -1111,8 +1128,8 @@ printk(KERN_DEBUG "Msusp ttyI%d\n", info->line); #endif l = strlen(id); - if ((info->isdn_driver >= 0) && l) { - cmd.parm.cmsg.Length = l+17; + if ((info->isdn_driver >= 0)) { + cmd.parm.cmsg.Length = l+18; cmd.parm.cmsg.Command = CAPI_FACILITY; cmd.parm.cmsg.Subcommand = CAPI_REQ; cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1; @@ -1150,10 +1167,6 @@ int l; l = strlen(id); - if (!l) { - isdn_tty_modem_result(4, info); - return; - } for (j = 7; j >= 0; j--) if (m->mdmreg[REG_SI1] & (1 << j)) { si = bit2si[j]; @@ -1207,7 +1220,7 @@ isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.arg = info->isdn_channel; - cmd.parm.cmsg.Length = l+17; + cmd.parm.cmsg.Length = l+18; cmd.parm.cmsg.Command = CAPI_FACILITY; cmd.parm.cmsg.Subcommand = CAPI_REQ; cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1; @@ -3091,9 +3104,12 @@ static void isdn_tty_get_msnstr(char *n, char **p) { - while ((*p[0] >= '0' && *p[0] <= '9') || + int limit = ISDN_MSNLEN - 1; + + while (((*p[0] >= '0' && *p[0] <= '9') || /* Why a comma ??? */ - (*p[0] == ',')) + (*p[0] == ',')) && + (limit--)) *n++ = *p[0]++; *n = '\0'; } @@ -3105,16 +3121,18 @@ isdn_tty_getdial(char *p, char *q,int cnt) { int first = 1; - int limit=39; /* MUST match the size in isdn_tty_parse to avoid - buffer overflow */ + int limit = ISDN_MSNLEN - 1; /* MUST match the size of interface var to avoid + buffer overflow */ while (strchr(" 0123456789,#.*WPTS-", *p) && *p && --cnt>0) { if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first) || - (*p == '*') || (*p == '#')) + (*p == '*') || (*p == '#')) { *q++ = *p; - p++; - if(!--limit) + limit--; + } + if(!limit) break; + p++; first = 0; } *q = 0; @@ -3258,6 +3276,8 @@ case 'F': /* &F -Set Factory-Defaults */ p[0]++; + if (info->msr & UART_MSR_DCD) + PARSE_ERROR1; isdn_tty_reset_profile(m); isdn_tty_modem_reset_regs(info, 1); break; @@ -3909,6 +3929,12 @@ break; case 'D': /* D - Dial */ + if (info->msr & UART_MSR_DCD) + PARSE_ERROR; + if (info->msr & UART_MSR_RI) { + isdn_tty_modem_result(3, info); + return; + } isdn_tty_getdial(++p, ds, sizeof ds); p += strlen(p); if (!strlen(m->msn)) @@ -4103,7 +4129,7 @@ c = *p; total++; if (c == m->mdmreg[REG_CR] || c == m->mdmreg[REG_LF]) { - /* Separator (CR oder LF) */ + /* Separator (CR or LF) */ m->mdmcmd[m->mdmcmdl] = 0; if (m->mdmreg[REG_ECHO] & BIT_ECHO) { eb[0] = c; @@ -4116,7 +4142,7 @@ continue; } if (c == m->mdmreg[REG_BS] && m->mdmreg[REG_BS] < 128) { - /* Backspace-Funktion */ + /* Backspace-Function */ if ((m->mdmcmdl > 2) || (!m->mdmcmdl)) { if (m->mdmcmdl) m->mdmcmdl--; @@ -4134,18 +4160,24 @@ if (m->mdmcmdl < 255) { c = my_toupper(c); switch (m->mdmcmdl) { - case 0: - if (c == 'A') - m->mdmcmd[m->mdmcmdl] = c; - break; case 1: - if (c == 'T') + if (c == 'T') { + m->mdmcmd[m->mdmcmdl] = c; + m->mdmcmd[++m->mdmcmdl] = 0; + break; + } else + m->mdmcmdl = 0; + /* Fall through, check for 'A' */ + case 0: + if (c == 'A') { m->mdmcmd[m->mdmcmdl] = c; + m->mdmcmd[++m->mdmcmdl] = 0; + } break; default: m->mdmcmd[m->mdmcmdl] = c; + m->mdmcmd[++m->mdmcmdl] = 0; } - m->mdmcmd[++m->mdmcmdl] = 0; } } } diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/isdn_v110.c linux/drivers/isdn/isdn_v110.c --- v2.2.13/linux/drivers/isdn/isdn_v110.c Tue Jan 4 11:10:35 2000 +++ linux/drivers/isdn/isdn_v110.c Tue Jan 4 10:12:17 2000 @@ -1,4 +1,4 @@ -/* $Id: isdn_v110.c,v 1.2 1998/02/22 19:44:25 fritz Exp $ +/* $Id: isdn_v110.c,v 1.3 1999/10/30 09:49:28 keil Exp $ * Linux ISDN subsystem, V.110 related functions (linklevel). * @@ -19,6 +19,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_v110.c,v $ + * Revision 1.3 1999/10/30 09:49:28 keil + * Reinit of v110 structs + * * Revision 1.2 1998/02/22 19:44:25 fritz * Bugfixes and improvements regarding V.110, V.110 now running. * @@ -36,7 +39,7 @@ #undef ISDN_V110_DEBUG -char *isdn_v110_revision = "$Revision: 1.2 $"; +char *isdn_v110_revision = "$Revision: 1.3 $"; #define V110_38400 255 #define V110_19200 15 @@ -148,7 +151,7 @@ } /* isdn_v110_close frees private V.110 data structures */ -static void +void isdn_v110_close(isdn_v110_stream * v) { if (v == NULL) diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/isdn_v110.h linux/drivers/isdn/isdn_v110.h --- v2.2.13/linux/drivers/isdn/isdn_v110.h Wed Apr 1 16:21:03 1998 +++ linux/drivers/isdn/isdn_v110.h Tue Jan 4 10:12:17 2000 @@ -1,4 +1,4 @@ -/* $Id: isdn_v110.h,v 1.1 1998/02/20 17:32:11 fritz Exp $ +/* $Id: isdn_v110.h,v 1.2 1999/10/30 09:49:28 keil Exp $ * Linux ISDN subsystem, V.110 related functions (linklevel). * @@ -19,6 +19,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_v110.h,v $ + * Revision 1.2 1999/10/30 09:49:28 keil + * Reinit of v110 structs + * * Revision 1.1 1998/02/20 17:32:11 fritz * First checkin (not yet completely functionable). * @@ -41,5 +44,6 @@ extern struct sk_buff *isdn_v110_decode(isdn_v110_stream *, struct sk_buff *); extern int isdn_v110_stat_callback(int, isdn_ctrl *); +extern void isdn_v110_close(isdn_v110_stream * v); #endif diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/pcbit/drv.c linux/drivers/isdn/pcbit/drv.c --- v2.2.13/linux/drivers/isdn/pcbit/drv.c Tue Jan 4 11:10:35 2000 +++ linux/drivers/isdn/pcbit/drv.c Tue Jan 4 10:12:17 2000 @@ -87,9 +87,11 @@ dev_pcbit[board] = dev; memset(dev, 0, sizeof(struct pcbit_dev)); - if (mem_base >= 0xA0000 && mem_base <= 0xFFFFF ) + if (mem_base >= 0xA0000 && mem_base <= 0xFFFFF ) { + dev->ph_mem = mem_base; dev->sh_mem = (unsigned char*) mem_base; - else + } + else { printk("memory address invalid"); kfree(dev); diff -u --recursive --new-file v2.2.13/linux/drivers/isdn/pcbit/pcbit.h linux/drivers/isdn/pcbit/pcbit.h --- v2.2.13/linux/drivers/isdn/pcbit/pcbit.h Tue Jan 4 11:10:35 2000 +++ linux/drivers/isdn/pcbit/pcbit.h Tue Jan 4 10:12:17 2000 @@ -46,6 +46,7 @@ /* board */ volatile unsigned char* sh_mem; /* RDP address */ + unsigned long ph_mem; unsigned int irq; unsigned int id; unsigned int interrupt; /* set during interrupt @@ -166,10 +167,3 @@ #define L2_ERROR 6 #endif - - - - - - - diff -u --recursive --new-file v2.2.13/linux/drivers/macintosh/macserial.c linux/drivers/macintosh/macserial.c --- v2.2.13/linux/drivers/macintosh/macserial.c Tue Jan 4 11:10:35 2000 +++ linux/drivers/macintosh/macserial.c Tue Jan 4 10:12:17 2000 @@ -6,7 +6,9 @@ * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au) * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * - * $Id: macserial.c,v 1.24.2.3 1999/09/10 02:05:58 paulus Exp $ + * Receive DMA code by Takashi Oe . + * + * $Id: macserial.c,v 1.24.2.4 1999/10/19 04:36:42 paulus Exp $ */ #include @@ -28,6 +30,7 @@ #ifdef CONFIG_SERIAL_CONSOLE #include #endif +#include #include #include @@ -42,6 +45,7 @@ #ifdef CONFIG_KGDB #include #endif +#include #include "macserial.h" @@ -53,6 +57,8 @@ }; #endif +#define SUPPORT_SERIAL_DMA + /* * It would be nice to dynamically allocate everything that * depends on NUM_SERIAL, so we could support any number of @@ -128,6 +134,13 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout); static int set_scc_power(struct mac_serial * info, int state); static int setup_scc(struct mac_serial * info); +static void dbdma_reset(volatile struct dbdma_regs *dma); +static void dbdma_flush(volatile struct dbdma_regs *dma); +static void rs_txdma_irq(int irq, void *dev_id, struct pt_regs *regs); +static void rs_rxdma_irq(int irq, void *dev_id, struct pt_regs *regs); +static void dma_init(struct mac_serial * info); +static void rxdma_start(struct mac_serial * info, int current); +static void rxdma_to_tty(struct mac_serial * info); static struct tty_struct *serial_table[NUM_CHANNELS]; static struct termios *serial_termios[NUM_CHANNELS]; @@ -153,7 +166,7 @@ __openfirmware #endif /* MODULE */ static inline int serial_paranoia_check(struct mac_serial *info, - dev_t device, const char *routine) + dev_t device, const char *routine) { #ifdef SERIAL_PARANOIA_CHECK static const char *badmagic = @@ -177,7 +190,7 @@ * Reading and writing Z8530 registers. */ static inline unsigned char read_zsreg(struct mac_zschannel *channel, - unsigned char reg) + unsigned char reg) { unsigned char retval; unsigned long flags; @@ -197,7 +210,7 @@ } static inline void write_zsreg(struct mac_zschannel *channel, - unsigned char reg, unsigned char value) + unsigned char reg, unsigned char value) { unsigned long flags; @@ -290,6 +303,39 @@ } /* + * Reset a Descriptor-Based DMA channel. + */ +static void dbdma_reset(volatile struct dbdma_regs *dma) +{ + int i; + + out_le32(&dma->control, (WAKE|FLUSH|PAUSE|RUN) << 16); + + /* + * Yes this looks peculiar, but apparently it needs to be this + * way on some machines. (We need to make sure the DBDMA + * engine has actually got the write above and responded + * to it. - paulus) + */ + for (i = 200; i > 0; --i) + if (ld_le32(&dma->control) & RUN) + udelay(1); +} + +/* + * Tells a DBDMA channel to stop and write any buffered data + * it might have to memory. + */ +static _INLINE_ void dbdma_flush(volatile struct dbdma_regs *dma) +{ + int i = 0; + + out_le32(&dma->control, (FLUSH << 16) | FLUSH); + while (((in_le32(&dma->status) & FLUSH) != 0) && (i++ < 100)) + udelay(1); +} + +/* * ---------------------------------------------------------------------- * * Here starts the interrupt handling routines. All of the following @@ -312,6 +358,22 @@ mark_bh(MACSERIAL_BH); } +/* Work out the flag value for a z8530 status value. */ +static _INLINE_ int stat_to_flag(int stat) +{ + int flag; + + if (stat & Rx_OVR) { + flag = TTY_OVERRUN; + } else if (stat & FRM_ERR) { + flag = TTY_FRAME; + } else if (stat & PAR_ERR) { + flag = TTY_PARITY; + } else + flag = 0; + return flag; +} + static _INLINE_ void receive_chars(struct mac_serial *info, struct pt_regs *regs) { @@ -349,14 +411,7 @@ if (flip_max_cnt < tty->flip.count) flip_max_cnt = tty->flip.count; } - if (stat & Rx_OVR) { - flag = TTY_OVERRUN; - } else if (stat & FRM_ERR) { - flag = TTY_FRAME; - } else if (stat & PAR_ERR) { - flag = TTY_PARITY; - } else - flag = 0; + flag = stat_to_flag(stat); if (flag) /* reset the error indication */ write_zsreg(info->zs_channel, 0, ERR_RES); @@ -452,6 +507,32 @@ info->read_reg_zero = status; } +static _INLINE_ void receive_special_dma(struct mac_serial *info) +{ + unsigned char stat, flag; + volatile struct dbdma_regs *rd = &info->rx->dma; + int where = RX_BUF_SIZE; + + spin_lock(&info->rx_dma_lock); + if ((ld_le32(&rd->status) & ACTIVE) != 0) + dbdma_flush(rd); + if (in_le32(&rd->cmdptr) + == virt_to_bus(info->rx_cmds[info->rx_cbuf] + 1)) + where -= in_le16(&info->rx->res_count); + where--; + + stat = read_zsreg(info->zs_channel, R1); + + flag = stat_to_flag(stat); + if (flag) { + info->rx_flag_buf[info->rx_cbuf][where] = flag; + /* reset the error indication */ + write_zsreg(info->zs_channel, 0, ERR_RES); + } + + spin_unlock(&info->rx_dma_lock); +} + /* * This is the serial driver's generic interrupt routine */ @@ -461,6 +542,12 @@ unsigned char zs_intreg; int shift; + if (!(info->flags & ZILOG_INITIALIZED)) { + printk("rs_interrupt: irq %d, port not initialized\n", irq); + disable_irq(irq); + return; + } + /* NOTE: The read register 3, which holds the irq status, * does so for both channels on each chip. Although * the status value itself must be read from the A @@ -477,19 +564,21 @@ for (;;) { zs_intreg = read_zsreg(info->zs_chan_a, 3) >> shift; #ifdef SERIAL_DEBUG_INTR - printk("rs_interrupt: irq %d, zs_intreg 0x%x\n", irq, (int)zs_intreg); + printk("rs_interrupt: irq %d, zs_intreg 0x%x\n", + irq, (int)zs_intreg); #endif if ((zs_intreg & CHAN_IRQMASK) == 0) break; - if (!(info->flags & ZILOG_INITIALIZED)) { - printk("rs_interrupt: irq %d, port not initialized\n", irq); - break; + if (zs_intreg & CHBRxIP) { + /* If we are doing DMA, we only ask for interrupts + on characters with errors or special conditions. */ + if (info->dma_initted) + receive_special_dma(info); + else + receive_chars(info, regs); } - - if (zs_intreg & CHBRxIP) - receive_chars(info, regs); if (zs_intreg & CHBTxIP) transmit_chars(info); if (zs_intreg & CHBEXT) @@ -497,6 +586,39 @@ } } +/* Transmit DMA interrupt - not used at present */ +static void rs_txdma_irq(int irq, void *dev_id, struct pt_regs *regs) +{ +} + +/* + * Receive DMA interrupt. + */ +static void rs_rxdma_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct mac_serial *info = (struct mac_serial *) dev_id; + volatile struct dbdma_cmd *cd; + + if (!info->dma_initted) + return; + spin_lock(&info->rx_dma_lock); + /* First, confirm that this interrupt is, indeed, coming */ + /* from Rx DMA */ + cd = info->rx_cmds[info->rx_cbuf] + 2; + if ((in_le16(&cd->xfer_status) & (RUN | ACTIVE)) != (RUN | ACTIVE)) { + spin_unlock(&info->rx_dma_lock); + return; + } + if (info->rx_fbuf != RX_NO_FBUF) { + info->rx_cbuf = info->rx_fbuf; + if (++info->rx_fbuf == info->rx_nbuf) + info->rx_fbuf = 0; + if (info->rx_fbuf == info->rx_ubuf) + info->rx_fbuf = RX_NO_FBUF; + } + spin_unlock(&info->rx_dma_lock); +} + /* * ------------------------------------------------------------------- * Here ends the serial interrupt routines. @@ -592,10 +714,6 @@ } } -static void rs_timer(void) -{ -} - static int startup(struct mac_serial * info, int can_sleep) { int delay; @@ -631,6 +749,10 @@ info->flags |= ZILOG_INITIALIZED; enable_irq(info->irq); + if (info->dma_initted) { +// enable_irq(info->tx_dma_irq); + enable_irq(info->rx_dma_irq); + } if (delay) { if (can_sleep) { @@ -644,6 +766,187 @@ return 0; } +static _INLINE_ void rxdma_start(struct mac_serial * info, int current) +{ + volatile struct dbdma_regs *rd = &info->rx->dma; + volatile struct dbdma_cmd *cd = info->rx_cmds[current]; + +//printk(KERN_DEBUG "SCC: rxdma_start\n"); + + st_le32(&rd->cmdptr, virt_to_bus(cd)); + out_le32(&rd->control, (RUN << 16) | RUN); +} + +static void rxdma_to_tty(struct mac_serial *info) +{ + struct tty_struct *tty = info->tty; + volatile struct dbdma_regs *rd = &info->rx->dma; + unsigned long flags; + int residue, available, space, do_queue; + + if (!tty) + return; + + do_queue = 0; + spin_lock_irqsave(&info->rx_dma_lock, flags); +more: + space = TTY_FLIPBUF_SIZE - tty->flip.count; + if (!space) { + do_queue++; + goto out; + } + residue = 0; + if (info->rx_ubuf == info->rx_cbuf) { + if ((ld_le32(&rd->status) & ACTIVE) != 0) { + dbdma_flush(rd); + if (in_le32(&rd->cmdptr) + == virt_to_bus(info->rx_cmds[info->rx_cbuf]+1)) + residue = in_le16(&info->rx->res_count); + } + } + available = RX_BUF_SIZE - residue - info->rx_done_bytes; + if (available > space) + available = space; + if (available) { + memcpy(tty->flip.char_buf_ptr, + info->rx_char_buf[info->rx_ubuf] + info->rx_done_bytes, + available); + memcpy(tty->flip.flag_buf_ptr, + info->rx_flag_buf[info->rx_ubuf] + info->rx_done_bytes, + available); + tty->flip.char_buf_ptr += available; + tty->flip.count += available; + tty->flip.flag_buf_ptr += available; + memset(info->rx_flag_buf[info->rx_ubuf] + info->rx_done_bytes, + 0, available); + info->rx_done_bytes += available; + do_queue++; + } + if (info->rx_done_bytes == RX_BUF_SIZE) { + volatile struct dbdma_cmd *cd = info->rx_cmds[info->rx_ubuf]; + + if (info->rx_ubuf == info->rx_cbuf) + goto out; + /* mark rx_char_buf[rx_ubuf] free */ + st_le16(&cd->command, DBDMA_NOP); + cd++; + st_le32(&cd->cmd_dep, 0); + st_le32((unsigned int *)&cd->res_count, 0); + cd++; + st_le16(&cd->xfer_status, 0); + + if (info->rx_fbuf == RX_NO_FBUF) { + info->rx_fbuf = info->rx_ubuf; + if (!(ld_le32(&rd->status) & ACTIVE)) { + dbdma_reset(&info->rx->dma); + rxdma_start(info, info->rx_ubuf); + info->rx_cbuf = info->rx_ubuf; + } + } + info->rx_done_bytes = 0; + if (++info->rx_ubuf == info->rx_nbuf) + info->rx_ubuf = 0; + if (info->rx_fbuf == info->rx_ubuf) + info->rx_fbuf = RX_NO_FBUF; + goto more; + } +out: + spin_unlock_irqrestore(&info->rx_dma_lock, flags); + if (do_queue) + queue_task(&tty->flip.tqueue, &tq_timer); +} + +static void poll_rxdma(void *private_) +{ + struct mac_serial *info = (struct mac_serial *) private_; + unsigned long flags; + + rxdma_to_tty(info); + spin_lock_irqsave(&info->rx_dma_lock, flags); + mod_timer(&info->poll_dma_timer, RX_DMA_TIMER); + spin_unlock_irqrestore(&info->rx_dma_lock, flags); +} + +static void dma_init(struct mac_serial * info) +{ + int i, size; + volatile struct dbdma_cmd *cd; + unsigned char *p; + +//printk(KERN_DEBUG "SCC: dma_init\n"); + + info->rx_nbuf = 8; + + /* various mem set up */ + size = sizeof(struct dbdma_cmd) * (3 * info->rx_nbuf + 2) + + (RX_BUF_SIZE * 2 + sizeof(*info->rx_cmds) + + sizeof(*info->rx_char_buf) + sizeof(*info->rx_flag_buf)) + * info->rx_nbuf; + info->dma_priv = kmalloc(size, GFP_KERNEL | GFP_DMA); + if (info->dma_priv == NULL) + return; + memset(info->dma_priv, 0, size); + + info->rx_cmds = (volatile struct dbdma_cmd **)info->dma_priv; + info->rx_char_buf = (unsigned char **) (info->rx_cmds + info->rx_nbuf); + info->rx_flag_buf = info->rx_char_buf + info->rx_nbuf; + p = (unsigned char *) (info->rx_flag_buf + info->rx_nbuf); + for (i = 0; i < info->rx_nbuf; i++, p += RX_BUF_SIZE) + info->rx_char_buf[i] = p; + for (i = 0; i < info->rx_nbuf; i++, p += RX_BUF_SIZE) + info->rx_flag_buf[i] = p; + + /* a bit of DMA programming */ + cd = info->rx_cmds[0] = (volatile struct dbdma_cmd *) DBDMA_ALIGN(p); + st_le16(&cd->command, DBDMA_NOP); + cd++; + st_le16(&cd->req_count, RX_BUF_SIZE); + st_le16(&cd->command, INPUT_MORE); + st_le32(&cd->phy_addr, virt_to_bus(info->rx_char_buf[0])); + cd++; + st_le16(&cd->req_count, 4); + st_le16(&cd->command, STORE_WORD | INTR_ALWAYS); + st_le32(&cd->phy_addr, virt_to_bus(cd-2)); + st_le32(&cd->cmd_dep, DBDMA_STOP); + for (i = 1; i < info->rx_nbuf; i++) { + info->rx_cmds[i] = ++cd; + st_le16(&cd->command, DBDMA_NOP); + cd++; + st_le16(&cd->req_count, RX_BUF_SIZE); + st_le16(&cd->command, INPUT_MORE); + st_le32(&cd->phy_addr, virt_to_bus(info->rx_char_buf[i])); + cd++; + st_le16(&cd->req_count, 4); + st_le16(&cd->command, STORE_WORD | INTR_ALWAYS); + st_le32(&cd->phy_addr, virt_to_bus(cd-2)); + st_le32(&cd->cmd_dep, DBDMA_STOP); + } + cd++; + st_le16(&cd->command, DBDMA_NOP | BR_ALWAYS); + st_le32(&cd->cmd_dep, virt_to_bus(info->rx_cmds[0])); + + /* setup DMA to our liking */ + dbdma_reset(&info->rx->dma); + st_le32(&info->rx->dma.intr_sel, 0x10001); + st_le32(&info->rx->dma.br_sel, 0x10001); + out_le32(&info->rx->dma.wait_sel, 0x10001); + + /* set various flags */ + info->rx_ubuf = 0; + info->rx_cbuf = 0; + info->rx_fbuf = info->rx_ubuf + 1; + if (info->rx_fbuf == info->rx_nbuf) + info->rx_fbuf = RX_NO_FBUF; + info->rx_done_bytes = 0; + + /* setup polling */ + init_timer(&info->poll_dma_timer); + info->poll_dma_timer.function = (void *)&poll_rxdma; + info->poll_dma_timer.data = (unsigned long)info; + + info->dma_initted = 1; +} + static int setup_scc(struct mac_serial * info) { unsigned long flags; @@ -669,6 +972,12 @@ info->xmit_fifo_size = 1; /* + * Reset DMAs + */ + if (info->has_dma) + dma_init(info); + + /* * Clear the interrupt registers. */ write_zsreg(info->zs_channel, 0, ERR_RES); @@ -682,7 +991,23 @@ /* * Finally, enable sequencing and interrupts */ - info->curregs[1] = (info->curregs[1] & ~0x18) | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB); + if (!info->dma_initted) { + /* interrupt on ext/status changes, all received chars, + transmit ready */ + info->curregs[1] = (info->curregs[1] & ~0x18) + | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB); + } else { + /* interrupt on ext/status changes, W/Req pin is + receive DMA request */ + info->curregs[1] = (info->curregs[1] & ~(0x18 | TxINT_ENAB)) + | (EXT_INT_ENAB | WT_RDY_RT | WT_FN_RDYFN); + write_zsreg(info->zs_channel, 1, info->curregs[1]); + /* enable W/Req pin */ + info->curregs[1] |= WT_RDY_ENAB; + write_zsreg(info->zs_channel, 1, info->curregs[1]); + /* enable interrupts on transmit ready and receive errors */ + info->curregs[1] |= INT_ERR_Rx | TxINT_ENAB; + } info->pendregs[1] = info->curregs[1]; info->curregs[3] |= (RxENABLE | Rx8); info->pendregs[3] = info->curregs[3]; @@ -708,6 +1033,14 @@ restore_flags(flags); + if (info->dma_initted) { + spin_lock_irqsave(&info->rx_dma_lock, flags); + rxdma_start(info, 0); + info->poll_dma_timer.expires = RX_DMA_TIMER; + add_timer(&info->poll_dma_timer); + spin_unlock_irqrestore(&info->rx_dma_lock, flags); + } + return 0; } @@ -729,7 +1062,14 @@ return; } - + + if (info->has_dma) { + del_timer(&info->poll_dma_timer); + dbdma_reset(info->tx_dma); + dbdma_reset(&info->rx->dma); + disable_irq(info->tx_dma_irq); + disable_irq(info->rx_dma_irq); + } disable_irq(info->irq); info->pendregs[1] = info->curregs[1] = 0; @@ -755,6 +1095,12 @@ info->xmit_buf = 0; } + if (info->has_dma && info->dma_priv) { + kfree(info->dma_priv); + info->dma_priv = NULL; + info->dma_initted = 0; + } + memset(info->curregs, 0, sizeof(info->curregs)); memset(info->curregs, 0, sizeof(info->pendregs)); @@ -1052,7 +1398,6 @@ if (!tty || !info->xmit_buf || !tmp_buf) return 0; - save_flags(flags); if (from_user) { down(&tmp_buf_sem); while (1) { @@ -1068,6 +1413,7 @@ ret = -EFAULT; break; } + save_flags(flags); cli(); c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); @@ -1083,6 +1429,7 @@ up(&tmp_buf_sem); } else { while (1) { + save_flags(flags); cli(); c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, @@ -1104,7 +1451,6 @@ if (info->xmit_cnt && !tty->stopped && !info->tx_stopped && !info->tx_active) transmit_chars(info); - restore_flags(flags); return ret; } @@ -1133,12 +1479,13 @@ static void rs_flush_buffer(struct tty_struct *tty) { struct mac_serial *info = (struct mac_serial *)tty->driver_data; + unsigned long flags; if (serial_paranoia_check(info, tty->device, "rs_flush_buffer")) return; - cli(); + save_flags(flags); cli(); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - sti(); + restore_flags(flags); wake_up_interruptible(&tty->write_wait); if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) @@ -1158,7 +1505,6 @@ struct mac_serial *info = (struct mac_serial *)tty->driver_data; unsigned long flags; #ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; printk("throttle %ld....\n",tty->ldisc.chars_in_buffer(tty)); #endif @@ -1195,7 +1541,6 @@ struct mac_serial *info = (struct mac_serial *)tty->driver_data; unsigned long flags; #ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; printk("unthrottle %s: %d....\n",tty->ldisc.chars_in_buffer(tty)); #endif @@ -1473,6 +1818,7 @@ save_flags(flags); cli(); if (tty_hung_up_p(filp)) { + MOD_DEC_USE_COUNT; restore_flags(flags); return; } @@ -1498,6 +1844,7 @@ info->count = 0; } if (info->count) { + MOD_DEC_USE_COUNT; restore_flags(flags); return; } @@ -1518,8 +1865,12 @@ printk("waiting end of Tx... (timeout:%d)\n", info->closing_wait); #endif tty->closing = 1; - if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE) + if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE) { + restore_flags(flags); tty_wait_until_sent(tty, info->closing_wait); + save_flags(flags); cli(); + } + /* * At this point we stop accepting input. To do this, we * disable the receiver and receive interrupts. @@ -1539,7 +1890,9 @@ #ifdef SERIAL_DEBUG_OPEN printk("waiting end of Rx...\n"); #endif + restore_flags(flags); rs_wait_until_sent(tty, info->timeout); + save_flags(flags); cli(); } shutdown(info); @@ -1565,6 +1918,7 @@ info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE| ZILOG_CLOSING); wake_up_interruptible(&info->close_wait); + MOD_DEC_USE_COUNT; } /* @@ -1603,7 +1957,6 @@ char_time = MIN(char_time, timeout); while ((read_zsreg(info->zs_channel, 1) & ALL_SNT) == 0) { current->state = TASK_INTERRUPTIBLE; - current->counter = 0; /* make us low-priority */ schedule_timeout(char_time); if (signal_pending(current)) break; @@ -1775,14 +2128,19 @@ int retval, line; unsigned long page; + MOD_INC_USE_COUNT; line = MINOR(tty->device) - tty->driver.minor_start; - if ((line < 0) || (line >= zs_channels_found)) + if ((line < 0) || (line >= zs_channels_found)) { + MOD_DEC_USE_COUNT; return -ENODEV; + } info = zs_soft + line; #ifdef CONFIG_KGDB - if (info->kgdb_channel) + if (info->kgdb_channel) { + MOD_DEC_USE_COUNT; return -ENODEV; + } #endif if (serial_paranoia_check(info, tty->device, "rs_open")) return -ENODEV; @@ -1865,7 +2223,58 @@ static void show_serial_version(void) { - printk("PowerMac Z8530 serial driver version 1.01\n"); + printk("PowerMac Z8530 serial driver version 2.0\n"); +} + +/* + * Initialize one channel, both the mac_serial and mac_zschannel + * structs. We use the dev_node field of the mac_serial struct. + */ +static void +chan_init(struct mac_serial *zss, struct mac_zschannel *zs_chan, + struct mac_zschannel *zs_chan_a) +{ + struct device_node *ch = zss->dev_node; + char *conn; + int len; + + zss->irq = ch->intrs[0].line; + zss->has_dma = 0; +#if !defined(CONFIG_KGDB) && defined(SUPPORT_SERIAL_DMA) + if (ch->n_addrs == 3 && ch->n_intrs == 3) + zss->has_dma = 1; +#endif + zss->dma_initted = 0; + + zs_chan->control = (volatile unsigned char *) + ioremap(ch->addrs[0].address, 0x1000); + zs_chan->data = zs_chan->control + 0x10; + spin_lock_init(&zs_chan->lock); + zs_chan->parent = zss; + zss->zs_channel = zs_chan; + zss->zs_chan_a = zs_chan_a; + + /* setup misc varariables */ + zss->kgdb_channel = 0; + zss->is_cobalt_modem = device_is_compatible(ch, "cobalt"); + + /* XXX tested only with wallstreet PowerBook, + should do no harm anyway */ + conn = get_property(ch, "AAPL,connector", &len); + zss->is_pwbk_ir = conn && (strcmp(conn, "infrared") == 0); + + if (zss->has_dma) { + zss->dma_priv = NULL; + /* it seems that the last two addresses are the + DMA controllers */ + zss->tx_dma = (volatile struct dbdma_regs *) + ioremap(ch->addrs[ch->n_addrs - 2].address, 0x100); + zss->rx = (volatile struct mac_dma *) + ioremap(ch->addrs[ch->n_addrs - 1].address, 0x100); + zss->tx_dma_irq = ch->intrs[1].line; + zss->rx_dma_irq = ch->intrs[2].line; + spin_lock_init(&zss->rx_dma_lock); + } } /* Ask the PROM how many Z8530s we have and initialize their zs_channels */ @@ -1874,51 +2283,63 @@ { struct device_node *dev, *ch; struct mac_serial **pp; - int n, lenp; - char *conn; + int n, chip, nchan; + struct mac_zschannel *zs_chan; + int chan_a_index; n = 0; pp = &zs_chain; + zs_chan = zs_channels; for (dev = find_devices("escc"); dev != 0; dev = dev->next) { + nchan = 0; + chip = n; if (n >= NUM_CHANNELS) { printk("Sorry, can't use %s: no more channels\n", dev->full_name); continue; } + chan_a_index = 0; for (ch = dev->child; ch != 0; ch = ch->sibling) { + if (nchan >= 2) { + printk(KERN_WARNING "SCC: Only 2 channels per " + "chip are supported\n"); + break; + } if (ch->n_addrs < 1 || (ch ->n_intrs < 1)) { printk("Can't use %s: %d addrs %d intrs\n", ch->full_name, ch->n_addrs, ch->n_intrs); continue; } - zs_channels[n].control = (volatile unsigned char *) - ioremap(ch->addrs[0].address, 0x1000); - zs_channels[n].data = zs_channels[n].control + 0x10; - spin_lock_init(&zs_channels[n].lock); - zs_soft[n].zs_channel = &zs_channels[n]; - zs_soft[n].dev_node = ch; - zs_soft[n].irq = ch->intrs[0].line; - zs_soft[n].zs_channel->parent = &zs_soft[n]; - zs_soft[n].is_cobalt_modem = device_is_compatible(ch, "cobalt"); - - /* XXX tested only with wallstreet PowerBook, - should do no harm anyway */ - conn = get_property(ch, "AAPL,connector", &lenp); - zs_soft[n].is_pwbk_ir = - conn && (strcmp(conn, "infrared") == 0); - - /* XXX this assumes the prom puts chan A before B */ - if (n & 1) - zs_soft[n].zs_chan_a = &zs_channels[n-1]; - else - zs_soft[n].zs_chan_a = &zs_channels[n]; + /* The channel with the higher address + will be the A side. */ + if (nchan > 0 && + ch->addrs[0].address + > zs_soft[n-1].dev_node->addrs[0].address) + chan_a_index = 1; + + /* minimal initialization for now */ + zs_soft[n].dev_node = ch; *pp = &zs_soft[n]; pp = &zs_soft[n].zs_next; + ++nchan; ++n; } + if (nchan == 0) + continue; + + /* set up A side */ + chan_init(&zs_soft[chip + chan_a_index], zs_chan, zs_chan); + ++zs_chan; + + /* set up B side, if it exists */ + if (nchan > 1) + chan_init(&zs_soft[chip + 1 - chan_a_index], + zs_chan, zs_chan - 1); + ++zs_chan; } *pp = 0; + zs_channels_found = n; #ifdef CONFIG_PMAC_PBOOK if (n) @@ -1935,8 +2356,6 @@ /* Setup base handler, and timer table. */ init_bh(MACSERIAL_BH, do_serial_bh); - timer_table[RS_TIMER].fn = rs_timer; - timer_table[RS_TIMER].expires = 0; /* Find out how many Z8530 SCCs we have */ if (zs_chain == 0) @@ -1948,6 +2367,18 @@ /* Register the interrupt handler for each one */ save_flags(flags); cli(); for (i = 0; i < zs_channels_found; ++i) { + if (zs_soft[i].has_dma) { + if (request_irq(zs_soft[i].tx_dma_irq, rs_txdma_irq, 0, + "SCC-txdma", &zs_soft[i])) + printk(KERN_ERR "macserial: can't get irq %d\n", + zs_soft[i].tx_dma_irq); + disable_irq(zs_soft[i].tx_dma_irq); + if (request_irq(zs_soft[i].rx_dma_irq, rs_rxdma_irq, 0, + "SCC-rxdma", &zs_soft[i])) + printk(KERN_ERR "macserial: can't get irq %d\n", + zs_soft[i].rx_dma_irq); + disable_irq(zs_soft[i].rx_dma_irq); + } if (request_irq(zs_soft[i].irq, rs_interrupt, 0, "SCC", &zs_soft[i])) printk(KERN_ERR "macserial: can't get irq %d\n", @@ -2107,8 +2538,13 @@ for (info = zs_chain, i = 0; info; info = info->zs_next, i++) set_scc_power(info, 0); save_flags(flags); cli(); - for (i = 0; i < zs_channels_found; ++i) + for (i = 0; i < zs_channels_found; ++i) { free_irq(zs_soft[i].irq, &zs_soft[i]); + if (zs_soft[i].has_dma) { + free_irq(zs_soft[i].tx_dma_irq, &zs_soft[i]); + free_irq(zs_soft[i].rx_dma_irq, &zs_soft[i]); + } + } restore_flags(flags); tty_unregister_driver(&callout_driver); tty_unregister_driver(&serial_driver); @@ -2238,6 +2674,8 @@ if (zs_chain == 0) return -1; + set_scc_power(info, 1); + /* Reset the channel */ write_zsreg(info->zs_channel, R9, CHRA); @@ -2481,14 +2919,13 @@ if (zs_chain == 0) probe_sccs(); - set_scc_power(&zs_soft[n], 1); + set_scc_power(&zs_soft[tty_num], 1); zs_kgdbchan = zs_soft[tty_num].zs_channel; zs_soft[tty_num].change_needed = 0; zs_soft[tty_num].clk_divisor = 16; zs_soft[tty_num].zs_baud = 38400; zs_soft[tty_num].kgdb_channel = 1; /* This runs kgdb */ - zs_soft[tty_num ^ 1].kgdb_channel = 0; /* This does not */ /* Turn on transmitter/receiver at 8-bits/char */ kgdb_chaninit(zs_soft[tty_num].zs_channel, 1, 38400); diff -u --recursive --new-file v2.2.13/linux/drivers/macintosh/macserial.h linux/drivers/macintosh/macserial.h --- v2.2.13/linux/drivers/macintosh/macserial.h Tue Jan 4 11:10:35 2000 +++ linux/drivers/macintosh/macserial.h Tue Jan 4 10:12:17 2000 @@ -92,6 +92,13 @@ struct mac_serial* parent; }; +struct mac_dma { + volatile struct dbdma_regs dma; + volatile unsigned short res_count; + volatile unsigned short command; + volatile unsigned int buf_addr; +}; + struct mac_serial { struct mac_serial *zs_next; /* For IRQ servicing chain */ struct mac_zschannel *zs_channel; /* Channel registers */ @@ -156,6 +163,28 @@ struct termios callout_termios; struct wait_queue *open_wait; struct wait_queue *close_wait; + + volatile struct dbdma_regs *tx_dma; + int tx_dma_irq; + volatile struct dbdma_cmd *tx_cmds; + volatile struct mac_dma *rx; + int rx_dma_irq; + volatile struct dbdma_cmd **rx_cmds; + unsigned char **rx_char_buf; + unsigned char **rx_flag_buf; +#define RX_BUF_SIZE 256 + int rx_nbuf; + int rx_done_bytes; + int rx_ubuf; + int rx_fbuf; +#define RX_NO_FBUF (-1) + int rx_cbuf; + spinlock_t rx_dma_lock; + int has_dma; + int dma_initted; + void *dma_priv; + struct timer_list poll_dma_timer; +#define RX_DMA_TIMER (jiffies + 10*HZ/1000) }; @@ -226,9 +255,9 @@ #define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */ #define INT_ERR_Rx 0x18 /* Int on error only */ -#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */ -#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */ -#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */ +#define WT_RDY_RT 0x20 /* W/Req reflects recv if 1, xmit if 0 */ +#define WT_FN_RDYFN 0x40 /* W/Req pin is DMA request if 1, wait if 0 */ +#define WT_RDY_ENAB 0x80 /* Enable W/Req pin */ /* Write Register #2 (Interrupt Vector) */ @@ -286,6 +315,9 @@ /* Write Register 7 (Sync bits 8-15/SDLC 01111110) */ +/* Write Register 7' (Some enhanced feature control) */ +#define ENEXREAD 0x40 /* Enable read of some write registers */ + /* Write Register 8 (transmit buffer) */ /* Write Register 9 (Master interrupt control) */ @@ -346,7 +378,9 @@ #define SNRZI 0xe0 /* Set NRZI mode */ /* Write Register 15 (external/status interrupt control) */ +#define EN85C30 1 /* Enable some 85c30-enhanced registers */ #define ZCIE 2 /* Zero count IE */ +#define ENSTFIFO 4 /* Enable status FIFO (SDLC) */ #define DCDIE 8 /* DCD IE */ #define SYNCIE 0x10 /* Sync/hunt IE */ #define CTSIE 0x20 /* CTS IE */ @@ -382,6 +416,15 @@ #define END_FR 0x80 /* End of Frame (SDLC) */ /* Read Register 2 (channel b only) - Interrupt vector */ +#define CHB_Tx_EMPTY 0x00 +#define CHB_EXT_STAT 0x02 +#define CHB_Rx_AVAIL 0x04 +#define CHB_SPECIAL 0x06 +#define CHA_Tx_EMPTY 0x08 +#define CHA_EXT_STAT 0x0a +#define CHA_Rx_AVAIL 0x0c +#define CHA_SPECIAL 0x0e +#define STATUS_MASK 0x06 /* Read Register 3 (interrupt pending register) ch a only */ #define CHBEXT 0x1 /* Channel B Ext/Stat IP */ diff -u --recursive --new-file v2.2.13/linux/drivers/macintosh/mediabay.c linux/drivers/macintosh/mediabay.c --- v2.2.13/linux/drivers/macintosh/mediabay.c Tue Jan 4 11:10:35 2000 +++ linux/drivers/macintosh/mediabay.c Tue Jan 4 10:12:17 2000 @@ -94,6 +94,12 @@ */ #define MB_IDE_WAIT 1500 +/* + * Wait at least this many ticks after resetting an IDE device before + * believing its ready bit. + */ +#define MB_IDE_MINWAIT 250 + static void poll_media_bay(int which); static void set_media_bay(int which, int id); static int media_bay_task(void *); @@ -285,7 +291,10 @@ #endif } #ifdef CONFIG_BLK_DEV_IDE - } else if (bay->cd_timer && (--bay->cd_timer == 0 || MB_IDE_READY(i)) + } else if (bay->cd_timer + && (--bay->cd_timer == 0 + || (bay->cd_timer < MB_IDE_WAIT - MB_IDE_MINWAIT + && MB_IDE_READY(i))) && bay->cd_index < 0) { bay->cd_timer = 0; printk(KERN_DEBUG "Registering IDE, base:0x%08lx, irq:%d\n", bay->cd_base, bay->cd_irq); @@ -299,11 +308,13 @@ } bay->previous_id = bay->content_id; - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); - if (signal_pending(current)) - return 0; - i = (i+1)%media_bay_count; + if (++i >= media_bay_count) { + i = 0; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + if (signal_pending(current)) + return 0; + } } } @@ -405,31 +416,30 @@ feature_set(bay->dev_node, FEATURE_Mediabay_enable); /* I suppose this is enough delay to stabilize MB_CONTENT ... */ mdelay(10); - /* We re-enable the bay using it's previous content only if - it did not change */ - if (MB_CONTENTS(i) == bay->content_id) { - set_media_bay(i, bay->content_id); - if (bay->content_id != MB_NO) { - mdelay(400); - /* Clear the bay reset */ - feature_clear(bay->dev_node, FEATURE_Mediabay_reset); - /* This small delay makes sure the device has time - to assert the BUSY bit (used by IDE sleep) */ - udelay(100); - /* We reset the state machine timers in case we were in the - middle of a wait loop */ - if (bay->reset_timer) - bay->reset_timer = MB_RESET_COUNT; - if (bay->cd_timer) - bay->cd_timer = MB_IDE_WAIT; - } - } + /* We re-enable the bay using it's previous content + only if it did not change */ + if (MB_CONTENTS(i) != bay->content_id) + continue; + set_media_bay(i, bay->content_id); + if (bay->content_id == MB_NO) + continue; + mdelay(400); + /* Clear the bay reset */ + feature_clear(bay->dev_node, FEATURE_Mediabay_reset); + /* This small delay makes sure the device has time + to assert the BUSY bit (used by IDE sleep) */ + udelay(100); + /* We reset the state machine timers in case we were + in the middle of a wait loop */ + if (bay->reset_timer) + bay->reset_timer = MB_RESET_COUNT; +#ifdef CONFIG_BLK_DEV_IDE + if (bay->cd_timer) + bay->cd_timer = MB_IDE_WAIT; +#endif } break; } return PBOOK_SLEEP_OK; } #endif /* CONFIG_PMAC_PBOOK */ - - - diff -u --recursive --new-file v2.2.13/linux/drivers/macintosh/via-pmu.c linux/drivers/macintosh/via-pmu.c --- v2.2.13/linux/drivers/macintosh/via-pmu.c Tue Oct 19 17:10:38 1999 +++ linux/drivers/macintosh/via-pmu.c Tue Jan 4 10:12:17 2000 @@ -1126,11 +1126,6 @@ macio_base = (unsigned long) ioremap(macio->addrs[0].address, 0x40); - /* Sync the disks. */ - /* XXX It would be nice to have some way to ensure that - * nobody is dirtying any new buffers while we wait. */ - fsync_dev(0); - /* Notify device drivers */ ret = broadcast_sleep(PBOOK_SLEEP_REQUEST, 1); if (ret != PBOOK_SLEEP_OK) { @@ -1138,6 +1133,15 @@ printk("pmu: sleep rejected\n"); return -EBUSY; } + + /* Sync the disks. */ + /* XXX It would be nice to have some way to ensure that + * nobody is dirtying any new buffers while we wait. + * BenH: Moved to _after_ sleep request and changed video + * drivers to vmalloc() during sleep request. This way, all + * vmalloc's are done before actual sleep of block drivers */ + fsync_dev(0); + broadcast_sleep(PBOOK_SLEEP_NOW, 0); /* Give the disks a little time to actually finish writing */ @@ -1190,6 +1194,10 @@ pmcr1 &= ~(GRACKLE_PM|GRACKLE_DOZE|GRACKLE_SLEEP|GRACKLE_NAP); grackle_pcibios_write_config_word(0, 0, 0x70, pmcr1); + /* Make sure the PMU is idle */ + while (pmu_state != idle) + pmu_poll(); + sti(); #if 0 /* According to someone from Apple, this should not be needed, @@ -1201,7 +1209,7 @@ /* Restore L2 cache */ if (save_l2cr) - _set_L2CR(save_l2cr | 0x200000); /* set invalidate bit */ + _set_L2CR(save_l2cr | 0x200000); /* set invalidate bit */ /* reenable interrupts */ sleep_restore_intrs(); @@ -1222,11 +1230,6 @@ unsigned long p, wait; struct adb_request sleep_req; - /* Sync the disks. */ - /* XXX It would be nice to have some way to ensure that - * nobody is dirtying any new buffers while we wait. */ - fsync_dev(0); - /* Notify device drivers */ ret = broadcast_sleep(PBOOK_SLEEP_REQUEST, 1); if (ret != PBOOK_SLEEP_OK) { @@ -1234,6 +1237,15 @@ printk("pmu: sleep rejected\n"); return -EBUSY; } + + /* Sync the disks. */ + /* XXX It would be nice to have some way to ensure that + * nobody is dirtying any new buffers while we wait. + * BenH: Moved to _after_ sleep request and changed video + * drivers to vmalloc() during sleep request. This way, all + * vmalloc's are done before actual sleep of block drivers */ + fsync_dev(0); + broadcast_sleep(PBOOK_SLEEP_NOW, 0); /* Give the disks a little time to actually finish writing */ diff -u --recursive --new-file v2.2.13/linux/drivers/misc/parport_pc.c linux/drivers/misc/parport_pc.c --- v2.2.13/linux/drivers/misc/parport_pc.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/misc/parport_pc.c Tue Jan 4 10:12:17 2000 @@ -32,6 +32,9 @@ * only in register addresses (eg because your registers are on 32-bit * word boundaries) then you can alter the constants in parport_pc.h to * accomodate this. + * + * Note that the ECP registers may not start at offset 0x400 for PCI cards, + * but rather will start at port->base_hi. */ #include @@ -43,6 +46,7 @@ #include #include #include +#include #include @@ -62,27 +66,27 @@ void parport_pc_write_epp(struct parport *p, unsigned char d) { - outb(d, p->base+EPPDATA); + outb(d, EPPDATA(p)); } unsigned char parport_pc_read_epp(struct parport *p) { - return inb(p->base+EPPDATA); + return inb(EPPDATA(p)); } void parport_pc_write_epp_addr(struct parport *p, unsigned char d) { - outb(d, p->base+EPPADDR); + outb(d, EPPADDR(p)); } unsigned char parport_pc_read_epp_addr(struct parport *p) { - return inb(p->base+EPPADDR); + return inb(EPPADDR(p)); } int parport_pc_check_epp_timeout(struct parport *p) { - if (!(inb(p->base+STATUS) & 1)) + if (!(inb(STATUS(p)) & 1)) return 0; parport_pc_epp_clear_timeout(p); return 1; @@ -90,24 +94,24 @@ unsigned char parport_pc_read_configb(struct parport *p) { - return inb(p->base+CONFIGB); + return inb(CONFIGB(p)); } void parport_pc_write_data(struct parport *p, unsigned char d) { - outb(d, p->base+DATA); + outb(d, DATA(p)); } unsigned char parport_pc_read_data(struct parport *p) { - return inb(p->base+DATA); + return inb(DATA(p)); } void parport_pc_write_control(struct parport *p, unsigned char d) { struct parport_pc_private *priv = p->private_data; priv->ctr = d;/* update soft copy */ - outb(d, p->base+CONTROL); + outb(d, CONTROL(p)); } unsigned char parport_pc_read_control(struct parport *p) @@ -121,34 +125,34 @@ struct parport_pc_private *priv = p->private_data; unsigned char ctr = priv->ctr; ctr = (ctr & ~mask) ^ val; - outb (ctr, p->base+CONTROL); + outb (ctr, CONTROL(p)); return priv->ctr = ctr; /* update soft copy */ } void parport_pc_write_status(struct parport *p, unsigned char d) { - outb(d, p->base+STATUS); + outb(d, STATUS(p)); } unsigned char parport_pc_read_status(struct parport *p) { - return inb(p->base+STATUS); + return inb(STATUS(p)); } void parport_pc_write_econtrol(struct parport *p, unsigned char d) { - outb(d, p->base+ECONTROL); + outb(d, ECONTROL(p)); } unsigned char parport_pc_read_econtrol(struct parport *p) { - return inb(p->base+ECONTROL); + return inb(ECONTROL(p)); } unsigned char parport_pc_frob_econtrol(struct parport *p, unsigned char mask, unsigned char val) { - unsigned char old = inb(p->base+ECONTROL); - outb(((old & ~mask) ^ val), p->base+ECONTROL); + unsigned char old = inb(ECONTROL(p)); + outb(((old & ~mask) ^ val), ECONTROL(p)); return old; } @@ -159,12 +163,12 @@ void parport_pc_write_fifo(struct parport *p, unsigned char v) { - outb (v, p->base+CONFIGA); + outb (v, CONFIGA(p)); } unsigned char parport_pc_read_fifo(struct parport *p) { - return inb (p->base+CONFIGA); + return inb (CONFIGA(p)); } void parport_pc_disable_irq(struct parport *p) @@ -183,7 +187,7 @@ free_irq(p->irq, p); release_region(p->base, p->size); if (p->modes & PARPORT_MODE_PCECR) - release_region(p->base+0x400, 3); + release_region(p->base_hi, 3); } int parport_pc_claim_resources(struct parport *p) @@ -195,7 +199,7 @@ return err; request_region(p->base, p->size, p->name); if (p->modes & PARPORT_MODE_PCECR) - request_region(p->base+0x400, 3, p->name); + request_region(p->base_hi, 3, p->name); return 0; } @@ -223,8 +227,8 @@ { size_t got = 0; for (; got < length; got++) { - *((char*)buf)++ = inb (p->base+EPPDATA); - if (inb (p->base+STATUS) & 0x01) + *((char*)buf)++ = inb (EPPDATA(p)); + if (inb (STATUS(p)) & 0x01) break; } return got; @@ -234,8 +238,8 @@ { size_t written = 0; for (; written < length; written++) { - outb (*((char*)buf)++, p->base+EPPDATA); - if (inb (p->base+STATUS) & 0x01) + outb (*((char*)buf)++, EPPDATA(p)); + if (inb (STATUS(p)) & 0x01) break; } return written; @@ -370,11 +374,11 @@ * allow reads, so read_control just returns a software * copy. Some ports _do_ allow reads, so bypass the software * copy here. In addition, some bits aren't writable. */ - r = inb (pb->base+CONTROL); + r = inb (CONTROL(pb)); if ((r & 0xf) == w) { w = 0xe; parport_pc_write_control (pb, w); - r = inb (pb->base+CONTROL); + r = inb (CONTROL(pb)); parport_pc_write_control (pb, 0xc); if ((r & 0xf) == w) return PARPORT_MODE_PCSPP; @@ -754,13 +758,16 @@ /* --- Initialisation code -------------------------------- */ -static int probe_one_port(unsigned long int base, int irq, int dma) +static int probe_one_port(unsigned long int base, + unsigned long int base_hi, + int irq, int dma) { struct parport *p; int probedirq = PARPORT_IRQ_NONE; if (check_region(base, 3)) return 0; if (!(p = parport_register_port(base, irq, dma, &parport_pc_ops))) return 0; + p->base_hi = base_hi; p->private_data = kmalloc (sizeof (struct parport_pc_private), GFP_KERNEL); if (!p->private_data) { @@ -770,16 +777,14 @@ return 0; } ((struct parport_pc_private *) (p->private_data))->ctr = 0xc; - if (p->base != 0x3bc) { - if (!check_region(base+0x400,3)) { - p->modes |= parport_ECR_present(p); - p->modes |= parport_ECP_supported(p); - p->modes |= parport_ECPPS2_supported(p); - } - if (!check_region(base+0x3, 5)) { - p->modes |= parport_EPP_supported(p); - p->modes |= parport_ECPEPP_supported(p); - } + if (base_hi && !check_region(base_hi,3)) { + p->modes |= parport_ECR_present(p); + p->modes |= parport_ECP_supported(p); + p->modes |= parport_ECPPS2_supported(p); + } + if (p->base != 0x3bc && !check_region(base+0x3, 5)) { + p->modes |= parport_EPP_supported(p); + p->modes |= parport_ECPEPP_supported(p); } if (!parport_SPP_supported(p)) { /* No port. */ @@ -791,6 +796,8 @@ p->size = (p->modes & (PARPORT_MODE_PCEPP | PARPORT_MODE_PCECPEPP))?8:3; printk(KERN_INFO "%s: PC-style at 0x%lx", p->name, p->base); + if (p->base_hi && (p->modes & PARPORT_MODE_PCECP)) + printk(" (0x%lx)", p->base_hi); if (p->irq == PARPORT_IRQ_AUTO) { p->irq = PARPORT_IRQ_NONE; parport_irq_probe(p); @@ -844,6 +851,124 @@ return 1; } +/* Look for PCI parallel port cards. */ +static int parport_pc_init_pci (int irq, int dma) +{ + struct { + unsigned short vendor; + unsigned short device; + int numports; + struct { + int lo; + int hi; /* -ve if not there */ + } addr[4]; + } cards[] = { + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550, 1, + { { 3, 4 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650, 1, + { { 3, 4 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850, 1, + { { 3, 4 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1P_10x, 1, + { { 2, 3 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P_10x, 2, + { { 2, 3 }, { 4, 5 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550, 1, + { { 4, 5 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650, 1, + { { 4, 5 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850, 1, + { { 4, 5 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1P_20x, 1, + { { 0, 1 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P_20x, 2, + { { 0, 1 }, { 2, 3 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550, 2, + { { 1, 2 }, { 3, 4 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650, 2, + { { 1, 2 }, { 3, 4 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850, 2, + { { 1, 2 }, { 3, 4 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550, 1, + { { 1, 2 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650, 1, + { { 1, 2 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850, 1, + { { 1, 2 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550, 1, + { { 2, 3 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650, 1, + { { 2, 3 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850, 1, + { { 2, 3 }, } }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PARALLEL, 1, + { { 0, -1 }, } }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_A, 1, + { { 0, -1 }, } }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_B, 1, + { { 0, -1 }, } }, + { 0, } + }; + + struct pci_dev *pcidev; + int count = 0; + int i; + + if (!pci_present ()) + return 0; + + for (i = 0; cards[i].vendor; i++) { + pcidev = NULL; + while ((pcidev = pci_find_device (cards[i].vendor, + cards[i].device, + pcidev)) != NULL) { + int n; + for (n = 0; n < cards[i].numports; n++) { + int lo = cards[i].addr[n].lo; + int hi = cards[i].addr[n].hi; + unsigned long io_lo, io_hi; + io_lo = pcidev->base_address[lo]; + io_hi = ((hi < 0) ? 0 : + pcidev->base_address[hi]); + io_lo &= PCI_BASE_ADDRESS_IO_MASK; + io_hi &= PCI_BASE_ADDRESS_IO_MASK; + if (irq == PARPORT_IRQ_AUTO) { + if (probe_one_port (io_lo, + io_hi, + pcidev->irq, + dma)) + count++; + } else if (probe_one_port (io_lo, io_hi, + irq, dma)) + count++; + } + } + } + + /* Look for parallel controllers that we don't know about. */ + for (pcidev = pci_devices; pcidev; pcidev = pcidev->next) { + const int class_noprogif = pcidev->class & ~0xff; + if (class_noprogif != (PCI_CLASS_COMMUNICATION_PARALLEL << 8)) + continue; + + for (i = 0; cards[i].vendor; i++) + if ((cards[i].vendor == pcidev->vendor) && + (cards[i].device == pcidev->device)) + break; + if (cards[i].vendor) + /* We know about this one. */ + continue; + + printk (KERN_INFO + "Unknown PCI parallel I/O card (%04x/%04x)\n" + "Please send 'lspci' output to " + "tim@cyberelk.demon.co.uk\n", + pcidev->vendor, pcidev->device); + } + + return count; +} + int parport_pc_init(int *io, int *irq, int *dma) { int count = 0, i = 0; @@ -851,13 +976,16 @@ /* Only probe the ports we were given. */ user_specified = 1; do { - count += probe_one_port(*(io++), *(irq++), *(dma++)); + unsigned long int io_hi = 0x400 + *io; + count += probe_one_port(*(io++), io_hi, + *(irq++), *(dma++)); } while (*io && (++i < PARPORT_PC_MAX_PORTS)); } else { /* Probe all the likely ports. */ - count += probe_one_port(0x3bc, irq[0], dma[0]); - count += probe_one_port(0x378, irq[0], dma[0]); - count += probe_one_port(0x278, irq[0], dma[0]); + count += probe_one_port(0x3bc, 0x7bc, irq[0], dma[0]); + count += probe_one_port(0x378, 0x778, irq[0], dma[0]); + count += probe_one_port(0x278, 0x678, irq[0], dma[0]); + count += parport_pc_init_pci (irq[0], dma[0]); } return count; diff -u --recursive --new-file v2.2.13/linux/drivers/misc/parport_share.c linux/drivers/misc/parport_share.c --- v2.2.13/linux/drivers/misc/parport_share.c Mon Jun 7 14:49:10 1999 +++ linux/drivers/misc/parport_share.c Tue Jan 4 10:12:17 2000 @@ -108,6 +108,7 @@ tmp->cad_lock = RW_LOCK_UNLOCKED; spin_lock_init(&tmp->waitlist_lock); spin_lock_init(&tmp->pardevice_lock); + tmp->base_hi = base + 0x400; name = kmalloc(15, GFP_KERNEL); if (!name) { diff -u --recursive --new-file v2.2.13/linux/drivers/net/Config.in linux/drivers/net/Config.in --- v2.2.13/linux/drivers/net/Config.in Tue Jan 4 11:10:36 2000 +++ linux/drivers/net/Config.in Tue Jan 4 10:12:17 2000 @@ -46,6 +46,7 @@ if [ "$CONFIG_PPC" = "y" ]; then tristate 'MACE (Power Mac ethernet) support' CONFIG_MACE tristate 'BMAC (G3 ethernet) support' CONFIG_BMAC + tristate 'Symbios 53c885 (Synergy ethernet) support' CONFIG_NCR885E fi if [ "$CONFIG_ZORRO" = "y" ]; then tristate 'Ariadne support' CONFIG_ARIADNE @@ -93,8 +94,6 @@ fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'RealTek 8129/8139 (not 8019/8029!) support' CONFIG_RTL8139 - tristate 'SiS 900 PCI Fast Ethernet Adapter support' CONFIG_SIS900 - tristate 'Packet Engines Yellowfin Gigabit-NIC support' CONFIG_YELLOWFIN fi bool 'Other ISA cards' CONFIG_NET_ISA if [ "$CONFIG_NET_ISA" = "y" ]; then @@ -125,15 +124,16 @@ if [ "$CONFIG_NET_EISA" = "y" ]; then tristate 'AMD PCnet32 (VLB and PCI) support' CONFIG_PCNET32 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'Alteon AceNIC/3Com 3C985/NetGear GA620 Gigabit support' CONFIG_ACENIC tristate 'Ansel Communications EISA 3200 support (EXPERIMENTAL)' CONFIG_AC3200 fi - tristate 'Apricot Xen-II on board Ethernet' CONFIG_APRICOT tristate 'CS89x0 support' CONFIG_CS89x0 tristate 'DM9102 PCI Fast Ethernet Adapter support (EXPERIMENTAL)' CONFIG_DM9102 tristate 'Generic DECchip & DIGITAL EtherWORKS PCI/EISA' CONFIG_DE4X5 tristate 'DECchip Tulip (dc21x4x) PCI support' CONFIG_DEC_ELCP + if [ "$CONFIG_DEC_ELCP" != "y" ]; then + tristate 'Old DECchip Tulip (dc21x4x) PCI support' CONFIG_DEC_ELCP_OLD + fi tristate 'Digi Intl. RightSwitch SE-X support' CONFIG_DGRS tristate 'EtherExpressPro/100 support' CONFIG_EEXPRESS_PRO100 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then @@ -143,6 +143,7 @@ tristate 'PCI NE2000 support' CONFIG_NE2K_PCI tristate 'TI ThunderLAN support' CONFIG_TLAN tristate 'VIA Rhine support' CONFIG_VIA_RHINE + tristate 'SiS 900/7016 PCI Fast Ethernet Adapter support' CONFIG_SIS900 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'Racal-Interlan EISA ES3210 support (EXPERIMENTAL)' CONFIG_ES3210 tristate 'SMC EtherPower II (EXPERIMENTAL)' CONFIG_EPIC100 @@ -159,13 +160,29 @@ endmenu +# +# Gigabit Ethernet +# + +mainmenu_option next_comment +comment 'Ethernet (1000 Mbit)' + + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'Alteon AceNIC/3Com 3C985/NetGear GA620 Gigabit support' CONFIG_ACENIC + tristate 'Packet Engines Yellowfin Gigabit-NIC support' CONFIG_YELLOWFIN + fi + tristate 'SysKonnect SK-98xx support' CONFIG_SK98LIN +endmenu + bool 'FDDI driver support' CONFIG_FDDI if [ "$CONFIG_FDDI" = "y" ]; then bool 'Digital DEFEA and DEFPA adapter support' CONFIG_DEFXX fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool 'HIPPI driver support (EXPERIMENTAL)' CONFIG_HIPPI + if [ "$CONFIG_INET" = "y" ]; then + bool 'HIPPI driver support (EXPERIMENTAL)' CONFIG_HIPPI + fi if [ "$CONFIG_HIPPI" = "y" ]; then tristate 'Essential RoadRunner HIPPI PCI adapter support' CONFIG_ROADRUNNER if [ "$CONFIG_ROADRUNNER" != "n" ]; then @@ -234,7 +251,7 @@ bool 'Fibre Channel driver support' CONFIG_NET_FC if [ "$CONFIG_NET_FC" = "y" ]; then - tristate 'Interphase 5526 Tachyon chipset based adaptor support' CONFIG_IPHASE5526 + dep_tristate 'Interphase 5526 Tachyon chipset based adaptor support' CONFIG_IPHASE5526 $CONFIG_SCSI fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then @@ -262,6 +279,27 @@ # dep_tristate 'Sealevel Systems 4021 support' CONFIG_SEALEVEL_4021 m +# +# COMX drivers +# + +tristate 'MultiGate (COMX) synchronous serial boards support' CONFIG_COMX + if [ "$CONFIG_COMX" != "n" ]; then + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate ' Support for COMX/CMX/HiCOMX boards' CONFIG_COMX_HW_COMX $CONFIG_COMX + fi + dep_tristate ' Support for LoCOMX board' CONFIG_COMX_HW_LOCOMX $CONFIG_COMX + dep_tristate ' Support for MixCOM board' CONFIG_COMX_HW_MIXCOM $CONFIG_COMX + dep_tristate ' Support for HDLC and syncPPP protocols on MultiGate boards' CONFIG_COMX_PROTO_PPP $CONFIG_COMX + if [ "$CONFIG_LAPB" = "y" ]; then + dep_tristate ' Support for LAPB protocol on MultiGate boards' CONFIG_COMX_PROTO_LAPB $CONFIG_COMX + fi + if [ "$CONFIG_LAPB" = "m" ]; then + dep_tristate ' Support for LAPB protocol on MultiGate boards' CONFIG_COMX_PROTO_LAPB $CONFIG_LAPB + fi + dep_tristate ' Support for Frame Relay on MultiGate boards' CONFIG_COMX_PROTO_FR $CONFIG_COMX +fi + tristate 'Frame relay DLCI support' CONFIG_DLCI if [ "$CONFIG_DLCI" != "n" ]; then int ' Max open DLCI' CONFIG_DLCI_COUNT 24 @@ -274,21 +312,20 @@ # if [ "$CONFIG_WAN_ROUTER" != "n" ]; then - bool 'WAN drivers' CONFIG_WAN_DRIVERS + bool 'WAN drivers' CONFIG_WAN_DRIVERS if [ "$CONFIG_WAN_DRIVERS" = "y" ]; then - dep_tristate 'Sangoma WANPIPE(tm) multiprotocol cards' CONFIG_VENDOR_SANGOMA $CONFIG_WAN_DRIVERS + dep_tristate 'Sangoma WANPIPE(tm) multi-prot cards: (select M not Y !)' CONFIG_VENDOR_SANGOMA $CONFIG_WAN_DRIVERS if [ "$CONFIG_VENDOR_SANGOMA" != "n" ]; then int ' Maximum number of cards' CONFIG_WANPIPE_CARDS 1 - bool ' WANPIPE X.25 support' CONFIG_WANPIPE_X25 + #bool ' WANPIPE X.25 support' CONFIG_WANPIPE_X25 bool ' WANPIPE Frame Relay support' CONFIG_WANPIPE_FR bool ' WANPIPE PPP support' CONFIG_WANPIPE_PPP - # bool ' WANPIPE Cisco HDLC support' CONFIG_WANPIPE_CHDLC + bool ' WANPIPE Cisco HDLC support' CONFIG_WANPIPE_CHDLC fi fi fi endmenu - # # X.25 network drivers diff -u --recursive --new-file v2.2.13/linux/drivers/net/Makefile linux/drivers/net/Makefile --- v2.2.13/linux/drivers/net/Makefile Tue Jan 4 11:10:36 2000 +++ linux/drivers/net/Makefile Tue Jan 4 10:12:17 2000 @@ -5,10 +5,14 @@ SUB_DIRS := MOD_SUB_DIRS := $(SUB_DIRS) -ALL_SUB_DIRS := $(SUB_DIRS) hamradio irda fc +ALL_SUB_DIRS := $(SUB_DIRS) hamradio irda fc sk98lin L_TARGET := net.a -L_OBJS := auto_irq.o +L_OBJS := +ifneq ($(ARCH),s390) +L_OBJS += auto_irq.o +endif + M_OBJS := MOD_LIST_NAME := NET_MODULES @@ -599,6 +603,16 @@ endif endif +ifeq ($(CONFIG_SK98LIN),y) + SUB_DIRS += sk98lin + MOD_IN_SUB_DIRS += sk98lin + L_OBJS += sk98lin/sk98lin.o +else + ifeq ($(CONFIG_SK98LIN),m) + MOD_IN_SUB_DIRS += sk98lin + endif +endif + ifeq ($(CONFIG_WAVELAN),y) L_OBJS += wavelan.o else @@ -737,6 +751,14 @@ endif endif +ifeq ($(CONFIG_DEC_ELCP_OLD),y) +L_OBJS += old_tulip.o +else + ifeq ($(CONFIG_DEC_ELCP_OLD),m) + M_OBJS += old_tulip.o + endif +endif + ifeq ($(CONFIG_ARCNET),y) LX_OBJS += arcnet.o else @@ -836,14 +858,66 @@ endif endif -# If anything built-in uses syncppp, then build it into the kernel also. -# If not, but a module uses it, build as a module. +# +# COMX drivers +# +ifeq ($(CONFIG_COMX),y) +LX_OBJS += comx.o +else + ifeq ($(CONFIG_COMX),m) + MX_OBJS += comx.o + endif +endif -ifdef CONFIG_SYNCPPP_BUILTIN -LX_OBJS += syncppp.o -else - ifdef CONFIG_SYNCPPP_MODULE - MX_OBJS += syncppp.o +ifeq ($(CONFIG_COMX_HW_COMX),y) +L_OBJS += comx-hw-comx.o +else + ifeq ($(CONFIG_COMX_HW_COMX),m) + M_OBJS += comx-hw-comx.o + endif +endif + +ifeq ($(CONFIG_COMX_HW_LOCOMX),y) +L_OBJS += comx-hw-locomx.o +CONFIG_85230_BUILTIN=y +else + ifeq ($(CONFIG_COMX_HW_LOCOMX),m) + M_OBJS += comx-hw-locomx.o + CONFIG_85230_MODULE=y + endif +endif + +ifeq ($(CONFIG_COMX_HW_MIXCOM),y) +L_OBJS += comx-hw-mixcom.o +else + ifeq ($(CONFIG_COMX_HW_MIXCOM),m) + M_OBJS += comx-hw-mixcom.o + endif +endif + +ifeq ($(CONFIG_COMX_PROTO_PPP),y) +L_OBJS += comx-proto-ppp.o +CONFIG_SYNCPPP_BUILTIN = y +else + ifeq ($(CONFIG_COMX_PROTO_PPP),m) + M_OBJS += comx-proto-ppp.o + CONFIG_SYNCPPP_MODULE = y + endif +endif + +ifeq ($(CONFIG_COMX_PROTO_LAPB),y) +L_OBJS += comx-proto-lapb.o +else + ifeq ($(CONFIG_COMX_PROTO_LAPB),m) + M_OBJS += comx-proto-lapb.o + endif +endif + +ifeq ($(CONFIG_COMX_PROTO_FR),y) +L_OBJS += comx-proto-fr.o +else + ifeq ($(CONFIG_COMX_PROTO_FR),m) + M_OBJS += comx-proto-fr.o endif endif @@ -852,9 +926,22 @@ ifdef CONFIG_85230_BUILTIN LX_OBJS += z85230.o +CONFIG_SYNCPPP_BUILTIN=y else ifdef CONFIG_85230_MODULE MX_OBJS += z85230.o + CONFIG_SYNCPPP_MODULE=y + endif +endif + +# If anything built-in uses syncppp, then build it into the kernel also. +# If not, but a module uses it, build as a module. + +ifdef CONFIG_SYNCPPP_BUILTIN +LX_OBJS += syncppp.o +else + ifdef CONFIG_SYNCPPP_MODULE + MX_OBJS += syncppp.o endif endif @@ -1089,11 +1176,20 @@ endif endif +ifeq ($(CONFIG_NCR885E),y) +L_OBJS += ncr885e.o +else + ifeq ($(CONFIG_NCR885E),m) + M_OBJS += ncr885e.o + endif +endif + ifeq ($(CONFIG_VENDOR_SANGOMA),y) LX_OBJS += sdladrv.o L_OBJS += sdlamain.o ifeq ($(CONFIG_WANPIPE_X25),y) L_OBJS += sdla_x25.o + L_OBJS += sdla_x25api.o endif ifeq ($(CONFIG_WANPIPE_FR),y) L_OBJS += sdla_fr.o @@ -1101,6 +1197,9 @@ ifeq ($(CONFIG_WANPIPE_PPP),y) L_OBJS += sdla_ppp.o endif + ifeq ($(CONFIG_WANPIPE_CHDLC),y) + L_OBJS += sdla_chdlc.o + endif endif ifeq ($(CONFIG_VENDOR_SANGOMA),m) @@ -1109,6 +1208,7 @@ WANPIPE_OBJS = sdlamain.o ifeq ($(CONFIG_WANPIPE_X25),y) WANPIPE_OBJS += sdla_x25.o + WANPIPE_OBJS += sdla_x25api.o endif ifeq ($(CONFIG_WANPIPE_FR),y) WANPIPE_OBJS += sdla_fr.o @@ -1116,6 +1216,9 @@ ifeq ($(CONFIG_WANPIPE_PPP),y) WANPIPE_OBJS += sdla_ppp.o endif + ifeq ($(CONFIG_WANPIPE_CHDLC),y) + WANPIPE_OBJS += sdla_chdlc.o + endif endif ifeq ($(CONFIG_X25_ASY),y) diff -u --recursive --new-file v2.2.13/linux/drivers/net/Space.c linux/drivers/net/Space.c --- v2.2.13/linux/drivers/net/Space.c Tue Jan 4 11:10:36 2000 +++ linux/drivers/net/Space.c Tue Jan 4 10:12:17 2000 @@ -103,6 +103,7 @@ extern int tlan_probe(struct device *); extern int mace_probe(struct device *); extern int bmac_probe(struct device *); +extern int ncr885e_probe(struct device *); extern int cs89x0_probe(struct device *dev); extern int ethertap_probe(struct device *dev); extern int ether1_probe (struct device *dev); @@ -124,6 +125,8 @@ /* Gigabit Ethernet adapters */ extern int yellowfin_probe(struct device *dev); extern int acenic_probe(struct device *dev); +extern int skge_probe(struct device *dev); + /* Detachable devices ("pocket adaptors") */ extern int atp_init(struct device *); @@ -198,7 +201,7 @@ #ifdef CONFIG_EEXPRESS_PRO100 /* Intel EtherExpress Pro/100 */ {eepro100_probe, 0}, #endif -#ifdef CONFIG_DEC_ELCP +#if defined(CONFIG_DEC_ELCP) || defined(CONFIG_DEC_ELCP_OLD) {tulip_probe, 0}, #endif #ifdef CONFIG_DE4X5 /* DEC DE425, DE434, DE435 adapters */ @@ -227,6 +230,9 @@ #ifdef CONFIG_ACENIC {acenic_probe, 0}, #endif +#ifdef CONFIG_SK98LIN + {skge_probe, 0}, +#endif #ifdef CONFIG_VIA_RHINE {via_rhine_probe, 0}, #endif @@ -454,6 +460,9 @@ #ifdef CONFIG_BMAC {bmac_probe, 0}, #endif +#ifdef CONFIG_NCR885E + {ncr885e_probe, 0}, +#endif {NULL, 0}, }; @@ -898,6 +907,47 @@ # define NEXT_DEV (&sb1000_dev) #endif +/* S/390 channels */ +#ifdef CONFIG_CTC + extern int ctc_probe(struct device *dev); + static struct device ctc7_dev = + {"ctc7", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, ctc_probe}; + static struct device ctc6_dev = + {"ctc6", 0, 0, 0, 0, 0, 0, 0, 0, 0, &ctc7_dev, ctc_probe}; + static struct device ctc5_dev = + {"ctc5", 0, 0, 0, 0, 0, 0, 0, 0, 0, &ctc6_dev, ctc_probe}; + static struct device ctc4_dev = + {"ctc4", 0, 0, 0, 0, 0, 0, 0, 0, 0, &ctc5_dev, ctc_probe}; + static struct device ctc3_dev = + {"ctc3", 0, 0, 0, 0, 0, 0, 0, 0, 0, &ctc4_dev, ctc_probe}; + static struct device ctc2_dev = + {"ctc2", 0, 0, 0, 0, 0, 0, 0, 0, 0, &ctc3_dev, ctc_probe}; + static struct device ctc1_dev = + {"ctc1", 0, 0, 0, 0, 0, 0, 0, 0, 0, &ctc2_dev, ctc_probe}; + static struct device ctc0_dev = + {"ctc0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &ctc1_dev, ctc_probe}; + + static struct device escon7_dev = + {"escon7", 0, 0, 0, 0, 0, 0, 0, 0, 0, &ctc0_dev, ctc_probe}; + static struct device escon6_dev = + {"escon6", 0, 0, 0, 0, 0, 0, 0, 0, 0, &escon7_dev, ctc_probe}; + static struct device escon5_dev = + {"escon5", 0, 0, 0, 0, 0, 0, 0, 0, 0, &escon6_dev, ctc_probe}; + static struct device escon4_dev = + {"escon4", 0, 0, 0, 0, 0, 0, 0, 0, 0, &escon5_dev, ctc_probe}; + static struct device escon3_dev = + {"escon3", 0, 0, 0, 0, 0, 0, 0, 0, 0, &escon4_dev, ctc_probe}; + static struct device escon2_dev = + {"escon2", 0, 0, 0, 0, 0, 0, 0, 0, 0, &escon3_dev, ctc_probe}; + static struct device escon1_dev = + {"escon1", 0, 0, 0, 0, 0, 0, 0, 0, 0, &escon2_dev, ctc_probe}; + static struct device escon0_dev = + {"escon0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &escon1_dev, ctc_probe}; + +#undef NEXT_DEV +#define NEXT_DEV (&escon0_dev) +#endif + extern int loopback_init(struct device *dev); struct device loopback_dev = { "lo", /* Software Loopback interface */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/arcnet.c linux/drivers/net/arcnet.c --- v2.2.13/linux/drivers/net/arcnet.c Tue Jan 4 11:10:36 2000 +++ linux/drivers/net/arcnet.c Tue Jan 4 10:12:17 2000 @@ -469,7 +469,7 @@ register_netdevice(lp->edev); #endif -#ifdef CONFIG_ARCNET_1051 +#if defined(CONFIG_ARCNET_1051) && defined(CONFIG_ARCNET_ETH) /* Initialize the RFC1051-encap protocol driver */ lp->sdev=(struct device *)kmalloc(sizeof(struct device)+10,GFP_KERNEL); if(lp->sdev == NULL) diff -u --recursive --new-file v2.2.13/linux/drivers/net/arlan-proc.c linux/drivers/net/arlan-proc.c --- v2.2.13/linux/drivers/net/arlan-proc.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/net/arlan-proc.c Tue Jan 4 10:12:17 2000 @@ -1,11 +1,11 @@ #include #include "arlan.h" +#include +#include #ifdef CONFIG_PROC_FS -#include -#include /* void enableReceive(struct device* dev); */ @@ -1001,6 +1001,11 @@ {0} }; #endif +#else +static ctl_table arlan_table[MAX_ARLANS + 1] = +{ + {0} +}; #endif static int mmtu = 1234; diff -u --recursive --new-file v2.2.13/linux/drivers/net/arlan.c linux/drivers/net/arlan.c --- v2.2.13/linux/drivers/net/arlan.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/net/arlan.c Tue Jan 4 10:12:17 2000 @@ -101,7 +101,7 @@ static void arlan_tx_done_interrupt (struct device * dev, int status); static void arlan_rx_interrupt (struct device * dev, u_char rxStatus, u_short, u_short); static void arlan_process_interrupt (struct device * dev); -static int arlan_command(struct device * dev, int command); +int arlan_command(struct device * dev, int command); EXPORT_SYMBOL(arlan_command); @@ -190,7 +190,7 @@ }; -static int arlan_command(struct device *dev, int command_p) +int arlan_command(struct device *dev, int command_p) { volatile struct arlan_shmem *arlan = ((struct arlan_private *) dev->priv)->card; diff -u --recursive --new-file v2.2.13/linux/drivers/net/comx-hw-comx.c linux/drivers/net/comx-hw-comx.c --- v2.2.13/linux/drivers/net/comx-hw-comx.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/comx-hw-comx.c Tue Jan 4 10:12:17 2000 @@ -0,0 +1,1298 @@ +/* + * Hardware-level driver for the COMX and HICOMX cards + * for Linux kernel 2.2.X + * + * Original authors: Arpad Bakay , + * Peter Bajan , + * Rewritten by: Tivadar Szemethy + * Currently maintained by: Gergely Madarasz + * + * Copyright (C) 1995-1999 ITConsult-Pro Co. + * + * 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. + * + * Version 0.80 (99/06/11): + * - port back to kernel, add support builtin driver + * - cleaned up the source code a bit + * + * Version 0.81 (99/06/22): + * - cleaned up the board load functions, no more long reset + * timeouts + * - lower modem lines on close + * - some interrupt handling fixes + * + * Version 0.82 (99/08/24): + * - fix multiple board support + * + */ + +#define VERSION "0.82" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "comx.h" +#include "comxhw.h" + +MODULE_AUTHOR("Gergely Madarasz , Tivadar Szemethy , Arpad Bakay"); +MODULE_DESCRIPTION("Hardware-level driver for the COMX and HICOMX adapters\n"); + +#define COMX_readw(dev, offset) (readw(dev->mem_start + offset + \ + (unsigned int)(((struct comx_privdata *)\ + ((struct comx_channel *)dev->priv)->HW_privdata)->channel) \ + * COMX_CHANNEL_OFFSET)) + +#define COMX_WRITE(dev, offset, value) (writew(value, dev->mem_start + offset \ + + (unsigned int)(((struct comx_privdata *) \ + ((struct comx_channel *)dev->priv)->HW_privdata)->channel) \ + * COMX_CHANNEL_OFFSET)) + +#define COMX_CMD(dev, cmd) (COMX_WRITE(dev, OFF_A_L2_CMD, cmd)) + +struct comx_firmware { + int len; + unsigned char *data; +}; + +struct comx_privdata { + struct comx_firmware *firmware; + u16 clock; + char channel; // channel no. + int memory_size; + short io_extent; + u_long histogram[5]; +}; + +struct device *memory_used[(COMX_MEM_MAX - COMX_MEM_MIN) / 0x10000]; +extern struct comx_hardware hicomx_hw; +extern struct comx_hardware comx_hw; +extern struct comx_hardware cmx_hw; + +static void COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +static void COMX_board_on(struct device *dev) +{ + outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) | + COMX_ENABLE_BOARD_IT | COMX_ENABLE_BOARD_MEM), dev->base_addr); +} + +static void COMX_board_off(struct device *dev) +{ + outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) | + COMX_ENABLE_BOARD_IT), dev->base_addr); +} + +static void HICOMX_board_on(struct device *dev) +{ + outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) | + HICOMX_ENABLE_BOARD_MEM), dev->base_addr); +} + +static void HICOMX_board_off(struct device *dev) +{ + outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) | + HICOMX_DISABLE_BOARD_MEM), dev->base_addr); +} + +static void COMX_set_clock(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct comx_privdata *hw = ch->HW_privdata; + + COMX_WRITE(dev, OFF_A_L1_CLKINI, hw->clock); +} + +static struct device *COMX_access_board(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct device *ret; + int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16; + unsigned long flags; + + save_flags(flags); cli(); + if (memory_used[mempos] == dev) { + restore_flags(flags); + return dev; + } + + if (ch->twin && memory_used[mempos] == ch->twin) { + memory_used[mempos] = dev; + ret = ch->twin; + } else { + ret = memory_used[mempos]; + if (ret) ch->HW_board_off(ret); + memory_used[mempos] = dev; + ch->HW_board_on(dev); + } + restore_flags(flags); + return ret; +} + +static void COMX_release_board(struct device *dev, struct device *savep) +{ + unsigned long flags; + int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16; + struct comx_channel *ch = dev->priv; + + save_flags(flags); cli(); + if (memory_used[mempos] == savep) { + restore_flags(flags); + return; + } + + memory_used[mempos] = savep; + if (!ch->twin || ch->twin != savep) { + ch->HW_board_off(dev); + if (savep) ch->HW_board_on(savep); + } + restore_flags(flags); +} + +static int COMX_txe(struct device *dev) +{ + struct device *savep; + struct comx_channel *ch = dev->priv; + int rc = 0; + + savep = ch->HW_access_board(dev); + if (COMX_readw(dev,OFF_A_L2_LINKUP) == LINKUP_READY) { + rc = COMX_readw(dev,OFF_A_L2_TxEMPTY); + } + ch->HW_release_board(dev,savep); + return rc; +} + +static int COMX_send_packet(struct device *dev, struct sk_buff *skb) +{ + struct device *savep; + struct comx_channel *ch = dev->priv; + struct comx_privdata *hw = ch->HW_privdata; + int ret = FRAME_DROPPED; + + savep = ch->HW_access_board(dev); + + if (ch->debug_flags & DEBUG_HW_TX) { + comx_debug_bytes(dev, skb->data, skb->len,"COMX_send packet"); + } + + if (skb->len > COMX_MAX_TX_SIZE) { + dev_kfree_skb(skb); + return FRAME_DROPPED; + } + + if ((ch->line_status & LINE_UP) && COMX_readw(dev, OFF_A_L2_TxEMPTY)) { + int lensave = skb->len; + int dest = COMX_readw(dev, OFF_A_L2_TxBUFP); + word *data = (word *)skb->data; + + writew((unsigned short)skb->len, dev->mem_start + dest); + dest += 2; + while (skb->len > 1) { + writew(*data++, dev->mem_start + dest); + dest += 2; skb->len -= 2; + } + if (skb->len == 1) { + writew(*((byte *)data), dev->mem_start + dest); + } + writew(0, dev->mem_start + (int)hw->channel * + COMX_CHANNEL_OFFSET + OFF_A_L2_TxEMPTY); + ch->stats.tx_packets++; + ch->stats.tx_bytes += lensave; + ret = FRAME_ACCEPTED; + } else { + ch->stats.tx_dropped++; + printk(KERN_INFO "%s: frame dropped\n",dev->name); + } + + ch->HW_release_board(dev, savep); + dev_kfree_skb(skb); + return ret; +} + +static inline int comx_read_buffer(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + word rbuf_offs; + struct sk_buff *skb; + word len; + int i=0; + word *writeptr; + + i = 0; + rbuf_offs = COMX_readw(dev, OFF_A_L2_RxBUFP); + len = readw(dev->mem_start + rbuf_offs); + if ((skb = dev_alloc_skb(len + 16)) == NULL) { + ch->stats.rx_dropped++; + COMX_WRITE(dev, OFF_A_L2_DAV, 0); + return 0; + } + rbuf_offs += 2; + skb_reserve(skb, 16); + skb_put(skb, len); + skb->dev = dev; + writeptr = (word *)skb->data; + while (i < len) { + *writeptr++ = readw(dev->mem_start + rbuf_offs); + rbuf_offs += 2; + i += 2; + } + COMX_WRITE(dev, OFF_A_L2_DAV, 0); + ch->stats.rx_packets++; + ch->stats.rx_bytes += len; + if (ch->debug_flags & DEBUG_HW_RX) { + comx_debug_skb(dev, skb, "COMX_interrupt receiving"); + } + ch->LINE_rx(dev, skb); + return 1; +} + +static inline char comx_line_change(struct device *dev, char linestat) +{ + struct comx_channel *ch=dev->priv; + char idle=1; + + + if (linestat & LINE_UP) { /* Vonal fol */ + if (ch->lineup_delay) { + if (!test_and_set_bit(0, &ch->lineup_pending)) { + ch->lineup_timer.function = comx_lineup_func; + ch->lineup_timer.data = (unsigned long)dev; + ch->lineup_timer.expires = jiffies + + HZ*ch->lineup_delay; + add_timer(&ch->lineup_timer); + idle=0; + } + } else { + idle=0; + ch->LINE_status(dev, ch->line_status |= LINE_UP); + } + } else { /* Vonal le */ + idle=0; + if (test_and_clear_bit(0, &ch->lineup_pending)) { + del_timer(&ch->lineup_timer); + } else { + ch->line_status &= ~LINE_UP; + if (ch->LINE_status) { + ch->LINE_status(dev, ch->line_status); + } + } + } + return idle; +} + + + +static void COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct device *dev = dev_id; + struct comx_channel *ch = dev->priv; + struct comx_privdata *hw = ch->HW_privdata; + struct device *interrupted; + unsigned long jiffs; + char idle = 0; + int count = 0; + word tmp; + + if (dev == NULL) { + printk(KERN_ERR "COMX_interrupt: irq %d for unknown device\n", irq); + return; + } + + if (dev->interrupt) { + printk(KERN_ERR "%s: re-entering interrupt handler!\n", dev->name); + return; + } + + printk("%s: entering interrupt handler\n", dev->name); + + jiffs = jiffies; + + dev->interrupt = 1; + interrupted = ch->HW_access_board(dev); + + while (!idle && count < 5000) { + char channel = 0; + idle = 1; + + while (channel < 2) { + char linestat = 0; + char buffers_emptied = 0; + + if (channel == 1) { + if (ch->twin) { + dev->interrupt = 0; + dev = ch->twin; + dev->interrupt = 1; + ch = dev->priv; + hw = ch->HW_privdata; + } else { + break; + } + } else { + COMX_WRITE(dev, OFF_A_L1_REPENA, + COMX_readw(dev, OFF_A_L1_REPENA) & 0xFF00); + } + channel++; + + if ((ch->init_status & (HW_OPEN | LINE_OPEN)) != + (HW_OPEN | LINE_OPEN)) { + continue; + } + + /* Collect stats */ + tmp = COMX_readw(dev, OFF_A_L1_ABOREC); + COMX_WRITE(dev, OFF_A_L1_ABOREC, 0); + ch->stats.rx_missed_errors += (tmp >> 8) & 0xff; + ch->stats.rx_over_errors += tmp & 0xff; + tmp = COMX_readw(dev, OFF_A_L1_CRCREC); + COMX_WRITE(dev, OFF_A_L1_CRCREC, 0); + ch->stats.rx_crc_errors += (tmp >> 8) & 0xff; + ch->stats.rx_missed_errors += tmp & 0xff; + + if ((ch->line_status & LINE_UP) && ch->LINE_rx) { + while (COMX_readw(dev, OFF_A_L2_DAV)) { + idle=0; + buffers_emptied+=comx_read_buffer(dev); + } + } + + if (COMX_readw(dev, OFF_A_L2_TxEMPTY) && ch->LINE_tx) { + ch->LINE_tx(dev); + } + + if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) { + linestat &= ~LINE_UP; + } else { + linestat |= LINE_UP; + } + + if ((linestat & LINE_UP) != (ch->line_status & LINE_UP)) { + ch->stats.tx_carrier_errors++; + idle &= comx_line_change(dev,linestat); + } + + hw->histogram[(int)buffers_emptied]++; + } + count++; + } + + if(count==5000) { + printk(KERN_WARNING "%s: interrupt stuck\n",dev->name); + } + + ch->HW_release_board(dev, interrupted); + printk("%s: leaving interrupt handler\n", dev->name); + dev->interrupt = 0; +} + +static int COMX_open(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct comx_privdata *hw = ch->HW_privdata; + struct proc_dir_entry *procfile = ch->procdir->subdir; + unsigned long jiffs; + struct device *savep; + + if (!dev->base_addr || !dev->irq || !dev->mem_start) { + return -ENODEV; + } + + if (!ch->twin || + (!(((struct comx_channel *)(ch->twin->priv))->init_status & HW_OPEN))) { + if (check_region(dev->base_addr, hw->io_extent)) { + return -EAGAIN; + } + if (request_irq(dev->irq, COMX_interrupt, SA_INTERRUPT, dev->name, + (void *)dev)) { + printk(KERN_ERR "comx-hw-comx: unable to obtain irq %d\n", dev->irq); + return -EAGAIN; + } + ch->init_status |= IRQ_ALLOCATED; + request_region(dev->base_addr, hw->io_extent, dev->name); + if (!ch->HW_load_board || ch->HW_load_board(dev)) { + release_region(dev->base_addr, hw->io_extent); + free_irq(dev->irq, (void *)dev); + ch->init_status &= ~IRQ_ALLOCATED; + return -ENODEV; + } + } + + savep = ch->HW_access_board(dev); + COMX_WRITE(dev, OFF_A_L2_LINKUP, 0); + + if (ch->HW_set_clock) { + ch->HW_set_clock(dev); + } + + COMX_CMD(dev, COMX_CMD_INIT); + jiffs = jiffies; + while (COMX_readw(dev, OFF_A_L2_LINKUP) != 1 && jiffies < jiffs + HZ) { + schedule_timeout(1); + } + + ch->HW_release_board(dev, savep); + if (jiffies > jiffs + HZ) { + printk(KERN_ERR "%s: board timeout on INIT command\n", dev->name); + release_region(dev->base_addr, hw->io_extent); + free_irq(dev->irq, (void *)dev); + return -EIO; + } + + savep = ch->HW_access_board(dev); + + /* Ide kellene irni, hogy DTE vagy DCE ? */ + COMX_CMD(dev, COMX_CMD_OPEN); + + ch->init_status |= HW_OPEN; + + /* Ez eleg ciki, de ilyen a rendszer */ + if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) { + ch->line_status &= ~LINE_UP; + } else { + ch->line_status |= LINE_UP; + } + + if (ch->LINE_status) { + ch->LINE_status(dev, ch->line_status); + } + + ch->HW_release_board(dev, savep); + + for ( ; procfile ; procfile = procfile->next) { + if (strcmp(procfile->name, FILENAME_IRQ) == 0 + || strcmp(procfile->name, FILENAME_IO) == 0 + || strcmp(procfile->name, FILENAME_MEMADDR) == 0 + || strcmp(procfile->name, FILENAME_CHANNEL) == 0 + || strcmp(procfile->name, FILENAME_FIRMWARE) == 0 + || strcmp(procfile->name, FILENAME_CLOCK) == 0) { + procfile->mode = S_IFREG | 0444; + + } + } + + return 0; +} + +static int COMX_close(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct proc_dir_entry *procfile = ch->procdir->subdir; + struct comx_privdata *hw = ch->HW_privdata; + struct comx_channel *twin_ch; + struct device *savep; + + savep = ch->HW_access_board(dev); + + COMX_CMD(dev, COMX_CMD_CLOSE); + udelay(1000); + COMX_CMD(dev, COMX_CMD_EXIT); + + ch->HW_release_board(dev, savep); + + if (ch->init_status & IRQ_ALLOCATED) { + free_irq(dev->irq, (void *)dev); + ch->init_status &= ~IRQ_ALLOCATED; + } + release_region(dev->base_addr, hw->io_extent); + + if (ch->twin && (twin_ch = ch->twin->priv) && + (twin_ch->init_status & HW_OPEN)) { + /* Pass the irq to the twin */ + if (request_irq(dev->irq, COMX_interrupt, 0, ch->twin->name, + (void *)ch->twin) == 0) { + twin_ch->init_status |= IRQ_ALLOCATED; + } + } + + for ( ; procfile ; procfile = procfile->next) { + if (strcmp(procfile->name, FILENAME_IRQ) == 0 + || strcmp(procfile->name, FILENAME_IO) == 0 + || strcmp(procfile->name, FILENAME_MEMADDR) == 0 + || strcmp(procfile->name, FILENAME_CHANNEL) == 0 + || strcmp(procfile->name, FILENAME_FIRMWARE) == 0 + || strcmp(procfile->name, FILENAME_CLOCK) == 0) { + procfile->mode = S_IFREG | 0644; + } + } + + ch->init_status &= ~HW_OPEN; + return 0; +} + +static int COMX_statistics(struct device *dev, char *page) +{ + struct comx_channel *ch = dev->priv; + struct comx_privdata *hw = ch->HW_privdata; + struct device *savep; + int len = 0; + + savep = ch->HW_access_board(dev); + + len += sprintf(page + len, "Board data: %s %s %s %s\nPBUFOVR: %02x, " + "MODSTAT: %02x, LINKUP: %02x, DAV: %02x\nRxBUFP: %02x, " + "TxEMPTY: %02x, TxBUFP: %02x\n", + (ch->init_status & HW_OPEN) ? "HW_OPEN" : "", + (ch->init_status & LINE_OPEN) ? "LINE_OPEN" : "", + (ch->init_status & FW_LOADED) ? "FW_LOADED" : "", + (ch->init_status & IRQ_ALLOCATED) ? "IRQ_ALLOCATED" : "", + COMX_readw(dev, OFF_A_L1_PBUFOVR) & 0xff, + (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) & 0xff, + COMX_readw(dev, OFF_A_L2_LINKUP) & 0xff, + COMX_readw(dev, OFF_A_L2_DAV) & 0xff, + COMX_readw(dev, OFF_A_L2_RxBUFP) & 0xff, + COMX_readw(dev, OFF_A_L2_TxEMPTY) & 0xff, + COMX_readw(dev, OFF_A_L2_TxBUFP) & 0xff); + + len += sprintf(page + len, "hist[0]: %8lu hist[1]: %8lu hist[2]: %8lu\n" + "hist[3]: %8lu hist[4]: %8lu\n",hw->histogram[0],hw->histogram[1], + hw->histogram[2],hw->histogram[3],hw->histogram[4]); + + ch->HW_release_board(dev, savep); + + return len; +} + +static int COMX_load_board(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct comx_privdata *hw = ch->HW_privdata; + struct comx_firmware *fw = hw->firmware; + word board_segment = dev->mem_start >> 16; + unsigned long jiff; + unsigned char id1, id2; + int len; + byte *COMX_address; + + if (!fw || !fw->len) { + struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL; + struct comx_privdata *twin_hw; + + if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) { + return -EAGAIN; + } + + if (!(fw = twin_hw->firmware) || !fw->len) { + return -EAGAIN; + } + } + + id1 = fw->data[OFF_FW_L1_ID]; + id2 = fw->data[OFF_FW_L1_ID + 1]; + + if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_COMX) { + printk(KERN_ERR "%s: incorrect firmware, load aborted\n", + dev->name); + return -EAGAIN; + } + + printk(KERN_INFO "%s: Loading COMX Layer 1 firmware %s\n", dev->name, + (char *)(fw->data + OFF_FW_L1_ID + 2)); + + id1 = fw->data[OFF_FW_L2_ID]; + id2 = fw->data[OFF_FW_L2_ID + 1]; + if (id1 == FW_L2_ID_1 && (id2 == 0xc0 || id2 == 0xc1 || id2 == 0xc2)) { + printk(KERN_INFO "with Layer 2 code %s\n", + (char *)(fw->data + OFF_FW_L2_ID + 2)); + } + + outb_p(board_segment | COMX_BOARD_RESET, dev->base_addr); + /* 10 usec should be enough here */ + udelay(100); + outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr); + + writeb(0, dev->mem_start + COMX_JAIL_OFFSET); + jiff=jiffies; + while(jiffies < jiff + HZ && readb(dev->mem_start + COMX_JAIL_OFFSET) + != COMX_JAIL_VALUE) { + schedule_timeout(1); + } + + if (readb(dev->mem_start + COMX_JAIL_OFFSET) != COMX_JAIL_VALUE) { + printk(KERN_ERR "%s: Can't reset board, JAIL value is %02x\n", + dev->name, readb(dev->mem_start + COMX_JAIL_OFFSET)); + outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr); + return -ENODEV; + } + + writeb(0x55, dev->mem_start + 0x18ff); + jiff=jiffies; + while(jiffies < jiff + 3*HZ && readb(dev->mem_start + 0x18ff) != 0) { + schedule_timeout(1); + } + + if(readb(dev->mem_start + 0x18ff) != 0) { + printk(KERN_ERR "%s: Can't reset board, reset timeout\n", + dev->name); + outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr); + return -ENODEV; + } + + len = 0; + COMX_address = (byte *)dev->mem_start; + while (fw->len > len) { + writeb(fw->data[len++], COMX_address++); + } + + len = 0; + COMX_address = (byte *)dev->mem_start; + while (len != fw->len && readb(COMX_address++) == fw->data[len]) { + len++; + } + + if (len != fw->len) { + printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x " + "instead of 0x%02x\n", dev->name, len, + readb(COMX_address - 1), fw->data[len]); + outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr); + return -EAGAIN; + } + + writeb(0, dev->mem_start + COMX_JAIL_OFFSET); + + jiff = jiffies; + while ( COMX_readw(dev, OFF_A_L2_LINKUP) != 1 && jiffies < jiff + 3*HZ ) { + schedule_timeout(1); + } + + if (jiffies > jiff + 3*HZ) { + printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n", + dev->name, COMX_readw(dev, OFF_A_L2_LINKUP)); + outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr); + return -EAGAIN; + } + + outb_p(board_segment | COMX_DISABLE_BOARD_MEM, dev->base_addr); + ch->init_status |= FW_LOADED; + return 0; +} + +static int CMX_load_board(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct comx_privdata *hw = ch->HW_privdata; + struct comx_firmware *fw = hw->firmware; + word board_segment = dev->mem_start >> 16; + unsigned long jiff; + #if 0 + unsigned char id1, id2; + #endif + int len; + byte *COMX_address; + + if (!fw || !fw->len) { + struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL; + struct comx_privdata *twin_hw; + + if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) { + return -EAGAIN; + } + + if (!(fw = twin_hw->firmware) || !fw->len) { + return -EAGAIN; + } + } + + /* Ide kell olyat tenni, hogy ellenorizze az ID-t */ + + if (inb_p(dev->base_addr) != CMX_ID_BYTE) { + printk(KERN_ERR "%s: CMX id byte is invalid(%02x)\n", dev->name, + inb_p(dev->base_addr)); + return -ENODEV; + } + + printk(KERN_INFO "%s: Loading CMX Layer 1 firmware %s\n", dev->name, + (char *)(fw->data + OFF_FW_L1_ID + 2)); + + outb_p(board_segment | COMX_ENABLE_BOARD_MEM | COMX_BOARD_RESET, + dev->base_addr); + + len = 0; + COMX_address = (byte *)dev->mem_start; + while (fw->len > len) { + writeb(fw->data[len++], COMX_address++); + } + + len = 0; + COMX_address = (byte *)dev->mem_start; + while (len != fw->len && readb(COMX_address++) == fw->data[len]) { + len++; + } + + outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr); + + if (len != fw->len) { + printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x " + "instead of 0x%02x\n", dev->name, len, + readb(COMX_address - 1), fw->data[len]); + outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr); + return -EAGAIN; + } + + jiff = jiffies; + while ( COMX_readw(dev, OFF_A_L2_LINKUP) != 1 && jiffies < jiff + 3*HZ ) { + schedule_timeout(1); + } + + if (jiffies > jiff + 3*HZ) { + printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n", + dev->name, COMX_readw(dev, OFF_A_L2_LINKUP)); + outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr); + return -EAGAIN; + } + + outb_p(board_segment | COMX_DISABLE_BOARD_MEM, dev->base_addr); + ch->init_status |= FW_LOADED; + return 0; +} + +static int HICOMX_load_board(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct comx_privdata *hw = ch->HW_privdata; + struct comx_firmware *fw = hw->firmware; + word board_segment = dev->mem_start >> 12; + unsigned long jiff; + unsigned char id1, id2; + int len; + word *HICOMX_address; + char id = 1; + + if (!fw || !fw->len) { + struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL; + struct comx_privdata *twin_hw; + + if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) { + return -EAGAIN; + } + + if (!(fw = twin_hw->firmware) || !fw->len) { + return -EAGAIN; + } + } + + while (id != 4) { + if (inb_p(dev->base_addr + id++) != HICOMX_ID_BYTE) { + break; + } + } + + if (id != 4) { + printk(KERN_ERR "%s: can't find HICOMX at 0x%04x, id[%d] = %02x\n", + dev->name, (unsigned int)dev->base_addr, id - 1, + inb_p(dev->base_addr + id - 1)); + return -1; + } + + id1 = fw->data[OFF_FW_L1_ID]; + id2 = fw->data[OFF_FW_L1_ID + 1]; + if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_HICOMX) { + printk(KERN_ERR "%s: incorrect firmware, load aborted\n", dev->name); + return -EAGAIN; + } + + printk(KERN_INFO "%s: Loading HICOMX Layer 1 firmware %s\n", dev->name, + (char *)(fw->data + OFF_FW_L1_ID + 2)); + + id1 = fw->data[OFF_FW_L2_ID]; + id2 = fw->data[OFF_FW_L2_ID + 1]; + if (id1 == FW_L2_ID_1 && (id2 == 0xc0 || id2 == 0xc1 || id2 == 0xc2)) { + printk(KERN_INFO "with Layer 2 code %s\n", + (char *)(fw->data + OFF_FW_L2_ID + 2)); + } + + outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr); + udelay(10); + outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr); + outb_p(HICOMX_PRG_MEM, dev->base_addr + 1); + + len = 0; + HICOMX_address = (word *)dev->mem_start; + while (fw->len > len) { + writeb(fw->data[len++], HICOMX_address++); + } + + len = 0; + HICOMX_address = (word *)dev->mem_start; + while (len != fw->len && (readw(HICOMX_address++) & 0xff) == fw->data[len]) { + len++; + } + + if (len != fw->len) { + printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x " + "instead of 0x%02x\n", dev->name, len, + readw(HICOMX_address - 1) & 0xff, fw->data[len]); + outb_p(board_segment | HICOMX_DISABLE_ALL, dev->base_addr); + outb_p(HICOMX_DATA_MEM, dev->base_addr + 1); + return -EAGAIN; + } + + outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr); + outb_p(HICOMX_DATA_MEM, dev->base_addr + 1); + outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr); + + jiff = jiffies; + while ( COMX_readw(dev, OFF_A_L2_LINKUP) != 1 && jiffies < jiff + 3*HZ ) { + schedule_timeout(1); + } + + if ( COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) { + printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n", + dev->name, COMX_readw(dev, OFF_A_L2_LINKUP)); + outb_p(board_segment | HICOMX_DISABLE_ALL, dev->base_addr); + return -EAGAIN; + } + + outb_p(board_segment | HICOMX_DISABLE_ALL, dev->base_addr); + ch->init_status |= FW_LOADED; + return 0; +} + +static struct device *comx_twin_check(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct proc_dir_entry *procfile = ch->procdir->parent->subdir; + struct comx_privdata *hw = ch->HW_privdata; + + struct device *twin; + struct comx_channel *ch_twin; + struct comx_privdata *hw_twin; + + + for ( ; procfile ; procfile = procfile->next) { + + if(!S_ISDIR(procfile->mode)) { + continue; + } + + twin=procfile->data; + ch_twin=twin->priv; + hw_twin=ch_twin->HW_privdata; + + + if (twin != dev && dev->irq && dev->base_addr && dev->mem_start && + dev->irq == twin->irq && dev->base_addr == twin->base_addr && + dev->mem_start == twin->mem_start && + hw->channel == (1 - hw_twin->channel) && + ch->hardware == ch_twin->hardware) { + return twin; + } + } + return NULL; +} + +static int comxhw_write_proc(struct file *file, const char *buffer, + u_long count, void *data) +{ + struct proc_dir_entry *entry = (struct proc_dir_entry *)data; + struct device *dev = entry->parent->data; + struct comx_channel *ch = dev->priv; + struct comx_privdata *hw = ch->HW_privdata; + char *page; + + + if (file->f_dentry->d_inode->i_ino != entry->low_ino) { + printk(KERN_ERR "comx_write_proc: file <-> data internal error\n"); + return -EIO; + } + + if(ch->init_status & HW_OPEN) { + return -EAGAIN; + } + + if (strcmp(FILENAME_FIRMWARE, entry->name) != 0) { + if (!(page = (char *)__get_free_page(GFP_KERNEL))) { + return -ENOMEM; + } + copy_from_user(page, buffer, count = (min(count, PAGE_SIZE))); + if (*(page + count - 1) == '\n') { + *(page + count - 1) = 0; + } + } else { + byte *tmp; + + if (!hw->firmware) { + if ((hw->firmware = kmalloc(sizeof(struct comx_firmware), + GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + hw->firmware->len = 0; + hw->firmware->data = NULL; + } + + if ((tmp = kmalloc(count + file->f_pos, GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + + /* Ha nem 0 a fpos, akkor meglevo file-t irunk. Gyenge trukk. */ + if (hw->firmware && hw->firmware->len && file->f_pos + && hw->firmware->len < count + file->f_pos) { + memcpy(tmp, hw->firmware->data, hw->firmware->len); + } + if (hw->firmware->data) { + kfree(hw->firmware->data); + } + copy_from_user(tmp + file->f_pos, buffer, count); + hw->firmware->len = entry->size = file->f_pos + count; + hw->firmware->data = tmp; + file->f_pos += count; + return count; + } + + if (strcmp(entry->name, FILENAME_CHANNEL) == 0) { + hw->channel = simple_strtoul(page, NULL, 0); + if (hw->channel >= MAX_CHANNELNO) { + printk(KERN_ERR "Invalid channel number\n"); + hw->channel = 0; + } + if ((ch->twin = comx_twin_check(dev)) != NULL) { + struct comx_channel *twin_ch = ch->twin->priv; + twin_ch->twin = dev; + } + } else if (strcmp(entry->name, FILENAME_IRQ) == 0) { + dev->irq = simple_strtoul(page, NULL, 0); + if (dev->irq == 2) { + dev->irq = 9; + } + if (dev->irq < 3 || dev->irq > 15) { + printk(KERN_ERR "comxhw: Invalid irq number\n"); + dev->irq = 0; + } + if ((ch->twin = comx_twin_check(dev)) != NULL) { + struct comx_channel *twin_ch = ch->twin->priv; + twin_ch->twin = dev; + } + } else if (strcmp(entry->name, FILENAME_IO) == 0) { + dev->base_addr = simple_strtoul(page, NULL, 0); + if ((dev->base_addr & 3) != 0 || dev->base_addr < 0x300 + || dev->base_addr > 0x3fc) { + printk(KERN_ERR "Invalid io value\n"); + dev->base_addr = 0; + } + if ((ch->twin = comx_twin_check(dev)) != NULL) { + struct comx_channel *twin_ch = ch->twin->priv; + + twin_ch->twin = dev; + } + } else if (strcmp(entry->name, FILENAME_MEMADDR) == 0) { + dev->mem_start = simple_strtoul(page, NULL, 0); + if (dev->mem_start <= 0xf000 && dev->mem_start >= 0xa000) { + dev->mem_start *= 16; + } + if ((dev->mem_start & 0xfff) != 0 || dev->mem_start < COMX_MEM_MIN + || dev->mem_start + hw->memory_size > COMX_MEM_MAX) { + printk(KERN_ERR "Invalid memory page\n"); + dev->mem_start = 0; + } + dev->mem_end = dev->mem_start + hw->memory_size; + if ((ch->twin = comx_twin_check(dev)) != NULL) { + struct comx_channel *twin_ch = ch->twin->priv; + + twin_ch->twin = dev; + } + } else if (strcmp(entry->name, FILENAME_CLOCK) == 0) { + if (strncmp("ext", page, 3) == 0) { + hw->clock = 0; + } else { + int kbps; + + kbps = simple_strtoul(page, NULL, 0); + hw->clock = kbps ? COMX_CLOCK_CONST/kbps : 0; + } + } + + free_page((unsigned long)page); + return count; +} + +static int comxhw_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct proc_dir_entry *file = (struct proc_dir_entry *)data; + struct device *dev = file->parent->data; + struct comx_channel *ch = dev->priv; + struct comx_privdata *hw = ch->HW_privdata; + int len = 0; + + + if (strcmp(file->name, FILENAME_IO) == 0) { + len = sprintf(page, "0x%03x\n", (unsigned int)dev->base_addr); + } else if (strcmp(file->name, FILENAME_IRQ) == 0) { + len = sprintf(page, "0x%02x\n", dev->irq == 9 ? 2 : dev->irq); + } else if (strcmp(file->name, FILENAME_CHANNEL) == 0) { + len = sprintf(page, "%01d\n", hw->channel); + } else if (strcmp(file->name, FILENAME_MEMADDR) == 0) { + len = sprintf(page, "0x%05x\n", (unsigned int)dev->mem_start); + } else if (strcmp(file->name, FILENAME_TWIN) == 0) { + len = sprintf(page, "%s\n", ch->twin ? ch->twin->name : "none"); + } else if (strcmp(file->name, FILENAME_CLOCK) == 0) { + if (hw->clock) { + len = sprintf(page, "%-8d\n", COMX_CLOCK_CONST/hw->clock); + } else { + len = sprintf(page, "external\n"); + } + } else if (strcmp(file->name, FILENAME_FIRMWARE) == 0) { + len = min(FILE_PAGESIZE, min(count, + hw->firmware ? (hw->firmware->len - off) : 0)); + if (len < 0) { + len = 0; + } + *start = hw->firmware ? (hw->firmware->data + off) : NULL; + if (off + len >= (hw->firmware ? hw->firmware->len : 0) || len == 0) { + *eof = 1; + } + return len; + } + + if (off >= len) { + *eof = 1; + return 0; + } + + *start = page + off; + if (count >= len - off) { + *eof = 1; + } + return(min(count, len - off)); +} + +/* Called on echo comx >boardtype */ +static int COMX_init(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct comx_privdata *hw; + struct proc_dir_entry *new_file; + + if ((ch->HW_privdata = kmalloc(sizeof(struct comx_privdata), + GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + memset(hw = ch->HW_privdata, 0, sizeof(struct comx_privdata)); + + if (ch->hardware == &comx_hw || ch->hardware == &cmx_hw) { + hw->memory_size = COMX_MEMORY_SIZE; + hw->io_extent = COMX_IO_EXTENT; + dev->base_addr = COMX_DEFAULT_IO; + dev->irq = COMX_DEFAULT_IRQ; + dev->mem_start = COMX_DEFAULT_MEMADDR; + dev->mem_end = COMX_DEFAULT_MEMADDR + COMX_MEMORY_SIZE; + } else if (ch->hardware == &hicomx_hw) { + hw->memory_size = HICOMX_MEMORY_SIZE; + hw->io_extent = HICOMX_IO_EXTENT; + dev->base_addr = HICOMX_DEFAULT_IO; + dev->irq = HICOMX_DEFAULT_IRQ; + dev->mem_start = HICOMX_DEFAULT_MEMADDR; + dev->mem_end = HICOMX_DEFAULT_MEMADDR + HICOMX_MEMORY_SIZE; + } else { + printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__); + } + + if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, ch->procdir)) + == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &comxhw_read_proc; + new_file->write_proc = &comxhw_write_proc; + new_file->ops = &comx_normal_inode_ops; + new_file->size = 6; + new_file->nlink = 1; + + if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, ch->procdir)) + == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &comxhw_read_proc; + new_file->write_proc = &comxhw_write_proc; + new_file->ops = &comx_normal_inode_ops; + new_file->size = 5; + new_file->nlink = 1; + + if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &comxhw_read_proc; + new_file->write_proc = &comxhw_write_proc; + new_file->ops = &comx_normal_inode_ops; + new_file->size = 2; // Ezt tudjuk + new_file->nlink = 1; + + if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) { + if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &comxhw_read_proc; + new_file->write_proc = &comxhw_write_proc; + new_file->ops = &comx_normal_inode_ops; + new_file->size = 9; + new_file->nlink = 1; + } + + if ((new_file = create_proc_entry(FILENAME_MEMADDR, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &comxhw_read_proc; + new_file->write_proc = &comxhw_write_proc; + new_file->ops = &comx_normal_inode_ops; + new_file->size = 8; + new_file->nlink = 1; + + if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &comxhw_read_proc; + new_file->write_proc = NULL; + new_file->ops = &comx_normal_inode_ops; + new_file->nlink = 1; + + if ((new_file = create_proc_entry(FILENAME_FIRMWARE, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &comxhw_read_proc; + new_file->write_proc = &comxhw_write_proc; + new_file->ops = &comx_normal_inode_ops; + new_file->nlink = 1; + + if (ch->hardware == &comx_hw) { + ch->HW_board_on = COMX_board_on; + ch->HW_board_off = COMX_board_off; + ch->HW_load_board = COMX_load_board; + } else if (ch->hardware == &cmx_hw) { + ch->HW_board_on = COMX_board_on; + ch->HW_board_off = COMX_board_off; + ch->HW_load_board = CMX_load_board; + ch->HW_set_clock = COMX_set_clock; + } else if (ch->hardware == &hicomx_hw) { + ch->HW_board_on = HICOMX_board_on; + ch->HW_board_off = HICOMX_board_off; + ch->HW_load_board = HICOMX_load_board; + ch->HW_set_clock = COMX_set_clock; + } else { + printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__); + } + + ch->HW_access_board = COMX_access_board; + ch->HW_release_board = COMX_release_board; + ch->HW_txe = COMX_txe; + ch->HW_open = COMX_open; + ch->HW_close = COMX_close; + ch->HW_send_packet = COMX_send_packet; + ch->HW_statistics = COMX_statistics; + + if ((ch->twin = comx_twin_check(dev)) != NULL) { + struct comx_channel *twin_ch = ch->twin->priv; + + twin_ch->twin = dev; + } + + MOD_INC_USE_COUNT; + return 0; +} + +/* Called on echo valami >boardtype */ +static int COMX_exit(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct comx_privdata *hw = ch->HW_privdata; + + if (hw->firmware) { + if (hw->firmware->data) kfree(hw->firmware->data); + kfree(hw->firmware); + } if (ch->twin) { + struct comx_channel *twin_ch = ch->twin->priv; + + twin_ch->twin = NULL; + } + + kfree(ch->HW_privdata); + remove_proc_entry(FILENAME_IO, ch->procdir); + remove_proc_entry(FILENAME_IRQ, ch->procdir); + remove_proc_entry(FILENAME_CHANNEL, ch->procdir); + remove_proc_entry(FILENAME_MEMADDR, ch->procdir); + remove_proc_entry(FILENAME_FIRMWARE, ch->procdir); + remove_proc_entry(FILENAME_TWIN, ch->procdir); + if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) { + remove_proc_entry(FILENAME_CLOCK, ch->procdir); + } + + MOD_DEC_USE_COUNT; + return 0; +} + +static int COMX_dump(struct device *dev) +{ + printk(KERN_INFO "%s: COMX_dump called, why ?\n", dev->name); + return 0; +} + +static struct comx_hardware comx_hw = { + "comx", + VERSION, + COMX_init, + COMX_exit, + COMX_dump, + NULL +}; + +static struct comx_hardware cmx_hw = { + "cmx", + VERSION, + COMX_init, + COMX_exit, + COMX_dump, + NULL +}; + +static struct comx_hardware hicomx_hw = { + "hicomx", + VERSION, + COMX_init, + COMX_exit, + COMX_dump, + NULL +}; + +#ifdef MODULE +#define comx_hw_comx_init init_module +#endif + +__initfunc(int comx_hw_comx_init(void)) +{ + comx_register_hardware(&comx_hw); + comx_register_hardware(&cmx_hw); + comx_register_hardware(&hicomx_hw); + memset(memory_used, 0, sizeof(memory_used)); + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + comx_unregister_hardware("comx"); + comx_unregister_hardware("cmx"); + comx_unregister_hardware("hicomx"); +} +#endif diff -u --recursive --new-file v2.2.13/linux/drivers/net/comx-hw-locomx.c linux/drivers/net/comx-hw-locomx.c --- v2.2.13/linux/drivers/net/comx-hw-locomx.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/comx-hw-locomx.c Tue Jan 4 10:12:17 2000 @@ -0,0 +1,498 @@ +/* + * Hardware driver for the LoCOMX card, using the generic z85230 + * functions + * + * Author: Gergely Madarasz + * + * Based on skeleton code and old LoCOMX driver by Tivadar Szemethy + * and the hostess_sv11 driver + * + * Copyright (C) 1999 ITConsult-Pro Co. + * + * 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. + * + * Version 0.10 (99/06/17): + * - rewritten for the z85230 layer + * + * Version 0.11 (99/06/21): + * - some printk's fixed + * - get rid of a memory leak (it was impossible though :)) + * + * Version 0.12 (99/07/07): + * - check CTS for modem lines, not DCD (which is always high + * in case of this board) + * Version 0.13 (99/07/08): + * - Fix the transmitter status check + * - Handle the net device statistics better + */ + +#define VERSION "0.13" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "comx.h" +#include "z85230.h" + +MODULE_AUTHOR("Gergely Madarasz "); +MODULE_DESCRIPTION("Hardware driver for the LoCOMX board"); + +#define RX_DMA 3 +#define TX_DMA 1 +#define LOCOMX_ID 0x33 +#define LOCOMX_IO_EXTENT 8 +#define LOCOMX_DEFAULT_IO 0x368 +#define LOCOMX_DEFAULT_IRQ 7 + +u8 z8530_locomx[] = { + 11, TCRTxCP, + 14, DTRREQ, + 255 +}; + +struct locomx_data { + int io_extent; + struct z8530_dev board; + struct timer_list status_timer; +}; + +static int LOCOMX_txe(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct locomx_data *hw = ch->HW_privdata; + + return (!hw->board.chanA.tx_next_skb); +} + + +static void locomx_rx(struct z8530_channel *c, struct sk_buff *skb) +{ + struct device *dev=c->netdevice; + struct comx_channel *ch=dev->priv; + + if (ch->debug_flags & DEBUG_HW_RX) { + comx_debug_skb(dev, skb, "locomx_rx receiving"); + } + ch->LINE_rx(dev,skb); +} + +static int LOCOMX_send_packet(struct device *dev, struct sk_buff *skb) +{ + struct comx_channel *ch = (struct comx_channel *)dev->priv; + struct locomx_data *hw = ch->HW_privdata; + + if (ch->debug_flags & DEBUG_HW_TX) { + comx_debug_bytes(dev, skb->data, skb->len, "LOCOMX_send_packet"); + } + + if (!(ch->line_status & LINE_UP)) { + return FRAME_DROPPED; + } + + if(z8530_queue_xmit(&hw->board.chanA,skb)) { + printk(KERN_WARNING "%s: FRAME_DROPPED\n",dev->name); + return FRAME_DROPPED; + } + + if (ch->debug_flags & DEBUG_HW_TX) { + comx_debug(dev, "%s: LOCOMX_send_packet was successful\n\n", dev->name); + } + + if(!hw->board.chanA.tx_next_skb) { + return FRAME_QUEUED; + } else { + return FRAME_ACCEPTED; + } +} + +static void locomx_status_timerfun(unsigned long d) +{ + struct device *dev=(struct device *)d; + struct comx_channel *ch=dev->priv; + struct locomx_data *hw=ch->HW_privdata; + + if(!(ch->line_status & LINE_UP) && + (hw->board.chanA.status & CTS)) { + ch->LINE_status(dev, ch->line_status | LINE_UP); + } + if((ch->line_status & LINE_UP) && + !(hw->board.chanA.status & CTS)) { + ch->LINE_status(dev, ch->line_status & ~LINE_UP); + } + mod_timer(&hw->status_timer,jiffies + ch->lineup_delay * HZ); +} + + +static int LOCOMX_open(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct locomx_data *hw = ch->HW_privdata; + struct proc_dir_entry *procfile = ch->procdir->subdir; + unsigned long flags; + int ret; + + if (!dev->base_addr || !dev->irq) { + return -ENODEV; + } + + if (check_region(dev->base_addr, hw->io_extent)) { + return -EAGAIN; + } + + request_region(dev->base_addr, hw->io_extent, dev->name); + + hw->board.chanA.ctrlio=dev->base_addr + 5; + hw->board.chanA.dataio=dev->base_addr + 7; + + hw->board.irq=dev->irq; + hw->board.chanA.netdevice=dev; + hw->board.chanA.dev=&hw->board; + hw->board.name=dev->name; + hw->board.chanA.txdma=TX_DMA; + hw->board.chanA.rxdma=RX_DMA; + hw->board.chanA.irqs=&z8530_nop; + hw->board.chanB.irqs=&z8530_nop; + + if(request_irq(dev->irq, z8530_interrupt, SA_INTERRUPT, + dev->name, &hw->board)) { + printk(KERN_ERR "%s: unable to obtain irq %d\n", dev->name, + dev->irq); + ret=-EAGAIN; + goto irq_fail; + } + if(request_dma(TX_DMA,"LoCOMX (TX)")) { + printk(KERN_ERR "%s: unable to obtain TX DMA (DMA channel %d)\n", + dev->name, TX_DMA); + ret=-EAGAIN; + goto dma1_fail; + } + + if(request_dma(RX_DMA,"LoCOMX (RX)")) { + printk(KERN_ERR "%s: unable to obtain RX DMA (DMA channel %d)\n", + dev->name, RX_DMA); + ret=-EAGAIN; + goto dma2_fail; + } + + save_flags(flags); + cli(); + + if(z8530_init(&hw->board)!=0) + { + printk(KERN_ERR "%s: Z8530 device not found.\n",dev->name); + ret=-ENODEV; + goto z8530_fail; + } + + hw->board.chanA.dcdcheck=CTS; + + z8530_channel_load(&hw->board.chanA, z8530_hdlc_kilostream_85230); + z8530_channel_load(&hw->board.chanA, z8530_locomx); + z8530_channel_load(&hw->board.chanB, z8530_dead_port); + + z8530_describe(&hw->board, "I/O", dev->base_addr); + + if((ret=z8530_sync_dma_open(dev, &hw->board.chanA))!=0) { + goto z8530_fail; + } + + restore_flags(flags); + + + hw->board.active=1; + hw->board.chanA.rx_function=locomx_rx; + + ch->init_status |= HW_OPEN; + if (hw->board.chanA.status & DCD) { + ch->line_status |= LINE_UP; + } else { + ch->line_status &= ~LINE_UP; + } + + comx_status(dev, ch->line_status); + + init_timer(&hw->status_timer); + hw->status_timer.function=locomx_status_timerfun; + hw->status_timer.data=(unsigned long)dev; + hw->status_timer.expires=jiffies + ch->lineup_delay * HZ; + add_timer(&hw->status_timer); + + for (; procfile ; procfile = procfile->next) { + if (strcmp(procfile->name, FILENAME_IO) == 0 || + strcmp(procfile->name, FILENAME_IRQ) == 0) { + procfile->mode = S_IFREG | 0444; + } + } + return 0; + +z8530_fail: + restore_flags(flags); + free_dma(RX_DMA); +dma2_fail: + free_dma(TX_DMA); +dma1_fail: + free_irq(dev->irq, &hw->board); +irq_fail: + release_region(dev->base_addr, hw->io_extent); + return ret; +} + +static int LOCOMX_close(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct locomx_data *hw = ch->HW_privdata; + struct proc_dir_entry *procfile = ch->procdir->subdir; + + hw->board.chanA.rx_function=z8530_null_rx; + dev->tbusy=1; + z8530_sync_dma_close(dev, &hw->board.chanA); + + z8530_shutdown(&hw->board); + + del_timer(&hw->status_timer); + free_dma(RX_DMA); + free_dma(TX_DMA); + free_irq(dev->irq,&hw->board); + release_region(dev->base_addr,8); + + for (; procfile ; procfile = procfile->next) { + if (strcmp(procfile->name, FILENAME_IO) == 0 || + strcmp(procfile->name, FILENAME_IRQ) == 0) { + procfile->mode = S_IFREG | 0644; + } + } + + ch->init_status &= ~HW_OPEN; + return 0; +} + +static int LOCOMX_statistics(struct device *dev,char *page) +{ + int len = 0; + + len += sprintf(page + len, "Hello\n"); + + return len; +} + +static int LOCOMX_dump(struct device *dev) { + printk(KERN_INFO "LOCOMX_dump called\n"); + return(-1); +} + +static int locomx_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct proc_dir_entry *file = (struct proc_dir_entry *)data; + struct device *dev = file->parent->data; + int len = 0; + + if (strcmp(file->name, FILENAME_IO) == 0) { + len = sprintf(page, "0x%x\n", (unsigned int)dev->base_addr); + } else if (strcmp(file->name, FILENAME_IRQ) == 0) { + len = sprintf(page, "%d\n", (unsigned int)dev->irq); + } else { + printk(KERN_ERR "hw_read_proc: internal error, filename %s\n", + file->name); + return -EBADF; + } + + if (off >= len) { + *eof = 1; + return 0; + } + + *start = page + off; + if (count >= len - off) { + *eof = 1; + } + return ( min(count, len - off) ); +} + +static int locomx_write_proc(struct file *file, const char *buffer, + u_long count, void *data) +{ + struct proc_dir_entry *entry = (struct proc_dir_entry *)data; + struct device *dev = (struct device *)entry->parent->data; + int val; + char *page; + + if (file->f_dentry->d_inode->i_ino != entry->low_ino) { + printk(KERN_ERR "hw_write_proc: file <-> data internal error\n"); + return -EIO; + } + + if (!(page = (char *)__get_free_page(GFP_KERNEL))) { + return -ENOMEM; + } + + copy_from_user(page, buffer, count = min(count, PAGE_SIZE)); + if (*(page + count - 1) == '\n') { + *(page + count - 1) = 0; + } + + if (strcmp(entry->name, FILENAME_IO) == 0) { + val = simple_strtoul(page, NULL, 0); + if (val != 0x360 && val != 0x368 && val != 0x370 && + val != 0x378) { + printk(KERN_ERR "LoCOMX: incorrect io address!\n"); + } else { + dev->base_addr = val; + } + } else if (strcmp(entry->name, FILENAME_IRQ) == 0) { + val = simple_strtoul(page, NULL, 0); + if (val != 3 && val != 4 && val != 5 && val != 6 && val != 7) { + printk(KERN_ERR "LoCOMX: incorrect irq value!\n"); + } else { + dev->irq = val; + } + } else { + printk(KERN_ERR "locomx_write_proc: internal error, filename %s\n", + entry->name); + free_page((unsigned long)page); + return -EBADF; + } + + free_page((unsigned long)page); + return count; +} + + + +static int LOCOMX_init(struct device *dev) +{ + struct comx_channel *ch = (struct comx_channel *)dev->priv; + struct locomx_data *hw; + struct proc_dir_entry *new_file; + + /* Alloc data for private structure */ + if ((ch->HW_privdata = kmalloc(sizeof(struct locomx_data), + GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + + memset(hw = ch->HW_privdata, 0, sizeof(struct locomx_data)); + hw->io_extent = LOCOMX_IO_EXTENT; + + /* Register /proc files */ + if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &locomx_read_proc; + new_file->write_proc = &locomx_write_proc; + new_file->ops = &comx_normal_inode_ops; + new_file->nlink = 1; + + if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &locomx_read_proc; + new_file->write_proc = &locomx_write_proc; + new_file->ops = &comx_normal_inode_ops; + new_file->nlink = 1; + +/* No clock yet */ +/* + if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &locomx_read_proc; + new_file->write_proc = &locomx_write_proc; + new_file->ops = &comx_normal_inode_ops; + new_file->nlink = 1; +*/ + + ch->HW_access_board = NULL; + ch->HW_release_board = NULL; + ch->HW_txe = LOCOMX_txe; + ch->HW_open = LOCOMX_open; + ch->HW_close = LOCOMX_close; + ch->HW_send_packet = LOCOMX_send_packet; + ch->HW_statistics = LOCOMX_statistics; + ch->HW_set_clock = NULL; + + ch->current_stats = &hw->board.chanA.stats; + memcpy(ch->current_stats, &ch->stats, sizeof(struct net_device_stats)); + + dev->base_addr = LOCOMX_DEFAULT_IO; + dev->irq = LOCOMX_DEFAULT_IRQ; + + + /* O.K. Count one more user on this module */ + MOD_INC_USE_COUNT; + return 0; +} + + +static int LOCOMX_exit(struct device *dev) +{ + struct comx_channel *ch = (struct comx_channel *)dev->priv; + + ch->HW_access_board = NULL; + ch->HW_release_board = NULL; + ch->HW_txe = NULL; + ch->HW_open = NULL; + ch->HW_close = NULL; + ch->HW_send_packet = NULL; + ch->HW_statistics = NULL; + ch->HW_set_clock = NULL; + memcpy(&ch->stats, ch->current_stats, sizeof(struct net_device_stats)); + ch->current_stats = &ch->stats; + + kfree(ch->HW_privdata); + + remove_proc_entry(FILENAME_IO, ch->procdir); + remove_proc_entry(FILENAME_IRQ, ch->procdir); +// remove_proc_entry(FILENAME_CLOCK, ch->procdir); + + MOD_DEC_USE_COUNT; + return 0; +} + +static struct comx_hardware locomx_hw = { + "locomx", + VERSION, + LOCOMX_init, + LOCOMX_exit, + LOCOMX_dump, + NULL +}; + +#ifdef MODULE +#define comx_hw_locomx_init init_module +#endif + +__initfunc(int comx_hw_locomx_init(void)) +{ + comx_register_hardware(&locomx_hw); + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + comx_unregister_hardware("locomx"); + return; +} +#endif diff -u --recursive --new-file v2.2.13/linux/drivers/net/comx-hw-mixcom.c linux/drivers/net/comx-hw-mixcom.c --- v2.2.13/linux/drivers/net/comx-hw-mixcom.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/comx-hw-mixcom.c Tue Jan 4 10:12:17 2000 @@ -0,0 +1,945 @@ +/* + * Hardware driver for the MixCom synchronous serial board + * + * Author: Gergely Madarasz + * + * based on skeleton driver code and a preliminary hscx driver by + * Tivadar Szemethy + * + * Copyright (C) 1998-1999 ITConsult-Pro Co. + * + * 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. + * + * Version 0.60 (99/06/11): + * - ported to the kernel, now works as builtin code + * + * Version 0.61 (99/06/11): + * - recognize the one-channel MixCOM card (id byte = 0x13) + * - printk fixes + * + * Version 0.62 (99/07/15): + * - fixes according to the new hw docs + * - report line status when open + * + * Version 0.63 (99/09/21): + * - line status report fixes + */ + +#define VERSION "0.63" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "comx.h" +#include "mixcom.h" +#include "hscx.h" + +MODULE_AUTHOR("Gergely Madarasz "); +MODULE_DESCRIPTION("Hardware-level driver for the serial port of the MixCom board"); + +#define MIXCOM_DATA(d) ((struct mixcom_privdata *)(COMX_CHANNEL(d)-> \ + HW_privdata)) + +#define MIXCOM_BOARD_BASE(d) (d->base_addr - MIXCOM_SERIAL_OFFSET - \ + (1 - MIXCOM_DATA(d)->channel) * MIXCOM_CHANNEL_OFFSET) + +#define MIXCOM_DEV_BASE(port,channel) (port + MIXCOM_SERIAL_OFFSET + \ + (1 - channel) * MIXCOM_CHANNEL_OFFSET) + +/* Values used to set the IRQ line */ +static unsigned char mixcom_set_irq[]={0xFF, 0xFF, 0xFF, 0x0, 0xFF, 0x2, 0x4, 0x6, 0xFF, 0xFF, 0x8, 0xA, 0xC, 0xFF, 0xE, 0xFF}; + +static unsigned char* hscx_versions[]={"A1", NULL, "A2", NULL, "A3", "2.1"}; + +struct mixcom_privdata { + u16 clock; + char channel; + char txbusy; + struct sk_buff *sending; + unsigned tx_ptr; + struct sk_buff *recving; + unsigned rx_ptr; + unsigned char status; + char card_has_status; +}; + +static inline void wr_hscx(struct device *dev, int reg, unsigned char val) +{ + outb(val, dev->base_addr + reg); +} + +static inline unsigned char rd_hscx(struct device *dev, int reg) +{ + return inb(dev->base_addr + reg); +} + +static inline void hscx_cmd(struct device *dev, int cmd) +{ + unsigned long jiffs = jiffies; + unsigned char cec; + unsigned delay = 0; + + while ((cec = (rd_hscx(dev, HSCX_STAR) & HSCX_CEC) != 0) && + (jiffs + HZ > jiffies)) { + udelay(1); + if (++delay > (100000 / HZ)) break; + } + if (cec) { + printk(KERN_WARNING "%s: CEC stuck, probably no clock!\n",dev->name); + } else { + wr_hscx(dev, HSCX_CMDR, cmd); + } +} + +static inline void hscx_fill_fifo(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct mixcom_privdata *hw = ch->HW_privdata; + register word to_send = hw->sending->len - hw->tx_ptr; + + + outsb(dev->base_addr + HSCX_FIFO, + &(hw->sending->data[hw->tx_ptr]), min(to_send, 32)); + if (to_send <= 32) { + hscx_cmd(dev, HSCX_XTF | HSCX_XME); + kfree_skb(hw->sending); + hw->sending = NULL; + hw->tx_ptr = 0; + } else { + hscx_cmd(dev, HSCX_XTF); + hw->tx_ptr += 32; + } +} + +static inline void hscx_empty_fifo(struct device *dev, int cnt) +{ + struct comx_channel *ch = dev->priv; + struct mixcom_privdata *hw = ch->HW_privdata; + + if (hw->recving == NULL) { + if (!(hw->recving = dev_alloc_skb(HSCX_MTU + 16))) { + ch->stats.rx_dropped++; + hscx_cmd(dev, HSCX_RHR); + } else { + skb_reserve(hw->recving, 16); + skb_put(hw->recving, HSCX_MTU); + } + hw->rx_ptr = 0; + } + if (cnt > 32 || !cnt || hw->recving == NULL) { + printk(KERN_ERR "hscx_empty_fifo: cnt is %d, hw->recving %p\n", + cnt, (void *)hw->recving); + return; + } + + insb(dev->base_addr + HSCX_FIFO, &(hw->recving->data[hw->rx_ptr]),cnt); + hw->rx_ptr += cnt; + hscx_cmd(dev, HSCX_RMC); +} + + +static int MIXCOM_txe(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct mixcom_privdata *hw = ch->HW_privdata; + + return !test_bit(0, &hw->txbusy); +} + +static int mixcom_probe(struct device *dev) +{ + unsigned long flags; + int id, vstr, ret=0; + + save_flags(flags); cli(); + + id=inb_p(MIXCOM_BOARD_BASE(dev) + MIXCOM_ID_OFFSET); + + if (id != MIXCOM_ID ) { + ret=-ENODEV; + printk(KERN_WARNING "%s: no MixCOM board found at 0x%04lx\n",dev->name, dev->base_addr); + goto out; + } + + vstr=inb_p(dev->base_addr + HSCX_VSTR) & 0x0f; + if(vstr>=sizeof(hscx_versions)/sizeof(char*) || + hscx_versions[vstr]==NULL) { + printk(KERN_WARNING "%s: board found but no HSCX chip detected at 0x%4lx (vstr = 0x%1x)\n",dev->name,dev->base_addr,vstr); + ret = -ENODEV; + } else { + printk(KERN_INFO "%s: HSCX chip version %s\n",dev->name,hscx_versions[vstr]); + ret = 0; + } + +out: + + restore_flags(flags); + return ret; +} + +#if 0 +static void MIXCOM_set_clock(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct mixcom_privdata *hw = ch->HW_privdata; + + if (hw->clock) { + ; + } else { + ; + } +} +#endif + +static void mixcom_board_on(struct device *dev) +{ + outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET); + udelay(1000); + outb_p(mixcom_set_irq[dev->irq] | MIXCOM_ON, + MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET); + udelay(1000); +} + +static void mixcom_board_off(struct device *dev) +{ + outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET); + udelay(1000); +} + +static void mixcom_off(struct device *dev) +{ + wr_hscx(dev, HSCX_CCR1, 0x0); +} + +static void mixcom_on(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + + wr_hscx(dev, HSCX_CCR1, HSCX_PU | HSCX_ODS | HSCX_ITF); // power up, push-pull + wr_hscx(dev, HSCX_CCR2, HSCX_CIE /* | HSCX_RIE */ ); + wr_hscx(dev, HSCX_MODE, HSCX_TRANS | HSCX_ADM8 | HSCX_RAC | HSCX_RTS ); + wr_hscx(dev, HSCX_RLCR, HSCX_RC | 47); // 1504 bytes + wr_hscx(dev, HSCX_MASK, HSCX_RSC | HSCX_TIN ); + hscx_cmd(dev, HSCX_XRES | HSCX_RHR); + + if (ch->HW_set_clock) ch->HW_set_clock(dev); + +} + +static int MIXCOM_send_packet(struct device *dev, struct sk_buff *skb) +{ + struct comx_channel *ch = dev->priv; + struct mixcom_privdata *hw = ch->HW_privdata; + unsigned long flags; + + if (ch->debug_flags & DEBUG_HW_TX) { + comx_debug_bytes(dev, skb->data, skb->len, "MIXCOM_send_packet"); + } + + if (!(ch->line_status & LINE_UP)) { + return FRAME_DROPPED; + } + + if (skb->len > HSCX_MTU) { + ch->stats.tx_errors++; + return FRAME_ERROR; + } + + save_flags(flags); cli(); + + if (test_and_set_bit(0, &hw->txbusy)) { + printk(KERN_ERR "%s: transmitter called while busy... dropping frame (length %d)\n", dev->name, skb->len); + restore_flags(flags); + return FRAME_DROPPED; + } + + + hw->sending = skb; + hw->tx_ptr = 0; + hw->txbusy = 1; +// atomic_inc(&skb->users); // save it + hscx_fill_fifo(dev); + restore_flags(flags); + + ch->stats.tx_packets++; + ch->stats.tx_bytes += skb->len; + + if (ch->debug_flags & DEBUG_HW_TX) { + comx_debug(dev, "MIXCOM_send_packet was successful\n\n"); + } + + return FRAME_ACCEPTED; +} + +static inline void mixcom_receive_frame(struct device *dev) +{ + struct comx_channel *ch=dev->priv; + struct mixcom_privdata *hw=ch->HW_privdata; + register byte rsta; + register word length; + + rsta = rd_hscx(dev, HSCX_RSTA) & (HSCX_VFR | HSCX_RDO | + HSCX_CRC | HSCX_RAB); + length = ((rd_hscx(dev, HSCX_RBCH) & 0x0f) << 8) | + rd_hscx(dev, HSCX_RBCL); + + if ( length > hw->rx_ptr ) { + hscx_empty_fifo(dev, length - hw->rx_ptr); + } + + if (!(rsta & HSCX_VFR)) { + ch->stats.rx_length_errors++; + } + if (rsta & HSCX_RDO) { + ch->stats.rx_over_errors++; + } + if (!(rsta & HSCX_CRC)) { + ch->stats.rx_crc_errors++; + } + if (rsta & HSCX_RAB) { + ch->stats.rx_frame_errors++; + } + ch->stats.rx_packets++; + ch->stats.rx_bytes += length; + + if (rsta == (HSCX_VFR | HSCX_CRC) && hw->recving) { + skb_trim(hw->recving, hw->rx_ptr - 1); + if (ch->debug_flags & DEBUG_HW_RX) { + comx_debug_skb(dev, hw->recving, + "MIXCOM_interrupt receiving"); + } + hw->recving->dev = dev; + if (ch->LINE_rx) { + ch->LINE_rx(dev, hw->recving); + } + } + else if(hw->recving) { + kfree_skb(hw->recving); + } + hw->recving = NULL; + hw->rx_ptr = 0; +} + + +static inline void mixcom_extended_interrupt(struct device *dev) +{ + struct comx_channel *ch=dev->priv; + struct mixcom_privdata *hw=ch->HW_privdata; + register byte exir; + + exir = rd_hscx(dev, HSCX_EXIR) & (HSCX_XDU | HSCX_RFO | HSCX_CSC ); + + if (exir & HSCX_RFO) { + ch->stats.rx_over_errors++; + if (hw->rx_ptr) { + kfree_skb(hw->recving); + hw->recving = NULL; hw->rx_ptr = 0; + } + printk(KERN_ERR "MIXCOM: rx overrun\n"); + hscx_cmd(dev, HSCX_RHR); + } + + if (exir & HSCX_XDU) { // xmit underrun + ch->stats.tx_errors++; + ch->stats.tx_aborted_errors++; + if (hw->tx_ptr) { + kfree_skb(hw->sending); + hw->sending = NULL; + hw->tx_ptr = 0; + } + hscx_cmd(dev, HSCX_XRES); + clear_bit(0, &hw->txbusy); + if (ch->LINE_tx) { + ch->LINE_tx(dev); + } + printk(KERN_ERR "MIXCOM: tx underrun\n"); + } + + if (exir & HSCX_CSC) { + ch->stats.tx_carrier_errors++; + if ((rd_hscx(dev, HSCX_STAR) & HSCX_CTS) == 0) { // Vonal le + if (test_and_clear_bit(0, &ch->lineup_pending)) { + del_timer(&ch->lineup_timer); + } else if (ch->line_status & LINE_UP) { + ch->line_status &= ~LINE_UP; + if (ch->LINE_status) { + ch->LINE_status(dev,ch->line_status); + } + } + } + if (!(ch->line_status & LINE_UP) && (rd_hscx(dev, HSCX_STAR) & + HSCX_CTS)) { // Vonal fol + if (!test_and_set_bit(0,&ch->lineup_pending)) { + ch->lineup_timer.function = comx_lineup_func; + ch->lineup_timer.data = (unsigned long)dev; + ch->lineup_timer.expires = jiffies + HZ * + ch->lineup_delay; + add_timer(&ch->lineup_timer); + hscx_cmd(dev, HSCX_XRES); + clear_bit(0, &hw->txbusy); + if (hw->sending) { + kfree_skb(hw->sending); + } + hw->sending=NULL; + hw->tx_ptr = 0; + } + } + } +} + + +static void MIXCOM_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned long flags; + struct device *dev = (struct device *)dev_id; + struct comx_channel *ch, *twin_ch; + struct mixcom_privdata *hw, *twin_hw; + register unsigned char ista; + + if (dev==NULL) { + printk(KERN_ERR "comx_interrupt: irq %d for unknown device\n",irq); + return; + } + + ch = dev->priv; + hw = ch->HW_privdata; + + save_flags(flags); cli(); + + while((ista = (rd_hscx(dev, HSCX_ISTA) & (HSCX_RME | HSCX_RPF | + HSCX_XPR | HSCX_EXB | HSCX_EXA | HSCX_ICA)))) { + register byte ista2 = 0; + + if (ista & HSCX_RME) { + mixcom_receive_frame(dev); + } + if (ista & HSCX_RPF) { + hscx_empty_fifo(dev, 32); + } + if (ista & HSCX_XPR) { + if (hw->tx_ptr) { + hscx_fill_fifo(dev); + } else { + clear_bit(0, &hw->txbusy); + ch->LINE_tx(dev); + } + } + + if (ista & HSCX_EXB) { + mixcom_extended_interrupt(dev); + } + + if ((ista & HSCX_EXA) && ch->twin) { + mixcom_extended_interrupt(ch->twin); + } + + if ((ista & HSCX_ICA) && ch->twin && + (ista2 = rd_hscx(ch->twin, HSCX_ISTA) & + (HSCX_RME | HSCX_RPF | HSCX_XPR ))) { + if (ista2 & HSCX_RME) { + mixcom_receive_frame(ch->twin); + } + if (ista2 & HSCX_RPF) { + hscx_empty_fifo(ch->twin, 32); + } + if (ista2 & HSCX_XPR) { + twin_ch=ch->twin->priv; + twin_hw=twin_ch->HW_privdata; + if (twin_hw->tx_ptr) { + hscx_fill_fifo(ch->twin); + } else { + clear_bit(0, &twin_hw->txbusy); + ch->LINE_tx(ch->twin); + } + } + } + } + + restore_flags(flags); + return; +} + +static int MIXCOM_open(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct mixcom_privdata *hw = ch->HW_privdata; + struct proc_dir_entry *procfile = ch->procdir->subdir; + unsigned long flags; + + if (!dev->base_addr || !dev->irq) return -ENODEV; + + + if(hw->channel==1) { + if(!TWIN(dev) || !(COMX_CHANNEL(TWIN(dev))->init_status & + IRQ_ALLOCATED)) { + printk(KERN_ERR "%s: channel 0 not yet initialized\n",dev->name); + return -EAGAIN; + } + } + + + /* Is our hw present at all ? Not checking for channel 0 if it is already + open */ + if(hw->channel!=0 || !(ch->init_status & IRQ_ALLOCATED)) { + if (check_region(dev->base_addr, MIXCOM_IO_EXTENT)) { + return -EAGAIN; + } + if (mixcom_probe(dev)) { + return -ENODEV; + } + } + + save_flags(flags); cli(); + + if(hw->channel==1) { + request_region(dev->base_addr, MIXCOM_IO_EXTENT, dev->name); + } + + if(hw->channel==0 && !(ch->init_status & IRQ_ALLOCATED)) { + if (request_irq(dev->irq, MIXCOM_interrupt, 0, + dev->name, (void *)dev)) { + printk(KERN_ERR "MIXCOM: unable to obtain irq %d\n", dev->irq); + return -EAGAIN; + } + ch->init_status|=IRQ_ALLOCATED; + request_region(dev->base_addr, MIXCOM_IO_EXTENT, dev->name); + mixcom_board_on(dev); + } + + mixcom_on(dev); + + restore_flags(flags); + + hw->status=inb(MIXCOM_BOARD_BASE(dev) + MIXCOM_STATUS_OFFSET); + if(hw->status != 0xff) { + printk(KERN_DEBUG "%s: board has status register, good\n", dev->name); + hw->card_has_status=1; + } + + hw->txbusy = 0; + ch->init_status |= HW_OPEN; + + if (rd_hscx(dev, HSCX_STAR) & HSCX_CTS) { + ch->line_status |= LINE_UP; + } else { + ch->line_status &= ~LINE_UP; + } + + ch->LINE_status(dev, ch->line_status); + + for (; procfile ; procfile = procfile->next) { + if (strcmp(procfile->name, FILENAME_IO) == 0 || + strcmp(procfile->name, FILENAME_CHANNEL) == 0 || + strcmp(procfile->name, FILENAME_CLOCK) == 0 || + strcmp(procfile->name, FILENAME_IRQ) == 0) { + procfile->mode = S_IFREG | 0444; + } + } + + return 0; +} + +static int MIXCOM_close(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct mixcom_privdata *hw = ch->HW_privdata; + struct proc_dir_entry *procfile = ch->procdir->subdir; + unsigned long flags; + + + save_flags(flags); cli(); + + mixcom_off(dev); + + /* This is channel 0, twin is not open, we can safely turn off everything */ + if(hw->channel==0 && (!(TWIN(dev)) || + !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN))) { + mixcom_board_off(dev); + free_irq(dev->irq, dev); + release_region(dev->base_addr, MIXCOM_IO_EXTENT); + ch->init_status &= ~IRQ_ALLOCATED; + } + + /* This is channel 1, channel 0 has already been shutdown, we can release + this one too */ + if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) { + if(COMX_CHANNEL(TWIN(dev))->init_status & IRQ_ALLOCATED) { + mixcom_board_off(TWIN(dev)); + free_irq(TWIN(dev)->irq, TWIN(dev)); + release_region(TWIN(dev)->base_addr, MIXCOM_IO_EXTENT); + COMX_CHANNEL(TWIN(dev))->init_status &= ~IRQ_ALLOCATED; + } + } + + /* the ioports for channel 1 can be safely released */ + if(hw->channel==1) { + release_region(dev->base_addr, MIXCOM_IO_EXTENT); + } + + restore_flags(flags); + + /* If we don't hold any hardware open */ + if(!(ch->init_status & IRQ_ALLOCATED)) { + for (; procfile ; procfile = procfile->next) { + if (strcmp(procfile->name, FILENAME_IO) == 0 || + strcmp(procfile->name, FILENAME_CHANNEL) == 0 || + strcmp(procfile->name, FILENAME_CLOCK) == 0 || + strcmp(procfile->name, FILENAME_IRQ) == 0) { + procfile->mode = S_IFREG | 0644; + } + } + } + + /* channel 0 was only waiting for us to close channel 1 + close it completely */ + + if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) { + for (procfile=COMX_CHANNEL(TWIN(dev))->procdir->subdir; + procfile ; procfile = procfile->next) { + if (strcmp(procfile->name, FILENAME_IO) == 0 || + strcmp(procfile->name, FILENAME_CHANNEL) == 0 || + strcmp(procfile->name, FILENAME_CLOCK) == 0 || + strcmp(procfile->name, FILENAME_IRQ) == 0) { + procfile->mode = S_IFREG | 0644; + } + } + } + + ch->init_status &= ~HW_OPEN; + return 0; +} + +static int MIXCOM_statistics(struct device *dev,char *page) +{ + struct comx_channel *ch = dev->priv; + // struct mixcom_privdata *hw = ch->HW_privdata; + int len = 0; + + if(ch->init_status && IRQ_ALLOCATED) { + len += sprintf(page + len, "Mixcom board: hardware open\n"); + } + + return len; +} + +static int MIXCOM_dump(struct device *dev) { + return 0; +} + +static int mixcom_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct proc_dir_entry *file = (struct proc_dir_entry *)data; + struct device *dev = file->parent->data; + struct comx_channel *ch = dev->priv; + struct mixcom_privdata *hw = ch->HW_privdata; + int len = 0; + + if (strcmp(file->name, FILENAME_IO) == 0) { + len = sprintf(page, "0x%x\n", + (unsigned int)MIXCOM_BOARD_BASE(dev)); + } else if (strcmp(file->name, FILENAME_IRQ) == 0) { + len = sprintf(page, "%d\n", (unsigned int)dev->irq); + } else if (strcmp(file->name, FILENAME_CLOCK) == 0) { + if (hw->clock) len = sprintf(page, "%d\n", hw->clock); + else len = sprintf(page, "external\n"); + } else if (strcmp(file->name, FILENAME_CHANNEL) == 0) { + len = sprintf(page, "%01d\n", hw->channel); + } else if (strcmp(file->name, FILENAME_TWIN) == 0) { + if (ch->twin) { + len = sprintf(page, "%s\n",ch->twin->name); + } else { + len = sprintf(page, "none\n"); + } + } else { + printk(KERN_ERR "mixcom_read_proc: internal error, filename %s\n", file->name); + return -EBADF; + } + + if (off >= len) { + *eof = 1; + return 0; + } + *start = page + off; + if (count >= len - off) *eof = 1; + return ( min(count, len - off) ); +} + + +static struct device *mixcom_twin_check(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct proc_dir_entry *procfile = ch->procdir->parent->subdir; + struct mixcom_privdata *hw = ch->HW_privdata; + + struct device *twin; + struct comx_channel *ch_twin; + struct mixcom_privdata *hw_twin; + + + for ( ; procfile ; procfile = procfile->next) { + if(!S_ISDIR(procfile->mode)) continue; + + twin = procfile->data; + ch_twin = twin->priv; + hw_twin = ch_twin->HW_privdata; + + + if (twin != dev && dev->irq && dev->base_addr && + dev->irq == twin->irq && + ch->hardware == ch_twin->hardware && + dev->base_addr == twin->base_addr + + (1-2*hw->channel)*MIXCOM_CHANNEL_OFFSET && + hw->channel == (1 - hw_twin->channel)) { + if (!TWIN(twin) || TWIN(twin)==dev) { + return twin; + } + } + } + return NULL; +} + + +static void setup_twin(struct device* dev) +{ + + if(TWIN(dev) && TWIN(TWIN(dev))) { + TWIN(TWIN(dev))=NULL; + } + if ((TWIN(dev) = mixcom_twin_check(dev)) != NULL) { + if (TWIN(TWIN(dev)) && TWIN(TWIN(dev)) != dev) { + TWIN(dev)=NULL; + } else { + TWIN(TWIN(dev))=dev; + } + } +} + +static int mixcom_write_proc(struct file *file, const char *buffer, + u_long count, void *data) +{ + struct proc_dir_entry *entry = (struct proc_dir_entry *)data; + struct device *dev = (struct device *)entry->parent->data; + struct comx_channel *ch = dev->priv; + struct mixcom_privdata *hw = ch->HW_privdata; + char *page; + int value; + + if (file->f_dentry->d_inode->i_ino != entry->low_ino) { + printk(KERN_ERR "mixcom_write_proc: file <-> data internal error\n"); + return -EIO; + } + + if (!(page = (char *)__get_free_page(GFP_KERNEL))) { + return -ENOMEM; + } + + copy_from_user(page, buffer, count = min(count, PAGE_SIZE)); + if (*(page + count - 1) == '\n') { + *(page + count - 1) = 0; + } + + if (strcmp(entry->name, FILENAME_IO) == 0) { + value = simple_strtoul(page, NULL, 0); + if (value != 0x180 && value != 0x280 && value != 0x380) { + printk(KERN_ERR "MIXCOM: incorrect io address!\n"); + } else { + dev->base_addr = MIXCOM_DEV_BASE(value,hw->channel); + } + } else if (strcmp(entry->name, FILENAME_IRQ) == 0) { + value = simple_strtoul(page, NULL, 0); + if (value < 0 || value > 15 || mixcom_set_irq[value]==0xFF) { + printk(KERN_ERR "MIXCOM: incorrect irq value!\n"); + } else { + dev->irq = value; + } + } else if (strcmp(entry->name, FILENAME_CLOCK) == 0) { + if (strncmp("ext", page, 3) == 0) { + hw->clock = 0; + } else { + int kbps; + + kbps = simple_strtoul(page, NULL, 0); + if (!kbps) { + hw->clock = 0; + } else { + hw->clock = kbps; + } + if (hw->clock < 32 || hw->clock > 2000) { + hw->clock = 0; + printk(KERN_ERR "MIXCOM: invalid clock rate!\n"); + } + } + if (ch->init_status & HW_OPEN && ch->HW_set_clock) { + ch->HW_set_clock(dev); + } + } else if (strcmp(entry->name, FILENAME_CHANNEL) == 0) { + value = simple_strtoul(page, NULL, 0); + if (value > 2) { + printk(KERN_ERR "Invalid channel number\n"); + } else { + dev->base_addr+=(hw->channel - value) * MIXCOM_CHANNEL_OFFSET; + hw->channel = value; + } + } else { + printk(KERN_ERR "hw_read_proc: internal error, filename %s\n", + entry->name); + return -EBADF; + } + + setup_twin(dev); + + free_page((unsigned long)page); + return count; +} + +static int MIXCOM_init(struct device *dev) { + struct comx_channel *ch = dev->priv; + struct mixcom_privdata *hw; + struct proc_dir_entry *new_file; + + if ((ch->HW_privdata = kmalloc(sizeof(struct mixcom_privdata), + GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + + memset(hw = ch->HW_privdata, 0, sizeof(struct mixcom_privdata)); + + if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &mixcom_read_proc; + new_file->write_proc = &mixcom_write_proc; + new_file->ops = &comx_normal_inode_ops; + new_file->nlink = 1; + + if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &mixcom_read_proc; + new_file->write_proc = &mixcom_write_proc; + new_file->ops = &comx_normal_inode_ops; + new_file->nlink = 1; + +#if 0 + if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &mixcom_read_proc; + new_file->write_proc = &mixcom_write_proc; + new_file->ops = &comx_normal_inode_ops; + new_file->nlink = 1; +#endif + + if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &mixcom_read_proc; + new_file->write_proc = &mixcom_write_proc; + new_file->ops = &comx_normal_inode_ops; + new_file->nlink = 1; + + if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &mixcom_read_proc; + new_file->write_proc = &mixcom_write_proc; + new_file->ops = &comx_normal_inode_ops; + new_file->nlink = 1; + + setup_twin(dev); + + /* Fill in ch_struct hw specific pointers */ + ch->HW_access_board = NULL; + ch->HW_release_board = NULL; + ch->HW_txe = MIXCOM_txe; + ch->HW_open = MIXCOM_open; + ch->HW_close = MIXCOM_close; + ch->HW_send_packet = MIXCOM_send_packet; + ch->HW_statistics = MIXCOM_statistics; + ch->HW_set_clock = NULL; + + dev->base_addr = MIXCOM_DEV_BASE(MIXCOM_DEFAULT_IO,0); + dev->irq = MIXCOM_DEFAULT_IRQ; + + MOD_INC_USE_COUNT; + return 0; +} + +static int MIXCOM_exit(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct mixcom_privdata *hw = ch->HW_privdata; + + if(hw->channel==0 && TWIN(dev)) { + return -EBUSY; + } + + if(hw->channel==1 && TWIN(dev)) { + TWIN(TWIN(dev))=NULL; + } + + kfree(ch->HW_privdata); + remove_proc_entry(FILENAME_IO, ch->procdir); + remove_proc_entry(FILENAME_IRQ, ch->procdir); +#if 0 + remove_proc_entry(FILENAME_CLOCK, ch->procdir); +#endif + remove_proc_entry(FILENAME_CHANNEL, ch->procdir); + remove_proc_entry(FILENAME_TWIN, ch->procdir); + + MOD_DEC_USE_COUNT; + return 0; +} + +static struct comx_hardware mixcomhw = { + "mixcom", + VERSION, + MIXCOM_init, + MIXCOM_exit, + MIXCOM_dump, + NULL +}; + +/* Module management */ + +#ifdef MODULE +#define comx_hw_mixcom_init init_module +#endif + +__initfunc(int comx_hw_mixcom_init(void)) +{ + return(comx_register_hardware(&mixcomhw)); +} + +#ifdef MODULE +void +cleanup_module(void) +{ + comx_unregister_hardware("mixcom"); +} +#endif diff -u --recursive --new-file v2.2.13/linux/drivers/net/comx-proto-fr.c linux/drivers/net/comx-proto-fr.c --- v2.2.13/linux/drivers/net/comx-proto-fr.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/comx-proto-fr.c Tue Jan 4 10:12:17 2000 @@ -0,0 +1,1012 @@ +/* + * Frame-relay protocol module for the COMX driver + * for Linux 2.2.X + * + * Original author: Tivadar Szemethy + * Maintainer: Gergely Madarasz + * + * Copyright (C) 1998-1999 ITConsult-Pro Co. + * + * 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. + * + * Version 0.70 (99/06/14): + * - cleaned up the source code a bit + * - ported back to kernel, now works as builtin code + * + * Version 0.71 (99/06/25): + * - use skb priorities and queues for sending keepalive + * - use device queues for slave->master data transmit + * - set IFF_RUNNING only line protocol up + * - fixes on slave device flags + * + * Version 0.72 (99/07/09): + * - handle slave tbusy with master tbusy (should be fixed) + * - fix the keepalive timer addition/deletion + */ + +#define VERSION "0.72" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "comx.h" +#include "comxhw.h" + +MODULE_AUTHOR("Author: Tivadar Szemethy "); +MODULE_DESCRIPTION("Frame Relay protocol implementation for the COMX drivers" + "for Linux kernel 2.2.X"); + +#define FRAD_UI 0x03 +#define NLPID_IP 0xcc +#define NLPID_Q933_LMI 0x08 +#define NLPID_CISCO_LMI 0x09 +#define Q933_ENQ 0x75 +#define Q933_LINESTAT 0x51 +#define Q933_COUNTERS 0x53 + +#define MAXALIVECNT 3 /* No. of failures */ + +struct fr_data { + u16 dlci; + struct device *master; + char keepa_pend; + char keepa_freq; + char keepalivecnt, keeploopcnt; + struct timer_list keepa_timer; + u8 local_cnt, remote_cnt; +}; + +static struct comx_protocol fr_master_protocol; +static struct comx_protocol fr_slave_protocol; +static struct comx_hardware fr_dlci; + +static void fr_keepalive_send(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct fr_data *fr = ch->LINE_privdata; + struct sk_buff *skb; + u8 *fr_packet; + + skb=alloc_skb(dev->hard_header_len + 13, GFP_ATOMIC); + + if(skb==NULL) + return; + + skb_reserve(skb, dev->hard_header_len); + + fr_packet=(u8*)skb_put(skb, 13); + + fr_packet[0] = (fr->dlci & (1024 - 15)) >> 2; + fr_packet[1] = (fr->dlci & 15) << 4 | 1; // EA bit 1 + fr_packet[2] = FRAD_UI; + fr_packet[3] = NLPID_Q933_LMI; + fr_packet[4] = 0; + fr_packet[5] = Q933_ENQ; + fr_packet[6] = Q933_LINESTAT; + fr_packet[7] = 0x01; + fr_packet[8] = 0x01; + fr_packet[9] = Q933_COUNTERS; + fr_packet[10] = 0x02; + fr_packet[11] = ++fr->local_cnt; + fr_packet[12] = fr->remote_cnt; + + skb->dev = dev; + skb->priority = TC_PRIO_CONTROL; + dev_queue_xmit(skb); +} + +static void fr_keepalive_timerfun(unsigned long d) +{ + struct device *dev = (struct device *)d; + struct comx_channel *ch = dev->priv; + struct fr_data *fr = ch->LINE_privdata; + struct proc_dir_entry *dir = ch->procdir->parent->subdir; + struct comx_channel *sch; + struct fr_data *sfr; + struct device *sdev; + + if (ch->init_status & LINE_OPEN) { + if (fr->keepalivecnt == MAXALIVECNT) { + comx_status(dev, ch->line_status & ~PROTO_UP); + dev->flags &= ~IFF_RUNNING; + for (; dir ; dir = dir->next) { + if(!S_ISDIR(dir->mode)) { + continue; + } + + if ((sdev = dir->data) && (sch = sdev->priv) && + (sdev->type == ARPHRD_DLCI) && + (sfr = sch->LINE_privdata) + && (sfr->master == dev) && + (sdev->flags & IFF_UP)) { + sdev->flags &= ~IFF_RUNNING; + comx_status(sdev, + sch->line_status & ~PROTO_UP); + } + } + } + if (fr->keepalivecnt <= MAXALIVECNT) { + ++fr->keepalivecnt; + } + fr_keepalive_send(dev); + } + mod_timer(&fr->keepa_timer, jiffies + HZ * fr->keepa_freq); +} + +static void fr_rx_lmi(struct device *dev, struct sk_buff *skb, + u16 dlci, u8 nlpid) +{ + struct comx_channel *ch = dev->priv; + struct fr_data *fr = ch->LINE_privdata; + struct proc_dir_entry *dir = ch->procdir->parent->subdir; + struct comx_channel *sch; + struct fr_data *sfr; + struct device *sdev; + + if (dlci != fr->dlci || nlpid != NLPID_Q933_LMI || !fr->keepa_freq) { + return; + } + + fr->remote_cnt = skb->data[7]; + if (skb->data[8] == fr->local_cnt) { // keepalive UP! + fr->keepalivecnt = 0; + if ((ch->line_status & LINE_UP) && + !(ch->line_status & PROTO_UP)) { + comx_status(dev, ch->line_status |= PROTO_UP); + dev->flags |= IFF_RUNNING; + for (; dir ; dir = dir->next) { + if(!S_ISDIR(dir->mode)) { + continue; + } + + if ((sdev = dir->data) && (sch = sdev->priv) && + (sdev->type == ARPHRD_DLCI) && + (sfr = sch->LINE_privdata) + && (sfr->master == dev) && + (sdev->flags & IFF_UP)) { + sdev->flags |= IFF_RUNNING; + comx_status(sdev, + sch->line_status | PROTO_UP); + } + } + } + } +} + +static void fr_set_keepalive(struct device *dev, int keepa) +{ + struct comx_channel *ch = dev->priv; + struct fr_data *fr = ch->LINE_privdata; + + if (!keepa && fr->keepa_freq) { // switch off + fr->keepa_freq = 0; + if (ch->line_status & LINE_UP) { + comx_status(dev, ch->line_status | PROTO_UP); + dev->flags |= IFF_RUNNING; + del_timer(&fr->keepa_timer); + } + return; + } + + if (keepa) { // bekapcs + if(fr->keepa_freq && (ch->line_status & LINE_UP)) { + del_timer(&fr->keepa_timer); + } + fr->keepa_freq = keepa; + fr->local_cnt = fr->remote_cnt = 0; + fr->keepa_timer.expires = jiffies + HZ; + fr->keepa_timer.function = fr_keepalive_timerfun; + fr->keepa_timer.data = (unsigned long)dev; + ch->line_status &= ~(PROTO_UP | PROTO_LOOP); + dev->flags &= ~IFF_RUNNING; + comx_status(dev, ch->line_status); + if(ch->line_status & LINE_UP) { + add_timer(&fr->keepa_timer); + } + } +} + +static void fr_rx(struct device *dev, struct sk_buff *skb) +{ + struct comx_channel *ch = dev->priv; + struct proc_dir_entry *dir = ch->procdir->parent->subdir; + struct device *sdev = dev; + struct comx_channel *sch; + struct fr_data *sfr; + u16 dlci; + u8 nlpid; + + if(skb->len <= 4 || skb->data[2] != FRAD_UI) { + kfree_skb(skb); + return; + } + + /* Itt majd ki kell talalni, melyik slave kapja a csomagot */ + dlci = ((skb->data[0] & 0xfc) << 2) | ((skb->data[1] & 0xf0) >> 4); + if ((nlpid = skb->data[3]) == 0) { // Optional padding + nlpid = skb->data[4]; + skb_pull(skb, 1); + } + skb_pull(skb, 4); /* DLCI and header throw away */ + + if (ch->debug_flags & DEBUG_COMX_DLCI) { + comx_debug(dev, "Frame received, DLCI: %d, NLPID: 0x%02x\n", + dlci, nlpid); + comx_debug_skb(dev, skb, "Contents"); + } + + /* Megkeressuk, kihez tartozik */ + for (; dir ; dir = dir->next) { + if(!S_ISDIR(dir->mode)) { + continue; + } + if ((sdev = dir->data) && (sch = sdev->priv) && + (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) && + (sfr->master == dev) && (sfr->dlci == dlci)) { + skb->dev = sdev; + if (ch->debug_flags & DEBUG_COMX_DLCI) { + comx_debug(dev, "Passing it to %s\n",sdev->name); + } + if (dev != sdev) { + sch->stats.rx_packets++; + sch->stats.rx_bytes += skb->len; + } + break; + } + } + switch(nlpid) { + case NLPID_IP: + skb->protocol = htons(ETH_P_IP); + skb->mac.raw = skb->data; + comx_rx(sdev, skb); + break; + case NLPID_Q933_LMI: + fr_rx_lmi(dev, skb, dlci, nlpid); + default: + kfree_skb(skb); + break; + } +} + +static int fr_tx(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct proc_dir_entry *dir = ch->procdir->parent->subdir; + struct device *sdev; + struct comx_channel *sch; + struct fr_data *sfr; + int cnt = 1; + + /* Ha minden igaz, 2 helyen fog allni a tbusy: a masternel, + es annal a slave-nel aki eppen kuldott. + Egy helyen akkor all, ha a master kuldott. + Ez megint jo lesz majd, ha utemezni akarunk */ + + /* This should be fixed, the slave tbusy should be set when + the masters queue is full and reset when not */ + + for (; dir ; dir = dir->next) { + if(!S_ISDIR(dir->mode)) { + continue; + } + if ((sdev = dir->data) && (sch = sdev->priv) && + (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) && + (sfr->master == dev) && (sdev->tbusy)) { + clear_bit(0, &sdev->tbusy); +// printk("%s: clearing tbusy\n",sdev->name); + cnt++; + } + } + + clear_bit(0, &dev->tbusy); // ezt nem talalja meg, mert ez FRAD +// printk("%s: clearing tbusy\n", dev->name); + + + mark_bh(NET_BH); + return 0; +} + +static void fr_status(struct device *dev, unsigned short status) +{ + struct comx_channel *ch = dev->priv; + struct fr_data *fr = ch->LINE_privdata; + struct proc_dir_entry *dir = ch->procdir->parent->subdir; + struct device *sdev; + struct comx_channel *sch; + struct fr_data *sfr; + + if (status & LINE_UP) { + if (!fr->keepa_freq) { + status |= PROTO_UP; + } + } else { + status &= ~(PROTO_UP | PROTO_LOOP); + } + + if (dev == fr->master && fr->keepa_freq) { + if (status & LINE_UP) { + fr->keepa_timer.expires = jiffies + HZ; + add_timer(&fr->keepa_timer); + fr->keepalivecnt = MAXALIVECNT + 1; + fr->keeploopcnt = 0; + } else { + del_timer(&fr->keepa_timer); + } + } + + /* Itt a status valtozast vegig kell vinni az osszes slave-n */ + for (; dir ; dir = dir->next) { + if(!S_ISDIR(dir->mode)) { + continue; + } + + if ((sdev = dir->data) && (sch = sdev->priv) && + (sdev->type == ARPHRD_FRAD || sdev->type == ARPHRD_DLCI) && + (sfr = sch->LINE_privdata) && (sfr->master == dev)) { + if(status & LINE_UP) { + sdev->tbusy = 0; +// printk("%s: clearing tbusy\n",sdev->name); + } + comx_status(sdev, status); + if(status & (PROTO_UP | PROTO_LOOP)) { + dev->flags |= IFF_RUNNING; + } else { + dev->flags &= ~IFF_RUNNING; + } + } + } +} + +static int fr_open(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct fr_data *fr = ch->LINE_privdata; + struct proc_dir_entry *comxdir = ch->procdir; + struct comx_channel *mch; + + if (!(ch->init_status & HW_OPEN)) { + return -ENODEV; + } + + if ((ch->hardware == &fr_dlci && ch->protocol != &fr_slave_protocol) || + (ch->protocol == &fr_slave_protocol && ch->hardware != &fr_dlci)) { + printk(KERN_ERR "Trying to open an improperly set FR interface, giving up\n"); + return -EINVAL; + } + + if (!fr->master) { + return -ENODEV; + } + mch = fr->master->priv; + if (fr->master != dev && (!(mch->init_status & LINE_OPEN) + || (mch->protocol != &fr_master_protocol))) { + printk(KERN_ERR "Master %s is inactive, or incorrectly set up, " + "unable to open %s\n", fr->master->name, dev->name); + return -ENODEV; + } + + ch->init_status |= LINE_OPEN; + ch->line_status &= ~(PROTO_UP | PROTO_LOOP); + dev->flags &= ~IFF_RUNNING; + + if (fr->master == dev) { + if (fr->keepa_freq) { + fr->keepa_timer.function = fr_keepalive_timerfun; + fr->keepa_timer.data = (unsigned long)dev; + add_timer(&fr->keepa_timer); + } else { + if (ch->line_status & LINE_UP) { + ch->line_status |= PROTO_UP; + dev->flags |= IFF_RUNNING; + } + } + } else { + ch->line_status = mch->line_status; + if(fr->master->flags & IFF_RUNNING) { + dev->flags |= IFF_RUNNING; + } + } + + for (; comxdir ; comxdir = comxdir->next) { + if (strcmp(comxdir->name, FILENAME_DLCI) == 0 || + strcmp(comxdir->name, FILENAME_MASTER) == 0 || + strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) { + comxdir->mode = S_IFREG | 0444; + } + } +// comx_status(dev, ch->line_status); + return 0; +} + +static int fr_close(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct fr_data *fr = ch->LINE_privdata; + struct proc_dir_entry *comxdir = ch->procdir; + + if (fr->master == dev) { // Ha master + struct proc_dir_entry *dir = ch->procdir->parent->subdir; + struct device *sdev = dev; + struct comx_channel *sch; + struct fr_data *sfr; + + if (!(ch->init_status & HW_OPEN)) { + return -ENODEV; + } + + if (fr->keepa_freq) { + del_timer(&fr->keepa_timer); + } + + for (; dir ; dir = dir->next) { + if(!S_ISDIR(dir->mode)) { + continue; + } + if ((sdev = dir->data) && (sch = sdev->priv) && + (sdev->type == ARPHRD_DLCI) && + (sfr = sch->LINE_privdata) && + (sfr->master == dev) && + (sch->init_status & LINE_OPEN)) { + dev_close(sdev); + } + } + } + + ch->init_status &= ~LINE_OPEN; + ch->line_status &= ~(PROTO_UP | PROTO_LOOP); + dev->flags &= ~IFF_RUNNING; + + for (; comxdir ; comxdir = comxdir->next) { + if (strcmp(comxdir->name, FILENAME_DLCI) == 0 || + strcmp(comxdir->name, FILENAME_MASTER) == 0 || + strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) { + comxdir->mode = S_IFREG | 0444; + } + } + + return 0; +} + +static int fr_xmit(struct sk_buff *skb, struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct comx_channel *sch, *mch; + struct fr_data *fr = ch->LINE_privdata; + struct fr_data *sfr; + struct device *sdev; + struct proc_dir_entry *dir = ch->procdir->parent->subdir; + + if (!fr->master) { + printk(KERN_ERR "BUG: fr_xmit without a master!!! dev: %s\n", dev->name); + return 0; + } + + mch = fr->master->priv; + + /* Ennek majd a slave utemezeskor lesz igazan jelentosege */ + if (ch->debug_flags & DEBUG_COMX_DLCI) { + comx_debug_skb(dev, skb, "Sending frame"); + } + + if (dev != fr->master) { + struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC); + newskb->dev=fr->master; + dev_queue_xmit(newskb); + dev_kfree_skb(skb); + ch->stats.tx_packets++; + ch->stats.tx_bytes += skb->len; + } else { + set_bit(0, &dev->tbusy); + for (; dir ; dir = dir->next) { + if(!S_ISDIR(dir->mode)) { + continue; + } + if ((sdev = dir->data) && (sch = sdev->priv) && + (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) && + (sfr->master == dev) && (sdev->tbusy)) { + set_bit(0, &sdev->tbusy); +// printk("%s: clearing tbusy\n",sdev->name); + } + } + +// printk("%s: set tbusy\n", dev->name); + switch(mch->HW_send_packet(dev, skb)) { + case FRAME_QUEUED: + clear_bit(0, &dev->tbusy); +// printk("%s: clear tbusy\n", dev->name); + break; + case FRAME_ACCEPTED: + case FRAME_DROPPED: + break; + case FRAME_ERROR: + printk(KERN_ERR "%s: Transmit frame error (len %d)\n", + dev->name, skb->len); + break; + } + } + return 0; +} + +static int fr_header(struct sk_buff *skb, struct device *dev, + unsigned short type, void *daddr, void *saddr, unsigned len) +{ + struct comx_channel *ch = dev->priv; + struct fr_data *fr = ch->LINE_privdata; + + skb_push(skb, dev->hard_header_len); + /* Put in DLCI */ + skb->data[0] = (fr->dlci & (1024 - 15)) >> 2; + skb->data[1] = (fr->dlci & 15) << 4 | 1; // EA bit 1 + skb->data[2] = FRAD_UI; + skb->data[3] = NLPID_IP; + + return dev->hard_header_len; +} + +static int fr_statistics(struct device *dev, char *page) +{ + struct comx_channel *ch = dev->priv; + struct fr_data *fr = ch->LINE_privdata; + int len = 0; + + if (fr->master == dev) { + struct proc_dir_entry *dir = ch->procdir->parent->subdir; + struct device *sdev; + struct comx_channel *sch; + struct fr_data *sfr; + int slaves = 0; + + len += sprintf(page + len, + "This is a Frame Relay master device\nSlaves: "); + for (; dir ; dir = dir->next) { + if(!S_ISDIR(dir->mode)) { + continue; + } + if ((sdev = dir->data) && (sch = sdev->priv) && + (sdev->type == ARPHRD_DLCI) && + (sfr = sch->LINE_privdata) && + (sfr->master == dev) && (sdev != dev)) { + slaves++; + len += sprintf(page + len, "%s ", sdev->name); + } + } + len += sprintf(page + len, "%s\n", slaves ? "" : "(none)"); + if (fr->keepa_freq) { + len += sprintf(page + len, "Line keepalive (value %d) " + "status %s [%d]\n", fr->keepa_freq, + ch->line_status & PROTO_LOOP ? "LOOP" : + ch->line_status & PROTO_UP ? "UP" : "DOWN", + fr->keepalivecnt); + } else { + len += sprintf(page + len, "Line keepalive protocol " + "is not set\n"); + } + } else { // if slave + len += sprintf(page + len, + "This is a Frame Relay slave device, master: %s\n", + fr->master ? fr->master->name : "(not set)"); + } + return len; +} + +static int fr_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct proc_dir_entry *file = (struct proc_dir_entry *)data; + struct device *dev = file->parent->data; + struct comx_channel *ch = dev->priv; + struct fr_data *fr = NULL; + int len = 0; + + if (ch) { + fr = ch->LINE_privdata; + } + + if (strcmp(file->name, FILENAME_DLCI) == 0) { + len = sprintf(page, "%04d\n", fr->dlci); + } else if (strcmp(file->name, FILENAME_MASTER) == 0) { + len = sprintf(page, "%-9s\n", fr->master ? fr->master->name : + "(none)"); + } else if (strcmp(file->name, FILENAME_KEEPALIVE) == 0) { + len = fr->keepa_freq ? sprintf(page, "% 3d\n", fr->keepa_freq) + : sprintf(page, "off\n"); + } else { + printk(KERN_ERR "comxfr: internal error, filename %s\n", file->name); + return -EBADF; + } + + if (off >= len) { + *eof = 1; + return 0; + } + + *start = page + off; + if (count >= len - off) *eof = 1; + return ( min(count, len - off) ); +} + +static int fr_write_proc(struct file *file, const char *buffer, + u_long count, void *data) +{ + struct proc_dir_entry *entry = (struct proc_dir_entry *)data; + struct device *dev = entry->parent->data; + struct comx_channel *ch = dev->priv; + struct fr_data *fr = NULL; + char *page; + + if (ch) { + fr = ch->LINE_privdata; + } + + if (file->f_dentry->d_inode->i_ino != entry->low_ino) { + printk(KERN_ERR "comxfr_write_proc: file <-> data internal error\n"); + return -EIO; + } + + if (!(page = (char *)__get_free_page(GFP_KERNEL))) { + return -ENOMEM; + } + + copy_from_user(page, buffer, count); + if (*(page + count - 1) == '\n') { + *(page + count - 1) = 0; + } + + if (strcmp(entry->name, FILENAME_DLCI) == 0) { + u16 dlci_new = simple_strtoul(page, NULL, 10); + + if (dlci_new > 1023) { + printk(KERN_ERR "Invalid DLCI value\n"); + } + else fr->dlci = dlci_new; + } else if (strcmp(entry->name, FILENAME_MASTER) == 0) { + struct device *new_master = dev_get(page); + + if (new_master && new_master->type == ARPHRD_FRAD) { + struct comx_channel *sch = new_master->priv; + struct fr_data *sfr = sch->LINE_privdata; + + if (sfr && sfr->master == new_master) { + fr->master = new_master; + /* Megorokli a master statuszat */ + ch->line_status = sch->line_status; + } + } + } else if (strcmp(entry->name, FILENAME_KEEPALIVE) == 0) { + int keepa_new = -1; + + if (strcmp(page, KEEPALIVE_OFF) == 0) { + keepa_new = 0; + } else { + keepa_new = simple_strtoul(page, NULL, 10); + } + + if (keepa_new < 0 || keepa_new > 100) { + printk(KERN_ERR "invalid keepalive\n"); + } else { + if (fr->keepa_freq && keepa_new != fr->keepa_freq) { + fr_set_keepalive(dev, 0); + } + if (keepa_new) { + fr_set_keepalive(dev, keepa_new); + } + } + } else { + printk(KERN_ERR "comxfr_write_proc: internal error, filename %s\n", + entry->name); + return -EBADF; + } + + free_page((unsigned long)page); + return count; +} + +static int fr_exit(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct fr_data *fr = ch->LINE_privdata; + struct device *sdev = dev; + struct comx_channel *sch; + struct fr_data *sfr; + struct proc_dir_entry *dir = ch->procdir->parent->subdir; + + /* Ha lezarunk egy master-t, le kell kattintani a slave-eket is */ + if (fr->master && fr->master == dev) { + for (; dir ; dir = dir->next) { + if(!S_ISDIR(dir->mode)) { + continue; + } + if ((sdev = dir->data) && (sch = sdev->priv) && + (sdev->type == ARPHRD_DLCI) && + (sfr = sch->LINE_privdata) && (sfr->master == dev)) { + dev_close(sdev); + sfr->master = NULL; + } + } + } + dev->flags = 0; + dev->type = 0; + dev->mtu = 0; + dev->hard_header_len = 0; + + ch->LINE_rx = NULL; + ch->LINE_tx = NULL; + ch->LINE_status = NULL; + ch->LINE_open = NULL; + ch->LINE_close = NULL; + ch->LINE_xmit = NULL; + ch->LINE_header = NULL; + ch->LINE_rebuild_header = NULL; + ch->LINE_statistics = NULL; + + ch->LINE_status = 0; + + if (fr->master != dev) { // if not master, remove dlci + remove_proc_entry(FILENAME_DLCI, ch->procdir); + remove_proc_entry(FILENAME_MASTER, ch->procdir); + } else { + if (fr->keepa_freq) { + fr_set_keepalive(dev, 0); + } + remove_proc_entry(FILENAME_KEEPALIVE, ch->procdir); + remove_proc_entry(FILENAME_DLCI, ch->procdir); + } + + kfree(fr); + ch->LINE_privdata = NULL; + + MOD_DEC_USE_COUNT; + return 0; +} + +static int fr_master_init(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct fr_data *fr; + struct proc_dir_entry *new_file; + + if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data), + GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + memset(fr, 0, sizeof(struct fr_data)); + fr->master = dev; // this means master + fr->dlci = 0; // let's say default + + dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; + dev->type = ARPHRD_FRAD; + dev->mtu = 1500; + dev->hard_header_len = 4; + dev->addr_len = 0; + + ch->LINE_rx = fr_rx; + ch->LINE_tx = fr_tx; + ch->LINE_status = fr_status; + ch->LINE_open = fr_open; + ch->LINE_close = fr_close; + ch->LINE_xmit = fr_xmit; + ch->LINE_header = fr_header; + ch->LINE_rebuild_header = NULL; + ch->LINE_statistics = fr_statistics; + + if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -ENOMEM; + } + new_file->data = (void *)new_file; + new_file->read_proc = &fr_read_proc; + new_file->write_proc = &fr_write_proc; + new_file->ops = &comx_normal_inode_ops; + new_file->size = 5; + new_file->nlink = 1; + + if ((new_file = create_proc_entry(FILENAME_KEEPALIVE, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -ENOMEM; + } + new_file->data = (void *)new_file; + new_file->read_proc = &fr_read_proc; + new_file->write_proc = &fr_write_proc; + new_file->ops = &comx_normal_inode_ops; + new_file->size = 4; + new_file->nlink = 1; + + fr_set_keepalive(dev, 0); + + MOD_INC_USE_COUNT; + return 0; +} + +static int fr_slave_init(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct fr_data *fr; + struct proc_dir_entry *new_file; + + if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data), + GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + memset(fr, 0, sizeof(struct fr_data)); + + dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; + dev->type = ARPHRD_DLCI; + dev->mtu = 1500; + dev->hard_header_len = 4; + dev->addr_len = 0; + + ch->LINE_rx = fr_rx; + ch->LINE_tx = fr_tx; + ch->LINE_status = fr_status; + ch->LINE_open = fr_open; + ch->LINE_close = fr_close; + ch->LINE_xmit = fr_xmit; + ch->LINE_header = fr_header; + ch->LINE_rebuild_header = NULL; + ch->LINE_statistics = fr_statistics; + + if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -ENOMEM; + } + + new_file->data = (void *)new_file; + new_file->read_proc = &fr_read_proc; + new_file->write_proc = &fr_write_proc; + new_file->ops = &comx_normal_inode_ops; + new_file->size = 5; + new_file->nlink = 1; + + if ((new_file = create_proc_entry(FILENAME_MASTER, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &fr_read_proc; + new_file->write_proc = &fr_write_proc; + new_file->ops = &comx_normal_inode_ops; + new_file->size = 10; + new_file->nlink = 1; + MOD_INC_USE_COUNT; + return 0; +} + +static int dlci_open(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + + ch->init_status |= HW_OPEN; + + MOD_INC_USE_COUNT; + return 0; +} + +static int dlci_close(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + + ch->init_status &= ~HW_OPEN; + + MOD_DEC_USE_COUNT; + return 0; +} + +static int dlci_txe(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct fr_data *fr = ch->LINE_privdata; + + if (!fr->master) { + return 0; + } + + ch = fr->master->priv; + fr = ch->LINE_privdata; + return ch->HW_txe(fr->master); +} + +static int dlci_statistics(struct device *dev, char *page) +{ + return 0; +} + +static int dlci_init(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + + ch->HW_open = dlci_open; + ch->HW_close = dlci_close; + ch->HW_txe = dlci_txe; + ch->HW_statistics = dlci_statistics; + + /* Nincs egyeb hw info, mert ugyis a fr->master-bol fog minden kiderulni */ + + MOD_INC_USE_COUNT; + return 0; +} + +static int dlci_exit(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + + ch->HW_open = NULL; + ch->HW_close = NULL; + ch->HW_txe = NULL; + ch->HW_statistics = NULL; + + MOD_DEC_USE_COUNT; + return 0; +} + +static int dlci_dump(struct device *dev) +{ + printk(KERN_INFO "dlci_dump %s, HOGY MI ???\n", dev->name); + return -1; +} + +static struct comx_protocol fr_master_protocol = { + "frad", + VERSION, + ARPHRD_FRAD, + fr_master_init, + fr_exit, + NULL +}; + +static struct comx_protocol fr_slave_protocol = { + "ietf-ip", + VERSION, + ARPHRD_DLCI, + fr_slave_init, + fr_exit, + NULL +}; + +static struct comx_hardware fr_dlci = { + "dlci", + VERSION, + dlci_init, + dlci_exit, + dlci_dump, + NULL +}; + +#ifdef MODULE +#define comx_proto_fr_init init_module +#endif + +__initfunc(int comx_proto_fr_init(void)) +{ + int ret; + + if ((ret = comx_register_hardware(&fr_dlci))) { + return ret; + } + if ((ret = comx_register_protocol(&fr_master_protocol))) { + return ret; + } + return comx_register_protocol(&fr_slave_protocol); +} + +#ifdef MODULE +void cleanup_module(void) +{ + comx_unregister_hardware(fr_dlci.name); + comx_unregister_protocol(fr_master_protocol.name); + comx_unregister_protocol(fr_slave_protocol.name); +} +#endif /* MODULE */ + diff -u --recursive --new-file v2.2.13/linux/drivers/net/comx-proto-lapb.c linux/drivers/net/comx-proto-lapb.c --- v2.2.13/linux/drivers/net/comx-proto-lapb.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/comx-proto-lapb.c Tue Jan 4 10:12:17 2000 @@ -0,0 +1,553 @@ +/* + * LAPB protocol module for the COMX driver + * for Linux kernel 2.2.X + * + * Original author: Tivadar Szemethy + * Maintainer: Gergely Madarasz + * + * Copyright (C) 1997-1999 (C) ITConsult-Pro Co. + * + * 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. + * + * Version 0.80 (99/06/14): + * - cleaned up the source code a bit + * - ported back to kernel, now works as non-module + * + */ + +#define VERSION "0.80" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "comx.h" +#include "comxhw.h" + +static struct proc_dir_entry *create_comxlapb_proc_entry(char *name, int mode, + int size, struct proc_dir_entry *dir); + +static void comxlapb_rx(struct device *dev, struct sk_buff *skb) +{ + if (!dev || !dev->priv) { + dev_kfree_skb(skb); + } else { + lapb_data_received(dev->priv, skb); + } +} + +static int comxlapb_tx(struct device *dev) +{ + clear_bit(0, &dev->tbusy); + mark_bh(NET_BH); + return 0; +} + +static int comxlapb_header(struct sk_buff *skb, struct device *dev, + unsigned short type, void *daddr, void *saddr, unsigned len) +{ + return dev->hard_header_len; +} + +static void comxlapb_status(struct device *dev, unsigned short status) +{ + struct comx_channel *ch; + + if (!dev || !(ch = dev->priv)) { + return; + } + if (status & LINE_UP) { + clear_bit(0, &dev->tbusy); + } + comx_status(dev, status); +} + +static int comxlapb_open(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + int err = 0; + + if (!(ch->init_status & HW_OPEN)) { + return -ENODEV; + } + + err = lapb_connect_request(ch); + + if (ch->debug_flags & DEBUG_COMX_LAPB) { + comx_debug(dev, "%s: lapb opened, error code: %d\n", + dev->name, err); + } + + if (!err) { + ch->init_status |= LINE_OPEN; + MOD_INC_USE_COUNT; + } + return err; +} + +static int comxlapb_close(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + + if (!(ch->init_status & HW_OPEN)) { + return -ENODEV; + } + + if (ch->debug_flags & DEBUG_COMX_LAPB) { + comx_debug(dev, "%s: lapb closed\n", dev->name); + } + + lapb_disconnect_request(ch); + + ch->init_status &= ~LINE_OPEN; + ch->line_status &= ~PROTO_UP; + MOD_DEC_USE_COUNT; + return 0; +} + +static int comxlapb_xmit(struct sk_buff *skb, struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct sk_buff *skb2; + + if (!dev || !(ch = dev->priv) || !(dev->flags & (IFF_UP | IFF_RUNNING))) { + return -ENODEV; + } + + if (dev->type == ARPHRD_X25) { // first byte tells what to do + switch(skb->data[0]) { + case 0x00: + break; // transmit + case 0x01: + lapb_connect_request(ch); + kfree_skb(skb); + return 0; + case 0x02: + lapb_disconnect_request(ch); + default: + kfree_skb(skb); + return 0; + } + skb_pull(skb,1); + } + + if (test_and_set_bit(0, &dev->tbusy)) { + ch->stats.tx_errors++; + return 1; + } + + if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) { + lapb_data_request(ch, skb2); + } + + return FRAME_ACCEPTED; +} + +static int comxlapb_statistics(struct device *dev, char *page) +{ + struct lapb_parms_struct parms; + int len = 0; + + len += sprintf(page + len, "Line status: "); + if (lapb_getparms(dev->priv, &parms) != LAPB_OK) { + len += sprintf(page + len, "not initialized\n"); + return len; + } + len += sprintf(page + len, "%s (%s), T1: %d/%d, T2: %d/%d, N2: %d/%d, " + "window: %d\n", parms.mode & LAPB_DCE ? "DCE" : "DTE", + parms.mode & LAPB_EXTENDED ? "EXTENDED" : "STANDARD", + parms.t1timer, parms.t1, parms.t2timer, parms.t2, + parms.n2count, parms.n2, parms.window); + + return len; +} + +static int comxlapb_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct proc_dir_entry *file = (struct proc_dir_entry *)data; + struct device *dev = file->parent->data; + struct lapb_parms_struct parms; + int len = 0; + + if (lapb_getparms(dev->priv, &parms)) { + return -ENODEV; + } + + if (strcmp(file->name, FILENAME_T1) == 0) { + len += sprintf(page + len, "%02u / %02u\n", + parms.t1timer, parms.t1); + } else if (strcmp(file->name, FILENAME_T2) == 0) { + len += sprintf(page + len, "%02u / %02u\n", + parms.t2timer, parms.t2); + } else if (strcmp(file->name, FILENAME_N2) == 0) { + len += sprintf(page + len, "%02u / %02u\n", + parms.n2count, parms.n2); + } else if (strcmp(file->name, FILENAME_WINDOW) == 0) { + len += sprintf(page + len, "%u\n", parms.window); + } else if (strcmp(file->name, FILENAME_MODE) == 0) { + len += sprintf(page + len, "%s, %s\n", + parms.mode & LAPB_DCE ? "DCE" : "DTE", + parms.mode & LAPB_EXTENDED ? "EXTENDED" : "STANDARD"); + } else { + printk(KERN_ERR "comxlapb: internal error, filename %s\n", file->name); + return -EBADF; + } + + if (off >= len) { + *eof = 1; + return 0; + } + + *start = page + off; + if (count >= len - off) { + *eof = 1; + } + return ( min(count, len - off) ); +} + +static int comxlapb_write_proc(struct file *file, const char *buffer, + u_long count, void *data) +{ + struct proc_dir_entry *entry = (struct proc_dir_entry *)data; + struct device *dev = entry->parent->data; + struct lapb_parms_struct parms; + unsigned long parm; + char *page; + + if (file->f_dentry->d_inode->i_ino != entry->low_ino) { + printk(KERN_ERR "comxlapb_write_proc: file <-> data internal error\n"); + return -EIO; + } + + if (lapb_getparms(dev->priv, &parms)) { + return -ENODEV; + } + + if (!(page = (char *)__get_free_page(GFP_KERNEL))) { + return -ENOMEM; + } + + copy_from_user(page, buffer, count); + if (*(page + count - 1) == '\n') { + *(page + count - 1) = 0; + } + + if (strcmp(entry->name, FILENAME_T1) == 0) { + parm=simple_strtoul(page,NULL,10); + if (parm > 0 && parm < 100) { + parms.t1=parm; + lapb_setparms(dev->priv, &parms); + } + } else if (strcmp(entry->name, FILENAME_T2) == 0) { + parm=simple_strtoul(page, NULL, 10); + if (parm > 0 && parm < 100) { + parms.t2=parm; + lapb_setparms(dev->priv, &parms); + } + } else if (strcmp(entry->name, FILENAME_N2) == 0) { + parm=simple_strtoul(page, NULL, 10); + if (parm > 0 && parm < 100) { + parms.n2=parm; + lapb_setparms(dev->priv, &parms); + } + } else if (strcmp(entry->name, FILENAME_WINDOW) == 0) { + parms.window = simple_strtoul(page, NULL, 10); + lapb_setparms(dev->priv, &parms); + } else if (strcmp(entry->name, FILENAME_MODE) == 0) { + if (comx_strcasecmp(page, "dte") == 0) { + parms.mode &= ~(LAPB_DCE | LAPB_DTE); + parms.mode |= LAPB_DTE; + } else if (comx_strcasecmp(page, "dce") == 0) { + parms.mode &= ~(LAPB_DTE | LAPB_DCE); + parms.mode |= LAPB_DCE; + } else if (comx_strcasecmp(page, "std") == 0 || + comx_strcasecmp(page, "standard") == 0) { + parms.mode &= ~LAPB_EXTENDED; + parms.mode |= LAPB_STANDARD; + } else if (comx_strcasecmp(page, "ext") == 0 || + comx_strcasecmp(page, "extended") == 0) { + parms.mode &= ~LAPB_STANDARD; + parms.mode |= LAPB_EXTENDED; + } + lapb_setparms(dev->priv, &parms); + } else { + printk(KERN_ERR "comxlapb_write_proc: internal error, filename %s\n", + entry->name); + return -EBADF; + } + + free_page((unsigned long)page); + return count; +} + +static void comxlapb_connected(void *token, int reason) +{ + struct comx_channel *ch = token; + struct proc_dir_entry *comxdir = ch->procdir->subdir; + + if (ch->debug_flags & DEBUG_COMX_LAPB) { + comx_debug(ch->dev, "%s: lapb connected, reason: %d\n", + ch->dev->name, reason); + } + + if (ch->dev->type == ARPHRD_X25) { + unsigned char *p; + struct sk_buff *skb; + + if ((skb = dev_alloc_skb(1)) == NULL) { + printk(KERN_ERR "comxlapb: out of memory!\n"); + return; + } + p = skb_put(skb,1); + *p = 0x01; // link established + skb->dev = ch->dev; + skb->protocol = htons(ETH_P_X25); + skb->mac.raw = skb->data; + skb->pkt_type = PACKET_HOST; + + netif_rx(skb); + } + + for (; comxdir; comxdir = comxdir->next) { + if (strcmp(comxdir->name, FILENAME_MODE) == 0) { + comxdir->mode = S_IFREG | 0444; + } + } + + + ch->line_status |= PROTO_UP; + comx_status(ch->dev, ch->line_status); +} + +static void comxlapb_disconnected(void *token, int reason) +{ + struct comx_channel *ch = token; + struct proc_dir_entry *comxdir = ch->procdir->subdir; + + if (ch->debug_flags & DEBUG_COMX_LAPB) { + comx_debug(ch->dev, "%s: lapb disconnected, reason: %d\n", + ch->dev->name, reason); + } + + if (ch->dev->type == ARPHRD_X25) { + unsigned char *p; + struct sk_buff *skb; + + if ((skb = dev_alloc_skb(1)) == NULL) { + printk(KERN_ERR "comxlapb: out of memory!\n"); + return; + } + p = skb_put(skb,1); + *p = 0x02; // link disconnected + skb->dev = ch->dev; + skb->protocol = htons(ETH_P_X25); + skb->mac.raw = skb->data; + skb->pkt_type = PACKET_HOST; + + netif_rx(skb); + } + + for (; comxdir; comxdir = comxdir->next) { + if (strcmp(comxdir->name, FILENAME_MODE) == 0) { + comxdir->mode = S_IFREG | 0644; + } + } + + ch->line_status &= ~PROTO_UP; + comx_status(ch->dev, ch->line_status); +} + +static void comxlapb_data_indication(void *token, struct sk_buff *skb) +{ + struct comx_channel *ch = token; + + if (ch->dev->type == ARPHRD_X25) { + skb_push(skb, 1); + skb->data[0] = 0; // indicate data for X25 + skb->protocol = htons(ETH_P_X25); + } else { + skb->protocol = htons(ETH_P_IP); + } + + skb->dev = ch->dev; + skb->mac.raw = skb->data; + comx_rx(ch->dev, skb); +} + +static void comxlapb_data_transmit(void *token, struct sk_buff *skb) +{ + struct comx_channel *ch = token; + + if (ch->HW_send_packet) { + ch->HW_send_packet(ch->dev, skb); + } +} + +static int comxlapb_exit(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + + dev->flags = 0; + dev->type = 0; + dev->mtu = 0; + dev->hard_header_len = 0; + + ch->LINE_rx = NULL; + ch->LINE_tx = NULL; + ch->LINE_status = NULL; + ch->LINE_open = NULL; + ch->LINE_close = NULL; + ch->LINE_xmit = NULL; + ch->LINE_header = NULL; + ch->LINE_statistics = NULL; + + if (ch->debug_flags & DEBUG_COMX_LAPB) { + comx_debug(dev, "%s: unregistering lapb\n", dev->name); + } + lapb_unregister(dev->priv); + + remove_proc_entry(FILENAME_T1, ch->procdir); + remove_proc_entry(FILENAME_T2, ch->procdir); + remove_proc_entry(FILENAME_N2, ch->procdir); + remove_proc_entry(FILENAME_MODE, ch->procdir); + remove_proc_entry(FILENAME_WINDOW, ch->procdir); + + MOD_DEC_USE_COUNT; + return 0; +} + +static int comxlapb_init(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct lapb_register_struct lapbreg; + + dev->mtu = 1500; + dev->hard_header_len = 4; + dev->addr_len = 0; + + ch->LINE_rx = comxlapb_rx; + ch->LINE_tx = comxlapb_tx; + ch->LINE_status = comxlapb_status; + ch->LINE_open = comxlapb_open; + ch->LINE_close = comxlapb_close; + ch->LINE_xmit = comxlapb_xmit; + ch->LINE_header = comxlapb_header; + ch->LINE_statistics = comxlapb_statistics; + + lapbreg.connect_confirmation = comxlapb_connected; + lapbreg.connect_indication = comxlapb_connected; + lapbreg.disconnect_confirmation = comxlapb_disconnected; + lapbreg.disconnect_indication = comxlapb_disconnected; + lapbreg.data_indication = comxlapb_data_indication; + lapbreg.data_transmit = comxlapb_data_transmit; + if (lapb_register(dev->priv, &lapbreg)) { + return -ENOMEM; + } + if (ch->debug_flags & DEBUG_COMX_LAPB) { + comx_debug(dev, "%s: lapb registered\n", dev->name); + } + + if (!create_comxlapb_proc_entry(FILENAME_T1, 0644, 8, ch->procdir)) { + return -ENOMEM; + } + if (!create_comxlapb_proc_entry(FILENAME_T2, 0644, 8, ch->procdir)) { + return -ENOMEM; + } + if (!create_comxlapb_proc_entry(FILENAME_N2, 0644, 8, ch->procdir)) { + return -ENOMEM; + } + if (!create_comxlapb_proc_entry(FILENAME_MODE, 0644, 14, ch->procdir)) { + return -ENOMEM; + } + if (!create_comxlapb_proc_entry(FILENAME_WINDOW, 0644, 0, ch->procdir)) { + return -ENOMEM; + } + + MOD_INC_USE_COUNT; + return 0; +} + +static int comxlapb_init_lapb(struct device *dev) +{ + dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; + dev->type = ARPHRD_LAPB; + + return(comxlapb_init(dev)); +} + +static int comxlapb_init_x25(struct device *dev) +{ + dev->flags = IFF_NOARP; + dev->type = ARPHRD_X25; + + return(comxlapb_init(dev)); +} + +static struct proc_dir_entry *create_comxlapb_proc_entry(char *name, int mode, + int size, struct proc_dir_entry *dir) +{ + struct proc_dir_entry *new_file; + + if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) { + new_file->data = (void *)new_file; + new_file->read_proc = &comxlapb_read_proc; + new_file->write_proc = &comxlapb_write_proc; + new_file->ops = &comx_normal_inode_ops; + new_file->size = size; + new_file->nlink = 1; + } + return(new_file); +} + +static struct comx_protocol comxlapb_protocol = { + "lapb", + VERSION, + ARPHRD_LAPB, + comxlapb_init_lapb, + comxlapb_exit, + NULL +}; + +static struct comx_protocol comx25_protocol = { + "x25", + VERSION, + ARPHRD_X25, + comxlapb_init_x25, + comxlapb_exit, + NULL +}; + +#ifdef MODULE +#define comx_proto_lapb_init init_module +#endif + +__initfunc(int comx_proto_lapb_init(void)) +{ + int ret; + + if ((ret = comx_register_protocol(&comxlapb_protocol)) != 0) { + return ret; + } + return comx_register_protocol(&comx25_protocol); +} + +#ifdef MODULE +void cleanup_module(void) +{ + comx_unregister_protocol(comxlapb_protocol.name); + comx_unregister_protocol(comx25_protocol.name); +} +#endif /* MODULE */ + diff -u --recursive --new-file v2.2.13/linux/drivers/net/comx-proto-ppp.c linux/drivers/net/comx-proto-ppp.c --- v2.2.13/linux/drivers/net/comx-proto-ppp.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/comx-proto-ppp.c Tue Jan 4 10:12:17 2000 @@ -0,0 +1,265 @@ +/* + * Synchronous PPP / Cisco-HDLC driver for the COMX boards + * + * Author: Gergely Madarasz + * + * based on skeleton code by Tivadar Szemethy + * + * Copyright (C) 1999 ITConsult-Pro Co. + * + * 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. + * + * + * Version 0.10 (99/06/10): + * - written the first code :) + * + * Version 0.20 (99/06/16): + * - added hdlc protocol + * - protocol up is IFF_RUNNING + * + * Version 0.21 (99/07/15): + * - some small fixes with the line status + * + * Version 0.22 (99/08/05): + * - don't test IFF_RUNNING but the pp_link_state of the sppp + * + */ + +#define VERSION "0.22" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "syncppp.h" +#include "comx.h" + +MODULE_AUTHOR("Author: Gergely Madarasz "); +MODULE_DESCRIPTION("Cisco-HDLC / Synchronous PPP driver for the COMX sync serial boards"); + +static struct comx_protocol syncppp_protocol; +static struct comx_protocol hdlc_protocol; + +struct syncppp_data { + struct timer_list status_timer; +}; + +static void syncppp_status_timerfun(unsigned long d) { + struct device *dev=(struct device *)d; + struct comx_channel *ch=dev->priv; + struct syncppp_data *spch=ch->LINE_privdata; + struct sppp *sp = &((struct ppp_device *)dev)->sppp; + + if(!(ch->line_status & PROTO_UP) && + (sp->pp_link_state==SPPP_LINK_UP)) { + comx_status(dev, ch->line_status | PROTO_UP); + } + if((ch->line_status & PROTO_UP) && + (sp->pp_link_state==SPPP_LINK_DOWN)) { + comx_status(dev, ch->line_status & ~PROTO_UP); + } + mod_timer(&spch->status_timer,jiffies + HZ*3); +} + +static int syncppp_tx(struct device *dev) +{ + clear_bit(0, &dev->tbusy); + mark_bh(NET_BH); + return 0; +} + +static void syncppp_status(struct device *dev, unsigned short status) +{ + status &= ~(PROTO_UP | PROTO_LOOP); + if(status & LINE_UP) { + dev->tbusy = 0; /* Just to be sure */ + sppp_open(dev); + } else { + /* Line went down */ + sppp_close(dev); + } + comx_status(dev, status); +} + +static int syncppp_open(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct syncppp_data *spch = ch->LINE_privdata; + + if (!(ch->init_status & HW_OPEN)) return -ENODEV; + + ch->init_status |= LINE_OPEN; + ch->line_status &= ~(PROTO_UP | PROTO_LOOP); + + if(ch->line_status & LINE_UP) { + sppp_open(dev); + } + + init_timer(&spch->status_timer); + spch->status_timer.function=syncppp_status_timerfun; + spch->status_timer.data=(unsigned long)dev; + spch->status_timer.expires=jiffies + HZ*3; + add_timer(&spch->status_timer); + + return 0; +} + +static int syncppp_close(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct syncppp_data *spch = ch->LINE_privdata; + + if (!(ch->init_status & HW_OPEN)) return -ENODEV; + del_timer(&spch->status_timer); + + sppp_close(dev); + + ch->init_status &= ~LINE_OPEN; + ch->line_status &= ~(PROTO_UP | PROTO_LOOP); + + return 0; +} + +static int syncppp_xmit(struct sk_buff *skb, struct device *dev) +{ + struct comx_channel *ch = dev->priv; + + if (test_and_set_bit(0, &dev->tbusy)) { + ch->stats.tx_errors++; + return 0; + } + switch(ch->HW_send_packet(dev, skb)) { + case FRAME_QUEUED: + clear_bit(0, &dev->tbusy); + break; + case FRAME_ACCEPTED: + case FRAME_DROPPED: + break; + case FRAME_ERROR: + printk(KERN_ERR "%s: Transmit frame error (len %d)\n", + dev->name, skb->len); + break; + } + return 0; +} + + +static int syncppp_statistics(struct device *dev, char *page) +{ + int len = 0; + + len += sprintf(page + len, " "); + return len; +} + + +static int syncppp_exit(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + + sppp_detach(dev); + + dev->flags = 0; + dev->type = 0; + dev->mtu = 0; + + ch->LINE_rx = NULL; + ch->LINE_tx = NULL; + ch->LINE_status = NULL; + ch->LINE_open = NULL; + ch->LINE_close = NULL; + ch->LINE_xmit = NULL; + ch->LINE_header = NULL; + ch->LINE_rebuild_header = NULL; + ch->LINE_statistics = NULL; + + kfree(ch->LINE_privdata); + ch->LINE_privdata = NULL; + + MOD_DEC_USE_COUNT; + return 0; +} + +static int syncppp_init(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct ppp_device *pppdev = (struct ppp_device*)dev; + + ch->LINE_privdata = kmalloc(sizeof(struct syncppp_data), GFP_KERNEL); + + sppp_attach(pppdev); + + if(ch->protocol == &hdlc_protocol) { + pppdev->sppp.pp_flags |= PP_CISCO; + dev->type = ARPHRD_HDLC; + } else { + pppdev->sppp.pp_flags &= ~PP_CISCO; + dev->type = ARPHRD_PPP; + } + + ch->LINE_rx = sppp_input; + ch->LINE_tx = syncppp_tx; + ch->LINE_status = syncppp_status; + ch->LINE_open = syncppp_open; + ch->LINE_close = syncppp_close; + ch->LINE_xmit = syncppp_xmit; + ch->LINE_header = NULL; + ch->LINE_statistics = syncppp_statistics; + + + MOD_INC_USE_COUNT; + return 0; +} + +static struct comx_protocol syncppp_protocol = { + "ppp", + VERSION, + ARPHRD_PPP, + syncppp_init, + syncppp_exit, + NULL +}; + +static struct comx_protocol hdlc_protocol = { + "hdlc", + VERSION, + ARPHRD_PPP, + syncppp_init, + syncppp_exit, + NULL +}; + + +#ifdef MODULE +#define comx_proto_ppp_init init_module +#endif + +__initfunc(int comx_proto_ppp_init(void)) +{ + int ret; + + if(0!=(ret=comx_register_protocol(&hdlc_protocol))) { + return ret; + } + return comx_register_protocol(&syncppp_protocol); +} + +#ifdef MODULE +void cleanup_module(void) +{ + comx_unregister_protocol(syncppp_protocol.name); + comx_unregister_protocol(hdlc_protocol.name); +} +#endif /* MODULE */ + diff -u --recursive --new-file v2.2.13/linux/drivers/net/comx.c linux/drivers/net/comx.c --- v2.2.13/linux/drivers/net/comx.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/comx.c Tue Jan 4 10:12:17 2000 @@ -0,0 +1,1234 @@ +/* + * Device driver framework for the COMX line of synchronous serial boards + * + * for Linux kernel 2.2.X + * + * Original authors: Arpad Bakay , + * Peter Bajan , + * Previous maintainer: Tivadar Szemethy + * Current maintainer: Gergely Madarasz + * + * Copyright (C) 1995-1999 ITConsult-Pro Co. + * + * 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. + * + * Version 0.80 (99/06/11): + * - clean up source code (playing a bit of indent) + * - port back to kernel, add support for non-module versions + * - add support for board resets when channel protocol is down + * - reset the device structure after protocol exit + * the syncppp driver needs it + * - add support for /proc/comx/protocols and + * /proc/comx/boardtypes + * + * Version 0.81 (99/06/21): + * - comment out the board reset support code, the locomx + * driver seems not buggy now + * - printk() levels fixed + * + * Version 0.82 (99/07/08): + * - Handle stats correctly if the lowlevel driver is + * is not a comx one (locomx - z85230) + * + * Version 0.83 (99/07/15): + * - reset line_status when interface is down + * + */ + +#define VERSION "0.82" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_KMOD +#include +#endif + +#ifndef CONFIG_PROC_FS +#error For now, COMX really needs the /proc filesystem +#endif + +#include "comx.h" +#include "syncppp.h" + +MODULE_AUTHOR("Gergely Madarasz "); +MODULE_DESCRIPTION("Common code for the COMX synchronous serial adapters"); + +extern int comx_hw_comx_init(void); +extern int comx_hw_locomx_init(void); +extern int comx_hw_mixcom_init(void); +extern int comx_proto_hdlc_init(void); +extern int comx_proto_ppp_init(void); +extern int comx_proto_syncppp_init(void); +extern int comx_proto_lapb_init(void); +extern int comx_proto_fr_init(void); + +static struct comx_hardware *comx_channels = NULL; +static struct comx_protocol *comx_lines = NULL; + +struct inode_operations comx_normal_inode_ops; +static struct inode_operations comx_root_inode_ops; // for mkdir +static struct inode_operations comx_debug_inode_ops; // mas a file_ops +static struct file_operations comx_normal_file_ops; // with open/relase +static struct file_operations comx_debug_file_ops; // with lseek+read + +static void comx_delete_dentry(struct dentry *dentry); +static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode, + int size, struct proc_dir_entry *dir); + +static void comx_fill_inode(struct inode *inode, int fill); + +static struct dentry_operations comx_dentry_operations = { + NULL, /* revalidate */ + NULL, /* d_hash */ + NULL, /* d_compare */ + &comx_delete_dentry /* d_delete */ +}; + + +struct proc_dir_entry comx_root_dir = { + 0, 4, "comx", + S_IFDIR | S_IWUSR | S_IRUGO | S_IXUGO, 2, 0, 0, + 0, &comx_root_inode_ops, + NULL, comx_fill_inode, + NULL, &proc_root, NULL +}; + +struct comx_debugflags_struct comx_debugflags[] = { + { "comx_rx", DEBUG_COMX_RX }, + { "comx_tx", DEBUG_COMX_TX }, + { "hw_tx", DEBUG_HW_TX }, + { "hw_rx", DEBUG_HW_RX }, + { "hdlc_keepalive", DEBUG_HDLC_KEEPALIVE }, + { "comxppp", DEBUG_COMX_PPP }, + { "comxlapb", DEBUG_COMX_LAPB }, + { "dlci", DEBUG_COMX_DLCI }, + { NULL, 0 } +}; + +static void comx_fill_inode(struct inode *inode, int fill) +{ + if (fill) + MOD_INC_USE_COUNT; + else + MOD_DEC_USE_COUNT; +} + + +int comx_debug(struct device *dev, char *fmt, ...) +{ + struct comx_channel *ch = dev->priv; + char *page,*str; + va_list args; + int len; + + if (!ch->debug_area) return 0; + + if (!(page = (char *)__get_free_page(GFP_ATOMIC))) return -ENOMEM; + + va_start(args, fmt); + len = vsprintf(str = page, fmt, args); + va_end(args); + + if (len >= PAGE_SIZE) { + printk(KERN_ERR "comx_debug: PANIC! len = %d !!!\n", len); + free_page((unsigned long)page); + return -EINVAL; + } + + while (len) { + int to_copy; + int free = (ch->debug_start - ch->debug_end + ch->debug_size) + % ch->debug_size; + + to_copy = min( free ? free : ch->debug_size, + min (ch->debug_size - ch->debug_end, len) ); + memcpy(ch->debug_area + ch->debug_end, str, to_copy); + str += to_copy; + len -= to_copy; + ch->debug_end = (ch->debug_end + to_copy) % ch->debug_size; + if (ch->debug_start == ch->debug_end) // Full ? push start away + ch->debug_start = (ch->debug_start + len + 1) % + ch->debug_size; + ch->debug_file->size = (ch->debug_end - ch->debug_start + + ch->debug_size) % ch->debug_size; + } + + free_page((unsigned long)page); + return 0; +} + +int comx_debug_skb(struct device *dev, struct sk_buff *skb, char *msg) +{ + struct comx_channel *ch = dev->priv; + + if (!ch->debug_area) return 0; + if (!skb) comx_debug(dev, "%s: %s NULL skb\n\n", dev->name, msg); + if (!skb->len) comx_debug(dev, "%s: %s empty skb\n\n", dev->name, msg); + + return comx_debug_bytes(dev, skb->data, skb->len, msg); +} + +int comx_debug_bytes(struct device *dev, unsigned char *bytes, int len, + char *msg) +{ + int pos = 0; + struct comx_channel *ch = dev->priv; + + if (!ch->debug_area) return 0; + + comx_debug(dev, "%s: %s len %d\n", dev->name, msg, len); + + while (pos != len) { + char line[80]; + int i = 0; + + memset(line, 0, 80); + sprintf(line,"%04d ", pos); + do { + sprintf(line + 5 + (pos % 16) * 3, "%02x", bytes[pos]); + sprintf(line + 60 + (pos % 16), "%c", + isprint(bytes[pos]) ? bytes[pos] : '.'); + pos++; + } while (pos != len && pos % 16); + + while ( i++ != 78 ) if (line[i] == 0) line[i] = ' '; + line[77] = '\n'; + line[78] = 0; + + comx_debug(dev, "%s", line); + } + comx_debug(dev, "\n"); + return 0; +} + +static void comx_loadavg_timerfun(unsigned long d) +{ + struct device *dev = (struct device *)d; + struct comx_channel *ch = dev->priv; + + ch->avg_bytes[ch->loadavg_counter] = ch->current_stats->rx_bytes; + ch->avg_bytes[ch->loadavg_counter + ch->loadavg_size] = + ch->current_stats->tx_bytes; + + ch->loadavg_counter = (ch->loadavg_counter + 1) % ch->loadavg_size; + + mod_timer(&ch->loadavg_timer,jiffies + HZ * ch->loadavg[0]); +} + +#if 0 +static void comx_reset_timerfun(unsigned long d) +{ + struct device *dev = (struct device *)d; + struct comx_channel *ch = dev->priv; + + if(!(ch->line_status & (PROTO_LOOP | PROTO_UP))) { + if(test_and_set_bit(0,&ch->reset_pending) && ch->HW_reset) { + ch->HW_reset(dev); + } + } + + mod_timer(&ch->reset_timer, jiffies + HZ * ch->reset_timeout); +} +#endif + +static int comx_open(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct proc_dir_entry *comxdir = ch->procdir->subdir; + int ret=0; + + if (!ch->protocol || !ch->hardware) return -ENODEV; + + if ((ret = ch->HW_open(dev))) return ret; + if ((ret = ch->LINE_open(dev))) { + ch->HW_close(dev); + return ret; + }; + + for (; comxdir ; comxdir = comxdir->next) { + if (strcmp(comxdir->name, FILENAME_HARDWARE) == 0 || + strcmp(comxdir->name, FILENAME_PROTOCOL) == 0) + comxdir->mode = S_IFREG | 0444; + } + +#if 0 + ch->reset_pending = 1; + ch->reset_timeout = 30; + ch->reset_timer.function = comx_reset_timerfun; + ch->reset_timer.data = (unsigned long)dev; + ch->reset_timer.expires = jiffies + HZ * ch->reset_timeout; + add_timer(&ch->reset_timer); +#endif + + return 0; +} + +static int comx_close(struct device *dev) +{ + struct comx_channel *ch = dev->priv; + struct proc_dir_entry *comxdir = ch->procdir->subdir; + int ret = -ENODEV; + + if (test_and_clear_bit(0, &ch->lineup_pending)) { + del_timer(&ch->lineup_timer); + } + +#if 0 + del_timer(&ch->reset_timer); +#endif + + if (ch->init_status & LINE_OPEN && ch->protocol && ch->LINE_close) { + ret = ch->LINE_close(dev); + } + + if (ret) return ret; + + if (ch->init_status & HW_OPEN && ch->hardware && ch->HW_close) { + ret = ch->HW_close(dev); + } + + ch->line_status=0; + + for (; comxdir ; comxdir = comxdir->next) { + if (strcmp(comxdir->name, FILENAME_HARDWARE) == 0 || + strcmp(comxdir->name, FILENAME_PROTOCOL) == 0) + comxdir->mode = S_IFREG | 0644; + } + + return ret; +} + +void comx_status(struct device *dev, int status) +{ + struct comx_channel *ch = dev->priv; + +#if 0 + if(status & (PROTO_UP | PROTO_LOOP)) { + clear_bit(0,&ch->reset_pending); + } +#endif + + if (dev->flags & IFF_UP) { + printk(KERN_NOTICE "Interface %s: modem status %s, line protocol %s\n", + dev->name, status & LINE_UP ? "UP" : "DOWN", + status & PROTO_LOOP ? "LOOP" : status & PROTO_UP ? + "UP" : "DOWN"); + } + + ch->line_status = status; +} + +static int comx_xmit(struct sk_buff *skb, struct device *dev) +{ + struct comx_channel *ch = dev->priv; + int rc; + + if (skb->len > dev->mtu + dev->hard_header_len) { + printk(KERN_ERR "comx_xmit: %s: skb->len %d > dev->mtu %d\n", dev->name, + (int)skb->len, dev->mtu); + } + + if (ch->debug_flags & DEBUG_COMX_TX) { + comx_debug_skb(dev, skb, "comx_xmit skb"); + } + + rc=ch->LINE_xmit(skb, dev); +// if (!rc) dev_kfree_skb(skb); + + return rc; +} + +static int comx_header(struct sk_buff *skb, struct device *dev, + unsigned short type, void *daddr, void *saddr, unsigned len) +{ + struct comx_channel *ch = dev->priv; + + if (ch->LINE_header) { + return (ch->LINE_header(skb, dev, type, daddr, saddr, len)); + } else { + return 0; + } +} + +static int comx_rebuild_header(struct sk_buff *skb) +{ + struct device *dev = skb->dev; + struct comx_channel *ch = dev->priv; + + if (ch->LINE_rebuild_header) { + return(ch->LINE_rebuild_header(skb)); + } else { + return 0; + } +} + +int comx_rx(struct device *dev, struct sk_buff *skb) +{ + struct comx_channel *ch = dev->priv; + + if (ch->debug_flags & DEBUG_COMX_RX) { + comx_debug_skb(dev, skb, "comx_rx skb"); + } + if (skb) { + netif_rx(skb); + } + return 0; +} + +static struct net_device_stats *comx_stats(struct device *dev) +{ + struct comx_channel *ch = (struct comx_channel *)dev->priv; + + return ch->current_stats; +} + +void comx_lineup_func(unsigned long d) +{ + struct device *dev = (struct device *)d; + struct comx_channel *ch = dev->priv; + + del_timer(&ch->lineup_timer); + clear_bit(0, &ch->lineup_pending); + + if (ch->LINE_status) { + ch->LINE_status(dev, ch->line_status |= LINE_UP); + } +} + +#define LOADAVG(avg, off) (int) \ + ((ch->avg_bytes[(ch->loadavg_counter - 1 + ch->loadavg_size * 2) \ + % ch->loadavg_size + off] - ch->avg_bytes[(ch->loadavg_counter - 1 \ + - ch->loadavg[avg] / ch->loadavg[0] + ch->loadavg_size * 2) \ + % ch->loadavg_size + off]) / ch->loadavg[avg] * 8) + +static int comx_statistics(struct device *dev, char *page) +{ + struct comx_channel *ch = dev->priv; + int len = 0; + int tmp; + int i = 0; + char tmpstr[20]; + int tmpstrlen = 0; + + len += sprintf(page + len, "Interface administrative status is %s, " + "modem status is %s, protocol is %s\n", + dev->flags & IFF_UP ? "UP" : "DOWN", + ch->line_status & LINE_UP ? "UP" : "DOWN", + ch->line_status & PROTO_LOOP ? "LOOP" : + ch->line_status & PROTO_UP ? "UP" : "DOWN"); + len += sprintf(page + len, "Modem status changes: %lu, Transmitter status " + "is %s, tbusy: %d\n", ch->current_stats->tx_carrier_errors, ch->HW_txe ? + ch->HW_txe(dev) ? "IDLE" : "BUSY" : "NOT READY", (int)dev->tbusy); + len += sprintf(page + len, "Interface load (input): %d / %d / %d bits/s (", + LOADAVG(0,0), LOADAVG(1, 0), LOADAVG(2, 0)); + tmpstr[0] = 0; + for (i=0; i != 3; i++) { + char tf; + + tf = ch->loadavg[i] % 60 == 0 && + ch->loadavg[i] / 60 > 0 ? 'm' : 's'; + tmpstrlen += sprintf(tmpstr + tmpstrlen, "%d%c%s", + ch->loadavg[i] / (tf == 'm' ? 60 : 1), tf, + i == 2 ? ")\n" : "/"); + } + len += sprintf(page + len, + "%s (output): %d / %d / %d bits/s (%s", tmpstr, + LOADAVG(0,ch->loadavg_size), LOADAVG(1, ch->loadavg_size), + LOADAVG(2, ch->loadavg_size), tmpstr); + + len += sprintf(page + len, "Debug flags: "); + tmp = len; i = 0; + while (comx_debugflags[i].name) { + if (ch->debug_flags & comx_debugflags[i].value) + len += sprintf(page + len, "%s ", + comx_debugflags[i].name); + i++; + } + len += sprintf(page + len, "%s\n", tmp == len ? "none" : ""); + + len += sprintf(page + len, "RX errors: len: %lu, overrun: %lu, crc: %lu, " + "aborts: %lu\n buffer overrun: %lu, pbuffer overrun: %lu\n" + "TX errors: underrun: %lu\n", + ch->current_stats->rx_length_errors, ch->current_stats->rx_over_errors, + ch->current_stats->rx_crc_errors, ch->current_stats->rx_frame_errors, + ch->current_stats->rx_missed_errors, ch->current_stats->rx_fifo_errors, + ch->current_stats->tx_fifo_errors); + + if (ch->LINE_statistics && (ch->init_status & LINE_OPEN)) { + len += ch->LINE_statistics(dev, page + len); + } else { + len += sprintf(page+len, "Line status: driver not initialized\n"); + } + if (ch->HW_statistics && (ch->init_status & HW_OPEN)) { + len += ch->HW_statistics(dev, page + len); + } else { + len += sprintf(page+len, "Board status: driver not initialized\n"); + } + + return len; +} + +static int comx_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +{ + struct comx_channel *ch = dev->priv; + + if (ch->LINE_ioctl) { + return(ch->LINE_ioctl(dev, ifr, cmd)); + } + return -EINVAL; +} + +static void comx_reset_dev(struct device *dev) +{ + dev->open = comx_open; + dev->stop = comx_close; + dev->hard_start_xmit = comx_xmit; + dev->hard_header = comx_header; + dev->rebuild_header = comx_rebuild_header; + dev->get_stats = comx_stats; + dev->do_ioctl = comx_ioctl; + dev->change_mtu = NULL; + dev->tx_queue_len = 20; + dev->flags = IFF_NOARP; +} + +static int comx_init_dev(struct device *dev) +{ + struct comx_channel *ch; + + if ((ch = kmalloc(sizeof(struct comx_channel), GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + memset(ch, 0, sizeof(struct comx_channel)); + + ch->loadavg[0] = 5; + ch->loadavg[1] = 300; + ch->loadavg[2] = 900; + ch->loadavg_size = ch->loadavg[2] / ch->loadavg[0] + 1; + if ((ch->avg_bytes = kmalloc(ch->loadavg_size * + sizeof(unsigned long) * 2, GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + + memset(ch->avg_bytes, 0, ch->loadavg_size * sizeof(unsigned long) * 2); + ch->loadavg_counter = 0; + ch->loadavg_timer.function = comx_loadavg_timerfun; + ch->loadavg_timer.data = (unsigned long)dev; + ch->loadavg_timer.expires = jiffies + HZ * ch->loadavg[0]; + add_timer(&ch->loadavg_timer); + + dev->priv = (void *)ch; + ch->dev = dev; + ch->line_status &= ~LINE_UP; + + ch->current_stats = &ch->stats; + + comx_reset_dev(dev); + return 0; +} + +static int comx_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct proc_dir_entry *file = (struct proc_dir_entry *)data; + struct device *dev = file->parent->data; + struct comx_channel *ch=(struct comx_channel *)dev->priv; + int len = 0; + + if (strcmp(file->name, FILENAME_STATUS) == 0) { + len = comx_statistics(dev, page); + } else if (strcmp(file->name, FILENAME_HARDWARE) == 0) { + len = sprintf(page, "%s\n", ch->hardware ? + ch->hardware->name : HWNAME_NONE); + } else if (strcmp(file->name, FILENAME_PROTOCOL) == 0) { + len = sprintf(page, "%s\n", ch->protocol ? + ch->protocol->name : PROTONAME_NONE); + } else if (strcmp(file->name, FILENAME_LINEUPDELAY) == 0) { + len = sprintf(page, "%01d\n", ch->lineup_delay); + } + + if (off >= len) { + *eof = 1; + return 0; + } + + *start = page + off; + if (count >= len - off) { + *eof = 1; + } + return( min(count, len - off) ); +} + + +static int comx_root_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct proc_dir_entry *file = (struct proc_dir_entry *)data; + struct comx_hardware *hw; + struct comx_protocol *line; + + int len = 0; + + if (strcmp(file->name, FILENAME_HARDWARELIST) == 0) { + for(hw=comx_channels;hw;hw=hw->next) + len+=sprintf(page+len, "%s\n", hw->name); + } else if (strcmp(file->name, FILENAME_PROTOCOLLIST) == 0) { + for(line=comx_lines;line;line=line->next) + len+=sprintf(page+len, "%s\n", line->name); + } + + if (off >= len) { + *eof = 1; + return 0; + } + + *start = page + off; + if (count >= len - off) { + *eof = 1; + } + return( min(count, len - off) ); +} + + + +static int comx_write_proc(struct file *file, const char *buffer, u_long count, + void *data) +{ + struct proc_dir_entry *entry = (struct proc_dir_entry *)data; + struct device *dev = (struct device *)entry->parent->data; + struct comx_channel *ch=(struct comx_channel *)dev->priv; + char *page; + struct comx_hardware *hw = comx_channels; + struct comx_protocol *line = comx_lines; + char str[30]; + int ret=0; + + if (file->f_dentry->d_inode->i_ino != entry->low_ino) { + printk(KERN_ERR "comx_write_proc: file <-> data internal error\n"); + return -EIO; + } + + if (count > PAGE_SIZE) { + printk(KERN_ERR "count is %lu > %d!!!\n", count, (int)PAGE_SIZE); + return -ENOSPC; + } + + if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM; + + copy_from_user(page, buffer, count); + + if (*(page + count - 1) == '\n') *(page + count - 1) = 0; + + if (strcmp(entry->name, FILENAME_DEBUG) == 0) { + int i; + int ret = 0; + + if ((i = simple_strtoul(page, NULL, 10)) != 0) { + unsigned long flags; + + save_flags(flags); cli(); + if (ch->debug_area) kfree(ch->debug_area); + if ((ch->debug_area = kmalloc(ch->debug_size = i, + GFP_KERNEL)) == NULL) { + ret = -ENOMEM; + } + ch->debug_start = ch->debug_end = 0; + restore_flags(flags); + free_page((unsigned long)page); + return count; + } + + if (*page != '+' && *page != '-') { + free_page((unsigned long)page); + return -EINVAL; + } + while (comx_debugflags[i].value && + strncmp(comx_debugflags[i].name, page + 1, + strlen(comx_debugflags[i].name))) { + i++; + } + + if (comx_debugflags[i].value == 0) { + printk(KERN_ERR "Invalid debug option\n"); + free_page((unsigned long)page); + return -EINVAL; + } + if (*page == '+') { + ch->debug_flags |= comx_debugflags[i].value; + } else { + ch->debug_flags &= ~comx_debugflags[i].value; + } + } else if (strcmp(entry->name, FILENAME_HARDWARE) == 0) { + if(strlen(page)>10) { + free_page((unsigned long)page); + return -EINVAL; + } + while (hw) { + if (strcmp(hw->name, page) == 0) { + break; + } else { + hw = hw->next; + } + } +#ifdef CONFIG_KMOD + if(!hw && comx_strcasecmp(HWNAME_NONE,page) != 0){ + sprintf(str,"comx-hw-%s",page); + request_module(str); + } + hw=comx_channels; + while (hw) { + if (comx_strcasecmp(hw->name, page) == 0) { + break; + } else { + hw = hw->next; + } + } +#endif + + if (comx_strcasecmp(HWNAME_NONE, page) != 0 && !hw) { + free_page((unsigned long)page); + return -ENODEV; + } + if (ch->init_status & HW_OPEN) { + free_page((unsigned long)page); + return -EBUSY; + } + if (ch->hardware && ch->hardware->hw_exit && + (ret=ch->hardware->hw_exit(dev))) { + free_page((unsigned long)page); + return ret; + } + ch->hardware = hw; + entry->size = strlen(page) + 1; + if (hw && hw->hw_init) hw->hw_init(dev); + } else if (strcmp(entry->name, FILENAME_PROTOCOL) == 0) { + if(strlen(page)>10) { + free_page((unsigned long)page); + return -EINVAL; + } + while (line) { + if (comx_strcasecmp(line->name, page) == 0) { + break; + } else { + line = line->next; + } + } +#ifdef CONFIG_KMOD + if(!line && comx_strcasecmp(PROTONAME_NONE, page) != 0) { + sprintf(str,"comx-proto-%s",page); + request_module(str); + } + line=comx_lines; + while (line) { + if (comx_strcasecmp(line->name, page) == 0) { + break; + } else { + line = line->next; + } + } +#endif + + if (comx_strcasecmp(PROTONAME_NONE, page) != 0 && !line) { + free_page((unsigned long)page); + return -ENODEV; + } + + if (ch->init_status & LINE_OPEN) { + free_page((unsigned long)page); + return -EBUSY; + } + + if (ch->protocol && ch->protocol->line_exit && + (ret=ch->protocol->line_exit(dev))) { + free_page((unsigned long)page); + return ret; + } + ch->protocol = line; + entry->size = strlen(page) + 1; + comx_reset_dev(dev); + if (line && line->line_init) line->line_init(dev); + } else if (strcmp(entry->name, FILENAME_LINEUPDELAY) == 0) { + int i; + + if ((i = simple_strtoul(page, NULL, 10)) != 0) { + if (i >=0 && i < 10) { + ch->lineup_delay = i; + } else { + printk(KERN_ERR "comx: invalid lineup_delay value\n"); + } + } + } + + free_page((unsigned long)page); + return count; +} + +static loff_t comx_debug_lseek(struct file *file, loff_t offset, int orig) +{ + switch(orig) { + case 0: + file->f_pos = max(0, min(offset, + file->f_dentry->d_inode->i_size)); + return(file->f_pos); + case 1: + file->f_pos = max(0, min(offset + file->f_pos, + file->f_dentry->d_inode->i_size)); + return(file->f_pos); + case 2: + file->f_pos = max(0, + min(offset + file->f_dentry->d_inode->i_size, + file->f_dentry->d_inode->i_size)); + return(file->f_pos); + } + return(file->f_pos); +} + +static int comx_file_open(struct inode *inode, struct file *file) +{ + + if((file->f_mode & FMODE_WRITE) && !(inode->i_mode & 0200)) { + return -EACCES; + } + + MOD_INC_USE_COUNT; + return 0; +} + +static int comx_file_release(struct inode *inode, struct file *file) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +static ssize_t comx_debug_read(struct file *file, char *buffer, size_t count, + loff_t *ppos) +{ + struct proc_dir_entry *de = file->f_dentry->d_inode->u.generic_ip; + struct device *dev = de->parent->data; + struct comx_channel *ch = dev->priv; + loff_t copied = 0; + unsigned long flags; + + save_flags(flags); cli(); // We may run into trouble when debug_area is filled + // from irq inside read. no problem if the buffer is + // large enough + + while (count > 0 && ch->debug_start != ch->debug_end) { + int len; + + len = min( (ch->debug_end - ch->debug_start + ch->debug_size) + %ch->debug_size, min (ch->debug_size - + ch->debug_start, count)); + + if (len) copy_to_user(buffer + copied, + ch->debug_area + ch->debug_start, len); + ch->debug_start = (ch->debug_start + len) % ch->debug_size; + + de->size -= len; + count -= len; + copied += len; + } + + restore_flags(flags); + return copied; +} + +static int comx_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + struct proc_dir_entry *new_dir, *debug_file; + struct device *dev; + struct comx_channel *ch; + + if (dir->i_ino != comx_root_dir.low_ino) return -ENOTDIR; + + if ((new_dir = create_proc_entry(dentry->d_name.name, mode | S_IFDIR, + &comx_root_dir)) == NULL) { + return -EIO; + } + + new_dir->ops = &proc_dir_inode_operations; // ez egy normalis /proc konyvtar + new_dir->nlink = 2; + new_dir->data = NULL; // ide jon majd a struct dev + + /* Ezek kellenek */ + if (!create_comx_proc_entry(FILENAME_HARDWARE, 0644, + strlen(HWNAME_NONE) + 1, new_dir)) { + return -ENOMEM; + } + if (!create_comx_proc_entry(FILENAME_PROTOCOL, 0644, + strlen(PROTONAME_NONE) + 1, new_dir)) { + return -ENOMEM; + } + if (!create_comx_proc_entry(FILENAME_STATUS, 0444, 0, new_dir)) { + return -ENOMEM; + } + if (!create_comx_proc_entry(FILENAME_LINEUPDELAY, 0644, 2, new_dir)) { + return -ENOMEM; + } + + if ((debug_file = create_proc_entry(FILENAME_DEBUG, + S_IFREG | 0644, new_dir)) == NULL) { + return -ENOMEM; + } + debug_file->ops = &comx_debug_inode_ops; + debug_file->data = (void *)debug_file; + debug_file->read_proc = NULL; // see below + debug_file->write_proc = &comx_write_proc; + debug_file->nlink = 1; + + /* struct ppp_device is a bit larger then struct device and the + syncppp driver needs it */ + if ((dev = kmalloc(sizeof(struct ppp_device), GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + memset(dev, 0, sizeof(struct ppp_device)); + dev->name = (char *)new_dir->name; + dev->init = comx_init_dev; + + if (register_netdevice(dev)) { + return -EIO; + } + ch=dev->priv; + ch->debug_file = debug_file; + ch->procdir = new_dir; + new_dir->data = dev; + + ch->debug_start = ch->debug_end = 0; + if ((ch->debug_area = kmalloc(ch->debug_size = DEFAULT_DEBUG_SIZE, + GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + + ch->lineup_delay = DEFAULT_LINEUP_DELAY; + + MOD_INC_USE_COUNT; + return 0; +} + +static int comx_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct proc_dir_entry *entry = dentry->d_inode->u.generic_ip; + struct device *dev = entry->data; + struct comx_channel *ch = dev->priv; + int ret; + + /* Egyelore miert ne ? */ + if (dir->i_ino != comx_root_dir.low_ino) return -ENOTDIR; + + if (dev->flags & IFF_UP) { + printk(KERN_ERR "%s: down interface before removing it\n", dev->name); + return -EBUSY; + } + + if (ch->protocol && ch->protocol->line_exit && + (ret=ch->protocol->line_exit(dev))) { + return ret; + } + if (ch->hardware && ch->hardware->hw_exit && + (ret=ch->hardware->hw_exit(dev))) { + if(ch->protocol && ch->protocol->line_init) { + ch->protocol->line_init(dev); + } + return ret; + } + ch->protocol = NULL; + ch->hardware = NULL; + + del_timer(&ch->loadavg_timer); + kfree(ch->avg_bytes); + + unregister_netdev(dev); + if (ch->debug_area) { + kfree(ch->debug_area); + } + if (dev->priv) { + kfree(dev->priv); + } + kfree(dev); + + remove_proc_entry(FILENAME_DEBUG, entry); + remove_proc_entry(FILENAME_LINEUPDELAY, entry); + remove_proc_entry(FILENAME_STATUS, entry); + remove_proc_entry(FILENAME_HARDWARE, entry); + remove_proc_entry(FILENAME_PROTOCOL, entry); + remove_proc_entry(dentry->d_name.name, &comx_root_dir); +// proc_unregister(&comx_root_dir, dentry->d_inode->i_ino); + + MOD_DEC_USE_COUNT; + return 0; +} + +static struct dentry *comx_lookup(struct inode *dir, struct dentry *dentry) +{ + struct proc_dir_entry *de; + struct inode *inode = NULL; + + if (!dir || !S_ISDIR(dir->i_mode)) { + return ERR_PTR(-ENOTDIR); + } + + if ((de = (struct proc_dir_entry *) dir->u.generic_ip) != NULL) { + for (de = de->subdir ; de ; de = de->next) { + if ((de && de->low_ino) && + (de->namelen == dentry->d_name.len) && + (memcmp(dentry->d_name.name, de->name, + de->namelen) == 0)) { + if ((inode = proc_get_inode(dir->i_sb, + de->low_ino, de)) == NULL) { + printk(KERN_ERR "COMX: lookup error\n"); + return ERR_PTR(-EINVAL); + } + break; + } + } + } + dentry->d_op = &comx_dentry_operations; + d_add(dentry, inode); + return NULL; +} + +int comx_strcasecmp(const char *cs, const char *ct) +{ + register signed char __res; + + while (1) { + if ((__res = toupper(*cs) - toupper(*ct++)) != 0 || !*cs++) { + break; + } + } + return __res; +} + +static void comx_delete_dentry(struct dentry *dentry) +{ + d_drop(dentry); +} + +static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode, + int size, struct proc_dir_entry *dir) +{ + struct proc_dir_entry *new_file; + + if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) { + new_file->ops = &comx_normal_inode_ops; + new_file->data = (void *)new_file; + new_file->read_proc = &comx_read_proc; + new_file->write_proc = &comx_write_proc; + new_file->size = size; + new_file->nlink = 1; + } + return(new_file); +} + +int comx_register_hardware(struct comx_hardware *comx_hw) +{ + struct comx_hardware *hw = comx_channels; + + if (!hw) { + comx_channels = comx_hw; + } else { + while (hw->next != NULL && strcmp(comx_hw->name, hw->name) != 0) { + hw = hw->next; + } + if (strcmp(comx_hw->name, hw->name) == 0) { + return -1; + } + hw->next = comx_hw; + } + + printk(KERN_INFO "COMX: driver for hardware type %s, version %s\n", comx_hw->name, comx_hw->version); + return 0; +} + +int comx_unregister_hardware(char *name) +{ + struct comx_hardware *hw = comx_channels; + + if (!hw) { + return -1; + } + + if (strcmp(hw->name, name) == 0) { + comx_channels = comx_channels->next; + return 0; + } + + while (hw->next != NULL && strcmp(hw->next->name,name) != 0) { + hw = hw->next; + } + + if (hw->next != NULL && strcmp(hw->next->name, name) == 0) { + hw->next = hw->next->next; + return 0; + } + return -1; +} + +int comx_register_protocol(struct comx_protocol *comx_line) +{ + struct comx_protocol *pr = comx_lines; + + if (!pr) { + comx_lines = comx_line; + } else { + while (pr->next != NULL && strcmp(comx_line->name, pr->name) !=0) { + pr = pr->next; + } + if (strcmp(comx_line->name, pr->name) == 0) { + return -1; + } + pr->next = comx_line; + } + + printk(KERN_INFO "COMX: driver for protocol type %s, version %s\n", comx_line->name, comx_line->version); + return 0; +} + +int comx_unregister_protocol(char *name) +{ + struct comx_protocol *pr = comx_lines; + + if (!pr) { + return -1; + } + + if (strcmp(pr->name, name) == 0) { + comx_lines = comx_lines->next; + return 0; + } + + while (pr->next != NULL && strcmp(pr->next->name,name) != 0) { + pr = pr->next; + } + + if (pr->next != NULL && strcmp(pr->next->name, name) == 0) { + pr->next = pr->next->next; + return 0; + } + return -1; +} + +#ifdef MODULE +#define comx_init init_module +#endif + +__initfunc(int comx_init(void)) +{ + struct proc_dir_entry *new_file; + + memcpy(&comx_root_inode_ops, &proc_dir_inode_operations, + sizeof(struct inode_operations)); + comx_root_inode_ops.lookup = &comx_lookup; + comx_root_inode_ops.mkdir = &comx_mkdir; + comx_root_inode_ops.rmdir = &comx_rmdir; + + memcpy(&comx_normal_inode_ops, &proc_net_inode_operations, + sizeof(struct inode_operations)); + comx_normal_inode_ops.default_file_ops = &comx_normal_file_ops; + comx_normal_inode_ops.lookup = &comx_lookup; + + memcpy(&comx_debug_inode_ops, &comx_normal_inode_ops, + sizeof(struct inode_operations)); + comx_debug_inode_ops.default_file_ops = &comx_debug_file_ops; + + memcpy(&comx_normal_file_ops, proc_net_inode_operations.default_file_ops, + sizeof(struct file_operations)); + comx_normal_file_ops.open = &comx_file_open; + comx_normal_file_ops.release = &comx_file_release; + + memcpy(&comx_debug_file_ops, &comx_normal_file_ops, + sizeof(struct file_operations)); + comx_debug_file_ops.llseek = &comx_debug_lseek; + comx_debug_file_ops.read = &comx_debug_read; + + if (proc_register(&proc_root, &comx_root_dir) < 0) return -ENOMEM; + + + if ((new_file = create_proc_entry(FILENAME_HARDWARELIST, + S_IFREG | 0444, &comx_root_dir)) == NULL) { + return -ENOMEM; + } + + new_file->ops = &comx_normal_inode_ops; + new_file->data = new_file; + new_file->read_proc = &comx_root_read_proc; + new_file->write_proc = NULL; + new_file->nlink = 1; + + if ((new_file = create_proc_entry(FILENAME_PROTOCOLLIST, + S_IFREG | 0444, &comx_root_dir)) == NULL) { + return -ENOMEM; + } + + new_file->ops = &comx_normal_inode_ops; + new_file->data = new_file; + new_file->read_proc = &comx_root_read_proc; + new_file->write_proc = NULL; + new_file->nlink = 1; + + + printk(KERN_INFO "COMX: driver version %s (C) 1995-1999 ITConsult-Pro Co. \n", + VERSION); + +#ifndef MODULE +#ifdef CONFIG_COMX_HW_COMX + comx_hw_comx_init(); +#endif +#ifdef CONFIG_COMX_HW_LOCOMX + comx_hw_locomx_init(); +#endif +#ifdef CONFIG_COMX_HW_MIXCOM + comx_hw_mixcom_init(); +#endif +#ifdef CONFIG_COMX_PROTO_HDLC + comx_proto_hdlc_init(); +#endif +#ifdef CONFIG_COMX_PROTO_PPP + comx_proto_ppp_init(); +#endif +#ifdef CONFIG_COMX_PROTO_LAPB + comx_proto_lapb_init(); +#endif +#ifdef CONFIG_COMX_PROTO_FR + comx_proto_fr_init(); +#endif +#endif + + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + remove_proc_entry(FILENAME_HARDWARELIST, &comx_root_dir); + remove_proc_entry(FILENAME_PROTOCOLLIST, &comx_root_dir); + proc_unregister(&proc_root, comx_root_dir.low_ino); +} +#endif + +EXPORT_SYMBOL(comx_register_hardware); +EXPORT_SYMBOL(comx_unregister_hardware); +EXPORT_SYMBOL(comx_register_protocol); +EXPORT_SYMBOL(comx_unregister_protocol); +EXPORT_SYMBOL(comx_debug_skb); +EXPORT_SYMBOL(comx_debug_bytes); +EXPORT_SYMBOL(comx_debug); +EXPORT_SYMBOL(comx_lineup_func); +EXPORT_SYMBOL(comx_status); +EXPORT_SYMBOL(comx_rx); +EXPORT_SYMBOL(comx_strcasecmp); +EXPORT_SYMBOL(comx_normal_inode_ops); +EXPORT_SYMBOL(comx_root_dir); diff -u --recursive --new-file v2.2.13/linux/drivers/net/comx.h linux/drivers/net/comx.h --- v2.2.13/linux/drivers/net/comx.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/comx.h Tue Jan 4 10:12:17 2000 @@ -0,0 +1,238 @@ +/* + * General definitions for the COMX driver + * + * Original authors: Arpad Bakay , + * Peter Bajan , + * Previous maintainer: Tivadar Szemethy + * Currently maintained by: Gergely Madarasz + * + * Copyright (C) 1995-1999 ITConsult-Pro Co. + * + * 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. + * + * + * net_device_stats: + * rx_length_errors rec_len < 4 || rec_len > 2000 + * rx_over_errors receive overrun (OVR) + * rx_crc_errors rx crc error + * rx_frame_errors aborts rec'd (ABO) + * rx_fifo_errors status fifo overrun (PBUFOVR) + * rx_missed_errors receive buffer overrun (BUFOVR) + * tx_aborted_errors ? + * tx_carrier_errors modem line status changes + * tx_fifo_errors tx underrun (locomx) + */ + +struct comx_protocol { + char *name; + char *version; + unsigned short encap_type; + int (*line_init)(struct device *dev); + int (*line_exit)(struct device *dev); + struct comx_protocol *next; + }; + +struct comx_hardware { + char *name; + char *version; + int (*hw_init)(struct device *dev); + int (*hw_exit)(struct device *dev); + int (*hw_dump)(struct device *dev); + struct comx_hardware *next; + }; + +struct comx_channel { + struct device *dev; // Where we belong to + struct device *twin; // On dual-port cards + struct proc_dir_entry *procdir; // the directory + + unsigned char init_status; + unsigned char line_status; + + struct timer_list lineup_timer; // against line jitter + int lineup_pending; + unsigned char lineup_delay; + +#if 0 + struct timer_list reset_timer; // for board resetting + int reset_pending; + int reset_timeout; +#endif + + struct net_device_stats stats; + struct net_device_stats *current_stats; +#if 0 + unsigned long board_resets; +#endif + unsigned long *avg_bytes; + int loadavg_counter, loadavg_size; + int loadavg[3]; + struct timer_list loadavg_timer; + int debug_flags; + char *debug_area; + int debug_start, debug_end, debug_size; + struct proc_dir_entry *debug_file; +#ifdef CONFIG_COMX_DEBUG_RAW + char *raw; + int raw_len; +#endif + // LINE specific + struct comx_protocol *protocol; + void (*LINE_rx)(struct device *dev, struct sk_buff *skb); + int (*LINE_tx)(struct device *dev); + void (*LINE_status)(struct device *dev, u_short status); + int (*LINE_open)(struct device *dev); + int (*LINE_close)(struct device *dev); + int (*LINE_xmit)(struct sk_buff *skb, struct device *dev); + int (*LINE_header)(struct sk_buff *skb, struct device *dev, + u_short type,void *daddr, void *saddr, + unsigned len); + int (*LINE_rebuild_header)(struct sk_buff *skb); + int (*LINE_statistics)(struct device *dev, char *page); + int (*LINE_parameter_check)(struct device *dev); + int (*LINE_ioctl)(struct device *dev, struct ifreq *ifr, + int cmd); + void (*LINE_mod_use)(int); + void * LINE_privdata; + + // HW specific + + struct comx_hardware *hardware; + void (*HW_board_on)(struct device *dev); + void (*HW_board_off)(struct device *dev); + struct device *(*HW_access_board)(struct device *dev); + void (*HW_release_board)(struct device *dev, struct device *savep); + int (*HW_txe)(struct device *dev); + int (*HW_open)(struct device *dev); + int (*HW_close)(struct device *dev); + int (*HW_send_packet)(struct device *dev,struct sk_buff *skb); + int (*HW_statistics)(struct device *dev, char *page); +#if 0 + int (*HW_reset)(struct device *dev, char *page); +#endif + int (*HW_load_board)(struct device *dev); + void (*HW_set_clock)(struct device *dev); + void *HW_privdata; + }; + +struct comx_debugflags_struct { + char *name; + int value; + }; + +#define COMX_ROOT_DIR_NAME "comx" + +#define FILENAME_HARDWARE "boardtype" +#define FILENAME_HARDWARELIST "boardtypes" +#define FILENAME_PROTOCOL "protocol" +#define FILENAME_PROTOCOLLIST "protocols" +#define FILENAME_DEBUG "debug" +#define FILENAME_CLOCK "clock" +#define FILENAME_STATUS "status" +#define FILENAME_IO "io" +#define FILENAME_IRQ "irq" +#define FILENAME_KEEPALIVE "keepalive" +#define FILENAME_LINEUPDELAY "lineup_delay" +#define FILENAME_CHANNEL "channel" +#define FILENAME_FIRMWARE "firmware" +#define FILENAME_MEMADDR "memaddr" +#define FILENAME_TWIN "twin" +#define FILENAME_T1 "t1" +#define FILENAME_T2 "t2" +#define FILENAME_N2 "n2" +#define FILENAME_WINDOW "window" +#define FILENAME_MODE "mode" +#define FILENAME_DLCI "dlci" +#define FILENAME_MASTER "master" +#ifdef CONFIG_COMX_DEBUG_RAW +#define FILENAME_RAW "raw" +#endif + +#define PROTONAME_NONE "none" +#define HWNAME_NONE "none" +#define KEEPALIVE_OFF "off" + +#define FRAME_ACCEPTED 0 /* sending and xmitter busy */ +#define FRAME_DROPPED 1 +#define FRAME_ERROR 2 /* xmitter error */ +#define FRAME_QUEUED 3 /* sending but more can come */ + +#define LINE_UP 1 /* Modem UP */ +#define PROTO_UP 2 +#define PROTO_LOOP 4 + +#define HW_OPEN 1 +#define LINE_OPEN 2 +#define FW_LOADED 4 +#define IRQ_ALLOCATED 8 + +#define DEBUG_COMX_RX 2 +#define DEBUG_COMX_TX 4 +#define DEBUG_HW_TX 16 +#define DEBUG_HW_RX 32 +#define DEBUG_HDLC_KEEPALIVE 64 +#define DEBUG_COMX_PPP 128 +#define DEBUG_COMX_LAPB 256 +#define DEBUG_COMX_DLCI 512 + +#define DEBUG_PAGESIZE 3072 +#define DEFAULT_DEBUG_SIZE 4096 +#define DEFAULT_LINEUP_DELAY 1 +#define FILE_PAGESIZE 3072 + +#ifndef COMX_PPP_MAJOR +#define COMX_PPP_MAJOR 88 +#endif + + +#ifndef min +#define min(a,b) ((a) > (b) ? (b) : (a)) +#endif +#ifndef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#endif + + +#define COMX_CHANNEL(dev) ((struct comx_channel*)dev->priv) + +#define TWIN(dev) (COMX_CHANNEL(dev)->twin) + + +#ifndef byte +typedef u8 byte; +#endif +#ifndef word +typedef u16 word; +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +extern struct proc_dir_entry comx_root_dir; + +extern int comx_register_hardware(struct comx_hardware *comx_hw); +extern int comx_unregister_hardware(char *name); +extern int comx_register_protocol(struct comx_protocol *comx_line); +extern int comx_unregister_protocol(char *name); + +extern int comx_rx(struct device *dev, struct sk_buff *skb); +extern void comx_status(struct device *dev, int status); +extern void comx_lineup_func(unsigned long d); + +extern int comx_debug(struct device *dev, char *fmt, ...); +extern int comx_debug_skb(struct device *dev, struct sk_buff *skb, char *msg); +extern int comx_debug_bytes(struct device *dev, unsigned char *bytes, int len, + char *msg); +extern int comx_strcasecmp(const char *cs, const char *ct); + +extern struct inode_operations comx_normal_inode_ops; diff -u --recursive --new-file v2.2.13/linux/drivers/net/comxhw.h linux/drivers/net/comxhw.h --- v2.2.13/linux/drivers/net/comxhw.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/comxhw.h Tue Jan 4 10:12:17 2000 @@ -0,0 +1,112 @@ +/* + * Defines for comxhw.c + * + * Original authors: Arpad Bakay , + * Peter Bajan , + * Previous maintainer: Tivadar Szemethy + * Current maintainer: Gergely Madarasz + * + * Copyright (C) 1995-1999 ITConsult-Pro Co. + * + * 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. + * + */ + +#define LOCOMX_IO_EXTENT 8 +#define COMX_IO_EXTENT 4 +#define HICOMX_IO_EXTENT 16 + +#define COMX_MAX_TX_SIZE 1600 + +#define COMX_JAIL_OFFSET 0xffff +#define COMX_JAIL_VALUE 0xfe +#define COMX_MEMORY_SIZE 65536 +#define HICOMX_MEMORY_SIZE 16384 +#define COMX_MEM_MIN 0xa0000 +#define COMX_MEM_MAX 0xf0000 + +#define COMX_DEFAULT_IO 0x360 +#define COMX_DEFAULT_IRQ 10 +#define COMX_DEFAULT_MEMADDR 0xd0000 +#define HICOMX_DEFAULT_IO 0x320 +#define HICOMX_DEFAULT_IRQ 10 +#define HICOMX_DEFAULT_MEMADDR 0xd0000 +#define LOCOMX_DEFAULT_IO 0x368 +#define LOCOMX_DEFAULT_IRQ 7 + +#define MAX_CHANNELNO 2 + +#define COMX_CHANNEL_OFFSET 0x2000 + +#define COMX_ENABLE_BOARD_IT 0x40 +#define COMX_BOARD_RESET 0x20 +#define COMX_ENABLE_BOARD_MEM 0x10 +#define COMX_DISABLE_BOARD_MEM 0 +#define COMX_DISABLE_ALL 0x00 + +#define HICOMX_DISABLE_ALL 0x00 +#define HICOMX_ENABLE_BOARD_MEM 0x02 +#define HICOMX_DISABLE_BOARD_MEM 0x0 +#define HICOMX_BOARD_RESET 0x01 +#define HICOMX_PRG_MEM 4 +#define HICOMX_DATA_MEM 0 +#define HICOMX_ID_BYTE 0x55 + +#define CMX_ID_BYTE 0x31 +#define COMX_CLOCK_CONST 8000 + +#define LINKUP_READY 3 + +#define OFF_FW_L1_ID 0x01e /* ID bytes */ +#define OFF_FW_L2_ID 0x1006 +#define FW_L1_ID_1 0xab +#define FW_L1_ID_2_COMX 0xc0 +#define FW_L1_ID_2_HICOMX 0xc1 +#define FW_L2_ID_1 0xab + +#define OFF_A_L2_CMD 0x130 /* command register for L2 */ +#define OFF_A_L2_CMDPAR 0x131 /* command parameter byte */ +#define OFF_A_L1_STATB 0x122 /* stat. block for L1 */ +#define OFF_A_L1_ABOREC 0x122 /* receive ABORT counter */ +#define OFF_A_L1_OVERRUN 0x123 /* receive overrun counter */ +#define OFF_A_L1_CRCREC 0x124 /* CRC error counter */ +#define OFF_A_L1_BUFFOVR 0x125 /* buffer overrun counter */ +#define OFF_A_L1_PBUFOVR 0x126 /* priority buffer overrun counter */ +#define OFF_A_L1_MODSTAT 0x127 /* current state of modem ctrl lines */ +#define OFF_A_L1_STATE 0x127 /* end of stat. block for L1 */ +#define OFF_A_L1_TXPC 0x128 /* Tx counter for the PC */ +#define OFF_A_L1_TXZ80 0x129 /* Tx counter for the Z80 */ +#define OFF_A_L1_RXPC 0x12a /* Rx counter for the PC */ +#define OFF_A_L1_RXZ80 0x12b /* Rx counter for the Z80 */ +#define OFF_A_L1_REPENA 0x12c /* IT rep disable */ +#define OFF_A_L1_CHNR 0x12d /* L1 channel logical number */ +#define OFF_A_L1_CLKINI 0x12e /* Timer Const */ +#define OFF_A_L2_LINKUP 0x132 /* Linkup byte */ +#define OFF_A_L2_DAV 0x134 /* Rx DAV */ +#define OFF_A_L2_RxBUFP 0x136 /* Rx buff relative to membase */ +#define OFF_A_L2_TxEMPTY 0x138 /* Tx Empty */ +#define OFF_A_L2_TxBUFP 0x13a /* Tx Buf */ +#define OFF_A_L2_NBUFFS 0x144 /* Number of buffers to fetch */ + +#define OFF_A_L2_SABMREC 0x164 /* LAPB no. of SABMs received */ +#define OFF_A_L2_SABMSENT 0x165 /* LAPB no. of SABMs sent */ +#define OFF_A_L2_REJREC 0x166 /* LAPB no. of REJs received */ +#define OFF_A_L2_REJSENT 0x167 /* LAPB no. of REJs sent */ +#define OFF_A_L2_FRMRREC 0x168 /* LAPB no. of FRMRs received */ +#define OFF_A_L2_FRMRSENT 0x169 /* LAPB no. of FRMRs sent */ +#define OFF_A_L2_PROTERR 0x16A /* LAPB no. of protocol errors rec'd */ +#define OFF_A_L2_LONGREC 0x16B /* LAPB no. of long frames */ +#define OFF_A_L2_INVNR 0x16C /* LAPB no. of invalid N(R)s rec'd */ +#define OFF_A_L2_UNDEFFR 0x16D /* LAPB no. of invalid frames */ + +#define OFF_A_L2_T1 0x174 /* T1 timer */ +#define OFF_A_L2_ADDR 0x176 /* DCE = 1, DTE = 3 */ + +#define COMX_CMD_INIT 1 +#define COMX_CMD_EXIT 2 +#define COMX_CMD_OPEN 16 +#define COMX_CMD_CLOSE 17 + diff -u --recursive --new-file v2.2.13/linux/drivers/net/hamradio/Config.in linux/drivers/net/hamradio/Config.in --- v2.2.13/linux/drivers/net/hamradio/Config.in Mon Aug 9 16:05:56 1999 +++ linux/drivers/net/hamradio/Config.in Tue Jan 4 10:12:17 2000 @@ -11,12 +11,12 @@ bool ' support for TRX that feedback the tx signal to rx' CONFIG_SCC_TRXECHO fi -tristate 'BAYCOM ser12 fullduplex driver for AX.25' CONFIG_BAYCOM_SER_FDX -tristate 'BAYCOM ser12 halfduplex driver for AX.25' CONFIG_BAYCOM_SER_HDX -dep_tristate 'BAYCOM picpar and par96 driver for AX.25' CONFIG_BAYCOM_PAR $CONFIG_PARPORT -dep_tristate 'BAYCOM epp driver for AX.25' CONFIG_BAYCOM_EPP $CONFIG_PARPORT +dep_tristate 'BAYCOM ser12 fullduplex driver for AX.25' CONFIG_BAYCOM_SER_FDX $CONFIG_AX25 +dep_tristate 'BAYCOM ser12 halfduplex driver for AX.25' CONFIG_BAYCOM_SER_HDX $CONFIG_AX25 +dep_tristate 'BAYCOM picpar and par96 driver for AX.25' CONFIG_BAYCOM_PAR $CONFIG_PARPORT $CONFIG_AX25 +dep_tristate 'BAYCOM epp driver for AX.25' CONFIG_BAYCOM_EPP $CONFIG_PARPORT $CONFIG_AX25 -tristate 'Soundcard modem driver' CONFIG_SOUNDMODEM +dep_tristate 'Soundcard modem driver' CONFIG_SOUNDMODEM $CONFIG_AX25 if [ "$CONFIG_SOUNDMODEM" != "n" ]; then bool ' soundmodem support for Soundblaster and compatible cards' CONFIG_SOUNDMODEM_SBC bool ' soundmodem support for WSS and Crystal cards' CONFIG_SOUNDMODEM_WSS @@ -29,4 +29,4 @@ bool ' soundmodem support for 9600 baud FSK G3RUH modulation' CONFIG_SOUNDMODEM_FSK9600 fi -tristate 'YAM driver for AX.25' CONFIG_YAM +dep_tristate 'YAM driver for AX.25' CONFIG_YAM $CONFIG_AX25 diff -u --recursive --new-file v2.2.13/linux/drivers/net/hamradio/yam.c linux/drivers/net/hamradio/yam.c --- v2.2.13/linux/drivers/net/hamradio/yam.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/net/hamradio/yam.c Tue Jan 4 10:12:17 2000 @@ -894,10 +894,12 @@ } #ifdef CONFIG_INET + #ifndef PROC_NET_YAM #define PROC_NET_YAM (PROC_NET_LAST+10) /* Sorry again... */ #endif +#ifdef CONFIG_PROC_FS struct proc_dir_entry yam_proc_dir_entry = { PROC_NET_YAM, 3, "yam", S_IFREG | S_IRUGO, 1, 0, 0, 0, @@ -906,6 +908,10 @@ #define yam_net_procfs_init() proc_net_register(&yam_proc_dir_entry); #define yam_net_procfs_remove() proc_net_unregister(PROC_NET_YAM); +#else +#define yam_net_procfs_init() +#define yam_net_procfs_remove() +#endif #else #define yam_net_procfs_init() #define yam_net_procfs_remove() diff -u --recursive --new-file v2.2.13/linux/drivers/net/hscx.h linux/drivers/net/hscx.h --- v2.2.13/linux/drivers/net/hscx.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/hscx.h Tue Jan 4 10:12:17 2000 @@ -0,0 +1,103 @@ +#define HSCX_MTU 1600 + +#define HSCX_ISTA 0x00 +#define HSCX_MASK 0x00 +#define HSCX_STAR 0x01 +#define HSCX_CMDR 0x01 +#define HSCX_MODE 0x02 +#define HSCX_TIMR 0x03 +#define HSCX_EXIR 0x04 +#define HSCX_XAD1 0x04 +#define HSCX_RBCL 0x05 +#define HSCX_SAD2 0x05 +#define HSCX_RAH1 0x06 +#define HSCX_RSTA 0x07 +#define HSCX_RAH2 0x07 +#define HSCX_RAL1 0x08 +#define HSCX_RCHR 0x09 +#define HSCX_RAL2 0x09 +#define HSCX_XBCL 0x0a +#define HSCX_BGR 0x0b +#define HSCX_CCR2 0x0c +#define HSCX_RBCH 0x0d +#define HSCX_XBCH 0x0d +#define HSCX_VSTR 0x0e +#define HSCX_RLCR 0x0e +#define HSCX_CCR1 0x0f +#define HSCX_FIFO 0x1e + +#define HSCX_HSCX_CHOFFS 0x400 +#define HSCX_SEROFFS 0x1000 + +#define HSCX_RME 0x80 +#define HSCX_RPF 0x40 +#define HSCX_RSC 0x20 +#define HSCX_XPR 0x10 +#define HSCX_TIN 0x08 +#define HSCX_ICA 0x04 +#define HSCX_EXA 0x02 +#define HSCX_EXB 0x01 + +#define HSCX_XMR 0x80 +#define HSCX_XDU 0x40 +#define HSCX_EXE 0x40 +#define HSCX_PCE 0x20 +#define HSCX_RFO 0x10 +#define HSCX_CSC 0x08 +#define HSCX_RFS 0x04 + +#define HSCX_XDOV 0x80 +#define HSCX_XFW 0x40 +#define HSCX_XRNR 0x20 +#define HSCX_RRNR 0x10 +#define HSCX_RLI 0x08 +#define HSCX_CEC 0x04 +#define HSCX_CTS 0x02 +#define HSCX_WFA 0x01 + +#define HSCX_RMC 0x80 +#define HSCX_RHR 0x40 +#define HSCX_RNR 0x20 +#define HSCX_XREP 0x20 +#define HSCX_STI 0x10 +#define HSCX_XTF 0x08 +#define HSCX_XIF 0x04 +#define HSCX_XME 0x02 +#define HSCX_XRES 0x01 + +#define HSCX_AUTO 0x00 +#define HSCX_NONAUTO 0x40 +#define HSCX_TRANS 0x80 +#define HSCX_XTRANS 0xc0 +#define HSCX_ADM16 0x20 +#define HSCX_ADM8 0x00 +#define HSCX_TMD_EXT 0x00 +#define HSCX_TMD_INT 0x10 +#define HSCX_RAC 0x08 +#define HSCX_RTS 0x04 +#define HSCX_TLP 0x01 + +#define HSCX_VFR 0x80 +#define HSCX_RDO 0x40 +#define HSCX_CRC 0x20 +#define HSCX_RAB 0x10 + +#define HSCX_CIE 0x04 +#define HSCX_RIE 0x02 + +#define HSCX_DMA 0x80 +#define HSCX_NRM 0x40 +#define HSCX_CAS 0x20 +#define HSCX_XC 0x10 + +#define HSCX_OV 0x10 + +#define HSCX_CD 0x80 + +#define HSCX_RC 0x80 + +#define HSCX_PU 0x80 +#define HSCX_NRZ 0x00 +#define HSCX_NRZI 0x40 +#define HSCX_ODS 0x10 +#define HSCX_ITF 0x08 diff -u --recursive --new-file v2.2.13/linux/drivers/net/lance.c linux/drivers/net/lance.c --- v2.2.13/linux/drivers/net/lance.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/net/lance.c Tue Jan 4 10:12:17 2000 @@ -40,12 +40,16 @@ 8/20/96 Fixed 7990 autoIRQ failure and reversed unneeded alignment -djb v1.12 10/27/97 Module support -djb v1.14 2/3/98 Module support modified, made PCI support optional -djb + v1.15 5/27/99 Fixed bug in the cleanup_module(). dev->priv was freed + before unregister_netdev() which caused NULL pointer + reference later in the chain (in rtnetlink_fill_ifinfo()) + -- Mika Kuoppala Forward ported v1.14 to 2.1.129, merged the PCI and misc changes from the 2.1 version of the old driver - Alan Cox */ -static const char *version = "lance.c:v1.14ac 1998/11/20 dplatt@3do.com, becker@cesdis.gsfc.nasa.gov\n"; +static const char *version = "lance.c:v1.15ac 1999/11/13 dplatt@3do.com, becker@cesdis.gsfc.nasa.gov\n"; #include #include @@ -349,11 +353,11 @@ for (this_dev = 0; this_dev < MAX_CARDS; this_dev++) { struct device *dev = &dev_lance[this_dev]; if (dev->priv != NULL) { - kfree(dev->priv); - dev->priv = NULL; + unregister_netdev(dev); free_dma(dev->dma); release_region(dev->base_addr, LANCE_TOTAL_SIZE); - unregister_netdev(dev); + kfree(dev->priv); + dev->priv = NULL; } } } diff -u --recursive --new-file v2.2.13/linux/drivers/net/mixcom.h linux/drivers/net/mixcom.h --- v2.2.13/linux/drivers/net/mixcom.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/mixcom.h Tue Jan 4 10:12:17 2000 @@ -0,0 +1,35 @@ +/* + * Defines for the mixcom board + * + * Author: Gergely Madarasz + * + * Copyright (C) 1999 ITConsult-Pro Co. + * + * 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. + * + */ + +#define MIXCOM_IO_EXTENT 0x20 + +#define MIXCOM_DEFAULT_IO 0x180 +#define MIXCOM_DEFAULT_IRQ 5 + +#define MIXCOM_ID 0x11 +#define MIXCOM_SERIAL_OFFSET 0x1000 +#define MIXCOM_CHANNEL_OFFSET 0x400 +#define MIXCOM_IT_OFFSET 0xc14 +#define MIXCOM_STATUS_OFFSET 0xc14 +#define MIXCOM_ID_OFFSET 0xc10 +#define MIXCOM_ON 0x1 +#define MIXCOM_OFF 0x0 + +/* Status register bits */ + +#define MIXCOM_CTSB 0x1 +#define MIXCOM_CTSA 0x2 +#define MIXCOM_CHANNELNO 0x20 +#define MIXCOM_POWERFAIL 0x40 +#define MIXCOM_BOOT 0x80 diff -u --recursive --new-file v2.2.13/linux/drivers/net/myri_sbus.c linux/drivers/net/myri_sbus.c --- v2.2.13/linux/drivers/net/myri_sbus.c Fri Mar 26 13:57:41 1999 +++ linux/drivers/net/myri_sbus.c Tue Jan 4 10:12:17 2000 @@ -222,11 +222,11 @@ } if(i == 5000) - printk("myricom: Chip would not reset after firmware load.\n"); + printk(KERN_ERR "myricom: Chip would not reset after firmware load.\n"); i = myri_do_handshake(mp); if(i) - printk("myricom: Handshake with LANAI failed.\n"); + printk(KERN_ERR "myricom: Handshake with LANAI failed.\n"); if(mp->eeprom.cpuvers == CPUVERS_4_0) mp->lregs->vers = 0; @@ -575,7 +575,7 @@ return 1; } else { DTX(("resetting, return 0\n")); - printk("%s: transmit timed out, resetting\n", dev->name); + printk(KERN_ERR "%s: transmit timed out, resetting\n", dev->name); mp->enet_stats.tx_errors++; myri_init(mp, in_interrupt()); dev->tbusy = 0; diff -u --recursive --new-file v2.2.13/linux/drivers/net/ncr885_debug.h linux/drivers/net/ncr885_debug.h --- v2.2.13/linux/drivers/net/ncr885_debug.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/ncr885_debug.h Tue Jan 4 10:12:17 2000 @@ -0,0 +1,54 @@ +#ifndef _H_NCR885_DEBUG +#define _H_NCR885_DEBUG + +struct ncr885e_regs { + unsigned long tx_status; + unsigned long rx_status; + unsigned long mac_config; + unsigned long tx_control; + unsigned long rx_control; + unsigned long tx_cmd_ptr; + unsigned long rx_cmd_ptr; + unsigned long int_status; +}; + +#ifndef __KERNEL__ + +struct ncr885e_private { + + struct dbdma_cmd *head; + struct dbdma_cmd *tx_cmds; + struct dbdma_cmd *rx_cmds; + struct dbdma_cmd *stop_cmd; + + struct sk_buff *tx_skbufs[NR_TX_RING]; + struct sk_buff *rx_skbufs[NR_RX_RING]; + + int rx_current; + int rx_dirty; + + int tx_dirty; + int tx_current; + + unsigned short tx_status[NR_TX_RING]; + + unsigned char tx_fullup; + unsigned char tx_active; + + struct net_device_stats stats; + + struct device *dev; + + struct timer_list tx_timeout; + int timeout_active; + + spinlock_t lock; +}; + +#endif /* __KERNEL__ */ + + +#define NCR885E_GET_PRIV _IOR('N',1,sizeof( struct ncr885e_private )) +#define NCR885E_GET_REGS _IOR('N',2,sizeof( struct ncr885e_regs )) + +#endif diff -u --recursive --new-file v2.2.13/linux/drivers/net/ncr885e.c linux/drivers/net/ncr885e.c --- v2.2.13/linux/drivers/net/ncr885e.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/ncr885e.c Tue Jan 4 10:12:17 2000 @@ -0,0 +1,1458 @@ +/* + * An Ethernet driver for the dual-function NCR 53C885 SCSI/Ethernet + * controller. + * + * + * 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. + * + */ + +static const char *version = +"ncr885e.c:v0.8 11/30/98 dan@synergymicro.com\n"; + +#include + +#ifdef MODULE +#ifdef MODVERSIONS +#include +#endif +#include +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ncr885e.h" +#include "ncr885_debug.h" + +static const char *chipname = "ncr885e"; + +/* debugging flags */ +#if 0 +#define DEBUG_FUNC 0x0001 +#define DEBUG_PACKET 0x0002 +#define DEBUG_CMD 0x0004 +#define DEBUG_CHANNEL 0x0008 +#define DEBUG_INT 0x0010 +#define DEBUG_RX 0x0020 +#define DEBUG_TX 0x0040 +#define DEBUG_DMA 0x0080 +#define DEBUG_MAC 0x0100 +#define DEBUG_DRIVER 0x0200 +#define DEBUG_ALL 0x1fff +#endif + +#ifdef DEBUG_NCR885E +#define NCR885E_DEBUG 0 +#else +#define NCR885E_DEBUG 0 +#endif + +/* The 885's Ethernet PCI device id. */ +#ifndef PCI_DEVICE_ID_NCR_53C885_ETHERNET +#define PCI_DEVICE_ID_NCR_53C885_ETHERNET 0x0701 +#endif + +#define NR_RX_RING 8 +#define NR_TX_RING 8 +#define MAX_TX_ACTIVE (NR_TX_RING-1) +#define NCMDS_TX NR_TX_RING + +#define RX_BUFLEN (ETH_FRAME_LEN + 8) +#define TX_TIMEOUT 5*HZ + +#define NCR885E_TOTAL_SIZE 0xe0 + +#define TXSR (1<<6) /* tx: xfer status written */ +#define TXABORT (1<<7) /* tx: abort */ +#define EOP (1<<7) /* rx: end of packet written to buffer */ + +int ncr885e_debug = NCR885E_DEBUG; +static int print_version = 0; + +struct ncr885e_private { + + /* preserve a 1-1 marking with buffs */ + struct dbdma_cmd *head; + struct dbdma_cmd *tx_cmds; + struct dbdma_cmd *rx_cmds; + struct dbdma_cmd *stop_cmd; + + struct sk_buff *tx_skbufs[NR_TX_RING]; + struct sk_buff *rx_skbufs[NR_RX_RING]; + + int rx_current; + int rx_dirty; + + int tx_dirty; + int tx_current; + + unsigned short tx_status[NR_TX_RING]; + + unsigned char tx_fullup; + unsigned char tx_active; + + struct net_device_stats stats; + + struct device *dev; + + struct timer_list tx_timeout; + int timeout_active; + + spinlock_t lock; +}; + +#ifdef MODULE +static struct device *root_dev = NULL; +#endif + + +static int ncr885e_open( struct device *dev ); +static int ncr885e_close( struct device *dev ); +static void ncr885e_rx( struct device *dev ); +static void ncr885e_tx( struct device *dev ); +static int ncr885e_probe1( struct device *dev, unsigned long ioaddr, + unsigned char irq ); +static int ncr885e_xmit_start( struct sk_buff *skb, struct device *dev ); +static struct net_device_stats *ncr885e_stats( struct device *dev ); +static void ncr885e_set_multicast( struct device *dev ); +static void ncr885e_config( struct device *dev ); +static int ncr885e_set_address( struct device *dev, void *addr ); +static void ncr885e_interrupt( int irq, void *dev_id, struct pt_regs *regs ); +static void show_dbdma_cmd( volatile struct dbdma_cmd *cmd ); +#if 0 +static int read_eeprom( unsigned int ioadddr, int location ); +#endif + +#ifdef NCR885E_DEBUG_MII +static void show_mii( unsigned long ioaddr ); +static int read_mii( unsigned long ioaddr, int reg ); +static void write_mii( unsigned long ioaddr, int reg, int data ); +#endif /* NCR885E_DEBUG_MII */ + +#define TX_RESET_FLAGS (TX_CHANNEL_RUN|TX_CHANNEL_PAUSE|TX_CHANNEL_WAKE) +#define RX_RESET_FLAGS (RX_CHANNEL_RUN|RX_CHANNEL_PAUSE|RX_CHANNEL_WAKE) + + +#if 0 +static int +debug_ioctl( struct device *dev, struct ifreq *req, int cmd ) +{ + unsigned long ioaddr = dev->base_addr; + struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv; + struct ncr885e_private *data; + struct ncr885e_regs *regs; + unsigned long flags; + + union { + struct ncr885e_regs dump; + struct ncr885e_private priv; + } temp; + + switch( cmd ) { + + /* dump the rx ring status */ + case NCR885E_GET_PRIV: + + data = (struct ncr885e_private *) &req->ifr_data; + + if ( verify_area(VERIFY_WRITE, &req->ifr_data, + sizeof( struct ncr885e_private ))) + return -EFAULT; + + memcpy((char *) &temp.priv, sp, sizeof( struct ncr885e_private )); + copy_to_user( data, (char *) &temp.priv, sizeof( struct ncr885e_private)); + break; + + case NCR885E_GET_REGS: + + regs = (struct ncr885e_regs *) &req->ifr_data; + + if ( verify_area( VERIFY_WRITE, &req->ifr_data, + sizeof( struct ncr885e_regs ))) + return -EFAULT; + + spin_lock_irqsave( &sp->lock, flags ); + + temp.dump.tx_status = inl( ioaddr + TX_CHANNEL_STATUS ); + temp.dump.rx_status = inl( ioaddr + RX_CHANNEL_STATUS ); + temp.dump.mac_config = inl( ioaddr + MAC_CONFIG ); + temp.dump.tx_control = inl( ioaddr + TX_CHANNEL_CONTROL ); + temp.dump.rx_control = inl( ioaddr + RX_CHANNEL_CONTROL ); + temp.dump.tx_cmd_ptr = inl( ioaddr + TX_CMD_PTR_LO ); + temp.dump.rx_cmd_ptr = inl( ioaddr + RX_CMD_PTR_LO ); + temp.dump.int_status = inl( ioaddr + INTERRUPT_STATUS_REG ); + + spin_unlock_irqrestore( &sp->lock, flags ); + copy_to_user( regs, (char *) &temp.dump, sizeof( struct ncr885e_regs )); + + break; + + default: + return -EOPNOTSUPP; + } + return 0; +} +#endif + +/* Enable interrupts on the 53C885 */ +static inline void +ncr885e_enable( struct device *dev ) + +{ + unsigned long ioaddr = dev->base_addr; + unsigned short reg; + + reg = inw(ioaddr + INTERRUPT_ENABLE); + outw(reg | INTERRUPT_INTE, ioaddr + INTERRUPT_ENABLE); +} + +/* Disable interrupts on the 53c885 */ +static inline void +ncr885e_disable( struct device *dev ) + +{ + unsigned long ioaddr = dev->base_addr; + unsigned short reg; + + reg = inw( ioaddr + INTERRUPT_ENABLE ); + outw( reg & ~INTERRUPT_INTE, ioaddr + INTERRUPT_ENABLE ); +} + + +static inline void +ncr885e_reset( struct device *dev ) + +{ + unsigned short reg; + unsigned long cntl; + int i; + unsigned long ioaddr = dev->base_addr; + + if (ncr885e_debug > 1) + printk( KERN_INFO "%s: Resetting 53C885...\n", dev->name ); + + /* disable interrupts on the 53C885 */ + ncr885e_disable( dev ); + + /* disable rx in the MAC */ + reg = inw( ioaddr + MAC_CONFIG ); + outw( reg & ~MAC_CONFIG_RXEN, ioaddr + MAC_CONFIG ); + + for( i=0; i < 100; i++ ) { + + if ( !(inw( ioaddr + MAC_CONFIG ) & MAC_CONFIG_RXEN )) + break; + udelay( 10 ); + } + + reg = inw( ioaddr + MAC_CONFIG ); + outw( reg | MAC_CONFIG_SRST, ioaddr + MAC_CONFIG ); + outw( reg, ioaddr + MAC_CONFIG ); + + /* disable both rx and tx DBDMA channels */ + outl( TX_DBDMA_ENABLE << 16, ioaddr + TX_CHANNEL_CONTROL ); + outl( RX_DBDMA_ENABLE << 16, ioaddr + RX_CHANNEL_CONTROL ); + + for( i=0; i < 100; i++ ) { + + if ( !(inw( ioaddr + TX_CHANNEL_STATUS ) & TX_DBDMA_ENABLE ) && + !(inw( ioaddr + RX_CHANNEL_STATUS ) & RX_DBDMA_ENABLE )) + break; + udelay( 10 ); + } + + /* perform a "software reset" */ + cntl = inl( ioaddr + DBDMA_CONTROL ); + outl( cntl | DBDMA_SRST, ioaddr + DBDMA_CONTROL ); + + for( i=0; i < 100; i++ ) { + + if ( !(inl( ioaddr + DBDMA_CONTROL ) & DBDMA_SRST )) + break; + udelay( 10 ); + } + + /* books says that a software reset should be done to the MAC, as + well. This true??? */ + + if (ncr885e_debug > 3) + printk( KERN_INFO "%s: reset complete\n", dev->name ); + +} + + +/* configure the 53C885 chip. + + The DBDMA command descriptors on the 53C885 can be programmed to + branch, interrupt or pause conditionally or always by using the + interrupt, branch and wait select registers. */ + +static void +ncr885e_config( struct device *dev ) + +{ + unsigned long ioaddr = dev->base_addr; + + if (ncr885e_debug > 3) + printk( KERN_INFO "%s: Configuring 53C885.\n", dev->name ); + + ncr885e_reset( dev ); + + /* The 53C885 can be programmed to perform conditional DBDMA + branches, interrupts or waits. + + Neither channel makes use of "wait", as it requires that the + DBDMA engine to be restarted. Don't go there. The rx channel + will branch upon the successful reception of a packet ('EOP' in + the xfer_status field). The branch address is to the STOP + DBDMA command descriptor, which shuts down the rx channel until + the interrupt is serviced. */ + + /* cause tx channel to stop after "status received" */ + outl( 0, ioaddr + TX_INT_SELECT ); + outl( (TX_WAIT_STAT_RECV << 16) | TX_WAIT_STAT_RECV, + ioaddr + TX_WAIT_SELECT ); + outl( 0, ioaddr + TX_BRANCH_SELECT ); + + /* cause rx channel to branch to the STOP descriptor on "End-of-Packet" */ +#if 0 + outl( (RX_INT_SELECT_EOP << 16) | RX_INT_SELECT_EOP, + ioaddr + RX_INT_SELECT ); +#else + outl( 0, ioaddr + RX_INT_SELECT ); +#endif +#if 0 + outl( 0, ioaddr + RX_WAIT_SELECT ); +#else + outl( (RX_WAIT_SELECT_EOP << 16) | RX_WAIT_SELECT_EOP, + ioaddr + RX_WAIT_SELECT ); +#endif +#if 1 + outl( 0, ioaddr + RX_BRANCH_SELECT ); +#else + outl( (RX_BRANCH_SELECT_EOP << 16) | RX_BRANCH_SELECT_EOP, + ioaddr + RX_BRANCH_SELECT ); +#endif + + /* configure DBDMA */ + outl( (DBDMA_BE | DBDMA_DPMRLE | DBDMA_TDPCE | + DBDMA_DDPE | DBDMA_TDPE | + (DBDMA_BURST_4 << DBDMA_TX_BST_SHIFT) | + (DBDMA_BURST_4 << DBDMA_RX_BST_SHIFT) | + (DBDMA_TX_ARBITRATION_DEFAULT) | + (DBDMA_RX_ARBITRATION_DEFAULT)), ioaddr + DBDMA_CONTROL ); + + outl( 0, ioaddr + TX_THRESHOLD ); + + /* disable MAC loopback */ + outl( (MAC_CONFIG_ITXA | MAC_CONFIG_RXEN | MAC_CONFIG_RETRYL | + MAC_CONFIG_PADEN | (0x18 << 16)), + ioaddr + MAC_CONFIG ); + + /* configure MAC */ + outl( (MAC_CONFIG_ITXA | MAC_CONFIG_RXEN | MAC_CONFIG_RETRYL | + MAC_CONFIG_PADEN | ( 0x18 << 16)), ioaddr + MAC_CONFIG ); + + outw( (0x1018), ioaddr + NBTOB_INTP_GAP ); + + /* clear and enable interrupts */ + inw( ioaddr + INTERRUPT_CLEAR ); + ncr885e_enable( dev ); + + /* and enable them in the chip */ + outl( (INTERRUPT_INTE|INTERRUPT_TX_MASK|INTERRUPT_RX_MASK)<<16, + ioaddr + INTERRUPT_ENABLE - 2); + + if (ncr885e_debug > 3) + printk( KERN_INFO "%s: 53C885 config complete.\n", dev->name ); + + return; +} + + + +/* + transmit interrupt */ + +static void +ncr885e_tx( struct device *dev ) + +{ + struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv; + volatile struct dbdma_cmd *cp, *dp; + unsigned short txbits, xfer; + int i; + + del_timer( &sp->tx_timeout ); + + if (ncr885e_debug > 3) + printk( KERN_INFO "%s: ncr885e_tx: active=%d, dirty=%d, current=%d\n", + dev->name, sp->tx_active, sp->tx_dirty, sp->tx_current ); + + sp->timeout_active = 0; + + i = sp->tx_dirty; + cp = sp->tx_cmds + (i*3); + dp = cp+1; + sp->tx_active--; + + xfer = inw( &dp->xfer_status ); + txbits = inw( &sp->tx_status[i] ); + + if (ncr885e_debug > 4) { + show_dbdma_cmd( cp ); + show_dbdma_cmd( dp ); + } + + /* get xmit result */ + txbits = inw( &sp->tx_status[i] ); + + if (ncr885e_debug > 3) + printk( KERN_INFO "%s: tx xfer=%04x, txbits=%04x\n", dev->name, + xfer, txbits ); + + /* look for any channel status (?) */ + if ( xfer ) { + + dev_kfree_skb( sp->tx_skbufs[i] ); + mark_bh( NET_BH ); + + if ( txbits & TX_STATUS_TXOK ) { + sp->stats.tx_packets++; + sp->stats.tx_bytes += inw( &cp->req_count ); + } + + /* dropped packets */ + if ( txbits & (TX_STATUS_TDLC|TX_STATUS_TDEC) ) { + sp->stats.tx_dropped++; + } + + /* add the collisions */ + sp->stats.collisions += ( txbits & 0x04 ); + + } + + dev->tbusy = 0; + + return; +} + +/* rx interrupt handling */ +static void +ncr885e_rx( struct device *dev ) + +{ + struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv; + volatile struct dbdma_cmd *cp; + struct sk_buff *skb; + int i, nb; + unsigned short status; + unsigned char *data, *stats; + unsigned long rxbits, ioaddr = dev->base_addr; + + i = sp->rx_current; + cp = sp->rx_cmds + (i*2); + + if (ncr885e_debug > 3) + printk( KERN_INFO "%s: ncr885e_rx dirty=%d, current=%d (cp@%p)\n", + dev->name, sp->rx_dirty, sp->rx_current, cp ); + + nb = inw( &cp->req_count ) - inw( &cp->res_count ); + status = inw( &cp->xfer_status ); + + if (ncr885e_debug > 3) + printk( KERN_INFO "%s: (rx %d) bytes=%d, xfer_status=%04x\n", + dev->name, i, nb, status ); + + if ( status ) { + + skb = sp->rx_skbufs[i]; + data = skb->data; + stats = data + nb - 3; + rxbits = (stats[0]|stats[1]<<8|stats[2]<<16); + + if (ncr885e_debug > 3) + printk( KERN_INFO " rx_bits=%06lx\n", rxbits ); + + skb->dev = dev; + skb_put( skb, nb-3 ); + skb->protocol = eth_type_trans( skb, dev ); + netif_rx( skb ); + sp->rx_skbufs[i] = 0; + + if ( rxbits & RX_STATUS_RXOK ) { + sp->stats.rx_packets++; + sp->stats.rx_bytes += nb; + } + + if ( rxbits & RX_STATUS_MCAST ) + sp->stats.multicast++; + + } + + sp->rx_dirty = sp->rx_current; + + if ( ++sp->rx_current >= NR_RX_RING ) + sp->rx_current = 0; + + /* fix up the one we just trashed */ + cp = sp->rx_cmds + (sp->rx_dirty * 2); + + skb = dev_alloc_skb( RX_BUFLEN + 2 ); + if ( skb != 0 ) { + skb_reserve( skb, 2 ); + sp->rx_skbufs[sp->rx_dirty] = skb; + } + + if (ncr885e_debug > 2) + printk( KERN_INFO "%s: ncr885e_rx: using ring index %d, filling cp @ %p\n", + dev->name, sp->rx_current, cp ); + + outw( RX_BUFLEN, &cp->req_count ); + outw( 0, &cp->res_count ); + data = skb->data; + outl( virt_to_bus( data ), &cp->phy_addr ); + outw( 0, &cp->xfer_status ); + + cp = sp->rx_cmds + (sp->rx_current * 2); + + /* restart rx DMA */ + outl( virt_to_bus( cp ), ioaddr + RX_CMD_PTR_LO ); + outl( (RX_DBDMA_ENABLE << 16)|RX_CHANNEL_RUN, + ioaddr + RX_CHANNEL_CONTROL ); + + return; +} + +static void +ncr885e_misc_ints( struct device *dev, unsigned short status ) + +{ + struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv; + struct dbdma_cmd *cp; + unsigned long ioaddr = dev->base_addr; + + if (ncr885e_debug > 1) + printk( KERN_INFO "miscellaneous interrupt handled; status=%02x\n", + status ); + + /* various transmit errors */ + if ( status & + (INTERRUPT_PPET | INTERRUPT_PBFT | INTERRUPT_IIDT) ) { + + /* illegal instruction in tx dma */ + if ( status & INTERRUPT_IIDT ) { + + cp = (struct dbdma_cmd *) bus_to_virt( inl( ioaddr + TX_CMD_PTR_LO )); + printk( KERN_INFO "%s: tx illegal insn:\n", dev->name ); + printk( KERN_INFO " tx DBDMA - cmd = %p, status = %04x\n", + cp, inw( ioaddr + TX_CHANNEL_STATUS )); + printk( KERN_INFO " command = %04x, phy_addr=%08x, req_count=%04x\n", + inw( &cp->command ), inw( &cp->phy_addr ), inw( &cp->req_count )); + } + + if ( status & INTERRUPT_PPET ) + printk( KERN_INFO "%s: tx PCI parity error\n", dev->name ); + + if ( status & INTERRUPT_PBFT ) + printk( KERN_INFO "%s: tx PCI bus fault\n", dev->name ); + } + + /* look for rx errors */ + if ( status & + (INTERRUPT_PPER | INTERRUPT_PBFR | INTERRUPT_IIDR)) { + + /* illegal instruction in rx dma */ + if ( status & INTERRUPT_IIDR ) { +#if 0 + cmd = inl( ioaddr + RX_CMD_PTR_LO ); +#endif + printk( KERN_ERR "%s: rx illegal DMA instruction:\n", dev->name ); + printk( KERN_ERR " channel status=%04x,\n", + inl( ioaddr + RX_CHANNEL_STATUS )); +#if 0 + show_dbdma_cmd( bus_to_virt( inl( ioaddr + RX_CMD_PTR_LO ))); + printk( KERN_ERR " instr (%08x) %08x %08x %08x\n", + (int) cmd, cmd[0], cmd[1], cmd[2] ); +#endif + } + + /* PCI parity error */ + if ( status & INTERRUPT_PPER ) + printk( KERN_INFO "%s: rx PCI parity error\n", dev->name ); + + if ( status & INTERRUPT_PBFR ) + printk( KERN_INFO "%s: rx PCI bus fault\n", dev->name ); + + sp->stats.rx_errors++; + } + + if ( status & INTERRUPT_WI ) { + printk( KERN_INFO "%s: link pulse\n", dev->name ); + } + + /* bump any counters */ + + + return; +} + +static void +ncr885e_interrupt( int irq, void *dev_id, struct pt_regs *regs ) + +{ + struct device *dev = (struct device *) dev_id; + struct ncr885e_private *sp; + unsigned short status; + int ioaddr; + + if ( dev == NULL ) { + printk( KERN_ERR "symba: Interrupt IRQ %d for unknown device\n", irq ); + return; + } + + ioaddr = dev->base_addr; + sp = (struct ncr885e_private *) dev->priv; + spin_lock( &sp->lock ); + + if ( dev->interrupt ) { + printk( KERN_ERR "%s: Re-entering interrupt handler...\n", + dev->name ); + } + + dev->interrupt = 1; + status = inw( ioaddr + INTERRUPT_CLEAR ); + + if (ncr885e_debug > 2) + printk( KERN_INFO "%s: 53C885 interrupt 0x%02x\n", dev->name, status ); + + /* handle non-tx and rx interrupts first */ + if ( status & ~(INTERRUPT_DIT|INTERRUPT_DIR)) + ncr885e_misc_ints( dev, status ); + + /* look for tx interrupt: more to transmit, DBDMA stopped, or tx done */ + if ( ( status & INTERRUPT_DIT ) ) { + + if (ncr885e_debug > 2) + printk( KERN_INFO "%s: tx int; int=%02x, chan stat=%02x\n", + dev->name, status, inw( ioaddr + TX_CHANNEL_STATUS )); + + /* turn off timer */ + del_timer( &sp->tx_timeout ); + sp->timeout_active = 0; + + /* stop DMA */ + outl( TX_DBDMA_ENABLE << 16, ioaddr + TX_CHANNEL_CONTROL ); + + ncr885e_tx( dev ); + } + + if ( status & INTERRUPT_DIR ) { + + if ( ncr885e_debug > 2 ) + printk( KERN_INFO "%s: rx interrupt; int=%02x, rx channel stat=%02x\n", + dev->name, status, inw( ioaddr + RX_CHANNEL_STATUS )); + + /* stop DMA */ + outl( RX_DBDMA_ENABLE << 16, ioaddr + RX_CHANNEL_CONTROL ); + + /* and handle the interrupt */ + ncr885e_rx( dev ); + } + + dev->interrupt = 0; + spin_unlock( &sp->lock ); + + return; +} + + +/* doesn't set the address permanently, however... */ +static int +ncr885e_set_address( struct device *dev, void *addr ) + +{ + struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv; + struct sockaddr *saddr = addr; + unsigned long flags; + unsigned short reg[3]; + unsigned char *ioaddr, *p; + int i; + + memcpy( dev->dev_addr, saddr->sa_data, dev->addr_len ); + + p = (unsigned char *) dev->dev_addr; + printk( KERN_INFO "%s: setting new MAC address - ", dev->name ); +#if 0 + for( p = (unsigned char *) dev->dev_addr, i=0; i < 6; i++, p++ ) + printk("%c%2.2x", i ? ':' : ' ', *p ); +#endif + + + p = (unsigned char *) ® + for( i=0; i < 6; i++ ) + p[i] = dev->dev_addr[i]; + +#if 0 + printk("%s: Setting new mac address - ", dev->name ); + for( i=0; i < 6; i++ ) { + printk("%02x", i ? ':' : ' ', p[i] ); + } + + printk("\n"); +#endif + + /* stop rx for the change */ + outl( RX_DBDMA_ENABLE << 16, ioaddr + RX_CHANNEL_CONTROL ); + + spin_lock_irqsave( &sp->lock, flags ); + + ioaddr = (unsigned char *) dev->base_addr; + + for( i = 0; i < 3; i++ ) { + reg[i] = ((reg[i] & 0xff) << 8) | ((reg[i] >> 8) & 0xff); + printk("%04x ", reg[i] ); + outw( reg[i], ioaddr + STATION_ADDRESS_0 + (i*2)); + } + printk("\n"); + + spin_unlock_irqrestore( &sp->lock, flags ); + + /* restart rx */ + outl((RX_DBDMA_ENABLE << 16)|RX_CHANNEL_RUN, + ioaddr + RX_CHANNEL_CONTROL ); + + return 0; +} + +static void +ncr885e_tx_timeout( unsigned long data ) + +{ + struct device *dev = (struct device *) data; + struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv; + unsigned long flags, ioaddr; + int i; + + save_flags( flags ); + cli(); + + ioaddr = dev->base_addr; + sp->timeout_active = 0; + i = sp->tx_dirty; + + /* if we weren't active, bail... */ + if ( sp->tx_active == 0 ) { + printk( KERN_INFO "%s: ncr885e_timeout...tx not active!\n", dev->name ); + goto out; + } + + printk( KERN_ERR "%s: 53C885 timed out. Resetting...\n", dev->name ); + + /* disable rx and tx DMA */ + outl( (TX_DBDMA_ENABLE << 16), ioaddr + TX_CHANNEL_CONTROL ); + outl( (RX_DBDMA_ENABLE << 16), ioaddr + RX_CHANNEL_CONTROL ); + + /* reset the chip */ + ncr885e_config( dev ); + ncr885e_enable( dev ); + + /* clear the wedged skb in the tx ring */ + sp->tx_active = 0; + ++sp->stats.tx_errors; + + if ( sp->tx_skbufs[i] ) { + dev_kfree_skb( sp->tx_skbufs[i] ); + sp->tx_skbufs[i] = 0; + } + + /* start anew from the beginning of the ring buffer (why not?) */ + sp->tx_current = 0; + dev->tbusy = 0; + mark_bh( NET_BH ); + + /* restart rx dma */ + outl( (RX_DBDMA_ENABLE << 16) | RX_CHANNEL_RUN, + ioaddr + RX_CHANNEL_CONTROL ); + out: + + restore_flags( flags ); +} + +static inline void +ncr885e_set_timeout( struct device *dev ) + +{ + struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv; + unsigned long flags; + + save_flags(flags); + cli(); + + if ( sp->timeout_active ) + del_timer( &sp->tx_timeout ); + + sp->tx_timeout.expires = jiffies + TX_TIMEOUT; + sp->tx_timeout.function = ncr885e_tx_timeout; + sp->tx_timeout.data = (unsigned long) dev; + add_timer( &sp->tx_timeout ); + sp->timeout_active = 1; + restore_flags( flags ); +} + + +/* + * The goal is to set up DBDMA such that the rx ring contains only + * one DMA descriptor per ring element and the tx ring has two (using + * the cool features of branch- and wait-select. However, I'm not sure + * if it's possible. For now, we plod through it with 3 descriptors + * for tx, and two for rx. + */ + +static int +ncr885e_open( struct device *dev ) + +{ + struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv; + unsigned long ioaddr = dev->base_addr; + struct sk_buff *skb; + int i, size; + char *data; + struct dbdma_cmd *cp; + unsigned long flags; + + /* allocate enough space for the tx and rx rings and a STOP descriptor */ + size = (sizeof( struct dbdma_cmd ) * + ((NR_TX_RING * 3) + (NR_RX_RING * 2) + 1)); + + cp = kmalloc( size, GFP_KERNEL ); + + if ( cp == 0 ) { + printk( KERN_ERR "Insufficient memory (%d bytes) for DBDMA\n", size ); + return -ENOMEM; + } + + spin_lock_init( &sp->lock ); + spin_lock_irqsave( &sp->lock, flags ); + + memset((char *) cp, 0, size ); + sp->head = cp; + + sp->stop_cmd = cp; + outl( DBDMA_STOP, &cp->command ); + + sp->rx_cmds = ++cp; + + for( i = 0; i < NR_RX_RING; i++ ) { + + cp = sp->rx_cmds + (i*2); + skb = dev_alloc_skb( RX_BUFLEN + 2 ); + + /* if there is insufficient memory, make this last ring use a + static buffer and leave the loop with that skb as final one */ + if ( skb == 0 ) { + printk( KERN_ERR "%s: insufficient memory for rx ring buffer\n", + dev->name ); + break; + } + + skb_reserve( skb, 2 ); + sp->rx_skbufs[i] = skb; + data = skb->data; + + /* The DMA commands here are done such that an EOP is the only + way that we should get an interrupt. This means that we could + fill more than one skbuff before getting the interrupt at EOP. */ + + /* Handle rx DMA such that it always interrupts.... */ + outw( (INPUT_MORE|INTR_ALWAYS), &cp->command ); + outw( RX_BUFLEN, &cp->req_count ); + outw( 0, &cp->res_count ); + outl( virt_to_bus( data ), &cp->phy_addr ); + outl( virt_to_bus( sp->stop_cmd ), &cp->cmd_dep ); + outw( 0, &cp->xfer_status ); +#if 0 + printk( KERN_INFO "rx at %p\n", cp ); + show_dbdma_cmd( cp ); +#endif + ++cp; + + outw( DBDMA_STOP, &cp->command ); + + } + + /* initialize to all rx buffers are available, fill limit is the end */ + sp->rx_dirty = 0; + sp->rx_current = 0; + + /* fill the tx ring */ + sp->tx_cmds = cp+1; + + for( i = 0; i < NR_TX_RING; i++ ) { + + /* minimal setup for tx command */ + cp = sp->tx_cmds + (i*3); + outw( OUTPUT_LAST, &cp->command ); + if (ncr885e_debug > 3) { + printk( KERN_INFO "tx OUTPUT_LAST at %p\n", cp ); + show_dbdma_cmd( cp ); + } + + /* full setup for the status cmd */ + cp++; + outw( INPUT_LAST|INTR_ALWAYS|WAIT_IFCLR, &cp->command ); + outl( virt_to_bus( &sp->tx_status[i] ), &cp->phy_addr ); + outw( 2, &cp->req_count ); + if ( ncr885e_debug > 3) { + printk( KERN_INFO "tx INPUT_LAST cmd at %p\n", cp ); + show_dbdma_cmd( cp ); + } + + ++cp; + outw( DBDMA_STOP, &cp->command ); + + } +#if 0 + /* chain the last tx DMA command to the STOP cmd */ + outw((INPUT_LAST|INTR_ALWAYS|BR_ALWAYS), &cp->command ); + outl( virt_to_bus( sp->stop_cmd ), &cp->cmd_dep ); +#endif + sp->tx_active = 0; + sp->tx_current = 0; + sp->tx_dirty = 0; + + spin_unlock_irqrestore( &sp->lock, flags ); + + /* the order seems important here for some reason. If the MPIC isn't + enabled before the ethernet chip is enabled, shrapnel from the + bootloader causes us to receive interrupts even though we've not + yet enabled the tx channel. Go figure. It'd be better to configure + the chip in the probe1() routine, but then we don't see interrupts + at all. Everything looks all right on the logic analyzer, but... */ + + ncr885e_config( dev ); + + /* enable ethernet interrupts */ + if ( request_irq( dev->irq, &ncr885e_interrupt, SA_SHIRQ, chipname, dev )) { + printk( KERN_ERR "%s: can't get irq %d\n", dev->name, dev->irq ); + return -EAGAIN; + } + + (void) inw( ioaddr + INTERRUPT_CLEAR ); + + ncr885e_enable( dev ); + + /* start rx DBDMA */ + outl( virt_to_bus( sp->rx_cmds ), ioaddr + RX_CMD_PTR_LO ); + outl( (RX_DBDMA_ENABLE << 16)|RX_CHANNEL_RUN, + ioaddr + RX_CHANNEL_CONTROL ); + + dev->start = 1; + dev->tbusy = 0; + dev->interrupt = 0; + + MOD_INC_USE_COUNT; + + return 0; +} + +static int +ncr885e_xmit_start( struct sk_buff *skb, struct device *dev ) + +{ + struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv; + volatile struct dbdma_cmd *cp, *dp; + unsigned long flags, ioaddr = dev->base_addr; + int len, next, fill, entry; + + if ( ncr885e_debug > 3) + printk( KERN_INFO "%s: xmit_start len=%d, dirty=%d, current=%d, active=%d\n", + dev->name, skb->len, sp->tx_dirty, sp->tx_current, sp->tx_active ); + + spin_lock_irqsave( &sp->lock, flags ); + + /* find the free slot in the ring buffer */ + fill = sp->tx_current; + next = fill + 1; + + if ( next >= NR_TX_RING ) + next = 0; + + /* mark ourselves as busy, even if we have too many packets waiting */ + dev->tbusy = 1; + + /* see if it's necessary to defer this packet */ + if ( sp->tx_active >= MAX_TX_ACTIVE ) { + spin_unlock_irqrestore( &sp->lock, flags ); + return -1; + } + + sp->tx_active++; /* bump "active tx" count */ + sp->tx_current = next; /* and show that we've used this buffer */ + sp->tx_dirty = fill; /* and mark this one to get picked up */ + + len = skb->len; + + if ( len > ETH_FRAME_LEN ) { + printk( KERN_DEBUG "%s: xmit frame too long (%d)\n", dev->name, len ); + len = ETH_FRAME_LEN; + } + + /* get index into the tx DBDMA chain */ + entry = fill * 3; + sp->tx_skbufs[fill] = skb; + cp = sp->tx_cmds + entry; + dp = cp + 1; + + /* update the rest of the OUTPUT_MORE descriptor */ + outw( len, &cp->req_count ); + outl( virt_to_bus( skb->data ), &cp->phy_addr ); + outw( 0, &cp->xfer_status ); + outw( 0, &cp->res_count ); + + /* and finish off the INPUT_MORE */ + outw( 0, &dp->xfer_status ); + outw( 0, &dp->res_count ); + sp->tx_status[fill] = 0; + outl( virt_to_bus( &sp->tx_status[fill] ), &dp->phy_addr ); + + if ( ncr885e_debug > 2 ) + printk(KERN_INFO "%s: xmit_start: active %d, tx_current %d, tx_dirty %d\n", + dev->name, sp->tx_active, sp->tx_current, sp->tx_dirty ); + + if ( ncr885e_debug > 4 ) { + show_dbdma_cmd( cp ); + show_dbdma_cmd( dp ); + } + + + /* restart the tx DMA engine */ + outl( virt_to_bus( cp ), ioaddr + TX_CMD_PTR_LO ); + outl( (TX_DBDMA_ENABLE << 16)|TX_CHANNEL_RUN, + ioaddr + TX_CHANNEL_CONTROL ); + + ncr885e_set_timeout( dev ); + + spin_unlock_irqrestore( &sp->lock, flags ); + dev->trans_start = jiffies; + + return 0; +} + +static int +ncr885e_close(struct device *dev) + +{ + int i; + struct ncr885e_private *np = (struct ncr885e_private *) dev->priv; + unsigned long ioaddr = dev->base_addr; + + dev->start = 0; + dev->tbusy = 1; + + spin_lock( &np->lock ); + + printk(KERN_INFO "%s: NCR885E Ethernet closing...\n", dev->name ); + + if (ncr885e_debug > 1) + printk(KERN_DEBUG "%s: Shutting down Ethernet chip\n", dev->name); + + ncr885e_disable(dev); + + del_timer(&np->tx_timeout); + + /* flip off rx and tx */ + outl( (RX_DBDMA_ENABLE << 16), ioaddr + RX_CHANNEL_CONTROL ); + outl( (TX_DBDMA_ENABLE << 16), ioaddr + TX_CHANNEL_CONTROL ); + + /* free up the IRQ */ + free_irq( dev->irq, dev ); + + for( i = 0; i < NR_RX_RING; i++ ) { + if (np->rx_skbufs[i]) + dev_kfree_skb( np->rx_skbufs[i] ); + np->rx_skbufs[i] = 0; + } +#if 0 + for (i = 0; i < NR_TX_RING; i++) { + if (np->tx_skbufs[i]) + dev_kfree_skb(np->tx_skbufs[i]); + np->tx_skbufs[i] = 0; + } +#endif + spin_unlock( &np->lock ); + + kfree( np->head ); + + MOD_DEC_USE_COUNT; + + return 0; +} + + +/* + * multicast promiscuous mode isn't used here. Allow code in the + * IP stack to determine which multicast packets are good or bad.... + * (this avoids having to use the hash table registers) + */ +static void +ncr885e_set_multicast( struct device *dev ) + +{ + int ioaddr = dev->base_addr; + + if ( ncr885e_debug > 3 ) + printk("%s: set_multicast: dev->flags = %x, AF=%04x\n", + dev->name, dev->flags, inw( ioaddr + ADDRESS_FILTER )); + + if ( dev->flags & IFF_PROMISC ) { + printk( KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name ); + outw( ADDRESS_RPPRO, ioaddr + ADDRESS_FILTER ); + } + + /* accept all multicast packets without checking the mc_list. */ + else if ( dev->flags & IFF_ALLMULTI ) { + printk( KERN_INFO "%s: Enabling all multicast packets.\n", + dev->name ); + outw( ADDRESS_RPPRM, ioaddr + ADDRESS_FILTER ); + } + + /* enable broadcast rx */ + else { + outw( ADDRESS_RPABC, ioaddr + ADDRESS_FILTER ); + } +} + +static struct net_device_stats * +ncr885e_stats( struct device *dev ) + +{ + struct ncr885e_private *np = (struct ncr885e_private *) dev->priv; + + return &np->stats; +} + +/* By this function, we're certain that we have a 885 Ethernet controller + * so we finish setting it up and wrap up all the required Linux ethernet + * configuration. + */ + +static int +ncr885e_probe1( struct device *dev, unsigned long ioaddr, unsigned char irq ) + +{ + struct ncr885e_private *sp; + unsigned short station_addr[3], val; + unsigned char *p; + int i; + + dev = init_etherdev( dev, 0 ); + + /* construct private data for the 885 ethernet */ + dev->priv = kmalloc( sizeof( struct ncr885e_private ), GFP_KERNEL ); + + if ( dev->priv == NULL ) + return -ENOMEM; + + sp = (struct ncr885e_private *) dev->priv; + memset( sp, 0, sizeof( struct ncr885e_private )); + + /* snag the station address and display it */ + for( i = 0; i < 3; i++ ) { + val = inw( ioaddr + STATION_ADDRESS_0 + (i*2)); + station_addr[i] = ((val >> 8) & 0xff) | ((val << 8) & 0xff00); + } + + printk( KERN_INFO "%s: %s at %08lx,", dev->name, chipname, ioaddr ); + + p = (unsigned char *) &station_addr; + + for( i=0; i < 6; i++ ) { + dev->dev_addr[i] = *p; + printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i] ); + p++; + } + + printk(", IRQ %d.\n", irq ); + + request_region( ioaddr, NCR885E_TOTAL_SIZE, dev->name ); + + /* set up a timer */ + init_timer( &sp->tx_timeout ); + sp->timeout_active = 0; + + dev->base_addr = ioaddr; + dev->irq = irq; + + ether_setup( dev ); + + /* everything else */ + dev->open = ncr885e_open; + dev->stop = ncr885e_close; + dev->get_stats = ncr885e_stats; + dev->hard_start_xmit = ncr885e_xmit_start; + dev->set_multicast_list = ncr885e_set_multicast; + dev->set_mac_address = ncr885e_set_address; + + return 0; +} + +/* Since the NCR 53C885 is a multi-function chip, I'm not worrying about + * trying to get the the device(s) in slot order. For our (Synergy's) + * purpose, there's just a single 53C885 on the board and we don't + * worry about the rest. + */ + +int __init ncr885e_probe( struct device *dev ) +{ + struct pci_dev *pdev = NULL; + unsigned int ioaddr, chips = 0; + unsigned short cmd; + unsigned char irq, latency; + + while(( pdev = pci_find_device( PCI_VENDOR_ID_NCR, + PCI_DEVICE_ID_NCR_53C885_ETHERNET, + pdev )) != NULL ) { + + if ( !print_version ) { + print_version++; + printk( KERN_INFO "%s", version ); + } + + /* Use I/O space */ + pci_read_config_dword( pdev, PCI_BASE_ADDRESS_0, &ioaddr ); + pci_read_config_byte( pdev, PCI_INTERRUPT_LINE, &irq ); + + ioaddr &= ~3; + /* Adjust around the Grackle... */ +#ifdef CONFIG_GEMINI + ioaddr |= 0xfe000000; +#endif + + if ( check_region( ioaddr, NCR885E_TOTAL_SIZE )) + continue; + + /* finish off the probe */ + if ( !(ncr885e_probe1( dev, ioaddr, irq ))) { + + chips++; + + /* Access is via I/O space, bus master enabled... */ + pci_read_config_word( pdev, PCI_COMMAND, &cmd ); + + if ( !(cmd & PCI_COMMAND_MASTER) ) { + printk( KERN_INFO " PCI master bit not set! Now setting.\n"); + cmd |= PCI_COMMAND_MASTER; + pci_write_config_word( pdev, PCI_COMMAND, cmd ); + } + + if ( !(cmd & PCI_COMMAND_IO) ) { + printk( KERN_INFO " Enabling I/O space.\n" ); + cmd |= PCI_COMMAND_IO; + pci_write_config_word( pdev, PCI_COMMAND, cmd ); + } + + pci_read_config_byte( pdev, PCI_LATENCY_TIMER, &latency ); + + if ( latency < 10 ) { + printk( KERN_INFO " PCI latency timer (CFLT) is unreasonably" + " low at %d. Setting to 255.\n", latency ); + pci_write_config_byte( pdev, PCI_LATENCY_TIMER, 255 ); + } + } + } + + if ( !chips ) + return -ENODEV; + else + return 0; +} + +/* debugging to peek at dma descriptors */ +static void +show_dbdma_cmd( volatile struct dbdma_cmd *cmd ) + +{ + printk( KERN_INFO " cmd %04x, physaddr %08x, req_count %04x\n", + inw( &cmd->command ), inl( &cmd->phy_addr ), inw( &cmd->req_count )); + printk( KERN_INFO " res_count %04x, xfer_status %04x, branch %08x\n", + inw( &cmd->res_count ), inw( &cmd->xfer_status ),inl( &cmd->cmd_dep )); +} + +#if 0 +static int +read_eeprom( unsigned int ioaddr, int location ) + +{ + int loop; + unsigned char val; + + outb( (location & 0xff), ioaddr + EE_WORD_ADDR ); + + /* take spillover from location in control reg */ + outb(EE_CONTROL_RND_READB | (location & (0x7<<8)), ioaddr + EE_CONTROL); + + loop = 1000; + while( (inb( ioaddr + EE_STATUS) & EE_SEB) && + (loop > 0) ) { + udelay( 10 ); + loop--; + } + + if ( inb( ioaddr + EE_STATUS ) & EE_SEE ) { + printk("%s: Serial EEPROM read error\n", chipname); + val = 0xff; + } + + else + val = inb( ioaddr + EE_READ_DATA ); + + return (int) val; +} +#endif + +#ifdef NCR885E_DEBUG_MII +static void +show_mii( unsigned long ioaddr ) + +{ + int phyctrl, phystat, phyadvert, phypartner, phyexpan; + + phyctrl = read_mii( ioaddr, MII_AUTO_NEGOTIATION_CONTROL ); + phystat = read_mii( ioaddr, MII_AUTO_NEGOTIATION_STATUS ); + phyadvert = read_mii( ioaddr, MII_AUTO_NEGOTIATION_ADVERTISEMENT ); + phypartner = read_mii( ioaddr, MII_AUTO_NEGOTIATION_LINK_PARTNER ); + phyexpan = read_mii( ioaddr, MII_AUTO_NEGOTIATION_EXPANSION ); + + printk( KERN_INFO "PHY: advert=%d %s, partner=%s %s, link=%d, %s%s\n", + (phyadvert & MANATECH_100BASETX_FULL_DUPLEX ? 100 : 10), + (phyctrl & MANC_AUTO_NEGOTIATION_ENABLE ? "auto" : "fixed"), + (phypartner & MANLP_ACKNOWLEDGE ? + (phypartner & MANATECH_100BASETX_FULL_DUPLEX ? "100" : "10") : + "?"), + (phyexpan & MANE_LINK_PARTNER_AUTO_ABLE ? "auto" : "fixed"), + (phyctrl & MANC_PHY_SPEED_100 ? 100 : 10), + (phystat & MANS_LINK_STATUS ? "up" : "down"), + (phyexpan & MANE_PARALLEL_DETECTION_FAULT ? " PD-fault" : "" )); + return; +} + + +static int +read_mii( unsigned long ioaddr, int reg ) + +{ + int timeout; + + + timeout = 100000; + + while( inw( ioaddr + MII_INDICATOR ) & MII_BUSY ) { + + if ( timeout-- < 0 ) { + printk( KERN_INFO "Timed out waiting for MII\n" ); + return -1; + } + } + + outw( (1<<8) + reg, ioaddr + MII_ADDRESS ); + outw( MIIM_RSTAT, ioaddr + MIIM_COMMAND ); + + timeout = 100000; + while( inw( ioaddr + MII_INDICATOR ) & MII_BUSY ) { + if ( timeout-- < 0 ) { + printk( KERN_INFO "Timed out waiting for MII\n" ); + return -1; + } + } + + return( inw( ioaddr + MII_READ_DATA )); +} + +static void +write_mii( unsigned long ioaddr, int reg, int data ) + +{ + int timeout=100000; + + printk( KERN_INFO "MII indicator: %02x\n", inw( ioaddr + MII_INDICATOR )); + + while( inw( ioaddr + MII_INDICATOR ) & MII_BUSY ) { + if ( timeout-- <= 0 ) { + printk( KERN_INFO "Timeout waiting to write to MII\n" ); + return; + } + udelay( 10 ); + } + + outw( (1<<8) + reg, ioaddr + MII_ADDRESS ); + outw( data, ioaddr + MII_WRITE_DATA ); + + return; +} + +#endif /* NCR885E_DEBUG_MII */ + +#ifdef MODULE +#if defined(LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x20118 +MODULE_AUTHOR("dan@synergymicro.com"); +MODULE_DESCRIPTION("Symbios 53C885 Ethernet driver"); +MODULE_PARM(debug, "i"); +#endif + +static int debug = 1; + +int +init_module(void) +{ + if ( debug >= 0) + ncr885e_debug = debug; + + return ncr885e_probe( NULL ); +} + +void +cleanup_module(void) +{ + struct ncr885e_private *np; + + if ( root_dev ) { + + unregister_netdev( root_dev ); + np = (struct ncr885e_private *) root_dev->priv; + release_region( root_dev->base_addr, NCR885E_TOTAL_SIZE ); + kfree( root_dev->priv ); + root_dev = NULL; + } +} +#endif /* MODULE */ + +/* + * Local variables: + * compile-command: "gcc -DMODULE -DMODVERSIONS -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O6 -c symba.c" + * End: + */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/ncr885e.h linux/drivers/net/ncr885e.h --- v2.2.13/linux/drivers/net/ncr885e.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/ncr885e.h Tue Jan 4 10:12:17 2000 @@ -0,0 +1,367 @@ +#ifndef _NET_H_SYMBA +#define _NET_H_SYMBA + +/* transmit status bit definitions */ +#define TX_STATUS_TXOK (1<<13) /* success */ +#define TX_STATUS_TDLC (1<<12) /* dropped for late colls */ +#define TX_STATUS_TCXSDFR (1<<11) /* excessive deferral */ +#define TX_STATUS_TDEC (1<<10) /* excessive collisions */ +#define TX_STATUS_TAUR (1<<9) /* abort on underrun/"jumbo" */ +#define TX_STATUS_PDFRD (1<<8) /* packet deferred */ +#define TX_STATUS_BCAST (1<<7) /* broadcast ok */ +#define TX_STATUS_MCAST (1<<6) /* multicast ok */ +#define TX_STATUS_CRCERR (1<<5) /* CRC error */ +#define TX_STATUS_LC (1<<4) /* late collision */ +#define TX_STATUS_CCNT_MASK 0xf /* collision count */ + +#define T_TXOK (1<<13) +#define T_TDLC (1<<12) +#define T_TCXSDFR (1<<11) +#define T_TDEC (1<<10) +#define T_TAUR (1<<9) +#define T_PDFRD (1<<8) +#define T_BCAST (1<<7) +#define T_MCAST (1<<6) +#define T_LC (1<<4) +#define T_CCNT_MASK 0xf + +/* receive status bit definitions */ +#define RX_STATUS_RXOVRN (1<<23) /* overrun */ +#define RX_STATUS_CEPS (1<<22) /* carrier event already seen */ +#define RX_STATUS_RXOK (1<<21) /* success */ +#define RX_STATUS_BCAST (1<<20) /* broadcast ok */ +#define RX_STATUS_MCAST (1<<19) /* multicast ok */ +#define RX_STATUS_CRCERR (1<<18) /* CRC error */ +#define RX_STATUS_DR (1<<17) /* dribble nibble */ +#define RX_STATUS_RCV (1<<16) /* rx code violation */ +#define RX_STATUS_PTL (1<<15) /* pkt > 1518 bytes */ +#define RX_STATUS_PTS (1<<14) /* pkt < 64 bytes */ +#define RX_STATUS_LEN_MASK 0x1fff /* length mask */ + +#define EEPROM_LENGTH 100 + + +/* Serial EEPROM interface */ +#define EE_STATUS 0xf0 +#define EE_CONTROL 0xf1 +#define EE_WORD_ADDR 0xf2 +#define EE_READ_DATA 0xf3 +#define EE_WRITE_DATA 0xf4 +#define EE_FEATURE_ENB 0xf5 + +/* Use on EE_STATUS */ +#define EE_SEB (1<<8) +#define EE_SEE 1 + +/* Serial EEPROM commands */ +#define EE_CONTROL_SEQ_READB (1<<4) +#define EE_CONTROL_RND_WRITEB (1<<5) +#define EE_CONTROL_RND_READB ((1<<4)|(1<<5)) + +/* Enable writing to serial EEPROM */ +#define EE_WRITE_ENB 1 + +/* The 885 configuration register */ +#define MAC_CONFIG 0xa0 +#define MAC_CONFIG_SRST 1<<15 +#define MAC_CONFIG_ITXA 1<<13 +#define MAC_CONFIG_RXEN 1<<12 +#define MAC_CONFIG_INTLB 1<<10 +#define MAC_CONFIG_MODE_MASK (1<<8|1<<9) +#define MAC_CONFIG_MODE_TP 1<<8 +#define MAC_CONFIG_HUGEN 1<<5 +#define MAC_CONFIG_RETRYL 1<<4 +#define MAC_CONFIG_CRCEN 1<<3 +#define MAC_CONFIG_PADEN 1<<2 +#define MAC_CONFIG_FULLD 1<<1 +#define MAC_CONFIG_NOCFR 1<<0 + + + + + +#define TX_WAIT_SELECT 0x18 +#define RX_CHANNEL_CONTROL 0x40 + +/* Tx channel status */ +#define TX_DBDMA_REG 0x00 +#define TX_CHANNEL_CONTROL 0x00 +#define TX_CHANNEL_STATUS 0x04 +#define TX_STATUS_RUN 1<<15 +#define TX_STATUS_PAUSE 1<<14 +#define TX_STATUS_WAKE 1<<12 +#define TX_STATUS_DEAD 1<<11 +#define TX_STATUS_ACTIVE 1<<10 +#define TX_STATUS_BT 1<<8 +#define TX_STATUS_TXABORT 1<<7 +#define TX_STATUS_TXSR 1<<6 + +#define TX_CHANNEL_RUN TX_STATUS_RUN +#define TX_CHANNEL_PAUSE TX_STATUS_PAUSE +#define TX_CHANNEL_WAKE TX_STATUS_WAKE +#define TX_CHANNEL_DEAD TX_STATUS_DEAD +#define TX_CHANNEL_ACTIVE TX_STATUS_ACTIVE +#define TX_CHANNEL_BT TX_STATUS_BT +#define TX_CHANNEL_TXABORT TX_STATUS_TXABORT +#define TX_CHANNEL_TXSR TX_STATUS_TXSR + +#define TX_DBDMA_ENABLE (TX_CHANNEL_WAKE | TX_CHANNEL_PAUSE | \ + TX_CHANNEL_RUN ) + +/* Transmit command ptr lo register */ +#define TX_CMD_PTR_LO 0x0c + +/* Transmit interrupt select register */ +#define TX_INT_SELECT 0x10 + +/* Transmit branch select register */ +#define TX_BRANCH_SELECT 0x14 + +/* Transmit wait select register */ +#define TX_WAIT_SELECT 0x18 +#define TX_WAIT_STAT_RECV 0x40 + +/* Rx channel status */ +#define RX_DBDMA_REG 0x40 +#define RX_CHANNEL_CONTROL 0x40 +#define RX_CHANNEL_STATUS 0x44 +#define RX_STATUS_RUN 1<<15 +#define RX_STATUS_PAUSE 1<<14 +#define RX_STATUS_WAKE 1<<12 +#define RX_STATUS_DEAD 1<<11 +#define RX_STATUS_ACTIVE 1<<10 +#define RX_STATUS_BT 1<<8 +#define RX_STATUS_EOP 1<<6 + +#define RX_CHANNEL_RUN RX_STATUS_RUN +#define RX_CHANNEL_PAUSE RX_STATUS_PAUSE +#define RX_CHANNEL_WAKE RX_STATUS_WAKE +#define RX_CHANNEL_DEAD RX_STATUS_DEAD +#define RX_CHANNEL_ACTIVE RX_STATUS_ACTIVE +#define RX_CHANNEL_BT RX_STATUS_BT +#define RX_CHANNEL_EOP RX_STATUS_EOP + +#define RX_DBDMA_ENABLE (RX_CHANNEL_WAKE | RX_CHANNEL_PAUSE | \ + RX_CHANNEL_RUN) + +/* Receive command ptr lo */ +#define RX_CMD_PTR_LO 0x4c + +/* Receive interrupt select register */ +#define RX_INT_SELECT 0x50 +#define RX_INT_SELECT_EOP 0x40 + +/* Receive branch select */ +#define RX_BRANCH_SELECT 0x54 +#define RX_BRANCH_SELECT_EOP 0x40 + +/* Receive wait select */ +#define RX_WAIT_SELECT 0x58 +#define RX_WAIT_SELECT_EOP 0x40 + +/* Event status register */ +#define EVENT_STATUS 0x80 +#define EVENT_TXSR 1<<2 +#define EVENT_EOP 1<<1 +#define EVENT_TXABORT 1<<0 + +/* Interrupt enable register */ +#define INTERRUPT_ENABLE 0x82 + +/* Interrupt clear register */ +#define INTERRUPT_CLEAR 0x84 + +/* Interrupt status register */ +#define INTERRUPT_STATUS_REG 0x86 + +/* bits for the above three interrupt registers */ +#define INTERRUPT_INTE 1<<15 /* interrupt enable */ +#define INTERRUPT_WI 1<<9 /* wakeup interrupt */ +#define INTERRUPT_ERI 1<<8 /* early recieve interrupt */ +#define INTERRUPT_PPET 1<<7 /* PCI Tx parity error */ +#define INTERRUPT_PBFT 1<<6 /* PCI Tx bus fault */ +#define INTERRUPT_IIDT 1<<5 /* illegal instruction Tx */ +#define INTERRUPT_DIT 1<<4 /* DBDMA Tx interrupt */ +#define INTERRUPT_PPER 1<<3 /* PCI Rx parity error */ +#define INTERRUPT_PBFR 1<<2 /* PCI Rx bus fault */ +#define INTERRUPT_IIDR 1<<1 /* illegal instruction Rx */ +#define INTERRUPT_DIR 1<<0 /* DBDMA Rx interrupt */ + +#define INTERRUPT_TX_MASK (INTERRUPT_PBFT|INTERRUPT_IIDT| \ + INTERRUPT_PPET|INTERRUPT_DIT) +#define INTERRUPT_RX_MASK (INTERRUPT_PBFR|INTERRUPT_IIDR| \ + INTERRUPT_PPER|INTERRUPT_DIR) + +/* chip revision register */ +#define CHIP_REVISION_REG 0x8c +#define CHIP_PCIREV_MASK (0xf<<16) +#define CHIP_PCIDEV_MASK 0xff + +/* Tx threshold register */ +#define TX_THRESHOLD 0x94 + +/* General purpose register */ +#define GEN_PURPOSE_REG 0x9e + +/* General purpose pin control reg */ +#define GEN_PIN_CONTROL_REG 0x9f + +/* DBDMA control register */ +#define DBDMA_CONTROL 0x90 +#define DBDMA_SRST 1<<31 +#define DBDMA_TDPCE 1<<23 +#define DBDMA_BE 1<<22 +#define DBDMA_TAP_MASK (1<<19|1<<20|1<<21) +#define DBDMA_RAP_MASK (1<<16|1<<17|1<<18) +#define DBDMA_DPMRLE 1<<15 +#define DBDMA_WIE 1<<14 +#define DBDMA_MP 1<<13 +#define DBDMA_SME 1<<12 +#define DBDMA_CME 1<<11 +#define DBDMA_DDPE 1<<10 +#define DBDMA_TDPE 1<<9 +#define DBDMA_EXTE 1<<8 +#define DBDMA_BST_MASK (1<<4|1<<5|1<<6) +#define DBDMA_BSR_MASK (1<<0|1<<1|1<<2) + +#define DBDMA_BURST_1 (0x00) +#define DBDMA_BURST_2 (0x01) +#define DBDMA_BURST_4 (0x02) +#define DBDMA_BURST_8 (0x03) +#define DBDMA_BURST_16 (0x04) +#define DBDMA_BURST_32 (0x05) +#define DBDMA_BURST_64 (0x06) +#define DBDMA_BURST_128 (0x07) + +#define DBDMA_TX_BST_SHIFT (4) +#define DBDMA_RX_BST_SHIFT (0) + +#define DBDMA_TX_ARBITRATION_DEFAULT ( 1 << 19 ) +#define DBDMA_RX_ARBITRATION_DEFAULT ( 2 << 16 ) + + +/* Back-to-back interpacket gap register */ +#define BTOB_INTP_GAP 0xa2 +#define BTOB_INTP_DEFAULT 0x18 + +/* Non-back-to-back interpacket gap register */ +#define NBTOB_INTP_GAP 0xa4 + +/* MIIM command register */ +#define MIIM_COMMAND 0xa6 +#define MIIM_SCAN 1<<1 +#define MIIM_RSTAT 1<<0 + +/* MII address register */ +#define MII_ADDRESS 0xa8 +#define MII_FIAD_MASK (1<<8|1<<9|1<<10|1<<11|1<<12) +#define MII_RGAD_MASK (1<<0|1<<1|1<<2|1<<3|1<<4) + +#define TPPMD_CONTROL_REG 0xa8 +#define TPPMD_FO 1<<1 +#define TPPMD_LB 1<<0 + +/* MII read and write registers */ +#define MII_WRITE_DATA 0xaa +#define MII_READ_DATA 0xac + +/* MII indicators */ +#define MII_INDICATOR 0xae +#define MII_NVALID 1<<2 +#define MII_SCAN 1<<1 +#define MII_BUSY 1<<0 + +/* Address filter */ +#define ADDRESS_FILTER 0xd0 +#define ADDRESS_RPPRM 1<<3 /* multicast promis. mode */ +#define ADDRESS_RPPRO 1<<2 /* promiscuous mode */ +#define ADDRESS_RPAMC 1<<1 /* accept multicasts */ +#define ADDRESS_RPABC 1<<0 /* accept broadcasts */ + +/* Station addresses + + Note that if the serial EEPROM is disabled, these values are all + zero. If, like us, you get the chips when they're fresh, they're + also zero and you have to initialize the address */ +#define STATION_ADDRESS_0 0xd2 +#define STATION_ADDRESS_1 0xd4 +#define STATION_ADDRESS_2 0xd6 + +/* Hash tables */ +#define HASH_TABLE_0 0xd8 +#define HASH_TABLE_1 0xda +#define HASH_TABLE_2 0xdc +#define HASH_TABLE_3 0xde + +/* PHY indentifiers */ +#define PHY_IDENTIFIER_0 0xe4 +#define PHY_IDENTIFIER_1 0xe6 + +/* MII Auto-negotiation register definitions */ + +#define MII_AUTO_NEGOTIATION_CONTROL (0x0000) +#define MANC_PHY_RESET (0x8000) +#define MANC_PHY_LOOPBACK_ENABLE (0x4000) +#define MANC_PHY_LOOPBACK_DISABLE (0x0000) +#define MANC_PHY_SPEED_100 (0x2000) +#define MANC_PHY_SPEED_10 (0x0000) +#define MANC_AUTO_NEGOTIATION_ENABLE (0x1000) +#define MANC_AUTO_NEGOTIATION_DISABLE (0x0000) +#define MANC_PHY_POWER_DOWN (0x0800) +#define MANC_PHY_POWER_UP (0x0000) +#define MANC_ISOLATE_ENABLE (0x0400) +#define MANC_ISOLATE_DISABLE (0x0000) +#define MANC_RESTART_AUTO_NEGOTIATION (0x0200) +#define MANC_FULL_DUPLEX (0x0100) +#define MANC_HALF_DUPLEX (0x0000) + +#define MII_AUTO_NEGOTIATION_STATUS (0x0001) +#define MANS_100BASE_T4_HALF_DUPLEX (0x8000) +#define MANS_100BASE_X_FULL_DUPLEX (0x4000) +#define MANS_100BASE_X_HALF_DUPLEX (0x2000) +#define MANS_10MBS_FULL_DUPLEX (0x1000) +#define MANS_10MBS_HALF_DUPLEX (0x0800) +#define MANS_AUTO_NEGOTIATION_COMPLETE (0x0020) +#define MANS_REMOTE_FAULT (0x0010) +#define MANS_AUTO_NEGOTIATION_ABILITY (0x0008) +#define MANS_LINK_STATUS (0x0004) +#define MANS_JABBER_DETECT (0x0002) +#define MANS_EXTENDED_CAPABILITY (0x0001) + +#define MII_PHY_IDENTIFIER_1 (0x0002) +#define MII_PHY_IDENTIFIER_2 (0x0003) + +#define MII_AUTO_NEGOTIATION_ADVERTISEMENT (0x0004) +#define MANA_NEXT_PAGE (0x8000) +#define MANA_REMOTE_FAULT (0x2000) +#define MANA_TECHNOLOGY_ABILITY_MASK (0x1FE0) +#define MANATECH_10BASET_HALF_DUPLEX (0x0020) +#define MANATECH_10BASET_FULL_DUPLEX (0x0040) +#define MANATECH_100BASETX_HALF_DUPLEX (0x0080) +#define MANATECH_100BASETX_FULL_DUPLEX (0x0100) +#define MANATECH_100BASET4 (0x0200) +#define MANA_SELECTOR_MASK (0x001F) +#define MANASELECTOR_802_3 (0x0001) + +#define MII_AUTO_NEGOTIATION_LINK_PARTNER (0x0005) +#define MANLP_NEXT_PAGE (0x8000) +#define MANLP_ACKNOWLEDGE (0x4000) +#define MANLP_REMOTE_FAULT (0x2000) +#define MANLP_TECHNOLOGY_ABILITY_MASK (0x1FE0) +#define MANLP_SELECTOR_MASK (0x001F) + +#define MII_AUTO_NEGOTIATION_EXPANSION (0x0006) +#define MANE_PARALLEL_DETECTION_FAULT (0x0010) +#define MANE_LINK_PARTNER_NEXT_PAGE_ABLE (0x0008) +#define MANE_NEXT_PAGE_ABLE (0x0004) +#define MANE_PAGE_RECEIVED (0x0002) +#define MANE_LINK_PARTNER_AUTO_ABLE (0x0001) + +#define MII_AUTO_NEGOTIATION_NEXT_PAGE_TRANSMIT (0x0007) +#define MANNPT_NEXT_PAGE (0x8000) +#define MANNPT_MESSAGE_PAGE (0x2000) +#define MANNPT_ACKNOWLEDGE_2 (0x1000) +#define MANNPT_TOGGLE (0x0800) +#define MANNPT_MESSAGE_FIELD_MASK (0x07FF) + +#endif diff -u --recursive --new-file v2.2.13/linux/drivers/net/old_tulip.c linux/drivers/net/old_tulip.c --- v2.2.13/linux/drivers/net/old_tulip.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/old_tulip.c Tue Jan 4 10:12:17 2000 @@ -0,0 +1,2853 @@ +/* tulip.c: A DEC 21040-family ethernet driver for Linux. */ +/* + Written 1994-1998 by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU Public License, incorporated herein by reference. + + This driver is for the Digital "Tulip" ethernet adapter interface. + It should work with most DEC 21*4*-based chips/ethercards, as well as + PNIC and MXIC chips. + + The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O + Center of Excellence in Space Data and Information Sciences + Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + + Support and updates available at + http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html +*/ + +#define SMP_CHECK +static const char version[] = "tulip.c:v0.89H 5/23/98 becker@cesdis.gsfc.nasa.gov\n"; + +/* A few user-configurable values. */ + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 25; + +#define MAX_UNITS 8 +/* Used to pass the full-duplex flag, etc. */ +static int full_duplex[MAX_UNITS] = {0, }; +static int options[MAX_UNITS] = {0, }; +static int mtu[MAX_UNITS] = {0, }; /* Jumbo MTU for interfaces. */ + +/* The possible media types that can be set in options[] are: */ +static const char * const medianame[] = { + "10baseT", "10base2", "AUI", "100baseTx", + "10baseT-FD", "100baseTx-FD", "100baseT4", "100baseFx", + "100baseFx-FD", "MII 10baseT", "MII 10baseT-FD", "MII", + "10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FD", "MII 100baseT4", +}; + +/* Set if the PCI BIOS detects the chips on a multiport board backwards. */ +#ifdef REVERSE_PROBE_ORDER +static int reverse_probe = 1; +#else +static int reverse_probe = 0; +#endif + +/* Keep the ring sizes a power of two for efficiency. + Making the Tx ring too large decreases the effectiveness of channel + bonding and packet priority. + There are no ill effects from too-large receive rings. */ +#define TX_RING_SIZE 16 +#define RX_RING_SIZE 32 + +/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ +#ifdef __alpha__ +static int rx_copybreak = 1518; +#else +static int rx_copybreak = 100; +#endif + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (4*HZ) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* Processor type for cache alignment. */ +#include +#include +#include + +#include +#include +#include + +/* Kernel compatibility defines, common to David Hind's PCMCIA package. + This is only in the support-all-kernels source code. */ +#include /* Evil, but neccessary */ + +#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10300 +#define RUN_AT(x) (x) /* What to put in timer->expires. */ +#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC) +#define virt_to_bus(addr) ((unsigned long)addr) +#define bus_to_virt(addr) ((void*)addr) + +#else /* 1.3.0 and later */ +#define RUN_AT(x) (jiffies + (x)) +#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2) +#endif + +#if (LINUX_VERSION_CODE >= 0x10344) +#define NEW_MULTICAST +#include +#endif +#ifdef SA_SHIRQ +#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs) +#else +#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs) +#endif + +#if (LINUX_VERSION_CODE < 0x20123) +#define hard_smp_processor_id() smp_processor_id() +#define test_and_set_bit(val, addr) set_bit(val, addr) +#endif + +/* This my implementation of shared IRQs, now only used for 1.2.13. */ +#ifdef HAVE_SHARED_IRQ +#define USE_SHARED_IRQ +#include +#endif + +/* The total size is unusually large: The 21040 aligns each of its 16 + longword-wide registers on a quadword boundary. */ +#define TULIP_TOTAL_SIZE 0x80 + +#ifdef HAVE_DEVLIST +struct netdev_entry tulip_drv = +{"Tulip", tulip_pci_probe, TULIP_TOTAL_SIZE, NULL}; +#endif + +#ifdef TULIP_DEBUG +int tulip_debug = TULIP_DEBUG; +#else +int tulip_debug = 1; +#endif + +/* + Theory of Operation + +I. Board Compatibility + +This device driver is designed for the DECchip "Tulip", Digital's +single-chip ethernet controllers for PCI. Supported members of the family +are the 21040, 21041, 21140, 21140A, 21142, and 21143. These chips are used on +many PCI boards including the SMC EtherPower series. + + +II. Board-specific settings + +PCI bus devices are configured by the system at boot time, so no jumpers +need to be set on the board. The system BIOS preferably should assign the +PCI INTA signal to an otherwise unused system IRQ line. +Note: Kernel versions earlier than 1.3.73 do not support shared PCI +interrupt lines. + +III. Driver operation + +IIIa. Ring buffers + +The Tulip can use either ring buffers or lists of Tx and Rx descriptors. +This driver uses statically allocated rings of Rx and Tx descriptors, set at +compile time by RX/TX_RING_SIZE. This version of the driver allocates skbuffs +for the Rx ring buffers at open() time and passes the skb->data field to the +Tulip as receive data buffers. When an incoming frame is less than +RX_COPYBREAK bytes long, a fresh skbuff is allocated and the frame is +copied to the new skbuff. When the incoming frame is larger, the skbuff is +passed directly up the protocol stack and replaced by a newly allocated +skbuff. + +The RX_COPYBREAK value is chosen to trade-off the memory wasted by +using a full-sized skbuff for small frames vs. the copying costs of larger +frames. For small frames the copying cost is negligible (esp. considering +that we are pre-loading the cache with immediately useful header +information). For large frames the copying cost is non-trivial, and the +larger copy might flush the cache of useful data. A subtle aspect of this +choice is that the Tulip only receives into longword aligned buffers, thus +the IP header at offset 14 isn't longword aligned for further processing. +Copied frames are put into the new skbuff at an offset of "+2", thus copying +has the beneficial effect of aligning the IP header and preloading the +cache. + +IIIC. Synchronization +The driver runs as two independent, single-threaded flows of control. One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag. The other thread is the interrupt handler, which is single +threaded by the hardware and other software. + +The send packet thread has partial control over the Tx ring and 'dev->tbusy' +flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next +queue slot is empty, it clears the tbusy flag when finished otherwise it sets +the 'tp->tx_full' flag. + +The interrupt handler has exclusive control over the Rx ring and records stats +from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so +we can't avoid the interrupt overhead by having the Tx routine reap the Tx +stats.) After reaping the stats, it marks the queue entry as empty by setting +the 'base' to zero. Iff the 'tp->tx_full' flag is set, it clears both the +tx_full and tbusy flags. + +IV. Notes + +Thanks to Duke Kamstra of SMC for providing an EtherPower board. + +IVb. References + +http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html +http://www.digital.com (search for current 21*4* datasheets and "21X4 SROM") +http://www.national.com/pf/DP/DP83840.html + +IVc. Errata + +The DEC databook doesn't document which Rx filter settings accept broadcast +packets. Nor does it document how to configure the part to configure the +serial subsystem for normal (vs. loopback) operation or how to have it +autoswitch between internal 10baseT, SIA and AUI transceivers. + +The 21040 databook claims that CSR13, CSR14, and CSR15 should each be the last +register of the set CSR12-15 written. Hmmm, now how is that possible? */ + + +/* A few values that may be tweaked. */ +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ + +/* This is a mysterious value that can be written to CSR11 in the 21040 (only) + to support a pre-NWay full-duplex signaling mechanism using short frames. + No one knows what it should be, but if left at its default value some + 10base2(!) packets trigger a full-duplex-request interrupt. */ +#define FULL_DUPLEX_MAGIC 0x6969 + +#ifndef PCI_VENDOR_ID_DEC /* Now defined in linux/pci.h */ +#define PCI_VENDOR_ID_DEC 0x1011 +#define PCI_DEVICE_ID_TULIP 0x0002 /* 21040. */ +#define PCI_DEVICE_ID_TULIP_FAST 0x0009 /* 21140. */ +#endif + +#ifndef PCI_DEVICE_ID_DEC_TULIP_PLUS +#define PCI_DEVICE_ID_DEC_TULIP_PLUS 0x0014 /* 21041. */ +#endif +#ifndef PCI_DEVICE_ID_DEC_TULIP_21142 +#define PCI_DEVICE_ID_DEC_TULIP_21142 0x0019 +#endif + +#ifndef PCI_VENDOR_ID_LITEON +#define PCI_VENDOR_ID_LITEON 0x11AD +#endif + +#ifndef PCI_VENDOR_ID_MXIC +#define PCI_VENDOR_ID_MXIC 0x10d9 +#define PCI_DEVICE_ID_MX98713 0x0512 +#define PCI_DEVICE_ID_MX98715 0x0531 +#define PCI_DEVICE_ID_MX98725 0x0531 +#endif + +/* The rest of these values should never change. */ + +static void tulip_timer(unsigned long data); +static void t21142_timer(unsigned long data); +static void mxic_timer(unsigned long data); +static void pnic_timer(unsigned long data); + +/* A table describing the chip types. */ +enum tbl_flag { HAS_MII=1, HAS_MEDIA_TABLE = 2, CSR12_IN_SROM = 4,}; +static struct tulip_chip_table { + int vendor_id, device_id; + char *chip_name; + int io_size; + int valid_intrs; /* CSR7 interrupt enable settings */ + int flags; + void (*media_timer)(unsigned long data); +} tulip_tbl[] = { + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP, + "Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer }, + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_PLUS, + "Digital DC21041 Tulip", 128, 0x0001ebef, HAS_MEDIA_TABLE, tulip_timer }, + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_FAST, + "Digital DS21140 Tulip", 128, 0x0001ebef, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, + tulip_timer }, + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_21142, + "Digital DS21142/3 Tulip", 128, 0x0801fbff, + HAS_MII | HAS_MEDIA_TABLE, t21142_timer }, + { PCI_VENDOR_ID_LITEON, 0x0002, + "Lite-On 82c168 PNIC", 256, 0x0001ebef, HAS_MII, pnic_timer }, + { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98713, + "Macronix 98713 PMAC", 128, 0x0001ebef, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer /* Tulip-like! */ }, + { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98715, + "Macronix 98715 PMAC", 256, 0x0001ebef, HAS_MEDIA_TABLE, mxic_timer }, + { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98725, + "Macronix 98725 PMAC", 256, 0x0001ebef, HAS_MEDIA_TABLE, mxic_timer }, + { 0x125B, 0x1400, "ASIX AX88140", 128, 0x0001fbff, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer }, + {0, 0, 0, 0}, +}; +/* This matches the table above. */ +enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3, + LC82C168, MX98713, MX98715, MX98725}; + +/* A full-duplex map for media types. */ +enum MediaIs {MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8, + MediaIs100=16}; +static const char media_cap[] = +{0,0,0,16, 3,19,16,24, 27,4,7,5, 0,20,23,20 }; +/* 21041 transceiver register settings: 10-T, 10-2, AUI, 10-T, 10T-FD*/ +static u16 t21041_csr13[] = { 0xEF05, 0xEF09, 0xEF09, 0xEF01, 0xEF09, }; +static u16 t21041_csr14[] = { 0x7F3F, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, }; +static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, }; + +static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0000, 0x0001, }; +static u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x0000, 0x7F3D, }; +static u16 t21142_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, }; + +/* Offsets to the Command and Status Registers, "CSRs". All accesses + must be longword instructions and quadword aligned. */ +enum tulip_offsets { + CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28, + CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58, + CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 }; + +/* The bits in the CSR5 status registers, mostly interrupt sources. */ +enum status_bits { + TimerInt=0x800, TPLnkFail=0x1000, TPLnkPass=0x10, + NormalIntr=0x10000, AbnormalIntr=0x8000, + RxJabber=0x200, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40, + TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01, +}; + +enum desc_status_bits { + DescOwned=0x80000000, RxDescFatalErr=0x8000, RxWholePkt=0x0300, +}; + +/* The Tulip Rx and Tx buffer descriptors. */ +struct tulip_rx_desc { + s32 status; + s32 length; + u32 buffer1, buffer2; +}; + +struct tulip_tx_desc { + s32 status; + s32 length; + u32 buffer1, buffer2; /* We use only buffer 1. */ +}; + +struct medialeaf { + u8 type; + u8 media; + unsigned char *leafdata; +}; + +struct mediatable { + u16 defaultmedia; + u8 leafcount, csr12dir; /* General purpose pin directions. */ + unsigned has_mii:1, has_nonmii:1; + struct medialeaf mleaf[0]; +}; + +struct mediainfo { + struct mediainfo *next; + int info_type; + int index; + unsigned char *info; +}; + +struct tulip_private { + char devname[8]; /* Used only for kernel debugging. */ + const char *product_name; + struct device *next_module; + struct tulip_rx_desc rx_ring[RX_RING_SIZE]; + struct tulip_tx_desc tx_ring[TX_RING_SIZE]; + /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + struct sk_buff* tx_skbuff[TX_RING_SIZE]; + /* The addresses of receive-in-place skbuffs. */ + struct sk_buff* rx_skbuff[RX_RING_SIZE]; + char *rx_buffs; /* Address of temporary Rx buffers. */ + u32 setup_frame[48]; /* Pseudo-Tx frame to init address table. */ + int chip_id; + int revision; +#if LINUX_VERSION_CODE > 0x20139 + struct net_device_stats stats; +#else + struct enet_statistics stats; +#endif + struct timer_list timer; /* Media selection timer. */ + int interrupt; /* In-interrupt flag. */ +#ifdef SMP_CHECK + int smp_proc_id; /* Which processor in IRQ handler. */ +#endif + unsigned int cur_rx, cur_tx; /* The next free ring entry */ + unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ + unsigned int tx_full:1; /* The Tx queue is full. */ + unsigned int full_duplex:1; /* Full-duplex operation requested. */ + unsigned int full_duplex_lock:1; + unsigned int fake_addr:1; /* Multiport board faked address. */ + unsigned int default_port:4; /* Last dev->if_port value. */ + unsigned int media2:4; /* Secondary monitored media port. */ + unsigned int medialock:1; /* Don't sense media type. */ + unsigned int mediasense:1; /* Media sensing in progress. */ + unsigned int csr6; /* Current CSR6 control settings. */ + unsigned char eeprom[128]; /* Serial EEPROM contents. */ + u16 to_advertise; /* NWay capabilities advertised. */ + u16 advertising[4]; + signed char phys[4], mii_cnt; /* MII device addresses. */ + struct mediatable *mtable; + int cur_index; /* Current media index. */ + unsigned char pci_bus, pci_dev_fn; + int pad0, pad1; /* Used for 8-byte alignment */ +}; + +static struct device *tulip_probe1(int pci_bus, int pci_devfn, + struct device *dev, + int chip_id, int options); +static void parse_eeprom(struct device *dev); +static int read_eeprom(long ioaddr, int location); +static int mdio_read(struct device *dev, int phy_id, int location); +static void mdio_write(struct device *dev, int phy_id, int location, int value); +static void select_media(struct device *dev, int startup); +static int tulip_open(struct device *dev); +static void tulip_timer(unsigned long data); +static void tulip_tx_timeout(struct device *dev); +static void tulip_init_ring(struct device *dev); +static int tulip_start_xmit(struct sk_buff *skb, struct device *dev); +static int tulip_rx(struct device *dev); +static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs); +static int tulip_close(struct device *dev); +static struct enet_statistics *tulip_get_stats(struct device *dev); +#ifdef HAVE_PRIVATE_IOCTL +static int private_ioctl(struct device *dev, struct ifreq *rq, int cmd); +#endif +#ifdef NEW_MULTICAST +static void set_rx_mode(struct device *dev); +#else +static void set_rx_mode(struct device *dev, int num_addrs, void *addrs); +#endif + + + +/* A list of all installed Tulip devices, for removing the driver module. */ +static struct device *root_tulip_dev = NULL; + +/* This 21040 probe no longer uses a large fixed contiguous Rx buffer region, + but now receives directly into full-sized skbuffs that are allocated + at open() time. + This allows the probe routine to use the old driver initialization + interface. */ + +int tulip_probe(struct device *dev) +{ + int cards_found = 0; + static int pci_index = 0; /* Static, for multiple probe calls. */ + unsigned char pci_bus, pci_device_fn; + + /* Ideally we would detect all network cards in slot order. That would + be best done a central PCI probe dispatch, which wouldn't work + well with the current structure. So instead we detect just the + Tulip cards in slot order. */ + +#if LINUX_VERSION_CODE >= 0x20155 + if (! pci_present()) + return -ENODEV; +#else + if (! pcibios_present()) + return -ENODEV; +#endif + for (;pci_index < 0xff; pci_index++) { + u16 vendor, device, pci_command, new_command; + unsigned long pci_ioaddr = 0; + int chip_idx = 0; + + if (pcibios_find_class + (PCI_CLASS_NETWORK_ETHERNET << 8, + reverse_probe ? 0xfe - pci_index : pci_index, + &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL) + if (reverse_probe) + continue; + else + break; + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_VENDOR_ID, &vendor); + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_DEVICE_ID, &device); + + for (chip_idx = 0; tulip_tbl[chip_idx].chip_name; chip_idx++) + if (vendor == tulip_tbl[chip_idx].vendor_id && + device == tulip_tbl[chip_idx].device_id) + break; + if (tulip_tbl[chip_idx].chip_name == 0) { + if (vendor == PCI_VENDOR_ID_DEC || + vendor == PCI_VENDOR_ID_LITEON) + printk(KERN_INFO "Unknown Tulip-style PCI ethernet chip type" + " %4.4x %4.4x"" detected: not configured.\n", + vendor, device); + continue; + } +#if LINUX_VERSION_CODE >= 0x20155 + pci_ioaddr = pci_find_slot(pci_bus, pci_device_fn)->base_address[0]; +#else + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, + &pci_ioaddr); +#endif + /* Remove I/O space marker in bit 0. */ + pci_ioaddr &= ~3; + + if (tulip_debug > 2) + printk(KERN_DEBUG "Found %s at I/O %#lx.\n", + tulip_tbl[chip_idx].chip_name, pci_ioaddr); + + if (check_region(pci_ioaddr, tulip_tbl[chip_idx].io_size)) + continue; + + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, &pci_command); + new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO; + if (pci_command != new_command) { + printk(KERN_INFO " The PCI BIOS has not enabled this" + " device! Updating PCI command %4.4x->%4.4x.\n", + pci_command, new_command); + pcibios_write_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, new_command); + } + + dev = tulip_probe1(pci_bus, pci_device_fn, dev, chip_idx, cards_found); + + /* Get and check the bus-master and latency values. */ + if (dev) { + unsigned char pci_latency; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, &pci_latency); + if (pci_latency < 10) { + printk(KERN_INFO " PCI latency timer (CFLT) is " + "unreasonably low at %d. Setting to 64 clocks.\n", + pci_latency); + pcibios_write_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, 64); + } else if (tulip_debug > 1) + printk(KERN_INFO " PCI latency timer (CFLT) is %#x, " + " PCI command is %4.4x.\n", + pci_latency, new_command); + /* Bring the 21143 out power-down mode. */ + if (device == PCI_DEVICE_ID_DEC_TULIP_21142) + pcibios_write_config_dword(pci_bus, pci_device_fn, + 0x40, 0x40000000); + dev = 0; + cards_found++; + } + } + + return cards_found ? 0 : -ENODEV; +} + +static struct device *tulip_probe1(int pci_bus, int pci_device_fn, + struct device *dev, + int chip_id, int board_idx) +{ + static int did_version = 0; /* Already printed version info. */ + struct tulip_private *tp; + long ioaddr; + int irq; + /* See note below on the multiport cards. */ + static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'}; + static int last_irq = 0; + static int multiport_cnt = 0; /* For four-port boards w/one EEPROM */ + int i; + unsigned short sum; + + if (tulip_debug > 0 && did_version++ == 0) + printk(KERN_INFO "%s", version); + + dev = init_etherdev(dev, 0); + +#if LINUX_VERSION_CODE >= 0x20155 + irq = pci_find_slot(pci_bus, pci_device_fn)->irq; + ioaddr = pci_find_slot(pci_bus, pci_device_fn)->base_address[0]; +#else + { + u8 pci_irq_line; + u32 pci_ioaddr; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq_line); + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, + &pci_ioaddr); + irq = pci_irq_line; + ioaddr = pci_ioaddr; + } +#endif + /* Remove I/O space marker in bit 0. */ + ioaddr &= ~3; + + printk(KERN_INFO "%s: %s at %#3lx,", + dev->name, tulip_tbl[chip_id].chip_name, ioaddr); + + /* Stop the chip's Tx and Rx processes. */ + outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6); + /* Clear the missed-packet counter. */ + (volatile int)inl(ioaddr + CSR8); + + if (chip_id == DC21041) { + if (inl(ioaddr + CSR9) & 0x8000) { + printk(" 21040 compatible mode,"); + chip_id = DC21040; + } else { + printk(" 21041 mode,"); + } + } + + /* The station address ROM is read byte serially. The register must + be polled, waiting for the value to be read bit serially from the + EEPROM. + */ + sum = 0; + if (chip_id == DC21040) { + outl(0, ioaddr + CSR9); /* Reset the pointer with a dummy write. */ + for (i = 0; i < 6; i++) { + int value, boguscnt = 100000; + do + value = inl(ioaddr + CSR9); + while (value < 0 && --boguscnt > 0); + dev->dev_addr[i] = value; + sum += value & 0xff; + } + } else if (chip_id == LC82C168) { + for (i = 0; i < 3; i++) { + int value, boguscnt = 100000; + outl(0x600 | i, ioaddr + 0x98); + do + value = inl(ioaddr + CSR9); + while (value < 0 && --boguscnt > 0); + ((u16*)dev->dev_addr)[i] = value; + sum += value & 0xffff; + } + } else { /* Must be a new chip, with a serial EEPROM interface. */ + /* We read the whole EEPROM, and sort it out later. DEC has a + specification _Digital Semiconductor 21X4 Serial ROM Format_ + but early vendor boards just put the address in the first six + EEPROM locations. */ + unsigned char ee_data[128]; + int sa_offset = 0; + + for (i = 0; i < sizeof(ee_data)/2; i++) + ((u16 *)ee_data)[i] = read_eeprom(ioaddr, i); + + /* Detect the simple EEPROM format by the duplicated station addr. */ + for (i = 0; i < 8; i ++) + if (ee_data[i] != ee_data[16+i]) + sa_offset = 20; + if (ee_data[0] == 0xff && ee_data[1] == 0xff && ee_data[2] == 0) { + sa_offset = 2; /* Grrr, damn Matrox boards. */ + multiport_cnt = 4; + } + for (i = 0; i < 6; i ++) { + dev->dev_addr[i] = ee_data[i + sa_offset]; + sum += ee_data[i + sa_offset]; + } + } + /* Lite-On boards have the address byte-swapped. */ + if (dev->dev_addr[0] == 0xA0 && dev->dev_addr[1] == 0x00) + for (i = 0; i < 6; i+=2) { + char tmp = dev->dev_addr[i]; + dev->dev_addr[i] = dev->dev_addr[i+1]; + dev->dev_addr[i+1] = tmp; + } + /* On the Zynx 315 Etherarray and other multiport boards only the + first Tulip has an EEPROM. + The addresses of the subsequent ports are derived from the first. + Many PCI BIOSes also incorrectly report the IRQ line, so we correct + that here as well. */ + if (sum == 0 || sum == 6*0xff) { + printk(" EEPROM not present,"); + for (i = 0; i < 5; i++) + dev->dev_addr[i] = last_phys_addr[i]; + dev->dev_addr[i] = last_phys_addr[i] + 1; +#if defined(__i386__) /* This BIOS bug doesn't exist on Alphas. */ + irq = last_irq; +#endif + } + + for (i = 0; i < 6; i++) + printk(" %2.2x", last_phys_addr[i] = dev->dev_addr[i]); + printk(", IRQ %d.\n", irq); + last_irq = irq; + + /* We do a request_region() only to register /proc/ioports info. */ + /* Note that proper size is tulip_tbl[chip_id].chip_name, but... */ + request_region(ioaddr, TULIP_TOTAL_SIZE, dev->name); + + dev->base_addr = ioaddr; + dev->irq = irq; + + /* Make certain the data structures are quadword aligned. */ + tp = (void *)(((long)kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA) + 7) & ~7); + memset(tp, 0, sizeof(*tp)); + dev->priv = tp; + + tp->next_module = root_tulip_dev; + root_tulip_dev = dev; + + tp->pci_bus = pci_bus; + tp->pci_dev_fn = pci_device_fn; + tp->chip_id = chip_id; + +#ifdef TULIP_FULL_DUPLEX + tp->full_duplex = 1; + tp->full_duplex_lock = 1; +#endif +#ifdef TULIP_DEFAULT_MEDIA + tp->default_port = TULIP_DEFAULT_MEDIA; +#endif +#ifdef TULIP_NO_MEDIA_SWITCH + tp->medialock = 1; +#endif + + /* The lower four bits are the media type. */ + if (board_idx >= 0 && board_idx < MAX_UNITS) { + tp->default_port = options[board_idx] & 15; + if ((options[board_idx] & 0x90) || full_duplex[board_idx] > 0) + tp->full_duplex = 1; + if (mtu[board_idx] > 0) + dev->mtu = mtu[board_idx]; + } + if (dev->mem_start) + tp->default_port = dev->mem_start; + if (tp->default_port) { + tp->medialock = 1; + if (media_cap[tp->default_port] & MediaAlwaysFD) + tp->full_duplex = 1; + } + if (tp->full_duplex) + tp->full_duplex_lock = 1; + + /* This is logically part of probe1(), but too complex to write inline. */ + if (tulip_tbl[chip_id].flags & HAS_MEDIA_TABLE) + parse_eeprom(dev); + + if (media_cap[tp->default_port] & MediaIsMII) { + u16 media2advert[] = { 0x20, 0x40, 0x03e0, 0x60, 0x80, 0x100, 0x200 }; + tp->to_advertise = media2advert[tp->default_port - 9]; + } else + tp->to_advertise = 0x03e1; + + if ((tp->mtable && tp->mtable->has_mii) || + ( ! tp->mtable && (tulip_tbl[tp->chip_id].flags & HAS_MII))) { + int phy, phy_idx; + /* Find the connected MII xcvrs. + Doing this in open() would allow detecting external xcvrs later, + but takes much time. */ + for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys); + phy++) { + int mii_status = mdio_read(dev, phy, 1); + if (mii_status != 0xffff && mii_status != 0x0000) { + int mii_reg0 = mdio_read(dev, phy, 0); + int reg4 = ((mii_status>>6) & tp->to_advertise) | 1; + tp->phys[phy_idx] = phy; + tp->advertising[phy_idx++] = reg4; + printk(KERN_INFO "%s: MII transceiver found at MDIO address " + "%d, config %4.4x status %4.4x.\n", + dev->name, phy, mii_reg0, mii_status); + if (1 || (media_cap[tp->default_port] & MediaIsMII)) { + printk(KERN_DEBUG "%s: Advertising %4.4x on PHY %d," + " previously advertising %4.4x.\n", + dev->name, reg4, phy, mdio_read(dev, phy, 4)); + mdio_write(dev, phy, 4, reg4); + } + /* Enable autonegotiation: some boards default to off. */ + mdio_write(dev, phy, 0, mii_reg0 | + (tp->full_duplex ? 0x1100 : 0x1000) | + (media_cap[tp->default_port]&MediaIs100 ? 0x2000:0)); + } + } + tp->mii_cnt = phy_idx; + if (tp->mtable && tp->mtable->has_mii && phy_idx == 0) { + printk(KERN_INFO "%s: ***WARNING***: No MII transceiver found!\n", + dev->name); + tp->phys[0] = 1; + } + } + + /* The Tulip-specific entries in the device structure. */ + dev->open = &tulip_open; + dev->hard_start_xmit = &tulip_start_xmit; + dev->stop = &tulip_close; + dev->get_stats = &tulip_get_stats; +#ifdef HAVE_PRIVATE_IOCTL + dev->do_ioctl = &private_ioctl; +#endif +#ifdef HAVE_MULTICAST + dev->set_multicast_list = &set_rx_mode; +#endif + + /* Reset the xcvr interface and turn on heartbeat. */ + switch (chip_id) { + case DC21041: + outl(0x00000000, ioaddr + CSR13); + outl(0xFFFFFFFF, ioaddr + CSR14); + outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */ + outl(inl(ioaddr + CSR6) | 0x0200, ioaddr + CSR6); + outl(0x0000EF05, ioaddr + CSR13); + break; + case DC21040: + outl(0x00000000, ioaddr + CSR13); + outl(0x00000004, ioaddr + CSR13); + break; + case DC21140: default: + if (tp->mtable) + outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12); + break; + case DC21142: + outl(0x82420200, ioaddr + CSR6); + outl(0x0001, ioaddr + CSR13); + outl(0x0003FFFF, ioaddr + CSR14); + outl(0x0008, ioaddr + CSR15); + outl(0x0001, ioaddr + CSR13); + outl(0x1301, ioaddr + CSR12); /* Start NWay. */ + break; + case LC82C168: + if ( ! tp->mii_cnt) { + outl(0x00420000, ioaddr + CSR6); + outl(0x30, ioaddr + CSR12); + outl(0x0001F078, ioaddr + 0xB8); + outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */ + } + break; + case MX98713: case MX98715: case MX98725: + outl(0x00000000, ioaddr + CSR6); + outl(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */ + outl(0x00000001, ioaddr + CSR13); + break; + } + + return dev; +} + +/* Serial EEPROM section. */ +/* The main routine to parse the very complicated SROM structure. + Search www.digital.com for "21X4 SROM" to get details. + This code is very complex, and will require changes to support + additional cards, so I'll be verbose about what is going on. + */ + +/* Known cards that have old-style EEPROMs. */ +static struct fixups { + char *name; + unsigned char addr0, addr1, addr2; + u16 newtable[32]; /* Max length below. */ +} eeprom_fixups[] = { + {"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c, + 0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }}, + {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x021f, + 0x0000, 0x009E, /* 10baseT */ + 0x0903, 0x006D, /* 100baseTx */ }}, + {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x033f, + 0x0107, 0x8021, /* 100baseFx */ + 0x0108, 0x8021, /* 100baseFx-FD */ + 0x0103, 0x006D, /* 100baseTx */ }}, + {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0313, + 0x1001, 0x009E, /* 10base2, CSR12 0x10*/ + 0x0000, 0x009E, /* 10baseT */ + 0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ }}, + {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x031F, + 0x1B01, 0x0000, /* 10base2, CSR12 0x1B */ + 0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */ + 0x0B00, 0x009E, /* 10baseT, CSR12 0x0B */ + }}, + {0, 0, 0, 0, {}}}; + +static const char * block_name[] = {"21140 non-MII", "21140 MII PHY", + "21142 Serial PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"}; + +#define EEPROM_SIZE 128 +#if defined(__i386__) +#define get_u16(ptr) (*(u16 *)(ptr)) +#else +#define get_u16(ptr) (((u8*)(ptr))[0] + (((u8*)(ptr))[1]<<8)) +#endif + +static void parse_eeprom(struct device *dev) +{ + /* The last media info list parsed, for multiport boards. */ + static struct mediatable *last_mediatable = NULL; + static unsigned char *last_ee_data = NULL; + static int controller_index = 0; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + unsigned char *ee_data = tp->eeprom; + int i; + + tp->mtable = 0; + for (i = 0; i < EEPROM_SIZE/2; i++) + ((u16 *)ee_data)[i] = read_eeprom(ioaddr, i); + + /* Detect an old-style (SA only) EEPROM layout: + memcmp(eedata, eedata+16, 8). */ + for (i = 0; i < 8; i ++) + if (ee_data[i] != ee_data[16+i]) + break; + if (i >= 8) { + if (ee_data[0] == 0xff) { + if (last_mediatable) { + controller_index++; + printk(KERN_INFO "%s: Controller %d of multiport board.\n", + dev->name, controller_index); + tp->mtable = last_mediatable; + ee_data = last_ee_data; + goto subsequent_board; + } else + printk(KERN_INFO "%s: Missing EEPROM, this interface may " + "not work correctly!\n", + dev->name); + return; + } + /* Do a fix-up based on the vendor half of the station address prefix. */ + for (i = 0; eeprom_fixups[i].name; i++) { + if (dev->dev_addr[0] == eeprom_fixups[i].addr0 + && dev->dev_addr[1] == eeprom_fixups[i].addr1 + && dev->dev_addr[2] == eeprom_fixups[i].addr2) { + if (dev->dev_addr[2] == 0xE8 && ee_data[0x1a] == 0x55) + i++; /* An Accton EN1207, not an outlaw Maxtech. */ + memcpy(ee_data + 26, eeprom_fixups[i].newtable, + sizeof(eeprom_fixups[i].newtable)); + printk(KERN_INFO "%s: Old format EEPROM on '%s' board. Using" + " substitute media control info.\n", + dev->name, eeprom_fixups[i].name); + break; + } + } + if (eeprom_fixups[i].name == NULL) { /* No fixup found. */ + printk(KERN_INFO "%s: Old style EEPROM -- no media selection information.\n", + dev->name); + return; + } + } + if (tulip_debug > 1) { + printk(KERN_DEBUG "read_eeprom:"); + for (i = 0; i < 64; i++) { + printk("%s%4.4x", (i & 7) == 0 ? "\n" KERN_DEBUG : " ", + read_eeprom(ioaddr, i)); + } + printk("\n"); + } + + controller_index = 0; + if (ee_data[19] > 1) { /* Multiport board. */ + last_ee_data = ee_data; + } +subsequent_board: + + if (ee_data[27] == 0) { /* No valid media table. */ + } else if (tp->chip_id == DC21041) { + unsigned char *p = (void *)ee_data + ee_data[27 + controller_index*3]; + short media; + int count; + + media = get_u16(p); + p += 2; + count = *p++; + + printk(KERN_INFO "%s:21041 Media information at %d, default media " + "%4.4x (%s).\n", dev->name, ee_data[27], media, + media & 0x0800 ? "Autosense" : medianame[media & 15]); + for (i = 0; i < count; i++) { + unsigned char media_code = *p++; + u16 csrvals[3]; + int idx; + for (idx = 0; idx < 3; idx++) { + csrvals[idx] = get_u16(p); + p += 2; + } + if (media_code & 0x40) { + printk(KERN_INFO "%s: 21041 media %2.2x (%s)," + " csr13 %4.4x csr14 %4.4x csr15 %4.4x.\n", + dev->name, media_code & 15, medianame[media_code & 15], + csrvals[0], csrvals[1], csrvals[2]); + } else + printk(KERN_INFO "%s: 21041 media #%d, %s.\n", + dev->name, media_code & 15, medianame[media_code & 15]); + } + } else { + unsigned char *p = (void *)ee_data + ee_data[27]; + unsigned char csr12dir = 0; + int count; + struct mediatable *mtable; + u16 media = get_u16(p); + + p += 2; + if (tulip_tbl[tp->chip_id].flags & CSR12_IN_SROM) + csr12dir = *p++; + count = *p++; + mtable = (struct mediatable *) + kmalloc(sizeof(struct mediatable) + count*sizeof(struct medialeaf), + GFP_KERNEL); + if (mtable == NULL) + return; /* Horrible, impossible failure. */ + last_mediatable = tp->mtable = mtable; + mtable->defaultmedia = media; + mtable->leafcount = count; + mtable->csr12dir = csr12dir; + mtable->has_nonmii = mtable->has_mii = 0; + + printk(KERN_INFO "%s: EEPROM default media type %s.\n", dev->name, + media & 0x0800 ? "Autosense" : medianame[media & 15]); + for (i = 0; i < count; i++) { + struct medialeaf *leaf = &mtable->mleaf[i]; + + if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */ + leaf->type = 0; + leaf->media = p[0] & 0x3f; + leaf->leafdata = p; + if ((p[2] & 0x61) == 0x01) /* Bogus, but Znyx boards do it. */ + mtable->has_mii = 1; + p += 4; + } else { + leaf->type = p[1]; + if (p[1] & 1) { + mtable->has_mii = 1; + leaf->media = 11; + } else { + mtable->has_nonmii = 1; + leaf->media = p[2] & 0x0f; + } + leaf->leafdata = p + 2; + p += (p[0] & 0x3f) + 1; + } + if (tulip_debug > 1 && leaf->media == 11) { + unsigned char *bp = leaf->leafdata; + printk(KERN_INFO "%s: MII interface PHY %d, setup/reset " + "sequences %d/%d long, capabilities %2.2x %2.2x.\n", + dev->name, bp[0], bp[1], bp[1 + bp[1]*2], + bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]); + } + printk(KERN_INFO "%s: Index #%d - Media %s (#%d) described " + "by a %s (%d) block.\n", + dev->name, i, medianame[leaf->media], leaf->media, + block_name[leaf->type], leaf->type); + } + } +} +/* Reading a serial EEPROM is a "bit" grungy, but we work our way through:->.*/ + +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */ +#define EE_CS 0x01 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x01 +#define EE_WRITE_1 0x05 +#define EE_DATA_READ 0x08 /* EEPROM chip data out. */ +#define EE_ENB (0x4800 | EE_CS) + +/* Delay between EEPROM clock transitions. + The 1.2 code is a "nasty" timing loop, but PC compatible machines are + *supposed* to delay an ISA-compatible period for the SLOW_DOWN_IO macro. */ +#ifdef _LINUX_DELAY_H +#define eeprom_delay(nanosec) udelay((nanosec + 999)/1000) +#else +#define eeprom_delay(nanosec) do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0) +#endif + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD (5 << 6) +#define EE_READ_CMD (6 << 6) +#define EE_ERASE_CMD (7 << 6) + +static int read_eeprom(long ioaddr, int location) +{ + int i; + unsigned short retval = 0; + long ee_addr = ioaddr + CSR9; + int read_cmd = location | EE_READ_CMD; + + outl(EE_ENB & ~EE_CS, ee_addr); + outl(EE_ENB, ee_addr); + + /* Shift the read command bits out. */ + for (i = 10; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + outl(EE_ENB | dataval, ee_addr); + eeprom_delay(100); + outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); + eeprom_delay(150); + outl(EE_ENB | dataval, ee_addr); /* Finish EEPROM a clock tick. */ + eeprom_delay(250); + } + outl(EE_ENB, ee_addr); + + for (i = 16; i > 0; i--) { + outl(EE_ENB | EE_SHIFT_CLK, ee_addr); + eeprom_delay(100); + retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0); + outl(EE_ENB, ee_addr); + eeprom_delay(100); + } + + /* Terminate the EEPROM access. */ + outl(EE_ENB & ~EE_CS, ee_addr); + return retval; +} + +/* MII transceiver control section. + Read and write the MII registers using software-generated serial + MDIO protocol. See the MII specifications or DP83840A data sheet + for details. */ + +/* The maximum data clock rate is 2.5 Mhz. The minimum timing is usually + met by back-to-back PCI I/O cycles, but we insert a delay to avoid + "overclocking" issues or future 66Mhz PCI. */ +#define mdio_delay() inl(mdio_addr) + +/* Read and write the MII registers using software-generated serial + MDIO protocol. It is just different enough from the EEPROM protocol + to not share code. The maxium data clock rate is 2.5 Mhz. */ +#define MDIO_SHIFT_CLK 0x10000 +#define MDIO_DATA_WRITE0 0x00000 +#define MDIO_DATA_WRITE1 0x20000 +#define MDIO_ENB 0x00000 /* Ignore the 0x02000 databook setting. */ +#define MDIO_ENB_IN 0x40000 +#define MDIO_DATA_READ 0x80000 + +static int mdio_read(struct device *dev, int phy_id, int location) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int i; + int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; + int retval = 0; + long mdio_addr = dev->base_addr + CSR9; + + if (tp->chip_id == LC82C168) { + long ioaddr = dev->base_addr; + int i = 1000; + outl(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0); + while (--i > 0) + if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000)) + return retval & 0xffff; + return 0xffff; + } + + /* Establish sync by sending at least 32 logic ones. */ + for (i = 32; i >= 0; i--) { + outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); + mdio_delay(); + outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Shift the read command bits out. */ + for (i = 15; i >= 0; i--) { + int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; + + outl(MDIO_ENB | dataval, mdio_addr); + mdio_delay(); + outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Read the two transition, 16 data, and wire-idle bits. */ + for (i = 19; i > 0; i--) { + outl(MDIO_ENB_IN, mdio_addr); + mdio_delay(); + retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); + outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + return (retval>>1) & 0xffff; +} + +static void mdio_write(struct device *dev, int phy_id, int location, int value) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int i; + int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; + long mdio_addr = dev->base_addr + CSR9; + + if (tp->chip_id == LC82C168) { + long ioaddr = dev->base_addr; + int i = 1000; + outl(cmd, ioaddr + 0xA0); + do + if ( ! (inl(ioaddr + 0xA0) & 0x80000000)) + break; + while (--i > 0); + return; + } + + /* Establish sync by sending 32 logic ones. */ + for (i = 32; i >= 0; i--) { + outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); + mdio_delay(); + outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; + outl(MDIO_ENB | dataval, mdio_addr); + mdio_delay(); + outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + outl(MDIO_ENB_IN, mdio_addr); + mdio_delay(); + outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + return; +} + + +static int +tulip_open(struct device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + int i = 0; + + /* On some chip revs we must set the MII/SYM port before the reset!? */ + if (tp->mii_cnt || (tp->mtable && tp->mtable->has_mii)) + outl(0x00040000, ioaddr + CSR6); + + /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */ + outl(0x00000001, ioaddr + CSR0); +#ifdef _LINUX_DELAY_H + udelay(2); +#else + SLOW_DOWN_IO; +#endif + /* Deassert reset. + 486: Set 8 longword cache alignment, 8 longword burst. + 586: Set 16 longword cache alignment, no burst limit. + Cache alignment bits 15:14 Burst length 13:8 + 0000 No alignment 0x00000000 unlimited 0800 8 longwords + 4000 8 longwords 0100 1 longword 1000 16 longwords + 8000 16 longwords 0200 2 longwords 2000 32 longwords + C000 32 longwords 0400 4 longwords + Wait the specified 50 PCI cycles after a reset by initializing + Tx and Rx queues and the address filter list. */ +#if defined(__alpha__) + /* ToDo: Alpha setting could be better. */ + outl(0x01A00000 | 0xE000, ioaddr + CSR0); +#elif defined(__powerpc__) + outl(0x01A00080 | 0x8000, ioaddr + CSR0); +#elif defined(__i386__) +#if defined(MODULE) + /* When a module we don't have 'x86' to check. */ + outl(0x01A00000 | 0x4800, ioaddr + CSR0); +#else +#if (LINUX_VERSION_CODE > 0x2014c) +#define x86 boot_cpu_data.x86 +#endif + outl(0x01A00000 | (x86 <= 4 ? 0x4800 : 0x8000), ioaddr + CSR0); + if (x86 <= 4) + printk(KERN_INFO "%s: This is a 386/486 PCI system, setting cache " + "alignment to %x.\n", dev->name, + 0x01A00000 | (x86 <= 4 ? 0x4800 : 0x8000)); +#endif +#else + outl(0x01A00000 | 0x4800, ioaddr + CSR0); +#warning Processor architecture undefined! +#endif + +#ifdef SA_SHIRQ + if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev)) { + return -EAGAIN; + } +#else + if (irq2dev_map[dev->irq] != NULL + || (irq2dev_map[dev->irq] = dev) == NULL + || dev->irq == 0 + || request_irq(dev->irq, &tulip_interrupt, 0, + tulip_tbl[tp->chip_id].chip_name)) { + return -EAGAIN; + } +#endif + + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: tulip_open() irq %d.\n", dev->name, dev->irq); + + MOD_INC_USE_COUNT; + + tulip_init_ring(dev); + + /* This is set_rx_mode(), but without starting the transmitter. */ + /* Fill the whole address filter table with our physical address. */ + { + u16 *eaddrs = (u16 *)dev->dev_addr; + u32 *setup_frm = tp->setup_frame, i; + + /* You must add the broadcast address when doing perfect filtering! */ + *setup_frm++ = 0xffff; + *setup_frm++ = 0xffff; + *setup_frm++ = 0xffff; + /* Fill the rest of the accept table with our physical address. */ + for (i = 1; i < 16; i++) { + *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; + } + /* Put the setup frame on the Tx list. */ + tp->tx_ring[0].length = 0x08000000 | 192; + tp->tx_ring[0].buffer1 = virt_to_bus(tp->setup_frame); + tp->tx_ring[0].status = 0x80000000; + + tp->cur_tx++; + } + + outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3); + outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4); + + if (dev->if_port == 0) + dev->if_port = tp->default_port; + if (tp->chip_id == DC21041 && dev->if_port > 4) + /* Invalid: Select initial TP, autosense, autonegotiate. */ + dev->if_port = 4; + + /* Allow selecting a default media. */ + if (tp->mtable == NULL) + goto media_picked; + if (dev->if_port) { + int looking_for = media_cap[dev->if_port] & MediaIsMII ? 11 : + (dev->if_port == 12 ? 0 : dev->if_port); + for (i = 0; i < tp->mtable->leafcount; i++) + if (tp->mtable->mleaf[i].media == looking_for) { + printk(KERN_INFO "%s: Using user-specified media %s.\n", + dev->name, medianame[dev->if_port]); + goto media_picked; + } + } + if ((tp->mtable->defaultmedia & 0x0800) == 0) + for (i = 0; i < tp->mtable->leafcount; i++) + if (tp->mtable->mleaf[i].media == (tp->mtable->defaultmedia & 15)) { + printk(KERN_INFO "%s: Using EEPROM-set media %s.\n", + dev->name, medianame[tp->mtable->mleaf[i].media]); + goto media_picked; + } + /* Start sensing first non-full-duplex media. */ + for (i = tp->mtable->leafcount - 1; + (media_cap[tp->mtable->mleaf[i].media] & MediaAlwaysFD) && i > 0; i--) + ; +media_picked: + + tp->csr6 = 0; + tp->cur_index = i; + if (dev->if_port == 0 && tp->chip_id == DC21142) { + tp->csr6 = 0x82420200; + outl(0x0003FFFF, ioaddr + CSR14); + outl(0x0008, ioaddr + CSR15); + outl(0x0001, ioaddr + CSR13); + outl(0x1301, ioaddr + CSR12); + } else if (tp->chip_id == LC82C168 && tp->mii_cnt && ! tp->medialock) { + dev->if_port = 11; + tp->csr6 = 0x816C0000 | (tp->full_duplex ? 0x0200 : 0); + outl(0x0001, ioaddr + CSR15); + } else + select_media(dev, 1); + + /* Start the chip's Tx to process setup frame. */ + outl(tp->csr6, ioaddr + CSR6); + outl(tp->csr6 | 0x2000, ioaddr + CSR6); + + dev->tbusy = 0; + tp->interrupt = 0; + dev->start = 1; + + /* Enable interrupts by setting the interrupt mask. */ + outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR5); + outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + outl(0, ioaddr + CSR2); /* Rx poll demand */ + + if (tulip_debug > 2) { + printk(KERN_DEBUG "%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR6 %8.8x.\n", + dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR5), + inl(ioaddr + CSR6)); + } + /* Set the timer to switch to check for link beat and perhaps switch + to an alternate media type. */ + init_timer(&tp->timer); + tp->timer.expires = RUN_AT(5*HZ); + tp->timer.data = (unsigned long)dev; + tp->timer.function = tulip_tbl[tp->chip_id].media_timer; + add_timer(&tp->timer); + + return 0; +} + +/* Set up the transceiver control registers for the selected media type. */ +static void select_media(struct device *dev, int startup) +{ + long ioaddr = dev->base_addr; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + struct mediatable *mtable = tp->mtable; + u32 new_csr6; + int check_mii =0, i; + + if (mtable) { + struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index]; + unsigned char *p = mleaf->leafdata; + switch (mleaf->type) { + case 0: /* 21140 non-MII xcvr. */ + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Using a 21140 non-MII transceiver" + " with control setting %2.2x.\n", + dev->name, p[1]); + dev->if_port = p[0]; + if (startup) + outl(mtable->csr12dir | 0x100, ioaddr + CSR12); + outl(p[1], ioaddr + CSR12); + new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18); + break; + case 2: case 4: { + u16 setup[3]; + for (i = 0; i < 3; i++) + setup[i] = get_u16(&p[i*2 + 1]); + + dev->if_port = p[0] & 15; + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: 21142 non-MII %s transceiver control %4.4x/%4.4x.\n", + dev->name, medianame[dev->if_port], setup[0], setup[1]); + if (p[0] & 0x40) { /* SIA (CSR13-15) setup values are provided. */ + outl(0, ioaddr + CSR13); + outl(setup[1], ioaddr + CSR14); + outl(setup[2], ioaddr + CSR15); + outl(setup[0], ioaddr + CSR13); + for (i = 0; i < 3; i++) /* Re-fill setup[] */ + setup[i] = get_u16(&p[i*2 + 7]); + } else if (dev->if_port <= 4) { + outl(0, ioaddr + CSR13); + outl(t21142_csr14[dev->if_port], ioaddr + CSR14); + outl(t21142_csr15[dev->if_port], ioaddr + CSR15); + outl(t21142_csr13[dev->if_port], ioaddr + CSR13); + } else { + outl(0, ioaddr + CSR14); + outl(8, ioaddr + CSR15); + outl(0, ioaddr + CSR13); + } + outl(setup[0]<<16, ioaddr + CSR15); /* Direction */ + outl(setup[1]<<16, ioaddr + CSR15); /* Data */ + if (mleaf->type == 4) + new_csr6 = 0x82020000 | ((setup[2] & 0x71) << 18); + else + new_csr6 = 0x82420000; + break; + } + case 1: case 3: { + int phy_num = p[0]; + int init_length = p[1]; + u16 *misc_info; + u16 to_advertise; + + dev->if_port = 11; + check_mii = 1; + new_csr6 = 0x020E0000; + if (mleaf->type == 3) { /* 21142 */ + u16 *init_sequence = (u16*)(p+2); + u16 *reset_sequence = &((u16*)(p+3))[init_length]; + int reset_length = p[2 + init_length*2]; + misc_info = reset_sequence + reset_length; + if (startup) + for (i = 0; i < reset_length; i++) + outl(get_u16(&reset_sequence[i]) << 16, ioaddr + CSR15); + for (i = 0; i < init_length; i++) + outl(get_u16(&init_sequence[i]) << 16, ioaddr + CSR15); + } else { + u8 *init_sequence = p + 2; + u8 *reset_sequence = p + 3 + init_length; + int reset_length = p[2 + init_length]; + misc_info = (u16*)(reset_sequence + reset_length); + if (startup) { + outl(mtable->csr12dir | 0x100, ioaddr + CSR12); + for (i = 0; i < reset_length; i++) + outl(reset_sequence[i], ioaddr + CSR12); + } + for (i = 0; i < init_length; i++) + outl(init_sequence[i], ioaddr + CSR12); + } + to_advertise = (get_u16(&misc_info[1]) & tp->to_advertise) | 1; + tp->advertising[phy_num] = to_advertise; + if (tulip_debug > 1 || 1) + printk(KERN_DEBUG "%s: Advertising %4.4x on PHY %d (%d).\n", + dev->name, to_advertise, phy_num, tp->phys[phy_num]); + /* Bogus: put in by a committee? */ + mdio_write(dev, tp->phys[phy_num], 4, to_advertise); + break; + } + default: + new_csr6 = 0x020E0000; + } + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Using media type %s, CSR12 is %2.2x.\n", + dev->name, medianame[dev->if_port], + inl(ioaddr + CSR12) & 0xff); + } else if (tp->chip_id == DC21041) { + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: 21041 using media %s, CSR12 is %4.4x.\n", + dev->name, medianame[dev->if_port & 15], + inl(ioaddr + CSR12) & 0xffff); + outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */ + outl(t21041_csr14[dev->if_port], ioaddr + CSR14); + outl(t21041_csr15[dev->if_port], ioaddr + CSR15); + outl(t21041_csr13[dev->if_port], ioaddr + CSR13); + new_csr6 = 0x80020000; + } else if (tp->chip_id == LC82C168) { + if (startup && ! tp->medialock) + dev->if_port = tp->mii_cnt ? 11 : 0; + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: PNIC PHY status is %3.3x, CSR12 %4.4x," + " media %s.\n", + dev->name, inl(ioaddr + 0xB8), inl(ioaddr + CSR12), + medianame[dev->if_port]); + if (tp->mii_cnt) { + new_csr6 = 0x812C0000; + outl(0x0001, ioaddr + CSR15); + outl(0x0201B07A, ioaddr + 0xB8); + } else if (startup) { + /* Start with 10mbps to do autonegotiation. */ + outl(0x32, ioaddr + CSR12); + new_csr6 = 0x00420000; + outl(0x0001B078, ioaddr + 0xB8); + outl(0x0201B078, ioaddr + 0xB8); + } else if (dev->if_port == 3 || dev->if_port == 5) { + outl(0x33, ioaddr + CSR12); + new_csr6 = 0x01860000; + if (startup) + outl(0x0201F868, ioaddr + 0xB8); /* Trigger autonegotiation. */ + else + outl(0x1F868, ioaddr + 0xB8); + } else { + outl(0x32, ioaddr + CSR12); + new_csr6 = 0x00420000; + outl(0x1F078, ioaddr + 0xB8); + } + } else if (tp->chip_id == DC21040) { /* 21040 */ + /* Turn on the xcvr interface. */ + int csr12 = inl(ioaddr + CSR12); + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: 21040 media type is %s, CSR12 is %2.2x.\n", + dev->name, dev->if_port ? "AUI" : "10baseT", csr12); + new_csr6 = (dev->if_port ? 0x01860000 : 0x00420000); + /* Set the full duplux match frame. */ + outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11); + outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */ + outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13); + } else { /* Unknown chip type with no media table. */ + if (tp->default_port == 0) + if (tp->mii_cnt) { + dev->if_port = 11; + } else + dev->if_port = 3; + if (media_cap[dev->if_port] & MediaIsMII) { + new_csr6 = 0x020E0000; + } else if (media_cap[dev->if_port] & MediaIsFx) { + new_csr6 = 0x028600000; + } else + new_csr6 = 0x038600000; + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: No media description table, assuming " + "%s transceiver, CSR12 %2.2x.\n", + dev->name, medianame[dev->if_port], + inl(ioaddr + CSR12)); + } + + tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | (tp->full_duplex ? 0x0200 : 0); + return; +} + +static void tulip_timer(unsigned long data) +{ + struct device *dev = (struct device *)data; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + u32 csr12 = inl(ioaddr + CSR12); + int next_tick = 0; + + if (tulip_debug > 3) { + printk(KERN_DEBUG "%s: Media selection tick, status %8.8x mode %8.8x " + "SIA %8.8x %8.8x %8.8x %8.8x.\n", + dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR6), + csr12, inl(ioaddr + CSR13), + inl(ioaddr + CSR14), inl(ioaddr + CSR15)); + } + switch (tp->chip_id) { + case DC21040: + if (csr12 & 0x0002) { /* Network error */ + printk(KERN_INFO "%s: No 10baseT link beat found, switching to %s media.\n", + dev->name, dev->if_port ? "10baseT" : "AUI"); + dev->if_port ^= 1; + outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13); + dev->trans_start = jiffies; + } + break; + case DC21041: + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: 21041 media tick CSR12 %8.8x.\n", + dev->name, csr12); + switch (dev->if_port) { + case 0: case 3: case 4: + if (csr12 & 0x0004) { /*LnkFail */ + /* 10baseT is dead. Check for activity on alternate port. */ + tp->mediasense = 1; + if (csr12 & 0x0200) + dev->if_port = 2; + else + dev->if_port = 1; + printk(KERN_INFO "%s: No 21041 10baseT link beat, Media switched to %s.\n", + dev->name, medianame[dev->if_port]); + outl(0, ioaddr + CSR13); /* Reset */ + outl(t21041_csr14[dev->if_port], ioaddr + CSR14); + outl(t21041_csr15[dev->if_port], ioaddr + CSR15); + outl(t21041_csr13[dev->if_port], ioaddr + CSR13); + next_tick = 10*HZ; /* 2.4 sec. */ + } else + next_tick = 30*HZ; + break; + case 1: /* 10base2 */ + case 2: /* AUI */ + if (csr12 & 0x0100) { + next_tick = (30*HZ); /* 30 sec. */ + tp->mediasense = 0; + } else if ((csr12 & 0x0004) == 0) { + printk(KERN_INFO "%s: 21041 media switched to 10baseT.\n", dev->name); + dev->if_port = 0; + select_media(dev, 0); + next_tick = (24*HZ)/10; /* 2.4 sec. */ + } else if (tp->mediasense || (csr12 & 0x0002)) { + dev->if_port = 3 - dev->if_port; /* Swap ports. */ + select_media(dev, 0); + next_tick = 20*HZ; + } else { + next_tick = 20*HZ; + } + break; + } + break; + case DC21140: case DC21142: case MX98713: default: { + struct medialeaf *mleaf; + unsigned char *p; + if (tp->mtable == NULL) { /* No EEPROM info, use generic code. */ + /* Not much that can be done. + Assume this a generic MII or SYM transceiver. */ + next_tick = 60*HZ; + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: network media monitor CSR6 %8.8x " + "CSR12 0x%2.2x.\n", + dev->name, inl(ioaddr + CSR6), csr12 & 0xff); + break; + } + mleaf = &tp->mtable->mleaf[tp->cur_index]; + p = mleaf->leafdata; + switch (mleaf->type) { + case 0: case 4: { + /* Type 0 serial or 4 SYM transceiver. Check the link beat bit. */ + int offset = mleaf->type == 4 ? 5 : 2; + s8 bitnum = p[offset]; + if (p[offset+1] & 0x80) { + if (tulip_debug > 1) + printk(KERN_DEBUG"%s: Transceiver monitor tick " + "CSR12=%#2.2x, no media sense.\n", + dev->name, csr12); + if (mleaf->type == 4) { + if (mleaf->media == 3 && (csr12 & 0x02)) + goto select_next_media; + } + break; + } + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: Transceiver monitor tick: CSR12=%#2.2x" + " bit %d is %d, expecting %d.\n", + dev->name, csr12, (bitnum >> 1) & 7, + (csr12 & (1 << ((bitnum >> 1) & 7))) != 0, + (bitnum >= 0)); + /* Check that the specified bit has the proper value. */ + if ((bitnum < 0) != + ((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) { + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Link beat detected for %s.\n", dev->name, + medianame[mleaf->media]); + if ((p[2] & 0x61) == 0x01) /* Bogus Znyx board. */ + goto actually_mii; + break; + } + if (tp->medialock) + break; + select_next_media: + if (--tp->cur_index < 0) { + /* We start again, but should instead look for default. */ + tp->cur_index = tp->mtable->leafcount - 1; + } + dev->if_port = tp->mtable->mleaf[tp->cur_index].media; + if (media_cap[dev->if_port] & MediaIsFD) + goto select_next_media; /* Skip FD entries. */ + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: No link beat on media %s," + " trying transceiver type %s.\n", + dev->name, medianame[mleaf->media & 15], + medianame[tp->mtable->mleaf[tp->cur_index].media]); + select_media(dev, 0); + /* Restart the transmit process. */ + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + next_tick = (24*HZ)/10; + break; + } + case 1: case 3: { /* 21140, 21142 MII */ + int mii_reg1, mii_reg5; + actually_mii: + mii_reg1 = mdio_read(dev, tp->phys[0], 1); + mii_reg5 = mdio_read(dev, tp->phys[0], 5); + if (tulip_debug > 1) + printk(KERN_INFO "%s: MII status %4.4x, Link partner report " + "%4.4x, CSR12 %2.2x, %cD.\n", + dev->name, mii_reg1, mii_reg5, csr12, + tp->full_duplex ? 'F' : 'H'); + if (mii_reg1 != 0xffff && (mii_reg1 & 0x0004) == 0) { + int new_reg1 = mdio_read(dev, tp->phys[0], 1); + if ((new_reg1 & 0x0004) == 0) { + printk(KERN_INFO "%s: No link beat on the MII interface," + " status then %4.4x now %4.4x.\n", + dev->name, mii_reg1, new_reg1); + if (tp->mtable && tp->mtable->has_nonmii) + goto select_next_media; + } + } + if (mii_reg5 == 0xffff || mii_reg5 == 0x0000) + ; /* No MII device or no link partner report */ + else if (tp->full_duplex_lock) + ; + else { + int negotiated = mii_reg5 & tp->advertising[0]; + int duplex = ((negotiated & 0x0100) != 0 + || (negotiated & 0x00C0) == 0x0040); + /* 100baseTx-FD or 10T-FD, but not 100-HD */ + if (tp->full_duplex != duplex) { + tp->full_duplex = duplex; + if (tp->full_duplex) + tp->csr6 |= 0x0200; + else + tp->csr6 &= ~0x0200; + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + if (tulip_debug > 0) /* Gurppp, should be >1 */ + printk(KERN_INFO "%s: Setting %s-duplex based on MII" + " Xcvr #%d parter capability of %4.4x.\n", + dev->name, tp->full_duplex ? "full" : "half", + tp->phys[0], mii_reg5); + } + } + next_tick = 60*HZ; + break; + } + case 2: /* 21142 serial block has no link beat. */ + default: + break; + } + } + break; + } + if (next_tick) { + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); + } +} + +/* Handle the 21143 uniquely: do autoselect with NWay, not the EEPROM list + of available transceivers. */ +static void t21142_timer(unsigned long data) +{ + struct device *dev = (struct device *)data; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + int csr12 = inl(ioaddr + CSR12); + int next_tick = 60*HZ; + int new_csr6 = 0; + + if (tulip_debug > 1) + printk(KERN_INFO"%s: 21142 negotiation status %8.8x, %s.\n", + dev->name, csr12, medianame[dev->if_port]); + if (dev->if_port == 3) { + if (csr12 & 2) { /* No 100mbps link beat, revert to 10mbps. */ + new_csr6 = 0x82420200; + outl(new_csr6, ioaddr + CSR6); + outl(0x0000, ioaddr + CSR13); + outl(0x0003FFFF, ioaddr + CSR14); + outl(0x0008, ioaddr + CSR15); + outl(0x0001, ioaddr + CSR13); + outl(0x1301, ioaddr + CSR12); /* Start NWay. */ + } + } else if ((csr12 & 0x7000) != 0x5000) { + /* Negotiation failed. Search media types. */ + if (tulip_debug > 1) + printk(KERN_INFO"%s: 21142 negotiation failed, status %8.8x.\n", + dev->name, csr12); + if (!(csr12 & 4)) { /* 10mbps link beat good. */ + new_csr6 = 0x82420000; + dev->if_port = 0; + outl(0, ioaddr + CSR13); + outl(0x0003FFFF, ioaddr + CSR14); + outl(t21142_csr15[dev->if_port], ioaddr + CSR15); + outl(t21142_csr13[dev->if_port], ioaddr + CSR13); + } else if (csr12 & 0x100) { + new_csr6 = 0x82420200; + dev->if_port = 2; + outl(0, ioaddr + CSR13); + outl(0x0003FFFF, ioaddr + CSR14); + outl(0x0008, ioaddr + CSR15); + outl(0x0001, ioaddr + CSR13); + } else { + /* Select 100mbps port to check for link beat. */ + new_csr6 = 0x83860000; + dev->if_port = 3; + outl(0, ioaddr + CSR13); + outl(0x0003FF7F, ioaddr + CSR14); + outl(8, ioaddr + CSR15); + outl(1, ioaddr + CSR13); + } + if (tulip_debug > 1) + printk(KERN_INFO"%s: Testing new 21142 media %s.\n", + dev->name, medianame[dev->if_port]); + if (new_csr6 != (tp->csr6 & ~0x00D5)) { + tp->csr6 &= 0x00D5; + tp->csr6 |= new_csr6; + outl(0x0301, ioaddr + CSR12); + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + } + } + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); +} + +static void t21142_lnk_change( struct device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + int csr12 = inl(ioaddr + CSR12); + + if (tulip_debug > 1) + printk(KERN_INFO"%s: 21142 link status interrupt %8.8x, CSR5 %x.\n", + dev->name, csr12, inl(ioaddr + CSR5)); + + if ((csr12 & 0x7000) == 0x5000) { + if (csr12 & 0x01800000) { + /* Switch to 100mbps mode. */ + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + if (csr12 & 0x01000000) { + dev->if_port = 5; + tp->csr6 = 0x83860200; + } else { + dev->if_port = 3; + tp->csr6 = 0x83860000; + } + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + } /* Else 10baseT-FD is handled automatically. */ + } else if (dev->if_port == 3) { + if (!(csr12 & 2)) + printk(KERN_INFO"%s: 21142 100baseTx link beat good.\n", + dev->name); + else + dev->if_port = 0; + } else if (dev->if_port == 0) { + if (!(csr12 & 4)) + printk(KERN_INFO"%s: 21142 10baseT link beat good.\n", + dev->name); + } else if (!(csr12 & 4)) { /* 10mbps link beat good. */ + printk(KERN_INFO"%s: 21142 10mpbs sensed media.\n", + dev->name); + dev->if_port = 0; + } else { /* 100mbps link beat good. */ + printk(KERN_INFO"%s: 21142 100baseTx sensed media.\n", + dev->name); + dev->if_port = 3; + tp->csr6 = 0x83860000; + outl(0x0003FF7F, ioaddr + CSR14); + outl(0x0301, ioaddr + CSR12); + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + } +} + + +static void mxic_timer(unsigned long data) +{ + struct device *dev = (struct device *)data; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + int next_tick = 60*HZ; + + if (tulip_debug > 3) { + printk(KERN_INFO"%s: MXIC negotiation status %8.8x.\n", dev->name, + inl(ioaddr + CSR12)); + } + if (next_tick) { + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); + } +} + +static void pnic_timer(unsigned long data) +{ + struct device *dev = (struct device *)data; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + int csr12 = inl(ioaddr + CSR12); + int next_tick = 60*HZ; + int new_csr6 = tp->csr6 & ~0x40C40200; + + if (media_cap[dev->if_port] & MediaIsMII) { + int negotiated = mdio_read(dev, tp->phys[0], 5) & tp->advertising[0]; + + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: LC82C168 negotiated capability %8.8x, " + "CSR5 %8.8x.\n", + dev->name, negotiated, inl(ioaddr + CSR5)); + + if (negotiated & 0x0380) /* 10 vs 100mbps */ + new_csr6 |= 0x812E0000; + else + new_csr6 |= 0x816E0000; + if (((negotiated & 0x0300) == 0x0100) /* Duplex */ + || (negotiated & 0x00C0) == 0x0040 + || tp->full_duplex_lock) { + tp->full_duplex = 1; + new_csr6 |= 0x0200; + } + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: LC82C168 MII PHY status %4.4x, Link " + "partner report %4.4x, csr6 %8.8x/%8.8x.\n", + dev->name, mdio_read(dev, tp->phys[0], 1), negotiated, + tp->csr6, inl(ioaddr + CSR6)); + } else { + int phy_reg = inl(ioaddr + 0xB8); + int csr5 = inl(ioaddr + CSR5); + + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: LC82C168 phy status %8.8x, CSR5 %8.8x.\n", + dev->name, phy_reg, csr5); + + if (phy_reg & 0x04000000) { /* Remote link fault */ + /*outl(0x0201F078, ioaddr + 0xB8);*/ + next_tick = 3*HZ; + } + if (inl(ioaddr + CSR5) & TPLnkFail) { /* 100baseTx link beat */ + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: %s link beat failed, CSR12 %4.4x, " + "CSR5 %8.8x, PHY %3.3x.\n", + dev->name, medianame[dev->if_port], csr12, + inl(ioaddr + CSR5), inl(ioaddr + 0xB8)); + if (tp->medialock) { + } else if (dev->if_port == 0) { + dev->if_port = 3; + outl(0x33, ioaddr + CSR12); + new_csr6 = 0x01860000; + outl(0x1F868, ioaddr + 0xB8); + } else { + dev->if_port = 0; + outl(0x32, ioaddr + CSR12); + new_csr6 = 0x00420000; + outl(0x1F078, ioaddr + 0xB8); + } + new_csr6 |= (tp->csr6 & 0xfdff); + next_tick = 3*HZ; + } else + new_csr6 = tp->csr6; + if (tp->full_duplex_lock || (phy_reg & 0x30000000) != 0) { + tp->full_duplex = 1; + new_csr6 |= 0x00000200; + } + } + if (tp->csr6 != new_csr6) { + tp->csr6 = new_csr6; + outl(tp->csr6 | 0x0002, ioaddr + CSR6); /* Restart Tx */ + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + dev->trans_start = jiffies; + if (tulip_debug > 0) /* Gurppp, should be >1 */ + printk(KERN_INFO "%s: Changing PNIC configuration to %s-duplex, " + "CSR6 %8.8x.\n", + dev->name, tp->full_duplex ? "full" : "half", new_csr6); + } + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); +} + +static void tulip_tx_timeout(struct device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + + if (media_cap[dev->if_port] & MediaIsMII) { + /* Do nothing -- the media monitor should handle this. */ + if (tulip_debug > 1) + printk(KERN_WARNING "%s: Transmit timeout using MII device.\n", + dev->name); + dev->trans_start = jiffies; + return; + } else if (tp->chip_id == DC21040) { + if (inl(ioaddr + CSR12) & 0x0002) { + printk(KERN_INFO "%s: transmit timed out, switching to %s media.\n", + dev->name, dev->if_port ? "10baseT" : "AUI"); + dev->if_port ^= 1; + outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13); + } + dev->trans_start = jiffies; + return; + } else if (tp->chip_id == DC21041) { + u32 csr12 = inl(ioaddr + CSR12); + + printk(KERN_WARNING "%s: 21041 transmit timed out, status %8.8x, CSR12 %8.8x," + " CSR13 %8.8x, CSR14 %8.8x, resetting...\n", + dev->name, inl(ioaddr + CSR5), csr12, + inl(ioaddr + CSR13), inl(ioaddr + CSR14)); + tp->mediasense = 1; + if (dev->if_port == 1 || dev->if_port == 2) + if (csr12 & 0x0004) { + dev->if_port = 2 - dev->if_port; + } else + dev->if_port = 0; + else + dev->if_port = 1; + select_media(dev, 0); + tp->stats.tx_errors++; + dev->trans_start = jiffies; + return; + } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142 + || tp->chip_id == MX98713) { + /* Stop the transmit process. */ + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, " + "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n", + dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12), + inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15)); + if (tp->mtable) { + if (--tp->cur_index < 0) { + /* We start again, but should instead look for default. */ + tp->cur_index = tp->mtable->leafcount - 1; + } + select_media(dev, 0); + printk(KERN_WARNING "%s: transmit timed out, switching to %s media.\n", + dev->name, dev->if_port ? "100baseTx" : "10baseT"); + } + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + tp->stats.tx_errors++; + dev->trans_start = jiffies; + return; + } else + printk(KERN_WARNING "%s: transmit timed out, status %8.8x, CSR12 %8.8x," + " resetting...\n", + dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12)); +#ifdef way_too_many_messages + printk(" Rx ring %8.8x: ", (int)tp->rx_ring); + for (i = 0; i < RX_RING_SIZE; i++) + printk(" %8.8x", (unsigned int)tp->rx_ring[i].status); + printk("\n Tx ring %8.8x: ", (int)tp->tx_ring); + for (i = 0; i < TX_RING_SIZE; i++) + printk(" %8.8x", (unsigned int)tp->tx_ring[i].status); + printk("\n"); +#endif + + /* Perhaps we should reinitialize the hardware here. */ + dev->if_port = 0; + /* Stop and restart the chip's Tx processes . */ + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + /* Trigger an immediate transmit demand. */ + outl(0, ioaddr + CSR1); + + dev->trans_start = jiffies; + tp->stats.tx_errors++; + return; +} + + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void +tulip_init_ring(struct device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int i; + + tp->tx_full = 0; + tp->cur_rx = tp->cur_tx = 0; + tp->dirty_rx = tp->dirty_tx = 0; + + for (i = 0; i < RX_RING_SIZE; i++) { + tp->rx_ring[i].status = 0x80000000; /* Owned by Tulip chip */ + tp->rx_ring[i].length = PKT_BUF_SZ; + { + /* Note the receive buffer must be longword aligned. + dev_alloc_skb() provides 16 byte alignment. But do *not* + use skb_reserve() to align the IP header! */ + struct sk_buff *skb; + skb = DEV_ALLOC_SKB(PKT_BUF_SZ); + tp->rx_skbuff[i] = skb; + if (skb == NULL) + break; /* Bad news! */ + skb->dev = dev; /* Mark as being used by this device. */ +#if LINUX_VERSION_CODE > 0x10300 + tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail); +#else + tp->rx_ring[i].buffer1 = virt_to_bus(skb->data); +#endif + } + tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]); + } + /* Mark the last entry as wrapping the ring. */ + tp->rx_ring[i-1].length = PKT_BUF_SZ | 0x02000000; + tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]); + + /* The Tx buffer descriptor is filled in as needed, but we + do need to clear the ownership bit. */ + for (i = 0; i < TX_RING_SIZE; i++) { + tp->tx_skbuff[i] = 0; + tp->tx_ring[i].status = 0x00000000; + tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]); + } + tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]); +} + +static int +tulip_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int entry; + u32 flag; + + /* 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) { + if (jiffies - dev->trans_start < TX_TIMEOUT) + return 1; + tulip_tx_timeout(dev); + return 1; + } + + /* Caution: the write order is important here, set the base address + with the "ownership" bits last. */ + + /* Calculate the next Tx descriptor entry. */ + entry = tp->cur_tx % TX_RING_SIZE; + + tp->tx_skbuff[entry] = skb; + tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data); + + if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */ + flag = 0x60000000; /* No interrupt */ + dev->tbusy = 0; + } else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) { + flag = 0xe0000000; /* Tx-done intr. */ + dev->tbusy = 0; + } else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) { + flag = 0x60000000; /* No Tx-done intr. */ + dev->tbusy = 0; + } else { + /* Leave room for set_rx_mode() to fill entries. */ + flag = 0xe0000000; /* Tx-done intr. */ + tp->tx_full = 1; + } + if (entry == TX_RING_SIZE-1) + flag |= 0xe2000000; + + tp->tx_ring[entry].length = skb->len | flag; + tp->tx_ring[entry].status = 0x80000000; /* Pass ownership to the chip. */ + tp->cur_tx++; + /* Trigger an immediate transmit demand. */ + outl(0, dev->base_addr + CSR1); + + dev->trans_start = jiffies; + + return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up + after the Tx thread. */ +static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs) +{ +#ifdef SA_SHIRQ /* Use the now-standard shared IRQ implementation. */ + struct device *dev = (struct device *)dev_instance; +#else + struct device *dev = (struct device *)(irq2dev_map[irq]); +#endif + + struct tulip_private *tp; + long ioaddr; + int csr5, work_budget = max_interrupt_work; + + if (dev == NULL) { + printk (KERN_ERR" tulip_interrupt(): irq %d for unknown device.\n", + irq); + return; + } + + ioaddr = dev->base_addr; + tp = (struct tulip_private *)dev->priv; + if (test_and_set_bit(0, (void*)&tp->interrupt)) { +#ifdef SMP_CHECK + printk(KERN_ERR "%s: Re-entering the interrupt handler with proc %d," + " proc %d already handling.\n", dev->name, + tp->smp_proc_id, hard_smp_processor_id()); +#else + printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); +#endif + return; + } + dev->interrupt = 1; +#ifdef SMP_CHECK + tp->smp_proc_id = hard_smp_processor_id(); +#endif + + do { + csr5 = inl(ioaddr + CSR5); + /* Acknowledge all of the current interrupt sources ASAP. */ + outl(csr5 & 0x0001ffff, ioaddr + CSR5); + + if (tulip_debug > 4) + printk(KERN_DEBUG "%s: interrupt csr5=%#8.8x new csr5=%#8.8x.\n", + dev->name, csr5, inl(dev->base_addr + CSR5)); + + if ((csr5 & (NormalIntr|AbnormalIntr)) == 0) + break; + + if (csr5 & (RxIntr | RxNoBuf)) + work_budget -= tulip_rx(dev); + + if (csr5 & (TxNoBuf | TxDied | TxIntr)) { + unsigned int dirty_tx; + + for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0; + dirty_tx++) { + int entry = dirty_tx % TX_RING_SIZE; + int status = tp->tx_ring[entry].status; + + if (status < 0) + break; /* It still hasn't been Txed */ + /* Check for Rx filter setup frames. */ + if (tp->tx_skbuff[entry] == NULL) + continue; + + if (status & 0x8000) { + /* There was an major error, log it. */ +#ifndef final_version + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", + dev->name, status); +#endif + tp->stats.tx_errors++; + if (status & 0x4104) tp->stats.tx_aborted_errors++; + if (status & 0x0C00) tp->stats.tx_carrier_errors++; + if (status & 0x0200) tp->stats.tx_window_errors++; + if (status & 0x0002) tp->stats.tx_fifo_errors++; + if ((status & 0x0080) && tp->full_duplex == 0) + tp->stats.tx_heartbeat_errors++; +#ifdef ETHER_STATS + if (status & 0x0100) tp->stats.collisions16++; +#endif + } else { +#ifdef ETHER_STATS + if (status & 0x0001) tp->stats.tx_deferred++; +#endif +#if LINUX_VERSION_CODE > 0x20127 + tp->stats.tx_bytes += tp->tx_ring[entry].length & 0x7ff; +#endif + tp->stats.collisions += (status >> 3) & 15; + tp->stats.tx_packets++; + } + + /* Free the original skb. */ +#if (LINUX_VERSION_CODE > 0x20155) + dev_kfree_skb(tp->tx_skbuff[entry]); +#else + dev_kfree_skb(tp->tx_skbuff[entry], FREE_WRITE); +#endif + tp->tx_skbuff[entry] = 0; + } + +#ifndef final_version + if (tp->cur_tx - dirty_tx > TX_RING_SIZE) { + printk(KERN_ERR "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", + dev->name, dirty_tx, tp->cur_tx, tp->tx_full); + dirty_tx += TX_RING_SIZE; + } +#endif + + if (tp->tx_full && dev->tbusy + && tp->cur_tx - dirty_tx < TX_RING_SIZE - 2) { + /* The ring is no longer full, clear tbusy. */ + tp->tx_full = 0; + dev->tbusy = 0; + mark_bh(NET_BH); + } + + tp->dirty_tx = dirty_tx; + if (csr5 & TxDied) { + if (tulip_debug > 1) + printk(KERN_WARNING "%s: The transmitter stopped!" + " CSR5 is %x, CSR6 %x.\n", + dev->name, csr5, inl(ioaddr + CSR6)); + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + } + } + + /* Log errors. */ + if (csr5 & AbnormalIntr) { /* Abnormal error summary bit. */ + if (csr5 & TxJabber) tp->stats.tx_errors++; + if (csr5 & TxFIFOUnderflow) { + if ((tp->csr6 & 0xC000) != 0xC000) + tp->csr6 += 0x4000; /* Bump up the Tx threshold */ + else + tp->csr6 |= 0x00200000; /* Store-n-forward. */ + /* Restart the transmit process. */ + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + } + if (csr5 & RxDied) { /* Missed a Rx frame. */ + tp->stats.rx_errors++; + tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; + } + if (csr5 & TimerInt) { + printk(KERN_ERR "%s: Something Wicked happened! %8.8x.\n", + dev->name, csr5); + /* Hmmmmm, it's not clear what to do here. */ + } + if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000) + && tp->chip_id == DC21142) { + if (tulip_debug > 1) + printk(KERN_INFO"%s: 21142 link change, CSR5 = %8.8x.\n", + dev->name, csr5); + t21142_lnk_change(dev); + } + /* Clear all error sources, included undocumented ones! */ + outl(0x0800f7ba, ioaddr + CSR5); + } + if (--work_budget < 0) { + if (tulip_debug > 1) + printk(KERN_WARNING "%s: Too much work at interrupt, " + "csr5=0x%8.8x.\n", dev->name, csr5); + /* Acknowledge all interrupt sources. */ + outl(0x8001ffff, ioaddr + CSR5); +#ifdef notdef + /* Clear all but standard interrupt sources. */ + outl((~csr5) & 0x0001ebef, ioaddr + CSR7); +#endif + break; + } + } while (1); + + if (tulip_debug > 3) + printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n", + dev->name, inl(ioaddr + CSR5)); + + dev->interrupt = 0; + clear_bit(0, (void*)&tp->interrupt); + return; +} + +static int +tulip_rx(struct device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int entry = tp->cur_rx % RX_RING_SIZE; + int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx; + int work_done = 0; + + if (tulip_debug > 4) + printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry, + tp->rx_ring[entry].status); + /* If we own the next entry, it's a new packet. Send it up. */ + while (tp->rx_ring[entry].status >= 0) { + s32 status = tp->rx_ring[entry].status; + + if (--rx_work_limit < 0) + break; + if ((status & 0x0300) != 0x0300) { + if ((status & 0xffff) != 0x7fff) { /* Ingore earlier buffers. */ + if (tulip_debug > 1) + printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " + "multiple buffers, status %8.8x!\n", + dev->name, status); + tp->stats.rx_length_errors++; + } + } else if (status & 0x8000) { + /* There was a fatal error. */ + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n", + dev->name, status); + tp->stats.rx_errors++; /* end of a packet.*/ + if (status & 0x0890) tp->stats.rx_length_errors++; + if (status & 0x0004) tp->stats.rx_frame_errors++; + if (status & 0x0002) tp->stats.rx_crc_errors++; + if (status & 0x0001) tp->stats.rx_fifo_errors++; + } else { + /* Omit the four octet CRC from the length. */ + short pkt_len = (status >> 16) - 4; + struct sk_buff *skb; + + /* Check if the packet is long enough to just accept without + copying to a properly sized skbuff. */ + if (pkt_len < rx_copybreak + && (skb = DEV_ALLOC_SKB(pkt_len+2)) != NULL) { + skb->dev = dev; + skb_reserve(skb, 2); /* 16 byte align the IP header */ +#if LINUX_VERSION_CODE < 0x10300 + memcpy(skb->data, tp->rx_ring[entry].buffer1, pkt_len); +#elif LINUX_VERSION_CODE < 0x20200 || defined(__alpha__) + memcpy(skb_put(skb, pkt_len), + bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len); +#else + eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1), + pkt_len, 0); + skb_put(skb, pkt_len); +#endif + work_done++; + } else { /* Pass up the skb already on the Rx ring. */ + skb = tp->rx_skbuff[entry]; + tp->rx_skbuff[entry] = NULL; +#ifndef final_version + { + void *temp = skb_put(skb, pkt_len); + if (bus_to_virt(tp->rx_ring[entry].buffer1) != temp) + printk(KERN_ERR "%s: Internal consistency error! The " + "skbuff addresses do not match in tulip_rx:" + " %p vs. %p / %p.\n", dev->name, + bus_to_virt(tp->rx_ring[entry].buffer1), + skb->head, temp); + } +#else + skb_put(skb, pkt_len); +#endif + } +#if LINUX_VERSION_CODE > 0x10300 + skb->protocol = eth_type_trans(skb, dev); +#else + skb->len = pkt_len; +#endif + netif_rx(skb); + dev->last_rx = jiffies; + tp->stats.rx_packets++; +#if LINUX_VERSION_CODE > 0x20127 + tp->stats.rx_bytes += pkt_len; +#endif + } + entry = (++tp->cur_rx) % RX_RING_SIZE; + } + + /* Refill the Rx ring buffers. */ + for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) { + entry = tp->dirty_rx % RX_RING_SIZE; + if (tp->rx_skbuff[entry] == NULL) { + struct sk_buff *skb; + skb = tp->rx_skbuff[entry] = DEV_ALLOC_SKB(PKT_BUF_SZ); + if (skb == NULL) + break; + skb->dev = dev; /* Mark as being used by this device. */ +#if LINUX_VERSION_CODE > 0x10300 + tp->rx_ring[entry].buffer1 = virt_to_bus(skb->tail); +#else + tp->rx_ring[entry].buffer1 = virt_to_bus(skb->data); +#endif + work_done++; + } + tp->rx_ring[entry].status = 0x80000000; + } + + return work_done; +} + +static int +tulip_close(struct device *dev) +{ + long ioaddr = dev->base_addr; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int i; + + dev->start = 0; + dev->tbusy = 1; + + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n", + dev->name, inl(ioaddr + CSR5)); + + /* Disable interrupts by clearing the interrupt mask. */ + outl(0x00000000, ioaddr + CSR7); + /* Stop the chip's Tx and Rx processes. */ + outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6); + /* 21040 -- Leave the card in 10baseT state. */ + if (tp->chip_id == DC21040) + outl(0x00000004, ioaddr + CSR13); + + tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; + + del_timer(&tp->timer); + +#ifdef SA_SHIRQ + free_irq(dev->irq, dev); +#else + free_irq(dev->irq); + irq2dev_map[dev->irq] = 0; +#endif + + /* Free all the skbuffs in the Rx queue. */ + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb = tp->rx_skbuff[i]; + tp->rx_skbuff[i] = 0; + tp->rx_ring[i].status = 0; /* Not owned by Tulip chip. */ + tp->rx_ring[i].length = 0; + tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */ + if (skb) { +#if LINUX_VERSION_CODE < 0x20100 + skb->free = 1; +#endif +#if (LINUX_VERSION_CODE > 0x20155) + dev_kfree_skb(skb); +#else + dev_kfree_skb(skb, FREE_WRITE); +#endif + } + } + for (i = 0; i < TX_RING_SIZE; i++) { + if (tp->tx_skbuff[i]) +#if (LINUX_VERSION_CODE > 0x20155) + dev_kfree_skb(tp->tx_skbuff[i]); +#else + dev_kfree_skb(tp->tx_skbuff[i], FREE_WRITE); +#endif + tp->tx_skbuff[i] = 0; + } + + + MOD_DEC_USE_COUNT; + + return 0; +} + +static struct enet_statistics * +tulip_get_stats(struct device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + + if (dev->start) + tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; + + return &tp->stats; +} + +#ifdef HAVE_PRIVATE_IOCTL +/* Provide ioctl() calls to examine the MII xcvr state. */ +static int private_ioctl(struct device *dev, struct ifreq *rq, int cmd) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + u16 *data = (u16 *)&rq->ifr_data; + int phy = tp->phys[0] & 0x1f; + long flags; + + switch(cmd) { + case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ + if (tp->mtable && tp->mtable->has_mii) + data[0] = phy; + else if (tp->chip_id == DC21142) + data[0] = 32; + else + return -ENODEV; + return 0; + case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ + if (data[0] == 32) { /* 21142 pseudo-MII */ + int csr12 = inl(ioaddr + CSR12); + int csr14 = inl(ioaddr + CSR14); + switch (data[1]) { + case 0: { + data[3] = ((csr14<<13)&0x4000) + ((csr14<<5)&0x1000); + break; } + case 1: + data[3] = 0x7848 + ((csr12&0x7000) == 0x5000 ? 0x20 : 0) + + (csr12&0x06 ? 0x04 : 0); + break; + case 4: { + int csr14 = inl(ioaddr + CSR14); + data[3] = ((csr14>>9)&0x0380) + ((csr14>>1)&0x20) + 1; + break; + } + case 5: data[3] = inl(ioaddr + CSR12) >> 16; break; + default: data[3] = 0; break; + } + } else { + save_flags(flags); + cli(); + data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); + restore_flags(flags); + } + return 0; + case SIOCDEVPRIVATE+2: /* Write the specified MII register */ + if (!suser()) + return -EPERM; + if (data[0] == 32) { /* 21142 pseudo-MII */ + } else { + save_flags(flags); + cli(); + mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); + restore_flags(flags); + } + return 0; + default: + return -EOPNOTSUPP; + } + + return -EOPNOTSUPP; +} +#endif /* HAVE_PRIVATE_IOCTL */ + +/* Set or clear the multicast filter for this adaptor. + Note that we only use exclusion around actually queueing the + new frame, not around filling tp->setup_frame. This is non-deterministic + when re-entered but still correct. */ + +/* The little-endian AUTODIN32 ethernet CRC calculation. + N.B. Do not use for bulk data, use a table-based routine instead. + This is common code and should be moved to net/core/crc.c */ +static unsigned const ethernet_polynomial_le = 0xedb88320U; +static inline unsigned ether_crc_le(int length, unsigned char *data) +{ + unsigned int crc = 0xffffffff; /* Initial value. */ + while(--length >= 0) { + unsigned char current_octet = *data++; + int bit; + for (bit = 8; --bit >= 0; current_octet >>= 1) { + if ((crc ^ current_octet) & 1) { + crc >>= 1; + crc ^= ethernet_polynomial_le; + } else + crc >>= 1; + } + } + return crc; +} + +#ifdef NEW_MULTICAST +static void set_rx_mode(struct device *dev) +#else +static void set_rx_mode(struct device *dev, int num_addrs, void *addrs) +#endif +{ + long ioaddr = dev->base_addr; + int csr6 = inl(ioaddr + CSR6) & ~0x00D5; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + + tp->csr6 &= ~0x00D5; + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + outl(csr6 | 0x00C0, ioaddr + CSR6); + /* Unconditionally log net taps. */ + printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name); + tp->csr6 |= 0xC0; + } else if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) { + /* Too many to filter perfectly -- accept all multicasts. */ + outl(csr6 | 0x0080, ioaddr + CSR6); + tp->csr6 |= 0x80; + } else { + u32 *setup_frm = tp->setup_frame; + struct dev_mc_list *mclist; + u16 *eaddrs; + u32 tx_flags; + int i; + + if (dev->mc_count > 14) { /* Must use a multicast hash table. */ + u16 hash_table[32]; + memset(hash_table, 0, sizeof(hash_table)); + /* This should work on big-endian machines as well. */ + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) + set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff, + hash_table); + /* Copy the hash table to the setup frame. + NOTE that only the LOW SHORTWORD of setup_frame[] is valid! */ + for (i = 0; i < 32; i++) + *setup_frm++ = hash_table[i]; + setup_frm += 7; + tx_flags = 0x08400000 | 192; + /* Too clever: i > 15 for fall-though. */ + } else { + /* We have <= 15 addresses so we can use the wonderful + 16 address perfect filtering of the Tulip. */ + for (i = 0, mclist = dev->mc_list; i < dev->mc_count; + i++, mclist = mclist->next) { + /* Note that only the low shortword of setup_frame[] is valid! + This code may require tweaking for non-x86 architectures! */ + eaddrs = (u16 *)mclist->dmi_addr; + *setup_frm++ = *eaddrs++; + *setup_frm++ = *eaddrs++; + *setup_frm++ = *eaddrs++; + } + /* Fill the rest of the table with our physical address. + Once again, only the low shortword or setup_frame[] is valid! */ + *setup_frm++ = 0xffff; + *setup_frm++ = 0xffff; + *setup_frm++ = 0xffff; + tx_flags = 0x08000000 | 192; + } + eaddrs = (u16 *)dev->dev_addr; + do { + *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; + } while (++i < 15); + /* Now add this frame to the Tx list. */ + if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) { + /* Same setup recently queued, we need not add it. */ + } else { + unsigned long flags; + unsigned int entry, dummy = 0; + + save_flags(flags); cli(); + entry = tp->cur_tx++ % TX_RING_SIZE; + + if (entry != 0) { + /* Avoid a chip errata by prefixing a dummy entry. */ + tp->tx_skbuff[entry] = 0; + tp->tx_ring[entry].length = + (entry == TX_RING_SIZE-1) ? 0x02000000 : 0; + tp->tx_ring[entry].buffer1 = 0; + /* race with chip, set DescOwned later */ + dummy = entry; + entry = tp->cur_tx++ % TX_RING_SIZE; + } + + tp->tx_skbuff[entry] = 0; + /* Put the setup frame on the Tx list. */ + if (entry == TX_RING_SIZE-1) + tx_flags |= 0x02000000; /* Wrap ring. */ + tp->tx_ring[entry].length = tx_flags; + tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame); + tp->tx_ring[entry].status = 0x80000000; + if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) { + dev->tbusy = 1; + tp->tx_full = 1; + } + if (dummy >= 0) + tp->tx_ring[dummy].status = DescOwned; + restore_flags(flags); + /* Trigger an immediate transmit demand. */ + outl(0, ioaddr + CSR1); + } + outl(csr6 | 0x0000, ioaddr + CSR6); + } +} + +#ifdef CARDBUS + +#include + +static dev_node_t *tulip_attach(dev_locator_t *loc) +{ + u16 dev_id; + u32 io; + u8 bus, devfn; + struct device *dev; + + if (loc->bus != LOC_PCI) return NULL; + bus = loc->b.pci.bus; devfn = loc->b.pci.devfn; + printk(KERN_INFO "tulip_attach(bus %d, function %d)\n", bus, devfn); + pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &io); + pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &dev_id); + io &= ~3; + dev = tulip_probe1(bus, devfn, NULL, DC21142, -1); + if (dev) { + dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL); + strcpy(node->dev_name, dev->name); + node->major = node->minor = 0; + node->next = NULL; + MOD_INC_USE_COUNT; + return node; + } + return NULL; +} + +static void tulip_detach(dev_node_t *node) +{ + struct device **devp, **next; + printk(KERN_INFO "tulip_detach(%s)\n", node->dev_name); + for (devp = &root_tulip_dev; *devp; devp = next) { + next = &((struct tulip_private *)(*devp)->priv)->next_module; + if (strcmp((*devp)->name, node->dev_name) == 0) break; + } + if (*devp) { + unregister_netdev(*devp); + kfree(*devp); + *devp = *next; + kfree(node); + MOD_DEC_USE_COUNT; + } +} + +struct driver_operations tulip_ops = { + "tulip_cb", tulip_attach, NULL, NULL, tulip_detach +}; + +#endif /* Cardbus support */ + + +#ifdef MODULE +#if LINUX_VERSION_CODE > 0x20118 +MODULE_AUTHOR("Donald Becker "); +MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet 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"); +#endif + +/* An additional parameter that may be passed in... */ +static int debug = -1; + +int +init_module(void) +{ + if (debug >= 0) + tulip_debug = debug; + +#ifdef CARDBUS + register_driver(&tulip_ops); + return 0; +#else + return tulip_probe(NULL); +#endif +} + +void +cleanup_module(void) +{ + struct device *next_dev; + +#ifdef CARDBUS + unregister_driver(&tulip_ops); +#endif + + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + while (root_tulip_dev) { + next_dev = ((struct tulip_private *)root_tulip_dev->priv)->next_module; + unregister_netdev(root_tulip_dev); + release_region(root_tulip_dev->base_addr, TULIP_TOTAL_SIZE); + kfree(root_tulip_dev); + root_tulip_dev = next_dev; + } +} + +#endif /* MODULE */ + +/* + * Local variables: + * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/pcnet32.c linux/drivers/net/pcnet32.c --- v2.2.13/linux/drivers/net/pcnet32.c Tue Jan 4 11:10:37 2000 +++ linux/drivers/net/pcnet32.c Tue Jan 4 10:12:17 2000 @@ -13,7 +13,7 @@ * This driver is for PCnet32 and PCnetPCI based ethercards */ -static const char *version = "pcnet32.c:v1.23 6.7.1999 tsbogend@alpha.franken.de\n"; +static const char *version = "pcnet32.c:v1.25kf 26.9.1999 tsbogend@alpha.franken.de\n"; #include #include @@ -39,16 +39,18 @@ #include #include #include +#include static unsigned int pcnet32_portlist[] __initdata = {0x300, 0x320, 0x340, 0x360, 0}; static int pcnet32_debug = 1; +static int tx_start = 1; /* Mapping -- 0:20, 1:64, 2:128, 3:~220 (depends on chip vers) */ #ifdef MODULE static struct device *pcnet32_dev = NULL; #endif -static const int max_interrupt_work = 20; +static const int max_interrupt_work = 80; static const int rx_copybreak = 200; #define PORT_AUI 0x00 @@ -156,6 +158,12 @@ * Michael Richard ) * added chip id for 79c973/975 (thanks to Zach Brown ) * v1.23 fixed small bug, when manual selecting MII speed/duplex + * v1.24 Applied Thomas' patch to use TxStartPoint and thus decrease TxFIFO + * underflows. Added tx_start_pt module parameter. Increased + * TX_RING_SIZE from 16 to 32. Added #ifdef'd code to use DXSUFLO + * for FAST[+] chipsets. + * v1.24ac Added SMP spinlocking - Alan Cox + * v1.25kf Added No Interrupt on successful Tx for some Tx's */ @@ -166,7 +174,7 @@ */ #ifndef PCNET32_LOG_TX_BUFFERS #define PCNET32_LOG_TX_BUFFERS 4 -#define PCNET32_LOG_RX_BUFFERS 4 +#define PCNET32_LOG_RX_BUFFERS 5 #endif #define TX_RING_SIZE (1 << (PCNET32_LOG_TX_BUFFERS)) @@ -255,12 +263,17 @@ struct sk_buff *rx_skbuff[RX_RING_SIZE]; struct pcnet32_access a; void *origmem; - int cur_rx, cur_tx; /* The next free ring entry */ - int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ + spinlock_t lock; /* Guard lock */ + unsigned int cur_rx, cur_tx; /* The next free ring entry */ + unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ struct net_device_stats stats; char tx_full; int options; int shared_irq:1, /* shared irq possible */ + ltint:1, +#ifdef DO_DXSUFLO + dxsuflo:1, /* disable transmit stop on uflo */ +#endif full_duplex:1, /* full duplex possible */ mii:1; /* mii port available */ #ifdef MODULE @@ -299,6 +312,10 @@ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, 0, 0, PCI_USES_IO|PCI_USES_MASTER, PCNET32_TOTAL_SIZE, pcnet32_probe1}, + { "AMD PCnetPCI series (IBM)", + PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, 0x1014, 0x2000, + PCI_USES_IO|PCI_USES_MASTER, PCNET32_TOTAL_SIZE, + pcnet32_probe1}, { "AMD PCnetHome series", PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_PCNETHOME, 0, 0, PCI_USES_IO|PCI_USES_MASTER, PCNET32_TOTAL_SIZE, @@ -448,8 +465,8 @@ int chip_idx; u16 sdid,svid; - pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &sdid); - pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &svid); + pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &svid); + pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &sdid); for (chip_idx = 0; pcnet32_tbl[chip_idx].vendor_id; chip_idx++) if ((pdev->vendor == pcnet32_tbl[chip_idx].vendor_id) && (pdev->device == pcnet32_tbl[chip_idx].device_id) && @@ -514,6 +531,10 @@ { struct pcnet32_private *lp; int i,media,fdx = 0, mii = 0; +#ifdef DO_DXSUFLO + int dxsuflo = 0; +#endif + int ltint = 0; int chip_version; char *chipname; char *priv; @@ -532,12 +553,14 @@ return ENODEV; } + chip_version = a->read_csr (ioaddr, 88) | (a->read_csr (ioaddr,89) << 16); if (pcnet32_debug > 2) printk(" PCnet chip version is %#x.\n", chip_version); if ((chip_version & 0xfff) != 0x003) return ENODEV; chip_version = (chip_version >> 12) & 0xffff; + switch (chip_version) { case 0x2420: chipname = "PCnet/PCI 79C970"; @@ -554,11 +577,33 @@ break; case 0x2623: chipname = "PCnet/FAST 79C971"; + /* To prevent Tx FIFO underflows ... (may increase Tx latency) */ + /* Set BCR18:NOUFLO to not start Tx until reach Tx start point */ + /* Looks like EEPROM sets BCR18:5/6 for BurstWrite/Read */ + a->write_bcr(ioaddr, 18, (a->read_bcr(ioaddr, 18) | 0x0800)); + /* Set CSR80:XMTSP, Tx start point = 20|64|128|248 bytes or size of frame */ + i = a->read_csr(ioaddr, 80) & ~0x0C00; /* Clear bits we are touching */ + a->write_csr(ioaddr, 80, i | (tx_start << 10)); fdx = 1; mii = 1; +#ifdef DO_DXSUFLO + dxsuflo = 1; +#endif + ltint = 1; break; case 0x2624: chipname = "PCnet/FAST+ 79C972"; + /* To prevent Tx FIFO underflows ... (may increase Tx latency) */ + /* Set BCR18:NOUFLO to not start Tx until reach Tx start point */ + /* Looks like EEPROM sets BCR18:5/6 for BurstWrite/Read */ + a->write_bcr(ioaddr, 18, (a->read_bcr(ioaddr, 18) | 0x0800)); + /* Set CSR80:XMTSP, Tx start point = 20|64|128|220 bytes or size of frame */ + i = a->read_csr(ioaddr, 80) & ~0x0C00; /* Clear bits we are touching */ + a->write_csr(ioaddr, 80, i | (tx_start << 10)); fdx = 1; mii = 1; +#ifdef DO_DXSUFLO + dxsuflo = 1; +#endif + ltint = 1; break; case 0x2625: chipname = "PCnet/FAST III 79C973"; @@ -602,6 +647,29 @@ for (i = 0; i < 6; i++) printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i)); + if (((chip_version + 1) & 0xfffe) == 0x2624) { /* Version 0x2623 or 0x2624 */ + i = a->read_csr(ioaddr, 80) & 0x0C00; /* Check tx_start_pt */ + printk("\n tx_start_pt(0x%04x):",i); + switch(i>>10) { + case 0: printk(" 20 bytes,"); break; + case 1: printk(" 64 bytes,"); break; + case 2: printk(" 128 bytes,"); break; + case 3: printk("~220 bytes,"); break; + } + i = a->read_bcr(ioaddr, 18); /* Check Burst/Bus control */ + printk(" BCR18(%x):",i&0xffff); + if (i & (1<<5)) printk("BurstWrEn "); + if (i & (1<<6)) printk("BurstRdEn "); + if (i & (1<<7)) printk("DWordIO "); + if (i & (1<<11)) printk("NoUFlow "); + i = a->read_bcr(ioaddr, 25); + printk("\n SRAMSIZE=0x%04x,",i<<8); + i = a->read_bcr(ioaddr, 26); + printk(" SRAM_BND=0x%04x,",i<<8); + i = a->read_bcr(ioaddr, 27); + if (i & (1<<14)) printk("LowLatRx,"); + } + dev->base_addr = ioaddr; request_region(ioaddr, PCNET32_TOTAL_SIZE, chipname); @@ -615,10 +683,17 @@ lp = (struct pcnet32_private *)(((unsigned long)priv+15) & ~15); memset(lp, 0, sizeof(*lp)); + + spin_lock_init(&lp->lock); + dev->priv = lp; lp->name = chipname; lp->shared_irq = shared; lp->full_duplex = fdx; +#ifdef DO_DXSUFLO + lp->dxsuflo = dxsuflo; +#endif + lp->ltint = ltint; lp->mii = mii; if (options[card_idx] > sizeof (options_mapping)) lp->options = PORT_ASEL; @@ -746,7 +821,7 @@ lp->a.write_bcr (ioaddr, 9, val); } - /* set/reset GPSI bit in test register */ + /* NOOP ??? set/reset GPSI bit in test register */ val = lp->a.read_csr (ioaddr, 124) & ~0x10; if ((lp->options & PORT_PORTSEL) == PORT_GPSI) val |= 0x10; @@ -760,6 +835,19 @@ val |= 0x08; lp->a.write_bcr (ioaddr, 32, val); } + +#ifdef DO_DXSUFLO + if (lp->dxsuflo) { /* Disable transmit stop on underflow */ + val = lp->a.read_csr (ioaddr, 3); + val |= 0x40; + lp->a.write_csr (ioaddr, 3, val); + } +#endif + if (lp->ltint) { /* Enable TxDone-intr inhibitor */ + val = lp->a.read_csr (ioaddr, 5); + val |= (1<<14); + lp->a.write_csr (ioaddr, 5, val); + } lp->init_block.mode = le16_to_cpu((lp->options & PORT_PORTSEL) << 7); lp->init_block.filter[0] = 0x00000000; @@ -856,7 +944,7 @@ lp->tx_ring[i].status = 0; } - lp->init_block.tlen_rlen = TX_RING_LEN_BITS | RX_RING_LEN_BITS; + lp->init_block.tlen_rlen = le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS); for (i = 0; i < 6; i++) lp->init_block.phys_addr[i] = dev->dev_addr[i]; lp->init_block.rx_ring = (u32)le32_to_cpu(virt_to_bus(lp->rx_ring)); @@ -890,6 +978,7 @@ { struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv; unsigned int ioaddr = dev->base_addr; + u16 status; int entry; unsigned long flags; @@ -937,8 +1026,23 @@ return 1; } - save_flags (flags); - cli (); + spin_lock_irqsave(&lp->lock, flags); + + /* Default status -- will not enable Successful-TxDone + * interrupt when that option is available to us. + */ + status = 0x8300; + if ((lp->ltint) && + ((lp->cur_tx - lp->dirty_tx == TX_RING_SIZE/2) || + (lp->cur_tx - lp->dirty_tx >= TX_RING_SIZE-2))) + { + /* Enable Successful-TxDone interrupt if we have + * 1/2 of, or nearly all of, our ring buffer Tx'd + * but not yet cleaned up. Thus, most of the time, + * we will not enable Successful-TxDone interrupts. + */ + status = 0x9300; + } /* Fill in a Tx ring entry */ @@ -954,7 +1058,8 @@ lp->tx_skbuff[entry] = skb; lp->tx_ring[entry].base = (u32)le32_to_cpu(virt_to_bus(skb->data)); - lp->tx_ring[entry].status = le16_to_cpu(0x8300); + + lp->tx_ring[entry].status = le16_to_cpu(status); lp->cur_tx++; lp->stats.tx_bytes += skb->len; @@ -968,7 +1073,7 @@ clear_bit (0, (void *)&dev->tbusy); else lp->tx_full = 1; - restore_flags(flags); + spin_unlock_irqrestore(&lp->lock, flags); return 0; } @@ -990,6 +1095,9 @@ ioaddr = dev->base_addr; lp = (struct pcnet32_private *)dev->priv; + + spin_lock(&lp->lock); + if (dev->interrupt) printk("%s: Re-entering the interrupt handler.\n", dev->name); @@ -1010,7 +1118,7 @@ pcnet32_rx(dev); if (csr0 & 0x0200) { /* Tx-done interrupt */ - int dirty_tx = lp->dirty_tx; + unsigned int dirty_tx = lp->dirty_tx; while (dirty_tx < lp->cur_tx) { int entry = dirty_tx & TX_RING_MOD_MASK; @@ -1028,14 +1136,27 @@ if (err_status & 0x04000000) lp->stats.tx_aborted_errors++; if (err_status & 0x08000000) lp->stats.tx_carrier_errors++; if (err_status & 0x10000000) lp->stats.tx_window_errors++; +#ifndef DO_DXSUFLO if (err_status & 0x40000000) { - /* Ackk! On FIFO errors the Tx unit is turned off! */ lp->stats.tx_fifo_errors++; + /* Ackk! On FIFO errors the Tx unit is turned off! */ /* Remove this verbosity later! */ - printk("%s: Tx FIFO error! Status %4.4x.\n", - dev->name, csr0); + printk("%s: Tx FIFO error! CSR0=%4.4x\n", + dev->name, csr0); must_restart = 1; } +#else + if (err_status & 0x40000000) { + lp->stats.tx_fifo_errors++; + if (! lp->dxsuflo) { /* If controller doesn't recover ... */ + /* Ackk! On FIFO errors the Tx unit is turned off! */ + /* Remove this verbosity later! */ + printk("%s: Tx FIFO error! CSR0=%4.4x\n", + dev->name, csr0); + must_restart = 1; + } + } +#endif } else { if (status & 0x1800) lp->stats.collisions++; @@ -1104,6 +1225,8 @@ dev->name, lp->a.read_csr (ioaddr, 0)); dev->interrupt = 0; + + spin_unlock(&lp->lock); return; } @@ -1252,12 +1375,11 @@ u16 saved_addr; unsigned long flags; - save_flags(flags); - cli(); + spin_lock_irqsave(&lp->lock, flags); saved_addr = lp->a.read_rap(ioaddr); lp->stats.rx_missed_errors = lp->a.read_csr (ioaddr, 112); lp->a.write_rap(ioaddr, saved_addr); - restore_flags(flags); + spin_unlock_irqrestore(&lp->lock, flags); return &lp->stats; } @@ -1371,18 +1493,22 @@ MODULE_PARM(debug, "i"); MODULE_PARM(max_interrupt_work, "i"); MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(tx_start_pt, "i"); MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); /* An additional parameter that may be passed in... */ static int debug = -1; +static int tx_start_pt = -1; int init_module(void) { if (debug > 0) pcnet32_debug = debug; + if ((tx_start_pt >= 0) && (tx_start_pt <= 3)) + tx_start = tx_start_pt; pcnet32_dev = NULL; return pcnet32_probe(NULL); diff -u --recursive --new-file v2.2.13/linux/drivers/net/plip.c linux/drivers/net/plip.c --- v2.2.13/linux/drivers/net/plip.c Sun Mar 7 15:47:46 1999 +++ linux/drivers/net/plip.c Tue Jan 4 10:12:17 2000 @@ -1257,6 +1257,7 @@ if (!dev_plip[i]->name) { printk(KERN_ERR "plip: memory squeeze.\n"); kfree(dev_plip[i]); + dev_plip[i] = NULL; break; } sprintf(dev_plip[i]->name, "plip%d", i); @@ -1264,6 +1265,7 @@ if (plip_init_dev(dev_plip[i],pb) || register_netdev(dev_plip[i])) { kfree(dev_plip[i]->name); kfree(dev_plip[i]); + dev_plip[i] = NULL; } else { i++; } diff -u --recursive --new-file v2.2.13/linux/drivers/net/sbni.c linux/drivers/net/sbni.c --- v2.2.13/linux/drivers/net/sbni.c Tue Jan 4 11:10:37 2000 +++ linux/drivers/net/sbni.c Tue Jan 4 10:12:17 2000 @@ -1371,7 +1371,7 @@ "movl %%edx,%%eax" : : "S" (mem), "D" (&crc32tab[0]), "c" (len), "d" (initial) - : "eax", "edx", "ecx" + : "eax" ); /* return crc; */ } diff -u --recursive --new-file v2.2.13/linux/drivers/net/sdla.c linux/drivers/net/sdla.c --- v2.2.13/linux/drivers/net/sdla.c Tue Dec 29 11:32:06 1998 +++ linux/drivers/net/sdla.c Tue Jan 4 10:12:17 2000 @@ -1666,7 +1666,7 @@ return(0); } -__initfunc(void sdla_setup(void)) +__initfunc(void sdla_c_setup(void)) { printk("%s.\n", version); register_frad(devname); @@ -1679,7 +1679,7 @@ { int result; - sdla_setup(); + sdla_c_setup(); if ((result = register_netdev(&sdla0)) != 0) return result; return 0; diff -u --recursive --new-file v2.2.13/linux/drivers/net/sdla_chdlc.c linux/drivers/net/sdla_chdlc.c --- v2.2.13/linux/drivers/net/sdla_chdlc.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sdla_chdlc.c Tue Jan 4 10:12:17 2000 @@ -0,0 +1,2785 @@ +/***************************************************************************** +* sdla_chdlc.c WANPIPE(tm) Multiprotocol WAN Link Driver. Cisco HDLC module. +* +* Authors: Nenad Corbic +* Gideon Hack +* +* Copyright: (c) 1995-1999 Sangoma Technologies Inc. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version +* 2 of the License, or (at your option) any later version. +* ============================================================================ +* Nov 20, 1999 Nenad Corbic Fixed zero length API bug. +* Sep 30, 1999 Nenad Corbic Fixed dynamic IP and route setup. +* Sep 23, 1999 Nenad Corbic Added SMP support, fixed tracing +* Sep 13, 1999 Nenad Corbic Split up Port 0 and 1 into separate devices. +* Jun 02, 1999 Gideon Hack Added support for the S514 adapter. +* Oct 30, 1998 Jaspreet Singh Added Support for CHDLC API (HDLC STREAMING). +* Oct 28, 1998 Jaspreet Singh Added Support for Dual Port CHDLC. +* Aug 07, 1998 David Fong Initial version. +*****************************************************************************/ + +#include +#include /* printk(), and other useful stuff */ +#include /* offsetof(), etc. */ +#include /* return codes */ +#include /* inline memset(), etc. */ +#include /* kmalloc(), kfree() */ +#include /* WAN router definitions */ +#include /* WANPIPE common user API definitions */ +#include /* ARPHRD_* defines */ +#include +#include +#include /* sockaddr_in */ +#include +#include +#include /* htons(), etc. */ +#include +#include + +#include /* CHDLC firmware API definitions */ + +/****** Defines & Macros ****************************************************/ + +#ifdef _DEBUG_ +#define STATIC +#else +#define STATIC static +#endif + +/* reasons for enabling the timer interrupt on the adapter */ +#define TMR_INT_ENABLED_UDP 0x0001 +#define TMR_INT_ENABLED_UPDATE 0x0002 + +#define CHDLC_DFLT_DATA_LEN 1500 /* default MTU */ +#define CHDLC_HDR_LEN 1 + +#define IFF_POINTTOPOINT 0x10 + +#define WANPIPE 0x00 +#define API 0x01 +#define CHDLC_API 0x01 + +#define PORT(x) (x == 0 ? "PRIMARY" : "SECONDARY" ) + + +/******Data Structures*****************************************************/ + +/* This structure is placed in the private data area of the device structure. + * The card structure used to occupy the private area but now the following + * structure will incorporate the card structure along with CHDLC specific data + */ + +typedef struct chdlc_private_area +{ + sdla_t *card; + int TracingEnabled; /* For enabling Tracing */ + unsigned long curr_trace_addr; /* Used for Tracing */ + unsigned long start_trace_addr; + unsigned long end_trace_addr; + unsigned long base_addr_trace_buffer; + unsigned long end_addr_trace_buffer; + unsigned short number_trace_elements; + unsigned available_buffer_space; + unsigned long router_start_time; + unsigned char route_status; + unsigned char route_removed; + unsigned long tick_counter; /* For 5s timeout counter */ + unsigned long router_up_time; + u32 IP_address; /* IP addressing */ + u32 IP_netmask; + unsigned char mc; /* Mulitcast support on/off */ + unsigned short udp_pkt_lgth; /* udp packet processing */ + char udp_pkt_src; + char udp_pkt_data[MAX_LGTH_UDP_MGNT_PKT]; + unsigned short timer_int_enabled; + char update_comms_stats; /* updating comms stats */ + //FIXME: add driver stats as per frame relay! + +} chdlc_private_area_t; + +/* Route Status options */ +#define NO_ROUTE 0x00 +#define ADD_ROUTE 0x01 +#define ROUTE_ADDED 0x02 +#define REMOVE_ROUTE 0x03 + + +/* variable for keeping track of enabling/disabling FT1 monitor status */ +static int rCount = 0; + +/* variable for tracking how many interfaces to open for WANPIPE on the + two ports */ + +extern void disable_irq(unsigned int); +extern void enable_irq(unsigned int); + +/****** Function Prototypes *************************************************/ +/* WAN link driver entry points. These are called by the WAN router module. */ +static int update (wan_device_t* wandev); +static int new_if (wan_device_t* wandev, struct device* dev, + wanif_conf_t* conf); +static int del_if (wan_device_t* wandev, struct device* dev); + +/* Network device interface */ +static int if_init (struct device* dev); +static int if_open (struct device* dev); +static int if_close (struct device* dev); +static int if_header (struct sk_buff* skb, struct device* dev, + unsigned short type, void* daddr, void* saddr, unsigned len); +#ifdef LINUX_2_1 +static int if_rebuild_hdr (struct sk_buff *skb); +#else +static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr, + struct sk_buff* skb); +#endif +static int if_send (struct sk_buff* skb, struct device* dev); +static struct enet_statistics* if_stats (struct device* dev); + +/* CHDLC Firmware interface functions */ +static int chdlc_configure (sdla_t* card, void* data); +static int chdlc_comm_enable (sdla_t* card); +static int chdlc_comm_disable (sdla_t* card); +static int chdlc_read_version (sdla_t* card, char* str); +static int chdlc_set_intr_mode (sdla_t* card, unsigned mode); +static int chdlc_send (sdla_t* card, void* data, unsigned len); +static int chdlc_read_comm_err_stats (sdla_t* card); +static int chdlc_read_op_stats (sdla_t* card); + + +/* Miscellaneous CHDLC Functions */ +static int set_chdlc_config (sdla_t* card); +static void init_chdlc_tx_rx_buff( sdla_t* card, struct device *dev ); +static int chdlc_error (sdla_t *card, int err, CHDLC_MAILBOX_STRUCT *mb); +static int process_chdlc_exception(sdla_t *card); +static int process_global_exception(sdla_t *card); +static int update_comms_stats(sdla_t* card, + chdlc_private_area_t* chdlc_priv_area); +static int configure_ip (sdla_t* card); +static int unconfigure_ip (sdla_t* card); +static void process_route(sdla_t *card); +static void port_set_state (sdla_t *card, int); + + +/* Interrupt handlers */ +static void wpc_isr (sdla_t* card); +static void rx_intr (sdla_t* card); +static void timer_intr(sdla_t *); + +/* Miscellaneous functions */ +static int chk_bcast_mcast_addr(sdla_t* card, struct device* dev, + struct sk_buff *skb); +static int reply_udp( unsigned char *data, unsigned int mbox_len ); +static int intr_test( sdla_t* card, struct device *dev ); +static int udp_pkt_type( struct sk_buff *skb , sdla_t* card); +static int store_udp_mgmt_pkt(char udp_pkt_src, sdla_t* card, + struct sk_buff *skb, struct device* dev, + chdlc_private_area_t* chdlc_priv_area); +static int process_udp_mgmt_pkt(sdla_t* card, struct device* dev, + chdlc_private_area_t* chdlc_priv_area); +static unsigned short calc_checksum (char *, int); +static void s508_lock (sdla_t *card, unsigned long *smp_flags); +static void s508_unlock (sdla_t *card, unsigned long *smp_flags); + + +static int Intr_test_counter; +/****** Public Functions ****************************************************/ + +/*============================================================================ + * Cisco HDLC protocol initialization routine. + * + * This routine is called by the main WANPIPE module during setup. At this + * point adapter is completely initialized and firmware is running. + * o read firmware version (to make sure it's alive) + * o configure adapter + * o initialize protocol-specific fields of the adapter data space. + * + * Return: 0 o.k. + * < 0 failure. + */ +int wpc_init (sdla_t* card, wandev_conf_t* conf) +{ + unsigned char port_num; + int err; + unsigned long max_permitted_baud = 0; + + union + { + char str[80]; + } u; + volatile CHDLC_MAILBOX_STRUCT* mb; + CHDLC_MAILBOX_STRUCT* mb1; + unsigned long timeout; + + /* Verify configuration ID */ + if (conf->config_id != WANCONFIG_CHDLC) { + printk(KERN_INFO "%s: invalid configuration ID %u!\n", + card->devname, conf->config_id); + return -EINVAL; + } + + /* Find out which Port to use */ + if ((conf->comm_port == WANOPT_PRI) || (conf->comm_port == WANOPT_SEC)){ + if (card->next){ + + if (conf->comm_port != card->next->u.c.comm_port){ + card->u.c.comm_port = conf->comm_port; + }else{ + printk(KERN_ERR "%s: ERROR - %s port used!\n", + card->wandev.name, PORT(conf->comm_port)); + return -EINVAL; + } + }else{ + card->u.c.comm_port = conf->comm_port; + } + }else{ + printk(KERN_ERR "%s: ERROR - Invalid Port Selected!\n", + card->wandev.name); + return -EINVAL; + } + + + /* Initialize protocol-specific fields */ + if(card->hw.type != SDLA_S514){ + + if (card->u.c.comm_port == WANOPT_PRI){ + card->mbox = (void *) card->hw.dpmbase; + }else{ + card->mbox = (void *) card->hw.dpmbase + + SEC_BASE_ADDR_MB_STRUCT - PRI_BASE_ADDR_MB_STRUCT; + } + }else{ + /* for a S514 adapter, set a pointer to the actual mailbox in the */ + /* allocated virtual memory area */ + if (card->u.c.comm_port == WANOPT_PRI){ + card->mbox = (void *) card->hw.dpmbase + PRI_BASE_ADDR_MB_STRUCT; + }else{ + card->mbox = (void *) card->hw.dpmbase + SEC_BASE_ADDR_MB_STRUCT; + } + } + + mb = mb1 = card->mbox; + + if (!card->configured){ + + /* The board will place an 'I' in the return code to indicate that it is + ready to accept commands. We expect this to be completed in less + than 1 second. */ + + timeout = jiffies; + while (mb->return_code != 'I') /* Wait 1s for board to initialize */ + if ((jiffies - timeout) > 1*HZ) break; + + if (mb->return_code != 'I') { + printk(KERN_INFO + "%s: Initialization not completed by adapter\n", + card->devname); + printk(KERN_INFO "Please contact Sangoma representative.\n"); + return -EIO; + } + } + + /* Read firmware version. Note that when adapter initializes, it + * clears the mailbox, so it may appear that the first command was + * executed successfully when in fact it was merely erased. To work + * around this, we execute the first command twice. + */ + + if (chdlc_read_version(card, u.str)) + return -EIO; + + printk(KERN_INFO "%s: Running Cisco HDLC firmware v%s\n", + card->devname, u.str); + + card->isr = &wpc_isr; + card->poll = NULL; + card->exec = NULL; + card->wandev.update = &update; + card->wandev.new_if = &new_if; + card->wandev.del_if = &del_if; + card->wandev.state = WAN_DUALPORT; + card->wandev.udp_port = conf->udp_port; + + card->wandev.new_if_cnt = 0; + + /* This is for the ports link state */ + card->u.c.state = WAN_DISCONNECTED; + + /* reset the number of times the 'update()' proc has been called */ + card->u.c.update_call_count = 0; + + card->wandev.ttl = conf->ttl; + card->wandev.interface = conf->interface; + + if ((card->u.c.comm_port == WANOPT_SEC && conf->interface == WANOPT_V35)&& + card->hw.type != SDLA_S514){ + printk(KERN_INFO "%s: ERROR - V35 Interface not supported on S508 %s port \n", + card->devname, PORT(card->u.c.comm_port)); + return -EIO; + } + + card->wandev.clocking = conf->clocking; + + port_num = card->u.c.comm_port; + + /* Setup Port Bps */ + + if(card->wandev.clocking) { + + if(port_num == WANOPT_PRI) { + /* For Primary Port 0 */ + max_permitted_baud = + (card->hw.type == SDLA_S514) ? + PRI_MAX_BAUD_RATE_S514 : + PRI_MAX_BAUD_RATE_S508; + } + else if(port_num == WANOPT_SEC) { + /* For Secondary Port 1 */ + max_permitted_baud = + (card->hw.type == SDLA_S514) ? + SEC_MAX_BAUD_RATE_S514 : + SEC_MAX_BAUD_RATE_S508; + } + + if(conf->bps > max_permitted_baud) { + conf->bps = max_permitted_baud; + printk(KERN_INFO "%s: Baud too high!\n", + card->wandev.name); + printk(KERN_INFO "%s: Baud rate set to %lu bps\n", + card->wandev.name, max_permitted_baud); + } + + card->wandev.bps = conf->bps; + }else{ + card->wandev.bps = 0; + } + + /* Setup the Port MTU */ + if(port_num == WANOPT_PRI) { + /* For Primary Port 0 */ + card->wandev.mtu = + (conf->mtu >= MIN_LGTH_CHDLC_DATA_CFG) ? + min(conf->mtu, PRI_MAX_NO_DATA_BYTES_IN_FRAME) : + CHDLC_DFLT_DATA_LEN; + } else if(port_num == WANOPT_SEC) { + /* For Secondary Port 1 */ + card->wandev.mtu = + (conf->mtu >= MIN_LGTH_CHDLC_DATA_CFG) ? + min(conf->mtu, SEC_MAX_NO_DATA_BYTES_IN_FRAME) : + CHDLC_DFLT_DATA_LEN; + } + + /* Set up the interrupt status area */ + /* Read the CHDLC Configuration and obtain: + * Ptr to shared memory infor struct + * Use this pointer to calculate the value of card->u.c.flags ! + */ + mb1->buffer_length = 0; + mb1->command = READ_CHDLC_CONFIGURATION; + err = sdla_exec(mb1) ? mb1->return_code : CMD_TIMEOUT; + if(err != COMMAND_OK) { + clear_bit(1, (void*)&card->wandev.critical); + + if(card->hw.type != SDLA_S514) + enable_irq(card->hw.irq); + + chdlc_error(card, err, mb1); + return -EIO; + } + + if(card->hw.type == SDLA_S514){ + card->u.c.flags = (void *)(card->hw.dpmbase + + (((CHDLC_CONFIGURATION_STRUCT *)mb1->data)-> + ptr_shared_mem_info_struct)); + }else{ + card->u.c.flags = (void *)(card->hw.dpmbase + + (((CHDLC_CONFIGURATION_STRUCT *)mb1->data)-> + ptr_shared_mem_info_struct % SDLA_WINDOWSIZE)); + } + + return 0; +} + +/******* WAN Device Driver Entry Points *************************************/ + +/*============================================================================ + * Update device status & statistics + * This procedure is called when updating the PROC file system and returns + * various communications statistics. These statistics are accumulated from 3 + * different locations: + * 1) The 'if_stats' recorded for the device. + * 2) Communication error statistics on the adapter. + * 3) CHDLC operational statistics on the adapter. + * The board level statistics are read during a timer interrupt. Note that we + * read the error and operational statistics during consecitive timer ticks so + * as to minimize the time that we are inside the interrupt handler. + * + */ +static int update (wan_device_t* wandev) +{ + sdla_t* card = wandev->private; + struct device* dev = card->wandev.dev; + volatile chdlc_private_area_t* chdlc_priv_area = dev->priv; + SHARED_MEMORY_INFO_STRUCT *flags; + unsigned long timeout; + + /* sanity checks */ + if((wandev == NULL) || (wandev->private == NULL)) + return -EFAULT; + + if(wandev->state == WAN_UNCONFIGURED) + return -ENODEV; + + /* more sanity checks */ + if(!card->u.c.flags) + return -ENODEV; + if(test_bit(1, (void*)&card->wandev.critical)) + return -EAGAIN; + + if(!dev->start) + return -ENODEV; + + flags = card->u.c.flags; + if(chdlc_priv_area->update_comms_stats){ + return -EAGAIN; + } + + /* we will need 2 timer interrupts to complete the */ + /* reading of the statistics */ + chdlc_priv_area->update_comms_stats = 2; + flags->interrupt_info_struct.interrupt_permission |= APP_INT_ON_TIMER; + chdlc_priv_area->timer_int_enabled = TMR_INT_ENABLED_UPDATE; + + /* wait a maximum of 1 second for the statistics to be updated */ + timeout = jiffies; + for(;;) { + if(chdlc_priv_area->update_comms_stats == 0) + break; + if ((jiffies - timeout) > (1 * HZ)){ + chdlc_priv_area->update_comms_stats = 0; + chdlc_priv_area->timer_int_enabled &= + ~TMR_INT_ENABLED_UPDATE; + return -EAGAIN; + } + } + + return 0; +} + + +/*============================================================================ + * Create new logical channel. + * This routine is called by the router when ROUTER_IFNEW IOCTL is being + * handled. + * o parse media- and hardware-specific configuration + * o make sure that a new channel can be created + * o allocate resources, if necessary + * o prepare network device structure for registaration. + * + * Return: 0 o.k. + * < 0 failure (channel will not be created) + */ +static int new_if (wan_device_t* wandev, struct device* dev, wanif_conf_t* conf) +{ + sdla_t* card = wandev->private; + chdlc_private_area_t* chdlc_priv_area; + + if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)) { + printk(KERN_INFO "%s: invalid interface name!\n", + card->devname); + return -EINVAL; + } + + /* allocate and initialize private data */ + chdlc_priv_area = kmalloc(sizeof(chdlc_private_area_t), GFP_KERNEL); + + if(chdlc_priv_area == NULL) + return -ENOMEM; + + memset(chdlc_priv_area, 0, sizeof(chdlc_private_area_t)); + + chdlc_priv_area->card = card; + + /* initialize data */ + strcpy(card->u.c.if_name, conf->name); + + if(card->wandev.new_if_cnt > 0) { + kfree(chdlc_priv_area); + return -EEXIST; + } + + card->wandev.new_if_cnt++; + + chdlc_priv_area->TracingEnabled = 0; + chdlc_priv_area->route_status = NO_ROUTE; + chdlc_priv_area->route_removed = 0; + + /* Setup protocol options */ + + card->u.c.protocol_options = 0; + + if (conf->ignore_dcd == WANOPT_YES){ + card->u.c.protocol_options |= IGNORE_DCD_FOR_LINK_STAT; + } + + if (conf->ignore_cts == WANOPT_YES){ + card->u.c.protocol_options |= IGNORE_CTS_FOR_LINK_STAT; + } + + if (conf->ignore_keepalive == WANOPT_YES) { + card->u.c.protocol_options |= IGNORE_KPALV_FOR_LINK_STAT; + card->u.c.kpalv_tx = MIN_Tx_KPALV_TIMER; + card->u.c.kpalv_rx = MIN_Rx_KPALV_TIMER; + card->u.c.kpalv_err = MIN_KPALV_ERR_TOL; + + } else { /* Do not ignore keepalives */ + + card->u.c.kpalv_tx = + (conf->keepalive_tx_tmr - MIN_Tx_KPALV_TIMER) >= 0 ? + min (conf->keepalive_tx_tmr, MAX_Tx_KPALV_TIMER) : + DEFAULT_Tx_KPALV_TIMER; + + card->u.c.kpalv_rx = + (conf->keepalive_rx_tmr - MIN_Rx_KPALV_TIMER) >= 0 ? + min (conf->keepalive_rx_tmr, MAX_Rx_KPALV_TIMER) : + DEFAULT_Rx_KPALV_TIMER; + + card->u.c.kpalv_err = + (conf->keepalive_err_margin - MIN_KPALV_ERR_TOL) >= 0 ? + min (conf->keepalive_err_margin, MAX_KPALV_ERR_TOL) : + DEFAULT_KPALV_ERR_TOL; + } + + + /* Setup slarp timer to control delay between slarps + */ + card->u.c.slarp_timer = + (conf->slarp_timer - MIN_SLARP_REQ_TIMER) >=0 ? + min (conf->slarp_timer, MAX_SLARP_REQ_TIMER) : + DEFAULT_SLARP_REQ_TIMER; + + + /* If HDLC_STRAMING is enabled then IGNORE DCD, CTS and KEEPALIVES + * are automatically ignored + */ + if (conf->hdlc_streaming == WANOPT_YES) { + printk(KERN_INFO "%s: Enabling HDLC STREAMING Mode\n", + wandev->name); + card->u.c.protocol_options = HDLC_STREAMING_MODE; + } + + + /* Setup wanpipe as a router (WANPIPE) or as an API */ + if( strcmp(conf->usedby, "WANPIPE") == 0) { + printk(KERN_INFO "%s: Running in WANPIPE mode !\n",wandev->name); + card->u.c.usedby = WANPIPE; + + } else if( strcmp(conf->usedby, "API") == 0){ + +#ifdef CHDLC_API + card->u.c.usedby = API; + printk(KERN_INFO "%s: Running in API mode !\n",wandev->name); +#else + printk(KERN_INFO "%s: API Mode is not supported!\n", + wandev->name); + printk(KERN_INFO "%s: Chdlc API patch can be obtained from Sangoma Tech.\n", + wandev->name); + kfree(chdlc_priv_area); + return -EINVAL; +#endif + } + + + /* Get Multicast Information */ + chdlc_priv_area->mc = conf->mc; + + /* prepare network device data space for registration */ + dev->name = card->u.c.if_name; + dev->init = &if_init; + dev->priv = chdlc_priv_area; + + return 0; +} + +/*============================================================================ + * Delete logical channel. + */ +static int del_if (wan_device_t* wandev, struct device* dev) +{ + +/* FIXME: This code generates kernel panic during + router stop!. Investigate futher. + (Error is dereferencing a NULL pointer) + + if(dev->priv){ + + kfree(dev->priv); + dev->priv = NULL; + + } +*/ + return 0; +} + + +/****** Network Device Interface ********************************************/ + +/*============================================================================ + * Initialize Linux network interface. + * + * This routine is called only once for each interface, during Linux network + * interface registration. Returning anything but zero will fail interface + * registration. + */ +static int if_init (struct device* dev) + { + chdlc_private_area_t* chdlc_priv_area = dev->priv; + sdla_t* card = chdlc_priv_area->card; + wan_device_t* wandev = &card->wandev; +#ifndef LINUX_2_1 + int i; +#endif + + /* Initialize device driver entry points */ + dev->open = &if_open; + dev->stop = &if_close; + dev->hard_header = &if_header; + dev->rebuild_header = &if_rebuild_hdr; + dev->hard_start_xmit = &if_send; + dev->get_stats = &if_stats; + + /* Initialize media-specific parameters */ + dev->flags |= IFF_POINTTOPOINT; + + /* Enable Mulitcasting if user selected */ + if (chdlc_priv_area->mc == WANOPT_YES){ + dev->flags |= IFF_MULTICAST; + } + +#ifndef LINUX_2_1 + dev->family = AF_INET; +#endif + dev->type = ARPHRD_PPP; /* ARP hw type -- dummy value */ + dev->mtu = card->wandev.mtu; + dev->hard_header_len = CHDLC_HDR_LEN; + + /* Initialize hardware parameters */ + dev->irq = wandev->irq; + dev->dma = wandev->dma; + dev->base_addr = wandev->ioport; + dev->mem_start = wandev->maddr; + dev->mem_end = wandev->maddr + wandev->msize - 1; + + /* Set transmit buffer queue length + * If too low packets will not be retransmitted + * by stack. + */ + dev->tx_queue_len = 100; + + /* Initialize socket buffers */ +#ifdef LINUX_2_1 + dev_init_buffers(dev); +#else + for (i = 0; i < DEV_NUMBUFFS; ++i) + skb_queue_head_init(&dev->buffs[i]); +#endif + + return 0; +} + +/*============================================================================ + * Open network interface. + * o enable communications and interrupts. + * o prevent module from unloading by incrementing use count + * + * Return 0 if O.k. or errno. + */ +static int if_open (struct device* dev) +{ + chdlc_private_area_t* chdlc_priv_area = dev->priv; + sdla_t* card = chdlc_priv_area->card; + SHARED_MEMORY_INFO_STRUCT* flags = card->u.c.flags; + struct timeval tv; + int err = 0; + + /* Only one open per interface is allowed */ + + if(dev->start) + return -EBUSY; + + if(test_and_set_bit(1, (void*)&card->wandev.critical)) { + return -EAGAIN; + } + + /* Setup the Board for CHDLC */ + if (set_chdlc_config(card)) { + clear_bit(1, (void*)&card->wandev.critical); + return -EIO; + } + + if (!card->configured && !card->wandev.piggyback){ + /* Perform interrupt testing */ + err = intr_test(card, dev); + + if(err || (Intr_test_counter < MAX_INTR_TEST_COUNTER)) { + printk(KERN_ERR "%s: Interrupt test failed (%i)\n", + card->devname, Intr_test_counter); + printk(KERN_ERR "%s: Please choose another interrupt\n", + card->devname); + clear_bit(1, (void*)&card->wandev.critical); + return -EIO; + } + + printk(KERN_INFO "%s: Interrupt test passed (%i)\n", + card->devname, Intr_test_counter); + card->configured = 1; + }else{ + printk(KERN_INFO "%s: Card configured, skip interrupt test\n", + card->devname); + } + + /* Initialize Rx/Tx buffer control fields */ + init_chdlc_tx_rx_buff(card, dev); + + /* Set interrupt mode and mask */ + if (chdlc_set_intr_mode(card, APP_INT_ON_RX_FRAME | + APP_INT_ON_GLOBAL_EXCEP_COND | + APP_INT_ON_TX_FRAME | + APP_INT_ON_CHDLC_EXCEP_COND | APP_INT_ON_TIMER)){ + + clear_bit(1, (void*)&card->wandev.critical); + return -EIO; + } + + + /* Mask the Transmit and Timer interrupt */ + flags->interrupt_info_struct.interrupt_permission &= + ~(APP_INT_ON_TX_FRAME | APP_INT_ON_TIMER); + + + /* Enable communications */ + if (chdlc_comm_enable(card)) { + clear_bit(1, (void*)&card->wandev.critical); + return -EIO; + } + + clear_bit(1, (void*)&card->wandev.critical); + + port_set_state(card, WAN_CONNECTING); + do_gettimeofday(&tv); + chdlc_priv_area->router_start_time = tv.tv_sec; + + dev->interrupt = 0; + dev->tbusy = 0; + dev->start = 1; + dev->flags |= IFF_POINTTOPOINT; + wanpipe_open(card); + + return err; +} + +/*============================================================================ + * Close network interface. + * o if this is the last close, then disable communications and interrupts. + * o reset flags. + */ +static int if_close (struct device* dev) +{ + chdlc_private_area_t* chdlc_priv_area = dev->priv; + sdla_t* card = chdlc_priv_area->card; + + if(test_and_set_bit(1, (void*)&card->wandev.critical)) + return -EAGAIN; + + dev->start = 0; + wanpipe_close(card); + port_set_state(card, WAN_DISCONNECTED); + chdlc_set_intr_mode(card, 0); + chdlc_comm_disable(card); + + clear_bit(1, (void*)&card->wandev.critical); + + return 0; +} + +/*============================================================================ + * Build media header. + * + * The trick here is to put packet type (Ethertype) into 'protocol' field of + * the socket buffer, so that we don't forget it. If packet type is not + * supported, set skb->protocol to 0 and discard packet later. + * + * Return: media header length. + */ +static int if_header (struct sk_buff* skb, struct device* dev, + unsigned short type, void* daddr, void* saddr, unsigned len) +{ + skb->protocol = htons(type); + + return CHDLC_HDR_LEN; +} + +/*============================================================================ + * Re-build media header. + * + * Return: 1 physical address resolved. + * 0 physical address not resolved + */ +#ifdef LINUX_2_1 +static int if_rebuild_hdr (struct sk_buff *skb) +{ + return 1; +} +#else +static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr, + struct sk_buff* skb) +{ + return 1; +} +#endif + +/*============================================================================ + * Send a packet on a network interface. + * o set tbusy flag (marks start of the transmission) to block a timer-based + * transmit from overlapping. + * o check link state. If link is not up, then drop the packet. + * o execute adapter send command. + * o free socket buffer + * + * Return: 0 complete (socket buffer must be freed) + * non-0 packet may be re-transmitted (tbusy must be set) + * + * Notes: + * 1. This routine is called either by the protocol stack or by the "net + * bottom half" (with interrupts enabled). + * 2. Setting tbusy flag will inhibit further transmit requests from the + * protocol stack and can be used for flow control with protocol layer. + */ +static int if_send (struct sk_buff* skb, struct device* dev) +{ + chdlc_private_area_t *chdlc_priv_area = dev->priv; + sdla_t *card = chdlc_priv_area->card; + SHARED_MEMORY_INFO_STRUCT *flags = card->u.c.flags; + INTERRUPT_INFORMATION_STRUCT *chdlc_int = &flags->interrupt_info_struct; + int udp_type = 0; + unsigned long smp_flags; + + if(skb == NULL) { + /* If we get here, some higher layer thinks we've missed an + * tx-done interrupt. + */ + printk(KERN_INFO "%s: interface %s got kicked!\n", + card->devname, dev->name); + mark_bh(NET_BH); + return 0; + } + + if(dev->tbusy) { + + /* If our device stays busy for at least 5 seconds then we will + * kick start the device by making dev->tbusy = 0. We expect + * that our device never stays busy more than 5 seconds. So this + * is only used as a last resort. + */ + ++card->wandev.stats.collisions; + + if((jiffies - chdlc_priv_area->tick_counter) < (5 * HZ)) { + return 1; + } + + printk (KERN_INFO "%s: Transmit timeout !\n", + card->devname); + + /* unbusy the interface */ + dev->tbusy = 0; + } + + if(ntohs(skb->protocol) != 0x16) { + + /* check the udp packet type */ + udp_type = udp_pkt_type(skb, card); + if(udp_type == UDP_CPIPE_TYPE) { + if(store_udp_mgmt_pkt(UDP_PKT_FRM_STACK, card, skb, dev, + chdlc_priv_area)) + chdlc_int->interrupt_permission |= + APP_INT_ON_TIMER; + return 0; + } + + /* check to see if the source IP address is a broadcast or */ + /* multicast IP address */ + if(chk_bcast_mcast_addr(card, dev, skb)) + return 0; + } + + /* Lock the 508 Card: SMP is supported */ + if(card->hw.type != SDLA_S514){ + s508_lock(card,&smp_flags); + } + + if(test_and_set_bit(0, (void*)&card->wandev.critical)) { + + printk(KERN_INFO "%s: Critical in if_send: %x\n", + card->wandev.name,card->wandev.critical); + ++card->wandev.stats.tx_dropped; +#ifdef LINUX_2_1 + dev_kfree_skb(skb); +#else + dev_kfree_skb(skb, FREE_WRITE); +#endif + if(card->hw.type != SDLA_S514){ + s508_unlock(card,&smp_flags); + } + return 0; + } + + if(card->u.c.state != WAN_CONNECTED) + ++card->wandev.stats.tx_dropped; + + else if(!skb->protocol) + ++card->wandev.stats.tx_errors; + + else { + void* data = skb->data; + unsigned len = skb->len; + unsigned char attr; + + /* If it's an API packet pull off the API + * header. Also check that the packet size + * is larger than the API header + */ + if (card->u.c.usedby == API){ + api_tx_hdr_t* api_tx_hdr; + + if (len <= sizeof(api_tx_hdr_t)){ +#ifdef LINUX_2_1 + dev_kfree_skb(skb); +#else + dev_kfree_skb(skb, FREE_WRITE); +#endif + ++card->wandev.stats.tx_dropped; + clear_bit(0, (void*)&card->wandev.critical); + if(card->hw.type != SDLA_S514){ + s508_unlock(card,&smp_flags); + } + return 0; + } + + api_tx_hdr = (api_tx_hdr_t *)data; + attr = api_tx_hdr->attr; + data += sizeof(api_tx_hdr_t); + len -= sizeof(api_tx_hdr_t); + } + + if(chdlc_send(card, data, len)) { + dev->tbusy = 1; + chdlc_priv_area->tick_counter = jiffies; + chdlc_int->interrupt_permission |= APP_INT_ON_TX_FRAME; + } + else { + ++card->wandev.stats.tx_packets; +#ifdef LINUX_2_1 + card->wandev.stats.tx_bytes += len; +#endif + } + } + + if (!dev->tbusy) { +#ifdef LINUX_2_1 + dev_kfree_skb(skb); +#else + dev_kfree_skb(skb, FREE_WRITE); +#endif + } + + clear_bit(0, (void*)&card->wandev.critical); + if(card->hw.type != SDLA_S514){ + s508_unlock(card,&smp_flags); + } + return dev->tbusy; +} + + +/*============================================================================ + * Check to see if the packet to be transmitted contains a broadcast or + * multicast source IP address. + */ + +static int chk_bcast_mcast_addr(sdla_t *card, struct device* dev, + struct sk_buff *skb) +{ + u32 src_ip_addr; + u32 broadcast_ip_addr = 0; +#ifdef LINUX_2_1 + struct in_device *in_dev; +#endif + /* read the IP source address from the outgoing packet */ + src_ip_addr = *(u32 *)(skb->data + 12); + + /* read the IP broadcast address for the device */ +#ifdef LINUX_2_1 + in_dev = dev->ip_ptr; + if(in_dev != NULL) { + struct in_ifaddr *ifa= in_dev->ifa_list; + if(ifa != NULL) + broadcast_ip_addr = ifa->ifa_broadcast; + else + return 0; + } +#else + broadcast_ip_addr = dev->pa_brdaddr; +#endif + + /* check if the IP Source Address is a Broadcast address */ + if((dev->flags & IFF_BROADCAST) && (src_ip_addr == broadcast_ip_addr)) { + printk(KERN_INFO "%s: Broadcast Source Address silently discarded\n", + card->devname); +#ifdef LINUX_2_1 + dev_kfree_skb(skb); +#else + dev_kfree_skb(skb, FREE_WRITE); +#endif + ++card->wandev.stats.tx_dropped; + return 1; + } + + /* check if the IP Source Address is a Multicast address */ + if((ntohl(src_ip_addr) >= 0xE0000001) && + (ntohl(src_ip_addr) <= 0xFFFFFFFE)) { + printk(KERN_INFO "%s: Multicast Source Address silently discarded\n", + card->devname); +#ifdef LINUX_2_1 + dev_kfree_skb(skb); +#else + dev_kfree_skb(skb, FREE_WRITE); +#endif + ++card->wandev.stats.tx_dropped; + return 1; + } + + return 0; +} + + +/*============================================================================ + * Reply to UDP Management system. + * Return length of reply. + */ +static int reply_udp( unsigned char *data, unsigned int mbox_len ) +{ + + unsigned short len, udp_length, temp, ip_length; + unsigned long ip_temp; + int even_bound = 0; + chdlc_udp_pkt_t *c_udp_pkt = (chdlc_udp_pkt_t *)data; + + /* Set length of packet */ + len = sizeof(ip_pkt_t)+ + sizeof(udp_pkt_t)+ + sizeof(wp_mgmt_t)+ + sizeof(cblock_t)+ + sizeof(trace_info_t)+ + mbox_len; + + /* fill in UDP reply */ + c_udp_pkt->wp_mgmt.request_reply = UDPMGMT_REPLY; + + /* fill in UDP length */ + udp_length = sizeof(udp_pkt_t)+ + sizeof(wp_mgmt_t)+ + sizeof(cblock_t)+ + sizeof(trace_info_t)+ + mbox_len; + + /* put it on an even boundary */ + if ( udp_length & 0x0001 ) { + udp_length += 1; + len += 1; + even_bound = 1; + } + + temp = (udp_length<<8)|(udp_length>>8); + c_udp_pkt->udp_pkt.udp_length = temp; + + /* swap UDP ports */ + temp = c_udp_pkt->udp_pkt.udp_src_port; + c_udp_pkt->udp_pkt.udp_src_port = + c_udp_pkt->udp_pkt.udp_dst_port; + c_udp_pkt->udp_pkt.udp_dst_port = temp; + + /* add UDP pseudo header */ + temp = 0x1100; + *((unsigned short *)(c_udp_pkt->data+mbox_len+even_bound)) = temp; + temp = (udp_length<<8)|(udp_length>>8); + *((unsigned short *)(c_udp_pkt->data+mbox_len+even_bound+2)) = temp; + + + /* calculate UDP checksum */ + c_udp_pkt->udp_pkt.udp_checksum = 0; + c_udp_pkt->udp_pkt.udp_checksum = calc_checksum(&data[UDP_OFFSET],udp_length+UDP_OFFSET); + + /* fill in IP length */ + ip_length = len; + temp = (ip_length<<8)|(ip_length>>8); + c_udp_pkt->ip_pkt.total_length = temp; + + /* swap IP addresses */ + ip_temp = c_udp_pkt->ip_pkt.ip_src_address; + c_udp_pkt->ip_pkt.ip_src_address = c_udp_pkt->ip_pkt.ip_dst_address; + c_udp_pkt->ip_pkt.ip_dst_address = ip_temp; + + /* fill in IP checksum */ + c_udp_pkt->ip_pkt.hdr_checksum = 0; + c_udp_pkt->ip_pkt.hdr_checksum = calc_checksum(data,sizeof(ip_pkt_t)); + + return len; + +} /* reply_udp */ + +unsigned short calc_checksum (char *data, int len) +{ + unsigned short temp; + unsigned long sum=0; + int i; + + for( i = 0; i > 16 ) { + sum = (sum & 0xffffUL) + (sum >> 16); + } + + temp = (unsigned short)sum; + temp = ~temp; + + if( temp == 0 ) + temp = 0xffff; + + return temp; +} + + +/*============================================================================ + * Get ethernet-style interface statistics. + * Return a pointer to struct enet_statistics. + */ +#ifdef LINUX_2_1 +static struct net_device_stats* if_stats (struct device* dev) +{ + sdla_t *my_card; + chdlc_private_area_t* chdlc_priv_area = dev->priv; + + my_card = chdlc_priv_area->card; + return &my_card->wandev.stats; +} +#else +static struct enet_statistics* if_stats (struct device* dev) +{ + sdla_t *my_card; + chdlc_private_area_t* chdlc_priv_area = dev->priv; + + my_card = chdlc_priv_area->card; + return &my_card->wandev.stats; +} +#endif + +/****** Cisco HDLC Firmware Interface Functions *******************************/ + +/*============================================================================ + * Read firmware code version. + * Put code version as ASCII string in str. + */ +static int chdlc_read_version (sdla_t* card, char* str) +{ + CHDLC_MAILBOX_STRUCT* mb = card->mbox; + int len; + char err; + mb->buffer_length = 0; + mb->command = READ_CHDLC_CODE_VERSION; + err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT; + + if(err != COMMAND_OK) { + chdlc_error(card,err,mb); + } + else if (str) { /* is not null */ + len = mb->buffer_length; + memcpy(str, mb->data, len); + str[len] = '\0'; + } + return (err); +} + +/*----------------------------------------------------------------------------- + * Configure CHDLC firmware. + */ +static int chdlc_configure (sdla_t* card, void* data) +{ + int err; + CHDLC_MAILBOX_STRUCT *mailbox = card->mbox; + int data_length = sizeof(CHDLC_CONFIGURATION_STRUCT); + + mailbox->buffer_length = data_length; + memcpy(mailbox->data, data, data_length); + mailbox->command = SET_CHDLC_CONFIGURATION; + err = sdla_exec(mailbox) ? mailbox->return_code : CMD_TIMEOUT; + + if (err != COMMAND_OK) chdlc_error (card, err, mailbox); + + return err; +} + + +/*============================================================================ + * Set interrupt mode -- HDLC Version. + */ + +static int chdlc_set_intr_mode (sdla_t* card, unsigned mode) +{ + CHDLC_MAILBOX_STRUCT* mb = card->mbox; + CHDLC_INT_TRIGGERS_STRUCT* int_data = + (CHDLC_INT_TRIGGERS_STRUCT *)mb->data; + int err; + + int_data->CHDLC_interrupt_triggers = mode; + int_data->IRQ = card->hw.irq; + int_data->interrupt_timer = 1; + + mb->buffer_length = sizeof(CHDLC_INT_TRIGGERS_STRUCT); + mb->command = SET_CHDLC_INTERRUPT_TRIGGERS; + err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT; + if (err != COMMAND_OK) + chdlc_error (card, err, mb); + return err; +} + + +/*============================================================================ + * Enable communications. + */ + +static int chdlc_comm_enable (sdla_t* card) +{ + int err; + CHDLC_MAILBOX_STRUCT* mb = card->mbox; + + mb->buffer_length = 0; + mb->command = ENABLE_CHDLC_COMMUNICATIONS; + err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT; + if (err != COMMAND_OK) + chdlc_error(card, err, mb); + return err; +} + +/*============================================================================ + * Disable communications and Drop the Modem lines (DCD and RTS). + */ +static int chdlc_comm_disable (sdla_t* card) +{ + int err; + CHDLC_MAILBOX_STRUCT* mb = card->mbox; + + mb->buffer_length = 0; + mb->command = DISABLE_CHDLC_COMMUNICATIONS; + err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT; + if (err != COMMAND_OK) + chdlc_error(card,err,mb); + + mb->command = SET_MODEM_STATUS; + mb->buffer_length = 1; + mb->data[0] = 0; + err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT; + if (err != COMMAND_OK) + chdlc_error(card,err,mb); + + return err; +} + +/*============================================================================ + * Read communication error statistics. + */ +static int chdlc_read_comm_err_stats (sdla_t* card) +{ + int err; + CHDLC_MAILBOX_STRUCT* mb = card->mbox; + + mb->buffer_length = 0; + mb->command = READ_COMMS_ERROR_STATS; + err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT; + if (err != COMMAND_OK) + chdlc_error(card,err,mb); + return err; +} + + +/*============================================================================ + * Read CHDLC operational statistics. + */ +static int chdlc_read_op_stats (sdla_t* card) +{ + int err; + CHDLC_MAILBOX_STRUCT* mb = card->mbox; + + mb->buffer_length = 0; + mb->command = READ_CHDLC_OPERATIONAL_STATS; + err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT; + if (err != COMMAND_OK) + chdlc_error(card,err,mb); + return err; +} + + +/*============================================================================ + * Update communications error and general packet statistics. + */ +static int update_comms_stats(sdla_t* card, + chdlc_private_area_t* chdlc_priv_area) +{ + CHDLC_MAILBOX_STRUCT* mb = card->mbox; + COMMS_ERROR_STATS_STRUCT* err_stats; + CHDLC_OPERATIONAL_STATS_STRUCT *op_stats; + + /* on the first timer interrupt, read the comms error statistics */ + if(chdlc_priv_area->update_comms_stats == 2) { + if(chdlc_read_comm_err_stats(card)) + return 1; + err_stats = (COMMS_ERROR_STATS_STRUCT *)mb->data; + card->wandev.stats.rx_over_errors = + err_stats->Rx_overrun_err_count; + card->wandev.stats.rx_crc_errors = + err_stats->CRC_err_count; + card->wandev.stats.rx_frame_errors = + err_stats->Rx_abort_count; + card->wandev.stats.rx_fifo_errors = + err_stats->Rx_dis_pri_bfrs_full_count; + card->wandev.stats.rx_missed_errors = + card->wandev.stats.rx_fifo_errors; + card->wandev.stats.tx_aborted_errors = + err_stats->sec_Tx_abort_count; + } + + /* on the second timer interrupt, read the operational statistics */ + else { + if(chdlc_read_op_stats(card)) + return 1; + op_stats = (CHDLC_OPERATIONAL_STATS_STRUCT *)mb->data; + card->wandev.stats.rx_length_errors = + (op_stats->Rx_Data_discard_short_count + + op_stats->Rx_Data_discard_long_count); + } + + return 0; +} + +/*============================================================================ + * Send packet. + * Return: 0 - o.k. + * 1 - no transmit buffers available + */ +static int chdlc_send (sdla_t* card, void* data, unsigned len) +{ + CHDLC_DATA_TX_STATUS_EL_STRUCT *txbuf = card->u.c.txbuf; + + if (txbuf->opp_flag) + return 1; + + sdla_poke(&card->hw, txbuf->ptr_data_bfr, data, len); + + txbuf->frame_length = len; + txbuf->opp_flag = 1; /* start transmission */ + + /* Update transmit buffer control fields */ + card->u.c.txbuf = ++txbuf; + + if ((void*)txbuf > card->u.c.txbuf_last) + card->u.c.txbuf = card->u.c.txbuf_base; + + return 0; +} + +/****** Firmware Error Handler **********************************************/ + +/*============================================================================ + * Firmware error handler. + * This routine is called whenever firmware command returns non-zero + * return code. + * + * Return zero if previous command has to be cancelled. + */ +static int chdlc_error (sdla_t *card, int err, CHDLC_MAILBOX_STRUCT *mb) +{ + unsigned cmd = mb->command; + + switch (err) { + + case CMD_TIMEOUT: + printk(KERN_ERR "%s: command 0x%02X timed out!\n", + card->devname, cmd); + break; + + case S514_BOTH_PORTS_SAME_CLK_MODE: + if(cmd == SET_CHDLC_CONFIGURATION) { + printk(KERN_INFO + "%s: Configure both ports for the same clock source\n", + card->devname); + break; + } + + default: + printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n", + card->devname, cmd, err); + } + + return 0; +} + +/****** Interrupt Handlers **************************************************/ + +/*============================================================================ + * Cisco HDLC interrupt service routine. + */ +STATIC void wpc_isr (sdla_t* card) +{ + struct device* dev; + chdlc_private_area_t* chdlc_priv_area; + SHARED_MEMORY_INFO_STRUCT* flags = NULL; + int i, interrupt_serviced = 0; + sdla_t *my_card; + + + /* Check for which port the interrupt has been generated + * Since Secondary Port is piggybacking on the Primary + * the check must be done here. + */ + + flags = card->u.c.flags; + if (!flags->interrupt_info_struct.interrupt_type){ + /* Check for a second port (piggybacking) */ + if((my_card = card->next)){ + flags = my_card->u.c.flags; + if (flags->interrupt_info_struct.interrupt_type){ + card = my_card; + } + } + } + + dev = card->wandev.dev; + + card->in_isr = 1; + + /* if critical due to peripheral operations + * ie. update() or getstats() then reset the interrupt and + * wait for the board to retrigger. + */ + if(test_bit(1, (void*)&card->wandev.critical)) { + if(card->u.c.flags != NULL) { + flags = card->u.c.flags; + if(flags->interrupt_info_struct. + interrupt_type) { + flags->interrupt_info_struct. + interrupt_type = 0; + } + } + card->in_isr = 0; + return; + } + + + /* On a 508 Card, if critical due to if_send + * Major Error !!! + */ + if(card->hw.type != SDLA_S514) { + if(test_and_set_bit(0, (void*)&card->wandev.critical)) { + printk(KERN_INFO "%s: Critical while in ISR: %x\n", + card->devname, card->wandev.critical); + card->in_isr = 0; + return; + } + } + + /* FIXME: Take this check out later in the future */ + if(card->u.c.flags != NULL) { + + flags = card->u.c.flags; + + switch(flags->interrupt_info_struct.interrupt_type) { + + case RX_APP_INT_PEND: /* 0x01: receive interrupt */ + interrupt_serviced = 1; + rx_intr(card); + break; + + case TX_APP_INT_PEND: /* 0x02: transmit interrupt */ + interrupt_serviced = 1; + flags->interrupt_info_struct.interrupt_permission &= + ~APP_INT_ON_TX_FRAME; + + chdlc_priv_area = dev->priv; + dev->tbusy = 0; + mark_bh(NET_BH); + break; + + case COMMAND_COMPLETE_APP_INT_PEND:/* 0x04: cmd cplt */ + interrupt_serviced = 1; + ++ Intr_test_counter; + break; + + case CHDLC_EXCEP_COND_APP_INT_PEND: /* 0x20 */ + interrupt_serviced = 1; + process_chdlc_exception(card); + break; + + case GLOBAL_EXCEP_COND_APP_INT_PEND: + interrupt_serviced = 1; + process_global_exception(card); + break; + + case TIMER_APP_INT_PEND: + interrupt_serviced = 1; + timer_intr(card); + break; + + default: + break; + } + } + + if(!interrupt_serviced) { + printk(KERN_INFO "%s: spurious interrupt 0x%02X!\n", + card->devname, + flags->interrupt_info_struct.interrupt_type); + printk(KERN_INFO "Code name: "); + for(i = 0; i < 4; i ++) + printk(KERN_INFO "%c", + flags->global_info_struct.codename[i]); + printk(KERN_INFO "\nCode version: "); + for(i = 0; i < 4; i ++) + printk(KERN_INFO "%c", + flags->global_info_struct.codeversion[i]); + printk(KERN_INFO "\n"); + } + + card->in_isr = 0; + flags->interrupt_info_struct.interrupt_type = 0; + if(card->hw.type != SDLA_S514){ + clear_bit(0, (void*)&card->wandev.critical); + } + +} + +/*============================================================================ + * Receive interrupt handler. + */ +static void rx_intr (sdla_t* card) +{ + struct device *dev; + chdlc_private_area_t *chdlc_priv_area; + SHARED_MEMORY_INFO_STRUCT *flags = card->u.c.flags; + CHDLC_DATA_RX_STATUS_EL_STRUCT *rxbuf = card->u.c.rxmb; + struct sk_buff *skb; + unsigned len; + void *buf; + int i,udp_type; + + if (rxbuf->opp_flag != 0x01) { + printk(KERN_INFO + "%s: corrupted Rx buffer @ 0x%X, flag = 0x%02X!\n", + card->devname, (unsigned)rxbuf, rxbuf->opp_flag); + printk(KERN_INFO "Code name: "); + for(i = 0; i < 4; i ++) + printk(KERN_INFO "%c", + flags->global_info_struct.codename[i]); + printk(KERN_INFO "\nCode version: "); + for(i = 0; i < 4; i ++) + printk(KERN_INFO "%c", + flags->global_info_struct.codeversion[i]); + printk(KERN_INFO "\n"); + return; + } + + dev = card->wandev.dev; + chdlc_priv_area = dev->priv; + + if(dev && dev->start) { + + len = rxbuf->frame_length; + + /* Allocate socket buffer */ + skb = dev_alloc_skb(len); + + if (skb != NULL) { + /* Copy data to the socket buffer */ + unsigned addr = rxbuf->ptr_data_bfr; + + if((addr + len) > + card->u.c.rx_top + 1) { + unsigned tmp = + card->u.c.rx_top - addr + 1; + buf = skb_put(skb, tmp); + sdla_peek(&card->hw, addr, buf, tmp); + addr = card->u.c.rx_base; + len -= tmp; + } + + buf = skb_put(skb, len); + sdla_peek(&card->hw, addr, buf, len); + + /* Decapsulate packet */ + skb->protocol = htons(ETH_P_IP); + + card->wandev.stats.rx_packets ++; +#ifdef LINUX_2_1 + card->wandev.stats.rx_bytes += skb->len; +#endif + udp_type = udp_pkt_type( skb, card ); + + if(udp_type == UDP_CPIPE_TYPE) { + if(store_udp_mgmt_pkt(UDP_PKT_FRM_NETWORK, + card, skb, dev, chdlc_priv_area)) { + flags->interrupt_info_struct. + interrupt_permission |= + APP_INT_ON_TIMER; + } + + } else { + + if(card->u.c.usedby == API) { + api_rx_hdr_t* api_rx_hdr; + skb_push(skb, sizeof(api_rx_hdr_t)); + api_rx_hdr = + (api_rx_hdr_t*)&skb->data[0x00]; + api_rx_hdr->error_flag = + rxbuf->error_flag; + api_rx_hdr->time_stamp = + rxbuf->time_stamp; + skb->protocol = htons(0x16); + skb->pkt_type = PACKET_HOST; + } + +/* FIXME: we should check to see if the received packet is a multicast packet so that we can increment the multicast statistic + ++ chdlc_priv_area->if_stats.multicast; +*/ + /* Pass it up the protocol stack */ + skb->dev = dev; + skb->mac.raw = skb->data; + netif_rx(skb); + } + + } else { + printk(KERN_INFO + "%s: no socket buffers available!\n", + card->devname); + ++card->wandev.stats.rx_dropped; + } + } + + /* Release buffer element and calculate a pointer to the next one */ + rxbuf->opp_flag = 0x00; + card->u.c.rxmb = ++ rxbuf; + if((void*)rxbuf > card->u.c.rxbuf_last) + card->u.c.rxmb = card->u.c.rxbuf_base; +} + +/*============================================================================ + * Timer interrupt handler. + * The timer interrupt is used for two purposes: + * 1) Processing udp calls from 'cpipemon'. + * 2) Reading board-level statistics for updating the proc file system. + */ +void timer_intr(sdla_t *card) +{ + struct device* dev; + chdlc_private_area_t* chdlc_priv_area = NULL; + SHARED_MEMORY_INFO_STRUCT* flags = NULL; + + dev = card->wandev.dev; + chdlc_priv_area = dev->priv; + + /* process a udp call if pending */ + if(chdlc_priv_area->timer_int_enabled & TMR_INT_ENABLED_UDP) { + process_udp_mgmt_pkt(card, dev, + chdlc_priv_area); + chdlc_priv_area->timer_int_enabled &= ~TMR_INT_ENABLED_UDP; + } + + /* read the communications statistics if required */ + if(chdlc_priv_area->timer_int_enabled & TMR_INT_ENABLED_UPDATE) { + update_comms_stats(card, chdlc_priv_area); + if(!(-- chdlc_priv_area->update_comms_stats)) { + chdlc_priv_area->timer_int_enabled &= + ~TMR_INT_ENABLED_UPDATE; + } + } + + /* only disable the timer interrupt if there are no udp or statistic */ + /* updates pending */ + if(!chdlc_priv_area->timer_int_enabled) { + flags = card->u.c.flags; + flags->interrupt_info_struct.interrupt_permission &= + ~APP_INT_ON_TIMER; + } +} + +/*------------------------------------------------------------------------------ + Miscellaneous Functions + - set_chdlc_config() used to set configuration options on the board +------------------------------------------------------------------------------*/ + +static int set_chdlc_config(sdla_t* card) +{ + + struct device * dev = card->wandev.dev; + chdlc_private_area_t *chdlc_priv_area = dev->priv; + CHDLC_CONFIGURATION_STRUCT cfg; + + memset(&cfg, 0, sizeof(CHDLC_CONFIGURATION_STRUCT)); + + if(card->wandev.clocking) + cfg.baud_rate = card->wandev.bps; + + cfg.line_config_options = (card->wandev.interface == WANOPT_RS232) ? + INTERFACE_LEVEL_RS232 : INTERFACE_LEVEL_V35; + + cfg.modem_config_options = 0; + cfg.modem_status_timer = 100; + + cfg.CHDLC_protocol_options = card->u.c.protocol_options; + cfg.percent_data_buffer_for_Tx = 50; + cfg.CHDLC_statistics_options = (CHDLC_TX_DATA_BYTE_COUNT_STAT | + CHDLC_RX_DATA_BYTE_COUNT_STAT); + cfg.max_CHDLC_data_field_length = card->wandev.mtu; + cfg.transmit_keepalive_timer = card->u.c.kpalv_tx; + cfg.receive_keepalive_timer = card->u.c.kpalv_rx; + cfg.keepalive_error_tolerance = card->u.c.kpalv_err; + cfg.SLARP_request_timer = card->u.c.slarp_timer; + + if (cfg.SLARP_request_timer) { + cfg.IP_address = 0; + cfg.IP_netmask = 0; + } + else { +#ifdef LINUX_2_1 + struct in_device *in_dev = dev->ip_ptr; + + if(in_dev != NULL) { + struct in_ifaddr *ifa = in_dev->ifa_list; + + if (ifa != NULL ) { + cfg.IP_address = ntohl(ifa->ifa_local); + cfg.IP_netmask = ntohl(ifa->ifa_mask); + chdlc_priv_area->IP_address = + ntohl(ifa->ifa_local); + chdlc_priv_area->IP_netmask = + ntohl(ifa->ifa_mask); + } + } +#else + cfg.IP_address = ntohl(dev->pa_addr); + cfg.IP_netmask = ntohl(dev->pa_mask); + chdlc_priv_area->IP_address = ntohl(dev->pa_addr); + chdlc_priv_area->IP_netmask = ntohl(dev->pa_mask); +#endif + + /* FIXME: We must re-think this message in next release + if((cfg.IP_address & 0x000000FF) > 2) { + printk(KERN_WARNING "\n"); + printk(KERN_WARNING " WARNING:%s configured with an\n", + card->devname); + printk(KERN_WARNING " invalid local IP address.\n"); + printk(KERN_WARNING " Slarp pragmatics will fail.\n"); + printk(KERN_WARNING " IP address should be of the\n"); + printk(KERN_WARNING " format A.B.C.1 or A.B.C.2.\n"); + } + */ + } + + return chdlc_configure(card, &cfg); +} + + + +/*============================================================================ + * Process global exception condition + */ +static int process_global_exception(sdla_t *card) +{ + CHDLC_MAILBOX_STRUCT* mbox = card->mbox; + int err; + + mbox->buffer_length = 0; + mbox->command = READ_GLOBAL_EXCEPTION_CONDITION; + err = sdla_exec(mbox) ? mbox->return_code : CMD_TIMEOUT; + + if(err != CMD_TIMEOUT ){ + + switch(mbox->return_code) { + + case EXCEP_MODEM_STATUS_CHANGE: + + printk(KERN_INFO "%s: Modem status change\n", + card->devname); + + switch(mbox->data[0] & (DCD_HIGH | CTS_HIGH)) { + case (DCD_HIGH): + printk(KERN_INFO "%s: DCD high, CTS low\n",card->devname); + break; + case (CTS_HIGH): + printk(KERN_INFO "%s: DCD low, CTS high\n",card->devname); break; + case ((DCD_HIGH | CTS_HIGH)): + printk(KERN_INFO "%s: DCD high, CTS high\n",card->devname); + break; + default: + printk(KERN_INFO "%s: DCD low, CTS low\n",card->devname); + break; + } + break; + + case EXCEP_TRC_DISABLED: + printk(KERN_INFO "%s: Line trace disabled\n", + card->devname); + break; + + case EXCEP_IRQ_TIMEOUT: + printk(KERN_INFO "%s: IRQ timeout occurred\n", + card->devname); + break; + + default: + printk(KERN_INFO "%s: Global exception %x\n", + card->devname, mbox->return_code); + break; + } + } + return 0; +} + + +/*============================================================================ + * Process chdlc exception condition + */ +static int process_chdlc_exception(sdla_t *card) +{ + CHDLC_MAILBOX_STRUCT* mb = card->mbox; + int err; + + mb->buffer_length = 0; + mb->command = READ_CHDLC_EXCEPTION_CONDITION; + err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT; + if(err != CMD_TIMEOUT) { + + switch (err) { + + case EXCEP_LINK_ACTIVE: + port_set_state(card, WAN_CONNECTED); + break; + + case EXCEP_LINK_INACTIVE_MODEM: + port_set_state(card, WAN_DISCONNECTED); + unconfigure_ip(card); + break; + + case EXCEP_LINK_INACTIVE_KPALV: + port_set_state(card, WAN_DISCONNECTED); + printk(KERN_INFO "%s: Keepalive timer expired.\n", + card->devname); + unconfigure_ip(card); + break; + + case EXCEP_IP_ADDRESS_DISCOVERED: + if (configure_ip(card)) + return -1; + break; + + case EXCEP_LOOPBACK_CONDITION: + printk(KERN_INFO "%s: Loopback Condition Detected.\n", + card->devname); + break; + + case NO_CHDLC_EXCEP_COND_TO_REPORT: + printk(KERN_INFO "%s: No exceptions reported.\n", + card->devname); + break; + } + + } + return 0; +} + + +/*============================================================================ + * Configure IP from SLARP negotiation + * This adds dynamic routes when SLARP has provided valid addresses + */ + +static int configure_ip (sdla_t* card) +{ + struct device *dev = card->wandev.dev; + chdlc_private_area_t *chdlc_priv_area = dev->priv; + char err; + + /* set to discover */ + if(card->u.c.slarp_timer != 0x00) { + CHDLC_MAILBOX_STRUCT* mb = card->mbox; + CHDLC_CONFIGURATION_STRUCT *cfg; + + mb->buffer_length = 0; + mb->command = READ_CHDLC_CONFIGURATION; + err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT; + + if(err != COMMAND_OK) { + chdlc_error(card,err,mb); + return -1; + } + + cfg = (CHDLC_CONFIGURATION_STRUCT *)mb->data; + chdlc_priv_area->IP_address = cfg->IP_address; + chdlc_priv_area->IP_netmask = cfg->IP_netmask; + } + + /* Set flag to add route */ + chdlc_priv_area->route_status = ADD_ROUTE; + + /* The idea here is to add the route in the poll routine. + This way, we aren't in interrupt context when adding routes */ + card->poll = process_route; + + return 0; +} + + +/*============================================================================ + * Un-Configure IP negotiated by SLARP + * This removes dynamic routes when the link becomes inactive. + */ + +static int unconfigure_ip (sdla_t* card) +{ + struct device *dev = card->wandev.dev; + chdlc_private_area_t *chdlc_priv_area= dev->priv; + + if (chdlc_priv_area->route_status == ROUTE_ADDED) { + chdlc_priv_area->route_status = REMOVE_ROUTE; + /* The idea here is to delete the route in + * the poll routine. + * This way, we aren't in interrupt context + * when adding routes + */ + card->poll = process_route; + } + return 0; +} + +/*============================================================================ + * Routine to add/remove routes + * Called like a polling routine when Routes are flagged to be added/removed. + */ + +static void process_route (sdla_t *card) +{ + struct device *dev = card->wandev.dev; + unsigned char port_num; + chdlc_private_area_t *chdlc_priv_area = NULL; + u32 local_IP_addr = 0; + u32 remote_IP_addr = 0; + u32 IP_netmask, IP_addr; + int err = 0; +#ifdef LINUX_2_1 + struct in_device *in_dev; + mm_segment_t fs; + struct ifreq if_info; + struct sockaddr_in *if_data1, *if_data2; +#else + unsigned long fs = 0; + struct rtentry route; +#endif + + chdlc_priv_area = dev->priv; + port_num = card->u.c.comm_port; + + if((chdlc_priv_area->route_status == ADD_ROUTE) && + ((chdlc_priv_area->IP_address & 0x000000FF) > 2)) { + printk(KERN_INFO "%s: Dynamic route failure.\n",card->devname); + if(card->u.c.slarp_timer) { + printk(KERN_INFO "%s: Bad IP address %s received\n", + card->devname, + in_ntoa(ntohl(chdlc_priv_area->IP_address))); + printk(KERN_INFO "%s: from remote station.\n", + card->devname); + }else{ + printk(KERN_INFO "%s: Bad IP address %s issued\n", + card->devname, + in_ntoa(ntohl(chdlc_priv_area->IP_address))); + printk(KERN_INFO "%s: to remote station. Local\n", + card->devname); + printk(KERN_INFO "%s: IP address must be A.B.C.1\n", + card->devname); + printk(KERN_INFO "%s: or A.B.C.2.\n",card->devname); + } + + /* remove the route due to the IP address error condition */ + chdlc_priv_area->route_status = REMOVE_ROUTE; + err = 1; + } + + /* If we are removing a route with bad IP addressing, then use the */ + /* locally configured IP addresses */ + if((chdlc_priv_area->route_status == REMOVE_ROUTE) && err) { + + /* do not remove a bad route that has already been removed */ + if(chdlc_priv_area->route_removed) { + card->poll = NULL; + return; + } + +#ifdef LINUX_2_1 + in_dev = dev->ip_ptr; + + if(in_dev != NULL) { + struct in_ifaddr *ifa = in_dev->ifa_list; + if (ifa != NULL ) { + local_IP_addr = ifa->ifa_local; + IP_netmask = ifa->ifa_mask; + } + } +#else + local_IP_addr = dev->pa_addr; + remote_IP_addr = dev->pa_dstaddr; + IP_netmask = dev->pa_mask; +#endif + }else{ + /* According to Cisco HDLC, if the point-to-point address is + A.B.C.1, then we are the opposite (A.B.C.2), and vice-versa. + */ + IP_netmask = ntohl(chdlc_priv_area->IP_netmask); + remote_IP_addr = ntohl(chdlc_priv_area->IP_address); + local_IP_addr = (remote_IP_addr & ntohl(0xFFFFFF00)) + + (~remote_IP_addr & ntohl(0x0003)); + + if(!card->u.c.slarp_timer) { + IP_addr = local_IP_addr; + local_IP_addr = remote_IP_addr; + remote_IP_addr = IP_addr; + } + } + + fs = get_fs(); /* Save file system */ + set_fs(get_ds()); /* Get user space block */ + +#ifdef LINUX_2_1 + /* Setup a structure for adding/removing routes */ + memset(&if_info, 0, sizeof(if_info)); + strcpy(if_info.ifr_name, dev->name); + +#else + /* Setup a structure for adding/removing routes */ + dev->pa_mask = IP_netmask; + dev->pa_dstaddr = remote_IP_addr; + dev->pa_addr = local_IP_addr; + + memset(&route, 0, sizeof(route)); + route.rt_dev = dev->name; + route.rt_flags = 0; + ((struct sockaddr_in *)&(route.rt_dst))->sin_addr.s_addr = + dev->pa_dstaddr; + ((struct sockaddr_in *)&(route.rt_dst))->sin_family = AF_INET; + ((struct sockaddr_in *)&(route.rt_genmask))->sin_addr.s_addr = + 0xFFFFFFFF; + ((struct sockaddr_in *)&(route.rt_genmask))->sin_family = + AF_INET; +#endif + + switch (chdlc_priv_area->route_status) { + + case ADD_ROUTE: + + if(!card->u.c.slarp_timer) { +#ifdef LINUX_2_1 + if_data2 = (struct sockaddr_in *)&if_info.ifr_dstaddr; + if_data2->sin_addr.s_addr = remote_IP_addr; + if_data2->sin_family = AF_INET; + err = devinet_ioctl(SIOCSIFDSTADDR, &if_info); +#else + err = ip_rt_new(&route); +#endif + } else { +#ifdef LINUX_2_1 + if_data1 = (struct sockaddr_in *)&if_info.ifr_addr; + if_data1->sin_addr.s_addr = local_IP_addr; + if_data1->sin_family = AF_INET; + if(!(err = devinet_ioctl(SIOCSIFADDR, &if_info))){ + if_data2 = (struct sockaddr_in *)&if_info.ifr_dstaddr; + if_data2->sin_addr.s_addr = remote_IP_addr; + if_data2->sin_family = AF_INET; + err = devinet_ioctl(SIOCSIFDSTADDR, &if_info); + } +#else + err = ip_rt_new(&route); +#endif + } + + if(err) { + printk(KERN_INFO "%s: Add route %s failed (%d)\n", + card->devname, in_ntoa(remote_IP_addr), err); + } else { + ((chdlc_private_area_t *)dev->priv)->route_status = ROUTE_ADDED; + printk(KERN_INFO "%s: Dynamic route added.\n", + card->devname); + printk(KERN_INFO "%s: Local IP addr : %s\n", + card->devname, in_ntoa(local_IP_addr)); + printk(KERN_INFO "%s: Remote IP addr: %s\n", + card->devname, in_ntoa(remote_IP_addr)); + chdlc_priv_area->route_removed = 0; + } + break; + + + case REMOVE_ROUTE: + +#ifdef LINUX_2_1 + /* Change the local ip address of the interface to 0. + * This will also delete the destination route. + */ + if(!card->u.c.slarp_timer) { + if_data2 = (struct sockaddr_in *)&if_info.ifr_dstaddr; + if_data2->sin_addr.s_addr = 0; + if_data2->sin_family = AF_INET; + err = devinet_ioctl(SIOCSIFDSTADDR, &if_info); + } else { + if_data1 = (struct sockaddr_in *)&if_info.ifr_addr; + if_data1->sin_addr.s_addr = 0; + if_data1->sin_family = AF_INET; + err = devinet_ioctl(SIOCSIFADDR,&if_info); + + } +#else + /* set the point-to-point IP address to 0.0.0.0 */ + dev->pa_dstaddr = 0; + err = ip_rt_kill(&route); +#endif + if(err) { + printk(KERN_INFO + "%s: Remove route %s failed, (err %d)\n", + card->devname, in_ntoa(remote_IP_addr), + err); + } else { + ((chdlc_private_area_t *)dev->priv)->route_status = + NO_ROUTE; + printk(KERN_INFO "%s: Dynamic route removed: %s\n", + card->devname, in_ntoa(local_IP_addr)); + chdlc_priv_area->route_removed = 1; + } + break; + } + + set_fs(fs); /* Restore file system */ + + /* Once we've processed the route, stop polling */ + card->poll = NULL; + +} + + +/*============================================================================= + * Store a UDP management packet for later processing. + */ + +static int store_udp_mgmt_pkt(char udp_pkt_src, sdla_t* card, + struct sk_buff *skb, struct device* dev, + chdlc_private_area_t* chdlc_priv_area ) +{ + int udp_pkt_stored = 0; + + if(!chdlc_priv_area->udp_pkt_lgth && + (skb->len <= MAX_LGTH_UDP_MGNT_PKT)) { + chdlc_priv_area->udp_pkt_lgth = skb->len; + chdlc_priv_area->udp_pkt_src = udp_pkt_src; + memcpy(chdlc_priv_area->udp_pkt_data, skb->data, skb->len); + chdlc_priv_area->timer_int_enabled = TMR_INT_ENABLED_UDP; + udp_pkt_stored = 1; + } + +#ifdef LINUX_2_1 + dev_kfree_skb(skb); +#else + if(udp_pkt_src == UDP_PKT_FRM_STACK) + dev_kfree_skb(skb, FREE_WRITE); + else + dev_kfree_skb(skb, FREE_READ); +#endif + + return(udp_pkt_stored); +} + + +/*============================================================================= + * Process UDP management packet. + */ + +static int process_udp_mgmt_pkt(sdla_t* card, struct device* dev, + chdlc_private_area_t* chdlc_priv_area ) +{ + unsigned char *buf; + unsigned int frames, len; + struct sk_buff *new_skb; + unsigned short buffer_length, real_len; + unsigned long data_ptr; + unsigned data_length; + int udp_mgmt_req_valid = 1; + CHDLC_MAILBOX_STRUCT *mb = card->mbox; + SHARED_MEMORY_INFO_STRUCT *flags = card->u.c.flags; + chdlc_udp_pkt_t *chdlc_udp_pkt; + struct timeval tv; + int err; + char ut_char; + + chdlc_udp_pkt = (chdlc_udp_pkt_t *) chdlc_priv_area->udp_pkt_data; + + switch(chdlc_udp_pkt->cblock.command) { + + case FT1_MONITOR_STATUS_CTRL: + case CPIPE_ENABLE_TRACING: + case CPIPE_DISABLE_TRACING: + case CPIPE_GET_TRACE_INFO: + case SET_FT1_MODE: + if(chdlc_priv_area->udp_pkt_src == + UDP_PKT_FRM_NETWORK) { + udp_mgmt_req_valid = 0; + } + break; + + default: + break; + } + + if(!udp_mgmt_req_valid) { + + /* set length to 0 */ + chdlc_udp_pkt->cblock.buffer_length = 0; + + /* set return code */ + chdlc_udp_pkt->cblock.return_code = 0xCD; + + } else { + unsigned long trace_status_cfg_addr = 0; + TRACE_STATUS_EL_CFG_STRUCT trace_cfg_struct; + TRACE_STATUS_ELEMENT_STRUCT trace_element_struct; + + switch(chdlc_udp_pkt->cblock.command) { + + case CPIPE_ENABLE_TRACING: + if (!chdlc_priv_area->TracingEnabled) { + + /* OPERATE_DATALINE_MONITOR */ + + mb->buffer_length = sizeof(LINE_TRACE_CONFIG_STRUCT); + mb->command = SET_TRACE_CONFIGURATION; + + ((LINE_TRACE_CONFIG_STRUCT *)mb->data)-> + trace_config = TRACE_ACTIVE; + /* Trace delay mode is not used because it slows + down transfer and results in a standoff situation + when there is a lot of data */ + + /* Configure the Trace based on user inputs */ + ((LINE_TRACE_CONFIG_STRUCT *)mb->data)->trace_config |= + chdlc_udp_pkt->data[0]; + + ((LINE_TRACE_CONFIG_STRUCT *)mb->data)-> + trace_deactivation_timer = 4000; + + + err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT; + if (err != COMMAND_OK) { + chdlc_error(card,err,mb); + card->TracingEnabled = 0; + chdlc_udp_pkt->cblock.return_code = err; + mb->buffer_length = 0; + break; + } + + /* Get the base address of the trace element list */ + mb->buffer_length = 0; + mb->command = READ_TRACE_CONFIGURATION; + err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT; + + if (err != COMMAND_OK) { + chdlc_error(card,err,mb); + chdlc_priv_area->TracingEnabled = 0; + chdlc_udp_pkt->cblock.return_code = err; + mb->buffer_length = 0; + break; + } + + trace_status_cfg_addr =((LINE_TRACE_CONFIG_STRUCT *) + mb->data) -> ptr_trace_stat_el_cfg_struct; + + sdla_peek(&card->hw, trace_status_cfg_addr, + &trace_cfg_struct, sizeof(trace_cfg_struct)); + + chdlc_priv_area->start_trace_addr = trace_cfg_struct. + base_addr_trace_status_elements; + + chdlc_priv_area->number_trace_elements = + trace_cfg_struct.number_trace_status_elements; + + chdlc_priv_area->end_trace_addr = (unsigned long) + ((TRACE_STATUS_ELEMENT_STRUCT *) + chdlc_priv_area->start_trace_addr + + (chdlc_priv_area->number_trace_elements - 1)); + + chdlc_priv_area->base_addr_trace_buffer = + trace_cfg_struct.base_addr_trace_buffer; + + chdlc_priv_area->end_addr_trace_buffer = + trace_cfg_struct.end_addr_trace_buffer; + + chdlc_priv_area->curr_trace_addr = + trace_cfg_struct.next_trace_element_to_use; + + chdlc_priv_area->available_buffer_space = 2000 - + sizeof(ip_pkt_t) - + sizeof(udp_pkt_t) - + sizeof(wp_mgmt_t) - + sizeof(cblock_t) - + sizeof(trace_info_t); + } + chdlc_udp_pkt->cblock.return_code = COMMAND_OK; + mb->buffer_length = 0; + chdlc_priv_area->TracingEnabled = 1; + break; + + + case CPIPE_DISABLE_TRACING: + if (chdlc_priv_area->TracingEnabled) { + + /* OPERATE_DATALINE_MONITOR */ + mb->buffer_length = sizeof(LINE_TRACE_CONFIG_STRUCT); + mb->command = SET_TRACE_CONFIGURATION; + ((LINE_TRACE_CONFIG_STRUCT *)mb->data)-> + trace_config = TRACE_INACTIVE; + err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT; + } + + chdlc_priv_area->TracingEnabled = 0; + chdlc_udp_pkt->cblock.return_code = COMMAND_OK; + mb->buffer_length = 0; + break; + + + case CPIPE_GET_TRACE_INFO: + + if (!chdlc_priv_area->TracingEnabled) { + chdlc_udp_pkt->cblock.return_code = 1; + mb->buffer_length = 0; + break; + } + + chdlc_udp_pkt->trace_info.ismoredata = 0x00; + buffer_length = 0; /* offset of packet already occupied */ + + for (frames=0; frames < chdlc_priv_area->number_trace_elements; frames++){ + + trace_pkt_t *trace_pkt = (trace_pkt_t *) + &chdlc_udp_pkt->data[buffer_length]; + + sdla_peek(&card->hw, chdlc_priv_area->curr_trace_addr, + (unsigned char *)&trace_element_struct, + sizeof(TRACE_STATUS_ELEMENT_STRUCT)); + + if (trace_element_struct.opp_flag == 0x00) { + break; + } + + /* get pointer to real data */ + data_ptr = trace_element_struct.ptr_data_bfr; + + /* See if there is actual data on the trace buffer */ + if (data_ptr){ + data_length = trace_element_struct.trace_length; + }else{ + data_length = 0; + chdlc_udp_pkt->trace_info.ismoredata = 0x01; + } + + if( (chdlc_priv_area->available_buffer_space - buffer_length) + < ( sizeof(trace_pkt_t) + data_length) ) { + + /* indicate there are more frames on board & exit */ + chdlc_udp_pkt->trace_info.ismoredata = 0x01; + break; + } + + trace_pkt->status = trace_element_struct.trace_type; + + trace_pkt->time_stamp = + trace_element_struct.trace_time_stamp; + + trace_pkt->real_length = + trace_element_struct.trace_length; + + /* see if we can fit the frame into the user buffer */ + real_len = trace_pkt->real_length; + + if (data_ptr == 0) { + trace_pkt->data_avail = 0x00; + } else { + unsigned tmp = 0; + + /* get the data from circular buffer + must check for end of buffer */ + trace_pkt->data_avail = 0x01; + + if ((data_ptr + real_len) > + chdlc_priv_area->end_addr_trace_buffer + 1){ + + tmp = chdlc_priv_area->end_addr_trace_buffer - data_ptr + 1; + sdla_peek(&card->hw, data_ptr, + trace_pkt->data,tmp); + data_ptr = chdlc_priv_area->base_addr_trace_buffer; + } + + sdla_peek(&card->hw, data_ptr, + &trace_pkt->data[tmp], real_len - tmp); + } + + /* zero the opp flag to show we got the frame */ + ut_char = 0x00; + sdla_poke(&card->hw, chdlc_priv_area->curr_trace_addr, &ut_char, 1); + + /* now move onto the next frame */ + chdlc_priv_area->curr_trace_addr += sizeof(TRACE_STATUS_ELEMENT_STRUCT); + + /* check if we went over the last address */ + if ( chdlc_priv_area->curr_trace_addr > chdlc_priv_area->end_trace_addr ) { + chdlc_priv_area->curr_trace_addr = chdlc_priv_area->start_trace_addr; + } + + if(trace_pkt->data_avail == 0x01) { + buffer_length += real_len - 1; + } + + /* for the header */ + buffer_length += sizeof(trace_pkt_t); + + } /* For Loop */ + + if (frames == chdlc_priv_area->number_trace_elements){ + chdlc_udp_pkt->trace_info.ismoredata = 0x01; + } + chdlc_udp_pkt->trace_info.num_frames = frames; + + mb->buffer_length = buffer_length; + chdlc_udp_pkt->cblock.buffer_length = buffer_length; + + chdlc_udp_pkt->cblock.return_code = COMMAND_OK; + + break; + + + case CPIPE_FT1_READ_STATUS: + ((unsigned char *)chdlc_udp_pkt->data )[0] = + flags->FT1_info_struct.parallel_port_A_input; + + ((unsigned char *)chdlc_udp_pkt->data )[1] = + flags->FT1_info_struct.parallel_port_B_input; + + chdlc_udp_pkt->cblock.return_code = COMMAND_OK; + mb->buffer_length = 2; + break; + + case CPIPE_ROUTER_UP_TIME: + do_gettimeofday( &tv ); + chdlc_priv_area->router_up_time = tv.tv_sec - + chdlc_priv_area->router_start_time; + *(unsigned long *)&chdlc_udp_pkt->data = + chdlc_priv_area->router_up_time; + mb->buffer_length = sizeof(unsigned long); + break; + + case FT1_MONITOR_STATUS_CTRL: + /* Enable FT1 MONITOR STATUS */ + if ((chdlc_udp_pkt->data[0] & ENABLE_READ_FT1_STATUS) || + (chdlc_udp_pkt->data[0] & ENABLE_READ_FT1_OP_STATS)) { + + if( rCount++ != 0 ) { + chdlc_udp_pkt->cblock. + return_code = COMMAND_OK; + mb->buffer_length = 1; + break; + } + } + + /* Disable FT1 MONITOR STATUS */ + if( chdlc_udp_pkt->data[0] == 0) { + + if( --rCount != 0) { + chdlc_udp_pkt->cblock. + return_code = COMMAND_OK; + mb->buffer_length = 1; + break; + } + } + + default: + /* it's a board command */ + mb->command = chdlc_udp_pkt->cblock.command; + mb->buffer_length = chdlc_udp_pkt->cblock.buffer_length; + if (mb->buffer_length) { + memcpy(&mb->data, (unsigned char *) chdlc_udp_pkt-> + data, mb->buffer_length); + } + /* run the command on the board */ + err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT; + if (err != COMMAND_OK) { + break; + } + + /* copy the result back to our buffer */ + memcpy(&chdlc_udp_pkt->cblock, mb, sizeof(cblock_t)); + + if (mb->buffer_length) { + memcpy(&chdlc_udp_pkt->data, &mb->data, + mb->buffer_length); + } + + } /* end of switch */ + } /* end of else */ + + /* Fill UDP TTL */ + chdlc_udp_pkt->ip_pkt.ttl = card->wandev.ttl; + + len = reply_udp(chdlc_priv_area->udp_pkt_data, mb->buffer_length); + + if(chdlc_priv_area->udp_pkt_src == UDP_PKT_FRM_NETWORK) { + if(!chdlc_send(card, chdlc_priv_area->udp_pkt_data, len)) { + ++ card->wandev.stats.tx_packets; +#ifdef LINUX_2_1 + card->wandev.stats.tx_bytes += len; +#endif + } + } else { + + /* Pass it up the stack + Allocate socket buffer */ + if ((new_skb = dev_alloc_skb(len)) != NULL) { + /* copy data into new_skb */ + + buf = skb_put(new_skb, len); + memcpy(buf, chdlc_priv_area->udp_pkt_data, len); + + /* Decapsulate pkt and pass it up the protocol stack */ + new_skb->protocol = htons(ETH_P_IP); + new_skb->dev = dev; + new_skb->mac.raw = new_skb->data; + + netif_rx(new_skb); + } else { + + printk(KERN_INFO "%s: no socket buffers available!\n", + card->devname); + } + } + + chdlc_priv_area->udp_pkt_lgth = 0; + + return 0; +} + +/*============================================================================ + * Initialize Receive and Transmit Buffers. + */ + +static void init_chdlc_tx_rx_buff( sdla_t* card, struct device *dev ) +{ + CHDLC_MAILBOX_STRUCT* mb = card->mbox; + CHDLC_TX_STATUS_EL_CFG_STRUCT *tx_config; + CHDLC_RX_STATUS_EL_CFG_STRUCT *rx_config; + char err; + + mb->buffer_length = 0; + mb->command = READ_CHDLC_CONFIGURATION; + err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT; + + if(err != COMMAND_OK) { + chdlc_error(card,err,mb); + return; + } + + if(card->hw.type == SDLA_S514) { + tx_config = (CHDLC_TX_STATUS_EL_CFG_STRUCT *)(card->hw.dpmbase + + (((CHDLC_CONFIGURATION_STRUCT *)mb->data)-> + ptr_CHDLC_Tx_stat_el_cfg_struct)); + rx_config = (CHDLC_RX_STATUS_EL_CFG_STRUCT *)(card->hw.dpmbase + + (((CHDLC_CONFIGURATION_STRUCT *)mb->data)-> + ptr_CHDLC_Rx_stat_el_cfg_struct)); + + /* Setup Head and Tails for buffers */ + card->u.c.txbuf_base = (void *)(card->hw.dpmbase + + tx_config->base_addr_Tx_status_elements); + card->u.c.txbuf_last = + (CHDLC_DATA_TX_STATUS_EL_STRUCT *) + card->u.c.txbuf_base + + (tx_config->number_Tx_status_elements - 1); + + card->u.c.rxbuf_base = (void *)(card->hw.dpmbase + + rx_config->base_addr_Rx_status_elements); + card->u.c.rxbuf_last = + (CHDLC_DATA_RX_STATUS_EL_STRUCT *) + card->u.c.rxbuf_base + + (rx_config->number_Rx_status_elements - 1); + + /* Set up next pointer to be used */ + card->u.c.txbuf = (void *)(card->hw.dpmbase + + tx_config->next_Tx_status_element_to_use); + card->u.c.rxmb = (void *)(card->hw.dpmbase + + rx_config->next_Rx_status_element_to_use); + } + else { + tx_config = (CHDLC_TX_STATUS_EL_CFG_STRUCT *)(card->hw.dpmbase + + (((CHDLC_CONFIGURATION_STRUCT *)mb->data)-> + ptr_CHDLC_Tx_stat_el_cfg_struct % SDLA_WINDOWSIZE)); + + rx_config = (CHDLC_RX_STATUS_EL_CFG_STRUCT *)(card->hw.dpmbase + + (((CHDLC_CONFIGURATION_STRUCT *)mb->data)-> + ptr_CHDLC_Rx_stat_el_cfg_struct % SDLA_WINDOWSIZE)); + + /* Setup Head and Tails for buffers */ + card->u.c.txbuf_base = (void *)(card->hw.dpmbase + + (tx_config->base_addr_Tx_status_elements % SDLA_WINDOWSIZE)); + card->u.c.txbuf_last = + (CHDLC_DATA_TX_STATUS_EL_STRUCT *)card->u.c.txbuf_base + + (tx_config->number_Tx_status_elements - 1); + card->u.c.rxbuf_base = (void *)(card->hw.dpmbase + + (rx_config->base_addr_Rx_status_elements % SDLA_WINDOWSIZE)); + card->u.c.rxbuf_last = + (CHDLC_DATA_RX_STATUS_EL_STRUCT *)card->u.c.rxbuf_base + + (rx_config->number_Rx_status_elements - 1); + + /* Set up next pointer to be used */ + card->u.c.txbuf = (void *)(card->hw.dpmbase + + (tx_config->next_Tx_status_element_to_use % SDLA_WINDOWSIZE)); + card->u.c.rxmb = (void *)(card->hw.dpmbase + + (rx_config->next_Rx_status_element_to_use % SDLA_WINDOWSIZE)); + } + + /* Setup Actual Buffer Start and end addresses */ + card->u.c.rx_base = rx_config->base_addr_Rx_buffer; + card->u.c.rx_top = rx_config->end_addr_Rx_buffer; + +} + +/*============================================================================= + * Perform Interrupt Test by running READ_CHDLC_CODE_VERSION command MAX_INTR + * _TEST_COUNTER times. + */ +static int intr_test( sdla_t* card, struct device *dev ) +{ + CHDLC_MAILBOX_STRUCT* mb = card->mbox; + int err,i; + + Intr_test_counter = 0; + + /* The critical flag is unset because during intialization (if_open) + * we want the interrupts to be enabled so that when the wpc_isr is + * called it does not exit due to critical flag set. + */ + + clear_bit(1, (void*)&card->wandev.critical); + err = chdlc_set_intr_mode(card, APP_INT_ON_COMMAND_COMPLETE); + + if (err == CMD_OK) { + for (i = 0; i < MAX_INTR_TEST_COUNTER; i ++) { + mb->buffer_length = 0; + mb->command = READ_CHDLC_CODE_VERSION; + err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT; + if (err != CMD_OK) + chdlc_error(card, err, mb); + } + } + else { + return err; + } + + err = chdlc_set_intr_mode(card, 0); + set_bit(1, (void*)&card->wandev.critical); + + + if (err != CMD_OK) + return err; + + return 0; +} + +/*============================================================================== + * Determine what type of UDP call it is. CPIPEAB ? + */ +static int udp_pkt_type(struct sk_buff *skb, sdla_t* card) +{ + chdlc_udp_pkt_t *chdlc_udp_pkt = (chdlc_udp_pkt_t *)skb->data; + + if (!strncmp(chdlc_udp_pkt->wp_mgmt.signature,UDPMGMT_SIGNATURE,8) && + (chdlc_udp_pkt->udp_pkt.udp_dst_port == ntohs(card->wandev.udp_port)) && + (chdlc_udp_pkt->ip_pkt.protocol == UDPMGMT_UDP_PROTOCOL) && + (chdlc_udp_pkt->wp_mgmt.request_reply == UDPMGMT_REQUEST)) { + return UDP_CPIPE_TYPE; + } + else return UDP_INVALID_TYPE; +} + +/*============================================================================ + * Set PORT state. + */ +static void port_set_state (sdla_t *card, int state) +{ + if (card->u.c.state != state) + { + switch (state) + { + case WAN_CONNECTED: + printk (KERN_INFO "%s: Link connected!\n", + card->devname); + break; + + case WAN_CONNECTING: + printk (KERN_INFO "%s: Link connecting...\n", + card->devname); + break; + + case WAN_DISCONNECTED: + printk (KERN_INFO "%s: Link disconnected!\n", + card->devname); + break; + } + + card->wandev.state = card->u.c.state = state; + } +} + +void s508_lock (sdla_t *card, unsigned long *smp_flags) +{ +#ifdef __SMP__ + spin_lock_irqsave(&card->lock, *smp_flags); + if (card->next){ + spin_lock(&card->next->lock); + } +#else + disable_irq(card->hw.irq); +#endif +} + +void s508_unlock (sdla_t *card, unsigned long *smp_flags) +{ +#ifdef __SMP__ + if (card->next){ + spin_unlock(&card->next->lock); + } + spin_unlock_irqrestore(&card->lock, *smp_flags); +#else + enable_irq(card->hw.irq); +#endif +} + +/****** End ****************************************************************/ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sdla_fr.c linux/drivers/net/sdla_fr.c --- v2.2.13/linux/drivers/net/sdla_fr.c Mon Jan 4 11:37:29 1999 +++ linux/drivers/net/sdla_fr.c Tue Jan 4 10:12:17 2000 @@ -1,16 +1,38 @@ /***************************************************************************** * sdla_fr.c WANPIPE(tm) Multiprotocol WAN Link Driver. Frame relay module. * -* Author(s): Gene Kozin -* Jaspreet Singh +* Author(s): Nenad Corbic +* Gideon Hack * -* Copyright: (c) 1995-1997 Sangoma Technologies Inc. +* Copyright: (c) 1995-1999 Sangoma Technologies Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * ============================================================================ +* Nov 08, 1999 Nenad Corbic o Combined all debug UDP calls into one function +* o Removed the ARP support. This has to be done +* in the next version. +* o Only a Node can implement NO signalling. +* Initialize DLCI during if_open() if NO +* signalling. +* o Took out IPX support, implement in next +* version +* Sep 29, 1999 Nenad Corbic o Added SMP support and changed the update +* function to use timer interrupt. +* o Fixed the CIR bug: Set the value of BC +* to CIR when the CIR is enabled. +* o Updated comments, statistics and tracing. +* Jun 02, 1999 Gideon Hack o Updated for S514 support. +* Sep 18, 1998 Jaspreet Singh o Updated for 2.2.X kernels. +* Jul 31, 1998 Jaspreet Singh o Removed wpf_poll routine. The channel/DLCI +* status is received through an event interrupt. +* Jul 08, 1998 David Fong o Added inverse ARP support. +* Mar 26, 1997 Jaspreet Singh o Returning return codes for failed UDP cmds. +* Jan 28, 1997 Jaspreet Singh o Improved handling of inactive DLCIs. +* Dec 30, 1997 Jaspreet Singh o Replaced dev_tint() with mark_bh(NET_BH) +* Dec 16, 1997 Jaspreet Singh o Implemented Multiple IPX support. * Nov 26, 1997 Jaspreet Singh o Improved load sharing with multiple boards * o Added Cli() to protect enabling of interrupts * while polling is called. @@ -42,7 +64,8 @@ * o Added ability to discard multicast and * broadcast source addressed packets * Jun 27, 1997 Jaspreet Singh o Added FT1 monitor capabilities -* New case (0x44) statement in if_send routine * Added a global variable rCount to keep track +* New case (0x44) statement in if_send routine +* Added a global variable rCount to keep track * of FT1 status enabled on the board. * May 29, 1997 Jaspreet Singh o Fixed major Flow Control Problem * With multiple boards a problem was seen where @@ -50,13 +73,15 @@ * packet after running for a while. The code * got into a stage where the interrupts were * disabled and dev->tbusy was set to 1. -* This caused the If_send() routine to get into* the if clause for it(0,dev->tbusy) +* This caused the If_send() routine to get into +* the if clause for it(0,dev->tbusy) * forever. * The code got into this stage due to an -* interrupt occurring within the if clause for +* interrupt occuring within the if clause for * set_bit(0,dev->tbusy). Since an interrupt * disables furhter transmit interrupt and -* makes dev->tbusy = 0, this effect was undone * by making dev->tbusy = 1 in the if clause. +* makes dev->tbusy = 0, this effect was undone +* by making dev->tbusy = 1 in the if clause. * The Fix checks to see if Transmit interrupts * are disabled then do not make dev->tbusy = 1 * Introduced a global variable: int_occur and @@ -83,6 +108,7 @@ * Jan 02, 1997 Gene Kozin Initial version. *****************************************************************************/ +#include #include /* printk(), and other useful stuff */ #include /* offsetof(), etc. */ #include /* return codes */ @@ -93,140 +119,133 @@ #include /* ARPHRD_* defines */ #include /* htons(), etc. */ #include /* for inb(), outb(), etc. */ -#include /* for do_gettimeofday */ -#define _GNUC_ -#include /* frame relay firmware API definitions */ +#include /* for do_gettimeofday */ +#include /* sockaddr_in */ +#include /* in_ntoa(), etc... */ #include +#include +#include +#include /* Dynamic Route Creation */ +#include +#include /* frame relay firmware API definitions */ +#if LINUX_VERSION_CODE < 0x020125 +#define test_and_set_bit set_bit +#endif + /****** Defines & Macros ****************************************************/ -#define MAX_CMD_RETRY 10 /* max number of firmware retries */ -#define FR_HEADER_LEN 8 /* max encapsulation header size */ -#define FR_CHANNEL_MTU 1500 /* unfragmented logical channel MTU */ +#define MAX_CMD_RETRY 10 /* max number of firmware retries */ -/* Q.922 frame types */ +#define FR_HEADER_LEN 8 /* max encapsulation header size */ +#define FR_CHANNEL_MTU 1500 /* unfragmented logical channel MTU */ -#define Q922_UI 0x03 /* Unnumbered Info frame */ -#define Q922_XID 0xAF /* ??? */ +/* Q.922 frame types */ +#define Q922_UI 0x03 /* Unnumbered Info frame */ +#define Q922_XID 0xAF /* DLCI configured or not */ - #define DLCI_NOT_CONFIGURED 0x00 #define DLCI_CONFIG_PENDING 0x01 #define DLCI_CONFIGURED 0x02 /* CIR enabled or not */ - #define CIR_ENABLED 0x00 #define CIR_DISABLED 0x01 -/* Interrupt mode for DLCI = 0 */ - -#define BUFFER_INTR_MODE 0x00 -#define DLCI_LIST_INTR_MODE 0x01 - -/* Transmit Interrupt Status */ - -#define DISABLED 0x00 -#define WAITING_TO_BE_ENABLED 0x01 +#define WANPIPE 0x00 +#define API 0x01 +#define FRAME_RELAY_API 1 /* For handle_IPXWAN() */ - #define CVHexToAscii(b) (((unsigned char)(b) > (unsigned char)9) ? ((unsigned char)'A' + ((unsigned char)(b) - (unsigned char)10)) : ((unsigned char)'0' + (unsigned char)(b))) - + /****** Data Structures *****************************************************/ /* This is an extention of the 'struct device' we create for each network * interface to keep the rest of channel-specific data. */ -typedef struct fr_channel { - char name[WAN_IFNAME_SZ + 1]; /* interface name, ASCIIZ */ - unsigned dlci_configured; /* check whether configured or not */ - unsigned cir_status; /* check whether CIR enabled or not */ - unsigned dlci; /* logical channel number */ - unsigned cir; /* committed information rate */ - unsigned bc; /* committed burst size */ - unsigned be; /* excess burst size */ - unsigned mc; /* multicast support on or off */ - unsigned tx_int_status; /* Transmit Interrupt Status */ +typedef struct fr_channel +{ + char name[WAN_IFNAME_SZ+1]; /* interface name, ASCIIZ */ + unsigned dlci_configured ; /* check whether configured or not */ + unsigned cir_status; /* check whether CIR enabled or not */ + unsigned dlci; /* logical channel number */ + unsigned cir; /* committed information rate */ + unsigned bc; /* committed burst size */ + unsigned be; /* excess burst size */ + unsigned mc; /* multicast support on or off */ + unsigned tx_int_status; /* Transmit Interrupt Status */ unsigned short pkt_length; /* Packet Length */ - unsigned long router_start_time; /* Router start time in seconds */ + unsigned long router_start_time;/* Router start time in seconds */ unsigned long tick_counter; /* counter for transmit time out */ char dev_pending_devtint; /* interface pending dev_tint() */ - char state; /* channel state */ - void *dlci_int_interface; /* pointer to the DLCI Interface */ - unsigned long IB_addr; /* physical address of Interface Byte */ + char state; /* channel state */ + void *dlci_int_interface; /* pointer to the DLCI Interface */ + unsigned long IB_addr; /* physical address of Interface Byte */ unsigned long state_tick; /* time of the last state change */ - sdla_t *card; /* -> owner */ - struct net_device_stats ifstats; /* interface statistics */ - unsigned long if_send_entry; - unsigned long if_send_skb_null; - unsigned long if_send_broadcast; - unsigned long if_send_multicast; - unsigned long if_send_critical_ISR; - unsigned long if_send_critical_non_ISR; - unsigned long if_send_busy; - unsigned long if_send_busy_timeout; - unsigned long if_send_FPIPE_request; - unsigned long if_send_DRVSTATS_request; - unsigned long if_send_wan_disconnected; - unsigned long if_send_dlci_disconnected; - unsigned long if_send_no_bfrs; - unsigned long if_send_adptr_bfrs_full; - unsigned long if_send_bfrs_passed_to_adptr; - unsigned long rx_intr_no_socket; - unsigned long rx_intr_dev_not_started; - unsigned long rx_intr_FPIPE_request; - unsigned long rx_intr_DRVSTATS_request; - unsigned long rx_intr_bfr_not_passed_to_stack; - unsigned long rx_intr_bfr_passed_to_stack; - unsigned long UDP_FPIPE_mgmt_kmalloc_err; - unsigned long UDP_FPIPE_mgmt_direction_err; - unsigned long UDP_FPIPE_mgmt_adptr_type_err; - unsigned long UDP_FPIPE_mgmt_adptr_cmnd_OK; - unsigned long UDP_FPIPE_mgmt_adptr_cmnd_timeout; - unsigned long UDP_FPIPE_mgmt_adptr_send_passed; - unsigned long UDP_FPIPE_mgmt_adptr_send_failed; - unsigned long UDP_FPIPE_mgmt_not_passed_to_stack; - unsigned long UDP_FPIPE_mgmt_passed_to_stack; - unsigned long UDP_FPIPE_mgmt_no_socket; - unsigned long UDP_DRVSTATS_mgmt_kmalloc_err; - unsigned long UDP_DRVSTATS_mgmt_adptr_cmnd_OK; - unsigned long UDP_DRVSTATS_mgmt_adptr_cmnd_timeout; - unsigned long UDP_DRVSTATS_mgmt_adptr_send_passed; - unsigned long UDP_DRVSTATS_mgmt_adptr_send_failed; - unsigned long UDP_DRVSTATS_mgmt_not_passed_to_stack; - unsigned long UDP_DRVSTATS_mgmt_passed_to_stack; - unsigned long UDP_DRVSTATS_mgmt_no_socket; + unsigned char enable_IPX; /* Enable/Disable the use of IPX */ + unsigned long network_number; /* Internal Network Number for IPX*/ + sdla_t *card; /* -> owner */ + unsigned route_flag; /* Add/Rem dest addr in route tables */ + unsigned inarp; /* Inverse Arp Request status */ + int inarp_interval; /* Time between InArp Requests */ + unsigned long inarp_tick; /* InArp jiffies tick counter */ + struct net_device_stats ifstats; /* interface statistics */ + if_send_stat_t drvstats_if_send; + rx_intr_stat_t drvstats_rx_intr; + pipe_mgmt_stat_t drvstats_gen; + + unsigned char usedby; /* Used by WANPIPE or API */ + unsigned long router_up_time; + + unsigned short transmit_length; + char transmit_buffer[FR_MAX_NO_DATA_BYTES_IN_FRAME]; } fr_channel_t; -typedef struct dlci_status { - unsigned short dlci PACKED; - unsigned char state PACKED; +/* Route Flag options */ +#define NO_ROUTE 0x00 +#define ADD_ROUTE 0x01 +#define ROUTE_ADDED 0x02 +#define REMOVE_ROUTE 0x03 + +/* inarp options */ +#define INARP_NONE 0x00 +#define INARP_REQUEST 0x01 +#define INARP_CONFIGURED 0x02 + +/* reasons for enabling the timer interrupt on the adapter */ +#define TMR_INT_ENABLED_UDP 0x01 +#define TMR_INT_ENABLED_UPDATE 0x02 + + +typedef struct dlci_status +{ + unsigned short dlci PACKED; + unsigned char state PACKED; } dlci_status_t; -typedef struct dlci_IB_mapping { - unsigned short dlci PACKED; - unsigned long addr_value PACKED; +typedef struct dlci_IB_mapping +{ + unsigned short dlci PACKED; + unsigned long addr_value PACKED; } dlci_IB_mapping_t; /* This structure is used for DLCI list Tx interrupt mode. It is used to enable interrupt bit and set the packet length for transmission */ +typedef struct fr_dlci_interface +{ + unsigned char gen_interrupt PACKED; + unsigned short packet_length PACKED; + unsigned char reserved PACKED; +} fr_dlci_interface_t; -typedef struct fr_dlci_interface { - unsigned char gen_interrupt PACKED; - unsigned short packet_length PACKED; - unsigned char reserved PACKED; -} fr_dlci_interface_t; - -static unsigned short num_frames; -static unsigned long curr_trace_addr; -static unsigned long start_trace_addr; -static unsigned short available_buffer_space; -static char TracingEnabled; /* variable for keeping track of enabling/disabling FT1 monitor status */ +/* variable for keeping track of enabling/disabling FT1 monitor status */ static int rCount = 0; + +extern int ip_rt_ioctl(unsigned int, void *); extern void disable_irq(unsigned int); extern void enable_irq(unsigned int); @@ -234,70 +253,96 @@ * interrupt test routine */ static int Intr_test_counter; - /****** Function Prototypes *************************************************/ /* WAN link driver entry points. These are called by the WAN router module. */ -static int update(wan_device_t * wandev); -static int new_if(wan_device_t * wandev, struct device *dev, - wanif_conf_t * conf); -static int del_if(wan_device_t * wandev, struct device *dev); +static int update(wan_device_t *wandev); +static int new_if(wan_device_t *wandev, struct device *dev, wanif_conf_t *conf); +static int del_if(wan_device_t *wandev, struct device *dev); + /* WANPIPE-specific entry points */ static int wpf_exec(struct sdla *card, void *u_cmd, void *u_data); + /* Network device interface */ static int if_init(struct device *dev); static int if_open(struct device *dev); static int if_close(struct device *dev); -static int if_header(struct sk_buff *skb, struct device *dev, - unsigned short type, void *daddr, void *saddr, unsigned len); +static int if_header(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len); static int if_rebuild_hdr(struct sk_buff *skb); static int if_send(struct sk_buff *skb, struct device *dev); +static int chk_bcast_mcast_addr(sdla_t *card, struct device* dev, + struct sk_buff *skb); static struct net_device_stats *if_stats(struct device *dev); + /* Interrupt handlers */ -static void fr502_isr(sdla_t * card); -static void fr508_isr(sdla_t * card); -static void fr502_rx_intr(sdla_t * card); -static void fr508_rx_intr(sdla_t * card); -static void tx_intr(sdla_t * card); -static void spur_intr(sdla_t * card); -/* Background polling routines */ -static void wpf_poll(sdla_t * card); +static void fr_isr(sdla_t *card); +static void rx_intr(sdla_t *card); +static void tx_intr(sdla_t *card); +static void timer_intr(sdla_t *card); +static void spur_intr(sdla_t *card); + /* Frame relay firmware interface functions */ -static int fr_read_version(sdla_t * card, char *str); -static int fr_configure(sdla_t * card, fr_conf_t * conf); -static int fr_dlci_configure(sdla_t * card, fr_dlc_conf_t * conf, unsigned dlci); -static int fr_set_intr_mode(sdla_t * card, unsigned mode, unsigned mtu); -static int fr_comm_enable(sdla_t * card); -static int fr_comm_disable(sdla_t * card); -static int fr_get_err_stats(sdla_t * card); -static int fr_get_stats(sdla_t * card); -static int fr_add_dlci(sdla_t * card, int dlci, int num); -static int fr_activate_dlci(sdla_t * card, int dlci, int num); -static int fr_issue_isf(sdla_t * card, int isf); -static int fr502_send(sdla_t * card, int dlci, int attr, int len, void *buf); -static int fr508_send(sdla_t * card, int dlci, int attr, int len, void *buf); +static int fr_read_version(sdla_t *card, char *str); +static int fr_configure(sdla_t *card, fr_conf_t *conf); +static int fr_dlci_configure(sdla_t *card, fr_dlc_conf_t *conf, unsigned dlci); +static int fr_init_dlci (sdla_t *card, fr_channel_t *chan); +static int fr_set_intr_mode (sdla_t *card, unsigned mode, unsigned mtu, unsigned short timeout); +static int fr_comm_enable(sdla_t *card); +static int fr_comm_disable(sdla_t *card); +static int fr_get_err_stats(sdla_t *card); +static int fr_get_stats(sdla_t *card); +static int fr_add_dlci(sdla_t *card, int dlci); +static int fr_activate_dlci(sdla_t *card, int dlci); +static int fr_delete_dlci (sdla_t* card, int dlci); +static int fr_issue_isf(sdla_t *card, int isf); +static int fr_send(sdla_t *card, int dlci, unsigned char attr, int len, + void *buf); + /* Firmware asynchronous event handlers */ -static int fr_event(sdla_t * card, int event, fr_mbox_t * mbox); -static int fr_modem_failure(sdla_t * card, fr_mbox_t * mbox); -static int fr_dlci_change(sdla_t * card, fr_mbox_t * mbox); +static int fr_event(sdla_t *card, int event, fr_mbox_t *mbox); +static int fr_modem_failure(sdla_t *card, fr_mbox_t *mbox); +static int fr_dlci_change(sdla_t *card, fr_mbox_t *mbox); + /* Miscellaneous functions */ static int update_chan_state(struct device *dev); static void set_chan_state(struct device *dev, int state); -static struct device *find_channel(sdla_t * card, unsigned dlci); -static int is_tx_ready(sdla_t * card, fr_channel_t * chan); +static struct device *find_channel(sdla_t *card, unsigned dlci); +static int is_tx_ready(sdla_t *card, fr_channel_t *chan); static unsigned int dec_to_uint(unsigned char *str, int len); -static int reply_udp(unsigned char *data, unsigned int mbox_len); -static int intr_test(sdla_t * card); -static void init_chan_statistics(fr_channel_t * chan); -static void init_global_statistics(sdla_t * card); -static void read_DLCI_IB_mapping(sdla_t * card, fr_channel_t * chan); +static int reply_udp( unsigned char *data, unsigned int mbox_len ); + +static int intr_test( sdla_t* card ); +static void init_chan_statistics( fr_channel_t* chan ); +static void init_global_statistics( sdla_t* card ); +static void read_DLCI_IB_mapping( sdla_t* card, fr_channel_t* chan ); +static void setup_for_delayed_transmit(struct device* dev, void* buf, + unsigned len); + + +/* Inverse ARP and Dynamic routing functions */ +int process_ARP(arphdr_1490_t *ArpPacket, sdla_t *card, struct device *dev); +int is_arp(void *buf); +int send_inarp_request(sdla_t *card, struct device *dev); + /* Udp management functions */ -static int process_udp_mgmt_pkt(char udp_pkt_src, sdla_t * card, struct sk_buff *skb, struct device *dev, int dlci, fr_channel_t * chan); -static int process_udp_driver_call(char udp_pkt_src, sdla_t * card, struct sk_buff *skb, struct device *dev, int dlci, fr_channel_t * chan); -static int udp_pkt_type(struct sk_buff *skb, sdla_t * card); +static int process_udp_mgmt_pkt(sdla_t *card); +static int udp_pkt_type( struct sk_buff *skb, sdla_t *card ); +static int store_udp_mgmt_pkt(int udp_type, char udp_pkt_src, sdla_t* card, + struct sk_buff *skb, int dlci); + /* IPX functions */ -static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming); -static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number); +static void switch_net_numbers(unsigned char *sendpacket, + unsigned long network_number, unsigned char incoming); + +static int handle_IPXWAN(unsigned char *sendpacket, char *devname, + unsigned char enable_IPX, unsigned long network_number); + +/* Lock Functions: SMP supported */ +void s508_s514_unlock(sdla_t *card, unsigned long *smp_flags); +void s508_s514_lock(sdla_t *card, unsigned long *smp_flags); + +unsigned short calc_checksum (char *, int); + /****** Public Functions ****************************************************/ @@ -313,154 +358,251 @@ * Return: 0 o.k. * < 0 failure. */ -int wpf_init(sdla_t * card, wandev_conf_t * conf) +int wpf_init(sdla_t *card, wandev_conf_t *conf) { - union { + + int err; + + union + { char str[80]; fr_conf_t cfg; } u; + fr_buf_info_t* buf_info; int i; + /* Verify configuration ID */ - if (conf->config_id != WANCONFIG_FR) - { + if (conf->config_id != WANCONFIG_FR) { + printk(KERN_INFO "%s: invalid configuration ID %u!\n", - card->devname, conf->config_id); + card->devname, conf->config_id); return -EINVAL; + } + /* Initialize protocol-specific fields of adapter data space */ - switch (card->hw.fwid) - { - case SFID_FR502: - card->mbox = (void *) (card->hw.dpmbase + FR502_MBOX_OFFS); - card->rxmb = (void *) (card->hw.dpmbase + FR502_RXMB_OFFS); - card->flags = (void *) (card->hw.dpmbase + FR502_FLAG_OFFS); - card->isr = &fr502_isr; - break; + switch (card->hw.fwid) { + case SFID_FR508: - card->mbox = (void *) (card->hw.dpmbase + FR508_MBOX_OFFS); - card->flags = (void *) (card->hw.dpmbase + FR508_FLAG_OFFS); - card->isr = &fr508_isr; + card->mbox = (void*)(card->hw.dpmbase + + FR508_MBOX_OFFS); + card->flags = (void*)(card->hw.dpmbase + + FR508_FLAG_OFFS); + if(card->hw.type == SDLA_S514) { + card->mbox += FR_MB_VECTOR; + card->flags += FR_MB_VECTOR; + } + card->isr = &fr_isr; break; + default: return -EINVAL; } + /* Read firmware version. Note that when adapter initializes, it * clears the mailbox, so it may appear that the first command was * executed successfully when in fact it was merely erased. To work * around this, we execute the first command twice. */ + if (fr_read_version(card, NULL) || fr_read_version(card, u.str)) return -EIO; + printk(KERN_INFO "%s: running frame relay firmware v%s\n", - card->devname, u.str); + card->devname, u.str); + /* Adjust configuration */ - conf->mtu = max(min(conf->mtu, 4080), FR_CHANNEL_MTU + FR_HEADER_LEN); + conf->mtu += FR_HEADER_LEN; + conf->mtu = (conf->mtu >= MIN_LGTH_FR_DATA_CFG) ? + min(conf->mtu, FR_MAX_NO_DATA_BYTES_IN_FRAME) : + FR_CHANNEL_MTU + FR_HEADER_LEN; + conf->bps = min(conf->bps, 2048000); - /* Configure adapter firmware */ + + /* Initialze the configuration structure sent to the board to zero */ memset(&u.cfg, 0, sizeof(u.cfg)); - u.cfg.mtu = conf->mtu; - u.cfg.kbps = conf->bps / 1000; - u.cfg.cir_fwd = u.cfg.cir_bwd = 16; - u.cfg.bc_fwd = u.cfg.bc_bwd = 16; - if (conf->station == WANOPT_CPE) - { - u.cfg.options = 0x0080; - printk(KERN_INFO "%s: Global CIR enabled by Default\n", card->devname); - } - else - { - u.cfg.options = 0x0081; - } - switch (conf->u.fr.signalling) - { - case WANOPT_FR_Q933: - u.cfg.options |= 0x0200; + + memset(card->u.f.dlci_to_dev_map, 0, sizeof(card->u.f.dlci_to_dev_map)); + + /* Configure adapter firmware */ + + u.cfg.mtu = conf->mtu; + u.cfg.kbps = conf->bps / 1000; + + u.cfg.cir_fwd = u.cfg.cir_bwd = 16; + u.cfg.bc_fwd = u.cfg.bc_bwd = 16; + + u.cfg.options = 0x0000; + printk(KERN_INFO "%s: Global CIR enabled by Default\n", card->devname); + + switch (conf->u.fr.signalling) { + + case WANOPT_FR_ANSI: + u.cfg.options = 0x0000; + break; + + case WANOPT_FR_Q933: + u.cfg.options |= 0x0200; + break; + + case WANOPT_FR_LMI: + u.cfg.options |= 0x0400; break; - case WANOPT_FR_LMI: - u.cfg.options |= 0x0400; + + case WANOPT_NO: + u.cfg.options |= 0x0800; break; + default: + printk(KERN_INFO "%s: Illegal Signalling option\n", + card->wandev.name); + return -EINVAL; } - if (conf->station == WANOPT_CPE) - { + + + card->wandev.signalling = conf->u.fr.signalling; + + if (conf->station == WANOPT_CPE) { + + + if (conf->u.fr.signalling == WANOPT_NO){ + printk(KERN_INFO + "%s: ERROR - For NO signalling, station must be set to Node!", + card->devname); + return -EINVAL; + } + + u.cfg.station = 0; u.cfg.options |= 0x8000; /* auto config DLCI */ - card->u.f.dlci_num = 0; - } - else - { + card->u.f.dlci_num = 0; + + } else { + u.cfg.station = 1; /* switch emulation mode */ + /* For switch emulation we have to create a list of dlci(s) * that will be sent to be global SET_DLCI_CONFIGURATION * command in fr_configure() routine. */ - card->u.f.dlci_num = min(max(conf->u.fr.dlci_num, 1), 100); - for (i = 0; i < card->u.f.dlci_num; i++) - { - card->u.f.node_dlci[i] = (unsigned short) - conf->u.fr.dlci[i] ? conf->u.fr.dlci[i] : 16; + + card->u.f.dlci_num = min(max(conf->u.fr.dlci_num, 1), 100); + + for ( i = 0; i < card->u.f.dlci_num; i++) { + + card->u.f.node_dlci[i] = (unsigned short) + conf->u.fr.dlci[i] ? conf->u.fr.dlci[i] : 16; + } } + if (conf->clocking == WANOPT_INTERNAL) u.cfg.port |= 0x0001; + if (conf->interface == WANOPT_RS232) u.cfg.port |= 0x0002; + if (conf->u.fr.t391) u.cfg.t391 = min(conf->u.fr.t391, 30); else u.cfg.t391 = 5; + if (conf->u.fr.t392) u.cfg.t392 = min(conf->u.fr.t392, 30); else u.cfg.t392 = 15; + if (conf->u.fr.n391) u.cfg.n391 = min(conf->u.fr.n391, 255); else u.cfg.n391 = 2; + if (conf->u.fr.n392) u.cfg.n392 = min(conf->u.fr.n392, 10); else - u.cfg.n392 = 3; + u.cfg.n392 = 3; + if (conf->u.fr.n393) u.cfg.n393 = min(conf->u.fr.n393, 10); else u.cfg.n393 = 4; + if (fr_configure(card, &u.cfg)) return -EIO; - if (card->hw.fwid == SFID_FR508) - { - fr_buf_info_t *buf_info = - (void *) (card->hw.dpmbase + FR508_RXBC_OFFS); - card->rxmb = (void *) (buf_info->rse_next - FR_MB_VECTOR + card->hw.dpmbase); - card->u.f.rxmb_base = (void *) (buf_info->rse_base - FR_MB_VECTOR + card->hw.dpmbase); - card->u.f.rxmb_last = (void *) (buf_info->rse_base + (buf_info->rse_num - 1) * - sizeof(fr_buf_ctl_t) - FR_MB_VECTOR + card->hw.dpmbase); - card->u.f.rx_base = buf_info->buf_base; - card->u.f.rx_top = buf_info->buf_top; - } - card->wandev.mtu = conf->mtu; - card->wandev.bps = conf->bps; - card->wandev.interface = conf->interface; - card->wandev.clocking = conf->clocking; - card->wandev.station = conf->station; - card->poll = &wpf_poll; - card->exec = &wpf_exec; - card->wandev.update = &update; - card->wandev.new_if = &new_if; - card->wandev.del_if = &del_if; - card->wandev.state = WAN_DISCONNECTED; - card->wandev.ttl = conf->ttl; - card->wandev.udp_port = conf->udp_port; - card->wandev.enable_tx_int = 0; - card->irq_dis_if_send_count = 0; - card->irq_dis_poll_count = 0; - card->wandev.enable_IPX = conf->enable_IPX; - if (conf->network_number) - card->wandev.network_number = conf->network_number; - else - card->wandev.network_number = 0xDEADBEEF; + + if (card->hw.type == SDLA_S514) { + + buf_info = (void*)(card->hw.dpmbase + FR_MB_VECTOR + + FR508_RXBC_OFFS); + + card->rxmb = (void*)(buf_info->rse_next + card->hw.dpmbase); + + card->u.f.rxmb_base = + (void*)(buf_info->rse_base + card->hw.dpmbase); + + card->u.f.rxmb_last = + (void*)(buf_info->rse_base + + (buf_info->rse_num - 1) * sizeof(fr_rx_buf_ctl_t) + + card->hw.dpmbase); + } + + else { + buf_info = (void*)(card->hw.dpmbase + FR508_RXBC_OFFS); + + card->rxmb = (void*)(buf_info->rse_next - + FR_MB_VECTOR + card->hw.dpmbase); + + card->u.f.rxmb_base = + (void*)(buf_info->rse_base - + FR_MB_VECTOR + card->hw.dpmbase); + + card->u.f.rxmb_last = + (void*)(buf_info->rse_base + + (buf_info->rse_num - 1) * sizeof(fr_rx_buf_ctl_t) - + FR_MB_VECTOR + card->hw.dpmbase); + } + + card->u.f.rx_base = buf_info->buf_base; + card->u.f.rx_top = buf_info->buf_top; + + card->u.f.tx_interrupts_pending = 0; + + card->wandev.mtu = conf->mtu; + card->wandev.bps = conf->bps; + card->wandev.interface = conf->interface; + card->wandev.clocking = conf->clocking; + card->wandev.station = conf->station; + card->poll = NULL; + card->exec = &wpf_exec; + card->wandev.update = &update; + card->wandev.new_if = &new_if; + card->wandev.del_if = &del_if; + card->wandev.state = WAN_DISCONNECTED; + card->wandev.ttl = conf->ttl; + card->wandev.udp_port = conf->udp_port; + /* Intialize global statistics for a card */ - init_global_statistics(card); - TracingEnabled = 0; - return 0; + init_global_statistics( card ); + + card->TracingEnabled = 0; + + /* Interrupt Test */ + Intr_test_counter = 0; + card->intr_mode = INTR_TEST_MODE; + err = intr_test( card ); + + if (err || (Intr_test_counter < MAX_INTR_TEST_COUNTER)) { + printk( + "%s: Interrupt Test Failed, Counter: %i\n", + card->devname, Intr_test_counter); + printk( "Please choose another interrupt\n"); + err = -EIO; + return err; + } + + printk(KERN_INFO "%s: Interrupt Test Passed, Counter: %i\n", + card->devname, Intr_test_counter); + + + return 0; } /******* WAN Device Driver Entry Points *************************************/ @@ -468,21 +610,38 @@ /*============================================================================ * Update device status & statistics. */ - -static int update(wan_device_t * wandev) +static int update (wan_device_t* wandev) { - sdla_t *card; + volatile sdla_t* card; + unsigned long timeout; + fr508_flags_t* flags; + /* sanity checks */ if ((wandev == NULL) || (wandev->private == NULL)) return -EFAULT; + if (wandev->state == WAN_UNCONFIGURED) return -ENODEV; - if (test_and_set_bit(0, (void *) &wandev->critical)) + + if (test_bit(1, (void*)&wandev->critical)) return -EAGAIN; + card = wandev->private; - fr_get_err_stats(card); - fr_get_stats(card); - wandev->critical = 0; + flags = card->flags; + + + card->u.f.update_comms_stats = 1; + card->u.f.timer_int_enabled |= TMR_INT_ENABLED_UPDATE; + flags->imask |= FR_INTR_TIMER; + timeout = jiffies; + for(;;) { + if(card->u.f.update_comms_stats == 0) + break; + if ((jiffies - timeout) > (1 * HZ)){ + card->u.f.update_comms_stats = 0; + return -EAGAIN; + } + } return 0; } @@ -498,85 +657,216 @@ * Return: 0 o.k. * < 0 failure (channel will not be created) */ - -static int new_if(wan_device_t * wandev, struct device *dev, wanif_conf_t * conf) +static int new_if (wan_device_t* wandev, struct device* dev, wanif_conf_t* conf) { - sdla_t *card = wandev->private; - fr_channel_t *chan; + sdla_t* card = wandev->private; + fr_channel_t* chan; + int dlci = 0; int err = 0; - if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)) - { + + if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)) { + printk(KERN_INFO "%s: invalid interface name!\n", - card->devname); + card->devname); return -EINVAL; } + /* allocate and initialize private data */ chan = kmalloc(sizeof(fr_channel_t), GFP_KERNEL); + if (chan == NULL) return -ENOMEM; + memset(chan, 0, sizeof(fr_channel_t)); strcpy(chan->name, conf->name); chan->card = card; + /* verify media address */ - if (is_digit(conf->addr[0])) - { - int dlci = dec_to_uint(conf->addr, 0); - if (dlci && (dlci <= 4095)) - { + if (is_digit(conf->addr[0])) { + + dlci = dec_to_uint(conf->addr, 0); + + if (dlci && (dlci <= HIGHEST_VALID_DLCI)) { + chan->dlci = dlci; - } - else - { - printk(KERN_ERR "%s: invalid DLCI %u on interface %s!\n", - wandev->name, dlci, chan->name); + + } else { + + printk(KERN_ERR + "%s: invalid DLCI %u on interface %s!\n", + wandev->name, dlci, chan->name); err = -EINVAL; } - } - else - { - printk(KERN_ERR "%s: invalid media address on interface %s!\n", - wandev->name, chan->name); + + } else { + printk(KERN_ERR + "%s: invalid media address on interface %s!\n", + wandev->name, chan->name); err = -EINVAL; } - if (err) - { + + /* Setup wanpipe as a router (WANPIPE) or as an API */ + if(strcmp(conf->usedby, "WANPIPE") == 0){ + printk(KERN_INFO "%s: Running in WANPIPE mode %s\n", + wandev->name, chan->name); + chan->usedby = WANPIPE; + + } else if(strcmp(conf->usedby, "API") == 0){ + +#ifdef FRAME_RELAY_API + chan->usedby = API; + printk(KERN_INFO "%s: Running in API mode %s\n", + wandev->name, chan->name); +#else + printk(KERN_INFO "%s: API Mode is not supported !\n", + wandev->name); + printk(KERN_INFO + "%s: API patch can be obtained from Sangoma Tech.\n", + wandev->name); + err = -EINVAL; +#endif + } + + if (err) { + kfree(chan); return err; } + + card->u.f.dlci_to_dev_map[dlci] = dev; + /* place cir,be,bc and other channel specific information into the * chan structure - */ - if (conf->cir) - { - chan->cir = max(1, min(conf->cir, 512)); - chan->cir_status = CIR_ENABLED; - if (conf->bc) - chan->bc = max(1, min(conf->bc, 512)); - if (conf->be) - chan->be = max(0, min(conf->be, 511)); - } - else + */ + if (conf->cir) { + + chan->cir = max( 1, min( conf->cir, 512 ) ); + chan->cir_status = CIR_ENABLED; + + + /* If CIR is enabled, force BC to equal CIR + * this solves number of potential problems if CIR is + * set and BC is not + */ + chan->bc = chan->cir; + + if (conf->be){ + chan->be = max( 0, min( conf->be, 511) ); + }else{ + conf->be = 0; + } + + printk (KERN_INFO "%s: CIR enabled for DLCI %i \n", + wandev->name,chan->dlci); + printk (KERN_INFO "%s: CIR = %i ; BC = %i ; BE = %i\n", + wandev->name,chan->cir,chan->bc,chan->be); + + + }else{ chan->cir_status = CIR_DISABLED; + printk (KERN_INFO "%s: CIR disabled for DLCI %i\n", + wandev->name,chan->dlci); + } + chan->mc = conf->mc; - chan->dlci_configured = DLCI_NOT_CONFIGURED; - chan->tx_int_status = DISABLED; + + /* FIXME: ARP is not supported by this frame relay verson */ + if (conf->inarp == WANOPT_YES){ + printk(KERN_INFO "%s: ERROR - This version of WANPIPE doesn't support ARPs\n", + card->devname); + + //chan->inarp = conf->inarp ? INARP_REQUEST : INARP_NONE; + //chan->inarp_interval = conf->inarp_interval ? conf->inarp_interval : 10; + kfree(chan); + return -EINVAL; + }else{ + chan->inarp = INARP_NONE; + chan->inarp_interval = 10; + } + + chan->dlci_configured = DLCI_NOT_CONFIGURED; + + + /*FIXME: IPX disabled in this WANPIPE version */ + if (conf->enable_IPX == WANOPT_YES){ + printk(KERN_INFO "%s: ERROR - This version of WANPIPE doesn't support IPX\n", + card->devname); + kfree(chan); + return -EINVAL; + }else{ + chan->enable_IPX = WANOPT_NO; + } + + if (conf->network_number){ + chan->network_number = conf->network_number; + }else{ + chan->network_number = 0xDEADBEEF; + } + + chan->route_flag = NO_ROUTE; + init_chan_statistics(chan); + + chan->transmit_length = 0; + /* prepare network device data space for registration */ dev->name = chan->name; dev->init = &if_init; dev->priv = chan; + + + /* Enable Interrupts and Communications */ + if (!wandev->new_if_cnt){ + fr508_flags_t* flags = card->flags; + + wandev->new_if_cnt++; + + /* + If you enable comms and then set ints, you get a Tx int as you + perform the SET_INT_TRIGGERS command. So, we only set int + triggers and then adjust the interrupt mask (to disable Tx ints) + before enabling comms. + */ + if (fr_set_intr_mode(card, (FR_INTR_RXRDY | FR_INTR_TXRDY | + FR_INTR_DLC | FR_INTR_TIMER | FR_INTR_TX_MULT_DLCIs) , + card->wandev.mtu, 0)) { + kfree(chan); + return -EIO; + } + + flags->imask &= ~(FR_INTR_TXRDY | FR_INTR_TIMER); + + if (fr_comm_enable(card)) { + kfree(chan); + return -EIO; + } + wanpipe_set_state(card, WAN_CONNECTED); + } + return 0; } + /*============================================================================ * Delete logical channel. */ -static int del_if(wan_device_t * wandev, struct device *dev) +static int del_if (wan_device_t* wandev, struct device* dev) { - if (dev->priv) - { + sdla_t *card = wandev->private; + + /* Execute shutdown very first time we enter del_if */ + + if (!wandev->del_if_cnt) { + wandev->del_if_cnt++; + wanpipe_set_state(card, WAN_DISCONNECTED); + fr_set_intr_mode(card, 0, 0, 0); + fr_comm_disable(card); + } + + if (dev->priv) { kfree(dev->priv); dev->priv = NULL; } + return 0; } @@ -585,41 +875,47 @@ /*============================================================================ * Execute adapter interface command. */ -static int wpf_exec(struct sdla *card, void *u_cmd, void *u_data) +static int wpf_exec (struct sdla* card, void* u_cmd, void* u_data) { - fr_mbox_t *mbox = card->mbox; + fr_mbox_t* mbox = card->mbox; int retry = MAX_CMD_RETRY; int err, len; fr_cmd_t cmd; - if(copy_from_user((void *) &cmd, u_cmd, sizeof(cmd))) - return -EFAULT; + + if(copy_from_user((void*)&cmd, u_cmd, sizeof(cmd))) + return -EFAULT; + /* execute command */ - do + do { memcpy(&mbox->cmd, &cmd, sizeof(cmd)); - if (cmd.length) - { - if(copy_from_user((void *) &mbox->data, u_data, cmd.length)) + + if (cmd.length){ + if( copy_from_user((void*)&mbox->data, u_data, cmd.length)) return -EFAULT; } + if (sdla_exec(mbox)) err = mbox->cmd.result; - else - return -EIO; - } - while (err && retry-- && fr_event(card, err, mbox)); - /* return result */ + else return -EIO; + + } while (err && retry-- && fr_event(card, err, mbox)); - if(copy_to_user(u_cmd, (void *) &mbox->cmd, sizeof(fr_cmd_t))) + /* return result */ + if (copy_to_user(u_cmd, (void*)&mbox->cmd, sizeof(fr_cmd_t))) return -EFAULT; + len = mbox->cmd.length; - if (len && u_data && copy_to_user(u_data, (void *) &mbox->data, len)) + + if (len && u_data && !copy_to_user(u_data, (void*)&mbox->data, len)) return -EFAULT; return 0; + } /****** Network Device Interface ********************************************/ + /*============================================================================ * Initialize Linux network interface. * @@ -627,35 +923,55 @@ * interface registration. Returning anything but zero will fail interface * registration. */ -static int if_init(struct device *dev) +static int if_init (struct device* dev) { - fr_channel_t *chan = dev->priv; - sdla_t *card = chan->card; - wan_device_t *wandev = &card->wandev; + fr_channel_t* chan = dev->priv; + sdla_t* card = chan->card; + wan_device_t* wandev = &card->wandev; /* Initialize device driver entry points */ - dev->open = &if_open; - dev->stop = &if_close; - dev->hard_header = &if_header; - dev->rebuild_header = &if_rebuild_hdr; - dev->hard_start_xmit = &if_send; - dev->get_stats = &if_stats; + dev->open = &if_open; + dev->stop = &if_close; + dev->hard_header = &if_header; + dev->rebuild_header = &if_rebuild_hdr; + dev->hard_start_xmit = &if_send; + dev->get_stats = &if_stats; + /* Initialize media-specific parameters */ - dev->type = ARPHRD_DLCI; /* ARP h/w type */ - dev->mtu = FR_CHANNEL_MTU; - dev->hard_header_len = FR_HEADER_LEN; /* media header length */ - dev->addr_len = 2; /* hardware address length */ - *(unsigned short *) dev->dev_addr = htons(chan->dlci); + dev->type = ARPHRD_DLCI; /* ARP h/w type */ + dev->flags |= IFF_POINTOPOINT; + + /* Enable Multicast addressing */ + if (chan->mc == WANOPT_YES){ + dev->flags |= IFF_MULTICAST; + } + + dev->mtu = wandev->mtu - FR_HEADER_LEN; + /* For an API, the maximum number of bytes that the stack will pass + to the driver is (dev->mtu + dev->hard_header_len). So, adjust the + mtu so that a frame of maximum size can be transmitted by the API. + */ + if(chan->usedby == API) { + dev->mtu += (sizeof(api_tx_hdr_t) - FR_HEADER_LEN); + } + + dev->hard_header_len = FR_HEADER_LEN;/* media header length */ + dev->addr_len = 2; /* hardware address length */ + *(unsigned short*)dev->dev_addr = htons(chan->dlci); + /* Initialize hardware parameters (just for reference) */ - dev->irq = wandev->irq; - dev->dma = wandev->dma; - dev->base_addr = wandev->ioport; - dev->mem_start = (unsigned long)wandev->maddr; - dev->mem_end = dev->mem_start + wandev->msize - 1; - /* Set transmit buffer queue length */ - dev->tx_queue_len = 10; + dev->irq = wandev->irq; + dev->dma = wandev->dma; + dev->base_addr = wandev->ioport; + dev->mem_start = wandev->maddr; + dev->mem_end = wandev->maddr + wandev->msize - 1; + + /* Set transmit buffer queue length */ + dev->tx_queue_len = 100; + /* Initialize socket buffers */ dev_init_buffers(dev); + set_chan_state(dev, WAN_DISCONNECTED); return 0; } @@ -667,127 +983,62 @@ * * Return 0 if O.k. or errno. */ - -static int if_open(struct device *dev) +static int if_open (struct device* dev) { - fr_channel_t *chan = dev->priv; - sdla_t *card = chan->card; - struct device *dev2; + fr_channel_t* chan = dev->priv; + sdla_t* card = chan->card; int err = 0; - fr508_flags_t *flags = card->flags; struct timeval tv; + if (dev->start) - return -EBUSY; /* only one open is allowed */ - if (test_and_set_bit(0, (void *) &card->wandev.critical)) + return -EBUSY; /* only one open is allowed */ + + if (test_and_set_bit(1, (void*)&card->wandev.critical)) return -EAGAIN; - if (!card->open_cnt) - { - Intr_test_counter = 0; - card->intr_mode = INTR_TEST_MODE; - err = intr_test(card); - if ((err) || (Intr_test_counter != (MAX_INTR_TEST_COUNTER + 1))) { - printk(KERN_INFO - "%s: Interrupt Test Failed, Counter: %i\n", - card->devname, Intr_test_counter); - err = -EIO; - card->wandev.critical = 0; - return err; - } - printk(KERN_INFO "%s: Interrupt Test Passed, Counter: %i\n" - ,card->devname, Intr_test_counter); - /* The following allocates and intializes a circular - * link list of interfaces per card. - */ - card->devs_struct = kmalloc(sizeof(load_sharing_t), GFP_KERNEL); - if (card->devs_struct == NULL) - return -ENOMEM; - card->dev_to_devtint_next = card->devs_struct; - for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) { - (card->devs_struct)->dev_ptr = dev2; - if (dev2->slave == NULL) - (card->devs_struct)->next = card->dev_to_devtint_next; - else { - (card->devs_struct)->next = kmalloc( - sizeof(load_sharing_t), GFP_KERNEL); - if ((card->devs_struct)->next == NULL) - return -ENOMEM; - card->devs_struct = (card->devs_struct)->next; - } - } - card->devs_struct = card->dev_to_devtint_next; - card->intr_mode = BUFFER_INTR_MODE; - /* - check all the interfaces for the device to see if CIR has - been enabled for any DLCI(s). If so then use the DLCI list - Interrupt mode for fr_set_intr_mode(), otherwise use the default global interrupt mode - */ - for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) { - if (((fr_channel_t *) dev2->priv)->cir_status - == CIR_ENABLED) { - card->intr_mode = DLCI_LIST_INTR_MODE; - break; - } - } - /* - If you enable comms and then set ints, you get a Tx int as you - perform the SET_INT_TRIGGERS command. So, we only set int - triggers and then adjust the interrupt mask (to disable Tx ints) before enabling comms. - */ - if (card->intr_mode == BUFFER_INTR_MODE) { - if (fr_set_intr_mode(card, 0x03, card->wandev.mtu)) { - err = -EIO; - card->wandev.critical = 0; - return err; - } - printk(KERN_INFO - "%s: Global Buffering Tx Interrupt Mode\n" - ,card->devname); - } else if (card->intr_mode == DLCI_LIST_INTR_MODE) { - if (fr_set_intr_mode(card, 0x83, card->wandev.mtu)) { - err = -EIO; - card->wandev.critical = 0; - return err; - } - printk(KERN_INFO - "%s: DLCI list Tx Interrupt Mode\n", - card->devname); - } - flags->imask &= ~0x02; - if (fr_comm_enable(card)) { - err = -EIO; - card->wandev.critical = 0; - return err; - } - wanpipe_set_state(card, WAN_CONNECTED); - if (card->wandev.station == WANOPT_CPE) { - /* CPE: issue full status enquiry */ - fr_issue_isf(card, FR_ISF_FSE); - } else { /* FR switch: activate DLCI(s) */ - /* For Switch emulation we have to ADD and ACTIVATE - * the DLCI(s) that were configured with the SET_DLCI_ - * CONFIGURATION command. Add and Activate will fail if - * DLCI specified is not included in the list. - * - * Also If_open is called once for each interface. But - * it does not get in here for all the interface. So - * we have to pass the entire list of DLCI(s) to add - * activate routines. - */ - fr_add_dlci(card, - card->u.f.node_dlci[0], card->u.f.dlci_num); - fr_activate_dlci(card, - card->u.f.node_dlci[0], card->u.f.dlci_num); + + + /* If signalling is set to NO, then setup + * DLCI addresses right away. Don't have to wait for + * link to connect. + */ + if (card->wandev.signalling == WANOPT_NO){ + printk(KERN_INFO "%s: Signalling set to NO: Mapping DLCI's\n", + card->wandev.name); + if (fr_init_dlci(card,chan)){ + return -EAGAIN; } } - dev->mtu = min(dev->mtu, card->wandev.mtu - FR_HEADER_LEN); + + if (card->wandev.station == WANOPT_CPE) { + + /* CPE: issue full status enquiry */ + fr_issue_isf(card, FR_ISF_FSE); + + } else { /* FR switch: activate DLCI(s) */ + + /* For Switch emulation we have to ADD and ACTIVATE + * the DLCI(s) that were configured with the SET_DLCI_ + * CONFIGURATION command. Add and Activate will fail if + * DLCI specified is not included in the list. + * + * Also If_open is called once for each interface. But + * it does not get in here for all the interface. So + * we have to pass the entire list of DLCI(s) to add + * activate routines. + */ + + fr_add_dlci(card, chan->dlci); + fr_activate_dlci(card, chan->dlci); + } + dev->interrupt = 0; dev->tbusy = 0; dev->start = 1; wanpipe_open(card); update_chan_state(dev); - do_gettimeofday(&tv); + do_gettimeofday( &tv ); chan->router_start_time = tv.tv_sec; - card->wandev.critical = 0; + clear_bit(1, (void*)&card->wandev.critical); return err; } @@ -796,22 +1047,21 @@ * o if this is the last open, then disable communications and interrupts. * o reset flags. */ - -static int if_close(struct device *dev) +static int if_close (struct device* dev) { - fr_channel_t *chan = dev->priv; - sdla_t *card = chan->card; - if (test_and_set_bit(0, (void *) &card->wandev.critical)) + fr_channel_t* chan = dev->priv; + sdla_t* card = chan->card; + + if (test_and_set_bit(1, (void*)&card->wandev.critical)) return -EAGAIN; + dev->start = 0; wanpipe_close(card); - if (!card->open_cnt) - { - wanpipe_set_state(card, WAN_DISCONNECTED); - fr_set_intr_mode(card, 0, 0); - fr_comm_disable(card); + if (card->wandev.station == WANOPT_NODE) { + fr_delete_dlci (card,chan->dlci); } - card->wandev.critical = 0; + + clear_bit(1, (void*)&card->wandev.critical); return 0; } @@ -825,15 +1075,15 @@ * * Return: media header length. */ - -static int if_header(struct sk_buff *skb, struct device *dev, - unsigned short type, void *daddr, void *saddr, unsigned len) +static int if_header (struct sk_buff* skb, struct device* dev, + unsigned short type, void* daddr, void* saddr, unsigned len) { int hdr_len = 0; + skb->protocol = type; hdr_len = wanrouter_encapsulate(skb, dev); - if (hdr_len < 0) - { + + if (hdr_len < 0) { hdr_len = 0; skb->protocol = 0; } @@ -849,14 +1099,14 @@ * Return: 1 physical address resolved. * 0 physical address not resolved */ - -static int if_rebuild_hdr(struct sk_buff *skb) +static int if_rebuild_hdr (struct sk_buff* skb) { - struct device *dev=skb->dev; - fr_channel_t *chan = dev->priv; - sdla_t *card = chan->card; + struct device *dev = skb->dev; + fr_channel_t* chan = dev->priv; + sdla_t* card = chan->card; + printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n", - card->devname, dev->name); + card->devname, dev->name); return 1; } @@ -864,6 +1114,7 @@ * Send a packet on a network interface. * o set tbusy flag (marks start of the transmission) to block a timer-based * transmit from overlapping. + * o set critical flag when accessing board. * o check link state. If link is not up, then drop the packet. * o check channel status. If it's down then initiate a call. * o pass a packet to corresponding WAN device. @@ -878,876 +1129,951 @@ * 2. Setting tbusy flag will inhibit further transmit requests from the * protocol stack and can be used for flow control with protocol layer. */ - -static int if_send(struct sk_buff *skb, struct device *dev) +static int if_send (struct sk_buff* skb, struct device* dev) { - fr_channel_t *chan = dev->priv; - sdla_t *card = chan->card; - int retry = 0, err; - unsigned char *sendpacket; - struct device *dev2; - unsigned long check_braddr, check_mcaddr; - fr508_flags_t *adptr_flags = card->flags; + fr_channel_t* chan = dev->priv; + sdla_t* card = chan->card; + int err; + unsigned char *sendpacket; + fr508_flags_t* adptr_flags = card->flags; int udp_type, send_data; - fr_dlci_interface_t *dlci_interface = chan->dlci_int_interface; - unsigned long host_cpu_flags; - ++chan->if_send_entry; + unsigned long smp_flags=0; + void* data; + unsigned len; + + chan->drvstats_if_send.if_send_entry++; + + if (skb == NULL) { + /* if we get here, some higher layer thinks we've missed an + * tx-done interrupt. + */ + printk(KERN_INFO "%s: interface %s got kicked!\n", + card->devname, dev->name); + chan->drvstats_if_send.if_send_skb_null ++; + mark_bh(NET_BH); + return 0; + } + + /* We must set the 'tbusy' flag if we already have a packet queued for + transmission in the transmit interrupt handler. However, we must + ensure that the transmit interrupt does not reset the 'tbusy' flag + just before we set it, as this will result in a "transmit timeout". + */ + set_bit(2, (void*)&card->wandev.critical); + if(chan->transmit_length) { + dev->tbusy = 1; + chan->tick_counter = jiffies; + clear_bit(2, (void*)&card->wandev.critical); + return 1; + } + clear_bit(2, (void*)&card->wandev.critical); + + if (dev->tbusy) { - if (dev->tbusy) - { /* If our device stays busy for at least 5 seconds then we will - * kick start the device by making dev->tbusy = 0. We expect - * that our device never stays busy more than 5 seconds. So this * is only used as a last resort. - */ - ++chan->if_send_busy; + * kick start the device by making dev->tbusy = 0. We expect + * that our device never stays busy more than 5 seconds. So this + * is only used as a last resort. + */ + + chan->drvstats_if_send.if_send_tbusy++; ++chan->ifstats.collisions; - if ((jiffies - chan->tick_counter) < (5 * HZ)) + + if ((jiffies - chan->tick_counter) < (5 * HZ)) { return 1; + } printk(KERN_INFO "%s: Transmit timed out\n", chan->name); - ++chan->if_send_busy_timeout; - /* unbusy all the interfaces on the card */ - for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) - dev2->tbusy = 0; - } + chan->drvstats_if_send.if_send_tbusy_timeout ++; + dev->tbusy = 0; + } + + data = skb->data; sendpacket = skb->data; + len = skb->len; + udp_type = udp_pkt_type(skb, card); - if (udp_type == UDP_DRVSTATS_TYPE) - { - ++chan->if_send_DRVSTATS_request; - process_udp_driver_call(UDP_PKT_FRM_STACK, card, skb, dev, 0, - chan); - dev_kfree_skb(skb); - return 0; - } - else if (udp_type == UDP_FPIPE_TYPE) - ++chan->if_send_FPIPE_request; - /* retreive source address in two forms: broadcast & multicast */ - check_braddr = sendpacket[17]; - check_mcaddr = sendpacket[14]; - check_braddr = check_braddr << 8; - check_mcaddr = check_mcaddr << 8; - check_braddr |= sendpacket[16]; - check_mcaddr |= sendpacket[15]; - check_braddr = check_braddr << 8; - check_mcaddr = check_mcaddr << 8; - check_braddr |= sendpacket[15]; - check_mcaddr |= sendpacket[16]; - check_braddr = check_braddr << 8; - check_mcaddr = check_mcaddr << 8; - check_braddr |= sendpacket[14]; - check_mcaddr |= sendpacket[17]; - /* if the Source Address is a Multicast address */ - if ((chan->mc == WANOPT_NO) && (check_mcaddr >= 0xE0000001) && - (check_mcaddr <= 0xFFFFFFFE)) - { - printk(KERN_INFO "%s: Multicast Src. Addr. silently discarded\n" - ,card->devname); - dev_kfree_skb(skb); - ++chan->ifstats.tx_dropped; - ++chan->if_send_multicast; + + if(udp_type != UDP_INVALID_TYPE) { + if(store_udp_mgmt_pkt(udp_type, UDP_PKT_FRM_STACK, card, skb, + chan->dlci)) { + adptr_flags->imask |= FR_INTR_TIMER; + if (udp_type == UDP_FPIPE_TYPE){ + chan->drvstats_if_send. + if_send_PIPE_request ++; + } + } return 0; } - disable_irq(card->hw.irq); - ++card->irq_dis_if_send_count; - if (test_and_set_bit(0, (void *) &card->wandev.critical)) - { - if (card->wandev.critical == CRITICAL_IN_ISR) - { - ++chan->if_send_critical_ISR; - if (card->intr_mode == DLCI_LIST_INTR_MODE) - { - /* The enable_tx_int flag is set here so that if - * the critical flag is set due to an interrupt - * then we want to enable transmit interrupts - * again. - */ - card->wandev.enable_tx_int = 1; - /* Setting this flag to WAITING_TO_BE_ENABLED - * specifies that interrupt bit has to be - * enabled for that particular interface. - * (delayed interrupt) - */ - chan->tx_int_status = WAITING_TO_BE_ENABLED; - /* This is used for enabling dynamic calculation - * of CIRs relative to the packet length. - */ - chan->pkt_length = skb->len; - dev->tbusy = 1; - chan->tick_counter = jiffies; - } - else - { - card->wandev.enable_tx_int = 1; - dev->tbusy = 1; - chan->tick_counter = jiffies; - } - save_flags(host_cpu_flags); - cli(); - if ((!(--card->irq_dis_if_send_count)) && - (!card->irq_dis_poll_count)) - enable_irq(card->hw.irq); - restore_flags(host_cpu_flags); - return 1; - } - ++chan->if_send_critical_non_ISR; - ++chan->ifstats.tx_dropped; + + if((chan->usedby == API) && (len <= sizeof(api_tx_hdr_t))) { + //FIXME: increment some error statistic + dev_kfree_skb(skb); + return 0; + } + + //FIXME: can we do better than sendpacket[2]? + if ((chan->usedby == WANPIPE) && (sendpacket[2] == 0x45)) { + /* check to see if the source IP address is a broadcast or */ + /* multicast IP address */ + if(chk_bcast_mcast_addr(card, dev, skb)) + return 0; + } + + /* Lock the 508 card: SMP Supported */ + s508_s514_lock(card,&smp_flags); + + if (test_and_set_bit(0, (void*)&card->wandev.critical)) { + chan->drvstats_if_send.if_send_critical_non_ISR ++; + chan->ifstats.tx_dropped ++; + printk(KERN_INFO "%s Critical in IF_SEND %02X\n", + card->devname, card->wandev.critical); dev_kfree_skb(skb); - save_flags(host_cpu_flags); - cli(); - if ((!(--card->irq_dis_if_send_count)) && - (!card->irq_dis_poll_count)) - enable_irq(card->hw.irq); - restore_flags(host_cpu_flags); + /* Unlock the 508 card */ + s508_s514_unlock(card,&smp_flags); return 0; } - card->wandev.critical = 0x21; - if (udp_type == UDP_FPIPE_TYPE) - { - err = process_udp_mgmt_pkt(UDP_PKT_FRM_STACK, card, skb, - dev, 0, chan); - } - else if (card->wandev.state != WAN_CONNECTED) - { - ++chan->if_send_wan_disconnected; + + if (card->wandev.state != WAN_CONNECTED) { + chan->drvstats_if_send.if_send_wan_disconnected ++; ++chan->ifstats.tx_dropped; - ++card->wandev.stats.tx_dropped; - } - else if (chan->state != WAN_CONNECTED) - { - ++chan->if_send_dlci_disconnected; + ++card->wandev.stats.tx_dropped; + + } else if (chan->state != WAN_CONNECTED) { + chan->drvstats_if_send.if_send_dlci_disconnected ++; + /* Critical area on 514, since disabl_irq is not used + * thus, interrupt would execute a command at + * the same time as if_send. + */ + set_bit(1, (void*)&card->wandev.critical); update_chan_state(dev); - ++chan->ifstats.tx_dropped; - ++card->wandev.stats.tx_dropped; - } - else if (!is_tx_ready(card, chan)) - { - if (card->intr_mode == DLCI_LIST_INTR_MODE) - { - dlci_interface->gen_interrupt |= 0x40; - dlci_interface->packet_length = skb->len; - } - dev->tbusy = 1; - chan->tick_counter = jiffies; - adptr_flags->imask |= 0x02; - ++chan->if_send_no_bfrs; - retry = 1; - } - else - { + clear_bit(1, (void*)&card->wandev.critical); + ++chan->ifstats.tx_dropped; + ++card->wandev.stats.tx_dropped; + + } else if (!is_tx_ready(card, chan)) { + setup_for_delayed_transmit(dev, data, len); + chan->drvstats_if_send.if_send_no_bfrs++; + } else { send_data = 1; - /* If it's an IPX packet */ - if (sendpacket[1] == 0x00 && - sendpacket[2] == 0x80 && - sendpacket[6] == 0x81 && - sendpacket[7] == 0x37) - { - if (card->wandev.enable_IPX) - { - switch_net_numbers(sendpacket, - card->wandev.network_number, 0); - } - else - { - /* increment some statistic here! */ + //FIXME: IPX is not implemented in this version of Frame Relay ? + if((chan->usedby == WANPIPE) && + sendpacket[1] == 0x00 && + sendpacket[2] == 0x80 && + sendpacket[6] == 0x81 && + sendpacket[7] == 0x37) { + + if( chan->enable_IPX ) { + switch_net_numbers(sendpacket, + chan->network_number, 0); + } else { + //FIXME: Take this out when IPX is fixed + printk(KERN_INFO + "%s: WARNING: Unsupported IPX data in send, packet dropped\n", + card->devname); send_data = 0; } } - if (send_data) - { - err = (card->hw.fwid == SFID_FR508) ? - fr508_send(card, chan->dlci, 0, skb->len, skb->data) : - fr502_send(card, chan->dlci, 0, skb->len, skb->data); - if (err) - { - if (card->intr_mode == DLCI_LIST_INTR_MODE) - { - dlci_interface->gen_interrupt |= 0x40; - dlci_interface->packet_length = skb->len; + + if (send_data) { + unsigned char attr = 0; + + /* For an API transmission, get rid of the API header */ + if (chan->usedby == API) { + api_tx_hdr_t* api_tx_hdr; + api_tx_hdr = (api_tx_hdr_t*)&skb->data[0x00]; + attr = api_tx_hdr->attr; + data += sizeof(api_tx_hdr_t); + len -= sizeof(api_tx_hdr_t); + } + + err = fr_send(card, chan->dlci, attr, len, data); + if (err) { + switch(err) { + case FRRES_CIR_OVERFLOW: + case FRRES_BUFFER_OVERFLOW: + setup_for_delayed_transmit(dev, data, + len); + chan->drvstats_if_send. + if_send_adptr_bfrs_full ++; + break; + default: + chan->drvstats_if_send. + if_send_dlci_disconnected ++; + ++chan->ifstats.tx_dropped; + ++card->wandev.stats.tx_dropped; + break; } - dev->tbusy = 1; - chan->tick_counter = jiffies; - adptr_flags->imask |= 0x02; - retry = 1; - ++chan->if_send_adptr_bfrs_full; - ++chan->ifstats.tx_errors; - ++card->wandev.stats.tx_errors; - } - else - { - ++chan->if_send_bfrs_passed_to_adptr; + } else { + chan->drvstats_if_send. + if_send_bfr_passed_to_adptr++; ++chan->ifstats.tx_packets; ++card->wandev.stats.tx_packets; - chan->ifstats.tx_bytes += skb->len; - card->wandev.stats.tx_bytes += skb->len; + chan->ifstats.tx_bytes += len; + card->wandev.stats.tx_bytes += len; } } } - if (!retry) + + if (!dev->tbusy) { dev_kfree_skb(skb); + } - card->wandev.critical = 0; - save_flags(host_cpu_flags); - cli(); - if ((!(--card->irq_dis_if_send_count)) && (!card->irq_dis_poll_count)) - enable_irq(card->hw.irq); - restore_flags(host_cpu_flags); - return retry; + clear_bit(0, (void*)&card->wandev.critical); + + s508_s514_unlock(card,&smp_flags); + + return (dev->tbusy); +} + + + +/*============================================================================ + * Setup so that a frame can be transmitted on the occurence of a transmit + * interrupt. + */ +static void setup_for_delayed_transmit (struct device* dev, void* buf, + unsigned len) +{ + fr_channel_t* chan = dev->priv; + sdla_t* card = chan->card; + fr508_flags_t* adptr_flags = card->flags; + fr_dlci_interface_t* dlci_interface = chan->dlci_int_interface; + + if(chan->transmit_length) { + printk(KERN_INFO "%s: Big mess in setup_for_del...\n", + card->devname); + return; + } + + if(len > FR_MAX_NO_DATA_BYTES_IN_FRAME) { + //FIXME: increment some statistic */ + return; + } + + chan->transmit_length = len; + memcpy(chan->transmit_buffer, buf, len); + + dlci_interface->gen_interrupt |= FR_INTR_TXRDY; + dlci_interface->packet_length = len; + adptr_flags->imask |= FR_INTR_TXRDY; + + card->u.f.tx_interrupts_pending ++; +} + + +/*============================================================================ + * Check to see if the packet to be transmitted contains a broadcast or + * multicast source IP address. + * Return 0 if not broadcast/multicast address, otherwise return 1. + */ + +static int chk_bcast_mcast_addr(sdla_t *card, struct device* dev, + struct sk_buff *skb) +{ + u32 src_ip_addr; + u32 broadcast_ip_addr = 0; + struct in_device *in_dev; + fr_channel_t* chan = dev->priv; + + /* read the IP source address from the outgoing packet */ + src_ip_addr = *(u32 *)(skb->data + 14); + + /* read the IP broadcast address for the device */ + in_dev = dev->ip_ptr; + if(in_dev != NULL) { + struct in_ifaddr *ifa= in_dev->ifa_list; + if(ifa != NULL) + broadcast_ip_addr = ifa->ifa_broadcast; + else + return 0; + } + + /* check if the IP Source Address is a Broadcast address */ + if((dev->flags & IFF_BROADCAST) && (src_ip_addr == broadcast_ip_addr)) { printk(KERN_INFO + "%s: Broadcast Source Address silently discarded\n", + card->devname); + dev_kfree_skb(skb); + ++ chan->ifstats.tx_dropped; + return 1; + } + + /* check if the IP Source Address is a Multicast address */ + if((chan->mc == WANOPT_NO) && (ntohl(src_ip_addr) >= 0xE0000001) && + (ntohl(src_ip_addr) <= 0xFFFFFFFE)) { + printk(KERN_INFO + "%s: Multicast Source Address silently discarded\n", + card->devname); + dev_kfree_skb(skb); + ++ chan->ifstats.tx_dropped; + return 1; + } + + return 0; } /*============================================================================ * Reply to UDP Management system. * Return nothing. */ - -static int reply_udp(unsigned char *data, unsigned int mbox_len) +static int reply_udp( unsigned char *data, unsigned int mbox_len ) { - unsigned short len, udp_length, temp, i, ip_length; - unsigned long sum; + unsigned short len, udp_length, temp, ip_length; + unsigned long ip_temp; + int even_bound = 0; + + + fr_udp_pkt_t *fr_udp_pkt = (fr_udp_pkt_t *)data; + /* Set length of packet */ - len = mbox_len + 62; + len = sizeof(fr_encap_hdr_t)+ + sizeof(ip_pkt_t)+ + sizeof(udp_pkt_t)+ + sizeof(wp_mgmt_t)+ + sizeof(cblock_t)+ + mbox_len; + + /* fill in UDP reply */ - data[38] = 0x02; + fr_udp_pkt->wp_mgmt.request_reply = UDPMGMT_REPLY; + /* fill in UDP length */ - udp_length = mbox_len + 40; + udp_length = sizeof(udp_pkt_t)+ + sizeof(wp_mgmt_t)+ + sizeof(cblock_t)+ + mbox_len; + + /* put it on an even boundary */ - if (udp_length & 0x0001) - { + if ( udp_length & 0x0001 ) { udp_length += 1; len += 1; + even_bound = 1; } - temp = (udp_length << 8) | (udp_length >> 8); - memcpy(&data[26], &temp, 2); + + temp = (udp_length<<8)|(udp_length>>8); + fr_udp_pkt->udp_pkt.udp_length = temp; + /* swap UDP ports */ - memcpy(&temp, &data[22], 2); - memcpy(&data[22], &data[24], 2); - memcpy(&data[24], &temp, 2); + temp = fr_udp_pkt->udp_pkt.udp_src_port; + fr_udp_pkt->udp_pkt.udp_src_port = + fr_udp_pkt->udp_pkt.udp_dst_port; + fr_udp_pkt->udp_pkt.udp_dst_port = temp; + + + /* add UDP pseudo header */ temp = 0x1100; - memcpy(&data[udp_length + 22], &temp, 2); - temp = (udp_length << 8) | (udp_length >> 8); - memcpy(&data[udp_length + 24], &temp, 2); + *((unsigned short *) + (fr_udp_pkt->data+mbox_len+even_bound)) = temp; + temp = (udp_length<<8)|(udp_length>>8); + *((unsigned short *) + (fr_udp_pkt->data+mbox_len+even_bound+2)) = temp; + /* calculate UDP checksum */ - data[28] = data[29] = 0; - sum = 0; - for (i = 0; i < udp_length + 12; i += 2) - { - memcpy(&temp, &data[14 + i], 2); - sum += (unsigned long) temp; - } - while (sum >> 16) - sum = (sum & 0xffffUL) + (sum >> 16); + fr_udp_pkt->udp_pkt.udp_checksum = 0; + + fr_udp_pkt->udp_pkt.udp_checksum = + calc_checksum(&data[UDP_OFFSET+sizeof(fr_encap_hdr_t)], + udp_length+UDP_OFFSET); - temp = (unsigned short) sum; - temp = ~temp; - if (temp == 0) - temp = 0xffff; - memcpy(&data[28], &temp, 2); /* fill in IP length */ - ip_length = udp_length + 20; - temp = (ip_length << 8) | (ip_length >> 8); - memcpy(&data[4], &temp, 2); + ip_length = udp_length + sizeof(ip_pkt_t); + temp = (ip_length<<8)|(ip_length>>8); + fr_udp_pkt->ip_pkt.total_length = temp; + /* swap IP addresses */ - memcpy(&temp, &data[14], 2); - memcpy(&data[14], &data[18], 2); - memcpy(&data[18], &temp, 2); - memcpy(&temp, &data[16], 2); - memcpy(&data[16], &data[20], 2); - memcpy(&data[20], &temp, 2); + ip_temp = fr_udp_pkt->ip_pkt.ip_src_address; + fr_udp_pkt->ip_pkt.ip_src_address = + fr_udp_pkt->ip_pkt.ip_dst_address; + fr_udp_pkt->ip_pkt.ip_dst_address = ip_temp; + + /* fill in IP checksum */ - data[12] = data[13] = 0; - sum = 0; - for (i = 0; i < 20; i += 2) - { - memcpy(&temp, &data[2 + i], 2); - sum += (unsigned long) temp; + fr_udp_pkt->ip_pkt.hdr_checksum = 0; + fr_udp_pkt->ip_pkt.hdr_checksum = + calc_checksum(&data[sizeof(fr_encap_hdr_t)], + sizeof(ip_pkt_t)); + + return len; +} /* reply_udp */ + +unsigned short calc_checksum (char *data, int len) +{ + unsigned short temp; + unsigned long sum=0; + int i; + + for( i = 0; i > 16) + + while (sum >> 16 ) { sum = (sum & 0xffffUL) + (sum >> 16); - temp = (unsigned short) sum; + } + + temp = (unsigned short)sum; temp = ~temp; - if (temp == 0) + + if( temp == 0 ) temp = 0xffff; - memcpy(&data[12], &temp, 2); - return len; -} /* reply_udp */ + + return temp; +} + /* If incoming is 0 (outgoing)- if the net numbers is ours make it 0 if incoming is 1 - if the net number is 0 make it ours - */ +*/ static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming) { unsigned long pnetwork_number; - pnetwork_number = (unsigned long) ((sendpacket[14] << 24) + - (sendpacket[15] << 16) + (sendpacket[16] << 8) + - sendpacket[17]); + + pnetwork_number = (unsigned long)((sendpacket[14] << 24) + + (sendpacket[15] << 16) + (sendpacket[16] << 8) + + sendpacket[17]); + if (!incoming) { /* If the destination network number is ours, make it 0 */ - if (pnetwork_number == network_number) { - sendpacket[14] = sendpacket[15] = sendpacket[16] = - sendpacket[17] = 0x00; + if( pnetwork_number == network_number) { + sendpacket[14] = sendpacket[15] = sendpacket[16] = + sendpacket[17] = 0x00; } } else { /* If the incoming network is 0, make it ours */ - if (pnetwork_number == 0) - { - sendpacket[14] = (unsigned char) (network_number >> 24); - sendpacket[15] = (unsigned char) ((network_number & - 0x00FF0000) >> 16); - sendpacket[16] = (unsigned char) ((network_number & - 0x0000FF00) >> 8); - sendpacket[17] = (unsigned char) (network_number & - 0x000000FF); + if( pnetwork_number == 0) { + sendpacket[14] = (unsigned char)(network_number >> 24); + sendpacket[15] = (unsigned char)((network_number & + 0x00FF0000) >> 16); + sendpacket[16] = (unsigned char)((network_number & + 0x0000FF00) >> 8); + sendpacket[17] = (unsigned char)(network_number & + 0x000000FF); } } - pnetwork_number = (unsigned long) ((sendpacket[26] << 24) + - (sendpacket[27] << 16) + (sendpacket[28] << 8) + - sendpacket[29]); - if (!incoming) { + + + pnetwork_number = (unsigned long)((sendpacket[26] << 24) + + (sendpacket[27] << 16) + (sendpacket[28] << 8) + + sendpacket[29]); + + if( !incoming ) { /* If the source network is ours, make it 0 */ - if (pnetwork_number == network_number) - { - sendpacket[26] = sendpacket[27] = sendpacket[28] = - sendpacket[29] = 0x00; + if( pnetwork_number == network_number) { + sendpacket[26] = sendpacket[27] = sendpacket[28] = + sendpacket[29] = 0x00; } } else { /* If the source network is 0, make it ours */ - if (pnetwork_number == 0) { - sendpacket[26] = (unsigned char) (network_number >> 24); - sendpacket[27] = (unsigned char) ((network_number & - 0x00FF0000) >> 16); - sendpacket[28] = (unsigned char) ((network_number & - 0x0000FF00) >> 8); - sendpacket[29] = (unsigned char) (network_number & - 0x000000FF); + if( pnetwork_number == 0 ) { + sendpacket[26] = (unsigned char)(network_number >> 24); + sendpacket[27] = (unsigned char)((network_number & + 0x00FF0000) >> 16); + sendpacket[28] = (unsigned char)((network_number & + 0x0000FF00) >> 8); + sendpacket[29] = (unsigned char)(network_number & + 0x000000FF); } } -} /* switch_net_numbers */ +} /* switch_net_numbers */ /*============================================================================ - * Get Ethernet-style interface statistics. - * Return a pointer to struct net_device_stats. + * Get ethernet-style interface statistics. + * Return a pointer to struct enet_statistics. */ - -static struct net_device_stats *if_stats(struct device *dev) +static struct enet_statistics* if_stats (struct device* dev) { - fr_channel_t *chan = dev->priv; - if(chan==NULL) + fr_channel_t* chan = dev->priv; + + if(chan == NULL) return NULL; - + return &chan->ifstats; } /****** Interrupt Handlers **************************************************/ -/*============================================================================ - * S502 frame relay interrupt service routine. - */ -static void fr502_isr(sdla_t * card) -{ - fr502_flags_t *flags = card->flags; - switch (flags->iflag) - { - case 0x01: /* receive interrupt */ - fr502_rx_intr(card); - break; - case 0x02: /* transmit interrupt */ - flags->imask &= ~0x02; - tx_intr(card); - break; - default: - spur_intr(card); - } - flags->iflag = 0; -} /*============================================================================ * S508 frame relay interrupt service routine. */ - -static void fr508_isr(sdla_t * card) +static void fr_isr (sdla_t* card) { - fr508_flags_t *flags = card->flags; - fr_buf_ctl_t *bctl; + fr508_flags_t* flags = card->flags; char *ptr = &flags->iflag; - struct device *dev = card->wandev.dev; - struct device *dev2; - int i; - unsigned long host_cpu_flags; - unsigned disable_tx_intr = 1; - fr_channel_t *chan; - fr_dlci_interface_t *dlci_interface; + int i,err; + fr_mbox_t* mbox = card->mbox; + /* This flag prevents nesting of interrupts. See sdla_isr() routine - * in sdlamain.c. + * in sdlamain.c. */ card->in_isr = 1; + ++card->statistics.isr_entry; - if (test_and_set_bit(0, (void *) &card->wandev.critical)) - { - printk(KERN_INFO "fr508_isr: %s, wandev.critical set to 0x%02X, int type = 0x%02X\n", card->devname, card->wandev.critical, flags->iflag); - ++card->statistics.isr_already_critical; + + if(test_bit(1, (void*)&card->wandev.critical)) { + card->wandev.critical = 0; + flags->iflag = 0; card->in_isr = 0; return; } - /* For all interrupts set the critical flag to CRITICAL_RX_INTR. - * If the if_send routine is called with this flag set it will set - * the enable transmit flag to 1. (for a delayed interrupt) - */ - card->wandev.critical = CRITICAL_IN_ISR; - card->dlci_int_mode_unbusy = 0; - card->buff_int_mode_unbusy = 0; - switch (flags->iflag) - { - case 0x01: /* receive interrupt */ - ++card->statistics.isr_rx; - fr508_rx_intr(card); - break; - case 0x02: /* transmit interrupt */ - ++card->statistics.isr_tx; - bctl = (void *) (flags->tse_offs - FR_MB_VECTOR + - card->hw.dpmbase); - bctl->flag = 0xA0; - if (card->intr_mode == DLCI_LIST_INTR_MODE) - { - /* Find the structure and make it unbusy */ - dev = find_channel(card, flags->dlci); - dev->tbusy = 0; - /* This is used to perform devtint at the - * end of the isr - */ - card->dlci_int_mode_unbusy = 1; - /* check to see if any other interfaces are - * busy. If so then do not disable the tx - * interrupts - */ - for (dev2 = card->wandev.dev; dev2; - dev2 = dev2->slave) - { - if (dev2->tbusy == 1) - { - disable_tx_intr = 0; - break; - } - } - if (disable_tx_intr) - flags->imask &= ~0x02; - } - else if (card->intr_mode == BUFFER_INTR_MODE) - { - for (dev2 = card->wandev.dev; dev2; - dev2 = dev2->slave) - { - if (!dev2 || !dev2->start) - { - ++card->statistics.tx_intr_dev_not_started; - continue; - } - if (dev2->tbusy) - { - card->buff_int_mode_unbusy = 1; - ((fr_channel_t *) dev2->priv)->dev_pending_devtint = 1; - dev2->tbusy = 0; - } - else - ((fr_channel_t *) dev2->priv)->dev_pending_devtint = 0; - } - flags->imask &= ~0x02; - } - break; - case 0x08: - Intr_test_counter++; + + if(card->hw.type != SDLA_S514) { + if (test_and_set_bit(0, (void*)&card->wandev.critical)) { + printk(KERN_INFO "%s: Critical while in ISR (0x%02X)\n", + card->devname, card->wandev.critical); + ++card->statistics.isr_already_critical; + card->in_isr = 0; + return; + } + } + + switch (flags->iflag) { + + case FR_INTR_RXRDY: /* receive interrupt */ + ++card->statistics.isr_rx; + rx_intr(card); + break; + + + case FR_INTR_TXRDY: /* transmit interrupt */ + ++ card->statistics.isr_tx; + tx_intr(card); + break; + + case FR_INTR_READY: + Intr_test_counter++; ++card->statistics.isr_intr_test; + break; + + case FR_INTR_DLC: /* Event interrupt occured */ + mbox->cmd.command = FR_READ_STATUS; + mbox->cmd.length = 0; + err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; + if (err) + fr_event(card, err, mbox); + break; + + case FR_INTR_TIMER: /* Timer interrupt */ + timer_intr(card); break; + default: - ++card->statistics.isr_spurious; - spur_intr(card); - printk(KERN_INFO "%s: Interrupt Type 0x%02X!\n", - card->devname, flags->iflag); - printk(KERN_INFO "%s: ID Bytes = ", card->devname); - for (i = 0; i < 8; i++) + ++card->statistics.isr_spurious; + spur_intr(card); + printk(KERN_INFO "%s: Interrupt Type 0x%02X!\n", + card->devname, flags->iflag); + + printk(KERN_INFO "%s: ID Bytes = ",card->devname); + for(i = 0; i < 8; i ++) printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i)); - printk(KERN_INFO "\n"); + printk(KERN_INFO "\n"); + break; - } - card->wandev.critical = CRITICAL_INTR_HANDLED; - if (card->wandev.enable_tx_int) - { - if (card->intr_mode == DLCI_LIST_INTR_MODE) - { - for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) - { - chan = dev2->priv; - if (chan->tx_int_status == WAITING_TO_BE_ENABLED) - { - dlci_interface = chan->dlci_int_interface; - dlci_interface->gen_interrupt |= 0x40; - dlci_interface->packet_length = chan->pkt_length; - chan->tx_int_status = DISABLED; - } - } - } - card->wandev.enable_tx_int = 0; - flags->imask |= 0x02; - ++card->statistics.isr_enable_tx_int; - } - save_flags(host_cpu_flags); - cli(); + } + card->in_isr = 0; - card->wandev.critical = 0xD1; flags->iflag = 0; - card->wandev.critical = 0; - restore_flags(host_cpu_flags); - /* Device is now ready to send. The instant this is executed the If_Send - routine is called. That is why this is put at the bottom of the ISR - to prevent a endless loop condition caused by repeated Interrupts and - enable_tx_int flag. - */ - if (card->dlci_int_mode_unbusy) - mark_bh(NET_BH); - if (card->buff_int_mode_unbusy) - { - for (;;) - { - if (((fr_channel_t *) ((card->devs_struct)->dev_ptr)->priv)->dev_pending_devtint == 1) - { - ((fr_channel_t *) ((card->devs_struct)->dev_ptr)->priv)->dev_pending_devtint = 0; - mark_bh(NET_BH); - } - if ((card->devs_struct)->next == card->dev_to_devtint_next) - break; - card->devs_struct = (card->devs_struct)->next; - } - card->devs_struct = (card->dev_to_devtint_next)->next; - card->dev_to_devtint_next = card->devs_struct; - } + if(card->hw.type != SDLA_S514) + clear_bit(0, (void*)&card->wandev.critical); } + + + /*============================================================================ * Receive interrupt handler. - */ + * When a receive interrupt occurs do the following: + * 1- Find the structure for the dlci that the interrupt occured on + * 2- If it doesn't exist then print appropriate msg and goto step 8. + * 3- If it exist then copy data to a skb. + * 4- If skb contains Sangoma UDP data then process them + * 5- If skb contains IPXWAN data then send IPXWAN reply packets + * 6- If skb contains Inverse Arp data then send Inv Arp replies + * 7- If skb contains any other data then decapsulate the packet and + * send it to the stack. + * 8- Release the receive element and update receive pointers on the board + */ +static void rx_intr (sdla_t* card) +{ + fr_rx_buf_ctl_t* frbuf = card->rxmb; + fr508_flags_t* flags = card->flags; + fr_channel_t* chan; + char *ptr = &flags->iflag; + struct sk_buff* skb; + struct device* dev; + void* buf; + unsigned dlci, len, offs, len_incl_hdr; + int i, udp_type; + + if (frbuf->flag != 0x01) { + + printk(KERN_INFO + "%s: corrupted Rx buffer @ 0x%X, flag = 0x%02X!\n", + card->devname, (unsigned)frbuf, frbuf->flag); + + printk(KERN_INFO "%s: ID Bytes = ",card->devname); + for(i = 0; i < 8; i ++) + printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i)); + printk(KERN_INFO "\n"); + + ++card->statistics.rx_intr_corrupt_rx_bfr; + return; + } + + len = frbuf->length; + dlci = frbuf->dlci; + offs = frbuf->offset; -static void fr502_rx_intr(sdla_t * card) -{ - fr_mbox_t *mbox = card->rxmb; - struct sk_buff *skb; - struct device *dev; - fr_channel_t *chan; - unsigned dlci, len; - void *buf; - unsigned char *sendpacket; - unsigned char buf2[3]; - int udp_type; - sdla_mapmem(&card->hw, FR502_RX_VECTOR); - dlci = mbox->cmd.dlci; - len = mbox->cmd.length; /* Find network interface for this packet */ dev = find_channel(card, dlci); - if (dev == NULL) - { - /* Invalid channel, discard packet */ - printk(KERN_INFO "%s: receiving on orphaned DLCI %d!\n", - card->devname, dlci); - sdla_mapmem(&card->hw, FR_MB_VECTOR); - } - chan = dev->priv; - if (!dev->start) - { - ++chan->ifstats.rx_dropped; - sdla_mapmem(&card->hw, FR_MB_VECTOR); - } - /* Allocate socket buffer */ - skb = dev_alloc_skb(len); - if (skb == NULL) - { - printk(KERN_INFO "%s: no socket buffers available!\n", - card->devname); - ++chan->ifstats.rx_dropped; - sdla_mapmem(&card->hw, FR_MB_VECTOR); - } - /* Copy data to the socket buffer */ - buf = skb_put(skb, len); - memcpy(buf, mbox->data, len); - sdla_mapmem(&card->hw, FR_MB_VECTOR); - /* Check if it's a UDP management packet */ - sendpacket = skb->data; - memcpy(&buf2, &card->wandev.udp_port, 2); - udp_type = udp_pkt_type(skb, card); - if ((udp_type == UDP_FPIPE_TYPE) || (udp_type == UDP_DRVSTATS_TYPE)) - { - if (udp_type == UDP_DRVSTATS_TYPE) - { - ++chan->rx_intr_DRVSTATS_request; - process_udp_driver_call(UDP_PKT_FRM_NETWORK, card, skb, - dev, dlci, chan); - } - else - { - ++chan->rx_intr_FPIPE_request; - process_udp_mgmt_pkt(UDP_PKT_FRM_NETWORK, card, skb, - dev, dlci, chan); - } - } - else - { - /* Decapsulate packet and pass it up the protocol stack */ - skb->dev = dev; - buf = skb_pull(skb, 1); /* remove hardware header */ - if (!wanrouter_type_trans(skb, dev)) - { - /* can't decapsulate packet */ - dev_kfree_skb(skb); - ++chan->ifstats.rx_errors; - ++card->wandev.stats.rx_errors; - } - else - { - netif_rx(skb); - ++chan->ifstats.rx_packets; - ++card->wandev.stats.rx_packets; - chan->ifstats.rx_bytes += skb->len; - card->wandev.stats.rx_bytes += skb->len; - } - } - sdla_mapmem(&card->hw, FR_MB_VECTOR); -} -/*============================================================================ - * Receive interrupt handler. - */ + + if (dev == NULL) { -static void fr508_rx_intr(sdla_t * card) -{ - fr_buf_ctl_t *frbuf = card->rxmb; - struct sk_buff *skb; - struct device *dev; - fr_channel_t *chan; - unsigned dlci, len, offs; - void *buf; - unsigned rx_count = 0; - fr508_flags_t *flags = card->flags; - char *ptr = &flags->iflag; - int i, err, udp_type; - if (frbuf->flag != 0x01) - { - printk(KERN_INFO - "%s: corrupted Rx buffer @ 0x%X, flag = 0x%02X!\n", - card->devname, (unsigned) frbuf, frbuf->flag); - printk(KERN_INFO "%s: ID Bytes = ", card->devname); - for (i = 0; i < 8; i++) - printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i)); - printk(KERN_INFO "\n"); - ++card->statistics.rx_intr_corrupt_rx_bfr; - return; - } - - do - { - len = frbuf->length; - dlci = frbuf->dlci; - offs = frbuf->offset; - /* Find network interface for this packet */ - dev = find_channel(card, dlci); + /* unconfigured DLCI, so discard packet */ + printk(KERN_INFO "%s: received data on unconfigured DLCI %d!\n", + card->devname, dlci); + ++card->statistics.rx_intr_on_orphaned_DLCI; + + } else { chan = dev->priv; - if (dev == NULL) - { - /* Invalid channel, discard packet */ - printk(KERN_INFO "%s: receiving on orphaned DLCI %d!\n" - ,card->devname, dlci); - ++card->statistics.rx_intr_on_orphaned_DLCI; - } - else - { - skb = dev_alloc_skb(len); - if (!dev->start || (skb == NULL)) - { - ++chan->ifstats.rx_dropped; - if (dev->start) - { - printk(KERN_INFO - "%s: no socket buffers available!\n", - card->devname); - ++chan->rx_intr_no_socket; - } else - ++chan->rx_intr_dev_not_started; + + skb = dev_alloc_skb(len); + + if (!dev->start || (skb == NULL)) { + ++chan->ifstats.rx_dropped; + + if(dev->start) { + + printk(KERN_INFO + "%s: no socket buffers available!\n", + card->devname); + chan->drvstats_rx_intr.rx_intr_no_socket ++; + + } else + chan->drvstats_rx_intr. + rx_intr_dev_not_started ++; + } else { + /* Copy data to the socket buffer */ + if ((offs + len) > card->u.f.rx_top + 1) { + unsigned tmp = card->u.f.rx_top - offs + 1; + + buf = skb_put(skb, tmp); + sdla_peek(&card->hw, offs, buf, tmp); + offs = card->u.f.rx_base; + len -= tmp; + } + + buf = skb_put(skb, len); + sdla_peek(&card->hw, offs, buf, len); + + udp_type = udp_pkt_type( skb, card ); + + if(udp_type != UDP_INVALID_TYPE) { + if(store_udp_mgmt_pkt(udp_type, + UDP_PKT_FRM_NETWORK, card, skb, dlci)) { + flags->imask |= FR_INTR_TIMER; + if (udp_type == UDP_FPIPE_TYPE){ + chan->drvstats_rx_intr. + rx_intr_PIPE_request ++; + } + } } - else - { - /* Copy data to the socket buffer */ - if ((offs + len) > card->u.f.rx_top + 1) - { - unsigned tmp = card->u.f.rx_top - offs + 1; - buf = skb_put(skb, tmp); - sdla_peek(&card->hw, offs, buf, tmp); - offs = card->u.f.rx_base; - len -= tmp; - } - buf = skb_put(skb, len); - sdla_peek(&card->hw, offs, buf, len); - udp_type = udp_pkt_type(skb, card); - if (udp_type == UDP_DRVSTATS_TYPE) - { - ++chan->rx_intr_DRVSTATS_request; - process_udp_driver_call( - UDP_PKT_FRM_NETWORK, card, skb, - dev, dlci, chan); + + else if (chan->usedby == API) { + api_rx_hdr_t* api_rx_hdr; + chan->drvstats_rx_intr. + rx_intr_bfr_passed_to_stack ++; + chan->ifstats.rx_packets ++; + card->wandev.stats.rx_packets ++; + chan->ifstats.rx_bytes += skb->len; + card->wandev.stats.rx_bytes += skb->len; + + skb_push(skb, sizeof(api_rx_hdr_t)); + api_rx_hdr = (api_rx_hdr_t*)&skb->data[0x00]; + api_rx_hdr->attr = frbuf->attr; + api_rx_hdr->time_stamp = frbuf->tmstamp; + skb->protocol = htons(0x16); + skb->pkt_type = PACKET_HOST; + /* Pass it up the protocol stack */ + skb->dev = dev; + skb->mac.raw = skb->data; + netif_rx(skb); + + } else if (handle_IPXWAN(skb->data,chan->name, + chan->enable_IPX, chan->network_number)) { + if (chan->enable_IPX) { + fr_send(card, dlci, 0, skb->len, + skb->data); } - else if (udp_type == UDP_FPIPE_TYPE) - { - ++chan->rx_intr_FPIPE_request; - err = process_udp_mgmt_pkt( - UDP_PKT_FRM_NETWORK, card, - skb, dev, dlci, chan); + dev_kfree_skb(skb); + +/*FIXME: Fix the ARPS in next release + + } else if (is_arp(skb->data)) { + if (process_ARP((arphdr_1490_t *)skb->data, card, dev)) { + printk (KERN_INFO "%s: Error processing ARP Packet.\n", card->devname); } - else if (handle_IPXWAN(skb->data, card->devname, card->wandev.enable_IPX, card->wandev.network_number)) - { - if (card->wandev.enable_IPX) - fr508_send(card, dlci, 0, skb->len, skb->data); - } - else - { - /* Decapsulate packet and pass it up the - protocol stack */ - skb->dev = dev; - /* remove hardware header */ - buf = skb_pull(skb, 1); - if (!wanrouter_type_trans(skb, dev)) - { - /* can't decapsulate packet */ - dev_kfree_skb(skb); - ++chan-> - rx_intr_bfr_not_passed_to_stack; - ++chan-> - ifstats.rx_errors; - ++card-> - wandev.stats.rx_errors; - } - else - { - netif_rx(skb); - ++chan->rx_intr_bfr_passed_to_stack; - ++chan->ifstats.rx_packets; - ++card->wandev.stats.rx_packets; - chan->ifstats.rx_bytes += skb->len; - card->wandev.stats.rx_bytes += skb->len; - } + dev_kfree_skb(skb); +*/ + + } else if ( skb->data[0] != 0x03) { + printk(KERN_INFO "%s: Non IETF packet discarded.\n", card->devname); + dev_kfree_skb(skb); + + } else { + + len_incl_hdr = skb->len; + /* Decapsulate packet and pass it up the + protocol stack */ + skb->dev = dev; + + /* remove hardware header */ + buf = skb_pull(skb, 1); + + if (!wanrouter_type_trans(skb, dev)) { + + /* can't decapsulate packet */ + dev_kfree_skb(skb); + chan->drvstats_rx_intr. + rx_intr_bfr_not_passed_to_stack ++; + ++ chan->ifstats.rx_errors; + ++ card->wandev.stats.rx_errors; + + } else { + netif_rx(skb); + chan->drvstats_rx_intr. + rx_intr_bfr_passed_to_stack ++; + ++ chan->ifstats.rx_packets; + ++ card->wandev.stats.rx_packets; + chan->ifstats.rx_bytes += len_incl_hdr; + card->wandev.stats.rx_bytes += + len_incl_hdr; } - } - } - /* Release buffer element and calculate a pointer to the next - one */ - frbuf->flag = 0; - card->rxmb = ++frbuf; - if ((void *) frbuf > card->u.f.rxmb_last) - card->rxmb = card->u.f.rxmb_base; - /* The loop put in is temporary, that is why the break is - * placed here. (?????) - */ - break; - frbuf = card->rxmb; - } - while (frbuf->flag && ((++rx_count) < 4)); + } + } + } + + /* Release buffer element and calculate a pointer to the next one */ + frbuf->flag = 0; + card->rxmb = ++frbuf; + if ((void*)frbuf > card->u.f.rxmb_last) + card->rxmb = card->u.f.rxmb_base; + } + /*============================================================================ * Transmit interrupt handler. - * o print a warning - * o - * If number of spurious interrupts exceeded some limit, then ??? */ -static void tx_intr(sdla_t * card) +static void tx_intr(sdla_t *card) { - struct device *dev = card->wandev.dev; - if (card->intr_mode == BUFFER_INTR_MODE) - { - for (; dev; dev = dev->slave) - { - if (!dev || !dev->start) - { - ++card->statistics.tx_intr_dev_not_started; - continue; + fr508_flags_t* flags = card->flags; + fr_tx_buf_ctl_t* bctl; + struct device* dev = card->wandev.dev; + fr_channel_t* chan; + + if(card->hw.type == SDLA_S514){ + bctl = (void*)(flags->tse_offs + card->hw.dpmbase); + }else{ + bctl = (void*)(flags->tse_offs - FR_MB_VECTOR + + card->hw.dpmbase); + } + + /* Find the structure and make it unbusy */ + dev = find_channel(card, flags->dlci); + chan = dev->priv; + + if(!chan->transmit_length) { + printk(KERN_INFO "%s: tx int error - transmit length zero\n", + card->wandev.name); + return; + } + + /* If the 'if_send()' procedure is currently checking the 'tbusy' + status, then we cannot transmit. Instead, we configure the microcode + so as to re-issue this transmit interrupt at a later stage. + */ + if (test_bit(2, (void*)&card->wandev.critical)) { + fr_dlci_interface_t* dlci_interface = chan->dlci_int_interface; + bctl->flag = 0xA0; + dlci_interface->gen_interrupt |= FR_INTR_TXRDY; + printk(KERN_INFO "%s: TX Interrupt Detected busy if_send\n",card->devname); + + } else { + bctl->dlci = flags->dlci; + bctl->length = chan->transmit_length; + sdla_poke(&card->hw, bctl->offset, chan->transmit_buffer, + chan->transmit_length); + bctl->flag = 0xC0; + + ++chan->ifstats.tx_packets; + ++card->wandev.stats.tx_packets; + chan->ifstats.tx_bytes += chan->transmit_length; + card->wandev.stats.tx_bytes += chan->transmit_length; + chan->transmit_length = 0; + + /* if any other interfaces have transmit interrupts pending, */ + /* do not disable the global transmit interrupt */ + if(!(-- card->u.f.tx_interrupts_pending)) + flags->imask &= ~FR_INTR_TXRDY; + + dev->tbusy = 0; + mark_bh(NET_BH); + } +} + + +/*============================================================================ + * Timer interrupt handler. + FIXME: update comments as we modify the code + * The timer interrupt is used for three purposes: + * 1) Processing udp calls from 'fpipemon'. + * 2) Processing update calls from /proc file system + * 2) Reading board-level statistics for updating the proc file system. + * 3) Sending inverse ARP request packets. + */ +static void timer_intr(sdla_t *card) +{ + fr508_flags_t* flags = card->flags; + + if(card->u.f.timer_int_enabled & TMR_INT_ENABLED_UDP) { + if(card->u.f.udp_type == UDP_FPIPE_TYPE) { + if(process_udp_mgmt_pkt(card)) { + card->u.f.timer_int_enabled &= + ~TMR_INT_ENABLED_UDP; } - dev->tbusy = 0; - mark_bh(NET_BH); } + } + + if(card->u.f.timer_int_enabled & TMR_INT_ENABLED_UPDATE) { + fr_get_err_stats(card); + fr_get_stats(card); + card->u.f.update_comms_stats = 0; + card->u.f.timer_int_enabled &= ~TMR_INT_ENABLED_UPDATE; } - else - { - dev->tbusy = 0; - mark_bh(NET_BH); + + +//FIXME: Fix the dynamic IP addressing +/* +goto L4; + + // Used to send inarp request at given interval + if (card->wandev.state == WAN_CONNECTED) { + int num_remaining = 0; + for (dev=card->wandev.dev;dev;dev=dev->slave) { + fr_channel_t *chan = dev->priv; + + if (chan->inarp == INARP_REQUEST && + chan->state == WAN_CONNECTED) { + num_remaining++; + + if ((jiffies - chan->inarp_tick) > (chan->inarp_interval * HZ)) { + send_inarp_request(card,dev); + chan->inarp_tick = jiffies; + } + } + } + if (!num_remaining) { // no more to process + flags->imask &= ~FR_INTR_TIMER; + } } +L4: + ; +*/ + if(!card->u.f.timer_int_enabled) + flags->imask &= ~FR_INTR_TIMER; } + /*============================================================================ * Spurious interrupt handler. * o print a warning * o - * If number of spurious interrupts exceeded some limit, then ??? */ - -static void spur_intr(sdla_t * card) +static void spur_intr (sdla_t* card) { printk(KERN_INFO "%s: spurious interrupt!\n", card->devname); } -/* - Return 0 for non-IPXWAN packet - 1 for IPXWAN packet or IPX is not enabled! +//FIXME: Fix the IPX in next version +/*=========================================================================== + * Return 0 for non-IPXWAN packet + * 1 for IPXWAN packet or IPX is not enabled! + * FIXME: Use a IPX structure here not offsets */ - static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number) { int i; - if (sendpacket[1] == 0x00 && + + if( sendpacket[1] == 0x00 && sendpacket[2] == 0x80 && sendpacket[6] == 0x81 && - sendpacket[7] == 0x37) - { + sendpacket[7] == 0x37) { + /* It's an IPX packet */ - if (!enable_IPX) { + if(!enable_IPX) { /* Return 1 so we don't pass it up the stack. */ + //FIXME: Take this out when IPX is fixed + printk (KERN_INFO + "%s: WARNING: Unsupported IPX packet received and dropped\n", + devname); return 1; } - } - else - { + } else { /* It's not IPX so return and pass it up the stack. */ return 0; } - if (sendpacket[24] == 0x90 && - sendpacket[25] == 0x04) + + if( sendpacket[24] == 0x90 && + sendpacket[25] == 0x04) { /* It's IPXWAN */ - if (sendpacket[10] == 0x02 && - sendpacket[42] == 0x00) + + if( sendpacket[10] == 0x02 && + sendpacket[42] == 0x00) { /* It's a timer request packet */ - printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n", devname); - /* Go through the routing options and answer no to every */ - /* option except Unnumbered RIP/SAP */ - for (i = 49; sendpacket[i] == 0x00; i += 5) + printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n",devname); + + /* Go through the routing options and answer no to every + * option except Unnumbered RIP/SAP + */ + for(i = 49; sendpacket[i] == 0x00; i += 5) { /* 0x02 is the option for Unnumbered RIP/SAP */ - if (sendpacket[i + 4] != 0x02) + if( sendpacket[i + 4] != 0x02) { sendpacket[i + 1] = 0; } } + /* Skip over the extended Node ID option */ - if (sendpacket[i] == 0x04) + if( sendpacket[i] == 0x04 ) + { i += 8; - /* We also want to turn off all header compression opt. */ - for (; sendpacket[i] == 0x80;) + } + + /* We also want to turn off all header compression opt. + */ + for(; sendpacket[i] == 0x80 ;) { sendpacket[i + 1] = 0; i += (sendpacket[i + 2] << 8) + (sendpacket[i + 3]) + 4; } + /* Set the packet type to timer response */ sendpacket[42] = 0x01; - printk(KERN_INFO "%s: Sending IPXWAN Timer Response\n", devname); + + printk(KERN_INFO "%s: Sending IPXWAN Timer Response\n",devname); } - else if (sendpacket[42] == 0x02) + else if( sendpacket[42] == 0x02 ) { /* This is an information request packet */ - printk(KERN_INFO "%s: Received IPXWAN Information Request packet\n", devname); + printk(KERN_INFO "%s: Received IPXWAN Information Request packet\n",devname); + /* Set the packet type to information response */ sendpacket[42] = 0x03; + /* Set the router name */ sendpacket[59] = 'F'; sendpacket[60] = 'P'; @@ -1756,465 +2082,558 @@ sendpacket[63] = 'E'; sendpacket[64] = '-'; sendpacket[65] = CVHexToAscii(network_number >> 28); - sendpacket[66] = CVHexToAscii((network_number & 0x0F000000) >> 24); - sendpacket[67] = CVHexToAscii((network_number & 0x00F00000) >> 20); - sendpacket[68] = CVHexToAscii((network_number & 0x000F0000) >> 16); - sendpacket[69] = CVHexToAscii((network_number & 0x0000F000) >> 12); - sendpacket[70] = CVHexToAscii((network_number & 0x00000F00) >> 8); - sendpacket[71] = CVHexToAscii((network_number & 0x000000F0) >> 4); + sendpacket[66] = CVHexToAscii((network_number & 0x0F000000)>> 24); + sendpacket[67] = CVHexToAscii((network_number & 0x00F00000)>> 20); + sendpacket[68] = CVHexToAscii((network_number & 0x000F0000)>> 16); + sendpacket[69] = CVHexToAscii((network_number & 0x0000F000)>> 12); + sendpacket[70] = CVHexToAscii((network_number & 0x00000F00)>> 8); + sendpacket[71] = CVHexToAscii((network_number & 0x000000F0)>> 4); sendpacket[72] = CVHexToAscii(network_number & 0x0000000F); - for (i = 73; i < 107; i += 1) + for(i = 73; i < 107; i+= 1) + { sendpacket[i] = 0; - printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n", devname); + } + + printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n",devname); } else { - printk(KERN_INFO "%s: Unknown IPXWAN packet!\n", devname); + printk(KERN_INFO "%s: Unknown IPXWAN packet!\n",devname); return 0; } + /* Set the WNodeID to our network address */ - sendpacket[43] = (unsigned char) (network_number >> 24); - sendpacket[44] = (unsigned char) ((network_number & 0x00FF0000) >> 16); - sendpacket[45] = (unsigned char) ((network_number & 0x0000FF00) >> 8); - sendpacket[46] = (unsigned char) (network_number & 0x000000FF); + sendpacket[43] = (unsigned char)(network_number >> 24); + sendpacket[44] = (unsigned char)((network_number & 0x00FF0000) >> 16); + sendpacket[45] = (unsigned char)((network_number & 0x0000FF00) >> 8); + sendpacket[46] = (unsigned char)(network_number & 0x000000FF); + return 1; } - /* If we get here, its an IPX-data packet so it'll get passed up the stack. */ - /* switch the network numbers */ - switch_net_numbers(sendpacket, network_number, 1); + + /* If we get here, its an IPX-data packet so it'll get passed up the + * stack. + * switch the network numbers + */ + switch_net_numbers(sendpacket, network_number ,1); return 0; } +/*============================================================================ + * Process Route. + * This routine is called as a polling routine to dynamically add/delete routes + * negotiated by inverse ARP. It is in this "task" because we don't want routes + * to be added while in interrupt context. +*/ + +static void process_route (sdla_t* card) +{ + struct device* dev; + struct in_device *in_dev; + struct rtentry route; + int err = 0; + mm_segment_t fs; + + + /* Dynamic Route adding/removing */ + for (dev = card->wandev.dev; dev ; dev = dev->slave) { + if ( ((fr_channel_t*)dev->priv)->route_flag == ADD_ROUTE || + ((fr_channel_t*)dev->priv)->route_flag == REMOVE_ROUTE ) { + fs = get_fs(); + + in_dev = dev->ip_ptr; -/****** Background Polling Routines ****************************************/ + if( in_dev != NULL && in_dev->ifa_list != NULL) { + memset(&route, 0, sizeof(route)); + route.rt_dev = dev->name; + route.rt_flags = 0; + + ((struct sockaddr_in *) &(route.rt_dst)) -> + sin_addr.s_addr=in_dev->ifa_list->ifa_address; + ((struct sockaddr_in *) &(route.rt_dst)) -> + sin_family = AF_INET; + ((struct sockaddr_in *) &(route.rt_genmask)) -> + sin_addr.s_addr = 0xFFFFFFFF; + ((struct sockaddr_in *) &(route.rt_genmask)) -> + sin_family = AF_INET; + + switch(((fr_channel_t*)dev->priv)->route_flag) { + + case ADD_ROUTE: + set_fs(get_ds()); /* get user space block */ + err = ip_rt_ioctl( SIOCADDRT, &route); + set_fs(fs); /* restore old block */ + + if (err) { + printk(KERN_INFO "%s: Adding of route failed. Error: %d\n", card->devname,err); + printk(KERN_INFO "%s: Address: %s\n", + ((fr_channel_t*)dev->priv)->name, + in_ntoa(in_dev->ifa_list->ifa_address) ); + } + else { + ((fr_channel_t*)dev->priv)-> + route_flag = ROUTE_ADDED; + } + break; -/*============================================================================ - * Main polling routine. - * This routine is repeatedly called by the WANPIPE 'thead' to allow for - * time-dependent housekeeping work. - * - * o fetch asynchronous network events. - * - * Notes: - * 1. This routine may be called on interrupt context with all interrupts - * enabled. Beware! - */ + case REMOVE_ROUTE: + set_fs(get_ds()); /* get user space block */ + err = ip_rt_ioctl( SIOCDELRT, &route); + set_fs(fs); /* restore old block */ + + if (err) { + printk(KERN_INFO "%s: Deleting of route failed. Error: %d\n", card->devname,err); + printk(KERN_INFO "%s: Address: %s\n", + dev->name,in_ntoa(in_dev->ifa_list->ifa_address) ); + } else { + printk(KERN_INFO "%s: Removed route.\n", + ((fr_channel_t*)dev->priv)->name); + ((fr_channel_t*)dev->priv)->route_flag = NO_ROUTE; + } + break; + } /* Case Statement */ + } + } /* If ADD/DELETE ROUTE */ + } /* Device 'For' Loop */ -static void wpf_poll(sdla_t * card) -{ -/* struct device* dev = card->wandev.dev; */ - fr508_flags_t *flags = card->flags; - unsigned long host_cpu_flags; - ++card->statistics.poll_entry; - if (((jiffies - card->state_tick) < HZ) || - (card->intr_mode == INTR_TEST_MODE)) - return; - disable_irq(card->hw.irq); - ++card->irq_dis_poll_count; - if (test_and_set_bit(0, (void *) &card->wandev.critical)) - { - ++card->statistics.poll_already_critical; - save_flags(host_cpu_flags); - cli(); - if ((!card->irq_dis_if_send_count) && - (!(--card->irq_dis_poll_count))) - enable_irq(card->hw.irq); - restore_flags(host_cpu_flags); - return; - } - card->wandev.critical = 0x11; - ++card->statistics.poll_processed; - /* This is to be changed later ??? */ - /* - if( dev && dev->tbusy && !(flags->imask & 0x02) ) { - printk(KERN_INFO "%s: Wpf_Poll: tbusy = 0x01, imask = 0x%02X\n", card->devname, flags->imask); - } - */ - if (flags->event) - { - fr_mbox_t *mbox = card->mbox; - int err; - memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); - mbox->cmd.command = FR_READ_STATUS; - err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - if (err) - fr_event(card, err, mbox); - } - card->wandev.critical = 0; - save_flags(host_cpu_flags); - cli(); - if ((!card->irq_dis_if_send_count) && (!(--card->irq_dis_poll_count))) - enable_irq(card->hw.irq); - restore_flags(host_cpu_flags); - card->state_tick = jiffies; + card->poll = NULL; } + + /****** Frame Relay Firmware-Specific Functions *****************************/ /*============================================================================ * Read firmware code version. * o fill string str with firmware version info. */ - -static int fr_read_version(sdla_t * card, char *str) +static int fr_read_version (sdla_t* card, char* str) { - fr_mbox_t *mbox = card->mbox; + fr_mbox_t* mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - do + + do { - memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); mbox->cmd.command = FR_READ_CODE_VERSION; + mbox->cmd.length = 0; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; } while (err && retry-- && fr_event(card, err, mbox)); - if (!err && str) - { + if (!err && str) { int len = mbox->cmd.length; memcpy(str, mbox->data, len); - str[len] = '\0'; + str[len] = '\0'; } return err; } + /*============================================================================ * Set global configuration. */ - -static int fr_configure(sdla_t * card, fr_conf_t * conf) +static int fr_configure (sdla_t* card, fr_conf_t *conf) { - fr_mbox_t *mbox = card->mbox; + fr_mbox_t* mbox = card->mbox; int retry = MAX_CMD_RETRY; int dlci_num = card->u.f.dlci_num; int err, i; - do + + do { - memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); memcpy(mbox->data, conf, sizeof(fr_conf_t)); - if (dlci_num) - for (i = 0; i < dlci_num; ++i) - ((fr_conf_t *) mbox->data)->dlci[i] = - card->u.f.node_dlci[i]; + + if (dlci_num) for (i = 0; i < dlci_num; ++i) + ((fr_conf_t*)mbox->data)->dlci[i] = + card->u.f.node_dlci[i]; + mbox->cmd.command = FR_SET_CONFIG; mbox->cmd.length = - sizeof(fr_conf_t) + dlci_num * sizeof(short); + sizeof(fr_conf_t) + dlci_num * sizeof(short); + err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } - while (err && retry-- && fr_event(card, err, mbox)); + + } while (err && retry-- && fr_event(card, err, mbox)); return err; } + /*============================================================================ * Set DLCI configuration. */ -static int fr_dlci_configure(sdla_t * card, fr_dlc_conf_t * conf, unsigned dlci) +static int fr_dlci_configure (sdla_t* card, fr_dlc_conf_t *conf, unsigned dlci) { - fr_mbox_t *mbox = card->mbox; + fr_mbox_t* mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; + do { - memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); memcpy(mbox->data, conf, sizeof(fr_dlc_conf_t)); - mbox->cmd.dlci = (unsigned short) dlci; + mbox->cmd.dlci = (unsigned short) dlci; mbox->cmd.command = FR_SET_CONFIG; - mbox->cmd.length = 0x0E; + mbox->cmd.length = sizeof(fr_dlc_conf_t); err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } - while (err && retry--); + } while (err && retry--); + return err; } /*============================================================================ * Set interrupt mode. */ -static int fr_set_intr_mode(sdla_t * card, unsigned mode, unsigned mtu) +static int fr_set_intr_mode (sdla_t* card, unsigned mode, unsigned mtu, + unsigned short timeout) { - fr_mbox_t *mbox = card->mbox; + fr_mbox_t* mbox = card->mbox; + fr508_intr_ctl_t* ictl = (void*)mbox->data; int retry = MAX_CMD_RETRY; int err; + do { - memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); - if (card->hw.fwid == SFID_FR502) - { - fr502_intr_ctl_t *ictl = (void *) mbox->data; - memset(ictl, 0, sizeof(fr502_intr_ctl_t)); - ictl->mode = mode; - ictl->tx_len = mtu; - mbox->cmd.length = sizeof(fr502_intr_ctl_t); - } - else - { - fr508_intr_ctl_t *ictl = (void *) mbox->data; - memset(ictl, 0, sizeof(fr508_intr_ctl_t)); - ictl->mode = mode; - ictl->tx_len = mtu; - ictl->irq = card->hw.irq; - mbox->cmd.length = sizeof(fr508_intr_ctl_t); - } + memset(ictl, 0, sizeof(fr508_intr_ctl_t)); + ictl->mode = mode; + ictl->tx_len = mtu; + ictl->irq = card->hw.irq; + + /* indicate timeout on timer */ + if (mode & 0x20) ictl->timeout = timeout; + + mbox->cmd.length = sizeof(fr508_intr_ctl_t); mbox->cmd.command = FR_SET_INTR_MODE; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } - while (err && retry-- && fr_event(card, err, mbox)); + + } while (err && retry-- && fr_event(card, err, mbox)); return err; } + /*============================================================================ * Enable communications. */ -static int fr_comm_enable(sdla_t * card) +static int fr_comm_enable (sdla_t* card) { - fr_mbox_t *mbox = card->mbox; + fr_mbox_t* mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - do + + do { - memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); mbox->cmd.command = FR_COMM_ENABLE; + mbox->cmd.length = 0; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } - while (err && retry-- && fr_event(card, err, mbox)); + } while (err && retry-- && fr_event(card, err, mbox)); return err; } + /*============================================================================ * Disable communications. */ -static int fr_comm_disable(sdla_t * card) +static int fr_comm_disable (sdla_t* card) { - fr_mbox_t *mbox = card->mbox; + fr_mbox_t* mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; + do { - memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); mbox->cmd.command = FR_COMM_DISABLE; + mbox->cmd.length = 0; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } - while (err && retry-- && fr_event(card, err, mbox)); + } while (err && retry-- && fr_event(card, err, mbox)); + + retry = MAX_CMD_RETRY; + + do { + mbox->cmd.command = FR_SET_MODEM_STATUS; + mbox->cmd.length = 1; + mbox->data[0] = 0; + err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; + } while (err && retry-- && fr_event(card, err, mbox)); return err; } + /*============================================================================ * Get communications error statistics. */ -static int fr_get_err_stats(sdla_t * card) +static int fr_get_err_stats (sdla_t* card) { - fr_mbox_t *mbox = card->mbox; + fr_mbox_t* mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - + + do { - memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); mbox->cmd.command = FR_READ_ERROR_STATS; + mbox->cmd.length = 0; + mbox->cmd.dlci = 0; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } - while (err && retry-- && fr_event(card, err, mbox)); - - if (!err) - { - fr_comm_stat_t *stats = (void *) mbox->data; - card->wandev.stats.rx_over_errors = stats->rx_overruns; - card->wandev.stats.rx_crc_errors = stats->rx_bad_crc; - card->wandev.stats.rx_missed_errors = stats->rx_aborts; - card->wandev.stats.rx_length_errors = stats->rx_too_long; + } while (err && retry-- && fr_event(card, err, mbox)); + + if (!err) { + fr_comm_stat_t* stats = (void*)mbox->data; + card->wandev.stats.rx_over_errors = stats->rx_overruns; + card->wandev.stats.rx_crc_errors = stats->rx_bad_crc; + card->wandev.stats.rx_missed_errors = stats->rx_aborts; + card->wandev.stats.rx_length_errors = stats->rx_too_long; card->wandev.stats.tx_aborted_errors = stats->tx_aborts; + } + return err; } + /*============================================================================ * Get statistics. */ -static int fr_get_stats(sdla_t * card) +static int fr_get_stats (sdla_t* card) { - fr_mbox_t *mbox = card->mbox; + fr_mbox_t* mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - do + + + do { - memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); mbox->cmd.command = FR_READ_STATISTICS; + mbox->cmd.length = 0; + mbox->cmd.dlci = 0; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } - while (err && retry-- && fr_event(card, err, mbox)); - - if (!err) - { - fr_link_stat_t *stats = (void *) mbox->data; + } while (err && retry-- && fr_event(card, err, mbox)); + + if (!err) { + fr_link_stat_t* stats = (void*)mbox->data; card->wandev.stats.rx_frame_errors = stats->rx_bad_format; - card->wandev.stats.rx_dropped = stats->rx_dropped + stats->rx_dropped2; + card->wandev.stats.rx_dropped = + stats->rx_dropped + stats->rx_dropped2; } + return err; } + /*============================================================================ * Add DLCI(s) (Access Node only!). * This routine will perform the ADD_DLCIs command for the specified DLCI. */ -static int fr_add_dlci(sdla_t * card, int dlci, int num) +static int fr_add_dlci (sdla_t* card, int dlci) { - fr_mbox_t *mbox = card->mbox; + fr_mbox_t* mbox = card->mbox; int retry = MAX_CMD_RETRY; - int err, i; - do + int err; + + do { - unsigned short *dlci_list = (void *) mbox->data; - memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); - for (i = 0; i < num; ++i) - dlci_list[i] = card->u.f.node_dlci[i]; - mbox->cmd.length = num * sizeof(short); + unsigned short* dlci_list = (void*)mbox->data; + + mbox->cmd.length = sizeof(short); + dlci_list[0] = dlci; mbox->cmd.command = FR_ADD_DLCI; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } - while (err && retry-- && fr_event(card, err, mbox)); + + } while (err && retry-- && fr_event(card, err, mbox)); return err; } + /*============================================================================ * Activate DLCI(s) (Access Node only!). - * This routine will perform the ACTIVATE_DLCIs command with a list of DLCIs. + * This routine will perform the ACTIVATE_DLCIs command with a DLCI number. */ -static int fr_activate_dlci(sdla_t * card, int dlci, int num) +static int fr_activate_dlci (sdla_t* card, int dlci) { - fr_mbox_t *mbox = card->mbox; + fr_mbox_t* mbox = card->mbox; int retry = MAX_CMD_RETRY; - int err, i; + int err; + do { - unsigned short *dlci_list = (void *) mbox->data; - memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); - for (i = 0; i < num; ++i) - dlci_list[i] = card->u.f.node_dlci[i]; - mbox->cmd.length = num * sizeof(short); + unsigned short* dlci_list = (void*)mbox->data; + + mbox->cmd.length = sizeof(short); + dlci_list[0] = dlci; mbox->cmd.command = FR_ACTIVATE_DLCI; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } - while (err && retry-- && fr_event(card, err, mbox)); + + } while (err && retry-- && fr_event(card, err, mbox)); return err; } + /*============================================================================ - * Issue in-channel signalling frame. + * Delete DLCI(s) (Access Node only!). + * This routine will perform the DELETE_DLCIs command with a DLCI number. */ -static int fr_issue_isf(sdla_t * card, int isf) +static int fr_delete_dlci (sdla_t* card, int dlci) { - fr_mbox_t *mbox = card->mbox; + fr_mbox_t* mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; + do { - memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); - mbox->data[0] = isf; - mbox->cmd.length = 1; - mbox->cmd.command = FR_ISSUE_IS_FRAME; + unsigned short* dlci_list = (void*)mbox->data; + + mbox->cmd.length = sizeof(short); + dlci_list[0] = dlci; + mbox->cmd.command = FR_DELETE_DLCI; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } - while (err && retry-- && fr_event(card, err, mbox)); + + } while (err && retry-- && fr_event(card, err, mbox)); return err; } + + + /*============================================================================ - * Send a frame (S502 version). + * Issue in-channel signalling frame. */ -static int fr502_send(sdla_t * card, int dlci, int attr, int len, void *buf) +static int fr_issue_isf (sdla_t* card, int isf) { - fr_mbox_t *mbox = card->mbox; + fr_mbox_t* mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - - do + + do { - memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); - memcpy(mbox->data, buf, len); - mbox->cmd.dlci = dlci; - mbox->cmd.attr = attr; - mbox->cmd.length = len; - mbox->cmd.command = FR_WRITE; + mbox->data[0] = isf; + mbox->cmd.length = 1; + mbox->cmd.command = FR_ISSUE_IS_FRAME; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } - while (err && retry-- && fr_event(card, err, mbox)); + } while (err && retry-- && fr_event(card, err, mbox)); return err; } + /*============================================================================ - * Send a frame (S508 version). + * Send a frame on a selected DLCI. */ -static int fr508_send(sdla_t * card, int dlci, int attr, int len, void *buf) +static int fr_send (sdla_t* card, int dlci, unsigned char attr, int len, + void *buf) { - fr_mbox_t *mbox = card->mbox; + fr_mbox_t* mbox = card->mbox + 0x800; int retry = MAX_CMD_RETRY; int err; - + do { - memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); - mbox->cmd.dlci = dlci; - mbox->cmd.attr = attr; - mbox->cmd.length = len; + mbox->cmd.dlci = dlci; + mbox->cmd.attr = attr; + mbox->cmd.length = len; mbox->cmd.command = FR_WRITE; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } - while (err && retry-- && fr_event(card, err, mbox)); - - if (!err) - { - fr_buf_ctl_t *frbuf = (void *) (*(unsigned long *) mbox->data - - FR_MB_VECTOR + card->hw.dpmbase); - sdla_poke(&card->hw, frbuf->offset, buf, len); - frbuf->flag = 0x01; - } + } while (err && retry-- && fr_event(card, err, mbox)); + + if (!err) { + + fr_tx_buf_ctl_t* frbuf; + + if(card->hw.type == SDLA_S514) + frbuf = (void*)(*(unsigned long*)mbox->data + + card->hw.dpmbase); + else + frbuf = (void*)(*(unsigned long*)mbox->data - + FR_MB_VECTOR + card->hw.dpmbase); + + sdla_poke(&card->hw, frbuf->offset, buf, len); + frbuf->flag = 0x01; + } + return err; } /****** Firmware Asynchronous Event Handlers ********************************/ /*============================================================================ - * Main asynchronous event/error handler. + * Main asyncronous event/error handler. * This routine is called whenever firmware command returns non-zero * return code. * * Return zero if previous command has to be cancelled. */ - -static int fr_event(sdla_t * card, int event, fr_mbox_t * mbox) +static int fr_event (sdla_t *card, int event, fr_mbox_t* mbox) { - fr508_flags_t *flags = card->flags; + fr508_flags_t* flags = card->flags; char *ptr = &flags->iflag; int i; - switch (event) - { + + switch (event) { + case FRRES_MODEM_FAILURE: return fr_modem_failure(card, mbox); + case FRRES_CHANNEL_DOWN: + { + struct device *dev; + + /* Remove all routes from associated DLCI's */ + for (dev = card->wandev.dev; dev; dev = dev->slave) { + fr_channel_t *chan = dev->priv; + if (chan->route_flag == ROUTE_ADDED) { + chan->route_flag = REMOVE_ROUTE; + card->poll = &process_route; + } + + if (chan->inarp == INARP_CONFIGURED) { + chan->inarp = INARP_REQUEST; + } + } + wanpipe_set_state(card, WAN_DISCONNECTED); return 1; + } + case FRRES_CHANNEL_UP: + { + struct device *dev; + int num_requests = 0; + + /* Remove all routes from associated DLCI's */ + for (dev = card->wandev.dev; dev; dev = dev->slave) { + fr_channel_t *chan = dev->priv; + if( chan->inarp == INARP_REQUEST ){ + num_requests++; + chan->inarp_tick = jiffies; + } + } + + /* Allow timer interrupts */ + if (num_requests) flags->imask |= 0x20; wanpipe_set_state(card, WAN_CONNECTED); return 1; + } + case FRRES_DLCI_CHANGE: return fr_dlci_change(card, mbox); + case FRRES_DLCI_MISMATCH: - printk(KERN_INFO "%s: DLCI list mismatch!\n", - card->devname); + printk(KERN_INFO "%s: DLCI list mismatch!\n", + card->devname); return 1; + case CMD_TIMEOUT: printk(KERN_ERR "%s: command 0x%02X timed out!\n", - card->devname, mbox->cmd.command); - printk(KERN_INFO "%s: ID Bytes = ", card->devname); - for (i = 0; i < 8; i++) - printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i)); - printk(KERN_INFO "\n"); + card->devname, mbox->cmd.command); + printk(KERN_INFO "%s: ID Bytes = ",card->devname); + for(i = 0; i < 8; i ++) + printk(KERN_INFO "0x%02X ", *(ptr + 0x18 + i)); + printk(KERN_INFO "\n"); + break; + case FRRES_DLCI_INACTIVE: - printk(KERN_ERR "%s: DLCI %u is inactive!\n", - card->devname, mbox->cmd.dlci); break; + case FRRES_CIR_OVERFLOW: break; case FRRES_BUFFER_OVERFLOW: - break; + break; default: printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n" - ,card->devname, mbox->cmd.command, event); + , card->devname, mbox->cmd.command, event); } + return 0; } @@ -2223,178 +2642,228 @@ * * Return zero if previous command has to be cancelled. */ -static int fr_modem_failure(sdla_t * card, fr_mbox_t * mbox) +static int fr_modem_failure (sdla_t *card, fr_mbox_t* mbox) { printk(KERN_INFO "%s: physical link down! (modem error 0x%02X)\n", - card->devname, mbox->data[0]); - switch (mbox->cmd.command) - { + card->devname, mbox->data[0]); + + switch (mbox->cmd.command){ case FR_WRITE: + case FR_READ: return 0; } + return 1; } + /*============================================================================ * Handle DLCI status change. * * Return zero if previous command has to be cancelled. */ -static int fr_dlci_change(sdla_t * card, fr_mbox_t * mbox) +static int fr_dlci_change (sdla_t *card, fr_mbox_t* mbox) { - dlci_status_t *status = (void *) mbox->data; + dlci_status_t* status = (void*)mbox->data; int cnt = mbox->cmd.length / sizeof(dlci_status_t); - fr_dlc_conf_t cfg; fr_channel_t *chan; - struct device *dev2; - for (; cnt; --cnt, ++status) - { - unsigned short dlci = status->dlci; - struct device *dev = find_channel(card, dlci); - if (dev == NULL) - { - printk(KERN_INFO - "%s: CPE contains unconfigured DLCI= %d\n", - card->devname, dlci); - } - else - { - if (status->state & 0x01) - { + struct device* dev2; + + + for (; cnt; --cnt, ++status) { + + unsigned short dlci= status->dlci; + struct device* dev = find_channel(card, dlci); + + if (dev == NULL){ + printk(KERN_INFO + "%s: CPE contains unconfigured DLCI= %d\n", + card->devname, dlci); + + printk(KERN_INFO + "%s: unconfigured DLCI %d reported by network\n" + , card->devname, dlci); + + }else{ + if (status->state == FR_LINK_INOPER) { printk(KERN_INFO - "%s: DLCI %u has been deleted!\n", - card->devname, dlci); + "%s: DLCI %u is inactive!\n", + card->devname, dlci); + if (dev && dev->start) set_chan_state(dev, WAN_DISCONNECTED); } - else if (status->state & 0x02) - { + + if (status->state & FR_DLCI_DELETED) { + printk(KERN_INFO - "%s: DLCI %u becomes active!\n", - card->devname, dlci); + "%s: DLCI %u has been deleted!\n", + card->devname, dlci); + + if (dev && dev->start) { + fr_channel_t *chan = dev->priv; + + if (chan->route_flag == ROUTE_ADDED) { + chan->route_flag = REMOVE_ROUTE; + card->poll = &process_route; + } + + if (chan->inarp == INARP_CONFIGURED) { + chan->inarp = INARP_REQUEST; + } + + set_chan_state(dev, WAN_DISCONNECTED); + } + + } else if (status->state & FR_DLCI_ACTIVE) { + chan = dev->priv; + /* This flag is used for configuring specific DLCI(s) when they become active. - */ + */ chan->dlci_configured = DLCI_CONFIG_PENDING; + if (dev && dev->start) set_chan_state(dev, WAN_CONNECTED); + } } } - for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) - { + + for (dev2 =card->wandev.dev; dev2; dev2 = dev2->slave){ + chan = dev2->priv; - if (chan->dlci_configured == DLCI_CONFIG_PENDING) - { - memset(&cfg, 0, sizeof(cfg)); - if (chan->cir_status == CIR_DISABLED) - { - cfg.cir_fwd = cfg.cir_bwd = 16; - cfg.bc_fwd = cfg.bc_bwd = 16; - cfg.conf_flags = 0x0001; - printk(KERN_INFO "%s: CIR Disabled for %s\n", - card->devname, chan->name); - } else if (chan->cir_status == CIR_ENABLED) { - cfg.cir_fwd = cfg.cir_bwd = chan->cir; - cfg.bc_fwd = cfg.bc_bwd = chan->bc; - cfg.be_fwd = cfg.be_bwd = chan->be; - cfg.conf_flags = 0x0000; - printk(KERN_INFO "%s: CIR Enabled for %s\n", - card->devname, chan->name); - } - if (fr_dlci_configure(card, &cfg, chan->dlci)) - { - printk(KERN_INFO - "%s: DLCI Configure failed for %d\n", - card->devname, chan->dlci); + + if (chan->dlci_configured == DLCI_CONFIG_PENDING) { + if (fr_init_dlci(card, chan)){ return 1; } - chan->dlci_configured = DLCI_CONFIGURED; - /* Read the interface byte mapping into the channel - structure. - */ - if (card->intr_mode == DLCI_LIST_INTR_MODE) - read_DLCI_IB_mapping(card, chan); } + } return 1; } + + +static int fr_init_dlci (sdla_t *card, fr_channel_t *chan) +{ + fr_dlc_conf_t cfg; + fr508_flags_t* flags = card->flags; + + memset(&cfg, 0, sizeof(cfg)); + + if ( chan->cir_status == CIR_DISABLED) { + + cfg.cir_fwd = cfg.cir_bwd = 16; + cfg.bc_fwd = cfg.bc_bwd = 16; + cfg.conf_flags = 0x0001; + + }else if (chan->cir_status == CIR_ENABLED) { + + cfg.cir_fwd = cfg.cir_bwd = chan->cir; + cfg.bc_fwd = cfg.bc_bwd = chan->bc; + cfg.be_fwd = cfg.be_bwd = chan->be; + cfg.conf_flags = 0x0000; + } + + if (fr_dlci_configure( card, &cfg , chan->dlci)){ + printk(KERN_INFO + "%s: DLCI Configure failed for %d\n", + card->devname, chan->dlci); + return 1; + } + + chan->dlci_configured = DLCI_CONFIGURED; + + /* Allow timer interrupts */ + if( chan->inarp == INARP_REQUEST && card->wandev.state == WAN_CONNECTED) { + chan->inarp_tick = jiffies; + flags->imask |= 0x20; + } + + /* Read the interface byte mapping into the channel + structure. + */ + read_DLCI_IB_mapping( card, chan ); + + return 0; +} /******* Miscellaneous ******************************************************/ /*============================================================================ * Update channel state. */ -static int update_chan_state(struct device *dev) +static int update_chan_state (struct device* dev) { - fr_channel_t *chan = dev->priv; - sdla_t *card = chan->card; - fr_mbox_t *mbox = card->mbox; + fr_channel_t* chan = dev->priv; + sdla_t* card = chan->card; + fr_mbox_t* mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - int dlci_found = 0; - do + do { - memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); mbox->cmd.command = FR_LIST_ACTIVE_DLCI; + mbox->cmd.length = 0; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } - while (err && retry-- && fr_event(card, err, mbox)); + } while (err && retry-- && fr_event(card, err, mbox)); - if (!err) - { - unsigned short *list = (void *) mbox->data; + if (!err) { + + unsigned short* list = (void*)mbox->data; int cnt = mbox->cmd.length / sizeof(short); - for (; cnt; --cnt, ++list) - { - if (*list == chan->dlci) - { - dlci_found = 1; - set_chan_state(dev, WAN_CONNECTED); + + for (; cnt; --cnt, ++list) { + + if (*list == chan->dlci) { + set_chan_state(dev, WAN_CONNECTED); break; } } - if (!dlci_found) - printk(KERN_INFO "%s: DLCI %u is inactive\n", - card->devname, chan->dlci); } - + return err; } + /*============================================================================ * Set channel state. */ -static void set_chan_state(struct device *dev, int state) +static void set_chan_state (struct device* dev, int state) { - fr_channel_t *chan = dev->priv; - sdla_t *card = chan->card; + fr_channel_t* chan = dev->priv; + sdla_t* card = chan->card; unsigned long flags; - + save_flags(flags); cli(); - - if (chan->state != state) - { - switch (state) - { + + if (chan->state != state) { + + switch (state) { + case WAN_CONNECTED: - printk(KERN_INFO "%s: interface %s connected!\n" - ,card->devname, dev->name); + printk(KERN_INFO + "%s: Interface %s: DLCI %d connected\n", + card->devname, dev->name, chan->dlci); break; + case WAN_CONNECTING: - printk(KERN_INFO - "%s: interface %s connecting...\n", - card->devname, dev->name); + printk(KERN_INFO + "%s: Interface %s: DLCI %d connecting\n", + card->devname, dev->name, chan->dlci); break; + case WAN_DISCONNECTED: - printk(KERN_INFO - "%s: interface %s disconnected!\n", - card->devname, dev->name); + printk (KERN_INFO + "%s: Interface %s: DLCI %d disconnected!\n", + card->devname, dev->name, chan->dlci); break; } + chan->state = state; } + chan->state_tick = jiffies; restore_flags(flags); } @@ -2402,14 +2871,15 @@ /*============================================================================ * Find network device by its channel number. */ -static struct device *find_channel(sdla_t * card, unsigned dlci) +static struct device* find_channel (sdla_t* card, unsigned dlci) { - struct device *dev; - for (dev = card->wandev.dev; dev; dev = dev->slave) - if (((fr_channel_t *) dev->priv)->dlci == dlci) - break; - return dev; + + if(dlci > HIGHEST_VALID_DLCI) + return NULL; + + return(card->u.f.dlci_to_dev_map[dlci]); } + /*============================================================================ * Check to see if a frame can be sent. If no transmit buffers available, * enable transmit interrupts. @@ -2417,22 +2887,17 @@ * Return: 1 - Tx buffer(s) available * 0 - no buffers available */ - -static int is_tx_ready(sdla_t * card, fr_channel_t * chan) +static int is_tx_ready (sdla_t* card, fr_channel_t* chan) { - if (card->hw.fwid == SFID_FR508) - { - unsigned char sb = inb(card->hw.port); - if (sb & 0x02) - return 1; - } - else - { - fr502_flags_t *flags = card->flags; - if (flags->tx_ready) - return 1; - flags->imask |= 0x02; - } + unsigned char sb; + + if(card->hw.type == SDLA_S514) + return 1; + + sb = inb(card->hw.port); + if (sb & 0x02) + return 1; + return 0; } @@ -2440,688 +2905,790 @@ * Convert decimal string to unsigned integer. * If len != 0 then only 'len' characters of the string are converted. */ -static unsigned int dec_to_uint(unsigned char *str, int len) +static unsigned int dec_to_uint (unsigned char* str, int len) { unsigned val; - if (!len) + + if (!len) len = strlen(str); + for (val = 0; len && is_digit(*str); ++str, --len) - val = (val * 10) + (*str - (unsigned) '0'); + val = (val * 10) + (*str - (unsigned)'0'); + return val; } + + +/*============================================================================= + * Store a UDP management packet for later processing. + */ + +static int store_udp_mgmt_pkt(int udp_type, char udp_pkt_src, sdla_t* card, + struct sk_buff *skb, int dlci) +{ + int udp_pkt_stored = 0; + + if(!card->u.f.udp_pkt_lgth &&(skb->len <= MAX_LGTH_UDP_MGNT_PKT)){ + card->u.f.udp_pkt_lgth = skb->len; + card->u.f.udp_type = udp_type; + card->u.f.udp_pkt_src = udp_pkt_src; + card->u.f.udp_dlci = dlci; + memcpy(card->u.f.udp_pkt_data, skb->data, skb->len); + card->u.f.timer_int_enabled |= TMR_INT_ENABLED_UDP; + udp_pkt_stored = 1; + + }else{ + printk(KERN_INFO "ERROR: UDP packet not stored for DLCI %d\n", + dlci); + } + + dev_kfree_skb(skb); + + return(udp_pkt_stored); +} + + /*============================================================================== * Process UDP call of type FPIPE8ND */ - -static int process_udp_mgmt_pkt(char udp_pkt_src, sdla_t * card, struct sk_buff *skb, struct device *dev, int dlci, fr_channel_t * chan) +static int process_udp_mgmt_pkt(sdla_t* card) { + int c_retry = MAX_CMD_RETRY; - unsigned char *data; unsigned char *buf; - unsigned char buf2[5]; - unsigned int loops, frames, len; - unsigned long data_ptr; - unsigned short real_len, buffer_length; + unsigned char frames; + unsigned int len; + unsigned short buffer_length; struct sk_buff *new_skb; - unsigned char *sendpacket; - fr_mbox_t *mbox = card->mbox; + fr_mbox_t* mbox = card->mbox; int err; struct timeval tv; int udp_mgmt_req_valid = 1; - sendpacket = skb->data; - memcpy(&buf2, &card->wandev.udp_port, 2); - if ((data = kmalloc(2000, GFP_ATOMIC)) == NULL) - { - printk(KERN_INFO - "%s: Error allocating memory for UDP management cmnd 0x%02X", - card->devname, data[47]); - ++chan->UDP_FPIPE_mgmt_kmalloc_err; - return 1; - } - memcpy(data, sendpacket, skb->len); - switch (data[47]) - { - /* FPIPE_ENABLE_TRACE */ - case 0x41: - /* FPIPE_DISABLE_TRACE */ - case 0x42: - /* FPIPE_GET_TRACE_INFO */ - case 0x43: - /* SET FT1 MODE */ - case 0x81: - if (udp_pkt_src == UDP_PKT_FRM_NETWORK) - { - ++chan->UDP_FPIPE_mgmt_direction_err; + struct device* dev; + fr_channel_t* chan; + fr_udp_pkt_t *fr_udp_pkt; + unsigned short num_trc_els; + fr_trc_el_t* ptr_trc_el; + fr_trc_el_t trc_el; + fpipemon_trc_t* fpipemon_trc; + + char udp_pkt_src = card->u.f.udp_pkt_src; + int dlci = card->u.f.udp_dlci; + + /* Find network interface for this packet */ + dev = find_channel(card, dlci); + chan = dev->priv; + + /* If the UDP packet is from the network, we are going to have to + transmit a response. Before doing so, we must check to see that + we are not currently transmitting a frame (in 'if_send()') and + that we are not already in a 'delayed transmit' state. + */ + if(udp_pkt_src == UDP_PKT_FRM_NETWORK) { + if (test_bit(0, (void*)&card->wandev.critical) || + test_bit(2, (void*)&card->wandev.critical)) { + return 0; + } + if((dev->tbusy) || (card->u.f.tx_interrupts_pending)) { + return 0; + } + } + + fr_udp_pkt = (fr_udp_pkt_t *)card->u.f.udp_pkt_data; + + switch(fr_udp_pkt->cblock.command) { + + case FPIPE_ENABLE_TRACING: + case FPIPE_DISABLE_TRACING: + case FPIPE_GET_TRACE_INFO: + case FR_SET_FT1_MODE: + if(udp_pkt_src == UDP_PKT_FRM_NETWORK) { + chan->drvstats_gen. + UDP_PIPE_mgmt_direction_err ++; udp_mgmt_req_valid = 0; break; } - /* FPIPE_FT1_READ_STATUS */ - case 0x44: - /* FT1 MONITOR STATUS */ - case 0x80: - if (card->hw.fwid != SFID_FR508) - { - ++chan->UDP_FPIPE_mgmt_adptr_type_err; - udp_mgmt_req_valid = 0; - } - break; + default: break; - } - if (!udp_mgmt_req_valid) - { + } + + if(!udp_mgmt_req_valid) { /* set length to 0 */ - data[48] = data[49] = 0; + fr_udp_pkt->cblock.length = 0; /* set return code */ - data[50] = (card->hw.fwid != SFID_FR508) ? 0x1F : 0xCD; - } - else - { - switch (data[47]) - { - /* FPIPE_ENABLE_TRACE */ - case 0x41: - if (!TracingEnabled) - { - do - { - /* SET_TRACE_CONFIGURATION */ - mbox->cmd.command = 0x60; - mbox->cmd.length = 1; - mbox->cmd.dlci = 0x00; - mbox->data[0] = 0x37; - err = sdla_exec(mbox) ? - mbox->cmd.result : CMD_TIMEOUT; - } - while (err && c_retry-- && fr_event(card, err, mbox)); - - if (err) - { - TracingEnabled = 0; - /* set the return code */ - data[50] = mbox->cmd.result; - mbox->cmd.length = 0; - break; - } - /* get num_frames */ - sdla_peek(&card->hw, 0x9000, &num_frames, 2); - sdla_peek(&card->hw, 0x9002, &curr_trace_addr,4); - start_trace_addr = curr_trace_addr; - /* MAX_SEND_BUFFER_SIZE - - * sizeof(UDP_MGMT_PACKET) - 41 */ - available_buffer_space = 1926; - /* set return code */ - data[50] = 0; - } - else - { - /* set return code to line trace already - enabled */ - data[50] = 1; - } - mbox->cmd.length = 0; - TracingEnabled = 1; - break; - /* FPIPE_DISABLE_TRACE */ - case 0x42: - if (TracingEnabled) - { - do - { - /* SET_TRACE_CONFIGURATION */ - mbox->cmd.command = 0x60; - mbox->cmd.length = 1; - mbox->cmd.dlci = 0x00; - mbox->data[0] = 0x36; - err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } - while (err && c_retry-- && fr_event(card, err, mbox)); - } - /* set return code */ - data[50] = 0; - mbox->cmd.length = 0; - TracingEnabled = 0; - break; - /* FPIPE_GET_TRACE_INFO */ - case 0x43: - /* Line trace cannot be performed on the 502 */ - if (!TracingEnabled) - { - /* set return code */ - data[50] = 1; + fr_udp_pkt->cblock.result = 0xCD; + } else { + + switch(fr_udp_pkt->cblock.command) { + + case FPIPE_ENABLE_TRACING: + if(!card->TracingEnabled) { + do { + mbox->cmd.command = FR_SET_TRACE_CONFIG; + mbox->cmd.length = 1; + mbox->cmd.dlci = 0x00; + mbox->data[0] = fr_udp_pkt->data[0] | + RESET_TRC; + err = sdla_exec(mbox) ? + mbox->cmd.result : CMD_TIMEOUT; + } while (err && c_retry-- && fr_event(card, err, + mbox)); + + if(err) { + card->TracingEnabled = 0; + /* set the return code */ + fr_udp_pkt->cblock.result = + mbox->cmd.result; mbox->cmd.length = 0; break; } - buffer_length = 0; - loops = (num_frames < 20) ? num_frames : 20; - for (frames = 0; frames < loops; frames += 1) - { - sdla_peek(&card->hw, curr_trace_addr, &buf2, 1); - /* no data on board so exit */ - if (buf2[0] == 0x00) - break; - /* 1+sizeof(FRAME_DATA) = 9 */ - if ((available_buffer_space - buffer_length) < 9) - { - /* indicate we have more frames on board - and exit */ - data[62] |= 0x02; - break; - } - /* get frame status */ - sdla_peek(&card->hw, curr_trace_addr + 0x05, &data[62 + buffer_length], 1); - /* get time stamp */ - sdla_peek(&card->hw, curr_trace_addr + 0x06, &data[66 + buffer_length], 2); - /* get frame length */ - sdla_peek(&card->hw, curr_trace_addr + 0x01, &data[64 + buffer_length], 2); - /* get pointer to real data */ - sdla_peek(&card->hw, curr_trace_addr + 0x0C,&data_ptr, 4); - /* see if we can fit the frame into the user buffer */ - memcpy(&real_len, &data[64 + buffer_length], 2); - if (data_ptr == 0 || real_len + 8 > available_buffer_space) - { - data[63 + buffer_length] = 0x00; - } - else - { - /* we can take it next time */ - if (available_buffer_space - buffer_length < real_len + 8) - { - data[62] |= 0x02; - break; - } - /* ok, get the frame */ - data[63 + buffer_length] = 0x01; - /* get the data */ - sdla_peek(&card->hw, data_ptr, &data[68 + buffer_length], real_len); - /* zero the opp flag to show we got the frame */ - buf2[0] = 0x00; - sdla_poke(&card->hw, curr_trace_addr, &buf2, 1); - /* now move onto the next frame */ - curr_trace_addr += 16; - /* check if we passed the last address */ - if (curr_trace_addr >= (start_trace_addr + num_frames * 16)) - curr_trace_addr = start_trace_addr; - /* update buffer length and make sure - its even */ - if (data[63 + buffer_length] == 0x01) - buffer_length += real_len - 1; - /* for the header */ - buffer_length += 8; - if (buffer_length & 0x0001) - buffer_length += 1; - } - } - /* ok now set the total number of frames passed in the - high 5 bits */ - data[62] = (frames << 3) | data[62]; - /* set the data length */ - mbox->cmd.length = buffer_length; - memcpy(&data[48], &buffer_length, 2); - data[50] = 0; - break; - /* FPIPE_FT1_READ_STATUS */ - case 0x44: - sdla_peek(&card->hw, 0xF020, &data[62], 2); - data[48] = 2; - data[49] = 0; - data[50] = 0; - mbox->cmd.length = 2; - break; - /* FPIPE_FLUSH_DRIVER_STATS */ - case 0x48: - init_chan_statistics(chan); - init_global_statistics(card); - mbox->cmd.length = 0; - break; - case 0x49: - do_gettimeofday(&tv); - chan->router_up_time = tv.tv_sec - chan->router_start_time; - *(unsigned long *) &data[62] = chan->router_up_time; - mbox->cmd.length = 4; - break; - /* FPIPE_KILL_BOARD */ - case 0x50: - break; - /* FT1 MONITOR STATUS */ - case 0x80: - if (data[62] == 1) - { - if (rCount++ != 0) - { - data[50] = 0; - mbox->cmd.length = 1; - break; - } - } - /* Disable FT1 MONITOR STATUS */ - if (data[62] == 0) - { - if (--rCount != 0) - { - data[50] = 0; - mbox->cmd.length = 1; - break; - } - } - default: - do - { - memcpy(&mbox->cmd, &sendpacket[47], sizeof(fr_cmd_t)); - if (mbox->cmd.length) - memcpy(&mbox->data, &sendpacket[62],mbox->cmd.length); - err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } - while (err && c_retry-- && fr_event(card, err, mbox)); + + sdla_peek(&card->hw, NO_TRC_ELEMENTS_OFF, + &num_trc_els, 2); + sdla_peek(&card->hw, BASE_TRC_ELEMENTS_OFF, + &card->u.f.trc_el_base, 4); + card->u.f.curr_trc_el = card->u.f.trc_el_base; + card->u.f.trc_el_last = card->u.f.curr_trc_el + + ((num_trc_els - 1) * + sizeof(fr_trc_el_t)); + + /* Calculate the maximum trace data area in */ + /* the UDP packet */ + card->u.f.trc_bfr_space=(MAX_LGTH_UDP_MGNT_PKT - + sizeof(fr_encap_hdr_t) - + sizeof(ip_pkt_t) - + sizeof(udp_pkt_t) - + sizeof(wp_mgmt_t) - + sizeof(cblock_t)); + + /* set return code */ + fr_udp_pkt->cblock.result = 0; - if (!err) - { - ++chan->UDP_FPIPE_mgmt_adptr_cmnd_OK; - memcpy(data, sendpacket, skb->len); - memcpy(&data[47], &mbox->cmd, sizeof(fr_cmd_t)); - if (mbox->cmd.length) - { - memcpy(&data[62], &mbox->data,mbox->cmd.length); - } + } else { + /* set return code to line trace already + enabled */ + fr_udp_pkt->cblock.result = 1; + } + + mbox->cmd.length = 0; + card->TracingEnabled = 1; + break; + + + case FPIPE_DISABLE_TRACING: + if(card->TracingEnabled) { + + do { + mbox->cmd.command = FR_SET_TRACE_CONFIG; + mbox->cmd.length = 1; + mbox->cmd.dlci = 0x00; + mbox->data[0] = ~ACTIVATE_TRC; + err = sdla_exec(mbox) ? + mbox->cmd.result : CMD_TIMEOUT; + } while (err && c_retry-- && fr_event(card, err, mbox)); + } + + /* set return code */ + fr_udp_pkt->cblock.result = 0; + mbox->cmd.length = 0; + card->TracingEnabled = 0; + break; + + case FPIPE_GET_TRACE_INFO: + + /* Line trace cannot be performed on the 502 */ + if(!card->TracingEnabled) { + /* set return code */ + fr_udp_pkt->cblock.result = 1; + mbox->cmd.length = 0; + break; + } + + (void *)ptr_trc_el = card->u.f.curr_trc_el; + + buffer_length = 0; + fr_udp_pkt->data[0x00] = 0x00; + + for(frames = 0; frames < MAX_FRMS_TRACED; frames ++) { + + sdla_peek(&card->hw, (unsigned long)ptr_trc_el, + (void *)&trc_el.flag, + sizeof(fr_trc_el_t)); + if(trc_el.flag == 0x00) { + break; } - else - { - ++chan->UDP_FPIPE_mgmt_adptr_cmnd_timeout; + if((card->u.f.trc_bfr_space - buffer_length) + < sizeof(fpipemon_trc_hdr_t)) { + fr_udp_pkt->data[0x00] |= MORE_TRC_DATA; + break; + } + + fpipemon_trc = + (fpipemon_trc_t *)&fr_udp_pkt->data[buffer_length]; + fpipemon_trc->fpipemon_trc_hdr.status = + trc_el.attr; + fpipemon_trc->fpipemon_trc_hdr.tmstamp = + trc_el.tmstamp; + fpipemon_trc->fpipemon_trc_hdr.length = + trc_el.length; + + if(!trc_el.offset || !trc_el.length) { + + fpipemon_trc->fpipemon_trc_hdr.data_passed = 0x00; + + }else if((trc_el.length + sizeof(fpipemon_trc_hdr_t) + 1) > + (card->u.f.trc_bfr_space - buffer_length)){ + + fpipemon_trc->fpipemon_trc_hdr.data_passed = 0x00; + fr_udp_pkt->data[0x00] |= MORE_TRC_DATA; + + }else { + fpipemon_trc->fpipemon_trc_hdr.data_passed = 0x01; + sdla_peek(&card->hw, trc_el.offset, + fpipemon_trc->data, + trc_el.length); + } + + trc_el.flag = 0x00; + sdla_poke(&card->hw, (unsigned long)ptr_trc_el, + &trc_el.flag, 1); + + ptr_trc_el ++; + if((void *)ptr_trc_el > card->u.f.trc_el_last) + (void*)ptr_trc_el = card->u.f.trc_el_base; + + buffer_length += sizeof(fpipemon_trc_hdr_t); + if(fpipemon_trc->fpipemon_trc_hdr.data_passed) { + buffer_length += trc_el.length; + } + + if(fr_udp_pkt->data[0x00] & MORE_TRC_DATA) { + break; } + } + + if(frames == MAX_FRMS_TRACED) { + fr_udp_pkt->data[0x00] |= MORE_TRC_DATA; + } + + card->u.f.curr_trc_el = (void *)ptr_trc_el; + + /* set the total number of frames passed */ + fr_udp_pkt->data[0x00] |= + ((frames << 1) & (MAX_FRMS_TRACED << 1)); + + /* set the data length and return code */ + fr_udp_pkt->cblock.length = mbox->cmd.length = buffer_length; + fr_udp_pkt->cblock.result = 0; + break; + + case FPIPE_FT1_READ_STATUS: + sdla_peek(&card->hw, 0xF020, + &fr_udp_pkt->data[0x00] , 2); + fr_udp_pkt->cblock.length = 2; + fr_udp_pkt->cblock.result = 0; + break; + + case FPIPE_FLUSH_DRIVER_STATS: + init_chan_statistics(chan); + init_global_statistics(card); + mbox->cmd.length = 0; + break; + + case FPIPE_ROUTER_UP_TIME: + do_gettimeofday(&tv); + chan->router_up_time = tv.tv_sec - + chan->router_start_time; + *(unsigned long *)&fr_udp_pkt->data = + chan->router_up_time; + mbox->cmd.length = 4; + break; + + + case FR_FT1_STATUS_CTRL: + if(fr_udp_pkt->data[0] == 1) { + if(rCount++ != 0 ){ + fr_udp_pkt->cblock.result = 0; + mbox->cmd.length = 1; + break; + } } - } - /* Fill UDP TTL */ - data[10] = card->wandev.ttl; - len = reply_udp(data, mbox->cmd.length); - if (udp_pkt_src == UDP_PKT_FRM_NETWORK) - { - err = fr508_send(card, dlci, 0, len, data); - if (err) - ++chan->UDP_FPIPE_mgmt_adptr_send_passed; + + /* Disable FT1 MONITOR STATUS */ + if(fr_udp_pkt->data[0] == 0) { + if( --rCount != 0) { + fr_udp_pkt->cblock.result = 0; + mbox->cmd.length = 1; + break; + } + } + + case FPIPE_DRIVER_STAT_IFSEND: + memcpy(fr_udp_pkt->data, + &chan->drvstats_if_send.if_send_entry, + sizeof(if_send_stat_t)); + mbox->cmd.length = sizeof(if_send_stat_t); + break; + + case FPIPE_DRIVER_STAT_INTR: + memcpy(fr_udp_pkt->data, + &card->statistics.isr_entry, + sizeof(global_stats_t)); + memcpy(&fr_udp_pkt->data[sizeof(global_stats_t)], + &chan->drvstats_rx_intr.rx_intr_no_socket, + sizeof(rx_intr_stat_t)); + mbox->cmd.length = sizeof(global_stats_t) + + sizeof(rx_intr_stat_t); + break; + + case FPIPE_DRIVER_STAT_GEN: + memcpy(fr_udp_pkt->data, + &chan->drvstats_gen.UDP_PIPE_mgmt_kmalloc_err, + sizeof(pipe_mgmt_stat_t)); + + memcpy(&fr_udp_pkt->data[sizeof(pipe_mgmt_stat_t)], + &card->statistics, sizeof(global_stats_t)); + + fr_udp_pkt->cblock.result = 0; + fr_udp_pkt->cblock.length = sizeof(global_stats_t)+ + sizeof(rx_intr_stat_t); + mbox->cmd.length = fr_udp_pkt->cblock.length; + break; + + default: + do { + memcpy(&mbox->cmd, + &fr_udp_pkt->cblock.command, + sizeof(fr_cmd_t)); + if(mbox->cmd.length) { + memcpy(&mbox->data, + (char *)fr_udp_pkt->data, + mbox->cmd.length); + } + + err = sdla_exec(mbox) ? mbox->cmd.result : + CMD_TIMEOUT; + } while (err && c_retry-- && fr_event(card, err, mbox)); + + if(!err) + chan->drvstats_gen. + UDP_PIPE_mgmt_adptr_cmnd_OK ++; + else + chan->drvstats_gen. + UDP_PIPE_mgmt_adptr_cmnd_timeout ++; + + /* copy the result back to our buffer */ + memcpy(&fr_udp_pkt->cblock.command, + &mbox->cmd, sizeof(fr_cmd_t)); + + if(mbox->cmd.length) { + memcpy(&fr_udp_pkt->data, + &mbox->data, mbox->cmd.length); + } + } + } + + /* Fill UDP TTL */ + fr_udp_pkt->ip_pkt.ttl = card->wandev.ttl; + len = reply_udp(card->u.f.udp_pkt_data, mbox->cmd.length); + + if(udp_pkt_src == UDP_PKT_FRM_NETWORK) { + + err = fr_send(card, dlci, 0, len, card->u.f.udp_pkt_data); + if (err) + chan->drvstats_gen.UDP_PIPE_mgmt_adptr_send_passed ++; else - ++chan->UDP_FPIPE_mgmt_adptr_send_failed; - dev_kfree_skb(skb); - } - else - { + chan->drvstats_gen.UDP_PIPE_mgmt_adptr_send_failed ++; + } else { /* Allocate socket buffer */ - if ((new_skb = dev_alloc_skb(len)) != NULL) - { + if((new_skb = dev_alloc_skb(len)) != NULL) { + /* copy data into new_skb */ buf = skb_put(new_skb, len); - memcpy(buf, data, len); + memcpy(buf, card->u.f.udp_pkt_data, len); + /* Decapsulate packet and pass it up the protocol stack */ new_skb->dev = dev; - buf = skb_pull(new_skb, 1); /* remove hardware header */ - if (!wanrouter_type_trans(new_skb, dev)) - { - ++chan->UDP_FPIPE_mgmt_not_passed_to_stack; + buf = skb_pull(new_skb, 1); /* remove hardware header*/ + + if(!wanrouter_type_trans(new_skb, dev)) { + + chan->drvstats_gen. + UDP_PIPE_mgmt_not_passed_to_stack ++; /* can't decapsulate packet */ dev_kfree_skb(new_skb); - } - else - { - ++chan->UDP_FPIPE_mgmt_passed_to_stack; + } else { + chan->drvstats_gen. + UDP_PIPE_mgmt_passed_to_stack ++; netif_rx(new_skb); - } - } - else - { - ++chan->UDP_FPIPE_mgmt_no_socket; - printk(KERN_INFO - "%s: UDP mgmt cmnd, no socket buffers available!\n", - card->devname); - } - } - kfree(data); - return 0; + } + + } else { + chan->drvstats_gen.UDP_PIPE_mgmt_no_socket ++; + printk(KERN_INFO + "%s: UDP mgmt cmnd, no socket buffers available!\n", + card->devname); + } + } + + card->u.f.udp_pkt_lgth = 0; + + return 1; } + /*============================================================================== - * Perform the Interrupt Test by running the READ_CODE_VERSION command MAX_INTR_ - * TEST_COUNTER times. + * Send Inverse ARP Request */ - -static int intr_test(sdla_t * card) + +int send_inarp_request(sdla_t *card, struct device *dev) { - fr_mbox_t *mb = card->mbox; - int err, i; - /* The critical flag is unset here because we want to get into the - ISR without the flag already set. The If_open sets the flag. - */ - card->wandev.critical = 0; - err = fr_set_intr_mode(card, 0x08, card->wandev.mtu); - if (err == CMD_OK) - { - for (i = 0; i < MAX_INTR_TEST_COUNTER; i++) - { - /* Run command READ_CODE_VERSION */ - memset(&mb->cmd, 0, sizeof(fr_cmd_t)); - mb->cmd.length = 0; - mb->cmd.command = 0x40; - err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; - if (err != CMD_OK) - fr_event(card, err, mb); - } - } - else - { - return err; + arphdr_1490_t *ArpPacket; + arphdr_fr_t *arphdr; + fr_channel_t *chan = dev->priv; + struct in_device *in_dev; + + in_dev = dev->ip_ptr; + + if(in_dev != NULL ) { + + ArpPacket = kmalloc(sizeof(arphdr_1490_t) + sizeof(arphdr_fr_t), GFP_ATOMIC); + /* SNAP Header indicating ARP */ + ArpPacket->control = 0x03; + ArpPacket->pad = 0x00; + ArpPacket->NLPID = 0x80; + ArpPacket->OUI[0] = 0; + ArpPacket->OUI[1] = 0; + ArpPacket->OUI[2] = 0; + ArpPacket->PID = 0x0608; + + arphdr = (arphdr_fr_t *)(ArpPacket + 1); // Go to ARP Packet + + /* InARP request */ + arphdr->ar_hrd = 0x0F00; /* Frame Relay HW type */ + arphdr->ar_pro = 0x0008; /* IP Protocol */ + arphdr->ar_hln = 2; /* HW addr length */ + arphdr->ar_pln = 4; /* IP addr length */ + arphdr->ar_op = htons(0x08); /* InARP Request */ + arphdr->ar_sha = 0; /* src HW DLCI - Doesn't matter */ + if(in_dev->ifa_list != NULL) + arphdr->ar_sip = in_dev->ifa_list->ifa_local; /* Local Address */else + arphdr->ar_sip = 0; + arphdr->ar_tha = 0; /* dst HW DLCI - Doesn't matter */ + arphdr->ar_tip = 0; /* Remote Address -- what we want */ + + printk(KERN_INFO "%s: Sending InARP request on DLCI %d.\n", card->devname, chan->dlci); + fr_send(card, chan->dlci, 0, + sizeof(arphdr_1490_t) + sizeof(arphdr_fr_t), + (void *)ArpPacket); + kfree(ArpPacket); } - err = fr_set_intr_mode(card, 0, card->wandev.mtu); - if (err != CMD_OK) - return err; - card->wandev.critical = 1; - return 0; + + return 1; } -/*============================================================================ - * Process UDP call of type DRVSTATS. + + +/*============================================================================== + * Check packet for ARP Type */ -static int process_udp_driver_call(char udp_pkt_src, sdla_t * card, struct sk_buff *skb, struct device *dev, int dlci, fr_channel_t * chan) + +int is_arp(void *buf) { - int c_retry = MAX_CMD_RETRY; - unsigned char *sendpacket; - unsigned char buf2[5]; - unsigned char *data; - unsigned char *buf; - unsigned int len; - fr_mbox_t *mbox = card->mbox; - struct sk_buff *new_skb; - int err; - sendpacket = skb->data; - memcpy(&buf2, &card->wandev.udp_port, 2); - if ((data = kmalloc(2000, GFP_ATOMIC)) == NULL) - { - printk(KERN_INFO - "%s: Error allocating memory for UDP DRIVER STATS cmnd0x%02X" - ,card->devname, data[45]); - ++chan->UDP_DRVSTATS_mgmt_kmalloc_err; + arphdr_1490_t *arphdr = (arphdr_1490_t *)buf; + + if (arphdr->pad == 0x00 && + arphdr->NLPID == 0x80 && + arphdr->PID == 0x0608) return 1; - } - memcpy(data, sendpacket, skb->len); - switch (data[47]) - { - case 0x45: - *(unsigned long *) &data[62] = chan->if_send_entry; - *(unsigned long *) &data[66] = chan->if_send_skb_null; - *(unsigned long *) &data[70] = chan->if_send_broadcast; - *(unsigned long *) &data[74] = chan->if_send_multicast; - *(unsigned long *) &data[78] = chan->if_send_critical_ISR; - *(unsigned long *) &data[82] = chan->if_send_critical_non_ISR; - *(unsigned long *) &data[86] = chan->if_send_busy; - *(unsigned long *) &data[90] = chan->if_send_busy_timeout; - *(unsigned long *) &data[94] = chan->if_send_DRVSTATS_request; - *(unsigned long *) &data[98] = chan->if_send_FPIPE_request; - *(unsigned long *) &data[102] = chan->if_send_wan_disconnected; - *(unsigned long *) &data[106] = chan->if_send_dlci_disconnected; - *(unsigned long *) &data[110] = chan->if_send_no_bfrs; - *(unsigned long *) &data[114] = chan->if_send_adptr_bfrs_full; - *(unsigned long *) &data[118] = chan->if_send_bfrs_passed_to_adptr; - *(unsigned long *) &data[120] = card->irq_dis_if_send_count; - mbox->cmd.length = 62; - break; - case 0x46: - *(unsigned long *) &data[62] = card->statistics.isr_entry; - *(unsigned long *) &data[66] = card->statistics.isr_already_critical; - *(unsigned long *) &data[70] = card->statistics.isr_rx; - *(unsigned long *) &data[74] = card->statistics.isr_tx; - *(unsigned long *) &data[78] = card->statistics.isr_intr_test; - *(unsigned long *) &data[82] = card->statistics.isr_spurious; - *(unsigned long *) &data[86] = card->statistics.isr_enable_tx_int; - *(unsigned long *) &data[90] = card->statistics.tx_intr_dev_not_started; - *(unsigned long *) &data[94] = card->statistics.rx_intr_corrupt_rx_bfr; - *(unsigned long *) &data[98] = card->statistics.rx_intr_on_orphaned_DLCI; - *(unsigned long *) &data[102] = chan->rx_intr_no_socket; - *(unsigned long *) &data[106] = chan->rx_intr_dev_not_started; - *(unsigned long *) &data[110] = chan->rx_intr_DRVSTATS_request; - *(unsigned long *) &data[114] = chan->rx_intr_FPIPE_request; - *(unsigned long *) &data[118] = chan->rx_intr_bfr_not_passed_to_stack; - *(unsigned long *) &data[122] = chan->rx_intr_bfr_passed_to_stack; - mbox->cmd.length = 64; - break; - case 0x47: - *(unsigned long *) &data[62] = chan->UDP_FPIPE_mgmt_kmalloc_err; - *(unsigned long *) &data[66] = chan->UDP_FPIPE_mgmt_adptr_type_err; - *(unsigned long *) &data[70] = chan->UDP_FPIPE_mgmt_direction_err; - *(unsigned long *) &data[74] = chan->UDP_FPIPE_mgmt_adptr_cmnd_timeout; - *(unsigned long *) &data[78] = chan->UDP_FPIPE_mgmt_adptr_cmnd_OK; - *(unsigned long *) &data[82] = chan->UDP_FPIPE_mgmt_adptr_send_passed; - *(unsigned long *) &data[86] = chan->UDP_FPIPE_mgmt_adptr_send_failed; - *(unsigned long *) &data[90] = chan->UDP_FPIPE_mgmt_no_socket; - *(unsigned long *) &data[94] = chan->UDP_FPIPE_mgmt_not_passed_to_stack; - *(unsigned long *) &data[98] = chan->UDP_FPIPE_mgmt_passed_to_stack; - *(unsigned long *) &data[102] = chan->UDP_DRVSTATS_mgmt_kmalloc_err; - *(unsigned long *) &data[106] = chan->UDP_DRVSTATS_mgmt_adptr_cmnd_timeout; - *(unsigned long *) &data[110] = chan->UDP_DRVSTATS_mgmt_adptr_cmnd_OK; - *(unsigned long *) &data[114] = chan->UDP_DRVSTATS_mgmt_adptr_send_passed; - *(unsigned long *) &data[118] = chan->UDP_DRVSTATS_mgmt_adptr_send_failed; - *(unsigned long *) &data[122] = chan->UDP_DRVSTATS_mgmt_no_socket; - *(unsigned long *) &data[126] = chan->UDP_DRVSTATS_mgmt_not_passed_to_stack; - *(unsigned long *) &data[130] = chan->UDP_DRVSTATS_mgmt_passed_to_stack; - *(unsigned long *) &data[134] = card->statistics.poll_entry; - *(unsigned long *) &data[138] = card->statistics.poll_already_critical; - *(unsigned long *) &data[142] = card->statistics.poll_processed; - *(unsigned long *) &data[144] = card->irq_dis_poll_count; - mbox->cmd.length = 86; - break; - default: - do - { - memcpy(&mbox->cmd, &sendpacket[47], sizeof(fr_cmd_t)); - if (mbox->cmd.length) - memcpy(&mbox->data, &sendpacket[62], mbox->cmd.length); - err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - } - while (err && c_retry-- && fr_event(card, err, mbox)); + else return 0; +} + +/*============================================================================== + * Process ARP Packet Type + */ + +int process_ARP(arphdr_1490_t *ArpPacket, sdla_t *card, struct device* dev) +{ + + arphdr_fr_t *arphdr = (arphdr_fr_t *)(ArpPacket + 1); /* Skip header */ + fr_rx_buf_ctl_t* frbuf = card->rxmb; + struct in_device *in_dev; + + + in_dev = dev->ip_ptr; + if( in_dev != NULL && in_dev->ifa_list != NULL) { + switch (ntohs(arphdr->ar_op)) { + + case 0x08: // Inverse ARP request -- Send Reply, add route. - if (!err) - { - ++chan->UDP_DRVSTATS_mgmt_adptr_cmnd_OK; - memcpy(data, sendpacket, skb->len); - memcpy(&data[47], &mbox->cmd, sizeof(fr_cmd_t)); - if (mbox->cmd.length) - memcpy(&data[62], &mbox->data, mbox->cmd.length); - } - else - { - ++chan->UDP_DRVSTATS_mgmt_adptr_cmnd_timeout; + /* Check for valid Address */ + printk(KERN_INFO "%s: Recvd PtP addr %s -InArp Req\n", ((fr_channel_t *)dev->priv)->name, in_ntoa(arphdr->ar_sip)); + + if ((in_dev->ifa_list->ifa_mask & arphdr->ar_sip) != (in_dev->ifa_list->ifa_mask & in_dev->ifa_list->ifa_local)) { + printk(KERN_INFO "%s: Invalid PtP address. InARP ignored.\n", card->devname); + printk(KERN_INFO "mask %X\n", in_dev->ifa_list->ifa_mask); + printk(KERN_INFO "local %X\n", in_dev->ifa_list->ifa_local); + return -1; } - } - /* Fill UDP TTL */ - data[10] = card->wandev.ttl; - len = reply_udp(data, mbox->cmd.length); - if (udp_pkt_src == UDP_PKT_FRM_NETWORK) - { - err = fr508_send(card, dlci, 0, len, data); - if (err) - ++chan->UDP_DRVSTATS_mgmt_adptr_send_failed; - else - ++chan->UDP_DRVSTATS_mgmt_adptr_send_passed; - dev_kfree_skb(skb); - } - else - { - /* Allocate socket buffer */ - if ((new_skb = dev_alloc_skb(len)) != NULL) - { - /* copy data into new_skb */ - buf = skb_put(new_skb, len); - memcpy(buf, data, len); - /* Decapsulate packet and pass it up the - protocol stack */ - new_skb->dev = dev; - /* remove hardware header */ - buf = skb_pull(new_skb, 1); - if (!wanrouter_type_trans(new_skb, dev)) + + if (in_dev->ifa_list->ifa_local == arphdr->ar_sip) { + printk(KERN_INFO "%s: Local addr = PtP addr. InARP ignored.\n", card->devname); + return -1; + } + + arphdr->ar_op = htons(0x09); /* InARP Reply */ + + /* Set addresses */ + arphdr->ar_tip = arphdr->ar_sip; + arphdr->ar_sip = in_dev->ifa_list->ifa_local; + + fr_send(card, frbuf->dlci, 0, frbuf->length, (void *)ArpPacket); + + /* Modify Point-to-Point Address */ { - /* can't decapsulate packet */ - ++chan->UDP_DRVSTATS_mgmt_not_passed_to_stack; - dev_kfree_skb(new_skb); + struct ifreq if_info; + struct sockaddr_in *if_data; + mm_segment_t fs = get_fs(); + int err; + + /* Set remote addresses */ + memset(&if_info, 0, sizeof(if_info)); + strcpy(if_info.ifr_name, dev->name); + + set_fs(get_ds()); /* get user space block */ + + if_data = (struct sockaddr_in *)&if_info.ifr_dstaddr; + if_data->sin_addr.s_addr = arphdr->ar_tip; + if_data->sin_family = AF_INET; + err = devinet_ioctl( SIOCSIFDSTADDR, &if_info ); + + set_fs(fs); /* restore old block */ } - else + + /* Add Route Flag */ + /* The route will be added in the polling routine so + that it is not interrupt context. */ + + ((fr_channel_t *) dev->priv)->route_flag = ADD_ROUTE; + card->poll = &process_route; + + break; + + case 0x09: // Inverse ARP reply + + /* Check for valid Address */ + printk(KERN_INFO "%s: Recvd PtP addr %s -InArp Reply\n", ((fr_channel_t *)dev->priv)->name, in_ntoa(arphdr->ar_sip)); + + if ((in_dev->ifa_list->ifa_mask & arphdr->ar_sip) != (in_dev->ifa_list->ifa_mask & in_dev->ifa_list->ifa_local)) { + printk(KERN_INFO "%s: Invalid PtP address. InARP ignored.\n", card->devname); + return -1; + } + + if (in_dev->ifa_list->ifa_local == arphdr->ar_sip) { + printk(KERN_INFO "%s: Local addr = PtP addr. InARP ignored.\n", card->devname); + return -1; + } + + /* Modify Point-to-Point Address */ { - ++chan->UDP_DRVSTATS_mgmt_passed_to_stack; - netif_rx(new_skb); + struct ifreq if_info; + struct sockaddr_in *if_data; + mm_segment_t fs = get_fs(); + int err; + + /* Set remote addresses */ + memset(&if_info, 0, sizeof(if_info)); + strcpy(if_info.ifr_name, dev->name); + + set_fs(get_ds()); /* get user space block */ + + if_data = (struct sockaddr_in *)&if_info.ifr_dstaddr; + if_data->sin_addr.s_addr = arphdr->ar_sip; + if_data->sin_family = AF_INET; + err = devinet_ioctl( SIOCSIFDSTADDR, &if_info ); + + set_fs(fs); /* restore old block */ } + + /* Add Route Flag */ + /* The route will be added in the polling routine so + that it is not interrupt context. */ + + ((fr_channel_t *) dev->priv)->route_flag = ADD_ROUTE; + ((fr_channel_t *) dev->priv)->inarp = INARP_CONFIGURED; + card->poll = &process_route; + + break; + default: // ARP's and RARP's -- Shouldn't happen. } - else - { - ++chan->UDP_DRVSTATS_mgmt_no_socket; - printk(KERN_INFO "%s: UDP mgmt cmnd, no socket buffers available!\n", card->devname); + } + + return 0; +} + + +/*============================================================================== + * Perform the Interrupt Test by running the READ_CODE_VERSION command MAX_INTR_ + * TEST_COUNTER times. + */ +static int intr_test( sdla_t* card ) +{ + fr_mbox_t* mb = card->mbox; + int err,i; + + /* The critical flag is unset here because we want to get into the + ISR without the flag already set. The If_open sets the flag. + */ + clear_bit(1, (void*)&card->wandev.critical); + + err = fr_set_intr_mode(card, FR_INTR_READY, card->wandev.mtu, 0 ); + + if (err == CMD_OK) { + + for ( i = 0; i < MAX_INTR_TEST_COUNTER; i++ ) { + /* Run command READ_CODE_VERSION */ + mb->cmd.length = 0; + mb->cmd.command = FR_READ_CODE_VERSION; + err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; + if (err != CMD_OK) + fr_event(card, err, mb); } + + } else { + return err; } - kfree(data); + + err = fr_set_intr_mode( card, 0, card->wandev.mtu, 0 ); + + if( err != CMD_OK ) + return err; + + set_bit(1, (void*)&card->wandev.critical); return 0; } /*============================================================================== - * Determine what type of UDP call it is. DRVSTATS or FPIPE8ND ? + * Determine what type of UDP call it is. FPIPE8ND ? */ - -static int udp_pkt_type(struct sk_buff *skb, sdla_t * card) +static int udp_pkt_type( struct sk_buff *skb, sdla_t* card ) { - unsigned char *sendpacket; - unsigned char buf2[5]; - sendpacket = skb->data; - memcpy(&buf2, &card->wandev.udp_port, 2); - if (sendpacket[2] == 0x45 && /* IP packet */ - sendpacket[11] == 0x11 && /* UDP packet */ - sendpacket[24] == buf2[1] && /* UDP Port */ - sendpacket[25] == buf2[0] && - sendpacket[38] == 0x01) - { - if (sendpacket[30] == 0x46 && /* FPIPE8ND: Signature */ - sendpacket[31] == 0x50 && - sendpacket[32] == 0x49 && - sendpacket[33] == 0x50 && - sendpacket[34] == 0x45 && - sendpacket[35] == 0x38 && - sendpacket[36] == 0x4E && - sendpacket[37] == 0x44) - { - return UDP_FPIPE_TYPE; - } else if (sendpacket[30] == 0x44 && /* DRVSTATS: Signature */ - sendpacket[31] == 0x52 && - sendpacket[32] == 0x56 && - sendpacket[33] == 0x53 && - sendpacket[34] == 0x54 && - sendpacket[35] == 0x41 && - sendpacket[36] == 0x54 && - sendpacket[37] == 0x53) - { - return UDP_DRVSTATS_TYPE; - } - else - return UDP_INVALID_TYPE; + fr_udp_pkt_t *fr_udp_pkt = (fr_udp_pkt_t *)skb->data; + + if((fr_udp_pkt->ip_pkt.protocol == UDPMGMT_UDP_PROTOCOL) && + (fr_udp_pkt->ip_pkt.ver_inet_hdr_length == 0x45) && + (fr_udp_pkt->udp_pkt.udp_dst_port == + ntohs(card->wandev.udp_port)) && + (fr_udp_pkt->wp_mgmt.request_reply == + UDPMGMT_REQUEST)) { + if(!strncmp(fr_udp_pkt->wp_mgmt.signature, + UDPMGMT_FPIPE_SIGNATURE, 8)) + return UDP_FPIPE_TYPE; } - else - return UDP_INVALID_TYPE; + + return UDP_INVALID_TYPE; } + + /*============================================================================== * Initializes the Statistics values in the fr_channel structure. */ - -void init_chan_statistics(fr_channel_t * chan) +void init_chan_statistics( fr_channel_t* chan) { - chan->if_send_entry = 0; - chan->if_send_skb_null = 0; - chan->if_send_broadcast = 0; - chan->if_send_multicast = 0; - chan->if_send_critical_ISR = 0; - chan->if_send_critical_non_ISR = 0; - chan->if_send_busy = 0; - chan->if_send_busy_timeout = 0; - chan->if_send_FPIPE_request = 0; - chan->if_send_DRVSTATS_request = 0; - chan->if_send_wan_disconnected = 0; - chan->if_send_dlci_disconnected = 0; - chan->if_send_no_bfrs = 0; - chan->if_send_adptr_bfrs_full = 0; - chan->if_send_bfrs_passed_to_adptr = 0; - chan->rx_intr_no_socket = 0; - chan->rx_intr_dev_not_started = 0; - chan->rx_intr_FPIPE_request = 0; - chan->rx_intr_DRVSTATS_request = 0; - chan->rx_intr_bfr_not_passed_to_stack = 0; - chan->rx_intr_bfr_passed_to_stack = 0; - chan->UDP_FPIPE_mgmt_kmalloc_err = 0; - chan->UDP_FPIPE_mgmt_direction_err = 0; - chan->UDP_FPIPE_mgmt_adptr_type_err = 0; - chan->UDP_FPIPE_mgmt_adptr_cmnd_OK = 0; - chan->UDP_FPIPE_mgmt_adptr_cmnd_timeout = 0; - chan->UDP_FPIPE_mgmt_adptr_send_passed = 0; - chan->UDP_FPIPE_mgmt_adptr_send_failed = 0; - chan->UDP_FPIPE_mgmt_not_passed_to_stack = 0; - chan->UDP_FPIPE_mgmt_passed_to_stack = 0; - chan->UDP_FPIPE_mgmt_no_socket = 0; - chan->UDP_DRVSTATS_mgmt_kmalloc_err = 0; - chan->UDP_DRVSTATS_mgmt_adptr_cmnd_OK = 0; - chan->UDP_DRVSTATS_mgmt_adptr_cmnd_timeout = 0; - chan->UDP_DRVSTATS_mgmt_adptr_send_passed = 0; - chan->UDP_DRVSTATS_mgmt_adptr_send_failed = 0; - chan->UDP_DRVSTATS_mgmt_not_passed_to_stack = 0; - chan->UDP_DRVSTATS_mgmt_passed_to_stack = 0; - chan->UDP_DRVSTATS_mgmt_no_socket = 0; + memset(&chan->drvstats_if_send.if_send_entry, 0, + sizeof(if_send_stat_t)); + memset(&chan->drvstats_rx_intr.rx_intr_no_socket, 0, + sizeof(rx_intr_stat_t)); + memset(&chan->drvstats_gen.UDP_PIPE_mgmt_kmalloc_err, 0, + sizeof(pipe_mgmt_stat_t)); } + /*============================================================================== * Initializes the Statistics values in the Sdla_t structure. */ - -void init_global_statistics(sdla_t * card) +void init_global_statistics( sdla_t* card ) { /* Intialize global statistics for a card */ - card->statistics.isr_entry = 0; - card->statistics.isr_already_critical = 0; - card->statistics.isr_rx = 0; - card->statistics.isr_tx = 0; - card->statistics.isr_intr_test = 0; - card->statistics.isr_spurious = 0; - card->statistics.isr_enable_tx_int = 0; - card->statistics.rx_intr_corrupt_rx_bfr = 0; - card->statistics.rx_intr_on_orphaned_DLCI = 0; - card->statistics.tx_intr_dev_not_started = 0; - card->statistics.poll_entry = 0; - card->statistics.poll_already_critical = 0; - card->statistics.poll_processed = 0; + memset(&card->statistics.isr_entry, 0, sizeof(global_stats_t)); } -static void read_DLCI_IB_mapping(sdla_t * card, fr_channel_t * chan) +static void read_DLCI_IB_mapping( sdla_t* card, fr_channel_t* chan ) { - fr_mbox_t *mbox = card->mbox; - int retry = MAX_CMD_RETRY; - dlci_IB_mapping_t *result; - int err, counter, found; - do - { - memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); + fr_mbox_t* mbox = card->mbox; + int retry = MAX_CMD_RETRY; + dlci_IB_mapping_t* result; + int err, counter, found; + + do { mbox->cmd.command = FR_READ_DLCI_IB_MAPPING; + mbox->cmd.length = 0; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; + } while (err && retry-- && fr_event(card, err, mbox)); + + if( mbox->cmd.result != 0){ + printk(KERN_INFO "%s: Read DLCI IB Mapping failed\n", + chan->name); } - while (err && retry-- && fr_event(card, err, mbox)); - - if (mbox->cmd.result != 0) - printk(KERN_INFO "%s: Read DLCI IB Mapping failed\n", chan->name); counter = mbox->cmd.length / sizeof(dlci_IB_mapping_t); - result = (void *) mbox->data; + result = (void *)mbox->data; + found = 0; - for (; counter; --counter, ++result) - { - if (result->dlci == chan->dlci) - { - printk(KERN_INFO "%s: DLCI= %d, IB addr = %lx for %s\n" - ,card->devname, result->dlci, result->addr_value ,chan->name); + for (; counter; --counter, ++result) { + if ( result->dlci == chan->dlci ) { chan->IB_addr = result->addr_value; - chan->dlci_int_interface = (void *) (card->hw.dpmbase + - (chan->IB_addr & 0x00001FFF)); + if(card->hw.type == SDLA_S514){ + chan->dlci_int_interface = + (void*)(card->hw.dpmbase + + chan->IB_addr); + }else{ + chan->dlci_int_interface = + (void*)(card->hw.dpmbase + + (chan->IB_addr & 0x00001FFF)); + + } found = 1; - break; - } + break; + } } if (!found) - printk(KERN_INFO "%s: DLCI %d not found by IB MAPPING cmd\n", - card->devname, chan->dlci); + printk( KERN_INFO "%s: DLCI %d not found by IB MAPPING cmd\n", + card->devname, chan->dlci); +} + +void s508_s514_lock(sdla_t *card, unsigned long *smp_flags) +{ + + if (card->hw.type != SDLA_S514){ +#ifdef __SMP__ + spin_lock_irqsave(&card->lock, *smp_flags); +#else + disable_irq(card->hw.irq); +#endif + } +#ifdef __SMP__ + else{ + spin_lock(&card->lock); + } +#endif +} + +void s508_s514_unlock(sdla_t *card, unsigned long *smp_flags) +{ + if (card->hw.type != SDLA_S514){ +#ifdef __SMP__ + spin_unlock_irqrestore(&card->lock, *smp_flags); +#else + enable_irq(card->hw.irq); +#endif + } +#ifdef __SMP__ + else{ + spin_unlock(&card->lock); + } +#endif + } /****** End *****************************************************************/ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sdla_ppp.c linux/drivers/net/sdla_ppp.c --- v2.2.13/linux/drivers/net/sdla_ppp.c Mon Jan 4 11:37:29 1999 +++ linux/drivers/net/sdla_ppp.c Tue Jan 4 10:12:17 2000 @@ -1,21 +1,39 @@ /***************************************************************************** * sdla_ppp.c WANPIPE(tm) Multiprotocol WAN Link Driver. PPP module. * -* Author: Jaspreet Singh +* Author: Nenad Corbic * -* Copyright: (c) 1995-1997 Sangoma Technologies Inc. +* Copyright: (c) 1995-1999 Sangoma Technologies Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * ============================================================================ -* Mar 15, 1998 Alan Cox o 2.1.8x basic port. +* +* Oct 25, 1999 Nenad Corbic o Support for 2.0.X kernels +* Moved dynamic route processing into +* a polling routine. +* Oct 07, 1999 Nenad Corbic o Support for S514 PCI card. +* Gideon Hack o UPD and Updates executed using timer interrupt +* Sep 10, 1999 Nenad Corbic o Fixed up the /proc statistics +* Jul 20, 1999 Nenad Corbic o Remove the polling routines and use +* interrupts instead. +* Sep 17, 1998 Jaspreet Singh o Updates for 2.2.X Kernels. +* Aug 13, 1998 Jaspreet Singh o Improved Line Tracing. +* Jun 22, 1998 David Fong o Added remote IP address assignment +* Mar 15, 1998 Alan Cox o 2.1.8x basic port. +* Apr 16, 1998 Jaspreet Singh o using htons() for the IPX protocol. +* Dec 09, 1997 Jaspreet Singh o Added PAP and CHAP. +* o Implemented new routines like +* ppp_set_inbnd_auth(), ppp_set_outbnd_auth(), +* tokenize() and strstrip(). * Nov 27, 1997 Jaspreet Singh o Added protection against enabling of irqs * while they have been disabled. * Nov 24, 1997 Jaspreet Singh o Fixed another RACE condition caused by * disabling and enabling of irqs. -* o Added new counters for stats on disable/enable* IRQs. +* o Added new counters for stats on disable/enable +* IRQs. * Nov 10, 1997 Jaspreet Singh o Initialized 'skb->mac.raw' to 'skb->data' * before every netif_rx(). * o Free up the device structure in del_if(). @@ -56,6 +74,7 @@ * Jan 06, 1997 Gene Kozin Initial version. *****************************************************************************/ +#include #include /* printk(), and other useful stuff */ #include /* offsetof(), etc. */ #include /* return codes */ @@ -65,10 +84,16 @@ #include /* WANPIPE common user API definitions */ #include /* ARPHRD_* defines */ #include /* htons(), etc. */ -#include /* copyto/from user */ -#define _GNUC_ -#include /* PPP firmware API definitions */ +#include /* sockaddr_in */ +#include /* in_aton(), in_ntoa() prototypes */ + +#include +#include +#include +#include +#include /* PPP firmware API definitions */ +#include /* S514 Type Definition */ /****** Defines & Macros ****************************************************/ #ifdef _DEBUG_ @@ -76,130 +101,183 @@ #else #define STATIC static #endif -#define PPP_DFLT_MTU 1500 /* default MTU */ -#define PPP_MAX_MTU 4000 /* maximum MTU */ + +#define PPP_DFLT_MTU 1500 /* default MTU */ +#define PPP_MAX_MTU 4000 /* maximum MTU */ #define PPP_HDR_LEN 1 -#define CONNECT_TIMEOUT (90*HZ) /* link connection timeout */ -#define HOLD_DOWN_TIME (30*HZ) /* link hold down time */ + +#define CONNECT_TIMEOUT (90*HZ) /* link connection timeout */ +#define HOLD_DOWN_TIME (5*HZ) /* link hold down time : Changed from 30 to 5 */ /* For handle_IPXWAN() */ #define CVHexToAscii(b) (((unsigned char)(b) > (unsigned char)9) ? ((unsigned char)'A' + ((unsigned char)(b) - (unsigned char)10)) : ((unsigned char)'0' + (unsigned char)(b))) +/* Macro for enabling/disabling debugging comments */ +//#define NEX_DEBUG +#ifdef NEX_DEBUG +#define NEX_PRINTK(format, a...) printk(format, ## a) +#else +#define NEX_PRINTK(format, a...) +#endif /* NEX_DEBUG */ + +#define DCD(a) ( a & 0x08 ? "HIGH" : "LOW" ) +#define CTS(a) ( a & 0x20 ? "HIGH" : "LOW" ) +#define LCP(a) ( a == 0x09 ? "OPEN" : "CLOSED" ) +#define IP(a) ( a == 0x09 ? "ENABLED" : "DISABLED" ) + +#define TMR_INT_ENABLED_UPDATE 1 +#define TMR_INT_ENABLED_PPP_EVENT 2 +#define TMR_INT_ENABLED_UDP 4 + +/* Set Configuraton Command Definitions */ +#define PERCENT_TX_BUFF 60 +#define TIME_BETWEEN_CONF_REQ 30 +#define TIME_BETWEEN_PAP_CHAP_REQ 30 +#define WAIT_PAP_CHAP_WITHOUT_REPLY 300 +#define WAIT_AFTER_DCD_CTS_LOW 5 +#define TIME_DCD_CTS_LOW_AFTER_LNK_DOWN 10 +#define WAIT_DCD_HIGH_AFTER_ENABLE_COMM 900 +#define MAX_CONF_REQ_WITHOUT_REPLY 10 +#define MAX_TERM_REQ_WITHOUT_REPLY 2 +#define NUM_CONF_NAK_WITHOUT_REPLY 5 +#define NUM_AUTH_REQ_WITHOUT_REPLY 10 + +#define END_OFFSET 0x1F0 +#if LINUX_VERSION_CODE < 0x020125 +#define test_and_set_bit set_bit +#endif + /******Data Structures*****************************************************/ + /* This structure is placed in the private data area of the device structure. * The card structure used to occupy the private area but now the following * structure will incorporate the card structure along with PPP specific data */ - -typedef struct ppp_private_area + +typedef struct ppp_private_area { - sdla_t *card; + sdla_t* card; unsigned long router_start_time; /*router start time in sec */ - unsigned long tick_counter; /*used for 5 second counter */ - unsigned mc; /*multicast support on or off */ + unsigned long tick_counter; /*used for 5 second counter*/ + unsigned mc; /*multicast support on or off*/ + unsigned char enable_IPX; + unsigned long network_number; + unsigned char pap; + unsigned char chap; + unsigned char sysname[31]; /* system name for in-bnd auth*/ + unsigned char userid[511]; /* list of user ids */ + unsigned char passwd[511]; /* list of passwords */ + unsigned protocol; /* SKB Protocol */ + u32 ip_local; /* Local IP Address */ + u32 ip_remote; /* remote IP Address */ + + unsigned char timer_int_enabled; /* Who enabled the timer inter*/ + unsigned char update_comms_stats; /* Used by update function */ + unsigned long curr_trace_addr; /* Trace information */ + unsigned long start_trace_addr; + unsigned long end_trace_addr; + + unsigned short udp_pkt_lgth; + char udp_pkt_src; + char udp_pkt_data[MAX_LGTH_UDP_MGNT_PKT]; + /* PPP specific statistics */ - unsigned long if_send_entry; - unsigned long if_send_skb_null; - unsigned long if_send_broadcast; - unsigned long if_send_multicast; - unsigned long if_send_critical_ISR; - unsigned long if_send_critical_non_ISR; - unsigned long if_send_busy; - unsigned long if_send_busy_timeout; - unsigned long if_send_DRVSTATS_request; - unsigned long if_send_PTPIPE_request; - unsigned long if_send_wan_disconnected; - unsigned long if_send_adptr_bfrs_full; - unsigned long if_send_protocol_error; - unsigned long if_send_tx_int_enabled; - unsigned long if_send_bfr_passed_to_adptr; - unsigned long rx_intr_no_socket; - unsigned long rx_intr_DRVSTATS_request; - unsigned long rx_intr_PTPIPE_request; - unsigned long rx_intr_bfr_not_passed_to_stack; - unsigned long rx_intr_bfr_passed_to_stack; - unsigned long UDP_PTPIPE_mgmt_kmalloc_err; - unsigned long UDP_PTPIPE_mgmt_adptr_type_err; - unsigned long UDP_PTPIPE_mgmt_direction_err; - unsigned long UDP_PTPIPE_mgmt_adptr_cmnd_timeout; - unsigned long UDP_PTPIPE_mgmt_adptr_cmnd_OK; - unsigned long UDP_PTPIPE_mgmt_passed_to_adptr; - unsigned long UDP_PTPIPE_mgmt_passed_to_stack; - unsigned long UDP_PTPIPE_mgmt_no_socket; - unsigned long UDP_DRVSTATS_mgmt_kmalloc_err; - unsigned long UDP_DRVSTATS_mgmt_adptr_type_err; - unsigned long UDP_DRVSTATS_mgmt_direction_err; - unsigned long UDP_DRVSTATS_mgmt_adptr_cmnd_timeout; - unsigned long UDP_DRVSTATS_mgmt_adptr_cmnd_OK; - unsigned long UDP_DRVSTATS_mgmt_passed_to_adptr; - unsigned long UDP_DRVSTATS_mgmt_passed_to_stack; - unsigned long UDP_DRVSTATS_mgmt_no_socket; - unsigned long router_up_time; -} ppp_private_area_t; -/* variable for keeping track of enabling/disabling FT1 monitor status */ + if_send_stat_t if_send_stat; + rx_intr_stat_t rx_intr_stat; + pipe_mgmt_stat_t pipe_mgmt_stat; + unsigned long router_up_time; + +}ppp_private_area_t; + +/* variable for keeping track of enabling/disabling FT1 monitor status */ static int rCount = 0; + extern void disable_irq(unsigned int); extern void enable_irq(unsigned int); /****** Function Prototypes *************************************************/ /* WAN link driver entry points. These are called by the WAN router module. */ -static int update(wan_device_t * wandev); -static int new_if(wan_device_t * wandev, struct device *dev, - wanif_conf_t * conf); -static int del_if(wan_device_t * wandev, struct device *dev); +static int update(wan_device_t *wandev); +static int new_if(wan_device_t *wandev, struct device *dev, wanif_conf_t *conf); +static int del_if(wan_device_t *wandev, struct device *dev); + /* WANPIPE-specific entry points */ -static int wpp_exec(struct sdla *card, void *u_cmd, void *u_data); +static int wpp_exec (struct sdla *card, void *u_cmd, void *u_data); + /* Network device interface */ static int if_init(struct device *dev); static int if_open(struct device *dev); static int if_close(struct device *dev); -static int if_header(struct sk_buff *skb, struct device *dev, - unsigned short type, void *daddr, void *saddr, unsigned len); +static int if_header(struct sk_buff *skb, struct device *dev, unsigned short type, + void *daddr, void *saddr, unsigned len); static int if_rebuild_hdr(struct sk_buff *skb); +static struct net_device_stats *if_stats(struct device *dev); static int if_send(struct sk_buff *skb, struct device *dev); -static struct enet_statistics *if_stats(struct device *dev); + + /* PPP firmware interface functions */ -static int ppp_read_version(sdla_t * card, char *str); -static int ppp_configure(sdla_t * card, void *data); -static int ppp_set_intr_mode(sdla_t * card, unsigned mode); -static int ppp_comm_enable(sdla_t * card); -static int ppp_comm_disable(sdla_t * card); -static int ppp_get_err_stats(sdla_t * card); -static int ppp_send(sdla_t * card, void *data, unsigned len, unsigned proto); -static int ppp_error(sdla_t * card, int err, ppp_mbox_t * mb); -/* Interrupt handlers */ -STATIC void wpp_isr(sdla_t * card); -static void rx_intr(sdla_t * card); -static void tx_intr(sdla_t * card); +static int ppp_read_version(sdla_t *card, char *str); +static int ppp_set_outbnd_auth(sdla_t *card, ppp_private_area_t *ppp_priv_area); +static int ppp_set_inbnd_auth(sdla_t *card, ppp_private_area_t *ppp_priv_area); +static int ppp_configure(sdla_t *card, void *data); +static int ppp_set_intr_mode(sdla_t *card, unsigned char mode); +static int ppp_comm_enable(sdla_t *card); +static int ppp_comm_disable(sdla_t *card); +static int ppp_get_err_stats(sdla_t *card); +static int ppp_send(sdla_t *card, void *data, unsigned len, unsigned proto); +static int ppp_error(sdla_t *card, int err, ppp_mbox_t *mb); + +STATIC void wpp_isr(sdla_t *card); +static void rx_intr(sdla_t *card); +static void event_intr(sdla_t *card); +static void timer_intr(sdla_t *card); + /* Background polling routines */ -static void wpp_poll(sdla_t * card); -static void poll_active(sdla_t * card); -static void poll_connecting(sdla_t * card); -static void poll_disconnected(sdla_t * card); +static void process_route(sdla_t *card); +static void poll_disconnected(sdla_t *card); + /* Miscellaneous functions */ -static int config502(sdla_t * card); -static int config508(sdla_t * card); +static int read_info( sdla_t *card ); +static int read_connection_info (sdla_t *card); +static int remove_route( sdla_t *card ); +static int config508(ppp_private_area_t *ppp_priv_area, sdla_t *card); static void show_disc_cause(sdla_t * card, unsigned cause); -static unsigned char bps_to_speed_code(unsigned long bps); -static int reply_udp(unsigned char *data, unsigned int mbox_len); -static int process_udp_mgmt_pkt(char udp_pkt_src, sdla_t * card, struct sk_buff *skb, struct device *dev, ppp_private_area_t * ppp_priv_area); -static int process_udp_driver_call(char udp_pkt_src, sdla_t * card, struct sk_buff *skb, struct device *dev, ppp_private_area_t * ppp_priv_area); -static void init_ppp_tx_rx_buff(sdla_t * card); -static int intr_test(sdla_t * card); -static int udp_pkt_type(struct sk_buff *skb, sdla_t * card); -static void init_ppp_priv_struct(ppp_private_area_t * ppp_priv_area); -static void init_global_statistics(sdla_t * card); +static int reply_udp( unsigned char *data, unsigned int mbox_len ); +static void process_udp_mgmt_pkt(sdla_t *card, struct device *dev, + ppp_private_area_t *ppp_priv_area); +static void init_ppp_tx_rx_buff( sdla_t *card ); +static int intr_test( sdla_t *card ); +static int udp_pkt_type( struct sk_buff *skb , sdla_t *card); +static void init_ppp_priv_struct( ppp_private_area_t *ppp_priv_area); +static void init_global_statistics( sdla_t *card ); +static int tokenize(char *str, char **tokens); +static char* strstrip(char *str, char *s); +static int chk_bcast_mcast_addr(sdla_t* card, struct device* dev, + struct sk_buff *skb); +static int Read_connection_info; static int Intr_test_counter; -static char TracingEnabled; -static unsigned long curr_trace_addr; -static unsigned long start_trace_addr; static unsigned short available_buffer_space; + /* IPX functions */ -static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming); -static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number, unsigned short proto); +static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, + unsigned char incoming); +static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_PX, + unsigned long network_number, unsigned short proto); + +/* Lock Functions */ +static void s508_lock (sdla_t *card, unsigned long *smp_flags); +static void s508_unlock (sdla_t *card, unsigned long *smp_flags); + +static int store_udp_mgmt_pkt(char udp_pkt_src, sdla_t* card, + struct sk_buff *skb, struct device* dev, + ppp_private_area_t* ppp_priv_area ); +static unsigned short calc_checksum (char *data, int len); + + + /****** Public Functions ****************************************************/ @@ -215,30 +293,40 @@ * Return: 0 o.k. * < 0 failure. */ -int wpp_init(sdla_t * card, wandev_conf_t * conf) +int wpp_init(sdla_t *card, wandev_conf_t *conf) { - union { + union + { char str[80]; } u; + /* Verify configuration ID */ if (conf->config_id != WANCONFIG_PPP) { + printk(KERN_INFO "%s: invalid configuration ID %u!\n", - card->devname, conf->config_id); + card->devname, conf->config_id); return -EINVAL; + } - /* Initialize protocol-specific fields */ - switch (card->hw.fwid) { - case SFID_PPP502: - card->mbox = (void *) (card->hw.dpmbase + PPP502_MB_OFFS); - card->flags = (void *) (card->hw.dpmbase + PPP502_FLG_OFFS); - break; - case SFID_PPP508: - card->mbox = (void *) (card->hw.dpmbase + PPP508_MB_OFFS); - card->flags = (void *) (card->hw.dpmbase + PPP508_FLG_OFFS); - break; - default: - return -EINVAL; + + /* Initialize miscellaneous pointers to structures on the adapter */ + switch (card->hw.type) { + + case SDLA_S508: + card->mbox =(void*)(card->hw.dpmbase + PPP508_MB_OFFS); + card->flags=(void*)(card->hw.dpmbase + PPP508_FLG_OFFS); + break; + + case SDLA_S514: + card->mbox =(void*)(card->hw.dpmbase + PPP514_MB_OFFS); + card->flags=(void*)(card->hw.dpmbase + PPP514_FLG_OFFS); + break; + + default: + return -EINVAL; + } + /* Read firmware version. Note that when adapter initializes, it * clears the mailbox, so it may appear that the first command was * executed successfully when in fact it was merely erased. To work @@ -246,33 +334,36 @@ */ if (ppp_read_version(card, NULL) || ppp_read_version(card, u.str)) return -EIO; - printk(KERN_INFO "%s: running PPP firmware v%s\n", card->devname, u.str); + + printk(KERN_INFO "%s: running PPP firmware v%s\n",card->devname, u.str); /* Adjust configuration and set defaults */ card->wandev.mtu = (conf->mtu) ? - min(conf->mtu, PPP_MAX_MTU) : PPP_DFLT_MTU; - card->wandev.bps = conf->bps; - card->wandev.interface = conf->interface; - card->wandev.clocking = conf->clocking; - card->wandev.station = conf->station; - card->isr = &wpp_isr; - card->poll = &wpp_poll; - card->exec = &wpp_exec; - card->wandev.update = &update; - card->wandev.new_if = &new_if; - card->wandev.del_if = &del_if; - card->wandev.state = WAN_DISCONNECTED; - card->wandev.udp_port = conf->udp_port; - card->wandev.ttl = conf->ttl; + min(conf->mtu, PPP_MAX_MTU) : PPP_DFLT_MTU; + + card->wandev.bps = conf->bps; + card->wandev.interface = conf->interface; + card->wandev.clocking = conf->clocking; + card->wandev.station = conf->station; + card->isr = &wpp_isr; + card->poll = NULL; + card->exec = &wpp_exec; + card->wandev.update = &update; + card->wandev.new_if = &new_if; + card->wandev.del_if = &del_if; + card->wandev.state = WAN_DISCONNECTED; + card->wandev.udp_port = conf->udp_port; + card->wandev.ttl = conf->ttl; card->irq_dis_if_send_count = 0; - card->irq_dis_poll_count = 0; - TracingEnabled = 0; - card->wandev.enable_IPX = conf->enable_IPX; - if (conf->network_number) - card->wandev.network_number = conf->network_number; - else - card->wandev.network_number = 0xDEADBEEF; + card->irq_dis_poll_count = 0; + card->u.p.authenticator = conf->u.ppp.authenticator; + card->u.p.ip_mode = conf->u.ppp.ip_mode ? + conf->u.ppp.ip_mode : WANOPT_PPP_STATIC; + card->TracingEnabled = 0; + Read_connection_info = 1; + /* initialize global statistics */ - init_global_statistics(card); + init_global_statistics( card ); + return 0; } @@ -281,19 +372,43 @@ /*============================================================================ * Update device status & statistics. */ -static int update(wan_device_t * wandev) +static int update(wan_device_t *wandev) { - sdla_t *card; + sdla_t* card = wandev->private; + struct device* dev = card->wandev.dev; + volatile ppp_private_area_t *ppp_priv_area = dev->priv; + ppp_flags_t *flags = card->flags; + unsigned long timeout; + /* sanity checks */ if ((wandev == NULL) || (wandev->private == NULL)) return -EFAULT; + if (wandev->state == WAN_UNCONFIGURED) return -ENODEV; - if (test_and_set_bit(0, (void *) &wandev->critical)) + + //FIXME: Do we need this + if (test_bit(0, (void*)&wandev->critical)) return -EAGAIN; - card = wandev->private; - ppp_get_err_stats(card); - wandev->critical = 0; + + ppp_priv_area->update_comms_stats = 2; + ppp_priv_area->timer_int_enabled |= TMR_INT_ENABLED_UPDATE; + flags->imask |= PPP_INTR_TIMER; + + /* wait a maximum of 1 second for the statistics to be updated */ + timeout = jiffies; + for(;;) { + if(ppp_priv_area->update_comms_stats == 0){ + break; + } + if ((jiffies - timeout) > (1 * HZ)){ + ppp_priv_area->update_comms_stats = 0; + ppp_priv_area->timer_int_enabled &= + ~TMR_INT_ENABLED_UPDATE; + return -EAGAIN; + } + } + return 0; } @@ -309,46 +424,98 @@ * Return: 0 o.k. * < 0 failure (channel will not be created) */ - -static int new_if(wan_device_t * wandev, struct device *dev, wanif_conf_t * conf) +static int new_if(wan_device_t *wandev, struct device *dev, wanif_conf_t *conf) { sdla_t *card = wandev->private; ppp_private_area_t *ppp_priv_area; + if (wandev->ndev) return -EEXIST; + if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)) { + printk(KERN_INFO "%s: invalid interface name!\n", - card->devname); + card->devname); return -EINVAL; + } + /* allocate and initialize private data */ ppp_priv_area = kmalloc(sizeof(ppp_private_area_t), GFP_KERNEL); - if (ppp_priv_area == NULL) - return -ENOMEM; + + if( ppp_priv_area == NULL ) + return -ENOMEM; + memset(ppp_priv_area, 0, sizeof(ppp_private_area_t)); - ppp_priv_area->card = card; + + ppp_priv_area->card = card; + /* initialize data */ strcpy(card->u.p.if_name, conf->name); + /* initialize data in ppp_private_area structure */ - init_ppp_priv_struct(ppp_priv_area); + + init_ppp_priv_struct( ppp_priv_area ); + ppp_priv_area->mc = conf->mc; + ppp_priv_area->pap = conf->pap; + ppp_priv_area->chap = conf->chap; + + /* If no user ids are specified */ + if(!strlen(conf->userid) && (ppp_priv_area->pap||ppp_priv_area->chap)){ + kfree(ppp_priv_area); + return -EINVAL; + } + + /* If no passwords are specified */ + if(!strlen(conf->passwd) && (ppp_priv_area->pap||ppp_priv_area->chap)){ + kfree(ppp_priv_area); + return -EINVAL; + } + + if(strlen(conf->sysname) > 31){ + kfree(ppp_priv_area); + return -EINVAL; + } + + /* If no system name is specified */ + if(!strlen(conf->sysname) && (card->u.p.authenticator)){ + kfree(ppp_priv_area); + return -EINVAL; + } + + /* copy the data into the ppp private structure */ + memcpy(ppp_priv_area->userid, conf->userid, strlen(conf->userid)); + memcpy(ppp_priv_area->passwd, conf->passwd, strlen(conf->passwd)); + memcpy(ppp_priv_area->sysname, conf->sysname, strlen(conf->sysname)); + + + ppp_priv_area->enable_IPX = conf->enable_IPX; + if (conf->network_number) + ppp_priv_area->network_number = conf->network_number; + else + ppp_priv_area->network_number = 0xDEADBEEF; + + /* prepare network device data space for registration */ dev->name = card->u.p.if_name; dev->init = &if_init; dev->priv = ppp_priv_area; + return 0; } /*============================================================================ * Delete logical channel. */ - -static int del_if(wan_device_t * wandev, struct device *dev) +static int del_if(wan_device_t *wandev, struct device *dev) { if (dev->priv) { - kfree(dev->priv); - dev->priv = NULL; - } + + kfree(dev->priv); + dev->priv = NULL; + } + return 0; } @@ -358,26 +525,36 @@ * Execute adapter interface command. */ +//FIXME: Why do we need this ???? static int wpp_exec(struct sdla *card, void *u_cmd, void *u_data) { ppp_mbox_t *mbox = card->mbox; int len; - if(copy_from_user((void *) &mbox->cmd, u_cmd, sizeof(ppp_cmd_t))) + + if (copy_from_user((void*)&mbox->cmd, u_cmd, sizeof(ppp_cmd_t))) return -EFAULT; + len = mbox->cmd.length; + if (len) { - if(copy_from_user((void *) &mbox->data, u_data, len)) + + if( copy_from_user((void*)&mbox->data, u_data, len)) return -EFAULT; + } + /* execute command */ if (!sdla_exec(mbox)) return -EIO; + /* return result */ - if(copy_to_user(u_cmd, (void *) &mbox->cmd, sizeof(ppp_cmd_t))) + if( copy_to_user(u_cmd, (void*)&mbox->cmd, sizeof(ppp_cmd_t))) return -EFAULT; len = mbox->cmd.length; - if (len && u_data && copy_to_user(u_data, (void *) &mbox->data, len)) + + if (len && u_data && copy_to_user(u_data, (void*)&mbox->data, len)) return -EFAULT; + return 0; } @@ -390,32 +567,49 @@ * interface registration. Returning anything but zero will fail interface * registration. */ - static int if_init(struct device *dev) { ppp_private_area_t *ppp_priv_area = dev->priv; sdla_t *card = ppp_priv_area->card; wan_device_t *wandev = &card->wandev; +#ifndef LINUX_2_1 + int i; +#endif /* Initialize device driver entry points */ - dev->open = &if_open; - dev->stop = &if_close; - dev->hard_header = &if_header; - dev->rebuild_header = &if_rebuild_hdr; - dev->hard_start_xmit = &if_send; - dev->get_stats = &if_stats; + dev->open = &if_open; + dev->stop = &if_close; + dev->hard_header = &if_header; + dev->rebuild_header = &if_rebuild_hdr; + dev->hard_start_xmit = &if_send; + dev->get_stats = &if_stats; + + /* Initialize media-specific parameters */ - dev->type = ARPHRD_PPP; /* ARP h/w type */ - dev->mtu = wandev->mtu; - dev->hard_header_len = PPP_HDR_LEN; /* media header length */ + dev->type = ARPHRD_PPP; /* ARP h/w type */ + dev->flags |= IFF_POINTOPOINT; + + /* Enable Mulitcasting if specified by user*/ + if (ppp_priv_area->mc == WANOPT_YES){ + dev->flags |= IFF_MULTICAST; + } + +#ifndef LINUX_2_1 + dev->family = AF_INET; +#endif + dev->mtu = wandev->mtu; + dev->hard_header_len = PPP_HDR_LEN; /* media header length */ + /* Initialize hardware parameters (just for reference) */ - dev->irq = wandev->irq; - dev->dma = wandev->dma; - dev->base_addr = wandev->ioport; - dev->mem_start = (unsigned long)wandev->maddr; - dev->mem_end = dev->mem_start + wandev->msize - 1; - /* Set transmit buffer queue length */ - dev->tx_queue_len = 100; + dev->irq = wandev->irq; + dev->dma = wandev->dma; + dev->base_addr = wandev->ioport; + dev->mem_start = wandev->maddr; + dev->mem_end = wandev->maddr + wandev->msize - 1; + + /* Set transmit buffer queue length */ + dev->tx_queue_len = 100; + /* Initialize socket buffers */ dev_init_buffers(dev); return 0; @@ -428,7 +622,6 @@ * * Return 0 if O.k. or errno. */ - static int if_open(struct device *dev) { ppp_private_area_t *ppp_priv_area = dev->priv; @@ -436,48 +629,100 @@ ppp_flags_t *flags = card->flags; struct timeval tv; int err = 0; + if (dev->start) - return -EBUSY; /* only one open is allowed */ - if (test_and_set_bit(0, (void *) &card->wandev.critical)) + return -EBUSY; /* only one open is allowed */ + + if (test_and_set_bit(0, (void*)&card->wandev.critical)) return -EAGAIN; - if ((card->hw.fwid == SFID_PPP502) ? config502(card) : config508(card)) { - err = -EIO; - card->wandev.critical = 0; - return err; - } - Intr_test_counter = 0; - err = intr_test(card); - if ((err) || (Intr_test_counter != (MAX_INTR_TEST_COUNTER + 1))) { - printk(KERN_INFO "%s: Interrupt Test Failed, Counter: %i\n", - card->devname, Intr_test_counter); - err = -EIO; - card->wandev.critical = 0; - return err; + + if (!card->configured){ + + if (config508(ppp_priv_area, card)){ + + err = -EIO; + card->wandev.critical = 0; + return err; + } + + Intr_test_counter = 0; + err = intr_test( card ); + + if(err || (Intr_test_counter < MAX_INTR_TEST_COUNTER)) { + printk("%s: Interrupt Test Failed, Counter: %i\n", + card->devname, Intr_test_counter); + printk( "%s: Please choose another interrupt\n",card->devname); + err = -EIO; + card->wandev.critical = 0; + return err; + } + + printk(KERN_INFO "%s: Interrupt Test Passed, Counter: %i\n", + card->devname, Intr_test_counter); + card->configured = 1; + } - printk(KERN_INFO "%s: Interrupt Test Passed, Counter: %i\n", - card->devname, Intr_test_counter); + /* Initialize Rx/Tx buffer control fields */ - init_ppp_tx_rx_buff(card); - if (ppp_set_intr_mode(card, 0x03)) { + init_ppp_tx_rx_buff( card ); + + if (ppp_set_intr_mode(card, PPP_INTR_RXRDY| + PPP_INTR_TXRDY| + PPP_INTR_MODEM| + PPP_INTR_CMD | + PPP_INTR_DISC | + PPP_INTR_OPEN | + PPP_INTR_DROP_DTR | + PPP_INTR_TIMER)) { + err = -EIO; card->wandev.critical = 0; return err; + + } + + /* Turn off the transmit and timer interrupt */ + flags->imask &= ~(PPP_INTR_TXRDY | PPP_INTR_TIMER) ; + + /* If you are not the authenticator and any one of the protocol is + * enabled then we call the set_out_bound_authentication. + */ + if ( !card->u.p.authenticator && (ppp_priv_area->pap || ppp_priv_area->chap)) { + if ( ppp_set_outbnd_auth(card, ppp_priv_area) ){ + err = -EIO; + card->wandev.critical = 0; + return err; + } + } + + /* If you are the authenticator and any one of the protocol is enabled + * then we call the set_in_bound_authentication. + */ + if ( card->u.p.authenticator && (ppp_priv_area->pap || ppp_priv_area->chap)) { + if ( ppp_set_inbnd_auth(card, ppp_priv_area) ){ + err = -EIO; + card->wandev.critical = 0; + return err; + } } - flags->imask &= ~0x02; + if (ppp_comm_enable(card)) { err = -EIO; card->wandev.critical = 0; return err; } + + wanpipe_set_state(card, WAN_CONNECTING); wanpipe_open(card); dev->mtu = min(dev->mtu, card->wandev.mtu); dev->interrupt = 0; dev->tbusy = 0; dev->start = 1; - do_gettimeofday(&tv); + do_gettimeofday( &tv ); ppp_priv_area->router_start_time = tv.tv_sec; card->wandev.critical = 0; + return err; } @@ -486,13 +731,14 @@ * o if this is the last open, then disable communications and interrupts. * o reset flags. */ - static int if_close(struct device *dev) { ppp_private_area_t *ppp_priv_area = dev->priv; sdla_t *card = ppp_priv_area->card; - if (test_and_set_bit(0, (void *) &card->wandev.critical)) + + if (test_and_set_bit(0, (void*)&card->wandev.critical)) return -EAGAIN; + dev->start = 0; wanpipe_close(card); wanpipe_set_state(card, WAN_DISCONNECTED); @@ -511,19 +757,21 @@ * * Return: media header length. */ - static int if_header(struct sk_buff *skb, struct device *dev, - unsigned short type, void *daddr, void *saddr, unsigned len) + unsigned short type, void *daddr, void *saddr, unsigned len) { - switch (type) + switch (type) { case ETH_P_IP: + case ETH_P_IPX: - skb->protocol = type; + skb->protocol = htons(type); break; + default: skb->protocol = 0; } + return PPP_HDR_LEN; } @@ -534,13 +782,14 @@ * 0 physical address not resolved */ -static int if_rebuild_hdr(struct sk_buff *skb) +static int if_rebuild_hdr (struct sk_buff *skb) { - struct device *dev=skb->dev; + struct device *dev = skb->dev; ppp_private_area_t *ppp_priv_area = dev->priv; sdla_t *card = ppp_priv_area->card; + printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n", - card->devname, dev->name); + card->devname, dev->name); return 1; } @@ -561,303 +810,355 @@ * 2. Setting tbusy flag will inhibit further transmit requests from the * protocol stack and can be used for flow control with protocol layer. */ - -static int if_send(struct sk_buff *skb, struct device *dev) +static int if_send (struct sk_buff *skb, struct device *dev) { ppp_private_area_t *ppp_priv_area = dev->priv; sdla_t *card = ppp_priv_area->card; unsigned char *sendpacket; - unsigned long check_braddr, check_mcaddr; - unsigned long host_cpu_flags; + unsigned long smp_flags; ppp_flags_t *flags = card->flags; int retry = 0; - int err, udp_type; - ++ppp_priv_area->if_send_entry; + int udp_type; + + + ++ppp_priv_area->if_send_stat.if_send_entry; + if (skb == NULL) { + /* If we get here, some higher layer thinks we've missed an * tx-done interrupt. */ printk(KERN_INFO "%s: interface %s got kicked!\n", - card->devname, dev->name); - ++ppp_priv_area->if_send_skb_null; + card->devname, dev->name); + + ++ppp_priv_area->if_send_stat.if_send_skb_null; + mark_bh(NET_BH); return 0; + } + if (dev->tbusy) { + /* If our device stays busy for at least 5 seconds then we will * kick start the device by making dev->tbusy = 0. We expect * that our device never stays busy more than 5 seconds. So this * is only used as a last resort. */ - ++ppp_priv_area->if_send_busy; - ++card->wandev.stats.collisions; - if ((jiffies - ppp_priv_area->tick_counter) < (5 * HZ)) { + + ++ppp_priv_area->if_send_stat.if_send_tbusy; + ++card->wandev.stats.collisions; + + if ((jiffies - ppp_priv_area->tick_counter) < (5*HZ)) { return 1; } - printk(KERN_INFO "%s: Transmit times out\n", card->devname); - ++ppp_priv_area->if_send_busy_timeout; - /* unbusy the card (because only one interface per card) */ + + printk (KERN_INFO "%s: Transmit times out\n",card->devname); + + ++ppp_priv_area->if_send_stat.if_send_tbusy_timeout; + ++card->wandev.stats.collisions; + + /* unbusy the card (because only one interface per card)*/ dev->tbusy = 0; - } + } sendpacket = skb->data; - udp_type = udp_pkt_type(skb, card); - if (udp_type == UDP_DRVSTATS_TYPE) { - ++ppp_priv_area->if_send_DRVSTATS_request; - process_udp_driver_call(UDP_PKT_FRM_STACK, card, skb, dev, - ppp_priv_area); - dev_kfree_skb(skb); - return 0; - } else if (udp_type == UDP_PTPIPE_TYPE) - ++ppp_priv_area->if_send_PTPIPE_request; - /* retreive source address in two forms: broadcast & multicast */ - check_braddr = sendpacket[15]; - check_mcaddr = sendpacket[12]; - check_braddr = check_braddr << 8; - check_mcaddr = check_mcaddr << 8; - check_braddr |= sendpacket[14]; - check_mcaddr |= sendpacket[13]; - check_braddr = check_braddr << 8; - check_mcaddr = check_mcaddr << 8; - check_braddr |= sendpacket[13]; - check_mcaddr |= sendpacket[14]; - check_braddr = check_braddr << 8; - check_mcaddr = check_mcaddr << 8; - check_braddr |= sendpacket[12]; - check_mcaddr |= sendpacket[15]; - /* if the Source Address is a Multicast address */ - if ((ppp_priv_area->mc == WANOPT_NO) && (check_mcaddr >= 0xE0000001) - && (check_mcaddr <= 0xFFFFFFFE)) { - printk(KERN_INFO "%s: Mutlicast Src. Addr. silently discarded\n" - ,card->devname); - dev_kfree_skb(skb); - ++ppp_priv_area->if_send_multicast; - ++card->wandev.stats.tx_dropped; + + udp_type = udp_pkt_type( skb, card ); + + + if (udp_type == UDP_PTPIPE_TYPE){ + if(store_udp_mgmt_pkt(UDP_PKT_FRM_STACK, card, skb, dev, + ppp_priv_area)){ + flags->imask |= PPP_INTR_TIMER; + } + ++ppp_priv_area->if_send_stat.if_send_PIPE_request; return 0; + } - disable_irq(card->hw.irq); - ++card->irq_dis_if_send_count; - if (test_and_set_bit(0, (void *) &card->wandev.critical)) { - if (card->wandev.critical == CRITICAL_IN_ISR) { - /* If the critical flag is set due to an Interrupt - * then set enable transmit interrupt flag to enable - * transmit interrupt. (delay interrupt) - */ - card->wandev.enable_tx_int = 1; - dev->tbusy = 1; - /* set the counter to see if we get the interrupt in - * 5 seconds. - */ - ppp_priv_area->tick_counter = jiffies; - ++ppp_priv_area->if_send_critical_ISR; - save_flags(host_cpu_flags); - cli(); - if ((!(--card->irq_dis_if_send_count)) && - (!card->irq_dis_poll_count)) - enable_irq(card->hw.irq); - restore_flags(host_cpu_flags); - return 1; + + /* Check for broadcast and multicast addresses + * If found, drop (deallocate) a packet and return. + */ + if(chk_bcast_mcast_addr(card, dev, skb)){ + return 0; + } + + + if(card->hw.type != SDLA_S514){ + s508_lock(card,&smp_flags); + } + + if (test_and_set_bit(0, (void*)&card->wandev.critical)) { + + printk(KERN_INFO "%s: Critical in if_send: %x\n", + card->wandev.name,card->wandev.critical); + dev_kfree_skb(skb); + + ++card->wandev.stats.tx_dropped; + ++ppp_priv_area->if_send_stat.if_send_critical_non_ISR; + + if(card->hw.type != SDLA_S514){ + s508_unlock(card,&smp_flags); } - dev_kfree_skb(skb); - ++ppp_priv_area->if_send_critical_non_ISR; - save_flags(host_cpu_flags); - cli(); - if ((!(--card->irq_dis_if_send_count)) && - (!card->irq_dis_poll_count)) - enable_irq(card->hw.irq); - restore_flags(host_cpu_flags); + return 0; } - if (udp_type == UDP_PTPIPE_TYPE) { - err = process_udp_mgmt_pkt(UDP_PKT_FRM_STACK, card, skb, - dev, ppp_priv_area); - } else if (card->wandev.state != WAN_CONNECTED) { - ++ppp_priv_area->if_send_wan_disconnected; - ++card->wandev.stats.tx_dropped; - } else if (!skb->protocol) { - ++ppp_priv_area->if_send_protocol_error; - ++card->wandev.stats.tx_errors; + + if (card->wandev.state != WAN_CONNECTED) { + + ++ppp_priv_area->if_send_stat.if_send_wan_disconnected; + ++card->wandev.stats.tx_dropped; + + } else if (!skb->protocol) { + ++ppp_priv_area->if_send_stat.if_send_protocol_error; + ++card->wandev.stats.tx_errors; + } else { - /*If it's IPX change the network numbers to 0 if they're ours. */ - if (skb->protocol == ETH_P_IPX) { - if (card->wandev.enable_IPX) { - switch_net_numbers(skb->data, - card->wandev.network_number, 0); + + /*If it's IPX change the network numbers to 0 if they're ours.*/ + if( skb->protocol == htons(ETH_P_IPX) ) { + if(ppp_priv_area->enable_IPX) { + switch_net_numbers( skb->data, + ppp_priv_area->network_number, 0); } else { ++card->wandev.stats.tx_dropped; goto tx_done; } } + if (ppp_send(card, skb->data, skb->len, skb->protocol)) { retry = 1; dev->tbusy = 1; - ++ppp_priv_area->if_send_adptr_bfrs_full; - ++ppp_priv_area->if_send_tx_int_enabled; + ++ppp_priv_area->if_send_stat.if_send_adptr_bfrs_full; + ++ppp_priv_area->if_send_stat.if_send_tx_int_enabled; ppp_priv_area->tick_counter = jiffies; - ++card->wandev.stats.tx_errors; flags->imask |= 0x02; /* unmask Tx interrupts */ } else { - ++ppp_priv_area->if_send_bfr_passed_to_adptr; + ++ppp_priv_area->if_send_stat.if_send_bfr_passed_to_adptr; ++card->wandev.stats.tx_packets; card->wandev.stats.tx_bytes += skb->len; } - } -tx_done: - if (!retry) { + } + +tx_done: + if (!retry){ dev_kfree_skb(skb); } + card->wandev.critical = 0; - save_flags(host_cpu_flags); - cli(); - if ((!(--card->irq_dis_if_send_count)) && (!card->irq_dis_poll_count)) - enable_irq(card->hw.irq); - restore_flags(host_cpu_flags); + + if(card->hw.type != SDLA_S514){ + s508_unlock(card,&smp_flags); + } + + return retry; } + +/*============================================================================= + * Store a UDP management packet for later processing. + */ + +static int store_udp_mgmt_pkt(char udp_pkt_src, sdla_t* card, + struct sk_buff *skb, struct device* dev, + ppp_private_area_t* ppp_priv_area ) +{ + int udp_pkt_stored = 0; + + if(!ppp_priv_area->udp_pkt_lgth && (skb->len<=MAX_LGTH_UDP_MGNT_PKT)){ + ppp_priv_area->udp_pkt_lgth = skb->len; + ppp_priv_area->udp_pkt_src = udp_pkt_src; + memcpy(ppp_priv_area->udp_pkt_data, skb->data, skb->len); + ppp_priv_area->timer_int_enabled |= TMR_INT_ENABLED_UDP; + ppp_priv_area->protocol = skb->protocol; + udp_pkt_stored = 1; + }else{ + if (skb->len > MAX_LGTH_UDP_MGNT_PKT){ + printk(KERN_INFO "%s: PIPEMON UDP request too long : %i\n", + card->devname, skb->len); + }else{ + printk(KERN_INFO "%s: PIPEMON UPD request already pending\n", + card->devname); + } + ppp_priv_area->udp_pkt_lgth = 0; + } + + dev_kfree_skb(skb); + return(udp_pkt_stored); +} + + + /*============================================================================ * Reply to UDP Management system. * Return length of reply. */ - -static int reply_udp(unsigned char *data, unsigned int mbox_len) +static int reply_udp( unsigned char *data, unsigned int mbox_len ) { - unsigned short len, udp_length, temp, i, ip_length; - unsigned long sum; + unsigned short len, udp_length, temp, ip_length; + unsigned long ip_temp; + int even_bound = 0; + ppp_udp_pkt_t *p_udp_pkt = (ppp_udp_pkt_t *)data; + /* Set length of packet */ - len = mbox_len + 60; + len = sizeof(ip_pkt_t)+ + sizeof(udp_pkt_t)+ + sizeof(wp_mgmt_t)+ + sizeof(cblock_t)+ + mbox_len; + /* fill in UDP reply */ - data[36] = 0x02; + p_udp_pkt->wp_mgmt.request_reply = UDPMGMT_REPLY; + /* fill in UDP length */ - udp_length = mbox_len + 40; + udp_length = sizeof(udp_pkt_t)+ + sizeof(wp_mgmt_t)+ + sizeof(cblock_t)+ + mbox_len; + + /* put it on an even boundary */ - if (udp_length & 0x0001) { + if ( udp_length & 0x0001 ) { udp_length += 1; len += 1; - } - temp = (udp_length << 8) | (udp_length >> 8); - memcpy(&data[24], &temp, 2); + even_bound=1; + } + + temp = (udp_length<<8)|(udp_length>>8); + p_udp_pkt->udp_pkt.udp_length = temp; + + /* swap UDP ports */ - memcpy(&temp, &data[20], 2); - memcpy(&data[20], &data[22], 2); - memcpy(&data[22], &temp, 2); + temp = p_udp_pkt->udp_pkt.udp_src_port; + p_udp_pkt->udp_pkt.udp_src_port = + p_udp_pkt->udp_pkt.udp_dst_port; + p_udp_pkt->udp_pkt.udp_dst_port = temp; + + /* add UDP pseudo header */ temp = 0x1100; - memcpy(&data[udp_length + 20], &temp, 2); - temp = (udp_length << 8) | (udp_length >> 8); - memcpy(&data[udp_length + 22], &temp, 2); + *((unsigned short *)(p_udp_pkt->data+mbox_len+even_bound)) = temp; + temp = (udp_length<<8)|(udp_length>>8); + *((unsigned short *)(p_udp_pkt->data+mbox_len+even_bound+2)) = temp; + /* calculate UDP checksum */ - data[26] = data[27] = 0; - sum = 0; - for (i = 0; i < udp_length + 12; i += 2) { - memcpy(&temp, &data[12 + i], 2); - sum += (unsigned long) temp; - } - while (sum >> 16) { - sum = (sum & 0xffffUL) + (sum >> 16); - } - temp = (unsigned short) sum; - temp = ~temp; - if (temp == 0) - temp = 0xffff; - memcpy(&data[26], &temp, 2); + p_udp_pkt->udp_pkt.udp_checksum = 0; + p_udp_pkt->udp_pkt.udp_checksum = + calc_checksum(&data[UDP_OFFSET],udp_length+UDP_OFFSET); + /* fill in IP length */ - ip_length = udp_length + 20; - temp = (ip_length << 8) | (ip_length >> 8); - memcpy(&data[2], &temp, 2); + ip_length = udp_length + sizeof(ip_pkt_t); + temp = (ip_length<<8)|(ip_length>>8); + p_udp_pkt->ip_pkt.total_length = temp; + /* swap IP addresses */ - memcpy(&temp, &data[12], 2); - memcpy(&data[12], &data[16], 2); - memcpy(&data[16], &temp, 2); - memcpy(&temp, &data[14], 2); - memcpy(&data[14], &data[18], 2); - memcpy(&data[18], &temp, 2); + ip_temp = p_udp_pkt->ip_pkt.ip_src_address; + p_udp_pkt->ip_pkt.ip_src_address = p_udp_pkt->ip_pkt.ip_dst_address; + p_udp_pkt->ip_pkt.ip_dst_address = ip_temp; + /* fill in IP checksum */ - data[10] = data[11] = 0; - sum = 0; - for (i = 0; i < 20; i += 2) { - memcpy(&temp, &data[i], 2); - sum += (unsigned long) temp; + p_udp_pkt->ip_pkt.hdr_checksum = 0; + p_udp_pkt->ip_pkt.hdr_checksum = calc_checksum(data,sizeof(ip_pkt_t)); + + return len; + +} /* reply_udp */ + +unsigned short calc_checksum (char *data, int len) +{ + unsigned short temp; + unsigned long sum=0; + int i; + + for( i = 0; i > 16) { + + while (sum >> 16 ) { sum = (sum & 0xffffUL) + (sum >> 16); } - temp = (unsigned short) sum; + + temp = (unsigned short)sum; temp = ~temp; - if (temp == 0) + + if( temp == 0 ) temp = 0xffff; - memcpy(&data[10], &temp, 2); - return len; -} /* reply_udp */ + + return temp; +} /* If incoming is 0 (outgoing)- if the net numbers is ours make it 0 if incoming is 1 - if the net number is 0 make it ours - */ +*/ static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming) { unsigned long pnetwork_number; - pnetwork_number = (unsigned long) ((sendpacket[6] << 24) + - (sendpacket[7] << 16) + (sendpacket[8] << 8) + - sendpacket[9]); + + pnetwork_number = (unsigned long)((sendpacket[6] << 24) + + (sendpacket[7] << 16) + (sendpacket[8] << 8) + + sendpacket[9]); + if (!incoming) { - /* If the destination network number is ours, make it 0 */ - if (pnetwork_number == network_number) { - sendpacket[6] = sendpacket[7] = sendpacket[8] = - sendpacket[9] = 0x00; + //If the destination network number is ours, make it 0 + if( pnetwork_number == network_number) { + sendpacket[6] = sendpacket[7] = sendpacket[8] = + sendpacket[9] = 0x00; } } else { - /* If the incoming network is 0, make it ours */ - if (pnetwork_number == 0) { - sendpacket[6] = (unsigned char) (network_number >> 24); - sendpacket[7] = (unsigned char) ((network_number & - 0x00FF0000) >> 16); - sendpacket[8] = (unsigned char) ((network_number & - 0x0000FF00) >> 8); - sendpacket[9] = (unsigned char) (network_number & - 0x000000FF); + //If the incoming network is 0, make it ours + if( pnetwork_number == 0) { + sendpacket[6] = (unsigned char)(network_number >> 24); + sendpacket[7] = (unsigned char)((network_number & + 0x00FF0000) >> 16); + sendpacket[8] = (unsigned char)((network_number & + 0x0000FF00) >> 8); + sendpacket[9] = (unsigned char)(network_number & + 0x000000FF); } } - pnetwork_number = (unsigned long) ((sendpacket[18] << 24) + - (sendpacket[19] << 16) + (sendpacket[20] << 8) + - sendpacket[21]); - if (!incoming) { - /* If the source network is ours, make it 0 */ - if (pnetwork_number == network_number) { - sendpacket[18] = sendpacket[19] = sendpacket[20] = - sendpacket[21] = 0x00; + + + pnetwork_number = (unsigned long)((sendpacket[18] << 24) + + (sendpacket[19] << 16) + (sendpacket[20] << 8) + + sendpacket[21]); + + if( !incoming ) { + //If the source network is ours, make it 0 + if( pnetwork_number == network_number) { + sendpacket[18] = sendpacket[19] = sendpacket[20] = + sendpacket[21] = 0x00; } } else { - /* If the source network is 0, make it ours */ - if (pnetwork_number == 0) { - sendpacket[18] = (unsigned char) (network_number >> 24); - sendpacket[19] = (unsigned char) ((network_number & - 0x00FF0000) >> 16); - sendpacket[20] = (unsigned char) ((network_number & - 0x0000FF00) >> 8); - sendpacket[21] = (unsigned char) (network_number & - 0x000000FF); + //If the source network is 0, make it ours + if( pnetwork_number == 0 ) { + sendpacket[18] = (unsigned char)(network_number >> 24); + sendpacket[19] = (unsigned char)((network_number & + 0x00FF0000) >> 16); + sendpacket[20] = (unsigned char)((network_number & + 0x0000FF00) >> 8); + sendpacket[21] = (unsigned char)(network_number & + 0x000000FF); } } -} /* switch_net_numbers */ +} /* switch_net_numbers */ /*============================================================================ - * Get Ethernet-style interface statistics. - * Return a pointer to struct enet_statistics. + * Get ethernet-style interface statistics. + * Return a pointer to struct net_device_stats. */ - -static struct enet_statistics *if_stats(struct device *dev) +static struct net_device_stats *if_stats(struct device *dev) { + ppp_private_area_t *ppp_priv_area = dev->priv; - sdla_t *card; + sdla_t* card; - /* - * Device is down:No statistics - */ - - if(ppp_priv_area==NULL) + if( ppp_priv_area == NULL ) return NULL; - + card = ppp_priv_area->card; return &card->wandev.stats; } @@ -868,122 +1169,257 @@ * Read firmware code version. * Put code version as ASCII string in str. */ - -static int ppp_read_version(sdla_t * card, char *str) +static int ppp_read_version(sdla_t *card, char *str) { ppp_mbox_t *mb = card->mbox; int err; + memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); mb->cmd.command = PPP_READ_CODE_VERSION; err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; + if (err != CMD_OK) + ppp_error(card, err, mb); + else if (str) { + int len = mb->cmd.length; + memcpy(str, mb->data, len); str[len] = '\0'; + + } + + return err; +} +/*=========================================================================== + * Set Out-Bound Authentication. +*/ +static int ppp_set_outbnd_auth (sdla_t *card, ppp_private_area_t *ppp_priv_area) +{ + ppp_mbox_t *mb = card->mbox; + int err; + + memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); + memset(&mb->data, 0, (strlen(ppp_priv_area->userid) + + strlen(ppp_priv_area->passwd) + 2 ) ); + memcpy(mb->data, ppp_priv_area->userid, strlen(ppp_priv_area->userid)); + memcpy((mb->data + strlen(ppp_priv_area->userid) + 1), + ppp_priv_area->passwd, strlen(ppp_priv_area->passwd)); + + mb->cmd.length = strlen(ppp_priv_area->userid) + + strlen(ppp_priv_area->passwd) + 2 ; + + mb->cmd.command = PPP_SET_OUTBOUND_AUTH; + + err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; + + if (err != CMD_OK) + ppp_error(card, err, mb); + + return err; +} + +/*=========================================================================== + * Set In-Bound Authentication. +*/ +static int ppp_set_inbnd_auth (sdla_t *card, ppp_private_area_t *ppp_priv_area) +{ + ppp_mbox_t *mb = card->mbox; + int err, i; + char* user_tokens[32]; + char* pass_tokens[32]; + int userids, passwds; + int add_ptr; + + memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); + memset(&mb->data, 0, 1008); + memcpy(mb->data, ppp_priv_area->sysname, + strlen(ppp_priv_area->sysname)); + + /* Parse the userid string and the password string and build a string + to copy it to the data area of the command structure. The string + will look like "SYS_NAMEUSER1PASS1USER2PASS2 + .... " + */ + userids = tokenize( ppp_priv_area->userid, user_tokens); + passwds = tokenize( ppp_priv_area->passwd, pass_tokens); + + if (userids != passwds){ + printk(KERN_INFO "%s: Number of passwords does not equal the number of user ids\n", card->devname); + return 1; + } + + add_ptr = strlen(ppp_priv_area->sysname) + 1; + for (i=0; idata + add_ptr), user_tokens[i], + strlen(user_tokens[i])); + memcpy((mb->data + add_ptr + strlen(user_tokens[i]) + 1), + pass_tokens[i], strlen(pass_tokens[i])); + add_ptr = add_ptr + strlen(user_tokens[i]) + 1 + + strlen(pass_tokens[i]) + 1; } + + mb->cmd.length = add_ptr + 1; + mb->cmd.command = PPP_SET_INBOUND_AUTH; + + err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; + + if (err != CMD_OK) + ppp_error(card, err, mb); + return err; } + +/*============================================================================ + * Tokenize string. + * Parse a string of the following syntax: + * ,,... + * and fill array of tokens with pointers to string elements. + * + */ +static int tokenize (char *str, char **tokens) +{ + int cnt = 0; + + tokens[0] = strtok(str, "/"); + while (tokens[cnt] && (cnt < 32 - 1)) + { + tokens[cnt] = strstrip(tokens[cnt], " \t"); + tokens[++cnt] = strtok(NULL, "/"); + } + return cnt; +} + /*============================================================================ - * Configure PPP firmware. + * Strip leading and trailing spaces off the string str. */ +static char* strstrip (char *str, char* s) +{ + char *eos = str + strlen(str); /* -> end of string */ -static int ppp_configure(sdla_t * card, void *data) + while (*str && strchr(s, *str)) + ++str /* strip leading spaces */ + ; + while ((eos > str) && strchr(s, *(eos - 1))) + --eos /* strip trailing spaces */ + ; + *eos = '\0'; + return str; +} +/*============================================================================ + * Configure PPP firmware. + */ +static int ppp_configure(sdla_t *card, void *data) { ppp_mbox_t *mb = card->mbox; - int data_len = (card->hw.fwid == SFID_PPP502) ? - sizeof(ppp502_conf_t) : sizeof(ppp508_conf_t); + int data_len = sizeof(ppp508_conf_t); int err; + memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); memcpy(mb->data, data, data_len); - mb->cmd.length = data_len; + mb->cmd.length = data_len; mb->cmd.command = PPP_SET_CONFIG; err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; - if (err != CMD_OK) + + if (err != CMD_OK) ppp_error(card, err, mb); + return err; } /*============================================================================ * Set interrupt mode. */ - -static int ppp_set_intr_mode(sdla_t * card, unsigned mode) +static int ppp_set_intr_mode(sdla_t *card, unsigned char mode) { ppp_mbox_t *mb = card->mbox; + ppp_intr_info_t *ppp_intr_data = (ppp_intr_info_t *) &mb->data[0]; int err; + memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); - mb->data[0] = mode; - switch (card->hw.fwid) { - case SFID_PPP502: - mb->cmd.length = 1; - break; - case SFID_PPP508: - default: - mb->data[1] = card->hw.irq; - mb->cmd.length = 2; - } + ppp_intr_data->i_enable = mode; + + ppp_intr_data->irq = card->hw.irq; + mb->cmd.length = 2; + + /* If timer has been enabled, set the timer delay to 1sec */ + if (mode & 0x80){ + ppp_intr_data->timer_len = 5;//100; //250; + mb->cmd.length = 4; + } + mb->cmd.command = PPP_SET_INTR_FLAGS; err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; - if (err != CMD_OK) + + if (err != CMD_OK) ppp_error(card, err, mb); + + return err; } /*============================================================================ * Enable communications. */ - -static int ppp_comm_enable(sdla_t * card) +static int ppp_comm_enable(sdla_t *card) { ppp_mbox_t *mb = card->mbox; int err; + memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); mb->cmd.command = PPP_COMM_ENABLE; err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; - if (err != CMD_OK) + + if (err != CMD_OK) ppp_error(card, err, mb); + return err; } /*============================================================================ * Disable communications. */ - -static int ppp_comm_disable(sdla_t * card) +static int ppp_comm_disable(sdla_t *card) { ppp_mbox_t *mb = card->mbox; int err; + memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); mb->cmd.command = PPP_COMM_DISABLE; err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; - if (err != CMD_OK) + if (err != CMD_OK) ppp_error(card, err, mb); + return err; } /*============================================================================ * Get communications error statistics. */ - -static int ppp_get_err_stats(sdla_t * card) +static int ppp_get_err_stats(sdla_t *card) { ppp_mbox_t *mb = card->mbox; int err; + memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); mb->cmd.command = PPP_READ_ERROR_STATS; err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; + if (err == CMD_OK) { - ppp_err_stats_t *stats = (void *) mb->data; - card->wandev.stats.rx_over_errors = stats->rx_overrun; - card->wandev.stats.rx_crc_errors = stats->rx_bad_crc; - card->wandev.stats.rx_missed_errors = stats->rx_abort; - card->wandev.stats.rx_length_errors = stats->rx_lost; + + ppp_err_stats_t* stats = (void*)mb->data; + card->wandev.stats.rx_over_errors = stats->rx_overrun; + card->wandev.stats.rx_crc_errors = stats->rx_bad_crc; + card->wandev.stats.rx_missed_errors = stats->rx_abort; + card->wandev.stats.rx_length_errors = stats->rx_lost; card->wandev.stats.tx_aborted_errors = stats->tx_abort; - } else + + } else ppp_error(card, err, mb); + return err; } @@ -992,27 +1428,30 @@ * Return: 0 - o.k. * 1 - no transmit buffers available */ - -static int ppp_send(sdla_t * card, void *data, unsigned len, unsigned proto) +static int ppp_send (sdla_t *card, void *data, unsigned len, unsigned proto) { ppp_buf_ctl_t *txbuf = card->u.p.txbuf; - unsigned long addr; + if (txbuf->flag) - return 1 - ; - if (card->hw.fwid == SFID_PPP502) - addr = (txbuf->buf.o_p[1] << 8) + txbuf->buf.o_p[0]; - else - addr = txbuf->buf.ptr; - sdla_poke(&card->hw, addr, data, len); - txbuf->length = len; /* frame length */ - if (proto == ETH_P_IPX) + return 1; + + sdla_poke(&card->hw, txbuf->buf.ptr, data, len); + + txbuf->length = len; /* frame length */ + + if (proto == htons(ETH_P_IPX)) txbuf->proto = 0x01; /* protocol ID */ - txbuf->flag = 1; /* start transmission */ + else + txbuf->proto = 0x00; /* protocol ID */ + + txbuf->flag = 1; /* start transmission */ + /* Update transmit buffer control fields */ card->u.p.txbuf = ++txbuf; - if ((void *) txbuf > card->u.p.txbuf_last) + + if ((void*)txbuf > card->u.p.txbuf_last) card->u.p.txbuf = card->u.p.txbuf_base; + return 0; } @@ -1025,19 +1464,22 @@ * * Return zero if previous command has to be cancelled. */ - -static int ppp_error(sdla_t * card, int err, ppp_mbox_t * mb) +static int ppp_error(sdla_t *card, int err, ppp_mbox_t *mb) { unsigned cmd = mb->cmd.command; + switch (err) { - case CMD_TIMEOUT: - printk(KERN_ERR "%s: command 0x%02X timed out!\n", - card->devname, cmd); - break; - default: - printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n" - ,card->devname, cmd, err); + + case CMD_TIMEOUT: + printk(KERN_ERR "%s: command 0x%02X timed out!\n", + card->devname, cmd); + break; + + default: + printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n" + , card->devname, cmd, err); } + return 0; } @@ -1046,81 +1488,89 @@ /*============================================================================ * PPP interrupt service routine. */ - -STATIC void wpp_isr(sdla_t * card) +STATIC void wpp_isr(sdla_t *card) { ppp_flags_t *flags = card->flags; char *ptr = &flags->iflag; - unsigned long host_cpu_flags; struct device *dev = card->wandev.dev; + + int i; + card->in_isr = 1; + ++card->statistics.isr_entry; - if (test_and_set_bit(0, (void *) &card->wandev.critical)) { - ++card->statistics.isr_already_critical; - printk(KERN_INFO "%s: Critical while in ISR!\n", card->devname); - card->in_isr = 0; - return; + + //FIXME: Do we need this + card->force_enable_irq = 0; + + if(card->hw.type != SDLA_S514){ + if (test_and_set_bit(0, (void*)&card->wandev.critical)) { + + ++card->statistics.isr_already_critical; + printk (KERN_INFO "%s: Critical while in ISR!\n", + card->devname); + card->in_isr = 0; + return; + + } } - /* For all interrupts set the critical flag to CRITICAL_IN_ISR. - * If the if_send routine is called with this flag set it will set - * the enable transmit flag to 1. (for a delayed interrupt) - */ - card->wandev.critical = CRITICAL_IN_ISR; + card->buff_int_mode_unbusy = 0; + switch (flags->iflag) { - case 0x01: /* receive interrupt */ - ++card->statistics.isr_rx; - rx_intr(card); - break; - case 0x02: /* transmit interrupt */ - ++card->statistics.isr_tx; - flags->imask &= ~0x02; - dev->tbusy = 0; - card->buff_int_mode_unbusy = 1; - break; - case 0x08: - ++Intr_test_counter; - ++card->statistics.isr_intr_test; - break; - default: /* unexpected interrupt */ - ++card->statistics.isr_spurious; - printk(KERN_INFO "%s: spurious interrupt 0x%02X!\n", - card->devname, flags->iflag); - printk(KERN_INFO "%s: ID Bytes = ", card->devname); - for (i = 0; i < 8; i++) - printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i)); - printk(KERN_INFO "\n"); - } - /* The critical flag is set to CRITICAL_INTR_HANDLED to let the - * if_send call know that the interrupt is handled so that - * transmit interrupts are not enabled again. - */ - card->wandev.critical = CRITICAL_INTR_HANDLED; - /* If the enable transmit interrupt flag is set then enable transmit - * interrupt on the board. This only goes through if if_send is called - * and the critical flag is set due to an Interrupt. - */ - if (card->wandev.enable_tx_int) { - flags->imask |= 0x02; - card->wandev.enable_tx_int = 0; - ++card->statistics.isr_enable_tx_int; + + case PPP_INTR_RXRDY: /* receive interrupt 0x01 (bit 0)*/ + ++card->statistics.isr_rx; + rx_intr(card); + break; + + case PPP_INTR_TXRDY: /* transmit interrupt 0x02 (bit 1)*/ + ++card->statistics.isr_tx; + flags->imask &= ~PPP_INTR_TXRDY; + dev->tbusy = 0; + card->buff_int_mode_unbusy = 1; + break; + + case PPP_INTR_CMD: /* interface command completed */ + ++Intr_test_counter; + ++card->statistics.isr_intr_test; + break; + + case PPP_INTR_MODEM: /* modem status change (DCD, CTS) 0x04 (bit 2)*/ + case PPP_INTR_DISC: /* Data link disconnected 0x10 (bit 4)*/ + case PPP_INTR_OPEN: /* Data link open 0x20 (bit 5)*/ + case PPP_INTR_DROP_DTR: /* DTR drop timeout expired 0x40 bit 6 */ + event_intr(card); + break; + + case PPP_INTR_TIMER: + timer_intr(card); + break; + + default: /* unexpected interrupt */ + ++card->statistics.isr_spurious; + printk(KERN_INFO "%s: spurious interrupt 0x%02X!\n", + card->devname, flags->iflag); + printk(KERN_INFO "%s: ID Bytes = ",card->devname); + for(i = 0; i < 8; i ++) + printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i)); + printk(KERN_INFO "\n"); } - save_flags(host_cpu_flags); - cli(); + card->in_isr = 0; flags->iflag = 0; card->wandev.critical = 0; - restore_flags(host_cpu_flags); - if (card->buff_int_mode_unbusy) + + if(card->buff_int_mode_unbusy) { mark_bh(NET_BH); + } } /*============================================================================ * Receive interrupt handler. */ - -static void rx_intr(sdla_t * card) +static void rx_intr(sdla_t *card) { ppp_buf_ctl_t *rxbuf = card->rxmb; struct device *dev = card->wandev.dev; @@ -1128,160 +1578,338 @@ struct sk_buff *skb; unsigned len; void *buf; - int i, err; - ppp_flags_t *flags = card->flags; - char *ptr = &flags->iflag; + int i; + ppp_flags_t *flags = card->flags; + char *ptr = &flags->iflag; int udp_type; + + if (rxbuf->flag != 0x01) { - printk(KERN_INFO - "%s: corrupted Rx buffer @ 0x%X, flag = 0x%02X!\n", - card->devname, (unsigned) rxbuf, rxbuf->flag); - printk(KERN_INFO "%s: ID Bytes = ", card->devname); - for (i = 0; i < 8; i++) + + printk(KERN_INFO + "%s: corrupted Rx buffer @ 0x%X, flag = 0x%02X!\n", + card->devname, (unsigned)rxbuf, rxbuf->flag); + + printk(KERN_INFO "%s: ID Bytes = ",card->devname); + + for(i = 0; i < 8; i ++) printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i)); - printk(KERN_INFO "\n"); + printk(KERN_INFO "\n"); + ++card->statistics.rx_intr_corrupt_rx_bfr; return; + } + if (dev && dev->start) { - len = rxbuf->length; + + len = rxbuf->length; ppp_priv_area = dev->priv; + /* Allocate socket buffer */ skb = dev_alloc_skb(len); + if (skb != NULL) { + /* Copy data to the socket buffer */ - if (card->hw.fwid == SFID_PPP502) { - unsigned addr = (rxbuf->buf.o_p[1] << 8) + - rxbuf->buf.o_p[0]; - buf = skb_put(skb, len); - sdla_peek(&card->hw, addr, buf, len); - } else { - unsigned addr = rxbuf->buf.ptr; - if ((addr + len) > card->u.p.rx_top + 1) { - unsigned tmp = card->u.p.rx_top - addr - + 1; - buf = skb_put(skb, tmp); - sdla_peek(&card->hw, addr, buf, tmp); - addr = card->u.p.rx_base; - len -= tmp; - } - buf = skb_put(skb, len); - sdla_peek(&card->hw, addr, buf, len); + unsigned addr = rxbuf->buf.ptr; + + if ((addr + len) > card->u.p.rx_top + 1) { + + unsigned tmp = card->u.p.rx_top - addr + 1; + buf = skb_put(skb, tmp); + sdla_peek(&card->hw, addr, buf, tmp); + addr = card->u.p.rx_base; + len -= tmp; } + buf = skb_put(skb, len); + sdla_peek(&card->hw, addr, buf, len); + /* Decapsulate packet */ - switch (rxbuf->proto) { - case 0x00: - skb->protocol = htons(ETH_P_IP); - break; - case 0x01: - skb->protocol = htons(ETH_P_IPX); - break; + switch (rxbuf->proto) { + + case 0x00: + skb->protocol = htons(ETH_P_IP); + break; + + case 0x01: + skb->protocol = htons(ETH_P_IPX); + break; } - udp_type = udp_pkt_type(skb, card); - if (udp_type == UDP_DRVSTATS_TYPE) { - ++ppp_priv_area->rx_intr_DRVSTATS_request; - process_udp_driver_call( - UDP_PKT_FRM_NETWORK, card, skb, - dev, ppp_priv_area); - dev_kfree_skb(skb); - } else if (udp_type == UDP_PTPIPE_TYPE) { - ++ppp_priv_area->rx_intr_PTPIPE_request; - err = process_udp_mgmt_pkt( - UDP_PKT_FRM_NETWORK, card, - skb, dev, ppp_priv_area); - dev_kfree_skb(skb); - } else if (handle_IPXWAN(skb->data, card->devname, card->wandev.enable_IPX, card->wandev.network_number, skb->protocol)) { - if (card->wandev.enable_IPX) { - ppp_send(card, skb->data, skb->len, ETH_P_IPX); - dev_kfree_skb(skb); + + udp_type = udp_pkt_type( skb, card ); + + if (udp_type == UDP_PTPIPE_TYPE){ + + /* Handle a UDP Request in Timer Interrupt */ + if(store_udp_mgmt_pkt(UDP_PKT_FRM_NETWORK, card, skb, dev, + ppp_priv_area)){ + flags->imask |= PPP_INTR_TIMER; + } + ++ppp_priv_area->rx_intr_stat.rx_intr_PIPE_request; + + + } else if (handle_IPXWAN(skb->data,card->devname, + ppp_priv_area->enable_IPX, + ppp_priv_area->network_number, + skb->protocol)) { + + /* Handle an IPXWAN packet */ + if( ppp_priv_area->enable_IPX) { + ppp_send(card, skb->data, skb->len, htons(ETH_P_IPX)); + dev_kfree_skb(skb); + + } else { ++card->wandev.stats.rx_dropped; } } else { - /* Pass it up the protocol stack */ - skb->dev = dev; - skb->mac.raw = skb->data; + /* Pass data up the protocol stack */ + skb->dev = dev; + skb->mac.raw = skb->data; + + ++card->wandev.stats.rx_packets; + card->wandev.stats.rx_bytes += skb->len; + ++ppp_priv_area->rx_intr_stat.rx_intr_bfr_passed_to_stack; netif_rx(skb); - ++card->wandev.stats.rx_packets; - card->wandev.stats.rx_bytes += skb->len; - ++ppp_priv_area->rx_intr_bfr_passed_to_stack; } + } else { + printk(KERN_INFO "%s: no socket buffers available!\n", - card->devname); + card->devname); ++card->wandev.stats.rx_dropped; - ++ppp_priv_area->rx_intr_no_socket; + ++ppp_priv_area->rx_intr_stat.rx_intr_no_socket; + + dev_kfree_skb(skb); } - } else + + } else { ++card->statistics.rx_intr_dev_not_started; + } + /* Release buffer element and calculate a pointer to the next one */ - rxbuf->flag = (card->hw.fwid == SFID_PPP502) ? 0xFF : 0x00; + rxbuf->flag = 0x00; card->rxmb = ++rxbuf; - if ((void *) rxbuf > card->u.p.rxbuf_last) + if ((void*)rxbuf > card->u.p.rxbuf_last) card->rxmb = card->u.p.rxbuf_base; } -/*============================================================================ - * Transmit interrupt handler. - */ -static void tx_intr(sdla_t * card) +void event_intr (sdla_t *card) { - struct device *dev = card->wandev.dev; - if (!dev || !dev->start) { - ++card->statistics.tx_intr_dev_not_started; - return; + + struct device* dev = card->wandev.dev; + ppp_private_area_t* ppp_priv_area = dev->priv; + volatile ppp_flags_t *flags = card->flags; + + switch (flags->iflag){ + + case PPP_INTR_MODEM: /* modem status change (DCD, CTS) 0x04 (bit 2)*/ + printk (KERN_INFO "%s: Modem status: DCD=%s CTS=%s\n", + card->devname, DCD(flags->mstatus), CTS(flags->mstatus)); + + break; + + case PPP_INTR_DISC: /* Data link disconnected 0x10 (bit 4)*/ + + NEX_PRINTK (KERN_INFO "Data link disconnected intr Cause %X\n", + flags->disc_cause); + + if (flags->disc_cause & + (PPP_LOCAL_TERMINATION | PPP_DCD_CTS_DROP | + PPP_REMOTE_TERMINATION)) { + if (card->u.p.ip_mode == WANOPT_PPP_PEER) { + Read_connection_info = 1; + remove_route (card); + } + wanpipe_set_state(card, WAN_DISCONNECTED); + show_disc_cause(card, flags->disc_cause); + ppp_priv_area->timer_int_enabled |= + TMR_INT_ENABLED_PPP_EVENT; + flags->imask |= PPP_INTR_TIMER; + } + break; + + case PPP_INTR_OPEN: /* Data link open 0x20 (bit 5)*/ + + NEX_PRINTK (KERN_INFO "%s: PPP Link Open, LCP=%s IP=%s\n", + card->devname,LCP(flags->lcp_state), + IP(flags->ip_state)); + + if (flags->lcp_state == 0x09 && + (flags->ip_state == 0x09 || flags->ipx_state == 0x09)){ + /* Initialize the polling timer and set the state + * to WAN_CONNNECTED + */ + card->state_tick = jiffies; + wanpipe_set_state(card, WAN_CONNECTED); + ppp_priv_area->timer_int_enabled |= + TMR_INT_ENABLED_PPP_EVENT; + flags->imask |= PPP_INTR_TIMER; + + } + break; + + case PPP_INTR_DROP_DTR: /* DTR drop timeout expired 0x40 bit 6 */ + + NEX_PRINTK(KERN_INFO "DTR Drop Timeout Interrrupt \n"); + if (card->u.p.ip_mode == WANOPT_PPP_PEER) { + Read_connection_info = 1; + remove_route (card); + } + wanpipe_set_state(card, WAN_DISCONNECTED); + show_disc_cause(card, flags->disc_cause); + ppp_priv_area->timer_int_enabled |= + TMR_INT_ENABLED_PPP_EVENT; + flags->imask |= PPP_INTR_TIMER; + break; + + default: + printk(KERN_INFO "%s: Error, Invalid PPP Event\n",card->devname); } - dev->tbusy = 0; - mark_bh(NET_BH); } + + +/* TIMER INTERRUPT */ + +void timer_intr (sdla_t *card) +{ + + struct device* dev = card->wandev.dev; + ppp_private_area_t* ppp_priv_area = dev->priv; + ppp_flags_t *flags = card->flags; + + /* Update statistics */ + if (ppp_priv_area->timer_int_enabled & TMR_INT_ENABLED_UPDATE){ + ppp_get_err_stats(card); + if(!(--ppp_priv_area->update_comms_stats)){ + ppp_priv_area->timer_int_enabled &= + ~TMR_INT_ENABLED_UPDATE; + } + } + + /* PPIPEMON UDP request */ + + if (ppp_priv_area->timer_int_enabled & TMR_INT_ENABLED_UDP){ + process_udp_mgmt_pkt(card,dev, ppp_priv_area); + ppp_priv_area->timer_int_enabled &= ~TMR_INT_ENABLED_UDP; + } + + + /* PPP Event */ + if (ppp_priv_area->timer_int_enabled & TMR_INT_ENABLED_PPP_EVENT){ + + if (card->wandev.state == WAN_DISCONNECTED){ + poll_disconnected(card); + } + + /* If the state is CONNECTING, it means that communicatins were + * enabled. When the remote side enables its comminication we + * should get an interrupt PPP_INTR_OPEN, thus turn off polling + */ + + else if (card->wandev.state == WAN_CONNECTING){ + /* Turn off the timer interrupt */ + ppp_priv_area->timer_int_enabled &= ~TMR_INT_ENABLED_PPP_EVENT; + } + + /* If state is connected and we are in PEER mode + * poll for an IP address which will be provided by remote end. + */ + else if ((card->wandev.state == WAN_CONNECTED && + card->u.p.ip_mode == WANOPT_PPP_PEER) && + Read_connection_info){ + + card->state_tick = jiffies; + if (!read_connection_info (card)){ + card->poll = &process_route; + } + + }else{ + /* If we are using Static IP,no need to poll for + * an IP address. + */ + NEX_PRINTK(KERN_INFO "Turning off TIMER \n"); + ppp_priv_area->timer_int_enabled &= ~TMR_INT_ENABLED_PPP_EVENT; + } + + }/* End of PPP_EVENT */ + + + /* Only disable the timer interrupt if there are no udp, statistic */ + /* updates or events pending */ + if(!ppp_priv_area->timer_int_enabled) { + flags->imask &= ~PPP_INTR_TIMER; + } +} + + static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number, unsigned short proto) { int i; - if (proto == htons(ETH_P_IPX)) { - /* It's an IPX packet */ - if (!enable_IPX) { - /* Return 1 so we don't pass it up the stack. */ + + if( proto == htons(ETH_P_IPX) ) { + //It's an IPX packet + if(!enable_IPX) { + //Return 1 so we don't pass it up the stack. return 1; } } else { - /* It's not IPX so pass it up the stack. */ + //It's not IPX so pass it up the stack. return 0; } - if (sendpacket[16] == 0x90 && - sendpacket[17] == 0x04) { - /* It's IPXWAN */ - if (sendpacket[2] == 0x02 && - sendpacket[34] == 0x00) { - /* It's a timer request packet */ - printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n", devname); - /* Go through the routing options and answer no to every */ - /* option except Unnumbered RIP/SAP */ - for (i = 41; sendpacket[i] == 0x00; i += 5) { - /* 0x02 is the option for Unnumbered RIP/SAP */ - if (sendpacket[i + 4] != 0x02) { + + if( sendpacket[16] == 0x90 && + sendpacket[17] == 0x04) + { + //It's IPXWAN + + if( sendpacket[2] == 0x02 && + sendpacket[34] == 0x00) + { + //It's a timer request packet + printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n",devname); + + //Go through the routing options and answer no to every + //option except Unnumbered RIP/SAP + for(i = 41; sendpacket[i] == 0x00; i += 5) + { + //0x02 is the option for Unnumbered RIP/SAP + if( sendpacket[i + 4] != 0x02) + { sendpacket[i + 1] = 0; } } - /* Skip over the extended Node ID option */ - if (sendpacket[i] == 0x04) { + + //Skip over the extended Node ID option + if( sendpacket[i] == 0x04 ) + { i += 8; } - /* We also want to turn off all header compression opt. */ - for (; sendpacket[i] == 0x80;) { + + //We also want to turn off all header compression opt. + for(; sendpacket[i] == 0x80 ;) + { sendpacket[i + 1] = 0; i += (sendpacket[i + 2] << 8) + (sendpacket[i + 3]) + 4; } - /* Set the packet type to timer response */ + + //Set the packet type to timer response sendpacket[34] = 0x01; - printk(KERN_INFO "%s: Sending IPXWAN Timer Response\n", devname); - } else if (sendpacket[34] == 0x02) { - /* This is an information request packet */ - printk(KERN_INFO "%s: Received IPXWAN Information Request packet\n", devname); - /* Set the packet type to information response */ + + printk(KERN_INFO "%s: Sending IPXWAN Timer Response\n",devname); + } + else if( sendpacket[34] == 0x02 ) + { + //This is an information request packet + printk(KERN_INFO "%s: Received IPXWAN Information Request packet\n",devname); + + //Set the packet type to information response sendpacket[34] = 0x03; - /* Set the router name */ + + //Set the router name sendpacket[51] = 'P'; sendpacket[52] = 'T'; sendpacket[53] = 'P'; @@ -1290,136 +1918,81 @@ sendpacket[56] = 'E'; sendpacket[57] = '-'; sendpacket[58] = CVHexToAscii(network_number >> 28); - sendpacket[59] = CVHexToAscii((network_number & 0x0F000000) >> 24); - sendpacket[60] = CVHexToAscii((network_number & 0x00F00000) >> 20); - sendpacket[61] = CVHexToAscii((network_number & 0x000F0000) >> 16); - sendpacket[62] = CVHexToAscii((network_number & 0x0000F000) >> 12); - sendpacket[63] = CVHexToAscii((network_number & 0x00000F00) >> 8); - sendpacket[64] = CVHexToAscii((network_number & 0x000000F0) >> 4); + sendpacket[59] = CVHexToAscii((network_number & 0x0F000000)>> 24); + sendpacket[60] = CVHexToAscii((network_number & 0x00F00000)>> 20); + sendpacket[61] = CVHexToAscii((network_number & 0x000F0000)>> 16); + sendpacket[62] = CVHexToAscii((network_number & 0x0000F000)>> 12); + sendpacket[63] = CVHexToAscii((network_number & 0x00000F00)>> 8); + sendpacket[64] = CVHexToAscii((network_number & 0x000000F0)>> 4); sendpacket[65] = CVHexToAscii(network_number & 0x0000000F); - for (i = 66; i < 99; i += 1) + for(i = 66; i < 99; i+= 1) + { sendpacket[i] = 0; - printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n", devname); - } else { - printk(KERN_INFO "%s: Unknown IPXWAN packet!\n", devname); + } + + printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n",devname); + } + else + { + printk(KERN_INFO "%s: Unknown IPXWAN packet!\n",devname); return 0; } - /* Set the WNodeID to our network address */ - sendpacket[35] = (unsigned char) (network_number >> 24); - sendpacket[36] = (unsigned char) ((network_number & 0x00FF0000) >> 16); - sendpacket[37] = (unsigned char) ((network_number & 0x0000FF00) >> 8); - sendpacket[38] = (unsigned char) (network_number & 0x000000FF); + + //Set the WNodeID to our network address + sendpacket[35] = (unsigned char)(network_number >> 24); + sendpacket[36] = (unsigned char)((network_number & 0x00FF0000) >> 16); + sendpacket[37] = (unsigned char)((network_number & 0x0000FF00) >> 8); + sendpacket[38] = (unsigned char)(network_number & 0x000000FF); + return 1; } else { - /* If we get here's its an IPX-data packet, so it'll get passed up the stack. */ - /* switch the network numbers */ - switch_net_numbers(sendpacket, network_number, 1); + //If we get here's its an IPX-data packet, so it'll get passed up the stack. + + //switch the network numbers + switch_net_numbers(sendpacket, network_number, 1); return 0; } } /****** Background Polling Routines ****************************************/ -/*============================================================================ - * Main polling routine. - * This routine is repeatedly called by the WANPIPE 'thread' to allow for - * time-dependent housekeeping work. - * - * Notes: - * 1. This routine may be called on interrupt context with all interrupts - * enabled. Beware! +/* All polling functions are invoked by the TIMER interrupt in the wpp_isr + * routine. */ -static void wpp_poll(sdla_t * card) +/*============================================================================ + * Monitor active link phase. + */ +static void process_route (sdla_t *card) { + ppp_flags_t *flags = card->flags; struct device *dev = card->wandev.dev; - ppp_flags_t *adptr_flags = card->flags; - unsigned long host_cpu_flags; - ++card->statistics.poll_entry; - /* The wpp_poll is called continously by the WANPIPE thread to allow - * for line state housekeeping. However if we are in a connected state - * then we do not need to go through all the checks everytime. When in - * connected state execute wpp_poll once every second. - */ - if (card->wandev.state == WAN_CONNECTED) { - if ((jiffies - card->state_tick) < HZ) - return; - } - disable_irq(card->hw.irq); - ++card->irq_dis_poll_count; - if (test_and_set_bit(0, (void *) &card->wandev.critical)) { - ++card->statistics.poll_already_critical; - printk(KERN_INFO "%s: critical inside wpp_poll\n", - card->devname); - save_flags(host_cpu_flags); - cli(); - if ((!card->irq_dis_if_send_count) && - (!(--card->irq_dis_poll_count))) - enable_irq(card->hw.irq); - restore_flags(host_cpu_flags); - return; - } - ++card->statistics.poll_processed; - if (dev && dev->tbusy && !(adptr_flags->imask & 0x02)) { - ++card->statistics.poll_tbusy_bad_status; - printk(KERN_INFO "%s: Wpp_Poll: tbusy = 0x01, imask = 0x%02X\n" - ,card->devname, adptr_flags->imask); - } - switch (card->wandev.state) { - case WAN_CONNECTED: - card->state_tick = jiffies; - poll_active(card); - break; - case WAN_CONNECTING: - poll_connecting(card); - break; - case WAN_DISCONNECTED: - poll_disconnected(card); - break; - default: - printk(KERN_INFO "%s: Unknown Poll State 0x%02X\n", - card->devname, card->wandev.state); - break; - } - card->wandev.critical = 0; - save_flags(host_cpu_flags); - cli(); - if ((!card->irq_dis_if_send_count) && (!(--card->irq_dis_poll_count))) - enable_irq(card->hw.irq); - restore_flags(host_cpu_flags); -} - -/*============================================================================ - * Monitor active link phase. - */ - -static void poll_active(sdla_t * card) -{ - ppp_flags_t *flags = card->flags; - /* We check the lcp_state to see if we are in DISCONNECTED state. - * We are considered to be connected for lcp states 0x06, 0x07, 0x08 - * and 0x09. - */ - if ((flags->lcp_state <= 0x05) || (flags->disc_cause & 0x03)) { - wanpipe_set_state(card, WAN_DISCONNECTED); - show_disc_cause(card, flags->disc_cause); + struct in_device *in_dev = dev->ip_ptr; + + if (in_dev != NULL ) { + if ((card->u.p.ip_mode == WANOPT_PPP_PEER) && + (Read_connection_info && flags->ip_state == 0x09)){ + + printk(KERN_INFO "%s: IPCP State Opened.\n", card->devname); + if (read_info( card )) { + printk(KERN_INFO + "%s: An error occurred in IP assignment.\n", + card->devname); + } else { + struct in_ifaddr *ifa = in_dev->ifa_list; + printk(KERN_INFO "%s: Assigned Lcl. Addr: %s\n", + card->devname, in_ntoa(ifa->ifa_local)); + printk(KERN_INFO "%s: Assigned Rmt. Addr: %s\n", + card->devname, in_ntoa(ifa->ifa_address)); + } + Read_connection_info = 0; + } + }else{ + printk(KERN_INFO "%s: Error: Null pointer in Poll Active\n", + card->devname); } -} - -/*============================================================================ - * Monitor link establishment phase. - * o if connection timed out, disconnect the link. - */ + card->poll = NULL; -static void poll_connecting(sdla_t * card) -{ - ppp_flags_t *flags = card->flags; - if (flags->lcp_state == 0x09) { - wanpipe_set_state(card, WAN_CONNECTED); - } else if (flags->disc_cause & 0x03) { - wanpipe_set_state(card, WAN_DISCONNECTED); - show_disc_cause(card, flags->disc_cause); - } } /*============================================================================ @@ -1427,794 +2000,796 @@ * o if interface is up and the hold-down timeout has expired, then retry * connection. */ - -static void poll_disconnected(sdla_t * card) +static void poll_disconnected(sdla_t *card) { struct device *dev = card->wandev.dev; + if (dev && dev->start && ((jiffies - card->state_tick) > HOLD_DOWN_TIME)) { + wanpipe_set_state(card, WAN_CONNECTING); - if (ppp_comm_enable(card) == CMD_OK) - init_ppp_tx_rx_buff(card); + + if(ppp_comm_enable(card) == CMD_OK){ + init_ppp_tx_rx_buff( card ); + } + } } /****** Miscellaneous Functions *********************************************/ /*============================================================================ - * Configure S502 adapter. - */ - -static int config502(sdla_t * card) -{ - ppp502_conf_t cfg; - /* Prepare PPP configuration structure */ - memset(&cfg, 0, sizeof(ppp502_conf_t)); - if (card->wandev.clocking) - cfg.line_speed = bps_to_speed_code(card->wandev.bps); - cfg.txbuf_num = 4; - cfg.mtu_local = card->wandev.mtu; - cfg.mtu_remote = card->wandev.mtu; - cfg.restart_tmr = 30; - cfg.auth_rsrt_tmr = 30; - cfg.auth_wait_tmr = 300; - cfg.mdm_fail_tmr = 5; - cfg.dtr_drop_tmr = 1; - cfg.connect_tmout = 0; /* changed it from 900 */ - cfg.conf_retry = 10; - cfg.term_retry = 2; - cfg.fail_retry = 5; - cfg.auth_retry = 10; - cfg.ip_options = 0x80; - cfg.ipx_options = 0xA0; - cfg.conf_flags |= 0x0E; -/* - cfg.ip_local = dev->pa_addr; - cfg.ip_remote = dev->pa_dstaddr; - */ - return ppp_configure(card, &cfg); -} - -/*============================================================================ * Configure S508 adapter. */ - -static int config508(sdla_t * card) +static int config508(ppp_private_area_t *ppp_priv_area, sdla_t *card) { ppp508_conf_t cfg; + struct device *dev = card->wandev.dev; + struct in_device *in_dev = dev->ip_ptr; + /* Prepare PPP configuration structure */ memset(&cfg, 0, sizeof(ppp508_conf_t)); + if (card->wandev.clocking) cfg.line_speed = card->wandev.bps; + if (card->wandev.interface == WANOPT_RS232) - cfg.conf_flags |= 0x0020; - cfg.conf_flags |= 0x300; /*send Configure-Request packets forever */ - cfg.txbuf_percent = 60; /* % of Tx bufs */ - cfg.mtu_local = card->wandev.mtu; - cfg.mtu_remote = card->wandev.mtu; - cfg.restart_tmr = 30; - cfg.auth_rsrt_tmr = 30; - cfg.auth_wait_tmr = 300; - cfg.mdm_fail_tmr = 100; - cfg.dtr_drop_tmr = 1; - cfg.connect_tmout = 0; /* changed it from 900 */ - cfg.conf_retry = 10; - cfg.term_retry = 2; - cfg.fail_retry = 5; - cfg.auth_retry = 10; - cfg.ip_options = 0x80; - cfg.ipx_options = 0xA0; -/* - cfg.ip_local = dev->pa_addr; - cfg.ip_remote = dev->pa_dstaddr; - */ + cfg.conf_flags |= INTERFACE_LEVEL_RS232; + + cfg.conf_flags |= DONT_TERMINATE_LNK_MAX_CONFIG; /*send Configure-Request packets forever*/ + cfg.txbuf_percent = PERCENT_TX_BUFF; /* % of Tx bufs */ + cfg.mtu_local = card->wandev.mtu; + cfg.mtu_remote = card->wandev.mtu; /* Default */ + cfg.restart_tmr = TIME_BETWEEN_CONF_REQ; /* 30 = 3sec */ + cfg.auth_rsrt_tmr = TIME_BETWEEN_PAP_CHAP_REQ; /* 30 = 3sec */ + cfg.auth_wait_tmr = WAIT_PAP_CHAP_WITHOUT_REPLY; /* 300 = 30s */ + cfg.mdm_fail_tmr = WAIT_AFTER_DCD_CTS_LOW; /* 5 = 0.5s */ + cfg.dtr_drop_tmr = TIME_DCD_CTS_LOW_AFTER_LNK_DOWN; /* 10 = 1s */ + cfg.connect_tmout = WAIT_DCD_HIGH_AFTER_ENABLE_COMM; /* 900 = 90s */ + cfg.conf_retry = MAX_CONF_REQ_WITHOUT_REPLY; /* 10 = 1s */ + cfg.term_retry = MAX_TERM_REQ_WITHOUT_REPLY; /* 2 times */ + cfg.fail_retry = NUM_CONF_NAK_WITHOUT_REPLY; /* 5 times */ + cfg.auth_retry = NUM_AUTH_REQ_WITHOUT_REPLY; /* 10 times */ + + + if( !card->u.p.authenticator ) { + printk(KERN_INFO "%s: Device is not configured as an authenticator\n", + card->devname); + cfg.auth_options = NO_AUTHENTICATION; + }else{ + printk(KERN_INFO "%s: Device is configured as an authenticator\n", + card->devname); + cfg.auth_options = INBOUND_AUTH; + } + if( ppp_priv_area->pap == WANOPT_YES){ + cfg.auth_options |=PAP_AUTH; + printk(KERN_INFO "%s: Pap enabled\n", card->devname); + } + if( ppp_priv_area->chap == WANOPT_YES){ + cfg.auth_options |= CHAP_AUTH; + printk(KERN_INFO "%s: Chap enabled\n", card->devname); + } + + + if (ppp_priv_area->enable_IPX == WANOPT_YES){ + cfg.ipx_options = ENABLE_IPX | ROUTING_PROT_DEFAULT; + }else{ + cfg.ipx_options = DISABLE_IPX; + } + + switch (card->u.p.ip_mode) { + + case WANOPT_PPP_STATIC: + + cfg.ip_options = L_AND_R_IP_NO_ASSIG | + ENABLE_IP; + cfg.ip_local = in_dev->ifa_list->ifa_local; + cfg.ip_remote = in_dev->ifa_list->ifa_address; + NEX_PRINTK(KERN_INFO "Local %s Remote %s Name %s\n", + in_ntoa(cfg.ip_local), + in_ntoa(cfg.ip_remote), + dev->name); + break; + + case WANOPT_PPP_PEER: + cfg.ip_options = L_IP_REMOTE_ASSIG | + R_IP_REMOTE_ASSIG | + ENABLE_IP; + cfg.ip_local = 0x00; + cfg.ip_remote = 0x00; + break; + + } return ppp_configure(card, &cfg); } /*============================================================================ * Show disconnection cause. */ - -static void show_disc_cause(sdla_t * card, unsigned cause) +static void show_disc_cause(sdla_t *card, unsigned cause) { - if (cause & 0x0002) - printk(KERN_INFO "%s: link terminated by peer\n", - card->devname); - else if (cause & 0x0004) - printk(KERN_INFO "%s: link terminated by user\n", - card->devname); - else if (cause & 0x0008) + if (cause & 0x0802) + + printk(KERN_INFO "%s: link terminated by peer\n", + card->devname); + + else if (cause & 0x0004) + + printk(KERN_INFO "%s: link terminated by user\n", + card->devname); + + else if (cause & 0x0008) + printk(KERN_INFO "%s: authentication failed\n", card->devname); - else if (cause & 0x0010) - printk(KERN_INFO - "%s: authentication protocol negotiation failed\n", - card->devname); - else if (cause & 0x0020) - printk(KERN_INFO - "%s: peer's request for authentication rejected\n", - card->devname); - else if (cause & 0x0040) - printk(KERN_INFO "%s: MRU option rejected by peer\n", - card->devname); - else if (cause & 0x0080) - printk(KERN_INFO "%s: peer's MRU was too small\n", - card->devname); - else if (cause & 0x0100) - printk(KERN_INFO "%s: failed to negotiate peer's LCP options\n", - card->devname); - else if (cause & 0x0200) - printk(KERN_INFO "%s: failed to negotiate peer's IPCP options\n" - ,card->devname); - else if (cause & 0x0400) + + else if (cause & 0x0010) + + printk(KERN_INFO + "%s: authentication protocol negotiation failed\n", + card->devname); + + else if (cause & 0x0020) + printk(KERN_INFO - "%s: failed to negotiate peer's IPXCP options\n", - card->devname); -} + "%s: peer's request for authentication rejected\n", + card->devname); -/*============================================================================ - * Convert line speed in bps to a number used by S502 code. - */ + else if (cause & 0x0040) + + printk(KERN_INFO "%s: MRU option rejected by peer\n", + card->devname); -static unsigned char bps_to_speed_code(unsigned long bps) -{ - unsigned char number; - if (bps <= 1200) - number = 0x01; - else if (bps <= 2400) - number = 0x02; - else if (bps <= 4800) - number = 0x03; - else if (bps <= 9600) - number = 0x04; - else if (bps <= 19200) - number = 0x05; - else if (bps <= 38400) - number = 0x06; - else if (bps <= 45000) - number = 0x07; - else if (bps <= 56000) - number = 0x08; - else if (bps <= 64000) - number = 0x09; - else if (bps <= 74000) - number = 0x0A; - else if (bps <= 112000) - number = 0x0B; - else if (bps <= 128000) - number = 0x0C; - else - number = 0x0D; - return number; -} + else if (cause & 0x0080) + + printk(KERN_INFO "%s: peer's MRU was too small\n", + card->devname); -/*============================================================================ - * Process UDP call of type DRVSTATS. - */ + else if (cause & 0x0100) -static int process_udp_driver_call(char udp_pkt_src, sdla_t * card, struct sk_buff *skb, struct device *dev, ppp_private_area_t * ppp_priv_area) -{ - unsigned char *sendpacket; - unsigned char buf2[5]; - unsigned char *data; - unsigned char *buf; - unsigned int len; - ppp_mbox_t *mbox = card->mbox; - struct sk_buff *new_skb; - int err; - sendpacket = skb->data; - memcpy(&buf2, &card->wandev.udp_port, 2); - if ((data = kmalloc(2000, GFP_ATOMIC)) == NULL) { - printk(KERN_INFO - "%s: Error allocating memory for UDP DRIVER STATS cmnd0x%02X" - ,card->devname, data[45]); - ++ppp_priv_area->UDP_DRVSTATS_mgmt_kmalloc_err; - return 1; - } - memcpy(data, sendpacket, skb->len); - switch (data[45]) { - /* PPIPE_DRIVER_STATISTICS */ - case 0x26: - *(unsigned long *) &data[60] = - ppp_priv_area->if_send_entry; - *(unsigned long *) &data[64] = - ppp_priv_area->if_send_skb_null; - *(unsigned long *) &data[68] = - ppp_priv_area->if_send_broadcast; - *(unsigned long *) &data[72] = - ppp_priv_area->if_send_multicast; - *(unsigned long *) &data[76] = - ppp_priv_area->if_send_critical_ISR; - *(unsigned long *) &data[80] = - ppp_priv_area->if_send_critical_non_ISR; - *(unsigned long *) &data[84] = - ppp_priv_area->if_send_busy; - *(unsigned long *) &data[88] = - ppp_priv_area->if_send_busy_timeout; - *(unsigned long *) &data[92] = - ppp_priv_area->if_send_DRVSTATS_request; - *(unsigned long *) &data[96] = - ppp_priv_area->if_send_PTPIPE_request; - *(unsigned long *) &data[100] = - ppp_priv_area->if_send_wan_disconnected; - *(unsigned long *) &data[104] = - ppp_priv_area->if_send_adptr_bfrs_full; - *(unsigned long *) &data[108] = - ppp_priv_area->if_send_protocol_error; - *(unsigned long *) &data[112] = - ppp_priv_area->if_send_tx_int_enabled; - *(unsigned long *) &data[116] = - ppp_priv_area->if_send_bfr_passed_to_adptr; - *(unsigned long *) &data[118] = - card->irq_dis_if_send_count; - mbox->cmd.length = 62; - break; - case 0x27: - *(unsigned long *) &data[60] = card->statistics.isr_entry; - *(unsigned long *) &data[64] = - card->statistics.isr_already_critical; - *(unsigned long *) &data[68] = card->statistics.isr_rx; - *(unsigned long *) &data[72] = card->statistics.isr_tx; - *(unsigned long *) &data[76] = - card->statistics.isr_intr_test; - *(unsigned long *) &data[80] = - card->statistics.isr_spurious; - *(unsigned long *) &data[84] = - card->statistics.isr_enable_tx_int; - *(unsigned long *) &data[88] = - card->statistics.rx_intr_corrupt_rx_bfr; - *(unsigned long *) &data[92] = - ppp_priv_area->rx_intr_no_socket; - *(unsigned long *) &data[96] = - ppp_priv_area->rx_intr_DRVSTATS_request; - *(unsigned long *) &data[100] = - ppp_priv_area->rx_intr_PTPIPE_request; - *(unsigned long *) &data[104] = - ppp_priv_area->rx_intr_bfr_passed_to_stack; - *(unsigned long *) &data[108] = - card->statistics.rx_intr_dev_not_started; - *(unsigned long *) &data[112] = - card->statistics.tx_intr_dev_not_started; - mbox->cmd.length = 56; - break; - case 0x28: - *(unsigned long *) &data[60] = - ppp_priv_area->UDP_PTPIPE_mgmt_kmalloc_err; - *(unsigned long *) &data[64] = - ppp_priv_area->UDP_PTPIPE_mgmt_adptr_type_err; - *(unsigned long *) &data[68] = - ppp_priv_area->UDP_PTPIPE_mgmt_direction_err; - *(unsigned long *) &data[72] = - ppp_priv_area-> - UDP_PTPIPE_mgmt_adptr_cmnd_timeout; - *(unsigned long *) &data[76] = - ppp_priv_area->UDP_PTPIPE_mgmt_adptr_cmnd_OK; - *(unsigned long *) &data[80] = - ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_adptr; - *(unsigned long *) &data[84] = - ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_stack; - *(unsigned long *) &data[88] = - ppp_priv_area->UDP_PTPIPE_mgmt_no_socket; - *(unsigned long *) &data[92] = - ppp_priv_area->UDP_DRVSTATS_mgmt_kmalloc_err; - *(unsigned long *) &data[96] = - ppp_priv_area-> - UDP_DRVSTATS_mgmt_adptr_cmnd_timeout; - *(unsigned long *) &data[100] = - ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_cmnd_OK; - *(unsigned long *) &data[104] = - ppp_priv_area-> - UDP_DRVSTATS_mgmt_passed_to_adptr; - *(unsigned long *) &data[108] = - ppp_priv_area-> - UDP_DRVSTATS_mgmt_passed_to_stack; - *(unsigned long *) &data[112] = - ppp_priv_area->UDP_DRVSTATS_mgmt_no_socket; - *(unsigned long *) &data[116] = - card->statistics.poll_entry; - *(unsigned long *) &data[120] = - card->statistics.poll_already_critical; - *(unsigned long *) &data[124] = - card->statistics.poll_processed; - *(unsigned long *) &data[126] = - card->irq_dis_poll_count; - mbox->cmd.length = 70; - break; - default: - /* it's a board command */ - memcpy(&mbox->cmd, &sendpacket[45], sizeof(ppp_cmd_t)); - if (mbox->cmd.length) { - memcpy(&mbox->data, &sendpacket[60], - mbox->cmd.length); - } - /* run the command on the board */ - err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - if (err != CMD_OK) { - ppp_error(card, err, mbox); - ++ppp_priv_area-> - UDP_DRVSTATS_mgmt_adptr_cmnd_timeout; - break; - } - ++ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_cmnd_OK; - /* copy the result back to our buffer */ - memcpy(data, sendpacket, skb->len); - memcpy(&data[45], &mbox->cmd, sizeof(ppp_cmd_t)); - if (mbox->cmd.length) { - memcpy(&data[60], &mbox->data, mbox->cmd.length); - } - } - /* Fill UDP TTL */ - data[8] = card->wandev.ttl; - len = reply_udp(data, mbox->cmd.length); - if (udp_pkt_src == UDP_PKT_FRM_NETWORK) { - ++ppp_priv_area->UDP_DRVSTATS_mgmt_passed_to_adptr; - ppp_send(card, data, len, skb->protocol); - } else { - /* Pass it up the stack - Allocate socket buffer */ - if ((new_skb = dev_alloc_skb(len)) != NULL) { - /* copy data into new_skb */ - buf = skb_put(new_skb, len); - memcpy(buf, data, len); - ++ppp_priv_area->UDP_DRVSTATS_mgmt_passed_to_stack; - /* Decapsulate packet and pass it up the protocol - stack */ - new_skb->protocol = htons(ETH_P_IP); - new_skb->dev = dev; - new_skb->mac.raw = new_skb->data; - netif_rx(new_skb); - } else { - ++ppp_priv_area->UDP_DRVSTATS_mgmt_no_socket; - printk(KERN_INFO "no socket buffers available!\n"); - } - } - kfree(data); - return 0; + printk(KERN_INFO "%s: failed to negotiate peer's LCP options\n", + card->devname); + + else if (cause & 0x0200) + + printk(KERN_INFO "%s: failed to negotiate peer's IPCP options\n" + , card->devname); + + else if (cause & 0x0400) + + printk(KERN_INFO + "%s: failed to negotiate peer's IPXCP options\n", + card->devname); } /*============================================================================= * Process UDP call of type PTPIPEAB. */ - -static int process_udp_mgmt_pkt(char udp_pkt_src, sdla_t * card, - struct sk_buff *skb, struct device *dev, - ppp_private_area_t * ppp_priv_area) +static void process_udp_mgmt_pkt(sdla_t *card, struct device *dev, + ppp_private_area_t *ppp_priv_area ) { - unsigned char *sendpacket; unsigned char buf2[5]; - unsigned char *data; unsigned char *buf; unsigned int frames, len; struct sk_buff *new_skb; - unsigned short buffer_length, real_len; + unsigned short data_length, buffer_length, real_len; unsigned long data_ptr; int udp_mgmt_req_valid = 1; ppp_mbox_t *mbox = card->mbox; struct timeval tv; int err; - sendpacket = skb->data; - memcpy(&buf2, &card->wandev.udp_port, 2); - if ((data = kmalloc(2000, GFP_ATOMIC)) == NULL) { - printk(KERN_INFO - "%s: Error allocating memory for UDP management cmnd0x%02X" - ,card->devname, data[45]); - ++ppp_priv_area->UDP_PTPIPE_mgmt_kmalloc_err; - return 1; - } - memcpy(data, sendpacket, skb->len); - switch (data[45]) { + ppp_udp_pkt_t *ppp_udp_pkt = (ppp_udp_pkt_t*)&ppp_priv_area->udp_pkt_data; + + memcpy(&buf2, &card->wandev.udp_port, 2 ); + + + switch(ppp_udp_pkt->cblock.command) { + /* FT1 MONITOR STATUS */ - case 0x80: - if (card->hw.fwid != SFID_PPP508) { - ++ppp_priv_area->UDP_PTPIPE_mgmt_adptr_type_err; - udp_mgmt_req_valid = 0; - break; - } + case FT1_MONITOR_STATUS_CTRL: + /* PPIPE_ENABLE_TRACING */ - case 0x20: + case PPIPE_ENABLE_TRACING: + /* PPIPE_DISABLE_TRACING */ - case 0x21: + case PPIPE_DISABLE_TRACING: + /* PPIPE_GET_TRACE_INFO */ - case 0x22: + case PPIPE_GET_TRACE_INFO: + /* SET FT1 MODE */ - case 0x81: - if (udp_pkt_src == UDP_PKT_FRM_NETWORK) { - ++ppp_priv_area->UDP_PTPIPE_mgmt_direction_err; - udp_mgmt_req_valid = 0; - } - break; - default: - break; - } - if (!udp_mgmt_req_valid) { + case SET_FT1_MODE: + if(ppp_priv_area->udp_pkt_src == UDP_PKT_FRM_NETWORK) { + + ++ppp_priv_area->pipe_mgmt_stat. + UDP_PIPE_mgmt_direction_err; + udp_mgmt_req_valid = 0; + } + break; + + default: + break; + } + + if(!udp_mgmt_req_valid) { + /* set length to 0 */ - data[46] = data[47] = 0; - /* set return code */ - data[48] = 0xCD; - } else { - switch (data[45]) { - /* PPIPE_ENABLE_TRACING */ - case 0x20: - if (!TracingEnabled) { + ppp_udp_pkt->cblock.length = 0x00; + + /* set return code */ + ppp_udp_pkt->cblock.result = 0xCD; + + } else { + /* Initialize the trace element */ + trace_element_t trace_element; + + switch (ppp_udp_pkt->cblock.command){ + + /* PPIPE_ENABLE_TRACING */ + case PPIPE_ENABLE_TRACING: + if (!card->TracingEnabled) { + /* OPERATE_DATALINE_MONITOR */ - mbox->cmd.command = 0x33; - mbox->cmd.length = 1; - mbox->data[0] = 0x03; - err = sdla_exec(mbox) ? - mbox->cmd.result : CMD_TIMEOUT; - if (err != CMD_OK) { + mbox->cmd.command = PPP_DATALINE_MONITOR; + mbox->cmd.length = 0x01; + mbox->data[0] = ppp_udp_pkt->data[0]; + err = sdla_exec(mbox) ? + mbox->cmd.result : CMD_TIMEOUT; + + if (err != CMD_OK) { + ppp_error(card, err, mbox); - TracingEnabled = 0; + card->TracingEnabled = 0; + /* set the return code */ - data[48] = mbox->cmd.result; - mbox->cmd.length = 0; - break; - } - if (card->hw.fwid == SFID_PPP502) { - sdla_peek(&card->hw, 0x9000, &buf2, 2); - } else { - sdla_peek(&card->hw, 0xC000, &buf2, 2); - } - curr_trace_addr = 0; - memcpy(&curr_trace_addr, &buf2, 2); - start_trace_addr = curr_trace_addr; - /* MAX_SEND_BUFFER_SIZE -sizeof(UDP_MGMT_PACKET) - - 41 */ - available_buffer_space = 1926; - } - data[48] = 0; - mbox->cmd.length = 0; - TracingEnabled = 1; - break; - /* PPIPE_DISABLE_TRACING */ - case 0x21: - if (TracingEnabled) { + + ppp_udp_pkt->cblock.result = mbox->cmd.result; + mbox->cmd.length = 0; + break; + } + + sdla_peek(&card->hw, 0xC000, &buf2, 2); + + ppp_priv_area->curr_trace_addr = 0; + memcpy(&ppp_priv_area->curr_trace_addr, &buf2, 2); + ppp_priv_area->start_trace_addr = + ppp_priv_area->curr_trace_addr; + ppp_priv_area->end_trace_addr = + ppp_priv_area->start_trace_addr + END_OFFSET; + + /* MAX_SEND_BUFFER_SIZE - 28 (IP header) + - 32 (ppipemon CBLOCK) */ + available_buffer_space = MAX_LGTH_UDP_MGNT_PKT - + sizeof(ip_pkt_t)- + sizeof(udp_pkt_t)- + sizeof(wp_mgmt_t)- + sizeof(cblock_t); + } + ppp_udp_pkt->cblock.result = 0; + mbox->cmd.length = 0; + card->TracingEnabled = 1; + break; + + /* PPIPE_DISABLE_TRACING */ + case PPIPE_DISABLE_TRACING: + + if(card->TracingEnabled) { + /* OPERATE_DATALINE_MONITOR */ - mbox->cmd.command = 0x3; - mbox->cmd.length = 1; - mbox->data[0] = 0x00; - err = sdla_exec(mbox) ? - mbox->cmd.result : CMD_TIMEOUT; - } - /*set return code */ - data[48] = 0; + mbox->cmd.command = 0x33; + mbox->cmd.length = 1; + mbox->data[0] = 0x00; + err = sdla_exec(mbox) ? + mbox->cmd.result : CMD_TIMEOUT; + + } + + /*set return code*/ + ppp_udp_pkt->cblock.result = 0; mbox->cmd.length = 0; - TracingEnabled = 0; + card->TracingEnabled = 0; break; - /* PPIPE_GET_TRACE_INFO */ - case 0x22: - if (TracingEnabled) { - buffer_length = 0; - /* frames < NUM_TRACE_FRAMES */ - for (frames = 0; frames < 62; frames += 1) { - sdla_peek(&card->hw, curr_trace_addr, - &buf2, 1); - /* no data on board so exit */ - if (buf2[0] == 0x00) - break; - /*1+sizeof(FRAME_DATA) = 9 */ - if ((available_buffer_space - - buffer_length) < 9) { - /*indicate we have more frames - on board and exit */ - data[60] |= 0x02; + + /* PPIPE_GET_TRACE_INFO */ + case PPIPE_GET_TRACE_INFO: + + if(!card->TracingEnabled) { + /* set return code */ + ppp_udp_pkt->cblock.result = 1; + mbox->cmd.length = 0; + } + + buffer_length = 0; + + /* frames < 62, where 62 is the number of trace + information elements. There is in total 496 + bytes of space and each trace information + element is 8 bytes. + */ + for ( frames=0; frames<62; frames++) { + + trace_pkt_t *trace_pkt = (trace_pkt_t *) + &ppp_udp_pkt->data[buffer_length]; + + /* Read the whole trace packet */ + sdla_peek(&card->hw, ppp_priv_area->curr_trace_addr, + &trace_element, sizeof(trace_element_t)); + + /* no data on board so exit */ + if( trace_element.opp_flag == 0x00 ) + break; + + data_ptr = trace_element.trace_data_ptr; + + /* See if there is actual data on the trace buffer */ + if (data_ptr){ + data_length = trace_element.trace_length; + }else{ + data_length = 0; + ppp_udp_pkt->data[0] |= 0x02; + } + + //FIXME: Do we need this check + if ((available_buffer_space - buffer_length) + < (sizeof(trace_element_t)+1)){ + + /*indicate we have more frames + * on board and exit + */ + ppp_udp_pkt->data[0] |= 0x02; + break; + } + + trace_pkt->status = trace_element.trace_type; + trace_pkt->time_stamp = trace_element.trace_time_stamp; + trace_pkt->real_length = trace_element.trace_length; + + real_len = trace_element.trace_length; + + if(data_ptr == 0){ + trace_pkt->data_avail = 0x00; + }else{ + /* we can take it next time */ + if ((available_buffer_space - buffer_length)< + (real_len + sizeof(trace_pkt_t))){ + + ppp_udp_pkt->data[0] |= 0x02; break; - } - /* get frame status */ - sdla_peek(&card->hw, curr_trace_addr + - 0x01, &data[60 + buffer_length], 1); - /* get time stamp */ - sdla_peek(&card->hw, curr_trace_addr + - 0x06, &data[64 + buffer_length], 2); - /* get frame length */ - sdla_peek(&card->hw, curr_trace_addr + - 0x02, &data[62 + buffer_length], 2); - /* get pointer to real data */ - sdla_peek(&card->hw, curr_trace_addr + - 0x04, &buf2, 2); - data_ptr = 0; - memcpy(&data_ptr, &buf2, 2); - /* see if we can fit the frame into the - user buffer */ - memcpy(&real_len, - &data[62 + buffer_length], 2); - if ((data_ptr == 0) || - ((real_len + 8) > - available_buffer_space)) { - data[61 + buffer_length] = 0x00; - } else { - /* we can take it next time */ - if ((available_buffer_space - - buffer_length) < - (real_len + 8)) { - data[60] |= 0x02; - break; - } - /* ok, get the frame */ - data[61 + buffer_length] = 0x01; - /* get the data */ - sdla_peek(&card->hw, data_ptr, - &data[66 + buffer_length], - real_len); - /* zero the opp flag to - show we got the frame */ - buf2[0] = 0x00; - sdla_poke(&card->hw, - curr_trace_addr, &buf2, 1); - /* now move onto the next - frame */ - curr_trace_addr += 8; - /* check if we passed the last - address */ - if (curr_trace_addr >= - start_trace_addr + 0x1F0) { - curr_trace_addr = - start_trace_addr; - } - /* update buffer length and make sure its even */ - if (data[61 + buffer_length] - == 0x01) { - buffer_length += - real_len - 1; - } - /* for the header */ - buffer_length += 8; - if (buffer_length & 0x0001) - buffer_length += 1; - } + } + trace_pkt->data_avail = 0x01; + + /* get the data */ + sdla_peek(&card->hw, data_ptr, + &trace_pkt->data, + real_len); + } + /* zero the opp flag to + show we got the frame */ + buf2[0] = 0x00; + sdla_poke(&card->hw, ppp_priv_area->curr_trace_addr, + &buf2, 1); + + /* now move onto the next + frame */ + ppp_priv_area->curr_trace_addr += 8; + + /* check if we passed the last address */ + if ( ppp_priv_area->curr_trace_addr >= + ppp_priv_area->end_trace_addr){ + + ppp_priv_area->curr_trace_addr = + ppp_priv_area->start_trace_addr; } - /* ok now set the total number of frames passed - in the high 5 bits */ - data[60] = (frames << 2) | data[60]; - /* set the data length */ - mbox->cmd.length = buffer_length; - memcpy(&data[46], &buffer_length, 2); - /* set return code */ - data[48] = 0; - } else { - /* set return code */ - data[48] = 1; - mbox->cmd.length = 0; + + /* update buffer length and make sure its even */ + + if ( trace_pkt->data_avail == 0x01 ) { + buffer_length += real_len - 1; + } + + /* for the header */ + buffer_length += 8; + + if( buffer_length & 0x0001 ) + buffer_length += 1; } - break; - /* PPIPE_GET_IBA_DATA */ - case 0x23: + + /* ok now set the total number of frames passed + in the high 5 bits */ + ppp_udp_pkt->data[0] |= (frames << 2); + + /* set the data length */ + mbox->cmd.length = buffer_length; + ppp_udp_pkt->cblock.length = buffer_length; + + /* set return code */ + ppp_udp_pkt->cblock.result = 0; + break; + + /* PPIPE_GET_IBA_DATA */ + case PPIPE_GET_IBA_DATA: + mbox->cmd.length = 0x09; - if (card->hw.fwid == SFID_PPP502) { - sdla_peek(&card->hw, 0xA003, &data[60], - mbox->cmd.length); - } else { - sdla_peek(&card->hw, 0xF003, &data[60], - mbox->cmd.length); - } + + sdla_peek(&card->hw, 0xF003, &ppp_udp_pkt->data, + mbox->cmd.length); + /* set the length of the data */ - data[46] = 0x09; + ppp_udp_pkt->cblock.length = 0x09; + /* set return code */ - data[48] = 0x00; + ppp_udp_pkt->cblock.result = 0x00; + break; - /* PPIPE_KILL_BOARD */ - case 0x24: + + /* PPIPE_KILL_BOARD */ + case PPIPE_KILL_BOARD: break; - /* PPIPE_FT1_READ_STATUS */ - case 0x25: - sdla_peek(&card->hw, 0xF020, &data[60], 2); - data[46] = 2; - data[47] = 0; - data[48] = 0; + + /* PPIPE_FT1_READ_STATUS */ + case PPIPE_FT1_READ_STATUS: + sdla_peek(&card->hw, 0xF020, &ppp_udp_pkt->data, 2); + ppp_udp_pkt->cblock.length = 2; + ppp_udp_pkt->cblock.result = 0; mbox->cmd.length = 2; break; - case 0x29: - init_ppp_priv_struct(ppp_priv_area); - init_global_statistics(card); + + case PPIPE_FLUSH_DRIVER_STATS: + init_ppp_priv_struct( ppp_priv_area ); + init_global_statistics( card ); mbox->cmd.length = 0; break; - case 0x30: - do_gettimeofday(&tv); - ppp_priv_area->router_up_time = tv.tv_sec - - ppp_priv_area->router_start_time; - *(unsigned long *) &data[60] = - ppp_priv_area->router_up_time; + + case PPIPE_ROUTER_UP_TIME: + + do_gettimeofday( &tv ); + ppp_priv_area->router_up_time = tv.tv_sec - + ppp_priv_area->router_start_time; + *(unsigned long *)&ppp_udp_pkt->data = ppp_priv_area->router_up_time; mbox->cmd.length = 4; break; - /* FT1 MONITOR STATUS */ - case 0x80: + + /* FT1 MONITOR STATUS */ + case FT1_MONITOR_STATUS_CTRL: + /* Enable FT1 MONITOR STATUS */ - if (data[60] == 1) { - if (rCount++ != 0) { - data[48] = 0; - mbox->cmd.length = 1; - break; - } - } - /* Disable FT1 MONITOR STATUS */ - if (data[60] == 0) { - if (--rCount != 0) { - data[48] = 0; - mbox->cmd.length = 1; - break; - } - } + if( ppp_udp_pkt->data[0] == 1) { + + if( rCount++ != 0 ) { + ppp_udp_pkt->cblock.result = 0; + mbox->cmd.length = 1; + break; + } + } + + /* Disable FT1 MONITOR STATUS */ + if( ppp_udp_pkt->data[0] == 0) { + + if( --rCount != 0) { + ppp_udp_pkt->cblock.result = 0; + mbox->cmd.length = 1; + break; + } + } + + /* PPIPE_DRIVER_STATISTICS */ + case PPIPE_DRIVER_STAT_IFSEND: + printk(KERN_INFO "Getting IF_SEND Drivers Statistics\n"); + memcpy(&ppp_udp_pkt->data, &ppp_priv_area->if_send_stat, + sizeof(if_send_stat_t)); + + + ppp_udp_pkt->cblock.result = 0; + ppp_udp_pkt->cblock.length = sizeof(if_send_stat_t); + mbox->cmd.length = sizeof(if_send_stat_t); + break; + + case PPIPE_DRIVER_STAT_INTR: + memcpy(&ppp_udp_pkt->data, &card->statistics, + sizeof(global_stats_t)); + + memcpy(&ppp_udp_pkt->data+sizeof(global_stats_t), + &ppp_priv_area->rx_intr_stat, + sizeof(rx_intr_stat_t)); + + ppp_udp_pkt->cblock.result = 0; + ppp_udp_pkt->cblock.length = sizeof(global_stats_t)+ + sizeof(rx_intr_stat_t); + mbox->cmd.length = ppp_udp_pkt->cblock.length; + break; + + case PPIPE_DRIVER_STAT_GEN: + memcpy( &ppp_udp_pkt->data, + &ppp_priv_area->pipe_mgmt_stat, + sizeof(pipe_mgmt_stat_t)); + + memcpy(&ppp_udp_pkt->data+sizeof(pipe_mgmt_stat_t), + &card->statistics, sizeof(global_stats_t)); + + ppp_udp_pkt->cblock.result = 0; + ppp_udp_pkt->cblock.length = sizeof(global_stats_t)+ + sizeof(rx_intr_stat_t); + mbox->cmd.length = ppp_udp_pkt->cblock.length; + break; + + default: + /* it's a board command */ - memcpy(&mbox->cmd, &sendpacket[45], sizeof(ppp_cmd_t)); - if (mbox->cmd.length) { - memcpy(&mbox->data, &sendpacket[60], + mbox->cmd.command = ppp_udp_pkt->cblock.command; + mbox->cmd.length = ppp_udp_pkt->cblock.length; + + if(mbox->cmd.length) { + memcpy(&mbox->data,(unsigned char *)ppp_udp_pkt->data, mbox->cmd.length); - } + } + /* run the command on the board */ err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; + if (err != CMD_OK) { - ppp_error(card, err, mbox); - ++ppp_priv_area-> - UDP_PTPIPE_mgmt_adptr_cmnd_timeout; + + ppp_error(card, err, mbox); + ++ppp_priv_area->pipe_mgmt_stat. + UDP_PIPE_mgmt_adptr_cmnd_timeout; break; } - ++ppp_priv_area->UDP_PTPIPE_mgmt_adptr_cmnd_OK; + + ++ppp_priv_area->pipe_mgmt_stat.UDP_PIPE_mgmt_adptr_cmnd_OK; + /* copy the result back to our buffer */ - memcpy(data, sendpacket, skb->len); - memcpy(&data[45], &mbox->cmd, sizeof(ppp_cmd_t)); - if (mbox->cmd.length) { - memcpy(&data[60], &mbox->data, mbox->cmd.length); - } - } /* end of switch */ - } /* end of else */ - /* Fill UDP TTL */ - data[8] = card->wandev.ttl; - len = reply_udp(data, mbox->cmd.length); - if (udp_pkt_src == UDP_PKT_FRM_NETWORK) { - ++ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_adptr; - ppp_send(card, data, len, skb->protocol); - } else { + memcpy(&ppp_udp_pkt->cblock,mbox, sizeof(cblock_t)); + + if(mbox->cmd.length) { + memcpy(&ppp_udp_pkt->data,&mbox->data,mbox->cmd.length); + } + + } /* end of switch */ + } /* end of else */ + + /* Fill UDP TTL */ + ppp_udp_pkt->ip_pkt.ttl = card->wandev.ttl; + len = reply_udp(ppp_priv_area->udp_pkt_data, mbox->cmd.length); + + if (ppp_priv_area->udp_pkt_src == UDP_PKT_FRM_NETWORK) { + + ++ppp_priv_area->pipe_mgmt_stat.UDP_PIPE_mgmt_passed_to_adptr; + ppp_send(card,ppp_priv_area->udp_pkt_data,len,ppp_priv_area->protocol); + + } else { + /* Pass it up the stack - Allocate socket buffer */ + Allocate socket buffer */ if ((new_skb = dev_alloc_skb(len)) != NULL) { + /* copy data into new_skb */ - buf = skb_put(new_skb, len); - memcpy(buf, data, len); - ++ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_stack; - /* Decapsulate packet and pass it up the protocol + + buf = skb_put(new_skb, len); + memcpy(buf,ppp_priv_area->udp_pkt_data, len); + + ++ppp_priv_area->pipe_mgmt_stat.UDP_PIPE_mgmt_passed_to_stack; + + /* Decapsulate packet and pass it up the protocol stack */ - new_skb->protocol = htons(ETH_P_IP); - new_skb->dev = dev; - new_skb->mac.raw = new_skb->data; + new_skb->protocol = htons(ETH_P_IP); + new_skb->dev = dev; + new_skb->mac.raw = new_skb->data; netif_rx(new_skb); + } else { - ++ppp_priv_area->UDP_PTPIPE_mgmt_no_socket; + + ++ppp_priv_area->pipe_mgmt_stat.UDP_PIPE_mgmt_no_socket; printk(KERN_INFO "no socket buffers available!\n"); - } - } - kfree(data); - return 0; + } + } + + ppp_priv_area->udp_pkt_lgth = 0; + + return; } /*============================================================================= * Initial the ppp_private_area structure. */ - -static void init_ppp_priv_struct(ppp_private_area_t * ppp_priv_area) +static void init_ppp_priv_struct( ppp_private_area_t *ppp_priv_area ) { - ppp_priv_area->if_send_entry = 0; - ppp_priv_area->if_send_skb_null = 0; - ppp_priv_area->if_send_broadcast = 0; - ppp_priv_area->if_send_multicast = 0; - ppp_priv_area->if_send_critical_ISR = 0; - ppp_priv_area->if_send_critical_non_ISR = 0; - ppp_priv_area->if_send_busy = 0; - ppp_priv_area->if_send_busy_timeout = 0; - ppp_priv_area->if_send_DRVSTATS_request = 0; - ppp_priv_area->if_send_PTPIPE_request = 0; - ppp_priv_area->if_send_wan_disconnected = 0; - ppp_priv_area->if_send_adptr_bfrs_full = 0; - ppp_priv_area->if_send_bfr_passed_to_adptr = 0; - ppp_priv_area->rx_intr_no_socket = 0; - ppp_priv_area->rx_intr_DRVSTATS_request = 0; - ppp_priv_area->rx_intr_PTPIPE_request = 0; - ppp_priv_area->rx_intr_bfr_not_passed_to_stack = 0; - ppp_priv_area->rx_intr_bfr_passed_to_stack = 0; - ppp_priv_area->UDP_PTPIPE_mgmt_kmalloc_err = 0; - ppp_priv_area->UDP_PTPIPE_mgmt_adptr_type_err = 0; - ppp_priv_area->UDP_PTPIPE_mgmt_direction_err = 0; - ppp_priv_area->UDP_PTPIPE_mgmt_adptr_cmnd_timeout = 0; - ppp_priv_area->UDP_PTPIPE_mgmt_adptr_cmnd_OK = 0; - ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_adptr = 0; - ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_stack = 0; - ppp_priv_area->UDP_PTPIPE_mgmt_no_socket = 0; - ppp_priv_area->UDP_DRVSTATS_mgmt_kmalloc_err = 0; - ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_type_err = 0; - ppp_priv_area->UDP_DRVSTATS_mgmt_direction_err = 0; - ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_cmnd_timeout = 0; - ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_cmnd_OK = 0; - ppp_priv_area->UDP_DRVSTATS_mgmt_passed_to_adptr = 0; - ppp_priv_area->UDP_DRVSTATS_mgmt_passed_to_stack = 0; - ppp_priv_area->UDP_DRVSTATS_mgmt_no_socket = 0; + + memset(&ppp_priv_area->if_send_stat, 0, sizeof(if_send_stat_t)); + memset(&ppp_priv_area->rx_intr_stat, 0, sizeof(rx_intr_stat_t)); + memset(&ppp_priv_area->pipe_mgmt_stat, 0, sizeof(pipe_mgmt_stat_t)); } /*============================================================================ * Initialize Global Statistics */ - -static void init_global_statistics(sdla_t * card) +static void init_global_statistics( sdla_t *card ) { - card->statistics.isr_entry = 0; - card->statistics.isr_already_critical = 0; - card->statistics.isr_tx = 0; - card->statistics.isr_rx = 0; - card->statistics.isr_intr_test = 0; - card->statistics.isr_spurious = 0; - card->statistics.isr_enable_tx_int = 0; - card->statistics.rx_intr_corrupt_rx_bfr = 0; - card->statistics.rx_intr_dev_not_started = 0; - card->statistics.tx_intr_dev_not_started = 0; - card->statistics.poll_entry = 0; - card->statistics.poll_already_critical = 0; - card->statistics.poll_processed = 0; - card->statistics.poll_tbusy_bad_status = 0; + memset(&card->statistics, 0, sizeof(global_stats_t)); } /*============================================================================ * Initialize Receive and Transmit Buffers. */ - -static void init_ppp_tx_rx_buff(sdla_t * card) +static void init_ppp_tx_rx_buff( sdla_t *card ) { - if (card->hw.fwid == SFID_PPP502) { - ppp502_buf_info_t *info = - (void *) (card->hw.dpmbase + PPP502_BUF_OFFS); - card->u.p.txbuf_base = - (void *) (card->hw.dpmbase + info->txb_offs); - card->u.p.txbuf_last = (ppp_buf_ctl_t *) card->u.p.txbuf_base + - (info->txb_num - 1); - card->u.p.rxbuf_base = - (void *) (card->hw.dpmbase + info->rxb_offs); - card->u.p.rxbuf_last = (ppp_buf_ctl_t *) card->u.p.rxbuf_base + - (info->rxb_num - 1); + ppp508_buf_info_t* info; + + if (card->hw.type == SDLA_S514) { + + info = (void*)(card->hw.dpmbase + PPP514_BUF_OFFS); + + card->u.p.txbuf_base = (void*)(card->hw.dpmbase + + info->txb_ptr); + + card->u.p.txbuf_last = (ppp_buf_ctl_t*)card->u.p.txbuf_base + + (info->txb_num - 1); + + card->u.p.rxbuf_base = (void*)(card->hw.dpmbase + + info->rxb_ptr); + + card->u.p.rxbuf_last = (ppp_buf_ctl_t*)card->u.p.rxbuf_base + + (info->rxb_num - 1); + } else { - ppp508_buf_info_t *info = - (void *) (card->hw.dpmbase + PPP508_BUF_OFFS); - card->u.p.txbuf_base = (void *) (card->hw.dpmbase + - (info->txb_ptr - PPP508_MB_VECT)); - card->u.p.txbuf_last = (ppp_buf_ctl_t *) card->u.p.txbuf_base + - (info->txb_num - 1); - card->u.p.rxbuf_base = (void *) (card->hw.dpmbase + - (info->rxb_ptr - PPP508_MB_VECT)); - card->u.p.rxbuf_last = (ppp_buf_ctl_t *) card->u.p.rxbuf_base + - (info->rxb_num - 1); - card->u.p.rx_base = info->rxb_base; - card->u.p.rx_top = info->rxb_end; + + info = (void*)(card->hw.dpmbase + PPP508_BUF_OFFS); + + card->u.p.txbuf_base = (void*)(card->hw.dpmbase + + (info->txb_ptr - PPP508_MB_VECT)); + + card->u.p.txbuf_last = (ppp_buf_ctl_t*)card->u.p.txbuf_base + + (info->txb_num - 1); + + card->u.p.rxbuf_base = (void*)(card->hw.dpmbase + + (info->rxb_ptr - PPP508_MB_VECT)); + + card->u.p.rxbuf_last = (ppp_buf_ctl_t*)card->u.p.rxbuf_base + + (info->rxb_num - 1); } + + card->u.p.rx_base = info->rxb_base; + card->u.p.rx_top = info->rxb_end; + card->u.p.txbuf = card->u.p.txbuf_base; card->rxmb = card->u.p.rxbuf_base; + +} + +/*============================================================================= + * Read Connection Information (ie for Remote IP address assginment). + * Called when ppp interface connected. + */ +static int read_info( sdla_t *card ) +{ + struct device *dev = card->wandev.dev; + ppp_private_area_t *ppp_priv_area = dev->priv; + int err; + struct ifreq if_info; + struct sockaddr_in *if_data1, *if_data2; + mm_segment_t fs; + + /* Set Local and remote addresses */ + memset(&if_info, 0, sizeof(if_info)); + strcpy(if_info.ifr_name, dev->name); + + fs = get_fs(); + set_fs(get_ds()); /* get user space block */ + + + /* Change the local and remote ip address of the interface. + * This will also add in the destination route. + */ + if_data1 = (struct sockaddr_in *)&if_info.ifr_addr; + if_data1->sin_addr.s_addr = ppp_priv_area->ip_local; + if_data1->sin_family = AF_INET; + err = devinet_ioctl( SIOCSIFADDR, &if_info ); + if_data2 = (struct sockaddr_in *)&if_info.ifr_dstaddr; + if_data2->sin_addr.s_addr = ppp_priv_area->ip_remote; + if_data2->sin_family = AF_INET; + err = devinet_ioctl( SIOCSIFDSTADDR, &if_info ); + + set_fs(fs); /* restore old block */ + + + if (err) { + printk (KERN_INFO "%s: Adding of route failed:\n", + card->devname); + printk (KERN_INFO "%s: Local : %s\n", + card->devname,in_ntoa(ppp_priv_area->ip_local)); + printk (KERN_INFO "%s: Remote: %s\n", + card->devname,in_ntoa(ppp_priv_area->ip_remote)); + } + return err; +} + +/*============================================================================= + * Remove Dynamic Route. + * Called when ppp interface disconnected. + */ + +static int remove_route( sdla_t *card ) +{ + + struct device *dev = card->wandev.dev; + long ip_addr; + int err; + + mm_segment_t fs; + struct ifreq if_info; + struct sockaddr_in *if_data1; + struct in_device *in_dev = dev->ip_ptr; + struct in_ifaddr *ifa = in_dev->ifa_list; + + + ip_addr = ifa->ifa_local; + + /* Set Local and remote addresses */ + memset(&if_info, 0, sizeof(if_info)); + strcpy(if_info.ifr_name, dev->name); + + + fs = get_fs(); + set_fs(get_ds()); /* get user space block */ + + + /* Change the local ip address of the interface to 0. + * This will also delete the destination route. + */ + if_data1 = (struct sockaddr_in *)&if_info.ifr_addr; + if_data1->sin_addr.s_addr = 0; + if_data1->sin_family = AF_INET; + err = devinet_ioctl( SIOCSIFADDR, &if_info ); + + set_fs(fs); /* restore old block */ + + + if (err) { + printk (KERN_INFO "%s: Deleting dynamic route failed %d!\n", + card->devname, err); + return err; + }else + printk (KERN_INFO "%s: PPP Deleting dynamic route %s successfuly\n", + card->devname, in_ntoa(ip_addr)); + + + return 0; } /*============================================================================= * Perform the Interrupt Test by running the READ_CODE_VERSION command MAX_INTR * _TEST_COUNTER times. */ - -static int intr_test(sdla_t * card) +static int intr_test( sdla_t *card ) { ppp_mbox_t *mb = card->mbox; - int err, i; - /* The critical flag is unset because during initialization (if_open) + int err,i; + + /* The critical flag is unset because during intialization (if_open) * we want the interrupts to be enabled so that when the wpp_isr is * called it does not exit due to critical flag set. - */ + */ + card->wandev.critical = 0; - err = ppp_set_intr_mode(card, 0x08); - if (err == CMD_OK) { - for (i = 0; i < MAX_INTR_TEST_COUNTER; i++) { + + err = ppp_set_intr_mode( card, 0x08 ); + + if (err == CMD_OK) { + + for (i = 0; i < MAX_INTR_TEST_COUNTER; i ++) { /* Run command READ_CODE_VERSION */ memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); - mb->cmd.length = 0; - mb->cmd.command = 0x10; + mb->cmd.length = 0; + mb->cmd.command = PPP_READ_CODE_VERSION; err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; - if (err != CMD_OK) + if (err != CMD_OK) ppp_error(card, err, mb); } - } else - return err; - err = ppp_set_intr_mode(card, 0); - if (err != CMD_OK) + } + else return err; + + err = ppp_set_intr_mode( card, 0 ); + if (err != CMD_OK) return err; + card->wandev.critical = 1; return 0; } @@ -2222,40 +2797,140 @@ /*============================================================================== * Determine what type of UDP call it is. DRVSTATS or PTPIPEAB ? */ - -static int udp_pkt_type(struct sk_buff *skb, sdla_t * card) +static int udp_pkt_type( struct sk_buff *skb, sdla_t *card ) { unsigned char *sendpacket; - unsigned char buf2[5]; + unsigned char buf2[5]; + //FIXME: Use the structure + ppp_udp_pkt_t *ppp_udp_pkt = (ppp_udp_pkt_t *)skb->data; + sendpacket = skb->data; memcpy(&buf2, &card->wandev.udp_port, 2); - if (sendpacket[0] == 0x45 && /* IP packet */ - sendpacket[9] == 0x11 && /* UDP packet */ - sendpacket[22] == buf2[1] && /* UDP Port */ - sendpacket[23] == buf2[0] && - sendpacket[36] == 0x01) { - if (sendpacket[28] == 0x50 && /* PTPIPEAB: Signature */ - sendpacket[29] == 0x54 && - sendpacket[30] == 0x50 && - sendpacket[31] == 0x49 && - sendpacket[32] == 0x50 && - sendpacket[33] == 0x45 && - sendpacket[34] == 0x41 && - sendpacket[35] == 0x42) { + + if( ppp_udp_pkt->ip_pkt.ver_inet_hdr_length == 0x45 && /* IP packet */ + sendpacket[9] == 0x11 && /* UDP packet */ + sendpacket[22] == buf2[1] && /* UDP Port */ + sendpacket[23] == buf2[0] && + sendpacket[36] == 0x01 ) { + + if ( sendpacket[28] == 0x50 && /* PTPIPEAB: Signature */ + sendpacket[29] == 0x54 && + sendpacket[30] == 0x50 && + sendpacket[31] == 0x49 && + sendpacket[32] == 0x50 && + sendpacket[33] == 0x45 && + sendpacket[34] == 0x41 && + sendpacket[35] == 0x42 ){ + return UDP_PTPIPE_TYPE; - } else if (sendpacket[28] == 0x44 && /* DRVSTATS: Signature */ - sendpacket[29] == 0x52 && - sendpacket[30] == 0x56 && - sendpacket[31] == 0x53 && - sendpacket[32] == 0x54 && - sendpacket[33] == 0x41 && - sendpacket[34] == 0x54 && - sendpacket[35] == 0x53) { + + } else if(sendpacket[28] == 0x44 && /* DRVSTATS: Signature */ + sendpacket[29] == 0x52 && + sendpacket[30] == 0x56 && + sendpacket[31] == 0x53 && + sendpacket[32] == 0x54 && + sendpacket[33] == 0x41 && + sendpacket[34] == 0x54 && + sendpacket[35] == 0x53 ){ + return UDP_DRVSTATS_TYPE; + } else return UDP_INVALID_TYPE; + } else return UDP_INVALID_TYPE; + +} + +/*============================================================================ + * Check to see if the packet to be transmitted contains a broadcast or + * multicast source IP address. + */ + +static int chk_bcast_mcast_addr(sdla_t *card, struct device* dev, + struct sk_buff *skb) +{ + u32 src_ip_addr; + u32 broadcast_ip_addr = 0; + struct in_device *in_dev; + /* read the IP source address from the outgoing packet */ + src_ip_addr = *(u32 *)(skb->data + 12); + + /* read the IP broadcast address for the device */ + in_dev = dev->ip_ptr; + if(in_dev != NULL) { + struct in_ifaddr *ifa= in_dev->ifa_list; + if(ifa != NULL) + broadcast_ip_addr = ifa->ifa_broadcast; + else + return 0; + } + + /* check if the IP Source Address is a Broadcast address */ + if((dev->flags & IFF_BROADCAST) && (src_ip_addr == broadcast_ip_addr)) { + printk(KERN_INFO "%s: Broadcast Source Address silently discarded\n", + card->devname); + dev_kfree_skb(skb); + ++card->wandev.stats.tx_dropped; + return 1; + } + + /* check if the IP Source Address is a Multicast address */ + if((ntohl(src_ip_addr) >= 0xE0000001) && + (ntohl(src_ip_addr) <= 0xFFFFFFFE)) { + printk(KERN_INFO "%s: Multicast Source Address silently discarded\n", + card->devname); + dev_kfree_skb(skb); + ++card->wandev.stats.tx_dropped; + return 1; + } + + return 0; +} + +void s508_lock (sdla_t *card, unsigned long *smp_flags) +{ +#ifdef __SMP__ + spin_lock_irqsave(&card->lock, *smp_flags); +#else + disable_irq(card->hw.irq); +#endif +} + +void s508_unlock (sdla_t *card, unsigned long *smp_flags) +{ +#ifdef __SMP__ + spin_unlock_irqrestore(&card->lock, *smp_flags); +#else + enable_irq(card->hw.irq); +#endif +} + +static int read_connection_info (sdla_t *card) +{ + ppp_mbox_t *mb = card->mbox; + struct device *dev = card->wandev.dev; + ppp_private_area_t *ppp_priv_area = dev->priv; + ppp508_connect_info_t *ppp508_connect_info; + int err; + + memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); + mb->cmd.length = 0; + mb->cmd.command = PPP_GET_CONNECTION_INFO; + err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; + + if (err != CMD_OK) { + ppp_error(card, err, mb); + } + else { + ppp508_connect_info = (ppp508_connect_info_t *)mb->data; + + ppp_priv_area->ip_remote = ppp508_connect_info->ip_remote; + ppp_priv_area->ip_local = ppp508_connect_info->ip_local; + } + + return err; } /****** End *****************************************************************/ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sdladrv.c linux/drivers/net/sdladrv.c --- v2.2.13/linux/drivers/net/sdladrv.c Thu Jan 7 09:21:53 1999 +++ linux/drivers/net/sdladrv.c Tue Jan 4 10:12:17 2000 @@ -4,15 +4,18 @@ * This module is a library of common hardware-specific functions * used by all Sangoma drivers. * -* Author: Gene Kozin +* Author: Gideon Hack * -* Copyright: (c) 1995-1996 Sangoma Technologies Inc. +* Copyright: (c) 1995-1999 Sangoma Technologies Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * ============================================================================ +* Jun 02, 1999 Gideon Hack Added support for the S514 adapter. +* Updates for Linux 2.2.X kernels. +* Sep 17, 1998 Jaspreet Singh Updates for linux 2.2.X kernels * Dec 20, 1996 Gene Kozin Version 3.0.0. Complete overhaul. * Jul 12, 1996 Gene Kozin Changes for Linux 2.0 compatibility. * Jun 12, 1996 Gene Kozin Added support for S503 card. @@ -81,6 +84,7 @@ #if defined(_LINUX_) /****** Linux *******************************/ +#include #include /* printk(), and other useful stuff */ #include /* offsetof(), etc. */ #include /* return codes */ @@ -89,13 +93,18 @@ #include /* for jiffies, HZ, etc. */ #include /* API definitions */ #include /* SDLA firmware module definitions */ -#include +#include /* SDLA PCI hardware definitions */ +#include /* PCI defines and function prototypes */ #include /* for inb(), outb(), etc. */ + #define _INB(port) (inb(port)) #define _OUTB(port, byte) (outb((byte),(port))) #define SYSTEM_TICK jiffies +#include + #elif defined(_SCO_UNIX_) /****** SCO Unix ****************************/ + #if !defined(INKERNEL) #error This code MUST be compiled in kernel mode! #endif @@ -135,6 +144,11 @@ #define S503_MINMEM 0x8000L #define S507_MINMEM 0x20000L #define S508_MINMEM 0x20000L +#define NO_PORT -1 + + + + /****** Function Prototypes *************************************************/ @@ -158,14 +172,18 @@ static int init_s503 (sdlahw_t* hw); static int init_s507 (sdlahw_t* hw); static int init_s508 (sdlahw_t* hw); - + static int detect_s502a (int port); static int detect_s502e (int port); static int detect_s503 (int port); static int detect_s507 (int port); static int detect_s508 (int port); +static int detect_s514 (sdlahw_t* hw); +static int find_s514_adapter(sdlahw_t* hw, char find_first_S514_card); /* Miscellaneous functions */ +static void peek_by_4 (unsigned long src, void* buf, unsigned len); +static void poke_by_4 (unsigned long dest, void* buf, unsigned len); static int calibrate_delay (int mks); static int get_option_index (unsigned* optlist, unsigned optval); static unsigned check_memregion (void* ptr, unsigned len); @@ -179,7 +197,7 @@ /* private data */ static char modname[] = "sdladrv"; static char fullname[] = "SDLA Support Module"; -static char copyright[] = "(c) 1995-1996 Sangoma Technologies Inc."; +static char copyright[] = "(c) 1995-1999 Sangoma Technologies Inc."; static unsigned exec_idle; /* Hardware configuration options. @@ -324,7 +342,7 @@ * Return: 0 ok. * < 0 error */ - + EXPORT_SYMBOL(sdla_setup); int sdla_setup (sdlahw_t* hw, void* sfm, unsigned len) @@ -332,118 +350,123 @@ unsigned* irq_opt = NULL; /* IRQ options */ unsigned* dpmbase_opt = NULL; /* DPM window base options */ unsigned* pclk_opt = NULL; /* CPU clock rate options */ - int err; + int err=0; - if (sdla_detect(hw)) - { - printk(KERN_ERR "%s: adapter S%04u not found at port 0x%X!\n", - modname, hw->type, hw->port) - ; - return -EINVAL; - } - printk(KERN_INFO "%s: found S%04u card at port 0x%X.\n", - modname, hw->type, hw->port) - ; - - hw->dpmsize = SDLA_WINDOWSIZE; - switch (hw->type) - { - case SDLA_S502A: - hw->io_range = S502A_IORANGE; - irq_opt = s502a_irq_options; - dpmbase_opt = s502a_dpmbase_options; - pclk_opt = s502a_pclk_options; - break; - - case SDLA_S502E: - hw->io_range = S502E_IORANGE; - irq_opt = s502e_irq_options; - dpmbase_opt = s508_dpmbase_options; - pclk_opt = s502e_pclk_options; - break; - - case SDLA_S503: - hw->io_range = S503_IORANGE; - irq_opt = s503_irq_options; - dpmbase_opt = s508_dpmbase_options; - pclk_opt = s503_pclk_options; - break; - - case SDLA_S507: - hw->io_range = S507_IORANGE; - irq_opt = s508_irq_options; - dpmbase_opt = s507_dpmbase_options; - pclk_opt = s507_pclk_options; - break; - - case SDLA_S508: - hw->io_range = S508_IORANGE; - irq_opt = s508_irq_options; - dpmbase_opt = s508_dpmbase_options; - pclk_opt = s508_pclk_options; - break; - } - - /* Verify IRQ configuration options */ - if (!get_option_index(irq_opt, hw->irq)) - { - printk(KERN_ERR "%s: IRQ %d is illegal!\n", - modname, hw->irq) - ; - return -EINVAL; - } - - /* Verify CPU clock rate configuration options */ - if (hw->pclk == 0) - hw->pclk = pclk_opt[1] /* use default */ - ; - else if (!get_option_index(pclk_opt, hw->pclk)) - { - printk(KERN_ERR "%s: CPU clock %u is illegal!\n", - modname, hw->pclk) - ; - return -EINVAL; - } - printk(KERN_INFO "%s: assuming CPU clock rate of %u kHz.\n", - modname, hw->pclk) - ; - - /* Setup adapter dual-port memory window and test memory */ - if (hw->dpmbase == 0) - { - err = sdla_autodpm(hw); - if (err) - { - printk(KERN_ERR + if (sdla_detect(hw)) { + if(hw->type != SDLA_S514) + printk(KERN_ERR "%s: no SDLA card found at port 0x%X\n", + modname, hw->port); + return -EINVAL; + } + + if(hw->type != SDLA_S514) { + printk(KERN_INFO "%s: found S%04u card at port 0x%X.\n", + modname, hw->type, hw->port); + + hw->dpmsize = SDLA_WINDOWSIZE; + switch (hw->type) { + case SDLA_S502A: + hw->io_range = S502A_IORANGE; + irq_opt = s502a_irq_options; + dpmbase_opt = s502a_dpmbase_options; + pclk_opt = s502a_pclk_options; + break; + + case SDLA_S502E: + hw->io_range = S502E_IORANGE; + irq_opt = s502e_irq_options; + dpmbase_opt = s508_dpmbase_options; + pclk_opt = s502e_pclk_options; + break; + + case SDLA_S503: + hw->io_range = S503_IORANGE; + irq_opt = s503_irq_options; + dpmbase_opt = s508_dpmbase_options; + pclk_opt = s503_pclk_options; + break; + + case SDLA_S507: + hw->io_range = S507_IORANGE; + irq_opt = s508_irq_options; + dpmbase_opt = s507_dpmbase_options; + pclk_opt = s507_pclk_options; + break; + + case SDLA_S508: + hw->io_range = S508_IORANGE; + irq_opt = s508_irq_options; + dpmbase_opt = s508_dpmbase_options; + pclk_opt = s508_pclk_options; + break; + } + + /* Verify IRQ configuration options */ + if (!get_option_index(irq_opt, hw->irq)) { + printk(KERN_ERR "%s: IRQ %d is illegal!\n", + modname, hw->irq); + return -EINVAL; + } + + /* Verify CPU clock rate configuration options */ + if (hw->pclk == 0) + hw->pclk = pclk_opt[1]; /* use default */ + + else if (!get_option_index(pclk_opt, hw->pclk)) { + printk(KERN_ERR "%s: CPU clock %u is illegal!\n", + modname, hw->pclk); + return -EINVAL; + } + printk(KERN_INFO "%s: assuming CPU clock rate of %u kHz.\n", + modname, hw->pclk); + + /* Setup adapter dual-port memory window and test memory */ + if (hw->dpmbase == 0) { + err = sdla_autodpm(hw); + if (err) { + printk(KERN_ERR "%s: can't find available memory region!\n", - modname) - ; - return err; + modname); + return err; + } + } + else if (!get_option_index(dpmbase_opt, + virt_to_phys(hw->dpmbase))) { + printk(KERN_ERR + "%s: memory address 0x%lX is illegal!\n", + modname, virt_to_phys(hw->dpmbase)); + return -EINVAL; + } + else if (sdla_setdpm(hw)) { + printk(KERN_ERR + "%s: 8K memory region at 0x%lX is not available!\n", + modname, virt_to_phys(hw->dpmbase)); + return -EINVAL; + } + printk(KERN_INFO + "%s: dual-port memory window is set at 0x%lX.\n", + modname, virt_to_phys(hw->dpmbase)); + } + + else { + hw->memory = test_memregion((void*)hw->dpmbase, + MAX_SIZEOF_S514_MEMORY); + if(hw->memory < (256 * 1024)) { + printk(KERN_ERR + "%s: error in testing S514 memory (0x%lX)\n", + modname, hw->memory); + sdla_down(hw); + return -EINVAL; } } - else if (!get_option_index(dpmbase_opt, virt_to_phys(hw->dpmbase))) - { - printk(KERN_ERR "%s: memory address 0x%lX is illegal!\n", - modname, virt_to_phys(hw->dpmbase)) - ; - return -EINVAL; - } - else if (sdla_setdpm(hw)) - { - printk(KERN_ERR - "%s: 8K memory region at 0x%lX is not available!\n", - modname, virt_to_phys(hw->dpmbase)); - return -EINVAL; - } - printk(KERN_INFO "%s: dual-port memory window is set at 0x%lX.\n", - modname, virt_to_phys(hw->dpmbase)); - - printk(KERN_INFO "%s: found %luK bytes of on-board memory.\n", + + printk(KERN_INFO "%s: found %luK bytes of on-board memory\n", modname, hw->memory / 1024); /* Load firmware. If loader fails then shut down adapter */ err = sdla_load(hw, sfm, len); if (err) sdla_down(hw); /* shutdown adapter */ + return err; } @@ -457,11 +480,13 @@ { unsigned port = hw->port; int i; + unsigned char CPU_no; + u32 int_config, int_status; - if (!port) return -EFAULT; + if(!port && (hw->type != SDLA_S514)) + return -EFAULT; - switch (hw->type) - { + switch (hw->type) { case SDLA_S502A: _OUTB(port, 0x08); /* halt CPU */ _OUTB(port, 0x08); @@ -486,6 +511,30 @@ hw->regs[0] = 0; break; + case SDLA_S514: + /* halt the adapter */ + *(char *)hw->vector = S514_CPU_HALT; + CPU_no = hw->S514_cpu_no[0]; + + /* disable the PCI IRQ and disable memory access */ + pci_read_config_dword(hw->pci_dev, PCI_INT_CONFIG, &int_config); + int_config &= (CPU_no == S514_CPU_A) ? ~PCI_DISABLE_IRQ_CPU_A : ~PCI_DISABLE_IRQ_CPU_B; + pci_write_config_dword(hw->pci_dev, PCI_INT_CONFIG, int_config); + read_S514_int_stat(hw, &int_status); + S514_intack(hw, int_status); + if(CPU_no == S514_CPU_A) + pci_write_config_dword(hw->pci_dev, PCI_MAP0_DWORD, + PCI_CPU_A_MEM_DISABLE); + else + pci_write_config_dword(hw->pci_dev, PCI_MAP1_DWORD, + PCI_CPU_B_MEM_DISABLE); + + /* free up the allocated virtual memory */ + iounmap((void *)hw->dpmbase); + iounmap((void *)hw->vector); + break; + + default: return -EINVAL; } @@ -503,12 +552,10 @@ unsigned port = hw->port; register int tmp; - switch (hw->type) - { + switch (hw->type) { case SDLA_S502A: case SDLA_S502E: - if (addr < S502_MAXMEM) /* verify parameter */ - { + if (addr < S502_MAXMEM) { /* verify parameter */ tmp = addr >> 13; /* convert to register mask */ _OUTB(port + 2, tmp); hw->regs[2] = tmp; @@ -517,8 +564,7 @@ break; case SDLA_S503: - if (addr < S503_MAXMEM) /* verify parameter */ - { + if (addr < S503_MAXMEM) { /* verify parameter */ tmp = (hw->regs[0] & 0x8F) | ((addr >> 9) & 0x70); _OUTB(port, tmp); hw->regs[0] = tmp; @@ -527,11 +573,9 @@ break; case SDLA_S507: - if (addr < S507_MAXMEM) - { + if (addr < S507_MAXMEM) { if (!(_INB(port) & 0x02)) - return -EIO - ; + return -EIO; tmp = addr >> 13; /* convert to register mask */ _OUTB(port + 2, tmp); hw->regs[2] = tmp; @@ -540,8 +584,7 @@ break; case SDLA_S508: - if (addr < S508_MAXMEM) - { + if (addr < S508_MAXMEM) { tmp = addr >> 13; /* convert to register mask */ _OUTB(port + 2, tmp); hw->regs[2] = tmp; @@ -549,7 +592,10 @@ else return -EINVAL; break; - default: + case SDLA_S514: + return 0; + + default: return -EINVAL; } hw->vector = addr & 0xFFFFE000L; @@ -559,7 +605,7 @@ /*============================================================================ * Enable interrupt generation. */ - + EXPORT_SYMBOL(sdla_inten); int sdla_inten (sdlahw_t* hw) @@ -567,14 +613,12 @@ unsigned port = hw->port; int tmp, i; - switch (hw->type) - { + switch (hw->type) { case SDLA_S502E: /* Note thar interrupt control operations on S502E are allowed * only if CPU is enabled (bit 0 of status register is set). */ - if (_INB(port) & 0x01) - { + if (_INB(port) & 0x01) { _OUTB(port, 0x02); /* bit1 = 1, bit2 = 0 */ _OUTB(port, 0x06); /* bit1 = 1, bit2 = 1 */ hw->regs[0] = 0x06; @@ -588,8 +632,7 @@ hw->regs[0] = tmp; /* update mirror */ for (i = 0; i < SDLA_IODELAY; ++i); /* delay */ if (!(_INB(port) & 0x02)) /* verify */ - return -EIO - ; + return -EIO; break; case SDLA_S508: @@ -598,14 +641,16 @@ hw->regs[0] = tmp; /* update mirror */ for (i = 0; i < SDLA_IODELAY; ++i); /* delay */ if (!(_INB(port + 1) & 0x10)) /* verify */ - return -EIO - ; + return -EIO; break; case SDLA_S502A: case SDLA_S507: break; + case SDLA_S514: + break; + default: return -EINVAL; @@ -624,8 +669,7 @@ unsigned port = hw->port; int tmp, i; - switch (hw->type) - { + switch (hw->type) { case SDLA_S502E: /* Notes: * 1) interrupt control operations are allowed only if CPU is @@ -634,8 +678,7 @@ * causes IRQ line go high, therefore we are going to use * 0x04 instead: lower it to inhibit interrupts to PC. */ - if (_INB(port) & 0x01) - { + if (_INB(port) & 0x01) { _OUTB(port, hw->regs[0] & ~0x04); hw->regs[0] &= ~0x04; } @@ -648,8 +691,7 @@ hw->regs[0] = tmp; /* update mirror */ for (i = 0; i < SDLA_IODELAY; ++i); /* delay */ if (_INB(port) & 0x02) /* verify */ - return -EIO - ; + return -EIO; break; case SDLA_S508: @@ -658,8 +700,7 @@ hw->regs[0] = tmp; /* update mirror */ for (i = 0; i < SDLA_IODELAY; ++i); /* delay */ if (_INB(port) & 0x10) /* verify */ - return -EIO - ; + return -EIO; break; case SDLA_S502A: @@ -683,16 +724,14 @@ unsigned port = hw->port; int tmp; - switch (hw->type) - { + switch (hw->type) { case SDLA_S502E: /* To acknoledge hardware interrupt we have to toggle bit 3 of * control register: \_/ * Note that interrupt control operations on S502E are allowed * only if CPU is enabled (bit 1 of status register is set). */ - if (_INB(port) & 0x01) - { + if (_INB(port) & 0x01) { tmp = hw->regs[0] & ~0x04; _OUTB(port, tmp); tmp |= 0x04; @@ -703,8 +742,7 @@ break; case SDLA_S503: - if (_INB(port) & 0x04) - { + if (_INB(port) & 0x04) { tmp = hw->regs[0] & ~0x08; _OUTB(port, tmp); tmp |= 0x08; @@ -724,6 +762,31 @@ return 0; } + +/*============================================================================ + * Acknowledge S514 hardware interrupt. + */ + +EXPORT_SYMBOL(S514_intack); + +void S514_intack (sdlahw_t* hw, u32 int_status) +{ + pci_write_config_dword(hw->pci_dev, PCI_INT_STATUS, int_status); +} + + +/*============================================================================ + * Read the S514 hardware interrupt status. + */ + +EXPORT_SYMBOL(read_S514_int_stat); + +void read_S514_int_stat (sdlahw_t* hw, u32* int_status) +{ + pci_read_config_dword(hw->pci_dev, PCI_INT_STATUS, int_status); +} + + /*============================================================================ * Generate an interrupt to adapter's CPU. */ @@ -734,11 +797,9 @@ { unsigned port = hw->port; - switch (hw->type) - { + switch (hw->type) { case SDLA_S502A: - if (!(_INB(port) & 0x40)) - { + if (!(_INB(port) & 0x40)) { _OUTB(port, 0x10); /* issue NMI to CPU */ hw->regs[0] = 0x10; } @@ -746,16 +807,14 @@ break; case SDLA_S507: - if ((_INB(port) & 0x06) == 0x06) - { + if ((_INB(port) & 0x06) == 0x06) { _OUTB(port + 3, 0); } else return -EIO; break; case SDLA_S508: - if (_INB(port + 1) & 0x02) - { + if (_INB(port + 1) & 0x02) { _OUTB(port, 0x08); } else return -EIO; @@ -784,14 +843,19 @@ unsigned long tstop; int nloops; - if (*flag) return 0; /* ???? */ + if(readb(flag) != 0x00) { + printk(KERN_INFO + "WANPIPE: opp flag set on entry to sdla_exec\n"); + return 0; + } + + writeb(0x01, flag); - *flag = 1; tstop = SYSTEM_TICK + EXEC_TIMEOUT; - for (nloops = 1; *flag; ++nloops) - { + + for (nloops = 1; (readb(flag) == 0x01); ++ nloops) { unsigned delay = exec_idle; - while (--delay); /* delay */ + while (-- delay); /* delay */ if (SYSTEM_TICK > tstop) return 0; /* time is up! */ } return nloops; @@ -806,39 +870,79 @@ * This function is not atomic, so caller must disable interrupt if * interrupt routines are accessing adapter shared memory. */ - + EXPORT_SYMBOL(sdla_peek); int sdla_peek (sdlahw_t* hw, unsigned long addr, void* buf, unsigned len) { - unsigned long oldvec = hw->vector; - unsigned winsize = hw->dpmsize; - unsigned curpos, curlen; /* current offset and block size */ - unsigned long curvec; /* current DPM window vector */ - int err = 0; if (addr + len > hw->memory) /* verify arguments */ - return -EINVAL - ; - while (len && !err) - { - curpos = addr % winsize; /* current window offset */ - curvec = addr - curpos; /* current window vector */ - curlen = (len > (winsize - curpos)) ? (winsize - curpos) : len; - - /* Relocate window and copy block of data */ - err = sdla_mapmem(hw, curvec); - memcpy(buf, (void *)((u8 *)hw->dpmbase + curpos), curlen); - addr += curlen; - (char*)buf += curlen; - len -= curlen; - } + return -EINVAL; - /* Restore DPM window position */ - sdla_mapmem(hw, oldvec); - return err; + if(hw->type == SDLA_S514) { /* copy data for the S514 adapter */ + peek_by_4 ((unsigned long)hw->dpmbase + addr, buf, len); + return 0; + } + + else { /* copy data for the S508 adapter */ + unsigned long oldvec = hw->vector; + unsigned winsize = hw->dpmsize; + unsigned curpos, curlen; /* current offset and block size */ + unsigned long curvec; /* current DPM window vector */ + int err = 0; + + while (len && !err) { + curpos = addr % winsize; /* current window offset */ + curvec = addr - curpos; /* current window vector */ + curlen = (len > (winsize - curpos)) ? + (winsize - curpos) : len; + /* Relocate window and copy block of data */ + err = sdla_mapmem(hw, curvec); + peek_by_4 ((unsigned long)hw->dpmbase + curpos, buf, + curlen); + addr += curlen; + (char*)buf += curlen; + len -= curlen; + } + + /* Restore DPM window position */ + sdla_mapmem(hw, oldvec); + return err; + } +} + + +/*============================================================================ + * Read data from adapter's memory to a data buffer in 4-byte chunks. + * Note that we ensure that the SDLA memory address is on a 4-byte boundary + * before we begin moving the data in 4-byte chunks. +*/ + +static void peek_by_4 (unsigned long src, void* buf, unsigned len) +{ + + /* byte copy data until we get to a 4-byte boundary */ + while (len && (src & 0x03)) { + *(char *)buf ++ = readb(src ++); + len --; + } + + /* copy data in 4-byte chunks */ + while (len >= 4) { + *(unsigned long *)buf = readl(src); + buf += 4; + src += 4; + len -= 4; + } + + /* byte copy any remaining data */ + while (len) { + *(char *)buf ++ = readb(src ++); + len --; + } } + /*============================================================================ * Write Absolute Adapter Memory. * Transfer data from data buffer to adapter's memory. @@ -848,39 +952,79 @@ * This function is not atomic, so caller must disable interrupt if * interrupt routines are accessing adapter shared memory. */ - + EXPORT_SYMBOL(sdla_poke); int sdla_poke (sdlahw_t* hw, unsigned long addr, void* buf, unsigned len) { - unsigned long oldvec = hw->vector; - unsigned winsize = hw->dpmsize; - unsigned curpos, curlen; /* current offset and block size */ - unsigned long curvec; /* current DPM window vector */ - int err = 0; if (addr + len > hw->memory) /* verify arguments */ - return -EINVAL - ; - while (len && !err) - { - curpos = addr % winsize; /* current window offset */ - curvec = addr - curpos; /* current window vector */ - curlen = (len > (winsize - curpos)) ? (winsize - curpos) : len; - - /* Relocate window and copy block of data */ - sdla_mapmem(hw, curvec); - memcpy((void*)((u8 *)hw->dpmbase + curpos), buf, curlen); - addr += curlen; - (char*)buf += curlen; - len -= curlen; - } + return -EINVAL; + + if(hw->type == SDLA_S514) { /* copy data for the S514 adapter */ + poke_by_4 ((unsigned long)hw->dpmbase + addr, buf, len); + return 0; + } + + else { /* copy data for the S508 adapter */ + unsigned long oldvec = hw->vector; + unsigned winsize = hw->dpmsize; + unsigned curpos, curlen; /* current offset and block size */ + unsigned long curvec; /* current DPM window vector */ + int err = 0; + + while (len && !err) { + curpos = addr % winsize; /* current window offset */ + curvec = addr - curpos; /* current window vector */ + curlen = (len > (winsize - curpos)) ? + (winsize - curpos) : len; + /* Relocate window and copy block of data */ + sdla_mapmem(hw, curvec); + poke_by_4 ((unsigned long)hw->dpmbase + curpos, buf, + curlen); + addr += curlen; + (char*)buf += curlen; + len -= curlen; + } + + /* Restore DPM window position */ + sdla_mapmem(hw, oldvec); + return err; + } +} - /* Restore DPM window position */ - sdla_mapmem(hw, oldvec); - return err; + +/*============================================================================ + * Write from a data buffer to adapter's memory in 4-byte chunks. + * Note that we ensure that the SDLA memory address is on a 4-byte boundary + * before we begin moving the data in 4-byte chunks. +*/ + +static void poke_by_4 (unsigned long dest, void* buf, unsigned len) +{ + + /* byte copy data until we get to a 4-byte boundary */ + while (len && (dest & 0x03)) { + writeb (*(char *)buf ++, dest ++); + len --; + } + + /* copy data in 4-byte chunks */ + while (len >= 4) { + writel (*(unsigned long *)buf, dest); + dest += 4; + buf += 4; + len -= 4; + } + + /* byte copy any remaining data */ + while (len) { + writeb (*(char *)buf ++ , dest ++); + len --; + } } + #ifdef DONT_COMPIPLE_THIS #endif /* DONT_COMPIPLE_THIS */ @@ -901,11 +1045,10 @@ unsigned port = hw->port; int err = 0; - if (!port) - return -EFAULT - ; - switch (hw->type) - { + if (!port && (hw->type != SDLA_S514)) + return -EFAULT; + + switch (hw->type) { case SDLA_S502A: if (!detect_s502a(port)) err = -ENODEV; break; @@ -926,22 +1069,21 @@ if (!detect_s508(port)) err = -ENODEV; break; + case SDLA_S514: + if (!detect_s514(hw)) err = -ENODEV; + break; + default: if (detect_s502a(port)) - hw->type = SDLA_S502A - ; + hw->type = SDLA_S502A; else if (detect_s502e(port)) - hw->type = SDLA_S502E - ; + hw->type = SDLA_S502E; else if (detect_s503(port)) - hw->type = SDLA_S503 - ; + hw->type = SDLA_S503; else if (detect_s507(port)) - hw->type = SDLA_S507 - ; + hw->type = SDLA_S507; else if (detect_s508(port)) - hw->type = SDLA_S508 - ; + hw->type = SDLA_S508; else err = -ENODEV; } return err; @@ -956,8 +1098,7 @@ int i, err = -EINVAL; unsigned* opt; - switch (hw->type) - { + switch (hw->type) { case SDLA_S502A: opt = s502a_dpmbase_options; break; @@ -976,8 +1117,7 @@ return -EINVAL; } - for (i = opt[0]; i && err; --i) - { + for (i = opt[0]; i && err; --i) { hw->dpmbase = phys_to_virt(opt[i]); err = sdla_setdpm(hw); } @@ -1000,8 +1140,7 @@ /* Shut down card and verify memory region */ sdla_down(hw); if (check_memregion(hw->dpmbase, hw->dpmsize)) - return -EINVAL - ; + return -EINVAL; /* Initialize adapter and test on-board memory segment by segment. * If memory size appears to be less than shared memory window size, @@ -1010,8 +1149,7 @@ err = sdla_init(hw); if (err) return err; - if (sdla_memtest(hw) < hw->dpmsize) /* less than window size */ - { + if (sdla_memtest(hw) < hw->dpmsize) { /* less than window size */ sdla_down(hw); return -EIO; } @@ -1026,32 +1164,28 @@ */ static int sdla_load (sdlahw_t* hw, sfm_t* sfm, unsigned len) { + int i; /* Verify firmware signature */ - if (strcmp(sfm->signature, SFM_SIGNATURE)) - { + if (strcmp(sfm->signature, SFM_SIGNATURE)) { printk(KERN_ERR "%s: not SDLA firmware!\n", - modname) - ; + modname); return -EINVAL; } /* Verify firmware module format version */ - if (sfm->version != SFM_VERSION) - { + if (sfm->version != SFM_VERSION) { printk(KERN_ERR "%s: firmware format %u rejected! Expecting %u.\n", - modname, sfm->version, SFM_VERSION) - ; + modname, sfm->version, SFM_VERSION); return -EINVAL; } /* Verify firmware module length and checksum */ if ((len - offsetof(sfm_t, image) != sfm->info.codesize) || (checksum((void*)&sfm->info, - sizeof(sfm_info_t) + sfm->info.codesize) != sfm->checksum)) - { + sizeof(sfm_info_t) + sfm->info.codesize) != sfm->checksum)) { printk(KERN_ERR "%s: firmware corrupted!\n", modname); return -EINVAL; } @@ -1059,8 +1193,11 @@ /* Announce */ printk(KERN_INFO "%s: loading %s (ID=%u)...\n", modname, (sfm->descr[0] != '\0') ? sfm->descr : "unknown firmware", - sfm->info.codeid) - ; + sfm->info.codeid); + + if(hw->type == SDLA_S514) + printk(KERN_INFO "%s: loading S514 adapter, CPU %c\n", + modname, hw->S514_cpu_no[0]); /* Scan through the list of compatible adapters and make sure our * adapter type is listed. @@ -1069,49 +1206,41 @@ (i < SFM_MAX_SDLA) && (sfm->info.adapter[i] != hw->type); ++i) ; - if (i == SFM_MAX_SDLA) - { + if (i == SFM_MAX_SDLA) { printk(KERN_ERR "%s: firmware is not compatible with S%u!\n", - modname, hw->type) + modname, hw->type); ; return -EINVAL; } + /* Make sure there is enough on-board memory */ - if (hw->memory < sfm->info.memsize) - { + if (hw->memory < sfm->info.memsize) { printk(KERN_ERR "%s: firmware needs %lu bytes of on-board memory!\n", - modname, sfm->info.memsize) - ; + modname, sfm->info.memsize); return -EINVAL; } /* Move code onto adapter */ - if (sdla_poke(hw, sfm->info.codeoffs, sfm->image, sfm->info.codesize)) - { + if (sdla_poke(hw, sfm->info.codeoffs, sfm->image, sfm->info.codesize)) { printk(KERN_ERR "%s: failed to load code segment!\n", - modname) - ; + modname); return -EIO; } /* Prepare boot-time configuration data and kick-off CPU */ sdla_bootcfg(hw, &sfm->info); - if (sdla_start(hw, sfm->info.startoffs)) - { + if (sdla_start(hw, sfm->info.startoffs)) { printk(KERN_ERR "%s: Damn... Adapter won't start!\n", - modname) - ; + modname); return -EIO; } /* position DPM window over the mailbox and enable interrupts */ - if (sdla_mapmem(hw, sfm->info.winoffs) || sdla_inten(hw)) - { + if (sdla_mapmem(hw, sfm->info.winoffs) || sdla_inten(hw)) { printk(KERN_ERR "%s: adapter hardware failure!\n", - modname) - ; + modname); return -EIO; } hw->fwid = sfm->info.codeid; /* set firmware ID */ @@ -1126,10 +1255,9 @@ int i; for (i = 0; i < SDLA_MAXIORANGE; ++i) - hw->regs[i] = 0 - ; - switch (hw->type) - { + hw->regs[i] = 0; + + switch (hw->type) { case SDLA_S502A: return init_s502a(hw); case SDLA_S502E: return init_s502e(hw); case SDLA_S503: return init_s503(hw); @@ -1172,23 +1300,28 @@ if (!sfminfo->datasize) return 0; /* nothing to do */ if (sdla_mapmem(hw, sfminfo->dataoffs) != 0) - return -EIO - ; - data = (void*)((u8 *)hw->dpmbase + (sfminfo->dataoffs - hw->vector)); - memset(data, 0, sfminfo->datasize); + return -EIO; + + if(hw->type == SDLA_S514) + data = (void*)(hw->dpmbase + sfminfo->dataoffs); + else + data = (void*)((u8 *)hw->dpmbase + + (sfminfo->dataoffs - hw->vector)); + + memset_io (data, 0, sfminfo->datasize); + + writeb (make_config_byte(hw), &data[0x00]); - data[0x00] = make_config_byte(hw); - switch (sfminfo->codeid) - { + switch (sfminfo->codeid) { case SFID_X25_502: case SFID_X25_508: - data[0x01] = 3; /* T1 timer */ - data[0x03] = 10; /* N2 */ - data[0x06] = 7; /* HDLC window size */ - data[0x0B] = 1; /* DTE */ - data[0x0C] = 2; /* X.25 packet window size */ - *(short*)&data[0x0D] = 128; /* default X.25 data size */ - *(short*)&data[0x0F] = 128; /* maximum X.25 data size */ + writeb (3, &data[0x01]); /* T1 timer */ + writeb (10, &data[0x03]); /* N2 */ + writeb (7, &data[0x06]); /* HDLC window size */ + writeb (1, &data[0x0B]); /* DTE */ + writeb (2, &data[0x0C]); /* X.25 packet window size */ + writew (128, &data[0x0D]); /* default X.25 data size */ + writew (128, &data[0x0F]); /* maximum X.25 data size */ break; } return 0; @@ -1201,16 +1334,15 @@ { unsigned char byte = 0; - switch (hw->pclk) - { + switch (hw->pclk) { case 5000: byte = 0x01; break; case 7200: byte = 0x02; break; case 8000: byte = 0x03; break; case 10000: byte = 0x04; break; case 16000: byte = 0x05; break; } - switch (hw->type) - { + + switch (hw->type) { case SDLA_S502E: byte |= 0x80; break; case SDLA_S503: byte |= 0x40; break; } @@ -1230,10 +1362,9 @@ unsigned char *bootp; int err, tmp, i; - if (!port) return -EFAULT; + if (!port && (hw->type != SDLA_S514)) return -EFAULT; - switch (hw->type) - { + switch (hw->type) { case SDLA_S502A: bootp = hw->dpmbase; bootp += 0x66; @@ -1243,6 +1374,7 @@ case SDLA_S503: case SDLA_S507: case SDLA_S508: + case SDLA_S514: bootp = hw->dpmbase; break; @@ -1253,12 +1385,11 @@ err = sdla_mapmem(hw, 0); if (err) return err; - *bootp = 0xC3; /* Z80: 'jp' opcode */ - bootp++; - *((unsigned short*)(bootp)) = addr; + writeb (0xC3, bootp); /* Z80: 'jp' opcode */ + bootp ++; + writew (addr, bootp); - switch (hw->type) - { + switch (hw->type) { case SDLA_S502A: _OUTB(port, 0x10); /* issue NMI to CPU */ hw->regs[0] = 0x10; @@ -1268,8 +1399,7 @@ _OUTB(port + 3, 0x01); /* start CPU */ hw->regs[3] = 0x01; for (i = 0; i < SDLA_IODELAY; ++i); - if (_INB(port) & 0x01) /* verify */ - { + if (_INB(port) & 0x01) { /* verify */ /* * Enabling CPU changes functionality of the * control register, so we have to reset its @@ -1287,8 +1417,7 @@ hw->regs[0] = tmp; /* update mirror */ for (i = 0; i < SDLA_IODELAY; ++i); if (!(_INB(port) & 0x01)) /* verify */ - return -EIO - ; + return -EIO; break; case SDLA_S507: @@ -1297,8 +1426,7 @@ hw->regs[0] = tmp; /* update mirror */ for (i = 0; i < SDLA_IODELAY; ++i); if (!(_INB(port) & 0x04)) /* verify */ - return -EIO - ; + return -EIO; break; case SDLA_S508: @@ -1307,8 +1435,11 @@ hw->regs[0] = tmp; /* update mirror */ for (i = 0; i < SDLA_IODELAY; ++i); if (!(_INB(port + 1) & 0x02)) /* verify */ - return -EIO - ; + return -EIO; + break; + + case SDLA_S514: + writeb (S514_CPU_START, hw->vector); break; default: @@ -1326,20 +1457,18 @@ int tmp, i; if (!detect_s502a(port)) - return -ENODEV - ; + return -ENODEV; + hw->regs[0] = 0x08; hw->regs[1] = 0xFF; /* Verify configuration options */ i = get_option_index(s502a_dpmbase_options, virt_to_phys(hw->dpmbase)); if (i == 0) - return -EINVAL - ; + return -EINVAL; tmp = s502a_hmcr[i - 1]; - switch (hw->dpmsize) - { + switch (hw->dpmsize) { case 0x2000: tmp |= 0x01; break; @@ -1367,18 +1496,15 @@ int tmp, i; if (!detect_s502e(port)) - return -ENODEV - ; + return -ENODEV; /* Verify configuration options */ i = get_option_index(s508_dpmbase_options, virt_to_phys(hw->dpmbase)); if (i == 0) - return -EINVAL - ; + return -EINVAL; tmp = s502e_hmcr[i - 1]; - switch (hw->dpmsize) - { + switch (hw->dpmsize) { case 0x2000: tmp |= 0x01; break; @@ -1411,18 +1537,15 @@ int tmp, i; if (!detect_s503(port)) - return -ENODEV - ; + return -ENODEV; /* Verify configuration options */ i = get_option_index(s508_dpmbase_options, virt_to_phys(hw->dpmbase)); if (i == 0) - return -EINVAL - ; + return -EINVAL; tmp = s502e_hmcr[i - 1]; - switch (hw->dpmsize) - { + switch (hw->dpmsize) { case 0x2000: tmp |= 0x01; break; @@ -1453,18 +1576,15 @@ int tmp, i; if (!detect_s507(port)) - return -ENODEV - ; + return -ENODEV; /* Verify configuration options */ i = get_option_index(s507_dpmbase_options, virt_to_phys(hw->dpmbase)); if (i == 0) - return -EINVAL - ; + return -EINVAL; tmp = s507_hmcr[i - 1]; - switch (hw->dpmsize) - { + switch (hw->dpmsize) { case 0x2000: tmp |= 0x01; break; @@ -1481,8 +1601,7 @@ hw->regs[0] = 0x01; for (i = 0; i < SDLA_IODELAY; ++i); /* delay */ if (!(_INB(port) & 0x20)) - return -EIO - ; + return -EIO; /* Setup dual-port memory window */ _OUTB(port + 1, tmp); @@ -1490,8 +1609,7 @@ /* Enable memory access */ tmp = hw->regs[0] | 0x04; - if (hw->irq) - { + if (hw->irq) { i = get_option_index(s508_irq_options, hw->irq); if (i) tmp |= s507_irqmask[i - 1]; } @@ -1510,14 +1628,12 @@ int tmp, i; if (!detect_s508(port)) - return -ENODEV - ; + return -ENODEV; /* Verify configuration options */ i = get_option_index(s508_dpmbase_options, virt_to_phys(hw->dpmbase)); if (i == 0) - return -EINVAL - ; + return -EINVAL; /* Setup memory configuration */ tmp = s508_hmcr[i - 1]; @@ -1550,13 +1666,11 @@ int i, j; if (!get_option_index(s502_port_options, port)) - return 0 - ; - for (j = 1; j < SDLA_MAXIORANGE; ++j) - { + return 0; + + for (j = 1; j < SDLA_MAXIORANGE; ++j) { if (_INB(port + j) != 0xFF) - return 0 - ; + return 0; for (i = 0; i < SDLA_IODELAY; ++i); /* delay */ } @@ -1565,18 +1679,15 @@ _OUTB(port, 0x08); for (i = 0; i < SDLA_IODELAY; ++i); /* delay */ if (_INB(port) != 0x40) - return 0 - ; + return 0; _OUTB(port, 0x00); for (i = 0; i < SDLA_IODELAY; ++i); /* delay */ if (_INB(port) != 0x40) - return 0 - ; + return 0; _OUTB(port, 0x04); for (i = 0; i < SDLA_IODELAY; ++i); /* delay */ if (_INB(port) != 0x44) - return 0 - ; + return 0; /* Reset adapter */ _OUTB(port, 0x08); @@ -1603,26 +1714,21 @@ int i, j; if (!get_option_index(s502_port_options, port)) - return 0 - ; - for (j = 1; j < SDLA_MAXIORANGE; ++j) - { + return 0; + for (j = 1; j < SDLA_MAXIORANGE; ++j) { if (_INB(port + j) != 0xFF) - return 0 - ; + return 0; for (i = 0; i < SDLA_IODELAY; ++i); /* delay */ } _OUTB(port + 3, 0); /* CPU control reg. */ for (i = 0; i < SDLA_IODELAY; ++i); /* delay */ if (_INB(port) != 0xF8) /* read status */ - return 0 - ; + return 0; _OUTB(port, 0x04); /* set bit 2 */ for (i = 0; i < SDLA_IODELAY; ++i); /* delay */ if (_INB(port) != 0xFC) /* verify */ - return 0 - ; + return 0; /* Reset adapter */ _OUTB(port, 0); @@ -1646,26 +1752,21 @@ int i, j; if (!get_option_index(s503_port_options, port)) - return 0 - ; - for (j = 1; j < SDLA_MAXIORANGE; ++j) - { + return 0; + for (j = 1; j < SDLA_MAXIORANGE; ++j) { if (_INB(port + j) != 0xFF) - return 0 - ; + return 0; for (i = 0; i < SDLA_IODELAY; ++i); /* delay */ } _OUTB(port, 0); /* reset control reg.*/ for (i = 0; i < SDLA_IODELAY; ++i); /* delay */ if (_INB(port) != 0xF0) /* read status */ - return 0 - ; + return 0; _OUTB(port, 0x04); /* set bit 2 */ for (i = 0; i < SDLA_IODELAY; ++i); /* delay */ if (_INB(port) != 0xF2) /* verify */ - return 0 - ; + return 0; /* Reset adapter */ _OUTB(port, 0); @@ -1689,27 +1790,22 @@ int tmp, i, j; if (!get_option_index(s508_port_options, port)) - return 0 - ; + return 0; tmp = _INB(port); - for (j = 1; j < S507_IORANGE; ++j) - { + for (j = 1; j < S507_IORANGE; ++j) { if (_INB(port + j) != tmp) - return 0 - ; + return 0; for (i = 0; i < SDLA_IODELAY; ++i); /* delay */ } _OUTB(port, 0x00); for (i = 0; i < SDLA_IODELAY; ++i); /* delay */ if ((_INB(port) & 0x7E) != 0x30) - return 0 - ; + return 0; _OUTB(port, 0x01); for (i = 0; i < SDLA_IODELAY; ++i); /* delay */ if ((_INB(port) & 0x7E) != 0x32) - return 0 - ; + return 0; /* Reset adapter */ _OUTB(port, 0x00); @@ -1732,24 +1828,205 @@ int i; if (!get_option_index(s508_port_options, port)) - return 0 - ; + return 0; _OUTB(port, 0x00); for (i = 0; i < SDLA_IODELAY; ++i); /* delay */ if ((_INB(port + 1) & 0x3F) != 0x00) - return 0 - ; + return 0; _OUTB(port, 0x10); for (i = 0; i < SDLA_IODELAY; ++i); /* delay */ if ((_INB(port + 1) & 0x3F) != 0x10) - return 0 - ; + return 0; /* Reset adapter */ _OUTB(port, 0x00); return 1; } +/*============================================================================ + * Detect s514 PCI adapter. + * Return 1 if detected o.k. or 0 if failed. + * Note: This test is destructive! Adapter will be left in shutdown + * state after the test. + */ +static int detect_s514 (sdlahw_t* hw) +{ + unsigned char CPU_no, slot_no; + int number_S514_cards = 0; + u32 S514_mem_base_addr = 0; + u32 ut_u32; + + struct pci_dev *pci_dev; + + +#ifdef CONFIG_PCI + if(!pci_present()) + { + printk(KERN_ERR "%s: PCI BIOS not present!\n", modname); + return 0; + } +#else + printk(KERN_ERR "%s: Linux not compiled for PCI usage!\n", modname); + return 0; +#endif + + /* + The 'setup()' procedure in 'sdlamain.c' passes the CPU number and the + slot number defined in 'router.conf' via the 'port' definition. + */ + CPU_no = hw->S514_cpu_no[0]; + slot_no = hw->S514_slot_no; + + printk(KERN_INFO "%s: detecting S514 card, CPU %c, slot #%d\n", + modname, CPU_no, slot_no); + + /* check to see that CPU A or B has been selected in 'router.conf' */ + switch(CPU_no) { + case S514_CPU_A: + case S514_CPU_B: + break; + + default: + printk(KERN_ERR "%s: S514 CPU definition invalid.\n", + modname); + printk(KERN_ERR "Must be 'A' or 'B'\n"); + return 0; + } + + number_S514_cards = find_s514_adapter(hw, 0); + if(!number_S514_cards) + return 0; + + /* we are using a single S514 adapter with a slot of 0 so re-read the */ /* location of this adapter */ + if((number_S514_cards == 1) && !slot_no) { + number_S514_cards = find_s514_adapter(hw, 1); + if(!number_S514_cards) { + printk(KERN_ERR "%s: Error finding PCI card\n", + modname); + return 0; + } + } + + pci_dev = hw->pci_dev; + /* read the physical memory base address */ + S514_mem_base_addr = (CPU_no == S514_CPU_A) ? + (pci_dev->base_address[1] & PCI_BASE_ADDRESS_MEM_MASK) : + (pci_dev->base_address[2] & PCI_BASE_ADDRESS_MEM_MASK); + + printk(KERN_INFO "%s: S514 PCI memory at 0x%X\n", + modname, S514_mem_base_addr); + if(!S514_mem_base_addr) { + if(CPU_no == S514_CPU_B) + printk(KERN_ERR "%s: CPU #B not present on the card\n", modname); + else + printk(KERN_ERR "%s: No PCI memory allocated to card\n", modname); + return 0; + } + + /* enable the PCI memory */ + pci_read_config_dword(pci_dev, + (CPU_no == S514_CPU_A) ? PCI_MAP0_DWORD : PCI_MAP1_DWORD, + &ut_u32); + pci_write_config_dword(pci_dev, + (CPU_no == S514_CPU_A) ? PCI_MAP0_DWORD : PCI_MAP1_DWORD, + (ut_u32 | PCI_MEMORY_ENABLE)); + + /* check the IRQ allocated and enable IRQ usage */ + if(!(hw->irq = pci_dev->irq)) { + printk(KERN_ERR "%s: IRQ not allocated to S514 adapter\n", + modname); + return 0; + } + pci_read_config_dword(pci_dev, PCI_INT_CONFIG, &ut_u32); + ut_u32 |= (CPU_no == S514_CPU_A) ? + PCI_ENABLE_IRQ_CPU_A : PCI_ENABLE_IRQ_CPU_B; + pci_write_config_dword(pci_dev, PCI_INT_CONFIG, ut_u32); + + printk(KERN_INFO "%s: IRQ %d allocated to the S514 card\n", + modname, hw->irq); + + /* map the physical PCI memory to virtual memory */ + (void *)hw->dpmbase = ioremap((unsigned long)S514_mem_base_addr, + (unsigned long)MAX_SIZEOF_S514_MEMORY); + /* map the physical control register memory to virtual memory */ + (void *)hw->vector = ioremap( + (unsigned long)(S514_mem_base_addr + S514_CTRL_REG_BYTE), + (unsigned long)16); + + if(!hw->dpmbase || !hw->vector) { + printk(KERN_ERR "%s: PCI virtual memory allocation failed\n", + modname); + return 0; + } + + /* halt the adapter */ + writeb (S514_CPU_HALT, hw->vector); + + return 1; +} + +/*============================================================================ + * Find the S514 PCI adapter in the PCI bus. + * Return the number of S514 adapters found (0 if no adapter found). + */ +static int find_s514_adapter(sdlahw_t* hw, char find_first_S514_card) +{ + unsigned char slot_no; + int number_S514_cards = 0; + char S514_found_in_slot = 0; + u16 PCI_subsys_vendor; + + struct pci_dev *pci_dev = NULL; + + slot_no = hw->S514_slot_no; + + while ((pci_dev = pci_find_device(V3_VENDOR_ID, V3_DEVICE_ID, pci_dev)) + != NULL) { + pci_read_config_word(pci_dev, PCI_SUBSYS_VENDOR_WORD, + &PCI_subsys_vendor); + if(PCI_subsys_vendor != SANGOMA_SUBSYS_VENDOR) + continue; + hw->pci_dev = pci_dev; + if(find_first_S514_card) + return(1); + number_S514_cards ++; + printk(KERN_INFO + "%s: S514 card found, slot #%d (devfn 0x%X)\n", + modname, ((pci_dev->devfn >> 3) & PCI_DEV_SLOT_MASK), + pci_dev->devfn); + if(slot_no && (((pci_dev->devfn >> 3) & PCI_DEV_SLOT_MASK) == + slot_no)) { + S514_found_in_slot = 1; + break; + } + } + + /* if no S514 adapter has been found, then exit */ + if(!number_S514_cards) { + printk(KERN_ERR "%s: no S514 adapters found\n", modname); + return 0; + } + /* if more than one S514 card has been found, then the user must have */ /* defined a slot number so that the correct adapter is used */ + else if((number_S514_cards > 1) && !slot_no) { + printk(KERN_ERR "%s: More than one S514 adapter found\n", + modname); + printk(KERN_ERR "Define a PCI slot number for this adapter\n"); + return 0; + } + /* if the user has specified a slot number and the S514 adapter has */ + /* not been found in that slot, then exit */ + else if (slot_no && !S514_found_in_slot) { + printk(KERN_ERR + "%s: S514 card not found in specified slot #%d\n", + modname, slot_no); + return 0; + } + + return (number_S514_cards); +} + + + /******* Miscellaneous ******************************************************/ /*============================================================================ @@ -1775,8 +2052,8 @@ int i; for (i = 1; i <= optlist[0]; ++i) - if ( optlist[i] == optval) return i - ; + if ( optlist[i] == optval) + return i; return 0; } @@ -1788,15 +2065,14 @@ { volatile unsigned char* p = ptr; - for (; len && (*p == 0xFF); --len, ++p) - { - *p = 0; /* attempt to write 0 */ - if (*p != 0xFF) /* still has to read 0xFF */ - { - *p = 0xFF; /* restore original value */ - break; /* not good */ - } - } + for (; len && (readb (p) == 0xFF); --len, ++p) { + writeb (0, p); /* attempt to write 0 */ + if (readb(p) != 0xFF) { /* still has to read 0xFF */ + writeb (0xFF, p);/* restore original value */ + break; /* not good */ + } + } + return len; } @@ -1811,28 +2087,28 @@ unsigned len_w = len >> 1; /* region len in words */ unsigned i; + for (i = 0, w_ptr = ptr; i < len_w; ++i, ++w_ptr) + writew (0xAA55, w_ptr); + for (i = 0, w_ptr = ptr; i < len_w; ++i, ++w_ptr) - *w_ptr = 0xAA55 - ; - for (i = 0, w_ptr = ptr; i < len_w; ++i, ++w_ptr) - if (*w_ptr != 0xAA55) - { - len_w = i; - break; - } - ; - for (i = 0, w_ptr = ptr; i < len_w; ++i, ++w_ptr) - *w_ptr = 0x55AA - ; - for (i = 0, w_ptr = ptr; i < len_w; ++i, ++w_ptr) - if (*w_ptr != 0x55AA) - { - len_w = i; - break; - } - ; - for (i = 0, w_ptr = ptr; i < len_w; ++i, ++w_ptr) *w_ptr = 0; - return len_w << 1; + if (readw (w_ptr) != 0xAA55) { + len_w = i; + break; + } + + for (i = 0, w_ptr = ptr; i < len_w; ++i, ++w_ptr) + writew (0x55AA, w_ptr); + + for (i = 0, w_ptr = ptr; i < len_w; ++i, ++w_ptr) + if (readw(w_ptr) != 0x55AA) { + len_w = i; + break; + } + + for (i = 0, w_ptr = ptr; i < len_w; ++i, ++w_ptr) + writew (0, w_ptr); + + return len_w << 1; } /*============================================================================ @@ -1843,10 +2119,8 @@ unsigned short crc = 0; unsigned mask, flag; - for (; len; --len, ++buf) - { - for (mask = 0x80; mask; mask >>= 1) - { + for (; len; --len, ++buf) { + for (mask = 0x80; mask; mask >>= 1) { flag = (crc & 0x8000); crc <<= 1; crc |= ((*buf & mask) ? 1 : 0); diff -u --recursive --new-file v2.2.13/linux/drivers/net/sdlamain.c linux/drivers/net/sdlamain.c --- v2.2.13/linux/drivers/net/sdlamain.c Mon Dec 28 11:05:14 1998 +++ linux/drivers/net/sdlamain.c Tue Jan 4 10:12:17 2000 @@ -1,16 +1,21 @@ /***************************************************************************** * sdlamain.c WANPIPE(tm) Multiprotocol WAN Link Driver. Main module. * -* Author: Gene Kozin -* Jaspreet Singh +* Author: Nenad Corbic +* Gideon Hack * -* Copyright: (c) 1995-1997 Sangoma Technologies Inc. +* Copyright: (c) 1995-1999 Sangoma Technologies Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * ============================================================================ +* Sep 23, 1999 Nenad Corbic Added support for SMP +* Sep 13, 1999 Nenad Corbic Each port is treated as a separate device. +* Jun 02, 1999 Gideon Hack Added support for the S514 adapter. +* Updates for Linux 2.2.X kernels. +* Sep 17, 1998 Jaspreet Singh Updated for 2.1.121+ kernel * Nov 28, 1997 Jaspreet Singh Changed DRV_RELEASE to 1 * Nov 10, 1997 Jaspreet Singh Changed sti() to restore_flags(); * Nov 06, 1997 Jaspreet Singh Changed DRV_VERSION to 4 and DRV_RELEASE to 0 @@ -29,6 +34,7 @@ * Jan 02, 1997 Gene Kozin Initial version. *****************************************************************************/ +#include #include /* OS configuration options */ #include /* offsetof(), etc. */ #include /* return codes */ @@ -42,7 +48,8 @@ #include /* WANPIPE common user API definitions */ #include /* kernel <-> user copy */ #include /* phys_to_virt() */ - +#include +#include /****** Defines & Macros ****************************************************/ @@ -52,8 +59,8 @@ #define STATIC static #endif -#define DRV_VERSION 4 /* version number */ -#define DRV_RELEASE 1 /* release (minor version) number */ +#define DRV_VERSION 5 /* version number */ +#define DRV_RELEASE 0 /* release (minor version) number */ #define MAX_CARDS 8 /* max number of adapters */ #ifndef CONFIG_WANPIPE_CARDS /* configurable option */ @@ -63,9 +70,11 @@ #define CMD_OK 0 /* normal firmware return code */ #define CMD_TIMEOUT 0xFF /* firmware command timed out */ #define MAX_CMD_RETRY 10 /* max number of firmware retries */ - /****** Function Prototypes *************************************************/ +extern void disable_irq(unsigned int); +extern void enable_irq(unsigned int); + /* Module entry points */ int init_module (void); void cleanup_module (void); @@ -75,13 +84,14 @@ static int shutdown (wan_device_t* wandev); static int ioctl (wan_device_t* wandev, unsigned cmd, unsigned long arg); -/* IOCTL hanlers */ +/* IOCTL handlers */ static int ioctl_dump (sdla_t* card, sdla_dump_t* u_dump); static int ioctl_exec (sdla_t* card, sdla_exec_t* u_exec); /* Miscellaneous functions */ STATIC void sdla_isr (int irq, void* dev_id, struct pt_regs *regs); STATIC void sdla_poll (void* data); +static void release_hw (sdla_t *card); /****** Global Data ********************************************************** * Note: All data must be explicitly initialized!!! @@ -90,7 +100,7 @@ /* private data */ static char drvname[] = "wanpipe"; static char fullname[] = "WANPIPE(tm) Multiprotocol Driver"; -static char copyright[] = "(c) 1995-1996 Sangoma Technologies Inc."; +static char copyright[] = "(c) 1995-1999 Sangoma Technologies Inc."; static int ncards = CONFIG_WANPIPE_CARDS; static int active = 0; /* number of active cards */ static sdla_t* card_array = NULL; /* adapter data space */ @@ -104,6 +114,7 @@ NULL /* .data */ }; + /******* Kernel Loadable Module Entry Points ********************************/ /*============================================================================ @@ -128,24 +139,23 @@ int cnt, err = 0; printk(KERN_INFO "%s v%u.%u %s\n", - fullname, DRV_VERSION, DRV_RELEASE, copyright) - ; + fullname, DRV_VERSION, DRV_RELEASE, copyright); /* Verify number of cards and allocate adapter data space */ ncards = min(ncards, MAX_CARDS); ncards = max(ncards, 1); card_array = kmalloc(sizeof(sdla_t) * ncards, GFP_KERNEL); if (card_array == NULL) - return -ENOMEM - ; + return -ENOMEM; + memset(card_array, 0, sizeof(sdla_t) * ncards); /* Register adapters with WAN router */ - for (cnt = 0; cnt < ncards; ++cnt) - { + for (cnt = 0; cnt < ncards; ++ cnt) { sdla_t* card = &card_array[cnt]; wan_device_t* wandev = &card->wandev; + card->next = NULL; sprintf(card->devname, "%s%d", drvname, cnt + 1); wandev->magic = ROUTER_MAGIC; wandev->name = card->devname; @@ -155,20 +165,18 @@ wandev->shutdown = &shutdown; wandev->ioctl = &ioctl; err = register_wan_device(wandev); - if (err) - { + if (err) { printk(KERN_ERR "%s: %s registration failed with error %d!\n", - drvname, card->devname, err) - ; + drvname, card->devname, err); break; } } - if (cnt) + if (cnt){ ncards = cnt; /* adjust actual number of cards */ - else - { + }else { kfree(card_array); + printk(KERN_INFO "IN Init Module: NO Cards registered\n"); err = -ENODEV; } return err; @@ -184,8 +192,7 @@ { int i; - for (i = 0; i < ncards; ++i) - { + for (i = 0; i < ncards; ++i) { sdla_t* card = &card_array[i]; unregister_wan_device(card->devname); } @@ -197,7 +204,7 @@ /******* WAN Device Driver Entry Points *************************************/ /*============================================================================ - * Setup/confugure WAN link driver. + * Setup/configure WAN link driver. * o check adapter state * o make sure firmware is present in configuration * o make sure I/O port and IRQ are specified @@ -217,7 +224,8 @@ { sdla_t* card; int err = 0; - int irq; + int irq=0; + int i; /* Sanity checks */ if ((wandev == NULL) || (wandev->private == NULL) || (conf == NULL)) @@ -227,80 +235,187 @@ if (wandev->state != WAN_UNCONFIGURED) return -EBUSY; /* already configured */ - if (!conf->data_size || (conf->data == NULL)) - { + printk(KERN_INFO "\nProcessing WAN device %s...\n", wandev->name); + + /* Initialize the counters for each wandev + * Used for counting number of times new_if and + * del_if get called. + */ + wandev->del_if_cnt = 0; + wandev->new_if_cnt = 0; + wandev->config_id = conf->config_id; + + if (!conf->data_size || (conf->data == NULL)) { printk(KERN_ERR "%s: firmware not found in configuration data!\n", wandev->name); return -EINVAL; } - if (conf->ioport <= 0) - { - printk(KERN_ERR + + /* only check I/O port and IRQ if not an S514 adapter */ + if(!conf->S514_CPU_no[0]) { + + if (conf->ioport <= 0) { + printk(KERN_ERR "%s: can't configure without I/O port address!\n", wandev->name); - return -EINVAL; - } + return -EINVAL; + } - if (conf->irq <= 0) - { - printk(KERN_ERR "%s: can't configure without IRQ!\n", + if (conf->irq <= 0) { + printk(KERN_ERR "%s: can't configure without IRQ!\n", wandev->name); - return -EINVAL; - } + return -EINVAL; + } - /* Make sure I/O port region is available */ - if (check_region(conf->ioport, SDLA_MAXIORANGE)) - { - printk(KERN_ERR "%s: I/O region 0x%X - 0x%X is in use!\n", - wandev->name, conf->ioport, - conf->ioport + SDLA_MAXIORANGE); - return -EINVAL; + /* Check for already loaded card with the same IO port and IRQ + * If found, copy its hardware configuration and use its + * resources (i.e. piggybacking) + */ + if (!card->configured){ + for (i = 0; i < ncards; i ++) { + sdla_t *nxt_card = &card_array[i]; + if (nxt_card->hw.port == conf->ioport && + nxt_card != card && + conf->config_id == WANCONFIG_CHDLC && + nxt_card->wandev.config_id == WANCONFIG_CHDLC){ + irq = nxt_card->hw.irq; + memcpy(&card->hw, &nxt_card->hw, sizeof(sdlahw_t)); + nxt_card->next = card; + card->next = nxt_card; + card->wandev.piggyback = WANOPT_YES; + } + } + + + /* Make sure I/O port region is available */ + if (check_region(conf->ioport, SDLA_MAXIORANGE) && + !card->wandev.piggyback) { + printk(KERN_ERR + "%s: I/O region 0x%X - 0x%X is in use!\n", + wandev->name, conf->ioport, + conf->ioport + SDLA_MAXIORANGE); + return -EINVAL; + } + } } - /* Allocate IRQ */ - irq = (conf->irq == 2) ? 9 : conf->irq; /* IRQ2 -> IRQ9 */ - if (request_irq(irq, sdla_isr, 0, wandev->name, card)) - { - printk(KERN_ERR "%s: can't reserve IRQ %d!\n", - wandev->name, irq); - return -EINVAL; + /* + For a S514 adapter, check for a possible configuration error in that + we are loading an adapter in the same slot as a previously loaded S514 + card. + */ + else { + if (!card->configured){ + for (i = 0; i < ncards; i ++) { + sdla_t* nxt_card = &card_array[i]; + if(nxt_card == card) + continue; + if((nxt_card->hw.type == SDLA_S514) && + (nxt_card->hw.S514_slot_no == conf->PCI_slot_no) && + (nxt_card->hw.S514_cpu_no[0] == conf->S514_CPU_no[0])&& + (conf->config_id == WANCONFIG_CHDLC)&& + (nxt_card->wandev.config_id == WANCONFIG_CHDLC)){ + + irq = nxt_card->hw.irq; + memcpy(&card->hw, &nxt_card->hw, sizeof(sdlahw_t)); + nxt_card->next = card; + card->next = nxt_card; + card->wandev.piggyback = WANOPT_YES; + } + } + } } - /* Configure hardware, load firmware, etc. */ - memset(&card->hw, 0, sizeof(sdlahw_t)); - card->hw.port = conf->ioport; - card->hw.irq = (conf->irq == 9) ? 2 : conf->irq; - /* Compute the virtual address of the card in kernel space */ - if(conf->maddr) - card->hw.dpmbase = phys_to_virt(conf->maddr); - else /* But 0 means NULL */ - card->hw.dpmbase = (void *)conf->maddr; - - card->hw.dpmsize = SDLA_WINDOWSIZE; - card->hw.type = conf->hw_opt[0]; - card->hw.pclk = conf->hw_opt[1]; - err = sdla_setup(&card->hw, conf->data, conf->data_size); - if (err) - { - free_irq(irq, card); - return err; - } + /* If the current card has already been configured + * or its a piggyback card, do not try to allocate + * resources. + */ + if (!card->wandev.piggyback && !card->configured){ + + /* Configure hardware, load firmware, etc. */ + memset(&card->hw, 0, sizeof(sdlahw_t)); + + /* for an S514 adapter, pass the CPU number and the slot number read */ + /* from 'router.conf' to the 'sdla_setup()' function via the 'port' */ + /* parameter */ + if (conf->S514_CPU_no[0]){ + + card->hw.S514_cpu_no[0] = conf->S514_CPU_no[0]; + card->hw.S514_slot_no = conf->PCI_slot_no; + printk(KERN_INFO "Setting CPU to %c and Slot to %i\n", + card->hw.S514_cpu_no[0], card->hw.S514_slot_no); + + }else{ + /* 508 Card io port and irq initialization */ + card->hw.port = conf->ioport; + card->hw.irq = (conf->irq == 9) ? 2 : conf->irq; + } + + + /* Compute the virtual address of the card in kernel space */ + if(conf->maddr) + card->hw.dpmbase = phys_to_virt(conf->maddr); + else /* But 0 means NULL */ + card->hw.dpmbase = (void *)conf->maddr; + + card->hw.dpmsize = SDLA_WINDOWSIZE; + /* set the adapter type if using an S514 adapter */ + card->hw.type = (conf->S514_CPU_no[0]) ? SDLA_S514 : conf->hw_opt[0]; + card->hw.pclk = conf->hw_opt[1]; + + err = sdla_setup(&card->hw, conf->data, conf->data_size); + if (err){ + return err; + } + + if(card->hw.type != SDLA_S514) + irq = (conf->irq == 2) ? 9 : conf->irq; /* IRQ2 -> IRQ9 */ + else + irq = card->hw.irq; + + /* request an interrupt vector - note that interrupts may be shared */ + /* when using the S514 PCI adapter */ + if(request_irq(irq, sdla_isr, + (card->hw.type == SDLA_S514) ? SA_SHIRQ : 0, wandev->name, card)){ - /* Intialize WAN device data space */ - wandev->irq = irq; - wandev->dma = 0; - wandev->ioport = card->hw.port; - wandev->maddr = card->hw.dpmbase; - wandev->msize = card->hw.dpmsize; - wandev->hw_opt[0] = card->hw.type; - wandev->hw_opt[1] = card->hw.pclk; - wandev->hw_opt[2] = card->hw.memory; - wandev->hw_opt[3] = card->hw.fwid; + printk(KERN_ERR "%s: Can't reserve IRQ %d!\n", wandev->name, irq); + return -EINVAL; + } + + }else{ + printk(KERN_INFO "%s: Card Configured %i or Piggybacking %i!\n", + wandev->name,card->configured,card->wandev.piggyback); + } + + + if (!card->configured){ + + #ifdef __SMP__ + /* Initialize the Spin lock */ + printk(KERN_INFO "%s: Initializing SMP\n",wandev->name); + spin_lock_init(&card->lock); + #endif + + /* Intialize WAN device data space */ + wandev->irq = irq; + wandev->dma = 0; + if(card->hw.type != SDLA_S514){ + wandev->ioport = card->hw.port; + }else{ + wandev->S514_cpu_no[0] = card->hw.S514_cpu_no[0]; + wandev->S514_slot_no = card->hw.S514_slot_no; + } + wandev->maddr = (unsigned long)card->hw.dpmbase; + wandev->msize = card->hw.dpmsize; + wandev->hw_opt[0] = card->hw.type; + wandev->hw_opt[1] = card->hw.pclk; + wandev->hw_opt[2] = card->hw.memory; + wandev->hw_opt[3] = card->hw.fwid; + } /* Protocol-specific initialization */ - switch (card->hw.fwid) - { + switch (card->hw.fwid) { #ifdef CONFIG_WANPIPE_X25 case SFID_X25_502: case SFID_X25_508: @@ -322,22 +437,49 @@ break; #endif +#ifdef CONFIG_WANPIPE_CHDLC + case SFID_CHDLC508: + case SFID_CHDLC514: +// if (conf->ft1){ +// printk(KERN_INFO "%s: Starting FT1 Configurator\n", +// card->devname); +// err = wpft1_init(card, conf); +// }else{ + err = wpc_init(card, conf); +// } + break; +#endif + +#ifdef CONFIG_WANPIPE_BSTRM + case SFID_BSC502: + err = bsc_init(card, conf); + break; +#endif + +#ifdef CONFIG_WANPIPE_HDLC + case SFID_HDLC508: + err = hdlc_init(card, conf); + break; +#endif + default: - printk(KERN_ERR "%s: this firmware is not supported!\n", - wandev->name) - ; + printk(KERN_ERR "%s: this firmware is not supported %X %X!\n", + wandev->name,card->hw.fwid,SFID_CHDLC508); err = -EINVAL; } - if (err) - { - sdla_down(&card->hw); - free_irq(irq, card); + + + if (err){ + release_hw(card); return err; + } + + /* Reserve I/O region and schedule background task */ -/* printk(KERN_INFO "about to request\n");*/ - request_region(card->hw.port, card->hw.io_range, wandev->name); -/* printk(KERN_INFO "request done\n");*/ + if(card->hw.type != SDLA_S514 && !card->wandev.piggyback) + request_region(card->hw.port, card->hw.io_range, wandev->name); + if (++active == 1) queue_task(&sdla_tq, &tq_scheduler); @@ -355,7 +497,7 @@ */ static int shutdown (wan_device_t* wandev) { - sdla_t* card; + sdla_t *card; /* sanity checks */ if ((wandev == NULL) || (wandev->private == NULL)) @@ -364,7 +506,7 @@ if (wandev->state == WAN_UNCONFIGURED) return 0; - /* If wee are in a critical section we lose */ + /* If we are in a critical section we lose */ if (test_and_set_bit(0, (void*)&wandev->critical)) return -EAGAIN; @@ -373,22 +515,67 @@ if (--active == 0) schedule(); /* stop background thread */ - -/* printk(KERN_INFO "active now %d\n", active); - printk(KERN_INFO "About to call sdla_down\n");*/ - sdla_down(&card->hw); -/* printk(KERN_INFO "sdla_down done\n"); - printk(KERN_INFO "About to call free_irq\n");*/ - free_irq(wandev->irq, card); -/* printk(KERN_INFO "free_irq done\n"); - printk(KERN_INFO "About to call release_region\n");*/ - release_region(card->hw.port, card->hw.io_range); -/* printk(KERN_INFO "release_region done\n");*/ + /* Release Resources */ + release_hw(card); + + /* only free the allocated I/O range if not an S514 adapter */ + if (wandev->hw_opt[0] != SDLA_S514 && !card->configured){ + release_region(card->hw.port, card->hw.io_range); + } + + if (!card->configured){ + memset(&card->hw, 0, sizeof(sdlahw_t)); + if (card->next){ + memset(&card->next->hw, 0, sizeof(sdlahw_t)); + } + } + wandev->critical = 0; return 0; } +static void release_hw (sdla_t *card) +{ + sdla_t *nxt_card; + + /* Check if next device exists */ + if (card->next){ + nxt_card = card->next; + /* If next device is down then release resources */ + if (nxt_card->wandev.state == WAN_UNCONFIGURED){ + if (card->wandev.piggyback){ + /* If this device is piggyback then use + * information of the master device + */ + printk(KERN_INFO "%s: Piggyback shutting down\n",card->devname); + sdla_down(&card->next->hw); + free_irq(card->wandev.irq, card->next); + card->configured = 0; + card->next->configured = 0; + card->wandev.piggyback = 0; + }else{ + /* Master device shutting down */ + printk(KERN_INFO "%s: Master shutting down\n",card->devname); + sdla_down(&card->hw); + free_irq(card->wandev.irq, card); + card->configured = 0; + card->next->configured = 0; + } + }else{ + printk(KERN_INFO "%s: Device still running\n", + nxt_card->devname); + card->configured = 1; + } + }else{ + printk(KERN_INFO "%s: Master shutting down\n",card->devname); + sdla_down(&card->hw); + free_irq(card->wandev.irq, card); + card->configured = 0; + } +} + + /*============================================================================ * Driver I/O control. * o verify arguments @@ -399,20 +586,29 @@ */ static int ioctl (wan_device_t* wandev, unsigned cmd, unsigned long arg) { + sdla_t* card; int err; /* sanity checks */ if ((wandev == NULL) || (wandev->private == NULL)) - return -EFAULT - ; + return -EFAULT; if (wandev->state == WAN_UNCONFIGURED) - return -ENODEV - ; - if (test_and_set_bit(0, (void*)&wandev->critical)) - return -EAGAIN - ; - switch (cmd) - { + return -ENODEV; + + card = wandev->private; + + if(card->hw.type != SDLA_S514){ + disable_irq(card->hw.irq); + } + + if (test_and_set_bit(0, (void*)&wandev->critical)) { + if(card->hw.type != SDLA_S514){ + enable_irq(card->hw.irq); + } + return -EAGAIN; + } + + switch (cmd) { case WANPIPE_DUMP: err = ioctl_dump(wandev->private, (void*)arg); break; @@ -424,11 +620,16 @@ default: err = -EINVAL; } - wandev->critical = 0; + + clear_bit(0, (void*)&wandev->critical); + if(card->hw.type != SDLA_S514){ + enable_irq(card->hw.irq); + } + return err; } -/****** Driver IOCTL Hanlers ************************************************/ +/****** Driver IOCTL Handlers ***********************************************/ /*============================================================================ * Dump adapter memory to user buffer. @@ -455,34 +656,47 @@ if ((dump.magic != WANPIPE_MAGIC) || (dump.offset + dump.length > card->hw.memory)) return -EINVAL; - + winsize = card->hw.dpmsize; save_flags(flags); cli(); /* >>> critical section start <<< */ - oldvec = card->hw.vector; - while (dump.length) - { - unsigned pos = dump.offset % winsize; /* current offset */ - unsigned long vec = dump.offset - pos; /* current vector */ - unsigned len = (dump.length > (winsize - pos)) ? - (winsize - pos) : dump.length - ; - if (sdla_mapmem(&card->hw, vec) != 0) /* relocate window */ - { - err = -EIO; - break; - } + + if(card->hw.type != SDLA_S514) { + oldvec = card->hw.vector; + while (dump.length) { + /* current offset */ + unsigned pos = dump.offset % winsize; + /* current vector */ + unsigned long vec = dump.offset - pos; + unsigned len = (dump.length > (winsize - pos)) ? + (winsize - pos) : dump.length; + /* relocate window */ + if (sdla_mapmem(&card->hw, vec) != 0) { + err = -EIO; + break; + } + /* FIXME::: COPY TO KERNEL BUFFER FIRST ?? */ + sti(); /* Not ideal but tough we have to do this */ + if(copy_to_user((void *)dump.ptr, + (u8 *)card->hw.dpmbase + pos, len)) + return -EFAULT; + cli(); + dump.length -= len; + dump.offset += len; + (char*)dump.ptr += len; + } + sdla_mapmem(&card->hw, oldvec);/* restore DPM window position */ + } + + else { /* FIXME::: COPY TO KERNEL BUFFER FIRST ?? */ - sti(); /* Not ideal but tough we have to do this */ - if(copy_to_user((void *)dump.ptr, - (u8 *)card->hw.dpmbase + pos, len)) - return -EFAULT; - cli(); - dump.length -= len; - dump.offset += len; - (char*)dump.ptr += len; + sti(); /* Not ideal but tough we have to do this */ + if(copy_to_user((void *)dump.ptr, + (u8 *)card->hw.dpmbase + dump.offset, dump.length)) + return -EFAULT; + cli(); } - sdla_mapmem(&card->hw, oldvec); /* restore DPM window position */ + restore_flags(flags); /* >>> critical section end <<< */ return err; } @@ -499,9 +713,10 @@ if (card->exec == NULL) return -ENODEV; - + if(copy_from_user((void*)&exec, (void*)u_exec, sizeof(sdla_exec_t))) return -EFAULT; + if ((exec.magic != WANPIPE_MAGIC) || (exec.cmd == NULL)) return -EINVAL; return card->exec(card, exec.cmd, exec.data); @@ -518,21 +733,110 @@ { #define card ((sdla_t*)dev_id) - if (!card || (card->wandev.state == WAN_UNCONFIGURED)) - return - ; - if (card->in_isr) - { - printk(KERN_WARNING "%s: interrupt re-entrancy on IRQ %d!\n", - card->devname, card->wandev.irq) - ; - return; + if(card->hw.type == SDLA_S514) { /* handle interrrupt on S514 */ + u32 int_status; + unsigned char CPU_no = card->hw.S514_cpu_no[0]; + unsigned char card_found_for_IRQ; + u8 IRQ_count = 0; + + for(;;) { + + read_S514_int_stat(&card->hw, &int_status); + + /* check if the interrupt is for this device */ + if(!((unsigned char)int_status & + (IRQ_CPU_A | IRQ_CPU_B))) + return; + + /* if the IRQ is for both CPUs on the same adapter, */ + /* then alter the interrupt status so as to handle */ + /* one CPU at a time */ + if(((unsigned char)int_status & (IRQ_CPU_A | IRQ_CPU_B)) + == (IRQ_CPU_A | IRQ_CPU_B)) { + int_status &= (CPU_no == S514_CPU_A) ? + ~IRQ_CPU_B : ~IRQ_CPU_A; + } + + card_found_for_IRQ = 0; + + /* check to see that the CPU number for this device */ + /* corresponds to the interrupt status read */ + switch (CPU_no) { + case S514_CPU_A: + if((unsigned char)int_status & + IRQ_CPU_A) + card_found_for_IRQ = 1; + break; + + case S514_CPU_B: + if((unsigned char)int_status & + IRQ_CPU_B) + card_found_for_IRQ = 1; + break; + } + + /* exit if the interrupt is for another CPU on the */ + /* same IRQ */ + if(!card_found_for_IRQ) + return; + + if (!card || + (card->wandev.state == WAN_UNCONFIGURED && !card->configured)){ + printk(KERN_INFO + "Received IRQ %d for CPU #%c\n", + irq, CPU_no); + printk(KERN_INFO + "IRQ for unconfigured adapter\n"); + S514_intack(&card->hw, int_status); + return; + } + + if (card->in_isr) { + printk(KERN_INFO + "%s: interrupt re-entrancy on IRQ %d\n", + card->devname, card->wandev.irq); + S514_intack(&card->hw, int_status); + return; + } + + S514_intack(&card->hw, int_status); + + if (card->isr) + card->isr(card); + + /* handle a maximum of two interrupts (one for each */ + /* CPU on the adapter) before returning */ + if((++ IRQ_count) == 2) + return; + } } - sdla_intack(&card->hw); - if (card->isr) - card->isr(card); + else { /* handle interrupt on S508 adapter */ + + if (!card || ((card->wandev.state == WAN_UNCONFIGURED) && !card->configured)) + return; + if (card->in_isr) { + printk(KERN_INFO + "%s: interrupt re-entrancy on IRQ %d!\n", + card->devname, card->wandev.irq); + return; + } + + /* Use spin lock only for S508 */ + +#ifdef __SMP__ + spin_lock(&card->lock); +#endif + sdla_intack(&card->hw); + if (card->isr) + card->isr(card); +#ifdef __SMP__ + spin_unlock(&card->lock); +#endif + + } + #undef card } @@ -547,13 +851,11 @@ { int i; - for (i = 0; i < ncards; ++i) - { + for (i = 0; i < ncards; ++i) { sdla_t* card = &card_array[i]; if ((card->wandev.state != WAN_UNCONFIGURED) && card->poll && - !card->wandev.critical) - { + !card->wandev.critical) { card->poll(card); } } @@ -594,26 +896,21 @@ save_flags(flags); cli(); - if (card->wandev.state != state) - { - switch (state) - { + if (card->wandev.state != state) { + switch (state) { case WAN_CONNECTED: printk (KERN_INFO "%s: link connected!\n", - card->devname) - ; + card->devname); break; case WAN_CONNECTING: printk (KERN_INFO "%s: link connecting...\n", - card->devname) - ; + card->devname); break; case WAN_DISCONNECTED: printk (KERN_INFO "%s: link disconnected!\n", - card->devname) - ; + card->devname); break; } card->wandev.state = state; diff -u --recursive --new-file v2.2.13/linux/drivers/net/shaper.c linux/drivers/net/shaper.c --- v2.2.13/linux/drivers/net/shaper.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/net/shaper.c Tue Jan 4 10:12:17 2000 @@ -557,6 +557,13 @@ { struct shaperconf *ss= (struct shaperconf *)&ifr->ifr_data; struct shaper *sh=dev->priv; + + if(ss->ss_cmd == SHAPER_SET_DEV || ss->ss_cmd == SHAPER_SET_SPEED) + { + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + } + switch(ss->ss_cmd) { case SHAPER_SET_DEV: diff -u --recursive --new-file v2.2.13/linux/drivers/net/sis900.c linux/drivers/net/sis900.c --- v2.2.13/linux/drivers/net/sis900.c Tue Jan 4 11:10:37 2000 +++ linux/drivers/net/sis900.c Tue Jan 4 10:12:17 2000 @@ -1,58 +1,31 @@ -/*****************************************************************************/ -/* sis900.c: A SiS 900 PCI Fast Ethernet driver for Linux. */ -/* */ -/* Silicon Integrated System Corporation */ -/* Revision: 1.05 Aug 7 1999 */ -/* */ -/*****************************************************************************/ - -/* - Modified from the driver which is originally written by Donald Becker. - - This software may be used and distributed according to the terms - of the GNU Public License (GPL), incorporated herein by reference. - Drivers based on this skeleton fall under the GPL and must retain - the authorship (implicit copyright) notice. - - The author may be reached as becker@tidalwave.net, or - Donald Becker - 312 Severn Ave. #W302 - Annapolis MD 21403 - - Support and updates [to the original skeleton] available at - http://www.tidalwave.net/~becker/pci-skeleton.html +/* sis900.c: A SiS 900/7016 PCI Fast Ethernet driver for Linux. + Copyright 1999 Silicon Integrated System Corporation + Revision: 1.05 Aug 7 1999 + + Modified from the driver which is originally written by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU Public License (GPL), incorporated herein by reference. + Drivers based on this skeleton fall under the GPL and must retain + the authorship (implicit copyright) notice. + + References: + SiS 7016 Fast Ethernet PCI Bus 10/100 Mbps LAN Controller with OnNow Support, + preliminary Rev. 1.0 Jan. 14, 1998 + SiS 900 Fast Ethernet PCI Bus 10/100 Mbps LAN Single Chip with OnNow Support, + preliminary Rev. 1.0 Nov. 10, 1998 + SiS 7014 Single Chip 100BASE-TX/10BASE-T Physical Layer Solution, + preliminary Rev. 1.0 Jan. 18, 1998 + http://www.sis.com.tw/support/databook.htm + + Rev 1.06.02 Nov. 23 1999 Ollie Lho + Rev 1.06.01 Nov. 16 1999 Ollie Lho CRC calculation provide by Joseph Zbiciak (im14u2c@primenet.com) + Rev 1.06 Nov. 4 1999 Ollie Lho (ollie@sis.com.tw) Second release + Rev 1.05.05 Oct. 29 1999 Ollie Lho (ollie@sis.com.tw) Single buffer Tx/Rx + Chin-Shan Li (lcs@sis.com.tw) Added AMD Am79c901 HomePNA PHY support + Rev 1.05 Aug. 7 1999 Jim Huang (cmhuang@sis.com.tw) Initial release */ -static const char *version = -"sis900.c:v1.05 8/07/99\n"; - -static int max_interrupt_work = 20; -#define sis900_debug debug -static int sis900_debug = 0; - -static int multicast_filter_limit = 128; - -#define MAX_UNITS 8 /* More are supported, limit only on options */ -static int speeds[MAX_UNITS] = {100, 100, 100, 100, 100, 100, 100, 100}; -static int full_duplex[MAX_UNITS] = {1, 1, 1, 1, 1, 1, 1, 1}; - -#define TX_BUF_SIZE 1536 -#define RX_BUF_SIZE 1536 - -#define TX_DMA_BURST 0 -#define RX_DMA_BURST 0 -#define TX_FIFO_THRESH 16 -#define TxDRNT_100 (1536>>5) -#define TxDRNT_10 16 -#define RxDRNT_100 8 -#define RxDRNT_10 8 -#define TRUE 1 -#define FALSE 0 - -/* Operational parameters that usually are not changed. */ -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT (4*HZ) - #include #include #include @@ -65,680 +38,321 @@ #include #include #include + #include #include #include /* Processor type for cache alignment. */ #include #include - -#define RUN_AT(x) (jiffies + (x)) - #include -#if LINUX_VERSION_CODE < 0x20123 -#define test_and_set_bit(val, addr) set_bit(val, addr) -#endif -#if LINUX_VERSION_CODE <= 0x20139 -#define net_device_stats enet_statistics -#else -#define NETSTATS_VER2 -#endif -#if LINUX_VERSION_CODE < 0x20155 || defined(CARDBUS) -/* Grrrr, the PCI code changed, but did not consider CardBus... */ -#include -#define PCI_SUPPORT_VER1 -#else -#define PCI_SUPPORT_VER2 -#endif -#if LINUX_VERSION_CODE < 0x20159 -#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE); -#else -#define dev_free_skb(skb) dev_kfree_skb(skb); -#endif +#include "sis900.h" -/* The I/O extent. */ -#define SIS900_TOTAL_SIZE 0x100 +static const char *version = +"sis900.c: v1.06.03 12/23/99\n"; -/* This table drives the PCI probe routines. It's mostly boilerplate in all - of the drivers, and will likely be provided by some future kernel. - Note the matching code -- the first table entry matchs all 56** cards but - second only the 1234 card. -*/ +static int max_interrupt_work = 20; +#define sis900_debug debug +static int sis900_debug = 0; -enum pci_flags_bit { - PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, -}; +static int multicast_filter_limit = 128; -struct pci_id_info { +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (4*HZ) + +struct mac_chip_info { const char *name; - u16 vendor_id, device_id, device_id_mask, flags; - int io_size; - struct device *(*probe1)(int pci_bus, int pci_devfn, struct device *dev, - long ioaddr, int irq, int chip_idx, int fnd_cnt); + u16 vendor_id, device_id, flags; + int io_size; + struct device *(*probe) (struct mac_chip_info *mac, struct pci_dev * pci_dev, + struct device * net_dev); }; +static struct device * sis900_mac_probe (struct mac_chip_info * mac, struct pci_dev * pci_dev, + struct device * net_dev); -static struct device * sis900_probe1(int pci_bus, int pci_devfn, - struct device *dev, long ioaddr, - int irq, int chp_idx, int fnd_cnt); - -static struct pci_id_info pci_tbl[] = -{{ "SiS 900 PCI Fast Ethernet", - 0x1039, 0x0900, 0xffff, PCI_USES_IO|PCI_USES_MASTER, 0x100, sis900_probe1}, - { "SiS 7016 PCI Fast Ethernet", - 0x1039, 0x7016, 0xffff, PCI_USES_IO|PCI_USES_MASTER, 0x100, sis900_probe1}, - {0,}, /* 0 terminated list. */ +static struct mac_chip_info mac_chip_table[] = { + { "SiS 900 PCI Fast Ethernet", PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_900, + PCI_COMMAND_IO|PCI_COMMAND_MASTER, SIS900_TOTAL_SIZE, sis900_mac_probe}, + { "SiS 7016 PCI Fast Ethernet",PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7016, + PCI_COMMAND_IO|PCI_COMMAND_MASTER, SIS900_TOTAL_SIZE, sis900_mac_probe}, + {0,}, /* 0 terminated list. */ }; -/* The capability table matches the chip table above. */ -enum {HAS_MII_XCVR=0x01, HAS_CHIP_XCVR=0x02, HAS_LNK_CHNG=0x04}; -static int sis_cap_tbl[] = { - HAS_MII_XCVR|HAS_CHIP_XCVR|HAS_LNK_CHNG, - HAS_MII_XCVR|HAS_CHIP_XCVR|HAS_LNK_CHNG, +static void sis900_read_mode(struct device *net_dev, int phy_addr, int *speed, int *duplex); +static void amd79c901_read_mode(struct device *net_dev, int phy_addr, int *speed, int *duplex); + +static struct mii_chip_info { + const char * name; + u16 phy_id0; + u16 phy_id1; + void (*read_mode) (struct device *net_dev, int phy_addr, int *speed, int *duplex); +} mii_chip_table[] = { + {"SiS 900 Internal MII PHY", 0x001d, 0x8000, sis900_read_mode}, + {"SiS 7014 Physical Layer Solution", 0x0016, 0xf830,sis900_read_mode}, + {"AMD 79C901 10BASE-T PHY", 0x0000, 0x35b9, amd79c901_read_mode}, + {"AMD 79C901 HomePNA PHY", 0x0000, 0x35c8, amd79c901_read_mode}, + {0,}, }; -/* The rest of these values should never change. */ -#define NUM_TX_DESC 16 /* Number of Tx descriptor registers. */ -#define NUM_RX_DESC 8 /* Number of Rx descriptor registers. */ - -/* Symbolic offsets to registers. */ -enum SIS900_registers { - cr=0x0, //Command Register - cfg=0x4, //Configuration Register - mear=0x8, //EEPROM Access Register - ptscr=0xc, //PCI Test Control Register - isr=0x10, //Interrupt Status Register - imr=0x14, //Interrupt Mask Register - ier=0x18, //Interrupt Enable Register - epar=0x18, //Enhanced PHY Access Register - txdp=0x20, //Transmit Descriptor Pointer Register - txcfg=0x24, //Transmit Configuration Register - rxdp=0x30, //Receive Descriptor Pointer Register - rxcfg=0x34, //Receive Configuration Register - flctrl=0x38, //Flow Control Register - rxlen=0x3c, //Receive Packet Length Register - rfcr=0x48, //Receive Filter Control Register - rfdr=0x4C, //Receive Filter Data Register - pmctrl=0xB0, //Power Management Control Register - pmer=0xB4 //Power Management Wake-up Event Register +struct mii_phy { + struct mii_phy * next; + struct mii_chip_info * chip_info; + int phy_addr; + u16 status; }; -#define RESET 0x00000100 -#define SWI 0x00000080 -#define RxRESET 0x00000020 -#define TxRESET 0x00000010 -#define RxDIS 0x00000008 -#define RxENA 0x00000004 -#define TxDIS 0x00000002 -#define TxENA 0x00000001 - -#define BISE 0x80000000 -#define EUPHCOM 0x00000100 -#define REQALG 0x00000080 -#define SB 0x00000040 -#define POW 0x00000020 -#define EXD 0x00000010 -#define PESEL 0x00000008 -#define LPM 0x00000004 -#define BEM 0x00000001 - -/* Interrupt register bits, using my own meaningful names. */ -#define WKEVT 0x10000000 -#define TxPAUSEEND 0x08000000 -#define TxPAUSE 0x04000000 -#define TxRCMP 0x02000000 -#define RxRCMP 0x01000000 -#define DPERR 0x00800000 -#define SSERR 0x00400000 -#define RMABT 0x00200000 -#define RTABT 0x00100000 -#define RxSOVR 0x00010000 -#define HIBERR 0x00008000 -#define SWINT 0x00001000 -#define MIBINT 0x00000800 -#define TxURN 0x00000400 -#define TxIDLE 0x00000200 -#define TxERR 0x00000100 -#define TxDESC 0x00000080 -#define TxOK 0x00000040 -#define RxORN 0x00000020 -#define RxIDLE 0x00000010 -#define RxEARLY 0x00000008 -#define RxERR 0x00000004 -#define RxDESC 0x00000002 -#define RxOK 0x00000001 - -#define IE 0x00000001 - -#define TxCSI 0x80000000 -#define TxHBI 0x40000000 -#define TxMLB 0x20000000 -#define TxATP 0x10000000 -#define TxIFG 0x0C000000 -#define TxMXF 0x03800000 -#define TxMXF_shift 0x23 -#define TxMXDMA 0x00700000 -#define TxMXDMA_shift 20 -#define TxRTCNT 0x000F0000 -#define TxRTCNT_shift 16 -#define TxFILLT 0x00007F00 -#define TxFILLT_shift 8 -#define TxDRNT 0x0000007F - -#define RxAEP 0x80000000 -#define RxARP 0x40000000 -#define RxATP 0x10000000 -#define RxAJAB 0x08000000 -#define RxMXF 0x03800000 -#define RxMXF_shift 23 -#define RxMXDMA 0x00700000 -#define RxMXDMA_shift 20 -#define RxDRNT 0x0000007F - -#define RFEN 0x80000000 -#define RFAAB 0x40000000 -#define RFAAM 0x20000000 -#define RFAAP 0x10000000 -#define RFPromiscuous (RFAAB|RFAAM|RFAAP) -#define RFAA_shift 28 -#define RFEP 0x00070000 -#define RFEP_shift 16 - -#define RFDAT 0x0000FFFF - -#define OWN 0x80000000 -#define MORE 0x40000000 -#define INTR 0x20000000 -#define OK 0x08000000 -#define DSIZE 0x00000FFF - -#define SUPCRC 0x10000000 -#define ABORT 0x04000000 -#define UNDERRUN 0x02000000 -#define NOCARRIER 0x01000000 -#define DEFERD 0x00800000 -#define EXCDEFER 0x00400000 -#define OWCOLL 0x00200000 -#define EXCCOLL 0x00100000 -#define COLCNT 0x000F0000 - -#define INCCRC 0x10000000 -// ABORT 0x04000000 -#define OVERRUN 0x02000000 -#define DEST 0x01800000 -#define BCAST 0x01800000 -#define MCAST 0x01000000 -#define UNIMATCH 0x00800000 -#define TOOLONG 0x00400000 -#define RUNT 0x00200000 -#define RXISERR 0x00100000 -#define CRCERR 0x00080000 -#define FAERR 0x00040000 -#define LOOPBK 0x00020000 -#define RXCOL 0x00010000 - -#define EuphLiteEEMACAddr 0x08 -#define EuphLiteEEVendorID 0x02 -#define EuphLiteEEDeviceID 0x03 -#define EuphLiteEECardTypeRev 0x0b -#define EuphLiteEEPlexusRev 0x0c -#define EuphLiteEEChecksum 0x0f - -#define RXSTS_shift 18 -#define OWN 0x80000000 -#define MORE 0x40000000 -#define INTR 0x20000000 -#define OK 0x08000000 -#define DSIZE 0x00000FFF -/* MII register offsets */ -#define MII_CONTROL 0x0000 -#define MII_STATUS 0x0001 -#define MII_PHY_ID0 0x0002 -#define MII_PHY_ID1 0x0003 -#define MII_ANAR 0x0004 -#define MII_ANLPAR 0x0005 -#define MII_ANER 0x0006 -/* MII Control register bit definitions. */ -#define MIICNTL_FDX 0x0100 -#define MIICNTL_RST_AUTO 0x0200 -#define MIICNTL_ISOLATE 0x0400 -#define MIICNTL_PWRDWN 0x0800 -#define MIICNTL_AUTO 0x1000 -#define MIICNTL_SPEED 0x2000 -#define MIICNTL_LPBK 0x4000 -#define MIICNTL_RESET 0x8000 -/* MII Status register bit significance. */ -#define MIISTAT_EXT 0x0001 -#define MIISTAT_JAB 0x0002 -#define MIISTAT_LINK 0x0004 -#define MIISTAT_CAN_AUTO 0x0008 -#define MIISTAT_FAULT 0x0010 -#define MIISTAT_AUTO_DONE 0x0020 -#define MIISTAT_CAN_T 0x0800 -#define MIISTAT_CAN_T_FDX 0x1000 -#define MIISTAT_CAN_TX 0x2000 -#define MIISTAT_CAN_TX_FDX 0x4000 -#define MIISTAT_CAN_T4 0x8000 -/* MII NWAY Register Bits ... -** valid for the ANAR (Auto-Negotiation Advertisement) and -** ANLPAR (Auto-Negotiation Link Partner) registers */ -#define MII_NWAY_NODE_SEL 0x001f -#define MII_NWAY_CSMA_CD 0x0001 -#define MII_NWAY_T 0x0020 -#define MII_NWAY_T_FDX 0x0040 -#define MII_NWAY_TX 0x0080 -#define MII_NWAY_TX_FDX 0x0100 -#define MII_NWAY_T4 0x0200 -#define MII_NWAY_RF 0x2000 -#define MII_NWAY_ACK 0x4000 -#define MII_NWAY_NP 0x8000 - -/* MII Auto-Negotiation Expansion Register Bits */ -#define MII_ANER_PDF 0x0010 -#define MII_ANER_LP_NP_ABLE 0x0008 -#define MII_ANER_NP_ABLE 0x0004 -#define MII_ANER_RX_PAGE 0x0002 -#define MII_ANER_LP_AN_ABLE 0x0001 -#define HALF_DUPLEX 1 -#define FDX_CAPABLE_DUPLEX_UNKNOWN 2 -#define FDX_CAPABLE_HALF_SELECTED 3 -#define FDX_CAPABLE_FULL_SELECTED 4 -#define HW_SPEED_UNCONFIG 0 -#define HW_SPEED_10_MBPS 10 -#define HW_SPEED_100_MBPS 100 -#define HW_SPEED_DEFAULT (HW_SPEED_10_MBPS) - -#define ACCEPT_ALL_PHYS 0x01 -#define ACCEPT_ALL_MCASTS 0x02 -#define ACCEPT_ALL_BCASTS 0x04 -#define ACCEPT_ALL_ERRORS 0x08 -#define ACCEPT_CAM_QUALIFIED 0x10 -#define MAC_LOOPBACK 0x20 -//#define FDX_CAPABLE_FULL_SELECTED 4 -#define CRC_SIZE 4 -#define MAC_HEADER_SIZE 14 - -typedef struct _EuphLiteDesc { - u32 llink; - unsigned char* buf; - u32 physAddr; - /* Hardware sees the physical address of descriptor */ - u32 plink; +typedef struct _BufferDesc { + u32 link; u32 cmdsts; - u32 bufPhys; -} EuphLiteDesc; + u32 bufptr; +} BufferDesc; struct sis900_private { - char devname[8]; /* Used only for kernel debugging. */ - const char *product_name; struct device *next_module; - int chip_id; - int chip_revision; - unsigned char pci_bus, pci_devfn; -#if LINUX_VERSION_CODE > 0x20139 struct net_device_stats stats; -#else - struct enet_statistics stats; -#endif - struct timer_list timer; /* Media selection timer. */ - unsigned int cur_rx; /* Index into the Rx buffer of next Rx pkt. */ - unsigned int cur_tx, dirty_tx, tx_flag; - - /* The saved address of a sent-in-place packet/buffer, for skfree(). */ - struct sk_buff* tx_skbuff[NUM_TX_DESC]; - EuphLiteDesc tx_buf[NUM_TX_DESC]; /* Tx bounce buffers */ - EuphLiteDesc rx_buf[NUM_RX_DESC]; - unsigned char *rx_bufs; - unsigned char *tx_bufs; /* Tx bounce buffer region. */ - char phys[4]; /* MII device addresses. */ - int phy_idx; /* Support Max 4 PHY */ - u16 pmd_status; - unsigned int tx_full; /* The Tx queue is full. */ - int MediaSpeed; /* user force speed */ - int MediaDuplex; /* user force duplex */ - int full_duplex; /* Full/Half-duplex. */ - int speeds; /* 100/10 Mbps. */ - u16 LinkOn; - u16 LinkChange; + struct pci_dev * pci_dev; + + struct mac_chip_info * mac; + struct mii_phy * mii; + unsigned int cur_phy; + + struct timer_list timer; /* Link status detection timer. */ + unsigned int cur_rx, dirty_rx; + unsigned int cur_tx, dirty_tx; + + /* The saved address of a sent/receive-in-place packet buffer */ + struct sk_buff *tx_skbuff[NUM_TX_DESC]; + struct sk_buff *rx_skbuff[NUM_RX_DESC]; + BufferDesc tx_ring[NUM_TX_DESC]; + BufferDesc rx_ring[NUM_RX_DESC]; + unsigned int tx_full; /* The Tx queue is full. */ + + int LinkOn; }; #ifdef MODULE #if LINUX_VERSION_CODE > 0x20115 MODULE_AUTHOR("Jim Huang "); MODULE_DESCRIPTION("SiS 900 PCI Fast Ethernet driver"); -MODULE_PARM(speeds, "1-" __MODULE_STRING(MAX_UNITS) "i"); -MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); MODULE_PARM(multicast_filter_limit, "i"); MODULE_PARM(max_interrupt_work, "i"); MODULE_PARM(debug, "i"); #endif #endif -static int sis900_open(struct device *dev); +static int sis900_open(struct device *net_dev); +static int sis900_mii_probe (struct device * net_dev); +static void sis900_init_rxfilter (struct device * net_dev); static u16 read_eeprom(long ioaddr, int location); -static int mdio_read(struct device *dev, int phy_id, int location); -static void mdio_write(struct device *dev, int phy_id, int location, int val); +static u16 mdio_read(struct device *net_dev, int phy_id, int location); +static void mdio_write(struct device *net_dev, int phy_id, int location, int val); static void sis900_timer(unsigned long data); -static void sis900_tx_timeout(struct device *dev); -static void sis900_init_ring(struct device *dev); -static int sis900_start_xmit(struct sk_buff *skb, struct device *dev); -static int sis900_rx(struct device *dev); +static void sis900_check_mode (struct device *net_dev, struct mii_phy *mii_phy); +static void sis900_tx_timeout(struct device *net_dev); +static void sis900_init_tx_ring(struct device *net_dev); +static void sis900_init_rx_ring(struct device *net_dev); +static int sis900_start_xmit(struct sk_buff *skb, struct device *net_dev); +static int sis900_rx(struct device *net_dev); +static void sis900_finish_xmit (struct device *net_dev); static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs); -static int sis900_close(struct device *dev); -static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd); -static struct enet_statistics *sis900_get_stats(struct device *dev); -static void set_rx_mode(struct device *dev); -static void sis900_reset(struct device *dev); -static u16 elAutoNegotiate(struct device *dev, int phy_id, int *duplex, int *speed); -static void elSetCapability(struct device *dev, int phy_id, int duplex, int speed); -static u16 elPMDreadMode(struct device *dev, int phy_id, int *speed, int *duplex); -static u16 elMIIpollBit(struct device *dev, int phy_id, int location, u16 mask, u16 polarity, u16 *value); -static void elSetMediaType(struct device *dev, int speed, int duplex); +static int sis900_close(struct device *net_dev); +static int mii_ioctl(struct device *net_dev, struct ifreq *rq, int cmd); +static struct enet_statistics *sis900_get_stats(struct device *net_dev); +static u16 sis900_compute_hashtable_index(u8 *addr); +static void set_rx_mode(struct device *net_dev); +static void sis900_reset(struct device *net_dev); /* A list of all installed SiS900 devices, for removing the driver module. */ static struct device *root_sis900_dev = NULL; -/* Ideally we would detect all network cards in slot order. That would - be best done a central PCI probe dispatch, which wouldn't work - well when dynamically adding drivers. So instead we detect just the - SiS 900 cards in slot order. */ - -int sis900_probe(struct device *dev) -{ - int cards_found = 0; - int pci_index = 0; - unsigned char pci_bus, pci_device_fn; - - if ( ! pcibios_present()) - return -ENODEV; - - for (;pci_index < 0xff; pci_index++) { - u16 vendor, device, pci_command, new_command; - int chip_idx, irq; - long ioaddr; - - if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, - pci_index, - &pci_bus, &pci_device_fn) - != PCIBIOS_SUCCESSFUL) { - break; - } - pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, - &vendor); - pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, - &device); - - for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++) - if (vendor == pci_tbl[chip_idx].vendor_id && - (device & pci_tbl[chip_idx].device_id_mask) == - pci_tbl[chip_idx].device_id) - break; - if (pci_tbl[chip_idx].vendor_id == 0) /* Compiled out! */ - continue; - - { -#if defined(PCI_SUPPORT_VER2) - struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn); - ioaddr = pdev->base_address[0] & ~3; - irq = pdev->irq; -#else - u32 pci_ioaddr; - u8 pci_irq_line; - pcibios_read_config_byte(pci_bus, pci_device_fn, - PCI_INTERRUPT_LINE, &pci_irq_line); - pcibios_read_config_dword(pci_bus, pci_device_fn, - PCI_BASE_ADDRESS_0, &pci_ioaddr); - ioaddr = pci_ioaddr & ~3; - irq = pci_irq_line; -#endif - } - - if ((pci_tbl[chip_idx].flags & PCI_USES_IO) && - check_region(ioaddr, pci_tbl[chip_idx].io_size)) - continue; - - /* Activate the card: fix for brain-damaged Win98 BIOSes. */ - pcibios_read_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, &pci_command); - new_command = pci_command | (pci_tbl[chip_idx].flags & 7); - if (pci_command != new_command) { - printk(KERN_INFO " The PCI BIOS has not enabled the" - " device at %d/%d!" - "Updating PCI command %4.4x->%4.4x.\n", - pci_bus, pci_device_fn, - pci_command, new_command); +/* walk through every ethernet PCI devices to see if some of them are matched with our card list*/ +int sis900_probe (struct device * net_dev) +{ + int found = 0; + struct pci_dev * pci_dev = NULL; + + if (!pci_present()) + return -ENODEV; + + while ((pci_dev = pci_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, pci_dev)) != NULL) { + /* pci_dev contains all ethernet devices */ + u32 pci_io_base; + struct mac_chip_info * mac; + + for (mac = mac_chip_table; mac->vendor_id; mac++) { + /* try to match our card list */ + if (pci_dev->vendor == mac->vendor_id && + pci_dev->device == mac->device_id) + break; + } + + if (mac->vendor_id == 0) + /* pci_dev does not match any of our cards */ + continue; + + /* now, pci_dev should be either 900 or 7016 */ + pci_io_base = pci_dev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; + if ((mac->flags & PCI_COMMAND_IO ) && + check_region(pci_io_base, mac->io_size)) + continue; + + /* setup various bits in PCI command register */ + pci_set_master(pci_dev); + + /* do the real low level jobs */ + net_dev = mac->probe(mac, pci_dev, net_dev); + + if (net_dev != NULL) { + found++; + } + net_dev = NULL; + } + return found ? 0 : -ENODEV; +} - pcibios_write_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, new_command); - } +static struct device * sis900_mac_probe (struct mac_chip_info * mac, struct pci_dev * pci_dev, + struct device * net_dev) +{ + struct sis900_private *sis_priv; + long ioaddr = pci_dev->base_address[0] & ~3; + int irq = pci_dev->irq; + static int did_version = 0; + u16 signature; + int i; + + if (did_version++ == 0) + printk(KERN_INFO "%s", version); + + if ((net_dev = init_etherdev(net_dev, 0)) == NULL) + return NULL; + + /* check to see if we have sane EEPROM */ + signature = (u16) read_eeprom(ioaddr, EEPROMSignature); + if (signature == 0xffff || signature == 0x0000) { + printk (KERN_INFO "%s: Error EEPROM read: %x\n", + net_dev->name, signature); + unregister_netdevice(net_dev); + return NULL; + } - dev = pci_tbl[chip_idx].probe1(pci_bus, - pci_device_fn, - dev, - ioaddr, - irq, - chip_idx, - cards_found); - - if (dev && (pci_tbl[chip_idx].flags & PCI_COMMAND_MASTER)) { - u8 pci_latency; - - pcibios_read_config_byte(pci_bus, pci_device_fn, - PCI_LATENCY_TIMER, &pci_latency); - - if (pci_latency < 32) { - printk(KERN_NOTICE " PCI latency timer (CFLT) is " - "unreasonably low at %d. Setting to 64 clocks.\n", - pci_latency); - pcibios_write_config_byte(pci_bus, pci_device_fn, - PCI_LATENCY_TIMER, 64); - } - } - dev = 0; - cards_found++; - } - return cards_found ? 0 : -ENODEV; -} + printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ", net_dev->name, mac->name, + ioaddr, irq); -static struct device * sis900_probe1( int pci_bus, - int pci_devfn, - struct device *dev, - long ioaddr, - int irq, - int chip_idx, - int found_cnt) -{ - static int did_version = 0; /* Already printed version info. */ - struct sis900_private *tp; - u16 status; - int duplex = found_cnt < MAX_UNITS ? full_duplex[found_cnt] : 0 ; - int speed = found_cnt < MAX_UNITS ? speeds[found_cnt] : 0 ; - int phy=0, phy_idx=0, i; - - if (did_version++ == 0) - printk(KERN_INFO "%s", version); - - dev = init_etherdev(dev, 0); - - if(dev==NULL) - return NULL; - - printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ", - dev->name, pci_tbl[chip_idx].name, ioaddr, irq); - - if ((u16)read_eeprom(ioaddr, EuphLiteEEVendorID) != 0xffff) { - for (i = 0; i < 3; i++) - ((u16 *)(dev->dev_addr))[i] = - read_eeprom(ioaddr,i+EuphLiteEEMACAddr); - for (i = 0; i < 5; i++) - printk("%2.2x:", (u8)dev->dev_addr[i]); - printk("%2.2x.\n", dev->dev_addr[i]); - } else - printk(KERN_INFO "Error EEPROM read\n"); - - /* We do a request_region() to register /proc/ioports info. */ - request_region(ioaddr, pci_tbl[chip_idx].io_size, dev->name); - - dev->base_addr = ioaddr; - dev->irq = irq; - - /* Some data structures must be quadword aligned. */ - tp = kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA); - if(tp==NULL) - { - release_region(ioaddr, pci_tbl[chip_idx].io_size); - return NULL; - } - memset(tp, 0, sizeof(*tp)); - dev->priv = tp; + /* get MAC address from EEPROM */ + for (i = 0; i < 3; i++) + ((u16 *)(net_dev->dev_addr))[i] = read_eeprom(ioaddr, i+EEPROMMACAddr); + for (i = 0; i < 5; i++) + printk("%2.2x:", (u8)net_dev->dev_addr[i]); + printk("%2.2x.\n", net_dev->dev_addr[i]); + + if ((net_dev->priv = kmalloc(sizeof(struct sis900_private), GFP_KERNEL)) == NULL) { + unregister_netdevice(net_dev); + return NULL; + } - tp->next_module = root_sis900_dev; - root_sis900_dev = dev; + sis_priv = net_dev->priv; + memset(sis_priv, 0, sizeof(struct sis900_private)); - tp->chip_id = chip_idx; - tp->pci_bus = pci_bus; - tp->pci_devfn = pci_devfn; - - /* Find the connected MII xcvrs. - Doing this in open() would allow detecting external xcvrs later, but - takes too much time. */ - if (sis_cap_tbl[chip_idx] & HAS_MII_XCVR) { - for (phy = 0, phy_idx = 0; - phy < 32 && phy_idx < sizeof(tp->phys); phy++) - { - int mii_status ; - mii_status = mdio_read(dev, phy, MII_STATUS); - - if (mii_status != 0xffff && mii_status != 0x0000) { - tp->phy_idx = phy_idx; - tp->phys[phy_idx++] = phy; - tp->pmd_status=mdio_read(dev, phy, MII_STATUS); - printk(KERN_INFO "%s: MII transceiver found " - "at address %d.\n", - dev->name, phy); - break; - } - } + /* We do a request_region() to register /proc/ioports info. */ + request_region(ioaddr, mac->io_size, net_dev->name); + net_dev->base_addr = ioaddr; + net_dev->irq = irq; + sis_priv->pci_dev = pci_dev; + sis_priv->mac = mac; + + /* probe for mii transciver */ + if (sis900_mii_probe(net_dev) == 0) { + unregister_netdev(net_dev); + kfree(sis_priv); + release_region(ioaddr, mac->io_size); + return NULL; + } - if (phy_idx == 0) { - printk(KERN_INFO "%s: No MII transceivers found!\n", - dev->name); - tp->phys[0] = -1; - tp->pmd_status = 0; - } - } else { - tp->phys[0] = -1; - tp->pmd_status = 0; - } + sis_priv->next_module = root_sis900_dev; + root_sis900_dev = net_dev; - if ((tp->pmd_status > 0) && (phy_idx > 0)) { - if (sis900_debug > 1) { - printk(KERN_INFO "duplex=%d, speed=%d\n", - duplex, speed); - } - if (!duplex && !speed) { - // auto-config media type - // Set full capability - if (sis900_debug > 1) { - printk(KERN_INFO "Auto Config ...\n"); - } - elSetCapability(dev, tp->phys[tp->phy_idx], 1, 100); - tp->pmd_status=elAutoNegotiate(dev, - tp->phys[tp->phy_idx], - &tp->full_duplex, - &tp->speeds); - } else { - tp->MediaSpeed = speed; - tp->MediaDuplex = duplex; - elSetCapability(dev, tp->phys[tp->phy_idx], - duplex, speed); - elAutoNegotiate(dev, tp->phys[tp->phy_idx], - &tp->full_duplex, - &tp->speeds); - status = mdio_read(dev, phy, MII_ANLPAR); - if ( !(status & (MII_NWAY_T | MII_NWAY_T_FDX | - MII_NWAY_TX | MII_NWAY_TX_FDX ))) - { - u16 cmd=0; - cmd |= ( speed == 100 ? - MIICNTL_SPEED : 0 ); - cmd |= ( duplex ? MIICNTL_FDX : 0 ); - mdio_write(dev, phy, MII_CONTROL, cmd); - elSetMediaType(dev, speed==100 ? - HW_SPEED_100_MBPS : - HW_SPEED_10_MBPS, - duplex ? - FDX_CAPABLE_FULL_SELECTED: - FDX_CAPABLE_HALF_SELECTED); - elMIIpollBit(dev, phy, MII_STATUS, - MIISTAT_LINK, TRUE, &status); - } else { - status = mdio_read(dev, phy, MII_STATUS); - } - } + /* The SiS900-specific entries in the device structure. */ + net_dev->open = &sis900_open; + net_dev->hard_start_xmit = &sis900_start_xmit; + net_dev->stop = &sis900_close; + net_dev->get_stats = &sis900_get_stats; + net_dev->set_multicast_list = &set_rx_mode; + net_dev->do_ioctl = &mii_ioctl; - if (tp->pmd_status & MIISTAT_LINK) - tp->LinkOn = TRUE; - else - tp->LinkOn = FALSE; + return net_dev; +} - tp->LinkChange = FALSE; +static int sis900_mii_probe (struct device * net_dev) +{ + struct sis900_private * sis_priv = (struct sis900_private *)net_dev->priv; + int phy_addr; - } + sis_priv->mii = NULL; + + /* search for total of 32 possible mii phy addresses */ + for (phy_addr = 0; phy_addr < 32; phy_addr++) { + u16 mii_status; + u16 phy_id0, phy_id1; + int i; + + mii_status = mdio_read(net_dev, phy_addr, MII_STATUS); + if (mii_status == 0xffff || mii_status == 0x0000) + /* the mii is not accessable, try next one */ + continue; + + phy_id0 = mdio_read(net_dev, phy_addr, MII_PHY_ID0); + phy_id1 = mdio_read(net_dev, phy_addr, MII_PHY_ID1); + + /* search our mii table for the current mii */ + for (i = 0; mii_chip_table[i].phy_id1; i++) + if (phy_id0 == mii_chip_table[i].phy_id0) { + struct mii_phy * mii_phy; + + printk(KERN_INFO + "%s: %s transceiver found at address %d.\n", + net_dev->name, mii_chip_table[i].name, + phy_addr);; + if ((mii_phy = kmalloc(sizeof(struct mii_phy), GFP_KERNEL)) != NULL) { + mii_phy->chip_info = mii_chip_table+i; + mii_phy->phy_addr = phy_addr; + mii_phy->status = mdio_read(net_dev, phy_addr, + MII_STATUS); + mii_phy->next = sis_priv->mii; + sis_priv->mii = mii_phy; + } + /* the current mii is on our mii_info_table, + try next address */ + break; + } + } + + if (sis_priv->mii == NULL) { + printk(KERN_INFO "%s: No MII transceivers found!\n", + net_dev->name); + return 0; + } - if (sis900_debug > 1) { - if (tp->full_duplex == FDX_CAPABLE_FULL_SELECTED) { - printk(KERN_INFO "%s: Media type is Full Duplex.\n", - dev->name); - } else { - printk(KERN_INFO "%s: Media type is Half Duplex.\n", - dev->name); - } - if (tp->speeds == HW_SPEED_100_MBPS) { - printk(KERN_INFO "%s: Speed is 100mbps.\n", dev->name); - } else { - printk(KERN_INFO "%s: Speed is 10mbps.\n", dev->name); - } - } - - /* The SiS900-specific entries in the device structure. */ - dev->open = &sis900_open; - dev->hard_start_xmit = &sis900_start_xmit; - dev->stop = &sis900_close; - dev->get_stats = &sis900_get_stats; - dev->set_multicast_list = &set_rx_mode; - dev->do_ioctl = &mii_ioctl; - - return dev; -} - -/* Serial EEPROM section. */ - -/* EEPROM_Ctrl bits. */ -#define EECLK 0x00000004 /* EEPROM shift clock. */ -#define EECS 0x00000008 /* EEPROM chip select. */ -#define EEDO 0x00000002 /* EEPROM chip data out. */ -#define EEDI 0x00000001 /* EEPROM chip data in. */ + /* arbitrary choose that last PHY and current PHY */ + sis_priv->cur_phy = sis_priv->mii->phy_addr; + printk(KERN_INFO "%s: Using %s as default\n", net_dev->name, + sis_priv->mii->chip_info->name); -/* Delay between EEPROM clock transitions. - No extra delay is needed with 33Mhz PCI, but 66Mhz may change this. - */ + if (sis_priv->mii->status & MII_STAT_LINK) + sis_priv->LinkOn = TRUE; + else + sis_priv->LinkOn = FALSE; -#define eeprom_delay() inl(ee_addr) + return 1; +} -/* The EEPROM commands include the alway-set leading bit. */ -#define EEread 0x0180 -#define EEwrite 0x0140 -#define EEerase 0x01C0 -#define EEwriteEnable 0x0130 -#define EEwriteDisable 0x0100 -#define EEeraseAll 0x0120 -#define EEwriteAll 0x0110 -#define EEaddrMask 0x013F -#define EEcmdShift 16 +/* Delay between EEPROM clock transitions. */ +#define eeprom_delay() inl(ee_addr) +/* Read Serial EEPROM through EEPROM Access Register, Note that location is + in word (16 bits) unit */ static u16 read_eeprom(long ioaddr, int location) { - int i; + int i; u16 retval = 0; long ee_addr = ioaddr + mear; u32 read_cmd = location | EEread; @@ -748,7 +362,7 @@ outl(EECLK, ee_addr); eeprom_delay(); - /* Shift the read command bits out. */ + /* Shift the read command (9) bits out. */ for (i = 8; i >= 0; i--) { u32 dataval = (read_cmd & (1 << i)) ? EEDI | EECS : EECS; outl(dataval, ee_addr); @@ -759,6 +373,7 @@ outb(EECS, ee_addr); eeprom_delay(); + /* read the 16-bits data in */ for (i = 16; i > 0; i--) { outl(EECS, ee_addr); eeprom_delay(); @@ -767,39 +382,20 @@ retval = (retval << 1) | ((inl(ee_addr) & EEDO) ? 1 : 0); eeprom_delay(); } - + /* Terminate the EEPROM access. */ outl(0, ee_addr); eeprom_delay(); outl(EECLK, ee_addr); + return (retval); } -/* MII serial management: mostly bogus for now. */ /* Read and write the MII management registers using software-generated - serial MDIO protocol. - The maximum data clock rate is 2.5 Mhz. The minimum timing is usually - met by back-to-back PCI I/O cycles, but we insert a delay to avoid - "overclocking" issues. */ - + serial MDIO protocol. Note that the command bits and data bits are + send out seperately */ #define mdio_delay() inl(mdio_addr) -#define MIIread 0x6000 -#define MIIwrite 0x6002 -#define MIIpmdMask 0x0F80 -#define MIIpmdShift 7 -#define MIIregMask 0x007C -#define MIIregShift 2 -#define MIIturnaroundBits 2 -#define MIIcmdLen 16 -#define MIIcmdShift 16 -#define MIIreset 0xFFFFFFFF -#define MIIwrLen 32 - -#define MDC 0x00000040 -#define MDDIR 0x00000020 -#define MDIO 0x00000010 - static void mdio_idle(long mdio_addr) { outl(MDIO | MDDIR, mdio_addr); @@ -821,11 +417,11 @@ return; } -static int mdio_read(struct device *dev, int phy_id, int location) +static u16 mdio_read(struct device *net_dev, int phy_id, int location) { - long mdio_addr = dev->base_addr + mear; + long mdio_addr = net_dev->base_addr + mear; int mii_cmd = MIIread|(phy_id<= 0; i--) { int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR; outl(dataval, mdio_addr); + mdio_delay(); outl(dataval | MDC, mdio_addr); + mdio_delay(); } - /* Read the two transition, 16 data, and wire-idle bits. */ + /* Read the 16 data bits. */ for (i = 16; i > 0; i--) { outl(0, mdio_addr); - //mdio_delay(); + mdio_delay(); retval = (retval << 1) | ((inl(mdio_addr) & MDIO) ? 1 : 0); outl(MDC, mdio_addr); mdio_delay(); @@ -848,9 +446,9 @@ return retval; } -static void mdio_write(struct device *dev, int phy_id, int location, int value) +static void mdio_write(struct device *net_dev, int phy_id, int location, int value) { - long mdio_addr = dev->base_addr + mear; + long mdio_addr = net_dev->base_addr + mear; int mii_cmd = MIIwrite|(phy_id<= 0; i--) { + for (i = 15; i >= 0; i--) { int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR; outb(dataval, mdio_addr); mdio_delay(); @@ -866,6 +464,17 @@ mdio_delay(); } mdio_delay(); + + /* Shift the value bits out. */ + for (i = 15; i >= 0; i--) { + int dataval = (value & (1 << i)) ? MDDIR | MDIO : MDDIR; + outl(dataval, mdio_addr); + mdio_delay(); + outl(dataval | MDC, mdio_addr); + mdio_delay(); + } + mdio_delay(); + /* Clear out extra bits. */ for (i = 2; i > 0; i--) { outb(0, mdio_addr); @@ -877,354 +486,377 @@ } static int -sis900_open(struct device *dev) +sis900_open(struct device *net_dev) { - struct sis900_private *tp = (struct sis900_private *)dev->priv; - long ioaddr = dev->base_addr; - - if (sis900_debug > 0) - printk(KERN_INFO "%s sis900_open, IO Addr=%x, Irq=%x\n", - dev->name, (unsigned int)ioaddr, dev->irq); + struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; + long ioaddr = net_dev->base_addr; /* Soft reset the chip. */ - outl(0, ioaddr + imr); - outl(0, ioaddr + ier); - outl(0, ioaddr + rfcr); - outl(RESET | RxRESET | TxRESET, ioaddr + cr); + sis900_reset(net_dev); - if (request_irq(dev->irq, &sis900_interrupt, SA_SHIRQ, dev->name, dev)) - { + if (request_irq(net_dev->irq, &sis900_interrupt, SA_SHIRQ, net_dev->name, net_dev)) { return -EAGAIN; } - MOD_INC_USE_COUNT; + MOD_INC_USE_COUNT; - tp->tx_bufs = kmalloc(TX_BUF_SIZE * NUM_TX_DESC, GFP_KERNEL); - tp->rx_bufs = kmalloc(RX_BUF_SIZE * NUM_RX_DESC, GFP_KERNEL); - if (tp->tx_bufs == NULL || tp->rx_bufs == NULL) { - if (tp->tx_bufs) - kfree(tp->tx_bufs); - if (tp->rx_bufs) - kfree(tp->rx_bufs); - if (!tp->tx_bufs) { - printk(KERN_ERR "%s: Can't allocate a %d byte TX Bufs.\n", - dev->name, TX_BUF_SIZE * NUM_TX_DESC); - } - if (!tp->rx_bufs) { - printk(KERN_ERR "%s: Can't allocate a %d byte RX Bufs.\n", - dev->name, RX_BUF_SIZE * NUM_RX_DESC); - } - return -ENOMEM; - } + sis900_init_rxfilter(net_dev); - { - u32 rfcrSave; - u32 w; - u32 i; - - rfcrSave = inl(rfcr); - outl(rfcrSave & ~RFEN, rfcr); - for (i=0 ; i<3 ; i++) { - w = (u16)*((u16*)(dev->dev_addr)+i); - outl((((u32) i) << RFEP_shift), ioaddr + rfcr); - outl((u32)w, ioaddr + rfdr); - if (sis900_debug > 4) { - printk(KERN_INFO "Filter Addr[%d]=%x\n", - i, inl(ioaddr + rfdr)); - } - } - outl(rfcrSave, rfcr); - } + sis900_init_tx_ring(net_dev); + sis900_init_rx_ring(net_dev); - sis900_init_ring(dev); - outl((u32)tp->tx_buf[0].physAddr, ioaddr + txdp); - outl((u32)tp->rx_buf[0].physAddr, ioaddr + rxdp); - - if (sis900_debug > 4) - printk(KERN_INFO "txdp:%8.8x\n", inl(ioaddr + txdp)); - - /* Check that the chip has finished the reset. */ - { - u32 status; - int j=0; - status = TxRCMP | RxRCMP; - while (status && (j++ < 30000)) { - status ^= (inl(isr) & status); - } - } + set_rx_mode(net_dev); - outl(PESEL, ioaddr + cfg); - - /* Must enable Tx/Rx before setting transfer thresholds! */ - /* - * #define TX_DMA_BURST 0 - * #define RX_DMA_BURST 0 - * #define TX_FIFO_THRESH 16 - * #define TxDRNT_100 (1536>>5) - * #define TxDRNT_10 (1536>>5) - * #define RxDRNT_100 (1536>>5) - * #define RxDRNT_10 (1536>>5) - */ - outl((RX_DMA_BURST<<20) | (RxDRNT_10 << 1), ioaddr+rxcfg); - outl(TxATP | (TX_DMA_BURST << 20) | (TX_FIFO_THRESH<<8) | TxDRNT_10, - ioaddr + txcfg); - if (sis900_debug > 1) - { - if (tp->LinkOn) { - printk(KERN_INFO"%s: Media Type %s%s-duplex.\n", - dev->name, - tp->speeds==HW_SPEED_100_MBPS ? - "100mbps " : "10mbps ", - tp->full_duplex== FDX_CAPABLE_FULL_SELECTED ? - "full" : "half"); - } - else - { - printk(KERN_INFO"%s: Media Link Off\n", dev->name); - } - } - set_rx_mode(dev); - - dev->tbusy = 0; - dev->interrupt = 0; - dev->start = 1; + net_dev->tbusy = 0; + net_dev->interrupt = 0; + net_dev->start = 1; /* Enable all known interrupts by setting the interrupt mask. */ - outl((RxOK|RxERR|RxORN|RxSOVR|TxOK|TxERR|TxURN), ioaddr + imr); + outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxIDLE), ioaddr + imr); outl(RxENA, ioaddr + cr); outl(IE, ioaddr + ier); - if (sis900_debug > 3) - printk(KERN_INFO "%s: sis900_open() ioaddr %#lx IRQ %d \n", - dev->name, ioaddr, dev->irq); + sis900_check_mode(net_dev, sis_priv->mii); /* Set the timer to switch to check for link beat and perhaps switch to an alternate media type. */ - init_timer(&tp->timer); - tp->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */ - tp->timer.data = (unsigned long)dev; - tp->timer.function = &sis900_timer; /* timer handler */ - add_timer(&tp->timer); + init_timer(&sis_priv->timer); + sis_priv->timer.expires = jiffies + HZ; + sis_priv->timer.data = (unsigned long)net_dev; + sis_priv->timer.function = &sis900_timer; + add_timer(&sis_priv->timer); return 0; } -static void sis900_timer(unsigned long data) +/* set receive filter address to our MAC address */ +static void +sis900_init_rxfilter (struct device * net_dev) { - struct device *dev = (struct device *)data; - struct sis900_private *tp = (struct sis900_private *)dev->priv; - int next_tick = 0; - u16 status; + long ioaddr = net_dev->base_addr; + u32 rfcrSave; + u32 i; + + rfcrSave = inl(rfcr + ioaddr); - if (!tp->LinkOn) { - status = mdio_read(dev, tp->phys[tp->phy_idx], MII_STATUS); - if (status & MIISTAT_LINK) { - elPMDreadMode(dev, tp->phys[tp->phy_idx], - &tp->speeds, &tp->full_duplex); - tp->LinkOn = TRUE; - printk(KERN_INFO "%s: Media Link On %s%s-duplex ", - dev->name, - tp->speeds == HW_SPEED_100_MBPS ? - "100mbps " : "10mbps ", - tp->full_duplex==FDX_CAPABLE_FULL_SELECTED ? - "full" : "half"); - } - } else { // previous link on - status = mdio_read(dev, tp->phys[tp->phy_idx], MII_STATUS); - if (!(status & MIISTAT_LINK)) { - tp->LinkOn = FALSE; - printk(KERN_INFO "%s: Media Link Off\n", dev->name); + /* disable packet filtering before setting filter */ + outl(rfcrSave & ~RFEN, rfcr); + + /* load MAC addr to filter data register */ + for (i = 0 ; i < 3 ; i++) { + u32 w; + + w = (u32) *((u16 *)(net_dev->dev_addr)+i); + outl((i << RFADDR_shift), ioaddr + rfcr); + outl(w, ioaddr + rfdr); + + if (sis900_debug > 2) { + printk(KERN_INFO "%s: Receive Filter Addrss[%d]=%x\n", + net_dev->name, i, inl(ioaddr + rfdr)); } - } - next_tick = 2*HZ; + } - if (next_tick) { - tp->timer.expires = RUN_AT(next_tick); - add_timer(&tp->timer); - } + /* enable packet filitering */ + outl(rfcrSave | RFEN, rfcr + ioaddr); } -static void sis900_tx_timeout(struct device *dev) +/* Initialize the Tx ring. */ +static void +sis900_init_tx_ring(struct device *net_dev) { - struct sis900_private *tp = (struct sis900_private *)dev->priv; - long ioaddr = dev->base_addr; + struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; + long ioaddr = net_dev->base_addr; int i; + + sis_priv->tx_full = 0; + sis_priv->dirty_tx = sis_priv->cur_tx = 0; + + for (i = 0; i < NUM_TX_DESC; i++) { + sis_priv->tx_skbuff[i] = NULL; - if (sis900_debug > 0) - printk(KERN_INFO "%s: Transmit timeout, status %2.2x %4.4x \n", - dev->name, inl(ioaddr + cr), inl(ioaddr + isr)); - - /* Disable interrupts by clearing the interrupt mask. */ - outl(0x0000, ioaddr + imr); + sis_priv->tx_ring[i].link = (u32) virt_to_bus(&sis_priv->tx_ring[i+1]); + sis_priv->tx_ring[i].cmdsts = 0; + sis_priv->tx_ring[i].bufptr = 0; + } + sis_priv->tx_ring[i-1].link = (u32) virt_to_bus(&sis_priv->tx_ring[0]); + + /* load Transmit Descriptor Register */ + outl(virt_to_bus(&sis_priv->tx_ring[0]), ioaddr + txdp); + if (sis900_debug > 2) + printk(KERN_INFO "%s: TX descriptor register loaded with: %8.8x\n", + net_dev->name, inl(ioaddr + txdp)); +} + +/* Initialize the Rx descriptor ring, pre-allocate recevie buffers */ +static void +sis900_init_rx_ring(struct device *net_dev) +{ + struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; + long ioaddr = net_dev->base_addr; + int i; + + sis_priv->cur_rx = 0; + sis_priv->dirty_rx = 0; + + /* init RX descriptor */ + for (i = 0; i < NUM_RX_DESC; i++) { + sis_priv->rx_skbuff[i] = NULL; + + sis_priv->rx_ring[i].link = (u32) virt_to_bus(&sis_priv->rx_ring[i+1]); + sis_priv->rx_ring[i].cmdsts = 0; + sis_priv->rx_ring[i].bufptr = 0; + } + sis_priv->rx_ring[i-1].link = (u32) virt_to_bus(&sis_priv->rx_ring[0]); - /* Emit info to figure out what went wrong. */ - if (sis900_debug > 1) { - printk(KERN_INFO "%s:Tx queue start entry %d dirty entry %d.\n", - dev->name, tp->cur_tx, tp->dirty_tx); - for (i = 0; i < NUM_TX_DESC; i++) - printk(KERN_INFO "%s: Tx descriptor %d is %8.8x.%s\n", - dev->name, i, (unsigned int)&tp->tx_buf[i], - i == tp->dirty_tx % NUM_TX_DESC ? - " (queue head)" : ""); + /* allocate sock buffers */ + for (i = 0; i < NUM_RX_DESC; i++) { + struct sk_buff *skb; + + if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) { + /* not enough memory for skbuff, this makes a "hole" + on the buffer ring, it is not clear how the + hardware will react to this kind of degenerated + buffer */ + break; + } + skb->dev = net_dev; + sis_priv->rx_skbuff[i] = skb; + sis_priv->rx_ring[i].cmdsts = RX_BUF_SIZE; + sis_priv->rx_ring[i].bufptr = virt_to_bus(skb->tail); } + sis_priv->dirty_rx = (unsigned int) (i - NUM_RX_DESC); - /* Soft reset the chip. */ - //outb(RESET, ioaddr + cr); - /* Check that the chip has finished the reset. */ - /* - for (i = 1000; i > 0; i--) - if ((inb(ioaddr + cr) & RESET) == 0) - break; - */ + /* load Receive Descriptor Register */ + outl(virt_to_bus(&sis_priv->rx_ring[0]), ioaddr + rxdp); + if (sis900_debug > 2) + printk(KERN_INFO "%s: RX descriptor register loaded with: %8.8x\n", + net_dev->name, inl(ioaddr + rxdp)); +} +/* on each timer ticks we check two things, Link Status (ON/OFF) and + Link Mode (10/100/Full/Half) + */ +static void sis900_timer(unsigned long data) +{ + struct device *net_dev = (struct device *)data; + struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; + struct mii_phy *mii_phy = sis_priv->mii; + static int next_tick = 5*HZ; + u16 status; - tp->cur_rx = 0; - /* Must enable Tx/Rx before setting transfer thresholds! */ - /* - set_rx_mode(dev); - */ - { /* Save the unsent Tx packets. */ - struct sk_buff *saved_skb[NUM_TX_DESC], *skb; - int j; - for (j = 0; tp->cur_tx - tp->dirty_tx > 0 ; j++, tp->dirty_tx++) - saved_skb[j]=tp->tx_skbuff[tp->dirty_tx % NUM_TX_DESC]; - tp->dirty_tx = tp->cur_tx = 0; - - for (i = 0; i < j; i++) { - skb = tp->tx_skbuff[i] = saved_skb[i]; - /* Always alignment */ - memcpy((unsigned char*)(tp->tx_buf[i].buf), - skb->data, skb->len); - tp->tx_buf[i].cmdsts = OWN | skb->len; - /* Note: the chip doesn't have auto-pad! */ - /* - outl(tp->tx_flag|(skb->len>=ETH_ZLEN?skb->len:ETH_ZLEN), - ioaddr + TxStatus0 + i*4); - */ - } - outl(TxENA, ioaddr + cr); - tp->cur_tx = i; - while (i < NUM_TX_DESC) - tp->tx_skbuff[i++] = 0; - if (tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */ - dev->tbusy = 0; - tp->tx_full = 0; - } else { - tp->tx_full = 1; - } - } + status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS); - dev->trans_start = jiffies; - tp->stats.tx_errors++; - /* Enable all known interrupts by setting the interrupt mask. */ - outl((RxOK|RxERR|RxORN|RxSOVR|TxOK|TxERR|TxURN), ioaddr + imr); - return; -} + /* current mii phy is failed to link, try another one */ + while (!(status & MII_STAT_LINK)) { + if (mii_phy->next == NULL) { + if (sis_priv->LinkOn) { + /* link stat change from ON to OFF */ + next_tick = HZ; + sis_priv->LinkOn = FALSE; + printk(KERN_INFO "%s: Media Link Off\n", + net_dev->name); + } + sis_priv->timer.expires = jiffies + next_tick; + add_timer(&sis_priv->timer); + return; + } + mii_phy = mii_phy->next; + status = mdio_read(net_dev, mii_phy->phy_addr, MII_STATUS); + } + if (!sis_priv->LinkOn) { + /* link stat change forn OFF to ON, read and report link mode */ + sis_priv->LinkOn = TRUE; + next_tick = 5*HZ; + /* change what cur_phy means */ + if (mii_phy->phy_addr != sis_priv->cur_phy) { + printk(KERN_INFO "%s: Changing transceiver to %s\n", net_dev->name, + mii_phy->chip_info->name); + status = mdio_read(net_dev, sis_priv->cur_phy, MII_CONTROL); + mdio_write(net_dev, sis_priv->cur_phy, + MII_CONTROL, status | MII_CNTL_ISOLATE); + status = mdio_read(net_dev, mii_phy->phy_addr, MII_CONTROL); + mdio_write(net_dev, mii_phy->phy_addr, + MII_CONTROL, status & ~MII_CNTL_ISOLATE); + sis_priv->cur_phy = mii_phy->phy_addr; + } + sis900_check_mode(net_dev, mii_phy); + } -/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ -static void -sis900_init_ring(struct device *dev) + sis_priv->timer.expires = jiffies + next_tick; + add_timer(&sis_priv->timer); +} +static void sis900_check_mode (struct device *net_dev, struct mii_phy *mii_phy) { - struct sis900_private *tp = (struct sis900_private *)dev->priv; - int i; + struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; + long ioaddr = net_dev->base_addr; + int speed, duplex; + u32 tx_flags = 0, rx_flags = 0; - tp->tx_full = 0; - tp->cur_rx = 0; - tp->dirty_tx = tp->cur_tx = 0; + mii_phy->chip_info->read_mode(net_dev, sis_priv->cur_phy, &speed, &duplex); - /* Tx Buffer */ - for (i = 0; i < NUM_TX_DESC; i++) { - tp->tx_skbuff[i] = 0; - tp->tx_buf[i].buf = &tp->tx_bufs[i*TX_BUF_SIZE]; - tp->tx_buf[i].bufPhys = - virt_to_bus(&tp->tx_bufs[i*TX_BUF_SIZE]); - } + tx_flags = TxATP | (TX_DMA_BURST << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift); + rx_flags = RX_DMA_BURST << RxMXDMA_shift; - /* Tx Descriptor */ - for (i = 0; i< NUM_TX_DESC; i++) { - tp->tx_buf[i].llink = (u32) - &(tp->tx_buf[((i+1) < NUM_TX_DESC) ? (i+1) : 0]); - tp->tx_buf[i].plink = (u32) - virt_to_bus(&(tp->tx_buf[((i+1) < NUM_TX_DESC) ? - (i+1) : 0].plink)); - tp->tx_buf[i].physAddr= - virt_to_bus(&(tp->tx_buf[i].plink)); - tp->tx_buf[i].cmdsts=0; - } + if (speed == HW_SPEED_HOME || speed == HW_SPEED_10_MBPS ) { + rx_flags |= (RxDRNT_10 << RxDRNT_shift); + tx_flags |= (TxDRNT_10 << TxDRNT_shift); + } + else { + rx_flags |= (RxDRNT_100 << RxDRNT_shift); + tx_flags |= (TxDRNT_100 << TxDRNT_shift); + } - /* Rx Buffer */ - for (i = 0; i < NUM_RX_DESC; i++) { - tp->rx_buf[i].buf = &tp->rx_bufs[i*RX_BUF_SIZE]; - tp->rx_buf[i].bufPhys = - virt_to_bus(&tp->rx_bufs[i*RX_BUF_SIZE]); - } + if (duplex == FDX_CAPABLE_FULL_SELECTED) { + tx_flags |= (TxCSI | TxHBI); + rx_flags |= RxATX; + } - /* Rx Descriptor */ - for (i = 0; i< NUM_RX_DESC; i++) { - tp->rx_buf[i].llink = (u32) - &(tp->rx_buf[((i+1) < NUM_RX_DESC) ? (i+1) : 0]); - tp->rx_buf[i].plink = (u32) - virt_to_bus(&(tp->rx_buf[((i+1) < NUM_RX_DESC) ? - (i+1) : 0].plink)); - tp->rx_buf[i].physAddr= - virt_to_bus(&(tp->rx_buf[i].plink)); - tp->rx_buf[i].cmdsts=RX_BUF_SIZE; - } + outl (tx_flags, ioaddr + txcfg); + outl (rx_flags, ioaddr + rxcfg); } +static void sis900_read_mode(struct device *net_dev, int phy_addr, int *speed, int *duplex) +{ + int i = 0; + u32 status; + + /* STSOUT register is Latched on Transition, read operation updates it */ + while (i++ < 2) + status = mdio_read(net_dev, phy_addr, MII_STSOUT); + + if (status & MII_STSOUT_SPD) + *speed = HW_SPEED_100_MBPS; + else + *speed = HW_SPEED_10_MBPS; + + if (status & MII_STSOUT_DPLX) + *duplex = FDX_CAPABLE_FULL_SELECTED; + else + *duplex = FDX_CAPABLE_HALF_SELECTED; + + if (status & MII_STSOUT_LINK_FAIL) + printk(KERN_INFO "%s: Media Link Off\n", net_dev->name); + else + printk(KERN_INFO "%s: Media Link On %s %s-duplex \n", + net_dev->name, + *speed == HW_SPEED_100_MBPS ? + "100mbps" : "10mbps", + *duplex == FDX_CAPABLE_FULL_SELECTED ? + "full" : "half"); +} +static void amd79c901_read_mode(struct device *net_dev, int phy_addr, int *speed, int *duplex) +{ + int i; + u16 status; + + for (i = 0; i < 2; i++) + status = mdio_read(net_dev, phy_addr, MII_STATUS); -static int -sis900_start_xmit(struct sk_buff *skb, struct device *dev) + if (status & MII_STAT_CAN_AUTO) { + /* 10BASE-T PHY */ + for (i = 0; i < 2; i++) + status = mdio_read(net_dev, phy_addr, MII_STATUS_SUMMARY); + if (status & MII_STSSUM_SPD) + *speed = HW_SPEED_100_MBPS; + else + *speed = HW_SPEED_10_MBPS; + if (status & MII_STSSUM_DPLX) + *duplex = FDX_CAPABLE_FULL_SELECTED; + else + *duplex = FDX_CAPABLE_HALF_SELECTED; + + if (status & MII_STSSUM_LINK) + printk(KERN_INFO "%s: Media Link On %s %s-duplex \n", + net_dev->name, + *speed == HW_SPEED_100_MBPS ? + "100mbps" : "10mbps", + *duplex == FDX_CAPABLE_FULL_SELECTED ? + "full" : "half"); + else + printk(KERN_INFO "%s: Media Link Off\n", net_dev->name); + } + else { + /* HomePNA */ + *speed = HW_SPEED_HOME; + *duplex = FDX_CAPABLE_HALF_SELECTED; + if (status & MII_STAT_LINK) + printk(KERN_INFO "%s: Media Link On 1mbps half-duplex \n", + net_dev->name); + else + printk(KERN_INFO "%s: Media Link Off\n", net_dev->name); + } +} +static void sis900_tx_timeout(struct device *net_dev) { - struct sis900_private *tp = (struct sis900_private *)dev->priv; - long ioaddr = dev->base_addr; - int entry; - - /* 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) { - if (jiffies - dev->trans_start < TX_TIMEOUT) - return 1; - sis900_tx_timeout(dev); - return 1; - } + struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; + long ioaddr = net_dev->base_addr; + int i; - /* Calculate the next Tx descriptor entry. ????? */ - entry = tp->cur_tx % NUM_TX_DESC; + printk(KERN_INFO "%s: Transmit timeout, status %8.8x %8.8x \n", + net_dev->name, inl(ioaddr + cr), inl(ioaddr + isr)); + + /* Disable interrupts by clearing the interrupt mask. */ + outl(0x0000, ioaddr + imr); - tp->tx_skbuff[entry] = skb; + /* discard unsent packets, should this code section be protected by + cli(), sti() ?? */ + sis_priv->dirty_tx = sis_priv->cur_tx = 0; + for (i = 0; i < NUM_TX_DESC; i++) { + if (sis_priv->tx_skbuff[i] != NULL) { + dev_kfree_skb(sis_priv->tx_skbuff[i]); + sis_priv->tx_skbuff[i] = 0; + sis_priv->tx_ring[i].cmdsts = 0; + sis_priv->tx_ring[i].bufptr = 0; + sis_priv->stats.tx_dropped++; + } + } + net_dev->trans_start = jiffies; + net_dev->tbusy = sis_priv->tx_full = 0; - if (sis900_debug > 5) { - int i; - printk(KERN_INFO "%s: SKB Tx Frame contents:(len=%d)", - dev->name,skb->len); - - for (i = 0; i < skb->len; i++) { - printk("%2.2x ", - (u8)skb->data[i]); - } - printk(".\n"); - } + /* FIXME: Should we restart the transmission thread here ?? */ - memcpy(tp->tx_buf[entry].buf, - skb->data, skb->len); + /* Enable all known interrupts by setting the interrupt mask. */ + outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxIDLE), ioaddr + imr); + return; +} - tp->tx_buf[entry].cmdsts=(OWN | skb->len); +static int +sis900_start_xmit(struct sk_buff *skb, struct device *net_dev) +{ + struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; + long ioaddr = net_dev->base_addr; + unsigned int entry; + + /* test tbusy to see if we have timeout situation then set it */ + if (test_and_set_bit(0, (void*)&net_dev->tbusy) != 0) { + if (jiffies - net_dev->trans_start > TX_TIMEOUT) + sis900_tx_timeout(net_dev); + return 1; + } - //tp->tx_buf[entry].plink = 0; + /* Calculate the next Tx descriptor entry. */ + entry = sis_priv->cur_tx % NUM_TX_DESC; + sis_priv->tx_skbuff[entry] = skb; + + /* set the transmit buffer descriptor and enable Transmit State Machine */ + sis_priv->tx_ring[entry].bufptr = virt_to_bus(skb->data); + sis_priv->tx_ring[entry].cmdsts = (OWN | skb->len); outl(TxENA, ioaddr + cr); - if (++tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */ - clear_bit(0, (void*)&dev->tbusy); + + if (++sis_priv->cur_tx - sis_priv->dirty_tx < NUM_TX_DESC) { + /* Typical path, clear tbusy to indicate more + transmission is possible */ + clear_bit(0, (void*)&net_dev->tbusy); } else { - tp->tx_full = 1; + /* no more transmit descriptor avaiable, tbusy remain set */ + sis_priv->tx_full = 1; } - /* Note: the chip doesn't have auto-pad! */ + net_dev->trans_start = jiffies; - dev->trans_start = jiffies; - if (sis900_debug > 4) - printk(KERN_INFO "%s: Queued Tx packet at " - "%p size %d to slot %d.\n", - dev->name, skb->data, (int)skb->len, entry); + if (sis900_debug > 3) + printk(KERN_INFO "%s: Queued Tx packet at %p size %d " + "to slot %d.\n", + net_dev->name, skb->data, (int)skb->len, entry); return 0; } @@ -1233,340 +865,261 @@ after the Tx thread. */ static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs) { - struct device *dev = (struct device *)dev_instance; - struct sis900_private *tp = (struct sis900_private *)dev->priv; + struct device *net_dev = (struct device *)dev_instance; int boguscnt = max_interrupt_work; - int status; - long ioaddr = dev->base_addr; + long ioaddr = net_dev->base_addr; + u32 status; #if defined(__i386__) /* A lock to prevent simultaneous entry bug on Intel SMP machines. */ - if (test_and_set_bit(0, (void*)&dev->interrupt)) { + if (test_and_set_bit(0, (void*)&net_dev->interrupt)) { printk(KERN_INFO "%s: SMP simultaneous entry of " - "an interrupt handler.\n", dev->name); - dev->interrupt = 0; /* Avoid halting machine. */ + "an interrupt handler.\n", net_dev->name); + net_dev->interrupt = 0; /* Avoid halting machine. */ return; } #else - if (dev->interrupt) { - printk(KERN_INFO "%s: Re-entering the " - "interrupt handler.\n", dev->name); + if (net_dev->interrupt) { + printk(KERN_INFO "%s: Re-entering the interrupt handler.\n", + net_dev->name); return; } - dev->interrupt = 1; + net_dev->interrupt = 1; #endif do { status = inl(ioaddr + isr); - /* Acknowledge all of the current interrupt sources ASAP. */ - outl(status, ioaddr + isr); // ????? - - if (sis900_debug > 4) - printk(KERN_INFO "%s: interrupt status=%#4.4x " - "new intstat=%#4.4x.\n", - dev->name, status, inl(ioaddr + isr)); - - if ((status & (TxURN|TxERR|TxOK | RxORN|RxERR|RxOK)) == 0) { + + if ((status & (HIBERR|TxURN|TxERR|TxIDLE|RxORN|RxERR|RxOK)) == 0) + /* nothing intresting happened */ break; - } - if (status & (RxOK|RxORN|RxERR)) /* Rx interrupt */ - sis900_rx(dev); - - if (status & (TxOK | TxERR)) { - unsigned int dirty_tx; - - if (sis900_debug > 5) { - printk(KERN_INFO "TxOK:tp->cur_tx:%d," - "tp->dirty_tx:%x\n", - tp->cur_tx, tp->dirty_tx); - } - for (dirty_tx = tp->dirty_tx; dirty_tx < tp->cur_tx; - dirty_tx++) - { - int i; - int entry = dirty_tx % NUM_TX_DESC; - int txstatus = tp->tx_buf[entry].cmdsts; - - if (sis900_debug > 4) { - printk(KERN_INFO "%s: Tx Frame contents:" - "(len=%d)", - dev->name, (txstatus & DSIZE)); - - for (i = 0; i < (txstatus & DSIZE) ; - i++) { - printk("%2.2x ", - (u8)(tp->tx_buf[entry].buf[i])); - } - printk(".\n"); - } - if ( ! (txstatus & (OK | UNDERRUN))) - { - if (sis900_debug > 1) - printk(KERN_INFO "Tx NOT (OK," - "UnderRun)\n"); - break; /* It still hasn't been Txed */ - } - - /* Note: TxCarrierLost is always asserted - at 100mbps. */ - if (txstatus & (OWCOLL | ABORT)) { - /* There was an major error, log it. */ - if (sis900_debug > 1) - printk(KERN_INFO "Tx Out of " - " Window,Abort\n"); -#ifndef final_version - if (sis900_debug > 1) - printk(KERN_INFO "%s: Transmit " - "error, Tx status %8.8x.\n", - dev->name, txstatus); -#endif - tp->stats.tx_errors++; - if (txstatus & ABORT) { - tp->stats.tx_aborted_errors++; - } - if (txstatus & NOCARRIER) - tp->stats.tx_carrier_errors++; - if (txstatus & OWCOLL) - tp->stats.tx_window_errors++; -#ifdef ETHER_STATS - if ((txstatus & COLCNT)==COLCNT) - tp->stats.collisions16++; -#endif - } else { -#ifdef ETHER_STATS - /* No count for tp->stats.tx_deferred */ -#endif - if (txstatus & UNDERRUN) { - if (sis900_debug > 2) - printk(KERN_INFO "Tx UnderRun\n"); - } - tp->stats.collisions += - (txstatus >> 16) & 0xF; -#if LINUX_VERSION_CODE > 0x20119 - tp->stats.tx_bytes += txstatus & DSIZE; -#endif - if (sis900_debug > 2) - printk(KERN_INFO "Tx Transmit OK\n"); - tp->stats.tx_packets++; - } - - /* Free the original skb. */ - if (sis900_debug > 2) - printk(KERN_INFO "Free original skb\n"); - dev_free_skb(tp->tx_skbuff[entry]); - tp->tx_skbuff[entry] = 0; - } // for dirty - -#ifndef final_version - if (tp->cur_tx - dirty_tx > NUM_TX_DESC) { - printk(KERN_INFO"%s: Out-of-sync dirty pointer," - " %d vs. %d, full=%d.\n", - dev->name, dirty_tx, - tp->cur_tx, tp->tx_full); - dirty_tx += NUM_TX_DESC; - } -#endif - - if (tp->tx_full && dirty_tx > tp->cur_tx-NUM_TX_DESC) { - /* The ring is no longer full, clear tbusy. */ - if (sis900_debug > 3) - printk(KERN_INFO "Tx Ring NO LONGER Full\n"); - tp->tx_full = 0; - dev->tbusy = 0; - mark_bh(NET_BH); - } - - tp->dirty_tx = dirty_tx; - if (sis900_debug > 2) - printk(KERN_INFO "TxOK,tp->cur_tx:%d,tp->dirty:%d\n", - tp->cur_tx, tp->dirty_tx); - } // if (TxOK | TxERR) - - /* Check uncommon events with one test. */ - if (status & (RxORN | TxERR | RxERR)) { - if (sis900_debug > 2) - printk(KERN_INFO "%s: Abnormal interrupt," - "status %8.8x.\n", dev->name, status); - - if (status == 0xffffffff) - break; - if (status & (RxORN | RxERR)) - tp->stats.rx_errors++; - - - if (status & RxORN) { - tp->stats.rx_over_errors++; - } - } + /* why dow't we break after Tx/Rx case ?? keyword: full-duplex */ + if (status & (RxORN | RxERR | RxOK)) + /* Rx interrupt */ + sis900_rx(net_dev); + + if (status & (TxURN | TxERR | TxIDLE)) + /* Tx interrupt */ + sis900_finish_xmit(net_dev); + + /* something strange happened !!! */ + if (status & HIBERR) { + printk(KERN_INFO "%s: Abnormal interrupt," + "status %#8.8x.\n", net_dev->name, status); + break; + } if (--boguscnt < 0) { printk(KERN_INFO "%s: Too much work at interrupt, " - "IntrStatus=0x%4.4x.\n", - dev->name, status); + "interrupt status = %#8.8x.\n", + net_dev->name, status); break; } } while (1); - + if (sis900_debug > 3) - printk(KERN_INFO "%s: exiting interrupt, intr_status=%#4.4x.\n", - dev->name, inl(ioaddr + isr)); - + printk(KERN_INFO "%s: exiting interrupt, " + "interrupt status = 0x%#8.8x.\n", + net_dev->name, inl(ioaddr + isr)); + #if defined(__i386__) - clear_bit(0, (void*)&dev->interrupt); + clear_bit(0, (void*)&net_dev->interrupt); #else - dev->interrupt = 0; + net_dev->interrupt = 0; #endif return; } -/* The data sheet doesn't describe the Rx ring at all, so I'm guessing at the - field alignments and semantics. */ -static int sis900_rx(struct device *dev) -{ - struct sis900_private *tp = (struct sis900_private *)dev->priv; - long ioaddr = dev->base_addr; - u16 cur_rx = tp->cur_rx % NUM_RX_DESC; - int rx_status=tp->rx_buf[cur_rx].cmdsts; - - if (sis900_debug > 4) - printk(KERN_INFO "%s: sis900_rx, current %4.4x," - " rx status=%8.8x\n", - dev->name, cur_rx, - rx_status); - +/* Process receive interrupt events, put buffer to higher layer and refill buffer pool + Note: This fucntion is called by interrupt handler, don't do "too much" work here */ +static int sis900_rx(struct device *net_dev) +{ + struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; + long ioaddr = net_dev->base_addr; + unsigned int entry = sis_priv->cur_rx % NUM_RX_DESC; + u32 rx_status = sis_priv->rx_ring[entry].cmdsts; + + if (sis900_debug > 3) + printk(KERN_INFO "sis900_rx, cur_rx:%4.4d, dirty_rx:%4.4d " + "status:0x%8.8x\n", + sis_priv->cur_rx, sis_priv->dirty_rx,rx_status); + while (rx_status & OWN) { - int rx_size = rx_status & DSIZE; - rx_size -= CRC_SIZE; - - if (sis900_debug > 4) { - int i; - printk(KERN_INFO "%s: sis900_rx, rx status %8.8x," - " size %4.4x, cur %4.4x.\n", - dev->name, rx_status, rx_size, cur_rx); - printk(KERN_INFO "%s: Rx Frame contents:", dev->name); - - for (i = 0; i < rx_size; i++) { - printk("%2.2x ", - (u8)(tp->rx_buf[cur_rx].buf[i])); - } - - printk(".\n"); - } - if (rx_status & TOOLONG) { - if (sis900_debug > 1) - printk(KERN_INFO "%s: Oversized Ethernet frame," - " status %4.4x!\n", - dev->name, rx_status); - tp->stats.rx_length_errors++; - } else if (rx_status & (RXISERR | RUNT | CRCERR | FAERR)) { - if (sis900_debug > 1) - printk(KERN_INFO"%s: Ethernet frame had errors," - " status %4.4x.\n", - dev->name, rx_status); - tp->stats.rx_errors++; + unsigned int rx_size; + + rx_size = (rx_status & DSIZE) - CRC_SIZE; + + if (rx_status & (ABORT|OVERRUN|TOOLONG|RUNT|RXISERR|CRCERR|FAERR)) { + /* corrupted packet received */ + if (sis900_debug > 3) + printk(KERN_INFO "%s: Corrupted packet " + "received, buffer status = 0x%8.8x.\n", + net_dev->name, rx_status); + sis_priv->stats.rx_errors++; + if (rx_status & OVERRUN) + sis_priv->stats.rx_over_errors++; + if (rx_status & (TOOLONG|RUNT)) + sis_priv->stats.rx_length_errors++; if (rx_status & (RXISERR | FAERR)) - tp->stats.rx_frame_errors++; - if (rx_status & (RUNT | TOOLONG)) - tp->stats.rx_length_errors++; - if (rx_status & CRCERR) tp->stats.rx_crc_errors++; + sis_priv->stats.rx_frame_errors++; + if (rx_status & CRCERR) + sis_priv->stats.rx_crc_errors++; + /* reset buffer descriptor state */ + sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE; } else { - /* Malloc up new buffer, compatible with net-2e. */ - /* Omit the four octet CRC from the length. */ - struct sk_buff *skb; - - skb = dev_alloc_skb(rx_size + 2); - if (skb == NULL) { - printk(KERN_INFO "%s: Memory squeeze," - "deferring packet.\n", - dev->name); - /* We should check that some rx space is free. - If not, - free one and mark stats->rx_dropped++. */ - tp->stats.rx_dropped++; - tp->rx_buf[cur_rx].cmdsts = RX_BUF_SIZE; - break; - } - skb->dev = dev; - skb_reserve(skb, 2); /* 16 byte align the IP fields. */ - if (rx_size+CRC_SIZE > RX_BUF_SIZE) { - /* - int semi_count = RX_BUF_LEN - ring_offset - 4; - memcpy(skb_put(skb, semi_count), - &rx_bufs[ring_offset + 4], semi_count); - memcpy(skb_put(skb, rx_size-semi_count), - rx_bufs, rx_size - semi_count); - if (sis900_debug > 4) { - int i; - printk(KERN_DEBUG"%s: Frame wrap @%d", - dev->name, semi_count); - for (i = 0; i < 16; i++) - printk(" %2.2x", rx_bufs[i]); - printk(".\n"); - memset(rx_bufs, 0xcc, 16); - } - */ - } else { -#if 0 /* USE_IP_COPYSUM */ - eth_copy_and_sum(skb, - tp->rx_buf[cur_rx].buf, rx_size, 0); - skb_put(skb, rx_size); -#else - memcpy(skb_put(skb, rx_size), - tp->rx_buf[cur_rx].buf, rx_size); -#endif - } - skb->protocol = eth_type_trans(skb, dev); + struct sk_buff * skb; + + /* This situation should never happen, but due to + some unknow bugs, it is possible that + we are working on NULL sk_buff :-( */ + if (sis_priv->rx_skbuff[entry] == NULL) { + printk(KERN_INFO "%s: NULL pointer " + "encountered in Rx ring, skipping\n", + net_dev->name); + break; + } + skb = sis_priv->rx_skbuff[entry]; + sis_priv->rx_skbuff[entry] = NULL; + /* reset buffer descriptor state */ + sis_priv->rx_ring[entry].cmdsts = 0; + sis_priv->rx_ring[entry].bufptr = 0; + + skb_put(skb, rx_size); + skb->protocol = eth_type_trans(skb, net_dev); netif_rx(skb); -#if LINUX_VERSION_CODE > 0x20119 - tp->stats.rx_bytes += rx_size; -#endif - tp->stats.rx_packets++; + + if ((rx_status & BCAST) == MCAST) + sis_priv->stats.multicast++; + net_dev->last_rx = jiffies; + sis_priv->stats.rx_bytes += rx_size; + sis_priv->stats.rx_packets++; } - tp->rx_buf[cur_rx].cmdsts = RX_BUF_SIZE; - - cur_rx = ((cur_rx+1) % NUM_RX_DESC); - rx_status = tp->rx_buf[cur_rx].cmdsts; + sis_priv->cur_rx++; + entry = sis_priv->cur_rx % NUM_RX_DESC; + rx_status = sis_priv->rx_ring[entry].cmdsts; } // while - if (sis900_debug > 4) - printk(KERN_INFO "%s: Done sis900_rx(), current %4.4x " - "Cmd %2.2x.\n", - dev->name, cur_rx, - inb(ioaddr + cr)); - tp->cur_rx = cur_rx; + + /* refill the Rx buffer, what if the rate of refilling is slower than + consuming ?? */ + for (;sis_priv->cur_rx - sis_priv->dirty_rx > 0; sis_priv->dirty_rx++) { + struct sk_buff *skb; + + entry = sis_priv->dirty_rx % NUM_RX_DESC; + + if (sis_priv->rx_skbuff[entry] == NULL) { + if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) { + /* not enough memory for skbuff, this makes a "hole" + on the buffer ring, it is not clear how the + hardware will react to this kind of degenerated + buffer */ + printk(KERN_INFO "%s: Memory squeeze," + "deferring packet.\n", + net_dev->name); + sis_priv->stats.rx_dropped++; + break; + } + skb->dev = net_dev; + sis_priv->rx_skbuff[entry] = skb; + sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE; + sis_priv->rx_ring[entry].bufptr = virt_to_bus(skb->tail); + } + } + /* re-enable the potentially idle receive state matchine */ + outl(RxENA , ioaddr + cr ); + return 0; } +/* finish up transmission of packets, check for error condition and free skbuff etc. + Note: This fucntion is called by interrupt handler, don't do "too much" work here */ +static void sis900_finish_xmit (struct device *net_dev) +{ + struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; + + for (; sis_priv->dirty_tx < sis_priv->cur_tx; sis_priv->dirty_tx++) { + unsigned int entry; + u32 tx_status; + + entry = sis_priv->dirty_tx % NUM_TX_DESC; + tx_status = sis_priv->tx_ring[entry].cmdsts; + + if (tx_status & OWN) { + /* The packet is not transmited yet (owned by hardware) ! + Note: the interrupt is generated only when Tx Machine + is idle, so this is an almost impossible case */ + break; + } + + if (tx_status & (ABORT | UNDERRUN | OWCOLL)) { + /* packet unsuccessfully transmited */ + if (sis900_debug > 3) + printk(KERN_INFO "%s: Transmit " + "error, Tx status %8.8x.\n", + net_dev->name, tx_status); + sis_priv->stats.tx_errors++; + if (tx_status & UNDERRUN) + sis_priv->stats.tx_fifo_errors++; + if (tx_status & ABORT) + sis_priv->stats.tx_aborted_errors++; + if (tx_status & NOCARRIER) + sis_priv->stats.tx_carrier_errors++; + if (tx_status & OWCOLL) + sis_priv->stats.tx_window_errors++; + } else { + /* packet successfully transmited */ + sis_priv->stats.collisions += (tx_status & COLCNT) >> 16; + sis_priv->stats.tx_bytes += tx_status & DSIZE; + sis_priv->stats.tx_packets++; + } + /* Free the original skb. */ + dev_kfree_skb(sis_priv->tx_skbuff[entry]); + sis_priv->tx_skbuff[entry] = NULL; + sis_priv->tx_ring[entry].bufptr = 0; + sis_priv->tx_ring[entry].cmdsts = 0; + } + + if (sis_priv->tx_full && net_dev->tbusy && + sis_priv->cur_tx - sis_priv->dirty_tx < NUM_TX_DESC - 4) { + /* The ring is no longer full, clear tbusy, tx_full and + schedule more transmission by marking NET_BH */ + sis_priv->tx_full = 0; + clear_bit(0, (void *)&net_dev->tbusy); + mark_bh(NET_BH); + } +} + static int -sis900_close(struct device *dev) +sis900_close(struct device *net_dev) { - long ioaddr = dev->base_addr; - struct sis900_private *tp = (struct sis900_private *)dev->priv; + long ioaddr = net_dev->base_addr; + struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; int i; - - dev->start = 0; - dev->tbusy = 1; - - if (sis900_debug > 1) - printk(KERN_DEBUG"%s: Shutting down ethercard, status was 0x%4.4x.\n", - dev->name, inl(ioaddr + isr)); + + net_dev->start = 0; + net_dev->tbusy = 1; /* Disable interrupts by clearing the interrupt mask. */ outl(0x0000, ioaddr + imr); + outl(0x0000, ioaddr + ier); - /* Stop the chip's Tx and Rx DMA processes. */ - outl(0x00, ioaddr + cr); + /* Stop the chip's Tx and Rx Status Machine */ + outl(RxDIS | TxDIS, ioaddr + cr); - del_timer(&tp->timer); + del_timer(&sis_priv->timer); - free_irq(dev->irq, dev); + free_irq(net_dev->irq, net_dev); + /* Free Tx and RX skbuff */ + for (i = 0; i < NUM_RX_DESC; i++) { + if (sis_priv->rx_skbuff[i] != NULL) + dev_kfree_skb(sis_priv->rx_skbuff[i]); + sis_priv->rx_skbuff[i] = 0; + } for (i = 0; i < NUM_TX_DESC; i++) { - if (tp->tx_skbuff[i]) - dev_free_skb(tp->tx_skbuff[i]); - tp->tx_skbuff[i] = 0; + if (sis_priv->tx_skbuff[i] != NULL) + dev_kfree_skb(sis_priv->tx_skbuff[i]); + sis_priv->tx_skbuff[i] = 0; } - kfree(tp->rx_bufs); - kfree(tp->tx_bufs); /* Green! Put the chip in low-power mode. */ @@ -1575,22 +1128,22 @@ return 0; } -static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd) +static int mii_ioctl(struct device *net_dev, struct ifreq *rq, int cmd) { - struct sis900_private *tp = (struct sis900_private *)dev->priv; + struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; u16 *data = (u16 *)&rq->ifr_data; switch(cmd) { - case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ - data[0] = tp->phys[tp->phy_idx]; + case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ + data[0] = sis_priv->mii->phy_addr; /* Fall Through */ - case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ - data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); + case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ + data[3] = mdio_read(net_dev, data[0] & 0x1f, data[1] & 0x1f); return 0; - case SIOCDEVPRIVATE+2: /* Write the specified MII register */ + case SIOCDEVPRIVATE+2: /* Write the specified MII register */ if (!suser()) return -EPERM; - mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); + mdio_write(net_dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); return 0; default: return -EOPNOTSUPP; @@ -1598,326 +1151,141 @@ } static struct enet_statistics * -sis900_get_stats(struct device *dev) -{ - struct sis900_private *tp = (struct sis900_private *)dev->priv; - - return &tp->stats; -} - -/* Set or clear the multicast filter for this adaptor. - This routine is not state sensitive and need not be SMP locked. */ - -static u16 elComputeHashTableIndex(u8 *addr) -{ -#define POLYNOMIAL 0x04C11DB6L - u32 crc = 0xffffffff, msb; - int i, j; - u8 byte; - - for( i=0; i<6; i++ ) { - byte = *addr++; - for( j=0; j<8; j++ ) { - msb = crc >> 31; - crc <<= 1; - if( msb ^ ( byte & 1 )) { - crc ^= POLYNOMIAL; - crc |= 1; - } - byte >>= 1; - } - } - // 7 bit crc for 128 bit hash table - return( (int)(crc >> 25) ); -} - -static u16 elMIIpollBit(struct device *dev, - int phy_id, - int location, - u16 mask, - u16 polarity, - u16 *value) -{ - u32 i; - i=0; - while (1) { - *value = mdio_read(dev, phy_id, location); - if (polarity) { - if (mask & *value) return(TRUE); - } else { - if (mask & ~(*value)) return(TRUE); - } - if (++i == 1200) break; - } - return(FALSE); -} - -static u16 elPMDreadMode(struct device *dev, - int phy_id, - int *speed, - int *duplex) +sis900_get_stats(struct device *net_dev) { - u16 status, OurCap; - - *speed = HW_SPEED_10_MBPS; - *duplex = FDX_CAPABLE_HALF_SELECTED; + struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; - status = mdio_read(dev, phy_id, MII_ANLPAR); - OurCap = mdio_read(dev, phy_id, MII_ANAR); - if (sis900_debug > 1) { - printk(KERN_INFO "Link Part Status %4X\n", status); - printk(KERN_INFO "Our Status %4X\n", OurCap); - printk(KERN_INFO "Status Reg %4X\n", - mdio_read(dev, phy_id, MII_STATUS)); - } - status &= OurCap; - - if ( !( status & - (MII_NWAY_T|MII_NWAY_T_FDX | MII_NWAY_TX | MII_NWAY_TX_FDX ))) { - if (sis900_debug > 1) { - printk(KERN_INFO "The other end NOT support NWAY...\n"); - } - while (( status = mdio_read(dev, phy_id, 18)) & 0x4000) ; - while (( status = mdio_read(dev, phy_id, 18)) & 0x0020) ; - if (status & 0x80) - *speed = HW_SPEED_100_MBPS; - if (status & 0x40) - *duplex = FDX_CAPABLE_FULL_SELECTED; - if (sis900_debug > 3) { - printk(KERN_INFO"%s: Setting %s%s-duplex.\n", - dev->name, - *speed == HW_SPEED_100_MBPS ? - "100mbps " : "10mbps ", - *duplex == FDX_CAPABLE_FULL_SELECTED ? - "full" : "half"); - } - } else { - if (sis900_debug > 1) { - printk(KERN_INFO "The other end support NWAY...\n"); - } - - if (status & (MII_NWAY_TX_FDX | MII_NWAY_T_FDX)) { - *duplex = FDX_CAPABLE_FULL_SELECTED; - } - if (status & (MII_NWAY_TX_FDX | MII_NWAY_TX)) { - *speed = HW_SPEED_100_MBPS; - } - if (sis900_debug > 3) { - printk(KERN_INFO"%s: Setting %s%s-duplex based on" - " auto-negotiated partner ability.\n", - dev->name, - *speed == HW_SPEED_100_MBPS ? - "100mbps " : "10mbps ", - *duplex == FDX_CAPABLE_FULL_SELECTED ? - "full" : "half"); - } - } - return (status); + return &sis_priv->stats; } -static u16 elAutoNegotiate(struct device *dev, int phy_id, int *duplex, int *speed) -{ - u16 status, retnVal; - - if (sis900_debug > 1) { - printk(KERN_INFO "AutoNegotiate...\n"); - } - mdio_write(dev, phy_id, MII_CONTROL, 0); - mdio_write(dev, phy_id, MII_CONTROL, MIICNTL_AUTO | MIICNTL_RST_AUTO); - retnVal = elMIIpollBit(dev, phy_id, MII_CONTROL, MIICNTL_RST_AUTO, - FALSE,&status); - if (!retnVal) { - printk(KERN_INFO "Not wait for Reset Complete\n"); - } - retnVal = elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_AUTO_DONE, - TRUE, &status); - if (!retnVal) { - printk(KERN_INFO "Not wait for AutoNego Complete\n"); - } - retnVal = elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_LINK, - TRUE, &status); - if (!retnVal) { - printk(KERN_INFO "Not wait for Link Complete\n"); - } - if (status & MIISTAT_LINK) { - elPMDreadMode(dev, phy_id, speed, duplex); - elSetMediaType(dev, *speed, *duplex); - } - return(status); -} - -static void elSetCapability(struct device *dev, int phy_id, - int duplex, int speed) -{ - u16 cap = ( MII_NWAY_T | MII_NWAY_T_FDX | - MII_NWAY_TX | MII_NWAY_TX_FDX | MII_NWAY_CSMA_CD ); - - if (speed != 100) { - cap &= ~( MII_NWAY_TX | MII_NWAY_TX_FDX ); - if (sis900_debug > 1) { - printk(KERN_INFO "UNSET 100Mbps\n"); - } - } - - if (!duplex) { - cap &= ~( MII_NWAY_T_FDX | MII_NWAY_TX_FDX ); - if (sis900_debug > 1) { - printk(KERN_INFO "UNSET full-duplex\n"); +/* SiS 900 uses the most sigificant 7 bits to index a 128 bits multicast hash table, which makes + this function a little bit different from other drivers */ +static u16 sis900_compute_hashtable_index(u8 *addr) +{ + +/* what is the correct value of the POLYNOMIAL ?? + Donald Becker use 0x04C11DB7U + Joseph Zbiciak im14u2c@primenet.com gives me the + correct answer, thank you Joe !! */ +#define POLYNOMIAL 0x04C11DB7L + u32 crc = 0xffffffff, msb; + int i, j; + u32 byte; + + for (i = 0; i < 6; i++) { + byte = *addr++; + for (j = 0; j < 8; j++) { + msb = crc >> 31; + crc <<= 1; + if (msb ^ (byte & 1)) { + crc ^= POLYNOMIAL; + } + byte >>= 1; } } - - mdio_write(dev, phy_id, MII_ANAR, cap); + /* leave 7 most siginifant bits */ + return ((int)(crc >> 25)); } -static void elSetMediaType(struct device *dev, int speed, int duplex) +static void set_rx_mode(struct device *net_dev) { - long ioaddr = dev->base_addr; - u32 txCfgOn = 0, txCfgOff = TxDRNT; - u32 rxCfgOn = 0, rxCfgOff = 0; - - if (speed == HW_SPEED_100_MBPS) { - txCfgOn |= (TxDRNT_100 | TxHBI); - } else { - txCfgOn |= TxDRNT_10; - } - - if (duplex == FDX_CAPABLE_FULL_SELECTED) { - txCfgOn |= (TxCSI | TxHBI); - rxCfgOn |= RxATP; - } else { - txCfgOff |= (TxCSI | TxHBI); - rxCfgOff |= RxATP; - } - outl( (inl(ioaddr + txcfg) & ~txCfgOff) | txCfgOn, ioaddr + txcfg); - outl( (inl(ioaddr + rxcfg) & ~rxCfgOff) | rxCfgOn, ioaddr + rxcfg); -} - -static void set_rx_mode(struct device *dev) -{ - long ioaddr = dev->base_addr; - u16 mc_filter[8]; + long ioaddr = net_dev->base_addr; + u16 mc_filter[8]; /* 128 bits multicast hash table */ int i; - int rx_mode; - u32 rxCfgOn = 0, rxCfgOff = 0; - u32 txCfgOn = 0, txCfgOff = 0; + u32 rx_mode; - if (sis900_debug > 3) - printk(KERN_INFO "%s: set_rx_mode (%4.4x) done--" - "RxCfg %8.8x.\n", - dev->name, dev->flags, inl(ioaddr + rxcfg)); - - /* Note: do not reorder, GCC is clever about common statements. */ - if (dev->flags & IFF_PROMISC) { - printk(KERN_NOTICE"%s: Promiscuous mode enabled.\n", dev->name); - rx_mode = ACCEPT_ALL_BCASTS | ACCEPT_ALL_MCASTS | - ACCEPT_CAM_QUALIFIED | ACCEPT_ALL_PHYS; - for (i=0 ; i<8 ; i++) - mc_filter[i]=0xffff; - } else if ((dev->mc_count > multicast_filter_limit) - || (dev->flags & IFF_ALLMULTI)) { - rx_mode = ACCEPT_ALL_BCASTS | ACCEPT_ALL_MCASTS | - ACCEPT_CAM_QUALIFIED; - for (i=0 ; i<8 ; i++) - mc_filter[i]=0xffff; + if (net_dev->flags & IFF_PROMISC) { + /* Accept any kinds of packets */ + rx_mode = RFPromiscuous; + for (i = 0; i < 8; i++) + mc_filter[i] = 0xffff; + } else if ((net_dev->mc_count > multicast_filter_limit) || + (net_dev->flags & IFF_ALLMULTI)) { + /* too many multicast addresses or accept all multicast packets */ + rx_mode = RFAAB | RFAAM; + for (i = 0; i < 8; i++) + mc_filter[i] = 0xffff; } else { + /* Accept Broadcast packets, destination addresses match our MAC address, + use Receive Filter to reject unwanted MCAST packets */ struct dev_mc_list *mclist; - rx_mode = ACCEPT_ALL_BCASTS | ACCEPT_ALL_MCASTS | - ACCEPT_CAM_QUALIFIED; - for (i=0 ; i<8 ; i++) + rx_mode = RFAAB; + for (i = 0; i < 8; i++) mc_filter[i]=0; - for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; - i++, mclist = mclist->next) - set_bit(elComputeHashTableIndex(mclist->dmi_addr), - mc_filter); + for (i = 0, mclist = net_dev->mc_list; mclist && i < net_dev->mc_count; + i++, mclist = mclist->next) + set_bit(sis900_compute_hashtable_index(mclist->dmi_addr), + mc_filter); } - for (i=0 ; i<8 ; i++) { - outl((u32)(0x00000004+i) << 16, ioaddr + rfcr); + /* update Multicast Hash Table in Receive Filter */ + for (i = 0; i < 8; i++) { + /* why plus 0x04 ??, That makes the correct value for hash table. */ + outl((u32)(0x00000004+i) << RFADDR_shift, ioaddr + rfcr); outl(mc_filter[i], ioaddr + rfdr); } - /* We can safely update without stopping the chip. */ - //rx_mode = ACCEPT_CAM_QUALIFIED | ACCEPT_ALL_BCASTS | ACCEPT_ALL_PHYS; - //rx_mode = ACCEPT_CAM_QUALIFIED | ACCEPT_ALL_BCASTS; - outl(RFEN | ((rx_mode & (ACCEPT_ALL_MCASTS | ACCEPT_ALL_BCASTS | - ACCEPT_ALL_PHYS)) << RFAA_shift), ioaddr + rfcr); - - if (rx_mode & ACCEPT_ALL_ERRORS) { - rxCfgOn = RxAEP | RxARP | RxAJAB; - } else { - rxCfgOff = RxAEP | RxARP | RxAJAB; - } - if (rx_mode & MAC_LOOPBACK) { - rxCfgOn |= RxATP; - txCfgOn |= TxMLB; - } else { - if (!(( (struct sis900_private *)(dev->priv) )->full_duplex)) - rxCfgOff |= RxATP; - txCfgOff |= TxMLB; - } - if (sis900_debug > 2) { - printk(KERN_INFO "Before Set TxCfg=%8.8x\n",inl(ioaddr+txcfg)); - printk(KERN_INFO "Before Set RxCfg=%8.8x\n",inl(ioaddr+rxcfg)); - } + outl(RFEN | rx_mode, ioaddr + rfcr); - outl((inl(ioaddr + rxcfg) | rxCfgOn) & ~rxCfgOff, ioaddr + rxcfg); - outl((inl(ioaddr + txcfg) | txCfgOn) & ~txCfgOff, ioaddr + txcfg); + /* sis900 is capatable of looping back packet at MAC level for debugging purpose */ + if (net_dev->flags & IFF_LOOPBACK) { + u32 cr_saved; + /* We must disable Tx/Rx before setting loopback mode */ + cr_saved = inl(ioaddr + cr); + outl(cr_saved | TxDIS | RxDIS, ioaddr + cr); + /* enable loopback */ + outl(inl(ioaddr + txcfg) | TxMLB, ioaddr + txcfg); + outl(inl(ioaddr + rxcfg) | RxATX, ioaddr + rxcfg); + /* restore cr */ + outl(cr_saved, ioaddr + cr); + } - if (sis900_debug > 2) { - printk(KERN_INFO "After Set TxCfg=%8.8x\n",inl(ioaddr+txcfg)); - printk(KERN_INFO "After Set RxCfg=%8.8x\n",inl(ioaddr+rxcfg)); - printk(KERN_INFO "Receive Filter Register:%8.8x\n", - inl(ioaddr + rfcr)); - } return; } -static void sis900_reset(struct device *dev) +static void sis900_reset(struct device *net_dev) { - long ioaddr = dev->base_addr; + long ioaddr = net_dev->base_addr; + int i = 0; + u32 status = TxRCMP | RxRCMP; outl(0, ioaddr + ier); outl(0, ioaddr + imr); outl(0, ioaddr + rfcr); outl(RxRESET | TxRESET | RESET, ioaddr + cr); - outl(PESEL, ioaddr + cfg); + + /* Check that the chip has finished the reset. */ + while (status && (i++ < 1000)) { + status ^= (inl(isr + ioaddr) & status); + } - set_rx_mode(dev); + outl(PESEL, ioaddr + cfg); } #ifdef MODULE int init_module(void) { - return sis900_probe(0); + return sis900_probe(NULL); } void cleanup_module(void) { - struct device *next_dev; + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + while (root_sis900_dev) { + struct sis900_private *sis_priv = + (struct sis900_private *)root_sis900_dev->priv; + struct device *next_dev = sis_priv->next_module; + + unregister_netdev(root_sis900_dev); + release_region(root_sis900_dev->base_addr, + sis_priv->mac->io_size); + kfree(sis_priv); + kfree(root_sis900_dev); - /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ - while (root_sis900_dev) { - struct sis900_private *tp = - (struct sis900_private *)root_sis900_dev->priv; - next_dev = tp->next_module; - unregister_netdev(root_sis900_dev); - release_region(root_sis900_dev->base_addr, - pci_tbl[tp->chip_id].io_size); - kfree(tp); - kfree(root_sis900_dev); - root_sis900_dev = next_dev; + root_sis900_dev = next_dev; } } #endif /* MODULE */ -/* - * Local variables: - * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c sis900.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" - * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c sis900.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" - * c-indent-level: 4 - * c-basic-offset: 4 - * tab-width: 4 - * End: - */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sis900.h linux/drivers/net/sis900.h --- v2.2.13/linux/drivers/net/sis900.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sis900.h Tue Jan 4 10:12:17 2000 @@ -0,0 +1,248 @@ +/* sis900.h Definitions for SiS ethernet controllers including 7014/7016 and 900 + * Copyright 1999 Silicon Integrated System Corporation + * References: + * SiS 7016 Fast Ethernet PCI Bus 10/100 Mbps LAN Controller with OnNow Support, + * preliminary Rev. 1.0 Jan. 14, 1998 + * SiS 900 Fast Ethernet PCI Bus 10/100 Mbps LAN Single Chip with OnNow Support, + * preliminary Rev. 1.0 Nov. 10, 1998 + * SiS 7014 Single Chip 100BASE-TX/10BASE-T Physical Layer Solution, + * preliminary Rev. 1.0 Jan. 18, 1998 + * http://www.sis.com.tw/support/databook.htm + */ + +/* MAC operationl registers of SiS 7016 and SiS 900 ehternet controller */ +/* The I/O extent, SiS 900 needs 256 bytes of io address */ +#define SIS900_TOTAL_SIZE 0x100 + +/* Symbolic offsets to registers. */ +enum sis900_registers { + cr=0x0, //Command Register + cfg=0x4, //Configuration Register + mear=0x8, //EEPROM Access Register + ptscr=0xc, //PCI Test Control Register + isr=0x10, //Interrupt Status Register + imr=0x14, //Interrupt Mask Register + ier=0x18, //Interrupt Enable Register + epar=0x18, //Enhanced PHY Access Register + txdp=0x20, //Transmit Descriptor Pointer Register + txcfg=0x24, //Transmit Configuration Register + rxdp=0x30, //Receive Descriptor Pointer Register + rxcfg=0x34, //Receive Configuration Register + flctrl=0x38, //Flow Control Register + rxlen=0x3c, //Receive Packet Length Register + rfcr=0x48, //Receive Filter Control Register + rfdr=0x4C, //Receive Filter Data Register + pmctrl=0xB0, //Power Management Control Register + pmer=0xB4 //Power Management Wake-up Event Register +}; + +/* Symbolic names for bits in various registers */ +enum sis900_command_register_bits { + RESET = 0x00000100, SWI = 0x00000080, RxRESET = 0x00000020, + TxRESET = 0x00000010, RxDIS = 0x00000008, RxENA = 0x00000004, + TxDIS = 0x00000002, TxENA = 0x00000001 +}; + +enum sis900_configuration_register_bits { + DESCRFMT = 0x00000100 /* 7016 specific */, REQALG = 0x00000080, + SB = 0x00000040, POW = 0x00000020, EXD = 0x00000010, + PESEL = 0x00000008, LPM = 0x00000004, BEM = 0x00000001 +}; + +enum sis900_eeprom_access_reigster_bits { + MDC = 0x00000040, MDDIR = 0x00000020, MDIO = 0x00000010, /* 7016 specific */ + EECS = 0x00000008, EECLK = 0x00000004, EEDO = 0x00000002, + EEDI = 0x00000001 +}; + +enum sis900_interrupt_register_bits { + WKEVT = 0x10000000, TxPAUSEEND = 0x08000000, TxPAUSE = 0x04000000, + TxRCMP = 0x02000000, RxRCMP = 0x01000000, DPERR = 0x00800000, + SSERR = 0x00400000, RMABT = 0x00200000, RTABT = 0x00100000, + RxSOVR = 0x00010000, HIBERR = 0x00008000, SWINT = 0x00001000, + MIBINT = 0x00000800, TxURN = 0x00000400, TxIDLE = 0x00000200, + TxERR = 0x00000100, TxDESC = 0x00000080, TxOK = 0x00000040, + RxORN = 0x00000020, RxIDLE = 0x00000010, RxEARLY = 0x00000008, + RxERR = 0x00000004, RxDESC = 0x00000002, RxOK = 0x00000001 +}; + +enum sis900_interrupt_enable_reigster_bits { + IE = 0x00000001 +}; + +/* maximum dma burst fro transmission and receive*/ +#define MAX_DMA_RANGE 7 /* actually 0 means MAXIMUM !! */ +#define TxMXDMA_shift 20 +#define RxMXDMA_shift 20 +#define TX_DMA_BURST 0 +#define RX_DMA_BURST 0 + +/* transmit FIFO threshholds */ +#define TX_FILL_THRESH 16 /* 1/4 FIFO size */ +#define TxFILLT_shift 8 +#define TxDRNT_shift 0 +#define TxDRNT_100 48 /* 3/4 FIFO size */ +#define TxDRNT_10 16 /* 1/2 FIFO size */ + +enum sis900_transmit_config_register_bits { + TxCSI = 0x80000000, TxHBI = 0x40000000, TxMLB = 0x20000000, + TxATP = 0x10000000, TxIFG = 0x0C000000, TxFILLT = 0x00003F00, + TxDRNT = 0x0000003F +}; + +/* recevie FIFO thresholds */ +#define RxDRNT_shift 1 +#define RxDRNT_100 16 /* 1/2 FIFO size */ +#define RxDRNT_10 24 /* 3/4 FIFO size */ + +enum sis900_reveive_config_register_bits { + RxAEP = 0x80000000, RxARP = 0x40000000, RxATX = 0x10000000, + RxAJAB = 0x08000000, RxDRNT = 0x0000007F +}; + +#define RFAA_shift 28 +#define RFADDR_shift 16 + +enum sis900_receive_filter_control_register_bits { + RFEN = 0x80000000, RFAAB = 0x40000000, RFAAM = 0x20000000, + RFAAP = 0x10000000, RFPromiscuous = (RFAAB|RFAAM|RFAAP) +}; + +enum sis900_reveive_filter_data_mask { + RFDAT = 0x0000FFFF +}; + +/* EEPROM Addresses */ +enum sis900_eeprom_address { + EEPROMSignature = 0x00, EEPROMVendorID = 0x02, EEPROMDeviceID = 0x03, + EEPROMMACAddr = 0x08, EEPROMChecksum = 0x0b +}; + +/* The EEPROM commands include the alway-set leading bit. Refer to NM93Cxx datasheet */ +enum sis900_eeprom_command { + EEread = 0x0180, EEwrite = 0x0140, EEerase = 0x01C0, + EEwriteEnable = 0x0130, EEwriteDisable = 0x0100, + EEeraseAll = 0x0120, EEwriteAll = 0x0110, + EEaddrMask = 0x013F, EEcmdShift = 16 +}; + +/* Manamgement Data I/O (mdio) frame */ +#define MIIread 0x6000 +#define MIIwrite 0x5002 +#define MIIpmdShift 7 +#define MIIregShift 2 +#define MIIcmdLen 16 +#define MIIcmdShift 16 + +/* Buffer Descriptor Status*/ +enum sis900_buffer_status { + OWN = 0x80000000, MORE = 0x40000000, INTR = 0x20000000, + SUPCRC = 0x10000000, INCCRC = 0x10000000, + OK = 0x08000000, DSIZE = 0x00000FFF +}; +/* Status for TX Buffers */ +enum sis900_tx_buffer_status { + ABORT = 0x04000000, UNDERRUN = 0x02000000, NOCARRIER = 0x01000000, + DEFERD = 0x00800000, EXCDEFER = 0x00400000, OWCOLL = 0x00200000, + EXCCOLL = 0x00100000, COLCNT = 0x000F0000 +}; + +enum sis900_rx_bufer_status { + OVERRUN = 0x02000000, DEST = 0x00800000, BCAST = 0x01800000, + MCAST = 0x01000000, UNIMATCH = 0x00800000, TOOLONG = 0x00400000, + RUNT = 0x00200000, RXISERR = 0x00100000, CRCERR = 0x00080000, + FAERR = 0x00040000, LOOPBK = 0x00020000, RXCOL = 0x00010000 +}; + +/* MII register offsets */ +enum mii_registers { + MII_CONTROL = 0x0000, MII_STATUS = 0x0001, MII_PHY_ID0 = 0x0002, + MII_PHY_ID1 = 0x0003, MII_ANADV = 0x0004, MII_ANLPAR = 0x0005, + MII_ANEXT = 0x0006 +}; + +/* mii registers specific to SiS 900 */ +enum sis_mii_registers { + MII_CONFIG1 = 0x0010, MII_CONFIG2 = 0x0011, MII_STSOUT = 0x0012, + MII_MASK = 0x0013 +}; + +/* mii registers specific to AMD 79C901 */ +enum amd_mii_registers { + MII_STATUS_SUMMARY = 0x0018 +}; + +/* MII Control register bit definitions. */ +enum mii_control_register_bits { + MII_CNTL_FDX = 0x0100, MII_CNTL_RST_AUTO = 0x0200, + MII_CNTL_ISOLATE = 0x0400, MII_CNTL_PWRDWN = 0x0800, + MII_CNTL_AUTO = 0x1000, MII_CNTL_SPEED = 0x2000, + MII_CNTL_LPBK = 0x4000, MII_CNTL_RESET = 0x8000 +}; + +/* MII Status register bit */ +enum mii_status_register_bits { + MII_STAT_EXT = 0x0001, MII_STAT_JAB = 0x0002, + MII_STAT_LINK = 0x0004, MII_STAT_CAN_AUTO = 0x0008, + MII_STAT_FAULT = 0x0010, MII_STAT_AUTO_DONE = 0x0020, + MII_STAT_CAN_T = 0x0800, MII_STAT_CAN_T_FDX = 0x1000, + MII_STAT_CAN_TX = 0x2000, MII_STAT_CAN_TX_FDX = 0x4000, + MII_STAT_CAN_T4 = 0x8000 +}; + +#define MII_ID1_OUI_LO 0xFC00 /* low bits of OUI mask */ +#define MII_ID1_MODEL 0x03F0 /* model number */ +#define MII_ID1_REV 0x000F /* model number */ + +/* MII NWAY Register Bits ... + valid for the ANAR (Auto-Negotiation Advertisement) and + ANLPAR (Auto-Negotiation Link Partner) registers */ +enum mii_nway_register_bits { + MII_NWAY_NODE_SEL = 0x001f, MII_NWAY_CSMA_CD = 0x0001, + MII_NWAY_T = 0x0020, MII_NWAY_T_FDX = 0x0040, + MII_NWAY_TX = 0x0080, MII_NWAY_TX_FDX = 0x0100, + MII_NWAY_T4 = 0x0200, MII_NWAY_PAUSE = 0x0400, + MII_NWAY_RF = 0x2000, MII_NWAY_ACK = 0x4000, + MII_NWAY_NP = 0x8000 +}; + +enum mii_stsout_register_bits { + MII_STSOUT_LINK_FAIL = 0x4000, + MII_STSOUT_SPD = 0x0080, MII_STSOUT_DPLX = 0x0040 +}; + +enum mii_stssum_register_bits { + MII_STSSUM_LINK = 0x0008, MII_STSSUM_DPLX = 0x0004, + MII_STSSUM_AUTO = 0x0002, MII_STSSUM_SPD = 0x0001 +}; + +#define FDX_CAPABLE_DUPLEX_UNKNOWN 0 +#define FDX_CAPABLE_HALF_SELECTED 1 +#define FDX_CAPABLE_FULL_SELECTED 2 + +#define HW_SPEED_UNCONFIG 0 +#define HW_SPEED_HOME 1 +#define HW_SPEED_10_MBPS 10 +#define HW_SPEED_100_MBPS 100 +#define HW_SPEED_DEFAULT (HW_SPEED_100_MBPS) + +#define CRC_SIZE 4 +#define MAC_HEADER_SIZE 14 + +#define TX_BUF_SIZE 1536 +#define RX_BUF_SIZE 1536 + +#define NUM_TX_DESC 16 /* Number of Tx descriptor registers. */ +#define NUM_RX_DESC 16 /* Number of Rx descriptor registers. */ + +#define TRUE 1 +#define FALSE 0 + +/* PCI stuff, should be move to pic.h */ +#define PCI_DEVICE_ID_SI_900 0x900 +#define PCI_DEVICE_ID_SI_7016 0x7016 + +/* ioctl for accessing MII transveiver */ +#define SIOCGMIIPHY (SIOCDEVPRIVATE) /* Get the PHY in use. */ +#define SIOCGMIIREG (SIOCDEVPRIVATE+1) /* Read a PHY register. */ +#define SIOCSMIIREG (SIOCDEVPRIVATE+2) /* Write a PHY register */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/Makefile linux/drivers/net/sk98lin/Makefile --- v2.2.13/linux/drivers/net/sk98lin/Makefile Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/Makefile Tue Jan 4 10:12:17 2000 @@ -0,0 +1,80 @@ +# File: drivers/sk98lin/Makefile +# +# Makefile for the SysKonnect SK-98xx device driver. +# + +ifeq ($(CONFIG_SK98LIN),y) + O_TARGET := sk98lin.o + O_OBJS = skge.o skaddr.o skgehwt.o skgeinit.o skgepnmi.o skgesirq.o \ + ski2c.o sklm80.o skqueue.o skrlmt.o sktimer.o skvpd.o \ + skxmac2.o skcsum.o +else + ifeq ($(CONFIG_SK98LIN),m) + MOD_LIST_NAME := SK98LIN_MODULES + M_OBJS := sk98lin.o + O_TARGET := sk98lin.o + O_OBJS = skge.o skaddr.o skgehwt.o skgeinit.o skgepnmi.o skgesirq.o \ + ski2c.o sklm80.o skqueue.o skrlmt.o sktimer.o skvpd.o \ + skxmac2.o skcsum.o + endif +endif + +# DBGDEF = \ +# -DDEBUG + +ifdef DEBUG +DBGDEF += \ +-DSK_DEBUG_CHKMOD=0x00000000L \ +-DSK_DEBUG_CHKCAT=0x00000000L +endif + + +# **** possible debug modules for SK_DEBUG_CHKMOD ***************** +# SK_DBGMOD_MERR 0x00000001L /* general module error indication */ +# SK_DBGMOD_HWM 0x00000002L /* Hardware init module */ +# SK_DBGMOD_RLMT 0x00000004L /* RLMT module */ +# SK_DBGMOD_VPD 0x00000008L /* VPD module */ +# SK_DBGMOD_I2C 0x00000010L /* I2C module */ +# SK_DBGMOD_PNMI 0x00000020L /* PNMI module */ +# SK_DBGMOD_CSUM 0x00000040L /* CSUM module */ +# SK_DBGMOD_ADDR 0x00000080L /* ADDR module */ +# SK_DBGMOD_DRV 0x00010000L /* DRV module */ + +# **** possible debug categories for SK_DEBUG_CHKCAT ************** +# *** common modules *** +# SK_DBGCAT_INIT 0x00000001L module/driver initialization +# SK_DBGCAT_CTRL 0x00000002L controlling: add/rmv MCA/MAC and other controls (IOCTL) +# SK_DBGCAT_ERR 0x00000004L error handling paths +# SK_DBGCAT_TX 0x00000008L transmit path +# SK_DBGCAT_RX 0x00000010L receive path +# SK_DBGCAT_IRQ 0x00000020L general IRQ handling +# SK_DBGCAT_QUEUE 0x00000040L any queue management +# SK_DBGCAT_DUMP 0x00000080L large data output e.g. hex dump +# SK_DBGCAT_FATAL 0x00000100L large data output e.g. hex dump + +# *** driver (file skge.c) *** +# SK_DBGCAT_DRV_ENTRY 0x00010000 entry points +# SK_DBGCAT_DRV_??? 0x00020000 not used +# SK_DBGCAT_DRV_MCA 0x00040000 multicast +# SK_DBGCAT_DRV_TX_PROGRESS 0x00080000 tx path +# SK_DBGCAT_DRV_RX_PROGRESS 0x00100000 rx path +# SK_DBGCAT_DRV_PROGRESS 0x00200000 general runtime +# SK_DBGCAT_DRV_??? 0x00400000 not used +# SK_DBGCAT_DRV_PROM 0x00800000 promiscuous mode +# SK_DBGCAT_DRV_TX_FRAME 0x01000000 display tx frames +# SK_DBGCAT_DRV_ERROR 0x02000000 error conditions +# SK_DBGCAT_DRV_INT_SRC 0x04000000 interrupts sources +# SK_DBGCAT_DRV_EVENT 0x08000000 driver events + +EXTRA_CFLAGS += -I. -DSK_USE_CSUM $(DBGDEF) + +include $(TOPDIR)/Rules.make + +clean: + rm -f core *.o *.a *.s + + + + + + diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/h/lm80.h linux/drivers/net/sk98lin/h/lm80.h --- v2.2.13/linux/drivers/net/sk98lin/h/lm80.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/h/lm80.h Tue Jan 4 10:12:17 2000 @@ -0,0 +1,192 @@ +/****************************************************************************** + * + * Name: lm80.h + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.2 $ + * Date: $Date: 1999/03/12 13:26:51 $ + * Purpose: Contains all defines for the LM80 Chip + * (National Semiconductor). + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * $Log: lm80.h,v $ + * Revision 1.2 1999/03/12 13:26:51 malthoff + * remove __STDC__. + * + * Revision 1.1 1998/06/19 09:28:31 malthoff + * created. + * + * + ******************************************************************************/ + +#ifndef __INC_LM80_H +#define __INC_LM80_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* defines ********************************************************************/ + +/* + * LM80 register definition + * + * All registers are 8 bit wide + */ +#define LM80_CFG 0x00 /* Configuration Register */ +#define LM80_ISRC_1 0x01 /* Interrupt Status Register 1 */ +#define LM80_ISRC_2 0x02 /* Interrupt Status Register 2 */ +#define LM80_IMSK_1 0x03 /* Interrupt Mask Register 1 */ +#define LM80_IMSK_2 0x04 /* Interrupt Mask Register 2 */ +#define LM80_FAN_CTRL 0x05 /* Fan Devisor/RST#/OS# Register */ +#define LM80_TEMP_CTRL 0x06 /* OS# Config, Temp Res. Reg */ + /* 0x07 - 0x1f reserved */ + /* current values */ +#define LM80_VT0_IN 0x20 /* current Voltage 0 value */ +#define LM80_VT1_IN 0x21 /* current Voltage 1 value */ +#define LM80_VT2_IN 0x22 /* current Voltage 2 value */ +#define LM80_VT3_IN 0x23 /* current Voltage 3 value */ +#define LM80_VT4_IN 0x24 /* current Voltage 4 value */ +#define LM80_VT5_IN 0x25 /* current Voltage 5 value */ +#define LM80_VT6_IN 0x26 /* current Voltage 6 value */ +#define LM80_TEMP_IN 0x27 /* current temperature value */ +#define LM80_FAN1_IN 0x28 /* current Fan 1 count */ +#define LM80_FAN2_IN 0x29 /* current Fan 2 count */ + /* limit values */ +#define LM80_VT0_HIGH_LIM 0x2a /* high limit val for Voltage 0 */ +#define LM80_VT0_LOW_LIM 0x2b /* low limit val for Voltage 0 */ +#define LM80_VT1_HIGH_LIM 0x2c /* high limit val for Voltage 1 */ +#define LM80_VT1_LOW_LIM 0x2d /* low limit val for Voltage 1 */ +#define LM80_VT2_HIGH_LIM 0x2e /* high limit val for Voltage 2 */ +#define LM80_VT2_LOW_LIM 0x2f /* low limit val for Voltage 2 */ +#define LM80_VT3_HIGH_LIM 0x30 /* high limit val for Voltage 3 */ +#define LM80_VT3_LOW_LIM 0x31 /* low limit val for Voltage 3 */ +#define LM80_VT4_HIGH_LIM 0x32 /* high limit val for Voltage 4 */ +#define LM80_VT4_LOW_LIM 0x33 /* low limit val for Voltage 4 */ +#define LM80_VT5_HIGH_LIM 0x34 /* high limit val for Voltage 5 */ +#define LM80_VT5_LOW_LIM 0x35 /* low limit val for Voltage 5 */ +#define LM80_VT6_HIGH_LIM 0x36 /* high limit val for Voltage 6 */ +#define LM80_VT6_LOW_LIM 0x37 /* low limit val for Voltage 6 */ +#define LM80_THOT_LIM_UP 0x38 /* hot temperature limit (high) */ +#define LM80_THOT_LIM_LO 0x39 /* hot temperature limit (low) */ +#define LM80_TOS_LIM_UP 0x3a /* OS temperature limit (high) */ +#define LM80_TOS_LIM_LO 0x3b /* OS temperature limit (low) */ +#define LM80_FAN1_COUNT_LIM 0x3c /* Fan 1 count limit (high) */ +#define LM80_FAN2_COUNT_LIM 0x3d /* Fan 2 count limit (low) */ + /* 0x3e - 0x3f reserved */ + +/* + * LM80 bit definitions + */ + +/* LM80_CFG Configuration Register */ +#define LM80_CFG_START (1<<0) /* start monitoring operation */ +#define LM80_CFG_INT_ENA (1<<1) /* enables the INT# Interrupt output */ +#define LM80_CFG_INT_POL (1<<2) /* INT# pol: 0 act low, 1 act high */ +#define LM80_CFG_INT_CLR (1<<3) /* disables INT#/RST_OUT#/OS# outputs */ +#define LM80_CFG_RESET (1<<4) /* signals a reset */ +#define LM80_CFG_CHASS_CLR (1<<5) /* clears Chassis Intrusion (CI) pin */ +#define LM80_CFG_GPO (1<<6) /* drives the GPO# pin */ +#define LM80_CFG_INIT (1<<7) /* restore power on defaults */ + +/* LM80_ISRC_1 Interrupt Status Register 1 */ +/* LM80_IMSK_1 Interrupt Mask Register 1 */ +#define LM80_IS_VT0 (1<<0) /* limit exceeded for Voltage 0 */ +#define LM80_IS_VT1 (1<<1) /* limit exceeded for Voltage 1 */ +#define LM80_IS_VT2 (1<<2) /* limit exceeded for Voltage 2 */ +#define LM80_IS_VT3 (1<<3) /* limit exceeded for Voltage 3 */ +#define LM80_IS_VT4 (1<<4) /* limit exceeded for Voltage 4 */ +#define LM80_IS_VT5 (1<<5) /* limit exceeded for Voltage 5 */ +#define LM80_IS_VT6 (1<<6) /* limit exceeded for Voltage 6 */ +#define LM80_IS_INT_IN (1<<7) /* state of INT_IN# */ + +/* LM80_ISRC_2 Interrupt Status Register 2 */ +/* LM80_IMSK_2 Interrupt Mask Register 2 */ +#define LM80_IS_TEMP (1<<0) /* HOT temperature limit exceeded */ +#define LM80_IS_BTI (1<<1) /* state of BTI# pin */ +#define LM80_IS_FAN1 (1<<2) /* count limit exceeded for Fan 1 */ +#define LM80_IS_FAN2 (1<<3) /* count limit exceeded for Fan 2 */ +#define LM80_IS_CI (1<<4) /* Chassis Intrusion occured */ +#define LM80_IS_OS (1<<5) /* OS temperature limit exceeded */ + /* bit 6 and 7 are reserved in LM80_ISRC_2 */ +#define LM80_IS_HT_IRQ_MD (1<<6) /* Hot temperature interrupt mode */ +#define LM80_IS_OT_IRQ_MD (1<<7) /* OS temperature interrupt mode */ + +/* LM80_FAN_CTRL Fan Devisor/RST#/OS# Register */ +#define LM80_FAN1_MD_SEL (1<<0) /* Fan 1 mode select */ +#define LM80_FAN2_MD_SEL (1<<1) /* Fan 2 mode select */ +#define LM80_FAN1_PRM_CTL (3<<2) /* Fan 1 speed control */ +#define LM80_FAN2_PRM_CTL (3<<4) /* Fan 2 speed control */ +#define LM80_FAN_OS_ENA (1<<6) /* enable OS mode on RST_OUT#/OS# pins*/ +#define LM80_FAN_RST_ENA (1<<7) /* sets RST_OUT#/OS# pins in RST mode */ + +/* LM80_TEMP_CTRL OS# Config, Temp Res. Reg */ +#define LM80_TEMP_OS_STAT (1<<0) /* mirrors the state of RST_OUT#/OS# */ +#define LM80_TEMP_OS_POL (1<<1) /* select OS# polarity */ +#define LM80_TEMP_OS_MODE (1<<2) /* selects Interrupt mode */ +#define LM80_TEMP_RES (1<<3) /* selects 9 or 11 bit temp resulution*/ +#define LM80_TEMP_LSB (0xf<<4)/* 4 LSBs of 11 bit temp data */ +#define LM80_TEMP_LSB_9 (1<<7) /* LSB of 9 bit temperature data */ + + /* 0x07 - 0x1f reserved */ +/* LM80_VT0_IN current Voltage 0 value */ +/* LM80_VT1_IN current Voltage 1 value */ +/* LM80_VT2_IN current Voltage 2 value */ +/* LM80_VT3_IN current Voltage 3 value */ +/* LM80_VT4_IN current Voltage 4 value */ +/* LM80_VT5_IN current Voltage 5 value */ +/* LM80_VT6_IN current Voltage 6 value */ +/* LM80_TEMP_IN current temperature value */ +/* LM80_FAN1_IN current Fan 1 count */ +/* LM80_FAN2_IN current Fan 2 count */ +/* LM80_VT0_HIGH_LIM high limit val for Voltage 0 */ +/* LM80_VT0_LOW_LIM low limit val for Voltage 0 */ +/* LM80_VT1_HIGH_LIM high limit val for Voltage 1 */ +/* LM80_VT1_LOW_LIM low limit val for Voltage 1 */ +/* LM80_VT2_HIGH_LIM high limit val for Voltage 2 */ +/* LM80_VT2_LOW_LIM low limit val for Voltage 2 */ +/* LM80_VT3_HIGH_LIM high limit val for Voltage 3 */ +/* LM80_VT3_LOW_LIM low limit val for Voltage 3 */ +/* LM80_VT4_HIGH_LIM high limit val for Voltage 4 */ +/* LM80_VT4_LOW_LIM low limit val for Voltage 4 */ +/* LM80_VT5_HIGH_LIM high limit val for Voltage 5 */ +/* LM80_VT5_LOW_LIM low limit val for Voltage 5 */ +/* LM80_VT6_HIGH_LIM high limit val for Voltage 6 */ +/* LM80_VT6_LOW_LIM low limit val for Voltage 6 */ +/* LM80_THOT_LIM_UP hot temperature limit (high) */ +/* LM80_THOT_LIM_LO hot temperature limit (low) */ +/* LM80_TOS_LIM_UP OS temperature limit (high) */ +/* LM80_TOS_LIM_LO OS temperature limit (low) */ +/* LM80_FAN1_COUNT_LIM Fan 1 count limit (high) */ +/* LM80_FAN2_COUNT_LIM Fan 2 count limit (low) */ + /* 0x3e - 0x3f reserved */ + +#define LM80_ADDR 0x28 /* LM80 default addr */ + +/* typedefs *******************************************************************/ + + +/* function prototypes ********************************************************/ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __INC_LM80_H */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/h/skaddr.h linux/drivers/net/sk98lin/h/skaddr.h --- v2.2.13/linux/drivers/net/sk98lin/h/skaddr.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/h/skaddr.h Tue Jan 4 10:12:17 2000 @@ -0,0 +1,309 @@ +/****************************************************************************** + * + * Name: skaddr.h + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.19 $ + * Date: $Date: 1999/05/28 10:56:07 $ + * Purpose: Header file for Address Management (MC, UC, Prom) + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: skaddr.h,v $ + * Revision 1.19 1999/05/28 10:56:07 rassmann + * Editorial changes. + * + * Revision 1.18 1999/04/06 17:22:04 rassmann + * Added private "ActivePort". + * + * Revision 1.17 1999/01/14 16:18:19 rassmann + * Corrected multicast initialization. + * + * Revision 1.16 1999/01/04 10:30:36 rassmann + * SkAddrOverride only possible after SK_INIT_IO phase. + * + * Revision 1.15 1998/12/29 13:13:11 rassmann + * An address override is now preserved in the SK_INIT_IO phase. + * All functions return an int now. + * Extended parameter checking. + * + * Revision 1.14 1998/11/24 12:39:45 rassmann + * Reserved multicast entry for BPDU address. + * 13 multicast entries left for protocol. + * + * Revision 1.13 1998/11/13 17:24:32 rassmann + * Changed return value of SkAddrOverride to int. + * + * Revision 1.12 1998/11/13 16:56:19 rassmann + * Added macro SK_ADDR_COMPARE. + * Changed return type of SkAddrOverride to SK_BOOL. + * + * Revision 1.11 1998/10/28 18:16:35 rassmann + * Avoiding I/Os before SK_INIT_RUN level. + * Aligning InexactFilter. + * + * Revision 1.10 1998/10/22 11:39:10 rassmann + * Corrected signed/unsigned mismatches. + * + * Revision 1.9 1998/10/15 15:15:49 rassmann + * Changed Flags Parameters from SK_U8 to int. + * Checked with lint. + * + * Revision 1.8 1998/09/24 19:15:12 rassmann + * Code cleanup. + * + * Revision 1.7 1998/09/18 20:22:13 rassmann + * Added HW access. + * + * Revision 1.6 1998/09/04 19:40:20 rassmann + * Interface enhancements. + * + * Revision 1.5 1998/09/04 12:40:57 rassmann + * Interface cleanup. + * + * Revision 1.4 1998/09/04 12:14:13 rassmann + * Interface cleanup. + * + * Revision 1.3 1998/09/02 16:56:40 rassmann + * Updated interface. + * + * Revision 1.2 1998/08/27 14:26:09 rassmann + * Updated interface. + * + * Revision 1.1 1998/08/21 08:31:08 rassmann + * First public version. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Description: + * + * This module is intended to manage multicast addresses and promiscuous mode + * on GEnesis adapters. + * + * Include File Hierarchy: + * + * "skdrv1st.h" + * ... + * "sktypes.h" + * "skqueue.h" + * "skaddr.h" + * ... + * "skdrv2nd.h" + * + ******************************************************************************/ + +#ifndef __INC_SKADDR_H +#define __INC_SKADDR_H + +#ifdef __cplusplus +xxxx /* not supported yet - force error */ +extern "C" { +#endif /* cplusplus */ + +/* defines ********************************************************************/ + +#define SK_MAC_ADDR_LEN 6 /* Length of MAC address. */ +#define SK_MAX_ADDRS 14 /* #Addrs for exact match. */ + +/* ----- Common return values ----- */ + +#define SK_ADDR_SUCCESS 0 /* Function returned successfully. */ +#define SK_ADDR_ILLEGAL_PORT 100 /* Port number too high. */ +#define SK_ADDR_TOO_EARLY 101 /* Function called too early. */ + +/* ----- Clear/Add flag bits ----- */ + +#define SK_ADDR_PERMANENT 1 /* RLMT Address */ + +/* ----- Additional Clear flag bits ----- */ + +#define SK_MC_SW_ONLY 2 /* Do not update HW when clearing. */ + +/* ----- Override flag bits ----- */ + +#define SK_ADDR_VIRTUAL_ADDRESS 0 +#define SK_ADDR_PHYSICAL_ADDRESS 1 + +/* ----- Override return values ----- */ + +#define SK_ADDR_OVERRIDE_SUCCESS (SK_ADDR_SUCCESS) +#define SK_ADDR_DUPLICATE_ADDRESS 1 +#define SK_ADDR_MULTICAST_ADDRESS 2 + +/* ----- Partitioning of excact match table ----- */ + +#define SK_ADDR_EXACT_MATCHES 16 /* #Exact match entries. */ + +#define SK_ADDR_FIRST_MATCH_RLMT 1 +#define SK_ADDR_LAST_MATCH_RLMT 2 +#define SK_ADDR_FIRST_MATCH_DRV 3 +#define SK_ADDR_LAST_MATCH_DRV (SK_ADDR_EXACT_MATCHES - 1) + +/* ----- SkAddrMcAdd/SkAddrMcUpdate return values ----- */ + +#define SK_MC_FILTERING_EXACT 0 /* Exact filtering. */ +#define SK_MC_FILTERING_INEXACT 1 /* Inexact filtering. */ + +/* ----- Additional SkAddrMcAdd return values ----- */ + +#define SK_MC_ILLEGAL_ADDRESS 2 /* Illegal address. */ +#define SK_MC_ILLEGAL_PORT 3 /* Illegal port (not the active one). */ +#define SK_MC_RLMT_OVERFLOW 4 /* Too many RLMT mc addresses. */ + +/* Promiscuous mode bits ----- */ + +#define SK_PROM_MODE_NONE 0 /* Normal receive. */ +#define SK_PROM_MODE_LLC 1 /* Receive all LLC frames. */ +#define SK_PROM_MODE_ALL_MC 2 /* Receive all multicast frames. */ +/* #define SK_PROM_MODE_NON_LLC 4 */ /* Receive all non-LLC frames. */ + +/* Macros */ + +#ifndef SK_ADDR_EQUAL +#ifndef SK_ADDR_DWORD_COMPARE +#define SK_ADDR_EQUAL(A1,A2) ( \ + ((SK_U8 *)(A1))[5] == ((SK_U8 *)(A2))[5] && \ + ((SK_U8 *)(A1))[4] == ((SK_U8 *)(A2))[4] && \ + ((SK_U8 *)(A1))[3] == ((SK_U8 *)(A2))[3] && \ + ((SK_U8 *)(A1))[2] == ((SK_U8 *)(A2))[2] && \ + ((SK_U8 *)(A1))[1] == ((SK_U8 *)(A2))[1] && \ + ((SK_U8 *)(A1))[0] == ((SK_U8 *)(A2))[0]) +#else /* SK_ADDR_DWORD_COMPARE */ +#define SK_ADDR_EQUAL(A1,A2) ( \ + *(SK_U32 *)&(((SK_U8 *)(A1))[2]) == *(SK_U32 *)&(((SK_U8 *)(A2))[2]) && \ + *(SK_U32 *)&(((SK_U8 *)(A1))[0]) == *(SK_U32 *)&(((SK_U8 *)(A2))[0])) +#endif /* SK_ADDR_DWORD_COMPARE */ +#endif /* SK_ADDR_EQUAL */ + +/* typedefs *******************************************************************/ + +typedef struct s_MacAddr { + SK_U8 a[SK_MAC_ADDR_LEN]; +} SK_MAC_ADDR; + +/* SK_FILTER is used to ensure alignment of the filter. */ +typedef union s_InexactFilter { + SK_U8 Bytes[8]; + SK_U64 Val; /* Dummy entry for alignment only. */ +} SK_FILTER64; + +typedef struct s_AddrPort { + +/* ----- Public part (read-only) ----- */ + + SK_MAC_ADDR PermanentMacAddress; /* Physical MAC Address. */ + SK_MAC_ADDR CurrentMacAddress; /* Physical MAC Address. */ + int PromMode; /* Promiscuous Mode. */ + +/* ----- Private part ----- */ + + SK_BOOL CurrentMacAddressSet; /* CurrentMacAddress is set. */ + SK_MAC_ADDR PreviousMacAddress; /* Prev. phys. MAC Address. */ + SK_U32 FirstExactMatchRlmt; + SK_U32 NextExactMatchRlmt; + SK_U32 FirstExactMatchDrv; + SK_U32 NextExactMatchDrv; + SK_MAC_ADDR Exact[SK_ADDR_EXACT_MATCHES]; + SK_FILTER64 InexactFilter; /* For 64-bit hash register. */ +} SK_ADDR_PORT; + +typedef struct s_Addr { + +/* ----- Public part (read-only) ----- */ + + SK_ADDR_PORT Port[SK_MAX_MACS]; + SK_MAC_ADDR PermanentMacAddress; /* Logical MAC Address. */ + SK_MAC_ADDR CurrentMacAddress; /* Logical MAC Address. */ + +/* ----- Private part ----- */ + +#if 0 + SK_BOOL Initialized; /* Flag: Addr module is initialized. */ +#endif /* 0 */ + SK_BOOL CurrentMacAddressSet; /* CurrentMacAddress is set. */ + SK_U32 ActivePort; /* Vie of module ADDR. */ +} SK_ADDR; + +/* function prototypes ********************************************************/ + +#ifndef SK_KR_PROTO + +/* Functions provided by SkRlmt */ + +/* ANSI/C++ compliant function prototypes */ + +extern int SkAddrInit( + SK_AC *pAC, + SK_IOC IoC, + int Level); + +extern int SkAddrMcClear( + SK_AC *pAC, + SK_IOC IoC, + SK_U32 PortIdx, + int Flags); + +extern int SkAddrMcAdd( + SK_AC *pAC, + SK_IOC IoC, + SK_U32 PortIdx, + SK_MAC_ADDR *pMc, + int Flags); + +extern int SkAddrMcUpdate( + SK_AC *pAC, + SK_IOC IoC, + SK_U32 PortIdx); + +extern int SkAddrOverride( + SK_AC *pAC, + SK_IOC IoC, + SK_U32 PortIdx, + SK_MAC_ADDR *pNewAddr, + int Flags); + +extern int SkAddrPromiscuousChange( + SK_AC *pAC, + SK_IOC IoC, + SK_U32 PortIdx, + int NewPromMode); + +extern int SkAddrSwap( + SK_AC *pAC, + SK_IOC IoC, + SK_U32 FromPortIdx, + SK_U32 ToPortIdx); + +#else /* defined(SK_KR_PROTO)) */ + +/* Non-ANSI/C++ compliant function prototypes */ + +xxxx /* not supported yet - force error */ + +#endif /* defined(SK_KR_PROTO)) */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __INC_SKADDR_H */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/h/skcsum.h linux/drivers/net/sk98lin/h/skcsum.h --- v2.2.13/linux/drivers/net/sk98lin/h/skcsum.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/h/skcsum.h Tue Jan 4 10:12:17 2000 @@ -0,0 +1,223 @@ +/****************************************************************************** + * + * Name: skcsum.h + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.2 $ + * Date: $Date: 1998/09/04 12:16:34 $ + * Purpose: Store/verify Internet checksum in send/receive packets. + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: skcsum.h,v $ + * Revision 1.2 1998/09/04 12:16:34 mhaveman + * Checked in for Stephan to allow compilation. + * -Added definition SK_CSUM_EVENT_CLEAR_PROTO_STATS to clear statistic + * -Added prototype for SkCsEvent() + * + * Revision 1.1 1998/09/01 15:36:53 swolf + * initial revision + * + * 01-Sep-1998 sw Created. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Description: + * + * Public header file for the "GEnesis" common module "CSUM". + * + * "GEnesis" is an abbreviation of "Gigabit Ethernet Network System in Silicon" + * and is the code name of this SysKonnect project. + * + * Compilation Options: + * + * SK_USE_CSUM - Define if CSUM is to be used. Otherwise, CSUM will be an + * empty module. + * + * SKCS_OVERWRITE_PROTO - Define to overwrite the default protocol id + * definitions. In this case, all SKCS_PROTO_xxx definitions must be made + * external. + * + * SKCS_OVERWRITE_STATUS - Define to overwrite the default return status + * definitions. In this case, all SKCS_STATUS_xxx definitions must be made + * external. + * + * Include File Hierarchy: + * + * "h/skcsum.h" + * "h/sktypes.h" + * "h/skqueue.h" + * + ******************************************************************************/ + +#ifndef __INC_SKCSUM_H +#define __INC_SKCSUM_H + +#include "h/sktypes.h" +#include "h/skqueue.h" + +/* defines ********************************************************************/ + +/* + * Define the default bit flags for 'SKCS_PACKET_INFO.ProtocolFlags' if no user + * overwrite. + */ +#ifndef SKCS_OVERWRITE_PROTO /* User overwrite? */ +#define SKCS_PROTO_IP 0x1 /* IP (Internet Protocol version 4) */ +#define SKCS_PROTO_TCP 0x2 /* TCP (Transmission Control Protocol) */ +#define SKCS_PROTO_UDP 0x4 /* UDP (User Datagram Protocol) */ + +/* Indices for protocol statistics. */ +#define SKCS_PROTO_STATS_IP 0 +#define SKCS_PROTO_STATS_UDP 1 +#define SKCS_PROTO_STATS_TCP 2 +#define SKCS_NUM_PROTOCOLS 3 /* Number of supported protocols. */ +#endif /* !SKCS_OVERWRITE_PROTO */ + +/* + * Define the default SKCS_STATUS type and values if no user overwrite. + * + * SKCS_STATUS_UNKNOWN_IP_VERSION - Not an IP v4 frame. + * SKCS_STATUS_IP_CSUM_ERROR - IP checksum error. + * SKCS_STATUS_IP_FRAGMENT - IP fragment (IP checksum ok). + * SKCS_STATUS_IP_CSUM_OK - IP checksum ok (not a TCP or UDP frame). + * SKCS_STATUS_TCP_CSUM_ERROR - TCP checksum error (IP checksum ok). + * SKCS_STATUS_UDP_CSUM_ERROR - UDP checksum error (IP checksum ok). + * SKCS_STATUS_TCP_CSUM_OK - IP and TCP checksum ok. + * SKCS_STATUS_UDP_CSUM_OK - IP and UDP checksum ok. + */ +#ifndef SKCS_OVERWRITE_STATUS /* User overwrite? */ +#define SKCS_STATUS int /* Define status type. */ + +#define SKCS_STATUS_UNKNOWN_IP_VERSION 1 +#define SKCS_STATUS_IP_CSUM_ERROR 2 +#define SKCS_STATUS_IP_FRAGMENT 3 +#define SKCS_STATUS_IP_CSUM_OK 4 +#define SKCS_STATUS_TCP_CSUM_ERROR 5 +#define SKCS_STATUS_UDP_CSUM_ERROR 6 +#define SKCS_STATUS_TCP_CSUM_OK 7 +#define SKCS_STATUS_UDP_CSUM_OK 8 +#endif /* !SKCS_OVERWRITE_STATUS */ + +/* Clear protocol statistics event. */ +#define SK_CSUM_EVENT_CLEAR_PROTO_STATS 1 + +/* + * Add two values in one's complement. + * + * Note: One of the two input values may be "longer" than 16-bit, but then the + * resulting sum may be 17 bits long. In this case, add zero to the result using + * SKCS_OC_ADD() again. + * + * Result = Value1 + Value2 + */ +#define SKCS_OC_ADD(Result, Value1, Value2) { \ + unsigned long Sum; \ + \ + Sum = (unsigned long) (Value1) + (unsigned long) (Value2); \ + /* Add-in any carry. */ \ + (Result) = (Sum & 0xffff) + (Sum >> 16); \ +} + +/* + * Subtract two values in one's complement. + * + * Result = Value1 - Value2 + */ +#define SKCS_OC_SUB(Result, Value1, Value2) \ + SKCS_OC_ADD((Result), (Value1), ~(Value2) & 0xffff) + +/* typedefs *******************************************************************/ + +/* + * SKCS_PROTO_STATS - The CSUM protocol statistics structure. + * + * There is one instance of this structure for each protocol supported. + */ +typedef struct s_CsProtocolStatistics { + SK_U64 RxOkCts; /* Receive checksum ok. */ + SK_U64 RxUnableCts; /* Unable to verify receive checksum. */ + SK_U64 RxErrCts; /* Receive checksum error. */ + SK_U64 TxOkCts; /* Transmit checksum ok. */ + SK_U64 TxUnableCts; /* Unable to verify transmit checksum. */ +} SKCS_PROTO_STATS; + +/* + * s_Csum - The CSUM module context structure. + */ +typedef struct s_Csum { + /* Enabled receive SK_PROTO_XXX bit flags. */ + unsigned ReceiveFlags; + + /* The protocol statistics structure; one per supported protocol. */ + SKCS_PROTO_STATS ProtoStats[SKCS_NUM_PROTOCOLS]; +} SK_CSUM; + +/* + * SKCS_PACKET_INFO - The packet information structure. + */ +typedef struct s_CsPacketInfo { + /* Bit field specifiying the desired/found protocols. */ + unsigned ProtocolFlags; + + /* Length of complete IP header, including any option fields. */ + unsigned IpHeaderLength; + + /* IP header checksum. */ + unsigned IpHeaderChecksum; + + /* TCP/UDP pseudo header checksum. */ + unsigned PseudoHeaderChecksum; +} SKCS_PACKET_INFO; + +/* function prototypes ********************************************************/ + +#ifndef SkCsCalculateChecksum +extern unsigned SkCsCalculateChecksum( + void *pData, + unsigned Length); +#endif + +extern int SkCsEvent( + SK_AC *pAc, + SK_IOC Ioc, + SK_U32 Event, + SK_EVPARA Param); + +extern SKCS_STATUS SkCsGetReceiveInfo( + SK_AC *pAc, + void *pIpHeader, + unsigned Checksum1, + unsigned Checksum2); + +extern void SkCsGetSendInfo( + SK_AC *pAc, + void *pIpHeader, + SKCS_PACKET_INFO *pPacketInfo); + +extern void SkCsSetReceiveFlags( + SK_AC *pAc, + unsigned ReceiveFlags, + unsigned *pChecksum1Offset, + unsigned *pChecksum2Offset); + +#endif /* __INC_SKCSUM_H */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/h/skdebug.h linux/drivers/net/sk98lin/h/skdebug.h --- v2.2.13/linux/drivers/net/sk98lin/h/skdebug.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/h/skdebug.h Tue Jan 4 10:12:17 2000 @@ -0,0 +1,111 @@ +/****************************************************************************** + * + * Name: skdebug.h + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.9 $ + * Date: $Date: 1999/09/14 14:02:43 $ + * Purpose: SK specific DEBUG support + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * $Log: skdebug.h,v $ + * Revision 1.9 1999/09/14 14:02:43 rwahl + * Added SK_DBGMOD_PECP. + * + * Revision 1.8 1998/11/25 08:31:54 gklug + * fix: no C++ comments allowed in common sources + * + * Revision 1.7 1998/11/24 16:47:24 swolf + * Driver may now define its own SK_DBG_MSG() (eg. in "h/skdrv1st.h"). + * + * Revision 1.6 1998/10/28 10:23:55 rassmann + * ADDED SK_DBGMOD_ADDR. + * + * Revision 1.5 1998/10/22 09:43:55 gklug + * add: CSUM module + * + * Revision 1.4 1998/10/01 07:54:44 gklug + * add: PNMI debug module + * + * Revision 1.3 1998/09/18 08:32:34 afischer + * Macros changed according ssr-spec.: + * SK_DBG_MODCHK -> SK_DBG_CHKMOD + * SK_DBG_CATCHK -> SK_DBG_CHKCAT + * + * Revision 1.2 1998/07/03 14:38:25 malthoff + * Add category SK_DBGCAT_FATAL. + * + * Revision 1.1 1998/06/19 13:39:01 malthoff + * created. + * + * + ******************************************************************************/ + +#ifndef __INC_SKDEBUG_H +#define __INC_SKDEBUG_H + +#ifdef DEBUG +#ifndef SK_DBG_MSG +#define SK_DBG_MSG(pAC,comp,cat,arg) \ + if ( ((comp) & SK_DBG_CHKMOD(pAC)) && \ + ((cat) & SK_DBG_CHKCAT(pAC)) ) { \ + SK_DBG_PRINTF arg ; \ + } +#endif +#else +#define SK_DBG_MSG(pAC,comp,lev,arg) +#endif + +/* PLS NOTE: + * ========= + * Due to any restrictions of kernel printf routines do not use other + * format identifiers as: %x %d %c %s . + * Never use any combined format identifiers such as: %lx %ld in your + * printf - argument (arg) because some OS specific kernel printfs may + * only support some basic identifiers. + */ + +/* Debug modules */ + +#define SK_DBGMOD_MERR 0x00000001L /* general module error indication */ +#define SK_DBGMOD_HWM 0x00000002L /* Hardware init module */ +#define SK_DBGMOD_RLMT 0x00000004L /* RLMT module */ +#define SK_DBGMOD_VPD 0x00000008L /* VPD module */ +#define SK_DBGMOD_I2C 0x00000010L /* I2C module */ +#define SK_DBGMOD_PNMI 0x00000020L /* PNMI module */ +#define SK_DBGMOD_CSUM 0x00000040L /* CSUM module */ +#define SK_DBGMOD_ADDR 0x00000080L /* ADDR module */ +#define SK_DBGMOD_PECP 0x00000100L /* PECP module */ + +/* Debug events */ + +#define SK_DBGCAT_INIT 0x00000001L /* module/driver initialization */ +#define SK_DBGCAT_CTRL 0x00000002L /* controlling: add/rmv MCA/MAC + * and other controls (IOCTL) + */ +#define SK_DBGCAT_ERR 0x00000004L /* error handling paths */ +#define SK_DBGCAT_TX 0x00000008L /* transmit path */ +#define SK_DBGCAT_RX 0x00000010L /* receive path */ +#define SK_DBGCAT_IRQ 0x00000020L /* general IRQ handling */ +#define SK_DBGCAT_QUEUE 0x00000040L /* any queue management */ +#define SK_DBGCAT_DUMP 0x00000080L /* large data output e.g. hex dump */ +#define SK_DBGCAT_FATAL 0x00000100L /* large data output e.g. hex dump */ + +#endif /* __INC_SKDEBUG_H */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/h/skdrv1st.h linux/drivers/net/sk98lin/h/skdrv1st.h --- v2.2.13/linux/drivers/net/sk98lin/h/skdrv1st.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/h/skdrv1st.h Tue Jan 4 10:12:17 2000 @@ -0,0 +1,213 @@ +/****************************************************************************** + * + * Name: skdrv1st.h + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.6 $ + * Date: $Date: 1999/07/27 08:03:33 $ + * Purpose: First header file for driver and all other modules + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: skdrv1st.h,v $ + * Revision 1.6 1999/07/27 08:03:33 cgoos + * Changed SK_IN/OUT macros to readX/writeX instead of memory + * accesses (necessary for ALPHA). + * + * Revision 1.5 1999/07/23 12:10:21 cgoos + * Removed SK_RLMT_SLOW_LOOKAHEAD define. + * + * Revision 1.4 1999/07/14 12:31:13 cgoos + * Added SK_RLMT_SLOW_LOOKAHEAD define. + * + * Revision 1.3 1999/04/07 10:12:54 cgoos + * Added check for KERNEL and OPTIMIZATION defines. + * + * Revision 1.2 1999/03/01 08:51:47 cgoos + * Fixed pcibios_read/write definitions. + * + * Revision 1.1 1999/02/16 07:40:49 cgoos + * First version. + * + * + * + ******************************************************************************/ + +/****************************************************************************** + * + * Description: + * + * This is the first include file of the driver, which includes all + * neccessary system header files and some of the GEnesis header files. + * It also defines some basic items. + * + * Include File Hierarchy: + * + * see skge.c + * + ******************************************************************************/ + +#ifndef __INC_SKDRV1ST_H +#define __INC_SKDRV1ST_H + + +typedef struct s_AC SK_AC; + +/* override some default functions with optimized linux functions */ + +#define SK_PNMI_STORE_U16(p,v) memcpy((char*)(p),(char*)&(v),2) +#define SK_PNMI_STORE_U32(p,v) memcpy((char*)(p),(char*)&(v),4) +#define SK_PNMI_STORE_U64(p,v) memcpy((char*)(p),(char*)&(v),8) +#define SK_PNMI_READ_U16(p,v) memcpy((char*)&(v),(char*)(p),2) +#define SK_PNMI_READ_U32(p,v) memcpy((char*)&(v),(char*)(p),2) +#define SK_PNMI_READ_U64(p,v) memcpy((char*)&(v),(char*)(p),2) + +#define SkCsCalculateChecksum(p,l) ((~ip_compute_csum(p, l)) & 0xffff) + +#define SK_ADDR_EQUAL(a1,a2) (!memcmp(a1,a2,6)) + + +#if !defined(__OPTIMIZE__) || !defined(__KERNEL__) +#warning You must compile this file with the correct options! +#warning See the last lines of the source file. +#error You must compile this driver with "-O". +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "h/sktypes.h" +#include "h/skerror.h" +#include "h/skdebug.h" +#include "h/lm80.h" +#include "h/xmac_ii.h" + +#ifdef __LITTLE_ENDIAN +#define SK_LITTLE_ENDIAN +#else +#define SK_BIG_ENDIAN +#endif + + +/* we use gethrtime(), return unit: nanoseconds */ +#define SK_TICKS_PER_SEC HZ + +#define SK_MEM_MAPPED_IO + +// #define SK_RLMT_SLOW_LOOKAHEAD + +#define SK_MAX_MACS 2 + +#define SK_IOC char* + +typedef struct s_DrvRlmtMbuf SK_MBUF; + +#define SK_CONST64 INT64_C +#define SK_CONSTU64 UINT64_C + +#define SK_MEMCPY(dest,src,size) memcpy(dest,src,size) +#define SK_MEMCMP(s1,s2,size) memcmp(s1,s2,size) +#define SK_MEMSET(dest,val,size) memset(dest,val,size) +#define SK_STRLEN(pStr) strlen((char*)pStr) +#define SK_STRNCPY(pDest,pSrc,size) strncpy((char*)pDest,(char*)pSrc,size) +#define SK_STRCMP(pStr1,pStr2) strcmp((char*)pStr1,(char*)pStr2) + +/* macros to access the adapter */ +#define SK_OUT8(b,a,v) writeb(v, (b+a)) +#define SK_OUT16(b,a,v) writew(v, (b+a)) +#define SK_OUT32(b,a,v) writel(v, (b+a)) +#define SK_IN8(b,a,pv) (*(pv) = readb(b+a)) +#define SK_IN16(b,a,pv) (*(pv) = readw(b+a)) +#define SK_IN32(b,a,pv) (*(pv) = readl(b+a)) + +#define int8_t char +#define int16_t short +#define int32_t long +#define int64_t long long +#define uint8_t u_char +#define uint16_t u_short +#define uint32_t u_long +#define uint64_t unsigned long long +#define t_scalar_t int +#define t_uscalar_t unsigned int +#define uintptr_t unsigned long + +#define __CONCAT__(A,B) A##B + +#define INT32_C(a) __CONCAT__(a,L) +#define INT64_C(a) __CONCAT__(a,LL) +#define UINT32_C(a) __CONCAT__(a,UL) +#define UINT64_C(a) __CONCAT__(a,ULL) + +#ifdef DEBUG +#define SK_DBG_PRINTF printk +#ifndef SK_DEBUG_CHKMOD +#define SK_DEBUG_CHKMOD 0 +#endif +#ifndef SK_DEBUG_CHKCAT +#define SK_DEBUG_CHKCAT 0 +#endif +/* those come from the makefile */ +#define SK_DBG_CHKMOD(pAC) (SK_DEBUG_CHKMOD) +#define SK_DBG_CHKCAT(pAC) (SK_DEBUG_CHKCAT) + +extern void SkDbgPrintf(const char *format,...); + +#define SK_DBGMOD_DRV 0x00010000 + +/**** possible driver debug categories ********************************/ +#define SK_DBGCAT_DRV_ENTRY 0x00010000 +#define SK_DBGCAT_DRV_SAP 0x00020000 +#define SK_DBGCAT_DRV_MCA 0x00040000 +#define SK_DBGCAT_DRV_TX_PROGRESS 0x00080000 +#define SK_DBGCAT_DRV_RX_PROGRESS 0x00100000 +#define SK_DBGCAT_DRV_PROGRESS 0x00200000 +#define SK_DBGCAT_DRV_MSG 0x00400000 +#define SK_DBGCAT_DRV_PROM 0x00800000 +#define SK_DBGCAT_DRV_TX_FRAME 0x01000000 +#define SK_DBGCAT_DRV_ERROR 0x02000000 +#define SK_DBGCAT_DRV_INT_SRC 0x04000000 +#define SK_DBGCAT_DRV_EVENT 0x08000000 + +#endif /* DEBUG */ + +#define SK_ERR_LOG SkErrorLog + +extern void SkErrorLog(SK_AC*, int, int, char*); + +#endif /* __INC_SKDRV1ST_H */ + diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/h/skdrv2nd.h linux/drivers/net/sk98lin/h/skdrv2nd.h --- v2.2.13/linux/drivers/net/sk98lin/h/skdrv2nd.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/h/skdrv2nd.h Tue Jan 4 10:12:17 2000 @@ -0,0 +1,445 @@ +/****************************************************************************** + * + * Name: skdrv2nd.h + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.7 $ + * Date: $Date: 1999/09/28 12:38:21 $ + * Purpose: Second header file for driver and all other modules + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: skdrv2nd.h,v $ + * Revision 1.7 1999/09/28 12:38:21 cgoos + * Added CheckQueue to SK_AC. + * + * Revision 1.6 1999/07/27 08:04:05 cgoos + * Added checksumming variables to SK_AC. + * + * Revision 1.5 1999/03/29 12:33:26 cgoos + * Rreversed to fine lock granularity. + * + * Revision 1.4 1999/03/15 12:14:02 cgoos + * Added DriverLock to SK_AC. + * Removed other locks. + * + * Revision 1.3 1999/03/01 08:52:27 cgoos + * Changed pAC->PciDev declaration. + * + * Revision 1.2 1999/02/18 10:57:14 cgoos + * Removed SkDrvTimeStamp prototype. + * Fixed SkGeOsGetTime prototype. + * + * Revision 1.1 1999/02/16 07:41:01 cgoos + * First version. + * + * + * + ******************************************************************************/ + +/****************************************************************************** + * + * Description: + * + * This is the second include file of the driver, which includes all other + * neccessary files and defines all structures and constants used by the + * driver and the common modules. + * + * Include File Hierarchy: + * + * see skge.c + * + ******************************************************************************/ + +#ifndef __INC_SKDRV2ND_H +#define __INC_SKDRV2ND_H + +#include "h/skqueue.h" +#include "h/skgehwt.h" +#include "h/sktimer.h" +#include "h/ski2c.h" +#include "h/skgepnmi.h" +#include "h/skvpd.h" +#include "h/skgehw.h" +#include "h/skgeinit.h" +#include "h/skaddr.h" +#include "h/skgesirq.h" +#include "h/skcsum.h" +#include "h/skrlmt.h" +#include "h/skgedrv.h" + +/* global function prototypes ******************************************/ +extern SK_MBUF *SkDrvAllocRlmtMbuf(SK_AC*, SK_IOC, unsigned); +extern void SkDrvFreeRlmtMbuf(SK_AC*, SK_IOC, SK_MBUF*); +extern SK_U64 SkOsGetTime(SK_AC*); +extern int SkPciReadCfgDWord(SK_AC*, int, SK_U32*); +extern int SkPciReadCfgWord(SK_AC*, int, SK_U16*); +extern int SkPciReadCfgByte(SK_AC*, int, SK_U8*); +extern int SkPciWriteCfgDWord(SK_AC*, int, SK_U32); +extern int SkPciWriteCfgWord(SK_AC*, int, SK_U16); +extern int SkPciWriteCfgByte(SK_AC*, int, SK_U8); +extern int SkDrvEvent(SK_AC*, SK_IOC IoC, SK_U32, SK_EVPARA); + +struct s_DrvRlmtMbuf { + SK_MBUF *pNext; /* Pointer to next RLMT Mbuf. */ + SK_U8 *pData; /* Data buffer (virtually contig.). */ + unsigned Size; /* Data buffer size. */ + unsigned Length; /* Length of packet (<= Size). */ + SK_U32 PortIdx; /* Receiving/transmitting port. */ +#ifdef SK_RLMT_MBUF_PRIVATE + SK_RLMT_MBUF Rlmt; /* Private part for RLMT. */ +#endif /* SK_RLMT_MBUF_PRIVATE */ + struct sk_buff *pOs; /* Pointer to message block */ +}; + + + +/* + * ioctl definitions + */ +#define SK_IOCTL_BASE (SIOCDEVPRIVATE) +#define SK_IOCTL_GETMIB (SK_IOCTL_BASE + 0) +#define SK_IOCTL_SETMIB (SK_IOCTL_BASE + 1) +#define SK_IOCTL_PRESETMIB (SK_IOCTL_BASE + 2) + +typedef struct s_IOCTL SK_GE_IOCTL; + +struct s_IOCTL { + char* pData; + unsigned int Len; +}; + + +/* + * define sizes of descriptor rings in bytes + */ + +#define TX_RING_SIZE (8*1024) +#define RX_RING_SIZE (24*1024) + +/* + * Buffer size for ethernet packets + */ +#define ETH_BUF_SIZE 1540 +#define ETH_MAX_MTU 1514 +#define ETH_MIN_MTU 60 +#define ETH_MULTICAST_BIT 0x01 +#define SK_JUMBO_MTU 9000 + +/* + * transmit priority selects the queue: LOW=asynchron, HIGH=synchron + */ +#define TX_PRIO_LOW 0 +#define TX_PRIO_HIGH 1 + +/* + * alignment of rx/tx descriptors + */ +#define DESCR_ALIGN 8 + +/* + * definitions for pnmi. TODO + */ +#define SK_DRIVER_RESET(pAC, IoC) 0 +#define SK_DRIVER_SENDEVENT(pAC, IoC) 0 +#define SK_DRIVER_SELFTEST(pAC, IoC) 0 + +/* TX and RX descriptors *****************************************************/ + +typedef struct s_RxD RXD; /* the receive descriptor */ + +struct s_RxD { + volatile SK_U32 RBControl; /* Receive Buffer Control */ + SK_U32 VNextRxd; /* Next receive descriptor,low dword */ + SK_U32 VDataLow; /* Receive buffer Addr, low dword */ + SK_U32 VDataHigh; /* Receive buffer Addr, high dword */ + SK_U32 FrameStat; /* Receive Frame Status word */ + SK_U32 TimeStamp; /* Time stamp from XMAX */ + SK_U32 TcpSums; /* TCP Sum 2 / TCP Sum 1 */ + SK_U32 TcpSumStarts; /* TCP Sum Start 2 / TCP Sum Start 1 */ + RXD *pNextRxd; /* Pointer to next Rxd */ + struct sk_buff *pMBuf; /* Pointer to Linux' socket buffer */ +}; + +typedef struct s_TxD TXD; /* the transmit descriptor */ + +struct s_TxD { + volatile SK_U32 TBControl; /* Transmit Buffer Control */ + SK_U32 VNextTxd; /* Next transmit descriptor,low dword */ + SK_U32 VDataLow; /* Transmit Buffer Addr, low dword */ + SK_U32 VDataHigh; /* Transmit Buffer Addr, high dword */ + SK_U32 FrameStat; /* Transmit Frame Status Word */ + SK_U32 TcpSumOfs; /* Reserved / TCP Sum Offset */ + SK_U32 TcpSumStWr; /* TCP Sum Start / TCP Sum Write */ + SK_U32 TcpReserved; /* not used */ + TXD *pNextTxd; /* Pointer to next Txd */ + struct sk_buff *pMBuf; /* Pointer to Linux' socket buffer */ +}; + + +/* definition of flags in descriptor control field */ +#define RX_CTRL_OWN_BMU UINT32_C(0x80000000) +#define RX_CTRL_STF UINT32_C(0x40000000) +#define RX_CTRL_EOF UINT32_C(0x20000000) +#define RX_CTRL_EOB_IRQ UINT32_C(0x10000000) +#define RX_CTRL_EOF_IRQ UINT32_C(0x08000000) +#define RX_CTRL_DEV_NULL UINT32_C(0x04000000) +#define RX_CTRL_STAT_VALID UINT32_C(0x02000000) +#define RX_CTRL_TIME_VALID UINT32_C(0x01000000) +#define RX_CTRL_CHECK_DEFAULT UINT32_C(0x00550000) +#define RX_CTRL_CHECK_CSUM UINT32_C(0x00560000) +#define RX_CTRL_LEN_MASK UINT32_C(0x0000FFFF) + +#define TX_CTRL_OWN_BMU UINT32_C(0x80000000) +#define TX_CTRL_STF UINT32_C(0x40000000) +#define TX_CTRL_EOF UINT32_C(0x20000000) +#define TX_CTRL_EOB_IRQ UINT32_C(0x10000000) +#define TX_CTRL_EOF_IRQ UINT32_C(0x08000000) +#define TX_CTRL_ST_FWD UINT32_C(0x04000000) +#define TX_CTRL_DISAB_CRC UINT32_C(0x02000000) +#define TX_CTRL_SOFTWARE UINT32_C(0x01000000) +#define TX_CTRL_CHECK_DEFAULT UINT32_C(0x00550000) +#define TX_CTRL_CHECK_CSUM UINT32_C(0x00560000) +#define TX_CTRL_LEN_MASK UINT32_C(0x0000FFFF) + + + +/* The offsets of registers in the TX and RX queue control io area ***********/ + +#define RX_Q_BUF_CTRL_CNT 0x00 +#define RX_Q_NEXT_DESCR_LOW 0x04 +#define RX_Q_BUF_ADDR_LOW 0x08 +#define RX_Q_BUF_ADDR_HIGH 0x0c +#define RX_Q_FRAME_STAT 0x10 +#define RX_Q_TIME_STAMP 0x14 +#define RX_Q_CSUM_1_2 0x18 +#define RX_Q_CSUM_START_1_2 0x1c +#define RX_Q_CUR_DESCR_LOW 0x20 +#define RX_Q_DESCR_HIGH 0x24 +#define RX_Q_CUR_ADDR_LOW 0x28 +#define RX_Q_CUR_ADDR_HIGH 0x2c +#define RX_Q_CUR_BYTE_CNT 0x30 +#define RX_Q_CTRL 0x34 +#define RX_Q_FLAG 0x38 +#define RX_Q_TEST1 0x3c +#define RX_Q_TEST2 0x40 +#define RX_Q_TEST3 0x44 + +#define TX_Q_BUF_CTRL_CNT 0x00 +#define TX_Q_NEXT_DESCR_LOW 0x04 +#define TX_Q_BUF_ADDR_LOW 0x08 +#define TX_Q_BUF_ADDR_HIGH 0x0c +#define TX_Q_FRAME_STAT 0x10 +#define TX_Q_CSUM_START 0x14 +#define TX_Q_CSUM_START_POS 0x18 +#define TX_Q_RESERVED 0x1c +#define TX_Q_CUR_DESCR_LOW 0x20 +#define TX_Q_DESCR_HIGH 0x24 +#define TX_Q_CUR_ADDR_LOW 0x28 +#define TX_Q_CUR_ADDR_HIGH 0x2c +#define TX_Q_CUR_BYTE_CNT 0x30 +#define TX_Q_CTRL 0x34 +#define TX_Q_FLAG 0x38 +#define TX_Q_TEST1 0x3c +#define TX_Q_TEST2 0x40 +#define TX_Q_TEST3 0x44 + +/* definition of flags in the queue control field */ +#define RX_Q_CTRL_POLL_ON 0x00000080 +#define RX_Q_CTRL_POLL_OFF 0x00000040 +#define RX_Q_CTRL_STOP 0x00000020 +#define RX_Q_CTRL_START 0x00000010 +#define RX_Q_CTRL_CLR_I_PAR 0x00000008 +#define RX_Q_CTRL_CLR_I_EOB 0x00000004 +#define RX_Q_CTRL_CLR_I_EOF 0x00000002 +#define RX_Q_CTRL_CLR_I_ERR 0x00000001 + +#define TX_Q_CTRL_POLL_ON 0x00000080 +#define TX_Q_CTRL_POLL_OFF 0x00000040 +#define TX_Q_CTRL_STOP 0x00000020 +#define TX_Q_CTRL_START 0x00000010 +#define TX_Q_CTRL_CLR_I_EOB 0x00000004 +#define TX_Q_CTRL_CLR_I_EOF 0x00000002 +#define TX_Q_CTRL_CLR_I_ERR 0x00000001 + + +/* Interrupt bits in the interrupts source register **************************/ +#define IRQ_HW_ERROR 0x80000000 +#define IRQ_RESERVED 0x40000000 +#define IRQ_PKT_TOUT_RX1 0x20000000 +#define IRQ_PKT_TOUT_RX2 0x10000000 +#define IRQ_PKT_TOUT_TX1 0x08000000 +#define IRQ_PKT_TOUT_TX2 0x04000000 +#define IRQ_I2C_READY 0x02000000 +#define IRQ_SW 0x01000000 +#define IRQ_EXTERNAL_REG 0x00800000 +#define IRQ_TIMER 0x00400000 +#define IRQ_MAC1 0x00200000 +#define IRQ_LINK_SYNC_C_M1 0x00100000 +#define IRQ_MAC2 0x00080000 +#define IRQ_LINK_SYNC_C_M2 0x00040000 +#define IRQ_EOB_RX1 0x00020000 +#define IRQ_EOF_RX1 0x00010000 +#define IRQ_CHK_RX1 0x00008000 +#define IRQ_EOB_RX2 0x00004000 +#define IRQ_EOF_RX2 0x00002000 +#define IRQ_CHK_RX2 0x00001000 +#define IRQ_EOB_SY_TX1 0x00000800 +#define IRQ_EOF_SY_TX1 0x00000400 +#define IRQ_CHK_SY_TX1 0x00000200 +#define IRQ_EOB_AS_TX1 0x00000100 +#define IRQ_EOF_AS_TX1 0x00000080 +#define IRQ_CHK_AS_TX1 0x00000040 +#define IRQ_EOB_SY_TX2 0x00000020 +#define IRQ_EOF_SY_TX2 0x00000010 +#define IRQ_CHK_SY_TX2 0x00000008 +#define IRQ_EOB_AS_TX2 0x00000004 +#define IRQ_EOF_AS_TX2 0x00000002 +#define IRQ_CHK_AS_TX2 0x00000001 + +#define DRIVER_IRQS (IRQ_SW | IRQ_EOF_RX1 | IRQ_EOF_RX2 | \ + IRQ_EOF_SY_TX1 | IRQ_EOF_AS_TX1 | \ + IRQ_EOF_SY_TX2 | IRQ_EOF_AS_TX2) + +#define SPECIAL_IRQS (IRQ_HW_ERROR | IRQ_PKT_TOUT_RX1 | IRQ_PKT_TOUT_RX2 | \ + IRQ_PKT_TOUT_TX1 | IRQ_PKT_TOUT_TX2 | \ + IRQ_I2C_READY | IRQ_EXTERNAL_REG | IRQ_TIMER | \ + IRQ_MAC1 | IRQ_LINK_SYNC_C_M1 | \ + IRQ_MAC2 | IRQ_LINK_SYNC_C_M2 | \ + IRQ_CHK_RX1 | IRQ_CHK_RX2 | \ + IRQ_CHK_SY_TX1 | IRQ_CHK_AS_TX1 | \ + IRQ_CHK_SY_TX2 | IRQ_CHK_AS_TX2) + +#define IRQ_MASK (IRQ_SW | IRQ_EOB_RX1 | IRQ_EOF_RX1 | \ + IRQ_EOB_RX2 | IRQ_EOF_RX2 | \ + IRQ_EOB_SY_TX1 | IRQ_EOF_SY_TX1 | \ + IRQ_EOB_AS_TX1 | IRQ_EOF_AS_TX1 | \ + IRQ_EOB_SY_TX2 | IRQ_EOF_SY_TX2 | \ + IRQ_EOB_AS_TX2 | IRQ_EOF_AS_TX2 | \ + IRQ_HW_ERROR | IRQ_PKT_TOUT_RX1 | IRQ_PKT_TOUT_RX2 | \ + IRQ_PKT_TOUT_TX1 | IRQ_PKT_TOUT_TX2 | \ + IRQ_I2C_READY | IRQ_EXTERNAL_REG | IRQ_TIMER | \ + IRQ_MAC1 | \ + IRQ_MAC2 | \ + IRQ_CHK_RX1 | IRQ_CHK_RX2 | \ + IRQ_CHK_SY_TX1 | IRQ_CHK_AS_TX1 | \ + IRQ_CHK_SY_TX2 | IRQ_CHK_AS_TX2) + +#define IRQ_HWE_MASK 0x00000FFF /* enable all HW irqs */ + +typedef struct s_TxPort TX_PORT; + +struct s_TxPort { + /* the transmit descriptor rings */ + caddr_t pTxDescrRing; /* descriptor area memory */ + SK_U64 VTxDescrRing; /* descr. area bus virt. addr. */ + TXD *pTxdRingHead; /* Head of Tx rings */ + TXD *pTxdRingTail; /* Tail of Tx rings */ + TXD *pTxdRingPrev; /* descriptor sent previously */ + int TxdRingFree; /* # of free entrys */ + spinlock_t TxDesRingLock; /* serialize descriptor accesses */ + caddr_t HwAddr; /* bmu registers address */ + int PortIndex; /* index number of port (0 or 1) */ +}; + +typedef struct s_RxPort RX_PORT; + +struct s_RxPort { + /* the receive descriptor rings */ + caddr_t pRxDescrRing; /* descriptor area memory */ + SK_U64 VRxDescrRing; /* descr. area bus virt. addr. */ + RXD *pRxdRingHead; /* Head of Rx rings */ + RXD *pRxdRingTail; /* Tail of Rx rings */ + RXD *pRxdRingPrev; /* descriptor given to BMU previously */ + int RxdRingFree; /* # of free entrys */ + spinlock_t RxDesRingLock; /* serialize descriptor accesses */ + int RxFillLimit; /* limit for buffers in ring */ + caddr_t HwAddr; /* bmu registers address */ + int PortIndex; /* index number of port (0 or 1) */ +}; + +typedef struct s_PerStrm PER_STRM; + +#define SK_ALLOC_IRQ 0x00000001 + +/**************************************************************************** + * Per board structure / Adapter Context structure: + * Allocated within attach(9e) and freed within detach(9e). + * Contains all 'per device' necessary handles, flags, locks etc.: + */ +struct s_AC { + SK_GEINIT GIni; /* GE init struct */ + SK_PNMI Pnmi; /* PNMI data struct */ + SK_VPD vpd; /* vpd data struct */ + SK_QUEUE Event; /* Event queue */ + SK_HWT Hwt; /* Hardware Timer control struct */ + SK_TIMCTRL Tim; /* Software Timer control struct */ + SK_I2C I2c; /* I2C relevant data structure */ + SK_ADDR Addr; /* for Address module */ + SK_CSUM Csum; /* for checksum module */ + SK_RLMT Rlmt; /* for rlmt module */ + spinlock_t SlowPathLock; /* Normal IRQ lock */ + SK_PNMI_STRUCT_DATA PnmiStruct; /* structure to get all Pnmi-Data */ + int RlmtMode; /* link check mode to set */ + + SK_IOC IoBase; /* register set of adapter */ + int BoardLevel; /* level of active hw init (0-2) */ + char DeviceStr[80]; /* adapter string from vpd */ + SK_U32 AllocFlag; /* flag allocation of resources */ + struct pci_dev PciDev; /* for access to pci config space */ + SK_U32 PciDevId; /* pci device id */ + struct device *dev; /* pointer to device struct */ + char Name[30]; /* driver name */ + struct device *Next; /* link all devices (for clearing) */ + int RxBufSize; /* length of receive buffers */ + struct net_device_stats stats; /* linux 'netstat -i' statistics */ + int Index; /* internal board index number */ + + /* adapter RAM sizes for queues of active port */ + int RxQueueSize; /* memory used for receive queue */ + int TxSQueueSize; /* memory used for sync. tx queue */ + int TxAQueueSize; /* memory used for async. tx queue */ + + int PromiscCount; /* promiscuous mode counter */ + int AllMultiCount; /* allmulticast mode counter */ + int MulticCount; /* number of different MC */ + /* addresses for this board */ + /* (may be more than HW can)*/ + + int ActivePort; /* the active XMAC port */ + int TxDescrPerRing; /* # of descriptors per tx ring */ + int RxDescrPerRing; /* # of descriptors per rx ring */ + + + caddr_t pDescrMem; /* Pointer to the descriptor area */ + /* the port structures with descriptor rings */ + TX_PORT TxPort[SK_MAX_MACS][2]; + RX_PORT RxPort[SK_MAX_MACS]; + + unsigned int CsOfs1; /* for checksum calculation */ + unsigned int CsOfs2; /* for checksum calculation */ + SK_U32 CsOfs; /* for checksum calculation */ + + SK_BOOL CheckQueue; /* check event queue soon */ +}; + + +#endif /* __INC_SKDRV2ND_H */ + diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/h/skerror.h linux/drivers/net/sk98lin/h/skerror.h --- v2.2.13/linux/drivers/net/sk98lin/h/skerror.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/h/skerror.h Tue Jan 4 10:12:17 2000 @@ -0,0 +1,75 @@ +/****************************************************************************** + * + * Name: skerror.h + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.3 $ + * Date: $Date: 1999/09/14 14:04:42 $ + * Purpose: SK specific Error log support + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * $Log: skerror.h,v $ + * Revision 1.3 1999/09/14 14:04:42 rwahl + * Added error base SK_ERRBASE_PECP. + * Changed error base for driver. + * + * Revision 1.2 1998/08/11 11:15:41 gklug + * chg: comments + * + * Revision 1.1 1998/08/11 11:09:38 gklug + * add: error bases + * add: error Classes + * first version + * + * + * + ******************************************************************************/ + +#ifndef _INC_SKERROR_H_ +#define _INC_SKERROR_H_ + +/* + * Define the Error Classes + */ +#define SK_ERRCL_OTHER (0) /* Other error */ +#define SK_ERRCL_CONFIG (1L<<0) /* Configuration error */ +#define SK_ERRCL_INIT (1L<<1) /* Initialization error */ +#define SK_ERRCL_NORES (1L<<2) /* Out of resources error */ +#define SK_ERRCL_SW (1L<<3) /* internal Software error */ +#define SK_ERRCL_HW (1L<<4) /* Hardware failure */ +#define SK_ERRCL_COMM (1L<<5) /* Communication error */ + + +/* + * Define Error code bases + */ +#define SK_ERRBASE_RLMT 100 /* Base Error number for RLMT */ +#define SK_ERRBASE_HWINIT 200 /* Base Error number for HWInit */ +#define SK_ERRBASE_VPD 300 /* Base Error number for VPD */ +#define SK_ERRBASE_PNMI 400 /* Base Error number for PNMI */ +#define SK_ERRBASE_CSUM 500 /* Base Error number for Checksum */ +#define SK_ERRBASE_SIRQ 600 /* Base Error number for Special IRQ */ +#define SK_ERRBASE_I2C 700 /* Base Error number for i2C module */ +#define SK_ERRBASE_QUEUE 800 /* Base Error number for Scheduler */ +#define SK_ERRBASE_ADDR 900 /* Base Error number for Address mod. */ +#define SK_ERRBASE_PECP 1000 /* Base Error number for PECP */ +#define SK_ERRBASE_DRV 1100 /* Base Error number for Driver */ + +#endif /* _INC_SKERROR_H_ */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/h/skgedrv.h linux/drivers/net/sk98lin/h/skgedrv.h --- v2.2.13/linux/drivers/net/sk98lin/h/skgedrv.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/h/skgedrv.h Tue Jan 4 10:12:17 2000 @@ -0,0 +1,62 @@ +/****************************************************************************** + * + * Name: skgedrv.h + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.3 $ + * Date: $Date: 1998/12/01 13:31:39 $ + * Purpose: Interface with the driver + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: skgedrv.h,v $ + * Revision 1.3 1998/12/01 13:31:39 cgoos + * SWITCH INTERN Event added. + * + * Revision 1.2 1998/11/25 08:28:38 gklug + * rmv: PORT SWITCH Event + * + * Revision 1.1 1998/09/29 06:14:07 gklug + * add: driver events (initial version) + * + * + ******************************************************************************/ + +#ifndef __INC_SKGEDRV_H_ +#define __INC_SKGEDRV_H_ + +/* defines ********************************************************************/ + +/* + * Define the driver events. + * Usually the events are defined by the destination module. In case of the + * driver we put the definition of the events here. + */ +#define SK_DRV_PORT_RESET 1 /* The port needs to be reset */ +#define SK_DRV_NET_UP 2 /* The net is now operational */ +#define SK_DRV_NET_DOWN 3 /* The net is now down */ +#define SK_DRV_SWITCH_SOFT 4 /* Ports switch with both links conn */ +#define SK_DRV_SWITCH_HARD 5 /* Port switch due to link failure */ +#define SK_DRV_RLMT_SEND 6 /* Send a RLMT packet */ +#define SK_DRV_ADAP_FAIL 7 /* The whole adapter fails */ +#define SK_DRV_PORT_FAIL 8 /* One port fails */ +#define SK_DRV_SWITCH_INTERN 9 /* Port switch from driver to itself */ + +#endif /* __INC_SKGEDRV_H_ */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/h/skgehw.h linux/drivers/net/sk98lin/h/skgehw.h --- v2.2.13/linux/drivers/net/sk98lin/h/skgehw.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/h/skgehw.h Tue Jan 4 10:12:17 2000 @@ -0,0 +1,1715 @@ +/****************************************************************************** + * + * Name: skgehw.h + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.33 $ + * Date: $Date: 1999/08/27 11:17:10 $ + * Purpose: Defines and Macros for the Gigabit Ethernet Adapter Product + * Family + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * $Log: skgehw.h,v $ + * Revision 1.33 1999/08/27 11:17:10 malthoff + * It's more savely to put bracket around marco parameters. + * Brackets added for PHY_READ and PHY_WRITE. + * + * Revision 1.32 1999/05/19 07:31:01 cgoos + * Changes for 1000Base-T. + * Added HWAC_LINK_LED macro. + * + * Revision 1.31 1999/03/12 13:27:40 malthoff + * Remove __STDC__. + * + * Revision 1.30 1999/02/09 09:28:20 malthoff + * Add PCI_ERRBITS. + * + * Revision 1.29 1999/01/26 08:55:48 malthoff + * Bugfix: The 16 bit field releations inside the descriptor are + * endianess dependend if the descriptor reversal feature + * (PCI_REV_DESC bit in PCI_OUR_REG_2) is enabled. + * Drivers which use this feature has to set the define + * SK_USE_REV_DESC. + * + * Revision 1.28 1998/12/10 11:10:22 malthoff + * bug fix: IS_IRQ_STAT and IS_IRQ_MST_ERR has been twisted. + * + * Revision 1.27 1998/11/13 14:19:21 malthoff + * Bug Fix: The bit definition of B3_PA_CTRL has completely + * changed from HW Spec v1.3 to v1.5. + * + * Revision 1.26 1998/11/04 08:31:48 cgoos + * Fixed byte ordering in XM_OUTADDR/XM_OUTHASH macros. + * + * Revision 1.25 1998/11/04 07:16:25 cgoos + * Changed byte ordering in XM_INADDR/XM_INHASH again. + * + * Revision 1.24 1998/11/02 11:08:43 malthoff + * RxCtrl and TxCtrl must be volatile. + * + * Revision 1.23 1998/10/28 13:50:45 malthoff + * Fix: Endian support missing in XM_IN/OUT-ADDR/HASH macros. + * + * Revision 1.22 1998/10/26 08:01:36 malthoff + * RX_MFF_CTRL1 is split up into RX_MFF_CTRL1, + * RX_MFF_STAT_TO, and RX_MFF_TIST_TO. + * TX_MFF_CTRL1 is split up TX_MFF_CTRL1 and TX_MFF_WAF. + * + * Revision 1.21 1998/10/20 07:43:10 malthoff + * Fix: XM_IN/OUT/ADDR/HASH macros: + * The pointer must be casted. + * + * Revision 1.20 1998/10/19 15:53:59 malthoff + * Remove ML proto definitions. + * + * Revision 1.19 1998/10/16 14:40:17 gklug + * fix: typo B0_XM_IMSK regs + * + * Revision 1.18 1998/10/16 09:46:54 malthoff + * Remove temp defines for ML diag prototyp. + * Fix register definition for B0_XM1_PHY_DATA, B0_XM1_PHY_DATA + * B0_XM2_PHY_DATA, B0_XM2_PHY_ADDR, B0_XA1_CSR, B0_XS1_CSR, + * B0_XS2_CSR, and B0_XA2_CSR. + * + * Revision 1.17 1998/10/14 06:03:14 cgoos + * Changed shifted constant to ULONG. + * + * Revision 1.16 1998/10/09 07:05:41 malthoff + * Rename ALL_PA_ENA_TO to PA_ENA_TO_ALL. + * + * Revision 1.15 1998/10/05 07:54:23 malthoff + * Split up RB_CTRL and it's bit definition into + * RB_CTRL, RB_TST1, and RB_TST2. + * Rename RB_RX_HTPP to RB_RX_LTPP. + * Add ALL_PA_ENA_TO. Modify F_WATER_MARK + * according to HW Spec. v1.5. + * Add MFF_TX_CTRL_DEF. + * + * Revision 1.14 1998/09/28 13:31:16 malthoff + * bug fix: B2_MAC_3 is 0x110 not 0x114 + * + * Revision 1.13 1998/09/24 14:42:56 malthoff + * Split the RX_MFF_TST into RX_MFF_CTRL2, + * RX_MFF_TST1, and RX_MFF_TST2. + * Rename RX_MFF_CTRL to RX_MFF_CTRL1. + * Add BMU bit CSR_SV_IDLE. + * Add macros PHY_READ() and PHY_WRITE(). + * Rename macro SK_ADDR() to SK_HW_ADDR() + * because of conflicts with the Address Module. + * + * Revision 1.12 1998/09/16 07:25:33 malthoff + * Change the parameter order in the XM_INxx and XM_OUTxx macros, + * to have the IoC as first parameter. + * + * Revision 1.11 1998/09/03 09:58:41 malthoff + * Rework the XM_xxx macros. Use {} instead of () to + * be compatible with SK_xxx macros which are defined + * with {}. + * + * Revision 1.10 1998/09/02 11:16:39 malthoff + * Temporary modify B2_I2C_SW to make tests with + * the GE/ML prototyp. + * + * Revision 1.9 1998/08/19 09:11:49 gklug + * fix: struct are removed from c-source (see CCC) + * add: typedefs for all structs + * + * Revision 1.8 1998/08/18 08:27:27 malthoff + * Add some temporary workarounds to test GE + * sources with the ML. + * + * Revision 1.7 1998/07/03 14:42:26 malthoff + * bug fix: Correct macro XMA(). + * Add temporary workaround to access the PCI config space over IO + * + * Revision 1.6 1998/06/23 11:30:36 malthoff + * Remove ';' with ',' in macors. + * + * Revision 1.5 1998/06/22 14:20:57 malthoff + * Add macro SK_ADDR(Base,Addr). + * + * Revision 1.4 1998/06/19 13:35:43 malthoff + * change 'pGec' with 'pAC' + * + * Revision 1.3 1998/06/17 14:58:16 cvs + * Lost keywords reinserted. + * + * Revision 1.1 1998/06/17 14:16:36 cvs + * created + * + * + ******************************************************************************/ + +#ifndef __INC_SKGEHW_H +#define __INC_SKGEHW_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* defines ********************************************************************/ + +/* + * Configuration Space header + * Since this module is used for different OS', those may be + * duplicate on some of them (e.g. Linux). But to keep the + * common source, we have to live with this... + */ +#define PCI_VENDOR_ID 0x00 /* 16 bit Vendor ID */ +#define PCI_DEVICE_ID 0x02 /* 16 bit Device ID */ +#define PCI_COMMAND 0x04 /* 16 bit Command */ +#define PCI_STATUS 0x06 /* 16 bit Status */ +#define PCI_REV_ID 0x08 /* 8 bit Revision ID */ +#define PCI_CLASS_CODE 0x09 /* 24 bit Class Code */ +#define PCI_CACHE_LSZ 0x0c /* 8 bit Cache Line Size */ +#define PCI_LAT_TIM 0x0d /* 8 bit Latency Timer */ +#define PCI_HEADER_T 0x0e /* 8 bit Header Type */ +#define PCI_BIST 0x0f /* 8 bit Built-in selftest */ +#define PCI_BASE_1ST 0x10 /* 32 bit 1st Base address */ +#define PCI_BASE_2ND 0x14 /* 32 bit 2nd Base address */ + /* Byte 18..2b: reserved */ +#define PCI_SUB_VID 0x2c /* 16 bit Subsystem Vendor ID */ +#define PCI_SUB_ID 0x2e /* 16 bit Subsystem ID */ +#define PCI_BASE_ROM 0x30 /* 32 bit Expansion ROM Base Address */ + /* Byte 34..33: reserved */ +#define PCI_CAP_PTR 0x34 /* 8 bit Capabilities Ptr */ + /* Byte 35..3b: reserved */ +#define PCI_IRQ_LINE 0x3c /* 8 bit Interrupt Line */ +#define PCI_IRQ_PIN 0x3d /* 8 bit Interrupt Pin */ +#define PCI_MIN_GNT 0x3e /* 8 bit Min_Gnt */ +#define PCI_MAX_LAT 0x3f /* 8 bit Max_Lat */ + /* Device Dependent Region */ +#define PCI_OUR_REG_1 0x40 /* 32 bit Our Register 1 */ +#define PCI_OUR_REG_2 0x44 /* 32 bit Our Register 2 */ + /* Power Management Region */ +#define PCI_PM_CAP_ID 0x48 /* 8 bit Power Management Cap. ID */ +#define PCI_PM_NITEM 0x49 /* 8 bit Next Item Ptr */ +#define PCI_PM_CAP_REG 0x4a /* 16 bit Power Management Capabilities */ +#define PCI_PM_CTL_STS 0x4c /* 16 bit Power Manag. Control/Status */ + /* Byte 0x4e: reserved */ +#define PCI_PM_DAT_REG 0x4f /* 8 bit Power Manag. Data Register */ + /* VPD Region */ +#define PCI_VPD_CAP_ID 0x50 /* 8 bit VPD Cap. ID */ +#define PCI_VPD_NITEM 0x51 /* 8 bit Next Item Ptr */ +#define PCI_VPD_ADR_REG 0x52 /* 16 bit VPD Address Register */ +#define PCI_VPD_DAT_REG 0x54 /* 32 bit VPD Data Register */ + /* Byte 58..ff: reserved */ + +/* + * I2C Address (PCI Config) + * + * Note: The temperature and voltage sensors are relocated on a different + * I2C bus. + */ +#define I2C_ADDR_VPD 0xA0 /* I2C address for the VPD EEPROM */ + +/* + * Define Bits and Values of the registers + */ +/* PCI_VENDOR_ID 16 bit Vendor ID */ +/* PCI_DEVICE_ID 16 bit Device ID */ +/* Values for Vendor ID and Device ID shall be patched into the code */ +/* PCI_COMMAND 16 bit Command */ + /* Bit 15..10: reserved */ +#define PCI_FBTEN (1<<9) /* Bit 9: Fast Back-To-Back enable */ +#define PCI_SERREN (1<<8) /* Bit 8: SERR enable */ +#define PCI_ADSTEP (1<<7) /* Bit 7: Address Stepping */ +#define PCI_PERREN (1<<6) /* Bit 6: Parity Report Response enable */ +#define PCI_VGA_SNOOP (1<<5) /* Bit 5: VGA palette snoop */ +#define PCI_MWIEN (1<<4) /* Bit 4: Memory write an inv cycl ena */ +#define PCI_SCYCEN (1<<3) /* Bit 3: Special Cycle enable */ +#define PCI_BMEN (1<<2) /* Bit 2: Bus Master enable */ +#define PCI_MEMEN (1<<1) /* Bit 1: Memory Space Access enable */ +#define PCI_IOEN (1<<0) /* Bit 0: IO Space Access enable */ + +/* PCI_STATUS 16 bit Status */ +#define PCI_PERR (1<<15) /* Bit 15: Parity Error */ +#define PCI_SERR (1<<14) /* Bit 14: Signaled SERR */ +#define PCI_RMABORT (1<<13) /* Bit 13: Received Master Abort */ +#define PCI_RTABORT (1<<12) /* Bit 12: Received Target Abort */ + /* Bit 11: reserved */ +#define PCI_DEVSEL (3<<9) /* Bit 10..9: DEVSEL Timing */ +#define PCI_DEV_FAST (0<<9) /* fast */ +#define PCI_DEV_MEDIUM (1<<9) /* medium */ +#define PCI_DEV_SLOW (2<<9) /* slow */ +#define PCI_DATAPERR (1<<8) /* Bit 8: DATA Parity error detected */ +#define PCI_FB2BCAP (1<<7) /* Bit 7: Fast Back-to-Back Capability */ +#define PCI_UDF (1<<6) /* Bit 6: User Defined Features */ +#define PCI_66MHZCAP (1<<5) /* Bit 5: 66 MHz PCI bus clock capable */ +#define PCI_NEWCAP (1<<4) /* Bit 4: New cap. list implemented */ + /* Bit 3..0: reserved */ + +#define PCI_ERRBITS (PCI_PERR | PCI_SERR | PCI_RMABORT | PCI_RTABORT |\ + PCI_DATAPERR) + +/* PCI_CLASS_CODE 24 bit Class Code */ +/* Byte 2: Base Class (02) */ +/* Byte 1: SubClass (00) */ +/* Byte 0: Programming Interface (00) */ + +/* PCI_CACHE_LSZ 8 bit Cache Line Size */ +/* Possible values: 0,2,4,8,16,32,64,128 */ + +/* PCI_HEADER_T 8 bit Header Type */ +#define PCI_HD_MF_DEV (1<<7) /* Bit 7: 0= single, 1= multi-func dev */ +#define PCI_HD_TYPE 0x7f /* Bit 6..0: Header Layout 0= normal */ + +/* PCI_BIST 8 bit Built-in selftest */ +/* Built-in Self test not supported (optional) */ + +/* PCI_BASE_1ST 32 bit 1st Base address */ +#define PCI_MEMSIZE 0x4000L /* use 16 kB Memory Base */ +#define PCI_MEMBASE_MSK 0xffffc000L /* Bit 31..14: Memory Base Address */ +#define PCI_MEMSIZE_MSK 0x00003ff0L /* Bit 13.. 4: Memory Size Req. */ +#define PCI_PREFEN (1L<<3) /* Bit 3: Prefetchable */ +#define PCI_MEM_TYP (3L<<2) /* Bit 2.. 1: Memory Type */ +#define PCI_MEM32BIT (0L<<1) /* Base addr anywhere in 32 Bit range */ +#define PCI_MEM1M (1L<<1) /* Base addr below 1 MegaByte */ +#define PCI_MEM64BIT (2L<<1) /* Base addr anywhere in 64 Bit range */ +#define PCI_MEMSPACE (1L<<0) /* Bit 0: Memory Space Indic. */ + +/* PCI_BASE_2ND 32 bit 2nd Base address */ +#define PCI_IOBASE 0xffffff00L /* Bit 31..8: I/O Base address */ +#define PCI_IOSIZE 0x000000fcL /* Bit 7..2: I/O Size Requirements */ + /* Bit 1: reserved */ +#define PCI_IOSPACE (1L<<0) /* Bit 0: I/O Space Indicator */ + +/* PCI_BASE_ROM 32 bit Expansion ROM Base Address */ +#define PCI_ROMBASE (0xfffeL<<17) /* Bit 31..17: ROM BASE address (1st)*/ +#define PCI_ROMBASZ (0x1cL<<14) /* Bit 16..14: Treat as BASE or SIZE */ +#define PCI_ROMSIZE (0x38L<<11) /* Bit 13..11: ROM Size Requirements */ + /* Bit 10.. 1: reserved */ +#define PCI_ROMEN (0x1L<<0) /* Bit 0: Address Decode enable */ + +/* Device Dependent Region */ +/* PCI_OUR_REG_1 32 bit Our Register 1 */ + /* Bit 31..26: reserved */ +#define PCI_VIO (1L<<25) /* Bit 25: PCI IO Voltage, */ + /* 0 = 3.3V / 1 = 5V */ +#define PCI_EN_BOOT (1L<<24) /* Bit 24: Enable BOOT via ROM */ + /* 1 = Don't boot wth ROM*/ + /* 0 = Boot with ROM */ +#define PCI_EN_IO (1L<<23) /* Bit 23: Mapping to IO space */ +#define PCI_EN_FPROM (1L<<22) /* Bit 22: FLASH mapped to mem? */ + /* 1 = Map Flash to Mem */ + /* 0 = Disable addr. dec*/ +#define PCI_PAGESIZE (3L<<20) /* Bit 21..20: FLASH Page Size */ +#define PCI_PAGE_16 (0L<<20) /* 16 k pages */ +#define PCI_PAGE_32K (1L<<20) /* 32 k pages */ +#define PCI_PAGE_64K (2L<<20) /* 64 k pages */ +#define PCI_PAGE_128K (3L<<20) /* 128 k pages */ + /* Bit 19: reserved */ +#define PCI_PAGEREG (7L<<16) /* Bit 18..16: Page Register */ +#define PCI_NOTAR (1L<<15) /* Bit 15: No turnaround cycle */ +#define PCI_FORCE_BE (1L<<14) /* Bit 14: Assert all BEs on MR */ +#define PCI_DIS_MRL (1L<<13) /* Bit 13: Disable Mem R Line */ +#define PCI_DIS_MRM (1L<<12) /* Bit 12: Disable Mem R multip */ +#define PCI_DIS_MWI (1L<<11) /* Bit 11: Disable Mem W & inv */ +#define PCI_DISC_CLS (1L<<10) /* Bit 10: Disc: cacheLsz bound */ +#define PCI_BURST_DIS (1L<<9) /* Bit 9: Burst Disable */ +#define PCI_DIS_PCI_CLK (1L<<8) /* Bit 8: Disable PCI clock driv*/ +#define PCI_SKEW_DAS (0xfL<<4) /* Bit 7..4: Skew Ctrl, DAS Ext */ +#define PCI_SKEW_BASE (0xfL<<0) /* Bit 3..0: Skew Ctrl, Base */ + + +/* PCI_OUR_REG_2 32 bit Our Register 2 */ +#define PCI_VPD_WR_THR (0xffL<<24) /* Bit 31..24: VPD Write Threshold */ +#define PCI_DEV_SEL (0x7fL<<17) /* Bit 23..17: EEPROM Device Select */ +#define PCI_VPD_ROM_SZ (7L<<14) /* Bit 16..14: VPD ROM Size */ + /* Bit 13..12: reserved */ +#define PCI_PATCH_DIR (0xfL<<8) /* Bit 11.. 8: Ext Patchs dir 3..0 */ +#define PCI_PATCH_DIR_0 (1L<<8) +#define PCI_PATCH_DIR_1 (1L<<9) +#define PCI_PATCH_DIR_2 (1L<<10) +#define PCI_PATCH_DIR_3 (1L<<11) +#define PCI_EXT_PATCHS (0xfL<<4) /* Bit 7..4: Extended Patches 3..0 */ +#define PCI_EXT_PATCH_0 (1L<<4) +#define PCI_EXT_PATCH_1 (1L<<5) +#define PCI_EXT_PATCH_2 (1L<<6) +#define PCI_EXT_PATCH_3 (1L<<7) +#define PCI_EN_DUMMY_RD (1L<<3) /* Bit 3: Enable Dummy Read */ +#define PCI_REV_DESC (1L<<2) /* Bit 2: Reverse Desc. Bytes */ + /* Bit 1: reserved */ +#define PCI_USEDATA64 (1L<<0) /* Bit 0: Use 64Bit Data bus ext*/ + + +/* Power Management Region */ +/* PCI_PM_CAP_REG 16 bit Power Management Capabilities */ +#define PCI_PME_SUP (0x1f<<11) /* Bit 15..11: PM Manag. Event Sup */ +#define PCI_PM_D2_SUB (1<<10) /* Bit 10: D2 Support Bit */ +#define PCI_PM_D1_SUB (1<<9) /* Bit 9: D1 Support Bit */ + /* Bit 8..6: reserved */ +#define PCI_PM_DSI (1<<5) /* Bit 5: Device Specific Init.*/ +#define PCI_PM_APS (1<<4) /* Bit 4: Auxialiary Power Src */ +#define PCI_PME_CLOCK (1<<3) /* Bit 3: PM Event Clock */ +#define PCI_PM_VER (7<<0) /* Bit 2..0: PM PCI Spec. version */ + +/* PCI_PM_CTL_STS 16 bit Power Manag. Control/Status */ +#define PCI_PME_STATUS (1<<15) /* Bit 15: PGA doesn't sup. PME# */ +#define PCI_PM_DAT_SCL (3<<13) /* Bit 14..13: dat reg Scaling factor*/ +#define PCI_PM_DAT_SEL (0xf<<9) /* Bit 12.. 9: PM data selector field*/ +#define PCI_PME_EN (1<<8) /* Bit 8: PGA doesn't sup. PME# */ + /* Bit 7.. 2: reserved */ +#define PCI_PM_STATE (3<<0) /* Bit 1.. 0: Power Management State*/ +#define PCI_PM_STATE_D0 (0<<0) /* D0: Operational (default) */ +#define PCI_PM_STATE_D1 (1<<0) /* D1: not supported */ +#define PCI_PM_STATE_D2 (2<<0) /* D2: not supported */ +#define PCI_PM_STATE_D3 (3<<0) /* D3: HOT, Power Down and Reset */ + +/* VPD Region */ +/* PCI_VPD_ADR_REG 16 bit VPD Address Register */ +#define PCI_VPD_FLAG (1L<<15) /* Bit 15: starts VPD rd/wd cycle*/ +#define PCI_VPD_ADDR (0x3fffL<<0) /* Bit 14..0: VPD address */ + +/* + * Control Register File: + * Bank 0 + */ +#define B0_RAP 0x0000 /* 8 bit Register Address Port */ + /* 0x0001 - 0x0003: reserved */ +#define B0_CTST 0x0004 /* 16 bit Control/Status register */ +#define B0_LED 0x0006 /* 8 Bit LED register */ + /* 0x0007: reserved */ +#define B0_ISRC 0x0008 /* 32 bit Interrupt Source Register */ +#define B0_IMSK 0x000c /* 32 bit Interrupt Mask Register */ +#define B0_HWE_ISRC 0x0010 /* 32 bit HW Error Interrupt Src Reg */ +#define B0_HWE_IMSK 0x0014 /* 32 bit HW Error Interrupt Mask Reg */ +#define B0_SP_ISRC 0x0018 /* 32 bit Special Interrupt Source Reg */ + /* 0x001c: reserved */ + +/* B0 XMAC 1 registers */ +#define B0_XM1_IMSK 0x0020 /* 16 bit r/w XMAC 1 Interrupt Mask Register*/ + /* 0x0022 - 0x0027 reserved */ +#define B0_XM1_ISRC 0x0028 /* 16 bit ro XMAC 1 Interrupt Status Reg */ + /* 0x002a - 0x002f reserved */ +#define B0_XM1_PHY_ADDR 0x0030 /* 16 bit r/w XMAC 1 PHY Address Register */ + /* 0x0032 - 0x0033 reserved */ +#define B0_XM1_PHY_DATA 0x0034 /* 16 bit r/w XMAC 1 PHY Data Register */ + /* 0x0036 - 0x003f reserved */ + +/* B0 XMAC 2 registers */ +#define B0_XM2_IMSK 0x0040 /* 16 bit r/w XMAC 2 Interrupt Mask Register*/ + /* 0x0042 - 0x0047 reserved */ +#define B0_XM2_ISRC 0x0048 /* 16 bit ro XMAC 2 Interrupt Status Reg */ + /* 0x004a - 0x004f reserved */ +#define B0_XM2_PHY_ADDR 0x0050 /* 16 bit r/w XMAC 2 PHY Address Register */ + /* 0x0052 - 0x0053 reserved */ +#define B0_XM2_PHY_DATA 0x0054 /* 16 bit r/w XMAC 2 PHY Data Register */ + /* 0x0056 - 0x005f reserved */ + +/* BMU Control Status Registers */ +#define B0_R1_CSR 0x0060 /* 32 bit BMU Ctrl/Stat Rx Queue 1 */ +#define B0_R2_CSR 0x0064 /* 32 bit BMU Ctrl/Stat Rx Queue 2 */ +#define B0_XS1_CSR 0x0068 /* 32 bit BMU Ctrl/Stat Sync Tx Queue 1 */ +#define B0_XA1_CSR 0x006c /* 32 bit BMU Ctrl/Stat Async Tx Queue 1*/ +#define B0_XS2_CSR 0x0070 /* 32 bit BMU Ctrl/Stat Sync Tx Queue 2 */ +#define B0_XA2_CSR 0x0074 /* 32 bit BMU Ctrl/Stat Async Tx Queue 2*/ + /* x0078 - 0x007f reserved */ + +/* + * Bank 1 + * - completely empty (this is the RAP Block window) + * Note: if RAP = 1 this page is reserved + */ + +/* + * Bank 2 + */ +/* NA reg = 48 bit Network Address Register, 3x16 or 8x8 bit readable */ + +#define B2_MAC_1 0x0100 /* NA reg MAC Address 1 */ + /* 0x0106 - 0x0107 reserved */ +#define B2_MAC_2 0x0108 /* NA reg MAC Address 2 */ + /* 0x010e - 0x010f reserved */ +#define B2_MAC_3 0x0110 /* NA reg MAC Address 3 */ + /* 0x0116 - 0x0117 reserved */ +#define B2_CONN_TYP 0x0118 /* 8 bit Connector type */ +#define B2_PMD_TYP 0x0119 /* 8 bit PMD type */ +#define B2_MAC_CFG 0x011a /* 8 bit MAC Configuration */ +#define B2_CHIP_REV 0x011b /* 8 bit Queen Chip Revision Number */ + /* Eprom registers are currently of no use */ +#define B2_E_0 0x011c /* 8 bit EPROM Byte 0 */ +#define B2_E_1 0x011d /* 8 bit EPROM Byte 1 */ +#define B2_E_2 0x011e /* 8 bit EPROM Byte 2 */ +#define B2_E_3 0x011f /* 8 bit EPROM Byte 3 */ +#define B2_FAR 0x0120 /* 32 bit Flash-Prom Addr Reg/Cnt */ +#define B2_FDP 0x0124 /* 8 bit Flash-Prom Data Port */ + /* 0x0125 - 0x0127: reserved */ +#define B2_LD_CRTL 0x0128 /* 8 bit EPROM loader control register */ +#define B2_LD_TEST 0x0129 /* 8 bit EPROM loader test register */ + /* 0x012a - 0x012f: reserved */ +#define B2_TI_INI 0x0130 /* 32 bit Timer init value */ +#define B2_TI_VAL 0x0134 /* 32 bit Timer value */ +#define B2_TI_CRTL 0x0138 /* 8 bit Timer control */ +#define B2_TI_TEST 0x0139 /* 8 Bit Timer Test */ + /* 0x013a - 0x013f: reserved */ +#define B2_IRQM_INI 0x0140 /* 32 bit IRQ Moderation Timer Init Reg.*/ +#define B2_IRQM_VAL 0x0144 /* 32 bit IRQ Moderation Timer Value */ +#define B2_IRQM_CTRL 0x0148 /* 8 bit IRQ Moderation Timer Control */ +#define B2_IRQM_TEST 0x0149 /* 8 bit IRQ Moderation Timer Test */ +#define B2_IRQM_MSK 0x014c /* 32 bit IRQ Moderation Mask */ +#define B2_IRQM_HWE_MSK 0x0150 /* 32 bit IRQ Moderation HW Error Mask */ + /* 0x0154 - 0x0157: reserved */ +#define B2_TST_CTRL1 0x0158 /* 8 bit Test Control Register 1 */ +#define B2_TST_CTRL2 0x0159 /* 8 bit Test Control Register 2 */ + /* 0x015a - 0x015b: reserved */ +#define B2_GP_IO 0x015c /* 32 bit General Purpose IO Register */ +#define B2_I2C_CTRL 0x0160 /* 32 bit I2C HW Control Register */ +#define B2_I2C_DATA 0x0164 /* 32 bit I2C HW Data Register */ +#define B2_I2C_IRQ 0x0168 /* 32 bit I2C HW IRQ Register */ +#define B2_I2C_SW 0x016c /* 32 bit I2C SW Port Register */ +#define B2_BSC_INI 0x0170 /* 32 bit Blink Source Counter Init Val */ +#define B2_BSC_VAL 0x0174 /* 32 bit Blink Source Counter Value */ +#define B2_BSC_CTRL 0x0178 /* 8 bit Blink Source Counter Control */ +#define B2_BSC_STAT 0x0179 /* 8 bit Blink Source Counter Status */ +#define B2_BSC_TST 0x017a /* 16 bit Blink Source Counter Test Reg */ + /* 0x017c - 0x017f: reserved */ + +/* + * Bank 3 + */ +#define B3_RAM_ADDR 0x0180 /* 32 bit RAM Address, to read or write */ +#define B3_RAM_DATA_LO 0x0184 /* 32 bit RAM Data Word (low dWord) */ +#define B3_RAM_DATA_HI 0x0188 /* 32 bit RAM Data Word (high dWord) */ + /* 0x018c - 0x018f: reserved */ +/* RAM Interface Registers */ +/* + * The HW-Spec. call this registers Timeout Value 0..11. But this names are + * not usable in SW. Please notice these are NOT real timeouts, these are + * the number of qWords transfered continously. + */ +#define B3_RI_WTO_R1 0x0190 /* 8 bit RAM Iface WR Timeout Queue R1 (TO0) */ +#define B3_RI_WTO_XA1 0x0191 /* 8 bit RAM Iface WR Timeout Queue XA1 (TO1) */ +#define B3_RI_WTO_XS1 0x0192 /* 8 bit RAM Iface WR Timeout Queue XS1 (TO2) */ +#define B3_RI_RTO_R1 0x0193 /* 8 bit RAM Iface RD Timeout Queue R1 (TO3) */ +#define B3_RI_RTO_XA1 0x0194 /* 8 bit RAM Iface RD Timeout Queue XA1 (TO4) */ +#define B3_RI_RTO_XS1 0x0195 /* 8 bit RAM Iface RD Timeout Queue XS1 (TO5) */ +#define B3_RI_WTO_R2 0x0196 /* 8 bit RAM Iface WR Timeout Queue R2 (TO6) */ +#define B3_RI_WTO_XA2 0x0197 /* 8 bit RAM Iface WR Timeout Queue XA2 (TO7) */ +#define B3_RI_WTO_XS2 0x0198 /* 8 bit RAM Iface WR Timeout Queue XS2 (TO8) */ +#define B3_RI_RTO_R2 0x0199 /* 8 bit RAM Iface RD Timeout Queue R2 (TO9) */ +#define B3_RI_RTO_XA2 0x019a /* 8 bit RAM Iface RD Timeout Queue XA2 (TO10)*/ +#define B3_RI_RTO_XS2 0x019b /* 8 bit RAM Iface RD Timeout Queue XS2 (TO11)*/ +#define B3_RI_TO_VAL 0x019c /* 8 bit RAM Iface Current Timeout Count Val */ + /* 0x019d - 0x019f reserved */ +#define B3_RI_CTRL 0x01a0 /* 16 bit RAM Iface Control Register */ +#define B3_RI_TEST 0x01a2 /* 8 bit RAM Iface Test Register */ + /* 0x01a3 - 0x01af reserved */ +/* MAC Arbiter Registers */ +/* Please notice these are the number of qWord tranfered continously and */ +/* NOT real timeouts */ +#define B3_MA_TOINI_RX1 0x01b0 /* 8 bit Timeout Init Value Rx Path MAC 1 */ +#define B3_MA_TOINI_RX2 0x01b1 /* 8 bit Timeout Init Value Rx Path MAC 2 */ +#define B3_MA_TOINI_TX1 0x01b2 /* 8 bit Timeout Init Value Tx Path MAC 1 */ +#define B3_MA_TOINI_TX2 0x01b3 /* 8 bit Timeout Init Value Tx Path MAC 2 */ +#define B3_MA_TOVAL_RX1 0x01b4 /* 8 bit Timeout Value Rx Path MAC 1 */ +#define B3_MA_TOVAL_RX2 0x01b5 /* 8 bit Timeout Value Rx Path MAC 1 */ +#define B3_MA_TOVAL_TX1 0x01b6 /* 8 bit Timeout Value Tx Path MAC 2 */ +#define B3_MA_TOVAL_TX2 0x01b7 /* 8 bit Timeout Value Tx Path MAC 2 */ +#define B3_MA_TO_CTRL 0x01b8 /* 16 bit MAC Arbiter Timeout Ctrl Reg */ +#define B3_MA_TO_TEST 0x01ba /* 16 bit MAC Arbiter Timeout Test Reg */ + /* 0x01bc - 0x01bf reserved */ +#define B3_MA_RCINI_RX1 0x01c0 /* 8 bit Recovery Init Value Rx Path MAC 1 */ +#define B3_MA_RCINI_RX2 0x01c1 /* 8 bit Recovery Init Value Rx Path MAC 2 */ +#define B3_MA_RCINI_TX1 0x01c2 /* 8 bit Recovery Init Value Tx Path MAC 1 */ +#define B3_MA_RCINI_TX2 0x01c3 /* 8 bit Recovery Init Value Tx Path MAC 2 */ +#define B3_MA_RCVAL_RX1 0x01c4 /* 8 bit Recovery Value Rx Path MAC 1 */ +#define B3_MA_RCVAL_RX2 0x01c5 /* 8 bit Recovery Value Rx Path MAC 1 */ +#define B3_MA_RCVAL_TX1 0x01c6 /* 8 bit Recovery Value Tx Path MAC 2 */ +#define B3_MA_RCVAL_TX2 0x01c7 /* 8 bit Recovery Value Tx Path MAC 2 */ +#define B3_MA_RC_CTRL 0x01c8 /* 16 bit MAC Arbiter Recovery Ctrl Reg */ +#define B3_MA_RC_TEST 0x01ca /* 16 bit MAC Arbiter Recovery Test Reg */ + /* 0x01cc - 0x01cf reserved */ +/* Packet Arbiter Registers, This are real timeouts */ +#define B3_PA_TOINI_RX1 0x01d0 /* 16 bit Timeout Init Val Rx Path MAC 1*/ + /* 0x01d2 - 0x01d3: reserved */ +#define B3_PA_TOINI_RX2 0x01d4 /* 16 bit Timeout Init Val Rx Path MAC 2*/ + /* 0x01d6 - 0x01d7: reserved */ +#define B3_PA_TOINI_TX1 0x01d8 /* 16 bit Timeout Init Val Tx Path MAC 1*/ + /* 0x01da - 0x01db: reserved */ +#define B3_PA_TOINI_TX2 0x01dc /* 16 bit Timeout Init Val Tx Path MAC 2*/ + /* 0x01de - 0x01df: reserved */ +#define B3_PA_TOVAL_RX1 0x01e0 /* 16 bit Timeout Val Rx Path MAC 1 */ + /* 0x01e2 - 0x01e3: reserved */ +#define B3_PA_TOVAL_RX2 0x01e4 /* 16 bit Timeout Val Rx Path MAC 2 */ + /* 0x01e6 - 0x01e7: reserved */ +#define B3_PA_TOVAL_TX1 0x01e8 /* 16 bit Timeout Val Tx Path MAC 1 */ + /* 0x01ea - 0x01eb: reserved */ +#define B3_PA_TOVAL_TX2 0x01ec /* 16 bit Timeout Val Tx Path MAC 2 */ + /* 0x01ee - 0x01ef: reserved */ +#define B3_PA_CTRL 0x01f0 /* 16 bit Packet Arbiter Ctrl Register */ +#define B3_PA_TEST 0x01f2 /* 16 bit Packet Arbiter Test Register */ + /* 0x01f4 - 0x01ff: reserved */ + +/* + * Bank 4 - 5 + */ + +/* Transmit Arbiter Registers MAC 1 and 2, user MR_ADDR() to address */ +#define TXA_ITI_INI 0x0200 /* 32 bit Tx Arb Interval Timer Init Val*/ +#define TXA_ITI_VAL 0x0204 /* 32 bit Tx Arb Interval Timer Value */ +#define TXA_LIM_INI 0x0208 /* 32 bit Tx Arb Limit Counter Init Val */ +#define TXA_LIM_VAL 0x020c /* 32 bit Tx Arb Limit Counter Value */ +#define TXA_CTRL 0x0210 /* 8 bit Tx Arbiter Control Register */ +#define TXA_TEST 0x0211 /* 8 bit Tx Arbiter Test Register */ +#define TXA_STAT 0x0212 /* 8 bit Tx Arbiter Status Register */ + /* 0x0213 - 0x027f: reserved */ + +/* + * Bank 6 + */ +/* External registers */ +#define B6_EXT_REG 0x0300 + +/* + * Bank 7 + */ +/* This is a copy of the Configuration register file (lower half) */ +#define B7_CFG_SPC 0x0380 + +/* + * Bank 8 - 15 + */ +/* Receive and Transmit Queue Registers, use Q_ADDR() to access */ +#define B8_Q_REGS 0x0400 + +/* Queue Register Offsets, use Q_ADDR() to access */ +#define Q_D 0x00 /* 8*32 bit Current Descriptor */ +#define Q_DA_L 0x20 /* 32 bit Current Descriptor Address Low dWord */ +#define Q_DA_H 0x24 /* 32 bit Current Descriptor Address High dWord */ +#define Q_AC_L 0x28 /* 32 bit Current Address Counter Low dWord */ +#define Q_AC_H 0x2c /* 32 bit Current Address Counter High dWord */ +#define Q_BC 0x30 /* 32 bit Current Byte Counter */ +#define Q_CSR 0x34 /* 32 bit BMU Control/Status Register */ +#define Q_F 0x38 /* 32 bit Flag Register */ +#define Q_T1 0x3c /* 32 bit Test Register 1 */ +#define Q_T1_TR 0x3c /* 8 bit Test Register 1 Transfer SM */ +#define Q_T1_WR 0x3d /* 8 bit Test Register 1 Write Descriptor SM */ +#define Q_T1_RD 0x3e /* 8 bit Test Register 1 Read Descriptor SM */ +#define Q_T1_SV 0x3f /* 8 bit Test Register 1 Supervisor SM */ +#define Q_T2 0x40 /* 32 bit Test Register 2 */ +#define Q_T3 0x44 /* 32 bit Test Register 3 */ + /* 0x48 - 0x7f: reserved */ + +/* + * Bank 16 - 23 + */ +/* RAM Buffer Registers */ +#define B16_RAM_REGS 0x0800 + +/* RAM Buffer Register Offsets */ +/* use RB_ADDR(Queue,Offs) to address */ +#define RB_START 0x00 /* 32 bit RAM Buffer Start Address */ +#define RB_END 0x04 /* 32 bit RAM Buffer End Address */ +#define RB_WP 0x08 /* 32 bit RAM Buffer Write Pointer */ +#define RB_RP 0x0c /* 32 bit RAM Buffer Read Pointer */ +#define RB_RX_UTPP 0x10 /* 32 bit Rx Upper Threshold, Pause Pack*/ +#define RB_RX_LTPP 0x14 /* 32 bit Rx Lower Threshold, Pause Pack*/ +#define RB_RX_UTHP 0x18 /* 32 bit Rx Upper Threshold, High Prio */ +#define RB_RX_LTHP 0x1c /* 32 bit Rx Lower Threshold, High Prio */ + /* 0x10 - 0x1f: reserved for Tx RAM Buffer Registers */ +#define RB_PC 0x20 /* 32 bit RAM Buffer Packet Counter */ +#define RB_LEV 0x24 /* 32 bit RAM Buffer Level Register */ +#define RB_CTRL 0x28 /* 8 bit RAM Buffer Control Register */ +#define RB_TST1 0x29 /* 8 bit RAM Buffer Test Register 1 */ +#define RB_TST2 0x2A /* 8 bit RAM Buffer Test Register 2 */ + /* 0x2c - 0x7f: reserved */ + +/* + * Bank 24 - 25 + */ +/* Receive MAC FIFO, Receive LED, and Link Sync regs, use MR_ADDR() to address*/ +#define RX_MFF_EA 0x0c00 /* 32 bit Receive MAC FIFO End Address */ +#define RX_MFF_WP 0x0c04 /* 32 bit Receive MAC FIFO Write Pointer*/ + /* 0x0c08 - 0x0c0b reserved */ +#define RX_MFF_RP 0x0c0c /* 32 bit Receive MAC FIFO Read Pointer */ +#define RX_MFF_PC 0x0c10 /* 32 bit Receive MAC FIFO Packet Cnt */ +#define RX_MFF_LEV 0x0c14 /* 32 bit Receive MAC FIFO Level */ +#define RX_MFF_CTRL1 0x0c18 /* 16 bit Receive MAC FIFO Control Reg 1*/ +#define RX_MFF_STAT_TO 0x0c1a /* 8 bit Receive MAC Status Timeout */ +#define RX_MFF_TIST_TO 0x0c1b /* 8 bit Receive MAC Timestamp Timeout */ +#define RX_MFF_CTRL2 0x0c1c /* 8 bit Receive MAC FIFO Control Reg 2*/ +#define RX_MFF_TST1 0x0c1d /* 8 bit Receive MAC FIFO Test Reg 1 */ +#define RX_MFF_TST2 0x0c1e /* 8 bit Receive MAC FIFO Test Reg 2 */ + /* 0x0c1f reserved */ +#define RX_LED_INI 0x0c20 /* 32 bit Receive LED Cnt Init Value */ +#define RX_LED_VAL 0x0c24 /* 32 bit Receive LED Cnt Current Value */ +#define RX_LED_CTRL 0x0c28 /* 8 bit Receive LED Cnt Control Reg */ +#define RX_LED_TST 0x0c29 /* 8 bit Receive LED Cnt Test Register */ + /* 0x0c2a - 0x0c2f reserved */ +#define LNK_SYNC_INI 0x0c30 /* 32 bit Link Sync Cnt Init Value */ +#define LNK_SYNC_VAL 0x0c34 /* 32 bit Link Sync Cnt Current Value */ +#define LNK_SYNC_CTRL 0x0c38 /* 8 bit Link Sync Cnt Control Register*/ +#define LNK_SYNC_TST 0x0c39 /* 8 bit Link Sync Cnt Test Register */ + /* 0x0c3a - 0x0c3b reserved */ +#define LNK_LED_REG 0x0c3c /* 8 bit Link LED Register */ + /* 0x0c3d - 0x0c7f reserved */ + +/* + * Bank 26 - 27 + */ +/* Transmit MAC FIFO and Transmit LED Registers, use MR_ADDR() to address */ +#define TX_MFF_EA 0x0d00 /* 32 bit Transmit MAC FIFO End Address */ +#define TX_MFF_WP 0x0d04 /* 32 bit Transmit MAC FIFO WR Pointer */ +#define TX_MFF_WSP 0x0d08 /* 32 bit Transmit MAC FIFO WR Shadow Pt*/ +#define TX_MFF_RP 0x0d0c /* 32 bit Transmit MAC FIFO RD Pointer */ +#define TX_MFF_PC 0x0d10 /* 32 bit Transmit MAC FIFO Packet Cnt */ +#define TX_MFF_LEV 0x0d14 /* 32 bit Transmit MAC FIFO Level */ +#define TX_MFF_CTRL1 0x0d18 /* 16 bit Transmit MAC FIFO Ctrl Reg 1 */ +#define TX_MFF_WAF 0x0d1a /* 8 bit Transmit MAC Wait after flush*/ + /* 0x0c1b reserved */ +#define TX_MFF_CTRL2 0x0d1c /* 8 bit Transmit MAC FIFO Ctrl Reg 2 */ +#define TX_MFF_TST1 0x0d1d /* 8 bit Transmit MAC FIFO Test Reg 1 */ +#define TX_MFF_TST2 0x0d1e /* 8 bit Transmit MAC FIFO Test Reg 2 */ + /* 0x0d1f reserved */ +#define TX_LED_INI 0x0d20 /* 32 bit Transmit LED Cnt Init Value */ +#define TX_LED_VAL 0x0d24 /* 32 bit Transmit LED Cnt Current Val */ +#define TX_LED_CTRL 0x0d28 /* 8 bit Transmit LED Cnt Control Reg */ +#define TX_LED_TST 0x0d29 /* 8 bit Transmit LED Cnt Test Register*/ + /* 0x0d2a - 0x0d7f reserved */ + +/* + * Bank 28 + */ +/* Descriptor Poll Timer Registers */ +#define B28_DPT_INI 0x0e00 /* 32 bit Descriptor Poll Timer Init Val*/ +#define B28_DPT_VAL 0x0e04 /* 32 bit Descriptor Poll Timer Curr Val*/ +#define B28_DPT_CTRL 0x0e08 /* 8 bit Descriptor Poll Timer Ctrl Reg*/ + /* 0x0e09: reserved */ +#define B28_DPT_TST 0x0e0a /* 8 bit Descriptor Poll Timer Test Reg*/ + /* 0x0e0b - 0x0e8f: reserved */ + +/* + * Bank 29 - 31 + */ +/* 0x0e90 - 0x0fff: reserved */ + +/* + * Bank 0x20 - 0x3f + */ +/* 0x1000 - 0x1fff: reserved */ + +/* + * Bank 0x40 - 0x4f + */ +/* XMAC 1 registers */ +#define B40_XMAC1 0x2000 + +/* + * Bank 0x50 - 0x5f + */ +/* 0x2800 - 0x2fff: reserved */ + +/* + * Bank 0x60 - 0x6f + */ +/* XMAC 2 registers */ +#define B40_XMAC2 0x3000 + +/* + * Bank 0x70 - 0x7f + */ +/* 0x3800 - 0x3fff: reserved */ + +/* + * Control Register Bit Definitions: + */ +/* B0_RAP 8 bit Register Address Port */ + /* Bit 7: reserved */ +#define RAP_RAP 0x3f /* Bit 6..0: 0 = block 0, .., 6f = block 6f*/ + +/* B0_CTST 16 bit Control/Status register */ + /* Bit 15..10: reserved */ +#define CS_BUS_CLOCK (1<<9) /* Bit 9: Bus Clock 0/1 = 33/66MHz */ +#define CS_BUS_SLOT_SZ (1<<8) /* Bit 8: Slot Size 0/1 = 32/64 bit slot*/ +#define CS_ST_SW_IRQ (1<<7) /* Bit 7: Set IRQ SW Request */ +#define CS_CL_SW_IRQ (1<<6) /* Bit 6: Clear IRQ SW Request */ +#define CS_STOP_DONE (1<<5) /* Bit 5: Stop Master is finished */ +#define CS_STOP_MAST (1<<4) /* Bit 4: Command Bit to stop the master*/ +#define CS_MRST_CLR (1<<3) /* Bit 3: Clear Master reset */ +#define CS_MRST_SET (1<<2) /* Bit 2: Set Master reset */ +#define CS_RST_CLR (1<<1) /* Bit 1: Clear Software reset */ +#define CS_RST_SET (1<<0) /* Bit 0: Set Software reset */ + +/* B0_LED 8 Bit LED register */ + /* Bit 7..2: reserved */ +#define LED_STAT_ON (1<<1) /* Bit 1: Status LED on */ +#define LED_STAT_OFF (1<<0) /* Bit 0: Status LED off */ + +/* B0_ISRC 32 bit Interrupt Source Register */ +/* B0_IMSK 32 bit Interrupt Mask Register */ +/* B0_SP_ISRC 32 bit Special Interrupt Source Reg */ +/* B2_IRQM_MSK 32 bit IRQ Moderation Mask */ +#define IS_ALL_MSK 0xbfffffffL /* All Interrupt bits */ +#define IS_HW_ERR (1UL<<31) /* Bit 31: Interrupt HW Error */ + /* Bit 30: reserved */ +#define IS_PA_TO_RX1 (1L<<29) /* Bit 29: Packet Arb Timeout Rx1*/ +#define IS_PA_TO_RX2 (1L<<28) /* Bit 28: Packet Arb Timeout Rx2*/ +#define IS_PA_TO_TX1 (1L<<27) /* Bit 27: Packet Arb Timeout Tx1*/ +#define IS_PA_TO_TX2 (1L<<26) /* Bit 26: Packet Arb Timeout Tx2*/ +#define IS_I2C_READY (1L<<25) /* Bit 25: IRQ on end of I2C tx */ +#define IS_IRQ_SW (1L<<24) /* Bit 24: SW forced IRQ */ +#define IS_EXT_REG (1L<<23) /* Bit 23: IRQ from external reg */ +#define IS_TIMINT (1L<<22) /* Bit 22: IRQ from Timer */ +#define IS_MAC1 (1L<<21) /* Bit 21: IRQ from MAC 1 */ +#define IS_LNK_SYNC_M1 (1L<<20) /* Bit 20: Link Sync Cnt wrap M1 */ +#define IS_MAC2 (1L<<19) /* Bit 19: IRQ from MAC 2 */ +#define IS_LNK_SYNC_M2 (1L<<18) /* Bit 18: Link Sync Cnt wrap M2 */ +/* Receive Queue 1 */ +#define IS_R1_B (1L<<17) /* Bit 17: Q_R1 End of Buffer */ +#define IS_R1_F (1L<<16) /* Bit 16: Q_R1 End of Frame */ +#define IS_R1_C (1L<<15) /* Bit 15: Q_R1 Encoding Error */ +/* Receive Queue 2 */ +#define IS_R2_B (1L<<14) /* Bit 14: Q_R2 End of Buffer */ +#define IS_R2_F (1L<<13) /* Bit 13: Q_R2 End of Frame */ +#define IS_R2_C (1L<<12) /* Bit 12: Q_R2 Encoding Error */ +/* Synchronous Transmit Queue 1 */ +#define IS_XS1_B (1L<<11) /* Bit 11: Q_XS1 End of Buffer */ +#define IS_XS1_F (1L<<10) /* Bit 10: Q_XS1 End of Frame */ +#define IS_XS1_C (1L<<9) /* Bit 9: Q_XS1 Encoding Error */ +/* Asynchronous Transmit Queue 1 */ +#define IS_XA1_B (1L<<8) /* Bit 8: Q_XA1 End of Buffer */ +#define IS_XA1_F (1L<<7) /* Bit 7: Q_XA1 End of Frame */ +#define IS_XA1_C (1L<<6) /* Bit 6: Q_XA1 Encoding Error */ +/* Synchronous Transmit Queue 2 */ +#define IS_XS2_B (1L<<5) /* Bit 5: Q_XS2 End of Buffer */ +#define IS_XS2_F (1L<<4) /* Bit 4: Q_XS2 End of Frame */ +#define IS_XS2_C (1L<<3) /* Bit 3: Q_XS2 Encoding Error */ +/* Asynchronous Transmit Queue 2 */ +#define IS_XA2_B (1L<<2) /* Bit 2: Q_XA2 End of Buffer */ +#define IS_XA2_F (1L<<1) /* Bit 1: Q_XA2 End of Frame */ +#define IS_XA2_C (1L<<0) /* Bit 0: Q_XA2 Encoding Error */ + + +/* B0_HWE_ISRC 32 bit HW Error Interrupt Src Reg */ +/* B0_HWE_IMSK 32 bit HW Error Interrupt Mask Reg */ +/* B2_IRQM_HWE_MSK 32 bit IRQ Moderation HW Error Mask */ +#define IS_ERR_MSK 0x00000fffL /* All Error bits */ + /* Bit 31..12: reserved */ +#define IS_IRQ_MST_ERR (1L<<11) /* Bit 11: IRQ master error */ + /* PERR,RMABORT,RTABORT,DATAPERR */ +#define IS_IRQ_STAT (1L<<10) /* Bit 10: IRQ status execption */ + /* RMABORT, RTABORT, DATAPERR */ +#define IS_NO_STAT_M1 (1L<<9) /* Bit 9: No Rx Status from MAC1*/ +#define IS_NO_STAT_M2 (1L<<8) /* Bit 8: No Rx Status from MAC2*/ +#define IS_NO_TIST_M1 (1L<<7) /* Bit 7: No Timestamp from MAC1*/ +#define IS_NO_TIST_M2 (1L<<6) /* Bit 6: No Timestamp from MAC2*/ +#define IS_RAM_RD_PAR (1L<<5) /* Bit 5: RAM Read Parity Error */ +#define IS_RAM_WR_PAR (1L<<4) /* Bit 4: RAM Write Parity Error*/ +#define IS_M1_PAR_ERR (1L<<3) /* Bit 3: MAC 1 Parity Error */ +#define IS_M2_PAR_ERR (1L<<2) /* Bit 2: MAC 2 Parity Error */ +#define IS_R1_PAR_ERR (1L<<1) /* Bit 1: Queue R1 Parity Error */ +#define IS_R2_PAR_ERR (1L<<0) /* Bit 0: Queue R2 Parity Error */ + +/* B2_CONN_TYP 8 bit Connector type */ +/* B2_PMD_TYP 8 bit PMD type */ +/* Values of connector and PMD type comply to SysKonnect internal std */ + +/* B2_MAC_CFG 8 bit MAC Configuration */ + /* Bit 7..2: reserved */ +#define CFG_DIS_M2_CLK (1<<1) /* Bit 1: Disable Clock for 2nd MAC */ +#define CFG_SNG_MAC (1<<0) /* Bit 0: MAC Config: 1=2 MACs / 0=1 MAC*/ + +/* B2_CHIP_REV 8 bit Queen Chip Revision Number */ +#define FIRST_CHIP_REV 0x0a /* Initial Revision Value */ + +/* B2_FAR 32 bit Flash-Prom Addr Reg/Cnt */ +#define FAR_ADDR 0x1ffffL /* Bit 16..0: FPROM Address mask */ + +/* B2_LD_CRTL 8 bit EPROM loader control register */ +/* Bits are currently reserved */ + +/* B2_LD_TEST 8 bit EPROM loader test register */ + /* Bit 7..4: reserved */ +#define LD_T_ON (1<<3) /* Bit 3: Loader Testmode on */ +#define LD_T_OFF (1<<2) /* Bit 2: Loader Testmode off */ +#define LD_T_STEP (1<<1) /* Bit 1: Decrement FPROM addr. Counter */ +#define LD_START (1<<0) /* Bit 0: Start loading FPROM */ + +/* + * Timer Section + */ +/* B2_TI_CRTL 8 bit Timer control */ +/* B2_IRQM_CTRL 8 bit IRQ Moderation Timer Control */ + /* Bit 7..3: reserved */ +#define TIM_START (1<<2) /* Bit 2: Start Timer */ +#define TIM_STOP (1<<1) /* Bit 1: Stop Timer */ +#define TIM_CLR_IRQ (1<<0) /* Bit 0: Clear Timer IRQ, (!IRQM) */ + +/* B2_TI_TEST 8 Bit Timer Test */ +/* B2_IRQM_TEST 8 bit IRQ Moderation Timer Test */ +/* B28_DPT_TST 8 bit Descriptor Poll Timer Test Reg */ + /* Bit 7..3: reserved */ +#define TIM_T_ON (1<<2) /* Bit 2: Test mode on */ +#define TIM_T_OFF (1<<1) /* Bit 1: Test mode off */ +#define TIM_T_STEP (1<<0) /* Bit 0: Test step */ + +/* B28_DPT_INI 32 bit Descriptor Poll Timer Init Val */ +/* B28_DPT_VAL 32 bit Descriptor Poll Timer Curr Val */ + /* Bit 31..24: reserved */ +#define DPT_MSK 0x00ffffffL /* Bit 23.. 0: Desc Poll Timer Bits */ + +/* B28_DPT_CTRL 8 bit Descriptor Poll Timer Ctrl Reg */ + /* Bit 7..2: reserved */ +#define DPT_START (1<<1) /* Bit 1: Start Desciptor Poll Timer */ +#define DPT_STOP (1<<0) /* Bit 0: Stop Desciptor Poll Timer */ + + +/* B2_TST_CTRL1 8 bit Test Control Register 1 */ +#define TST_FRC_DPERR_MR (1<<7) /* Bit 7: force DATAPERR on MST RD */ +#define TST_FRC_DPERR_MW (1<<6) /* Bit 6: force DATAPERR on MST WR */ +#define TST_FRC_DPERR_TR (1<<5) /* Bit 5: force DATAPERR on TRG RD */ +#define TST_FRC_DPERR_TW (1<<4) /* Bit 4: force DATAPERR on TRG WR */ +#define TST_FRC_APERR_M (1<<3) /* Bit 3: force ADDRPERR on MST */ +#define TST_FRC_APERR_T (1<<2) /* Bit 2: force ADDRPERR on TRG */ +#define TST_CFG_WRITE_ON (1<<1) /* Bit 1: Enable Config Reg WR */ +#define TST_CFG_WRITE_OFF (1<<0) /* Bit 0: Disable Config Reg WR */ + +/* B2_TST_CTRL2 8 bit Test Control Register 2 */ + /* Bit 7..4: reserved */ + /* force the following error on */ + /* the next master read/write */ +#define TST_FRC_DPERR_MR64 (1<<3) /* Bit 3: DataPERR RD 64 */ +#define TST_FRC_DPERR_MW64 (1<<2) /* Bit 2: DataPERR WR 64 */ +#define TST_FRC_APERR_1M64 (1<<1) /* Bit 1: AddrPERR on 1. phase */ +#define TST_FRC_APERR_2M64 (1<<0) /* Bit 0: AddrPERR on 2. phase */ + +/* B2_GP_IO 32 bit General Purpose IO Register */ + /* Bit 31..26: reserved */ +#define GP_DIR_9 (1L<<25) /* Bit 25: IO_9 direct, 0=I/1=O */ +#define GP_DIR_8 (1L<<24) /* Bit 24: IO_8 direct, 0=I/1=O */ +#define GP_DIR_7 (1L<<23) /* Bit 23: IO_7 direct, 0=I/1=O */ +#define GP_DIR_6 (1L<<22) /* Bit 22: IO_6 direct, 0=I/1=O */ +#define GP_DIR_5 (1L<<21) /* Bit 21: IO_5 direct, 0=I/1=O */ +#define GP_DIR_4 (1L<<20) /* Bit 20: IO_4 direct, 0=I/1=O */ +#define GP_DIR_3 (1L<<19) /* Bit 19: IO_3 direct, 0=I/1=O */ +#define GP_DIR_2 (1L<<18) /* Bit 18: IO_2 direct, 0=I/1=O */ +#define GP_DIR_1 (1L<<17) /* Bit 17: IO_1 direct, 0=I/1=O */ +#define GP_DIR_0 (1L<<16) /* Bit 16: IO_0 direct, 0=I/1=O */ + /* Bit 15..10: reserved */ +#define GP_IO_9 (1L<<9) /* Bit 9: IO_9 pin */ +#define GP_IO_8 (1L<<8) /* Bit 8: IO_8 pin */ +#define GP_IO_7 (1L<<7) /* Bit 7: IO_7 pin */ +#define GP_IO_6 (1L<<6) /* Bit 6: IO_6 pin */ +#define GP_IO_5 (1L<<5) /* Bit 5: IO_5 pin */ +#define GP_IO_4 (1L<<4) /* Bit 4: IO_4 pin */ +#define GP_IO_3 (1L<<3) /* Bit 3: IO_3 pin */ +#define GP_IO_2 (1L<<2) /* Bit 2: IO_2 pin */ +#define GP_IO_1 (1L<<1) /* Bit 1: IO_1 pin */ +#define GP_IO_0 (1L<<0) /* Bit 0: IO_0 pin */ + +/* B2_I2C_CTRL 32 bit I2C HW Control Register */ +#define I2C_FLAG (1UL<<31) /* Bit 31: Start read/write if WR*/ +#define I2C_ADDR (0x7fffL<<16) /* Bit 30..16: Addr to be RD/WR */ +#define I2C_DEV_SEL (0x7fL<<9) /* Bit 15.. 9: I2C Device Select */ + /* Bit 8.. 5: reserved */ +#define I2C_BURST_LEN (1L<<4) /* Bit 4: Burst Len, 1/4 bytes */ +#define I2C_DEV_SIZE (7L<<1) /* Bit 3.. 1: I2C Device Size */ +#define I2C_025K_DEV (0L<<1) /* 0: 256 Bytes or smal. */ +#define I2C_05K_DEV (1L<<1) /* 1: 512 Bytes */ +#define I2C_1K_DEV (2L<<1) /* 2: 1024 Bytes */ +#define I2C_2K_DEV (3L<<1) /* 3: 2048 Bytes */ +#define I2C_4K_DEV (4L<<1) /* 4: 4096 Bytes */ +#define I2C_8K_DEV (5L<<1) /* 5: 8192 Bytes */ +#define I2C_16K_DEV (6L<<1) /* 6: 16384 Bytes */ +#define I2C_32K_DEV (7L<<1) /* 7: 32768 Bytes */ +#define I2C_STOP (1L<<0) /* Bit 0: Interrupt I2C transfer*/ + +/* B2_I2C_IRQ 32 bit I2C HW IRQ Register */ + /* Bit 31..1 reserved */ +#define I2C_CLR_IRQ (1<<0) /* Bit 0: Clear I2C IRQ */ + +/* B2_I2C_SW 32 bit I2C HW SW Port Register */ + /* Bit 7..3: reserved */ +#define I2C_DATA_DIR (1<<2) /* Bit 2: direction of I2C_DATA */ +#define I2C_DATA (1<<1) /* Bit 1: I2C Data Port */ +#define I2C_CLK (1<<0) /* Bit 0: I2C Clock Port */ + +/* + * I2C Address + */ +#define I2C_SENS_ADDR LM80_ADDR /* I2C Sensor Address, (Volt and Temp)*/ + + +/* B2_BSC_CTRL 8 bit Blink Source Counter Control */ + /* Bit 7..2: reserved */ +#define BSC_START (1<<1) /* Bit 1: Start Blink Source Counter */ +#define BSC_STOP (1<<0) /* Bit 0: Stop Blink Source Counter */ + +/* B2_BSC_STAT 8 bit Blink Source Counter Status */ + /* Bit 7..1: reserved */ +#define BSC_SRC (1<<0) /* Bit 0: Blink Source, 0=Off / 1=On */ + +/* B2_BSC_TST 16 bit Blink Source Counter Test Reg */ +#define BSC_T_ON (1<<2) /* Bit 2: Test mode on */ +#define BSC_T_OFF (1<<1) /* Bit 1: Test mode off */ +#define BSC_T_STEP (1<<0) /* Bit 0: Test step */ + + +/* B3_RAM_ADDR 32 bit RAM Address, to read or write */ + /* Bit 31..19: reserved */ +#define RAM_ADR_RAN 0x0007ffffL /* Bit 18.. 0: RAM Address Range */ + +/* RAM Interface Registers */ +/* B3_RI_CTRL 16 bit RAM Iface Control Register */ + /* Bit 15..10: reserved */ +#define RI_CLR_RD_PERR (1<<9) /* Bit 9: Clear IRQ RAM Read Parity Err */ +#define RI_CLR_WR_PERR (1<<8) /* Bit 8: Clear IRQ RAM Write Parity Err*/ + /* Bit 7..2: reserved */ +#define RI_RST_CLR (1<<1) /* Bit 1: Clear RAM Interface Reset */ +#define RI_RST_SET (1<<0) /* Bit 0: Set RAM Interface Reset */ + +/* B3_RI_TEST 8 bit RAM Iface Test Register */ + /* Bit 15..4: reserved */ +#define RI_T_EV (1<<3) /* Bit 3: Timeout Event occured */ +#define RI_T_ON (1<<2) /* Bit 2: Timeout Timer Test On */ +#define RI_T_OFF (1<<1) /* Bit 1: Timeout Timer Test Off */ +#define RI_T_STEP (1<<0) /* Bit 0: Timeout Timer Step */ + +/* MAC Arbiter Registers */ +/* B3_MA_TO_CTRL 16 bit MAC Arbiter Timeout Ctrl Reg */ + /* Bit 15..4: reserved */ +#define MA_FOE_ON (1<<3) /* Bit 3: XMAC Fast Output Enable ON */ +#define MA_FOE_OFF (1<<2) /* Bit 2: XMAC Fast Output Enable OFF */ +#define MA_RST_CLR (1<<1) /* Bit 1: Clear MAC Arbiter Reset */ +#define MA_RST_SET (1<<0) /* Bit 0: Set MAC Arbiter Reset */ + +/* B3_MA_RC_CTRL 16 bit MAC Arbiter Recovery Ctrl Reg */ + /* Bit 15..8: reserved */ +#define MA_ENA_REC_TX2 (1<<7) /* Bit 7: Enable Recovery Timer TX2 */ +#define MA_DIS_REC_TX2 (1<<6) /* Bit 6: Disable Recovery Timer TX2 */ +#define MA_ENA_REC_TX1 (1<<5) /* Bit 5: Enable Recovery Timer TX1 */ +#define MA_DIS_REC_TX1 (1<<4) /* Bit 4: Disable Recovery Timer TX1 */ +#define MA_ENA_REC_RX2 (1<<3) /* Bit 3: Enable Recovery Timer RX2 */ +#define MA_DIS_REC_RX2 (1<<2) /* Bit 2: Disable Recovery Timer RX2 */ +#define MA_ENA_REC_RX1 (1<<1) /* Bit 1: Enable Recovery Timer RX1 */ +#define MA_DIS_REC_RX1 (1<<0) /* Bit 0: Disable Recovery Timer RX1 */ + +/* Packet Arbiter Registers */ +/* B3_PA_CTRL 16 bit Packet Arbiter Ctrl Register */ + /* Bit 15..14: reserved */ +#define PA_CLR_TO_TX2 (1<<13) /* Bit 13: Clear IRQ Packet Timeout TX2 */ +#define PA_CLR_TO_TX1 (1<<12) /* Bit 12: Clear IRQ Packet Timeout TX1 */ +#define PA_CLR_TO_RX2 (1<<11) /* Bit 11: Clear IRQ Packet Timeout RX2 */ +#define PA_CLR_TO_RX1 (1<<10) /* Bit 10: Clear IRQ Packet Timeout RX1 */ +#define PA_ENA_TO_TX2 (1<<9) /* Bit 9: Enable Timeout Timer TX2 */ +#define PA_DIS_TO_TX2 (1<<8) /* Bit 8: Disable Timeout Timer TX2 */ +#define PA_ENA_TO_TX1 (1<<7) /* Bit 7: Enable Timeout Timer TX1 */ +#define PA_DIS_TO_TX1 (1<<6) /* Bit 6: Disable Timeout Timer TX1 */ +#define PA_ENA_TO_RX2 (1<<5) /* Bit 5: Enable Timeout Timer RX2 */ +#define PA_DIS_TO_RX2 (1<<4) /* Bit 4: Disable Timeout Timer RX2 */ +#define PA_ENA_TO_RX1 (1<<3) /* Bit 3: Enable Timeout Timer RX1 */ +#define PA_DIS_TO_RX1 (1<<2) /* Bit 2: Disable Timeout Timer RX1 */ +#define PA_RST_CLR (1<<1) /* Bit 1: Clear MAC Arbiter Reset */ +#define PA_RST_SET (1<<0) /* Bit 0: Set MAC Arbiter Reset */ + +#define PA_ENA_TO_ALL (PA_ENA_TO_RX1 | PA_ENA_TO_RX2 |\ + PA_ENA_TO_TX1 | PA_ENA_TO_TX2) + +/* Rx/Tx Path related Arbiter Test Registers */ +/* B3_MA_TO_TEST 16 bit MAC Arbiter Timeout Test Reg */ +/* B3_MA_RC_TEST 16 bit MAC Arbiter Recovery Test Reg */ +/* B3_PA_TEST 16 bit Packet Arbiter Test Register */ +/* Bit 15, 11, 7, and 3 are reserved in B3_PA_TEST */ +#define TX2_T_EV (1<<15) /* Bit 15: TX2 Timeout/Recv Event occured*/ +#define TX2_T_ON (1<<14) /* Bit 14: TX2 Timeout/Recv Timer Test On*/ +#define TX2_T_OFF (1<<13) /* Bit 13: TX2 Timeout/Recv Timer Tst Off*/ +#define TX2_T_STEP (1<<12) /* Bit 12: TX2 Timeout/Recv Timer Step */ +#define TX1_T_EV (1<<11) /* Bit 11: TX1 Timeout/Recv Event occured*/ +#define TX1_T_ON (1<<10) /* Bit 10: TX1 Timeout/Recv Timer Test On*/ +#define TX1_T_OFF (1<<9) /* Bit 9: TX1 Timeout/Recv Timer Tst Off*/ +#define TX1_T_STEP (1<<8) /* Bit 8: TX1 Timeout/Recv Timer Step */ +#define RX2_T_EV (1<<7) /* Bit 7: RX2 Timeout/Recv Event occured*/ +#define RX2_T_ON (1<<6) /* Bit 6: RX2 Timeout/Recv Timer Test On*/ +#define RX2_T_OFF (1<<5) /* Bit 5: RX2 Timeout/Recv Timer Tst Off*/ +#define RX2_T_STEP (1<<4) /* Bit 4: RX2 Timeout/Recv Timer Step */ +#define RX1_T_EV (1<<3) /* Bit 3: RX1 Timeout/Recv Event occured*/ +#define RX1_T_ON (1<<2) /* Bit 2: RX1 Timeout/Recv Timer Test On*/ +#define RX1_T_OFF (1<<1) /* Bit 1: RX1 Timeout/Recv Timer Tst Off*/ +#define RX1_T_STEP (1<<0) /* Bit 0: RX1 Timeout/Recv Timer Step */ + + +/* Transmit Arbiter Registers MAC 1 and 2, user MR_ADDR() to address */ +/* TXA_ITI_INI 32 bit Tx Arb Interval Timer Init Val */ +/* TXA_ITI_VAL 32 bit Tx Arb Interval Timer Value */ +/* TXA_LIM_INI 32 bit Tx Arb Limit Counter Init Val */ +/* TXA_LIM_VAL 32 bit Tx Arb Limit Counter Value */ + /* Bit 31..24: reserved */ +#define TXA_MAX_VAL 0x00ffffffL /* Bit 23.. 0: Max TXA Timer/Cnt Val */ + +/* TXA_CTRL 8 bit Tx Arbiter Control Register */ +#define TXA_ENA_FSYNC (1<<7) /* Bit 7: Enable force of sync tx queue */ +#define TXA_DIS_FSYNC (1<<6) /* Bit 6: Disable force of sync tx queue*/ +#define TXA_ENA_ALLOC (1<<5) /* Bit 5: Enable alloc of free bandwidth*/ +#define TXA_DIS_ALLOC (1<<4) /* Bit 4: Disabl alloc of free bandwidth*/ +#define TXA_START_RC (1<<3) /* Bit 3: Start sync Rate Control */ +#define TXA_STOP_RC (1<<2) /* Bit 2: Stop sync Rate Control */ +#define TXA_ENA_ARB (1<<1) /* Bit 1: Enable Tx Arbiter */ +#define TXA_DIS_ARB (1<<0) /* Bit 0: Disable Tx Arbiter */ + +/* TXA_TEST 8 bit Tx Arbiter Test Register */ + /* Bit 7..6: reserved */ +#define TXA_INT_T_ON (1<<5) /* Bit 5: Tx Arb Interval Timer Test On */ +#define TXA_INT_T_OFF (1<<4) /* Bit 4: Tx Arb Interval Timer Test Off*/ +#define TXA_INT_T_STEP (1<<3) /* Bit 3: Tx Arb Interval Timer Step */ +#define TXA_LIM_T_ON (1<<2) /* Bit 2: Tx Arb Limit Timer Test On */ +#define TXA_LIM_T_OFF (1<<1) /* Bit 1: Tx Arb Limit Timer Test Off */ +#define TXA_LIM_T_STEP (1<<0) /* Bit 0: Tx Arb Limit Timer Step */ + +/* TXA_STAT 8 bit Tx Arbiter Status Register */ + /* Bit 7..1: reserved */ +#define TXA_PRIO_XS (1<<0) /* Bit 0: sync queue has prio to send */ + +/* Q_BC 32 bit Current Byte Counter */ + /* Bit 31..16: reserved */ +#define BC_MAX 0xffff /* Bit 15.. 0: Byte counter */ + +/* BMU Control Status Registers */ +/* B0_R1_CSR 32 bit BMU Ctrl/Stat Rx Queue 1 */ +/* B0_R2_CSR 32 bit BMU Ctrl/Stat Rx Queue 2 */ +/* B0_XA1_CSR 32 bit BMU Ctrl/Stat Sync Tx Queue 1 */ +/* B0_XS1_CSR 32 bit BMU Ctrl/Stat Async Tx Queue 1 */ +/* B0_XA2_CSR 32 bit BMU Ctrl/Stat Sync Tx Queue 2 */ +/* B0_XS2_CSR 32 bit BMU Ctrl/Stat Async Tx Queue 2 */ +/* Q_CSR 32 bit BMU Control/Status Register */ + /* Bit 31..25: reserved */ +#define CSR_SV_IDLE (1L<<24) /* Bit 24: BMU SM Idle */ + /* Bit 23..22: reserved */ +#define CSR_DESC_CLR (1L<<21) /* Bit 21: Clear Reset for Descr */ +#define CSR_DESC_SET (1L<<20) /* Bit 20: Set Reset for Descr */ +#define CSR_FIFO_CLR (1L<<19) /* Bit 19: Clear Reset for FIFO */ +#define CSR_FIFO_SET (1L<<18) /* Bit 18: Set Reset for FIFO */ +#define CSR_HPI_RUN (1L<<17) /* Bit 17: Release HPI SM */ +#define CSR_HPI_RST (1L<<16) /* Bit 16: Reset HPI SM to Idle */ +#define CSR_SV_RUN (1L<<15) /* Bit 15: Release Supervisor SM */ +#define CSR_SV_RST (1L<<14) /* Bit 14: Reset Supervisor SM */ +#define CSR_DREAD_RUN (1L<<13) /* Bit 13: Release Descr Read SM */ +#define CSR_DREAD_RST (1L<<12) /* Bit 12: Reset Descr Read SM */ +#define CSR_DWRITE_RUN (1L<<11) /* Bit 11: Rel. Descr Write SM */ +#define CSR_DWRITE_RST (1L<<10) /* Bit 10: Reset Descr Write SM */ +#define CSR_TRANS_RUN (1L<<9) /* Bit 9: Release Transfer SM */ +#define CSR_TRANS_RST (1L<<8) /* Bit 8: Reset Transfer SM */ +#define CSR_ENA_POL (1L<<7) /* Bit 7: Enable Descr Polling */ +#define CSR_DIS_POL (1L<<6) /* Bit 6: Disable Descr Polling */ +#define CSR_STOP (1L<<5) /* Bit 5: Stop Rx/Tx Queue */ +#define CSR_START (1L<<4) /* Bit 4: Start Rx/Tx Queue */ +#define CSR_IRQ_CL_P (1L<<3) /* Bit 3: (Rx) Clear Parity IRQ */ +#define CSR_IRQ_CL_B (1L<<2) /* Bit 2: Clear EOB IRQ */ +#define CSR_IRQ_CL_F (1L<<1) /* Bit 1: Clear EOF IRQ */ +#define CSR_IRQ_CL_C (1L<<0) /* Bit 0: Clear ERR IRQ */ + +#define CSR_SET_RESET (CSR_DESC_SET|CSR_FIFO_SET|CSR_HPI_RST|CSR_SV_RST|\ + CSR_DREAD_RST|CSR_DWRITE_RST|CSR_TRANS_RST) +#define CSR_CLR_RESET (CSR_DESC_CLR|CSR_FIFO_CLR|CSR_HPI_RUN|CSR_SV_RUN|\ + CSR_DREAD_RUN|CSR_DWRITE_RUN|CSR_TRANS_RUN) + + +/* Q_F 32 bit Flag Register */ + /* Bit 28..31: reserved */ +#define F_ALM_FULL (1L<<27) (Rx) /* Bit 27: (Rx) FIFO almost full */ +#define F_EMPTY (1L<<27) (Tx) /* Bit 27: (Tx) FIFO empty flag */ +#define F_FIFO_EOF (1L<<26) /* Bit 26: Fag bit in FIFO */ +#define F_WM_REACHED (1L<<25) /* Bit 25: Watermark reached */ + /* Bit 24: reserved */ +#define F_FIFO_LEVEL (0x1fL<<16) /* Bit 23..16: # of Qwords in FIFO */ + /* Bit 15..11: reserved */ +#define F_WATER_MARK 0x0007ffL /* Bit 10.. 0: Watermark */ + +/* Q_T1 32 bit Test Register 1 */ +/* Holds four State Machine control Bytes */ +#define SM_CRTL_SV (0xffL<<24) /* Bit 31..24: Control Supervisor SM */ +#define SM_CRTL_RD (0xffL<<16) /* Bit 23..16: Control Read Desc SM */ +#define SM_CRTL_WR (0xffL<<8) /* Bit 15.. 8: Control Write Desc SM */ +#define SM_CRTL_TR (0xffL<<0) /* Bit 7.. 0: Control Transfer SM */ + +/* Q_T1_TR 8 bit Test Register 1 Transfer SM */ +/* Q_T1_WR 8 bit Test Register 1 Write Descriptor SM */ +/* Q_T1_RD 8 bit Test Register 1 Read Descriptor SM */ +/* Q_T1_SV 8 bit Test Register 1 Supervisor SM */ +/* The control status byte of each machine looks like ... */ +#define SM_STATE 0xf0 /* Bit 7..4: State which shall be loaded */ +#define SM_LOAD (1<<3) /* Bit 3: Load the SM with SM_STATE */ +#define SM_TEST_ON (1<<2) /* Bit 2: Switch on SM Test Mode */ +#define SM_TEST_OFF (1<<1) /* Bit 1: Go off the Test Mode */ +#define SM_STEP (1<<0) /* Bit 0: Step the State Machine */ +/* The encoding of the states is not supported by the Diagnostics Tool */ + +/* Q_T2 32 bit Test Register 2 */ + /* Bit 31..8: reserved */ +#define T2_AC_T_ON (1<<7) /* Bit 7: Address Counter Test Mode on */ +#define T2_AC_T_OFF (1<<6) /* Bit 6: Address Counter Test Mode off*/ +#define T2_BC_T_ON (1<<5) /* Bit 5: Byte Counter Test Mode on */ +#define T2_BC_T_OFF (1<<4) /* Bit 4: Byte Counter Test Mode off */ +#define T2_STEP04 (1<<3) /* Bit 3: Inc AC/Dec BC by 4 */ +#define T2_STEP03 (1<<2) /* Bit 2: Inc AC/Dec BC by 3 */ +#define T2_STEP02 (1<<1) /* Bit 1: Inc AC/Dec BC by 2 */ +#define T2_STEP01 (1<<0) /* Bit 0: Inc AC/Dec BC by 1 */ + +/* Q_T3 32 bit Test Register 3 */ + /* Bit 31..7: reserved */ +#define T3_MUX (7<<4) /* Bit 6.. 4: Mux Position */ + /* Bit 3: reserved */ +#define T3_VRAM (7<<0) /* Bit 2.. 0: Virtual RAM Buffer Address */ + +/* RAM Buffer Register Offsets */ +/* use RB_ADDR(Queue,Offs) to address */ +/* RB_START 32 bit RAM Buffer Start Address */ +/* RB_END 32 bit RAM Buffer End Address */ +/* RB_WP 32 bit RAM Buffer Write Pointer */ +/* RB_RP 32 bit RAM Buffer Read Pointer */ +/* RB_RX_UTPP 32 bit Rx Upper Threshold, Pause Pack */ +/* RB_RX_LTPP 32 bit Rx Lower Threshold, Pasue Pack */ +/* RB_RX_UTHP 32 bit Rx Upper Threshold, High Prio */ +/* RB_RX_LTHP 32 bit Rx Lower Threshold, High Prio */ +/* RB_PC 32 bit RAM Buffer Packet Counter */ +/* RB_LEV 32 bit RAM Buffer Level Register */ + /* Bit 31..19: reserved */ +#define RB_MSK 0x0007ffff /* Bit 18.. 0: RAM Buffer Pointer Bits */ + +/* RB_TST2 8 bit RAM Buffer Test Register 2 */ + /* Bit 4..7: reserved */ +#define RB_PC_DEC (1<<3) /* Bit 3: Packet Counter Decrem */ +#define RB_PC_T_ON (1<<2) /* Bit 2: Packet Counter Test On */ +#define RB_PC_T_OFF (1<<1) /* Bit 1: Packet Counter Tst Off */ +#define RB_PC_INC (1<<0) /* Bit 0: Packet Counter Increm */ + +/* RB_TST1 8 bit RAM Buffer Test Register 1 */ + /* Bit 7: reserved */ +#define RB_WP_T_ON (1<<6) /* Bit 6: Write Pointer Test On */ +#define RB_WP_T_OFF (1<<5) /* Bit 5: Write Pointer Test Off */ +#define RB_WP_INC (1<<4) /* Bit 4: Write Pointer Increm */ + /* Bit 3: reserved */ +#define RB_RP_T_ON (1<<2) /* Bit 2: Read Pointer Test On */ +#define RB_RP_T_OFF (1<<1) /* Bit 1: Read Pointer Test Off */ +#define RB_RP_DEC (1<<0) /* Bit 0: Read Pointer Decrement */ + +/* RB_CTRL 8 bit RAM Buffer Control Register */ + /* Bit 7..6: reserved */ +#define RB_ENA_STFWD (1<<5) /* Bit 5: Enable Store & Forward */ +#define RB_DIS_STFWD (1<<4) /* Bit 4: Disab. Store & Forward */ +#define RB_ENA_OP_MD (1<<3) /* Bit 3: Enable Operation Mode */ +#define RB_DIS_OP_MD (1<<2) /* Bit 2: Disab. Operation Mode */ +#define RB_RST_CLR (1<<1) /* Bit 1: Clr RAM Buf STM Reset */ +#define RB_RST_SET (1<<0) /* Bit 0: Set RAM Buf STM Reset */ + + +/* Receive and Transmit MAC FIFO Registers, use MR_ADDR() to address */ +/* RX_MFF_EA 32 bit Receive MAC FIFO End Address */ +/* RX_MFF_WP 32 bit Receive MAC FIFO Write Pointer */ +/* RX_MFF_RP 32 bit Receive MAC FIFO Read Pointer */ +/* RX_MFF_PC 32 bit Receive MAC FIFO Packet Counter*/ +/* RX_MFF_LEV 32 bit Receive MAC FIFO Level */ +/* TX_MFF_EA 32 bit Transmit MAC FIFO End Address */ +/* TX_MFF_WP 32 bit Transmit MAC FIFO Write Pointer*/ +/* TX_MFF_WSP 32 bit Transmit MAC FIFO WR Shadow Pt*/ +/* TX_MFF_RP 32 bit Transmit MAC FIFO Read Pointer */ +/* TX_MFF_PC 32 bit Transmit MAC FIFO Packet Cnt */ +/* TX_MFF_LEV 32 bit Transmit MAC FIFO Level */ + /* Bit 31..6: reserved */ +#define MFF_MSK 0x007fL /* Bit 5..0: MAC FIFO Address/Pointer Bits */ + +/* RX_MFF_CTRL1 16 bit Receive MAC FIFO Control Reg 1 */ + /* Bit 15..14: reserved */ +#define MFF_ENA_RDY_PAT (1<<13) /* Bit 13: Enable Ready Patch */ +#define MFF_DIS_RDY_PAT (1<<12) /* Bit 12: Disable Ready Patch */ +#define MFF_ENA_TIM_PAT (1<<11) /* Bit 11: Enable Timing Patch */ +#define MFF_DIS_TIM_PAT (1<<10) /* Bit 10: Disable Timing Patch */ +#define MFF_ENA_ALM_FUL (1<<9) /* Bit 9: Enable AlmostFull Sign*/ +#define MFF_DIS_ALM_FUL (1<<8) /* Bit 8: Disab. AlmostFull Sign*/ +#define MFF_ENA_PAUSE (1<<7) /* Bit 7: Enable Pause Signaling*/ +#define MFF_DIS_PAUSE (1<<6) /* Bit 6: Disab. Pause Signaling*/ +#define MFF_ENA_FLUSH (1<<5) /* Bit 5: Enable Frame Flushing */ +#define MFF_DIS_FLUSH (1<<4) /* Bit 4: Disab. Frame Flushing */ +#define MFF_ENA_TIST (1<<3) /* Bit 3: Enable Timestamp Gener*/ +#define MFF_DIS_TIST (1<<2) /* Bit 2: Disab. Timestamp Gener*/ +#define MFF_CLR_INTIST (1<<1) /* Bit 1: Clear IRQ No Timestamp*/ +#define MFF_CLR_INSTAT (1<<0) /* Bit 0: Clear IRQ No Status */ + +#define MFF_RX_CTRL_DEF MFF_ENA_TIM_PAT + +/* TX_MFF_CTRL1 16 bit Transmit MAC FIFO Control Reg 1 */ +#define MFF_CLR_PERR (1<<15) /* Bit 15: Clear Parity Error IRQ*/ + /* Bit 14: reserved */ +#define MFF_ENA_PKT_REC (1<<13) /* Bit 13: Enable Packet Recovery*/ +#define MFF_DIS_PKT_REC (1<<12) /* Bit 12: Disable Packet Recov. */ +/* MFF_ENA_TIM_PAT (see RX_MFF_CTRL1)Bit 11: Enable Timing Patch */ +/* MFF_DIS_TIM_PAT (see RX_MFF_CTRL1)Bit 10: Disable Timing Patch */ +/* MFF_ENA_ALM_FUL (see RX_MFF_CTRL1)Bit 9: Enable AlmostFull Sign*/ +/* MFF_DIS_ALM_FUL (see RX_MFF_CTRL1)Bit 8: Disab. AlmostFull Sign*/ +#define MFF_ENA_W4E (1<<7) /* Bit 7: Enable Wait for Empty */ +#define MFF_DIS_W4E (1<<6) /* Bit 6: Disab. Wait for Empty */ +/* MFF_ENA_FLUSH (see RX_MFF_CTRL1)Bit 5: Enable Frame Flushing */ +/* MFF_DIS_FLUSH (see RX_MFF_CTRL1)Bit 4: Disab. Frame Flushing */ +#define MFF_ENA_LOOPB (1<<3) /* Bit 3: Enable Loopback */ +#define MFF_DIS_LOOPB (1<<2) /* Bit 2: Disable Loopback */ +#define MFF_CLR_MAC_RST (1<<1) /* Bit 1: Clear XMAC Reset */ +#define MFF_SET_MAC_RST (1<<0) /* Bit 0: Set XMAC Reset */ + +#define MFF_TX_CTRL_DEF (MFF_ENA_PKT_REC | MFF_ENA_TIM_PAT | MFF_ENA_FLUSH) + +/* RX_MFF_TST2 8 bit Receive MAC FIFO Test Register 2 */ +/* TX_MFF_TST2 8 bit Transmit MAC FIFO Test Register 2 */ + /* Bit 7: reserved */ +#define MFF_WSP_T_ON (1<<6) /* Bit 6: (Tx) Write Shadow Pt TestOn */ +#define MFF_WSP_T_OFF (1<<5) /* Bit 5: (Tx) Write Shadow Pt TstOff */ +#define MFF_WSP_INC (1<<4) /* Bit 4: (Tx) Write Shadow Pt Increm */ +#define MFF_PC_DEC (1<<3) /* Bit 3: Packet Counter Decrem */ +#define MFF_PC_T_ON (1<<2) /* Bit 2: Packet Counter Test On */ +#define MFF_PC_T_OFF (1<<1) /* Bit 1: Packet Counter Tst Off */ +#define MFF_PC_INC (1<<0) /* Bit 0: Packet Counter Increm */ + +/* RX_MFF_TST1 8 bit Receive MAC FIFO Test Register 1 */ +/* TX_MFF_TST1 8 bit Transmit MAC FIFO Test Register 1 */ + /* Bit 7: reserved */ +#define MFF_WP_T_ON (1<<6) /* Bit 6: Write Pointer Test On */ +#define MFF_WP_T_OFF (1<<5) /* Bit 5: Write Pointer Test Off */ +#define MFF_WP_INC (1<<4) /* Bit 4: Write Pointer Increm */ + /* Bit 3: reserved */ +#define MFF_RP_T_ON (1<<2) /* Bit 2: Read Pointer Test On */ +#define MFF_RP_T_OFF (1<<1) /* Bit 1: Read Pointer Test Off */ +#define MFF_RP_DEC (1<<0) /* Bit 0: Read Pointer Decrement */ + +/* RX_MFF_CTRL2 8 bit Receive MAC FIFO Control Reg 2 */ +/* TX_MFF_CTRL2 8 bit Transmit MAC FIFO Control Reg 2 */ + /* Bit 7..4: reserved */ +#define MFF_ENA_OP_MD (1<<3) /* Bit 3: Enable Operation Mode */ +#define MFF_DIS_OP_MD (1<<2) /* Bit 2: Disab. Operation Mode */ +#define MFF_RST_CLR (1<<1) /* Bit 1: Clear MAC FIFO Reset */ +#define MFF_RST_SET (1<<0) /* Bit 0: Set MAC FIFO Reset */ + + +/* Receive, Transmit, and Link LED Counter Registers */ +/* RX_LED_CTRL 8 bit Receive LED Cnt Control Reg */ +/* TX_LED_CTRL 8 bit Transmit LED Cnt Control Reg */ +/* LNK_SYNC_CTRL 8 bit Link Sync Cnt Control Register */ + /* Bit 7..3: reserved */ +#define LED_START (1<<2) /* Bit 2: Start Timer */ +#define LED_STOP (1<<1) /* Bit 1: Stop Timer */ +#define LED_STATE (1<<0) /* Bit 0:(Rx/Tx)LED State, 1=LED on */ +#define LED_CLR_IRQ (1<<0) /* Bit 0:(Lnk) Clear Link IRQ */ + +/* RX_LED_TST 8 bit Receive LED Cnt Test Register */ +/* TX_LED_TST 8 bit Transmit LED Cnt Test Register */ +/* LNK_SYNC_TST 8 bit Link Sync Cnt Test Register */ + /* Bit 7..3: reserved */ +#define LED_T_ON (1<<2) /* Bit 2: LED Counter Testmode On */ +#define LED_T_OFF (1<<1) /* Bit 1: LED Counter Testmode Off */ +#define LED_T_STEP (1<<0) /* Bit 0: LED Counter Step */ + +/* LNK_LED_REG 8 bit Link LED Register */ + /* Bit 7..6: reserved */ +#define LED_BLK_ON (1<<5) /* Bit 5: Link LED Blinking On */ +#define LED_BLK_OFF (1<<4) /* Bit 4: Link LED Blinking Off */ +#define LED_SYNC_ON (1<<3) /* Bit 3: Use Sync Wire to switch LED */ +#define LED_SYNC_OFF (1<<2) /* Bit 2: Disable Sync Wire Input */ +#define LED_ON (1<<1) /* Bit 1: switch LED on */ +#define LED_OFF (1<<0) /* Bit 0: switch LED off */ + + +/* Receive and Transmit Descriptors ******************************************/ + +/* Transmit Descriptor struct */ +typedef struct s_HwTxd { + SK_U32 volatile TxCtrl; /* Transmit Buffer Control Field */ + SK_U32 TxNext ; /* Physical Address Pointer to the next TxD */ + SK_U32 TxAdrLo ; /* Physical Tx Buffer Address lower dword */ + SK_U32 TxAdrHi ; /* Physical Tx Buffer Address upper dword */ + SK_U32 TxStat ; /* Transmit Frame Status Word */ +#ifndef SK_USE_REV_DESC + SK_U16 TxTcpOffs ; /* TCP Checksum Calculation Start Value */ + SK_U16 TxRes1 ; /* 16 bit reserved field */ + SK_U16 TxTcpWp ; /* TCP Checksum Write Position */ + SK_U16 TxTcpSp ; /* TCP Checksum Calculation Start Position */ +#else /* SK_USE_REV_DESC */ + SK_U16 TxRes1 ; /* 16 bit reserved field */ + SK_U16 TxTcpOffs ; /* TCP Checksum Calculation Start Value */ + SK_U16 TxTcpSp ; /* TCP Checksum Calculation Start Position */ + SK_U16 TxTcpWp ; /* TCP Checksum Write Position */ +#endif /* SK_USE_REV_DESC */ + SK_U32 TxRes2; /* 32 bit reserved field */ +} SK_HWTXD; + +/* Receive Descriptor struct */ +typedef struct s_HwRxd { + SK_U32 volatile RxCtrl; /* Receive Buffer Control Field */ + SK_U32 RxNext ; /* Physical Address Pointer to the next TxD */ + SK_U32 RxAdrLo ; /* Physical Receive Buffer Address lower dword*/ + SK_U32 RxAdrHi ; /* Physical Receive Buffer Address upper dword*/ + SK_U32 RxStat ; /* Receive Frame Status Word */ + SK_U32 RxTiSt ; /* Receive Timestamp provided by the XMAC */ +#ifndef SK_USE_REV_DESC + SK_U16 RxTcpSum1 ; /* TCP Checksum 1 */ + SK_U16 RxTcpSum2 ; /* TCP Checksum 2 */ + SK_U16 RxTcpSp1 ; /* TCP Checksum Calculation Start Position 1 */ + SK_U16 RxTcpSp2 ; /* TCP Checksum Calculation Start Position 2 */ +#else /* SK_USE_REV_DESC */ + SK_U16 RxTcpSum2 ; /* TCP Checksum 2 */ + SK_U16 RxTcpSum1 ; /* TCP Checksum 1 */ + SK_U16 RxTcpSp2 ; /* TCP Checksum Calculation Start Position 2 */ + SK_U16 RxTcpSp1 ; /* TCP Checksum Calculation Start Position 1 */ +#endif /* SK_USE_REV_DESC */ +} SK_HWRXD; + +/* + * Drivers which use the reverse descriptor feature (PCI_OUR_REG_2) + * should set the define SK_USE_REV_DESC. + * Structures are 'normaly' not endianess dependent. But in + * this case the SK_U16 fields are bound to bit positions inside the + * descriptor. RxTcpSum1 e.g. must start at bit 0 within the 6.th DWord. + * The bit positions inside a DWord are of course endianess dependent and + * swaps if the DWord is swaped by the hardware. + */ + + +/* Descriptor Bit Definition */ +/* TxCtrl Transmit Buffer Control Field */ +/* RxCtrl Receive Buffer Control Field */ +#define BMU_OWN (1UL<<31) /* Bit 31: OWN bit: 0=host/1=BMU */ +#define BMU_STF (1L<<30) /* Bit 30: Start of Frame ? */ +#define BMU_EOF (1L<<29) /* Bit 29: End of Frame ? */ +#define BMU_IRQ_EOB (1L<<28) /* Bit 28: Req "End of Buff" IRQ */ +#define BMU_IRQ_EOF (1L<<27) /* Bit 27: Req "End of Frame" IRQ*/ +/* TxCtrl specific bits */ +#define BMU_STFWD (1L<<26) /* Bit 26: (Tx) Store&Forward Frame */ +#define BMU_NO_FCS (1L<<25) /* Bit 25: (Tx) disable XMAC FCS gener*/ +#define BMU_SW (1L<<24) /* Bit 24: (Tx) 1 bit res. for SW use */ +/* RxCtrl specific bits */ +#define BMU_DEV_0 (1L<<26) /* Bit 26: (Rx) transfer data to Dev0 */ +#define BMU_STAT_VAL (1L<<25) /* Bit 25: (Rx) RxStat Valid */ +#define BMU_TIST_VAL (1L<<24) /* Bit 24: (Rx) RxTiSt Valid */ + /* Bit 23..16: BMU Check Opcodes */ +#define BMU_CHECK 0x00550000L /* Default BMU check */ +#define BMU_TCP_CHECK 0x00560000L /* Descr with TCP ext */ +#define BMU_BBC 0x0000FFFFL /* Bit 15..0: Buffer Byte Counter */ + +/* TxStat Transmit Frame Status Word */ +/* RxStat Receive Frame Status Word */ +/* + *Note: TxStat is reserved for ASIC loopback mode only + * + * The Bits of the Status words are defined in xmac_ii.h + * (see XMR_FS bits) + */ + +/* other defines *************************************************************/ + +/* + * FlashProm specification + */ +#define MAX_PAGES 0x20000L /* Every byte has a single page */ +#define MAX_FADDR 1 /* 1 byte per page */ +#define SKFDDI_PSZ 8 /* address PROM size */ + +/* macros ********************************************************************/ + +/* + * Receive and Transmit Queues + */ +#define Q_R1 0x0000 /* Receive Queue 1 */ +#define Q_R2 0x0080 /* Receive Queue 2 */ +#define Q_XS1 0x0200 /* Synchronous Transmit Queue 1 */ +#define Q_XA1 0x0280 /* Asynchronous Transmit Queue 1 */ +#define Q_XS2 0x0300 /* Synchronous Transmit Queue 2 */ +#define Q_XA2 0x0380 /* Asynchronous Transmit Queue 2 */ + +/* + * Macro Q_ADDR() + * + * Use this macro to address the Receive and Transmit Queue Registers. + * + * para Queue Queue to address. + * Values: Q_R1, Q_R2, Q_XS1, Q_XA1, Q_XS2, and Q_XA2 + * Offs Queue register offset. + * Values: Q_D, Q_DA_L ... Q_T2, Q_T3 + * + * usage SK_IN32(pAC,Q_ADDR(Q_R2,Q_BC),pVal) + */ +#define Q_ADDR(Queue,Offs) (B8_Q_REGS + (Queue) + (Offs)) + +/* + * Macro RB_ADDR() + * + * Use this macro to address the RAM Buffer Registers. + * + * para Queue Queue to address. + * Values: Q_R1, Q_R2, Q_XS1, Q_XA1, Q_XS2, and Q_XA2 + * Offs Queue register offset. + * Values: RB_START, RB_END ... RB_LEV, RB_CTRL + * + * usage SK_IN32(pAC,RB_ADDR(Q_R2,RB_RP),pVal) + */ +#define RB_ADDR(Queue,Offs) (B16_RAM_REGS + (Queue) + (Offs)) + + +/* + * MAC Related Registers + */ +#define MAC_1 0 /* belongs to the port near the slot */ +#define MAC_2 1 /* belongs to the port far away from the slot */ + +/* + * Macro MR_ADDR() + * + * Use this macro to address a MAC Related Registers in side the ASIC. + * + * para Queue Queue to address. + * Values: TXA_ITI_INI ... TXA_TEST, + * RX_MFF_EA ... RX_LED_TST, + * LNK_SYNC_INI ... LNK_LED_REG, and + * TX_MFF_EA ... TX_LED_TST + * Mac MAC to address. + * Values: MAC_1, MAC_2 + * + * usage SK_IN32(pAC,MR_ADDR(MAC_1,TX_MFF_EA),pVal) + */ +#define MR_ADDR(Mac,Offs) (((Mac) << 7) + (Offs)) + + + +/* + * macros to access the XMAC + * + * XM_IN16(), to read a 16 bit register (e.g. XM_MMU_CMD) + * XM_OUT16(), to write a 16 bit register (e.g. XM_MMU_CMD) + * XM_IN32(), to read a 32 bit register (e.g. XM_TX_EV_CNT) + * XM_OUT32(), to write a 32 bit register (e.g. XM_TX_EV_CNT) + * XM_INADDR(), to read a network address register (e.g. XM_SRC_CHK) + * XM_OUTADDR(), to write a network address register (e.g. XM_SRC_CHK) + * XM_INHASH(), to read the XM_HSM_CHK register + * XM_OUTHASH() to write the XM_HSM_CHK register + * + * para: Mac XMAC to address values: MAC_1 or MAC_2 + * IoC I/O context needed for SK IO macros + * Reg XMAC Register to read or write + * (p)Val Value or pointer to the value which should be read or + * written. + * + * usage: XM_OUT16(IoC, MAC_1, XM_MMU_CMD, Value) ; + */ + +#ifdef SK_LITTLE_ENDIAN +#define XM_WORD_LO 0 +#define XM_WORD_HI 1 +#else /* !SK_LITTLE_ENDIAN */ +#define XM_WORD_LO 1 +#define XM_WORD_HI 0 +#endif /* !SK_LITTLE_ENDIAN */ + +#define XMA(Mac,Reg) (((0x1000 << (Mac)) + 0x1000) | ((Reg) << 1)) + +#define XM_IN16(IoC,Mac,Reg,pVal) SK_IN16((IoC),XMA((Mac),(Reg)),(pVal)) +#define XM_OUT16(IoC,Mac,Reg,Val) SK_OUT16((IoC),XMA((Mac),(Reg)),(Val)) + +#define XM_IN32(IoC,Mac,Reg,pVal) { \ + SK_IN16((IoC),XMA((Mac),(Reg)), \ + (SK_U16 *)&((SK_U16 *)(pVal))[XM_WORD_LO]); \ + SK_IN16((IoC),XMA((Mac),(Reg+2)), \ + (SK_U16 *)&((SK_U16 *)(pVal))[XM_WORD_HI]); \ +} + +#define XM_OUT32(IoC,Mac,Reg,Val) { \ + SK_OUT16((IoC),XMA((Mac),(Reg)), (SK_U16)((Val) & 0x0000ffffL));\ + SK_OUT16((IoC),XMA((Mac),(Reg+2)),(SK_U16)(((Val)>>16) & 0x0000ffffL));\ +} + +/* + * Remember: we are always writing to / reading from LITTLE ENDIAN memory + */ + +#define XM_INADDR(IoC, Mac, Reg, pVal) { \ + SK_U16 Word; \ + SK_U8 *pByte; \ + pByte = (SK_U8 *)&((SK_U8 *)(pVal))[0]; \ + SK_IN16((IoC), XMA((Mac), (Reg)), &Word); \ + pByte[0] = (SK_U8) (Word & 0x00ff); \ + pByte[1] = (SK_U8) ((Word >> 8) & 0x00ff); \ + SK_IN16((IoC), XMA((Mac), (Reg+2)), &Word); \ + pByte[2] = (SK_U8) (Word & 0x00ff); \ + pByte[3] = (SK_U8) ((Word >> 8) & 0x00ff); \ + SK_IN16((IoC), XMA((Mac), (Reg+4)), &Word); \ + pByte[4] = (SK_U8) (Word & 0x00ff); \ + pByte[5] = (SK_U8) ((Word >> 8) & 0x00ff); \ +} + +#define XM_OUTADDR(IoC, Mac, Reg, pVal) { \ + SK_U8 *pByte; \ + pByte = (SK_U8 *)&((SK_U8 *)(pVal))[0]; \ + SK_OUT16((IoC), XMA((Mac), (Reg)), (SK_U16) \ + (((SK_U16)(pByte[0]) & 0x00ff)| \ + (((SK_U16)(pByte[1]) << 8) & 0xff00))); \ + SK_OUT16((IoC), XMA((Mac), (Reg+2)), (SK_U16) \ + (((SK_U16)(pByte[2]) & 0x00ff)| \ + (((SK_U16)(pByte[3]) << 8) & 0xff00))); \ + SK_OUT16((IoC), XMA((Mac), (Reg+4)), (SK_U16) \ + (((SK_U16)(pByte[4]) & 0x00ff)| \ + (((SK_U16)(pByte[5]) << 8) & 0xff00))); \ +} + +#define XM_INHASH(IoC, Mac, Reg, pVal) { \ + SK_U16 Word; \ + SK_U8 *pByte; \ + pByte = (SK_U8 *)&((SK_U8 *)(pVal))[0]; \ + SK_IN16((IoC), XMA((Mac), (Reg)), &Word); \ + pByte[0] = (SK_U8) (Word & 0x00ff); \ + pByte[1] = (SK_U8) ((Word >> 8) & 0x00ff); \ + SK_IN16((IoC), XMA((Mac), (Reg+2)), &Word); \ + pByte[2] = (SK_U8) (Word & 0x00ff); \ + pByte[3] = (SK_U8) ((Word >> 8) & 0x00ff); \ + SK_IN16((IoC), XMA((Mac), (Reg+4)), &Word); \ + pByte[4] = (SK_U8) (Word & 0x00ff); \ + pByte[5] = (SK_U8) ((Word >> 8) & 0x00ff); \ + SK_IN16((IoC), XMA((Mac), (Reg+6)), &Word); \ + pByte[6] = (SK_U8) (Word & 0x00ff); \ + pByte[7] = (SK_U8) ((Word >> 8) & 0x00ff); \ +} + +#define XM_OUTHASH(IoC, Mac, Reg, pVal) { \ + SK_U8 *pByte; \ + pByte = (SK_U8 *)&((SK_U8 *)(pVal))[0]; \ + SK_OUT16((IoC), XMA((Mac), (Reg)), (SK_U16) \ + (((SK_U16)(pByte[0]) & 0x00ff)| \ + (((SK_U16)(pByte[1]) << 8) & 0xff00))); \ + SK_OUT16((IoC), XMA((Mac), (Reg+2)), (SK_U16) \ + (((SK_U16)(pByte[2]) & 0x00ff)| \ + (((SK_U16)(pByte[3]) << 8) & 0xff00))); \ + SK_OUT16((IoC), XMA((Mac), (Reg+4)), (SK_U16) \ + (((SK_U16)(pByte[4]) & 0x00ff)| \ + (((SK_U16)(pByte[5]) << 8) & 0xff00))); \ + SK_OUT16((IoC), XMA((Mac), (Reg+6)), (SK_U16) \ + (((SK_U16)(pByte[6]) & 0x00ff)| \ + (((SK_U16)(pByte[7]) << 8) & 0xff00))); \ +} + +/* + * Different PHY Types + */ +#define SK_PHY_XMAC 0 /* integrated in Xmac II*/ +#define SK_PHY_BCOM 1 /* Broadcom BCM5400 */ +#define SK_PHY_LONE 2 /* Level One LXT1000 */ +#define SK_PHY_NAT 3 /* National DP83891 */ + +/* + * PHY addresses (bits 8..12 of PHY address reg) + */ +#define PHY_ADDR_XMAC (0<<8) +#define PHY_ADDR_BCOM (1<<8) +#define PHY_ADDR_LONE (3<<8) +#define PHY_ADDR_NAT (0<<8) + +/* + * macros to access the PHY + * + * PHY_READ() read a 16 bit value from the PHY + * PHY_WIRTE() write a 16 bit value to the PHY + * + * para: IoC I/O context needed for SK IO macros + * pPort Pointer to port struct for PhyAddr + * Mac XMAC to address values: MAC_1 or MAC_2 + * PhyReg PHY Register to read or write + * (p)Val Value or pointer to the value which should be read or + * written. + * + * usage: PHY_READ(IoC, pPort, MAC_1, PHY_CTRL, Value); + */ +#define PHY_READ(IoC, pPort, Mac, PhyReg, pVal) { \ + SK_U16 Mmu; \ + \ + XM_OUT16((IoC),(Mac), XM_PHY_ADDR, (PhyReg)|(pPort)->PhyAddr); \ + XM_IN16((IoC), (Mac), XM_PHY_DATA, (pVal)); \ + if ((pPort)->PhyType != SK_PHY_XMAC) { \ + do { \ + XM_IN16((IoC), (Mac), XM_MMU_CMD, &Mmu); \ + } while ((Mmu & XM_MMU_PHY_RDY) == 0); \ + XM_IN16((IoC), (Mac), XM_PHY_DATA, (pVal)); \ + } \ +} + +#define PHY_WRITE(IoC, pPort, Mac, PhyReg, Val) { \ + SK_U16 Mmu; \ + \ + if ((pPort)->PhyType != SK_PHY_XMAC) { \ + do { \ + XM_IN16((IoC), (Mac), XM_MMU_CMD, &Mmu); \ + } while ((Mmu & XM_MMU_PHY_BUSY) != 0); \ + } \ + XM_OUT16((IoC), (Mac), XM_PHY_ADDR, (PhyReg)|(pPort)->PhyAddr); \ + XM_OUT16((IoC), (Mac), XM_PHY_DATA, (Val)); \ + if ((pPort)->PhyType != SK_PHY_XMAC) { \ + do { \ + XM_IN16((IoC), (Mac), XM_MMU_CMD, &Mmu); \ + } while ((Mmu & XM_MMU_PHY_BUSY) != 0); \ + } \ +} + +/* + * Macro PCI_C() + * + * Use this macro to address PCI config register from the IO space. + * + * para Addr PCI configuration register to address. + * Values: PCI_VENDOR_ID ... PCI_VPD_ADDR, + * + * usage SK_IN16(pAC,PCI_C(PCI_VENDOR_ID),pVal); + */ +#define PCI_C(Addr) (B7_CFG_SPC + (Addr)) /* PCI Config Space */ + +/* + * Macro SK_ADDR(Base,Addr) + * + * Calculates the effective HW address + * + * para Base IO- or memory base address + * Addr Address offset + * + * usage: May be used in SK_INxx and SK_OUTxx macros + * #define SK_IN8(pAC,Addr,pVal) ...\ + * *pVal = (SK_U8) inp(SK_ADDR(pAC->Hw.Iop,Addr))) + */ +#ifdef SK_MEM_MAPPED_IO +#define SK_HW_ADDR(Base,Addr) ((Base)+(Addr)) +#else /* SK_MEM_MAPPED_IO */ +#define SK_HW_ADDR(Base,Addr) ((Base)+(((Addr)&0x7F)|((Addr)>>7 ? 0x80:0))) +#endif /* SK_MEM_MAPPED_IO */ + +#define SZ_LONG (sizeof(SK_U32)) + +/* + * Macro SK_HWAC_LINK_LED() + * + * Use this macro to set the link LED mode. + * para pAC Pointer to adapter context struct + * IoC I/O context needed for SK IO macros + * Port Port number + * Mode Mode to set for this LED + */ +#define SK_HWAC_LINK_LED(pAC, IoC, Port, Mode) \ +SK_OUT8(IoC, MR_ADDR(Port,LNK_LED_REG), Mode); + + +/* typedefs *******************************************************************/ + + +/* function prototypes ********************************************************/ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __INC_SKGEHW_H */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/h/skgehwt.h linux/drivers/net/sk98lin/h/skgehwt.h --- v2.2.13/linux/drivers/net/sk98lin/h/skgehwt.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/h/skgehwt.h Tue Jan 4 10:12:18 2000 @@ -0,0 +1,71 @@ +/****************************************************************************** + * + * Name: skhwt.h + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.4 $ + * Date: $Date: 1998/08/19 09:50:58 $ + * Purpose: Defines for the hardware timer functions + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: skgehwt.h,v $ + * Revision 1.4 1998/08/19 09:50:58 gklug + * fix: remove struct keyword from c-code (see CCC) add typedefs + * + * Revision 1.3 1998/08/14 07:09:29 gklug + * fix: chg pAc -> pAC + * + * Revision 1.2 1998/08/07 12:54:21 gklug + * fix: first compiled version + * + * Revision 1.1 1998/08/07 09:32:58 gklug + * first version + * + * + * + * + * + ******************************************************************************/ + +/* + * SKGEHWT.H contains all defines and types for the timer functions + */ + +#ifndef _SKGEHWT_H_ +#define _SKGEHWT_H_ + +/* + * SK Hardware Timer + * - needed wherever the HWT module is used + * - use in Adapters context name pAC->Hwt + */ +typedef struct s_Hwt { + SK_U32 TStart ; /* HWT start */ + SK_U32 TStop ; /* HWT stop */ + int TActive ; /* HWT: flag : active/inactive */ +} SK_HWT; + +extern void SkHwtInit(SK_AC *pAC, SK_IOC Ioc); +extern void SkHwtStart(SK_AC *pAC, SK_IOC Ioc, SK_U32 Time); +extern void SkHwtStop(SK_AC *pAC, SK_IOC Ioc); +extern SK_U32 SkHwtRead(SK_AC *pAC,SK_IOC Ioc); +extern void SkHwtIsr(SK_AC *pAC, SK_IOC Ioc); +#endif /* _SKGEHWT_H_ */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/h/skgei2c.h linux/drivers/net/sk98lin/h/skgei2c.h --- v2.2.13/linux/drivers/net/sk98lin/h/skgei2c.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/h/skgei2c.h Tue Jan 4 10:12:18 2000 @@ -0,0 +1,245 @@ +/****************************************************************************** + * + * Name: skgei2c.h + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.16 $ + * Date: $Date: 1999/11/12 08:24:10 $ + * Purpose: Special genesis defines for I2C + * (taken from Monalisa (taken from Concentrator)) + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: skgei2c.h,v $ + * Revision 1.16 1999/11/12 08:24:10 malthoff + * Change voltage warning and error limits + * (warning +-5%, error +-10%). + * + * Revision 1.15 1999/09/14 14:14:43 malthoff + * The 1000BT Dual Link adapter has got only one Fan. + * The second Fan has been removed. + * + * Revision 1.14 1999/05/27 13:40:50 malthoff + * Fan Divisor = 1. Assuming fan with 6500 rpm. + * + * Revision 1.13 1999/05/20 14:56:55 malthoff + * Bug Fix: Missing brace in SK_LM80_FAN_FAKTOR. + * + * Revision 1.12 1999/05/20 09:22:00 cgoos + * Changes for 1000Base-T (Fan sensors). + * + * Revision 1.11 1998/10/14 05:57:22 cgoos + * Fixed compilation warnings. + * + * Revision 1.10 1998/09/04 08:37:00 malthoff + * bugfix: correct the SK_I2C_GET_CTL() macro. + * + * Revision 1.9 1998/08/25 06:10:03 gklug + * add: thresholds for all sensors + * + * Revision 1.8 1998/08/20 11:37:42 gklug + * chg: change Ioc to IoC + * + * Revision 1.7 1998/08/20 08:53:11 gklug + * fix: compiler errors + * add: Threshold values + * + * Revision 1.6 1998/08/17 11:37:09 malthoff + * Bugfix in SK_I2C_CTL macro. The parameter 'dev' + * has to be shifted 9 bits. + * + * Revision 1.5 1998/08/17 06:52:21 malthoff + * Remove unrequired macros. + * Add macros for accessing I2C SW register. + * + * Revision 1.4 1998/08/13 08:30:18 gklug + * add: conversion factors for read values + * add: new state SEN_VALEXT to read extension value of temperature sensor + * + * Revision 1.3 1998/08/12 13:37:56 gklug + * rmv: error numbers and messages + * + * Revision 1.2 1998/08/11 07:54:38 gklug + * add: sensor states for GE sensors + * add: Macro to access I2c hardware register + * chg: Error messages for I2c errors + * + * Revision 1.1 1998/07/17 11:27:56 gklug + * Created. + * + * + * + ******************************************************************************/ + +/* + * SKGEI2C.H contains all SK-98xx specific defines for the I2C handling + */ + +#ifndef _INC_SKGEI2C_H_ +#define _INC_SKGEI2C_H_ + +/* + * Macros to access the B2_I2C_CTRL + */ +#define SK_I2C_CTL(IoC,flag,dev,reg,burst) \ + SK_OUT32(IoC,B2_I2C_CTRL,\ + (flag ? 0x80000000UL : 0x0L ) | \ + (((SK_U32) reg << 16) & I2C_ADDR) | \ + (((SK_U32) dev << 9) & I2C_DEV_SEL) | \ + (( burst << 4) & I2C_BURST_LEN) ) + +#define SK_I2C_STOP(IoC) { \ + SK_U32 I2cCtrl; \ + SK_IN32(IoC, B2_I2C_CTRL, &I2cCtrl); \ + SK_OUT32(IoC, B2_I2C_CTRL, I2cCtrl | I2C_STOP); \ +} + +#define SK_I2C_GET_CTL(Ioc,pI2cCtrl) SK_IN32(Ioc,B2_I2C_CTRL,pI2cCtrl) + +/* + * Macros to access the I2C SW Registers + */ +#define SK_I2C_SET_BIT(IoC, SetBits) { \ + SK_U8 OrgBits; \ + SK_IN8(IoC, B2_I2C_SW, &OrgBits); \ + SK_OUT8(IoC, B2_I2C_SW, OrgBits | (SetBits)); \ +} + +#define SK_I2C_CLR_BIT(IoC,ClrBits) { \ + SK_U8 OrgBits; \ + SK_IN8(IoC, B2_I2C_SW, &OrgBits); \ + SK_OUT8(IoC, B2_I2C_SW, OrgBits & ~(ClrBits)); \ +} + +#define SK_I2C_GET_SW(IoC,pI2cSw) SK_IN8(IoC,B2_I2C_SW,pI2cSw) + +/* + * define the possible sensor states + */ +#define SK_SEN_IDLE 0 /* Idle: sensor not read */ +#define SK_SEN_VALUE 1 /* Value Read cycle */ +#define SK_SEN_VALEXT 2 /* Extended Value Read cycle */ + +/* + * Conversion factor to convert read Voltage sensor to milli Volt + * Conversion factor to convert read Temperature sensor to 10th degree Celsius + */ +#define SK_LM80_VT_LSB 22 /* 22mV LSB resolution */ +#define SK_LM80_TEMP_LSB 10 /* 1 degree LSB resolution */ +#define SK_LM80_TEMPEXT_LSB 5 /* 0.5 degree LSB resolution for the + * extension value + */ +#define SK_LM80_FAN_FAKTOR ((22500L*60)/(1*2)) +/* formula: counter = (22500*60)/(rpm * divisor * pulses/2) + * assuming: 6500rpm, 4 pulses, divisor 1 + */ + +/* + * Define sensor management data + * Maximum is reached on copperfield with dual Broadcom. + * Board specific maximum is in pAC->I2c.MaxSens + */ +#define SK_MAX_SENSORS 8 /* maximal no. of installed sensors */ +#define SK_MIN_SENSORS 5 /* minimal no. of installed sensors */ + +/* + * Defines for the individual Thresholds + */ + +/* Temperature sensor */ +#define SK_SEN_ERRHIGH0 800 /* Temperature High Err Threshold */ +#define SK_SEN_WARNHIGH0 700 /* Temperature High Warn Threshold */ +#define SK_SEN_WARNLOW0 100 /* Temperature Low Err Threshold */ +#define SK_SEN_ERRLOW0 0 /* Temperature Low Warn Threshold */ + +/* VCC which should be 5 V */ +#define SK_SEN_ERRHIGH1 5588 /* Voltage PCI High Err Threshold */ +#define SK_SEN_WARNHIGH1 5346 /* Voltage PCI High Warn Threshold */ +#define SK_SEN_WARNLOW1 4664 /* Voltage PCI Low Err Threshold */ +#define SK_SEN_ERRLOW1 4422 /* Voltage PCI Low Warn Threshold */ + +/* + * VIO may be 5 V or 3.3 V. Initialization takes two parts: + * 1. Initialize lowest lower limit and highest higher limit. + * 2. After the first value is read correct the upper or the lower limit to + * the appropriate C constant. + * + * Warning limits are +-5% of the exepected voltage. + * Error limits are +-10% of the expected voltage. + */ +#define SK_SEN_ERRHIGH2 5588 /* Voltage PCI-IO High Err Threshold */ +#define SK_SEN_WARNHIGH2 5346 /* Voltage PCI-IO High Warn Threshold */ +#define SK_SEN_WARNLOW2 3146 /* Voltage PCI-IO Low Err Threshold */ +#define SK_SEN_ERRLOW2 2970 /* Voltage PCI-IO Low Warn Threshold */ + +/* correction values for the second pass */ +#define SK_SEN_ERRHIGH2C 3630 /* Voltage PCI-IO High Err Threshold */ +#define SK_SEN_WARNHIGH2C 3476 /* Voltage PCI-IO High Warn Threshold */ +#define SK_SEN_WARNLOW2C 4664 /* Voltage PCI-IO Low Err Threshold */ +#define SK_SEN_ERRLOW2C 4422 /* Voltage PCI-IO Low Warn Threshold */ + +/* + * VDD voltage + */ +#define SK_SEN_ERRHIGH3 3630 /* Voltage ASIC High Err Threshold */ +#define SK_SEN_WARNHIGH3 3476 /* Voltage ASIC High Warn Threshold */ +#define SK_SEN_WARNLOW3 3146 /* Voltage ASIC Low Err Threshold */ +#define SK_SEN_ERRLOW3 2970 /* Voltage ASIC Low Warn Threshold */ + +/* + * PLC_3V3 voltage + * PHY_PLL_A_3V3 voltage + */ +#define SK_SEN_ERRHIGH4 3630 /* Voltage PMA High Err Threshold */ +#define SK_SEN_WARNHIGH4 3476 /* Voltage PMA High Warn Threshold */ +#define SK_SEN_WARNLOW4 3146 /* Voltage PMA Low Err Threshold */ +#define SK_SEN_ERRLOW4 2970 /* Voltage PMA Low Warn Threshold */ + +/* + * PHY_2V5 voltage + */ +#define SK_SEN_ERRHIGH5 2750 /* Voltage PHY High Err Threshold */ +#define SK_SEN_WARNHIGH5 2640 /* Voltage PHY High Warn Threshold */ +#define SK_SEN_WARNLOW5 2376 /* Voltage PHY Low Err Threshold */ +#define SK_SEN_ERRLOW5 2222 /* Voltage PHY Low Warn Threshold */ + +/* + * PHY_PLL_B_3V3 voltage + */ +#define SK_SEN_ERRHIGH6 3630 /* Voltage PMA High Err Threshold */ +#define SK_SEN_WARNHIGH6 3476 /* Voltage PMA High Warn Threshold */ +#define SK_SEN_WARNLOW6 3146 /* Voltage PMA Low Err Threshold */ +#define SK_SEN_ERRLOW6 2970 /* Voltage PMA Low Warn Threshold */ + +/* + * FAN 1 speed + */ +/* assuming: 6500rpm +-15%, 4 pulses, + * warning at: 80 % + * error at: 70 % + * no upper limit + */ +#define SK_SEN_ERRHIGH 20000 /* FAN Speed High Err Threshold */ +#define SK_SEN_WARNHIGH 20000 /* FAN Speed High Warn Threshold */ +#define SK_SEN_WARNLOW 5200 /* FAN Speed Low Err Threshold */ +#define SK_SEN_ERRLOW 4550 /* FAN Speed Low Warn Threshold */ + +extern int SkLm80ReadSensor(SK_AC *pAC, SK_IOC IoC, SK_SENSOR *pSen); +#endif /* n_INC_SKGEI2C_H */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/h/skgeinit.h linux/drivers/net/sk98lin/h/skgeinit.h --- v2.2.13/linux/drivers/net/sk98lin/h/skgeinit.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/h/skgeinit.h Tue Jan 4 10:12:18 2000 @@ -0,0 +1,733 @@ +/****************************************************************************** + * + * Name: skgeinit.h + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.44 $ + * Date: $Date: 1999/10/26 07:34:15 $ + * Purpose: Structures and prototypes for the GE Init Module + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: skgeinit.h,v $ + * Revision 1.44 1999/10/26 07:34:15 malthoff + * The define SK_LNK_ON has been lost in v1.41. + * + * Revision 1.43 1999/10/06 09:30:16 cgoos + * Changed SK_XM_THR_JUMBO. + * + * Revision 1.42 1999/09/16 12:58:26 cgoos + * Changed SK_LED_STANDY macro to be independent of HW link sync. + * + * Revision 1.41 1999/07/30 06:56:14 malthoff + * Correct comment for SK_MS_STAT_UNSET. + * + * Revision 1.40 1999/05/27 13:38:46 cgoos + * Added SK_BMU_TX_WM. + * Made SK_BMU_TX_WM and SK_BMU_RX_WM user-definable. + * Changed XMAC Tx treshold to max. values. + * + * Revision 1.39 1999/05/20 14:35:26 malthoff + * Remove prototypes for SkGeLinkLED(). + * + * Revision 1.38 1999/05/19 11:59:12 cgoos + * Added SK_MS_CAP_INDETERMINATED define. + * + * Revision 1.37 1999/05/19 07:32:33 cgoos + * Changes for 1000Base-T. + * LED-defines for HWAC_LINK_LED macro. + * + * Revision 1.36 1999/04/08 14:00:24 gklug + * add:Port struct field PLinkResCt + * + * Revision 1.35 1999/03/25 07:43:07 malthoff + * Add error string for SKERR_HWI_E018MSG. + * + * Revision 1.34 1999/03/12 16:25:57 malthoff + * Remove PPollRxD and PPollTxD. + * Add SKERR_HWI_E017MSG. and SK_DPOLL_MAX. + * + * Revision 1.33 1999/03/12 13:34:41 malthoff + * Add Autonegotiation error codes. + * Change defines for parameter Mode in SkXmSetRxCmd(). + * Replace __STDC__ by SK_KR_PROTO. + * + * Revision 1.32 1999/01/25 14:40:20 mhaveman + * Added new return states for the virtual management port if multiple + * ports are active but differently configured. + * + * Revision 1.31 1998/12/11 15:17:02 gklug + * add: Link partnet autoneg states : Unknown Manual and Autonegotiation + * + * Revision 1.30 1998/12/07 12:17:04 gklug + * add: Link Partner autonegotiation flag + * + * Revision 1.29 1998/12/01 10:54:42 gklug + * add: variables for XMAC Errata + * + * Revision 1.28 1998/12/01 10:14:15 gklug + * add: PIsave saves the Interrupt status word + * + * Revision 1.27 1998/11/26 15:24:52 mhaveman + * Added link status states SK_LMODE_STAT_AUTOHALF and + * SK_LMODE_STAT_AUTOFULL which are used by PNMI. + * + * Revision 1.26 1998/11/26 14:53:01 gklug + * add:autoNeg Timeout variable + * + * Revision 1.25 1998/11/26 08:58:50 gklug + * add: Link Mode configuration (AUTO Sense mode) + * + * Revision 1.24 1998/11/24 13:30:27 gklug + * add: PCheckPar to port struct + * + * Revision 1.23 1998/11/18 13:23:26 malthoff + * Add SK_PKT_TO_MAX. + * + * Revision 1.22 1998/11/18 13:19:54 gklug + * add: PPrevShorts and PLinkBroken to port struct for WA XMAC Errata #C1 + * + * Revision 1.21 1998/10/26 08:02:57 malthoff + * Add GIRamOffs. + * + * Revision 1.20 1998/10/19 07:28:37 malthoff + * Add prototyp for SkGeInitRamIface(). + * + * Revision 1.19 1998/10/14 14:47:48 malthoff + * SK_TIMER should not be defined for Diagnostics. + * Add SKERR_HWI_E015MSG and SKERR_HWI_E016MSG. + * + * Revision 1.18 1998/10/14 14:00:03 gklug + * add: timer to port struct for workaround of Errata #2 + * + * Revision 1.17 1998/10/14 11:23:09 malthoff + * Add prototype for SkXmAutoNegDone(). + * Fix SkXmSetRxCmd() prototype statement. + * + * Revision 1.16 1998/10/14 05:42:29 gklug + * add: HWLinkUp flag to Port struct + * + * Revision 1.15 1998/10/09 08:26:33 malthoff + * Rename SK_RB_ULPP_B to SK_RB_LLPP_B. + * + * Revision 1.14 1998/10/09 07:11:13 malthoff + * bug fix: SK_FACT_53 is 85 not 117. + * Rework time out init values. + * Add GIPortUsage and corresponding defines. + * Add some error log messages. + * + * Revision 1.13 1998/10/06 14:13:14 malthoff + * Add prototyp for SkGeLoadLnkSyncCnt(). + * + * Revision 1.12 1998/10/05 11:29:53 malthoff + * bug fix: A comment was not closed. + * + * Revision 1.11 1998/10/05 08:01:59 malthoff + * Add default Timeout- Threshold- and + * Watermark constants. Add QRam start and end + * variables. Also add vars to store the polling + * mode and receive command. Add new Error Log + * Messages and function prototypes. + * + * Revision 1.10 1998/09/28 13:34:48 malthoff + * Add mode bits for LED functions. + * Move Autoneg and Flow Ctrl bits from shgesirq.h + * Add the required Error Log Entries + * and Function Prototypes. + * + * Revision 1.9 1998/09/16 14:38:41 malthoff + * Rework the SK_LNK_xxx defines. + * Add error log message defines. + * Add prototypes for skxmac2.c + * + * Revision 1.8 1998/09/11 05:29:18 gklug + * add: init state of a port + * + * Revision 1.7 1998/09/08 08:35:52 gklug + * add: defines of the Init Levels + * + * Revision 1.6 1998/09/03 13:48:42 gklug + * add: Link strati, capabilities to Port struct + * + * Revision 1.5 1998/09/03 13:30:59 malthoff + * Add SK_LNK_BLINK and SK_LNK_PERM. + * + * Revision 1.4 1998/09/03 09:55:31 malthoff + * Add constants for parameters Dir and RstMode + * when calling SkGeStopPort(). + * Rework the prototyp section. + * Add Queue Address offsets PRxQOff, PXsQOff, and PXaQOff. + * Remove Ioc with IoC. + * + * Revision 1.3 1998/08/19 09:11:54 gklug + * fix: struct are removed from c-source (see CCC) + * add: typedefs for all structs + * + * Revision 1.2 1998/07/28 12:38:26 malthoff + * The prototypes got the parameter 'IoC'. + * + * Revision 1.1 1998/07/23 09:50:24 malthoff + * Created. + * + * + ******************************************************************************/ + +#ifndef __INC_SKGEINIT_H_ +#define __INC_SKGEINIT_H_ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* defines ********************************************************************/ + +/* + * defines for modifying Link LED behaviour (has been used with SkGeLinkLED()) + */ +#define SK_LNK_OFF LED_OFF +#define SK_LNK_ON (LED_ON | LED_BLK_OFF| LED_SYNC_OFF) +#define SK_LNK_BLINK (LED_ON | LED_BLK_ON | LED_SYNC_ON) +#define SK_LNK_PERM (LED_ON | LED_BLK_OFF| LED_SYNC_ON) +#define SK_LNK_TST (LED_ON | LED_BLK_ON | LED_SYNC_OFF) + +/* + * defines for parameter 'Mode' when calling SK_HWAC_LINK_LED() + */ +#define SK_LED_OFF LED_OFF +#define SK_LED_ACTIVE (LED_ON | LED_BLK_OFF| LED_SYNC_OFF) +#define SK_LED_STANDBY (LED_ON | LED_BLK_ON| LED_SYNC_OFF) + +/* + * defines for parameter 'Mode' when calling SkGeXmitLED() + */ +#define SK_LED_DIS 0 +#define SK_LED_ENA 1 +#define SK_LED_TST 2 + +/* + * Counter and Timer constants, for a host clock of 62.5 MHz + */ +#define SK_XMIT_DUR 0x002faf08L /* 50 ms */ +#define SK_BLK_DUR 0x01dcd650L /* 500 ms */ + +#define SK_DPOLL_DEF 0x00EE6B28L /* 250 ms */ +#define SK_DPOLL_MAX 0x00FFFFFFL /* ca. 268ms */ + +#define SK_FACT_62 100 /* is given in percent */ +#define SK_FACT_53 85 + +/* + * Timeout values + */ +#define SK_MAC_TO_53 72 /* MAC arbiter timeout */ +#define SK_PKT_TO_53 0x2000 /* Packet arbiter timeout */ +#define SK_PKT_TO_MAX 0xffff /* Maximum value */ +#define SK_RI_TO_53 36 /* RAM interface timeout */ + +/* + * RAM Buffer High Pause Threshold values + */ +#define SK_RB_ULPP ( 8 * 1024) /* Upper Level in kB/8 */ +#define SK_RB_LLPP_S (10 * 1024) /* Lower Level for small Queues */ +#define SK_RB_LLPP_B (16 * 1024) /* Lower Level for big Queues */ + +#ifndef SK_BMU_RX_WM +#define SK_BMU_RX_WM 0x600 /* BMU Rx Watermark */ +#endif +#ifndef SK_BMU_TX_WM +#define SK_BMU_TX_WM 0x600 /* BMU Rx Watermark */ +#endif + +/* XMAC II Tx Threshold */ +#define SK_XM_THR_REDL 0x01fb /* .. for redundant link usage */ +#define SK_XM_THR_SL 0x01fb /* .. for single link adapters */ +#define SK_XM_THR_MULL 0x01fb /* .. for multiple link usage */ +#define SK_XM_THR_JUMBO 0x03fc /* .. for jumbo frame usage */ + +/* values for GIPortUsage */ +#define SK_RED_LINK 1 /* redundant link usage */ +#define SK_MUL_LINK 2 /* multiple link usage */ +#define SK_JUMBO_LINK 3 /* driver uses jumbo frames */ + +/* Minimum RAM Buffer Receive Queue Size */ +#define SK_MIN_RXQ_SIZE 16 /* 16 kB */ +/* + * defines for parameter 'Dir' when calling SkGeStopPort() + */ +#define SK_STOP_TX 1 /* Stops the transmit path, resets the XMAC */ +#define SK_STOP_RX 2 /* Stops the receive path */ +#define SK_STOP_ALL 3 /* Stops rx and tx path, resets the XMAC */ + +/* + * defines for parameter 'RstMode' when calling SkGeStopPort() + */ +#define SK_SOFT_RST 1 /* perform a software reset */ +#define SK_HARD_RST 2 /* perform a hardware reset */ + +/* + * Define Init Levels + */ +#define SK_INIT_DATA 0 /* Init level 0: init data structures */ +#define SK_INIT_IO 1 /* Init level 1: init with IOs */ +#define SK_INIT_RUN 2 /* Init level 2: init for run time */ + +/* + * Set Link Mode Parameter + */ +#define SK_LMODE_HALF 1 /* Half Duplex Mode */ +#define SK_LMODE_FULL 2 /* Full Duplex Mode */ +#define SK_LMODE_AUTOHALF 3 /* AutoHalf Duplex Mode */ +#define SK_LMODE_AUTOFULL 4 /* AutoFull Duplex Mode */ +#define SK_LMODE_AUTOBOTH 5 /* AutoBoth Duplex Mode */ +#define SK_LMODE_AUTOSENSE 6 /* configured mode auto sensing */ +#define SK_LMODE_INDETERMINATED 7 /* Return value for virtual port if + * multiple ports are differently + * configured. + */ + +/* + * Autonegotiation timeout in 100ms granularity. + */ +#define SK_AND_MAX_TO 6 /* Wait 600 msec before link comes up */ + +/* + * Define Autonegotiation error codes here + */ +#define SK_AND_OK 0 /* no error */ +#define SK_AND_OTHER 1 /* other error than below */ +#define SK_AND_DUP_CAP 2 /* Duplex capabilities error */ + +/* + * Link Capability value + */ +#define SK_LMODE_CAP_HALF (1<<0) /* Half Duplex Mode */ +#define SK_LMODE_CAP_FULL (1<<1) /* Full Duplex Mode */ +#define SK_LMODE_CAP_AUTOHALF (1<<2) /* AutoHalf Duplex Mode */ +#define SK_LMODE_CAP_AUTOFULL (1<<3) /* AutoFull Duplex Mode */ +#define SK_LMODE_CAP_INDETERMINATED (1<<4) /* Return value for virtual port if + * multiple ports are differently + * configured. + */ + +/* + * Link mode current state + */ +#define SK_LMODE_STAT_UNKNOWN 1 /* Unknown Duplex Mode */ +#define SK_LMODE_STAT_HALF 2 /* Half Duplex Mode */ +#define SK_LMODE_STAT_FULL 3 /* Full Duplex Mode */ +#define SK_LMODE_STAT_AUTOHALF 4 /* Half Duplex Mode obtained by AutoNeg */ +#define SK_LMODE_STAT_AUTOFULL 5 /* Half Duplex Mode obtained by AutoNeg */ +#define SK_LMODE_STAT_INDETERMINATED 6 /* Return value for virtual port if + * multiple ports are differently + * configured. + */ +/* + * Set Flow Control Mode Parameter (and capabilities) + */ +#define SK_FLOW_MODE_NONE 1 /* No Flow Control */ +#define SK_FLOW_MODE_LOC_SEND 2 /* Local station sends PAUSE */ +#define SK_FLOW_MODE_SYMMETRIC 3 /* Both station may send PAUSE */ +#define SK_FLOW_MODE_SYM_OR_REM 4 /* Both station may send PAUSE or + * just the remote station may send + * PAUSE + */ +#define SK_FLOW_MODE_INDETERMINATED 5 /* Return value for virtual port if + * multiple ports are differently + * configured. + */ + +/* + * Flow Control Status Parameter + */ +#define SK_FLOW_STAT_NONE 1 /* No Flow Control */ +#define SK_FLOW_STAT_REM_SEND 2 /* Remote Station sends PAUSE */ +#define SK_FLOW_STAT_LOC_SEND 3 /* Local station sends PAUSE */ +#define SK_FLOW_STAT_SYMMETRIC 4 /* Both station may send PAUSE */ +#define SK_FLOW_STAT_INDETERMINATED 5 /* Return value for virtual port if + * multiple ports are differently + * configured. + */ +/* + * Master/Slave Mode capabilities + */ +#define SK_MS_CAP_AUTO (1<<0) /* Automatic resolution */ +#define SK_MS_CAP_MASTER (1<<1) /* This station is master */ +#define SK_MS_CAP_SLAVE (1<<2) /* This station is slave */ +#define SK_MS_CAP_INDETERMINATED (1<<3) /* Return value for virtual port if + * multiple ports are differently + * configured. + */ + +/* + * Set Master/Slave Mode Parameter (and capabilities) + */ +#define SK_MS_MODE_AUTO 1 /* Automatic resolution */ +#define SK_MS_MODE_MASTER 2 /* This station is master */ +#define SK_MS_MODE_SLAVE 3 /* This station is slave */ +#define SK_MS_MODE_INDETERMINATED 4 /* Return value for virtual port if + * multiple ports are differently + */ + +/* + * Master/Slave Status Parameter + */ +#define SK_MS_STAT_UNSET 1 /* The MS status is never been determ*/ +#define SK_MS_STAT_MASTER 2 /* This station is master */ +#define SK_MS_STAT_SLAVE 3 /* This station is slave */ +#define SK_MS_STAT_FAULT 4 /* MS resolution failed */ +#define SK_MS_STAT_INDETERMINATED 5 /* Return value for virtual port if + * multiple ports are differently + */ + +/* + * defines for parameter 'Mode' when calling SkXmSetRxCmd() + */ +#define SK_STRIP_FCS_ON (1<<0) /* Enable FCS stripping of rx frames */ +#define SK_STRIP_FCS_OFF (1<<1) /* Disable FCS stripping of rx frames */ +#define SK_STRIP_PAD_ON (1<<2) /* Enable pad byte stripping of rx f */ +#define SK_STRIP_PAD_OFF (1<<3) /* Disable pad byte stripping of rx f */ +#define SK_LENERR_OK_ON (1<<4) /* Don't chk fr for in range len error*/ +#define SK_LENERR_OK_OFF (1<<5) /* Check frames for in range len error*/ +#define SK_BIG_PK_OK_ON (1<<6) /* Don't set rcvError bit for big fr */ +#define SK_BIG_PK_OK_OFF (1<<7) /* Set rcvError bit for big frames */ + +/* + * States of PState + */ +#define SK_PRT_RESET 0 /* the port is reset */ +#define SK_PRT_STOP 1 /* the port is stopped (similar to sw reset) */ +#define SK_PRT_INIT 2 /* the port is initialized */ +#define SK_PRT_RUN 3 /* the port has an active link */ + +/* + * Default receive frame limit for Workaround of XMAC Errata + */ +#define SK_DEF_RX_WA_LIM SK_CONSTU64(100) + +/* + * Define link partner Status + */ +#define SK_LIPA_UNKNOWN 0 /* Link partner is in unknown state */ +#define SK_LIPA_MANUAL 1 /* Link partner is in detected manual state */ +#define SK_LIPA_AUTO 2 /* Link partner is in autonegotiation state */ + +/* + * Define Maximum Restarts before restart is ignored (3com WA) + */ +#define SK_MAX_LRESTART 3 /* Max. 3 times the link is restarted */ + +/* structures *****************************************************************/ + +/* + * Port Structure + */ +typedef struct s_GePort { +#ifndef SK_DIAG + SK_TIMER PWaTimer; /* Workaround Timer */ +#endif + SK_U64 PPrevShorts; /* Previous short Counter checking */ + SK_U64 PPrevRx; /* Previous RxOk Counter checking */ + SK_U64 PPrevFcs; /* Previous FCS Error Counter checking */ + SK_U64 PRxLim; /* Previous RxOk Counter checking */ + int PLinkResCt; /* Link Restart Counter */ + int PAutoNegTimeOut;/* AutoNegotiation timeout current value */ + int PRxQSize; /* Port Rx Queue Size in kB */ + int PXSQSize; /* Port Synchronous Transmit Queue Size in kB */ + int PXAQSize; /* Port Asynchronous Transmit Queue Size in kB*/ + SK_U32 PRxQRamStart; /* Receive Queue RAM Buffer Start Address */ + SK_U32 PRxQRamEnd; /* Receive Queue RAM Buffer End Address */ + SK_U32 PXsQRamStart; /* Sync Tx Queue RAM Buffer Start Address */ + SK_U32 PXsQRamEnd; /* Sync Tx Queue RAM Buffer End Address */ + SK_U32 PXaQRamStart; /* Async Tx Queue RAM Buffer Start Address */ + SK_U32 PXaQRamEnd; /* Async Tx Queue RAM Buffer End Address */ + int PRxQOff; /* Rx Queue Address Offset */ + int PXsQOff; /* Synchronous Tx Queue Address Offset */ + int PXaQOff; /* Asynchronous Tx Queue Address Offset */ + SK_U16 PRxCmd; /* Port Receive Command Configuration Value */ + SK_U16 PIsave; /* Saved Interrupt status word */ + SK_U16 PSsave; /* Saved PHY status word */ + SK_BOOL PHWLinkUp; /* The hardware Link is up (wireing) */ + SK_BOOL PState; /* Is port initialized ? */ + SK_BOOL PLinkBroken; /* Is Link broken ? */ + SK_BOOL PCheckPar; /* Do we check for parity errors ? */ + SK_U8 PLinkCap; /* Link Capabilities */ + SK_U8 PLinkModeConf; /* Link Mode configured */ + SK_U8 PLinkMode; /* Link Mode currently used */ + SK_U8 PLinkModeStatus; /* Link Mode Status */ + SK_U8 PFlowCtrlCap; /* Flow Control Capabilities */ + SK_U8 PFlowCtrlMode; /* Flow Control Mode */ + SK_U8 PFlowCtrlStatus; /* Flow Control Status */ + SK_U8 PMSCap; /* Master/Slave Capabilities */ + SK_U8 PMSMode; /* Master/Slave Mode */ + SK_U8 PMSStatus; /* Master/Slave Status */ + SK_U8 PAutoNegFail; /* Autonegotiation fail flag */ + SK_U8 PLipaAutoNeg; /* Autonegotiation possible with Link Partner */ + int PhyType; /* PHY used on this port */ + SK_U16 PhyAddr; /* MDIO/MDC PHY address */ +} SK_GEPORT; + +/* + * Gigabit Ethernet Initalization Struct + * (has to be included in the adapter context + */ +typedef struct s_GeInit { + int GIMacsFound; /* Number of MACs found on this adapter */ + int GIPciHwRev; /* PCI HW Revision Number */ + SK_U32 GIRamOffs; /* RAM Address Offset for addr calculation */ + int GIRamSize; /* The RAM size of the adapter in kB */ + int GIHstClkFact; /* Host Clock Factor (62.5 / HstClk * 100) */ + int GIPortUsage; /* driver port usage: SK_RED_LINK/SK_MUL_LINK */ + SK_U32 GIPollTimerVal; /* Descriptor Poll Timer Init Val in clk ticks*/ + int GILevel; /* Initialization Level Completed */ + SK_BOOL GIAnyPortAct; /* Is True if one or more port is initialized */ + SK_GEPORT GP[SK_MAX_MACS]; /* Port Dependent Information */ +} SK_GEINIT; + +/* + * Define the error numbers and messages for xmac_ii.c and skgeinit.c + */ +#define SKERR_HWI_E001 (SK_ERRBASE_HWINIT) +#define SKERR_HWI_E001MSG "SkXmClrExactAddr() has got illegal parameters" +#define SKERR_HWI_E002 (SKERR_HWI_E001+1) +#define SKERR_HWI_E002MSG "SkGeInit() Level 1 call missing" +#define SKERR_HWI_E003 (SKERR_HWI_E002+1) +#define SKERR_HWI_E003MSG "SkGeInit() called with illegal init Level" +#define SKERR_HWI_E004 (SKERR_HWI_E003+1) +#define SKERR_HWI_E004MSG "SkGeInitPort() Queue size illegal configured" +#define SKERR_HWI_E005 (SKERR_HWI_E004+1) +#define SKERR_HWI_E005MSG "SkGeInitPort() cannot init running ports" +#define SKERR_HWI_E006 (SKERR_HWI_E005+1) +#define SKERR_HWI_E006MSG "SkGeXmInit(): PState does not match HW state" +#define SKERR_HWI_E007 (SKERR_HWI_E006+1) +#define SKERR_HWI_E007MSG "SkXmInitDupMd() called with invalid Dup Mode" +#define SKERR_HWI_E008 (SKERR_HWI_E007+1) +#define SKERR_HWI_E008MSG "SkXmSetRxCmd() called with invalid Mode" +#define SKERR_HWI_E009 (SKERR_HWI_E008+1) +#define SKERR_HWI_E009MSG "SkGeCfgSync() called although PXSQSize zero" +#define SKERR_HWI_E010 (SKERR_HWI_E009+1) +#define SKERR_HWI_E010MSG "SkGeCfgSync() called with invalid parameters" +#define SKERR_HWI_E011 (SKERR_HWI_E010+1) +#define SKERR_HWI_E011MSG "SkGeInitPort() Receive Queue Size to small" +#define SKERR_HWI_E012 (SKERR_HWI_E011+1) +#define SKERR_HWI_E012MSG "SkGeInitPort() invalid Queue Size specified" +#define SKERR_HWI_E013 (SKERR_HWI_E012+1) +#define SKERR_HWI_E013MSG "SkGeInitPort() cfg changed for running queue" +#define SKERR_HWI_E014 (SKERR_HWI_E013+1) +#define SKERR_HWI_E014MSG "SkGeInitPort() unknown GIPortUsage specified" +#define SKERR_HWI_E015 (SKERR_HWI_E014+1) +#define SKERR_HWI_E015MSG "Illegal Link mode parameter" +#define SKERR_HWI_E016 (SKERR_HWI_E015+1) +#define SKERR_HWI_E016MSG "Illegal Flow control mode parameter" +#define SKERR_HWI_E017 (SKERR_HWI_E016+1) +#define SKERR_HWI_E017MSG "Illegal value specified for GIPollTimerVal" +#define SKERR_HWI_E018 (SKERR_HWI_E017+1) +#define SKERR_HWI_E018MSG "FATAL: SkGeStopPort() does not terminate" +#define SKERR_HWI_E019 (SKERR_HWI_E018+1) +#define SKERR_HWI_E019MSG "" + +/* function prototypes ********************************************************/ + +#ifndef SK_KR_PROTO + +/* + * public functions in skgeinit.c + */ +extern void SkGePollRxD( + SK_AC *pAC, + SK_IOC IoC, + int Port, + SK_BOOL PollRxD); + +extern void SkGePollTxD( + SK_AC *pAC, + SK_IOC IoC, + int Port, + SK_BOOL PollTxD); + +extern void SkGeYellowLED( + SK_AC *pAC, + SK_IOC IoC, + int State); + +extern int SkGeCfgSync( + SK_AC *pAC, + SK_IOC IoC, + int Port, + SK_U32 IntTime, + SK_U32 LimCount, + int SyncMode); + +extern void SkGeLoadLnkSyncCnt( + SK_AC *pAC, + SK_IOC IoC, + int Port, + SK_U32 CntVal); + +extern void SkGeStopPort( + SK_AC *pAC, + SK_IOC IoC, + int Port, + int Dir, + int RstMode); + +extern int SkGeInit( + SK_AC *pAC, + SK_IOC IoC, + int Level); + +extern void SkGeDeInit( + SK_AC *pAC, + SK_IOC IoC); + +extern int SkGeInitPort( + SK_AC *pAC, + SK_IOC IoC, + int Port); + +extern void SkGeXmitLED( + SK_AC *pAC, + SK_IOC IoC, + int Led, + int Mode); + +extern void SkGeInitRamIface( + SK_AC *pAC, + SK_IOC IoC); + +/* + * public functions in skxmac2.c + */ +extern void SkXmSetRxCmd( + SK_AC *pAC, + SK_IOC IoC, + int Port, + int Mode); + +extern void SkXmClrExactAddr( + SK_AC *pAC, + SK_IOC IoC, + int Port, + int StartNum, + int StopNum); + +extern void SkXmFlushTxFifo( + SK_AC *pAC, + SK_IOC IoC, + int Port); + +extern void SkXmFlushRxFifo( + SK_AC *pAC, + SK_IOC IoC, + int Port); + +extern void SkXmSoftRst( + SK_AC *pAC, + SK_IOC IoC, + int Port); + +extern void SkXmHardRst( + SK_AC *pAC, + SK_IOC IoC, + int Port); + +extern void SkXmInitMac( + SK_AC *pAC, + SK_IOC IoC, + int Port); + +extern void SkXmInitDupMd( + SK_AC *pAC, + SK_IOC IoC, + int Port); + +extern void SkXmInitPauseMd( + SK_AC *pAC, + SK_IOC IoC, + int Port); + +extern int SkXmAutoNegDone( + SK_AC *pAC, + SK_IOC IoC, + int Port); + +extern void SkXmAutoNegLipaXmac( + SK_AC *pAC, + SK_IOC IoC, + int Port, + SK_U16 IStatus); + +extern void SkXmAutoNegLipaBcom( + SK_AC *pAC, + SK_IOC IoC, + int Port, + SK_U16 IStatus); + +extern void SkXmAutoNegLipaLone( + SK_AC *pAC, + SK_IOC IoC, + int Port, + SK_U16 IStatus); + +extern void SkXmIrq( + SK_AC *pAC, + SK_IOC IoC, + int Port, + SK_U16 IStatus); + +#else /* SK_KR_PROTO */ + +/* + * public functions in skgeinit.c + */ +extern void SkGePollRxD(); +extern void SkGePollTxD(); +extern void SkGeYellowLED(); +extern int SkGeCfgSync(); +extern void SkGeLoadLnkSyncCnt(); +extern void SkGeStopPort(); +extern int SkGeInit(); +extern void SkGeDeInit(); +extern int SkGeInitPort(); +extern void SkGeXmitLED(); +extern void SkGeInitRamIface(); + +/* + * public functions in skxmac2.c + */ +extern void SkXmSetRxCmd(); +extern void SkXmClrExactAddr(); +extern void SkXmFlushTxFifo(); +extern void SkXmFlushRxFifo(); +extern void SkXmSoftRst(); +extern void SkXmHardRst(); +extern void SkXmInitMac(); +extern void SkXmInitDupMd(); +extern void SkXmInitPauseMd(); +extern int SkXmAutoNegDone(); +extern void SkXmAutoNegLipa(); +extern void SkXmIrq(); + +#endif /* SK_KR_PROTO */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __INC_SKGEINIT_H_ */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/h/skgepnm2.h linux/drivers/net/sk98lin/h/skgepnm2.h --- v2.2.13/linux/drivers/net/sk98lin/h/skgepnm2.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/h/skgepnm2.h Tue Jan 4 10:12:18 2000 @@ -0,0 +1,417 @@ +/***************************************************************************** + * + * Name: skgepnm2.h + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.24 $ + * Date: $Date: 1999/04/13 15:11:11 $ + * Purpose: Defines for Private Network Management Interface + * + ****************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/***************************************************************************** + * + * History: + * + * $Log: skgepnm2.h,v $ + * Revision 1.24 1999/04/13 15:11:11 mhaveman + * Changed copyright. + * + * Revision 1.23 1999/01/28 15:07:12 mhaveman + * Changed default threshold for port switches per hour from 10 + * to 240 which means 4 switches per minute. This fits better + * the granularity of 32 for the port switch estimate + * counter. + * + * Revision 1.22 1999/01/05 12:52:30 mhaveman + * Removed macro SK_PNMI_MICRO_SEC. + * + * Revision 1.21 1999/01/05 12:50:34 mhaveman + * Enlarged macro definition SK_PNMI_HUNDREDS_SEC() so that no 64-bit + * arithmetic is necessary if SK_TICKS_PER_SEC is 100. + * + * Revision 1.20 1998/12/09 14:02:53 mhaveman + * Defined macro SK_PNMI_DEF_RLMT_CHG_THRES for default port switch + * threshold. + * + * Revision 1.19 1998/12/03 11:28:41 mhaveman + * Removed SK_PNMI_CHECKPTR macro. + * + * Revision 1.18 1998/12/03 11:21:00 mhaveman + * -Added pointer check macro SK_PNMI_CHECKPTR + * -Added macros SK_PNMI_VPD_ARR_SIZE and SK_PNMI_VPD_STR_SIZE for + * VPD key evaluation. + * + * Revision 1.17 1998/11/20 13:20:33 mhaveman + * Fixed bug in SK_PNMI_SET_STAT macro. ErrorStatus was not correctly set. + * + * Revision 1.16 1998/11/20 08:08:49 mhaveman + * Macro SK_PNMI_CHECKFLAGS has got a if clause. + * + * Revision 1.15 1998/11/03 13:53:40 mhaveman + * Fixed alignment problem in macor SK_PNMI_SET_STAT macro. + * + * Revision 1.14 1998/10/30 15:50:13 mhaveman + * Added macro SK_PNMI_MICRO_SEC() + * + * Revision 1.13 1998/10/30 12:32:20 mhaveman + * Added forgotten cast in SK_PNMI_READ_U32 macro. + * + * Revision 1.12 1998/10/29 15:40:26 mhaveman + * -Changed SK_PNMI_TRAP_SENSOR_LEN because SensorDescr has now + * variable string length. + * -Defined SK_PNMI_CHECKFLAGS macro + * + * Revision 1.11 1998/10/29 08:53:34 mhaveman + * Removed SK_PNMI_RLM_XXX table indexed because these counters need + * not been saved over XMAC resets. + * + * Revision 1.10 1998/10/28 08:48:20 mhaveman + * -Added macros for storage according to alignment + * -Changed type of Instance to SK_U32 because of VPD + * -Removed trap structures. Not needed because of alignment problem + * -Changed type of Action form SK_U8 to int + * + * Revision 1.9 1998/10/21 13:34:45 mhaveman + * Shit, mismatched calculation of SK_PNMI_HUNDREDS_SEC. Corrected. + * + * Revision 1.8 1998/10/21 13:24:58 mhaveman + * Changed calculation of hundreds of seconds. + * + * Revision 1.7 1998/10/20 07:31:41 mhaveman + * Made type changes to unsigned int where possible. + * + * Revision 1.6 1998/09/04 17:04:05 mhaveman + * Added Sync counters to offset storage to provided settled values on + * port switch. + * + * Revision 1.5 1998/09/04 12:45:35 mhaveman + * Removed dummies for SK_DRIVER_ macros. They should be added by driver + * writer in skdrv2nd.h. + * + * Revision 1.4 1998/09/04 11:59:50 mhaveman + * Everything compiles now. Driver Macros for counting still missing. + * + * Revision 1.3 1998/08/24 12:01:35 mhaveman + * Intermediate state. + * + * Revision 1.2 1998/08/17 07:51:40 mhaveman + * Intermediate state. + * + * Revision 1.1 1998/08/11 09:08:40 mhaveman + * Intermediate state. + * + ****************************************************************************/ + +#ifndef _SKGEPNM2_H_ +#define _SKGEPNM2_H_ + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE !(FALSE) +#endif + +/* + * General definitions + */ +#define SK_PNMI_CHIPSET 1 /* XMAC11800FP */ + +#define SK_PNMI_BUS_PCI 1 /* PCI bus*/ + +/* + * Actions + */ +#define SK_PNMI_ACT_IDLE 1 +#define SK_PNMI_ACT_RESET 2 +#define SK_PNMI_ACT_SELFTEST 3 +#define SK_PNMI_ACT_RESETCNT 4 + +/* + * VPD releated defines + */ +#define SK_PNMI_VPD_ARR_SIZE 40 +#define SK_PNMI_VPD_STR_SIZE 5 + +#define SK_PNMI_VPD_RW 1 +#define SK_PNMI_VPD_RO 2 + +#define SK_PNMI_VPD_OK 0 +#define SK_PNMI_VPD_NOTFOUND 1 +#define SK_PNMI_VPD_CUT 2 +#define SK_PNMI_VPD_TIMEOUT 3 +#define SK_PNMI_VPD_FULL 4 +#define SK_PNMI_VPD_NOWRITE 5 +#define SK_PNMI_VPD_FATAL 6 + +#define SK_PNMI_VPD_IGNORE 0 +#define SK_PNMI_VPD_CREATE 1 +#define SK_PNMI_VPD_DELETE 2 + + +/* + * RLMT related defines + */ +#define SK_PNMI_DEF_RLMT_CHG_THRES 240 /* 4 changes per minute */ + +/* + * Internal table definitions + */ +#define SK_PNMI_GET 0 +#define SK_PNMI_PRESET 1 +#define SK_PNMI_SET 2 + +#define SK_PNMI_RO 0 +#define SK_PNMI_RW 1 + +typedef struct s_OidTabEntry { + SK_U32 Id; + SK_U32 InstanceNo; + unsigned int StructSize; + unsigned int Offset; + int Access; + int (* Func)(SK_AC *pAc, SK_IOC pIo, int action, + SK_U32 Id, char* pBuf, unsigned int* pLen, + SK_U32 Instance, unsigned int TableIndex); + SK_U16 Param; +} SK_PNMI_TAB_ENTRY; + + +/* + * Trap lengths + */ +#define SK_PNMI_TRAP_SIMPLE_LEN 17 +#define SK_PNMI_TRAP_SENSOR_LEN_BASE 46 +#define SK_PNMI_TRAP_RLMT_CHANGE_LEN 23 +#define SK_PNMI_TRAP_RLMT_PORT_LEN 23 + + +/* + * MAC statistic data structures + */ +#define SK_PNMI_HTX 0 +#define SK_PNMI_HTX_OCTET 1 +#define SK_PNMI_HTX_OCTETHIGH 1 +#define SK_PNMI_HTX_OCTETLOW 2 +#define SK_PNMI_HTX_BROADCAST 3 +#define SK_PNMI_HTX_MULTICAST 4 +#define SK_PNMI_HTX_UNICAST 5 +#define SK_PNMI_HTX_LONGFRAMES 6 +#define SK_PNMI_HTX_BURST 7 +#define SK_PNMI_HTX_PMACC 8 +#define SK_PNMI_HTX_MACC 9 +#define SK_PNMI_HTX_SINGLE_COL 10 +#define SK_PNMI_HTX_MULTI_COL 11 +#define SK_PNMI_HTX_EXCESS_COL 12 +#define SK_PNMI_HTX_LATE_COL 13 +#define SK_PNMI_HTX_DEFFERAL 14 +#define SK_PNMI_HTX_EXCESS_DEF 15 +#define SK_PNMI_HTX_UNDERRUN 16 +#define SK_PNMI_HTX_CARRIER 17 +#define SK_PNMI_HTX_UTILUNDER 18 +#define SK_PNMI_HTX_UTILOVER 19 +#define SK_PNMI_HTX_64 20 +#define SK_PNMI_HTX_127 21 +#define SK_PNMI_HTX_255 22 +#define SK_PNMI_HTX_511 23 +#define SK_PNMI_HTX_1023 24 +#define SK_PNMI_HTX_MAX 25 +#define SK_PNMI_HTX_RESERVED26 26 +#define SK_PNMI_HTX_RESERVED27 27 +#define SK_PNMI_HTX_RESERVED28 28 +#define SK_PNMI_HTX_RESERVED29 29 +#define SK_PNMI_HTX_RESERVED30 30 +#define SK_PNMI_HTX_RESERVED31 31 +#define SK_PNMI_HRX (32 + 0) +#define SK_PNMI_HRX_OCTET (32 + 1) +#define SK_PNMI_HRX_OCTETHIGH (32 + 1) +#define SK_PNMI_HRX_OCTETLOW (32 + 2) +#define SK_PNMI_HRX_BROADCAST (32 + 3) +#define SK_PNMI_HRX_MULTICAST (32 + 4) +#define SK_PNMI_HRX_UNICAST (32 + 5) +#define SK_PNMI_HRX_PMACC (32 + 6) +#define SK_PNMI_HRX_MACC (32 + 7) +#define SK_PNMI_HRX_PMACC_ERR (32 + 8) +#define SK_PNMI_HRX_MACC_UNKWN (32 + 9) +#define SK_PNMI_HRX_BURST (32 + 10) +#define SK_PNMI_HRX_MISSED (32 + 11) +#define SK_PNMI_HRX_FRAMING (32 + 12) +#define SK_PNMI_HRX_OVERFLOW (32 + 13) +#define SK_PNMI_HRX_JABBER (32 + 14) +#define SK_PNMI_HRX_CARRIER (32 + 15) +#define SK_PNMI_HRX_IRLENGTH (32 + 16) +#define SK_PNMI_HRX_SYMBOL (32 + 17) +#define SK_PNMI_HRX_SHORTS (32 + 18) +#define SK_PNMI_HRX_RUNT (32 + 19) +#define SK_PNMI_HRX_TOO_LONG (32 + 20) +#define SK_PNMI_HRX_FCS (32 + 21) +#define SK_PNMI_HRX_RESERVED22 (32 + 22) +#define SK_PNMI_HRX_CEXT (32 + 23) +#define SK_PNMI_HRX_UTILUNDER (32 + 24) +#define SK_PNMI_HRX_UTILOVER (32 + 25) +#define SK_PNMI_HRX_64 (32 + 26) +#define SK_PNMI_HRX_127 (32 + 27) +#define SK_PNMI_HRX_255 (32 + 28) +#define SK_PNMI_HRX_511 (32 + 29) +#define SK_PNMI_HRX_1023 (32 + 30) +#define SK_PNMI_HRX_MAX (32 + 31) + +#define SK_PNMI_HTX_SYNC 64 +#define SK_PNMI_HTX_SYNC_OCTET 65 + +#define SK_PNMI_MAX_IDX (SK_PNMI_CNT_NO) + +/* + * MAC specific data + */ +typedef struct s_PnmiStatAddr { + SK_BOOL GetOffset; /* TRUE: Call GetStatVal function */ + SK_U16 Param; /* XMAC register containing value */ +} SK_PNMI_STATADDR; + + +/* + * SK_PNMI_STRUCT_DATA copy offset evaluation macros + */ +#define SK_PNMI_OFF(e) ((SK_U32)&(((SK_PNMI_STRUCT_DATA *)0)->e)) +#define SK_PNMI_MAI_OFF(e) ((SK_U32)&(((SK_PNMI_STRUCT_DATA *)0)->e)) +#define SK_PNMI_VPD_OFF(e) ((SK_U32)&(((SK_PNMI_VPD *)0)->e)) +#define SK_PNMI_SEN_OFF(e) ((SK_U32)&(((SK_PNMI_SENSOR *)0)->e)) +#define SK_PNMI_CHK_OFF(e) ((SK_U32)&(((SK_PNMI_CHECKSUM *)0)->e)) +#define SK_PNMI_STA_OFF(e) ((SK_U32)&(((SK_PNMI_STAT *)0)->e)) +#define SK_PNMI_CNF_OFF(e) ((SK_U32)&(((SK_PNMI_CONF *)0)->e)) +#define SK_PNMI_RLM_OFF(e) ((SK_U32)&(((SK_PNMI_RLMT *)0)->e)) +#define SK_PNMI_MON_OFF(e) ((SK_U32)&(((SK_PNMI_RLMT_MONITOR *)0)->e)) +#define SK_PNMI_TRP_OFF(e) ((SK_U32)&(((SK_PNMI_TRAP *)0)->e)) + +#define SK_PNMI_SET_STAT(b,s,o) {SK_U32 Val32; char *pVal; \ + Val32 = (s); \ + pVal = (char *)(b) + ((SK_U32) \ + &(((SK_PNMI_STRUCT_DATA *)0)-> \ + ReturnStatus.ErrorStatus)); \ + SK_PNMI_STORE_U32(pVal, Val32); \ + Val32 = (o); \ + pVal = (char *)(b) + ((SK_U32) \ + &(((SK_PNMI_STRUCT_DATA *)0)-> \ + ReturnStatus.ErrorOffset)); \ + SK_PNMI_STORE_U32(pVal, Val32);} + +/* + * Time macros + */ +#if SK_TICKS_PER_SEC == 100 +#define SK_PNMI_HUNDREDS_SEC(t) (t) +#else +#define SK_PNMI_HUNDREDS_SEC(t) (((t) * 100) / (SK_TICKS_PER_SEC)) +#endif + +/* + * Macros to work around alignment problems + */ +#ifndef SK_PNMI_STORE_U16 +#define SK_PNMI_STORE_U16(p,v) {*(char *)(p) = *((char *)&(v)); \ + *((char *)(p) + 1) = \ + *(((char *)&(v)) + 1);} +#endif + +#ifndef SK_PNMI_STORE_U32 +#define SK_PNMI_STORE_U32(p,v) {*(char *)(p) = *((char *)&(v)); \ + *((char *)(p) + 1) = \ + *(((char *)&(v)) + 1); \ + *((char *)(p) + 2) = \ + *(((char *)&(v)) + 2); \ + *((char *)(p) + 3) = \ + *(((char *)&(v)) + 3);} +#endif + +#ifndef SK_PNMI_STORE_U64 +#define SK_PNMI_STORE_U64(p,v) {*(char *)(p) = *((char *)&(v)); \ + *((char *)(p) + 1) = \ + *(((char *)&(v)) + 1); \ + *((char *)(p) + 2) = \ + *(((char *)&(v)) + 2); \ + *((char *)(p) + 3) = \ + *(((char *)&(v)) + 3); \ + *((char *)(p) + 4) = \ + *(((char *)&(v)) + 4); \ + *((char *)(p) + 5) = \ + *(((char *)&(v)) + 5); \ + *((char *)(p) + 6) = \ + *(((char *)&(v)) + 6); \ + *((char *)(p) + 7) = \ + *(((char *)&(v)) + 7);} +#endif + +#ifndef SK_PNMI_READ_U16 +#define SK_PNMI_READ_U16(p,v) {*((char *)&(v)) = *(char *)(p); \ + *(((char *)&(v)) + 1) = \ + *((char *)(p) + 1);} +#endif + +#ifndef SK_PNMI_READ_U32 +#define SK_PNMI_READ_U32(p,v) {*((char *)&(v)) = *(char *)(p); \ + *(((char *)&(v)) + 1) = \ + *((char *)(p) + 1); \ + *(((char *)&(v)) + 2) = \ + *((char *)(p) + 2); \ + *(((char *)&(v)) + 3) = \ + *((char *)(p) + 3);} +#endif + +#ifndef SK_PNMI_READ_U64 +#define SK_PNMI_READ_U64(p,v) {*((char *)&(v)) = *(char *)(p); \ + *(((char *)&(v)) + 1) = \ + *((char *)(p) + 1); \ + *(((char *)&(v)) + 2) = \ + *((char *)(p) + 2); \ + *(((char *)&(v)) + 3) = \ + *((char *)(p) + 3); \ + *(((char *)&(v)) + 4) = \ + *((char *)(p) + 4); \ + *(((char *)&(v)) + 5) = \ + *((char *)(p) + 5); \ + *(((char *)&(v)) + 6) = \ + *((char *)(p) + 6); \ + *(((char *)&(v)) + 7) = \ + *((char *)(p) + 7);} +#endif + +/* + * Macros for Debug + */ +#ifdef DEBUG + +#define SK_PNMI_CHECKFLAGS(vSt) {if (pAC->Pnmi.MacUpdatedFlag > 0 || \ + pAC->Pnmi.RlmtUpdatedFlag > 0 || \ + pAC->Pnmi.SirqUpdatedFlag > 0) { \ + SK_DBG_MSG(pAC, \ + SK_DBGMOD_PNMI, \ + SK_DBGCAT_CTRL, \ + ("PNMI: ERR: %s MacUFlag=%d, RlmtUFlag=%d, SirqUFlag=%d\n", \ + vSt, \ + pAC->Pnmi.MacUpdatedFlag, \ + pAC->Pnmi.RlmtUpdatedFlag, \ + pAC->Pnmi.SirqUpdatedFlag))}} + +#else /* !DEBUG */ + +#define SK_PNMI_CHECKFLAGS(vSt) /* Nothing */ + +#endif /* !DEBUG */ + +#endif /* _SKGEPNM2_H_ */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/h/skgepnmi.h linux/drivers/net/sk98lin/h/skgepnmi.h --- v2.2.13/linux/drivers/net/sk98lin/h/skgepnmi.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/h/skgepnmi.h Tue Jan 4 10:12:18 2000 @@ -0,0 +1,914 @@ +/***************************************************************************** + * + * Name: skgepnmi.h + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.37 $ + * Date: $Date: 1999/09/14 14:25:32 $ + * Purpose: Defines for Private Network Management Interface + * + ****************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/***************************************************************************** + * + * History: + * + * $Log: skgepnmi.h,v $ + * Revision 1.37 1999/09/14 14:25:32 rwahl + * Set MDB version for 1000Base-T (sensors, Master/Slave) changes. + * + * Revision 1.36 1999/05/20 09:24:56 cgoos + * Changes for 1000Base-T (sensors, Master/Slave). + * + * Revision 1.35 1999/04/13 15:10:51 mhaveman + * Replaced RLMT macros SK_RLMT_CHECK_xxx again by those of PNMI to + * grant unified interface. But PNMI macros will store the same + * value as RLMT macros. + * + * Revision 1.34 1999/04/13 15:03:49 mhaveman + * -Changed copyright + * -Removed SK_PNMI_RLMT_MODE_CHK_xxx macros. Those of RLMT should be + * used. + * + * Revision 1.33 1999/03/23 10:41:02 mhaveman + * Changed comments. + * + * Revision 1.32 1999/01/25 15:01:33 mhaveman + * Added support for multiple simultaniously active ports. + * + * Revision 1.31 1999/01/19 10:06:26 mhaveman + * Added new error log message. + * + * Revision 1.30 1999/01/05 10:34:49 mhaveman + * Fixed little error in RlmtChangeEstimate calculation. + * + * Revision 1.29 1999/01/05 09:59:41 mhaveman + * Redesigned port switch average calculation to avoid 64bit + * arithmetic. + * + * Revision 1.28 1998/12/08 10:05:48 mhaveman + * Defined macro SK_PNMI_MIN_STRUCT_SIZE. + * + * Revision 1.27 1998/12/03 14:39:35 mhaveman + * Fixed problem that LSTAT was enumerated wrong. + * + * Revision 1.26 1998/12/03 11:19:51 mhaveman + * Changed contents of errlog message SK_PNMI_ERR016MSG + * + * Revision 1.25 1998/12/01 10:40:04 mhaveman + * Changed size of SensorNumber, ChecksumNumber and RlmtPortNumber in + * SK_PNMI_STRUCT_DATA to be conform with OID definition. + * + * Revision 1.24 1998/11/20 08:09:27 mhaveman + * Added macros to convert between logical, physical port indexes and + * instances. + * + * Revision 1.23 1998/11/10 13:41:13 mhaveman + * Needed to change interface, because NT driver needs a return value + * of needed buffer space on TOO_SHORT errors. Therefore all + * SkPnmiGet/Preset/Set functions now have a pointer to the length + * parameter, where the needed space on error is returned. + * + * Revision 1.22 1998/11/03 12:05:51 mhaveman + * Added pAC parameter to counter macors. + * + * Revision 1.21 1998/11/02 10:47:36 mhaveman + * Added syslog messages for internal errors. + * + * Revision 1.20 1998/10/30 15:49:36 mhaveman + * -Removed unused SK_PNMI_UTILIZATION_BASE and EstOldCnt. + * -Redefined SK_PNMI_CHG_EST_BASE to hundreds of seconds. + * + * Revision 1.19 1998/10/29 15:38:44 mhaveman + * Changed string lengths of PNMI_STRUCT_DATA structure because + * string OIDs are now encoded with leading length ocetet. + * + * Revision 1.18 1998/10/29 08:52:27 mhaveman + * -Added byte to strings in PNMI_STRUCT_DATA structure. + * -Shortened SK_PNMI_RLMT structure to SK_MAX_MACS elements. + * + * Revision 1.17 1998/10/28 08:49:50 mhaveman + * -Changed type of Instance back to SK_U32 because of VPD + * -Changed type from SK_U8 to char of PciBusSpeed, PciBusWidth, PMD, + * and Connector. + * + * Revision 1.16 1998/10/22 10:42:31 mhaveman + * -Removed (SK_U32) casts for OIDs + * -excluded NDIS OIDs when they are already defined with ifndef _NDIS_ + * + * Revision 1.15 1998/10/20 13:56:28 mhaveman + * Headerfile includes now directly other header files to comile correctly. + * + * Revision 1.14 1998/10/20 07:31:09 mhaveman + * Made type changes to unsigned int where possible. + * + * Revision 1.13 1998/10/19 10:53:13 mhaveman + * -Casted OID definitions to SK_U32 + * -Renamed RlmtMAC... to RlmtPort... + * -Changed wrong type of VpdEntriesList from SK_U32 to char * + * + * Revision 1.12 1998/10/13 07:42:27 mhaveman + * -Added OIDs OID_SKGE_TRAP_NUMBER and OID_SKGE_ALL_DATA + * -Removed old cvs history entries + * -Renamed MacNumber to PortNumber + * + * Revision 1.11 1998/10/07 10:55:24 mhaveman + * -Added OID_MDB_VERSION. Therefore was a renumbering of the VPD OIDs + * necessary. + * -Added OID_GEN_ Ids to support the windows driver. + * + * Revision 1.10 1998/09/30 13:41:10 mhaveman + * Renamed some OIDs to reduce usage of 'MAC' which is replaced by 'PORT'. + * + * Revision 1.9 1998/09/04 17:06:17 mhaveman + * -Added SyncCounter as macro. + * -Renamed OID_SKGE_.._NO_DESCR_CTS to OID_SKGE_.._NO_BUF_CTS. + * -Added macros for driver description and version strings. + * + * Revision 1.8 1998/09/04 14:36:52 mhaveman + * Added OIDs and Structure to access value of macro counters which are + * counted by the driver. + * + * Revision 1.7 1998/09/04 11:59:36 mhaveman + * Everything compiles now. Driver Macros for counting still missing. + * + ****************************************************************************/ + +#ifndef _SKGEPNMI_H_ +#define _SKGEPNMI_H_ + +/* + * Include dependencies + */ +#include "h/sktypes.h" +#include "h/skerror.h" +#include "h/sktimer.h" +#include "h/ski2c.h" +#include "h/skaddr.h" +#include "h/skrlmt.h" + + +/* + * Management Database Version + */ +#define SK_PNMI_MDB_VERSION 0x00030000 /* 3.0 */ + + +/* + * Event definitions + */ +#define SK_PNMI_EVT_SIRQ_OVERFLOW 1 /* Counter overflow */ +#define SK_PNMI_EVT_SEN_WAR_LOW 2 /* Lower war thres exceeded */ +#define SK_PNMI_EVT_SEN_WAR_UPP 3 /* Upper war thres exceeded */ +#define SK_PNMI_EVT_SEN_ERR_LOW 4 /* Lower err thres exceeded */ +#define SK_PNMI_EVT_SEN_ERR_UPP 5 /* Upper err thres exceeded */ +#define SK_PNMI_EVT_CHG_EST_TIMER 6 /* Timer event for RLMT Chg */ +#define SK_PNMI_EVT_UTILIZATION_TIMER 7 /* Timer event for Utiliza. */ +#define SK_PNMI_EVT_CLEAR_COUNTER 8 /* Clear statistic counters */ +#define SK_PNMI_EVT_XMAC_RESET 9 /* XMAC will be reset */ + +#define SK_PNMI_EVT_RLMT_PORT_UP 10 /* Port came logically up */ +#define SK_PNMI_EVT_RLMT_PORT_DOWN 11 /* Port went logically down */ +#define SK_PNMI_EVT_RLMT_PORT_SWITCH 12 /* Switched active port */ +#define SK_PNMI_EVT_RLMT_SEGMENTATION 13 /* Two SP root bridges found */ +#define SK_PNMI_EVT_RLMT_ACTIVE_DOWN 14 /* Port went logically down */ +#define SK_PNMI_EVT_RLMT_ACTIVE_UP 15 /* Port came logically up */ + +/* + * Return values + */ +#define SK_PNMI_ERR_OK 0 +#define SK_PNMI_ERR_GENERAL 1 +#define SK_PNMI_ERR_TOO_SHORT 2 +#define SK_PNMI_ERR_BAD_VALUE 3 +#define SK_PNMI_ERR_READ_ONLY 4 +#define SK_PNMI_ERR_UNKNOWN_OID 5 +#define SK_PNMI_ERR_UNKNOWN_INST 6 + + +/* + * Return values of driver reset function SK_DRIVER_RESET() and + * driver event function SK_DRIVER_EVENT() + */ +#define SK_PNMI_ERR_OK 0 +#define SK_PNMI_ERR_FAIL 1 + + +/* + * Return values of driver test function SK_DRIVER_SELFTEST() + */ +#define SK_PNMI_TST_UNKNOWN (1 << 0) +#define SK_PNMI_TST_TRANCEIVER (1 << 1) +#define SK_PNMI_TST_ASIC (1 << 2) +#define SK_PNMI_TST_SENSOR (1 << 3) +#define SK_PNMI_TST_POWERMGMT (1 << 4) +#define SK_PNMI_TST_PCI (1 << 5) +#define SK_PNMI_TST_MAC (1 << 6) + + +/* + * RLMT specific definitions + */ +#define SK_PNMI_RLMT_STATUS_STANDBY 1 +#define SK_PNMI_RLMT_STATUS_ACTIVE 2 +#define SK_PNMI_RLMT_STATUS_ERROR 3 + +#define SK_PNMI_RLMT_LSTAT_PHY_DOWN 1 +#define SK_PNMI_RLMT_LSTAT_AUTONEG 2 +#define SK_PNMI_RLMT_LSTAT_LOG_DOWN 3 +#define SK_PNMI_RLMT_LSTAT_LOG_UP 4 +#define SK_PNMI_RLMT_LSTAT_INDETERMINATED 5 + +#define SK_PNMI_RLMT_MODE_CHK_LINK (SK_RLMT_CHECK_LINK) +#define SK_PNMI_RLMT_MODE_CHK_RX (SK_RLMT_CHECK_LOC_LINK) +#define SK_PNMI_RLMT_MODE_CHK_SPT (SK_RLMT_CHECK_SEG) +/* #define SK_PNMI_RLMT_MODE_CHK_EX */ + +/* + * OID definition + */ +#ifndef _NDIS_ /* Check, whether NDIS already included OIDs */ + +#define OID_GEN_XMIT_OK 0x00020101 +#define OID_GEN_RCV_OK 0x00020102 +#define OID_GEN_XMIT_ERROR 0x00020103 +#define OID_GEN_RCV_ERROR 0x00020104 +#define OID_GEN_RCV_NO_BUFFER 0x00020105 + +/* #define OID_GEN_DIRECTED_BYTES_XMIT 0x00020201 */ +#define OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202 +/* #define OID_GEN_MULTICAST_BYTES_XMIT 0x00020203 */ +#define OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204 +/* #define OID_GEN_BROADCAST_BYTES_XMIT 0x00020205 */ +#define OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206 +/* #define OID_GEN_DIRECTED_BYTES_RCV 0x00020207 */ +#define OID_GEN_DIRECTED_FRAMES_RCV 0x00020208 +/* #define OID_GEN_MULTICAST_BYTES_RCV 0x00020209 */ +#define OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A +/* #define OID_GEN_BROADCAST_BYTES_RCV 0x0002020B */ +#define OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C +#define OID_GEN_RCV_CRC_ERROR 0x0002020D +#define OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E + +#define OID_802_3_PERMANENT_ADDRESS 0x01010101 +#define OID_802_3_CURRENT_ADDRESS 0x01010102 +/* #define OID_802_3_MULTICAST_LIST 0x01010103 */ +/* #define OID_802_3_MAXIMUM_LIST_SIZE 0x01010104 */ +/* #define OID_802_3_MAC_OPTIONS 0x01010105 */ + +#define OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101 +#define OID_802_3_XMIT_ONE_COLLISION 0x01020102 +#define OID_802_3_XMIT_MORE_COLLISIONS 0x01020103 +#define OID_802_3_XMIT_DEFERRED 0x01020201 +#define OID_802_3_XMIT_MAX_COLLISIONS 0x01020202 +#define OID_802_3_RCV_OVERRUN 0x01020203 +#define OID_802_3_XMIT_UNDERRUN 0x01020204 +#define OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206 +#define OID_802_3_XMIT_LATE_COLLISIONS 0x01020207 + +#endif /* _NDIS_ */ + +#define OID_SKGE_MDB_VERSION 0xFF010100 +#define OID_SKGE_SUPPORTED_LIST 0xFF010101 +#define OID_SKGE_VPD_FREE_BYTES 0xFF010102 +#define OID_SKGE_VPD_ENTRIES_LIST 0xFF010103 +#define OID_SKGE_VPD_ENTRIES_NUMBER 0xFF010104 +#define OID_SKGE_VPD_KEY 0xFF010105 +#define OID_SKGE_VPD_VALUE 0xFF010106 +#define OID_SKGE_VPD_ACCESS 0xFF010107 +#define OID_SKGE_VPD_ACTION 0xFF010108 + +#define OID_SKGE_PORT_NUMBER 0xFF010110 +#define OID_SKGE_DEVICE_TYPE 0xFF010111 +#define OID_SKGE_DRIVER_DESCR 0xFF010112 +#define OID_SKGE_DRIVER_VERSION 0xFF010113 +#define OID_SKGE_HW_DESCR 0xFF010114 +#define OID_SKGE_HW_VERSION 0xFF010115 +#define OID_SKGE_CHIPSET 0xFF010116 +#define OID_SKGE_ACTION 0xFF010117 +#define OID_SKGE_RESULT 0xFF010118 +#define OID_SKGE_BUS_TYPE 0xFF010119 +#define OID_SKGE_BUS_SPEED 0xFF01011A +#define OID_SKGE_BUS_WIDTH 0xFF01011B + +/*#define OID_SKGE_MULTICAST_LIST 0xFF01011C*/ + +#define OID_SKGE_SENSOR_NUMBER 0xFF020100 +#define OID_SKGE_SENSOR_INDEX 0xFF020101 +#define OID_SKGE_SENSOR_DESCR 0xFF020102 +#define OID_SKGE_SENSOR_TYPE 0xFF020103 +#define OID_SKGE_SENSOR_VALUE 0xFF020104 +#define OID_SKGE_SENSOR_WAR_THRES_LOW 0xFF020105 +#define OID_SKGE_SENSOR_WAR_THRES_UPP 0xFF020106 +#define OID_SKGE_SENSOR_ERR_THRES_LOW 0xFF020107 +#define OID_SKGE_SENSOR_ERR_THRES_UPP 0xFF020108 +#define OID_SKGE_SENSOR_STATUS 0xFF020109 +#define OID_SKGE_SENSOR_WAR_CTS 0xFF02010A +#define OID_SKGE_SENSOR_ERR_CTS 0xFF02010B +#define OID_SKGE_SENSOR_WAR_TIME 0xFF02010C +#define OID_SKGE_SENSOR_ERR_TIME 0xFF02010D + +#define OID_SKGE_CHKSM_NUMBER 0xFF020110 +#define OID_SKGE_CHKSM_RX_OK_CTS 0xFF020111 +#define OID_SKGE_CHKSM_RX_UNABLE_CTS 0xFF020112 +#define OID_SKGE_CHKSM_RX_ERR_CTS 0xFF020113 +#define OID_SKGE_CHKSM_TX_OK_CTS 0xFF020114 +#define OID_SKGE_CHKSM_TX_UNABLE_CTS 0xFF020115 + +#define OID_SKGE_STAT_TX 0xFF020120 +#define OID_SKGE_STAT_TX_OCTETS 0xFF020121 +#define OID_SKGE_STAT_TX_BROADCAST 0xFF020122 +#define OID_SKGE_STAT_TX_MULTICAST 0xFF020123 +#define OID_SKGE_STAT_TX_UNICAST 0xFF020124 +#define OID_SKGE_STAT_TX_LONGFRAMES 0xFF020125 +#define OID_SKGE_STAT_TX_BURST 0xFF020126 +#define OID_SKGE_STAT_TX_PFLOWC 0xFF020127 +#define OID_SKGE_STAT_TX_FLOWC 0xFF020128 +#define OID_SKGE_STAT_TX_SINGLE_COL 0xFF020129 +#define OID_SKGE_STAT_TX_MULTI_COL 0xFF02012A +#define OID_SKGE_STAT_TX_EXCESS_COL 0xFF02012B +#define OID_SKGE_STAT_TX_LATE_COL 0xFF02012C +#define OID_SKGE_STAT_TX_DEFFERAL 0xFF02012D +#define OID_SKGE_STAT_TX_EXCESS_DEF 0xFF02012E +#define OID_SKGE_STAT_TX_UNDERRUN 0xFF02012F +#define OID_SKGE_STAT_TX_CARRIER 0xFF020130 +/* #define OID_SKGE_STAT_TX_UTIL 0xFF020131 */ +#define OID_SKGE_STAT_TX_64 0xFF020132 +#define OID_SKGE_STAT_TX_127 0xFF020133 +#define OID_SKGE_STAT_TX_255 0xFF020134 +#define OID_SKGE_STAT_TX_511 0xFF020135 +#define OID_SKGE_STAT_TX_1023 0xFF020136 +#define OID_SKGE_STAT_TX_MAX 0xFF020137 +#define OID_SKGE_STAT_TX_SYNC 0xFF020138 +#define OID_SKGE_STAT_TX_SYNC_OCTETS 0xFF020139 +#define OID_SKGE_STAT_RX 0xFF02013A +#define OID_SKGE_STAT_RX_OCTETS 0xFF02013B +#define OID_SKGE_STAT_RX_BROADCAST 0xFF02013C +#define OID_SKGE_STAT_RX_MULTICAST 0xFF02013D +#define OID_SKGE_STAT_RX_UNICAST 0xFF02013E +#define OID_SKGE_STAT_RX_PFLOWC 0xFF02013F +#define OID_SKGE_STAT_RX_FLOWC 0xFF020140 +#define OID_SKGE_STAT_RX_PFLOWC_ERR 0xFF020141 +#define OID_SKGE_STAT_RX_FLOWC_UNKWN 0xFF020142 +#define OID_SKGE_STAT_RX_BURST 0xFF020143 +#define OID_SKGE_STAT_RX_MISSED 0xFF020144 +#define OID_SKGE_STAT_RX_FRAMING 0xFF020145 +#define OID_SKGE_STAT_RX_OVERFLOW 0xFF020146 +#define OID_SKGE_STAT_RX_JABBER 0xFF020147 +#define OID_SKGE_STAT_RX_CARRIER 0xFF020148 +#define OID_SKGE_STAT_RX_IR_LENGTH 0xFF020149 +#define OID_SKGE_STAT_RX_SYMBOL 0xFF02014A +#define OID_SKGE_STAT_RX_SHORTS 0xFF02014B +#define OID_SKGE_STAT_RX_RUNT 0xFF02014C +#define OID_SKGE_STAT_RX_CEXT 0xFF02014D +#define OID_SKGE_STAT_RX_TOO_LONG 0xFF02014E +#define OID_SKGE_STAT_RX_FCS 0xFF02014F +/* #define OID_SKGE_STAT_RX_UTIL 0xFF020150 */ +#define OID_SKGE_STAT_RX_64 0xFF020151 +#define OID_SKGE_STAT_RX_127 0xFF020152 +#define OID_SKGE_STAT_RX_255 0xFF020153 +#define OID_SKGE_STAT_RX_511 0xFF020154 +#define OID_SKGE_STAT_RX_1023 0xFF020155 +#define OID_SKGE_STAT_RX_MAX 0xFF020156 + +#define OID_SKGE_PHYS_CUR_ADDR 0xFF010120 +#define OID_SKGE_PHYS_FAC_ADDR 0xFF010121 +#define OID_SKGE_PMD 0xFF010122 +#define OID_SKGE_CONNECTOR 0xFF010123 +#define OID_SKGE_LINK_CAP 0xFF010124 +#define OID_SKGE_LINK_MODE 0xFF010125 +#define OID_SKGE_LINK_MODE_STATUS 0xFF010126 +#define OID_SKGE_LINK_STATUS 0xFF010127 +#define OID_SKGE_FLOWCTRL_CAP 0xFF010128 +#define OID_SKGE_FLOWCTRL_MODE 0xFF010129 +#define OID_SKGE_FLOWCTRL_STATUS 0xFF01012A +#define OID_SKGE_PHY_OPERATION_CAP 0xFF01012B +#define OID_SKGE_PHY_OPERATION_MODE 0xFF01012C +#define OID_SKGE_PHY_OPERATION_STATUS 0xFF01012D + +#define OID_SKGE_TRAP 0xFF010130 +#define OID_SKGE_TRAP_NUMBER 0xFF010131 + +#define OID_SKGE_RLMT_MODE 0xFF010140 +#define OID_SKGE_RLMT_PORT_NUMBER 0xFF010141 +#define OID_SKGE_RLMT_PORT_ACTIVE 0xFF010142 +#define OID_SKGE_RLMT_PORT_PREFERED 0xFF010143 +#define OID_SKGE_RLMT_CHANGE_CTS 0xFF020160 +#define OID_SKGE_RLMT_CHANGE_TIME 0xFF020161 +#define OID_SKGE_RLMT_CHANGE_ESTIM 0xFF020162 +#define OID_SKGE_RLMT_CHANGE_THRES 0xFF020163 + +#define OID_SKGE_RLMT_PORT_INDEX 0xFF020164 +#define OID_SKGE_RLMT_STATUS 0xFF020165 +#define OID_SKGE_RLMT_TX_HELLO_CTS 0xFF020166 +#define OID_SKGE_RLMT_RX_HELLO_CTS 0xFF020167 +#define OID_SKGE_RLMT_TX_SP_REQ_CTS 0xFF020168 +#define OID_SKGE_RLMT_RX_SP_CTS 0xFF020169 + +#define OID_SKGE_RLMT_MONITOR_NUMBER 0xFF010150 +#define OID_SKGE_RLMT_MONITOR_INDEX 0xFF010151 +#define OID_SKGE_RLMT_MONITOR_ADDR 0xFF010152 +#define OID_SKGE_RLMT_MONITOR_ERRS 0xFF010153 +#define OID_SKGE_RLMT_MONITOR_TIMESTAMP 0xFF010154 +#define OID_SKGE_RLMT_MONITOR_ADMIN 0xFF010155 + +#define OID_SKGE_TX_SW_QUEUE_LEN 0xFF020170 +#define OID_SKGE_TX_SW_QUEUE_MAX 0xFF020171 +#define OID_SKGE_TX_RETRY 0xFF020172 +#define OID_SKGE_RX_INTR_CTS 0xFF020173 +#define OID_SKGE_TX_INTR_CTS 0xFF020174 +#define OID_SKGE_RX_NO_BUF_CTS 0xFF020175 +#define OID_SKGE_TX_NO_BUF_CTS 0xFF020176 +#define OID_SKGE_TX_USED_DESCR_NO 0xFF020177 +#define OID_SKGE_RX_DELIVERED_CTS 0xFF020178 +#define OID_SKGE_RX_OCTETS_DELIV_CTS 0xFF020179 +#define OID_SKGE_RX_HW_ERROR_CTS 0xFF02017A +#define OID_SKGE_TX_HW_ERROR_CTS 0xFF02017B +#define OID_SKGE_IN_ERRORS_CTS 0xFF02017C +#define OID_SKGE_OUT_ERROR_CTS 0xFF02017D +#define OID_SKGE_ERR_RECOVERY_CTS 0xFF02017E +#define OID_SKGE_SYSUPTIME 0xFF02017F + +#define OID_SKGE_ALL_DATA 0xFF020190 + + +#define OID_SKGE_TRAP_SEN_WAR_LOW 500 +#define OID_SKGE_TRAP_SEN_WAR_UPP 501 +#define OID_SKGE_TRAP_SEN_ERR_LOW 502 +#define OID_SKGE_TRAP_SEN_ERR_UPP 503 +#define OID_SKGE_TRAP_RLMT_CHANGE_THRES 520 +#define OID_SKGE_TRAP_RLMT_CHANGE_PORT 521 +#define OID_SKGE_TRAP_RLMT_PORT_DOWN 522 +#define OID_SKGE_TRAP_RLMT_PORT_UP 523 +#define OID_SKGE_TRAP_RLMT_SEGMENTATION 524 + + +/* + * Define error numbers and messages for syslog + */ +#define SK_PNMI_ERR001 (SK_ERRBASE_PNMI + 1) +#define SK_PNMI_ERR001MSG "SkPnmiGetStruct: Unknown OID" +#define SK_PNMI_ERR002 (SK_ERRBASE_PNMI + 2) +#define SK_PNMI_ERR002MSG "SkPnmiGetStruct: Cannot read VPD keys" +#define SK_PNMI_ERR003 (SK_ERRBASE_PNMI + 3) +#define SK_PNMI_ERR003MSG "OidStruct: Called with wrong OID" +#define SK_PNMI_ERR004 (SK_ERRBASE_PNMI + 4) +#define SK_PNMI_ERR004MSG "OidStruct: Called with wrong action" +#define SK_PNMI_ERR005 (SK_ERRBASE_PNMI + 5) +#define SK_PNMI_ERR005MSG "Perform: Cannot reset driver" +#define SK_PNMI_ERR006 (SK_ERRBASE_PNMI + 6) +#define SK_PNMI_ERR006MSG "Perform: Unknown OID action command" +#define SK_PNMI_ERR007 (SK_ERRBASE_PNMI + 7) +#define SK_PNMI_ERR007MSG "General: Driver description not initialized" +#define SK_PNMI_ERR008 (SK_ERRBASE_PNMI + 8) +#define SK_PNMI_ERR008MSG "Addr: Tried to get unknown OID" +#define SK_PNMI_ERR009 (SK_ERRBASE_PNMI + 9) +#define SK_PNMI_ERR009MSG "Addr: Unknown OID" +#define SK_PNMI_ERR010 (SK_ERRBASE_PNMI + 10) +#define SK_PNMI_ERR010MSG "CsumStat: Unknown OID" +#define SK_PNMI_ERR011 (SK_ERRBASE_PNMI + 11) +#define SK_PNMI_ERR011MSG "SensorStat: Sensor descr string too long" +#define SK_PNMI_ERR012 (SK_ERRBASE_PNMI + 12) +#define SK_PNMI_ERR012MSG "SensorStat: Unknown OID" +#define SK_PNMI_ERR013 (SK_ERRBASE_PNMI + 13) +#define SK_PNMI_ERR013MSG "SensorStat: Unknown OID should be errored before" +#define SK_PNMI_ERR014 (SK_ERRBASE_PNMI + 14) +#define SK_PNMI_ERR014MSG "Vpd: Cannot read VPD keys" +#define SK_PNMI_ERR015 (SK_ERRBASE_PNMI + 15) +#define SK_PNMI_ERR015MSG "Vpd: Internal array for VPD keys to small" +#define SK_PNMI_ERR016 (SK_ERRBASE_PNMI + 16) +#define SK_PNMI_ERR016MSG "Vpd: Key string too long" +#define SK_PNMI_ERR017 (SK_ERRBASE_PNMI + 17) +#define SK_PNMI_ERR017MSG "Vpd: Invalid VPD status pointer" +#define SK_PNMI_ERR018 (SK_ERRBASE_PNMI + 18) +#define SK_PNMI_ERR018MSG "Vpd: VPD data not valid" +#define SK_PNMI_ERR019 (SK_ERRBASE_PNMI + 19) +#define SK_PNMI_ERR019MSG "Vpd: VPD entries list string too long" +#define SK_PNMI_ERR021 (SK_ERRBASE_PNMI + 21) +#define SK_PNMI_ERR021MSG "Vpd: VPD data string too long" +#define SK_PNMI_ERR022 (SK_ERRBASE_PNMI + 22) +#define SK_PNMI_ERR022MSG "Vpd: VPD data string too long should be errored before" +#define SK_PNMI_ERR023 (SK_ERRBASE_PNMI + 23) +#define SK_PNMI_ERR023MSG "Vpd: Unknown OID in get action" +#define SK_PNMI_ERR024 (SK_ERRBASE_PNMI + 24) +#define SK_PNMI_ERR024MSG "Vpd: Unknown OID in preset/set action" +#define SK_PNMI_ERR025 (SK_ERRBASE_PNMI + 25) +#define SK_PNMI_ERR025MSG "Vpd: Cannot write VPD after modify entry" +#define SK_PNMI_ERR026 (SK_ERRBASE_PNMI + 26) +#define SK_PNMI_ERR026MSG "Vpd: Cannot update VPD" +#define SK_PNMI_ERR027 (SK_ERRBASE_PNMI + 27) +#define SK_PNMI_ERR027MSG "Vpd: Cannot delete VPD entry" +#define SK_PNMI_ERR028 (SK_ERRBASE_PNMI + 28) +#define SK_PNMI_ERR028MSG "Vpd: Cannot update VPD after delete entry" +#define SK_PNMI_ERR029 (SK_ERRBASE_PNMI + 29) +#define SK_PNMI_ERR029MSG "General: Driver description string too long" +#define SK_PNMI_ERR030 (SK_ERRBASE_PNMI + 30) +#define SK_PNMI_ERR030MSG "General: Driver version not initialized" +#define SK_PNMI_ERR031 (SK_ERRBASE_PNMI + 31) +#define SK_PNMI_ERR031MSG "General: Driver version string too long" +#define SK_PNMI_ERR032 (SK_ERRBASE_PNMI + 32) +#define SK_PNMI_ERR032MSG "General: Cannot read VPD Name for HW descr" +#define SK_PNMI_ERR033 (SK_ERRBASE_PNMI + 33) +#define SK_PNMI_ERR033MSG "General: HW description string too long" +#define SK_PNMI_ERR034 (SK_ERRBASE_PNMI + 34) +#define SK_PNMI_ERR034MSG "General: Unknown OID" +#define SK_PNMI_ERR035 (SK_ERRBASE_PNMI + 35) +#define SK_PNMI_ERR035MSG "Rlmt: Unknown OID" +#define SK_PNMI_ERR036 (SK_ERRBASE_PNMI + 36) +#define SK_PNMI_ERR036MSG "Rlmt: Unknown OID should be errored before" +#define SK_PNMI_ERR037 (SK_ERRBASE_PNMI + 37) +#define SK_PNMI_ERR037MSG "Rlmt: SK_RLMT_MODE_CHANGE event return not 0" +#define SK_PNMI_ERR038 (SK_ERRBASE_PNMI + 38) +#define SK_PNMI_ERR038MSG "Rlmt: SK_RLMT_PREFPORT_CHANGE event return not 0" +#define SK_PNMI_ERR039 (SK_ERRBASE_PNMI + 39) +#define SK_PNMI_ERR039MSG "RlmtStat: Unknown OID" +#define SK_PNMI_ERR040 (SK_ERRBASE_PNMI + 40) +#define SK_PNMI_ERR040MSG "RlmtStat: Unknown OID should be errored before" +#define SK_PNMI_ERR041 (SK_ERRBASE_PNMI + 41) +#define SK_PNMI_ERR041MSG "MacPrivateConf: Unknown OID" +#define SK_PNMI_ERR042 (SK_ERRBASE_PNMI + 42) +#define SK_PNMI_ERR042MSG "MacPrivateConf: Unknown OID should be errored before" +#define SK_PNMI_ERR043 (SK_ERRBASE_PNMI + 43) +#define SK_PNMI_ERR043MSG "MacPrivateConf: SK_HWEV_SET_LMODE returned not 0" +#define SK_PNMI_ERR044 (SK_ERRBASE_PNMI + 44) +#define SK_PNMI_ERR044MSG "MacPrivateConf: SK_HWEV_SET_FLOWMODE returned not 0" +#define SK_PNMI_ERR045 (SK_ERRBASE_PNMI + 45) +#define SK_PNMI_ERR045MSG "MacPrivateConf: Unknown OID in set action" +#define SK_PNMI_ERR046 (SK_ERRBASE_PNMI + 46) +#define SK_PNMI_ERR046MSG "Monitor: Unknown OID" +#define SK_PNMI_ERR047 (SK_ERRBASE_PNMI + 47) +#define SK_PNMI_ERR047MSG "SirqUpdate: Event function returns not 0" +#define SK_PNMI_ERR048 (SK_ERRBASE_PNMI + 48) +#define SK_PNMI_ERR048MSG "RlmtUpdate: Event function returns not 0" +#define SK_PNMI_ERR049 (SK_ERRBASE_PNMI + 49) +#define SK_PNMI_ERR049MSG "" +#define SK_PNMI_ERR050 (SK_ERRBASE_PNMI + 50) +#define SK_PNMI_ERR050MSG "MacUpdate: Cannot update statistic counter" +#define SK_PNMI_ERR051 (SK_ERRBASE_PNMI + 51) +#define SK_PNMI_ERR051MSG "SkPnmiEvent: Port switch suspicious" + +/* + * Management counter macros called by the driver + */ +#define SK_PNMI_SET_DRIVER_DESCR(pAC,v) ((pAC)->Pnmi.pDriverDescription = \ + (char *)(v)) + +#define SK_PNMI_SET_DRIVER_VER(pAC,v) ((pAC)->Pnmi.pDriverVersion = \ + (char *)(v)) + +#define SK_PNMI_CNT_TX_QUEUE_LEN(pAC,v) \ + { \ + (pAC)->Pnmi.TxSwQueueLen = (SK_U64)(v); \ + if ((pAC)->Pnmi.TxSwQueueLen > (pAC)->Pnmi.TxSwQueueMax) { \ + (pAC)->Pnmi.TxSwQueueMax = (pAC)->Pnmi.TxSwQueueLen; \ + } \ + } +#define SK_PNMI_CNT_TX_RETRY(pAC) (((pAC)->Pnmi.TxRetryCts)++) +#define SK_PNMI_CNT_RX_INTR(pAC) (((pAC)->Pnmi.RxIntrCts)++) +#define SK_PNMI_CNT_TX_INTR(pAC) (((pAC)->Pnmi.TxIntrCts)++) +#define SK_PNMI_CNT_NO_RX_BUF(pAC) (((pAC)->Pnmi.RxNoBufCts)++) +#define SK_PNMI_CNT_NO_TX_BUF(pAC) (((pAC)->Pnmi.TxNoBufCts)++) +#define SK_PNMI_CNT_USED_TX_DESCR(pAC,v) \ + ((pAC)->Pnmi.TxUsedDescrNo=(SK_U64)(v)); +#define SK_PNMI_CNT_RX_OCTETS_DELIVERED(pAC,v) \ + { \ + ((pAC)->Pnmi.RxDeliveredCts)++; \ + (pAC)->Pnmi.RxOctetsDeliveredCts += (SK_U64)(v); \ + } +#define SK_PNMI_CNT_ERR_RECOVERY(pAC) (((pAC)->Pnmi.ErrRecoveryCts)++); + +#define SK_PNMI_CNT_SYNC_OCTETS(pAC,p,v) \ + { \ + if (((p) >= 0) && ((p) < SK_MAX_MACS)) { \ + ((pAC)->Pnmi.StatSyncCts[p])++; \ + (pAC)->Pnmi.StatSyncOctetsCts[p] += (SK_U64)(v); \ + } \ + } + +/* + * Conversion Macros + */ +#define SK_PNMI_PORT_INST2LOG(i) ((unsigned int)(i) - 1) +#define SK_PNMI_PORT_LOG2INST(l) ((unsigned int)(l) + 1) +#define SK_PNMI_PORT_PHYS2LOG(p) ((unsigned int)(p) + 1) +#define SK_PNMI_PORT_LOG2PHYS(pAC,l) ((unsigned int)(l) - 1) +#define SK_PNMI_PORT_PHYS2INST(p) ((unsigned int)(p) + 2) +#define SK_PNMI_PORT_INST2PHYS(pAC,i) ((unsigned int)(i) - 2) + +/* + * Structure definition for SkPnmiGetStruct and SkPnmiSetStruct + */ +#define SK_PNMI_VPD_ENTRIES 20 +#define SK_PNMI_VPD_DATALEN 128 +#define SK_PNMI_MULTICAST_LISTLEN 64 +#define SK_PNMI_SENSOR_ENTRIES (SK_MAX_SENSORS) +#define SK_PNMI_CHECKSUM_ENTRIES 3 +#define SK_PNMI_MAC_ENTRIES (SK_MAX_MACS + 1) +#define SK_PNMI_MONITOR_ENTRIES 20 +#define SK_PNMI_TRAP_ENTRIES 10 +#define SK_PNMI_TRAPLEN 128 +#define SK_PNMI_STRINGLEN1 80 +#define SK_PNMI_STRINGLEN2 25 +#define SK_PNMI_TRAP_QUEUE_LEN 512 + +typedef struct s_PnmiVpd { + char VpdKey[5]; + char VpdValue[SK_PNMI_VPD_DATALEN]; + SK_U8 VpdAccess; + SK_U8 VpdAction; +} SK_PNMI_VPD; + +typedef struct s_PnmiSensor { + SK_U8 SensorIndex; + char SensorDescr[SK_PNMI_STRINGLEN2]; + SK_U8 SensorType; + SK_U32 SensorValue; + SK_U32 SensorWarningThresholdLow; + SK_U32 SensorWarningThresholdHigh; + SK_U32 SensorErrorThresholdLow; + SK_U32 SensorErrorThresholdHigh; + SK_U8 SensorStatus; + SK_U64 SensorWarningCts; + SK_U64 SensorErrorCts; + SK_U64 SensorWarningTimestamp; + SK_U64 SensorErrorTimestamp; +} SK_PNMI_SENSOR; + +typedef struct s_PnmiChecksum { + SK_U64 ChecksumRxOkCts; + SK_U64 ChecksumRxUnableCts; + SK_U64 ChecksumRxErrCts; + SK_U64 ChecksumTxOkCts; + SK_U64 ChecksumTxUnableCts; +} SK_PNMI_CHECKSUM; + +typedef struct s_PnmiStat { + SK_U64 StatTxOkCts; + SK_U64 StatTxOctetsOkCts; + SK_U64 StatTxBroadcastOkCts; + SK_U64 StatTxMulticastOkCts; + SK_U64 StatTxUnicastOkCts; + SK_U64 StatTxLongFramesCts; + SK_U64 StatTxBurstCts; + SK_U64 StatTxPauseMacCtrlCts; + SK_U64 StatTxMacCtrlCts; + SK_U64 StatTxSingleCollisionCts; + SK_U64 StatTxMultipleCollisionCts; + SK_U64 StatTxExcessiveCollisionCts; + SK_U64 StatTxLateCollisionCts; + SK_U64 StatTxDeferralCts; + SK_U64 StatTxExcessiveDeferralCts; + SK_U64 StatTxFifoUnderrunCts; + SK_U64 StatTxCarrierCts; + SK_U64 Dummy1; /* StatTxUtilization */ + SK_U64 StatTx64Cts; + SK_U64 StatTx127Cts; + SK_U64 StatTx255Cts; + SK_U64 StatTx511Cts; + SK_U64 StatTx1023Cts; + SK_U64 StatTxMaxCts; + SK_U64 StatTxSyncCts; + SK_U64 StatTxSyncOctetsCts; + SK_U64 StatRxOkCts; + SK_U64 StatRxOctetsOkCts; + SK_U64 StatRxBroadcastOkCts; + SK_U64 StatRxMulticastOkCts; + SK_U64 StatRxUnicastOkCts; + SK_U64 StatRxPauseMacCtrlCts; + SK_U64 StatRxMacCtrlCts; + SK_U64 StatRxPauseMacCtrlErrorCts; + SK_U64 StatRxMacCtrlUnknownCts; + SK_U64 StatRxBurstCts; + SK_U64 StatRxMissedCts; + SK_U64 StatRxFramingCts; + SK_U64 StatRxFifoOverflowCts; + SK_U64 StatRxJabberCts; + SK_U64 StatRxCarrierCts; + SK_U64 StatRxIRLengthCts; + SK_U64 StatRxSymbolCts; + SK_U64 StatRxShortsCts; + SK_U64 StatRxRuntCts; + SK_U64 StatRxCextCts; + SK_U64 StatRxTooLongCts; + SK_U64 StatRxFcsCts; + SK_U64 Dummy2; /* StatRxUtilization */ + SK_U64 StatRx64Cts; + SK_U64 StatRx127Cts; + SK_U64 StatRx255Cts; + SK_U64 StatRx511Cts; + SK_U64 StatRx1023Cts; + SK_U64 StatRxMaxCts; +} SK_PNMI_STAT; + +typedef struct s_PnmiConf { + char ConfMacCurrentAddr[6]; + char ConfMacFactoryAddr[6]; + SK_U8 ConfPMD; + SK_U8 ConfConnector; + SK_U8 ConfLinkCapability; + SK_U8 ConfLinkMode; + SK_U8 ConfLinkModeStatus; + SK_U8 ConfLinkStatus; + SK_U8 ConfFlowCtrlCapability; + SK_U8 ConfFlowCtrlMode; + SK_U8 ConfFlowCtrlStatus; + SK_U8 ConfPhyOperationCapability; + SK_U8 ConfPhyOperationMode; + SK_U8 ConfPhyOperationStatus; +} SK_PNMI_CONF; + +typedef struct s_PnmiRlmt { + SK_U32 RlmtIndex; + SK_U32 RlmtStatus; + SK_U64 RlmtTxHelloCts; + SK_U64 RlmtRxHelloCts; + SK_U64 RlmtTxSpHelloReqCts; + SK_U64 RlmtRxSpHelloCts; +} SK_PNMI_RLMT; + +typedef struct s_PnmiRlmtMonitor { + SK_U32 RlmtMonitorIndex; + char RlmtMonitorAddr[6]; + SK_U64 RlmtMonitorErrorCts; + SK_U64 RlmtMonitorTimestamp; + SK_U8 RlmtMonitorAdmin; +} SK_PNMI_RLMT_MONITOR; + +typedef struct s_PnmiRequestStatus { + SK_U32 ErrorStatus; + SK_U32 ErrorOffset; +} SK_PNMI_REQUEST_STATUS; + +typedef struct s_PnmiStrucData { + SK_U32 MgmtDBVersion; + SK_PNMI_REQUEST_STATUS ReturnStatus; + SK_U32 VpdFreeBytes; + char VpdEntriesList[SK_PNMI_VPD_DATALEN]; + SK_U32 VpdEntriesNumber; + SK_PNMI_VPD Vpd[SK_PNMI_VPD_ENTRIES]; + SK_U32 PortNumber; + SK_U32 DeviceType; + char DriverDescr[SK_PNMI_STRINGLEN1]; + char DriverVersion[SK_PNMI_STRINGLEN2]; + char HwDescr[SK_PNMI_STRINGLEN1]; + char HwVersion[SK_PNMI_STRINGLEN2]; + SK_U16 Chipset; + SK_U32 Action; + SK_U32 TestResult; + SK_U8 BusType; + SK_U8 BusSpeed; + SK_U8 BusWidth; + SK_U8 SensorNumber; + SK_PNMI_SENSOR Sensor[SK_PNMI_SENSOR_ENTRIES]; + SK_U8 ChecksumNumber; + SK_PNMI_CHECKSUM Checksum[SK_PNMI_CHECKSUM_ENTRIES]; + SK_PNMI_STAT Stat[SK_PNMI_MAC_ENTRIES]; + SK_PNMI_CONF Conf[SK_PNMI_MAC_ENTRIES]; + SK_U8 RlmtMode; + SK_U32 RlmtPortNumber; + SK_U8 RlmtPortActive; + SK_U8 RlmtPortPreferred; + SK_U64 RlmtChangeCts; + SK_U64 RlmtChangeTime; + SK_U64 RlmtChangeEstimate; + SK_U64 RlmtChangeThreshold; + SK_PNMI_RLMT Rlmt[SK_MAX_MACS]; + SK_U32 RlmtMonitorNumber; + SK_PNMI_RLMT_MONITOR RlmtMonitor[SK_PNMI_MONITOR_ENTRIES]; + SK_U32 TrapNumber; + SK_U8 Trap[SK_PNMI_TRAP_QUEUE_LEN]; + SK_U64 TxSwQueueLen; + SK_U64 TxSwQueueMax; + SK_U64 TxRetryCts; + SK_U64 RxIntrCts; + SK_U64 TxIntrCts; + SK_U64 RxNoBufCts; + SK_U64 TxNoBufCts; + SK_U64 TxUsedDescrNo; + SK_U64 RxDeliveredCts; + SK_U64 RxOctetsDeliveredCts; + SK_U64 RxHwErrorsCts; + SK_U64 TxHwErrorsCts; + SK_U64 InErrorsCts; + SK_U64 OutErrorsCts; + SK_U64 ErrRecoveryCts; + SK_U64 SysUpTime; +} SK_PNMI_STRUCT_DATA; + +#define SK_PNMI_STRUCT_SIZE (sizeof(SK_PNMI_STRUCT_DATA)) +#define SK_PNMI_MIN_STRUCT_SIZE ((unsigned int)&(((SK_PNMI_STRUCT_DATA *)0)->\ + VpdFreeBytes)) /* + * ReturnStatus field + * must be located + * before VpdFreeBytes + */ + +/* + * Various definitions + */ +#define SK_PNMI_MAX_PROTOS 3 + +#define SK_PNMI_SCNT_NOT 64 +#define SK_PNMI_CNT_NO 66 + +/* + * Estimate data structure + */ +typedef struct s_PnmiEstimate { + unsigned int EstValueIndex; + SK_U64 EstValue[7]; + SK_U64 Estimate; + SK_TIMER EstTimer; +} SK_PNMI_ESTIMATE; + + +/* + * PNMI specific adatper context structure + */ +typedef struct s_PnmiPort { + SK_U32 CounterHigh[SK_PNMI_SCNT_NOT]; + SK_U64 CounterOffset[SK_PNMI_CNT_NO]; + SK_U64 StatSyncCts; + SK_U64 StatSyncOctetsCts; + SK_BOOL ActiveFlag; +} SK_PNMI_PORT; + +typedef struct s_PnmiData { + SK_PNMI_PORT Port[SK_MAX_MACS]; + SK_U64 VirtualCounterOffset[SK_PNMI_CNT_NO]; + SK_U32 TestResult; + char HwVersion[10]; + + char *pDriverDescription; + char *pDriverVersion; + + char TrapBuf[SK_PNMI_TRAP_QUEUE_LEN]; + unsigned int TrapBufFree; + unsigned int TrapQueueBeg; + unsigned int TrapQueueEnd; + unsigned int TrapBufPad; + unsigned int TrapUnique; + + int MacUpdatedFlag; + int RlmtUpdatedFlag; + int SirqUpdatedFlag; + + SK_U64 RlmtChangeCts; + SK_U64 RlmtChangeTime; + SK_PNMI_ESTIMATE RlmtChangeEstimate; + SK_U64 RlmtChangeThreshold; + + SK_U32 DeviceType; + char PciBusSpeed; + char PciBusWidth; + char PMD; + char Connector; + + SK_U64 TxSwQueueLen; + SK_U64 TxSwQueueMax; + SK_U64 TxRetryCts; + SK_U64 RxIntrCts; + SK_U64 TxIntrCts; + SK_U64 RxNoBufCts; + SK_U64 TxNoBufCts; + SK_U64 TxUsedDescrNo; + SK_U64 RxDeliveredCts; + SK_U64 RxOctetsDeliveredCts; + SK_U64 ErrRecoveryCts; + SK_U64 StartUpTime; +} SK_PNMI; + + +/* + * Function prototypes + */ +extern int SkPnmiInit(SK_AC *pAc, SK_IOC IoC, int level); +extern int SkPnmiGetVar(SK_AC *pAc, SK_IOC IoC, SK_U32 Id, void* pBuf, + unsigned int* pLen, SK_U32 Instance); +extern int SkPnmiPreSetVar(SK_AC *pAc, SK_IOC IoC, SK_U32 Id, + void* pBuf, unsigned int *pLen, SK_U32 Instance); +extern int SkPnmiSetVar(SK_AC *pAc, SK_IOC IoC, SK_U32 Id, void* pBuf, + unsigned int *pLen, SK_U32 Instance); +extern int SkPnmiGetStruct(SK_AC *pAc, SK_IOC IoC, void* pBuf, + unsigned int *pLen); +extern int SkPnmiPreSetStruct(SK_AC *pAc, SK_IOC IoC, void* pBuf, + unsigned int *pLen); +extern int SkPnmiSetStruct(SK_AC *pAc, SK_IOC IoC, void* pBuf, + unsigned int *pLen); +extern int SkPnmiEvent(SK_AC *pAc, SK_IOC IoC, SK_U32 Event, + SK_EVPARA Param); + +#endif diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/h/skgesirq.h linux/drivers/net/sk98lin/h/skgesirq.h --- v2.2.13/linux/drivers/net/sk98lin/h/skgesirq.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/h/skgesirq.h Tue Jan 4 10:12:18 2000 @@ -0,0 +1,161 @@ +/****************************************************************************** + * + * Name: skgesirq.h + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.18 $ + * Date: $Date: 1999/05/19 07:32:59 $ + * Purpose: SK specific Gigabit Ethernet special IRQ functions + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * $Log: skgesirq.h,v $ + * Revision 1.18 1999/05/19 07:32:59 cgoos + * Changes for 1000Base-T. + * + * Revision 1.17 1999/03/12 13:29:31 malthoff + * Move Autonegotiation Error Codes to skgeinit.h. + * + * Revision 1.16 1999/03/08 10:11:28 gklug + * add: AutoNegDone return codes + * + * Revision 1.15 1998/11/18 13:20:53 gklug + * add: different timeouts for active and non-active links + * + * Revision 1.14 1998/11/04 07:18:14 cgoos + * Added prototype for SkXmRxTxEnable. + * + * Revision 1.13 1998/10/21 05:52:23 gklug + * add: parameter DoLoop to InitPhy function + * + * Revision 1.12 1998/10/19 06:45:03 cgoos + * Added prototype for SkXmInitPhy. + * + * Revision 1.11 1998/10/15 14:34:10 gklug + * add: WA_TIME is 500 msec + * + * Revision 1.10 1998/10/14 14:49:41 malthoff + * Remove err log defines E021 and E022. They are + * defined in skgeinit.h now. + * + * Revision 1.9 1998/10/14 14:00:39 gklug + * add: eroor logs for init phys + * + * Revision 1.8 1998/10/14 05:44:05 gklug + * add: E020 + * + * Revision 1.7 1998/10/02 06:24:58 gklug + * add: error messages + * + * Revision 1.6 1998/10/01 07:54:45 gklug + * add: PNMI debug module + * + * Revision 1.5 1998/09/28 13:36:31 malthoff + * Move the bit definitions for Autonegotiation + * and Flow Control to skgeinit.h. + * + * Revision 1.4 1998/09/15 12:29:34 gklug + * add: error logs + * + * Revision 1.3 1998/09/03 13:54:02 gklug + * add: function prototypes + * + * Revision 1.2 1998/09/03 10:24:36 gklug + * add: Events send by PNMI + * add: parameter definition for Flow Control etc. + * + * Revision 1.1 1998/08/27 11:50:27 gklug + * initial revision + * + * + ******************************************************************************/ + +#ifndef _INC_SKGESIRQ_H_ +#define _INC_SKGESIRQ_H_ + +/* + * Define the Event the special IRQ/INI module can handle + */ +#define SK_HWEV_WATIM 1 /* Timeout for WA errata #2 XMAC */ +#define SK_HWEV_PORT_START 2 /* Port Start Event by RLMT */ +#define SK_HWEV_PORT_STOP 3 /* Port Stop Event by RLMT */ +#define SK_HWEV_CLEAR_STAT 4 /* Clear Statistics by PNMI */ +#define SK_HWEV_UPDATE_STAT 5 /* Update Statistics by PNMI */ +#define SK_HWEV_SET_LMODE 6 /* Set Link Mode by PNMI */ +#define SK_HWEV_SET_FLOWMODE 7 /* Set Flow Control Mode by PNMI */ + +#define SK_WA_ACT_TIME (5000000L) /* 5 sec */ +#define SK_WA_INA_TIME (100000L) /* 100 msec */ + +/* + * Define the error numbers and messages + */ +#define SKERR_SIRQ_E001 (SK_ERRBASE_SIRQ+0) +#define SKERR_SIRQ_E001MSG "Unknown event" +#define SKERR_SIRQ_E002 (SKERR_SIRQ_E001+1) +#define SKERR_SIRQ_E002MSG "Packet timeout RX1" +#define SKERR_SIRQ_E003 (SKERR_SIRQ_E002+1) +#define SKERR_SIRQ_E003MSG "Packet timeout RX2" +#define SKERR_SIRQ_E004 (SKERR_SIRQ_E003+1) +#define SKERR_SIRQ_E004MSG "XMAC 1 not correctly initialized" +#define SKERR_SIRQ_E005 (SKERR_SIRQ_E004+1) +#define SKERR_SIRQ_E005MSG "XMAC 2 not correctly initialized" +#define SKERR_SIRQ_E006 (SKERR_SIRQ_E005+1) +#define SKERR_SIRQ_E006MSG "CHECK failure R1" +#define SKERR_SIRQ_E007 (SKERR_SIRQ_E006+1) +#define SKERR_SIRQ_E007MSG "CHECK failure R2" +#define SKERR_SIRQ_E008 (SKERR_SIRQ_E007+1) +#define SKERR_SIRQ_E008MSG "CHECK failure XS1" +#define SKERR_SIRQ_E009 (SKERR_SIRQ_E008+1) +#define SKERR_SIRQ_E009MSG "CHECK failure XA1" +#define SKERR_SIRQ_E010 (SKERR_SIRQ_E009+1) +#define SKERR_SIRQ_E010MSG "CHECK failure XS2" +#define SKERR_SIRQ_E011 (SKERR_SIRQ_E010+1) +#define SKERR_SIRQ_E011MSG "CHECK failure XA2" +#define SKERR_SIRQ_E012 (SKERR_SIRQ_E011+1) +#define SKERR_SIRQ_E012MSG "unexpected IRQ Master error" +#define SKERR_SIRQ_E013 (SKERR_SIRQ_E012+1) +#define SKERR_SIRQ_E013MSG "unexpected IRQ Status error" +#define SKERR_SIRQ_E014 (SKERR_SIRQ_E013+1) +#define SKERR_SIRQ_E014MSG "Parity error on RAM (read)" +#define SKERR_SIRQ_E015 (SKERR_SIRQ_E014+1) +#define SKERR_SIRQ_E015MSG "Parity error on RAM (write)" +#define SKERR_SIRQ_E016 (SKERR_SIRQ_E015+1) +#define SKERR_SIRQ_E016MSG "Parity error MAC 1" +#define SKERR_SIRQ_E017 (SKERR_SIRQ_E016+1) +#define SKERR_SIRQ_E017MSG "Parity error MAC 2" +#define SKERR_SIRQ_E018 (SKERR_SIRQ_E017+1) +#define SKERR_SIRQ_E018MSG "Parity error RX 1" +#define SKERR_SIRQ_E019 (SKERR_SIRQ_E018+1) +#define SKERR_SIRQ_E019MSG "Parity error RX 2" +#define SKERR_SIRQ_E020 (SKERR_SIRQ_E019+1) +#define SKERR_SIRQ_E020MSG "XMAC transmit FIFO underrun" +#define SKERR_SIRQ_E021 (SKERR_SIRQ_E020+1) +#define SKERR_SIRQ_E021MSG "Spurious I2C interrupt" +#define SKERR_SIRQ_E022 (SKERR_SIRQ_E020+1) +#define SKERR_SIRQ_E022MSG "Cable pair swap error" + +extern void SkGeSirqIsr(SK_AC *pAC, SK_IOC IoC, SK_U32 Istatus); +extern int SkGeSirqEvent(SK_AC *pAC, SK_IOC IoC, SK_U32 Event, SK_EVPARA Para); +extern void SkXmInitPhy( SK_AC *pAC, SK_IOC IoC, int Port, SK_BOOL DoLoop); +extern int SkXmRxTxEnable(SK_AC *pAC, SK_IOC IoC, int Port); +extern void SkHWLinkUp(SK_AC *pAC, SK_IOC IoC, int Port); +extern void SkHWLinkDown(SK_AC *pAC, SK_IOC IoC, int Port); + +#endif /* _INC_SKGESIRQ_H_ */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/h/ski2c.h linux/drivers/net/sk98lin/h/ski2c.h --- v2.2.13/linux/drivers/net/sk98lin/h/ski2c.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/h/ski2c.h Tue Jan 4 10:12:18 2000 @@ -0,0 +1,263 @@ +/****************************************************************************** + * + * Name: ski2c.h + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.27 $ + * Date: $Date: 1999/05/20 09:23:10 $ + * Purpose: Defines to access Voltage and Temperature Sensor + * (taken from Monalisa (taken from Concentrator)) + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: ski2c.h,v $ + * Revision 1.27 1999/05/20 09:23:10 cgoos + * Changes for 1000Base-T (Fan sensors). + * + * Revision 1.26 1998/12/01 13:45:47 gklug + * add: InitLevel to I2c struct + * + * Revision 1.25 1998/11/03 06:55:16 gklug + * add: Dummy Reads to I2c struct + * + * Revision 1.24 1998/10/02 14:28:59 cgoos + * Added prototype for SkI2cIsr. + * + * Revision 1.23 1998/09/08 12:20:11 gklug + * add: prototypes for init and read functions + * + * Revision 1.22 1998/09/08 07:37:56 gklug + * add: log error if PCI_IO voltage sensor could not be initialized + * + * Revision 1.21 1998/09/04 08:38:05 malthoff + * Change the values for I2C_READ and I2C_WRITE + * + * Revision 1.20 1998/08/25 07:52:22 gklug + * chg: Timestamps (last) added for logging + * + * Revision 1.19 1998/08/25 06:09:00 gklug + * rmv: warning and error levels of the individual sensors. + * add: timing definitions for sending traps and logging errors + * + * Revision 1.18 1998/08/20 11:41:15 gklug + * chg: omit STRCPY macro by using char * as Sensor Description + * + * Revision 1.17 1998/08/20 11:37:43 gklug + * chg: change Ioc to IoC + * + * Revision 1.16 1998/08/20 11:30:38 gklug + * fix: SenRead declaration + * + * Revision 1.15 1998/08/20 11:27:53 gklug + * fix: Compile bugs with new awrning constants + * + * Revision 1.14 1998/08/20 08:53:12 gklug + * fix: compiler errors + * add: Threshold values + * + * Revision 1.13 1998/08/19 12:21:16 gklug + * fix: remove struct from C files (see CCC) + * add: typedefs for all structs + * + * Revision 1.12 1998/08/19 10:57:41 gklug + * add: Warning levels + * + * Revision 1.11 1998/08/18 08:37:02 malthoff + * Prototypes not required for SK_DIAG. + * + * Revision 1.10 1998/08/17 13:54:00 gklug + * fix: declaration of event function + * + * Revision 1.9 1998/08/17 06:48:39 malthoff + * Remove some unrequired macros. + * Fix the compiler errors. + * + * Revision 1.8 1998/08/14 06:47:19 gklug + * fix: Values are intergers + * + * Revision 1.7 1998/08/14 06:26:05 gklug + * add: Init error message + * + * Revision 1.6 1998/08/13 08:31:08 gklug + * add: Error message + * + * Revision 1.5 1998/08/12 14:32:04 gklug + * add: new error code/message + * + * Revision 1.4 1998/08/12 13:39:08 gklug + * chg: names of error messages + * add: defines for Sensor type and thresholds + * + * Revision 1.3 1998/08/11 07:57:16 gklug + * add: sensor struct + * add: Timeout defines + * add: I2C control struct for pAC + * + * Revision 1.2 1998/07/17 11:29:02 gklug + * rmv: Microwire and SMTPANIC + * + * Revision 1.1 1998/06/19 14:30:10 malthoff + * Created. Sources taken from ML Project. + * + * + ******************************************************************************/ + +/* + * SKI2C.H contains all I2C specific defines + */ + +#ifndef _SKI2C_H_ +#define _SKI2C_H_ + +typedef struct s_Sensor SK_SENSOR; + +#include "h/skgei2c.h" + +/* + * Define the I2c events. + */ +#define SK_I2CEV_IRQ 1 /* IRQ happened Event */ +#define SK_I2CEV_TIM 2 /* Timeout event */ +#define SK_I2CEV_CLEAR 3 /* Clear Mib Values */ + +/* + * Define READ and WRITE Constants. + */ +#define I2C_READ 0 +#define I2C_WRITE 1 +#define I2C_BURST 1 +#define I2C_SIGLE 0 + +#define SKERR_I2C_E001 (SK_ERRBASE_I2C+0) +#define SKERR_I2C_E001MSG "Sensor index unknown" +#define SKERR_I2C_E002 (SKERR_I2C_E001+1) +#define SKERR_I2C_E002MSG "I2C: transfer does not complete.\n" +#define SKERR_I2C_E003 (SKERR_I2C_E002+1) +#define SKERR_I2C_E003MSG "lm80: NAK on device send.\n" +#define SKERR_I2C_E004 (SKERR_I2C_E003+1) +#define SKERR_I2C_E004MSG "lm80: NAK on register send.\n" +#define SKERR_I2C_E005 (SKERR_I2C_E004+1) +#define SKERR_I2C_E005MSG "lm80: NAK on device (2) send.\n" +#define SKERR_I2C_E006 (SKERR_I2C_E005+1) +#define SKERR_I2C_E006MSG "Unknown event" +#define SKERR_I2C_E007 (SKERR_I2C_E006+1) +#define SKERR_I2C_E007MSG "LM80 read out of state" +#define SKERR_I2C_E008 (SKERR_I2C_E007+1) +#define SKERR_I2C_E008MSG "unexpected sensor read completed" +#define SKERR_I2C_E009 (SKERR_I2C_E008+1) +#define SKERR_I2C_E009MSG "WARNING: temperature sensor out of range" +#define SKERR_I2C_E010 (SKERR_I2C_E009+1) +#define SKERR_I2C_E010MSG "WARNING: voltage sensor out of range" +#define SKERR_I2C_E011 (SKERR_I2C_E010+1) +#define SKERR_I2C_E011MSG "ERROR: temperature sensor out of range" +#define SKERR_I2C_E012 (SKERR_I2C_E011+1) +#define SKERR_I2C_E012MSG "ERROR: voltage sensor out of range" +#define SKERR_I2C_E013 (SKERR_I2C_E012+1) +#define SKERR_I2C_E013MSG "ERROR: couldn't init sensor" +#define SKERR_I2C_E014 (SKERR_I2C_E013+1) +#define SKERR_I2C_E014MSG "WARNING: fan sensor out of range" +#define SKERR_I2C_E015 (SKERR_I2C_E014+1) +#define SKERR_I2C_E015MSG "ERROR: fan sensor out of range" + +/* + * Define Timeout values + */ +#define SK_I2C_TIM_LONG 2000000L /* 2 second */ +#define SK_I2C_TIM_SHORT 100000L /* 100 milli second */ + +/* + * Define trap and error log hold times + */ +#ifndef SK_SEN_ERR_TR_HOLD +#define SK_SEN_ERR_TR_HOLD (4*SK_TICKS_PER_SEC) +#endif +#ifndef SK_SEN_ERR_LOG_HOLD +#define SK_SEN_ERR_LOG_HOLD (60*SK_TICKS_PER_SEC) +#endif +#ifndef SK_SEN_WARN_TR_HOLD +#define SK_SEN_WARN_TR_HOLD (15*SK_TICKS_PER_SEC) +#endif +#ifndef SK_SEN_WARN_LOG_HOLD +#define SK_SEN_WARN_LOG_HOLD (15*60*SK_TICKS_PER_SEC) +#endif + +/* + * Defines for SenType + */ +#define SK_SEN_TEMP 1 +#define SK_SEN_VOLT 2 +#define SK_SEN_FAN 3 + +/* + * Define for the ErrorFlag + */ +#define SK_SEN_ERR_OK 1 /* Error Flag: O.K. */ +#define SK_SEN_ERR_WARN 2 /* Error Flag: Warning */ +#define SK_SEN_ERR_ERR 3 /* Error Flag: Error */ + +/* + * Define the Sensor struct + */ +struct s_Sensor { + char *SenDesc; /* Description */ + int SenType; /* Voltage or Temperature */ + SK_I32 SenValue; /* Current value of the sensor */ + SK_I32 SenThreErrHigh; /* High error Threshhold of this sensor */ + SK_I32 SenThreWarnHigh;/* High warning Threshhold of this sensor */ + SK_I32 SenThreErrLow; /* Lower error Threshold of the sensor */ + SK_I32 SenThreWarnLow; /* Lower warning Threshold of the sensor */ + int SenErrFlag; /* Sensor indicated an error */ + SK_BOOL SenInit; /* Is sensor initialized? */ + SK_U64 SenErrCts; /* Error trap counter */ + SK_U64 SenWarnCts; /* Warning trap counter */ + SK_U64 SenBegErrTS; /* Begin error timestamp */ + SK_U64 SenBegWarnTS; /* Begin warning timestamp */ + SK_U64 SenLastErrTrapTS; /* Last error trap timestamp */ + SK_U64 SenLastErrLogTS; /* Last error log timestamp */ + SK_U64 SenLastWarnTrapTS; /* Last warning trap timestamp */ + SK_U64 SenLastWarnLogTS; /* Last warning log timestamp */ + int SenState; /* Sensor State (see HW specific include) */ + int (*SenRead)(SK_AC *pAC, SK_IOC IoC,struct s_Sensor *pSen) ; + /* Sensors read function */ + SK_U16 SenReg; /* Register Address for this sensor */ + SK_U8 SenDev; /* DeviceSelection for this sensor */ +} ; + + +typedef struct s_I2c { + SK_SENSOR SenTable[SK_MAX_SENSORS]; /* Sensor Table */ + int CurrSens; /* Which sensor is currently queried */ + int MaxSens; /* Max. number of sensors */ + int InitLevel; /* Initialized Level */ +#ifndef SK_DIAG + int DummyReads; /* Number of non-checked dummy reads */ + SK_TIMER SenTimer; /* Sensors timer */ +#endif /* !SK_DIAG */ +} SK_I2C; + +extern int SkI2cReadSensor(SK_AC *pAC, SK_IOC IoC, SK_SENSOR *pSen); +#ifndef SK_DIAG +extern int SkI2cEvent(SK_AC *pAC, SK_IOC IoC, SK_U32 Event, + SK_EVPARA Para); +extern int SkI2cInit(SK_AC *pAC, SK_IOC IoC, int Level); +extern void SkI2cIsr(SK_AC *pAC, SK_IOC IoC); + +#endif +#endif /* n_SKI2C_H */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/h/skqueue.h linux/drivers/net/sk98lin/h/skqueue.h --- v2.2.13/linux/drivers/net/sk98lin/h/skqueue.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/h/skqueue.h Tue Jan 4 10:12:18 2000 @@ -0,0 +1,136 @@ +/****************************************************************************** + * + * Name: skqueue.h + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.12 $ + * Date: $Date: 1998/09/08 08:48:01 $ + * Purpose: Defines for the Event queue + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: skqueue.h,v $ + * Revision 1.12 1998/09/08 08:48:01 gklug + * add: init level handling + * + * Revision 1.11 1998/09/03 14:15:11 gklug + * add: CSUM and HWAC Eventclass and function. + * fix: pParaPtr according to CCC + * + * Revision 1.10 1998/08/20 12:43:03 gklug + * add: typedef SK_QUEUE + * + * Revision 1.9 1998/08/19 09:50:59 gklug + * fix: remove struct keyword from c-code (see CCC) add typedefs + * + * Revision 1.8 1998/08/18 07:00:01 gklug + * fix: SK_PTR not defined use void * instead. + * + * Revision 1.7 1998/08/17 13:43:19 gklug + * chg: Parameter will be union of 64bit para, 2 times SK_U32 or SK_PTR + * + * Revision 1.6 1998/08/14 07:09:30 gklug + * fix: chg pAc -> pAC + * + * Revision 1.5 1998/08/11 14:26:44 gklug + * chg: Event Dispatcher returns now int. + * + * Revision 1.4 1998/08/11 12:15:21 gklug + * add: Error numbers of skqueue module + * + * Revision 1.3 1998/08/07 12:54:23 gklug + * fix: first compiled version + * + * Revision 1.2 1998/08/07 09:34:00 gklug + * adapt structure defs to CCC + * add: prototypes for functions + * + * Revision 1.1 1998/07/30 14:52:12 gklug + * Initial version. + * Defines Event Classes, Event structs and queue management variables. + * + * + * + ******************************************************************************/ + +/* + * SKQUEUE.H contains all defines and types for the event queue + */ + +#ifndef _SKQUEUE_H_ +#define _SKQUEUE_H_ + + +/* + * define the event classes to be served + */ +#define SKGE_DRV 1 /* Driver Event Class */ +#define SKGE_RLMT 2 /* RLMT Event Class */ +#define SKGE_I2C 3 /* i2C Event Class */ +#define SKGE_PNMI 4 /* PNMI Event Class */ +#define SKGE_CSUM 5 /* Checksum Event Class */ +#define SKGE_HWAC 6 /* Hardware Access Event Class */ + +/* + * define event queue as circular buffer + */ +#define SK_MAX_EVENT 64 + +/* + * Parameter union for the Para stuff + */ +typedef union u_EvPara { + void *pParaPtr; /* Parameter Pointer */ + SK_U64 Para64; /* Parameter 64bit version */ + SK_U32 Para32[2]; /* Parameter Array of 32bit parameters */ +} SK_EVPARA; + +/* + * Event Queue + * skqueue.c + * events are class/value pairs + * class is addressee, e.g. RMT, PCM etc. + * value is command, e.g. line state change, ring op change etc. + */ +typedef struct s_EventElem { + SK_U32 Class ; /* Event class */ + SK_U32 Event ; /* Event value */ + SK_EVPARA Para ; /* Event parameter */ +} SK_EVENTELEM; + +typedef struct s_Queue { + SK_EVENTELEM EvQueue[SK_MAX_EVENT]; + SK_EVENTELEM *EvPut ; + SK_EVENTELEM *EvGet ; +} SK_QUEUE; + +extern void SkEventInit(SK_AC *pAC, SK_IOC Ioc, int Level); +extern void SkEventQueue(SK_AC *pAC, SK_U32 Class, SK_U32 Event, + SK_EVPARA Para); +extern int SkEventDispatcher(SK_AC *pAC,SK_IOC Ioc); + + +/* Define Error Numbers and messages */ +#define SKERR_Q_E001 (SK_ERRBASE_QUEUE+0) +#define SKERR_Q_E001MSG "Event queue overflow" +#define SKERR_Q_E002 (SKERR_Q_E001+1) +#define SKERR_Q_E002MSG "Undefined event class" +#endif /* _SKQUEUE_H_ */ + diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/h/skrlmt.h linux/drivers/net/sk98lin/h/skrlmt.h --- v2.2.13/linux/drivers/net/sk98lin/h/skrlmt.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/h/skrlmt.h Tue Jan 4 10:12:18 2000 @@ -0,0 +1,494 @@ +/****************************************************************************** + * + * Name: skrlmt.h + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.26 $ + * Date: $Date: 1999/10/04 14:01:19 $ + * Purpose: Header file for Redundant Link ManagemenT. + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: skrlmt.h,v $ + * Revision 1.26 1999/10/04 14:01:19 rassmann + * Corrected reaction to reception of BPDU frames. + * Added parameter descriptions to "For Readme" section skrlmt.txt. + * Clarified usage of lookahead result *pForRlmt. + * Requested driver to present RLMT packets as soon as poosible. + * + * Revision 1.25 1999/07/20 12:53:39 rassmann + * Fixed documentation errors for lookahead macros. + * + * Revision 1.24 1999/05/28 11:15:56 rassmann + * Changed behaviour to reflect Design Spec v1.2. + * Controlling Link LED(s). + * Introduced RLMT Packet Version field in RLMT Packet. + * Newstyle lookahead macros (checking meta-information before looking at + * the packet). + * + * Revision 1.23 1999/01/28 12:50:42 rassmann + * Not using broadcast time stamps in CheckLinkState mode. + * + * Revision 1.22 1999/01/27 14:13:04 rassmann + * Monitoring broadcast traffic. + * Switching more reliably and not too early if switch is + * configured for spanning tree. + * + * Revision 1.21 1998/12/08 13:11:25 rassmann + * Stopping SegTimer at RlmtStop. + * + * Revision 1.20 1998/11/24 12:37:33 rassmann + * Implemented segmentation check. + * + * Revision 1.19 1998/11/17 13:43:06 rassmann + * Handling (logical) tx failure. + * Sending packet on logical address after PORT_SWITCH. + * + * Revision 1.18 1998/11/13 16:56:56 rassmann + * Added macro version of SkRlmtLookaheadPacket. + * + * Revision 1.17 1998/11/06 18:06:05 rassmann + * Corrected timing when RLMT checks fail. + * Clearing tx counter earlier in periodical checks. + * + * Revision 1.16 1998/11/03 13:53:50 rassmann + * RLMT should switch now (at least in mode 3). + * + * Revision 1.15 1998/10/22 11:39:52 rassmann + * Corrected signed/unsigned mismatches. + * Corrected receive list handling and address recognition. + * + * Revision 1.14 1998/10/15 15:16:36 rassmann + * Finished Spanning Tree checking. + * Checked with lint. + * + * Revision 1.13 1998/09/24 19:16:08 rassmann + * Code cleanup. + * Introduced Timer for PORT_DOWN due to no RX. + * + * Revision 1.12 1998/09/16 11:09:52 rassmann + * Syntax corrections. + * + * Revision 1.11 1998/09/15 11:28:50 rassmann + * Syntax corrections. + * + * Revision 1.10 1998/09/14 17:07:38 rassmann + * Added code for port checking via LAN. + * Changed Mbuf definition. + * + * Revision 1.9 1998/09/07 11:14:15 rassmann + * Syntax corrections. + * + * Revision 1.8 1998/09/07 09:06:08 rassmann + * Syntax corrections. + * + * Revision 1.7 1998/09/04 19:41:34 rassmann + * Syntax corrections. + * Started entering code for checking local links. + * + * Revision 1.6 1998/09/04 12:14:28 rassmann + * Interface cleanup. + * + * Revision 1.5 1998/09/02 16:55:29 rassmann + * Updated to reflect new DRV/HWAC/RLMT interface. + * + * Revision 1.4 1998/09/02 07:26:02 afischer + * typedef for SK_RLMT_PORT + * + * Revision 1.3 1998/08/27 14:29:03 rassmann + * Code cleanup. + * + * Revision 1.2 1998/08/27 14:26:25 rassmann + * Updated interface. + * + * Revision 1.1 1998/08/21 08:29:10 rassmann + * First public version. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Description: + * + * This is the header file for Redundant Link ManagemenT. + * + * Include File Hierarchy: + * + * "skdrv1st.h" + * ... + * "sktypes.h" + * "skqueue.h" + * "skaddr.h" + * "skrlmt.h" + * ... + * "skdrv2nd.h" + * + ******************************************************************************/ + +#ifndef __INC_SKRLMT_H +#define __INC_SKRLMT_H + +#ifdef __cplusplus +xxxx /* not supported yet - force error */ +extern "C" { +#endif /* cplusplus */ + +/* defines ********************************************************************/ + +#define SK_RLMT_NET_DOWN_TEMP 1 /* NET_DOWN due to last port down. */ +#define SK_RLMT_NET_DOWN_FINAL 2 /* NET_DOWN due to RLMT_STOP. */ + +/* ----- Default queue sizes - must be multiples of 8 KB ----- */ + +/* Less than 8 KB free in RX queue => pause frames. */ +#define SK_RLMT_STANDBY_QRXSIZE 128 /* Size of rx standby queue in KB. */ +#define SK_RLMT_STANDBY_QXASIZE 32 /* Size of async standby queue in KB. */ +#define SK_RLMT_STANDBY_QXSSIZE 0 /* Size of sync standby queue in KB. */ + +#define SK_RLMT_MAX_TX_BUF_SIZE 60 /* Maximum RLMT transmit size. */ + +/* ----- PORT states ----- */ + +#define SK_RLMT_PS_INIT 0 /* Port state: Init. */ +#define SK_RLMT_PS_LINK_DOWN 1 /* Port state: Link down. */ +#define SK_RLMT_PS_DOWN 2 /* Port state: Port down. */ +#define SK_RLMT_PS_GOING_UP 3 /* Port state: Going up. */ +#define SK_RLMT_PS_UP 4 /* Port state: Up. */ + +/* ----- RLMT states ----- */ + +#define SK_RLMT_RS_INIT 0 /* RLMT state: Init. */ +#define SK_RLMT_RS_NET_DOWN 1 /* RLMT state: Net down. */ +#define SK_RLMT_RS_NET_UP 2 /* RLMT state: Net up. */ + +/* ----- PORT events ----- */ + +#define SK_RLMT_LINK_UP 1001 /* Link came up. */ +#define SK_RLMT_LINK_DOWN 1002 /* Link went down. */ +#define SK_RLMT_PORT_ADDR 1003 /* Port address changed. */ + +/* ----- RLMT events ----- */ + +#define SK_RLMT_START 2001 /* Start RLMT. */ +#define SK_RLMT_STOP 2002 /* Stop RLMT. */ +#define SK_RLMT_PACKET_RECEIVED 2003 /* Packet was received for RLMT. */ +#define SK_RLMT_STATS_CLEAR 2004 /* Clear statistics. */ +#define SK_RLMT_STATS_UPDATE 2005 /* Update statistics. */ +#define SK_RLMT_PREFPORT_CHANGE 2006 /* Change preferred port. */ +#define SK_RLMT_MODE_CHANGE 2007 /* New RlmtMode. */ + +/* ----- RLMT mode bits ----- */ + +#define SK_RLMT_CHECK_LINK 1 /* Check Link. */ +#define SK_RLMT_CHECK_LOC_LINK 2 /* Check other link on same adapter. */ +#define SK_RLMT_CHECK_SEG 4 /* Check segmentation. */ + +#ifndef RLMT_CHECK_REMOTE +#define SK_RLMT_CHECK_OTHERS SK_RLMT_CHECK_LOC_LINK +#else /* RLMT_CHECK_REMOTE */ +#define SK_RLMT_CHECK_REM_LINK 8 /* Check link(s) on other adapter(s). */ +#define SK_RLMT_MAX_REMOTE_PORTS_CHECKED 3 +#define SK_RLMT_CHECK_OTHERS (SK_RLMT_CHECK_LOC_LINK | \ + SK_RLMT_CHECK_REM_LINK) +#endif /* RLMT_CHECK_REMOTE */ + +/* ----- RLMT modes ----- */ + +/* Check Link State. */ +#define SK_RLMT_MODE_CLS (SK_RLMT_CHECK_LINK) + +/* Check Local Ports: check other links on the same adapter. */ +#define SK_RLMT_MODE_CLP (SK_RLMT_CHECK_LINK | \ + SK_RLMT_CHECK_LOC_LINK) + +/* Check Local Ports and Segmentation Status. */ +#define SK_RLMT_MODE_CLPSS (SK_RLMT_CHECK_LINK | \ + SK_RLMT_CHECK_LOC_LINK | \ + SK_RLMT_CHECK_SEG) + +#ifdef RLMT_CHECK_REMOTE +/* Check Local and Remote Ports: check links (local or remote). */ + Name of define TBD! +#define SK_RLMT_MODE_CRP (SK_RLMT_CHECK_LINK | \ + SK_RLMT_CHECK_LOC_LINK | \ + SK_RLMT_CHECK_REM_LINK) + +/* Check Local and Remote Ports and Segmentation Status. */ + Name of define TBD! +#define SK_RLMT_MODE_CRPSS (SK_RLMT_CHECK_LINK | \ + SK_RLMT_CHECK_LOC_LINK | \ + SK_RLMT_CHECK_REM_LINK | \ + SK_RLMT_CHECK_SEG) +#endif /* RLMT_CHECK_REMOTE */ + +/* ----- RLMT lookahead result bits ----- */ + +#define SK_RLMT_RX_RLMT 1 /* Give packet to RLMT. */ +#define SK_RLMT_RX_PROTOCOL 2 /* Give packet to protocol. */ + +/* Macros */ + +#if 0 +SK_AC *pAC /* adapter context */ +SK_U32 PortIdx /* receiving port */ +unsigned PacketLength /* received packet's length */ +SK_BOOL IsBc /* Flag: broadcast received */ +unsigned *pOffset /* Result: offset of bytes to present + to SK_RLMT_LOOKAHEAD */ +unsigned *pNumBytes /* Result: #Bytes to present + to SK_RLMT_LOOKAHEAD */ +#endif /* 0 */ + + +#define SK_RLMT_PRE_LOOKAHEAD(pAC,PortIdx,PacketLength,IsBc,pOffset,pNumBytes) { \ + SK_AC *_pAC; \ + SK_U32 _PortIdx; \ + _pAC = (pAC); \ + _PortIdx = (SK_U32)(PortIdx); \ + _pAC->Rlmt.Port[_PortIdx].PacketsRx++; \ + _pAC->Rlmt.Port[_PortIdx].PacketsPerTimeSlot++; \ + if ((IsBc) && _pAC->Rlmt.RlmtMode != SK_RLMT_MODE_CLS) { \ + *(pOffset) = 6; \ + *(pNumBytes) = 6; \ + } \ + else { \ + *(pOffset) = 0; \ + if ((PacketLength) > SK_RLMT_MAX_TX_BUF_SIZE) { \ + _pAC->Rlmt.Port[_PortIdx].DataPacketsPerTimeSlot++; \ + *(pNumBytes) = 0; \ + } \ + else { \ + *(pNumBytes) = 6; \ + } \ + } \ +} + +#if 0 +SK_AC *pAC /* adapter context */ +SK_U32 PortIdx /* receiving port */ +SK_U8 *pLaPacket, /* received packet's data (points to pOffset) */ +SK_BOOL IsBc /* Flag: broadcast received */ +SK_BOOL IsMc /* Flag: multicast received */ +unsigned *pForRlmt /* Result: bits SK_RLMT_RX_RLMT, + SK_RLMT_RX_PROTOCOL */ +SK_RLMT_LOOKAHEAD() expects *pNumBytes from +packet offset *pOffset (s.a.) at *pLaPacket. + +If you use SK_RLMT_LOOKAHEAD in a path where you already know if the packet is +BC, MC, or UC, you should use constants for IsBc and IsMc, so that your compiler +can trash unneeded parts of the if construction. +#endif /* 0 */ +#define SK_RLMT_LOOKAHEAD(pAC,PortIdx,pLaPacket,IsBc,IsMc,pForRlmt) { \ + SK_AC *_pAC; \ + SK_U32 _PortIdx; \ + SK_U8 *_pLaPacket; \ + _pAC = (pAC); \ + _PortIdx = (SK_U32)(PortIdx); \ + _pLaPacket = (SK_U8 *)(pLaPacket); \ + if (IsBc) {\ + if (!SK_ADDR_EQUAL( \ + _pLaPacket, \ + _pAC->Addr.CurrentMacAddress.a)) { \ + _pAC->Rlmt.Port[_PortIdx].BcTimeStamp = \ + SkOsGetTime(_pAC); \ + } \ + _pAC->Rlmt.Port[_PortIdx].DataPacketsPerTimeSlot++; \ + *(pForRlmt) = SK_RLMT_RX_PROTOCOL; \ + } \ + else if (IsMc) { \ + if (SK_ADDR_EQUAL(_pLaPacket, BridgeMcAddr.a)) { \ + _pAC->Rlmt.Port[_PortIdx].BpduPacketsPerTimeSlot++; \ + if (_pAC->Rlmt.RlmtMode & SK_RLMT_CHECK_SEG) { \ + *(pForRlmt) = SK_RLMT_RX_RLMT | \ + SK_RLMT_RX_PROTOCOL; \ + } \ + else { \ + *(pForRlmt) = SK_RLMT_RX_PROTOCOL; \ + } \ + } \ + else if (SK_ADDR_EQUAL(_pLaPacket, SkRlmtMcAddr.a)) { \ + *(pForRlmt) = SK_RLMT_RX_RLMT; \ + } \ + else { \ + _pAC->Rlmt.Port[_PortIdx].DataPacketsPerTimeSlot++; \ + *(pForRlmt) = SK_RLMT_RX_PROTOCOL; \ + } \ + } \ + else { \ + if (SK_ADDR_EQUAL( \ + _pLaPacket, \ + _pAC->Addr.Port[_PortIdx].CurrentMacAddress.a)) { \ + *(pForRlmt) = SK_RLMT_RX_RLMT; \ + } \ + else { \ + _pAC->Rlmt.Port[_PortIdx].DataPacketsPerTimeSlot++; \ + *(pForRlmt) = SK_RLMT_RX_PROTOCOL; \ + } \ + } \ +} + +#ifdef SK_RLMT_FAST_LOOKAHEAD +Error: SK_RLMT_FAST_LOOKAHEAD no longer used. Use new macros for lookahead. +#endif /* SK_RLMT_FAST_LOOKAHEAD */ + +/* typedefs *******************************************************************/ + +typedef struct s_RootId { + SK_U8 Id[8]; /* Root Bridge Id. */ +} SK_RLMT_ROOT_ID; + +typedef struct s_port { + SK_MAC_ADDR CheckAddr; + SK_BOOL SuspectTx; +} SK_PORT_CHECK; + +typedef struct s_RlmtPort { + +/* ----- Public part (read-only) ----- */ + + SK_U8 PortState; /* Current state of this port. */ + + /* For PNMI */ + + SK_BOOL LinkDown; + SK_BOOL PortDown; + + SK_U64 TxHelloCts; + SK_U64 RxHelloCts; + SK_U64 TxSpHelloReqCts; + SK_U64 RxSpHelloCts; + +/* ----- Private part ----- */ + + SK_BOOL PortStarted; /* Port is started. */ + SK_BOOL PortNoRx; /* NoRx for >= 1 time slot. */ + SK_U32 CheckingState; /* Checking State. */ + + SK_U64 PacketsRx; /* Total packets received. */ + SK_U32 PacketsPerTimeSlot; /* Packets rxed between TOs. */ + SK_U32 DataPacketsPerTimeSlot; /* Data packets ... */ +#if 0 + SK_U32 RlmtAcksPerTimeSlot; /* RLMT Acks rxed in TS. */ + SK_U32 RlmtChksPerTimeSlot; /* RLMT Chks rxed in TS. */ +#endif /* 0 */ + SK_U32 BpduPacketsPerTimeSlot; /* BPDU packets rxed in TS. */ + SK_U64 BcTimeStamp; /* Time of last BC receive. */ + SK_U64 GuTimeStamp; /* Time of entering GOING_UP. */ + + SK_TIMER UpTimer; /* Timer struct Link/Port up. */ + SK_TIMER DownRxTimer; /* Timer struct down rx. */ + SK_TIMER DownTxTimer; /* Timer struct down tx. */ + + SK_U8 Random[4]; /* Random value. */ + unsigned PortsChecked; /* #ports checked. */ + unsigned PortsSuspect; /* #ports checked that are s. */ + SK_PORT_CHECK PortCheck[1]; +/* SK_PORT_CHECK PortCheck[SK_MAX_MACS - 1]; */ + + SK_BOOL RootIdSet; + SK_RLMT_ROOT_ID Root; /* Root Bridge Id. */ +} SK_RLMT_PORT; + +#ifdef SK_RLMT_MBUF_PRIVATE +typedef struct s_RlmtMbuf { + some content +} SK_RLMT_MBUF; +#endif /* SK_RLMT_MBUF_PRIVATE */ + +#ifdef SK_LA_INFO +typedef struct s_Rlmt_PacketInfo { + unsigned PacketLength; /* Length of packet. */ + unsigned PacketType; /* Directed/Multicast/Broadcast. */ +} SK_RLMT_PINFO; +#endif /* SK_LA_INFO */ + +typedef struct s_Rlmt { + +/* ----- Public part (read-only) ----- */ + + SK_U8 RlmtState; /* Current RLMT state. */ + SK_RLMT_PORT Port[SK_MAX_MACS]; /* Array of available ports. */ + SK_U32 PrefPort; /* Preferred port. */ + + /* For PNMI */ + + SK_U32 RlmtMode; /* Check ... */ + SK_U32 MacActive; /* Active port. */ + SK_U32 MacPreferred; /* 0xFFFFFFFF: Automatic. */ + +/* ----- Private part ----- */ + + int LinksUp; /* #Links up. */ + int PortsUp; /* #Ports up. */ + SK_U32 TimeoutValue; /* RLMT timeout value. */ + SK_TIMER LocTimer; /* Timer struct. */ + + SK_U32 CheckingState; /* Checking State. */ + SK_BOOL RootIdSet; + SK_RLMT_ROOT_ID Root; /* Root Bridge Id. */ + SK_TIMER SegTimer; /* Timer struct. */ +} SK_RLMT; + +extern SK_MAC_ADDR BridgeMcAddr; +extern SK_MAC_ADDR SkRlmtMcAddr; + +/* function prototypes ********************************************************/ + + +#ifndef SK_KR_PROTO + +/* Functions provided by SkRlmt */ + +/* ANSI/C++ compliant function prototypes */ + +extern void SkRlmtInit( + SK_AC *pAC, + SK_IOC IoC, + int Level); + +#ifdef SK_RLMT_SLOW_LOOKAHEAD +extern SK_BOOL SkRlmtLookaheadPacket( + SK_AC *pAC, + SK_U32 PortIdx, + SK_U8 *pLaPacket, + unsigned PacketLength, + unsigned LaLength); +#endif /* SK_RLMT_SLOW_LOOKAHEAD */ + +extern int SkRlmtEvent( + SK_AC *pAC, + SK_IOC IoC, + SK_U32 Event, + SK_EVPARA Para); + +#else /* defined(SK_KR_PROTO)) */ + +/* Non-ANSI/C++ compliant function prototypes */ + +xxxx /* not supported yet - force error */ + +#endif /* defined(SK_KR_PROTO)) */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __INC_SKRLMT_H */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/h/sktimer.h linux/drivers/net/sk98lin/h/sktimer.h --- v2.2.13/linux/drivers/net/sk98lin/h/sktimer.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/h/sktimer.h Tue Jan 4 10:12:18 2000 @@ -0,0 +1,96 @@ +/****************************************************************************** + * + * Name: sktimer.h + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.8 $ + * Date: $Date: 1998/09/08 08:48:02 $ + * Purpose: Defines for the timer functions + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: sktimer.h,v $ + * Revision 1.8 1998/09/08 08:48:02 gklug + * add: init level handling + * + * Revision 1.7 1998/08/20 12:31:29 gklug + * fix: SK_TIMCTRL needs to be defined + * + * Revision 1.6 1998/08/19 09:51:00 gklug + * fix: remove struct keyword from c-code (see CCC) add typedefs + * + * Revision 1.5 1998/08/17 13:43:21 gklug + * chg: Parameter will be union of 64bit para, 2 times SK_U32 or SK_PTR + * + * Revision 1.4 1998/08/14 07:09:31 gklug + * fix: chg pAc -> pAC + * + * Revision 1.3 1998/08/07 12:54:24 gklug + * fix: first compiled version + * + * Revision 1.2 1998/08/07 09:35:29 gklug + * add: Timer control struct for Adapters context + * add: function prototypes + * + * Revision 1.1 1998/08/05 11:27:01 gklug + * First version: adapted from SMT + * + * + ******************************************************************************/ + +/* + * SKTIMER.H contains all defines and types for the timer functions + */ + +#ifndef _SKTIMER_H_ +#define _SKTIMER_H_ + +#include "h/skqueue.h" + +/* + * SK timer + * - needed wherever a timer is used. Put this in your data structure + * wherever you want. + */ +typedef struct s_Timer SK_TIMER; + +struct s_Timer { + SK_TIMER *TmNext ; /* linked list */ + SK_U32 TmClass ; /* Timer Event class */ + SK_U32 TmEvent ; /* Timer Event value */ + SK_EVPARA TmPara ; /* Timer Event parameter */ + SK_U32 TmDelta ; /* delta time */ + int TmActive ; /* flag : active/inactive */ +} ; + +/* + * Timer control struct. + * - use in Adapters context name pAC->Tim + */ +typedef struct s_TimCtrl { + SK_TIMER *StQueue ; /* Head of Timer queue */ +} SK_TIMCTRL ; + +extern void SkTimerInit(SK_AC *pAC,SK_IOC Ioc, int Level); +extern void SkTimerStop(SK_AC *pAC,SK_IOC Ioc,SK_TIMER *pTimer); +extern void SkTimerStart(SK_AC *pAC,SK_IOC Ioc,SK_TIMER *pTimer, + SK_U32 Time,SK_U32 Class,SK_U32 Event,SK_EVPARA Para); +extern void SkTimerDone(SK_AC *pAC,SK_IOC Ioc); +#endif /* _SKTIMER_H_ */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/h/sktypes.h linux/drivers/net/sk98lin/h/sktypes.h --- v2.2.13/linux/drivers/net/sk98lin/h/sktypes.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/h/sktypes.h Tue Jan 4 10:12:18 2000 @@ -0,0 +1,81 @@ +/****************************************************************************** + * + * Name: sktypes.h + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.1 $ + * Date: $Date: 1999/02/16 07:41:40 $ + * Purpose: Define data types for Linux + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + + /***************************************************************************** + * + * History: + * + * $Log: sktypes.h,v $ + * Revision 1.1 1999/02/16 07:41:40 cgoos + * First version. + * + * + * + *****************************************************************************/ + +/****************************************************************************** + * + * Description: + * + * In this file, all data types that are needed by the common modules + * are mapped to Linux data types. + * + * + * Include File Hierarchy: + * + * + ******************************************************************************/ + +#ifndef __INC_SKTYPES_H +#define __INC_SKTYPES_H + + +/* defines *******************************************************************/ + +/* + * Data types with a specific size. 'I' = signed, 'U' = unsigned. + */ +#define SK_I8 s8 +#define SK_U8 u8 +#define SK_I16 s16 +#define SK_U16 u16 +#define SK_I32 s32 +#define SK_U32 u32 +#define SK_I64 s64 +#define SK_U64 u64 + +#define SK_UPTR ulong /* casting pointer <-> integral */ + +/* +* Boolean type. +*/ +#define SK_BOOL SK_U8 +#define SK_FALSE 0 +#define SK_TRUE (!SK_FALSE) + +/* typedefs *******************************************************************/ + +/* function prototypes ********************************************************/ + +#endif /* __INC_SKTYPES_H */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/h/skvpd.h linux/drivers/net/sk98lin/h/skvpd.h --- v2.2.13/linux/drivers/net/sk98lin/h/skvpd.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/h/skvpd.h Tue Jan 4 10:12:18 2000 @@ -0,0 +1,295 @@ +/****************************************************************************** + * + * Name: skvpd.h + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.8 $ + * Date: $Date: 1999/03/11 14:26:40 $ + * Purpose: Defines and Macros for VPD handling + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: skvpd.h,v $ + * Revision 1.8 1999/03/11 14:26:40 malthoff + * Replace __STDC__ with SK_KR_PROTO. + * + * Revision 1.7 1998/10/28 07:27:17 gklug + * rmv: SWAP macros + * add: VPD_IN/OUT8 macros + * chg: interface definition + * + * Revision 1.6 1998/10/22 10:03:44 gklug + * fix: use SK_OUT16 instead of SK_OUTW + * + * Revision 1.5 1998/10/14 07:05:31 cgoos + * Changed constants in SK_SWAP_32 to UL. + * + * Revision 1.4 1998/08/19 08:14:09 gklug + * fix: remove struct keyword as much as possible from the c-code (see CCC) + * + * Revision 1.3 1998/08/18 08:18:56 malthoff + * Modify VPD in and out macros for SK_DIAG + * + * Revision 1.2 1998/07/03 14:49:08 malthoff + * Add VPD_INxx() and VPD_OUTxx() macros for the Diagnostics tool. + * + * Revision 1.1 1998/06/19 14:08:03 malthoff + * Created. + * + * + ******************************************************************************/ + +/* + * skvpd.h contains Diagnostic specific defines for VPD handling + */ + +#ifndef __INC_SKVPD_H_ +#define __INC_SKVPD_H_ + +/* + * Define Resource Type Identifiers and VPD keywords + */ +#define RES_ID 0x82 /* Resource Type ID String (Product Name) */ +#define RES_VPD_R 0x90 /* start of VPD read only area */ +#define RES_VPD_W 0x91 /* start of VPD read/write area */ +#define RES_END 0x78 /* Resource Type End Tag */ + +#ifndef VPD_NAME +#define VPD_NAME "Name" /* Product Name, VPD name of RES_ID */ +#endif /* VPD_NAME */ +#define VPD_PN "PN" /* Adapter Part Number */ +#define VPD_EC "EC" /* Adapter Engineering Level */ +#define VPD_MN "MN" /* Manufacture ID */ +#define VPD_SN "SN" /* Serial Number */ +#define VPD_CP "CP" /* Extended Capability */ +#define VPD_RV "RV" /* Checksum and Reserved */ +#define VPD_YA "YA" /* Asset Tag Identifier */ +#define VPD_VL "VL" /* First Error Log Message (SK specific) */ +#define VPD_VF "VF" /* Second Error Log Message (SK specific) */ +#define VPD_RW "RW" /* Remaining Read / Write Area */ + +/* 'type' values for vpd_setup_para() */ +#define VPD_RO_KEY 1 /* RO keys are "PN", "EC", "MN", "SN", "RV" */ +#define VPD_RW_KEY 2 /* RW keys are "Yx", "Vx", and "RW" */ + +/* 'op' values for vpd_setup_para() */ +#define ADD_KEY 1 /* add the key at the pos "RV" or "RW" */ +#define OWR_KEY 2 /* overwrite key if already exists */ + +/* + * Define READ and WRITE Constants. + */ +#define VPD_SIZE 512 +#define VPD_READ 0x0000 +#define VPD_WRITE 0x8000 + +#define VPD_STOP(pAC,IoC) VPD_OUT16(pAC,IoC,PCI_VPD_ADR_REG,VPD_WRITE) + +#define VPD_GET_RES_LEN(p) ((unsigned int) \ + (* (SK_U8 *)&(p)[1]) |\ + ((* (SK_U8 *)&(p)[2]) << 8)) +#define VPD_GET_VPD_LEN(p) ((unsigned int)(* (SK_U8 *)&(p)[2])) +#define VPD_GET_VAL(p) ((char *)&(p)[3]) + +#define VPD_MAX_LEN 50 + +/* VPD status */ + /* bit 7..1 reserved */ +#define VPD_VALID (1<<0) /* VPD data buffer, vpd_free_ro, */ + /* and vpd_free_rw valid */ + +/* + * VPD structs + */ +typedef struct s_vpd_status { + unsigned short vpd_status ; /* VPD status, description see above */ + int vpd_free_ro ; /* unused bytes in read only area */ + int vpd_free_rw ; /* bytes available in read/write area */ +} SK_VPD_STATUS; + +typedef struct s_vpd { + SK_VPD_STATUS v ; /* VPD status structure */ + char vpd_buf[VPD_SIZE] ; /* VPD buffer */ +} SK_VPD; + +typedef struct s_vpd_para { + unsigned int p_len ; /* parameter length */ + char *p_val ; /* points to the value */ +} SK_VPD_PARA; + +/* + * structure of Large Resource Type Identifiers + */ +/* was removed, because of alignment problems */ + +/* + * sturcture of VPD keywords + */ +typedef struct s_vpd_key { + char p_key[2] ; /* 2 bytes ID string */ + unsigned char p_len ; /* 1 byte length */ + char p_val ; /* start of the value string */ +} SK_VPD_KEY; + + +/* + * System specific VPD macros + */ +#ifndef SKDIAG +#ifndef VPD_DO_IO +#define VPD_OUT8(pAC,IoC,Addr,Val) (void)SkPciWriteCfgByte(pAC,Addr,Val) +#define VPD_OUT16(pAC,IoC,Addr,Val) (void)SkPciWriteCfgWord(pAC,Addr,Val) +#define VPD_OUT32(pAC,IoC,Addr,Val) (void)SkPciWriteCfgDWord(pAC,Addr,Val) +#define VPD_IN8(pAC,IoC,Addr,pVal) (void)SkPciReadCfgByte(pAC,Addr,pVal) +#define VPD_IN16(pAC,IoC,Addr,pVal) (void)SkPciReadCfgWord(pAC,Addr,pVal) +#define VPD_IN32(pAC,IoC,Addr,pVal) (void)SkPciReadCfgDWord(pAC,Addr,pVal) +#else /* VPD_DO_IO */ +#define VPD_OUT8(pAC,IoC,Addr,Val) SK_OUT8(IoC,PCI_C(Addr),Val) +#define VPD_OUT16(pAC,IoC,Addr,Val) SK_OUT16(IoC,PCI_C(Addr),Val) +#define VPD_OUT32(pAC,IoC,Addr,Val) SK_OUT32(IoC,PCI_C(Addr),Val) +#define VPD_IN8(pAC,IoC,Addr,pVal) SK_IN8(IoC,PCI_C(Addr),pVal) +#define VPD_IN16(pAC,IoC,Addr,pVal) SK_IN16(IoC,PCI_C(Addr),pVal) +#define VPD_IN32(pAC,IoC,Addr,pVal) SK_IN32(IoC,PCI_C(Addr),pVal) +#endif /* VPD_DO_IO */ +#else /* SKDIAG */ +#define VPD_OUT8(pAC,Ioc,Addr,Val) { \ + if ((pAC)->DgT.DgUseCfgCycle) \ + SkPciWriteCfgByte(pAC,Addr,Val) ; \ + else \ + SK_OUT8(pAC,PCI_C(Addr),Val); \ + } +#define VPD_OUT16(pAC,Ioc,Addr,Val) { \ + if ((pAC)->DgT.DgUseCfgCycle) \ + SkPciWriteCfgWord(pAC,Addr,Val) ; \ + else \ + SK_OUT16(pAC,PCI_C(Addr),Val); \ + } +#define VPD_OUT32(pAC,Ioc,Addr,Val) { \ + if ((pAC)->DgT.DgUseCfgCycle) \ + SkPciWriteCfgDWord(pAC,Addr,Val) ; \ + else \ + SK_OUT32(pAC,PCI_C(Addr),Val); \ + } +#define VPD_IN8(pAC,Ioc,Addr,pVal) { \ + if ((pAC)->DgT.DgUseCfgCycle) \ + SkPciReadCfgByte(pAC,Addr,pVal) ; \ + else \ + SK_IN8(pAC,PCI_C(Addr),pVal); \ + } +#define VPD_IN16(pAC,Ioc,Addr,pVal) { \ + if ((pAC)->DgT.DgUseCfgCycle) \ + SkPciReadCfgWord(pAC,Addr,pVal) ; \ + else \ + SK_IN16(pAC,PCI_C(Addr),pVal); \ + } +#define VPD_IN32(pAC,Ioc,Addr,pVal) { \ + if ((pAC)->DgT.DgUseCfgCycle) \ + SkPciReadCfgDWord(pAC,Addr,pVal) ; \ + else \ + SK_IN32(pAC,PCI_C(Addr),pVal); \ + } +#endif /* nSKDIAG */ + +/* function prototypes ********************************************************/ + +#ifndef SK_KR_PROTO +extern SK_U32 VpdReadDWord( + SK_AC *pAC, + SK_IOC IoC, + int addr) ; + +extern int VpdSetupPara( + SK_AC *pAC, + char *key, + char *buf, + int len, + int type, + int op) ; + +extern SK_VPD_STATUS *VpdStat( + SK_AC *pAC, + SK_IOC IoC) ; + +extern int VpdKeys( + SK_AC *pAC, + SK_IOC IoC, + char *buf, + int *len, + int *elements) ; + +extern int VpdRead( + SK_AC *pAC, + SK_IOC IoC, + char *key, + char *buf, + int *len) ; + +extern SK_BOOL VpdMayWrite( + char *key) ; + +extern int VpdWrite( + SK_AC *pAC, + SK_IOC IoC, + char *key, + char *buf) ; + +extern int VpdDelete( + SK_AC *pAC, + SK_IOC IoC, + char *key) ; + +extern int VpdUpdate( + SK_AC *pAC, + SK_IOC IoC); + +extern void VpdErrLog( + SK_AC *pAC, + SK_IOC IoC, + char *msg) ; + +#ifdef SKDIAG +extern int VpdReadBlock( + SK_AC *pAC, + SK_IOC IoC, + char *buf, + int addr, + int len) ; + +extern int VpdWriteBlock( + SK_AC *pAC, + SK_IOC IoC, + char *buf, + int addr, + int len) ; +#endif /* SKDIAG */ +#else /* SK_KR_PROTO */ +extern SK_U32 VpdReadDWord() ; +extern int VpdSetupPara() ; +extern SK_VPD_STATUS *VpdStat() ; +extern int VpdKeys() ; +extern int VpdRead() ; +extern SK_BOOL VpdMayWrite() ; +extern int VpdWrite() ; +extern int VpdDelete() ; +extern int VpdUpdate() ; +extern void VpdErrLog() ; +#endif /* SK_KR_PROTO */ + +#endif /* __INC_SKVPD_H_ */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/h/xmac_ii.h linux/drivers/net/sk98lin/h/xmac_ii.h --- v2.2.13/linux/drivers/net/sk98lin/h/xmac_ii.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/h/xmac_ii.h Tue Jan 4 10:12:18 2000 @@ -0,0 +1,1155 @@ +/****************************************************************************** + * + * Name: xmac_ii.h + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.25 $ + * Date: $Date: 1999/08/12 19:19:38 $ + * Purpose: Defines and Macros for XaQti's Gigabit Ethernet Controller + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: xmac_ii.h,v $ + * Revision 1.25 1999/08/12 19:19:38 malthoff + * Add PHY_B_AC_TX_TST bit according to BCOM A1 errata sheet. + * + * Revision 1.24 1999/07/30 11:27:21 cgoos + * Fixed a missing end-of-comment. + * + * Revision 1.23 1999/07/30 07:03:31 malthoff + * Cut some long comments. + * Correct the XMAC PHY ID definitions. + * + * Revision 1.22 1999/05/19 07:33:18 cgoos + * Changes for 1000Base-T. + * + * Revision 1.21 1999/03/25 07:46:11 malthoff + * Add XM_HW_CFG, XM_TS_READ, and XM_TS_LOAD registers. + * + * Revision 1.20 1999/03/12 13:36:09 malthoff + * Remove __STDC__. + * + * Revision 1.19 1998/12/10 12:22:54 gklug + * fix: RX_PAGE must be in interrupt mask + * + * Revision 1.18 1998/12/10 10:36:36 gklug + * fix: swap of pause bits + * + * Revision 1.17 1998/11/18 13:21:45 gklug + * fix: Default interrupt mask + * + * Revision 1.16 1998/10/29 15:53:21 gklug + * fix: Default mask uses ASS (GP0) signal + * + * Revision 1.15 1998/10/28 13:52:52 malthoff + * Add new bits in RX_CMD register. + * + * Revision 1.14 1998/10/19 15:34:53 gklug + * fix: typos + * + * Revision 1.13 1998/10/14 07:19:03 malthoff + * bug fix: Every define which describes bit 31 + * must be declared as unsigned long 'UL'. + * fix bit definitions of PHY_AN_RFB and PHY_AN_PAUSE. + * Remove ANP defines. Rework the RFB defines. + * + * Revision 1.12 1998/10/14 06:22:44 cgoos + * Changed shifted constant to ULONG. + * + * Revision 1.11 1998/10/14 05:43:26 gklug + * add: shift pause codeing + * fix: PAUSE bits definition + * + * Revision 1.10 1998/10/13 09:19:21 malthoff + * Again change XMR_FS_ANY_ERR because of new info from XaQti. + * + * Revision 1.9 1998/10/09 07:58:30 malthoff + * Add XMR_FS_FCS_ERR to XMR_FS_ANY_ERR. + * + * Revision 1.8 1998/10/09 07:18:17 malthoff + * bug fix of a bug fix: XM_PAUSE_MODE and XM_DEF_MODE + * are not inverted! Bug XM_DEF_MSK is inverted. + * + * Revision 1.7 1998/10/05 08:04:32 malthoff + * bug fix: XM_PAUSE_MODE and XM_DEF_MODE + * must be inverted declarations. + * + * Revision 1.6 1998/09/28 13:38:18 malthoff + * Add default modes and masks XM_DEF_MSK, + * XM_PAUSE_MODE and XM_DEF_MODE + * + * Revision 1.5 1998/09/16 14:42:04 malthoff + * Bug Fix: XM_GP_PORT is a 32 bit (not a 16 bit) register. + * + * Revision 1.4 1998/08/20 14:59:47 malthoff + * Rework this file after reading the XaQti data sheet + * "Differences between Rev. B2 & Rev. C XMAC II". + * This file is now 100% XMAC II Rev. C complained. + * + * Revision 1.3 1998/06/29 12:18:23 malthoff + * Correct XMR_FS_ANY_ERR definition. + * + * Revision 1.2 1998/06/29 12:10:56 malthoff + * Add define XMR_FS_ANY_ERR. + * + * Revision 1.1 1998/06/19 13:37:17 malthoff + * created. + * + * + ******************************************************************************/ + +#ifndef __INC_XMAC_H +#define __INC_XMAC_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* defines ********************************************************************/ + +/* + * XMAC II registers + * + * The XMAC registers are 16 or 32 bits wide. The XMACs host processor + * interface is set to 16 bit mode, therefore ALL registers will be + * addressed with 16 bit accesses. + * + * The following macros are provided to access the XMAC registers + * XM_IN16(), XM_OUT16, XM_IN32(), MX_OUT32(), XM_INADR(), XM_OUTADR(), + * XM_INHASH(), and XM_OUTHASH(). + * The macros are defined in SkGeHw.h. + * + * Note: NA reg = Network Address e.g DA, SA etc. + * + */ +#define XM_MMU_CMD 0x0000 /* 16 bit r/w MMU Command Register */ + /* 0x0004: reserved */ +#define XM_POFF 0x0008 /* 32 bit r/w Packet Offset Register */ +#define XM_BURST 0x000c /* 32 bit r/w Burst Register for half duplex*/ +#define XM_1L_VLAN_TAG 0x0010 /* 16 bit r/w One Level VLAN Tag ID */ +#define XM_2L_VLAN_TAG 0x0014 /* 16 bit r/w Two Level VLAN Tag ID */ + /* 0x0018 - 0x001e: reserved */ +#define XM_TX_CMD 0x0020 /* 16 bit r/w Transmit Command Register */ +#define XM_TX_RT_LIM 0x0024 /* 16 bit r/w Transmit Retry Limit Register */ +#define XM_TX_STIME 0x0028 /* 16 bit r/w Transmit Slottime Register */ +#define XM_TX_IPG 0x002c /* 16 bit r/w Transmit Inter Packet Gap */ +#define XM_RX_CMD 0x0030 /* 16 bit r/w Receive Command Register */ +#define XM_PHY_ADDR 0x0034 /* 16 bit r/w PHY Address Register */ +#define XM_PHY_DATA 0x0038 /* 16 bit r/w PHY Data Register */ + /* 0x003c: reserved */ +#define XM_GP_PORT 0x0040 /* 32 bit r/w General Purpose Port Register */ +#define XM_IMSK 0x0044 /* 16 bit r/w Interrupt Mask Register */ +#define XM_ISRC 0x0048 /* 16 bit ro Interrupt Status Register */ +#define XM_HW_CFG 0x004c /* 16 bit r/w Hardware Config Register */ + /* 0x0050 - 0x005e: reserved */ +#define XM_TX_LO_WM 0x0060 /* 16 bit r/w Tx FIFO Low Water Mark */ +#define XM_TX_HI_WM 0x0062 /* 16 bit r/w Tx FIFO High Water Mark */ +#define XM_TX_THR 0x0064 /* 16 bit r/w Tx Request Threshold */ +#define XM_HT_THR 0x0066 /* 16 bit r/w Host Request Threshold */ +#define XM_PAUSE_DA 0x0068 /* NA reg r/w Pause Destination Address */ + /* 0x006e: reserved */ +#define XM_CTL_PARA 0x0070 /* 32 bit r/w Control Parameter Register */ +#define XM_MAC_OPCODE 0x0074 /* 16 bit r/w Opcode for MAC control frames */ +#define XM_MAC_PTIME 0x0076 /* 16 bit r/w Pause time for MAC ctrl frames*/ +#define XM_TX_STAT 0x0078 /* 32 bit ro Tx Status LIFO Register */ + + /* 0x0080 - 0x00fc: 16 NA reg r/w Exakt Match Address Registers */ + /* use the XM_EMX() macro to address */ +#define XM_EXM_START 0x0080 /* r/w Start Address of the EXM Regs */ + + /* + * XM_EXM(Reg) + * + * returns the XMAC address offset off specified Exakt Match Addr Reg + * + * para: Reg EXM register to addr (0 .. 15) + * + * usage: XM_INADDR(XMAC_1,pAC,XM_EXM(i),&val[i]) ; + */ +#define XM_EXM(Reg) (XM_EXM_START + ((Reg) << 3)) + +#define XM_SRC_CHK 0x0100 /* NA reg r/w Source Check Address Register */ +#define XM_SA 0x0108 /* NA reg r/w Station Address Register */ +#define XM_HSM 0x0110 /* 64 bit r/w Hash Match Address Registers */ +#define XM_RX_LO_WM 0x0118 /* 16 bit r/w Receive Low Water Mark */ +#define XM_RX_HI_WM 0x011a /* 16 bit r/w Receive High Water Mark */ +#define XM_RX_THR 0x011c /* 32 bit r/w Receive Request Threshold */ +#define XM_DEV_ID 0x0120 /* 32 bit ro Device ID Register */ +#define XM_MODE 0x0124 /* 32 bit r/w Mode Register */ +#define XM_LSA 0x0128 /* NA reg ro Last Source Register */ + /* 0x012e: reserved */ +#define XM_TS_READ 0x0130 /* 32 bit ro TimeStamp Read Regeister */ +#define XM_TS_LOAD 0x0134 /* 32 bit ro TimeStamp Load Value */ + /* 0x0138 - 0x01fe: reserved */ +#define XM_STAT_CMD 0x0200 /* 16 bit r/w Statistics Command Register */ +#define XM_RX_CNT_EV 0x0204 /* 32 bit ro Rx Counter Event Register */ +#define XM_TX_CNT_EV 0x0208 /* 32 bit ro Tx Counter Event Register */ +#define XM_RX_EV_MSK 0x020c /* 32 bit r/w Rx Counter Event Mask */ +#define XM_TX_EV_MSK 0x0210 /* 32 bit r/w Tx Counter Event Mask */ + /* 0x0204 - 0x027e: reserved */ +#define XM_TXF_OK 0x0280 /* 32 bit ro Frames Transmitted OK Conuter */ +#define XM_TXO_OK_HI 0x0284 /* 32 bit ro Octets Transmitted OK High Cnt*/ +#define XM_TXO_OK_LO 0x0288 /* 32 bit ro Octets Transmitted OK Low Cnt */ +#define XM_TXF_BC_OK 0x028c /* 32 bit ro Broadcast Frames Xmitted OK */ +#define XM_TXF_MC_OK 0x0290 /* 32 bit ro Multicast Frames Xmitted OK */ +#define XM_TXF_UC_OK 0x0294 /* 32 bit ro Unicast Frames Xmitted OK */ +#define XM_TXF_LONG 0x0298 /* 32 bit ro Tx Long Frame Counter */ +#define XM_TXE_BURST 0x029c /* 32 bit ro Tx Burst Event Counter */ +#define XM_TXF_MPAUSE 0x02a0 /* 32 bit ro Tx Pause MAC Ctrl Frame Cnt */ +#define XM_TXF_MCTRL 0x02a4 /* 32 bit ro Tx MAC Ctrl Frame Counter */ +#define XM_TXF_SNG_COL 0x02a8 /* 32 bit ro Tx Single Colliosion Counter */ +#define XM_TXF_MUL_COL 0x02ac /* 32 bit ro Tx Multiple Collision Counter */ +#define XM_TXF_ABO_COL 0x02b0 /* 32 bit ro Tx aborted due to Exessive Col*/ +#define XM_TXF_LAT_COL 0x02b4 /* 32 bit ro Tx Late Collision Counter */ +#define XM_TXF_DEF 0x02b8 /* 32 bit ro Tx Deferred Frame Counter */ +#define XM_TXF_EX_DEF 0x02bc /* 32 bit ro Tx Excessive Deferall Counter */ +#define XM_TXE_FIFO_UR 0x02c0 /* 32 bit ro Tx FIFO Underrun Event Cnt */ +#define XM_TXE_CS_ERR 0x02c4 /* 32 bit ro Tx Carrier Sence Error Cnt */ +#define XM_TXP_UTIL 0x02c8 /* 32 bit ro Tx Utilization in % */ + /* 0x02cc - 0x02ce: reserved */ +#define XM_TXF_64B 0x02d0 /* 32 bit ro 64 Byte Tx Frame Counter */ +#define XM_TXF_127B 0x02d4 /* 32 bit ro 65-127 Byte Tx Frame Counter */ +#define XM_TXF_255B 0x02d8 /* 32 bit ro 128-255 Byte Tx Frame Counter */ +#define XM_TXF_511B 0x02dc /* 32 bit ro 256-511 Byte Tx Frame Counter */ +#define XM_TXF_1023B 0x02e0 /* 32 bit ro 512-1023 Byte Tx Frame Counter*/ +#define XM_TXF_MAX_SZ 0x02e4 /* 32 bit ro 1024-MaxSize Byte Tx Frame Cnt*/ + /* 0x02e8 - 0x02fe: reserved */ +#define XM_RXF_OK 0x0300 /* 32 bit ro Frames Received OK */ +#define XM_RXO_OK_HI 0x0304 /* 32 bit ro Octets Received OK High Cnt */ +#define XM_RXO_OK_LO 0x0308 /* 32 bit ro Octets Received OK Low Counter*/ +#define XM_RXF_BC_OK 0x030c /* 32 bit ro Broadcast Frames Received OK */ +#define XM_RXF_MC_OK 0x0310 /* 32 bit ro Multicast Frames Received OK */ +#define XM_RXF_UC_OK 0x0314 /* 32 bit ro Unicast Frames Received OK */ +#define XM_RXF_MPAUSE 0x0318 /* 32 bit ro Rx Pause MAC Ctrl Frame Cnt */ +#define XM_RXF_MCTRL 0x031c /* 32 bit ro Rx MAC Ctrl Frame Counter */ +#define XM_RXF_INV_MP 0x0320 /* 32 bit ro Rx invalid Pause Frame Cnt */ +#define XM_RXF_INV_MOC 0x0324 /* 32 bit ro Rx Frames with inv. MAC Opcode*/ +#define XM_RXE_BURST 0x0328 /* 32 bit ro Rx Burst Event Counter */ +#define XM_RXE_FMISS 0x032c /* 32 bit ro Rx Missed Frames Event Cnt */ +#define XM_RXF_FRA_ERR 0x0330 /* 32 bit ro Rx Framing Error Counter */ +#define XM_RXE_FIFO_OV 0x0334 /* 32 bit ro Rx FIFO overflow Event Cnt */ +#define XM_RXF_JAB_PKT 0x0338 /* 32 bit ro Rx Jabber Packet Frame Cnt */ +#define XM_RXE_CAR_ERR 0x033c /* 32 bit ro Rx Carrier Event Error Cnt */ +#define XM_RXF_LEN_ERR 0x0340 /* 32 bit ro Rx in Range Length Error */ +#define XM_RXE_SYM_ERR 0x0344 /* 32 bit ro Rx Symbol Error Counter */ +#define XM_RXE_SHT_ERR 0x0348 /* 32 bit ro Rx Short Event Error Cnt */ +#define XM_RXE_RUNT 0x034c /* 32 bit ro Rx Runt Event Counter */ +#define XM_RXF_LNG_ERR 0x0350 /* 32 bit ro Rx Frame too Long Error Cnt */ +#define XM_RXF_FCS_ERR 0x0354 /* 32 bit ro Rx Frame Check Seq. Error Cnt */ + /* 0x0358 - 0x035a: reserved */ +#define XM_RXF_CEX_ERR 0x035c /* 32 bit ro Rx Carrier Ext Error Frame Cnt*/ +#define XM_RXP_UTIL 0x0360 /* 32 bit ro Rx Utilization in % */ + /* 0x0364 - 0x0366: reserved */ +#define XM_RXF_64B 0x0368 /* 32 bit ro 64 Byte Rx Frame Counter */ +#define XM_RXF_127B 0x036c /* 32 bit ro 65-127 Byte Rx Frame Counter */ +#define XM_RXF_255B 0x0370 /* 32 bit ro 128-255 Byte Rx Frame Counter */ +#define XM_RXF_511B 0x0374 /* 32 bit ro 256-511 Byte Rx Frame Counter */ +#define XM_RXF_1023B 0x0378 /* 32 bit ro 512-1023 Byte Rx Frame Counter*/ +#define XM_RXF_MAX_SZ 0x037c /* 32 bit ro 1024-MaxSize Byte Rx Frame Cnt*/ + /* 0x02e8 - 0x02fe: reserved */ + + +/*----------------------------------------------------------------------------*/ +/* + * XMAC Bit Definitions + * + * If the bit access behaviour differs from the register access behaviour + * (r/w, ro) this is docomented after the bit number. The following bit + * access behaviours are used: + * (sc) self clearing + * (ro) read only + */ + +/* XM_MMU_CMD 16 bit r/w MMU Comamnd Register */ + /* Bit 15..13: reserved */ +#define XM_MMU_PHY_RDY (1<<12) /* Bit 12: PHY Read Ready */ +#define XM_MMU_PHY_BUSY (1<<11) /* Bit 11: PHY Busy */ +#define XM_MMU_IGN_PF (1<<10) /* Bit 10: Ignore Pause Frame */ +#define XM_MMU_MAC_LB (1<<9) /* Bit 9: Enable MAC Loopback */ + /* Bit 8: reserved */ +#define XM_MMU_FRC_COL (1<<7) /* Bit 7: Force Collision */ +#define XM_MMU_SIM_COL (1<<6) /* Bit 6: Simulate Collision */ +#define XM_MMU_NO_PRE (1<<5) /* Bit 5: No MDIO Preamble */ +#define XM_MMU_GMII_FD (1<<4) /* Bit 4: GMII uses Full Duplex */ +#define XM_MMU_RAT_CTRL (1<<3) /* Bit 3: Enable Rate Control */ +#define XM_MMU_GMII_LOOP (1<<2) /* Bit 2: PHY is in Lookback Mode */ +#define XM_MMU_ENA_RX (1<<1) /* Bit 1: Enable Receiver */ +#define XM_MMU_ENA_TX (1<<0) /* Bit 0: Enable Transmitter */ + + +/* XM_TX_CMD 16 bit r/w Transmit Command Register */ + /* Bit 15..7: reserved */ +#define XM_TX_BK2BK (1<<6) /* Bit 6: Ignor Carrier Sense (tx Bk2Bk)*/ +#define XM_TX_ENC_BYP (1<<5) /* Bit 5: Set Encoder in Bypass Mode */ +#define XM_TX_SAM_LINE (1<<4) /* Bit 4: (sc) Start utilization calculation */ +#define XM_TX_NO_GIG_MD (1<<3) /* Bit 3: Disable Carrier Extension */ +#define XM_TX_NO_PRE (1<<2) /* Bit 2: Disable Preamble Generation */ +#define XM_TX_NO_CRC (1<<1) /* Bit 1: Disable CRC Generation */ +#define XM_TX_AUTO_PAD (1<<0) /* Bit 0: Enable Automatic Padding */ + + +/* XM_TX_RT_LIM 16 bit r/w Transmit Retry Limit Register */ + /* Bit 15..5: reserved */ +#define XM_RT_LIM_MSK 0x1f /* Bit 4..0: Tx Retry Limit */ + + +/* XM_TX_STIME 16 bit r/w Transmit Slottime Register */ + /* Bit 15..7: reserved */ +#define XM_STIME_MSK 0x7f /* Bit 6..0: Tx Slottime bits */ + + +/* XM_TX_IPG 16 bit r/w Transmit Inter Packet Gap */ + /* Bit 15..8: reserved */ +#define XM_IPG_MSK 0xff /* Bit 7..0: IPG value bits */ + + +/* XM_RX_CMD 16 bit r/w Receive Command Register */ + /* Bit 15..9: reserved */ +#define XM_RX_LENERR_OK (1<<8) /* Bit 8 don't set Rx Err bit for */ + /* inrange error packets */ +#define XM_RX_BIG_PK_OK (1<<7) /* Bit 7 don't set Rx Err bit for */ + /* jumbo packets */ +#define XM_RX_IPG_CAP (1<<6) /* Bit 6 repl. type field with IPG */ +#define XM_RX_TP_MD (1<<5) /* Bit 5: Enable transparent Mode */ +#define XM_RX_STRIP_FCS (1<<4) /* Bit 4: Enable FCS Stripping */ +#define XM_RX_SELF_RX (1<<3) /* Bit 3: Enable Rx of own packets */ +#define XM_RX_SAM_LINE (1<<2) /* Bit 2: (sc) Start utilization calculation */ +#define XM_RX_STRIP_PAD (1<<1) /* Bit 1: Strip pad bytes of rx frames */ +#define XM_RX_DIS_CEXT (1<<0) /* Bit 0: Disable carrier ext. check */ + + +/* XM_PHY_ADDR 16 bit r/w PHY Address Register */ + /* Bit 15..5: reserved */ +#define XM_PHY_ADDR_SZ 0x1f /* Bit 4..0: PHY Address bits */ + + +/* XM_GP_PORT 32 bit r/w General Purpose Port Register */ + /* Bit 31..7: reserved */ +#define XM_GP_ANIP (1L<<6) /* Bit 6: (ro) Auto Negotiation in Progress */ +#define XM_GP_FRC_INT (1L<<5) /* Bit 5: (sc) Force Interrupt */ + /* Bit 4: reserved */ +#define XM_GP_RES_MAC (1L<<3) /* Bit 3: (sc) Reset MAC and FIFOs */ +#define XM_GP_RES_STAT (1L<<2) /* Bit 2: (sc) Reset the statistics module */ + /* Bit 1: reserved */ +#define XM_GP_INP_ASS (1L<<0) /* Bit 0: (ro) GP Input Pin asserted */ + + +/* XM_IMSK 16 bit r/w Interrupt Mask Register */ +/* XM_ISRC 16 bit ro Interrupt Status Register */ + /* Bit 15: reserved */ +#define XM_IS_LNK_AE (1<<14) /* Bit 14: Link Asynchronous Event */ +#define XM_IS_TX_ABORT (1<<13) /* Bit 13: Transmit Abort, late Col. etc */ +#define XM_IS_FRC_INT (1<<12) /* Bit 12: Force INT bit set in GP */ +#define XM_IS_INP_ASS (1<<11) /* Bit 11: Input Asserted, GP bit 0 set */ +#define XM_IS_LIPA_RC (1<<10) /* Bit 10: Link Partner requests config */ +#define XM_IS_RX_PAGE (1<<9) /* Bit 9: Page Received */ +#define XM_IS_TX_PAGE (1<<8) /* Bit 8: Next Page Loaded for Transmit */ +#define XM_IS_AND (1<<7) /* Bit 7: Auto Negotiation Done */ +#define XM_IS_TSC_OV (1<<6) /* Bit 6: Time Stamp Counter Overflow */ +#define XM_IS_RXC_OV (1<<5) /* Bit 5: Rx Counter Event Overflow */ +#define XM_IS_TXC_OV (1<<4) /* Bit 4: Tx Counter Event Overflow */ +#define XM_IS_RXF_OV (1<<3) /* Bit 3: Receive FIFO Overflow */ +#define XM_IS_TXF_UR (1<<2) /* Bit 2: Transmit FIFO Underrun */ +#define XM_IS_TX_COMP (1<<1) /* Bit 1: Frame Tx Complete */ +#define XM_IS_RX_COMP (1<<0) /* Bit 0: Frame Rx Complete */ + +#define XM_DEF_MSK (~(XM_IS_INP_ASS | XM_IS_LIPA_RC | XM_IS_RX_PAGE |\ + XM_IS_AND | XM_IS_RXC_OV | XM_IS_TXC_OV |\ + XM_IS_TXF_UR)) + + +/* XM_HW_CFG 16 bit r/w Hardware Config Register */ + /* Bit 15.. 4: reserved */ +#define XM_HW_GEN_EOP (1<<3) /* Bit 3: generate End of Packet pulse */ +#define XM_HW_COM4SIG (1<<2) /* Bit 2: use Comma Detect for Sig. Det.*/ + /* Bit 1: reserved */ +#define XM_HW_GMII_MD (1<<0) /* Bit 0: GMII Interface selected */ + + +/* XM_TX_LO_WM 16 bit r/w Tx FIFO Low Water Mark */ +/* XM_TX_HI_WM 16 bit r/w Tx FIFO High Water Mark */ + /* Bit 15..10 reserved */ +#define XM_TX_WM_MSK 0x01ff /* Bit 9.. 0 Tx FIFO Watermark bits */ + +/* XM_TX_THR 16 bit r/w Tx Request Threshold */ +/* XM_HT_THR 16 bit r/w Host Request Threshold */ +/* XM_RX_THR 16 bit r/w Receive Request Threshold */ + /* Bit 15..11 reserved */ +#define XM_THR_MSK 0x03ff /* Bit 10.. 0 Tx FIFO Watermark bits */ + + +/* XM_TX_STAT 32 bit ro Tx Status LIFO Register */ +#define XM_ST_VALID (1UL<<31) /* Bit 31: Status Valid */ +#define XM_ST_BYTE_CNT (0x3fffL<<17) /* Bit 30..17: Tx frame Length */ +#define XM_ST_RETRY_CNT (0x1fL<<12) /* Bit 16..12: Retry Count */ +#define XM_ST_EX_COL (1L<<11) /* Bit 11: Excessive Collisions */ +#define XM_ST_EX_DEF (1L<<10) /* Bit 10: Excessive Deferral */ +#define XM_ST_BURST (1L<<9) /* Bit 9: p. xmitted in burst md*/ +#define XM_ST_DEFER (1L<<8) /* Bit 8: packet was defered */ +#define XM_ST_BC (1L<<7) /* Bit 7: Broadcast packet */ +#define XM_ST_MC (1L<<6) /* Bit 6: Multicast packet */ +#define XM_ST_UC (1L<<5) /* Bit 5: Unicast packet */ +#define XM_ST_TX_UR (1L<<4) /* Bit 4: FIFO Underrun occured */ +#define XM_ST_CS_ERR (1L<<3) /* Bit 3: Carrier Sense Error */ +#define XM_ST_LAT_COL (1L<<2) /* Bit 2: Late Collision Error */ +#define XM_ST_MUL_COL (1L<<1) /* Bit 1: Multiple Collisions */ +#define XM_ST_SGN_COL (1L<<0) /* Bit 0: Single Collision */ + +/* XM_RX_LO_WM 16 bit r/w Receive Low Water Mark */ +/* XM_RX_HI_WM 16 bit r/w Receive High Water Mark */ + /* Bit 15..11: reserved */ +#define XM_RX_WM_MSK 0x03ff /* Bit 11.. 0: Rx FIFO Watermark bits */ + + +/* XM_DEV_ID 32 bit ro Device ID Register */ +#define XM_DEV_OUI (0x00ffffffUL<<8) /* Bit 31..8: Device OUI */ +#define XM_DEV_REV (0x07L << 5) /* Bit 7..5: Chip Rev Num */ + + +/* XM_MODE 32 bit r/w Mode Register */ + /* Bit 31..27: reserved */ +#define XM_MD_ENA_REJ (1L<<26) /* Bit 26: Enable Frame Reject */ +#define XM_MD_SPOE_E (1L<<25) /* Bit 25: Send Pause on Edge */ + /* extern generated */ +#define XM_MD_TX_REP (1L<<24) /* Bit 24: Transmit Repeater Mode*/ +#define XM_MD_SPOFF_I (1L<<23) /* Bit 23: Send Pause on FIFOfull*/ + /* intern generated */ +#define XM_MD_LE_STW (1L<<22) /* Bit 22: Rx Stat Word in Lit En*/ +#define XM_MD_TX_CONT (1L<<21) /* Bit 21: Send Continuous */ +#define XM_MD_TX_PAUSE (1L<<20) /* Bit 20: (sc) Send Pause Frame */ +#define XM_MD_ATS (1L<<19) /* Bit 19: Append Time Stamp */ +#define XM_MD_SPOL_I (1L<<18) /* Bit 18: Send Pause on Low */ + /* intern generated */ +#define XM_MD_SPOH_I (1L<<17) /* Bit 17: Send Pause on High */ + /* intern generated */ +#define XM_MD_CAP (1L<<16) /* Bit 16: Check Address Pair */ +#define XM_MD_ENA_HSH (1L<<15) /* Bit 15: Enable Hashing */ +#define XM_MD_CSA (1L<<14) /* Bit 14: Check Station Address */ +#define XM_MD_CAA (1L<<13) /* Bit 13: Check Address Array */ +#define XM_MD_RX_MCTRL (1L<<12) /* Bit 12: Rx MAC Control Frames */ +#define XM_MD_RX_RUNT (1L<<11) /* Bit 11: Rx Runt Frames */ +#define XM_MD_RX_IRLE (1L<<10) /* Bit 10: Rx in Range Len Err F */ +#define XM_MD_RX_LONG (1L<<9) /* Bit 9: Rx Long Frames */ +#define XM_MD_RX_CRCE (1L<<8) /* Bit 8: Rx CRC Error Frames */ +#define XM_MD_RX_ERR (1L<<7) /* Bit 7: Rx Error Frames */ +#define XM_MD_DIS_UC (1L<<6) /* Bit 6: Disable Rx Unicast */ +#define XM_MD_DIS_MC (1L<<5) /* Bit 5: Disable Rx Multicast */ +#define XM_MD_DIS_BC (1L<<4) /* Bit 4: Disable Rx Boradcast */ +#define XM_MD_ENA_PROM (1L<<3) /* Bit 3: Enable Promiscuous */ +#define XM_MD_ENA_BE (1L<<2) /* Bit 2: Enable Big Endian */ +#define XM_MD_FTF (1L<<1) /* Bit 1: (sc) Flush Tx FIFO */ +#define XM_MD_FRF (1L<<0) /* Bit 0: (sc) Flush Rx FIFO */ + +#define XM_PAUSE_MODE (XM_MD_SPOE_E | XM_MD_SPOL_I | XM_MD_SPOH_I) +#define XM_DEF_MODE (XM_MD_RX_RUNT | XM_MD_RX_IRLE | XM_MD_RX_LONG |\ + XM_MD_RX_CRCE | XM_MD_RX_ERR | XM_MD_CSA |\ + XM_MD_CAA) + +/* XM_STAT_CMD 16 bit r/w Statistics Command Register */ + /* Bit 16..6: reserved */ +#define XM_SC_SNP_RXC (1<<5) /* Bit 5: (sc) Snap Rx Counters */ +#define XM_SC_SNP_TXC (1<<4) /* Bit 4: (sc) Snap Tx Counters */ +#define XM_SC_CP_RXC (1<<3) /* Bit 3: Copy Rx Counters Continuously */ +#define XM_SC_CP_TXC (1<<2) /* Bit 2: Copy Tx Counters Continuously */ +#define XM_SC_CLR_RXC (1<<1) /* Bit 1: (sc) Clear Rx Counters */ +#define XM_SC_CLR_TXC (1<<0) /* Bit 0: (sc) Clear Tx Counters */ + + +/* XM_RX_CNT_EV 32 bit ro Rx Counter Event Register */ +/* XM_RX_EV_MSK 32 bit r/w Rx Counter Event Mask */ +#define XMR_MAX_SZ_OV (1UL<<31) /* Bit 31: 1024-MaxSize Rx Cnt Ov*/ +#define XMR_1023B_OV (1L<<30) /* Bit 30: 512-1023Byte Rx Cnt Ov*/ +#define XMR_511B_OV (1L<<29) /* Bit 29: 256-511 Byte Rx Cnt Ov*/ +#define XMR_255B_OV (1L<<28) /* Bit 28: 128-255 Byte Rx Cnt Ov*/ +#define XMR_127B_OV (1L<<27) /* Bit 27: 65-127 Byte Rx Cnt Ov */ +#define XMR_64B_OV (1L<<26) /* Bit 26: 64 Byte Rx Cnt Ov */ +#define XMR_UTIL_OV (1L<<25) /* Bit 25: Rx Util Cnt Overflow */ +#define XMR_UTIL_UR (1L<<24) /* Bit 24: Rx Util Cnt Underrun */ +#define XMR_CEX_ERR_OV (1L<<23) /* Bit 23: CEXT Err Cnt Ov */ + /* Bit 22: reserved */ +#define XMR_FCS_ERR_OV (1L<<21) /* Bit 21: Rx FCS Error Cnt Ov */ +#define XMR_LNG_ERR_OV (1L<<20) /* Bit 20: Rx too Long Err Cnt Ov*/ +#define XMR_RUNT_OV (1L<<19) /* Bit 19: Runt Event Cnt Ov */ +#define XMR_SHT_ERR_OV (1L<<18) /* Bit 18: Rx Short Ev Err Cnt Ov*/ +#define XMR_SYM_ERR_OV (1L<<17) /* Bit 17: Rx Sym Err Cnt Ov */ + /* Bit 16: reserved */ +#define XMR_CAR_ERR_OV (1L<<15) /* Bit 15: Rx Carr Ev Err Cnt Ov */ +#define XMR_JAB_PKT_OV (1L<<14) /* Bit 14: Rx Jabb Packet Cnt Ov */ +#define XMR_FIFO_OV (1L<<13) /* Bit 13: Rx FIFO Ov Ev Cnt Ov */ +#define XMR_FRA_ERR_OV (1L<<12) /* Bit 12: Rx Framing Err Cnt Ov */ +#define XMR_FMISS_OV (1L<<11) /* Bit 11: Rx Missed Ev Cnt Ov */ +#define XMR_BURST (1L<<10) /* Bit 10: Rx Burst Event Cnt Ov */ +#define XMR_INV_MOC (1L<<9) /* Bit 9: Rx with inv. MAC OC Ov*/ +#define XMR_INV_MP (1L<<8) /* Bit 8: Rx inv Pause Frame Ov */ +#define XMR_MCTRL_OV (1L<<7) /* Bit 7: Rx MAC Ctrl-F Cnt Ov */ +#define XMR_MPAUSE_OV (1L<<6) /* Bit 6: Rx Pause MAC Ctrl-F Ov*/ +#define XMR_UC_OK_OV (1L<<5) /* Bit 5: Rx Unicast Frame CntOv*/ +#define XMR_MC_OK_OV (1L<<4) /* Bit 4: Rx Multicast Cnt Ov */ +#define XMR_BC_OK_OV (1L<<3) /* Bit 3: Rx Broadcast Cnt Ov */ +#define XMR_OK_LO_OV (1L<<2) /* Bit 2: Octets Rx OK Low CntOv*/ +#define XMR_OK_HI_OV (1L<<1) /* Bit 1: Octets Rx OK Hi Cnt Ov*/ +#define XMR_OK_OV (1L<<0) /* Bit 0: Frames Received Ok Ov */ + +#define XMR_DEF_MSK 0x00000006L /* all bits excepting 1 and 2 */ + +/* XM_TX_CNT_EV 32 bit ro Tx Counter Event Register */ +/* XM_TX_EV_MSK 32 bit r/w Tx Counter Event Mask */ + /* Bit 31..26: reserved */ +#define XMT_MAX_SZ_OV (1L<<25) /* Bit 25: 1024-MaxSize Tx Cnt Ov*/ +#define XMT_1023B_OV (1L<<24) /* Bit 24: 512-1023Byte Tx Cnt Ov*/ +#define XMT_511B_OV (1L<<23) /* Bit 23: 256-511 Byte Tx Cnt Ov*/ +#define XMT_255B_OV (1L<<22) /* Bit 22: 128-255 Byte Tx Cnt Ov*/ +#define XMT_127B_OV (1L<<21) /* Bit 21: 65-127 Byte Tx Cnt Ov */ +#define XMT_64B_OV (1L<<20) /* Bit 20: 64 Byte Tx Cnt Ov */ +#define XMT_UTIL_OV (1L<<19) /* Bit 19: Tx Util Cnt Overflow */ +#define XMT_UTIL_UR (1L<<18) /* Bit 18: Tx Util Cnt Underrun */ +#define XMT_CS_ERR_OV (1L<<17) /* Bit 17: Tx Carr Sen Err Cnt Ov*/ +#define XMT_FIFO_UR_OV (1L<<16) /* Bit 16: Tx FIFO Ur Ev Cnt Ov */ +#define XMT_EX_DEF_OV (1L<<15) /* Bit 15: Tx Ex Deferall Cnt Ov */ +#define XMT_DEF (1L<<14) /* Bit 14: Tx Deferred Cnt Ov */ +#define XMT_LAT_COL_OV (1L<<13) /* Bit 13: Tx Late Col Cnt Ov */ +#define XMT_ABO_COL_OV (1L<<12) /* Bit 12: Tx abo dueto Ex Col Ov*/ +#define XMT_MUL_COL_OV (1L<<11) /* Bit 11: Tx Mult Col Cnt Ov */ +#define XMT_SNG_COL (1L<<10) /* Bit 10: Tx Single Col Cnt Ov */ +#define XMT_MCTRL_OV (1L<<9) /* Bit 9: Tx MAC Ctrl Counter Ov*/ +#define XMT_MPAUSE (1L<<8) /* Bit 8: Tx Pause MAC Ctrl-F Ov*/ +#define XMT_BURST (1L<<7) /* Bit 7: Tx Burst Event Cnt Ov */ +#define XMT_LONG (1L<<6) /* Bit 6: Tx Long Frame Cnt Ov */ +#define XMT_UC_OK_OV (1L<<5) /* Bit 5: Tx Unicast Cnt Ov */ +#define XMT_MC_OK_OV (1L<<4) /* Bit 4: Tx Multicast Cnt Ov */ +#define XMT_BC_OK_OV (1L<<3) /* Bit 3: Tx Broadcast Cnt Ov */ +#define XMT_OK_LO_OV (1L<<2) /* Bit 2: Octets Tx OK Low CntOv*/ +#define XMT_OK_HI_OV (1L<<1) /* Bit 1: Octets Tx OK Hi Cnt Ov*/ +#define XMT_OK_OV (1L<<0) /* Bit 0: Frames Tx Ok Ov */ + +#define XMT_DEF_MSK 0x00000006L /* all bits excepting 1 and 2 */ + +/* + * Receive Frame Status Encoding + */ +#define XMR_FS_LEN (0x3fffUL<<18) /* Bit 31..18: Rx Frame Length */ +#define XMR_FS_2L_VLAN (1L<<17) /* Bit 17: tagged wh 2Lev VLAN ID*/ +#define XMR_FS_1L_VLAN (1L<<16) /* Bit 16: tagged wh 1Lev VLAN ID*/ +#define XMR_FS_BC (1L<<15) /* Bit 15: Broadcast Frame */ +#define XMR_FS_MC (1L<<14) /* Bit 14: Multicast Frame */ +#define XMR_FS_UC (1L<<13) /* Bit 13: Unicast Frame */ + /* Bit 12: reserved */ +#define XMR_FS_BURST (1L<<11) /* Bit 11: Burst Mode */ +#define XMR_FS_CEX_ERR (1L<<10) /* Bit 10: Carrier Ext. Error */ +#define XMR_FS_802_3 (1L<<9) /* Bit 9: 802.3 Frame */ +#define XMR_FS_COL_ERR (1L<<8) /* Bit 8: Collision Error */ +#define XMR_FS_CAR_ERR (1L<<7) /* Bit 7: Carrier Event Error */ +#define XMR_FS_LEN_ERR (1L<<6) /* Bit 6: In-Range Length Error */ +#define XMR_FS_FRA_ERR (1L<<5) /* Bit 5: Framing Error */ +#define XMR_FS_RUNT (1L<<4) /* Bit 4: Runt Error */ +#define XMR_FS_LNG_ERR (1L<<3) /* Bit 3: Gaint Error */ +#define XMR_FS_FCS_ERR (1L<<2) /* Bit 2: Frame Check Sequ Err */ +#define XMR_FS_ERR (1L<<1) /* Bit 1: Frame Error */ +#define XMR_FS_MCTRL (1L<<0) /* Bit 0: MAC Control Packet */ + +/* + * XMR_FS_ERR will be set if + * XMR_FS_FCS_ERR, XMR_FS_LNG_ERR, XMR_FS_RUNT, + * XMR_FS_FRA_ERR, XMR_FS_LEN_ERR, or XMR_FS_CEX_ERR + * is set. XMR_FS_LNG_ERR and XMR_FS_LEN_ERR will issue + * XMR_FS_ERR unless the corresponding bit in the Receive Command + * Register is set. + */ +#define XMR_FS_ANY_ERR XMR_FS_ERR + +/*----------------------------------------------------------------------------*/ +/* + * XMAC-PHY Registers, indirect addressed over the XMAC + */ +#define PHY_XMAC_CTRL 0x00 /* 16 bit r/w PHY Control Register */ +#define PHY_XMAC_STAT 0x01 /* 16 bit r/w PHY Status Register */ +#define PHY_XMAC_ID0 0x02 /* 16 bit ro PHY ID0 Register */ +#define PHY_XMAC_ID1 0x03 /* 16 bit ro PHY ID1 Register */ +#define PHY_XMAC_AUNE_ADV 0x04 /* 16 bit r/w Autoneg Advertisement */ +#define PHY_XMAC_AUNE_LP 0x05 /* 16 bit ro Link Partner Abi Reg */ +#define PHY_XMAC_AUNE_EXP 0x06 /* 16 bit ro Autoneg Expansion Reg */ +#define PHY_XMAC_NEPG 0x07 /* 16 bit r/w Next Page Register */ +#define PHY_XMAC_NEPG_LP 0x08 /* 16 bit ro Next Page Link P Reg */ + /* 0x09 - 0x0e: reserved */ +#define PHY_XMAC_EXT_STAT 0x0f /* 16 bit ro Ext Status Register */ +#define PHY_XMAC_RES_ABI 0x10 /* 16 bit ro PHY Resolved Ability */ + +/*----------------------------------------------------------------------------*/ +/* + * Broadcom-PHY Registers, indirect addressed over XMAC + */ +#define PHY_BCOM_CTRL 0x00 /* 16 bit r/w PHY Control Register */ +#define PHY_BCOM_STAT 0x01 /* 16 bit ro PHY Status Register */ +#define PHY_BCOM_ID0 0x02 /* 16 bit ro PHY ID0 Register */ +#define PHY_BCOM_ID1 0x03 /* 16 bit ro PHY ID1 Register */ +#define PHY_BCOM_AUNE_ADV 0x04 /* 16 bit r/w Autoneg Advertisement */ +#define PHY_BCOM_AUNE_LP 0x05 /* 16 bit ro Link Part Ability Reg */ +#define PHY_BCOM_AUNE_EXP 0x06 /* 16 bit ro Autoneg Expansion Reg */ +#define PHY_BCOM_NEPG 0x07 /* 16 bit r/w Next Page Register */ +#define PHY_BCOM_NEPG_LP 0x08 /* 16 bit ro Next Page Link P Reg */ + /* Broadcom-specific registers */ +#define PHY_BCOM_1000T_CTRL 0x09 /* 16 bit r/w 1000Base-T Ctrl Reg */ +#define PHY_BCOM_1000T_STAT 0x0a /* 16 bit ro 1000Base-T Status Reg */ + /* 0x0b - 0x0e: reserved */ +#define PHY_BCOM_EXT_STAT 0x0f /* 16 bit ro Extended Status Reg */ +#define PHY_BCOM_P_EXT_CTRL 0x10 /* 16 bit r/w PHY Extended Ctrl Reg */ +#define PHY_BCOM_P_EXT_STAT 0x11 /* 16 bit ro PHY Extended Stat Reg */ +#define PHY_BCOM_RE_CTR 0x12 /* 16 bit r/w Receive Error Counter */ +#define PHY_BCOM_FC_CTR 0x13 /* 16 bit r/w False Carr Sense Cnt */ +#define PHY_BCOM_RNO_CTR 0x14 /* 16 bit r/w Receiver NOT_OK Cnt */ + /* 0x15 - 0x17: reserved */ +#define PHY_BCOM_AUX_CTRL 0x18 /* 16 bit r/w Auxiliary Control Reg */ +#define PHY_BCOM_AUX_STAT 0x19 /* 16 bit ro Auxiliary Stat Summary*/ +#define PHY_BCOM_INT_STAT 0x1a /* 16 bit ro Interrupt Status Reg */ +#define PHY_BCOM_INT_MASK 0x1b /* 16 bit r/w Interrupt Mask Reg */ + /* 0x1c: reserved */ + /* 0x1d - 0x1f: test registers */ + +/*----------------------------------------------------------------------------*/ +/* + * Level One-PHY Registers, indirect addressed over XMAC + */ +#define PHY_LONE_CTRL 0x00 /* 16 bit r/w PHY Control Register */ +#define PHY_LONE_STAT 0x01 /* 16 bit ro PHY Status Register */ +#define PHY_LONE_ID0 0x02 /* 16 bit ro PHY ID0 Register */ +#define PHY_LONE_ID1 0x03 /* 16 bit ro PHY ID1 Register */ +#define PHY_LONE_AUNE_ADV 0x04 /* 16 bit r/w Autoneg Advertisement */ +#define PHY_LONE_AUNE_LP 0x05 /* 16 bit ro Link Part Ability Reg */ +#define PHY_LONE_AUNE_EXP 0x06 /* 16 bit ro Autoneg Expansion Reg */ +#define PHY_LONE_NEPG 0x07 /* 16 bit r/w Next Page Register */ +#define PHY_LONE_NEPG_LP 0x08 /* 16 bit ro Next Page Link Partner*/ + /* Level One-specific registers */ +#define PHY_LONE_1000T_CTRL 0x09 /* 16 bit r/w 1000Base-T Control Reg*/ +#define PHY_LONE_1000T_STAT 0x0a /* 16 bit ro 1000Base-T Status Reg */ + /* 0x0b -0x0e: reserved */ +#define PHY_LONE_EXT_STAT 0x0f /* 16 bit ro Extended Status Reg */ +#define PHY_LONE_PORT_CFG 0x10 /* 16 bit r/w Port Configuration Reg*/ +#define PHY_LONE_Q_STAT 0x11 /* 16 bit ro Quick Status Reg */ +#define PHY_LONE_INT_ENAB 0x12 /* 16 bit r/w Interrupt Enable Reg */ +#define PHY_LONE_INT_STAT 0x13 /* 16 bit ro Interrupt Status Reg */ +#define PHY_LONE_LED_CFG 0x14 /* 16 bit r/w LED Configuration Reg */ +#define PHY_LONE_PORT_CTRL 0x15 /* 16 bit r/w Port Control Reg */ +#define PHY_LONE_CIM 0x16 /* 16 bit ro CIM Reg */ + /* 0x17 -0x1c: reserved */ + +/*----------------------------------------------------------------------------*/ +/* + * National-PHY Registers, indirect addressed over XMAC + */ +#define PHY_NAT_CTRL 0x00 /* 16 bit r/w PHY Control Register */ +#define PHY_NAT_STAT 0x01 /* 16 bit r/w PHY Status Register */ +#define PHY_NAT_ID0 0x02 /* 16 bit ro PHY ID0 Register */ +#define PHY_NAT_ID1 0x03 /* 16 bit ro PHY ID1 Register */ +#define PHY_NAT_AUNE_ADV 0x04 /* 16 bit r/w Autonegotiation Advertisement */ +#define PHY_NAT_AUNE_LP 0x05 /* 16 bit ro Link Partner Ability Reg */ +#define PHY_NAT_AUNE_EXP 0x06 /* 16 bit ro Autonegotiation Expansion Reg */ +#define PHY_NAT_NEPG 0x07 /* 16 bit r/w Next Page Register */ +#define PHY_NAT_NEPG_LP 0x08 /* 16 bit ro Next Page Link Partner Reg */ + /* National-specific registers */ +#define PHY_NAT_1000T_CTRL 0x09 /* 16 bit r/w 1000Base-T Control Reg */ +#define PHY_NAT_1000T_STAT 0x0a /* 16 bit ro 1000Base-T Status Reg */ + /* 0x0b -0x0e: reserved */ +#define PHY_NAT_EXT_STAT 0x0f /* 16 bit ro Extended Status Register */ +#define PHY_NAT_EXT_CTRL1 0x10 /* 16 bit ro Extended Control Reg1 */ +#define PHY_NAT_Q_STAT1 0x11 /* 16 bit ro Quick Status Reg1 */ +#define PHY_NAT_10B_OP 0x12 /* 16 bit ro 10Base-T Operations Reg */ +#define PHY_NAT_EXT_CTRL2 0x13 /* 16 bit ro Extended Control Reg1 */ +#define PHY_NAT_Q_STAT2 0x14 /* 16 bit ro Quick Status Reg2 */ + /* 0x15 -0x18: reserved */ +#define PHY_NAT_PHY_ADDR 0x19 /* 16 bit ro PHY Address Register */ + + +/*----------------------------------------------------------------------------*/ + +/* + * PHY bit definitions + * Bits defined as PHY_X_..., PHY_B_..., PHY_L_... or PHY_N_... are + * Xmac/Broadcom/LevelOne/National-specific. + * All other are general. + */ + +/***** PHY_XMAC_CTRL 16 bit r/w PHY Control Register *****/ +/***** PHY_BCOM_CTRL 16 bit r/w PHY Control Register *****/ +/***** PHY_LONE_CTRL 16 bit r/w PHY Control Register *****/ +#define PHY_CT_RESET (1<<15) /* Bit 15: (sc) clear all PHY releated regs */ +#define PHY_CT_LOOP (1<<14) /* Bit 14: enable Loopback over PHY */ +#define PHY_CT_SPS_LSB (1<<13) /* Bit 13: (BC,L1) Speed select, lower bit */ +#define PHY_CT_ANE (1<<12) /* Bit 12: Autonegotiation Enabled */ +#define PHY_CT_PDOWN (1<<11) /* Bit 11: (BC,L1) Power Down Mode */ +#define PHY_CT_ISOL (1<<10) /* Bit 10: (BC,L1) Isolate Mode */ +#define PHY_CT_RE_CFG (1<<9) /* Bit 9: (sc) Restart Autonegotiation */ +#define PHY_CT_DUP_MD (1<<8) /* Bit 8: Duplex Mode */ +#define PHY_CT_COL_TST (1<<7) /* Bit 7: (BC,L1) Collsion Test enabled */ +#define PHY_CT_SPS_MSB (1<<6) /* Bit 6: (BC,L1) Speed select, upper bit */ + /* Bit 5..0: reserved */ + +#define PHY_B_CT_SP1000 (1<<6) /* Bit 6: enable speed of 1000 MBit/s */ +#define PHY_B_CT_SP100 (1<<13) /* Bit 13: enable speed of 100 MBit/s */ +#define PHY_B_CT_SP10 (0) /* Bit 6/13 not set, speed of 10 MBit/s */ + +#define PHY_L_CT_SP1000 (1<<6) /* Bit 6: enable speed of 1000 MBit/s */ +#define PHY_L_CT_SP100 (1<<13) /* Bit 13: enable speed of 100 MBit/s */ +#define PHY_L_CT_SP10 (0) /* Bit 6/13 not set, speed of 10 MBit/s */ + + +/***** PHY_XMAC_STAT 16 bit r/w PHY Status Register *****/ +/***** PHY_BCOM_STAT 16 bit r/w PHY Status Register *****/ +/***** PHY_LONE_STAT 16 bit r/w PHY Status Register *****/ + /* Bit 15..9: reserved */ + /* (BC/L1) 100/10 MBit/s cap bits ignored*/ +#define PHY_ST_EXT_ST (1<<8) /* Bit 8: Extended Status Present */ + /* Bit 7: reserved */ +#define PHY_ST_PRE_SUB (1<<6) /* Bit 6: (BC/L1) preamble suppression */ +#define PHY_ST_AN_OVER (1<<5) /* Bit 5: Autonegotiation Over */ +#define PHY_ST_REM_FLT (1<<4) /* Bit 4: Remode Fault Condition Occured*/ +#define PHY_ST_AN_CAP (1<<3) /* Bit 3: Autonegotiation Capability */ +#define PHY_ST_LSYNC (1<<2) /* Bit 2: Link Synchronized */ +#define PHY_ST_JAP_DET (1<<1) /* Bit 1: (BC/L1) Japper Detected */ +#define PHY_ST_EXT_REG (1<<0) /* Bit 0: Extended Register available */ + + +/* PHY_XMAC_ID1 16 bit ro PHY ID1 Register */ +/* PHY_BCOM_ID1 16 bit ro PHY ID1 Register */ +/* PHY_LONE_ID1 16 bit ro PHY ID1 Register */ +#define PHY_I1_OUI (0x3f<<10) /* Bit 15..10: Organiz. Unique ID */ +#define PHY_I1_MOD_NUM (0x3f<<4) /* Bit 9.. 4: Model Number */ +#define PHY_I1_REV (0x0f<<0) /* Bit 3.. 0: Revision Number */ + + +/***** PHY_XMAC_AUNE_ADV 16 bit r/w Autoneg Advertisement *****/ +/***** PHY_XMAC_AUNE_LP 16 bit ro Link Partner Ability Reg *****/ +#define PHY_AN_NXT_PG (1<<15) /* Bit 15: Request Next Page */ +#define PHY_X_AN_ACK (1<<14) /* Bit 14: (ro) Acknowledge Received */ +#define PHY_X_AN_RFB (3<<12) /* Bit 13..12: Remode Fault Bits */ + /* Bit 11.. 9: reserved */ +#define PHY_X_AN_PAUSE (3<<7) /* Bit 8.. 7: Pause Bits */ +#define PHY_X_AN_HD (1<<6) /* Bit 6: Half Duplex */ +#define PHY_X_AN_FD (1<<5) /* Bit 5: Full Duplex */ + /* Bit 4.. 0: reserved */ + +/***** PHY_BCOM_AUNE_ADV 16 bit r/w Autoneg Advertisement *****/ +/***** PHY_BCOM_AUNE_LP 16 bit ro Link Partner Ability Reg *****/ +/* PHY_AN_NXT_PG (see XMAC) Bit 15: Request Next Page */ + /* Bit 14: reserved */ +#define PHY_B_AN_RF (1<<13) /* Bit 13: Remote Fault */ + /* Bit 12: reserved */ +#define PHY_B_AN_ASP (1<<11) /* Bit 11: Asymetric Pause */ +#define PHY_B_AN_PC (1<<10) /* Bit 10: Pause Capable */ + /* Bit 9..5: 100/10 BT cap bits ingnored */ +#define PHY_B_AN_SEL (0x1f<<0)/* Bit 4..0: Selector Field, 00001=Ethernet*/ + +/***** PHY_LONE_AUNE_ADV 16 bit r/w Autoneg Advertisement *****/ +/***** PHY_LONE_AUNE_LP 16 bit ro Link Partner Ability Reg *****/ +/* PHY_AN_NXT_PG (see XMAC) Bit 15: Request Next Page */ + /* Bit 14: reserved */ +#define PHY_L_AN_RF (1<<13) /* Bit 13: Remote Fault */ + /* Bit 12: reserved */ +#define PHY_L_AN_ASP (1<<11) /* Bit 11: Asymetric Pause */ +#define PHY_L_AN_PC (1<<10) /* Bit 10: Pause Capable */ + /* Bit 9..5: 100/10 BT cap bits ingnored */ +#define PHY_L_AN_SEL (0x1f<<0)/* Bit 4..0: Selector Field, 00001=Ethernet*/ + +/***** PHY_NAT_AUNE_ADV 16 bit r/w Autoneg Advertisement *****/ +/***** PHY_NAT_AUNE_LP 16 bit ro Link Partner Ability Reg *****/ +/* PHY_AN_NXT_PG (see XMAC) Bit 15: Request Next Page */ + /* Bit 14: reserved */ +#define PHY_N_AN_RF (1<<13) /* Bit 13: Remote Fault */ + /* Bit 12: reserved */ +#define PHY_N_AN_100F (1<<11) /* Bit 11: 100Base-T2 FD Support */ +#define PHY_N_AN_100H (1<<10) /* Bit 10: 100Base-T2 HD Support */ + /* Bit 9..5: 100/10 BT cap bits ingnored */ +#define PHY_N_AN_SEL (0x1f<<0)/* Bit 4..0: Selector Field, 00001=Ethernet*/ + +/* field type definition for PHY_x_AN_SEL */ +#define PHY_SEL_TYPE 0x01 /* 00001 = Ethernet */ + +/***** PHY_XMAC_AUNE_EXP 16 bit ro Autoneg Expansion Reg *****/ + /* Bit 15..4: reserved */ +#define PHY_AN_LP_NP (1<<3) /* Bit 3: Link Partner can Next Page */ +#define PHY_AN_LOC_NP (1<<2) /* Bit 2: Local PHY can Next Page */ +#define PHY_AN_RX_PG (1<<1) /* Bit 1: Page Received */ + /* Bit 0: reserved */ + +/***** PHY_BCOM_AUNE_EXP 16 bit ro Autoneg Expansion Reg *****/ + /* Bit 15..5: reserved */ +#define PHY_B_AN_PDF (1<<4) /* Bit 4: Parallel Detection Fault */ +/* PHY_AN_LP_NP (see XMAC) Bit 3: Link Partner can Next Page */ +/* PHY_AN_LOC_NP (see XMAC) Bit 2: Local PHY can Next Page */ +/* PHY_AN_RX_PG (see XMAC) Bit 1: Page Received */ +#define PHY_B_AN_LP_CAP (1<<0) /* Bit 0: Link Partner Autoneg Cap. */ + +/***** PHY_LONE_AUNE_EXP 16 bit ro Autoneg Expansion Reg *****/ +#define PHY_L_AN_BP (1<<5) /* Bit 5: Base Page Indication */ +#define PHY_L_AN_PDF (1<<4) /* Bit 4: Parallel Detection Fault */ +/* PHY_AN_LP_NP (see XMAC) Bit 3: Link Partner can Next Page */ +/* PHY_AN_LOC_NP (see XMAC) Bit 2: Local PHY can Next Page */ +/* PHY_AN_RX_PG (see XMAC) Bit 1: Page Received */ +#define PHY_B_AN_LP_CAP (1<<0) /* Bit 0: Link Partner Autoneg Cap. */ + + +/***** PHY_XMAC_NEPG 16 bit r/w Next Page Register *****/ +/***** PHY_BCOM_NEPG 16 bit r/w Next Page Register *****/ +/***** PHY_LONE_NEPG 16 bit r/w Next Page Register *****/ +/***** PHY_XMAC_NEPG_LP 16 bit ro Next Page Link Partner *****/ +/***** PHY_BCOM_NEPG_LP 16 bit ro Next Page Link Partner *****/ +/***** PHY_LONE_NEPG_LP 16 bit ro Next Page Link Partner *****/ +#define PHY_NP_MORE (1<<15) /* Bit 15: More, Next Pages to follow */ +#define PHY_NP_ACK1 (1<<14) /* Bit 14: (ro) Ack 1, for receiving a message*/ +#define PHY_NP_MSG_VAL (1<<13) /* Bit 13: Message Page valid */ +#define PHY_NP_ACK2 (1<<12) /* Bit 12: Ack 2, comply with msg content*/ +#define PHY_NP_TOG (1<<11) /* Bit 11: Toggle Bit, ensure sync */ +#define PHY_NP_MSG 0x07ff /* Bit 10..0: Message from/to Link Partner */ + +/* + * XMAC-Specific + */ +/***** PHY_XMAC_EXT_STAT 16 bit r/w Extended Status Register *****/ +#define PHY_X_EX_FD (1<<15) /* Bit 15: Device Supports Full Duplex */ +#define PHY_X_EX_HD (1<<14) /* Bit 14: Device Supports Half Duplex */ + /* Bit 13..0: reserved */ + +/***** PHY_XMAC_RES_ABI 16 bit ro PHY Resolved Ability *****/ + /* Bit 15..9: reserved */ +#define PHY_X_RS_PAUSE (3<<7) /* Bit 8..7: selected Pause Mode */ +#define PHY_X_RS_HD (1<<6) /* Bit 6: Half Duplex Mode selected */ +#define PHY_X_RS_FD (1<<5) /* Bit 5: Full Duplex Mode selected */ +#define PHY_X_RS_ABLMIS (1<<4) /* Bit 4: duplex or pause cap mismatch */ +#define PHY_X_RS_PAUMIS (1<<3) /* Bit 3: pause capability missmatch */ + /* Bit 2..0: reserved */ +/* + * Remote Fault Bits (PHY_X_AN_RFB) encoding + */ +#define X_RFB_OK (0<<12) /* Bit 12..13 No errors, Link OK */ +#define X_RFB_LF (1<<12) /* Bit 12..13 Link Failure */ +#define X_RFB_OFF (2<<12) /* Bit 12..13 Offline */ +#define X_RFB_AN_ERR (3<<12) /* Bit 12..13 Autonegotiation Error */ + +/* + * Pause Bits (PHY_X_AN_PAUSE and PHY_X_RS_PAUSE) encoding + */ +#define PHY_X_P_NO_PAUSE (0<<7) /* Bit 8..7: no Pause Mode */ +#define PHY_X_P_SYM_MD (1<<7) /* Bit 8..7: symmetric Pause Mode */ +#define PHY_X_P_ASYM_MD (2<<7) /* Bit 8..7: asymmetric Pause Mode */ +#define PHY_X_P_BOTH_MD (3<<7) /* Bit 8..7: both Pause Mode */ + + +/* + * Broadcom-Specific + */ +/***** PHY_BCOM_1000T_CTRL 16 bit r/w 1000Base-T Control Reg *****/ +#define PHY_B_1000C_TEST (7<<13) /* Bit 15..13: Test Modes */ +#define PHY_B_1000C_MSE (1<<12) /* Bit 12: Master/Slave Enable */ +#define PHY_B_1000C_MSC (1<<11) /* Bit 11: M/S Configuration */ +#define PHY_B_1000C_RD (1<<10) /* Bit 10: Repeater/DTE */ +#define PHY_B_1000C_AFD (1<<9) /* Bit 9: Advertise Full Duplex */ +#define PHY_B_1000C_AHD (1<<8) /* Bit 8: Advertise Half Duplex */ + /* Bit 7..0: reserved */ + +/***** PHY_BCOM_1000T_STAT 16 bit ro 1000Base-T Status Reg *****/ +#define PHY_B_1000S_MSF (1<<15) /* Bit 15: Master/Slave Fault */ +#define PHY_B_1000S_MSR (1<<14) /* Bit 14: Master/Slave Result */ +#define PHY_B_1000S_LRS (1<<13) /* Bit 13: Local Receiver Status */ +#define PHY_B_1000S_RRS (1<<12) /* Bit 12: Remote Receiver Status */ +#define PHY_B_1000S_LP_FD (1<<11) /* Bit 11: Link Partner can FD */ +#define PHY_B_1000S_LP_HD (1<<10) /* Bit 10: Link Partner can HD */ + /* Bit 9..8: reserved */ +#define PHY_B_1000S_IEC (255<<0)/* Bit 7..0: Idle Error Count */ + +/***** PHY_BCOM_EXT_STAT 16 bit ro Extended Status Register *****/ +#define PHY_B_ES_X_FD_CAP (1<<15) /* Bit 15: 1000Base-X FD capable */ +#define PHY_B_ES_X_HD_CAP (1<<14) /* Bit 14: 1000Base-X HD capable */ +#define PHY_B_ES_T_FD_CAP (1<<13) /* Bit 13: 1000Base-T FD capable */ +#define PHY_B_ES_T_HD_CAP (1<<12) /* Bit 12: 1000Base-T HD capable */ + /* Bit 11..0: reserved */ + +/***** PHY_BCOM_P_EXT_CTRL 16 bit r/w PHY Extended Control Reg *****/ +#define PHY_B_PEC_MAC_PHY (1<<15) /* Bit 15: 10BIT/GMI-Interface */ +#define PHY_B_PEC_DIS_CROSS (1<<14) /* Bit 14: Disable MDI Crossover */ +#define PHY_B_PEC_TX_DIS (1<<13) /* Bit 13: Tx output Disabled */ +#define PHY_B_PEC_INT_DIS (1<<12) /* Bit 12: Interrupts Disabled */ +#define PHY_B_PEC_F_INT (1<<11) /* Bit 11: Force Interrupt */ +#define PHY_B_PEC_BY_45 (1<<10) /* Bit 10: Bypass 4B5B-Decoder */ +#define PHY_B_PEC_BY_SCR (1<<9) /* Bit 9: Bypass Scrambler */ +#define PHY_B_PEC_BY_MLT3 (1<<8) /* Bit 8: Bypass MLT3 Encoder */ +#define PHY_B_PEC_BY_RXA (1<<7) /* Bit 7: Bypass Rx Alignm. */ +#define PHY_B_PEC_RES_SCR (1<<6) /* Bit 6: Reset Scrambler */ +#define PHY_B_PEC_EN_LTR (1<<5) /* Bit 5: Ena LED Traffic Mode */ +#define PHY_B_PEC_LED_ON (1<<4) /* Bit 4: Force LED's on */ +#define PHY_B_PEC_LED_OFF (1<<3) /* Bit 3: Force LED's off */ +#define PHY_B_PEC_EX_IPG (1<<2) /* Bit 2: Extend Tx IPG Mode */ +#define PHY_B_PEC_3_LED (1<<1) /* Bit 1: Three Link LED mode */ +#define PHY_B_PEC_HIGH_LA (1<<0) /* Bit 0: GMII Fifo Elasticy */ + +/***** PHY_BCOM_P_EXT_STAT 16 bit ro PHY Extended Status Reg *****/ + /* Bit 15..14: reserved */ +#define PHY_B_PES_CROSS_STAT (1<<13) /* Bit 13: MDI Crossover Status */ +#define PHY_B_PES_INT_STAT (1<<12) /* Bit 12: Interrupt Status */ +#define PHY_B_PES_RRS (1<<11) /* Bit 11: Remote Receiver Stat. */ +#define PHY_B_PES_LRS (1<<10) /* Bit 10: Local Receiver Stat. */ +#define PHY_B_PES_LOCKED (1<<9) /* Bit 9: Locked */ +#define PHY_B_PES_LS (1<<8) /* Bit 8: Link Status */ +#define PHY_B_PES_RF (1<<7) /* Bit 7: Remote Fault */ +#define PHY_B_PES_CE_ER (1<<6) /* Bit 6: Carrier Ext Error */ +#define PHY_B_PES_BAD_SSD (1<<5) /* Bit 5: Bad SSD */ +#define PHY_B_PES_BAD_ESD (1<<4) /* Bit 4: Bad ESD */ +#define PHY_B_PES_RX_ER (1<<3) /* Bit 3: Receive Error */ +#define PHY_B_PES_TX_ER (1<<2) /* Bit 2: Transmit Error */ +#define PHY_B_PES_LOCK_ER (1<<1) /* Bit 1: Lock Error */ +#define PHY_B_PES_MLT3_ER (1<<0) /* Bit 0: MLT3 code Error */ + +/***** PHY_BCOM_FC_CTR 16 bit r/w False Carrier Counter *****/ + /* Bit 15..8: reserved */ +#define PHY_B_FC_CTR (255<<0)/* Bit 7..0: False Carrier Counter */ + +/***** PHY_BCOM_RNO_CTR 16 bit r/w Receive NOT_OK Counter *****/ +#define PHY_B_RC_LOC (255<<8)/* Bit 15..8: Local Rx NOT_OK cnt */ +#define PHY_B_RC_REM (255<<0)/* Bit 7..0: Remote Rx NOT_OK cnt */ + +/***** PHY_BCOM_AUX_CTRL 16 bit r/w Auxiliary Control Reg *****/ +#define PHY_B_AC_L_SQE (1<<15) /* Bit 15: Low Squelch */ +#define PHY_B_AC_LONG_PACK (1<<14) /* Bit 14: Rx Long Packets */ +#define PHY_B_AC_ER_CTRL (3<<12) /* Bit 13..12: Edgerate Control */ + /* Bit 11: reserved */ +#define PHY_B_AC_TX_TST (1<<10) /* Bit 10: tx test bit, always 1 */ + /* Bit 9.. 8: reserved */ +#define PHY_B_AC_DIS_PRF (1<<7) /* Bit 7: dis part resp filter */ + /* Bit 6.. 4: reserved */ +#define PHY_B_AC_DIAG (1<<3) /* Bit 3: Diagnostic Mode */ + /* Bit 2.. 0: reserved */ + +/***** PHY_BCOM_AUX_STAT 16 bit ro Auxiliary Status Reg *****/ +#define PHY_B_AS_AN_C (1<<15) /* Bit 15: AutoNeg complete */ +#define PHY_B_AS_AN_CA (1<<14) /* Bit 14: AN Complete Ack */ +#define PHY_B_AS_ANACK_D (1<<13) /* Bit 13: AN Ack Detect */ +#define PHY_B_AS_ANAB_D (1<<12) /* Bit 12: AN Ability Detect */ +#define PHY_B_AS_NPW (1<<11) /* Bit 11: AN Next Page Wait */ +#define PHY_B_AS_AN_RES (7<<8) /* Bit 10..8: AN HDC */ +#define PHY_B_AS_PDF (1<<7) /* Bit 7: Parallel Detect. Fault*/ +#define PHY_B_AS_RF (1<<6) /* Bit 6: Remote Fault */ +#define PHY_B_AS_ANP_R (1<<5) /* Bit 5: AN Page Received */ +#define PHY_B_AS_LP_ANAB (1<<4) /* Bit 4: LP AN Ability */ +#define PHY_B_AS_LP_NPAB (1<<3) /* Bit 3: LP Next Page Ability */ +#define PHY_B_AS_LS (1<<2) /* Bit 2: Link Status */ +#define PHY_B_AS_PRR (1<<1) /* Bit 1: Pause Resolution-Rx */ +#define PHY_B_AS_PRT (1<<0) /* Bit 0: Pause Resolution-Tx */ + +/***** PHY_BCOM_INT_STAT 16 bit ro Interrupt Status Reg *****/ +/***** PHY_BCOM_INT_MASK 16 bit r/w Interrupt Mask Reg *****/ + /* Bit 15: reserved */ +#define PHY_B_IS_PSE (1<<14) /* Bit 14: Pair Swap Error */ +#define PHY_B_IS_MDXI_SC (1<<13) /* Bit 13: MDIX Status Change */ +#define PHY_B_IS_HCT (1<<12) /* Bit 12: counter above 32k */ +#define PHY_B_IS_LCT (1<<11) /* Bit 11: all counter below 128 */ +#define PHY_B_IS_AN_PR (1<<10) /* Bit 10: Page Received */ +#define PHY_B_IS_NO_HDCL (1<<9) /* Bit 9: No HCD Link */ +#define PHY_B_IS_NO_HDC (1<<8) /* Bit 8: No HCD */ +#define PHY_B_IS_NEG_USHDC (1<<7) /* Bit 7: Negotiated Unsup. HCD */ +#define PHY_B_IS_SCR_S_ER (1<<6) /* Bit 6: Scrambler Sync Error */ +#define PHY_B_IS_RRS_CHANGE (1<<5) /* Bit 5: Remote Rx Stat Change */ +#define PHY_B_IS_LRS_CHANGE (1<<4) /* Bit 4: Local Rx Stat Change */ +#define PHY_B_IS_DUP_CHANGE (1<<3) /* Bit 3: Duplex Mode Change */ +#define PHY_B_IS_LSP_CHANGE (1<<2) /* Bit 2: Link Speed Change */ +#define PHY_B_IS_LST_CHANGE (1<<1) /* Bit 1: Link Status Changed */ +#define PHY_B_IS_CRC_ER (1<<0) /* Bit 0: CRC Error */ + +#define PHY_B_DEF_MSK (~(PHY_B_IS_AN_PR | PHY_B_IS_LST_CHANGE)) + + +/* + * Pause Bits (PHY_B_AN_ASP and PHY_B_AN_PC) encoding + */ +#define PHY_B_P_NO_PAUSE (0<<10) /* Bit 11..10: no Pause Mode */ +#define PHY_B_P_SYM_MD (1<<10) /* Bit 11..10: symmetric Pause Mode */ +#define PHY_B_P_ASYM_MD (2<<10) /* Bit 11..10: asymmetric Pause Mode */ +#define PHY_B_P_BOTH_MD (3<<10) /* Bit 11..10: both Pause Mode */ + +/* + * Resolved Duplex mode and Capabilities (Aux Status Summary Reg) + */ +#define PHY_B_RES_1000FD (7<<8) /* Bit 10..8: 1000Base-T Full Dup. */ +#define PHY_B_RES_1000HD (6<<8) /* Bit 10..8: 1000Base-T Half Dup. */ +/* others: 100/10: invalid for us */ + +/* + * Level One-Specific + */ +/***** PHY_LONE_1000T_CTRL 16 bit r/w 1000Base-T Control Reg *****/ +#define PHY_L_1000C_TEST (7<<13) /* Bit 15..13: Test Modes */ +#define PHY_L_1000C_MSE (1<<12) /* Bit 12: Master/Slave Enable */ +#define PHY_L_1000C_MSC (1<<11) /* Bit 11: M/S Configuration */ +#define PHY_L_1000C_RD (1<<10) /* Bit 10: Repeater/DTE */ +#define PHY_L_1000C_AFD (1<<9) /* Bit 9: Advertise Full Duplex */ +#define PHY_L_1000C_AHD (1<<8) /* Bit 8: Advertise Half Duplex */ + /* Bit 7..0: reserved */ + +/***** PHY_LONE_1000T_STAT 16 bit ro 1000Base-T Status Reg *****/ +#define PHY_L_1000S_MSF (1<<15) /* Bit 15: Master/Slave Fault */ +#define PHY_L_1000S_MSR (1<<14) /* Bit 14: Master/Slave Result */ +#define PHY_L_1000S_LRS (1<<13) /* Bit 13: Local Receiver Status */ +#define PHY_L_1000S_RRS (1<<12) /* Bit 12: Remote Receiver Status*/ +#define PHY_L_1000S_LP_FD (1<<11) /* Bit 11: Link Partner can FD */ +#define PHY_L_1000S_LP_HD (1<<10) /* Bit 10: Link Partner can HD */ + /* Bit 9..8: reserved */ +#define PHY_B_1000S_IEC (255<<0)/* Bit 7..0: Idle Error Count */ + +/***** PHY_LONE_EXT_STAT 16 bit ro Extended Status Register *****/ +#define PHY_L_ES_X_FD_CAP (1<<15) /* Bit 15: 1000Base-X FD capable */ +#define PHY_L_ES_X_HD_CAP (1<<14) /* Bit 14: 1000Base-X HD capable */ +#define PHY_L_ES_T_FD_CAP (1<<13) /* Bit 13: 1000Base-T FD capable */ +#define PHY_L_ES_T_HD_CAP (1<<12) /* Bit 12: 1000Base-T HD capable */ + /* Bit 11..0: reserved */ + +/***** PHY_LONE_PORT_CFG 16 bit r/w Port Configuration Reg *****/ +#define PHY_L_PC_REP_MODE (1<<15) /* Bit 15: Repeater Mode */ + /* Bit 14: reserved */ +#define PHY_L_PC_TX_DIS (1<<13) /* Bit 13: Tx output Disabled */ +#define PHY_L_PC_BY_SCR (1<<12) /* Bit 12: Bypass Scrambler */ +#define PHY_L_PC_BY_45 (1<<11) /* Bit 11: Bypass 4B5B-Decoder */ +#define PHY_L_PC_JAB_DIS (1<<10) /* Bit 10: Jabber Disabled */ +#define PHY_L_PC_SQE (1<<9) /* Bit 9: Enable Heartbeat */ +#define PHY_L_PC_TP_LOOP (1<<8) /* Bit 8: TP Loopback */ +#define PHY_L_PC_SSS (1<<7) /* Bit 7: Smart Speed Selection */ +#define PHY_L_PC_FIFO_SIZE (1<<6) /* Bit 6: FIFO Size */ +#define PHY_L_PC_PRE_EN (1<<5) /* Bit 5: Preamble Enable */ +#define PHY_L_PC_CIM (1<<4) /* Bit 4: Carrier Integrity Mon */ +#define PHY_L_PC_10_SER (1<<3) /* Bit 3: Use Serial Output */ +#define PHY_L_PC_ANISOL (1<<2) /* Bit 2: Unisolate Port */ +#define PHY_L_PC_TEN_BIT (1<<1) /* Bit 1: 10bit iface mode on */ +#define PHY_L_PC_ALTCLOCK (1<<0) /* Bit 0: (ro) ALTCLOCK Mode on */ + +/***** PHY_LONE_Q_STAT 16 bit ro Quick Status Reg *****/ +#define PHY_L_QS_D_RATE (3<<14) /* Bit 15..14: Data Rate */ +#define PHY_L_QS_TX_STAT (1<<13) /* Bit 13: Transmitting */ +#define PHY_L_QS_RX_STAT (1<<12) /* Bit 12: Receiving */ +#define PHY_L_QS_COL_STAT (1<<11) /* Bit 11: Collision */ +#define PHY_L_QS_L_STAT (1<<10) /* Bit 10: Link is up */ +#define PHY_L_QS_DUP_MOD (1<<9) /* Bit 9: Full/Half Duplex */ +#define PHY_L_QS_AN (1<<8) /* Bit 8: AutoNeg is On */ +#define PHY_L_QS_AN_C (1<<7) /* Bit 7: AN is Complete */ +#define PHY_L_QS_LLE (7<<4) /* Bit 6: Line Length Estim. */ +#define PHY_L_QS_PAUSE (1<<3) /* Bit 3: LP advertised Pause */ +#define PHY_L_QS_AS_PAUSE (1<<2) /* Bit 2: LP adv. asym. Pause */ +#define PHY_L_QS_ISOLATE (1<<1) /* Bit 1: CIM Isolated */ +#define PHY_L_QS_EVENT (1<<0) /* Bit 0: Event has occurred */ + +/***** PHY_LONE_INT_ENAB 16 bit r/w Interrupt Enable Reg *****/ +/***** PHY_LONE_INT_STAT 16 bit ro Interrupt Status Reg *****/ + /* Bit 15..14: reserved */ +#define PHY_L_IS_AN_F (1<<13) /* Bit 13: Autoneg fault */ + /* Bit 12: not described */ +#define PHY_L_IS_CROSS (1<<11) /* Bit 11: Crossover used */ +#define PHY_L_IS_POL (1<<10) /* Bit 10: Polarity correct. used*/ +#define PHY_L_IS_SS (1<<9) /* Bit 9: Smart Speed Downgrade*/ +#define PHY_L_IS_CFULL (1<<8) /* Bit 8: Counter Full */ +#define PHY_L_IS_AN_C (1<<7) /* Bit 7: AutoNeg Complete */ +#define PHY_L_IS_SPEED (1<<6) /* Bit 6: Speed Changed */ +#define PHY_L_IS_DUP (1<<5) /* Bit 5: Duplex Changed */ +#define PHY_L_IS_LS (1<<4) /* Bit 4: Link Status Changed */ +#define PHY_L_IS_ISOL (1<<3) /* Bit 3: Isolate Occured */ +#define PHY_L_IS_MDINT (1<<2) /* Bit 2: (ro) STAT: MII Int Pending */ +#define PHY_L_IS_INTEN (1<<1) /* Bit 1: ENAB: Enable IRQs */ +#define PHY_L_IS_FORCE (1<<0) /* Bit 0: ENAB: Force Interrupt */ + +#define PHY_L_DEF_MSK (PHY_L_IS_LS | PHY_L_IS_ISOL | \ + PHY_L_IS_INTEN) /* int. mask */ + +/***** PHY_LONE_LED_CFG 16 bit r/w LED Configuration Reg *****/ +#define PHY_L_LC_LEDC (3<<14) /* Bit 15..14: Col/Blink/On/Off */ +#define PHY_L_LC_LEDR (3<<12) /* Bit 13..12: Rx/Blink/On/Off */ +#define PHY_L_LC_LEDT (3<<10) /* Bit 11..10: Tx/Blink/On/Off */ +#define PHY_L_LC_LEDG (3<<8) /* Bit 9..8: Giga/Blink/On/Off */ +#define PHY_L_LC_LEDS (3<<6) /* Bit 7..6: 10-100/Blink/On/Off */ +#define PHY_L_LC_LEDL (3<<4) /* Bit 5..4: Link/Blink/On/Off */ +#define PHY_L_LC_LEDF (3<<2) /* Bit 3..2: Duplex/Blink/On/Off */ +#define PHY_L_LC_PSTRECH (1<<1) /* Bit 1: Strech LED Pulses */ +#define PHY_L_LC_FREQ (1<<0) /* Bit 0: 30/100 ms */ + +/***** PHY_LONE_PORT_CTRL 16 bit r/w Port Control Reg *****/ +#define PHY_L_PC_TX_TCLK (1<<15) /* Bit 15: Enable TX_TCLK */ + /* Bit 14: reserved */ +#define PHY_L_PC_ALT_NP (1<<13) /* Bit 14: Alternate Next Page */ +#define PHY_L_PC_GMII_ALT (1<<12) /* Bit 13: Alternate GMII driver */ + /* Bit 11: reserved */ +#define PHY_L_PC_TEN_CRS (1<<10) /* Bit 10: Extend CRS*/ + /* Bit 9..0: not described */ + +/***** PHY_LONE_CIM 16 bit ro CIM Reg *****/ +#define PHY_L_CIM_ISOL (255<<8)/* Bit 15..8: Isolate Count */ +#define PHY_L_CIM_FALSE_CAR (255<<0)/* Bit 7..0: False Carrier Count */ + + +/* + * Pause Bits (PHY_L_AN_ASP and PHY_L_AN_PC) encoding + */ +#define PHY_L_P_NO_PAUSE (0<<10) /* Bit 11..10: no Pause Mode */ +#define PHY_L_P_SYM_MD (1<<10) /* Bit 11..10: symmetric Pause Mode */ +#define PHY_L_P_ASYM_MD (2<<10) /* Bit 11..10: asymmetric Pause Mode */ +#define PHY_L_P_BOTH_MD (3<<10) /* Bit 11..10: both Pause Mode */ + + +/* + * National-Specific + */ +/***** PHY_NAT_1000T_CTRL 16 bit r/w 1000Base-T Control Reg *****/ +#define PHY_N_1000C_TEST (7<<13) /* Bit 15..13: Test Modes */ +#define PHY_N_1000C_MSE (1<<12) /* Bit 12: Master/Slave Enable */ +#define PHY_N_1000C_MSC (1<<11) /* Bit 11: M/S Configuration */ +#define PHY_N_1000C_RD (1<<10) /* Bit 10: Repeater/DTE */ +#define PHY_N_1000C_AFD (1<<9) /* Bit 9: Advertise Full Duplex */ +#define PHY_N_1000C_AHD (1<<8) /* Bit 8: Advertise Half Duplex */ +#define PHY_N_1000C_APC (1<<7) /* Bit 7: Asymetric Pause Cap. */ + /* Bit 6..0: reserved */ + +/***** PHY_NAT_1000T_STAT 16 bit ro 1000Base-T Status Reg *****/ +#define PHY_N_1000S_MSF (1<<15) /* Bit 15: Master/Slave Fault */ +#define PHY_N_1000S_MSR (1<<14) /* Bit 14: Master/Slave Result */ +#define PHY_N_1000S_LRS (1<<13) /* Bit 13: Local Receiver Status */ +#define PHY_N_1000S_RRS (1<<12) /* Bit 12: Remote Receiver Status*/ +#define PHY_N_1000S_LP_FD (1<<11) /* Bit 11: Link Partner can FD */ +#define PHY_N_1000S_LP_HD (1<<10) /* Bit 10: Link Partner can HD */ +#define PHY_N_1000C_LP_APC (1<<9) /* Bit 9: LP Asym. Pause Cap. */ + /* Bit 8: reserved */ +#define PHY_N_1000S_IEC (255<<0)/* Bit 7..0: Idle Error Count */ + +/***** PHY_NAT_EXT_STAT 16 bit ro Extended Status Register *****/ +#define PHY_N_ES_X_FD_CAP (1<<15) /* Bit 15: 1000Base-X FD capable */ +#define PHY_N_ES_X_HD_CAP (1<<14) /* Bit 14: 1000Base-X HD capable */ +#define PHY_N_ES_T_FD_CAP (1<<13) /* Bit 13: 1000Base-T FD capable */ +#define PHY_N_ES_T_HD_CAP (1<<12) /* Bit 12: 1000Base-T HD capable */ + /* Bit 11..0: reserved */ + +/* todo: those are still missing */ +/***** PHY_NAT_EXT_CTRL1 16 bit ro Extended Control Reg1 *****/ +/***** PHY_NAT_Q_STAT1 16 bit ro Quick Status Reg1 *****/ +/***** PHY_NAT_10B_OP 16 bit ro 10Base-T Operations Reg *****/ +/***** PHY_NAT_EXT_CTRL2 16 bit ro Extended Control Reg1 *****/ +/***** PHY_NAT_Q_STAT2 16 bit ro Quick Status Reg2 *****/ +/***** PHY_NAT_PHY_ADDR 16 bit ro PHY Address Register *****/ + +/* typedefs *******************************************************************/ + + +/* function prototypes ********************************************************/ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __INC_XMAC_H */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/skaddr.c linux/drivers/net/sk98lin/skaddr.c --- v2.2.13/linux/drivers/net/sk98lin/skaddr.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/skaddr.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,1300 @@ +/****************************************************************************** + * + * Name: skaddr.c + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.33 $ + * Date: $Date: 1999/05/28 10:56:06 $ + * Purpose: Manage Addresses (Multicast and Unicast) and Promiscuous Mode + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skge.c" for further information. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: skaddr.c,v $ + * Revision 1.33 1999/05/28 10:56:06 rassmann + * Editorial changes. + * + * Revision 1.32 1999/03/31 10:59:20 rassmann + * Returning Success instead of DupAddr if address shall be overridden + * with same value. + * + * Revision 1.31 1999/01/14 16:18:17 rassmann + * Corrected multicast initialization. + * + * Revision 1.30 1999/01/04 10:30:35 rassmann + * SkAddrOverride only possible after SK_INIT_IO phase. + * + * Revision 1.29 1998/12/29 13:13:10 rassmann + * An address override is now preserved in the SK_INIT_IO phase. + * All functions return an int now. + * Extended parameter checking. + * + * Revision 1.28 1998/12/01 11:45:53 rassmann + * Code cleanup. + * + * Revision 1.27 1998/12/01 09:22:49 rassmann + * SkAddrMcAdd and SkAddrMcUpdate returned SK_MC_FILTERING_INEXACT + * too often. + * + * Revision 1.26 1998/11/24 12:39:44 rassmann + * Reserved multicast entry for BPDU address. + * 13 multicast entries left for protocol. + * + * Revision 1.25 1998/11/17 16:54:23 rassmann + * Using exact match for up to 14 multicast addresses. + * Still receiving all multicasts if more addresses are added. + * + * Revision 1.24 1998/11/13 17:24:31 rassmann + * Changed return value of SkAddrOverride to int. + * + * Revision 1.23 1998/11/13 16:56:18 rassmann + * Added macro SK_ADDR_COMPARE. + * Changed return type of SkAddrOverride to SK_BOOL. + * + * Revision 1.22 1998/11/04 17:06:17 rassmann + * Corrected McUpdate and PromiscuousChange functions. + * + * Revision 1.21 1998/10/29 14:34:04 rassmann + * Clearing SK_ADDR struct at startup. + * + * Revision 1.20 1998/10/28 18:16:34 rassmann + * Avoiding I/Os before SK_INIT_RUN level. + * Aligning InexactFilter. + * + * Revision 1.19 1998/10/28 11:29:28 rassmann + * Programming physical address in SkAddrMcUpdate. + * Corrected programming of exact match entries. + * + * Revision 1.18 1998/10/28 10:34:48 rassmann + * Corrected reading of physical addresses. + * + * Revision 1.17 1998/10/28 10:26:13 rassmann + * Getting ports' current MAC addresses from EPROM now. + * Added debug output. + * + * Revision 1.16 1998/10/27 16:20:12 rassmann + * Reading MAC address byte by byte. + * + * Revision 1.15 1998/10/22 11:39:09 rassmann + * Corrected signed/unsigned mismatches. + * + * Revision 1.14 1998/10/19 17:12:35 rassmann + * Syntax corrections. + * + * Revision 1.13 1998/10/19 17:02:19 rassmann + * Now reading permanent MAC addresses from CRF. + * + * Revision 1.12 1998/10/15 15:15:48 rassmann + * Changed Flags Parameters from SK_U8 to int. + * Checked with lint. + * + * Revision 1.11 1998/09/24 19:15:12 rassmann + * Code cleanup. + * + * Revision 1.10 1998/09/18 20:18:54 rassmann + * Added HW access. + * Implemented swapping. + * + * Revision 1.9 1998/09/16 11:32:00 rassmann + * Including skdrv1st.h again. :( + * + * Revision 1.8 1998/09/16 11:09:34 rassmann + * Syntax corrections. + * + * Revision 1.7 1998/09/14 17:06:34 rassmann + * Minor changes. + * + * Revision 1.6 1998/09/07 08:45:41 rassmann + * Syntax corrections. + * + * Revision 1.5 1998/09/04 19:40:19 rassmann + * Interface enhancements. + * + * Revision 1.4 1998/09/04 12:14:12 rassmann + * Interface cleanup. + * + * Revision 1.3 1998/09/02 16:56:40 rassmann + * Updated interface. + * + * Revision 1.2 1998/08/27 14:26:09 rassmann + * Updated interface. + * + * Revision 1.1 1998/08/21 08:30:22 rassmann + * First public version. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Description: + * + * This module is intended to manage multicast addresses, address override, + * and promiscuous mode on GEnesis adapters. + * + * Address Layout: + * port address: physical MAC address + * 1st exact match: logical MAC address + * 2nd exact match: RLMT multicast + * exact match 3-13: OS-specific multicasts + * + * Include File Hierarchy: + * + * "skdrv1st.h" + * "skdrv2nd.h" + * + ******************************************************************************/ + +#ifndef lint +static const char SysKonnectFileId[] = + "@(#) $Id: skaddr.c,v 1.33 1999/05/28 10:56:06 rassmann Exp $ (C) SysKonnect."; +#endif /* !defined(lint) */ + +#define __SKADDR_C + +#ifdef __cplusplus +xxxx /* not supported yet - force error */ +extern "C" { +#endif /* cplusplus */ + +#include "h/skdrv1st.h" +#include "h/skdrv2nd.h" + +/* defines ********************************************************************/ + +#define SK_ADDR_CHEAT YES /* Cheat. */ + +/* + * G32: + * POLY equ 04C11DB6h ; CRC polynominal term + * bit-reversed: 6DB88320 + */ + +#define CRC32_POLY 0xEDB88320UL /* CRC32-Poly - XMAC: Little Endian */ +#if 0 +#define CRC32_POLY 0x6DB88320UL /* CRC32-Poly - XMAC: Little Endian */ +#endif /* 0 */ +#define HASH_BITS 6 /* #bits in hash */ +#define SK_MC_BIT 0x01 + +/* Error numbers and messages. */ + +#define SKERR_ADDR_E001 (SK_ERRBASE_ADDR + 0) +#define SKERR_ADDR_E001MSG "Bad Flags." +#define SKERR_ADDR_E002 (SKERR_ADDR_E001 + 1) +#define SKERR_ADDR_E002MSG "New Error." + +/* typedefs *******************************************************************/ + +/* None. */ + +/* global variables ***********************************************************/ + +/* 64-bit hash values with all bits set. */ + +SK_U16 OnesHash[4] = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}; + +/* local variables ************************************************************/ + +#ifdef DEBUG +static int Next0[SK_MAX_MACS] = {0, 0}; +#endif /* DEBUG */ + +/* functions ******************************************************************/ + +#if 0 +void SkAddrDummy(void) +{ + SkAddrInit(NULL, NULL, 0); +} /* SkAddrDummy */ +#endif /* 0 */ + +/****************************************************************************** + * + * SkAddrInit - initialize data, set state to init + * + * Description: + * + * SK_INIT_DATA + * ============ + * + * This routine clears the multicast tables and resets promiscuous mode. + * Some entries are reserved for the "logical board address", the + * SK-RLMT multicast address, and the BPDU multicast address. + * + * + * SK_INIT_IO + * ========== + * + * All permanent MAC addresses are read from EPROM. + * If the current MAC addresses are not already set in software, + * they are set to the values of the permanent addresses. + * The current addresses are written to the corresponding XMAC. + * + * + * SK_INIT_RUN + * =========== + * + * Nothing. + * + * Context: + * init, pageable + * + * Returns: + * SK_ADDR_SUCCESS + */ +int SkAddrInit( +SK_AC *pAC, /* the adapter context */ +SK_IOC IoC, /* I/O context */ +int Level) /* initialization level */ +{ + int j; + SK_U32 i; + SK_U8 *InAddr; + SK_U16 *OutAddr; + SK_ADDR_PORT *pAPort; + + switch (Level) { + case SK_INIT_DATA: + SK_MEMSET((char *)&pAC->Addr, 0, sizeof(SK_ADDR)); + + for (i = 0; i < SK_MAX_MACS; i++) { + pAPort = &pAC->Addr.Port[i]; + pAPort->PromMode = SK_PROM_MODE_NONE; + + pAPort->FirstExactMatchRlmt = + SK_ADDR_FIRST_MATCH_RLMT; + pAPort->FirstExactMatchDrv = + SK_ADDR_FIRST_MATCH_DRV; + pAPort->NextExactMatchRlmt = + SK_ADDR_FIRST_MATCH_RLMT; + pAPort->NextExactMatchDrv = + SK_ADDR_FIRST_MATCH_DRV; + +#if 0 + /* Not here ... */ + + /* Reset Promiscuous mode. */ + + (void)SkAddrPromiscuousChange( + pAC, + IoC, + i, + SK_PROM_MODE_NONE); +#endif /* 0 */ + } + +#ifdef DEBUG + for (i = 0; i < SK_MAX_MACS; i++) { + if (pAC->Addr.Port[i].NextExactMatchRlmt < + SK_ADDR_FIRST_MATCH_RLMT) { + Next0[i] |= 4; + } + } +#endif /* DEBUG */ + + /* pAC->Addr.InitDone = SK_INIT_DATA; */ + break; + + case SK_INIT_IO: + pAC->Addr.ActivePort = pAC->Rlmt.MacActive; + +#ifdef DEBUG + for (i = 0; i < SK_MAX_MACS; i++) { + if (pAC->Addr.Port[i].NextExactMatchRlmt < + SK_ADDR_FIRST_MATCH_RLMT) { + Next0[i] |= 8; + } + } +#endif /* DEBUG */ + + /* Read permanent virtual address from Control Register File. */ + + for (j = 0; j < SK_MAC_ADDR_LEN; j++) { + InAddr = (SK_U8 *)&pAC->Addr.PermanentMacAddress.a[j]; + SK_IN8(IoC, B2_MAC_1 + j, InAddr); + } + + if (!pAC->Addr.CurrentMacAddressSet) { + /* + * Set the current virtual MAC address + * to the permanent one. + */ + + pAC->Addr.CurrentMacAddress = + pAC->Addr.PermanentMacAddress; + pAC->Addr.CurrentMacAddressSet = SK_TRUE; + } + + /* Set the current virtual MAC address. */ + + pAC->Addr.Port[pAC->Addr.ActivePort].Exact[0] = + pAC->Addr.CurrentMacAddress; + +#ifdef xDEBUG + SK_DBG_MSG( + pAC, + SK_DBGMOD_ADDR, + SK_DBGCAT_INIT, + ("Permanent MAC Address: %02X %02X %02X %02X %02X %02X\n", + pAC->Addr.PermanentMacAddress.a[0], + pAC->Addr.PermanentMacAddress.a[1], + pAC->Addr.PermanentMacAddress.a[2], + pAC->Addr.PermanentMacAddress.a[3], + pAC->Addr.PermanentMacAddress.a[4], + pAC->Addr.PermanentMacAddress.a[5])) + SK_DBG_MSG( + pAC, + SK_DBGMOD_ADDR, + SK_DBGCAT_INIT, + ("Virtual MAC Address: %02X %02X %02X %02X %02X %02X\n", + pAC->Addr.CurrentMacAddress.a[0], + pAC->Addr.CurrentMacAddress.a[1], + pAC->Addr.CurrentMacAddress.a[2], + pAC->Addr.CurrentMacAddress.a[3], + pAC->Addr.CurrentMacAddress.a[4], + pAC->Addr.CurrentMacAddress.a[5])) +#endif /* DEBUG */ + +#if 0 + /* Not here ... */ + + (void)SkAddrMcUpdate(pAC, IoC, pAC->Addr.ActivePort); +#endif /* 0 */ + + for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + pAPort = &pAC->Addr.Port[i]; + + /* + * Read permanent port addresses from + * Control Register File. + */ + + for (j = 0; j < SK_MAC_ADDR_LEN; j++) { + InAddr = (SK_U8 *) + &pAPort->PermanentMacAddress.a[j]; + SK_IN8(IoC, B2_MAC_2 + 8 * i + j, InAddr); + } + + if (!pAPort->CurrentMacAddressSet) { + /* + * Set the current and previous physical + * MAC address of this port to its permanent + * MAC address. + */ + + pAPort->CurrentMacAddress = + pAPort->PermanentMacAddress; + pAPort->PreviousMacAddress = + pAPort->PermanentMacAddress; + pAPort->CurrentMacAddressSet = SK_TRUE; + } + + /* Set port's current MAC addresses. */ + + OutAddr = (SK_U16 *)&pAPort->CurrentMacAddress.a[0]; + XM_OUTADDR(IoC, i, XM_SA, OutAddr); + +#ifdef xDEBUG + SK_DBG_MSG( + pAC, + SK_DBGMOD_ADDR, + SK_DBGCAT_INIT, + ("Permanent Physical MAC Address: %02X %02X %02X %02X %02X %02X\n", + pAPort->PermanentMacAddress.a[0], + pAPort->PermanentMacAddress.a[1], + pAPort->PermanentMacAddress.a[2], + pAPort->PermanentMacAddress.a[3], + pAPort->PermanentMacAddress.a[4], + pAPort->PermanentMacAddress.a[5])) + SK_DBG_MSG( + pAC, + SK_DBGMOD_ADDR, + SK_DBGCAT_INIT, + ("Phsical MAC Address: %02X %02X %02X %02X %02X %02X\n", + pAPort->CurrentMacAddress.a[0], + pAPort->CurrentMacAddress.a[1], + pAPort->CurrentMacAddress.a[2], + pAPort->CurrentMacAddress.a[3], + pAPort->CurrentMacAddress.a[4], + pAPort->CurrentMacAddress.a[5])) +#endif /* DEBUG */ + } + /* pAC->Addr.InitDone = SK_INIT_IO; */ + break; + + case SK_INIT_RUN: +#ifdef DEBUG + for (i = 0; i < SK_MAX_MACS; i++) { + if (pAC->Addr.Port[i].NextExactMatchRlmt < + SK_ADDR_FIRST_MATCH_RLMT) { + Next0[i] |= 16; + } + } +#endif /* DEBUG */ + + /* pAC->Addr.InitDone = SK_INIT_RUN; */ + break; + + default: /* error */ + break; + } + + return (SK_ADDR_SUCCESS); +} /* SkAddrInit */ + + +/****************************************************************************** + * + * SkAddrMcClear - clear the multicast table + * + * Description: + * This routine clears the multicast table + * (either entry 2 or entries 3-16 and InexactFilter) of the given port. + * If not suppressed by Flag SK_MC_SW_ONLY, the hardware is updated + * immediately. + * + * Context: + * runtime, pageable + * may be called starting with SK_INIT_DATA with flag SK_MC_SW_ONLY + * may be called after SK_INIT_IO without limitation + * + * Returns: + * SK_ADDR_SUCCESS + * SK_ADDR_ILLEGAL_PORT + */ +int SkAddrMcClear( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* I/O context */ +SK_U32 PortIdx, /* Index of affected port */ +int Flags) /* permanent/non-perm, sw-only */ +{ + int i; + + if (PortIdx >= (SK_U32)pAC->GIni.GIMacsFound) { + return (SK_ADDR_ILLEGAL_PORT); + } + + if (Flags & SK_ADDR_PERMANENT) { + + /* Clear RLMT multicast addresses. */ + + pAC->Addr.Port[PortIdx].NextExactMatchRlmt = + SK_ADDR_FIRST_MATCH_RLMT; + } + else { /* not permanent => DRV */ + + /* Clear InexactFilter. */ + + for (i = 0; i < 8; i++) { + pAC->Addr.Port[PortIdx].InexactFilter.Bytes[i] = 0; + } + + /* Clear DRV multicast addresses. */ + + pAC->Addr.Port[PortIdx].NextExactMatchDrv = + SK_ADDR_FIRST_MATCH_DRV; + } + + if (!(Flags & SK_MC_SW_ONLY)) { + (void)SkAddrMcUpdate(pAC, IoC, PortIdx); + } + + return (SK_ADDR_SUCCESS); +} /* SkAddrMcClear */ + +#ifndef SK_ADDR_CHEAT +// RA;:;: +/****************************************************************************** + * + * SkCrc32McHash - hash multicast address + * + * Description: + * This routine computes the hash value for a multicast address. + * + * Notes: + * The code was adapted from the XaQti data sheet. + * + * Context: + * runtime, pageable + * + * Returns: + * Hash value of multicast address. + */ +unsigned SkCrc32McHash( +unsigned char *pMc) /* Multicast address */ +{ + unsigned Idx; + unsigned Bit; + unsigned Data; + unsigned Crc; + + Crc = 0xFFFFFFFFUL; + for (Idx = 0; Idx < SK_MAC_ADDR_LEN; Idx++) { + Data = *pMc++; + for (Bit = 0; Bit < 8; Bit++, Data >>= 1) { + Crc = (Crc >> 1) ^ + (((Crc ^ Data) & 1) ? CRC32_POLY : 0); + } + } + + return (Crc & ((1 << HASH_BITS) - 1)); + +} /* SkCrc32McHash */ + +#endif /* not SK_ADDR_CHEAT */ + +/****************************************************************************** + * + * SkAddrMcAdd - add a multicast address to a port + * + * Description: + * This routine enables reception for a given address on the given port. + * + * Notes: + * The return code is only valid for SK_PROM_MODE_NONE. + * + * In the current version, only RLMT may add addresses to the non-active + * port. + * + * The multicast bit is only checked if there are no free exact match + * entries. + * + * Context: + * runtime, pageable + * may be called after SK_INIT_DATA + * + * Returns: + * SK_MC_FILTERING_EXACT + * SK_MC_FILTERING_INEXACT + * SK_MC_ILLEGAL_ADDRESS + * SK_MC_ILLEGAL_PORT + * SK_MC_RLMT_OVERFLOW + */ +int SkAddrMcAdd( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* I/O context */ +SK_U32 PortIdx, /* Port Index */ +SK_MAC_ADDR *pMc, /* multicast address to be added */ +int Flags) /* permanent/non-permanent */ +{ + int i; + SK_U8 Inexact; +#ifndef SK_ADDR_CHEAT + unsigned HashBit; +#endif /* !defined(SK_ADDR_CHEAT) */ + + if (PortIdx >= (SK_U32)pAC->GIni.GIMacsFound) { + return (SK_ADDR_ILLEGAL_PORT); + } + + if (Flags & SK_ADDR_PERMANENT) { +#ifdef DEBUG + if (pAC->Addr.Port[PortIdx].NextExactMatchRlmt < + SK_ADDR_FIRST_MATCH_RLMT) { + Next0[PortIdx] |= 1; + return (SK_MC_RLMT_OVERFLOW); + } +#endif /* DEBUG */ + + if (pAC->Addr.Port[PortIdx].NextExactMatchRlmt > + SK_ADDR_LAST_MATCH_RLMT) { + return (SK_MC_RLMT_OVERFLOW); + } + + /* Set an RLMT multicast address. */ + + pAC->Addr.Port[PortIdx].Exact[ + pAC->Addr.Port[PortIdx].NextExactMatchRlmt++] = *pMc; + + return (SK_MC_FILTERING_EXACT); + } + + /* Not PERMANENT => DRV */ + + if (PortIdx != pAC->Addr.ActivePort) { + + /* Only RLMT is allowed to do this. */ + + return (SK_MC_ILLEGAL_PORT); + } + +#ifdef DEBUG + if (pAC->Addr.Port[PortIdx].NextExactMatchDrv < + SK_ADDR_FIRST_MATCH_DRV) { + Next0[PortIdx] |= 2; + return (SK_MC_RLMT_OVERFLOW); + } +#endif /* DEBUG */ + + if (pAC->Addr.Port[PortIdx].NextExactMatchDrv <= SK_ADDR_LAST_MATCH_DRV) { + + /* Set exact match entry. */ + + pAC->Addr.Port[PortIdx].Exact[ + pAC->Addr.Port[PortIdx].NextExactMatchDrv++] = *pMc; + + /* Clear InexactFilter. */ + + for (i = 0; i < 8; i++) { + pAC->Addr.Port[PortIdx + ].InexactFilter.Bytes[i] = 0; + } + } + else { + if (!(pMc->a[0] & SK_MC_BIT)) { + + /* + * Hashing only possible with + * multicast addresses. + */ + + return (SK_MC_ILLEGAL_ADDRESS); + } +#ifndef SK_ADDR_CHEAT + /* Compute hash value of address. */ +RA;:;: untested + HashBit = SkCrc32McHash(&pMc->a[0]); + + /* Add bit to InexactFilter. */ + + pAC->Addr.Port[PortIdx].InexactFilter.Bytes[HashBit / 8] |= + 1 << (HashBit % 8); + +#else /* SK_ADDR_CHEAT */ + + /* Set all bits in InexactFilter. */ + + for (i = 0; i < 8; i++) { + pAC->Addr.Port[PortIdx].InexactFilter.Bytes[i] = 0xFF; + } +#endif /* SK_ADDR_CHEAT */ + } + + for (Inexact = 0, i = 0; i < 8; i++) { + Inexact |= pAC->Addr.Port[PortIdx].InexactFilter.Bytes[i]; + } + + if (Inexact == 0 && pAC->Addr.Port[PortIdx].PromMode == 0) { + return (SK_MC_FILTERING_EXACT); + } + else { + return (SK_MC_FILTERING_INEXACT); + } +} /* SkAddrMcAdd */ + + +/****************************************************************************** + * + * SkAddrMcUpdate - update the HW MC address table and set the MAC address + * + * Description: + * This routine enables reception of the addresses contained in a local + * table for a given port. + * It also programs the port's current physical MAC address. + * + * Notes: + * The return code is only valid for SK_PROM_MODE_NONE. + * + * Context: + * runtime, pageable + * may be called after SK_INIT_IO + * + * Returns: + * SK_MC_FILTERING_EXACT + * SK_MC_FILTERING_INEXACT + * SK_ADDR_ILLEGAL_PORT + */ +int SkAddrMcUpdate( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* I/O context */ +SK_U32 PortIdx) /* Port Index */ +{ + SK_U32 i; + SK_U8 Inexact; + SK_U16 *OutAddr; + SK_U16 LoMode; /* Lower 16 bits of XMAC Mode Reg. */ + SK_ADDR_PORT *pAPort; + + if (PortIdx >= (SK_U32)pAC->GIni.GIMacsFound) { + return (SK_ADDR_ILLEGAL_PORT); + } + + SK_DBG_MSG( + pAC, + SK_DBGMOD_ADDR, + SK_DBGCAT_CTRL, + ("SkAddrMcUpdate on Port %u.\n", PortIdx)) + + pAPort = &pAC->Addr.Port[PortIdx]; + +#ifdef DEBUG + SK_DBG_MSG( + pAC, + SK_DBGMOD_ADDR, + SK_DBGCAT_CTRL, + ("Next0 on Port %d: %d\n", PortIdx, Next0[PortIdx])) +#endif /* DEBUG */ + + for (i = 0; /* Also program the virtual address. */ + i < pAPort->NextExactMatchRlmt; + i++) { + + /* Set exact match address i on HW. */ + + OutAddr = (SK_U16 *)&pAPort->Exact[i].a[0]; + XM_OUTADDR(IoC, PortIdx, XM_EXM(i), OutAddr); + } + + /* Clear other permanent exact match addresses on HW. */ + + if (pAPort->NextExactMatchRlmt <= SK_ADDR_LAST_MATCH_RLMT) { + SkXmClrExactAddr( + pAC, + IoC, + PortIdx, + pAPort->NextExactMatchRlmt, + SK_ADDR_LAST_MATCH_RLMT); + } + + for (i = pAPort->FirstExactMatchDrv; + i < pAPort->NextExactMatchDrv; + i++) { + + OutAddr = (SK_U16 *)&pAPort->Exact[i].a[0]; + XM_OUTADDR(IoC, PortIdx, XM_EXM(i), OutAddr); + + } + + /* Clear other non-permanent exact match addresses on HW. */ + + if (pAPort->NextExactMatchDrv <= SK_ADDR_LAST_MATCH_DRV) { + SkXmClrExactAddr( + pAC, + IoC, + PortIdx, + pAPort->NextExactMatchDrv, + SK_ADDR_LAST_MATCH_DRV); + } + + for (Inexact = 0xFF, i = 0; i < 8; i++) { + Inexact &= pAPort->InexactFilter.Bytes[i]; + } + if (pAPort->PromMode & SK_PROM_MODE_ALL_MC) { + + /* Set all bits in 64-bit hash register. */ + + XM_OUTHASH(IoC, PortIdx, XM_HSM, &OnesHash); + + /* Set bit 15 in mode register. */ + + XM_IN16(IoC, PortIdx, XM_MODE, &LoMode); + LoMode |= XM_MD_ENA_HSH; + XM_OUT16(IoC, PortIdx, XM_MODE, LoMode); + } + else if (Inexact != 0xFF) { + + /* Clear bit 15 in mode register. */ + + XM_IN16(IoC, PortIdx, XM_MODE, &LoMode); + LoMode &= ~XM_MD_ENA_HSH; + XM_OUT16(IoC, PortIdx, XM_MODE, LoMode); + } + else { + /* Set 64-bit hash register to InexactFilter. */ + + XM_OUTHASH( + IoC, + PortIdx, + XM_HSM, + &pAPort->InexactFilter.Bytes[0]); + + /* Set bit 15 in mode register. */ + + XM_IN16(IoC, PortIdx, XM_MODE, &LoMode); + LoMode |= XM_MD_ENA_HSH; + XM_OUT16(IoC, PortIdx, XM_MODE, LoMode); + } + + if (pAPort->PromMode != SK_PROM_MODE_NONE) { + (void)SkAddrPromiscuousChange( + pAC, + IoC, + PortIdx, + pAPort->PromMode); + } + + /* Set port's current MAC address. */ + + OutAddr = (SK_U16 *)&pAPort->CurrentMacAddress.a[0]; + XM_OUTADDR(IoC, PortIdx, XM_SA, OutAddr); + +#ifdef DEBUG + for (i = 0; /* Also program the virtual address. */ + i < pAPort->NextExactMatchRlmt; + i++) { + SK_U8 InAddr8[6]; + SK_U16 *InAddr; + + /* Get exact match address i from port PortIdx. */ + + InAddr = (SK_U16 *)&InAddr8[0]; + XM_INADDR(IoC, PortIdx, XM_EXM(i), InAddr); + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("MC address %d on Port %u: %02x %02x %02x %02x %02x %02x -- %02x %02x %02x %02x %02x %02x.\n", + i, + PortIdx, + InAddr8[0], + InAddr8[1], + InAddr8[2], + InAddr8[3], + InAddr8[4], + InAddr8[5], + pAPort->Exact[i].a[0], + pAPort->Exact[i].a[1], + pAPort->Exact[i].a[2], + pAPort->Exact[i].a[3], + pAPort->Exact[i].a[4], + pAPort->Exact[i].a[5])) + } +#endif /* DEBUG */ + + /* Determine return value. */ + + for (Inexact = 0, i = 0; i < 8; i++) { + Inexact |= pAPort->InexactFilter.Bytes[i]; + } + if (Inexact == 0 && pAPort->PromMode == 0) { + return (SK_MC_FILTERING_EXACT); + } + else { + return (SK_MC_FILTERING_INEXACT); + } +} /* SkAddrMcUpdate */ + + +/****************************************************************************** + * + * SkAddrOverride - override a port's MAC address + * + * Description: + * This routine overrides the MAC address of one port. + * + * Context: + * runtime, pageable + * may be called after SK_INIT_IO + * + * Returns: + * SK_ADDR_SUCCESS if successful. + * SK_ADDR_DUPLICATE_ADDRESS if duplicate MAC address. + * SK_ADDR_MULTICAST_ADDRESS if multicast or broadcast address. + * SK_ADDR_TOO_EARLY if SK_INIT_IO was not executed before. + */ +int SkAddrOverride( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* I/O context */ +SK_U32 PortIdx, /* Port Index */ +SK_MAC_ADDR *pNewAddr, /* new MAC address */ +int Flags) /* logical/physical address */ +{ + SK_U32 i; + SK_U16 *OutAddr; + SK_EVPARA Para; +#if 0 + SK_MAC_ADDR NewAddr; /* new MAC address */ + SK_U8 AddrBits; +#endif /* 0 */ + + if (PortIdx >= (SK_U32)pAC->GIni.GIMacsFound) { + return (SK_ADDR_ILLEGAL_PORT); + } + + if (pNewAddr->a[0] & SK_MC_BIT) { + return (SK_ADDR_MULTICAST_ADDRESS); + } + +#if 0 +DANGEROUS! + if (Flags & SK_ADDR_PHYSICAL_ADDRESS) { /* Physical address. */ + if (!pAC->Addr.Port[PortIdx].CurrentMacAddressSet) { + pAC->Addr.Port[PortIdx].PreviousMacAddress = *pNewAddr; + pAC->Addr.Port[PortIdx].CurrentMacAddress = *pNewAddr; + pAC->Addr.Port[PortIdx].CurrentMacAddressSet = SK_TRUE; + return (SK_ADDR_SUCCESS); + } + } + else { + if (!pAC->Addr.CurrentMacAddressSet) { + pAC->Addr.CurrentMacAddress = *pNewAddr; + pAC->Addr.CurrentMacAddressSet = SK_TRUE; + return (SK_ADDR_SUCCESS); + } + } +DANGEROUS! +#endif /* 0 */ + + if (!pAC->Addr.CurrentMacAddressSet) { + return (SK_ADDR_TOO_EARLY); + } + + if (Flags & SK_ADDR_PHYSICAL_ADDRESS) { /* Physical address. */ + if (SK_ADDR_EQUAL(pNewAddr->a, pAC->Addr.CurrentMacAddress.a)) { + return (SK_ADDR_DUPLICATE_ADDRESS); + } + + for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + if (!pAC->Addr.Port[i].CurrentMacAddressSet) { + return (SK_ADDR_TOO_EARLY); + } + + if (SK_ADDR_EQUAL( + pNewAddr->a, + pAC->Addr.Port[i].CurrentMacAddress.a)) { + if (i == PortIdx) { + return (SK_ADDR_SUCCESS); + } + else { + return (SK_ADDR_DUPLICATE_ADDRESS); + } + } + } + + pAC->Addr.Port[PortIdx].PreviousMacAddress = + pAC->Addr.Port[PortIdx].CurrentMacAddress; + pAC->Addr.Port[PortIdx].CurrentMacAddress = *pNewAddr; + + /* Change port's address. */ + + OutAddr = (SK_U16 *)pNewAddr; + XM_OUTADDR(IoC, PortIdx, XM_SA, OutAddr); + + /* Report address change to RLMT. */ + + Para.Para32[0] = PortIdx; + SkEventQueue(pAC, SKGE_RLMT, SK_RLMT_PORT_ADDR, Para); + } + else { /* Logical Address. */ + if (SK_ADDR_EQUAL(pNewAddr->a, pAC->Addr.CurrentMacAddress.a)) { + return (SK_ADDR_SUCCESS); + } + + for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + if (!pAC->Addr.Port[i].CurrentMacAddressSet) { + return (SK_ADDR_TOO_EARLY); + } + + if (SK_ADDR_EQUAL( + pNewAddr->a, + pAC->Addr.Port[i].CurrentMacAddress.a)) { + return (SK_ADDR_DUPLICATE_ADDRESS); + } + } + + pAC->Addr.CurrentMacAddress = *pNewAddr; + pAC->Addr.Port[PortIdx].Exact[0] = *pNewAddr; + +#ifdef DEBUG + SK_DBG_MSG( + pAC, + SK_DBGMOD_ADDR, + SK_DBGCAT_CTRL, + ("Permanent MAC Address: %02X %02X %02X %02X %02X %02X\n", + pAC->Addr.PermanentMacAddress.a[0], + pAC->Addr.PermanentMacAddress.a[1], + pAC->Addr.PermanentMacAddress.a[2], + pAC->Addr.PermanentMacAddress.a[3], + pAC->Addr.PermanentMacAddress.a[4], + pAC->Addr.PermanentMacAddress.a[5])) + SK_DBG_MSG( + pAC, + SK_DBGMOD_ADDR, + SK_DBGCAT_CTRL, + ("New Virtual MAC Address: %02X %02X %02X %02X %02X %02X\n", + pAC->Addr.CurrentMacAddress.a[0], + pAC->Addr.CurrentMacAddress.a[1], + pAC->Addr.CurrentMacAddress.a[2], + pAC->Addr.CurrentMacAddress.a[3], + pAC->Addr.CurrentMacAddress.a[4], + pAC->Addr.CurrentMacAddress.a[5])) +#endif /* DEBUG */ + + /* Write address to first exact match entry of active port. */ + + (void)SkAddrMcUpdate(pAC, IoC, PortIdx); + } + + return (SK_ADDR_SUCCESS); +} /* SkAddrOverride */ + + +/****************************************************************************** + * + * SkAddrPromiscuousChange - set promiscuous mode for given port + * + * Description: + * This routine manages promiscuous mode: + * - none + * - all LLC frames + * - all MC frames + * + * Context: + * runtime, pageable + * may be called after SK_INIT_IO + * + * Returns: + * SK_ADDR_SUCCESS + * SK_ADDR_ILLEGAL_PORT + */ +int SkAddrPromiscuousChange( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* I/O context */ +SK_U32 PortIdx, /* port whose promiscuous mode changes */ +int NewPromMode) /* new promiscuous mode */ +{ + int i; + SK_BOOL InexactModeBit; + SK_U8 Inexact; + SK_U8 HwInexact; + SK_FILTER64 HwInexactFilter; + SK_U16 LoMode; /* Lower 16 bits of XMAC Mode Register. */ + int CurPromMode = SK_PROM_MODE_NONE; + + if (PortIdx >= (SK_U32)pAC->GIni.GIMacsFound) { + return (SK_ADDR_ILLEGAL_PORT); + } + + /* Read CurPromMode from Hardware. */ + + XM_IN16(IoC, PortIdx, XM_MODE, &LoMode); + + if (LoMode & XM_MD_ENA_PROM) { + CurPromMode |= SK_PROM_MODE_LLC; + } + + for (Inexact = 0xFF, i = 0; i < 8; i++) { + Inexact &= pAC->Addr.Port[PortIdx].InexactFilter.Bytes[i]; + } + if (Inexact == 0xFF) { + CurPromMode |= (pAC->Addr.Port[PortIdx].PromMode & + SK_PROM_MODE_ALL_MC); + } + else { + /* Read InexactModeBit (bit 15 in mode register). */ + + XM_IN16(IoC, PortIdx, XM_MODE, &LoMode); + + InexactModeBit = (LoMode & XM_MD_ENA_HSH) != 0; + + /* Read 64-bit hash register from HW. */ + + XM_INHASH(IoC, PortIdx, XM_HSM, &HwInexactFilter.Bytes[0]); + + for (HwInexact = 0xFF, i = 0; i < 8; i++) { + HwInexact &= HwInexactFilter.Bytes[i]; + } + + if (InexactModeBit && (HwInexact == 0xFF)) { + CurPromMode |= SK_PROM_MODE_ALL_MC; + } + } + + pAC->Addr.Port[PortIdx].PromMode = NewPromMode; + + if (NewPromMode == CurPromMode) { + return (SK_ADDR_SUCCESS); + } + + if ((NewPromMode & SK_PROM_MODE_ALL_MC) && + !(CurPromMode & SK_PROM_MODE_ALL_MC)) { /* All MC. */ + + /* Set all bits in 64-bit hash register. */ + + XM_OUTHASH(IoC, PortIdx, XM_HSM, &OnesHash); + + /* Set bit 15 in mode register. */ + + XM_IN16(IoC, PortIdx, XM_MODE, &LoMode); + LoMode |= XM_MD_ENA_HSH; + XM_OUT16(IoC, PortIdx, XM_MODE, LoMode); + } + else if ((CurPromMode & SK_PROM_MODE_ALL_MC) && + !(NewPromMode & SK_PROM_MODE_ALL_MC)) { /* Norm MC. */ + + for (Inexact = 0, i = 0; i < 8; i++) { + Inexact |= + pAC->Addr.Port[PortIdx].InexactFilter.Bytes[i]; + } + if (Inexact == 0) { + /* Clear bit 15 in mode register. */ + + XM_IN16(IoC, PortIdx, XM_MODE, &LoMode); + LoMode &= ~XM_MD_ENA_HSH; + XM_OUT16(IoC, PortIdx, XM_MODE, LoMode); + } + else { + /* Set 64-bit hash register to InexactFilter. */ + + XM_OUTHASH( + IoC, + PortIdx, + XM_HSM, + &pAC->Addr.Port[PortIdx + ].InexactFilter.Bytes[0]); + + /* Set bit 15 in mode register. */ + + XM_IN16(IoC, PortIdx, XM_MODE, &LoMode); + LoMode |= XM_MD_ENA_HSH; + XM_OUT16(IoC, PortIdx, XM_MODE, LoMode); + } + } + + if ((NewPromMode & SK_PROM_MODE_LLC) && + !(CurPromMode & SK_PROM_MODE_LLC)) { /* Prom. LLC */ + + /* Set promiscuous bit in mode register. */ + + XM_IN16(IoC, PortIdx, XM_MODE, &LoMode); +#if 0 + /* Receive MAC frames. */ + + LoMode |= XM_MD_RX_MCTRL; +#endif /* 0 */ + LoMode |= XM_MD_ENA_PROM; + XM_OUT16(IoC, PortIdx, XM_MODE, LoMode); + } + else if ((CurPromMode & SK_PROM_MODE_LLC) && + !(NewPromMode & SK_PROM_MODE_LLC)) { /* Norm. LLC. */ + + /* Clear promiscuous bit in mode register. */ + + XM_IN16(IoC, PortIdx, XM_MODE, &LoMode); +#if 0 + /* Don't receive MAC frames. */ + + LoMode &= ~XM_MD_RX_MCTRL; +#endif /* 0 */ + LoMode &= ~XM_MD_ENA_PROM; + XM_OUT16(IoC, PortIdx, XM_MODE, LoMode); + } + + return (SK_ADDR_SUCCESS); +} /* SkAddrPromiscuousChange */ + + +/****************************************************************************** + * + * SkAddrSwap - swap address info + * + * Description: + * This routine swaps address info of two ports. + * + * Context: + * runtime, pageable + * may be called after SK_INIT_IO + * + * Returns: + * SK_ADDR_SUCCESS + * SK_ADDR_ILLEGAL_PORT + */ +int SkAddrSwap( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* I/O context */ +SK_U32 FromPortIdx, /* Port1 Index */ +SK_U32 ToPortIdx) /* Port2 Index */ +{ + int i; + SK_U8 Byte; + SK_MAC_ADDR MacAddr; + SK_U32 DWord; + + if (FromPortIdx >= (SK_U32)pAC->GIni.GIMacsFound) { + return (SK_ADDR_ILLEGAL_PORT); + } + + if (ToPortIdx >= (SK_U32)pAC->GIni.GIMacsFound) { + return (SK_ADDR_ILLEGAL_PORT); + } + + /* + * Swap + * - Exact Match Entries + * - FirstExactMatchRlmt; + * - NextExactMatchRlmt; + * - FirstExactMatchDrv; + * - NextExactMatchDrv; + * - 64-bit filter + * - Promiscuous Mode + * of ports. + */ + + for (i = 0; i < SK_ADDR_EXACT_MATCHES; i++) { + MacAddr = pAC->Addr.Port[FromPortIdx].Exact[i]; + pAC->Addr.Port[FromPortIdx].Exact[i] = + pAC->Addr.Port[ToPortIdx].Exact[i]; + pAC->Addr.Port[ToPortIdx].Exact[i] = MacAddr; + } + + for (i = 0; i < 8; i++) { + Byte = pAC->Addr.Port[FromPortIdx].InexactFilter.Bytes[i]; + pAC->Addr.Port[FromPortIdx].InexactFilter.Bytes[i] = + pAC->Addr.Port[ToPortIdx].InexactFilter.Bytes[i]; + pAC->Addr.Port[ToPortIdx].InexactFilter.Bytes[i] = Byte; + } + + i = pAC->Addr.Port[FromPortIdx].PromMode; + pAC->Addr.Port[FromPortIdx].PromMode = + pAC->Addr.Port[ToPortIdx].PromMode; + pAC->Addr.Port[ToPortIdx].PromMode = i; + + DWord = pAC->Addr.Port[FromPortIdx].FirstExactMatchRlmt; + pAC->Addr.Port[FromPortIdx].FirstExactMatchRlmt = + pAC->Addr.Port[ToPortIdx].FirstExactMatchRlmt; + pAC->Addr.Port[ToPortIdx].FirstExactMatchRlmt = DWord; + + DWord = pAC->Addr.Port[FromPortIdx].NextExactMatchRlmt; + pAC->Addr.Port[FromPortIdx].NextExactMatchRlmt = + pAC->Addr.Port[ToPortIdx].NextExactMatchRlmt; + pAC->Addr.Port[ToPortIdx].NextExactMatchRlmt = DWord; + + DWord = pAC->Addr.Port[FromPortIdx].FirstExactMatchDrv; + pAC->Addr.Port[FromPortIdx].FirstExactMatchDrv = + pAC->Addr.Port[ToPortIdx].FirstExactMatchDrv; + pAC->Addr.Port[ToPortIdx].FirstExactMatchDrv = DWord; + + DWord = pAC->Addr.Port[FromPortIdx].NextExactMatchDrv; + pAC->Addr.Port[FromPortIdx].NextExactMatchDrv = + pAC->Addr.Port[ToPortIdx].NextExactMatchDrv; + pAC->Addr.Port[ToPortIdx].NextExactMatchDrv = DWord; + + pAC->Addr.ActivePort = ToPortIdx; + + (void)SkAddrMcUpdate(pAC, IoC, FromPortIdx); + (void)SkAddrMcUpdate(pAC, IoC, ToPortIdx); + + return (SK_ADDR_SUCCESS); +} /* SkAddrSwap */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/skcsum.c linux/drivers/net/sk98lin/skcsum.c --- v2.2.13/linux/drivers/net/sk98lin/skcsum.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/skcsum.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,847 @@ +/****************************************************************************** + * + * Name: skcsum.c + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.3 $ + * Date: $Date: 1999/05/10 08:39:33 $ + * Purpose: Store/verify Internet checksum in send/receive packets. + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skge.c" for further information. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: skcsum.c,v $ + * Revision 1.3 1999/05/10 08:39:33 mkarl + * prevent overflows in SKCS_HTON16 + * fixed a bug in pseudo header checksum calculation + * added some comments + * + * Revision 1.2 1998/10/22 11:53:28 swolf + * Now using SK_DBG_MSG. + * + * Revision 1.1 1998/09/01 15:35:41 swolf + * initial revision + * + * 13-May-1998 sw Created. + * + ******************************************************************************/ + +#ifdef SK_USE_CSUM /* Check if CSUM is to be used. */ + +#ifndef lint +static const char SysKonnectFileId[] = "@(#)" + "$Id: skcsum.c,v 1.3 1999/05/10 08:39:33 mkarl Exp $" + " (C) SysKonnect."; +#endif /* !lint */ + +/****************************************************************************** + * + * Description: + * + * This is the "GEnesis" common module "CSUM". + * + * This module contains the code necessary to calculate, store, and verify the + * Internet Checksum of IP, TCP, and UDP frames. + * + * "GEnesis" is an abbreviation of "Gigabit Ethernet Network System in Silicon" + * and is the code name of this SysKonnect project. + * + * Compilation Options: + * + * SK_USE_CSUM - Define if CSUM is to be used. Otherwise, CSUM will be an + * empty module. + * + * SKCS_OVERWRITE_PROTO - Define to overwrite the default protocol id + * definitions. In this case, all SKCS_PROTO_xxx definitions must be made + * external. + * + * SKCS_OVERWRITE_STATUS - Define to overwrite the default return status + * definitions. In this case, all SKCS_STATUS_xxx definitions must be made + * external. + * + * Include File Hierarchy: + * + * "h/skdrv1st.h" + * "h/skcsum.h" + * "h/sktypes.h" + * "h/skqueue.h" + * "h/skdrv2nd.h" + * + ******************************************************************************/ + +#include "h/skdrv1st.h" +#include "h/skcsum.h" +#include "h/skdrv2nd.h" + +/* defines ********************************************************************/ + +/* The size of an Ethernet MAC header. */ +#define SKCS_ETHERNET_MAC_HEADER_SIZE (6+6+2) + +/* The size of the used topology's MAC header. */ +#define SKCS_MAC_HEADER_SIZE SKCS_ETHERNET_MAC_HEADER_SIZE + +/* The size of the IP header without any option fields. */ +#define SKCS_IP_HEADER_SIZE 20 + +/* + * Field offsets within the IP header. + */ + +/* "Internet Header Version" and "Length". */ +#define SKCS_OFS_IP_HEADER_VERSION_AND_LENGTH 0 + +/* "Total Length". */ +#define SKCS_OFS_IP_TOTAL_LENGTH 2 + +/* "Flags" "Fragment Offset". */ +#define SKCS_OFS_IP_FLAGS_AND_FRAGMENT_OFFSET 6 + +/* "Next Level Protocol" identifier. */ +#define SKCS_OFS_IP_NEXT_LEVEL_PROTOCOL 9 + +/* Source IP address. */ +#define SKCS_OFS_IP_SOURCE_ADDRESS 12 + +/* Destination IP address. */ +#define SKCS_OFS_IP_DESTINATION_ADDRESS 16 + +/* IP "Next Level Protocol" identifiers (see RFC 790). */ +#define SKCS_PROTO_ID_TCP 6 /* Transport Control Protocol */ +#define SKCS_PROTO_ID_UDP 17 /* User Datagram Protocol */ + +/* IP "Don't Fragment" bit. */ +#define SKCS_IP_DONT_FRAGMENT SKCS_HTON16(0x4000) + +/* Add a byte offset to a pointer. */ +#define SKCS_IDX(pPtr, Ofs) ((void *) ((char *) (pPtr) + (Ofs))) + +/* + * Macros that convert host to network representation and vice versa, i.e. + * little/big endian conversion on little endian machines only. + */ +#ifdef SK_LITTLE_ENDIAN +#define SKCS_HTON16(Val16) (((unsigned) (Val16) >> 8) | (((Val16) & 0xFF) << 8)) +#endif /* SK_LITTLE_ENDIAN */ +#ifdef SK_BIG_ENDIAN +#define SKCS_HTON16(Val16) (Val16) +#endif /* SK_BIG_ENDIAN */ +#define SKCS_NTOH16(Val16) SKCS_HTON16(Val16) + +/* typedefs *******************************************************************/ + +/* function prototypes ********************************************************/ + +/****************************************************************************** + * + * SkCsGetSendInfo - get checksum information for a send packet + * + * Description: + * Get all checksum information necessary to send a TCP or UDP packet. The + * function checks the IP header passed to it. If the high-level protocol + * is either TCP or UDP the pseudo header checksum is calculated and + * returned. + * + * The function returns the total length of the IP header (including any + * IP option fields), which is the same as the start offset of the IP data + * which in turn is the start offset of the TCP or UDP header. + * + * The function also returns the TCP or UDP pseudo header checksum, which + * should be used as the start value for the hardware checksum calculation. + * (Note that any actual pseudo header checksum can never calculate to + * zero.) + * + * Note: + * There is a bug in the ASIC whic may lead to wrong checksums. + * + * Arguments: + * pAc - A pointer to the adapter context struct. + * + * pIpHeader - Pointer to IP header. Must be at least the IP header *not* + * including any option fields, i.e. at least 20 bytes. + * + * Note: This pointer will be used to address 8-, 16-, and 32-bit + * variables with the respective alignment offsets relative to the pointer. + * Thus, the pointer should point to a 32-bit aligned address. If the + * target system cannot address 32-bit variables on non 32-bit aligned + * addresses, then the pointer *must* point to a 32-bit aligned address. + * + * pPacketInfo - A pointer to the packet information structure for this + * packet. Before calling this SkCsGetSendInfo(), the following field must + * be initialized: + * + * ProtocolFlags - Initialize with any combination of + * SKCS_PROTO_XXX bit flags. SkCsGetSendInfo() will only work on + * the protocols specified here. Any protocol(s) not specified + * here will be ignored. + * + * Note: Only one checksum can be calculated in hardware. Thus, if + * SKCS_PROTO_IP is specified in the 'ProtocolFlags', + * SkCsGetSendInfo() must calculate the IP header checksum in + * software. It might be a better idea to have the calling + * protocol stack calculate the IP header checksum. + * + * Returns: N/A + * On return, the following fields in 'pPacketInfo' may or may not have + * been filled with information, depending on the protocol(s) found in the + * packet: + * + * ProtocolFlags - Returns the SKCS_PROTO_XXX bit flags of the protocol(s) + * that were both requested by the caller and actually found in the packet. + * Protocol(s) not specified by the caller and/or not found in the packet + * will have their respective SKCS_PROTO_XXX bit flags reset. + * + * Note: For IP fragments, TCP and UDP packet information is ignored. + * + * IpHeaderLength - The total length in bytes of the complete IP header + * including any option fields is returned here. This is the start offset + * of the IP data, i.e. the TCP or UDP header if present. + * + * IpHeaderChecksum - If IP has been specified in the 'ProtocolFlags', the + * 16-bit Internet Checksum of the IP header is returned here. This value + * is to be stored into the packet's 'IP Header Checksum' field. + * + * PseudoHeaderChecksum - If this is a TCP or UDP packet and if TCP or UDP + * has been specified in the 'ProtocolFlags', the 16-bit Internet Checksum + * of the TCP or UDP pseudo header is returned here. + */ +void SkCsGetSendInfo( +SK_AC *pAc, /* Adapter context struct. */ +void *pIpHeader, /* IP header. */ +SKCS_PACKET_INFO *pPacketInfo) /* Packet information struct. */ +{ + /* Internet Header Version found in IP header. */ + unsigned InternetHeaderVersion; + + /* Length of the IP header as found in IP header. */ + unsigned IpHeaderLength; + + /* Bit field specifiying the desired/found protocols. */ + unsigned ProtocolFlags; + + /* Next level protocol identifier found in IP header. */ + unsigned NextLevelProtocol; + + /* Length of IP data portion. */ + unsigned IpDataLength; + + /* TCP/UDP pseudo header checksum. */ + unsigned long PseudoHeaderChecksum; + + /* Pointer to next level protocol statistics structure. */ + SKCS_PROTO_STATS *NextLevelProtoStats; + + /* Temporary variable. */ + unsigned Tmp; + + Tmp = *(SK_U8 *) + SKCS_IDX(pIpHeader, SKCS_OFS_IP_HEADER_VERSION_AND_LENGTH); + + /* Get the Internet Header Version (IHV). */ + /* Note: The IHV is stored in the upper four bits. */ + + InternetHeaderVersion = Tmp >> 4; + + /* Check the Internet Header Version. */ + /* Note: We currently only support IP version 4. */ + + if (InternetHeaderVersion != 4) { /* IPv4? */ + SK_DBG_MSG(pAc, SK_DBGMOD_CSUM, SK_DBGCAT_ERR | SK_DBGCAT_TX, + ("Tx: Unknown Internet Header Version %u.\n", + InternetHeaderVersion)); + pPacketInfo->ProtocolFlags = 0; + pAc->Csum.ProtoStats[SKCS_PROTO_STATS_IP].TxUnableCts++; + return; + } + + /* Get the IP header length (IHL). */ + /* + * Note: The IHL is stored in the lower four bits as the number of + * 4-byte words. + */ + + IpHeaderLength = (Tmp & 0xf) * 4; + pPacketInfo->IpHeaderLength = IpHeaderLength; + + /* Check the IP header length. */ + + /* 04-Aug-1998 sw - Really check the IHL? Necessary? */ + + if (IpHeaderLength < 5*4) { + SK_DBG_MSG(pAc, SK_DBGMOD_CSUM, SK_DBGCAT_ERR | SK_DBGCAT_TX, + ("Tx: Invalid IP Header Length %u.\n", IpHeaderLength)); + pPacketInfo->ProtocolFlags = 0; + pAc->Csum.ProtoStats[SKCS_PROTO_STATS_IP].TxUnableCts++; + return; + } + + /* This is an IPv4 frame with a header of valid length. */ + + pAc->Csum.ProtoStats[SKCS_PROTO_STATS_IP].TxOkCts++; + + /* Check if we should calculate the IP header checksum. */ + + ProtocolFlags = pPacketInfo->ProtocolFlags; + + if (ProtocolFlags & SKCS_PROTO_IP) { + pPacketInfo->IpHeaderChecksum = + SkCsCalculateChecksum(pIpHeader, IpHeaderLength); + } + + /* Get the next level protocol identifier. */ + + NextLevelProtocol = + *(SK_U8 *) SKCS_IDX(pIpHeader, SKCS_OFS_IP_NEXT_LEVEL_PROTOCOL); + + /* + * Check if this is a TCP or UDP frame and if we should calculate the + * TCP/UDP pseudo header checksum. + * + * Also clear all protocol bit flags of protocols not present in the + * frame. + */ + + if ((ProtocolFlags & SKCS_PROTO_TCP) != 0 && + NextLevelProtocol == SKCS_PROTO_ID_TCP) { + /* TCP/IP frame. */ + ProtocolFlags &= SKCS_PROTO_TCP | SKCS_PROTO_IP; + NextLevelProtoStats = + &pAc->Csum.ProtoStats[SKCS_PROTO_STATS_TCP]; + } + else if ((ProtocolFlags & SKCS_PROTO_UDP) != 0 && + NextLevelProtocol == SKCS_PROTO_ID_UDP) { + /* UDP/IP frame. */ + ProtocolFlags &= SKCS_PROTO_UDP | SKCS_PROTO_IP; + NextLevelProtoStats = + &pAc->Csum.ProtoStats[SKCS_PROTO_STATS_UDP]; + } + else { + /* + * Either not a TCP or UDP frame and/or TCP/UDP processing not + * specified. + */ + pPacketInfo->ProtocolFlags = ProtocolFlags & SKCS_PROTO_IP; + return; + } + + /* Check if this is an IP fragment. */ + + /* + * Note: An IP fragment has a non-zero "Fragment Offset" field and/or + * the "More Fragments" bit set. Thus, if both the "Fragment Offset" + * and the "More Fragments" are zero, it is *not* a fragment. We can + * easily check both at the same time since they are in the same 16-bit + * word. + */ + + if ((*(SK_U16 *) + SKCS_IDX(pIpHeader, SKCS_OFS_IP_FLAGS_AND_FRAGMENT_OFFSET) & + ~SKCS_IP_DONT_FRAGMENT) != 0) { + /* IP fragment; ignore all other protocols. */ + pPacketInfo->ProtocolFlags = ProtocolFlags & SKCS_PROTO_IP; + NextLevelProtoStats->TxUnableCts++; + return; + } + + /* + * Calculate the TCP/UDP pseudo header checksum. + */ + + /* Get total length of IP header and data. */ + + IpDataLength = + *(SK_U16 *) SKCS_IDX(pIpHeader, SKCS_OFS_IP_TOTAL_LENGTH); + + /* Get length of IP data portion. */ + + IpDataLength = SKCS_NTOH16(IpDataLength) - IpHeaderLength; + + /* Calculate the sum of all pseudo header fields (16-bit). */ + + PseudoHeaderChecksum = + (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, + SKCS_OFS_IP_SOURCE_ADDRESS + 0) + + (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, + SKCS_OFS_IP_SOURCE_ADDRESS + 2) + + (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, + SKCS_OFS_IP_DESTINATION_ADDRESS + 0) + + (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, + SKCS_OFS_IP_DESTINATION_ADDRESS + 2) + + (unsigned long) (NextLevelProtocol << 8) + + (unsigned long) SKCS_HTON16(IpDataLength); + + /* Add-in any carries. */ + + SKCS_OC_ADD(PseudoHeaderChecksum, PseudoHeaderChecksum, 0); + + /* Add-in any new carry. */ + + SKCS_OC_ADD(pPacketInfo->PseudoHeaderChecksum, PseudoHeaderChecksum, 0); + + NextLevelProtoStats->TxOkCts++; /* Success. */ +} + +/****************************************************************************** + * + * SkCsGetReceiveInfo - verify checksum information for a received packet + * + * Description: + * Verify a received frame's checksum. The function returns a status code + * reflecting the result of the verification. + * + * Note: + * Before calling this function you have to verify that the frame is + * not padded and Checksum1 and Checksum2 are bigger than 1. + * + * Arguments: + * pAc - Pointer to adapter context struct. + * + * pIpHeader - Pointer to IP header. Must be at least the length in bytes + * of the received IP header including any option fields. + * + * Note: The actual length of the IP header is stored in the lower four + * bits of the first octet of the IP header as the number of 4-byte words, + * so it must be multiplied by four to get the length in bytes. Thus, the + * maximum IP header length is 15 * 4 = 60 bytes. + * + * Checksum1 - The first 16-bit Internet Checksum calculated by the + * hardware starting at the offset returned by SkCsSetReceiveFlags(). + * + * Checksum2 - The second 16-bit Internet Checksum calculated by the + * hardware starting at the offset returned by SkCsSetReceiveFlags(). + * + * Returns: + * SKCS_STATUS_UNKNOWN_IP_VERSION - Not an IP v4 frame. + * SKCS_STATUS_IP_CSUM_ERROR - IP checksum error. + * SKCS_STATUS_IP_FRAGMENT - IP fragment (IP checksum ok). + * SKCS_STATUS_IP_CSUM_OK - IP checksum ok (not a TCP or UDP frame). + * SKCS_STATUS_TCP_CSUM_ERROR - TCP checksum error (IP checksum ok). + * SKCS_STATUS_UDP_CSUM_ERROR - UDP checksum error (IP checksum ok). + * SKCS_STATUS_TCP_CSUM_OK - IP and TCP checksum ok. + * SKCS_STATUS_UDP_CSUM_OK - IP and UDP checksum ok. + * + * Note: The SKCS_STATUS_XXX values returned here are *not* defined by + * the CSUM module but must be defined in some header file by the module + * using CSUM. In this way, the calling module can assign return values + * for its own needs, e.g. by assigning bit flags to the individual + * protocols. + */ +SKCS_STATUS SkCsGetReceiveInfo( +SK_AC *pAc, /* Adapter context struct. */ +void *pIpHeader, /* IP header. */ +unsigned Checksum1, /* Hardware checksum 1. */ +unsigned Checksum2) /* Hardware checksum 2. */ +{ + /* Internet Header Version found in IP header. */ + unsigned InternetHeaderVersion; + + /* Length of the IP header as found in IP header. */ + unsigned IpHeaderLength; + + /* Length of IP data portion. */ + unsigned IpDataLength; + + /* IP header checksum. */ + unsigned IpHeaderChecksum; + + /* IP header options checksum, if any. */ + unsigned IpOptionsChecksum; + + /* IP data checksum, i.e. TCP/UDP checksum. */ + unsigned IpDataChecksum; + + /* Next level protocol identifier found in IP header. */ + unsigned NextLevelProtocol; + + /* The checksum of the "next level protocol", i.e. TCP or UDP. */ + unsigned long NextLevelProtocolChecksum; + + /* Pointer to next level protocol statistics structure. */ + SKCS_PROTO_STATS *NextLevelProtoStats; + + /* Temporary variable. */ + unsigned Tmp; + + Tmp = *(SK_U8 *) + SKCS_IDX(pIpHeader, SKCS_OFS_IP_HEADER_VERSION_AND_LENGTH); + + /* Get the Internet Header Version (IHV). */ + /* Note: The IHV is stored in the upper four bits. */ + + InternetHeaderVersion = Tmp >> 4; + + /* Check the Internet Header Version. */ + /* Note: We currently only support IP version 4. */ + + if (InternetHeaderVersion != 4) { /* IPv4? */ + SK_DBG_MSG(pAc, SK_DBGMOD_CSUM, SK_DBGCAT_ERR | SK_DBGCAT_RX, + ("Rx: Unknown Internet Header Version %u.\n", + InternetHeaderVersion)); + pAc->Csum.ProtoStats[SKCS_PROTO_STATS_IP].RxUnableCts++; + return (SKCS_STATUS_UNKNOWN_IP_VERSION); + } + + /* Get the IP header length (IHL). */ + /* + * Note: The IHL is stored in the lower four bits as the number of + * 4-byte words. + */ + + IpHeaderLength = (Tmp & 0xf) * 4; + + /* Check the IP header length. */ + + /* 04-Aug-1998 sw - Really check the IHL? Necessary? */ + + if (IpHeaderLength < 5*4) { + SK_DBG_MSG(pAc, SK_DBGMOD_CSUM, SK_DBGCAT_ERR | SK_DBGCAT_RX, + ("Rx: Invalid IP Header Length %u.\n", IpHeaderLength)); + pAc->Csum.ProtoStats[SKCS_PROTO_STATS_IP].RxErrCts++; + return (SKCS_STATUS_IP_CSUM_ERROR); + } + + /* This is an IPv4 frame with a header of valid length. */ + + /* Get the IP header and data checksum. */ + + IpDataChecksum = Checksum2; + + /* + * The IP header checksum is calculated as follows: + * + * IpHeaderChecksum = Checksum1 - Checksum2 + */ + + SKCS_OC_SUB(IpHeaderChecksum, Checksum1, Checksum2); + + /* Check if any IP header options. */ + + if (IpHeaderLength > SKCS_IP_HEADER_SIZE) { + + /* Get the IP options checksum. */ + + IpOptionsChecksum = SkCsCalculateChecksum( + SKCS_IDX(pIpHeader, SKCS_IP_HEADER_SIZE), + IpHeaderLength - SKCS_IP_HEADER_SIZE); + + /* Adjust the IP header and IP data checksums. */ + + SKCS_OC_ADD(IpHeaderChecksum, + IpHeaderChecksum, IpOptionsChecksum); + + SKCS_OC_SUB(IpDataChecksum, + IpDataChecksum, IpOptionsChecksum); + } + + /* Check if the IP header checksum is ok. */ + /* + * NOTE: We must check the IP header checksum even if the caller does + * not want us to do so because we cannot do any further processing of + * the packet without a valid IP checksum. + */ + + if (IpHeaderChecksum != 0xFFFF) { + pAc->Csum.ProtoStats[SKCS_PROTO_STATS_IP].RxErrCts++; + return (SKCS_STATUS_IP_CSUM_ERROR); + } + + /* Get the next level protocol identifier. */ + + NextLevelProtocol = + *(SK_U8 *) SKCS_IDX(pIpHeader, SKCS_OFS_IP_NEXT_LEVEL_PROTOCOL); + + /* + * Check if this is a TCP or UDP frame and if we should calculate the + * TCP/UDP pseudo header checksum. + * + * Also clear all protocol bit flags of protocols not present in the + * frame. + */ + + if ((pAc->Csum.ReceiveFlags & SKCS_PROTO_TCP) != 0 && + NextLevelProtocol == SKCS_PROTO_ID_TCP) { + /* TCP/IP frame. */ + NextLevelProtoStats = + &pAc->Csum.ProtoStats[SKCS_PROTO_STATS_TCP]; + } + else if ((pAc->Csum.ReceiveFlags & SKCS_PROTO_UDP) != 0 && + NextLevelProtocol == SKCS_PROTO_ID_UDP) { + /* UDP/IP frame. */ + NextLevelProtoStats = + &pAc->Csum.ProtoStats[SKCS_PROTO_STATS_UDP]; + } + else { + /* + * Either not a TCP or UDP frame and/or TCP/UDP processing not + * specified. + */ + return (SKCS_STATUS_IP_CSUM_OK); + } + + /* Check if this is an IP fragment. */ + + /* + * Note: An IP fragment has a non-zero "Fragment Offset" field and/or + * the "More Fragments" bit set. Thus, if both the "Fragment Offset" + * and the "More Fragments" are zero, it is *not* a fragment. We can + * easily check both at the same time since they are in the same 16-bit + * word. + */ + + if ((*(SK_U16 *) + SKCS_IDX(pIpHeader, SKCS_OFS_IP_FLAGS_AND_FRAGMENT_OFFSET) & + ~SKCS_IP_DONT_FRAGMENT) != 0) { + /* IP fragment; ignore all other protocols. */ + NextLevelProtoStats->RxUnableCts++; + return (SKCS_STATUS_IP_FRAGMENT); + } + + /* + * Calculate the TCP/UDP checksum. + */ + + /* Get total length of IP header and data. */ + + IpDataLength = + *(SK_U16 *) SKCS_IDX(pIpHeader, SKCS_OFS_IP_TOTAL_LENGTH); + + /* Get length of IP data portion. */ + + IpDataLength = SKCS_NTOH16(IpDataLength) - IpHeaderLength; + + NextLevelProtocolChecksum = + + /* Calculate the pseudo header checksum. */ + + (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, + SKCS_OFS_IP_SOURCE_ADDRESS + 0) + + (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, + SKCS_OFS_IP_SOURCE_ADDRESS + 2) + + (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, + SKCS_OFS_IP_DESTINATION_ADDRESS + 0) + + (unsigned long) *(SK_U16 *) SKCS_IDX(pIpHeader, + SKCS_OFS_IP_DESTINATION_ADDRESS + 2) + + (unsigned long) (NextLevelProtocol << 8) + + (unsigned long) SKCS_HTON16(IpDataLength) + + + /* Add the TCP/UDP header checksum. */ + + (unsigned long) IpDataChecksum; + + /* Add-in any carries. */ + + SKCS_OC_ADD(NextLevelProtocolChecksum, NextLevelProtocolChecksum, 0); + + /* Add-in any new carry. */ + + SKCS_OC_ADD(NextLevelProtocolChecksum, NextLevelProtocolChecksum, 0); + + /* Check if the TCP/UDP checksum is ok. */ + + if ((unsigned) NextLevelProtocolChecksum == 0xFFFF) { + + /* TCP/UDP checksum ok. */ + + NextLevelProtoStats->RxOkCts++; + + return (NextLevelProtocol == SKCS_PROTO_ID_TCP ? + SKCS_STATUS_TCP_CSUM_OK : SKCS_STATUS_UDP_CSUM_OK); + } + + /* TCP/UDP checksum error. */ + + NextLevelProtoStats->RxErrCts++; + + return (NextLevelProtocol == SKCS_PROTO_ID_TCP ? + SKCS_STATUS_TCP_CSUM_ERROR : SKCS_STATUS_UDP_CSUM_ERROR); +} + +/****************************************************************************** + * + * SkCsSetReceiveFlags - set checksum receive flags + * + * Description: + * Use this function to set the various receive flags. According to the + * protocol flags set by the caller, the start offsets within received + * packets of the two hardware checksums are returned. These offsets must + * be stored in all receive descriptors. + * + * Arguments: + * pAc - Pointer to adapter context struct. + * + * ReceiveFlags - Any combination of SK_PROTO_XXX flags of the protocols + * for which the caller wants checksum information on received frames. + * + * pChecksum1Offset - The start offset of the first receive descriptor + * hardware checksum to be calculated for received frames is returned + * here. + * + * pChecksum2Offset - The start offset of the second receive descriptor + * hardware checksum to be calculated for received frames is returned + * here. + * + * Returns: N/A + * Returns the two hardware checksum start offsets. + */ +void SkCsSetReceiveFlags( +SK_AC *pAc, /* Adapter context struct. */ +unsigned ReceiveFlags, /* New receive flags. */ +unsigned *pChecksum1Offset, /* Offset for hardware checksum 1. */ +unsigned *pChecksum2Offset) /* Offset for hardware checksum 2. */ +{ + /* Save the receive flags. */ + + pAc->Csum.ReceiveFlags = ReceiveFlags; + + /* First checksum start offset is the IP header. */ + *pChecksum1Offset = SKCS_MAC_HEADER_SIZE; + + /* + * Second checksum start offset is the IP data. Note that this may vary + * if there are any IP header options in the actual packet. + */ + *pChecksum2Offset = SKCS_MAC_HEADER_SIZE + SKCS_IP_HEADER_SIZE; +} + +#ifndef SkCsCalculateChecksum +/****************************************************************************** + * + * SkCsCalculateChecksum - calculate checksum for specified data + * + * Description: + * Calculate and return the 16-bit Internet Checksum for the specified + * data. + * + * Arguments: + * pData - Pointer to data for which the checksum shall be calculated. + * Note: The pointer should be aligned on a 16-bit boundary. + * + * Length - Length in bytes of data to checksum. + * + * Returns: + * The 16-bit Internet Checksum for the specified data. + * + * Note: The checksum is calculated in the machine's natural byte order, + * i.e. little vs. big endian. Thus, the resulting checksum is different + * for the same input data on little and big endian machines. + * + * However, when written back to the network packet, the byte order is + * always in correct network order. + */ +unsigned SkCsCalculateChecksum( +void *pData, /* Data to checksum. */ +unsigned Length) /* Length of data. */ +{ + SK_U16 *pU16; /* Pointer to the data as 16-bit words. */ + unsigned long Checksum; /* Checksum; must be at least 32 bits. */ + + /* Sum up all 16-bit words. */ + + pU16 = (SK_U16 *) pData; + for (Checksum = 0; Length > 1; Length -= 2) { + Checksum += *pU16++; + } + + /* If this is an odd number of bytes, add-in the last byte. */ + + if (Length > 0) { +#ifdef SK_BIG_ENDIAN + /* Add the last byte as the high byte. */ + Checksum += ((unsigned) *(SK_U8 *) pU16) << 8; +#else /* !SK_BIG_ENDIAN */ + /* Add the last byte as the low byte. */ + Checksum += *(SK_U8 *) pU16; +#endif /* !SK_BIG_ENDIAN */ + } + + /* Add-in any carries. */ + + SKCS_OC_ADD(Checksum, Checksum, 0); + + /* Add-in any new carry. */ + + SKCS_OC_ADD(Checksum, Checksum, 0); + + /* Note: All bits beyond the 16-bit limit are now zero. */ + + return ((unsigned) Checksum); +} +#endif /* SkCsCalculateChecksum */ + +/****************************************************************************** + * + * SkCsEvent - the CSUM event dispatcher + * + * Description: + * This is the event handler for the CSUM module. + * + * Arguments: + * pAc - Pointer to adapter context. + * + * Ioc - I/O context. + * + * Event - Event id. + * + * Param - Event dependent parameter. + * + * Returns: + * The 16-bit Internet Checksum for the specified data. + * + * Note: The checksum is calculated in the machine's natural byte order, + * i.e. little vs. big endian. Thus, the resulting checksum is different + * for the same input data on little and big endian machines. + * + * However, when written back to the network packet, the byte order is + * always in correct network order. + */ +int SkCsEvent( +SK_AC *pAc, /* Pointer to adapter context. */ +SK_IOC Ioc, /* I/O context. */ +SK_U32 Event, /* Event id. */ +SK_EVPARA Param) /* Event dependent parameter. */ +{ + int ProtoIndex; + + switch (Event) { + /* + * Clear protocol statistics. + * + * Param - Protocol index, or -1 for all protocols. + */ + case SK_CSUM_EVENT_CLEAR_PROTO_STATS: + + ProtoIndex = (int) Param.Para32[0]; + if (ProtoIndex < 0) { /* Clear for all protocols. */ + memset(&pAc->Csum.ProtoStats[0], 0, + sizeof(pAc->Csum.ProtoStats)); + } + else { /* Clear for individual protocol. */ + memset(&pAc->Csum.ProtoStats[ProtoIndex], 0, + sizeof(pAc->Csum.ProtoStats[ProtoIndex])); + } + break; + default: + break; + } + return (0); /* Success. */ +} + +#endif /* SK_USE_CSUM */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/skge.c linux/drivers/net/sk98lin/skge.c --- v2.2.13/linux/drivers/net/sk98lin/skge.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/skge.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,3686 @@ +/****************************************************************************** + * + * Name: skge.c + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.27 $ + * Date: $Date: 1999/11/25 09:06:28 $ + * Purpose: The main driver source module + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * Driver for SysKonnect Gigabit Ethernet Server Adapters: + * + * SK-9841 (single link 1000Base-LX) + * SK-9842 (dual link 1000Base-LX) + * SK-9843 (single link 1000Base-SX) + * SK-9844 (dual link 1000Base-SX) + * SK-9821 (single link 1000Base-T) + * SK-9822 (dual link 1000Base-T) + * + * Created 10-Feb-1999, based on Linux' acenic.c, 3c59x.c and + * SysKonnects GEnesis Solaris driver + * Author: Christoph Goos (cgoos@syskonnect.de) + * + * Address all question to: linux@syskonnect.de + * + * The technical manual for the adapters is available from SysKonnect's + * web pages: www.syskonnect.com + * Goto "Support" and search Knowledge Base for "manual". + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: skge.c,v $ + * Revision 1.27 1999/11/25 09:06:28 cgoos + * Changed base_addr to unsigned long. + * + * Revision 1.26 1999/11/22 13:29:16 cgoos + * Changed license header to GPL. + * Changes for inclusion in linux kernel (2.2.13). + * Removed 2.0.x defines. + * Changed SkGeProbe to skge_probe. + * Added checks in SkGeIoctl. + * + * Revision 1.25 1999/10/07 14:47:52 cgoos + * Changed 984x to 98xx. + * + * Revision 1.24 1999/09/30 07:21:01 cgoos + * Removed SK_RLMT_SLOW_LOOKAHEAD option. + * Giving spanning tree packets also to OS now. + * + * Revision 1.23 1999/09/29 07:36:50 cgoos + * Changed assignment for IsBc/IsMc. + * + * Revision 1.22 1999/09/28 12:57:09 cgoos + * Added CheckQueue also to Single-Port-ISR. + * + * Revision 1.21 1999/09/28 12:42:41 cgoos + * Changed parameter strings for RlmtMode. + * + * Revision 1.20 1999/09/28 12:37:57 cgoos + * Added CheckQueue for fast delivery of RLMT frames. + * + * Revision 1.19 1999/09/16 07:57:25 cgoos + * Copperfield changes. + * + * Revision 1.18 1999/09/03 13:06:30 cgoos + * Fixed RlmtMode=CheckSeg bug: wrong DEV_KFREE_SKB in RLMT_SEND caused + * double allocated skb's. + * FrameStat in ReceiveIrq was accessed via wrong Rxd. + * Queue size for async. standby Tx queue was zero. + * FillRxLimit of 0 could cause problems with ReQueue, changed to 1. + * Removed debug output of checksum statistic. + * + * Revision 1.17 1999/08/11 13:55:27 cgoos + * Transmit descriptor polling was not reenabled after SkGePortInit. + * + * Revision 1.16 1999/07/27 15:17:29 cgoos + * Added some "\n" in output strings (removed while debuging...). + * + * Revision 1.15 1999/07/23 12:09:30 cgoos + * Performance optimization, rx checksumming, large frame support. + * + * Revision 1.14 1999/07/14 11:26:27 cgoos + * Removed Link LED settings (now in RLMT). + * Added status output at NET UP. + * Fixed SMP problems with Tx and SWITCH running in parallel. + * Fixed return code problem at RLMT_SEND event. + * + * Revision 1.13 1999/04/07 10:11:42 cgoos + * Fixed Single Port problems. + * Fixed Multi-Adapter problems. + * Always display startup string. + * + * Revision 1.12 1999/03/29 12:26:37 cgoos + * Reversed locking to fine granularity. + * Fixed skb double alloc problem (caused by incorrect xmit return code). + * Enhanced function descriptions. + * + * Revision 1.11 1999/03/15 13:10:51 cgoos + * Changed device identifier in output string to ethX. + * + * Revision 1.10 1999/03/15 12:12:34 cgoos + * Changed copyright notice. + * + * Revision 1.9 1999/03/15 12:10:17 cgoos + * Changed locking to one driver lock. + * Added check of SK_AC-size (for consistency with library). + * + * Revision 1.8 1999/03/08 11:44:02 cgoos + * Fixed missing dev->tbusy in SkGeXmit. + * Changed large frame (jumbo) buffer number. + * Added copying of short frames. + * + * Revision 1.7 1999/03/04 13:26:57 cgoos + * Fixed spinlock calls for SMP. + * + * Revision 1.6 1999/03/02 09:53:51 cgoos + * Added descriptor revertion for big endian machines. + * + * Revision 1.5 1999/03/01 08:50:59 cgoos + * Fixed SkGeChangeMtu. + * Fixed pci config space accesses. + * + * Revision 1.4 1999/02/18 15:48:44 cgoos + * Corrected some printk's. + * + * Revision 1.3 1999/02/18 12:45:55 cgoos + * Changed SK_MAX_CARD_PARAM to default 16 + * + * Revision 1.2 1999/02/18 10:55:32 cgoos + * Removed SkGeDrvTimeStamp function. + * Printing "ethX:" before adapter type at adapter init. + * + * + * 10-Feb-1999 cg Created, based on Linux' acenic.c, 3c59x.c and + * SysKonnects GEnesis Solaris driver + * + ******************************************************************************/ + +/****************************************************************************** + * + * Possible compiler options (#define xxx / -Dxxx): + * + * debugging can be enable by changing SK_DEBUG_CHKMOD and + * SK_DEBUG_CHKCAT in makefile (described there). + * + ******************************************************************************/ + +/****************************************************************************** + * + * Description: + * + * This is the main module of the Linux GE driver. + * + * All source files except skge.c, skdrv1st.h, skdrv2nd.h and sktypes.h + * are part of SysKonnect's COMMON MODULES for the SK-98xx adapters. + * Those are used for drivers on multiple OS', so some thing may seem + * unnecessary complicated on Linux. Please do not try to 'clean up' + * them without VERY good reasons, because this will make it more + * difficult to keep the Linux driver in synchronisation with the + * other versions. + * + * Include file hierarchy: + * + * + * + * "h/skdrv1st.h" + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * those three depending on kernel version used: + * + * + * + * + * + * "h/skerror.h" + * "h/skdebug.h" + * "h/sktypes.h" + * "h/lm80.h" + * "h/xmac_ii.h" + * + * "h/skdrv2nd.h" + * "h/skqueue.h" + * "h/skgehwt.h" + * "h/sktimer.h" + * "h/ski2c.h" + * "h/skgepnmi.h" + * "h/skvpd.h" + * "h/skgehw.h" + * "h/skgeinit.h" + * "h/skaddr.h" + * "h/skgesirq.h" + * "h/skcsum.h" + * "h/skrlmt.h" + * + ******************************************************************************/ + +static const char SysKonnectFileId[] = "@(#)" __FILE__ " (C) SysKonnect."; +static const char SysKonnectBuildNumber[] = + "@(#)SK-BUILD: 3.02 (19991111) PL: 01"; + +#include + +#include "h/skdrv1st.h" +#include "h/skdrv2nd.h" + +/* defines ******************************************************************/ + +#define BOOT_STRING "sk98lin: Network Device Driver v3.02\n" \ + "Copyright (C) 1999 SysKonnect" + +#define VER_STRING "3.02" + + +/* for debuging on x86 only */ +/* #define BREAKPOINT() asm(" int $3"); */ + +/* use of a transmit complete interrupt */ +#define USE_TX_COMPLETE + +/* use interrupt moderation (for tx complete only) */ +// #define USE_INT_MOD +#define INTS_PER_SEC 1000 + +/* + * threshold for copying small receive frames + * set to 0 to avoid copying, set to 9001 to copy all frames + */ +#define SK_COPY_THRESHOLD 200 + +/* number of adapters that can be configured via command line params */ +#define SK_MAX_CARD_PARAM 16 + +/* + * use those defines for a compile-in version of the driver instead + * of command line parameters + */ +// #define AUTO_NEG_A {"Sense", } +// #define AUTO_NEG_B {"Sense", } +// #define DUP_CAP_A {"Both", } +// #define DUP_CAP_B {"Both", } +// #define FLOW_CTRL_A {"SymOrRem", } +// #define FLOW_CTRL_B {"SymOrRem", } +// #define ROLE_A {"Auto", } +// #define ROLE_B {"Auto", } +// #define PREF_PORT {"A", } +// #define RLMT_MODE {"CheckLink", } + + +#define DEV_KFREE_SKB(skb) dev_kfree_skb(skb); + +/* function prototypes ******************************************************/ +static void FreeResources(struct device *dev); +int init_module(void); +void cleanup_module(void); +static int SkGeBoardInit(struct device *dev, SK_AC *pAC); +static SK_BOOL BoardAllocMem(SK_AC *pAC); +static void BoardFreeMem(SK_AC *pAC); +static void BoardInitMem(SK_AC *pAC); +static void SetupRing(SK_AC*, void*, uintptr_t, RXD**, RXD**, RXD**, + int*, SK_BOOL); + +static void SkGeIsr(int irq, void *dev_id, struct pt_regs *ptregs); +static void SkGeIsrOnePort(int irq, void *dev_id, struct pt_regs *ptregs); +static int SkGeOpen(struct device *dev); +static int SkGeClose(struct device *dev); +static int SkGeXmit(struct sk_buff *skb, struct device *dev); +static int SkGeSetMacAddr(struct device *dev, void *p); +static void SkGeSetRxMode(struct device *dev); +static struct net_device_stats *SkGeStats(struct device *dev); +static int SkGeIoctl(struct device *dev, struct ifreq *rq, int cmd); +static void GetConfiguration(SK_AC*); +static void ProductStr(SK_AC*); +static int XmitFrame(SK_AC*, TX_PORT*, struct sk_buff*); +static void FreeTxDescriptors(SK_AC*pAC, TX_PORT*); +static void FillRxRing(SK_AC*, RX_PORT*); +static SK_BOOL FillRxDescriptor(SK_AC*, RX_PORT*); +static void ReceiveIrq(SK_AC*, RX_PORT*); +static void ClearAndStartRx(SK_AC*, int); +static void ClearTxIrq(SK_AC*, int, int); +static void ClearRxRing(SK_AC*, RX_PORT*); +static void ClearTxRing(SK_AC*, TX_PORT*); +static void SetQueueSizes(SK_AC *pAC); +static int SkGeChangeMtu(struct device *dev, int new_mtu); +static void PortReInitBmu(SK_AC*, int); +static int SkGeIocMib(SK_AC*, unsigned int, int); +#ifdef DEBUG +static void DumpMsg(struct sk_buff*, char*); +static void DumpData(char*, int); +static void DumpLong(char*, int); +#endif + + +/* global variables *********************************************************/ +static const char *BootString = BOOT_STRING; +static struct device *root_dev = NULL; +static int probed __initdata = 0; + +/* local variables **********************************************************/ +static uintptr_t TxQueueAddr[SK_MAX_MACS][2] = {{0x680, 0x600},{0x780, 0x700}}; +static uintptr_t RxQueueAddr[SK_MAX_MACS] = {0x400, 0x480}; + +/***************************************************************************** + * + * skge_probe - find all SK-98xx adapters + * + * Description: + * This function scans the PCI bus for SK-98xx adapters. Resources for + * each adapter are allocated and the adapter is brought into Init 1 + * state. + * + * Returns: + * 0, if everything is ok + * !=0, on error + */ +__initfunc(int skge_probe (struct device *dev)) +{ +int boards_found = 0; +int version_disp = 0; +SK_AC *pAC; +struct pci_dev *pdev = NULL; +unsigned long base_address; + + if (probed) + return -ENODEV; + probed++; + + /* display driver info */ + if (!version_disp) + { + /* set display flag to TRUE so that */ + /* we only display this string ONCE */ + version_disp = 1; + printk("%s\n", BootString); + } + + if (!pci_present()) /* is PCI support present? */ + return -ENODEV; + + while((pdev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, pdev))) + { + dev = NULL; + + if (pdev->vendor != PCI_VENDOR_ID_SYSKONNECT || + pdev->device != PCI_DEVICE_ID_SYSKONNECT_GE) { + continue; + } + dev = init_etherdev(dev, sizeof(SK_AC)); + + if (dev == NULL){ + printk(KERN_ERR "Unable to allocate etherdev " + "structure!\n"); + break; + } + + if (!dev->priv) + dev->priv = kmalloc(sizeof(SK_AC), GFP_KERNEL); + if (dev->priv == NULL){ + printk(KERN_ERR "Unable to allocate adapter " + "structure!\n"); + break; + } + + + memset(dev->priv, 0, sizeof(SK_AC)); + + pAC = dev->priv; + pAC->PciDev = *pdev; + pAC->PciDevId = pdev->device; + pAC->dev = dev; + sprintf(pAC->Name, "SysKonnect SK-98xx"); + pAC->CheckQueue = SK_FALSE; + + dev->irq = pdev->irq; + + dev->open = &SkGeOpen; + dev->stop = &SkGeClose; + dev->hard_start_xmit = &SkGeXmit; + dev->get_stats = &SkGeStats; + dev->set_multicast_list = &SkGeSetRxMode; + dev->set_mac_address = &SkGeSetMacAddr; + dev->do_ioctl = &SkGeIoctl; + dev->change_mtu = &SkGeChangeMtu; + + /* + * Dummy value. + */ + dev->base_addr = 42; + + pci_set_master(pdev); + + base_address = pdev->base_address[0]; + +#ifdef SK_BIG_ENDIAN + /* + * On big endian machines, we use the adapter's aibility of + * reading the descriptors as big endian. + */ + { + SK_U32 our2; + SkPciReadCfgDWord(pAC, PCI_OUR_REG_2, &our2); + our2 |= PCI_REV_DESC; + SkPciWriteCfgDWord(pAC, PCI_OUR_REG_2, our2); + } +#endif /* BIG ENDIAN */ + + /* + * Remap the regs into kernel space. + */ + + + pAC->IoBase = (char*)ioremap(base_address, 0x4000); + if (!pAC->IoBase){ + printk(KERN_ERR "%s: Unable to map I/O register, " + "SK 98xx No. %i will be disabled.\n", + dev->name, boards_found); + break; + } + pAC->Index = boards_found; + + if (SkGeBoardInit(dev, pAC)) { + FreeResources(dev); + continue; + } + + memcpy((caddr_t) &dev->dev_addr, + (caddr_t) &pAC->Addr.CurrentMacAddress, 6); + + boards_found++; + + /* + * This is bollocks, but we need to tell the net-init + * code that it shall go for the next device. + */ +#ifndef MODULE + dev->base_addr = 0; +#endif + } + + /* + * If we're at this point we're going through skge_probe() for + * the first time. Return success (0) if we've initialized 1 + * or more boards. Otherwise, return failure (-ENODEV). + */ + +#ifdef MODULE + return boards_found; +#else + if (boards_found > 0) + return 0; + else + return -ENODEV; +#endif +} /* skge_probe */ + + +/***************************************************************************** + * + * FreeResources - release resources allocated for adapter + * + * Description: + * This function releases the IRQ, unmaps the IO and + * frees the desriptor ring. + * + * Returns: N/A + * + */ +static void FreeResources(struct device *dev) +{ +SK_U32 AllocFlag; +SK_AC *pAC; + + if (dev->priv) { + pAC = (SK_AC*) dev->priv; + AllocFlag = pAC->AllocFlag; + if (AllocFlag & SK_ALLOC_IRQ) { + free_irq(dev->irq, dev); + } + if (pAC->IoBase) { + iounmap(pAC->IoBase); + } + if (pAC->pDescrMem) { + BoardFreeMem(pAC); + } + } + +} /* FreeResources */ + + +#ifdef MODULE + +MODULE_AUTHOR("Christoph Goos "); +MODULE_DESCRIPTION("SysKonnect SK-NET Gigabit Ethernet SK-98xx driver"); +MODULE_PARM(AutoNeg_A, "1-" __MODULE_STRING(SK_MAX_CARD_PARAM) "s"); +MODULE_PARM(AutoNeg_B, "1-" __MODULE_STRING(SK_MAX_CARD_PARAM) "s"); +MODULE_PARM(DupCap_A, "1-" __MODULE_STRING(SK_MAX_CARD_PARAM) "s"); +MODULE_PARM(DupCap_B, "1-" __MODULE_STRING(SK_MAX_CARD_PARAM) "s"); +MODULE_PARM(FlowCtrl_A, "1-" __MODULE_STRING(SK_MAX_CARD_PARAM) "s"); +MODULE_PARM(FlowCtrl_B, "1-" __MODULE_STRING(SK_MAX_CARD_PARAM) "s"); +MODULE_PARM(Role_A, "1-" __MODULE_STRING(SK_MAX_CARD_PARAM) "s"); +MODULE_PARM(Role_B, "1-" __MODULE_STRING(SK_MAX_CARD_PARAM) "s"); +MODULE_PARM(PrefPort, "1-" __MODULE_STRING(SK_MAX_CARD_PARAM) "s"); +MODULE_PARM(RlmtMode, "1-" __MODULE_STRING(SK_MAX_CARD_PARAM) "s"); +/* not used, just there because every driver should have them: */ +MODULE_PARM(options, "1-" __MODULE_STRING(SK_MAX_CARD_PARAM) "i"); +MODULE_PARM(debug, "i"); + +#endif // MODULE + + +#ifdef AUTO_NEG_A +static char *AutoNeg_A[SK_MAX_CARD_PARAM] = AUTO_NEG_A; +#else +static char *AutoNeg_A[SK_MAX_CARD_PARAM] = {"", }; +#endif + +#ifdef DUP_CAP_A +static char *DupCap_A[SK_MAX_CARD_PARAM] = DUP_CAP_A; +#else +static char *DupCap_A[SK_MAX_CARD_PARAM] = {"", }; +#endif + +#ifdef FLOW_CTRL_A +static char *FlowCtrl_A[SK_MAX_CARD_PARAM] = FLOW_CTRL_A; +#else +static char *FlowCtrl_A[SK_MAX_CARD_PARAM] = {"", }; +#endif + +#ifdef ROLE_A +static char *Role_A[SK_MAX_CARD_PARAM] = ROLE_A; +#else +static char *Role_A[SK_MAX_CARD_PARAM] = {"", }; +#endif + +#ifdef AUTO_NEG_B +static char *AutoNeg_B[SK_MAX_CARD_PARAM] = AUTO_NEG_B; +#else +static char *AutoNeg_B[SK_MAX_CARD_PARAM] = {"", }; +#endif + +#ifdef DUP_CAP_B +static char *DupCap_B[SK_MAX_CARD_PARAM] = DUP_CAP_B; +#else +static char *DupCap_B[SK_MAX_CARD_PARAM] = {"", }; +#endif + +#ifdef FLOW_CTRL_B +static char *FlowCtrl_B[SK_MAX_CARD_PARAM] = FLOW_CTRL_B; +#else +static char *FlowCtrl_B[SK_MAX_CARD_PARAM] = {"", }; +#endif + +#ifdef ROLE_B +static char *Role_B[SK_MAX_CARD_PARAM] = ROLE_B; +#else +static char *Role_B[SK_MAX_CARD_PARAM] = {"", }; +#endif + +#ifdef PREF_PORT +static char *PrefPort[SK_MAX_CARD_PARAM] = PREF_PORT; +#else +static char *PrefPort[SK_MAX_CARD_PARAM] = {"", }; +#endif + +#ifdef RLMT_MODE +static char *RlmtMode[SK_MAX_CARD_PARAM] = RLMT_MODE; +#else +static char *RlmtMode[SK_MAX_CARD_PARAM] = {"", }; +#endif + + +#ifdef MODULE + +static int debug = 0; /* not used */ +static int options[SK_MAX_CARD_PARAM] = {0, }; /* not used */ + + +/***************************************************************************** + * + * init_module - module initialization function + * + * Description: + * Very simple, only call skge_probe and return approriate result. + * + * Returns: + * 0, if everything is ok + * !=0, on error + */ +int init_module(void) +{ +int cards; + + root_dev = NULL; + + /* just to avoid warnings ... */ + debug = 0; + options[0] = 0; + + cards = skge_probe(NULL); + if (cards == 0) { + printk("No adapter found\n"); + } + return cards ? 0 : -ENODEV; +} /* init_module */ + + +/***************************************************************************** + * + * cleanup_module - module unload function + * + * Description: + * Disable adapter if it is still running, free resources, + * free device struct. + * + * Returns: N/A + */ +void cleanup_module(void) +{ +SK_AC *pAC; +struct device *next; +unsigned long Flags; +SK_EVPARA EvPara; + + while (root_dev) { + pAC = (SK_AC*)root_dev->priv; + next = pAC->Next; + + root_dev->tbusy = 1; + SkGeYellowLED(pAC, pAC->IoBase, 0); + + if(pAC->BoardLevel == 2) { + /* board is still alive */ + spin_lock_irqsave(&pAC->SlowPathLock, Flags); + SkEventQueue(pAC, SKGE_RLMT, SK_RLMT_STOP, EvPara); + SkEventDispatcher(pAC, pAC->IoBase); + /* disable interrupts */ + SK_OUT32(pAC->IoBase, B0_IMSK, 0); + SkGeDeInit(pAC, pAC->IoBase); + spin_unlock_irqrestore(&pAC->SlowPathLock, Flags); + pAC->BoardLevel = 0; + /* We do NOT check here, if IRQ was pending, of course*/ + } + + if(pAC->BoardLevel == 1) { + /* board is still alive */ + SkGeDeInit(pAC, pAC->IoBase); + pAC->BoardLevel = 0; + } + + FreeResources(root_dev); + + root_dev->get_stats = NULL; + /* + * otherwise unregister_netdev calls get_stats with + * invalid IO ... :-( + */ + unregister_netdev(root_dev); + kfree(root_dev); + + root_dev = next; + } +} +#endif /* cleanup_module */ + + +/***************************************************************************** + * + * SkGeBoardInit - do level 0 and 1 initialization + * + * Description: + * This function prepares the board hardware for running. The desriptor + * ring is set up, the IRQ is allocated and the configuration settings + * are examined. + * + * Returns: + * 0, if everything is ok + * !=0, on error + */ +__initfunc(static int SkGeBoardInit(struct device *dev, SK_AC *pAC)) +{ +short i; +unsigned long Flags; +char *DescrString = "sk98lin: Driver for Linux"; /* this is given to PNMI */ +char *VerStr = VER_STRING; +int Ret; /* return code of request_irq */ + + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY, + ("IoBase: %08lX\n", (unsigned long)pAC->IoBase)); + for (i=0; iTxPort[i][0].HwAddr = pAC->IoBase + TxQueueAddr[i][0]; + pAC->TxPort[i][0].PortIndex = i; + pAC->RxPort[i].HwAddr = pAC->IoBase + RxQueueAddr[i]; + pAC->RxPort[i].PortIndex = i; + } + + /* Initialize the mutexes */ + + for (i=0; iTxPort[i][0].TxDesRingLock); + spin_lock_init(&pAC->RxPort[i].RxDesRingLock); + } + spin_lock_init(&pAC->SlowPathLock); + + /* level 0 init common modules here */ + + spin_lock_irqsave(&pAC->SlowPathLock, Flags); + /* Does a RESET on board ...*/ + if (SkGeInit(pAC, pAC->IoBase, 0) != 0) { + printk("HWInit (0) failed.\n"); + spin_unlock_irqrestore(&pAC->SlowPathLock, Flags); + return(-EAGAIN); + } + SkI2cInit( pAC, pAC->IoBase, 0); + SkEventInit(pAC, pAC->IoBase, 0); + SkPnmiInit( pAC, pAC->IoBase, 0); + SkAddrInit( pAC, pAC->IoBase, 0); + SkRlmtInit( pAC, pAC->IoBase, 0); + SkTimerInit(pAC, pAC->IoBase, 0); + + pAC->BoardLevel = 0; + pAC->RxBufSize = ETH_BUF_SIZE; + + SK_PNMI_SET_DRIVER_DESCR(pAC, DescrString); + SK_PNMI_SET_DRIVER_VER(pAC, VerStr); + + spin_unlock_irqrestore(&pAC->SlowPathLock, Flags); + + GetConfiguration(pAC); + + /* level 1 init common modules here (HW init) */ + spin_lock_irqsave(&pAC->SlowPathLock, Flags); + if (SkGeInit(pAC, pAC->IoBase, 1) != 0) { + printk("HWInit (1) failed.\n"); + spin_unlock_irqrestore(&pAC->SlowPathLock, Flags); + return(-EAGAIN); + } + SkI2cInit( pAC, pAC->IoBase, 1); + SkEventInit(pAC, pAC->IoBase, 1); + SkPnmiInit( pAC, pAC->IoBase, 1); + SkAddrInit( pAC, pAC->IoBase, 1); + SkRlmtInit( pAC, pAC->IoBase, 1); + SkTimerInit(pAC, pAC->IoBase, 1); + + pAC->BoardLevel = 1; + spin_unlock_irqrestore(&pAC->SlowPathLock, Flags); + + if (pAC->GIni.GIMacsFound == 2) { + Ret = request_irq(dev->irq, SkGeIsr, SA_SHIRQ, pAC->Name, dev); + } else if (pAC->GIni.GIMacsFound == 1) { + Ret = request_irq(dev->irq, SkGeIsrOnePort, SA_SHIRQ, + pAC->Name, dev); + } else { + printk(KERN_WARNING "%s: illegal number of ports: %d\n", + dev->name, pAC->GIni.GIMacsFound); + return -EAGAIN; + } + if (Ret) { + printk(KERN_WARNING "%s: Requested IRQ %d is busy\n", + dev->name, dev->irq); + return -EAGAIN; + } + pAC->AllocFlag |= SK_ALLOC_IRQ; + + /* Alloc memory for this board (Mem for RxD/TxD) : */ + if(!BoardAllocMem(pAC)) { + printk("No memory for descriptor rings\n"); + return(-EAGAIN); + } + + SkCsSetReceiveFlags(pAC, + SKCS_PROTO_IP | SKCS_PROTO_TCP | SKCS_PROTO_UDP, + &pAC->CsOfs1, &pAC->CsOfs2); + pAC->CsOfs = (pAC->CsOfs2 << 16) | pAC->CsOfs1; + + BoardInitMem(pAC); + + SetQueueSizes(pAC); + + /* Print adapter specific string from vpd */ + ProductStr(pAC); + printk("%s: %s\n", dev->name, pAC->DeviceStr); + + SkGeYellowLED(pAC, pAC->IoBase, 1); + + /* + * Register the device here + */ + pAC->Next = root_dev; + root_dev = dev; + + return (0); +} /* SkGeBoardInit */ + + +/***************************************************************************** + * + * BoardAllocMem - allocate the memory for the descriptor rings + * + * Description: + * This function allocates the memory for all descriptor rings. + * Each ring is aligned for the desriptor alignment and no ring + * has a 4 GByte boundary in it (because the upper 32 bit must + * be constant for all descriptiors in one rings). + * + * Returns: + * SK_TRUE, if all memory could be allocated + * SK_FALSE, if not + */ +static SK_BOOL BoardAllocMem( +SK_AC *pAC) +{ +caddr_t pDescrMem; /* pointer to descriptor memory area */ +size_t AllocLength; /* length of complete descriptor area */ +int i; /* loop counter */ +unsigned long BusAddr; + + + /* rings plus one for alignment (do not cross 4 GB boundary) */ + /* RX_RING_SIZE is assumed bigger than TX_RING_SIZE */ +#if (BITS_PER_LONG == 32) + AllocLength = (RX_RING_SIZE + TX_RING_SIZE) * pAC->GIni.GIMacsFound + 8; +#else + AllocLength = (RX_RING_SIZE + TX_RING_SIZE) * pAC->GIni.GIMacsFound + + RX_RING_SIZE + 8; +#endif + pDescrMem = kmalloc(AllocLength, GFP_KERNEL); + if (pDescrMem == NULL) { + return (SK_FALSE); + } + pAC->pDescrMem = pDescrMem; + memset(pDescrMem, 0, AllocLength); + /* Descriptors need 8 byte alignment */ + BusAddr = virt_to_bus(pDescrMem); + if (BusAddr & (DESCR_ALIGN-1)) { + pDescrMem += DESCR_ALIGN - (BusAddr & (DESCR_ALIGN-1)); + } + for (i=0; iGIni.GIMacsFound; i++) { + if ((virt_to_bus(pDescrMem) & ~0xFFFFFFFFULL) != + (virt_to_bus(pDescrMem+TX_RING_SIZE) & ~0xFFFFFFFFULL)) { + pDescrMem += TX_RING_SIZE; + } + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS, + ("TX%d/A: pDescrMem: %lX, PhysDescrMem: %lX\n", + i, (unsigned long) pDescrMem, + (unsigned long)virt_to_bus(pDescrMem))); + pAC->TxPort[i][0].pTxDescrRing = pDescrMem; + pAC->TxPort[i][0].VTxDescrRing = virt_to_bus(pDescrMem); + pDescrMem += TX_RING_SIZE; + + if ((virt_to_bus(pDescrMem) & ~0xFFFFFFFFULL) != + (virt_to_bus(pDescrMem+RX_RING_SIZE) & ~0xFFFFFFFFULL)) { + pDescrMem += RX_RING_SIZE; + } + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS, + ("RX%d: pDescrMem: %lX, PhysDescrMem: %lX\n", + i, (unsigned long) pDescrMem, + (unsigned long)(virt_to_bus(pDescrMem)))); + pAC->RxPort[i].pRxDescrRing = pDescrMem; + pAC->RxPort[i].VRxDescrRing = virt_to_bus(pDescrMem); + pDescrMem += RX_RING_SIZE; + } /* for */ + + return (SK_TRUE); +} /* BoardAllocMem */ + + +/**************************************************************************** + * + * BoardFreeMem - reverse of BoardAllocMem + * + * Description: + * Free all memory allocated in BoardAllocMem: adapter context, + * descriptor rings, locks. + * + * Returns: N/A + */ +static void BoardFreeMem( +SK_AC *pAC) +{ + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY, + ("BoardFreeMem\n")); + kfree(pAC->pDescrMem); +} /* BoardFreeMem */ + + +/***************************************************************************** + * + * BoardInitMem - initiate the descriptor rings + * + * Description: + * This function sets the descriptor rings up in memory. + * The adapter is initialized with the descriptor start addresses. + * + * Returns: N/A + */ +static void BoardInitMem( +SK_AC *pAC) /* pointer to adapter context */ +{ +int i; /* loop counter */ +int RxDescrSize; /* the size of a rx descriptor rounded up to alignment*/ +int TxDescrSize; /* the size of a tx descriptor rounded up to alignment*/ + + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY, + ("BoardInitMem\n")); + + RxDescrSize = (((sizeof(RXD) - 1) / DESCR_ALIGN) + 1) * DESCR_ALIGN; + pAC->RxDescrPerRing = RX_RING_SIZE / RxDescrSize; + TxDescrSize = (((sizeof(TXD) - 1) / DESCR_ALIGN) + 1) * DESCR_ALIGN; + pAC->TxDescrPerRing = TX_RING_SIZE / RxDescrSize; + + for (i=0; iGIni.GIMacsFound; i++) { + SetupRing( + pAC, + pAC->TxPort[i][0].pTxDescrRing, + pAC->TxPort[i][0].VTxDescrRing, + (RXD**)&pAC->TxPort[i][0].pTxdRingHead, + (RXD**)&pAC->TxPort[i][0].pTxdRingTail, + (RXD**)&pAC->TxPort[i][0].pTxdRingPrev, + &pAC->TxPort[i][0].TxdRingFree, + SK_TRUE); + SetupRing( + pAC, + pAC->RxPort[i].pRxDescrRing, + pAC->RxPort[i].VRxDescrRing, + &pAC->RxPort[i].pRxdRingHead, + &pAC->RxPort[i].pRxdRingTail, + &pAC->RxPort[i].pRxdRingPrev, + &pAC->RxPort[i].RxdRingFree, + SK_FALSE); + } +} /* BoardInitMem */ + + +/***************************************************************************** + * + * SetupRing - create one descriptor ring + * + * Description: + * This function creates one descriptor ring in the given memory area. + * The head, tail and number of free descriptors in the ring are set. + * + * Returns: + * none + */ +static void SetupRing( +SK_AC *pAC, +void *pMemArea, /* a pointer to the memory area for the ring */ +uintptr_t VMemArea, /* the virtual bus address of the memory area */ +RXD **ppRingHead, /* address where the head should be written */ +RXD **ppRingTail, /* address where the tail should be written */ +RXD **ppRingPrev, /* address where the tail should be written */ +int *pRingFree, /* address where the # of free descr. goes */ +SK_BOOL IsTx) /* flag: is this a tx ring */ +{ +int i; /* loop counter */ +int DescrSize; /* the size of a descriptor rounded up to alignment*/ +int DescrNum; /* number of descriptors per ring */ +RXD *pDescr; /* pointer to a descriptor (receive or transmit) */ +RXD *pNextDescr; /* pointer to the next descriptor */ +RXD *pPrevDescr; /* pointer to the previous descriptor */ +uintptr_t VNextDescr; /* the virtual bus address of the next descriptor */ + + if (IsTx == SK_TRUE) { + DescrSize = (((sizeof(TXD) - 1) / DESCR_ALIGN) + 1) * + DESCR_ALIGN; + DescrNum = TX_RING_SIZE / DescrSize; + } + else { + DescrSize = (((sizeof(RXD) - 1) / DESCR_ALIGN) + 1) * + DESCR_ALIGN; + DescrNum = RX_RING_SIZE / DescrSize; + } + + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS, + ("Descriptor size: %d Descriptor Number: %d\n", + DescrSize,DescrNum)); + + pDescr = (RXD*) pMemArea; + pPrevDescr = NULL; + pNextDescr = (RXD*) (((char*)pDescr) + DescrSize); + VNextDescr = VMemArea + DescrSize; + for(i=0; iVNextRxd = VNextDescr & 0xffffffffULL; + pDescr->pNextRxd = pNextDescr; + pDescr->TcpSumStarts = pAC->CsOfs; + /* advance on step */ + pPrevDescr = pDescr; + pDescr = pNextDescr; + pNextDescr = (RXD*) (((char*)pDescr) + DescrSize); + VNextDescr += DescrSize; + } + pPrevDescr->pNextRxd = (RXD*) pMemArea; + pPrevDescr->VNextRxd = VMemArea; + pDescr = (RXD*) pMemArea; + *ppRingHead = (RXD*) pMemArea; + *ppRingTail = *ppRingHead; + *ppRingPrev = pPrevDescr; + *pRingFree = DescrNum; +} /* SetupRing */ + + +/***************************************************************************** + * + * PortReInitBmu - re-initiate the descriptor rings for one port + * + * Description: + * This function reinitializes the descriptor rings of one port + * in memory. The port must be stopped before. + * The HW is initialized with the descriptor start addresses. + * + * Returns: + * none + */ +static void PortReInitBmu( +SK_AC *pAC, /* pointer to adapter context */ +int PortIndex) /* index of the port for which to re-init */ +{ + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY, + ("PortReInitBmu ")); + + /* set address of first descriptor of ring in BMU */ + SK_OUT32(pAC->IoBase, TxQueueAddr[PortIndex][TX_PRIO_LOW]+ + TX_Q_CUR_DESCR_LOW, + (uint32_t)(((caddr_t) + (pAC->TxPort[PortIndex][TX_PRIO_LOW].pTxdRingHead) - + pAC->TxPort[PortIndex][TX_PRIO_LOW].pTxDescrRing + + pAC->TxPort[PortIndex][TX_PRIO_LOW].VTxDescrRing) & + 0xFFFFFFFF)); + SK_OUT32(pAC->IoBase, TxQueueAddr[PortIndex][TX_PRIO_LOW]+ + TX_Q_DESCR_HIGH, + (uint32_t)(((caddr_t) + (pAC->TxPort[PortIndex][TX_PRIO_LOW].pTxdRingHead) - + pAC->TxPort[PortIndex][TX_PRIO_LOW].pTxDescrRing + + pAC->TxPort[PortIndex][TX_PRIO_LOW].VTxDescrRing) >> 32)); + SK_OUT32(pAC->IoBase, RxQueueAddr[PortIndex]+RX_Q_CUR_DESCR_LOW, + (uint32_t)(((caddr_t)(pAC->RxPort[PortIndex].pRxdRingHead) - + pAC->RxPort[PortIndex].pRxDescrRing + + pAC->RxPort[PortIndex].VRxDescrRing) & 0xFFFFFFFF)); + SK_OUT32(pAC->IoBase, RxQueueAddr[PortIndex]+RX_Q_DESCR_HIGH, + (uint32_t)(((caddr_t)(pAC->RxPort[PortIndex].pRxdRingHead) - + pAC->RxPort[PortIndex].pRxDescrRing + + pAC->RxPort[PortIndex].VRxDescrRing) >> 32)); +} /* PortReInitBmu */ + + +/**************************************************************************** + * + * SkGeIsr - handle adapter interrupts + * + * Description: + * The interrupt routine is called when the network adapter + * generates an interrupt. It may also be called if another device + * shares this interrupt vector with the driver. + * + * Returns: N/A + * + */ +static void SkGeIsr(int irq, void *dev_id, struct pt_regs *ptregs) +{ +struct device *dev = (struct device *)dev_id; +SK_AC *pAC; +SK_U32 IntSrc; /* interrupts source register contents */ + + pAC = (SK_AC*) dev->priv; + + /* + * Check and process if its our interrupt + */ + SK_IN32(pAC->IoBase, B0_SP_ISRC, &IntSrc); + if (IntSrc == 0) { + return; + } + + while (((IntSrc & IRQ_MASK) & ~SPECIAL_IRQS) != 0) { +#if 0 /* software irq currently not used */ + if (IntSrc & IRQ_SW) { + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, + SK_DBGCAT_DRV_INT_SRC, + ("Software IRQ\n")); + } +#endif + if (IntSrc & IRQ_EOF_RX1) { + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, + SK_DBGCAT_DRV_INT_SRC, + ("EOF RX1 IRQ\n")); + ReceiveIrq(pAC, &pAC->RxPort[0]); + SK_PNMI_CNT_RX_INTR(pAC); + } + if (IntSrc & IRQ_EOF_RX2) { + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, + SK_DBGCAT_DRV_INT_SRC, + ("EOF RX2 IRQ\n")); + ReceiveIrq(pAC, &pAC->RxPort[1]); + SK_PNMI_CNT_RX_INTR(pAC); + } +#ifdef USE_TX_COMPLETE /* only if tx complete interrupt used */ + if (IntSrc & IRQ_EOF_AS_TX1) { + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, + SK_DBGCAT_DRV_INT_SRC, + ("EOF AS TX1 IRQ\n")); + SK_PNMI_CNT_TX_INTR(pAC); + spin_lock(&pAC->TxPort[0][TX_PRIO_LOW].TxDesRingLock); + FreeTxDescriptors(pAC, &pAC->TxPort[0][TX_PRIO_LOW]); + spin_unlock(&pAC->TxPort[0][TX_PRIO_LOW].TxDesRingLock); + } + if (IntSrc & IRQ_EOF_AS_TX2) { + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, + SK_DBGCAT_DRV_INT_SRC, + ("EOF AS TX2 IRQ\n")); + SK_PNMI_CNT_TX_INTR(pAC); + spin_lock(&pAC->TxPort[1][TX_PRIO_LOW].TxDesRingLock); + FreeTxDescriptors(pAC, &pAC->TxPort[1][TX_PRIO_LOW]); + spin_unlock(&pAC->TxPort[1][TX_PRIO_LOW].TxDesRingLock); + } +#if 0 /* only if sync. queues used */ + if (IntSrc & IRQ_EOF_SY_TX1) { + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, + SK_DBGCAT_DRV_INT_SRC, + ("EOF SY TX1 IRQ\n")); + SK_PNMI_CNT_TX_INTR(pAC); + spin_lock(&pAC->TxPort[0][TX_PRIO_HIGH].TxDesRingLock); + FreeTxDescriptors(pAC, 0, TX_PRIO_HIGH); + spin_unlock(&pAC->TxPort[0][TX_PRIO_HIGH].TxDesRingLock); + ClearTxIrq(pAC, 0, TX_PRIO_HIGH); + } + if (IntSrc & IRQ_EOF_SY_TX2) { + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, + SK_DBGCAT_DRV_INT_SRC, + ("EOF SY TX2 IRQ\n")); + SK_PNMI_CNT_TX_INTR(pAC); + spin_lock(&pAC->TxPort[1][TX_PRIO_HIGH].TxDesRingLock); + FreeTxDescriptors(pAC, 1, TX_PRIO_HIGH); + spin_unlock(&pAC->TxPort[1][TX_PRIO_HIGH].TxDesRingLock); + ClearTxIrq(pAC, 1, TX_PRIO_HIGH); + } +#endif /* 0 */ +#endif /* USE_TX_COMPLETE */ + + /* do all IO at once */ + if (IntSrc & IRQ_EOF_RX1) + ClearAndStartRx(pAC, 0); + if (IntSrc & IRQ_EOF_RX2) + ClearAndStartRx(pAC, 1); +#ifdef USE_TX_COMPLETE /* only if tx complete interrupt used */ + if (IntSrc & IRQ_EOF_AS_TX1) + ClearTxIrq(pAC, 0, TX_PRIO_LOW); + if (IntSrc & IRQ_EOF_AS_TX2) + ClearTxIrq(pAC, 1, TX_PRIO_LOW); +#endif + SK_IN32(pAC->IoBase, B0_ISRC, &IntSrc); + } /* while (IntSrc & IRQ_MASK != 0) */ + + if ((IntSrc & SPECIAL_IRQS) || pAC->CheckQueue) { + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC, + ("SPECIAL IRQ\n")); + pAC->CheckQueue = SK_FALSE; + spin_lock(&pAC->SlowPathLock); + if (IntSrc & SPECIAL_IRQS) + SkGeSirqIsr(pAC, pAC->IoBase, IntSrc); + SkEventDispatcher(pAC, pAC->IoBase); + spin_unlock(&pAC->SlowPathLock); + } + /* + * do it all again is case we cleared an interrupt that + * came in after handling the ring (OUTs may be delayed + * in hardware buffers, but are through after IN) + */ + ReceiveIrq(pAC, &pAC->RxPort[pAC->ActivePort]); +// ReceiveIrq(pAC, &pAC->RxPort[1]); + +#if 0 +// #ifdef USE_TX_COMPLETE /* only if tx complete interrupt used */ + spin_lock(&pAC->TxPort[0][TX_PRIO_LOW].TxDesRingLock); + FreeTxDescriptors(pAC, &pAC->TxPort[0][TX_PRIO_LOW]); + spin_unlock(&pAC->TxPort[0][TX_PRIO_LOW].TxDesRingLock); + + spin_lock(&pAC->TxPort[1][TX_PRIO_LOW].TxDesRingLock); + FreeTxDescriptors(pAC, &pAC->TxPort[1][TX_PRIO_LOW]); + spin_unlock(&pAC->TxPort[1][TX_PRIO_LOW].TxDesRingLock); + +#if 0 /* only if sync. queues used */ + spin_lock(&pAC->TxPort[0][TX_PRIO_HIGH].TxDesRingLock); + FreeTxDescriptors(pAC, 0, TX_PRIO_HIGH); + spin_unlock(&pAC->TxPort[0][TX_PRIO_HIGH].TxDesRingLock); + + spin_lock(&pAC->TxPort[1][TX_PRIO_HIGH].TxDesRingLock); + FreeTxDescriptors(pAC, 1, TX_PRIO_HIGH); + spin_unlock(&pAC->TxPort[1][TX_PRIO_HIGH].TxDesRingLock); +#endif /* 0 */ +#endif /* USE_TX_COMPLETE */ + + /* IRQ is processed - Enable IRQs again*/ + SK_OUT32(pAC->IoBase, B0_IMSK, IRQ_MASK); + + return; +} /* SkGeIsr */ + + +/**************************************************************************** + * + * SkGeIsrOnePort - handle adapter interrupts for single port adapter + * + * Description: + * The interrupt routine is called when the network adapter + * generates an interrupt. It may also be called if another device + * shares this interrupt vector with the driver. + * This is the same as above, but handles only one port. + * + * Returns: N/A + * + */ +static void SkGeIsrOnePort(int irq, void *dev_id, struct pt_regs *ptregs) +{ +struct device *dev = (struct device *)dev_id; +SK_AC *pAC; +SK_U32 IntSrc; /* interrupts source register contents */ + + pAC = (SK_AC*) dev->priv; + + /* + * Check and process if its our interrupt + */ + SK_IN32(pAC->IoBase, B0_SP_ISRC, &IntSrc); + if (IntSrc == 0) { + return; + } + + while (((IntSrc & IRQ_MASK) & ~SPECIAL_IRQS) != 0) { +#if 0 /* software irq currently not used */ + if (IntSrc & IRQ_SW) { + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, + SK_DBGCAT_DRV_INT_SRC, + ("Software IRQ\n")); + } +#endif + if (IntSrc & IRQ_EOF_RX1) { + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, + SK_DBGCAT_DRV_INT_SRC, + ("EOF RX1 IRQ\n")); + ReceiveIrq(pAC, &pAC->RxPort[0]); + SK_PNMI_CNT_RX_INTR(pAC); + } +#ifdef USE_TX_COMPLETE /* only if tx complete interrupt used */ + if (IntSrc & IRQ_EOF_AS_TX1) { + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, + SK_DBGCAT_DRV_INT_SRC, + ("EOF AS TX1 IRQ\n")); + SK_PNMI_CNT_TX_INTR(pAC); + spin_lock(&pAC->TxPort[0][TX_PRIO_LOW].TxDesRingLock); + FreeTxDescriptors(pAC, &pAC->TxPort[0][TX_PRIO_LOW]); + spin_unlock(&pAC->TxPort[0][TX_PRIO_LOW].TxDesRingLock); + } +#if 0 /* only if sync. queues used */ + if (IntSrc & IRQ_EOF_SY_TX1) { + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, + SK_DBGCAT_DRV_INT_SRC, + ("EOF SY TX1 IRQ\n")); + SK_PNMI_CNT_TX_INTR(pAC); + spin_lock(&pAC->TxPort[0][TX_PRIO_HIGH].TxDesRingLock); + FreeTxDescriptors(pAC, 0, TX_PRIO_HIGH); + spin_unlock(&pAC->TxPort[0][TX_PRIO_HIGH].TxDesRingLock); + ClearTxIrq(pAC, 0, TX_PRIO_HIGH); + } +#endif /* 0 */ +#endif /* USE_TX_COMPLETE */ + + /* do all IO at once */ + if (IntSrc & IRQ_EOF_RX1) + ClearAndStartRx(pAC, 0); +#ifdef USE_TX_COMPLETE /* only if tx complete interrupt used */ + if (IntSrc & IRQ_EOF_AS_TX1) + ClearTxIrq(pAC, 0, TX_PRIO_LOW); +#endif + SK_IN32(pAC->IoBase, B0_ISRC, &IntSrc); + } /* while (IntSrc & IRQ_MASK != 0) */ + + if ((IntSrc & SPECIAL_IRQS) || pAC->CheckQueue) { + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC, + ("SPECIAL IRQ\n")); + pAC->CheckQueue = SK_FALSE; + spin_lock(&pAC->SlowPathLock); + if (IntSrc & SPECIAL_IRQS) + SkGeSirqIsr(pAC, pAC->IoBase, IntSrc); + SkEventDispatcher(pAC, pAC->IoBase); + spin_unlock(&pAC->SlowPathLock); + } + /* + * do it all again is case we cleared an interrupt that + * came in after handling the ring (OUTs may be delayed + * in hardware buffers, but are through after IN) + */ + ReceiveIrq(pAC, &pAC->RxPort[0]); + +#if 0 +// #ifdef USE_TX_COMPLETE /* only if tx complete interrupt used */ + spin_lock(&pAC->TxPort[0][TX_PRIO_LOW].TxDesRingLock); + FreeTxDescriptors(pAC, &pAC->TxPort[0][TX_PRIO_LOW]); + spin_unlock(&pAC->TxPort[0][TX_PRIO_LOW].TxDesRingLock); + +#if 0 /* only if sync. queues used */ + spin_lock(&pAC->TxPort[0][TX_PRIO_HIGH].TxDesRingLock); + FreeTxDescriptors(pAC, 0, TX_PRIO_HIGH); + spin_unlock(&pAC->TxPort[0][TX_PRIO_HIGH].TxDesRingLock); + +#endif /* 0 */ +#endif /* USE_TX_COMPLETE */ + + /* IRQ is processed - Enable IRQs again*/ + SK_OUT32(pAC->IoBase, B0_IMSK, IRQ_MASK); + + return; +} /* SkGeIsrOnePort */ + + +/**************************************************************************** + * + * SkGeOpen - handle start of initialized adapter + * + * Description: + * This function starts the initialized adapter. + * The board level variable is set and the adapter is + * brought to full functionality. + * The device flags are set for operation. + * Do all necessary level 2 initialization, enable interrupts and + * give start command to RLMT. + * + * Returns: + * 0 on success + * != 0 on error + */ +static int SkGeOpen( +struct device *dev) +{ +SK_AC *pAC; /* pointer to adapter context struct */ +unsigned int Flags; /* for spin lock */ +int i; +SK_EVPARA EvPara; /* an event parameter union */ + + pAC = (SK_AC*) dev->priv; + + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY, + ("SkGeOpen: pAC=0x%lX:\n", (unsigned long)pAC)); + + if (pAC->BoardLevel == 0) { + /* level 1 init common modules here */ + if (SkGeInit(pAC, pAC->IoBase, 1) != 0) { + printk("%s: HWInit(1) failed\n", pAC->dev->name); + return (-1); + } + SkI2cInit (pAC, pAC->IoBase, 1); + SkEventInit (pAC, pAC->IoBase, 1); + SkPnmiInit (pAC, pAC->IoBase, 1); + SkAddrInit (pAC, pAC->IoBase, 1); + SkRlmtInit (pAC, pAC->IoBase, 1); + SkTimerInit (pAC, pAC->IoBase, 1); + pAC->BoardLevel = 1; + } + + /* level 2 init modules here */ + SkGeInit (pAC, pAC->IoBase, 2); + SkI2cInit (pAC, pAC->IoBase, 2); + SkEventInit (pAC, pAC->IoBase, 2); + SkPnmiInit (pAC, pAC->IoBase, 2); + SkAddrInit (pAC, pAC->IoBase, 2); + SkRlmtInit (pAC, pAC->IoBase, 2); + SkTimerInit (pAC, pAC->IoBase, 2); + pAC->BoardLevel = 2; + + for (i=0; iGIni.GIMacsFound; i++) { + // Enable transmit descriptor polling. + SkGePollTxD(pAC, pAC->IoBase, i, SK_TRUE); + FillRxRing(pAC, &pAC->RxPort[i]); + } + SkGeYellowLED(pAC, pAC->IoBase, 1); + +#ifdef USE_INT_MOD +// moderate only TX complete interrupts (these are not time critical) +#define IRQ_MOD_MASK (IRQ_EOF_AS_TX1 | IRQ_EOF_AS_TX2) + { + unsigned long ModBase; + ModBase = 53125000 / INTS_PER_SEC; + SK_OUT32(pAC->IoBase, B2_IRQM_INI, ModBase); + SK_OUT32(pAC->IoBase, B2_IRQM_MSK, IRQ_MOD_MASK); + SK_OUT32(pAC->IoBase, B2_IRQM_CTRL, TIM_START); + } +#endif + + /* enable Interrupts */ + SK_OUT32(pAC->IoBase, B0_IMSK, IRQ_MASK); + SK_OUT32(pAC->IoBase, B0_HWE_IMSK, IRQ_HWE_MASK); + + spin_lock_irqsave(&pAC->SlowPathLock, Flags); + SkEventQueue(pAC, SKGE_RLMT, SK_RLMT_START, EvPara); + if (pAC->RlmtMode != 0) { + EvPara.Para32[0] = pAC->RlmtMode; + SkEventQueue(pAC, SKGE_RLMT, SK_RLMT_MODE_CHANGE, + EvPara); + } + SkEventDispatcher(pAC, pAC->IoBase); + spin_unlock_irqrestore(&pAC->SlowPathLock, Flags); + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + MOD_INC_USE_COUNT; + + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY, + ("SkGeOpen suceeded\n")); + + return (0); +} /* SkGeOpen */ + + +/**************************************************************************** + * + * SkGeClose - Stop initialized adapter + * + * Description: + * Close initialized adapter. + * + * Returns: + * 0 - on success + * error code - on error + */ +static int SkGeClose( +struct device *dev) +{ +SK_AC *pAC; +unsigned int Flags; /* for spin lock */ +int i; +SK_EVPARA EvPara; + + dev->start = 0; + set_bit(0, (void*)&dev->tbusy); + + pAC = (SK_AC*) dev->priv; + + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY, + ("SkGeClose: pAC=0x%lX ", (unsigned long)pAC)); + + /* + * Clear multicast table, promiscuous mode .... + */ + SkAddrMcClear(pAC, pAC->IoBase, pAC->ActivePort, 0); + SkAddrPromiscuousChange(pAC, pAC->IoBase, pAC->ActivePort, + SK_PROM_MODE_NONE); + + + spin_lock_irqsave(&pAC->SlowPathLock, Flags); + /* disable interrupts */ + SK_OUT32(pAC->IoBase, B0_IMSK, 0); + SkEventQueue(pAC, SKGE_RLMT, SK_RLMT_STOP, EvPara); + SkEventDispatcher(pAC, pAC->IoBase); + SK_OUT32(pAC->IoBase, B0_IMSK, 0); + /* stop the hardware */ + SkGeDeInit(pAC, pAC->IoBase); + pAC->BoardLevel = 0; + + spin_unlock_irqrestore(&pAC->SlowPathLock, Flags); + + for (i=0; iGIni.GIMacsFound; i++) { + /* clear all descriptor rings */ + ReceiveIrq(pAC, &pAC->RxPort[i]); + ClearRxRing(pAC, &pAC->RxPort[i]); + ClearTxRing(pAC, &pAC->TxPort[i][TX_PRIO_LOW]); + } + + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY, + ("SkGeClose: done ")); + + MOD_DEC_USE_COUNT; + + return (0); +} /* SkGeClose */ + +/***************************************************************************** + * + * SkGeXmit - Linux frame transmit function + * + * Description: + * The system calls this function to send frames onto the wire. + * It puts the frame in the tx descriptor ring. If the ring is + * full then, the 'tbusy' flag is set. + * + * Returns: + * 0, if everything is ok + * !=0, on error + * WARNING: returning 1 in 'tbusy' case caused system crashes (double + * allocated skb's) !!! + */ +static int SkGeXmit(struct sk_buff *skb, struct device *dev) +{ +SK_AC *pAC; +int Rc; /* return code of XmitFrame */ + + pAC = (SK_AC*) dev->priv; + + Rc = XmitFrame(pAC, &pAC->TxPort[pAC->ActivePort][TX_PRIO_LOW], skb); + + if (Rc == 0) { + /* transmitter out of resources */ + set_bit(0, (void*) &dev->tbusy); + return (0); + } + dev->trans_start = jiffies; + return (0); +} /* SkGeXmit */ + + +/***************************************************************************** + * + * XmitFrame - fill one socket buffer into the transmit ring + * + * Description: + * This function puts a message into the transmit descriptor ring + * if there is a descriptors left. + * Linux skb's consist of only one continuous buffer. + * The first step locks the ring. It is held locked + * all time to avoid problems with SWITCH_../PORT_RESET. + * Then the descriptoris allocated. + * The second part is linking the buffer to the descriptor. + * At the very last, the Control field of the descriptor + * is made valid for the BMU and a start TX command is given + * if necessary. + * + * Returns: + * > 0 - on succes: the number of bytes in the message + * = 0 - on resource shortage: this frame sent or dropped, now + * the ring is full ( -> set tbusy) + * < 0 - on failure: other problems (not used) + */ +static int XmitFrame( +SK_AC *pAC, /* pointer to adapter context */ +TX_PORT *pTxPort, /* pointer to struct of port to send to */ +struct sk_buff *pMessage) /* pointer to send-message */ +{ +TXD *pTxd; /* the rxd to fill */ +unsigned int Flags; +SK_U64 PhysAddr; +int BytesSend; + + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS, + ("X")); + + spin_lock_irqsave(&pTxPort->TxDesRingLock, Flags); + + if (pTxPort->TxdRingFree == 0) { + /* no enough free descriptors in ring at the moment */ + FreeTxDescriptors(pAC, pTxPort); + if (pTxPort->TxdRingFree == 0) { + spin_unlock_irqrestore(&pTxPort->TxDesRingLock, Flags); + SK_PNMI_CNT_NO_TX_BUF(pAC); + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, + SK_DBGCAT_DRV_TX_PROGRESS, + ("XmitFrame failed\n")); + /* this message can not be sent now */ + DEV_KFREE_SKB(pMessage); + return (0); + } + } + /* advance head counter behind descriptor needed for this frame */ + pTxd = pTxPort->pTxdRingHead; + pTxPort->pTxdRingHead = pTxd->pNextTxd; + pTxPort->TxdRingFree--; + /* the needed descriptor is reserved now */ + + /* + * everything allocated ok, so add buffer to descriptor + */ + +#ifdef SK_DUMP_TX + DumpMsg(pMessage, "XmitFrame"); +#endif + + /* set up descriptor and CONTROL dword */ + PhysAddr = virt_to_bus(pMessage->data); + pTxd->VDataLow = (SK_U32) (PhysAddr & 0xffffffff); + pTxd->VDataHigh = (SK_U32) (PhysAddr >> 32); + pTxd->pMBuf = pMessage; + pTxd->TBControl = TX_CTRL_OWN_BMU | TX_CTRL_STF | + TX_CTRL_CHECK_DEFAULT | TX_CTRL_SOFTWARE | +#ifdef USE_TX_COMPLETE + TX_CTRL_EOF | TX_CTRL_EOF_IRQ | pMessage->len; +#else + TX_CTRL_EOF | pMessage->len; +#endif + + if ((pTxPort->pTxdRingPrev->TBControl & TX_CTRL_OWN_BMU) == 0) { + /* previous descriptor already done, so give tx start cmd */ + /* StartTx(pAC, pTxPort->HwAddr); */ + SK_OUT8(pTxPort->HwAddr, TX_Q_CTRL, TX_Q_CTRL_START); + } + pTxPort->pTxdRingPrev = pTxd; + + + BytesSend = pMessage->len; + /* after releasing the lock, the skb may be immidiately freed */ + if (pTxPort->TxdRingFree != 0) { + spin_unlock_irqrestore(&pTxPort->TxDesRingLock, Flags); + return (BytesSend); + } + else { + /* ring full: set tbusy on return */ + spin_unlock_irqrestore(&pTxPort->TxDesRingLock, Flags); + return (0); + } +} /* XmitFrame */ + + +/***************************************************************************** + * + * FreeTxDescriptors - release descriptors from the descriptor ring + * + * Description: + * This function releases descriptors from a transmit ring if they + * have been sent by the BMU. + * If a descriptors is sent, it can be freed and the message can + * be freed, too. + * The SOFTWARE controllable bit is used to prevent running around a + * completely free ring for ever. If this bit is no set in the + * frame (by XmitFrame), this frame has never been sent or is + * already freed. + * The Tx descriptor ring lock must be held while calling this function !!! + * + * Returns: + * none + */ +static void FreeTxDescriptors( +SK_AC *pAC, /* pointer to the adapter context */ +TX_PORT *pTxPort) /* pointer to destination port structure */ +{ +TXD *pTxd; /* pointer to the checked descriptor */ +TXD *pNewTail; /* pointer to 'end' of the ring */ +SK_U32 Control; /* TBControl field of descriptor */ + + pNewTail = pTxPort->pTxdRingTail; + pTxd = pNewTail; + + /* + * loop forever; exits if TX_CTRL_SOFTWARE bit not set in start frame + * or TX_CTRL_OWN_BMU bit set in any frame + */ + while (1) { + Control = pTxd->TBControl; + if ((Control & TX_CTRL_SOFTWARE) == 0) { + /* + * software controllable bit is set in first + * fragment when given to BMU. Not set means that + * this fragment was never sent or is already + * freed ( -> ring completely free now). + */ + pTxPort->pTxdRingTail = pTxd; + pAC->dev->tbusy = 0; + return; + } + if (Control & TX_CTRL_OWN_BMU) { + pTxPort->pTxdRingTail = pTxd; + if (pTxPort->TxdRingFree > 0) { + pAC->dev->tbusy = 0; + } + return; + } + + DEV_KFREE_SKB(pTxd->pMBuf); /* free message */ + pTxPort->TxdRingFree++; + pTxd->TBControl &= ~TX_CTRL_SOFTWARE; + pTxd = pTxd->pNextTxd; /* point behind fragment with EOF */ + } /* while(forever) */ +} /* FreeTxDescriptors */ + + +/***************************************************************************** + * + * FillRxRing - fill the receive ring with valid descriptors + * + * Description: + * This function fills the receive ring descriptors with data + * segments and makes them valid for the BMU. + * The active ring is filled completely, if possible. + * The non-active ring is filled only partial to save memory. + * + * Description of rx ring structure: + * head - points to the descriptor which will be used next by the BMU + * tail - points to the next descriptor to give to the BMU + * + * Returns: N/A + */ +static void FillRxRing( +SK_AC *pAC, /* pointer to the adapter context */ +RX_PORT *pRxPort) /* ptr to port struct for which the ring + should be filled */ +{ +unsigned int Flags; + + spin_lock_irqsave(&pRxPort->RxDesRingLock, Flags); + while (pRxPort->RxdRingFree > pRxPort->RxFillLimit) { + if(!FillRxDescriptor(pAC, pRxPort)) + break; + } + spin_unlock_irqrestore(&pRxPort->RxDesRingLock, Flags); +} /* FillRxRing */ + + +/***************************************************************************** + * + * FillRxDescriptor - fill one buffer into the receive ring + * + * Description: + * The function allocates a new receive buffer and + * puts it into the next descriptor. + * + * Returns: + * SK_TRUE - a buffer was added to the ring + * SK_FALSE - a buffer could not be added + */ +static SK_BOOL FillRxDescriptor( +SK_AC *pAC, /* pointer to the adapter context struct */ +RX_PORT *pRxPort) /* ptr to port struct of ring to fill */ +{ +struct sk_buff *pMsgBlock; /* pointer to a new message block */ +RXD *pRxd; /* the rxd to fill */ +SK_U16 Length; /* data fragment length */ +SK_U64 PhysAddr; /* physical address of a rx buffer */ + + pMsgBlock = alloc_skb(pAC->RxBufSize, GFP_ATOMIC); + if (pMsgBlock == NULL) { + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, + SK_DBGCAT_DRV_ENTRY, + ("%s: Allocation of rx buffer failed !\n", + pAC->dev->name)); + SK_PNMI_CNT_NO_RX_BUF(pAC); + return(SK_FALSE); + } + skb_reserve(pMsgBlock, 2); /* to align IP frames */ + /* skb allocated ok, so add buffer */ + pRxd = pRxPort->pRxdRingTail; + pRxPort->pRxdRingTail = pRxd->pNextRxd; + pRxPort->RxdRingFree--; + Length = pAC->RxBufSize; + PhysAddr = virt_to_bus(pMsgBlock->data); + pRxd->VDataLow = (SK_U32) (PhysAddr & 0xffffffff); + pRxd->VDataHigh = (SK_U32) (PhysAddr >> 32); + pRxd->pMBuf = pMsgBlock; + pRxd->RBControl = RX_CTRL_OWN_BMU | RX_CTRL_STF | + RX_CTRL_EOF_IRQ | RX_CTRL_CHECK_CSUM | Length; + return (SK_TRUE); + +} /* FillRxDescriptor */ + + +/***************************************************************************** + * + * ReQueueRxBuffer - fill one buffer back into the receive ring + * + * Description: + * Fill a given buffer back into the rx ring. The buffer + * has been previously allocated and aligned, and its phys. + * address calculated, so this is no more necessary. + * + * Returns: N/A + */ +static void ReQueueRxBuffer( +SK_AC *pAC, /* pointer to the adapter context struct */ +RX_PORT *pRxPort, /* ptr to port struct of ring to fill */ +struct sk_buff *pMsg, /* pointer to the buffer */ +SK_U32 PhysHigh, /* phys address high dword */ +SK_U32 PhysLow) /* phys address low dword */ +{ +RXD *pRxd; /* the rxd to fill */ +SK_U16 Length; /* data fragment length */ + + pRxd = pRxPort->pRxdRingTail; + pRxPort->pRxdRingTail = pRxd->pNextRxd; + pRxPort->RxdRingFree--; + Length = pAC->RxBufSize; + pRxd->VDataLow = PhysLow; + pRxd->VDataHigh = PhysHigh; + pRxd->pMBuf = pMsg; + pRxd->RBControl = RX_CTRL_OWN_BMU | RX_CTRL_STF | + RX_CTRL_EOF_IRQ | RX_CTRL_CHECK_CSUM | Length; + return; +} /* ReQueueRxBuffer */ + + +/***************************************************************************** + * + * ReceiveIrq - handle a receive IRQ + * + * Description: + * This function is called when a receive IRQ is set. + * It walks the receive descriptor ring and sends up all + * frames that are complete. + * + * Returns: N/A + */ +static void ReceiveIrq( +SK_AC *pAC, /* pointer to adapter context */ +RX_PORT *pRxPort) /* pointer to receive port struct */ +{ +RXD *pRxd; /* pointer to receive descriptors */ +SK_U32 Control; /* control field of descriptor */ +struct sk_buff *pMsg; /* pointer to message holding frame */ +struct sk_buff *pNewMsg; /* pointer to a new message for copying frame */ +int FrameLength; /* total length of received frame */ +SK_MBUF *pRlmtMbuf; /* ptr to a buffer for giving a frame to rlmt */ +SK_EVPARA EvPara; /* an event parameter union */ +int PortIndex = pRxPort->PortIndex; +unsigned int Offset; +unsigned int NumBytes; +unsigned int ForRlmt; +SK_BOOL IsBc; +SK_BOOL IsMc; +SK_U32 FrameStat; +unsigned short Csum1; +unsigned short Csum2; +unsigned short Type; +int Result; + +rx_start: + /* do forever; exit if RX_CTRL_OWN_BMU found */ + while (pRxPort->RxdRingFree < pAC->RxDescrPerRing) { + pRxd = pRxPort->pRxdRingHead; + + Control = pRxd->RBControl; + + /* check if this descriptor is ready */ + if ((Control & RX_CTRL_OWN_BMU) != 0) { + /* this descriptor is not yet ready */ + FillRxRing(pAC, pRxPort); + return; + } + + /* get length of frame and check it */ + FrameLength = Control & RX_CTRL_LEN_MASK; + if (FrameLength > pAC->RxBufSize) + goto rx_failed; + + /* check for STF and EOF */ + if ((Control & (RX_CTRL_STF | RX_CTRL_EOF)) != + (RX_CTRL_STF | RX_CTRL_EOF)) + goto rx_failed; + + /* here we have a complete frame in the ring */ + pMsg = pRxd->pMBuf; + + /* + * if short frame then copy data to reduce memory waste + */ + if (FrameLength < SK_COPY_THRESHOLD) { + pNewMsg = alloc_skb(FrameLength+2, GFP_ATOMIC); + if (pNewMsg == NULL) { + /* use original skb */ + /* set length in message */ + skb_put(pMsg, FrameLength); + } + else { + /* alloc new skb and copy data */ + skb_reserve(pNewMsg, 2); + skb_put(pNewMsg, FrameLength); + eth_copy_and_sum(pNewMsg, pMsg->data, + FrameLength, 0); + ReQueueRxBuffer(pAC, pRxPort, pMsg, + pRxd->VDataHigh, pRxd->VDataLow); + pMsg = pNewMsg; + } + } + else { + /* set length in message */ + skb_put(pMsg, FrameLength); + /* hardware checksum */ + Type = ntohs(*((short*)&pMsg->data[12])); + if (Type == 0x800) { + Csum1= pRxd->TcpSums & 0xffff; + Csum2=(pRxd->TcpSums >> 16) & 0xffff; + if ((Csum1 & 0xfffe) && (Csum2 & 0xfffe)) { + Result = SkCsGetReceiveInfo(pAC, + &pMsg->data[14], + Csum1, Csum2); + if (Result == + SKCS_STATUS_IP_FRAGMENT || + Result == + SKCS_STATUS_IP_CSUM_OK || + Result == + SKCS_STATUS_TCP_CSUM_OK || + Result == + SKCS_STATUS_UDP_CSUM_OK) { + pMsg->ip_summed = + CHECKSUM_UNNECESSARY; + } + } /* checksum calculation valid */ + } /* IP frame */ + } /* frame > SK_COPY_TRESHOLD */ + + FrameStat = pRxd->FrameStat; + pRxd = pRxd->pNextRxd; + pRxPort->pRxdRingHead = pRxd; + pRxPort->RxdRingFree ++; + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS, + ("Received frame of length %d on port %d\n", + FrameLength, PortIndex)); + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS, + ("Number of free rx descriptors: %d\n", + pRxPort->RxdRingFree)); + + if ((Control & RX_CTRL_STAT_VALID) == RX_CTRL_STAT_VALID && + (FrameStat & + (XMR_FS_ANY_ERR | XMR_FS_1L_VLAN | XMR_FS_2L_VLAN)) + == 0) { + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, + SK_DBGCAT_DRV_RX_PROGRESS,("V")); + ForRlmt = SK_RLMT_RX_PROTOCOL; + IsBc = (FrameStat & XMR_FS_BC)==XMR_FS_BC; + SK_RLMT_PRE_LOOKAHEAD(pAC, PortIndex, FrameLength, + IsBc, &Offset, &NumBytes); + if (NumBytes != 0) { + IsMc = (FrameStat & XMR_FS_MC)==XMR_FS_MC; + SK_RLMT_LOOKAHEAD(pAC, PortIndex, + &pMsg->data[Offset], + IsBc, IsMc, &ForRlmt); + } + if (ForRlmt == SK_RLMT_RX_PROTOCOL) { + /* send up only frames from active port */ + if (PortIndex == pAC->ActivePort) { + /* frame for upper layer */ + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, + SK_DBGCAT_DRV_RX_PROGRESS, + ("U")); +#ifdef DUMP_RX + DumpMsg(pMsg, "Rx"); +#endif + pMsg->dev = pAC->dev; + pMsg->protocol = eth_type_trans(pMsg, + pAC->dev); + SK_PNMI_CNT_RX_OCTETS_DELIVERED(pAC, + FrameLength); + netif_rx(pMsg); + pAC->dev->last_rx = jiffies; + } + else { + /* drop frame */ + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, + SK_DBGCAT_DRV_RX_PROGRESS, + ("D")); + DEV_KFREE_SKB(pMsg); + } + } /* if not for rlmt */ + else { + /* packet for rlmt */ + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, + SK_DBGCAT_DRV_RX_PROGRESS, ("R")); + pRlmtMbuf = SkDrvAllocRlmtMbuf(pAC, + pAC->IoBase, FrameLength); + if (pRlmtMbuf != NULL) { + pRlmtMbuf->pNext = NULL; + pRlmtMbuf->Length = FrameLength; + pRlmtMbuf->PortIdx = PortIndex; + EvPara.pParaPtr = pRlmtMbuf; + memcpy((char*)(pRlmtMbuf->pData), + (char*)(pMsg->data), + FrameLength); + SkEventQueue(pAC, SKGE_RLMT, + SK_RLMT_PACKET_RECEIVED, + EvPara); + pAC->CheckQueue = SK_TRUE; + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, + SK_DBGCAT_DRV_RX_PROGRESS, + ("Q")); + } + if ((pAC->dev->flags & + (IFF_PROMISC | IFF_ALLMULTI)) != 0 || + (ForRlmt & SK_RLMT_RX_PROTOCOL) == + SK_RLMT_RX_PROTOCOL) { + pMsg->dev = pAC->dev; + pMsg->protocol = eth_type_trans(pMsg, + pAC->dev); + netif_rx(pMsg); + pAC->dev->last_rx = jiffies; + } + else { + DEV_KFREE_SKB(pMsg); + } + + } /* if packet for rlmt */ + } /* if valid frame */ + else { + /* there is a receive error in this frame */ + if ((FrameStat & XMR_FS_1L_VLAN) != 0) { + printk("%s: Received frame" + " with VLAN Level 1 header, check" + " switch configuration\n", + pAC->dev->name); + } + if ((FrameStat & XMR_FS_2L_VLAN) != 0) { + printk("%s: Received frame" + " with VLAN Level 2 header, check" + " switch configuration\n", + pAC->dev->name); + } + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, + SK_DBGCAT_DRV_RX_PROGRESS, + ("skge: Error in received frame, dropped!\n" + "Control: %x\nRxStat: %x\n", + Control, FrameStat)); + DEV_KFREE_SKB(pMsg); + } + } /* while */ + FillRxRing(pAC, pRxPort); + /* do not start if called from Close */ + if (pAC->BoardLevel > 0) { + ClearAndStartRx(pAC, PortIndex); + } + return; + +rx_failed: + /* remove error frame */ + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ERROR, + ("Schrottdescriptor, length: 0x%x\n", FrameLength)); + DEV_KFREE_SKB(pRxd->pMBuf); + pRxd->pMBuf = NULL; + pRxPort->RxdRingFree++; + pRxPort->pRxdRingHead = pRxd->pNextRxd; + goto rx_start; + +} /* ReceiveIrq */ + + +/***************************************************************************** + * + * ClearAndStartRx - give a start receive command to BMU, clear IRQ + * + * Description: + * This function sends a start command and a clear interrupt + * command for one receive queue to the BMU. + * + * Returns: N/A + * none + */ +static void ClearAndStartRx( +SK_AC *pAC, /* pointer to the adapter context */ +int PortIndex) /* index of the receive port (XMAC) */ +{ + SK_OUT8(pAC->IoBase, RxQueueAddr[PortIndex]+RX_Q_CTRL, + RX_Q_CTRL_START | RX_Q_CTRL_CLR_I_EOF); +} /* ClearAndStartRx */ + + +/***************************************************************************** + * + * ClearTxIrq - give a clear transmit IRQ command to BMU + * + * Description: + * This function sends a clear tx IRQ command for one + * transmit queue to the BMU. + * + * Returns: N/A + */ +static void ClearTxIrq( +SK_AC *pAC, /* pointer to the adapter context */ +int PortIndex, /* index of the transmit port (XMAC) */ +int Prio) /* priority or normal queue */ +{ + SK_OUT8(pAC->IoBase, TxQueueAddr[PortIndex][Prio]+TX_Q_CTRL, + TX_Q_CTRL_CLR_I_EOF); +} /* ClearTxIrq */ + + +/***************************************************************************** + * + * ClearRxRing - remove all buffers from the receive ring + * + * Description: + * This function removes all receive buffers from the ring. + * The receive BMU must be stopped before calling this function. + * + * Returns: N/A + */ +static void ClearRxRing( +SK_AC *pAC, /* pointer to adapter context */ +RX_PORT *pRxPort) /* pointer to rx port struct */ +{ +RXD *pRxd; /* pointer to the current descriptor */ +unsigned int Flags; + + if (pRxPort->RxdRingFree == pAC->RxDescrPerRing) { + return; + } + spin_lock_irqsave(&pRxPort->RxDesRingLock, Flags); + pRxd = pRxPort->pRxdRingHead; + do { + if (pRxd->pMBuf != NULL) { + DEV_KFREE_SKB(pRxd->pMBuf); + pRxd->pMBuf = NULL; + } + pRxd->RBControl &= RX_CTRL_OWN_BMU; + pRxd = pRxd->pNextRxd; + pRxPort->RxdRingFree++; + } while (pRxd != pRxPort->pRxdRingTail); + pRxPort->pRxdRingTail = pRxPort->pRxdRingHead; + spin_unlock_irqrestore(&pRxPort->RxDesRingLock, Flags); +} /* ClearRxRing */ + + +/***************************************************************************** + * + * ClearTxRing - remove all buffers from the transmit ring + * + * Description: + * This function removes all transmit buffers from the ring. + * The transmit BMU must be stopped before calling this function + * and transmitting at the upper level must be disabled. + * The BMU own bit of all descriptors is cleared, the rest is + * done by calling FreeTxDescriptors. + * + * Returns: N/A + */ +static void ClearTxRing( +SK_AC *pAC, /* pointer to adapter context */ +TX_PORT *pTxPort) /* pointer to tx prt struct */ +{ +TXD *pTxd; /* pointer to the current descriptor */ +int i; +unsigned int Flags; + + spin_lock_irqsave(&pTxPort->TxDesRingLock, Flags); + pTxd = pTxPort->pTxdRingHead; + for (i=0; iTxDescrPerRing; i++) { + pTxd->TBControl &= ~TX_CTRL_OWN_BMU; + pTxd = pTxd->pNextTxd; + } + FreeTxDescriptors(pAC, pTxPort); + spin_unlock_irqrestore(&pTxPort->TxDesRingLock, Flags); +} /* ClearTxRing */ + + +/***************************************************************************** + * + * SetQueueSizes - configure the sizes of rx and tx queues + * + * Description: + * This function assigns the sizes for active and passive port + * to the appropriate HWinit structure variables. + * The passive port(s) get standard values, all remaining RAM + * is given to the active port. + * The queue sizes are in kbyte and must be multiple of 8. + * The limits for the number of buffers filled into the rx rings + * is also set in this routine. + * + * Returns: + * none + */ +static void SetQueueSizes( +SK_AC *pAC) /* pointer to the adapter context */ +{ +int StandbyRam; /* adapter RAM used for a standby port */ +int RemainingRam; /* adapter RAM available for the active port */ +int RxRam; /* RAM used for the active port receive queue */ +int i; /* loop counter */ + + StandbyRam = SK_RLMT_STANDBY_QRXSIZE + SK_RLMT_STANDBY_QXASIZE + + SK_RLMT_STANDBY_QXSSIZE; + RemainingRam = pAC->GIni.GIRamSize - + (pAC->GIni.GIMacsFound-1) * StandbyRam; + for (i=0; iGIni.GIMacsFound; i++) { + pAC->GIni.GP[i].PRxQSize = SK_RLMT_STANDBY_QRXSIZE; + pAC->GIni.GP[i].PXSQSize = SK_RLMT_STANDBY_QXSSIZE; + pAC->GIni.GP[i].PXAQSize = SK_RLMT_STANDBY_QXASIZE; + } + RxRam = (RemainingRam * 8 / 10) & ~7; + pAC->GIni.GP[pAC->ActivePort].PRxQSize = RxRam; + pAC->GIni.GP[pAC->ActivePort].PXSQSize = 0; + pAC->GIni.GP[pAC->ActivePort].PXAQSize = + (RemainingRam - RxRam) & ~7; + pAC->RxQueueSize = RxRam; + pAC->TxSQueueSize = 0; + pAC->TxAQueueSize = (RemainingRam - RxRam) & ~7; + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY, + ("queue sizes settings - rx:%d txA:%d txS:%d\n", + pAC->RxQueueSize,pAC->TxAQueueSize, pAC->TxSQueueSize)); + + for (i=0; iRxPort[i].RxFillLimit = pAC->RxDescrPerRing; + } + for (i=0; iGIni.GIMacsFound; i++) { + pAC->RxPort[i].RxFillLimit = pAC->RxDescrPerRing - 100; + } + /* + * Do not set the Limit to 0, because this could cause + * wrap around with ReQueue'ed buffers (a buffer could + * be requeued in the same position, made accessable to + * the hardware, and the hardware could change its + * contents! + */ + pAC->RxPort[pAC->ActivePort].RxFillLimit = 1; + +#ifdef DEBUG + for (i=0; iGIni.GIMacsFound; i++) { + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS, + ("i: %d, RxQSize: %d, PXSQsize: %d, PXAQSize: %d\n", + i, + pAC->GIni.GP[i].PRxQSize, + pAC->GIni.GP[i].PXSQSize, + pAC->GIni.GP[i].PXAQSize)); + } +#endif +} /* SetQueueSizes */ + + +/***************************************************************************** + * + * SkGeSetMacAddr - Set the hardware MAC address + * + * Description: + * This function sets the MAC address used by the adapter. + * + * Returns: + * 0, if everything is ok + * !=0, on error + */ +static int SkGeSetMacAddr(struct device *dev, void *p) +{ +SK_AC *pAC = (SK_AC*) dev->priv; +struct sockaddr *addr = p; +unsigned int Flags; + + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY, + ("SkGeSetMacAddr starts now...\n")); + if(dev->start) { + return -EBUSY; + } + memcpy(dev->dev_addr, addr->sa_data,dev->addr_len); + + spin_lock_irqsave(&pAC->SlowPathLock, Flags); + SkAddrOverride(pAC, pAC->IoBase, pAC->ActivePort, + (SK_MAC_ADDR*)dev->dev_addr, SK_ADDR_VIRTUAL_ADDRESS); + + spin_unlock_irqrestore(&pAC->SlowPathLock, Flags); + return 0; +} /* SkGeSetMacAddr */ + + +/***************************************************************************** + * + * SkGeSetRxMode - set receive mode + * + * Description: + * This function sets the receive mode of an adapter. The adapter + * supports promiscuous mode, allmulticast mode and a number of + * multicast addresses. If more multicast addresses the available + * are selected, a hash function in the hardware is used. + * + * Returns: + * 0, if everything is ok + * !=0, on error + */ +static void SkGeSetRxMode(struct device *dev) +{ +SK_AC *pAC; +struct dev_mc_list *pMcList; +int i; +unsigned int Flags; + + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY, + ("SkGeSetRxMode starts now... ")); + pAC = (SK_AC*) dev->priv; + + spin_lock_irqsave(&pAC->SlowPathLock, Flags); + if (dev->flags & IFF_PROMISC) { + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY, + ("PROMISCUOUS mode\n")); + SkAddrPromiscuousChange(pAC, pAC->IoBase, pAC->ActivePort, + SK_PROM_MODE_LLC); + } else if (dev->flags & IFF_ALLMULTI) { + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY, + ("ALLMULTI mode\n")); + SkAddrPromiscuousChange(pAC, pAC->IoBase, pAC->ActivePort, + SK_PROM_MODE_ALL_MC); + } else { + SkAddrPromiscuousChange(pAC, pAC->IoBase, pAC->ActivePort, + SK_PROM_MODE_NONE); + SkAddrMcClear(pAC, pAC->IoBase, pAC->ActivePort, 0); + + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY, + ("Number of MC entries: %d ", dev->mc_count)); + + pMcList = dev->mc_list; + for (i=0; imc_count; i++, pMcList = pMcList->next) { + SkAddrMcAdd(pAC, pAC->IoBase, pAC->ActivePort, + (SK_MAC_ADDR*)pMcList->dmi_addr, 0); + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MCA, + ("%02x:%02x:%02x:%02x:%02x:%02x\n", + pMcList->dmi_addr[0], + pMcList->dmi_addr[1], + pMcList->dmi_addr[2], + pMcList->dmi_addr[3], + pMcList->dmi_addr[4], + pMcList->dmi_addr[5])); + } + SkAddrMcUpdate(pAC, pAC->IoBase, pAC->ActivePort); + + } + spin_unlock_irqrestore(&pAC->SlowPathLock, Flags); + + return; +} /* SkGeSetRxMode */ + + +/***************************************************************************** + * + * SkGeChangeMtu - set the MTU to another value + * + * Description: + * This function sets is called whenever the MTU size is changed + * (ifconfig mtu xxx dev ethX). If the MTU is bigger than standard + * ethernet MTU size, long frame support is activated. + * + * Returns: + * 0, if everything is ok + * !=0, on error + */ +static int SkGeChangeMtu(struct device *dev, int NewMtu) +{ +SK_AC *pAC; +unsigned int Flags; +int i; +SK_EVPARA EvPara; + + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY, + ("SkGeChangeMtu starts now...\n")); + + pAC = (SK_AC*) dev->priv; + if ((NewMtu < 68) || (NewMtu > SK_JUMBO_MTU)) { + return -EINVAL; + } + + pAC->RxBufSize = NewMtu + 32; + dev->mtu = NewMtu; + + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY, + ("New MTU: %d\n", NewMtu)); + + /* prevent reconfiguration while changing the MTU */ + + /* disable interrupts */ + SK_OUT32(pAC->IoBase, B0_IMSK, 0); + spin_lock_irqsave(&pAC->SlowPathLock, Flags); + SkEventQueue(pAC, SKGE_RLMT, SK_RLMT_STOP, EvPara); + SkEventDispatcher(pAC, pAC->IoBase); + + for (i=0; iGIni.GIMacsFound; i++) { + spin_lock_irqsave( + &pAC->TxPort[i][TX_PRIO_LOW].TxDesRingLock, Flags); + } + pAC->dev->tbusy = 1; + + /* + * adjust number of rx buffers allocated + */ + if (NewMtu > 1500) { + /* use less rx buffers */ + for (i=0; iGIni.GIMacsFound; i++) { + if (i == pAC->ActivePort) + pAC->RxPort[i].RxFillLimit = + pAC->RxDescrPerRing - 100; + else + pAC->RxPort[i].RxFillLimit = + pAC->RxDescrPerRing - 10; + + } + } + else { + /* use normal anoumt of rx buffers */ + for (i=0; iGIni.GIMacsFound; i++) { + if (i == pAC->ActivePort) + pAC->RxPort[i].RxFillLimit = 1; + else + pAC->RxPort[i].RxFillLimit = + pAC->RxDescrPerRing - 100; + } + } + + SkGeDeInit(pAC, pAC->IoBase); + + /* + * enable/disable hardware support for long frames + */ + if (NewMtu > 1500) { + pAC->GIni.GIPortUsage = SK_JUMBO_LINK; + for (i=0; iGIni.GIMacsFound; i++) { + pAC->GIni.GP[i].PRxCmd = + XM_RX_STRIP_FCS | XM_RX_LENERR_OK; + } + } + else { + pAC->GIni.GIPortUsage = SK_RED_LINK; + for (i=0; iGIni.GIMacsFound; i++) { + pAC->GIni.GP[i].PRxCmd = + XM_RX_STRIP_FCS | XM_RX_LENERR_OK; + } + } + + SkGeInit( pAC, pAC->IoBase, 1); + SkI2cInit( pAC, pAC->IoBase, 1); + SkEventInit(pAC, pAC->IoBase, 1); + SkPnmiInit( pAC, pAC->IoBase, 1); + SkAddrInit( pAC, pAC->IoBase, 1); + SkRlmtInit( pAC, pAC->IoBase, 1); + SkTimerInit(pAC, pAC->IoBase, 1); + + SkGeInit( pAC, pAC->IoBase, 2); + SkI2cInit( pAC, pAC->IoBase, 2); + SkEventInit(pAC, pAC->IoBase, 2); + SkPnmiInit( pAC, pAC->IoBase, 2); + SkAddrInit( pAC, pAC->IoBase, 2); + SkRlmtInit( pAC, pAC->IoBase, 2); + SkTimerInit(pAC, pAC->IoBase, 2); + + /* + * clear and reinit the rx rings here + */ + for (i=0; iGIni.GIMacsFound; i++) { + ReceiveIrq(pAC, &pAC->RxPort[i]); + ClearRxRing(pAC, &pAC->RxPort[i]); + FillRxRing(pAC, &pAC->RxPort[i]); + + // Enable transmit descriptor polling. + SkGePollTxD(pAC, pAC->IoBase, i, SK_TRUE); + FillRxRing(pAC, &pAC->RxPort[i]); + }; + + SkGeYellowLED(pAC, pAC->IoBase, 1); + +#ifdef USE_INT_MOD + { + unsigned long ModBase; + ModBase = 53125000 / INTS_PER_SEC; + SK_OUT32(pAC->IoBase, B2_IRQM_INI, ModBase); + SK_OUT32(pAC->IoBase, B2_IRQM_MSK, IRQ_MOD_MASK); + SK_OUT32(pAC->IoBase, B2_IRQM_CTRL, TIM_START); + } +#endif + + pAC->dev->tbusy = 0; + for (i=pAC->GIni.GIMacsFound-1; i>=0; i--) { + spin_unlock_irqrestore( + &pAC->TxPort[i][TX_PRIO_LOW].TxDesRingLock, Flags); + } + + /* enable Interrupts */ + SK_OUT32(pAC->IoBase, B0_IMSK, IRQ_MASK); + SK_OUT32(pAC->IoBase, B0_HWE_IMSK, IRQ_HWE_MASK); + + SkEventQueue(pAC, SKGE_RLMT, SK_RLMT_START, EvPara); + SkEventDispatcher(pAC, pAC->IoBase); + + + spin_unlock_irqrestore(&pAC->SlowPathLock, Flags); + + return 0; +} /* SkGeChangeMtu */ + + +/***************************************************************************** + * + * SkGeStats - return ethernet device statistics + * + * Description: + * This function return statistic data about the ethernet device + * to the operating system. + * + * Returns: + * pointer to the statistic structure. + */ +static struct net_device_stats *SkGeStats(struct device *dev) +{ +SK_AC *pAC = (SK_AC*) dev->priv; +SK_PNMI_STRUCT_DATA *pPnmiStruct; /* structure for all Pnmi-Data */ +SK_PNMI_STAT *pPnmiStat; /* pointer to virtual XMAC stat. data */SK_PNMI_CONF *pPnmiConf; /* pointer to virtual link config. */ +unsigned int Size; /* size of pnmi struct */ +unsigned int Flags; /* for spin lock */ + + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY, + ("SkGeStats starts now...\n")); + pPnmiStruct = &pAC->PnmiStruct; + memset(pPnmiStruct, 0, sizeof(SK_PNMI_STRUCT_DATA)); + spin_lock_irqsave(&pAC->SlowPathLock, Flags); + Size = SK_PNMI_STRUCT_SIZE; + SkPnmiGetStruct(pAC, pAC->IoBase, pPnmiStruct, &Size); + spin_unlock_irqrestore(&pAC->SlowPathLock, Flags); + pPnmiStat = &pPnmiStruct->Stat[0]; + pPnmiConf = &pPnmiStruct->Conf[0]; + + pAC->stats.rx_packets = (SK_U32) pPnmiStruct->RxDeliveredCts & 0xFFFFFFFF; + pAC->stats.tx_packets = (SK_U32) pPnmiStat->StatTxOkCts & 0xFFFFFFFF; + pAC->stats.rx_bytes = (SK_U32) pPnmiStruct->RxOctetsDeliveredCts; + pAC->stats.tx_bytes = (SK_U32) pPnmiStat->StatTxOctetsOkCts; + pAC->stats.rx_errors = (SK_U32) pPnmiStruct->InErrorsCts & 0xFFFFFFFF; + pAC->stats.tx_errors = (SK_U32) pPnmiStat->StatTxSingleCollisionCts & 0xFFFFFFFF; + pAC->stats.rx_dropped = (SK_U32) pPnmiStruct->RxNoBufCts & 0xFFFFFFFF; + pAC->stats.tx_dropped = (SK_U32) pPnmiStruct->TxNoBufCts & 0xFFFFFFFF; + pAC->stats.multicast = (SK_U32) pPnmiStat->StatRxMulticastOkCts & 0xFFFFFFFF; + pAC->stats.collisions = (SK_U32) pPnmiStat->StatTxSingleCollisionCts & 0xFFFFFFFF; + + /* detailed rx_errors: */ + pAC->stats.rx_length_errors = (SK_U32) pPnmiStat->StatRxRuntCts & 0xFFFFFFFF; + pAC->stats.rx_over_errors = (SK_U32) pPnmiStat->StatRxFifoOverflowCts & 0xFFFFFFFF; + pAC->stats.rx_crc_errors = (SK_U32) pPnmiStat->StatRxFcsCts & 0xFFFFFFFF; + pAC->stats.rx_frame_errors = (SK_U32) pPnmiStat->StatRxFramingCts & 0xFFFFFFFF; + pAC->stats.rx_fifo_errors = (SK_U32) pPnmiStat->StatRxFifoOverflowCts & 0xFFFFFFFF; + pAC->stats.rx_missed_errors = (SK_U32) pPnmiStat->StatRxMissedCts & 0xFFFFFFFF; + + /* detailed tx_errors */ + pAC->stats.tx_aborted_errors = (SK_U32) 0; + pAC->stats.tx_carrier_errors = (SK_U32) pPnmiStat->StatTxCarrierCts & 0xFFFFFFFF; + pAC->stats.tx_fifo_errors = (SK_U32) pPnmiStat->StatTxFifoUnderrunCts & 0xFFFFFFFF; + pAC->stats.tx_heartbeat_errors = (SK_U32) pPnmiStat->StatTxCarrierCts & 0xFFFFFFFF; + pAC->stats.tx_window_errors = (SK_U32) 0; + + return(&pAC->stats); +} /* SkGeStats */ + + +/***************************************************************************** + * + * SkGeIoctl - IO-control function + * + * Description: + * This function is called if an ioctl is issued on the device. + * There are three subfunction for reading, writing and test-writing + * the private MIB data structure (usefull for SysKonnect-internal tools). + * + * Returns: + * 0, if everything is ok + * !=0, on error + */ +static int SkGeIoctl(struct device *dev, struct ifreq *rq, int cmd) +{ +SK_AC *pAC; +SK_GE_IOCTL Ioctl; +unsigned int Err = 0; +int Size; + + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY, + ("SkGeIoctl starts now...\n")); + pAC = (SK_AC*) dev->priv; + + if(copy_from_user(&Ioctl, rq->ifr_data, sizeof(SK_GE_IOCTL))) { + return -EFAULT; + } + + switch(cmd) { + case SK_IOCTL_SETMIB: + case SK_IOCTL_PRESETMIB: + if (!capable(CAP_NET_ADMIN)) return -EPERM; + case SK_IOCTL_GETMIB: + if(copy_from_user(&pAC->PnmiStruct, Ioctl.pData, + Ioctl.LenPnmiStruct)? + Ioctl.Len : sizeof(pAC->PnmiStruct))) { + return -EFAULT; + } + Size = SkGeIocMib(pAC, Ioctl.Len, cmd); + if(copy_to_user(Ioctl.pData, &pAC->PnmiStruct, + Ioctl.Lenifr_data, &Ioctl, sizeof(SK_GE_IOCTL))) { + return -EFAULT; + } + break; + default: + Err = -EOPNOTSUPP; + } + return(Err); +} /* SkGeIoctl */ + + +/***************************************************************************** + * + * SkGeIocMib - handle a GetMib, SetMib- or PresetMib-ioctl message + * + * Description: + * This function reads/writes the MIB data using PNMI (Private Network + * Management Interface). + * The destination for the data must be provided with the + * ioctl call and is given to the driver in the form of + * a user space address. + * Copying from the user-provided data area into kernel messages + * and back is done by copy_from_user and copy_to_user calls in + * SkGeIoctl. + * + * Returns: + * returned size from PNMI call + */ +static int SkGeIocMib( +SK_AC *pAC, /* pointer to the adapter context */ +unsigned int Size, /* length of ioctl data */ +int mode) /* flag for set/preset */ +{ +unsigned int Flags; /* for spin lock */ + + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY, + ("SkGeIocMib starts now...\n")); + /* access MIB */ + spin_lock_irqsave(&pAC->SlowPathLock, Flags); + switch(mode) { + case SK_IOCTL_GETMIB: + SkPnmiGetStruct(pAC, pAC->IoBase, &pAC->PnmiStruct, &Size); + break; + case SK_IOCTL_PRESETMIB: + SkPnmiPreSetStruct(pAC, pAC->IoBase, &pAC->PnmiStruct, &Size); + break; + case SK_IOCTL_SETMIB: + SkPnmiSetStruct(pAC, pAC->IoBase, &pAC->PnmiStruct, &Size); + break; + default: + break; + } + spin_unlock_irqrestore(&pAC->SlowPathLock, Flags); + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ENTRY, + ("MIB data access succeeded\n")); + return (Size); +} /* SkGeIocMib */ + + +/***************************************************************************** + * + * GetConfiguration - read configuration information + * + * Description: + * This function reads per-adapter configuration information from + * the options provided on the command line. + * + * Returns: + * none + */ +static void GetConfiguration( +SK_AC *pAC) /* pointer to the adapter context structure */ +{ +SK_I32 Port; /* preferred port */ +int AutoNeg; /* auto negotiation off (0) or on (1) */ +int DuplexCap; /* duplex capabilities (0=both, 1=full, 2=half */ +int MSMode; /* master / slave mode selection */ +SK_BOOL AutoSet; +SK_BOOL DupSet; +/* + * The two parameters AutoNeg. and DuplexCap. map to one configuration + * parameter. The mapping is described by this table: + * DuplexCap -> | both | full | half | + * AutoNeg | | | | + * ----------------------------------------------------------------- + * Off | illegal | Full | Half | + * ----------------------------------------------------------------- + * On | AutoBoth | AutoFull | AutoHalf | + * ----------------------------------------------------------------- + * Sense | AutoSense | AutoSense | AutoSense | + */ +int Capabilities[3][3] = + { { -1, SK_LMODE_FULL, SK_LMODE_HALF}, + {SK_LMODE_AUTOBOTH, SK_LMODE_AUTOFULL, SK_LMODE_AUTOHALF}, + {SK_LMODE_AUTOSENSE, SK_LMODE_AUTOSENSE, SK_LMODE_AUTOSENSE} }; +#define DC_BOTH 0 +#define DC_FULL 1 +#define DC_HALF 2 +#define AN_OFF 0 +#define AN_ON 1 +#define AN_SENS 2 + + /* settings for port A */ + AutoNeg = AN_SENS; /* default: do auto Sense */ + AutoSet = SK_FALSE; + if (AutoNeg_A != NULL && pAC->IndexIndex] != NULL) { + AutoSet = SK_TRUE; + if (strcmp(AutoNeg_A[pAC->Index],"")==0) { + AutoSet = SK_FALSE; + } + else if (strcmp(AutoNeg_A[pAC->Index],"On")==0) { + AutoNeg = AN_ON; + } + else if (strcmp(AutoNeg_A[pAC->Index],"Off")==0) { + AutoNeg = AN_OFF; + } + else if (strcmp(AutoNeg_A[pAC->Index],"Sense")==0) { + AutoNeg = AN_SENS; + } + else printk("%s: Illegal value for AutoNeg_A\n", + pAC->dev->name); + } + + DuplexCap = DC_BOTH; + DupSet = SK_FALSE; + if (DupCap_A != NULL && pAC->IndexIndex] != NULL) { + DupSet = SK_TRUE; + if (strcmp(DupCap_A[pAC->Index],"")==0) { + DupSet = SK_FALSE; + } + else if (strcmp(DupCap_A[pAC->Index],"Both")==0) { + DuplexCap = DC_BOTH; + } + else if (strcmp(DupCap_A[pAC->Index],"Full")==0) { + DuplexCap = DC_FULL; + } + else if (strcmp(DupCap_A[pAC->Index],"Half")==0) { + DuplexCap = DC_HALF; + } + else printk("%s: Illegal value for DupCap_A\n", + pAC->dev->name); + } + + /* check for illegal combinations */ + if (AutoSet && AutoNeg==AN_SENS && DupSet) { + printk("%s, Port A: DuplexCapabilities" + " ignored using Sense mode\n", pAC->dev->name); + } + if (AutoSet && AutoNeg==AN_OFF && DupSet && DuplexCap==DC_BOTH){ + printk("%s, Port A: Illegal combination" + " of values AutoNeg. and DuplexCap.\n Using " + "Full Duplex\n", pAC->dev->name); + + DuplexCap = DC_FULL; + } + if (AutoSet && AutoNeg==AN_OFF && !DupSet) { + DuplexCap = DC_FULL; + } + + if (!AutoSet && DupSet) { + printk("%s, Port A: Duplex setting not" + " possible in\n default AutoNegotiation mode" + " (Sense).\n Using AutoNegotiation On\n", + pAC->dev->name); + AutoNeg = AN_ON; + } + + /* set the desired mode */ + pAC->GIni.GP[0].PLinkModeConf = + Capabilities[AutoNeg][DuplexCap]; + + pAC->GIni.GP[0].PFlowCtrlMode = SK_FLOW_MODE_SYM_OR_REM; + if (FlowCtrl_A != NULL && pAC->IndexIndex] != NULL) { + if (strcmp(FlowCtrl_A[pAC->Index],"") == 0) { + } + else if (strcmp(FlowCtrl_A[pAC->Index],"SymOrRem") == 0) { + pAC->GIni.GP[0].PFlowCtrlMode = + SK_FLOW_MODE_SYM_OR_REM; + } + else if (strcmp(FlowCtrl_A[pAC->Index],"Sym")==0) { + pAC->GIni.GP[0].PFlowCtrlMode = + SK_FLOW_MODE_SYMMETRIC; + } + else if (strcmp(FlowCtrl_A[pAC->Index],"LocSend")==0) { + pAC->GIni.GP[0].PFlowCtrlMode = + SK_FLOW_MODE_LOC_SEND; + } + else if (strcmp(FlowCtrl_A[pAC->Index],"None")==0) { + pAC->GIni.GP[0].PFlowCtrlMode = + SK_FLOW_MODE_NONE; + } + else printk("Illegal value for FlowCtrl_A\n"); + } + if (AutoNeg==AN_OFF && pAC->GIni.GP[0].PFlowCtrlMode!= + SK_FLOW_MODE_NONE) { + printk("%s, Port A: FlowControl" + " impossible without AutoNegotiation," + " disabled\n", pAC->dev->name); + pAC->GIni.GP[0].PFlowCtrlMode = SK_FLOW_MODE_NONE; + } + + MSMode = SK_MS_MODE_AUTO; /* default: do auto select */ + if (Role_A != NULL && pAC->IndexIndex] != NULL) { + if (strcmp(Role_A[pAC->Index],"")==0) { + } + else if (strcmp(Role_A[pAC->Index],"Auto")==0) { + MSMode = SK_MS_MODE_AUTO; + } + else if (strcmp(Role_A[pAC->Index],"Master")==0) { + MSMode = SK_MS_MODE_MASTER; + } + else if (strcmp(Role_A[pAC->Index],"Slave")==0) { + MSMode = SK_MS_MODE_SLAVE; + } + else printk("%s: Illegal value for Role_A\n", + pAC->dev->name); + } + pAC->GIni.GP[0].PMSMode = MSMode; + + + /* settings for port B */ + AutoNeg = AN_SENS; /* default: do auto Sense */ + AutoSet = SK_FALSE; + if (AutoNeg_B != NULL && pAC->IndexIndex] != NULL) { + AutoSet = SK_TRUE; + if (strcmp(AutoNeg_B[pAC->Index],"")==0) { + AutoSet = SK_FALSE; + } + else if (strcmp(AutoNeg_B[pAC->Index],"On")==0) { + AutoNeg = AN_ON; + } + else if (strcmp(AutoNeg_B[pAC->Index],"Off")==0) { + AutoNeg = AN_OFF; + } + else if (strcmp(AutoNeg_B[pAC->Index],"Sense")==0) { + AutoNeg = AN_SENS; + } + else printk("Illegal value for AutoNeg_B\n"); + } + + DuplexCap = DC_BOTH; + DupSet = SK_FALSE; + if (DupCap_B != NULL && pAC->IndexIndex] != NULL) { + DupSet = SK_TRUE; + if (strcmp(DupCap_B[pAC->Index],"")==0) { + DupSet = SK_FALSE; + } + else if (strcmp(DupCap_B[pAC->Index],"Both")==0) { + DuplexCap = DC_BOTH; + } + else if (strcmp(DupCap_B[pAC->Index],"Full")==0) { + DuplexCap = DC_FULL; + } + else if (strcmp(DupCap_B[pAC->Index],"Half")==0) { + DuplexCap = DC_HALF; + } + else printk("Illegal value for DupCap_B\n"); + } + + /* check for illegal combinations */ + if (AutoSet && AutoNeg==AN_SENS && DupSet) { + printk("%s, Port B: DuplexCapabilities" + " ignored using Sense mode\n", pAC->dev->name); + } + if (AutoSet && AutoNeg==AN_OFF && DupSet && DuplexCap==DC_BOTH){ + printk("%s, Port B: Illegal combination" + " of values AutoNeg. and DuplexCap.\n Using " + "Full Duplex\n", pAC->dev->name); + + DuplexCap = DC_FULL; + } + if (AutoSet && AutoNeg==AN_OFF && !DupSet) { + DuplexCap = DC_FULL; + } + + if (!AutoSet && DupSet) { + printk("%s, Port B: Duplex setting not" + " possible in\n default AutoNegotiation mode" + " (Sense).\n Using AutoNegotiation On\n", + pAC->dev->name); + AutoNeg = AN_ON; + } + + /* set the desired mode */ + pAC->GIni.GP[1].PLinkModeConf = + Capabilities[AutoNeg][DuplexCap]; + + pAC->GIni.GP[1].PFlowCtrlMode = SK_FLOW_MODE_SYM_OR_REM; + if (FlowCtrl_B != NULL && pAC->IndexIndex] != NULL) { + if (strcmp(FlowCtrl_B[pAC->Index],"") == 0) { + } + else if (strcmp(FlowCtrl_B[pAC->Index],"SymOrRem") == 0) { + pAC->GIni.GP[1].PFlowCtrlMode = + SK_FLOW_MODE_SYM_OR_REM; + } + else if (strcmp(FlowCtrl_B[pAC->Index],"Sym")==0) { + pAC->GIni.GP[1].PFlowCtrlMode = + SK_FLOW_MODE_SYMMETRIC; + } + else if (strcmp(FlowCtrl_B[pAC->Index],"LocSend")==0) { + pAC->GIni.GP[1].PFlowCtrlMode = + SK_FLOW_MODE_LOC_SEND; + } + else if (strcmp(FlowCtrl_B[pAC->Index],"None")==0) { + pAC->GIni.GP[1].PFlowCtrlMode = + SK_FLOW_MODE_NONE; + } + else printk("Illegal value for FlowCtrl_B\n"); + } + if (AutoNeg==AN_OFF && pAC->GIni.GP[1].PFlowCtrlMode!= + SK_FLOW_MODE_NONE) { + printk("%s, Port B: FlowControl" + " impossible without AutoNegotiation," + " disabled\n", pAC->dev->name); + pAC->GIni.GP[1].PFlowCtrlMode = SK_FLOW_MODE_NONE; + } + + MSMode = SK_MS_MODE_AUTO; /* default: do auto select */ + if (Role_B != NULL && pAC->IndexIndex] != NULL) { + if (strcmp(Role_B[pAC->Index],"")==0) { + } + else if (strcmp(Role_B[pAC->Index],"Auto")==0) { + MSMode = SK_MS_MODE_AUTO; + } + else if (strcmp(Role_B[pAC->Index],"Master")==0) { + MSMode = SK_MS_MODE_MASTER; + } + else if (strcmp(Role_B[pAC->Index],"Slave")==0) { + MSMode = SK_MS_MODE_SLAVE; + } + else printk("%s: Illegal value for Role_B\n", + pAC->dev->name); + } + pAC->GIni.GP[1].PMSMode = MSMode; + + + /* settings for both ports */ + pAC->ActivePort = 0; + if (PrefPort != NULL && pAC->IndexIndex] != NULL) { + if (strcmp(PrefPort[pAC->Index],"") == 0) { /* Auto */ + pAC->ActivePort = 0; + pAC->Rlmt.MacPreferred = -1; /* auto */ + pAC->Rlmt.PrefPort = 0; + } + else if (strcmp(PrefPort[pAC->Index],"A") == 0) { + /* + * do not set ActivePort here, thus a port + * switch is issued after net up. + */ + Port = 0; + pAC->Rlmt.MacPreferred = Port; + pAC->Rlmt.PrefPort = Port; + } + else if (strcmp(PrefPort[pAC->Index],"B") == 0) { + /* + * do not set ActivePort here, thus a port + * switch is issued after net up. + */ + Port = 1; + pAC->Rlmt.MacPreferred = Port; + pAC->Rlmt.PrefPort = Port; + } + else printk("%s: Illegal value for PrefPort\n", + pAC->dev->name); + } + + if (RlmtMode != NULL && pAC->IndexIndex] != NULL) { + if (strcmp(RlmtMode[pAC->Index], "") == 0) { + pAC->RlmtMode = 0; + } + else if (strcmp(RlmtMode[pAC->Index], "CheckLinkState") == 0) { + pAC->RlmtMode = SK_RLMT_CHECK_LINK; + } + else if (strcmp(RlmtMode[pAC->Index], "CheckLocalPort") == 0) { + pAC->RlmtMode = SK_RLMT_CHECK_LINK | + SK_RLMT_CHECK_LOC_LINK; + } + else if (strcmp(RlmtMode[pAC->Index], "CheckSeg") == 0) { + pAC->RlmtMode = SK_RLMT_CHECK_LINK | + SK_RLMT_CHECK_LOC_LINK | + SK_RLMT_CHECK_SEG; + } + else { + printk("%s: Illegal value for" + " RlmtMode, using default\n", pAC->dev->name); + pAC->RlmtMode = 0; + } + } + else { + pAC->RlmtMode = 0; + } +} /* GetConfiguration */ + + +/***************************************************************************** + * + * ProductStr - return a adapter identification string from vpd + * + * Description: + * This function reads the product name string from the vpd area + * and puts it the field pAC->DeviceString. + * + * Returns: N/A + */ +static void ProductStr( +SK_AC *pAC /* pointer to adapter context */ +) +{ +int StrLen = 80; /* length of the string, defined in SK_AC */ +char Keyword[] = VPD_NAME; /* vpd productname identifier */ +int ReturnCode; /* return code from vpd_read */ +unsigned int Flags; + + spin_lock_irqsave(&pAC->SlowPathLock, Flags); + ReturnCode = VpdRead(pAC, pAC->IoBase, Keyword, pAC->DeviceStr, + &StrLen); + spin_unlock_irqrestore(&pAC->SlowPathLock, Flags); + if (ReturnCode != 0) { + /* there was an error reading the vpd data */ + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_ERROR, + ("Error reading VPD data: %d\n", ReturnCode)); + pAC->DeviceStr[0] = '\0'; + } +} /* ProductStr */ + + + + +/****************************************************************************/ +/* functions for common modules *********************************************/ +/****************************************************************************/ + + +/***************************************************************************** + * + * SkDrvAllocRlmtMbuf - allocate an RLMT mbuf + * + * Description: + * This routine returns an RLMT mbuf or NULL. The RLMT Mbuf structure + * is embedded into a socket buff data area. + * + * Context: + * runtime + * + * Returns: + * NULL or pointer to Mbuf. + */ +SK_MBUF *SkDrvAllocRlmtMbuf( +SK_AC *pAC, /* pointer to adapter context */ +SK_IOC IoC, /* the IO-context */ +unsigned BufferSize) /* size of the requested buffer */ +{ +SK_MBUF *pRlmtMbuf; /* pointer to a new rlmt-mbuf structure */ +struct sk_buff *pMsgBlock; /* pointer to a new message block */ + + pMsgBlock = alloc_skb(BufferSize + sizeof(SK_MBUF), GFP_ATOMIC); + if (pMsgBlock == NULL) { + return (NULL); + } + pRlmtMbuf = (SK_MBUF*) pMsgBlock->data; + skb_reserve(pMsgBlock, sizeof(SK_MBUF)); + pRlmtMbuf->pNext = NULL; + pRlmtMbuf->pOs = pMsgBlock; + pRlmtMbuf->pData = pMsgBlock->data; /* Data buffer. */ + pRlmtMbuf->Size = BufferSize; /* Data buffer size. */ + pRlmtMbuf->Length = 0; /* Length of packet (<= Size). */ + return (pRlmtMbuf); + +} /* SkDrvAllocRlmtMbuf */ + + +/***************************************************************************** + * + * SkDrvFreeRlmtMbuf - free an RLMT mbuf + * + * Description: + * This routine frees one or more RLMT mbuf(s). + * + * Context: + * runtime + * + * Returns: + * Nothing + */ +void SkDrvFreeRlmtMbuf( +SK_AC *pAC, /* pointer to adapter context */ +SK_IOC IoC, /* the IO-context */ +SK_MBUF *pMbuf) /* size of the requested buffer */ +{ +SK_MBUF *pFreeMbuf; +SK_MBUF *pNextMbuf; + + pFreeMbuf = pMbuf; + do { + pNextMbuf = pFreeMbuf->pNext; + DEV_KFREE_SKB(pFreeMbuf->pOs); + pFreeMbuf = pNextMbuf; + } while ( pFreeMbuf != NULL ); +} /* SkDrvFreeRlmtMbuf */ + + +/***************************************************************************** + * + * SkOsGetTime - provide a time value + * + * Description: + * This routine provides a time value. The unit is 1/HZ (defined by Linux). + * It is not used for absolute time, but only for time differences. + * + * + * Returns: + * Time value + */ +SK_U64 SkOsGetTime(SK_AC *pAC) +{ + return jiffies; +} /* SkOsGetTime */ + + +/***************************************************************************** + * + * SkPciReadCfgDWord - read a 32 bit value from pci config space + * + * Description: + * This routine reads a 32 bit value from the pci configuration + * space. + * + * Returns: + * 0 - indicate everything worked ok. + * != 0 - error indication + */ +int SkPciReadCfgDWord( +SK_AC *pAC, /* Adapter Control structure pointer */ +int PciAddr, /* PCI register address */ +SK_U32 *pVal) /* pointer to store the read value */ +{ + pci_read_config_dword(&pAC->PciDev, PciAddr, pVal); + return(0); +} /* SkPciReadCfgDWord */ + + +/***************************************************************************** + * + * SkPciReadCfgWord - read a 16 bit value from pci config space + * + * Description: + * This routine reads a 16 bit value from the pci configuration + * space. + * + * Returns: + * 0 - indicate everything worked ok. + * != 0 - error indication + */ +int SkPciReadCfgWord( +SK_AC *pAC, /* Adapter Control structure pointer */ +int PciAddr, /* PCI register address */ +SK_U16 *pVal) /* pointer to store the read value */ +{ + pci_read_config_word(&pAC->PciDev, PciAddr, pVal); + return(0); +} /* SkPciReadCfgWord */ + + +/***************************************************************************** + * + * SkPciReadCfgByte - read a 8 bit value from pci config space + * + * Description: + * This routine reads a 8 bit value from the pci configuration + * space. + * + * Returns: + * 0 - indicate everything worked ok. + * != 0 - error indication + */ +int SkPciReadCfgByte( +SK_AC *pAC, /* Adapter Control structure pointer */ +int PciAddr, /* PCI register address */ +SK_U8 *pVal) /* pointer to store the read value */ +{ + pci_read_config_byte(&pAC->PciDev, PciAddr, pVal); + return(0); +} /* SkPciReadCfgByte */ + + +/***************************************************************************** + * + * SkPciWriteCfgDWord - write a 32 bit value to pci config space + * + * Description: + * This routine writes a 32 bit value to the pci configuration + * space. + * + * Returns: + * 0 - indicate everything worked ok. + * != 0 - error indication + */ +int SkPciWriteCfgDWord( +SK_AC *pAC, /* Adapter Control structure pointer */ +int PciAddr, /* PCI register address */ +SK_U32 Val) /* pointer to store the read value */ +{ + pci_write_config_dword(&pAC->PciDev, PciAddr, Val); + return(0); +} /* SkPciWriteCfgDWord */ + + +/***************************************************************************** + * + * SkPciWriteCfgWord - write a 16 bit value to pci config space + * + * Description: + * This routine writes a 16 bit value to the pci configuration + * space. The flag PciConfigUp indicates whether the config space + * is accesible or must be set up first. + * + * Returns: + * 0 - indicate everything worked ok. + * != 0 - error indication + */ +int SkPciWriteCfgWord( +SK_AC *pAC, /* Adapter Control structure pointer */ +int PciAddr, /* PCI register address */ +SK_U16 Val) /* pointer to store the read value */ +{ + pci_write_config_word(&pAC->PciDev, PciAddr, Val); + return(0); +} /* SkPciWriteCfgWord */ + + +/***************************************************************************** + * + * SkPciWriteCfgWord - write a 8 bit value to pci config space + * + * Description: + * This routine writes a 8 bit value to the pci configuration + * space. The flag PciConfigUp indicates whether the config space + * is accesible or must be set up first. + * + * Returns: + * 0 - indicate everything worked ok. + * != 0 - error indication + */ +int SkPciWriteCfgByte( +SK_AC *pAC, /* Adapter Control structure pointer */ +int PciAddr, /* PCI register address */ +SK_U8 Val) /* pointer to store the read value */ +{ + pci_write_config_byte(&pAC->PciDev, PciAddr, Val); + return(0); +} /* SkPciWriteCfgByte */ + + +/***************************************************************************** + * + * SkDrvEvent - handle driver events + * + * Description: + * This function handles events from all modules directed to the driver + * + * Context: + * Is called under protection of slow path lock. + * + * Returns: + * 0 if everything ok + * < 0 on error + * + */ +int SkDrvEvent( +SK_AC *pAC, /* pointer to adapter context */ +SK_IOC IoC, /* io-context */ +SK_U32 Event, /* event-id */ +SK_EVPARA Param) /* event-parameter */ +{ +SK_MBUF *pRlmtMbuf; /* pointer to a rlmt-mbuf structure */ +struct sk_buff *pMsg; /* pointer to a message block */ +int FromPort; /* the port from which we switch away */ +int ToPort; /* the port we switch to */ +SK_EVPARA NewPara; /* parameter for further events */ +int Stat; +unsigned int Flags; + + switch (Event) { + case SK_DRV_ADAP_FAIL: + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_EVENT, + ("ADAPTER FAIL EVENT\n")); + printk("%s: Adapter failed.\n", pAC->dev->name); + /* disable interrupts */ + SK_OUT32(pAC->IoBase, B0_IMSK, 0); + /* cgoos */ + break; + case SK_DRV_PORT_FAIL: + FromPort = Param.Para32[0]; + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_EVENT, + ("PORT FAIL EVENT, Port: %d\n", FromPort)); + if (FromPort == 0) { + printk("%s: Port A failed.\n", pAC->dev->name); + } else { + printk("%s: Port B failed.\n", pAC->dev->name); + } + /* cgoos */ + break; + case SK_DRV_PORT_RESET: /* SK_U32 PortIdx */ + /* action list 4 */ + FromPort = Param.Para32[0]; + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_EVENT, + ("PORT RESET EVENT, Port: %d ", FromPort)); + NewPara.Para64 = FromPort; + SkPnmiEvent(pAC, IoC, SK_PNMI_EVT_XMAC_RESET, NewPara); + spin_lock_irqsave( + &pAC->TxPort[FromPort][TX_PRIO_LOW].TxDesRingLock, + Flags); + SkGeStopPort(pAC, IoC, FromPort, SK_STOP_ALL, SK_HARD_RST); + spin_unlock_irqrestore( + &pAC->TxPort[FromPort][TX_PRIO_LOW].TxDesRingLock, + Flags); + + /* clear rx ring from received frames */ + ReceiveIrq(pAC, &pAC->RxPort[FromPort]); + + ClearTxRing(pAC, &pAC->TxPort[FromPort][TX_PRIO_LOW]); + spin_lock_irqsave( + &pAC->TxPort[FromPort][TX_PRIO_LOW].TxDesRingLock, + Flags); + SkGeInitPort(pAC, IoC, FromPort); + SkAddrMcUpdate(pAC,IoC, FromPort); + PortReInitBmu(pAC, FromPort); + SkGePollTxD(pAC, IoC, FromPort, SK_TRUE); + ClearAndStartRx(pAC, FromPort); + spin_unlock_irqrestore( + &pAC->TxPort[FromPort][TX_PRIO_LOW].TxDesRingLock, + Flags); + break; + case SK_DRV_NET_UP: /* SK_U32 PortIdx */ + /* action list 5 */ + FromPort = Param.Para32[0]; + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_EVENT, + ("NET UP EVENT, Port: %d ", Param.Para32[0])); + printk("%s: network connection up using" + " port %c\n", pAC->dev->name, 'A'+Param.Para32[0]); + printk(" speed: 1000\n"); + Stat = pAC->GIni.GP[FromPort].PLinkModeStatus; + if (Stat == SK_LMODE_STAT_AUTOHALF || + Stat == SK_LMODE_STAT_AUTOFULL) { + printk(" autonegotiation: yes\n"); + } + else { + printk(" autonegotiation: no\n"); + } + if (Stat == SK_LMODE_STAT_AUTOHALF || + Stat == SK_LMODE_STAT_HALF) { + printk(" duplex mode: half\n"); + } + else { + printk(" duplex mode: full\n"); + } + Stat = pAC->GIni.GP[FromPort].PFlowCtrlStatus; + if (Stat == SK_FLOW_STAT_REM_SEND ) { + printk(" flowctrl: remote send\n"); + } + else if (Stat == SK_FLOW_STAT_LOC_SEND ){ + printk(" flowctrl: local send\n"); + } + else if (Stat == SK_FLOW_STAT_SYMMETRIC ){ + printk(" flowctrl: symmetric\n"); + } + else { + printk(" flowctrl: none\n"); + } + if (pAC->GIni.GP[FromPort].PhyType != SK_PHY_XMAC) { + Stat = pAC->GIni.GP[FromPort].PMSStatus; + if (Stat == SK_MS_STAT_MASTER ) { + printk(" role: master\n"); + } + else if (Stat == SK_MS_STAT_SLAVE ) { + printk(" role: slave\n"); + } + else { + printk(" role: ???\n"); + } + } + + if (Param.Para32[0] != pAC->ActivePort) { + NewPara.Para32[0] = pAC->ActivePort; + NewPara.Para32[1] = Param.Para32[0]; + SkEventQueue(pAC, SKGE_DRV, SK_DRV_SWITCH_INTERN, + NewPara); + } + break; + case SK_DRV_NET_DOWN: /* SK_U32 Reason */ + /* action list 7 */ + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_EVENT, + ("NET DOWN EVENT ")); + printk("%s: network connection down\n", pAC->dev->name); + break; + case SK_DRV_SWITCH_HARD: /* SK_U32 FromPortIdx SK_U32 ToPortIdx */ + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_EVENT, + ("PORT SWITCH HARD ")); + case SK_DRV_SWITCH_SOFT: /* SK_U32 FromPortIdx SK_U32 ToPortIdx */ + /* action list 6 */ + printk("%s: switching to port %c\n", pAC->dev->name, + 'A'+Param.Para32[1]); + case SK_DRV_SWITCH_INTERN: /* SK_U32 FromPortIdx SK_U32 ToPortIdx */ + FromPort = Param.Para32[0]; + ToPort = Param.Para32[1]; + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_EVENT, + ("PORT SWITCH EVENT, From: %d To: %d (Pref %d) ", + FromPort, ToPort, pAC->Rlmt.PrefPort)); + NewPara.Para64 = FromPort; + SkPnmiEvent(pAC, IoC, SK_PNMI_EVT_XMAC_RESET, NewPara); + NewPara.Para64 = ToPort; + SkPnmiEvent(pAC, IoC, SK_PNMI_EVT_XMAC_RESET, NewPara); + spin_lock_irqsave( + &pAC->TxPort[FromPort][TX_PRIO_LOW].TxDesRingLock, + Flags); + spin_lock_irqsave( + &pAC->TxPort[ToPort][TX_PRIO_LOW].TxDesRingLock, Flags); + SkGeStopPort(pAC, IoC, FromPort, SK_STOP_ALL, SK_SOFT_RST); + SkGeStopPort(pAC, IoC, ToPort, SK_STOP_ALL, SK_SOFT_RST); + spin_unlock_irqrestore( + &pAC->TxPort[ToPort][TX_PRIO_LOW].TxDesRingLock, Flags); + spin_unlock_irqrestore( + &pAC->TxPort[FromPort][TX_PRIO_LOW].TxDesRingLock, + Flags); + + ReceiveIrq(pAC, &pAC->RxPort[FromPort]); /* clears rx ring */ + ReceiveIrq(pAC, &pAC->RxPort[ToPort]); /* clears rx ring */ + + ClearTxRing(pAC, &pAC->TxPort[FromPort][TX_PRIO_LOW]); + ClearTxRing(pAC, &pAC->TxPort[ToPort][TX_PRIO_LOW]); + spin_lock_irqsave( + &pAC->TxPort[FromPort][TX_PRIO_LOW].TxDesRingLock, + Flags); + spin_lock_irqsave( + &pAC->TxPort[ToPort][TX_PRIO_LOW].TxDesRingLock, Flags); + pAC->ActivePort = ToPort; + SetQueueSizes(pAC); + SkGeInitPort(pAC, IoC, FromPort); + SkGeInitPort(pAC, IoC, ToPort); + if (Event == SK_DRV_SWITCH_SOFT) { + SkXmRxTxEnable(pAC, IoC, FromPort); + } + SkXmRxTxEnable(pAC, IoC, ToPort); + SkAddrSwap(pAC, IoC, FromPort, ToPort); + SkAddrMcUpdate(pAC, IoC, FromPort); + SkAddrMcUpdate(pAC, IoC, ToPort); + PortReInitBmu(pAC, FromPort); + PortReInitBmu(pAC, ToPort); + SkGePollTxD(pAC, IoC, FromPort, SK_TRUE); + SkGePollTxD(pAC, IoC, ToPort, SK_TRUE); + ClearAndStartRx(pAC, FromPort); + ClearAndStartRx(pAC, ToPort); + spin_unlock_irqrestore( + &pAC->TxPort[ToPort][TX_PRIO_LOW].TxDesRingLock, Flags); + spin_unlock_irqrestore( + &pAC->TxPort[FromPort][TX_PRIO_LOW].TxDesRingLock, + Flags); + break; + case SK_DRV_RLMT_SEND: /* SK_MBUF *pMb */ + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_EVENT, + ("RLS ")); + pRlmtMbuf = (SK_MBUF*) Param.pParaPtr; + pMsg = (struct sk_buff*) pRlmtMbuf->pOs; + skb_put(pMsg, pRlmtMbuf->Length); + XmitFrame(pAC, &pAC->TxPort[pRlmtMbuf->PortIdx][TX_PRIO_LOW], + pMsg); + break; + default: + break; + } + SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_EVENT, + ("END EVENT ")); + + return (0); +} /* SkDrvEvent */ + + +/***************************************************************************** + * + * SkErrorLog - log errors + * + * Description: + * This function logs errors to the system buffer and to the console + * + * Returns: + * 0 if everything ok + * < 0 on error + * + */ +void SkErrorLog( +SK_AC *pAC, +int ErrClass, +int ErrNum, +char *pErrorMsg) +{ +char ClassStr[80]; + + switch (ErrClass) { + case SK_ERRCL_OTHER: + strcpy(ClassStr, "Other error"); + break; + case SK_ERRCL_CONFIG: + strcpy(ClassStr, "Configuration error"); + break; + case SK_ERRCL_INIT: + strcpy(ClassStr, "Initialization error"); + break; + case SK_ERRCL_NORES: + strcpy(ClassStr, "Out of resources error"); + break; + case SK_ERRCL_SW: + strcpy(ClassStr, "internal Software error"); + break; + case SK_ERRCL_HW: + strcpy(ClassStr, "Hardware failure"); + break; + case SK_ERRCL_COMM: + strcpy(ClassStr, "Communication error"); + break; + } + printk(KERN_INFO "%s: -- ERROR --\n Class: %s\n" + " Nr: 0x%x\n Msg: %s\n", pAC->dev->name, + ClassStr, ErrNum, pErrorMsg); + +} /* SkErrorLog */ + +#ifdef DEBUG /***************************************************************/ +/* "debug only" section *****************************************************/ +/****************************************************************************/ + + +/***************************************************************************** + * + * DumpMsg - print a frame + * + * Description: + * This function prints frames to the system logfile/to the console. + * + * Returns: N/A + * + */ +static void DumpMsg(struct sk_buff *skb, char *str) +{ + int msglen; + + if (skb == NULL) { + printk("DumpMsg(): NULL-Message\n"); + return; + } + + if (skb->data == NULL) { + printk("DumpMsg(): Message empty\n"); + return; + } + + msglen = skb->len; + if (msglen > 64) + msglen = 64; + + printk("--- Begin of message from %s , len %d (from %d) ----\n", str, msglen, skb->len); + + DumpData((char *)skb->data, msglen); + + printk("------- End of message ---------\n"); +} /* DumpMsg */ + + + +/***************************************************************************** + * + * DumpData - print a data area + * + * Description: + * This function prints a area of data to the system logfile/to the + * console. + * + * Returns: N/A + * + */ +static void DumpData(char *p, int size) +{ +register int i; +int haddr, addr; +char hex_buffer[180]; +char asc_buffer[180]; +char HEXCHAR[] = "0123456789ABCDEF"; + + addr = 0; + haddr = 0; + hex_buffer[0] = 0; + asc_buffer[0] = 0; + for (i=0; i < size; ) { + if (*p >= '0' && *p <='z') + asc_buffer[addr] = *p; + else + asc_buffer[addr] = '.'; + addr++; + asc_buffer[addr] = 0; + hex_buffer[haddr] = HEXCHAR[(*p & 0xf0) >> 4]; + haddr++; + hex_buffer[haddr] = HEXCHAR[*p & 0x0f]; + haddr++; + hex_buffer[haddr] = ' '; + haddr++; + hex_buffer[haddr] = 0; + p++; + i++; + if (i%16 == 0) { + printk("%s %s\n", hex_buffer, asc_buffer); + addr = 0; + haddr = 0; + } + } +} /* DumpData */ + + +/***************************************************************************** + * + * DumpLong - print a data area as long values + * + * Description: + * This function prints a area of data to the system logfile/to the + * console. + * + * Returns: N/A + * + */ +static void DumpLong(char *pc, int size) +{ +register int i; +int haddr, addr; +char hex_buffer[180]; +char asc_buffer[180]; +char HEXCHAR[] = "0123456789ABCDEF"; +long *p; +int l; + + addr = 0; + haddr = 0; + hex_buffer[0] = 0; + asc_buffer[0] = 0; + p = (long*) pc; + for (i=0; i < size; ) { + l = (long) *p; + hex_buffer[haddr] = HEXCHAR[(l >> 28) & 0xf]; + haddr++; + hex_buffer[haddr] = HEXCHAR[(l >> 24) & 0xf]; + haddr++; + hex_buffer[haddr] = HEXCHAR[(l >> 20) & 0xf]; + haddr++; + hex_buffer[haddr] = HEXCHAR[(l >> 16) & 0xf]; + haddr++; + hex_buffer[haddr] = HEXCHAR[(l >> 12) & 0xf]; + haddr++; + hex_buffer[haddr] = HEXCHAR[(l >> 8) & 0xf]; + haddr++; + hex_buffer[haddr] = HEXCHAR[(l >> 4) & 0xf]; + haddr++; + hex_buffer[haddr] = HEXCHAR[l & 0x0f]; + haddr++; + hex_buffer[haddr] = ' '; + haddr++; + hex_buffer[haddr] = 0; + p++; + i++; + if (i%8 == 0) { + printk("%4x %s\n", (i-8)*4, hex_buffer); + haddr = 0; + } + } + printk("------------------------\n"); +} /* DumpLong */ + +#endif /* DEBUG */ + +/* + * Local variables: + * compile-command: "make" + * End: + */ + diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/skgehwt.c linux/drivers/net/sk98lin/skgehwt.c --- v2.2.13/linux/drivers/net/sk98lin/skgehwt.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/skgehwt.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,213 @@ +/****************************************************************************** + * + * Name: skgehwt.c + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.12 $ + * Date: $Date: 1998/10/15 15:11:34 $ + * Purpose: Hardware Timer. + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skge.c" for further information. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: skgehwt.c,v $ + * Revision 1.12 1998/10/15 15:11:34 gklug + * fix: ID_sccs to SysKonnectFileId + * + * Revision 1.11 1998/10/08 15:27:51 gklug + * chg: correction factor is host clock dependent + * + * Revision 1.10 1998/09/15 14:18:31 cgoos + * Changed more BOOLEANs to SK_xxx + * + * Revision 1.9 1998/09/15 14:16:06 cgoos + * Changed line 107: FALSE to SK_FALSE + * + * Revision 1.8 1998/08/24 13:04:44 gklug + * fix: typo + * + * Revision 1.7 1998/08/19 09:50:49 gklug + * fix: remove struct keyword from c-code (see CCC) add typedefs + * + * Revision 1.6 1998/08/17 09:59:02 gklug + * fix: typos + * + * Revision 1.5 1998/08/14 07:09:10 gklug + * fix: chg pAc -> pAC + * + * Revision 1.4 1998/08/10 14:14:52 gklug + * rmv: unneccessary SK_ADDR macro + * + * Revision 1.3 1998/08/07 12:53:44 gklug + * fix: first compiled version + * + * Revision 1.2 1998/08/07 09:19:29 gklug + * adapt functions to the C coding conventions + * rmv unneccessary functions. + * + * Revision 1.1 1998/08/05 11:28:36 gklug + * first version: adapted from SMT/FDDI + * + * + * + * + ******************************************************************************/ + + +/* + Event queue and dispatcher +*/ +static const char SysKonnectFileId[] = + "$Header: /usr56/projects/ge/schedule/skgehwt.c,v 1.12 1998/10/15 15:11:34 gklug Exp $" ; + +#include "h/skdrv1st.h" /* Driver Specific Definitions */ +#include "h/skdrv2nd.h" /* Adapter Control- and Driver specific Def. */ + +#ifdef __C2MAN__ +/* + Hardware Timer function queue management. + + General Description: + + */ +intro() +{} +#endif + +/* + * Prototypes of local functions. + */ +#define SK_HWT_MAX (65000) + +/* correction factor */ +#define SK_HWT_FAC (1000 * (SK_U32)pAC->GIni.GIHstClkFact / 100) + +/* + * Initialize hardware timer. + * + * Must be called during init level 1. + */ +void SkHwtInit( +SK_AC *pAC, /* Adapters context */ +SK_IOC Ioc) /* IoContext */ +{ + pAC->Hwt.TStart = 0 ; + pAC->Hwt.TStop = 0 ; + pAC->Hwt.TActive = SK_FALSE ; + + SkHwtStop(pAC,Ioc) ; +} + +/* + * + * Start hardware timer (clock ticks are 16us). + * + */ +void SkHwtStart( +SK_AC *pAC, /* Adapters context */ +SK_IOC Ioc, /* IoContext */ +SK_U32 Time) /* Time in units of 16us to load the timer with. */ +{ + SK_U32 Cnt ; + + if (Time > SK_HWT_MAX) + Time = SK_HWT_MAX ; + + pAC->Hwt.TStart = Time ; + pAC->Hwt.TStop = 0L ; + + Cnt = Time ; + + /* + * if time < 16 us + * time = 16 us + */ + if (!Cnt) { + Cnt++ ; + } + + SK_OUT32(Ioc, B2_TI_INI, Cnt * SK_HWT_FAC) ; + SK_OUT16(Ioc, B2_TI_CRTL, TIM_START) ; /* Start timer. */ + + pAC->Hwt.TActive = SK_TRUE ; +} + +/* + * Stop hardware timer. + * and clear the timer IRQ + */ +void SkHwtStop( +SK_AC *pAC, /* Adapters context */ +SK_IOC Ioc) /* IoContext */ +{ + SK_OUT16(Ioc, B2_TI_CRTL, TIM_STOP) ; + SK_OUT16(Ioc, B2_TI_CRTL, TIM_CLR_IRQ) ; + + pAC->Hwt.TActive = SK_FALSE ; +} + + +/* + * Stop hardware timer and read time elapsed since last start. + * + * returns + * The elapsed time since last start in units of 16us. + * + */ +SK_U32 SkHwtRead( +SK_AC *pAC, /* Adapters context */ +SK_IOC Ioc) /* IoContext */ +{ + SK_U32 TRead ; + SK_U32 IStatus ; + + if (pAC->Hwt.TActive) { + SkHwtStop(pAC,Ioc) ; + + SK_IN32(Ioc, B2_TI_VAL, &TRead); + TRead /= SK_HWT_FAC; + + SK_IN32(Ioc, B0_ISRC, &IStatus); + + /* Check if timer expired (or wraparound). */ + if ((TRead > pAC->Hwt.TStart) || (IStatus & IS_TIMINT)) { + SkHwtStop(pAC,Ioc) ; + pAC->Hwt.TStop = pAC->Hwt.TStart ; + } else { + pAC->Hwt.TStop = pAC->Hwt.TStart - TRead ; + } + } + return (pAC->Hwt.TStop) ; +} + +/* + * interrupt source= timer + */ +void SkHwtIsr( +SK_AC *pAC, /* Adapters context */ +SK_IOC Ioc) /* IoContext */ +{ + SkHwtStop(pAC,Ioc); + pAC->Hwt.TStop = pAC->Hwt.TStart; + SkTimerDone(pAC,Ioc) ; +} + +/* End of file */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/skgeinit.c linux/drivers/net/sk98lin/skgeinit.c --- v2.2.13/linux/drivers/net/sk98lin/skgeinit.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/skgeinit.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,1847 @@ +/****************************************************************************** + * + * Name: skgeinit.c + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.54 $ + * Date: $Date: 1999/10/26 07:32:54 $ + * Purpose: Contains functions to initialize the GE HW + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skge.c" for further information. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: skgeinit.c,v $ + * Revision 1.54 1999/10/26 07:32:54 malthoff + * Initialize PHWLinkUp with SK_FALSE. Required for Diagnostics. + * + * Revision 1.53 1999/08/12 19:13:50 malthoff + * Fix for 1000BT. Do not owerwrite XM_MMU_CMD when + * disabling receiver and transmitter. Other bits + * may be lost. + * + * Revision 1.52 1999/07/01 09:29:54 gklug + * fix: DoInitRamQueue needs pAC + * + * Revision 1.51 1999/07/01 08:42:21 gklug + * chg: use Store & forward for RAM buffer when Jumbos are used + * + * Revision 1.50 1999/05/27 13:19:38 cgoos + * Added Tx PCI watermark initialization. + * Removed Tx RAM queue Store & Forward setting. + * + * Revision 1.49 1999/05/20 14:32:45 malthoff + * SkGeLinkLED() is completly removed now. + * + * Revision 1.48 1999/05/19 07:28:24 cgoos + * SkGeLinkLED no more available for drivers. + * Changes for 1000Base-T. + * + * Revision 1.47 1999/04/08 13:57:45 gklug + * add: Init of new port struct fiels PLinkResCt + * chg: StopPort Timer check + * + * Revision 1.46 1999/03/25 07:42:15 malthoff + * SkGeStopPort(): Add workaround for cache incoherency. + * Create error log entry, disable port, and + * exit loop if it does not terminate. + * Add XM_RX_LENERR_OK to the default value for the + * XMAC receive command register. + * + * Revision 1.45 1999/03/12 16:24:47 malthoff + * Remove PPollRxD and PPollTxD. + * Add check for GIPollTimerVal. + * + * Revision 1.44 1999/03/12 13:40:23 malthoff + * Fix: SkGeXmitLED(), SK_LED_TST mode does not work. + * Add: Jumbo frame support. + * Chg: Resolution of parameter IntTime in SkGeCfgSync(). + * + * Revision 1.43 1999/02/09 10:29:46 malthoff + * Bugfix: The previous modification again also for the second location. + * + * Revision 1.42 1999/02/09 09:35:16 malthoff + * Bugfix: The bits '66 MHz Capable' and 'NEWCAP are reset while + * clearing the error bits in the PCI status register. + * + * Revision 1.41 1999/01/18 13:07:02 malthoff + * Bugfix: Do not use CFG cycles after during Init- or Runtime, because + * they may not be available after Boottime. + * + * Revision 1.40 1999/01/11 12:40:49 malthoff + * Bug fix: PCI_STATUS: clearing error bits sets the UDF bit. + * + * Revision 1.39 1998/12/11 15:17:33 gklug + * chg: Init LipaAutoNeg with Unknown + * + * Revision 1.38 1998/12/10 11:02:57 malthoff + * Disable Error Log Message when calling SkGeInit(level 2) + * more than once. + * + * Revision 1.37 1998/12/07 12:18:25 gklug + * add: refinement of autosense mode: take into account the autoneg cap of LiPa + * + * Revision 1.36 1998/12/07 07:10:39 gklug + * fix: init values of LinkBroken/ Capabilities for management + * + * Revision 1.35 1998/12/02 10:56:20 gklug + * fix: do NOT init LoinkSync Counter. + * + * Revision 1.34 1998/12/01 10:53:21 gklug + * add: init of additional Counters for workaround + * + * Revision 1.33 1998/12/01 10:00:49 gklug + * add: init PIsave var in Port struct + * + * Revision 1.32 1998/11/26 14:50:40 gklug + * chg: Default is autosensing with AUTOFULL mode + * + * Revision 1.31 1998/11/25 15:36:16 gklug + * fix: do NOT stop LED Timer when port should be stoped + * + * Revision 1.30 1998/11/24 13:15:28 gklug + * add: Init PCkeckPar struct member + * + * Revision 1.29 1998/11/18 13:19:27 malthoff + * Disable packet arbiter timeouts on receive side. + * Use maximum timeout value for packet arbiter + * transmit timeouts. + * Add TestStopBit() function to handle stop RX/TX + * problem with active descriptor poll timers. + * Bug Fix: Descriptor Poll Timer not started, beacuse + * GIPollTimerVal was initilaized with 0. + * + * Revision 1.28 1998/11/13 14:24:26 malthoff + * Bug Fix: SkGeStopPort() may hang if a Packet Arbiter Timout + * is pending or occurs while waiting for TX_STOP and RX_STOP. + * The PA timeout is cleared now while waiting for TX- or RX_STOP. + * + * Revision 1.27 1998/11/02 11:04:36 malthoff + * fix the last fix + * + * Revision 1.26 1998/11/02 10:37:03 malthoff + * Fix: SkGePollTxD() enables always the synchronounous poll timer. + * + * Revision 1.25 1998/10/28 07:12:43 cgoos + * Fixed "LED_STOP" in SkGeLnkSyncCnt, "== SK_INIT_IO" in SkGeInit. + * Removed: Reset of RAM Interface in SkGeStopPort. + * + * Revision 1.24 1998/10/27 08:13:12 malthoff + * Remove temporary code. + * + * Revision 1.23 1998/10/26 07:45:03 malthoff + * Add Address Calculation Workaround: If the EPROM byte + * Id is 3, the address offset is 512 kB. + * Initialize default values for PLinkMode and PFlowCtrlMode. + * + * Revision 1.22 1998/10/22 09:46:47 gklug + * fix SysKonnectFileId typo + * + * Revision 1.21 1998/10/20 12:11:56 malthoff + * Don't dendy the Queue config if the size of the unused + * rx qeueu is zero. + * + * Revision 1.20 1998/10/19 07:27:58 malthoff + * SkGeInitRamIface() is public to be called by diagnostics. + * + * Revision 1.19 1998/10/16 13:33:45 malthoff + * Fix: enabling descriptor polling is not allowed until + * the descriptor addresses are set. Descriptor polling + * must be handled by the driver. + * + * Revision 1.18 1998/10/16 10:58:27 malthoff + * Remove temp. code for Diag prototype. + * Remove lint warning for dummy reads. + * Call SkGeLoadLnkSyncCnt() during SkGeInitPort(). + * + * Revision 1.17 1998/10/14 09:16:06 malthoff + * Change parameter LimCount and programming of + * the limit counter in SkGeCfgSync(). + * + * Revision 1.16 1998/10/13 09:21:16 malthoff + * Don't set XM_RX_SELF_RX in RxCmd Reg, because it's + * like a Loopback Mode in half duplex. + * + * Revision 1.15 1998/10/09 06:47:40 malthoff + * SkGeInitMacArb(): set recovery counters init value + * to zero although this counters are not uesd. + * Bug fix in Rx Upper/Lower Pause Threshold calculation. + * Add XM_RX_SELF_RX to RxCmd. + * + * Revision 1.14 1998/10/06 15:15:53 malthoff + * Make sure no pending IRQ is cleared in SkGeLoadLnkSyncCnt(). + * + * Revision 1.13 1998/10/06 14:09:36 malthoff + * Add SkGeLoadLnkSyncCnt(). Modify + * the 'port stopped' condition according + * to the current problem report. + * + * Revision 1.12 1998/10/05 08:17:21 malthoff + * Add functions: SkGePollRxD(), SkGePollTxD(), + * DoCalcAddr(), SkGeCheckQSize(), + * DoInitRamQueue(), and SkGeCfgSync(). + * Add coding for SkGeInitMacArb(), SkGeInitPktArb(), + * SkGeInitMacFifo(), SkGeInitRamBufs(), + * SkGeInitRamIface(), and SkGeInitBmu(). + * + * Revision 1.11 1998/09/29 08:26:29 malthoff + * bug fix: SkGeInit0() 'i' should be increment. + * + * Revision 1.10 1998/09/28 13:19:01 malthoff + * Coding time: Save the done work. + * Modify SkGeLinkLED(), add SkGeXmitLED(), + * define SkGeCheckQSize(), SkGeInitMacArb(), + * SkGeInitPktArb(), SkGeInitMacFifo(), + * SkGeInitRamBufs(), SkGeInitRamIface(), + * and SkGeInitBmu(). Do coding for SkGeStopPort(), + * SkGeInit1(), SkGeInit2(), and SkGeInit3(). + * Do coding for SkGeDinit() and SkGeInitPort(). + * + * Revision 1.9 1998/09/16 14:29:05 malthoff + * Some minor changes. + * + * Revision 1.8 1998/09/11 05:29:14 gklug + * add: init state of a port + * + * Revision 1.7 1998/09/04 09:26:25 malthoff + * Short temporary modification. + * + * Revision 1.6 1998/09/04 08:27:59 malthoff + * Remark the do-while in StopPort() because it never ends + * without a GE adapter. + * + * Revision 1.5 1998/09/03 14:05:45 malthoff + * Change comment for SkGeInitPort(). Do not + * repair the queue sizes if invalid. + * + * Revision 1.4 1998/09/03 10:03:19 malthoff + * Implement the new interface according to the + * reviewed interface specification. + * + * Revision 1.3 1998/08/19 09:11:25 gklug + * fix: struct are removed from c-source (see CCC) + * + * Revision 1.2 1998/07/28 12:33:58 malthoff + * Add 'IoC' parameter in function declaration and SK IO macros. + * + * Revision 1.1 1998/07/23 09:48:57 malthoff + * Creation. First dummy 'C' file. + * SkGeInit(Level 0) is card_start for ML. + * SkGeDeInit() is card_stop for ML. + * + * + ******************************************************************************/ + +#include "h/skdrv1st.h" +#include "h/xmac_ii.h" +#include "h/skdrv2nd.h" + +/* defines ********************************************************************/ + +/* defines for SkGeXmitLed() */ +#define XMIT_LED_INI 0 +#define XMIT_LED_CNT (RX_LED_VAL - RX_LED_INI) +#define XMIT_LED_CTRL (RX_LED_CTRL- RX_LED_INI) +#define XMIT_LED_TST (RX_LED_TST - RX_LED_INI) + +/* Queue Size units */ +#define QZ_UNITS 0x7 + +/* Types of RAM Buffer Queues */ +#define SK_RX_SRAM_Q 1 /* small receive queue */ +#define SK_RX_BRAM_Q 2 /* big receive queue */ +#define SK_TX_RAM_Q 3 /* small or big transmit queue */ + +/* typedefs *******************************************************************/ +/* global variables ***********************************************************/ + +/* local variables ************************************************************/ + +static const char SysKonnectFileId[] = + "@(#)$Id: skgeinit.c,v 1.54 1999/10/26 07:32:54 malthoff Exp $ (C) SK "; + +struct s_QOffTab { + int RxQOff; /* Receive Queue Address Offset */ + int XsQOff; /* Sync Tx Queue Address Offset */ + int XaQOff; /* Async Tx Queue Address Offset */ +}; +static struct s_QOffTab QOffTab[] = { + { Q_R1, Q_XS1, Q_XA1 }, { Q_R2, Q_XS2, Q_XA2 } +}; + + +/****************************************************************************** + * + * SkGePollRxD() - Enable/Disable Descriptor Polling of RxD Ring + * + * Description: + * Enable or disable the descriptor polling the receive descriptor + * ring (RxD) of port 'port'. + * The new configuration is *not* saved over any SkGeStopPort() and + * SkGeInitPort() calls. + * + * Returns: + * nothing + */ +void SkGePollRxD( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port, /* Port Index (MAC_1 + n) */ +SK_BOOL PollRxD) /* SK_TRUE (enable pol.), SK_FALSE (disable pol.) */ +{ + SK_GEPORT *pPrt; + + pPrt = &pAC->GIni.GP[Port]; + + if (PollRxD) { + SK_OUT32(IoC, Q_ADDR(pPrt->PRxQOff,Q_CSR), CSR_ENA_POL); + } + else { + SK_OUT32(IoC, Q_ADDR(pPrt->PRxQOff,Q_CSR), CSR_DIS_POL); + } +} + +/****************************************************************************** + * + * SkGePollTxD() - Enable/Disable Descriptor Polling of TxD Rings + * + * Description: + * Enable or disable the descriptor polling the transmit descriptor + * ring(s) (RxD) of port 'port'. + * The new configuration is *not* saved over any SkGeStopPort() and + * SkGeInitPort() calls. + * + * Returns: + * nothing + */ +void SkGePollTxD( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port, /* Port Index (MAC_1 + n) */ +SK_BOOL PollTxD) /* SK_TRUE (enable pol.), SK_FALSE (disable pol.) */ +{ + SK_GEPORT *pPrt; + SK_U32 DWord; + + pPrt = &pAC->GIni.GP[Port]; + + if (PollTxD) { + DWord = CSR_ENA_POL; + } + else { + DWord = CSR_DIS_POL; + } + + if (pPrt->PXSQSize != 0) { + SK_OUT32(IoC, Q_ADDR(pPrt->PXsQOff,Q_CSR), DWord); + } + if (pPrt->PXAQSize != 0) { + SK_OUT32(IoC, Q_ADDR(pPrt->PXaQOff,Q_CSR), DWord); + } +} + + +/****************************************************************************** + * + * SkGeYellowLED() - Switch the yellow LED on or off. + * + * Description: + * Switch the yellow LED on or off. + * + * Note: + * This function may be called any time after SkGeInit(Level 1). + * + * Returns: + * nothing + */ +void SkGeYellowLED( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int State) /* yellow LED state, 0 = OFF, 0 != ON */ +{ + if (State == 0) { + /* Switch yellow LED OFF */ + SK_OUT8(IoC, B0_LED, LED_STAT_OFF) ; + } + else { + /* Switch yellow LED ON */ + SK_OUT8(IoC, B0_LED, LED_STAT_ON) ; + } +} + +/****************************************************************************** + * + * SkGeXmitLED() - Modify the Operational Mode of a transmission LED. + * + * Description: + * The Rx or Tx LED which is specified by 'Led' will be + * enabled, disabled or switched on in test mode. + * + * Note: + * 'Led' must contain the address offset of the LEDs INI register. + * + * Usage: + * SkGeXmitLED(pAC, IoC, MR_ADDR(Port,TX_LED_INI), SK_LED_ENA); + * + * Returns: + * nothing + */ +void SkGeXmitLED( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Led, /* offset to the LED Init Value register */ +int Mode) /* Mode may be SK_LED_DIS, SK_LED_ENA, SK_LED_TST */ +{ + SK_U32 LedIni; + + switch (Mode) { + case SK_LED_ENA: + LedIni = SK_XMIT_DUR * (SK_U32)pAC->GIni.GIHstClkFact / 100; + SK_OUT32(IoC, Led+XMIT_LED_INI, LedIni); + SK_OUT8(IoC, Led+XMIT_LED_CTRL, LED_START); + break ; + case SK_LED_TST: + SK_OUT8(IoC, Led+XMIT_LED_TST, LED_T_ON); + SK_OUT32(IoC, Led+XMIT_LED_CNT, 100); + SK_OUT8(IoC, Led+XMIT_LED_CTRL, LED_START); + break ; + case SK_LED_DIS: + default: + /* + * Do NOT stop the LED Timer here. The LED might be + * in on state. But it needs to go off. + */ + SK_OUT32(IoC, Led+XMIT_LED_CNT, 0); + SK_OUT8(IoC, Led+XMIT_LED_TST, LED_T_OFF); + break ; + } + + /* + * 1000BT: The Transmit LED is driven by the PHY. + * But the default LED configuration is used for + * Level One and Broadcom PHYs. + * (Broadcom: It may be that PHY_B_PEC_EN_LTR has to be set.) + * (In this case it has to be added here. But we will see. XXX) + */ +} + +/****************************************************************************** + * + * DoCalcAddr() - Calculates the start and the end address of a queue. + * + * Description: + * This function calculates the start- end the end address + * of a queue. Afterwards the 'StartVal' is incremented to the + * next start position. + * If the port is already initialized the calculated values + * will be checked against the configured values and an + * error will be returned, if they are not equal. + * If the port is not initialized the values will be written to + * *StartAdr and *EndAddr. + * + * Returns: + * 0: success + * 1: configuration error + */ +static int DoCalcAddr( +SK_AC *pAC, /* adapter context */ +SK_GEPORT *pPrt, /* port index */ +int QuSize, /* size of the queue to configure in kB */ +SK_U32 *StartVal, /* start value for address calculation */ +SK_U32 *QuStartAddr, /* start addr to calculate */ +SK_U32 *QuEndAddr) /* end address to calculate */ +{ + SK_U32 EndVal; + SK_U32 NextStart; + int Rtv; + + Rtv = 0; + if (QuSize == 0) { + EndVal = *StartVal; + NextStart = EndVal; + } + else { + EndVal = *StartVal + ((SK_U32)QuSize * 1024) - 1; + NextStart = EndVal + 1; + } + + if (pPrt->PState >= SK_PRT_INIT) { + if (*StartVal != *QuStartAddr || EndVal != *QuEndAddr) { + Rtv = 1; + } + } + else { + *QuStartAddr = *StartVal; + *QuEndAddr = EndVal; + } + + *StartVal = NextStart; + return (Rtv); +} + + +/****************************************************************************** + * + * SkGeCheckQSize() - Checks the Adapters Queue Size Configuration + * + * Description: + * This function verifies the Queue Size Configuration specified + * in the variabels PRxQSize, PXSQSize, and PXAQSize of all + * used ports. + * This requirements must be fullfilled to have a valid configuration: + * - The size of all queues must not exceed GIRamSize. + * - The queue sizes must be specified in units of 8 kB. + * - The size of rx queues of available ports must not be + * smaller than 16kB. + * - The RAM start and end addresses must not be changed + * for ports which are already initialized. + * Furthermore SkGeCheckQSize() defines the Start and End + * Addresses of all ports and stores them into the HWAC port + * structure. + * + * Returns: + * 0: Queue Size Configuration valid + * 1: Queue Size Configuration invalid + */ +static int SkGeCheckQSize( +SK_AC *pAC, /* adapter context */ +int Port) /* port index */ +{ + SK_GEPORT *pPrt; + int UsedMem; + int i; + int Rtv; + int Rtv2; + SK_U32 StartAddr; + + UsedMem = 0; + Rtv = 0; + for (i = 0; i < pAC->GIni.GIMacsFound; i++) { + pPrt = &pAC->GIni.GP[i]; + + if (( pPrt->PRxQSize & QZ_UNITS) || + (pPrt->PXSQSize & QZ_UNITS) || + (pPrt->PXAQSize & QZ_UNITS)) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, + SKERR_HWI_E012, + SKERR_HWI_E012MSG); + Rtv = 1; + goto CheckQSizeEnd; + } + + UsedMem += pPrt->PRxQSize + pPrt->PXSQSize + pPrt->PXAQSize; + + if (i == Port && pPrt->PRxQSize < SK_MIN_RXQ_SIZE) { + SK_ERR_LOG(pAC, SK_ERRCL_SW, + SKERR_HWI_E011, + SKERR_HWI_E011MSG); + Rtv = 1; + goto CheckQSizeEnd; + } + } + if (UsedMem > pAC->GIni.GIRamSize) { + SK_ERR_LOG(pAC, SK_ERRCL_SW, SKERR_HWI_E012, SKERR_HWI_E012MSG); + Rtv = 1; + goto CheckQSizeEnd; + } + + /* Now start address calculation */ + StartAddr = pAC->GIni.GIRamOffs; + for (i = 0; i < pAC->GIni.GIMacsFound; i++) { + pPrt = &pAC->GIni.GP[i]; + + /* Calculate/Check values for the receive queue */ + Rtv2 = DoCalcAddr(pAC, pPrt, pPrt->PRxQSize, &StartAddr, + &pPrt->PRxQRamStart, &pPrt->PRxQRamEnd); + Rtv |= Rtv2; + + /* Calculate/Check values for the synchronous tx queue */ + Rtv2 = DoCalcAddr(pAC, pPrt, pPrt->PXSQSize, &StartAddr, + &pPrt->PXsQRamStart, &pPrt->PXsQRamEnd); + Rtv |= Rtv2; + + /* Calculate/Check values for the asynchronous tx queue */ + Rtv2 = DoCalcAddr(pAC, pPrt, pPrt->PXAQSize, &StartAddr, + &pPrt->PXaQRamStart, &pPrt->PXaQRamEnd); + Rtv |= Rtv2; + + if (Rtv) { + SK_ERR_LOG(pAC, SK_ERRCL_SW, + SKERR_HWI_E013, + SKERR_HWI_E013MSG); + break; + } + } + + +CheckQSizeEnd: + return (Rtv); +} + +/****************************************************************************** + * + * SkGeInitMacArb() - Initialize the MAC Arbiter + * + * Description: + * This function initializes the MAC Arbiter. + * It must not be called if there is still an + * initilaized or active port. + * + * Returns: + * nothing: + */ +static void SkGeInitMacArb( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC) /* IO context */ +{ + /* release local reset */ + SK_OUT16(IoC, B3_MA_TO_CTRL, MA_RST_CLR); + + /* configure timeout values */ + SK_OUT8(IoC, B3_MA_TOINI_RX1, SK_MAC_TO_53); + SK_OUT8(IoC, B3_MA_TOINI_RX2, SK_MAC_TO_53); + SK_OUT8(IoC, B3_MA_TOINI_TX1, SK_MAC_TO_53); + SK_OUT8(IoC, B3_MA_TOINI_TX2, SK_MAC_TO_53); + + SK_OUT8(IoC, B3_MA_RCINI_RX1, 0); + SK_OUT8(IoC, B3_MA_RCINI_RX2, 0); + SK_OUT8(IoC, B3_MA_RCINI_TX1, 0); + SK_OUT8(IoC, B3_MA_RCINI_TX2, 0); + + /* recovery values are needed for XMAC II Rev. B2 only */ + /* Fast Output Enable Mode was intended to use with Rev. B2, but now? */ + + /* + * There is not start or enable buttom to push, therefore + * the MAC arbiter is configured and enabled now. + */ +} + +/****************************************************************************** + * + * SkGeInitPktArb() - Initialize the Packet Arbiter + * + * Description: + * This function initializes the Packet Arbiter. + * It must not be called if there is still an + * initilaized or active port. + * + * Returns: + * nothing: + */ +static void SkGeInitPktArb( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC) /* IO context */ +{ + /* release local reset */ + SK_OUT16(IoC, B3_PA_CTRL, PA_RST_CLR); + + /* configure timeout values */ + SK_OUT16(IoC, B3_PA_TOINI_RX1, SK_PKT_TO_MAX); + SK_OUT16(IoC, B3_PA_TOINI_RX2, SK_PKT_TO_MAX); + SK_OUT16(IoC, B3_PA_TOINI_TX1, SK_PKT_TO_MAX); + SK_OUT16(IoC, B3_PA_TOINI_TX2, SK_PKT_TO_MAX); + + /* enable timeout timers if jumbo frames not used */ + if (pAC->GIni.GIPortUsage != SK_JUMBO_LINK) { + if (pAC->GIni.GIMacsFound == 1) { + SK_OUT16(IoC, B3_PA_CTRL, PA_ENA_TO_TX1); + } + else { + SK_OUT16(IoC, B3_PA_CTRL,(PA_ENA_TO_TX1|PA_ENA_TO_TX2)); + } + } +} + +/****************************************************************************** + * + * SkGeInitMacFifo() - Initialize the MAC FIFOs + * + * Description: + * Initialize all MAC FIFOs of the specified port + * + * Returns: + * nothing + */ +static void SkGeInitMacFifo( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port) /* Port Index (MAC_1 + n) */ +{ + /* + * For each FIFO: + * - release local reset + * - use default value for MAC FIFO size + * - setup defaults for the control register + * - enable the FIFO + */ + /* Configure RX MAC FIFO */ + SK_OUT8(IoC, MR_ADDR(Port, RX_MFF_CTRL2), MFF_RST_CLR); + SK_OUT16(IoC, MR_ADDR(Port, RX_MFF_CTRL1), MFF_RX_CTRL_DEF); + SK_OUT8(IoC, MR_ADDR(Port, RX_MFF_CTRL2), MFF_ENA_OP_MD); + + /* Configure TX MAC FIFO */ + SK_OUT8(IoC, MR_ADDR(Port, TX_MFF_CTRL2), MFF_RST_CLR); + SK_OUT16(IoC, MR_ADDR(Port, TX_MFF_CTRL1), MFF_TX_CTRL_DEF); + SK_OUT8(IoC, MR_ADDR(Port, TX_MFF_CTRL2), MFF_ENA_OP_MD); + + /* Enable frame flushing if jumbo frames used */ + if (pAC->GIni.GIPortUsage == SK_JUMBO_LINK) { + SK_OUT16(IoC, MR_ADDR(Port, RX_MFF_CTRL1), MFF_ENA_FLUSH); + } +} + +/****************************************************************************** + * + * SkGeLoadLnkSyncCnt() - Load the Link Sync Counter and starts counting + * + * Description: + * This function starts the Link Sync Counter of the specified + * port and enables the generation of an Link Sync IRQ. + * The Link Sync Counter may be used to detect an active link, + * if autonegotiation is not used. + * + * Note: + * o To ensure receiving the Link Sync Event the LinkSyncCounter + * should be initialized BEFORE clearing the XMACs reset! + * o Enable IS_LNK_SYNC_M1 and IS_LNK_SYNC_M2 after calling this + * function. + * + * Retruns: + * nothing + */ +void SkGeLoadLnkSyncCnt( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port, /* Port Index (MAC_1 + n) */ +SK_U32 CntVal) /* Counter value */ +{ + SK_U32 OrgIMsk; + SK_U32 NewIMsk; + SK_U32 ISrc; + SK_BOOL IrqPend; + + /* stop counter */ + SK_OUT8(IoC, MR_ADDR(Port,LNK_SYNC_CTRL), LED_STOP); + + /* + * ASIC problem: + * Each time starting the Link Sync Counter an IRQ is generated + * by the adapter. See problem report entry from 21.07.98 + * + * Workaround: Disable Link Sync IRQ and clear the unexpeced IRQ + * if no IRQ is already pending. + */ + IrqPend = SK_FALSE; + SK_IN32(IoC, B0_ISRC, &ISrc); + SK_IN32(IoC, B0_IMSK, &OrgIMsk); + if (Port == MAC_1) { + NewIMsk = OrgIMsk & ~IS_LNK_SYNC_M1; + if (ISrc & IS_LNK_SYNC_M1) { + IrqPend = SK_TRUE; + } + } + else { + NewIMsk = OrgIMsk & ~IS_LNK_SYNC_M2; + if (ISrc & IS_LNK_SYNC_M2) { + IrqPend = SK_TRUE; + } + } + if (!IrqPend) { + SK_OUT32(IoC, B0_IMSK, NewIMsk); + } + + /* load counter */ + SK_OUT32(IoC, MR_ADDR(Port,LNK_SYNC_INI), CntVal); + + /* start counter */ + SK_OUT8(IoC, MR_ADDR(Port,LNK_SYNC_CTRL), LED_START); + + if (!IrqPend) { + /* clear the unexpected IRQ, and restore the interrupt mask */ + SK_OUT8(IoC, MR_ADDR(Port,LNK_SYNC_CTRL), LED_CLR_IRQ); + SK_OUT32(IoC, B0_IMSK, OrgIMsk); + } +} + +/****************************************************************************** + * + * SkGeCfgSync() - Configure synchronous bandwidth for this port. + * + * Description: + * This function may be used to configure synchronous bandwidth + * to the specified port. This may be done any time after + * initializing the port. The configuration values are NOT saved + * in the HWAC port structure and will be overwritten any + * time when stopping and starting the port. + * Any values for the synchronous configuration will be ignored + * if the size of the synchronous queue is zero! + * + * The default configuration for the synchronous service is + * TXA_ENA_FSYNC. This means if the size of + * the synchronous queue is unequal zero but no specific + * synchronous bandwidth is configured, the synchronous queue + * will always have the 'unlimitted' transmit priority! + * + * This mode will be restored if the synchronous bandwidth is + * deallocated ('IntTime' = 0 and 'LimCount' = 0). + * + * Returns: + * 0: success + * 1: paramter configuration error + * 2: try to configure quality of service although no + * synchronous queue is configured + */ +int SkGeCfgSync( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port, /* Port Index (MAC_1 + n) */ +SK_U32 IntTime, /* Interval Timer Value in units of 8ns */ +SK_U32 LimCount, /* Number of bytes to transfer during IntTime */ +int SyncMode) /* Sync Mode: TXA_ENA_ALLOC | TXA_DIS_ALLOC | 0 */ +{ + int Rtv; + + Rtv = 0; + + /* check the parameters */ + if (LimCount > IntTime || + (LimCount == 0 && IntTime != 0) || + (LimCount !=0 && IntTime == 0)) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SKERR_HWI_E010, SKERR_HWI_E010MSG); + Rtv = 1; + goto CfgSyncEnd; + } + if (pAC->GIni.GP[Port].PXSQSize != 0) { + /* calculate register values */ + IntTime = (IntTime / 2) * pAC->GIni.GIHstClkFact / 100; + LimCount = LimCount / 8; + if (IntTime > TXA_MAX_VAL || LimCount > TXA_MAX_VAL) { + SK_ERR_LOG(pAC, SK_ERRCL_SW, SKERR_HWI_E010, + SKERR_HWI_E010MSG); + Rtv = 1; + goto CfgSyncEnd; + } + + /* + * - Enable 'Force Sync' to ensure the synchronous queue + * has the priority while configuring the new values. + * - Also 'disable alloc' to ensure the settings complies + * to the SyncMode parameter. + * - Disable 'Rate Control' to configure the new values. + * - write IntTime and Limcount + * - start 'Rate Control' and disable 'Force Sync' + * if Interval Timer or Limit Counter not zero. + */ + SK_OUT8(IoC, MR_ADDR(Port,TXA_CTRL), + TXA_ENA_FSYNC | TXA_DIS_ALLOC | TXA_STOP_RC); + SK_OUT32(IoC, MR_ADDR(Port,TXA_ITI_INI), IntTime); + SK_OUT32(IoC, MR_ADDR(Port,TXA_LIM_INI), LimCount); + SK_OUT8(IoC, MR_ADDR(Port,TXA_CTRL), + (SyncMode & (TXA_ENA_ALLOC|TXA_DIS_ALLOC))); + if (IntTime != 0 || LimCount != 0) { + SK_OUT8(IoC, MR_ADDR(Port,TXA_CTRL), + TXA_DIS_FSYNC|TXA_START_RC); + } + } + else { + SK_ERR_LOG(pAC, SK_ERRCL_SW, SKERR_HWI_E009, SKERR_HWI_E009MSG); + Rtv = 2; + } + +CfgSyncEnd: + return (Rtv); +} + +/****************************************************************************** + * + * DoInitRamQueue() - Initilaize the RAM Buffer Address of a single Queue + * + * Desccription: + * If the queue is used, enable and initilaize it. + * Make sure the queue is still reset, if it is not used. + * + * Returns: + * nothing + */ +static void DoInitRamQueue( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int QuIoOffs, /* Queue IO Address Offset */ +SK_U32 QuStartAddr, /* Queue Start Address */ +SK_U32 QuEndAddr, /* Queue End Address */ +int QuType) /* Queue Type (SK_RX_SRAM_Q|SK_RX_BRAM_Q|SK_TX_RAM_Q) */ +{ + SK_U32 RxUpThresVal; + SK_U32 RxLoThresVal; + + if (QuStartAddr != QuEndAddr) { + /* calculate thresholds, assume we have a big Rx queue */ + RxUpThresVal = (QuEndAddr + 1 - QuStartAddr - SK_RB_ULPP) / 8; + RxLoThresVal = (QuEndAddr + 1 - QuStartAddr - SK_RB_LLPP_B)/8; + + /* build HW address format */ + QuStartAddr = QuStartAddr / 8; + QuEndAddr = QuEndAddr / 8; + + /* release local reset */ + SK_OUT8(IoC, RB_ADDR(QuIoOffs,RB_CTRL), RB_RST_CLR); + + /* configure addresses */ + SK_OUT32(IoC, RB_ADDR(QuIoOffs,RB_START), QuStartAddr); + SK_OUT32(IoC, RB_ADDR(QuIoOffs,RB_END), QuEndAddr); + SK_OUT32(IoC, RB_ADDR(QuIoOffs,RB_WP), QuStartAddr); + SK_OUT32(IoC, RB_ADDR(QuIoOffs,RB_RP), QuStartAddr); + + switch (QuType) { + case SK_RX_SRAM_Q: + /* configure threshold for small Rx Queue */ + RxLoThresVal += (SK_RB_LLPP_B - SK_RB_LLPP_S) / 8; + + /* continue with SK_RX_BRAM_Q */ + case SK_RX_BRAM_Q: + /* write threshold for Rx Queue */ + + SK_OUT32(IoC, RB_ADDR(QuIoOffs,RB_RX_UTPP), + RxUpThresVal); + SK_OUT32(IoC, RB_ADDR(QuIoOffs,RB_RX_LTPP), + RxLoThresVal); + /* the high priority threshold not used */ + break; + case SK_TX_RAM_Q: + /* + * Do NOT use Store and forward under normal + * operation due to performance optimization. + * But if Jumbo frames are configured we NEED + * the store and forward of the RAM buffer. + */ + if (pAC->GIni.GIPortUsage == SK_JUMBO_LINK) { + /* + * enable Store & Forward Mode for the + * Tx Side + */ + SK_OUT8(IoC, RB_ADDR(QuIoOffs,RB_CTRL), + RB_ENA_STFWD); + } + break; + } + + /* set queue operational */ + SK_OUT8(IoC, RB_ADDR(QuIoOffs,RB_CTRL), RB_ENA_OP_MD); + } + else { + /* ensure the queue is still disabled */ + SK_OUT8(IoC, RB_ADDR(QuIoOffs,RB_CTRL), RB_RST_SET); + } +} + +/****************************************************************************** + * + * SkGeInitRamBufs() - Initialize the RAM Buffer Queues + * + * Description: + * Initialize all RAM Buffer Queues of the specified port + * + * Returns: + * nothing + */ +static void SkGeInitRamBufs( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port) /* Port Index (MAC_1 + n) */ +{ + SK_GEPORT *pPrt; + int RxQType; + + pPrt = &pAC->GIni.GP[Port]; + + if (pPrt->PRxQSize == SK_MIN_RXQ_SIZE) { + RxQType = SK_RX_SRAM_Q; /* small Rx Queue */ + } + else { + RxQType = SK_RX_BRAM_Q; /* big Rx Queue */ + } + + DoInitRamQueue(pAC, IoC, pPrt->PRxQOff, pPrt->PRxQRamStart, + pPrt->PRxQRamEnd, RxQType); + DoInitRamQueue(pAC, IoC, pPrt->PXsQOff, pPrt->PXsQRamStart, + pPrt->PXsQRamEnd, SK_TX_RAM_Q); + DoInitRamQueue(pAC, IoC, pPrt->PXaQOff, pPrt->PXaQRamStart, + pPrt->PXaQRamEnd, SK_TX_RAM_Q); +} + +/****************************************************************************** + * + * SkGeInitRamIface() - Initialize the RAM Interface + * + * Description: + * This function initializes the Adapbers RAM Interface. + * + * Note: + * This function is used in the diagnostics. + * + * Returns: + * nothing + */ +void SkGeInitRamIface( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC) /* IO context */ +{ + /* release local reset */ + SK_OUT16(IoC, B3_RI_CTRL, RI_RST_CLR); + + /* configure timeout values */ + SK_OUT8(IoC, B3_RI_WTO_R1, SK_RI_TO_53); + SK_OUT8(IoC, B3_RI_WTO_XA1, SK_RI_TO_53); + SK_OUT8(IoC, B3_RI_WTO_XS1, SK_RI_TO_53); + SK_OUT8(IoC, B3_RI_RTO_R1, SK_RI_TO_53); + SK_OUT8(IoC, B3_RI_RTO_XA1, SK_RI_TO_53); + SK_OUT8(IoC, B3_RI_RTO_XS1, SK_RI_TO_53); + SK_OUT8(IoC, B3_RI_WTO_R2, SK_RI_TO_53); + SK_OUT8(IoC, B3_RI_WTO_XA2, SK_RI_TO_53); + SK_OUT8(IoC, B3_RI_WTO_XS2, SK_RI_TO_53); + SK_OUT8(IoC, B3_RI_RTO_R2, SK_RI_TO_53); + SK_OUT8(IoC, B3_RI_RTO_XA2, SK_RI_TO_53); + SK_OUT8(IoC, B3_RI_RTO_XS2, SK_RI_TO_53); +} + +/****************************************************************************** + * + * SkGeInitBmu() - Initialize the BMU state machines + * + * Description: + * Initialize all BMU state machines of the specified port + * + * Returns: + * nothing + */ +static void SkGeInitBmu( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port) /* Port Index (MAC_1 + n) */ +{ + SK_GEPORT *pPrt; + + pPrt = &pAC->GIni.GP[Port]; + + /* Rx Queue: Release all local resets and set the watermark */ + SK_OUT32(IoC, Q_ADDR(pPrt->PRxQOff,Q_CSR), CSR_CLR_RESET); + SK_OUT32(IoC, Q_ADDR(pPrt->PRxQOff,Q_F), SK_BMU_RX_WM); + + /* + * Tx Queue: Release all local resets if the queue is used! + * set watermark + */ + if (pPrt->PXSQSize != 0) { + SK_OUT32(IoC, Q_ADDR(pPrt->PXsQOff,Q_CSR), CSR_CLR_RESET); + SK_OUT32(IoC, Q_ADDR(pPrt->PXsQOff,Q_F), SK_BMU_TX_WM); + } + if (pPrt->PXAQSize != 0) { + SK_OUT32(IoC, Q_ADDR(pPrt->PXaQOff,Q_CSR), CSR_CLR_RESET); + SK_OUT32(IoC, Q_ADDR(pPrt->PXaQOff,Q_F), SK_BMU_TX_WM); + } + /* + * Do NOT enable the descriptor poll timers here, because + * the descriptor addresses are not specified yet. + */ +} + +/****************************************************************************** + * + * TestStopBit() - Test the stop bit of the queue + * + * Description: + * Stopping a queue is not as simple as it seems to be. + * If descriptor polling is enabled, it may happen + * that RX/TX stop is done and SV idle is NOT set. + * In this case we have to issue another stop command. + * + * Retruns: + * The queues control status register + */ +static SK_U32 TestStopBit( +SK_AC *pAC, /* Adapter Context */ +SK_IOC IoC, /* IO Context */ +int QuIoOffs) /* Queue IO Address Offset */ +{ + SK_U32 QuCsr; /* CSR contents */ + + SK_IN32(IoC, Q_ADDR(QuIoOffs,Q_CSR), &QuCsr); + if ((QuCsr & (CSR_STOP|CSR_SV_IDLE)) == 0) { + SK_OUT32(IoC, Q_ADDR(QuIoOffs,Q_CSR), CSR_STOP); + SK_IN32(IoC, Q_ADDR(QuIoOffs,Q_CSR), &QuCsr); + } + return (QuCsr); +} + +/****************************************************************************** + * + * SkGeStopPort() - Stop the Rx/Tx activity of the port 'Port'. + * + * Description: + * After calling this function the descriptor rings and rx and tx + * queues of this port may be reconfigured. + * + * It is possible to stop the receive and transmit path seperate or + * both together. + * + * Dir = SK_STOP_TX Stops the transmit path only and resets + * the XMAC. The receive queue is still and + * the pending rx frames may still transfered + * into the RxD. + * SK_STOP_RX Stop the receive path. The tansmit path + * has to be stoped once before. + * SK_STOP_ALL SK_STOP_TX + SK_STOP_RX + * + * RstMode=SK_SOFT_RST Resets the XMAC. The PHY is still alive. + * SK_HARD_RST Resets the XMAC and the PHY. + * + * Example: + * 1) A Link Down event was signaled for a port. Therefore the activity + * of this port should be stoped and a hardware reset should be issued + * to enable the workaround of XMAC errata #2. But the received frames + * should not be discarded. + * ... + * SkGeStopPort(pAC, IoC, Port, SK_STOP_TX, SK_HARD_RST); + * (transfer all pending rx frames) + * SkGeStopPort(pAC, IoC, Port, SK_STOP_RX, SK_HARD_RST); + * ... + * + * 2) An event was issued which request the driver to switch + * the 'virtual active' link to an other already active port + * as soon as possible. The frames in the receive queue of this + * port may be lost. But the PHY must not be reset during this + * event. + * ... + * SkGeStopPort(pAC, IoC, Port, SK_STOP_ALL, SK_SOFT_RST); + * ... + * + * Extended Description: + * If SK_STOP_TX is set, + * o disable the XMACs receive and transmiter to prevent + * from sending incomplete frames + * o stop the port's transmit queues before terminating the + * BMUs to prevent from performing incomplete PCI cycles + * on the PCI bus + * - The network rx and tx activity and PCI tx transfer is + * disabled now. + * o reset the XMAC depending on the RstMode + * o Stop Interval Timer and Limit Counter of Tx Arbiter, + * also disable Force Sync bit and Enable Alloc bit. + * o perform a local reset of the port's tx path + * - reset the PCI FIFO of the async tx queue + * - reset the PCI FIFO of the sync tx queue + * - reset the RAM Buffer async tx queue + * - reset the RAM Butter sync tx queue + * - reset the MAC Tx FIFO + * o switch Link and Tx LED off, stop the LED counters + * + * If SK_STOP_RX is set, + * o stop the port's receive queue + * - The path data transfer activity is fully stopped now. + * o perform a local reset of the port's rx path + * - reset the PCI FIFO of the rx queue + * - reset the RAM Buffer receive queue + * - reset the MAC Rx FIFO + * o switch Rx LED off, stop the LED counter + * + * If all ports are stopped, + * o reset the RAM Interface. + * + * Notes: + * o This function may be called during the driver states RESET_PORT and + * SWITCH_PORT. + */ +void SkGeStopPort( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port, /* port to stop (MAC_1 + n) */ +int Dir, /* Direction to Stop (SK_STOP_RX, SK_STOP_TX, SK_STOP_ALL) */ +int RstMode)/* Reset Mode (SK_SOFT_RST, SK_HARD_RST) */ +{ +#ifndef SK_DIAG + SK_EVPARA Para; +#endif /* !SK_DIAG */ + SK_GEPORT *pPrt; + SK_U32 DWord; + SK_U16 Word; + SK_U32 XsCsr; + SK_U32 XaCsr; + int i; + SK_BOOL AllPortsDis; + SK_U64 ToutStart; + int ToutCnt; + + pPrt = &pAC->GIni.GP[Port]; + + if (Dir & SK_STOP_TX) { + /* disable the XMACs receiver and transmitter */ + XM_IN16(IoC, Port, XM_MMU_CMD, &Word); + XM_OUT16(IoC, Port, XM_MMU_CMD, + Word & ~(XM_MMU_ENA_RX | XM_MMU_ENA_TX)); + + /* dummy read to ensure writing */ + XM_IN16(IoC, Port, XM_MMU_CMD, &Word); + + /* stop both transmit queues */ + /* + * If the BMU is in the reset state CSR_STOP will terminate + * immediately. + */ + SK_OUT32(IoC, Q_ADDR(pPrt->PXsQOff,Q_CSR), CSR_STOP); + SK_OUT32(IoC, Q_ADDR(pPrt->PXaQOff,Q_CSR), CSR_STOP); + + ToutStart = SkOsGetTime(pAC); + ToutCnt = 0; + do { + /* + * Clear packet arbiter timeout to make sure + * this loop will terminate + */ + if (Port == MAC_1) { + Word = PA_CLR_TO_TX1; + } + else { + Word = PA_CLR_TO_TX2; + } + SK_OUT16(IoC, B3_PA_CTRL, Word); + + /* + * If the transfer stucks at the XMAC the STOP command + * will not terminate if we don't flush the XMACs + * transmit FIFO ! + */ + XM_IN32(IoC, Port, XM_MODE, &DWord); + DWord |= XM_MD_FTF ; + XM_OUT32(IoC, Port, XM_MODE, DWord); + + XsCsr = TestStopBit(pAC, IoC, pPrt->PXsQOff); + XaCsr = TestStopBit(pAC, IoC, pPrt->PXaQOff); + + if (ToutStart + (SK_TICKS_PER_SEC / 18) < + SkOsGetTime(pAC)) { + + /* + * Timeout of 1/18 second reached. + */ + ToutCnt++; + switch (ToutCnt) { + case 1: + /* + * Cache Incoherency workaround: + * Assume a start command has been + * lost while sending the frame. + */ + ToutStart = SkOsGetTime(pAC); + if (XsCsr & CSR_STOP) { + SK_OUT32(IoC, + Q_ADDR(pPrt->PXsQOff, + Q_CSR), CSR_START); + } + if (XaCsr & CSR_STOP) { + SK_OUT32(IoC, + Q_ADDR(pPrt->PXaQOff, + Q_CSR), CSR_START); + } + break; + case 2: + default: /* Fatal Error, Loop aborted */ + /* Create an Error Log Entry */ + SK_ERR_LOG(pAC, SK_ERRCL_HW, + SKERR_HWI_E018, + SKERR_HWI_E018MSG); +#ifndef SK_DIAG + Para.Para64 = Port; + SkEventQueue(pAC, SKGE_DRV, + SK_DRV_PORT_FAIL, Para); +#endif /* !SK_DIAG */ + return; + } + } + + /* + * because of the ASIC problem report entry from 21.08.98 + * it is required to wait until CSR_STOP is reset and + * CSR_SV_IDLE is set. + */ + } while ((XsCsr & (CSR_STOP|CSR_SV_IDLE)) != CSR_SV_IDLE || + (XaCsr & (CSR_STOP|CSR_SV_IDLE)) != CSR_SV_IDLE); + + /* reset the XMAC depending on the RstMode */ + if (RstMode == SK_SOFT_RST) { + SkXmSoftRst(pAC, IoC, Port); + } + else { + SkXmHardRst(pAC, IoC, Port); + } + + /* + * Stop Interval Timer and Limit Counter of Tx Arbiter, + * also disable Force Sync bit and Enable Alloc bit. + */ + SK_OUT8(IoC, MR_ADDR(Port,TXA_CTRL), + TXA_DIS_FSYNC | TXA_DIS_ALLOC | TXA_STOP_RC); + SK_OUT32(IoC, MR_ADDR(Port,TXA_ITI_INI), 0x00000000L); + SK_OUT32(IoC, MR_ADDR(Port,TXA_LIM_INI), 0x00000000L); + + /* + * perform a local reset of the port's tx path + * - reset the PCI FIFO of the async tx queue + * - reset the PCI FIFO of the sync tx queue + * - reset the RAM Buffer async tx queue + * - reset the RAM Butter sync tx queue + * - reset the MAC Tx FIFO + */ + SK_OUT32(IoC, Q_ADDR(pPrt->PXaQOff,Q_CSR), CSR_SET_RESET); + SK_OUT32(IoC, Q_ADDR(pPrt->PXsQOff,Q_CSR), CSR_SET_RESET); + SK_OUT8(IoC, RB_ADDR(pPrt->PXaQOff,RB_CTRL), RB_RST_SET); + SK_OUT8(IoC, RB_ADDR(pPrt->PXsQOff,RB_CTRL), RB_RST_SET); + /* Note: MFF_RST_SET does NOT reset the XMAC! */ + SK_OUT8(IoC, MR_ADDR(Port, TX_MFF_CTRL2), MFF_RST_SET); + + /* switch Link and Tx LED off, stop the LED counters */ + /* Link LED is switched off by the RLMT and the Diag itself */ + SkGeXmitLED(pAC, IoC, MR_ADDR(Port,TX_LED_INI), SK_LED_DIS); + } + + if (Dir & SK_STOP_RX) { + /* + * The RX Stop Command will not terminate if no buffers + * are queued in the RxD ring. But it will always reach + * the Idle state. Therefore we can use this feature to + * stop the transfer of received packets. + */ + /* stop the port's receive queue */ + SK_OUT32(IoC, Q_ADDR(pPrt->PRxQOff,Q_CSR), CSR_STOP); + i = 100; + do { + /* + * Clear packet arbiter timeout to make sure + * this loop will terminate + */ + if (Port == MAC_1) { + Word = PA_CLR_TO_RX1; + } + else { + Word = PA_CLR_TO_RX2; + } + SK_OUT16(IoC, B3_PA_CTRL, Word); + + DWord = TestStopBit(pAC, IoC, pPrt->PRxQOff); + if (i != 0) { + i--; + } + + /* finish if CSR_STOP is done or CSR_SV_IDLE is true and i==0 */ + /* + * because of the ASIC problem report entry from 21.08.98 + * it is required to wait until CSR_STOP is reset and + * CSR_SV_IDLE is set. + */ + } while ((DWord & (CSR_STOP|CSR_SV_IDLE)) != CSR_SV_IDLE && + ((DWord & CSR_SV_IDLE) == 0 || i != 0)); + + /* The path data transfer activity is fully stopped now. */ + + /* + * perform a local reset of the port's rx path + * - reset the PCI FIFO of the rx queue + * - reset the RAM Buffer receive queue + * - reset the MAC Rx FIFO + */ + SK_OUT32(IoC, Q_ADDR(pPrt->PRxQOff,Q_CSR), CSR_SET_RESET); + SK_OUT8(IoC, RB_ADDR(pPrt->PRxQOff,RB_CTRL), RB_RST_SET); + SK_OUT8(IoC, MR_ADDR(Port, RX_MFF_CTRL2), MFF_RST_SET); + + /* switch Rx LED off, stop the LED counter */ + SkGeXmitLED(pAC, IoC, MR_ADDR(Port,RX_LED_INI), SK_LED_DIS); + + } + + /* + * If all ports are stopped reset the RAM Interface. + */ + for (i = 0, AllPortsDis = SK_TRUE; i < pAC->GIni.GIMacsFound; i++) { + if (pAC->GIni.GP[i].PState != SK_PRT_RESET && + pAC->GIni.GP[i].PState != SK_PRT_STOP) { + + AllPortsDis = SK_FALSE; + break; + } + } + if (AllPortsDis) { + pAC->GIni.GIAnyPortAct = SK_FALSE; + } +} + +/****************************************************************************** + * + * SkGeInit0() - Level 0 Initialization + * + * Description: + * - Initialize the BMU address offsets + * + * Returns: + * nothing + */ +static void SkGeInit0( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC) /* IO context */ +{ + int i; + SK_GEPORT *pPrt; + + for (i = 0; i < SK_MAX_MACS; i++) { + pPrt = &pAC->GIni.GP[i]; + pPrt->PState = SK_PRT_RESET; + pPrt->PRxQOff = QOffTab[i].RxQOff; + pPrt->PXsQOff = QOffTab[i].XsQOff; + pPrt->PXaQOff = QOffTab[i].XaQOff; + pPrt->PCheckPar = SK_FALSE; + pPrt->PRxCmd = XM_RX_STRIP_FCS | XM_RX_LENERR_OK; + pPrt->PIsave = 0; + pPrt->PPrevShorts = 0; + pPrt->PLinkResCt = 0; + pPrt->PPrevRx = 0; + pPrt->PPrevFcs = 0; + pPrt->PRxLim = SK_DEF_RX_WA_LIM; + pPrt->PLinkMode = SK_LMODE_AUTOFULL; + pPrt->PLinkModeConf = SK_LMODE_AUTOSENSE; + pPrt->PFlowCtrlMode = SK_FLOW_MODE_SYM_OR_REM; + pPrt->PLinkBroken = SK_TRUE; /* See WA code */ + pPrt->PLinkCap = (SK_LMODE_CAP_HALF | SK_LMODE_CAP_FULL | + SK_LMODE_CAP_AUTOHALF | SK_LMODE_CAP_AUTOFULL); + pPrt->PLinkModeStatus = SK_LMODE_STAT_UNKNOWN; + pPrt->PFlowCtrlCap = SK_FLOW_MODE_SYM_OR_REM; + pPrt->PFlowCtrlStatus = SK_FLOW_STAT_NONE; + pPrt->PMSCap = (SK_MS_CAP_AUTO | SK_MS_CAP_MASTER | + SK_MS_CAP_SLAVE); + pPrt->PMSMode = SK_MS_MODE_AUTO; + pPrt->PMSStatus = SK_MS_STAT_UNSET; + pPrt->PAutoNegFail = SK_FALSE; + pPrt->PLipaAutoNeg = SK_LIPA_UNKNOWN; + pPrt->PHWLinkUp = SK_FALSE; + } + + pAC->GIni.GIPortUsage = SK_RED_LINK; + pAC->GIni.GIAnyPortAct = SK_FALSE; +} + +/****************************************************************************** + * + * SkGeInit1() - Level 1 Initialization + * + * Description: + * o Do a software reset. + * o Clear all reset bits. + * o Verify that the detected hardware is present. + * Return an error if not. + * o Get the hardware configuration + * + Read the number of MACs/Ports. + * + Read the RAM size. + * + Read the PCI Revision ID. + * + Find out the adapters host clock speed + * + Read and check the PHY type + * + * Returns: + * 0: success + * 5: Unexpected PHY type detected + */ +static int SkGeInit1( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC) /* IO context */ +{ + SK_U8 Byte; + SK_U16 Word; + int RetVal; + int i; + + RetVal = 0; + + /* Do the reset */ + SK_OUT8(IoC, B0_CTST, CS_RST_SET); + + /* Release the reset */ + SK_OUT8(IoC, B0_CTST, CS_RST_CLR); + + /* Reset all error bits in the PCI STATUS register */ + /* + * Note: Cfg cycles cannot be used, because they are not + * available on some platforms after 'boot time'. + */ + SK_OUT8(IoC, B2_TST_CTRL1, TST_CFG_WRITE_ON); + SK_IN16(IoC, PCI_C(PCI_STATUS), &Word); + SK_OUT16(IoC, PCI_C(PCI_STATUS), Word | PCI_ERRBITS); + SK_OUT8(IoC, B2_TST_CTRL1, TST_CFG_WRITE_OFF); + + /* Release Master_Reset */ + SK_OUT8(IoC, B0_CTST, CS_MRST_CLR); + + /* Read number of MACs */ + SK_IN8(IoC, B2_MAC_CFG, &Byte); + if (Byte & CFG_SNG_MAC) { + pAC->GIni.GIMacsFound = 1; + } + else { + pAC->GIni.GIMacsFound = 2; + } + SK_IN8(IoC, PCI_C(PCI_REV_ID), &Byte); + pAC->GIni.GIPciHwRev = (int) Byte; + + /* Read the adapters RAM size */ + SK_IN8(IoC, B2_E_0, &Byte); + if (Byte == 3) { + pAC->GIni.GIRamSize = (int)(Byte-1) * 512; + pAC->GIni.GIRamOffs = (SK_U32)512 * 1024; + } + else { + pAC->GIni.GIRamSize = (int)Byte * 512; + pAC->GIni.GIRamOffs = 0; + } + + /* All known GE Adapters works with 53.125 MHz host clock */ + pAC->GIni.GIHstClkFact = SK_FACT_53; + pAC->GIni.GIPollTimerVal = + SK_DPOLL_DEF * (SK_U32)pAC->GIni.GIHstClkFact / 100; + + /* Read the PHY type */ + SK_IN8(IoC, B2_E_1, &Byte); + Byte &= 0x0f; /* the PHY type is stored in the lower nibble */ + for (i=0; iGIni.GIMacsFound; i++) { + pAC->GIni.GP[i].PhyType = Byte; + switch (Byte) { + case SK_PHY_XMAC: + pAC->GIni.GP[i].PhyAddr = PHY_ADDR_XMAC; + break; + case SK_PHY_BCOM: + pAC->GIni.GP[i].PhyAddr = PHY_ADDR_BCOM; + break; + case SK_PHY_LONE: + pAC->GIni.GP[i].PhyAddr = PHY_ADDR_LONE; + break; + case SK_PHY_NAT: + pAC->GIni.GP[i].PhyAddr = PHY_ADDR_NAT; + break; + default: + /* ERROR: unexpected PHY typ detected */ + RetVal = 5; + break; + } + } + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_INIT, + ("PHY type: %d PHY addr: %x\n", pAC->GIni.GP[i].PhyType, + pAC->GIni.GP[i].PhyAddr)) ; + + return (RetVal); +} + +/****************************************************************************** + * + * SkGeInit2() - Level 2 Initialization + * + * Description: + * - start the Blink Source Counter + * - start the Descriptor Poll Timer + * - configure the MAC-Arbiter + * - configure the Packet-Arbiter + * - enable the Tx Arbiters + * - enable the RAM Interface Arbiter + * + * Returns: + * nothing + */ +static void SkGeInit2( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC) /* IO context */ +{ + SK_GEPORT *pPrt; + SK_U32 DWord; + int i; + + /* start the Blink Source Counter */ + DWord = SK_BLK_DUR * (SK_U32)pAC->GIni.GIHstClkFact / 100; + SK_OUT32(IoC, B2_BSC_INI, DWord); + SK_OUT8(IoC, B2_BSC_CTRL, BSC_START); + + /* start the Descriptor Poll Timer */ + if (pAC->GIni.GIPollTimerVal != 0) { + if (pAC->GIni.GIPollTimerVal > SK_DPOLL_MAX) { + pAC->GIni.GIPollTimerVal = SK_DPOLL_MAX; + + /* Create an Error Log Entry */ + SK_ERR_LOG(pAC, SK_ERRCL_SW, SKERR_HWI_E017, + SKERR_HWI_E017MSG); + } + SK_OUT32(IoC, B28_DPT_INI, pAC->GIni.GIPollTimerVal); + SK_OUT8(IoC, B28_DPT_CTRL, DPT_START); + } + + /* + * Configure + * - the MAC-Arbiter and + * - the Paket Arbiter + * + * The MAC and the packet arbiter will be started once + * and never be stopped. + */ + SkGeInitMacArb(pAC, IoC); + SkGeInitPktArb(pAC, IoC); + + /* enable the Tx Arbiters */ + SK_OUT8(IoC, MR_ADDR(MAC_1,TXA_CTRL), TXA_ENA_ARB); + if (pAC->GIni.GIMacsFound > 1) { + SK_OUT8(IoC, MR_ADDR(MAC_2,TXA_CTRL), TXA_ENA_ARB); + } + + /* enable the RAM Interface Arbiter */ + SkGeInitRamIface(pAC, IoC); + + for (i = 0; i < SK_MAX_MACS; i++) { + pPrt = &pAC->GIni.GP[i]; + if (pAC->GIni.GIPortUsage == SK_JUMBO_LINK) { + pPrt->PRxCmd |= XM_RX_BIG_PK_OK; + } + } +} + +/****************************************************************************** + * + * SkGeInit() - Initialize the GE Adapter with the specified level. + * + * Description: + * Level 0: Initialize the Module structures. + * Level 1: Generic Hardware Initialization. The + * IOP/MemBase pointer has to be set before + * calling this level. + * + * o Do a software reset. + * o Clear all reset bits. + * o Verify that the detected hardware is present. + * Return an error if not. + * o Get the hardware configuration + * + Set GIMacsFound with the number of MACs. + * + Store the RAM size in GIRamSize. + * + Save the PCI Revision ID in GIPciHwRev. + * o return an error + * if Number of MACs > SK_MAX_MACS + * + * After returning from Level 0 the adapter + * may be accessed with IO operations. + * + * Level 2: start the Blink Source Counter + * + * Returns: + * 0: success + * 1: Number of MACs exceeds SK_MAX_MACS ( after level 1) + * 2: Adapter not present or not accessable + * 3: Illegal initialization level + * 4: Initialization Level 1 Call missing + * 5: Unexpected PHY type detected + */ +int SkGeInit( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Level) /* initialization level */ +{ + int RetVal; /* return value */ + SK_U32 DWord; + + RetVal = 0; + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_INIT, + ("SkGeInit(Level %d)\n",Level)) ; + + switch (Level) { + case SK_INIT_DATA: + /* Initialization Level 0 */ + SkGeInit0(pAC,IoC) ; + pAC->GIni.GILevel = SK_INIT_DATA; + break; + case SK_INIT_IO: + /* Initialization Level 1 */ + RetVal = SkGeInit1(pAC,IoC) ; + + /* Check if the adapter seems to be accessable */ + SK_OUT32(IoC, B2_IRQM_INI, 0x11335577L); + SK_IN32(IoC, B2_IRQM_INI, &DWord); + SK_OUT32(IoC, B2_IRQM_INI, 0x00000000L); + if (DWord != 0x11335577L) { + RetVal = 2; + break; + } + + /* Check if the number of GIMacsFound matches SK_MAX_MACS */ + if (pAC->GIni.GIMacsFound > SK_MAX_MACS) { + RetVal = 1; + break; + } + + /* Level 1 successfully passed */ + pAC->GIni.GILevel = SK_INIT_IO; + break; + case SK_INIT_RUN: + /* Initialization Level 2 */ + if (pAC->GIni.GILevel != SK_INIT_IO) { +#ifndef SK_DIAG + SK_ERR_LOG(pAC, SK_ERRCL_SW, SKERR_HWI_E002, + SKERR_HWI_E002MSG); +#endif + RetVal = 4; + break; + } + SkGeInit2(pAC,IoC) ; + + /* Level 2 successfully passed */ + pAC->GIni.GILevel = SK_INIT_RUN; + break; + default: + /* Create an Error Log Entry */ + SK_ERR_LOG(pAC, SK_ERRCL_SW, SKERR_HWI_E003, SKERR_HWI_E003MSG); + RetVal = 3 ; + break; + } + + return (RetVal); +} + +/****************************************************************************** + * + * SkGeDeInit() - Deinitialize the adapter. + * + * Description: + * All ports of the adapter will be stopped if not already done. + * Do a software reset and switch off all LEDs. + * + * Returns: + * nothing + */ +void SkGeDeInit( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC) /* IO context */ +{ + int i; + SK_U16 Word; + + /* Stop all current transfer activity */ + for (i = 0; i < pAC->GIni.GIMacsFound; i++) { + if (pAC->GIni.GP[i].PState != SK_PRT_STOP && + pAC->GIni.GP[i].PState != SK_PRT_RESET) { + + SkGeStopPort(pAC, IoC, i, SK_STOP_ALL, SK_HARD_RST); + } + } + + /* Reset all bits in the PCI STATUS register */ + /* + * Note: Cfg cycles cannot be used, because they are not + * available on some platforms after 'boot time'. + */ + SK_OUT8(IoC, B2_TST_CTRL1, TST_CFG_WRITE_ON); + SK_IN16(IoC, PCI_C(PCI_STATUS), &Word); + SK_OUT16(IoC, PCI_C(PCI_STATUS), Word | PCI_ERRBITS); + SK_OUT8(IoC, B2_TST_CTRL1, TST_CFG_WRITE_OFF); + + /* Do the reset, all LEDs are switched off now */ + SK_OUT8(IoC, B0_CTST, CS_RST_SET); +} + +/****************************************************************************** + * + * SkGeInitPort() Initialize the specified prot. + * + * Description: + * PRxQSize, PXSQSize, and PXAQSize has to be + * configured for the specified port before calling this + * function. The descriptor rings has to be initialized, too. + * + * o (Re)configure queues of the specified port. + * o configure the XMAC of the specified port. + * o put ASIC and XMAC(s) in operational mode. + * o initialize Rx/Tx and Sync LED + * o initialize RAM Buffers and MAC FIFOs + * + * The port is ready to connect when returning. + * + * Note: + * The XMACs Rx and Tx state machine is still disabled when + * returning. + * + * Returns: + * 0: success + * 1: Queue size initialization error. The configured values + * for PRxQSize, PXSQSize, or PXAQSize are invalid for one + * or more queues. The specified port was NOT initialized. + * An error log entry was generated. + * 2: The port has to be stopped before it can be initilaized again. + */ +int SkGeInitPort( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port) /* Port to configure */ +{ + SK_GEPORT *pPrt; + + pPrt = &pAC->GIni.GP[Port]; + + if (SkGeCheckQSize(pAC,Port) != 0) { + SK_ERR_LOG(pAC, SK_ERRCL_SW, SKERR_HWI_E004, SKERR_HWI_E004MSG); + return (1); + } + if (pPrt->PState == SK_PRT_INIT || pPrt->PState == SK_PRT_RUN) { + SK_ERR_LOG(pAC, SK_ERRCL_SW, SKERR_HWI_E005, SKERR_HWI_E005MSG); + return (2); + } + + /* Configuration ok, initialize the Port now */ + + /* Initialize Rx, Tx and Link LED */ + /* + * If 1000BT Phy needs LED initialization than swap + * LED and XMAC initialization order + */ + SkGeXmitLED(pAC, IoC, MR_ADDR(Port,TX_LED_INI), SK_LED_ENA); + SkGeXmitLED(pAC, IoC, MR_ADDR(Port,RX_LED_INI), SK_LED_ENA); + /* The Link LED is initialized by RLMT or Diagnostics itself */ + + /* Do NOT initialize the Link Sync Counter */ + + /* + * Configure + * - XMAC + * - MAC FIFOs + * - RAM Buffers + * - enable Force Sync bit if synchronous queue available + * - BMUs + */ + SkXmInitMac(pAC, IoC, Port); + SkGeInitMacFifo(pAC, IoC, Port); + SkGeInitRamBufs(pAC, IoC, Port); + if (pPrt->PXSQSize != 0) { + SK_OUT8(IoC, MR_ADDR(Port, TXA_CTRL), TXA_ENA_FSYNC); + } + SkGeInitBmu(pAC, IoC, Port); + + /* Mark port as initialized. */ + pPrt->PState = SK_PRT_INIT; + pAC->GIni.GIAnyPortAct = SK_TRUE; + + return (0); +} diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/skgepnmi.c linux/drivers/net/sk98lin/skgepnmi.c --- v2.2.13/linux/drivers/net/sk98lin/skgepnmi.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/skgepnmi.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,7142 @@ +/***************************************************************************** + * + * Name: skgepnmi.c + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.69 $ + * Date: $Date: 1999/10/18 11:42:15 $ + * Purpose: Private Network Management Interface + * + ****************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skge.c" for further information. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/***************************************************************************** + * + * History: + * + * $Log: skgepnmi.c,v $ + * Revision 1.69 1999/10/18 11:42:15 rwahl + * Added typecasts for checking event dependent param (debug only). + * + * Revision 1.68 1999/10/06 09:35:59 cgoos + * Added state check to PHY_READ call (hanged if called during startup). + * + * Revision 1.67 1999/09/22 09:53:20 rwahl + * - Read Broadcom register for updating fcs error counter (1000Base-T). + * + * Revision 1.66 1999/08/26 13:47:56 rwahl + * Added SK_DRIVER_SENDEVENT when queueing RLMT_CHANGE_THRES trap. + * + * Revision 1.65 1999/07/26 07:49:35 cgoos + * Added two typecasts to avoid compiler warnings. + * + * Revision 1.64 1999/05/20 09:24:12 cgoos + * Changes for 1000Base-T (sensors, Master/Slave). + * + * Revision 1.63 1999/04/13 15:11:58 mhaveman + * Moved include of rlmt.h to header skgepnmi.h because some macros + * are needed there. + * + * Revision 1.62 1999/04/13 15:08:07 mhaveman + * Replaced again SK_RLMT_CHECK_LINK with SK_PNMI_RLMT_MODE_CHK_LINK + * to grant unified interface by only using the PNMI header file. + * SK_PNMI_RLMT_MODE_CHK_LINK is defined the same as SK_RLMT_CHECK_LINK. + * + * Revision 1.61 1999/04/13 15:02:48 mhaveman + * Changes caused by review: + * -Changed some comments + * -Removed redundant check for OID_SKGE_PHYS_FAC_ADDR + * -Optimized PRESET check. + * -Meaning of error SK_ADDR_DUPLICATE_ADDRESS changed. Set of same + * address will now not cause this error. Removed corresponding check. + * + * Revision 1.60 1999/03/23 10:41:23 mhaveman + * Added comments. + * + * Revision 1.59 1999/02/19 08:01:28 mhaveman + * Fixed bug 10372 that after counter reset all ports were displayed + * as inactive. + * + * Revision 1.58 1999/02/16 18:04:47 mhaveman + * Fixed problem of twisted OIDs SENSOR_WAR_TIME and SENSOR_ERR_TIME. + * + * Revision 1.56 1999/01/27 12:29:11 mhaveman + * SkTimerStart was called with time value in milli seconds but needs + * micro seconds. + * + * Revision 1.55 1999/01/25 15:00:38 mhaveman + * Added support to allow multiple ports to be active. If this feature in + * future will be used, the Management Data Base variables PORT_ACTIVE + * and PORT_PREFERED should be moved to the port specific part of RLMT. + * Currently they return the values of the first active physical port + * found. A set to the virtual port will actually change all active + * physical ports. A get returns the melted values of all active physical + * ports. If the port values differ a return value INDETERMINATED will + * be returned. This effects especially the CONF group. + * + * Revision 1.54 1999/01/19 10:10:22 mhaveman + * -Fixed bug 10354: Counter values of virtual port were wrong after port + * switches + * -Added check if a switch to the same port is notified. + * + * Revision 1.53 1999/01/07 09:25:21 mhaveman + * Forgot to initialize a variable. + * + * Revision 1.52 1999/01/05 10:34:33 mhaveman + * Fixed little error in RlmtChangeEstimate calculation. + * + * Revision 1.51 1999/01/05 09:59:07 mhaveman + * -Moved timer start to init level 2 + * -Redesigned port switch average calculation to avoid 64bit + * arithmetic. + * + * Revision 1.50 1998/12/10 15:13:59 mhaveman + * -Fixed: PHYS_CUR_ADDR returned wrong addresses + * -Fixed: RLMT_PORT_PREFERED and RLMT_CHANGE_THRES preset returned + * always BAD_VALUE. + * -Fixed: TRAP buffer seemed to sometimes suddenly empty + * + * Revision 1.49 1998/12/09 16:17:07 mhaveman + * Fixed: Couldnot delete VPD keys on UNIX. + * + * Revision 1.48 1998/12/09 14:11:10 mhaveman + * -Add: Debugmessage for XMAC_RESET supressed to minimize output. + * -Fixed: RlmtChangeThreshold will now be initialized. + * -Fixed: VPD_ENTRIES_LIST extended value with unnecessary space char. + * -Fixed: On VPD key creation an invalid key name could be created + * (e.g. A5) + * -Some minor changes in comments and code. + * + * Revision 1.47 1998/12/08 16:00:31 mhaveman + * -Fixed: For RLMT_PORT_ACTIVE will now be returned a 0 if no port + * is active. + * -Fixed: For the RLMT statistics group only the last value was + * returned and the rest of the buffer was filled with 0xff + * -Fixed: Mysteriously the preset on RLMT_MODE still returned + * BAD_VALUE. + * Revision 1.46 1998/12/08 10:04:56 mhaveman + * -Fixed: Preset on RLMT_MODE returned always BAD_VALUE error. + * -Fixed: Alignment error in GetStruct + * -Fixed: If for Get/Preset/SetStruct the buffer size is equal or + * larger than SK_PNMI_MIN_STRUCT_SIZE the return value is stored + * to the buffer. In this case the caller should always return + * ok to its upper routines. Only if the buffer size is less + * than SK_PNMI_MIN_STRUCT_SIZE and the return value is unequal + * to 0, an error should be returned by the caller. + * -Fixed: Wrong number of instances with RLMT statistic. + * -Fixed: Return now SK_LMODE_STAT_UNKNOWN if the LinkModeStatus is 0. + * + * Revision 1.45 1998/12/03 17:17:24 mhaveman + * -Removed for VPD create action the buffer size limitation to 4 bytes. + * -Pass now physical/active physical port to ADDR for CUR_ADDR set + * + * Revision 1.44 1998/12/03 15:14:35 mhaveman + * Another change to Vpd instance evaluation. + * + * Revision 1.43 1998/12/03 14:18:10 mhaveman + * -Fixed problem in PnmiSetStruct. It was impossible to set any value. + * -Removed VPD key evaluation for VPD_FREE_BYTES and VPD_ACTION. + * + * Revision 1.42 1998/12/03 11:31:47 mhaveman + * Inserted cast to satisfy lint. + * + * Revision 1.41 1998/12/03 11:28:16 mhaveman + * Removed SK_PNMI_CHECKPTR + * + * Revision 1.40 1998/12/03 11:19:07 mhaveman + * Fixed problems + * -A set to virtual port will now be ignored. A set with broadcast + * address to any port will be ignored. + * -GetStruct function made VPD instance calculation wrong. + * -Prefered port returned -1 instead of 0. + * + * Revision 1.39 1998/11/26 15:30:29 mhaveman + * Added sense mode to link mode. + * + * Revision 1.38 1998/11/23 15:34:00 mhaveman + * -Fixed bug for RX counters. On an RX overflow interrupt the high + * words of all RX counters were incremented. + * -SET operations on FLOWCTRL_MODE and LINK_MODE accept now the + * value 0, which has no effect. It is usefull for multiple instance + * SETs. + * + * Revision 1.37 1998/11/20 08:02:04 mhaveman + * -Fixed: Ports were compared with MAX_SENSORS + * -Fixed: Crash in GetTrapEntry with MEMSET macro + * -Fixed: Conversions between physical, logical port index and instance + * + * Revision 1.36 1998/11/16 07:48:53 mhaveman + * Casted SK_DRIVER_SENDEVENT with (void) to eleminate compiler warnings + * on Solaris. + * + * Revision 1.35 1998/11/16 07:45:34 mhaveman + * SkAddrOverride now returns value and will be checked. + * + * Revision 1.34 1998/11/10 13:40:37 mhaveman + * Needed to change interface, because NT driver needs a return value + * of needed buffer space on TOO_SHORT errors. Therefore all + * SkPnmiGet/Preset/Set functions now have a pointer to the length + * parameter, where the needed space on error is returned. + * + * Revision 1.33 1998/11/03 13:52:46 mhaveman + * Made file lint conform. + * + * Revision 1.32 1998/11/03 13:19:07 mhaveman + * The events SK_HWEV_SET_LMODE and SK_HWEV_SET_FLOWMODE pass now in + * Para32[0] the physical MAC index and in Para32[1] the new mode. + * + * Revision 1.31 1998/11/03 12:30:40 gklug + * fix: compiler warning memset + * + * Revision 1.30 1998/11/03 12:04:46 mhaveman + * Fixed problem in SENSOR_VALUE, which wrote beyond the buffer end + * Fixed alignment problem with CHIPSET. + * + * Revision 1.29 1998/11/02 11:23:54 mhaveman + * Corrected SK_ERROR_LOG to SK_ERR_LOG. Sorry. + * + * Revision 1.28 1998/11/02 10:47:16 mhaveman + * Added syslog messages for internal errors. + * + * Revision 1.27 1998/10/30 15:48:06 mhaveman + * Fixed problems after simulation of SK_PNMI_EVT_CHG_EST_TIMER and + * RlmtChangeThreshold calculation. + * + * Revision 1.26 1998/10/29 15:36:55 mhaveman + * -Fixed bug in trap buffer handling. + * -OID_SKGE_DRIVER_DESCR, OID_SKGE_DRIVER_VERSION, OID_SKGE_HW_DESCR, + * OID_SKGE_HW_VERSION, OID_SKGE_VPD_ENTRIES_LIST, OID_SKGE_VPD_KEY, + * OID_SKGE_VPD_VALUE, and OID_SKGE_SENSOR_DESCR return values with + * a leading octet before each string storing the string length. + * -Perform a RlmtUpdate during SK_PNMI_EVT_XMAC_RESET to minimize + * RlmtUpdate calls in GetStatVal. + * -Inserted SK_PNMI_CHECKFLAGS macro increase readability. + * + * Revision 1.25 1998/10/29 08:50:36 mhaveman + * Fixed problems after second event simulation. + * + * Revision 1.24 1998/10/28 08:44:37 mhaveman + * -Fixed alignment problem + * -Fixed problems during event simulation + * -Fixed sequence of error return code (INSTANCE -> ACCESS -> SHORT) + * -Changed type of parameter Instance back to SK_U32 because of VPD + * -Updated new VPD function calls + * + * Revision 1.23 1998/10/23 10:16:37 mhaveman + * Fixed bugs after buffer test simulation. + * + * Revision 1.22 1998/10/21 13:23:52 mhaveman + * -Call syntax of SkOsGetTime() changed to SkOsGetTime(pAc). + * -Changed calculation of hundrets of seconds. + * + * Revision 1.20 1998/10/20 07:30:45 mhaveman + * Made type changes to unsigned integer where possible. + * + * Revision 1.19 1998/10/19 10:51:30 mhaveman + * -Made Bug fixes after simulation run + * -Renamed RlmtMAC... to RlmtPort... + * -Marked workarounds with Errata comments + * + * Revision 1.18 1998/10/14 07:50:08 mhaveman + * -For OID_SKGE_LINK_STATUS the link down detection has moved from RLMT + * to HWACCESS. + * -Provided all MEMCPY/MEMSET macros with (char *) pointers, because + * Solaris throwed warnings when mapping to bcopy/bset. + * + * Revision 1.17 1998/10/13 07:42:01 mhaveman + * -Added OIDs OID_SKGE_TRAP_NUMBER and OID_SKGE_ALL_DATA + * -Removed old cvs history entries + * -Renamed MacNumber to PortNumber + * + * Revision 1.16 1998/10/07 10:52:49 mhaveman + * -Inserted handling of some OID_GEN_ Ids for windows + * -Fixed problem with 803.2 statistic. + * + * Revision 1.15 1998/10/01 09:16:29 mhaveman + * Added Debug messages for function call and UpdateFlag tracing. + * + * Revision 1.14 1998/09/30 13:39:09 mhaveman + * -Reduced namings of 'MAC' by replacing them with 'PORT'. + * -Completed counting of OID_SKGE_RX_HW_ERROR_CTS, + * OID_SKGE_TX_HW_ERROR_CTS, + * OID_SKGE_IN_ERRORS_CTS, and OID_SKGE_OUT_ERROR_CTS. + * -SET check for RlmtMode + * + * Revision 1.13 1998/09/28 13:13:08 mhaveman + * Hide strcmp, strlen, and strncpy behind macros SK_STRCMP, SK_STRLEN, + * and SK_STRNCPY. (Same reasons as for mem.. and MEM..) + * + * Revision 1.12 1998/09/16 08:18:36 cgoos + * Fix: XM_INxx and XM_OUTxx called with different parameter order: + * sometimes IoC,Mac,... sometimes Mac,IoC,... Now always first variant. + * Fix: inserted "Pnmi." into some pAC->pDriverDescription / Version. + * Change: memset, memcpy to makros SK_MEMSET, SK_MEMCPY + * + * Revision 1.11 1998/09/04 17:01:45 mhaveman + * Added SyncCounter as macro and OID_SKGE_.._NO_DESCR_CTS to + * OID_SKGE_RX_NO_BUF_CTS. + * + * Revision 1.10 1998/09/04 14:35:35 mhaveman + * Added macro counters, that are counted by driver. + * + ****************************************************************************/ + + +static const char SysKonnectFileId[] = + "@(#) $Id: skgepnmi.c,v 1.69 1999/10/18 11:42:15 rwahl Exp $ (C) SysKonnect."; + +#include "h/skdrv1st.h" +#include "h/sktypes.h" +#include "h/xmac_ii.h" + +#include "h/skdebug.h" +#include "h/skqueue.h" +#include "h/skgepnmi.h" +#include "h/skgesirq.h" +#include "h/skcsum.h" +#include "h/skvpd.h" +#include "h/skgehw.h" +#include "h/skgeinit.h" +#include "h/skdrv2nd.h" +#include "h/skgepnm2.h" + + +/* + * Public Function prototypes + */ +int SkPnmiInit(SK_AC *pAC, SK_IOC IoC, int level); +int SkPnmiGetVar(SK_AC *pAC, SK_IOC IoC, SK_U32 Id, void *pBuf, + unsigned int *pLen, SK_U32 Instance); +int SkPnmiPreSetVar(SK_AC *pAC, SK_IOC IoC, SK_U32 Id, void *pBuf, + unsigned int *pLen, SK_U32 Instance); +int SkPnmiSetVar(SK_AC *pAC, SK_IOC IoC, SK_U32 Id, void *pBuf, + unsigned int *pLen, SK_U32 Instance); +int SkPnmiGetStruct(SK_AC *pAC, SK_IOC IoC, void *pBuf, unsigned int *pLen); +int SkPnmiPreSetStruct(SK_AC *pAC, SK_IOC IoC, void *pBuf, unsigned int *pLen); +int SkPnmiSetStruct(SK_AC *pAC, SK_IOC IoC, void *pBuf, unsigned int *pLen); +int SkPnmiEvent(SK_AC *pAC, SK_IOC IoC, SK_U32 Event, SK_EVPARA Param); + + +/* + * Private Function prototypes + */ +static int Addr(SK_AC *pAC, SK_IOC IoC, int action, + SK_U32 Id, char *pBuf, unsigned int *pLen, SK_U32 Instance, + unsigned int TableIndex); +static SK_U8 CalculateLinkModeStatus(SK_AC *pAC, SK_IOC IoC, unsigned int + PhysPortIndex); +static SK_U8 CalculateLinkStatus(SK_AC *pAC, SK_IOC IoC, unsigned int + PhysPortIndex); +static void CopyMac(char *pDst, SK_MAC_ADDR *pMac); +static void CopyTrapQueue(SK_AC *pAC, char *pDstBuf); +static int CsumStat(SK_AC *pAC, SK_IOC IoC, int action, SK_U32 Id, + char *pBuf, unsigned int *pLen, SK_U32 Instance, + unsigned int TableIndex); +static int General(SK_AC *pAC, SK_IOC IoC, int action, SK_U32 Id, + char *pBuf, unsigned int *pLen, SK_U32 Instance, + unsigned int TableIndex); +static SK_U64 GetPhysStatVal(SK_AC *pAC, SK_IOC IoC, + unsigned int PhysPortIndex, unsigned int StatIndex); +static SK_U64 GetStatVal(SK_AC *pAC, SK_IOC IoC, unsigned int LogPortIndex, + unsigned int StatIndex); +static char* GetTrapEntry(SK_AC *pAC, SK_U32 TrapId, unsigned int Size); +static void GetTrapQueueLen(SK_AC *pAC, unsigned int *pLen, + unsigned int *pEntries); +static int GetVpdKeyArr(SK_AC *pAC, SK_IOC IoC, char *pKeyArr, + unsigned int KeyArrLen, unsigned int *pKeyNo); +static int LookupId(SK_U32 Id); +static int Mac8023Stat(SK_AC *pAC, SK_IOC IoC, int action, SK_U32 Id, + char *pBuf, unsigned int *pLen, SK_U32 Instance, + unsigned int TableIndex); +static int MacPrivateConf(SK_AC *pAC, SK_IOC IoC, int action, SK_U32 Id, + char *pBuf, unsigned int *pLen, SK_U32 Instance, + unsigned int TableIndex); +static int MacPrivateStat(SK_AC *pAC, SK_IOC IoC, int action, SK_U32 Id, + char *pBuf, unsigned int *pLen, SK_U32 Instance, + unsigned int TableIndex); +static int MacUpdate(SK_AC *pAC, SK_IOC IoC, unsigned int FirstMac, + unsigned int LastMac); +static int Monitor(SK_AC *pAC, SK_IOC IoC, int action, + SK_U32 Id, char *pBuf, unsigned int *pLen, SK_U32 Instance, + unsigned int TableIndex); +static int OidStruct(SK_AC *pAC, SK_IOC IoC, int action, SK_U32 Id, + char *pBuf, unsigned int *pLen, SK_U32 Instance, + unsigned int TableIndex); +static int Perform(SK_AC *pAC, SK_IOC IoC, int action, SK_U32 Id, + char *pBuf, unsigned int* pLen, SK_U32 Instance, + unsigned int TableIndex); +static int PnmiStruct(SK_AC *pAC, SK_IOC IoC, int Action, char *pBuf, + unsigned int *pLen); +static int PnmiVar(SK_AC *pAC, SK_IOC IoC, int Action, SK_U32 Id, + char *pBuf, unsigned int *pLen, SK_U32 Instance); +static void QueueRlmtNewMacTrap(SK_AC *pAC, unsigned int ActiveMac); +static void QueueRlmtPortTrap(SK_AC *pAC, SK_U32 TrapId, + unsigned int PortIndex); +static void QueueSensorTrap(SK_AC *pAC, SK_U32 TrapId, + unsigned int SensorIndex); +static void QueueSimpleTrap(SK_AC *pAC, SK_U32 TrapId); +static void ResetCounter(SK_AC *pAC, SK_IOC IoC); +static int Rlmt(SK_AC *pAC, SK_IOC IoC, int action, SK_U32 Id, + char *pBuf, unsigned int *pLen, SK_U32 Instance, + unsigned int TableIndex); +static int RlmtStat(SK_AC *pAC, SK_IOC IoC, int action, SK_U32 Id, + char *pBuf, unsigned int *pLen, SK_U32 Instance, + unsigned int TableIndex); +static int RlmtUpdate(SK_AC *pAC, SK_IOC IoC); +static int SensorStat(SK_AC *pAC, SK_IOC IoC, int action, SK_U32 Id, + char *pBuf, unsigned int *pLen, SK_U32 Instance, + unsigned int TableIndex); +static int SirqUpdate(SK_AC *pAC, SK_IOC IoC); +static void VirtualConf(SK_AC *pAC, SK_IOC IoC, SK_U32 Id, char *pBuf); +static int Vpd(SK_AC *pAC, SK_IOC IoC, int action, SK_U32 Id, + char *pBuf, unsigned int *pLen, SK_U32 Instance, + unsigned int TableIndex); + + +/****************************************************************************** + * + * Global variables + */ + +/* + * Table to correlate OID with handler function and index to + * hardware register stored in StatAddress if applicable. + */ +static const SK_PNMI_TAB_ENTRY IdTable[] = { + {OID_GEN_XMIT_OK, + 0, + 0, + 0, + SK_PNMI_RO, Mac8023Stat, SK_PNMI_HTX}, + {OID_GEN_RCV_OK, + 0, + 0, + 0, + SK_PNMI_RO, Mac8023Stat, SK_PNMI_HRX}, + {OID_GEN_XMIT_ERROR, + 0, + 0, + 0, + SK_PNMI_RO, General, 0}, + {OID_GEN_RCV_ERROR, + 0, + 0, + 0, + SK_PNMI_RO, General, 0}, + {OID_GEN_RCV_NO_BUFFER, + 0, + 0, + 0, + SK_PNMI_RO, General, 0}, + {OID_GEN_DIRECTED_FRAMES_XMIT, + 0, + 0, + 0, + SK_PNMI_RO, Mac8023Stat, SK_PNMI_HTX_UNICAST}, + {OID_GEN_MULTICAST_FRAMES_XMIT, + 0, + 0, + 0, + SK_PNMI_RO, Mac8023Stat, SK_PNMI_HTX_MULTICAST}, + {OID_GEN_BROADCAST_FRAMES_XMIT, + 0, + 0, + 0, + SK_PNMI_RO, Mac8023Stat, SK_PNMI_HTX_BROADCAST}, + {OID_GEN_DIRECTED_FRAMES_RCV, + 0, + 0, + 0, + SK_PNMI_RO, Mac8023Stat, SK_PNMI_HRX_UNICAST}, + {OID_GEN_MULTICAST_FRAMES_RCV, + 0, + 0, + 0, + SK_PNMI_RO, Mac8023Stat, SK_PNMI_HRX_MULTICAST}, + {OID_GEN_BROADCAST_FRAMES_RCV, + 0, + 0, + 0, + SK_PNMI_RO, Mac8023Stat, SK_PNMI_HRX_BROADCAST}, + {OID_GEN_RCV_CRC_ERROR, + 0, + 0, + 0, + SK_PNMI_RO, Mac8023Stat, SK_PNMI_HRX_FCS}, + {OID_GEN_TRANSMIT_QUEUE_LENGTH, + 0, + 0, + 0, + SK_PNMI_RO, General, 0}, + {OID_802_3_PERMANENT_ADDRESS, + 0, + 0, + 0, + SK_PNMI_RO, Mac8023Stat, 0}, + {OID_802_3_CURRENT_ADDRESS, + 0, + 0, + 0, + SK_PNMI_RO, Mac8023Stat, 0}, + {OID_802_3_RCV_ERROR_ALIGNMENT, + 0, + 0, + 0, + SK_PNMI_RO, Mac8023Stat, SK_PNMI_HRX_FRAMING}, + {OID_802_3_XMIT_ONE_COLLISION, + 0, + 0, + 0, + SK_PNMI_RO, Mac8023Stat, SK_PNMI_HTX_SINGLE_COL}, + {OID_802_3_XMIT_MORE_COLLISIONS, + 0, + 0, + 0, + SK_PNMI_RO, Mac8023Stat, SK_PNMI_HTX_MULTI_COL}, + {OID_802_3_XMIT_DEFERRED, + 0, + 0, + 0, + SK_PNMI_RO, Mac8023Stat, SK_PNMI_HTX_DEFFERAL}, + {OID_802_3_XMIT_MAX_COLLISIONS, + 0, + 0, + 0, + SK_PNMI_RO, Mac8023Stat, SK_PNMI_HTX_EXCESS_COL}, + {OID_802_3_RCV_OVERRUN, + 0, + 0, + 0, + SK_PNMI_RO, Mac8023Stat, SK_PNMI_HRX_OVERFLOW}, + {OID_802_3_XMIT_UNDERRUN, + 0, + 0, + 0, + SK_PNMI_RO, Mac8023Stat, SK_PNMI_HTX_UNDERRUN}, + {OID_802_3_XMIT_TIMES_CRS_LOST, + 0, + 0, + 0, + SK_PNMI_RO, Mac8023Stat, SK_PNMI_HTX_CARRIER}, + {OID_802_3_XMIT_LATE_COLLISIONS, + 0, + 0, + 0, + SK_PNMI_RO, Mac8023Stat, SK_PNMI_HTX_LATE_COL}, + {OID_SKGE_MDB_VERSION, + 1, + 0, + SK_PNMI_MAI_OFF(MgmtDBVersion), + SK_PNMI_RO, General, 0}, + {OID_SKGE_SUPPORTED_LIST, + 0, + 0, + 0, + SK_PNMI_RO, General, 0}, + {OID_SKGE_ALL_DATA, + 0, + 0, + 0, + SK_PNMI_RW, OidStruct, 0}, + {OID_SKGE_VPD_FREE_BYTES, + 1, + 0, + SK_PNMI_MAI_OFF(VpdFreeBytes), + SK_PNMI_RO, Vpd, 0}, + {OID_SKGE_VPD_ENTRIES_LIST, + 1, + 0, + SK_PNMI_MAI_OFF(VpdEntriesList), + SK_PNMI_RO, Vpd, 0}, + {OID_SKGE_VPD_ENTRIES_NUMBER, + 1, + 0, + SK_PNMI_MAI_OFF(VpdEntriesNumber), + SK_PNMI_RO, Vpd, 0}, + {OID_SKGE_VPD_KEY, + SK_PNMI_VPD_ENTRIES, + sizeof(SK_PNMI_VPD), + SK_PNMI_OFF(Vpd) + SK_PNMI_VPD_OFF(VpdKey), + SK_PNMI_RO, Vpd, 0}, + {OID_SKGE_VPD_VALUE, + SK_PNMI_VPD_ENTRIES, + sizeof(SK_PNMI_VPD), + SK_PNMI_OFF(Vpd) + SK_PNMI_VPD_OFF(VpdValue), + SK_PNMI_RO, Vpd, 0}, + {OID_SKGE_VPD_ACCESS, + SK_PNMI_VPD_ENTRIES, + sizeof(SK_PNMI_VPD), + SK_PNMI_OFF(Vpd) + SK_PNMI_VPD_OFF(VpdAccess), + SK_PNMI_RO, Vpd, 0}, + {OID_SKGE_VPD_ACTION, + SK_PNMI_VPD_ENTRIES, + sizeof(SK_PNMI_VPD), + SK_PNMI_OFF(Vpd) + SK_PNMI_VPD_OFF(VpdAction), + SK_PNMI_RW, Vpd, 0}, + {OID_SKGE_PORT_NUMBER, + 1, + 0, + SK_PNMI_MAI_OFF(PortNumber), + SK_PNMI_RO, General, 0}, + {OID_SKGE_DEVICE_TYPE, + 1, + 0, + SK_PNMI_MAI_OFF(DeviceType), + SK_PNMI_RO, General, 0}, + {OID_SKGE_DRIVER_DESCR, + 1, + 0, + SK_PNMI_MAI_OFF(DriverDescr), + SK_PNMI_RO, General, 0}, + {OID_SKGE_DRIVER_VERSION, + 1, + 0, + SK_PNMI_MAI_OFF(DriverVersion), + SK_PNMI_RO, General, 0}, + {OID_SKGE_HW_DESCR, + 1, + 0, + SK_PNMI_MAI_OFF(HwDescr), + SK_PNMI_RO, General, 0}, + {OID_SKGE_HW_VERSION, + 1, + 0, + SK_PNMI_MAI_OFF(HwVersion), + SK_PNMI_RO, General, 0}, + {OID_SKGE_CHIPSET, + 1, + 0, + SK_PNMI_MAI_OFF(Chipset), + SK_PNMI_RO, General, 0}, + {OID_SKGE_ACTION, + 1, + 0, + SK_PNMI_MAI_OFF(Action), + SK_PNMI_RW, Perform, 0}, + {OID_SKGE_RESULT, + 1, + 0, + SK_PNMI_MAI_OFF(TestResult), + SK_PNMI_RO, General, 0}, + {OID_SKGE_BUS_TYPE, + 1, + 0, + SK_PNMI_MAI_OFF(BusType), + SK_PNMI_RO, General, 0}, + {OID_SKGE_BUS_SPEED, + 1, + 0, + SK_PNMI_MAI_OFF(BusSpeed), + SK_PNMI_RO, General, 0}, + {OID_SKGE_BUS_WIDTH, + 1, + 0, + SK_PNMI_MAI_OFF(BusWidth), + SK_PNMI_RO, General, 0}, + {OID_SKGE_TX_SW_QUEUE_LEN, + 1, + 0, + SK_PNMI_MAI_OFF(TxSwQueueLen), + SK_PNMI_RO, General, 0}, + {OID_SKGE_TX_SW_QUEUE_MAX, + 1, + 0, + SK_PNMI_MAI_OFF(TxSwQueueMax), + SK_PNMI_RO, General, 0}, + {OID_SKGE_TX_RETRY, + 1, + 0, + SK_PNMI_MAI_OFF(TxRetryCts), + SK_PNMI_RO, General, 0}, + {OID_SKGE_RX_INTR_CTS, + 1, + 0, + SK_PNMI_MAI_OFF(RxIntrCts), + SK_PNMI_RO, General, 0}, + {OID_SKGE_TX_INTR_CTS, + 1, + 0, + SK_PNMI_MAI_OFF(TxIntrCts), + SK_PNMI_RO, General, 0}, + {OID_SKGE_RX_NO_BUF_CTS, + 1, + 0, + SK_PNMI_MAI_OFF(RxNoBufCts), + SK_PNMI_RO, General, 0}, + {OID_SKGE_TX_NO_BUF_CTS, + 1, + 0, + SK_PNMI_MAI_OFF(TxNoBufCts), + SK_PNMI_RO, General, 0}, + {OID_SKGE_TX_USED_DESCR_NO, + 1, + 0, + SK_PNMI_MAI_OFF(TxUsedDescrNo), + SK_PNMI_RO, General, 0}, + {OID_SKGE_RX_DELIVERED_CTS, + 1, + 0, + SK_PNMI_MAI_OFF(RxDeliveredCts), + SK_PNMI_RO, General, 0}, + {OID_SKGE_RX_OCTETS_DELIV_CTS, + 1, + 0, + SK_PNMI_MAI_OFF(RxOctetsDeliveredCts), + SK_PNMI_RO, General, 0}, + {OID_SKGE_RX_HW_ERROR_CTS, + 1, + 0, + SK_PNMI_MAI_OFF(RxHwErrorsCts), + SK_PNMI_RO, General, 0}, + {OID_SKGE_TX_HW_ERROR_CTS, + 1, + 0, + SK_PNMI_MAI_OFF(TxHwErrorsCts), + SK_PNMI_RO, General, 0}, + {OID_SKGE_IN_ERRORS_CTS, + 1, + 0, + SK_PNMI_MAI_OFF(InErrorsCts), + SK_PNMI_RO, General, 0}, + {OID_SKGE_OUT_ERROR_CTS, + 1, + 0, + SK_PNMI_MAI_OFF(OutErrorsCts), + SK_PNMI_RO, General, 0}, + {OID_SKGE_ERR_RECOVERY_CTS, + 1, + 0, + SK_PNMI_MAI_OFF(ErrRecoveryCts), + SK_PNMI_RO, General, 0}, + {OID_SKGE_SYSUPTIME, + 1, + 0, + SK_PNMI_MAI_OFF(SysUpTime), + SK_PNMI_RO, General, 0}, + {OID_SKGE_SENSOR_NUMBER, + 1, + 0, + SK_PNMI_MAI_OFF(SensorNumber), + SK_PNMI_RO, General, 0}, + {OID_SKGE_SENSOR_INDEX, + SK_PNMI_SENSOR_ENTRIES, + sizeof(SK_PNMI_SENSOR), + SK_PNMI_OFF(Sensor) + SK_PNMI_SEN_OFF(SensorIndex), + SK_PNMI_RO, SensorStat, 0}, + {OID_SKGE_SENSOR_DESCR, + SK_PNMI_SENSOR_ENTRIES, + sizeof(SK_PNMI_SENSOR), + SK_PNMI_OFF(Sensor) + SK_PNMI_SEN_OFF(SensorDescr), + SK_PNMI_RO, SensorStat, 0}, + {OID_SKGE_SENSOR_TYPE, + SK_PNMI_SENSOR_ENTRIES, + sizeof(SK_PNMI_SENSOR), + SK_PNMI_OFF(Sensor) + SK_PNMI_SEN_OFF(SensorType), + SK_PNMI_RO, SensorStat, 0}, + {OID_SKGE_SENSOR_VALUE, + SK_PNMI_SENSOR_ENTRIES, + sizeof(SK_PNMI_SENSOR), + SK_PNMI_OFF(Sensor) + SK_PNMI_SEN_OFF(SensorValue), + SK_PNMI_RO, SensorStat, 0}, + {OID_SKGE_SENSOR_WAR_THRES_LOW, + SK_PNMI_SENSOR_ENTRIES, + sizeof(SK_PNMI_SENSOR), + SK_PNMI_OFF(Sensor) + SK_PNMI_SEN_OFF(SensorWarningThresholdLow), + SK_PNMI_RO, SensorStat, 0}, + {OID_SKGE_SENSOR_WAR_THRES_UPP, + SK_PNMI_SENSOR_ENTRIES, + sizeof(SK_PNMI_SENSOR), + SK_PNMI_OFF(Sensor) + SK_PNMI_SEN_OFF(SensorWarningThresholdHigh), + SK_PNMI_RO, SensorStat, 0}, + {OID_SKGE_SENSOR_ERR_THRES_LOW, + SK_PNMI_SENSOR_ENTRIES, + sizeof(SK_PNMI_SENSOR), + SK_PNMI_OFF(Sensor) + SK_PNMI_SEN_OFF(SensorErrorThresholdLow), + SK_PNMI_RO, SensorStat, 0}, + {OID_SKGE_SENSOR_ERR_THRES_UPP, + SK_PNMI_SENSOR_ENTRIES, + sizeof(SK_PNMI_SENSOR), + SK_PNMI_OFF(Sensor) + SK_PNMI_SEN_OFF(SensorErrorThresholdHigh), + SK_PNMI_RO, SensorStat, 0}, + {OID_SKGE_SENSOR_STATUS, + SK_PNMI_SENSOR_ENTRIES, + sizeof(SK_PNMI_SENSOR), + SK_PNMI_OFF(Sensor) + SK_PNMI_SEN_OFF(SensorStatus), + SK_PNMI_RO, SensorStat, 0}, + {OID_SKGE_SENSOR_WAR_CTS, + SK_PNMI_SENSOR_ENTRIES, + sizeof(SK_PNMI_SENSOR), + SK_PNMI_OFF(Sensor) + SK_PNMI_SEN_OFF(SensorWarningCts), + SK_PNMI_RO, SensorStat, 0}, + {OID_SKGE_SENSOR_ERR_CTS, + SK_PNMI_SENSOR_ENTRIES, + sizeof(SK_PNMI_SENSOR), + SK_PNMI_OFF(Sensor) + SK_PNMI_SEN_OFF(SensorErrorCts), + SK_PNMI_RO, SensorStat, 0}, + {OID_SKGE_SENSOR_WAR_TIME, + SK_PNMI_SENSOR_ENTRIES, + sizeof(SK_PNMI_SENSOR), + SK_PNMI_OFF(Sensor) + SK_PNMI_SEN_OFF(SensorWarningTimestamp), + SK_PNMI_RO, SensorStat, 0}, + {OID_SKGE_SENSOR_ERR_TIME, + SK_PNMI_SENSOR_ENTRIES, + sizeof(SK_PNMI_SENSOR), + SK_PNMI_OFF(Sensor) + SK_PNMI_SEN_OFF(SensorErrorTimestamp), + SK_PNMI_RO, SensorStat, 0}, + {OID_SKGE_CHKSM_NUMBER, + 1, + 0, + SK_PNMI_MAI_OFF(ChecksumNumber), + SK_PNMI_RO, General, 0}, + {OID_SKGE_CHKSM_RX_OK_CTS, + SKCS_NUM_PROTOCOLS, + sizeof(SK_PNMI_CHECKSUM), + SK_PNMI_OFF(Checksum) + SK_PNMI_CHK_OFF(ChecksumRxOkCts), + SK_PNMI_RO, CsumStat, 0}, + {OID_SKGE_CHKSM_RX_UNABLE_CTS, + SKCS_NUM_PROTOCOLS, + sizeof(SK_PNMI_CHECKSUM), + SK_PNMI_OFF(Checksum) + SK_PNMI_CHK_OFF(ChecksumRxUnableCts), + SK_PNMI_RO, CsumStat, 0}, + {OID_SKGE_CHKSM_RX_ERR_CTS, + SKCS_NUM_PROTOCOLS, + sizeof(SK_PNMI_CHECKSUM), + SK_PNMI_OFF(Checksum) + SK_PNMI_CHK_OFF(ChecksumRxErrCts), + SK_PNMI_RO, CsumStat, 0}, + {OID_SKGE_CHKSM_TX_OK_CTS, + SKCS_NUM_PROTOCOLS, + sizeof(SK_PNMI_CHECKSUM), + SK_PNMI_OFF(Checksum) + SK_PNMI_CHK_OFF(ChecksumTxOkCts), + SK_PNMI_RO, CsumStat, 0}, + {OID_SKGE_CHKSM_TX_UNABLE_CTS, + SKCS_NUM_PROTOCOLS, + sizeof(SK_PNMI_CHECKSUM), + SK_PNMI_OFF(Checksum) + SK_PNMI_CHK_OFF(ChecksumTxUnableCts), + SK_PNMI_RO, CsumStat, 0}, + {OID_SKGE_STAT_TX, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTxOkCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX}, + {OID_SKGE_STAT_TX_OCTETS, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTxOctetsOkCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX_OCTET}, + {OID_SKGE_STAT_TX_BROADCAST, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTxBroadcastOkCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX_BROADCAST}, + {OID_SKGE_STAT_TX_MULTICAST, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTxMulticastOkCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX_MULTICAST}, + {OID_SKGE_STAT_TX_UNICAST, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTxUnicastOkCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX_UNICAST}, + {OID_SKGE_STAT_TX_LONGFRAMES, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTxLongFramesCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX_LONGFRAMES}, + {OID_SKGE_STAT_TX_BURST, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTxBurstCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX_BURST}, + {OID_SKGE_STAT_TX_PFLOWC, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTxPauseMacCtrlCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX_PMACC}, + {OID_SKGE_STAT_TX_FLOWC, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTxMacCtrlCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX_MACC}, + {OID_SKGE_STAT_TX_SINGLE_COL, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTxSingleCollisionCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX_SINGLE_COL}, + {OID_SKGE_STAT_TX_MULTI_COL, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTxMultipleCollisionCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX_MULTI_COL}, + {OID_SKGE_STAT_TX_EXCESS_COL, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTxExcessiveCollisionCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX_EXCESS_COL}, + {OID_SKGE_STAT_TX_LATE_COL, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTxLateCollisionCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX_LATE_COL}, + {OID_SKGE_STAT_TX_DEFFERAL, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTxDeferralCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX_DEFFERAL}, + {OID_SKGE_STAT_TX_EXCESS_DEF, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTxExcessiveDeferralCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX_EXCESS_DEF}, + {OID_SKGE_STAT_TX_UNDERRUN, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTxFifoUnderrunCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX_UNDERRUN}, + {OID_SKGE_STAT_TX_CARRIER, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTxCarrierCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX_CARRIER}, +/* {OID_SKGE_STAT_TX_UTIL, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTxUtilization), + SK_PNMI_RO, MacPrivateStat, (SK_U16)(-1)}, */ + {OID_SKGE_STAT_TX_64, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTx64Cts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX_64}, + {OID_SKGE_STAT_TX_127, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTx127Cts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX_127}, + {OID_SKGE_STAT_TX_255, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTx255Cts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX_255}, + {OID_SKGE_STAT_TX_511, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTx511Cts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX_511}, + {OID_SKGE_STAT_TX_1023, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTx1023Cts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX_1023}, + {OID_SKGE_STAT_TX_MAX, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTxMaxCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX_MAX}, + {OID_SKGE_STAT_TX_SYNC, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTxSyncCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX_SYNC}, + {OID_SKGE_STAT_TX_SYNC_OCTETS, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatTxSyncOctetsCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HTX_SYNC_OCTET}, + {OID_SKGE_STAT_RX, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRxOkCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX}, + {OID_SKGE_STAT_RX_OCTETS, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRxOctetsOkCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_OCTET}, + {OID_SKGE_STAT_RX_BROADCAST, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRxBroadcastOkCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_BROADCAST}, + {OID_SKGE_STAT_RX_MULTICAST, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRxMulticastOkCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_MULTICAST}, + {OID_SKGE_STAT_RX_UNICAST, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRxUnicastOkCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_UNICAST}, + {OID_SKGE_STAT_RX_PFLOWC, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRxPauseMacCtrlCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_PMACC}, + {OID_SKGE_STAT_RX_FLOWC, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRxMacCtrlCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_MACC}, + {OID_SKGE_STAT_RX_PFLOWC_ERR, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRxPauseMacCtrlErrorCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_PMACC_ERR}, + {OID_SKGE_STAT_RX_FLOWC_UNKWN, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRxMacCtrlUnknownCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_MACC_UNKWN}, + {OID_SKGE_STAT_RX_BURST, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRxBurstCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_BURST}, + {OID_SKGE_STAT_RX_MISSED, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRxMissedCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_MISSED}, + {OID_SKGE_STAT_RX_FRAMING, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRxFramingCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_FRAMING}, + {OID_SKGE_STAT_RX_OVERFLOW, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRxFifoOverflowCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_OVERFLOW}, + {OID_SKGE_STAT_RX_JABBER, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRxJabberCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_JABBER}, + {OID_SKGE_STAT_RX_CARRIER, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRxCarrierCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_CARRIER}, + {OID_SKGE_STAT_RX_IR_LENGTH, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRxIRLengthCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_IRLENGTH}, + {OID_SKGE_STAT_RX_SYMBOL, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRxSymbolCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_SYMBOL}, + {OID_SKGE_STAT_RX_SHORTS, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRxShortsCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_SHORTS}, + {OID_SKGE_STAT_RX_RUNT, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRxRuntCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_RUNT}, + {OID_SKGE_STAT_RX_CEXT, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRxCextCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_CEXT}, + {OID_SKGE_STAT_RX_TOO_LONG, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRxTooLongCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_TOO_LONG}, + {OID_SKGE_STAT_RX_FCS, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRxFcsCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_FCS}, +/* {OID_SKGE_STAT_RX_UTIL, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRxUtilization), + SK_PNMI_RO, MacPrivateStat, (SK_U16)(-1)}, */ + {OID_SKGE_STAT_RX_64, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRx64Cts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_64}, + {OID_SKGE_STAT_RX_127, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRx127Cts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_127}, + {OID_SKGE_STAT_RX_255, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRx255Cts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_255}, + {OID_SKGE_STAT_RX_511, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRx511Cts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_511}, + {OID_SKGE_STAT_RX_1023, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRx1023Cts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_1023}, + {OID_SKGE_STAT_RX_MAX, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_STAT), + SK_PNMI_OFF(Stat) + SK_PNMI_STA_OFF(StatRxMaxCts), + SK_PNMI_RO, MacPrivateStat, SK_PNMI_HRX_MAX}, + {OID_SKGE_PHYS_CUR_ADDR, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_CONF), + SK_PNMI_OFF(Conf) + SK_PNMI_CNF_OFF(ConfMacCurrentAddr), + SK_PNMI_RW, Addr, 0}, + {OID_SKGE_PHYS_FAC_ADDR, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_CONF), + SK_PNMI_OFF(Conf) + SK_PNMI_CNF_OFF(ConfMacFactoryAddr), + SK_PNMI_RO, Addr, 0}, + {OID_SKGE_PMD, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_CONF), + SK_PNMI_OFF(Conf) + SK_PNMI_CNF_OFF(ConfPMD), + SK_PNMI_RO, MacPrivateConf, 0}, + {OID_SKGE_CONNECTOR, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_CONF), + SK_PNMI_OFF(Conf) + SK_PNMI_CNF_OFF(ConfConnector), + SK_PNMI_RO, MacPrivateConf, 0}, + {OID_SKGE_LINK_CAP, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_CONF), + SK_PNMI_OFF(Conf) + SK_PNMI_CNF_OFF(ConfLinkCapability), + SK_PNMI_RO, MacPrivateConf, 0}, + {OID_SKGE_LINK_MODE, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_CONF), + SK_PNMI_OFF(Conf) + SK_PNMI_CNF_OFF(ConfLinkMode), + SK_PNMI_RW, MacPrivateConf, 0}, + {OID_SKGE_LINK_MODE_STATUS, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_CONF), + SK_PNMI_OFF(Conf) + SK_PNMI_CNF_OFF(ConfLinkModeStatus), + SK_PNMI_RO, MacPrivateConf, 0}, + {OID_SKGE_LINK_STATUS, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_CONF), + SK_PNMI_OFF(Conf) + SK_PNMI_CNF_OFF(ConfLinkStatus), + SK_PNMI_RO, MacPrivateConf, 0}, + {OID_SKGE_FLOWCTRL_CAP, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_CONF), + SK_PNMI_OFF(Conf) + SK_PNMI_CNF_OFF(ConfFlowCtrlCapability), + SK_PNMI_RO, MacPrivateConf, 0}, + {OID_SKGE_FLOWCTRL_MODE, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_CONF), + SK_PNMI_OFF(Conf) + SK_PNMI_CNF_OFF(ConfFlowCtrlMode), + SK_PNMI_RW, MacPrivateConf, 0}, + {OID_SKGE_FLOWCTRL_STATUS, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_CONF), + SK_PNMI_OFF(Conf) + SK_PNMI_CNF_OFF(ConfFlowCtrlStatus), + SK_PNMI_RO, MacPrivateConf, 0}, + {OID_SKGE_PHY_OPERATION_CAP, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_CONF), + SK_PNMI_OFF(Conf) + SK_PNMI_CNF_OFF(ConfPhyOperationCapability), + SK_PNMI_RO, MacPrivateConf, 0}, + {OID_SKGE_PHY_OPERATION_MODE, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_CONF), + SK_PNMI_OFF(Conf) + SK_PNMI_CNF_OFF(ConfPhyOperationMode), + SK_PNMI_RW, MacPrivateConf, 0}, + {OID_SKGE_PHY_OPERATION_STATUS, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_CONF), + SK_PNMI_OFF(Conf) + SK_PNMI_CNF_OFF(ConfPhyOperationStatus), + SK_PNMI_RO, MacPrivateConf, 0}, + {OID_SKGE_TRAP, + 1, + 0, + SK_PNMI_MAI_OFF(Trap), + SK_PNMI_RO, General, 0}, + {OID_SKGE_TRAP_NUMBER, + 1, + 0, + SK_PNMI_MAI_OFF(TrapNumber), + SK_PNMI_RO, General, 0}, + {OID_SKGE_RLMT_MODE, + 1, + 0, + SK_PNMI_MAI_OFF(RlmtMode), + SK_PNMI_RW, Rlmt, 0}, + {OID_SKGE_RLMT_PORT_NUMBER, + 1, + 0, + SK_PNMI_MAI_OFF(RlmtPortNumber), + SK_PNMI_RO, Rlmt, 0}, + {OID_SKGE_RLMT_PORT_ACTIVE, + 1, + 0, + SK_PNMI_MAI_OFF(RlmtPortActive), + SK_PNMI_RO, Rlmt, 0}, + {OID_SKGE_RLMT_PORT_PREFERED, + 1, + 0, + SK_PNMI_MAI_OFF(RlmtPortPreferred), + SK_PNMI_RW, Rlmt, 0}, + {OID_SKGE_RLMT_CHANGE_CTS, + 1, + 0, + SK_PNMI_MAI_OFF(RlmtChangeCts), + SK_PNMI_RO, Rlmt, 0}, + {OID_SKGE_RLMT_CHANGE_TIME, + 1, + 0, + SK_PNMI_MAI_OFF(RlmtChangeTime), + SK_PNMI_RO, Rlmt, 0}, + {OID_SKGE_RLMT_CHANGE_ESTIM, + 1, + 0, + SK_PNMI_MAI_OFF(RlmtChangeEstimate), + SK_PNMI_RO, Rlmt, 0}, + {OID_SKGE_RLMT_CHANGE_THRES, + 1, + 0, + SK_PNMI_MAI_OFF(RlmtChangeThreshold), + SK_PNMI_RW, Rlmt, 0}, + {OID_SKGE_RLMT_PORT_INDEX, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_RLMT), + SK_PNMI_OFF(Rlmt) + SK_PNMI_RLM_OFF(RlmtIndex), + SK_PNMI_RO, RlmtStat, 0}, + {OID_SKGE_RLMT_STATUS, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_RLMT), + SK_PNMI_OFF(Rlmt) + SK_PNMI_RLM_OFF(RlmtStatus), + SK_PNMI_RO, RlmtStat, 0}, + {OID_SKGE_RLMT_TX_HELLO_CTS, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_RLMT), + SK_PNMI_OFF(Rlmt) + SK_PNMI_RLM_OFF(RlmtTxHelloCts), + SK_PNMI_RO, RlmtStat, 0}, + {OID_SKGE_RLMT_RX_HELLO_CTS, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_RLMT), + SK_PNMI_OFF(Rlmt) + SK_PNMI_RLM_OFF(RlmtRxHelloCts), + SK_PNMI_RO, RlmtStat, 0}, + {OID_SKGE_RLMT_TX_SP_REQ_CTS, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_RLMT), + SK_PNMI_OFF(Rlmt) + SK_PNMI_RLM_OFF(RlmtTxSpHelloReqCts), + SK_PNMI_RO, RlmtStat, 0}, + {OID_SKGE_RLMT_RX_SP_CTS, + SK_PNMI_MAC_ENTRIES, + sizeof(SK_PNMI_RLMT), + SK_PNMI_OFF(Rlmt) + SK_PNMI_RLM_OFF(RlmtRxSpHelloCts), + SK_PNMI_RO, RlmtStat, 0}, + {OID_SKGE_RLMT_MONITOR_NUMBER, + 1, + 0, + SK_PNMI_MAI_OFF(RlmtMonitorNumber), + SK_PNMI_RO, General, 0}, + {OID_SKGE_RLMT_MONITOR_INDEX, + SK_PNMI_MONITOR_ENTRIES, + sizeof(SK_PNMI_RLMT_MONITOR), + SK_PNMI_OFF(RlmtMonitor) + SK_PNMI_MON_OFF(RlmtMonitorIndex), + SK_PNMI_RO, Monitor, 0}, + {OID_SKGE_RLMT_MONITOR_ADDR, + SK_PNMI_MONITOR_ENTRIES, + sizeof(SK_PNMI_RLMT_MONITOR), + SK_PNMI_OFF(RlmtMonitor) + SK_PNMI_MON_OFF(RlmtMonitorAddr), + SK_PNMI_RO, Monitor, 0}, + {OID_SKGE_RLMT_MONITOR_ERRS, + SK_PNMI_MONITOR_ENTRIES, + sizeof(SK_PNMI_RLMT_MONITOR), + SK_PNMI_OFF(RlmtMonitor) + SK_PNMI_MON_OFF(RlmtMonitorErrorCts), + SK_PNMI_RO, Monitor, 0}, + {OID_SKGE_RLMT_MONITOR_TIMESTAMP, + SK_PNMI_MONITOR_ENTRIES, + sizeof(SK_PNMI_RLMT_MONITOR), + SK_PNMI_OFF(RlmtMonitor) + SK_PNMI_MON_OFF(RlmtMonitorTimestamp), + SK_PNMI_RO, Monitor, 0}, + {OID_SKGE_RLMT_MONITOR_ADMIN, + SK_PNMI_MONITOR_ENTRIES, + sizeof(SK_PNMI_RLMT_MONITOR), + SK_PNMI_OFF(RlmtMonitor) + SK_PNMI_MON_OFF(RlmtMonitorAdmin), + SK_PNMI_RW, Monitor, 0}, +}; + +/* + * Table for hardware register saving on resets and port switches +*/ +static const SK_PNMI_STATADDR StatAddress[SK_PNMI_MAX_IDX] = { + /* 0 */ {TRUE, XM_TXF_OK}, + /* 1 */ {TRUE, 0}, + /* 2 */ {FALSE, 0}, + /* 3 */ {TRUE, XM_TXF_BC_OK}, + /* 4 */ {TRUE, XM_TXF_MC_OK}, + /* 5 */ {TRUE, XM_TXF_UC_OK}, + /* 6 */ {TRUE, XM_TXF_LONG}, + /* 7 */ {TRUE, XM_TXE_BURST}, + /* 8 */ {TRUE, XM_TXF_MPAUSE}, + /* 9 */ {TRUE, XM_TXF_MCTRL}, + /* 10 */ {TRUE, XM_TXF_SNG_COL}, + /* 11 */ {TRUE, XM_TXF_MUL_COL}, + /* 12 */ {TRUE, XM_TXF_ABO_COL}, + /* 13 */ {TRUE, XM_TXF_LAT_COL}, + /* 14 */ {TRUE, XM_TXF_DEF}, + /* 15 */ {TRUE, XM_TXF_EX_DEF}, + /* 16 */ {TRUE, XM_TXE_FIFO_UR}, + /* 17 */ {TRUE, XM_TXE_CS_ERR}, + /* 18 */ {FALSE, 0}, + /* 19 */ {FALSE, 0}, + /* 20 */ {TRUE, XM_TXF_64B}, + /* 21 */ {TRUE, XM_TXF_127B}, + /* 22 */ {TRUE, XM_TXF_255B}, + /* 23 */ {TRUE, XM_TXF_511B}, + /* 24 */ {TRUE, XM_TXF_1023B}, + /* 25 */ {TRUE, XM_TXF_MAX_SZ}, + /* 26 */ {FALSE, 0}, + /* 27 */ {FALSE, 0}, + /* 28 */ {FALSE, 0}, + /* 29 */ {FALSE, 0}, + /* 30 */ {FALSE, 0}, + /* 31 */ {FALSE, 0}, + /* 32 */ {TRUE, XM_RXF_OK}, + /* 33 */ {TRUE, 0}, + /* 34 */ {FALSE, 0}, + /* 35 */ {TRUE, XM_RXF_BC_OK}, + /* 36 */ {TRUE, XM_RXF_MC_OK}, + /* 37 */ {TRUE, XM_RXF_UC_OK}, + /* 38 */ {TRUE, XM_RXF_MPAUSE}, + /* 39 */ {TRUE, XM_RXF_MCTRL}, + /* 40 */ {TRUE, XM_RXF_INV_MP}, + /* 41 */ {TRUE, XM_RXF_INV_MOC}, + /* 42 */ {TRUE, XM_RXE_BURST}, + /* 43 */ {TRUE, XM_RXE_FMISS}, + /* 44 */ {TRUE, XM_RXF_FRA_ERR}, + /* 45 */ {TRUE, XM_RXE_FIFO_OV}, + /* 46 */ {TRUE, XM_RXF_JAB_PKT}, + /* 47 */ {TRUE, XM_RXE_CAR_ERR}, + /* 48 */ {TRUE, XM_RXF_LEN_ERR}, + /* 49 */ {TRUE, XM_RXE_SYM_ERR}, + /* 50 */ {TRUE, XM_RXE_SHT_ERR}, + /* 51 */ {TRUE, XM_RXE_RUNT}, + /* 52 */ {TRUE, XM_RXF_LNG_ERR}, + /* 53 */ {TRUE, XM_RXF_FCS_ERR}, + /* 54 */ {FALSE, 0}, + /* 55 */ {TRUE, XM_RXF_CEX_ERR}, + /* 56 */ {FALSE, 0}, + /* 57 */ {FALSE, 0}, + /* 58 */ {TRUE, XM_RXF_64B}, + /* 59 */ {TRUE, XM_RXF_127B}, + /* 60 */ {TRUE, XM_RXF_255B}, + /* 61 */ {TRUE, XM_RXF_511B}, + /* 62 */ {TRUE, XM_RXF_1023B}, + /* 63 */ {TRUE, XM_RXF_MAX_SZ}, + /* 64 */ {FALSE, 0}, + /* 65 */ {FALSE, 0} +}; + + +/***************************************************************************** + * + * Public functions + * + */ + +/***************************************************************************** + * + * SkPnmiInit - Init function of PNMI + * + * Description: + * SK_INIT_DATA: Initialises the data structures + * SK_INIT_IO: Resets the XMAC statistics, determines the device and + * connector type. + * SK_INIT_RUN: Starts a timer event for port switch per hour + * calculation. + * + * Returns: + * Always 0 + */ + +int SkPnmiInit( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +int Level) /* Initialization level */ +{ + unsigned int PortMax; /* Number of ports */ + unsigned int PortIndex; /* Current port index in loop */ + SK_U16 Val16; /* Multiple purpose 16 bit variable */ + SK_U8 Val8; /* Mulitple purpose 8 bit variable */ + SK_EVPARA EventParam; /* Event struct for timer event */ + + + SK_DBG_MSG(pAC, SK_DBGMOD_PNMI, SK_DBGCAT_CTRL, + ("PNMI: SkPnmiInit: Called, level=%d\n", Level)); + + switch (Level) { + + case SK_INIT_DATA: + SK_MEMSET((char *)&pAC->Pnmi, 0, sizeof(pAC->Pnmi)); + pAC->Pnmi.TrapBufFree = SK_PNMI_TRAP_QUEUE_LEN; + pAC->Pnmi.StartUpTime = SK_PNMI_HUNDREDS_SEC(SkOsGetTime(pAC)); + pAC->Pnmi.RlmtChangeThreshold = SK_PNMI_DEF_RLMT_CHG_THRES; + for (PortIndex = 0; PortIndex < SK_MAX_MACS; PortIndex ++) { + + pAC->Pnmi.Port[PortIndex].ActiveFlag = SK_FALSE; + } + break; + + case SK_INIT_IO: + /* + * Reset MAC counters + */ + PortMax = pAC->GIni.GIMacsFound; + + for (PortIndex = 0; PortIndex < PortMax; PortIndex ++) { + + Val16 = XM_SC_CLR_RXC | XM_SC_CLR_TXC; + XM_OUT16(IoC, PortIndex, XM_STAT_CMD, Val16); + /* Clear two times according to Errata #3 */ + XM_OUT16(IoC, PortIndex, XM_STAT_CMD, Val16); + } + + /* + * Get pci bus speed + */ + SK_IN16(IoC, B0_CTST, &Val16); + if ((Val16 & CS_BUS_CLOCK) == 0) { + + pAC->Pnmi.PciBusSpeed = 33; + } + else { + pAC->Pnmi.PciBusSpeed = 66; + } + + /* + * Get pci bus width + */ + SK_IN16(IoC, B0_CTST, &Val16); + if ((Val16 & CS_BUS_SLOT_SZ) == 0) { + + pAC->Pnmi.PciBusWidth = 32; + } + else { + pAC->Pnmi.PciBusWidth = 64; + } + + /* + * Get PMD and DeviceType + */ + SK_IN8(IoC, B2_PMD_TYP, &Val8); + switch (Val8) { + case 'S': + pAC->Pnmi.PMD = 3; + if (pAC->GIni.GIMacsFound > 1) { + + pAC->Pnmi.DeviceType = 0x00020002; + } + else { + pAC->Pnmi.DeviceType = 0x00020001; + } + break; + + case 'L': + pAC->Pnmi.PMD = 2; + if (pAC->GIni.GIMacsFound > 1) { + + pAC->Pnmi.DeviceType = 0x00020004; + } + else { + pAC->Pnmi.DeviceType = 0x00020003; + } + break; + + case 'C': + pAC->Pnmi.PMD = 4; + if (pAC->GIni.GIMacsFound > 1) { + + pAC->Pnmi.DeviceType = 0x00020006; + } + else { + pAC->Pnmi.DeviceType = 0x00020005; + } + break; + + case 'T': + pAC->Pnmi.PMD = 5; + if (pAC->GIni.GIMacsFound > 1) { + + pAC->Pnmi.DeviceType = 0x00020008; + } + else { + pAC->Pnmi.DeviceType = 0x00020007; + } + break; + + default : + pAC->Pnmi.PMD = 1; + pAC->Pnmi.DeviceType = 0; + break; + } + + /* + * Get connector + */ + SK_IN8(IoC, B2_CONN_TYP, &Val8); + switch (Val8) { + case 'C': + pAC->Pnmi.Connector = 2; + break; + + case 'D': + pAC->Pnmi.Connector = 3; + break; + + case 'F': + pAC->Pnmi.Connector = 4; + break; + + case 'J': + pAC->Pnmi.Connector = 5; + break; + + default: + pAC->Pnmi.Connector = 1; + break; + } + break; + + case SK_INIT_RUN: + /* + * Start timer for RLMT change counter + */ + SK_MEMSET((char *)&EventParam, 0, sizeof(EventParam)); + SkTimerStart(pAC, IoC, &pAC->Pnmi.RlmtChangeEstimate.EstTimer, + 28125000, SKGE_PNMI, SK_PNMI_EVT_CHG_EST_TIMER, + EventParam); + break; + + default: + break; /* Nothing todo */ + } + + return (0); +} + +/***************************************************************************** + * + * SkPnmiGetVar - Retrieves the value of a single OID + * + * Description: + * Calls a general sub-function for all this stuff. If the instance + * -1 is passed, the values of all instances are returned in an + * array of values. + * + * Returns: + * SK_PNMI_ERR_OK The request was successfully performed + * SK_PNMI_ERR_GENERAL A general severe internal error occured + * SK_PNMI_ERR_TOO_SHORT The passed buffer is too short to take + * the data. + * SK_PNMI_ERR_UNKNOWN_OID The requested OID is unknown + * SK_PNMI_ERR_UNKNOWN_INST The requested instance of the OID doesn't + * exist (e.g. port instance 3 on a two port + * adapter. + */ + +int SkPnmiGetVar( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +SK_U32 Id, /* Object ID that is to be processed */ +void *pBuf, /* Buffer to which to mgmt data will be retrieved */ +unsigned int *pLen, /* On call: buffer length. On return: used buffer */ +SK_U32 Instance) /* Instance (1..n) that is to be queried or -1 */ +{ + SK_DBG_MSG(pAC, SK_DBGMOD_PNMI, SK_DBGCAT_CTRL, + ("PNMI: SkPnmiGetVar: Called, Id=0x%x, BufLen=%d\n", Id, + *pLen)); + + return (PnmiVar(pAC, IoC, SK_PNMI_GET, Id, (char *)pBuf, pLen, + Instance)); +} + +/***************************************************************************** + * + * SkPnmiPreSetVar - Presets the value of a single OID + * + * Description: + * Calls a general sub-function for all this stuff. The preset does + * the same as a set, but returns just before finally setting the + * new value. This is usefull to check if a set might be successfull. + * If as instance a -1 is passed, an array of values is supposed and + * all instance of the OID will be set. + * + * Returns: + * SK_PNMI_ERR_OK The request was successfully performed. + * SK_PNMI_ERR_GENERAL A general severe internal error occured. + * SK_PNMI_ERR_TOO_SHORT The passed buffer is too short to contain + * the correct data (e.g. a 32bit value is + * needed, but a 16 bit value was passed). + * SK_PNMI_ERR_BAD_VALUE The passed value is not in the valid + * value range. + * SK_PNMI_ERR_READ_ONLY The OID is read-only and cannot be set. + * SK_PNMI_ERR_UNKNOWN_OID The requested OID is unknown. + * SK_PNMI_ERR_UNKNOWN_INST The requested instance of the OID doesn't + * exist (e.g. port instance 3 on a two port + * adapter. + */ + +int SkPnmiPreSetVar( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +SK_U32 Id, /* Object ID that is to be processed */ +void *pBuf, /* Buffer which stores the mgmt data to be set */ +unsigned int *pLen, /* Total length of mgmt data */ +SK_U32 Instance) /* Instance (1..n) that is to be set or -1 */ +{ + SK_DBG_MSG(pAC, SK_DBGMOD_PNMI, SK_DBGCAT_CTRL, + ("PNMI: SkPnmiPreSetVar: Called, Id=0x%x, BufLen=%d\n", + Id, *pLen)); + + return (PnmiVar(pAC, IoC, SK_PNMI_PRESET, Id, (char *)pBuf, pLen, + Instance)); +} + +/***************************************************************************** + * + * SkPnmiSetVar - Sets the value of a single OID + * + * Description: + * Calls a general sub-function for all this stuff. The preset does + * the same as a set, but returns just before finally setting the + * new value. This is usefull to check if a set might be successfull. + * If as instance a -1 is passed, an array of values is supposed and + * all instance of the OID will be set. + * + * Returns: + * SK_PNMI_ERR_OK The request was successfully performed. + * SK_PNMI_ERR_GENERAL A general severe internal error occured. + * SK_PNMI_ERR_TOO_SHORT The passed buffer is too short to contain + * the correct data (e.g. a 32bit value is + * needed, but a 16 bit value was passed). + * SK_PNMI_ERR_BAD_VALUE The passed value is not in the valid + * value range. + * SK_PNMI_ERR_READ_ONLY The OID is read-only and cannot be set. + * SK_PNMI_ERR_UNKNOWN_OID The requested OID is unknown. + * SK_PNMI_ERR_UNKNOWN_INST The requested instance of the OID doesn't + * exist (e.g. port instance 3 on a two port + * adapter. + */ + +int SkPnmiSetVar( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +SK_U32 Id, /* Object ID that is to be processed */ +void *pBuf, /* Buffer which stores the mgmt data to be set */ +unsigned int *pLen, /* Total length of mgmt data */ +SK_U32 Instance) /* Instance (1..n) that is to be set or -1 */ +{ + SK_DBG_MSG(pAC, SK_DBGMOD_PNMI, SK_DBGCAT_CTRL, + ("PNMI: SkPnmiSetVar: Called, Id=0x%x, BufLen=%d\n", Id, + *pLen)); + + return (PnmiVar(pAC, IoC, SK_PNMI_SET, Id, (char *)pBuf, pLen, + Instance)); +} + +/***************************************************************************** + * + * SkPnmiGetStruct - Retrieves the management database in SK_PNMI_STRUCT_DATA + * + * Description: + * Runs through the IdTable, queries the single OIDs and stores the + * returned data into the management database structure + * SK_PNMI_STRUCT_DATA. The offset of the OID in the structure + * is stored in the IdTable. The return value of the function will also + * be stored in SK_PNMI_STRUCT_DATA if the passed buffer has the + * minimum size of SK_PNMI_MIN_STRUCT_SIZE. + * + * Returns: + * SK_PNMI_ERR_OK The request was successfully performed + * SK_PNMI_ERR_GENERAL A general severe internal error occured + * SK_PNMI_ERR_TOO_SHORT The passed buffer is too short to take + * the data. + */ + +int SkPnmiGetStruct( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +void *pBuf, /* Buffer which will store the retrieved data */ +unsigned int *pLen) /* Length of buffer */ +{ + int Ret; + unsigned int TableIndex; + unsigned int DstOffset; + unsigned int InstanceNo; + unsigned int InstanceCnt; + SK_U32 Instance; + unsigned int TmpLen; + char KeyArr[SK_PNMI_VPD_ARR_SIZE][SK_PNMI_VPD_STR_SIZE]; + + + SK_DBG_MSG(pAC, SK_DBGMOD_PNMI, SK_DBGCAT_CTRL, + ("PNMI: SkPnmiGetStruct: Called, BufLen=%d\n", *pLen)); + + if (*pLen < SK_PNMI_STRUCT_SIZE) { + + if (*pLen >= SK_PNMI_MIN_STRUCT_SIZE) { + + SK_PNMI_SET_STAT(pBuf, SK_PNMI_ERR_TOO_SHORT, + (SK_U32)(-1)); + } + + *pLen = SK_PNMI_STRUCT_SIZE; + return (SK_PNMI_ERR_TOO_SHORT); + } + + /* Update statistic */ + SK_PNMI_CHECKFLAGS("SkPnmiGetStruct: On call"); + + if ((Ret = MacUpdate(pAC, IoC, 0, pAC->GIni.GIMacsFound - 1)) != + SK_PNMI_ERR_OK) { + + SK_PNMI_SET_STAT(pBuf, Ret, (SK_U32)(-1)); + *pLen = SK_PNMI_MIN_STRUCT_SIZE; + return (Ret); + } + + if ((Ret = RlmtUpdate(pAC, IoC)) != SK_PNMI_ERR_OK) { + + SK_PNMI_SET_STAT(pBuf, Ret, (SK_U32)(-1)); + *pLen = SK_PNMI_MIN_STRUCT_SIZE; + return (Ret); + } + + if ((Ret = SirqUpdate(pAC, IoC)) != SK_PNMI_ERR_OK) { + + SK_PNMI_SET_STAT(pBuf, Ret, (SK_U32)(-1)); + *pLen = SK_PNMI_MIN_STRUCT_SIZE; + return (Ret); + } + + /* + * Increment semaphores to indicate that an update was + * already done + */ + pAC->Pnmi.MacUpdatedFlag ++; + pAC->Pnmi.RlmtUpdatedFlag ++; + pAC->Pnmi.SirqUpdatedFlag ++; + + /* Get vpd keys for instance calculation */ + Ret = GetVpdKeyArr(pAC, IoC, &KeyArr[0][0], sizeof(KeyArr), &TmpLen); + if (Ret != SK_PNMI_ERR_OK) { + + pAC->Pnmi.MacUpdatedFlag --; + pAC->Pnmi.RlmtUpdatedFlag --; + pAC->Pnmi.SirqUpdatedFlag --; + + SK_PNMI_CHECKFLAGS("SkPnmiGetStruct: On return"); + SK_PNMI_SET_STAT(pBuf, Ret, (SK_U32)(-1)); + *pLen = SK_PNMI_MIN_STRUCT_SIZE; + return (SK_PNMI_ERR_GENERAL); + } + + /* Retrieve values */ + SK_MEMSET((char *)pBuf, 0, SK_PNMI_STRUCT_SIZE); + for (TableIndex = 0; TableIndex < sizeof(IdTable)/sizeof(IdTable[0]); + TableIndex ++) { + + InstanceNo = IdTable[TableIndex].InstanceNo; + + for (InstanceCnt = 1; InstanceCnt <= InstanceNo; + InstanceCnt ++) { + + DstOffset = IdTable[TableIndex].Offset + + (InstanceCnt - 1) * + IdTable[TableIndex].StructSize; + + /* + * For the VPD the instance is not an index number + * but the key itself. Determin with the instance + * counter the VPD key to be used. + */ + if (IdTable[TableIndex].Id == OID_SKGE_VPD_KEY || + IdTable[TableIndex].Id == OID_SKGE_VPD_VALUE || + IdTable[TableIndex].Id == OID_SKGE_VPD_ACCESS || + IdTable[TableIndex].Id == OID_SKGE_VPD_ACTION) { + + SK_PNMI_READ_U32(KeyArr[InstanceCnt - 1], + Instance); + } + else { + Instance = (SK_U32)InstanceCnt; + } + + TmpLen = *pLen - DstOffset; + Ret = IdTable[TableIndex].Func(pAC, IoC, SK_PNMI_GET, + IdTable[TableIndex].Id, (char *)pBuf + + DstOffset, &TmpLen, Instance, TableIndex); + + /* + * An unknown instance error means that we reached + * the last instance of that variable. Proceed with + * the next OID in the table and ignore the return + * code. + */ + if (Ret == SK_PNMI_ERR_UNKNOWN_INST) { + + break; + } + + if (Ret != SK_PNMI_ERR_OK) { + + pAC->Pnmi.MacUpdatedFlag --; + pAC->Pnmi.RlmtUpdatedFlag --; + pAC->Pnmi.SirqUpdatedFlag --; + + SK_PNMI_CHECKFLAGS("SkPnmiGetStruct: On return"); + SK_PNMI_SET_STAT(pBuf, Ret, DstOffset); + *pLen = SK_PNMI_MIN_STRUCT_SIZE; + return (Ret); + } + } + } + + pAC->Pnmi.MacUpdatedFlag --; + pAC->Pnmi.RlmtUpdatedFlag --; + pAC->Pnmi.SirqUpdatedFlag --; + + *pLen = SK_PNMI_STRUCT_SIZE; + SK_PNMI_CHECKFLAGS("SkPnmiGetStruct: On return"); + SK_PNMI_SET_STAT(pBuf, SK_PNMI_ERR_OK, (SK_U32)(-1)); + return (SK_PNMI_ERR_OK); +} + +/***************************************************************************** + * + * SkPnmiPreSetStruct - Presets the management database in SK_PNMI_STRUCT_DATA + * + * Description: + * Calls a general sub-function for all this set stuff. The preset does + * the same as a set, but returns just before finally setting the + * new value. This is usefull to check if a set might be successfull. + * The sub-function runs through the IdTable, checks which OIDs are able + * to set, and calls the handler function of the OID to perform the + * preset. The return value of the function will also be stored in + * SK_PNMI_STRUCT_DATA if the passed buffer has the minimum size of + * SK_PNMI_MIN_STRUCT_SIZE. + * + * Returns: + * SK_PNMI_ERR_OK The request was successfully performed. + * SK_PNMI_ERR_GENERAL A general severe internal error occured. + * SK_PNMI_ERR_TOO_SHORT The passed buffer is too short to contain + * the correct data (e.g. a 32bit value is + * needed, but a 16 bit value was passed). + * SK_PNMI_ERR_BAD_VALUE The passed value is not in the valid + * value range. + */ + +int SkPnmiPreSetStruct( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +void *pBuf, /* Buffer which contains the data to be set */ +unsigned int *pLen) /* Length of buffer */ +{ + SK_DBG_MSG(pAC, SK_DBGMOD_PNMI, SK_DBGCAT_CTRL, + ("PNMI: SkPnmiPreSetStruct: Called, BufLen=%d\n", *pLen)); + + return (PnmiStruct(pAC, IoC, SK_PNMI_PRESET, (char *)pBuf, pLen)); +} + +/***************************************************************************** + * + * SkPnmiSetStruct - Sets the management database in SK_PNMI_STRUCT_DATA + * + * Description: + * Calls a general sub-function for all this set stuff. The return value + * of the function will also be stored in SK_PNMI_STRUCT_DATA if the + * passed buffer has the minimum size of SK_PNMI_MIN_STRUCT_SIZE. + * The sub-function runs through the IdTable, checks which OIDs are able + * to set, and calls the handler function of the OID to perform the + * set. The return value of the function will also be stored in + * SK_PNMI_STRUCT_DATA if the passed buffer has the minimum size of + * SK_PNMI_MIN_STRUCT_SIZE. + * + * Returns: + * SK_PNMI_ERR_OK The request was successfully performed. + * SK_PNMI_ERR_GENERAL A general severe internal error occured. + * SK_PNMI_ERR_TOO_SHORT The passed buffer is too short to contain + * the correct data (e.g. a 32bit value is + * needed, but a 16 bit value was passed). + * SK_PNMI_ERR_BAD_VALUE The passed value is not in the valid + * value range. + */ + +int SkPnmiSetStruct( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +void *pBuf, /* Buffer which contains the data to be set */ +unsigned int *pLen) /* Length of buffer */ +{ + SK_DBG_MSG(pAC, SK_DBGMOD_PNMI, SK_DBGCAT_CTRL, + ("PNMI: SkPnmiSetStruct: Called, BufLen=%d\n", *pLen)); + + return (PnmiStruct(pAC, IoC, SK_PNMI_SET, (char *)pBuf, pLen)); +} + +/***************************************************************************** + * + * SkPnmiEvent - Event handler + * + * Description: + * Handles the following events: + * SK_PNMI_EVT_SIRQ_OVERFLOW When a hardware counter overflows an + * interrupt will be generated which is + * first handled by SIRQ which generates a + * this event. The event increments the + * upper 32 bit of the 64 bit counter. + * SK_PNMI_EVT_SEN_XXX The event is generated by the I2C module + * when a sensor reports a warning or + * error. The event will store a trap + * message in the trap buffer. + * SK_PNMI_EVT_CHG_EST_TIMER The timer event was initiated by this + * module and is used to calculate the + * port switches per hour. + * SK_PNMI_EVT_CLEAR_COUNTER The event clears all counters and + * timestamps. + * SK_PNMI_EVT_XMAC_RESET The event is generated by the driver + * before a hard reset of the XMAC is + * performed. All counters will be saved + * and added to the hardware counter + * values after reset to grant continuous + * counter values. + * SK_PNMI_EVT_RLMT_PORT_UP Generated by RLMT to notify that a port + * went logically up. A trap message will + * be stored to the trap buffer. + * SK_PNMI_EVT_RLMT_PORT_DOWN Generated by RLMT to notify that a port + * went logically down. A trap message will + * be stored to the trap buffer. + * SK_PNMI_EVT_RLMT_PORT_SWITCH Generated by RLMT to notify that the + * active port switched. PNMI will split + * this into two message ACTIVE_DOWN and + * ACTIVE_UP to be future compatible with + * load balancing and card fail over. + * SK_PNMI_EVT_RLMT_SEGMENTATION Generated by RLMT to notify that two + * spanning tree root bridges were + * detected. A trap message will be stored + * to the trap buffer. + * SK_PNMI_EVT_RLMT_ACTIVE_DOWN Notifies PNMI that an active port went + * down. PNMI will not further add the + * statistic values to the virtual port. + * SK_PNMI_EVT_RLMT_ACTIVE_UP Notifies PNMI that a port went up and + * is now an active port. PNMI will now + * add the statistic data of this port to + * the virtual port. + * + * Returns: + * Always 0 + */ + +int SkPnmiEvent( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +SK_U32 Event, /* Event-Id */ +SK_EVPARA Param) /* Event dependent parameter */ +{ + unsigned int PhysPortIndex; + int CounterIndex; + int Ret; + SK_U16 MacStatus; + SK_U64 OverflowStatus; + SK_U64 Mask; + SK_U32 MacCntEvent; + SK_U64 Value; + SK_U16 Register; + SK_EVPARA EventParam; + SK_U64 NewestValue; + SK_U64 OldestValue; + SK_U64 Delta; + SK_PNMI_ESTIMATE *pEst; + + +#ifdef DEBUG + if (Event != SK_PNMI_EVT_XMAC_RESET) { + + SK_DBG_MSG(pAC, SK_DBGMOD_PNMI, SK_DBGCAT_CTRL, + ("PNMI: SkPnmiEvent: Called, Event=0x%x, Param=0x%x\n", + (unsigned long)Event, (unsigned long)Param.Para64)); + } +#endif + SK_PNMI_CHECKFLAGS("SkPnmiEvent: On call"); + + switch (Event) { + + case SK_PNMI_EVT_SIRQ_OVERFLOW: + PhysPortIndex = (int)Param.Para32[0]; + MacStatus = (SK_U16)Param.Para32[1]; +#ifdef DEBUG + if (PhysPortIndex >= SK_MAX_MACS) { + + SK_DBG_MSG(pAC, SK_DBGMOD_PNMI, SK_DBGCAT_CTRL, + ("PNMI: ERR: SkPnmiEvent: SK_PNMI_EVT_SIRQ_OVERFLOW parameter wrong, PhysPortIndex=0x%x\n", + PhysPortIndex)); + return (0); + } +#endif + OverflowStatus = 0; + + /* + * Check which source caused an overflow interrupt. The + * interrupt source is a self-clearing register. We only + * need to check the interrupt source once. Another check + * will be done by the SIRQ module to be sure that no + * interrupt get lost during process time. + */ + if ((MacStatus & XM_IS_RXC_OV) == XM_IS_RXC_OV) { + + XM_IN32(IoC, PhysPortIndex, XM_RX_CNT_EV, + &MacCntEvent); + OverflowStatus |= (SK_U64)MacCntEvent << 32; + } + if ((MacStatus & XM_IS_TXC_OV) == XM_IS_TXC_OV) { + + XM_IN32(IoC, PhysPortIndex, XM_TX_CNT_EV, + &MacCntEvent); + OverflowStatus |= (SK_U64)MacCntEvent; + } + if (OverflowStatus == 0) { + + SK_PNMI_CHECKFLAGS("SkPnmiEvent: On return"); + return (0); + } + + /* + * Check the overflow status register and increment + * the upper dword of corresponding counter. + */ + for (CounterIndex = 0; CounterIndex < sizeof(Mask) * 8; + CounterIndex ++) { + + Mask = (SK_U64)1 << CounterIndex; + if ((OverflowStatus & Mask) == 0) { + + continue; + } + + switch (CounterIndex) { + + case SK_PNMI_HTX_UTILUNDER: + case SK_PNMI_HTX_UTILOVER: + XM_IN16(IoC, PhysPortIndex, XM_TX_CMD, + &Register); + Register |= XM_TX_SAM_LINE; + XM_OUT16(IoC, PhysPortIndex, XM_TX_CMD, + Register); + break; + + case SK_PNMI_HRX_UTILUNDER: + case SK_PNMI_HRX_UTILOVER: + XM_IN16(IoC, PhysPortIndex, XM_RX_CMD, + &Register); + Register |= XM_RX_SAM_LINE; + XM_OUT16(IoC, PhysPortIndex, XM_RX_CMD, + Register); + break; + + case SK_PNMI_HTX_OCTETHIGH: + case SK_PNMI_HTX_OCTETLOW: + case SK_PNMI_HTX_RESERVED26: + case SK_PNMI_HTX_RESERVED27: + case SK_PNMI_HTX_RESERVED28: + case SK_PNMI_HTX_RESERVED29: + case SK_PNMI_HTX_RESERVED30: + case SK_PNMI_HTX_RESERVED31: + case SK_PNMI_HRX_OCTETHIGH: + case SK_PNMI_HRX_OCTETLOW: + case SK_PNMI_HRX_IRLENGTH: + case SK_PNMI_HRX_RESERVED22: + case SK_PNMI_HTX_SYNC: + case SK_PNMI_HTX_SYNC_OCTET: + break; + + default: + pAC->Pnmi.Port[PhysPortIndex]. + CounterHigh[CounterIndex] ++; + } + } + break; + + case SK_PNMI_EVT_SEN_WAR_LOW: +#ifdef DEBUG + if ((unsigned int)Param.Para64 >= (unsigned int)pAC->I2c.MaxSens) { + + SK_DBG_MSG(pAC, SK_DBGMOD_PNMI, SK_DBGCAT_CTRL, + ("PNMI: ERR: SkPnmiEvent: SK_PNMI_EVT_SEN_WAR_LOW parameter wrong, SensorIndex=%d\n", + (unsigned int)Param.Para64)); + return (0); + } +#endif + /* + * Store a trap message in the trap buffer and generate + * an event for user space applications with the + * SK_DRIVER_SENDEVENT macro. + */ + QueueSensorTrap(pAC, OID_SKGE_TRAP_SEN_WAR_LOW, + (unsigned int)Param.Para64); + (void)SK_DRIVER_SENDEVENT(pAC, IoC); + break; + + case SK_PNMI_EVT_SEN_WAR_UPP: +#ifdef DEBUG + if ((unsigned int)Param.Para64 >= (unsigned int)pAC->I2c.MaxSens) { + + SK_DBG_MSG(pAC, SK_DBGMOD_PNMI, SK_DBGCAT_CTRL, + ("PNMI: ERR:SkPnmiEvent: SK_PNMI_EVT_SEN_WAR_UPP parameter wrong, SensorIndex=%d\n", + (unsigned int)Param.Para64)); + return (0); + } +#endif + /* + * Store a trap message in the trap buffer and generate + * an event for user space applications with the + * SK_DRIVER_SENDEVENT macro. + */ + QueueSensorTrap(pAC, OID_SKGE_TRAP_SEN_WAR_UPP, + (unsigned int)Param.Para64); + (void)SK_DRIVER_SENDEVENT(pAC, IoC); + break; + + case SK_PNMI_EVT_SEN_ERR_LOW: +#ifdef DEBUG + if ((unsigned int)Param.Para64 >= (unsigned int)pAC->I2c.MaxSens) { + + SK_DBG_MSG(pAC, SK_DBGMOD_PNMI, SK_DBGCAT_CTRL, + ("PNMI: ERR: SkPnmiEvent: SK_PNMI_EVT_SEN_ERR_LOW parameter wrong, SensorIndex=%d\n", + (unsigned int)Param.Para64)); + return (0); + } +#endif + /* + * Store a trap message in the trap buffer and generate + * an event for user space applications with the + * SK_DRIVER_SENDEVENT macro. + */ + QueueSensorTrap(pAC, OID_SKGE_TRAP_SEN_ERR_LOW, + (unsigned int)Param.Para64); + (void)SK_DRIVER_SENDEVENT(pAC, IoC); + break; + + case SK_PNMI_EVT_SEN_ERR_UPP: +#ifdef DEBUG + if ((unsigned int)Param.Para64 >= (unsigned int)pAC->I2c.MaxSens) { + + SK_DBG_MSG(pAC, SK_DBGMOD_PNMI, SK_DBGCAT_CTRL, + ("PNMI: ERR: SkPnmiEvent: SK_PNMI_EVT_SEN_ERR_UPP parameter wrong, SensorIndex=%d\n", + (unsigned int)Param.Para64)); + return (0); + } +#endif + /* + * Store a trap message in the trap buffer and generate + * an event for user space applications with the + * SK_DRIVER_SENDEVENT macro. + */ + QueueSensorTrap(pAC, OID_SKGE_TRAP_SEN_ERR_UPP, + (unsigned int)Param.Para64); + (void)SK_DRIVER_SENDEVENT(pAC, IoC); + break; + + case SK_PNMI_EVT_CHG_EST_TIMER: + /* + * Calculate port switch average on a per hour basis + * Time interval for check : 28125 ms + * Number of values for average : 8 + * + * Be careful in changing these values, on change check + * - typedef of SK_PNMI_ESTIMATE (Size of EstValue + * array one less than value number) + * - Timer initilization SkTimerStart() in SkPnmiInit + * - Delta value below must be multiplicated with + * power of 2 + * + */ + pEst = &pAC->Pnmi.RlmtChangeEstimate; + CounterIndex = pEst->EstValueIndex + 1; + if (CounterIndex == 7) { + + CounterIndex = 0; + } + pEst->EstValueIndex = CounterIndex; + + NewestValue = pAC->Pnmi.RlmtChangeCts; + OldestValue = pEst->EstValue[CounterIndex]; + pEst->EstValue[CounterIndex] = NewestValue; + + /* + * Calculate average. Delta stores the number of + * port switches per 28125 * 8 = 225000 ms + */ + if (NewestValue >= OldestValue) { + + Delta = NewestValue - OldestValue; + } + else { + /* Overflow situation */ + Delta = (SK_U64)(0 - OldestValue) + NewestValue; + } + + /* + * Extrapolate delta to port switches per hour. + * Estimate = Delta * (3600000 / 225000) + * = Delta * 16 + * = Delta << 4 + */ + pAC->Pnmi.RlmtChangeEstimate.Estimate = Delta << 4; + + /* + * Check if threshold is exceeded. If the threshold is + * permanently exceeded every 28125 ms an event will be + * generated to remind the user of this condition. + */ + if ((pAC->Pnmi.RlmtChangeThreshold != 0) && + (pAC->Pnmi.RlmtChangeEstimate.Estimate >= + pAC->Pnmi.RlmtChangeThreshold)) { + + QueueSimpleTrap(pAC, OID_SKGE_TRAP_RLMT_CHANGE_THRES); + (void)SK_DRIVER_SENDEVENT(pAC, IoC); + } + + SK_MEMSET((char *)&EventParam, 0, sizeof(EventParam)); + SkTimerStart(pAC, IoC, &pAC->Pnmi.RlmtChangeEstimate.EstTimer, + 28125000, SKGE_PNMI, SK_PNMI_EVT_CHG_EST_TIMER, + EventParam); + break; + + case SK_PNMI_EVT_CLEAR_COUNTER: + /* + * Set all counters and timestamps to zero + */ + ResetCounter(pAC, IoC); + break; + + case SK_PNMI_EVT_XMAC_RESET: + /* + * To grant continuous counter values store the current + * XMAC statistic values to the entries 1..n of the + * CounterOffset array. XMAC Errata #2 + */ +#ifdef DEBUG + if ((unsigned int)Param.Para64 >= SK_MAX_MACS) { + + SK_DBG_MSG(pAC, SK_DBGMOD_PNMI, SK_DBGCAT_CTRL, + ("PNMI: ERR: SkPnmiEvent: SK_PNMI_EVT_XMAC_RESET parameter wrong, PhysPortIndex=%d\n", + (unsigned int)Param.Para64)); + return (0); + } +#endif + PhysPortIndex = (unsigned int)Param.Para64; + + /* + * Update XMAC statistic to get fresh values + */ + Ret = MacUpdate(pAC, IoC, 0, pAC->GIni.GIMacsFound - 1); + if (Ret != SK_PNMI_ERR_OK) { + + SK_PNMI_CHECKFLAGS("SkPnmiEvent: On return"); + return (0); + } + /* + * Increment semaphore to indicate that an update was + * already done + */ + pAC->Pnmi.MacUpdatedFlag ++; + + for (CounterIndex = 0; CounterIndex < SK_PNMI_MAX_IDX; + CounterIndex ++) { + + if (!StatAddress[CounterIndex].GetOffset) { + + continue; + } + + pAC->Pnmi.Port[PhysPortIndex]. + CounterOffset[CounterIndex] = GetPhysStatVal( + pAC, IoC, PhysPortIndex, CounterIndex); + pAC->Pnmi.Port[PhysPortIndex]. + CounterHigh[CounterIndex] = 0; + } + + pAC->Pnmi.MacUpdatedFlag --; + break; + + case SK_PNMI_EVT_RLMT_PORT_UP: +#ifdef DEBUG + if ((unsigned int)Param.Para32[0] >= SK_MAX_MACS) { + + SK_DBG_MSG(pAC, SK_DBGMOD_PNMI, SK_DBGCAT_CTRL, + ("PNMI: ERR: SkPnmiEvent: SK_PNMI_EVT_RLMT_PORT_UP parameter wrong, PhysPortIndex=%d\n", + (unsigned int)Param.Para32[0])); + + return (0); + } +#endif + /* + * Store a trap message in the trap buffer and generate + * an event for user space applications with the + * SK_DRIVER_SENDEVENT macro. + */ + QueueRlmtPortTrap(pAC, OID_SKGE_TRAP_RLMT_PORT_UP, + (unsigned int)Param.Para32[0]); + (void)SK_DRIVER_SENDEVENT(pAC, IoC); + break; + + case SK_PNMI_EVT_RLMT_PORT_DOWN: +#ifdef DEBUG + if ((unsigned int)Param.Para32[0] >= SK_MAX_MACS) { + + SK_DBG_MSG(pAC, SK_DBGMOD_PNMI, SK_DBGCAT_CTRL, + ("PNMI: ERR: SkPnmiEvent: SK_PNMI_EVT_RLMT_PORT_DOWN parameter wrong, PhysPortIndex=%d\n", + (unsigned int)Param.Para32[0])); + + return (0); + } +#endif + /* + * Store a trap message in the trap buffer and generate + * an event for user space applications with the + * SK_DRIVER_SENDEVENT macro. + */ + QueueRlmtPortTrap(pAC, OID_SKGE_TRAP_RLMT_PORT_DOWN, + (unsigned int)Param.Para32[0]); + (void)SK_DRIVER_SENDEVENT(pAC, IoC); + break; + + case SK_PNMI_EVT_RLMT_ACTIVE_DOWN: + PhysPortIndex = (unsigned int)Param.Para32[0]; +#ifdef DEBUG + if (PhysPortIndex >= SK_MAX_MACS) { + + SK_DBG_MSG(pAC, SK_DBGMOD_PNMI, SK_DBGCAT_CTRL, + ("PNMI: ERR: SkPnmiEvent: SK_PNMI_EVT_RLMT_ACTIVE_DOWN parameter too high, PhysPort=%d\n", + PhysPortIndex)); + } +#endif + /* + * Nothing to do if port is already inactive + */ + if (!pAC->Pnmi.Port[PhysPortIndex].ActiveFlag) { + + return (0); + } + + /* + * Update statistic counters to calculate new offset + * for the virtual port and increment semaphore to + * indicate that an update was already done. + */ + if (MacUpdate(pAC, IoC, 0, pAC->GIni.GIMacsFound - 1) != + SK_PNMI_ERR_OK) { + + SK_PNMI_CHECKFLAGS("SkPnmiEvent: On return"); + return (0); + } + pAC->Pnmi.MacUpdatedFlag ++; + + /* + * Calculate new counter offset for virtual port to + * grant continous counting on port switches. The virtual + * port consists of all currently active ports. The port + * down event indicates that a port is removed fromt the + * virtual port. Therefore add the counter value of the + * removed port to the CounterOffset for the virtual port + * to grant the same counter value. + */ + for (CounterIndex = 0; CounterIndex < SK_PNMI_MAX_IDX; + CounterIndex ++) { + + if (!StatAddress[CounterIndex].GetOffset) { + + continue; + } + + Value = GetPhysStatVal(pAC, IoC, PhysPortIndex, + CounterIndex); + + pAC->Pnmi.VirtualCounterOffset[CounterIndex] += Value; + } + + /* + * Set port to inactive + */ + pAC->Pnmi.Port[PhysPortIndex].ActiveFlag = SK_FALSE; + + pAC->Pnmi.MacUpdatedFlag --; + break; + + case SK_PNMI_EVT_RLMT_ACTIVE_UP: + PhysPortIndex = (unsigned int)Param.Para32[0]; +#ifdef DEBUG + if (PhysPortIndex >= SK_MAX_MACS) { + + SK_DBG_MSG(pAC, SK_DBGMOD_PNMI, SK_DBGCAT_CTRL, + ("PNMI: ERR: SkPnmiEvent: SK_PNMI_EVT_RLMT_ACTIVE_UP parameter too high, PhysPort=%d\n", + PhysPortIndex)); + } +#endif + /* + * Nothing to do if port is already active + */ + if (pAC->Pnmi.Port[PhysPortIndex].ActiveFlag) { + + return (0); + } + + /* + * Statistic maintanence + */ + pAC->Pnmi.RlmtChangeCts ++; + pAC->Pnmi.RlmtChangeTime = + SK_PNMI_HUNDREDS_SEC(SkOsGetTime(pAC)); + + /* + * Store a trap message in the trap buffer and generate + * an event for user space applications with the + * SK_DRIVER_SENDEVENT macro. + */ + QueueRlmtNewMacTrap(pAC, PhysPortIndex); + (void)SK_DRIVER_SENDEVENT(pAC, IoC); + + /* + * Update statistic counters to calculate new offset + * for the virtual port and increment semaphore to indicate + * that an update was already done. + */ + if (MacUpdate(pAC, IoC, 0, pAC->GIni.GIMacsFound - 1) != + SK_PNMI_ERR_OK) { + + SK_PNMI_CHECKFLAGS("SkPnmiEvent: On return"); + return (0); + } + pAC->Pnmi.MacUpdatedFlag ++; + + /* + * Calculate new counter offset for virtual port to + * grant continous counting on port switches. A new port + * is added to the virtual port. Therefore substract the + * counter value of the new port from the CounterOffset + * for the virtual port to grant the same value. + */ + for (CounterIndex = 0; CounterIndex < SK_PNMI_MAX_IDX; + CounterIndex ++) { + + if (!StatAddress[CounterIndex].GetOffset) { + + continue; + } + + Value = GetPhysStatVal(pAC, IoC, PhysPortIndex, + CounterIndex); + + pAC->Pnmi.VirtualCounterOffset[CounterIndex] -= Value; + } + + /* + * Set port to active + */ + pAC->Pnmi.Port[PhysPortIndex].ActiveFlag = SK_TRUE; + + pAC->Pnmi.MacUpdatedFlag --; + break; + + case SK_PNMI_EVT_RLMT_PORT_SWITCH: + /* + * This event becomes obsolete if RLMT generates directly + * the events SK_PNMI_EVT_RLMT_ACTIVE_DOWN and + * SK_PNMI_EVT_RLMT_ACTIVE_UP. The events are here emulated. + * PNMI handles that multiple ports may become active. + * Increment semaphore to indicate that an update was + * already done. + */ + if (MacUpdate(pAC, IoC, 0, pAC->GIni.GIMacsFound - 1) != + SK_PNMI_ERR_OK) { + + SK_PNMI_CHECKFLAGS("SkPnmiEvent: On return"); + return (0); + } + pAC->Pnmi.MacUpdatedFlag ++; + + SkPnmiEvent(pAC, IoC, SK_PNMI_EVT_RLMT_ACTIVE_DOWN, Param); + Param.Para32[0] = Param.Para32[1]; + SkPnmiEvent(pAC, IoC, SK_PNMI_EVT_RLMT_ACTIVE_UP, Param); + + pAC->Pnmi.MacUpdatedFlag --; + break; + + case SK_PNMI_EVT_RLMT_SEGMENTATION: + /* + * Store a trap message in the trap buffer and generate + * an event for user space applications with the + * SK_DRIVER_SENDEVENT macro. + */ + QueueSimpleTrap(pAC, OID_SKGE_TRAP_RLMT_SEGMENTATION); + (void)SK_DRIVER_SENDEVENT(pAC, IoC); + break; + + default: + break; + } + + SK_PNMI_CHECKFLAGS("SkPnmiEvent: On return"); + return (0); +} + + +/****************************************************************************** + * + * Private functions + * + */ + +/***************************************************************************** + * + * PnmiVar - Gets, presets, and sets single OIDs + * + * Description: + * Looks up the requested OID, calls the corresponding handler + * function, and passes the parameters with the get, preset, or + * set command. The function is called by SkGePnmiGetVar, + * SkGePnmiPreSetVar, or SkGePnmiSetVar. + * + * Returns: + * SK_PNMI_ERR_XXX. For details have a look to the description of the + * calling functions. + */ + +static int PnmiVar( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +int Action, /* Get/PreSet/Set action */ +SK_U32 Id, /* Object ID that is to be processed */ +char *pBuf, /* Buffer which stores the mgmt data to be set */ +unsigned int *pLen, /* Total length of mgmt data */ +SK_U32 Instance) /* Instance (1..n) that is to be set or -1 */ +{ + unsigned int TableIndex; + int Ret; + + + if ((TableIndex = LookupId(Id)) == (unsigned int)(-1)) { + + *pLen = 0; + return (SK_PNMI_ERR_UNKNOWN_OID); + } + + SK_PNMI_CHECKFLAGS("PnmiVar: On call"); + + Ret = IdTable[TableIndex].Func(pAC, IoC, Action, Id, pBuf, pLen, + Instance, TableIndex); + + SK_PNMI_CHECKFLAGS("PnmiVar: On return"); + + return (Ret); +} + +/***************************************************************************** + * + * PnmiStruct - Presets and Sets data in structure SK_PNMI_STRUCT_DATA + * + * Description: + * The return value of the function will also be stored in + * SK_PNMI_STRUCT_DATA if the passed buffer has the minimum size of + * SK_PNMI_MIN_STRUCT_SIZE. The sub-function runs through the IdTable, + * checks which OIDs are able to set, and calls the handler function of + * the OID to perform the set. The return value of the function will + * also be stored in SK_PNMI_STRUCT_DATA if the passed buffer has the + * minimum size of SK_PNMI_MIN_STRUCT_SIZE. The function is called + * by SkGePnmiPreSetStruct and SkGePnmiSetStruct. + * + * Returns: + * SK_PNMI_ERR_XXX. The codes are described in the calling functions. + */ + +static int PnmiStruct( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +int Action, /* Set action to be performed */ +char *pBuf, /* Buffer which contains the data to be set */ +unsigned int *pLen) /* Length of buffer */ +{ + int Ret; + unsigned int TableIndex; + unsigned int DstOffset; + unsigned int Len; + unsigned int InstanceNo; + unsigned int InstanceCnt; + SK_U32 Instance; + SK_U32 Id; + + + /* Check if the passed buffer has the right size */ + if (*pLen < SK_PNMI_STRUCT_SIZE) { + + /* Check if we can return the error within the buffer */ + if (*pLen >= SK_PNMI_MIN_STRUCT_SIZE) { + + SK_PNMI_SET_STAT(pBuf, SK_PNMI_ERR_TOO_SHORT, + (SK_U32)(-1)); + } + + *pLen = SK_PNMI_STRUCT_SIZE; + return (SK_PNMI_ERR_TOO_SHORT); + } + + SK_PNMI_CHECKFLAGS("PnmiStruct: On call"); + + /* + * Update the values of RLMT and SIRQ and increment semaphores to + * indicate that an update was already done. + */ + if ((Ret = RlmtUpdate(pAC, IoC)) != SK_PNMI_ERR_OK) { + + SK_PNMI_SET_STAT(pBuf, Ret, (SK_U32)(-1)); + *pLen = SK_PNMI_MIN_STRUCT_SIZE; + return (Ret); + } + + if ((Ret = SirqUpdate(pAC, IoC)) != SK_PNMI_ERR_OK) { + + SK_PNMI_SET_STAT(pBuf, Ret, (SK_U32)(-1)); + *pLen = SK_PNMI_MIN_STRUCT_SIZE; + return (Ret); + } + + pAC->Pnmi.RlmtUpdatedFlag ++; + pAC->Pnmi.SirqUpdatedFlag ++; + + /* Preset/Set values */ + for (TableIndex = 0; TableIndex < sizeof(IdTable)/sizeof(IdTable[0]); + TableIndex ++) { + + if (IdTable[TableIndex].Access != SK_PNMI_RW) { + + continue; + } + + InstanceNo = IdTable[TableIndex].InstanceNo; + Id = IdTable[TableIndex].Id; + + for (InstanceCnt = 1; InstanceCnt <= InstanceNo; + InstanceCnt ++) { + + DstOffset = IdTable[TableIndex].Offset + + (InstanceCnt - 1) * + IdTable[TableIndex].StructSize; + + /* + * Because VPD multiple instance variables are + * not setable we do not need to evaluate VPD + * instances. Have a look to VPD instance + * calculation in SkPnmiGetStruct(). + */ + Instance = (SK_U32)InstanceCnt; + + /* + * Evaluate needed buffer length + */ + Len = 0; + Ret = IdTable[TableIndex].Func(pAC, IoC, + SK_PNMI_GET, IdTable[TableIndex].Id, + NULL, &Len, Instance, TableIndex); + + if (Ret == SK_PNMI_ERR_UNKNOWN_INST) { + + break; + } + if (Ret != SK_PNMI_ERR_TOO_SHORT) { + + pAC->Pnmi.RlmtUpdatedFlag --; + pAC->Pnmi.SirqUpdatedFlag --; + + SK_PNMI_CHECKFLAGS("PnmiStruct: On return"); + SK_PNMI_SET_STAT(pBuf, + SK_PNMI_ERR_GENERAL, DstOffset); + *pLen = SK_PNMI_MIN_STRUCT_SIZE; + return (SK_PNMI_ERR_GENERAL); + } + if (Id == OID_SKGE_VPD_ACTION) { + + switch (*(pBuf + DstOffset)) { + + case SK_PNMI_VPD_CREATE: + Len = 3 + *(pBuf + DstOffset + 3); + break; + + case SK_PNMI_VPD_DELETE: + Len = 3; + break; + + default: + Len = 1; + break; + } + } + + /* Call the OID handler function */ + Ret = IdTable[TableIndex].Func(pAC, IoC, Action, + IdTable[TableIndex].Id, pBuf + DstOffset, + &Len, Instance, TableIndex); + + if (Ret != SK_PNMI_ERR_OK) { + + pAC->Pnmi.RlmtUpdatedFlag --; + pAC->Pnmi.SirqUpdatedFlag --; + + SK_PNMI_CHECKFLAGS("PnmiStruct: On return"); + SK_PNMI_SET_STAT(pBuf, SK_PNMI_ERR_BAD_VALUE, + DstOffset); + *pLen = SK_PNMI_MIN_STRUCT_SIZE; + return (SK_PNMI_ERR_BAD_VALUE); + } + } + } + + pAC->Pnmi.RlmtUpdatedFlag --; + pAC->Pnmi.SirqUpdatedFlag --; + + SK_PNMI_CHECKFLAGS("PnmiStruct: On return"); + SK_PNMI_SET_STAT(pBuf, SK_PNMI_ERR_OK, (SK_U32)(-1)); + return (SK_PNMI_ERR_OK); +} + +/***************************************************************************** + * + * LookupId - Lookup an OID in the IdTable + * + * Description: + * Scans the IdTable to find the table entry of an OID. + * + * Returns: + * The table index or -1 if not found. + */ + +static int LookupId( +SK_U32 Id) /* Object identifier to be searched */ +{ + int i; + int Len = sizeof(IdTable)/sizeof(IdTable[0]); + + for (i=0; i sizeof(SK_U32)) { + + return (SK_PNMI_ERR_BAD_VALUE); + } + + /* Check if the command is a known one */ + SK_PNMI_READ_U32(pBuf, ActionOp); + if (*pLen > sizeof(SK_U32) || + (ActionOp != SK_PNMI_ACT_IDLE && + ActionOp != SK_PNMI_ACT_RESET && + ActionOp != SK_PNMI_ACT_SELFTEST && + ActionOp != SK_PNMI_ACT_RESETCNT)) { + + *pLen = 0; + return (SK_PNMI_ERR_BAD_VALUE); + } + + /* A preset ends here */ + if (Action == SK_PNMI_PRESET) { + + return (SK_PNMI_ERR_OK); + } + + switch (ActionOp) { + + case SK_PNMI_ACT_IDLE: + /* Nothing to do */ + break; + + case SK_PNMI_ACT_RESET: + /* + * Perform a driver reset or something that comes near + * to this. + */ + Ret = SK_DRIVER_RESET(pAC, IoC); + if (Ret != 0) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR005, + SK_PNMI_ERR005MSG); + + return (SK_PNMI_ERR_GENERAL); + } + break; + + case SK_PNMI_ACT_SELFTEST: + /* + * Perform a driver selftest or something similar to this. + * Currently this feature is not used and will probably + * implemented in another way. + */ + Ret = SK_DRIVER_SELFTEST(pAC, IoC); + pAC->Pnmi.TestResult = Ret; + break; + + case SK_PNMI_ACT_RESETCNT: + /* Set all counters and timestamps to zero */ + ResetCounter(pAC, IoC); + break; + + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR006, + SK_PNMI_ERR006MSG); + + return (SK_PNMI_ERR_GENERAL); + } + + return (SK_PNMI_ERR_OK); +} + +/***************************************************************************** + * + * Mac8023Stat - OID handler of OID_GEN_XXX and OID_802_3_XXX + * + * Description: + * Retrieves the statistic values of the virtual port (logical + * index 0). Only special OIDs of NDIS are handled which consist + * of a 32 bit instead of a 64 bit value. The OIDs are public + * because perhaps some other platform can use them too. + * + * Returns: + * SK_PNMI_ERR_OK The request was successfully performed. + * SK_PNMI_ERR_GENERAL A general severe internal error occured. + * SK_PNMI_ERR_TOO_SHORT The passed buffer is too short to contain + * the correct data (e.g. a 32bit value is + * needed, but a 16 bit value was passed). + * SK_PNMI_ERR_UNKNOWN_INST The requested instance of the OID doesn't + * exist (e.g. port instance 3 on a two port + * adapter. + */ + +static int Mac8023Stat( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +int Action, /* Get/PreSet/Set action */ +SK_U32 Id, /* Object ID that is to be processed */ +char *pBuf, /* Buffer to which to mgmt data will be retrieved */ +unsigned int *pLen, /* On call: buffer length. On return: used buffer */ +SK_U32 Instance, /* Instance (1..n) that is to be queried or -1 */ +unsigned int TableIndex) /* Index to the Id table */ +{ + int Ret; + SK_U32 StatVal; + + + /* + * Only the active Mac is returned + */ + if (Instance != (SK_U32)(-1) && Instance != 1) { + + *pLen = 0; + return (SK_PNMI_ERR_UNKNOWN_INST); + } + + /* + * Check action type + */ + if (Action != SK_PNMI_GET) { + + *pLen = 0; + return (SK_PNMI_ERR_READ_ONLY); + } + + /* + * Check length + */ + switch (Id) { + + case OID_802_3_PERMANENT_ADDRESS: + case OID_802_3_CURRENT_ADDRESS: + if (*pLen < sizeof(SK_MAC_ADDR)) { + + *pLen = sizeof(SK_MAC_ADDR); + return (SK_PNMI_ERR_TOO_SHORT); + } + break; + + default: + if (*pLen < 4) { + + *pLen = 4; + return (SK_PNMI_ERR_TOO_SHORT); + } + break; + } + + /* + * Update all statistics, because we retrieve virtual MAC, which + * consists of multiple physical statistics and increment semaphore + * to indicate that an update was already done. + */ + Ret = MacUpdate(pAC, IoC, 0, pAC->GIni.GIMacsFound - 1); + if ( Ret != SK_PNMI_ERR_OK) { + + *pLen = 0; + return (Ret); + } + pAC->Pnmi.MacUpdatedFlag ++; + + /* + * Get value (MAC Index 0 identifies the virtual MAC) + */ + switch (Id) { + + case OID_802_3_PERMANENT_ADDRESS: + CopyMac(pBuf, &pAC->Addr.PermanentMacAddress); + *pLen = sizeof(SK_MAC_ADDR); + break; + + case OID_802_3_CURRENT_ADDRESS: + CopyMac(pBuf, &pAC->Addr.CurrentMacAddress); + *pLen = sizeof(SK_MAC_ADDR); + break; + + default: + StatVal = (SK_U32)GetStatVal(pAC, IoC, 0, + IdTable[TableIndex].Param); + SK_PNMI_STORE_U32(pBuf, StatVal); + *pLen = sizeof(SK_U32); + break; + } + + pAC->Pnmi.MacUpdatedFlag --; + + return (SK_PNMI_ERR_OK); +} + +/***************************************************************************** + * + * MacPrivateStat - OID handler function of OID_SKGE_STAT_XXX + * + * Description: + * Retrieves the XMAC statistic data. + * + * Returns: + * SK_PNMI_ERR_OK The request was successfully performed. + * SK_PNMI_ERR_GENERAL A general severe internal error occured. + * SK_PNMI_ERR_TOO_SHORT The passed buffer is too short to contain + * the correct data (e.g. a 32bit value is + * needed, but a 16 bit value was passed). + * SK_PNMI_ERR_UNKNOWN_INST The requested instance of the OID doesn't + * exist (e.g. port instance 3 on a two port + * adapter. + */ + +static int MacPrivateStat( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +int Action, /* Get/PreSet/Set action */ +SK_U32 Id, /* Object ID that is to be processed */ +char *pBuf, /* Buffer to which to mgmt data will be retrieved */ +unsigned int *pLen, /* On call: buffer length. On return: used buffer */ +SK_U32 Instance, /* Instance (1..n) that is to be queried or -1 */ +unsigned int TableIndex) /* Index to the Id table */ +{ + unsigned int LogPortMax; + unsigned int LogPortIndex; + unsigned int PhysPortMax; + unsigned int Limit; + unsigned int Offset; + int Ret; + SK_U64 StatVal; + + + /* + * Calculate instance if wished. MAC index 0 is the virtual + * MAC. + */ + PhysPortMax = pAC->GIni.GIMacsFound; + LogPortMax = SK_PNMI_PORT_PHYS2LOG(PhysPortMax); + + if ((Instance != (SK_U32)(-1))) { + + if ((Instance < 1) || (Instance > LogPortMax)) { + + *pLen = 0; + return (SK_PNMI_ERR_UNKNOWN_INST); + } + + LogPortIndex = SK_PNMI_PORT_INST2LOG(Instance); + Limit = LogPortIndex + 1; + } + else { + LogPortIndex = 0; + Limit = LogPortMax; + } + + /* + * Check action + */ + if (Action != SK_PNMI_GET) { + + *pLen = 0; + return (SK_PNMI_ERR_READ_ONLY); + } + + /* + * Check length + */ + if (*pLen < (Limit - LogPortIndex) * sizeof(SK_U64)) { + + *pLen = (Limit - LogPortIndex) * sizeof(SK_U64); + return (SK_PNMI_ERR_TOO_SHORT); + } + + /* + * Update XMAC statistic and increment semaphore to indicate that + * an update was already done. + */ + Ret = MacUpdate(pAC, IoC, 0, PhysPortMax - 1); + if (Ret != SK_PNMI_ERR_OK) { + + *pLen = 0; + return (Ret); + } + pAC->Pnmi.MacUpdatedFlag ++; + + /* + * Get value + */ + Offset = 0; + for (; LogPortIndex < Limit; LogPortIndex ++) { + + switch (Id) { + +/* XXX not yet implemented due to XMAC problems + case OID_SKGE_STAT_TX_UTIL: + return (SK_PNMI_ERR_GENERAL); +*/ +/* XXX not yet implemented due to XMAC problems + case OID_SKGE_STAT_RX_UTIL: + return (SK_PNMI_ERR_GENERAL); +*/ + default: + StatVal = GetStatVal(pAC, IoC, LogPortIndex, + IdTable[TableIndex].Param); + SK_PNMI_STORE_U64(pBuf + Offset, StatVal); + break; + } + + Offset += sizeof(SK_U64); + } + *pLen = Offset; + + pAC->Pnmi.MacUpdatedFlag --; + + return (SK_PNMI_ERR_OK); +} + +/***************************************************************************** + * + * Addr - OID handler function of OID_SKGE_PHYS_CUR_ADDR and _FAC_ADDR + * + * Description: + * Get/Presets/Sets the current and factory MAC address. The MAC + * address of the virtual port, which is reported to the OS, may + * not be changed, but the physical ones. A set to the virtual port + * will be ignored. No error should be reported because otherwise + * a multiple instance set (-1) would always fail. + * + * Returns: + * SK_PNMI_ERR_OK The request was successfully performed. + * SK_PNMI_ERR_GENERAL A general severe internal error occured. + * SK_PNMI_ERR_TOO_SHORT The passed buffer is too short to contain + * the correct data (e.g. a 32bit value is + * needed, but a 16 bit value was passed). + * SK_PNMI_ERR_BAD_VALUE The passed value is not in the valid + * value range. + * SK_PNMI_ERR_READ_ONLY The OID is read-only and cannot be set. + * SK_PNMI_ERR_UNKNOWN_INST The requested instance of the OID doesn't + * exist (e.g. port instance 3 on a two port + * adapter. + */ + +static int Addr( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +int Action, /* Get/PreSet/Set action */ +SK_U32 Id, /* Object ID that is to be processed */ +char *pBuf, /* Buffer to which to mgmt data will be retrieved */ +unsigned int *pLen, /* On call: buffer length. On return: used buffer */ +SK_U32 Instance, /* Instance (1..n) that is to be queried or -1 */ +unsigned int TableIndex) /* Index to the Id table */ +{ + int Ret; + unsigned int LogPortMax; + unsigned int PhysPortMax; + unsigned int LogPortIndex; + unsigned int PhysPortIndex; + unsigned int Limit; + unsigned int Offset = 0; + + + /* + * Calculate instance if wished + */ + PhysPortMax = pAC->GIni.GIMacsFound; + LogPortMax = SK_PNMI_PORT_PHYS2LOG(PhysPortMax); + + if ((Instance != (SK_U32)(-1))) { + + if ((Instance < 1) || (Instance > SKCS_NUM_PROTOCOLS)) { + + *pLen = 0; + return (SK_PNMI_ERR_UNKNOWN_INST); + } + + LogPortIndex = SK_PNMI_PORT_INST2LOG(Instance); + Limit = LogPortIndex + 1; + } + else { + LogPortIndex = 0; + Limit = LogPortMax; + } + + /* + * Perform Action + */ + if (Action == SK_PNMI_GET) { + + /* + * Check length + */ + if (*pLen < (Limit - LogPortIndex) * 6) { + + *pLen = (Limit - LogPortIndex) * 6; + return (SK_PNMI_ERR_TOO_SHORT); + } + + /* + * Get value + */ + for (; LogPortIndex < Limit; LogPortIndex ++) { + + switch (Id) { + + case OID_SKGE_PHYS_CUR_ADDR: + if (LogPortIndex == 0) { + + CopyMac(pBuf + Offset, &pAC->Addr. + CurrentMacAddress); + } + else { + PhysPortIndex = SK_PNMI_PORT_LOG2PHYS( + pAC, LogPortIndex); + + CopyMac(pBuf + Offset, &pAC->Addr. + Port[PhysPortIndex]. + CurrentMacAddress); + } + Offset += 6; + break; + + case OID_SKGE_PHYS_FAC_ADDR: + if (LogPortIndex == 0) { + + CopyMac(pBuf + Offset, &pAC->Addr. + PermanentMacAddress); + } + else { + PhysPortIndex = SK_PNMI_PORT_LOG2PHYS( + pAC, LogPortIndex); + + CopyMac(pBuf + Offset, &pAC->Addr. + Port[PhysPortIndex]. + PermanentMacAddress); + } + Offset += 6; + break; + + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR008, + SK_PNMI_ERR008MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + } + + *pLen = Offset; + } + else { + /* + * The logical MAC address may not be changed only + * the physical ones + */ + if (Id == OID_SKGE_PHYS_FAC_ADDR) { + + *pLen = 0; + return (SK_PNMI_ERR_READ_ONLY); + } + + /* + * Only the current address may be changed + */ + if (Id != OID_SKGE_PHYS_CUR_ADDR) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR009, + SK_PNMI_ERR009MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + + /* + * Check length + */ + if (*pLen < (Limit - LogPortIndex) * 6) { + + *pLen = (Limit - LogPortIndex) * 6; + return (SK_PNMI_ERR_TOO_SHORT); + } + if (*pLen > (Limit - LogPortIndex) * 6) { + + *pLen = 0; + return (SK_PNMI_ERR_BAD_VALUE); + } + + /* + * Check Action + */ + if (Action == SK_PNMI_PRESET) { + + *pLen = 0; + return (SK_PNMI_ERR_OK); + } + + /* + * Set OID_SKGE_MAC_CUR_ADDR + */ + for (; LogPortIndex < Limit; LogPortIndex ++, Offset += 6) { + + /* + * A set to virtual port and set of broadcast + * address will be ignored + */ + if (LogPortIndex == 0 || SK_MEMCMP(pBuf + Offset, + "\xff\xff\xff\xff\xff\xff", 6) == 0) { + + continue; + } + + PhysPortIndex = SK_PNMI_PORT_LOG2PHYS(pAC, + LogPortIndex); + + Ret = SkAddrOverride(pAC, IoC, PhysPortIndex, + (SK_MAC_ADDR *)(pBuf + Offset), + (LogPortIndex == 0 ? SK_ADDR_VIRTUAL_ADDRESS : + SK_ADDR_PHYSICAL_ADDRESS)); + if (Ret != SK_ADDR_OVERRIDE_SUCCESS) { + + return (SK_PNMI_ERR_GENERAL); + } + } + *pLen = Offset; + } + + return (SK_PNMI_ERR_OK); +} + +/***************************************************************************** + * + * CsumStat - OID handler function of OID_SKGE_CHKSM_XXX + * + * Description: + * Retrieves the statistic values of the CSUM module. The CSUM data + * structure must be available in the SK_AC even if the CSUM module + * is not included, because PNMI reads the statistic data from the + * CSUM part of SK_AC directly. + * + * Returns: + * SK_PNMI_ERR_OK The request was successfully performed. + * SK_PNMI_ERR_GENERAL A general severe internal error occured. + * SK_PNMI_ERR_TOO_SHORT The passed buffer is too short to contain + * the correct data (e.g. a 32bit value is + * needed, but a 16 bit value was passed). + * SK_PNMI_ERR_UNKNOWN_INST The requested instance of the OID doesn't + * exist (e.g. port instance 3 on a two port + * adapter. + */ + +static int CsumStat( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +int Action, /* Get/PreSet/Set action */ +SK_U32 Id, /* Object ID that is to be processed */ +char *pBuf, /* Buffer to which to mgmt data will be retrieved */ +unsigned int *pLen, /* On call: buffer length. On return: used buffer */ +SK_U32 Instance, /* Instance (1..n) that is to be queried or -1 */ +unsigned int TableIndex) /* Index to the Id table */ +{ + unsigned int Index; + unsigned int Limit; + unsigned int Offset = 0; + SK_U64 StatVal; + + + /* + * Calculate instance if wished + */ + if (Instance != (SK_U32)(-1)) { + + if ((Instance < 1) || (Instance > SKCS_NUM_PROTOCOLS)) { + + *pLen = 0; + return (SK_PNMI_ERR_UNKNOWN_INST); + } + + Index = (unsigned int)Instance - 1; + Limit = (unsigned int)Instance; + } + else { + Index = 0; + Limit = SKCS_NUM_PROTOCOLS; + } + + /* + * Check action + */ + if (Action != SK_PNMI_GET) { + + *pLen = 0; + return (SK_PNMI_ERR_READ_ONLY); + } + + /* + * Check length + */ + if (*pLen < (Limit - Index) * sizeof(SK_U64)) { + + *pLen = (Limit - Index) * sizeof(SK_U64); + return (SK_PNMI_ERR_TOO_SHORT); + } + + /* + * Get value + */ + for (; Index < Limit; Index ++) { + + switch (Id) { + + case OID_SKGE_CHKSM_RX_OK_CTS: + StatVal = pAC->Csum.ProtoStats[Index].RxOkCts; + break; + + case OID_SKGE_CHKSM_RX_UNABLE_CTS: + StatVal = pAC->Csum.ProtoStats[Index].RxUnableCts; + break; + + case OID_SKGE_CHKSM_RX_ERR_CTS: + StatVal = pAC->Csum.ProtoStats[Index].RxErrCts; + break; + + case OID_SKGE_CHKSM_TX_OK_CTS: + StatVal = pAC->Csum.ProtoStats[Index].TxOkCts; + break; + + case OID_SKGE_CHKSM_TX_UNABLE_CTS: + StatVal = pAC->Csum.ProtoStats[Index].TxUnableCts; + break; + + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR010, + SK_PNMI_ERR010MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + + SK_PNMI_STORE_U64(pBuf + Offset, StatVal); + Offset += sizeof(SK_U64); + } + + /* + * Store used buffer space + */ + *pLen = Offset; + + return (SK_PNMI_ERR_OK); +} + +/***************************************************************************** + * + * SensorStat - OID handler function of OID_SKGE_SENSOR_XXX + * + * Description: + * Retrieves the statistic values of the I2C module, which handles + * the temperature and voltage sensors. + * + * Returns: + * SK_PNMI_ERR_OK The request was successfully performed. + * SK_PNMI_ERR_GENERAL A general severe internal error occured. + * SK_PNMI_ERR_TOO_SHORT The passed buffer is too short to contain + * the correct data (e.g. a 32bit value is + * needed, but a 16 bit value was passed). + * SK_PNMI_ERR_UNKNOWN_INST The requested instance of the OID doesn't + * exist (e.g. port instance 3 on a two port + * adapter. + */ + +static int SensorStat( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +int Action, /* Get/PreSet/Set action */ +SK_U32 Id, /* Object ID that is to be processed */ +char *pBuf, /* Buffer to which to mgmt data will be retrieved */ +unsigned int *pLen, /* On call: buffer length. On return: used buffer */ +SK_U32 Instance, /* Instance (1..n) that is to be queried or -1 */ +unsigned int TableIndex) /* Index to the Id table */ +{ + unsigned int i; + unsigned int Index; + unsigned int Limit; + unsigned int Offset; + unsigned int Len; + SK_U32 Val32; + SK_U64 Val64; + + + /* + * Calculate instance if wished + */ + if ((Instance != (SK_U32)(-1))) { + + if ((Instance < 1) || (Instance > (SK_U32)pAC->I2c.MaxSens)) { + + *pLen = 0; + return (SK_PNMI_ERR_UNKNOWN_INST); + } + + Index = (unsigned int)Instance -1; + Limit = (unsigned int)Instance; + } + else { + Index = 0; + Limit = (unsigned int) pAC->I2c.MaxSens; + } + + /* + * Check action + */ + if (Action != SK_PNMI_GET) { + + *pLen = 0; + return (SK_PNMI_ERR_READ_ONLY); + } + + /* + * Check length + */ + switch (Id) { + + case OID_SKGE_SENSOR_VALUE: + case OID_SKGE_SENSOR_WAR_THRES_LOW: + case OID_SKGE_SENSOR_WAR_THRES_UPP: + case OID_SKGE_SENSOR_ERR_THRES_LOW: + case OID_SKGE_SENSOR_ERR_THRES_UPP: + if (*pLen < (Limit - Index) * sizeof(SK_U32)) { + + *pLen = (Limit - Index) * sizeof(SK_U32); + return (SK_PNMI_ERR_TOO_SHORT); + } + break; + + case OID_SKGE_SENSOR_DESCR: + for (Offset = 0, i = Index; i < Limit; i ++) { + + Len = (unsigned int) + SK_STRLEN(pAC->I2c.SenTable[i].SenDesc) + 1; + if (Len >= SK_PNMI_STRINGLEN2) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR011, + SK_PNMI_ERR011MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + Offset += Len; + } + if (*pLen < Offset) { + + *pLen = Offset; + return (SK_PNMI_ERR_TOO_SHORT); + } + break; + + case OID_SKGE_SENSOR_INDEX: + case OID_SKGE_SENSOR_TYPE: + case OID_SKGE_SENSOR_STATUS: + if (*pLen < Limit - Index) { + + *pLen = Limit - Index; + return (SK_PNMI_ERR_TOO_SHORT); + } + break; + + case OID_SKGE_SENSOR_WAR_CTS: + case OID_SKGE_SENSOR_WAR_TIME: + case OID_SKGE_SENSOR_ERR_CTS: + case OID_SKGE_SENSOR_ERR_TIME: + if (*pLen < (Limit - Index) * sizeof(SK_U64)) { + + *pLen = (Limit - Index) * sizeof(SK_U64); + return (SK_PNMI_ERR_TOO_SHORT); + } + break; + + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR012, + SK_PNMI_ERR012MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + + } + + /* + * Get value + */ + for (Offset = 0; Index < Limit; Index ++) { + + switch (Id) { + + case OID_SKGE_SENSOR_INDEX: + *(pBuf + Offset) = (char)Index; + Offset += sizeof(char); + break; + + case OID_SKGE_SENSOR_DESCR: + Len = SK_STRLEN(pAC->I2c.SenTable[Index].SenDesc); + SK_MEMCPY(pBuf + Offset + 1, + pAC->I2c.SenTable[Index].SenDesc, Len); + *(pBuf + Offset) = (char)Len; + Offset += Len + 1; + break; + + case OID_SKGE_SENSOR_TYPE: + *(pBuf + Offset) = + (char)pAC->I2c.SenTable[Index].SenType; + Offset += sizeof(char); + break; + + case OID_SKGE_SENSOR_VALUE: + Val32 = (SK_U32)pAC->I2c.SenTable[Index].SenValue; + SK_PNMI_STORE_U32(pBuf + Offset, Val32); + Offset += sizeof(SK_U32); + break; + + case OID_SKGE_SENSOR_WAR_THRES_LOW: + Val32 = (SK_U32)pAC->I2c.SenTable[Index]. + SenThreWarnLow; + SK_PNMI_STORE_U32(pBuf + Offset, Val32); + Offset += sizeof(SK_U32); + break; + + case OID_SKGE_SENSOR_WAR_THRES_UPP: + Val32 = (SK_U32)pAC->I2c.SenTable[Index]. + SenThreWarnHigh; + SK_PNMI_STORE_U32(pBuf + Offset, Val32); + Offset += sizeof(SK_U32); + break; + + case OID_SKGE_SENSOR_ERR_THRES_LOW: + Val32 = (SK_U32)pAC->I2c.SenTable[Index]. + SenThreErrLow; + SK_PNMI_STORE_U32(pBuf + Offset, Val32); + Offset += sizeof(SK_U32); + break; + + case OID_SKGE_SENSOR_ERR_THRES_UPP: + Val32 = pAC->I2c.SenTable[Index].SenThreErrHigh; + SK_PNMI_STORE_U32(pBuf + Offset, Val32); + Offset += sizeof(SK_U32); + break; + + case OID_SKGE_SENSOR_STATUS: + *(pBuf + Offset) = + (char)pAC->I2c.SenTable[Index].SenErrFlag; + Offset += sizeof(char); + break; + + case OID_SKGE_SENSOR_WAR_CTS: + Val64 = pAC->I2c.SenTable[Index].SenWarnCts; + SK_PNMI_STORE_U64(pBuf + Offset, Val64); + Offset += sizeof(SK_U64); + break; + + case OID_SKGE_SENSOR_ERR_CTS: + Val64 = pAC->I2c.SenTable[Index].SenErrCts; + SK_PNMI_STORE_U64(pBuf + Offset, Val64); + Offset += sizeof(SK_U64); + break; + + case OID_SKGE_SENSOR_WAR_TIME: + Val64 = SK_PNMI_HUNDREDS_SEC(pAC->I2c.SenTable[Index]. + SenBegWarnTS); + SK_PNMI_STORE_U64(pBuf + Offset, Val64); + Offset += sizeof(SK_U64); + break; + + case OID_SKGE_SENSOR_ERR_TIME: + Val64 = SK_PNMI_HUNDREDS_SEC(pAC->I2c.SenTable[Index]. + SenBegErrTS); + SK_PNMI_STORE_U64(pBuf + Offset, Val64); + Offset += sizeof(SK_U64); + break; + + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR013, + SK_PNMI_ERR013MSG); + + return (SK_PNMI_ERR_GENERAL); + } + } + + /* + * Store used buffer space + */ + *pLen = Offset; + + return (SK_PNMI_ERR_OK); +} + +/***************************************************************************** + * + * Vpd - OID handler function of OID_SKGE_VPD_XXX + * + * Description: + * Get/preset/set of VPD data. As instance the name of a VPD key + * can be passed. The Instance parameter is a SK_U32 and can be + * used as a string buffer for the VPD key, because their maximum + * length is 4 byte. + * + * Returns: + * SK_PNMI_ERR_OK The request was successfully performed. + * SK_PNMI_ERR_GENERAL A general severe internal error occured. + * SK_PNMI_ERR_TOO_SHORT The passed buffer is too short to contain + * the correct data (e.g. a 32bit value is + * needed, but a 16 bit value was passed). + * SK_PNMI_ERR_BAD_VALUE The passed value is not in the valid + * value range. + * SK_PNMI_ERR_READ_ONLY The OID is read-only and cannot be set. + * SK_PNMI_ERR_UNKNOWN_INST The requested instance of the OID doesn't + * exist (e.g. port instance 3 on a two port + * adapter. + */ + +static int Vpd( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +int Action, /* Get/PreSet/Set action */ +SK_U32 Id, /* Object ID that is to be processed */ +char *pBuf, /* Buffer to which to mgmt data will be retrieved */ +unsigned int *pLen, /* On call: buffer length. On return: used buffer */ +SK_U32 Instance, /* Instance (1..n) that is to be queried or -1 */ +unsigned int TableIndex) /* Index to the Id table */ +{ + SK_VPD_STATUS *pVpdStatus; + unsigned int BufLen; + char Buf[256]; + char KeyArr[SK_PNMI_VPD_ARR_SIZE][SK_PNMI_VPD_STR_SIZE]; + char KeyStr[SK_PNMI_VPD_STR_SIZE]; + unsigned int KeyNo; + unsigned int Offset; + unsigned int Index; + unsigned int FirstIndex; + unsigned int LastIndex; + unsigned int Len; + int Ret; + SK_U32 Val32; + + /* + * Get array of all currently stored VPD keys + */ + Ret = GetVpdKeyArr(pAC, IoC, &KeyArr[0][0], sizeof(KeyArr), + &KeyNo); + if (Ret != SK_PNMI_ERR_OK) { + + *pLen = 0; + return (Ret); + } + + /* + * If instance is not -1, try to find the requested VPD key for + * the multiple instance variables. The other OIDs as for example + * OID VPD_ACTION are single instance variables and must be + * handled separatly. + */ + FirstIndex = 0; + LastIndex = KeyNo; + + if ((Instance != (SK_U32)(-1))) { + + if (Id == OID_SKGE_VPD_KEY || Id == OID_SKGE_VPD_VALUE || + Id == OID_SKGE_VPD_ACCESS) { + + SK_STRNCPY(KeyStr, (char *)&Instance, 4); + KeyStr[4] = 0; + + for (Index = 0; Index < KeyNo; Index ++) { + + if (SK_STRCMP(KeyStr, KeyArr[Index]) == 0) { + + FirstIndex = Index; + LastIndex = Index+1; + break; + } + } + if (Index == KeyNo) { + + *pLen = 0; + return (SK_PNMI_ERR_UNKNOWN_INST); + } + } + else if (Instance != 1) { + + *pLen = 0; + return (SK_PNMI_ERR_UNKNOWN_INST); + } + } + + /* + * Get value, if a query should be performed + */ + if (Action == SK_PNMI_GET) { + + switch (Id) { + + case OID_SKGE_VPD_FREE_BYTES: + /* Check length of buffer */ + if (*pLen < sizeof(SK_U32)) { + + *pLen = sizeof(SK_U32); + return (SK_PNMI_ERR_TOO_SHORT); + } + /* Get number of free bytes */ + pVpdStatus = VpdStat(pAC, IoC); + if (pVpdStatus == NULL) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR017, + SK_PNMI_ERR017MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + if ((pVpdStatus->vpd_status & VPD_VALID) == 0) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR018, + SK_PNMI_ERR018MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + + Val32 = (SK_U32)pVpdStatus->vpd_free_rw; + SK_PNMI_STORE_U32(pBuf, Val32); + *pLen = sizeof(SK_U32); + break; + + case OID_SKGE_VPD_ENTRIES_LIST: + /* Check length */ + for (Len = 0, Index = 0; Index < KeyNo; Index ++) { + + Len += SK_STRLEN(KeyArr[Index]) + 1; + } + if (*pLen < Len) { + + *pLen = Len; + return (SK_PNMI_ERR_TOO_SHORT); + } + + /* Get value */ + *(pBuf) = (char)Len - 1; + for (Offset = 1, Index = 0; Index < KeyNo; Index ++) { + + Len = SK_STRLEN(KeyArr[Index]); + SK_MEMCPY(pBuf + Offset, KeyArr[Index], Len); + + Offset += Len; + + if (Index < KeyNo - 1) { + + *(pBuf + Offset) = ' '; + Offset ++; + } + } + *pLen = Offset; + break; + + case OID_SKGE_VPD_ENTRIES_NUMBER: + /* Check length */ + if (*pLen < sizeof(SK_U32)) { + + *pLen = sizeof(SK_U32); + return (SK_PNMI_ERR_TOO_SHORT); + } + + Val32 = (SK_U32)KeyNo; + SK_PNMI_STORE_U32(pBuf, Val32); + *pLen = sizeof(SK_U32); + break; + + case OID_SKGE_VPD_KEY: + /* Check buffer length, if it is large enough */ + for (Len = 0, Index = FirstIndex; + Index < LastIndex; Index ++) { + + Len += SK_STRLEN(KeyArr[Index]) + 1; + } + if (*pLen < Len) { + + *pLen = Len; + return (SK_PNMI_ERR_TOO_SHORT); + } + + /* + * Get the key to an intermediate buffer, because + * we have to prepend a length byte. + */ + for (Offset = 0, Index = FirstIndex; + Index < LastIndex; Index ++) { + + Len = SK_STRLEN(KeyArr[Index]); + + *(pBuf + Offset) = (char)Len; + SK_MEMCPY(pBuf + Offset + 1, KeyArr[Index], + Len); + Offset += Len + 1; + } + *pLen = Offset; + break; + + case OID_SKGE_VPD_VALUE: + /* Check the buffer length if it is large enough */ + for (Offset = 0, Index = FirstIndex; + Index < LastIndex; Index ++) { + + BufLen = 256; + if (VpdRead(pAC, IoC, KeyArr[Index], Buf, + (int *)&BufLen) > 0 || + BufLen >= SK_PNMI_VPD_DATALEN) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, + SK_PNMI_ERR021, + SK_PNMI_ERR021MSG); + + return (SK_PNMI_ERR_GENERAL); + } + Offset += BufLen + 1; + } + if (*pLen < Offset) { + + *pLen = Offset; + return (SK_PNMI_ERR_TOO_SHORT); + } + + /* + * Get the value to an intermediate buffer, because + * we have to prepend a length byte. + */ + for (Offset = 0, Index = FirstIndex; + Index < LastIndex; Index ++) { + + BufLen = 256; + if (VpdRead(pAC, IoC, KeyArr[Index], Buf, + (int *)&BufLen) > 0 || + BufLen >= SK_PNMI_VPD_DATALEN) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, + SK_PNMI_ERR022, + SK_PNMI_ERR022MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + + *(pBuf + Offset) = (char)BufLen; + SK_MEMCPY(pBuf + Offset + 1, Buf, BufLen); + Offset += BufLen + 1; + } + *pLen = Offset; + break; + + case OID_SKGE_VPD_ACCESS: + if (*pLen < LastIndex - FirstIndex) { + + *pLen = LastIndex - FirstIndex; + return (SK_PNMI_ERR_TOO_SHORT); + } + + for (Offset = 0, Index = FirstIndex; + Index < LastIndex; Index ++) { + + if (VpdMayWrite(KeyArr[Index])) { + + *(pBuf + Offset) = SK_PNMI_VPD_RW; + } + else { + *(pBuf + Offset) = SK_PNMI_VPD_RO; + } + Offset ++; + } + *pLen = Offset; + break; + + case OID_SKGE_VPD_ACTION: + Offset = LastIndex - FirstIndex; + if (*pLen < Offset) { + + *pLen = Offset; + return (SK_PNMI_ERR_TOO_SHORT); + } + SK_MEMSET(pBuf, 0, Offset); + *pLen = Offset; + break; + + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR023, + SK_PNMI_ERR023MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + } + else { + /* The only OID which can be set is VPD_ACTION */ + if (Id != OID_SKGE_VPD_ACTION) { + + if (Id == OID_SKGE_VPD_FREE_BYTES || + Id == OID_SKGE_VPD_ENTRIES_LIST || + Id == OID_SKGE_VPD_ENTRIES_NUMBER || + Id == OID_SKGE_VPD_KEY || + Id == OID_SKGE_VPD_VALUE || + Id == OID_SKGE_VPD_ACCESS) { + + *pLen = 0; + return (SK_PNMI_ERR_READ_ONLY); + } + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR024, + SK_PNMI_ERR024MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + + /* + * From this point we handle VPD_ACTION. Check the buffer + * length. It should at least have the size of one byte. + */ + if (*pLen < 1) { + + *pLen = 1; + return (SK_PNMI_ERR_TOO_SHORT); + } + + /* + * The first byte contains the VPD action type we should + * perform. + */ + switch (*pBuf) { + + case SK_PNMI_VPD_IGNORE: + /* Nothing to do */ + break; + + case SK_PNMI_VPD_CREATE: + /* + * We have to create a new VPD entry or we modify + * an existing one. Check first the buffer length. + */ + if (*pLen < 4) { + + *pLen = 4; + return (SK_PNMI_ERR_TOO_SHORT); + } + KeyStr[0] = pBuf[1]; + KeyStr[1] = pBuf[2]; + KeyStr[2] = 0; + + /* + * Is the entry writable or does it belong to the + * read-only area? + */ + if (!VpdMayWrite(KeyStr)) { + + *pLen = 0; + return (SK_PNMI_ERR_BAD_VALUE); + } + + Offset = (int)pBuf[3] & 0xFF; + + SK_MEMCPY(Buf, pBuf + 4, Offset); + Buf[Offset] = 0; + + /* A preset ends here */ + if (Action == SK_PNMI_PRESET) { + + return (SK_PNMI_ERR_OK); + } + + /* Write the new entry or modify an existing one */ + Ret = VpdWrite(pAC, IoC, KeyStr, Buf); + if (Ret == SK_PNMI_VPD_NOWRITE ) { + + *pLen = 0; + return (SK_PNMI_ERR_BAD_VALUE); + } + else if (Ret != SK_PNMI_VPD_OK) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR025, + SK_PNMI_ERR025MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + + /* + * Perform an update of the VPD data. This is + * not mandantory, but just to be sure. + */ + Ret = VpdUpdate(pAC, IoC); + if (Ret != SK_PNMI_VPD_OK) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR026, + SK_PNMI_ERR026MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + break; + + case SK_PNMI_VPD_DELETE: + /* Check if the buffer size is plausible */ + if (*pLen < 3) { + + *pLen = 3; + return (SK_PNMI_ERR_TOO_SHORT); + } + if (*pLen > 3) { + + *pLen = 0; + return (SK_PNMI_ERR_BAD_VALUE); + } + KeyStr[0] = pBuf[1]; + KeyStr[1] = pBuf[2]; + KeyStr[2] = 0; + + /* Find the passed key in the array */ + for (Index = 0; Index < KeyNo; Index ++) { + + if (SK_STRCMP(KeyStr, KeyArr[Index]) == 0) { + + break; + } + } + /* + * If we cannot find the key it is wrong, so we + * return an appropriate error value. + */ + if (Index == KeyNo) { + + *pLen = 0; + return (SK_PNMI_ERR_BAD_VALUE); + } + + if (Action == SK_PNMI_PRESET) { + + return (SK_PNMI_ERR_OK); + } + + /* Ok, you wanted it and you will get it */ + Ret = VpdDelete(pAC, IoC, KeyStr); + if (Ret != SK_PNMI_VPD_OK) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR027, + SK_PNMI_ERR027MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + + /* + * Perform an update of the VPD data. This is + * not mandantory, but just to be sure. + */ + Ret = VpdUpdate(pAC, IoC); + if (Ret != SK_PNMI_VPD_OK) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR028, + SK_PNMI_ERR028MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + break; + + default: + *pLen = 0; + return (SK_PNMI_ERR_BAD_VALUE); + } + } + + return (SK_PNMI_ERR_OK); +} + +/***************************************************************************** + * + * General - OID handler function of various single instance OIDs + * + * Description: + * The code is simple. No description necessary. + * + * Returns: + * SK_PNMI_ERR_OK The request was successfully performed. + * SK_PNMI_ERR_GENERAL A general severe internal error occured. + * SK_PNMI_ERR_TOO_SHORT The passed buffer is too short to contain + * the correct data (e.g. a 32bit value is + * needed, but a 16 bit value was passed). + * SK_PNMI_ERR_UNKNOWN_INST The requested instance of the OID doesn't + * exist (e.g. port instance 3 on a two port + * adapter. + */ + +static int General( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +int Action, /* Get/PreSet/Set action */ +SK_U32 Id, /* Object ID that is to be processed */ +char *pBuf, /* Buffer to which to mgmt data will be retrieved */ +unsigned int *pLen, /* On call: buffer length. On return: used buffer */ +SK_U32 Instance, /* Instance (1..n) that is to be queried or -1 */ +unsigned int TableIndex) /* Index to the Id table */ +{ + int Ret; + unsigned int Index; + unsigned int Len; + unsigned int Offset; + unsigned int Val; + SK_U8 Val8; + SK_U16 Val16; + SK_U32 Val32; + SK_U64 Val64; + SK_U64 Val64RxHwErrs = 0; + SK_U64 Val64TxHwErrs = 0; + char Buf[256]; + + + /* + * Check instance. We only handle single instance variables + */ + if (Instance != (SK_U32)(-1) && Instance != 1) { + + *pLen = 0; + return (SK_PNMI_ERR_UNKNOWN_INST); + } + + /* + * Check action. We only allow get requests. + */ + if (Action != SK_PNMI_GET) { + + *pLen = 0; + return (SK_PNMI_ERR_READ_ONLY); + } + + /* + * Check length for the various supported OIDs + */ + switch (Id) { + + case OID_SKGE_PORT_NUMBER: + case OID_SKGE_DEVICE_TYPE: + case OID_SKGE_RESULT: + case OID_SKGE_RLMT_MONITOR_NUMBER: + case OID_GEN_XMIT_ERROR: + case OID_GEN_RCV_ERROR: + case OID_GEN_RCV_NO_BUFFER: + case OID_GEN_TRANSMIT_QUEUE_LENGTH: + case OID_SKGE_TRAP_NUMBER: + case OID_SKGE_MDB_VERSION: + if (*pLen < sizeof(SK_U32)) { + + *pLen = sizeof(SK_U32); + return (SK_PNMI_ERR_TOO_SHORT); + } + break; + + case OID_SKGE_CHIPSET: + if (*pLen < sizeof(SK_U16)) { + + *pLen = sizeof(SK_U16); + return (SK_PNMI_ERR_TOO_SHORT); + } + break; + + case OID_SKGE_BUS_TYPE: + case OID_SKGE_BUS_SPEED: + case OID_SKGE_BUS_WIDTH: + case OID_SKGE_SENSOR_NUMBER: + case OID_SKGE_CHKSM_NUMBER: + if (*pLen < sizeof(SK_U8)) { + + *pLen = sizeof(SK_U8); + return (SK_PNMI_ERR_TOO_SHORT); + } + break; + + case OID_SKGE_TX_SW_QUEUE_LEN: + case OID_SKGE_TX_SW_QUEUE_MAX: + case OID_SKGE_TX_RETRY: + case OID_SKGE_RX_INTR_CTS: + case OID_SKGE_TX_INTR_CTS: + case OID_SKGE_RX_NO_BUF_CTS: + case OID_SKGE_TX_NO_BUF_CTS: + case OID_SKGE_TX_USED_DESCR_NO: + case OID_SKGE_RX_DELIVERED_CTS: + case OID_SKGE_RX_OCTETS_DELIV_CTS: + case OID_SKGE_RX_HW_ERROR_CTS: + case OID_SKGE_TX_HW_ERROR_CTS: + case OID_SKGE_IN_ERRORS_CTS: + case OID_SKGE_OUT_ERROR_CTS: + case OID_SKGE_ERR_RECOVERY_CTS: + case OID_SKGE_SYSUPTIME: + if (*pLen < sizeof(SK_U64)) { + + *pLen = sizeof(SK_U64); + return (SK_PNMI_ERR_TOO_SHORT); + } + break; + + default: + /* Checked later */ + break; + } + + /* Update statistic */ + if (Id == OID_SKGE_RX_HW_ERROR_CTS || + Id == OID_SKGE_TX_HW_ERROR_CTS || + Id == OID_SKGE_IN_ERRORS_CTS || + Id == OID_SKGE_OUT_ERROR_CTS || + Id == OID_GEN_XMIT_ERROR || + Id == OID_GEN_RCV_ERROR) { + + /* Force the XMAC to update its statistic counters and + * Increment semaphore to indicate that an update was + * already done. + */ + Ret = MacUpdate(pAC, IoC, 0, pAC->GIni.GIMacsFound - 1); + if (Ret != SK_PNMI_ERR_OK) { + + *pLen = 0; + return (Ret); + } + pAC->Pnmi.MacUpdatedFlag ++; + + /* + * Some OIDs consist of multiple hardware counters. Those + * values which are contained in all of them will be added + * now. + */ + switch (Id) { + + case OID_SKGE_RX_HW_ERROR_CTS: + case OID_SKGE_IN_ERRORS_CTS: + case OID_GEN_RCV_ERROR: + Val64RxHwErrs = + GetStatVal(pAC, IoC, 0, SK_PNMI_HRX_MISSED) + + GetStatVal(pAC, IoC, 0, SK_PNMI_HRX_FRAMING) + + GetStatVal(pAC, IoC, 0, SK_PNMI_HRX_OVERFLOW)+ + GetStatVal(pAC, IoC, 0, SK_PNMI_HRX_JABBER) + + GetStatVal(pAC, IoC, 0, SK_PNMI_HRX_CARRIER) + + GetStatVal(pAC, IoC, 0, SK_PNMI_HRX_IRLENGTH)+ + GetStatVal(pAC, IoC, 0, SK_PNMI_HRX_SYMBOL) + + GetStatVal(pAC, IoC, 0, SK_PNMI_HRX_SHORTS) + + GetStatVal(pAC, IoC, 0, SK_PNMI_HRX_RUNT) + + GetStatVal(pAC, IoC, 0, SK_PNMI_HRX_TOO_LONG)+ + GetStatVal(pAC, IoC, 0, SK_PNMI_HRX_FCS) + + GetStatVal(pAC, IoC, 0, SK_PNMI_HRX_CEXT); + break; + + case OID_SKGE_TX_HW_ERROR_CTS: + case OID_SKGE_OUT_ERROR_CTS: + case OID_GEN_XMIT_ERROR: + Val64TxHwErrs = + GetStatVal(pAC, IoC, 0, + SK_PNMI_HTX_EXCESS_COL) + + GetStatVal(pAC, IoC, 0, SK_PNMI_HTX_LATE_COL)+ + GetStatVal(pAC, IoC, 0, SK_PNMI_HTX_UNDERRUN)+ + GetStatVal(pAC, IoC, 0, SK_PNMI_HTX_CARRIER)+ + GetStatVal(pAC, IoC, 0, + SK_PNMI_HTX_EXCESS_COL); + break; + } + } + + /* + * Retrieve value + */ + switch (Id) { + + case OID_SKGE_SUPPORTED_LIST: + Len = sizeof(IdTable)/sizeof(IdTable[0]) * sizeof(SK_U32); + if (*pLen < Len) { + + *pLen = Len; + return (SK_PNMI_ERR_TOO_SHORT); + } + for (Offset = 0, Index = 0; Offset < Len; + Offset += sizeof(SK_U32), Index ++) { + + Val32 = (SK_U32)IdTable[Index].Id; + SK_PNMI_STORE_U32(pBuf + Offset, Val32); + } + *pLen = Len; + break; + + case OID_SKGE_PORT_NUMBER: + Val32 = (SK_U32)pAC->GIni.GIMacsFound; + SK_PNMI_STORE_U32(pBuf, Val32); + *pLen = sizeof(SK_U32); + break; + + case OID_SKGE_DEVICE_TYPE: + Val32 = (SK_U32)pAC->Pnmi.DeviceType; + SK_PNMI_STORE_U32(pBuf, Val32); + *pLen = sizeof(SK_U32); + break; + + case OID_SKGE_DRIVER_DESCR: + if (pAC->Pnmi.pDriverDescription == NULL) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR007, + SK_PNMI_ERR007MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + + Len = SK_STRLEN(pAC->Pnmi.pDriverDescription) + 1; + if (Len > SK_PNMI_STRINGLEN1) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR029, + SK_PNMI_ERR029MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + + if (*pLen < Len) { + + *pLen = Len; + return (SK_PNMI_ERR_TOO_SHORT); + } + *pBuf = (char)(Len - 1); + SK_MEMCPY(pBuf + 1, pAC->Pnmi.pDriverDescription, Len - 1); + *pLen = Len; + break; + + case OID_SKGE_DRIVER_VERSION: + if (pAC->Pnmi.pDriverVersion == NULL) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR030, + SK_PNMI_ERR030MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + + Len = SK_STRLEN(pAC->Pnmi.pDriverVersion) + 1; + if (Len > SK_PNMI_STRINGLEN1) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR031, + SK_PNMI_ERR031MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + + if (*pLen < Len) { + + *pLen = Len; + return (SK_PNMI_ERR_TOO_SHORT); + } + *pBuf = (char)(Len - 1); + SK_MEMCPY(pBuf + 1, pAC->Pnmi.pDriverVersion, Len - 1); + *pLen = Len; + break; + + case OID_SKGE_HW_DESCR: + /* + * The hardware description is located in the VPD. This + * query may move to the initialisation routine. But + * the VPD data is cached and therefore a call here + * will not make much difference. + */ + Len = 256; + if (VpdRead(pAC, IoC, VPD_NAME, Buf, (int *)&Len) > 0) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR032, + SK_PNMI_ERR032MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + Len ++; + if (Len > SK_PNMI_STRINGLEN1) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR033, + SK_PNMI_ERR033MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + if (*pLen < Len) { + + *pLen = Len; + return (SK_PNMI_ERR_TOO_SHORT); + } + *pBuf = (char)(Len - 1); + SK_MEMCPY(pBuf + 1, Buf, Len - 1); + *pLen = Len; + break; + + case OID_SKGE_HW_VERSION: + /* Oh, I love to do some string manipulation */ + if (*pLen < 5) { + + *pLen = 5; + return (SK_PNMI_ERR_TOO_SHORT); + } + Val8 = (SK_U8)pAC->GIni.GIPciHwRev; + pBuf[0] = 4; + pBuf[1] = 'v'; + pBuf[2] = (char)(0x30 | ((Val8 >> 4) & 0x0F)); + pBuf[3] = '.'; + pBuf[4] = (char)(0x30 | (Val8 & 0x0F)); + *pLen = 5; + break; + + case OID_SKGE_CHIPSET: + Val16 = SK_PNMI_CHIPSET; + SK_PNMI_STORE_U16(pBuf, Val16); + *pLen = sizeof(SK_U16); + break; + + case OID_SKGE_BUS_TYPE: + *pBuf = (char)SK_PNMI_BUS_PCI; + *pLen = sizeof(char); + break; + + case OID_SKGE_BUS_SPEED: + *pBuf = pAC->Pnmi.PciBusSpeed; + *pLen = sizeof(char); + break; + + case OID_SKGE_BUS_WIDTH: + *pBuf = pAC->Pnmi.PciBusWidth; + *pLen = sizeof(char); + break; + + case OID_SKGE_RESULT: + Val32 = pAC->Pnmi.TestResult; + SK_PNMI_STORE_U32(pBuf, Val32); + *pLen = sizeof(SK_U32); + break; + + case OID_SKGE_SENSOR_NUMBER: + *pBuf = (char)pAC->I2c.MaxSens; + *pLen = sizeof(char); + break; + + case OID_SKGE_CHKSM_NUMBER: + *pBuf = SKCS_NUM_PROTOCOLS; + *pLen = sizeof(char); + break; + + case OID_SKGE_TRAP_NUMBER: + GetTrapQueueLen(pAC, &Len, &Val); + Val32 = (SK_U32)Val; + SK_PNMI_STORE_U32(pBuf, Val32); + *pLen = sizeof(SK_U32); + break; + + case OID_SKGE_TRAP: + GetTrapQueueLen(pAC, &Len, &Val); + if (*pLen < Len) { + + *pLen = Len; + return (SK_PNMI_ERR_TOO_SHORT); + } + CopyTrapQueue(pAC, pBuf); + *pLen = Len; + break; + + case OID_SKGE_RLMT_MONITOR_NUMBER: +/* XXX Not yet implemented by RLMT therefore we return zero elements */ + Val32 = 0; + SK_PNMI_STORE_U32(pBuf, Val32); + *pLen = sizeof(SK_U32); + break; + + case OID_SKGE_TX_SW_QUEUE_LEN: + Val64 = pAC->Pnmi.TxSwQueueLen; + SK_PNMI_STORE_U64(pBuf, Val64); + *pLen = sizeof(SK_U64); + break; + + case OID_SKGE_TX_SW_QUEUE_MAX: + Val64 = pAC->Pnmi.TxSwQueueMax; + SK_PNMI_STORE_U64(pBuf, Val64); + *pLen = sizeof(SK_U64); + break; + + case OID_SKGE_TX_RETRY: + Val64 = pAC->Pnmi.TxRetryCts; + SK_PNMI_STORE_U64(pBuf, Val64); + *pLen = sizeof(SK_U64); + break; + + case OID_SKGE_RX_INTR_CTS: + Val64 = pAC->Pnmi.RxIntrCts; + SK_PNMI_STORE_U64(pBuf, Val64); + *pLen = sizeof(SK_U64); + break; + + case OID_SKGE_TX_INTR_CTS: + Val64 = pAC->Pnmi.TxIntrCts; + SK_PNMI_STORE_U64(pBuf, Val64); + *pLen = sizeof(SK_U64); + break; + + case OID_SKGE_RX_NO_BUF_CTS: + Val64 = pAC->Pnmi.RxNoBufCts; + SK_PNMI_STORE_U64(pBuf, Val64); + *pLen = sizeof(SK_U64); + break; + + case OID_SKGE_TX_NO_BUF_CTS: + Val64 = pAC->Pnmi.TxNoBufCts; + SK_PNMI_STORE_U64(pBuf, Val64); + *pLen = sizeof(SK_U64); + break; + + case OID_SKGE_TX_USED_DESCR_NO: + Val64 = pAC->Pnmi.TxUsedDescrNo; + SK_PNMI_STORE_U64(pBuf, Val64); + *pLen = sizeof(SK_U64); + break; + + case OID_SKGE_RX_DELIVERED_CTS: + Val64 = pAC->Pnmi.RxDeliveredCts; + SK_PNMI_STORE_U64(pBuf, Val64); + *pLen = sizeof(SK_U64); + break; + + case OID_SKGE_RX_OCTETS_DELIV_CTS: + Val64 = pAC->Pnmi.RxOctetsDeliveredCts; + SK_PNMI_STORE_U64(pBuf, Val64); + *pLen = sizeof(SK_U64); + break; + + case OID_SKGE_RX_HW_ERROR_CTS: + SK_PNMI_STORE_U64(pBuf, Val64RxHwErrs); + *pLen = sizeof(SK_U64); + break; + + case OID_SKGE_TX_HW_ERROR_CTS: + SK_PNMI_STORE_U64(pBuf, Val64TxHwErrs); + *pLen = sizeof(SK_U64); + break; + + case OID_SKGE_IN_ERRORS_CTS: + Val64 = Val64RxHwErrs + pAC->Pnmi.RxNoBufCts; + SK_PNMI_STORE_U64(pBuf, Val64); + *pLen = sizeof(SK_U64); + break; + + case OID_SKGE_OUT_ERROR_CTS: + Val64 = Val64TxHwErrs + pAC->Pnmi.TxNoBufCts; + SK_PNMI_STORE_U64(pBuf, Val64); + *pLen = sizeof(SK_U64); + break; + + case OID_SKGE_ERR_RECOVERY_CTS: + Val64 = pAC->Pnmi.ErrRecoveryCts; + SK_PNMI_STORE_U64(pBuf, Val64); + *pLen = sizeof(SK_U64); + break; + + case OID_SKGE_SYSUPTIME: + Val64 = SK_PNMI_HUNDREDS_SEC(SkOsGetTime(pAC)); + Val64 -= pAC->Pnmi.StartUpTime; + SK_PNMI_STORE_U64(pBuf, Val64); + *pLen = sizeof(SK_U64); + break; + + case OID_SKGE_MDB_VERSION: + Val32 = SK_PNMI_MDB_VERSION; + SK_PNMI_STORE_U32(pBuf, Val32); + *pLen = sizeof(SK_U32); + break; + + case OID_GEN_RCV_ERROR: + Val32 = (SK_U32)(Val64RxHwErrs + pAC->Pnmi.RxNoBufCts); + SK_PNMI_STORE_U32(pBuf, Val32); + *pLen = sizeof(SK_U32); + break; + + case OID_GEN_XMIT_ERROR: + Val32 = (SK_U32)(Val64TxHwErrs + pAC->Pnmi.TxNoBufCts); + SK_PNMI_STORE_U32(pBuf, Val32); + *pLen = sizeof(SK_U32); + break; + + case OID_GEN_RCV_NO_BUFFER: + Val32 = (SK_U32)pAC->Pnmi.RxNoBufCts; + SK_PNMI_STORE_U32(pBuf, Val32); + *pLen = sizeof(SK_U32); + break; + + case OID_GEN_TRANSMIT_QUEUE_LENGTH: + Val32 = (SK_U32)pAC->Pnmi.TxSwQueueLen; + SK_PNMI_STORE_U32(pBuf, Val32); + *pLen = sizeof(SK_U32); + break; + + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR034, + SK_PNMI_ERR034MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + + if (Id == OID_SKGE_RX_HW_ERROR_CTS || + Id == OID_SKGE_TX_HW_ERROR_CTS || + Id == OID_SKGE_IN_ERRORS_CTS || + Id == OID_SKGE_OUT_ERROR_CTS || + Id == OID_GEN_XMIT_ERROR || + Id == OID_GEN_RCV_ERROR) { + + pAC->Pnmi.MacUpdatedFlag --; + } + + return (SK_PNMI_ERR_OK); +} + +/***************************************************************************** + * + * Rlmt - OID handler function of OID_SKGE_RLMT_XXX single instance. + * + * Description: + * Get/Presets/Sets the RLMT OIDs. + * + * Returns: + * SK_PNMI_ERR_OK The request was successfully performed. + * SK_PNMI_ERR_GENERAL A general severe internal error occured. + * SK_PNMI_ERR_TOO_SHORT The passed buffer is too short to contain + * the correct data (e.g. a 32bit value is + * needed, but a 16 bit value was passed). + * SK_PNMI_ERR_BAD_VALUE The passed value is not in the valid + * value range. + * SK_PNMI_ERR_READ_ONLY The OID is read-only and cannot be set. + * SK_PNMI_ERR_UNKNOWN_INST The requested instance of the OID doesn't + * exist (e.g. port instance 3 on a two port + * adapter. + */ + +static int Rlmt( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +int Action, /* Get/PreSet/Set action */ +SK_U32 Id, /* Object ID that is to be processed */ +char *pBuf, /* Buffer to which to mgmt data will be retrieved */ +unsigned int *pLen, /* On call: buffer length. On return: used buffer */ +SK_U32 Instance, /* Instance (1..n) that is to be queried or -1 */ +unsigned int TableIndex) /* Index to the Id table */ +{ + int Ret; + unsigned int PhysPortIndex; + unsigned int PhysPortMax; + SK_EVPARA EventParam; + SK_U32 Val32; + SK_U64 Val64; + + + /* + * Check instance. Only single instance OIDs are allowed here. + */ + if (Instance != (SK_U32)(-1) && Instance != 1) { + + *pLen = 0; + return (SK_PNMI_ERR_UNKNOWN_INST); + } + + /* + * Perform the requested action + */ + if (Action == SK_PNMI_GET) { + + /* + * Check if the buffer length is large enough. + */ + + switch (Id) { + + case OID_SKGE_RLMT_MODE: + case OID_SKGE_RLMT_PORT_ACTIVE: + case OID_SKGE_RLMT_PORT_PREFERED: + if (*pLen < sizeof(SK_U8)) { + + *pLen = sizeof(SK_U8); + return (SK_PNMI_ERR_TOO_SHORT); + } + break; + + case OID_SKGE_RLMT_PORT_NUMBER: + if (*pLen < sizeof(SK_U32)) { + + *pLen = sizeof(SK_U32); + return (SK_PNMI_ERR_TOO_SHORT); + } + break; + + case OID_SKGE_RLMT_CHANGE_CTS: + case OID_SKGE_RLMT_CHANGE_TIME: + case OID_SKGE_RLMT_CHANGE_ESTIM: + case OID_SKGE_RLMT_CHANGE_THRES: + if (*pLen < sizeof(SK_U64)) { + + *pLen = sizeof(SK_U64); + return (SK_PNMI_ERR_TOO_SHORT); + } + break; + + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR035, + SK_PNMI_ERR035MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + + /* + * Update RLMT statistic and increment semaphores to indicate + * that an update was already done. Maybe RLMT will hold its + * statistic always up to date some time. Then we can + * remove this type of call. + */ + if ((Ret = RlmtUpdate(pAC, IoC)) != SK_PNMI_ERR_OK) { + + *pLen = 0; + return (Ret); + } + pAC->Pnmi.RlmtUpdatedFlag ++; + + /* + * Retrieve Value + */ + switch (Id) { + + case OID_SKGE_RLMT_MODE: + *pBuf = (char)pAC->Rlmt.RlmtMode; + *pLen = sizeof(char); + break; + + case OID_SKGE_RLMT_PORT_NUMBER: + Val32 = (SK_U32)pAC->GIni.GIMacsFound; + SK_PNMI_STORE_U32(pBuf, Val32); + *pLen = sizeof(SK_U32); + break; + + case OID_SKGE_RLMT_PORT_ACTIVE: + *pBuf = 0; + /* + * If multiple ports may become active this OID + * doesn't make sense any more. A new variable in + * the port structure should be created. However, + * for this variable the first active port is + * returned. + */ + PhysPortMax = pAC->GIni.GIMacsFound; + + for (PhysPortIndex = 0; PhysPortIndex < PhysPortMax; + PhysPortIndex ++) { + + if (pAC->Pnmi.Port[PhysPortIndex].ActiveFlag) { + + *pBuf = (char)SK_PNMI_PORT_PHYS2LOG( + PhysPortIndex); + break; + } + } + *pLen = sizeof(char); + break; + + case OID_SKGE_RLMT_PORT_PREFERED: + *pBuf = (char)SK_PNMI_PORT_PHYS2LOG( + pAC->Rlmt.MacPreferred); + *pLen = sizeof(char); + break; + + case OID_SKGE_RLMT_CHANGE_CTS: + Val64 = pAC->Pnmi.RlmtChangeCts; + SK_PNMI_STORE_U64(pBuf, Val64); + *pLen = sizeof(SK_U64); + break; + + case OID_SKGE_RLMT_CHANGE_TIME: + Val64 = pAC->Pnmi.RlmtChangeTime; + SK_PNMI_STORE_U64(pBuf, Val64); + *pLen = sizeof(SK_U64); + break; + + case OID_SKGE_RLMT_CHANGE_ESTIM: + Val64 = pAC->Pnmi.RlmtChangeEstimate.Estimate; + SK_PNMI_STORE_U64(pBuf, Val64); + *pLen = sizeof(SK_U64); + break; + + case OID_SKGE_RLMT_CHANGE_THRES: + Val64 = pAC->Pnmi.RlmtChangeThreshold; + SK_PNMI_STORE_U64(pBuf, Val64); + *pLen = sizeof(SK_U64); + break; + + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR036, + SK_PNMI_ERR036MSG); + + pAC->Pnmi.RlmtUpdatedFlag --; + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + + pAC->Pnmi.RlmtUpdatedFlag --; + } + else { + /* Perform a preset or set */ + switch (Id) { + + case OID_SKGE_RLMT_MODE: + /* Check if the buffer length is plausible */ + if (*pLen < sizeof(char)) { + + *pLen = sizeof(char); + return (SK_PNMI_ERR_TOO_SHORT); + } + /* Check if the value range is correct */ + if (*pLen != sizeof(char) || + (*pBuf & SK_PNMI_RLMT_MODE_CHK_LINK) == 0 || + *(SK_U8 *)pBuf > 15) { + + *pLen = 0; + return (SK_PNMI_ERR_BAD_VALUE); + } + /* The preset ends here */ + if (Action == SK_PNMI_PRESET) { + + *pLen = 0; + return (SK_PNMI_ERR_OK); + } + /* Send an event to RLMT to change the mode */ + SK_MEMSET((char *)&EventParam, 0, sizeof(EventParam)); + EventParam.Para32[0] |= (SK_U32)(*pBuf); + if (SkRlmtEvent(pAC, IoC, SK_RLMT_MODE_CHANGE, + EventParam) > 0) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR037, + SK_PNMI_ERR037MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + break; + + case OID_SKGE_RLMT_PORT_PREFERED: + /* Check if the buffer length is plausible */ + if (*pLen < sizeof(char)) { + + *pLen = sizeof(char); + return (SK_PNMI_ERR_TOO_SHORT); + } + /* Check if the value range is correct */ + if (*pLen != sizeof(char) || *(SK_U8 *)pBuf > + (SK_U8)pAC->GIni.GIMacsFound) { + + *pLen = 0; + return (SK_PNMI_ERR_BAD_VALUE); + } + /* The preset ends here */ + if (Action == SK_PNMI_PRESET) { + + *pLen = 0; + return (SK_PNMI_ERR_OK); + } + + /* + * Send an event to RLMT change the preferred port. + * A param of -1 means automatic mode. RLMT will + * make the decision which is the preferred port. + */ + SK_MEMSET((char *)&EventParam, 0, sizeof(EventParam)); + EventParam.Para32[0] = (SK_U32)(*pBuf) - 1; + if (SkRlmtEvent(pAC, IoC, SK_RLMT_PREFPORT_CHANGE, + EventParam) > 0) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR038, + SK_PNMI_ERR038MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + break; + + case OID_SKGE_RLMT_CHANGE_THRES: + /* Check if the buffer length is plausible */ + if (*pLen < sizeof(SK_U64)) { + + *pLen = sizeof(SK_U64); + return (SK_PNMI_ERR_TOO_SHORT); + } + /* + * There are not many restrictions to the + * value range. + */ + if (*pLen != sizeof(SK_U64)) { + + *pLen = 0; + return (SK_PNMI_ERR_BAD_VALUE); + } + /* A preset ends here */ + if (Action == SK_PNMI_PRESET) { + + *pLen = 0; + return (SK_PNMI_ERR_OK); + } + /* + * Store the new threshold, which will be taken + * on the next timer event. + */ + SK_PNMI_READ_U64(pBuf, Val64); + pAC->Pnmi.RlmtChangeThreshold = Val64; + break; + + default: + /* The other OIDs are not be able for set */ + *pLen = 0; + return (SK_PNMI_ERR_READ_ONLY); + } + } + + return (SK_PNMI_ERR_OK); +} + +/***************************************************************************** + * + * RlmtStat - OID handler function of OID_SKGE_RLMT_XXX multiple instance. + * + * Description: + * Performs get requests on multiple instance variables. + * + * Returns: + * SK_PNMI_ERR_OK The request was successfully performed. + * SK_PNMI_ERR_GENERAL A general severe internal error occured. + * SK_PNMI_ERR_TOO_SHORT The passed buffer is too short to contain + * the correct data (e.g. a 32bit value is + * needed, but a 16 bit value was passed). + * SK_PNMI_ERR_UNKNOWN_INST The requested instance of the OID doesn't + * exist (e.g. port instance 3 on a two port + * adapter. + */ + +static int RlmtStat( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +int Action, /* Get/PreSet/Set action */ +SK_U32 Id, /* Object ID that is to be processed */ +char *pBuf, /* Buffer to which to mgmt data will be retrieved */ +unsigned int *pLen, /* On call: buffer length. On return: used buffer */ +SK_U32 Instance, /* Instance (1..n) that is to be queried or -1 */ +unsigned int TableIndex) /* Index to the Id table */ +{ + unsigned int PhysPortMax; + unsigned int PhysPortIndex; + unsigned int Limit; + unsigned int Offset; + int Ret; + SK_U32 Val32; + SK_U64 Val64; + + + /* + * Calculate the port indexes from the instance + */ + PhysPortMax = pAC->GIni.GIMacsFound; + + if ((Instance != (SK_U32)(-1))) { + + if ((Instance < 1) || (Instance > PhysPortMax)) { + + *pLen = 0; + return (SK_PNMI_ERR_UNKNOWN_INST); + } + + PhysPortIndex = Instance - 1; + Limit = PhysPortIndex + 1; + } + else { + PhysPortIndex = 0; + Limit = PhysPortMax; + } + + /* + * Currently only get requests are allowed. + */ + if (Action != SK_PNMI_GET) { + + *pLen = 0; + return (SK_PNMI_ERR_READ_ONLY); + } + + /* + * Check if the buffer length is large enough. + */ + switch (Id) { + + case OID_SKGE_RLMT_PORT_INDEX: + case OID_SKGE_RLMT_STATUS: + if (*pLen < (Limit - PhysPortIndex) * sizeof(SK_U32)) { + + *pLen = (Limit - PhysPortIndex) * sizeof(SK_U32); + return (SK_PNMI_ERR_TOO_SHORT); + } + break; + + case OID_SKGE_RLMT_TX_HELLO_CTS: + case OID_SKGE_RLMT_RX_HELLO_CTS: + case OID_SKGE_RLMT_TX_SP_REQ_CTS: + case OID_SKGE_RLMT_RX_SP_CTS: + if (*pLen < (Limit - PhysPortIndex) * sizeof(SK_U64)) { + + *pLen = (Limit - PhysPortIndex) * sizeof(SK_U64); + return (SK_PNMI_ERR_TOO_SHORT); + } + break; + + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR039, + SK_PNMI_ERR039MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + + } + + /* + * Update statistic and increment semaphores to indicate that + * an update was already done. + */ + if ((Ret = RlmtUpdate(pAC, IoC)) != SK_PNMI_ERR_OK) { + + *pLen = 0; + return (Ret); + } + pAC->Pnmi.RlmtUpdatedFlag ++; + + /* + * Get value + */ + Offset = 0; + for (; PhysPortIndex < Limit; PhysPortIndex ++) { + + switch (Id) { + + case OID_SKGE_RLMT_PORT_INDEX: + Val32 = PhysPortIndex; + SK_PNMI_STORE_U32(pBuf + Offset, Val32); + Offset += sizeof(SK_U32); + break; + + case OID_SKGE_RLMT_STATUS: + if (pAC->Rlmt.Port[PhysPortIndex].PortState == + SK_RLMT_PS_INIT || + pAC->Rlmt.Port[PhysPortIndex].PortState == + SK_RLMT_PS_DOWN) { + + Val32 = SK_PNMI_RLMT_STATUS_ERROR; + } + else if (pAC->Pnmi.Port[PhysPortIndex].ActiveFlag) { + + Val32 = SK_PNMI_RLMT_STATUS_ACTIVE; + } + else { + Val32 = SK_PNMI_RLMT_STATUS_STANDBY; + } + SK_PNMI_STORE_U32(pBuf + Offset, Val32); + Offset += sizeof(SK_U32); + break; + + case OID_SKGE_RLMT_TX_HELLO_CTS: + Val64 = pAC->Rlmt.Port[PhysPortIndex].TxHelloCts; + SK_PNMI_STORE_U64(pBuf + Offset, Val64); + Offset += sizeof(SK_U64); + break; + + case OID_SKGE_RLMT_RX_HELLO_CTS: + Val64 = pAC->Rlmt.Port[PhysPortIndex].RxHelloCts; + SK_PNMI_STORE_U64(pBuf + Offset, Val64); + Offset += sizeof(SK_U64); + break; + + case OID_SKGE_RLMT_TX_SP_REQ_CTS: + Val64 = pAC->Rlmt.Port[PhysPortIndex].TxSpHelloReqCts; + SK_PNMI_STORE_U64(pBuf + Offset, Val64); + Offset += sizeof(SK_U64); + break; + + case OID_SKGE_RLMT_RX_SP_CTS: + Val64 = pAC->Rlmt.Port[PhysPortIndex].RxSpHelloCts; + SK_PNMI_STORE_U64(pBuf + Offset, Val64); + Offset += sizeof(SK_U64); + break; + + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR040, + SK_PNMI_ERR040MSG); + + pAC->Pnmi.RlmtUpdatedFlag --; + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + } + *pLen = Offset; + + pAC->Pnmi.RlmtUpdatedFlag --; + + return (SK_PNMI_ERR_OK); +} + +/***************************************************************************** + * + * MacPrivateConf - OID handler function of OIDs concerning the configuration + * + * Description: + * Get/Presets/Sets the OIDs concerning the configuration. + * + * Returns: + * SK_PNMI_ERR_OK The request was successfully performed. + * SK_PNMI_ERR_GENERAL A general severe internal error occured. + * SK_PNMI_ERR_TOO_SHORT The passed buffer is too short to contain + * the correct data (e.g. a 32bit value is + * needed, but a 16 bit value was passed). + * SK_PNMI_ERR_BAD_VALUE The passed value is not in the valid + * value range. + * SK_PNMI_ERR_READ_ONLY The OID is read-only and cannot be set. + * SK_PNMI_ERR_UNKNOWN_INST The requested instance of the OID doesn't + * exist (e.g. port instance 3 on a two port + * adapter. + */ + +static int MacPrivateConf( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +int Action, /* Get/PreSet/Set action */ +SK_U32 Id, /* Object ID that is to be processed */ +char *pBuf, /* Buffer to which to mgmt data will be retrieved */ +unsigned int *pLen, /* On call: buffer length. On return: used buffer */ +SK_U32 Instance, /* Instance (1..n) that is to be queried or -1 */ +unsigned int TableIndex) /* Index to the Id table */ +{ + unsigned int PhysPortMax; + unsigned int PhysPortIndex; + unsigned int LogPortMax; + unsigned int LogPortIndex; + unsigned int Limit; + unsigned int Offset; + char Val8; + int Ret; + SK_EVPARA EventParam; + + + /* + * Calculate instance if wished. MAC index 0 is the virtual + * MAC. + */ + PhysPortMax = pAC->GIni.GIMacsFound; + LogPortMax = SK_PNMI_PORT_PHYS2LOG(PhysPortMax); + + if ((Instance != (SK_U32)(-1))) { + + if ((Instance < 1) || (Instance > LogPortMax)) { + + *pLen = 0; + return (SK_PNMI_ERR_UNKNOWN_INST); + } + + LogPortIndex = SK_PNMI_PORT_INST2LOG(Instance); + Limit = LogPortIndex + 1; + } + else { + LogPortIndex = 0; + Limit = LogPortMax; + } + + /* + * Perform action + */ + if (Action == SK_PNMI_GET) { + + /* + * Check length + */ + switch (Id) { + + case OID_SKGE_PMD: + case OID_SKGE_CONNECTOR: + case OID_SKGE_LINK_CAP: + case OID_SKGE_LINK_MODE: + case OID_SKGE_LINK_MODE_STATUS: + case OID_SKGE_LINK_STATUS: + case OID_SKGE_FLOWCTRL_CAP: + case OID_SKGE_FLOWCTRL_MODE: + case OID_SKGE_FLOWCTRL_STATUS: + case OID_SKGE_PHY_OPERATION_CAP: + case OID_SKGE_PHY_OPERATION_MODE: + case OID_SKGE_PHY_OPERATION_STATUS: + if (*pLen < (Limit - LogPortIndex) * sizeof(SK_U8)) { + + *pLen = (Limit - LogPortIndex) * + sizeof(SK_U8); + return (SK_PNMI_ERR_TOO_SHORT); + } + break; + + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR041, + SK_PNMI_ERR041MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + + /* + * Update statistic and increment semaphore to indicate + * that an update was already done. + */ + if ((Ret = SirqUpdate(pAC, IoC)) != SK_PNMI_ERR_OK) { + + *pLen = 0; + return (Ret); + } + pAC->Pnmi.SirqUpdatedFlag ++; + + /* + * Get value + */ + Offset = 0; + for (; LogPortIndex < Limit; LogPortIndex ++) { + + switch (Id) { + + case OID_SKGE_PMD: + *(pBuf + Offset) = pAC->Pnmi.PMD; + Offset += sizeof(char); + break; + + case OID_SKGE_CONNECTOR: + *(pBuf + Offset) = pAC->Pnmi.Connector; + Offset += sizeof(char); + break; + + case OID_SKGE_LINK_CAP: + if (LogPortIndex == 0) { + + /* Get value for virtual port */ + VirtualConf(pAC, IoC, Id, pBuf + + Offset); + } + else { + /* Get value for physical ports */ + PhysPortIndex = SK_PNMI_PORT_LOG2PHYS( + pAC, LogPortIndex); + + *(pBuf + Offset) = pAC->GIni.GP[ + PhysPortIndex].PLinkCap; + } + Offset += sizeof(char); + break; + + case OID_SKGE_LINK_MODE: + if (LogPortIndex == 0) { + + /* Get value for virtual port */ + VirtualConf(pAC, IoC, Id, pBuf + + Offset); + } + else { + /* Get value for physical ports */ + PhysPortIndex = SK_PNMI_PORT_LOG2PHYS( + pAC, LogPortIndex); + + *(pBuf + Offset) = pAC->GIni.GP[ + PhysPortIndex].PLinkModeConf; + } + + Offset += sizeof(char); + break; + + case OID_SKGE_LINK_MODE_STATUS: + if (LogPortIndex == 0) { + + /* Get value for virtual port */ + VirtualConf(pAC, IoC, Id, pBuf + + Offset); + } + else { + /* Get value for physical port */ + PhysPortIndex = SK_PNMI_PORT_LOG2PHYS( + pAC, LogPortIndex); + + *(pBuf + Offset) = + CalculateLinkModeStatus(pAC, + IoC, PhysPortIndex); + } + Offset += sizeof(char); + break; + + case OID_SKGE_LINK_STATUS: + if (LogPortIndex == 0) { + + /* Get value for virtual port */ + VirtualConf(pAC, IoC, Id, pBuf + + Offset); + } + else { + /* Get value for physical ports */ + PhysPortIndex = SK_PNMI_PORT_LOG2PHYS( + pAC, LogPortIndex); + + *(pBuf + Offset) = + CalculateLinkStatus(pAC, + IoC, PhysPortIndex); + } + Offset += sizeof(char); + break; + + case OID_SKGE_FLOWCTRL_CAP: + if (LogPortIndex == 0) { + + /* Get value for virtual port */ + VirtualConf(pAC, IoC, Id, pBuf + + Offset); + } + else { + /* Get value for physical ports */ + PhysPortIndex = SK_PNMI_PORT_LOG2PHYS( + pAC, LogPortIndex); + + *(pBuf + Offset) = pAC->GIni.GP[ + PhysPortIndex].PFlowCtrlCap; + } + Offset += sizeof(char); + break; + + case OID_SKGE_FLOWCTRL_MODE: + if (LogPortIndex == 0) { + + /* Get value for virtual port */ + VirtualConf(pAC, IoC, Id, pBuf + + Offset); + } + else { + /* Get value for physical port */ + PhysPortIndex = SK_PNMI_PORT_LOG2PHYS( + pAC, LogPortIndex); + + *(pBuf + Offset) = pAC->GIni.GP[ + PhysPortIndex].PFlowCtrlMode; + } + Offset += sizeof(char); + break; + + case OID_SKGE_FLOWCTRL_STATUS: + if (LogPortIndex == 0) { + + /* Get value for virtual port */ + VirtualConf(pAC, IoC, Id, pBuf + + Offset); + } + else { + /* Get value for physical port */ + PhysPortIndex = SK_PNMI_PORT_LOG2PHYS( + pAC, LogPortIndex); + + *(pBuf + Offset) = pAC->GIni.GP[ + PhysPortIndex].PFlowCtrlStatus; + } + Offset += sizeof(char); + break; + + case OID_SKGE_PHY_OPERATION_CAP: + if (LogPortIndex == 0) { + + /* Get value for virtual port */ + VirtualConf(pAC, IoC, Id, pBuf + + Offset); + } + else { + /* Get value for physical ports */ + PhysPortIndex = SK_PNMI_PORT_LOG2PHYS( + pAC, LogPortIndex); + + *(pBuf + Offset) = pAC->GIni.GP[ + PhysPortIndex].PMSCap; + } + Offset += sizeof(char); + break; + + case OID_SKGE_PHY_OPERATION_MODE: + if (LogPortIndex == 0) { + + /* Get value for virtual port */ + VirtualConf(pAC, IoC, Id, pBuf + + Offset); + } + else { + /* Get value for physical port */ + PhysPortIndex = SK_PNMI_PORT_LOG2PHYS( + pAC, LogPortIndex); + + *(pBuf + Offset) = pAC->GIni.GP[ + PhysPortIndex].PMSMode; + } + Offset += sizeof(char); + break; + + case OID_SKGE_PHY_OPERATION_STATUS: + if (LogPortIndex == 0) { + + /* Get value for virtual port */ + VirtualConf(pAC, IoC, Id, pBuf + + Offset); + } + else { + /* Get value for physical port */ + PhysPortIndex = SK_PNMI_PORT_LOG2PHYS( + pAC, LogPortIndex); + + *(pBuf + Offset) = pAC->GIni.GP[ + PhysPortIndex].PMSStatus; + } + Offset += sizeof(char); + break; + + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR042, + SK_PNMI_ERR042MSG); + + pAC->Pnmi.SirqUpdatedFlag --; + return (SK_PNMI_ERR_GENERAL); + } + } + *pLen = Offset; + pAC->Pnmi.SirqUpdatedFlag --; + + return (SK_PNMI_ERR_OK); + } + + /* + * From here SET or PRESET action. Check if the passed + * buffer length is plausible. + */ + switch (Id) { + + case OID_SKGE_LINK_MODE: + case OID_SKGE_FLOWCTRL_MODE: + if (*pLen < Limit - LogPortIndex) { + + *pLen = Limit - LogPortIndex; + return (SK_PNMI_ERR_TOO_SHORT); + } + if (*pLen != Limit - LogPortIndex) { + + *pLen = 0; + return (SK_PNMI_ERR_BAD_VALUE); + } + break; + + default: + *pLen = 0; + return (SK_PNMI_ERR_READ_ONLY); + } + + /* + * Perform preset or set + */ + Offset = 0; + for (; LogPortIndex < Limit; LogPortIndex ++) { + + switch (Id) { + + case OID_SKGE_LINK_MODE: + /* Check the value range */ + Val8 = *(pBuf + Offset); + if (Val8 == 0) { + + Offset += sizeof(char); + break; + } + if (Val8 < SK_LMODE_HALF || + Val8 > SK_LMODE_AUTOSENSE) { + + *pLen = 0; + return (SK_PNMI_ERR_BAD_VALUE); + } + + /* The preset ends here */ + if (Action == SK_PNMI_PRESET) { + + return (SK_PNMI_ERR_OK); + } + + if (LogPortIndex == 0) { + + /* + * The virtual port consists of all currently + * active ports. Find them and send an event + * with the new link mode to SIRQ. + */ + for (PhysPortIndex = 0; + PhysPortIndex < PhysPortMax; + PhysPortIndex ++) { + + if (!pAC->Pnmi.Port[PhysPortIndex]. + ActiveFlag) { + + continue; + } + + EventParam.Para32[0] = PhysPortIndex; + EventParam.Para32[1] = (SK_U32)Val8; + if (SkGeSirqEvent(pAC, IoC, + SK_HWEV_SET_LMODE, + EventParam) > 0) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, + SK_PNMI_ERR043, + SK_PNMI_ERR043MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + } + } + else { + /* + * Send an event with the new link mode to + * the SIRQ module. + */ + EventParam.Para32[0] = SK_PNMI_PORT_LOG2PHYS( + pAC, LogPortIndex); + EventParam.Para32[1] = (SK_U32)Val8; + if (SkGeSirqEvent(pAC, IoC, SK_HWEV_SET_LMODE, + EventParam) > 0) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, + SK_PNMI_ERR043, + SK_PNMI_ERR043MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + } + Offset += sizeof(char); + break; + + case OID_SKGE_FLOWCTRL_MODE: + /* Check the value range */ + Val8 = *(pBuf + Offset); + if (Val8 == 0) { + + Offset += sizeof(char); + break; + } + if (Val8 < SK_FLOW_MODE_NONE || + Val8 > SK_FLOW_MODE_SYM_OR_REM) { + + *pLen = 0; + return (SK_PNMI_ERR_BAD_VALUE); + } + + /* The preset ends here */ + if (Action == SK_PNMI_PRESET) { + + return (SK_PNMI_ERR_OK); + } + + if (LogPortIndex == 0) { + + /* + * The virtual port consists of all currently + * active ports. Find them and send an event + * with the new flow control mode to SIRQ. + */ + for (PhysPortIndex = 0; + PhysPortIndex < PhysPortMax; + PhysPortIndex ++) { + + if (!pAC->Pnmi.Port[PhysPortIndex]. + ActiveFlag) { + + continue; + } + + EventParam.Para32[0] = PhysPortIndex; + EventParam.Para32[1] = (SK_U32)Val8; + if (SkGeSirqEvent(pAC, IoC, + SK_HWEV_SET_FLOWMODE, + EventParam) > 0) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, + SK_PNMI_ERR044, + SK_PNMI_ERR044MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + } + } + else { + /* + * Send an event with the new flow control + * mode to the SIRQ module. + */ + EventParam.Para32[0] = SK_PNMI_PORT_LOG2PHYS( + pAC, LogPortIndex); + EventParam.Para32[1] = (SK_U32)Val8; + if (SkGeSirqEvent(pAC, IoC, + SK_HWEV_SET_FLOWMODE, EventParam) + > 0) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, + SK_PNMI_ERR044, + SK_PNMI_ERR044MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + } + Offset += sizeof(char); + break; + + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR045, + SK_PNMI_ERR045MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + } + + return (SK_PNMI_ERR_OK); +} + +/***************************************************************************** + * + * Monitor - OID handler function for RLMT_MONITOR_XXX + * + * Description: + * Because RLMT currently does not support the monitoring of + * remote adapter cards, we return always an empty table. + * + * Returns: + * SK_PNMI_ERR_OK The request was successfully performed. + * SK_PNMI_ERR_GENERAL A general severe internal error occured. + * SK_PNMI_ERR_TOO_SHORT The passed buffer is too short to contain + * the correct data (e.g. a 32bit value is + * needed, but a 16 bit value was passed). + * SK_PNMI_ERR_BAD_VALUE The passed value is not in the valid + * value range. + * SK_PNMI_ERR_READ_ONLY The OID is read-only and cannot be set. + * SK_PNMI_ERR_UNKNOWN_INST The requested instance of the OID doesn't + * exist (e.g. port instance 3 on a two port + * adapter. + */ + +static int Monitor( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +int Action, /* Get/PreSet/Set action */ +SK_U32 Id, /* Object ID that is to be processed */ +char *pBuf, /* Buffer to which to mgmt data will be retrieved */ +unsigned int *pLen, /* On call: buffer length. On return: used buffer */ +SK_U32 Instance, /* Instance (1..n) that is to be queried or -1 */ +unsigned int TableIndex) /* Index to the Id table */ +{ + unsigned int Index; + unsigned int Limit; + unsigned int Offset; + unsigned int Entries; + + + /* + * Calculate instance if wished. + */ +/* XXX Not yet implemented. Return always an empty table. */ + Entries = 0; + + if ((Instance != (SK_U32)(-1))) { + + if ((Instance < 1) || (Instance > Entries)) { + + *pLen = 0; + return (SK_PNMI_ERR_UNKNOWN_INST); + } + + Index = (unsigned int)Instance - 1; + Limit = (unsigned int)Instance; + } + else { + Index = 0; + Limit = Entries; + } + + /* + * Get/Set value + */ + if (Action == SK_PNMI_GET) { + + for (Offset=0; Index < Limit; Index ++) { + + switch (Id) { + + case OID_SKGE_RLMT_MONITOR_INDEX: + case OID_SKGE_RLMT_MONITOR_ADDR: + case OID_SKGE_RLMT_MONITOR_ERRS: + case OID_SKGE_RLMT_MONITOR_TIMESTAMP: + case OID_SKGE_RLMT_MONITOR_ADMIN: + break; + + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR046, + SK_PNMI_ERR046MSG); + + *pLen = 0; + return (SK_PNMI_ERR_GENERAL); + } + } + *pLen = Offset; + } + else { + /* Only MONITOR_ADMIN can be set */ + if (Id != OID_SKGE_RLMT_MONITOR_ADMIN) { + + *pLen = 0; + return (SK_PNMI_ERR_READ_ONLY); + } + + /* Check if the length is plausible */ + if (*pLen < (Limit - Index)) { + + return (SK_PNMI_ERR_TOO_SHORT); + } + /* Okay, we have a wide value range */ + if (*pLen != (Limit - Index)) { + + *pLen = 0; + return (SK_PNMI_ERR_BAD_VALUE); + } +/* + for (Offset=0; Index < Limit; Index ++) { + } +*/ +/* + * XXX Not yet implemented. Return always BAD_VALUE, because the table + * is empty. + */ + *pLen = 0; + return (SK_PNMI_ERR_BAD_VALUE); + } + + return (SK_PNMI_ERR_OK); +} + +/***************************************************************************** + * + * VirtualConf - Calculates the values of configuration OIDs for virtual port + * + * Description: + * We handle here the get of the configuration group OIDs, which are + * a little bit complicated. The virtual port consists of all currently + * active physical ports. If multiple ports are active and configured + * differently we get in some trouble to return a single value. So we + * get the value of the first active port and compare it with that of + * the other active ports. If they are not the same, we return a value + * that indicates that the state is indeterminated. + * + * Returns: + * Nothing + */ + +static void VirtualConf( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +SK_U32 Id, /* Object ID that is to be processed */ +char *pBuf) /* Buffer to which to mgmt data will be retrieved */ +{ + unsigned int PhysPortMax; + unsigned int PhysPortIndex; + SK_U8 Val8; + SK_BOOL PortActiveFlag; + + + *pBuf = 0; + PortActiveFlag = SK_FALSE; + PhysPortMax = pAC->GIni.GIMacsFound; + + for (PhysPortIndex = 0; PhysPortIndex < PhysPortMax; + PhysPortIndex ++) { + + /* Check if the physical port is active */ + if (!pAC->Pnmi.Port[PhysPortIndex].ActiveFlag) { + + continue; + } + + PortActiveFlag = SK_TRUE; + + switch (Id) { + + case OID_SKGE_LINK_CAP: + + /* + * Different capabilities should not happen, but + * in the case of the cases OR them all together. + * From a curious point of view the virtual port + * is capable of all found capabilities. + */ + *pBuf |= pAC->GIni.GP[PhysPortIndex].PLinkCap; + break; + + case OID_SKGE_LINK_MODE: + /* Check if it is the first active port */ + if (*pBuf == 0) { + + *pBuf = pAC->GIni.GP[PhysPortIndex]. + PLinkModeConf; + continue; + } + + /* + * If we find an active port with a different link + * mode than the first one we return a value that + * indicates that the link mode is indeterminated. + */ + if (*pBuf != pAC->GIni.GP[PhysPortIndex].PLinkModeConf + ) { + + *pBuf = SK_LMODE_INDETERMINATED; + } + break; + + case OID_SKGE_LINK_MODE_STATUS: + /* Get the link mode of the physical port */ + Val8 = CalculateLinkModeStatus(pAC, IoC, + PhysPortIndex); + + /* Check if it is the first active port */ + if (*pBuf == 0) { + + *pBuf = Val8; + continue; + } + + /* + * If we find an active port with a different link + * mode status than the first one we return a value + * that indicates that the link mode status is + * indeterminated. + */ + if (*pBuf != Val8) { + + *pBuf = SK_LMODE_STAT_INDETERMINATED; + } + break; + + case OID_SKGE_LINK_STATUS: + /* Get the link status of the physical port */ + Val8 = CalculateLinkStatus(pAC, IoC, PhysPortIndex); + + /* Check if it is the first active port */ + if (*pBuf == 0) { + + *pBuf = Val8; + continue; + } + + /* + * If we find an active port with a different link + * status than the first one, we return a value + * that indicates that the link status is + * indeterminated. + */ + if (*pBuf != Val8) { + + *pBuf = SK_PNMI_RLMT_LSTAT_INDETERMINATED; + } + break; + + case OID_SKGE_FLOWCTRL_CAP: + /* Check if it is the first active port */ + if (*pBuf == 0) { + + *pBuf = pAC->GIni.GP[PhysPortIndex]. + PFlowCtrlCap; + continue; + } + + /* + * From a curious point of view the virtual port + * is capable of all found capabilities. + */ + *pBuf |= pAC->GIni.GP[PhysPortIndex].PFlowCtrlCap; + break; + + case OID_SKGE_FLOWCTRL_MODE: + /* Check if it is the first active port */ + if (*pBuf == 0) { + + *pBuf = pAC->GIni.GP[PhysPortIndex]. + PFlowCtrlMode; + continue; + } + + /* + * If we find an active port with a different flow + * control mode than the first one, we return a value + * that indicates that the mode is indeterminated. + */ + if (*pBuf != pAC->GIni.GP[PhysPortIndex]. + PFlowCtrlMode) { + + *pBuf = SK_FLOW_MODE_INDETERMINATED; + } + break; + + case OID_SKGE_FLOWCTRL_STATUS: + /* Check if it is the first active port */ + if (*pBuf == 0) { + + *pBuf = pAC->GIni.GP[PhysPortIndex]. + PFlowCtrlStatus; + continue; + } + + /* + * If we find an active port with a different flow + * control status than the first one, we return a + * value that indicates that the status is + * indeterminated. + */ + if (*pBuf != pAC->GIni.GP[PhysPortIndex]. + PFlowCtrlStatus) { + + *pBuf = SK_FLOW_STAT_INDETERMINATED; + } + break; + case OID_SKGE_PHY_OPERATION_CAP: + /* Check if it is the first active port */ + if (*pBuf == 0) { + + *pBuf = pAC->GIni.GP[PhysPortIndex]. + PMSCap; + continue; + } + + /* + * From a curious point of view the virtual port + * is capable of all found capabilities. + */ + *pBuf |= pAC->GIni.GP[PhysPortIndex].PMSCap; + break; + + case OID_SKGE_PHY_OPERATION_MODE: + /* Check if it is the first active port */ + if (*pBuf == 0) { + + *pBuf = pAC->GIni.GP[PhysPortIndex]. + PMSMode; + continue; + } + + /* + * If we find an active port with a different master/ + * slave mode than the first one, we return a value + * that indicates that the mode is indeterminated. + */ + if (*pBuf != pAC->GIni.GP[PhysPortIndex]. + PMSMode) { + + *pBuf = SK_MS_MODE_INDETERMINATED; + } + break; + + case OID_SKGE_PHY_OPERATION_STATUS: + /* Check if it is the first active port */ + if (*pBuf == 0) { + + *pBuf = pAC->GIni.GP[PhysPortIndex]. + PMSStatus; + continue; + } + + /* + * If we find an active port with a different master/ + * slave status than the first one, we return a + * value that indicates that the status is + * indeterminated. + */ + if (*pBuf != pAC->GIni.GP[PhysPortIndex]. + PMSStatus) { + + *pBuf = SK_MS_STAT_INDETERMINATED; + } + break; + } + } + + /* + * If no port is active return an indeterminated answer + */ + if (!PortActiveFlag) { + + switch (Id) { + + case OID_SKGE_LINK_CAP: + *pBuf = SK_LMODE_CAP_INDETERMINATED; + break; + + case OID_SKGE_LINK_MODE: + *pBuf = SK_LMODE_INDETERMINATED; + break; + + case OID_SKGE_LINK_MODE_STATUS: + *pBuf = SK_LMODE_STAT_INDETERMINATED; + break; + + case OID_SKGE_LINK_STATUS: + *pBuf = SK_PNMI_RLMT_LSTAT_INDETERMINATED; + break; + + case OID_SKGE_FLOWCTRL_CAP: + case OID_SKGE_FLOWCTRL_MODE: + *pBuf = SK_FLOW_MODE_INDETERMINATED; + break; + + case OID_SKGE_FLOWCTRL_STATUS: + *pBuf = SK_FLOW_STAT_INDETERMINATED; + break; + + case OID_SKGE_PHY_OPERATION_CAP: + *pBuf = SK_MS_CAP_INDETERMINATED; + break; + + case OID_SKGE_PHY_OPERATION_MODE: + *pBuf = SK_MS_MODE_INDETERMINATED; + break; + + case OID_SKGE_PHY_OPERATION_STATUS: + *pBuf = SK_MS_STAT_INDETERMINATED; + break; + } + } +} + +/***************************************************************************** + * + * CalculateLinkStatus - Determins the link status of a physical port + * + * Description: + * Determins the link status the following way: + * LSTAT_PHY_DOWN: Link is down + * LSTAT_AUTONEG: Auto-negotiation failed + * LSTAT_LOG_DOWN: Link is up but RLMT did not yet put the port + * logically up. + * LSTAT_LOG_UP: RLMT marked the port as up + * + * Returns: + * Link status of physical port + */ + +static SK_U8 CalculateLinkStatus( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +unsigned int PhysPortIndex) /* Physical port index */ +{ + SK_U8 Result; + + + if (!pAC->GIni.GP[PhysPortIndex].PHWLinkUp) { + + Result = SK_PNMI_RLMT_LSTAT_PHY_DOWN; + } + else if (pAC->GIni.GP[PhysPortIndex].PAutoNegFail > 0) { + + Result = SK_PNMI_RLMT_LSTAT_AUTONEG; + } + else if (!pAC->Rlmt.Port[PhysPortIndex].PortDown) { + + Result = SK_PNMI_RLMT_LSTAT_LOG_UP; + } + else { + Result = SK_PNMI_RLMT_LSTAT_LOG_DOWN; + } + + return (Result); +} + +/***************************************************************************** + * + * CalculateLinkModeStatus - Determins the link mode status of a phys. port + * + * Description: + * The COMMON module only tells us if the mode is half or full duplex. + * But in the decade of auto sensing it is usefull for the user to + * know if the mode was negotiated or forced. Therefore we have a + * look to the mode, which was last used by the negotiation process. + * + * Returns: + * The link mode status + */ + +static SK_U8 CalculateLinkModeStatus( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +unsigned int PhysPortIndex) /* Physical port index */ +{ + SK_U8 Result; + + + /* Get the current mode, which can be full or half duplex */ + Result = pAC->GIni.GP[PhysPortIndex].PLinkModeStatus; + + /* Check if no valid mode could be found (link is down) */ + if (Result < SK_LMODE_STAT_HALF) { + + Result = SK_LMODE_STAT_UNKNOWN; + } + else if (pAC->GIni.GP[PhysPortIndex].PLinkMode >= SK_LMODE_AUTOHALF) { + + /* + * Auto-negotiation was used to bring up the link. Change + * the already found duplex status that it indicates + * auto-negotiation was involved. + */ + if (Result == SK_LMODE_STAT_HALF) { + + Result = SK_LMODE_STAT_AUTOHALF; + } + else if (Result == SK_LMODE_STAT_FULL) { + + Result = SK_LMODE_STAT_AUTOFULL; + } + } + + return (Result); +} + +/***************************************************************************** + * + * GetVpdKeyArr - Obtain an array of VPD keys + * + * Description: + * Read the VPD keys and build an array of VPD keys, which are + * easy to access. + * + * Returns: + * SK_PNMI_ERR_OK Task successfully performed. + * SK_PNMI_ERR_GENERAL Something went wrong. + */ + +static int GetVpdKeyArr( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +char *pKeyArr, /* Ptr KeyArray */ +unsigned int KeyArrLen, /* Length of array in bytes */ +unsigned int *pKeyNo) /* Number of keys */ +{ + unsigned int BufKeysLen = 128; + char BufKeys[128]; + unsigned int StartOffset; + unsigned int Offset; + int Index; + int Ret; + + + SK_MEMSET(pKeyArr, 0, KeyArrLen); + + /* + * Get VPD key list + */ + Ret = VpdKeys(pAC, IoC, (char *)&BufKeys, (int *)&BufKeysLen, + (int *)pKeyNo); + if (Ret > 0) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR014, + SK_PNMI_ERR014MSG); + + return (SK_PNMI_ERR_GENERAL); + } + /* If no keys are available return now */ + if (*pKeyNo == 0 || BufKeysLen == 0) { + + return (SK_PNMI_ERR_OK); + } + /* + * If the key list is too long for us trunc it and give a + * errorlog notification. This case should not happen because + * the maximum number of keys is limited due to RAM limitations + */ + if (*pKeyNo > SK_PNMI_VPD_ARR_SIZE) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR015, + SK_PNMI_ERR015MSG); + + *pKeyNo = SK_PNMI_VPD_ARR_SIZE; + } + + /* + * Now build an array of fixed string length size and copy + * the keys together. + */ + for (Index = 0, StartOffset = 0, Offset = 0; Offset < BufKeysLen; + Offset ++) { + + if (BufKeys[Offset] != 0) { + + continue; + } + + if (Offset - StartOffset > SK_PNMI_VPD_STR_SIZE) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR016, + SK_PNMI_ERR016MSG); + return (SK_PNMI_ERR_GENERAL); + } + + SK_STRNCPY(pKeyArr + Index * SK_PNMI_VPD_STR_SIZE, + &BufKeys[StartOffset], SK_PNMI_VPD_STR_SIZE); + + Index ++; + StartOffset = Offset + 1; + } + + /* Last key not zero terminated? Get it anyway */ + if (StartOffset < Offset) { + + SK_STRNCPY(pKeyArr + Index * SK_PNMI_VPD_STR_SIZE, + &BufKeys[StartOffset], SK_PNMI_VPD_STR_SIZE); + } + + return (SK_PNMI_ERR_OK); +} + +/***************************************************************************** + * + * SirqUpdate - Let the SIRQ update its internal values + * + * Description: + * Just to be sure that the SIRQ module holds its internal data + * structures up to date, we send an update event before we make + * any access. + * + * Returns: + * SK_PNMI_ERR_OK Task successfully performed. + * SK_PNMI_ERR_GENERAL Something went wrong. + */ + +static int SirqUpdate( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC) /* IO context handle */ +{ + SK_EVPARA EventParam; + + + /* Was the module already updated during the current PNMI call? */ + if (pAC->Pnmi.SirqUpdatedFlag > 0) { + + return (SK_PNMI_ERR_OK); + } + + /* Send an synchronuous update event to the module */ + SK_MEMSET((char *)&EventParam, 0, sizeof(EventParam)); + if (SkGeSirqEvent(pAC, IoC, SK_HWEV_UPDATE_STAT, EventParam) > 0) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR047, + SK_PNMI_ERR047MSG); + + return (SK_PNMI_ERR_GENERAL); + } + + return (SK_PNMI_ERR_OK); +} + +/***************************************************************************** + * + * RlmtUpdate - Let the RLMT update its internal values + * + * Description: + * Just to be sure that the RLMT module holds its internal data + * structures up to date, we send an update event before we make + * any access. + * + * Returns: + * SK_PNMI_ERR_OK Task successfully performed. + * SK_PNMI_ERR_GENERAL Something went wrong. + */ + +static int RlmtUpdate( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC) /* IO context handle */ +{ + SK_EVPARA EventParam; + + + /* Was the module already updated during the current PNMI call? */ + if (pAC->Pnmi.RlmtUpdatedFlag > 0) { + + return (SK_PNMI_ERR_OK); + } + + /* Send an synchronuous update event to the module */ + SK_MEMSET((char *)&EventParam, 0, sizeof(EventParam)); + if (SkRlmtEvent(pAC, IoC, SK_RLMT_STATS_UPDATE, EventParam) > 0) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SK_PNMI_ERR048, + SK_PNMI_ERR048MSG); + + return (SK_PNMI_ERR_GENERAL); + } + + return (SK_PNMI_ERR_OK); +} + +/***************************************************************************** + * + * MacUpdate - Force the XMAC to output the current statistic + * + * Description: + * The XMAC holds its statistic internally. To obtain the current + * values we must send a command so that the statistic data will + * be written to a predefined memory area on the adapter. + * + * Returns: + * SK_PNMI_ERR_OK Task successfully performed. + * SK_PNMI_ERR_GENERAL Something went wrong. + */ + +static int MacUpdate( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +unsigned int FirstMac, /* Index of the first Mac to be updated */ +unsigned int LastMac) /* Index of the last Mac to be updated */ +{ + unsigned int MacIndex; + SK_U16 StatReg; + unsigned int WaitIndex; + + + /* + * Were the statistics already updated during the + * current PNMI call? + */ + if (pAC->Pnmi.MacUpdatedFlag > 0) { + + return (SK_PNMI_ERR_OK); + } + + /* Send an update command to all XMACs specified */ + for (MacIndex = FirstMac; MacIndex <= LastMac; MacIndex ++) { + + StatReg = XM_SC_SNP_TXC | XM_SC_SNP_RXC; + XM_OUT16(IoC, MacIndex, XM_STAT_CMD, StatReg); + + /* + * It is an auto-clearing register. If the command bits + * went to zero again, the statistics are transfered. + * Normally the command should be executed immediately. + * But just to be sure we execute a loop. + */ + for (WaitIndex = 0; WaitIndex < 10; WaitIndex ++) { + + XM_IN16(IoC, MacIndex, XM_STAT_CMD, &StatReg); + if ((StatReg & (XM_SC_SNP_TXC | XM_SC_SNP_RXC)) == + 0) { + + break; + } + } + if (WaitIndex == 10 ) { + + SK_ERR_LOG(pAC, SK_ERRCL_HW, SK_PNMI_ERR050, + SK_PNMI_ERR050MSG); + + return (SK_PNMI_ERR_GENERAL); + } + } + + return (SK_PNMI_ERR_OK); +} + +/***************************************************************************** + * + * GetStatVal - Retrieve an XMAC statistic counter + * + * Description: + * Retrieves the statistic counter of a virtual or physical port. The + * virtual port is identified by the index 0. It consists of all + * currently active ports. To obtain the counter value for this port + * we must add the statistic counter of all active ports. To grant + * continuous counter values for the virtual port even when port + * switches occur we must additionally add a delta value, which was + * calculated during a SK_PNMI_EVT_RLMT_ACTIVE_UP event. + * + * Returns: + * Requested statistic value + */ + +static SK_U64 GetStatVal( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +unsigned int LogPortIndex, /* Index of the logical Port to be processed */ +unsigned int StatIndex) /* Index to statistic value */ +{ + unsigned int PhysPortIndex; + unsigned int PhysPortMax; + SK_U64 Val = 0; + + + if (LogPortIndex == 0) { + + PhysPortMax = pAC->GIni.GIMacsFound; + + /* Add counter of all active ports */ + for (PhysPortIndex = 0; PhysPortIndex < PhysPortMax; + PhysPortIndex ++) { + + if (pAC->Pnmi.Port[PhysPortIndex].ActiveFlag) { + + Val += GetPhysStatVal(pAC, IoC, PhysPortIndex, + StatIndex); + } + } + + /* Correct value because of port switches */ + Val += pAC->Pnmi.VirtualCounterOffset[StatIndex]; + } + else { + /* Get counter value of physical port */ + PhysPortIndex = SK_PNMI_PORT_LOG2PHYS(pAC, LogPortIndex); + Val = GetPhysStatVal(pAC, IoC, PhysPortIndex, StatIndex); + } + + return (Val); +} + +/***************************************************************************** + * + * GetPhysStatVal - Get counter value for physical port + * + * Description: + * Builds a 64bit counter value. Except for the octet counters + * the lower 32bit are counted in hardware and the upper 32bit + * in software by monitoring counter overflow interrupts in the + * event handler. To grant continous counter values during XMAC + * resets (caused by a workaround) we must add a delta value. + * The delta was calculated in the event handler when a + * SK_PNMI_EVT_XMAC_RESET was received. + * + * Returns: + * Counter value + */ + +static SK_U64 GetPhysStatVal( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC, /* IO context handle */ +unsigned int PhysPortIndex, /* Index of the logical Port to be processed */ +unsigned int StatIndex) /* Index to statistic value */ +{ + SK_U64 Val = 0; + SK_U32 LowVal; + SK_U32 HighVal; + + + switch (StatIndex) { + + case SK_PNMI_HTX_OCTET: + XM_IN32(IoC, PhysPortIndex, XM_TXO_OK_LO, &LowVal); + XM_IN32(IoC, PhysPortIndex, XM_TXO_OK_HI, &HighVal); + break; + + case SK_PNMI_HRX_OCTET: + XM_IN32(IoC, PhysPortIndex, XM_RXO_OK_LO, &LowVal); + XM_IN32(IoC, PhysPortIndex, XM_RXO_OK_HI, &HighVal); + break; + + case SK_PNMI_HTX_OCTETLOW: + case SK_PNMI_HRX_OCTETLOW: + return (Val); + + case SK_PNMI_HTX_SYNC: + LowVal = (SK_U32)pAC->Pnmi.Port[PhysPortIndex].StatSyncCts; + HighVal = (SK_U32) + (pAC->Pnmi.Port[PhysPortIndex].StatSyncCts >> 32); + break; + + case SK_PNMI_HTX_SYNC_OCTET: + LowVal = (SK_U32)pAC->Pnmi.Port[PhysPortIndex]. + StatSyncOctetsCts; + HighVal = (SK_U32) + (pAC->Pnmi.Port[PhysPortIndex].StatSyncOctetsCts >> + 32); + break; + + case SK_PNMI_HRX_FCS: + /* + * Broadcom filters fcs errors and counts it in + * Receive Error Counter register + */ + if (pAC->GIni.GP[PhysPortIndex].PhyType == SK_PHY_BCOM) { + /* do not read while not initialized (PHY_READ hangs!)*/ + if (pAC->GIni.GP[PhysPortIndex].PState) { + PHY_READ(IoC, &pAC->GIni.GP[PhysPortIndex], + PhysPortIndex, PHY_BCOM_RE_CTR, + &LowVal); + } + else { + LowVal = 0; + } + HighVal = pAC->Pnmi.Port[PhysPortIndex].CounterHigh[StatIndex]; + } + else { + XM_IN32(IoC, PhysPortIndex, + StatAddress[StatIndex].Param, &LowVal); + HighVal = pAC->Pnmi.Port[PhysPortIndex].CounterHigh[StatIndex]; + } + default: + XM_IN32(IoC, PhysPortIndex, StatAddress[StatIndex].Param, + &LowVal); + HighVal = pAC->Pnmi.Port[PhysPortIndex].CounterHigh[StatIndex]; + break; + } + + Val = (((SK_U64)HighVal << 32) | (SK_U64)LowVal); + + /* Correct value because of possible XMAC reset. XMAC Errata #2 */ + Val += pAC->Pnmi.Port[PhysPortIndex].CounterOffset[StatIndex]; + + return (Val); +} + +/***************************************************************************** + * + * ResetCounter - Set all counters and timestamps to zero + * + * Description: + * Notifies other common modules which store statistic data to + * reset their counters and finally reset our own counters. + * + * Returns: + * Nothing + */ + +static void ResetCounter( +SK_AC *pAC, /* Pointer to adapter context */ +SK_IOC IoC) /* IO context handle */ +{ + unsigned int PhysPortIndex; + SK_EVPARA EventParam; + + + SK_MEMSET((char *)&EventParam, 0, sizeof(EventParam)); + + /* Notify sensor module */ + SkEventQueue(pAC, SKGE_I2C, SK_I2CEV_CLEAR, EventParam); + + /* Notify RLMT module */ + SkEventQueue(pAC, SKGE_RLMT, SK_RLMT_STATS_CLEAR, EventParam); + + /* Notify SIRQ module */ + SkEventQueue(pAC, SKGE_HWAC, SK_HWEV_CLEAR_STAT, EventParam); + + /* Notify CSUM module */ +#ifdef SK_USE_CSUM + EventParam.Para64 = (SK_U64)(-1); + SkEventQueue(pAC, SKGE_CSUM, SK_CSUM_EVENT_CLEAR_PROTO_STATS, + EventParam); +#endif + + /* Clear XMAC statistic */ + for (PhysPortIndex = 0; PhysPortIndex < + (unsigned int)pAC->GIni.GIMacsFound; PhysPortIndex ++) { + + XM_OUT16(IoC, PhysPortIndex, XM_STAT_CMD, + XM_SC_CLR_RXC | XM_SC_CLR_TXC); + /* Clear two times according to Errata #3 */ + XM_OUT16(IoC, PhysPortIndex, XM_STAT_CMD, + XM_SC_CLR_RXC | XM_SC_CLR_TXC); + + SK_MEMSET((char *)&pAC->Pnmi.Port[PhysPortIndex].CounterHigh, + 0, sizeof(pAC->Pnmi.Port[PhysPortIndex].CounterHigh)); + SK_MEMSET((char *)&pAC->Pnmi.Port[PhysPortIndex]. + CounterOffset, 0, sizeof(pAC->Pnmi.Port[ + PhysPortIndex].CounterOffset)); + SK_MEMSET((char *)&pAC->Pnmi.Port[PhysPortIndex].StatSyncCts, + 0, sizeof(pAC->Pnmi.Port[PhysPortIndex].StatSyncCts)); + SK_MEMSET((char *)&pAC->Pnmi.Port[PhysPortIndex]. + StatSyncOctetsCts, 0, sizeof(pAC->Pnmi.Port[ + PhysPortIndex].StatSyncOctetsCts)); + } + + /* + * Clear local statistics + */ + pAC->Pnmi.RlmtChangeCts = 0; + pAC->Pnmi.RlmtChangeTime = 0; + SK_MEMSET((char *)&pAC->Pnmi.RlmtChangeEstimate.EstValue[0], 0, + sizeof(pAC->Pnmi.RlmtChangeEstimate.EstValue)); + pAC->Pnmi.RlmtChangeEstimate.EstValueIndex = 0; + pAC->Pnmi.RlmtChangeEstimate.Estimate = 0; + pAC->Pnmi.TxSwQueueMax = 0; + pAC->Pnmi.TxRetryCts = 0; + pAC->Pnmi.RxIntrCts = 0; + pAC->Pnmi.TxIntrCts = 0; + pAC->Pnmi.RxNoBufCts = 0; + pAC->Pnmi.TxNoBufCts = 0; + pAC->Pnmi.TxUsedDescrNo = 0; + pAC->Pnmi.RxDeliveredCts = 0; + pAC->Pnmi.RxOctetsDeliveredCts = 0; + pAC->Pnmi.ErrRecoveryCts = 0; +} + +/***************************************************************************** + * + * GetTrapEntry - Get an entry in the trap buffer + * + * Description: + * The trap buffer stores various events. A user application somehow + * gets notified that an event occured and retrieves the trap buffer + * contens (or simply polls the buffer). The buffer is organized as + * a ring which stores the newest traps at the beginning. The oldest + * traps are overwritten by the newest ones. Each trap entry has a + * unique number, so that applications may detect new trap entries. + * + * Returns: + * A pointer to the trap entry + */ + +static char* GetTrapEntry( +SK_AC *pAC, /* Pointer to adapter context */ +SK_U32 TrapId, /* SNMP ID of the trap */ +unsigned int Size) /* Space needed for trap entry */ +{ + unsigned int BufPad = pAC->Pnmi.TrapBufPad; + unsigned int BufFree = pAC->Pnmi.TrapBufFree; + unsigned int Beg = pAC->Pnmi.TrapQueueBeg; + unsigned int End = pAC->Pnmi.TrapQueueEnd; + char *pBuf = &pAC->Pnmi.TrapBuf[0]; + int Wrap; + unsigned int NeededSpace; + unsigned int EntrySize; + SK_U32 Val32; + SK_U64 Val64; + + + /* Last byte of entry will get a copy of the entry length */ + Size ++; + + /* + * Calculate needed buffer space */ + if (Beg >= Size) { + + NeededSpace = Size; + Wrap = FALSE; + } + else { + NeededSpace = Beg + Size; + Wrap = TRUE; + } + + /* + * Check if enough buffer space is provided. Otherwise + * free some entries. Leave one byte space between begin + * and end of buffer to make it possible to detect whether + * the buffer is full or empty + */ + while (BufFree < NeededSpace + 1) { + + if (End == 0) { + + End = SK_PNMI_TRAP_QUEUE_LEN; + } + + EntrySize = (unsigned int)*((unsigned char *)pBuf + End - 1); + BufFree += EntrySize; + End -= EntrySize; +#ifdef DEBUG + SK_MEMSET(pBuf + End, (char)(-1), EntrySize); +#endif + if (End == BufPad) { +#ifdef DEBUG + SK_MEMSET(pBuf, (char)(-1), End); +#endif + BufFree += End; + End = 0; + BufPad = 0; + } + } + + /* + * Insert new entry as first entry. Newest entries are + * stored at the beginning of the queue. + */ + if (Wrap) { + + BufPad = Beg; + Beg = SK_PNMI_TRAP_QUEUE_LEN - Size; + } + else { + Beg = Beg - Size; + } + BufFree -= NeededSpace; + + /* Save the current offsets */ + pAC->Pnmi.TrapQueueBeg = Beg; + pAC->Pnmi.TrapQueueEnd = End; + pAC->Pnmi.TrapBufPad = BufPad; + pAC->Pnmi.TrapBufFree = BufFree; + + /* Initialize the trap entry */ + *(pBuf + Beg + Size - 1) = (char)Size; + *(pBuf + Beg) = (char)Size; + Val32 = (pAC->Pnmi.TrapUnique) ++; + SK_PNMI_STORE_U32(pBuf + Beg + 1, Val32); + SK_PNMI_STORE_U32(pBuf + Beg + 1 + sizeof(SK_U32), TrapId); + Val64 = SK_PNMI_HUNDREDS_SEC(SkOsGetTime(pAC)); + SK_PNMI_STORE_U64(pBuf + Beg + 1 + 2 * sizeof(SK_U32), Val64); + + return (pBuf + Beg); +} + +/***************************************************************************** + * + * CopyTrapQueue - Copies the trap buffer for the TRAP OID + * + * Description: + * On a query of the TRAP OID the trap buffer contents will be + * copied continuously to the request buffer, which must be large + * enough. No length check is performed. + * + * Returns: + * Nothing + */ + +static void CopyTrapQueue( +SK_AC *pAC, /* Pointer to adapter context */ +char *pDstBuf) /* Buffer to which the queued traps will be copied */ +{ + unsigned int BufPad = pAC->Pnmi.TrapBufPad; + unsigned int Trap = pAC->Pnmi.TrapQueueBeg; + unsigned int End = pAC->Pnmi.TrapQueueEnd; + char *pBuf = &pAC->Pnmi.TrapBuf[0]; + unsigned int Len; + unsigned int DstOff = 0; + + + while (Trap != End) { + + Len = (unsigned int)*(pBuf + Trap); + + /* + * Last byte containing a copy of the length will + * not be copied. + */ + *(pDstBuf + DstOff) = (char)(Len - 1); + SK_MEMCPY(pDstBuf + DstOff + 1, pBuf + Trap + 1, Len - 2); + DstOff += Len - 1; + + Trap += Len; + if (Trap == SK_PNMI_TRAP_QUEUE_LEN) { + + Trap = BufPad; + } + } +} + +/***************************************************************************** + * + * GetTrapQueueLen - Get the length of the trap buffer + * + * Description: + * Evaluates the number of currently stored traps and the needed + * buffer size to retrieve them. + * + * Returns: + * Nothing + */ + +static void GetTrapQueueLen( +SK_AC *pAC, /* Pointer to adapter context */ +unsigned int *pLen, /* Length in Bytes of all queued traps */ +unsigned int *pEntries) /* Returns number of trapes stored in queue */ +{ + unsigned int BufPad = pAC->Pnmi.TrapBufPad; + unsigned int Trap = pAC->Pnmi.TrapQueueBeg; + unsigned int End = pAC->Pnmi.TrapQueueEnd; + char *pBuf = &pAC->Pnmi.TrapBuf[0]; + unsigned int Len; + unsigned int Entries = 0; + unsigned int TotalLen = 0; + + + while (Trap != End) { + + Len = (unsigned int)*(pBuf + Trap); + TotalLen += Len - 1; + Entries ++; + + Trap += Len; + if (Trap == SK_PNMI_TRAP_QUEUE_LEN) { + + Trap = BufPad; + } + } + + *pEntries = Entries; + *pLen = TotalLen; +} + +/***************************************************************************** + * + * QueueSimpleTrap - Store a simple trap to the trap buffer + * + * Description: + * A simple trap is a trap with now additional data. It consists + * simply of a trap code. + * + * Returns: + * Nothing + */ + +static void QueueSimpleTrap( +SK_AC *pAC, /* Pointer to adapter context */ +SK_U32 TrapId) /* Type of sensor trap */ +{ + GetTrapEntry(pAC, TrapId, SK_PNMI_TRAP_SIMPLE_LEN); +} + +/***************************************************************************** + * + * QueueSensorTrap - Stores a sensor trap in the trap buffer + * + * Description: + * Gets an entry in the trap buffer and fills it with sensor related + * data. + * + * Returns: + * Nothing + */ + +static void QueueSensorTrap( +SK_AC *pAC, /* Pointer to adapter context */ +SK_U32 TrapId, /* Type of sensor trap */ +unsigned int SensorIndex) /* Index of sensor which caused the trap */ +{ + char *pBuf; + unsigned int Offset; + unsigned int DescrLen; + SK_U32 Val32; + + + /* Get trap buffer entry */ + DescrLen = SK_STRLEN(pAC->I2c.SenTable[SensorIndex].SenDesc); + pBuf = GetTrapEntry(pAC, TrapId, + SK_PNMI_TRAP_SENSOR_LEN_BASE + DescrLen); + Offset = SK_PNMI_TRAP_SIMPLE_LEN; + + /* Store additionally sensor trap related data */ + Val32 = OID_SKGE_SENSOR_INDEX; + SK_PNMI_STORE_U32(pBuf + Offset, Val32); + *(pBuf + Offset + 4) = 4; + Val32 = (SK_U32)SensorIndex; + SK_PNMI_STORE_U32(pBuf + Offset + 5, Val32); + Offset += 9; + + Val32 = (SK_U32)OID_SKGE_SENSOR_DESCR; + SK_PNMI_STORE_U32(pBuf + Offset, Val32); + *(pBuf + Offset + 4) = (char)DescrLen; + SK_MEMCPY(pBuf + Offset + 5, pAC->I2c.SenTable[SensorIndex].SenDesc, + DescrLen); + Offset += DescrLen + 5; + + Val32 = OID_SKGE_SENSOR_TYPE; + SK_PNMI_STORE_U32(pBuf + Offset, Val32); + *(pBuf + Offset + 4) = 1; + *(pBuf + Offset + 5) = (char)pAC->I2c.SenTable[SensorIndex].SenType; + Offset += 6; + + Val32 = OID_SKGE_SENSOR_VALUE; + SK_PNMI_STORE_U32(pBuf + Offset, Val32); + *(pBuf + Offset + 4) = 4; + Val32 = (SK_U32)pAC->I2c.SenTable[SensorIndex].SenValue; + SK_PNMI_STORE_U32(pBuf + Offset + 5, Val32); +} + +/***************************************************************************** + * + * QueueRlmtNewMacTrap - Store a port switch trap in the trap buffer + * + * Description: + * Nothing further to explain. + * + * Returns: + * Nothing + */ + +static void QueueRlmtNewMacTrap( +SK_AC *pAC, /* Pointer to adapter context */ +unsigned int ActiveMac) /* Index (0..n) of the currently active port */ +{ + char *pBuf; + SK_U32 Val32; + + + pBuf = GetTrapEntry(pAC, OID_SKGE_TRAP_RLMT_CHANGE_PORT, + SK_PNMI_TRAP_RLMT_CHANGE_LEN); + + Val32 = OID_SKGE_RLMT_PORT_ACTIVE; + SK_PNMI_STORE_U32(pBuf + SK_PNMI_TRAP_SIMPLE_LEN, Val32); + *(pBuf + SK_PNMI_TRAP_SIMPLE_LEN + 4) = 1; + *(pBuf + SK_PNMI_TRAP_SIMPLE_LEN + 5) = (char)ActiveMac; +} + +/***************************************************************************** + * + * QueueRlmtPortTrap - Store port related RLMT trap to trap buffer + * + * Description: + * Nothing further to explain. + * + * Returns: + * Nothing + */ + +static void QueueRlmtPortTrap( +SK_AC *pAC, /* Pointer to adapter context */ +SK_U32 TrapId, /* Type of RLMT port trap */ +unsigned int PortIndex) /* Index of the port, which changed its state */ +{ + char *pBuf; + SK_U32 Val32; + + + pBuf = GetTrapEntry(pAC, TrapId, SK_PNMI_TRAP_RLMT_PORT_LEN); + + Val32 = OID_SKGE_RLMT_PORT_INDEX; + SK_PNMI_STORE_U32(pBuf + SK_PNMI_TRAP_SIMPLE_LEN, Val32); + *(pBuf + SK_PNMI_TRAP_SIMPLE_LEN + 4) = 1; + *(pBuf + SK_PNMI_TRAP_SIMPLE_LEN + 5) = (char)PortIndex; +} + +/***************************************************************************** + * + * CopyMac - Copies a MAC address + * + * Description: + * Nothing further to explain. + * + * Returns: + * Nothing + */ + +static void CopyMac( +char *pDst, /* Pointer to destination buffer */ +SK_MAC_ADDR *pMac) /* Pointer of Source */ +{ + int i; + + + for (i = 0; i < sizeof(SK_MAC_ADDR); i ++) { + + *(pDst + i) = pMac->a[i]; + } +} diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/skgesirq.c linux/drivers/net/sk98lin/skgesirq.c --- v2.2.13/linux/drivers/net/sk98lin/skgesirq.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/skgesirq.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,1921 @@ +/****************************************************************************** + * + * Name: skgesirq.c + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.46 $ + * Date: $Date: 1999/09/16 10:30:07 $ + * Purpose: Special IRQ module + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skge.c" for further information. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: skgesirq.c,v $ + * Revision 1.46 1999/09/16 10:30:07 cgoos + * Removed debugging output statement from Linux. + * + * Revision 1.45 1999/09/16 07:32:55 cgoos + * Fixed dual-port copperfield bug (PHY_READ from resetted port). + * Removed some unused variables. + * + * Revision 1.44 1999/08/03 15:25:04 cgoos + * Removed workaround for disabled interrupts in half duplex mode. + * + * Revision 1.43 1999/08/03 14:27:58 cgoos + * Removed SENSE mode code from SkGePortCheckUpBcom. + * + * Revision 1.42 1999/07/26 09:16:54 cgoos + * Added some typecasts to avoid compiler warnings. + * + * Revision 1.41 1999/05/19 07:28:59 cgoos + * Changes for 1000Base-T. + * + * Revision 1.40 1999/04/08 13:59:39 gklug + * fix: problem with 3Com switches endless RESTARTs + * + * Revision 1.39 1999/03/08 10:10:52 gklug + * fix: AutoSensing did switch to next mode even if LiPa indicated offline + * + * Revision 1.38 1999/03/08 09:49:03 gklug + * fix: Bug using pAC instead of IoC, causing AIX problems + * fix: change compare for Linux compiler bug workaround + * + * Revision 1.37 1999/01/28 14:51:33 gklug + * fix: monitor for autosensing and extra RESETS the RX on wire counters + * + * Revision 1.36 1999/01/22 09:19:55 gklug + * fix: Init DupMode and InitPauseMd are now called in RxTxEnable + * + * Revision 1.35 1998/12/11 15:22:59 gklug + * chg: autosensing: check for receive if manual mode was guessed + * chg: simplified workaround for XMAC errata + * chg: wait additional 100 ms before link goes up. + * chg: autoneg timeout to 600 ms + * chg: restart autoneg even if configured to autonegotiation + * + * Revision 1.34 1998/12/10 10:33:14 gklug + * add: more debug messages + * fix: do a new InitPhy if link went down (AutoSensing problem) + * chg: Check for zero shorts if link is NOT up + * chg: reset Port if link goes down + * chg: wait additional 100 ms when link comes up to check shorts + * fix: dummy read extended autoneg status to prevent link going down immediately + * + * Revision 1.33 1998/12/07 12:18:29 gklug + * add: refinement of autosense mode: take into account the autoneg cap of LiPa + * + * Revision 1.32 1998/12/07 07:11:21 gklug + * fix: compiler warning + * + * Revision 1.31 1998/12/02 09:29:05 gklug + * fix: WA XMAC Errata: FCSCt check was not correct. + * fix: WA XMAC Errata: Prec Counter were NOT updated in case of short checks. + * fix: Clear Stat : now clears the Prev counters of all known Ports + * + * Revision 1.30 1998/12/01 10:54:15 gklug + * dd: workaround for XMAC errata changed. Check RX count and CRC err Count, too. + * + * Revision 1.29 1998/12/01 10:01:53 gklug + * fix: if MAC IRQ occurs during port down, this will be handled correctly + * + * Revision 1.28 1998/11/26 16:22:11 gklug + * fix: bug in autosense if manual modes are used + * + * Revision 1.27 1998/11/26 15:50:06 gklug + * fix: PNMI needs to set PLinkModeConf + * + * Revision 1.26 1998/11/26 14:51:58 gklug + * add: AutoSensing functionalty + * + * Revision 1.25 1998/11/26 07:34:37 gklug + * fix: Init PrevShorts when restarting port due to Link connection + * + * Revision 1.24 1998/11/25 10:57:32 gklug + * fix: remove unreferenced local vars + * + * Revision 1.23 1998/11/25 08:26:40 gklug + * fix: don't do a RESET on a starting or stopping port + * + * Revision 1.22 1998/11/24 13:29:44 gklug + * add: Workaround for MAC parity errata + * + * Revision 1.21 1998/11/18 15:31:06 gklug + * fix: lint bugs + * + * Revision 1.20 1998/11/18 12:58:54 gklug + * fix: use PNMI query instead of hardware access + * + * Revision 1.19 1998/11/18 12:54:55 gklug + * chg: add new workaround for XMAC Errata + * add: short event counter monitoring on active link too + * + * Revision 1.18 1998/11/13 14:27:41 malthoff + * Bug Fix: Packet Arbiter Timeout was not cleared correctly + * for timeout on TX1 and TX2. + * + * Revision 1.17 1998/11/04 07:01:59 cgoos + * Moved HW link poll sequence. + * Added call to SkXmRxTxEnable. + * + * Revision 1.16 1998/11/03 13:46:03 gklug + * add: functionality of SET_LMODE and SET_FLOW_MODE + * fix: send RLMT LinkDown event when Port stop is given with LinkUp + * + * Revision 1.15 1998/11/03 12:56:47 gklug + * fix: Needs more events + * + * Revision 1.14 1998/10/30 07:36:35 gklug + * rmv: unnecessary code + * + * Revision 1.13 1998/10/29 15:21:57 gklug + * add: Poll link feature for activating HW link + * fix: Deactivate HWLink when Port STOP is given + * + * Revision 1.12 1998/10/28 07:38:57 cgoos + * Checking link status at begin of SkHWLinkUp. + * + * Revision 1.11 1998/10/22 09:46:50 gklug + * fix SysKonnectFileId typo + * + * Revision 1.10 1998/10/14 13:57:47 gklug + * add: Port start/stop event + * + * Revision 1.9 1998/10/14 05:48:29 cgoos + * Added definition for Para. + * + * Revision 1.8 1998/10/14 05:40:09 gklug + * add: Hardware Linkup signal used + * + * Revision 1.7 1998/10/09 06:50:20 malthoff + * Remove ID_sccs by SysKonnectFileId. + * + * Revision 1.6 1998/10/08 09:11:49 gklug + * add: clear IRQ commands + * + * Revision 1.5 1998/10/02 14:27:35 cgoos + * Fixed some typos and wrong event names. + * + * Revision 1.4 1998/10/02 06:24:17 gklug + * add: HW error function + * fix: OUT macros + * + * Revision 1.3 1998/10/01 07:03:00 gklug + * add: ISR for the usual interrupt source register + * + * Revision 1.2 1998/09/03 13:50:33 gklug + * add: function prototypes + * + * Revision 1.1 1998/08/27 11:50:21 gklug + * initial revision + * + * + * + ******************************************************************************/ + + +/* + Special Interrupt handler + + The following abstract should show how this module is included + in the driver path: + + In the ISR of the driver the bits for frame transmission complete and + for receive complete are checked and handled by the driver itself. + The bits of the slow path mask are checked after this and then the + entry into the so-called "slow path" is prepared. It is an implemetors + decision whether this is executed directly or just scheduled by + disabling the mask. In the interrupt service routine events may be + generated, so it would be a good idea to call the EventDispatcher + right after this ISR. + + The Interrupt service register of the adapter is NOT read by this + module. SO if the drivers implemetor needs a while loop around the + slow data paths Interrupt bits, he needs to call the SkGeIsr() for + each loop entered. + + However, the XMAC Interrupt status registers are read in a while loop. + +*/ +static const char SysKonnectFileId[] = + "$Id: skgesirq.c,v 1.46 1999/09/16 10:30:07 cgoos Exp $" ; + +#include "h/skdrv1st.h" /* Driver Specific Definitions */ +#include "h/skgepnmi.h" /* PNMI Definitions */ +#include "h/skrlmt.h" /* RLMT Definitions */ +#include "h/skdrv2nd.h" /* Adapter Control- and Driver specific Def. */ + + +/* local function prototypes */ +static int SkGePortCheckUpXmac(SK_AC*, SK_IOC, int); +static int SkGePortCheckUpBcom(SK_AC*, SK_IOC, int); +static int SkGePortCheckUpLone(SK_AC*, SK_IOC, int); +static int SkGePortCheckUpNat(SK_AC*, SK_IOC, int); +static void SkPhyIsrBcom(SK_AC*, SK_IOC, int, SK_U16); +static void SkPhyIsrLone(SK_AC*, SK_IOC, int, SK_U16); + + +#ifdef __C2MAN__ +/* + Special IRQ function + + General Description: + + */ +intro() +{} +#endif + +/* + * Define return codes of SkGePortCheckUp and CheckShort + */ +#define SK_HW_PS_NONE 0 /* No action needed */ +#define SK_HW_PS_RESTART 1 /* Restart needed */ +#define SK_HW_PS_LINK 2 /* Link Up actions needed */ + +/****************************************************************************** + * + * SkHWInitDefSense() - Default Autosensing mode initialization + * + * Description: + * This function handles the Hardware link down signal + * + * Note: + * + */ +void SkHWInitDefSense( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port) /* Port Index (MAC_1 + n) */ +{ + SK_GEPORT *pPrt; + + pPrt = &pAC->GIni.GP[Port] ; + + pPrt->PAutoNegTimeOut = 0; + + if (pPrt->PLinkModeConf != SK_LMODE_AUTOSENSE) { + pPrt->PLinkMode = pPrt->PLinkModeConf; + return; + } + + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_IRQ, + ("AutoSensing: First mode %d on Port %d\n", + (int) SK_LMODE_AUTOFULL, + Port)); + + pPrt->PLinkMode = SK_LMODE_AUTOFULL; + + return; +} + +/****************************************************************************** + * + * SkHWSenseGetNext() - GetNextAutosensing Mode + * + * Description: + * This function handles the AutoSensing + * + * Note: + * + */ +SK_U8 SkHWSenseGetNext( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port) /* Port Index (MAC_1 + n) */ +{ + SK_GEPORT *pPrt; + + pPrt = &pAC->GIni.GP[Port] ; + + pPrt->PAutoNegTimeOut = 0; + + if (pPrt->PLinkModeConf != SK_LMODE_AUTOSENSE) { + /* Leave all as configured */ + return(pPrt->PLinkModeConf); + } + + if (pPrt->PLinkMode == SK_LMODE_AUTOFULL) { + /* Return next mode AUTOBOTH */ + return(SK_LMODE_AUTOBOTH); + } + + /* Return default autofull */ + return(SK_LMODE_AUTOFULL); +} + +/****************************************************************************** + * + * SkHWSenseSetNext() - Autosensing Set next mode + * + * Description: + * This function sets the appropriate next mode. + * + * Note: + * + */ +void SkHWSenseSetNext( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port, /* Port Index (MAC_1 + n) */ +SK_U8 NewMode) /* New Mode to be written in sense mode */ +{ + SK_GEPORT *pPrt; + + pPrt = &pAC->GIni.GP[Port] ; + + pPrt->PAutoNegTimeOut = 0; + + if (pPrt->PLinkModeConf != SK_LMODE_AUTOSENSE) { + return; + } + + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_IRQ, + ("AutoSensing: next mode %d on Port %d\n", (int) NewMode, + Port)); + pPrt->PLinkMode = NewMode; + + return; +} + +/****************************************************************************** + * + * SkHWLinkDown() - Link Down handling + * + * Description: + * This function handles the Hardware link down signal + * + * Note: + * + */ +void SkHWLinkDown( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port) /* Port Index (MAC_1 + n) */ +{ + SK_GEPORT *pPrt; + SK_U16 Word; + + pPrt = &pAC->GIni.GP[Port] ; + + /* Disable all XMAC interrupts */ + XM_OUT16(IoC, Port, XM_IMSK, 0xffff); + + /* Disable Receive and Transmitter */ + XM_IN16(IoC, Port, XM_MMU_CMD, &Word); + XM_OUT16(IoC, Port, XM_MMU_CMD, Word & ~(XM_MMU_ENA_RX|XM_MMU_ENA_TX)); + + /* disable all PHY interrupts */ + switch (pAC->GIni.GP[Port].PhyType) { + case SK_PHY_BCOM: + PHY_WRITE(IoC, pPrt, Port, PHY_BCOM_INT_MASK, + 0xffff); + break; + case SK_PHY_LONE: + PHY_WRITE(IoC, pPrt, Port, PHY_LONE_INT_ENAB, + 0x0); + break; + case SK_PHY_NAT: + /* todo: National + PHY_WRITE(IoC, pPrt, Port, PHY_NAT_INT_MASK, + 0xffff); */ + break; + } + + /* Init default sense mode */ + SkHWInitDefSense(pAC, IoC, Port); + + if (!pPrt->PHWLinkUp) { + return; + } + + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_IRQ, + ("Link down Port %d\n", Port)); + + /* Set Link to DOWN */ + pPrt->PHWLinkUp = SK_FALSE; + + /* Reset Port stati */ + pPrt->PLinkModeStatus = SK_LMODE_STAT_UNKNOWN; + pPrt->PFlowCtrlStatus = SK_FLOW_STAT_NONE ; + + /* + * Reinit Phy especially when the AutoSense default is set now + */ + SkXmInitPhy(pAC, IoC, Port, SK_FALSE); + + /* + * GP0: used for workaround of Rev. C + * Errata 2 + */ + + /* Do NOT signal to RLMT */ + + /* Do NOT start the timer here */ +} + +/****************************************************************************** + * + * SkHWLinkUp() - Link Up handling + * + * Description: + * This function handles the Hardware link up signal + * + * Note: + * + */ +void SkHWLinkUp( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port) /* Port Index (MAC_1 + n) */ +{ + SK_GEPORT *pPrt; + + pPrt = &pAC->GIni.GP[Port] ; + + if (pPrt->PHWLinkUp) { + /* We do NOT need to proceed on active link */ + return; + } + + pPrt->PHWLinkUp = SK_TRUE ; + pPrt->PAutoNegFail = SK_FALSE ; + pPrt->PLinkModeStatus = SK_LMODE_STAT_UNKNOWN; + + if (pPrt->PLinkMode != SK_LMODE_AUTOHALF && + pPrt->PLinkMode != SK_LMODE_AUTOFULL && + pPrt->PLinkMode != SK_LMODE_AUTOBOTH) { + /* Link is up and no Autonegotiation should be done */ + + /* Configure Port */ + + /* Set Link Mode */ + if (pPrt->PLinkMode == SK_LMODE_FULL) { + pPrt->PLinkModeStatus = SK_LMODE_STAT_FULL; + } else { + pPrt->PLinkModeStatus = SK_LMODE_STAT_HALF; + } + + /* No flow control without autonegotiation */ + pPrt->PFlowCtrlStatus = SK_FLOW_STAT_NONE ; + + /* RX/TX enable */ + SkXmRxTxEnable(pAC, IoC, Port); + } +} + +/****************************************************************************** + * + * SkMacParity - does everything to handle MAC parity errors correctly + * + */ +static void SkMacParity( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port) /* Port Index of the port failed */ +{ + SK_EVPARA Para; + SK_GEPORT *pPrt; /* GIni Port struct pointer */ + SK_U64 TxMax; /* TxMax Counter */ + unsigned int Len; + + pPrt = &pAC->GIni.GP[Port]; + + /* Clear IRQ */ + SK_OUT16(IoC, MR_ADDR(Port,TX_MFF_CTRL1), MFF_CLR_PERR) ; + + if (pPrt->PCheckPar) { + if (Port == MAC_1) { + SK_ERR_LOG(pAC, SK_ERRCL_HW , SKERR_SIRQ_E016, + SKERR_SIRQ_E016MSG) ; + } else { + SK_ERR_LOG(pAC, SK_ERRCL_HW , SKERR_SIRQ_E017, + SKERR_SIRQ_E017MSG) ; + } + Para.Para64 = Port; + SkEventQueue(pAC, SKGE_DRV, SK_DRV_PORT_FAIL, Para); + Para.Para32[0] = Port; + SkEventQueue(pAC, SKGE_RLMT, SK_RLMT_LINK_DOWN, Para); + + return; + } + + + /* Check whether frames with a size of 1k were sent */ + Len = sizeof(SK_U64); + SkPnmiGetVar(pAC, IoC, OID_SKGE_STAT_TX_MAX, (char *) &TxMax, + &Len, (SK_U32) SK_PNMI_PORT_PHYS2INST(Port)); + + if (TxMax > 0) { + /* From now on check the parity */ + pPrt->PCheckPar = SK_TRUE; + } +} + +/****************************************************************************** + * + * Hardware Error service routine + * + * Description: + * + * Notes: + */ +static void SkGeHwErr( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +SK_U32 HwStatus) /* Interrupt status word */ +{ + SK_EVPARA Para; + + if (HwStatus & IS_IRQ_STAT) { + SK_ERR_LOG(pAC, SK_ERRCL_HW , SKERR_SIRQ_E013, + SKERR_SIRQ_E013MSG) ; + Para.Para64 = 0; + SkEventQueue(pAC, SKGE_DRV, SK_DRV_ADAP_FAIL, Para); + } + + if (HwStatus & IS_IRQ_MST_ERR) { + SK_ERR_LOG(pAC, SK_ERRCL_HW , SKERR_SIRQ_E012, + SKERR_SIRQ_E012MSG) ; + Para.Para64 = 0; + SkEventQueue(pAC, SKGE_DRV, SK_DRV_ADAP_FAIL, Para); + } + + if (HwStatus & IS_NO_STAT_M1) { + /* Ignore it */ + /* This situation is also indicated in the descriptor */ + SK_OUT16(IoC, MR_ADDR(MAC_1,RX_MFF_CTRL1), MFF_CLR_INSTAT) ; + } + + if (HwStatus & IS_NO_STAT_M2) { + /* Ignore it */ + /* This situation is also indicated in the descriptor */ + SK_OUT16(IoC, MR_ADDR(MAC_2,RX_MFF_CTRL1), MFF_CLR_INSTAT) ; + } + + if (HwStatus & IS_NO_TIST_M1) { + /* Ignore it */ + /* This situation is also indicated in the descriptor */ + SK_OUT16(IoC, MR_ADDR(MAC_1,RX_MFF_CTRL1), MFF_CLR_INTIST) ; + } + + if (HwStatus & IS_NO_TIST_M2) { + /* Ignore it */ + /* This situation is also indicated in the descriptor */ + SK_OUT16(IoC, MR_ADDR(MAC_2,RX_MFF_CTRL1), MFF_CLR_INTIST) ; + } + + if (HwStatus & IS_RAM_RD_PAR) { + SK_OUT16(IoC, B3_RI_CTRL, RI_CLR_RD_PERR) ; + SK_ERR_LOG(pAC, SK_ERRCL_HW , SKERR_SIRQ_E014, + SKERR_SIRQ_E014MSG) ; + Para.Para64 = 0; + SkEventQueue(pAC, SKGE_DRV, SK_DRV_ADAP_FAIL, Para); + } + + if (HwStatus & IS_RAM_WR_PAR) { + SK_OUT16(IoC, B3_RI_CTRL, RI_CLR_WR_PERR) ; + SK_ERR_LOG(pAC, SK_ERRCL_HW , SKERR_SIRQ_E015, + SKERR_SIRQ_E015MSG) ; + Para.Para64 = 0; + SkEventQueue(pAC, SKGE_DRV, SK_DRV_ADAP_FAIL, Para); + } + + if (HwStatus & IS_M1_PAR_ERR) { + SkMacParity(pAC, IoC, MAC_1) ; + } + + if (HwStatus & IS_M2_PAR_ERR) { + SkMacParity(pAC, IoC, MAC_2) ; + } + + if (HwStatus & IS_R1_PAR_ERR) { + /* Clear IRQ */ + SK_OUT32(IoC, B0_R1_CSR, CSR_IRQ_CL_P) ; + + SK_ERR_LOG(pAC, SK_ERRCL_HW , SKERR_SIRQ_E018, + SKERR_SIRQ_E018MSG) ; + Para.Para64 = MAC_1; + SkEventQueue(pAC, SKGE_DRV, SK_DRV_PORT_FAIL, Para); + Para.Para32[0] = MAC_1; + SkEventQueue(pAC, SKGE_RLMT, SK_RLMT_LINK_DOWN, Para); + } + + if (HwStatus & IS_R2_PAR_ERR) { + /* Clear IRQ */ + SK_OUT32(IoC, B0_R2_CSR, CSR_IRQ_CL_P) ; + + SK_ERR_LOG(pAC, SK_ERRCL_HW , SKERR_SIRQ_E019, + SKERR_SIRQ_E019MSG) ; + Para.Para64 = MAC_2; + SkEventQueue(pAC, SKGE_DRV, SK_DRV_PORT_FAIL, Para); + Para.Para32[0] = MAC_2; + SkEventQueue(pAC, SKGE_RLMT, SK_RLMT_LINK_DOWN, Para); + } + +} + +/****************************************************************************** + * + * Interrupt service routine + * + * Description: + * + * Notes: + */ +void SkGeSirqIsr( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +SK_U32 Istatus) /* Interrupt status word */ +{ + SK_U32 RegVal32; /* Read register Value */ + SK_EVPARA Para; + SK_U16 XmIsr; + + if (Istatus & IS_HW_ERR) { + SK_IN32(IoC, B0_HWE_ISRC, &RegVal32) ; + SkGeHwErr(pAC, IoC, RegVal32) ; + } + + /* + * Packet Timeout interrupts + */ + /* Check whether XMACs are correctly initialized */ + if ((Istatus & (IS_PA_TO_RX1 | IS_PA_TO_TX1)) && + !pAC->GIni.GP[MAC_1].PState) { + /* XMAC was not initialized but Packet timeout occured */ + SK_ERR_LOG(pAC, SK_ERRCL_SW | SK_ERRCL_INIT, SKERR_SIRQ_E004, + SKERR_SIRQ_E004MSG) ; + } + + if ((Istatus & (IS_PA_TO_RX2 | IS_PA_TO_TX2)) && + !pAC->GIni.GP[MAC_2].PState) { + /* XMAC was not initialized but Packet timeout occured */ + SK_ERR_LOG(pAC, SK_ERRCL_SW | SK_ERRCL_INIT, SKERR_SIRQ_E005, + SKERR_SIRQ_E005MSG) ; + } + + if (Istatus & IS_PA_TO_RX1) { + /* Means network is filling us up */ + SK_ERR_LOG(pAC, SK_ERRCL_HW | SK_ERRCL_INIT, SKERR_SIRQ_E002, + SKERR_SIRQ_E002MSG) ; + SK_OUT16(IoC, B3_PA_CTRL, PA_CLR_TO_RX1) ; + } + + if (Istatus & IS_PA_TO_RX2) { + /* Means network is filling us up */ + SK_ERR_LOG(pAC, SK_ERRCL_HW | SK_ERRCL_INIT, SKERR_SIRQ_E003, + SKERR_SIRQ_E003MSG) ; + SK_OUT16(IoC, B3_PA_CTRL, PA_CLR_TO_RX2) ; + } + + if (Istatus & IS_PA_TO_TX1) { + /* May be a normal situation in a server with a slow network */ + SK_OUT16(IoC, B3_PA_CTRL, PA_CLR_TO_TX1) ; + } + + if (Istatus & IS_PA_TO_TX2) { + /* May be a normal situation in a server with a slow network */ + SK_OUT16(IoC, B3_PA_CTRL, PA_CLR_TO_TX2) ; + } + + /* + * Check interrupts of the particular queues. + */ + if (Istatus & IS_R1_C) { + /* Clear IRQ */ + SK_OUT32(IoC, B0_R1_CSR, CSR_IRQ_CL_C) ; + SK_ERR_LOG(pAC, SK_ERRCL_SW | SK_ERRCL_INIT, SKERR_SIRQ_E006, + SKERR_SIRQ_E006MSG) ; + Para.Para64 = MAC_1; + SkEventQueue(pAC, SKGE_DRV, SK_DRV_PORT_FAIL, Para); + Para.Para32[0] = MAC_1; + SkEventQueue(pAC, SKGE_RLMT, SK_RLMT_LINK_DOWN, Para); + } + + if (Istatus & IS_R2_C) { + /* Clear IRQ */ + SK_OUT32(IoC, B0_R2_CSR, CSR_IRQ_CL_C) ; + SK_ERR_LOG(pAC, SK_ERRCL_SW | SK_ERRCL_INIT, SKERR_SIRQ_E007, + SKERR_SIRQ_E007MSG) ; + Para.Para64 = MAC_2; + SkEventQueue(pAC, SKGE_DRV, SK_DRV_PORT_FAIL, Para); + Para.Para32[0] = MAC_2; + SkEventQueue(pAC, SKGE_RLMT, SK_RLMT_LINK_DOWN, Para); + } + + if (Istatus & IS_XS1_C) { + /* Clear IRQ */ + SK_OUT32(IoC, B0_XS1_CSR, CSR_IRQ_CL_C) ; + SK_ERR_LOG(pAC, SK_ERRCL_SW | SK_ERRCL_INIT, SKERR_SIRQ_E008, + SKERR_SIRQ_E008MSG) ; + Para.Para64 = MAC_1; + SkEventQueue(pAC, SKGE_DRV, SK_DRV_PORT_FAIL, Para); + Para.Para32[0] = MAC_1; + SkEventQueue(pAC, SKGE_RLMT, SK_RLMT_LINK_DOWN, Para); + } + + if (Istatus & IS_XA1_C) { + /* Clear IRQ */ + SK_OUT32(IoC, B0_XA1_CSR, CSR_IRQ_CL_C) ; + SK_ERR_LOG(pAC, SK_ERRCL_SW | SK_ERRCL_INIT, SKERR_SIRQ_E009, + SKERR_SIRQ_E009MSG) ; + Para.Para64 = MAC_1; + SkEventQueue(pAC, SKGE_DRV, SK_DRV_PORT_FAIL, Para); + Para.Para32[0] = MAC_1; + SkEventQueue(pAC, SKGE_RLMT, SK_RLMT_LINK_DOWN, Para); + } + + if (Istatus & IS_XS2_C) { + /* Clear IRQ */ + SK_OUT32(IoC, B0_XS2_CSR, CSR_IRQ_CL_C) ; + SK_ERR_LOG(pAC, SK_ERRCL_SW | SK_ERRCL_INIT, SKERR_SIRQ_E010, + SKERR_SIRQ_E010MSG) ; + Para.Para64 = MAC_2; + SkEventQueue(pAC, SKGE_DRV, SK_DRV_PORT_FAIL, Para); + Para.Para32[0] = MAC_2; + SkEventQueue(pAC, SKGE_RLMT, SK_RLMT_LINK_DOWN, Para); + } + + if (Istatus & IS_XA2_C) { + /* Clear IRQ */ + SK_OUT32(IoC, B0_XA2_CSR, CSR_IRQ_CL_C) ; + SK_ERR_LOG(pAC, SK_ERRCL_SW | SK_ERRCL_INIT, SKERR_SIRQ_E011, + SKERR_SIRQ_E011MSG) ; + Para.Para64 = MAC_2; + SkEventQueue(pAC, SKGE_DRV, SK_DRV_PORT_FAIL, Para); + Para.Para32[0] = MAC_2; + SkEventQueue(pAC, SKGE_RLMT, SK_RLMT_LINK_DOWN, Para); + } + + /* + * I2C Ready interrupt + */ + if (Istatus & IS_I2C_READY) { + SK_U16 PhyInt; + SK_U16 PhyIMsk; + SK_BOOL IsPhyInt = SK_FALSE; + int i; + /* test IRQs from PHY */ + for (i=0; iGIni.GIMacsFound; i++) { + switch (pAC->GIni.GP[i].PhyType) { + case SK_PHY_XMAC: + break; + case SK_PHY_BCOM: + if(pAC->GIni.GP[i].PState) { + PHY_READ(IoC, &pAC->GIni.GP[i], i, + PHY_BCOM_INT_STAT, &PhyInt); + PHY_READ(IoC, &pAC->GIni.GP[i], i, + PHY_BCOM_INT_MASK, &PhyIMsk); + + if (PhyInt & (~PhyIMsk)) { + SK_DBG_MSG(pAC,SK_DBGMOD_HWM, + SK_DBGCAT_IRQ, + ("Port %d Bcom Int: %x " + " Mask: %x\n", + i, PhyInt, PhyIMsk)); + SkPhyIsrBcom(pAC, IoC, i, + (SK_U16) + (PhyInt & (~PhyIMsk))); + IsPhyInt = SK_TRUE; + } + } + else { + } + break; + case SK_PHY_LONE: + PHY_READ(IoC, &pAC->GIni.GP[i], i, + PHY_LONE_INT_STAT, &PhyInt); + PHY_READ(IoC, &pAC->GIni.GP[i], i, + PHY_LONE_INT_ENAB, &PhyIMsk); + + if (PhyInt & PhyIMsk) { + SK_DBG_MSG(pAC,SK_DBGMOD_HWM, + SK_DBGCAT_IRQ, + ("Port %d Lone Int: %x " + " Mask: %x\n", + i, PhyInt, PhyIMsk)); + SkPhyIsrLone(pAC, IoC, i, + (SK_U16) (PhyInt & PhyIMsk)); + IsPhyInt = SK_TRUE; + } + break; + case SK_PHY_NAT: + /* todo: National */ + break; + } + } + if (!IsPhyInt) { + SkI2cIsr(pAC, IoC); + } + } + + if (Istatus & IS_LNK_SYNC_M1) { + /* + * We do NOT need the Link Sync interrupt, because it shows + * us only a link going down. + */ + /* clear interrupt */ + SK_OUT8(IoC, MR_ADDR(MAC_1,LNK_SYNC_CTRL), LED_CLR_IRQ); + } + + /* Check MAC after link sync counter */ + if (Istatus & IS_MAC1) { + XM_IN16(IoC, MAC_1, XM_ISRC, &XmIsr) ; + SkXmIrq(pAC, IoC, MAC_1, XmIsr); + } + + if (Istatus & IS_LNK_SYNC_M2) { + /* + * We do NOT need the Link Sync interrupt, because it shows + * us only a link going down. + */ + /* clear interrupt */ + SK_OUT8(IoC, MR_ADDR(MAC_2,LNK_SYNC_CTRL), LED_CLR_IRQ); + } + + /* Check MAC after link sync counter */ + if (Istatus & IS_MAC2) { + XM_IN16(IoC, MAC_2, XM_ISRC, &XmIsr) ; + SkXmIrq(pAC, IoC, MAC_2, XmIsr); + } + + /* + * Timer interrupt + * To be served last + */ + if (Istatus & IS_TIMINT) { + SkHwtIsr(pAC, IoC); + } +} + +/* + * Define an array of RX counter which are checked + * in AutoSense mode to check whether a link is not able to autonegotiate. + */ +static const SK_U32 SkGeRxOids[]= { + OID_SKGE_STAT_RX_64, + OID_SKGE_STAT_RX_127, + OID_SKGE_STAT_RX_255, + OID_SKGE_STAT_RX_511, + OID_SKGE_STAT_RX_1023, + OID_SKGE_STAT_RX_MAX, +} ; +/****************************************************************************** + * + * SkGePortCheckShorts - Implementing of the Workaround Errata # 2 + * + * return: + * 0 o.k. nothing needed + * 1 Restart needed on this port + */ +int SkGePortCheckShorts( +SK_AC *pAC, /* Adapters context */ +SK_IOC IoC, /* IO Context */ +int Port) /* Which port should be checked */ +{ + SK_U64 Shorts; /* Short Event Counter */ + SK_U64 CheckShorts; /* Check value for Short Event Counter */ + SK_U64 RxCts; /* RX Counter (packets on network) */ + SK_U64 RxTmp; /* RX temp. Counter */ + SK_U64 FcsErrCts; /* FCS Error Counter */ + SK_GEPORT *pPrt; /* GIni Port struct pointer */ + unsigned int Len; + int Rtv; /* Return value */ + int i; + + pPrt = &pAC->GIni.GP[Port]; + + /* Default: no action */ + Rtv = SK_HW_PS_NONE; + + /* + * Extra precaution: check for short Event counter + */ + Len = sizeof(SK_U64); + SkPnmiGetVar(pAC, IoC, OID_SKGE_STAT_RX_SHORTS, (char *) &Shorts, + &Len, (SK_U32) SK_PNMI_PORT_PHYS2INST(Port)); + + /* + * Read RX counter (packets seen on the network and not neccesarily + * really received. + */ + Len = sizeof(SK_U64); + RxCts = 0; + + for (i = 0; i < sizeof(SkGeRxOids)/sizeof(SK_U32) ; i++) { + SkPnmiGetVar(pAC, IoC, SkGeRxOids[i], (char *) &RxTmp, + &Len, (SK_U32) SK_PNMI_PORT_PHYS2INST(Port)); + RxCts += RxTmp; + } + + /* On default: check shorts against zero */ + CheckShorts = 0; + + /* + * Extra extra precaution on active links: + */ + if (pPrt->PHWLinkUp) { + /* + * Reset Link Restart counter + */ + pPrt->PLinkResCt = 0; + + /* If link is up check for 2 */ + CheckShorts = 2; + + Len = sizeof(SK_U64); + SkPnmiGetVar(pAC, IoC, OID_SKGE_STAT_RX_FCS, + (char *) &FcsErrCts, &Len, + (SK_U32) SK_PNMI_PORT_PHYS2INST(Port)); + + if (pPrt->PLinkModeConf == SK_LMODE_AUTOSENSE && + pPrt->PLipaAutoNeg == SK_LIPA_UNKNOWN && + (pPrt->PLinkMode == SK_LMODE_HALF || + pPrt->PLinkMode == SK_LMODE_FULL)) { + /* + * This is autosensing and we are in the fallback + * manual full/half duplex mode. + */ + if (RxCts == pPrt->PPrevRx) { + /* + * Nothing received + * restart link + */ + pPrt->PPrevFcs = FcsErrCts; + pPrt->PPrevShorts = Shorts; + return(SK_HW_PS_RESTART); + } else { + pPrt->PLipaAutoNeg = SK_LIPA_MANUAL; + } + } + + if (((RxCts - pPrt->PPrevRx) > pPrt->PRxLim) || + (!(FcsErrCts - pPrt->PPrevFcs))) { + /* + * Note: The compare with zero above has to be done + * the way shown, otherwise the Linux driver will + * have a problem. + */ + /* + * we received a bunch of frames or no + * CRC error occured on the network -> + * ok. + */ + pPrt->PPrevRx = RxCts; + pPrt->PPrevFcs = FcsErrCts; + pPrt->PPrevShorts = Shorts; + + return(SK_HW_PS_NONE) ; + } + + pPrt->PPrevFcs = FcsErrCts; + } + + + if ((Shorts - pPrt->PPrevShorts) > CheckShorts) { + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_IRQ, + ("Short Event Count Restart Port %d \n", Port)); + Rtv = SK_HW_PS_RESTART; + } + + pPrt->PPrevShorts = Shorts; + pPrt->PPrevRx = RxCts; + + return(Rtv); +} + + +/****************************************************************************** + * + * SkGePortCheckUp - Implementing of the Workaround Errata # 2 + * + * return: + * 0 o.k. nothing needed + * 1 Restart needed on this port + * 2 Link came up + */ +int SkGePortCheckUp( +SK_AC *pAC, /* Adapters context */ +SK_IOC IoC, /* IO Context */ +int Port) /* Which port should be checked */ +{ + SK_GEPORT *pPrt; /* GIni Port struct pointer */ + + pPrt = &pAC->GIni.GP[Port]; + + switch (pPrt->PhyType) { + case SK_PHY_XMAC: + return (SkGePortCheckUpXmac(pAC, IoC, Port)); + case SK_PHY_BCOM: + return (SkGePortCheckUpBcom(pAC, IoC, Port)); + case SK_PHY_LONE: + return (SkGePortCheckUpLone(pAC, IoC, Port)); + case SK_PHY_NAT: + return (SkGePortCheckUpNat(pAC, IoC, Port)); + } + + return(SK_HW_PS_NONE) ; +} + + +/****************************************************************************** + * + * SkGePortCheckUpXmac - Implementing of the Workaround Errata # 2 + * + * return: + * 0 o.k. nothing needed + * 1 Restart needed on this port + * 2 Link came up + */ +static int SkGePortCheckUpXmac( +SK_AC *pAC, /* Adapters context */ +SK_IOC IoC, /* IO Context */ +int Port) /* Which port should be checked */ +{ + SK_GEPORT *pPrt; /* GIni Port struct pointer */ + SK_BOOL AutoNeg; /* Is Autonegotiation used ? */ + SK_U16 Isrc; /* Interrupt source register */ + SK_U32 GpReg; /* General Purpose register value */ + SK_U16 IsrcSum; /* Interrupt source register sum */ + SK_U16 LpAb; /* Link Partner Ability */ + SK_U16 ResAb; /* Resolved Ability */ + SK_U64 Shorts; /* Short Event Counter */ + unsigned int Len; + SK_U8 NextMode; /* Next AutoSensing Mode */ + SK_U16 ExtStat; /* Extended Status Register */ + int Done; + + pPrt = &pAC->GIni.GP[Port]; + + if (pPrt->PHWLinkUp) { + if (pPrt->PhyType != SK_PHY_XMAC) { + return(SK_HW_PS_NONE) ; + } + else { + return(SkGePortCheckShorts(pAC, IoC, Port)) ; + } + } + + IsrcSum = pPrt->PIsave; + pPrt->PIsave = 0; + + /* Now wait for each ports link */ + if (pPrt->PLinkMode == SK_LMODE_HALF || + pPrt->PLinkMode == SK_LMODE_FULL) { + AutoNeg = SK_FALSE; + } else { + AutoNeg = SK_TRUE; + } + + if (pPrt->PLinkBroken) { + /* Link was broken */ + XM_IN32(IoC,Port,XM_GP_PORT, &GpReg) ; + + if ((GpReg & XM_GP_INP_ASS) == 0) { + /* The Link is in sync */ + XM_IN16(IoC,Port,XM_ISRC, &Isrc) ; + IsrcSum |= Isrc; + SkXmAutoNegLipaXmac(pAC, IoC, Port, IsrcSum); + if ((Isrc & XM_IS_INP_ASS) == 0) { + /* It has been in sync since last Time */ + /* Restart the PORT */ + + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_IRQ, + ("Link in sync Restart Port %d\n", + Port)); + + /* + * We now need to reinitialize the PrevSHorts + * counter. + */ + Len = sizeof(SK_U64); + SkPnmiGetVar(pAC, IoC, + OID_SKGE_STAT_RX_SHORTS, + (char *) &Shorts, + &Len, + (SK_U32) SK_PNMI_PORT_PHYS2INST(Port)); + pPrt->PPrevShorts = Shorts; + + pAC->GIni.GP[Port].PLinkBroken = SK_FALSE ; + + /* + * Link Restart Workaround: + * it may be possible that the other Link side + * restarts its link as well an we detect + * another LinkBroken. To prevent this + * happening we check for a maximum number + * of consecutive restart. If those happens, + * we do NOT restart the active link and + * check whether the lionk is now o.k. + */ + pAC->GIni.GP[Port].PLinkResCt ++; + pPrt->PAutoNegTimeOut = 0; + + if (pAC->GIni.GP[Port].PLinkResCt < + SK_MAX_LRESTART) { + return(SK_HW_PS_RESTART) ; + } + + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("Do NOT restart on Port %d %x %x\n", + Port, Isrc, IsrcSum)); + pAC->GIni.GP[Port].PLinkResCt = 0; + } else { + pPrt->PIsave = (SK_U16) (IsrcSum & (XM_IS_AND)); + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("Save Sync/nosync Port %d %x %x\n", + Port, Isrc, IsrcSum)); + /* Do nothing more if link is broken */ + return(SK_HW_PS_NONE) ; + } + } else { + /* Do nothing more if link is broken */ + return(SK_HW_PS_NONE) ; + } + + } else { + /* Link was not broken, check if it is */ + XM_IN16(IoC,Port,XM_ISRC, &Isrc) ; + IsrcSum |= Isrc; + if ((Isrc & XM_IS_INP_ASS) == XM_IS_INP_ASS) { + XM_IN16(IoC,Port,XM_ISRC, &Isrc) ; + IsrcSum |= Isrc; + if ((Isrc & XM_IS_INP_ASS) == XM_IS_INP_ASS) { + XM_IN16(IoC,Port,XM_ISRC, &Isrc) ; + IsrcSum |= Isrc; + if ((Isrc & XM_IS_INP_ASS) == XM_IS_INP_ASS) { + pPrt->PLinkBroken = SK_TRUE ; + /* + * Re-Init Link partner Autoneg flag + */ + pPrt->PLipaAutoNeg = SK_LIPA_UNKNOWN; + SK_DBG_MSG(pAC,SK_DBGMOD_HWM, + SK_DBGCAT_IRQ, + ("Link broken Port %d\n", + Port)); + + /* cable removed-> reinit Sensemode */ + /* Init default sense mode */ + SkHWInitDefSense(pAC, IoC, Port); + + return(SK_HW_PS_RESTART) ; + } + } + } else { + SkXmAutoNegLipaXmac(pAC, IoC, Port, Isrc); + if (SkGePortCheckShorts(pAC, IoC, Port) == + SK_HW_PS_RESTART) { + return(SK_HW_PS_RESTART) ; + } + } + } + + /* + * here we usually can check whether the link is in sync and + * autonegotiation is done. + */ + XM_IN32(IoC,Port,XM_GP_PORT, &GpReg) ; + XM_IN16(IoC,Port,XM_ISRC, &Isrc) ; + IsrcSum |= Isrc; + + SkXmAutoNegLipaXmac(pAC, IoC, Port, IsrcSum); + if ((GpReg & XM_GP_INP_ASS) != 0 || (IsrcSum & XM_IS_INP_ASS) != 0) { + if ((GpReg & XM_GP_INP_ASS) == 0) { + /* + * Save Autonegotiation Done interrupt only if link + * is in sync + */ + pPrt->PIsave = (SK_U16) (IsrcSum & (XM_IS_AND)); + } +#ifdef DEBUG + if (pPrt->PIsave & (XM_IS_AND)) { + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("AutoNeg done rescheduled Port %d\n", Port)); + } +#endif + return(SK_HW_PS_NONE) ; + } + + if (AutoNeg) { + if (IsrcSum & XM_IS_AND) { + SkHWLinkUp(pAC, IoC, Port) ; + Done = SkXmAutoNegDone(pAC,IoC,Port); + if (Done != SK_AND_OK) { + /* Get PHY parameters, for debuging only */ + PHY_READ(IoC, pPrt, Port, PHY_XMAC_AUNE_LP, + &LpAb); + PHY_READ(IoC, pPrt, Port, PHY_XMAC_RES_ABI, + &ResAb); + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("AutoNeg FAIL Port %d (LpAb %x, ResAb %x)\n", + Port, LpAb, ResAb)); + + /* Try next possible mode */ + NextMode = SkHWSenseGetNext(pAC, IoC, Port); + SkHWLinkDown(pAC, IoC, Port) ; + if (Done == SK_AND_DUP_CAP) { + /* GoTo next mode */ + SkHWSenseSetNext(pAC, IoC, Port, + NextMode); + } + + return(SK_HW_PS_RESTART) ; + + } else { + /* + * Dummy Read extended status to prevent + * extra link down/ups + * (clear Page Received bit if set) + */ + PHY_READ(IoC, pPrt, Port, PHY_XMAC_AUNE_EXP, &ExtStat); + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("AutoNeg done Port %d\n", Port)); + return(SK_HW_PS_LINK) ; + } + } + + /* + * AutoNeg not done, but HW link is up. Check for timeouts + */ + pPrt->PAutoNegTimeOut ++; + if (pPrt->PAutoNegTimeOut >= SK_AND_MAX_TO) { + /* + * Timeout occured. + * What do we need now? + */ + SK_DBG_MSG(pAC,SK_DBGMOD_HWM, + SK_DBGCAT_IRQ, + ("AutoNeg timeout Port %d\n", + Port)); + if (pPrt->PLinkModeConf == SK_LMODE_AUTOSENSE && + pPrt->PLipaAutoNeg != SK_LIPA_AUTO) { + /* + * Timeout occured + * Set Link manually up. + */ + SkHWSenseSetNext(pAC, IoC, Port, + SK_LMODE_FULL); + SK_DBG_MSG(pAC,SK_DBGMOD_HWM, + SK_DBGCAT_IRQ, + ("Set manual full duplex Port %d\n", + Port)); + } + + /* + * Do the restart + */ + return(SK_HW_PS_RESTART) ; + } + } else { + /* + * Link is up and we don't need more. + */ +#ifdef DEBUG + if (pPrt->PLipaAutoNeg == SK_LIPA_AUTO) { + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("ERROR: Lipa auto detected on port %d\n", + Port)); + } +#endif + + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_IRQ, + ("Link sync(GP), Port %d\n", Port)); + SkHWLinkUp(pAC, IoC, Port) ; + return(SK_HW_PS_LINK) ; + } + + return(SK_HW_PS_NONE) ; +} + + +/****************************************************************************** + * + * SkGePortCheckUpBcom - Check, if the link is up + * + * return: + * 0 o.k. nothing needed + * 1 Restart needed on this port + * 2 Link came up + */ +static int SkGePortCheckUpBcom( +SK_AC *pAC, /* Adapters context */ +SK_IOC IoC, /* IO Context */ +int Port) /* Which port should be checked */ +{ + SK_GEPORT *pPrt; /* GIni Port struct pointer */ + SK_BOOL AutoNeg; /* Is Autonegotiation used ? */ + SK_U16 Isrc; /* Interrupt source register */ + SK_U16 LpAb; /* Link Partner Ability */ + SK_U16 ExtStat; /* Extended Status Register */ + SK_U16 PhyStat; /* Phy Status Register */ + int Done; + SK_U16 ResAb; + + pPrt = &pAC->GIni.GP[Port]; + PHY_READ(IoC, pPrt, Port, PHY_BCOM_STAT, &PhyStat); + + if (pPrt->PHWLinkUp) { + return(SK_HW_PS_NONE) ; + } + + pPrt->PIsave = 0; + + /* Now wait for each ports link */ + if (pPrt->PLinkMode == SK_LMODE_HALF || + pPrt->PLinkMode == SK_LMODE_FULL) { + AutoNeg = SK_FALSE; + } else { + AutoNeg = SK_TRUE; + } + + /* + * here we usually can check whether the link is in sync and + * autonegotiation is done. + */ + XM_IN16(IoC, Port, XM_ISRC, &Isrc) ; + + PHY_READ(IoC, pPrt, Port, PHY_BCOM_STAT, &PhyStat); + + SkXmAutoNegLipaBcom(pAC, IoC, Port, PhyStat); + if ((PhyStat & PHY_ST_LSYNC) == 0){ + return(SK_HW_PS_NONE) ; + } + + if (AutoNeg) { + if (PhyStat & PHY_ST_AN_OVER) { + SkHWLinkUp(pAC, IoC, Port) ; + Done = SkXmAutoNegDone(pAC,IoC,Port); + if (Done != SK_AND_OK) { + /* Get PHY parameters, for debuging only */ + PHY_READ(IoC, pPrt, Port, + PHY_BCOM_AUNE_LP, + &LpAb); + PHY_READ(IoC, pPrt, Port, + PHY_BCOM_1000T_STAT, + &ExtStat); + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("AutoNeg FAIL Port %d (LpAb %x, " + "1000TStat %x)\n", + Port, LpAb, ExtStat)); + return(SK_HW_PS_RESTART) ; + + } else { + /* + * Dummy Read interrupt status to prevent + * extra link down/ups + */ + PHY_READ(IoC, pPrt, Port, PHY_BCOM_INT_STAT, + &ExtStat); + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("AutoNeg done Port %d\n", Port)); + return(SK_HW_PS_LINK) ; + } + } + } else { + /* + * Link is up and we don't need more. + */ +#ifdef DEBUG + if (pPrt->PLipaAutoNeg == SK_LIPA_AUTO) { + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("ERROR: Lipa auto detected on port %d\n", + Port)); + } +#endif + PHY_READ(IoC, pPrt, Port, PHY_BCOM_1000T_STAT, &ResAb); + if (ResAb & (PHY_B_1000S_MSF)) { + /* Error */ + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("Master/Slave Fault port %d\n", Port)); + pPrt->PAutoNegFail = SK_TRUE; + pPrt->PMSStatus = SK_MS_STAT_FAULT; + return (SK_AND_OTHER); + } else if (ResAb & PHY_B_1000S_MSR) { + pPrt->PMSStatus = SK_MS_STAT_MASTER ; + } else { + pPrt->PMSStatus = SK_MS_STAT_SLAVE ; + } + + + /* + * Dummy Read interrupt status to prevent + * extra link down/ups + */ + PHY_READ(IoC, pPrt, Port, PHY_BCOM_INT_STAT, &ExtStat); + + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_IRQ, + ("Link sync(GP), Port %d\n", Port)); + SkHWLinkUp(pAC, IoC, Port) ; + return(SK_HW_PS_LINK) ; + } + + return(SK_HW_PS_NONE) ; +} + +/****************************************************************************** + * + * SkGePortCheckUpLone - Check if the link is up + * + * return: + * 0 o.k. nothing needed + * 1 Restart needed on this port + * 2 Link came up + */ +static int SkGePortCheckUpLone( +SK_AC *pAC, /* Adapters context */ +SK_IOC IoC, /* IO Context */ +int Port) /* Which port should be checked */ +{ + SK_GEPORT *pPrt; /* GIni Port struct pointer */ + SK_BOOL AutoNeg; /* Is Autonegotiation used ? */ + SK_U16 Isrc; /* Interrupt source register */ + SK_U16 LpAb; /* Link Partner Ability */ + SK_U8 NextMode; /* Next AutoSensing Mode */ + SK_U16 ExtStat; /* Extended Status Register */ + SK_U16 PhyStat; /* Phy Status Register */ + SK_U16 StatSum; + int Done; + + pPrt = &pAC->GIni.GP[Port]; + + if (pPrt->PHWLinkUp) { + return(SK_HW_PS_NONE) ; + } + + StatSum = pPrt->PIsave; + pPrt->PIsave = 0; + + /* Now wait for each ports link */ + if (pPrt->PLinkMode == SK_LMODE_HALF || + pPrt->PLinkMode == SK_LMODE_FULL) { + AutoNeg = SK_FALSE; + } else { + AutoNeg = SK_TRUE; + } + + /* + * here we usually can check whether the link is in sync and + * autonegotiation is done. + */ + XM_IN16(IoC, Port, XM_ISRC, &Isrc) ; + PHY_READ(IoC, pPrt, Port, PHY_LONE_STAT, &PhyStat); + StatSum |= PhyStat; + + SkXmAutoNegLipaLone(pAC, IoC, Port, PhyStat); + if ((PhyStat & PHY_ST_LSYNC) == 0){ + /* + * Save Autonegotiation Done bit + */ + pPrt->PIsave = (SK_U16) (StatSum & PHY_ST_AN_OVER); +#ifdef DEBUG + if (pPrt->PIsave & PHY_ST_AN_OVER) { + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("AutoNeg done rescheduled Port %d\n", Port)); + } +#endif + return(SK_HW_PS_NONE) ; + } + + if (AutoNeg) { + if (StatSum & PHY_ST_AN_OVER) { + SkHWLinkUp(pAC, IoC, Port) ; + Done = SkXmAutoNegDone(pAC,IoC,Port); + if (Done != SK_AND_OK) { + /* Get PHY parameters, for debuging only */ + PHY_READ(IoC, pPrt, Port, + PHY_LONE_AUNE_LP, + &LpAb); + PHY_READ(IoC, pPrt, Port, + PHY_LONE_1000T_STAT, + &ExtStat); + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("AutoNeg FAIL Port %d (LpAb %x, 1000TStat %x)\n", + Port, LpAb, ExtStat)); + + /* Try next possible mode */ + NextMode = SkHWSenseGetNext(pAC, IoC, Port); + SkHWLinkDown(pAC, IoC, Port) ; + if (Done == SK_AND_DUP_CAP) { + /* GoTo next mode */ + SkHWSenseSetNext(pAC, IoC, Port, + NextMode); + } + + return(SK_HW_PS_RESTART) ; + + } else { + /* + * Dummy Read interrupt status to prevent + * extra link down/ups + */ + PHY_READ(IoC, pPrt, Port, PHY_LONE_INT_STAT, + &ExtStat); + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("AutoNeg done Port %d\n", Port)); + return(SK_HW_PS_LINK) ; + } + } + + /* + * AutoNeg not done, but HW link is up. Check for timeouts + */ + pPrt->PAutoNegTimeOut ++; + if (pPrt->PAutoNegTimeOut >= SK_AND_MAX_TO) { + /* + * Timeout occured. + * What do we need now? + */ + SK_DBG_MSG(pAC,SK_DBGMOD_HWM, + SK_DBGCAT_IRQ, + ("AutoNeg timeout Port %d\n", + Port)); + if (pPrt->PLinkModeConf == SK_LMODE_AUTOSENSE && + pPrt->PLipaAutoNeg != SK_LIPA_AUTO) { + /* + * Timeout occured + * Set Link manually up. + */ + SkHWSenseSetNext(pAC, IoC, Port, + SK_LMODE_FULL); + SK_DBG_MSG(pAC,SK_DBGMOD_HWM, + SK_DBGCAT_IRQ, + ("Set manual full duplex Port %d\n", + Port)); + } + + /* + * Do the restart + */ + return(SK_HW_PS_RESTART) ; + } + } else { + /* + * Link is up and we don't need more. + */ +#ifdef DEBUG + if (pPrt->PLipaAutoNeg == SK_LIPA_AUTO) { + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("ERROR: Lipa auto detected on port %d\n", + Port)); + } +#endif + + /* + * Dummy Read interrupt status to prevent + * extra link down/ups + */ + PHY_READ(IoC, pPrt, Port, PHY_LONE_INT_STAT, &ExtStat); + + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_IRQ, + ("Link sync(GP), Port %d\n", Port)); + SkHWLinkUp(pAC, IoC, Port) ; + return(SK_HW_PS_LINK) ; + } + + return(SK_HW_PS_NONE) ; +} + + +/****************************************************************************** + * + * SkGePortCheckUpNat - Check if the link is up + * + * return: + * 0 o.k. nothing needed + * 1 Restart needed on this port + * 2 Link came up + */ +static int SkGePortCheckUpNat( +SK_AC *pAC, /* Adapters context */ +SK_IOC IoC, /* IO Context */ +int Port) /* Which port should be checked */ +{ + /* todo: National */ + return(SK_HW_PS_NONE) ; +} + + +/****************************************************************************** + * + * Event service routine + * + * Description: + * + * Notes: + */ +int SkGeSirqEvent( +SK_AC *pAC, /* Adapters context */ +SK_IOC IoC, /* Io Context */ +SK_U32 Event, /* Module specific Event */ +SK_EVPARA Para) /* Event specific Parameter */ +{ + SK_U32 Port; + SK_U32 Time; + SK_U8 Val8 ; + int PortStat; + + Port = Para.Para32[0]; + + switch (Event) { + case SK_HWEV_WATIM: + /* Check whether port came up */ + PortStat = SkGePortCheckUp(pAC, IoC, Port); + + switch (PortStat) { + case SK_HW_PS_RESTART: + if (pAC->GIni.GP[Port].PHWLinkUp) { + /* + * Set Link to down. + */ + SkHWLinkDown(pAC, IoC, Port); + + /* + * Signal directly to RLMT to ensure correct + * sequence of SWITCH and RESET event. + */ + Para.Para32[0] = (SK_U32) Port; + SkRlmtEvent(pAC, IoC, SK_RLMT_LINK_DOWN, Para); + + /* Start workaround Errata #2 timer */ + SkTimerStart(pAC, IoC, + &pAC->GIni.GP[Port].PWaTimer, + SK_WA_INA_TIME, + SKGE_HWAC, + SK_HWEV_WATIM, + Para); + } + + /* Restart needed */ + SkEventQueue(pAC, SKGE_DRV, SK_DRV_PORT_RESET, Para); + break; + + case SK_HW_PS_LINK: + /* Signal to RLMT */ + SkEventQueue(pAC, SKGE_RLMT, SK_RLMT_LINK_UP, Para); + break; + + } + + /* Start again the check Timer */ + if (pAC->GIni.GP[Port].PHWLinkUp) { + Time = SK_WA_ACT_TIME; + } else { + Time = SK_WA_INA_TIME; + } + + /* todo: still needed for non-Xmac-PHYs ??? */ + /* Start workaround Errata #2 timer */ + SkTimerStart(pAC, IoC, &pAC->GIni.GP[Port].PWaTimer, Time, + SKGE_HWAC, SK_HWEV_WATIM, Para); + + break; + + case SK_HWEV_PORT_START: + if (pAC->GIni.GP[Port].PHWLinkUp) { + /* + * Signal directly to RLMT to ensure correct + * sequence of SWITCH and RESET event. + */ + Para.Para32[0] = (SK_U32) Port; + SkRlmtEvent(pAC, IoC, SK_RLMT_LINK_DOWN, Para); + } + + SkHWLinkDown(pAC, IoC, Port) ; + + /* Schedule Port RESET */ + SkEventQueue(pAC, SKGE_DRV, SK_DRV_PORT_RESET, Para); + + /* Start workaround Errata #2 timer */ + SkTimerStart(pAC, IoC, &pAC->GIni.GP[Port].PWaTimer, + SK_WA_INA_TIME,SKGE_HWAC,SK_HWEV_WATIM,Para); + break; + + case SK_HWEV_PORT_STOP: + if (pAC->GIni.GP[Port].PHWLinkUp) { + /* + * Signal directly to RLMT to ensure correct + * sequence of SWITCH and RESET event. + */ + Para.Para32[0] = (SK_U32) Port; + SkRlmtEvent(pAC, IoC, SK_RLMT_LINK_DOWN, Para); + } + /* Stop Workaround Timer */ + SkTimerStop(pAC, IoC, &pAC->GIni.GP[Port].PWaTimer) ; + + SkHWLinkDown(pAC, IoC, Port) ; + break; + + case SK_HWEV_UPDATE_STAT: + /* We do NOT need to update any statistics */ + break; + + case SK_HWEV_CLEAR_STAT: + /* We do NOT need to clear any statistics */ + for (Port = 0; Port < (SK_U32) pAC->GIni.GIMacsFound; Port++) { + pAC->GIni.GP[Port].PPrevRx = 0; + pAC->GIni.GP[Port].PPrevFcs = 0; + pAC->GIni.GP[Port].PPrevShorts = 0; + } + break; + + case SK_HWEV_SET_LMODE: + Val8 = (SK_U8) Para.Para32[1]; + if (pAC->GIni.GP[Port].PLinkModeConf != Val8) { + /* Set New link mode */ + pAC->GIni.GP[Port].PLinkModeConf = Val8; + + /* Restart Port */ + SkEventQueue(pAC, SKGE_HWAC, SK_HWEV_PORT_STOP, Para); + SkEventQueue(pAC, SKGE_HWAC, SK_HWEV_PORT_START, Para); + } + break; + + case SK_HWEV_SET_FLOWMODE: + Val8 = (SK_U8) Para.Para32[1]; + if (pAC->GIni.GP[Port].PFlowCtrlMode != Val8) { + /* Set New Flow Control mode */ + pAC->GIni.GP[Port].PFlowCtrlMode = Val8; + + /* Restart Port */ + SkEventQueue(pAC, SKGE_HWAC, SK_HWEV_PORT_STOP, Para); + SkEventQueue(pAC, SKGE_HWAC, SK_HWEV_PORT_START, Para); + } + break; + + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW, SKERR_SIRQ_E001, + SKERR_SIRQ_E001MSG); + break; + } + + return(0) ; +} + + +/****************************************************************************** + * + * SkPhyIsrBcom - PHY interrupt service routine + * + * Description: handle all interrupts from BCOM PHY + * + * Returns: N/A + */ +static void SkPhyIsrBcom( +SK_AC *pAC, /* Adapters context */ +SK_IOC IoC, /* Io Context */ +int Port, /* Port Num = PHY Num */ +SK_U16 IStatus) /* Interrupts masked with PHY-Mask */ +{ + SK_EVPARA Para; + + if (IStatus & PHY_B_IS_PSE) { + /* incorrectable pair swap error */ + SK_ERR_LOG(pAC, SK_ERRCL_SW | SK_ERRCL_INIT, SKERR_SIRQ_E022, + SKERR_SIRQ_E022MSG) ; + } + + if (IStatus & PHY_B_IS_MDXI_SC) { + /* not used */ + } + + if (IStatus & PHY_B_IS_HCT) { + /* not used */ + } + + if (IStatus & PHY_B_IS_LCT) { + /* not used */ + } + + if (IStatus & (PHY_B_IS_AN_PR | PHY_B_IS_LST_CHANGE)) { + SkHWLinkDown(pAC, IoC, Port); + + /* Signal to RLMT */ + Para.Para32[0] = (SK_U32) Port; + SkEventQueue(pAC, SKGE_RLMT, SK_RLMT_LINK_DOWN, Para); + + /* Start workaround Errata #2 timer */ + SkTimerStart(pAC, IoC, &pAC->GIni.GP[Port].PWaTimer, + SK_WA_INA_TIME,SKGE_HWAC,SK_HWEV_WATIM,Para); + } + + if (IStatus & PHY_B_IS_NO_HDCL) { + /* not used */ + } + + if (IStatus & PHY_B_IS_NO_HDC) { + /* not used */ + } + + if (IStatus & PHY_B_IS_NEG_USHDC) { + /* not used */ + } + + if (IStatus & PHY_B_IS_SCR_S_ER) { + /* not used */ + } + + if (IStatus & PHY_B_IS_RRS_CHANGE) { + /* not used */ + } + + if (IStatus & PHY_B_IS_LRS_CHANGE) { + /* not used */ + } + + if (IStatus & PHY_B_IS_DUP_CHANGE) { + /* not used */ + } + + if (IStatus & PHY_B_IS_LSP_CHANGE) { + /* not used */ + } + + if (IStatus & PHY_B_IS_CRC_ER) { + /* not used */ + } + +} + + +/****************************************************************************** + * + * SkPhyIsrLone - PHY interrupt service routine + * + * Description: handle all interrupts from LONE PHY + * + * Returns: N/A + */ +static void SkPhyIsrLone( +SK_AC *pAC, /* Adapters context */ +SK_IOC IoC, /* Io Context */ +int Port, /* Port Num = PHY Num */ +SK_U16 IStatus) /* Interrupts masked with PHY-Mask */ +{ + SK_EVPARA Para; + + if (IStatus & PHY_L_IS_CROSS) { + /* not used */ + } + + if (IStatus & PHY_L_IS_POL) { + /* not used */ + } + + if (IStatus & PHY_L_IS_SS) { + /* not used */ + } + + if (IStatus & PHY_L_IS_CFULL) { + /* not used */ + } + + if (IStatus & PHY_L_IS_AN_C) { + /* not used */ + } + + if (IStatus & PHY_L_IS_SPEED) { + /* not used */ + } + + if (IStatus & PHY_L_IS_CFULL) { + /* not used */ + } + + if (IStatus & (PHY_L_IS_DUP | PHY_L_IS_ISOL)) { + SkHWLinkDown(pAC, IoC, Port); + + /* Signal to RLMT */ + Para.Para32[0] = (SK_U32) Port; + SkEventQueue(pAC, SKGE_RLMT, SK_RLMT_LINK_DOWN, Para); + + /* Start workaround Errata #2 timer */ + SkTimerStart(pAC, IoC, &pAC->GIni.GP[Port].PWaTimer, + SK_WA_INA_TIME,SKGE_HWAC,SK_HWEV_WATIM,Para); + } + + if (IStatus & PHY_L_IS_MDINT) { + /* not used */ + } + +} + + +/* End of File */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/ski2c.c linux/drivers/net/sk98lin/ski2c.c --- v2.2.13/linux/drivers/net/sk98lin/ski2c.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/ski2c.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,1181 @@ +/****************************************************************************** + * + * Name: ski2c.c + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.41 $ + * Date: $Date: 1999/09/14 14:11:30 $ + * Purpose: Funktions to access Voltage and Temperature Sensor + * (taken from Monalisa (taken from Concentrator)) + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skge.c" for further information. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: ski2c.c,v $ + * Revision 1.41 1999/09/14 14:11:30 malthoff + * The 1000BT Dual Link adapter has got only one Fan. + * The second Fan has been removed. + * + * Revision 1.40 1999/05/27 13:37:27 malthoff + * Set divisor of 1 for fan count calculation. + * + * Revision 1.39 1999/05/20 14:54:43 malthoff + * I2c.DummyReads is not used in Diagnostics. + * + * Revision 1.38 1999/05/20 09:20:56 cgoos + * Changes for 1000Base-T (up to 9 sensors and fans). + * + * Revision 1.37 1999/03/25 15:11:36 gklug + * fix: reset error flag if sensor reads correct value + * + * Revision 1.36 1999/01/07 14:11:16 gklug + * fix: break added + * + * Revision 1.35 1999/01/05 15:31:49 gklug + * fix: CLEAR STAT command is now added correctly + * + * Revision 1.34 1998/12/01 13:45:16 gklug + * fix: introduced Init level, because we don't need reinits + * + * Revision 1.33 1998/11/09 14:54:25 malthoff + * Modify I2C Transfer Timeout handling for Diagnostics. + * + * Revision 1.32 1998/11/03 06:54:35 gklug + * fix: Need dummy reads at the beginning to init sensors + * + * Revision 1.31 1998/11/03 06:42:42 gklug + * fix: select correctVIO range only if between warning levels + * + * Revision 1.30 1998/11/02 07:36:53 gklug + * fix: Error should not include WARNING message + * + * Revision 1.29 1998/10/30 15:07:43 malthoff + * Disable 'I2C does not compelete' error log for diagnostics. + * + * Revision 1.28 1998/10/22 09:48:11 gklug + * fix: SysKonnectFileId typo + * + * Revision 1.27 1998/10/20 09:59:46 gklug + * add: parameter to SkOsGetTime + * + * Revision 1.26 1998/10/09 06:10:59 malthoff + * Remove ID_sccs by SysKonnectFileId. + * + * Revision 1.25 1998/09/08 12:40:26 gklug + * fix: syntax error in if clause + * + * Revision 1.24 1998/09/08 12:19:42 gklug + * chg: INIT Level checking + * + * Revision 1.23 1998/09/08 07:37:20 gklug + * fix: log error if PCI_IO voltage sensor could not be initialized + * + * Revision 1.22 1998/09/04 08:30:03 malthoff + * Bugfixes during SK_DIAG testing: + * - correct NS2BCLK() macro + * - correct SkI2cSndDev() + * - correct SkI2cWait() loop waiting for an event + * + * Revision 1.21 1998/08/27 14:46:01 gklug + * chg: if-then-else replaced by switch + * + * Revision 1.20 1998/08/27 14:40:07 gklug + * test: integral types + * + * Revision 1.19 1998/08/25 07:51:54 gklug + * fix: typos for compiling + * + * Revision 1.18 1998/08/25 06:12:24 gklug + * add: count errors and warnings + * fix: check not the sensor state but the ErrFlag! + * + * Revision 1.17 1998/08/25 05:56:48 gklug + * add: CheckSensor function + * + * Revision 1.16 1998/08/20 11:41:10 gklug + * chg: omit STRCPY macro by using char * as Sensor Description + * + * Revision 1.15 1998/08/20 11:37:35 gklug + * chg: change Ioc to IoC + * + * Revision 1.14 1998/08/20 11:32:52 gklug + * fix: Para compile error + * + * Revision 1.13 1998/08/20 11:27:41 gklug + * fix: Compile bugs with new awrning constants + * + * Revision 1.12 1998/08/20 08:53:05 gklug + * fix: compiler errors + * add: Threshold values + * + * Revision 1.11 1998/08/19 12:39:22 malthoff + * Compiler Fix: Some names have changed. + * + * Revision 1.10 1998/08/19 12:20:56 gklug + * fix: remove struct from C files (see CCC) + * + * Revision 1.9 1998/08/19 06:28:46 malthoff + * SkOsGetTime returns SK_U64 now. + * + * Revision 1.8 1998/08/17 13:53:33 gklug + * fix: Parameter of event function and its result + * + * Revision 1.7 1998/08/17 07:02:15 malthoff + * Modify the functions for accessing the I2C SW Registers. + * Modify SkI2cWait(). + * Put Lm80RcvReg into sklm80.c + * Remove Compiler Errors. + * + * Revision 1.6 1998/08/14 07:13:20 malthoff + * remove pAc with pAC + * remove smc with pAC + * change names to new convention + * + * Revision 1.5 1998/08/14 06:24:49 gklug + * add: init level 1 and 2 + * + * Revision 1.4 1998/08/12 14:31:12 gklug + * add: error log for unknown event + * + * Revision 1.3 1998/08/12 13:37:04 gklug + * add: Init 0 function + * + * Revision 1.2 1998/08/11 07:27:15 gklug + * add: functions of the interface + * adapt rest of source to C coding Conventions + * rmv: unneccessary code taken from Mona Lisa + * + * Revision 1.1 1998/06/19 14:28:43 malthoff + * Created. Sources taken from ML Projekt. + * Sources have to be reworked for GE. + * + * + ******************************************************************************/ + + +/* + i2C Protocol +*/ +static const char SysKonnectFileId[] = + "$Id: ski2c.c,v 1.41 1999/09/14 14:11:30 malthoff Exp $" ; + +#include "h/skdrv1st.h" /* Driver Specific Definitions */ +#include "h/lm80.h" +#include "h/skdrv2nd.h" /* Adapter Control- and Driver specific Def. */ + +#ifdef __C2MAN__ +/* + I2C protocol implemetation. + + General Description: + + The I2C protocol is used for the temperature sensors and for + the serial EEPROM which hold the configuration. + + This file covers functions that allow to read write and do + some bulk requests a specified I2C address. + + The Genesis has 2 I2C busses. One for the EEPROM which holds + the VPD Data and one for temperature and voltage sensor. + The following picture shows the I2C busses, I2C devices and + there control registers. + + Note: The VPD functions are in skvpd.c +. +. PCI Config I2C Bus for VPD Data: +. +. +------------+ +. | VPD EEPROM | +. +------------+ +. | +. | <-- I2C +. | +. +-----------+-----------+ +. | | +. +-----------------+ +-----------------+ +. | PCI_VPD_ADR_REG | | PCI_VPD_DAT_REG | +. +-----------------+ +-----------------+ +. +. +. I2C Bus for LM80 sensor: +. +. +-----------------+ +. | Temperature and | +. | Voltage Sensor | +. | LM80 | +. +-----------------+ +. | +. | +. I2C --> | +. | +. +----+ +. +-------------->| OR |<--+ +. | +----+ | +. +------+------+ | +. | | | +. +--------+ +--------+ +----------+ +. | B2_I2C | | B2_I2C | | B2_I2C | +. | _CTRL | | _DATA | | _SW | +. +--------+ +--------+ +----------+ +. + The I2C bus may be driven by the B2_I2C_SW or by the B2_I2C_CTRL + and B2_I2C_DATA registers. + For driver software it is recommended to use the I2C control and + data register, because I2C bus timing is done by the ASIC and + an interrupt may be received when the I2C request is completed. + + Clock Rate Timing: MIN MAX generated by + VPD EEPROM: 50 kHz 100 kHz HW + LM80 over I2C Ctrl/Data reg. 50 kHz 100 kHz HW + LM80 over B2_I2C_SW register 0 400 kHz SW + + Note: The clock generated by the hardware is dependend on the + PCI clock. If the PCI bus clock is 33 MHz, the I2C/VPD + clock is 50 kHz. + */ +intro() +{} +#endif + +#ifdef SK_DIAG +/* + * I2C Fast Mode timing values used by the LM80. + * If new devices are added to the I2C bus the timing values have to be checked. + */ +#ifndef I2C_SLOW_TIMING +#define T_CLK_LOW 1300L /* clock low time in ns */ +#define T_CLK_HIGH 600L /* clock high time in ns */ +#define T_DATA_IN_SETUP 100L /* data in Set-UP Time */ +#define T_START_HOLD 600L /* start condition hold time */ +#define T_START_SETUP 600L /* start condition Set-up time */ +#define T_STOP_SETUP 600L /* stop condition Set-up time */ +#define T_BUS_IDLE 1300L /* time the bus must free after tx */ +#define T_CLK_2_DATA_OUT 900L /* max. clock low to data output valid */ +#else /* I2C_SLOW_TIMING */ +/* I2C Standard Mode Timing */ +#define T_CLK_LOW 4700L /* clock low time in ns */ +#define T_CLK_HIGH 4000L /* clock high time in ns */ +#define T_DATA_IN_SETUP 250L /* data in Set-UP Time */ +#define T_START_HOLD 4000L /* start condition hold time */ +#define T_START_SETUP 4700L /* start condition Set_up time */ +#define T_STOP_SETUP 4000L /* stop condition Set-up time */ +#define T_BUS_IDLE 4700L /* time the bus must free after tx */ +#endif /* !I2C_SLOW_TIMING */ + +#define NS2BCLK(x) (((x)*125)/10000) + +/* + * I2C Wire Operations + * + * About I2C_CLK_LOW(): + * + * The Data Direction bit (I2C_DATA_DIR) has to be set to input when setting + * clock to low, to prevent the ASIC and the I2C data client from driving the + * serial data line simultaneously (ASIC: last bit of a byte = '1', I2C client + * send an 'ACK'). See also Concentrator Bugreport No. 10192. + */ +#define I2C_DATA_HIGH(IoC) SK_I2C_SET_BIT(IoC,I2C_DATA) +#define I2C_DATA_LOW(IoC) SK_I2C_CLR_BIT(IoC,I2C_DATA) +#define I2C_DATA_OUT(IoC) SK_I2C_SET_BIT(IoC,I2C_DATA_DIR) +#define I2C_DATA_IN(IoC) SK_I2C_CLR_BIT(IoC,I2C_DATA_DIR|I2C_DATA) +#define I2C_CLK_HIGH(IoC) SK_I2C_SET_BIT(IoC,I2C_CLK) +#define I2C_CLK_LOW(IoC) SK_I2C_CLR_BIT(IoC,I2C_CLK|I2C_DATA_DIR) +#define I2C_START_COND(IoC) SK_I2C_CLR_BIT(IoC,I2C_CLK) + +#define NS2CLKT(x) ((x*125L)/10000) + +/*--------------- I2C Interface Register Functions --------------- */ + +/* + * sending one bit + */ +void SkI2cSndBit( +SK_IOC IoC, /* IoContext */ +SK_U8 Bit) /* Bit to send */ +{ + I2C_DATA_OUT(IoC) ; + if (Bit) { + I2C_DATA_HIGH(IoC); + } else { + I2C_DATA_LOW(IoC); + } + SkDgWaitTime(IoC,NS2BCLK(T_DATA_IN_SETUP)); + I2C_CLK_HIGH(IoC); + SkDgWaitTime(IoC,NS2BCLK(T_CLK_HIGH)); + I2C_CLK_LOW(IoC); +} + + + +/* + * Signal a start to the i2C Bus. + * + * A start is signaled when data goes to low in a high clock cycle. + * + * Ends with Clock Low. + * + * Status: not tested + */ +void SkI2cStart(SK_IOC IoC) /* I/O Context */ +{ + /* Init data and Clock to output lines */ + /* Set Data high */ + I2C_DATA_OUT(IoC) ; + I2C_DATA_HIGH(IoC) ; + /* Set Clock high */ + I2C_CLK_HIGH(IoC) ; + + SkDgWaitTime(IoC,NS2BCLK(T_START_SETUP)) ; + + /* Set Data Low */ + I2C_DATA_LOW(IoC) ; + + SkDgWaitTime(IoC,NS2BCLK(T_START_HOLD)) ; + + /* Clock low without Data to Input */ + I2C_START_COND(IoC) ; + + SkDgWaitTime(IoC,NS2BCLK(T_CLK_LOW)) ; +} + + +void SkI2cStop(SK_IOC IoC) /* I/O Context */ +{ + /* Init data and Clock to output lines */ + /* Set Data low */ + I2C_DATA_OUT(IoC) ; + I2C_DATA_LOW(IoC) ; + + SkDgWaitTime(IoC,NS2BCLK(T_CLK_2_DATA_OUT)) ; + + /* Set Clock high */ + I2C_CLK_HIGH(IoC) ; + + SkDgWaitTime(IoC,NS2BCLK(T_STOP_SETUP)) ; + + /* + * Set Data High: Do it by setting the Data Line to Input. + * Because of a pull up resistor the Data Line + * floods to high. + */ + I2C_DATA_IN(IoC) ; + + /* + * When I2C activity is stopped + * o DATA should be set to input and + * o CLOCK should be set to high! + */ + SkDgWaitTime(IoC,NS2BCLK(T_BUS_IDLE)) ; +} + +/* + * Receive just one bit via the i2C bus. + * + * Note: Clock must be set to LOW before calling this function. + * + * Returns The received bit. + */ +int SkI2cRcvBit(SK_IOC IoC) /* I/O Context */ +{ + int Bit; + SK_U8 I2cSwCtrl; + + /* Init data as input line */ + I2C_DATA_IN(IoC); + + SkDgWaitTime(IoC,NS2BCLK(T_CLK_2_DATA_OUT)) ; + + I2C_CLK_HIGH(IoC); + + SkDgWaitTime(IoC,NS2BCLK(T_CLK_HIGH)) ; + + SK_I2C_GET_SW(IoC,&I2cSwCtrl) ; + if (I2cSwCtrl & I2C_DATA) { + Bit = 1; + } else { + Bit = 0; + } + + I2C_CLK_LOW(IoC); + SkDgWaitTime(IoC,NS2BCLK(T_CLK_LOW-T_CLK_2_DATA_OUT)) ; + + return(Bit); +} + +/* + * Receive an ACK. + * + * returns 0 If acknoledged + * 1 in case of an error + */ +int SkI2cRcvAck(SK_IOC IoC) /* I/O Context */ +{ + /* + * Received bit must be zero. + */ + return (SkI2cRcvBit(IoC) != 0) ; +} + +/* + * Send an NACK. + */ +void SkI2cSndNAck(SK_IOC IoC) /* I/O Context */ +{ + /* + * Received bit must be zero. + */ + SkI2cSndBit(IoC,1) ; +} + +/* + * Send an ACK. + */ +void SkI2cSndAck(SK_IOC IoC) /* I/O Context */ +{ + /* + * Received bit must be zero. + * + */ + SkI2cSndBit(IoC,0) ; +} + +/* + * Send one byte to the i2C device and wait for ACK. + * + * Return acknoleged status. + */ +int SkI2cSndByte( +SK_IOC IoC, /* I/O Context */ +int Byte) /* byte to send */ +{ + int i; + + for (i=0; i<8; i++) { + if (Byte & (1<<(7-i))) { + SkI2cSndBit(IoC,1) ; + } else { + SkI2cSndBit(IoC,0) ; + } + } + + return(SkI2cRcvAck(IoC)) ; +} + + +/* + * Receive one byte and ack it. + * + * Return byte. + */ +int SkI2cRcvByte( +SK_IOC IoC, /* I/O Context */ +int Last) /* Last Byte Flag */ +{ + int i; + int Byte = 0; + + for (i=0; i<8; i++) { + Byte <<= 1 ; + Byte |= SkI2cRcvBit(IoC) ; + } + + if (Last) { + SkI2cSndNAck(IoC) ; + } else { + SkI2cSndAck(IoC) ; + } + + return(Byte) ; +} + + +/* + * Start dialog and send device address + * + * Return 0 if acknoleged, 1 in case of an error + */ +int SkI2cSndDev( +SK_IOC IoC, /* I/O Context */ +int Addr, /* Device Address */ +int Rw) /* Read / Write Flag */ +{ + SkI2cStart(IoC) ; + Rw = ~Rw ; + Rw &= I2C_WRITE ; + return(SkI2cSndByte(IoC, (Addr<<1) | Rw)) ; +} + +#endif /* SK_DIAG */ + +/*----------------- I2C CTRL Register Functions ----------*/ + +/* + * waits for a completetion of a I2C transfer + * + * returns 0: success, transfer completes + * 1: error, transfer does not complete, I2C transfer + * killed, wait loop terminated. + */ +int SkI2cWait( +SK_AC *pAC, /* Adapter Context */ +SK_IOC IoC, /* IoContext */ +int Event) /* complete event to wait for (I2C_READ or I2C_WRITE) */ +{ + SK_U64 StartTime ; + SK_U32 I2cCtrl ; + + StartTime = SkOsGetTime(pAC) ; + do { + if (SkOsGetTime(pAC) - StartTime > SK_TICKS_PER_SEC/16) { + SK_I2C_STOP(IoC) ; +#ifndef SK_DIAG + SK_ERR_LOG(pAC, SK_ERRCL_SW, SKERR_I2C_E002, + SKERR_I2C_E002MSG) ; +#endif /* !SK_DIAG */ + return(1) ; + } + SK_I2C_GET_CTL(IoC,&I2cCtrl) ; + } while((I2cCtrl & I2C_FLAG) == (SK_U32)Event << 31) ; + + return(0) ; +} + +#ifdef SK_DIAG +/* + * writes a single byte or 4 bytes into the I2C device + * + * returns 0: success + * 1: error + */ +int SkI2cWrite( +SK_AC *pAC, /* Adapter Context */ +SK_U32 I2cData, /* I2C Data to write */ +int I2cDev, /* I2C Device Address */ +int I2cReg, /* I2C Device Register Address */ +int I2cBurst) /* I2C Burst Flag ( 0 || I2C_BURST ) */ +{ + SK_OUT32(pAC,B2_I2C_DATA,I2cData) ; + SK_I2C_CTL(pAC,I2C_WRITE,I2cDev,I2cReg,I2cBurst); + return(SkI2cWait(pAC,pAC,I2C_WRITE)) ; +} + +/* + * reads a single byte or 4 bytes from the I2C device + * + * returns the word read + */ +SK_U32 SkI2cRead( +SK_AC *pAC, /* Adapter Context */ +int I2cDev, /* I2C Device Address */ +int I2cReg, /* I2C Device Register Address */ +int I2cBurst) /* I2C Burst Flag ( 0 || I2C_BURST ) */ +{ + SK_U32 Data ; + + SK_OUT32(pAC,B2_I2C_DATA,0) ; + SK_I2C_CTL(pAC,I2C_READ,I2cDev,I2cReg,I2cBurst); + if (SkI2cWait(pAC,pAC,I2C_READ)) { + w_print("I2c Transfer Timeout!\n"); + } + SK_IN32(pAC,B2_I2C_DATA,&Data) ; + return(Data) ; +} +#endif /* SK_DIAG */ + +/* + * read a sensors value + * + * This function read a sensors value from the I2c sensor chip. The sensor + * is defined by its index into the sensors database in the struct pAC points + * to. + * Returns 1 if the read is completed + * 0 if the read must be continued (I2c Bus still allocated) + */ +int SkI2cReadSensor( +SK_AC *pAC, /* Adapter Context */ +SK_IOC IoC, /* IoContext */ +SK_SENSOR *pSen) /* Sensor to be read */ +{ + return((*pSen->SenRead)(pAC,IoC,pSen)) ; +} + +/* + * Do the Init state 0 initialization + */ +static int SkI2cInit0( +SK_AC *pAC) /* Adapter Context */ +{ + int i; + + /* Begin with first sensor */ + pAC->I2c.CurrSens = 0; + + /* Set to mimimum sensor number */ + pAC->I2c.MaxSens = SK_MIN_SENSORS; + +#ifndef SK_DIAG + /* Initialize Number of Dummy Reads */ + pAC->I2c.DummyReads = SK_MAX_SENSORS; +#endif + + for (i=0; i < SK_MAX_SENSORS; i ++) { + switch (i) { + case 0: + pAC->I2c.SenTable[i].SenDesc = "Temperature" ; + pAC->I2c.SenTable[i].SenType = SK_SEN_TEMP; + pAC->I2c.SenTable[i].SenThreErrHigh = SK_SEN_ERRHIGH0; + pAC->I2c.SenTable[i].SenThreErrLow = SK_SEN_ERRLOW0; + pAC->I2c.SenTable[i].SenThreWarnHigh = SK_SEN_WARNHIGH0; + pAC->I2c.SenTable[i].SenThreWarnLow = SK_SEN_WARNLOW0; + pAC->I2c.SenTable[i].SenReg = LM80_TEMP_IN; + pAC->I2c.SenTable[i].SenInit = SK_TRUE; + break; + case 1: + pAC->I2c.SenTable[i].SenDesc = "Voltage PCI"; + pAC->I2c.SenTable[i].SenType = SK_SEN_VOLT; + pAC->I2c.SenTable[i].SenThreErrHigh = SK_SEN_ERRHIGH1; + pAC->I2c.SenTable[i].SenThreErrLow = SK_SEN_ERRLOW1; + pAC->I2c.SenTable[i].SenThreWarnHigh = SK_SEN_WARNHIGH1; + pAC->I2c.SenTable[i].SenThreWarnLow = SK_SEN_WARNLOW1; + pAC->I2c.SenTable[i].SenReg = LM80_VT0_IN; + pAC->I2c.SenTable[i].SenInit = SK_TRUE; + break; + case 2: + pAC->I2c.SenTable[i].SenDesc = "Voltage PCI-IO"; + pAC->I2c.SenTable[i].SenType = SK_SEN_VOLT; + pAC->I2c.SenTable[i].SenThreErrHigh = SK_SEN_ERRHIGH2; + pAC->I2c.SenTable[i].SenThreErrLow = SK_SEN_ERRLOW2; + pAC->I2c.SenTable[i].SenThreWarnHigh = SK_SEN_WARNHIGH2; + pAC->I2c.SenTable[i].SenThreWarnLow = SK_SEN_WARNLOW2; + pAC->I2c.SenTable[i].SenReg = LM80_VT1_IN; + pAC->I2c.SenTable[i].SenInit = SK_FALSE; + break; + case 3: + pAC->I2c.SenTable[i].SenDesc = "Voltage ASIC"; + pAC->I2c.SenTable[i].SenType = SK_SEN_VOLT; + pAC->I2c.SenTable[i].SenThreErrHigh = SK_SEN_ERRHIGH3; + pAC->I2c.SenTable[i].SenThreErrLow = SK_SEN_ERRLOW3; + pAC->I2c.SenTable[i].SenThreWarnHigh = SK_SEN_WARNHIGH3; + pAC->I2c.SenTable[i].SenThreWarnLow = SK_SEN_WARNLOW3; + pAC->I2c.SenTable[i].SenReg = LM80_VT2_IN; + pAC->I2c.SenTable[i].SenInit = SK_TRUE; + break; + case 4: + pAC->I2c.SenTable[i].SenDesc = "Voltage PMA"; + pAC->I2c.SenTable[i].SenType = SK_SEN_VOLT; + pAC->I2c.SenTable[i].SenThreErrHigh = SK_SEN_ERRHIGH4; + pAC->I2c.SenTable[i].SenThreErrLow = SK_SEN_ERRLOW4; + pAC->I2c.SenTable[i].SenThreWarnHigh = SK_SEN_WARNHIGH4; + pAC->I2c.SenTable[i].SenThreWarnLow = SK_SEN_WARNLOW4; + pAC->I2c.SenTable[i].SenReg = LM80_VT3_IN; + pAC->I2c.SenTable[i].SenInit = SK_TRUE; + break; + case 5: + pAC->I2c.SenTable[i].SenDesc = "Voltage PHY 2V5"; + pAC->I2c.SenTable[i].SenType = SK_SEN_VOLT; + pAC->I2c.SenTable[i].SenThreErrHigh = SK_SEN_ERRHIGH5; + pAC->I2c.SenTable[i].SenThreErrLow = SK_SEN_ERRLOW5; + pAC->I2c.SenTable[i].SenThreWarnHigh = SK_SEN_WARNHIGH5; + pAC->I2c.SenTable[i].SenThreWarnLow = SK_SEN_WARNLOW5; + pAC->I2c.SenTable[i].SenReg = LM80_VT4_IN; + pAC->I2c.SenTable[i].SenInit = SK_TRUE; + break; + case 6: + pAC->I2c.SenTable[i].SenDesc = "Voltage PHY B PLL"; + pAC->I2c.SenTable[i].SenType = SK_SEN_VOLT; + pAC->I2c.SenTable[i].SenThreErrHigh = SK_SEN_ERRHIGH6; + pAC->I2c.SenTable[i].SenThreErrLow = SK_SEN_ERRLOW6; + pAC->I2c.SenTable[i].SenThreWarnHigh = SK_SEN_WARNHIGH6; + pAC->I2c.SenTable[i].SenThreWarnLow = SK_SEN_WARNLOW6; + pAC->I2c.SenTable[i].SenReg = LM80_VT5_IN; + pAC->I2c.SenTable[i].SenInit = SK_TRUE; + break; + case 7: + pAC->I2c.SenTable[i].SenDesc = "Speed Fan"; + pAC->I2c.SenTable[i].SenType = SK_SEN_FAN; + pAC->I2c.SenTable[i].SenThreErrHigh = SK_SEN_ERRHIGH; + pAC->I2c.SenTable[i].SenThreErrLow = SK_SEN_ERRLOW; + pAC->I2c.SenTable[i].SenThreWarnHigh = SK_SEN_WARNHIGH; + pAC->I2c.SenTable[i].SenThreWarnLow = SK_SEN_WARNLOW; + pAC->I2c.SenTable[i].SenReg = LM80_FAN2_IN; + pAC->I2c.SenTable[i].SenInit = SK_TRUE; + break; + default: + SK_ERR_LOG(pAC, SK_ERRCL_INIT | SK_ERRCL_SW, + SKERR_I2C_E001, SKERR_I2C_E001MSG); + break; + } + + pAC->I2c.SenTable[i].SenValue = 0; + pAC->I2c.SenTable[i].SenErrFlag = SK_SEN_ERR_OK; + pAC->I2c.SenTable[i].SenErrCts = 0; + pAC->I2c.SenTable[i].SenBegErrTS = 0; + pAC->I2c.SenTable[i].SenState = SK_SEN_IDLE; + pAC->I2c.SenTable[i].SenRead = SkLm80ReadSensor; + pAC->I2c.SenTable[i].SenDev = LM80_ADDR; + } + + /* Now we are INIT dataed */ + pAC->I2c.InitLevel = SK_INIT_DATA; + return(0); +} + +/* + * Do the init state 1 initialization + * + * initialize the following register of the LM80: + * Configuration register: + * - START, noINT, activeLOW, noINT#Clear, noRESET, noCI, noGPO#, noINIT + * + * Interrupt Mask Register 1: + * - all interrupts are Disabled (0xff) + * + * Interrupt Mask Register 2: + * - all interrupts are Disabled (0xff) Interrupt modi doesn't matter. + * + * Fan Divisor/RST_OUT register: + * - Divisors set to 1 (bits 00), all others 0s. + * + * OS# Configuration/Temperature resolution Register: + * - all 0s + * + */ +static int SkI2cInit1( +SK_AC *pAC, /* Adapter Context */ +SK_IOC IoC) /* IoContext needed in level 1 */ +{ + if (pAC->I2c.InitLevel != SK_INIT_DATA) { + /* ReInit not needed in I2C module */ + return(0); + } + + SK_OUT32(IoC, B2_I2C_DATA, 0); + SK_I2C_CTL(IoC, I2C_WRITE, LM80_ADDR, LM80_CFG, 0); + (void)SkI2cWait(pAC, IoC, I2C_WRITE) ; + + SK_OUT32(IoC, B2_I2C_DATA, 0xff); + SK_I2C_CTL(IoC, I2C_WRITE, LM80_ADDR, LM80_IMSK_1, 0); + (void)SkI2cWait(pAC, IoC, I2C_WRITE) ; + + SK_OUT32(IoC, B2_I2C_DATA, 0xff); + SK_I2C_CTL(IoC, I2C_WRITE, LM80_ADDR, LM80_IMSK_2, 0); + (void)SkI2cWait(pAC, IoC, I2C_WRITE) ; + + SK_OUT32(IoC, B2_I2C_DATA, 0x0); + SK_I2C_CTL(IoC, I2C_WRITE, LM80_ADDR, LM80_FAN_CTRL, 0); + (void)SkI2cWait(pAC, IoC, I2C_WRITE) ; + + SK_OUT32(IoC, B2_I2C_DATA, 0); + SK_I2C_CTL(IoC, I2C_WRITE, LM80_ADDR, LM80_TEMP_CTRL, 0); + (void)SkI2cWait(pAC, IoC, I2C_WRITE) ; + + SK_OUT32(IoC, B2_I2C_DATA, LM80_CFG_START); + SK_I2C_CTL(IoC, I2C_WRITE, LM80_ADDR, LM80_CFG, 0); + (void)SkI2cWait(pAC, IoC, I2C_WRITE) ; + + /* + * MaxSens has to be initialized here, because PhyType is not + * set when performing Init Level 1 + */ + switch (pAC->GIni.GP[0].PhyType) { + case SK_PHY_XMAC: + pAC->I2c.MaxSens = 5; + break; + case SK_PHY_BCOM: + pAC->I2c.SenTable[4].SenDesc = "Voltage PHY A PLL"; + if (pAC->GIni.GIMacsFound == 1) { + pAC->I2c.MaxSens = 6; + } + else { + pAC->I2c.MaxSens = 8; + } + break; + case SK_PHY_LONE: + pAC->I2c.MaxSens = 5; + break; + } + +#ifndef SK_DIAG + pAC->I2c.DummyReads = pAC->I2c.MaxSens; +#endif /* !SK_DIAG */ + + /* Now we are IO initialized */ + pAC->I2c.InitLevel = SK_INIT_IO; + return(0); +} + +/* + * Init level 2: Start first sensors read + */ +static int SkI2cInit2( +SK_AC *pAC, /* Adapter Context */ +SK_IOC IoC) /* IoContext needed in level 1 */ +{ + int ReadComplete; + SK_SENSOR *pSen; + + if (pAC->I2c.InitLevel != SK_INIT_IO) { + /* ReInit not needed in I2C module */ + /* Init0 and Init2 not permitted */ + return(0); + } + + pSen = &pAC->I2c.SenTable[pAC->I2c.CurrSens]; + ReadComplete = SkI2cReadSensor(pAC,IoC,pSen); + + if (ReadComplete) { + SK_ERR_LOG(pAC, SK_ERRCL_INIT, SKERR_I2C_E008, + SKERR_I2C_E008MSG); + } + + /* Now we are correctly initialized */ + pAC->I2c.InitLevel = SK_INIT_RUN; + + return(0); +} + +/* + * Initialize I2C devices + * + * Get the first voltage value and discard it. + * Go into temperature read mode. A default pointer is not set. + * + * The things to be done depend on the init level in the parameter list: + * Level 0: + * Initialize only the data structures. Do NOT access hardware. + * Level 1: + * Initialize hardware through SK_IN?OUT commands. Do NOT use interrupts. + * Level 2: + * Everything is possible. Interrupts may be used from now on. + * + * return: 0 = success + * other = error. + */ +int SkI2cInit( +SK_AC *pAC, /* Adapter Context */ +SK_IOC IoC, /* IoContext needed in level 1 */ +int Level) /* Init Level */ +{ + + switch (Level) { + case SK_INIT_DATA: + return(SkI2cInit0(pAC)) ; + case SK_INIT_IO: + return(SkI2cInit1(pAC, IoC)) ; + case SK_INIT_RUN: + return(SkI2cInit2(pAC, IoC)) ; + default: + break; + } + + return(0) ; +} + +#ifndef SK_DIAG +/* + * Interrupt service function for the I2c Interface + * + * Clears the Interrupt source + * + * Reads the register and check it for sending a trap. + * + * Starts the timer if necessary. + */ +void SkI2cIsr( +SK_AC *pAC, /* Adapters context */ +SK_IOC IoC) /* Io Context */ +{ + SK_EVPARA Para; + + /* Clear the interrupt source */ + SK_OUT32(IoC, B2_I2C_IRQ, I2C_CLR_IRQ) ; + + Para.Para64 = 0; + SkEventQueue(pAC, SKGE_I2C, SK_I2CEV_IRQ, Para); +} + +/* + * Check this sensors Value against the threshold and send events. + */ +static void SkI2cCheckSensor( +SK_AC *pAC, /* Adapters context */ +SK_SENSOR *pSen) +{ + SK_EVPARA ParaLocal; + SK_BOOL TooHigh; /* Is sensor too high? */ + SK_BOOL TooLow; /* Is sensor too low? */ + SK_U64 CurrTime; /* current Time */ + SK_BOOL DoTrapSend; /* We need to send a trap */ + SK_BOOL DoErrLog; /* We need to log the error */ + SK_BOOL IsError; /* We need to log the error */ + + /* Check Dummy Reads first */ + if (pAC->I2c.DummyReads > 0) { + pAC->I2c.DummyReads -- ; + return; + } + + /* Get the current time */ + CurrTime = SkOsGetTime(pAC) ; + + /* Set para to the most usefull setting: + * The current sensor. + */ + ParaLocal.Para64 = (SK_U64) pAC->I2c.CurrSens; + + /* Check the Value against the thresholds */ + /* First: Error Thresholds */ + TooHigh = (pSen->SenValue > pSen->SenThreErrHigh) ; + TooLow = (pSen->SenValue < pSen->SenThreErrLow) ; + + IsError = SK_FALSE ; + if (TooHigh || TooLow) { + /* Error condition is satiesfied */ + DoTrapSend = SK_TRUE; + DoErrLog = SK_TRUE; + + /* Now error condition is satisfied */ + IsError = SK_TRUE ; + + if (pSen->SenErrFlag == SK_SEN_ERR_ERR) { + /* This state is the former one */ + + /* So check first whether we have to send a trap */ + if (pSen->SenLastErrTrapTS + SK_SEN_ERR_TR_HOLD > + CurrTime) { + /* + * Do NOT send the Trap. The hold back time + * has to run out first. + */ + DoTrapSend = SK_FALSE; + } + + /* Check now whether we have to log an Error */ + if (pSen->SenLastErrLogTS + SK_SEN_ERR_LOG_HOLD > + CurrTime) { + /* + * Do NOT log the error. The hold back time + * has to run out first. + */ + DoErrLog = SK_FALSE; + } + } else { + /* We came from a different state */ + /* -> Set Begin Time Stamp */ + pSen->SenBegErrTS = CurrTime; + pSen->SenErrFlag = SK_SEN_ERR_ERR ; + } + + if (DoTrapSend) { + /* Set current Time */ + pSen->SenLastErrTrapTS = CurrTime; + pSen->SenErrCts ++; + + /* Queue PNMI Event */ + SkEventQueue(pAC, SKGE_PNMI, (TooHigh ? + SK_PNMI_EVT_SEN_ERR_UPP : + SK_PNMI_EVT_SEN_ERR_LOW), + ParaLocal) ; + } + + if (DoErrLog) { + /* Set current Time */ + pSen->SenLastErrLogTS = CurrTime; + + if (pSen->SenType == SK_SEN_TEMP) { + SK_ERR_LOG(pAC, SK_ERRCL_HW, SKERR_I2C_E011, + SKERR_I2C_E011MSG); + } else if (pSen->SenType == SK_SEN_VOLT) { + SK_ERR_LOG(pAC, SK_ERRCL_HW, SKERR_I2C_E012, + SKERR_I2C_E012MSG); + } else + { + SK_ERR_LOG(pAC, SK_ERRCL_HW, SKERR_I2C_E015, + SKERR_I2C_E015MSG); + } + } + } + + /* Check the Value against the thresholds */ + /* 2nd: Warning thresholds */ + TooHigh = (pSen->SenValue > pSen->SenThreWarnHigh) ; + TooLow = (pSen->SenValue < pSen->SenThreWarnLow) ; + + + if (!IsError && (TooHigh || TooLow)) { + /* Error condition is satiesfied */ + DoTrapSend = SK_TRUE; + DoErrLog = SK_TRUE; + + if (pSen->SenErrFlag == SK_SEN_ERR_WARN) { + /* This state is the former one */ + + /* So check first whether we have to send a trap */ + if (pSen->SenLastWarnTrapTS + SK_SEN_WARN_TR_HOLD > + CurrTime) { + /* + * Do NOT send the Trap. The hold back time + * has to run out first. + */ + DoTrapSend = SK_FALSE; + } + + /* Check now whether we have to log an Error */ + if (pSen->SenLastWarnLogTS + SK_SEN_WARN_LOG_HOLD > + CurrTime) { + /* + * Do NOT log the error. The hold back time + * has to run out first. + */ + DoErrLog = SK_FALSE; + } + } else { + /* We came from a different state */ + /* -> Set Begin Time Stamp */ + pSen->SenBegWarnTS = CurrTime; + pSen->SenErrFlag = SK_SEN_ERR_WARN ; + } + + if (DoTrapSend) { + /* Set current Time */ + pSen->SenLastWarnTrapTS = CurrTime; + pSen->SenWarnCts ++; + + /* Queue PNMI Event */ + SkEventQueue(pAC, SKGE_PNMI, (TooHigh ? + SK_PNMI_EVT_SEN_WAR_UPP : + SK_PNMI_EVT_SEN_WAR_LOW), + ParaLocal) ; + } + + if (DoErrLog) { + /* Set current Time */ + pSen->SenLastWarnLogTS = CurrTime; + + if (pSen->SenType == SK_SEN_TEMP) { + SK_ERR_LOG(pAC, SK_ERRCL_HW, SKERR_I2C_E009, + SKERR_I2C_E009MSG); + } else if (pSen->SenType == SK_SEN_VOLT) { + SK_ERR_LOG(pAC, SK_ERRCL_HW, SKERR_I2C_E009, + SKERR_I2C_E009MSG); + } else + { + SK_ERR_LOG(pAC, SK_ERRCL_HW, SKERR_I2C_E014, + SKERR_I2C_E014MSG); + } + } + } + + /* Check for NO error at all */ + if (!IsError && !TooHigh && !TooLow) { + /* Set o.k. Status if no error and no warning condition */ + pSen->SenErrFlag = SK_SEN_ERR_OK ; + } + + /* End of check against the thresholds */ + + /* + * Check initialization state: + * The VIO Thresholds need adaption + */ + if (!pSen->SenInit && pSen->SenReg == LM80_VT1_IN && + pSen->SenValue > SK_SEN_WARNLOW2C && + pSen->SenValue < SK_SEN_WARNHIGH2) { + pSen->SenThreErrLow = SK_SEN_ERRLOW2C; + pSen->SenThreWarnLow = SK_SEN_WARNLOW2C; + pSen->SenInit = SK_TRUE; + } + + if (!pSen->SenInit && pSen->SenReg == LM80_VT1_IN && + pSen->SenValue > SK_SEN_WARNLOW2 && + pSen->SenValue < SK_SEN_WARNHIGH2C) { + pSen->SenThreErrHigh = SK_SEN_ERRHIGH2C; + pSen->SenThreWarnHigh = SK_SEN_WARNHIGH2C; + pSen->SenInit = SK_TRUE; + } + + if (!pSen->SenInit) { + SK_ERR_LOG(pAC, SK_ERRCL_HW, SKERR_I2C_E013, SKERR_I2C_E013MSG); + } +} + +/* + * The only Event to be served is the timeout event + * + */ +int SkI2cEvent( +SK_AC *pAC, /* Adapters context */ +SK_IOC IoC, /* Io Context */ +SK_U32 Event, /* Module specific Event */ +SK_EVPARA Para) /* Event specific Parameter */ +{ + int ReadComplete; + SK_SENSOR *pSen; + SK_U32 Time; + SK_EVPARA ParaLocal; + int i; + + switch (Event) { + case SK_I2CEV_IRQ: + case SK_I2CEV_TIM: + pSen = &pAC->I2c.SenTable[pAC->I2c.CurrSens]; + ReadComplete = SkI2cReadSensor(pAC, IoC, pSen); + + if (ReadComplete) { + /* Check sensor against defined thresholds */ + SkI2cCheckSensor (pAC, pSen) ; + + /* Increment Current and set appropriate Timeout */ + Time = SK_I2C_TIM_SHORT; + + pAC->I2c.CurrSens ++; + if (pAC->I2c.CurrSens == pAC->I2c.MaxSens) { + pAC->I2c.CurrSens = 0; + Time = SK_I2C_TIM_LONG; + } + + /* Start Timer */ + ParaLocal.Para64 = (SK_U64) 0; + SkTimerStart(pAC, IoC, &pAC->I2c.SenTimer, Time, + SKGE_I2C, SK_I2CEV_TIM, ParaLocal) ; + } + break; + case SK_I2CEV_CLEAR: + for (i=0; i < SK_MAX_SENSORS; i ++) { + pAC->I2c.SenTable[i].SenErrFlag = SK_SEN_ERR_OK; + pAC->I2c.SenTable[i].SenErrCts = 0; + pAC->I2c.SenTable[i].SenWarnCts = 0; + pAC->I2c.SenTable[i].SenBegErrTS = 0; + pAC->I2c.SenTable[i].SenBegWarnTS = 0; + pAC->I2c.SenTable[i].SenLastErrTrapTS = (SK_U64) 0; + pAC->I2c.SenTable[i].SenLastErrLogTS = (SK_U64) 0; + pAC->I2c.SenTable[i].SenLastWarnTrapTS = (SK_U64) 0; + pAC->I2c.SenTable[i].SenLastWarnLogTS = (SK_U64) 0; + } + break; + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW, SKERR_I2C_E006, SKERR_I2C_E006MSG); + } + + return(0); +} +#endif /* !SK_DIAG */ +/* End of File */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/sklm80.c linux/drivers/net/sk98lin/sklm80.c --- v2.2.13/linux/drivers/net/sk98lin/sklm80.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/sklm80.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,270 @@ +/****************************************************************************** + * + * Name: sklm80.c + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.16 $ + * Date: $Date: 1999/05/27 14:05:47 $ + * Purpose: Funktions to access Voltage and Temperature Sensor (LM80) + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skge.c" for further information. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: sklm80.c,v $ + * Revision 1.16 1999/05/27 14:05:47 malthoff + * Fans: Set SenVal to 0 if the fan value is 0 or 0xff. Both values + * are outside the limits (0: div zero error, 0xff: value not in + * range, assume 0). + * + * Revision 1.15 1999/05/27 13:38:51 malthoff + * Pervent from Division by zero errors. + * + * Revision 1.14 1999/05/20 09:20:01 cgoos + * Changes for 1000Base-T (Fan sensors). + * + * Revision 1.13 1998/10/22 09:48:14 gklug + * fix: SysKonnectFileId typo + * + * Revision 1.12 1998/10/09 06:12:06 malthoff + * Remove ID_sccs by SysKonnectFileId. + * + * Revision 1.11 1998/09/04 08:33:48 malthoff + * bug fix: SenState = SK_SEN_IDLE when + * leaving SK_SEN_VALEXT state + * + * Revision 1.10 1998/08/20 12:02:10 gklug + * fix: compiler warnings type mismatch + * + * Revision 1.9 1998/08/20 11:37:38 gklug + * chg: change Ioc to IoC + * + * Revision 1.8 1998/08/19 12:20:58 gklug + * fix: remove struct from C files (see CCC) + * + * Revision 1.7 1998/08/17 07:04:57 malthoff + * Take SkLm80RcvReg() function from ski2c.c. + * Add IoC parameter to BREAK_OR_WAIT() macro. + * + * Revision 1.6 1998/08/14 07:11:28 malthoff + * remove pAc with pAC. + * + * Revision 1.5 1998/08/14 06:46:55 gklug + * fix: temperature can get negative + * + * Revision 1.4 1998/08/13 08:27:04 gklug + * add: temperature reading now o.k. + * fix: pSen declaration, SK_ERR_LOG call, ADDR macro + * + * Revision 1.3 1998/08/13 07:28:21 gklug + * fix: pSen was wrong initialized + * add: correct conversion for voltage readings + * + * Revision 1.2 1998/08/11 07:52:14 gklug + * add: Lm80 read sensor function + * + * Revision 1.1 1998/07/17 09:57:12 gklug + * initial version + * + * + * + ******************************************************************************/ + + +/* + LM80 functions +*/ +static const char SysKonnectFileId[] = + "$Id: sklm80.c,v 1.16 1999/05/27 14:05:47 malthoff Exp $" ; + +#include "h/skdrv1st.h" /* Driver Specific Definitions */ +#include "h/lm80.h" +#include "h/skdrv2nd.h" /* Adapter Control- and Driver specific Def. */ + +#ifdef SK_DIAG +#define BREAK_OR_WAIT(pAC,IoC,Event) SkI2cWait(pAC,IoC,Event) +#else /* nSK_DIAG */ +#define BREAK_OR_WAIT(pAC,IoC,Event) break +#endif /* nSK_DIAG */ + +#ifdef SK_DIAG +/* + * read the regeister 'reg' from the device 'device' + * + * return read error -1 + * success the read value + */ +int SkLm80RcvReg( +SK_IOC IoC, /* Adapter Context */ +int Dev, /* I2C device address */ +int Reg) /* register to read */ +{ + int Val = 0; + int TempExt; + + /* Signal device number */ + if (SkI2cSndDev(IoC, Dev, I2C_WRITE)) { + return(-1); + } + + if (SkI2cSndByte(IoC, Reg)) { + return(-1); + } + + /* repeat start */ + if (SkI2cSndDev(IoC, Dev, I2C_READ)) { + return(-1); + } + + switch(Reg) { + case LM80_TEMP_IN: + Val = (int)SkI2cRcvByte(IoC, 1) ; + + /* First: correct the value: it might be negative */ + if ((Val & 0x80) != 0) { + /* Value is negative */ + Val = Val - 256; + } + Val = Val * SK_LM80_TEMP_LSB; + SkI2cStop(IoC); + TempExt = (int) SkLm80RcvReg(IoC,LM80_ADDR,LM80_TEMP_CTRL); + if (Val > 0) { + Val += ((TempExt >> 7) * SK_LM80_TEMPEXT_LSB); + } + else { + Val -= ((TempExt >> 7) * SK_LM80_TEMPEXT_LSB); + } + return(Val); + break; + case LM80_VT0_IN: + case LM80_VT1_IN: + case LM80_VT2_IN: + case LM80_VT3_IN: + Val = (int) SkI2cRcvByte(IoC, 1) * SK_LM80_VT_LSB; + break; + default: + Val = (int) SkI2cRcvByte(IoC, 1); + break; + } + + SkI2cStop(IoC); + return(Val); +} +#endif /* SK_DIAG */ + +/* + * read a sensors value (LM80 specific) + * + * This function reads a sensors value from the I2c sensor chip LM80. The + * sensor is defined by its index into the sensors database in the struct + * pAC points to. + * + * Returns 1 if the read is completed + * 0 if the read must be continued (I2c Bus still allocated) + */ +int SkLm80ReadSensor( +SK_AC *pAC, /* Adapter Context */ +SK_IOC IoC, /* IoContext needed in level 1 and 2 */ +SK_SENSOR *pSen) /* Sensor to be read */ +{ + SK_I32 Value; + + switch(pSen->SenState) { + case SK_SEN_IDLE: + /* Send address to ADDR register */ + SK_I2C_CTL(IoC,I2C_READ,pSen->SenDev,pSen->SenReg,0); + + pSen->SenState = SK_SEN_VALUE ; + BREAK_OR_WAIT(pAC, IoC, I2C_READ) ; + case SK_SEN_VALUE: + /* Read value from data register */ + SK_IN32(IoC,B2_I2C_DATA, ((SK_U32 *)&Value)); + Value &= 0xff; /* only least significant byte is valid */ + + /* Do NOT check the Value against the thresholds */ + /* Checking is done in the calling instance */ + + if (pSen->SenType == SK_SEN_VOLT) { + /* Voltage sensor */ + pSen->SenValue = Value * SK_LM80_VT_LSB; + pSen->SenState = SK_SEN_IDLE ; + return(1); + } + + if (pSen->SenType == SK_SEN_FAN) { + if (Value != 0 && Value != 0xff) { + /* Fan speed counter */ + pSen->SenValue = SK_LM80_FAN_FAKTOR/Value; + } + else { + /* Indicate Fan error */ + pSen->SenValue = 0; + } + pSen->SenState = SK_SEN_IDLE ; + return(1); + } + + /* First: correct the value: it might be negative */ + if ((Value & 0x80) != 0) { + /* Value is negative */ + Value = Value - 256; + } + + /* We have a temperature sensor and need to get the signed + * extension. For now we get the extension from the last + * reading, so in the normal case we won't see flickering + * temperatures. + */ + pSen->SenValue = (Value * SK_LM80_TEMP_LSB) + + (pSen->SenValue % SK_LM80_TEMP_LSB); + + /* Send address to ADDR register */ + SK_I2C_CTL(IoC, I2C_READ, pSen->SenDev, LM80_TEMP_CTRL, 0); + + pSen->SenState = SK_SEN_VALEXT ; + BREAK_OR_WAIT(pAC, IoC, I2C_READ) ; + case SK_SEN_VALEXT: + /* Read value from data register */ + SK_IN32(IoC,B2_I2C_DATA,((SK_U32 *)&Value)); + Value &= LM80_TEMP_LSB_9; /* only bit 7 is valid */ + + /* cut the LSB bit */ + pSen->SenValue = ((pSen->SenValue / SK_LM80_TEMP_LSB) * + SK_LM80_TEMP_LSB) ; + + if (pSen->SenValue < 0) { + /* Value negative: The bit value must be subtracted */ + pSen->SenValue -= ((Value >> 7) * SK_LM80_TEMPEXT_LSB); + } else { + /* Value positive: The bit value must be added */ + pSen->SenValue += ((Value >> 7) * SK_LM80_TEMPEXT_LSB); + } + + pSen->SenState = SK_SEN_IDLE ; + return(1); + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW, SKERR_I2C_E007, + SKERR_I2C_E007MSG) ; + return(1); + } + + /* Not completed */ + return(0) ; +} diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/skqueue.c linux/drivers/net/sk98lin/skqueue.c --- v2.2.13/linux/drivers/net/sk98lin/skqueue.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/skqueue.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,204 @@ +/****************************************************************************** + * + * Name: skqueue.c + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.14 $ + * Date: $Date: 1998/10/15 15:11:35 $ + * Purpose: Management of an event queue. + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skge.c" for further information. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: skqueue.c,v $ + * Revision 1.14 1998/10/15 15:11:35 gklug + * fix: ID_sccs to SysKonnectFileId + * + * Revision 1.13 1998/09/08 08:47:52 gklug + * add: init level handling + * + * Revision 1.12 1998/09/08 07:43:20 gklug + * fix: Sirq Event function name + * + * Revision 1.11 1998/09/08 05:54:34 gklug + * chg: define SK_CSUM is replaced by SK_USE_CSUM + * + * Revision 1.10 1998/09/03 14:14:49 gklug + * add: CSUM and HWAC Eventclass and function. + * + * Revision 1.9 1998/08/19 09:50:50 gklug + * fix: remove struct keyword from c-code (see CCC) add typedefs + * + * Revision 1.8 1998/08/17 13:43:11 gklug + * chg: Parameter will be union of 64bit para, 2 times SK_U32 or SK_PTR + * + * Revision 1.7 1998/08/14 07:09:11 gklug + * fix: chg pAc -> pAC + * + * Revision 1.6 1998/08/11 12:13:14 gklug + * add: return code feature of Event service routines + * add: correct Error log calls + * + * Revision 1.5 1998/08/07 12:53:45 gklug + * fix: first compiled version + * + * Revision 1.4 1998/08/07 09:20:48 gklug + * adapt functions to C coding conventions. + * + * Revision 1.3 1998/08/05 11:29:32 gklug + * rmv: Timer event entry. Timer will queue event directly + * + * Revision 1.2 1998/07/31 11:22:40 gklug + * Initial version + * + * Revision 1.1 1998/07/30 15:14:01 gklug + * Initial version. Adapted from SMT + * + * + * + ******************************************************************************/ + + +/* + Event queue and dispatcher +*/ +static const char SysKonnectFileId[] = + "$Header: /usr56/projects/ge/schedule/skqueue.c,v 1.14 1998/10/15 15:11:35 gklug Exp $" ; + +#include "h/skdrv1st.h" /* Driver Specific Definitions */ +#include "h/skqueue.h" /* Queue Definitions */ +#include "h/skdrv2nd.h" /* Adapter Control- and Driver specific Def. */ + +#ifdef __C2MAN__ +/* + Event queue management. + + General Description: + + */ +intro() +{} +#endif + +#define PRINTF(a,b,c) + +/* + * init event queue management + * + * Must be called during init level 0. + */ +void SkEventInit( +SK_AC *pAC, /* Adapter context */ +SK_IOC Ioc, /* IO context */ +int Level) /* Init level */ +{ + switch (Level) { + case SK_INIT_DATA: + pAC->Event.EvPut = pAC->Event.EvGet = pAC->Event.EvQueue ; + break; + default: + break; + } +} + +/* + * add event to queue + */ +void SkEventQueue( +SK_AC *pAC, /* Adapters context */ +SK_U32 Class, /* Event Class */ +SK_U32 Event, /* Event to be queued */ +SK_EVPARA Para) /* Event parameter */ +{ + pAC->Event.EvPut->Class = Class ; + pAC->Event.EvPut->Event = Event ; + pAC->Event.EvPut->Para = Para ; + if (++pAC->Event.EvPut == &pAC->Event.EvQueue[SK_MAX_EVENT]) + pAC->Event.EvPut = pAC->Event.EvQueue ; + + if (pAC->Event.EvPut == pAC->Event.EvGet) { + SK_ERR_LOG(pAC, SK_ERRCL_NORES, SKERR_Q_E001, SKERR_Q_E001MSG) ; + } +} + +/* + * event dispatcher + * while event queue is not empty + * get event from queue + * send command to state machine + * end + * return error reported by individual Event function + * 0 if no error occured. + */ +int SkEventDispatcher( +SK_AC *pAC, /* Adapters Context */ +SK_IOC Ioc) /* Io context */ +{ + SK_EVENTELEM *pEv ; /* pointer into queue */ + SK_U32 Class ; + int Rtv ; + + pEv = pAC->Event.EvGet ; + PRINTF("dispatch get %x put %x\n",pEv,pAC->Event.ev_put) ; + while (pEv != pAC->Event.EvPut) { + PRINTF("dispatch Class %d Event %d\n",pEv->Class,pEv->Event) ; + switch(Class = pEv->Class) { + case SKGE_DRV : /* Driver Event */ + Rtv = SkDrvEvent(pAC,Ioc,pEv->Event,pEv->Para); + break ; + case SKGE_RLMT : /* RLMT Event */ + Rtv = SkRlmtEvent(pAC,Ioc,pEv->Event,pEv->Para); + break ; + case SKGE_I2C : /* I2C Event */ + Rtv = SkI2cEvent(pAC,Ioc,pEv->Event,pEv->Para); + break ; + case SKGE_PNMI : + Rtv = SkPnmiEvent(pAC,Ioc,pEv->Event,pEv->Para); + break ; + case SKGE_HWAC : + Rtv = SkGeSirqEvent(pAC,Ioc,pEv->Event,pEv->Para); + break ; +#ifdef SK_USE_CSUM + case SKGE_CSUM : + Rtv = SkCsEvent(pAC,Ioc,pEv->Event,pEv->Para); + break ; +#endif /* SK_USE_CSUM */ + default : + SK_ERR_LOG(pAC, SK_ERRCL_SW, SKERR_Q_E002, + SKERR_Q_E002MSG) ; + Rtv = 0; + } + + if (Rtv != 0) { + return(Rtv) ; + } + + if (++pEv == &pAC->Event.EvQueue[SK_MAX_EVENT]) + pEv = pAC->Event.EvQueue ; + + /* Renew get: it is used in queue_events to detect overruns */ + pAC->Event.EvGet = pEv; + } + + return(0) ; +} + +/* End of file */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/skrlmt.c linux/drivers/net/sk98lin/skrlmt.c --- v2.2.13/linux/drivers/net/sk98lin/skrlmt.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/skrlmt.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,3423 @@ +/****************************************************************************** + * + * Name: skrlmt.c + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.48 $ + * Date: $Date: 1999/10/04 14:01:17 $ + * Purpose: Manage links on SK-NET Adapters, esp. redundant ones. + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skge.c" for further information. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: skrlmt.c,v $ + * Revision 1.48 1999/10/04 14:01:17 rassmann + * Corrected reaction to reception of BPDU frames. + * Added parameter descriptions to "For Readme" section skrlmt.txt. + * Clarified usage of lookahead result *pForRlmt. + * Requested driver to present RLMT packets as soon as poosible. + * + * Revision 1.47 1999/07/20 12:53:36 rassmann + * Fixed documentation errors for lookahead macros. + * + * Revision 1.46 1999/05/28 13:29:16 rassmann + * Replaced C++-style comment. + * + * Revision 1.45 1999/05/28 13:28:08 rassmann + * Corrected syntax error (xxx). + * + * Revision 1.44 1999/05/28 11:15:54 rassmann + * Changed behaviour to reflect Design Spec v1.2. + * Controlling Link LED(s). + * Introduced RLMT Packet Version field in RLMT Packet. + * Newstyle lookahead macros (checking meta-information before looking at + * the packet). + * + * Revision 1.43 1999/01/28 13:12:43 rassmann + * Corrected Lookahead (bug introduced in previous Rev.). + * + * Revision 1.42 1999/01/28 12:50:41 rassmann + * Not using broadcast time stamps in CheckLinkState mode. + * + * Revision 1.41 1999/01/27 14:13:02 rassmann + * Monitoring broadcast traffic. + * Switching more reliably and not too early if switch is + * configured for spanning tree. + * + * Revision 1.40 1999/01/22 13:17:30 rassmann + * Informing PNMI of NET_UP. + * Clearing RLMT multicast addresses before first setting them. + * Reporting segmentation earlier, setting a "quiet time" + * after a report. + * + * Revision 1.39 1998/12/10 15:29:53 rassmann + * Corrected SuspectStatus in SkRlmtBuildCheckChain(). + * Corrected CHECK_SEG mode. + * + * Revision 1.38 1998/12/08 13:11:23 rassmann + * Stopping SegTimer at RlmtStop. + * + * Revision 1.37 1998/12/07 16:51:42 rassmann + * Corrected comments. + * + * Revision 1.36 1998/12/04 10:58:56 rassmann + * Setting next pointer to NULL when receiving. + * + * Revision 1.35 1998/12/03 16:12:42 rassmann + * Ignoring/correcting illegal PrefPort values. + * + * Revision 1.34 1998/12/01 11:45:35 rassmann + * Code cleanup. + * + * Revision 1.33 1998/12/01 10:29:32 rassmann + * Starting standby ports before getting the net up. + * Checking if a port is started when the link comes up. + * + * Revision 1.32 1998/11/30 16:19:50 rassmann + * New default for PortNoRx. + * + * Revision 1.31 1998/11/27 19:17:13 rassmann + * Corrected handling of LINK_DOWN coming shortly after LINK_UP. + * + * Revision 1.30 1998/11/24 12:37:31 rassmann + * Implemented segmentation check. + * + * Revision 1.29 1998/11/18 13:04:32 rassmann + * Secured PortUpTimer event. + * Waiting longer before starting standby port(s). + * + * Revision 1.28 1998/11/17 13:43:04 rassmann + * Handling (logical) tx failure. + * Sending packet on logical address after PORT_SWITCH. + * + * Revision 1.27 1998/11/13 17:09:50 rassmann + * Secured some events against being called in wrong state. + * + * Revision 1.26 1998/11/13 16:56:54 rassmann + * Added macro version of SkRlmtLookaheadPacket. + * + * Revision 1.25 1998/11/06 18:06:04 rassmann + * Corrected timing when RLMT checks fail. + * Clearing tx counter earlier in periodical checks. + * + * Revision 1.24 1998/11/05 10:37:27 rassmann + * Checking destination address in Lookahead. + * + * Revision 1.23 1998/11/03 13:53:49 rassmann + * RLMT should switch now (at least in mode 3). + * + * Revision 1.22 1998/10/29 14:34:49 rassmann + * Clearing SK_RLMT struct at startup. + * Initializing PortsUp during SK_RLMT_START. + * + * Revision 1.21 1998/10/28 11:30:17 rassmann + * Default mode is now SK_RLMT_CHECK_LOC_LINK. + * + * Revision 1.20 1998/10/26 16:02:03 rassmann + * Ignoring LINK_DOWN for links that are down. + * + * Revision 1.19 1998/10/22 15:54:01 rassmann + * Corrected EtherLen. + * Starting Link Check when second port comes up. + * + * Revision 1.18 1998/10/22 11:39:50 rassmann + * Corrected signed/unsigned mismatches. + * Corrected receive list handling and address recognition. + * + * Revision 1.17 1998/10/19 17:01:20 rassmann + * More detailed checking of received packets. + * + * Revision 1.16 1998/10/15 15:16:34 rassmann + * Finished Spanning Tree checking. + * Checked with lint. + * + * Revision 1.15 1998/09/24 19:16:07 rassmann + * Code cleanup. + * Introduced Timer for PORT_DOWN due to no RX. + * + * Revision 1.14 1998/09/18 20:27:14 rassmann + * Added address override. + * + * Revision 1.13 1998/09/16 11:31:48 rassmann + * Including skdrv1st.h again. :( + * + * Revision 1.12 1998/09/16 11:09:50 rassmann + * Syntax corrections. + * + * Revision 1.11 1998/09/15 12:32:03 rassmann + * Syntax correction. + * + * Revision 1.10 1998/09/15 11:28:49 rassmann + * Syntax corrections. + * + * Revision 1.9 1998/09/14 17:07:37 rassmann + * Added code for port checking via LAN. + * Changed Mbuf definition. + * + * Revision 1.8 1998/09/07 11:14:14 rassmann + * Syntax corrections. + * + * Revision 1.7 1998/09/07 09:06:07 rassmann + * Syntax corrections. + * + * Revision 1.6 1998/09/04 19:41:33 rassmann + * Syntax corrections. + * Started entering code for checking local links. + * + * Revision 1.5 1998/09/04 12:14:27 rassmann + * Interface cleanup. + * + * Revision 1.4 1998/09/02 16:55:28 rassmann + * Updated to reflect new DRV/HWAC/RLMT interface. + * + * Revision 1.3 1998/08/27 14:29:03 rassmann + * Code cleanup. + * + * Revision 1.2 1998/08/27 14:26:24 rassmann + * Updated interface. + * + * Revision 1.1 1998/08/21 08:26:49 rassmann + * First public version. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Description: + * + * This module contains code for Link ManagemenT (LMT) of SK-NET Adapters. + * It is mainly intended for adapters with more than one link. + * For such adapters, this module realizes Redundant Link ManagemenT (RLMT). + * + * Include File Hierarchy: + * + * "skdrv1st.h" + * "skdrv2nd.h" + * + ******************************************************************************/ + +#ifndef lint +static const char SysKonnectFileId[] = + "@(#) $Id: skrlmt.c,v 1.48 1999/10/04 14:01:17 rassmann Exp $ (C) SysKonnect."; +#endif /* !defined(lint) */ + +#define __SKRLMT_C + +#ifdef __cplusplus +xxxx /* not supported yet - force error */ +extern "C" { +#endif /* cplusplus */ + +#include "h/skdrv1st.h" +#include "h/skdrv2nd.h" + +/* defines ********************************************************************/ + +#ifndef SK_HWAC_LINK_LED +#define SK_HWAC_LINK_LED(a,b,c,d) +#endif /* !defined(SK_HWAC_LINK_LED) */ + +#ifndef DEBUG +#define RLMT_STATIC static +#else /* DEBUG */ +#define RLMT_STATIC + +#ifndef SK_LITTLE_ENDIAN +/* First 32 bits */ +#define OFFS_LO32 1 +/* Second 32 bits */ +#define OFFS_HI32 0 +#else /* SK_LITTLE_ENDIAN */ +/* First 32 bits */ +#define OFFS_LO32 0 +/* Second 32 bits */ +#define OFFS_HI32 1 +#endif /* SK_LITTLE_ENDIAN */ + +#endif /* DEBUG */ + +/* ----- Private timeout values ----- */ + +#define SK_RLMT_MIN_TO_VAL 125000 /* 1/8 sec. */ +#define SK_RLMT_DEF_TO_VAL 1000000 /* 1 sec. */ +#define SK_RLMT_PORTDOWN_TIM_VAL 900000 /* another 0.9 sec. */ +#define SK_RLMT_PORTSTART_TIM_VAL 100000 /* 0.1 sec. */ +#define SK_RLMT_PORTUP_TIM_VAL 2500000 /* 2.5 sec. */ +#define SK_RLMT_SEG_TO_VAL 900000000 /* 15 min. */ + +/* + * Amount that a time stamp must be later to be recognized as "substantially + * later". This is about 1/128 sec. + */ + +#define SK_RLMT_BC_DELTA ((SK_TICKS_PER_SEC >> 7) + 1) + +/* ----- Private RLMT defaults ----- */ + +#define SK_RLMT_DEF_PREF_PORT 0 /* "Lower" port. */ +#define SK_RLMT_DEF_MODE SK_RLMT_CHECK_LINK /* Default RLMT Mode. */ + +/* ----- Private RLMT checking states ----- */ + +#define SK_RLMT_RCS_SEG 1 /* RLMT Check State: check seg. */ +#define SK_RLMT_RCS_START_SEG 2 /* RLMT Check State: start check seg. */ +#define SK_RLMT_RCS_SEND_SEG 4 /* RLMT Check State: send BPDU packet */ +#define SK_RLMT_RCS_REPORT_SEG 8 /* RLMT Check State: report seg. */ + +/* ----- Private PORT checking states ----- */ + +#define SK_RLMT_PCS_TX 1 /* Port Check State: check tx. */ +#define SK_RLMT_PCS_RX 2 /* Port Check State: check rx. */ + +/* ----- Private PORT events ----- */ + +/* Note: Update simulation when changing these. */ + +#define SK_RLMT_PORTSTART_TIM 1100 /* Port start timeout. */ +#define SK_RLMT_PORTUP_TIM 1101 /* Port can now go up. */ +#define SK_RLMT_PORTDOWN_RX_TIM 1102 /* Port did not receive once ... */ +#define SK_RLMT_PORTDOWN 1103 /* Port went down. */ +#define SK_RLMT_PORTDOWN_TX_TIM 1104 /* Partner did not receive ... */ + +/* ----- Private RLMT events ----- */ + +/* Note: Update simulation when changing these. */ + +#define SK_RLMT_TIM 2100 /* RLMT timeout. */ +#define SK_RLMT_SEG_TIM 2101 /* RLMT segmentation check timeout. */ + +#define TO_SHORTEN(tim) ((tim) / 2) + +/* Error numbers and messages. */ + +#define SKERR_RLMT_E001 (SK_ERRBASE_RLMT + 0) +#define SKERR_RLMT_E001_MSG "No Packet." +#define SKERR_RLMT_E002 (SKERR_RLMT_E001 + 1) +#define SKERR_RLMT_E002_MSG "Short Packet." +#define SKERR_RLMT_E003 (SKERR_RLMT_E002 + 1) +#define SKERR_RLMT_E003_MSG "Unknown RLMT event." +#define SKERR_RLMT_E004 (SKERR_RLMT_E003 + 1) +#define SKERR_RLMT_E004_MSG "PortsUp incorrect." +#define SKERR_RLMT_E005 (SKERR_RLMT_E004 + 1) +#define SKERR_RLMT_E005_MSG \ + "Net seems to be segmented (different root bridges are reported on the ports)." +#define SKERR_RLMT_E006 (SKERR_RLMT_E005 + 1) +#define SKERR_RLMT_E006_MSG "Duplicate MAC Address detected." +#define SKERR_RLMT_E007 (SKERR_RLMT_E006 + 1) +#define SKERR_RLMT_E007_MSG "LinksUp incorrect." +#define SKERR_RLMT_E008 (SKERR_RLMT_E007 + 1) +#define SKERR_RLMT_E008_MSG "Port not started but link came up." +#define SKERR_RLMT_E009 (SKERR_RLMT_E008 + 1) +#define SKERR_RLMT_E009_MSG "Corrected illegal setting of Preferred Port." +#define SKERR_RLMT_E010 (SKERR_RLMT_E009 + 1) +#define SKERR_RLMT_E010_MSG "Ignored illegal Preferred Port." + +/* LLC field values. */ + +#define LLC_COMMAND_RESPONSE_BIT 1 +#define LLC_TEST_COMMAND 0xE3 +#define LLC_UI 0x03 + +/* RLMT Packet fields. */ + +#define SK_RLMT_DSAP 0 +#define SK_RLMT_SSAP 0 +#define SK_RLMT_CTRL (LLC_TEST_COMMAND) +#define SK_RLMT_INDICATOR0 0x53 /* S */ +#define SK_RLMT_INDICATOR1 0x4B /* K */ +#define SK_RLMT_INDICATOR2 0x2D /* - */ +#define SK_RLMT_INDICATOR3 0x52 /* R */ +#define SK_RLMT_INDICATOR4 0x4C /* L */ +#define SK_RLMT_INDICATOR5 0x4D /* M */ +#define SK_RLMT_INDICATOR6 0x54 /* T */ +#define SK_RLMT_PACKET_VERSION 0 + +/* RLMT SPT Flag values. */ + +#define SK_RLMT_SPT_FLAG_CHANGE 0x01 +#define SK_RLMT_SPT_FLAG_CHANGE_ACK 0x80 + +/* RLMT SPT Packet fields. */ + +#define SK_RLMT_SPT_DSAP 0x42 +#define SK_RLMT_SPT_SSAP 0x42 +#define SK_RLMT_SPT_CTRL (LLC_UI) +#define SK_RLMT_SPT_PROTOCOL_ID0 0x00 +#define SK_RLMT_SPT_PROTOCOL_ID1 0x00 +#define SK_RLMT_SPT_PROTOCOL_VERSION_ID 0x00 +#define SK_RLMT_SPT_BPDU_TYPE 0x00 +#define SK_RLMT_SPT_FLAGS 0x00 /* ?? */ +#define SK_RLMT_SPT_ROOT_ID0 0xFF /* Lowest possible priority. */ +#define SK_RLMT_SPT_ROOT_ID1 0xFF /* Lowest possible priority. */ + +/* Remaining 6 bytes will be the current port address. */ + +#define SK_RLMT_SPT_ROOT_PATH_COST0 0x00 +#define SK_RLMT_SPT_ROOT_PATH_COST1 0x00 +#define SK_RLMT_SPT_ROOT_PATH_COST2 0x00 +#define SK_RLMT_SPT_ROOT_PATH_COST3 0x00 +#define SK_RLMT_SPT_BRIDGE_ID0 0xFF /* Lowest possible priority. */ +#define SK_RLMT_SPT_BRIDGE_ID1 0xFF /* Lowest possible priority. */ + +/* Remaining 6 bytes will be the current port address. */ + +#define SK_RLMT_SPT_PORT_ID0 0xFF /* Lowest possible priority. */ +#define SK_RLMT_SPT_PORT_ID1 0xFF /* Lowest possible priority. */ +#define SK_RLMT_SPT_MSG_AGE0 0x00 +#define SK_RLMT_SPT_MSG_AGE1 0x00 +#define SK_RLMT_SPT_MAX_AGE0 0x00 +#define SK_RLMT_SPT_MAX_AGE1 0xFF +#define SK_RLMT_SPT_HELLO_TIME0 0x00 +#define SK_RLMT_SPT_HELLO_TIME1 0xFF +#define SK_RLMT_SPT_FWD_DELAY0 0x00 +#define SK_RLMT_SPT_FWD_DELAY1 0x40 + +/* Size defines. */ + +#define SK_RLMT_MIN_PACKET_SIZE 34 +#define SK_RLMT_MAX_PACKET_SIZE (SK_RLMT_MAX_TX_BUF_SIZE) +#define SK_PACKET_DATA_LEN (SK_RLMT_MAX_PACKET_SIZE - \ + SK_RLMT_MIN_PACKET_SIZE) + +/* ----- RLMT packet types ----- */ + +#define SK_PACKET_ANNOUNCE 1 /* Port announcement. */ +#define SK_PACKET_ALIVE 2 /* Alive packet to port. */ +#define SK_PACKET_ADDR_CHANGED 3 /* Port address changed. */ +#define SK_PACKET_CHECK_TX 4 /* Check your tx line. */ + +#ifdef SK_LITTLE_ENDIAN +#define SK_U16_TO_NETWORK_ORDER(Val,Addr) { \ + SK_U8 *_Addr = (SK_U8*)(Addr); \ + SK_U16 _Val = (SK_U16)(Val); \ + *_Addr++ = (SK_U8)(_Val >> 8); \ + *_Addr = (SK_U8)(_Val & 0xFF); \ +} +#endif /* SK_LITTLE_ENDIAN */ + +#ifdef SK_BIG_ENDIAN +#define SK_U16_TO_NETWORK_ORDER(Val,Addr) (*(SK_U16*)(Addr) = (SK_U16)(Val)) +#endif /* SK_BIG_ENDIAN */ + +#define AUTONEG_FAILED SK_FALSE +#define AUTONEG_SUCCESS SK_TRUE + + +/* typedefs *******************************************************************/ + +/* RLMT packet. Length: SK_RLMT_MAX_PACKET_SIZE (60) bytes. */ + +typedef struct s_RlmtPacket { + SK_U8 DstAddr[SK_MAC_ADDR_LEN]; + SK_U8 SrcAddr[SK_MAC_ADDR_LEN]; + SK_U8 TypeLen[2]; + SK_U8 DSap; + SK_U8 SSap; + SK_U8 Ctrl; + SK_U8 Indicator[7]; + SK_U8 RlmtPacketType[2]; + SK_U8 Align1[2]; + SK_U8 Random[4]; /* Random value of requesting(!) station. */ + SK_U8 RlmtPacketVersion[2]; /* RLMT Packet version */ + SK_U8 Data[SK_PACKET_DATA_LEN]; +} SK_RLMT_PACKET; + +typedef struct s_SpTreeRlmtPacket { + SK_U8 DstAddr[SK_MAC_ADDR_LEN]; + SK_U8 SrcAddr[SK_MAC_ADDR_LEN]; + SK_U8 TypeLen[2]; + SK_U8 DSap; + SK_U8 SSap; + SK_U8 Ctrl; + SK_U8 ProtocolId[2]; + SK_U8 ProtocolVersionId; + SK_U8 BpduType; + SK_U8 Flags; + SK_U8 RootId[8]; + SK_U8 RootPathCost[4]; + SK_U8 BridgeId[8]; + SK_U8 PortId[2]; + SK_U8 MessageAge[2]; + SK_U8 MaxAge[2]; + SK_U8 HelloTime[2]; + SK_U8 ForwardDelay[2]; +} SK_SPTREE_PACKET; + +/* global variables ***********************************************************/ + +SK_MAC_ADDR SkRlmtMcAddr = {{0x01, 0x00, 0x5A, 0x52, 0x4C, 0x4D}}; +SK_MAC_ADDR BridgeMcAddr = {{0x01, 0x80, 0xC2, 0x00, 0x00, 0x00}}; +SK_MAC_ADDR BcAddr = {{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; + +/* local variables ************************************************************/ + +/* None. */ + +/* functions ******************************************************************/ + +RLMT_STATIC void SkRlmtCheckSwitch( + SK_AC *pAC, + SK_IOC IoC); +RLMT_STATIC void SkRlmtCheckSeg( + SK_AC *pAC, + SK_IOC IoC); + +/****************************************************************************** + * + * SkRlmtInit - initialize data, set state to init + * + * Description: + * + * SK_INIT_DATA + * ============ + * + * This routine initializes all RLMT-related variables to a known state. + * The initial state is SK_RLMT_RS_INIT. + * All ports are initialized to SK_RLMT_PS_INIT. + * + * + * SK_INIT_IO + * ========== + * + * Nothing. + * + * + * SK_INIT_RUN + * =========== + * + * Determine the adapter's random value. + * Set the hw registers, the "logical adapter address", the + * RLMT multicast address, and eventually the BPDU multicast address. + * + * Context: + * init, pageable + * + * Returns: + * Nothing. + */ +void SkRlmtInit( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* I/O context */ +int Level) /* initialization level */ +{ + SK_U32 i, j; + SK_U64 Random; + SK_EVPARA Para; + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_INIT, + ("RLMT Init level %d.\n", Level)) + + switch (Level) { + case SK_INIT_DATA: /* Initialize data structures. */ + SK_MEMSET((char *)&pAC->Rlmt, 0, sizeof(SK_RLMT)); + + for (i = 0; i < SK_MAX_MACS; i++) { + pAC->Rlmt.Port[i].PortState = SK_RLMT_PS_INIT; + pAC->Rlmt.Port[i].LinkDown = SK_TRUE; + pAC->Rlmt.Port[i].PortDown = SK_TRUE; + pAC->Rlmt.Port[i].PortStarted = SK_FALSE; + pAC->Rlmt.Port[i].PortNoRx = SK_FALSE; + pAC->Rlmt.Port[i].RootIdSet = SK_FALSE; + } + + pAC->Rlmt.RlmtState = SK_RLMT_RS_INIT; + pAC->Rlmt.RootIdSet = SK_FALSE; + pAC->Rlmt.MacPreferred = 0xFFFFFFFF; /* Automatic. */ + pAC->Rlmt.PrefPort = SK_RLMT_DEF_PREF_PORT; + pAC->Rlmt.MacActive = pAC->Rlmt.PrefPort; /* Just assuming. */ + pAC->Rlmt.RlmtMode = SK_RLMT_DEF_MODE; + pAC->Rlmt.TimeoutValue = SK_RLMT_DEF_TO_VAL; + break; + + case SK_INIT_IO: /* GIMacsFound first available here. */ + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_INIT, + ("RLMT: %d MACs were detected.\n", + pAC->GIni.GIMacsFound)) + + /* Initialize HW registers? */ + + if (pAC->GIni.GIMacsFound < 2) { + Para.Para32[0] = SK_RLMT_CHECK_LINK; + (void)SkRlmtEvent(pAC, IoC, SK_RLMT_MODE_CHANGE, Para); + } + break; + + case SK_INIT_RUN: + for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + Random = SkOsGetTime(pAC); + *(SK_U32*)&pAC->Rlmt.Port[i].Random = *(SK_U32*)&Random; + + for (j = 0; j < 4; j++) { + pAC->Rlmt.Port[i].Random[j] ^= pAC->Addr.Port[i + ].CurrentMacAddress.a[SK_MAC_ADDR_LEN - + 1 - j]; + } + + (void)SkAddrMcClear( + pAC, + IoC, + i, + SK_ADDR_PERMANENT | SK_MC_SW_ONLY); + + /* Add RLMT MC address. */ + + (void)SkAddrMcAdd( + pAC, + IoC, + i, + &SkRlmtMcAddr, + SK_ADDR_PERMANENT); + + if (pAC->Rlmt.RlmtMode & SK_RLMT_CHECK_SEG) { + /* Add BPDU MC address. */ + + (void)SkAddrMcAdd( + pAC, + IoC, + i, + &BridgeMcAddr, + SK_ADDR_PERMANENT); + } + + (void)SkAddrMcUpdate(pAC, IoC, i); + } + break; + + default: /* error */ + break; + } + + return; + +} /* SkRlmtInit */ + + +/****************************************************************************** + * + * SkRlmtBuildCheckChain - build the check chain + * + * Description: + * This routine builds the local check chain: + * - Each port that is up checks the next port. + * - The last port that is up checks the first port that is up. + * + * Notes: + * - Currently only local ports are considered when building the chain. + * - Currently the SuspectState is just reset; + * it would be better to save it ... + * + * Context: + * runtime, pageable? + * + * Returns: + * Nothing + */ +RLMT_STATIC void SkRlmtBuildCheckChain( +SK_AC *pAC) /* adapter context */ +{ + SK_U32 i; + SK_U32 NumMacsUp; + SK_U32 FirstMacUp=0; + SK_U32 PrevMacUp=0; + + if (!(pAC->Rlmt.RlmtMode & SK_RLMT_CHECK_LOC_LINK)) { + for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + pAC->Rlmt.Port[i].PortsChecked = 0; + } + return; /* Nothing to build. */ + } + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SkRlmtBuildCheckChain.\n")) + + NumMacsUp = 0; + + for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + pAC->Rlmt.Port[i].PortsChecked = 0; + pAC->Rlmt.Port[i].PortsSuspect = 0; + pAC->Rlmt.Port[i].CheckingState &= + ~(SK_RLMT_PCS_RX | SK_RLMT_PCS_TX); + + /* + * If more than two links are detected we should consider + * checking at least two other ports: + * 1. the next port that is not LinkDown and + * 2. the next port that is not PortDown. + */ + + if (!pAC->Rlmt.Port[i].LinkDown) { + if (NumMacsUp == 0) { + FirstMacUp = i; + } + else { + pAC->Rlmt.Port[PrevMacUp].PortCheck[ + pAC->Rlmt.Port[ + PrevMacUp].PortsChecked].CheckAddr = + pAC->Addr.Port[i].CurrentMacAddress; + pAC->Rlmt.Port[PrevMacUp].PortCheck[ + pAC->Rlmt.Port[ + PrevMacUp].PortsChecked].SuspectTx = + SK_FALSE; + pAC->Rlmt.Port[PrevMacUp].PortsChecked++; + } + PrevMacUp = i; + NumMacsUp++; + } + } + + if (NumMacsUp > 1) { + pAC->Rlmt.Port[PrevMacUp].PortCheck[pAC->Rlmt.Port[ + PrevMacUp].PortsChecked].CheckAddr = + pAC->Addr.Port[FirstMacUp].CurrentMacAddress; + pAC->Rlmt.Port[PrevMacUp].PortCheck[pAC->Rlmt.Port[ + PrevMacUp].PortsChecked].SuspectTx = SK_FALSE; + pAC->Rlmt.Port[PrevMacUp].PortsChecked++; + } + +#ifdef DEBUG + for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("Port %d checks %d other ports: %2X.\n", + i, + pAC->Rlmt.Port[i].PortsChecked, + pAC->Rlmt.Port[i].PortCheck[0].CheckAddr.a[5])) + } +#endif /* DEBUG */ + + return; +} /* SkRlmtBuildCheckChain */ + +#ifdef SK_RLMT_SLOW_LOOKAHEAD + +/****************************************************************************** + * + * SkRlmtLookaheadPacket - examine received packet shortly, count s-th + * + * Description: + * This routine examines each received packet fast and short and + * increments some counters. + * The received packet has to be stored virtually contiguous. + * This function does the following: + * - Increment some counters. + * - Ensure length is sufficient. + * - Ensure that destination address is physical port address, + * RLMT multicast, or BPDU multicast address. + * + * Notes: + * This function is fully reentrant while the fast path is not blocked. + * + * Context: + * isr/dpr, not pageable + * + * Returns: + * SK_FALSE packet for upper layers + * SK_TRUE packet for RLMT + */ +SK_BOOL SkRlmtLookaheadPacket( +SK_AC *pAC, /* adapter context */ +SK_U32 PortIdx, /* receiving port */ +SK_U8 *pLaPacket, /* received packet's data */ +unsigned PacketLength, /* received packet's length */ /* Necessary? */ +unsigned LaLength) /* lookahead length */ +{ + int i; + SK_BOOL IsBc; /* Broadcast address? */ + int RxDest; /* Receive destination? */ + int Offset; /* Offset of data to present to LOOKAHEAD. */ + int NumBytes; /* #Bytes to present to LOOKAHEAD. */ + SK_RLMT_PACKET *pRPacket; + SK_ADDR_PORT *pAPort; + +#ifdef DEBUG + PacketLength = PacketLength; +#endif /* DEBUG */ + + pRPacket = (SK_RLMT_PACKET*)pLaPacket; + pAPort = &pAC->Addr.Port[PortIdx]; + +#ifdef DEBUG + if (pLaPacket == NULL) { + + /* Create error log entry. */ + + SK_ERR_LOG( + pAC, + SK_ERRCL_SW, + SKERR_RLMT_E001, + SKERR_RLMT_E001_MSG); + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_RX, + ("SkRlmtLookaheadPacket: NULL pointer.\n")) + + return (SK_FALSE); + } +#endif /* DEBUG */ + + /* Drivers should get IsBc from the descriptor. */ + + IsBc = SK_TRUE; + for (i = 0; IsBc && i < SK_MAC_ADDR_LEN; i++) { + IsBc = IsBc && (pLaPacket[i] == 0xFF); + } + + SK_RLMT_PRE_LOOKAHEAD( + pAC, + PortIdx, + PacketLength, + IsBc, + &Offset, + &NumBytes) + + if (NumBytes == 0) { + return (SK_FALSE); + } + + SK_RLMT_LOOKAHEAD( + pAC, + PortIdx, + &pLaPacket[Offset], + IsBc, + pLaPacket[0] & 1, /* Drivers: Get info from descriptor. */ + &RxDest) + + if (RxDest & SK_RLMT_RX_RLMT) { + return (SK_TRUE); + } + + return (SK_FALSE); +} /* SkRlmtLookaheadPacket */ + +#endif /* SK_RLMT_SLOW_LOOKAHEAD */ + +/****************************************************************************** + * + * SkRlmtBuildPacket - build an RLMT packet + * + * Description: + * This routine sets up an RLMT packet. + * + * Context: + * runtime, pageable? + * + * Returns: + * NULL or pointer to RLMT mbuf + */ +RLMT_STATIC SK_MBUF *SkRlmtBuildPacket( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* I/O context */ +SK_U32 PortIdx, /* sending port */ +SK_U16 PacketType, /* RLMT packet type */ +SK_MAC_ADDR *SrcAddr, /* source address */ +SK_MAC_ADDR *DestAddr) /* destination address */ +{ + int i; + SK_U16 Length; + SK_MBUF *pMb; + SK_RLMT_PACKET *pPacket; + + if ((pMb = SkDrvAllocRlmtMbuf(pAC, IoC, SK_RLMT_MAX_PACKET_SIZE)) != + NULL) { + pPacket = (SK_RLMT_PACKET*)pMb->pData; + for (i = 0; i < SK_MAC_ADDR_LEN; i++) { + pPacket->DstAddr[i] = DestAddr->a[i]; + pPacket->SrcAddr[i] = SrcAddr->a[i]; + } + pPacket->DSap = SK_RLMT_DSAP; + pPacket->SSap = SK_RLMT_SSAP; + pPacket->Ctrl = SK_RLMT_CTRL; + pPacket->Indicator[0] = SK_RLMT_INDICATOR0; + pPacket->Indicator[1] = SK_RLMT_INDICATOR1; + pPacket->Indicator[2] = SK_RLMT_INDICATOR2; + pPacket->Indicator[3] = SK_RLMT_INDICATOR3; + pPacket->Indicator[4] = SK_RLMT_INDICATOR4; + pPacket->Indicator[5] = SK_RLMT_INDICATOR5; + pPacket->Indicator[6] = SK_RLMT_INDICATOR6; + + SK_U16_TO_NETWORK_ORDER( + PacketType, + &pPacket->RlmtPacketType[0]); + + for (i = 0; i < 4; i++) { + pPacket->Random[i] = pAC->Rlmt.Port[PortIdx].Random[i]; + } + + SK_U16_TO_NETWORK_ORDER( + SK_RLMT_PACKET_VERSION, + &pPacket->RlmtPacketVersion[0]); + + for (i = 0; i < SK_PACKET_DATA_LEN; i++) { + pPacket->Data[i] = 0x00; + } + + Length = SK_RLMT_MAX_PACKET_SIZE; /* Or smaller. */ + pMb->Length = Length; + pMb->PortIdx = PortIdx; + Length -= 14; + SK_U16_TO_NETWORK_ORDER(Length, &pPacket->TypeLen[0]); + + if (PacketType == SK_PACKET_ALIVE) { + pAC->Rlmt.Port[PortIdx].TxHelloCts++; + } + } + + return (pMb); +} /* SkRlmtBuildPacket */ + + +/****************************************************************************** + * + * SkRlmtBuildSpanningTreePacket - build spanning tree check packet + * + * Description: + * This routine sets up a BPDU packet for spanning tree check. + * + * Context: + * runtime, pageable? + * + * Returns: + * NULL or pointer to RLMT mbuf + */ +RLMT_STATIC SK_MBUF *SkRlmtBuildSpanningTreePacket( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* I/O context */ +SK_U32 PortIdx) /* sending port */ +{ + unsigned i; + SK_U16 Length; + SK_MBUF *pMb; + SK_SPTREE_PACKET *pSPacket; + + if ((pMb = SkDrvAllocRlmtMbuf(pAC, IoC, SK_RLMT_MAX_PACKET_SIZE)) != + NULL) { + pSPacket = (SK_SPTREE_PACKET*)pMb->pData; + for (i = 0; i < SK_MAC_ADDR_LEN; i++) { + pSPacket->DstAddr[i] = BridgeMcAddr.a[i]; + pSPacket->SrcAddr[i] = + pAC->Addr.Port[PortIdx].CurrentMacAddress.a[i]; + } + pSPacket->DSap = SK_RLMT_SPT_DSAP; + pSPacket->SSap = SK_RLMT_SPT_SSAP; + pSPacket->Ctrl = SK_RLMT_SPT_CTRL; + + pSPacket->ProtocolId[0] = SK_RLMT_SPT_PROTOCOL_ID0; + pSPacket->ProtocolId[1] = SK_RLMT_SPT_PROTOCOL_ID1; + pSPacket->ProtocolVersionId = SK_RLMT_SPT_PROTOCOL_VERSION_ID; + pSPacket->BpduType = SK_RLMT_SPT_BPDU_TYPE; + pSPacket->Flags = SK_RLMT_SPT_FLAGS; + pSPacket->RootId[0] = SK_RLMT_SPT_ROOT_ID0; + pSPacket->RootId[1] = SK_RLMT_SPT_ROOT_ID1; + pSPacket->RootPathCost[0] = SK_RLMT_SPT_ROOT_PATH_COST0; + pSPacket->RootPathCost[1] = SK_RLMT_SPT_ROOT_PATH_COST1; + pSPacket->RootPathCost[2] = SK_RLMT_SPT_ROOT_PATH_COST2; + pSPacket->RootPathCost[3] = SK_RLMT_SPT_ROOT_PATH_COST3; + pSPacket->BridgeId[0] = SK_RLMT_SPT_BRIDGE_ID0; + pSPacket->BridgeId[1] = SK_RLMT_SPT_BRIDGE_ID1; + + /* + * Use virtual address as bridge ID and filter these packets + * on receive. + */ + + for (i = 0; i < SK_MAC_ADDR_LEN; i++) { + pSPacket->BridgeId[i + 2] = pSPacket->RootId[i + 2] = + pAC->Addr.CurrentMacAddress.a[i]; + } + pSPacket->PortId[0] = SK_RLMT_SPT_PORT_ID0; + pSPacket->PortId[1] = SK_RLMT_SPT_PORT_ID1; + pSPacket->MessageAge[0] = SK_RLMT_SPT_MSG_AGE0; + pSPacket->MessageAge[1] = SK_RLMT_SPT_MSG_AGE1; + pSPacket->MaxAge[0] = SK_RLMT_SPT_MAX_AGE0; + pSPacket->MaxAge[1] = SK_RLMT_SPT_MAX_AGE1; + pSPacket->HelloTime[0] = SK_RLMT_SPT_HELLO_TIME0; + pSPacket->HelloTime[1] = SK_RLMT_SPT_HELLO_TIME1; + pSPacket->ForwardDelay[0] = SK_RLMT_SPT_FWD_DELAY0; + pSPacket->ForwardDelay[1] = SK_RLMT_SPT_FWD_DELAY1; + + Length = SK_RLMT_MAX_PACKET_SIZE; /* Or smaller. */ + pMb->Length = Length; + pMb->PortIdx = PortIdx; + Length -= 14; + SK_U16_TO_NETWORK_ORDER(Length, &pSPacket->TypeLen[0]); + + pAC->Rlmt.Port[PortIdx].TxSpHelloReqCts++; + } + + return (pMb); +} /* SkRlmtBuildSpanningTreePacket */ + + +/****************************************************************************** + * + * SkRlmtSend - build and send check packets + * + * Description: + * Depending on the RLMT state and the checking state, several packets + * are sent through the indicated port. + * + * Context: + * runtime, pageable? + * + * Returns: + * Nothing. + */ +RLMT_STATIC void SkRlmtSend( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* I/O context */ +SK_U32 PortIdx) +{ + unsigned j; + SK_EVPARA Para; + SK_RLMT_PORT *pRPort; + + pRPort = &pAC->Rlmt.Port[PortIdx]; + if (pAC->Rlmt.RlmtMode & SK_RLMT_CHECK_LOC_LINK) { + if (pRPort->CheckingState & + (SK_RLMT_PCS_TX | SK_RLMT_PCS_RX)) { + + /* + * Port is suspicious. Send the RLMT packet to the + * RLMT multicast address. + */ + + if ((Para.pParaPtr = SkRlmtBuildPacket( + pAC, + IoC, + PortIdx, + SK_PACKET_ALIVE, + &pAC->Addr.Port[PortIdx].CurrentMacAddress, + &SkRlmtMcAddr)) != NULL) { + SkEventQueue( + pAC, + SKGE_DRV, + SK_DRV_RLMT_SEND, + Para); + } + } + else { + /* + * Send a directed RLMT packet to all ports that are + * checked by the indicated port. + */ + + for (j = 0; j < pRPort->PortsChecked; j++) { + if ((Para.pParaPtr = + SkRlmtBuildPacket( + pAC, + IoC, + PortIdx, + SK_PACKET_ALIVE, + &pAC->Addr.Port[PortIdx].CurrentMacAddress, + &pRPort->PortCheck[j].CheckAddr) + ) != NULL) { + SkEventQueue( + pAC, + SKGE_DRV, + SK_DRV_RLMT_SEND, + Para); + } + } + } + } + + if ((pAC->Rlmt.RlmtMode & SK_RLMT_CHECK_SEG) && + (pAC->Rlmt.CheckingState & SK_RLMT_RCS_SEND_SEG)) { + + /* + * Send a BPDU packet to make a connected switch tell us + * the correct root bridge. + */ + + if ((Para.pParaPtr = + SkRlmtBuildSpanningTreePacket( + pAC, + IoC, + PortIdx) + ) != NULL) { + + pAC->Rlmt.CheckingState &= ~SK_RLMT_RCS_SEND_SEG; + pRPort->RootIdSet = SK_FALSE; + + SkEventQueue( + pAC, + SKGE_DRV, + SK_DRV_RLMT_SEND, + Para); + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_TX, + ("SkRlmtSend: BPDU Packet on Port %u.\n", + PortIdx)) + } + } + + return; + +} /* SkRlmtSend */ + + +/****************************************************************************** + * + * SkRlmtPortReceives - check if port is (going) down and bring it up + * + * Description: + * This routine checks if a port who received a non-BPDU packet + * needs to go up or needs to be stopped going down. + * +* Context: + * runtime, pageable? + * + * Returns: + * Nothing. + */ +RLMT_STATIC void SkRlmtPortReceives( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* I/O context */ +SK_U32 PortIdx) /* port to check */ +{ + SK_RLMT_PORT *pRPort; + SK_EVPARA Para; + + pRPort = &pAC->Rlmt.Port[PortIdx]; + pRPort->PortNoRx = SK_FALSE; + + if ((pRPort->PortState == SK_RLMT_PS_DOWN) && + !(pRPort->CheckingState & SK_RLMT_PCS_TX)) { + + /* + * Port is marked down (rx), but received a non-BPDU packet. + * Bring it up. + */ + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_RX, + ("SkRlmtPacketReceive: Received on PortDown.\n")) + + pRPort->PortState = SK_RLMT_PS_GOING_UP; + pRPort->GuTimeStamp = SkOsGetTime(pAC); + Para.Para32[0] = PortIdx; + SkTimerStart( + pAC, + IoC, + &pRPort->UpTimer, + SK_RLMT_PORTUP_TIM_VAL, + SKGE_RLMT, + SK_RLMT_PORTUP_TIM, + Para); + SkRlmtCheckSwitch(pAC, IoC); + } + else if (pRPort->CheckingState & SK_RLMT_PCS_RX) { + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_RX, + ("SkRlmtPacketReceive: Stop bringing port down.\n")) + SkTimerStop(pAC, IoC, &pRPort->DownRxTimer); + SkRlmtCheckSwitch(pAC, IoC); + } + + pRPort->CheckingState &= ~SK_RLMT_PCS_RX; + return; +} /* SkRlmtPortReceives */ + + +/****************************************************************************** + * + * SkRlmtPacketReceive - receive a packet for closer examination + * + * Description: + * This routine examines a packet more closely than SkRlmtLookahead*(). + * + * Context: + * runtime, pageable? + * + * Returns: + * Nothing. + */ +RLMT_STATIC void SkRlmtPacketReceive( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* I/O context */ +SK_MBUF *pMb) /* received packet */ +{ +#ifdef xDEBUG + extern void DumpData(char *p, int size); +#endif /* DEBUG */ + int i; + unsigned j; + SK_U16 PacketType; + SK_U32 PortIdx; + SK_ADDR_PORT *pAPort; + SK_RLMT_PORT *pRPort; + SK_RLMT_PACKET *pRPacket; + SK_SPTREE_PACKET *pSPacket; + SK_EVPARA Para; + + PortIdx = pMb->PortIdx; + pAPort = &pAC->Addr.Port[PortIdx]; + pRPort = &pAC->Rlmt.Port[PortIdx]; + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_RX, + ("SkRlmtPacketReceive: PortIdx == %d.\n", PortIdx)) + + pRPacket = (SK_RLMT_PACKET*)pMb->pData; + pSPacket = (SK_SPTREE_PACKET*)pRPacket; + +#ifdef xDEBUG + DumpData((char *)pRPacket, 32); +#endif /* DEBUG */ + + if ((pRPort->PacketsPerTimeSlot - pRPort->BpduPacketsPerTimeSlot) != 0) { + SkRlmtPortReceives(pAC, IoC, PortIdx); + } + + /* Check destination address. */ + + if (!SK_ADDR_EQUAL(pAPort->CurrentMacAddress.a, pRPacket->DstAddr) && + !SK_ADDR_EQUAL(SkRlmtMcAddr.a, pRPacket->DstAddr) && + !SK_ADDR_EQUAL(BridgeMcAddr.a, pRPacket->DstAddr)) { + + /* + * Not sent to current MAC or registered MC address + * => Trash it. + */ + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_RX, + ("SkRlmtPacketReceive: Not for me.\n")) + + SkDrvFreeRlmtMbuf(pAC, IoC, pMb); + return; + } + else if (SK_ADDR_EQUAL(pAPort->CurrentMacAddress.a, pRPacket->SrcAddr)) { + + /* + * Was sent by same port (may happen during port switching + * or in case of duplicate MAC addresses). + */ + + /* + * Check for duplicate address here: + * If Packet.Random != My.Random => DupAddr. + */ + + for (i = 3; i >= 0; i--) { + if (pRPort->Random[i] != + pRPacket->Random[i]) { + break; + } + } + + /* + * CAUTION: Do not check for duplicate MAC + * address in RLMT Alive Reply packets. + */ + + if (i >= 0 && pRPacket->DSap == SK_RLMT_DSAP && + pRPacket->Ctrl == SK_RLMT_CTRL && + pRPacket->SSap == SK_RLMT_SSAP && + pRPacket->Indicator[0] == SK_RLMT_INDICATOR0 && + pRPacket->Indicator[1] == SK_RLMT_INDICATOR1 && + pRPacket->Indicator[2] == SK_RLMT_INDICATOR2 && + pRPacket->Indicator[3] == SK_RLMT_INDICATOR3 && + pRPacket->Indicator[4] == SK_RLMT_INDICATOR4 && + pRPacket->Indicator[5] == SK_RLMT_INDICATOR5 && + pRPacket->Indicator[6] == SK_RLMT_INDICATOR6) { + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_RX, + ("SkRlmtPacketReceive: Duplicate MAC Address.\n")) + + /* Error Log entry. */ + + SK_ERR_LOG( + pAC, + SK_ERRCL_COMM, + SKERR_RLMT_E006, + SKERR_RLMT_E006_MSG); + } + else { + /* Simply trash it. */ + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_RX, + ("SkRlmtPacketReceive: Sent by me.\n")) + } + + SkDrvFreeRlmtMbuf(pAC, IoC, pMb); + + return; + } + + /* Check SuspectTx entries. */ + + if (pRPort->PortsSuspect > 0) { + for (j = 0; j < pRPort->PortsChecked; j++) { + if (pRPort->PortCheck[j].SuspectTx && + SK_ADDR_EQUAL( + pRPacket->SrcAddr, + pRPort->PortCheck[j].CheckAddr.a)) { + pRPort->PortCheck[j].SuspectTx = SK_FALSE; + pRPort->PortsSuspect--; + break; + } + } + } + + /* Determine type of packet. */ + + if (pRPacket->DSap == SK_RLMT_DSAP && + pRPacket->Ctrl == SK_RLMT_CTRL && + (pRPacket->SSap & ~LLC_COMMAND_RESPONSE_BIT) == SK_RLMT_SSAP && + pRPacket->Indicator[0] == SK_RLMT_INDICATOR0 && + pRPacket->Indicator[1] == SK_RLMT_INDICATOR1 && + pRPacket->Indicator[2] == SK_RLMT_INDICATOR2 && + pRPacket->Indicator[3] == SK_RLMT_INDICATOR3 && + pRPacket->Indicator[4] == SK_RLMT_INDICATOR4 && + pRPacket->Indicator[5] == SK_RLMT_INDICATOR5 && + pRPacket->Indicator[6] == SK_RLMT_INDICATOR6) { + + /* It's an RLMT packet. */ + + PacketType = (SK_U16)((pRPacket->RlmtPacketType[0] << 8) | + pRPacket->RlmtPacketType[1]); + + switch (PacketType) { + case SK_PACKET_ANNOUNCE: /* Not yet used. */ +#if 0 + /* Build the check chain. */ + + SkRlmtBuildCheckChain(pAC); +#endif /* 0 */ + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_RX, + ("SkRlmtPacketReceive: Announce.\n")) + + SkDrvFreeRlmtMbuf(pAC, IoC, pMb); + break; + + case SK_PACKET_ALIVE: + if (pRPacket->SSap & LLC_COMMAND_RESPONSE_BIT) { + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_RX, + ("SkRlmtPacketReceive: Alive Reply.\n")) + + /* Alive Reply Packet. */ + +#if 0 + pRPort->RlmtAcksPerTimeSlot++; +#endif /* 0 */ + + if (!(pAC->Addr.Port[PortIdx].PromMode & + SK_PROM_MODE_LLC) || + SK_ADDR_EQUAL( + pRPacket->DstAddr, + pAPort->CurrentMacAddress.a)) { + + /* Obviously we could send something. */ + + if (pRPort->CheckingState & + SK_RLMT_PCS_TX) { + pRPort->CheckingState &= + ~SK_RLMT_PCS_TX; + SkTimerStop( + pAC, + IoC, + &pRPort->DownTxTimer); + } + + if ((pRPort->PortState == + SK_RLMT_PS_DOWN) && + !(pRPort->CheckingState & + SK_RLMT_PCS_RX)) { + pRPort->PortState = + SK_RLMT_PS_GOING_UP; + pRPort->GuTimeStamp = + SkOsGetTime(pAC); + + SkTimerStop( + pAC, + IoC, + &pRPort->DownTxTimer); + + Para.Para32[0] = PortIdx; + SkTimerStart( + pAC, + IoC, + &pRPort->UpTimer, + SK_RLMT_PORTUP_TIM_VAL, + SKGE_RLMT, + SK_RLMT_PORTUP_TIM, + Para); + } + } + + /* Mark sending port as alive? */ + + SkDrvFreeRlmtMbuf(pAC, IoC, pMb); + } + else { /* Alive Request Packet. */ + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_RX, + ("SkRlmtPacketReceive: Alive Request.\n")) + + pRPort->RxHelloCts++; +#if 0 + pRPort->RlmtChksPerTimeSlot++; +#endif /* 0 */ + + /* Answer. */ + + for (i = 0; i < SK_MAC_ADDR_LEN; i++) { + pRPacket->DstAddr[i] = + pRPacket->SrcAddr[i]; + pRPacket->SrcAddr[i] = pAC->Addr.Port[ + PortIdx].CurrentMacAddress.a[i]; + } + pRPacket->SSap |= LLC_COMMAND_RESPONSE_BIT; + + Para.pParaPtr = pMb; + SkEventQueue( + pAC, + SKGE_DRV, + SK_DRV_RLMT_SEND, + Para); + } + break; + + case SK_PACKET_CHECK_TX: + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_RX, + ("SkRlmtPacketReceive: Check your tx line.\n")) + + /* + * A port checking us requests us to check our tx line. + */ + + pRPort->CheckingState |= SK_RLMT_PCS_TX; + + /* Start PortDownTx timer. */ + + Para.Para32[0] = PortIdx; + SkTimerStart( + pAC, + IoC, + &pRPort->DownTxTimer, + SK_RLMT_PORTDOWN_TIM_VAL, + SKGE_RLMT, + SK_RLMT_PORTDOWN_TX_TIM, + Para); + + SkDrvFreeRlmtMbuf(pAC, IoC, pMb); + + if ((Para.pParaPtr = SkRlmtBuildPacket( + pAC, + IoC, + PortIdx, + SK_PACKET_ALIVE, + &pAC->Addr.Port[PortIdx].CurrentMacAddress, + &SkRlmtMcAddr)) != NULL) { + SkEventQueue( + pAC, + SKGE_DRV, + SK_DRV_RLMT_SEND, + Para); + } + break; + + case SK_PACKET_ADDR_CHANGED: + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_RX, + ("SkRlmtPacketReceive: Address Change.\n")) + + /* Build the check chain. */ + + SkRlmtBuildCheckChain(pAC); + SkDrvFreeRlmtMbuf(pAC, IoC, pMb); + break; + + default: + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_RX, + ("SkRlmtPacketReceive: Unknown RLMT packet.\n")) + + /* RA;:;: ??? */ + + SkDrvFreeRlmtMbuf(pAC, IoC, pMb); + } + } + else if (pSPacket->DSap == SK_RLMT_SPT_DSAP && + pSPacket->Ctrl == SK_RLMT_SPT_CTRL && + (pSPacket->SSap & ~LLC_COMMAND_RESPONSE_BIT) == + SK_RLMT_SPT_SSAP) { + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_RX, + ("SkRlmtPacketReceive: BPDU Packet.\n")) + + /* Spanning Tree packet. */ + + pRPort->RxSpHelloCts++; + + if (!SK_ADDR_EQUAL( + &pSPacket->RootId[2], + &pAC->Addr.CurrentMacAddress.a[0])) { + + /* + * Check segmentation if a new root bridge is set and + * the segmentation check is not currently running. + */ + + if (!SK_ADDR_EQUAL( + &pSPacket->RootId[2], + &pRPort->Root.Id[2]) && + (pAC->Rlmt.LinksUp > 1) && + (pAC->Rlmt.RlmtMode & SK_RLMT_CHECK_SEG) && + !(pAC->Rlmt.CheckingState & SK_RLMT_RCS_SEG)) { + pAC->Rlmt.CheckingState |= + SK_RLMT_RCS_START_SEG | + SK_RLMT_RCS_SEND_SEG; + } + + /* Store tree view of this port. */ + + for (i = 0; i < 8; i++) { + pRPort->Root.Id[i] = pSPacket->RootId[i]; + } + pRPort->RootIdSet = SK_TRUE; + } + + SkDrvFreeRlmtMbuf(pAC, IoC, pMb); + + if (pAC->Rlmt.CheckingState & SK_RLMT_RCS_REPORT_SEG) { + SkRlmtCheckSeg(pAC, IoC); + } + } + else { + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_RX, + ("SkRlmtPacketReceive: Unknown Packet Type.\n")) + + /* Unknown packet. */ + + SkDrvFreeRlmtMbuf(pAC, IoC, pMb); + } + return; +} /* SkRlmtPacketReceive */ + + +/****************************************************************************** + * + * SkRlmtCheckPort - check if a port works + * + * Description: + * This routine checks if a port whose link is up received something + * and if it seems to transmit successfully. + * + * # PortState: PsInit, PsLinkDown, PsDown, PsGoingUp, PsUp + * # PortCheckingState (Bitfield): ChkTx, ChkRx, ChkSeg + * # RlmtCheckingState (Bitfield): ChkSeg, StartChkSeg, ReportSeg + * + * if (Rx - RxBpdu == 0) { # No rx. + * if (state == PsUp) { + * PortCheckingState |= ChkRx + * } + * if (ModeCheckSeg && (Timeout == + * TO_SHORTEN(RLMT_DEFAULT_TIMEOUT))) { + * RlmtCheckingState |= ChkSeg) + * PortCheckingState |= ChkSeg + * } + * NewTimeout = TO_SHORTEN(Timeout) + * if (NewTimeout < RLMT_MIN_TIMEOUT) { + * NewTimeout = RLMT_MIN_TIMEOUT + * PortState = PsDown + * ... + * } + * } + * else { # something was received + * # Set counter to 0 at LinkDown? + * # No - rx may be reported after LinkDown ??? + * PortCheckingState &= ~ChkRx + * NewTimeout = RLMT_DEFAULT_TIMEOUT + * if (RxAck == 0) { + * possible reasons: + * is my tx line bad? -- + * send RLMT multicast and report + * back internally? (only possible + * between ports on same adapter) + * } + * if (RxChk == 0) { + * possible reasons: + * - tx line of port set to check me + * maybe bad + * - no other port/adapter available or set + * to check me + * - adapter checking me has a longer + * timeout + * ??? anything that can be done here? + * } + * } + * + * Context: + * runtime, pageable? + * + * Returns: + * New timeout value. + */ +RLMT_STATIC SK_U32 SkRlmtCheckPort( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* I/O context */ +SK_U32 PortIdx) /* port to check */ +{ + unsigned i; + SK_U32 NewTimeout; + SK_RLMT_PORT *pRPort; + SK_EVPARA Para; + + pRPort = &pAC->Rlmt.Port[PortIdx]; + + if ((pRPort->PacketsPerTimeSlot - pRPort->BpduPacketsPerTimeSlot) == 0) { + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SkRlmtCheckPort %d: No (%d) receives in last time slot.\n", + PortIdx, + pRPort->PacketsPerTimeSlot)) + + /* + * Check segmentation if there was no receive at least twice + * in a row (PortNoRx is already set) and the segmentation + * check is not currently running. + */ + + if (pRPort->PortNoRx && (pAC->Rlmt.LinksUp > 1) && + (pAC->Rlmt.RlmtMode & SK_RLMT_CHECK_SEG) && + !(pAC->Rlmt.CheckingState & SK_RLMT_RCS_SEG)) { + pAC->Rlmt.CheckingState |= + SK_RLMT_RCS_START_SEG | SK_RLMT_RCS_SEND_SEG; + } + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SkRlmtCheckPort: PortsSuspect %d, PcsRx %d.\n", + pRPort->PortsSuspect, + pRPort->CheckingState & SK_RLMT_PCS_RX)) + + if (pRPort->PortState != SK_RLMT_PS_DOWN) { + NewTimeout = TO_SHORTEN(pAC->Rlmt.TimeoutValue); + if (NewTimeout < SK_RLMT_MIN_TO_VAL) { + NewTimeout = SK_RLMT_MIN_TO_VAL; + } + + if (!(pRPort->CheckingState & SK_RLMT_PCS_RX)) { + Para.Para32[0] = PortIdx; + pRPort->CheckingState |= SK_RLMT_PCS_RX; + + /* + * What shall we do if the port checked + * by this one receives our request + * frames? What's bad - our rx line + * or his tx line? + */ + + SkTimerStart( + pAC, + IoC, + &pRPort->DownRxTimer, + SK_RLMT_PORTDOWN_TIM_VAL, + SKGE_RLMT, + SK_RLMT_PORTDOWN_RX_TIM, + Para); + + for (i = 0; i < pRPort->PortsChecked; i++) { + if (pRPort->PortCheck[i].SuspectTx) { + continue; + } + pRPort->PortCheck[i].SuspectTx = SK_TRUE; + pRPort->PortsSuspect++; + if ((Para.pParaPtr = + SkRlmtBuildPacket( + pAC, + IoC, + PortIdx, + SK_PACKET_CHECK_TX, + &pAC->Addr.Port[PortIdx].CurrentMacAddress, + &pRPort->PortCheck[i].CheckAddr) + ) != NULL) { + SkEventQueue( + pAC, + SKGE_DRV, + SK_DRV_RLMT_SEND, + Para); + } + } + } + } + else { /* PortDown -- or all partners suspect. */ + NewTimeout = SK_RLMT_DEF_TO_VAL; + } + pRPort->PortNoRx = SK_TRUE; + } + else { /* A non-BPDU packet was received. */ + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SkRlmtCheckPort %d: %d (%d) receives in last time slot.\n", + PortIdx, + pRPort->PacketsPerTimeSlot - + pRPort->BpduPacketsPerTimeSlot, + pRPort->PacketsPerTimeSlot)) + + SkRlmtPortReceives(pAC, IoC, PortIdx); + NewTimeout = SK_RLMT_DEF_TO_VAL; + } + + return (NewTimeout); +} /* SkRlmtCheckPort */ + + +/****************************************************************************** + * + * SkRlmtSelectBcRx - select new active port, criteria 1 (CLP) + * + * Description: + * This routine selects the port that received a broadcast frame + * substantially later than all other ports. + * + * Context: + * runtime, pageable? + * + * Returns: + * SK_BOOL + */ +RLMT_STATIC SK_BOOL SkRlmtSelectBcRx( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* I/O context */ +SK_U32 A, /* active port */ +SK_U32 P, /* preferred port */ +SK_U32 *N) /* new active port */ +{ + SK_U64 BcTimeStamp; + SK_U32 i; + SK_BOOL PortFound; + + BcTimeStamp = 0; /* Not totally necessary, but feeling better. */ + PortFound = SK_FALSE; + + /* Select port with the latest TimeStamp. */ + + for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("TimeStamp Port %d: %.08x%.08x.\n", + i, + *((SK_U32*)(&pAC->Rlmt.Port[i].BcTimeStamp) + OFFS_HI32), + *((SK_U32*)(&pAC->Rlmt.Port[i].BcTimeStamp) + OFFS_LO32))) + if (!pAC->Rlmt.Port[i].PortDown && + !pAC->Rlmt.Port[i].PortNoRx) { + if (!PortFound || + pAC->Rlmt.Port[i].BcTimeStamp > BcTimeStamp) { + BcTimeStamp = pAC->Rlmt.Port[i].BcTimeStamp; + *N = i; + PortFound = SK_TRUE; + } + } + } + + if (PortFound) { + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("Port %d received the last broadcast.\n", *N)) + + /* Look if another port's time stamp is similar. */ + + for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + if (i == *N) { + continue; + } + if (!pAC->Rlmt.Port[i].PortDown && + !pAC->Rlmt.Port[i].PortNoRx && + (pAC->Rlmt.Port[i].BcTimeStamp > + BcTimeStamp - SK_RLMT_BC_DELTA || + pAC->Rlmt.Port[i].BcTimeStamp + + SK_RLMT_BC_DELTA > BcTimeStamp)) { + PortFound = SK_FALSE; + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("Port %d received a broadcast %s.\n", + i, + "at a similar time")) + break; + } + } + } + +#ifdef DEBUG + if (PortFound) { + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_CHECK_SWITCH found Port %d receiving %s.\n", + *N, + "the substantially latest broadcast")) + } +#endif /* DEBUG */ + + return (PortFound); +} /* SkRlmtSelectBcRx */ + + +/****************************************************************************** + * + * SkRlmtSelectNotSuspect - select new active port, criteria 2 (CLP) + * + * Description: + * This routine selects a good port (it is PortUp && !SuspectRx). + * + * Context: + * runtime, pageable? + * + * Returns: + * SK_BOOL + */ +RLMT_STATIC SK_BOOL SkRlmtSelectNotSuspect( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* I/O context */ +SK_U32 A, /* active port */ +SK_U32 P, /* preferred port */ +SK_U32 *N) /* new active port */ +{ + SK_U32 i; + SK_BOOL PortFound; + + PortFound = SK_FALSE; + + /* Select first port that is PortUp && !SuspectRx. */ + + for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + if (!pAC->Rlmt.Port[i].PortDown && + !(pAC->Rlmt.Port[i].CheckingState & SK_RLMT_PCS_RX)) { + *N = i; + if (!pAC->Rlmt.Port[A].PortDown && + !(pAC->Rlmt.Port[A].CheckingState & + SK_RLMT_PCS_RX)) { + *N = A; + } + if (!pAC->Rlmt.Port[P].PortDown && + !(pAC->Rlmt.Port[P].CheckingState & + SK_RLMT_PCS_RX)) { + *N = P; + } + PortFound = SK_TRUE; + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_CHECK_SWITCH found Port %d up and not check RX.\n", + *N)) + break; + } + } + + return (PortFound); +} /* SkRlmtSelectNotSuspect */ + + +/****************************************************************************** + * + * SkRlmtSelectUp - select new active port, criteria 3, 4 (CLP) + * + * Description: + * This routine selects a port that is up. + * + * Context: + * runtime, pageable? + * + * Returns: + * SK_BOOL + */ +RLMT_STATIC SK_BOOL SkRlmtSelectUp( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* I/O context */ +SK_U32 A, /* active port */ +SK_U32 P, /* preferred port */ +SK_U32 *N, /* new active port */ +SK_BOOL AutoNegDone) /* successfully auto-negotiated? */ +{ + SK_U32 i; + SK_BOOL PortFound; + + PortFound = SK_FALSE; + + /* Select first port that is PortUp. */ + + for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + if (pAC->Rlmt.Port[i].PortState == SK_RLMT_PS_UP && + pAC->GIni.GP[i].PAutoNegFail != AutoNegDone) { + *N = i; + if (pAC->Rlmt.Port[A].PortState == SK_RLMT_PS_UP && + pAC->GIni.GP[A].PAutoNegFail != AutoNegDone) { + *N = A; + } + if (pAC->Rlmt.Port[P].PortState == SK_RLMT_PS_UP && + pAC->GIni.GP[P].PAutoNegFail != AutoNegDone) { + *N = P; + } + PortFound = SK_TRUE; + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_CHECK_SWITCH found Port %d up.\n", + *N)) + break; + } + } + + return (PortFound); +} /* SkRlmtSelectUp */ + + +/****************************************************************************** + * + * SkRlmtSelectGoingUp - select new active port, criteria 5, 6 (CLP) + * + * Description: + * This routine selects the port that is going up for the longest time. + * + * Context: + * runtime, pageable? + * + * Returns: + * SK_BOOL + */ +RLMT_STATIC SK_BOOL SkRlmtSelectGoingUp( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* I/O context */ +SK_U32 A, /* active port */ +SK_U32 P, /* preferred port */ +SK_U32 *N, /* new active port */ +SK_BOOL AutoNegDone) /* successfully auto-negotiated? */ +{ + SK_U64 GuTimeStamp=0; + SK_U32 i; + SK_BOOL PortFound; + + PortFound = SK_FALSE; + + /* Select port that is PortGoingUp for the longest time. */ + + for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + if (pAC->Rlmt.Port[i].PortState == SK_RLMT_PS_GOING_UP && + pAC->GIni.GP[i].PAutoNegFail != AutoNegDone) { + GuTimeStamp = pAC->Rlmt.Port[i].GuTimeStamp; + *N = i; + PortFound = SK_TRUE; + break; + } + } + + if (!PortFound) { + return (SK_FALSE); + } + + for (i = *N + 1; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + if (pAC->Rlmt.Port[i].PortState == SK_RLMT_PS_GOING_UP && + pAC->Rlmt.Port[i].GuTimeStamp < GuTimeStamp && + pAC->GIni.GP[i].PAutoNegFail != AutoNegDone) { + + GuTimeStamp = pAC->Rlmt.Port[i].GuTimeStamp; + *N = i; + } + } + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_CHECK_SWITCH found Port %d going up.\n", *N)) + + return (SK_TRUE); +} /* SkRlmtSelectGoingUp */ + + +/****************************************************************************** + * + * SkRlmtSelectDown - select new active port, criteria 7, 8 (CLP) + * + * Description: + * This routine selects a port that is down. + * + * Context: + * runtime, pageable? + * + * Returns: + * SK_BOOL + */ +RLMT_STATIC SK_BOOL SkRlmtSelectDown( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* I/O context */ +SK_U32 A, /* active port */ +SK_U32 P, /* preferred port */ +SK_U32 *N, /* new active port */ +SK_BOOL AutoNegDone) /* successfully auto-negotiated? */ +{ + SK_U32 i; + SK_BOOL PortFound; + + PortFound = SK_FALSE; + + /* Select first port that is PortDown. */ + + for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + if (pAC->Rlmt.Port[i].PortState == SK_RLMT_PS_DOWN && + pAC->GIni.GP[i].PAutoNegFail != AutoNegDone) { + *N = i; + if (pAC->Rlmt.Port[A].PortState == SK_RLMT_PS_DOWN && + pAC->GIni.GP[A].PAutoNegFail != AutoNegDone) { + *N = A; + } + if (pAC->Rlmt.Port[P].PortState == SK_RLMT_PS_DOWN && + pAC->GIni.GP[P].PAutoNegFail != AutoNegDone) { + *N = P; + } + PortFound = SK_TRUE; + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_CHECK_SWITCH found Port %d down.\n", + *N)) + break; + } + } + + return (PortFound); +} /* SkRlmtSelectDown */ + + +/****************************************************************************** + * + * SkRlmtCheckSwitch - select new active port and switch to it + * + * Description: + * This routine decides which port should be the active one and queues + * port switching if necessary. + * + * Context: + * runtime, pageable? + * + * Returns: + * Nothing. + */ +RLMT_STATIC void SkRlmtCheckSwitch( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC) /* I/O context */ +{ + SK_EVPARA Para; + SK_U32 A; + SK_U32 P; + SK_U32 i; + SK_BOOL PortFound; + + if (pAC->Rlmt.RlmtState == SK_RLMT_RS_INIT) { + return; + } + + A = pAC->Rlmt.MacActive; /* Index of active port. */ + P = pAC->Rlmt.PrefPort; /* Index of preferred port. */ + PortFound = SK_FALSE; + + if (pAC->Rlmt.LinksUp == 0) { + + /* + * Last link went down - shut down the net. + */ + + pAC->Rlmt.RlmtState = SK_RLMT_RS_NET_DOWN; + Para.Para32[0] = SK_RLMT_NET_DOWN_TEMP; + SkEventQueue( + pAC, + SKGE_DRV, + SK_DRV_NET_DOWN, + Para); + + return; + } + else if (pAC->Rlmt.LinksUp == 1 && + pAC->Rlmt.RlmtState == SK_RLMT_RS_NET_DOWN) { + + /* First link came up - get the net up. */ + + pAC->Rlmt.RlmtState = SK_RLMT_RS_NET_UP; + + /* + * If pAC->Rlmt.MacActive != Para.Para32[0], + * the DRV switches to the port that came up. + */ + + for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + if (!pAC->Rlmt.Port[i].LinkDown) { + if (!pAC->Rlmt.Port[A].LinkDown) { + i = A; + } + if (!pAC->Rlmt.Port[P].LinkDown) { + i = P; + } + PortFound = SK_TRUE; + break; + } + } + + if (PortFound) { + Para.Para32[0] = pAC->Rlmt.MacActive; + Para.Para32[1] = i; + SkEventQueue( + pAC, + SKGE_PNMI, + SK_PNMI_EVT_RLMT_PORT_SWITCH, + Para); + + pAC->Rlmt.MacActive = i; + Para.Para32[0] = i; + SkEventQueue(pAC, SKGE_DRV, SK_DRV_NET_UP, Para); + + if ((Para.pParaPtr = SkRlmtBuildPacket( + pAC, + IoC, + i, + SK_PACKET_ANNOUNCE, + &pAC->Addr.CurrentMacAddress, + &SkRlmtMcAddr) + ) != NULL) { + + /* + * Send packet to RLMT multicast address + * to force switches to learn the new + * location of the address. + */ + + SkEventQueue( + pAC, + SKGE_DRV, + SK_DRV_RLMT_SEND, + Para); + } + } + else { + SK_ERR_LOG( + pAC, + SK_ERRCL_SW, + SKERR_RLMT_E007, + SKERR_RLMT_E007_MSG); + } + + return; + } + else { + Para.Para32[0] = A; + + /* + * Preselection: + * If RLMT Mode != CheckLinkState + * select port that received a broadcast frame + * substantially later than all other ports + * else select first port that is not SuspectRx + * else select first port that is PortUp + * else select port that is PortGoingUp for the longest time + * else select first port that is PortDown + * else stop. + * + * For the preselected port: + * If ActivePort is equal in quality, select ActivePort. + * + * If PrefPort is equal in quality, select PrefPort. + * + * If MacActive != SelectedPort, + * If old ActivePort is LinkDown, + * SwitchHard + * else + * SwitchSoft + */ + + if (pAC->Rlmt.RlmtMode != SK_RLMT_CHECK_LINK) { + if (!PortFound) { + PortFound = SkRlmtSelectBcRx(pAC, IoC, + A, P, &Para.Para32[1]); + } + + if (!PortFound) { + PortFound = SkRlmtSelectNotSuspect(pAC, IoC, + A, P, &Para.Para32[1]); + } + } + + if (!PortFound) { + PortFound = SkRlmtSelectUp(pAC, IoC, + A, P, &Para.Para32[1], AUTONEG_SUCCESS); + } + + if (!PortFound) { + PortFound = SkRlmtSelectUp(pAC, IoC, + A, P, &Para.Para32[1], AUTONEG_FAILED); + } + + if (!PortFound) { + PortFound = SkRlmtSelectGoingUp(pAC, IoC, + A, P, &Para.Para32[1], AUTONEG_SUCCESS); + } + + if (!PortFound) { + PortFound = SkRlmtSelectGoingUp(pAC, IoC, + A, P,&Para.Para32[1], AUTONEG_FAILED); + } + + if (pAC->Rlmt.RlmtMode != SK_RLMT_CHECK_LINK) { + if (!PortFound) { + PortFound = SkRlmtSelectDown(pAC, IoC, + A, P,&Para.Para32[1], AUTONEG_SUCCESS); + } + + if (!PortFound) { + PortFound = SkRlmtSelectDown(pAC, IoC, + A, P,&Para.Para32[1], AUTONEG_FAILED); + } + } + + if (PortFound) { + if (Para.Para32[1] != A) { + pAC->Rlmt.MacActive = Para.Para32[1]; + SK_HWAC_LINK_LED( + pAC, + IoC, + Para.Para32[1], + SK_LED_ACTIVE); + if (pAC->Rlmt.Port[A].LinkDown) { + SkEventQueue( + pAC, + SKGE_DRV, + SK_DRV_SWITCH_HARD, + Para); + } + else { + SK_HWAC_LINK_LED( + pAC, + IoC, + Para.Para32[0], + SK_LED_STANDBY); + SkEventQueue( + pAC, + SKGE_DRV, + SK_DRV_SWITCH_SOFT, + Para); + } + SkEventQueue( + pAC, + SKGE_PNMI, + SK_PNMI_EVT_RLMT_PORT_SWITCH, + Para); + if ((Para.pParaPtr = SkRlmtBuildPacket( + pAC, + IoC, + Para.Para32[1], + SK_PACKET_ANNOUNCE, + &pAC->Addr.CurrentMacAddress, + &SkRlmtMcAddr) + ) != NULL) { + + /* + * Send "new" packet to RLMT multicast + * address to force switches to learn + * the new location of the MAC address. + */ + + SkEventQueue( + pAC, + SKGE_DRV, + SK_DRV_RLMT_SEND, + Para); + } + } + } + else { + SK_ERR_LOG( + pAC, + SK_ERRCL_SW, + SKERR_RLMT_E004, + SKERR_RLMT_E004_MSG); + } + } + + return; +} /* SkRlmtCheckSwitch */ + + +/****************************************************************************** + * + * SkRlmtCheckSeg - Report if segmentation is detected + * + * Description: + * This routine checks if the ports see different root bridges and reports + * segmentation in such a case. + * + * Context: + * runtime, pageable? + * + * Returns: + * Nothing. + */ +RLMT_STATIC void SkRlmtCheckSeg( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC) /* I/O context */ +{ + SK_BOOL Equal; + SK_U32 i, j; + + pAC->Rlmt.RootIdSet = SK_FALSE; + Equal = SK_TRUE; + for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + if (pAC->Rlmt.Port[i].LinkDown || + !pAC->Rlmt.Port[i].RootIdSet) { + continue; + } + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("Root ID %d: %02x %02x %02x %02x %02x %02x %02x %02x.\n", + i, + pAC->Rlmt.Port[i].Root.Id[0], + pAC->Rlmt.Port[i].Root.Id[1], + pAC->Rlmt.Port[i].Root.Id[2], + pAC->Rlmt.Port[i].Root.Id[3], + pAC->Rlmt.Port[i].Root.Id[4], + pAC->Rlmt.Port[i].Root.Id[5], + pAC->Rlmt.Port[i].Root.Id[6], + pAC->Rlmt.Port[i].Root.Id[7])) + + if (!pAC->Rlmt.RootIdSet) { + pAC->Rlmt.Root = pAC->Rlmt.Port[i].Root; + pAC->Rlmt.RootIdSet = SK_TRUE; + continue; + } + + for (j = 0; j < 8; j ++) { + Equal &= pAC->Rlmt.Port[i].Root.Id[j] == + pAC->Rlmt.Root.Id[j]; + if (!Equal) { + break; + } + } + + if (!Equal) { + SK_ERR_LOG( + pAC, + SK_ERRCL_COMM, + SKERR_RLMT_E005, + SKERR_RLMT_E005_MSG); +#ifdef SK_PNMI_EVT_SEGMENTATION + SkEventQueue( + pAC, + SKGE_PNMI, + SK_PNMI_EVT_SEGMENTATION, + Para); +#endif /* SK_PNMI_EVT_SEGMENTATION */ + pAC->Rlmt.CheckingState &= ~SK_RLMT_RCS_REPORT_SEG; + break; + } + } +} /* SkRlmtCheckSeg */ + + +/****************************************************************************** + * + * SkRlmtPortStart - initialize port variables and start port + * + * Description: + * This routine initializes a port's variables and issues a PORT_START + * to the HWAC module. This handles retries if the start fails or the + * link eventually goes down. + * + * Context: + * runtime, pageable? + * + * Returns: + * Nothing + */ +RLMT_STATIC void SkRlmtPortStart( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* I/O context */ +SK_U32 PortIdx) /* event code */ +{ + SK_EVPARA Para; + + pAC->Rlmt.Port[PortIdx].PortState = SK_RLMT_PS_LINK_DOWN; + pAC->Rlmt.Port[PortIdx].PortStarted = SK_TRUE; + pAC->Rlmt.Port[PortIdx].LinkDown = SK_TRUE; + pAC->Rlmt.Port[PortIdx].PortDown = SK_TRUE; + pAC->Rlmt.Port[PortIdx].CheckingState = 0; + pAC->Rlmt.Port[PortIdx].RootIdSet = SK_FALSE; + Para.Para32[0] = PortIdx; + SkEventQueue(pAC, SKGE_HWAC, SK_HWEV_PORT_START, Para); +} /* SkRlmtPortStart */ + + +/****************************************************************************** + * + * SkRlmtEvent - a PORT- or an RLMT-specific event happened + * + * Description: + * This routine handles PORT- and RLMT-specific events. + * + * Context: + * runtime, pageable? + * may be called after SK_INIT_IO + * + * Returns: + * 0 + */ +int SkRlmtEvent( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* I/O context */ +SK_U32 Event, /* event code */ +SK_EVPARA Para) /* event-specific parameter */ +{ + SK_U32 i, j; + SK_RLMT_PORT *pRPort; + SK_EVPARA Para2; + SK_MBUF *pMb; + SK_MBUF *pNextMb; + SK_MAC_ADDR *pOldMacAddr; + SK_MAC_ADDR *pNewMacAddr; + SK_U32 Timeout; + SK_U32 NewTimeout; + SK_U32 PrevRlmtMode; + + switch (Event) { + case SK_RLMT_PORTSTART_TIM: /* From RLMT via TIME. */ + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_PORTSTART_TIMEOUT Port %d Event (%d) BEGIN.\n", Para.Para32[0], Event)) + + /* + * Used to start non-preferred ports if the preferred one + * does not come up. + * This timeout needs only be set when starting the first + * (preferred) port. + */ + + if (pAC->Rlmt.Port[Para.Para32[0]].LinkDown) { + + /* PORT_START failed. */ + + for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + if (!pAC->Rlmt.Port[i].PortStarted) { + SkRlmtPortStart(pAC, IoC, i); + } + } + } + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_PORTSTART_TIMEOUT Event (%d) END.\n", Event)) + break; + + case SK_RLMT_LINK_UP: /* From SIRQ. */ + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_LINK_UP Port %d Event (%d) BEGIN.\n", + Para.Para32[0], Event)) + + pRPort = &pAC->Rlmt.Port[Para.Para32[0]]; + + if (!pRPort->PortStarted) { + SK_ERR_LOG( + pAC, + SK_ERRCL_SW, + SKERR_RLMT_E008, + SKERR_RLMT_E008_MSG); + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_LINK_UP Event (%d) EMPTY.\n", Event)) + break; + } + + if (!pRPort->LinkDown) { + /* RA;:;: Any better solution? */ + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_LINK_UP Event (%d) EMPTY.\n", Event)) + break; + } + + SkTimerStop(pAC, IoC, &pRPort->UpTimer); + SkTimerStop(pAC, IoC, &pRPort->DownRxTimer); + SkTimerStop(pAC, IoC, &pRPort->DownTxTimer); + + /* Do something if timer already fired? */ + + pRPort->LinkDown = SK_FALSE; + pRPort->PortState = SK_RLMT_PS_GOING_UP; + pRPort->GuTimeStamp = SkOsGetTime(pAC); + pRPort->BcTimeStamp = 0; + if (pAC->Rlmt.LinksUp == 0) { + SK_HWAC_LINK_LED( + pAC, + IoC, + Para.Para32[0], + SK_LED_ACTIVE); + } + else { + SK_HWAC_LINK_LED( + pAC, + IoC, + Para.Para32[0], + SK_LED_STANDBY); + } + pAC->Rlmt.LinksUp++; + + for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + if (!pAC->Rlmt.Port[i].PortStarted) { + SkRlmtPortStart(pAC, IoC, i); + } + } + + SkRlmtCheckSwitch(pAC, IoC); + + if (pAC->Rlmt.LinksUp >= 2) { + if (pAC->Rlmt.RlmtMode & SK_RLMT_CHECK_LOC_LINK) { + + /* Build the check chain. */ + + SkRlmtBuildCheckChain(pAC); + } + } + + /* + * If the first link comes up, start the periodical + * RLMT timeout. + */ + + if (pAC->GIni.GIMacsFound > 1 && pAC->Rlmt.LinksUp == 1 && + (pAC->Rlmt.RlmtMode & SK_RLMT_CHECK_OTHERS)) { + SkTimerStart( + pAC, + IoC, + &pAC->Rlmt.LocTimer, + pAC->Rlmt.TimeoutValue, + SKGE_RLMT, + SK_RLMT_TIM, + Para); + } + + SkTimerStart( + pAC, + IoC, + &pRPort->UpTimer, + SK_RLMT_PORTUP_TIM_VAL, + SKGE_RLMT, + SK_RLMT_PORTUP_TIM, + Para); + + /* + * Later: + * if (pAC->Rlmt.RlmtMode & SK_RLMT_CHECK_LOC_LINK) && + */ + + if ((pAC->Rlmt.RlmtMode & SK_RLMT_CHECK_LINK) && + (Para2.pParaPtr = SkRlmtBuildPacket( + pAC, + IoC, + Para.Para32[0], + SK_PACKET_ANNOUNCE, + &pAC->Addr.Port[Para.Para32[0]].CurrentMacAddress, + &SkRlmtMcAddr) + ) != NULL) { + + /* Send "new" packet to RLMT multicast address. */ + + SkEventQueue(pAC, SKGE_DRV, SK_DRV_RLMT_SEND, Para2); + } + + if (pAC->Rlmt.RlmtMode & SK_RLMT_CHECK_SEG) { + if ((Para2.pParaPtr = + SkRlmtBuildSpanningTreePacket( + pAC, + IoC, + Para.Para32[0]) + ) != NULL) { + + pAC->Rlmt.Port[Para.Para32[0]].RootIdSet = + SK_FALSE; + pAC->Rlmt.CheckingState |= + SK_RLMT_RCS_SEG | + SK_RLMT_RCS_REPORT_SEG; + + SkEventQueue( + pAC, + SKGE_DRV, + SK_DRV_RLMT_SEND, + Para2); + + SkTimerStart( + pAC, + IoC, + &pAC->Rlmt.SegTimer, + SK_RLMT_SEG_TO_VAL, + SKGE_RLMT, + SK_RLMT_SEG_TIM, + Para); + } + } + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_LINK_UP Event (%d) END.\n", Event)) + break; + + case SK_RLMT_PORTUP_TIM: /* From RLMT via TIME. */ + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_PORTUP_TIM Port %d Event (%d) BEGIN.\n", Para.Para32[0], Event)) + + pRPort = &pAC->Rlmt.Port[Para.Para32[0]]; + + if (pRPort->LinkDown || (pRPort->PortState == SK_RLMT_PS_UP)) { + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_PORTUP_TIM Port %d Event (%d) EMPTY.\n", + Para.Para32[0], + Event)) + break; + } + + pRPort->PortDown = SK_FALSE; + pRPort->PortState = SK_RLMT_PS_UP; + pAC->Rlmt.PortsUp++; + + if (pAC->Rlmt.RlmtState != SK_RLMT_RS_INIT) { + SkRlmtCheckSwitch(pAC, IoC); + + SkEventQueue( + pAC, + SKGE_PNMI, + SK_PNMI_EVT_RLMT_PORT_UP, + Para); + } + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_PORTUP_TIM Event (%d) END.\n", Event)) + break; + + case SK_RLMT_PORTDOWN: /* From RLMT. */ + case SK_RLMT_PORTDOWN_RX_TIM: /* From RLMT via TIME. */ + case SK_RLMT_PORTDOWN_TX_TIM: /* From RLMT via TIME. */ + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_PORTDOWN* Port %d Event (%d) BEGIN.\n", Para.Para32[0], Event)) + + pRPort = &pAC->Rlmt.Port[Para.Para32[0]]; + + if (!pRPort->PortStarted || + (Event == SK_RLMT_PORTDOWN_TX_TIM && + !(pRPort->CheckingState & SK_RLMT_PCS_TX))) { + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_PORTDOWN* Event (%d) EMPTY.\n", Event)) + break; + } + /* Stop port's timers. */ + + SkTimerStop(pAC, IoC, &pRPort->UpTimer); + SkTimerStop(pAC, IoC, &pRPort->DownRxTimer); + SkTimerStop(pAC, IoC, &pRPort->DownTxTimer); + + if (pRPort->PortState != SK_RLMT_PS_LINK_DOWN) { + pRPort->PortState = SK_RLMT_PS_DOWN; + } + + if (!pRPort->PortDown) { + pAC->Rlmt.PortsUp--; + pRPort->PortDown = SK_TRUE; + + SkEventQueue( + pAC, + SKGE_PNMI, + SK_PNMI_EVT_RLMT_PORT_DOWN, + Para); + } + + pRPort->PacketsPerTimeSlot = 0; + pRPort->DataPacketsPerTimeSlot = 0; + pRPort->BpduPacketsPerTimeSlot = 0; +#if 0 + pRPort->RlmtChksPerTimeSlot = 0; + pRPort->RlmtAcksPerTimeSlot = 0; +#endif /* 0 */ + + /* + * RA;:;: To be checked: + * - actions at RLMT_STOP: We should not switch anymore. + */ + + if (pAC->Rlmt.RlmtState != SK_RLMT_RS_INIT) { + if (Para.Para32[0] == pAC->Rlmt.MacActive) { + + /* Active Port went down. */ + + SkRlmtCheckSwitch(pAC, IoC); + } + } + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_PORTDOWN* Event (%d) END.\n", Event)) + break; + + case SK_RLMT_LINK_DOWN: /* From SIRQ. */ + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_LINK_DOWN Port %d Event (%d) BEGIN.\n", + Para.Para32[0], Event)) + + if (!pAC->Rlmt.Port[Para.Para32[0]].LinkDown) { + pAC->Rlmt.LinksUp--; + pAC->Rlmt.Port[Para.Para32[0]].LinkDown = SK_TRUE; + pAC->Rlmt.Port[Para.Para32[0]].PortState = + SK_RLMT_PS_LINK_DOWN; + SK_HWAC_LINK_LED( + pAC, + IoC, + Para.Para32[0], + SK_LED_OFF); + + if (pAC->Rlmt.RlmtMode & SK_RLMT_CHECK_LOC_LINK) { + + /* Build the check chain. */ + + SkRlmtBuildCheckChain(pAC); + } + + /* Ensure that port is marked down. */ + + (void)SkRlmtEvent( + pAC, + IoC, + SK_RLMT_PORTDOWN, + Para); + } + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_LINK_DOWN Event (%d) END.\n", Event)) + break; + + case SK_RLMT_PORT_ADDR: /* From ADDR. */ + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_PORT_ADDR Port %d Event (%d) BEGIN.\n", Para.Para32[0], Event)) + + /* Port's physical MAC address changed. */ + + pOldMacAddr = + &pAC->Addr.Port[Para.Para32[0]].PreviousMacAddress; + pNewMacAddr = + &pAC->Addr.Port[Para.Para32[0]].CurrentMacAddress; + + /* + * NOTE: This is not scalable for solutions where ports are + * checked remotely. There, we need to send an RLMT + * address change packet - and how do we ensure delivery? + */ + + for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + pRPort = &pAC->Rlmt.Port[i]; + for (j = 0; j < pRPort->PortsChecked; j++) { + if (SK_ADDR_EQUAL( + pRPort->PortCheck[j].CheckAddr.a, + pOldMacAddr->a)) { + pRPort->PortCheck[j].CheckAddr = + *pNewMacAddr; + } + } + } + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_PORT_ADDR Event (%d) END.\n", Event)) + break; + + /* ----- RLMT events ----- */ + + case SK_RLMT_START: /* From DRV. */ + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_START Event (%d) BEGIN.\n", Event)) + + if (pAC->Rlmt.RlmtState != SK_RLMT_RS_INIT) { + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_START Event (%d) EMPTY.\n", Event)) + break; + } + + if (pAC->Rlmt.PrefPort >= (SK_U32)pAC->GIni.GIMacsFound) { + SK_ERR_LOG( + pAC, + SK_ERRCL_SW, + SKERR_RLMT_E009, + SKERR_RLMT_E009_MSG); + + /* Change PrefPort to internal default. */ + + Para.Para32[0] = 0xFFFFFFFF; + (void)SkRlmtEvent( + pAC, + IoC, + SK_RLMT_PREFPORT_CHANGE, + Para); + } +#if 0 + if (pAC->GIni.GIMacsFound == 1 && + pAC->Rlmt.RlmtMode != SK_RLMT_CHECK_LINK) { + Para.Para32[0] = SK_RLMT_CHECK_LINK; + (void)SkRlmtEvent( + pAC, + IoC, + SK_RLMT_MODE_CHANGE, + Para); + } +#endif /* 0 */ + pAC->Rlmt.LinksUp = 0; + pAC->Rlmt.PortsUp = 0; + pAC->Rlmt.CheckingState = 0; + pAC->Rlmt.RlmtState = SK_RLMT_RS_NET_DOWN; + + SkRlmtPortStart(pAC, IoC, pAC->Rlmt.PrefPort); + + /* Start Timer (for first port only). */ + + Para2.Para32[0] = pAC->Rlmt.PrefPort; + SkTimerStart( + pAC, + IoC, + &pAC->Rlmt.Port[pAC->Rlmt.PrefPort].UpTimer, + SK_RLMT_PORTSTART_TIM_VAL, + SKGE_RLMT, + SK_RLMT_PORTSTART_TIM, + Para2); + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_START Event (%d) END.\n", Event)) + break; + + case SK_RLMT_STOP: /* From DRV. */ + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_STOP Event (%d) BEGIN.\n", Event)) + + if (pAC->Rlmt.RlmtState == SK_RLMT_RS_INIT) { + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_STOP Event (%d) EMPTY.\n", Event)) + break; + } + + /* Stop RLMT timers. */ + + SkTimerStop(pAC, IoC, &pAC->Rlmt.LocTimer); + SkTimerStop(pAC, IoC, &pAC->Rlmt.SegTimer); + + /* Stop Net. */ + + pAC->Rlmt.RlmtState = SK_RLMT_RS_INIT; + pAC->Rlmt.RootIdSet = SK_FALSE; + Para2.Para32[0] = SK_RLMT_NET_DOWN_FINAL; + SkEventQueue(pAC, SKGE_DRV, SK_DRV_NET_DOWN, Para2); + + /* Stop ports. */ + + for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + if (pAC->Rlmt.Port[i].PortState != SK_RLMT_PS_INIT) { + SkTimerStop( + pAC, + IoC, + &pAC->Rlmt.Port[i].UpTimer); + SkTimerStop( + pAC, + IoC, + &pAC->Rlmt.Port[i].DownRxTimer); + SkTimerStop( + pAC, + IoC, + &pAC->Rlmt.Port[i].DownTxTimer); + + pAC->Rlmt.Port[i].PortState = SK_RLMT_PS_INIT; + pAC->Rlmt.Port[i].RootIdSet = SK_FALSE; + pAC->Rlmt.Port[i].PortStarted = SK_FALSE; + Para2.Para32[0] = i; + SkEventQueue( + pAC, + SKGE_HWAC, + SK_HWEV_PORT_STOP, + Para2); + } + } + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_STOP Event (%d) END.\n", Event)) + break; + + case SK_RLMT_TIM: /* From RLMT via TIME. */ +#if 0 + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_TIM Event (%d) BEGIN.\n", Event)) +#endif /* 0 */ + + if (!(pAC->Rlmt.RlmtMode & SK_RLMT_CHECK_OTHERS) || + pAC->Rlmt.LinksUp == 0) { + + /* + * Mode changed or all links down: + * No more link checking. + */ + + break; + } + +#if 0 + pAC->Rlmt.SwitchCheckCounter--; + if (pAC->Rlmt.SwitchCheckCounter == 0) { + pAC->Rlmt.SwitchCheckCounter; + } +#endif /* 0 */ + + NewTimeout = SK_RLMT_DEF_TO_VAL; + for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + pRPort = &pAC->Rlmt.Port[i]; + if (!pRPort->LinkDown) { + Timeout = SkRlmtCheckPort(pAC, IoC, i); + if (Timeout < NewTimeout) { + NewTimeout = Timeout; + } + + /* + * This counter should be set to 0 for all + * ports before the first frame is sent in the + * next loop. + */ + + pRPort->PacketsPerTimeSlot = 0; + pRPort->DataPacketsPerTimeSlot = 0; + pRPort->BpduPacketsPerTimeSlot = 0; +#if 0 + pRPort->RlmtChksPerTimeSlot = 0; + pRPort->RlmtAcksPerTimeSlot = 0; +#endif /* 0 */ + } + } + pAC->Rlmt.TimeoutValue = NewTimeout; + + if (pAC->Rlmt.LinksUp > 1) { + /* + * If checking remote ports, also send packets if + * (LinksUp == 1) && + * this port checks at least one (remote) port. + */ + + /* + * Must be new loop, as SkRlmtCheckPort can request to + * check segmentation when e.g. checking the last port. + */ + + for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + pRPort = &pAC->Rlmt.Port[i]; + if (!pRPort->LinkDown) { /* !PortDown? */ + SkRlmtSend(pAC, IoC, i); + } + } + } + + SkTimerStart( + pAC, + IoC, + &pAC->Rlmt.LocTimer, + pAC->Rlmt.TimeoutValue, + SKGE_RLMT, + SK_RLMT_TIM, + Para); + + if (pAC->Rlmt.LinksUp > 1 && + (pAC->Rlmt.RlmtMode & SK_RLMT_CHECK_SEG) && + (pAC->Rlmt.CheckingState & SK_RLMT_RCS_START_SEG)) { + SkTimerStart( + pAC, + IoC, + &pAC->Rlmt.SegTimer, + SK_RLMT_SEG_TO_VAL, + SKGE_RLMT, + SK_RLMT_SEG_TIM, + Para); + pAC->Rlmt.CheckingState &= + ~SK_RLMT_RCS_START_SEG; + pAC->Rlmt.CheckingState |= + SK_RLMT_RCS_SEG | SK_RLMT_RCS_REPORT_SEG; + } + +#if 0 + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_TIM Event (%d) END.\n", Event)) +#endif /* 0 */ + break; + + case SK_RLMT_SEG_TIM: + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_SEG_TIM Event (%d) BEGIN.\n", Event)) + +#ifdef DEBUG + for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + SK_U8 InAddr8[6]; + SK_U16 *InAddr; + SK_ADDR_PORT *pAPort; + + InAddr = (SK_U16 *)&InAddr8[0]; + pAPort = &pAC->Addr.Port[i]; + for (j = 0; + j < pAPort->NextExactMatchRlmt; + j++) { + + /* Get exact match address j from port i. */ + + XM_INADDR(IoC, i, XM_EXM(j), InAddr); + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("MC address %d on Port %u: %02x %02x %02x %02x %02x %02x -- %02x %02x %02x %02x %02x %02x.\n", + j, + i, + InAddr8[0], + InAddr8[1], + InAddr8[2], + InAddr8[3], + InAddr8[4], + InAddr8[5], + pAPort->Exact[j].a[0], + pAPort->Exact[j].a[1], + pAPort->Exact[j].a[2], + pAPort->Exact[j].a[3], + pAPort->Exact[j].a[4], + pAPort->Exact[j].a[5])) + } + } +#endif /* DEBUG */ + + pAC->Rlmt.CheckingState &= ~SK_RLMT_RCS_SEG; + + SkRlmtCheckSeg(pAC, IoC); + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_SEG_TIM Event (%d) END.\n", Event)) + break; + + case SK_RLMT_PACKET_RECEIVED: /* From DRV. */ + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_PACKET_RECEIVED Event (%d) BEGIN.\n", Event)) + + /* Should we ignore frames during port switching? */ + +#ifdef DEBUG + pMb = Para.pParaPtr; + if (pMb == NULL) { + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("No mbuf.\n")) + } + else if (pMb->pNext != NULL) { + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("More than one mbuf or pMb->pNext not set.\n")) + } +#endif /* DEBUG */ + + for (pMb = Para.pParaPtr; pMb != NULL; pMb = pNextMb) { + pNextMb = pMb->pNext; + pMb->pNext = NULL; + SkRlmtPacketReceive(pAC, IoC, pMb); + } + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_PACKET_RECEIVED Event (%d) END.\n", Event)) + break; + + case SK_RLMT_STATS_CLEAR: /* From PNMI. */ + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_STATS_CLEAR Event (%d) BEGIN.\n", Event)) + + /* Clear statistics for virtual and physical ports. */ + + for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + pAC->Rlmt.Port[i].TxHelloCts = 0; + pAC->Rlmt.Port[i].RxHelloCts = 0; + pAC->Rlmt.Port[i].TxSpHelloReqCts = 0; + pAC->Rlmt.Port[i].RxSpHelloCts = 0; + } + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_STATS_CLEAR Event (%d) END.\n", Event)) + break; + + case SK_RLMT_STATS_UPDATE: /* From PNMI. */ +#if 0 + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_STATS_UPDATE Event (%d) BEGIN.\n", Event)) + + /* Update statistics. */ + + /* Currently always up-to-date. */ + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_STATS_UPDATE Event (%d) END.\n", Event)) +#endif /* 0 */ + break; + + case SK_RLMT_PREFPORT_CHANGE: /* From PNMI. */ + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_PREFPORT_CHANGE to Port %d Event (%d) BEGIN.\n", Para.Para32[0], Event)) + + /* 0xFFFFFFFF == auto-mode. */ + + if (Para.Para32[0] == 0xFFFFFFFF) { + pAC->Rlmt.PrefPort = SK_RLMT_DEF_PREF_PORT; + } + else { + if (Para.Para32[0] >= (SK_U32)pAC->GIni.GIMacsFound) { + SK_ERR_LOG( + pAC, + SK_ERRCL_SW, + SKERR_RLMT_E010, + SKERR_RLMT_E010_MSG); + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_PREFPORT_CHANGE Event (%d) EMPTY.\n", Event)) + break; + } + + pAC->Rlmt.PrefPort = Para.Para32[0]; + } + + pAC->Rlmt.MacPreferred = Para.Para32[0]; + + SkRlmtCheckSwitch(pAC, IoC); + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_PREFPORT_CHANGE Event (%d) END.\n", Event)) + break; + + case SK_RLMT_MODE_CHANGE: /* From PNMI. */ + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_MODE_CHANGE Event (%d) BEGIN.\n", Event)) + + if (pAC->GIni.GIMacsFound < 2) { + pAC->Rlmt.RlmtMode = SK_RLMT_CHECK_LINK; + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("Forced RLMT mode to CLS on single link adapter.\n")) + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_MODE_CHANGE Event (%d) EMPTY.\n", + Event)) + break; + } + + /* Update RLMT mode. */ + + PrevRlmtMode = pAC->Rlmt.RlmtMode; + pAC->Rlmt.RlmtMode = Para.Para32[0] | SK_RLMT_CHECK_LINK; + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("RLMT: Changed Mode to %X.\n", pAC->Rlmt.RlmtMode)) + + if ((PrevRlmtMode & SK_RLMT_CHECK_LOC_LINK) != + (pAC->Rlmt.RlmtMode & SK_RLMT_CHECK_LOC_LINK)) { + if (!(PrevRlmtMode & SK_RLMT_CHECK_OTHERS) && + pAC->GIni.GIMacsFound > 1 && + pAC->Rlmt.PortsUp == 1) { + SkTimerStart( + pAC, + IoC, + &pAC->Rlmt.LocTimer, + pAC->Rlmt.TimeoutValue, + SKGE_RLMT, + SK_RLMT_TIM, + Para); + } + } + + if ((PrevRlmtMode & SK_RLMT_CHECK_SEG) != + (pAC->Rlmt.RlmtMode & SK_RLMT_CHECK_SEG)) { + + for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) { + (void)SkAddrMcClear( + pAC, + IoC, + i, + SK_ADDR_PERMANENT | SK_MC_SW_ONLY); + + /* Add RLMT MC address. */ + + (void)SkAddrMcAdd( + pAC, + IoC, + i, + &SkRlmtMcAddr, + SK_ADDR_PERMANENT); + + if (pAC->Rlmt.RlmtMode & SK_RLMT_CHECK_SEG) { + /* Add BPDU MC address. */ + + (void)SkAddrMcAdd( + pAC, + IoC, + i, + &BridgeMcAddr, + SK_ADDR_PERMANENT); + + if (pAC->Rlmt.RlmtState != + SK_RLMT_RS_INIT) { + if (!pAC->Rlmt.Port[i].LinkDown && + (Para2.pParaPtr = + SkRlmtBuildSpanningTreePacket( + pAC, + IoC, + i) + ) != NULL) { + + pAC->Rlmt.Port[i + ].RootIdSet = + SK_FALSE; + + SkEventQueue( + pAC, + SKGE_DRV, + SK_DRV_RLMT_SEND, + Para2); + } + } + } + + (void)SkAddrMcUpdate(pAC, IoC, i); + } + + if (pAC->Rlmt.RlmtMode & SK_RLMT_CHECK_SEG) { + SkTimerStart( + pAC, + IoC, + &pAC->Rlmt.SegTimer, + SK_RLMT_SEG_TO_VAL, + SKGE_RLMT, + SK_RLMT_SEG_TIM, + Para); + } + } + + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("SK_RLMT_MODE_CHANGE Event (%d) END.\n", Event)) + break; + + default: /* Create error log entry. */ + SK_DBG_MSG( + pAC, + SK_DBGMOD_RLMT, + SK_DBGCAT_CTRL, + ("Unknown RLMT Event %d.\n", Event)) + + SK_ERR_LOG( + pAC, + SK_ERRCL_SW, + SKERR_RLMT_E003, + SKERR_RLMT_E003_MSG); + break; + } + + return (0); +} /* SkRlmtEvent */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/sktimer.c linux/drivers/net/sk98lin/sktimer.c --- v2.2.13/linux/drivers/net/sk98lin/sktimer.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/sktimer.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,290 @@ +/****************************************************************************** + * + * Name: sktimer.c + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.11 $ + * Date: $Date: 1998/12/17 13:24:13 $ + * Purpose: High level timer functions. + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skge.c" for further information. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: sktimer.c,v $ + * Revision 1.11 1998/12/17 13:24:13 gklug + * fix: restart problem: do NOT destroy timer queue if init 1 is done + * + * Revision 1.10 1998/10/15 15:11:36 gklug + * fix: ID_sccs to SysKonnectFileId + * + * Revision 1.9 1998/09/15 15:15:04 cgoos + * Changed TRUE/FALSE to SK_TRUE/SK_FALSE + * + * Revision 1.8 1998/09/08 08:47:55 gklug + * add: init level handling + * + * Revision 1.7 1998/08/19 09:50:53 gklug + * fix: remove struct keyword from c-code (see CCC) add typedefs + * + * Revision 1.6 1998/08/17 13:43:13 gklug + * chg: Parameter will be union of 64bit para, 2 times SK_U32 or SK_PTR + * + * Revision 1.5 1998/08/14 07:09:14 gklug + * fix: chg pAc -> pAC + * + * Revision 1.4 1998/08/07 12:53:46 gklug + * fix: first compiled version + * + * Revision 1.3 1998/08/07 09:31:53 gklug + * fix: delta spelling + * + * Revision 1.2 1998/08/07 09:31:02 gklug + * adapt functions to new c coding conventions + * rmv: "fast" handling + * chg: inserting of new timer in queue. + * chg: event queue generation when timer runs out + * + * Revision 1.1 1998/08/05 11:27:55 gklug + * first version: adapted from SMT + * + * + * + * + ******************************************************************************/ + + +/* + Event queue and dispatcher +*/ +static const char SysKonnectFileId[] = + "$Header: /usr56/projects/ge/schedule/sktimer.c,v 1.11 1998/12/17 13:24:13 gklug Exp $" ; + +#include "h/skdrv1st.h" /* Driver Specific Definitions */ +#include "h/skdrv2nd.h" /* Adapter Control- and Driver specific Def. */ + +#ifdef __C2MAN__ +/* + Event queue management. + + General Description: + + */ +intro() +{} +#endif + + +/* Forward declaration */ +static void timer_done(SK_AC *pAC,SK_IOC Ioc,int Restart); + + +/* + * Inits the software timer + * + * needs to be called during Init level 1. + */ +void SkTimerInit( +SK_AC *pAC, /* Adapters context */ +SK_IOC Ioc, /* IoContext */ +int Level) /* Init Level */ +{ + switch (Level) { + case SK_INIT_DATA: + pAC->Tim.StQueue = 0 ; + break; + case SK_INIT_IO: + SkHwtInit(pAC,Ioc) ; + SkTimerDone(pAC, Ioc); + break; + default: + break; + } +} + +/* + * Stops a high level timer + * - If a timer is not in the queue the function returns normally, too. + */ +void SkTimerStop( +SK_AC *pAC, /* Adapters context */ +SK_IOC Ioc, /* IoContext */ +SK_TIMER *pTimer) /* Timer Pointer to be started */ +{ + SK_TIMER **ppTimPrev ; + SK_TIMER *pTm ; + + /* + * remove timer from queue + */ + pTimer->TmActive = SK_FALSE ; + if (pAC->Tim.StQueue == pTimer && !pTimer->TmNext) { + SkHwtStop(pAC,Ioc) ; + } + for (ppTimPrev = &pAC->Tim.StQueue ; (pTm = *ppTimPrev) ; + ppTimPrev = &pTm->TmNext ) { + if (pTm == pTimer) { + /* + * Timer found in queue + * - dequeue it and + * - correct delta of the next timer + */ + *ppTimPrev = pTm->TmNext ; + + if (pTm->TmNext) { + /* correct delta of next timer in queue */ + pTm->TmNext->TmDelta += pTm->TmDelta ; + } + return ; + } + } +} + +/* + * Start a high level software timer + */ +void SkTimerStart( +SK_AC *pAC, /* Adapters context */ +SK_IOC Ioc, /* IoContext */ +SK_TIMER *pTimer, /* Timer Pointer to be started */ +SK_U32 Time, /* Time value */ +SK_U32 Class, /* Event Class for this timer */ +SK_U32 Event, /* Event Value for this timer */ +SK_EVPARA Para) /* Event Parameter for this timer */ +{ + SK_TIMER **ppTimPrev ; + SK_TIMER *pTm ; + SK_U32 Delta ; + + Time /= 16 ; /* input is uS, clock ticks are 16uS */ + if (!Time) + Time = 1 ; + + SkTimerStop(pAC,Ioc,pTimer) ; + + pTimer->TmClass = Class ; + pTimer->TmEvent = Event ; + pTimer->TmPara = Para ; + pTimer->TmActive = SK_TRUE ; + + if (!pAC->Tim.StQueue) { + /* First Timer to be started */ + pAC->Tim.StQueue = pTimer ; + pTimer->TmNext = 0 ; + pTimer->TmDelta = Time ; + SkHwtStart(pAC,Ioc,Time) ; + return ; + } + + /* + * timer correction + */ + timer_done(pAC,Ioc,0) ; + + /* + * find position in queue + */ + Delta = 0 ; + for (ppTimPrev = &pAC->Tim.StQueue ; (pTm = *ppTimPrev) ; + ppTimPrev = &pTm->TmNext ) { + if (Delta + pTm->TmDelta > Time) { + /* Position found */ + /* Here the timer needs to be inserted. */ + break ; + } + Delta += pTm->TmDelta ; + } + + /* insert in queue */ + *ppTimPrev = pTimer ; + pTimer->TmNext = pTm ; + pTimer->TmDelta = Time - Delta ; + + if (pTm) { + /* There is a next timer + * -> correct its Delta value. + */ + pTm->TmDelta -= pTimer->TmDelta ; + } + + /* + * start new with first + */ + SkHwtStart(pAC,Ioc,pAC->Tim.StQueue->TmDelta) ; +} + + +void SkTimerDone( +SK_AC *pAC, /* Adapters context */ +SK_IOC Ioc) /* IoContext */ +{ + timer_done(pAC,Ioc,1) ; +} + + +static void timer_done( +SK_AC *pAC, /* Adapters context */ +SK_IOC Ioc, /* IoContext */ +int Restart) /* Do we need to restart the Hardware timer ? */ +{ + SK_U32 Delta ; + SK_TIMER *pTm ; + SK_TIMER *pTComp ; /* Timer completed now now */ + SK_TIMER **ppLast ; /* Next field of Last timer to be deq */ + int Done = 0 ; + + Delta = SkHwtRead(pAC,Ioc) ; + ppLast = &pAC->Tim.StQueue ; + pTm = pAC->Tim.StQueue ; + while (pTm && !Done) { + if (Delta >= pTm->TmDelta) { + /* Timer ran out */ + pTm->TmActive = SK_FALSE ; + Delta -= pTm->TmDelta ; + ppLast = &pTm->TmNext ; + pTm = pTm->TmNext ; + } else { + /* We found the first timer that did not run out */ + pTm->TmDelta -= Delta ; + Delta = 0 ; + Done = 1 ; + } + } + *ppLast = 0 ; + /* + * pTm points to the first Timer that did not run out. + * StQueue points to the first Timer that run out. + */ + + for ( pTComp = pAC->Tim.StQueue ; pTComp ; pTComp = pTComp->TmNext) { + SkEventQueue(pAC,pTComp->TmClass, pTComp->TmEvent, + pTComp->TmPara) ; + } + + /* Set head of timer queue to the first timer that did not run out */ + pAC->Tim.StQueue = pTm ; + + if (Restart && pAC->Tim.StQueue) { + /* Restart HW timer */ + SkHwtStart(pAC,Ioc,pAC->Tim.StQueue->TmDelta) ; + } +} + +/* End of file */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/skvpd.c linux/drivers/net/sk98lin/skvpd.c --- v2.2.13/linux/drivers/net/sk98lin/skvpd.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/skvpd.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,1198 @@ +/****************************************************************************** + * + * Name: skvpd.c + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.24 $ + * Date: $Date: 1999/03/11 14:25:49 $ + * Purpose: Shared software to read and write VPD data + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skge.c" for further information. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: skvpd.c,v $ + * Revision 1.24 1999/03/11 14:25:49 malthoff + * Replace __STDC__ with SK_KR_PROTO. + * + * Revision 1.23 1999/01/11 15:13:11 gklug + * fix: syntax error + * + * Revision 1.22 1998/10/30 06:41:15 gklug + * rmv: WARNING + * + * Revision 1.21 1998/10/29 07:15:14 gklug + * fix: Write Stream function needs verify. + * + * Revision 1.20 1998/10/28 18:05:08 gklug + * chg: no DEBUG in VpdMayWrite + * + * Revision 1.19 1998/10/28 15:56:11 gklug + * fix: Return len at end of ReadStream + * fix: Write even less than 4 bytes correctly + * + * Revision 1.18 1998/10/28 09:00:47 gklug + * fix: unreferenced local vars + * + * Revision 1.17 1998/10/28 08:25:45 gklug + * fix: WARNING + * + * Revision 1.16 1998/10/28 08:17:30 gklug + * fix: typo + * + * Revision 1.15 1998/10/28 07:50:32 gklug + * fix: typo + * + * Revision 1.14 1998/10/28 07:20:38 gklug + * chg: Interface functions to use IoC as parameter as well + * fix: VpdRead/WriteDWord now return SK_U32 + * chg: VPD_IN/OUT names conform to SK_IN/OUT + * add: usage of VPD_IN/OUT8 macros + * add: VpdRead/Write Stream functions to r/w a stream of data + * fix: VpdTransferBlock swapped illeagal + * add: VpdMayWrite + * + * Revision 1.13 1998/10/22 10:02:37 gklug + * fix: SysKonnectFileId typo + * + * Revision 1.12 1998/10/20 10:01:01 gklug + * fix: parameter to SkOsGetTime + * + * Revision 1.11 1998/10/15 12:51:48 malthoff + * Remove unrequired parameter p in vpd_setup_para(). + * + * Revision 1.10 1998/10/08 14:52:43 malthoff + * Remove CvsId by SysKonnectFileId. + * + * Revision 1.9 1998/09/16 07:33:52 malthoff + * remove memcmp() by SK_MEMCMP and + * memcpy() by SK_MEMCPY() to be + * independant from the 'C' Standard Library. + * + * Revision 1.8 1998/08/19 12:52:35 malthoff + * compiler fix: use SK_VPD_KEY instead of S_VPD. + * + * Revision 1.7 1998/08/19 08:14:01 gklug + * fix: remove struct keyword as much as possible from the c-code (see CCC) + * + * Revision 1.6 1998/08/18 13:03:58 gklug + * SkOsGetTime now returns SK_U64 + * + * Revision 1.5 1998/08/18 08:17:29 malthoff + * Ensure we issue a VPD read in vpd_read_dword(). + * Discard all VPD keywords other than Vx or Yx, where + * x is '0..9' or 'A..Z'. + * + * Revision 1.4 1998/07/03 14:52:19 malthoff + * Add category SK_DBGCAT_FATAL to some debug macros. + * bug fix: correct the keyword name check in vpd_write(). + * + * Revision 1.3 1998/06/26 11:16:53 malthoff + * Correct the modified File Identifier. + * + * Revision 1.2 1998/06/26 11:13:43 malthoff + * Modify the File Identifier. + * + * Revision 1.1 1998/06/19 14:11:08 malthoff + * Created, Tests with AIX were performed successfully + * + * + ******************************************************************************/ + +/* + Please refer skvpd.txt for infomation how to include this module + */ +static const char SysKonnectFileId[] = + "@(#)$Id: skvpd.c,v 1.24 1999/03/11 14:25:49 malthoff Exp $ (C) SK" ; + +#include "h/skdrv1st.h" +#include "h/sktypes.h" +#include "h/skdebug.h" +#include "h/skdrv2nd.h" + +/* + * Static functions + */ +#ifndef SK_KR_PROTO +static SK_VPD_PARA *vpd_find_para( + SK_AC *pAC, + char *key, + SK_VPD_PARA *p) ; +#else /* SK_KR_PROTO */ +static SK_VPD_PARA *vpd_find_para() ; +#endif /* SK_KR_PROTO */ + +/* + * waits for a completetion of a VPD transfer + * The VPD transfer must complete within SK_TICKS_PER_SEC/16 + * + * returns 0: success, transfer completes + * error exit(9) with a error message + */ +static int VpdWait( +SK_AC *pAC, /* Adapters context */ +SK_IOC IoC, /* IO Context */ +int event) /* event to wait for (VPD_READ / VPD_write) completion*/ +{ + SK_U64 start_time ; + SK_U16 state ; + + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_CTRL, + ("vpd wait for %s\n",event?"Write":"Read")) ; + start_time = SkOsGetTime(pAC) ; + do { + if (SkOsGetTime(pAC) - start_time > SK_TICKS_PER_SEC/16) { + VPD_STOP(pAC,IoC) ; + SK_DBG_MSG(pAC,SK_DBGMOD_VPD, + SK_DBGCAT_FATAL|SK_DBGCAT_ERR, + ("ERROR:vpd wait timeout\n")) ; + return(1) ; + } + VPD_IN16(pAC,IoC,PCI_VPD_ADR_REG,&state) ; + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_CTRL, + ("state = %x, event %x\n",state,event)) ; + } while((int)(state & PCI_VPD_FLAG) == event) ; + + return(0) ; +} + + +/* + * Read the dword at address 'addr' from the VPD EEPROM. + * + * Needed Time: MIN 1,3 ms MAX 2,6 ms + * + * Note: The DWord is returned in the endianess of the machine the routine + * is running on. + * + * Returns the data read. + */ +SK_U32 VpdReadDWord( +SK_AC *pAC, /* Adapters context */ +SK_IOC IoC, /* IO Context */ +int addr) /* VPD address */ +{ + SK_U32 Rtv ; + + /* start VPD read */ + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_CTRL, + ("vpd read dword at 0x%x\n",addr)) ; + addr &= ~VPD_WRITE ; /* ensure the R/W bit is set to read */ + + VPD_OUT16(pAC,IoC,PCI_VPD_ADR_REG, (SK_U16) addr) ; + + /* ignore return code here */ + (void)VpdWait(pAC,IoC,VPD_READ) ; + + /* Don't swap here, it's a data stream of bytes */ + Rtv = 0 ; + + VPD_IN32(pAC,IoC,PCI_VPD_DAT_REG,&Rtv) ; + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_CTRL, + ("vpd read dword data = 0x%x\n",Rtv)) ; + return (Rtv) ; +} + +/* + Write the dword 'data' at address 'addr' into the VPD EEPROM, and + verify that the data is written. + + Needed Time: + +. MIN MAX +. ------------------------------------------------------------------- +. write 1.8 ms 3.6 ms +. internal write cyles 0.7 ms 7.0 ms +. ------------------------------------------------------------------- +. over all program time 2.5 ms 10.6 ms +. read 1.3 ms 2.6 ms +. ------------------------------------------------------------------- +. over all 3.8 ms 13.2 ms +. + + + Returns 0: success + 1: error, I2C transfer does not terminate + 2: error, data verify error + + */ +static int VpdWriteDWord( +SK_AC *pAC, /* pAC pointer */ +SK_IOC IoC, /* IO Context */ +int addr, /* VPD address */ +SK_U32 data) /* VPD data to write */ +{ + /* start VPD write */ + /* Don't swap here, it's a data stream of bytes */ + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_CTRL, + ("vpd write dword at addr 0x%x, data = 0x%x\n",addr,data)) ; + VPD_OUT32(pAC,IoC,PCI_VPD_DAT_REG, (SK_U32)data) ; + /* But do it here */ + addr |= VPD_WRITE ; + + VPD_OUT16(pAC,IoC,PCI_VPD_ADR_REG, (SK_U16)(addr | VPD_WRITE)) ; + + /* this may take up to 10,6 ms */ + if (VpdWait(pAC,IoC,VPD_WRITE)) { + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR, + ("Write Timed Out\n")) ; + return(1) ; + } ; + + /* verify data */ + if (VpdReadDWord(pAC,IoC,addr) != data) { + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR|SK_DBGCAT_FATAL, + ("Data Verify Error\n")) ; + return(2) ; + } + return(0) ; +} + +/* + * Read one Stream of 'len' bytes of VPD data, starting at 'addr' from + * or to the I2C EEPROM. + * + * Returns number of bytes read / written. + */ +static int VpdWriteStream( +SK_AC *pAC, /* Adapters context */ +SK_IOC IoC, /* IO Context */ +char *buf, /* data buffer */ +int Addr, /* VPD start address */ +int Len) /* number of bytes to read / to write */ +{ + int i ; + int j ; + SK_U16 AdrReg ; + int Rtv ; + SK_U8 * pComp; /* Compare pointer */ + SK_U8 Data ; /* Input Data for Compare */ + + /* Init Compare Pointer */ + pComp = (SK_U8 *) buf; + + for (i=0; i < Len; i ++, buf++) { + if ((i%sizeof(SK_U32)) == 0) { + /* + * At the begin of each cycle read the Data Reg + * So it is initialized even if only a few bytes + * are written. + */ + AdrReg = (SK_U16) Addr ; + AdrReg &= ~VPD_WRITE ; /* READ operation */ + + VPD_OUT16(pAC,IoC,PCI_VPD_ADR_REG, AdrReg) ; + + /* ignore return code here */ + Rtv = VpdWait(pAC,IoC,VPD_READ) ; + if (Rtv != 0) { + return(i) ; + } + } + + /* Write current Byte */ + VPD_OUT8(pAC,IoC,PCI_VPD_DAT_REG+(i%sizeof(SK_U32)), + *(SK_U8*)buf) ; + + if (((i%sizeof(SK_U32)) == 3) || (i == (Len - 1))) { + /* New Address needs to be written to VPD_ADDR reg */ + AdrReg = (SK_U16) Addr ; + Addr += sizeof(SK_U32); + AdrReg |= VPD_WRITE ; /* WRITE operation */ + + VPD_OUT16(pAC,IoC,PCI_VPD_ADR_REG, AdrReg) ; + + /* Wait for termination */ + Rtv = VpdWait(pAC,IoC,VPD_WRITE) ; + if (Rtv != 0) { + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR, + ("Write Timed Out\n")) ; + return(i - (i%sizeof(SK_U32))) ; + } + + /* + * Now re-read to verify + */ + AdrReg &= ~VPD_WRITE ; /* READ operation */ + + VPD_OUT16(pAC,IoC,PCI_VPD_ADR_REG, AdrReg) ; + + /* Wait for termination */ + Rtv = VpdWait(pAC,IoC,VPD_READ) ; + if (Rtv != 0) { + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR, + ("Verify Timed Out\n")) ; + return(i - (i%sizeof(SK_U32))) ; + } + + for (j = 0; j <= (int) (i%sizeof(SK_U32)); + j ++, pComp ++ ) { + VPD_IN8(pAC,IoC,PCI_VPD_DAT_REG+j, &Data) ; + if (Data != *pComp) { + /* Verify Error */ + SK_DBG_MSG(pAC,SK_DBGMOD_VPD, + SK_DBGCAT_ERR, + ("WriteStream Verify Error\n")); + return(i - (i%sizeof(SK_U32)) + j); + } + } + + } + } + + return(Len); +} + + +/* + * Read one Stream of 'len' bytes of VPD data, starting at 'addr' from + * or to the I2C EEPROM. + * + * Returns number of bytes read / written. + */ +static int VpdReadStream( +SK_AC *pAC, /* Adapters context */ +SK_IOC IoC, /* IO Context */ +char *buf, /* data buffer */ +int Addr, /* VPD start address */ +int Len) /* number of bytes to read / to write */ +{ + int i ; + SK_U16 AdrReg ; + int Rtv ; + + for (i=0; i < Len; i ++, buf++) { + if ((i%sizeof(SK_U32)) == 0) { + /* New Address needs to be written to VPD_ADDR reg */ + AdrReg = (SK_U16) Addr ; + Addr += sizeof(SK_U32); + AdrReg &= ~VPD_WRITE ; /* READ operation */ + + VPD_OUT16(pAC,IoC,PCI_VPD_ADR_REG, AdrReg) ; + + /* ignore return code here */ + Rtv = VpdWait(pAC,IoC,VPD_READ) ; + if (Rtv != 0) { + return(i) ; + } + + } + VPD_IN8(pAC,IoC,PCI_VPD_DAT_REG+(i%sizeof(SK_U32)), + (SK_U8 *)buf) ; + } + + return(Len) ; +} + +/* + * Read ore wirtes 'len' bytes of VPD data, starting at 'addr' from + * or to the I2C EEPROM. + * + * Returns number of bytes read / written. + */ +static int VpdTransferBlock( +SK_AC *pAC, /* Adapters context */ +SK_IOC IoC, /* IO Context */ +char *buf, /* data buffer */ +int addr, /* VPD start address */ +int len, /* number of bytes to read / to write */ +int dir) /* transfer direction may be VPD_READ or VPD_WRITE */ +{ + int Rtv ; /* Return value */ + int vpd_rom_size ; + SK_U32 our_reg2 ; + + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_CTRL, + ("vpd %s block, addr = 0x%x, len = %d\n", + dir?"write":"read",addr,len)) ; + + if (len == 0) + return (0) ; + + VPD_IN32(pAC,IoC,PCI_OUR_REG_2,&our_reg2) ; + vpd_rom_size = 256 << ((our_reg2 & PCI_VPD_ROM_SZ) >> 14); + if (addr > vpd_rom_size - 4) { + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR|SK_DBGCAT_FATAL, + ("Address error: 0x%x, exp. < 0x%x\n", + addr, vpd_rom_size - 4)) ; + return (0) ; + } + if (addr + len > vpd_rom_size) { + len = vpd_rom_size - addr ; + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR, + ("Warning: len was cut to %d\n",len)) ; + } + + if (dir == VPD_READ) { + Rtv = VpdReadStream(pAC, IoC, buf, addr, len); + } else { + Rtv = VpdWriteStream(pAC, IoC, buf, addr, len); + } + + return (Rtv) ; +} + +#ifdef SKDIAG + +/* + * Read 'len' bytes of VPD data, starting at 'addr'. + * + * Returns number of bytes read. + */ +int VpdReadBlock( +SK_AC *pAC, /* pAC pointer */ +SK_IOC IoC, /* IO Context */ +char *buf, /* buffer were the data should be stored */ +int addr, /* start reading at the VPD address */ +int len) /* number of bytes to read */ +{ + return (VpdTransferBlock(pAC, IoC, buf, addr, len, VPD_READ)) ; +} + +/* + * Write 'len' bytes of *but to the VPD EEPROM, starting at 'addr'. + * + * Returns number of bytes writes. + */ +int VpdWriteBlock( +SK_AC *pAC, /* pAC pointer */ +SK_IOC IoC, /* IO Context */ +char *buf, /* buffer, holds the data to write */ +int addr, /* start writing at the VPD address */ +int len) /* number of bytes to write */ +{ + return (VpdTransferBlock(pAC, IoC, buf, addr, len, VPD_WRITE)) ; +} +#endif /* SKDIAG */ + +/* + * (re)initialize the VPD buffer + * + * Reads the VPD data from the EEPROM into the VPD buffer. + * Get the remaining read only and read / write space. + * + * return 0: success + * 1: fatal VPD error + */ +static int VpdInit( +SK_AC *pAC, /* Adapters context */ +SK_IOC IoC) /* IO Context */ +{ + SK_VPD_PARA *r, rp ; /* RW or RV */ + int i ; + unsigned char x ; + + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_INIT,("VpdInit .. ")) ; + /* read the VPD data into the VPD buffer */ + if (VpdTransferBlock(pAC,IoC,pAC->vpd.vpd_buf,0,VPD_SIZE,VPD_READ) + != VPD_SIZE) { + + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR, + ("Block Read Error\n")) ; + return(1) ; + } + + /* find the end tag of the RO area */ + if (!(r = vpd_find_para(pAC,VPD_RV,&rp))) { + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR | SK_DBGCAT_FATAL, + ("Encoding Error: RV Tag not found\n")) ; + return (1) ; + } + if (r->p_val + r->p_len > pAC->vpd.vpd_buf + VPD_SIZE/2) { + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR | SK_DBGCAT_FATAL, + ("Encoding Error: Invalid VPD struct size\n")) ; + return (1) ; + } + pAC->vpd.v.vpd_free_ro = r->p_len - 1 ; + + /* test the checksum */ + for (i = 0, x = 0; (unsigned)i<=(unsigned)VPD_SIZE/2 - r->p_len; i++) { + x += pAC->vpd.vpd_buf[i] ; + } + if (x != 0) { + /* checksum error */ + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR | SK_DBGCAT_FATAL, + ("VPD Checksum Error\n")) ; + return (1) ; + } + + /* find and check the end tag of the RW area */ + if (!(r = vpd_find_para(pAC,VPD_RW,&rp))) { + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR | SK_DBGCAT_FATAL, + ("Encoding Error: RV Tag not found\n")) ; + return (1) ; + } + if (r->p_val < pAC->vpd.vpd_buf + VPD_SIZE/2) { + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR | SK_DBGCAT_FATAL, + ("Encoding Error: Invalid VPD struct size\n")) ; + return (1) ; + } + pAC->vpd.v.vpd_free_rw = r->p_len ; + + /* everything seems to be ok */ + pAC->vpd.v.vpd_status |= VPD_VALID ; + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_INIT, + ("done. Free RO = %d, Free RW = %d\n", + pAC->vpd.v.vpd_free_ro, pAC->vpd.v.vpd_free_rw)) ; + + return(0) ; +} + +/* + * find the Keyword 'key' in the VPD buffer and fills the + * parameter sturct 'p' with it's values + * + * returns *p success + * 0: parameter was not found or VPD encoding error + */ +static SK_VPD_PARA *vpd_find_para( +SK_AC *pAC, /* common data base */ +char *key, /* keyword to find (e.g. "MN") */ +SK_VPD_PARA *p) /* parameter description struct */ +{ + char *v ; /* points to vpd buffer */ + int max ; /* Maximum Number of Iterations */ + + v = pAC->vpd.vpd_buf ; + max = 128 ; + + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_CTRL, + ("vpd find para %s .. ",key)) ; + + /* check mandatory resource type ID string (Product Name) */ + if (*v != (char) RES_ID) { + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR | SK_DBGCAT_FATAL, + ("Error: 0x%x missing\n",RES_ID)) ; + return (0) ; + } + + if (strcmp(key,VPD_NAME) == 0) { + p->p_len = VPD_GET_RES_LEN(v) ; + p->p_val = VPD_GET_VAL(v) ; + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_CTRL, + ("found, len = %d\n",p->p_len)) ; + return(p) ; + } + + v += 3 + VPD_GET_RES_LEN(v) + 3 ; + for ( ; ; ) { + if (SK_MEMCMP(key,v,2) == 0) { + p->p_len = VPD_GET_VPD_LEN(v) ; + p->p_val = VPD_GET_VAL(v) ; + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_CTRL, + ("found, len = %d\n",p->p_len)) ; + return (p) ; + } + + /* exit when reaching the "RW" Tag or the maximum of itera. */ + max-- ; + if (SK_MEMCMP(VPD_RW,v,2) == 0 || max == 0) { + break ; + } + + if (SK_MEMCMP(VPD_RV,v,2) == 0) { + v += 3 + VPD_GET_VPD_LEN(v) + 3 ; /* skip VPD-W */ + } else { + v += 3 + VPD_GET_VPD_LEN(v) ; + } + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_CTRL, + ("scanning '%c%c' len = %d\n",v[0],v[1],v[2])) ; + } + +#ifdef DEBUG + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_CTRL,("not found\n")) ; + if (max == 0) { + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR | SK_DBGCAT_FATAL, + ("Key/Len Encoding error\n")) ; + } +#endif + return (0) ; +} + +/* + * Move 'n' bytes. Begin with the last byte if 'n' is > 0, + * Start with the last byte if n is < 0. + * + * returns nothing + */ +static void vpd_move_para( +char *start, /* start of memory block */ +char *end, /* end of memory block to move */ +int n) /* number of bytes the memory block has to be moved */ +{ + char *p ; + int i ; /* number of byte copied */ + + if (n == 0) + return ; + + i = end - start + 1 ; + if (n < 0) { + p = start + n ; + while (i != 0) { + *p++ = *start++ ; + i-- ; + } + } else { + p = end + n ; + while (i != 0) { + *p-- = *end-- ; + i-- ; + } + } +} + +/* + * setup the VPD keyword 'key' at 'ip'. + * + * returns nothing + */ +static void vpd_insert_key( +char *key, /* keyword to insert */ +char *buf, /* buffer with the keyword value */ +int len, /* length of the value string */ +char *ip) /* inseration point */ +{ + SK_VPD_KEY *p ; + + p = (SK_VPD_KEY *) ip ; + p->p_key[0] = key[0] ; + p->p_key[1] = key[1] ; + p->p_len = (unsigned char) len ; + SK_MEMCPY(&p->p_val,buf,len) ; +} + +/* + * Setup the VPD end tag "RV" / "RW". + * Also correct the remaining space variables vpd_free_ro / vpd_free_rw. + * + * returns 0: success + * 1: encoding error + */ +static int vpd_mod_endtag( +SK_AC *pAC, /* common data base */ +char *etp) /* end pointer input position */ +{ + SK_VPD_KEY *p ; + unsigned char x ; + int i ; + + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_CTRL, + ("vpd modify endtag at 0x%x = '%c%c'\n",etp,etp[0],etp[1])) ; + + p = (SK_VPD_KEY *) etp ; + + if (p->p_key[0] != 'R' || (p->p_key[1] != 'V' && p->p_key[1] != 'W')) { + /* something wrong here, encoding error */ + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR | SK_DBGCAT_FATAL, + ("Encoding Error: invalid end tag\n")) ; + return(1) ; + } + if (etp > pAC->vpd.vpd_buf + VPD_SIZE/2) { + /* create "RW" tag */ + p->p_len = (unsigned char)(pAC->vpd.vpd_buf+VPD_SIZE-etp-3-1) ; + pAC->vpd.v.vpd_free_rw = (int) p->p_len ; + i = pAC->vpd.v.vpd_free_rw ; + etp += 3 ; + } else { + /* create "RV" tag */ + p->p_len = (unsigned char)(pAC->vpd.vpd_buf+VPD_SIZE/2-etp-3) ; + pAC->vpd.v.vpd_free_ro = (int) p->p_len - 1 ; + + /* setup checksum */ + for (i = 0, x = 0; i < VPD_SIZE/2 - p->p_len; i++) { + x += pAC->vpd.vpd_buf[i] ; + } + p->p_val = (char) 0 - x ; + i = pAC->vpd.v.vpd_free_ro ; + etp += 4 ; + } + while (i) { + *etp++ = 0x00 ; + i-- ; + } + + return (0) ; +} + +/* + * Insert a VPD keyword into the VPD buffer. + * + * The keyword 'key' is inserted at the position 'ip' in the + * VPD buffer. + * The keywords behind the input position will + * be moved. The VPD end tag "RV" or "RW" is generated again. + * + * returns 0: success + * 2: value string was cut + * 4: VPD full, keyword was not written + * 6: fatal VPD error + * + */ +int VpdSetupPara( +SK_AC *pAC, /* common data base */ +char *key, /* keyword to insert */ +char *buf, /* buffer with the keyword value */ +int len, /* length of the keyword value */ +int type, /* VPD_RO_KEY or VPD_RW_KEY */ +int op) /* operation to do: ADD_KEY or OWR_KEY */ +{ + SK_VPD_PARA vp ; + char *etp ; /* end tag position */ + int free ; /* remaining space in selected area */ + char *ip ; /* input position inside the VPD buffer */ + int rtv ; /* return code */ + int head ; /* additional haeder bytes to move */ + int found ; /* additinoal bytes if the keyword was found */ + + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_CTRL, + ("vpd setup para key = %s, val = %s\n",key,buf)) ; + + rtv = 0 ; + ip = 0 ; + if (type == VPD_RW_KEY) { + /* end tag is "RW" */ + free = pAC->vpd.v.vpd_free_rw ; + etp = pAC->vpd.vpd_buf + (VPD_SIZE - free - 1 - 3) ; + } else { + /* end tag is "RV" */ + free = pAC->vpd.v.vpd_free_ro ; + etp = pAC->vpd.vpd_buf + (VPD_SIZE/2 - free - 4) ; + } + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_CTRL, + ("Free RO = %d, Free RW = %d\n", + pAC->vpd.v.vpd_free_ro, pAC->vpd.v.vpd_free_rw)) ; + + head = 0 ; + found = 0 ; + if (op == OWR_KEY) { + if (vpd_find_para(pAC,key,&vp)) { + found = 3 ; + ip = vp.p_val - 3 ; + free += vp.p_len + 3 ; + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_CTRL, + ("Overwrite Key\n")) ; + } else { + op = ADD_KEY ; + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_CTRL, + ("Add Key\n")) ; + } + } + if (op == ADD_KEY) { + ip = etp ; + vp.p_len = 0 ; + head = 3 ; + } + + if (len + 3 > free) { + if (free < 7) { + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR, + ("VPD Buffer Overflow, keyword not written\n")); + return (4) ; + } + /* cut it again */ + len = free - 3 ; + rtv = 2 ; + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR, + ("VPD Buffer Full, Keyword was cut\n")) ; + } + + vpd_move_para(ip + vp.p_len + found, etp+2, len-vp.p_len+head) ; + vpd_insert_key(key, buf, len, ip) ; + if (vpd_mod_endtag(pAC, etp + len - vp.p_len + head)) { + pAC->vpd.v.vpd_status &= ~VPD_VALID ; + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR, + ("VPD Encoding Error\n")) ; + return(6) ; + } + + return (rtv) ; +} + + +/* + * Read the contents of the VPD EEPROM and copy it to the + * VPD buffer if not already done. + * + * return: A pointer to the vpd_status structure. The structure contain + * this fields. + */ +SK_VPD_STATUS *VpdStat( +SK_AC *pAC, /* Adapters context */ +SK_IOC IoC) /* IO Context */ +{ + if (!(pAC->vpd.v.vpd_status & VPD_VALID)) { + (void)VpdInit(pAC,IoC) ; + } + return(&pAC->vpd.v) ; +} + + +/* + * Read the contents of the VPD EEPROM and copy it to the VPD + * buffer if not already done. + * Scan the VPD buffer for VPD keywords and create the VPD + * keyword list by copying the keywords to 'buf', all after + * each other and terminated with a '\0'. + * + * Exceptions: o The Resource Type ID String (product name) is called "Name" + * o The VPD end tags 'RV' and 'RW' are not listed + * + * The number of copied keywords is counted in 'elements'. + * + * returns 0: success + * 2: buffer overfull, one or more keywords are missing + * 6: fatal VPD error + * + * example values after returning: + * + * buf = "Name\0PN\0EC\0MN\0SN\0CP\0VF\0VL\0YA\0" + * *len = 30 + * *elements = 9 + */ +int VpdKeys( +SK_AC *pAC, /* common data base */ +SK_IOC IoC, /* IO Context */ +char *buf, /* buffer where to copy the keywords */ +int *len, /* buffer length */ +int *elements) /* number of keywords returned */ +{ + char *v ; + int n ; + + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_RX,("list vpd keys .. ")) ; + *elements = 0 ; + if (!(pAC->vpd.v.vpd_status & VPD_VALID)) { + if (VpdInit(pAC,IoC) != 0 ) { + *len = 0 ; + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR, + ("VPD Init Error, terminated\n")) ; + return(6) ; + } + } + + if ((signed)strlen(VPD_NAME) + 1 <= *len) { + v = pAC->vpd.vpd_buf ; + strcpy(buf,VPD_NAME) ; + n = strlen(VPD_NAME) + 1 ; + buf += n ; + *elements = 1 ; + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_RX, + ("'%c%c' ",v[0],v[1])) ; + } else { + *len = 0 ; + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR, + ("buffer overflow\n")) ; + return(2) ; + } + + v += 3 + VPD_GET_RES_LEN(v) + 3 ; + for ( ; ; ) { + /* exit when reaching the "RW" Tag */ + if (SK_MEMCMP(VPD_RW,v,2) == 0) { + break ; + } + + if (SK_MEMCMP(VPD_RV,v,2) == 0) { + v += 3 + VPD_GET_VPD_LEN(v) + 3 ; /* skip VPD-W */ + continue ; + } + + if (n+3 <= *len) { + SK_MEMCPY(buf,v,2) ; + buf += 2 ; + *buf++ = '\0' ; + n += 3 ; + v += 3 + VPD_GET_VPD_LEN(v) ; + *elements += 1 ; + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_RX, + ("'%c%c' ",v[0],v[1])) ; + } else { + *len = n ; + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR, + ("buffer overflow\n")) ; + return (2) ; + } + } + + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_RX,("\n")) ; + *len = n ; + return(0) ; +} + + +/* + * Read the contents of the VPD EEPROM and copy it to the + * VPD buffer if not already done. Search for the VPD keyword + * 'key' and copy its value to 'buf'. Add a terminating '\0'. + * If the value does not fit into the buffer cut it after + * 'len' - 1 bytes. + * + * returns 0: success + * 1: keyword not found + * 2: value string was cut + * 3: VPD transfer timeout + * 6: fatal VPD error + */ +int VpdRead( +SK_AC *pAC, /* common data base */ +SK_IOC IoC, /* IO Context */ +char *key, /* keyword to read (e.g. "MN") */ +char *buf, /* buffer where to copy the keyword value */ +int *len) /* buffer length */ +{ + SK_VPD_PARA *p, vp ; + + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_RX,("vpd read %s .. ",key)) ; + if (!(pAC->vpd.v.vpd_status & VPD_VALID)) { + if (VpdInit(pAC,IoC) != 0 ) { + *len = 0 ; + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR, + ("vpd init error\n")) ; + return(6) ; + } + } + + if ((p = vpd_find_para(pAC,key,&vp))) { + if (p->p_len > (*(unsigned *)len)-1) { + p->p_len = *len - 1 ; + } + SK_MEMCPY(buf,p->p_val,p->p_len) ; + buf[p->p_len] = '\0' ; + *len = p->p_len ; + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_RX, + ("%c%c%c%c.., len = %d\n", + buf[0],buf[1],buf[2],buf[3],*len)) ; + } else { + *len = 0 ; + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR,("not found\n")) ; + return (1) ; + } + return (0) ; +} + + +/* + * Check whether a given key may be written + * + * returns + * SK_TRUE Yes it may be written + * SK_FALSE No it may be written + */ +SK_BOOL VpdMayWrite( +char *key) /* keyword to write (allowed values "Yx", "Vx") */ +{ + if ((*key != 'Y' && *key != 'V') || + key[1] < '0' || key[1] > 'Z' || + (key[1] > '9' && key[1] < 'A') || strlen(key) != 2) { + + return (SK_FALSE) ; + } + return (SK_TRUE) ; +} + +/* + * Read the contents of the VPD EEPROM and copy it to the VPD + * buffer if not already done. Insert/overwrite the keyword 'key' + * in the VPD buffer. Cut the keyword value if it does not fit + * into the VPD read / write area. + * + * returns 0: success + * 2: value string was cut + * 3: VPD transfer timeout + * 4: VPD full, keyword was not written + * 5: keyword cannot be written + * 6: fatal VPD error + */ +int VpdWrite( +SK_AC *pAC, /* common data base */ +SK_IOC IoC, /* IO Context */ +char *key, /* keyword to write (allowed values "Yx", "Vx") */ +char *buf) /* buffer where the keyword value can be read from */ +{ + int len ; /* lenght of the keyword to write */ + int rtv ; /* return code */ + int rtv2 ; + + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_TX, + ("vpd write %s = %s\n",key,buf)) ; + + if ((*key != 'Y' && *key != 'V') || + key[1] < '0' || key[1] > 'Z' || + (key[1] > '9' && key[1] < 'A') || strlen(key) != 2) { + + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR, + ("illegal key tag, keyword not written\n")) ; + return (5) ; + } + + if (!(pAC->vpd.v.vpd_status & VPD_VALID)) { + if (VpdInit(pAC,IoC) != 0 ) { + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR, + ("vpd init error\n")) ; + return(6) ; + } + } + + rtv = 0 ; + len = strlen(buf) ; + if (len > VPD_MAX_LEN) { + /* cut it */ + len = VPD_MAX_LEN ; + rtv = 2 ; + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR, + ("keyword to long, cut after %d bytes\n",VPD_MAX_LEN)) ; + } + if ((rtv2 = VpdSetupPara(pAC,key,buf,len,VPD_RW_KEY,OWR_KEY)) != 0) { + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR, + ("vpd write error\n")) ; + return(rtv2) ; + } + + return (rtv) ; +} + +/* + * Read the contents of the VPD EEPROM and copy it to the + * VPD buffer if not already done. Remove the VPD keyword + * 'key' from the VPD buffer. + * Only the keywords in the read/write area can be deleted. + * Keywords in the read only area cannot be deleted. + * + * returns 0: success, keyword was removed + * 1: keyword not found + * 5: keyword cannot be deleted + * 6: fatal VPD error + */ +int VpdDelete( +SK_AC *pAC, /* common data base */ +SK_IOC IoC, /* IO Context */ +char *key) /* keyword to read (e.g. "MN") */ +{ + SK_VPD_PARA *p, vp ; + char *etp ; + + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_TX,("vpd delete key %s\n",key)) ; + if (!(pAC->vpd.v.vpd_status & VPD_VALID)) { + if (VpdInit(pAC,IoC) != 0 ) { + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR, + ("vpd init error\n")) ; + return(6) ; + } + } + + if ((p = vpd_find_para(pAC,key,&vp))) { + if (p->p_val < pAC->vpd.vpd_buf + VPD_SIZE/2) { + /* try to delete read only keyword */ + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR, + ("cannot delete RO keyword\n")) ; + return (5) ; + } + + etp = pAC->vpd.vpd_buf + (VPD_SIZE-pAC->vpd.v.vpd_free_rw-1-3) ; + + vpd_move_para(vp.p_val+vp.p_len, etp+2, + - ((int)(vp.p_len + 3))) ; + if (vpd_mod_endtag(pAC, etp - vp.p_len - 3)) { + pAC->vpd.v.vpd_status &= ~VPD_VALID ; + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR, + ("vpd encoding error\n")) ; + return(6) ; + } + } else { + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR, + ("keyword not found\n")) ; + return (1) ; + } + + return (0) ; +} + +/* + * If the VPD buffer contains valid data write the VPD + * read/write area back to the VPD EEPROM. + * + * returns 0: success + * 3: VPD transfer timeout + */ +int VpdUpdate( +SK_AC *pAC, /* Adapters context */ +SK_IOC IoC) /* IO Context */ +{ + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_TX,("vpd update .. ")) ; + if (pAC->vpd.v.vpd_status & VPD_VALID) { + if (VpdTransferBlock(pAC,IoC,pAC->vpd.vpd_buf + VPD_SIZE/2, + VPD_SIZE/2, VPD_SIZE/2, VPD_WRITE) != VPD_SIZE/2) { + + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR, + ("transfer timed out\n")) ; + return(3) ; + } + } + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_TX,("done\n")) ; + return (0) ; +} + + + +/* + * Read the contents of the VPD EEPROM and copy it to the VPD buffer + * if not already done. If the keyword "VF" is not present it will be + * created and the error log message will be stored to this keyword. + * If "VF" is not present the error log message will be stored to the + * keyword "VL". "VL" will created or overwritten if "VF" is present. + * The VPD read/write area is saved to the VPD EEPROM. + * + * returns nothing, errors will be ignored. + */ +void VpdErrLog( +SK_AC *pAC, /* common data base */ +SK_IOC IoC, /* IO Context */ +char *msg) /* error log message */ +{ + SK_VPD_PARA *v, vf ; /* VF */ + int len ; + + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_TX, + ("vpd error log msg %s\n",msg)) ; + if (!(pAC->vpd.v.vpd_status & VPD_VALID)) { + if (VpdInit(pAC,IoC) != 0 ) { + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_ERR, + ("vpd init error\n")) ; + return ; + } + } + + len = strlen(msg) ; + if (len > VPD_MAX_LEN) { + /* cut it */ + len = VPD_MAX_LEN ; + } + if ((v = vpd_find_para(pAC,VPD_VF,&vf))) { + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_TX,("overwrite VL\n")) ; + (void)VpdSetupPara(pAC,VPD_VL,msg,len,VPD_RW_KEY,OWR_KEY) ; + } else { + SK_DBG_MSG(pAC,SK_DBGMOD_VPD,SK_DBGCAT_TX,("write VF\n")) ; + (void)VpdSetupPara(pAC,VPD_VF,msg,len,VPD_RW_KEY,ADD_KEY) ; + } + + (void)VpdUpdate(pAC,IoC) ; +} + diff -u --recursive --new-file v2.2.13/linux/drivers/net/sk98lin/skxmac2.c linux/drivers/net/sk98lin/skxmac2.c --- v2.2.13/linux/drivers/net/sk98lin/skxmac2.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sk98lin/skxmac2.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,2047 @@ +/****************************************************************************** + * + * Name: skxmac2.c + * Project: GEnesis, PCI Gigabit Ethernet Adapter + * Version: $Revision: 1.49 $ + * Date: $Date: 1999/11/22 08:12:13 $ + * Purpose: Contains functions to initialize the XMAC II + * + ******************************************************************************/ + +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skge.c" for further information. + * + * 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. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/****************************************************************************** + * + * History: + * + * $Log: skxmac2.c,v $ + * Revision 1.49 1999/11/22 08:12:13 malthoff + * Add workaround for power consumption feature of Bcom C0 chip. + * + * Revision 1.48 1999/11/16 08:39:01 malthoff + * Fix: MDIO preamble suppression is port dependend. + * + * Revision 1.47 1999/08/27 08:55:35 malthoff + * 1000BT: Optimizing MDIO transfer by oppressing MDIO preamble. + * + * Revision 1.46 1999/08/13 11:01:12 malthoff + * Fix for 1000BT: pFlowCtrlMode was not set correctly. + * + * Revision 1.45 1999/08/12 19:18:28 malthoff + * 1000BT Fixes: Do not owerwrite XM_MMU_CMD. + * Do not execute BCOM A1 workaround for B1 chips. + * Fix pause frame setting. + * Always set PHY_B_AC_TX_TST in PHY_BCOM_AUX_CTRL. + * + * Revision 1.44 1999/08/03 15:23:48 cgoos + * Fixed setting of PHY interrupt mask in half duplex mode. + * + * Revision 1.43 1999/08/03 15:22:17 cgoos + * Added some debug output. + * Disabled XMac GP0 interrupt for external PHYs. + * + * Revision 1.42 1999/08/02 08:39:23 malthoff + * BCOM PHY: TX LED: To get the mono flop behaviour it is required + * to set the LED Traffic Mode bit in PHY_BCOM_P_EXT_CTRL. + * + * Revision 1.41 1999/07/30 06:54:31 malthoff + * Add temp. workarounds for the BCOM Phy revision A1. + * + * Revision 1.40 1999/06/01 07:43:26 cgoos + * Changed Link Mode Status in SkXmAutoNegDone... from FULL/HALF to + * AUTOFULL/AUTOHALF. + * + * Revision 1.39 1999/05/19 07:29:51 cgoos + * Changes for 1000Base-T. + * + * Revision 1.38 1999/04/08 14:35:10 malthoff + * Add code for enabling signal detect. Enabling signal + * detect is disabled. + * + * Revision 1.37 1999/03/12 13:42:54 malthoff + * Add: Jumbo Frame Support. + * Add: Receive modes SK_LENERR_OK_ON/OFF and + * SK_BIG_PK_OK_ON/OFF in SkXmSetRxCmd(). + * + * Revision 1.36 1999/03/08 10:10:55 gklug + * fix: AutoSensing did switch to next mode even if LiPa indicated offline + * + * Revision 1.35 1999/02/22 15:16:41 malthoff + * Remove some compiler warnings. + * + * Revision 1.34 1999/01/22 09:19:59 gklug + * fix: Init DupMode and InitPauseMd are now called in RxTxEnable + * + * Revision 1.33 1998/12/11 15:19:11 gklug + * chg: lipa autoneg stati + * chg: debug messages + * chg: do NOT use spurious XmIrq + * + * Revision 1.32 1998/12/10 11:08:44 malthoff + * bug fix: pAC has been used for IOs in SkXmHardRst(). + * SkXmInitPhy() is also called for the Diag in SkXmInitMac(). + * + * Revision 1.31 1998/12/10 10:39:11 gklug + * fix: do 4 RESETS of the XMAC at the beginning + * fix: dummy read interrupt source register BEFORE initializing the Phy + * add: debug messages + * fix: Linkpartners autoneg capability cannot be shown by TX_PAGE interrupt + * + * Revision 1.30 1998/12/07 12:18:32 gklug + * add: refinement of autosense mode: take into account the autoneg cap of LiPa + * + * Revision 1.29 1998/12/07 07:12:29 gklug + * fix: if page is received the link is down. + * + * Revision 1.28 1998/12/01 10:12:47 gklug + * chg: if spurious IRQ from XMAC encountered, save it + * + * Revision 1.27 1998/11/26 07:33:38 gklug + * add: InitPhy call is now in XmInit function + * + * Revision 1.26 1998/11/18 13:38:24 malthoff + * 'Imsk' is also unused in SkXmAutoNegDone. + * + * Revision 1.25 1998/11/18 13:28:01 malthoff + * Remove unused variable 'Reg' in SkXmAutoNegDone(). + * + * Revision 1.24 1998/11/18 13:18:45 gklug + * add: workaround for xmac errata #1 + * add: detect Link Down also when Link partner requested config + * chg: XMIrq is only used when link is up + * + * Revision 1.23 1998/11/04 07:07:04 cgoos + * Added function SkXmRxTxEnable. + * + * Revision 1.22 1998/10/30 07:35:54 gklug + * fix: serve LinkDown interrupt when link is already down + * + * Revision 1.21 1998/10/29 15:32:03 gklug + * fix: Link Down signaling + * + * Revision 1.20 1998/10/29 11:17:27 gklug + * fix: AutoNegDone bug + * + * Revision 1.19 1998/10/29 10:14:43 malthoff + * Add endainesss comment for reading/writing MAC addresses. + * + * Revision 1.18 1998/10/28 07:48:55 cgoos + * Fix: ASS somtimes signaled although link is up. + * + * Revision 1.17 1998/10/26 07:55:39 malthoff + * Fix in SkXmInitPauseMd(): Pause Mode + * was disabled and not enabled. + * Fix in SkXmAutoNegDone(): Checking Mode bits + * always failed, becaues of some missing braces. + * + * Revision 1.16 1998/10/22 09:46:52 gklug + * fix SysKonnectFileId typo + * + * Revision 1.15 1998/10/21 05:51:37 gklug + * add: para DoLoop to InitPhy function for loopback set-up + * + * Revision 1.14 1998/10/16 10:59:23 malthoff + * Remove Lint warning for dummy reads. + * + * Revision 1.13 1998/10/15 14:01:20 malthoff + * Fix: SkXmAutoNegDone() is (int) but does not return a value. + * + * Revision 1.12 1998/10/14 14:45:04 malthoff + * Remove SKERR_SIRQ_E0xx and SKERR_SIRQ_E0xxMSG by + * SKERR_HWI_Exx and SKERR_HWI_E0xxMSG to be independant + * from the Sirq module. + * + * Revision 1.11 1998/10/14 13:59:01 gklug + * add: InitPhy function + * + * Revision 1.10 1998/10/14 11:20:57 malthoff + * Make SkXmAutoNegDone() public, because it's + * used in diagnostics, too. + * The Link Up event to the RLMT is issued in + * SkXmIrq(). SkXmIrq() is not available in + * diagnostics. Use PHY_READ when reading + * PHY registers. + * + * Revision 1.9 1998/10/14 05:50:10 cgoos + * Added definition for Para. + * + * Revision 1.8 1998/10/14 05:41:28 gklug + * add: Xmac IRQ + * add: auto negotiation done function + * + * Revision 1.7 1998/10/09 06:55:20 malthoff + * The configuration of the XMACs Tx Request Threshold + * depends from the drivers port usage now. The port + * usage is configured in GIPortUsage. + * + * Revision 1.6 1998/10/05 07:48:00 malthoff + * minor changes + * + * Revision 1.5 1998/10/01 07:03:54 gklug + * add: dummy function for XMAC ISR + * + * Revision 1.4 1998/09/30 12:37:44 malthoff + * Add SkXmSetRxCmd() and related code. + * + * Revision 1.3 1998/09/28 13:26:40 malthoff + * Add SkXmInitMac(), SkXmInitDupMd(), and SkXmInitPauseMd() + * + * Revision 1.2 1998/09/16 14:34:21 malthoff + * Add SkXmClrExactAddr(), SkXmClrSrcCheck(), + * SkXmClrHashAddr(), SkXmFlushTxFifo(), + * SkXmFlushRxFifo(), and SkXmHardRst(). + * Finish Coding of SkXmSoftRst(). + * The sources may be compiled now. + * + * Revision 1.1 1998/09/04 10:05:56 malthoff + * Created. + * + * + ******************************************************************************/ + +#include "h/skdrv1st.h" +#include "h/xmac_ii.h" +#include "h/skdrv2nd.h" + +/* defines ********************************************************************/ +/* typedefs *******************************************************************/ +/* global variables ***********************************************************/ + +/* local variables ************************************************************/ + +static const char SysKonnectFileId[] = + "@(#)$Id: skxmac2.c,v 1.49 1999/11/22 08:12:13 malthoff Exp $ (C) SK "; + +/* BCOM PHY magic pattern list */ +typedef struct s_PhyHack { + int PhyReg; /* Phy register */ + SK_U16 PhyVal; /* Value to write */ +} BCOM_HACK; + +BCOM_HACK BcomRegA1Hack[] = { + { 0x18, 0x0c20 }, { 0x17, 0x0012 }, { 0x15, 0x1104 }, { 0x17, 0x0013 }, + { 0x15, 0x0404 }, { 0x17, 0x8006 }, { 0x15, 0x0132 }, { 0x17, 0x8006 }, + { 0x15, 0x0232 }, { 0x17, 0x800D }, { 0x15, 0x000F }, { 0x18, 0x0420 }, + { 0, 0 } +}; +BCOM_HACK BcomRegC0Hack[] = { + { 0x18, 0x0c20 }, { 0x17, 0x0012 }, { 0x15, 0x1204 }, { 0x17, 0x0013 }, + { 0x15, 0x0A04 }, { 0x18, 0x0420 }, + { 0, 0 } +}; + +/* function prototypes ********************************************************/ +static void SkXmInitPhyXmac(SK_AC*, SK_IOC, int, SK_BOOL); +static void SkXmInitPhyBcom(SK_AC*, SK_IOC, int, SK_BOOL); +static void SkXmInitPhyLone(SK_AC*, SK_IOC, int, SK_BOOL); +static void SkXmInitPhyNat (SK_AC*, SK_IOC, int, SK_BOOL); +static int SkXmAutoNegDoneXmac(SK_AC*, SK_IOC, int); +static int SkXmAutoNegDoneBcom(SK_AC*, SK_IOC, int); +static int SkXmAutoNegDoneLone(SK_AC*, SK_IOC, int); +static int SkXmAutoNegDoneNat (SK_AC*, SK_IOC, int); + +/****************************************************************************** + * + * SkXmSetRxCmd() - Modify the value of the XMACs Rx Command Register + * + * Description: + * The features + * o FCS stripping, SK_STRIP_FCS_ON/OFF + * o pad byte stripping, SK_STRIP_PAD_ON/OFF + * o don't set XMR_FS_ERR in frame SK_LENERR_OK_ON/OFF + * status for inrange length error + * frames, and + * o don't set XMR_FS_ERR in frame SK_BIG_PK_OK_ON/OFF + * status for frames > 1514 bytes + * + * for incomming packets may be enabled/disabled by this function. + * Additional modes may be added later. + * Multiple modes can be enabled/disabled at the same time. + * The new configuration is stored into the HWAC port configuration + * and is written to the Receive Command register immediatlely. + * The new configuration is saved over any SkGePortStop() and + * SkGeInitPort() calls. The configured value will be overwritten + * when SkGeInit(Level 0) is executed. + * + * Returns: + * nothing + */ +void SkXmSetRxCmd( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port, /* The XMAC to handle with belongs to this Port */ +int Mode) /* Mode is SK_STRIP_FCS_ON/OFF, SK_STRIP_PAD_ON/OFF, + SK_LENERR_OK_ON/OFF, or SK_BIG_PK_OK_ON/OFF */ +{ + SK_GEPORT *pPrt; + SK_U16 OldRxMode; + + pPrt = &pAC->GIni.GP[Port]; + OldRxMode = pPrt->PRxCmd; + + switch(Mode & (SK_STRIP_FCS_ON | SK_STRIP_FCS_OFF)) { + case SK_STRIP_FCS_ON: + pPrt->PRxCmd |= XM_RX_STRIP_FCS; + break; + case SK_STRIP_FCS_OFF: + pPrt->PRxCmd &= ~XM_RX_STRIP_FCS; + break; + } + + switch(Mode & (SK_STRIP_PAD_ON | SK_STRIP_PAD_OFF)) { + case SK_STRIP_PAD_ON: + pPrt->PRxCmd |= XM_RX_STRIP_PAD; + break; + case SK_STRIP_PAD_OFF: + pPrt->PRxCmd &= ~XM_RX_STRIP_PAD; + break; + } + + switch(Mode & (SK_LENERR_OK_ON | SK_LENERR_OK_OFF)) { + case SK_LENERR_OK_ON: + pPrt->PRxCmd |= XM_RX_LENERR_OK; + break; + case SK_LENERR_OK_OFF: + pPrt->PRxCmd &= ~XM_RX_LENERR_OK; + break; + } + + switch(Mode & (SK_BIG_PK_OK_ON | SK_BIG_PK_OK_OFF)) { + case SK_BIG_PK_OK_ON: + pPrt->PRxCmd |= XM_RX_BIG_PK_OK; + break; + case SK_BIG_PK_OK_OFF: + pPrt->PRxCmd &= ~XM_RX_BIG_PK_OK; + break; + } + + /* Write the new mode to the receive command register if required */ + if (OldRxMode != pPrt->PRxCmd) { + XM_OUT16(IoC, Port, XM_RX_CMD, pPrt->PRxCmd); + } +} + +/****************************************************************************** + * + * SkXmClrExactAddr() - Clear Exact Match Address Registers + * + * Description: + * All Exact Match Address registers of the XMAC 'Port' will be + * cleared starting with 'StartNum' up to (and including) the + * Exact Match address number of 'StopNum'. + * + * Returns: + * nothing + */ +void SkXmClrExactAddr( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port, /* The XMAC to handle with belongs to this Port */ +int StartNum, /* Begin with this Address Register Index (0..15) */ +int StopNum) /* Stop after finished with this Register Idx (0..15) */ +{ + int i; + SK_U16 ZeroAddr[3] = { 0x0000, 0x0000, 0x0000 }; + + if ((unsigned)StartNum > 15 || (unsigned)StopNum > 15 || + StartNum > StopNum) { + + SK_ERR_LOG(pAC, SK_ERRCL_SW, SKERR_HWI_E001, SKERR_HWI_E001MSG); + return; + } + + for (i = StartNum; i <= StopNum; i++) { + XM_OUTADDR(IoC, Port, XM_EXM(i), &ZeroAddr[0]); + } +} + +/****************************************************************************** + * + * SkXmClrSrcCheck() - Clear Source Check Address Register + * + * Description: + * The Source Check Address Register of the XMAC 'Port' number + * will be cleared. + * + * Returns: + * nothing + */ +static void SkXmClrSrcCheck( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port) /* The XMAC to handle with belongs to this Port (MAC_1 + n) */ +{ + SK_U16 ZeroAddr[3] = { 0x0000, 0x0000, 0x0000 }; + + XM_OUTHASH(IoC, Port, XM_SRC_CHK, &ZeroAddr); +} + +/****************************************************************************** + * + * SkXmClrHashAddr() - Clear Hash Address Registers + * + * Description: + * The Hash Address Register of the XMAC 'Port' will be cleared. + * + * Returns: + * nothing + */ +static void SkXmClrHashAddr( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port) /* The XMAC to handle with belongs to this Port (MAC_1 + n) */ +{ + SK_U16 ZeroAddr[4] = { 0x0000, 0x0000, 0x0000, 0x0000 }; + + XM_OUTHASH(IoC, Port, XM_HSM, &ZeroAddr); +} + +/****************************************************************************** + * + * SkXmFlushTxFifo() - Flush the XMACs transmit FIFO + * + * Description: + * Flush the transmit FIFO of the XMAC specified by the index 'Port' + * + * Returns: + * nothing + */ +void SkXmFlushTxFifo( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port) /* The XMAC to handle with belongs to this Port (MAC_1 + n) */ +{ + SK_U32 MdReg; + + XM_IN32(IoC, Port, XM_MODE, &MdReg); + MdReg |= XM_MD_FTF; + XM_OUT32(IoC, Port, XM_MODE, MdReg); +} + +/****************************************************************************** + * + * SkXmFlushRxFifo() - Flush the XMACs receive FIFO + * + * Description: + * Flush the receive FIFO of the XMAC specified by the index 'Port' + * + * Returns: + * nothing + */ +void SkXmFlushRxFifo( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port) /* The XMAC to handle with belongs to this Port (MAC_1 + n) */ +{ + SK_U32 MdReg; + + XM_IN32(IoC, Port, XM_MODE, &MdReg); + MdReg |= XM_MD_FRF; + XM_OUT32(IoC, Port, XM_MODE, MdReg); +} + +/****************************************************************************** + * + * SkXmSoftRst() - Do a XMAC software reset + * + * Description: + * The PHY registers should not be destroyed during this + * kind of software reset. Therefore the XMAC Software Reset + * (XM_GP_RES_MAC bit in XM_GP_PORT) must not be used! + * + * The software reset is done by + * - disabling the Rx and Tx state maschine, + * - reseting the statistics module, + * - clear all other significant XMAC Mode, + * Command, and Control Registers + * - clearing the Hash Register and the + * Exact Match Address registers, and + * - flushing the XMAC's Rx and Tx FIFOs. + * + * Note: + * Another requirement when stopping the XMAC is to + * avoid sending corrupted frames on the network. + * Disabling the Tx state maschine will NOT interrupt + * the currently transmitted frame. But we must take care + * that the tx FIFO is cleared AFTER the current frame + * is complete sent to the network. + * + * It takes about 12ns to send a frame with 1538 bytes. + * One PCI clock goes at least 15ns (66MHz). Therefore + * after reading XM_GP_PORT back, we are sure that the + * transmitter is disabled AND idle. And this means + * we may flush the transmit FIFO now. + * + * Returns: + * nothing + */ +void SkXmSoftRst( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port) /* port to stop (MAC_1 + n) */ +{ + SK_GEPORT *pPrt; + SK_U16 Word; + + pPrt = &pAC->GIni.GP[Port]; + + /* disable the receiver and transmitter */ + XM_IN16(IoC, Port, XM_MMU_CMD, &Word); + XM_OUT16(IoC, Port, XM_MMU_CMD, Word & ~(XM_MMU_ENA_RX|XM_MMU_ENA_TX)); + + /* reset the statistics module */ + XM_OUT32(IoC, Port, XM_GP_PORT, XM_GP_RES_STAT); + + /* + * clear all other significant XMAC Mode, + * Command, and Control Registers + */ + XM_OUT16(IoC, Port, XM_IMSK, 0xffff); /* disable all IRQs */ + XM_OUT32(IoC, Port, XM_MODE, 0x00000000); /* clear Mode Reg */ + XM_OUT16(IoC, Port, XM_TX_CMD, 0x0000); /* reset TX CMD Reg */ + XM_OUT16(IoC, Port, XM_RX_CMD, 0x0000); /* reset RX CMD Reg */ + + /* disable all PHY IRQs */ + switch (pAC->GIni.GP[Port].PhyType) { + case SK_PHY_BCOM: + PHY_WRITE(IoC, pPrt, Port, PHY_BCOM_INT_MASK, + 0xffff); + break; + case SK_PHY_LONE: + PHY_WRITE(IoC, pPrt, Port, PHY_LONE_INT_ENAB, + 0x0); + break; + case SK_PHY_NAT: + /* todo: National + PHY_WRITE(IoC, pPrt, Port, PHY_NAT_INT_MASK, + 0xffff); */ + break; + } + + /* clear the Hash Register */ + SkXmClrHashAddr(pAC, IoC, Port); + + /* clear the Exact Match Address registers */ + SkXmClrExactAddr(pAC, IoC, Port, 0, 15); + SkXmClrSrcCheck(pAC, IoC, Port); + + /* flush the XMAC's Rx and Tx FIFOs */ + SkXmFlushTxFifo(pAC, IoC, Port); + SkXmFlushRxFifo(pAC, IoC, Port); + + pAC->GIni.GP[Port].PState = SK_PRT_STOP; +} + +/****************************************************************************** + * + * SkXmHardRst() - Do a XMAC hardware reset + * + * Description: + * The XMAC of the specified 'Port' and all connected devices + * (PHY and SERDES) will receive a reset signal on its *Reset + * pins. + * External PHYs must be reset be clearing a bit in the GPIO + * register (Timing requirements: Broadcom: 400ns, Level One: + * none, National: 80ns). + * + * Returns: + * nothing + */ +void SkXmHardRst( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port) /* port to stop (MAC_1 + n) */ +{ + SK_U16 Word; + int i; + SK_U32 Reg; + + for (i=0; i<4; i++) { + /* TX_MFF_CTRL1 is a 32 bit register but only the lowest 16 */ + /* bit contains buttoms to press */ + SK_OUT16(IoC, MR_ADDR(Port, TX_MFF_CTRL1), + (SK_U16) MFF_CLR_MAC_RST); + SK_OUT16(IoC, MR_ADDR(Port, TX_MFF_CTRL1), + (SK_U16) MFF_SET_MAC_RST); + do { + SK_IN16(IoC,MR_ADDR(Port,TX_MFF_CTRL1), &Word); + } while ((Word & MFF_SET_MAC_RST) == 0); + } + if (pAC->GIni.GP[Port].PhyType != SK_PHY_XMAC) { + + /* reset external PHY */ + SK_IN32(IoC, B2_GP_IO, &Reg); + if (Port == 0) { + Reg |= GP_DIR_0; /* set to output */ + Reg &= ~GP_IO_0; + } + else { + Reg |= GP_DIR_2; /* set to output */ + Reg &= ~GP_IO_2; + } + SK_OUT32(IoC, B2_GP_IO, Reg); + + /* short delay */ + SK_IN32(IoC, B2_GP_IO, &Reg); + } + + pAC->GIni.GP[Port].PState = SK_PRT_RESET; +} + +/****************************************************************************** + * + * SkXmInitMac() - Initialize the XMAC II + * + * Description: + * Initialize all the XMAC of the specified port. + * The XMAC must be reset or stopped before calling this function. + * + * Note: + * The XMACs Rx and Tx state machine is still disabled when + * returning. + * + * Returns: + * nothing + */ +void SkXmInitMac( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port) /* Port Index (MAC_1 + n) */ +{ + SK_GEPORT *pPrt; + SK_U16 SWord; + int i; + SK_U32 Reg; + + pPrt = &pAC->GIni.GP[Port]; + + if (pPrt->PState == SK_PRT_STOP) { + /* Port State: SK_PRT_STOP */ + /* Verify that the reset bit is cleared */ + SK_IN16(IoC, MR_ADDR(Port, TX_MFF_CTRL1), &SWord); + if (SWord & (SK_U16) MFF_SET_MAC_RST) { + /* PState does not match HW state */ + SK_ERR_LOG(pAC, SK_ERRCL_SW, SKERR_HWI_E006, + SKERR_HWI_E006MSG); + /* correct it */ + pPrt->PState = SK_PRT_RESET; + } + } + if (pPrt->PState == SK_PRT_RESET) { + /* + * clear HW reset + * Note: The SW reset is self clearing, therefore there is + * nothing to do here. + */ + SK_OUT16(IoC, MR_ADDR(Port, TX_MFF_CTRL1), + (SK_U16) MFF_CLR_MAC_RST); + + /* + * clear PHY reset + */ + if (pAC->GIni.GP[Port].PhyType != SK_PHY_XMAC) { + + SK_IN32(IoC, B2_GP_IO, &Reg); + if (Port == 0) { + Reg |= GP_DIR_0; /* set to output */ + Reg |= GP_IO_0; + } + else { + Reg |= GP_DIR_2; /* set to output */ + Reg |= GP_IO_2; + } + SK_OUT32(IoC, B2_GP_IO, Reg); + + /* enable GMII interface */ + XM_OUT16(IoC, Port, XM_HW_CFG, XM_HW_GMII_MD); + + /* optimize MDIO transfer by oppressing preamble */ + XM_IN16(IoC, Port, XM_MMU_CMD, &SWord); + XM_OUT16(IoC, Port, XM_MMU_CMD, SWord | XM_MMU_NO_PRE); + + /* Workaround BCOM Errata for the A1 type */ + /* Write magic patterns to reserved registers */ + PHY_READ(IoC, pPrt, Port, PHY_XMAC_ID1, &SWord); + if (SWord == 0x6041) { + i = 0; + while (BcomRegA1Hack[i].PhyReg != 0) { + PHY_WRITE(IoC, pPrt, Port, + BcomRegA1Hack[i].PhyReg, + BcomRegA1Hack[i].PhyVal); + i++; + } + } + /* Workaround BCOM Errata for the C0 type */ + /* Write magic patterns to reserved registers */ + if (SWord == 0x6044) { + i = 0; + while (BcomRegC0Hack[i].PhyReg != 0) { + PHY_WRITE(IoC, pPrt, Port, + BcomRegC0Hack[i].PhyReg, + BcomRegC0Hack[i].PhyVal); + i++; + } + } + + /* + * PHY LED initialization is performed in + * SkGeXmitLED() (but not here). + */ + } + + /* Dummy read the Interrupt source register */ + XM_IN16(IoC, Port, XM_ISRC, &SWord); + + /* + * The autonegotiation process starts immediately after + * clearing the reset. Autonegotiation process should be + * started by the SIRQ, therefore stop it here immediately. + */ + SkXmInitPhy(pAC, IoC, Port, SK_FALSE); + +#if 0 + /* temp. code: enable signal detect */ + /* WARNING: do not override GMII setting above */ + XM_OUT16(pAC, Port, XM_HW_CFG, XM_HW_COM4SIG); +#endif + } + + /* + * configure the XMACs Station Address + * B2_MAC_2 = xx xx xx xx xx x1 is programed to XMAC A + * B2_MAC_3 = xx xx xx xx xx x2 is programed to XMAC B + */ + for (i = 0; i < 3; i++) { + /* + * The following 2 statements are together endianess + * independant. Remember this when changing. + */ + SK_IN16(IoC, (B2_MAC_2 + Port * 8 + i * 2), &SWord); + XM_OUT16(IoC, Port, (XM_SA + i * 2), SWord); + } + + /* Tx Inter Packet Gap (XM_TX_IPG): use default */ + /* Tx High Water Mark (XM_TX_HI_WM): use default */ + /* Tx Low Water Mark (XM_TX_LO_WM): use default */ + /* Host Request Threshold (XM_HT_THR): use default */ + /* Rx Request Threshold (XM_RX_THR): use default */ + /* Rx Low Water Mark (XM_RX_LO_WM): use default */ + + /* configure Rx High Water Mark (XM_RX_HI_WM) */ + XM_OUT16(IoC, Port, XM_RX_HI_WM, 0x05aa); + + if (pAC->GIni.GIMacsFound > 1) { + switch (pAC->GIni.GIPortUsage) { + case SK_RED_LINK: + /* Configure Tx Request Threshold for red. link */ + XM_OUT16(IoC, Port, XM_TX_THR, SK_XM_THR_REDL); + break; + case SK_MUL_LINK: + /* Configure Tx Request Threshold for load bal. */ + XM_OUT16(IoC, Port, XM_TX_THR, SK_XM_THR_MULL); + break; + case SK_JUMBO_LINK: + /* Configure Tx Request Threshold for jumbo frames */ + XM_OUT16(IoC, Port, XM_TX_THR, SK_XM_THR_JUMBO); + break; + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW, SKERR_HWI_E014, + SKERR_HWI_E014MSG); + break; + } + } + else { + /* Configure Tx Request Threshold for single port */ + XM_OUT16(IoC, Port, XM_TX_THR, SK_XM_THR_SL); + } + + /* + * setup register defaults for the Rx Command Register + * - Enable Automatic Frame Padding on Tx side + */ + XM_OUT16(IoC, Port, XM_TX_CMD, XM_TX_AUTO_PAD); + + /* + * setup register defaults for the Rx Command Register, + * program value of PRxCmd + */ + XM_OUT16(IoC, Port, XM_RX_CMD, pPrt->PRxCmd); + + /* + * setup register defaults for the Mode Register + * - Don't strip error frames to avoid Store & Forward + * on the rx side. + * - Enable 'Check Station Address' bit + * - Enable 'Check Address Array' bit + */ + XM_OUT32(IoC, Port, XM_MODE, XM_DEF_MODE); + + /* + * Initialize the Receive Counter Event Mask (XM_RX_EV_MSK) + * - Enable all bits excepting 'Octets Rx OK Low CntOv' + * and 'Octets Rx OK Hi Cnt Ov'. + */ + XM_OUT32(IoC, Port, XM_RX_EV_MSK, XMR_DEF_MSK); + + /* + * Initialize the Transmit Counter Event Mask (XM_TX_EV_MSK) + * - Enable all bits excepting 'Octets Tx OK Low CntOv' + * and 'Octets Tx OK Hi Cnt Ov'. + */ + XM_OUT32(IoC, Port, XM_TX_EV_MSK, XMT_DEF_MSK); + + /* + * Do NOT init XMAC interrupt mask here. + * All interrupts remain disable until link comes up! + */ + pPrt->PState = SK_PRT_INIT; + + /* + * Any additional configuration changes may be done now. + * The last action is to enable the rx and tx state machine. + * This should be done after the autonegotiation process + * has been completed successfully. + */ +} + +/****************************************************************************** + * + * SkXmInitDupMd() - Initialize the XMACs Duplex Mode + * + * Description: + * This function initilaizes the XMACs Duplex Mode. + * It should be called after successfully finishing + * the Autonegotiation Process + * + * Returns: + * nothing + */ +void SkXmInitDupMd( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port) /* Port Index (MAC_1 + n) */ +{ + switch (pAC->GIni.GP[Port].PLinkModeStatus) { + case SK_LMODE_STAT_AUTOHALF: + case SK_LMODE_STAT_HALF: + /* Configuration Actions for Half Duplex Mode */ + /* + * XM_BURST = default value. We are propable not quick + * enough at the 'XMAC' bus to burst 8kB. + * The XMAC stopps bursting if no transmit frames + * are available or the burst limit is exceeded. + */ + /* XM_TX_RT_LIM = default value (15) */ + /* XM_TX_STIME = default value (0xff = 4096 bit times) */ + break; + case SK_LMODE_STAT_AUTOFULL: + case SK_LMODE_STAT_FULL: + /* Configuration Actions for Full Duplex Mode */ + /* + * The duplex mode is configured by the PHY, + * therefore it seems to be that there is nothing + * to do here. + */ + break; + case SK_LMODE_STAT_UNKNOWN: + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW, SKERR_HWI_E007, SKERR_HWI_E007MSG); + break; + } +} + +/****************************************************************************** + * + * SkXmInitPauseMd() - initialize the Pause Mode to be used for this port + * + * Description: + * This function initilaizes the Pause Mode which should + * be used for this port. + * It should be called after successfully finishing + * the Autonegotiation Process + * + * Returns: + * nothing + */ +void SkXmInitPauseMd( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port) /* Port Index (MAC_1 + n) */ +{ + SK_GEPORT *pPrt; + SK_U16 Word; + SK_U32 DWord; + + pPrt = &pAC->GIni.GP[Port]; + + if (pPrt->PFlowCtrlStatus == SK_FLOW_STAT_NONE || + pPrt->PFlowCtrlStatus == SK_FLOW_STAT_LOC_SEND) { + + /* Disable Pause Frame Reception */ + XM_IN16(IoC, Port, XM_MMU_CMD, &Word); + XM_OUT16(IoC, Port, XM_MMU_CMD, Word | XM_MMU_IGN_PF); + } + else { + /* + * enabling pause frame reception is required for 1000BT + * because the XMAC is not reset if the link is going down + */ + /* Enable Pause Frame Reception */ + XM_IN16(IoC, Port, XM_MMU_CMD, &Word); + XM_OUT16(IoC, Port, XM_MMU_CMD, Word & ~XM_MMU_IGN_PF); + } + + if (pPrt->PFlowCtrlStatus == SK_FLOW_STAT_SYMMETRIC || + pPrt->PFlowCtrlStatus == SK_FLOW_STAT_LOC_SEND) { + + /* + * Configure Pause Frame Generation + * Use internal and external Pause Frame Generation. + * Sending pause frames is edge triggert. Send a + * Pause frame with the maximum pause time if + * internal oder external FIFO full condition + * occurs. Send a zero pause time frame to + * start transmission again. + */ + + /* XM_PAUSE_DA = '010000C28001' (default) */ + + /* XM_MAC_PTIME = 0xffff (maximum) */ + /* remember this value is defined in big endian (!) */ + XM_OUT16(IoC, Port, XM_MAC_PTIME, 0xffff); + + /* Set Pause Mode in Mode Register */ + XM_IN32(IoC, Port, XM_MODE, &DWord); + XM_OUT32(IoC, Port, XM_MODE, DWord | XM_PAUSE_MODE); + + /* Set Pause Mode in MAC Rx FIFO */ + SK_OUT16(IoC, MR_ADDR(Port,RX_MFF_CTRL1), MFF_ENA_PAUSE); + } + else { + /* + * disable pause frame generation is required for 1000BT + * because the XMAC is not reset if the link is going down + */ + /* Disable Pause Mode in Mode Register */ + XM_IN32(IoC, Port, XM_MODE, &DWord); + XM_OUT32(IoC, Port, XM_MODE, DWord & ~XM_PAUSE_MODE); + + /* Disable Pause Mode in MAC Rx FIFO */ + SK_OUT16(IoC, MR_ADDR(Port,RX_MFF_CTRL1), MFF_DIS_PAUSE); + } + +} + + +/****************************************************************************** + * + * SkXmInitPhy() - Initialize the XMAC II Phy registers + * + * Description: + * Initialize all the XMACs Phy registers + * + * Note: + * + * Returns: + * nothing + */ +void SkXmInitPhy( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port, /* Port Index (MAC_1 + n) */ +SK_BOOL DoLoop) /* Should a Phy LOOback be set-up? */ +{ + SK_GEPORT *pPrt; + + pPrt = &pAC->GIni.GP[Port]; + switch (pPrt->PhyType) { + case SK_PHY_XMAC: + SkXmInitPhyXmac(pAC, IoC, Port, DoLoop); + break; + case SK_PHY_BCOM: + SkXmInitPhyBcom(pAC, IoC, Port, DoLoop); + break; + case SK_PHY_LONE: + SkXmInitPhyLone(pAC, IoC, Port, DoLoop); + break; + case SK_PHY_NAT: + SkXmInitPhyNat(pAC, IoC, Port, DoLoop); + break; + } +} + +/****************************************************************************** + * + * SkXmInitPhyXmac() - Initialize the XMAC II Phy registers + * + * Description: + * Initialize all the XMACs Phy registers + * + * Note: + * + * Returns: + * nothing + */ +static void SkXmInitPhyXmac( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port, /* Port Index (MAC_1 + n) */ +SK_BOOL DoLoop) /* Should a Phy LOOback be set-up? */ +{ + SK_GEPORT *pPrt; + SK_U16 Crtl; + + pPrt = &pAC->GIni.GP[Port]; + + /* Autonegotiation ? */ + if (pPrt->PLinkMode == SK_LMODE_HALF || + pPrt->PLinkMode == SK_LMODE_FULL) { + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("InitPhyXmac: no autonegotiation Port %d\n", Port)); + /* No Autonegiotiation */ + /* Set DuplexMode in Config register */ + Crtl = (pPrt->PLinkMode == SK_LMODE_FULL ? PHY_CT_DUP_MD : 0); + + /* + * Do NOT enable Autonegotiation here. This would hold + * the link down because no IDLES are transmitted + */ + } else { + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("InitPhyXmac: with autonegotiation Port %d\n", Port)); + /* Set Autonegotiation advertisement */ + Crtl = 0; + + /* Set Full/half duplex capabilities */ + switch (pPrt->PLinkMode) { + case SK_LMODE_AUTOHALF: + Crtl |= PHY_X_AN_HD; + break; + case SK_LMODE_AUTOFULL: + Crtl |= PHY_X_AN_FD; + break; + case SK_LMODE_AUTOBOTH: + Crtl |= PHY_X_AN_FD | PHY_X_AN_HD; + break; + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW | SK_ERRCL_INIT, + SKERR_HWI_E015, SKERR_HWI_E015MSG) ; + } + + switch (pPrt->PFlowCtrlMode) { + case SK_FLOW_MODE_NONE: + Crtl |= PHY_X_P_NO_PAUSE; + break; + case SK_FLOW_MODE_LOC_SEND: + Crtl |= PHY_X_P_ASYM_MD; + break; + case SK_FLOW_MODE_SYMMETRIC: + Crtl |= PHY_X_P_SYM_MD; + break; + case SK_FLOW_MODE_SYM_OR_REM: + Crtl |= PHY_X_P_BOTH_MD; + break; + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW | SK_ERRCL_INIT, + SKERR_HWI_E016, SKERR_HWI_E016MSG) ; + } + + /* Write AutoNeg Advertisement Register */ + PHY_WRITE(IoC, pPrt, Port, PHY_XMAC_AUNE_ADV, Crtl) ; + + /* Restart Autonegotiation */ + Crtl = PHY_CT_ANE | PHY_CT_RE_CFG; + } + + if (DoLoop) { + /* Set the Phy Loopback bit, too */ + Crtl |= PHY_CT_LOOP; + } + + /* Write to the Phy control register */ + PHY_WRITE(IoC, pPrt, Port, PHY_XMAC_CTRL, Crtl) ; +} + +/****************************************************************************** + * + * SkXmInitPhyBcom() - Initialize the Broadcom Phy registers + * + * Description: + * Initialize all the Broadcom Phy registers + * + * Note: + * + * Returns: + * nothing + */ +static void SkXmInitPhyBcom( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port, /* Port Index (MAC_1 + n) */ +SK_BOOL DoLoop) /* Should a Phy LOOback be set-up? */ +{ + SK_GEPORT *pPrt; + SK_U16 Crtl1 = PHY_B_CT_SP1000; + SK_U16 Crtl2 = 0; + SK_U16 Crtl3 = PHY_SEL_TYPE; + SK_U16 Crtl4 = PHY_B_PEC_EN_LTR; + SK_U16 Crtl5 = PHY_B_AC_TX_TST; + + pPrt = &pAC->GIni.GP[Port]; + + /* manuell Master/Slave ? */ + if (pPrt->PMSMode != SK_MS_MODE_AUTO) { + Crtl2 |= PHY_B_1000C_MSE; + if (pPrt->PMSMode == SK_MS_MODE_MASTER) { + Crtl2 |= PHY_B_1000C_MSC; + } + } + /* Autonegotiation ? */ + if (pPrt->PLinkMode == SK_LMODE_HALF || + pPrt->PLinkMode == SK_LMODE_FULL) { + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("InitPhyBcom: no autonegotiation Port %d\n", Port)); + /* No Autonegiotiation */ + /* Set DuplexMode in Config register */ + Crtl1 |= (pPrt->PLinkMode == SK_LMODE_FULL ? PHY_CT_DUP_MD : 0); + + /* Determine Master/Slave manuell if not already done */ + if (pPrt->PMSMode == SK_MS_MODE_AUTO) { + Crtl2 |= PHY_B_1000C_MSE; /* set it to Slave */ + } + + /* + * Do NOT enable Autonegotiation here. This would hold + * the link down because no IDLES are transmitted + */ + } else { + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("InitPhyBcom: with autonegotiation Port %d\n", Port)); + /* Set Autonegotiation advertisement */ + + /* Set Full/half duplex capabilities */ + switch (pPrt->PLinkMode) { + case SK_LMODE_AUTOHALF: + Crtl2 |= PHY_B_1000C_AHD; + break; + case SK_LMODE_AUTOFULL: + Crtl2 |= PHY_B_1000C_AFD; + break; + case SK_LMODE_AUTOBOTH: + Crtl2 |= PHY_B_1000C_AFD | PHY_B_1000C_AHD; + break; + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW | SK_ERRCL_INIT, + SKERR_HWI_E015, SKERR_HWI_E015MSG) ; + } + + switch (pPrt->PFlowCtrlMode) { + case SK_FLOW_MODE_NONE: + Crtl3 |= PHY_B_P_NO_PAUSE; + break; + case SK_FLOW_MODE_LOC_SEND: + Crtl3 |= PHY_B_P_ASYM_MD; + break; + case SK_FLOW_MODE_SYMMETRIC: + Crtl3 |= PHY_B_P_SYM_MD; + break; + case SK_FLOW_MODE_SYM_OR_REM: + Crtl3 |= PHY_B_P_BOTH_MD; + break; + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW | SK_ERRCL_INIT, + SKERR_HWI_E016, SKERR_HWI_E016MSG); + } + + /* Restart Autonegotiation */ + Crtl1 |= PHY_CT_ANE | PHY_CT_RE_CFG; + + } + + /* Initialize LED register here? */ + /* No. Please do it in SkDgXmitLed() (if required) and swap + init order of LEDs and XMAC. (MAl) */ + + /* Write 1000Base-T Control Register */ + PHY_WRITE(IoC, pPrt, Port, PHY_BCOM_1000T_CTRL, Crtl2); + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("1000Base-T Control Reg = %x\n", Crtl2)); + + /* Write AutoNeg Advertisement Register */ + PHY_WRITE(IoC, pPrt, Port, PHY_BCOM_AUNE_ADV, Crtl3); + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("AutoNeg Advertisment Reg = %x\n", Crtl3)); + + + if (DoLoop) { + /* Set the Phy Loopback bit, too */ + Crtl1 |= PHY_CT_LOOP; + } + + if (pAC->GIni.GIPortUsage == SK_JUMBO_LINK) { + /* configure fifo to high latency for xmission of ext. packets*/ + Crtl4 |= PHY_B_PEC_HIGH_LA; + + /* configure reception of extended packets */ + Crtl5 |= PHY_B_AC_LONG_PACK; + + PHY_WRITE(IoC, pPrt, Port, PHY_BCOM_AUX_CTRL, Crtl5); + } + + /* Configure LED Traffic Mode and Jumbo Frame usage if specified */ + PHY_WRITE(IoC, pPrt, Port, PHY_BCOM_P_EXT_CTRL, Crtl4); + + /* Write to the Phy control register */ + PHY_WRITE(IoC, pPrt, Port, PHY_BCOM_CTRL, Crtl1); + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("PHY Control Reg = %x\n", Crtl1)); +} + +/****************************************************************************** + * + * SkXmInitPhyLone() - Initialize the Level One Phy registers + * + * Description: + * Initialize all the Level One Phy registers + * + * Note: + * + * Returns: + * nothing + */ +static void SkXmInitPhyLone( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port, /* Port Index (MAC_1 + n) */ +SK_BOOL DoLoop) /* Should a Phy LOOback be set-up? */ +{ + SK_GEPORT *pPrt; + SK_U16 Crtl1 = PHY_L_CT_SP1000; + SK_U16 Crtl2 = 0; + SK_U16 Crtl3 = PHY_SEL_TYPE; + + pPrt = &pAC->GIni.GP[Port]; + + /* manuell Master/Slave ? */ + if (pPrt->PMSMode != SK_MS_MODE_AUTO) { + Crtl2 |= PHY_L_1000C_MSE; + if (pPrt->PMSMode == SK_MS_MODE_MASTER) { + Crtl2 |= PHY_L_1000C_MSC; + } + } + /* Autonegotiation ? */ + if (pPrt->PLinkMode == SK_LMODE_HALF || + pPrt->PLinkMode == SK_LMODE_FULL) { + /* + * level one spec say: "1000Mbps: manual mode not allowed" + * but lets see what happens... + */ + SK_ERR_LOG(pAC, SK_ERRCL_SW | SK_ERRCL_INIT, + 0, "Level One PHY only works with Autoneg"); + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("InitPhyLone: no autonegotiation Port %d\n", Port)); + /* No Autonegiotiation */ + /* Set DuplexMode in Config register */ + Crtl1 = (pPrt->PLinkMode == SK_LMODE_FULL ? PHY_CT_DUP_MD : 0); + + /* Determine Master/Slave manuell if not already done */ + if (pPrt->PMSMode == SK_MS_MODE_AUTO) { + Crtl2 |= PHY_L_1000C_MSE; /* set it to Slave */ + } + + /* + * Do NOT enable Autonegotiation here. This would hold + * the link down because no IDLES are transmitted + */ + } else { + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("InitPhyLone: with autonegotiation Port %d\n", Port)); + /* Set Autonegotiation advertisement */ + + /* Set Full/half duplex capabilities */ + switch (pPrt->PLinkMode) { + case SK_LMODE_AUTOHALF: + Crtl2 |= PHY_L_1000C_AHD; + break; + case SK_LMODE_AUTOFULL: + Crtl2 |= PHY_L_1000C_AFD; + break; + case SK_LMODE_AUTOBOTH: + Crtl2 |= PHY_L_1000C_AFD | PHY_L_1000C_AHD; + break; + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW | SK_ERRCL_INIT, + SKERR_HWI_E015, SKERR_HWI_E015MSG) ; + } + + switch (pPrt->PFlowCtrlMode) { + case SK_FLOW_MODE_NONE: + Crtl3 |= PHY_L_P_NO_PAUSE; + break; + case SK_FLOW_MODE_LOC_SEND: + Crtl3 |= PHY_L_P_ASYM_MD; + break; + case SK_FLOW_MODE_SYMMETRIC: + Crtl3 |= PHY_L_P_SYM_MD; + break; + case SK_FLOW_MODE_SYM_OR_REM: + Crtl3 |= PHY_L_P_BOTH_MD; + break; + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW | SK_ERRCL_INIT, + SKERR_HWI_E016, SKERR_HWI_E016MSG); + } + + /* Restart Autonegotiation */ + Crtl1 = PHY_CT_ANE | PHY_CT_RE_CFG; + + } + + /* Initialize LED register here ? */ + /* No. Please do it in SkDgXmitLed() (if required) and swap + init order of LEDs and XMAC. (MAl) */ + + /* Write 1000Base-T Control Register */ + PHY_WRITE(IoC, pPrt, Port, PHY_LONE_1000T_CTRL, Crtl2); + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("1000Base-T Control Reg = %x\n", Crtl2)); + + /* Write AutoNeg Advertisement Register */ + PHY_WRITE(IoC, pPrt, Port, PHY_LONE_AUNE_ADV, Crtl3); + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("AutoNeg Advertisment Reg = %x\n", Crtl3)); + + + if (DoLoop) { + /* Set the Phy Loopback bit, too */ + Crtl1 |= PHY_CT_LOOP; + } + + if (pAC->GIni.GIPortUsage == SK_JUMBO_LINK) { + /* + * nothing to do for Level one. + * PHY supports frames up to 10k. + */ + } + + /* Write to the Phy control register */ + PHY_WRITE(IoC, pPrt, Port, PHY_LONE_CTRL, Crtl1); + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("PHY Control Reg = %x\n", Crtl1)); +} + +/****************************************************************************** + * + * SkXmInitPhyNat() - Initialize the National Phy registers + * + * Description: + * Initialize all the National Phy registers + * + * Note: + * + * Returns: + * nothing + */ +static void SkXmInitPhyNat( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port, /* Port Index (MAC_1 + n) */ +SK_BOOL DoLoop) /* Should a Phy LOOback be set-up? */ +{ +/* todo: National */ +} + +/****************************************************************************** + * + * SkXmAutoNegLipaXmac() - Decides whether Link Partner could do autoneg + * + * This function analyses the Interrupt status word. If any of the + * Autonegotiating interrupt bits are set, the PLipaAutoNeg variable + * is set true. + */ +void SkXmAutoNegLipaXmac( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port, /* Port Index (MAC_1 + n) */ +SK_U16 IStatus) /* Interrupt Status word to analyse */ +{ + SK_GEPORT *pPrt; + + pPrt = &pAC->GIni.GP[Port]; + + if (pPrt->PLipaAutoNeg != SK_LIPA_AUTO && + (IStatus & (XM_IS_LIPA_RC|XM_IS_RX_PAGE|XM_IS_AND))) { + + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("AutoNegLipa: AutoNeg detected on port %d %x\n", Port, IStatus)); + pPrt->PLipaAutoNeg = SK_LIPA_AUTO; + } +} + +/****************************************************************************** + * + * SkXmAutoNegLipaBcom() - Decides whether Link Partner could do autoneg + * + * This function analyses the PHY status word. If any of the + * Autonegotiating bits are set, The PLipaAutoNeg variable + * is set true. + */ +void SkXmAutoNegLipaBcom( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port, /* Port Index (MAC_1 + n) */ +SK_U16 PhyStat) /* PHY Status word to analyse */ +{ + SK_GEPORT *pPrt; + + pPrt = &pAC->GIni.GP[Port]; + + if (pPrt->PLipaAutoNeg != SK_LIPA_AUTO && + (PhyStat & (PHY_ST_AN_OVER))) { + + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("AutoNegLipa: AutoNeg detected on port %d %x\n", Port, PhyStat)); + pPrt->PLipaAutoNeg = SK_LIPA_AUTO; + } +} + +/****************************************************************************** + * + * SkXmAutoNegLipaLone() - Decides whether Link Partner could do autoneg + * + * This function analyses the PHY status word. If any of the + * Autonegotiating bits are set, The PLipaAutoNeg variable + * is set true. + */ +void SkXmAutoNegLipaLone( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port, /* Port Index (MAC_1 + n) */ +SK_U16 PhyStat) /* PHY Status word to analyse */ +{ + SK_GEPORT *pPrt; + + pPrt = &pAC->GIni.GP[Port]; + + if (pPrt->PLipaAutoNeg != SK_LIPA_AUTO && + (PhyStat & (PHY_ST_AN_OVER))) { + + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("AutoNegLipa: AutoNeg detected on port %d %x\n", Port, PhyStat)); + pPrt->PLipaAutoNeg = SK_LIPA_AUTO; + } +} + +/****************************************************************************** + * + * SkXmAutoNegLipaNat() - Decides whether Link Partner could do autoneg + * + * This function analyses the PHY status word. If any of the + * Autonegotiating bits are set, The PLipaAutoNeg variable + * is set true. + */ +void SkXmAutoNegLipaNat( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port, /* Port Index (MAC_1 + n) */ +SK_U16 PhyStat) /* PHY Status word to analyse */ +{ + SK_GEPORT *pPrt; + + pPrt = &pAC->GIni.GP[Port]; + + if (pPrt->PLipaAutoNeg != SK_LIPA_AUTO && + (PhyStat & (PHY_ST_AN_OVER))) { + + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("AutoNegLipa: AutoNeg detected on port %d %x\n", Port, PhyStat)); + pPrt->PLipaAutoNeg = SK_LIPA_AUTO; + } +} +/****************************************************************************** + * + * SkXmAutoNegDone() - Auto negotiation handling + * + * Description: + * This function handles the autonegotiation if the Done bit is set. + * + * Note: + * o The XMACs interrupt source register is NOT read here. + * o This function is public because it is used in the diagnostics + * tool, too. + * + * Returns: + * SK_AND_OK o.k. + * SK_AND_DUP_CAP Duplex capability error happened + * SK_AND_OTHER Other error happened + */ +int SkXmAutoNegDone( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port) /* Port Index (MAC_1 + n) */ +{ + SK_GEPORT *pPrt; + + pPrt = &pAC->GIni.GP[Port]; + + switch (pPrt->PhyType) { + case SK_PHY_XMAC: + return (SkXmAutoNegDoneXmac(pAC, IoC, Port)); + case SK_PHY_BCOM: + return (SkXmAutoNegDoneBcom(pAC, IoC, Port)); + case SK_PHY_LONE: + return (SkXmAutoNegDoneLone(pAC, IoC, Port)); + case SK_PHY_NAT: + return (SkXmAutoNegDoneNat(pAC, IoC, Port)); + } + return(SK_AND_OTHER); +} + +/****************************************************************************** + * + * SkXmAutoNegDoneXmac() - Auto negotiation handling + * + * Description: + * This function handles the autonegotiation if the Done bit is set. + * + * Note: + * o The XMACs interrupt source register is NOT read here. + * + * Returns: + * SK_AND_OK o.k. + * SK_AND_DUP_CAP Duplex capability error happened + * SK_AND_OTHER Other error happened + */ +static int SkXmAutoNegDoneXmac( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port) /* Port Index (MAC_1 + n) */ +{ + SK_GEPORT *pPrt; + SK_U16 ResAb ; /* Resolved Ability */ + SK_U16 LPAb ; /* Link Partner Ability */ + + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, ("AutoNegDoneXmac" + "Port %d\n",Port)); + + pPrt = &pAC->GIni.GP[Port]; + + /* Get PHY parameters */ + PHY_READ(IoC, pPrt, Port, PHY_XMAC_AUNE_LP, &LPAb); + PHY_READ(IoC, pPrt, Port, PHY_XMAC_RES_ABI, &ResAb); + + if (LPAb & PHY_X_AN_RFB) { + /* At least one of the remote fault bit is set */ + /* Error */ + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("AutoNegFail: Remote fault bit set Port %d\n", Port)); + pPrt->PAutoNegFail = SK_TRUE ; + return (SK_AND_OTHER) ; + } + + /* Check Duplex mismatch */ + if ((ResAb & (PHY_X_RS_HD | PHY_X_RS_FD)) == PHY_X_RS_FD) { + pPrt->PLinkModeStatus = SK_LMODE_STAT_AUTOFULL ; + } else if ((ResAb & (PHY_X_RS_HD | PHY_X_RS_FD)) == PHY_X_RS_HD) { + pPrt->PLinkModeStatus = SK_LMODE_STAT_AUTOHALF ; + } else { + /* Error */ + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("AutoNegFail: Duplex mode mismatch port %d\n", Port)); + pPrt->PAutoNegFail = SK_TRUE ; + return (SK_AND_DUP_CAP) ; + } + + /* Check PAUSE mismatch */ + /* We are NOT using chapter 4.23 of the Xaqti manual */ + /* We are using IEEE 802.3z/D5.0 Table 37-4 */ + if ((pPrt->PFlowCtrlMode == SK_FLOW_MODE_SYMMETRIC || + pPrt->PFlowCtrlMode == SK_FLOW_MODE_SYM_OR_REM) && + (LPAb & PHY_X_P_SYM_MD)) { + /* Symmetric PAUSE */ + pPrt->PFlowCtrlStatus = SK_FLOW_STAT_SYMMETRIC; + } else if (pPrt->PFlowCtrlMode == SK_FLOW_MODE_SYM_OR_REM && + (LPAb & PHY_X_RS_PAUSE) == PHY_X_P_ASYM_MD) { + /* Enable PAUSE receive, disable PAUSE transmit */ + pPrt->PFlowCtrlStatus = SK_FLOW_STAT_REM_SEND; + } else if (pPrt->PFlowCtrlMode == SK_FLOW_MODE_LOC_SEND && + (LPAb & PHY_X_RS_PAUSE) == PHY_X_P_BOTH_MD) { + /* Disable PAUSE receive, enable PAUSE transmit */ + pPrt->PFlowCtrlStatus = SK_FLOW_STAT_LOC_SEND; + } else { + /* PAUSE mismatch -> no PAUSE */ + pPrt->PFlowCtrlStatus = SK_FLOW_STAT_NONE; + } + + /* We checked everything and may now enable the link */ + pPrt->PAutoNegFail = SK_FALSE ; + + SkXmRxTxEnable(pAC, IoC, Port); + return(SK_AND_OK) ; +} + +/****************************************************************************** + * + * SkXmAutoNegDoneBcom() - Auto negotiation handling + * + * Description: + * This function handles the autonegotiation if the Done bit is set. + * + * Note: + * o The XMACs interrupt source register is NOT read here. + * + * Returns: + * SK_AND_OK o.k. + * SK_AND_DUP_CAP Duplex capability error happened + * SK_AND_OTHER Other error happened + */ +static int SkXmAutoNegDoneBcom( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port) /* Port Index (MAC_1 + n) */ +{ + SK_GEPORT *pPrt; + SK_U16 ResAb ; /* Resolved Ability */ + SK_U16 LPAb ; /* Link Partner Ability */ + SK_U16 AuxStat; /* Auxiliary Status */ + + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, ("AutoNegDoneBcom," + " Port %d\n",Port)); + pPrt = &pAC->GIni.GP[Port]; + + /* Get PHY parameters */ + PHY_READ(IoC, pPrt, Port, PHY_BCOM_AUNE_LP, &LPAb); + PHY_READ(IoC, pPrt, Port, PHY_BCOM_1000T_STAT, &ResAb); + PHY_READ(IoC, pPrt, Port, PHY_BCOM_AUX_STAT, &AuxStat); + + if (LPAb & PHY_B_AN_RF) { + /* Remote fault bit is set */ + /* Error */ + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("AutoNegFail: Remote fault bit set Port %d\n", Port)); + pPrt->PAutoNegFail = SK_TRUE ; + return (SK_AND_OTHER) ; + } + + /* Check Duplex mismatch */ + if ((AuxStat & PHY_B_AS_AN_RES) == PHY_B_RES_1000FD) { + pPrt->PLinkModeStatus = SK_LMODE_STAT_AUTOFULL ; + } else if ((AuxStat & PHY_B_AS_AN_RES) == PHY_B_RES_1000HD) { + pPrt->PLinkModeStatus = SK_LMODE_STAT_AUTOHALF ; + } else { + /* Error */ + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("AutoNegFail: Duplex mode mismatch port %d\n", Port)); + pPrt->PAutoNegFail = SK_TRUE ; + return (SK_AND_DUP_CAP) ; + } + + /* Check Master/Slave resolution */ + if (ResAb & (PHY_B_1000S_MSF)) { + /* Error */ + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("Master/Slave Fault port %d\n", Port)); + pPrt->PAutoNegFail = SK_TRUE; + pPrt->PMSStatus = SK_MS_STAT_FAULT; + return (SK_AND_OTHER); + } else if (ResAb & PHY_B_1000S_MSR) { + pPrt->PMSStatus = SK_MS_STAT_MASTER ; + } else { + pPrt->PMSStatus = SK_MS_STAT_SLAVE ; + } + + /* Check PAUSE mismatch */ + /* We are NOT using chapter 4.23 of the Xaqti manual */ + /* We are using IEEE 802.3z/D5.0 Table 37-4 */ + if ((AuxStat & (PHY_B_AS_PRR | PHY_B_AS_PRT)) == + (PHY_B_AS_PRR | PHY_B_AS_PRT)) { + /* Symmetric PAUSE */ + pPrt->PFlowCtrlStatus = SK_FLOW_STAT_SYMMETRIC; + } else if ((AuxStat & (PHY_B_AS_PRR | PHY_B_AS_PRT)) == PHY_B_AS_PRR) { + /* Enable PAUSE receive, disable PAUSE transmit */ + pPrt->PFlowCtrlStatus = SK_FLOW_STAT_REM_SEND; + } else if ((AuxStat & (PHY_B_AS_PRR | PHY_B_AS_PRT)) == PHY_B_AS_PRT) { + /* Disable PAUSE receive, enable PAUSE transmit */ + pPrt->PFlowCtrlStatus = SK_FLOW_STAT_LOC_SEND; + } else { + /* PAUSE mismatch -> no PAUSE */ + pPrt->PFlowCtrlStatus = SK_FLOW_STAT_NONE; + } + + /* We checked everything and may now enable the link */ + pPrt->PAutoNegFail = SK_FALSE ; + + SkXmRxTxEnable(pAC, IoC, Port); + return(SK_AND_OK) ; +} + +/****************************************************************************** + * + * SkXmAutoNegDoneLone() - Auto negotiation handling + * + * Description: + * This function handles the autonegotiation if the Done bit is set. + * + * Note: + * o The XMACs interrupt source register is NOT read here. + * + * Returns: + * SK_AND_OK o.k. + * SK_AND_DUP_CAP Duplex capability error happened + * SK_AND_OTHER Other error happened + */ +static int SkXmAutoNegDoneLone( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port) /* Port Index (MAC_1 + n) */ +{ + SK_GEPORT *pPrt; + SK_U16 ResAb ; /* Resolved Ability */ + SK_U16 LPAb ; /* Link Partner Ability */ + SK_U16 QuickStat; /* Auxiliary Status */ + + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, ("AutoNegDoneLone" + "Port %d\n",Port)); + pPrt = &pAC->GIni.GP[Port]; + + /* Get PHY parameters */ + PHY_READ(IoC, pPrt, Port, PHY_LONE_AUNE_LP, &LPAb); + PHY_READ(IoC, pPrt, Port, PHY_LONE_1000T_STAT, &ResAb); + PHY_READ(IoC, pPrt, Port, PHY_LONE_Q_STAT, &QuickStat); + + if (LPAb & PHY_L_AN_RF) { + /* Remote fault bit is set */ + /* Error */ + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("AutoNegFail: Remote fault bit set Port %d\n", Port)); + pPrt->PAutoNegFail = SK_TRUE ; + return (SK_AND_OTHER) ; + } + + /* Check Duplex mismatch */ + if (QuickStat & PHY_L_QS_DUP_MOD) { + pPrt->PLinkModeStatus = SK_LMODE_STAT_AUTOFULL ; + } else { + pPrt->PLinkModeStatus = SK_LMODE_STAT_AUTOHALF ; + } + + /* Check Master/Slave resolution */ + if (ResAb & (PHY_L_1000S_MSF)) { + /* Error */ + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_CTRL, + ("Master/Slave Fault port %d\n", Port)); + pPrt->PAutoNegFail = SK_TRUE; + pPrt->PMSStatus = SK_MS_STAT_FAULT ; + return (SK_AND_OTHER); + } else if (ResAb & PHY_L_1000S_MSR) { + pPrt->PMSStatus = SK_MS_STAT_MASTER ; + } else { + pPrt->PMSStatus = SK_MS_STAT_SLAVE ; + } + + /* Check PAUSE mismatch */ + /* We are NOT using chapter 4.23 of the Xaqti manual */ + /* We are using IEEE 802.3z/D5.0 Table 37-4 */ + /* we must manually resolve the abilities here */ + pPrt->PFlowCtrlStatus = SK_FLOW_STAT_NONE; + switch (pPrt->PFlowCtrlMode) { + case SK_FLOW_MODE_NONE: + /* default */ + break; + case SK_FLOW_MODE_LOC_SEND: + if ((QuickStat & (PHY_L_QS_PAUSE | PHY_L_QS_AS_PAUSE)) == + (PHY_L_QS_PAUSE | PHY_L_QS_AS_PAUSE)) { + /* Disable PAUSE receive, enable PAUSE transmit */ + pPrt->PFlowCtrlStatus = SK_FLOW_STAT_LOC_SEND; + } + break; + case SK_FLOW_MODE_SYMMETRIC: + if ((QuickStat & PHY_L_QS_PAUSE) == PHY_L_QS_PAUSE) { + /* Symmetric PAUSE */ + pPrt->PFlowCtrlStatus = SK_FLOW_STAT_SYMMETRIC; + } + break; + case SK_FLOW_MODE_SYM_OR_REM: + if ((QuickStat & (PHY_L_QS_PAUSE | PHY_L_QS_AS_PAUSE)) == + PHY_L_QS_AS_PAUSE) { + /* Enable PAUSE receive, disable PAUSE transmit */ + pPrt->PFlowCtrlStatus = SK_FLOW_STAT_REM_SEND; + } + else if ((QuickStat & PHY_L_QS_PAUSE) == PHY_L_QS_PAUSE) { + /* Symmetric PAUSE */ + pPrt->PFlowCtrlStatus = SK_FLOW_STAT_SYMMETRIC; + } + break; + default: + SK_ERR_LOG(pAC, SK_ERRCL_SW | SK_ERRCL_INIT, + SKERR_HWI_E016, SKERR_HWI_E016MSG); + } + + /* We checked everything and may now enable the link */ + pPrt->PAutoNegFail = SK_FALSE ; + + SkXmRxTxEnable(pAC, IoC, Port); + return(SK_AND_OK); +} + +/****************************************************************************** + * + * SkXmAutoNegDoneNat() - Auto negotiation handling + * + * Description: + * This function handles the autonegotiation if the Done bit is set. + * + * Note: + * o The XMACs interrupt source register is NOT read here. + * o This function is public because it is used in the diagnostics + * tool, too. + * + * Returns: + * SK_AND_OK o.k. + * SK_AND_DUP_CAP Duplex capability error happened + * SK_AND_OTHER Other error happened + */ +static int SkXmAutoNegDoneNat( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port) /* Port Index (MAC_1 + n) */ +{ +/* todo: National */ + return(SK_AND_OK); +} + +/****************************************************************************** + * + * SkXmRxTxEnable() - Enable RxTx activity if port is up + * + * Description: + * + * Note: + * o The XMACs interrupt source register is NOT read here. + * + * Returns: + * 0 o.k. + * != 0 Error happened + */ +int SkXmRxTxEnable( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port) /* Port Index (MAC_1 + n) */ +{ + SK_GEPORT *pPrt; + SK_U16 Reg ; /* 16bit register value */ + SK_U16 IntMask; /* XMac interrupt mask */ + + pPrt = &pAC->GIni.GP[Port]; + + if (!pPrt->PHWLinkUp) { + /* The Hardware link is NOT up */ + return(0) ; + } + + if ((pPrt->PLinkMode == SK_LMODE_AUTOHALF || + pPrt->PLinkMode == SK_LMODE_AUTOFULL || + pPrt->PLinkMode == SK_LMODE_AUTOBOTH) && + pPrt->PAutoNegFail) { + /* Autonegotiation is not done or failed */ + return(0) ; + } + + /* Set Dup Mode and Pause Mode */ + SkXmInitDupMd (pAC, IoC, Port); + SkXmInitPauseMd (pAC, IoC, Port); + + /* + * Initialize the Interrupt Mask Register. Default IRQs are... + * - Link Asynchronous Event + * - Link Partner requests config + * - Auto Negotiation Done + * - Rx Counter Event Overflow + * - Tx Counter Event Overflow + * - Transmit FIFO Underrun + */ + if (pPrt->PhyType == SK_PHY_XMAC) { + IntMask = XM_DEF_MSK; + } + else { + /* disable GP0 interrupt bit */ + IntMask = XM_DEF_MSK | XM_IS_INP_ASS; + } + XM_OUT16(IoC, Port, XM_IMSK, IntMask); + + /* RX/TX enable */ + XM_IN16(IoC, Port, XM_MMU_CMD, &Reg); + if (pPrt->PhyType != SK_PHY_XMAC && + (pPrt->PLinkModeStatus == SK_LMODE_STAT_FULL || + pPrt->PLinkModeStatus == SK_LMODE_STAT_AUTOFULL)) { + Reg |= XM_MMU_GMII_FD; + } + switch (pPrt->PhyType) { + case SK_PHY_BCOM: + PHY_WRITE(IoC, pPrt, Port, PHY_BCOM_INT_MASK, + PHY_B_DEF_MSK); + break; + case SK_PHY_LONE: + PHY_WRITE(IoC, pPrt, Port, PHY_LONE_INT_ENAB, + PHY_L_DEF_MSK); + break; + case SK_PHY_NAT: + /* todo National: + PHY_WRITE(IoC, pPrt, Port, PHY_NAT_INT_MASK, + PHY_N_DEF_MSK); */ + /* no interrupts possible from National ??? */ + break; + } + XM_OUT16(IoC, Port, XM_MMU_CMD, Reg | XM_MMU_ENA_RX | XM_MMU_ENA_TX); + + return (0); +} + +#ifndef SK_DIAG +/****************************************************************************** + * + * SkXmIrq() - Interrupt service routine + * + * Description: + * Services an Interrupt of the XMAC II + * + * Note: + * The XMACs interrupt source register is NOT read here. + * With an external PHY, some interrupt bits are not meaningfull + * any more: + * - LinkAsyncEvent (bit #14) XM_IS_LNK_AE + * - LinkPartnerReqConfig (bit #10) XM_IS_LIPA_RC + * - Page Received (bit #9) XM_IS_RX_PAGE + * - NextPageLoadedForXmt (bit #8) XM_IS_TX_PAGE + * - AutoNegDone (bit #7) XM_IS_AND + * Also probably not valid any more is the GP0 input bit: + * - GPRegisterBit0set XM_IS_INP_ASS + * + * Returns: + * nothing + */ +void SkXmIrq( +SK_AC *pAC, /* adapter context */ +SK_IOC IoC, /* IO context */ +int Port, /* Port Index (MAC_1 + n) */ +SK_U16 IStatus) /* Interrupt status read from the XMAC */ +{ + SK_GEPORT *pPrt; + SK_EVPARA Para; + + pPrt = &pAC->GIni.GP[Port]; + + if (pPrt->PhyType != SK_PHY_XMAC) { + /* mask bits that are not used with ext. PHY */ + IStatus &= ~(XM_IS_LNK_AE | XM_IS_LIPA_RC | + XM_IS_RX_PAGE | XM_IS_TX_PAGE | + XM_IS_AND | XM_IS_INP_ASS); + } + + /* + * LinkPartner Autonegable ? + */ + if (pPrt->PhyType == SK_PHY_XMAC) { + SkXmAutoNegLipaXmac(pAC, IoC, Port, IStatus); + } + + SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_IRQ, + ("XmacIrq Port %d Isr %x\n", Port, IStatus)); + + if (!pPrt->PHWLinkUp) { + /* Spurious XMAC interrupt */ + SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_IRQ, + ("SkXmIrq: spurious interrupt on port %d\n", + Port)); + return; + } + + if (IStatus & XM_IS_LNK_AE) { + /* not used GP0 is used instead */ + } + + if (IStatus & XM_IS_TX_ABORT) { + /* not used */ + } + + if (IStatus & XM_IS_FRC_INT) { + /* not used. use ASIC IRQ instead if needed */ + } + + if (IStatus & (XM_IS_INP_ASS | XM_IS_LIPA_RC | XM_IS_RX_PAGE)) { + SkHWLinkDown(pAC, IoC, Port); + + /* Signal to RLMT */ + Para.Para32[0] = (SK_U32) Port; + SkEventQueue(pAC, SKGE_RLMT, SK_RLMT_LINK_DOWN, Para); + + /* Start workaround Errata #2 timer */ + SkTimerStart(pAC, IoC, &pAC->GIni.GP[Port].PWaTimer, + SK_WA_INA_TIME,SKGE_HWAC,SK_HWEV_WATIM,Para); + } + + if (IStatus & XM_IS_RX_PAGE) { + /* not used */ + } + + if (IStatus & XM_IS_TX_PAGE) { + /* not used */ + } + + if (IStatus & XM_IS_AND) { + SK_DBG_MSG(pAC,SK_DBGMOD_HWM,SK_DBGCAT_IRQ, + ("SkXmIrq: AND on link that is up port %d\n", Port)); + } + + if (IStatus & XM_IS_TSC_OV) { + /* not used */ + } + + if (IStatus & XM_IS_RXC_OV) { + Para.Para32[0] = (SK_U32) Port; + Para.Para32[1] = (SK_U32) IStatus; + SkPnmiEvent(pAC, IoC, SK_PNMI_EVT_SIRQ_OVERFLOW, Para); + } + + if (IStatus & XM_IS_TXC_OV) { + Para.Para32[0] = (SK_U32) Port; + Para.Para32[1] = (SK_U32) IStatus; + SkPnmiEvent(pAC, IoC, SK_PNMI_EVT_SIRQ_OVERFLOW, Para); + } + + if (IStatus & XM_IS_RXF_OV) { + /* normal situation -> no effect */ + } + + if (IStatus & XM_IS_TXF_UR) { + /* may NOT happen -> error log */ + SK_ERR_LOG(pAC, SK_ERRCL_HW , SKERR_SIRQ_E020, + SKERR_SIRQ_E020MSG) ; + } + + if (IStatus & XM_IS_TX_COMP) { + /* not served here */ + } + + if (IStatus & XM_IS_RX_COMP) { + /* not served here */ + } + +} +#endif /* !SK_DIAG */ + +/* End of file */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sktr.c linux/drivers/net/sktr.c --- v2.2.13/linux/drivers/net/sktr.c Tue Jan 4 11:10:37 2000 +++ linux/drivers/net/sktr.c Tue Jan 4 10:12:18 2000 @@ -258,12 +258,12 @@ /* Remove I/O space marker in bit 0. */ pci_ioaddr &= ~3; - if((vendor != PCI_VENDOR_ID_SK) && + if((vendor != PCI_VENDOR_ID_SYSKONNECT) && (vendor != PCI_VENDOR_ID_COMPAQ)) continue; - if((vendor == PCI_VENDOR_ID_SK) && - (device != PCI_DEVICE_ID_SK_TR)) + if((vendor == PCI_VENDOR_ID_SYSKONNECT) && + (device != PCI_DEVICE_ID_SYSKONNECT_TR)) continue; else if((vendor == PCI_VENDOR_ID_COMPAQ) && (device != PCI_DEVICE_ID_COMPAQ_TOKENRING)) diff -u --recursive --new-file v2.2.13/linux/drivers/net/slhc.c linux/drivers/net/slhc.c --- v2.2.13/linux/drivers/net/slhc.c Fri Dec 18 09:40:56 1998 +++ linux/drivers/net/slhc.c Tue Jan 4 10:12:18 2000 @@ -760,12 +760,6 @@ #endif /* MODULE */ #else /* CONFIG_INET */ -EXPORT_SYMBOL(slhc_init); -EXPORT_SYMBOL(slhc_free); -EXPORT_SYMBOL(slhc_remember); -EXPORT_SYMBOL(slhc_compress); -EXPORT_SYMBOL(slhc_uncompress); -EXPORT_SYMBOL(slhc_toss); int slhc_toss(struct slcompress *comp) @@ -806,5 +800,11 @@ printk(KERN_DEBUG "Called IP function on non IP-system: slhc_init"); return NULL; } +EXPORT_SYMBOL(slhc_init); +EXPORT_SYMBOL(slhc_free); +EXPORT_SYMBOL(slhc_remember); +EXPORT_SYMBOL(slhc_compress); +EXPORT_SYMBOL(slhc_uncompress); +EXPORT_SYMBOL(slhc_toss); #endif /* CONFIG_INET */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sunbmac.c linux/drivers/net/sunbmac.c --- v2.2.13/linux/drivers/net/sunbmac.c Mon Mar 15 16:11:30 1999 +++ linux/drivers/net/sunbmac.c Tue Jan 4 10:12:18 2000 @@ -85,7 +85,7 @@ } if(tries) return 0; - printk("BigMAC: Cannot reset the QEC.\n"); + printk(KERN_ERR "BigMAC: Cannot reset the QEC.\n"); return -1; } @@ -133,8 +133,8 @@ udelay(20); if(!tries) { - printk("BIGMAC: Transmitter will not reset.\n"); - printk("BIGMAC: tx_cfg is %08x\n", bregs->tx_cfg); + printk(KERN_ERR "BIGMAC: Transmitter will not reset.\n"); + printk(KERN_ERR "BIGMAC: tx_cfg is %08x\n", bregs->tx_cfg); } } @@ -147,8 +147,8 @@ udelay(20); if(!tries) { - printk("BIGMAC: Receiver will not reset.\n"); - printk("BIGMAC: rx_cfg is %08x\n", bregs->rx_cfg); + printk(KERN_ERR "BIGMAC: Receiver will not reset.\n"); + printk(KERN_ERR "BIGMAC: rx_cfg is %08x\n", bregs->rx_cfg); } } @@ -292,7 +292,7 @@ MGMT_PAL_DCLOCK); garbage = tregs->mgmt_pal; } else { - printk("write_tcvr_bit: No transceiver type known!\n"); + printk(KERN_ERR "write_tcvr_bit: No transceiver type known!\n"); } } @@ -314,7 +314,7 @@ garbage = tregs->mgmt_pal; retval = (tregs->mgmt_pal & MGMT_PAL_EXT_MDIO) >> 2; } else { - printk("read_tcvr_bit: No transceiver type known!\n"); + printk(KERN_ERR "read_tcvr_bit: No transceiver type known!\n"); } return retval; } @@ -337,7 +337,7 @@ tregs->mgmt_pal = (MGMT_PAL_INT_MDIO | MGMT_PAL_DCLOCK); garbage = tregs->mgmt_pal; } else { - printk("read_tcvr_bit2: No transceiver type known!\n"); + printk(KERN_ERR "read_tcvr_bit2: No transceiver type known!\n"); } return retval; } @@ -367,7 +367,7 @@ break; default: - printk("bigmac_tcvr_read: Whoops, no known transceiver type.\n"); + printk(KERN_ERR "bigmac_tcvr_read: Whoops, no known transceiver type.\n"); return; }; @@ -406,7 +406,7 @@ break; default: - printk("bigmac_tcvr_read: Whoops, no known transceiver type.\n"); + printk(KERN_ERR "bigmac_tcvr_read: Whoops, no known transceiver type.\n"); return 0xffff; }; @@ -493,9 +493,9 @@ TCVR_PAL_LTENABLE); garbage = tregs->tcvr_pal; } else { - printk("BIGMAC: AIEEE, neither internal nor " + printk(KERN_ERR "BIGMAC: AIEEE, neither internal nor " "external MDIO available!\n"); - printk("BIGMAC: mgmt_pal[%08x] tcvr_pal[%08x]\n", + printk(KERN_ERR "BIGMAC: mgmt_pal[%08x] tcvr_pal[%08x]\n", tregs->mgmt_pal, tregs->tcvr_pal); } } @@ -521,7 +521,7 @@ udelay(20); } if(timeout == 0) - printk("%s: PHY reset failed.\n", bp->dev->name); + printk(KERN_ERR "%s: PHY reset failed.\n", bp->dev->name); bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR); @@ -546,7 +546,7 @@ bp->sw_bmsr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMSR); bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR); if(bp->sw_bmsr & BMSR_LSTATUS) { - printk("%s: Link is now up at %s.\n", + printk(KERN_INFO "%s: Link is now up at %s.\n", bp->dev->name, (bp->sw_bmcr & BMCR_SPEED100) ? "100baseT" : "10baseT"); @@ -558,11 +558,11 @@ ret = try_next_permutation(bp, tregs); if(ret == -1) { - printk("%s: Link down, cable problem?\n", + printk(KERN_ERR "%s: Link down, cable problem?\n", bp->dev->name); ret = bigmac_init(bp, 0); if(ret) { - printk("%s: Error, cannot re-init the " + printk(KERN_ERR "%s: Error, cannot re-init the " "BigMAC.\n", bp->dev->name); } return; @@ -575,7 +575,7 @@ } } else { /* Can't happens.... */ - printk("%s: Aieee, link timer is asleep but we got one anyways!\n", + printk(KERN_ERR "%s: Aieee, link timer is asleep but we got one anyways!\n", bp->dev->name); restart_timer = 0; bp->timer_ticks = 0; @@ -614,7 +614,7 @@ udelay(20); } if(timeout == 0) - printk("%s: PHY reset failed.\n", bp->dev->name); + printk(KERN_ERR "%s: PHY reset failed.\n", bp->dev->name); bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR); @@ -724,7 +724,7 @@ unsigned int qec_status, unsigned int bmac_status) { - printk("bigmac_is_medium_rare: "); + printk(KERN_ERR "bigmac_is_medium_rare: "); if(qec_status & (GLOB_STAT_ER | GLOB_STAT_BM)) { if(qec_status & GLOB_STAT_ER) printk("QEC_ERROR, "); @@ -838,7 +838,7 @@ #ifdef NEED_DMA_SYNCHRONIZATION #ifdef __sparc_v9__ if ((unsigned long) (skb->data + skb->len) >= MAX_DMA_ADDRESS) { - printk("sunbmac: Bogus DMA buffer address " + printk(KERN_CRIT "sunbmac: Bogus DMA buffer address " "[%016lx]\n", ((unsigned long) skb->data)); panic("DMA address too large, tell DaveM"); } @@ -895,7 +895,7 @@ } bp->rx_new = elem; if(drops) - printk("%s: Memory squeeze, deferring packet.\n", bp->dev->name); + printk(KERN_NOTICE "%s: Memory squeeze, deferring packet.\n", bp->dev->name); } #ifndef __sparc_v9__ @@ -948,7 +948,7 @@ } bp->rx_new = elem; if(drops) - printk("%s: Memory squeeze, deferring packet.\n", bp->dev->name); + printk(KERN_NOTICE "%s: Memory squeeze, deferring packet.\n", bp->dev->name); } #endif @@ -1024,14 +1024,14 @@ if(sparc_cpu_model == sun4c) { if(request_irq(dev->irq, &sun4c_bigmac_interrupt, SA_SHIRQ, "BIG MAC", (void *) bp)) { - printk("BIGMAC: Can't order irq %d to go.\n", dev->irq); + printk(KERN_ERR "BIGMAC: Can't order irq %d to go.\n", dev->irq); return -EAGAIN; } } else #endif if(request_irq(dev->irq, &bigmac_interrupt, SA_SHIRQ, "BIG MAC", (void *) bp)) { - printk("BIGMAC: Can't order irq %d to go.\n", dev->irq); + printk(KERN_ERR "BIGMAC: Can't order irq %d to go.\n", dev->irq); return -EAGAIN; } init_timer(&bp->bigmac_timer); @@ -1069,7 +1069,7 @@ if (tickssofar < 40) { return 1; } else { - printk ("%s: transmit timed out, resetting\n", dev->name); + printk (KERN_ERR "%s: transmit timed out, resetting\n", dev->name); bp->enet_stats.tx_errors++; bigmac_init(bp, 0); dev->tbusy = 0; @@ -1080,7 +1080,7 @@ } if(test_and_set_bit(0, (void *) &dev->tbusy) != 0) { - printk("%s: Transmitter access conflict.\n", dev->name); + printk(KERN_ERR "%s: Transmitter access conflict.\n", dev->name); return 1; } @@ -1138,7 +1138,7 @@ if (tickssofar < 40) { return 1; } else { - printk ("%s: transmit timed out, resetting\n", dev->name); + printk (KERN_ERR "%s: transmit timed out, resetting\n", dev->name); bp->enet_stats.tx_errors++; bigmac_init(bp, 0); dev->tbusy = 0; @@ -1148,7 +1148,7 @@ } if(test_and_set_bit(0, (void *) &dev->tbusy) != 0) { - printk("%s: Transmitter access conflict.\n", dev->name); + printk(KERN_ERR "%s: Transmitter access conflict.\n", dev->name); return 1; } @@ -1266,10 +1266,10 @@ dev = init_etherdev(0, sizeof(struct bigmac)); if(version_printed++ == 0) - printk(version); + printk(KERN_INFO "%s", version); /* Report what we have found to the user. */ - printk("%s: BigMAC 100baseT Ethernet ", dev->name); + printk(KERN_INFO "%s: BigMAC 100baseT Ethernet ", dev->name); dev->base_addr = (long) qec_sdev; for(i = 0; i < 6; i++) printk("%2.2x%c", dev->dev_addr[i] = idprom->id_ethaddr[i], @@ -1287,10 +1287,10 @@ /* Verify the registers we expect, are actually there. */ if((bp->bigmac_sbus_dev->num_registers != 3) || (bp->qec_sbus_dev->num_registers != 2)) { - printk("BIGMAC: Device does not have 2 and 3 regs, it has %d and %d.\n", + printk(KERN_ERR "BIGMAC: Device does not have 2 and 3 regs, it has %d and %d.\n", bp->qec_sbus_dev->num_registers, bp->bigmac_sbus_dev->num_registers); - printk("BIGMAC: Would you like that for here or to go?\n"); + printk(KERN_ERR "BIGMAC: Would you like that for here or to go?\n"); goto fail_and_cleanup; } @@ -1309,7 +1309,7 @@ qranges[k].ot_child_space) break; if(k >= num_qranges) { - printk("BigMAC: Aieee, bogus QEC range for space %08x\n", + printk(KERN_ERR "BigMAC: Aieee, bogus QEC range for space %08x\n", bp->bigmac_sbus_dev->reg_addrs[j].which_io); goto fail_and_cleanup; } @@ -1338,13 +1338,13 @@ bp->qec_sbus_dev->reg_addrs[0].which_io, 0); if(!bp->gregs) { - printk("BIGMAC: Cannot map QEC global registers.\n"); + printk(KERN_ERR "BIGMAC: Cannot map QEC global registers.\n"); goto fail_and_cleanup; } /* Make sure QEC is in BigMAC mode. */ if((bp->gregs->ctrl & 0xf0000000) != GLOB_CTRL_BMODE) { - printk("BigMAC: AIEEE, QEC is not in BigMAC mode!\n"); + printk(KERN_ERR "BigMAC: AIEEE, QEC is not in BigMAC mode!\n"); goto fail_and_cleanup; } @@ -1380,7 +1380,7 @@ bp->bigmac_sbus_dev->reg_addrs[0].which_io, 0); if(!bp->creg) { - printk("BIGMAC: Cannot map QEC channel registers.\n"); + printk(KERN_ERR "BIGMAC: Cannot map QEC channel registers.\n"); goto fail_and_cleanup; } @@ -1392,7 +1392,7 @@ bp->bigmac_sbus_dev->reg_addrs[1].which_io, 0); if(!bp->bregs) { - printk("BIGMAC: Cannot map BigMAC primary registers.\n"); + printk(KERN_ERR "BIGMAC: Cannot map BigMAC primary registers.\n"); goto fail_and_cleanup; } @@ -1406,7 +1406,7 @@ bp->bigmac_sbus_dev->reg_addrs[2].which_io, 0); if(!bp->tregs) { - printk("BIGMAC: Cannot map BigMAC transceiver registers.\n"); + printk(KERN_ERR "BIGMAC: Cannot map BigMAC transceiver registers.\n"); goto fail_and_cleanup; } diff -u --recursive --new-file v2.2.13/linux/drivers/net/sunhme.c linux/drivers/net/sunhme.c --- v2.2.13/linux/drivers/net/sunhme.c Tue Jan 4 11:10:37 2000 +++ linux/drivers/net/sunhme.c Tue Jan 4 10:12:18 2000 @@ -297,7 +297,7 @@ while(!(hme_read32(hp, &tregs->frame) & 0x10000) && --tries) udelay(20); if(!tries) { - printk("happy meal: Aieee, transceiver MIF read bolixed\n"); + printk(KERN_ERR "happy meal: Aieee, transceiver MIF read bolixed\n"); return TCVR_FAILURE; } retval = hme_read32(hp, &tregs->frame) & 0xffff; @@ -328,7 +328,7 @@ /* Anything else? */ if(!tries) - printk("happy meal: Aieee, transceiver MIF write bolixed\n"); + printk(KERN_ERR "happy meal: Aieee, transceiver MIF write bolixed\n"); /* Fifty-two cents is your change, have a nice day. */ } @@ -391,7 +391,7 @@ static void display_link_mode(struct happy_meal *hp, struct hmeal_tcvregs *tregs) { - printk("%s: Link is up using ", hp->dev->name); + printk(KERN_INFO "%s: Link is up using ", hp->dev->name); if(hp->tcvr_type == external) printk("external "); else @@ -413,7 +413,7 @@ static void display_forced_link_mode(struct happy_meal *hp, struct hmeal_tcvregs *tregs) { - printk("%s: Link has been forced up using ", hp->dev->name); + printk(KERN_INFO "%s: Link has been forced up using ", hp->dev->name); if(hp->tcvr_type == external) printk("external "); else @@ -492,6 +492,25 @@ static int happy_meal_init(struct happy_meal *hp, int from_irq); +static int is_lucent_phy(struct happy_meal *hp) +{ + struct hmeal_tcvregs *tregs = hp->tcvregs; + unsigned short mr2, mr3; + int ret = 0; + + mr2 = happy_meal_tcvr_read(hp, tregs, 2); + mr3 = happy_meal_tcvr_read(hp, tregs, 3); + if ((mr2 & 0xffff) == 0x0180 && + ((mr3 & 0xffff) >> 10) == 0x1d) { +#if 0 + printk("HMEDEBUG: Lucent PHY detected.\n"); +#endif + ret = 1; + } + + return ret; +} + static void happy_meal_timer(unsigned long data) { struct happy_meal *hp = (struct happy_meal *) data; @@ -508,19 +527,22 @@ /* Enter force mode. */ do_force_mode: hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR); - printk("%s: Auto-Negotiation unsuccessful, trying force link mode\n", + printk(KERN_NOTICE "%s: Auto-Negotiation unsuccessful, trying force link mode\n", hp->dev->name); hp->sw_bmcr = BMCR_SPEED100; happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr); - /* OK, seems we need do disable the transceiver for the first - * tick to make sure we get an accurate link state at the - * second tick. - */ - hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG); - hp->sw_csconfig &= ~(CSCONFIG_TCVDISAB); - happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, hp->sw_csconfig); - + if (!is_lucent_phy(hp)) { + /* OK, seems we need do disable the transceiver for + * the first tick to make sure we get an accurate + * link state at the second tick. + */ + hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs, + DP83840_CSCONFIG); + hp->sw_csconfig &= ~(CSCONFIG_TCVDISAB); + happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, + hp->sw_csconfig); + } hp->timer_state = ltrywait; hp->timer_ticks = 0; restart_timer = 1; @@ -567,7 +589,7 @@ restart_timer = 0; } else { if(hp->timer_ticks >= 10) { - printk("%s: Auto negotiation successful, link still " + printk(KERN_NOTICE "%s: Auto negotiation successful, link still " "not completely up.\n", hp->dev->name); hp->timer_ticks = 0; restart_timer = 1; @@ -587,15 +609,24 @@ hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG); if(hp->timer_ticks == 1) { /* Re-enable transceiver, we'll re-enable the transceiver next - * tick, then check link state on the following tick. */ - hp->sw_csconfig |= CSCONFIG_TCVDISAB; - happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, hp->sw_csconfig); + * tick, then check link state on the following tick. + * XXX But dont do this on Lucent PHY -DaveM + */ + if (!is_lucent_phy(hp)) { + hp->sw_csconfig |= CSCONFIG_TCVDISAB; + happy_meal_tcvr_write(hp, tregs, + DP83840_CSCONFIG, hp->sw_csconfig); + } restart_timer = 1; break; } if(hp->timer_ticks == 2) { - hp->sw_csconfig &= ~(CSCONFIG_TCVDISAB); - happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, hp->sw_csconfig); + /* XXX See above about Lucent PHY -DaveM */ + if (!is_lucent_phy(hp)) { + hp->sw_csconfig &= ~(CSCONFIG_TCVDISAB); + happy_meal_tcvr_write(hp, tregs, + DP83840_CSCONFIG, hp->sw_csconfig); + } restart_timer = 1; break; } @@ -616,20 +647,25 @@ */ /* Let the user know... */ - printk("%s: Link down, cable problem?\n", + printk(KERN_NOTICE "%s: Link down, cable problem?\n", hp->dev->name); ret = happy_meal_init(hp, 0); if(ret) { /* ho hum... */ - printk("%s: Error, cannot re-init the " + printk(KERN_ERR "%s: Error, cannot re-init the " "Happy Meal.\n", hp->dev->name); } return; } - hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG); - hp->sw_csconfig |= CSCONFIG_TCVDISAB; - happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, hp->sw_csconfig); + hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs, + DP83840_CSCONFIG); + if (!is_lucent_phy(hp)) { + hp->sw_csconfig |= CSCONFIG_TCVDISAB; + happy_meal_tcvr_write(hp, tregs, + DP83840_CSCONFIG, + hp->sw_csconfig); + } hp->timer_ticks = 0; restart_timer = 1; } else { @@ -641,7 +677,7 @@ case asleep: default: /* Can't happens.... */ - printk("%s: Aieee, link timer is asleep but we got one anyways!\n", + printk(KERN_ERR "%s: Aieee, link timer is asleep but we got one anyways!\n", hp->dev->name); restart_timer = 0; hp->timer_ticks = 0; @@ -672,7 +708,7 @@ /* Lettuce, tomato, buggy hardware (no extra charge)? */ if(!tries) - printk("happy meal: Transceiver BigMac ATTACK!"); + printk(KERN_ERR "happy meal: Transceiver BigMac ATTACK!"); /* Take care. */ HMD(("done\n")); @@ -692,7 +728,7 @@ /* Will that be all? */ if(!tries) - printk("happy meal: Receiver BigMac ATTACK!"); + printk(KERN_ERR "happy meal: Receiver BigMac ATTACK!"); /* Don't forget your vik_1137125_wa. Have a nice day. */ HMD(("done\n")); @@ -714,7 +750,7 @@ /* Come back next week when we are "Sun Microelectronics". */ if(!tries) - printk("happy meal: Fry guys."); + printk(KERN_ERR "happy meal: Fry guys."); /* Remember: "Different name, same old buggy as shit hardware." */ HMD(("done\n")); @@ -910,8 +946,11 @@ return -1; } ASD((" SUCCESS and CSCONFIG_DFBYPASS\n")); - result = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG); - happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, (result | CSCONFIG_DFBYPASS)); + if (!is_lucent_phy(hp)) { + result = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG); + happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, + (result | CSCONFIG_DFBYPASS)); + } return 0; } @@ -972,7 +1011,7 @@ hp->tcvr_type = internal; ASD(("\n")); } else { - printk("happy meal: Transceiver and a coke please."); + printk(KERN_ERR "happy meal: Transceiver and a coke please."); hp->tcvr_type = none; /* Grrr... */ ASD(("\n")); } @@ -1201,9 +1240,9 @@ udelay(10); } if(!timeout) { - printk("%s: Happy Meal would not start auto negotiation " + printk(KERN_ERR "%s: Happy Meal would not start auto negotiation " "BMCR=0x%04x\n", hp->dev->name, hp->sw_bmcr); - printk("%s: Performing force link detection.\n", + printk(KERN_NOTICE "%s: Performing force link detection.\n", hp->dev->name); goto force_link; } else { @@ -1230,17 +1269,17 @@ hp->sw_bmcr |= BMCR_FULLDPLX; } happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr); - - /* OK, seems we need do disable the transceiver for the first - * tick to make sure we get an accurate link state at the - * second tick. - */ - hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs, - DP83840_CSCONFIG); - hp->sw_csconfig &= ~(CSCONFIG_TCVDISAB); - happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, - hp->sw_csconfig); - + if (!is_lucent_phy(hp)) { + /* OK, seems we need do disable the transceiver for the first + * tick to make sure we get an accurate link state at the + * second tick. + */ + hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs, + DP83840_CSCONFIG); + hp->sw_csconfig &= ~(CSCONFIG_TCVDISAB); + happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, + hp->sw_csconfig); + } hp->timer_state = ltrywait; } @@ -1479,8 +1518,8 @@ regtmp = hme_read32(hp, &erxregs->cfg); hme_write32(hp, &erxregs->cfg, ERX_CFG_DEFAULT(RX_OFFSET)); if(hme_read32(hp, &erxregs->cfg) != ERX_CFG_DEFAULT(RX_OFFSET)) { - printk("happy meal: Eieee, rx config register gets greasy fries.\n"); - printk("happy meal: Trying to set %08x, reread gives %08lx\n", + printk(KERN_ERR "happy meal: Eieee, rx config register gets greasy fries.\n"); + printk(KERN_ERR "happy meal: Trying to set %08x, reread gives %08lx\n", ERX_CFG_DEFAULT(RX_OFFSET), regtmp); /* XXX Should return failure here... */ } @@ -1605,24 +1644,24 @@ GREG_STAT_MIFIRQ | GREG_STAT_TXEACK | GREG_STAT_TXLERR | GREG_STAT_TXPERR | GREG_STAT_TXTERR | GREG_STAT_SLVERR | GREG_STAT_SLVPERR)) - printk("%s: Error interrupt for happy meal, status = %08lx\n", + printk(KERN_ERR "%s: Error interrupt for happy meal, status = %08lx\n", hp->dev->name, status); if(status & GREG_STAT_RFIFOVF) { /* The receive FIFO overflowwed, usually a DMA error. */ - printk("%s: Happy Meal receive FIFO overflow.\n", hp->dev->name); + printk(KERN_ERR "%s: Happy Meal receive FIFO overflow.\n", hp->dev->name); reset = 1; } if(status & GREG_STAT_STSTERR) { /* BigMAC SQE link test failed. */ - printk("%s: Happy Meal BigMAC SQE test failed.\n", hp->dev->name); + printk(KERN_ERR "%s: Happy Meal BigMAC SQE test failed.\n", hp->dev->name); reset = 1; } if(status & GREG_STAT_TFIFO_UND) { /* Transmit FIFO underrun, again DMA error likely. */ - printk("%s: Happy Meal transmitter FIFO underrun, DMA error.\n", + printk(KERN_ERR "%s: Happy Meal transmitter FIFO underrun, DMA error.\n", hp->dev->name); reset = 1; } @@ -1631,7 +1670,7 @@ /* Driver error, tried to transmit something larger * than ethernet max mtu. */ - printk("%s: Happy Meal MAX Packet size error.\n", hp->dev->name); + printk(KERN_ERR "%s: Happy Meal MAX Packet size error.\n", hp->dev->name); reset = 1; } @@ -1648,7 +1687,7 @@ if(status & (GREG_STAT_RXERR|GREG_STAT_RXPERR|GREG_STAT_RXTERR)) { /* All sorts of DMA receive errors. */ - printk("%s: Happy Meal rx DMA errors [ ", hp->dev->name); + printk(KERN_ERR "%s: Happy Meal rx DMA errors [ ", hp->dev->name); if(status & GREG_STAT_RXERR) printk("GenericError "); if(status & GREG_STAT_RXPERR) @@ -1663,20 +1702,20 @@ /* Driver bug, didn't set EOP bit in tx descriptor given * to the happy meal. */ - printk("%s: EOP not set in happy meal transmit descriptor!\n", + printk(KERN_ERR "%s: EOP not set in happy meal transmit descriptor!\n", hp->dev->name); reset = 1; } if(status & GREG_STAT_MIFIRQ) { /* MIF signalled an interrupt, were we polling it? */ - printk("%s: Happy Meal MIF interrupt.\n", hp->dev->name); + printk(KERN_ERR "%s: Happy Meal MIF interrupt.\n", hp->dev->name); } if(status & (GREG_STAT_TXEACK|GREG_STAT_TXLERR|GREG_STAT_TXPERR|GREG_STAT_TXTERR)) { /* All sorts of transmit DMA errors. */ - printk("%s: Happy Meal tx DMA errors [ ", hp->dev->name); + printk(KERN_ERR "%s: Happy Meal tx DMA errors [ ", hp->dev->name); if(status & GREG_STAT_TXEACK) printk("GenericError "); if(status & GREG_STAT_TXLERR) @@ -1693,14 +1732,14 @@ /* Bus or parity error when cpu accessed happy meal registers * or it's internal FIFO's. Should never see this. */ - printk("%s: Happy Meal register access SBUS slave (%s) error.\n", + printk(KERN_ERR "%s: Happy Meal register access SBUS slave (%s) error.\n", hp->dev->name, (status & GREG_STAT_SLVPERR) ? "parity" : "generic"); reset = 1; } if(reset) { - printk("%s: Resetting...\n", hp->dev->name); + printk(KERN_NOTICE "%s: Resetting...\n", hp->dev->name); happy_meal_init(hp, 1); return 1; } @@ -1711,22 +1750,22 @@ struct hmeal_gregs *gregs, struct hmeal_tcvregs *tregs) { - printk("%s: Link status change.\n", hp->dev->name); + printk(KERN_INFO "%s: Link status change.\n", hp->dev->name); hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR); hp->sw_lpa = happy_meal_tcvr_read(hp, tregs, DP83840_LPA); /* Use the fastest transmission protocol possible. */ if(hp->sw_lpa & LPA_100FULL) { - printk("%s: Switching to 100Mbps at full duplex.", hp->dev->name); + printk(KERN_INFO "%s: Switching to 100Mbps at full duplex.", hp->dev->name); hp->sw_bmcr |= (BMCR_FULLDPLX | BMCR_SPEED100); } else if(hp->sw_lpa & LPA_100HALF) { - printk("%s: Switching to 100MBps at half duplex.", hp->dev->name); + printk(KERN_INFO "%s: Switching to 100MBps at half duplex.", hp->dev->name); hp->sw_bmcr |= BMCR_SPEED100; } else if(hp->sw_lpa & LPA_10FULL) { - printk("%s: Switching to 10MBps at full duplex.", hp->dev->name); + printk(KERN_INFO "%s: Switching to 10MBps at full duplex.", hp->dev->name); hp->sw_bmcr |= BMCR_FULLDPLX; } else { - printk("%s: Using 10Mbps at half duplex.", hp->dev->name); + printk(KERN_INFO "%s: Using 10Mbps at half duplex.", hp->dev->name); } happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr); @@ -1942,7 +1981,7 @@ } hp->rx_new = elem; if(drops) - printk("%s: Memory squeeze, deferring packet.\n", hp->dev->name); + printk(KERN_INFO "%s: Memory squeeze, deferring packet.\n", hp->dev->name); RXD((">")); } @@ -2061,7 +2100,7 @@ } hp->rx_new = elem; if(drops) - printk("%s: Memory squeeze, deferring packet.\n", hp->dev->name); + printk(KERN_INFO "%s: Memory squeeze, deferring packet.\n", hp->dev->name); RXD((">")); } #endif @@ -2126,7 +2165,7 @@ } hp->rx_new = elem; if(drops) - printk("%s: Memory squeeze, deferring packet.\n", hp->dev->name); + printk(KERN_INFO "%s: Memory squeeze, deferring packet.\n", hp->dev->name); RXD((">")); } @@ -2235,7 +2274,7 @@ } hp->rx_new = elem; if(drops) - printk("%s: Memory squeeze, deferring packet.\n", hp->dev->name); + printk(KERN_INFO "%s: Memory squeeze, deferring packet.\n", hp->dev->name); RXD((">")); } #endif @@ -2487,14 +2526,14 @@ if(request_irq(dev->irq, &sun4c_happy_meal_interrupt, SA_SHIRQ, "HAPPY MEAL", (void *) dev)) { HMD(("EAGAIN\n")); - printk("happy meal: Can't order irq %d to go.\n", dev->irq); + printk(KERN_ERR "happy meal: Can't order irq %d to go.\n", dev->irq); return -EAGAIN; } } else if (sparc_cpu_model == sun4d) { if(request_irq(dev->irq, &sun4d_happy_meal_interrupt, SA_SHIRQ, "HAPPY MEAL", (void *) dev)) { HMD(("EAGAIN\n")); - printk("happy_meal(SBUS): Can't order irq %s to go.\n", + printk(KERN_ERR "happy_meal(SBUS): Can't order irq %s to go.\n", __irq_itoa(dev->irq)); return -EAGAIN; } @@ -2505,7 +2544,7 @@ if(request_irq(dev->irq, &pci_happy_meal_interrupt, SA_SHIRQ, "HAPPY MEAL (PCI)", dev)) { HMD(("EAGAIN\n")); - printk("happy_meal(PCI: Can't order irq %s to go.\n", + printk(KERN_ERR "happy_meal(PCI: Can't order irq %s to go.\n", __irq_itoa(dev->irq)); return -EAGAIN; } @@ -2514,7 +2553,7 @@ if(request_irq(dev->irq, &happy_meal_interrupt, SA_SHIRQ, "HAPPY MEAL", (void *)dev)) { HMD(("EAGAIN\n")); - printk("happy_meal(SBUS): Can't order irq %s to go.\n", + printk(KERN_ERR "happy_meal(SBUS): Can't order irq %s to go.\n", __irq_itoa(dev->irq)); return -EAGAIN; } @@ -2568,10 +2607,10 @@ int tickssofar = jiffies - dev->trans_start; if (tickssofar >= 40) { - printk ("%s: transmit timed out, resetting\n", dev->name); + printk (KERN_ERR "%s: transmit timed out, resetting\n", dev->name); hp->net_stats.tx_errors++; tx_dump_log(); - printk ("%s: Happy Status %08x TX[%08x:%08x]\n", dev->name, + printk (KERN_ERR "%s: Happy Status %08x TX[%08x:%08x]\n", dev->name, hme_read32(hp, &hp->gregs->stat), hme_read32(hp, &hp->etxregs->cfg), hme_read32(hp, &hp->bigmacregs->tx_cfg)); @@ -2633,7 +2672,7 @@ if (tickssofar >= 40) { unsigned long flags; - printk ("%s: transmit timed out, resetting\n", dev->name); + printk (KERN_ERR "%s: transmit timed out, resetting\n", dev->name); save_and_cli(flags); tx_dump_log(); @@ -2690,7 +2729,7 @@ if (tickssofar < 40) { return 1; } else { - printk ("%s: transmit timed out, resetting\n", dev->name); + printk (KERN_ERR "%s: transmit timed out, resetting\n", dev->name); hp->net_stats.tx_errors++; happy_meal_init(hp, 0); dev->tbusy = 0; @@ -2700,7 +2739,7 @@ } if(test_and_set_bit(0, (void *) &dev->tbusy) != 0) { - printk("happy meal: Transmitter access conflict.\n"); + printk(KERN_ERR "happy meal: Transmitter access conflict.\n"); return 1; } @@ -2742,10 +2781,10 @@ int tickssofar = jiffies - dev->trans_start; if (tickssofar >= 40) { - printk ("%s: transmit timed out, resetting\n", dev->name); + printk (KERN_ERR "%s: transmit timed out, resetting\n", dev->name); hp->net_stats.tx_errors++; tx_dump_log(); - printk ("%s: Happy Status %08x TX[%08x:%08x]\n", dev->name, + printk (KERN_ERR "%s: Happy Status %08x TX[%08x:%08x]\n", dev->name, hme_read32(hp, &hp->gregs->stat), hme_read32(hp, &hp->etxregs->cfg), hme_read32(hp, &hp->bigmacregs->tx_cfg)); @@ -3085,7 +3124,7 @@ SA_SHIRQ, "Quattro", qp); if(err != 0) { - printk("Quattro: Fatal IRQ registery error %d.\n", err); + printk(KERN_ERR "Quattro: Fatal IRQ registery error %d.\n", err); panic("QFE request irq"); } } @@ -3117,13 +3156,13 @@ return -ENOMEM; } if(hme_version_printed++ == 0) - printk(version); + printk(KERN_INFO "%s", version); if(qfe_slot != -1) - printk("%s: Quattro HME slot %d (SBUS) 10/100baseT Ethernet ", + printk(KERN_INFO "%s: Quattro HME slot %d (SBUS) 10/100baseT Ethernet ", dev->name, qfe_slot); else - printk("%s: HAPPY MEAL (SBUS) 10/100baseT Ethernet ", + printk(KERN_INFO "%s: HAPPY MEAL (SBUS) 10/100baseT Ethernet ", dev->name); dev->base_addr = (long) sdev; @@ -3148,9 +3187,9 @@ #endif if(sdev->num_registers != 5) { - printk("happymeal: Device does not have 5 regs, it has %d.\n", + printk(KERN_ERR "happymeal: Device does not have 5 regs, it has %d.\n", sdev->num_registers); - printk("happymeal: Would you like that for here or to go?\n"); + printk(KERN_ERR "happymeal: Would you like that for here or to go?\n"); return ENODEV; } @@ -3168,7 +3207,7 @@ "Happy Meal Global Regs", sdev->reg_addrs[0].which_io, 0); if(!hp->gregs) { - printk("happymeal: Cannot map Happy Meal global registers.\n"); + printk(KERN_ERR "happymeal: Cannot map Happy Meal global registers.\n"); return ENODEV; } @@ -3177,7 +3216,7 @@ "Happy Meal MAC TX Regs", sdev->reg_addrs[1].which_io, 0); if(!hp->etxregs) { - printk("happymeal: Cannot map Happy Meal MAC Transmit registers.\n"); + printk(KERN_ERR "happymeal: Cannot map Happy Meal MAC Transmit registers.\n"); return ENODEV; } @@ -3186,7 +3225,7 @@ "Happy Meal MAC RX Regs", sdev->reg_addrs[2].which_io, 0); if(!hp->erxregs) { - printk("happymeal: Cannot map Happy Meal MAC Receive registers.\n"); + printk(KERN_ERR "happymeal: Cannot map Happy Meal MAC Receive registers.\n"); return ENODEV; } @@ -3195,7 +3234,7 @@ "Happy Meal BIGMAC Regs", sdev->reg_addrs[3].which_io, 0); if(!hp->bigmacregs) { - printk("happymeal: Cannot map Happy Meal BIGMAC registers.\n"); + printk(KERN_ERR "happymeal: Cannot map Happy Meal BIGMAC registers.\n"); return ENODEV; } @@ -3204,7 +3243,7 @@ "Happy Meal Tranceiver Regs", sdev->reg_addrs[4].which_io, 0); if(!hp->tcvregs) { - printk("happymeal: Cannot map Happy Meal Tranceiver registers.\n"); + printk(KERN_ERR "happymeal: Cannot map Happy Meal Tranceiver registers.\n"); return ENODEV; } @@ -3299,7 +3338,7 @@ /* Now make sure pci_dev cookie is there. */ pcp = pdev->sysdata; if(pcp == NULL || pcp->prom_node == -1) { - printk("happymeal(PCI): Some PCI device info missing\n"); + printk(KERN_ERR "happymeal(PCI): Some PCI device info missing\n"); return ENODEV; } node = pcp->prom_node; @@ -3323,7 +3362,7 @@ return -ENOMEM; } if(hme_version_printed++ == 0) - printk(version); + printk(KERN_INFO "%s", version); if (!qfe_slot) { prom_name[0] = 0; @@ -3331,7 +3370,7 @@ int i = simple_strtoul(dev->name + 3, NULL, 10); sprintf(prom_name, "-%d", i + 3); } - printk("%s%s: Quattro HME (PCI/CheerIO) 10/100baseT Ethernet ", dev->name, prom_name); + printk(KERN_INFO "%s%s: Quattro HME (PCI/CheerIO) 10/100baseT Ethernet ", dev->name, prom_name); if (qp->quattro_pci_dev->vendor == PCI_VENDOR_ID_DEC && qp->quattro_pci_dev->device == PCI_DEVICE_ID_DEC_21153) printk("DEC 21153 PCI Bridge\n"); @@ -3340,10 +3379,10 @@ qp->quattro_pci_dev->vendor, qp->quattro_pci_dev->device); } if(qfe_slot != -1) - printk("%s: Quattro HME slot %d (PCI/CheerIO) 10/100baseT Ethernet ", + printk(KERN_INFO "%s: Quattro HME slot %d (PCI/CheerIO) 10/100baseT Ethernet ", dev->name, qfe_slot); else - printk("%s: HAPPY MEAL (PCI/CheerIO) 10/100BaseT Ethernet ", + printk(KERN_INFO "%s: HAPPY MEAL (PCI/CheerIO) 10/100BaseT Ethernet ", dev->name); dev->base_addr = (long) pdev; @@ -3362,7 +3401,7 @@ hpreg_base = pdev->base_address[0]; if((hpreg_base & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) { - printk("happymeal(PCI): Cannot find proper PCI device base address.\n"); + printk(KERN_ERR "happymeal(PCI): Cannot find proper PCI device base address.\n"); return ENODEV; } hpreg_base &= PCI_BASE_ADDRESS_MEM_MASK; @@ -3404,7 +3443,7 @@ hp->happy_block = (struct hmeal_init_block *) get_free_page(GFP_DMA); if(!hp->happy_block) { - printk("happymeal(PCI): Cannot get hme init block.\n"); + printk(KERN_ERR "happymeal(PCI): Cannot get hme init block.\n"); return ENODEV; } @@ -3443,6 +3482,9 @@ * PROM leaves it at zero. */ { +#if 1 + unsigned char latency_timer = 64; +#else unsigned char min_gnt, latency_timer; pci_read_config_byte(pdev, PCI_MIN_GNT, &min_gnt); @@ -3450,6 +3492,7 @@ latency_timer = 64; else latency_timer = ((min_gnt << 3) & 0xff); +#endif pci_write_config_byte(pdev, PCI_LATENCY_TIMER, latency_timer); } #ifdef __sparc_v9__ diff -u --recursive --new-file v2.2.13/linux/drivers/net/sunlance.c linux/drivers/net/sunlance.c --- v2.2.13/linux/drivers/net/sunlance.c Sun Mar 21 07:22:00 1999 +++ linux/drivers/net/sunlance.c Tue Jan 4 10:12:18 2000 @@ -1,4 +1,4 @@ -/* $Id: sunlance.c,v 1.85 1999/03/21 05:22:05 davem Exp $ +/* $Id: sunlance.c,v 1.85.2.1 1999/10/09 06:03:38 davem Exp $ * lance.c: Linux/Sparc/Lance driver * * Written 1995, 1996 by Miguel de Icaza @@ -428,7 +428,7 @@ for (i = 0; (i < 100) && !(ll->rdp & (LE_C0_ERR | LE_C0_IDON)); i++) barrier(); if ((i == 100) || (ll->rdp & LE_C0_ERR)) { - printk ("LANCE unopened after %d ticks, csr0=%4.4x.\n", i, ll->rdp); + printk (KERN_ERR "LANCE unopened after %d ticks, csr0=%4.4x.\n", i, ll->rdp); if (lp->ledma) printk ("dcsr=%8.8x\n", (unsigned int) lp->ledma->regs->cond_reg); @@ -489,7 +489,7 @@ skb = dev_alloc_skb (len+2); if (skb == 0) { - printk ("%s: Memory squeeze, deferring packet.\n", + printk (KERN_INFO "%s: Memory squeeze, deferring packet.\n", dev->name); lp->stats.rx_dropped++; rd->mblength = 0; @@ -547,7 +547,7 @@ lp->stats.tx_carrier_errors++; if (lp->auto_select) { lp->tpe = 1 - lp->tpe; - printk("%s: Carrier Lost, trying %s\n", + printk(KERN_NOTICE "%s: Carrier Lost, trying %s\n", dev->name, lp->tpe?"TPE":"AUI"); /* Stop the lance */ ll->rap = LE_CSR0; @@ -565,7 +565,7 @@ if (status & (LE_T3_BUF|LE_T3_UFL)) { lp->stats.tx_fifo_errors++; - printk ("%s: Tx: ERR_BUF|ERR_UFL, restarting\n", + printk (KERN_ERR "%s: Tx: ERR_BUF|ERR_UFL, restarting\n", dev->name); /* Stop the lance */ ll->rap = LE_CSR0; @@ -606,7 +606,7 @@ int csr0; if (dev->interrupt) - printk ("%s: again", dev->name); + printk (KERN_ERR "%s: again", dev->name); dev->interrupt = 1; @@ -643,7 +643,7 @@ struct sparc_dma_registers *dregs = lp->ledma->regs; unsigned long tst = (unsigned long)dregs->st_addr; - printk ("%s: Memory error, status %04x, addr %06lx\n", + printk (KERN_ERR "%s: Memory error, status %04x, addr %06lx\n", dev->name, csr0, tst & 0xffffff); ll->rdp = LE_C0_STOP; @@ -674,7 +674,7 @@ if (request_irq (dev->irq, &lance_interrupt, SA_SHIRQ, lancestr, (void *) dev)) { - printk ("Lance: Can't get irq %s\n", __irq_itoa(dev->irq)); + printk (KERN_ERR "Lance: Can't get irq %s\n", __irq_itoa(dev->irq)); return -EAGAIN; } @@ -775,7 +775,7 @@ /* On the 4m, reset the dma too */ if (lp->ledma) { - printk ("resetting ledma\n"); + printk (KERN_NOTICE "resetting ledma\n"); lp->ledma->regs->cond_reg |= DMA_RST_ENET; udelay (200); lp->ledma->regs->cond_reg &= ~DMA_RST_ENET; @@ -789,7 +789,7 @@ dev->tbusy = 0; status = init_restart_lance (lp); #ifdef DEBUG_DRIVER - printk ("Lance restart=%d\n", status); + printk (KERN_DEBUG "Lance restart=%d\n", status); #endif return status; } @@ -809,7 +809,7 @@ if (tickssofar < 100) return 1; - printk ("%s: transmit timed out, status %04x, reset\n", + printk (KERN_ERR "%s: transmit timed out, status %04x, reset\n", dev->name, ll->rdp); lp->stats.tx_errors++; lance_reset (dev); @@ -986,9 +986,9 @@ memset(dev->priv, 0, sizeof (struct lance_private) + 8); } if (sparc_lance_debug && version_printed++ == 0) - printk (version); + printk (KERN_INFO "%s", version); - printk ("%s: LANCE ", dev->name); + printk (KERN_INFO "%s: LANCE ", dev->name); /* Fill the dev fields */ dev->base_addr = (long) sdev; @@ -1061,7 +1061,7 @@ if (prop[0] == 0) { int topnd, nd; - printk("%s: using auto-carrier-detection.\n", + printk(KERN_INFO "%s: using auto-carrier-detection.\n", dev->name); /* Is this found at /options .attributes in all @@ -1081,9 +1081,9 @@ sizeof(prop)); if (strcmp(prop, "true")) { - printk("%s: warning: overriding option " + printk(KERN_NOTICE "%s: warning: overriding option " "'tpe-link-test?'\n", dev->name); - printk("%s: warning: mail any problems " + printk(KERN_NOTICE "%s: warning: mail any problems " "to ecd@skynet.be\n", dev->name); set_auxio(AUXIO_LINK_TEST, 0); } @@ -1106,7 +1106,7 @@ /* This should never happen. */ if ((unsigned long)(lp->init_block->brx_ring) & 0x07) { - printk("%s: ERROR: Rx and Tx rings not on even boundary.\n", + printk(KERN_ERR "%s: ERROR: Rx and Tx rings not on even boundary.\n", dev->name); return ENODEV; } diff -u --recursive --new-file v2.2.13/linux/drivers/net/sunqe.c linux/drivers/net/sunqe.c --- v2.2.13/linux/drivers/net/sunqe.c Tue Jan 4 11:10:37 2000 +++ linux/drivers/net/sunqe.c Tue Jan 4 10:12:18 2000 @@ -67,7 +67,7 @@ } if(tries) return 0; - printk("QuadEther: AIEEE cannot reset the QEC!\n"); + printk(KERN_ERR "QuadEther: AIEEE cannot reset the QEC!\n"); return -1; } @@ -91,7 +91,7 @@ break; } if(!tries) { - printk("QuadEther: AIEEE cannot reset the MACE!\n"); + printk(KERN_ERR "QuadEther: AIEEE cannot reset the MACE!\n"); return -1; } @@ -105,7 +105,7 @@ break; } if(!tries) { - printk("QuadEther: Cannot reset QE channel!\n"); + printk(KERN_ERR "QuadEther: Cannot reset QE channel!\n"); return -1; } return 0; @@ -216,7 +216,7 @@ break; } if (tries == 0) - printk("%s: Warning, link state is down.\n", qep->dev->name); + printk(KERN_NOTICE "%s: Warning, link state is down.\n", qep->dev->name); } /* Missed packet counter is cleared on a read. */ @@ -240,41 +240,41 @@ int mace_hwbug_workaround = 0; if(qe_status & CREG_STAT_EDEFER) { - printk("%s: Excessive transmit defers.\n", dev->name); + printk(KERN_ERR "%s: Excessive transmit defers.\n", dev->name); qep->net_stats.tx_errors++; } if(qe_status & CREG_STAT_CLOSS) { - printk("%s: Carrier lost, link down?\n", dev->name); + printk(KERN_ERR "%s: Carrier lost, link down?\n", dev->name); qep->net_stats.tx_errors++; qep->net_stats.tx_carrier_errors++; } if(qe_status & CREG_STAT_ERETRIES) { - printk("%s: Excessive transmit retries (more than 16).\n", dev->name); + printk(KERN_ERR "%s: Excessive transmit retries (more than 16).\n", dev->name); qep->net_stats.tx_errors++; mace_hwbug_workaround = 1; } if(qe_status & CREG_STAT_LCOLL) { - printk("%s: Late transmit collision.\n", dev->name); + printk(KERN_ERR "%s: Late transmit collision.\n", dev->name); qep->net_stats.tx_errors++; qep->net_stats.collisions++; mace_hwbug_workaround = 1; } if(qe_status & CREG_STAT_FUFLOW) { - printk("%s: Transmit fifo underflow, driver bug.\n", dev->name); + printk(KERN_ERR "%s: Transmit fifo underflow, driver bug.\n", dev->name); qep->net_stats.tx_errors++; mace_hwbug_workaround = 1; } if(qe_status & CREG_STAT_JERROR) { - printk("%s: Jabber error.\n", dev->name); + printk(KERN_ERR "%s: Jabber error.\n", dev->name); } if(qe_status & CREG_STAT_BERROR) { - printk("%s: Babble error.\n", dev->name); + printk(KERN_ERR "%s: Babble error.\n", dev->name); } if(qe_status & CREG_STAT_CCOFLOW) { @@ -283,27 +283,27 @@ } if(qe_status & CREG_STAT_TXDERROR) { - printk("%s: Transmit descriptor is bogus, driver bug.\n", dev->name); + printk(KERN_ERR "%s: Transmit descriptor is bogus, driver bug.\n", dev->name); qep->net_stats.tx_errors++; qep->net_stats.tx_aborted_errors++; mace_hwbug_workaround = 1; } if(qe_status & CREG_STAT_TXLERR) { - printk("%s: Transmit late error.\n", dev->name); + printk(KERN_ERR "%s: Transmit late error.\n", dev->name); qep->net_stats.tx_errors++; mace_hwbug_workaround = 1; } if(qe_status & CREG_STAT_TXPERR) { - printk("%s: Transmit DMA parity error.\n", dev->name); + printk(KERN_ERR "%s: Transmit DMA parity error.\n", dev->name); qep->net_stats.tx_errors++; qep->net_stats.tx_aborted_errors++; mace_hwbug_workaround = 1; } if(qe_status & CREG_STAT_TXSERR) { - printk("%s: Transmit DMA sbus error ack.\n", dev->name); + printk(KERN_ERR "%s: Transmit DMA sbus error ack.\n", dev->name); qep->net_stats.tx_errors++; qep->net_stats.tx_aborted_errors++; mace_hwbug_workaround = 1; @@ -325,13 +325,13 @@ } if(qe_status & CREG_STAT_RXFOFLOW) { - printk("%s: Receive fifo overflow.\n", dev->name); + printk(KERN_ERR "%s: Receive fifo overflow.\n", dev->name); qep->net_stats.rx_errors++; qep->net_stats.rx_over_errors++; } if(qe_status & CREG_STAT_RLCOLL) { - printk("%s: Late receive collision.\n", dev->name); + printk(KERN_ERR "%s: Late receive collision.\n", dev->name); qep->net_stats.rx_errors++; qep->net_stats.collisions++; } @@ -347,33 +347,33 @@ } if(qe_status & CREG_STAT_RXDROP) { - printk("%s: Receive packet dropped.\n", dev->name); + printk(KERN_INFO "%s: Receive packet dropped.\n", dev->name); qep->net_stats.rx_errors++; qep->net_stats.rx_dropped++; qep->net_stats.rx_missed_errors++; } if(qe_status & CREG_STAT_RXSMALL) { - printk("%s: Receive buffer too small, driver bug.\n", dev->name); + printk(KERN_ERR "%s: Receive buffer too small, driver bug.\n", dev->name); qep->net_stats.rx_errors++; qep->net_stats.rx_length_errors++; } if(qe_status & CREG_STAT_RXLERR) { - printk("%s: Receive late error.\n", dev->name); + printk(KERN_ERR "%s: Receive late error.\n", dev->name); qep->net_stats.rx_errors++; mace_hwbug_workaround = 1; } if(qe_status & CREG_STAT_RXPERR) { - printk("%s: Receive DMA parity error.\n", dev->name); + printk(KERN_ERR "%s: Receive DMA parity error.\n", dev->name); qep->net_stats.rx_errors++; qep->net_stats.rx_missed_errors++; mace_hwbug_workaround = 1; } if(qe_status & CREG_STAT_RXSERR) { - printk("%s: Receive DMA sbus error ack.\n", dev->name); + printk(KERN_ERR "%s: Receive DMA sbus error ack.\n", dev->name); qep->net_stats.rx_errors++; qep->net_stats.rx_missed_errors++; mace_hwbug_workaround = 1; @@ -437,7 +437,7 @@ } qep->rx_new = elem; if(drops) - printk("%s: Memory squeeze, deferring packet.\n", qep->dev->name); + printk(KERN_INFO "%s: Memory squeeze, deferring packet.\n", qep->dev->name); } /* Interrupts for all QE's get filtered out via the QEC master controller, @@ -538,7 +538,7 @@ long tickssofar = jiffies - dev->trans_start; if (tickssofar >= 40) { - printk("%s: transmit timed out, resetting\n", dev->name); + printk(KERN_NOTICE "%s: transmit timed out, resetting\n", dev->name); qe_init(qep, 1); dev->tbusy = 0; dev->trans_start = jiffies; @@ -711,7 +711,7 @@ qe_devs[0]->dev_addr[j] = idprom->id_ethaddr[j]; if(version_printed++ == 0) - printk(version); + printk(KERN_INFO "%s", version); qe_devs[1] = qe_devs[2] = qe_devs[3] = NULL; for(i = 1; i < 4; i++) { @@ -777,7 +777,7 @@ qranges[k].ot_child_space) break; if(k >= num_qranges) - printk("QuadEther: Aieee, bogus QEC range for " + printk(KERN_ERR "QuadEther: Aieee, bogus QEC range for " "space %08x\n",qesdevs[i]->reg_addrs[j].which_io); qesdevs[i]->reg_addrs[j].which_io = qranges[k].ot_parent_space; qesdevs[i]->reg_addrs[j].phys_addr += qranges[k].ot_parent_base; @@ -796,14 +796,14 @@ "QEC Global Registers", sdev->reg_addrs[0].which_io, 0); if(!qecp->gregs) { - printk("QuadEther: Cannot map QEC global registers.\n"); + printk(KERN_ERR "QuadEther: Cannot map QEC global registers.\n"); res = ENODEV; goto qec_free_devs; } /* Make sure the QEC is in MACE mode. */ if((qecp->gregs->ctrl & 0xf0000000) != GLOB_CTRL_MMODE) { - printk("QuadEther: AIEEE, QEC is not in MACE mode!\n"); + printk(KERN_ERR "QuadEther: AIEEE, QEC is not in MACE mode!\n"); res = ENODEV; goto qec_free_devs; } @@ -841,7 +841,7 @@ "QEC Per-Channel Registers", qesdevs[i]->reg_addrs[0].which_io, 0); if(!qeps[i]->qcregs) { - printk("QuadEther: Cannot map QE %d's channel registers.\n", i); + printk(KERN_ERR "QuadEther: Cannot map QE %d's channel registers.\n", i); res = ENODEV; goto qec_free_devs; } @@ -852,7 +852,7 @@ "QE MACE Registers", qesdevs[i]->reg_addrs[1].which_io, 0); if(!qeps[i]->mregs) { - printk("QuadEther: Cannot map QE %d's MACE registers.\n", i); + printk(KERN_ERR "QuadEther: Cannot map QE %d's MACE registers.\n", i); res = ENODEV; goto qec_free_devs; } @@ -888,14 +888,14 @@ */ if(request_irq(sdev->irqs[0], &qec_interrupt, SA_SHIRQ, "QuadEther", (void *) qecp)) { - printk("QuadEther: Can't register QEC master irq handler.\n"); + printk(KERN_ERR "QuadEther: Can't register QEC master irq handler.\n"); res = EAGAIN; goto qec_free_devs; } /* Report the QE channels. */ for(i = 0; i < 4; i++) { - printk("%s: QuadEthernet channel[%d] ", qe_devs[i]->name, i); + printk(KERN_INFO "%s: QuadEthernet channel[%d] ", qe_devs[i]->name, i); for(j = 0; j < 6; j++) printk ("%2.2x%c", qe_devs[i]->dev_addr[j], diff -u --recursive --new-file v2.2.13/linux/drivers/net/syncppp.c linux/drivers/net/syncppp.c --- v2.2.13/linux/drivers/net/syncppp.c Tue Jan 4 11:10:37 2000 +++ linux/drivers/net/syncppp.c Tue Jan 4 10:12:18 2000 @@ -151,7 +151,9 @@ static void if_down(struct device *dev) { - ; + struct sppp *sp = &((struct ppp_device *)dev)->sppp; + + sp->pp_link_state=SPPP_LINK_DOWN; } /* @@ -192,7 +194,7 @@ skb->dev=dev; skb->mac.raw=skb->data; - if (dev->flags & IFF_UP) + if (dev->flags & IFF_RUNNING) { /* Count received bytes, add FCS and one flag */ sp->ibytes+= skb->len + 3; @@ -368,7 +370,7 @@ /* Keepalive mode disabled or channel down? */ if (! (sp->pp_flags & PP_KEEPALIVE) || - ! (dev->flags & IFF_RUNNING)) + ! (dev->flags & IFF_UP)) continue; /* No keepalive in PPP mode if LCP not opened yet. */ @@ -528,10 +530,10 @@ if (h->ident != sp->lcp.confid) break; sppp_clear_timeout (sp); - if (! (dev->flags & IFF_UP) && - (dev->flags & IFF_RUNNING)) { + if ((sp->pp_link_state != SPPP_LINK_UP) && + (dev->flags & IFF_UP)) { /* Coming out of loopback mode. */ - dev->flags |= IFF_UP; + sp->pp_link_state=SPPP_LINK_UP; printk (KERN_INFO "%s: up\n", dev->name); } switch (sp->lcp.state) { @@ -696,9 +698,9 @@ break; } sp->pp_loopcnt = 0; - if (! (dev->flags & IFF_UP) && - (dev->flags & IFF_RUNNING)) { - dev->flags |= IFF_UP; + if (sp->pp_link_state==SPPP_LINK_DOWN && + (dev->flags & IFF_UP)) { + sp->pp_link_state=SPPP_LINK_UP; printk (KERN_INFO "%s: up\n", dev->name); } break; @@ -823,7 +825,7 @@ int sppp_close (struct device *dev) { struct sppp *sp = &((struct ppp_device *)dev)->sppp; - dev->flags &= ~IFF_RUNNING; + sp->pp_link_state = SPPP_LINK_DOWN; sp->lcp.state = LCP_STATE_CLOSED; sp->ipcp.state = IPCP_STATE_CLOSED; sppp_clear_timeout (sp); @@ -837,9 +839,10 @@ { struct sppp *sp = &((struct ppp_device *)dev)->sppp; sppp_close(dev); - dev->flags |= IFF_RUNNING; - if (!(sp->pp_flags & PP_CISCO)) + if (!(sp->pp_flags & PP_CISCO)) { sppp_lcp_open (sp); + } + sp->pp_link_state = SPPP_LINK_DOWN; return 0; } @@ -849,7 +852,6 @@ { struct sppp *sp = &((struct ppp_device *)dev)->sppp; sppp_close(dev); - dev->flags |= IFF_RUNNING; if (!(sp->pp_flags & PP_CISCO)) { sp->lcp.magic = jiffies; @@ -858,7 +860,8 @@ sp->ipcp.state = IPCP_STATE_CLOSED; /* Give it a moment for the line to settle then go */ sppp_set_timeout (sp, 1); - } + } + sp->pp_link_state=SPPP_LINK_DOWN; return 0; } @@ -1176,7 +1179,7 @@ cli(); sp->pp_flags &= ~PP_TIMO; - if (! (sp->pp_if->flags & IFF_RUNNING) || (sp->pp_flags & PP_CISCO)) { + if (! (sp->pp_if->flags & IFF_UP) || (sp->pp_flags & PP_CISCO)) { restore_flags(flags); return; } diff -u --recursive --new-file v2.2.13/linux/drivers/net/syncppp.h linux/drivers/net/syncppp.h --- v2.2.13/linux/drivers/net/syncppp.h Sat Feb 6 12:46:21 1999 +++ linux/drivers/net/syncppp.h Tue Jan 4 10:12:18 2000 @@ -47,6 +47,7 @@ u32 ipkts,opkts; /* Packets in/out */ struct timer_list pp_timer; struct device *pp_if; + char pp_link_state; }; struct ppp_device @@ -71,6 +72,9 @@ #define IPCP_STATE_ACK_RCVD 1 /* IPCP state: conf-ack received */ #define IPCP_STATE_ACK_SENT 2 /* IPCP state: conf-ack sent */ #define IPCP_STATE_OPENED 3 /* IPCP state: opened */ + +#define SPPP_LINK_DOWN 0 /* link down - no keepalive */ +#define SPPP_LINK_UP 1 /* link is up - keepalive ok */ void sppp_attach (struct ppp_device *pd); void sppp_detach (struct device *dev); diff -u --recursive --new-file v2.2.13/linux/drivers/net/tlan.c linux/drivers/net/tlan.c --- v2.2.13/linux/drivers/net/tlan.c Tue Jan 4 11:10:37 2000 +++ linux/drivers/net/tlan.c Tue Jan 4 10:12:18 2000 @@ -32,6 +32,8 @@ * Alan Cox : Fixed the out of memory * handling. * + * Torben Mathiasen New Maintainer! + * ********************************************************************/ diff -u --recursive --new-file v2.2.13/linux/drivers/net/tlan.h linux/drivers/net/tlan.h --- v2.2.13/linux/drivers/net/tlan.h Sun Aug 9 10:42:41 1998 +++ linux/drivers/net/tlan.h Tue Jan 4 10:12:18 2000 @@ -14,6 +14,8 @@ * ** This file is best viewed/edited with tabstop=4, colums>=132 * + * Dec 10, 1999 Torben Mathiasen + * New Maintainer ********************************************************************/ diff -u --recursive --new-file v2.2.13/linux/drivers/net/tulip.c linux/drivers/net/tulip.c --- v2.2.13/linux/drivers/net/tulip.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/net/tulip.c Tue Jan 4 10:12:18 2000 @@ -1,13 +1,13 @@ /* tulip.c: A DEC 21040-family ethernet driver for Linux. */ /* - Written 1994-1998 by Donald Becker. + Written/copyright 1994-1999 by Donald Becker. This software may be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. - This driver is for the Digital "Tulip" ethernet adapter interface. + This driver is for the Digital "Tulip" Ethernet adapter interface. It should work with most DEC 21*4*-based chips/ethercards, as well as - PNIC and MXIC chips. + with work-alike chips from Lite-On (PNIC) and Macronix (MXIC) and ASIX. The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O Center of Excellence in Space Data and Information Sciences @@ -18,7 +18,7 @@ */ #define SMP_CHECK -static const char version[] = "tulip.c:v0.89H 5/23/98 becker@cesdis.gsfc.nasa.gov\n"; +static const char version[] = "tulip.c:v0.91g-ppc 7/16/99 becker@cesdis.gsfc.nasa.gov\n"; /* A few user-configurable values. */ @@ -60,80 +60,134 @@ static int rx_copybreak = 100; #endif +/* + Set the bus performance register. + Typical: Set 16 longword cache alignment, no burst limit. + Cache alignment bits 15:14 Burst length 13:8 + 0000 No alignment 0x00000000 unlimited 0800 8 longwords + 4000 8 longwords 0100 1 longword 1000 16 longwords + 8000 16 longwords 0200 2 longwords 2000 32 longwords + C000 32 longwords 0400 4 longwords + Warning: many older 486 systems are broken and require setting 0x00A04800 + 8 longword cache alignment, 8 longword burst. + ToDo: Non-Intel setting could be better. +*/ + +#if defined(__alpha__) +static int csr0 = 0x01A00000 | 0xE000; +#elif defined(__i386__) || defined(__powerpc__) || defined(__sparc__) +static int csr0 = 0x01A00000 | 0x8000; +#else +#warning Processor architecture undefined! +static int csr0 = 0x00A00000 | 0x4800; +#endif + /* Operational parameters that usually are not changed. */ /* Time in jiffies before concluding the transmitter is hung. */ #define TX_TIMEOUT (4*HZ) +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ +/* This is a mysterious value that can be written to CSR11 in the 21040 (only) + to support a pre-NWay full-duplex signaling mechanism using short frames. + No one knows what it should be, but if left at its default value some + 10base2(!) packets trigger a full-duplex-request interrupt. */ +#define FULL_DUPLEX_MAGIC 0x6969 + +#if !defined(__OPTIMIZE__) || !defined(__KERNEL__) +#warning You must compile this file with the correct options! +#warning See the last lines of the source file. +#error You must compile this driver with "-O". +#endif +#include +#include +#ifdef MODULE +#ifdef MODVERSIONS +#include +#endif #include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + #include #include #include #include -#include #include #include #include #include #include - -#include /* Processor type for cache alignment. */ -#include -#include -#include - #include #include #include +#include /* Processor type for cache alignment. */ +#include +#include +#include -/* Kernel compatibility defines, common to David Hind's PCMCIA package. +/* Kernel compatibility defines, some common to David Hind's PCMCIA package. This is only in the support-all-kernels source code. */ -#include /* Evil, but neccessary */ -#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10300 -#define RUN_AT(x) (x) /* What to put in timer->expires. */ -#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC) -#define virt_to_bus(addr) ((unsigned long)addr) -#define bus_to_virt(addr) ((void*)addr) +#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115 +MODULE_AUTHOR("Donald Becker "); +MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver"); +MODULE_PARM(debug, "i"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(reverse_probe, "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(csr0, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +#endif -#else /* 1.3.0 and later */ #define RUN_AT(x) (jiffies + (x)) -#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2) -#endif -#if (LINUX_VERSION_CODE >= 0x10344) -#define NEW_MULTICAST -#include -#endif -#ifdef SA_SHIRQ -#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs) -#else -#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs) +#if (LINUX_VERSION_CODE >= 0x20100) +static char kernel_version[] = UTS_RELEASE; #endif -#if (LINUX_VERSION_CODE < 0x20123) +#if LINUX_VERSION_CODE < 0x20123 #define hard_smp_processor_id() smp_processor_id() #define test_and_set_bit(val, addr) set_bit(val, addr) +#define le16_to_cpu(val) (val) +#define le32_to_cpu(val) (val) +#define cpu_to_le32(val) (val) #endif - -/* This my implementation of shared IRQs, now only used for 1.2.13. */ -#ifdef HAVE_SHARED_IRQ -#define USE_SHARED_IRQ -#include +#if LINUX_VERSION_CODE <= 0x20139 +#define net_device_stats enet_statistics +#else +#define NETSTATS_VER2 #endif - -/* The total size is unusually large: The 21040 aligns each of its 16 - longword-wide registers on a quadword boundary. */ -#define TULIP_TOTAL_SIZE 0x80 - -#ifdef HAVE_DEVLIST -struct netdev_entry tulip_drv = -{"Tulip", tulip_pci_probe, TULIP_TOTAL_SIZE, NULL}; +#if LINUX_VERSION_CODE < 0x20155 +/* Grrrr, the PCI code changed, but did not consider CardBus... */ +#include +#define PCI_SUPPORT_VER1 +#else +#define PCI_SUPPORT_VER2 +#endif +#if LINUX_VERSION_CODE < 0x20159 +#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE); +#else +#define dev_free_skb(skb) dev_kfree_skb(skb); +#endif +#if ! defined(CAP_NET_ADMIN) +#define capable(CAP_XXX) (suser()) #endif +#if ! defined(HAS_NETIF_QUEUE) +#define netif_wake_queue(dev) mark_bh(NET_BH); +#endif + +/* Condensed operations for readability. */ +#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr)) +#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr)) +#define tulip_debug debug #ifdef TULIP_DEBUG -int tulip_debug = TULIP_DEBUG; +static int tulip_debug = TULIP_DEBUG; #else -int tulip_debug = 1; +static int tulip_debug = 1; #endif /* @@ -143,17 +197,27 @@ This device driver is designed for the DECchip "Tulip", Digital's single-chip ethernet controllers for PCI. Supported members of the family -are the 21040, 21041, 21140, 21140A, 21142, and 21143. These chips are used on -many PCI boards including the SMC EtherPower series. - +are the 21040, 21041, 21140, 21140A, 21142, and 21143. Similar work-alike +chips from Lite-On, Macronics, ASIX, Compex and other listed below are also +supported. + +These chips are used on at least 140 unique PCI board designs. The great +number of chips and board designs supported is the reason for the +driver size and complexity. Almost of the increasing complexity is in the +board configuration and media selection code. There is very little +increasing in the operational critical path length. II. Board-specific settings PCI bus devices are configured by the system at boot time, so no jumpers need to be set on the board. The system BIOS preferably should assign the PCI INTA signal to an otherwise unused system IRQ line. -Note: Kernel versions earlier than 1.3.73 do not support shared PCI -interrupt lines. + +Some boards have EEPROMs tables with default media entry. The factory default +is usually "autoselect". This should only be overridden when using +transceiver connections without link beat e.g. 10base2 or AUI, or (rarely!) +for forcing full-duplex when used with old link partners that do not do +autonegotiation. III. Driver operation @@ -201,111 +265,159 @@ IV. Notes -Thanks to Duke Kamstra of SMC for providing an EtherPower board. +Thanks to Duke Kamstra of SMC for long ago providing an EtherPower board. +Greg LaPolla at Linksys provided PNIC and other Linksys boards. +Znyx provided a four-port card for testing. IVb. References http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html http://www.digital.com (search for current 21*4* datasheets and "21X4 SROM") -http://www.national.com/pf/DP/DP83840.html +http://www.national.com/pf/DP/DP83840A.html +http://www.asix.com.tw/pmac.htm +http://www.admtek.com.tw/ IVc. Errata -The DEC databook doesn't document which Rx filter settings accept broadcast -packets. Nor does it document how to configure the part to configure the -serial subsystem for normal (vs. loopback) operation or how to have it -autoswitch between internal 10baseT, SIA and AUI transceivers. - +The old DEC databooks were light on details. The 21040 databook claims that CSR13, CSR14, and CSR15 should each be the last -register of the set CSR12-15 written. Hmmm, now how is that possible? */ - - -/* A few values that may be tweaked. */ -#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ +register of the set CSR12-15 written. Hmmm, now how is that possible? -/* This is a mysterious value that can be written to CSR11 in the 21040 (only) - to support a pre-NWay full-duplex signaling mechanism using short frames. - No one knows what it should be, but if left at its default value some - 10base2(!) packets trigger a full-duplex-request interrupt. */ -#define FULL_DUPLEX_MAGIC 0x6969 +The DEC SROM format is very badly designed not precisely defined, leading to +part of the media selection junkheap below. Some boards do not have EEPROM +media tables and need to be patched up. Worse, other boards use the DEC +design kit media table when it isn't correct for their board. -#ifndef PCI_VENDOR_ID_DEC /* Now defined in linux/pci.h */ -#define PCI_VENDOR_ID_DEC 0x1011 -#define PCI_DEVICE_ID_TULIP 0x0002 /* 21040. */ -#define PCI_DEVICE_ID_TULIP_FAST 0x0009 /* 21140. */ -#endif +We cannot use MII interrupts because there is no defined GPIO pin to attach +them. The MII transceiver status is polled using an kernel timer. -#ifndef PCI_DEVICE_ID_DEC_TULIP_PLUS -#define PCI_DEVICE_ID_DEC_TULIP_PLUS 0x0014 /* 21041. */ -#endif -#ifndef PCI_DEVICE_ID_DEC_TULIP_21142 -#define PCI_DEVICE_ID_DEC_TULIP_21142 0x0019 -#endif +*/ -#ifndef PCI_VENDOR_ID_LITEON -#define PCI_VENDOR_ID_LITEON 0x11AD -#endif +static struct device * +tulip_probe1(int pci_bus, int pci_devfn, struct device *dev, long ioaddr, + int irq, int chip_idx, int board_idx); + +/* This table drives the PCI probe routines. It's mostly boilerplate in all + of the drivers, and will likely be provided by some future kernel. + Note the matching code -- the first table entry matchs all 56** cards but + second only the 1234 card. +*/ +enum pci_flags_bit { + PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, + PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3, +}; +#define PCI_ADDR0_IO (PCI_USES_IO|PCI_ADDR0) -#ifndef PCI_VENDOR_ID_MXIC -#define PCI_VENDOR_ID_MXIC 0x10d9 -#define PCI_DEVICE_ID_MX98713 0x0512 -#define PCI_DEVICE_ID_MX98715 0x0531 -#define PCI_DEVICE_ID_MX98725 0x0531 -#endif +struct pci_id_info { + const char *name; + u16 vendor_id, device_id, device_id_mask, flags; + int io_size, min_latency; + struct device *(*probe1)(int pci_bus, int pci_devfn, struct device *dev, + long ioaddr, int irq, int chip_idx, int fnd_cnt); +}; +#ifndef CARDBUS +static struct pci_id_info pci_tbl[] = { + { "Digital DC21040 Tulip", + 0x1011, 0x0002, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 }, + { "Digital DC21041 Tulip", + 0x1011, 0x0014, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 }, + { "Digital DS21140 Tulip", + 0x1011, 0x0009, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 }, + { "Digital DS21143 Tulip", + 0x1011, 0x0019, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 }, + { "Lite-On 82c168 PNIC", + 0x11AD, 0x0002, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 }, + { "Macronix 98713 PMAC", + 0x10d9, 0x0512, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 }, + { "Macronix 98715 PMAC", + 0x10d9, 0x0531, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 }, + { "Macronix 98725 PMAC", + 0x10d9, 0x0531, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 }, + { "ASIX AX88140", + 0x125B, 0x1400, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 }, + { "Lite-On LC82C115 PNIC-II", + 0x11AD, 0xc115, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 }, + { "ADMtek AN981 Comet", + 0x1317, 0x0981, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 }, + { "Compex RL100-TX", + 0x11F6, 0x9881, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 }, + { "Intel 21145 Tulip", + 0x8086, 0x0039, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 }, + { "Xircom Tulip clone", + 0x115d, 0x0003, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 }, + {0}, +}; +#endif /* !CARD_BUS */ -/* The rest of these values should never change. */ +/* This table use during operation for capabilities and media timer. */ static void tulip_timer(unsigned long data); static void t21142_timer(unsigned long data); static void mxic_timer(unsigned long data); static void pnic_timer(unsigned long data); +static void comet_timer(unsigned long data); -/* A table describing the chip types. */ -enum tbl_flag { HAS_MII=1, HAS_MEDIA_TABLE = 2, CSR12_IN_SROM = 4,}; +enum tbl_flag { + HAS_MII=1, HAS_MEDIA_TABLE=2, CSR12_IN_SROM=4, ALWAYS_CHECK_MII=8, + HAS_PWRDWN=0x10, MC_HASH_ONLY=0x20, /* Hash-only multicast filter. */ + HAS_PNICNWAY=0x80, HAS_NWAY143=0x40, /* Uses internal NWay xcvr. */ + HAS_8023X=0x100, +}; static struct tulip_chip_table { - int vendor_id, device_id; char *chip_name; int io_size; int valid_intrs; /* CSR7 interrupt enable settings */ int flags; void (*media_timer)(unsigned long data); } tulip_tbl[] = { - { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP, - "Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer }, - { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_PLUS, - "Digital DC21041 Tulip", 128, 0x0001ebef, HAS_MEDIA_TABLE, tulip_timer }, - { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_FAST, - "Digital DS21140 Tulip", 128, 0x0001ebef, - HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, - tulip_timer }, - { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_21142, - "Digital DS21142/3 Tulip", 128, 0x0801fbff, - HAS_MII | HAS_MEDIA_TABLE, t21142_timer }, - { PCI_VENDOR_ID_LITEON, 0x0002, - "Lite-On 82c168 PNIC", 256, 0x0001ebef, HAS_MII, pnic_timer }, - { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98713, - "Macronix 98713 PMAC", 128, 0x0001ebef, - HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer /* Tulip-like! */ }, - { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98715, - "Macronix 98715 PMAC", 256, 0x0001ebef, HAS_MEDIA_TABLE, mxic_timer }, - { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98725, - "Macronix 98725 PMAC", 256, 0x0001ebef, HAS_MEDIA_TABLE, mxic_timer }, - { 0x125B, 0x1400, "ASIX AX88140", 128, 0x0001fbff, + { "Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer }, + { "Digital DC21041 Tulip", 128, 0x0001ebff, HAS_MEDIA_TABLE, tulip_timer }, + { "Digital DS21140 Tulip", 128, 0x0001ebef, HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer }, - {0, 0, 0, 0}, + { "Digital DS21143 Tulip", 128, 0x0801fbff, + HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_PWRDWN | HAS_NWAY143, + t21142_timer }, + { "Lite-On 82c168 PNIC", 256, 0x0001ebef, + HAS_MII | HAS_PNICNWAY, pnic_timer }, + { "Macronix 98713 PMAC", 128, 0x0001ebef, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer }, + { "Macronix 98715 PMAC", 256, 0x0001ebef, + HAS_MEDIA_TABLE, mxic_timer }, + { "Macronix 98725 PMAC", 256, 0x0001ebef, + HAS_MEDIA_TABLE, mxic_timer }, + { "ASIX AX88140", 128, 0x0001fbff, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | MC_HASH_ONLY, tulip_timer }, + { "Lite-On PNIC-II", 256, 0x0801fbff, + HAS_MII | HAS_NWAY143 | HAS_8023X, t21142_timer }, + { "ADMtek Comet", 256, 0x0001abef, + MC_HASH_ONLY, comet_timer }, + { "Compex 9881 PMAC", 128, 0x0001ebef, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer }, + { "Intel DS21145 Tulip", 128, 0x0801fbff, + HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_PWRDWN | HAS_NWAY143, + t21142_timer }, + { "Xircom tulip work-alike", 128, 0x0801fbff, + HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_PWRDWN | HAS_NWAY143, + t21142_timer }, + {0}, +}; +/* This matches the table above. Note 21142 == 21143. */ +enum chips { + DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3, + LC82C168, MX98713, MX98715, MX98725, AX88140, PNIC2, COMET, COMPEX9881, + I21145, }; -/* This matches the table above. */ -enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3, - LC82C168, MX98713, MX98715, MX98725}; /* A full-duplex map for media types. */ -enum MediaIs {MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8, - MediaIs100=16}; +enum MediaIs { + MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8, + MediaIs100=16}; static const char media_cap[] = {0,0,0,16, 3,19,16,24, 27,4,7,5, 0,20,23,20 }; +static u8 t21040_csr13[] = {2,0x0C,8,4, 4,0,0,0, 0,0,0,0, 4,0,0,0}; /* 21041 transceiver register settings: 10-T, 10-2, AUI, 10-T, 10T-FD*/ -static u16 t21041_csr13[] = { 0xEF05, 0xEF09, 0xEF09, 0xEF01, 0xEF09, }; -static u16 t21041_csr14[] = { 0x7F3F, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, }; +static u16 t21041_csr13[] = { 0xEF01, 0xEF09, 0xEF09, 0xEF01, 0xEF09, }; +static u16 t21041_csr14[] = { 0xFFFF, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, }; static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, }; static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0000, 0x0001, }; @@ -321,16 +433,12 @@ /* The bits in the CSR5 status registers, mostly interrupt sources. */ enum status_bits { - TimerInt=0x800, TPLnkFail=0x1000, TPLnkPass=0x10, + TimerInt=0x800, SytemError=0x2000, TPLnkFail=0x1000, TPLnkPass=0x10, NormalIntr=0x10000, AbnormalIntr=0x8000, RxJabber=0x200, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40, TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01, }; -enum desc_status_bits { - DescOwned=0x80000000, RxDescFatalErr=0x8000, RxWholePkt=0x0300, -}; - /* The Tulip Rx and Tx buffer descriptors. */ struct tulip_rx_desc { s32 status; @@ -344,6 +452,22 @@ u32 buffer1, buffer2; /* We use only buffer 1. */ }; +enum desc_status_bits { + DescOwned=0x80000000, RxDescFatalErr=0x8000, RxWholePkt=0x0300, +}; + +/* Ring-wrap flag in length field, use for last ring entry. + 0x01000000 means chain on buffer2 address, + 0x02000000 means use the ring start address in CSR2/3. + Note: Some work-alike chips do not function correctly in chained mode. + The ASIX chip works only in chained mode. + Thus we indicates ring mode, but always write the 'next' field for + chained mode as well. +*/ +#define DESC_RING_WRAP 0x02000000 + +#define EEPROM_SIZE 128 /* 2 << EEPROM_ADDRLEN */ + struct medialeaf { u8 type; u8 media; @@ -353,7 +477,8 @@ struct mediatable { u16 defaultmedia; u8 leafcount, csr12dir; /* General purpose pin directions. */ - unsigned has_mii:1, has_nonmii:1; + unsigned has_mii:1, has_nonmii:1, has_reset:6; + u32 csr15dir, csr15val; /* 21143 NWay setting. */ struct medialeaf mleaf[0]; }; @@ -375,19 +500,13 @@ /* The addresses of receive-in-place skbuffs. */ struct sk_buff* rx_skbuff[RX_RING_SIZE]; char *rx_buffs; /* Address of temporary Rx buffers. */ - u32 setup_frame[48]; /* Pseudo-Tx frame to init address table. */ + u16 setup_frame[96]; /* Pseudo-Tx frame to init address table. */ int chip_id; int revision; -#if LINUX_VERSION_CODE > 0x20139 + int flags; struct net_device_stats stats; -#else - struct enet_statistics stats; -#endif struct timer_list timer; /* Media selection timer. */ int interrupt; /* In-interrupt flag. */ -#ifdef SMP_CHECK - int smp_proc_id; /* Which processor in IRQ handler. */ -#endif unsigned int cur_rx, cur_tx; /* The next free ring entry */ unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ unsigned int tx_full:1; /* The Tx queue is full. */ @@ -398,134 +517,134 @@ unsigned int media2:4; /* Secondary monitored media port. */ unsigned int medialock:1; /* Don't sense media type. */ unsigned int mediasense:1; /* Media sensing in progress. */ + unsigned int nway:1, nwayset:1; /* 21143 internal NWay. */ + unsigned int csr0; /* CSR0 setting. */ unsigned int csr6; /* Current CSR6 control settings. */ - unsigned char eeprom[128]; /* Serial EEPROM contents. */ + unsigned char eeprom[EEPROM_SIZE]; /* Serial EEPROM contents. */ + void (*link_change)(struct device *dev, int csr5); u16 to_advertise; /* NWay capabilities advertised. */ + u16 lpar; /* 21143 Link partner ability. */ u16 advertising[4]; signed char phys[4], mii_cnt; /* MII device addresses. */ struct mediatable *mtable; int cur_index; /* Current media index. */ - unsigned char pci_bus, pci_dev_fn; + int saved_if_port; + unsigned char pci_bus, pci_devfn; + int ttimer; + int susp_rx; + unsigned long nir; int pad0, pad1; /* Used for 8-byte alignment */ }; -static struct device *tulip_probe1(int pci_bus, int pci_devfn, - struct device *dev, - int chip_id, int options); static void parse_eeprom(struct device *dev); -static int read_eeprom(long ioaddr, int location); +static int read_eeprom(long ioaddr, int location, int addr_len); static int mdio_read(struct device *dev, int phy_id, int location); static void mdio_write(struct device *dev, int phy_id, int location, int value); static void select_media(struct device *dev, int startup); static int tulip_open(struct device *dev); -static void tulip_timer(unsigned long data); +/* Chip-specific media selection (timer functions prototyped above). */ +static void t21142_lnk_change(struct device *dev, int csr5); +static void t21142_start_nway(struct device *dev); +static void pnic_lnk_change(struct device *dev, int csr5); +static void pnic_do_nway(struct device *dev); + static void tulip_tx_timeout(struct device *dev); static void tulip_init_ring(struct device *dev); static int tulip_start_xmit(struct sk_buff *skb, struct device *dev); +static int tulip_refill_rx(struct device *dev); static int tulip_rx(struct device *dev); -static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs); +static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs); static int tulip_close(struct device *dev); -static struct enet_statistics *tulip_get_stats(struct device *dev); +static struct net_device_stats *tulip_get_stats(struct device *dev); #ifdef HAVE_PRIVATE_IOCTL static int private_ioctl(struct device *dev, struct ifreq *rq, int cmd); #endif -#ifdef NEW_MULTICAST static void set_rx_mode(struct device *dev); -#else -static void set_rx_mode(struct device *dev, int num_addrs, void *addrs); -#endif -/* A list of all installed Tulip devices, for removing the driver module. */ +/* A list of all installed Tulip devices. */ static struct device *root_tulip_dev = NULL; -/* This 21040 probe no longer uses a large fixed contiguous Rx buffer region, - but now receives directly into full-sized skbuffs that are allocated - at open() time. - This allows the probe routine to use the old driver initialization - interface. */ - +#ifndef CARDBUS int tulip_probe(struct device *dev) { int cards_found = 0; - static int pci_index = 0; /* Static, for multiple probe calls. */ + int pci_index = 0; unsigned char pci_bus, pci_device_fn; - /* Ideally we would detect all network cards in slot order. That would - be best done a central PCI probe dispatch, which wouldn't work - well with the current structure. So instead we detect just the - Tulip cards in slot order. */ - -#if LINUX_VERSION_CODE >= 0x20155 - if (! pci_present()) - return -ENODEV; -#else - if (! pcibios_present()) + if ( ! pcibios_present()) return -ENODEV; -#endif + for (;pci_index < 0xff; pci_index++) { u16 vendor, device, pci_command, new_command; - unsigned long pci_ioaddr = 0; - int chip_idx = 0; + int chip_idx; + int irq; + long ioaddr; if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, reverse_probe ? 0xfe - pci_index : pci_index, - &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL) + &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL) { if (reverse_probe) continue; else break; + } pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, &vendor); pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, &device); - for (chip_idx = 0; tulip_tbl[chip_idx].chip_name; chip_idx++) - if (vendor == tulip_tbl[chip_idx].vendor_id && - device == tulip_tbl[chip_idx].device_id) + for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++) + if (vendor == pci_tbl[chip_idx].vendor_id + && (device & pci_tbl[chip_idx].device_id_mask) == + pci_tbl[chip_idx].device_id) break; - if (tulip_tbl[chip_idx].chip_name == 0) { - if (vendor == PCI_VENDOR_ID_DEC || - vendor == PCI_VENDOR_ID_LITEON) - printk(KERN_INFO "Unknown Tulip-style PCI ethernet chip type" - " %4.4x %4.4x"" detected: not configured.\n", - vendor, device); + if (pci_tbl[chip_idx].vendor_id == 0) continue; - } -#if LINUX_VERSION_CODE >= 0x20155 - pci_ioaddr = pci_find_slot(pci_bus, pci_device_fn)->base_address[0]; -#else - pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, - &pci_ioaddr); + + { +#if defined(PCI_SUPPORT_VER2) + struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn); + ioaddr = pdev->base_address[0] & ~3; + irq = pdev->irq; +#else + u32 pci_ioaddr; + u8 pci_irq_line; + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_ioaddr); + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq_line); + ioaddr = pci_ioaddr & ~3; + irq = pci_irq_line; #endif - /* Remove I/O space marker in bit 0. */ - pci_ioaddr &= ~3; + } - if (tulip_debug > 2) - printk(KERN_DEBUG "Found %s at I/O %#lx.\n", - tulip_tbl[chip_idx].chip_name, pci_ioaddr); + if (debug > 2) + printk(KERN_INFO "Found %s at PCI I/O address %#lx.\n", + pci_tbl[chip_idx].name, ioaddr); - if (check_region(pci_ioaddr, tulip_tbl[chip_idx].io_size)) + if (check_region(ioaddr, pci_tbl[chip_idx].io_size)) continue; pcibios_read_config_word(pci_bus, pci_device_fn, PCI_COMMAND, &pci_command); new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO; if (pci_command != new_command) { - printk(KERN_INFO " The PCI BIOS has not enabled this" - " device! Updating PCI command %4.4x->%4.4x.\n", - pci_command, new_command); + printk(KERN_INFO " The PCI BIOS has not enabled the" + " device at %d/%d! Updating PCI command %4.4x->%4.4x.\n", + pci_bus, pci_device_fn, pci_command, new_command); pcibios_write_config_word(pci_bus, pci_device_fn, PCI_COMMAND, new_command); } - dev = tulip_probe1(pci_bus, pci_device_fn, dev, chip_idx, cards_found); + dev = pci_tbl[chip_idx].probe1(pci_bus, pci_device_fn, dev, ioaddr, + irq, chip_idx, cards_found); /* Get and check the bus-master and latency values. */ if (dev) { - unsigned char pci_latency; + u8 pci_latency; pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, &pci_latency); if (pci_latency < 10) { @@ -534,75 +653,62 @@ pci_latency); pcibios_write_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, 64); - } else if (tulip_debug > 1) - printk(KERN_INFO " PCI latency timer (CFLT) is %#x, " - " PCI command is %4.4x.\n", - pci_latency, new_command); - /* Bring the 21143 out power-down mode. */ - if (device == PCI_DEVICE_ID_DEC_TULIP_21142) - pcibios_write_config_dword(pci_bus, pci_device_fn, - 0x40, 0x40000000); - dev = 0; - cards_found++; + } } + dev = 0; + cards_found++; } return cards_found ? 0 : -ENODEV; } +#endif /* not CARDBUS */ -static struct device *tulip_probe1(int pci_bus, int pci_device_fn, - struct device *dev, - int chip_id, int board_idx) +static struct device *tulip_probe1(int pci_bus, int pci_devfn, + struct device *dev, long ioaddr, int irq, + int chip_idx, int board_idx) { static int did_version = 0; /* Already printed version info. */ struct tulip_private *tp; - long ioaddr; - int irq; /* See note below on the multiport cards. */ static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'}; static int last_irq = 0; static int multiport_cnt = 0; /* For four-port boards w/one EEPROM */ + u8 chip_rev; int i; unsigned short sum; + u8 ee_data[EEPROM_SIZE]; if (tulip_debug > 0 && did_version++ == 0) printk(KERN_INFO "%s", version); dev = init_etherdev(dev, 0); -#if LINUX_VERSION_CODE >= 0x20155 - irq = pci_find_slot(pci_bus, pci_device_fn)->irq; - ioaddr = pci_find_slot(pci_bus, pci_device_fn)->base_address[0]; -#else - { - u8 pci_irq_line; - u32 pci_ioaddr; - pcibios_read_config_byte(pci_bus, pci_device_fn, - PCI_INTERRUPT_LINE, &pci_irq_line); - pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, - &pci_ioaddr); - irq = pci_irq_line; - ioaddr = pci_ioaddr; - } -#endif - /* Remove I/O space marker in bit 0. */ - ioaddr &= ~3; + /* Make certain the data structures are quadword aligned. */ + tp = (void *)(((long)kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA) + 7) & ~7); + memset(tp, 0, sizeof(*tp)); + dev->priv = tp; + + tp->next_module = root_tulip_dev; + root_tulip_dev = dev; + + pcibios_read_config_byte(pci_bus, pci_devfn, PCI_REVISION_ID, &chip_rev); + + /* Bring the 21041/21143 out of sleep mode. + Caution: Snooze mode does not work with some boards! */ + if (tulip_tbl[chip_idx].flags & HAS_PWRDWN) + pcibios_write_config_dword(pci_bus, pci_devfn, 0x40, 0x00000000); - printk(KERN_INFO "%s: %s at %#3lx,", - dev->name, tulip_tbl[chip_id].chip_name, ioaddr); + printk(KERN_INFO "%s: %s rev %d at %#3lx,", + dev->name, tulip_tbl[chip_idx].chip_name, chip_rev, ioaddr); /* Stop the chip's Tx and Rx processes. */ outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6); /* Clear the missed-packet counter. */ (volatile int)inl(ioaddr + CSR8); - if (chip_id == DC21041) { - if (inl(ioaddr + CSR9) & 0x8000) { - printk(" 21040 compatible mode,"); - chip_id = DC21040; - } else { - printk(" 21041 mode,"); - } + if (chip_idx == DC21041 && inl(ioaddr + CSR9) & 0x8000) { + printk(" 21040 compatible mode,"); + chip_idx = DC21040; } /* The station address ROM is read byte serially. The register must @@ -610,7 +716,7 @@ EEPROM. */ sum = 0; - if (chip_id == DC21040) { + if (chip_idx == DC21040) { outl(0, ioaddr + CSR9); /* Reset the pointer with a dummy write. */ for (i = 0; i < 6; i++) { int value, boguscnt = 100000; @@ -620,28 +726,34 @@ dev->dev_addr[i] = value; sum += value & 0xff; } - } else if (chip_id == LC82C168) { + } else if (chip_idx == LC82C168) { for (i = 0; i < 3; i++) { int value, boguscnt = 100000; outl(0x600 | i, ioaddr + 0x98); do value = inl(ioaddr + CSR9); while (value < 0 && --boguscnt > 0); - ((u16*)dev->dev_addr)[i] = value; + put_unaligned(le16_to_cpu(value), ((u16*)dev->dev_addr) + i); sum += value & 0xffff; } - } else { /* Must be a new chip, with a serial EEPROM interface. */ - /* We read the whole EEPROM, and sort it out later. DEC has a - specification _Digital Semiconductor 21X4 Serial ROM Format_ - but early vendor boards just put the address in the first six - EEPROM locations. */ - unsigned char ee_data[128]; + } else if (chip_idx == COMET) { + /* No need to read the EEPROM. */ + put_unaligned(inl(ioaddr + 0xA4), (u32 *)dev->dev_addr); + put_unaligned(inl(ioaddr + 0xA8), (u16 *)(dev->dev_addr + 4)); + for (i = 0; i < 6; i ++) + sum += dev->dev_addr[i]; + } else { + /* A serial EEPROM interface, we read now and sort it out later. */ int sa_offset = 0; + int ee_addr_size = read_eeprom(ioaddr, 0xff, 8) & 0x40000 ? 8 : 6; for (i = 0; i < sizeof(ee_data)/2; i++) - ((u16 *)ee_data)[i] = read_eeprom(ioaddr, i); + ((u16 *)ee_data)[i] = + le16_to_cpu(read_eeprom(ioaddr, i, ee_addr_size)); - /* Detect the simple EEPROM format by the duplicated station addr. */ + /* DEC now has a specification (see Notes) but early board makers + just put the address in the first EEPROM locations. */ + /* This does memcmp(eedata, eedata+16, 8) */ for (i = 0; i < 8; i ++) if (ee_data[i] != ee_data[16+i]) sa_offset = 20; @@ -655,7 +767,8 @@ } } /* Lite-On boards have the address byte-swapped. */ - if (dev->dev_addr[0] == 0xA0 && dev->dev_addr[1] == 0x00) + if ((dev->dev_addr[0] == 0xA0 || dev->dev_addr[0] == 0xC0) + && dev->dev_addr[1] == 0x00) for (i = 0; i < 6; i+=2) { char tmp = dev->dev_addr[i]; dev->dev_addr[i] = dev->dev_addr[i+1]; @@ -671,34 +784,37 @@ for (i = 0; i < 5; i++) dev->dev_addr[i] = last_phys_addr[i]; dev->dev_addr[i] = last_phys_addr[i] + 1; -#if defined(__i386__) /* This BIOS bug doesn't exist on Alphas. */ - irq = last_irq; +#if defined(__i386__) /* Patch up x86 BIOS bug. */ + if (last_irq) + irq = last_irq; #endif } for (i = 0; i < 6; i++) - printk(" %2.2x", last_phys_addr[i] = dev->dev_addr[i]); + printk("%c%2.2X", i ? ':' : ' ', last_phys_addr[i] = dev->dev_addr[i]); printk(", IRQ %d.\n", irq); last_irq = irq; /* We do a request_region() only to register /proc/ioports info. */ - /* Note that proper size is tulip_tbl[chip_id].chip_name, but... */ - request_region(ioaddr, TULIP_TOTAL_SIZE, dev->name); + /* Note that proper size is tulip_tbl[chip_idx].chip_name, but... */ + request_region(ioaddr, tulip_tbl[chip_idx].io_size, dev->name); dev->base_addr = ioaddr; dev->irq = irq; - /* Make certain the data structures are quadword aligned. */ - tp = (void *)(((long)kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA) + 7) & ~7); - memset(tp, 0, sizeof(*tp)); - dev->priv = tp; - - tp->next_module = root_tulip_dev; - root_tulip_dev = dev; - tp->pci_bus = pci_bus; - tp->pci_dev_fn = pci_device_fn; - tp->chip_id = chip_id; + tp->pci_devfn = pci_devfn; + tp->chip_id = chip_idx; + tp->revision = chip_rev; + tp->flags = tulip_tbl[chip_idx].flags; + tp->csr0 = csr0; + + /* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles. + And the ASIX must have a burst limit or horrible things happen. */ + if (chip_idx == DC21143 && chip_rev == 65) + tp->csr0 &= ~0x01000000; + else if (chip_idx == AX88140) + tp->csr0 |= 0x2000; #ifdef TULIP_FULL_DUPLEX tp->full_duplex = 1; @@ -729,37 +845,58 @@ if (tp->full_duplex) tp->full_duplex_lock = 1; - /* This is logically part of probe1(), but too complex to write inline. */ - if (tulip_tbl[chip_id].flags & HAS_MEDIA_TABLE) - parse_eeprom(dev); - if (media_cap[tp->default_port] & MediaIsMII) { u16 media2advert[] = { 0x20, 0x40, 0x03e0, 0x60, 0x80, 0x100, 0x200 }; tp->to_advertise = media2advert[tp->default_port - 9]; - } else - tp->to_advertise = 0x03e1; + } else if (tp->flags & HAS_8023X) + tp->to_advertise = 0x05e1; + else + tp->to_advertise = 0x01e1; + + /* This is logically part of probe1(), but too complex to write inline. */ + if (tp->flags & HAS_MEDIA_TABLE) { + memcpy(tp->eeprom, ee_data, sizeof(tp->eeprom)); + parse_eeprom(dev); + } - if ((tp->mtable && tp->mtable->has_mii) || - ( ! tp->mtable && (tulip_tbl[tp->chip_id].flags & HAS_MII))) { + if ((tp->flags & ALWAYS_CHECK_MII) || + (tp->mtable && tp->mtable->has_mii) || + ( ! tp->mtable && (tp->flags & HAS_MII))) { int phy, phy_idx; + if (tp->mtable && tp->mtable->has_mii) { + for (i = 0; i < tp->mtable->leafcount; i++) + if (tp->mtable->mleaf[i].media == 11) { + tp->cur_index = i; + tp->saved_if_port = dev->if_port; + select_media(dev, 1); + dev->if_port = tp->saved_if_port; + break; + } + } /* Find the connected MII xcvrs. Doing this in open() would allow detecting external xcvrs later, but takes much time. */ for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) { int mii_status = mdio_read(dev, phy, 1); - if (mii_status != 0xffff && mii_status != 0x0000) { + if ((mii_status & 0x8301) == 0x8001 || + ((mii_status & 0x8000) == 0 && (mii_status & 0x7800) != 0)) { int mii_reg0 = mdio_read(dev, phy, 0); + int mii_advert = mdio_read(dev, phy, 4); int reg4 = ((mii_status>>6) & tp->to_advertise) | 1; tp->phys[phy_idx] = phy; tp->advertising[phy_idx++] = reg4; - printk(KERN_INFO "%s: MII transceiver found at MDIO address " - "%d, config %4.4x status %4.4x.\n", - dev->name, phy, mii_reg0, mii_status); - if (1 || (media_cap[tp->default_port] & MediaIsMII)) { + printk(KERN_INFO "%s: MII transceiver #%d " + "config %4.4x status %4.4x advertising %4.4x.\n", + dev->name, phy, mii_reg0, mii_status, mii_advert); + /* Fixup for DLink with miswired PHY. */ + if (mii_advert != reg4) { printk(KERN_DEBUG "%s: Advertising %4.4x on PHY %d," " previously advertising %4.4x.\n", - dev->name, reg4, phy, mdio_read(dev, phy, 4)); + dev->name, reg4, phy, mii_advert); + printk(KERN_DEBUG "%s: Advertising %4.4x (to advertise" + " is %4.4x).\n", + dev->name, reg4, tp->to_advertise); mdio_write(dev, phy, 4, reg4); } /* Enable autonegotiation: some boards default to off. */ @@ -788,9 +925,15 @@ dev->set_multicast_list = &set_rx_mode; #endif + if ((tp->flags & HAS_NWAY143) || tp->chip_id == DC21041) + tp->link_change = t21142_lnk_change; + else if (tp->flags & HAS_PNICNWAY) + tp->link_change = pnic_lnk_change; + /* Reset the xcvr interface and turn on heartbeat. */ - switch (chip_id) { + switch (chip_idx) { case DC21041: + tp->to_advertise = 0x0061; outl(0x00000000, ioaddr + CSR13); outl(0xFFFFFFFF, ioaddr + CSR14); outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */ @@ -806,28 +949,43 @@ outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12); break; case DC21142: - outl(0x82420200, ioaddr + CSR6); - outl(0x0001, ioaddr + CSR13); - outl(0x0003FFFF, ioaddr + CSR14); - outl(0x0008, ioaddr + CSR15); - outl(0x0001, ioaddr + CSR13); - outl(0x1301, ioaddr + CSR12); /* Start NWay. */ + case PNIC2: + if (tp->mii_cnt || media_cap[dev->if_port] & MediaIsMII) { + outl(0x82020000, ioaddr + CSR6); + outl(0x0000, ioaddr + CSR13); + outl(0x0000, ioaddr + CSR14); + outl(0x820E0000, ioaddr + CSR6); + } else + t21142_start_nway(dev); break; case LC82C168: if ( ! tp->mii_cnt) { + tp->nway = 1; + tp->nwayset = 0; outl(0x00420000, ioaddr + CSR6); outl(0x30, ioaddr + CSR12); outl(0x0001F078, ioaddr + 0xB8); outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */ } break; - case MX98713: case MX98715: case MX98725: + case MX98713: case COMPEX9881: outl(0x00000000, ioaddr + CSR6); outl(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */ outl(0x00000001, ioaddr + CSR13); break; + case MX98715: case MX98725: + outl(0x01a80000, ioaddr + CSR6); + outl(0xFFFFFFFF, ioaddr + CSR14); + outl(0x00001000, ioaddr + CSR12); + break; + case COMET: + /* No initialization necessary. */ + break; } + if (tulip_tbl[chip_idx].flags & HAS_PWRDWN) + pcibios_write_config_dword(pci_bus, pci_devfn, 0x40, 0x40000000); + return dev; } @@ -846,29 +1004,37 @@ } eeprom_fixups[] = { {"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c, 0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }}, - {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x021f, + {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x041f, 0x0000, 0x009E, /* 10baseT */ - 0x0903, 0x006D, /* 100baseTx */ }}, - {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x033f, + 0x0004, 0x009E, /* 10baseT-FD */ + 0x0903, 0x006D, /* 100baseTx */ + 0x0905, 0x006D, /* 100baseTx-FD */ }}, + {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x063f, 0x0107, 0x8021, /* 100baseFx */ 0x0108, 0x8021, /* 100baseFx-FD */ - 0x0103, 0x006D, /* 100baseTx */ }}, - {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0313, + 0x0100, 0x009E, /* 10baseT */ + 0x0104, 0x009E, /* 10baseT-FD */ + 0x0103, 0x006D, /* 100baseTx */ + 0x0105, 0x006D, /* 100baseTx-FD */ }}, + {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0513, 0x1001, 0x009E, /* 10base2, CSR12 0x10*/ 0x0000, 0x009E, /* 10baseT */ - 0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ }}, - {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x031F, - 0x1B01, 0x0000, /* 10base2, CSR12 0x1B */ - 0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */ - 0x0B00, 0x009E, /* 10baseT, CSR12 0x0B */ + 0x0004, 0x009E, /* 10baseT-FD */ + 0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ + 0x0305, 0x006D, /* 100baseTx-FD CSR12 0x03 */}}, + {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x051F, + 0x1B01, 0x0000, /* 10base2, CSR12 0x1B */ + 0x0B00, 0x009E, /* 10baseT, CSR12 0x0B */ + 0x0B04, 0x009E, /* 10baseT-FD,CSR12 0x0B */ + 0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */ + 0x1B05, 0x006D, /* 100baseTx-FD CSR12 0x1B */ }}, {0, 0, 0, 0, {}}}; static const char * block_name[] = {"21140 non-MII", "21140 MII PHY", "21142 Serial PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"}; -#define EEPROM_SIZE 128 -#if defined(__i386__) +#if defined(__i386__) /* AKA get_unaligned() */ #define get_u16(ptr) (*(u16 *)(ptr)) #else #define get_u16(ptr) (((u8*)(ptr))[0] + (((u8*)(ptr))[1]<<8)) @@ -881,14 +1047,10 @@ static unsigned char *last_ee_data = NULL; static int controller_index = 0; struct tulip_private *tp = (struct tulip_private *)dev->priv; - long ioaddr = dev->base_addr; unsigned char *ee_data = tp->eeprom; int i; tp->mtable = 0; - for (i = 0; i < EEPROM_SIZE/2; i++) - ((u16 *)ee_data)[i] = read_eeprom(ioaddr, i); - /* Detect an old-style (SA only) EEPROM layout: memcmp(eedata, eedata+16, 8). */ for (i = 0; i < 8; i ++) @@ -925,20 +1087,13 @@ } } if (eeprom_fixups[i].name == NULL) { /* No fixup found. */ - printk(KERN_INFO "%s: Old style EEPROM -- no media selection information.\n", + printk(KERN_INFO "%s: Old style EEPROM with no media selection " + "information.\n", dev->name); return; } } - if (tulip_debug > 1) { - printk(KERN_DEBUG "read_eeprom:"); - for (i = 0; i < 64; i++) { - printk("%s%4.4x", (i & 7) == 0 ? "\n" KERN_DEBUG : " ", - read_eeprom(ioaddr, i)); - } - printk("\n"); - } - + controller_index = 0; if (ee_data[19] > 1) { /* Multiport board. */ last_ee_data = ee_data; @@ -948,42 +1103,29 @@ if (ee_data[27] == 0) { /* No valid media table. */ } else if (tp->chip_id == DC21041) { unsigned char *p = (void *)ee_data + ee_data[27 + controller_index*3]; - short media; - int count; - - media = get_u16(p); - p += 2; - count = *p++; + int media = get_u16(p); + int count = p[2]; + p += 3; - printk(KERN_INFO "%s:21041 Media information at %d, default media " - "%4.4x (%s).\n", dev->name, ee_data[27], media, + printk(KERN_INFO "%s: 21041 Media table, default media %4.4x (%s).\n", + dev->name, media, media & 0x0800 ? "Autosense" : medianame[media & 15]); for (i = 0; i < count; i++) { unsigned char media_code = *p++; - u16 csrvals[3]; - int idx; - for (idx = 0; idx < 3; idx++) { - csrvals[idx] = get_u16(p); - p += 2; - } - if (media_code & 0x40) { - printk(KERN_INFO "%s: 21041 media %2.2x (%s)," - " csr13 %4.4x csr14 %4.4x csr15 %4.4x.\n", - dev->name, media_code & 15, medianame[media_code & 15], - csrvals[0], csrvals[1], csrvals[2]); - } else - printk(KERN_INFO "%s: 21041 media #%d, %s.\n", - dev->name, media_code & 15, medianame[media_code & 15]); + if (media_code & 0x40) + p += 6; + printk(KERN_INFO "%s: 21041 media #%d, %s.\n", + dev->name, media_code & 15, medianame[media_code & 15]); } } else { unsigned char *p = (void *)ee_data + ee_data[27]; unsigned char csr12dir = 0; - int count; + int count, new_advertise = 0; struct mediatable *mtable; u16 media = get_u16(p); p += 2; - if (tulip_tbl[tp->chip_id].flags & CSR12_IN_SROM) + if (tp->flags & CSR12_IN_SROM) csr12dir = *p++; count = *p++; mtable = (struct mediatable *) @@ -995,13 +1137,14 @@ mtable->defaultmedia = media; mtable->leafcount = count; mtable->csr12dir = csr12dir; - mtable->has_nonmii = mtable->has_mii = 0; + mtable->has_nonmii = mtable->has_mii = mtable->has_reset = 0; + mtable->csr15dir = mtable->csr15val = 0; printk(KERN_INFO "%s: EEPROM default media type %s.\n", dev->name, media & 0x0800 ? "Autosense" : medianame[media & 15]); for (i = 0; i < count; i++) { struct medialeaf *leaf = &mtable->mleaf[i]; - + if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */ leaf->type = 0; leaf->media = p[0] & 0x3f; @@ -1011,12 +1154,34 @@ p += 4; } else { leaf->type = p[1]; - if (p[1] & 1) { + if (p[1] == 0x05) { + mtable->has_reset = i; + leaf->media = p[2] & 0x0f; + } else if (p[1] & 1) { mtable->has_mii = 1; leaf->media = 11; } else { mtable->has_nonmii = 1; leaf->media = p[2] & 0x0f; + switch (leaf->media) { + case 0: new_advertise |= 0x0020; break; + case 4: new_advertise |= 0x0040; break; + case 3: new_advertise |= 0x0080; break; + case 5: new_advertise |= 0x0100; break; + case 6: new_advertise |= 0x0200; break; + } + if (p[1] == 2 && leaf->media == 0) { + if (p[2] & 0x40) { + u32 base15 = get_unaligned((u16*)&p[7]); + mtable->csr15dir = + (get_unaligned((u16*)&p[9])<<16) + base15; + mtable->csr15val = + (get_unaligned((u16*)&p[11])<<16) + base15; + } else { + mtable->csr15dir = get_unaligned((u16*)&p[3])<<16; + mtable->csr15val = get_unaligned((u16*)&p[5])<<16; + } + } } leaf->leafdata = p + 2; p += (p[0] & 0x3f) + 1; @@ -1025,7 +1190,7 @@ unsigned char *bp = leaf->leafdata; printk(KERN_INFO "%s: MII interface PHY %d, setup/reset " "sequences %d/%d long, capabilities %2.2x %2.2x.\n", - dev->name, bp[0], bp[1], bp[1 + bp[1]*2], + dev->name, bp[0], bp[1], bp[2 + bp[1]*2], bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]); } printk(KERN_INFO "%s: Index #%d - Media %s (#%d) described " @@ -1033,6 +1198,8 @@ dev->name, i, medianame[leaf->media], leaf->media, block_name[leaf->type], leaf->type); } + if (new_advertise) + tp->to_advertise = new_advertise; } } /* Reading a serial EEPROM is a "bit" grungy, but we work our way through:->.*/ @@ -1040,54 +1207,48 @@ /* EEPROM_Ctrl bits. */ #define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */ #define EE_CS 0x01 /* EEPROM chip select. */ -#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */ +#define EE_DATA_WRITE 0x04 /* Data from the Tulip to EEPROM. */ #define EE_WRITE_0 0x01 #define EE_WRITE_1 0x05 -#define EE_DATA_READ 0x08 /* EEPROM chip data out. */ +#define EE_DATA_READ 0x08 /* Data from the EEPROM chip. */ #define EE_ENB (0x4800 | EE_CS) /* Delay between EEPROM clock transitions. - The 1.2 code is a "nasty" timing loop, but PC compatible machines are - *supposed* to delay an ISA-compatible period for the SLOW_DOWN_IO macro. */ -#ifdef _LINUX_DELAY_H -#define eeprom_delay(nanosec) udelay((nanosec + 999)/1000) -#else -#define eeprom_delay(nanosec) do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0) -#endif + Even at 33Mhz current PCI implementations don't overrun the EEPROM clock. + We add a bus turn-around to insure that this remains true. */ +#define eeprom_delay() inl(ee_addr) /* The EEPROM commands include the alway-set leading bit. */ -#define EE_WRITE_CMD (5 << 6) -#define EE_READ_CMD (6 << 6) -#define EE_ERASE_CMD (7 << 6) +#define EE_READ_CMD (6) -static int read_eeprom(long ioaddr, int location) +/* Note: this routine returns extra data bits for size detection. */ +static int read_eeprom(long ioaddr, int location, int addr_len) { int i; - unsigned short retval = 0; + unsigned retval = 0; long ee_addr = ioaddr + CSR9; - int read_cmd = location | EE_READ_CMD; - + int read_cmd = location | (EE_READ_CMD << addr_len); + outl(EE_ENB & ~EE_CS, ee_addr); outl(EE_ENB, ee_addr); - + /* Shift the read command bits out. */ - for (i = 10; i >= 0; i--) { + for (i = 4 + addr_len; i >= 0; i--) { short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; outl(EE_ENB | dataval, ee_addr); - eeprom_delay(100); + eeprom_delay(); outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); - eeprom_delay(150); - outl(EE_ENB | dataval, ee_addr); /* Finish EEPROM a clock tick. */ - eeprom_delay(250); + eeprom_delay(); + retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0); } outl(EE_ENB, ee_addr); - + for (i = 16; i > 0; i--) { outl(EE_ENB | EE_SHIFT_CLK, ee_addr); - eeprom_delay(100); + eeprom_delay(); retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0); outl(EE_ENB, ee_addr); - eeprom_delay(100); + eeprom_delay(); } /* Terminate the EEPROM access. */ @@ -1121,19 +1282,33 @@ int i; int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; int retval = 0; - long mdio_addr = dev->base_addr + CSR9; + long ioaddr = dev->base_addr; + long mdio_addr = ioaddr + CSR9; if (tp->chip_id == LC82C168) { - long ioaddr = dev->base_addr; int i = 1000; outl(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0); + inl(ioaddr + 0xA0); + inl(ioaddr + 0xA0); while (--i > 0) if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000)) return retval & 0xffff; return 0xffff; } - /* Establish sync by sending at least 32 logic ones. */ + if (tp->chip_id == COMET) { + if (phy_id == 1) { + if (location < 7) + return inl(ioaddr + 0xB4 + (location<<2)); + else if (location == 17) + return inl(ioaddr + 0xD0); + else if (location >= 29 && location <= 31) + return inl(ioaddr + 0xD4 + ((location-29)<<2)); + } + return 0xffff; + } + + /* Establish sync by sending at least 32 logic ones. */ for (i = 32; i >= 0; i--) { outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); mdio_delay(); @@ -1165,10 +1340,10 @@ struct tulip_private *tp = (struct tulip_private *)dev->priv; int i; int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; - long mdio_addr = dev->base_addr + CSR9; + long ioaddr = dev->base_addr; + long mdio_addr = ioaddr + CSR9; if (tp->chip_id == LC82C168) { - long ioaddr = dev->base_addr; int i = 1000; outl(cmd, ioaddr + 0xA0); do @@ -1178,7 +1353,19 @@ return; } - /* Establish sync by sending 32 logic ones. */ + if (tp->chip_id == COMET) { + if (phy_id != 1) + return; + if (location < 7) + outl(value, ioaddr + 0xB4 + (location<<2)); + else if (location == 17) + outl(value, ioaddr + 0xD0); + else if (location >= 29 && location <= 31) + outl(value, ioaddr + 0xD4 + ((location-29)<<2)); + return; + } + + /* Establish sync by sending 32 logic ones. */ for (i = 32; i >= 0; i--) { outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); mdio_delay(); @@ -1209,7 +1396,12 @@ { struct tulip_private *tp = (struct tulip_private *)dev->priv; long ioaddr = dev->base_addr; - int i = 0; + int next_tick = 3*HZ; + int i; + + /* Wake the chip from sleep/snooze mode. */ + if (tp->flags & HAS_PWRDWN) + pcibios_write_config_dword(tp->pci_bus, tp->pci_devfn, 0x40, 0); /* On some chip revs we must set the MII/SYM port before the reset!? */ if (tp->mii_cnt || (tp->mtable && tp->mtable->has_mii)) @@ -1217,86 +1409,61 @@ /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */ outl(0x00000001, ioaddr + CSR0); -#ifdef _LINUX_DELAY_H - udelay(2); -#else - SLOW_DOWN_IO; -#endif + + if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev)) + return -EAGAIN; + MOD_INC_USE_COUNT; + /* Deassert reset. - 486: Set 8 longword cache alignment, 8 longword burst. - 586: Set 16 longword cache alignment, no burst limit. - Cache alignment bits 15:14 Burst length 13:8 - 0000 No alignment 0x00000000 unlimited 0800 8 longwords - 4000 8 longwords 0100 1 longword 1000 16 longwords - 8000 16 longwords 0200 2 longwords 2000 32 longwords - C000 32 longwords 0400 4 longwords Wait the specified 50 PCI cycles after a reset by initializing Tx and Rx queues and the address filter list. */ -#if defined(__alpha__) - /* ToDo: Alpha setting could be better. */ - outl(0x01A00000 | 0xE000, ioaddr + CSR0); -#elif defined(__powerpc__) - outl(0x01A00080 | 0x8000, ioaddr + CSR0); -#elif defined(__i386__) -#if defined(MODULE) - /* When a module we don't have 'x86' to check. */ - outl(0x01A00000 | 0x4800, ioaddr + CSR0); -#else -#if (LINUX_VERSION_CODE > 0x2014c) -#define x86 boot_cpu_data.x86 -#endif - outl(0x01A00000 | (x86 <= 4 ? 0x4800 : 0x8000), ioaddr + CSR0); - if (x86 <= 4) - printk(KERN_INFO "%s: This is a 386/486 PCI system, setting cache " - "alignment to %x.\n", dev->name, - 0x01A00000 | (x86 <= 4 ? 0x4800 : 0x8000)); -#endif -#else - outl(0x01A00000 | 0x4800, ioaddr + CSR0); -#warning Processor architecture undefined! -#endif - -#ifdef SA_SHIRQ - if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev)) { - return -EAGAIN; - } -#else - if (irq2dev_map[dev->irq] != NULL - || (irq2dev_map[dev->irq] = dev) == NULL - || dev->irq == 0 - || request_irq(dev->irq, &tulip_interrupt, 0, - tulip_tbl[tp->chip_id].chip_name)) { - return -EAGAIN; - } -#endif + outl(tp->csr0, ioaddr + CSR0); if (tulip_debug > 1) printk(KERN_DEBUG "%s: tulip_open() irq %d.\n", dev->name, dev->irq); - MOD_INC_USE_COUNT; - tulip_init_ring(dev); - /* This is set_rx_mode(), but without starting the transmitter. */ - /* Fill the whole address filter table with our physical address. */ - { +#if 0 + if (tp->chip_id == PNIC2) { + u32 addr_low = cpu_to_le32(get_unaligned((u32 *)dev->dev_addr)); + u32 addr_high = cpu_to_le16(get_unaligned((u16 *)(dev->dev_addr+4))); + addr_high = (dev->dev_addr[4]<<8) + (dev->dev_addr[5]<<0); + outl((dev->dev_addr[0]<<8) + dev->dev_addr[1] + + (dev->dev_addr[2]<<24) + (dev->dev_addr[3]<<16), + ioaddr + 0xB0); + outl(addr_high + (addr_high<<16), ioaddr + 0xB8); + } +#endif + if (tp->flags & MC_HASH_ONLY) { + u32 addr_low = cpu_to_le32(get_unaligned((u32 *)dev->dev_addr)); + u32 addr_high = cpu_to_le32(get_unaligned((u16 *)(dev->dev_addr+4))); + if (tp->chip_id == AX88140) { + outl(0, ioaddr + CSR13); + outl(addr_low, ioaddr + CSR14); + outl(1, ioaddr + CSR13); + outl(addr_high, ioaddr + CSR14); + } else if (tp->chip_id == COMET) { + outl(addr_low, ioaddr + 0xA4); + outl(addr_high, ioaddr + 0xA8); + outl(0, ioaddr + 0xAC); + outl(0, ioaddr + 0xB0); + } + } else { + /* This is set_rx_mode(), but without starting the transmitter. */ u16 *eaddrs = (u16 *)dev->dev_addr; - u32 *setup_frm = tp->setup_frame, i; + u16 *setup_frm = &tp->setup_frame[15*6]; - /* You must add the broadcast address when doing perfect filtering! */ - *setup_frm++ = 0xffff; - *setup_frm++ = 0xffff; - *setup_frm++ = 0xffff; - /* Fill the rest of the accept table with our physical address. */ - for (i = 1; i < 16; i++) { - *setup_frm++ = eaddrs[0]; - *setup_frm++ = eaddrs[1]; - *setup_frm++ = eaddrs[2]; - } + /* 21140 bug: you must add the broadcast address. */ + memset(tp->setup_frame, 0xff, sizeof(tp->setup_frame)); + /* Fill the final entry of the table with our physical address. */ + *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; /* Put the setup frame on the Tx list. */ - tp->tx_ring[0].length = 0x08000000 | 192; - tp->tx_ring[0].buffer1 = virt_to_bus(tp->setup_frame); - tp->tx_ring[0].status = 0x80000000; + tp->tx_ring[0].length = cpu_to_le32(0x08000000 | 192); + tp->tx_ring[0].buffer1 = virt_to_le32desc(tp->setup_frame); + tp->tx_ring[0].status = cpu_to_le32(DescOwned); tp->cur_tx++; } @@ -1304,13 +1471,12 @@ outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3); outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4); + tp->saved_if_port = dev->if_port; if (dev->if_port == 0) dev->if_port = tp->default_port; - if (tp->chip_id == DC21041 && dev->if_port > 4) - /* Invalid: Select initial TP, autosense, autonegotiate. */ - dev->if_port = 4; /* Allow selecting a default media. */ + i = 0; if (tp->mtable == NULL) goto media_picked; if (dev->if_port) { @@ -1323,31 +1489,80 @@ goto media_picked; } } - if ((tp->mtable->defaultmedia & 0x0800) == 0) + if ((tp->mtable->defaultmedia & 0x0800) == 0) { + int looking_for = tp->mtable->defaultmedia & 15; for (i = 0; i < tp->mtable->leafcount; i++) - if (tp->mtable->mleaf[i].media == (tp->mtable->defaultmedia & 15)) { - printk(KERN_INFO "%s: Using EEPROM-set media %s.\n", - dev->name, medianame[tp->mtable->mleaf[i].media]); - goto media_picked; - } + if (tp->mtable->mleaf[i].media == looking_for) { + printk(KERN_INFO "%s: Using EEPROM-set media %s.\n", + dev->name, medianame[looking_for]); + goto media_picked; + } + } /* Start sensing first non-full-duplex media. */ for (i = tp->mtable->leafcount - 1; (media_cap[tp->mtable->mleaf[i].media] & MediaAlwaysFD) && i > 0; i--) - ; + ; media_picked: tp->csr6 = 0; tp->cur_index = i; + tp->nwayset = 0; + if (dev->if_port == 0 && tp->chip_id == DC21041) { + tp->nway = 1; + } if (dev->if_port == 0 && tp->chip_id == DC21142) { - tp->csr6 = 0x82420200; - outl(0x0003FFFF, ioaddr + CSR14); + if (tp->mii_cnt) { + select_media(dev, 1); + if (tulip_debug > 1) + printk(KERN_INFO "%s: Using MII transceiver %d, status " + "%4.4x.\n", + dev->name, tp->phys[0], mdio_read(dev, tp->phys[0], 1)); + outl(0x82020000, ioaddr + CSR6); + tp->csr6 = 0x820E0000; + dev->if_port = 11; + outl(0x0000, ioaddr + CSR13); + outl(0x0000, ioaddr + CSR14); + } else + t21142_start_nway(dev); + } else if (tp->chip_id == PNIC2) { + t21142_start_nway(dev); + } else if (tp->chip_id == LC82C168 && ! tp->medialock) { + if (tp->mii_cnt) { + dev->if_port = 11; + tp->csr6 = 0x814C0000 | (tp->full_duplex ? 0x0200 : 0); + outl(0x0001, ioaddr + CSR15); + } else if (inl(ioaddr + CSR5) & TPLnkPass) + pnic_do_nway(dev); + else { + /* Start with 10mbps to do autonegotiation. */ + outl(0x32, ioaddr + CSR12); + tp->csr6 = 0x00420000; + outl(0x0001B078, ioaddr + 0xB8); + outl(0x0201B078, ioaddr + 0xB8); + next_tick = 1*HZ; + } + } else if ((tp->chip_id == MX98713 || tp->chip_id == COMPEX9881) + && ! tp->medialock) { + dev->if_port = 0; + tp->csr6 = 0x01880000 | (tp->full_duplex ? 0x0200 : 0); + outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80); + } else if (tp->chip_id == MX98715 || tp->chip_id == MX98725) { + /* Provided by BOLO, Macronix - 12/10/1998. */ + dev->if_port = 0; + tp->csr6 = 0x01a80200; + outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80); + outl(0x11000 | inw(ioaddr + 0xa0), ioaddr + 0xa0); + } else if (tp->chip_id == DC21143 && + media_cap[dev->if_port] & MediaIsMII) { + /* We must reset the media CSRs when we force-select MII mode. */ + outl(0x0000, ioaddr + CSR13); + outl(0x0000, ioaddr + CSR14); outl(0x0008, ioaddr + CSR15); - outl(0x0001, ioaddr + CSR13); - outl(0x1301, ioaddr + CSR12); - } else if (tp->chip_id == LC82C168 && tp->mii_cnt && ! tp->medialock) { - dev->if_port = 11; - tp->csr6 = 0x816C0000 | (tp->full_duplex ? 0x0200 : 0); - outl(0x0001, ioaddr + CSR15); + } else if (tp->chip_id == COMET) { + dev->if_port = 0; + tp->csr6 = 0x00040000; + } else if (tp->chip_id == AX88140) { + tp->csr6 = tp->mii_cnt ? 0x00040100 : 0x00000100; } else select_media(dev, 1); @@ -1373,7 +1588,7 @@ /* Set the timer to switch to check for link beat and perhaps switch to an alternate media type. */ init_timer(&tp->timer); - tp->timer.expires = RUN_AT(5*HZ); + tp->timer.expires = RUN_AT(next_tick); tp->timer.data = (unsigned long)dev; tp->timer.function = tulip_tbl[tp->chip_id].media_timer; add_timer(&tp->timer); @@ -1388,7 +1603,7 @@ struct tulip_private *tp = (struct tulip_private *)dev->priv; struct mediatable *mtable = tp->mtable; u32 new_csr6; - int check_mii =0, i; + int i; if (mtable) { struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index]; @@ -1406,33 +1621,56 @@ new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18); break; case 2: case 4: { - u16 setup[3]; - for (i = 0; i < 3; i++) + u16 setup[5]; + u32 csr13val, csr14val, csr15dir, csr15val; + for (i = 0; i < 5; i++) setup[i] = get_u16(&p[i*2 + 1]); dev->if_port = p[0] & 15; + if (media_cap[dev->if_port] & MediaAlwaysFD) + tp->full_duplex = 1; + + if (startup && mtable->has_reset) { + struct medialeaf *rleaf = &mtable->mleaf[mtable->has_reset]; + unsigned char *rst = rleaf->leafdata; + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Resetting the transceiver.\n", + dev->name); + for (i = 0; i < rst[0]; i++) + outl(get_u16(rst + 1 + (i<<1)) << 16, ioaddr + CSR15); + } if (tulip_debug > 1) - printk(KERN_DEBUG "%s: 21142 non-MII %s transceiver control %4.4x/%4.4x.\n", + printk(KERN_DEBUG "%s: 21143 non-MII %s transceiver control " + "%4.4x/%4.4x.\n", dev->name, medianame[dev->if_port], setup[0], setup[1]); if (p[0] & 0x40) { /* SIA (CSR13-15) setup values are provided. */ + csr13val = setup[0]; + csr14val = setup[1]; + csr15dir = (setup[3]<<16) | setup[2]; + csr15val = (setup[4]<<16) | setup[2]; outl(0, ioaddr + CSR13); - outl(setup[1], ioaddr + CSR14); - outl(setup[2], ioaddr + CSR15); - outl(setup[0], ioaddr + CSR13); - for (i = 0; i < 3; i++) /* Re-fill setup[] */ - setup[i] = get_u16(&p[i*2 + 7]); - } else if (dev->if_port <= 4) { - outl(0, ioaddr + CSR13); - outl(t21142_csr14[dev->if_port], ioaddr + CSR14); - outl(t21142_csr15[dev->if_port], ioaddr + CSR15); - outl(t21142_csr13[dev->if_port], ioaddr + CSR13); + outl(csr14val, ioaddr + CSR14); + outl(csr15dir, ioaddr + CSR15); /* Direction */ + outl(csr15val, ioaddr + CSR15); /* Data */ + outl(csr13val, ioaddr + CSR13); } else { - outl(0, ioaddr + CSR14); - outl(8, ioaddr + CSR15); - outl(0, ioaddr + CSR13); + csr13val = 1; + csr14val = 0x0003FF7F; + csr15dir = (setup[0]<<16) | 0x0008; + csr15val = (setup[1]<<16) | 0x0008; + if (dev->if_port <= 4) + csr14val = t21142_csr14[dev->if_port]; + if (startup) { + outl(0, ioaddr + CSR13); + outl(csr14val, ioaddr + CSR14); + } + outl(csr15dir, ioaddr + CSR15); /* Direction */ + outl(csr15val, ioaddr + CSR15); /* Data */ + if (startup) outl(csr13val, ioaddr + CSR13); } - outl(setup[0]<<16, ioaddr + CSR15); /* Direction */ - outl(setup[1]<<16, ioaddr + CSR15); /* Data */ + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Setting CSR15 to %8.8x/%8.8x.\n", + dev->name, csr15dir, csr15val); if (mleaf->type == 4) new_csr6 = 0x82020000 | ((setup[2] & 0x71) << 18); else @@ -1446,7 +1684,6 @@ u16 to_advertise; dev->if_port = 11; - check_mii = 1; new_csr6 = 0x020E0000; if (mleaf->type == 3) { /* 21142 */ u16 *init_sequence = (u16*)(p+2); @@ -1473,7 +1710,7 @@ } to_advertise = (get_u16(&misc_info[1]) & tp->to_advertise) | 1; tp->advertising[phy_num] = to_advertise; - if (tulip_debug > 1 || 1) + if (tulip_debug > 1) printk(KERN_DEBUG "%s: Advertising %4.4x on PHY %d (%d).\n", dev->name, to_advertise, phy_num, tp->phys[phy_num]); /* Bogus: put in by a committee? */ @@ -1481,32 +1718,33 @@ break; } default: - new_csr6 = 0x020E0000; + printk(KERN_DEBUG "%s: Invalid media table selection %d.\n", + dev->name, mleaf->type); + new_csr6 = 0x020E0000; } if (tulip_debug > 1) printk(KERN_DEBUG "%s: Using media type %s, CSR12 is %2.2x.\n", dev->name, medianame[dev->if_port], inl(ioaddr + CSR12) & 0xff); } else if (tp->chip_id == DC21041) { + int port = dev->if_port <= 4 ? dev->if_port : 0; if (tulip_debug > 1) printk(KERN_DEBUG "%s: 21041 using media %s, CSR12 is %4.4x.\n", - dev->name, medianame[dev->if_port & 15], - inl(ioaddr + CSR12) & 0xffff); + dev->name, medianame[port == 3 ? 12: port], + inl(ioaddr + CSR12)); outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */ - outl(t21041_csr14[dev->if_port], ioaddr + CSR14); - outl(t21041_csr15[dev->if_port], ioaddr + CSR15); - outl(t21041_csr13[dev->if_port], ioaddr + CSR13); + outl(t21041_csr14[port], ioaddr + CSR14); + outl(t21041_csr15[port], ioaddr + CSR15); + outl(t21041_csr13[port], ioaddr + CSR13); new_csr6 = 0x80020000; } else if (tp->chip_id == LC82C168) { if (startup && ! tp->medialock) dev->if_port = tp->mii_cnt ? 11 : 0; if (tulip_debug > 1) - printk(KERN_DEBUG "%s: PNIC PHY status is %3.3x, CSR12 %4.4x," - " media %s.\n", - dev->name, inl(ioaddr + 0xB8), inl(ioaddr + CSR12), - medianame[dev->if_port]); + printk(KERN_DEBUG "%s: PNIC PHY status is %3.3x, media %s.\n", + dev->name, inl(ioaddr + 0xB8), medianame[dev->if_port]); if (tp->mii_cnt) { - new_csr6 = 0x812C0000; + new_csr6 = 0x810C0000; outl(0x0001, ioaddr + CSR15); outl(0x0201B07A, ioaddr + 0xB8); } else if (startup) { @@ -1518,10 +1756,8 @@ } else if (dev->if_port == 3 || dev->if_port == 5) { outl(0x33, ioaddr + CSR12); new_csr6 = 0x01860000; - if (startup) - outl(0x0201F868, ioaddr + 0xB8); /* Trigger autonegotiation. */ - else - outl(0x1F868, ioaddr + 0xB8); + /* Trigger autonegotiation. */ + outl(startup ? 0x0201F868 : 0x0001F868, ioaddr + 0xB8); } else { outl(0x32, ioaddr + CSR12); new_csr6 = 0x00420000; @@ -1532,18 +1768,24 @@ int csr12 = inl(ioaddr + CSR12); if (tulip_debug > 1) printk(KERN_DEBUG "%s: 21040 media type is %s, CSR12 is %2.2x.\n", - dev->name, dev->if_port ? "AUI" : "10baseT", csr12); - new_csr6 = (dev->if_port ? 0x01860000 : 0x00420000); + dev->name, medianame[dev->if_port], csr12); + if (media_cap[dev->if_port] & MediaAlwaysFD) + tp->full_duplex = 1; + new_csr6 = 0x20000; /* Set the full duplux match frame. */ outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11); outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */ - outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13); + if (t21040_csr13[dev->if_port] & 8) { + outl(0x0705, ioaddr + CSR14); + outl(0x0006, ioaddr + CSR15); + } else { + outl(0xffff, ioaddr + CSR14); + outl(0x0000, ioaddr + CSR15); + } + outl(0x8f01 | t21040_csr13[dev->if_port], ioaddr + CSR13); } else { /* Unknown chip type with no media table. */ if (tp->default_port == 0) - if (tp->mii_cnt) { - dev->if_port = 11; - } else - dev->if_port = 3; + dev->if_port = tp->mii_cnt ? 11 : 3; if (media_cap[dev->if_port] & MediaIsMII) { new_csr6 = 0x020E0000; } else if (media_cap[dev->if_port] & MediaIsFx) { @@ -1561,28 +1803,80 @@ return; } -static void tulip_timer(unsigned long data) +/* + Check the MII negotiated duplex, and change the CSR6 setting if + required. + Return 0 if everything is OK. + Return < 0 if the transceiver is missing or has no link beat. + */ +static int check_duplex(struct device *dev) +{ + long ioaddr = dev->base_addr; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int mii_reg1, mii_reg5, negotiated, duplex; + + if (tp->full_duplex_lock) + return 0; + mii_reg1 = mdio_read(dev, tp->phys[0], 1); + mii_reg5 = mdio_read(dev, tp->phys[0], 5); + if (tulip_debug > 1) + printk(KERN_INFO "%s: MII status %4.4x, Link partner report " + "%4.4x.\n", dev->name, mii_reg1, mii_reg5); + if (mii_reg1 == 0xffff) + return -2; + if ((mii_reg1 & 0x0004) == 0) { + int new_reg1 = mdio_read(dev, tp->phys[0], 1); + if ((new_reg1 & 0x0004) == 0) { + if (tulip_debug > 1) + printk(KERN_INFO "%s: No link beat on the MII interface," + " status %4.4x.\n", dev->name, new_reg1); + return -1; + } + } + negotiated = mii_reg5 & tp->advertising[0]; + duplex = ((negotiated & 0x0300) == 0x0100 + || (negotiated & 0x00C0) == 0x0040); + /* 100baseTx-FD or 10T-FD, but not 100-HD */ + if (tp->full_duplex != duplex) { + tp->full_duplex = duplex; + if (negotiated & 0x038) /* 100mbps. */ + tp->csr6 &= ~0x00400000; + if (tp->full_duplex) tp->csr6 |= 0x0200; + else tp->csr6 &= ~0x0200; + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + if (tulip_debug > 0) + printk(KERN_INFO "%s: Setting %s-duplex based on MII" + "#%d link partner capability of %4.4x.\n", + dev->name, tp->full_duplex ? "full" : "half", + tp->phys[0], mii_reg5); + return 1; + } + return 0; +} + +static void tulip_timer(unsigned long data) { struct device *dev = (struct device *)data; struct tulip_private *tp = (struct tulip_private *)dev->priv; long ioaddr = dev->base_addr; u32 csr12 = inl(ioaddr + CSR12); - int next_tick = 0; + int next_tick = 2*HZ; - if (tulip_debug > 3) { - printk(KERN_DEBUG "%s: Media selection tick, status %8.8x mode %8.8x " - "SIA %8.8x %8.8x %8.8x %8.8x.\n", - dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR6), - csr12, inl(ioaddr + CSR13), + if (tulip_debug > 2) { + printk(KERN_DEBUG "%s: Media selection tick, %s, status %8.8x mode" + " %8.8x SIA %8.8x %8.8x %8.8x %8.8x.\n", + dev->name, medianame[dev->if_port], inl(ioaddr + CSR5), + inl(ioaddr + CSR6), csr12, inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15)); } switch (tp->chip_id) { case DC21040: - if (csr12 & 0x0002) { /* Network error */ - printk(KERN_INFO "%s: No 10baseT link beat found, switching to %s media.\n", - dev->name, dev->if_port ? "10baseT" : "AUI"); - dev->if_port ^= 1; - outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13); + if (!tp->medialock && csr12 & 0x0002) { /* Network error */ + printk(KERN_INFO "%s: No link beat found.\n", + dev->name); + dev->if_port = (dev->if_port == 2 ? 0 : 2); + select_media(dev, 0); dev->trans_start = jiffies; } break; @@ -1590,6 +1884,7 @@ if (tulip_debug > 2) printk(KERN_DEBUG "%s: 21041 media tick CSR12 %8.8x.\n", dev->name, csr12); + if (tp->medialock) break; switch (dev->if_port) { case 0: case 3: case 4: if (csr12 & 0x0004) { /*LnkFail */ @@ -1611,25 +1906,26 @@ break; case 1: /* 10base2 */ case 2: /* AUI */ - if (csr12 & 0x0100) { - next_tick = (30*HZ); /* 30 sec. */ - tp->mediasense = 0; - } else if ((csr12 & 0x0004) == 0) { - printk(KERN_INFO "%s: 21041 media switched to 10baseT.\n", dev->name); - dev->if_port = 0; - select_media(dev, 0); - next_tick = (24*HZ)/10; /* 2.4 sec. */ - } else if (tp->mediasense || (csr12 & 0x0002)) { - dev->if_port = 3 - dev->if_port; /* Swap ports. */ - select_media(dev, 0); - next_tick = 20*HZ; - } else { - next_tick = 20*HZ; - } - break; + if (csr12 & 0x0100) { + next_tick = (30*HZ); /* 30 sec. */ + tp->mediasense = 0; + } else if ((csr12 & 0x0004) == 0) { + printk(KERN_INFO "%s: 21041 media switched to 10baseT.\n", + dev->name); + dev->if_port = 0; + select_media(dev, 0); + next_tick = (24*HZ)/10; /* 2.4 sec. */ + } else if (tp->mediasense || (csr12 & 0x0002)) { + dev->if_port = 3 - dev->if_port; /* Swap ports. */ + select_media(dev, 0); + next_tick = 20*HZ; + } else { + next_tick = 20*HZ; + } + break; } break; - case DC21140: case DC21142: case MX98713: default: { + case DC21140: case DC21142: case MX98713: case COMPEX9881: default: { struct medialeaf *mleaf; unsigned char *p; if (tp->mtable == NULL) { /* No EEPROM info, use generic code. */ @@ -1698,53 +1994,11 @@ next_tick = (24*HZ)/10; break; } - case 1: case 3: { /* 21140, 21142 MII */ - int mii_reg1, mii_reg5; + case 1: case 3: /* 21140, 21142 MII */ actually_mii: - mii_reg1 = mdio_read(dev, tp->phys[0], 1); - mii_reg5 = mdio_read(dev, tp->phys[0], 5); - if (tulip_debug > 1) - printk(KERN_INFO "%s: MII status %4.4x, Link partner report " - "%4.4x, CSR12 %2.2x, %cD.\n", - dev->name, mii_reg1, mii_reg5, csr12, - tp->full_duplex ? 'F' : 'H'); - if (mii_reg1 != 0xffff && (mii_reg1 & 0x0004) == 0) { - int new_reg1 = mdio_read(dev, tp->phys[0], 1); - if ((new_reg1 & 0x0004) == 0) { - printk(KERN_INFO "%s: No link beat on the MII interface," - " status then %4.4x now %4.4x.\n", - dev->name, mii_reg1, new_reg1); - if (tp->mtable && tp->mtable->has_nonmii) - goto select_next_media; - } - } - if (mii_reg5 == 0xffff || mii_reg5 == 0x0000) - ; /* No MII device or no link partner report */ - else if (tp->full_duplex_lock) - ; - else { - int negotiated = mii_reg5 & tp->advertising[0]; - int duplex = ((negotiated & 0x0100) != 0 - || (negotiated & 0x00C0) == 0x0040); - /* 100baseTx-FD or 10T-FD, but not 100-HD */ - if (tp->full_duplex != duplex) { - tp->full_duplex = duplex; - if (tp->full_duplex) - tp->csr6 |= 0x0200; - else - tp->csr6 &= ~0x0200; - outl(tp->csr6 | 0x0002, ioaddr + CSR6); - outl(tp->csr6 | 0x2002, ioaddr + CSR6); - if (tulip_debug > 0) /* Gurppp, should be >1 */ - printk(KERN_INFO "%s: Setting %s-duplex based on MII" - " Xcvr #%d parter capability of %4.4x.\n", - dev->name, tp->full_duplex ? "full" : "half", - tp->phys[0], mii_reg5); - } - } + check_duplex(dev); next_tick = 60*HZ; break; - } case 2: /* 21142 serial block has no link beat. */ default: break; @@ -1752,10 +2006,8 @@ } break; } - if (next_tick) { - tp->timer.expires = RUN_AT(next_tick); - add_timer(&tp->timer); - } + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); } /* Handle the 21143 uniquely: do autoselect with NWay, not the EEPROM list @@ -1769,49 +2021,50 @@ int next_tick = 60*HZ; int new_csr6 = 0; - if (tulip_debug > 1) - printk(KERN_INFO"%s: 21142 negotiation status %8.8x, %s.\n", + if (tulip_debug > 2) + printk(KERN_INFO"%s: 21143 negotiation status %8.8x, %s.\n", dev->name, csr12, medianame[dev->if_port]); - if (dev->if_port == 3) { - if (csr12 & 2) { /* No 100mbps link beat, revert to 10mbps. */ - new_csr6 = 0x82420200; - outl(new_csr6, ioaddr + CSR6); - outl(0x0000, ioaddr + CSR13); - outl(0x0003FFFF, ioaddr + CSR14); - outl(0x0008, ioaddr + CSR15); - outl(0x0001, ioaddr + CSR13); - outl(0x1301, ioaddr + CSR12); /* Start NWay. */ + if (media_cap[dev->if_port] & MediaIsMII) { + check_duplex(dev); + next_tick = 60*HZ; + } else if (tp->nwayset) { + /* Don't screw up a negotiated session! */ + if (tulip_debug > 1) + printk(KERN_INFO"%s: Using NWay-set %s media, csr12 %8.8x.\n", + dev->name, medianame[dev->if_port], csr12); + } else if (tp->medialock) { + ; + } else if (dev->if_port == 3) { + if (csr12 & 2) { /* No 100mbps link beat, revert to 10mbps. */ + if (tulip_debug > 1) + printk(KERN_INFO"%s: No 21143 100baseTx link beat, %8.8x, " + "trying NWay.\n", dev->name, csr12); + t21142_start_nway(dev); + next_tick = 3*HZ; } } else if ((csr12 & 0x7000) != 0x5000) { /* Negotiation failed. Search media types. */ if (tulip_debug > 1) - printk(KERN_INFO"%s: 21142 negotiation failed, status %8.8x.\n", + printk(KERN_INFO"%s: 21143 negotiation failed, status %8.8x.\n", dev->name, csr12); if (!(csr12 & 4)) { /* 10mbps link beat good. */ new_csr6 = 0x82420000; dev->if_port = 0; outl(0, ioaddr + CSR13); outl(0x0003FFFF, ioaddr + CSR14); - outl(t21142_csr15[dev->if_port], ioaddr + CSR15); + outw(t21142_csr15[dev->if_port], ioaddr + CSR15); outl(t21142_csr13[dev->if_port], ioaddr + CSR13); - } else if (csr12 & 0x100) { - new_csr6 = 0x82420200; - dev->if_port = 2; - outl(0, ioaddr + CSR13); - outl(0x0003FFFF, ioaddr + CSR14); - outl(0x0008, ioaddr + CSR15); - outl(0x0001, ioaddr + CSR13); } else { /* Select 100mbps port to check for link beat. */ new_csr6 = 0x83860000; dev->if_port = 3; outl(0, ioaddr + CSR13); outl(0x0003FF7F, ioaddr + CSR14); - outl(8, ioaddr + CSR15); + outw(8, ioaddr + CSR15); outl(1, ioaddr + CSR13); } if (tulip_debug > 1) - printk(KERN_INFO"%s: Testing new 21142 media %s.\n", + printk(KERN_INFO"%s: Testing new 21143 media %s.\n", dev->name, medianame[dev->if_port]); if (new_csr6 != (tp->csr6 & ~0x00D5)) { tp->csr6 &= 0x00D5; @@ -1820,51 +2073,147 @@ outl(tp->csr6 | 0x0002, ioaddr + CSR6); outl(tp->csr6 | 0x2002, ioaddr + CSR6); } + next_tick = 3*HZ; } + if (tp->cur_tx - tp->dirty_tx > 0 && + jiffies - dev->trans_start > TX_TIMEOUT) { + printk(KERN_WARNING "%s: Tx hung, %d vs. %d.\n", + dev->name, tp->cur_tx, tp->dirty_tx); + tulip_tx_timeout(dev); + } + tp->timer.expires = RUN_AT(next_tick); add_timer(&tp->timer); } -static void t21142_lnk_change( struct device *dev) +static void t21142_start_nway(struct device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + int csr14 = ((tp->to_advertise & 0x0780) << 9) | + ((tp->to_advertise&0x0020)<<1) | 0xffbf; + + dev->if_port = 0; + tp->nway = tp->mediasense = 1; + tp->nwayset = tp->lpar = 0; + if (debug > 1) + printk(KERN_DEBUG "%s: Restarting 21143 autonegotiation, %8.8x.\n", + dev->name, csr14); + outl(0x0001, ioaddr + CSR13); + outl(csr14, ioaddr + CSR14); + tp->csr6 = 0x82420000 | (tp->to_advertise & 0x0040 ? 0x0200 : 0); + outl(tp->csr6, ioaddr + CSR6); + if (tp->mtable && tp->mtable->csr15dir) { + outl(tp->mtable->csr15dir, ioaddr + CSR15); + outl(tp->mtable->csr15val, ioaddr + CSR15); + } else + outw(0x0008, ioaddr + CSR15); + outl(0x1301, ioaddr + CSR12); /* Trigger NWAY. */ +} + +static void t21142_lnk_change(struct device *dev, int csr5) { struct tulip_private *tp = (struct tulip_private *)dev->priv; long ioaddr = dev->base_addr; int csr12 = inl(ioaddr + CSR12); if (tulip_debug > 1) - printk(KERN_INFO"%s: 21142 link status interrupt %8.8x, CSR5 %x.\n", - dev->name, csr12, inl(ioaddr + CSR5)); + printk(KERN_INFO"%s: 21143 link status interrupt %8.8x, CSR5 %x, " + "%8.8x.\n", dev->name, csr12, csr5, inl(ioaddr + CSR14)); - if ((csr12 & 0x7000) == 0x5000) { - if (csr12 & 0x01800000) { - /* Switch to 100mbps mode. */ - outl(tp->csr6 | 0x0002, ioaddr + CSR6); - if (csr12 & 0x01000000) { - dev->if_port = 5; - tp->csr6 = 0x83860200; - } else { + /* If NWay finished and we have a negotiated partner capability. */ + if (tp->nway && !tp->nwayset && (csr12 & 0x7000) == 0x5000) { + int setup_done = 0; + int negotiated = tp->to_advertise & (csr12 >> 16); + tp->lpar = csr12 >> 16; + tp->nwayset = 1; + if (negotiated & 0x0100) dev->if_port = 5; + else if (negotiated & 0x0080) dev->if_port = 3; + else if (negotiated & 0x0040) dev->if_port = 4; + else if (negotiated & 0x0020) dev->if_port = 0; + else { + tp->nwayset = 0; + if ((csr12 & 2) == 0 && (tp->to_advertise & 0x0180)) dev->if_port = 3; - tp->csr6 = 0x83860000; - } - outl(tp->csr6 | 0x2002, ioaddr + CSR6); - } /* Else 10baseT-FD is handled automatically. */ - } else if (dev->if_port == 3) { - if (!(csr12 & 2)) - printk(KERN_INFO"%s: 21142 100baseTx link beat good.\n", - dev->name); - else - dev->if_port = 0; - } else if (dev->if_port == 0) { - if (!(csr12 & 4)) - printk(KERN_INFO"%s: 21142 10baseT link beat good.\n", + } + tp->full_duplex = (media_cap[dev->if_port] & MediaAlwaysFD) ? 1:0; + + if (tulip_debug > 1) { + if (tp->nwayset) + printk(KERN_INFO "%s: Switching to %s based on link " + "negotiation %4.4x & %4.4x = %4.4x.\n", + dev->name, medianame[dev->if_port], tp->to_advertise, + tp->lpar, negotiated); + else + printk(KERN_INFO "%s: Autonegotiation failed, using %s," + " link beat status %4.4x.\n", + dev->name, medianame[dev->if_port], csr12); + } + + if (tp->mtable) { + int i; + for (i = 0; i < tp->mtable->leafcount; i++) + if (tp->mtable->mleaf[i].media == dev->if_port) { + tp->cur_index = i; + select_media(dev, 0); + setup_done = 1; + break; + } + } + if ( ! setup_done) { + tp->csr6 = dev->if_port & 1 ? 0x83860000 : 0x82420000; + if (tp->full_duplex) + tp->csr6 |= 0x0200; + outl(1, ioaddr + CSR13); + } +#if 0 /* Restart shouldn't be needed. */ + outl(tp->csr6 | 0x0000, ioaddr + CSR6); + if (debug > 2) + printk(KERN_DEBUG "%s: Restarting Tx and Rx, CSR5 is %8.8x.\n", + dev->name, inl(ioaddr + CSR5)); +#endif + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + if (debug > 2) + printk(KERN_DEBUG "%s: Setting CSR6 %8.8x/%x CSR12 %8.8x.\n", + dev->name, tp->csr6, inl(ioaddr + CSR6), + inl(ioaddr + CSR12)); + } else if ((tp->nwayset && (csr5 & 0x08000000) + && (dev->if_port == 3 || dev->if_port == 5) + && (csr12 & 2) == 2) || + (tp->nway && (csr5 & (TPLnkFail)))) { + /* Link blew? Maybe restart NWay. */ + del_timer(&tp->timer); + t21142_start_nway(dev); + tp->timer.expires = RUN_AT(3*HZ); + add_timer(&tp->timer); + } else if (dev->if_port == 3 || dev->if_port == 5) { + if (tulip_debug > 1) + printk(KERN_INFO"%s: 21143 %s link beat %s.\n", + dev->name, medianame[dev->if_port], + (csr12 & 2) ? "failed" : "good"); + if ((csr12 & 2) && ! tp->medialock) { + del_timer(&tp->timer); + t21142_start_nway(dev); + tp->timer.expires = RUN_AT(3*HZ); + add_timer(&tp->timer); + } + } else if (dev->if_port == 0 || dev->if_port == 4) { + if ((csr12 & 4) == 0) + printk(KERN_INFO"%s: 21143 10baseT link beat good.\n", dev->name); } else if (!(csr12 & 4)) { /* 10mbps link beat good. */ - printk(KERN_INFO"%s: 21142 10mpbs sensed media.\n", + if (tulip_debug) + printk(KERN_INFO"%s: 21143 10mbps sensed media.\n", + dev->name); + dev->if_port = 0; + } else if (tp->nwayset) { + if (tulip_debug) + printk(KERN_INFO"%s: 21143 using NWay-set %s, csr6 %8.8x.\n", + dev->name, medianame[dev->if_port], tp->csr6); + } else { /* 100mbps link beat good. */ + if (tulip_debug) + printk(KERN_INFO"%s: 21143 100baseTx sensed media.\n", dev->name); - dev->if_port = 0; - } else { /* 100mbps link beat good. */ - printk(KERN_INFO"%s: 21142 100baseTx sensed media.\n", - dev->name); dev->if_port = 3; tp->csr6 = 0x83860000; outl(0x0003FF7F, ioaddr + CSR14); @@ -1873,7 +2222,6 @@ outl(tp->csr6 | 0x2002, ioaddr + CSR6); } } - static void mxic_timer(unsigned long data) { @@ -1892,57 +2240,98 @@ } } +static void pnic_do_nway(struct device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + u32 phy_reg = inl(ioaddr + 0xB8); + u32 new_csr6 = tp->csr6 & ~0x40C40200; + + if (phy_reg & 0x78000000) { /* Ignore baseT4 */ + if (phy_reg & 0x20000000) dev->if_port = 5; + else if (phy_reg & 0x40000000) dev->if_port = 3; + else if (phy_reg & 0x10000000) dev->if_port = 4; + else if (phy_reg & 0x08000000) dev->if_port = 0; + tp->nwayset = 1; + new_csr6 = (dev->if_port & 1) ? 0x01860000 : 0x00420000; + outl(0x32 | (dev->if_port & 1), ioaddr + CSR12); + if (dev->if_port & 1) + outl(0x1F868, ioaddr + 0xB8); + if (phy_reg & 0x30000000) { + tp->full_duplex = 1; + new_csr6 |= 0x00000200; + } + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: PNIC autonegotiated status %8.8x, %s.\n", + dev->name, phy_reg, medianame[dev->if_port]); + if (tp->csr6 != new_csr6) { + tp->csr6 = new_csr6; + outl(tp->csr6 | 0x0002, ioaddr + CSR6); /* Restart Tx */ + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + dev->trans_start = jiffies; + } + } +} +static void pnic_lnk_change(struct device *dev, int csr5) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + int phy_reg = inl(ioaddr + 0xB8); + + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: PNIC link changed state %8.8x, CSR5 %8.8x.\n", + dev->name, phy_reg, csr5); + if (inl(ioaddr + CSR5) & TPLnkFail) { + outl((inl(ioaddr + CSR7) & ~TPLnkFail) | TPLnkPass, ioaddr + CSR7); + if (! tp->nwayset || jiffies - dev->trans_start > 1*HZ) { + tp->csr6 = 0x00420000 | (tp->csr6 & 0x0000fdff); + outl(tp->csr6, ioaddr + CSR6); + outl(0x30, ioaddr + CSR12); + outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */ + dev->trans_start = jiffies; + } + } else if (inl(ioaddr + CSR5) & TPLnkPass) { + pnic_do_nway(dev); + outl((inl(ioaddr + CSR7) & ~TPLnkPass) | TPLnkFail, ioaddr + CSR7); + } +} static void pnic_timer(unsigned long data) { struct device *dev = (struct device *)data; struct tulip_private *tp = (struct tulip_private *)dev->priv; long ioaddr = dev->base_addr; - int csr12 = inl(ioaddr + CSR12); int next_tick = 60*HZ; - int new_csr6 = tp->csr6 & ~0x40C40200; if (media_cap[dev->if_port] & MediaIsMII) { - int negotiated = mdio_read(dev, tp->phys[0], 5) & tp->advertising[0]; - - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: LC82C168 negotiated capability %8.8x, " - "CSR5 %8.8x.\n", - dev->name, negotiated, inl(ioaddr + CSR5)); - - if (negotiated & 0x0380) /* 10 vs 100mbps */ - new_csr6 |= 0x812E0000; - else - new_csr6 |= 0x816E0000; - if (((negotiated & 0x0300) == 0x0100) /* Duplex */ - || (negotiated & 0x00C0) == 0x0040 - || tp->full_duplex_lock) { - tp->full_duplex = 1; - new_csr6 |= 0x0200; - } - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: LC82C168 MII PHY status %4.4x, Link " - "partner report %4.4x, csr6 %8.8x/%8.8x.\n", - dev->name, mdio_read(dev, tp->phys[0], 1), negotiated, - tp->csr6, inl(ioaddr + CSR6)); + if (check_duplex(dev) > 0) + next_tick = 3*HZ; } else { + int csr12 = inl(ioaddr + CSR12); + int new_csr6 = tp->csr6 & ~0x40C40200; int phy_reg = inl(ioaddr + 0xB8); int csr5 = inl(ioaddr + CSR5); if (tulip_debug > 1) - printk(KERN_DEBUG "%s: LC82C168 phy status %8.8x, CSR5 %8.8x.\n", - dev->name, phy_reg, csr5); - + printk(KERN_DEBUG "%s: PNIC timer PHY status %8.8x, %s " + "CSR5 %8.8x.\n", + dev->name, phy_reg, medianame[dev->if_port], csr5); if (phy_reg & 0x04000000) { /* Remote link fault */ - /*outl(0x0201F078, ioaddr + 0xB8);*/ - next_tick = 3*HZ; - } - if (inl(ioaddr + CSR5) & TPLnkFail) { /* 100baseTx link beat */ + outl(0x0201F078, ioaddr + 0xB8); + next_tick = 1*HZ; + tp->nwayset = 0; + } else if (phy_reg & 0x78000000) { /* Ignore baseT4 */ + pnic_do_nway(dev); + next_tick = 60*HZ; + } else if (csr5 & TPLnkFail) { /* 100baseTx link beat */ if (tulip_debug > 1) printk(KERN_DEBUG "%s: %s link beat failed, CSR12 %4.4x, " "CSR5 %8.8x, PHY %3.3x.\n", dev->name, medianame[dev->if_port], csr12, inl(ioaddr + CSR5), inl(ioaddr + 0xB8)); + next_tick = 3*HZ; if (tp->medialock) { + } else if (tp->nwayset && (dev->if_port & 1)) { + next_tick = 1*HZ; } else if (dev->if_port == 0) { dev->if_port = 3; outl(0x33, ioaddr + CSR12); @@ -1954,121 +2343,144 @@ new_csr6 = 0x00420000; outl(0x1F078, ioaddr + 0xB8); } - new_csr6 |= (tp->csr6 & 0xfdff); - next_tick = 3*HZ; - } else - new_csr6 = tp->csr6; - if (tp->full_duplex_lock || (phy_reg & 0x30000000) != 0) { - tp->full_duplex = 1; - new_csr6 |= 0x00000200; + if (tp->csr6 != new_csr6) { + tp->csr6 = new_csr6; + outl(tp->csr6 | 0x0002, ioaddr + CSR6); /* Restart Tx */ + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + dev->trans_start = jiffies; + if (tulip_debug > 1) + printk(KERN_INFO "%s: Changing PNIC configuration to %s " + "%s-duplex, CSR6 %8.8x.\n", + dev->name, medianame[dev->if_port], + tp->full_duplex ? "full" : "half", new_csr6); + } } } - if (tp->csr6 != new_csr6) { - tp->csr6 = new_csr6; - outl(tp->csr6 | 0x0002, ioaddr + CSR6); /* Restart Tx */ - outl(tp->csr6 | 0x2002, ioaddr + CSR6); - dev->trans_start = jiffies; - if (tulip_debug > 0) /* Gurppp, should be >1 */ - printk(KERN_INFO "%s: Changing PNIC configuration to %s-duplex, " - "CSR6 %8.8x.\n", - dev->name, tp->full_duplex ? "full" : "half", new_csr6); - } + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); +} + +static void comet_timer(unsigned long data) +{ + struct device *dev = (struct device *)data; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + int next_tick = 60*HZ; + + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Comet link status %4.4x partner capability " + "%4.4x.\n", + dev->name, inl(ioaddr + 0xB8), inl(ioaddr + 0xC8)); tp->timer.expires = RUN_AT(next_tick); add_timer(&tp->timer); } static void tulip_tx_timeout(struct device *dev) { - struct tulip_private *tp = (struct tulip_private *)dev->priv; - long ioaddr = dev->base_addr; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; - if (media_cap[dev->if_port] & MediaIsMII) { - /* Do nothing -- the media monitor should handle this. */ - if (tulip_debug > 1) - printk(KERN_WARNING "%s: Transmit timeout using MII device.\n", - dev->name); - dev->trans_start = jiffies; - return; - } else if (tp->chip_id == DC21040) { - if (inl(ioaddr + CSR12) & 0x0002) { - printk(KERN_INFO "%s: transmit timed out, switching to %s media.\n", - dev->name, dev->if_port ? "10baseT" : "AUI"); - dev->if_port ^= 1; - outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13); - } - dev->trans_start = jiffies; - return; - } else if (tp->chip_id == DC21041) { - u32 csr12 = inl(ioaddr + CSR12); + if (media_cap[dev->if_port] & MediaIsMII) { + /* Do nothing -- the media monitor should handle this. */ + if (tulip_debug > 1) + printk(KERN_WARNING "%s: Transmit timeout using MII device.\n", + dev->name); + } else if (tp->chip_id == DC21040) { + if ( !tp->medialock && inl(ioaddr + CSR12) & 0x0002) { + dev->if_port = (dev->if_port == 2 ? 0 : 2); + printk(KERN_INFO "%s: transmit timed out, switching to " + "%s.\n", + dev->name, medianame[dev->if_port]); + select_media(dev, 0); + } + dev->trans_start = jiffies; + return; + } else if (tp->chip_id == DC21041) { + int csr12 = inl(ioaddr + CSR12); + + printk(KERN_WARNING "%s: 21041 transmit timed out, status %8.8x, " + "CSR12 %8.8x, CSR13 %8.8x, CSR14 %8.8x, resetting...\n", + dev->name, inl(ioaddr + CSR5), csr12, + inl(ioaddr + CSR13), inl(ioaddr + CSR14)); + tp->mediasense = 1; + if ( ! tp->medialock) { + if (dev->if_port == 1 || dev->if_port == 2) + if (csr12 & 0x0004) { + dev->if_port = 2 - dev->if_port; + } else + dev->if_port = 0; + else + dev->if_port = 1; + select_media(dev, 0); + } + } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142 + || tp->chip_id == MX98713 || tp->chip_id == COMPEX9881) { + printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, " + "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n", + dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12), + inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15)); + if ( ! tp->medialock && tp->mtable) { + do + --tp->cur_index; + while (tp->cur_index >= 0 + && (media_cap[tp->mtable->mleaf[tp->cur_index].media] + & MediaIsFD)); + if (--tp->cur_index < 0) { + /* We start again, but should instead look for default. */ + tp->cur_index = tp->mtable->leafcount - 1; + } + select_media(dev, 0); + printk(KERN_WARNING "%s: transmit timed out, switching to %s " + "media.\n", dev->name, medianame[dev->if_port]); + } + } else { + printk(KERN_WARNING "%s: Transmit timed out, status %8.8x, CSR12 " + "%8.8x, resetting...\n", + dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12)); + dev->if_port = 0; + } + +#if defined(way_too_many_messages) + if (tulip_debug > 3) { + int i; + for (i = 0; i < RX_RING_SIZE; i++) { + u8 *buf = (u8 *)(tp->rx_ring[i].buffer1); + int j; + printk(KERN_DEBUG "%2d: %8.8x %8.8x %8.8x %8.8x " + "%2.2x %2.2x %2.2x.\n", + i, (unsigned int)tp->rx_ring[i].status, + (unsigned int)tp->rx_ring[i].length, + (unsigned int)tp->rx_ring[i].buffer1, + (unsigned int)tp->rx_ring[i].buffer2, + buf[0], buf[1], buf[2]); + for (j = 0; buf[j] != 0xee && j < 1600; j++) + if (j < 100) printk(" %2.2x", buf[j]); + printk(" j=%d.\n", j); + } + printk(KERN_DEBUG " Rx ring %8.8x: ", (int)tp->rx_ring); + for (i = 0; i < RX_RING_SIZE; i++) + printk(" %8.8x", (unsigned int)tp->rx_ring[i].status); + printk("\n" KERN_DEBUG " Tx ring %8.8x: ", (int)tp->tx_ring); + for (i = 0; i < TX_RING_SIZE; i++) + printk(" %8.8x", (unsigned int)tp->tx_ring[i].status); + printk("\n"); + } +#endif + + /* Stop and restart the chip's Tx processes . */ + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + /* Trigger an immediate transmit demand. */ + outl(0, ioaddr + CSR1); - printk(KERN_WARNING "%s: 21041 transmit timed out, status %8.8x, CSR12 %8.8x," - " CSR13 %8.8x, CSR14 %8.8x, resetting...\n", - dev->name, inl(ioaddr + CSR5), csr12, - inl(ioaddr + CSR13), inl(ioaddr + CSR14)); - tp->mediasense = 1; - if (dev->if_port == 1 || dev->if_port == 2) - if (csr12 & 0x0004) { - dev->if_port = 2 - dev->if_port; - } else - dev->if_port = 0; - else - dev->if_port = 1; - select_media(dev, 0); - tp->stats.tx_errors++; dev->trans_start = jiffies; + tp->stats.tx_errors++; return; - } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142 - || tp->chip_id == MX98713) { - /* Stop the transmit process. */ - outl(tp->csr6 | 0x0002, ioaddr + CSR6); - printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, " - "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n", - dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12), - inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15)); - if (tp->mtable) { - if (--tp->cur_index < 0) { - /* We start again, but should instead look for default. */ - tp->cur_index = tp->mtable->leafcount - 1; - } - select_media(dev, 0); - printk(KERN_WARNING "%s: transmit timed out, switching to %s media.\n", - dev->name, dev->if_port ? "100baseTx" : "10baseT"); - } - outl(tp->csr6 | 0x2002, ioaddr + CSR6); - tp->stats.tx_errors++; - dev->trans_start = jiffies; - return; - } else - printk(KERN_WARNING "%s: transmit timed out, status %8.8x, CSR12 %8.8x," - " resetting...\n", - dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12)); -#ifdef way_too_many_messages - printk(" Rx ring %8.8x: ", (int)tp->rx_ring); - for (i = 0; i < RX_RING_SIZE; i++) - printk(" %8.8x", (unsigned int)tp->rx_ring[i].status); - printk("\n Tx ring %8.8x: ", (int)tp->tx_ring); - for (i = 0; i < TX_RING_SIZE; i++) - printk(" %8.8x", (unsigned int)tp->tx_ring[i].status); - printk("\n"); -#endif - - /* Perhaps we should reinitialize the hardware here. */ - dev->if_port = 0; - /* Stop and restart the chip's Tx processes . */ - outl(tp->csr6 | 0x0002, ioaddr + CSR6); - outl(tp->csr6 | 0x2002, ioaddr + CSR6); - /* Trigger an immediate transmit demand. */ - outl(0, ioaddr + CSR1); - - dev->trans_start = jiffies; - tp->stats.tx_errors++; - return; } /* Initialize the Rx and Tx rings, along with various 'dev' bits. */ -static void -tulip_init_ring(struct device *dev) +static void tulip_init_ring(struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; int i; @@ -2076,40 +2488,42 @@ tp->tx_full = 0; tp->cur_rx = tp->cur_tx = 0; tp->dirty_rx = tp->dirty_tx = 0; + tp->susp_rx = 0; + tp->ttimer = 0; + tp->nir = 0; for (i = 0; i < RX_RING_SIZE; i++) { - tp->rx_ring[i].status = 0x80000000; /* Owned by Tulip chip */ - tp->rx_ring[i].length = PKT_BUF_SZ; - { - /* Note the receive buffer must be longword aligned. - dev_alloc_skb() provides 16 byte alignment. But do *not* - use skb_reserve() to align the IP header! */ - struct sk_buff *skb; - skb = DEV_ALLOC_SKB(PKT_BUF_SZ); - tp->rx_skbuff[i] = skb; - if (skb == NULL) - break; /* Bad news! */ - skb->dev = dev; /* Mark as being used by this device. */ -#if LINUX_VERSION_CODE > 0x10300 - tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail); -#else - tp->rx_ring[i].buffer1 = virt_to_bus(skb->data); -#endif - } - tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]); + tp->rx_ring[i].status = 0x00000000; + tp->rx_ring[i].length = cpu_to_le32(PKT_BUF_SZ); + tp->rx_ring[i].buffer2 = virt_to_le32desc(&tp->rx_ring[i+1]); + tp->rx_skbuff[i] = NULL; } /* Mark the last entry as wrapping the ring. */ - tp->rx_ring[i-1].length = PKT_BUF_SZ | 0x02000000; - tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]); + tp->rx_ring[i-1].length = cpu_to_le32(PKT_BUF_SZ | DESC_RING_WRAP); + tp->rx_ring[i-1].buffer2 = virt_to_le32desc(&tp->rx_ring[0]); + + for (i = 0; i < RX_RING_SIZE; i++) { + /* Note the receive buffer must be longword aligned. + dev_alloc_skb() provides 16 byte alignment. But do *not* + use skb_reserve() to align the IP header! */ + struct sk_buff *skb = dev_alloc_skb(PKT_BUF_SZ); + tp->rx_skbuff[i] = skb; + if (skb == NULL) + break; + skb->dev = dev; /* Mark as being used by this device. */ + tp->rx_ring[i].status = cpu_to_le32(DescOwned); /* Owned by Tulip chip */ + tp->rx_ring[i].buffer1 = virt_to_le32desc(skb->tail); + } + tp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); /* The Tx buffer descriptor is filled in as needed, but we do need to clear the ownership bit. */ for (i = 0; i < TX_RING_SIZE; i++) { tp->tx_skbuff[i] = 0; tp->tx_ring[i].status = 0x00000000; - tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]); + tp->tx_ring[i].buffer2 = virt_to_le32desc(&tp->tx_ring[i+1]); } - tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]); + tp->tx_ring[i-1].buffer2 = virt_to_le32desc(&tp->tx_ring[0]); } static int @@ -2128,80 +2542,76 @@ return 1; } - /* Caution: the write order is important here, set the base address - with the "ownership" bits last. */ + /* Caution: the write order is important here, set the field + with the ownership bits last. */ /* Calculate the next Tx descriptor entry. */ entry = tp->cur_tx % TX_RING_SIZE; tp->tx_skbuff[entry] = skb; - tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data); + tp->tx_ring[entry].buffer1 = virt_to_le32desc(skb->data); if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */ - flag = 0x60000000; /* No interrupt */ - dev->tbusy = 0; + flag = 0x60000000; /* No interrupt */ } else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) { - flag = 0xe0000000; /* Tx-done intr. */ - dev->tbusy = 0; + flag = 0xe0000000; /* Tx-done intr. */ } else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) { - flag = 0x60000000; /* No Tx-done intr. */ - dev->tbusy = 0; - } else { - /* Leave room for set_rx_mode() to fill entries. */ - flag = 0xe0000000; /* Tx-done intr. */ - tp->tx_full = 1; + flag = 0x60000000; /* No Tx-done intr. */ + } else { /* Leave room for set_rx_mode() to fill entries. */ + tp->tx_full = 1; + flag = 0xe0000000; /* Tx-done intr. */ } if (entry == TX_RING_SIZE-1) - flag |= 0xe2000000; + flag = 0xe0000000 | DESC_RING_WRAP; - tp->tx_ring[entry].length = skb->len | flag; - tp->tx_ring[entry].status = 0x80000000; /* Pass ownership to the chip. */ + tp->tx_ring[entry].length = cpu_to_le32(skb->len | flag); + tp->tx_ring[entry].status = cpu_to_le32(DescOwned); tp->cur_tx++; - /* Trigger an immediate transmit demand. */ - outl(0, dev->base_addr + CSR1); + if ( ! tp->tx_full) + clear_bit(0, (void*)&dev->tbusy); dev->trans_start = jiffies; + /* Trigger an immediate transmit demand. */ + outl(0, dev->base_addr + CSR1); return 0; } /* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */ -static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs) +static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs) { -#ifdef SA_SHIRQ /* Use the now-standard shared IRQ implementation. */ struct device *dev = (struct device *)dev_instance; -#else - struct device *dev = (struct device *)(irq2dev_map[irq]); -#endif - - struct tulip_private *tp; - long ioaddr; - int csr5, work_budget = max_interrupt_work; - - if (dev == NULL) { - printk (KERN_ERR" tulip_interrupt(): irq %d for unknown device.\n", - irq); + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + int csr5; + int entry; + int missed; + int rx = 0; + int tx = 0; + int oi = 0; + int maxrx = RX_RING_SIZE; + int maxtx = TX_RING_SIZE; + int maxoi = TX_RING_SIZE; + +#if defined(__i386__) && defined(SMP_CHECK) + if (test_and_set_bit(0, (void*)&dev->interrupt)) { + printk(KERN_ERR "%s: Duplicate entry of the interrupt handler by " + "processor %d.\n", + dev->name, hard_smp_processor_id()); + dev->interrupt = 0; return; } - - ioaddr = dev->base_addr; - tp = (struct tulip_private *)dev->priv; - if (test_and_set_bit(0, (void*)&tp->interrupt)) { -#ifdef SMP_CHECK - printk(KERN_ERR "%s: Re-entering the interrupt handler with proc %d," - " proc %d already handling.\n", dev->name, - tp->smp_proc_id, hard_smp_processor_id()); #else + if (dev->interrupt) { printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); -#endif return; } dev->interrupt = 1; -#ifdef SMP_CHECK - tp->smp_proc_id = hard_smp_processor_id(); #endif + tp->nir++; + do { csr5 = inl(ioaddr + CSR5); /* Acknowledge all of the current interrupt sources ASAP. */ @@ -2214,23 +2624,25 @@ if ((csr5 & (NormalIntr|AbnormalIntr)) == 0) break; - if (csr5 & (RxIntr | RxNoBuf)) - work_budget -= tulip_rx(dev); + if (csr5 & (RxIntr | RxNoBuf)) { + rx += tulip_rx(dev); + tulip_refill_rx(dev); + } - if (csr5 & (TxNoBuf | TxDied | TxIntr)) { + if (csr5 & (TxNoBuf | TxDied | TxIntr | TimerInt)) { unsigned int dirty_tx; for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0; dirty_tx++) { int entry = dirty_tx % TX_RING_SIZE; - int status = tp->tx_ring[entry].status; + int status = le32_to_cpu(tp->tx_ring[entry].status); if (status < 0) - break; /* It still hasn't been Txed */ + break; /* It still has not been Txed */ /* Check for Rx filter setup frames. */ if (tp->tx_skbuff[entry] == NULL) continue; - + if (status & 0x8000) { /* There was an major error, log it. */ #ifndef final_version @@ -2253,19 +2665,16 @@ if (status & 0x0001) tp->stats.tx_deferred++; #endif #if LINUX_VERSION_CODE > 0x20127 - tp->stats.tx_bytes += tp->tx_ring[entry].length & 0x7ff; + tp->stats.tx_bytes += tp->tx_skbuff[entry]->len; #endif tp->stats.collisions += (status >> 3) & 15; tp->stats.tx_packets++; } /* Free the original skb. */ -#if (LINUX_VERSION_CODE > 0x20155) - dev_kfree_skb(tp->tx_skbuff[entry]); -#else - dev_kfree_skb(tp->tx_skbuff[entry], FREE_WRITE); -#endif + dev_free_skb(tp->tx_skbuff[entry]); tp->tx_skbuff[entry] = 0; + tx++; } #ifndef final_version @@ -2281,15 +2690,15 @@ /* The ring is no longer full, clear tbusy. */ tp->tx_full = 0; dev->tbusy = 0; - mark_bh(NET_BH); + netif_wake_queue(dev); } tp->dirty_tx = dirty_tx; if (csr5 & TxDied) { - if (tulip_debug > 1) - printk(KERN_WARNING "%s: The transmitter stopped!" - " CSR5 is %x, CSR6 %x.\n", - dev->name, csr5, inl(ioaddr + CSR6)); + if (tulip_debug > 2) + printk(KERN_WARNING "%s: The transmitter stopped." + " CSR5 is %x, CSR6 %x, new CSR6 %x.\n", + dev->name, csr5, inl(ioaddr + CSR6), tp->csr6); outl(tp->csr6 | 0x0002, ioaddr + CSR6); outl(tp->csr6 | 0x2002, ioaddr + CSR6); } @@ -2297,6 +2706,8 @@ /* Log errors. */ if (csr5 & AbnormalIntr) { /* Abnormal error summary bit. */ + if (csr5 == 0xffffffff) + break; if (csr5 & TxJabber) tp->stats.tx_errors++; if (csr5 & TxFIFOUnderflow) { if ((tp->csr6 & 0xC000) != 0xC000) @@ -2306,128 +2717,186 @@ /* Restart the transmit process. */ outl(tp->csr6 | 0x0002, ioaddr + CSR6); outl(tp->csr6 | 0x2002, ioaddr + CSR6); + outl(0, ioaddr + CSR1); } if (csr5 & RxDied) { /* Missed a Rx frame. */ tp->stats.rx_errors++; tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; + outl(tp->csr6 | 0x2002, ioaddr + CSR6); } - if (csr5 & TimerInt) { - printk(KERN_ERR "%s: Something Wicked happened! %8.8x.\n", - dev->name, csr5); - /* Hmmmmm, it's not clear what to do here. */ + if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)) { + if (tp->link_change) + (tp->link_change)(dev, csr5); } - if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000) - && tp->chip_id == DC21142) { - if (tulip_debug > 1) - printk(KERN_INFO"%s: 21142 link change, CSR5 = %8.8x.\n", - dev->name, csr5); - t21142_lnk_change(dev); + if (csr5 & SytemError) { + printk(KERN_ERR "%s: (%lu) System Error occured\n", dev->name, tp->nir); } /* Clear all error sources, included undocumented ones! */ outl(0x0800f7ba, ioaddr + CSR5); + oi++; + } + if (csr5 & TimerInt) { +#if 0 + if (tulip_debug > 2) + printk(KERN_ERR "%s: Re-enabling interrupts, %8.8x.\n", + dev->name, csr5); + outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7); +#endif + tp->ttimer = 0; + oi++; } - if (--work_budget < 0) { + if (tx > maxtx || rx > maxrx || oi > maxoi) { if (tulip_debug > 1) - printk(KERN_WARNING "%s: Too much work at interrupt, " - "csr5=0x%8.8x.\n", dev->name, csr5); + printk(KERN_WARNING "%s: Too much work during an interrupt, " + "csr5=0x%8.8x. (%lu) (%d,%d,%d)\n", dev->name, csr5, tp->nir, tx, rx, oi); /* Acknowledge all interrupt sources. */ - outl(0x8001ffff, ioaddr + CSR5); -#ifdef notdef - /* Clear all but standard interrupt sources. */ - outl((~csr5) & 0x0001ebef, ioaddr + CSR7); +#if 0 + /* Clear all interrupting sources, set timer to re-enable. */ + outl(((~csr5) & 0x0001ebef) | NormalIntr | AbnormalIntr | TimerInt, + ioaddr + CSR7); + outl(12, ioaddr + CSR11); + tp->ttimer = 1; #endif break; } } while (1); - if (tulip_debug > 3) + tulip_refill_rx(dev); + + /* check if we card is in suspend mode */ + entry = tp->dirty_rx % RX_RING_SIZE; + if (tp->rx_skbuff[entry] == NULL) { + if (tulip_debug > 1) + printk(KERN_WARNING "%s: in rx suspend mode: (%lu) (tp->cur_rx = %u, ttimer = %d, rx = %d) go/stay in suspend mode\n", dev->name, tp->nir, tp->cur_rx, tp->ttimer, rx); + if (tp->ttimer == 0 || (inl(ioaddr + CSR11) & 0xffff) == 0) { + if (tulip_debug > 1) + printk(KERN_WARNING "%s: in rx suspend mode: (%lu) set timer\n", dev->name, tp->nir); + outl(tulip_tbl[tp->chip_id].valid_intrs | TimerInt, + ioaddr + CSR7); + outl(TimerInt, ioaddr + CSR5); + outl(12, ioaddr + CSR11); + tp->ttimer = 1; + } + } + + if ((missed = inl(ioaddr + CSR8) & 0x1ffff)) { + tp->stats.rx_dropped += missed & 0x10000 ? 0x10000 : missed; + } + + if (tulip_debug > 4) printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n", dev->name, inl(ioaddr + CSR5)); +#if defined(__i386__) + clear_bit(0, (void*)&dev->interrupt); +#else dev->interrupt = 0; - clear_bit(0, (void*)&tp->interrupt); +#endif return; } -static int -tulip_rx(struct device *dev) +static int tulip_refill_rx(struct device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int entry; + int refilled = 0; + + /* Refill the Rx ring buffers. */ + for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) { + entry = tp->dirty_rx % RX_RING_SIZE; + if (tp->rx_skbuff[entry] == NULL) { + struct sk_buff *skb; + skb = tp->rx_skbuff[entry] = dev_alloc_skb(PKT_BUF_SZ); + if (skb == NULL) + break; + skb->dev = dev; /* Mark as being used by this device. */ + tp->rx_ring[entry].buffer1 = virt_to_le32desc(skb->tail); + refilled++; + } + tp->rx_ring[entry].status = cpu_to_le32(DescOwned); + } + return refilled; +} + +static int tulip_rx(struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; int entry = tp->cur_rx % RX_RING_SIZE; int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx; - int work_done = 0; + int received = 0; if (tulip_debug > 4) printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry, tp->rx_ring[entry].status); - /* If we own the next entry, it's a new packet. Send it up. */ - while (tp->rx_ring[entry].status >= 0) { - s32 status = tp->rx_ring[entry].status; - + /* If we own the next entry, it is a new packet. Send it up. */ + while ( ! (tp->rx_ring[entry].status & cpu_to_le32(DescOwned))) { + s32 status = le32_to_cpu(tp->rx_ring[entry].status); + + if (tulip_debug > 5) + printk(KERN_DEBUG "%s: In tulip_rx(), entry %d %8.8x.\n", + dev->name, entry, status); if (--rx_work_limit < 0) break; - if ((status & 0x0300) != 0x0300) { - if ((status & 0xffff) != 0x7fff) { /* Ingore earlier buffers. */ - if (tulip_debug > 1) - printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " - "multiple buffers, status %8.8x!\n", + if ((status & 0x38008300) != 0x0300) { + if ((status & 0x38000300) != 0x0300) { + /* Ingore earlier buffers. */ + if ((status & 0xffff) != 0x7fff) { + if (tulip_debug > 1) + printk(KERN_WARNING "%s: Oversized Ethernet frame " + "spanned multiple buffers, status %8.8x!\n", + dev->name, status); + tp->stats.rx_length_errors++; + } + } else if (status & RxDescFatalErr) { + /* There was a fatal error. */ + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n", dev->name, status); - tp->stats.rx_length_errors++; + tp->stats.rx_errors++; /* end of a packet.*/ + if (status & 0x0890) tp->stats.rx_length_errors++; + if (status & 0x0004) tp->stats.rx_frame_errors++; + if (status & 0x0002) tp->stats.rx_crc_errors++; + if (status & 0x0001) tp->stats.rx_fifo_errors++; } - } else if (status & 0x8000) { - /* There was a fatal error. */ - if (tulip_debug > 2) - printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n", - dev->name, status); - tp->stats.rx_errors++; /* end of a packet.*/ - if (status & 0x0890) tp->stats.rx_length_errors++; - if (status & 0x0004) tp->stats.rx_frame_errors++; - if (status & 0x0002) tp->stats.rx_crc_errors++; - if (status & 0x0001) tp->stats.rx_fifo_errors++; } else { /* Omit the four octet CRC from the length. */ - short pkt_len = (status >> 16) - 4; + short pkt_len = ((status >> 16) & 0x7ff) - 4; struct sk_buff *skb; - /* Check if the packet is long enough to just accept without - copying to a properly sized skbuff. */ +#ifndef final_version + if (pkt_len > 1518) { + printk(KERN_WARNING "%s: Bogus packet size of %d (%#x).\n", + dev->name, pkt_len, pkt_len); + pkt_len = 1518; + tp->stats.rx_length_errors++; + } +#endif + /* Check if the packet is long enough to accept without copying + to a minimally-sized skbuff. */ if (pkt_len < rx_copybreak - && (skb = DEV_ALLOC_SKB(pkt_len+2)) != NULL) { + && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ -#if LINUX_VERSION_CODE < 0x10300 - memcpy(skb->data, tp->rx_ring[entry].buffer1, pkt_len); -#elif LINUX_VERSION_CODE < 0x20200 || defined(__alpha__) - memcpy(skb_put(skb, pkt_len), - bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len); -#else - eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1), - pkt_len, 0); +#if ! defined(__alpha__) + eth_copy_and_sum(skb, tp->rx_skbuff[entry]->tail, pkt_len, 0); skb_put(skb, pkt_len); +#else + memcpy(skb_put(skb, pkt_len), tp->rx_skbuff[entry]->tail, + pkt_len); #endif - work_done++; } else { /* Pass up the skb already on the Rx ring. */ - skb = tp->rx_skbuff[entry]; + char *temp = skb_put(skb = tp->rx_skbuff[entry], pkt_len); tp->rx_skbuff[entry] = NULL; #ifndef final_version - { - void *temp = skb_put(skb, pkt_len); - if (bus_to_virt(tp->rx_ring[entry].buffer1) != temp) - printk(KERN_ERR "%s: Internal consistency error! The " - "skbuff addresses do not match in tulip_rx:" - " %p vs. %p / %p.\n", dev->name, - bus_to_virt(tp->rx_ring[entry].buffer1), - skb->head, temp); - } -#else - skb_put(skb, pkt_len); + if (le32desc_to_virt(tp->rx_ring[entry].buffer1) != temp) + printk(KERN_ERR "%s: Internal fault: The skbuff addresses " + "do not match in tulip_rx: %p vs. %p / %p.\n", + dev->name, + le32desc_to_virt(tp->rx_ring[entry].buffer1), + skb->head, temp); #endif } -#if LINUX_VERSION_CODE > 0x10300 skb->protocol = eth_type_trans(skb, dev); -#else - skb->len = pkt_len; -#endif netif_rx(skb); dev->last_rx = jiffies; tp->stats.rx_packets++; @@ -2435,33 +2904,14 @@ tp->stats.rx_bytes += pkt_len; #endif } + received++; entry = (++tp->cur_rx) % RX_RING_SIZE; } - /* Refill the Rx ring buffers. */ - for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) { - entry = tp->dirty_rx % RX_RING_SIZE; - if (tp->rx_skbuff[entry] == NULL) { - struct sk_buff *skb; - skb = tp->rx_skbuff[entry] = DEV_ALLOC_SKB(PKT_BUF_SZ); - if (skb == NULL) - break; - skb->dev = dev; /* Mark as being used by this device. */ -#if LINUX_VERSION_CODE > 0x10300 - tp->rx_ring[entry].buffer1 = virt_to_bus(skb->tail); -#else - tp->rx_ring[entry].buffer1 = virt_to_bus(skb->data); -#endif - work_done++; - } - tp->rx_ring[entry].status = 0x80000000; - } - - return work_done; + return received; } -static int -tulip_close(struct device *dev) +static int tulip_close(struct device *dev) { long ioaddr = dev->base_addr; struct tulip_private *tp = (struct tulip_private *)dev->priv; @@ -2476,22 +2926,20 @@ /* Disable interrupts by clearing the interrupt mask. */ outl(0x00000000, ioaddr + CSR7); - /* Stop the chip's Tx and Rx processes. */ + /* Stop the Tx and Rx processes. */ outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6); /* 21040 -- Leave the card in 10baseT state. */ if (tp->chip_id == DC21040) outl(0x00000004, ioaddr + CSR13); - tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; + if (inl(ioaddr + CSR6) != 0xffffffff) + tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; del_timer(&tp->timer); -#ifdef SA_SHIRQ free_irq(dev->irq, dev); -#else - free_irq(dev->irq); - irq2dev_map[dev->irq] = 0; -#endif + + dev->if_port = tp->saved_if_port; /* Free all the skbuffs in the Rx queue. */ for (i = 0; i < RX_RING_SIZE; i++) { @@ -2504,31 +2952,26 @@ #if LINUX_VERSION_CODE < 0x20100 skb->free = 1; #endif -#if (LINUX_VERSION_CODE > 0x20155) - dev_kfree_skb(skb); -#else - dev_kfree_skb(skb, FREE_WRITE); -#endif + dev_free_skb(skb); } } for (i = 0; i < TX_RING_SIZE; i++) { if (tp->tx_skbuff[i]) -#if (LINUX_VERSION_CODE > 0x20155) - dev_kfree_skb(tp->tx_skbuff[i]); -#else - dev_kfree_skb(tp->tx_skbuff[i], FREE_WRITE); -#endif + dev_free_skb(tp->tx_skbuff[i]); tp->tx_skbuff[i] = 0; } + /* Leave the driver in snooze, not sleep, mode. */ + if (tp->flags & HAS_PWRDWN) + pcibios_write_config_dword(tp->pci_bus, tp->pci_devfn, 0x40, + 0x40000000); MOD_DEC_USE_COUNT; return 0; } -static struct enet_statistics * -tulip_get_stats(struct device *dev) +static struct net_device_stats *tulip_get_stats(struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; long ioaddr = dev->base_addr; @@ -2551,31 +2994,32 @@ switch(cmd) { case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ - if (tp->mtable && tp->mtable->has_mii) + if (tp->mii_cnt) data[0] = phy; - else if (tp->chip_id == DC21142) + else if (tp->flags & HAS_NWAY143) data[0] = 32; + else if (tp->chip_id == COMET) + data[0] = 1; else return -ENODEV; - return 0; case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ - if (data[0] == 32) { /* 21142 pseudo-MII */ + if (data[0] == 32 && (tp->flags & HAS_NWAY143)) { int csr12 = inl(ioaddr + CSR12); int csr14 = inl(ioaddr + CSR14); switch (data[1]) { case 0: { - data[3] = ((csr14<<13)&0x4000) + ((csr14<<5)&0x1000); + data[3] = (csr14<<5) & 0x1000; break; } case 1: data[3] = 0x7848 + ((csr12&0x7000) == 0x5000 ? 0x20 : 0) + (csr12&0x06 ? 0x04 : 0); break; case 4: { - int csr14 = inl(ioaddr + CSR14); - data[3] = ((csr14>>9)&0x0380) + ((csr14>>1)&0x20) + 1; + data[3] = ((csr14>>9)&0x07C0) + + ((inl(ioaddr + CSR6)>>3)&0x0040) + ((csr14>>1)&0x20) + 1; break; } - case 5: data[3] = inl(ioaddr + CSR12) >> 16; break; + case 5: data[3] = csr12 >> 16; break; default: data[3] = 0; break; } } else { @@ -2586,9 +3030,11 @@ } return 0; case SIOCDEVPRIVATE+2: /* Write the specified MII register */ - if (!suser()) + if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (data[0] == 32) { /* 21142 pseudo-MII */ + if (data[0] == 32 && (tp->flags & HAS_NWAY143)) { + if (data[1] == 5) + tp->to_advertise = data[2]; } else { save_flags(flags); cli(); @@ -2613,9 +3059,9 @@ N.B. Do not use for bulk data, use a table-based routine instead. This is common code and should be moved to net/core/crc.c */ static unsigned const ethernet_polynomial_le = 0xedb88320U; -static inline unsigned ether_crc_le(int length, unsigned char *data) +static inline u32 ether_crc_le(int length, unsigned char *data) { - unsigned int crc = 0xffffffff; /* Initial value. */ + u32 crc = 0xffffffff; /* Initial value. */ while(--length >= 0) { unsigned char current_octet = *data++; int bit; @@ -2629,80 +3075,107 @@ } return crc; } +static unsigned const ethernet_polynomial = 0x04c11db7U; +static inline u32 ether_crc(int length, unsigned char *data) +{ + int crc = -1; + + while(--length >= 0) { + unsigned char current_octet = *data++; + int bit; + for (bit = 0; bit < 8; bit++, current_octet >>= 1) + crc = (crc << 1) ^ + ((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0); + } + return crc; +} -#ifdef NEW_MULTICAST static void set_rx_mode(struct device *dev) -#else -static void set_rx_mode(struct device *dev, int num_addrs, void *addrs) -#endif { + struct tulip_private *tp = (struct tulip_private *)dev->priv; long ioaddr = dev->base_addr; int csr6 = inl(ioaddr + CSR6) & ~0x00D5; - struct tulip_private *tp = (struct tulip_private *)dev->priv; tp->csr6 &= ~0x00D5; if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ - outl(csr6 | 0x00C0, ioaddr + CSR6); + tp->csr6 |= 0x00C0; + csr6 |= 0x00C0; /* Unconditionally log net taps. */ printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name); - tp->csr6 |= 0xC0; } else if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) { - /* Too many to filter perfectly -- accept all multicasts. */ - outl(csr6 | 0x0080, ioaddr + CSR6); - tp->csr6 |= 0x80; + /* Too many to filter well -- accept all multicasts. */ + tp->csr6 |= 0x0080; + csr6 |= 0x0080; + } else if (tp->flags & MC_HASH_ONLY) { + /* Some work-alikes have only a 64-entry hash filter table. */ + /* Should verify correctness on big-endian/__powerpc__ */ + struct dev_mc_list *mclist; + int i; + u32 mc_filter[2]; /* Multicast hash filter */ + if (dev->mc_count > 64) { /* Arbitrary non-effective limit. */ + tp->csr6 |= 0x0080; + csr6 |= 0x0080; + } else { + mc_filter[1] = mc_filter[0] = 0; + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) + set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr)>>26, mc_filter); + if (tp->chip_id == AX88140) { + outl(2, ioaddr + CSR13); + outl(mc_filter[0], ioaddr + CSR14); + outl(3, ioaddr + CSR13); + outl(mc_filter[1], ioaddr + CSR14); + } else if (tp->chip_id == COMET) { /* Has a simple hash filter. */ + outl(mc_filter[0], ioaddr + 0xAC); + outl(mc_filter[1], ioaddr + 0xB0); + } + } } else { - u32 *setup_frm = tp->setup_frame; + u16 *eaddrs, *setup_frm = tp->setup_frame; struct dev_mc_list *mclist; - u16 *eaddrs; - u32 tx_flags; + u32 tx_flags = 0x08000000 | 192; int i; + /* Note that only the low-address shortword of setup_frame is valid! + The values are doubled for big-endian architectures. */ if (dev->mc_count > 14) { /* Must use a multicast hash table. */ - u16 hash_table[32]; - memset(hash_table, 0, sizeof(hash_table)); - /* This should work on big-endian machines as well. */ - for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; - i++, mclist = mclist->next) - set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff, - hash_table); - /* Copy the hash table to the setup frame. - NOTE that only the LOW SHORTWORD of setup_frame[] is valid! */ - for (i = 0; i < 32; i++) - *setup_frm++ = hash_table[i]; - setup_frm += 7; - tx_flags = 0x08400000 | 192; - /* Too clever: i > 15 for fall-though. */ + u16 hash_table[32]; + tx_flags = 0x08400000 | 192; /* Use hash filter. */ + memset(hash_table, 0, sizeof(hash_table)); + set_bit(255, hash_table); /* Broadcast entry */ + /* This should work on big-endian machines as well. */ + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) + set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff, + hash_table); + for (i = 0; i < 32; i++) + *setup_frm++ = *setup_frm++ = hash_table[i]; + setup_frm = &tp->setup_frame[13*6]; } else { - /* We have <= 15 addresses so we can use the wonderful - 16 address perfect filtering of the Tulip. */ - for (i = 0, mclist = dev->mc_list; i < dev->mc_count; - i++, mclist = mclist->next) { - /* Note that only the low shortword of setup_frame[] is valid! - This code may require tweaking for non-x86 architectures! */ - eaddrs = (u16 *)mclist->dmi_addr; - *setup_frm++ = *eaddrs++; - *setup_frm++ = *eaddrs++; - *setup_frm++ = *eaddrs++; - } - /* Fill the rest of the table with our physical address. - Once again, only the low shortword or setup_frame[] is valid! */ - *setup_frm++ = 0xffff; - *setup_frm++ = 0xffff; - *setup_frm++ = 0xffff; - tx_flags = 0x08000000 | 192; + /* We have <= 14 addresses so we can use the wonderful + 16 address perfect filtering of the Tulip. */ + for (i = 0, mclist = dev->mc_list; i < dev->mc_count; + i++, mclist = mclist->next) { + eaddrs = (u16 *)mclist->dmi_addr; + *setup_frm++ = *setup_frm++ = *eaddrs++; + *setup_frm++ = *setup_frm++ = *eaddrs++; + *setup_frm++ = *setup_frm++ = *eaddrs++; + } + /* Fill the unused entries with the broadcast address. */ + memset(setup_frm, 0xff, (15-i)*12); + setup_frm = &tp->setup_frame[15*6]; } + /* Fill the final entry with our physical address. */ eaddrs = (u16 *)dev->dev_addr; - do { - *setup_frm++ = eaddrs[0]; - *setup_frm++ = eaddrs[1]; - *setup_frm++ = eaddrs[2]; - } while (++i < 15); + *setup_frm++ = *setup_frm++ = eaddrs[0]; + *setup_frm++ = *setup_frm++ = eaddrs[1]; + *setup_frm++ = *setup_frm++ = eaddrs[2]; /* Now add this frame to the Tx list. */ if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) { /* Same setup recently queued, we need not add it. */ } else { unsigned long flags; - unsigned int entry, dummy = 0; + unsigned int entry; save_flags(flags); cli(); entry = tp->cur_tx++ % TX_RING_SIZE; @@ -2711,32 +3184,29 @@ /* Avoid a chip errata by prefixing a dummy entry. */ tp->tx_skbuff[entry] = 0; tp->tx_ring[entry].length = - (entry == TX_RING_SIZE-1) ? 0x02000000 : 0; + (entry == TX_RING_SIZE-1) ? cpu_to_le32(DESC_RING_WRAP) : 0; tp->tx_ring[entry].buffer1 = 0; - /* race with chip, set DescOwned later */ - dummy = entry; + tp->tx_ring[entry].status = cpu_to_le32(DescOwned); entry = tp->cur_tx++ % TX_RING_SIZE; } tp->tx_skbuff[entry] = 0; /* Put the setup frame on the Tx list. */ if (entry == TX_RING_SIZE-1) - tx_flags |= 0x02000000; /* Wrap ring. */ - tp->tx_ring[entry].length = tx_flags; - tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame); - tp->tx_ring[entry].status = 0x80000000; + tx_flags |= DESC_RING_WRAP; /* Wrap ring. */ + tp->tx_ring[entry].length = cpu_to_le32(tx_flags); + tp->tx_ring[entry].buffer1 = virt_to_le32desc(tp->setup_frame); + tp->tx_ring[entry].status = cpu_to_le32(DescOwned); if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) { - dev->tbusy = 1; + set_bit(0, (void*)&dev->tbusy); tp->tx_full = 1; } - if (dummy >= 0) - tp->tx_ring[dummy].status = DescOwned; restore_flags(flags); /* Trigger an immediate transmit demand. */ outl(0, ioaddr + CSR1); } - outl(csr6 | 0x0000, ioaddr + CSR6); } + outl(csr6 | 0x0000, ioaddr + CSR6); } #ifdef CARDBUS @@ -2745,18 +3215,18 @@ static dev_node_t *tulip_attach(dev_locator_t *loc) { + struct device *dev; u16 dev_id; u32 io; - u8 bus, devfn; - struct device *dev; + u8 bus, devfn, irq; if (loc->bus != LOC_PCI) return NULL; bus = loc->b.pci.bus; devfn = loc->b.pci.devfn; printk(KERN_INFO "tulip_attach(bus %d, function %d)\n", bus, devfn); pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &io); pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &dev_id); - io &= ~3; - dev = tulip_probe1(bus, devfn, NULL, DC21142, -1); + pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq); + dev = tulip_probe1(bus, devfn, NULL, io & ~3, irq, DC21142, 0); if (dev) { dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL); strcpy(node->dev_name, dev->name); @@ -2768,6 +3238,45 @@ return NULL; } +static void tulip_suspend(dev_node_t *node) +{ + struct device **devp, **next; + printk(KERN_INFO "tulip_suspend(%s)\n", node->dev_name); + for (devp = &root_tulip_dev; *devp; devp = next) { + next = &((struct tulip_private *)(*devp)->priv)->next_module; + if (strcmp((*devp)->name, node->dev_name) == 0) break; + } + if (*devp) { + long ioaddr = (*devp)->base_addr; + struct tulip_private *tp = (struct tulip_private *)(*devp)->priv; + int csr6 = inl(ioaddr + CSR6); + /* Disable interrupts, stop the chip, gather stats. */ + if (csr6 != 0xffffffff) { + outl(0x00000000, ioaddr + CSR7); + outl(csr6 & ~0x2002, ioaddr + CSR6); + tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; + } + tulip_close(*devp); + /* Put the 21143 into sleep mode. */ + pcibios_write_config_dword(tp->pci_bus,tp->pci_devfn, 0x40,0x80000000); + } +} + +static void tulip_resume(dev_node_t *node) +{ + struct device **devp, **next; + printk(KERN_INFO "tulip_resume(%s)\n", node->dev_name); + for (devp = &root_tulip_dev; *devp; devp = next) { + next = &((struct tulip_private *)(*devp)->priv)->next_module; + if (strcmp((*devp)->name, node->dev_name) == 0) break; + } + if (*devp) { + struct tulip_private *tp = (struct tulip_private *)(*devp)->priv; + pcibios_write_config_dword(tp->pci_bus, tp->pci_devfn, 0x40, 0x0000); + tulip_open(*devp); + } +} + static void tulip_detach(dev_node_t *node) { struct device **devp, **next; @@ -2786,34 +3295,17 @@ } struct driver_operations tulip_ops = { - "tulip_cb", tulip_attach, NULL, NULL, tulip_detach + "tulip_cb", tulip_attach, tulip_suspend, tulip_resume, tulip_detach }; #endif /* Cardbus support */ #ifdef MODULE -#if LINUX_VERSION_CODE > 0x20118 -MODULE_AUTHOR("Donald Becker "); -MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet 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"); -#endif - -/* An additional parameter that may be passed in... */ -static int debug = -1; - -int -init_module(void) +int init_module(void) { - if (debug >= 0) - tulip_debug = debug; - #ifdef CARDBUS + reverse_probe = 0; /* Not used. */ register_driver(&tulip_ops); return 0; #else @@ -2821,8 +3313,7 @@ #endif } -void -cleanup_module(void) +void cleanup_module(void) { struct device *next_dev; @@ -2832,9 +3323,11 @@ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ while (root_tulip_dev) { - next_dev = ((struct tulip_private *)root_tulip_dev->priv)->next_module; + struct tulip_private *tp = (struct tulip_private *)root_tulip_dev->priv; + next_dev = tp->next_module; unregister_netdev(root_tulip_dev); - release_region(root_tulip_dev->base_addr, TULIP_TOTAL_SIZE); + release_region(root_tulip_dev->base_addr, + tulip_tbl[tp->chip_id].io_size); kfree(root_tulip_dev); root_tulip_dev = next_dev; } @@ -2844,8 +3337,9 @@ /* * Local variables: - * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" - * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * cardbus-compile-command: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c -o tulip_cb.o -I/usr/src/pcmcia-cs-3.0.9/include/" * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 diff -u --recursive --new-file v2.2.13/linux/drivers/net/wavelan.c linux/drivers/net/wavelan.c --- v2.2.13/linux/drivers/net/wavelan.c Tue Jan 4 11:10:37 2000 +++ linux/drivers/net/wavelan.c Tue Jan 4 10:12:18 2000 @@ -1910,18 +1910,18 @@ case SIOCSIWNWID: /* Set NWID in WaveLAN. */ - if(wrq->u.nwid.on) + if(!wrq->u.nwid.disabled) { - /* Set NWID in psa. */ - psa.psa_nwid[0] = (wrq->u.nwid.nwid & 0xFF00) >> 8; - psa.psa_nwid[1] = wrq->u.nwid.nwid & 0xFF; + /* Set NWID in psa */ + psa.psa_nwid[0] = (wrq->u.nwid.value & 0xFF00) >> 8; + psa.psa_nwid[1] = wrq->u.nwid.value & 0xFF; psa.psa_nwid_select = 0x01; psa_write(ioaddr, lp->hacr, (char *)psa.psa_nwid - (char *)&psa, (unsigned char *)psa.psa_nwid, 3); /* Set NWID in mmc. */ - m.w.mmw_netw_id_l = wrq->u.nwid.nwid & 0xFF; - m.w.mmw_netw_id_h = (wrq->u.nwid.nwid & 0xFF00) >> 8; + m.w.mmw_netw_id_l = psa.psa_nwid[1]; + m.w.mmw_netw_id_h = psa.psa_nwid[0]; mmc_write(ioaddr, (char *)&m.w.mmw_netw_id_l - (char *)&m, (unsigned char *)&m.w.mmw_netw_id_l, 2); mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), 0x00); @@ -1945,8 +1945,9 @@ /* Read the NWID. */ psa_read(ioaddr, lp->hacr, (char *)psa.psa_nwid - (char *)&psa, (unsigned char *)psa.psa_nwid, 3); - wrq->u.nwid.nwid = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1]; - wrq->u.nwid.on = psa.psa_nwid_select; + wrq->u.nwid.value = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1]; + wrq->u.nwid.disabled = !(psa.psa_nwid_select); + wrq->u.nwid.fixed = 1; /* Superfluous */ break; case SIOCSIWFREQ: @@ -2006,83 +2007,96 @@ wrq->u.sens.fixed = 1; break; - case SIOCSIWENCODE: - /* Set encryption key. */ - if(!mmc_encr(ioaddr)) - { - ret = -EOPNOTSUPP; - break; - } - - if(wrq->u.encoding.method) - { /* Enable encryption. */ - int i; - long long key = wrq->u.encoding.code; - - for(i = 7; i >= 0; i--) - { - psa.psa_encryption_key[i] = key & 0xFF; - key >>= 8; - } - psa.psa_encryption_select = 1; - psa_write(ioaddr, lp->hacr, - (char *) &psa.psa_encryption_select - (char *) &psa, - (unsigned char *) &psa.psa_encryption_select, 8+1); - - mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), - MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE); - mmc_write(ioaddr, mmwoff(0, mmw_encr_key), - (unsigned char *) &psa.psa_encryption_key, 8); - } - else - { /* Disable encryption. */ - psa.psa_encryption_select = 0; - psa_write(ioaddr, lp->hacr, - (char *) &psa.psa_encryption_select - (char *) &psa, - (unsigned char *) &psa.psa_encryption_select, 1); - - mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), 0); - } - /* update the Wavelan checksum */ - update_psa_checksum(dev, ioaddr, lp->hacr); - break; - - case SIOCGIWENCODE: - /* Read the encryption key. */ - if(!mmc_encr(ioaddr)) - { - ret = -EOPNOTSUPP; - break; - } - - /* Only super-user can see encryption key. */ - if(!suser()) - { - ret = -EPERM; - break; - } - else - { - int i; - long long key = 0; + case SIOCSIWENCODE: + /* Set encryption key */ + if(!mmc_encr(ioaddr)) + { + ret = -EOPNOTSUPP; + break; + } + + /* Basic checking... */ + if(wrq->u.encoding.pointer != (caddr_t) 0) + { + /* Check the size of the key */ + if(wrq->u.encoding.length != 8) + { + ret = -EINVAL; + break; + } - psa_read(ioaddr, lp->hacr, + /* Copy the key in the driver */ + if(copy_from_user(psa.psa_encryption_key, wrq->u.encoding.pointer, + wrq->u.encoding.length)) + { + ret = -EFAULT; + break; + } + + psa.psa_encryption_select = 1; + psa_write(ioaddr, lp->hacr, (char *) &psa.psa_encryption_select - (char *) &psa, - (unsigned char *) &psa.psa_encryption_select, 1+8); - for(i = 0; i < 8; i++) - { - key <<= 8; - key += psa.psa_encryption_key[i]; - } - wrq->u.encoding.code = key; - - /* encryption is enabled */ - if(psa.psa_encryption_select) - wrq->u.encoding.method = mmc_encr(ioaddr); - else - wrq->u.encoding.method = 0; - } - break; + (unsigned char *) &psa.psa_encryption_select, 8+1); + + mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), + MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE); + mmc_write(ioaddr, mmwoff(0, mmw_encr_key), + (unsigned char *) &psa.psa_encryption_key, 8); + } + + if(wrq->u.encoding.flags & IW_ENCODE_DISABLED) + { /* disable encryption */ + psa.psa_encryption_select = 0; + psa_write(ioaddr, lp->hacr, + (char *) &psa.psa_encryption_select - (char *) &psa, + (unsigned char *) &psa.psa_encryption_select, 1); + + mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), 0); + } + /* update the Wavelan checksum */ + update_psa_checksum(dev, ioaddr, lp->hacr); + break; + + case SIOCGIWENCODE: + /* Read the encryption key */ + if(!mmc_encr(ioaddr)) + { + ret = -EOPNOTSUPP; + break; + } + + /* only super-user can see encryption key */ + if(!suser()) + { + ret = -EPERM; + break; + } + + /* Basic checking... */ + if(wrq->u.encoding.pointer != (caddr_t) 0) + { + /* Verify the user buffer */ + ret = verify_area(VERIFY_WRITE, wrq->u.encoding.pointer, 8); + if(ret) + break; + + psa_read(ioaddr, lp->hacr, + (char *) &psa.psa_encryption_select - (char *) &psa, + (unsigned char *) &psa.psa_encryption_select, 1+8); + + /* encryption is enabled ? */ + if(psa.psa_encryption_select) + wrq->u.encoding.flags = IW_ENCODE_ENABLED; + else + wrq->u.encoding.flags = IW_ENCODE_DISABLED; + wrq->u.encoding.flags |= mmc_encr(ioaddr); + + /* Copy the key to the user buffer */ + wrq->u.encoding.length = 8; + if(copy_to_user(wrq->u.encoding.pointer, psa.psa_encryption_key, 8)) + ret = -EFAULT; + } + break; case SIOCGIWRANGE: /* basic checking */ @@ -2116,6 +2130,19 @@ range.num_bitrates = 1; range.bitrate[0] = 2000000; /* 2 Mb/s */ + + /* Encryption supported ? */ + if(mmc_encr(ioaddr)) + { + range.encoding_size[0] = 8; /* DES = 64 bits key */ + range.num_encoding_sizes = 1; + range.max_encoding_tokens = 1; /* Only one key possible */ + } + else + { + range.num_encoding_sizes = 0; + range.max_encoding_tokens = 0; + } /* Copy structure to the user buffer. */ if (copy_to_user(wrq->u.data.pointer, &range, sizeof(struct iw_range))) diff -u --recursive --new-file v2.2.13/linux/drivers/net/wavelan.p.h linux/drivers/net/wavelan.p.h --- v2.2.13/linux/drivers/net/wavelan.p.h Mon Aug 9 16:05:56 1999 +++ linux/drivers/net/wavelan.p.h Tue Jan 4 10:12:18 2000 @@ -300,6 +300,11 @@ * - Add the (short) list of bit-rates in range * - Developp a new sensitivity... (sens.value & sens.fixed) * + * Changes made for release in 2.2.14 & 2.3.23 : + * ------------------------------------------- + * - Fix check for root permission (break instead of exit) + * - New nwid & encoding setting (Wireless Extension 9) + * * Wishes & dreams: * ---------------- * - roaming (see Pcmcia driver) @@ -390,7 +395,7 @@ /************************ CONSTANTS & MACROS ************************/ #ifdef DEBUG_VERSION_SHOW -static const char *version = "wavelan.c : v20 (wireless extensions) 29/7/99\n"; +static const char *version = "wavelan.c : v21 (wireless extensions) 16/10/99\n"; #endif /* Watchdog temporisation */ diff -u --recursive --new-file v2.2.13/linux/drivers/net/z85230.c linux/drivers/net/z85230.c --- v2.2.13/linux/drivers/net/z85230.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/net/z85230.c Tue Jan 4 10:12:18 2000 @@ -14,10 +14,6 @@ * Asynchronous mode dropped for 2.2. For 2.3 we will attempt the * unification of all the Z85x30 asynchronous drivers for real. * - * To Do: - * - * Finish DMA mode support. - * * Performance * * Z85230: @@ -169,7 +165,7 @@ /* * As above but for enhanced chips. */ - + u8 z8530_hdlc_kilostream_85230[]= { 4, SYNC_ENAB|SDLC|X1CLK, @@ -354,13 +350,15 @@ z8530_tx_done(chan); } - if(altered&DCD) + if(altered&chan->dcdcheck) { - if(status&DCD) + if(status&chan->dcdcheck) { printk(KERN_INFO "%s: DCD raised\n", chan->dev->name); write_zsreg(chan, R3, chan->regs[3]|RxENABLE); - if(chan->netdevice) + if(chan->netdevice && + ((chan->netdevice->type == ARPHRD_HDLC) || + (chan->netdevice->type == ARPHRD_PPP))) sppp_reopen(chan->netdevice); } else @@ -440,7 +438,6 @@ if(status&TxEOM) { flags=claim_dma_lock(); - /* Transmit underrun */ disable_dma(chan->txdma); clear_dma_ff(chan->txdma); chan->txdma_on=0; @@ -448,13 +445,15 @@ z8530_tx_done(chan); } } - if(altered&DCD) + if(altered&chan->dcdcheck) { - if(status&DCD) + if(status&chan->dcdcheck) { printk(KERN_INFO "%s: DCD raised\n", chan->dev->name); write_zsreg(chan, R3, chan->regs[3]|RxENABLE); - if(chan->netdevice) + if(chan->netdevice && + ((chan->netdevice->type == ARPHRD_HDLC) || + (chan->netdevice->type == ARPHRD_PPP))) sppp_reopen(chan->netdevice); } else @@ -1012,6 +1011,8 @@ floating IRQ transition when we reset the chip */ dev->chanA.irqs=&z8530_nop; dev->chanB.irqs=&z8530_nop; + dev->chanA.dcdcheck=DCD; + dev->chanB.dcdcheck=DCD; /* Reset the chip */ write_zsreg(&dev->chanA, R9, 0xC0); udelay(200); @@ -1104,7 +1105,7 @@ c->mtu=1500; c->max=0; c->count=0; - c->status=0; /* Fixme - check DCD now */ + c->status=read_zsreg(c, R0); c->sync=1; write_zsreg(c, R3, c->regs[R3]|RxENABLE); return 0; @@ -1251,7 +1252,7 @@ * Save the ready state and the buffer currently * being used as the DMA target */ - + int ready=c->dma_ready; unsigned char *rxb=c->rx_buf[c->dma_num]; unsigned long flags; diff -u --recursive --new-file v2.2.13/linux/drivers/net/z85230.h linux/drivers/net/z85230.h --- v2.2.13/linux/drivers/net/z85230.h Sat Oct 17 15:33:45 1998 +++ linux/drivers/net/z85230.h Tue Jan 4 10:12:18 2000 @@ -270,6 +270,7 @@ struct sk_buff *skb; /* Buffer dptr points into */ struct sk_buff *skb2; /* Pending buffer */ u8 status; /* Current DCD */ + u8 dcdcheck; /* which bit to check for line */ u8 sync; /* Set if in sync mode */ u8 regs[32]; /* Register map for the chip */ @@ -413,7 +414,7 @@ * Standard interrupt vector sets */ -struct z8530_irqhandler z8530_sync, z8530_async, z8530_nop; +extern struct z8530_irqhandler z8530_sync, z8530_async, z8530_nop; /* * Asynchronous Interfacing diff -u --recursive --new-file v2.2.13/linux/drivers/pci/oldproc.c linux/drivers/pci/oldproc.c --- v2.2.13/linux/drivers/pci/oldproc.c Tue Jan 4 11:10:37 2000 +++ linux/drivers/pci/oldproc.c Tue Jan 4 10:12:18 2000 @@ -396,6 +396,9 @@ DEVICE( ALLIANCE, ALLIANCE_PROVIDEO, "Provideo"), DEVICE( ALLIANCE, ALLIANCE_AT24, "AT24"), DEVICE( ALLIANCE, ALLIANCE_AT3D, "AT3D"), + DEVICE( SYSKONNECT, SYSKONNECT_FP, "SK-FDDI-PCI"), + DEVICE( SYSKONNECT, SYSKONNECT_TR, "SK-TR-PCI"), + DEVICE( SYSKONNECT, SYSKONNECT_GE, "SK-98xx"), DEVICE( VMIC, VMIC_VME, "VMIVME-7587"), DEVICE( DIGI, DIGI_EPC, "AccelPort EPC"), DEVICE( DIGI, DIGI_RIGHTSWITCH, "RightSwitch SE-6"), @@ -526,6 +529,7 @@ DEVICE( INTEL, INTEL_82441, "82441FX Natoma"), DEVICE( INTEL, INTEL_82380FB, "82380FB Mobile"), DEVICE( INTEL, INTEL_82439, "82439HX Triton II"), + DEVICE( INTEL, INTEL_MEGARAID, "OEM MegaRAID Controller"), DEVICE( INTEL, INTEL_82371SB_0,"82371SB PIIX3 ISA"), DEVICE( INTEL, INTEL_82371SB_1,"82371SB PIIX3 IDE"), DEVICE( INTEL, INTEL_82371SB_2,"82371SB PIIX3 USB"), @@ -536,13 +540,16 @@ DEVICE( INTEL, INTEL_82371AB_2,"82371AB PIIX4 USB"), DEVICE( INTEL, INTEL_82371AB_3,"82371AB PIIX4 ACPI"), DEVICE( INTEL, INTEL_82443LX_0,"440LX - 82443LX PAC Host"), - DEVICE( COMPUTONE, COMPUTONE_IP2EX, "Computone IntelliPort Plus"), DEVICE( INTEL, INTEL_82443LX_1,"440LX - 82443LX PAC AGP"), DEVICE( INTEL, INTEL_82443BX_0,"440BX - 82443BX Host"), DEVICE( INTEL, INTEL_82443BX_1,"440BX - 82443BX AGP"), DEVICE( INTEL, INTEL_82443BX_2,"440BX - 82443BX Host (no AGP)"), DEVICE( INTEL, INTEL_P6, "Orion P6"), - DEVICE( INTEL, INTEL_82450GX, "82450GX Orion P6"), + DEVICE( INTEL, INTEL_82450GX, "450KX/GX [Orion] - 82454KX/GX PCI Bridge"), + DEVICE( INTEL, INTEL_82453GX, "450KX/GX [Orion] - 82453KX/GX Memory Controller"), + DEVICE( INTEL, INTEL_82451NX, "450NX - 82451NX Memory & I/O Controller"), + DEVICE( INTEL, INTEL_82454NX, "450NX - 82454NX PCI Expander Bridge"), + DEVICE( COMPUTONE, COMPUTONE_IP2EX, "Computone IntelliPort Plus"), DEVICE( KTI, KTI_ET32P2, "ET32P2"), DEVICE( ADAPTEC, ADAPTEC_7810, "AIC-7810 RAID"), DEVICE( ADAPTEC, ADAPTEC_7821, "AIC-7860"), @@ -847,6 +854,7 @@ case PCI_VENDOR_ID_ATRONICS: return "Atronics"; case PCI_VENDOR_ID_TIGERJET: return "TigerJet"; case PCI_VENDOR_ID_ARK: return "ARK Logic"; + case PCI_VENDOR_ID_SYSKONNECT: return "SysKonnect"; default: return "Unknown vendor"; } } diff -u --recursive --new-file v2.2.13/linux/drivers/s390/Config.in linux/drivers/s390/Config.in --- v2.2.13/linux/drivers/s390/Config.in Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/Config.in Tue Jan 4 10:12:18 2000 @@ -0,0 +1,67 @@ +mainmenu_option next_comment +comment 'S/390 block device drivers' + +tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP +if [ "$CONFIG_NET" = "y" ]; then + tristate 'Network block device support' CONFIG_BLK_DEV_NBD +fi +bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD +if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then + tristate ' Linear (append) mode' CONFIG_MD_LINEAR + tristate ' RAID-0 (striping) mode' CONFIG_MD_STRIPED + tristate ' RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING + tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5 +fi +if [ "$CONFIG_MD_LINEAR" = "y" -o "$CONFIG_MD_STRIPED" = "y" ]; then + bool ' Boot support (linear, striped)' CONFIG_MD_BOOT +fi +tristate 'RAM disk support' CONFIG_BLK_DEV_RAM +if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then + bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD +fi + +bool 'Support for VM minidisk (VM only)' CONFIG_MDISK +if [ "$CONFIG_MDISK" = "y" ]; then + bool ' Support for synchronous read-write' CONFIG_MDISK_SYNC +fi + +tristate 'Support for DASD devices' CONFIG_DASD +if [ "$CONFIG_DASD" != "n" ]; then + comment 'DASD disciplines' + bool ' Support for ECKD Disks' CONFIG_DASD_ECKD +fi + +#menu_option next_comment +#endmenu + +if [ "$CONFIG_NET" = "y" ]; then + mainmenu_option next_comment + comment 'S/390 Network device support' + + bool 'Network device support' CONFIG_NETDEVICES + if [ "$CONFIG_NETDEVICES" = "y" ]; then + menu_option next_comment + comment 'S390 Network devices' + bool 'CTC device support' CONFIG_CTC + bool 'IUCV device support (VM only)' CONFIG_IUCV + tristate 'Dummy net driver support' CONFIG_DUMMY + bool 'Ethernet (10 or 100Mbit)' CONFIG_NET_ETHERNET + bool 'Token Ring driver support' CONFIG_TR + fi + endmenu +fi + +mainmenu_option next_comment +comment 'S/390 Terminal and Console options' + +bool 'Support for 3215 line mode terminal' CONFIG_3215 +if [ "$CONFIG_3215" = "y" ]; then + bool 'Support for console on 3215 line mode terminal' CONFIG_3215_CONSOLE +fi + +bool 'Support for HWC line mode terminal' CONFIG_HWC +if [ "$CONFIG_HWC" = "y" ]; then + bool 'console on HWC line mode terminal' CONFIG_HWC_CONSOLE +fi +endmenu + diff -u --recursive --new-file v2.2.13/linux/drivers/s390/Makefile linux/drivers/s390/Makefile --- v2.2.13/linux/drivers/s390/Makefile Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/Makefile Tue Jan 4 10:12:18 2000 @@ -0,0 +1,40 @@ +# +# Makefile for the linux i386-specific parts of the memory manager. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +all: io.o + +CFLAGS += +O_TARGET := io.o +O_OBJS := +M_OBJS := + +SUBDIRS := $(SUBDIRS) arch/s390/drivers/block arch/s390/drivers/char \ + arch/s390/drivers/misc arch/s390/drivers/net +MOD_SUB_DIRS += ./net + +O_OBJS := block/s390-block.o \ + char/s390-char.o \ + misc/s390-misc.o \ + net/s390-net.o + +io.o: $(O_OBJS) + +block/s390-block.o: dummy + $(MAKE) -C block + +char/s390-char.o: dummy + $(MAKE) -C char + +misc/s390-misc.o: dummy + $(MAKE) -C misc + +net/s390-net.o: dummy + $(MAKE) -C net + +include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.2.13/linux/drivers/s390/block/Makefile linux/drivers/s390/block/Makefile --- v2.2.13/linux/drivers/s390/block/Makefile Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/block/Makefile Tue Jan 4 10:12:18 2000 @@ -0,0 +1,25 @@ +all: s390-block.o + +CFLAGS += +O_TARGET := s390-block.o +O_OBJS := +M_OBJS := + +ifeq ($(CONFIG_DASD),y) + O_OBJS += dasd.o dasd_ccwstuff.o + ifeq ($(CONFIG_PROC_FS),y) + O_OBJS += dasd_proc.o dasd_profile.o + endif + ifeq ($(CONFIG_DASD_ECKD),y) + O_OBJS += dasd_eckd.o + endif +endif + +ifeq ($(CONFIG_MDISK),y) + O_OBJS += mdisk.o +endif + +dasd_mod.o: $(D_OBJS) + $(LD) $(LD_RFLAG) -r -o $@ $+ + +include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.2.13/linux/drivers/s390/block/dasd.c linux/drivers/s390/block/dasd.c --- v2.2.13/linux/drivers/s390/block/dasd.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/block/dasd.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,1490 @@ +/* + * File...........: linux/drivers/s390/block/dasd.c + * Author(s)......: Holger Smolinski + * : Utz Bacher + * Bugreports.to..: + * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999 + */ + +#include +#include + +#ifdef MODULE +#include +#endif /* MODULE */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../../arch/s390/kernel/irq.h" + +#include "dasd.h" +#include + +#include "dasd_types.h" +#include "dasd_ccwstuff.h" + +#define PRINTK_HEADER DASD_NAME":" + +#define CCW_READ_DEVICE_CHARACTERISTICS 0x64 + +#define DASD_SSCH_RETRIES 5 + +/* This macro is a little tricky, but makes the code more easy to read... */ +#define MATCH(info,ct,cm,dt,dm) ( \ +( info -> sid_data.cu_type ct ) && ( info -> sid_data.cu_model cm ) && \ +( info -> sid_data.dev_type dt ) && ( info -> sid_data.dev_model dm ) ) + +/* Prototypes for the functions called from external */ +static ssize_t dasd_read (struct file *, char *, size_t, loff_t *); +static ssize_t dasd_write (struct file *, const char *, size_t, loff_t *); +static int dasd_ioctl (struct inode *, struct file *, unsigned int, unsigned long); +static int dasd_open (struct inode *, struct file *); +static int dasd_fsync (struct file *, struct dentry *); +static int dasd_release (struct inode *, struct file *); +void dasd_debug (unsigned long tag); +void dasd_profile_add (cqr_t *cqr); +void dasd_proc_init (void); + +static struct file_operations dasd_file_operations; + +spinlock_t dasd_lock; /* general purpose lock for the dasd driver */ + +/* All asynchronous I/O should waint on this wait_queue */ +struct wait_queue *dasd_waitq = NULL; + +static int dasd_autodetect = 1; +static int dasd_devno[DASD_MAX_DEVICES] = +{0,}; +static int dasd_count = 0; + +extern dasd_chanq_t *cq_head; + +static int +dasd_get_hexdigit (char c) +{ + if ((c >= '0') && (c <= '9')) + return c - '0'; + if ((c >= 'a') && (c <= 'f')) + return c + 10 - 'a'; + if ((c >= 'A') && (c <= 'F')) + return c + 10 - 'A'; + return -1; +} + +/* sets the string pointer after the next comma */ +static void +dasd_scan_for_next_comma (char **strptr) +{ + while (((**strptr) != ',') && ((**strptr)++)) + (*strptr)++; + + /* set the position AFTER the comma */ + if (**strptr == ',') + (*strptr)++; +} + +/*sets the string pointer after the next comma, if a parse error occured */ +static int +dasd_get_next_int (char **strptr) +{ + int j, i = -1; /* for cosmetic reasons first -1, then 0 */ + if (isxdigit (**strptr)) { + for (i = 0; isxdigit (**strptr);) { + i <<= 4; + j = dasd_get_hexdigit (**strptr); + if (j == -1) { + PRINT_ERR ("no integer: skipping range.\n"); + dasd_scan_for_next_comma (strptr); + i = -1; + break; + } + i += j; + (*strptr)++; + if (i > 0xffff) { + PRINT_ERR (" value too big, skipping range.\n"); + dasd_scan_for_next_comma (strptr); + i = -1; + break; + } + } + } + return i; +} + +static inline int +devindex_from_devno (int devno) +{ + int i; + for (i = 0; i < dasd_count; i++) { + if (dasd_devno[i] == devno) + return i; + } + if (dasd_autodetect) { + if (dasd_count < DASD_MAX_DEVICES) { + dasd_devno[dasd_count] = devno; + return dasd_count++; + } + return -EOVERFLOW; + } + return -ENODEV; +} + +/* returns 1, if dasd_no is in the specified ranges, otherwise 0 */ +static inline int +dasd_is_accessible (int devno) +{ + return (devindex_from_devno (devno) >= 0); +} + +/* dasd_insert_range skips ranges, if the start or the end is -1 */ +static void +dasd_insert_range (int start, int end) +{ + int curr; + FUNCTION_ENTRY ("dasd_insert_range"); + if (dasd_count >= DASD_MAX_DEVICES) { + PRINT_ERR (" too many devices specified, ignoring some.\n"); + FUNCTION_EXIT ("dasd_insert_range"); + return; + } + if ((start == -1) || (end == -1)) { + PRINT_ERR ("invalid format of parameter, skipping range\n"); + FUNCTION_EXIT ("dasd_insert_range"); + return; + } + if (end < start) { + PRINT_ERR (" ignoring range from %x to %x - start value " \ + "must be less than end value.\n", start, end); + FUNCTION_EXIT ("dasd_insert_range"); + return; + } +/* concurrent execution would be critical, but will not occur here */ + for (curr = start; curr <= end; curr++) { + if (dasd_is_accessible (curr)) { + PRINT_WARN (" %x is already in list as device %d\n", + curr, devindex_from_devno (curr)); + } + dasd_devno[dasd_count] = curr; + dasd_count++; + if (dasd_count >= DASD_MAX_DEVICES) { + PRINT_ERR (" too many devices specified, ignoring some.\n"); + break; + } + } + PRINT_INFO (" added dasd range from %x to %x.\n", + start, dasd_devno[dasd_count - 1]); + + FUNCTION_EXIT ("dasd_insert_range"); +} + +void +dasd_setup (char *str, int *ints) +{ + int devno, devno2; + + FUNCTION_ENTRY ("dasd_setup"); + dasd_autodetect = 0; + while (*str && *str != 1) { + if (!isxdigit (*str)) { + str++; /* to avoid looping on two commas */ + PRINT_ERR (" kernel parameter in invalid format.\n"); + continue; + } + devno = dasd_get_next_int (&str); + + /* range was skipped? -> scan for comma has been done */ + if (devno == -1) + continue; + + if (*str == ',') { + str++; + dasd_insert_range (devno, devno); + continue; + } + if (*str == '-') { + str++; + devno2 = dasd_get_next_int (&str); + if (devno2 == -1) { + PRINT_ERR (" invalid character in " \ + "kernel parameters."); + } else { + dasd_insert_range (devno, devno2); + } + dasd_scan_for_next_comma (&str); + continue; + } + if (*str == 0) { + dasd_insert_range (devno, devno); + break; + } + PRINT_ERR (" unexpected character in kernel parameter, " \ + "skipping range.\n"); + } + FUNCTION_EXIT ("dasd_setup"); +} + +static void +dd_geninit (struct gendisk *ignored) +{ + FUNCTION_ENTRY ("dd_geninit"); + FUNCTION_EXIT ("dd_geninit"); +} + +static struct hd_struct dd_hdstruct[DASD_MAX_DEVICES << PARTN_BITS]; +static int dd_blocksizes[DASD_MAX_DEVICES << PARTN_BITS]; + +struct gendisk dd_gendisk = +{ + MAJOR_NR, /* Major number */ + "dd", /* Major name */ + PARTN_BITS, /* Bits to shift to get real from partn */ + 1 << PARTN_BITS, /* Number of partitions per real */ + DASD_MAX_DEVICES, /* maximum number of real */ + dd_geninit, /* init function */ + dd_hdstruct, /* hd struct */ + dd_blocksizes, /* block sizes */ + 0, /* number */ + NULL, /* internal */ + NULL /* next */ + +}; + +void +sleep_done (struct semaphore *sem) +{ + if (sem != NULL) { + up (sem); + } +} + +void +sleep (int timeout) +{ + struct semaphore sem = MUTEX_LOCKED; + struct timer_list timer; + + init_timer (&timer); + timer.data = (unsigned long) &sem; + timer.expires = jiffies + timeout; + timer.function = (void (*)(unsigned long)) sleep_done; + printk (KERN_DEBUG PRINTK_HEADER + "Sleeping for timer tics %d\n", timeout); + add_timer (&timer); + down (&sem); + del_timer (&timer); +} + +#ifdef CONFIG_DASD_ECKD +extern dasd_operations_t dasd_eckd_operations; +#endif /* CONFIG_DASD_ECKD */ + +dasd_operations_t *dasd_disciplines[] = +{ +#ifdef CONFIG_DASD_ECKD + &dasd_eckd_operations +#endif /* CONFIG_DASD_ECKD */ +}; + +char *dasd_name[] = +{ +#ifdef CONFIG_DASD_ECKD + "ECKD" +#endif /* CONFIG_DASD_ECKD */ +}; + +dasd_information_t *dasd_info[DASD_MAX_DEVICES] = +{NULL,}; + +static int dasd_blks[256] = +{0,}; +static int dasd_secsize[256] = +{0,}; +static int dasd_blksize[256] = +{0,}; +static int dasd_maxsecs[256] = +{0,}; + +void +fill_sizes (int di) +{ + int rc; + int minor; + rc = dasd_disciplines[dasd_info[di]->type]->fill_sizes (di); + switch (rc) { + case -EMEDIUMTYPE: + dasd_info[di]->flags |= DASD_NOT_FORMATTED; + break; + } + PRINT_INFO ("%ld kB <- 'soft'-block: %d, hardsect %d Bytes\n", + dasd_info[di]->sizes.kbytes, + dasd_info[di]->sizes.bp_block, + dasd_info[di]->sizes.bp_sector); + switch (dasd_info[di]->type) { +#ifdef CONFIG_DASD_ECKD + case dasd_eckd: + dasd_info[di]->sizes.first_sector = + 3 << dasd_info[di]->sizes.s2b_shift; + break; +#endif /* CONFIG_DASD_ECKD */ + default: + INTERNAL_CHECK ("Unknown dasd type %d\n", dasd_info[di]->type); + } + minor = di << PARTN_BITS; + dasd_blks[minor] = dasd_info[di]->sizes.kbytes; + dasd_secsize[minor] = dasd_info[di]->sizes.bp_sector; + dasd_blksize[minor] = dasd_info[di]->sizes.bp_block; + dasd_maxsecs[minor] = + ((PAGE_SIZE/sizeof(ccw1_t))-4)<sizes.s2b_shift; + dasd_blks[minor + 1] = dasd_info[di]->sizes.kbytes - + (dasd_info[di]->sizes.first_sector >> 1); + dasd_secsize[minor + 1] = dasd_info[di]->sizes.bp_sector; + dasd_blksize[minor + 1] = dasd_info[di]->sizes.bp_block; + dasd_maxsecs[minor+1] = + ((PAGE_SIZE/sizeof(ccw1_t))-4)<sizes.s2b_shift; +} + +int +dasd_format (int dev, format_data_t * fdata) +{ + int rc; + int devindex = DEVICE_NR (dev); + PRINT_INFO ("Format called with devno %x\n", dev); + if (MINOR (dev) & (0xff >> (8 - PARTN_BITS))) { + PRINT_WARN ("Can't format partition! minor %x %x\n", + MINOR (dev), 0xff >> (8 - PARTN_BITS)); + return -EINVAL; + } + down (&dasd_info[devindex]->sem); + if (dasd_info[devindex]->open_count == 1) { + rc = dasd_disciplines[dasd_info[devindex]->type]-> + dasd_format (devindex, fdata); + if (rc) { + PRINT_WARN ("Formatting failed rc=%d\n", rc); + } + } else { + PRINT_WARN ("device is open! %d\n", dasd_info[devindex]->open_count); + rc = -EINVAL; + } + if (!rc) { + fill_sizes (devindex); + dasd_info[devindex]->flags &= ~DASD_NOT_FORMATTED; + } else { + dasd_info[devindex]->flags |= DASD_NOT_FORMATTED; + } + up (&dasd_info[devindex]->sem); + return rc; +} + +static inline int +do_dasd_ioctl (struct inode *inp, unsigned int no, unsigned long data) +{ + int rc; + int di; + dasd_information_t *dev; + + di = DEVICE_NR (inp->i_rdev); + if (!dasd_info[di]) { + PRINT_WARN ("No device registered as %d\n", inp->i_rdev); + return -EINVAL; + } + if ((_IOC_DIR (no) != _IOC_NONE) && (data == 0)) { + PRINT_DEBUG ("empty data ptr"); + return -EINVAL; + } + dev = dasd_info[di]; + if (!dev) { + PRINT_WARN ("No device registered as %d\n", inp->i_rdev); + return -EINVAL; + } + PRINT_INFO ("ioctl 0x%08x %s'0x%x'%d(%d) on dev %d/%d (%d) with data %8lx\n", no, + _IOC_DIR (no) == _IOC_NONE ? "0" : + _IOC_DIR (no) == _IOC_READ ? "r" : + _IOC_DIR (no) == _IOC_WRITE ? "w" : + _IOC_DIR (no) == (_IOC_READ | _IOC_WRITE) ? "rw" : "u", + _IOC_TYPE (no), _IOC_NR (no), _IOC_SIZE (no), + MAJOR (inp->i_rdev), MINOR (inp->i_rdev), di, data); + + switch (no) { + case BLKGETSIZE:{ /* Return device size */ + unsigned long blocks; + if (inp->i_rdev & 0x01) { + blocks = (dev->sizes.blocks - 3) << + dev->sizes.s2b_shift; + } else { + blocks = dev->sizes.kbytes << dev->sizes.s2b_shift; + } + rc = copy_to_user ((long *) data, &blocks, sizeof (long)); + break; + } + case BLKFLSBUF:{ + rc = fsync_dev (inp->i_rdev); + break; + } + case BLKRAGET:{ + rc = copy_to_user ((long *) data, + read_ahead + MAJOR_NR, sizeof (long)); + break; + } + case BLKRASET:{ + rc = copy_from_user (read_ahead + MAJOR_NR, + (long *) data, sizeof (long)); + break; + } + case BLKRRPART:{ + INTERNAL_CHECK ("BLKRPART not implemented%s", ""); + rc = -EINVAL; + break; + } + case HDIO_GETGEO:{ + INTERNAL_CHECK ("HDIO_GETGEO not implemented%s", ""); + rc = -EINVAL; + break; + } + RO_IOCTLS (inp->i_rdev, data); + + case BIODASDRSID:{ + rc = copy_to_user ((void *) data, + &(dev->info.sid_data), + sizeof (senseid_t)); + break; + } + case BIODASDRWTB:{ + int offset = 0; + int xlt; + rc = copy_from_user (&xlt, (void *) data, + sizeof (int)); + PRINT_INFO("Xlating %d to",xlt); + if (rc) + break; + if (MINOR (inp->i_rdev) & 1) + offset = 3; + xlt += offset; + printk(" %d \n",xlt); + rc = copy_to_user ((void *) data, &xlt, + sizeof (int)); + break; + } + case BIODASDFORMAT:{ + /* fdata == NULL is a valid arg to dasd_format ! */ + format_data_t *fdata = NULL; + if (data) { + fdata = kmalloc (sizeof (format_data_t), + GFP_ATOMIC); + if (!fdata) { + rc = -ENOMEM; + break; + } + rc = copy_from_user (fdata, (void *) data, + sizeof (format_data_t)); + if (rc) + break; + } + rc = dasd_format (inp->i_rdev, fdata); + if (fdata) { + kfree (fdata); + } + break; + } + default: + rc = -EINVAL; + break; + } + return rc; +} + +static void +dasd_end_request (struct request *req, int uptodate) +{ + struct buffer_head *bh; + FUNCTION_ENTRY ("dasd_end_request"); +#if DASD_PARANOIA > 2 + if (!req) { + INTERNAL_CHECK ("end_request called with zero arg%s\n", ""); + } +#endif /* DASD_PARANOIA */ + while ((bh = req->bh) != NULL) { + req->bh = bh->b_reqnext; + bh->b_reqnext = NULL; + bh->b_end_io (bh, uptodate); + } + if (!end_that_request_first (req, uptodate, DEVICE_NAME)) { +#ifndef DEVICE_NO_RANDOM + add_blkdev_randomness (MAJOR (req->rq_dev)); +#endif + DEVICE_OFF (req->rq_dev); + end_that_request_last (req); + } + FUNCTION_EXIT ("dasd_end_request"); + return; +} + +void +dasd_wakeup (void) +{ + wake_up (&dasd_waitq); +} + +int +dasd_unregister_dasd (int irq, dasd_type_t dt, dev_info_t * info) +{ + int rc = 0; + FUNCTION_ENTRY ("dasd_unregister_dasd"); + INTERNAL_CHECK ("dasd_unregister_dasd not implemented%s\n", ""); + FUNCTION_EXIT ("dasd_unregister_dasd"); + return rc; +} + +/* Below you find the functions already cleaned up */ +static dasd_type_t +check_type (dev_info_t * info) +{ + dasd_type_t type = dasd_none; + + FUNCTION_ENTRY ("check_type"); +#ifdef CONFIG_DASD_ECKD + if (MATCH (info, == 0x3990, ||1, == 0x3390, ||1) || + MATCH (info, == 0x3990, ||1, == 0x3380, ||1)) { + type = dasd_eckd; + } else +#endif /* CONFIG_DASD_ECKD */ + { + type = dasd_none; + } + FUNCTION_EXIT ("check_type"); + return type; +} + +static int +dasd_read_characteristics (dasd_information_t * info) +{ + int rc; + int ct = 0; + dev_info_t *di; + dasd_type_t dt; + + FUNCTION_ENTRY ("read_characteristics"); + if (info == NULL) { + return -ENODEV; + } + di = &(info->info); + if (di == NULL) { + return -ENODEV; + } + dt = check_type (di); + /* Some cross-checks, if the cu supports RDC */ + if (MATCH (di, == 0x2835, ||1, ||1, ||1) || + MATCH (di, == 0x3830, ||1, ||1, ||1) || + MATCH (di, == 0x3830, ||1, ||1, ||1) || + MATCH (di, == 0x3990, <=0x03, == 0x3380, <=0x0d)) { + PRINT_WARN ("Device %d (%x/%x at %x/%x) supports no RDC\n", + info->info.irq, + di->sid_data.dev_type, + di->sid_data.dev_model, + di->sid_data.cu_type, + di->sid_data.cu_model); + return -EINVAL; + } + switch (dt) { +#ifdef CONFIG_DASD_ECKD + case dasd_eckd: + ct = 64; + break; +#endif /* CONFIG_DASD_ECKD */ + default: + INTERNAL_ERROR ("don't know dasd type %d\n", dt); + } + rc = read_dev_chars (info->info.irq, + (void *) &(info->rdc_data), ct); + if (rc) { + PRINT_WARN ("RDC resulted in rc=%d\n", rc); + } + FUNCTION_EXIT ("read_characteristics"); + return rc; +} + +/* How many sectors must be in a request to dequeue it ? */ +#define QUEUE_BLOCKS 100 +#define QUEUE_SECTORS (QUEUE_BLOCKS << dasd_info[di]->sizes.s2b_shift) + +/* How often to retry an I/O before raising an error */ +#define DASD_MAX_RETRIES 5 + +static atomic_t bh_scheduled = ATOMIC_INIT (0); + +static inline void +schedule_bh (void (*func) (void)) +{ + static struct tq_struct dasd_tq = + {0,}; + /* Protect against rescheduling, when already running */ + if (atomic_compare_and_swap (0, 1, &bh_scheduled)) + return; + dasd_tq.routine = (void *) (void *) func; + queue_task (&dasd_tq, &tq_immediate); + mark_bh (IMMEDIATE_BH); + return; +} + +static inline + cqr_t * +dasd_cqr_from_req (struct request *req) +{ + cqr_t *cqr = NULL; + int di; + dasd_information_t *info; + + if (!req) { + PRINT_ERR ("No request passed!"); + return NULL; + } + di = DEVICE_NR (req->rq_dev); + info = dasd_info[di]; + if (!info) + return NULL; + /* if applicable relocate block */ + if (MINOR (req->rq_dev) & 0x1) { + req->sector += info->sizes.first_sector; + } + /* Now check for consistency */ + if (!req->nr_sectors) { + PRINT_WARN ("req: %p dev: %08x sector: %ld nr_sectors: %ld bh: %p\n", + req, req->rq_dev, req->sector, req->nr_sectors, req->bh); + return NULL; + } + if (((req->sector + req->nr_sectors) >> 1) > info->sizes.kbytes) { + printk (KERN_ERR PRINTK_HEADER + "Requesting I/O past end of device %d\n", + di); + return NULL; + } + cqr = dasd_disciplines[info->type]->get_req_ccw (di, req); + if (!cqr) { + PRINT_WARN ("empty CQR generated\n"); + } else { + cqr->req = req; + cqr->int4cqr = cqr; + cqr->devindex = di; +#ifdef DASD_PROFILE + asm volatile ("STCK %0":"=m" (cqr->buildclk)); +#endif /* DASD_PROFILE */ + if (atomic_compare_and_swap (CQR_STATUS_EMPTY, + CQR_STATUS_FILLED, + &cqr->status)) { + PRINT_WARN ("cqr from req stat changed %d\n", + atomic_read (&cqr->status)); + } + } + return cqr; +} + +int +dasd_start_IO (cqr_t * cqr) +{ + int rc = 0; + int retries = DASD_SSCH_RETRIES; + int di, irq; + + if (!cqr) { + PRINT_WARN ("(start_IO) no cqr passed\n"); + return -EINVAL; + } + if (cqr->magic != DASD_MAGIC) { + PRINT_WARN ("(start_IO) magic number mismatch\n"); + return -EINVAL; + } + if (atomic_compare_and_swap (CQR_STATUS_QUEUED, + CQR_STATUS_IN_IO, + &cqr->status)) { + PRINT_WARN ("start_IO: status changed %d\n", + atomic_read (&cqr->status)); + atomic_set (&cqr->status, CQR_STATUS_ERROR); + return -EINVAL; + } + di = cqr->devindex; + irq = dasd_info[di]->info.irq; + do { + asm volatile ("STCK %0":"=m" (cqr->startclk)); + rc = do_IO (irq, cqr->cpaddr, (long) cqr, 0x00, cqr->options); + switch (rc) { + case 0: + if (!(cqr->options & DOIO_WAIT_FOR_INTERRUPT)) + atomic_set_mask (DASD_CHANQ_BUSY, + &dasd_info[di]->queue.flags); + break; + case -ENODEV: + PRINT_WARN ("cqr %p: 0x%04x error, %d retries left\n", + cqr, dasd_info[di]->info.devno, retries); + break; + case -EIO: + PRINT_WARN ("cqr %p: 0x%04x I/O, %d retries left\n", + cqr, dasd_info[di]->info.devno, retries); + break; + case -EBUSY: /* set up timer, try later */ + PRINT_WARN ("cqr %p: 0x%04x busy, %d retries left\n", + cqr, dasd_info[di]->info.devno, retries); + break; + default: + PRINT_WARN ("cqr %p: 0x%04x %d, %d retries left\n", + cqr, rc, dasd_info[di]->info.devno, + retries); + break; + } + } while (rc && --retries); + if (rc) { + if (atomic_compare_and_swap (CQR_STATUS_IN_IO, + CQR_STATUS_ERROR, + &cqr->status)) { + PRINT_WARN ("start_IO:(done) status changed %d\n", + atomic_read (&cqr->status)); + atomic_set (&cqr->status, CQR_STATUS_ERROR); + } + } + return rc; +} + +static inline +void +dasd_end_cqr (cqr_t * cqr, int uptodate) +{ + struct request *req = cqr->req; + asm volatile ("STCK %0":"=m" (cqr->endclk)); +#ifdef DASD_PROFILE + dasd_profile_add (cqr); +#endif /* DASD_PROFILE */ + dasd_chanq_deq (&dasd_info[cqr->devindex]->queue, cqr); + if (req) { + dasd_end_request (req, uptodate); + } +} + +void +dasd_dump_sense (devstat_t * stat) +{ + int sl, sct; + printk (KERN_INFO PRINTK_HEADER + "-------------------I/O Error-----------------------------\n"); + for (sl = 0; sl < 4; sl++) { + printk (KERN_INFO PRINTK_HEADER "Sense:"); + for (sct = 0; sct < 8; sct++) { + printk (" %2d:0x%02X", 8 * sl + sct, + stat->ii.sense.data[8 * sl + sct]); + } + printk ("\n"); + } +} + +void +dasd_do_chanq (void) +{ + dasd_chanq_t *qp = NULL; + cqr_t *cqr; + long flags; + int irq,di; + int tasks; + atomic_set (&bh_scheduled, 0); + dasd_debug (0x44445f69); /* DD_i */ + while ((tasks = atomic_read(&chanq_tasks)) != 0) { +/* initialization and wraparound */ + if (qp == NULL) { + dasd_debug (0x44445f68); /* DD_h */ + qp = cq_head; + if (!qp) { + dasd_debug (0x44445f45); /* DD_E */ + dasd_debug (tasks); + PRINT_ERR("Mismatch of NULL queue pointer and " + "still %d chanq_tasks to do!!\n" + "Please send output of /proc/dasd/debug " + "to Linux390@de.ibm.com\n", tasks); + atomic_set(&chanq_tasks,0); + break; + } + } +/* Get the irq number: Ouch, FIXME!!!!! */ +#if 0 + di =(((unsigned long)qp)- + ((unsigned long)&(dasd_info[0]->queue))) + / sizeof(dasd_information_t); + dasd_debug(di); + irq = dasd_info[di]->info.irq; + dasd_debug(irq); +#endif +/* Get first request */ + cqr = (cqr_t *) (qp->head); + dasd_debug ((unsigned long) cqr); /* cqr */ +/* empty queue -> dequeue and proceed */ + if (!cqr) { + dasd_chanq_t *nqp = qp->next_q; + cql_deq (qp); + qp = nqp; + continue; + } +/* process all requests on that queue */ + do { + cqr_t *next; + dasd_debug (0x44445f64); /* DD_d */ + dasd_debug ((unsigned long) cqr); /* cqr */ + if (cqr->magic != DASD_MAGIC) { + dasd_debug (0x44445f65); /* DD_e */ + PRINT_WARN ("do_cq:magic mismatch %p\n", cqr); + break; + } + irq = dasd_info[cqr->devindex]->info.irq; + s390irq_spin_lock_irqsave (irq, flags); + switch (atomic_read (&cqr->status)) { + case CQR_STATUS_IN_IO: + dasd_debug (0x44445f48); /* DD_I */ + cqr = NULL; + break; + case CQR_STATUS_QUEUED: + dasd_debug (0x44445f53); /* DD_S */ + if (dasd_start_IO (cqr) == 0) { + atomic_dec (&chanq_tasks); + cqr = NULL; + } + break; + case CQR_STATUS_ERROR: + dasd_debug (0x44445f54); /* DD_E */ + dasd_dump_sense (cqr->dstat); + if (cqr->retries++ < 5) { + atomic_set (&cqr->status, + CQR_STATUS_QUEUED); + if (dasd_start_IO (cqr) == 0) { + atomic_dec (&chanq_tasks); + cqr = NULL; + } + } else { + atomic_set (&cqr->status, + CQR_STATUS_FAILED); + } + break; + case CQR_STATUS_DONE: + next = cqr->next; + dasd_debug (0x44445f44); /* DD_D */ + dasd_end_cqr (cqr, 1); + atomic_dec (&chanq_tasks); + cqr = next; + break; + case CQR_STATUS_FAILED: + next = cqr->next; + dasd_debug (0x44445f45); /* DD_F */ + dasd_end_cqr (cqr, 0); + atomic_dec (&chanq_tasks); + cqr = next; + break; + default: + PRINT_WARN ("unknown cqrstatus\n"); + cqr = NULL; + } + s390irq_spin_unlock_irqrestore (irq, flags); + } while (cqr); + qp = qp->next_q; + } + spin_lock (&io_request_lock); + do_dasd_request (); + spin_unlock (&io_request_lock); + dasd_debug (0x44445f6f); /* DD_o */ +} + +/* + The request_fn is called from ll_rw_blk for any new request. + We use it to feed the chanqs. + This implementation assumes we are serialized by the io_request_lock. + */ +void +do_dasd_request (void) +{ + struct request *req; + struct request *prev; + struct request *next; + char broken[DASD_MAX_DEVICES] = {0,}; + char busy[DASD_MAX_DEVICES] = {0,}; + int di; + cqr_t *cqr; + long caller; + dasd_chanq_t *q; + long flags; + int irq; + + __asm__ ("lr %0,14":"=d" (caller)); + dasd_debug (0x44525f69); /* DR_i */ + dasd_debug ((unsigned long) caller); /* calleraddres */ + prev = NULL; + for (req = CURRENT; req != NULL; req = next) { + next = req->next; + di = DEVICE_NR (req->rq_dev); + dasd_debug ((unsigned long) req); /* req */ + dasd_debug (0x44520000 + /* DR## */ + ((((di/16)<9?(di/16)+'0':(di/16)+'a'))<<8) + + (((di%16)<9?(di%16)+'0':(di%16)+'a'))); + irq = dasd_info[di]->info.irq; + s390irq_spin_lock_irqsave (irq, flags); + q = &dasd_info[di]->queue; + busy[di] = busy[di]||(atomic_read(&q->flags)&DASD_CHANQ_BUSY); + if ( !busy[di] || + ((!broken[di]) && (req->nr_sectors >= QUEUE_SECTORS))) { + if (prev) { + prev->next = next; + } else { + CURRENT = next; + } + req->next = NULL; + if (req == &blk_dev[MAJOR_NR].plug) { + dasd_debug (0x44525f75); /* DR_u */ + goto cont; + } + cqr = dasd_cqr_from_req (req); + if (!cqr) { + dasd_debug (0x44525f65); /* DR_e */ + dasd_end_request (req, 0); + goto cont; + } + dasd_debug (0x44525f71); /* DR_q */ + dasd_debug ((unsigned long) cqr); /* cqr */ + dasd_chanq_enq (q, cqr); + if (!(atomic_read (&q->flags) & DASD_CHANQ_ACTIVE)) { + cql_enq_head (q); + } + if (!busy[di]) { + if (dasd_start_IO (cqr) == 0) + dasd_debug (0x44525f73); /* DR_s */ + else { + atomic_inc (&chanq_tasks); + schedule_bh (dasd_do_chanq); + busy[di] = 1; + } + } + } else { + dasd_debug (0x44525f62); /* DR_b */ + broken[di] = 1; + prev = req; + } + cont: + s390irq_spin_unlock_irqrestore (irq, flags); + } + dasd_debug (0x44525f6f); /* DR_o */ +} + +void +dasd_handler (int irq, void *ds, struct pt_regs *regs) +{ + devstat_t *stat = (devstat_t *) ds; + int ip; + cqr_t *cqr; + int done_fast_io = 0; + + dasd_debug (0x44485f69); /* DH_i */ + if (!stat) + PRINT_ERR ("handler called without devstat"); + ip = stat->intparm; + dasd_debug (ip); /* intparm */ + switch (ip) { /* filter special intparms... */ + case 0x00000000: /* no intparm: unsolicited interrupt */ + dasd_debug (0x44485f30); /* DH_0 */ + PRINT_INFO ("Unsolicited interrupt on device %04X\n", + stat->devno); + dasd_dump_sense (stat); + return; + default: + if (ip & 0x80000001) { + PRINT_INFO ("Spurious interrupt %08x on device %04X\n", + ip, stat->devno); + return; + } + cqr = (cqr_t *) ip; + if (cqr->magic != DASD_MAGIC) { + dasd_debug (0x44485f65); /* DH_e */ + PRINT_WARN ("handler:magic mismatch on %p %08x\n", + cqr, cqr->magic); + return; + } + asm volatile ("STCK %0":"=m" (cqr->stopclk)); + if (stat->cstat == 0x00 && stat->dstat == 0x0c) { + dasd_debug (0x44486f6b); /* DHok */ + if (atomic_compare_and_swap (CQR_STATUS_IN_IO, + CQR_STATUS_DONE, + &cqr->status)) { + PRINT_WARN ("handler: cqrstat changed%d\n", + atomic_read (&cqr->status)); + } + if (cqr->next) { + dasd_debug (0x44485f6e); /* DH_n */ + if (dasd_start_IO (cqr->next) == 0) + done_fast_io = 1; + } + break; + } + dasd_debug (0x44482121); /* DH!! */ + if (!cqr->dstat) + cqr->dstat = kmalloc (sizeof (devstat_t), + GFP_ATOMIC); + if (cqr->dstat) { + memcpy (cqr->dstat, stat, sizeof (devstat_t)); + dasd_dump_sense (cqr->dstat); + } else { + PRINT_ERR ("no memory for dtstat\n"); + } + /* errorprocessing */ + if (cqr->retries < DASD_MAX_RETRIES) { + dasd_debug (0x44485f72); /* DH_r */ + atomic_set (&cqr->status, CQR_STATUS_ERROR); + } else { + dasd_debug (0x44485f6e); /* DH_f */ + atomic_set (&cqr->status, CQR_STATUS_FAILED); + } + + } + if (done_fast_io == 0) + atomic_clear_mask (DASD_CHANQ_BUSY, + &dasd_info[cqr->devindex]-> + queue.flags); + + if (cqr->flags & DASD_DO_IO_SLEEP) { + dasd_debug (0x44485f77); /* DH_w */ + dasd_wakeup (); + } else if (! (cqr->options & DOIO_WAIT_FOR_INTERRUPT) ){ + dasd_debug (0x44485f73); /* DH_s */ + atomic_inc (&chanq_tasks); + schedule_bh (dasd_do_chanq); + } else { + dasd_debug (cqr->flags); /* DH_g */ + dasd_debug (0x44485f6f); /* DH_g */ + } +} + +static int +register_dasd (int irq, dasd_type_t dt, dev_info_t * info) +{ + int rc = 0; + int di; + static spinlock_t register_lock = SPIN_LOCK_UNLOCKED; + spin_lock (®ister_lock); + FUNCTION_ENTRY ("register_dasd"); + di = devindex_from_devno (info->devno); + if (di < 0) { + INTERNAL_CHECK ("Can't get index for devno %d\n", info->devno); + return -ENODEV; + } + if (dasd_info[di]) { /* devindex is not free */ + INTERNAL_CHECK ("reusing allocated deviceindex %d\n", di); + return -ENODEV; + } + dasd_info[di] = (dasd_information_t *) + kmalloc (sizeof (dasd_information_t), GFP_ATOMIC); + if (dasd_info[di] == NULL) { + PRINT_WARN ("No memory for dasd_info_t on irq %d\n", irq); + return -ENOMEM; + } + memset (dasd_info[di], 0, sizeof (dasd_information_t)); + memcpy (&(dasd_info[di]->info), info, sizeof (dev_info_t)); + spin_lock_init (&dasd_info[di]->queue.f_lock); + spin_lock_init (&dasd_info[di]->queue.q_lock); + atomic_set (&dasd_info[di]->queue.flags, 0); + dasd_info[di]->type = dt; + dasd_info[di]->irq = irq; + dasd_info[di]->sem = MUTEX; + rc = dasd_read_characteristics (dasd_info[di]); + if (rc) { + PRINT_WARN ("RDC returned error %d\n", rc); + rc = -ENODEV; + goto unalloc; + } +#if DASD_PARANOIA > 1 + if (dasd_disciplines[dt]->ck_characteristics) +#endif /* DASD_PARANOIA */ + rc = dasd_disciplines[dt]-> + ck_characteristics (dasd_info[di]->rdc_data); + + if (rc) { + INTERNAL_CHECK ("Discipline returned non-zero when" + "checking device characteristics%s\n", ""); + rc = -ENODEV; + goto unalloc; + } + rc = request_irq (irq, dasd_handler, 0, "dasd", + &(dasd_info[di]->dev_status)); + if (rc) { +#if DASD_PARANOIA > 0 + printk (KERN_WARNING PRINTK_HEADER + "Cannot register irq %d, rc=%d\n", + irq, rc); +#endif /* DASD_DEBUG */ + rc = -ENODEV; + goto unalloc; + } +#if DASD_PARANOIA > 1 + if (!dasd_disciplines[dt]->fill_sizes) { + INTERNAL_CHECK ("No fill_sizes for dt=%d\n", dt); + goto unregister; + } +#endif /* DASD_PARANOIA */ + fill_sizes (di); + goto exit; + + unregister: + free_irq (irq, &(dasd_info[di]->dev_status)); + unalloc: + kfree (dasd_info[di]); + exit: + spin_unlock (®ister_lock); + FUNCTION_EXIT ("register_dasd"); + return rc; +} + +static int +probe_for_dasd (int irq) +{ + int rc; + dev_info_t info; + dasd_type_t dt; + + FUNCTION_ENTRY ("probe_for_dasd"); + + rc = get_dev_info_by_irq (irq, &info); + if (rc == -ENODEV) { /* end of device list */ + return rc; + } +#if DASD_PARANOIA > 2 + if (rc) { + INTERNAL_CHECK ("unknown rc %d of get_dev_info", rc); + return rc; + } +#endif /* DASD_PARANOIA */ + if ((info.status & DEVSTAT_NOT_OPER)) { + return -ENODEV; + } + dt = check_type (&info); + switch (dt) { +#ifdef CONFIG_DASD_ECKD + case dasd_eckd: +#endif /* CONFIG_DASD_ECKD */ + FUNCTION_CONTROL ("Probing devno %d...\n", info.devno); + if (!dasd_is_accessible (info.devno)) { + FUNCTION_CONTROL ("out of range...skip%s\n", ""); + return -ENODEV; + } + if (dasd_disciplines[dt]->ck_devinfo) { + rc = dasd_disciplines[dt]->ck_devinfo (&info); + } +#if DASD_PARANOIA > 1 + else { + INTERNAL_ERROR ("no ck_devinfo function%s\n", ""); + return -ENODEV; + } +#endif /* DASD_PARANOIA */ + if (rc == -ENODEV) { + return rc; + } +#if DASD_PARANOIA > 2 + if (rc) { + INTERNAL_CHECK ("unknown error rc=%d\n", rc); + return -ENODEV; + } +#endif /* DASD_PARANOIA */ + rc = register_dasd (irq, dt, &info); + if (rc) { + PRINT_INFO ("devno %x not enabled as minor %d due to errors\n", + info.devno, + devindex_from_devno (info.devno) << + PARTN_BITS); + } else { + PRINT_INFO ("devno %x added as minor %d (%s)\n", + info.devno, + devindex_from_devno (info.devno) << PARTN_BITS, + dasd_name[dt]); + } + case dasd_none: + break; + default: + PRINT_DEBUG ("unknown device type\n"); + break; + } + FUNCTION_EXIT ("probe_for_dasd"); + return rc; +} + +static int +register_major (int major) +{ + int rc = 0; + + FUNCTION_ENTRY ("register_major"); + rc = register_blkdev (major, DASD_NAME, &dasd_file_operations); +#if DASD_PARANOIA > 1 + if (rc) { + PRINT_WARN ("registering major -> rc=%d aborting... \n", rc); + return rc; + } +#endif /* DASD_PARANOIA */ + blk_dev[major].request_fn = do_dasd_request; + FUNCTION_CONTROL ("successfully registered major: %d\n", major); + FUNCTION_EXIT ("register_major"); + return rc; +} + +/* + Below you find functions which are called from outside. Some of them may be + static, because they are called by their function pointers only. Thus static + modifier is to make sure, that they are only called via the kernel's methods + */ + +static ssize_t +dasd_read (struct file *filp, char *b, size_t s, loff_t * o) +{ + ssize_t rc; + FUNCTION_ENTRY ("dasd_read"); + rc = block_read (filp, b, s, o); + FUNCTION_EXIT ("dasd_read"); + return rc; +} +static ssize_t +dasd_write (struct file *filp, const char *b, size_t s, loff_t * o) +{ + ssize_t rc; + FUNCTION_ENTRY ("dasd_write"); + rc = block_write (filp, b, s, o); + FUNCTION_EXIT ("dasd_write"); + return rc; +} +static int +dasd_ioctl (struct inode *inp, struct file *filp, + unsigned int no, unsigned long data) +{ + int rc = 0; + FUNCTION_ENTRY ("dasd_ioctl"); + if ((!inp) || !(inp->i_rdev)) { + return -EINVAL; + } + rc = do_dasd_ioctl (inp, no, data); + FUNCTION_EXIT ("dasd_ioctl"); + return rc; +} + +static int +dasd_open (struct inode *inp, struct file *filp) +{ + int rc = 0; + dasd_information_t *dev; + FUNCTION_ENTRY ("dasd_open"); + if ((!inp) || !(inp->i_rdev)) { + return -EINVAL; + } + dev = dasd_info[DEVICE_NR (inp->i_rdev)]; + if (!dev) { + PRINT_DEBUG ("No device registered as %d (%d)\n", + inp->i_rdev, DEVICE_NR (inp->i_rdev)); + return -EINVAL; + } + down (&dev->sem); + up (&dev->sem); +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif /* MODULE */ +#if DASD_PARANOIA > 2 + if (dev->open_count < 0) { + INTERNAL_ERROR ("open count cannot be less than 0: %d", + dev->open_count); + return -EINVAL; + } +#endif /* DASD_PARANOIA */ + dev->open_count++; + FUNCTION_EXIT ("dasd_open"); + return rc; +} + +static int +dasd_fsync (struct file *filp, struct dentry *d) +{ + int rc = 0; + FUNCTION_ENTRY ("dasd_fsync"); + if (!filp) { + return -EINVAL; + } + rc = block_fsync (filp, d); + FUNCTION_EXIT ("dasd_fsync"); + return rc; +} + +static int +dasd_release (struct inode *inp, struct file *filp) +{ + int rc = 0; + dasd_information_t *dev; + FUNCTION_ENTRY ("dasd_release"); + if ((!inp) || !(inp->i_rdev)) { + return -EINVAL; + } + dev = dasd_info[DEVICE_NR (inp->i_rdev)]; + if (!dev) { + PRINT_WARN ("No device registered as %d\n", inp->i_rdev); + return -EINVAL; + } +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif /* MODULE */ +#if DASD_PARANOIA > 2 + if (!dev->open_count) { + PRINT_WARN ("device %d has not been opened before:\n", + inp->i_rdev); + } +#endif /* DASD_PARANOIA */ + dev->open_count--; +#if DASD_PARANOIA > 2 + if (dev->open_count < 0) { + INTERNAL_ERROR ("open count cannot be less than 0: %d", + dev->open_count); + return -EINVAL; + } +#endif /* DASD_PARANOIA */ + FUNCTION_EXIT ("dasd_release"); + return rc; +} + +static struct +file_operations dasd_file_operations = +{ + NULL, /* loff_t(*llseek)(struct file *,loff_t,int); */ + dasd_read, + dasd_write, + NULL, /* int(*readdir)(struct file *,void *,filldir_t); */ + NULL, /* u int(*poll)(struct file *,struct poll_table_struct *); */ + dasd_ioctl, + NULL, /* int (*mmap) (struct file *, struct vm_area_struct *); */ + dasd_open, + NULL, /* int (*flush) (struct file *) */ + dasd_release, + dasd_fsync, + NULL, /* int (*fasync) (int, struct file *, int); + int (*check_media_change) (kdev_t dev); + int (*revalidate) (kdev_t dev); + int (*lock) (struct file *, int, struct file_lock *); */ +}; + +int +dasd_init (void) +{ + int rc = 0; + int i; + + FUNCTION_ENTRY ("dasd_init"); + PRINT_INFO ("initializing...\n"); + atomic_set (&chanq_tasks, 0); + atomic_set (&bh_scheduled, 0); + spin_lock_init (&dasd_lock); + /* First register to the major number */ + rc = register_major (MAJOR_NR); +#if DASD_PARANOIA > 1 + if (rc) { + PRINT_WARN ("registering major_nr returned rc=%d\n", rc); + return rc; + } +#endif /* DASD_PARANOIA */ + read_ahead[MAJOR_NR] = 8; + blk_size[MAJOR_NR] = dasd_blks; + hardsect_size[MAJOR_NR] = dasd_secsize; + blksize_size[MAJOR_NR] = dasd_blksize; + max_sectors[MAJOR_NR] = dasd_maxsecs; +#ifdef CONFIG_PROC_FS + dasd_proc_init (); +#endif /* CONFIG_PROC_FS */ + /* Now scan the device list for DASDs */ + FUNCTION_CONTROL ("entering detection loop%s\n", ""); + for (i = 0; i < NR_IRQS; i++) { + int irc; /* Internal return code */ + LOOP_CONTROL ("Probing irq %d...\n", i); + irc = probe_for_dasd (i); + switch (irc) { + case 0: + LOOP_CONTROL ("Added DASD%s\n", ""); + break; + case -ENODEV: + LOOP_CONTROL ("No DASD%s\n", ""); + break; + case -EMEDIUMTYPE: + PRINT_WARN ("DASD not formatted%s\n", ""); + break; + default: + INTERNAL_CHECK ("probe_for_dasd: unknown rc=%d", irc); + break; + } + } + FUNCTION_CONTROL ("detection loop completed%s\n", ""); +/* Finally do the genhd stuff */ +#if 0 /* 2 b done */ + dd_gendisk.next = gendisk_head; + gendisk_head = &dd_gendisk; + for (i = 0; i < DASD_MAXDEVICES; i++) { + LOOP_CONTROL ("Setting partitions of DASD %d\n", i); + resetup_one_dev (&dd_gendisk, i); + } +#endif /* 0 */ + FUNCTION_EXIT ("dasd_init"); + return rc; +} + +#ifdef MODULE +int +init_module (void) +{ + int rc = 0; + + FUNCTION_ENTRY ("init_module"); + PRINT_INFO ("trying to load module\n"); + rc = dasd_init (); + if (rc == 0) { + PRINT_INFO ("module loaded successfully\n"); + } else { + PRINT_WARN ("warning: Module load returned rc=%d\n", rc); + } + FUNCTION_EXIT ("init_module"); + return rc; +} + +void +cleanup_module (void) +{ + int rc = 0; + + FUNCTION_ENTRY ("cleanup_module"); + PRINT_INFO ("trying to unload module \n"); + + /* FIXME: replace by proper unload functionality */ + INTERNAL_ERROR ("Modules not yet implemented %s", ""); + + if (rc == 0) { + PRINT_INFO ("module unloaded successfully\n"); + } else { + PRINT_WARN ("module unloaded with errors\n"); + } + FUNCTION_EXIT ("cleanup_module"); +} +#endif /* MODULE */ + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-indent-level: 4 + * c-brace-imaginary-offset: 0 + * c-brace-offset: -4 + * c-argdecl-indent: 4 + * c-label-offset: -4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: 0 + * indent-tabs-mode: nil + * tab-width: 8 + * End: + */ diff -u --recursive --new-file v2.2.13/linux/drivers/s390/block/dasd.h linux/drivers/s390/block/dasd.h --- v2.2.13/linux/drivers/s390/block/dasd.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/block/dasd.h Tue Jan 4 10:12:18 2000 @@ -0,0 +1,226 @@ + +#ifndef DASD_H +#define DASD_H + +/* First of all the external stuff */ +#include +#include + +#define IOCTL_LETTER 'D' +#define BIODASDFORMAT _IO(IOCTL_LETTER,0) /* Format the volume or an extent */ +#define BIODASDDISABLE _IO(IOCTL_LETTER,1) /* Disable the volume (for Linux) */ +#define BIODASDENABLE _IO(IOCTL_LETTER,2) /* Enable the volume (for Linux) */ +/* Stuff for reading and writing the Label-Area to/from user space */ +#define BIODASDGTVLBL _IOR(IOCTL_LETTER,3,dasd_volume_label_t) +#define BIODASDSTVLBL _IOW(IOCTL_LETTER,4,dasd_volume_label_t) +#define BIODASDRWTB _IOWR(IOCTL_LETTER,5,int) +#define BIODASDRSID _IOR(IOCTL_LETTER,6,senseid_t) + +typedef +union { + char bytes[512]; + struct { + /* 80 Bytes of Label data */ + char identifier[4]; /* e.g. "LNX1", "VOL1" or "CMS1" */ + char label[6]; /* Given by user */ + char security; + char vtoc[5]; /* Null in "LNX1"-labelled partitions */ + char reserved0[5]; + long ci_size; + long blk_per_ci; + long lab_per_ci; + char reserved1[4]; + char owner[0xe]; + char no_part; + char reserved2[0x1c]; + /* 16 Byte of some information on the dasd */ + short blocksize; + char nopart; + char unused; + long unused2[3]; + /* 7*10 = 70 Bytes of partition data */ + struct { + char type; + long start; + long size; + char unused; + } part[7]; + } __attribute__ ((packed)) label; +} dasd_volume_label_t; + +typedef union { + struct { + unsigned long no; + unsigned int ct; + } __attribute__ ((packed)) input; + struct { + unsigned long noct; + } __attribute__ ((packed)) output; +} __attribute__ ((packed)) dasd_xlate_t; + +void dasd_setup (char *, int *); +int dasd_init (void); +#ifdef MODULE +int init_module (void); +void cleanup_module (void); +#endif /* MODULE */ + +/* Definitions for blk.h */ +/* #define DASD_MAGIC 0x44415344 is ascii-"DASD" */ +/* #define dasd_MAGIC 0x64617364; is ascii-"DASD" */ +#define DASD_MAGIC 0xC4C1E2C4 /* is ebcdic-"DASD" */ +#define dasd_MAGIC 0x8481A284 /* is ebcdic-"DASD" */ +#define DASD_NAME "dasd" +#define DASD_PARTN_BITS 2 +#define DASD_MAX_DEVICES (256>>DASD_PARTN_BITS) + +#define MAJOR_NR DASD_MAJOR +#define PARTN_BITS DASD_PARTN_BITS + +#ifdef __KERNEL__ +/* Now lets turn to the internal sbtuff */ + +/* + define the debug levels: + - 0 No debugging output to console or syslog + - 1 Log internal errors to syslog, ignore check conditions + - 2 Log internal errors and check conditions to syslog + - 3 Log internal errors to console, log check conditions to syslog + - 4 Log internal errors and check conditions to console + - 5 panic on internal errors, log check conditions to console + - 6 panic on both, internal errors and check conditions + */ +#define DASD_DEBUG 4 + +#define DASD_PROFILE +/* + define the level of paranoia + - 0 quite sure, that things are going right + - 1 sanity checking, only to avoid panics + - 2 normal sanity checking + - 3 extensive sanity checks + - 4 exhaustive debug messages + */ +#define DASD_PARANOIA 2 + +/* + define the depth of flow control, which is logged as a check condition + - 0 No flow control messages + - 1 Entry of functions logged like check condition + - 2 Entry and exit of functions logged like check conditions + - 3 Internal structure broken down + - 4 unrolling of loops,... + */ +#define DASD_FLOW_CONTROL 0 + +#if DASD_DEBUG > 0 +#define PRINT_DEBUG(x...) printk ( KERN_DEBUG PRINTK_HEADER x ) +#define PRINT_INFO(x...) printk ( KERN_INFO PRINTK_HEADER x ) +#define PRINT_WARN(x...) printk ( KERN_WARNING PRINTK_HEADER x ) +#define PRINT_ERR(x...) printk ( KERN_ERR PRINTK_HEADER x ) +#define PRINT_FATAL(x...) panic ( PRINTK_HEADER x ) +#else +#define PRINT_DEBUG(x...) printk ( KERN_DEBUG PRINTK_HEADER x ) +#define PRINT_INFO(x...) printk ( KERN_DEBUG PRINTK_HEADER x ) +#define PRINT_WARN(x...) printk ( KERN_DEBUG PRINTK_HEADER x ) +#define PRINT_ERR(x...) printk ( KERN_DEBUG PRINTK_HEADER x ) +#define PRINT_FATAL(x...) printk ( KERN_DEBUG PRINTK_HEADER x ) +#endif /* DASD_DEBUG */ + +#define INTERNAL_ERRMSG(x,y...) \ +"Internal error: in file " __FILE__ " line: %d: " x, __LINE__, y +#define INTERNAL_CHKMSG(x,y...) \ +"Inconsistency: in file " __FILE__ " line: %d: " x, __LINE__, y +#define INTERNAL_FLWMSG(x,y...) \ +"Flow control: file " __FILE__ " line: %d: " x, __LINE__, y + +#if DASD_DEBUG > 4 +#define INTERNAL_ERROR(x...) PRINT_FATAL ( INTERNAL_ERRMSG ( x ) ) +#elif DASD_DEBUG > 2 +#define INTERNAL_ERROR(x...) PRINT_ERR ( INTERNAL_ERRMSG ( x ) ) +#elif DASD_DEBUG > 0 +#define INTERNAL_ERROR(x...) PRINT_WARN ( INTERNAL_ERRMSG ( x ) ) +#else +#define INTERNAL_ERROR(x...) +#endif /* DASD_DEBUG */ + +#if DASD_DEBUG > 5 +#define INTERNAL_CHECK(x...) PRINT_FATAL ( INTERNAL_CHKMSG ( x ) ) +#elif DASD_DEBUG > 3 +#define INTERNAL_CHECK(x...) PRINT_ERR ( INTERNAL_CHKMSG ( x ) ) +#elif DASD_DEBUG > 1 +#define INTERNAL_CHECK(x...) PRINT_WARN ( INTERNAL_CHKMSG ( x ) ) +#else +#define INTERNAL_CHECK(x...) +#endif /* DASD_DEBUG */ + +#if DASD_DEBUG > 3 +#define INTERNAL_FLOW(x...) PRINT_ERR ( INTERNAL_FLWMSG ( x ) ) +#elif DASD_DEBUG > 2 +#define INTERNAL_FLOW(x...) PRINT_WARN ( INTERNAL_FLWMSG ( x ) ) +#else +#define INTERNAL_FLOW(x...) +#endif /* DASD_DEBUG */ + +#if DASD_FLOW_CONTROL > 0 +#define FUNCTION_ENTRY(x) INTERNAL_FLOW( x "entered %s\n","" ); +#else +#define FUNCTION_ENTRY(x) +#endif /* DASD_FLOW_CONTROL */ + +#if DASD_FLOW_CONTROL > 1 +#define FUNCTION_EXIT(x) INTERNAL_FLOW( x "exited %s\n","" ); +#else +#define FUNCTION_EXIT(x) +#endif /* DASD_FLOW_CONTROL */ + +#if DASD_FLOW_CONTROL > 2 +#define FUNCTION_CONTROL(x...) INTERNAL_FLOW( x ); +#else +#define FUNCTION_CONTROL(x...) +#endif /* DASD_FLOW_CONTROL */ + +#if DASD_FLOW_CONTROL > 3 +#define LOOP_CONTROL(x...) INTERNAL_FLOW( x ); +#else +#define LOOP_CONTROL(x...) +#endif /* DASD_FLOW_CONTROL */ + +#define DASD_DO_IO_SLEEP 0x01 +#define DASD_DO_IO_NOLOCK 0x02 +#define DASD_DO_IO_NODEC 0x04 + +#define DASD_NOT_FORMATTED 0x01 + +extern struct wait_queue *dasd_waitq; + +#undef DEBUG_DASD_MALLOC +#ifdef DEBUG_DASD_MALLOC +void *b; +#define kmalloc(x...) (PRINT_INFO(" kmalloc %p\n",b=kmalloc(x)),b) +#define kfree(x) PRINT_INFO(" kfree %p\n",x);kfree(x) +#define get_free_page(x...) (PRINT_INFO(" gfp %p\n",b=get_free_page(x)),b) +#define __get_free_pages(x...) (PRINT_INFO(" gfps %p\n",b=__get_free_pages(x)),b) +#endif /* DEBUG_DASD_MALLOC */ + +#endif /* __KERNEL__ */ +#endif /* DASD_H */ + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-indent-level: 4 + * c-brace-imaginary-offset: 0 + * c-brace-offset: -4 + * c-argdecl-indent: 4 + * c-label-offset: -4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: 0 + * indent-tabs-mode: nil + * tab-width: 8 + * End: + */ diff -u --recursive --new-file v2.2.13/linux/drivers/s390/block/dasd_ccwstuff.c linux/drivers/s390/block/dasd_ccwstuff.c --- v2.2.13/linux/drivers/s390/block/dasd_ccwstuff.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/block/dasd_ccwstuff.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,424 @@ +/* + * File...........: linux/drivers/s390/block/dasd_ccwstuff.c + * Author(s)......: Holger Smolinski + * Bugreports.to..: + * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999 + */ + +#include +#include +#include +#include +#include + +#include "dasd.h" +#include "dasd_types.h" + +#define PRINTK_HEADER "dasd_ccw:" +#define MAX_CP_POWER 9 /* Maximum allowed index */ +#define CP_PER_PAGE_POWER 9 /* Maximum index, fitting on page */ + +#define get_free_pages __get_free_pages + +/* Stuff for the handling task_list */ +dasd_chanq_t *cq_head = NULL; /* head of task_list */ +atomic_t chanq_tasks; + +/* Array of freelists for the channel programs' -space */ +static ccw1_t *ccwarea[CP_PER_PAGE_POWER + 1] = +{NULL,}; + +/* array of pages retrieved for internal use */ +#define MAX_DASD_PAGES 64 +static int dasd_page_count = 0; +static long dasd_page[MAX_DASD_PAGES]; + +#ifdef __SMP__ +static spinlock_t ccw_lock=SPIN_LOCK_UNLOCKED; /* spinlock for ccwareas */ +static spinlock_t cq_lock=SPIN_LOCK_UNLOCKED; /* spinlock for cq_head */ +#endif /* __SMP__ */ + +void +ccwarea_enq (int index, ccw1_t * area) +{ + FUNCTION_ENTRY ("ccwarea_enq"); +#if DASD_PARANOIA > 2 + if (!area) { + INTERNAL_CHECK ("zero area %s\n", ""); + } + if (index > CP_PER_PAGE_POWER) { + INTERNAL_CHECK ("index too large %d\n", index); + } +#endif + *(ccw1_t **) area = ccwarea[index]; + ccwarea[index] = area; + FUNCTION_EXIT ("ccwarea_enq"); + return; +} + +ccw1_t * +ccwarea_deq (int index) +{ + ccw1_t *cp; + FUNCTION_ENTRY ("ccwarea_deq"); +#if DASD_PARANOIA > 2 + if (index > CP_PER_PAGE_POWER) { + INTERNAL_CHECK ("index too large %d\n", index); + } +#endif + cp = ccwarea[index]; + ccwarea[index] = *(ccw1_t **) ccwarea[index]; +#if DASD_PARANOIA > 2 + if (!cp) { + INTERNAL_CHECK ("returning NULL %s\n", ""); + } +#endif + FUNCTION_EXIT ("ccwarea_deq"); + return cp; +} + +ccw1_t * +request_cpa (int index) +{ + ccw1_t *freeblk; + FUNCTION_ENTRY ("request_cpa"); + if (index > MAX_CP_POWER) { + INTERNAL_ERROR ("index too large %d\n", index); + freeblk = NULL; + goto exit; + } + if (index > CP_PER_PAGE_POWER) { + int pc = 1 << (index - CP_PER_PAGE_POWER); + do { + freeblk = (ccw1_t *) get_free_pages (GFP_ATOMIC, index - CP_PER_PAGE_POWER); + if (dasd_page_count + pc >= MAX_DASD_PAGES) { + PRINT_WARN ("Requesting too many pages..."); + } else { + int i; + for (i = 0; i < pc; i++) + dasd_page[dasd_page_count++] = + (long) freeblk + i * PAGE_SIZE; + } + FUNCTION_CONTROL ("requesting index %d", index); + if ( ! freeblk ) { + panic ("No memory received\n"); + } + } while (!freeblk); + memset(freeblk,0,PAGE_SIZE<<(index-CP_PER_PAGE_POWER)); + goto exit; + } + while (ccwarea[index] == NULL) { + ccw1_t *blk; + if (index == CP_PER_PAGE_POWER) { + do { + blk = (ccw1_t *) get_free_page (GFP_ATOMIC); + if (dasd_page_count + 1 >= MAX_DASD_PAGES) { + PRINT_WARN ("Requesting too many pages..."); + } else { + dasd_page[dasd_page_count++] = + (long) blk; + } + if (blk == NULL) { + PRINT_WARN ("Can't allocate page!\n"); + } + } while ( ! blk ); + memset(blk,0,PAGE_SIZE); + ccwarea_enq (CP_PER_PAGE_POWER, blk); + continue; + } + blk = request_cpa (index + 1); +#if DASD_PARANOIA > 1 + if (!blk) { + PRINT_WARN ("retrieved NULL"); + } +#endif /* DASD_PARANOIA */ + ccwarea_enq (index, blk); + ccwarea_enq (index, blk + (1 << index)); + } +#if DASD_PARANOIA > 2 + if (!ccwarea[index]) { + INTERNAL_ERROR ("ccwarea is NULL\n%s", ""); + } +#endif /* DASD_PARANOIA */ + + freeblk = ccwarea_deq (index); +#if DASD_PARANOIA > 1 + if (!freeblk) { + INTERNAL_ERROR ("freeblk is NULL\n%s", ""); + } +#endif /* DASD_PARANOIA */ + exit: + FUNCTION_EXIT ("request_cpa"); + return freeblk; +} + +ccw1_t * +request_cp (int size) +{ + ccw1_t *freeblk; + int index; + int blksize; + /* Determine the index of ccwarea to look at */ + for (index = 0, blksize = 1; + size > blksize; + index++, blksize = blksize << 1) { + } + if (index > MAX_CP_POWER) { + INTERNAL_ERROR ("index too large %d\n", index); + } + spin_lock (&ccw_lock); + freeblk = request_cpa (index); + spin_unlock (&ccw_lock); + if (freeblk == NULL) { + printk (KERN_WARNING PRINTK_HEADER + "No way to deliver free ccw space\n"); + } + return freeblk; +} + +void +release_cp (int size, ccw1_t * area) +{ + int index; + int blksize; + /* Determine the index of ccwarea to look at */ + for (index = 0, blksize = 1; + size > blksize; + index++, blksize = blksize << 1) { + } + if (index > MAX_CP_POWER) { + INTERNAL_ERROR ("index too large %d\n", index); + } else if (index > CP_PER_PAGE_POWER) { + free_pages ((unsigned long) area, + index - CP_PER_PAGE_POWER); + INTERNAL_CHECK ("large index used: %d\n", index); + } else { + spin_lock (&ccw_lock); + ccwarea_enq (index, area); + spin_unlock (&ccw_lock); + } + return; +} + +/* ---------------------------------------------------------- */ + +static cqr_t *cqrp = NULL; +#ifdef __SMP__ +static spinlock_t cqr_lock=SPIN_LOCK_UNLOCKED; +#endif /* __SMP__ */ + +void +cqf_enq (cqr_t * cqf) +{ + *(cqr_t **) cqf = cqrp; + cqrp = cqf; +} + +cqr_t * +cqf_deq (void) +{ + cqr_t *cqr = cqrp; + cqrp = *(cqr_t **) cqrp; + return cqr; +} + +cqr_t * +request_cq (void) +{ + cqr_t *cqr = NULL; + int i; + cqr_t *area; + + spin_lock (&cqr_lock); + while (cqrp == NULL) { + do { + area = (cqr_t *) get_free_page (GFP_ATOMIC); + if (area == NULL) { + printk (KERN_WARNING PRINTK_HEADER + "No memory for chanq area\n"); + } + } while ( ! area ); + memset(area,0,PAGE_SIZE); + if (dasd_page_count + 1 >= MAX_DASD_PAGES) { + PRINT_WARN ("Requesting too many pages..."); + } else { + dasd_page[dasd_page_count++] = + (long) area; + } + for (i = 0; i < 4096 / sizeof (cqr_t); i++) { + cqf_enq (area + i); + } + } + cqr = cqf_deq (); + spin_unlock (&cqr_lock); + return cqr; +} + +void +release_cq (cqr_t * cqr) +{ + spin_lock (&cqr_lock); + cqf_enq (cqr); + spin_unlock (&cqr_lock); + return; +} + +/* ----------------------------------------------------------- */ +cqr_t * +request_cqr (int cpsize, int datasize) +{ + cqr_t *cqr = NULL; + cqr = request_cq (); + if (cqr == NULL) { + printk (KERN_WARNING PRINTK_HEADER __FILE__ + "No memory for chanq request\n"); + goto exit; + } + memset (cqr, 0, sizeof (cqr_t)); + cqr -> magic = DASD_MAGIC; + if (cpsize) { + cqr->cpaddr = request_cp (cpsize); + if (cqr->cpaddr == NULL) { + printk (KERN_WARNING PRINTK_HEADER __FILE__ + "No memory for channel program\n"); + goto nocp; + } + cqr->cplength = cpsize; + } + if (datasize) { + do { + cqr->data = (char *) kmalloc (datasize, GFP_ATOMIC); + if (cqr->data == NULL) { + printk (KERN_WARNING PRINTK_HEADER __FILE__ + "No memory for cqr data area\n"); + } + } while (!cqr->data); + memset (cqr->data,0,datasize); + } + goto exit; + nodata: + release_cp (cqr->cplength, cqr->cpaddr); + nocp: + release_cq (cqr); + cqr = NULL; + exit: + return cqr; +} + +int +release_cqr (cqr_t * cqr) +{ + int rc = 0; + if (cqr == NULL) { + rc = -ENOENT; + return rc; + } + if (cqr->data) { + kfree (cqr->data); + } + if (cqr->dstat) { + kfree (cqr->dstat); + } + if (cqr->cpaddr) { + release_cp (cqr->cplength, cqr->cpaddr); + } + cqr -> magic = dasd_MAGIC; + release_cq (cqr); + return rc; +} + +/* -------------------------------------------------------------- */ +void +dasd_chanq_enq (dasd_chanq_t * q, cqr_t * cqr) +{ + if (q->head != NULL) { + q->tail->next = cqr; + } else + q->head = cqr; + cqr->next = NULL; + q->tail = cqr; + if (atomic_compare_and_swap(CQR_STATUS_FILLED, + CQR_STATUS_QUEUED, + &cqr->status)) { + PRINT_WARN ("q_cqr: %p status changed %d\n", + cqr,atomic_read(&cqr->status)); + atomic_set(&cqr->status,CQR_STATUS_QUEUED); + } +} + +int +dasd_chanq_deq (dasd_chanq_t * q, cqr_t * cqr) +{ + cqr_t *prev; + + if (cqr == NULL) + return -ENOENT; + if (cqr == (cqr_t *) q->head) { + q->head = cqr->next; + if (q->head == NULL) + q->tail = NULL; + } else { + prev = (cqr_t *) q->head; + while (prev && prev->next != cqr) + prev = prev->next; + if (prev == NULL) + return -ENOENT; + prev->next = cqr->next; + if (prev->next == NULL) + q->tail = prev; + } + cqr->next = NULL; + return release_cqr(cqr); +} + +/* -------------------------------------------------------------------------- */ +void +cql_enq_head (dasd_chanq_t * q) +{ + if (q == NULL) { + INTERNAL_ERROR ("NULL queue passed%s\n", ""); + return; + } + if (atomic_read(&q->flags) & DASD_CHANQ_ACTIVE) { + PRINT_WARN("Queue already active"); + return; + } + spin_lock(&cq_lock); + atomic_set_mask(DASD_CHANQ_ACTIVE,&q->flags); + q->next_q = cq_head; + cq_head = q; + spin_unlock(&cq_lock); +} + +void +cql_deq (dasd_chanq_t * q) +{ + dasd_chanq_t *c; + + if (cq_head == NULL) { + INTERNAL_ERROR ("Channel queue is empty%s\n", ""); + return; + } + if (q == NULL) { + INTERNAL_ERROR ("NULL queue passed%s\n", ""); + return; + } + spin_lock(&cq_lock); + if (! (atomic_read(&q->flags) & DASD_CHANQ_ACTIVE)) { + PRINT_WARN("Queue not active\n"); + } + else if (cq_head == q) { + cq_head = q->next_q; + } else { + c = cq_head; + while (c->next_q && c->next_q != q) + c = c->next_q; + if (c->next_q != q) + INTERNAL_ERROR ("Entry not in queue%s\n", ""); + else + c->next_q = q->next_q; + } + q->next_q = NULL; + atomic_clear_mask(DASD_CHANQ_ACTIVE,&q->flags); + spin_unlock(&cq_lock); +} diff -u --recursive --new-file v2.2.13/linux/drivers/s390/block/dasd_ccwstuff.h linux/drivers/s390/block/dasd_ccwstuff.h --- v2.2.13/linux/drivers/s390/block/dasd_ccwstuff.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/block/dasd_ccwstuff.h Tue Jan 4 10:12:18 2000 @@ -0,0 +1,9 @@ +extern atomic_t chanq_tasks; +extern dasd_chanq_t *cq_head; + +cqr_t *request_cqr (int, int); +int release_cqr (cqr_t *); +int dasd_chanq_enq (dasd_chanq_t *, cqr_t *); +int dasd_chanq_deq (dasd_chanq_t *, cqr_t *); +void cql_enq_head (dasd_chanq_t * q); +void cql_deq (dasd_chanq_t * q); diff -u --recursive --new-file v2.2.13/linux/drivers/s390/block/dasd_eckd.c linux/drivers/s390/block/dasd_eckd.c --- v2.2.13/linux/drivers/s390/block/dasd_eckd.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/block/dasd_eckd.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,1000 @@ +/* + * File...........: linux/drivers/s390/block/dasd_eckd.c + * Author(s)......: Holger Smolinski + * Bugreports.to..: + * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999 + */ + +#include +#include + +#ifdef MODULE +#include +#endif /* MODULE */ + +#include +#include + +#include "../../../arch/s390/kernel/irq.h" + +#include "dasd_types.h" +#include "dasd_ccwstuff.h" + +#include "dasd.h" + +#ifdef PRINTK_HEADER +#undef PRINTK_HEADER +#endif /* PRINTK_HEADER */ +#define PRINTK_HEADER "dasd(eckd):" + +#define ECKD_C0(i) (i->home_bytes) +#define ECKD_F(i) (i -> formula) +#define ECKD_F1(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f1):(i->factors.f_0x02.f1)) +#define ECKD_F2(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f2):(i->factors.f_0x02.f2)) +#define ECKD_F3(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f3):(i->factors.f_0x02.f3)) +#define ECKD_F4(i) (ECKD_F(i)==0x02?(i->factors.f_0x02.f4):0) +#define ECKD_F5(i) (ECKD_F(i)==0x02?(i->factors.f_0x02.f5):0) +#define ECKD_F6(i) (i -> factor6) +#define ECKD_F7(i) (i -> factor7) +#define ECKD_F8(i) (i -> factor8) + +#define DASD_ECKD_CCW_LOCATE_RECORD 0x47 + +#define DASD_ECKD_CCW_READ_HOME_ADDRESS 0x0a +#define DASD_ECKD_CCW_WRITE_HOME_ADDRESS 0x09 + +#define DASD_ECKD_CCW_READ_RECORD_ZERO 0x16 +#define DASD_ECKD_CCW_WRITE_RECORD_ZERO 0x15 + +#define DASD_ECKD_CCW_READ_COUNT 0x12 +#define DASD_ECKD_CCW_READ 0x06 +#define DASD_ECKD_CCW_READ_MT 0x86 +#define DASD_ECKD_CCW_WRITE 0x05 +#define DASD_ECKD_CCW_WRITE_MT 0x85 +#define DASD_ECKD_CCW_READ_CKD 0x1e +#define DASD_ECKD_CCW_READ_CKD_MT 0x9e +#define DASD_ECKD_CCW_WRITE_CKD 0x1d +#define DASD_ECKD_CCW_WRITE_CKD_MT 0x9d + +typedef +struct { + __u16 cyl; + __u16 head; +} __attribute__ ((packed)) + +ch_t; + +typedef +struct { + __u16 cyl; + __u16 head; + __u32 sector; +} __attribute__ ((packed)) + +chs_t; + +typedef +struct { + __u16 cyl; + __u16 head; + __u8 record; +} __attribute__ ((packed)) + +chr_t; + +typedef +struct { + __u16 cyl; + __u16 head; + __u32 sector; +} geom_t; + +typedef struct { + struct { + struct { + unsigned char identifier:2; + unsigned char token_id:1; + unsigned char sno_valid:1; + unsigned char subst_sno:1; + unsigned char recNED:1; + unsigned char emuNED:1; + unsigned char reserved:1; + } __attribute__ ((packed)) flags; + __u8 descriptor; + __u8 dev_class; + __u8 reserved; + unsigned char dev_type[6]; + unsigned char dev_model[3]; + unsigned char HDA_manufacturer[3]; + unsigned char HDA_location[2]; + unsigned char HDA_seqno[12]; + __u16 ID; + } __attribute__ ((packed)) ned1; + struct { + struct { + unsigned char identifier:2; + unsigned char token_id:1; + unsigned char sno_valid:1; + unsigned char subst_sno:1; + unsigned char recNED:1; + unsigned char emuNED:1; + unsigned char reserved:1; + } __attribute__ ((packed)) flags; + __u8 descriptor; + __u8 reserved[2]; + unsigned char dev_type[6]; + unsigned char dev_model[3]; + unsigned char DASD_manufacturer[3]; + unsigned char DASD_location[2]; + unsigned char DASD_seqno[12]; + __u16 ID; + } __attribute__ ((packed)) ned2; + struct { + struct { + unsigned char identifier:2; + unsigned char token_id:1; + unsigned char sno_valid:1; + unsigned char subst_sno:1; + unsigned char recNED:1; + unsigned char emuNED:1; + unsigned char reserved:1; + } __attribute__ ((packed)) flags; + __u8 descriptor; + __u8 reserved[2]; + unsigned char cont_type[6]; + unsigned char cont_model[3]; + unsigned char cont_manufacturer[3]; + unsigned char cont_location[2]; + unsigned char cont_seqno[12]; + __u16 ID; + } __attribute__ ((packed)) ned3; + struct { + struct { + unsigned char identifier:2; + unsigned char token_id:1; + unsigned char sno_valid:1; + unsigned char subst_sno:1; + unsigned char recNED:1; + unsigned char emuNED:1; + unsigned char reserved:1; + } __attribute__ ((packed)) flags; + __u8 descriptor; + __u8 reserved[2]; + unsigned char cont_type[6]; + unsigned char empty[3]; + unsigned char cont_manufacturer[3]; + unsigned char cont_location[2]; + unsigned char cont_seqno[12]; + __u16 ID; + } __attribute__ ((packed)) ned4; + unsigned char ned5[32]; + unsigned char ned6[32]; + unsigned char ned7[32]; + struct { + struct { + unsigned char identifier:2; + unsigned char reserved:6; + } __attribute__ ((packed)) flags; + __u8 selector; + __u16 interfaceID; + __u32 reserved; + __u16 subsystemID; + struct { + unsigned char sp0:1; + unsigned char sp1:1; + unsigned char reserved:5; + unsigned char scluster:1; + } __attribute__ ((packed)) spathID; + __u8 unit_address; + __u8 dev_ID; + __u8 dev_address; + __u8 adapterID; + __u16 link_address; + struct { + unsigned char parallel:1; + unsigned char escon:1; + unsigned char reserved:1; + unsigned char ficon:1; + unsigned char reserved2:4; + } __attribute__ ((packed)) protocol_type; + struct { + unsigned char PID_in_236:1; + unsigned char reserved:7; + } __attribute__ ((packed)) format_flags; + __u8 log_dev_address; + unsigned char reserved2[12]; + } __attribute__ ((packed)) neq; + +} __attribute__ ((packed)) + +eckd_confdata_t; + +typedef +struct { + struct { + unsigned char perm:2; /* Permissions on this extent */ + unsigned char reserved:1; + unsigned char seek:2; /* Seek control */ + unsigned char auth:2; /* Access authorization */ + unsigned char pci:1; /* PCI Fetch mode */ + } __attribute__ ((packed)) mask; + struct { + unsigned char mode:2; /* Architecture mode */ + unsigned char ckd:1; /* CKD Conversion */ + unsigned char operation:3; /* Operation mode */ + unsigned char cfw:1; /* Cache fast write */ + unsigned char dfw:1; /* DASD fast write */ + } __attribute__ ((packed)) attributes; + __u16 short blk_size; /* Blocksize */ + __u16 fast_write_id; + __u8 unused; + __u8 reserved; + ch_t beg_ext; + ch_t end_ext; +} __attribute__ ((packed, aligned (32))) + +DE_eckd_data_t; + +typedef +struct { + struct { + unsigned char orientation:2; + unsigned char operation:6; + } __attribute__ ((packed)) operation; + struct { + unsigned char last_bytes_used:1; + unsigned char reserved:6; + unsigned char read_count_suffix:1; + } __attribute__ ((packed)) auxiliary; + __u8 unused; + __u8 count; + ch_t seek_addr; + chr_t search_arg; + __u8 sector; + __u16 length; +} __attribute__ ((packed, aligned (32))) + +LO_eckd_data_t; + +/* Stuff for handling home addresses */ +typedef struct { + __u8 skip_control[14]; + __u16 cell_number; + __u8 physical_addr[3]; + __u8 flag; + ch_t track_addr; + __u8 reserved; + __u8 key_length; + __u8 reserved2[2]; +} __attribute__ ((packed, aligned (32))) + +eckd_home_t; + +/* eckd count area */ +typedef struct { + __u16 cyl; + __u16 head; + __u8 record; + __u8 kl; + __u16 dl; +} __attribute__ ((packed)) + +eckd_count_t; + +static unsigned int +round_up_multiple (unsigned int no, unsigned int mult) +{ + int rem = no % mult; + return (rem ? no - rem + mult : no); +/* return (no % mult ? no - (no % mult) + mult : no); */ +} + +static unsigned int +ceil_quot (unsigned int d1, unsigned int d2) +{ + return (d1 + (d2 - 1)) / d2; +} + +static int +bytes_per_record (dasd_eckd_characteristics_t * rdc, + int kl, /* key length */ + int dl /* data length */ ) +{ + int bpr = 0; + switch (rdc->formula) { + case 0x01:{ + unsigned int fl1, fl2; + fl1 = round_up_multiple (ECKD_F2 (rdc) + dl, + ECKD_F1 (rdc)); + fl2 = round_up_multiple (kl ? ECKD_F2 (rdc) + kl : 0, + ECKD_F1 (rdc)); + bpr = fl1 + fl2; + break; + } + case 0x02:{ + unsigned int fl1, fl2, int1, int2; + int1 = ceil_quot (dl + ECKD_F6 (rdc), + ECKD_F5 (rdc) << 1); + int2 = ceil_quot (kl + ECKD_F6 (rdc), + ECKD_F5 (rdc) << 1); + fl1 = round_up_multiple (ECKD_F1 (rdc) * + ECKD_F2 (rdc) + + (dl + ECKD_F6 (rdc) + + ECKD_F4 (rdc) * int1), + ECKD_F1 (rdc)); + fl2 = round_up_multiple (ECKD_F1 (rdc) * + ECKD_F3 (rdc) + + (kl + ECKD_F6 (rdc) + + ECKD_F4 (rdc) * int2), + ECKD_F1 (rdc)); + bpr = fl1 + fl2; + break; + } + default: + INTERNAL_ERROR ("unknown formula%d\n", rdc->formula); + } + return bpr; +} + +static inline unsigned int +bytes_per_track (dasd_eckd_characteristics_t * rdc) +{ + return *(unsigned int *) (rdc->byte_per_track) >> 8; +} + +static unsigned int +recs_per_track (dasd_eckd_characteristics_t * rdc, + unsigned int kl, unsigned int dl) +{ + int rpt = 0; + if (rdc->formula == 0x01) { + if (kl) + return 1499 / (15 + + 7 + ceil_quot (kl + 12, 32) + + ceil_quot (dl + 12, 32)); + else + return 1499 / (15 + ceil_quot (dl + 12, 32)); + + } + if (rdc->formula == 0x02) { + int dn = ceil_quot (dl + 6, 232) + 1; + if (kl) { + int kn = ceil_quot (kl + 6, 232) + 1; + return 1729 / (10 + + 9 + ceil_quot (kl + 6 * kn, 34) + + 9 + ceil_quot (dl + 6 * dn, 34)); + } else + return 1729 / (10 + + 9 + ceil_quot (dl + 6 * dn, 34)); + } + return rpt; +} + +static +void +define_extent (ccw1_t * de_ccw, + DE_eckd_data_t * data, + int trk, + int totrk, + int cmd, + dasd_information_t * info) +{ + ch_t geo, beg, end; + + geo.cyl = info->rdc_data->eckd.no_cyl; + geo.head = info->rdc_data->eckd.trk_per_cyl; + beg.cyl = trk / geo.head; + beg.head = trk % geo.head; + end.cyl = totrk / geo.head; + end.head = totrk % geo.head; + + memset (de_ccw, 0, sizeof (ccw1_t)); + de_ccw->cmd_code = CCW_DEFINE_EXTENT; + de_ccw->count = 16; + de_ccw->cda = (void *) virt_to_phys (data); + + memset (data, 0, sizeof (DE_eckd_data_t)); + switch (cmd) { + case DASD_ECKD_CCW_READ_HOME_ADDRESS: + case DASD_ECKD_CCW_READ_RECORD_ZERO: + case DASD_ECKD_CCW_READ: + case DASD_ECKD_CCW_READ_MT: + case DASD_ECKD_CCW_READ_CKD: /* Fallthrough */ + case DASD_ECKD_CCW_READ_CKD_MT: + case DASD_ECKD_CCW_READ_COUNT: + data->mask.perm = 0x1; + break; + case DASD_ECKD_CCW_WRITE: + case DASD_ECKD_CCW_WRITE_MT: + break; + case DASD_ECKD_CCW_WRITE_CKD: + case DASD_ECKD_CCW_WRITE_CKD_MT: + data->attributes.operation = 0x1; /* format through cache */ + break; + case DASD_ECKD_CCW_WRITE_HOME_ADDRESS: + case DASD_ECKD_CCW_WRITE_RECORD_ZERO: + data->mask.perm = 0x3; + data->mask.auth = 0x1; + data->attributes.operation = 0x1; /* format through cache */ + break; + default: + INTERNAL_ERROR ("unknown opcode 0x%x\n", cmd); + break; + } + data->attributes.mode = 0x3; + data->beg_ext.cyl = beg.cyl; + data->beg_ext.head = beg.head; + data->end_ext.cyl = end.cyl; + data->end_ext.head = end.head; +} + +static inline void +locate_record (ccw1_t * lo_ccw, + LO_eckd_data_t * data, + int trk, + int rec_on_trk, + int no_rec, + int cmd, + dasd_information_t * info) +{ + ch_t geo = + {info->rdc_data->eckd.no_cyl, + info->rdc_data->eckd.trk_per_cyl}; + ch_t seek = + {trk / (geo.head), trk % (geo.head)}; + int reclen = info->sizes.bp_block; + memset (lo_ccw, 0, sizeof (ccw1_t)); + lo_ccw->cmd_code = DASD_ECKD_CCW_LOCATE_RECORD; + lo_ccw->count = 16; + lo_ccw->cda = (void *) virt_to_phys (data); + + memset (data, 0, sizeof (LO_eckd_data_t)); + switch (cmd) { + case DASD_ECKD_CCW_WRITE_HOME_ADDRESS: + data->operation.orientation = 0x3; + data->operation.operation = 0x03; + break; + case DASD_ECKD_CCW_READ_HOME_ADDRESS: + data->operation.orientation = 0x3; + data->operation.operation = 0x16; + break; + case DASD_ECKD_CCW_WRITE_RECORD_ZERO: + data->operation.orientation = 0x3; + data->operation.operation = 0x03; + data->count++; + break; + case DASD_ECKD_CCW_READ_RECORD_ZERO: + data->operation.orientation = 0x3; + data->operation.operation = 0x16; + data->count++; + break; + case DASD_ECKD_CCW_WRITE: + case DASD_ECKD_CCW_WRITE_MT: + data->auxiliary.last_bytes_used = 0x1; + data->length = reclen; + data->operation.operation = 0x01; + break; + case DASD_ECKD_CCW_WRITE_CKD: + case DASD_ECKD_CCW_WRITE_CKD_MT: + data->auxiliary.last_bytes_used = 0x1; + data->length = reclen; + data->operation.operation = 0x03; + break; + case DASD_ECKD_CCW_READ: + case DASD_ECKD_CCW_READ_MT: + data->auxiliary.last_bytes_used = 0x1; + data->length = reclen; + data->operation.operation = 0x06; + break; + case DASD_ECKD_CCW_READ_CKD: + case DASD_ECKD_CCW_READ_CKD_MT: + data->auxiliary.last_bytes_used = 0x1; + data->length = reclen; + data->operation.operation = 0x16; + break; + case DASD_ECKD_CCW_READ_COUNT: + data->operation.operation = 0x06; + break; + default: + INTERNAL_ERROR ("unknown opcode 0x%x\n", cmd); + } + memcpy (&(data->seek_addr), &seek, sizeof (ch_t)); + memcpy (&(data->search_arg), &seek, sizeof (ch_t)); + data->search_arg.record = rec_on_trk; + data->count += no_rec; +} + +void +dasd_eckd_print_error (devstat_t * stat) +{ + int sct, sl; + char *sense = stat->ii.sense.data; + PRINT_WARN ("IRQ on devno %x: with intparm:%x DS:0x%02x CS:0x%02x\n", + stat->devno, stat->intparm, stat->dstat, stat->cstat); + PRINT_WARN ("Failing CCW: %p\n", (ccw1_t *) stat->cpa); + for (sl = 0; sl < 4; sl++) { + PRINT_DEBUG ("Sense:"); + for (sct = 0; sct < 8; sct++) { + printk (" %2d:0x%02x", + 8 * sl + sct, sense[8 * sl + sct]); + } + printk ("\n"); + } + if (sense[27] & 0x80) { /* 32 Byte Sense Data */ + PRINT_INFO ("Sense Data is 32 Byte information\n"); + PRINT_INFO ("Format: %x Exception class %x\n", + sense[6] & 0x0f, sense[22] >> 4); + } else { /* 24 Byte Sense Data */ + PRINT_INFO ("Sense Data is 24 Byte information\n"); + PRINT_INFO ("FMT: %x MSG %x, %s MSGb to SYSOP\n", + sense[7] >> 4, sense[7] & 0x0f, + sense[1] & 0x10 ? "" : "no"); + } +} + +int +dasd_eckd_format_track (int di, int trk, int bs) +{ + int rc = 0; + int i; + int flags = 0x00; /* FORMAT_R0 = 0x01, FORMAT_HA = 0x03 */ + dasd_information_t * info=dasd_info[di]; + cqr_t *fcp; + DE_eckd_data_t *DE_data; + LO_eckd_data_t *LO_data; + eckd_count_t *ct_data; + eckd_count_t *r0_data; + ccw1_t *last_ccw; + int retries = 5; + + int rpt = recs_per_track (&(info->rdc_data->eckd), 0, bs); + int cyl = trk / info->rdc_data->eckd.trk_per_cyl; + int head = trk % info->rdc_data->eckd.trk_per_cyl; + + fcp = request_cqr (2 + 1 + rpt, + sizeof (DE_eckd_data_t) + + sizeof (LO_eckd_data_t) + + (rpt + 1) * sizeof (eckd_count_t)); + fcp -> devindex=di; + DE_data = (DE_eckd_data_t *) fcp->data; + LO_data = (LO_eckd_data_t *) (((long) DE_data) + + sizeof (DE_eckd_data_t)); + r0_data = (eckd_count_t *) (((long) LO_data) + + sizeof (LO_eckd_data_t)); + ct_data = (eckd_count_t *) (((long) r0_data) + + sizeof (eckd_count_t)); + last_ccw = fcp->cpaddr; + switch (flags) { + case 0x03: + define_extent (last_ccw, DE_data, trk, trk, + DASD_ECKD_CCW_WRITE_HOME_ADDRESS, info); + last_ccw->flags = CCW_FLAG_CC; + last_ccw++; + locate_record (last_ccw, LO_data, trk, 0, rpt, + DASD_ECKD_CCW_WRITE_HOME_ADDRESS, info); + last_ccw->flags = CCW_FLAG_CC; + last_ccw++; + break; + case 0x01: + define_extent (last_ccw, DE_data, trk, trk, + DASD_ECKD_CCW_WRITE_RECORD_ZERO, info); + last_ccw->flags = CCW_FLAG_CC; + last_ccw++; + locate_record (last_ccw, LO_data, trk, 0, rpt, + DASD_ECKD_CCW_WRITE_RECORD_ZERO, info); + last_ccw->flags = CCW_FLAG_CC; + last_ccw++; + break; + case 0x00: + define_extent (last_ccw, DE_data, trk, trk, + DASD_ECKD_CCW_WRITE_CKD, info); + last_ccw->flags = CCW_FLAG_CC; + last_ccw++; + locate_record (last_ccw, LO_data, trk, 0, rpt, + DASD_ECKD_CCW_WRITE_CKD, info); + LO_data->length = bs; + last_ccw->flags = CCW_FLAG_CC; + last_ccw++; + break; + default: + PRINT_WARN ("Unknown format flags...%d\n", flags); + return -EINVAL; + } + if (flags & 0x02) { + PRINT_WARN ("Unsupported format flag...%d\n", flags); + return -EINVAL; + } + if (flags & 0x01) { /* write record zero */ + memset (r0_data, 0, sizeof (eckd_count_t)); + r0_data->cyl = cyl; + r0_data->head = head; + r0_data->record = 0; + r0_data->kl = 0; + r0_data->dl = 8; + last_ccw->cmd_code = 0x03; + last_ccw->count = 8; + last_ccw->flags = CCW_FLAG_CC | CCW_FLAG_SLI; + last_ccw->cda = (void *) virt_to_phys (r0_data); + last_ccw++; + } + /* write remaining records */ + for (i = 0; i < rpt; i++, last_ccw++) { + memset (ct_data + i, 0, sizeof (eckd_count_t)); + (ct_data + i)->cyl = cyl; + (ct_data + i)->head = head; + (ct_data + i)->record = i + 1; + (ct_data + i)->kl = 0; + (ct_data + i)->dl = bs; + last_ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD; + last_ccw->flags = CCW_FLAG_CC | CCW_FLAG_SLI; + last_ccw->count = 8; + last_ccw->cda = (void *) + virt_to_phys (ct_data + i); + } + (last_ccw - 1)->flags &= ~(CCW_FLAG_CC | CCW_FLAG_DC); + fcp -> devindex = di; + fcp -> flags = DASD_DO_IO_SLEEP; + do { + struct wait_queue wait = {current, NULL}; + unsigned long flags; + int irq; + int cs; + + irq = dasd_info[fcp->devindex]->info.irq; + s390irq_spin_lock_irqsave (irq, flags); + atomic_set(&fcp->status,CQR_STATUS_QUEUED); + rc = dasd_start_IO ( fcp ); + add_wait_queue (&dasd_waitq, &wait); + do { + current->state = TASK_UNINTERRUPTIBLE; + s390irq_spin_unlock_irqrestore (irq, flags); + schedule (); + s390irq_spin_lock_irqsave (irq, flags); + } while (((cs = atomic_read (&fcp->status)) != + CQR_STATUS_DONE) && + (cs != CQR_STATUS_ERROR)); + remove_wait_queue (&dasd_waitq, &wait); + s390irq_spin_unlock_irqrestore (irq, flags); + + retries --; + } while ( (rc || (atomic_read(&fcp->status) != CQR_STATUS_DONE)) && + retries); + if ((rc || (atomic_read(&fcp->status) != CQR_STATUS_DONE))) + rc = -EIO; + release_cqr (fcp); + return rc; +} + +int +dasd_eckd_ck_devinfo (dev_info_t * info) +{ + return 0; +} + +cqr_t * +dasd_eckd_build_req (int devindex, + struct request * req) +{ + cqr_t *rw_cp = NULL; + ccw1_t *ccw; + + DE_eckd_data_t *DE_data; + LO_eckd_data_t *LO_data; + struct buffer_head *bh; + int rw_cmd; + dasd_information_t *info = dasd_info[devindex]; + int blk_per_trk = recs_per_track (&(info->rdc_data->eckd), + 0, info->sizes.bp_block); + int byt_per_blk = info->sizes.bp_block; + int noblk = req-> nr_sectors >> info->sizes.s2b_shift; + int btrk = (req->sector >> info->sizes.s2b_shift) / blk_per_trk; + int etrk = ((req->sector + req->nr_sectors - 1) >> + info->sizes.s2b_shift) / blk_per_trk; + + if ( ! noblk ) { + PRINT_ERR("No blocks to write...returning\n"); + return NULL; + } + + if (req->cmd == READ) { + rw_cmd = DASD_ECKD_CCW_READ_MT; + } else +#if DASD_PARANOIA > 2 + if (req->cmd == WRITE) +#endif /* DASD_PARANOIA */ + { + rw_cmd = DASD_ECKD_CCW_WRITE_MT; + } +#if DASD_PARANOIA > 2 + else { + PRINT_ERR ("Unknown command %d\n", req->cmd); + return NULL; + } +#endif /* DASD_PARANOIA */ + /* Build the request */ + rw_cp = request_cqr (2 + noblk, + sizeof (DE_eckd_data_t) + + sizeof (LO_eckd_data_t)); + if ( ! rw_cp ) { + return NULL; + } + DE_data = rw_cp->data; + LO_data = rw_cp->data + sizeof (DE_eckd_data_t); + ccw = rw_cp->cpaddr; + + define_extent (ccw, DE_data, btrk, etrk, rw_cmd, info); + ccw->flags = CCW_FLAG_CC; + ccw++; + locate_record (ccw, LO_data, btrk, + (req->sector >> info->sizes.s2b_shift) % + blk_per_trk + 1, + req->nr_sectors >> info->sizes.s2b_shift, + rw_cmd, info); + ccw->flags = CCW_FLAG_CC; + for (bh = req->bh; bh; bh = bh->b_reqnext) { + long size; + for (size = 0; size < bh->b_size; size += byt_per_blk) { + ccw++; + ccw->flags = CCW_FLAG_CC; + ccw->cmd_code = rw_cmd; + ccw->count = byt_per_blk; + ccw->cda = (void *) virt_to_phys (bh->b_data + size); + } + } + ccw->flags &= ~(CCW_FLAG_DC | CCW_FLAG_CC); + return rw_cp; +} + +cqr_t * +dasd_eckd_rw_label (int devindex, int rw, char *buffer) +{ + int cmd_code = 0x03; + dasd_information_t *info = dasd_info[devindex]; + cqr_t *cqr; + ccw1_t *ccw; + + switch (rw) { + case READ: + cmd_code = DASD_ECKD_CCW_READ; + break; + case WRITE: + cmd_code = DASD_ECKD_CCW_WRITE; + break; +#if DASD_PARANOIA > 2 + default: + INTERNAL_ERROR ("unknown cmd %d", rw); + return NULL; +#endif /* DASD_PARANOIA */ + } + cqr = request_cqr (3, sizeof (DE_eckd_data_t) + + sizeof (LO_eckd_data_t)); + ccw = cqr->cpaddr; + define_extent (ccw, cqr->data, 0, 0, cmd_code, info); + ccw->flags |= CCW_FLAG_CC; + ccw++; + locate_record (ccw, cqr->data + 1, 0, 2, 1, cmd_code, info); + ccw->flags |= CCW_FLAG_CC; + ccw++; + ccw->cmd_code = cmd_code; + ccw->flags |= CCW_FLAG_SLI; + ccw->count = sizeof (dasd_volume_label_t); + ccw->cda = (void *) virt_to_phys ((void *) buffer); + return cqr; + +} + +void +dasd_eckd_print_char (dasd_characteristics_t * i) +{ + dasd_eckd_characteristics_t * c = + (dasd_eckd_characteristics_t *)i; + PRINT_INFO ("%x/%x (%x/%x) Cyl: %d Head: %d Sec: %d \n", + c->dev_type, c->dev_model, + c->cu_type, c->cu_model.model, + c->no_cyl, c->trk_per_cyl, + c->sec_per_trk); + PRINT_INFO ("Estimate: %d Byte/trk %d byte/kByte %d kByte/trk \n", + bytes_per_track (c), + bytes_per_record (c, 0, 1024), + recs_per_track (c, 0, 1024)); +}; + +int +dasd_eckd_ck_char (dasd_characteristics_t * i) +{ + int rc = 0; + dasd_eckd_print_char (i); + return rc; +} + +int +dasd_eckd_format (int devindex, format_data_t * fdata) +{ + int rc = 0; + int i; + dasd_information_t *info = dasd_info[devindex]; + format_data_t fd; + + if (!fdata) { + fd.start_unit = 0; + fd.stop_unit = info->rdc_data->eckd.no_cyl * + info->rdc_data->eckd.trk_per_cyl - 1; + fd.blksize = 4096; + } else { + memcpy (&fd, fdata, sizeof (format_data_t)); + if ( fd.stop_unit == -1 ) { + fd.stop_unit = info->rdc_data->eckd.no_cyl * + info->rdc_data->eckd.trk_per_cyl - 1; + } + if ( fd.blksize == 0 ) { + fd.blksize = 4096; + } + } + PRINT_INFO("Formatting device %d from %d to %d with bs %d\n", + devindex,fd.start_unit,fd.stop_unit,fd.blksize); + if ( fd.start_unit > fd.stop_unit ) { + PRINT_WARN ("start unit .gt. stop unit\n"); + return -EINVAL; + } + if ( (fd.start_unit > info->rdc_data->eckd.no_cyl * + info->rdc_data->eckd.trk_per_cyl - 1) ) { + PRINT_WARN ("start unit beyond end of disk\n"); + return -EINVAL; + } + if ( (fd.stop_unit > info->rdc_data->eckd.no_cyl * + info->rdc_data->eckd.trk_per_cyl - 1) ) { + PRINT_WARN ("stop unit beyond end of disk\n"); + return -EINVAL; + } + switch (fd.blksize) { + case 512: + case 1024: + case 2048: + case 4096: + break; + default: + PRINT_WARN ("invalid blocksize\n"); + return -EINVAL; + } + for (i = fd.start_unit; i <= fd.stop_unit; i++) { + /* print 20 messages per disk at all */ + if ( ! ( i % (info->rdc_data->eckd.trk_per_cyl * + (info->rdc_data->eckd.no_cyl / 20 ) ))) { + PRINT_INFO ("Format %d Cylinder: %d\n",devindex, + i/info->rdc_data->eckd.trk_per_cyl); + } + rc = dasd_eckd_format_track (devindex, i, fd.blksize); + if (rc) { + PRINT_WARN ("Formatting of Track %d failed...exiting\n", i); + break; + } + } + PRINT_INFO("Formated device %d from %d to %d with bs %d\n", + devindex,fd.start_unit,fd.stop_unit,fd.blksize); + return rc; +} + +int +dasd_eckd_read_count (int di) +{ + int rc; + cqr_t *rw_cp = NULL; + ccw1_t *ccw; + DE_eckd_data_t *DE_data; + LO_eckd_data_t *LO_data; + eckd_count_t *count_data; + int retries = 5; + unsigned long flags; + int irq; + int cs; + dasd_information_t *info = dasd_info[di]; + rw_cp = request_cqr (3, + sizeof (DE_eckd_data_t) + + sizeof (LO_eckd_data_t) + + sizeof (eckd_count_t)); + DE_data = rw_cp->data; + LO_data = rw_cp->data + sizeof (DE_eckd_data_t); + count_data = (eckd_count_t*)((long)LO_data + sizeof (LO_eckd_data_t)); + ccw = rw_cp->cpaddr; + define_extent (ccw, DE_data, 0, 0, DASD_ECKD_CCW_READ_COUNT, info); + ccw->flags = CCW_FLAG_CC; + ccw++; + locate_record (ccw, LO_data, 0, 1, 1, DASD_ECKD_CCW_READ_COUNT, info); + ccw->flags = CCW_FLAG_CC; + ccw++; + ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT; + ccw->count = 8; + ccw->cda = (void *) virt_to_phys (count_data); + rw_cp->devindex = di; + rw_cp -> options = DOIO_WAIT_FOR_INTERRUPT; + do { + irq = dasd_info[di]->info.irq; + s390irq_spin_lock_irqsave (irq, flags); + atomic_set(&rw_cp -> status, CQR_STATUS_QUEUED); + rc = dasd_start_IO ( rw_cp ); + s390irq_spin_unlock_irqrestore (irq, flags); + retries --; + } while ( ( ( (cs=atomic_read(&rw_cp->status)) != CQR_STATUS_DONE) || + rc ) && retries ); + if ( ( rc || cs != CQR_STATUS_DONE) ) { + if ( ( cs == CQR_STATUS_ERROR ) && + ( rw_cp -> dstat -> ii.sense.data[1] == 0x08 ) ) { + rc = -EMEDIUMTYPE; + } else { + dasd_eckd_print_error (rw_cp->dstat); + rc = -EIO; + } + } else { + rc = count_data->dl; + } + release_cqr (rw_cp); + return rc; +} + +int +dasd_eckd_fill_sizes (int devindex) +{ + int bs = 0; + int sb; + dasd_information_t *in = dasd_info[devindex]; + bs = dasd_eckd_read_count (devindex); + if (bs <= 0) { + PRINT_INFO("Cannot figure out blocksize. did you format the disk?\n"); + memset (&(in -> sizes), 0, sizeof(dasd_sizes_t )); + return -EMEDIUMTYPE; + } else { + in->sizes.bp_block = bs; + } + in->sizes.bp_sector = in->sizes.bp_block; + + in->sizes.b2k_shift = 0; /* bits to shift a block to get 1k */ + for (sb = 1024; sb < bs; sb = sb << 1) + in->sizes.b2k_shift++; + + in->sizes.s2b_shift = 0; /* bits to shift 512 to get a block */ + for (sb = 512; sb < bs; sb = sb << 1) + in->sizes.s2b_shift++; + + in->sizes.blocks = in->rdc_data->eckd.no_cyl * + in->rdc_data->eckd.trk_per_cyl * + recs_per_track (&(in->rdc_data->eckd), 0, bs); + in->sizes.kbytes = in->sizes.blocks << in->sizes.b2k_shift; + + PRINT_INFO ("Verified: %d B/trk %d B/Blk(%d B) %d Blks/trk %d kB/trk \n", + bytes_per_track (&(in->rdc_data->eckd)), + bytes_per_record (&(in->rdc_data->eckd), 0, in->sizes.bp_block), + in->sizes.bp_block, + recs_per_track (&(in->rdc_data->eckd), 0, in->sizes.bp_block), + (recs_per_track (&(in->rdc_data->eckd), 0, in->sizes.bp_block) << + in->sizes.b2k_shift )); + return 0; +} + +dasd_operations_t dasd_eckd_operations = +{ + dasd_eckd_ck_devinfo, + dasd_eckd_build_req, + dasd_eckd_rw_label, + dasd_eckd_ck_char, + dasd_eckd_fill_sizes, + dasd_eckd_format, +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-indent-level: 4 + * c-brace-imaginary-offset: 0 + * c-brace-offset: -4 + * c-argdecl-indent: 4 + * c-label-offset: -4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: 0 + * indent-tabs-mode: nil + * tab-width: 8 + * End: + */ diff -u --recursive --new-file v2.2.13/linux/drivers/s390/block/dasd_proc.c linux/drivers/s390/block/dasd_proc.c --- v2.2.13/linux/drivers/s390/block/dasd_proc.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/block/dasd_proc.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,115 @@ +/* + Structure of the proc filesystem: + /proc/dasd/ + /proc/dasd/devices # List of devices + /proc/dasd/ddabcd # Device node for devno abcd + /proc/dasd/ddabcd1 # Device node for partition abcd + /proc/dasd/abcd # Device information for devno abcd +*/ + +#include + +#include "dasd.h" +#include "dasd_types.h" + +int dasd_proc_read_devices ( char *, char **, off_t, int, int); +#ifdef DASD_PROFILE +extern int dasd_proc_read_statistics ( char *, char **, off_t, int, int); +extern int dasd_proc_read_debug ( char *, char **, off_t, int, int); +#endif /* DASD_PROFILE */ + +struct proc_dir_entry dasd_proc_root_entry = { + 0, + 4,"dasd", + S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR | S_IWGRP, + 1,0,0, + 0, + NULL, +}; + +struct proc_dir_entry dasd_proc_devices_entry = { + 0, + 7,"devices", + S_IFREG | S_IRUGO | S_IXUGO | S_IWUSR | S_IWGRP, + 1,0,0, + 0, + NULL, + &dasd_proc_read_devices, +}; + +#ifdef DASD_PROFILE +struct proc_dir_entry dasd_proc_stats_entry = { + 0, + 10,"statistics", + S_IFREG | S_IRUGO | S_IXUGO | S_IWUSR | S_IWGRP, + 1,0,0, + 0, + NULL, + &dasd_proc_read_statistics, +}; + +struct proc_dir_entry dasd_proc_debug_entry = { + 0, + 5,"debug", + S_IFREG | S_IRUGO | S_IXUGO | S_IWUSR | S_IWGRP, + 1,0,0, + 0, + NULL, + &dasd_proc_read_debug, +}; +#endif /* DASD_PROFILE */ + +struct proc_dir_entry dasd_proc_device_template = { + 0, + 6,"dd????", + S_IFBLK | S_IRUGO | S_IWUSR | S_IWGRP, + 1,0,0, + 0, + NULL, +}; + +void +dasd_proc_init ( void ) +{ + proc_register( & proc_root, & dasd_proc_root_entry); + proc_register( & dasd_proc_root_entry, & dasd_proc_devices_entry); +#ifdef DASD_PROFILE + proc_register( & dasd_proc_root_entry, & dasd_proc_stats_entry); + proc_register( & dasd_proc_root_entry, & dasd_proc_debug_entry); +#endif /* DASD_PROFILE */ +} + + +int +dasd_proc_read_devices ( char * buf, char **start, off_t off, int len, int d) +{ + int i; + len = sprintf ( buf, "dev# MAJ minor node Format\n"); + for ( i = 0; i < DASD_MAX_DEVICES; i++ ) { + dasd_information_t *info = dasd_info[i]; + if ( ! info ) + continue; + if ( len >= PAGE_SIZE - 80 ) + len += sprintf ( buf + len, "terminated...\n"); + len += sprintf ( buf + len, + "%04X %3d %5d /dev/dd%04X", + dasd_info[i]->info.devno, + DASD_MAJOR, + i << PARTN_BITS, + i ); + if (info->flags == DASD_NOT_FORMATTED) { + len += sprintf ( buf + len, " n/a"); + } else { + len += sprintf ( buf + len, " %6d", + info->sizes.bp_block); + } + len += sprintf ( buf + len, "\n"); + } + return len; +} + + +void +dasd_proc_add_node (int di) +{ +} diff -u --recursive --new-file v2.2.13/linux/drivers/s390/block/dasd_profile.c linux/drivers/s390/block/dasd_profile.c --- v2.2.13/linux/drivers/s390/block/dasd_profile.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/block/dasd_profile.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,209 @@ +#include +#include + +#include "dasd.h" +#include "dasd_types.h" + +#define PRINTK_HEADER "dasd_profile:" + +static long dasd_io_reqs=0; /* number of requests processed at all */ +static long dasd_io_secs[16]; /* histogram of request's sizes */ +static long dasd_io_times[16]; /* histogram of requests's times */ +static long dasd_io_timps[16]; /* histogram of requests's times per sector */ +static long dasd_io_time1[16]; /* histogram of time from build to start */ +static long dasd_io_time2[16]; /* histogram of time from start to irq */ +static long dasd_io_time2ps[16]; /* histogram of time from start to irq */ +static long dasd_io_time3[16]; /* histogram of time from irq to end */ + +void +dasd_profile_add ( cqr_t *cqr ) +{ + int ind; + long strtime,irqtime,endtime,tottime; + long tottimeps,sectors; + long help; + if ( ! cqr -> req ) + return; + sectors = cqr -> req -> nr_sectors; + strtime = ((cqr->startclk - cqr->buildclk) >> 12); + irqtime = ((cqr->stopclk - cqr->startclk) >> 12); + endtime = ((cqr->endclk - cqr->stopclk) >> 12); + tottime = ((cqr->endclk - cqr->buildclk) >> 12); + tottimeps = tottime / sectors; + + if (! dasd_io_reqs ++){ + for ( ind = 0; ind < 16; ind ++) { + dasd_io_secs[ind] = 0; + dasd_io_times[ind]=0; + dasd_io_timps[ind]=0; + dasd_io_time1[ind]=0; + dasd_io_time2[ind]=0; + dasd_io_time2ps[ind]=0; + dasd_io_time3[ind]=0; + } + }; + + for ( ind = 0, help = sectors >> 3; + ind < 15 && help; + help = help >> 1,ind ++); + dasd_io_secs[ind] ++; + + for ( ind = 0, help = tottime >> 3; + ind < 15 && help; + help = help >> 1,ind ++); + dasd_io_times[ind] ++; + + for ( ind = 0, help = tottimeps >> 3; + ind < 15 && help; + help = help >> 1,ind ++); + dasd_io_timps[ind] ++; + + for ( ind = 0, help = strtime >> 3; + ind < 15 && help; + help = help >> 1,ind ++); + dasd_io_time1[ind] ++; + + for ( ind = 0, help = irqtime >> 3; + ind < 15 && help; + help = help >> 1,ind ++); + dasd_io_time2[ind] ++; + + for ( ind = 0, help = (irqtime/sectors) >> 3; + ind < 15 && help; + help = help >> 1,ind ++); + dasd_io_time2ps[ind] ++; + + for ( ind = 0, help = endtime >> 3; + ind < 15 && help; + help = help >> 1,ind ++); + dasd_io_time3[ind] ++; +} + +int +dasd_proc_read_statistics ( char * buf, char **start, + off_t off, int len, int d) +{ + int i; + int shift, help; + + for ( shift = 0, help = dasd_io_reqs; + help > 8192; + help = help >> 1,shift ++); + len = sprintf ( buf, "%ld dasd I/O requests\n", dasd_io_reqs); + len += sprintf ( buf+len, "__<4 ___8 __16 __32 __64 _128 _256 _512 __1k __2k __4k __8k _16k _32k _64k >64k\n"); + len += sprintf ( buf+len, "Histogram of sizes (512B secs)\n"); + for ( i = 0; i < 16; i ++) { + len += sprintf ( buf+len, "%4ld ",dasd_io_secs[i] >> shift ); + } + len += sprintf ( buf+len, "\n"); + len += sprintf ( buf+len, "Histogram of I/O times\n"); + for ( i = 0; i < 16; i ++) { + len += sprintf ( buf+len, "%4ld ",dasd_io_times[i] >> shift ); + } + len += sprintf ( buf+len, "\n"); + len += sprintf ( buf+len, "Histogram of I/O times per sector\n"); + for ( i = 0; i < 16; i ++) { + len += sprintf ( buf+len, "%4ld ",dasd_io_timps[i] >> shift ); + } + len += sprintf ( buf+len, "\n"); + len += sprintf ( buf+len, "Histogram of I/O time till ssch\n"); + for ( i = 0; i < 16; i ++) { + len += sprintf ( buf+len, "%4ld ",dasd_io_time1[i] >> shift ); + } + len += sprintf ( buf+len, "\n"); + len += sprintf ( buf+len, "Histogram of I/O time between ssch and irq\n"); + for ( i = 0; i < 16; i ++) { + len += sprintf ( buf+len, "%4ld ",dasd_io_time2[i] >> shift ); + } + len += sprintf ( buf+len, "\n"); + len += sprintf ( buf+len, "Histogram of I/O time between ssch and irq per sector\n"); + for ( i = 0; i < 16; i ++) { + len += sprintf ( buf+len, "%4ld ",dasd_io_time2ps[i] >> shift ); + } + len += sprintf ( buf+len, "\n"); + len += sprintf ( buf+len, "Histogram of I/O time between irq and end\n"); + for ( i = 0; i < 16; i ++) { + len += sprintf ( buf+len, "%4ld ",dasd_io_time3[i] >> shift ); + } + len += sprintf ( buf+len, "\n"); + return len; +} +typedef +struct { + union { + unsigned long long clock; + struct { + unsigned int ts1; + unsigned int ts2 : 20; + unsigned int unused : 8; + unsigned int cpu : 4; + } __attribute__ ((packed)) s; + } __attribute__ ((packed)) u; + unsigned long caller_address; + unsigned long tag; +} __attribute__ ((packed)) dasd_debug_entry; + +static dasd_debug_entry *dasd_debug_area = NULL; +static dasd_debug_entry *dasd_debug_actual; +static spinlock_t debug_lock = SPIN_LOCK_UNLOCKED; + +void +dasd_debug ( unsigned long tag ) +{ + long flags; + dasd_debug_entry *d; + /* initialize in first call ... */ + if ( ! dasd_debug_area ) { + dasd_debug_actual = dasd_debug_area = + get_free_page (GFP_ATOMIC); + if ( ! dasd_debug_area ) { + PRINT_WARN("No debug area allocated\n"); + return; + } + memset (dasd_debug_area,0,PAGE_SIZE); + } + /* renormalize to page */ + spin_lock_irqsave(&debug_lock,flags); + dasd_debug_actual = (dasd_debug_entry *) + ( (unsigned long) dasd_debug_area + + ( ( (unsigned long)dasd_debug_actual - + (unsigned long)dasd_debug_area ) % 1808 ) ); + d = dasd_debug_actual ++; + spin_unlock_irqrestore(&debug_lock,flags); + /* write CPUID to lowest 12 bits of clock... */ + __asm__ __volatile__ ( "STCK %0\n" + "ST 14,%1\n" + :"=m" (d->u.clock), + "=m" (d->caller_address)); + d->tag = tag; + d->u.s.cpu = smp_processor_id(); +} + +int +dasd_proc_read_debug ( char * buf, char **start, + off_t off, int len, int dd) +{ + dasd_debug_entry *d; + char tag[9] = { 0, }; + long flags; + spin_lock_irqsave(&debug_lock,flags); + len = 0; + for( d = dasd_debug_area; + len < 4068 ; + d ++ ) { + if ( *(char*)(&d->tag) == 'D' ) { + memcpy(tag,&(d->tag),4); + tag[4]=0; + } + else { + sprintf(tag,"%08x",d->tag); + tag[8]=0; + } + len += sprintf ( buf+len, + "%lx %08x%05x %08lx (%8s)\n", + d->u.s.cpu, d->u.s.ts1, d->u.s.ts2, + d->caller_address,tag); + } + spin_unlock_irqrestore(&debug_lock,flags); + return len; +} diff -u --recursive --new-file v2.2.13/linux/drivers/s390/block/dasd_types.h linux/drivers/s390/block/dasd_types.h --- v2.2.13/linux/drivers/s390/block/dasd_types.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/block/dasd_types.h Tue Jan 4 10:12:18 2000 @@ -0,0 +1,303 @@ +/* 456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789. + * IBM CONFIDENTIAL + * File...........: linux/drivers/s390/block/dasd_types.h + * Author.........: Holger Smolinski + * Created........: 08/31/1999 + * Last Modified..: 09/29/1999 + * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999 + + * List of Changes: + - Initial Release as of 09/29/1999 + + * Description + + * Restrictions + + * Known Bugs + + * Todo-List + + */ + +#ifndef DASD_TYPES_H +#define DASD_TYPES_H + +#include "dasd.h" + +#include + +#include "../../../arch/s390/kernel/irq.h" + +#define CCW_DEFINE_EXTENT 0x63 +#define CCW_LOCATE_RECORD 0x43 +#define CCW_READ_DEVICE_CHARACTERISTICS 0x64 + +typedef +enum { + dasd_none = -1, +#ifdef CONFIG_DASD_FBA + dasd_fba, +#endif /* CONFIG_DASD_FBA */ +#ifdef CONFIG_DASD_CKD + dasd_ckd, +#endif /* CONFIG_DASD_CKD */ +#ifdef CONFIG_DASD_ECKD + dasd_eckd +#endif /* CONFIG_DASD_ECKD */ +} dasd_type_t; + +typedef +struct { + union { + __u8 c; + struct { + unsigned char reserved:1; + unsigned char overrunnable:1; + unsigned char burst_byte:1; + unsigned char data_chain:1; + unsigned char zeros:4; + } __attribute__ ((packed)) bits; + } __attribute__ ((packed)) mode; + union { + __u8 c; + struct { + unsigned char zero0:1; + unsigned char removable:1; + unsigned char shared:1; + unsigned char zero1:1; + unsigned char bam:1; + unsigned char hpsa:1; + unsigned char zeros:2; + } __attribute__ ((packed)) bits; + } __attribute__ ((packed)) features; + __u8 dev_class; + __u8 unit_type; + __u16 blk_size; + __u32 blk_per_cycl; + __u32 blk_per_bound; + __u32 blk_bdsa; + __u32 blk_hpsa; + __u16 reserved1; + __u16 blk_ce; + __u32 reserved2; + __u16 reserved3; +} __attribute__ ((packed, aligned (32))) + +dasd_fba_characteristics_t; + +typedef +struct { + __u16 cu_type; + struct { + unsigned char support:2; + unsigned char async:1; + unsigned char reserved:1; + unsigned char cache_info:1; + unsigned char model:3; + } __attribute__ ((packed)) cu_model; + __u16 dev_type; + __u8 dev_model; + struct { + unsigned char mult_burst:1; + unsigned char RT_in_LR:1; + unsigned char reserved1:1; + unsigned char RD_IN_LR:1; + unsigned char reserved2:4; + unsigned char reserved3:8; + unsigned char defect_wr:1; + unsigned char reserved4:2; + unsigned char striping:1; + unsigned char reserved5:4; + unsigned char cfw:1; + unsigned char reserved6:2; + unsigned char cache:1; + unsigned char dual_copy:1; + unsigned char dfw:1; + unsigned char reset_alleg:1; + unsigned char sense_down:1; + } __attribute__ ((packed)) facilities; + __u8 dev_class; + __u8 unit_type; + __u16 no_cyl; + __u16 trk_per_cyl; + __u8 sec_per_trk; + __u8 byte_per_track[3]; + __u16 home_bytes; + __u8 formula; + union { + struct { + __u8 f1; + __u16 f2; + __u16 f3; + } __attribute__ ((packed)) f_0x01; + struct { + __u8 f1; + __u8 f2; + __u8 f3; + __u8 f4; + __u8 f5; + } __attribute__ ((packed)) f_0x02; + } __attribute__ ((packed)) factors; + __u16 first_alt_trk; + __u16 no_alt_trk; + __u16 first_dia_trk; + __u16 no_dia_trk; + __u16 first_sup_trk; + __u16 no_sup_trk; + __u8 MDR_ID; + __u8 OBR_ID; + __u8 director; + __u8 rd_trk_set; + __u16 max_rec_zero; + __u8 reserved1; + __u8 RWANY_in_LR; + __u8 factor6; + __u8 factor7; + __u8 factor8; + __u8 reserved2[3]; + __u8 reserved3[10]; +} __attribute__ ((packed, aligned (32))) + +dasd_eckd_characteristics_t; + +#ifdef CONFIG_DASD_CKD +struct dasd_ckd_characteristics { + char info[64]; +}; + +#endif /* CONFIG_DASD_CKD */ + +#ifdef CONFIG_DASD_ECKD +struct dasd_eckd_characteristics { + char info[64]; +}; + +#endif /* CONFIG_DASD_ECKD */ + +typedef +union { + char __attribute__ ((aligned (32))) bytes[64]; +#ifdef CONFIG_DASD_FBA + dasd_fba_characteristics_t fba; +#endif /* CONFIG_DASD_FBA */ +#ifdef CONFIG_DASD_CKD + struct dasd_ckd_characteristics ckd; +#endif /* CONFIG_DASD_CKD */ +#ifdef CONFIG_DASD_ECKD + dasd_eckd_characteristics_t eckd; +#endif /* CONFIG_DASD_ECKD */ +} __attribute__ ((aligned (32))) + +dasd_characteristics_t; + +#define CQR_STATUS_EMPTY 0x00 +#define CQR_STATUS_FILLED 0x01 +#define CQR_STATUS_QUEUED 0x02 +#define CQR_STATUS_IN_IO 0x04 +#define CQR_STATUS_DONE 0x08 +#define CQR_STATUS_RETRY 0x10 +#define CQR_STATUS_ERROR 0x20 +#define CQR_STATUS_FAILED 0x40 +#define CQR_STATUS_SLEEP 0x80 + +#define CQR_FLAGS_SLEEP 0x01 +#define CQR_FLAGS_WAIT 0x02 +#define CQR_FLAGS_NOLOCK 0x04 +#define CQR_FLAGS_NORETRY 0x08 + +typedef +struct cqr_t { + unsigned int magic; /* magic number should be "DASD" */ + atomic_t status; /* current status of request */ + unsigned short retries; /* counter for retry in error case */ + unsigned short cplength;/* Length of channel program (CP) */ + unsigned short devindex;/* device number */ + unsigned short flags; /* Flags for execution */ + + void * data; /* additional data area for CP */ + ccw1_t *cpaddr; /* Address of CP */ + struct request *req; /* backpointer to struct request */ + struct cqr_t *next; /* forward chain in chanq */ + struct cqr_t *int4cqr; /* which cqr ist the nect PCI for? */ + unsigned long long buildclk; + unsigned long long startclk; + unsigned long long stopclk; + unsigned long long endclk; + devstat_t *dstat; /* savearea for devstat */ + spinlock_t lock; + int options; +} __attribute__ ((packed)) +cqr_t; + +typedef +struct { + unsigned long int kbytes; + unsigned int bp_sector; + unsigned int bp_block; + unsigned int blocks; + unsigned int s2b_shift; + unsigned int b2k_shift; + unsigned int first_sector; +} dasd_sizes_t; + +#define DASD_CHANQ_ACTIVE 0x01 +#define DASD_CHANQ_BUSY 0x02 +typedef +struct dasd_chanq_t { + volatile cqr_t *head; + volatile cqr_t *tail; + spinlock_t q_lock; /* lock for queue operations */ + spinlock_t f_lock; /* lock for flag operations */ + long lockflags; + atomic_t flags; + struct dasd_chanq_t *next_q; /* pointer to next queue */ +} __attribute__ ((packed, aligned (16))) +dasd_chanq_t; + +typedef +struct dasd_information_t { + devstat_t dev_status; + dasd_characteristics_t *rdc_data; + dasd_volume_label_t *label; + dasd_type_t type; + dev_info_t info; + dasd_sizes_t sizes; + dasd_chanq_t queue; + int open_count; + spinlock_t lock; + struct semaphore sem; + unsigned long flags; + int irq; + struct proc_dir_entry *proc_device; + union { + struct { + char dummy; + } fba; + struct { + char dummy; + } ckd; + struct { + int blk_per_trk; + } eckd; + } private; +} dasd_information_t; + +typedef struct { + int start_unit; + int stop_unit; + int blksize; +} format_data_t; + +typedef +struct { + int (*ck_devinfo) (dev_info_t *); + cqr_t *(*get_req_ccw) (int, struct request *); + cqr_t *(*rw_label) (int, int, char *); + int (*ck_characteristics) (dasd_characteristics_t *); + int (*fill_sizes) (int); + int (*dasd_format) (int, format_data_t *); +} dasd_operations_t; + +extern dasd_information_t *dasd_info[]; + +#endif /* DASD_TYPES_H */ diff -u --recursive --new-file v2.2.13/linux/drivers/s390/block/mdisk.c linux/drivers/s390/block/mdisk.c --- v2.2.13/linux/drivers/s390/block/mdisk.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/block/mdisk.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,697 @@ +/* + * drivers/s390/block/mdisk.c + * VM minidisk device driver. + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hp@de.ibm.com) + */ + + +#ifndef __KERNEL__ +# define __KERNEL__ +#endif + +#define __NO_VERSION__ +#include + +char kernel_version [] = UTS_RELEASE; + +#include +#include +#include /* printk() */ +#include /* kmalloc() */ +#include /* vmalloc() */ +#include /* everything... */ +#include /* error codes */ +#include +#include /* size_t */ +#include /* O_ACCMODE */ +#include /* HDIO_GETGEO */ +#include /* initfunc */ +#include +#include + +#include /* cli(), *_flags */ +#include /* access_ok */ +#include /* virt_to_phys */ + + /* Added statement HSM 12/03/99 */ +#include "../../../arch/s390/kernel/irq.h" + +#define MAJOR_NR MDISK_MAJOR /* force definitions on in blk.h */ + +#include + + +#include "mdisk.h" /* local definitions */ + +/* + * structure for all device specific information + */ + +typedef struct mdisk_Dev { + u32 vdev; /* vdev of mindisk */ + u32 size; /* size in blocks */ + u32 status; /* status of last io operation */ + u32 nr_bhs; /* number of buffer of last io operation */ + u32 blksize; /* blksize from minidisk */ + u32 blkmult; /* multiplier between blksize and 512 HARDSECT */ + u32 blkshift; /* loe2 of multiplier above */ + /* + * each device has own iob and bio, + * it's possible to run io in parallel + * not used yet due to only one CURRENT per MAJOR + */ + + mdisk_rw_io_t* iob; /* each device has it own iob and bio */ + mdisk_bio_t* bio; + /* Added statement HSM 12/03/99 */ + devstat_t dev_status; /* Here we hold the I/O status */ + + int usage; /* usage counter */ + + struct tq_struct tqueue; /* per device task queue */ +} mdisk_Dev; + + +/* + * appended to global structures in mdisk_init; + */ + +static int mdisk_blksizes[MDISK_DEVS]; +static int mdisk_sizes[MDISK_DEVS] = { 0 }; +static int mdisk_hardsects[MDISK_DEVS]; +static int mdisk_maxsectors[MDISK_DEVS]; + +/* + * structure hold device specific information + */ + +static mdisk_Dev mdisk_devices[MDISK_DEVS]; +static mdisk_rw_io_t mdisk_iob[MDISK_DEVS] __attribute__ ((aligned(8))); +static mdisk_bio_t mdisk_bio[MDISK_DEVS][256]__attribute__ ((aligned(8))); + + +/* + * Parameter parsing + */ +struct { + long vdev[MDISK_DEVS]; + long size[MDISK_DEVS]; + long offset[MDISK_DEVS]; + long blksize[MDISK_DEVS]; +} mdisk_setup_data; + +/* + * Parameter parsing function, called from init/main.c + * vdev : virtual device number + * size : size in kbyte + * offset : offset after which minidisk is available + * blksize : blocksize minidisk is formated + * Format is: mdisk=:::,::... + * ::: can be shortened to : with offset=0,blksize=512 + */ +__initfunc(void mdisk_setup(char *str,int *ints)) +{ + char *cur = str; + int vdev, size, offset=0,blksize; + static i = 0; + if (!i) + memset(&mdisk_setup_data,0,sizeof(mdisk_setup_data)); + + while (*cur != 0) { + blksize=MDISK_HARDSECT; + vdev = size = offset = 0; + if (!isxdigit(*cur)) goto syntax_error; + vdev = simple_strtoul(cur,&cur,16); + if (*cur++ != ':') goto syntax_error; + if (!isxdigit(*cur)) goto syntax_error; + size = simple_strtoul(cur,&cur,16); + if (*cur == ':') { /* another colon -> offset specified */ + cur++; + if (!isxdigit(*cur)) goto syntax_error; + offset = simple_strtoul(cur,&cur,16); + if (*cur == ':') { /* another colon -> blksize */ + cur++; + if (!isxdigit(*cur)) goto syntax_error; + blksize = simple_strtoul(cur,&cur,16); + } + } + if (*cur != ',' && *cur != 0) goto syntax_error; + if (*cur == ',') cur++; + if (i >= MDISK_DEVS) { + printk(KERN_WARNING "mnd: too many devices\n"); + return; + } + mdisk_setup_data.vdev[i] = vdev; + mdisk_setup_data.size[i] = size; + mdisk_setup_data.offset[i] = offset; + mdisk_setup_data.blksize[i] = blksize; + + i++; + } + + return; + +syntax_error: + printk(KERN_WARNING "mnd: syntax error in parameter string: %s\n", str); + return; +} + +/* + * Open and close + */ + +static int mdisk_open (struct inode *inode, struct file *filp) +{ + mdisk_Dev *dev; /* device information */ + int num = MINOR(inode->i_rdev); + + /* + * size 0 means device not installed + */ + if ((num >= MDISK_DEVS) || (mdisk_sizes[num] == 0)) + return -ENODEV; + MOD_INC_USE_COUNT; + dev = &mdisk_devices[num]; + dev->usage++; + return 0; /* success */ +} + +static int mdisk_release (struct inode *inode, struct file *filp) +{ + mdisk_Dev *dev = &mdisk_devices[MINOR(inode->i_rdev)]; + + /* + * flush device + */ + + fsync_dev(inode->i_rdev); + dev->usage--; + MOD_DEC_USE_COUNT; + return 0; +} + + +/* + * The mdisk() implementation + */ + +static int mdisk_ioctl (struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + int err,rc, size=0; + struct hd_geometry *geo = (struct hd_geometry *)arg; + mdisk_Dev *dev = mdisk_devices + MINOR(inode->i_rdev); + + switch(cmd) { + + case BLKGETSIZE: + rc = copy_to_user ((long *) arg, &dev->size, sizeof (long)); + printk(KERN_WARNING "mnd: ioctl BLKGETSIZE %d\n",dev->size); + return rc; + case BLKFLSBUF: /* flush */ + if (!suser()) return -EACCES; /* only root */ + fsync_dev(inode->i_rdev); + invalidate_buffers(inode->i_rdev); + return 0; + + case BLKRAGET: /* return the readahead value */ + if (!arg) return -EINVAL; + err = access_ok(VERIFY_WRITE, (long *) arg, sizeof(long)); + if (err) return err; + put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg); + return 0; + + case BLKRASET: /* set the readahead value */ + if (!suser()) return -EACCES; + if (arg > 0xff) return -EINVAL; /* limit it */ + read_ahead[MAJOR(inode->i_rdev)] = arg; + return 0; + + case BLKRRPART: /* re-read partition table: can't do it */ + return -EINVAL; + + RO_IOCTLS(inode->i_rdev, arg); /* the default RO operations */ + + case HDIO_GETGEO: + /* + * get geometry of device -> linear + */ + size = dev->size; + if (geo==NULL) return -EINVAL; + err = access_ok(VERIFY_WRITE, geo, sizeof(*geo)); + if (err) return err; + put_user(1, &geo->cylinders); + put_user(1, &geo->heads); + put_user(size, &geo->sectors); + put_user(0, &geo->start); + return 0; + } + + return -EINVAL; /* unknown command */ +} + +/* + * The file operations + */ + +static struct file_operations mdisk_fops = { + NULL, /* lseek: default */ + block_read, + block_write, + NULL, /* mdisk_readdir */ + NULL, /* mdisk_select */ + mdisk_ioctl, + NULL, /* mdisk_mmap */ + mdisk_open, + NULL, /* mdisk_flush */ + mdisk_release, + block_fsync, + NULL, /* mdisk_fasync */ + NULL, /* mdisk_check_change */ + NULL, /* mdisk_revalidate */ + NULL, /* mdisk_lock */ +}; + +/* + * The 'low level' IO function + */ + + +static __inline__ int +dia250(void* iob,int cmd) +{ + int rc; + + iob = (void*) virt_to_phys(iob); + + asm volatile (" lr 2,%1\n" + " lr 3,%2\n" + " .long 0x83230250\n" + " lr %0,3" + : "=d" (rc) + : "d" (iob) , "d" (cmd) + : "2", "3" ); + return rc; +} + +/* + * Init of minidisk device + */ + +static __inline__ int +mdisk_init_io(mdisk_Dev *dev,int blocksize,int offset,int size) +{ + mdisk_init_io_t *iob = (mdisk_init_io_t*) dev->iob; + int rc; + + memset(iob,0,sizeof(mdisk_init_io_t)); + + iob->dev_nr = dev->vdev; + iob->block_size = blocksize; + iob->offset = offset; + iob->start_block= 0; + iob->end_block = size; + + rc = dia250(iob,INIT_BIO); + + /* + * clear for following io once + */ + + memset(iob,0,sizeof(mdisk_rw_io_t)); + + return rc; +} + +/* + * release of minidisk device + */ + +static __inline__ int +mdisk_term_io(mdisk_Dev *dev) +{ + mdisk_init_io_t *iob = (mdisk_init_io_t*) dev->iob; + + memset(iob,0,sizeof(mdisk_init_io_t)); + + iob->dev_nr = dev->vdev; + + return dia250(iob,TERM_BIO); +} + +/* + * setup and start of minidisk io request + */ + +static __inline__ int +mdisk_rw_io_clustered (mdisk_Dev *dev, + mdisk_bio_t* bio_array, + int length, + int req, + int sync) +{ + int rc; + mdisk_rw_io_t *iob = dev->iob; + + iob->dev_nr = dev->vdev; + iob->key = 0; + iob->flags = sync; + + iob->block_count = length; + iob->interrupt_params = req; + iob->bio_list = virt_to_phys(bio_array); + + rc = dia250(iob,RW_BIO); + rc = (rc == 8) ? 0 : rc; + return rc; +} + + +/* + * this handles a clustered request in success case + * all buffers are detach and marked uptodate to the kernel + * then CURRENT->bh is set to the last processed but not + * update buffer + */ + +static __inline__ void +mdisk_end_request(int nr_bhs) +{ + int i; + struct buffer_head *bh; + struct request *req; + + if (nr_bhs != 1) { + req = CURRENT; + bh = req->bh; + + for (i=0;ibh = bh->b_reqnext; + bh->b_reqnext = NULL; + bh->b_end_io(bh,1); + bh = req->bh; + } + + /* + * set CURRENT to last processed, not marked buffer + */ + req->buffer = bh->b_data; + req->current_nr_sectors = bh->b_size >> 9; + CURRENT = req; + } + end_request(1); +} + + + +/* + * Block-driver specific functions + */ + +void mdisk_request(void) +{ + mdisk_Dev *dev; + mdisk_bio_t *bio; + struct buffer_head *bh; + unsigned int sector, nr, offset; + int rc,rw,i; + + i = 0; + while(CURRENT) { + INIT_REQUEST; + + /* Check if the minor number is in range */ + if (DEVICE_NR(CURRENT_DEV) > MDISK_DEVS) { + static int count = 0; + if (count++ < 5) /* print the message at most five times */ + printk(KERN_WARNING "mnd: request for minor %d out of range\n", + DEVICE_NR(CURRENT_DEV) ) ; + end_request(0); + continue; + } + + /* + * Pointer to device structure, from the static array + */ + dev = mdisk_devices + DEVICE_NR(CURRENT_DEV); + + /* + * check, if operation is past end of devices + */ + if (CURRENT->nr_sectors + CURRENT->sector > dev->size) { + static int count = 0; + if (count++ < 5) + printk(KERN_WARNING "mnd%c: request past end of device\n", + DEVICE_NR(CURRENT_DEV)); + end_request(0); + continue; + } + + /* + * do command (read or write) + */ + switch(CURRENT->cmd) { + case READ: + rw = MDISK_READ_REQ; + break; + case WRITE: + rw = MDISK_WRITE_REQ; + break; + default: + /* can't happen */ + end_request(0); + continue; + } + + /* + * put the clustered requests in mdisk_bio array + * nr_sectors is checked against max_sectors in make_request + * nr_sectors and sector are always blocks of 512 + * but bh_size depends on the filesystems size + */ + sector = CURRENT->sector>>dev->blkshift; + bh = CURRENT->bh; + bio = dev->bio; + dev->nr_bhs = 0; + + /* + * sector is translated to block in minidisk context + * + */ + offset = 0; + + + + for (nr = 0,i = 0; + nr < CURRENT->nr_sectors && bh; + nr+=dev->blkmult, sector++,i++) { + memset(&bio[i], 0, sizeof(mdisk_bio_t)); + bio[i].type = rw; + bio[i].block_number = sector; + bio[i].buffer = virt_to_phys(bh->b_data+offset); + offset += dev->blksize; + if (bh->b_size <= offset) { + offset = 0; + bh = bh->b_reqnext; + dev->nr_bhs++; + } + } + + if (( rc = mdisk_rw_io_clustered(dev, &bio[0], i, + (unsigned long) dev, +#ifdef CONFIG_MDISK_SYNC + MDISK_SYNC +#else + MDISK_ASYNC +#endif + )) != 0 ) { + printk(KERN_WARNING "mnd%c: %s request failed rc %d" + " sector %ld nr_sectors %ld \n", + DEVICE_NR(CURRENT_DEV), + rw == MDISK_READ_REQ ? "read" : "write", + rc, CURRENT->sector, CURRENT->nr_sectors); + end_request(0); + continue; + } + i = 0; + /* + * Synchron: looping to end of request (INIT_REQUEST has return) + * Asynchron: end_request done in bottom half + */ +#ifdef CONFIG_MDISK_SYNC + mdisk_end_request(dev->nr_bhs); +#else + return; +#endif + } +} + + +/* + * mdisk interrupt handler called when read/write request finished + * queues and marks a bottom half. + * + */ +void do_mdisk_interrupt(void) +{ + u16 code; + mdisk_Dev *dev; + + code = S390_lowcore.cpu_addr; + + if ((code >> 8) != 0x03) { + printk("mnd: wrong sub-interruption code %d",code>>8); + return; + } + + /* + * pointer to devives structure given as external interruption + * parameter + */ + dev = (mdisk_Dev*) S390_lowcore.ext_params; + dev->status = code & 0x00ff; + + queue_task(&dev->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +/* + * the bottom half checks the status of request + * on success it calls end_request and calls mdisk_request + * if more transfer to do + */ + +static void +do_mdisk_bh(void *data) +{ + mdisk_Dev *dev = (mdisk_Dev*) data; + unsigned long flags; + + spin_lock_irqsave(&io_request_lock, flags); + /* + * check for status of asynchronous rw + */ + if (dev->status != 0x00) { + printk("mnd: status of async rw %d",dev->status); + end_request(0); + } else { + /* + * end request for clustered requests + */ + mdisk_end_request(dev->nr_bhs); + } + + /* + * if more to do, call mdisk_request + */ + if (CURRENT) + mdisk_request(); + spin_unlock_irqrestore(&io_request_lock, flags); +} + +void /* Added fuction HSM 12/03/99 */ +mdisk_handler (int cpu, void *ds, struct pt_regs *regs) +{ + printk (KERN_ERR "mnd: received I/O interrupt... shouldn't happen\n"); +} + +__initfunc(int mdisk_init(void)) +{ + int rc,i; + mdisk_Dev *dev; + + /* + * register block device + */ + if (register_blkdev(MAJOR_NR,"mnd",&mdisk_fops) < 0) { + printk("mnd: unable to get major %d for mini disk\n" + ,MAJOR_NR); + return MAJOR_NR; + } + + /* + * setup global major dependend structures + */ + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + + /* + * setup sizes for available devices + */ + read_ahead[MAJOR_NR] = MDISK_RAHEAD; /* 8 sector (4kB) read-ahead */ + blk_size[MAJOR_NR] = mdisk_sizes; /* size of reserved mdisk */ + blksize_size[MAJOR_NR] = mdisk_blksizes; /* blksize of device */ + hardsect_size[MAJOR_NR] = mdisk_hardsects; + max_sectors[MAJOR_NR] = mdisk_maxsectors; + + for (i=0;ibio=mdisk_bio[i]; + dev->iob=&mdisk_iob[i]; + dev->vdev = mdisk_setup_data.vdev[i]; + dev->size = mdisk_setup_data.size[i] * 2; /* buffer 512 b */ + dev->blksize = mdisk_setup_data.blksize[i]; + dev->tqueue.routine = do_mdisk_bh; + dev->tqueue.data = dev; + dev->blkmult = dev->blksize/512; + dev->blkshift = + dev->blkmult==1?0: + dev->blkmult==2?1: + dev->blkmult==4?2: + dev->blkmult==8?3:-1; + + mdisk_blksizes[i] = mdisk_setup_data.blksize[i]; + mdisk_hardsects[i] = mdisk_setup_data.blksize[i]; + + /* + * max sectors for one clustered req + */ + mdisk_maxsectors[i] = MDISK_MAXSECTORS*dev->blkmult; + + rc = mdisk_init_io(dev, + mdisk_setup_data.blksize[i], + mdisk_setup_data.offset[i],/* offset in vdev*/ + dev->size>>dev->blkshift /* size in blocks */ + ); + if (rc > 4) { + printk("mnd%c: init failed (rc: %d)\n",'a'+i,rc); + mdisk_sizes[i] = 0; + continue; + } + + /* + * set vdev in device structure for further rw access + * vdev and size given by linload + */ + printk("mnd%c: register device at major %X with %d blocks %d blksize \n", + 'a' + i, MAJOR_NR, dev->size>>dev->blkshift,dev->blkmult*512); + } + + /* + * enable service-signal external interruptions, + * Control Register 0 bit 22 := 1 + * (besides PSW bit 7 must be set to 1 somewhere for external + * interruptions) + */ + ctl_set_bit(0, 9); + + return 0; +} diff -u --recursive --new-file v2.2.13/linux/drivers/s390/block/mdisk.h linux/drivers/s390/block/mdisk.h --- v2.2.13/linux/drivers/s390/block/mdisk.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/block/mdisk.h Tue Jan 4 10:12:18 2000 @@ -0,0 +1,71 @@ +/* + * drivers/s390/block/mdisk.h + * VM minidisk device driver. + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hp@de.ibm.com) + */ + +#include +#include + +#define MDISK_DEVS 4 /* for disks */ +#define MDISK_RAHEAD 8 /* read ahead */ +#define MDISK_BLKSIZE 1024 /* 1k blocks */ +#define MDISK_HARDSECT 512 /* FIXME -- 512 byte blocks */ +#define MDISK_MAXSECTORS 256 /* max sectors for one request */ + + + +/* + * low level io defines for diagnose 250 + */ + +#define MDISK_WRITE_REQ 0x01 +#define MDISK_READ_REQ 0x02 + +#define MDISK_SYNC 0x01 +#define MDISK_ASYNC 0x03 +#define INIT_BIO 0x00 +#define RW_BIO 0x01 +#define TERM_BIO 0x02 + +/* + * This stucture is used for clustered request + * up to 256 different request can be handled with one invocation + */ + +typedef struct { + u8 type; + u8 status; + u16 spare1; + u32 block_number; + u32 alet; + u32 buffer; +} mdisk_bio_t; + +typedef struct { + u16 dev_nr; + u16 spare1[11]; + u32 block_size; + u32 offset; + u32 start_block; + u32 end_block; + u32 spare2[6]; +} mdisk_init_io_t; + +typedef struct { + u16 dev_nr; + u16 spare1[11]; + u8 key; + u8 flags; + u16 spare2; + u32 block_count; + u32 alet; + u32 bio_list; + u32 interrupt_params; + u32 spare3[5]; +} mdisk_rw_io_t; + + diff -u --recursive --new-file v2.2.13/linux/drivers/s390/char/Makefile linux/drivers/s390/char/Makefile --- v2.2.13/linux/drivers/s390/char/Makefile Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/char/Makefile Tue Jan 4 10:12:18 2000 @@ -0,0 +1,16 @@ +all: s390-char.o + +CFLAFS += +O_TARGET := s390-char.o +O_OBJS := +M_OBJS := + +ifeq ($(CONFIG_3215_CONSOLE),y) + O_OBJS += con3215.o +endif + +ifeq ($(CONFIG_HWC),y) + O_OBJS += hwc_con.o hwc_rw.o hwc_tty.o +endif + +include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.2.13/linux/drivers/s390/char/con3215.c linux/drivers/s390/char/con3215.c --- v2.2.13/linux/drivers/s390/char/con3215.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/char/con3215.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,1106 @@ +/* + * drivers/s390/char/con3215.c + * 3215 line mode terminal driver. + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../../../arch/s390/kernel/cpcmd.h" +#include "../../../arch/s390/kernel/irq.h" + +#define NR_3215 1 +#define NR_3215_REQ (4*NR_3215) +#define RAW3215_BUFFER_SIZE 65536 /* output buffer size */ +#define RAW3215_INBUF_SIZE 256 /* input buffer size */ +#define RAW3215_MIN_SPACE 128 /* minimum free space for wakeup */ +#define RAW3215_MIN_WRITE 1024 /* min. length for immediate output */ +#define RAW3215_MAX_CCWLEN 3968 /* max. bytes to write with one ccw */ +#define RAW3215_NR_CCWS ((RAW3215_BUFFER_SIZE/RAW3215_MAX_CCWLEN)+2) +#define RAW3215_TIMEOUT HZ/10 /* time for delayed output */ + +#define RAW3215_FIXED 1 /* 3215 console device is not be freed */ +#define RAW3215_ACTIVE 2 /* set if the device is in use */ +#define RAW3215_WORKING 4 /* set if a request is being worked on */ +#define RAW3215_THROTTLED 8 /* set if reading is disabled */ +#define RAW3215_STOPPED 16 /* set if writing is disabled */ +#define RAW3215_CLOSING 32 /* set while in close process */ +#define RAW3215_TIMER_RUNS 64 /* set if the output delay timer is on */ +#define RAW3215_FLUSHING 128 /* set to flush buffer (no delay) */ + +struct _raw3215_info; /* forward declaration ... */ + +/* + * Request types for a 3215 device + */ +typedef enum { + RAW3215_READ, RAW3215_WRITE +} raw3215_type; + +/* + * Request structure for a 3215 device + */ +typedef struct _raw3215_req { + raw3215_type type; /* type of the request */ + int start, end; /* start/end index into output buffer */ + int residual; /* residual count for read request */ + ccw1_t ccws[RAW3215_NR_CCWS]; /* space for the channel program */ + struct _raw3215_info *info; /* pointer to main structure */ + struct _raw3215_req *next; /* pointer to next request */ +} raw3215_req __attribute__ ((aligned(8))); + +typedef struct _raw3215_info { + int flags; /* state flags */ + int irq; /* interrupt number to do_IO */ + char *buffer; /* pointer to output buffer */ + char *inbuf; /* pointer to input buffer */ + int head; /* first free byte in output buffer */ + int count; /* number of bytes in output buffer */ + devstat_t devstat; /* device status structure for do_IO */ + struct tty_struct *tty; /* pointer to tty structure if present */ + struct tq_struct tqueue; /* task queue to bottom half */ + raw3215_req *queued_read; /* pointer to queued read requests */ + raw3215_req *queued_write; /* pointer to queued write requests */ + struct wait_queue *empty_wait;/* wait queue for flushing */ + struct timer_list timer; /* timer for delayed output */ + char *message; /* pending message from raw3215_irq */ + int msg_dstat; /* dstat for pending message */ + int msg_cstat; /* cstat for pending message */ +} raw3215_info; + +static raw3215_info *raw3215[NR_3215]; /* array of 3215 devices structures */ +static raw3215_req *raw3215_freelist; /* list of free request structures */ +static spinlock_t raw3215_freelist_lock;/* spinlock to protect free list */ + +static struct tty_driver tty3215_driver; +static struct tty_struct *tty3215_table[NR_3215]; +static struct termios *tty3215_termios[NR_3215]; +static struct termios *tty3215_termios_locked[NR_3215]; +static int tty3215_refcount; + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +/* + * Get a request structure from the free list + */ +extern inline raw3215_req *raw3215_alloc_req(void) { + raw3215_req *req; + unsigned long flags; + + spin_lock_irqsave(&raw3215_freelist_lock, flags); + req = raw3215_freelist; + raw3215_freelist = req->next; + spin_unlock_irqrestore(&raw3215_freelist_lock, flags); + return req; +} + +/* + * Put a request structure back to the free list + */ +extern inline void raw3215_free_req(raw3215_req *req) { + unsigned long flags; + + spin_lock_irqsave(&raw3215_freelist_lock, flags); + req->next = raw3215_freelist; + raw3215_freelist = req; + spin_unlock_irqrestore(&raw3215_freelist_lock, flags); +} + +/* + * Get a write request structure. That is either a new or the last + * queued write request. The request structure is set up in + * raw3215_mk_write_ccw. + */ +static raw3215_req *raw3215_mk_write_req(raw3215_info *raw) +{ + raw3215_req *req; + + /* check if there is a queued write request */ + req = raw->queued_write; + if (req == NULL) { + /* no queued write request, use new req structure */ + req = raw3215_alloc_req(); + req->type = RAW3215_WRITE; + req->info = raw; + req->start = raw->head; + } else + raw->queued_write = NULL; + return req; +} + +/* + * Get a read request structure. If there is a queued read request + * it is used, but that shouldn't happen because a 3215 terminal + * won't accept a new read before the old one is completed. + */ +static raw3215_req *raw3215_mk_read_req(raw3215_info *raw) +{ + raw3215_req *req; + + /* there can only be ONE read request at a time */ + req = raw->queued_read; + if (req == NULL) { + /* no queued read request, use new req structure */ + req = raw3215_alloc_req(); + req->type = RAW3215_READ; + req->info = raw; + } else + raw->queued_read = NULL; + return req; +} + +/* + * Set up a write request with the information from the main structure. + * A ccw chain is created that writes everything in the output buffer + * to the 3215 device. + */ +static int raw3215_mk_write_ccw(raw3215_info *raw, raw3215_req *req) +{ + ccw1_t *ccw; + int len, count, ix; + + ccw = req->ccws; + req->end = (raw->head - 1) & (RAW3215_BUFFER_SIZE - 1); + len = ((req->end - req->start) & (RAW3215_BUFFER_SIZE - 1)) + 1; + ix = req->start; + while (len > 0) { + if (ccw > req->ccws) + ccw[-1].flags |= 0x40; /* use command chaining */ + ccw->cmd_code = 0x01; /* write, auto carrier return */ + ccw->flags = 0x20; /* ignore incorrect length ind. */ + ccw->cda = + (void *) virt_to_phys(raw->buffer + ix); + count = (len > RAW3215_MAX_CCWLEN) ? + RAW3215_MAX_CCWLEN : len; + if (ix + count > RAW3215_BUFFER_SIZE) + count = RAW3215_BUFFER_SIZE-ix; + ccw->count = count; + len -= count; + ix = (ix + count) & (RAW3215_BUFFER_SIZE - 1); + ccw++; + } + if (ccw > req->ccws) + ccw[-1].flags |= 0x40; /* use command chaining */ + ccw->cmd_code = 0x03; /* nop at the end */ + ccw->flags = 0x20; /* ignore incorrect length */ + ccw->cda = NULL; /* no data address for nop */ + ccw->count = 0; /* no count for nop */ + return len; +} + +/* + * Set up a read request that reads up to 160 byte from the 3215 device. + */ +static void raw3215_mk_read_ccw(raw3215_info *raw, raw3215_req *req) +{ + ccw1_t *ccw; + + ccw = req->ccws; + ccw->cmd_code = 0x0A; /* read inquiry */ + ccw->flags = 0x60; /* ignore incorrect length & command chaining */ + ccw->count = 160; + ccw->cda = (void *) virt_to_phys(raw->inbuf); + ccw++; + ccw->cmd_code = 0x03; /* nop at the end */ + ccw->flags = 0x20; /* ignore incorrect length ind. */ + ccw->cda = NULL; /* no data address for nop */ + ccw->count = 0; /* no count for nop */ +} + +/* + * Start a read or a write request + */ +static void raw3215_start_io(raw3215_info *raw) +{ + raw3215_req *req; + int res; + + req = raw->queued_read; + if (req != NULL && + !(raw->flags & (RAW3215_WORKING | RAW3215_THROTTLED))) { + /* dequeue request */ + raw->queued_read = NULL; + res = do_IO(raw->irq, req->ccws, (__u32) req, 0, 0); + if (res != 0) { + /* do_IO failed, put request back to queue */ + raw->queued_read = req; + } else { + raw->flags |= RAW3215_WORKING; + } + } + req = raw->queued_write; + if (req != NULL && + !(raw->flags & (RAW3215_WORKING | RAW3215_STOPPED))) { + /* dequeue request */ + raw->queued_write = NULL; + res = do_IO(raw->irq, req->ccws, (__u32) req, 0, 0); + if (res != 0) { + /* do_IO failed, put request back to queue */ + raw->queued_write = req; + } else { + raw->flags |= RAW3215_WORKING; + } + } +} + +/* + * Function to start a delayed output after RAW3215_TIMEOUT seconds + */ +static void raw3215_timeout(unsigned long __data) +{ + raw3215_info *raw = (raw3215_info *) __data; + unsigned long flags; + + s390irq_spin_lock_irqsave(raw->irq, flags); + if (raw->flags & RAW3215_TIMER_RUNS) { + del_timer(&raw->timer); + raw->flags &= ~RAW3215_TIMER_RUNS; + raw3215_start_io(raw); + } + s390irq_spin_unlock_irqrestore(raw->irq, flags); +} + +/* + * Function to conditionally start an IO. A read is started immediatly, + * a write is only started immediatly if the flush flag is on or the + * amount of data is bigger than RAW3215_MIN_WRITE. If a write is not + * done immediatly a timer is started with a delay of RAW3215_TIMEOUT. + */ +extern inline void raw3215_try_io(raw3215_info *raw) +{ + if (!(raw->flags & RAW3215_ACTIVE)) + return; + if (raw->queued_read != NULL) + raw3215_start_io(raw); + else if (raw->queued_write != NULL) { + if (raw->count >= RAW3215_MIN_WRITE || + (raw->flags & RAW3215_FLUSHING)) { + /* execute write requests bigger than minimum size */ + raw3215_start_io(raw); + if (raw->flags & RAW3215_TIMER_RUNS) { + del_timer(&raw->timer); + raw->flags &= ~RAW3215_TIMER_RUNS; + } + } else if (!(raw->flags & RAW3215_TIMER_RUNS)) { + /* delay small writes */ + init_timer(&raw->timer); + raw->timer.expires = RAW3215_TIMEOUT + jiffies; + raw->timer.data = (unsigned long) raw; + raw->timer.function = raw3215_timeout; + add_timer(&raw->timer); + raw->flags |= RAW3215_TIMER_RUNS; + } + } +} + +/* + * The bottom half handler routine for 3215 devices. It tries to start + * the next IO and wakes up processes waiting on the tty. + */ +static void raw3215_softint(void *data) +{ + raw3215_info *raw; + struct tty_struct *tty; + unsigned long flags; + + raw = (raw3215_info *) data; + s390irq_spin_lock_irqsave(raw->irq, flags); + raw3215_try_io((raw3215_info *) data); + s390irq_spin_unlock_irqrestore(raw->irq, flags); + /* Check for pending message from raw3215_irq */ + if (raw->message != NULL) { + printk(raw->message, raw->irq, raw->msg_dstat, raw->msg_cstat); + raw->message = NULL; + } + tty = raw->tty; + if (tty != NULL && + RAW3215_BUFFER_SIZE - raw->count >= RAW3215_MIN_SPACE) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); + } +} + +/* + * Find the raw3215_info structure associated with irq + */ +static inline raw3215_info *raw3215_find_info(int irq) { + raw3215_info *raw; + int i; + + for (i = 0; i < NR_3215; i++) { + raw = raw3215[i]; + if (raw != NULL && raw->irq == irq && + (raw->flags & RAW3215_ACTIVE)) + break; + } + return (i >= NR_3215) ? NULL : raw; +} + +/* + * Interrupt routine, called from Ingo's I/O layer + */ +static void raw3215_irq(int irq, void *int_parm, struct pt_regs *regs) +{ + raw3215_info *raw; + raw3215_req *req; + struct tty_struct *tty; + devstat_t *stat; + int cstat, dstat; + int count; + + stat = (devstat_t *) int_parm; + req = (raw3215_req *) stat->intparm; + cstat = stat->cstat; + dstat = stat->dstat; + if (cstat != 0) { + raw = raw3215_find_info(irq); + if (raw != NULL) { + raw->message = KERN_WARNING + "Got nonzero channel status in raw3215_irq " + "(dev %i, dev sts 0x%2x, sch sts 0x%2x)"; + raw->msg_dstat = dstat; + raw->msg_cstat = cstat; + queue_task(&raw->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + } + if (dstat & 0x01) { /* we got a unit exception */ + raw = raw3215_find_info(irq); + if (raw != NULL) { + raw->message = KERN_WARNING + "Got a unit exception in raw3215_irq " + "(dev %i, dev sts 0x%2x, sch sts 0x%2x)"; + raw->msg_dstat = dstat; + raw->msg_cstat = cstat; + queue_task(&raw->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + dstat &= ~0x01; + } + switch (dstat) { + case 0x80: + if (cstat != 0) + break; + /* Attention interrupt, someone hit the enter key */ + if ((raw = raw3215_find_info(irq)) == NULL) + return; /* That shouldn't happen ... */ + /* Setup a read request */ + req = raw3215_mk_read_req(raw); + raw3215_mk_read_ccw(raw, req); + raw->queued_read = req; + queue_task(&raw->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + break; + case 0x08: + case 0x0C: + /* Channel end interrupt. */ + raw = req->info; + if (req->type == RAW3215_READ) { + /* store residual count, then wait for device end */ + req->residual = stat->rescnt; + } + if (dstat == 0x08) + break; + case 0x04: + /* Device end interrupt. */ + raw = req->info; + if (req->type == RAW3215_READ && raw->tty != NULL) { + tty = raw->tty; + count = 160 - req->residual; + if (count >= TTY_FLIPBUF_SIZE - tty->flip.count) + count = TTY_FLIPBUF_SIZE - tty->flip.count - 1; + EBCASC(raw->inbuf, count); + if (count == 2 && + strncmp(raw->inbuf, "^c", 2) == 0) { + /* emulate a control C = break */ + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_NORMAL; + *tty->flip.char_buf_ptr++ = INTR_CHAR(tty); + tty_flip_buffer_push(raw->tty); + } else if (count == 2 && + strncmp(raw->inbuf, "^d", 2) == 0) { + /* emulate a control D = end of file */ + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_NORMAL; + *tty->flip.char_buf_ptr++ = EOF_CHAR(tty); + tty_flip_buffer_push(raw->tty); + } else if (count == 2 && + strncmp(raw->inbuf, "^z", 2) == 0) { + /* emulate a control Z = suspend */ + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_NORMAL; + *tty->flip.char_buf_ptr++ = SUSP_CHAR(tty); + tty_flip_buffer_push(raw->tty); + } else { + memcpy(tty->flip.char_buf_ptr, + raw->inbuf, count); + if (count < 2 || + strncmp(raw->inbuf+count-2, "^n", 2)) { + /* don't add the auto \n */ + tty->flip.char_buf_ptr[count] = '\n'; + memset(tty->flip.flag_buf_ptr, + TTY_NORMAL, count + 1); + count++; + } else + count-=2; + tty->flip.char_buf_ptr += count; + tty->flip.flag_buf_ptr += count; + tty->flip.count += count; + tty_flip_buffer_push(raw->tty); + } + } else if (req->type == RAW3215_WRITE) { + raw->count -= ((req->end - req->start) & + (RAW3215_BUFFER_SIZE - 1)) + 1; + } + raw->flags &= ~RAW3215_WORKING; + raw3215_free_req(req); + /* check for empty wait */ + if (raw->empty_wait != NULL && + raw->queued_write == NULL && + raw->queued_read == NULL) { + wake_up_interruptible(&raw->empty_wait); + raw->empty_wait = NULL; + } + queue_task(&raw->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + break; + default: + if (dstat & 0x04) { + /* Strange interrupt, I'll do my best to clean up */ + if (req != NULL && req->info != NULL && + req->type == RAW3215_WRITE) { + req->info->count -= ((req->end - req->start) & + (RAW3215_BUFFER_SIZE-1))+1; + } + } + raw = raw3215_find_info(irq); + if (raw != NULL) { + raw->message = KERN_WARNING + "Spurious interrupt in in raw3215_irq " + "(dev %i, dev sts 0x%2x, sch sts 0x%2x)"; + raw->msg_dstat = dstat; + raw->msg_cstat = cstat; + queue_task(&raw->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + } + return; +} + +/* + * String write routine for 3215 devices + */ +static int +raw3215_write(raw3215_info *raw, const char *str, + int from_user, unsigned int length) +{ + raw3215_req *req; + unsigned long flags; + int ret, c; + int count; + + ret = 0; + while (length > 0) { + s390irq_spin_lock_irqsave(raw->irq, flags); + count = (length > RAW3215_BUFFER_SIZE) ? + RAW3215_BUFFER_SIZE : length; + length -= count; + + while (RAW3215_BUFFER_SIZE - raw->count < count) { + /* there might be a request pending */ + raw3215_try_io(raw); + if (wait_cons_dev(raw->irq) != 0) { + /* that shouldn't happen */ + raw->count = 0; + } + } + + req = raw3215_mk_write_req(raw); + /* copy string to output buffer and convert it to EBCDIC */ + if (from_user) { + while (1) { + c = MIN(count, + MIN(RAW3215_BUFFER_SIZE - raw->count, + RAW3215_BUFFER_SIZE - raw->head)); + if (c <= 0) + break; + c -= copy_from_user(raw->buffer + raw->head, + str, c); + if (c == 0) { + if (!ret) + ret = -EFAULT; + break; + } + ASCEBC(raw->buffer + raw->head, c); + raw->head = (raw->head + c) & + (RAW3215_BUFFER_SIZE - 1); + raw->count += c; + str += c; + count -= c; + ret += c; + } + } else { + while (1) { + c = MIN(count, + MIN(RAW3215_BUFFER_SIZE - raw->count, + RAW3215_BUFFER_SIZE - raw->head)); + if (c <= 0) + break; + memcpy(raw->buffer + raw->head, str, c); + ASCEBC(raw->buffer + raw->head, c); + raw->head = (raw->head + c) & + (RAW3215_BUFFER_SIZE - 1); + raw->count += c; + str += c; + count -= c; + ret += c; + } + } + raw3215_mk_write_ccw(raw, req); + raw->queued_write = req; + /* start or queue request */ + raw3215_try_io(raw); + s390irq_spin_unlock_irqrestore(raw->irq, flags); + + } + + return ret; +} + +/* + * Put character routine for 3215 devices + */ +static void raw3215_putchar(raw3215_info *raw, unsigned char ch) +{ + raw3215_req *req; + unsigned long flags; + + s390irq_spin_lock_irqsave(raw->irq, flags); + while (RAW3215_BUFFER_SIZE - raw->count < 1) { + /* there might be a request pending */ + raw3215_try_io(raw); + if (wait_cons_dev(raw->irq) != 0) { + /* that shouldn't happen */ + raw->count = 0; + } + } + + req = raw3215_mk_write_req(raw); + raw->buffer[raw->head] = (char) _ascebc[(int) ch]; + raw->head = (raw->head + 1) & (RAW3215_BUFFER_SIZE - 1); + raw->count++; + raw3215_mk_write_ccw(raw, req); + raw->queued_write = req; + /* start or queue request */ + raw3215_try_io(raw); + s390irq_spin_unlock_irqrestore(raw->irq, flags); +} + +/* + * Flush routine, it simply sets the flush flag and tries to start + * pending IO. + */ +static void raw3215_flush_buffer(raw3215_info *raw) +{ + unsigned long flags; + + s390irq_spin_lock_irqsave(raw->irq, flags); + if (raw->count > 0) { + raw->flags |= RAW3215_FLUSHING; + raw3215_try_io(raw); + raw->flags &= ~RAW3215_FLUSHING; + } + s390irq_spin_unlock_irqrestore(raw->irq, flags); +} + +/* + * Fire up a 3215 device. + */ +static int raw3215_startup(raw3215_info *raw) +{ + unsigned long flags; + + if (raw->flags & RAW3215_ACTIVE) + return 0; + if (request_irq(raw->irq, raw3215_irq, SA_INTERRUPT, + "3215 terminal driver", &raw->devstat) != 0) + return -1; + raw->flags |= RAW3215_ACTIVE; + s390irq_spin_lock_irqsave(raw->irq, flags); + raw3215_try_io(raw); + s390irq_spin_unlock_irqrestore(raw->irq, flags); + + return 0; +} + +/* + * Shutdown a 3215 device. + */ +static void raw3215_shutdown(raw3215_info *raw) +{ + struct wait_queue wait = { current, NULL }; + unsigned long flags; + + if (!(raw->flags & RAW3215_ACTIVE) || (raw->flags & RAW3215_FIXED)) + return; + /* Wait for outstanding requests, then free irq */ + s390irq_spin_lock_irqsave(raw->irq, flags); + if ((raw->flags & RAW3215_WORKING) || + raw->queued_write != NULL || + raw->queued_read != NULL) { + raw->flags |= RAW3215_CLOSING; + add_wait_queue(&raw->empty_wait, &wait); + current->state = TASK_INTERRUPTIBLE; + s390irq_spin_unlock_irqrestore(raw->irq, flags); + schedule(); + s390irq_spin_lock_irqsave(raw->irq, flags); + current->state = TASK_RUNNING; + raw->flags &= ~(RAW3215_ACTIVE | RAW3215_CLOSING); + } + free_irq(raw->irq, NULL); + s390irq_spin_unlock_irqrestore(raw->irq, flags); +} + +static int +raw3215_find_dev(int number) +{ + dev_info_t dinfo; + int irq; + int count; + + irq = 0; + count = 0; + while (count <= number && get_dev_info(irq, &dinfo) != -ENODEV) { + if (dinfo.sid_data.cu_type == 0x3215) + count++; + irq++; + } + if (count <= number) + irq = -1; /* console not found */ + else + irq--; + return irq; +} + +#ifdef CONFIG_3215_CONSOLE + +/* + * Try to request the console IRQ. Called from init/main.c + */ +int con3215_activate(void) +{ + raw3215_info *raw; + + if (!MACHINE_IS_VM) + return 0; + raw = raw3215[0]; /* 3215 console is the first one */ + if (raw->irq == -1) /* now console device found in con3215_init */ + return -1; + return raw3215_startup(raw); +} + +/* + * Write a string to the 3215 console + */ +static void +con3215_write(struct console *co, const char *str, unsigned int count) +{ + raw3215_info *raw; + + if (count <= 0) + return; + raw = raw3215[0]; /* console 3215 is the first one */ + raw3215_write(raw, str, 0, count); +} + +kdev_t con3215_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, c->index); +} + +/* + * panic() calls console_unblank before the system enters a + * disabled, endless loop. + */ +void con3215_unblank(void) +{ + raw3215_info *raw; + unsigned long flags; + + raw = raw3215[0]; /* console 3215 is the first one */ + s390irq_spin_lock_irqsave(raw->irq, flags); + while (RAW3215_BUFFER_SIZE > raw->count) { + /* there might be a request pending */ + raw->flags |= RAW3215_FLUSHING; + raw3215_try_io(raw); + if (wait_cons_dev(raw->irq) != 0) { + /* that shouldn't happen */ + raw->count = 0; + } + raw->flags &= ~RAW3215_FLUSHING; + } + s390irq_spin_unlock_irqrestore(raw->irq, flags); +} + +__initfunc(static int con3215_setup(struct console *co, char *options)) +{ + return 0; +} + +/* + * The console structure for the 3215 console + */ +static struct console con3215 = { + "tty3215", + con3215_write, + NULL, + con3215_device, + NULL, + con3215_unblank, + con3215_setup, + CON_PRINTBUFFER, + 0, + 0, + NULL +}; + +#endif + +/* + * tty3215_open + * + * This routine is called whenever a 3215 tty is opened. + */ +static int tty3215_open(struct tty_struct *tty, struct file * filp) +{ + raw3215_info *raw; + int retval, line; + + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (line >= NR_3215)) + return -ENODEV; + + raw = raw3215[line]; + if (raw == NULL) { + raw = kmalloc(sizeof(raw3215_info) + + RAW3215_INBUF_SIZE, GFP_KERNEL); + if (raw == NULL) + return -ENOMEM; + raw->irq = raw3215_find_dev(line); + if (raw->irq == -1) { + kfree(raw); + return -ENODEV; + } + raw->inbuf = (char *) raw + sizeof(raw3215_info); + memset(raw, 0, sizeof(raw3215_info)); + raw->buffer = (char *) kmalloc(RAW3215_BUFFER_SIZE, GFP_KERNEL); + if (raw->buffer == NULL) { + kfree_s(raw, sizeof(raw3215_info)); + return -ENOMEM; + } + raw->tqueue.routine = raw3215_softint; + raw->tqueue.data = raw; + raw3215[line] = raw; + } + + tty->driver_data = raw; + raw->tty = tty; + + tty->low_latency = 0; /* don't use bottom half for pushing chars */ + /* + * Start up 3215 device + */ + retval = raw3215_startup(raw); + if (retval) + return retval; + + return 0; +} + +/* + * tty3215_close() + * + * This routine is called when the 3215 tty is closed. We wait + * for the remaining request to be completed. Then we clean up. + */ +static void tty3215_close(struct tty_struct *tty, struct file * filp) +{ + raw3215_info *raw; + + raw = (raw3215_info *) tty->driver_data; + if (raw == NULL || tty->count > 1) + return; + tty->closing = 1; + /* Shutdown the terminal */ + raw3215_shutdown(raw); + tty->closing = 0; + raw->tty = NULL; +} + +/* + * Returns the amount of free space in the output buffer. + */ +static int tty3215_write_room(struct tty_struct *tty) +{ + raw3215_info *raw; + + raw = (raw3215_info *) tty->driver_data; + return RAW3215_BUFFER_SIZE - raw->count; +} + +/* + * String write routine for 3215 ttys + */ +static int tty3215_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + raw3215_info *raw; + int ret; + + if (!tty) + return 0; + raw = (raw3215_info *) tty->driver_data; + ret = raw3215_write(raw, buf, from_user, count); + return ret; +} + +/* + * Put character routine for 3215 ttys + */ +static void tty3215_put_char(struct tty_struct *tty, unsigned char ch) +{ + raw3215_info *raw; + + if (!tty) + return; + raw = (raw3215_info *) tty->driver_data; + raw3215_putchar(raw, ch); +} + +static void tty3215_flush_chars(struct tty_struct *tty) +{ +} + +/* + * Returns the number of characters in the output buffer + */ +static int tty3215_chars_in_buffer(struct tty_struct *tty) +{ + raw3215_info *raw; + + raw = (raw3215_info *) tty->driver_data; + return raw->count; +} + +static void tty3215_flush_buffer(struct tty_struct *tty) +{ + raw3215_info *raw; + + raw = (raw3215_info *) tty->driver_data; + raw3215_flush_buffer(raw); + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + +/* + * Currently we don't have any io controls for 3215 ttys + */ +static int tty3215_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + + switch (cmd) { + default: + return -ENOIOCTLCMD; + } + return 0; +} + +/* + * Disable reading from a 3215 tty + */ +static void tty3215_throttle(struct tty_struct * tty) +{ + raw3215_info *raw; + + raw = (raw3215_info *) tty->driver_data; + raw->flags |= RAW3215_THROTTLED; +} + +/* + * Enable reading from a 3215 tty + */ +static void tty3215_unthrottle(struct tty_struct * tty) +{ + raw3215_info *raw; + unsigned long flags; + + raw = (raw3215_info *) tty->driver_data; + if (raw->flags & RAW3215_THROTTLED) { + s390irq_spin_lock_irqsave(raw->irq, flags); + raw->flags &= ~RAW3215_THROTTLED; + raw3215_try_io(raw); + s390irq_spin_unlock_irqrestore(raw->irq, flags); + } +} + +/* + * Disable writing to a 3215 tty + */ +static void tty3215_stop(struct tty_struct *tty) +{ + raw3215_info *raw; + + raw = (raw3215_info *) tty->driver_data; + raw->flags |= RAW3215_STOPPED; +} + +/* + * Enable writing to a 3215 tty + */ +static void tty3215_start(struct tty_struct *tty) +{ + raw3215_info *raw; + unsigned long flags; + + raw = (raw3215_info *) tty->driver_data; + if (raw->flags & RAW3215_STOPPED) { + s390irq_spin_lock_irqsave(raw->irq, flags); + raw->flags &= ~RAW3215_STOPPED; + raw3215_try_io(raw); + s390irq_spin_unlock_irqrestore(raw->irq, flags); + } +} + +/* + * 3215 console driver boottime initialization code. + * Register console. We can't request the IRQ here, because + * it's too early (kmalloc isn't working yet). We'll have to + * buffer all the console requests until we can request the + * irq. For this purpose we use some pages of fixed memory. + */ +__initfunc (long con3215_init(long kmem_start, long kmem_end)) +{ + raw3215_info *raw; + raw3215_req *req; + int i; + + if (!MACHINE_IS_VM) + return kmem_start; + cpcmd("TERM CONMODE 3215", NULL, 0); + cpcmd("TERM AUTOCR OFF", NULL, 0); + cpcmd("TERM HOLD OFF", NULL, 0); + cpcmd("TERM MORE 5 5", NULL, 0); + + kmem_start = (kmem_start + 7) & -8L; + + /* allocate 3215 request structures */ + raw3215_freelist = NULL; + spin_lock_init(&raw3215_freelist_lock); + for (i = 0; i < NR_3215_REQ; i++) { + req = (raw3215_req *) kmem_start; + kmem_start += sizeof(raw3215_req); + req->next = raw3215_freelist; + raw3215_freelist = req; + } + +#ifdef CONFIG_3215_CONSOLE + raw3215[0] = raw = (raw3215_info *) kmem_start; + kmem_start += sizeof(raw3215_info); + memset(raw, 0, sizeof(raw3215_info)); + raw->buffer = (char *) kmem_start; + kmem_start += RAW3215_BUFFER_SIZE; + raw->inbuf = (char *) kmem_start; + kmem_start += RAW3215_INBUF_SIZE; + /* Find the first console */ + raw->irq = raw3215_find_dev(0); + raw->flags |= RAW3215_FIXED; + raw->tqueue.routine = raw3215_softint; + raw->tqueue.data = raw; + + if (raw->irq != -1) { + register_console(&con3215); + s390irq_spin_lock(raw->irq); + set_cons_dev(raw->irq); + s390irq_spin_unlock(raw->irq); + } else { + kmem_start = (long) raw; + raw3215[0] = NULL; + printk("Couldn't find a 3215 console device\n"); + } +#endif + + /* + * Initialize the tty_driver structure + * Entries in tty3215_driver that are NOT initialized: + * proc_entry, set_termios, flush_buffer, set_ldisc, write_proc + */ + + memset(&tty3215_driver, 0, sizeof(struct tty_driver)); + tty3215_driver.magic = TTY_DRIVER_MAGIC; + tty3215_driver.driver_name = "tty3215"; + tty3215_driver.name = "ttyS"; + tty3215_driver.name_base = 0; + tty3215_driver.major = TTY_MAJOR; + tty3215_driver.minor_start = 64; + tty3215_driver.num = NR_3215; + tty3215_driver.type = TTY_DRIVER_TYPE_SYSTEM; + tty3215_driver.subtype = SYSTEM_TYPE_TTY; + tty3215_driver.init_termios = tty_std_termios; + tty3215_driver.init_termios.c_iflag = IGNBRK | IGNPAR; + tty3215_driver.init_termios.c_oflag = ONLCR; + tty3215_driver.init_termios.c_lflag = ISIG; + tty3215_driver.flags = TTY_DRIVER_REAL_RAW; + tty3215_driver.refcount = &tty3215_refcount; + tty3215_driver.table = tty3215_table; + tty3215_driver.termios = tty3215_termios; + tty3215_driver.termios_locked = tty3215_termios_locked; + + tty3215_driver.open = tty3215_open; + tty3215_driver.close = tty3215_close; + tty3215_driver.write = tty3215_write; + tty3215_driver.put_char = tty3215_put_char; + tty3215_driver.flush_chars = tty3215_flush_chars; + tty3215_driver.write_room = tty3215_write_room; + tty3215_driver.chars_in_buffer = tty3215_chars_in_buffer; + tty3215_driver.flush_buffer = tty3215_flush_buffer; + tty3215_driver.ioctl = tty3215_ioctl; + tty3215_driver.throttle = tty3215_throttle; + tty3215_driver.unthrottle = tty3215_unthrottle; + tty3215_driver.send_xchar = NULL; + tty3215_driver.set_termios = NULL; + tty3215_driver.stop = tty3215_stop; + tty3215_driver.start = tty3215_start; + tty3215_driver.hangup = NULL; + tty3215_driver.break_ctl = NULL; + tty3215_driver.wait_until_sent = NULL; + tty3215_driver.read_proc = NULL; + + if (tty_register_driver(&tty3215_driver)) + panic("Couldn't register tty3215 driver\n"); + + return kmem_start; +} diff -u --recursive --new-file v2.2.13/linux/drivers/s390/char/hwc.h linux/drivers/s390/char/hwc.h --- v2.2.13/linux/drivers/s390/char/hwc.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/char/hwc.h Tue Jan 4 10:12:18 2000 @@ -0,0 +1,228 @@ +/* + * drivers/s390/char/hwc.h + * + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Peschke + * + * + * + */ + +#ifndef __HWC_H__ +#define __HWC_H__ + + +#define ET_OpCmd 0x01 +#define ET_Msg 0x02 +#define ET_StateChange 0x08 +#define ET_PMsgCmd 0x09 +#define ET_CntlProgOpCmd 0x20 + + +#define ET_OpCmd_Mask 0x80000000 +#define ET_Msg_Mask 0x40000000 +#define ET_StateChange_Mask 0x01000000 +#define ET_PMsgCmd_Mask 0x00800000 +#define ET_CtlProgOpCmd_Mask 0x00000001 + + +#define GMF_DOM 0x8000 +#define GMF_SndAlrm 0x4000 +#define GMF_HoldMsg 0x2000 + + +#define LTF_CntlText 0x8000 +#define LTF_LabelText 0x4000 +#define LTF_DataText 0x2000 +#define LTF_EndText 0x1000 +#define LTF_PromptText 0x0800 + + +#define HWC_COMMAND_INITIATED 0 +#define HWC_BUSY 2 +#define HWC_NOT_OPERATIONAL 3 + + +#define HWC_CMDW_READDATA 0x00770005 + +#define HWC_CMDW_WRITEDATA 0x00760005 + +#define HWC_CMDW_WRITEMASK 0x00780005 + + +#define GDS_ID_MDSMU 0x1310 +#define GDS_ID_MDSRouteInfo 0x1311 +#define GDS_ID_AgUnWrkCorr 0x1549 +#define GDS_ID_SNACondReport 0x1532 +#define GDS_ID_CPMSU 0x1212 +#define GDS_ID_RoutTargInstr 0x154D +#define GDS_ID_OpReq 0x8070 +#define GDS_ID_TextCmd 0x1320 + + +#define GDS_KEY_SelfDefTextMsg 0x31 + +#define _HWCB_HEADER u16 length; \ + u8 function_code; \ + u8 control_mask[3]; \ + u16 response_code; + +#define _EBUF_HEADER u16 length; \ + u8 type; \ + u8 flags; \ + u16 _reserved; + +typedef struct { + _EBUF_HEADER +} __attribute__((packed)) evbuf_t; + + + +#define _MDB_HEADER u16 length; \ + u16 type; \ + u32 tag; \ + u32 revision_code; + +#define _GO_HEADER u16 length; \ + u16 type; \ + u32 domid; \ + u8 hhmmss_time[8]; \ + u8 th_time[3]; \ + u8 _reserved_0; \ + u8 dddyyyy_date[7]; \ + u8 _reserved_1; \ + u16 general_msg_flags; \ + u8 _reserved_2[10]; \ + u8 originating_system_name[8]; \ + u8 job_guest_name[8]; + +#define _MTO_HEADER u16 length; \ + u16 type; \ + u16 line_type_flags; \ + u8 alarm_control; \ + u8 _reserved[3]; + +typedef struct { + _GO_HEADER +} __attribute__((packed)) go_t; + +typedef struct { + go_t go; +} __attribute__((packed)) mdb_body_t; + +typedef struct { + _MDB_HEADER + mdb_body_t mdb_body; +} __attribute__((packed)) mdb_t; + +typedef struct { + _EBUF_HEADER + mdb_t mdb; +} __attribute__((packed)) msgbuf_t; + +typedef struct { + _HWCB_HEADER + msgbuf_t msgbuf; +} __attribute__((packed)) write_hwcb_t; + +typedef struct { + _MTO_HEADER +} __attribute__((packed)) mto_t; + +static write_hwcb_t write_hwcb_template = { + sizeof(write_hwcb_t), + 0x00, + { + 0x00, + 0x00, + 0x00 + }, + 0x0000, + { + sizeof(msgbuf_t), + ET_Msg, + 0x00, + 0x0000, + { + sizeof(mdb_t), + 0x0001, + 0xD4C4C240, + 0x00000001, + { + { + sizeof(go_t), + 0x0001 + } + } + } + } +}; + +static mto_t mto_template = { + sizeof(mto_t), + 0x0004, + LTF_EndText, + 0x00 +}; + + +typedef u32 _hwcb_mask_t; + +typedef struct { + _HWCB_HEADER + u16 _reserved; + u16 mask_length; + _hwcb_mask_t cp_receive_mask; + _hwcb_mask_t cp_send_mask; + _hwcb_mask_t hwc_receive_mask; + _hwcb_mask_t hwc_send_mask; +} __attribute__((packed)) init_hwcb_t; + +static init_hwcb_t init_hwcb_template = { + sizeof(init_hwcb_t), + 0x00, + { + 0x00, + 0x00, + 0x00 + }, + 0x0000, + 0x0000, + sizeof(_hwcb_mask_t), + ET_OpCmd_Mask | ET_PMsgCmd_Mask, + ET_Msg_Mask +}; + + +#define _GDS_VECTOR_HEADER u16 length; \ + u16 gds_id; + +#define _GDS_SUBVECTOR_HEADER u8 length; \ + u8 key; + +typedef struct { + _GDS_VECTOR_HEADER +} __attribute__((packed)) gds_vector_t; + +typedef struct { + _GDS_SUBVECTOR_HEADER +} __attribute__((packed)) gds_subvector_t; + +typedef struct { + _HWCB_HEADER +} __attribute__((packed)) read_hwcb_t; + +static read_hwcb_t read_hwcb_template = { + PAGE_SIZE, + 0x00, + { + 0x00, + 0x00, + 0x80 + } +}; + +#endif /* __HWC_H__ */ + diff -u --recursive --new-file v2.2.13/linux/drivers/s390/char/hwc_con.c linux/drivers/s390/char/hwc_con.c --- v2.2.13/linux/drivers/s390/char/hwc_con.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/char/hwc_con.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,87 @@ +/* + * drivers/s390/char/hwc_con.c + * HWC line mode console driver + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Peschke + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hwc_rw.h" + +extern void hwc_tty_init(void); + + +#ifdef CONFIG_HWC_CONSOLE + +#define hwc_console_major 4 +#define hwc_console_minor 0 +#define hwc_console_name "console" + +void hwc_console_write(struct console *, const char *, unsigned int); +kdev_t hwc_console_device(struct console *); + +#define HWC_CON_PRINT_HEADER "hwc console driver: " + +struct console hwc_console = { + hwc_console_name, + hwc_console_write, + NULL, + hwc_console_device, + NULL, + NULL, + NULL, + CON_PRINTBUFFER, + 0, + 0, + NULL +}; + +void hwc_console_write( + struct console *console, + const char *message, + unsigned int count) +{ + if (console->device(console) != hwc_console.device(&hwc_console)) + { + hwc_printk(KERN_WARNING HWC_CON_PRINT_HEADER + "hwc_console_write() called with wrong " + "device number"); + return; + } + hwc_write(0, message, count); +} + + +kdev_t hwc_console_device(struct console *c) +{ + return MKDEV(hwc_console_major, hwc_console_minor); +} + +#endif + +__initfunc(unsigned long hwc_console_init(unsigned long kmem_start)) +{ + +#ifdef CONFIG_3215 + if (MACHINE_IS_VM) + return kmem_start; +#endif + if (hwc_init(&kmem_start) == 0) { +#ifdef CONFIG_HWC_CONSOLE + register_console(&hwc_console); +#endif + hwc_tty_init(); + } + else panic(HWC_CON_PRINT_HEADER "hwc initialisation failed !"); + return kmem_start; +} diff -u --recursive --new-file v2.2.13/linux/drivers/s390/char/hwc_rw.c linux/drivers/s390/char/hwc_rw.c --- v2.2.13/linux/drivers/s390/char/hwc_rw.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/char/hwc_rw.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,1821 @@ +/* + * drivers/s390/char/hwc_rw.c + * driver: reading from and writing to system console on S/390 via HWC + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Peschke + * + * + * + * + * + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifndef MIN +#define MIN(a,b) ((amto_char_sum) +#define _HWCB_MTO(hwcb) (_LIST(hwcb)->mto_number) +#define _HWCB_CHAR_LOST(hwcb) (_LIST(hwcb)->mto_char_sum_lost) +#define _HWCB_MTO_LOST(hwcb) (_LIST(hwcb)->mto_number_lost) +#define _HWCB_TIMES_LOST(hwcb) (_LIST(hwcb)->times_lost) +#define _HWCB_NEXT(hwcb) (_LIST(hwcb)->next) +#define BUF_HWCB_CHAR _HWCB_CHAR(BUF_HWCB) +#define BUF_HWCB_MTO _HWCB_MTO(BUF_HWCB) +#define BUF_HWCB_NEXT _HWCB_NEXT(BUF_HWCB) +#define OUT_HWCB_CHAR _HWCB_CHAR(OUT_HWCB) +#define OUT_HWCB_MTO _HWCB_MTO(OUT_HWCB) +#define OUT_HWCB_NEXT _HWCB_NEXT(OUT_HWCB) +#define BUF_HWCB_CHAR_LOST _HWCB_CHAR_LOST(BUF_HWCB) +#define BUF_HWCB_MTO_LOST _HWCB_MTO_LOST(BUF_HWCB) +#define OUT_HWCB_CHAR_LOST _HWCB_CHAR_LOST(OUT_HWCB) +#define OUT_HWCB_MTO_LOST _HWCB_MTO_LOST(OUT_HWCB) +#define BUF_HWCB_TIMES_LOST _HWCB_TIMES_LOST(BUF_HWCB) + + +#include "hwc.h" + +#define __HWC_RW_C__ +#include "hwc_rw.h" +#undef __HWC_RW_C__ + +static unsigned char _obuf[MAX_HWCB_ROOM]; + +static unsigned char + _page[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); + +typedef u32 kmem_pages_t; + +#define MAX_KMEM_PAGES (sizeof(kmem_pages_t) << 3) +#define HWC_TIMER_RUNS 1 +#define FLUSH_HWCBS 2 + +static struct { + hwc_ioctls_t ioctls; + hwc_ioctls_t init_ioctls; + unsigned char *hwcb_list_head; + unsigned char *hwcb_list_tail; + unsigned short int mto_number; + unsigned int mto_char_sum; + unsigned char hwcb_count; + unsigned long kmem_start; + unsigned long kmem_end; + kmem_pages_t kmem_pages; + unsigned char *obuf; + unsigned short int obuf_cursor; + unsigned short int obuf_count; + unsigned short int obuf_start; + unsigned char *page; + u32 current_servc; + unsigned char *current_hwcb; + unsigned char write_nonprio:1; + unsigned char write_prio:1; + unsigned char read_nonprio:1; + unsigned char read_prio:1; + unsigned char flags; + spinlock_t lock; + struct timer_list write_timer; +} hwc_data = { + { }, + { + 8, + 0, + 80, + CODE_ASCII, + 1, + 50, + MAX_KMEM_PAGES, + 0, + 0x6c + }, + NULL, + NULL, + 0, + 0, + 0, + 0, + 0, + 0, + _obuf, + 0, + 0, + 0, + _page, + 0, + NULL, + 0, + 0, + 0, + 0, + 0 +}; + +#define DELAYED_WRITE 0 +#define IMMEDIATE_WRITE 1 + +static signed int do_hwc_write(int from_user, const unsigned char *, + unsigned int, + unsigned char, + unsigned char); + + +static asmlinkage int internal_print(char write_time, const char *fmt, ...) +{ + va_list args; + int i; + unsigned char buf[512]; + + va_start(args, fmt); + i = vsprintf(buf, fmt, args); + va_end(args); + return do_hwc_write(0, buf, i, CODE_ASCII, write_time); +} + + +int hwc_printk(const char *fmt, ...) +{ + va_list args; + int i; + unsigned char buf[512]; + unsigned long flags; + int retval; + + spin_lock_irqsave(&hwc_data.lock, flags); + + i = vsprintf(buf, fmt, args); + va_end(args); + retval = do_hwc_write(0, buf, i, CODE_ASCII, IMMEDIATE_WRITE); + + spin_unlock_irqrestore(&hwc_data.lock, flags); + + return retval; +} + +#ifdef DUMP_HWCB_INPUT + +static void dump_storage_area(unsigned char *area, unsigned short int count) +{ + unsigned short int index; + ioctl_nl_t old_final_nl; + + if (!area || !count) + return; + + old_final_nl = hwc_data.ioctls.final_nl; + hwc_data.ioctls.final_nl = 1; + + internal_print(DELAYED_WRITE, "\n%8x ", area); + + + for (index = 0; index < count; index++) { + + if (area[index] <= 0xF) + internal_print(DELAYED_WRITE, "0%x", area[index]); + else internal_print(DELAYED_WRITE, "%x", area[index]); + + if ((index & 0xF) == 0xF) + internal_print(DELAYED_WRITE, "\n%8x ", + &area[index + 1]); + else if ((index & 3) == 3) + + internal_print(DELAYED_WRITE, " "); + } + + internal_print(IMMEDIATE_WRITE, "\n"); + hwc_data.ioctls.final_nl = old_final_nl; +} +#endif + +static inline u32 service_call( + u32 hwc_command_word, + unsigned char hwcb[]) +{ + unsigned int condition_code = 1; + + __asm__ __volatile__("L 1, 0(0,%0) \n\t" + "LRA 2, 0(0,%1) \n\t" + ".long 0xB2200012 \n\t" + : + :"a"(&hwc_command_word), "a"(hwcb) + :"1", "2", "memory"); + + __asm__ __volatile__("IPM %0 \n\t" + "SRL %0, 28 \n\t" + :"=r"(condition_code)); + return condition_code; +} + + + +static inline unsigned char * ext_int_param(void) +{ + u32 param; + + __asm__ __volatile__("L %0,128(0,0)\n\t" + :"=r"(param)); + + return ((unsigned char *) param); +} + +static int prepare_write_hwcb(void) +{ + write_hwcb_t *hwcb; + + if (!BUF_HWCB) + return -ENOMEM; + + BUF_HWCB_MTO = 0; + BUF_HWCB_CHAR = 0; + hwcb = (write_hwcb_t *) BUF_HWCB; + memcpy(hwcb, &write_hwcb_template, sizeof(write_hwcb_t)); + + if (!hwc_data.write_nonprio && hwc_data.write_prio) + hwcb->msgbuf.type = ET_PMsgCmd; + + return 0; +} + +static int sane_write_hwcb(void) +{ + unsigned short int lost_msg; + unsigned int lost_char; + unsigned char lost_hwcb; + unsigned char *bad_addr; + unsigned long page; + int page_nr; + + if (!OUT_HWCB) + return -ENOMEM; + + if ((unsigned long)OUT_HWCB & 0xFFF) { + bad_addr = OUT_HWCB; +#ifdef DUMP_HWC_WRITE_LIST_ERROR + __asm__("LHI 1,0xe30\n\t" + "LRA 2,0(0,%0) \n\t" + "J .+0 \n\t" + : + :"a"(bad_addr) + :"1", "2"); +#endif + + hwc_data.kmem_pages = 0; + if ((unsigned long)BUF_HWCB & 0xFFF) { + lost_hwcb = hwc_data.hwcb_count; + lost_msg = ALL_HWCB_MTO; + lost_char = ALL_HWCB_CHAR; + + OUT_HWCB = NULL; + BUF_HWCB = NULL; + ALL_HWCB_MTO = 0; + ALL_HWCB_CHAR = 0; + hwc_data.hwcb_count = 0; + } else { + lost_hwcb = hwc_data.hwcb_count - 1; + lost_msg = ALL_HWCB_MTO - BUF_HWCB_MTO; + lost_char = ALL_HWCB_CHAR - BUF_HWCB_CHAR; + OUT_HWCB = BUF_HWCB; + ALL_HWCB_MTO = BUF_HWCB_MTO; + ALL_HWCB_CHAR = BUF_HWCB_CHAR; + hwc_data.hwcb_count = 1; + page = (unsigned long) BUF_HWCB; + + + if (page >= hwc_data.kmem_start && + page < hwc_data.kmem_end) { + + page_nr = (int) + ((page - hwc_data.kmem_start) >> 12); + set_bit(page_nr, &hwc_data.kmem_pages); + } + } + + internal_print( + DELAYED_WRITE, + HWC_RW_PRINT_HEADER + "found invalid HWCB at address 0x%x. List corrupted. " + "Lost %i HWCBs with %i characters within up to %i " + "messages. Saved %i HWCB with last %i characters i" + "within up to %i messages.\n", + (unsigned int)bad_addr, + lost_hwcb, lost_char, lost_msg, + hwc_data.hwcb_count, + ALL_HWCB_CHAR, ALL_HWCB_MTO); + } + return 0; +} + +static int reuse_write_hwcb(void) +{ + int retval; + + if (hwc_data.hwcb_count < 2) +#ifdef DUMP_HWC_WRITE_LIST_ERROR + __asm__("LHI 1,0xe31\n\t" + "LRA 2,0(0,%0)\n\t" + "LRA 3,0(0,%1)\n\t" + "J .+0 \n\t" + : + :"a"(BUF_HWCB), "a"(OUT_HWCB) + :"1", "2", "3"); +#else + return -EPERM; +#endif + + if (hwc_data.current_hwcb == OUT_HWCB) { + if (hwc_data.hwcb_count > 2) { + BUF_HWCB_NEXT = OUT_HWCB_NEXT; + BUF_HWCB = OUT_HWCB_NEXT; + OUT_HWCB_NEXT = BUF_HWCB_NEXT; + BUF_HWCB_NEXT = NULL; + } + } else { + BUF_HWCB_NEXT = OUT_HWCB; + BUF_HWCB = OUT_HWCB; + OUT_HWCB = OUT_HWCB_NEXT; + BUF_HWCB_NEXT = NULL; + } + BUF_HWCB_TIMES_LOST += 1; + BUF_HWCB_CHAR_LOST += BUF_HWCB_CHAR; + BUF_HWCB_MTO_LOST += BUF_HWCB_MTO; + ALL_HWCB_MTO -= BUF_HWCB_MTO; + ALL_HWCB_CHAR -= BUF_HWCB_CHAR; + + retval = prepare_write_hwcb(); + + if (hwc_data.hwcb_count == hwc_data.ioctls.max_hwcb) + internal_print( + DELAYED_WRITE, + HWC_RW_PRINT_HEADER + "reached my own limit of " + "allowed buffer space for output (%i HWCBs = %li " + "bytes), skipped content of oldest HWCB %i time(s) " + "(%i lines = %i characters)\n", + hwc_data.ioctls.max_hwcb, + hwc_data.ioctls.max_hwcb * PAGE_SIZE, + BUF_HWCB_TIMES_LOST, + BUF_HWCB_MTO_LOST, + BUF_HWCB_CHAR_LOST); + else internal_print( + DELAYED_WRITE, + HWC_RW_PRINT_HEADER + "page allocation failed, " + "could not expand buffer for output (currently in " + "use: %i HWCBs = %li bytes), skipped content of " + "oldest HWCB %i time(s) (%i lines = %i characters)\n", + hwc_data.hwcb_count, + hwc_data.hwcb_count * PAGE_SIZE, + BUF_HWCB_TIMES_LOST, + BUF_HWCB_MTO_LOST, + BUF_HWCB_CHAR_LOST); + + return retval; +} + +static int allocate_write_hwcb(void) +{ + unsigned char *page; + int page_nr; + + if (hwc_data.hwcb_count == hwc_data.ioctls.max_hwcb) + return -ENOMEM; + + page_nr = find_first_zero_bit(&hwc_data.kmem_pages, MAX_KMEM_PAGES); + if (page_nr < hwc_data.ioctls.kmem_hwcb) { + page = (unsigned char *) + (hwc_data.kmem_start + (page_nr << 12)); + set_bit(page_nr, &hwc_data.kmem_pages); + } else page = (unsigned char *) __get_free_page(GFP_ATOMIC); + if (!page) + return -ENOMEM; + + if (!OUT_HWCB) + OUT_HWCB = page; + else + BUF_HWCB_NEXT = page; + + BUF_HWCB = page; + BUF_HWCB_NEXT = NULL; + hwc_data.hwcb_count++; + prepare_write_hwcb(); + BUF_HWCB_TIMES_LOST = 0; + BUF_HWCB_MTO_LOST = 0; + BUF_HWCB_CHAR_LOST = 0; + +#ifdef BUFFER_STRESS_TEST + internal_print( + DELAYED_WRITE, + "*** " HWC_RW_PRINT_HEADER + "page #%i at 0x%x for buffering allocated. ***\n", + hwc_data.hwcb_count, page); + +#endif + return 0; +} + +static int release_write_hwcb(void) +{ + unsigned long page; + int page_nr; + + if (!hwc_data.hwcb_count) + return -ENODATA; + + if (hwc_data.hwcb_count == 1) { + prepare_write_hwcb(); + + ALL_HWCB_CHAR = 0; + ALL_HWCB_MTO = 0; + BUF_HWCB_TIMES_LOST = 0; + BUF_HWCB_MTO_LOST = 0; + BUF_HWCB_CHAR_LOST = 0; + } + else { + page = (unsigned long) OUT_HWCB; + + ALL_HWCB_MTO -= OUT_HWCB_MTO; + ALL_HWCB_CHAR -= OUT_HWCB_CHAR; + hwc_data.hwcb_count--; + + OUT_HWCB = OUT_HWCB_NEXT; + + if (page >= hwc_data.kmem_start && + page < hwc_data.kmem_end) { + memset((void *) page, 0, PAGE_SIZE); + page_nr = (int) ((page - hwc_data.kmem_start) >> 12); + clear_bit(page_nr, &hwc_data.kmem_pages); + } + else + free_page(page); +#ifdef BUFFER_STRESS_TEST + internal_print( + DELAYED_WRITE, + "*** " HWC_RW_PRINT_HEADER + "page at 0x%x released, %i pages still in use ***\n", + page, hwc_data.hwcb_count); + +#endif + } + return 0; +} + +static int add_mto( + unsigned char *message, + unsigned short int count) +{ + unsigned short int mto_size; + write_hwcb_t *hwcb; + mto_t *mto; + void *dest; + + if (!BUF_HWCB) + return -ENOMEM; + + if (BUF_HWCB == hwc_data.current_hwcb) + return -ENOMEM; + + mto_size = sizeof(mto_t) + count; + hwcb = (write_hwcb_t *) BUF_HWCB; + + if ((MAX_HWCB_ROOM - hwcb->length) < mto_size) + return -ENOMEM; + + mto = (mto_t *) (((unsigned long) hwcb) + hwcb->length); + memcpy(mto, &mto_template, sizeof(mto_t)); + dest = (void *) (((unsigned long) mto) + sizeof(mto_t)); + memcpy(dest, message, count); + mto->length += count; + + hwcb->length += mto_size; + hwcb->msgbuf.length += mto_size; + hwcb->msgbuf.mdb.length += mto_size; + + BUF_HWCB_MTO++; + ALL_HWCB_MTO++; + BUF_HWCB_CHAR += count; + ALL_HWCB_CHAR += count; + + return count; +} + +static int write_event_data_1(void) +{ + unsigned short int condition_code; + int retval; + + if ((!hwc_data.write_prio) && (!hwc_data.write_nonprio)) + return -EPERM; + + if (hwc_data.current_servc) + return -EBUSY; + + retval = sane_write_hwcb(); + if (retval < 0) + return retval; + if (!OUT_HWCB_MTO) + return -ENODATA; + condition_code = service_call(HWC_CMDW_WRITEDATA, OUT_HWCB); +#ifdef DUMP_HWC_WRITE_ERROR + if (condition_code != HWC_COMMAND_INITIATED) + __asm__("LHI 1,0xe20\n\t" + "L 2,0(0,%0)\n\t" + "LRA 3,0(0,%1)\n\t" + "J .+0 \n\t" + : + :"a"(&condition_code), "a"(OUT_HWCB) + :"1", "2", "3"); +#endif + switch (condition_code) { + case HWC_COMMAND_INITIATED : + hwc_data.current_servc = HWC_CMDW_WRITEDATA; + hwc_data.current_hwcb = OUT_HWCB; + retval = condition_code; + break; + case HWC_BUSY : + retval = -EBUSY; + break; + default : + retval = -EIO; + } + return retval; +} + +static void flush_hwcbs(void) +{ + while (hwc_data.hwcb_count > 1) + release_write_hwcb(); + release_write_hwcb(); + hwc_data.flags &= ~FLUSH_HWCBS; +} + +static int write_event_data_2(void) +{ + write_hwcb_t *hwcb; + int retval; + unsigned char *param; + + param = ext_int_param(); + if (param != hwc_data.current_hwcb) + return -EINVAL; + + hwcb = (write_hwcb_t *) OUT_HWCB; + +#ifdef DUMP_HWC_WRITE_ERROR +#if 0 + if (((unsigned char *) hwcb) != param) + __asm__("LHI 1,0xe22\n\t" + "LRA 2,0(0,%0)\n\t" + "LRA 3,0(0,%1)\n\t" + "LRA 4,0(0,%2)\n\t" + "LRA 5,0(0,%3)\n\t" + "J .+0 \n\t" + : + :"a"(OUT_HWCB), + "a"(hwc_data.current_hwcb), + "a"(BUF_HWCB), + "a"(param) + :"1", "2", "3", "4", "5"); +#endif + if (hwcb->response_code != 0x0020) +#if 0 + internal_print(DELAYED_WRITE, HWC_RW_PRINT_HEADER + "\n************************ error in write_event_data_2()\n" + "OUT_HWCB: 0x%x\n" + "BUF_HWCB: 0x%x\n" + "response_code: 0x%x\n" + "hwc_data.hwcb_count: %d\n" + "hwc_data.kmem_pages: 0x%x\n" + "hwc_data.ioctls.kmem_hwcb: %d\n" + "hwc_data.ioctls.max_hwcb: %d\n" + "hwc_data.kmem_start: 0x%x\n" + "hwc_data.kmem_end: 0x%x\n" + "*****************************************************\n", + OUT_HWCB, + BUF_HWCB, + hwcb->response_code, + hwc_data.hwcb_count, + hwc_data.kmem_pages, + hwc_data.ioctls.kmem_hwcb, + hwc_data.ioctls.max_hwcb, + hwc_data.kmem_start, + hwc_data.kmem_end); +#endif + __asm__("LHI 1,0xe21\n\t" + "LRA 2,0(0,%0)\n\t" + "LRA 3,0(0,%1)\n\t" + "LRA 4,0(0,%2)\n\t" + "LH 5,0(0,%3)\n\t" + "SRL 5,8(0)\n\t" + "J .+0 \n\t" + : + :"a"(OUT_HWCB), "a"(hwc_data.current_hwcb), + "a"(BUF_HWCB), + "a"(&(hwc_data.hwcb_count)) + :"1", "2", "3", "4", "5"); +#endif + + + if (hwcb->response_code == 0x0020) { + retval = OUT_HWCB_CHAR; + release_write_hwcb(); + } else retval = -EIO; + + hwc_data.current_servc = 0; + hwc_data.current_hwcb = NULL; + + if (hwc_data.flags & FLUSH_HWCBS) + flush_hwcbs(); + + return retval; +} + +static void do_put_line( + unsigned char * message, + unsigned short count) +{ + if (add_mto(message, count) != count) { + if (allocate_write_hwcb() < 0) + reuse_write_hwcb(); + +#ifdef DUMP_HWC_WRITE_LIST_ERROR + if (add_mto(message, count) != count) + __asm__("LHI 1,0xe32\n\t" + "LRA 2,0(0,%0)\n\t" + "L 3,0(0,%1)\n\t" + "LRA 4,0(0,%2)\n\t" + "LRA 5,0(0,%3)\n\t" + "J .+0 \n\t" + : + :"a"(message), "a"(&hwc_data.kmem_pages), + "a"(BUF_HWCB), "a"(OUT_HWCB) + :"1", "2", "3", "4", "5"); +#else + add_mto(message, count); +#endif + } +} + +static void put_line( + unsigned char * message, + unsigned short count) +{ + + if ((!hwc_data.obuf_start) && (hwc_data.flags & HWC_TIMER_RUNS)) { + del_timer(&hwc_data.write_timer); + hwc_data.flags &= ~HWC_TIMER_RUNS; + } + + hwc_data.obuf_start += count; + do_put_line(message, count); + hwc_data.obuf_start -= count; +} + +static void set_alarm(void) +{ + write_hwcb_t *hwcb; + if ((!BUF_HWCB) || (BUF_HWCB == hwc_data.current_hwcb)) + allocate_write_hwcb(); + + hwcb = (write_hwcb_t *) BUF_HWCB; + hwcb->msgbuf.mdb.mdb_body.go.general_msg_flags |= GMF_SndAlrm; +} + +static void hwc_write_timeout(unsigned long data) +{ + unsigned long flags; + + spin_lock_irqsave(&hwc_data.lock, flags); + + hwc_data.obuf_start = hwc_data.obuf_count; + if (hwc_data.obuf_count) + put_line(hwc_data.obuf, hwc_data.obuf_count); + hwc_data.obuf_start = 0; + hwc_data.obuf_cursor = 0; + hwc_data.obuf_count = 0; + + write_event_data_1(); + spin_unlock_irqrestore(&hwc_data.lock, flags); +} + +static int do_hwc_write( + int from_user, + const unsigned char *msg, + unsigned int count, + unsigned char code, + unsigned char write_time) +{ + unsigned int i_msg = 0; + unsigned short int spaces = 0; + unsigned int processed_characters = 0; + unsigned char ch, orig_ch; + unsigned short int obuf_count; + unsigned short int obuf_cursor; + unsigned short int obuf_columns; + + if (hwc_data.obuf_start) { + obuf_cursor = 0; + obuf_count = 0; + obuf_columns = MIN(hwc_data.ioctls.columns, + MAX_MESSAGE_SIZE - hwc_data.obuf_start); + } else { + obuf_cursor = hwc_data.obuf_cursor; + obuf_count = hwc_data.obuf_count; + obuf_columns = hwc_data.ioctls.columns; + } + + for (i_msg = 0; i_msg < count; i_msg++) { + if (from_user) + get_user(orig_ch, msg + i_msg); + else + orig_ch = msg[i_msg]; + if (code == CODE_EBCDIC) + ch = _ebcasc[orig_ch]; + else ch = orig_ch; + + processed_characters++; + + if ((obuf_cursor == obuf_columns) && + (ch != '\n') && + (ch != '\t')) { + put_line(&hwc_data.obuf[hwc_data.obuf_start], + obuf_columns); + obuf_cursor = 0; + obuf_count = 0; + } + + switch (ch) { + case '\n' : + put_line(&hwc_data.obuf[hwc_data.obuf_start], + obuf_count); + obuf_cursor = 0; + obuf_count = 0; + break; + + case '\a' : + hwc_data.obuf_start += obuf_count; + set_alarm(); + hwc_data.obuf_start -= obuf_count; + break; + + case '\t' : + do + { + if (obuf_cursor < obuf_columns) { + hwc_data.obuf[hwc_data.obuf_start + + obuf_cursor] + = 0x20; + obuf_cursor++; + } + else break; + } while (obuf_cursor % hwc_data.ioctls.width_htab); + break; + + case '\f' : + case '\v' : + spaces = obuf_cursor; + put_line(&hwc_data.obuf[hwc_data.obuf_start], + obuf_count); + obuf_count = obuf_cursor; + while (spaces) { + hwc_data.obuf[hwc_data.obuf_start + + obuf_cursor - spaces] + = 0x20; + spaces--; + } + break; + + case '\b' : + if (obuf_cursor) + obuf_cursor--; + break; + + case '\r' : + obuf_cursor = 0; + break; + + case 0x00 : + put_line(&hwc_data.obuf[hwc_data.obuf_start], + obuf_count); + obuf_cursor = 0; + obuf_count = 0; + goto out; + + default: + if (isprint(ch)) + hwc_data.obuf[hwc_data.obuf_start + + obuf_cursor++] + = (code == CODE_ASCII) ? + _ascebc[orig_ch]:orig_ch; + } + if (obuf_cursor > obuf_count) + obuf_count = obuf_cursor; + } + + + if (obuf_cursor) { + if (hwc_data.obuf_start || + (hwc_data.ioctls.final_nl == 0)) { + + put_line(&hwc_data.obuf[hwc_data.obuf_start], + obuf_count); + obuf_cursor = 0; + obuf_count = 0; + } + else { + if (hwc_data.ioctls.final_nl > 0) { + if (hwc_data.flags & HWC_TIMER_RUNS) { + hwc_data.write_timer.expires = + jiffies + + hwc_data.ioctls.final_nl*HZ/10; + } + else { + init_timer(&hwc_data.write_timer); + hwc_data.write_timer.function = + hwc_write_timeout; + hwc_data.write_timer.data = + (unsigned long)NULL; + hwc_data.write_timer.expires = + jiffies + + hwc_data.ioctls.final_nl*HZ/10; + add_timer(&hwc_data.write_timer); + hwc_data.flags |= HWC_TIMER_RUNS; + } + } + else ; + } + } + else ; +out : + if (!hwc_data.obuf_start) { + hwc_data.obuf_cursor = obuf_cursor; + hwc_data.obuf_count = obuf_count; + } + + if (write_time == IMMEDIATE_WRITE) + write_event_data_1(); + + return processed_characters; +} + + + + + +signed int hwc_write(int from_user, const unsigned char *msg, unsigned int count) +{ + unsigned long flags; + int retval; + + spin_lock_irqsave(&hwc_data.lock, flags); + retval = do_hwc_write(from_user, msg, count, hwc_data.ioctls.code, + IMMEDIATE_WRITE); + spin_unlock_irqrestore(&hwc_data.lock, flags); + return retval; +} + + + + + +unsigned int hwc_chars_in_buffer(unsigned char flag) +{ + unsigned short int number = 0; + unsigned long flags; + + spin_lock_irqsave(&hwc_data.lock, flags); + if (flag & IN_HWCB) + number += ALL_HWCB_CHAR; + + if (flag & IN_WRITE_BUF) + number += hwc_data.obuf_cursor; + + spin_unlock_irqrestore(&hwc_data.lock, flags); + return number; +} + +static inline int nr_setbits(kmem_pages_t arg) +{ + int i; + int nr = 0; + + for (i = 0; i < (sizeof(arg) << 3); i++) { + if (arg & 1) + nr++; + arg >>= 1; + } + return nr; +} + +unsigned int hwc_write_room(unsigned char flag) +{ + unsigned int number = 0; + unsigned long flags; + write_hwcb_t *hwcb; + + spin_lock_irqsave(&hwc_data.lock, flags); + if (flag & IN_HWCB) { + if (BUF_HWCB) { + hwcb = (write_hwcb_t *) BUF_HWCB; + number += MAX_HWCB_ROOM - hwcb->length; + } + number += (hwc_data.ioctls.kmem_hwcb - + nr_setbits(hwc_data.kmem_pages)) * + (MAX_HWCB_ROOM - + (sizeof(write_hwcb_t) + sizeof(mto_t))); + } + if (flag & IN_WRITE_BUF) + number += MAX_HWCB_ROOM - hwc_data.obuf_cursor; + spin_unlock_irqrestore(&hwc_data.lock, flags); + return number; +} + +void hwc_flush_buffer(unsigned char flag) +{ + unsigned long flags; + spin_lock_irqsave(&hwc_data.lock, flags); + if (flag & IN_HWCB) { + if (hwc_data.current_servc != HWC_CMDW_WRITEDATA) + flush_hwcbs(); + else hwc_data.flags |= FLUSH_HWCBS; + } + if (flag & IN_WRITE_BUF) { + hwc_data.obuf_cursor = 0; + hwc_data.obuf_count = 0; + } + spin_unlock_irqrestore(&hwc_data.lock, flags); +} + +unsigned short int seperate_cases(unsigned char *buf, unsigned short int count) +{ + unsigned short int i_in; + unsigned short int i_out = 0; + unsigned char _case = 0; + + for (i_in = 0; i_in < count; i_in++) { + if (buf[i_in] == hwc_data.ioctls.delim) { + if ((i_in + 1 < count) && + (buf[i_in + 1] == hwc_data.ioctls.delim)) { + buf[i_out] = hwc_data.ioctls.delim; + i_out++; + i_in++; + } else _case = ~_case; + } else { + if (_case) { + if (hwc_data.ioctls.tolower) + buf[i_out] = _ebc_toupper[buf[i_in]]; + else buf[i_out] = _ebc_tolower[buf[i_in]]; + } else buf[i_out] = buf[i_in]; + i_out++; + } + } + return i_out; +} + + + +#ifdef DUMP_HWCB_INPUT + +static int gds_vector_name(u16 id, unsigned char name[]) +{ + int retval = 0; + + switch (id) { + case GDS_ID_MDSMU : + name = "Multiple Domain Support Message Unit"; + break; + case GDS_ID_MDSRouteInfo : + name = "MDS Routing Information"; + break; + case GDS_ID_AgUnWrkCorr : + name = "Agent Unit of Work Correlator"; + break; + case GDS_ID_SNACondReport : + name = "SNA Condition Report"; + break; + case GDS_ID_CPMSU : + name = "CP Management Services Unit"; + break; + case GDS_ID_RoutTargInstr : + name = "Routing and Targeting Instructions"; + break; + case GDS_ID_OpReq : + name = "Operate Request"; + break; + case GDS_ID_TextCmd : + name = "Text Command"; + break; + + default : + name = "unknown GDS variable"; + retval = -EINVAL; + } + + return retval; +} +#endif + + + +inline static gds_vector_t * find_gds_vector( + gds_vector_t *start, void *end, u16 id) +{ + gds_vector_t *vec; + gds_vector_t *retval = NULL; + + vec = start; + + while (((void *) vec) < end) { + if (vec->gds_id == id) { + +#ifdef DUMP_HWCB_INPUT + int retval_name; + unsigned char name[64]; + + retval_name = gds_vector_name(id, name); + internal_print( + DELAYED_WRITE, + HWC_RW_PRINT_HEADER + "%s at 0x%x up to 0x%x, length: %d", + name, + (unsigned long) vec, + ((unsigned long) vec) + vec->length - 1, + vec->length); + if (retval_name < 0) + internal_print( + IMMEDIATE_WRITE, + ", id: 0x%x\n", + vec->gds_id); + else internal_print( + IMMEDIATE_WRITE, + "\n"); +#endif + retval = vec; + break; + } + + vec = (gds_vector_t *) (((unsigned long) vec) + vec->length); + } + + return retval; +} + + +inline static gds_subvector_t * find_gds_subvector( + gds_subvector_t *start, void *end, u8 key) +{ + gds_subvector_t *subvec; + gds_subvector_t *retval = NULL; + + subvec = start; + + while (((void *) subvec) < end) { + if (subvec->key == key) { + retval = subvec; + break; + } + subvec = (gds_subvector_t *) + (((unsigned long) subvec) + subvec->length); + } + return retval; +} + + +inline static int get_input(void *start, void *end) +{ + int count; + + count = ((unsigned long) end) - ((unsigned long) start); + + if (hwc_data.ioctls.tolower) + EBC_TOLOWER(start, count); + if (hwc_data.ioctls.delim) + count = seperate_cases(start, count); + if (hwc_data.ioctls.echo) + do_hwc_write(0, start, count, CODE_EBCDIC, IMMEDIATE_WRITE); + if (hwc_data.ioctls.code == CODE_ASCII) + EBCASC(start, count); + store_hwc_input(start, count); + return count; +} + + +inline static int eval_selfdeftextmsg(gds_subvector_t *start, void *end) +{ + gds_subvector_t *subvec; + void *subvec_data; + void *subvec_end; + int retval = 0; + + subvec = start; + + while (((void *) subvec) < end) { + subvec = find_gds_subvector(subvec, end, 0x30); + if (!subvec) + break; + subvec_data = (void *) + (((unsigned long) subvec) + + sizeof(gds_subvector_t)); + subvec_end = (void *) + (((unsigned long) subvec) + subvec->length); + retval += get_input(subvec_data, subvec_end); + subvec = (gds_subvector_t *) subvec_end; + } + return retval; +} + + +inline static int eval_textcmd(gds_subvector_t *start, void *end) +{ + gds_subvector_t *subvec; + gds_subvector_t *subvec_data; + void *subvec_end; + int retval = 0; + + subvec = start; + + while (((void *) subvec) < end) { + subvec = find_gds_subvector( + subvec, end, GDS_KEY_SelfDefTextMsg); + if (!subvec) + break; + subvec_data = (gds_subvector_t *) + (((unsigned long) subvec) + + sizeof(gds_subvector_t)); + subvec_end = (void *) + (((unsigned long) subvec) + subvec->length); + retval += eval_selfdeftextmsg(subvec_data, subvec_end); + subvec = (gds_subvector_t *) subvec_end; + } + + return retval; +} + + +inline static int eval_cpmsu(gds_vector_t *start, void *end) +{ + gds_vector_t *vec; + gds_subvector_t *vec_data; + void *vec_end; + int retval = 0; + + vec = start; + + while (((void *) vec) < end) { + vec = find_gds_vector(vec, end, GDS_ID_TextCmd); + if (!vec) + break; + vec_data = (gds_subvector_t *) + (((unsigned long) vec) + sizeof(gds_vector_t)); + vec_end = (void *) (((unsigned long) vec) + vec->length); + retval += eval_textcmd(vec_data, vec_end); + vec = (gds_vector_t *) vec_end; + } + + return retval; +} + + +inline static int eval_mdsmu(gds_vector_t *start, void *end) +{ + gds_vector_t *vec; + gds_vector_t *vec_data; + void *vec_end; + int retval = 0; + + vec = find_gds_vector(start, end, GDS_ID_CPMSU); + if (vec) { + vec_data = (gds_vector_t *) + (((unsigned long) vec) + sizeof(gds_vector_t)); + vec_end = (void *) (((unsigned long) vec) + vec->length); + retval = eval_cpmsu(vec_data, vec_end); + } + + return retval; +} + + +inline static int eval_evbuf(gds_vector_t *start, void *end) +{ + gds_vector_t *vec; + gds_vector_t *vec_data; + void *vec_end; + int retval = 0; + + vec = find_gds_vector(start, end, GDS_ID_MDSMU); + if (vec) { + vec_data = (gds_vector_t *) + (((unsigned long) vec) + sizeof(gds_vector_t)); + vec_end = (void *) (((unsigned long) vec) + vec->length); + retval = eval_mdsmu(vec_data, vec_end); + } + + return retval; +} + + +static int process_evbufs(void *start, void *end) +{ + int retval = 0; + evbuf_t *evbuf; + void *evbuf_end; + gds_vector_t *evbuf_data; + + evbuf = (evbuf_t *) start; + while (((void *) evbuf) < end) { + evbuf_data = (gds_vector_t *) + (((unsigned long) evbuf) + sizeof(evbuf_t)); + evbuf_end = (void *) (((unsigned long) evbuf) + evbuf->length); + switch (evbuf->type) { + case ET_OpCmd : + case ET_CntlProgOpCmd : + case ET_PMsgCmd : +#ifdef DUMP_HWCB_INPUT + + internal_print( + DELAYED_WRITE, + HWC_RW_PRINT_HEADER + "event buffer " + "at 0x%x up to 0x%x, length: %d\n", + (unsigned long) evbuf, + (unsigned long) (evbuf_end - 1), + evbuf->length); + dump_storage_area((void *)evbuf, evbuf->length); +#endif + retval += eval_evbuf(evbuf_data, evbuf_end); + break; + case ET_StateChange : + + retval = -ENOSYS; + break; + default : + printk( + KERN_WARNING + HWC_RW_PRINT_HEADER + "unconditional read: " + "unknown event buffer found, " + "type 0x%x", + evbuf->type); + retval = -ENOSYS; + } + evbuf = (evbuf_t *) evbuf_end; + } + return retval; +} + +static int unconditional_read_1(void) +{ + unsigned short int condition_code; + read_hwcb_t *hwcb = (read_hwcb_t *) hwc_data.page; + int retval; + + if ((!hwc_data.read_prio) && (!hwc_data.read_nonprio)) + return -EOPNOTSUPP; + if (hwc_data.current_servc) + return -EBUSY; + + memset(hwcb, 0x00, PAGE_SIZE); + memcpy(hwcb, &read_hwcb_template, sizeof(read_hwcb_t)); + + condition_code = service_call(HWC_CMDW_READDATA, hwc_data.page); + +#ifdef DUMP_HWC_READ_ERROR + if (condition_code == HWC_NOT_OPERATIONAL) + __asm__("LHI 1,0xe40\n\t" + "L 2,0(0,%0)\n\t" + "LRA 3,0(0,%1)\n\t" + "J .+0 \n\t" + : + :"a"(&condition_code), "a"(hwc_data.page) + :"1", "2", "3"); +#endif + + switch (condition_code) { + case HWC_COMMAND_INITIATED : + hwc_data.current_servc = HWC_CMDW_READDATA; + hwc_data.current_hwcb = hwc_data.page; + retval = condition_code; + break; + case HWC_BUSY : + retval = -EBUSY; + break; + default : + retval = -EIO; + } + + return retval; +} + +static int unconditional_read_2(void) +{ + read_hwcb_t *hwcb = (read_hwcb_t *) hwc_data.page; + +#ifdef DUMP_HWC_READ_ERROR + if ((hwcb->response_code != 0x0020) && + (hwcb->response_code != 0x0220) && + (hwcb->response_code != 0x60F0) && + (hwcb->response_code != 0x62F0)) + __asm__("LHI 1,0xe41\n\t" + "LRA 2,0(0,%0)\n\t" + "L 3,0(0,%1)\n\t" + "J .+0\n\t" + : + :"a"(hwc_data.page), "a"(&(hwcb->response_code)) + :"1", "2", "3"); +#endif + + hwc_data.current_servc = 0; + hwc_data.current_hwcb = NULL; + + switch (hwcb->response_code) { + case 0x0020 : + case 0x0220 : + return process_evbufs( + (void *) (((unsigned long) hwcb) + sizeof(read_hwcb_t)), + (void *) (((unsigned long) hwcb) + hwcb->length)); + case 0x60F0 : + case 0x62F0 : + return 0; + case 0x0100 : + internal_print( + IMMEDIATE_WRITE, + HWC_RW_PRINT_HEADER + "unconditional read: HWCB boundary violation - this " + "must not occur in a correct driver, please contact " + "author\n"); + return -EIO; + + case 0x0300 : + internal_print( + IMMEDIATE_WRITE, + HWC_RW_PRINT_HEADER + "unconditional read: " + "insufficient HWCB length - this must not occur in a " + "correct driver, please contact author\n"); + return -EIO; + + case 0x01F0 : + internal_print( + IMMEDIATE_WRITE, + HWC_RW_PRINT_HEADER + "unconditional read: " + "invalid command - this must not occur in a correct " + "driver, please contact author\n"); + return -EIO; + + case 0x40F0 : + internal_print( + IMMEDIATE_WRITE, + HWC_RW_PRINT_HEADER + "unconditional read: invalid function code - this " + "must not occur in a correct driver, please contact " + "author\n"); + return -EIO; + + case 0x70F0 : + internal_print( + IMMEDIATE_WRITE, + HWC_RW_PRINT_HEADER + "unconditional read: invalid selection mask - this " + "must not occur in a correct driver, please contact " + "author\n"); + return -EIO; + + case 0x0040 : + internal_print( + IMMEDIATE_WRITE, + HWC_RW_PRINT_HEADER + "unconditional read: HWC equipment check - don't " + "know how to handle this case\n"); + return -EIO; + + default : + internal_print( + IMMEDIATE_WRITE, + HWC_RW_PRINT_HEADER + "unconditional read: invalid response code %x - this " + "must not occur in a correct driver, please contact " + "author\n", + hwcb->response_code); + return -EIO; + } +} + +static int write_event_mask_1(void) +{ + unsigned int condition_code; + int retval; + + memcpy(hwc_data.page, &init_hwcb_template, sizeof(init_hwcb_t)); + condition_code = service_call(HWC_CMDW_WRITEMASK, hwc_data.page); +#ifdef DUMP_HWC_INIT_ERROR + if (condition_code != HWC_COMMAND_INITIATED) + __asm__("LHI 1,0xe10\n\t" + "L 2,0(0,%0)\n\t" + "LRA 3,0(0,%1)\n\t" + "J .+0\n\t" + : + :"a"(&condition_code), "a"(hwc_data.page) + :"1", "2", "3"); +#endif + + switch (condition_code) { + case HWC_COMMAND_INITIATED : + hwc_data.current_servc = HWC_CMDW_WRITEMASK; + hwc_data.current_hwcb = hwc_data.page; + retval = condition_code; + break; + case HWC_BUSY : + retval = -EBUSY; + break; + default : + retval = -EIO; + } + return retval; +} + + + + + +static int write_event_mask_2(void) +{ + init_hwcb_t *hwcb = (init_hwcb_t *) hwc_data.page; + int retval = 0; + + + if (hwcb->hwc_receive_mask & ET_Msg_Mask) + hwc_data.write_nonprio = 1; + + if (hwcb->hwc_receive_mask & ET_PMsgCmd_Mask) + hwc_data.write_prio = 1; + + if (hwcb->hwc_send_mask & ET_OpCmd_Mask) + hwc_data.read_nonprio = 1; + + if (hwcb->hwc_send_mask & ET_PMsgCmd_Mask) + hwc_data.read_nonprio = 1; + + if ((hwcb->response_code != 0x0020) || + (!hwc_data.write_nonprio) || + ((!hwc_data.read_nonprio) && (!hwc_data.read_prio))) +#ifdef DUMP_HWC_INIT_ERROR + __asm__("LHI 1,0xe11\n\t" + "LRA 2,0(0,%0)\n\t" + "L 3,0(0,%1)\n\t" + "J .+0\n\t" + : + :"a"(hwcb), "a"(&(hwcb->response_code)) + :"1", "2", "3"); +#else + retval = -EIO +#endif + + hwc_data.current_servc = 0; + hwc_data.current_hwcb = NULL; + + return retval; +} + +static int set_hwc_ioctls(hwc_ioctls_t *ioctls, char correct) +{ + int retval = 0; + hwc_ioctls_t tmp; + + if (ioctls->width_htab > MAX_MESSAGE_SIZE) { + if (correct) + tmp.width_htab = MAX_MESSAGE_SIZE; + else retval = -EINVAL; + } else tmp.width_htab = ioctls->width_htab; + + tmp.echo = ioctls->echo; + + if (ioctls->columns > MAX_MESSAGE_SIZE) { + if (correct) + tmp.columns = MAX_MESSAGE_SIZE; + else retval = -EINVAL; + } else tmp.columns = ioctls->columns; + + + switch (ioctls->code) { + case CODE_EBCDIC : + case CODE_ASCII : + tmp.code = ioctls->code; + break; + default : + if (correct) + tmp.code = CODE_ASCII; + else retval = -EINVAL; + } + + tmp.final_nl = ioctls->final_nl; + + if (ioctls->max_hwcb < 2) { + if (correct) + tmp.max_hwcb = 2; + else retval = -EINVAL; + } else tmp.max_hwcb = ioctls->max_hwcb; + + tmp.tolower = ioctls->tolower; + + if (ioctls->kmem_hwcb > ioctls->max_hwcb) { + if (correct) + tmp.kmem_hwcb = ioctls->max_hwcb; + else retval = -EINVAL; + } else tmp.kmem_hwcb = ioctls->kmem_hwcb; + + + if (ioctls->kmem_hwcb > MAX_KMEM_PAGES) { + if (correct) + ioctls->kmem_hwcb = MAX_KMEM_PAGES; + else retval = -EINVAL; + } + + if (ioctls->kmem_hwcb < 2) { + if (correct) + ioctls->kmem_hwcb = 2; + else retval = -EINVAL; + } + + tmp.delim = ioctls->delim; + + + if (!(retval < 0)) + hwc_data.ioctls = tmp; + + return retval; +} + +int hwc_init(unsigned long *kmem_start) +{ + int retval; +#ifdef BUFFER_STRESS_TEST + + init_hwcb_t *hwcb; + int i; + +#endif +#ifdef CONFIG_3215 + if (MACHINE_IS_VM) + return kmem_start; +#endif + spin_lock_init(&hwc_data.lock); + retval = write_event_mask_1(); + if (retval < 0) + return retval; +#ifdef USE_VM_DETECTION + if (MACHINE_IS_VM) { + if (hwc_data.init_ioctls.columns > 76) + hwc_data.init_ioctls.columns = 76; + hwc_data.init_ioctls.tolower = 1; + if (!hwc_data.init_ioctls.delim) + hwc_data.init_ioctls.delim = DEFAULT_CASE_DELIMITER; + } else { + hwc_data.init_ioctls.tolower = 0; + hwc_data.init_ioctls.delim = 0; + } +#endif + retval = set_hwc_ioctls(&hwc_data.init_ioctls, 1); + + *kmem_start = (*kmem_start + PAGE_SIZE - 1) & -4096L; + hwc_data.kmem_start = *kmem_start; + *kmem_start += hwc_data.ioctls.kmem_hwcb * PAGE_SIZE; + hwc_data.kmem_end = *kmem_start - 1; + + ctl_set_bit(0, 9); +#ifdef BUFFER_STRESS_TEST + internal_print( + DELAYED_WRITE, + HWC_RW_PRINT_HEADER + "use %i bytes for buffering.\n", + hwc_data.ioctls.kmem_hwcb * PAGE_SIZE); + for (i = 0; i < 500; i++) { + hwcb = (init_hwcb_t *) BUF_HWCB; + internal_print( + DELAYED_WRITE, + HWC_RW_PRINT_HEADER + "This is stress test message #%i, free: %i bytes\n", + i, + MAX_HWCB_ROOM - (hwcb->length + sizeof(mto_t))); + } + +#endif + return retval; +} + +void do_hwc_interrupt(void) +{ + spin_lock(&hwc_data.lock); + if (!hwc_data.current_servc) { + unconditional_read_1(); + } else { + switch (hwc_data.current_servc) { + case HWC_CMDW_WRITEMASK : + write_event_mask_2(); + break; + + case HWC_CMDW_WRITEDATA : + write_event_data_2(); + break; + + case HWC_CMDW_READDATA : + unconditional_read_2(); + break; + } + write_event_data_1(); + } + wake_up_hwc_tty(); + spin_unlock(&hwc_data.lock); +} + + +int hwc_ioctl(unsigned int cmd, unsigned long arg) +{ + hwc_ioctls_t tmp = hwc_data.ioctls; + int retval = 0; + unsigned long flags; + unsigned int obuf; + + spin_lock_irqsave(&hwc_data.lock, flags); + + switch (cmd) { + case TIOCHWCSHTAB : + if (get_user(tmp.width_htab, (ioctl_htab_t *) arg)) + goto fault; + break; + + case TIOCHWCSECHO : + if (get_user(tmp.echo, (ioctl_echo_t *) arg)) + goto fault; + break; + + case TIOCHWCSCOLS : + if (get_user(tmp.columns, (ioctl_cols_t *) arg)) + goto fault; + break; + + case TIOCHWCSCODE : + if (get_user(tmp.code, (ioctl_code_t *) arg)) + goto fault; + + break; + + case TIOCHWCSNL : + if (get_user(tmp.final_nl, (ioctl_nl_t *) arg)) + goto fault; + break; + + case TIOCHWCSOBUF : + if (get_user(obuf, (unsigned int *) arg)) + goto fault; + if (obuf & 0xFFF) + tmp.max_hwcb = (((obuf | 0xFFF) + 1) >> 12); + else tmp.max_hwcb = (obuf >> 12); + break; + + + case TIOCHWCSCASE : + if (get_user(tmp.tolower, (ioctl_case_t *) arg)) + goto fault; + break; + + case TIOCHWCSDELIM : + if (get_user(tmp.delim, (ioctl_delim_t *) arg)) + goto fault; + break; + + + case TIOCHWCSINIT : + retval = set_hwc_ioctls(&hwc_data.init_ioctls, 1); + break; + + + case TIOCHWCGHTAB : + if (put_user(tmp.width_htab, (ioctl_htab_t *) arg)) + goto fault; + break; + + + case TIOCHWCGECHO : + if (put_user(tmp.echo, (ioctl_echo_t *) arg)) + goto fault; + break; + + + case TIOCHWCGCOLS : + if (put_user(tmp.columns, (ioctl_cols_t *) arg)) + goto fault; + break; + + + case TIOCHWCGCODE : + if (put_user(tmp.code, (ioctl_code_t *) arg)) + goto fault; + + break; + + + case TIOCHWCGNL : + if (put_user(tmp.final_nl, (ioctl_nl_t *) arg)) + goto fault; + break; + + + case TIOCHWCGOBUF : + if (put_user(tmp.max_hwcb, (ioctl_obuf_t *) arg)) + goto fault; + break; + + + case TIOCHWCGKBUF : + if (put_user(tmp.kmem_hwcb, (ioctl_obuf_t *) arg)) + goto fault; + break; + + + case TIOCHWCGCASE : + if (put_user(tmp.tolower, (ioctl_case_t *) arg)) + goto fault; + break; + + case TIOCHWCGDELIM : + if (put_user(tmp.delim, (ioctl_delim_t *) arg)) + goto fault; + break; +#if 0 + + case TIOCHWCGINIT : + if (put_user(&hwc_data.init_ioctls, (hwc_ioctls_t *) arg)) + goto fault; + break; + + + case TIOCHWCGCURR : + if (put_user(&hwc_data.ioctls, (hwc_ioctls_t *) arg)) + goto fault; + break; +#endif + + default : + goto noioctlcmd; + } + + + if (_IOC_DIR(cmd) == _IOC_WRITE) + retval = set_hwc_ioctls(&tmp, 0); + + goto out; + + fault: + retval = -EFAULT; + goto out; + noioctlcmd: + retval = -ENOIOCTLCMD; + out: + spin_unlock_irqrestore(&hwc_data.lock, flags); + return retval; +} diff -u --recursive --new-file v2.2.13/linux/drivers/s390/char/hwc_rw.h linux/drivers/s390/char/hwc_rw.h --- v2.2.13/linux/drivers/s390/char/hwc_rw.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/char/hwc_rw.h Tue Jan 4 10:12:18 2000 @@ -0,0 +1,210 @@ +/* + * drivers/s390/char/hwc_rw.h + * interface to the HWC-read/write driver + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Peschke + */ + +#ifndef __HWC_RW_H__ +#define __HWC_RW_H__ + + +#include + + + + + + + +#ifndef __HWC_RW_C__ + + + + + + + +extern int hwc_init(unsigned long *); + + + + + + +extern int hwc_write(int from_user, const unsigned char *, unsigned int); + + + + + + + +extern unsigned int hwc_chars_in_buffer(unsigned char); + + + + + + + +extern unsigned int hwc_write_room(unsigned char); + + + + + + + +extern void hwc_flush_buffer(unsigned char); + + + + + + +extern signed int hwc_ioctl(unsigned int, unsigned long); + + + + + + + + + +extern void do_hwc_interrupt(void); + + + + + + +extern int hwc_printk(const char *, ...); + +#else + + + + + +extern void store_hwc_input(unsigned char*, unsigned int); + + + + +extern void wake_up_hwc_tty(void); + +#endif + + + + + + + + + + + + + + + +#define IN_HWCB 1 +#define IN_WRITE_BUF 2 +#define IN_BUFS_TOTAL (IN_HWCB | IN_WRITE_BUF) + + + +typedef unsigned short int ioctl_htab_t; +typedef unsigned char ioctl_echo_t; +typedef unsigned short int ioctl_cols_t; +typedef unsigned char ioctl_code_t; +typedef signed char ioctl_nl_t; +typedef unsigned short int ioctl_obuf_t; +typedef unsigned char ioctl_case_t; +typedef unsigned char ioctl_delim_t; + + +typedef struct { + ioctl_htab_t width_htab; + ioctl_echo_t echo; + ioctl_cols_t columns; + ioctl_code_t code; + ioctl_nl_t final_nl; + ioctl_obuf_t max_hwcb; + ioctl_obuf_t kmem_hwcb; + ioctl_case_t tolower; + ioctl_delim_t delim; +} hwc_ioctls_t; + + + + + + +static hwc_ioctls_t _hwc_ioctls; + + + + + + + +#define HWC_IOCTL_LETTER 'B' + + +#define TIOCHWCSHTAB _IOW(HWC_IOCTL_LETTER, 0, _hwc_ioctls.width_htab) + +#define TIOCHWCSECHO _IOW(HWC_IOCTL_LETTER, 1, _hwc_ioctls.echo) + +#define TIOCHWCSCOLS _IOW(HWC_IOCTL_LETTER, 2, _hwc_ioctls.columns) + +#define TIOCHWCSCODE _IOW(HWC_IOCTL_LETTER, 3, _hwc_ioctls.code) + +#define TIOCHWCSNL _IOW(HWC_IOCTL_LETTER, 4, _hwc_ioctls.final_nl) + +#define TIOCHWCSOBUF _IOW(HWC_IOCTL_LETTER, 5, _hwc_ioctls.max_hwcb) + +#define TIOCHWCSINIT _IO(HWC_IOCTL_LETTER, 6) + +#define TIOCHWCSCASE _IOW(HWC_IOCTL_LETTER, 7, _hwc_ioctls.tolower) + + +#define TIOCHWCSDELIM _IOW(HWC_IOCTL_LETTER, 9, _hwc_ioctls.delim) + + +#define TIOCHWCGHTAB _IOR(HWC_IOCTL_LETTER, 10, _hwc_ioctls.width_htab) + +#define TIOCHWCGECHO _IOR(HWC_IOCTL_LETTER, 11, _hwc_ioctls.echo) + +#define TIOCHWCGCOLS _IOR(HWC_IOCTL_LETTER, 12, _hwc_ioctls.columns) + +#define TIOCHWCGCODE _IOR(HWC_IOCTL_LETTER, 13, _hwc_ioctls.code) + +#define TIOCHWCGNL _IOR(HWC_IOCTL_LETTER, 14, _hwc_ioctls.final_nl) + +#define TIOCHWCGOBUF _IOR(HWC_IOCTL_LETTER, 15, _hwc_ioctls.max_hwcb) + +#define TIOCHWCGINIT _IOR(HWC_IOCTL_LETTER, 16, _hwc_ioctls) + +#define TIOCHWCGCASE _IOR(HWC_IOCTL_LETTER, 17, _hwc_ioctls.tolower) + + +#define TIOCHWCGDELIM _IOR(HWC_IOCTL_LETTER, 19, _hwc_ioctls.delim) + +#define TIOCHWCGKBUF _IOR(HWC_IOCTL_LETTER, 20, _hwc_ioctls.max_hwcb) + +#define TIOCHWCGCURR _IOR(HWC_IOCTL_LETTER, 21, _hwc_ioctls) + + + + +#define CODE_ASCII 0x0 +#define CODE_EBCDIC 0x1 + + +#endif diff -u --recursive --new-file v2.2.13/linux/drivers/s390/char/hwc_tty.c linux/drivers/s390/char/hwc_tty.c --- v2.2.13/linux/drivers/s390/char/hwc_tty.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/char/hwc_tty.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,234 @@ +/* + * drivers/s390/char/hwc_tty.c + * HWC line mode terminal driver. + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Peschke + * + * Thanks to Martin Schwidefsky. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "hwc_rw.h" + +#define HWC_TTY_PRINT_HEADER "hwc tty driver: " +#define HWC_TTY_BUF_SIZE 512 + +typedef struct { + struct tty_struct *tty; + unsigned char buf[HWC_TTY_BUF_SIZE]; + unsigned short int buf_count; + spinlock_t lock; +} hwc_tty_data_struct; + +static hwc_tty_data_struct hwc_tty_data; +static struct tty_driver hwc_tty_driver; +static struct tty_struct * hwc_tty_table[1]; +static struct termios * hwc_tty_termios[1]; +static struct termios * hwc_tty_termios_locked[1]; +static int hwc_tty_refcount = 0; + +extern struct termios tty_std_termios; + +static int hwc_tty_open(struct tty_struct *tty, + struct file *filp) +{ + if (MINOR(tty->device) - tty->driver.minor_start) + return -ENODEV; + + tty->driver_data = &hwc_tty_data; + hwc_tty_data.buf_count = 0; + hwc_tty_data.tty = tty; + tty->low_latency = 0; + + return 0; +} + +void wake_up_hwc_tty(void) +{ + if ((hwc_tty_data.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + hwc_tty_data.tty->ldisc.write_wakeup) + (hwc_tty_data.tty->ldisc.write_wakeup)(hwc_tty_data.tty); + wake_up_interruptible(&hwc_tty_data.tty->write_wait); +} + +static void hwc_tty_close(struct tty_struct *tty, + struct file *filp) +{ + if (MINOR(tty->device) != tty->driver.minor_start) { + printk(KERN_WARNING HWC_TTY_PRINT_HEADER + "do not close hwc tty because of wrong device number"); + return; + } + + hwc_tty_data.tty = NULL; +} + +static int hwc_tty_write_room (struct tty_struct *tty) +{ + int retval; + + retval = hwc_write_room(IN_BUFS_TOTAL); + return retval; +} + +static int hwc_tty_write(struct tty_struct *tty, + int from_user, + const unsigned char *buf, + int count) +{ + int retval; + + if (hwc_tty_data.buf_count > 0) + { + hwc_write(0, hwc_tty_data.buf, hwc_tty_data.buf_count); + hwc_tty_data.buf_count = 0; + } + retval = hwc_write(from_user, buf, count); + return retval; +} + +static void hwc_tty_put_char(struct tty_struct *tty, + unsigned char ch) +{ + unsigned long flags; + + spin_lock_irqsave(&hwc_tty_data.lock, flags); + if (hwc_tty_data.buf_count >= HWC_TTY_BUF_SIZE) + { + hwc_write(0, hwc_tty_data.buf, hwc_tty_data.buf_count); + hwc_tty_data.buf_count = 0; + } + hwc_tty_data.buf[hwc_tty_data.buf_count] = ch; + hwc_tty_data.buf_count++; + spin_unlock_irqrestore(&hwc_tty_data.lock, flags); +} + +static void hwc_tty_flush_chars(struct tty_struct *tty) +{ + unsigned long flags; + + spin_lock_irqsave(&hwc_tty_data.lock, flags); + hwc_write(0, hwc_tty_data.buf, hwc_tty_data.buf_count); + hwc_tty_data.buf_count = 0; + spin_unlock_irqrestore(&hwc_tty_data.lock, flags); +} + + +static int hwc_tty_chars_in_buffer(struct tty_struct *tty) +{ + int retval; + + retval = hwc_chars_in_buffer(IN_BUFS_TOTAL); + return retval; +} + +static void hwc_tty_flush_buffer(struct tty_struct *tty) +{ + wake_up_hwc_tty(); +} + +static int hwc_tty_ioctl( + struct tty_struct *tty, + struct file * file, + unsigned int cmd, + unsigned long arg) +{ + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + + return hwc_ioctl(cmd, arg); +} + +void store_hwc_input(unsigned char* buf, unsigned int count) +{ + struct tty_struct *tty = hwc_tty_data.tty; + + if (tty != NULL) { + if (count == 2 && strncmp(buf, "^c", 2) == 0) { + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_NORMAL; + *tty->flip.char_buf_ptr++ = INTR_CHAR(tty); + } else if (count == 2 && strncmp(buf, "^d", 2) == 0) { + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_NORMAL; + *tty->flip.char_buf_ptr++ = EOF_CHAR(tty); + } else if (count == 2 && strncmp(buf, "^z", 2) == 0) { + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_NORMAL; + *tty->flip.char_buf_ptr++ = SUSP_CHAR(tty); + } else { + memcpy(tty->flip.char_buf_ptr, buf, count); + if (count < 2 || + strncmp(buf + count - 2, "^n", 2)) { + tty->flip.char_buf_ptr[count] = '\n'; + count++; + } else count -= 2; + memset(tty->flip.flag_buf_ptr, TTY_NORMAL, count); + tty->flip.char_buf_ptr += count; + tty->flip.flag_buf_ptr += count; + tty->flip.count += count; + } + tty_flip_buffer_push(tty); + wake_up_hwc_tty(); + } +} + +void hwc_tty_init(void) +{ + memset (&hwc_tty_driver, 0, sizeof(struct tty_driver)); + hwc_tty_driver.magic = TTY_DRIVER_MAGIC; + hwc_tty_driver.driver_name = "tty_hwc"; + hwc_tty_driver.name = "ttyS"; + hwc_tty_driver.name_base = 0; + hwc_tty_driver.major = TTY_MAJOR; + hwc_tty_driver.minor_start = 64; + hwc_tty_driver.num = 1; + hwc_tty_driver.type = TTY_DRIVER_TYPE_SYSTEM; + hwc_tty_driver.subtype = SYSTEM_TYPE_TTY; + hwc_tty_driver.init_termios = tty_std_termios; + hwc_tty_driver.init_termios.c_iflag = IGNBRK | IGNPAR; + hwc_tty_driver.init_termios.c_oflag = ONLCR; + hwc_tty_driver.init_termios.c_lflag = ISIG | ECHO; + hwc_tty_driver.flags = TTY_DRIVER_REAL_RAW; + hwc_tty_driver.refcount = &hwc_tty_refcount; + + hwc_tty_driver.table = hwc_tty_table; + hwc_tty_driver.termios = hwc_tty_termios; + hwc_tty_driver.termios_locked = hwc_tty_termios_locked; + + hwc_tty_driver.open = hwc_tty_open; + hwc_tty_driver.close = NULL /* hwc_tty_close */; + hwc_tty_driver.write = hwc_tty_write; + hwc_tty_driver.put_char = hwc_tty_put_char; + hwc_tty_driver.flush_chars = hwc_tty_flush_chars; + hwc_tty_driver.write_room = hwc_tty_write_room; + hwc_tty_driver.chars_in_buffer = hwc_tty_chars_in_buffer; + hwc_tty_driver.flush_buffer = hwc_tty_flush_buffer; + hwc_tty_driver.ioctl = hwc_tty_ioctl; + + hwc_tty_driver.throttle = NULL; + hwc_tty_driver.unthrottle = NULL; + hwc_tty_driver.send_xchar = NULL; + hwc_tty_driver.set_termios = NULL; + hwc_tty_driver.set_ldisc = NULL; + hwc_tty_driver.stop = NULL; + hwc_tty_driver.start = NULL; + hwc_tty_driver.hangup = NULL; + hwc_tty_driver.break_ctl = NULL; + hwc_tty_driver.wait_until_sent = NULL; + hwc_tty_driver.read_proc = NULL; + hwc_tty_driver.write_proc = NULL; + + if (tty_register_driver(&hwc_tty_driver)) + panic("Couldn't register hwc_tty driver\n"); +} diff -u --recursive --new-file v2.2.13/linux/drivers/s390/ebcdic.c linux/drivers/s390/ebcdic.c --- v2.2.13/linux/drivers/s390/ebcdic.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/ebcdic.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,246 @@ +/* + * arch/s390/kernel/ebcdic.c + * ECBDIC -> ASCII, ASCII -> ECBDIC conversion tables. + * + * S390 version + * Copyright (C) 1998 IBM Corporation + * Author(s): Martin Schwidefsky + */ + +#include + +/* + * ASCII -> EBCDIC + */ +__u8 _ascebc[256] = +{ + /*00 NL SH SX EX ET NQ AK BL */ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, + /*08 BS HT LF VT FF CR SO SI */ + 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /*10 DL D1 D2 D3 D4 NK SN EB */ + 0x10, 0x11, 0x12, 0x13, 0x3C, 0x15, 0x32, 0x26, + /*18 CN EM SB EC FS GS RS US */ + 0x18, 0x19, 0x3F, 0x27, 0x1C, 0x1D, 0x1E, 0x1F, + /*20 SP ! " # $ % & ' */ + 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, + /*28 ( ) * + , - . / */ + 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, + /*30 0 1 2 3 4 5 6 7 */ + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + /*38 8 9 : ; < = > ? */ + 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, + /*40 @ A B C D E F G */ + 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + /*48 H I J K L M N O */ + 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + /*50 P Q R S T U V W */ + 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, + /*58 X Y Z [ \ ] ^ _ */ + 0xE7, 0xE8, 0xE9, 0xAD, 0xE0, 0xBD, 0x5F, 0x6D, + /*60 ` a b c d e f g */ + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + /*68 h i j k l m n o */ + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + /*70 p q r s t u v w */ + 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + /*78 x y z { | } ~ DL */ + 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0xFF +}; + +/* + * EBCDIC -> ASCII + */ +__u8 _ebcasc[256] = +{ + /* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */ + 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, + /* 0x08 -GE -SPS -RPT VT FF CR SO SI */ + 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC + -ENP ->LF */ + 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, + /* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB + -IUS */ + 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + /* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC + -INP */ + 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, + /* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL + -SW */ + 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, + /* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */ + 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, + /* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */ + 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, + /* 0x40 SP RSP ä ---- */ + 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, + /* 0x48 . < ( + | */ + 0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, + /* 0x50 & ---- */ + 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, + /* 0x58 ß ! $ * ) ; */ + 0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA, + /* 0x60 - / ---- Ä ---- ---- ---- */ + 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, + /* 0x68 ---- , % _ > ? */ + 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, + /* 0x70 ---- ---- ---- ---- ---- ---- ---- */ + 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + /* 0x78 * ` : # @ ' = " */ + 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, + /* 0x80 * a b c d e f g */ + 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + /* 0x88 h i ---- ---- ---- */ + 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, + /* 0x90 ° j k l m n o p */ + 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, + /* 0x98 q r ---- ---- */ + 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, + /* 0xA0 ~ s t u v w x */ + 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + /* 0xA8 y z ---- ---- ---- ---- */ + 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, + /* 0xB0 ^ ---- § ---- */ + 0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, + /* 0xB8 ---- [ ] ---- ---- ---- ---- */ + 0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07, + /* 0xC0 { A B C D E F G */ + 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + /* 0xC8 H I ---- ö ---- */ + 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, + /* 0xD0 } J K L M N O P */ + 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + /* 0xD8 Q R ---- ü */ + 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, + /* 0xE0 \ S T U V W X */ + 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + /* 0xE8 Y Z ---- Ö ---- ---- ---- */ + 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, + /* 0xF0 0 1 2 3 4 5 6 7 */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + /* 0xF8 8 9 ---- ---- Ü ---- ---- ---- */ + 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07 +}; + +/* + * EBCDIC (capitals) -> ASCII (small case) + */ +__u8 _ebcasc_reduce_case[256] = +{ + /* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */ + 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, + + /* 0x08 -GE -SPS -RPT VT FF CR SO SI */ + 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + + /* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC + -ENP ->LF */ + 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, + + /* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB + -IUS */ + 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + + /* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC + -INP */ + 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, + + /* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL + -SW */ + 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, + + /* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */ + 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, + + /* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */ + 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, + + /* 0x40 SP RSP ä ---- */ + 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, + + /* 0x48 . < ( + | */ + 0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, + + /* 0x50 & ---- */ + 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, + + /* 0x58 ß ! $ * ) ; */ + 0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA, + + /* 0x60 - / ---- Ä ---- ---- ---- */ + 0x2D, 0x2F, 0x07, 0x84, 0x07, 0x07, 0x07, 0x8F, + + /* 0x68 ---- , % _ > ? */ + 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, + + /* 0x70 ---- ---- ---- ---- ---- ---- ---- */ + 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + + /* 0x78 * ` : # @ ' = " */ + 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, + + /* 0x80 * a b c d e f g */ + 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + + /* 0x88 h i ---- ---- ---- */ + 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, + + /* 0x90 ° j k l m n o p */ + 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, + + /* 0x98 q r ---- ---- */ + 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, + + /* 0xA0 ~ s t u v w x */ + 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + + /* 0xA8 y z ---- ---- ---- ---- */ + 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, + + /* 0xB0 ^ ---- § ---- */ + 0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, + + /* 0xB8 ---- [ ] ---- ---- ---- ---- */ + 0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07, + + /* 0xC0 { A B C D E F G */ + 0x7B, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + + /* 0xC8 H I ---- ö ---- */ + 0x68, 0x69, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, + + /* 0xD0 } J K L M N O P */ + 0x7D, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, + + /* 0xD8 Q R ---- ü */ + 0x71, 0x72, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, + + /* 0xE0 \ S T U V W X */ + 0x5C, 0xF6, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + + /* 0xE8 Y Z ---- Ö ---- ---- ---- */ + 0x79, 0x7A, 0xFD, 0x07, 0x94, 0x07, 0x07, 0x07, + + /* 0xF0 0 1 2 3 4 5 6 7 */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + + /* 0xF8 8 9 ---- ---- Ü ---- ---- ---- */ + 0x38, 0x39, 0x07, 0x07, 0x81, 0x07, 0x07, 0x07 +}; diff -u --recursive --new-file v2.2.13/linux/drivers/s390/misc/Makefile linux/drivers/s390/misc/Makefile --- v2.2.13/linux/drivers/s390/misc/Makefile Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/misc/Makefile Tue Jan 4 10:12:18 2000 @@ -0,0 +1,8 @@ +all: s390-misc.o + +CFLAFS += +O_TARGET := s390-misc.o +O_OBJS := +M_OBJS := + +include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.2.13/linux/drivers/s390/net/Makefile linux/drivers/s390/net/Makefile --- v2.2.13/linux/drivers/s390/net/Makefile Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/net/Makefile Tue Jan 4 10:12:18 2000 @@ -0,0 +1,16 @@ +all: s390-net.o + +CFLAFS += +O_TARGET := s390-net.o +O_OBJS := +M_OBJS := + +ifeq ($(CONFIG_CTC),y) + O_OBJS += ctc.o +endif + +ifeq ($(CONFIG_IUCV),y) + O_OBJS += iucv.o +endif + +include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.2.13/linux/drivers/s390/net/ctc.c linux/drivers/s390/net/ctc.c --- v2.2.13/linux/drivers/s390/net/ctc.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/net/ctc.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,1408 @@ +/* + * drivers/s390/net/ctc.c + * CTC / ESCON network driver + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Dieter Wellerdiek (wel@de.ibm.com) + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../../../arch/s390/kernel/irq.h" + + +//#define DEBUG + +/* Redefine message level, so that all messages occure on 3215 console in DEBUG mode */ +#ifdef DEBUG + #undef KERN_INFO + #undef KERN_WARNING + #undef KERN_DEBUG + #define KERN_INFO KERN_EMERG + #define KERN_WARNING KERN_EMERG + #define KERN_DEBUG KERN_EMERG +#endif +//#undef DEBUG + +#define CCW_CMD_WRITE 0x01 +#define CCW_CMD_READ 0x02 +#define CCW_CMD_SET_EXTENDED 0xc3 +#define CCW_CMD_PREPARE 0xe3 + +#define MAX_DEVICES 16 +#define MAX_ADAPTERS MAX_DEVICES / 2 +#define CTC_DEFAULT_MTU_SIZE 1500 +#define READ 0 +#define WRITE 1 +#define CTC 0 +#define ESCON 1 +#define CHANNEL_MEDIA 2 +#define CTC_BLOCKS 8 /* 8 blocks * 2 times * 64k = 1M */ + +#define TB_TX 0 /* sk buffer handling in process */ +#define TB_STOP 1 /* network device stop in process */ +#define TB_RETRY 2 /* retry in process */ +#define TB_NOBUFFER 3 /* no buffer on free queue */ + +/* state machine codes used in ctc_irq_handler */ +#define CTC_STOP 0 +#define CTC_START_HALT_IO 1 +#define CTC_START_SET_X_MODE 2 +#define CTC_START_SELECT 4 +#define CTC_START_READ_TEST 32 +#define CTC_START_READ 33 +#define CTC_START_WRITE_TEST 64 +#define CTC_START_WRITE 65 + + +typedef enum { + channel_type_none, /* Device is not a channel */ + channel_type_undefined, /* Device is a channel but we dont know anything about it */ + channel_type_ctca, /* Device is a CTC/A and we can deal with it */ + channel_type_escon, /* Device is a ESCON channel and we can deal with it */ + channel_type_unsupported /* Device is a unsupported model */ +} channel_type_t; + + + +/* + * Structures needed in the initial phase + * + */ + +static int channel_tab_initialized = 0; /* channel[] structure initialized */ + +struct devicelist { + unsigned int devno; + __u8 flag; +#define CHANNEL_IN_USE 0x08 /* - Show that channel is in use */ +}; + +static struct { + struct devicelist list[MAX_DEVICES]; + int count; + int left; +} channel[CHANNEL_MEDIA]; + + + +static int ctc_no_auto = 0; + +struct adapterlist{ + unsigned int devno[2]; + __u16 protocol; +}; + +static struct adapterlist ctc_adapter[CHANNEL_MEDIA][MAX_ADAPTERS]; + + +/* + * Structure used after the initial phase + * + */ + +struct buffer { + struct buffer *next; + int packets; + struct block *block; +}; + + +struct channel { + unsigned int devno; + int irq; + int IO_active; + ccw1_t ccw[3]; + __u32 state; + int buffer_count; + struct buffer *free_anchor; + struct buffer *proc_anchor; + devstat_t *devstat; + struct device *dev; /* backward pointer to the network device */ + struct wait_queue *wait; + struct tq_struct tq; + struct timer_list timer; + unsigned long flag_a; /* atomic flags */ +#define CTC_BH_ACTIVE 0 + __u8 last_dstat; + __u8 flag; +#define CTC_WRITE 0x01 /* - Set if this is a write channel */ +#define CTC_TIMER 0x80 /* - Set if timer made the wake_up */ +}; + + +struct ctc_priv { + struct net_device_stats stats; + struct channel channel[2]; + __u16 protocol; +}; + +/* + * This structure works as shuttle between two systems + * - A block can contain one or more packets + */ + +#define PACKET_HEADER_LENGTH 6 +struct packet { + __u16 length; + __u16 type; + __u16 unused; + __u8 data; +}; + +#define BLOCK_HEADER_LENGTH 2 +struct block { + __u16 length; + struct packet data; +}; + + +/* Interrupt handler */ +static void ctc_irq_handler(int irq, void *initparm, struct pt_regs *regs); +static void ctc_irq_bh(struct channel *ctc); +static void ctc_read_retry (struct channel *ctc); +static void ctc_write_retry (struct channel *ctc); + + +/* Functions for the DEV methods */ +void ctc_setup(char *dev_name, int *ints); +int ctc_probe(struct device *dev); + + +static int ctc_open(struct device *dev); +static void ctc_timer (struct channel *ctc); +static int ctc_release(struct device *dev); +static int ctc_tx(struct sk_buff *skb, struct device *dev); +static int ctc_change_mtu(struct device *dev, int new_mtu); +struct net_device_stats* ctc_stats(struct device *dev); + + +/* + * Channel Routines + * + */ + +static void channel_init(void); +static void channel_scan(void); +static int channel_get(int media, int devno); +static int channel_get_next(int media); +static int channel_free(int media, int devno); +static channel_type_t channel_check_for_type (senseid_t *id); +static void channel_sort(struct devicelist list[], int n); + + +/* + * initialize the channel[].list + */ +static void channel_init(void) +{ + int m; +#ifdef DEBUG + int c; +#endif + + if (!test_and_set_bit(0, (void *)& channel_tab_initialized)){ + channel_scan(); + for (m = 0; m < CHANNEL_MEDIA; m++) { + channel_sort (channel[m].list, MAX_DEVICES); + channel[m].left = channel[m].count; + } + if (channel[CTC].count == 0 && channel[ESCON].count == 0) + printk(KERN_INFO "channel: no Channel devices recognized\n"); + else + printk(KERN_INFO "channel: %d Parallel channel found - %d ESCON channel found\n", + channel[CTC].count, channel[ESCON].count); +#ifdef DEBUG + for (m = 0; m < CHANNEL_MEDIA; m++) { + for (c = 0; c < MAX_DEVICES; c++){ + printk(KERN_DEBUG "channel: Adapter=%x Entry=%x devno=%04x\n", + m, c, channel[m].list[c].devno); + } + } +#endif + } +} + + +/* +* scan for all channels and put the device numbers into the channel[].list +*/ +static void channel_scan(void) +{ + int m; + int c; + int irq; + dev_info_t temp; + + for (m = 0; m < CHANNEL_MEDIA; m++) { + for (c = 0; c < MAX_DEVICES; c++){ + channel[m].list[c].devno = -ENODEV; + } + } + + for (irq = 0; irq < NR_IRQS; irq++) { + /* CTC/A */ + if (channel[CTC].count < MAX_DEVICES ) { + if (get_dev_info(irq, &temp) == 0 && + channel_check_for_type(&temp.sid_data) == channel_type_ctca) { + channel[CTC].list[channel[CTC].count].devno = temp.devno; + channel[CTC].count++; + } + } + + /* ESCON */ + if (channel[ESCON].count < MAX_DEVICES ) { + if (get_dev_info(irq, &temp) == 0 && + channel_check_for_type(&temp.sid_data) == channel_type_escon) { + channel[ESCON].list[channel[ESCON].count].devno = temp.devno; + channel[ESCON].count++; + + } + } + } +} + + +/* + * free specific channel from the channel[].list + */ +static int channel_free(int media, int devno) +{ + int i; + + for (i = 0; i < channel[media].count; i++) { + if ((devno == channel[media].list[i].devno) && + ((channel[media].list[i].flag & CHANNEL_IN_USE) != 0x00)) { + channel[media].list[i].flag &= ~CHANNEL_IN_USE; + return 0; + } + } + printk(KERN_WARNING "channel: dev %04x is not a channel or in use\n", devno); + return -ENODEV; +} + + +/* + * get specific channel from the channel[].list + */ +static int channel_get(int media, int devno) +{ + int i; + + for (i = 0; i < channel[media].count; i++) { + if ((devno == channel[media].list[i].devno) && + ((channel[media].list[i].flag & CHANNEL_IN_USE) == 0x00)) { + channel[media].list[i].flag |= CHANNEL_IN_USE; + return channel[media].list[i].devno; + } + } + printk(KERN_WARNING "channel: dev %04x is not a channel or in use\n", devno); + return -ENODEV; + +} + + +/* + * get the next free channel from the channel[].list + */ +static int channel_get_next(int media) +{ + int i; + + for (i = 0; i < channel[media].count; i++) { + if ((channel[media].list[i].flag & CHANNEL_IN_USE) == 0x00) { +#ifdef DEBUG + printk(KERN_DEBUG "channel: picked=%04x\n", channel[media].list[i].devno); +#endif + channel[media].list[i].flag |= CHANNEL_IN_USE; + return channel[media].list[i].devno; + } + } + return -ENODEV; +} + + +/* + * picks the next free channel from the channel[].list + */ +static int channel_left(int media) +{ + return channel[media].left; +} + + +/* + * defines all devices which are channels + */ +static channel_type_t channel_check_for_type (senseid_t *id) + { + channel_type_t type; + + switch (id->cu_type) { + case 0x3088: + + switch (id->cu_model) { + case 0x08: + type = channel_type_ctca; /* 3088/08 ==> CTCA */ + break; + + case 0x1F: + type = channel_type_escon; /* 3088/1F ==> ESCON channel */ + break; + + case 0x01: /* 3088/01 ==> P390 OSA emulation */ + case 0x60: /* 3088/60 ==> OSA/2 Adapter */ + type = channel_type_unsupported; + break; + + default: + type = channel_type_undefined; + printk(KERN_INFO "channel: Unknown model found 3088/%02x\n",id->cu_model); + } + break; + + default: + type = channel_type_none; + + } + return type; +} + + +/* + * sort the channel[].list + */ +static void channel_sort(struct devicelist list[], int n) +{ + int i; + int sorted = 0; + struct devicelist tmp; + + while (!sorted) { + sorted = 1; + + for (i = 0; i < n-1; i++) { + if (list[i].devno > list[i+1].devno) { + tmp = list[i]; + list[i] = list[i+1]; + list[i+1] = tmp; + sorted = 0; + } + } + } +} + + +/* + * General routines + * + */ + +static int inline extract_channel_id(char *name) +{ + if (name[0] == 'c') + return (name[3]-'0'); + else + return (name[5]-'0'); +} + + +static int inline extract_channel_media(char *name) +{ + if (name[0] == 'c') + return CTC; + else + return ESCON; +} + + +static void ctc_tab_init(void) +{ + int m; + int i; + static int t; + + if (t == 0){ + for (m = 0; m < CHANNEL_MEDIA; m++) { + for (i = 0; i < MAX_ADAPTERS; i++) { + ctc_adapter[m][i].devno[WRITE] = -ENODEV; + ctc_adapter[m][i].devno[READ] = -ENODEV; + } + } + t = 1; + } +} + + +static int ctc_buffer_alloc(struct channel *ctc) { + + struct buffer *p; + struct buffer *q; + + p = kmalloc(sizeof(p), GFP_KERNEL); + if (p == NULL) + return -ENOMEM; + else { + p->next = NULL; + p->packets = 0; + p->block = (struct block *) __get_free_pages(GFP_KERNEL+GFP_DMA, 4); + if (p->block == NULL) { + kfree(p); + return -ENOMEM; + } + } + + if (ctc->free_anchor == NULL) + ctc->free_anchor = p; + else { + q = ctc->free_anchor; + while (q->next != NULL) + q = q->next; + q->next = p; + } + ctc->buffer_count++; + return 0; +} + + +static int ctc_buffer_free(struct channel *ctc) { + + struct buffer *p; + + + if (ctc->free_anchor == NULL) + return -ENOMEM; + + p = ctc->free_anchor; + ctc->free_anchor = p->next; + free_pages((__u32)p->block, 4); + kfree(p); + + return 0; +} + + +static int inline ctc_buffer_swap(struct buffer **from, struct buffer **to) { + + struct buffer *p = NULL; + struct buffer *q = NULL; + + if (*from == NULL) + return -ENOMEM; + + p = *from; + *from = p->next; + p->next = NULL; + + if (*to == NULL) + *to = p; + else { + q = *to; + while (q->next != NULL) + q = q->next; + q->next = p; + + } + return 0; +} + + +/* + * ctc_setup function + * this function is called for each ctc= keyword passed into the kernel + * + * valid parameter are: ctc=n,0xnnnn,0xnnnn,ctcx + * where n is the channel protocol always 0 + * 0xnnnn is the cu number read + * 0xnnnn is the cu number write + * ctcx can be ctc0 to ctc7 or escon0 to escon7 + */ +void ctc_setup(char *dev_name, int *ints) +{ + struct adapterlist tmp; + + ctc_tab_init(); + + ctc_no_auto = 1; + + tmp.devno[WRITE] = -ENODEV; + tmp.devno[READ] = -ENODEV; + + switch (ints[0]) { + + case 3: /* write channel passed */ + tmp.devno[WRITE] = ints[3]; + + case 2: /* read channel passed */ + tmp.devno[READ] = ints[2]; + if (tmp.devno[WRITE] == -ENODEV) + tmp.devno[WRITE] = tmp.devno[READ]++; + + case 1: /* protocol type passed */ + tmp.protocol = ints[1]; + if (tmp.protocol == 0) { + break; + } else { + printk(KERN_WARNING "%s: wrong Channel protocol type passed\n", dev_name); + return; + } + + default: + printk(KERN_WARNING "%s: wrong number of parameter passed\n", dev_name); + return; + } + ctc_adapter[extract_channel_media(dev_name)][extract_channel_id(dev_name)] = tmp; +#ifdef DEBUG + printk(DEBUG "%s: protocol=%x read=%04x write=%04x\n", + dev_name, tmp.protocol, tmp.devno[READ], tmp.devno[WRITE]); +#endif + return; + +} + + +/* + * ctc_probe + * this function is called for each channel network device, + * which is defined in the /init/main.c + */ +int ctc_probe(struct device *dev) +{ + int rc; + int c; + int i; + int m; + + struct ctc_priv *privptr; + + /* Only the first time the ctc_probe gets control */ + if (channel_tab_initialized == 0) { + channel_init(); + + + } + + ctc_tab_init(); + + m = extract_channel_media(dev->name); + i = extract_channel_id(dev->name); + + if (channel_left(m) <=1) + return -ENODEV; + + dev->priv = kmalloc(sizeof(struct ctc_priv), GFP_KERNEL); + if (dev->priv == NULL) + return -ENOMEM; + memset(dev->priv, 0, sizeof(struct ctc_priv)); + privptr = (struct ctc_priv *) (dev->priv); + + + for (c = 0; c < 2; c++) { + privptr->channel[c].devstat = kmalloc(sizeof(devstat_t), GFP_KERNEL); + if (privptr->channel[c].devstat == NULL){ + if (i == WRITE) + kfree(privptr->channel[READ].devstat); + return -ENOMEM; + } + memset(privptr->channel[c].devstat, 0, sizeof(devstat_t)); + + if (ctc_no_auto == 0) + ctc_adapter[m][i].devno[c] = channel_get_next(m); + else + ctc_adapter[m][i].devno[c] = channel_get(m, ctc_adapter[m][i].devno[c]); + + if ( ctc_adapter[m][i].devno[c] != -ENODEV){ + rc = request_irq(get_irq_by_devno(ctc_adapter[m][i].devno[c]), + (void *)ctc_irq_handler, SA_INTERRUPT, dev->name, + privptr->channel[c].devstat); + if (rc) { + printk(KERN_WARNING "%s: requested device busy %02x\n", dev->name, rc); + return -EBUSY; + } + } else { + if (i == WRITE) { + free_irq(get_irq_by_devno(ctc_adapter[m][i].devno[c]), privptr->channel[i].devstat); + channel_free(m, ctc_adapter[m][i].devno[READ]); + kfree(privptr->channel[READ].devstat); + } + kfree(privptr->channel[i].devstat); + return -ENODEV; + } + } + + privptr->channel[READ].devno = ctc_adapter[m][i].devno[READ]; + privptr->channel[READ].irq = get_irq_by_devno(ctc_adapter[m][i].devno[READ]); + privptr->channel[WRITE].devno = ctc_adapter[m][i].devno[WRITE]; + privptr->channel[WRITE].irq = get_irq_by_devno(ctc_adapter[m][i].devno[WRITE]); + privptr->protocol = ctc_adapter[m][i].protocol; + channel[m].left = channel[m].left - 2; + + printk(KERN_INFO "%s: read dev: %04x irq: %04x - write dev: %04x irq: %04x \n", + dev->name, privptr->channel[READ].devno, privptr->channel[READ].irq, + privptr->channel[WRITE].devno, privptr->channel[WRITE].irq); + + dev->mtu = CTC_DEFAULT_MTU_SIZE; + dev->hard_start_xmit = ctc_tx; + dev->open = ctc_open; + dev->stop = ctc_release; + dev->get_stats = ctc_stats; + dev->change_mtu = ctc_change_mtu; + dev->hard_header_len = 0; + dev->addr_len = 0; + dev->type = ARPHRD_SLIP; + dev->tx_queue_len = 100; + dev_init_buffers(dev); + dev->flags = IFF_POINTOPOINT | IFF_NOARP; + + return 0; +} + + +/* + * Interrupt processing + * + */ + +static void inline ccw_check_return_code (struct device *dev, int return_code) +{ + if (return_code != 0) { + switch (return_code) { + case -EBUSY: + printk(KERN_INFO "%s: Busy !\n", dev->name); + break; + case -ENODEV: + printk(KERN_EMERG "%s: Invalid device called for IO\n", dev->name); + break; + case -EIO: + printk(KERN_EMERG "%s: Status pending... \n", dev->name); + break; + default: + printk(KERN_EMERG "%s: Unknown error in Do_IO %04x\n", + dev->name, return_code); + } + } +} + + +static void inline ccw_check_unit_check (struct device *dev, char sense) +{ +#ifdef DEBUG + printk(KERN_INFO "%s: Unit Check with sense code: %02x\n", + dev->name, sense); +#endif + + if (sense & 0x40) { +#ifdef DEBUG + if (sense & 0x01) + printk(KERN_DEBUG "%s: Interface disconnect or Selective reset occurred (remote side)\n", dev->name); + else + printk(KERN_DEBUG "%s: System reset occured (remote side)\n", dev->name); +#endif + } else if (sense & 0x20) { + if (sense & 0x04) + printk(KERN_WARNING "%s: Data-streaming timeout)\n", dev->name); + else + printk(KERN_WARNING "%s: Data-transfer parity error\n", dev->name); + } else if (sense & 0x10) { + if (sense & 0x20) + printk(KERN_WARNING "%s: Hardware malfunction (remote side)\n", dev->name); + else + printk(KERN_WARNING "%s: Read-data parity error (remote side)\n", dev->name); + } + +} + + +static void ctc_irq_handler (int irq, void *initparm, struct pt_regs *regs) +{ + int rc = 0; + __u32 parm; + __u8 flags = 0x00; + struct channel *ctc = NULL; + struct ctc_priv *privptr = NULL; + struct device *dev = NULL; + + ccw1_t ccw_set_x_mode[2] = {{CCW_CMD_SET_EXTENDED, CCW_FLAG_SLI | CCW_FLAG_CC, 0, NULL}, + {CCW_CMD_NOOP, CCW_FLAG_SLI, 0, NULL}}; + + devstat_t *devstat = ((devstat_t *)initparm); + + /* Bypass all 'unsolited interrupts' */ + if (devstat->intparm == 0) { +#ifdef DEBUG + printk(KERN_DEBUG "ctc: unsolited interrupt for device: %04x received c-%02x d-%02x f-%02x\n", + devstat->devno, devstat->cstat, devstat->dstat, devstat->flag); +#endif + /* FIXME - find the related intparm!!! No IO outstanding!!!! */ + return; + } + + ctc = (struct channel *) (devstat->intparm); + dev = (struct device *) ctc->dev; + privptr = dev->priv; + +#ifdef DEBUG + printk(KERN_DEBUG "%s: interrupt for device: %04x received c-%02x d-%02x f-%02x state-%02x\n", + dev->name, ctc->devno, devstat->cstat, devstat->dstat, devstat->flag, ctc->state); +#endif + + /* Check for good subchannel return code, otherwise error message */ + if (devstat->cstat) { + printk(KERN_WARNING "%s: subchannel check for device: %04x - %02x\n", + dev->name, ctc->devno, devstat->cstat); + return; + } + + + /* Check the reason-code of a unit check */ + if (devstat->dstat & DEV_STAT_UNIT_CHECK) + ccw_check_unit_check(dev, devstat->ii.sense.data[0]); + + + /* State machine to bring the connection up / down and to restart */ + + ctc->last_dstat = devstat->dstat; + + switch (ctc->state) { + + case CTC_STOP: /* HALT_IO issued by ctc_release (halt sequence) */ + if (!devstat->flag & DEVSTAT_FINAL_STATUS) + return; + wake_up(&ctc->wait); /* wake up ctc_release */ + return; + + + case CTC_START_HALT_IO: /* HALT_IO issued by ctc_open (start sequence) */ + if (!devstat->flag & DEVSTAT_FINAL_STATUS) + return; + + ctc->state = CTC_START_SET_X_MODE; + parm = (__u32) ctc; + rc = do_IO (ctc->irq, &ccw_set_x_mode[0], parm, 0xff, flags); + if (rc != 0) + ccw_check_return_code(dev, rc); + return; + + + case CTC_START_SET_X_MODE: + if (devstat->dstat & DEV_STAT_UNIT_CHECK) { + if ((devstat->ii.sense.data[0] & 0x41) != 0x41 || + (devstat->ii.sense.data[0] & 0x40) != 0x40) { + wake_up(&ctc->wait); /* wake up ctc_open (READ or WRITE) */ + return; + } + } + if (!devstat->flag & DEVSTAT_FINAL_STATUS) + return; + ctc->state = CTC_START_SELECT; + + + case CTC_START_SELECT: + if (!ctc->flag & CTC_WRITE) { + ctc->state = CTC_START_READ_TEST; + ctc->ccw[1].cda = (char *)virt_to_phys(ctc->free_anchor->block); + parm = (__u32) ctc; + rc = do_IO (ctc->irq, &ctc->ccw[0], parm, 0xff, flags ); + if (rc != 0) + ccw_check_return_code(dev, rc); + wake_up(&ctc->wait); /* wake up ctc_open (READ) */ + + } else { + ctc->state = CTC_START_WRITE_TEST; + /* ADD HERE THE RIGHT PACKET TO ISSUE A ROUND TRIP - PART 1 */ + ctc->ccw[1].count = 0; + ctc->ccw[1].cda = (char *)virt_to_phys(ctc->free_anchor->block); + parm = (__u32) ctc; + rc = do_IO (ctc->irq, &ctc->ccw[0], parm, 0xff, flags); + if (rc != 0) + ccw_check_return_code(dev, rc); + } + return; + + + case CTC_START_READ_TEST: + if (devstat->dstat & DEV_STAT_UNIT_CHECK) { + if ((devstat->ii.sense.data[0] & 0x41) == 0x41 || + (devstat->ii.sense.data[0] & 0x40) == 0x40 || + devstat->ii.sense.data[0] == 0 ) { + init_timer(&ctc->timer); + ctc->timer.function = (void *)ctc_read_retry; + ctc->timer.data = (__u32)ctc; + ctc->timer.expires = jiffies + 10*HZ; + add_timer(&ctc->timer); +#ifdef DEBUG + printk(KERN_DEBUG "%s: read connection restarted\n",dev->name); +#endif + } + return; + } + + if ((devstat->dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) != 0x00) { + if ((devstat->dstat & DEV_STAT_ATTENTION) && + (devstat->dstat & DEV_STAT_BUSY)) { + printk(KERN_WARNING "%s: read channel is connected with the remote side read channel\n", dev->name); + } + wake_up(&privptr->channel[WRITE].wait); /* wake up ctc_open (WRITE) */ + return; + } + + ctc->state = CTC_START_READ; + set_bit(0, (void *)&ctc->IO_active); + + /* ADD HERE THE RIGHT PACKET TO ISSUE A ROUND TRIP - PART 2 */ + /* wake_up(&privptr->channel[WRITE].wait);*/ /* wake up ctc_open (WRITE) */ + + + case CTC_START_READ: + if (devstat->dstat & DEV_STAT_UNIT_CHECK) { + if ((devstat->ii.sense.data[0] & 0x41) == 0x41 || + (devstat->ii.sense.data[0] & 0x40) == 0x40 || + devstat->ii.sense.data[0] == 0 ) { + privptr->stats.rx_errors++; + set_bit(TB_RETRY, (void *)&dev->tbusy); + init_timer(&ctc->timer); + ctc->timer.function = (void *)ctc_read_retry; + ctc->timer.data = (__u32)ctc; + ctc->timer.expires = jiffies + 30*HZ; + add_timer(&ctc->timer); + printk(KERN_INFO "%s: connection restarted!! problem on remote side\n",dev->name); + } + return; + } + + if(!devstat->flag & DEVSTAT_FINAL_STATUS) + return; + + clear_bit(TB_RETRY, (void *)&dev->tbusy); + + ctc_buffer_swap(&ctc->free_anchor, &ctc->proc_anchor); + + if (ctc->free_anchor != NULL) { + ctc->ccw[1].cda = (char *)virt_to_phys(ctc->free_anchor->block); + parm = (__u32) ctc; + rc = do_IO (ctc->irq, &ctc->ccw[0], parm, 0xff, flags ); + if (rc != 0) + ccw_check_return_code(dev, rc); + } else { + clear_bit(0, (void *)&ctc->IO_active); +#ifdef DEBUG + printk(KERN_DEBUG "%s: No HOT READ started in IRQ\n",dev->name); +#endif + } + + if (test_and_set_bit(CTC_BH_ACTIVE, (void *)&ctc->flag_a) == 0) { + queue_task(&ctc->tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + return; + + + case CTC_START_WRITE_TEST: + if (devstat->dstat & DEV_STAT_UNIT_CHECK) { + if ((devstat->ii.sense.data[0] & 0x41) == 0x41 || + (devstat->ii.sense.data[0] & 0x40) == 0x40 || + devstat->ii.sense.data[0] == 0 ) { + init_timer(&ctc->timer); + ctc->timer.function = (void *)ctc_write_retry; + ctc->timer.data = (__u32)ctc; + ctc->timer.expires = jiffies + 10*HZ; + add_timer(&ctc->timer); +#ifdef DEBUG + printk(KERN_DEBUG "%s: write connection restarted\n",dev->name); +#endif + } + return; + } + + ctc->state = CTC_START_WRITE; + wake_up(&ctc->wait); /* wake up ctc_open (WRITE) */ + return; + + + case CTC_START_WRITE: + if (devstat->dstat & DEV_STAT_UNIT_CHECK) { + privptr->stats.tx_errors += ctc->proc_anchor->packets; +#ifdef DEBUG + printk(KERN_DEBUG "%s: Unit Check on write channel\n",dev->name); +#endif + } else { + if (!devstat->flag & DEVSTAT_FINAL_STATUS) + return; + privptr->stats.tx_packets += ctc->proc_anchor->packets; + } + + ctc->proc_anchor->block->length = 0; + ctc_buffer_swap(&ctc->proc_anchor, &ctc->free_anchor); + clear_bit(TB_NOBUFFER, (void *)&dev->tbusy); + + if (ctc->proc_anchor != NULL) { +#ifdef DEBUG + printk(KERN_DEBUG "%s: IRQ early swap buffer\n",dev->name); +#endif + ctc->ccw[1].count = ctc->proc_anchor->block->length; + ctc->ccw[1].cda = (char *)virt_to_phys(ctc->proc_anchor->block); + parm = (__u32) ctc; + rc = do_IO (ctc->irq, &ctc->ccw[0], parm, 0xff, flags ); + if (rc != 0) + ccw_check_return_code(dev, rc); + dev->trans_start = jiffies; + return; + + } + + if (ctc->free_anchor->block->length != 0) { + if (test_and_set_bit(TB_TX, (void *)&dev->tbusy) == 0) { /* set transmission to busy */ + ctc_buffer_swap(&ctc->free_anchor, &ctc->proc_anchor); + clear_bit(TB_TX, (void *)&dev->tbusy); +#ifdef DEBUG + printk(KERN_DEBUG "%s: last buffer move in IRQ\n",dev->name); +#endif + ctc->ccw[1].count = ctc->proc_anchor->block->length; + ctc->ccw[1].cda = (char *)virt_to_phys(ctc->proc_anchor->block); + parm = (__u32) ctc; + rc = do_IO (ctc->irq, &ctc->ccw[0], parm, 0xff, flags ); + if (rc != 0) + ccw_check_return_code(dev, rc); + dev->trans_start = jiffies; + return; + } + } + + clear_bit(0, (void *)&ctc->IO_active); /* set by ctc_tx or ctc_bh */ + return; + + + default: + printk(KERN_WARNING "%s: wrong selection code - irq\n",dev->name); + return; + } +} + + +static void ctc_irq_bh (struct channel *ctc) +{ + int rc = 0; + __u16 data_len; + __u32 parm; + + __u8 flags = 0x00; + __u32 saveflags; + struct device *dev; + struct ctc_priv *privptr; + struct packet *lp; + struct sk_buff *skb; + + dev = (struct device *) ctc->dev; + privptr = (struct ctc_priv *) dev->priv; + +#ifdef DEBUG + printk(KERN_DEBUG "%s: bh routine - state-%02x\n" ,dev->name, ctc->state); +#endif + + while (ctc->proc_anchor != NULL) { + + lp = &ctc->proc_anchor->block->data; + + while ((__u8 *) lp < (__u8 *) &ctc->proc_anchor->block->length + ctc->proc_anchor->block->length) { + data_len = lp->length - PACKET_HEADER_LENGTH; + skb = dev_alloc_skb(data_len); + if (skb) { + memcpy(skb_put(skb, data_len),&lp->data, data_len); + skb->mac.raw = skb->data; + skb->dev = dev; + skb->protocol = htons(ETH_P_IP); + skb->ip_summed = CHECKSUM_UNNECESSARY; /* no UC happend!!! */ + netif_rx(skb); + privptr->stats.rx_packets++; + } else { + privptr->stats.rx_dropped++; + printk(KERN_WARNING "%s: is low on memory\n",dev->name); + } + (__u8 *)lp += lp->length; + } + + s390irq_spin_lock_irqsave(ctc->irq, saveflags); + ctc_buffer_swap(&ctc->proc_anchor, &ctc->free_anchor); + + if (test_and_set_bit(0, (void *)&ctc->IO_active) == 0) { +#ifdef DEBUG + printk(KERN_DEBUG "%s: HOT READ started in bh routine\n" ,dev->name); +#endif + ctc->ccw[1].cda = (char *)virt_to_phys(ctc->free_anchor->block); + parm = (__u32) ctc; + rc = do_IO (ctc->irq, &ctc->ccw[0], parm, 0xff, flags ); + if (rc != 0) + ccw_check_return_code(dev, rc); + } + s390irq_spin_unlock_irqrestore(ctc->irq, saveflags); + } + clear_bit(CTC_BH_ACTIVE, (void *)&ctc->flag_a); + return; +} + + +static void ctc_read_retry (struct channel *ctc) +{ + int rc = 0; + __u32 parm; + __u8 flags = 0x00; + __u32 saveflags; + struct device *dev; + + dev = (struct device *) ctc->dev; + +#ifdef DEBUG + printk(KERN_DEBUG "%s: read retry - state-%02x\n" ,dev->name, ctc->state); +#endif + s390irq_spin_lock_irqsave(ctc->irq, saveflags); + ctc->ccw[1].cda = (char *)virt_to_phys(ctc->free_anchor->block); + parm = (__u32) ctc; + rc = do_IO (ctc->irq, &ctc->ccw[0], parm, 0xff, flags ); + s390irq_spin_unlock_irqrestore(ctc->irq, saveflags); + if (rc != 0) + ccw_check_return_code(dev, rc); + return; +} + + +static void ctc_write_retry (struct channel *ctc) +{ + int rc = 0; + __u32 parm; + __u8 flags = 0x00; + __u32 saveflags; + struct device *dev; + + dev = (struct device *) ctc->dev; + +#ifdef DEBUG + printk(KERN_DEBUG "%s: write retry - state-%02x\n" ,dev->name, ctc->state); +#endif + s390irq_spin_lock_irqsave(ctc->irq, saveflags); + ctc->ccw[1].count = 0; + ctc->ccw[1].cda = (char *)virt_to_phys(ctc->proc_anchor->block); + parm = (__u32) ctc; + rc = do_IO (ctc->irq, &ctc->ccw[0], parm, 0xff, flags ); + s390irq_spin_unlock_irqrestore(ctc->irq, saveflags); + if (rc != 0) + ccw_check_return_code(dev, rc); + return; +} + + + +/* + * ctc_open + * + */ +static int ctc_open(struct device *dev) +{ + int rc; + int i; + int j; + __u8 flags = 0x00; + __u32 saveflags; + __u32 parm; + struct ctc_priv *privptr; + struct wait_queue wait = { current, NULL }; + struct timer_list timer; + + + dev->tbusy = 1; + dev->start = 0; + + privptr = (struct ctc_priv *) (dev->priv); + + privptr->channel[READ].flag = 0x00; + privptr->channel[WRITE].flag = CTC_WRITE; + + for (i = 0; i < 2; i++) { + for (j = 0; j < CTC_BLOCKS; j++) { + rc = ctc_buffer_alloc(&privptr->channel[i]); + if (rc != 0) + return -ENOMEM; + } + privptr->channel[i].tq.next = NULL; + privptr->channel[i].tq.sync = 0; + privptr->channel[i].tq.routine = (void *)(void *)ctc_irq_bh; + privptr->channel[i].tq.data = &privptr->channel[i]; + + privptr->channel[i].dev = dev; + + privptr->channel[i].flag_a = 0; + + privptr->channel[i].ccw[0].cmd_code = CCW_CMD_PREPARE; + privptr->channel[i].ccw[0].flags = CCW_FLAG_SLI | CCW_FLAG_CC; + privptr->channel[i].ccw[0].count = 0; + privptr->channel[i].ccw[0].cda = NULL; + if (i == READ) { + privptr->channel[i].ccw[1].cmd_code = CCW_CMD_READ; + privptr->channel[i].ccw[1].flags = CCW_FLAG_SLI; + privptr->channel[i].ccw[1].count = 0xffff; /* MAX size */ + privptr->channel[i].ccw[1].cda = NULL; + } else { + privptr->channel[i].ccw[1].cmd_code = CCW_CMD_WRITE; + privptr->channel[i].ccw[1].flags = CCW_FLAG_SLI | CCW_FLAG_CC; + privptr->channel[i].ccw[1].count = 0; + privptr->channel[i].ccw[1].cda = NULL; + } + privptr->channel[i].ccw[2].cmd_code = CCW_CMD_NOOP; /* jointed CE+DE */ + privptr->channel[i].ccw[2].flags = CCW_FLAG_SLI; + privptr->channel[i].ccw[2].count = 0; + privptr->channel[i].ccw[2].cda = NULL; + + privptr->channel[i].flag &= ~CTC_TIMER; + init_timer(&timer); + timer.function = (void *)ctc_timer; + timer.data = (__u32)&privptr->channel[i]; + timer.expires = jiffies + 150*HZ; /* time to connect with the remote side */ + add_timer(&timer); + + s390irq_spin_lock_irqsave(privptr->channel[i].irq, saveflags); + parm = (unsigned long) &privptr->channel[i]; + privptr->channel[i].state = CTC_START_HALT_IO; + rc = halt_IO(privptr->channel[i].irq, parm, flags); + add_wait_queue(&privptr->channel[i].wait, &wait); + current->state = TASK_INTERRUPTIBLE; + s390irq_spin_unlock_irqrestore(privptr->channel[i].irq, saveflags); + schedule(); + remove_wait_queue(&privptr->channel[i].wait, &wait); + if(rc != 0) + ccw_check_return_code(dev, rc); + if((privptr->channel[i].flag & CTC_TIMER) == 0x00) + del_timer(&timer); + } + + if ((((privptr->channel[READ].last_dstat | privptr->channel[WRITE].last_dstat) & + ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) != 0x00) || + (((privptr->channel[READ].flag | privptr->channel[WRITE].flag) & CTC_TIMER) != 0x00)) { +#ifdef DEBUG + printk(KERN_DEBUG "%s: channel problems during open - read: %02x - write: %02x\n", + dev->name, privptr->channel[READ].last_dstat, privptr->channel[WRITE].last_dstat); +#endif + printk(KERN_INFO "%s: remote side is currently not ready\n", dev->name); + + for (i = 0; i < 2; i++) { + s390irq_spin_lock_irqsave(privptr->channel[i].irq, saveflags); + parm = (unsigned long) &privptr->channel[i]; + privptr->channel[i].state = CTC_STOP; + rc = halt_IO(privptr->channel[i].irq, parm, flags); + s390irq_spin_unlock_irqrestore(privptr->channel[i].irq, saveflags); + if (rc != 0) + ccw_check_return_code(dev, rc); + for (j = 0; j < CTC_BLOCKS; j++) + ctc_buffer_free(&privptr->channel[i]); + } + return -EIO; + } + + printk(KERN_INFO "%s: connected with remote side\n",dev->name); + dev->start = 1; + dev->tbusy = 0; + return 0; +} + + +static void ctc_timer (struct channel *ctc) +{ +#ifdef DEBUG + struct device *dev; + + dev = (struct device *) ctc->dev; + printk(KERN_DEBUG "%s: timer return\n" ,dev->name); +#endif + ctc->flag |= CTC_TIMER; + wake_up(&ctc->wait); + return; +} + +/* + * ctc_release + * + */ +static int ctc_release(struct device *dev) +{ + int rc; + int i; + int j; + __u8 flags = 0x00; + __u32 saveflags; + __u32 parm; + struct ctc_priv *privptr; + struct wait_queue wait = { current, NULL }; + + privptr = (struct ctc_priv *) dev->priv; + + dev->start = 0; + set_bit(TB_STOP, (void *)&dev->tbusy); + + for (i = 0; i < 2; i++) { + s390irq_spin_lock_irqsave(privptr->channel[i].irq, saveflags); + privptr->channel[i].state = CTC_STOP; + parm = (__u32) &privptr->channel[i]; + rc = halt_IO (privptr->channel[i].irq, parm, flags ); + add_wait_queue(&privptr->channel[i].wait, &wait); + current->state = TASK_INTERRUPTIBLE; + s390irq_spin_unlock_irqrestore(privptr->channel[i].irq, saveflags); + schedule(); + remove_wait_queue(&privptr->channel[i].wait, &wait); + if (rc != 0) { + ccw_check_return_code(dev, rc); + } + + for (j = 0; j < CTC_BLOCKS; j++) { + ctc_buffer_swap(&privptr->channel[i].proc_anchor, &privptr->channel[i].free_anchor); + ctc_buffer_free(&privptr->channel[i]); + } + } + + if (((privptr->channel[READ].last_dstat | privptr->channel[WRITE].last_dstat) & + ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) != 0x00) { + printk(KERN_WARNING "%s: channel problems during close - read: %02x - write: %02x\n", + dev->name, privptr->channel[READ].last_dstat, privptr->channel[WRITE].last_dstat); + return -EIO; + } + + return 0; +} + + +/* + * ctc_tx + * + * + */ +static int ctc_tx(struct sk_buff *skb, struct device *dev) +{ + int rc; + __u32 parm; + __u8 flags = 0x00; + __u32 saveflags; + struct ctc_priv *privptr; + struct packet *lp; + + privptr = (struct ctc_priv *) (dev->priv); + + if (skb == NULL) { + printk(KERN_WARNING "%s: NULL pointer as sk_buffer passed\n", dev->name); + privptr->stats.tx_dropped++; + return -EIO; + } + + if (dev->tbusy != 0) { + return -EBUSY; + } + + if (test_and_set_bit(TB_TX, (void *)&dev->tbusy) != 0) { /* set transmission to busy */ + return -EBUSY; + } + + if (65535 - privptr->channel[WRITE].free_anchor->block->length - PACKET_HEADER_LENGTH <= skb->len + PACKET_HEADER_LENGTH + 2) { +#ifdef DEBUG + printk(KERN_DEBUG "%s: early swap\n", dev->name); +#endif + s390irq_spin_lock_irqsave(privptr->channel[WRITE].irq, saveflags); + ctc_buffer_swap(&privptr->channel[WRITE].free_anchor, &privptr->channel[WRITE].proc_anchor); + s390irq_spin_unlock_irqrestore(privptr->channel[WRITE].irq, saveflags); + if (privptr->channel[WRITE].free_anchor == NULL){ + set_bit(TB_NOBUFFER, (void *)&dev->tbusy); + clear_bit(TB_TX, (void *)&dev->tbusy); + return -EBUSY; + } + } + + if (privptr->channel[WRITE].free_anchor->block->length == 0) { + privptr->channel[WRITE].free_anchor->block->length = BLOCK_HEADER_LENGTH; + privptr->channel[WRITE].free_anchor->packets = 0; + } + + + (__u8 *)lp = (__u8 *) &privptr->channel[WRITE].free_anchor->block->length + privptr->channel[WRITE].free_anchor->block->length; + privptr->channel[WRITE].free_anchor->block->length += skb->len + PACKET_HEADER_LENGTH; + lp->length = skb->len + PACKET_HEADER_LENGTH; + lp->type = 0x0800; + lp->unused = 0; + memcpy(&lp->data, skb->data, skb->len); + (__u8 *) lp += lp->length; + lp->length = 0; + dev_kfree_skb(skb); + privptr->channel[WRITE].free_anchor->packets++; + + if (test_and_set_bit(0, (void *)&privptr->channel[WRITE].IO_active) == 0) { + s390irq_spin_lock_irqsave(privptr->channel[WRITE].irq, saveflags); + ctc_buffer_swap(&privptr->channel[WRITE].free_anchor,&privptr->channel[WRITE].proc_anchor); + privptr->channel[WRITE].ccw[1].count = privptr->channel[WRITE].proc_anchor->block->length; + privptr->channel[WRITE].ccw[1].cda = (char *)virt_to_phys(privptr->channel[WRITE].proc_anchor->block); + parm = (__u32) &privptr->channel[WRITE]; + rc = do_IO (privptr->channel[WRITE].irq, &privptr->channel[WRITE].ccw[0], parm, 0xff, flags ); + if (rc != 0) + ccw_check_return_code(dev, rc); + dev->trans_start = jiffies; + s390irq_spin_unlock_irqrestore(privptr->channel[WRITE].irq, saveflags); + } + + if (privptr->channel[WRITE].free_anchor == NULL) + set_bit(TB_NOBUFFER, (void *)&dev->tbusy); + + clear_bit(TB_TX, (void *)&dev->tbusy); + return 0; +} + + +/* + * ctc_change_mtu + * + * S/390 can handle MTU sizes from 576 to 32760 for VM, VSE + * 576 to 65527 for OS/390 + * + */ +static int ctc_change_mtu(struct device *dev, int new_mtu) +{ + if ((new_mtu < 576) || (new_mtu > 65528)) + return -EINVAL; + dev->mtu = new_mtu; + return 0; +} + + +/* + * ctc_stats + * + */ +struct net_device_stats *ctc_stats(struct device *dev) +{ + struct ctc_priv *privptr; + + privptr = dev->priv; + return &privptr->stats; +} + + +/* Module code goes here */ + +/* + free_irq(privptr->channel[i].irq, privptr->channel[i].devstat); + kfree(privptr->channel[i].devstat); + +*/ +/* --- This is the END my friend --- */ diff -u --recursive --new-file v2.2.13/linux/drivers/s390/net/iucv.c linux/drivers/s390/net/iucv.c --- v2.2.13/linux/drivers/s390/net/iucv.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/net/iucv.c Tue Jan 4 10:12:18 2000 @@ -0,0 +1,1110 @@ +/* + * drivers/s390/net/iucv.c + * Network driver for VM using iucv + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Stefan Hegewald + * Hartmut Penner + */ + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif + +#include +#include +#include /* printk() */ +#include /* kmalloc() */ +#include /* error codes */ +#include /* size_t */ +#include /* mark_bh */ +#include /* struct device, and other headers */ +#include /* struct device, and other headers */ +#include +#include +#include /* struct iphdr */ +#include /* struct tcphdr */ +#include +#include +#include +#include +#include +#include + +#include "iucv.h" + +#define DEBUG123 +#define MAX_DEVICES 10 + +extern char _ascebc[]; + +/* + * global structures + */ +static char iucv_userid[MAX_DEVICES][8]; +static char iucv_ascii_userid[MAX_DEVICES][8]; +static int iucv_pathid[MAX_DEVICES] = {0}; +static unsigned char iucv_ext_int_buffer[40] __attribute__((aligned (8))) ={0}; +static unsigned char glob_command_buffer[40] __attribute__((aligned (8))); +struct device iucv_devs[]; + + +/* This structure is private to each device. It is used to pass */ +/* packets in and out, so there is place for a packet */ +struct iucv_priv { + struct net_device_stats stats; + int packetlen; + int status; + u8 *packetdata; + int pathid; /* used device */ + unsigned char command_buffer[40] __attribute__((aligned (8))); + unsigned char ext_int_buffer[40] __attribute__((aligned (8))); + u8* receive_buffer; + int receive_buffer_len; + u8* send_buffer; + int send_buffer_len; + char * new_send_buf; /* send buffer ptr */ + unsigned char recv_buf[2048]; /* size is just a guess */ + unsigned char userid[8]; +}; + +struct iucv_header { + short len; +}; + +/* + * Following the iucv primitives + */ + + +extern inline void b2f0(int code,void* parm) +{ + asm volatile ("LR 1,%1\n\tLR 0,%0\n\t.long 0xb2f01000" :: + "d" (code) ,"a" (parm) :"0", "1"); +} + +int iucv_enable(void *parms) +{ + MASK_T *parm = parms; + memset(parms,0,sizeof(parm)); + parm->ipmask = 0xF8; + b2f0(SETMASK,parm); + memset(parms,0,sizeof(parm)); + parm->ipmask = 0xF8; + b2f0(SETCMASK,parm); + return parm->iprcode; +} + + +int iucv_declare_buffer(void *parms, DCLBFR_T *buffer) +{ + DCLBFR_T *parm = parms; + memset(parms,0,sizeof(parm)); + parm->ipflags1= 0x00; + parm->ipbfadr1 = virt_to_phys(buffer); + b2f0(DECLARE_BUFFER, parm); + return parm->iprcode; +} + + +int iucv_retrieve_buffer(void *parms) +{ + DCLBFR_T *parm = parms; + memset(parms,0x0,sizeof(parm)); + parm->iprcode = 0x0; + b2f0(RETRIEVE_BUFFER, parm); + return parm->iprcode; +} + + +int iucv_connect(void *parms, + const char *userid, + const char *host, + const char *ipusr, + unsigned short * used_pathid) +{ + CONNECT_T *parm = parms; /* ipflags was 0x60*/ + memset(parms,0x0,sizeof(parm)); + parm->ipflags1 = 0x80; + parm->ipmsglim = 0x0a; + memcpy(parm->ipvmid,userid,8); + if (ipusr) + memcpy(parm->ipuser,ipusr,16); + memcpy(parm->iptarget,host,8); + b2f0(CONNECT, parm); + *used_pathid = parm->ippathid; + return parm->iprcode; +} + + + +int iucv_accept(void *parms,int pathid) +{ +#ifdef DEBUG + int i=0; +#endif + ACCEPT_T *parm = parms; + memset(parms,0,sizeof(parm)); + parm->ippathid = pathid; + parm->ipflags1 = 0x80; + parm->ipmsglim = 0x0a; +#ifdef DEBUG + printk("iucv: iucv_accept input.\n"); + for (i=0;i<40; i++) + { + printk("%02x ",((char *)parms)[i]); + } + printk("\n"); +#endif + b2f0(ACCEPT, parm); + return parm->iprcode; +} + + + +int iucv_receive(void *parms,void *bufferarray,int len) +{ +#ifdef DEBUG + int i=0; +#endif + RECEIVE_T *parm = parms; + memset(parms,0x0,sizeof(parm)); + /*parm->ipflags1 = 0x42;*/ + parm->ipflags1 = 0x0; + parm->ipmsgid = 0x0; + parm->iptrgcls = 0x0; + parm->ipbfadr1 = (ULONG) virt_to_phys(bufferarray); + parm->ipbfln1f = len; + parm->ipbfln2f = 0x0; + b2f0(RECEIVE, parm); + if (parm->iprcode == 0) + len = parm->ipbfln1f; +// len = len-parm->ipbfln1f; +#ifdef DEBUG + printk("iucv: iucv_receive command input:\n"); + for (i=0;i<40;i++) /* show iucv buffer before send */ + { + printk("%02x ",((char *)parms)[i]); + } + printk("\n"); + + printk("iucv: iucv_receive data buffer:\n"); + for (i=0;iiprcode; +} + + +int iucv_send(void *parms,int pathid,void *bufferarray,int len, + void *recv_buf, int recv_len) +{ +#ifdef DEBUG + int i=0; +#endif + SEND_T *parm = parms; + memset(parms,0x0,sizeof(parm)); + /* parm->ipflags1 = 0x48; ??*/ + parm->ippathid = pathid; + parm->ipflags1 = 0x14; /* any options ?? */ + parm->ipmsgid = 0x0; + parm->iptrgcls = 0x0; + parm->ipbfadr1 = virt_to_phys(bufferarray); + parm->ipbfln1f = len; + parm->ipsrccls = 0x0; + parm->ipmsgtag = 0x0; + parm->ipbfadr2 = virt_to_phys(recv_buf); + parm->ipbfln2f = recv_len; + + +#ifdef DEBUG + printk("iucv: iucv_send command input:\n"); + for (i=0;i<40;i++) /* show iucv buffer before send */ + { + printk("%02x ",((char *)parms)[i]); + } + printk("\n"); + + printk("iucv: iucv_send data buffer:\n"); + for (i=0;iiprcode; +} + + + +int iucv_sever(void *parms) +{ + SEVER_T *parm = parms; + memset(parms,0x0,sizeof(parm)); + parm->ippathid = 0x0; + parm->ipflags1 = 0x0; + parm->iprcode = 0xF; + memset(parm->ipuser,0,16); + b2f0(SEVER, parm); + return parm->iprcode; +} + + +#ifdef DEBUG +/*--------------------------*/ +/* Dump buffer formatted */ +/*--------------------------*/ +static void dumpit(char* buf, int len) +{ + int i; + for (i=0;ipriv); + if (memcmp(privptr->userid,userid,8)==0) + return &iucv_devs[i]; + } + printk("iucv: get_device_from_uid: no device for userid %s\n",userid); + return 0; +} + + +/*--------------------------*/ +/* Open iucv Device Driver */ +/*--------------------------*/ +int iucv_open(struct device *dev) +{ + int rc; + unsigned short iucv_used_pathid; + struct iucv_priv *privptr; + char iucv_host[8] ={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + char vmident[16] ={0xf0,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0xf0,0x40,0x40,0x40,0x40,0x40,0x40,0x40}; + +#ifdef DEBUG + printk( "iucv: iucv_open, device: %s\n",dev->name); +#endif + + privptr = (struct iucv_priv *)(dev->priv); + if(privptr->pathid != -1) { + dev->start = 1; + dev->tbusy = 0; + return 0; + } + if ((rc = iucv_connect(privptr->command_buffer, + privptr->userid, + iucv_host, + vmident, + &iucv_used_pathid))!=0) { + printk( "iucv: iucv connect failed with rc %X\n",rc); + iucv_retrieve_buffer(privptr->command_buffer); + return -ENODEV; + } + + privptr->pathid = iucv_used_pathid; + iucv_pathid[dev-iucv_devs]=privptr->pathid; + +#ifdef DEBUG + printk( "iucv: iucv_connect ended with rc: %X\n",rc); + printk( "iucv[%d] pathid %X \n",(int)(dev-iucv_devs),privptr->pathid); +#endif + dev->start = 1; + dev->tbusy = 0; + return 0; +} + + + +/*-----------------------------------------------------------------------*/ +/* Receive a packet: retrieve, encapsulate and pass over to upper levels */ +/*-----------------------------------------------------------------------*/ +void iucv_rx(struct device *dev, int len, unsigned char *buf) +{ + + struct sk_buff *skb; + struct iucv_priv *privptr = (struct iucv_priv *)dev->priv; + +#ifdef DEBUG + printk( "iucv: iucv_rx len: %X, device %s\n",len,dev->name); + printk( "iucv rx: received orig:\n"); + dumpit(buf,len); +#endif + + /* strip iucv header now */ + len = len - 2; /* short header */ + buf = buf + 2; /* short header */ + + skb = dev_alloc_skb(len+2); /* why +2 ? alignment ? */ + if (!skb) { + printk( "iucv rx: low on mem, returning...\n"); + return; + } + skb_reserve(skb, 2); /* align IP on 16B boundary*/ + memcpy(skb_put(skb, len), buf, len); +#ifdef DEBUG + printk( "iucv rx: data before netif_rx()\n"); + dumpit(buf,len); +#endif + + /* Write metadata, and then pass to the receive level */ + skb->mac.raw = skb->data; + skb->pkt_type = PACKET_HOST; + skb->dev = dev; + skb->protocol = htons(ETH_P_IP); + skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it*/ + privptr->stats.rx_packets++; + netif_rx(skb); + + return; +} /* end iucv_rx() */ + + + + +/*----------------------------*/ +/* handle interrupts */ +/*----------------------------*/ +void do_iucv_interrupt(void) +{ + int rc; + struct in_device *indev; + struct in_ifaddr *inaddr; + unsigned long len=0; + struct device *dev=0; + struct iucv_priv *privptr; + INTERRUPT_T * extern_int_buffer; + unsigned short iucv_data_len=0; + unsigned short iucv_next=0; + unsigned char * rcvptr; + + /* get own buffer: */ + extern_int_buffer = (INTERRUPT_T*) iucv_ext_int_buffer; + + dev->interrupt = 1; /* lock ! */ + +#ifdef DEBUG + printk( "iucv: do_iucv_interrupt %x received; pathid: %02X\n", + extern_int_buffer->iptype,extern_int_buffer->ippathid); + printk( "iucv: extern_int_buffer:\n"); + dumpit((char *)&extern_int_buffer[0],40); +#endif + + switch (extern_int_buffer->iptype) + { + case 0x01: /* connection pending ext interrrupt */ +#ifdef DEBUG + printk( "iucv: connection pending IRQ.\n"); +#endif + + rc = iucv_accept(glob_command_buffer, + extern_int_buffer->ippathid); + if (rc != 0) { + printk( "iucv: iucv_accept failed with rc: %X\n",rc); + iucv_retrieve_buffer(glob_command_buffer); + break; + } +#ifdef DEBUG + dumpit(&((char *)extern_int_buffer)[8],8); +#endif + dev = get_device_from_userid(&((char*)extern_int_buffer)[8]); + privptr = (struct iucv_priv *)(dev->priv); + privptr->pathid = extern_int_buffer->ippathid; + +#ifdef DEBUG + printk( "iucv: iucv_accept ended with rc: %X\n",rc); + printk( "iucv: device %s found.\n",dev->name); +#endif + break; + + case 0x02: /* connection completed ext interrrupt */ + /* set own global IP address */ + /* & set global routing addr */ +#ifdef DEBUG + printk( "connection completed.\n"); +#endif + + if( extern_int_buffer->ipmsgtag !=0) + { + /* get ptr's to kernel struct with local & broadcast address */ + dev = get_device_from_pathid(extern_int_buffer->ippathid); + privptr = (struct iucv_priv *)(dev->priv); + indev = dev->ip_ptr; + inaddr = (struct in_ifaddr*) indev->ifa_list; + } + break; + + + case 0x03: /* connection severed ext interrrupt */ + /* we do not handle this one at this time */ +#ifdef DEBUG + printk( "connection severed.\n"); +#endif + break; + + + case 0x04: /* connection quiesced ext interrrupt */ + /* we do not handle this one at this time */ +#ifdef DEBUG + printk( "connection quiesced.\n"); +#endif + break; + + + case 0x05: /* connection resumed ext interrrupt */ + /* we do not handle this one at this time */ +#ifdef DEBUG + printk( "connection resumed.\n"); +#endif + break; + + + case 0x06: /* priority message complete ext interrupt */ + case 0x07: /* non priority message complete ext interrupt */ + /* send it to iucv_rx for handling */ +#ifdef DEBUG + printk( "message completed.\n"); +#endif + + if (extern_int_buffer->ipaudit ==0) /* ok case */ + { +#ifdef DEBUG + printk( "iucv: msg complete interrupt successful, rc: %X\n", + (unsigned int)extern_int_buffer->ipaudit); +#endif + ; + } + else + { + printk( "iucv: msg complete interrupt error, rc: %X\n", + (unsigned int)extern_int_buffer->ipaudit); + } + /* a transmission is over: tell we are no more busy */ + dev = get_device_from_pathid(extern_int_buffer->ippathid); + privptr = (struct iucv_priv *)(dev->priv); + privptr->stats.tx_packets++; + mark_bh(NET_BH); + dev->tbusy = 0; /* transmission is no longer busy*/ + break; + + + case 0x08: /* priority message pending */ + case 0x09: /* non priority message pending */ +#ifdef DEBUG + printk( "message pending.\n"); +#endif + dev = get_device_from_pathid(extern_int_buffer->ippathid); + privptr = (struct iucv_priv *)(dev->priv); + rcvptr = &privptr->receive_buffer[0]; + + /* re-set receive buffer */ + memset(privptr->receive_buffer,0,privptr->receive_buffer_len); + len = privptr->receive_buffer_len; + + /* get data now */ + if (extern_int_buffer->ipflags1 & 0x80) + { /* data is in the message */ +#ifdef DEBUG + printk( "iucv: iucv_receive data is in header!\n"); +#endif + memcpy(privptr->receive_buffer, + (char *)extern_int_buffer->iprmmsg1, + (unsigned long)(extern_int_buffer->iprmmsg2)); + } + else /* data is in buffer, do a receive */ + { + rc = iucv_receive(privptr->command_buffer,rcvptr,len); + if (rc != 0 || len == 0) + { + printk( "iucv: iucv_receive failed with rc: %X, length: %lX\n",rc,len); + iucv_retrieve_buffer(privptr->command_buffer); + break; + } + } /* end else */ + + iucv_next = 0; + /* get next packet offset */ + iucv_data_len= *((unsigned short*)rcvptr); + do{ /* until receive buffer is empty, i.e. iucv_next == 0 ! */ + + /* get data length: */ + iucv_data_len= iucv_data_len - iucv_next; + +#ifdef DEBUG + printk( "iucv: iucv_receive: len is %02X, last: %02X\n", + iucv_data_len,iucv_next); +#endif + /* transmit upstairs */ + iucv_rx(dev,(iucv_data_len),rcvptr); + +#ifdef DEBUG + printk( "iucv: transaction complete now.\n"); +#endif + rcvptr = rcvptr + iucv_data_len; + iucv_next = iucv_data_len; + /* get next packet offset */ + iucv_data_len= *((unsigned short*)rcvptr); + + } while (iucv_data_len != 0); + dev->tbusy = 0; /* transmission is no longer busy*/ + break; + + default: + printk( "unknown iucv interrupt \n"); + break; + + } /* end switch */ + dev->interrupt = 0; /* release lock*/ + +#ifdef DEBUG + printk( "iucv: leaving do_iucv_interrupt.\n"); +#endif + +} /* end do_iucv_interrupt() */ + + + +/*-------------------------------------------*/ +/* Transmit a packet (low level interface) */ +/*-------------------------------------------*/ +int iucv_hw_tx(char *send_buf, int len,struct device *dev) +{ + /* This function deals with hw details. */ + /* This interface strips off the ethernet header details. */ + /* In other words, this function implements the iucv behaviour,*/ + /* while all other procedures are rather device-independent */ + struct iucv_priv *privptr; + int rc, recv_len=2000; + + privptr = (struct iucv_priv *)(dev->priv); + +#ifdef DEBUG + printk( "iucv: iucv_hw_tx, device %s\n",dev->name); + printk( "iucv: hw_TX_data len: %X\n",len); + dumpit(send_buf,len); +#endif + + /* I am paranoid. Ain't I? */ + if (len < sizeof(struct iphdr)) + { + printk( "iucv: Hmm... packet too short (%i octets)\n",len); + return -EINVAL; + } + + /* + * build IUCV header (preceeding halfword offset) + * works as follows: Each packet is preceded by the + * halfword offset to the next one. + * The last packet is followed by an offset of zero. + * E.g., AL2(12),10-byte packet, AL2(34), 32-byte packet, AL2(0) + */ + + memcpy(&privptr->send_buffer[2],send_buf,len+2); + privptr->send_buffer[len+2] = 0; + privptr->send_buffer[len+3] = 0; + *((unsigned short*) &privptr->send_buffer[0]) = len + 2; + +#ifdef DEBUG + printk( "iucv: iucv_hw_tx, device %s\n",dev->name); + printk( "iucv: send len: %X\n",len+4); + dumpit(privptr->send_buffer,len+4); +#endif + *((unsigned short*) &privptr->send_buffer[0]) = len + 2; + + /* Ok, now the packet is ready for transmission: send it. */ + if ((rc = iucv_send(privptr->command_buffer, + privptr->pathid, + &privptr->send_buffer[0],len+4, + privptr->recv_buf,recv_len))!=0) { + printk( "iucv: send_iucv failed, rc: %X\n",rc); + iucv_retrieve_buffer(privptr->command_buffer); + } +#ifdef DEBUG + printk( "iucv: send_iucv ended, rc: %X\n",rc); +#endif + return rc; +} /* end iucv_hw_tx() */ + + + + + + +/*------------------------------------------*/ +/* Transmit a packet (called by the kernel) */ +/*------------------------------------------*/ +int iucv_tx(struct sk_buff *skb, struct device *dev) +{ + int retval=0; + + struct iucv_priv *privptr; + + if (dev == NULL) + { + printk("iucv: NULL dev passed\n"); + return 0; + } + + privptr = (struct iucv_priv *) (dev->priv); + + if (skb == NULL) + { + printk("iucv: %s: NULL buffer passed\n", dev->name); + privptr->stats.tx_errors++; + return 0; + } + +#ifdef DEBUG + printk( "iucv: enter iucv_tx, using %s\n",dev->name); +#endif + + if (dev->tbusy) /* shouldn't happen*/ + { + privptr->stats.tx_errors++; + dev_kfree_skb(skb); + printk("iucv: %s: transmit access conflict ! leaving iucv_tx.\n", dev->name); + } + + dev->tbusy = 1; /* transmission is busy*/ + dev->trans_start = jiffies; /* save the timestamp*/ + + /* actual deliver of data is device-specific, and not shown here */ + retval = iucv_hw_tx(skb->data, skb->len, dev); + + dev_kfree_skb(skb); /* release it*/ + +#ifdef DEBUG + printk( "iucv:leaving iucv_tx, device %s\n",dev->name); +#endif + + return retval; /* zero == done; nonzero == fail*/ +} /* end iucv_tx( struct sk_buff *skb, struct device *dev) */ + + + + + + +/*---------------*/ +/* iucv_release */ +/*---------------*/ +int iucv_release(struct device *dev) +{ + int rc =0; + struct iucv_priv *privptr; + privptr = (struct iucv_priv *) (dev->priv); + + dev->start = 0; + dev->tbusy = 1; /* can't transmit any more*/ + rc = iucv_sever(privptr->command_buffer); + if (rc!=0) + { + printk("iucv: %s: iucv_release pending...rc:%02x\n",dev->name,rc); + } + +#ifdef DEBUG + printk("iucv: iucv_sever ended with rc: %X\n",rc); +#endif + + return rc; +} /* end iucv_release() */ + + + + + +/*-----------------------------------------------*/ +/* Configuration changes (passed on by ifconfig) */ +/*-----------------------------------------------*/ +int iucv_config(struct device *dev, struct ifmap *map) +{ + if (dev->flags & IFF_UP) /* can't act on a running interface*/ + return -EBUSY; + + /* ignore other fields */ + return 0; +} +/* end iucv_config() */ + + + + + +/*----------------*/ +/* Ioctl commands */ +/*----------------*/ +int iucv_ioctl(struct device *dev, struct ifreq *rq, int cmd) +{ + printk( "iucv: device %s; iucv_ioctl\n",dev->name); + return 0; +} + +/*---------------------------------*/ +/* Return statistics to the caller */ +/*---------------------------------*/ +struct net_device_stats *iucv_stats(struct device *dev) +{ + struct iucv_priv *priv = (struct iucv_priv *)dev->priv; +#ifdef DEBUG + printk( "iucv: device %s; iucv_stats\n",dev->name); +#endif + return &priv->stats; +} + + +/* + * iucv_change_mtu + * IUCV can handle MTU sizes from 576 to approx. 32000 + */ + +static int iucv_change_mtu(struct device *dev, int new_mtu) +{ +#ifdef DEBUG + printk( "iucv: device %s; iucv_change_mtu\n",dev->name); +#endif + if ((new_mtu < 64) || (new_mtu > 32000)) + return -EINVAL; + dev->mtu = new_mtu; + return 0; +} + + + + +/*--------------------------------------------*/ +/* The init function (sometimes called probe).*/ +/* It is invoked by register_netdev() */ +/*--------------------------------------------*/ +int iucv_init(struct device *dev) +{ + int rc; + struct iucv_priv *privptr; + +#ifdef DEBUG + printk( "iucv: iucv_init, device: %s\n",dev->name); +#endif + + dev->open = iucv_open; + dev->stop = iucv_release; + dev->set_config = iucv_config; + dev->hard_start_xmit = iucv_tx; + dev->do_ioctl = iucv_ioctl; + dev->get_stats = iucv_stats; + dev->change_mtu = iucv_change_mtu; + + /* keep the default flags, just add NOARP */ + + dev->hard_header_len = 0; + dev->addr_len = 0; + dev->type = ARPHRD_SLIP; + dev->tx_queue_len = 100; + dev->flags = IFF_NOARP|IFF_POINTOPOINT; + dev->mtu = 4092; + + dev_init_buffers(dev); + + /* Then, allocate the priv field. This encloses the statistics */ + /* and a few private fields.*/ + dev->priv = kmalloc(sizeof(struct iucv_priv), GFP_KERNEL); + if (dev->priv == NULL){ + printk( "iucv: no memory for dev->priv.\n"); + return -ENOMEM; + } + memset(dev->priv, 0, sizeof(struct iucv_priv)); + privptr = (struct iucv_priv *)(dev->priv); + + + privptr->send_buffer = (u8*) __get_free_pages(GFP_KERNEL+GFP_DMA,8); + if (privptr->send_buffer == NULL) { + printk(KERN_INFO "%s: could not get pages for send buffer\n", + dev->name); + return -ENOMEM; + } + memset(privptr->send_buffer, 0, 8*PAGE_SIZE); + privptr->send_buffer_len=8*PAGE_SIZE; + + privptr->receive_buffer = (u8*) __get_free_pages(GFP_KERNEL+GFP_DMA,8); + if (privptr->receive_buffer == NULL) { + printk(KERN_INFO "%s: could not get pages for receive buffer\n", + dev->name); + return -ENOMEM; + } + memset(privptr->receive_buffer, 0, 8*PAGE_SIZE); + privptr->receive_buffer_len=8*PAGE_SIZE; + + /* now use the private fields ... */ + /* init pathid */ + privptr->pathid = -1; + + /* init private userid from global userid */ + memcpy(privptr->userid,iucv_userid[dev-iucv_devs],8); + + + /* we can use only ONE buffer for external interrupt ! */ + rc=iucv_declare_buffer(privptr->command_buffer, + (DCLBFR_T *)iucv_ext_int_buffer); + if (rc!=0 && rc!=19) /* ignore existing buffer */ + { + printk( "iucv:iucv_declare failed, rc: %X\n",rc); + return -ENODEV; + } + + rc = iucv_enable(privptr->command_buffer); + if (rc!=0) + { + printk( "iucv:iucv_enable failed, rc: %x\n",rc); + iucv_retrieve_buffer(privptr->command_buffer); + return -ENODEV; + } +#ifdef DEBUG + printk( "iucv: iucv_init endend OK for device %s.\n",dev->name); +#endif + return 0; +} + + +/* + * setup iucv devices + * + * string passed: iucv=userid1,...,useridn + */ + +__initfunc(int iucv_setup(char* str, int *ints)) +{ + int result=0, i=0,j=0, k=0, device_present=0; + char *s = str; + struct device * dev ={0}; + +#ifdef DEBUG + printk( "iucv: start registering device(s)... \n"); +#endif + + /* + * scan device userids + */ + + while(*s != 0x20 && *s != '\0'){ + if(*s == ','){ + /* fill userid up to 8 chars */ + for(k=i;k<8;k++){ + iucv_userid[j][k] = 0x40; + } /* end for */ + /* new device */ + j++; + s++; /* ignore current char */ + i=0; + if (j>MAX_DEVICES) { + printk("iucv: setup devices: max devices %d reached.\n", + MAX_DEVICES); + break; + } /* end if */ + continue; + } /* end if */ + iucv_ascii_userid[j][i] = (int)*s; + iucv_userid[j][i] = _ascebc[(int)*s++]; + i++; + } /* end while */ + + /* + * fill last userid up to 8 chars + */ + for(k=i;k<8;k++) { + iucv_userid[j][k] = 0x40; + } + + /* + * set device name and register + */ + + for (k=0;k<=j;k++) { + memcpy(iucv_devs[k].name, "iucv0", 4); + dev = &iucv_devs[k]; + dev->name[4] = k + '0'; + +#ifdef DEBUGX + printk("iucv: (ASCII- )Userid:%s\n",&iucv_ascii_userid[k][0]); + printk("iucv: (ASCII-)Userid: "); + for (i=0;i<8;i++) { + printk( "%02X ",(int)iucv_ascii_userid[k][i]); + } + printk("\n"); + printk("iucv: (EBCDIC-)Userid: "); + for (i=0;i<8;i++) { + printk( "%02X ",(int)iucv_userid[k][i]); + } + printk("\n"); + printk("iucv: device name :%s\n",iucv_devs[k].name); +#endif + + if ( (result = register_netdev(iucv_devs + k)) ) + printk("iucv: error %i registering device \"%s\"\n", + result, iucv_devs[k].name); + else + { + device_present++; + } + } /* end for */ + +#ifdef DEBUG + printk( "iucv: end register devices, %d devices present\n",device_present); +#endif + return device_present ? 0 : -ENODEV; +} + + + + + +/*-------------*/ +/* The devices */ +/*-------------*/ +char iucv_names[MAX_DEVICES*8]; /* MAX_DEVICES eight-byte buffers */ +struct device iucv_devs[MAX_DEVICES] = { + { + iucv_names, /* name -- set at load time */ + 0, 0, 0, 0, /* shmem addresses */ + 0x000, /* ioport */ + 0, /* irq line */ + 0, 0, 0, /* various flags: init to 0 */ + NULL, /* next ptr */ + iucv_init, /* init function, fill other fields with NULL's */ + }, + { + iucv_names+8,/* name -- set at load time */ + 0, 0, 0, 0, /* shmem addresses */ + 0x000, /* ioport */ + 0, /* irq line */ + 0, 0, 0, /* various flags: init to 0 */ + NULL, /* next ptr */ + iucv_init, /* init function, fill other fields with NULL's */ + }, + { + iucv_names+16,/* name -- set at load time */ + 0, 0, 0, 0, /* shmem addresses */ + 0x000, /* ioport */ + 0, /* irq line */ + 0, 0, 0, /* various flags: init to 0 */ + NULL, /* next ptr */ + iucv_init, /* init function, fill other fields with NULL's */ + }, + { + iucv_names+24,/* name -- set at load time */ + 0, 0, 0, 0, /* shmem addresses */ + 0x000, /* ioport */ + 0, /* irq line */ + 0, 0, 0, /* various flags: init to 0 */ + NULL, /* next ptr */ + iucv_init, /* init function, fill other fields with NULL's */ + }, + { + iucv_names+32,/* name -- set at load time */ + 0, 0, 0, 0, /* shmem addresses */ + 0x000, /* ioport */ + 0, /* irq line */ + 0, 0, 0, /* various flags: init to 0 */ + NULL, /* next ptr */ + iucv_init, /* init function, fill other fields with NULL's */ + }, + { + iucv_names+40,/* name -- set at load time */ + 0, 0, 0, 0, /* shmem addresses */ + 0x000, /* ioport */ + 0, /* irq line */ + 0, 0, 0, /* various flags: init to 0 */ + NULL, /* next ptr */ + iucv_init, /* init function, fill other fields with NULL's */ + }, + { + iucv_names+48,/* name -- set at load time */ + 0, 0, 0, 0, /* shmem addresses */ + 0x000, /* ioport */ + 0, /* irq line */ + 0, 0, 0, /* various flags: init to 0 */ + NULL, /* next ptr */ + iucv_init, /* init function, fill other fields with NULL's */ + }, + { + iucv_names+56,/* name -- set at load time */ + 0, 0, 0, 0, /* shmem addresses */ + 0x000, /* ioport */ + 0, /* irq line */ + 0, 0, 0, /* various flags: init to 0 */ + NULL, /* next ptr */ + iucv_init, /* init function, fill other fields with NULL's */ + }, + { + iucv_names+64,/* name -- set at load time */ + 0, 0, 0, 0, /* shmem addresses */ + 0x000, /* ioport */ + 0, /* irq line */ + 0, 0, 0, /* various flags: init to 0 */ + NULL, /* next ptr */ + iucv_init, /* init function, fill other fields with NULL's */ + }, + { + iucv_names+72,/* name -- set at load time */ + 0, 0, 0, 0, /* shmem addresses */ + 0x000, /* ioport */ + 0, /* irq line */ + 0, 0, 0, /* various flags: init to 0 */ + NULL, /* next ptr */ + iucv_init, /* init function, fill other fields with NULL's */ + } +}; + diff -u --recursive --new-file v2.2.13/linux/drivers/s390/net/iucv.h linux/drivers/s390/net/iucv.h --- v2.2.13/linux/drivers/s390/net/iucv.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/s390/net/iucv.h Tue Jan 4 10:12:18 2000 @@ -0,0 +1,146 @@ +/* + * drivers/s390/net/iucv.h + * Network driver for VM using iucv + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Stefan Hegewald + * Hartmut Penner + */ + +#ifndef _IUCV_H +#define _IUCV_H + + +#define UCHAR unsigned char +#define USHORT unsigned short +#define ULONG unsigned long + +#define DEFAULT_BUFFERSIZE 2048 +#define DEFAULT_FN_LENGTH 27 +#define TRANSFERLENGTH 10 + + + +/* function ID's */ +#define RETRIEVE_BUFFER 2 +#define REPLY 3 +#define SEND 4 +#define RECEIVE 5 +#define ACCEPT 10 +#define CONNECT 11 +#define DECLARE_BUFFER 12 +#define SEVER 15 +#define SETMASK 16 +#define SETCMASK 17 +#define PURGE 9999 + +/* structures */ +typedef struct { + USHORT res0; + UCHAR ipflags1; + UCHAR iprcode; + ULONG res1; + ULONG res2; + ULONG ipbfadr1; + ULONG res[6]; +} DCLBFR_T; + +typedef struct { + USHORT ippathid; + UCHAR ipflags1; + UCHAR iprcode; + USHORT ipmsglim; + USHORT res1; + UCHAR ipvmid[8]; + UCHAR ipuser[16]; + UCHAR iptarget[8]; +} CONNECT_T; + +typedef struct { + USHORT ippathid; + UCHAR ipflags1; + UCHAR iprcode; + USHORT ipmsglim; + USHORT res1; + UCHAR res2[8]; + UCHAR ipuser[16]; + UCHAR res3[8]; +} ACCEPT_T; + +typedef struct { + USHORT ippathid; + UCHAR ipflags1; + UCHAR iprcode; + ULONG ipmsgid; + ULONG iptrgcls; + ULONG ipbfadr1; + ULONG ipbfln1f; + ULONG ipsrccls; + ULONG ipmsgtag; + ULONG ipbfadr2; + ULONG ipbfln2f; + ULONG res; +} SEND_T; + +typedef struct { + USHORT ippathid; + UCHAR ipflags1; + UCHAR iprcode; + ULONG ipmsgid; + ULONG iptrgcls; + ULONG iprmmsg1; + ULONG iprmmsg2; + ULONG res1[2]; + ULONG ipbfadr2; + ULONG ipbfln2f; + ULONG res2; +} REPLY_T; + +typedef struct { + USHORT ippathid; + UCHAR ipflags1; + UCHAR iprcode; + ULONG ipmsgid; + ULONG iptrgcls; + ULONG ipbfadr1; + ULONG ipbfln1f; + ULONG res1[3]; + ULONG ipbfln2f; + ULONG res2; +} RECEIVE_T; + +typedef struct { + USHORT ippathid; + UCHAR ipflags1; + UCHAR iprcode; + ULONG res1[3]; + UCHAR ipuser[16]; + ULONG res2[2]; +} SEVER_T; + +typedef struct { + UCHAR ipmask; + UCHAR res1[2]; + UCHAR iprcode; + ULONG res2[9]; +} MASK_T; + +typedef struct { + USHORT ippathid; + UCHAR ipflags1; + UCHAR iptype; + ULONG ipmsgid; + ULONG ipaudit; + ULONG iprmmsg1; + ULONG iprmmsg2; + ULONG ipsrccls; + ULONG ipmsgtag; + ULONG ipbfadr2; + ULONG ipbfln2f; + UCHAR ippollfg; + UCHAR res2[3]; +} INTERRUPT_T; + + +#endif diff -u --recursive --new-file v2.2.13/linux/drivers/sbus/audio/amd7930.c linux/drivers/sbus/audio/amd7930.c --- v2.2.13/linux/drivers/sbus/audio/amd7930.c Tue Jan 4 11:10:37 2000 +++ linux/drivers/sbus/audio/amd7930.c Tue Jan 4 10:12:18 2000 @@ -135,7 +135,7 @@ unsigned char xmit_idle_char; /* Callback routine (and argument) when output is done on */ - void (*output_callback)(); + void (*output_callback)(void *, unsigned char); void * output_callback_arg; /* Current buffer that the driver is recording on channel */ @@ -144,7 +144,7 @@ volatile unsigned long input_limit; /* Callback routine (and argument) when input is done on */ - void (*input_callback)(); + void (*input_callback)(void *, unsigned char, unsigned long); void * input_callback_arg; }; @@ -520,7 +520,7 @@ channel->input_count = 0; if (channel->input_callback) (*channel->input_callback) - (channel->input_callback_arg, 1); + (channel->input_callback_arg, 1, 0); } } } diff -u --recursive --new-file v2.2.13/linux/drivers/sbus/audio/cs4215.h linux/drivers/sbus/audio/cs4215.h --- v2.2.13/linux/drivers/sbus/audio/cs4215.h Mon Mar 15 16:11:30 1999 +++ linux/drivers/sbus/audio/cs4215.h Tue Jan 4 10:12:18 2000 @@ -11,11 +11,12 @@ struct cs4215 { __u8 data[4]; /* Data mode: Time slots 5-8 */ __u8 ctrl[4]; /* Ctrl mode: Time slots 1-4 */ - __volatile__ struct dbri_mem td; - __volatile__ struct dbri_mem rd; __u8 onboard; - __u32 status; - __u32 version; + __volatile__ __u32 status; + __volatile__ __u32 version; + int offset; + int ctrlmode; + int datamode; }; diff -u --recursive --new-file v2.2.13/linux/drivers/sbus/audio/cs4231.c linux/drivers/sbus/audio/cs4231.c --- v2.2.13/linux/drivers/sbus/audio/cs4231.c Tue Jan 4 11:10:37 2000 +++ linux/drivers/sbus/audio/cs4231.c Tue Jan 4 10:12:18 2000 @@ -904,8 +904,9 @@ if (l < 0) l = 0; } - l_adj = l * (CS4231_MAX_GAIN + 1) / (AUDIO_MAX_GAIN + 1); - r_adj = r * (CS4231_MAX_GAIN + 1) / (AUDIO_MAX_GAIN + 1); + /* See cs4231_play_gain for why we do things this way. -DaveM */ + l_adj = ((l * (CS4231_MAX_GAIN + 1)) + AUDIO_MAX_GAIN) / (AUDIO_MAX_GAIN + 1); + r_adj = ((r * (CS4231_MAX_GAIN + 1)) + AUDIO_MAX_GAIN) / (AUDIO_MAX_GAIN + 1); CS4231_WRITE8(cs4231_chip, &(cs4231_chip->regs->iar), 0x0); old_gain = CS4231_READ8(cs4231_chip, &(cs4231_chip->regs->idr)); @@ -932,7 +933,7 @@ int tmp = 0, r, l, r_adj, l_adj; unsigned char old_gain; - tprintk(("in play_gain: %d %c\n", value, balance)); + tprintk(("in play_gain: %d %x, ", value, balance)); r = l = value; if (balance < AUDIO_MID_BALANCE) { r = (int)(value - ((AUDIO_MID_BALANCE - balance) << AUDIO_BALANCE_SHIFT)); @@ -942,11 +943,15 @@ if (l < 0) l = 0; } + /* We have to round up else several OSS programs + * get confused because for small increments we + * would otherwise not increase the volume ever. -DaveM + */ (l == 0) ? (l_adj = CS4231_MAX_DEV_ATEN) : (l_adj = CS4231_MAX_ATEN - - (l * (CS4231_MAX_ATEN + 1) / + (((l * (CS4231_MAX_ATEN + 1)) + AUDIO_MAX_GAIN) / (AUDIO_MAX_GAIN + 1))); (r == 0) ? (r_adj = CS4231_MAX_DEV_ATEN) : (r_adj = CS4231_MAX_ATEN - - (r * (CS4231_MAX_ATEN + 1) / + (((r * (CS4231_MAX_ATEN + 1)) + AUDIO_MAX_GAIN) / (AUDIO_MAX_GAIN + 1))); CS4231_WRITE8(cs4231_chip, &(cs4231_chip->regs->iar), 0x6); diff -u --recursive --new-file v2.2.13/linux/drivers/sbus/audio/dbri.c linux/drivers/sbus/audio/dbri.c --- v2.2.13/linux/drivers/sbus/audio/dbri.c Tue Jan 4 11:10:37 2000 +++ linux/drivers/sbus/audio/dbri.c Tue Jan 4 10:12:18 2000 @@ -64,6 +64,7 @@ #include #include #include +#include #include #include #include @@ -229,7 +230,7 @@ dbri->regs->reg8, dbri->regs->reg9)); dbri->regs->reg0 = D_R; /* Soft Reset */ - for(i = 0; (dbri->regs->reg0 & D_R) && i < 10; i++) + for(i = 0; (dbri->regs->reg0 & D_R) && i < 64; i++) udelay(10); } @@ -370,9 +371,9 @@ int rd; int status; void *buffer; - int count; + int count = 0; void (*callback)(void *, int, unsigned int) = NULL; - void *callback_arg; + void *callback_arg = NULL; if ((pipe < 0) || (pipe > 15)) { printk("DBRI: invalid pipe in reception_complete_intr\n"); @@ -804,6 +805,7 @@ dbri_cmdsend(dbri, cmd); } +#if 0 /* unlink_time_slot() * * I don't use this function, so it's basically untested. @@ -865,6 +867,7 @@ dbri_cmdsend(dbri, cmd); } +#endif /* xmit_fixed() / recv_fixed() * @@ -1475,7 +1478,7 @@ * codec at this location on the CHI, so return false. */ - i = 10; + i = 64; while (((dbri->mm.status & 0xe4) != 0x20) && --i) udelay(125); if (i == 0) { return 0; diff -u --recursive --new-file v2.2.13/linux/drivers/sbus/char/openprom.c linux/drivers/sbus/char/openprom.c --- v2.2.13/linux/drivers/sbus/char/openprom.c Tue Jan 4 11:10:37 2000 +++ linux/drivers/sbus/char/openprom.c Tue Jan 4 10:12:19 2000 @@ -209,7 +209,7 @@ buf, len); restore_flags(flags); - if (error <= 0) + if (error < 0) error = -EINVAL; break; diff -u --recursive --new-file v2.2.13/linux/drivers/sbus/char/pcikbd.c linux/drivers/sbus/char/pcikbd.c --- v2.2.13/linux/drivers/sbus/char/pcikbd.c Tue May 11 08:24:32 1999 +++ linux/drivers/sbus/char/pcikbd.c Tue Jan 4 10:12:19 2000 @@ -1,4 +1,4 @@ -/* $Id: pcikbd.c,v 1.27 1999/05/09 06:40:47 ecd Exp $ +/* $Id: pcikbd.c,v 1.27.2.1 1999/10/01 05:09:36 davem Exp $ * pcikbd.c: Ultra/AX PC keyboard support. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -934,10 +934,34 @@ aux_fasync, }; +static int aux_no_open(struct inode *inode, struct file *file) +{ + return -ENODEV; +} + +struct file_operations psaux_no_fops = { + NULL, /* seek */ + NULL, + NULL, + NULL, /* readdir */ + NULL, + NULL, /* ioctl */ + NULL, /* mmap */ + aux_no_open, + NULL, /* flush */ + NULL, + NULL, + NULL, +}; + static struct miscdevice psaux_mouse = { PSMOUSE_MINOR, "ps2aux", &psaux_fops }; +static struct miscdevice psaux_no_mouse = { + PSMOUSE_MINOR, "ps2aux", &psaux_no_fops +}; + __initfunc(int pcimouse_init(void)) { struct linux_ebus *ebus; @@ -947,7 +971,7 @@ if (pcikbd_mrcoffee) { if ((pcimouse_iobase = pcikbd_iobase) == 0) { printk("pcimouse_init: no 8042 given\n"); - return -ENODEV; + goto do_enodev; } pcimouse_irq = pcikbd_irq; } else { @@ -962,7 +986,7 @@ } } printk("pcimouse_init: no 8042 found\n"); - return -ENODEV; + goto do_enodev; found: pcimouse_iobase = child->base_address[0]; @@ -987,7 +1011,7 @@ SA_SHIRQ, "mouse", (void *)pcimouse_iobase)) { printk("8042: Cannot register IRQ %s\n", __irq_itoa(pcimouse_irq)); - return -ENODEV; + goto do_enodev; } printk("8042(mouse) at %lx (irq %s)\n", pcimouse_iobase, @@ -1016,8 +1040,17 @@ aux_end_atomic(); return 0; + +do_enodev: + misc_register(&psaux_no_mouse); + return -ENODEV; } +__initfunc(int pcimouse_no_init(void)) +{ + misc_register(&psaux_no_mouse); + return -ENODEV; +} __initfunc(int ps2kbd_probe(unsigned long *memory_start)) { @@ -1034,7 +1067,7 @@ len = prom_getproperty(prom_root_node, "name", prop, sizeof(prop)); if (len < 0) { printk("ps2kbd_probe: no name of root node\n"); - return -ENODEV; + goto do_enodev; } if (strncmp(prop, "SUNW,JavaStation-1", len) == 0) { pcikbd_mrcoffee = 1; /* Brain damage detected */ @@ -1047,7 +1080,7 @@ node = prom_getchild(prom_root_node); node = prom_searchsiblings(node, "aliases"); if (!node) - return -ENODEV; + goto do_enodev; len = prom_getproperty(node, "keyboard", prop, sizeof(prop)); if (len > 0) { @@ -1055,7 +1088,7 @@ kbnode = prom_finddevice(prop); } if (!kbnode) - return -ENODEV; + goto do_enodev; len = prom_getproperty(node, "mouse", prop, sizeof(prop)); if (len > 0) { @@ -1063,7 +1096,7 @@ msnode = prom_finddevice(prop); } if (!msnode) - return -ENODEV; + goto do_enodev; /* * Find matching EBus nodes... @@ -1131,6 +1164,8 @@ pnode = prom_getsibling(pnode); pnode = prom_searchsiblings(pnode, "pci"); } +do_enodev: + sunkbd_setinitfunc(memory_start, pcimouse_no_init); return -ENODEV; found: diff -u --recursive --new-file v2.2.13/linux/drivers/sbus/char/sab82532.c linux/drivers/sbus/char/sab82532.c --- v2.2.13/linux/drivers/sbus/char/sab82532.c Thu Mar 25 09:23:34 1999 +++ linux/drivers/sbus/char/sab82532.c Tue Jan 4 10:12:19 2000 @@ -1,4 +1,4 @@ -/* $Id: sab82532.c,v 1.30 1999/03/24 11:34:52 davem Exp $ +/* $Id: sab82532.c,v 1.30.2.2 1999/10/12 14:11:20 davem Exp $ * sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -36,6 +36,12 @@ static DECLARE_TASK_QUEUE(tq_serial); +/* This is (one of many) a special gross hack to allow SU and + * SAB serials to co-exist on the same machine. -DaveM + */ +#undef SERIAL_BH +#define SERIAL_BH AURORA_BH + static struct tty_driver serial_driver, callout_driver; static int sab82532_refcount; @@ -250,6 +256,8 @@ static void batten_down_hatches(struct sab82532 *info) { unsigned char saved_rfc; + + if (!stop_a_enabled) return; /* If we are doing kadb, we call the debugger * else we just drop into the boot monitor. @@ -2136,7 +2144,7 @@ __initfunc(static inline void show_serial_version(void)) { - char *revision = "$Revision: 1.30 $"; + char *revision = "$Revision: 1.30.2.2 $"; char *version, *p; version = strchr(revision, ' '); @@ -2146,6 +2154,8 @@ printk("SAB82532 serial driver version %s\n", serial_version); } +extern int su_num_ports; + /* * The serial driver boot-time initialization code! */ @@ -2169,7 +2179,7 @@ serial_driver.driver_name = "serial"; serial_driver.name = "ttyS"; serial_driver.major = TTY_MAJOR; - serial_driver.minor_start = 64; + serial_driver.minor_start = 64 + su_num_ports; serial_driver.num = NR_PORTS; serial_driver.type = TTY_DRIVER_TYPE_SERIAL; serial_driver.subtype = SERIAL_TYPE_NORMAL; @@ -2267,7 +2277,7 @@ printk(KERN_INFO "ttyS%02d at 0x%lx (irq = %s) is a SAB82532 %s\n", - info->line, (unsigned long)info->regs, + info->line + su_num_ports, (unsigned long)info->regs, __irq_itoa(info->irq), sab82532_version[info->type]); } @@ -2565,8 +2575,9 @@ __initfunc(int sab82532_console_init(void)) { extern int con_is_present(void); + extern int su_console_registered; - if (con_is_present()) + if (con_is_present() || su_console_registered) return 0; if (!sab82532_chain) { diff -u --recursive --new-file v2.2.13/linux/drivers/sbus/char/su.c linux/drivers/sbus/char/su.c --- v2.2.13/linux/drivers/sbus/char/su.c Mon Mar 15 16:11:30 1999 +++ linux/drivers/sbus/char/su.c Tue Jan 4 10:12:19 2000 @@ -1,8 +1,8 @@ -/* $Id: su.c,v 1.18 1999/01/02 16:47:37 davem Exp $ +/* $Id: su.c,v 1.18.2.5 1999/10/14 08:44:35 davem Exp $ * su.c: Small serial driver for keyboard/mouse interface on sparc32/PCI * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) - * Coypright (C) 1998 Pete Zaitcev (zaitcev@metabyte.com) + * Copyright (C) 1998-1999 Pete Zaitcev (zaitcev@metabyte.com) * * This is mainly a variation of drivers/char/serial.c, * credits go to authors mentioned therein. @@ -92,6 +92,11 @@ int su_serial_console_init(void); #endif +enum su_type { SU_PORT_NONE, SU_PORT_MS, SU_PORT_KBD, SU_PORT_PORT }; +static char *su_typev[] = { "???", "mouse", "kbd", "serial" }; + +#define SU_PROPSIZE 128 + /* * serial.c saves memory when it allocates async_info upon first open. * We have parts of state structure together because we do call startup @@ -107,8 +112,8 @@ int line; int cflag; - int kbd_node; - int ms_node; + enum su_type port_type; /* Hookup type: e.g. mouse */ + int is_console; int port_node; char name[16]; @@ -145,6 +150,18 @@ unsigned long last_active; /* For async_struct, to be */ }; +/* + * Scan status structure. + * "prop" is a local variable but it eats stack to keep it in each + * stack frame of a recursive procedure. + */ +struct su_probe_scan { + int msnode, kbnode; /* PROM nodes for mouse and keyboard */ + int msx, kbx; /* minors for mouse and keyboard */ + int devices; /* scan index */ + char prop[SU_PROPSIZE]; +}; + static char *serial_name = "PCIO serial driver"; static char serial_version[16]; @@ -223,8 +240,6 @@ return 0; } -#ifdef __sparc_v9__ - static inline unsigned int su_inb(struct su_struct *info, unsigned long offset) { @@ -234,20 +249,7 @@ static inline void su_outb(struct su_struct *info, unsigned long offset, int value) { - outb(value, info->port + offset); -} - -#else - -static inline -unsigned int su_inb(struct su_struct *info, unsigned long offset) -{ - return (unsigned int)(*(volatile unsigned char *)(info->port + offset)); -} - -static inline void -su_outb(struct su_struct *info, unsigned long offset, int value) -{ +#ifndef __sparc_v9__ /* * MrCoffee has weird schematics: IRQ4 & P10(?) pins of SuperIO are * connected with a gate then go to SlavIO. When IRQ4 goes tristated @@ -257,10 +259,9 @@ * This problem is similar to what Alpha people suffer, see serial.c. */ if (offset == UART_MCR) value |= UART_MCR_OUT2; - *(volatile unsigned char *)(info->port + offset) = value; -} - #endif + outb(value, info->port + offset); +} #define serial_in(info, off) su_inb(info, off) #define serial_inp(info, off) su_inb(info, off) @@ -341,14 +342,14 @@ } static __inline__ void -receive_kbd_ms_chars(struct su_struct *info, struct pt_regs *regs) +receive_kbd_ms_chars(struct su_struct *info, struct pt_regs *regs, int is_brk) { unsigned char status = 0; unsigned char ch; do { ch = serial_inp(info, UART_RX); - if (info->kbd_node) { + if (info->port_type == SU_PORT_KBD) { if(ch == SUNKBD_RESET) { l1a_state.kbd_id = 1; l1a_state.l1_down = 0; @@ -368,7 +369,7 @@ } sunkbd_inchar(ch, regs); } else { - sun_mouse_inbyte(ch); + sun_mouse_inbyte(ch, is_brk); } status = su_inb(info, UART_LSR); @@ -380,12 +381,15 @@ { struct tty_struct *tty = info->tty; unsigned char ch; - int ignored = 0; + int ignored = 0, saw_console_brk = 0; struct async_icount *icount; icount = &info->icount; do { ch = serial_inp(info, UART_RX); + if (info->is_console && + (ch == 0 || (*status &UART_LSR_BI))) + saw_console_brk = 1; if (tty->flip.count >= TTY_FLIPBUF_SIZE) break; *tty->flip.char_buf_ptr = ch; @@ -461,6 +465,8 @@ printk("E%02x.R%d", *status, tty->flip.count); #endif tty_flip_buffer_push(tty); + if (saw_console_brk != 0) + batten_down_hatches(); } static __inline__ void @@ -529,7 +535,7 @@ (status & UART_MSR_DCD)) hardpps(); #endif - } + } if (status & UART_MSR_DCTS) icount->cts++; wake_up_interruptible(&info->delta_msr_wait); @@ -598,8 +604,9 @@ #ifdef SERIAL_DEBUG_INTR printk("status = %x...", status); #endif - if (status & UART_LSR_DR) - receive_kbd_ms_chars(info, regs); + if ((status & UART_LSR_DR) || (status & UART_LSR_BI)) + receive_kbd_ms_chars(info, regs, + (status & UART_LSR_BI) != 0); #ifdef SERIAL_DEBUG_INTR printk("end.\n"); @@ -775,7 +782,7 @@ /* * Allocate the IRQ if necessary */ - if (info->kbd_node || info->ms_node) { + if (info->port_type != SU_PORT_PORT) { retval = request_irq(info->irq, su_kbd_ms_interrupt, SA_SHIRQ, info->name, info); } else { @@ -956,7 +963,7 @@ int bits; unsigned long flags; - if (!info->kbd_node && !info->ms_node) { + if (info->port_type == SU_PORT_PORT) { if (!info->tty || !info->tty->termios) return; if (!info->port) @@ -1133,9 +1140,9 @@ struct su_struct *info = su_table; int lsr; - if (!info->kbd_node) + if (info->port_type != SU_PORT_KBD) ++info; - if (!info) + if (info->port_type != SU_PORT_KBD) return; do { @@ -1151,9 +1158,9 @@ { struct su_struct *info = su_table; - if (!info->ms_node) + if (info->port_type != SU_PORT_MS) ++info; - if (!info) + if (info->port_type != SU_PORT_MS) return; info->cflag &= ~(CBAUDEX | CBAUD); @@ -2026,6 +2033,8 @@ if ((line < 0) || (line >= NR_PORTS)) return -ENODEV; info = su_table + line; + if (info->type == PORT_UNKNOWN) + return -ENODEV; info->count++; tty->driver_data = info; info->tty = tty; @@ -2202,9 +2211,9 @@ /* * --------------------------------------------------------------------- - * su_init() and friends + * su_XXX_init() and friends * - * su_init() is called at boot-time to initialize the serial driver. + * su_XXX_init() is called at boot-time to initialize the serial driver. * --------------------------------------------------------------------- */ @@ -2215,7 +2224,7 @@ */ __initfunc(static __inline__ void show_su_version(void)) { - char *revision = "$Revision: 1.18 $"; + char *revision = "$Revision: 1.18.2.5 $"; char *version, *p; version = strchr(revision, ' '); @@ -2226,8 +2235,8 @@ } /* - * This routine is called by su_init() to initialize a specific serial - * port. It determines what type of UART chip this serial port is + * This routine is called by su_{serial|kbd_ms}_init() to initialize a specific + * serial port. It determines what type of UART chip this serial port is * using: 8250, 16450, 16550, 16550A. The important question is * whether or not this UART is a 16550A, since this will determine * whether or not we can use its FIFO features. @@ -2236,38 +2245,38 @@ autoconfig(struct su_struct *info) { unsigned char status1, status2, scratch, scratch2; -#ifdef __sparc_v9__ struct linux_ebus_device *dev = 0; struct linux_ebus *ebus; -#else +#ifndef __sparc_v9__ struct linux_prom_registers reg0; #endif unsigned long flags; -#ifdef __sparc_v9__ + if (!info->port_node || !info->port_type) + return; + + /* + * First we look for Ebus-bases su's + */ for_each_ebus(ebus) { for_each_ebusdev(dev, ebus) { - if (!strncmp(dev->prom_name, "su", 2)) { - if (dev->prom_node == info->kbd_node) - goto ebus_done; - if (dev->prom_node == info->ms_node) - goto ebus_done; + if (dev->prom_node == info->port_node) { + info->port = dev->base_address[0]; + info->irq = dev->irqs[0]; + goto ebus_done; } } } -ebus_done: - if (!dev) - return; - - info->port = dev->base_address[0]; - if (check_region(info->port, 8)) - return; - info->irq = dev->irqs[0]; +#ifdef __sparc_v9__ + /* + * Not on Ebus, bailing. + */ + return; #else - if (!info->port_node) - return; - + /* + * Not on Ebus, must be OBIO. + */ if (prom_getproperty(info->port_node, "reg", (char *)®0, sizeof(reg0)) == -1) { prom_printf("su: no \"reg\" property\n"); @@ -2279,21 +2288,24 @@ prom_printf("su: cannot map\n"); return; } + /* - * There is no intr property on MrCoffee, so hardwire it. Krups? + * There is no intr property on MrCoffee, so hardwire it. */ info->irq = IRQ_4M(13); #endif -#ifdef DEBUG_SERIAL_OPEN - printk("Found 'su' at %016lx IRQ %s\n", dev->base_address[0], - __irq_itoa(dev->irqs[0])); +ebus_done: + +#ifdef SERIAL_DEBUG_OPEN + printk("Found 'su' at %016lx IRQ %s\n", info->port, + __irq_itoa(info->irq)); #endif info->magic = SERIAL_MAGIC; save_flags(flags); cli(); - + /* * Do a simple existence test first; if we fail this, there's * no point trying anything else. @@ -2312,17 +2324,20 @@ return; /* We failed; there's nothing here */ } -#if 0 /* P3: This does not work on MrCoffee. OUT2 is 0x80 - should work... */ scratch = serial_inp(info, UART_MCR); serial_outp(info, UART_MCR, UART_MCR_LOOP | scratch); serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A); status1 = serial_inp(info, UART_MSR) & 0xF0; serial_outp(info, UART_MCR, scratch); if (status1 != 0x90) { + /* + * This code fragment used to fail, now it fixed itself. + * We keep the printout for a case. + */ + printk("su: loopback returned status 0x%02x\n", status1); restore_flags(flags); return; } -#endif scratch2 = serial_in(info, UART_LCR); serial_outp(info, UART_LCR, 0xBF); /* set up for StarTech test */ @@ -2389,11 +2404,7 @@ return; } - if (info->kbd_node || info->ms_node) - sprintf(info->name, "su(%s)", info->ms_node ? "mouse" : "kbd"); - else - strcpy(info->name, "su(serial)"); - + sprintf(info->name, "su(%s)", su_typev[info->port_type]); #ifdef __sparc_v9__ request_region(info->port, 8, info->name); #endif @@ -2409,6 +2420,11 @@ restore_flags(flags); } +/* This is used by the SAB driver to adjust where its minor + * numbers start, we always are probed for first. + */ +int su_num_ports = 0; + /* * The serial driver boot-time initialization code! */ @@ -2499,11 +2515,18 @@ if (info->type == PORT_UNKNOWN) continue; - printk(KERN_INFO "%s at %16lx (irq = %s) is a %s\n", - info->name, info->port, __irq_itoa(info->irq), + printk(KERN_INFO "%s at 0x%lx (tty %d irq %s) is a %s\n", + info->name, (long)info->port, i, __irq_itoa(info->irq), uart_config[info->type].name); } + for (i = 0, info = su_table; i < NR_PORTS; i++, info++) + if (info->type == PORT_UNKNOWN) + break; + + su_num_ports = i; + serial_driver.num = callout_driver.num = i; + return 0; } @@ -2519,7 +2542,7 @@ info->type = PORT_UNKNOWN; info->baud_base = BAUD_BASE; - if (info->kbd_node) + if (info->port_type == SU_PORT_KBD) info->cflag = B1200 | CS8 | CLOCAL | CREAD; else info->cflag = B4800 | CS8 | CLOCAL | CREAD; @@ -2528,12 +2551,12 @@ if (info->type == PORT_UNKNOWN) continue; - printk(KERN_INFO "%s at %16lx (irq = %s) is a %s\n", + printk(KERN_INFO "%s at 0x%lx (irq = %s) is a %s\n", info->name, info->port, __irq_itoa(info->irq), uart_config[info->type].name); startup(info); - if (info->kbd_node) + if (info->port_type == SU_PORT_KBD) keyboard_zsinit(su_put_char_kbd); else sun_mouse_zsinit(); @@ -2541,154 +2564,131 @@ return 0; } -__initfunc(int su_probe (unsigned long *memory_start)) +/* + * We got several platforms which present 'su' in different parts + * of device tree. 'su' may be found under obio, ebus, isa and pci. + * We walk over the tree and find them wherever PROM hides them. + */ +__initfunc(void su_probe_any(struct su_probe_scan *t, int sunode)) { - struct su_struct *info = su_table; - int node, enode, tnode, sunode; - int kbnode = 0, msnode = 0; - int devices = 0; - char prop[128]; + struct su_struct *info; int len; - /* - * Find su on MrCoffee. We return OK code if find any. - * Then su_init finds every one and initializes them. - * We do this early because MrCoffee got no aliases. - */ - node = prom_getchild(prom_root_node); - if ((node = prom_searchsiblings(node, "obio")) != 0) { - if ((sunode = prom_getchild(node)) != 0) { - if ((sunode = prom_searchsiblings(sunode, "su")) != 0) { - info->port_node = sunode; -#ifdef CONFIG_SERIAL_CONSOLE - /* - * Console must be initiated after the generic - * initialization. - * sunserial_setinitfunc inverts order, so - * call this before next one. - */ - sunserial_setinitfunc(memory_start, - su_serial_console_init); -#endif - sunserial_setinitfunc(memory_start, - su_serial_init); - return 0; + if (t->devices >= NR_PORTS) + return; + + for (; sunode != 0; sunode = prom_getsibling(sunode)) { + len = prom_getproperty(sunode, "name", t->prop, SU_PROPSIZE); + if (len <= 1) + continue; /* Broken PROM node */ + if (strncmp(t->prop, "su", len) == 0 || + strncmp(t->prop, "serial", len) == 0 || + strncmp(t->prop, "su_pnp", len) == 0) { + info = &su_table[t->devices]; + if (t->kbnode != 0 && sunode == t->kbnode) { + t->kbx = t->devices; + info->port_type = SU_PORT_KBD; + } else if (t->msnode != 0 && sunode == t->msnode) { + t->msx = t->devices; + info->port_type = SU_PORT_MS; + } else { + info->port_type = SU_PORT_PORT; } + info->is_console = 0; + info->port_node = sunode; + ++t->devices; + } else { + su_probe_any(t, prom_getchild(sunode)); } } +} + +__initfunc(int su_probe (unsigned long *memory_start)) +{ + int node; + int len; + struct su_probe_scan scan; + + /* + * First, we scan the tree. + */ + scan.devices = 0; + scan.msx = -1; + scan.kbx = -1; + scan.kbnode = 0; + scan.msnode = 0; /* * Get the nodes for keyboard and mouse from 'aliases'... */ node = prom_getchild(prom_root_node); node = prom_searchsiblings(node, "aliases"); - if (!node) - return -ENODEV; + if (node != 0) { - len = prom_getproperty(node, "keyboard", prop, sizeof(prop)); - if (len > 0) { - prop[len] = 0; - kbnode = prom_finddevice(prop); - } - if (!kbnode) - return -ENODEV; + len = prom_getproperty(node, "keyboard", scan.prop,SU_PROPSIZE); + if (len > 0) { + scan.prop[len] = 0; + scan.kbnode = prom_finddevice(scan.prop); + } - len = prom_getproperty(node, "mouse", prop, sizeof(prop)); - if (len > 0) { - prop[len] = 0; - msnode = prom_finddevice(prop); + len = prom_getproperty(node, "mouse", scan.prop, SU_PROPSIZE); + if (len > 0) { + scan.prop[len] = 0; + scan.msnode = prom_finddevice(scan.prop); + } } - if (!msnode) - return -ENODEV; - /* - * Find matching EBus nodes... - */ - node = prom_getchild(prom_root_node); - if ((node = prom_searchsiblings(node, "pci")) == 0) { - return -ENODEV; /* Plain sparc */ - } + su_probe_any(&scan, prom_getchild(prom_root_node)); /* - * Check for SUNW,sabre on Ultra 5/10/AXi. - */ - len = prom_getproperty(node, "model", prop, sizeof(prop)); - if ((len > 0) && !strncmp(prop, "SUNW,sabre", len)) { - node = prom_getchild(node); - node = prom_searchsiblings(node, "pci"); + * Second, we process the special case of keyboard and mouse. + * + * Currently if we got keyboard and mouse hooked to "su" ports + * we do not use any possible remaining "su" as a serial port. + * Thus, we ignore values of .msx and .kbx, then compact ports. + * Those who want to address this issue need to merge + * su_serial_init() and su_ms_kbd_init(). + */ + if (scan.msx != -1 && scan.kbx != -1) { + su_table[0].port_type = SU_PORT_MS; + su_table[0].is_console = 0; + su_table[0].port_node = scan.msnode; + su_table[1].port_type = SU_PORT_KBD; + su_table[1].is_console = 0; + su_table[1].port_node = scan.kbnode; + + sunserial_setinitfunc(memory_start, su_kbd_ms_init); + rs_ops.rs_change_mouse_baud = su_change_mouse_baud; + sunkbd_setinitfunc(memory_start, sun_kbd_init); + kbd_ops.compute_shiftstate = sun_compute_shiftstate; + kbd_ops.setledstate = sun_setledstate; + kbd_ops.getledstate = sun_getledstate; + kbd_ops.setkeycode = sun_setkeycode; + kbd_ops.getkeycode = sun_getkeycode; +#ifdef CONFIG_PCI + sunkbd_install_keymaps(memory_start, sun_key_maps, + sun_keymap_count, sun_func_buf, sun_func_table, + sun_funcbufsize, sun_funcbufleft, + sun_accent_table, sun_accent_table_size); +#endif + return 0; + } + if (scan.msx != -1 || scan.kbx != -1) { + printk("su_probe: cannot match keyboard and mouse, confused\n"); + return -ENODEV; } + if (scan.devices == 0) + return -ENODEV; + +#ifdef CONFIG_SERIAL_CONSOLE /* - * For each PCI bus... + * Console must be initiated after the generic initialization. + * sunserial_setinitfunc inverts order, so call this before next one. */ - while (node) { - enode = prom_getchild(node); - enode = prom_searchsiblings(enode, "ebus"); - - /* - * For each EBus on this PCI... - */ - while (enode) { - sunode = prom_getchild(enode); - tnode = prom_searchsiblings(sunode, "su"); - if (!tnode) - tnode = prom_searchsiblings(sunode, "su_pnp"); - sunode = tnode; - - /* - * For each 'su' on this EBus... - */ - while (sunode) { - /* - * Does it match? - */ - if (sunode == kbnode) { - info->kbd_node = sunode; - ++info; - ++devices; - } - if (sunode == msnode) { - info->ms_node = sunode; - ++info; - ++devices; - } - - /* - * Found everything we need? - */ - if (devices == 2) - goto found; - - sunode = prom_getsibling(sunode); - tnode = prom_searchsiblings(sunode, "su"); - if (!tnode) - tnode = prom_searchsiblings(sunode, - "su_pnp"); - sunode = tnode; - } - enode = prom_getsibling(enode); - enode = prom_searchsiblings(enode, "ebus"); - } - node = prom_getsibling(node); - node = prom_searchsiblings(node, "pci"); - } - return -ENODEV; - -found: - sunserial_setinitfunc(memory_start, su_kbd_ms_init); - rs_ops.rs_change_mouse_baud = su_change_mouse_baud; - sunkbd_setinitfunc(memory_start, sun_kbd_init); - kbd_ops.compute_shiftstate = sun_compute_shiftstate; - kbd_ops.setledstate = sun_setledstate; - kbd_ops.getledstate = sun_getledstate; - kbd_ops.setkeycode = sun_setkeycode; - kbd_ops.getkeycode = sun_getkeycode; -#ifdef CONFIG_PCI - sunkbd_install_keymaps(memory_start, sun_key_maps, sun_keymap_count, - sun_func_buf, sun_func_table, - sun_funcbufsize, sun_funcbufleft, - sun_accent_table, sun_accent_table_size); + sunserial_setinitfunc(memory_start, su_serial_console_init); #endif + sunserial_setinitfunc(memory_start, su_serial_init); return 0; } @@ -2910,6 +2910,8 @@ if (su_inb(info, UART_LSR) == 0xff) return -1; + info->is_console = 1; + return 0; } @@ -2927,6 +2929,8 @@ NULL }; +int su_console_registered = 0; + /* * Register console. */ @@ -2942,6 +2946,7 @@ return 0; sercons.index = 0; register_console(&sercons); + su_console_registered = 1; return 0; } diff -u --recursive --new-file v2.2.13/linux/drivers/sbus/char/sunkeymap.c linux/drivers/sbus/char/sunkeymap.c --- v2.2.13/linux/drivers/sbus/char/sunkeymap.c Mon Jan 27 00:42:54 1997 +++ linux/drivers/sbus/char/sunkeymap.c Tue Jan 4 10:12:19 2000 @@ -1,3 +1,4 @@ + /* Do not edit this file! It was automatically generated by */ /* loadkeys --mktable defkeymap.map > defkeymap.c */ @@ -30,7 +31,7 @@ 0xf110, 0xf111, 0xf112, 0xf703, 0xf603, 0xf11d, 0xf200, 0xf203, 0xf601, 0xf200, 0xf200, 0xf600, 0xf602, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 0xf026, 0xf02a, 0xf028, 0xf029, - 0xf05f, 0xf02b, 0xf07e, 0xf07f, 0xf115, 0xf200, 0xf30d, 0xf30c, + 0xf05f, 0xf02b, 0xf07e, 0xf07f, 0xf115, 0xf03d, 0xf30d, 0xf30c, 0xf200, 0xf200, 0xf310, 0xf200, 0xf114, 0xf009, 0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, 0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf07f, 0xf20e, 0xf307, 0xf308, 0xf309, 0xf30b, @@ -38,7 +39,7 @@ 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03a, 0xf022, 0xf07c, 0xf201, 0xf30e, 0xf304, 0xf305, 0xf306, 0xf300, 0xf200, 0xf20b, 0xf200, 0xf208, 0xf700, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, - 0xfb42, 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf200, + 0xfb42, 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf00a, 0xf301, 0xf302, 0xf303, 0xf200, 0xf200, 0xf200, 0xf11b, 0xf207, 0xf200, 0xf020, 0xf200, 0xf20a, 0xf200, 0xf30a, 0xf200, 0xf200, }; @@ -49,7 +50,7 @@ 0xf512, 0xf513, 0xf514, 0xf703, 0xf603, 0xf11d, 0xf200, 0xf202, 0xf601, 0xf200, 0xf200, 0xf600, 0xf602, 0xf200, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200, 0xf07b, 0xf05b, 0xf05d, 0xf07d, - 0xf05c, 0xf200, 0xf200, 0xf200, 0xf115, 0xf200, 0xf30d, 0xf30c, + 0xf05c, 0xf200, 0xf200, 0xf200, 0xf115, 0xf03d, 0xf30d, 0xf30c, 0xf200, 0xf200, 0xf310, 0xf200, 0xf114, 0xf200, 0xfb71, 0xfb77, 0xf918, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, 0xfb6f, 0xfb70, 0xf200, 0xf07e, 0xf200, 0xf20e, 0xf911, 0xf912, 0xf913, 0xf30b, @@ -57,7 +58,7 @@ 0xf919, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf200, 0xf200, 0xf200, 0xf201, 0xf30e, 0xf90e, 0xf90f, 0xf910, 0xf90a, 0xf200, 0xf118, 0xf200, 0xf208, 0xf700, 0xfb7a, 0xfb78, 0xf916, 0xfb76, - 0xf915, 0xfb6e, 0xfb6d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf200, + 0xf915, 0xfb6e, 0xfb6d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf00a, 0xf90b, 0xf90c, 0xf90d, 0xf200, 0xf200, 0xf200, 0xf11b, 0xf207, 0xf200, 0xf200, 0xf200, 0xf119, 0xf200, 0xf30a, 0xf200, 0xf200, }; @@ -68,7 +69,7 @@ 0xf106, 0xf107, 0xf108, 0xf703, 0xf603, 0xf11d, 0xf200, 0xf204, 0xf601, 0xf200, 0xf200, 0xf600, 0xf602, 0xf200, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f, 0xf07f, 0xf200, 0xf200, - 0xf01f, 0xf200, 0xf000, 0xf008, 0xf115, 0xf200, 0xf30d, 0xf30c, + 0xf01f, 0xf200, 0xf000, 0xf008, 0xf115, 0xf03d, 0xf30d, 0xf30c, 0xf200, 0xf200, 0xf310, 0xf200, 0xf114, 0xf200, 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf008, 0xf20e, 0xf307, 0xf308, 0xf309, 0xf30b, @@ -76,7 +77,7 @@ 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 0xf007, 0xf01c, 0xf201, 0xf30e, 0xf304, 0xf305, 0xf306, 0xf300, 0xf200, 0xf118, 0xf200, 0xf208, 0xf700, 0xf01a, 0xf018, 0xf003, 0xf016, - 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf200, + 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf00a, 0xf301, 0xf302, 0xf303, 0xf200, 0xf200, 0xf200, 0xf11b, 0xf207, 0xf200, 0xf000, 0xf200, 0xf119, 0xf200, 0xf30a, 0xf200, 0xf200, }; @@ -87,7 +88,7 @@ 0xf200, 0xf200, 0xf200, 0xf703, 0xf603, 0xf11d, 0xf200, 0xf200, 0xf601, 0xf200, 0xf200, 0xf600, 0xf602, 0xf200, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf01f, 0xf200, 0xf200, 0xf200, 0xf115, 0xf200, 0xf30d, 0xf30c, + 0xf01f, 0xf200, 0xf200, 0xf200, 0xf115, 0xf03d, 0xf30d, 0xf30c, 0xf200, 0xf200, 0xf310, 0xf200, 0xf114, 0xf200, 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 0xf00f, 0xf010, 0xf200, 0xf200, 0xf200, 0xf20e, 0xf307, 0xf308, 0xf309, 0xf30b, @@ -95,7 +96,7 @@ 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 0xf200, 0xf200, 0xf201, 0xf30e, 0xf304, 0xf305, 0xf306, 0xf300, 0xf200, 0xf118, 0xf200, 0xf208, 0xf700, 0xf01a, 0xf018, 0xf003, 0xf016, - 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf200, + 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf00a, 0xf301, 0xf302, 0xf303, 0xf200, 0xf200, 0xf200, 0xf11b, 0xf207, 0xf200, 0xf200, 0xf200, 0xf119, 0xf200, 0xf30a, 0xf200, 0xf200, }; @@ -106,15 +107,15 @@ 0xf506, 0xf507, 0xf508, 0xf703, 0xf603, 0xf11d, 0xf200, 0xf209, 0xf210, 0xf200, 0xf200, 0xf600, 0xf211, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 0xf837, 0xf838, 0xf839, 0xf830, - 0xf82d, 0xf83d, 0xf860, 0xf87f, 0xf115, 0xf200, 0xf30d, 0xf30c, + 0xf82d, 0xf83d, 0xf860, 0xf87f, 0xf115, 0xf03d, 0xf30d, 0xf30c, 0xf200, 0xf200, 0xf310, 0xf200, 0xf114, 0xf809, 0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, 0xf86f, 0xf870, - 0xf85b, 0xf85d, 0xf200, 0xf87f, 0xf907, 0xf908, 0xf909, 0xf30b, + 0xf85b, 0xf85d, 0xf87f, 0xf20e, 0xf907, 0xf908, 0xf909, 0xf30b, 0xf200, 0xf200, 0xf117, 0xf200, 0xf702, 0xf861, 0xf873, 0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b, 0xf827, 0xf85c, 0xf80d, 0xf30e, 0xf904, 0xf905, 0xf906, 0xf900, 0xf200, 0xf118, 0xf200, 0xf208, 0xf700, 0xf87a, 0xf878, 0xf863, 0xf876, - 0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf200, + 0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf00a, 0xf901, 0xf902, 0xf903, 0xf200, 0xf200, 0xf200, 0xf11b, 0xf207, 0xf200, 0xf820, 0xf200, 0xf119, 0xf200, 0xf30a, 0xf200, 0xf200, }; @@ -125,7 +126,7 @@ 0xf506, 0xf507, 0xf508, 0xf703, 0xf603, 0xf11d, 0xf200, 0xf200, 0xf601, 0xf200, 0xf200, 0xf600, 0xf602, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf115, 0xf200, 0xf30d, 0xf30c, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf115, 0xf03d, 0xf30d, 0xf30c, 0xf200, 0xf200, 0xf20c, 0xf200, 0xf114, 0xf200, 0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 0xf80f, 0xf810, 0xf200, 0xf200, 0xf20c, 0xf20e, 0xf307, 0xf308, 0xf309, 0xf30b, @@ -133,7 +134,7 @@ 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 0xf200, 0xf200, 0xf201, 0xf30e, 0xf304, 0xf305, 0xf306, 0xf300, 0xf200, 0xf118, 0xf200, 0xf208, 0xf700, 0xf81a, 0xf818, 0xf803, 0xf816, - 0xf802, 0xf80e, 0xf80d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf200, + 0xf802, 0xf80e, 0xf80d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf00a, 0xf301, 0xf302, 0xf303, 0xf200, 0xf200, 0xf200, 0xf11b, 0xf207, 0xf200, 0xf200, 0xf200, 0xf119, 0xf200, 0xf30a, 0xf200, 0xf200, }; @@ -147,6 +148,7 @@ unsigned int keymap_count = 7; + /* * Philosophy: most people do not define more strings, but they who do * often want quite a lot of string space. So, we statically allocate @@ -183,6 +185,7 @@ '\033', '[', 'M', 0, '\033', '[', 'P', 0, }; + char *funcbufptr = func_buf; int funcbufsize = sizeof(func_buf); diff -u --recursive --new-file v2.2.13/linux/drivers/sbus/char/sunkeymap.map linux/drivers/sbus/char/sunkeymap.map --- v2.2.13/linux/drivers/sbus/char/sunkeymap.map Mon Jan 27 00:42:54 1997 +++ linux/drivers/sbus/char/sunkeymap.map Tue Jan 4 10:12:19 2000 @@ -160,7 +160,7 @@ alt keycode 0x41 = Meta_bracketright keycode 0x42 = Delete Delete control keycode 0x42 = BackSpace - alt keycode 0x43 = Meta_Delete + alt keycode 0x42 = Meta_Delete control alt keycode 0x42 = Boot keycode 0x43 = Compose keycode 0x44 = KP_7 diff -u --recursive --new-file v2.2.13/linux/drivers/sbus/char/sunmouse.c linux/drivers/sbus/char/sunmouse.c --- v2.2.13/linux/drivers/sbus/char/sunmouse.c Thu Apr 22 19:24:51 1999 +++ linux/drivers/sbus/char/sunmouse.c Tue Jan 4 10:12:19 2000 @@ -81,7 +81,7 @@ unsigned int tail; union { char stream [STREAM_SIZE]; - Firm_event ev [0]; + Firm_event ev [EV_SIZE]; } queue; }; @@ -142,8 +142,6 @@ } /* Auto baud rate "detection". ;-) */ -static int mouse_bogon_bytes = 0; -static int mouse_baud_changing = 0; /* For reporting things to the user. */ static int mouse_baud = 4800; /* Initial rate set by zilog driver. */ /* Change the baud rate after receiving too many "bogon bytes". */ @@ -161,71 +159,112 @@ mouse_baud = 1200; rs_change_mouse_baud(mouse_baud); - mouse_baud_changing = 1; } -void mouse_baud_detection(unsigned char c) +/* This tries to monitor the mouse state so that it + * can automatically adjust to the correct baud rate. + * The mouse spits out BRKs when the baud rate is + * incorrect. + * + * It returns non-zero if we should ignore this byte. + */ +int mouse_baud_detection(unsigned char c, int is_break) { - static int wait_for_synchron = 1; + static int mouse_got_break = 0; static int ctr = 0; - if(wait_for_synchron) { - if((c < 0x80) || (c > 0x87)) - mouse_bogon_bytes++; - else { - ctr = 0; - wait_for_synchron = 0; - } - } else { + if (is_break) { + /* Let a few normal bytes go by before + * we jump the gun and say we need to + * try another baud rate. + */ + if (mouse_got_break && ctr < 8) + return 1; + + /* OK, we need to try another baud rate. */ + sun_mouse_change_baud(); + ctr = 0; + mouse_got_break = 1; + return 1; + } + if (mouse_got_break) { ctr++; - if(ctr >= 4) { - ctr = 0; - wait_for_synchron = 1; - if(mouse_baud_changing == 1) { - printk(KERN_DEBUG "sunmouse: Successfully adjusted to %d baud.\n", - mouse_baud); - mouse_baud_changing = 0; - } + if (c == 0x87) { + printk(KERN_INFO "sunmouse: Successfully " + "adjusted to %d baud.\n", mouse_baud); + mouse_got_break = 0; } + return 1; } - if(mouse_bogon_bytes > 12) { - sun_mouse_change_baud(); - mouse_bogon_bytes = 0; - wait_for_synchron = 1; - } + + return 0; } -/* The following is called from the zs driver when bytes are received on - * the Mouse zs8530 channel. +/* You ask me, why does this cap the lower bound at -127 and not + * -128? Because the xf86 mouse code is crap and treats -128 + * as an illegal value and resets it's protocol state machine + * when it sees this value. + */ +#define CLIP(__X) (((__X) > 127) ? 127 : (((__X) < -127) ? -127 : (__X))) + +/* The following is called from the serial driver when bytes/breaks + * are received on the Mouse line. */ void -sun_mouse_inbyte(unsigned char byte) +sun_mouse_inbyte(unsigned char byte, int is_break) { signed char mvalue; int d, pushed = 0; Firm_event ev; add_mouse_randomness (byte); +#if 0 + { + static int xxx = 0; + printk("mouse(%02x:%d) ", + byte, is_break); + if (byte == 0x87) { + xxx = 0; + printk("\n"); + } + } +#endif + if (mouse_baud_detection(byte, is_break)) + return; + if(!sunmouse.active) return; - mouse_baud_detection(byte); + /* Ignore this if it is garbage. */ + if (sunmouse.byte == 69) { + if (byte != 0x87) + return; - if (!gen_events){ - push_char (byte); - return; + /* Ok, we've begun the state machine. */ + sunmouse.byte = 0; } - +#if 0 /* If the mouse sends us a byte from 0x80 to 0x87 * we are starting at byte zero in the transaction * protocol. */ if(byte >= 0x80 && byte <= 0x87) sunmouse.byte = 0; +#endif mvalue = (signed char) byte; switch(sunmouse.byte) { case 0: + /* If we get a bogus button byte, just skip it. + * When we get here the baud detection code has + * passed, so the only other things which can + * cause this are dropped serial characters and + * confused mouse. We check this because otherwise + * begin posting erroneous mouse events. + */ + if ((byte & 0xf0) != 0x80) + return; + /* Button state */ sunmouse.button_state = (~byte) & 0x7; #ifdef SMOUSE_DEBUG @@ -258,6 +297,7 @@ printk("DX2<%d>", mvalue); #endif sunmouse.delta_x += mvalue; + sunmouse.delta_x = CLIP(sunmouse.delta_x); sunmouse.byte++; return; case 4: @@ -266,7 +306,8 @@ printk("DY2<%d>", mvalue); #endif sunmouse.delta_y += mvalue; - sunmouse.byte = 69; /* Some ridiculous value */ + sunmouse.delta_y = CLIP(sunmouse.delta_y); + sunmouse.byte = 0; /* Back to button state */ break; case 69: /* Until we get the (0x80 -> 0x87) value we aren't @@ -279,6 +320,12 @@ sunmouse.byte = 69; /* What could cause this? */ return; }; + if (!gen_events){ + push_char (~sunmouse.button_state & 0x87); + push_char (sunmouse.delta_x); + push_char (sunmouse.delta_y); + return; + } d = bstate ^ pstate; pstate = bstate; if (d){ @@ -389,6 +436,10 @@ if (current->tss.flags & SPARC_FLAG_32BIT) { Firm_event *q = get_from_queue(); + if ((end - p) < + ((sizeof(Firm_event) - sizeof(struct timeval) + + (sizeof(u32) * 2)))) + break; copy_to_user_ret((Firm_event *)p, q, sizeof(Firm_event)-sizeof(struct timeval), -EFAULT); @@ -400,6 +451,8 @@ } else #endif { + if ((end - p) < sizeof(Firm_event)) + break; copy_to_user_ret((Firm_event *)p, get_from_queue(), sizeof(Firm_event), -EFAULT); p += sizeof (Firm_event); @@ -409,16 +462,25 @@ file->f_dentry->d_inode->i_atime = CURRENT_TIME; return p-buffer; } else { - int c; - - for (c = count; !queue_empty() && c; c--){ - put_user_ret(sunmouse.queue.stream[sunmouse.tail], buffer, -EFAULT); + int c, limit = 3; + + if (count < limit) + limit = count; + for (c = 0; c < limit; c++) { + put_user(sunmouse.queue.stream[sunmouse.tail], buffer); buffer++; sunmouse.tail = (sunmouse.tail + 1) % STREAM_SIZE; } + while (c < count) { + if (c >= 5) + break; + put_user(0, buffer); + buffer++; + c++; + } sunmouse.ready = !queue_empty(); file->f_dentry->d_inode->i_atime = CURRENT_TIME; - return count-c; + return c; } /* Only called if nothing was sent */ if (signal_pending(current)) diff -u --recursive --new-file v2.2.13/linux/drivers/sbus/char/sunmouse.h linux/drivers/sbus/char/sunmouse.h --- v2.2.13/linux/drivers/sbus/char/sunmouse.h Thu Sep 4 12:54:49 1997 +++ linux/drivers/sbus/char/sunmouse.h Tue Jan 4 10:12:19 2000 @@ -1,4 +1,4 @@ -/* $Id: sunmouse.h,v 1.1 1997/08/28 02:23:38 ecd Exp $ +/* $Id: sunmouse.h,v 1.1.6.1 1999/10/14 08:44:33 davem Exp $ * sunmouse.h: Interface to the SUN mouse driver. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -8,6 +8,6 @@ #define _SPARC_SUNMOUSE_H 1 extern void sun_mouse_zsinit(void); -extern void sun_mouse_inbyte(unsigned char); +extern void sun_mouse_inbyte(unsigned char, int); #endif /* !(_SPARC_SUNMOUSE_H) */ diff -u --recursive --new-file v2.2.13/linux/drivers/sbus/char/sunserial.c linux/drivers/sbus/char/sunserial.c --- v2.2.13/linux/drivers/sbus/char/sunserial.c Mon Mar 15 16:11:30 1999 +++ linux/drivers/sbus/char/sunserial.c Tue Jan 4 10:12:19 2000 @@ -1,4 +1,4 @@ -/* $Id: sunserial.c,v 1.68 1998/12/09 18:53:51 davem Exp $ +/* $Id: sunserial.c,v 1.68.2.2 1999/10/04 14:57:02 jj Exp $ * serial.c: Serial port driver infrastructure for the Sparc. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -19,6 +19,7 @@ #include "sunserial.h" int serial_console; +int stop_a_enabled = 1; __initfunc(int con_is_present(void)) { @@ -389,12 +390,10 @@ * So be very careful not to probe for keyboards if we are on a * serial console. */ - if (!serial_console) { - if (ps2kbd_probe(&memory_start) == 0) - return memory_start; - if (su_probe(&memory_start) == 0) - return memory_start; - } + if (!serial_console) + ps2kbd_probe(&memory_start); + if (su_probe(&memory_start) == 0) + return memory_start; #endif if (!ret) diff -u --recursive --new-file v2.2.13/linux/drivers/sbus/char/sunserial.h linux/drivers/sbus/char/sunserial.h --- v2.2.13/linux/drivers/sbus/char/sunserial.h Mon Jan 12 15:15:45 1998 +++ linux/drivers/sbus/char/sunserial.h Tue Jan 4 10:12:19 2000 @@ -1,4 +1,4 @@ -/* $Id: sunserial.h,v 1.17 1997/12/19 07:33:12 ecd Exp $ +/* $Id: sunserial.h,v 1.17.4.1 1999/10/04 14:57:08 jj Exp $ * sunserial.h: SUN serial driver infrastructure (including keyboards). * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -41,6 +41,7 @@ extern void sunkbd_setinitfunc(unsigned long *, int (*) (void)); extern int serial_console; +extern int stop_a_enabled; extern void sunserial_console_termios(struct console *); #ifdef CONFIG_PCI diff -u --recursive --new-file v2.2.13/linux/drivers/sbus/char/zs.c linux/drivers/sbus/char/zs.c --- v2.2.13/linux/drivers/sbus/char/zs.c Tue Jan 4 11:10:37 2000 +++ linux/drivers/sbus/char/zs.c Tue Jan 4 10:12:19 2000 @@ -1,4 +1,4 @@ -/* $Id: zs.c,v 1.41.2.2 1999/09/21 15:50:45 davem Exp $ +/* $Id: zs.c,v 1.41.2.4 1999/10/14 08:44:40 davem Exp $ * zs.c: Zilog serial port driver for the Sparc. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -374,6 +374,8 @@ */ void batten_down_hatches(void) { + if (!stop_a_enabled) + return; /* If we are doing kadb, we call the debugger * else we just drop into the boot monitor. * Note that we must flush the user windows @@ -471,7 +473,7 @@ goto next_char; } if(info->cons_mouse) { - sun_mouse_inbyte(ch); + sun_mouse_inbyte(ch, 0); do_queue_task = 0; goto next_char; } @@ -584,8 +586,12 @@ * 'break asserted' status change interrupt, call * the boot prom. */ - if((status & BRK_ABRT) && info->break_abort) - batten_down_hatches(); + if(status & BRK_ABRT) { + if (info->break_abort) + batten_down_hatches(); + if (info->cons_mouse) + sun_mouse_inbyte(0, 1); + } /* XXX Whee, put in a buffer somewhere, the status information * XXX whee whee whee... Where does the information go... @@ -1849,7 +1855,7 @@ static void show_serial_version(void) { - char *revision = "$Revision: 1.41.2.2 $"; + char *revision = "$Revision: 1.41.2.4 $"; char *version, *p; version = strchr(revision, ' '); diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/advansys.c linux/drivers/scsi/advansys.c --- v2.2.13/linux/drivers/scsi/advansys.c Tue Jan 4 11:10:38 2000 +++ linux/drivers/scsi/advansys.c Tue Jan 4 10:12:19 2000 @@ -1,5 +1,5 @@ -/* $Id: advansys.c,v 1.58 1999/09/03 23:02:16 bobf Exp bobf $ */ -#define ASC_VERSION "3.2F" /* AdvanSys Driver Version */ +/* $Id: advansys.c,v 1.69 1999/11/29 18:37:53 bobf Exp bobf $ */ +#define ASC_VERSION "3.2M" /* AdvanSys Driver Version */ /* * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters @@ -19,7 +19,7 @@ * ftp://ftp.advansys.com/pub/linux/linux.tgz * * Please send questions, comments, bug reports to: - * bobf@advansys.com (Bob Frey) + * linux@advansys.com */ /* @@ -42,13 +42,13 @@ A. Linux Kernel Testing This driver has been tested in the following Linux kernels: v1.2.13, - v1.3.57, v2.0.38, v2.2.12, and v2.3.16. These kernel versions are major + v1.3.57, v2.0.38, v2.2.13, and v2.3.28. These kernel versions are major releases of Linux or the latest Linux kernel versions available when this version of the driver was released. The driver should also work in earlier versions of the Linux kernel. Beginning with v1.3.58 the AdvanSys driver is included with all Linux kernels. Please refer to sections C, D, and E for instructions on adding or upgrading the - AdvanSys driver. + AdvanSys driver. The driver is supported for x86 and alpha systems. B. Adapters Supported by this Driver @@ -103,6 +103,7 @@ ABP980 - Four Channel Bus-Master PCI (240 CDB Per Channel) ABP980U - Four Channel Bus-Master PCI Ultra (240 CDB Per Channel) ABP980UA/3980UA - Four Channel Bus-Master PCI Ultra (16 CDB Per Chan.) + ABP3950U2W - Bus-Master PCI LVD/Ultra2-Wide and Ultra-Wide (253 CDB) C. Linux v1.2.X - Directions for Adding the AdvanSys Driver @@ -634,7 +635,48 @@ 3.2F (9/3/99): 1. Handle new initial function code added in v2.3.16 for all driver versions. - + + 3.2G (9/8/99): + 1. Fix PCI board detection in v2.3.13 and greater kernels. + 2. Fix comiple errors in v2.3.X with debugging enabled. + + 3.2H (9/13/99): + 1. Add 64-bit address, long support for Alpha and UltraSPARC. + The driver has been verified to work on an Alpha system. + 2. Add partial byte order handling support for Power PC and + other big-endian platforms. This support has not yet been + completed or verified. + 3. For wide boards replace block zeroing of request and + scatter-gather structures with individual field initialization + to improve performance. + 4. Correct and clarify ROM BIOS version detection. + + 3.2I (10/8/99): + 1. Update to Adv Library 5.4. + 2. Add v2.3.19 underrun reporting to asc_isr_callback() and + adv_isr_callback(). Remove DID_UNDERRUN constant and other + no longer needed code that previously documented the lack + of underrun handling. + + 3.2J (10/14/99): + 1. Eliminate compile errors for v2.0 and earlier kernels. + + 3.2K (11/15/99): + 1. Correct debug compile error in asc_prt_adv_scsi_req_q(). + 2. Update Adv Library to 5.5. + 3. Add ifdef handling for /proc changes added in v2.3.28. + 4. Increase Wide board scatter-gather list maximum length to + 255 when the driver is compiled into the kernel. + + 3.2L (11/18/99): + 1. Fix bug in adv_get_sglist() that caused an assertion failure + at line 7475. The reqp->sgblkp pointer must be initialized + to NULL in adv_get_sglist(). + + 3.2M (11/29/99): + 1. Really fix bug in adv_get_sglist(). + 2. Incorporate v2.3.29 changes into driver. + J. Known Problems/Fix List (XXX) 1. Need to add memory mapping workaround. Test the memory mapping. @@ -649,7 +691,6 @@ 6. Need to fix sti/cli code in Asc Library. 7. Need to fix abort code in Adv Library. 8. Reduce io_request_lock hold time. - 9. Add big-endian support for Alpha. K. Credits @@ -673,6 +714,9 @@ Doug Gilbert has made changes and suggestions to improve the driver and done testing. + Ken Mort reported a DEBUG compile bug fixed + in 3.2K. + L. AdvanSys Contact Information Mail: Advanced System Products, Inc. @@ -736,7 +780,9 @@ #include #include #endif /* version >= v1.3.0 */ -#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,95) +#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,3,18) +#include +#elif LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,95) #include #endif /* version >= 2.1.95 */ #include "scsi.h" @@ -756,12 +802,12 @@ #define ASC_CONFIG_PCI #endif /* version < v2.1.93 */ -/* - * If Linux eventually defines a DID_UNDERRUN, the constant here can be - * removed. The current value of zero for DID_UNDERRUN results in underrun - * conditions being ignored. - */ -#define DID_UNDERRUN 0 +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,0) +#define cpu_to_le16(word) (word) +#define le16_to_cpu(word) (word) +#define cpu_to_le32(dword) (dword) +#define le32_to_cpu(dword) (dword) +#endif /* version < v2.1.0 */ /* @@ -804,6 +850,31 @@ #define ASC_LIB_VERSION_MINOR 24 #define ASC_LIB_SERIAL_NUMBER 121 +/* + * Portable Data Types + * + * Any instance where a 32-bit long or pointer type is assumed + * for precision or HW defined structures, the following define + * types must be used. In Linux the char, short, and int types + * are all consistent at 8, 16, and 32 bits respectively. Pointers + * and long types are 64 bits on Alpha and UltraSPARC. + */ +#define ASC_PADDR __u32 /* Physical/Bus address data type. */ +#define ASC_VADDR __u32 /* Virtual address data type. */ +#define ASC_DCNT __u32 /* Unsigned Data count type. */ +#define ASC_SDCNT __s32 /* Signed Data count type. */ + +/* + * These macros are used to convert a virtual address to a + * 32-bit value. This currently can be used on Linux Alpha + * which uses 64-bit virtual address but a 32-bit bus address. + * This is likely to break in the future, but doing this now + * will give us time to change the HW and FW to handle 64-bit + * addresses. + */ +#define ASC_VADDR_TO_U32 virt_to_bus +#define ASC_U32_TO_VADDR bus_to_virt + typedef unsigned char uchar; #ifndef NULL @@ -815,55 +886,11 @@ #ifndef FALSE #define FALSE (0) #endif -#define REG register -#define rchar REG __s8 -#define rshort REG __s16 -#define rint REG __s32 -#define rlong REG __s32 -#define ruchar REG __u8 -#define rushort REG __u16 -#define ruint REG __u32 -#define rulong REG __u32 -#define NULLPTR (void *)0 -#define FNULLPTR (void *)0UL + #define EOF (-1) -#define EOS '\0' #define ERR (-1) -#define UB_ERR (uchar)(0xFF) #define UW_ERR (uint)(0xFFFF) -#define UL_ERR (ulong)(0xFFFFFFFFUL) -#define iseven_word(val) ((((uint)val) & (uint)0x0001) == 0) #define isodd_word(val) ((((uint)val) & (uint)0x0001) != 0) -#define toeven_word(val) (((uint)val) & (uint)0xFFFE) -#define biton(val, bits) (((uint)(val >> bits) & (uint)0x0001) != 0) -#define bitoff(val, bits) (((uint)(val >> bits) & (uint)0x0001) == 0) -#define lbiton(val, bits) (((ulong)(val >> bits) & (ulong)0x00000001UL) != 0) -#define lbitoff(val, bits) (((ulong)(val >> bits) & (ulong)0x00000001UL) == 0) -#define absh(val) ((val) < 0 ? -(val) : (val)) -#define swapbyte(ch) ((((ch) << 4) | ((ch) >> 4))) -#ifndef GBYTE -#define GBYTE (0x40000000UL) -#endif -#ifndef MBYTE -#define MBYTE (0x100000UL) -#endif -#ifndef KBYTE -#define KBYTE (0x400) -#endif -#define HI_BYTE(x) (*((__u8 *)(&x)+1)) -#define LO_BYTE(x) (*((__u8 *)&x)) -#define HI_WORD(x) (*((__u16 *)(&x)+1)) -#define LO_WORD(x) (*((__u16 *)&x)) -#ifndef MAKEWORD -#define MAKEWORD(lo, hi) ((__u16) (((__u16) lo) | ((__u16) hi << 8))) -#endif -#ifndef MAKELONG -#define MAKELONG(lo, hi) ((__u32) (((__u32) lo) | ((__u32) hi << 16))) -#endif -#define SwapWords(dWord) ((__u32) ((dWord >> 16) | (dWord << 16))) -#define SwapBytes(word) ((__u16) ((word >> 8) | (word << 8))) -#define BigToLittle(dWord) ((__u32) (SwapWords(MAKELONG(SwapBytes(LO_WORD(dWord)), SwapBytes(HI_WORD(dWord)))))) -#define LittleToBig(dWord) BigToLittle(dWord) #define AscPCIConfigVendorIDRegister 0x0000 #define AscPCIConfigDeviceIDRegister 0x0002 #define AscPCIConfigCommandRegister 0x0004 @@ -898,25 +925,17 @@ #define ASC_SRB2SCSIQ(srb_ptr) (srb_ptr) #define PortAddr unsigned short /* port address size */ -#define Ptr2Func ulong #define inp(port) inb(port) #define inpw(port) inw(port) #define inpl(port) inl(port) #define outp(port, byte) outb((byte), (port)) #define outpw(port, word) outw((word), (port)) -#define outpl(port, long) outl((long), (port)) +#define outpl(port, dword) outl((dword), (port)) #define ASC_MAX_SG_QUEUE 7 -#define ASC_MAX_SG_LIST SG_ALL +#define ASC_MAX_SG_LIST 255 #define ASC_CS_TYPE unsigned short -#ifndef asc_ptr_type -#define asc_ptr_type -#endif -#ifndef ASC_GET_PTR2FUNC -#define ASC_GET_PTR2FUNC(fun) (Ptr2Func)(fun) -#endif -#define FLIP_BYTE_NIBBLE(x) (((x<<4)& 0xFF) | (x>>4)) #define ASC_IS_ISA (0x0001) #define ASC_IS_ISAPNP (0x0081) #define ASC_IS_EISA (0x0002) @@ -967,34 +986,10 @@ #define ASC_SCSI_ID_BITS 3 #define ASC_SCSI_TIX_TYPE uchar #define ASC_ALL_DEVICE_BIT_SET 0xFF -#ifdef ASC_WIDESCSI_16 -#undef ASC_SCSI_ID_BITS -#define ASC_SCSI_ID_BITS 4 -#define ASC_ALL_DEVICE_BIT_SET 0xFFFF -#endif -#ifdef ASC_WIDESCSI_32 -#undef ASC_SCSI_ID_BITS -#define ASC_SCSI_ID_BITS 5 -#define ASC_ALL_DEVICE_BIT_SET 0xFFFFFFFFL -#endif -#if ASC_SCSI_ID_BITS == 3 #define ASC_SCSI_BIT_ID_TYPE uchar #define ASC_MAX_TID 7 #define ASC_MAX_LUN 7 #define ASC_SCSI_WIDTH_BIT_SET 0xFF -#elif ASC_SCSI_ID_BITS == 4 -#define ASC_SCSI_BIT_ID_TYPE ushort -#define ASC_MAX_TID 15 -#define ASC_MAX_LUN 7 -#define ASC_SCSI_WIDTH_BIT_SET 0xFFFF -#elif ASC_SCSI_ID_BITS == 5 -#define ASC_SCSI_BIT_ID_TYPE ulong -#define ASC_MAX_TID 31 -#define ASC_MAX_LUN 7 -#define ASC_SCSI_WIDTH_BIT_SET 0xFFFFFFFF -#else -#error ASC_SCSI_ID_BITS definition is wrong -#endif #define ASC_MAX_SENSE_LEN 32 #define ASC_MIN_SENSE_LEN 14 #define ASC_MAX_CDB_LEN 12 @@ -1298,15 +1293,15 @@ uchar sg_queue_cnt; uchar target_id; uchar target_lun; - ulong data_addr; - ulong data_cnt; - ulong sense_addr; + ASC_PADDR data_addr; + ASC_DCNT data_cnt; + ASC_PADDR sense_addr; uchar sense_len; uchar extra_bytes; } ASC_SCSIQ_1; typedef struct asc_scisq_2 { - ulong srb_ptr; + ASC_VADDR srb_ptr; uchar target_ix; uchar flag; uchar cdb_len; @@ -1329,8 +1324,8 @@ uchar y_res; ushort x_req_count; ushort x_reconnect_rtn; - ulong x_saved_data_addr; - ulong x_saved_data_cnt; + ASC_PADDR x_saved_data_addr; + ASC_DCNT x_saved_data_cnt; } ASC_SCSIQ_4; typedef struct asc_q_done_info { @@ -1342,12 +1337,12 @@ uchar sense_len; uchar extra_bytes; uchar res; - ulong remain_bytes; + ASC_DCNT remain_bytes; } ASC_QDONE_INFO; typedef struct asc_sg_list { - ulong addr; - ulong bytes; + ASC_PADDR addr; + ASC_DCNT bytes; } ASC_SG_LIST; typedef struct asc_sg_head { @@ -1621,14 +1616,19 @@ #define ASC_MIN_TAGGED_CMD 7 #define ASC_MAX_SCSI_RESET_WAIT 30 +struct asc_dvc_var; /* Forward Declaration. */ + +typedef void (* ASC_ISR_CALLBACK)(struct asc_dvc_var *, ASC_QDONE_INFO *); +typedef int (* ASC_EXE_CALLBACK)(struct asc_dvc_var *, ASC_SCSI_Q *); + typedef struct asc_dvc_var { PortAddr iop_base; ushort err_code; ushort dvc_cntl; ushort bug_fix_cntl; ushort bus_type; - Ptr2Func isr_callback; - Ptr2Func exe_callback; + ASC_ISR_CALLBACK isr_callback; + ASC_EXE_CALLBACK exe_callback; ASC_SCSI_BIT_ID_TYPE init_sdtr; ASC_SCSI_BIT_ID_TYPE sdtr_done; ASC_SCSI_BIT_ID_TYPE use_tagged_qng; @@ -1650,32 +1650,26 @@ ASC_SCSI_Q *scsiq_busy_tail[ASC_MAX_TID + 1]; uchar sdtr_period_tbl[ASC_MAX_SYN_XFER_NO]; ASC_DVC_CFG *cfg; - Ptr2Func saved_ptr2func; ASC_SCSI_BIT_ID_TYPE pci_fix_asyn_xfer_always; char redo_scam; ushort res2; uchar dos_int13_table[ASC_MAX_TID + 1]; - ulong max_dma_count; + ASC_DCNT max_dma_count; ASC_SCSI_BIT_ID_TYPE no_scam; ASC_SCSI_BIT_ID_TYPE pci_fix_asyn_xfer; uchar max_sdtr_index; uchar host_init_sdtr_index; - ulong drv_ptr; - ulong uc_break; - ulong res7; - ulong res8; + struct asc_board *drv_ptr; + ASC_DCNT uc_break; } ASC_DVC_VAR; -typedef int (* ASC_ISR_CALLBACK) (ASC_DVC_VAR asc_ptr_type *, ASC_QDONE_INFO *); -typedef int (* ASC_EXE_CALLBACK) (ASC_DVC_VAR asc_ptr_type *, ASC_SCSI_Q *); - typedef struct asc_dvc_inq_info { uchar type[ASC_MAX_TID + 1][ASC_MAX_LUN + 1]; } ASC_DVC_INQ_INFO; typedef struct asc_cap_info { - ulong lba; - ulong blk_size; + ASC_DCNT lba; + ASC_DCNT blk_size; } ASC_CAP_INFO; typedef struct asc_cap_info_array { @@ -2033,29 +2027,29 @@ STATIC uchar AscGetIsaDmaSpeed(PortAddr); STATIC uchar AscReadLramByte(PortAddr, ushort); STATIC ushort AscReadLramWord(PortAddr, ushort); -STATIC ulong AscReadLramDWord(PortAddr, ushort); +STATIC ASC_DCNT AscReadLramDWord(PortAddr, ushort); STATIC void AscWriteLramWord(PortAddr, ushort, ushort); -STATIC void AscWriteLramDWord(PortAddr, ushort, ulong); +STATIC void AscWriteLramDWord(PortAddr, ushort, ASC_DCNT); STATIC void AscWriteLramByte(PortAddr, ushort, uchar); -STATIC ulong AscMemSumLramWord(PortAddr, ushort, rint); -STATIC void AscMemWordSetLram(PortAddr, ushort, ushort, rint); +STATIC ASC_DCNT AscMemSumLramWord(PortAddr, ushort, int); +STATIC void AscMemWordSetLram(PortAddr, ushort, ushort, int); STATIC void AscMemWordCopyToLram(PortAddr, ushort, ushort *, int); -STATIC void AscMemDWordCopyToLram(PortAddr, ushort, ulong *, int); +STATIC void AscMemDWordCopyToLram(PortAddr, ushort, ASC_DCNT *, int); STATIC void AscMemWordCopyFromLram(PortAddr, ushort, ushort *, int); -STATIC ushort AscInitAscDvcVar(ASC_DVC_VAR asc_ptr_type *); -STATIC ushort AscInitFromEEP(ASC_DVC_VAR asc_ptr_type *); -STATIC ushort AscInitFromAscDvcVar(ASC_DVC_VAR asc_ptr_type *); -STATIC ushort AscInitMicroCodeVar(ASC_DVC_VAR asc_ptr_type * asc_dvc); -STATIC int AscTestExternalLram(ASC_DVC_VAR asc_ptr_type *); -STATIC uchar AscMsgOutSDTR(ASC_DVC_VAR asc_ptr_type *, uchar, uchar); -STATIC uchar AscCalSDTRData(ASC_DVC_VAR asc_ptr_type *, uchar, uchar); +STATIC ushort AscInitAscDvcVar(ASC_DVC_VAR *); +STATIC ushort AscInitFromEEP(ASC_DVC_VAR *); +STATIC ushort AscInitFromAscDvcVar(ASC_DVC_VAR *); +STATIC ushort AscInitMicroCodeVar(ASC_DVC_VAR *); +STATIC int AscTestExternalLram(ASC_DVC_VAR *); +STATIC uchar AscMsgOutSDTR(ASC_DVC_VAR *, uchar, uchar); +STATIC uchar AscCalSDTRData(ASC_DVC_VAR *, uchar, uchar); STATIC void AscSetChipSDTR(PortAddr, uchar, uchar); -STATIC uchar AscGetSynPeriodIndex(ASC_DVC_VAR asc_ptr_type *, ruchar); +STATIC uchar AscGetSynPeriodIndex(ASC_DVC_VAR *, uchar); STATIC uchar AscAllocFreeQueue(PortAddr, uchar); STATIC uchar AscAllocMultipleFreeQueue(PortAddr, uchar, uchar); -STATIC int AscRiscHaltedAbortSRB(ASC_DVC_VAR asc_ptr_type *, ulong); +STATIC int AscRiscHaltedAbortSRB(ASC_DVC_VAR *, ASC_DCNT); #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89) -STATIC int AscRiscHaltedAbortTIX(ASC_DVC_VAR asc_ptr_type *, uchar); +STATIC int AscRiscHaltedAbortTIX(ASC_DVC_VAR *, uchar); #endif /* version >= v1.3.89 */ STATIC int AscHostReqRiscHalt(PortAddr); STATIC int AscStopQueueExe(PortAddr); @@ -2064,39 +2058,39 @@ STATIC int AscCleanUpDiscQueue(PortAddr); #endif /* version >= v1.3.89 */ STATIC int AscCleanUpBusyQueue(PortAddr); -STATIC int AscWaitTixISRDone(ASC_DVC_VAR asc_ptr_type *, uchar); -STATIC int AscWaitISRDone(ASC_DVC_VAR asc_ptr_type *); -STATIC ulong AscGetOnePhyAddr(ASC_DVC_VAR asc_ptr_type *, uchar *, - ulong); -STATIC int AscSendScsiQueue(ASC_DVC_VAR asc_ptr_type * asc_dvc, +STATIC int AscWaitTixISRDone(ASC_DVC_VAR *, uchar); +STATIC int AscWaitISRDone(ASC_DVC_VAR *); +STATIC ASC_PADDR AscGetOnePhyAddr(ASC_DVC_VAR *, uchar *, + ASC_DCNT); +STATIC int AscSendScsiQueue(ASC_DVC_VAR *, ASC_SCSI_Q * scsiq, uchar n_q_required); -STATIC int AscPutReadyQueue(ASC_DVC_VAR asc_ptr_type *, +STATIC int AscPutReadyQueue(ASC_DVC_VAR *, ASC_SCSI_Q *, uchar); -STATIC int AscPutReadySgListQueue(ASC_DVC_VAR asc_ptr_type *, +STATIC int AscPutReadySgListQueue(ASC_DVC_VAR *, ASC_SCSI_Q *, uchar); STATIC int AscSetChipSynRegAtID(PortAddr, uchar, uchar); STATIC int AscSetRunChipSynRegAtID(PortAddr, uchar, uchar); -STATIC ushort AscInitLram(ASC_DVC_VAR asc_ptr_type *); -STATIC int AscReInitLram(ASC_DVC_VAR asc_ptr_type *); -STATIC ushort AscInitQLinkVar(ASC_DVC_VAR asc_ptr_type *); -STATIC int AscSetLibErrorCode(ASC_DVC_VAR asc_ptr_type *, ushort); +STATIC ushort AscInitLram(ASC_DVC_VAR *); +STATIC int AscReInitLram(ASC_DVC_VAR *); +STATIC ushort AscInitQLinkVar(ASC_DVC_VAR *); +STATIC int AscSetLibErrorCode(ASC_DVC_VAR *, ushort); #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89) STATIC int _AscWaitQDone(PortAddr, ASC_SCSI_Q *); #endif /* version >= v1.3.89 */ -STATIC int AscIsrChipHalted(ASC_DVC_VAR asc_ptr_type *); +STATIC int AscIsrChipHalted(ASC_DVC_VAR *); STATIC uchar _AscCopyLramScsiDoneQ(PortAddr, ushort, - ASC_QDONE_INFO *, ulong); -STATIC int AscIsrQDone(ASC_DVC_VAR asc_ptr_type *); + ASC_QDONE_INFO *, ASC_DCNT); +STATIC int AscIsrQDone(ASC_DVC_VAR *); STATIC int AscCompareString(uchar *, uchar *, int); STATIC ushort AscGetEisaChipCfg(PortAddr); -STATIC ulong AscGetEisaProductID(PortAddr); +STATIC ASC_DCNT AscGetEisaProductID(PortAddr); STATIC PortAddr AscSearchIOPortAddrEISA(PortAddr); STATIC uchar AscGetChipScsiCtrl(PortAddr); STATIC uchar AscSetChipScsiID(PortAddr, uchar); STATIC uchar AscGetChipVersion(PortAddr, ushort); STATIC ushort AscGetChipBusType(PortAddr); -STATIC ulong AscLoadMicroCode(PortAddr, ushort, ushort *, ushort); +STATIC ASC_DCNT AscLoadMicroCode(PortAddr, ushort, ushort *, ushort); STATIC int AscFindSignature(PortAddr); STATIC PortAddr AscSearchIOPortAddr11(PortAddr); STATIC void AscToggleIRQAct(PortAddr); @@ -2108,38 +2102,38 @@ STATIC void DvcLeaveCritical(int); STATIC void DvcInPortWords(PortAddr, ushort *, int); STATIC void DvcOutPortWords(PortAddr, ushort *, int); -STATIC void DvcOutPortDWords(PortAddr, ulong *, int); -STATIC uchar DvcReadPCIConfigByte(ASC_DVC_VAR asc_ptr_type *, ushort); -STATIC void DvcWritePCIConfigByte(ASC_DVC_VAR asc_ptr_type *, +STATIC void DvcOutPortDWords(PortAddr, ASC_DCNT *, int); +STATIC uchar DvcReadPCIConfigByte(ASC_DVC_VAR *, ushort); +STATIC void DvcWritePCIConfigByte(ASC_DVC_VAR *, ushort, uchar); STATIC ushort AscGetChipBiosAddress(PortAddr, ushort); -STATIC void DvcSleepMilliSecond(ulong); -STATIC void DvcDelayNanoSecond(ASC_DVC_VAR asc_ptr_type *, ulong); -STATIC ulong DvcGetSGList(ASC_DVC_VAR asc_ptr_type *, uchar *, - ulong, ASC_SG_HEAD *); +STATIC void DvcSleepMilliSecond(ASC_DCNT); +STATIC void DvcDelayNanoSecond(ASC_DVC_VAR *, ASC_DCNT); +STATIC ASC_DCNT DvcGetSGList(ASC_DVC_VAR *, uchar *, + ASC_DCNT, ASC_SG_HEAD *); STATIC void DvcPutScsiQ(PortAddr, ushort, ushort *, int); STATIC void DvcGetQinfo(PortAddr, ushort, ushort *, int); STATIC PortAddr AscSearchIOPortAddr(PortAddr, ushort); -STATIC ushort AscInitGetConfig(ASC_DVC_VAR asc_ptr_type *); -STATIC ushort AscInitSetConfig(ASC_DVC_VAR asc_ptr_type *); -STATIC ushort AscInitAsc1000Driver(ASC_DVC_VAR asc_ptr_type *); -STATIC void AscAsyncFix(ASC_DVC_VAR asc_ptr_type *, uchar, +STATIC ushort AscInitGetConfig(ASC_DVC_VAR *); +STATIC ushort AscInitSetConfig(ASC_DVC_VAR *); +STATIC ushort AscInitAsc1000Driver(ASC_DVC_VAR *); +STATIC void AscAsyncFix(ASC_DVC_VAR *, uchar, ASC_SCSI_INQUIRY *); STATIC int AscTagQueuingSafe(ASC_SCSI_INQUIRY *); -STATIC void AscInquiryHandling(ASC_DVC_VAR asc_ptr_type *, +STATIC void AscInquiryHandling(ASC_DVC_VAR *, uchar, ASC_SCSI_INQUIRY *); -STATIC int AscExeScsiQueue(ASC_DVC_VAR asc_ptr_type *, ASC_SCSI_Q *); -STATIC int AscISR(ASC_DVC_VAR asc_ptr_type *); -STATIC uint AscGetNumOfFreeQueue(ASC_DVC_VAR asc_ptr_type *, uchar, +STATIC int AscExeScsiQueue(ASC_DVC_VAR *, ASC_SCSI_Q *); +STATIC int AscISR(ASC_DVC_VAR *); +STATIC uint AscGetNumOfFreeQueue(ASC_DVC_VAR *, uchar, uchar); STATIC int AscSgListToQueue(int); -STATIC int AscAbortSRB(ASC_DVC_VAR asc_ptr_type *, ulong); +STATIC int AscAbortSRB(ASC_DVC_VAR *, ASC_VADDR); #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89) -STATIC int AscResetDevice(ASC_DVC_VAR asc_ptr_type *, uchar); +STATIC int AscResetDevice(ASC_DVC_VAR *, uchar); #endif /* version >= v1.3.89 */ -STATIC int AscResetSB(ASC_DVC_VAR asc_ptr_type *); +STATIC int AscResetSB(ASC_DVC_VAR *); STATIC void AscEnableIsaDma(uchar); -STATIC ulong AscGetMaxDmaCount(ushort); +STATIC ASC_DCNT AscGetMaxDmaCount(ushort); /* @@ -2147,7 +2141,7 @@ */ #define ADV_LIB_VERSION_MAJOR 5 -#define ADV_LIB_VERSION_MINOR 2 +#define ADV_LIB_VERSION_MINOR 5 /* d_os_dep.h */ #define ADV_OS_LINUX @@ -2155,10 +2149,36 @@ /* * Define Adv Library required special types. */ + +/* + * Portable Data Types + * + * Any instance where a 32-bit long or pointer type is assumed + * for precision or HW defined structures, the following define + * types must be used. In Linux the char, short, and int types + * are all consistent at 8, 16, and 32 bits respectively. Pointers + * and long types are 64 bits on Alpha and UltraSPARC. + */ +#define ADV_PADDR __u32 /* Physical address data type. */ +#define ADV_VADDR __u32 /* Virtual address data type. */ +#define ADV_DCNT __u32 /* Unsigned Data count type. */ +#define ADV_SDCNT __s32 /* Signed Data count type. */ + +/* + * These macros are used to convert a virtual address to a + * 32-bit value. This currently can be used on Linux Alpha + * which uses 64-bit virtual address but a 32-bit bus address. + * This is likely to break in the future, but doing this now + * will give us time to change the HW and FW to handle 64-bit + * addresses. + */ +#define ADV_VADDR_TO_U32 virt_to_bus +#define ADV_U32_TO_VADDR bus_to_virt + #if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,0) #define AdvPortAddr unsigned short /* I/O Port address size */ #else /* version >= v1,3,0 */ -#define AdvPortAddr unsigned long /* Virtual memory address size */ +#define AdvPortAddr ulong /* Virtual memory address size */ #endif /* version >= v1,3,0 */ /* @@ -2181,15 +2201,21 @@ /* * Define total number of simultaneous maximum element scatter-gather - * requests, i.e. ADV_TOT_SG_LIST * ADV_MAX_SG_LIST is the total number - * of simultaneous scatter-gather elements supported per wide adapter. + * request blocks per wide adapter. ASC_DEF_MAX_HOST_QNG (253) is the + * maximum number of outstanding commands per wide host adapter. Each + * command uses one or more ADV_SG_BLOCK each with 15 scatter-gather + * elements. Allow each command to have at least one ADV_SG_BLOCK structure. + * This allows about 15 commands to have the maximum 17 ADV_SG_BLOCK + * structures or 255 scatter-gather elements. + * */ -#define ADV_TOT_SG_LIST 64 +#define ADV_TOT_SG_BLOCK ASC_DEF_MAX_HOST_QNG /* - * Define Adv Library required per request scatter-gather element limit. + * Define Adv Library required maximum number of scatter-gather + * elements per request. */ -#define ADV_MAX_SG_LIST 64 +#define ADV_MAX_SG_LIST 255 /* Number of SG blocks needed. */ #define ADV_NUM_SG_BLOCK \ @@ -2204,8 +2230,6 @@ #define ADV_NUM_PAGE_CROSSING \ ((ADV_SG_TOTAL_MEM_SIZE + (ASC_PAGE_SIZE - 1))/ASC_PAGE_SIZE) -#define ADV_ASSERT(a) ASC_ASSERT(a) - /* a_condor.h */ #define ADV_PCI_VENDOR_ID 0x10CD #define ADV_PCI_DEVICE_ID_REV_A 0x2300 @@ -2221,8 +2245,13 @@ #define ADV_EEPROM_BIG_ENDIAN 0x8000 /* EEPROM Bit 15 */ #define ADV_EEPROM_BIOS_ENABLE 0x4000 /* EEPROM Bit 14 */ +/* + * For the ASC3550 Bit 13 is Termination Polarity control bit. + * For later ICs Bit 13 controls whether the CIS (Card Information + * Service Section) is loaded from EEPROM. + */ #define ADV_EEPROM_TERM_POL 0x2000 /* EEPROM Bit 13 */ -#define ADV_EEPROM_CIS_LD 0x1000 /* EEPROM Bit 12 */ +#define ADV_EEPROM_CIS_LD 0x2000 /* EEPROM Bit 13 */ typedef struct adveep_3550_config { @@ -2299,8 +2328,7 @@ /* Word Offset, Description */ ushort cfg_lsw; /* 00 power up initialization */ - /* bit 12 set - CIS Load */ - /* bit 13 set - Term Polarity Control */ + /* bit 13 set - Load CIS */ /* bit 14 set - BIOS Enable */ /* bit 15 set - Big Endian Mode */ ushort cfg_msw; /* 01 unused */ @@ -2448,12 +2476,12 @@ #define IOPB_SOFT_OVER_WR 0x0E #define IOPB_RES_ADDR_F 0x0F #define IOPB_MEM_CFG 0x10 -#define IOPB_GPIO_CNTL 0x11 +#define IOPB_RES_ADDR_11 0x11 #define IOPB_GPIO_DATA 0x12 #define IOPB_RES_ADDR_13 0x13 #define IOPB_FLASH_PAGE 0x14 #define IOPB_RES_ADDR_15 0x15 -#define IOPB_RES_ADDR_16 0x16 +#define IOPB_GPIO_CNTL 0x16 #define IOPB_RES_ADDR_17 0x17 #define IOPB_FLASH_DATA 0x18 #define IOPB_RES_ADDR_19 0x19 @@ -2489,7 +2517,7 @@ #define IOPB_RES_ADDR_37 0x37 #define IOPB_RAM_BIST 0x38 #define IOPB_PLL_TEST 0x39 -#define IOPB_RES_ADDR_3A 0x3A +#define IOPB_PCI_INT_CFG 0x3A #define IOPB_RES_ADDR_3B 0x3B #define IOPB_RFIFO_CNT 0x3C #define IOPB_RES_ADDR_3D 0x3D @@ -2850,16 +2878,16 @@ typedef struct adv_carr_t { - ulong carr_va; /* Carrier Virtual Address */ - ulong carr_pa; /* Carrier Physical Address */ - ulong areq_vpa; /* ASC_SCSI_REQ_Q Virtual or Physical Address */ + ADV_VADDR carr_va; /* Carrier Virtual Address */ + ADV_PADDR carr_pa; /* Carrier Physical Address */ + ADV_VADDR areq_vpa; /* ASC_SCSI_REQ_Q Virtual or Physical Address */ /* * next_vpa [31:4] Carrier Virtual or Physical Next Pointer * * next_vpa [3:1] Reserved Bits * next_vpa [0] Done Flag set in Response Queue. */ - ulong next_vpa; + ADV_VADDR next_vpa; } ADV_CARR_T; /* @@ -2870,7 +2898,7 @@ #define ASC_RQ_DONE 0x00000001 #define ASC_CQ_STOPPER 0x00000000 -#define ASC_GET_CARRP(carrp) ((ADV_CARR_T *) ((carrp) & ASC_NEXT_VPA_MASK)) +#define ASC_GET_CARRP(carrp) ((carrp) & ASC_NEXT_VPA_MASK) #define ADV_PAGE_SIZE 4096 /* Assume 4KB page size. */ @@ -2965,7 +2993,7 @@ uchar max_host_qng; /* maximum number of Q'ed command allowed */ uchar irq_no; /* IRQ number */ ushort no_scam; /* scam_tolerant of EEPROM */ - ulong drv_ptr; /* driver pointer to private structure */ + struct asc_board *drv_ptr; /* driver pointer to private structure */ uchar chip_scsi_id; /* chip SCSI target ID */ uchar chip_type; uchar bist_err_code; @@ -2988,10 +3016,10 @@ uchar reserved2; uchar reserved3; uchar sg_cnt; /* Valid entries in block. */ - struct asc_sg_block *sg_ptr; /* links to the next sg block */ + ADV_PADDR sg_ptr; /* Pointer to next sg block. */ struct { - ulong sg_addr; /* SG element address. */ - ulong sg_count; /* SG element count. */ + ADV_PADDR sg_addr; /* SG element address. */ + ADV_DCNT sg_count; /* SG element count. */ } sg_list[NO_OF_SG_PER_BLOCK]; } ADV_SG_BLOCK; @@ -3005,13 +3033,13 @@ */ typedef struct adv_scsi_req_q { uchar cntl; /* Ucode flags and state (ASC_MC_QC_*). */ - uchar reserved; + uchar target_cmd; uchar target_id; /* Device target identifier. */ uchar target_lun; /* Device target logical unit number. */ - ulong data_addr; /* Data buffer physical address. */ - ulong data_cnt; /* Data count. Ucode sets to residual. */ - ulong sense_addr; - ulong carr_pa; + ADV_PADDR data_addr; /* Data buffer physical address. */ + ADV_DCNT data_cnt; /* Data count. Ucode sets to residual. */ + ADV_PADDR sense_addr; + ADV_PADDR carr_pa; uchar mflag; uchar sense_len; uchar cdb_len; /* SCSI CDB length. */ @@ -3021,18 +3049,18 @@ uchar host_status; /* Ucode host status. */ uchar sg_working_ix; uchar cdb[12]; /* SCSI command block. */ - ulong sg_real_addr; /* SG list physical address. */ - ulong scsiq_rptr; - ulong sg_working_data_cnt; - struct adv_scsi_req_q *scsiq_ptr; - ulong carr_va; + ADV_PADDR sg_real_addr; /* SG list physical address. */ + ADV_PADDR scsiq_rptr; + ADV_DCNT sg_working_data_cnt; + ADV_VADDR scsiq_ptr; + ADV_VADDR carr_va; /* * End of microcode structure - 60 bytes. The rest of the structure * is used by the Adv Library and ignored by the microcode. */ - ulong srb_ptr; + ADV_VADDR srb_ptr; ADV_SG_BLOCK *sg_list_ptr; /* SG list virtual address. */ - ulong vdata_addr; /* Data buffer virtual address. */ + char *vdata_addr; /* Data buffer virtual address. */ uchar a_flag; } ADV_SCSI_REQ_Q; @@ -3077,18 +3105,17 @@ */ STATIC int DvcEnterCritical(void); STATIC void DvcLeaveCritical(int); -STATIC void DvcSleepMilliSecond(ulong); +STATIC void DvcSleepMilliSecond(ADV_DCNT); STATIC uchar DvcAdvReadPCIConfigByte(ADV_DVC_VAR *, ushort); STATIC void DvcAdvWritePCIConfigByte(ADV_DVC_VAR *, ushort, uchar); -STATIC ulong DvcGetPhyAddr(ADV_DVC_VAR *, ADV_SCSI_REQ_Q *, - uchar *, long *, int); +STATIC ADV_PADDR DvcGetPhyAddr(ADV_DVC_VAR *, ADV_SCSI_REQ_Q *, + uchar *, ASC_SDCNT *, int); STATIC void DvcDelayMicroSecond(ADV_DVC_VAR *, ushort); /* * Adv Library functions available to drivers. */ -STATIC int AdvExeScsiQueue(ADV_DVC_VAR *, - ADV_SCSI_REQ_Q *); +STATIC int AdvExeScsiQueue(ADV_DVC_VAR *, ADV_SCSI_REQ_Q *); STATIC int AdvISR(ADV_DVC_VAR *); STATIC int AdvInitGetConfig(ADV_DVC_VAR *); STATIC int AdvInitAsc3550Driver(ADV_DVC_VAR *); @@ -3099,7 +3126,7 @@ /* * Internal Adv Library functions. */ -STATIC int AdvSendIdleCmd(ADV_DVC_VAR *, ushort, ulong); +STATIC int AdvSendIdleCmd(ADV_DVC_VAR *, ushort, ADV_DCNT); STATIC void AdvInquiryHandling(ADV_DVC_VAR *, ADV_SCSI_REQ_Q *); STATIC int AdvInitFrom3550EEP(ADV_DVC_VAR *); STATIC int AdvInitFrom38C0800EEP(ADV_DVC_VAR *); @@ -3119,16 +3146,20 @@ #if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,0) /* Read byte from a register. */ -#define AdvReadByteRegister(iop_base, reg_off) (inp((iop_base) + (reg_off))) +#define AdvReadByteRegister(iop_base, reg_off) \ + (inp((iop_base) + (reg_off))) /* Write byte to a register. */ -#define AdvWriteByteRegister(iop_base, reg_off, byte) (outp((iop_base) + (reg_off), (byte))) +#define AdvWriteByteRegister(iop_base, reg_off, byte) \ + (outp((iop_base) + (reg_off), (byte))) /* Read word (2 bytes) from a register. */ -#define AdvReadWordRegister(iop_base, reg_off) (inpw((iop_base) + (reg_off))) +#define AdvReadWordRegister(iop_base, reg_off) \ + (le16_to_cpu(inpw((iop_base) + (reg_off)))) /* Write word (2 bytes) to a register. */ -#define AdvWriteWordRegister(iop_base, reg_off, word) (outpw((iop_base) + (reg_off), (word))) +#define AdvWriteWordRegister(iop_base, reg_off, word) \ + (outpw((iop_base) + (reg_off), cpu_to_le16(word))) /* Read byte from LRAM. */ #define AdvReadByteLram(iop_base, addr, byte) \ @@ -3146,29 +3177,31 @@ #define AdvReadWordLram(iop_base, addr, word) \ do { \ outpw((iop_base) + IOPW_RAM_ADDR, (addr)); \ - (word) = inpw((iop_base) + IOPW_RAM_DATA); \ + (word) = le16_to_cpu(inpw((iop_base) + IOPW_RAM_DATA)); \ } while (0) /* Write word (2 bytes) to LRAM. */ #define AdvWriteWordLram(iop_base, addr, word) \ (outpw((iop_base) + IOPW_RAM_ADDR, (addr)), \ - outpw((iop_base) + IOPW_RAM_DATA, (word))) + outpw((iop_base) + IOPW_RAM_DATA, cpu_to_le16(word))) /* Write double word (4 bytes) to LRAM */ /* Because of unspecified C language ordering don't use auto-increment. */ #define AdvWriteDWordLram(iop_base, addr, dword) \ ((outpw((iop_base) + IOPW_RAM_ADDR, (addr)), \ - outpw((iop_base) + IOPW_RAM_DATA, (ushort) ((dword) & 0xFFFF))), \ + outpw((iop_base) + IOPW_RAM_DATA, \ + cpu_to_le16((ushort) ((dword) & 0xFFFF)))), \ (outpw((iop_base) + IOPW_RAM_ADDR, (addr) + 2), \ - outpw((iop_base) + IOPW_RAM_DATA, (ushort) ((dword >> 16) & 0xFFFF)))) + outpw((iop_base) + IOPW_RAM_DATA, \ + cpu_to_le16((ushort) ((dword >> 16) & 0xFFFF))))) /* Read word (2 bytes) from LRAM assuming that the address is already set. */ #define AdvReadWordAutoIncLram(iop_base) \ - (inpw((iop_base) + IOPW_RAM_DATA)) + (le16_to_cpu(inpw((iop_base) + IOPW_RAM_DATA))) /* Write word (2 bytes) to LRAM assuming that the address is already set. */ #define AdvWriteWordAutoIncLram(iop_base, word) \ - (outpw((iop_base) + IOPW_RAM_DATA, (word))) + (outpw((iop_base) + IOPW_RAM_DATA, cpu_to_le16(word))) #else /* version >= v1,3,0 */ @@ -3182,11 +3215,11 @@ /* Read word (2 bytes) from a register. */ #define AdvReadWordRegister(iop_base, reg_off) \ - (ADV_MEM_READW((iop_base) + (reg_off))) + le16_to_cpu(ADV_MEM_READW((iop_base) + (reg_off))) /* Write word (2 bytes) to a register. */ #define AdvWriteWordRegister(iop_base, reg_off, word) \ - (ADV_MEM_WRITEW((iop_base) + (reg_off), (word))) + (ADV_MEM_WRITEW((iop_base) + (reg_off), cpu_to_le16(word))) /* Read byte from LRAM. */ #define AdvReadByteLram(iop_base, addr, byte) \ @@ -3204,31 +3237,31 @@ #define AdvReadWordLram(iop_base, addr, word) \ do { \ ADV_MEM_WRITEW((iop_base) + IOPW_RAM_ADDR, (addr)); \ - (word) = ADV_MEM_READW((iop_base) + IOPW_RAM_DATA); \ + (word) = le16_to_cpu(ADV_MEM_READW((iop_base) + IOPW_RAM_DATA)); \ } while (0) /* Write word (2 bytes) to LRAM. */ #define AdvWriteWordLram(iop_base, addr, word) \ (ADV_MEM_WRITEW((iop_base) + IOPW_RAM_ADDR, (addr)), \ - ADV_MEM_WRITEW((iop_base) + IOPW_RAM_DATA, (word))) + ADV_MEM_WRITEW((iop_base) + IOPW_RAM_DATA, cpu_to_le16(word))) /* Write double word (4 bytes) to LRAM */ /* Because of unspecified C language ordering don't use auto-increment. */ #define AdvWriteDWordLram(iop_base, addr, dword) \ ((ADV_MEM_WRITEW((iop_base) + IOPW_RAM_ADDR, (addr)), \ ADV_MEM_WRITEW((iop_base) + IOPW_RAM_DATA, \ - (ushort) ((dword) & 0xFFFF))), \ + cpu_to_le16((ushort) ((dword) & 0xFFFF)))), \ (ADV_MEM_WRITEW((iop_base) + IOPW_RAM_ADDR, (addr) + 2), \ ADV_MEM_WRITEW((iop_base) + IOPW_RAM_DATA, \ - (ushort) ((dword >> 16) & 0xFFFF)))) + cpu_to_le16((ushort) ((dword >> 16) & 0xFFFF))))) /* Read word (2 bytes) from LRAM assuming that the address is already set. */ #define AdvReadWordAutoIncLram(iop_base) \ - (ADV_MEM_READW((iop_base) + IOPW_RAM_DATA)) + le16_to_cpu(ADV_MEM_READW((iop_base) + IOPW_RAM_DATA)) /* Write word (2 bytes) to LRAM assuming that the address is already set. */ #define AdvWriteWordAutoIncLram(iop_base, word) \ - (ADV_MEM_WRITEW((iop_base) + IOPW_RAM_DATA, (word))) + (ADV_MEM_WRITEW((iop_base) + IOPW_RAM_DATA, cpu_to_le16(word))) #endif /* version >= v1,3,0 */ @@ -3266,7 +3299,7 @@ */ #define AdvAbortQueue(asc_dvc, scsiq) \ AdvSendIdleCmd((asc_dvc), (ushort) IDLE_CMD_ABORT, \ - (ulong) (scsiq)) + (ADV_DCNT) (scsiq)) /* * Send a Bus Device Reset Message to the specified target ID. @@ -3281,7 +3314,7 @@ */ #define AdvResetDevice(asc_dvc, target_id) \ AdvSendIdleCmd((asc_dvc), (ushort) IDLE_CMD_DEVICE_RESET, \ - (ulong) (target_id)) + (ADV_DCNT) (target_id)) /* * SCSI Wide Type definition. @@ -3374,15 +3407,6 @@ (sizeof(ADV_SG_BLOCK) * \ ((ADV_MAX_SG_LIST + (NO_OF_SG_PER_BLOCK - 1))/NO_OF_SG_PER_BLOCK)) -/* - * A driver may optionally define the assertion macro ADV_ASSERT() in - * its d_os_dep.h file. If the macro has not already been defined, - * then define the macro to a no-op. - */ -#ifndef ADV_ASSERT -#define ADV_ASSERT(a) -#endif /* ADV_ASSERT */ - typedef struct { uchar peri_dvc_type : 5; /* peripheral device type */ uchar peri_qualifier : 3; /* peripheral qualifier */ @@ -3819,28 +3843,28 @@ /* Per board statistics structure */ struct asc_stats { /* Driver Entrypoint Statistics */ - ulong command; /* # calls to advansys_command() */ - ulong queuecommand; /* # calls to advansys_queuecommand() */ - ulong abort; /* # calls to advansys_abort() */ - ulong reset; /* # calls to advansys_reset() */ - ulong biosparam; /* # calls to advansys_biosparam() */ - ulong interrupt; /* # advansys_interrupt() calls */ - ulong callback; /* # calls to asc/adv_isr_callback() */ - ulong done; /* # calls to request's scsi_done function */ - ulong build_error; /* # asc/adv_build_req() ASC_ERROR returns. */ - ulong adv_build_noreq; /* # adv_build_req() adv_req_t alloc. fail. */ - ulong adv_build_nosg; /* # adv_build_req() adv_sgblk_t alloc. fail. */ + ADV_DCNT command; /* # calls to advansys_command() */ + ADV_DCNT queuecommand; /* # calls to advansys_queuecommand() */ + ADV_DCNT abort; /* # calls to advansys_abort() */ + ADV_DCNT reset; /* # calls to advansys_reset() */ + ADV_DCNT biosparam; /* # calls to advansys_biosparam() */ + ADV_DCNT interrupt; /* # advansys_interrupt() calls */ + ADV_DCNT callback; /* # calls to asc/adv_isr_callback() */ + ADV_DCNT done; /* # calls to request's scsi_done function */ + ADV_DCNT build_error; /* # asc/adv_build_req() ASC_ERROR returns. */ + ADV_DCNT adv_build_noreq; /* # adv_build_req() adv_req_t alloc. fail. */ + ADV_DCNT adv_build_nosg; /* # adv_build_req() adv_sgblk_t alloc. fail. */ /* AscExeScsiQueue()/AdvExeScsiQueue() Statistics */ - ulong exe_noerror; /* # ASC_NOERROR returns. */ - ulong exe_busy; /* # ASC_BUSY returns. */ - ulong exe_error; /* # ASC_ERROR returns. */ - ulong exe_unknown; /* # unknown returns. */ + ADV_DCNT exe_noerror; /* # ASC_NOERROR returns. */ + ADV_DCNT exe_busy; /* # ASC_BUSY returns. */ + ADV_DCNT exe_error; /* # ASC_ERROR returns. */ + ADV_DCNT exe_unknown; /* # unknown returns. */ /* Data Transfer Statistics */ - ulong cont_cnt; /* # non-scatter-gather I/O requests received */ - ulong cont_xfer; /* # contiguous transfer 512-bytes */ - ulong sg_cnt; /* # scatter-gather I/O requests received */ - ulong sg_elem; /* # scatter-gather elements */ - ulong sg_xfer; /* # scatter-gather transfer 512-bytes */ + ADV_DCNT cont_cnt; /* # non-scatter-gather I/O requests received */ + ADV_DCNT cont_xfer; /* # contiguous transfer 512-bytes */ + ADV_DCNT sg_cnt; /* # scatter-gather I/O requests received */ + ADV_DCNT sg_elem; /* # scatter-gather elements */ + ADV_DCNT sg_xfer; /* # scatter-gather transfer 512-bytes */ }; #endif /* ADVANSYS_STATS */ @@ -3854,8 +3878,8 @@ #ifdef ADVANSYS_STATS short q_cur_cnt[ADV_MAX_TID+1]; /* current queue count */ short q_max_cnt[ADV_MAX_TID+1]; /* maximum queue count */ - ulong q_tot_cnt[ADV_MAX_TID+1]; /* total enqueue count */ - ulong q_tot_tim[ADV_MAX_TID+1]; /* total time queued */ + ADV_DCNT q_tot_cnt[ADV_MAX_TID+1]; /* total enqueue count */ + ADV_DCNT q_tot_tim[ADV_MAX_TID+1]; /* total time queued */ ushort q_max_tim[ADV_MAX_TID+1]; /* maximum time queued */ ushort q_min_tim[ADV_MAX_TID+1]; /* minimum time queued */ #endif /* ADVANSYS_STATS */ @@ -3864,20 +3888,20 @@ /* * Adv Library Request Structures * - * The following two se structures are used to process Wide Board requests. - * One structure is needed for each command received from the Mid-Level SCSI - * driver. + * The following two structures are used to process Wide Board requests. * * The ADV_SCSI_REQ_Q structure in adv_req_t is passed to the Adv Library * and microcode with the ADV_SCSI_REQ_Q field 'srb_ptr' pointing to the * adv_req_t. The adv_req_t structure 'cmndp' field in turn points to the * Mid-Level SCSI request structure. * - * The adv_sgblk_t structure is used to handle requests that include - * scatter-gather elements. + * Zero or more ADV_SG_BLOCK are used with each ADV_SCSI_REQ_Q. Each + * ADV_SG_BLOCK structure holds 15 scatter-gather elements. Under Linux + * up to 255 scatter-gather elements may be used per request or + * ADV_SCSI_REQ_Q. */ typedef struct adv_sgblk { - ADV_SG_BLOCK sg_block[ADV_NUM_SG_BLOCK + ADV_NUM_PAGE_CROSSING]; + ADV_SG_BLOCK sg_block; /* Sgblock structure. */ uchar align2[4]; /* Sgblock structure padding. */ struct adv_sgblk *next_sgblkp; /* Next scatter-gather structure. */ } adv_sgblk_t; @@ -3945,7 +3969,6 @@ ADV_CARR_T *orig_carrp; /* ADV_CARR_T memory block. */ adv_req_t *orig_reqp; /* adv_req_t memory block. */ adv_req_t *adv_reqp; /* Request structures. */ - adv_sgblk_t *orig_sgblkp; /* adv_sgblk_t memory block. */ adv_sgblk_t *adv_sgblkp; /* Scatter-gather structures. */ ushort bios_signature; /* BIOS Signature. */ ushort bios_version; /* BIOS Version. */ @@ -3992,9 +4015,9 @@ uchar latencyTimer; uchar headerType; uchar bist; - ulong baseAddress[6]; + ADV_PADDR baseAddress[6]; ushort reserved[4]; - ulong optionRomAddr; + ADV_PADDR optionRomAddr; ushort reserved2[4]; uchar irqLine; uchar irqPin; @@ -4009,7 +4032,8 @@ /* Note: All driver global data should be initialized. */ -#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0) +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,3,28) && \ + LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0) struct proc_dir_entry proc_scsi_advansys = { PROC_SCSI_ADVANSYS, /* unsigned short low_ino */ @@ -4018,7 +4042,7 @@ S_IFDIR | S_IRUGO | S_IXUGO, /* mode_t mode */ 2 /* nlink_t nlink */ }; -#endif /* version >= v1.3.0 */ +#endif /* v2.3.28 > version >= v1.3.0 */ /* Number of boards detected in system. */ STATIC int asc_board_count = 0; @@ -4063,7 +4087,8 @@ */ uchar adv_carr_buf[20 * sizeof(ADV_CARR_T)] = { 0 }; uchar adv_req_buf[16 * sizeof(adv_req_t)] = { 0 }; -uchar adv_sgblk_buf[16 * sizeof(adv_sgblk_t)] = { 0 }; +#define ADV_SGBLK_BUF_CNT 32 +uchar adv_sgblk_buf[ADV_SGBLK_BUF_CNT * sizeof(adv_sgblk_t)] = { 0 }; #endif /* version >= v1,3,0 */ #ifdef ADVANSYS_DEBUG @@ -4075,7 +4100,7 @@ "ASC_IS_PCI", }; -STATIC int asc_dbglvl = 0; +STATIC int asc_dbglvl = 2; #endif /* ADVANSYS_DEBUG */ /* Declaration for Asc Library internal data referenced by driver. */ @@ -4105,7 +4130,7 @@ STATIC int asc_execute_scsi_cmnd(Scsi_Cmnd *); STATIC int asc_build_req(asc_board_t *, Scsi_Cmnd *); STATIC int adv_build_req(asc_board_t *, Scsi_Cmnd *, ADV_SCSI_REQ_Q **); -STATIC int adv_get_sglist(ADV_DVC_VAR *, ADV_SCSI_REQ_Q *, Scsi_Cmnd *); +STATIC int adv_get_sglist(asc_board_t *, adv_req_t *, Scsi_Cmnd *); STATIC void asc_isr_callback(ASC_DVC_VAR *, ASC_QDONE_INFO *); STATIC void adv_isr_callback(ADV_DVC_VAR *, ADV_SCSI_REQ_Q *); STATIC void adv_async_callback(ADV_DVC_VAR *, uchar); @@ -4432,6 +4457,7 @@ asc_board_t *boardp = NULL; ASC_DVC_VAR *asc_dvc_varp = NULL; ADV_DVC_VAR *adv_dvc_varp = NULL; + adv_sgblk_t *sgp = NULL; int ioport = 0; int share_irq = FALSE; int iolen = 0; @@ -4444,7 +4470,7 @@ PCI_DEVICE pciDevice; PCI_CONFIG_SPACE pciConfig; #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0) - unsigned long pci_memory_address; + ADV_PADDR pci_memory_address; #endif /* version >= v1,3,0 */ #endif /* ASC_CONFIG_PCI */ #else /* version >= v2.1.93 */ @@ -4462,7 +4488,7 @@ ASC_PCI_DEVICE_ID_2300, ASC_PCI_DEVICE_ID_2500 }; - unsigned long pci_memory_address; + ADV_PADDR pci_memory_address; #endif /* CONFIG_PCI */ #endif /* version >= v2.1.93 */ int warn_code, err_code; @@ -4477,7 +4503,9 @@ ASC_DBG(1, "advansys_detect: begin\n"); -#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0) +#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,3,28) + tpnt->proc_name = "advansys"; +#elif LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0) tpnt->proc_dir = &proc_scsi_advansys; #endif /* version >= v1.3.0 */ @@ -4711,7 +4739,7 @@ #if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,3,13) iop = pci_devp->base_address[0] & PCI_IOADDRESS_MASK; #else /* version >= v2.3.13 */ - iop = pci_devp->resource[1].start & PCI_IOADDRESS_MASK; + iop = pci_devp->resource[0].start & PCI_IOADDRESS_MASK; #endif /* version >= v2.3.13 */ ASC_DBG2(1, "advansys_detect: vendorID %X, deviceID %X\n", @@ -4785,15 +4813,15 @@ ASC_DBG(1, "advansys_detect: narrow board\n"); asc_dvc_varp = &boardp->dvc_var.asc_dvc_var; asc_dvc_varp->bus_type = asc_bus[bus]; - asc_dvc_varp->drv_ptr = (ulong) boardp; + asc_dvc_varp->drv_ptr = boardp; asc_dvc_varp->cfg = &boardp->dvc_cfg.asc_dvc_cfg; asc_dvc_varp->cfg->overrun_buf = &overrun_buf[0]; asc_dvc_varp->iop_base = iop; - asc_dvc_varp->isr_callback = (Ptr2Func) asc_isr_callback; + asc_dvc_varp->isr_callback = asc_isr_callback; } else { ASC_DBG(1, "advansys_detect: wide board\n"); adv_dvc_varp = &boardp->dvc_var.adv_dvc_var; - adv_dvc_varp->drv_ptr = (ulong) boardp; + adv_dvc_varp->drv_ptr = boardp; adv_dvc_varp->cfg = &boardp->dvc_cfg.adv_dvc_cfg; adv_dvc_varp->isr_callback = adv_isr_callback; adv_dvc_varp->async_callback = adv_async_callback; @@ -4848,13 +4876,13 @@ #ifdef ASC_CONFIG_PCI pci_memory_address = pciConfig.baseAddress[1]; ASC_DBG1(1, "advansys_detect: pci_memory_address: %lu\n", - pci_memory_address); + (ulong) pci_memory_address); if ((boardp->ioremap_addr = ioremap(pci_memory_address & PAGE_MASK, PAGE_SIZE)) == 0) { ASC_PRINT3( -"advansys_detect: board %d: ioremap(%lx, %d) returned NULL\n", - boardp->id, pci_memory_address, iolen); +"advansys_detect: board %d: ioremap(%lu, %x) returned NULL\n", + boardp->id, (ulong) pci_memory_address, iolen); scsi_unregister(shp); asc_board_count--; continue; @@ -4874,13 +4902,13 @@ #else /* version >= v2.3.13 */ pci_memory_address = pci_devp->resource[1].start; #endif /* version >= v2.3.13 */ - ASC_DBG1(1, "advansys_detect: pci_memory_address: %lu\n", + ASC_DBG1(1, "advansys_detect: pci_memory_address: %x\n", pci_memory_address); if ((boardp->ioremap_addr = ioremap(pci_memory_address & PAGE_MASK, PAGE_SIZE)) == 0) { ASC_PRINT3( -"advansys_detect: board %d: ioremap(%lx, %d) returned NULL\n", +"advansys_detect: board %d: ioremap(%x, %d) returned NULL\n", boardp->id, pci_memory_address, iolen); scsi_unregister(shp); asc_board_count--; @@ -4905,7 +4933,8 @@ */ boardp->ioport = iop; - ASC_DBG2(1, "iopb_chip_id_1 %x, iopw_chip_id_0 %x\n", + ASC_DBG2(1, + "advansys_detect: iopb_chip_id_1 %x, iopw_chip_id_0 %x\n", (ushort) inp(iop + 1), (ushort) inpw(iop)); } @@ -5335,6 +5364,7 @@ shp->sg_tablesize = ADV_MAX_SG_LIST; } +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,0,0) #ifdef MODULE /* * If the driver is compiled as a module, set a limit on the @@ -5346,6 +5376,7 @@ shp->sg_tablesize = 64; } #endif /* MODULE */ +#endif /* version < v2.0.0 */ /* * The value of 'sg_tablesize' can not exceed the SCSI @@ -5362,9 +5393,14 @@ /* BIOS start address. */ if (ASC_NARROW_BOARD(boardp)) { - shp->base = (char *) ((ulong) AscGetChipBiosAddress( - asc_dvc_varp->iop_base, - asc_dvc_varp->bus_type)); +#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,3,29) + shp->base = +#else /* version >= v2.3.29 */ + shp->base = (char *) +#endif /* version < v2.3.29 */ + ((ulong) AscGetChipBiosAddress( + asc_dvc_varp->iop_base, + asc_dvc_varp->bus_type)); } else { /* * Fill-in BIOS board variables. The Wide BIOS saves @@ -5396,7 +5432,12 @@ * Convert x86 realmode code segment to a linear * address by shifting left 4. */ - shp->base = (uchar *) (boardp->bios_codeseg << 4); +#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,3,29) + shp->base = +#else /* version >= v2.3.29 */ + shp->base = (char *) +#endif /* version < v2.3.29 */ + ((ulong) boardp->bios_codeseg << 4); } else { shp->base = 0; } @@ -5503,21 +5544,26 @@ int req_cnt; adv_req_t *reqp = NULL; int sg_cnt = 0; - adv_sgblk_t *sgp = NULL; #if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,0) carrp = (ADV_CARR_T *) &adv_carr_buf[0]; req_cnt = sizeof(adv_req_buf)/sizeof(adv_req_t); sg_cnt = sizeof(adv_sgblk_buf)/sizeof(adv_sgblk_t); reqp = (adv_req_t *) &adv_req_buf[0]; - sgp = (adv_sgblk_t *) &adv_sgblk_buf[0]; + boardp->adv_sgblkp = NULL; + for (sg_cnt = 0; sg_cnt < ADV_SGBLK_BUF_CNT; sg_cnt++) { + sgp = (adv_sgblk_t *) &adv_sgblk_buf[sg_cnt]; + sgp->next_sgblkp = boardp->adv_sgblkp; + boardp->adv_sgblkp = sgp; + } #else /* version >= v1.3.0 */ /* - * Allocate buffer carrier structures. + * Allocate buffer carrier structures. The total size + * is about 4 KB, so allocate all at once. */ carrp = (ADV_CARR_T *) kmalloc(ADV_CARRIER_BUFSIZE, GFP_ATOMIC); - ASC_DBG1(1, "advansys_detect: carrp %x\n", (unsigned) carrp); + ASC_DBG1(1, "advansys_detect: carrp %lx\n", (ulong) carrp); if (carrp == NULL) { goto kmalloc_error; @@ -5525,7 +5571,9 @@ /* * Allocate up to 'max_host_qng' request structures for - * the Wide board. + * the Wide board. The total size is about 16 KB, so + * allocate all at once. If the allocation fails decrement + * and try again. */ for (req_cnt = adv_dvc_varp->max_host_qng; req_cnt > 0; req_cnt--) { @@ -5534,8 +5582,9 @@ kmalloc(sizeof(adv_req_t) * req_cnt, GFP_ATOMIC); ASC_DBG3(1, - "advansys_detect: reqp %x, req_cnt %d, bytes %d\n", - (unsigned) reqp, req_cnt, sizeof(adv_req_t) * req_cnt); + "advansys_detect: reqp %lx, req_cnt %d, bytes %lu\n", + (ulong) reqp, req_cnt, + (ulong) sizeof(adv_req_t) * req_cnt); if (reqp != NULL) { break; @@ -5547,22 +5596,27 @@ } /* - * Allocate up to ADV_TOT_SG_LIST request structures for - * the Wide board. + * Allocate up to ADV_TOT_SG_BLOCK request structures for + * the Wide board. Each structure is about 136 bytes. */ - for (sg_cnt = ADV_TOT_SG_LIST; sg_cnt > 0; sg_cnt--) { + boardp->adv_sgblkp = NULL; + for (sg_cnt = 0; sg_cnt < ADV_TOT_SG_BLOCK; sg_cnt++) { sgp = (adv_sgblk_t *) - kmalloc(sizeof(adv_sgblk_t) * sg_cnt, GFP_ATOMIC); + kmalloc(sizeof(adv_sgblk_t), GFP_ATOMIC); - ASC_DBG3(1, - "advansys_detect: sgp %x, sg_cnt %d, bytes %d\n", - (unsigned) sgp, sg_cnt, sizeof(adv_sgblk_t) * sg_cnt); - - if (sgp != NULL) { + if (sgp == NULL) { break; } + + sgp->next_sgblkp = boardp->adv_sgblkp; + boardp->adv_sgblkp = sgp; + } + ASC_DBG3(1, + "advansys_detect: sg_cnt %d * %u = %u bytes\n", + sg_cnt, sizeof(adv_sgblk_t), + (unsigned) (sizeof(adv_sgblk_t) * sg_cnt)); /* * If no request structures or scatter-gather structures could @@ -5582,11 +5636,11 @@ "advansys_detect: board %d: error: failed to kmalloc() adv_req_t buffer.\n", boardp->id); err_code = ADV_ERROR; - } else if (sgp == NULL) { + } else if (boardp->adv_sgblkp == NULL) { kfree(carrp); kfree(reqp); ASC_PRINT1( -"advansys_detect: board %d: error: failed to kmalloc() adv_sgblk_t buffer.\n", +"advansys_detect: board %d: error: failed to kmalloc() adv_sgblk_t buffers.\n", boardp->id); err_code = ADV_ERROR; } else { @@ -5599,12 +5653,6 @@ * driver is built as a module and can be unloaded. */ boardp->orig_reqp = reqp; - - /* - * Save original pointer for kfree() in case the - * driver is built as a module and can be unloaded. - */ - boardp->orig_sgblkp = sgp; #endif /* version >= v1.3.0 */ adv_dvc_varp->carrier_buf = carrp; @@ -5620,17 +5668,6 @@ } boardp->adv_reqp = &reqp[0]; - /* - * Point 'adv_sgblkp' to the request structures and - * link them together. - */ - sg_cnt--; - sgp[sg_cnt].next_sgblkp = NULL; - for (; sg_cnt > 0; sg_cnt--) { - sgp[sg_cnt - 1].next_sgblkp = &sgp[sg_cnt]; - } - boardp->adv_sgblkp = &sgp[0]; - if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550) { ASC_DBG(2, @@ -5646,7 +5683,7 @@ if (warn_code || err_code) { ASC_PRINT3( "AdvInitAsc3550/38C0800Driver: board %d: error: warn %x, error %x\n", - boardp->id, warn_code, adv_dvc_varp->err_code); + boardp->id, warn_code, err_code); } #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0) } @@ -5666,9 +5703,10 @@ kfree(boardp->orig_reqp); boardp->orig_reqp = boardp->adv_reqp = NULL; } - if (boardp->orig_sgblkp) { - kfree(boardp->orig_sgblkp); - boardp->orig_sgblkp = boardp->adv_sgblkp = NULL; + while ((sgp = boardp->adv_sgblkp) != NULL) + { + boardp->adv_sgblkp = sgp->next_sgblkp; + kfree(sgp); } } #endif /* version >= v1,3,0 */ @@ -5718,6 +5756,8 @@ release_region(shp->io_port, shp->n_io_port); #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0) if (ASC_WIDE_BOARD(boardp)) { + adv_sgblk_t *sgp = NULL; + iounmap(boardp->ioremap_addr); if (boardp->orig_carrp) { kfree(boardp->orig_carrp); @@ -5727,9 +5767,10 @@ kfree(boardp->orig_reqp); boardp->orig_reqp = boardp->adv_reqp = NULL; } - if (boardp->orig_sgblkp) { - kfree(boardp->orig_sgblkp); - boardp->orig_sgblkp = boardp->adv_sgblkp = NULL; + while ((sgp = boardp->adv_sgblkp) != NULL) + { + boardp->adv_sgblkp = sgp->next_sgblkp; + kfree(sgp); } } ASC_ASSERT(boardp->prtbuf != NULL); @@ -5772,13 +5813,13 @@ } sprintf(info, #if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,92) -"AdvanSys SCSI %s: %s %u CDB: BIOS %X, IO %X/%X, IRQ %u, DMA %u", +"AdvanSys SCSI %s: %s %u CDB: BIOS %lX, IO %lX/%X, IRQ %u, DMA %u", #else /* version >= v2.1.92 */ -"AdvanSys SCSI %s: %s %u CDB: BIOS %X, IO %lX/%X, IRQ %u, DMA %u", +"AdvanSys SCSI %s: %s %u CDB: BIOS %lX, IO %lX/%X, IRQ %u, DMA %u", #endif /* version >= v2.1.92 */ ASC_VERSION, busname, asc_dvc_varp->max_total_qng, - (unsigned) shp->base, - shp->io_port, shp->n_io_port - 1, + (ulong) shp->base, + (ulong) shp->io_port, shp->n_io_port - 1, shp->irq, shp->dma_channel); } else if (asc_dvc_varp->bus_type & ASC_IS_PCI) { if ((asc_dvc_varp->bus_type & ASC_IS_PCI_ULTRA) @@ -5808,12 +5849,12 @@ } sprintf(info, #if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,92) - "AdvanSys SCSI %s: %s %u CDB: BIOS %X, IO %X/%X, IRQ %u", + "AdvanSys SCSI %s: %s %u CDB: BIOS %lX, IO %X/%X, IRQ %u", #else /* version >= v2.1.92 */ - "AdvanSys SCSI %s: %s %u CDB: BIOS %X, IO %lX/%X, IRQ %u", + "AdvanSys SCSI %s: %s %u CDB: BIOS %lX, IO %lX/%X, IRQ %u", #endif /* version >= v2.1.92 */ ASC_VERSION, busname, asc_dvc_varp->max_total_qng, - (unsigned) shp->base, shp->io_port - 1, + (ulong) shp->base, shp->io_port - 1, shp->n_io_port, shp->irq); } } else { @@ -5871,7 +5912,7 @@ int advansys_command(Scsi_Cmnd *scp) { - ASC_DBG1(1, "advansys_command: scp %x\n", (unsigned) scp); + ASC_DBG1(1, "advansys_command: scp %lx\n", (ulong) scp); ASC_STATS(scp->host, command); scp->SCp.Status = 0; /* Set to a known state */ advansys_queuecommand(scp, advansys_command_done); @@ -5914,13 +5955,13 @@ if (boardp->flags & (ASC_HOST_IN_RESET | ASC_HOST_IN_ABORT)) { if (boardp->flags & ASC_HOST_IN_RESET) { ASC_DBG1(1, - "advansys_queuecommand: scp %x blocked for reset request\n", - (unsigned) scp); + "advansys_queuecommand: scp %lx blocked for reset request\n", + (ulong) scp); scp->result = HOST_BYTE(DID_RESET); } else { ASC_DBG1(1, - "advansys_queuecommand: scp %x blocked for abort request\n", - (unsigned) scp); + "advansys_queuecommand: scp %lx blocked for abort request\n", + (ulong) scp); scp->result = HOST_BYTE(DID_ABORT); } @@ -5998,7 +6039,7 @@ save_flags(flags); cli(); - ASC_DBG1(1, "advansys_abort: scp %x\n", (unsigned) scp); + ASC_DBG1(1, "advansys_abort: scp %lx\n", (ulong) scp); #ifdef ADVANSYS_STATS if (scp->host != NULL) { @@ -6015,8 +6056,8 @@ #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89) if (scp->serial_number != scp->serial_number_at_timeout) { ASC_PRINT1( -"advansys_abort: timeout serial number changed for request %x\n", - (unsigned) scp); +"advansys_abort: timeout serial number changed for request %lx\n", + (ulong) scp); do_scsi_done = ASC_FALSE; scp_found = ASC_FALSE; ret = SCSI_ABORT_NOT_RUNNING; @@ -6052,8 +6093,8 @@ * queue, it had not been sent to the device. After * the queue is removed, no other handling is required. */ - ASC_DBG1(1, "advansys_abort: scp %x found on waiting queue\n", - (unsigned) scp); + ASC_DBG1(1, "advansys_abort: scp %lx found on waiting queue\n", + (ulong) scp); scp_found = ASC_TRUE; scp->result = HOST_BYTE(DID_ABORT); ret = SCSI_ABORT_SUCCESS; @@ -6073,9 +6114,10 @@ scp->result = HOST_BYTE(DID_ABORT); /* sti(); XXX */ /* Enable interrupts for AscAbortSRB(). */ - ASC_DBG1(1, "advansys_abort: before AscAbortSRB(), scp %x\n", - (unsigned) scp); - switch (AscAbortSRB(asc_dvc_varp, (ulong) scp)) { + ASC_DBG1(1, "advansys_abort: before AscAbortSRB(), scp %lx\n", + (ulong) scp); + /* XXX */ + switch (AscAbortSRB(asc_dvc_varp, ASC_VADDR_TO_U32(scp))) { case ASC_TRUE: /* asc_isr_callback() will be called */ ASC_DBG(1, "advansys_abort: AscAbortSRB() TRUE\n"); @@ -6100,10 +6142,11 @@ adv_dvc_varp = &boardp->dvc_var.adv_dvc_var; scp->result = HOST_BYTE(DID_ABORT); - ASC_DBG1(1, "advansys_abort: before AdvAbortQueue(), scp %x\n", - (unsigned) scp); + ASC_DBG1(1, + "advansys_abort: before AdvAbortQueue(), scp %lx\n", + (ulong) scp); #if 0 /* XXX */ - switch (AdvAbortQueue(adv_dvc_varp, (ulong) XXX)) { + switch (AdvAbortQueue(adv_dvc_varp, (ADV_VADDR) XXX)) { case ASC_TRUE: /* asc_isr_callback() will be called */ ASC_DBG(1, "advansys_abort: AdvAbortQueue() TRUE\n"); @@ -6184,13 +6227,13 @@ if (do_scsi_done == ASC_TRUE) { if (scp->scsi_done == NULL) { ASC_PRINT1( -"advansys_abort: aborted request scsi_done() is NULL, %x\n", - (unsigned) scp); +"advansys_abort: aborted request scsi_done() is NULL, %lx\n", + (ulong) scp); } else { if (scp_found == ASC_FALSE) { ASC_PRINT1( -"advansys_abort: abort request not active or waiting, completing anyway %x\n", - (unsigned) scp); +"advansys_abort: abort request not active or waiting, completing anyway %lx\n", + (ulong) scp); } ASC_STATS(scp->host, done); scp->scsi_done(scp); @@ -6245,7 +6288,7 @@ save_flags(flags); cli(); - ASC_DBG1(1, "advansys_reset: %x\n", (unsigned) scp); + ASC_DBG1(1, "advansys_reset: %lx\n", (ulong) scp); #ifdef ADVANSYS_STATS if (scp->host != NULL) { @@ -6257,8 +6300,8 @@ if ((reset_flags & SCSI_RESET_ASYNCHRONOUS) && (scp->serial_number != scp->serial_number_at_timeout)) { ASC_PRINT1( -"advansys_reset: timeout serial number changed for request %x\n", - (unsigned) scp); +"advansys_reset: timeout serial number changed for request %lx\n", + (ulong) scp); do_scsi_done = ASC_FALSE; scp_found = ASC_FALSE; ret = SCSI_RESET_NOT_RUNNING; @@ -6581,13 +6624,13 @@ if (do_scsi_done == ASC_TRUE) { if (scp->scsi_done == NULL) { ASC_PRINT1( -"advansys_reset: reset request scsi_done() is NULL, %x\n", - (unsigned) scp); +"advansys_reset: reset request scsi_done() is NULL, %lx\n", + (ulong) scp); } else { if (scp_found == ASC_FALSE) { ASC_PRINT1( -"advansys_reset: reset request not active or waiting, completing anyway %x\n", - (unsigned) scp); +"advansys_reset: reset request not active or waiting, completing anyway %lx\n", + (ulong) scp); } ASC_STATS(scp->host, done); scp->scsi_done(scp); @@ -6915,8 +6958,8 @@ device->queue_depth = boardp->dvc_var.adv_dvc_var.max_dvc_qng; } - ASC_DBG3(1, "advansys_select_queue_depths: shp %x, id %d, depth %d\n", - (unsigned) shp, device->id, device->queue_depth); + ASC_DBG3(1, "advansys_select_queue_depths: shp %lx, id %d, depth %d\n", + (ulong) shp, device->id, device->queue_depth); } } #endif /* version >= v1.3.89 */ @@ -6928,7 +6971,7 @@ STATIC void advansys_command_done(Scsi_Cmnd *scp) { - ASC_DBG1(1, "advansys_command_done: scp %x\n", (unsigned) scp); + ASC_DBG1(1, "advansys_command_done: scp %lx\n", (ulong) scp); scp->SCp.Status = 1; } @@ -6945,7 +6988,7 @@ ASC_DBG(2, "asc_scsi_done_list: begin\n"); while (scp != NULL) { - ASC_DBG1(3, "asc_scsi_done_list: scp %x\n", (unsigned) scp); + ASC_DBG1(3, "asc_scsi_done_list: scp %lx\n", (ulong) scp); tscp = REQPNEXT(scp); REQPNEXT(scp) = NULL; ASC_STATS(scp->host, done); @@ -7011,8 +7054,8 @@ int ret; ASC_ASSERT(interrupts_enabled() == ASC_FALSE); - ASC_DBG2(1, "asc_execute_scsi_cmnd: scp %x, done %x\n", - (unsigned) scp, (unsigned) scp->scsi_done); + ASC_DBG2(1, "asc_execute_scsi_cmnd: scp %lx, done %lx\n", + (ulong) scp, (ulong) scp->scsi_done); boardp = ASC_BOARDP(scp->host); device = boardp->device[scp->target]; @@ -7163,7 +7206,7 @@ /* * Point the ASC_SCSI_Q to the 'Scsi_Cmnd'. */ - asc_scsi_q.q2.srb_ptr = (ulong) scp; + asc_scsi_q.q2.srb_ptr = ASC_VADDR_TO_U32(scp); /* * Build the ASC_SCSI_Q request. @@ -7178,9 +7221,9 @@ asc_scsi_q.q1.target_lun = scp->lun; asc_scsi_q.q2.target_ix = ASC_TIDLUN_TO_IX(scp->target, scp->lun); #if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,0,0) - asc_scsi_q.q1.sense_addr = (ulong) &scp->sense_buffer[0]; + asc_scsi_q.q1.sense_addr = (ADV_PADDR) &scp->sense_buffer[0]; #else /* version >= v2.0.0 */ - asc_scsi_q.q1.sense_addr = virt_to_bus(&scp->sense_buffer[0]); + asc_scsi_q.q1.sense_addr = cpu_to_le32(virt_to_bus(&scp->sense_buffer[0])); #endif /* version >= v2.0.0 */ asc_scsi_q.q1.sense_len = sizeof(scp->sense_buffer); @@ -7212,9 +7255,10 @@ */ ASC_STATS(scp->host, cont_cnt); #if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,0,0) - asc_scsi_q.q1.data_addr = (ulong) scp->request_buffer; + asc_scsi_q.q1.data_addr = (ADV_PADDR) scp->request_buffer; #else /* version >= v2.0.0 */ - asc_scsi_q.q1.data_addr = virt_to_bus(scp->request_buffer); + asc_scsi_q.q1.data_addr = + cpu_to_le32(virt_to_bus(scp->request_buffer)); #endif /* version >= v2.0.0 */ asc_scsi_q.q1.data_cnt = scp->request_bufflen; ASC_STATS_ADD(scp->host, cont_xfer, @@ -7258,11 +7302,12 @@ slp = (struct scatterlist *) scp->request_buffer; for (sgcnt = 0; sgcnt < scp->use_sg; sgcnt++, slp++) { #if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,0,0) - asc_sg_head.sg_list[sgcnt].addr = (ulong) slp->address; + asc_sg_head.sg_list[sgcnt].addr = (ADV_PADDR) slp->address; #else /* version >= v2.0.0 */ - asc_sg_head.sg_list[sgcnt].addr = virt_to_bus(slp->address); + asc_sg_head.sg_list[sgcnt].addr = + cpu_to_le32(virt_to_bus(slp->address)); #endif /* version >= v2.0.0 */ - asc_sg_head.sg_list[sgcnt].bytes = slp->length; + asc_sg_head.sg_list[sgcnt].bytes = cpu_to_le32(slp->length); ASC_STATS_ADD(scp->host, sg_xfer, ASC_CEILING(slp->length, 512)); } } @@ -7278,6 +7323,10 @@ * * If an adv_req_t can not be allocated to issue the request, * then return ASC_BUSY. If an error occurs, then return ASC_ERROR. + * + * Multi-byte fields in the ASC_SCSI_REQ_Q that are used by the + * microcode for DMA addresses or math operations are byte swapped + * to little-endian order. */ STATIC int adv_build_req(asc_board_t *boardp, Scsi_Cmnd *scp, @@ -7286,6 +7335,7 @@ adv_req_t *reqp; ADV_SCSI_REQ_Q *scsiqp; int i; + int ret; /* * Allocate an adv_req_t structure from the board to execute @@ -7305,12 +7355,16 @@ * Get 4-byte aligned ADV_SCSI_REQ_Q and ADV_SG_BLOCK pointers. */ scsiqp = (ADV_SCSI_REQ_Q *) ADV_DWALIGN(&reqp->scsi_req_q); - memset(scsiqp, 0, sizeof(ADV_SCSI_REQ_Q)); + + /* + * Initialize the structure. + */ + scsiqp->cntl = scsiqp->scsi_cntl = scsiqp->done_status = 0; /* * Set the ADV_SCSI_REQ_Q 'srb_ptr' to point to the adv_req_t structure. */ - scsiqp->srb_ptr = (ulong) reqp; + scsiqp->srb_ptr = ASC_VADDR_TO_U32(reqp); /* * Set the adv_req_t 'cmndp' to point to the Scsi_Cmnd structure. @@ -7337,9 +7391,9 @@ scsiqp->target_lun = scp->lun; #if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,0,0) - scsiqp->sense_addr = (ulong) &scp->sense_buffer[0]; + scsiqp->sense_addr = (ADV_PADDR) &scp->sense_buffer[0]; #else /* version >= v2.0.0 */ - scsiqp->sense_addr = virt_to_bus(&scp->sense_buffer[0]); + scsiqp->sense_addr = cpu_to_le32(virt_to_bus(&scp->sense_buffer[0])); #endif /* version >= v2.0.0 */ scsiqp->sense_len = sizeof(scp->sense_buffer); @@ -7347,12 +7401,12 @@ * Build ADV_SCSI_REQ_Q for a contiguous buffer or a scatter-gather * buffer command. */ - scsiqp->data_cnt = scp->request_bufflen; - scsiqp->vdata_addr = (ulong) scp->request_buffer; + scsiqp->data_cnt = cpu_to_le32(scp->request_bufflen); + scsiqp->vdata_addr = scp->request_buffer; #if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,0,0) - scsiqp->data_addr = (ulong) scp->request_buffer; + scsiqp->data_addr = (ADV_PADDR) scp->request_buffer; #else /* version >= v2.0.0 */ - scsiqp->data_addr = virt_to_bus(scp->request_buffer); + scsiqp->data_addr = cpu_to_le32(virt_to_bus(scp->request_buffer)); #endif /* version >= v2.0.0 */ if (scp->use_sg == 0) { @@ -7361,6 +7415,7 @@ */ reqp->sgblkp = NULL; scsiqp->sg_list_ptr = NULL; + scsiqp->sg_real_addr = 0; ASC_STATS(scp->host, cont_cnt); ASC_STATS_ADD(scp->host, cont_xfer, ASC_CEILING(scp->request_bufflen, 512)); @@ -7385,46 +7440,7 @@ return ASC_ERROR; } - /* - * Allocate an 'adv_sgblk_t' structure from the board to - * execute the command. - */ - if (boardp->adv_sgblkp == NULL) { - ASC_DBG(1, "adv_build_req: no free adv_sgblk_t\n"); - ASC_STATS(scp->host, adv_build_nosg); - /* - * Free the 'adv_req_t' structure by adding it back to the - * board free list. - */ - reqp->next_reqp = boardp->adv_reqp; - boardp->adv_reqp = reqp; - return ASC_BUSY; - } else { - reqp->sgblkp = boardp->adv_sgblkp; - boardp->adv_sgblkp = reqp->sgblkp->next_sgblkp; - reqp->sgblkp->next_sgblkp = NULL; - } - - /* - * Build scatter-gather list. - */ - scsiqp->sg_list_ptr = (ADV_SG_BLOCK *) - ADV_DWALIGN(&reqp->sgblkp->sg_block[0]); - - memset(scsiqp->sg_list_ptr, 0, sizeof(ADV_SG_BLOCK) * - (ADV_NUM_SG_BLOCK + ADV_NUM_PAGE_CROSSING)); - - if (adv_get_sglist(&boardp->dvc_var.adv_dvc_var, scsiqp, scp) == - ADV_ERROR) { - - /* - * Free the adv_sgblk_t structure, if any, by adding it back - * to the board free list. - */ - ASC_ASSERT(reqp->sgblkp != NULL); - reqp->sgblkp->next_sgblkp = boardp->adv_sgblkp; - boardp->adv_sgblkp = reqp->sgblkp; - + if ((ret = adv_get_sglist(boardp, reqp, scp)) != ADV_SUCCESS) { /* * Free the adv_req_t structure by adding it back to the * board free list. @@ -7432,7 +7448,7 @@ reqp->next_reqp = boardp->adv_reqp; boardp->adv_reqp = reqp; - return ADV_ERROR; + return ret; } ASC_STATS(scp->host, sg_cnt); @@ -7450,65 +7466,124 @@ /* * Build scatter-gather list for Adv Library (Wide Board). * + * Additional ADV_SG_BLOCK structures will need to be allocated + * if the total number of scatter-gather elements exceeds + * NO_OF_SG_PER_BLOCK (15). The ADV_SG_BLOCK structures are + * assumed to be physically contiguous. + * * Return: * ADV_SUCCESS(1) - SG List successfully created * ADV_ERROR(-1) - SG List creation failed */ STATIC int -adv_get_sglist(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp, - Scsi_Cmnd *scp) +adv_get_sglist(asc_board_t *boardp, adv_req_t *reqp, Scsi_Cmnd *scp) { - ADV_SG_BLOCK *sg_block; /* virtual address of a SG */ - ulong sg_block_next_addr; /* block and its next */ - ulong sg_block_physical_addr; - int i; + adv_sgblk_t *sgblkp; + ADV_SCSI_REQ_Q *scsiqp; struct scatterlist *slp; int sg_elem_cnt; + ADV_SG_BLOCK *sg_block, *prev_sg_block; + ADV_PADDR sg_block_paddr; + int i; + scsiqp = (ADV_SCSI_REQ_Q *) ADV_DWALIGN(&reqp->scsi_req_q); slp = (struct scatterlist *) scp->request_buffer; sg_elem_cnt = scp->use_sg; + prev_sg_block = NULL; + reqp->sgblkp = NULL; + + do + { + /* + * Allocate a 'adv_sgblk_t' structure from the board free + * list. One 'adv_sgblk_t' structure holds NO_OF_SG_PER_BLOCK + * (15) scatter-gather elements. + */ + if ((sgblkp = boardp->adv_sgblkp) == NULL) { + ASC_DBG(1, "adv_get_sglist: no free adv_sgblk_t\n"); + ASC_STATS(scp->host, adv_build_nosg); + + /* + * Allocation failed. Free 'adv_sgblk_t' structures already + * allocated for the request. + */ + while ((sgblkp = reqp->sgblkp) != NULL) + { + /* Remove 'sgblkp' from the request list. */ + reqp->sgblkp = sgblkp->next_sgblkp; + + /* Add 'sgblkp' to the board free list. */ + sgblkp->next_sgblkp = boardp->adv_sgblkp; + boardp->adv_sgblkp = sgblkp; + } + return ASC_BUSY; + } else { + /* Complete 'adv_sgblk_t' board allocation. */ + boardp->adv_sgblkp = sgblkp->next_sgblkp; + sgblkp->next_sgblkp = NULL; - sg_block = scsiqp->sg_list_ptr; - sg_block_next_addr = (ulong) sg_block; /* allow math operation */ - sg_block_physical_addr = + /* + * Get 4 byte aligned virtual and physical addresses for + * the allocated ADV_SG_BLOCK structure. + */ + sg_block = (ADV_SG_BLOCK *) ADV_DWALIGN(&sgblkp->sg_block); + sg_block_paddr = #if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,0,0) - (ulong) scsiqp->sg_list_ptr; + (ADV_PADDR) sg_block; #else /* version >= v2.0.0 */ - virt_to_bus(scsiqp->sg_list_ptr); + virt_to_bus(sg_block); #endif /* version >= v2.0.0 */ - ADV_ASSERT(ADV_DWALIGN(sg_block_physical_addr) == - sg_block_physical_addr); - scsiqp->sg_real_addr = sg_block_physical_addr; - do - { + /* + * Check if this is the first 'adv_sgblk_t' for the request. + */ + if (reqp->sgblkp == NULL) + { + /* Request's first scatter-gather block. */ + reqp->sgblkp = sgblkp; + + /* + * Set ADV_SCSI_REQ_T ADV_SG_BLOCK virtual and physical + * address pointers. + */ + scsiqp->sg_list_ptr = sg_block; + scsiqp->sg_real_addr = cpu_to_le32(sg_block_paddr); + } else + { + /* Request's second or later scatter-gather block. */ + sgblkp->next_sgblkp = reqp->sgblkp; + reqp->sgblkp = sgblkp; + + /* + * Point the previous ADV_SG_BLOCK structure to + * the newly allocated ADV_SG_BLOCK structure. + */ + ASC_ASSERT(prev_sg_block != NULL); + prev_sg_block->sg_ptr = cpu_to_le32(sg_block_paddr); + } + } + for (i = 0; i < NO_OF_SG_PER_BLOCK; i++) { sg_block->sg_list[i].sg_addr = #if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,0,0) - (ulong) slp->address; + (ADV_PADDR) slp->address; #else /* version >= v2.0.0 */ - virt_to_bus(slp->address); + cpu_to_le32(virt_to_bus(slp->address)); #endif /* version >= v2.0.0 */ - sg_block->sg_list[i].sg_count = slp->length; + sg_block->sg_list[i].sg_count = cpu_to_le32(slp->length); ASC_STATS_ADD(scp->host, sg_xfer, ASC_CEILING(slp->length, 512)); if (--sg_elem_cnt == 0) - { /* last entry, get out */ + { /* Last ADV_SG_BLOCK and scatter-gather entry. */ sg_block->sg_cnt = i + 1; - sg_block->sg_ptr = 0L; /* next link = NULL */ + sg_block->sg_ptr = 0L; /* Last ADV_SG_BLOCK in list. */ return ADV_SUCCESS; } slp++; - } + } sg_block->sg_cnt = NO_OF_SG_PER_BLOCK; - sg_block_next_addr += sizeof(ADV_SG_BLOCK); - sg_block_physical_addr += sizeof(ADV_SG_BLOCK); - ADV_ASSERT(ADV_DWALIGN(sg_block_physical_addr) == - sg_block_physical_addr); - - sg_block->sg_ptr = (ADV_SG_BLOCK *) sg_block_physical_addr; - sg_block = (ADV_SG_BLOCK *) sg_block_next_addr; /* virtual addr */ + prev_sg_block = sg_block; } while (1); /* NOTREACHED */ @@ -7525,20 +7600,19 @@ asc_board_t *boardp; Scsi_Cmnd *scp; struct Scsi_Host *shp; - int underrun = ASC_FALSE; int i; ASC_ASSERT(interrupts_enabled() == ASC_FALSE); - ASC_DBG2(1, "asc_isr_callback: asc_dvc_varp %x, qdonep %x\n", - (unsigned) asc_dvc_varp, (unsigned) qdonep); + ASC_DBG2(1, "asc_isr_callback: asc_dvc_varp %lx, qdonep %lx\n", + (ulong) asc_dvc_varp, (ulong) qdonep); ASC_DBG_PRT_ASC_QDONE_INFO(2, qdonep); /* * Get the Scsi_Cmnd structure and Scsi_Host structure for the * command that has been completed. */ - scp = (Scsi_Cmnd *) qdonep->d2.srb_ptr; - ASC_DBG1(1, "asc_isr_callback: scp %x\n", (unsigned) scp); + scp = (Scsi_Cmnd *) ASC_U32_TO_VADDR(qdonep->d2.srb_ptr); + ASC_DBG1(1, "asc_isr_callback: scp %lx\n", (ulong) scp); if (scp == NULL) { ASC_PRINT("asc_isr_callback: scp is NULL\n"); @@ -7557,13 +7631,14 @@ } } if (i == asc_board_count) { - ASC_PRINT2("asc_isr_callback: scp %x has bad host pointer, host %x\n", - (unsigned) scp, (unsigned) shp); + ASC_PRINT2( + "asc_isr_callback: scp %lx has bad host pointer, host %lx\n", + (ulong) scp, (ulong) shp); return; } ASC_STATS(shp, callback); - ASC_DBG1(1, "asc_isr_callback: shp %x\n", (unsigned) shp); + ASC_DBG1(1, "asc_isr_callback: shp %lx\n", (ulong) shp); /* * If the request isn't found on the active queue, it may @@ -7573,36 +7648,19 @@ boardp = ASC_BOARDP(shp); ASC_ASSERT(asc_dvc_varp == &boardp->dvc_var.asc_dvc_var); if (asc_rmqueue(&boardp->active, scp) == ASC_FALSE) { - ASC_PRINT2("asc_isr_callback: board %d: scp %x not on active queue\n", - boardp->id, (unsigned) scp); + ASC_PRINT2( + "asc_isr_callback: board %d: scp %lx not on active queue\n", + boardp->id, (ulong) scp); return; } /* - * Check for an underrun condition. - */ - if (scp->request_bufflen != 0 && qdonep->remain_bytes != 0 && - qdonep->remain_bytes <= scp->request_bufflen != 0) { - ASC_DBG1(1, "asc_isr_callback: underrun condition %u bytes\n", - (unsigned) qdonep->remain_bytes); - underrun = ASC_TRUE; - } - - /* * 'qdonep' contains the command's ending status. */ switch (qdonep->d3.done_stat) { case QD_NO_ERROR: ASC_DBG(2, "asc_isr_callback: QD_NO_ERROR\n"); - switch (qdonep->d3.host_stat) { - case QHSTA_NO_ERROR: - scp->result = 0; - break; - default: - /* QHSTA error occurred */ - scp->result = HOST_BYTE(DID_ERROR); - break; - } + scp->result = 0; /* * If an INQUIRY command completed successfully, then call @@ -7615,16 +7673,20 @@ (ASC_SCSI_INQUIRY *) scp->request_buffer); } +#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,3,19) /* - * If there was an underrun without any other error, - * set DID_ERROR to indicate the underrun error. + * Check for an underrun condition. * - * Note: There is no way yet to indicate the number - * of underrun bytes. + * If there was no error and an underrun condition, then + * then return the number of underrun bytes. */ - if (scp->result == 0 && underrun == ASC_TRUE) { - scp->result = HOST_BYTE(DID_UNDERRUN); + if (scp->request_bufflen != 0 && qdonep->remain_bytes != 0 && + qdonep->remain_bytes <= scp->request_bufflen != 0) { + ASC_DBG1(1, "asc_isr_callback: underrun condition %u bytes\n", + (unsigned) qdonep->remain_bytes); + scp->resid = qdonep->remain_bytes; } +#endif /* version >= v2.3.19 */ break; case QD_WITH_ERROR: @@ -7706,14 +7768,14 @@ { asc_board_t *boardp; adv_req_t *reqp; + adv_sgblk_t *sgblkp; Scsi_Cmnd *scp; struct Scsi_Host *shp; - int underrun = ASC_FALSE; int i; ASC_ASSERT(interrupts_enabled() == ASC_FALSE); - ASC_DBG2(1, "adv_isr_callback: adv_dvc_varp %x, scsiqp %x\n", - (unsigned) adv_dvc_varp, (unsigned) scsiqp); + ASC_DBG2(1, "adv_isr_callback: adv_dvc_varp %lx, scsiqp %lx\n", + (ulong) adv_dvc_varp, (ulong) scsiqp); ASC_DBG_PRT_ADV_SCSI_REQ_Q(2, scsiqp); /* @@ -7721,8 +7783,8 @@ * completed. The adv_req_t structure actually contains the * completed ADV_SCSI_REQ_Q structure. */ - reqp = (adv_req_t *) scsiqp->srb_ptr; - ASC_DBG1(1, "adv_isr_callback: reqp %x\n", (unsigned) reqp); + reqp = (adv_req_t *) ADV_U32_TO_VADDR(scsiqp->srb_ptr); + ASC_DBG1(1, "adv_isr_callback: reqp %lx\n", (ulong) reqp); if (reqp == NULL) { ASC_PRINT("adv_isr_callback: reqp is NULL\n"); return; @@ -7737,7 +7799,7 @@ * determined. */ scp = reqp->cmndp; - ASC_DBG1(1, "adv_isr_callback: scp %x\n", (unsigned) scp); + ASC_DBG1(1, "adv_isr_callback: scp %lx\n", (ulong) scp); if (scp == NULL) { ASC_PRINT("adv_isr_callback: scp is NULL; adv_req_t dropped.\n"); return; @@ -7759,13 +7821,14 @@ * structure and adv_sgblk_t structure, if any, is dropped. */ if (i == asc_board_count) { - ASC_PRINT2("adv_isr_callback: scp %x has bad host pointer, host %x\n", - (unsigned) scp, (unsigned) shp); + ASC_PRINT2( + "adv_isr_callback: scp %lx has bad host pointer, host %lx\n", + (ulong) scp, (ulong) shp); return; } ASC_STATS(shp, callback); - ASC_DBG1(1, "adv_isr_callback: shp %x\n", (unsigned) shp); + ASC_DBG1(1, "adv_isr_callback: shp %lx\n", (ulong) shp); /* * If the request isn't found on the active queue, it may have been @@ -7778,47 +7841,34 @@ boardp = ASC_BOARDP(shp); ASC_ASSERT(adv_dvc_varp == &boardp->dvc_var.adv_dvc_var); if (asc_rmqueue(&boardp->active, scp) == ASC_FALSE) { - ASC_PRINT2("adv_isr_callback: board %d: scp %x not on active queue\n", - boardp->id, (unsigned) scp); + ASC_PRINT2( + "adv_isr_callback: board %d: scp %lx not on active queue\n", + boardp->id, (ulong) scp); return; } /* - * Check for an underrun condition. - */ - if (scp->request_bufflen != 0 && scsiqp->data_cnt != 0) { - ASC_DBG1(1, "adv_isr_callback: underrun condition %lu bytes\n", - scsiqp->data_cnt); - underrun = ASC_TRUE; - } - - /* * 'done_status' contains the command's ending status. */ switch (scsiqp->done_status) { case QD_NO_ERROR: ASC_DBG(2, "adv_isr_callback: QD_NO_ERROR\n"); - switch (scsiqp->host_status) { - case QHSTA_NO_ERROR: - scp->result = 0; - break; - default: - /* QHSTA error occurred. */ - ASC_DBG1(2, "adv_isr_callback: host_status %x\n", - scsiqp->host_status); - scp->result = HOST_BYTE(DID_ERROR); - break; - } + scp->result = 0; + +#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,3,19) /* - * If there was an underrun without any other error, - * set DID_ERROR to indicate the underrun error. + * Check for an underrun condition. * - * Note: There is no way yet to indicate the number - * of underrun bytes. + * If there was no error and an underrun condition, then + * then return the number of underrun bytes. */ - if (scp->result == 0 && underrun == ASC_TRUE) { - scp->result = HOST_BYTE(DID_UNDERRUN); + if (scp->request_bufflen != 0 && scsiqp->data_cnt != 0 && + scsiqp->data_cnt <= scp->request_bufflen) { + ASC_DBG1(1, "adv_isr_callback: underrun condition %lu bytes\n", + (ulong) scsiqp->data_cnt); + scp->resid = scsiqp->data_cnt; } +#endif /* version >= v2.3.19 */ break; case QD_WITH_ERROR: @@ -7886,12 +7936,16 @@ asc_enqueue(&boardp->done, scp, ASC_BACK); /* - * Free the adv_sgblk_t structure, if any, by adding it back - * to the board free list. + * Free all 'adv_sgblk_t' structures allocated for the request. */ - if (reqp->sgblkp != NULL) { - reqp->sgblkp->next_sgblkp = boardp->adv_sgblkp; - boardp->adv_sgblkp = reqp->sgblkp; + while ((sgblkp = reqp->sgblkp) != NULL) + { + /* Remove 'sgblkp' from the request list. */ + reqp->sgblkp = sgblkp->next_sgblkp; + + /* Add 'sgblkp' to the board free list. */ + sgblkp->next_sgblkp = boardp->adv_sgblkp; + boardp->adv_sgblkp = sgblkp; } /* @@ -8112,15 +8166,15 @@ ) { ushort tmp; - ulong address; - ulong lbus = pciData->bus; - ulong lslot = pciData->slot; - ulong lfunc = pciData->func; + ADV_DCNT address; + ADV_DCNT lbus = pciData->bus; + ADV_DCNT lslot = pciData->slot; + ADV_DCNT lfunc = pciData->func; uchar t2CFA, t2CF8; - ulong t1CF8, t1CFC; + ADV_DCNT t1CF8, t1CFC; - ASC_DBG4(4, "asc_get_cfg_word: type %d, bus %lu, slot %lu, func %lu\n", - pciData->type, lbus, lslot, lfunc); + ASC_DBG4(4, "asc_get_cfg_word: type %d, bus %u, slot %u, func %u\n", + pciData->type, (unsigned) lbus, (unsigned) lslot, (unsigned) lfunc); /* * Check type of configuration mechanism. @@ -8160,7 +8214,7 @@ * enable <31>, bus = <23:16>, slot = <15:11>, * func = <10:8>, reg = <7:2> */ - address = (ulong) ((lbus << 16) | (lslot << 11) | + address = (ADV_DCNT) ((lbus << 16) | (lslot << 11) | (lfunc << 8) | (pciData->offset & 0xFC) | 0x80000000L); /* @@ -8194,11 +8248,11 @@ asc_get_cfg_byte(PCI_DATA *pciData) ) { - uchar tmp; - ulong address; - ulong lbus = pciData->bus, lslot = pciData->slot, lfunc = pciData->func; - uchar t2CFA, t2CF8; - ulong t1CF8, t1CFC; + uchar tmp; + ADV_DCNT address; + ADV_DCNT lbus = pciData->bus, lslot = pciData->slot, lfunc = pciData->func; + ADV_DCNT t2CFA, t2CF8; + ADV_DCNT t1CF8, t1CFC; ASC_DBG1(4, "asc_get_cfg_byte: type: %d\n", pciData->type); @@ -8243,7 +8297,7 @@ * enable <31>, bus = <23:16>, slot = <15:11>, func = <10:8>, * reg = <7:2> */ - address = (ulong) ((lbus << 16) | (lslot << 11) | + address = (ADV_DCNT) ((lbus << 16) | (lslot << 11) | (lfunc << 8) | (pciData->offset & 0xFC) | 0x80000000L); /* @@ -8274,11 +8328,11 @@ asc_put_cfg_byte(PCI_DATA *pciData, uchar byte_data) ) { - ulong tmpl; - ulong address; - ulong lbus = pciData->bus, lslot = pciData->slot, lfunc = pciData->func; - uchar t2CFA, t2CF8; - ulong t1CF8, t1CFC; + ADV_DCNT tmpl; + ADV_DCNT address; + ADV_DCNT lbus = pciData->bus, lslot = pciData->slot, lfunc = pciData->func; + uchar t2CFA, t2CF8; + ADV_DCNT t1CF8, t1CFC; ASC_DBG2(4, "asc_put_cfg_byte: type: %d, byte_data %x\n", pciData->type, byte_data); @@ -8328,7 +8382,7 @@ * enable <31>, bus = <23:16>, slot = <15:11>, func = <10:8>, * reg = <7:2> */ - address = (ulong) ((lbus << 16) | (lslot << 11) | (lfunc << 8) | + address = (ADV_DCNT) ((lbus << 16) | (lslot << 11) | (lfunc << 8) | (pciData->offset & 0xFC) | 0x80000000L); /* * Write out address to CONFIG_ADDRESS. @@ -8366,8 +8420,8 @@ { int tid; - ASC_DBG3(3, "asc_enqueue: ascq %x, reqp %x, flag %d\n", - (unsigned) ascq, (unsigned) reqp, flag); + ASC_DBG3(3, "asc_enqueue: ascq %lx, reqp %lx, flag %d\n", + (ulong) ascq, (ulong) reqp, flag); ASC_ASSERT(interrupts_enabled() == ASC_FALSE); ASC_ASSERT(reqp != NULL); ASC_ASSERT(flag == ASC_FRONT || flag == ASC_BACK); @@ -8404,7 +8458,7 @@ } REQPTIME(reqp) = REQTIMESTAMP(); #endif /* ADVANSYS_STATS */ - ASC_DBG1(3, "asc_enqueue: reqp %x\n", (unsigned) reqp); + ASC_DBG1(3, "asc_enqueue: reqp %lx\n", (ulong) reqp); return; } @@ -8420,7 +8474,7 @@ { REQP reqp; - ASC_DBG2(3, "asc_dequeue: ascq %x, tid %d\n", (unsigned) ascq, tid); + ASC_DBG2(3, "asc_dequeue: ascq %lx, tid %d\n", (ulong) ascq, tid); ASC_ASSERT(interrupts_enabled() == ASC_FALSE); ASC_ASSERT(tid >= 0 && tid <= ADV_MAX_TID); if ((reqp = ascq->q_first[tid]) != NULL) { @@ -8439,7 +8493,7 @@ REQTIMESTAT("asc_dequeue", ascq, reqp, tid); #endif /* ADVANSYS_STATS */ } - ASC_DBG1(3, "asc_dequeue: reqp %x\n", (unsigned) reqp); + ASC_DBG1(3, "asc_dequeue: reqp %lx\n", (ulong) reqp); return reqp; } @@ -8469,7 +8523,7 @@ REQP firstp, lastp; int i; - ASC_DBG2(3, "asc_dequeue_list: ascq %x, tid %d\n", (unsigned) ascq, tid); + ASC_DBG2(3, "asc_dequeue_list: ascq %lx, tid %d\n", (ulong) ascq, tid); ASC_ASSERT(interrupts_enabled() == ASC_FALSE); ASC_ASSERT((tid == ASC_TID_ALL) || (tid >= 0 && tid <= ADV_MAX_TID)); @@ -8530,7 +8584,7 @@ if (lastpp) { *lastpp = lastp; } - ASC_DBG1(3, "asc_dequeue_list: firstp %x\n", (unsigned) firstp); + ASC_DBG1(3, "asc_dequeue_list: firstp %lx\n", (ulong) firstp); return firstp; } @@ -8551,8 +8605,8 @@ int tid; int ret = ASC_FALSE; - ASC_DBG2(3, "asc_rmqueue: ascq %x, reqp %x\n", - (unsigned) ascq, (unsigned) reqp); + ASC_DBG2(3, "asc_rmqueue: ascq %lx, reqp %lx\n", + (ulong) ascq, (ulong) reqp); ASC_ASSERT(interrupts_enabled() == ASC_FALSE); ASC_ASSERT(reqp != NULL); @@ -8604,7 +8658,7 @@ } ASC_ASSERT(ascq->q_cur_cnt[tid] >= 0); #endif /* ADVANSYS_STATS */ - ASC_DBG2(3, "asc_rmqueue: reqp %x, ret %d\n", (unsigned) reqp, ret); + ASC_DBG2(3, "asc_rmqueue: reqp %lx, ret %d\n", (ulong) reqp, ret); return ret; } @@ -8619,8 +8673,8 @@ int tid; int ret = ASC_FALSE; - ASC_DBG2(3, "asc_isqueued: ascq %x, reqp %x\n", - (unsigned) ascq, (unsigned) reqp); + ASC_DBG2(3, "asc_isqueued: ascq %lx, reqp %lx\n", + (ulong) ascq, (ulong) reqp); ASC_ASSERT(interrupts_enabled() == ASC_FALSE); ASC_ASSERT(reqp != NULL); @@ -8650,7 +8704,7 @@ REQP reqp; int i; - ASC_DBG1(1, "asc_execute_queue: ascq %x\n", (unsigned) ascq); + ASC_DBG1(1, "asc_execute_queue: ascq %lx\n", (ulong) ascq); ASC_ASSERT(interrupts_enabled() == ASC_FALSE); /* * Execute queued commands for devices attached to @@ -8734,7 +8788,6 @@ int leftlen; int totlen; int len; - int upgrade = ASC_FALSE; ushort major, minor, letter; boardp = ASC_BOARDP(shp); @@ -8749,9 +8802,14 @@ * the BIOS code segment base address. */ if (boardp->bios_signature != 0x55AA) { - len = asc_prt_line(cp, leftlen, "Pre-3.1\n"); + len = asc_prt_line(cp, leftlen, "Disabled or Pre-3.1\n"); + ASC_PRT_NEXT(); + len = asc_prt_line(cp, leftlen, +"BIOS either disabled or Pre-3.1. If it is pre-3.1, then a newer version\n"); + ASC_PRT_NEXT(); + len = asc_prt_line(cp, leftlen, +"can be found at the AdvanSys FTP site: ftp://ftp.advansys.com/pub\n"); ASC_PRT_NEXT(); - upgrade = ASC_TRUE; } else { major = (boardp->bios_version >> 12) & 0xF; minor = (boardp->bios_version >> 8) & 0xF; @@ -8761,16 +8819,20 @@ major, minor, letter >= 26 ? '?' : letter + 'A'); ASC_PRT_NEXT(); - /* Current available ROM BIOS release is 3.1C. */ + /* + * Current available ROM BIOS release is 3.1I for UW + * and 3.2I for U2W. This code doesn't differentiate + * UW and U2W boards. + */ if (major < 3 || (major <= 3 && minor < 1) || - (major <= 3 && minor <= 1 && letter < ('C'- 'A'))) { - upgrade = ASC_TRUE; - } - } - if (upgrade == ASC_TRUE) { - len = asc_prt_line(cp, leftlen, -"Newer version of ROM BIOS available: ftp://ftp.advansys.com/pub\n"); + (major <= 3 && minor <= 1 && letter < ('I'- 'A'))) { + len = asc_prt_line(cp, leftlen, +"Newer version of ROM BIOS is available at the AdvanSys FTP site:\n"); + ASC_PRT_NEXT(); + len = asc_prt_line(cp, leftlen, +"ftp://ftp.advansys.com/pub\n"); ASC_PRT_NEXT(); + } } return totlen; @@ -9814,16 +9876,16 @@ if (offset <= advoffset) { /* Read offset below current offset, copy everything. */ cnt = ASC_MIN(cplen, leftlen); - ASC_DBG3(2, "asc_proc_copy: curbuf %x, cp %x, cnt %d\n", - (unsigned) curbuf, (unsigned) cp, cnt); + ASC_DBG3(2, "asc_proc_copy: curbuf %lx, cp %lx, cnt %d\n", + (ulong) curbuf, (ulong) cp, cnt); memcpy(curbuf, cp, cnt); } else if (offset < advoffset + cplen) { /* Read offset within current range, partial copy. */ cnt = (advoffset + cplen) - offset; cp = (cp + cplen) - cnt; cnt = ASC_MIN(cnt, leftlen); - ASC_DBG3(2, "asc_proc_copy: curbuf %x, cp %x, cnt %d\n", - (unsigned) curbuf, (unsigned) cp, cnt); + ASC_DBG3(2, "asc_proc_copy: curbuf %lx, cp %lx, cnt %d\n", + (ulong) curbuf, (ulong) cp, cnt); memcpy(curbuf, cp, cnt); } return cnt; @@ -9874,13 +9936,13 @@ * called when interrupts are disabled. */ STATIC void -DvcSleepMilliSecond(ulong n) +DvcSleepMilliSecond(ADV_DCNT n) { #if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,0) - ulong i; + ADV_DCNT i; #endif /* version < v2.1.0 */ - ASC_DBG1(4, "DvcSleepMilliSecond: %lu\n", n); + ASC_DBG1(4, "DvcSleepMilliSecond: %lu\n", (ulong) n); #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,0) mdelay(n); #else /* version < v2.1.0 */ @@ -9906,16 +9968,16 @@ restore_flags(flags); } -STATIC ulong -DvcGetSGList(ASC_DVC_VAR *asc_dvc_sg, uchar *buf_addr, ulong buf_len, +STATIC ADV_DCNT +DvcGetSGList(ASC_DVC_VAR *asc_dvc_sg, uchar *buf_addr, ADV_DCNT buf_len, ASC_SG_HEAD *asc_sg_head_ptr) { - ulong buf_size; + ADV_DCNT buf_size; buf_size = buf_len; asc_sg_head_ptr->entry_cnt = 1; #if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,0,0) - asc_sg_head_ptr->sg_list[0].addr = (ulong) buf_addr; + asc_sg_head_ptr->sg_list[0].addr = (ADV_PADDR) buf_addr; #else /* version >= v2.0.0 */ asc_sg_head_ptr->sg_list[0].addr = virt_to_bus(buf_addr); #endif /* version >= v2.0.0 */ @@ -10010,7 +10072,7 @@ } /* - * void DvcOutPortDWords(PortAddr port, ulong *pdw, int dwords) + * void DvcOutPortDWords(PortAddr port, ADV_DCNT *pdw, int dwords) * * Calling/Exit State: * none @@ -10020,10 +10082,10 @@ * 16 bit integer units */ STATIC void -DvcOutPortDWords(PortAddr port, ulong *pdw, int dwords) +DvcOutPortDWords(PortAddr port, ADV_DCNT *pdw, int dwords) { - int i; - int words; + int i; + int words; ushort *pw; pw = (ushort *) pdw; @@ -10040,7 +10102,7 @@ ASC_INITFUNC( STATIC uchar, DvcReadPCIConfigByte( - ASC_DVC_VAR asc_ptr_type *asc_dvc, + ASC_DVC_VAR *asc_dvc, ushort offset) ) { @@ -10077,7 +10139,7 @@ ASC_INITFUNC( STATIC void, DvcWritePCIConfigByte( - ASC_DVC_VAR asc_ptr_type *asc_dvc, + ASC_DVC_VAR *asc_dvc, ushort offset, uchar byte_data) ) @@ -10168,21 +10230,21 @@ * Note: Because Linux currently doesn't page the kernel and all * kernel buffers are physically contiguous, leave '*lenp' unchanged. */ -ulong +ADV_PADDR DvcGetPhyAddr(ADV_DVC_VAR *asc_dvc, ADV_SCSI_REQ_Q *scsiq, - uchar *vaddr, long *lenp, int flag) + uchar *vaddr, ADV_SDCNT *lenp, int flag) { - ulong paddr; + ADV_PADDR paddr; #if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,0,0) - paddr = (ulong) vaddr; + paddr = (ADV_PADDR) vaddr; #else /* version >= v2.0.0 */ paddr = virt_to_bus(vaddr); #endif /* version >= v2.0.0 */ ASC_DBG4(4, "DvcGetPhyAddr: vaddr 0x%lx, lenp 0x%lx *lenp %lu, paddr 0x%lx\n", - (ulong) vaddr, (ulong) lenp, (ulong) *((ulong *) lenp), paddr); + (ulong) vaddr, (ulong) lenp, (ulong) *((ulong *) lenp), (ulong) paddr); return paddr; } @@ -10432,16 +10494,15 @@ boardp = ASC_BOARDP(s); - printk("Scsi_Host at addr %x\n", (unsigned) s); + printk("Scsi_Host at addr %lx\n", (ulong) s); printk( -" next %x, extra_bytes %u, host_busy %u, host_no %d, last_reset %d,\n", - (unsigned) s->next, s->extra_bytes, s->host_busy, s->host_no, +" next %lx, extra_bytes %u, host_busy %u, host_no %d, last_reset %d,\n", + (ulong) s->next, s->extra_bytes, s->host_busy, s->host_no, (unsigned) s->last_reset); printk( -" host_wait %x, host_queue %x, hostt %x, block %x,\n", - (unsigned) s->host_wait, (unsigned) s->host_queue, - (unsigned) s->hostt, (unsigned) s->block); +" host_queue %lx, hostt %lx, block %lx,\n", + (ulong) s->host_queue, (ulong) s->hostt, (ulong) s->block); printk( " wish_block %d, base %lu, io_port %lu, n_io_port %u, irq %d,\n", @@ -10472,7 +10533,7 @@ STATIC void asc_prt_scsi_cmnd(Scsi_Cmnd *s) { - printk("Scsi_Cmnd at addr %x\n", (unsigned) s); + printk("Scsi_Cmnd at addr %lx\n", (ulong) s); #if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,0) printk( @@ -10480,8 +10541,8 @@ (unsigned) s->host, (unsigned) s->device, s->target, s->lun); #else /* version >= v1.3.0 */ printk( -" host %x, device %x, target %u, lun %u, channel %u,\n", - (unsigned) s->host, (unsigned) s->device, s->target, s->lun, +" host %lx, device %lx, target %u, lun %u, channel %u,\n", + (ulong) s->host, (ulong) s->device, s->target, s->lun, s->channel); #endif /* version >= v1.3.0 */ @@ -10511,9 +10572,9 @@ s->internal_timeout, s->flags, s->this_count); printk( -" scsi_done %x, done %x, host_scribble %x, result %x\n", - (unsigned) s->scsi_done, (unsigned) s->done, - (unsigned) s->host_scribble, s->result); +" scsi_done %lx, done %lx, host_scribble %lx, result %x\n", + (ulong) s->scsi_done, (ulong) s->done, + (ulong) s->host_scribble, s->result); printk( " tag %u, pid %u\n", @@ -10526,15 +10587,15 @@ STATIC void asc_prt_asc_dvc_var(ASC_DVC_VAR *h) { - printk("ASC_DVC_VAR at addr %x\n", (unsigned) h); + printk("ASC_DVC_VAR at addr %lx\n", (ulong) h); printk( " iop_base %x, err_code %x, dvc_cntl %x, bug_fix_cntl %d,\n", h->iop_base, h->err_code, h->dvc_cntl, h->bug_fix_cntl); printk( -" bus_type %d, isr_callback %x, exe_callback %x, init_sdtr %x,\n", - h->bus_type, (unsigned) h->isr_callback, (unsigned) h->exe_callback, +" bus_type %d, isr_callback %lx, exe_callback %lx, init_sdtr %x,\n", + h->bus_type, (ulong) h->isr_callback, (ulong) h->exe_callback, (unsigned) h->init_sdtr); printk( @@ -10558,8 +10619,8 @@ (unsigned) h->no_scam, (unsigned) h->pci_fix_asyn_xfer); printk( -" cfg %x, saved_ptr2func %x\n", - (unsigned) h->cfg, (unsigned) h->saved_ptr2func); +" cfg %lx\n", + (ulong) h->cfg); } /* @@ -10568,7 +10629,7 @@ STATIC void asc_prt_asc_dvc_cfg(ASC_DVC_CFG *h) { - printk("ASC_DVC_CFG at addr %x\n", (unsigned) h); + printk("ASC_DVC_CFG at addr %lx\n", (ulong) h); printk( " can_tagged_qng %x, cmd_qng_enabled %x, disc_enable %x, sdtr_enable %x,\n", @@ -10585,8 +10646,8 @@ h->pci_device_id, h->lib_serial_no, h->lib_version, h->mcode_date); printk( -" mcode_version %d, overrun_buf %x\n", - h->mcode_version, (unsigned) h->overrun_buf); +" mcode_version %d, overrun_buf %lx\n", + h->mcode_version, (ulong) h->overrun_buf); } /* @@ -10598,7 +10659,7 @@ ASC_SG_HEAD *sgp; int i; - printk("ASC_SCSI_Q at addr %x\n", (unsigned) q); + printk("ASC_SCSI_Q at addr %lx\n", (ulong) q); printk( " target_ix %u, target_lun %u, srb_ptr %x, tag_code %u,\n", @@ -10606,22 +10667,23 @@ (unsigned) q->q2.srb_ptr, q->q2.tag_code); printk( -" data_addr %x, data_cnt %lu, sense_addr %x, sense_len %u,\n", - (unsigned) q->q1.data_addr, q->q1.data_cnt, - (unsigned) q->q1.sense_addr, q->q1.sense_len); +" data_addr %lx, data_cnt %lu, sense_addr %lx, sense_len %u,\n", + (ulong) q->q1.data_addr, (ulong) q->q1.data_cnt, + (ulong) q->q1.sense_addr, q->q1.sense_len); printk( -" cdbptr %x, cdb_len %u, sg_head %x, sg_queue_cnt %u\n", - (unsigned) q->cdbptr, q->q2.cdb_len, - (unsigned) q->sg_head, q->q1.sg_queue_cnt); +" cdbptr %lx, cdb_len %u, sg_head %lx, sg_queue_cnt %u\n", + (ulong) q->cdbptr, q->q2.cdb_len, + (ulong) q->sg_head, q->q1.sg_queue_cnt); if (q->sg_head) { sgp = q->sg_head; - printk("ASC_SG_HEAD at addr %x\n", (unsigned) sgp); + printk("ASC_SG_HEAD at addr %lx\n", (ulong) sgp); printk(" entry_cnt %u, queue_cnt %u\n", sgp->entry_cnt, sgp->queue_cnt); for (i = 0; i < sgp->entry_cnt; i++) { - printk(" [%u]: addr %x, bytes %lu\n", - i, (unsigned) sgp->sg_list[i].addr, sgp->sg_list[i].bytes); + printk(" [%u]: addr %lx, bytes %lu\n", + i, (ulong) sgp->sg_list[i].addr, + (ulong) sgp->sg_list[i].bytes); } } @@ -10633,7 +10695,7 @@ STATIC void asc_prt_asc_qdone_info(ASC_QDONE_INFO *q) { - printk("ASC_QDONE_INFO at addr %x\n", (unsigned) q); + printk("ASC_QDONE_INFO at addr %lx\n", (ulong) q); printk( " srb_ptr %x, target_ix %u, cdb_len %u, tag_code %u, done_stat %x\n", (unsigned) q->d2.srb_ptr, q->d2.target_ix, q->d2.cdb_len, @@ -10658,8 +10720,8 @@ (ulong) h->iop_base, h->err_code, (unsigned) h->ultra_able); printk( -" isr_callback 0x%x, sdtr_able 0x%x, wdtr_able 0x%x\n", - (unsigned) h->isr_callback, (unsigned) h->wdtr_able, +" isr_callback 0x%lx, sdtr_able 0x%x, wdtr_able 0x%x\n", + (ulong) h->isr_callback, (unsigned) h->wdtr_able, (unsigned) h->sdtr_able); printk( @@ -10672,11 +10734,9 @@ (unsigned) h->max_host_qng, (unsigned) h->max_dvc_qng, (ulong) h->carr_freelist); - printk( " icq_sp %lx, irq_sp %lx\n", - (ulong) h->icq_sp, - (ulong) h->irq_sp); + (ulong) h->icq_sp, (ulong) h->irq_sp); printk( " no_scam 0x%x, tagqng_able 0x%x\n", @@ -10725,34 +10785,31 @@ int sg_blk_cnt; struct asc_sg_block *sg_ptr; - printk("ADV_SCSI_REQ_Q at addr %x\n", (unsigned) q); + printk("ADV_SCSI_REQ_Q at addr %lx\n", (ulong) q); printk( " target_id %u, target_lun %u, srb_ptr 0x%lx, a_flag 0x%x\n", - q->target_id, q->target_lun, q->srb_ptr, q->a_flag); + q->target_id, q->target_lun, (ulong) q->srb_ptr, q->a_flag); printk(" cntl 0x%x, data_addr 0x%lx, vdata_addr 0x%lx\n", - q->cntl, q->data_addr, q->vdata_addr); - - printk( -" cntl 0x%x, data_addr %lx, vdata_addr %lx\n", - q->cntl, q->data_addr, q->vdata_addr); + q->cntl, (ulong) q->data_addr, (ulong) q->vdata_addr); printk( " data_cnt %lu, sense_addr 0x%lx, sense_len %u,\n", - q->data_cnt, q->sense_addr, q->sense_len); + (ulong) q->data_cnt, (ulong) q->sense_addr, q->sense_len); printk( " cdb_len %u, done_status 0x%x, host_status 0x%x, scsi_status 0x%x\n", q->cdb_len, q->done_status, q->host_status, q->scsi_status); printk( -" sg_working_ix %x, sg_working_data_cnt %lx, reserved %u\n", - q->sg_working_ix, q->sg_working_data_cnt, q->reserved); +" sg_working_ix %x, sg_working_data_cnt %lx, target_cmd %u\n", + q->sg_working_ix, (ulong) q->sg_working_data_cnt, q->target_cmd); printk( " scsiq_rptr %lx, sg_real_addr %lx, sg_list_ptr %lx\n", - q->scsiq_rptr, q->sg_real_addr, (ulong) q->sg_list_ptr); + (ulong) q->scsiq_rptr, (ulong) q->sg_real_addr, + (ulong) q->sg_list_ptr); /* Display the request's ADV_SG_BLOCK structures. */ if (q->sg_list_ptr != NULL) @@ -10768,7 +10825,7 @@ */ sg_ptr = &(((ADV_SG_BLOCK *) (q->sg_list_ptr))[sg_blk_cnt]); asc_prt_adv_sgblock(sg_blk_cnt, sg_ptr); - if (sg_ptr->sg_ptr == NULL) + if (sg_ptr->sg_ptr == 0) { break; } @@ -10792,13 +10849,13 @@ printk(" sg_cnt %u, sg_ptr %lx\n", b->sg_cnt, (ulong) b->sg_ptr); ASC_ASSERT(b->sg_cnt <= NO_OF_SG_PER_BLOCK); - if (b->sg_ptr != NULL) + if (b->sg_ptr != 0) { ASC_ASSERT(b->sg_cnt == NO_OF_SG_PER_BLOCK); } for (i = 0; i < b->sg_cnt; i++) { printk(" [%u]: sg_addr %lx, sg_count %lx\n", - i, b->sg_list[i].sg_addr, b->sg_list[i].sg_count); + i, (ulong) b->sg_list[i].sg_addr, (ulong) b->sg_list[i].sg_count); } } @@ -10989,7 +11046,7 @@ } ASC_INITFUNC( -STATIC ulong, +STATIC ASC_DCNT, AscLoadMicroCode( PortAddr iop_base, ushort s_addr, @@ -10998,7 +11055,7 @@ ) ) { - ulong chksum; + ASC_DCNT chksum; ushort mcode_word_size; ushort mcode_chksum; @@ -11227,7 +11284,7 @@ STATIC int AscIsrChipHalted( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc + ASC_DVC_VAR *asc_dvc ) { EXT_MSG ext_msg; @@ -11247,10 +11304,10 @@ uchar cur_dvc_qng; uchar asyn_sdtr; uchar scsi_status; - asc_board_t *boardp; + asc_board_t *boardp; - ASC_ASSERT(asc_dvc->drv_ptr != 0); - boardp = (asc_board_t *) asc_dvc->drv_ptr; + ASC_ASSERT(asc_dvc->drv_ptr != NULL); + boardp = asc_dvc->drv_ptr; iop_base = asc_dvc->iop_base; int_halt_code = AscReadLramWord(iop_base, ASCV_HALTCODE_W); @@ -11497,162 +11554,162 @@ return (0); } else if (int_halt_code == ASC_HALT_HOST_COPY_SG_LIST_TO_RISC) { - uchar q_no; - ushort q_addr; - ulong srb_ptr; - uchar sg_wk_q_no; - uchar first_sg_wk_q_no; - ASC_SCSI_Q *scsiq; /* Ptr to driver request. */ - ASC_SG_HEAD *sg_head; /* Ptr to driver SG request. */ - ASC_SG_LIST_Q scsi_sg_q; /* Structure written to queue. */ - ushort sg_list_dwords; - ushort sg_entry_cnt; - uchar next_qp; - int i; - - q_no = AscReadLramByte(iop_base, (ushort) ASCV_REQ_SG_LIST_QP); - if (q_no == ASC_QLINK_END) - { - return(0); - } - - q_addr = ASC_QNO_TO_QADDR(q_no); - - /* Read request's SRB pointer. */ - srb_ptr = AscReadLramDWord(iop_base, - (ushort) (q_addr + ASC_SCSIQ_D_SRBPTR)); + uchar q_no; + ushort q_addr; + uchar sg_wk_q_no; + uchar first_sg_wk_q_no; + ASC_SCSI_Q *scsiq; /* Ptr to driver request. */ + ASC_SG_HEAD *sg_head; /* Ptr to driver SG request. */ + ASC_SG_LIST_Q scsi_sg_q; /* Structure written to queue. */ + ushort sg_list_dwords; + ushort sg_entry_cnt; + uchar next_qp; + int i; - /* - * Get request's first and working SG queue. - */ - sg_wk_q_no = AscReadLramByte(iop_base, - (ushort) (q_addr + ASC_SCSIQ_B_SG_WK_QP)); + q_no = AscReadLramByte(iop_base, (ushort) ASCV_REQ_SG_LIST_QP); + if (q_no == ASC_QLINK_END) + { + return(0); + } - first_sg_wk_q_no = AscReadLramByte(iop_base, - (ushort) (q_addr + ASC_SCSIQ_B_FIRST_SG_WK_QP)); + q_addr = ASC_QNO_TO_QADDR(q_no); - /* - * Reset request's working SG queue back to the - * first SG queue. - */ - AscWriteLramByte(iop_base, - (ushort) (q_addr + (ushort) ASC_SCSIQ_B_SG_WK_QP), - first_sg_wk_q_no); + /* + * Convert the request's SRB pointer to a host ASC_SCSI_REQ + * structure pointer using a macro provided by the driver. + * The ASC_SCSI_REQ pointer provides a pointer to the + * host ASC_SG_HEAD structure. + */ + /* Read request's SRB pointer. */ + scsiq = (ASC_SCSI_Q *) + ASC_SRB2SCSIQ( + ASC_U32_TO_VADDR(AscReadLramDWord(iop_base, + (ushort) (q_addr + ASC_SCSIQ_D_SRBPTR)))); - /* - * Convert the request's SRB pointer to a host ASC_SCSI_REQ - * structure pointer using a macro provided by the driver. - * The ASC_SCSI_REQ pointer provides a pointer to the - * host ASC_SG_HEAD structure. - */ - scsiq = (ASC_SCSI_Q *) ASC_SRB2SCSIQ(srb_ptr); + /* + * Get request's first and working SG queue. + */ + sg_wk_q_no = AscReadLramByte(iop_base, + (ushort) (q_addr + ASC_SCSIQ_B_SG_WK_QP)); - sg_head = scsiq->sg_head; + first_sg_wk_q_no = AscReadLramByte(iop_base, + (ushort) (q_addr + ASC_SCSIQ_B_FIRST_SG_WK_QP)); - /* - * Set sg_entry_cnt to the number of SG elements - * that will be completed on this interrupt. - * - * Note: The allocated SG queues contain ASC_MAX_SG_LIST - 1 - * SG elements. The data_cnt and data_addr fields which - * add 1 to the SG element capacity are not used when - * restarting SG handling after a halt. - */ - if (scsiq->remain_sg_entry_cnt > (ASC_MAX_SG_LIST - 1)) - { - sg_entry_cnt = ASC_MAX_SG_LIST - 1; - - /* - * Keep track of remaining number of SG elements that will - * need to be handled on the next interrupt. - */ - scsiq->remain_sg_entry_cnt -= (ASC_MAX_SG_LIST - 1); - } else - { - sg_entry_cnt = scsiq->remain_sg_entry_cnt; - scsiq->remain_sg_entry_cnt = 0; - } + /* + * Reset request's working SG queue back to the + * first SG queue. + */ + AscWriteLramByte(iop_base, + (ushort) (q_addr + (ushort) ASC_SCSIQ_B_SG_WK_QP), + first_sg_wk_q_no); - /* - * Copy SG elements into the list of allocated SG queues. - * - * Last index completed is saved in scsiq->next_sg_index. - */ - next_qp = first_sg_wk_q_no; - q_addr = ASC_QNO_TO_QADDR(next_qp); - scsi_sg_q.sg_head_qp = q_no; - scsi_sg_q.cntl = QCSG_SG_XFER_LIST; - for( i = 0; i < sg_head->queue_cnt; i++) - { - scsi_sg_q.seq_no = i + 1; - if (sg_entry_cnt > ASC_SG_LIST_PER_Q) - { - sg_list_dwords = (uchar) (ASC_SG_LIST_PER_Q * 2); - sg_entry_cnt -= ASC_SG_LIST_PER_Q; - /* - * After very first SG queue RISC FW uses next - * SG queue first element then checks sg_list_cnt - * against zero and then decrements, so set - * sg_list_cnt 1 less than number of SG elements - * in each SG queue. - */ - scsi_sg_q.sg_list_cnt = ASC_SG_LIST_PER_Q - 1; - scsi_sg_q.sg_cur_list_cnt = ASC_SG_LIST_PER_Q - 1; - } else { - /* - * This is the last SG queue in the list of - * allocated SG queues. If there are more - * SG elements than will fit in the allocated - * queues, then set the QCSG_SG_XFER_MORE flag. - */ - if (scsiq->remain_sg_entry_cnt != 0) - { - scsi_sg_q.cntl |= QCSG_SG_XFER_MORE; - } else - { - scsi_sg_q.cntl |= QCSG_SG_XFER_END; - } - /* equals sg_entry_cnt * 2 */ - sg_list_dwords = sg_entry_cnt << 1; - scsi_sg_q.sg_list_cnt = sg_entry_cnt - 1; - scsi_sg_q.sg_cur_list_cnt = sg_entry_cnt - 1; - sg_entry_cnt = 0; - } - - scsi_sg_q.q_no = next_qp; - AscMemWordCopyToLram(iop_base, - (ushort) (q_addr+ASC_SCSIQ_SGHD_CPY_BEG), - (ushort *) &scsi_sg_q, - (ushort) (sizeof(ASC_SG_LIST_Q) >> 1)); - - AscMemDWordCopyToLram( iop_base, - (ushort) (q_addr+ASC_SGQ_LIST_BEG ), - (ulong *) &sg_head->sg_list[scsiq->next_sg_index], - (ushort) sg_list_dwords); - - scsiq->next_sg_index += ASC_SG_LIST_PER_Q; - - /* - * If the just completed SG queue contained the - * last SG element, then no more SG queues need - * to be written. - */ - if (scsi_sg_q.cntl & QCSG_SG_XFER_END) - { - break; - } - - next_qp = AscReadLramByte( iop_base, - ( ushort )( q_addr+ASC_SCSIQ_B_FWD ) ); - q_addr = ASC_QNO_TO_QADDR( next_qp ); - } + sg_head = scsiq->sg_head; - /* - * Clear the halt condition so the RISC will be restarted - * after the return. - */ - AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0); - return(0); + /* + * Set sg_entry_cnt to the number of SG elements + * that will be completed on this interrupt. + * + * Note: The allocated SG queues contain ASC_MAX_SG_LIST - 1 + * SG elements. The data_cnt and data_addr fields which + * add 1 to the SG element capacity are not used when + * restarting SG handling after a halt. + */ + if (scsiq->remain_sg_entry_cnt > (ASC_MAX_SG_LIST - 1)) + { + sg_entry_cnt = ASC_MAX_SG_LIST - 1; + + /* + * Keep track of remaining number of SG elements that will + * need to be handled on the next interrupt. + */ + scsiq->remain_sg_entry_cnt -= (ASC_MAX_SG_LIST - 1); + } else + { + sg_entry_cnt = scsiq->remain_sg_entry_cnt; + scsiq->remain_sg_entry_cnt = 0; + } + + /* + * Copy SG elements into the list of allocated SG queues. + * + * Last index completed is saved in scsiq->next_sg_index. + */ + next_qp = first_sg_wk_q_no; + q_addr = ASC_QNO_TO_QADDR(next_qp); + scsi_sg_q.sg_head_qp = q_no; + scsi_sg_q.cntl = QCSG_SG_XFER_LIST; + for( i = 0; i < sg_head->queue_cnt; i++) + { + scsi_sg_q.seq_no = i + 1; + if (sg_entry_cnt > ASC_SG_LIST_PER_Q) + { + sg_list_dwords = (uchar) (ASC_SG_LIST_PER_Q * 2); + sg_entry_cnt -= ASC_SG_LIST_PER_Q; + /* + * After very first SG queue RISC FW uses next + * SG queue first element then checks sg_list_cnt + * against zero and then decrements, so set + * sg_list_cnt 1 less than number of SG elements + * in each SG queue. + */ + scsi_sg_q.sg_list_cnt = ASC_SG_LIST_PER_Q - 1; + scsi_sg_q.sg_cur_list_cnt = ASC_SG_LIST_PER_Q - 1; + } else { + /* + * This is the last SG queue in the list of + * allocated SG queues. If there are more + * SG elements than will fit in the allocated + * queues, then set the QCSG_SG_XFER_MORE flag. + */ + if (scsiq->remain_sg_entry_cnt != 0) + { + scsi_sg_q.cntl |= QCSG_SG_XFER_MORE; + } else + { + scsi_sg_q.cntl |= QCSG_SG_XFER_END; + } + /* equals sg_entry_cnt * 2 */ + sg_list_dwords = sg_entry_cnt << 1; + scsi_sg_q.sg_list_cnt = sg_entry_cnt - 1; + scsi_sg_q.sg_cur_list_cnt = sg_entry_cnt - 1; + sg_entry_cnt = 0; + } + + scsi_sg_q.q_no = next_qp; + AscMemWordCopyToLram(iop_base, + (ushort) (q_addr+ASC_SCSIQ_SGHD_CPY_BEG), + (ushort *) &scsi_sg_q, + (ushort) (sizeof(ASC_SG_LIST_Q) >> 1)); + + AscMemDWordCopyToLram( iop_base, + (ushort) (q_addr+ASC_SGQ_LIST_BEG ), + (ADV_PADDR *) + &sg_head->sg_list[scsiq->next_sg_index], + (ushort) sg_list_dwords); + + scsiq->next_sg_index += ASC_SG_LIST_PER_Q; + + /* + * If the just completed SG queue contained the + * last SG element, then no more SG queues need + * to be written. + */ + if (scsi_sg_q.cntl & QCSG_SG_XFER_END) + { + break; + } + + next_qp = AscReadLramByte( iop_base, + ( ushort )( q_addr+ASC_SCSIQ_B_FWD ) ); + q_addr = ASC_QNO_TO_QADDR( next_qp ); + } + + /* + * Clear the halt condition so the RISC will be restarted + * after the return. + */ + AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0); + return(0); } return (0); } @@ -11661,8 +11718,8 @@ _AscCopyLramScsiDoneQ( PortAddr iop_base, ushort q_addr, - REG ASC_QDONE_INFO * scsiq, - ulong max_dma_count + ASC_QDONE_INFO * scsiq, + ASC_DCNT max_dma_count ) { ushort _val; @@ -11688,7 +11745,7 @@ /* * Read high word of remain bytes from alternate location. */ - scsiq->remain_bytes = (((ulong) AscReadLramWord( iop_base, + scsiq->remain_bytes = (((ADV_DCNT) AscReadLramWord( iop_base, (ushort) (q_addr+ (ushort) ASC_SCSIQ_W_ALT_DC1))) << 16); /* * Read low word of remain bytes from original location. @@ -11702,7 +11759,7 @@ STATIC int AscIsrQDone( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc + ASC_DVC_VAR *asc_dvc ) { uchar next_qp; @@ -11719,12 +11776,12 @@ ushort sg_q_addr; uchar cur_target_qng; ASC_QDONE_INFO scsiq_buf; - REG ASC_QDONE_INFO *scsiq; + ASC_QDONE_INFO *scsiq; int false_overrun; ASC_ISR_CALLBACK asc_isr_callback; iop_base = asc_dvc->iop_base; - asc_isr_callback = (ASC_ISR_CALLBACK) asc_dvc->isr_callback; + asc_isr_callback = asc_dvc->isr_callback; n_q_used = 1; scsiq = (ASC_QDONE_INFO *) & scsiq_buf; done_q_tail = (uchar) AscGetVarDoneQTail(iop_base); @@ -11789,7 +11846,7 @@ } else if (scsiq->q_status == QS_DONE) { false_overrun = FALSE; if (scsiq->extra_bytes != 0) { - scsiq->remain_bytes += (ulong) scsiq->extra_bytes; + scsiq->remain_bytes += (ADV_DCNT) scsiq->extra_bytes; } if (scsiq->d3.done_stat == QD_WITH_ERROR) { if (scsiq->d3.host_stat == QHSTA_M_DATA_OVER_RUN) { @@ -11839,7 +11896,7 @@ STATIC int AscISR( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc + ASC_DVC_VAR *asc_dvc ) { ASC_CS_TYPE chipstat; @@ -12084,7 +12141,7 @@ }; STATIC ushort _asc_mcode_size ASC_INITDATA = sizeof(_asc_mcode_buf); -STATIC ulong _asc_mcode_chksum ASC_INITDATA = 0x012C453FUL; +STATIC ADV_DCNT _asc_mcode_chksum ASC_INITDATA = 0x012C453FUL; #define ASC_SYN_OFFSET_ONE_DISABLE_LIST 16 STATIC uchar _syn_offset_one_disable_cmd[ASC_SYN_OFFSET_ONE_DISABLE_LIST] = @@ -12109,8 +12166,8 @@ STATIC int AscExeScsiQueue( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, - REG ASC_SCSI_Q * scsiq + ASC_DVC_VAR *asc_dvc, + ASC_SCSI_Q *scsiq ) { PortAddr iop_base; @@ -12119,7 +12176,7 @@ int n_q_required; int disable_syn_offset_one_fix; int i; - ulong addr; + ASC_PADDR addr; ASC_EXE_CALLBACK asc_exe_callback; ushort sg_entry_cnt = 0; ushort sg_entry_cnt_minus_one = 0; @@ -12129,12 +12186,12 @@ uchar extra_bytes; uchar scsi_cmd; uchar disable_cmd; - ASC_SG_HEAD *sg_head; - ulong data_cnt; + ASC_SG_HEAD *sg_head; + ASC_DCNT data_cnt; iop_base = asc_dvc->iop_base; sg_head = scsiq->sg_head; - asc_exe_callback = (ASC_EXE_CALLBACK) asc_dvc->exe_callback; + asc_exe_callback = asc_dvc->exe_callback; if (asc_dvc->err_code != 0) return (ERR); if (scsiq == (ASC_SCSI_Q *) 0L) { @@ -12180,8 +12237,8 @@ } #endif /* !CC_VERY_LONG_SG_LIST */ if (sg_entry_cnt == 1) { - scsiq->q1.data_addr = (ulong) sg_head->sg_list[0].addr; - scsiq->q1.data_cnt = (ulong) sg_head->sg_list[0].bytes; + scsiq->q1.data_addr = (ADV_PADDR) sg_head->sg_list[0].addr; + scsiq->q1.data_cnt = (ADV_DCNT) sg_head->sg_list[0].bytes; scsiq->q1.cntl &= ~(QC_SG_HEAD | QC_SG_SWAP_QUEUE); } sg_entry_cnt_minus_one = sg_entry_cnt - 1; @@ -12193,7 +12250,7 @@ if (scsiq->q1.cntl & QC_SG_HEAD) { data_cnt = 0; for (i = 0; i < sg_entry_cnt; i++) { - data_cnt += (ulong) sg_head->sg_list[i].bytes; + data_cnt += (ADV_DCNT) sg_head->sg_list[i].bytes; } } else { data_cnt = scsiq->q1.data_cnt; @@ -12228,8 +12285,10 @@ if ((scsi_cmd == SCSICMD_Read6) || (scsi_cmd == SCSICMD_Read10)) { addr = - (ulong) sg_head->sg_list[sg_entry_cnt_minus_one].addr + - (ulong) sg_head->sg_list[sg_entry_cnt_minus_one].bytes; + (ADV_PADDR) + sg_head->sg_list[sg_entry_cnt_minus_one].addr + + (ADV_DCNT) + sg_head->sg_list[sg_entry_cnt_minus_one].bytes; extra_bytes = (uchar) ((ushort) addr & 0x0003); if ((extra_bytes != 0) && ((scsiq->q2.tag_code & ASC_TAG_FLAG_EXTRA_BYTES) @@ -12237,7 +12296,7 @@ scsiq->q2.tag_code |= ASC_TAG_FLAG_EXTRA_BYTES; scsiq->q1.extra_bytes = extra_bytes; sg_head->sg_list[sg_entry_cnt_minus_one].bytes -= - (ulong) extra_bytes; + (ASC_DCNT) extra_bytes; } } } @@ -12277,7 +12336,7 @@ == 0)) { if (((ushort) scsiq->q1.data_cnt & 0x01FF) == 0) { scsiq->q2.tag_code |= ASC_TAG_FLAG_EXTRA_BYTES; - scsiq->q1.data_cnt -= (ulong) extra_bytes; + scsiq->q1.data_cnt -= (ASC_DCNT) extra_bytes; scsiq->q1.extra_bytes = extra_bytes; } } @@ -12305,8 +12364,8 @@ STATIC int AscSendScsiQueue( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, - REG ASC_SCSI_Q * scsiq, + ASC_DVC_VAR *asc_dvc, + ASC_SCSI_Q *scsiq, uchar n_q_required ) { @@ -12369,7 +12428,7 @@ STATIC uint AscGetNumOfFreeQueue( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, + ASC_DVC_VAR *asc_dvc, uchar target_ix, uchar n_qs ) @@ -12411,8 +12470,8 @@ STATIC int AscPutReadyQueue( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, - REG ASC_SCSI_Q * scsiq, + ASC_DVC_VAR *asc_dvc, + ASC_SCSI_Q *scsiq, uchar q_no ) { @@ -12456,8 +12515,8 @@ STATIC int AscPutReadySgListQueue( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, - REG ASC_SCSI_Q * scsiq, + ASC_DVC_VAR *asc_dvc, + ASC_SCSI_Q *scsiq, uchar q_no ) { @@ -12465,8 +12524,8 @@ int i; ASC_SG_HEAD *sg_head; ASC_SG_LIST_Q scsi_sg_q; - ulong saved_data_addr; - ulong saved_data_cnt; + ASC_DCNT saved_data_addr; + ASC_DCNT saved_data_cnt; PortAddr iop_base; ushort sg_list_dwords; ushort sg_index; @@ -12478,8 +12537,8 @@ sg_head = scsiq->sg_head; saved_data_addr = scsiq->q1.data_addr; saved_data_cnt = scsiq->q1.data_cnt; - scsiq->q1.data_addr = (ulong) sg_head->sg_list[0].addr; - scsiq->q1.data_cnt = (ulong) sg_head->sg_list[0].bytes; + scsiq->q1.data_addr = (ASC_PADDR) sg_head->sg_list[0].addr; + scsiq->q1.data_cnt = (ASC_DCNT) sg_head->sg_list[0].bytes; /* * If sg_head->entry_cnt is greater than ASC_MAX_SG_LIST * then not all SG elements will fit in the allocated queues. @@ -12564,7 +12623,7 @@ (ushort) (sizeof (ASC_SG_LIST_Q) >> 1)); AscMemDWordCopyToLram(iop_base, (ushort) (q_addr + ASC_SGQ_LIST_BEG), - (ulong *) & sg_head->sg_list[sg_index], + (ADV_PADDR *) &sg_head->sg_list[sg_index], (ushort) sg_list_dwords); sg_index += ASC_SG_LIST_PER_Q; scsiq->next_sg_index = sg_index; @@ -12580,8 +12639,8 @@ STATIC int AscAbortSRB( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, - ulong srb_ptr + ASC_DVC_VAR *asc_dvc, + ADV_VADDR srb_ptr ) { int sta; @@ -12610,7 +12669,7 @@ #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89) STATIC int AscResetDevice( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, + ASC_DVC_VAR *asc_dvc, uchar target_ix ) { @@ -12683,7 +12742,7 @@ STATIC int AscResetSB( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc + ASC_DVC_VAR *asc_dvc ) { int sta; @@ -12697,7 +12756,7 @@ AscStopQueueExe(iop_base); asc_dvc->sdtr_done = 0; AscResetChipAndScsiBus(asc_dvc); - DvcSleepMilliSecond((ulong) ((ushort) asc_dvc->scsi_reset_wait * 1000)); + DvcSleepMilliSecond((ASC_DCNT) ((ushort) asc_dvc->scsi_reset_wait * 1000)); AscReInitLram(asc_dvc); for (i = 0; i <= ASC_MAX_TID; i++) { asc_dvc->cur_dvc_qng[i] = 0; @@ -12772,7 +12831,7 @@ STATIC int AscReInitLram( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc + ASC_DVC_VAR *asc_dvc ) { AscInitLram(asc_dvc); @@ -12782,7 +12841,7 @@ STATIC ushort AscInitLram( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc + ASC_DVC_VAR *asc_dvc ) { uchar i; @@ -12835,7 +12894,7 @@ STATIC ushort AscInitQLinkVar( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc + ASC_DVC_VAR *asc_dvc ) { PortAddr iop_base; @@ -12868,7 +12927,7 @@ STATIC int AscSetLibErrorCode( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, + ASC_DVC_VAR *asc_dvc, ushort err_code ) { @@ -12885,7 +12944,7 @@ STATIC int _AscWaitQDone( PortAddr iop_base, - REG ASC_SCSI_Q * scsiq + ASC_SCSI_Q * scsiq ) { ushort q_addr; @@ -12907,7 +12966,7 @@ STATIC uchar AscMsgOutSDTR( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, + ASC_DVC_VAR *asc_dvc, uchar sdtr_period, uchar sdtr_offset ) @@ -12944,7 +13003,7 @@ STATIC uchar AscCalSDTRData( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, + ASC_DVC_VAR *asc_dvc, uchar sdtr_period, uchar syn_offset ) @@ -12976,11 +13035,11 @@ STATIC uchar AscGetSynPeriodIndex( - ASC_DVC_VAR asc_ptr_type * asc_dvc, - ruchar syn_time + ASC_DVC_VAR *asc_dvc, + uchar syn_time ) { - ruchar *period_table; + uchar *period_table; int max_index; int min_index; int i; @@ -13041,8 +13100,8 @@ STATIC int AscRiscHaltedAbortSRB( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, - ulong srb_ptr + ASC_DVC_VAR *asc_dvc, + ASC_VADDR srb_ptr ) { PortAddr iop_base; @@ -13054,7 +13113,7 @@ int last_int_level; iop_base = asc_dvc->iop_base; - asc_isr_callback = (ASC_ISR_CALLBACK) asc_dvc->isr_callback; + asc_isr_callback = asc_dvc->isr_callback; last_int_level = DvcEnterCritical(); scsiq = (ASC_QDONE_INFO *) & scsiq_buf; for (q_no = ASC_MIN_ACTIVE_QNO; q_no <= asc_dvc->max_total_qng; @@ -13087,7 +13146,7 @@ #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89) STATIC int AscRiscHaltedAbortTIX( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, + ASC_DVC_VAR *asc_dvc, uchar target_ix ) { @@ -13100,7 +13159,7 @@ int last_int_level; iop_base = asc_dvc->iop_base; - asc_isr_callback = (ASC_ISR_CALLBACK) asc_dvc->isr_callback; + asc_isr_callback = asc_dvc->isr_callback; last_int_level = DvcEnterCritical(); scsiq = (ASC_QDONE_INFO *) & scsiq_buf; for (q_no = ASC_MIN_ACTIVE_QNO; q_no <= asc_dvc->max_total_qng; @@ -13235,7 +13294,7 @@ STATIC int AscWaitTixISRDone( - ASC_DVC_VAR asc_ptr_type * asc_dvc, + ASC_DVC_VAR *asc_dvc, uchar target_ix ) { @@ -13258,7 +13317,7 @@ STATIC int AscWaitISRDone( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc + ASC_DVC_VAR *asc_dvc ) { int tid; @@ -13269,24 +13328,24 @@ return (1); } -STATIC ulong +STATIC ASC_PADDR AscGetOnePhyAddr( - REG ASC_DVC_VAR asc_ptr_type * asc_dvc, + ASC_DVC_VAR *asc_dvc, uchar * buf_addr, - ulong buf_size + ASC_DCNT buf_size ) { ASC_MIN_SG_HEAD sg_head; sg_head.entry_cnt = ASC_MIN_SG_LIST; if (DvcGetSGList(asc_dvc, (uchar *) buf_addr, - buf_size, (ASC_SG_HEAD *) & sg_head) != buf_size) { + buf_size, (ASC_SG_HEAD *) &sg_head) != buf_size) { return (0L); } if (sg_head.entry_cnt > 1) { return (0L); } - return ((ulong) sg_head.sg_list[0].addr); + return ((ASC_PADDR) sg_head.sg_list[0].addr); } STATIC void @@ -13296,13 +13355,13 @@ } STATIC void -DvcDelayNanoSecond(ASC_DVC_VAR asc_ptr_type * asc_dvc, ulong nano_sec) +DvcDelayNanoSecond(ASC_DVC_VAR *asc_dvc, ASC_DCNT nano_sec) { udelay((nano_sec + 999)/1000); } ASC_INITFUNC( -STATIC ulong, +STATIC ASC_DCNT, AscGetEisaProductID( PortAddr iop_base ) @@ -13310,12 +13369,13 @@ { PortAddr eisa_iop; ushort product_id_high, product_id_low; - ulong product_id; + ASC_DCNT product_id; eisa_iop = ASC_GET_EISA_SLOT(iop_base) | ASC_EISA_PID_IOP_MASK; product_id_low = inpw(eisa_iop); product_id_high = inpw(eisa_iop + 2); - product_id = ((ulong) product_id_high << 16) | (ulong) product_id_low; + product_id = ((ASC_DCNT) product_id_high << 16) | + (ASC_DCNT) product_id_low; return (product_id); } @@ -13326,7 +13386,7 @@ ) ) { - ulong eisa_product_id; + ASC_DCNT eisa_product_id; if (iop_base == 0) { iop_base = ASC_EISA_MIN_IOP_ADDR; @@ -13518,7 +13578,7 @@ } ASC_INITFUNC( -STATIC ulong, +STATIC ASC_DCNT, AscGetMaxDmaCount( ushort bus_type ) @@ -13606,7 +13666,7 @@ ASC_INITFUNC( STATIC ushort, AscReadPCIConfigWord( - ASC_DVC_VAR asc_ptr_type *asc_dvc, + ASC_DVC_VAR *asc_dvc, ushort pci_config_offset) ) { @@ -13620,7 +13680,7 @@ ASC_INITFUNC( STATIC ushort, AscInitGetConfig( - ASC_DVC_VAR asc_ptr_type * asc_dvc + ASC_DVC_VAR *asc_dvc ) ) { @@ -13705,7 +13765,7 @@ ASC_INITFUNC( STATIC ushort, AscInitSetConfig( - ASC_DVC_VAR asc_ptr_type * asc_dvc + ASC_DVC_VAR *asc_dvc ) ) { @@ -13726,7 +13786,7 @@ ASC_INITFUNC( STATIC ushort, AscInitFromAscDvcVar( - ASC_DVC_VAR asc_ptr_type * asc_dvc + ASC_DVC_VAR *asc_dvc ) ) { @@ -13789,7 +13849,7 @@ ASC_INITFUNC( STATIC ushort, AscInitAsc1000Driver( - ASC_DVC_VAR asc_ptr_type * asc_dvc + ASC_DVC_VAR *asc_dvc ) ) { @@ -13801,7 +13861,8 @@ if ((asc_dvc->dvc_cntl & ASC_CNTL_RESET_SCSI) && !(asc_dvc->init_state & ASC_INIT_RESET_SCSI_DONE)) { AscResetChipAndScsiBus(asc_dvc); - DvcSleepMilliSecond((ulong) ((ushort) asc_dvc->scsi_reset_wait * 1000)); + DvcSleepMilliSecond((ASC_DCNT) + ((ushort) asc_dvc->scsi_reset_wait * 1000)); } asc_dvc->init_state |= ASC_INIT_STATE_BEG_LOAD_MC; if (asc_dvc->err_code != 0) @@ -13828,7 +13889,7 @@ ASC_INITFUNC( STATIC ushort, AscInitAscDvcVar( - ASC_DVC_VAR asc_ptr_type * asc_dvc + ASC_DVC_VAR *asc_dvc ) ) { @@ -13862,8 +13923,6 @@ asc_dvc->redo_scam = 0; asc_dvc->res2 = 0; asc_dvc->host_init_sdtr_index = 0; - asc_dvc->res7 = 0; - asc_dvc->res8 = 0; asc_dvc->cfg->can_tagged_qng = 0; asc_dvc->cfg->cmd_qng_enabled = 0; asc_dvc->dvc_cntl = ASC_DEF_DVC_CNTL; @@ -13943,7 +14002,7 @@ ASC_INITFUNC( STATIC ushort, AscInitFromEEP( - ASC_DVC_VAR asc_ptr_type * asc_dvc + ASC_DVC_VAR *asc_dvc ) ) { @@ -13964,7 +14023,8 @@ (AscGetChipScsiCtrl(iop_base) != 0)) { asc_dvc->init_state |= ASC_INIT_RESET_SCSI_DONE; AscResetChipAndScsiBus(asc_dvc); - DvcSleepMilliSecond((ulong) ((ushort) asc_dvc->scsi_reset_wait * 1000)); + DvcSleepMilliSecond((ASC_DCNT) + ((ushort) asc_dvc->scsi_reset_wait * 1000)); } if (AscIsChipHalted(iop_base) == FALSE) { asc_dvc->err_code |= ASC_IERR_START_STOP_CHIP; @@ -14098,14 +14158,14 @@ ASC_INITFUNC( STATIC ushort, AscInitMicroCodeVar( - ASC_DVC_VAR asc_ptr_type * asc_dvc + ASC_DVC_VAR *asc_dvc ) ) { int i; ushort warn_code; PortAddr iop_base; - ulong phy_addr; + ASC_PADDR phy_addr; iop_base = asc_dvc->iop_base; warn_code = 0; @@ -14124,7 +14184,8 @@ ASC_OVERRUN_BSIZE)) == 0L) { asc_dvc->err_code |= ASC_IERR_GET_PHY_ADDR; } else { - phy_addr = (phy_addr & 0xFFFFFFF8UL) + 8; + /* Align on an 8 byte boundary. */ + phy_addr = cpu_to_le32((phy_addr + 7) & ~0x7); AscWriteLramDWord(iop_base, ASCV_OVERRUN_PADDR_D, phy_addr); AscWriteLramDWord(iop_base, ASCV_OVERRUN_BSIZE_D, ASC_OVERRUN_BSIZE - 8); @@ -14148,7 +14209,7 @@ ASC_INITFUNC( STATIC int, AscTestExternalLram( - ASC_DVC_VAR asc_ptr_type * asc_dvc + ASC_DVC_VAR *asc_dvc ) ) { @@ -14302,7 +14363,7 @@ { ushort wval; ushort sum; - ushort *wbuf; + ushort *wbuf; int cfg_beg; int cfg_end; int s_addr; @@ -14416,7 +14477,7 @@ STATIC void AscAsyncFix( - ASC_DVC_VAR asc_ptr_type *asc_dvc, + ASC_DVC_VAR *asc_dvc, uchar tid_no, ASC_SCSI_INQUIRY *inq) { @@ -14470,7 +14531,7 @@ } STATIC void -AscInquiryHandling(ASC_DVC_VAR asc_ptr_type *asc_dvc, +AscInquiryHandling(ASC_DVC_VAR *asc_dvc, uchar tid_no, ASC_SCSI_INQUIRY *inq) { ASC_SCSI_BIT_ID_TYPE tid_bit = ASC_TIX_TO_TARGET_ID(tid_no); @@ -14516,8 +14577,8 @@ STATIC int AscCompareString( - ruchar * str1, - ruchar * str2, + uchar *str1, + uchar *str2, int len ) { @@ -14566,19 +14627,19 @@ return (word_data); } -STATIC ulong +STATIC ASC_DCNT AscReadLramDWord( PortAddr iop_base, ushort addr ) { ushort val_low, val_high; - ulong dword_data; + ASC_DCNT dword_data; AscSetChipLramAddr(iop_base, addr); val_low = AscGetChipLramData(iop_base); val_high = AscGetChipLramData(iop_base); - dword_data = ((ulong) val_high << 16) | (ulong) val_low; + dword_data = ((ASC_DCNT) val_high << 16) | (ASC_DCNT) val_low; return (dword_data); } @@ -14598,7 +14659,7 @@ AscWriteLramDWord( PortAddr iop_base, ushort addr, - ulong dword_val + ASC_DCNT dword_val ) { ushort word_val; @@ -14651,7 +14712,7 @@ AscMemDWordCopyToLram( PortAddr iop_base, ushort s_addr, - ulong * s_buffer, + ASC_DCNT *s_buffer, int dwords ) { @@ -14673,15 +14734,15 @@ return; } -STATIC ulong +STATIC ASC_DCNT AscMemSumLramWord( PortAddr iop_base, ushort s_addr, - rint words + int words ) { - ulong sum; - int i; + ASC_DCNT sum; + int i; sum = 0L; for (i = 0; i < words; i++, s_addr += 2) { @@ -14695,10 +14756,10 @@ PortAddr iop_base, ushort s_addr, ushort set_wval, - rint words + int words ) { - rint i; + int i; AscSetChipLramAddr(iop_base, s_addr); for (i = 0; i < words; i++) { @@ -14714,664 +14775,675 @@ /* a_mcode.h */ STATIC unsigned char _adv_asc3550_buf[] = { - 0x00, 0x00, 0x00, 0xf2, 0x00, 0xf0, 0x00, 0x16, 0x00, 0xfc, 0x48, 0xe4, 0x01, 0x00, 0x01, 0xf6, - 0x00, 0xf6, 0x18, 0xe4, 0x0a, 0x19, 0x18, 0x80, 0x02, 0x00, 0xff, 0xff, 0x03, 0xf6, 0x00, 0xfa, - 0xff, 0x00, 0x82, 0xe7, 0x9e, 0xe7, 0x01, 0xfa, 0x06, 0x0e, 0x09, 0xe7, 0x00, 0xea, 0x01, 0xe6, - 0x03, 0x00, 0x08, 0x00, 0x18, 0xf4, 0x55, 0xf0, 0x3e, 0x01, 0x3e, 0x57, 0x04, 0x00, 0x1e, 0xf0, - 0x85, 0xf0, 0x00, 0xe6, 0x00, 0xec, 0x32, 0xf0, 0x86, 0xf0, 0xa4, 0x0c, 0xd0, 0x01, 0xd5, 0xf0, - 0xf6, 0x18, 0x38, 0x54, 0x98, 0x57, 0xbc, 0x00, 0xb1, 0xf0, 0xb4, 0x00, 0x01, 0xfc, 0x02, 0x13, - 0x03, 0xfc, 0x9e, 0x0c, 0x00, 0x57, 0x01, 0xf0, 0x03, 0xe6, 0x0c, 0x1c, 0x10, 0x00, 0x18, 0x40, - 0x30, 0x12, 0x3e, 0x1c, 0xbd, 0x00, 0xe0, 0x00, 0x02, 0x48, 0x02, 0x80, 0x3c, 0x00, 0x4e, 0x01, - 0x66, 0x15, 0x6c, 0x01, 0x6e, 0x01, 0xbb, 0x00, 0xda, 0x12, 0x00, 0x4e, 0x01, 0x01, 0x01, 0xea, - 0x08, 0x12, 0x30, 0xe4, 0x6a, 0x0f, 0xa8, 0x0c, 0xae, 0x0f, 0xb6, 0x00, 0xb9, 0x54, 0x00, 0x80, - 0x04, 0x12, 0x06, 0xf7, 0x24, 0x01, 0x28, 0x01, 0x32, 0x00, 0x3c, 0x01, 0x3c, 0x56, 0x3e, 0x00, - 0x4b, 0xe4, 0x4c, 0x1c, 0x68, 0x01, 0x6a, 0x01, 0x70, 0x01, 0x72, 0x01, 0x74, 0x01, 0x76, 0x01, - 0x78, 0x01, 0x00, 0x01, 0x02, 0xee, 0x02, 0xfc, 0x03, 0x58, 0x03, 0xf7, 0x04, 0x80, 0x05, 0xfc, - 0x08, 0x44, 0x09, 0xf0, 0x0a, 0x15, 0x10, 0x44, 0x1b, 0x80, 0x20, 0x01, 0x38, 0x1c, 0x40, 0x00, - 0x4b, 0xf4, 0x4e, 0x1c, 0x5b, 0xf0, 0x5d, 0xf0, 0x80, 0x00, 0xaa, 0x00, 0xaa, 0x14, 0xb6, 0x08, - 0xb8, 0x0f, 0xbb, 0x55, 0xbd, 0x56, 0xbe, 0x00, 0xc0, 0x00, 0x00, 0x4c, 0x00, 0xdc, 0x02, 0x4a, - 0x05, 0xf0, 0x05, 0xf8, 0x06, 0x13, 0x08, 0x13, 0x0c, 0x00, 0x0e, 0x47, 0x0e, 0xf7, 0x0f, 0x00, - 0x19, 0x00, 0x20, 0x00, 0x2a, 0x01, 0x32, 0x1c, 0x36, 0x00, 0x38, 0x12, 0x3c, 0x0b, 0x45, 0x5a, - 0x56, 0x14, 0x59, 0xf0, 0x62, 0x0a, 0x69, 0x08, 0x83, 0x59, 0xae, 0x17, 0xb8, 0xf0, 0xba, 0x0f, - 0xba, 0x17, 0xf0, 0x00, 0xf6, 0x0d, 0x02, 0xfa, 0x03, 0xfa, 0x04, 0x10, 0x04, 0xea, 0x04, 0xf6, - 0x04, 0xfc, 0x05, 0x00, 0x06, 0x00, 0x06, 0x12, 0x0a, 0x10, 0x0b, 0xf0, 0x0c, 0xf0, 0x12, 0x10, - 0x30, 0x1c, 0x33, 0x00, 0x34, 0x00, 0x38, 0x44, 0x40, 0x5c, 0x4a, 0xe4, 0x5a, 0x14, 0x62, 0x1a, - 0x64, 0x0a, 0x68, 0x08, 0x68, 0x54, 0x83, 0x55, 0x83, 0x5a, 0x91, 0x44, 0x98, 0x12, 0x9a, 0x16, - 0xa4, 0x00, 0xb0, 0x57, 0xb5, 0x00, 0xba, 0x00, 0xce, 0x45, 0xd0, 0x00, 0xe1, 0x00, 0xe7, 0x00, - 0xec, 0x0d, 0x00, 0x54, 0x01, 0x48, 0x01, 0x58, 0x02, 0x10, 0x02, 0xe6, 0x03, 0xa1, 0x04, 0x13, - 0x05, 0xe6, 0x06, 0x0b, 0x06, 0x83, 0x06, 0xf0, 0x07, 0x00, 0x0a, 0x00, 0x0a, 0x12, 0x0a, 0xf0, - 0x0c, 0x04, 0x0c, 0x10, 0x0c, 0x12, 0x0e, 0x13, 0x10, 0x10, 0x12, 0x1c, 0x17, 0x00, 0x18, 0x0e, - 0x19, 0xe4, 0x1a, 0x10, 0x1c, 0x00, 0x1c, 0x12, 0x1c, 0x14, 0x1d, 0xf7, 0x1e, 0x13, 0x20, 0x1c, - 0x20, 0xe7, 0x22, 0x01, 0x26, 0x01, 0x2a, 0x12, 0x30, 0xe7, 0x41, 0x58, 0x43, 0x48, 0x44, 0x55, - 0x46, 0x1c, 0x4e, 0xe4, 0x5c, 0xf0, 0x72, 0x02, 0x74, 0x03, 0x77, 0x57, 0x88, 0x12, 0x89, 0x48, - 0x92, 0x13, 0x99, 0x00, 0x9b, 0x00, 0x9c, 0x32, 0x9e, 0x00, 0xa8, 0x00, 0xaa, 0x12, 0xb9, 0x00, - 0xba, 0x06, 0xbf, 0x57, 0xc0, 0x01, 0xc0, 0x08, 0xc2, 0x01, 0xfe, 0x9c, 0xf0, 0x26, 0x02, 0xfe, - 0xc6, 0x0c, 0xff, 0x10, 0x00, 0x00, 0xfc, 0xfe, 0x18, 0x19, 0x00, 0xfa, 0xfe, 0x80, 0x01, 0xff, - 0x03, 0x00, 0x00, 0x2f, 0xfe, 0x01, 0x05, 0xff, 0x40, 0x00, 0x00, 0x0d, 0xff, 0x09, 0x00, 0x00, - 0xff, 0x08, 0x01, 0x01, 0xff, 0x10, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, 0xff, 0x10, 0xff, 0xff, - 0xff, 0x0f, 0x00, 0x00, 0xfe, 0x78, 0x56, 0xfe, 0x34, 0x12, 0xff, 0x21, 0x00, 0x00, 0xfe, 0x04, - 0xf7, 0xfa, 0x35, 0x51, 0x0c, 0x01, 0xfe, 0xb6, 0x0e, 0xfe, 0x04, 0xf7, 0xfa, 0x51, 0x0c, 0x1d, - 0x35, 0xfe, 0x3d, 0xf0, 0xfe, 0xf8, 0x01, 0xfe, 0x20, 0xf0, 0xd0, 0x04, 0x55, 0x50, 0x02, 0xfe, - 0xe2, 0x0c, 0x01, 0xfe, 0x42, 0x0d, 0xfe, 0xe9, 0x12, 0x02, 0xfe, 0x04, 0x03, 0xfe, 0x28, 0x1c, - 0x04, 0xfe, 0xa6, 0x00, 0xfe, 0xdd, 0x12, 0x4e, 0x13, 0xfe, 0xa6, 0x00, 0xc3, 0xfe, 0x48, 0xf0, - 0xfe, 0x7c, 0x02, 0xfe, 0x49, 0xf0, 0xfe, 0x96, 0x02, 0xfe, 0x4a, 0xf0, 0xfe, 0xb4, 0x02, 0xfe, - 0x46, 0xf0, 0xfe, 0x46, 0x02, 0xfe, 0x47, 0xf0, 0xfe, 0x4c, 0x02, 0xfe, 0x43, 0xf0, 0xfe, 0x3a, - 0x02, 0xfe, 0x44, 0xf0, 0xfe, 0x3e, 0x02, 0xfe, 0x45, 0xf0, 0xfe, 0x42, 0x02, 0x07, 0x0c, 0x9d, - 0x07, 0x06, 0x13, 0xb8, 0x02, 0x26, 0xfe, 0x00, 0x1c, 0xfe, 0xf1, 0x10, 0xfe, 0x02, 0x1c, 0xfe, - 0xed, 0x10, 0xfe, 0x1e, 0x1c, 0xfe, 0xe9, 0x10, 0x01, 0xfe, 0x0e, 0x17, 0xfe, 0xe7, 0x10, 0xfe, - 0x06, 0xfc, 0xf5, 0x0e, 0x7b, 0x01, 0xc0, 0x02, 0x26, 0x17, 0x54, 0x47, 0xba, 0x01, 0xfe, 0x2c, - 0x0f, 0x0e, 0x7b, 0x01, 0x9a, 0xfe, 0xbd, 0x10, 0x0e, 0x7b, 0x01, 0x9a, 0xfe, 0xad, 0x10, 0xfe, - 0x16, 0x1c, 0xfe, 0x58, 0x1c, 0x07, 0x06, 0x13, 0xb8, 0x35, 0x1f, 0x26, 0xfe, 0x3d, 0xf0, 0xfe, - 0xf8, 0x01, 0x27, 0xfe, 0x8a, 0x02, 0xfe, 0x5a, 0x1c, 0xd5, 0xfe, 0x14, 0x1c, 0x17, 0xfe, 0x30, - 0x00, 0x47, 0xba, 0x01, 0xfe, 0x1c, 0x0f, 0x07, 0x06, 0x13, 0xb8, 0x02, 0xfc, 0x22, 0x2b, 0x05, - 0x10, 0x2f, 0xfe, 0x69, 0x10, 0x07, 0x06, 0x13, 0xb8, 0xfe, 0x04, 0xec, 0x2b, 0x08, 0x2b, 0x07, - 0x3a, 0x1d, 0x01, 0x40, 0x7f, 0xfe, 0x05, 0xf6, 0xf5, 0x01, 0xfe, 0x40, 0x16, 0x0b, 0x49, 0x89, - 0x37, 0x11, 0x43, 0x1d, 0xca, 0x08, 0x1c, 0x07, 0x3f, 0x01, 0x6a, 0x02, 0x26, 0x0e, 0x3b, 0x01, - 0x14, 0x05, 0x10, 0xd3, 0x08, 0x1c, 0x07, 0x3f, 0x01, 0x76, 0xfe, 0x28, 0x10, 0x0e, 0xbd, 0x01, - 0x14, 0xe5, 0x0e, 0x7c, 0x01, 0x14, 0xfe, 0x49, 0x54, 0x72, 0xfe, 0x12, 0x03, 0x08, 0x1c, 0x07, - 0x3f, 0x01, 0x6a, 0x02, 0x26, 0x35, 0x7f, 0xfe, 0x02, 0xe8, 0x2d, 0xf9, 0xfe, 0x9e, 0x43, 0xed, - 0xfe, 0x07, 0x4b, 0xfe, 0x20, 0xf0, 0xd0, 0xfe, 0x40, 0x1c, 0x1f, 0xec, 0xfe, 0x26, 0xf0, 0xfe, - 0x70, 0x03, 0xfe, 0xa0, 0xf0, 0xfe, 0x5e, 0x03, 0xfe, 0x11, 0xf0, 0xd0, 0xfe, 0x0e, 0x10, 0xfe, - 0x9f, 0xf0, 0xfe, 0x7e, 0x03, 0xe8, 0x12, 0xfe, 0x11, 0x00, 0x02, 0x4b, 0x35, 0xfe, 0x48, 0x1c, - 0xe8, 0x1f, 0xec, 0x33, 0xec, 0xfe, 0x82, 0xf0, 0xfe, 0x84, 0x03, 0x29, 0x22, 0xbb, 0x68, 0x16, - 0xbb, 0x0e, 0x7c, 0x01, 0x14, 0x68, 0x7d, 0x08, 0x1c, 0x07, 0x3f, 0x01, 0x40, 0x11, 0x3b, 0x08, - 0x3b, 0x07, 0x99, 0x01, 0x6a, 0xf3, 0x11, 0xfe, 0xe4, 0x00, 0x2c, 0xfe, 0xca, 0x03, 0x1f, 0x31, - 0x20, 0xfe, 0xda, 0x03, 0x01, 0x4a, 0xcb, 0xfe, 0xea, 0x03, 0x69, 0x8e, 0xcf, 0xfe, 0xaa, 0x06, - 0x02, 0x25, 0x04, 0x7b, 0x2a, 0x1b, 0xfe, 0x1c, 0x05, 0x17, 0x84, 0x01, 0x38, 0x01, 0x95, 0x01, - 0x98, 0x33, 0xfe, 0x5c, 0x02, 0x02, 0xeb, 0xe8, 0x35, 0x51, 0x18, 0xfe, 0x67, 0x1b, 0xf9, 0xed, - 0xfe, 0x48, 0x1c, 0x8b, 0x01, 0xee, 0xa8, 0xfe, 0x96, 0xf0, 0xfe, 0x24, 0x04, 0x2c, 0xfe, 0x28, - 0x04, 0x33, 0x26, 0x0e, 0x3b, 0x01, 0x14, 0x05, 0x10, 0x1b, 0xfe, 0x08, 0x05, 0x3c, 0x92, 0x9e, - 0x2d, 0x81, 0x6d, 0x1f, 0x31, 0x20, 0x25, 0x04, 0x7b, 0x2a, 0xfe, 0x10, 0x12, 0x17, 0x84, 0x01, - 0x38, 0x33, 0xfe, 0x5c, 0x02, 0x02, 0xeb, 0x30, 0xfe, 0xa0, 0x00, 0xfe, 0x9b, 0x57, 0xfe, 0x5e, - 0x12, 0x0b, 0x09, 0x06, 0xfe, 0x56, 0x12, 0x23, 0x28, 0x93, 0x01, 0x0a, 0x81, 0x6d, 0x20, 0xfe, - 0xd8, 0x04, 0x23, 0x28, 0x93, 0x01, 0x0a, 0x20, 0x25, 0x23, 0x28, 0xb1, 0xfe, 0x4c, 0x44, 0xfe, - 0x32, 0x12, 0x56, 0xfe, 0x44, 0x48, 0x08, 0xd6, 0xfe, 0x4c, 0x54, 0x72, 0xfe, 0x08, 0x05, 0x7f, - 0x9e, 0x2d, 0xfe, 0x06, 0x80, 0xfe, 0x48, 0x47, 0xfe, 0x48, 0x13, 0x3d, 0x05, 0xfe, 0xcc, 0x00, - 0xfe, 0x40, 0x13, 0x0b, 0x09, 0x06, 0x8d, 0xfe, 0x06, 0x10, 0x23, 0x28, 0xb1, 0x0b, 0x09, 0x36, - 0xdb, 0x17, 0xa2, 0x0b, 0x09, 0x06, 0x50, 0x17, 0xfe, 0x0d, 0x00, 0x01, 0x38, 0x33, 0xfe, 0x86, - 0x0c, 0x02, 0x25, 0x39, 0x11, 0xfe, 0xe6, 0x00, 0xfe, 0x1c, 0x90, 0xac, 0x03, 0x17, 0xa2, 0x01, - 0x38, 0x33, 0x26, 0x1f, 0x26, 0x02, 0xfe, 0x10, 0x05, 0xfe, 0x42, 0x5b, 0x51, 0x18, 0xfe, 0x46, - 0x59, 0xf9, 0xed, 0x17, 0x74, 0xfe, 0x07, 0x80, 0xfe, 0x31, 0x44, 0x0b, 0x09, 0x0c, 0xfe, 0x78, - 0x13, 0xfe, 0x20, 0x80, 0x05, 0x18, 0xfe, 0x70, 0x12, 0x6c, 0x09, 0x06, 0xfe, 0x60, 0x13, 0x04, - 0xfe, 0xa2, 0x00, 0x2a, 0x1b, 0xfe, 0xa8, 0x05, 0xfe, 0x31, 0xe4, 0x6f, 0x6c, 0x09, 0x0c, 0xfe, - 0x4a, 0x13, 0x04, 0xfe, 0xa0, 0x00, 0x2a, 0xfe, 0x42, 0x12, 0x59, 0x2c, 0xfe, 0x68, 0x05, 0x1f, - 0x31, 0xef, 0x01, 0x0a, 0x24, 0xfe, 0xc0, 0x05, 0x11, 0xfe, 0xe3, 0x00, 0x29, 0x6c, 0xfe, 0x4a, - 0xf0, 0xfe, 0x92, 0x05, 0xfe, 0x49, 0xf0, 0xfe, 0x8c, 0x05, 0xd1, 0x21, 0xfe, 0x21, 0x00, 0xa4, - 0x21, 0xfe, 0x22, 0x00, 0x9d, 0x21, 0x89, 0xfe, 0x09, 0x48, 0x01, 0x0a, 0x24, 0xfe, 0xc0, 0x05, - 0xfe, 0xe2, 0x08, 0x6c, 0x09, 0xda, 0x50, 0x01, 0xb6, 0x21, 0x06, 0x16, 0xe2, 0x47, 0xfe, 0x27, - 0x01, 0x0b, 0x09, 0x36, 0xe3, 0x4e, 0x01, 0xae, 0x17, 0xa2, 0x0b, 0x09, 0x06, 0x50, 0x17, 0xfe, - 0x0d, 0x00, 0x01, 0x38, 0x01, 0x95, 0x01, 0x98, 0x33, 0xfe, 0x86, 0x0c, 0x02, 0x25, 0x04, 0xfe, - 0x9c, 0x00, 0x2a, 0xfe, 0x3e, 0x12, 0x04, 0x52, 0x2a, 0xfe, 0x36, 0x13, 0x4e, 0x01, 0xae, 0x24, - 0xfe, 0x38, 0x06, 0x0e, 0x06, 0x6c, 0x09, 0x19, 0xfe, 0x02, 0x12, 0x79, 0x01, 0xfe, 0xf0, 0x13, - 0x20, 0xfe, 0x2e, 0x06, 0x11, 0xbe, 0x01, 0x4a, 0x11, 0xfe, 0xe5, 0x00, 0x04, 0x52, 0xb9, 0x0f, - 0x52, 0x04, 0xf4, 0x2a, 0xfe, 0x62, 0x12, 0x04, 0x4d, 0x2a, 0xfe, 0x5a, 0x13, 0x01, 0xfe, 0x60, - 0x18, 0x01, 0xfe, 0xb2, 0x18, 0xe6, 0xc8, 0x19, 0x08, 0x61, 0xff, 0x02, 0x00, 0x57, 0x64, 0x7e, - 0x1a, 0x4f, 0xc7, 0xc8, 0x87, 0x4e, 0x01, 0xae, 0x24, 0xfe, 0xa2, 0x06, 0x6c, 0x09, 0x1e, 0xa3, - 0x7a, 0x0e, 0x54, 0x01, 0xfe, 0x1e, 0x14, 0x20, 0xfe, 0x98, 0x06, 0x11, 0xbe, 0x01, 0x4a, 0x11, - 0xfe, 0xe5, 0x00, 0x04, 0x4d, 0xb9, 0x0f, 0x4d, 0x07, 0x06, 0x01, 0xae, 0xf3, 0x71, 0x8b, 0x01, - 0xee, 0xa8, 0x11, 0xfe, 0xe2, 0x00, 0x2c, 0xf8, 0x1f, 0x31, 0xcf, 0xfe, 0xd6, 0x06, 0x80, 0xfe, - 0x74, 0x07, 0xcb, 0xfe, 0x7c, 0x07, 0x69, 0x8e, 0x02, 0x25, 0x0b, 0x09, 0x0c, 0xfe, 0x2e, 0x12, - 0x15, 0x18, 0x01, 0x0a, 0x15, 0x00, 0x01, 0x0a, 0x15, 0x00, 0x01, 0x0a, 0x15, 0x00, 0x01, 0x0a, - 0xfe, 0x99, 0xa4, 0x01, 0x0a, 0x15, 0x00, 0x02, 0xfe, 0x3a, 0x08, 0x66, 0x09, 0x1e, 0x8d, 0x0b, - 0x09, 0x1e, 0xfe, 0x30, 0x13, 0x15, 0xfe, 0x1b, 0x00, 0x01, 0x0a, 0x15, 0x00, 0x01, 0x0a, 0x15, - 0x00, 0x01, 0x0a, 0x15, 0x00, 0x01, 0x0a, 0x15, 0x06, 0x01, 0x0a, 0x15, 0x00, 0x02, 0xc9, 0x79, - 0xfe, 0x9a, 0x81, 0x65, 0x89, 0xfe, 0x09, 0x6f, 0xfe, 0x93, 0x45, 0x1b, 0xfe, 0x84, 0x07, 0x2c, - 0xfe, 0x5c, 0x07, 0x1f, 0x31, 0xcf, 0xfe, 0x54, 0x07, 0x69, 0x8e, 0x80, 0xfe, 0x74, 0x07, 0x02, - 0x25, 0x01, 0x4a, 0x02, 0xf8, 0x15, 0x19, 0x02, 0xf8, 0xfe, 0x9c, 0xf7, 0xfe, 0xf0, 0x07, 0xfe, - 0x2c, 0x90, 0xfe, 0xae, 0x90, 0x73, 0xfe, 0xd2, 0x07, 0x0f, 0x5c, 0x13, 0x5d, 0x0b, 0x49, 0x6f, - 0x37, 0x01, 0xfe, 0xf6, 0x17, 0x05, 0x10, 0x82, 0xfe, 0x83, 0xe7, 0x88, 0xa4, 0xfe, 0x03, 0x40, - 0x0b, 0x49, 0x74, 0x37, 0x01, 0xb7, 0xab, 0xfe, 0x1f, 0x40, 0x16, 0x60, 0x01, 0xf6, 0xfe, 0x08, - 0x50, 0xfe, 0x8a, 0x50, 0xfe, 0x34, 0x51, 0xfe, 0xb6, 0x51, 0xfe, 0x08, 0x90, 0xfe, 0x8a, 0x90, - 0x0f, 0x5a, 0x13, 0x5b, 0xfe, 0x0c, 0x90, 0xfe, 0x8e, 0x90, 0xfe, 0x28, 0x50, 0xfe, 0xaa, 0x50, - 0x0f, 0x41, 0x13, 0x42, 0xfe, 0x4a, 0x10, 0x0b, 0x09, 0x6f, 0xe3, 0xfe, 0x2c, 0x90, 0xfe, 0xae, - 0x90, 0x0f, 0x5c, 0x13, 0x5d, 0x0b, 0x09, 0x74, 0xc7, 0x01, 0xb7, 0xfe, 0x1f, 0x80, 0x16, 0x60, - 0xfe, 0x34, 0x90, 0xfe, 0xb6, 0x90, 0x0f, 0x5e, 0x13, 0x5f, 0xfe, 0x08, 0x90, 0xfe, 0x8a, 0x90, - 0x0f, 0x5a, 0x13, 0x5b, 0xfe, 0x28, 0x90, 0xfe, 0xaa, 0x90, 0x0f, 0x41, 0x13, 0x42, 0x0f, 0x3e, - 0x13, 0x57, 0x0b, 0x49, 0x19, 0x37, 0x35, 0x08, 0xa1, 0x2c, 0xfe, 0x50, 0x08, 0xfe, 0x9e, 0xf0, - 0xfe, 0x64, 0x08, 0xc2, 0x1b, 0x31, 0x35, 0x6b, 0xfe, 0xed, 0x10, 0xa5, 0xfe, 0x88, 0x08, 0xa6, - 0xfe, 0xa4, 0x08, 0x80, 0xfe, 0x7c, 0x08, 0xcb, 0xfe, 0x82, 0x08, 0x69, 0x8e, 0x02, 0x25, 0x01, - 0x4a, 0xfe, 0xc9, 0x10, 0x15, 0x19, 0xfe, 0xc9, 0x10, 0x66, 0x09, 0x06, 0xfe, 0x10, 0x12, 0x66, - 0x09, 0x0c, 0x48, 0x0b, 0x09, 0x0c, 0xfe, 0x66, 0x12, 0xfe, 0x2e, 0x1c, 0xa7, 0x66, 0x09, 0x06, - 0x48, 0x66, 0x09, 0x0c, 0xfe, 0x52, 0x12, 0xfe, 0x2c, 0x1c, 0xfe, 0xaa, 0xf0, 0xfe, 0x24, 0x09, - 0xfe, 0xac, 0xf0, 0xfe, 0xc4, 0x08, 0xfe, 0x92, 0x10, 0xfe, 0x34, 0x1c, 0xfe, 0xf3, 0x10, 0xfe, - 0xad, 0xf0, 0xfe, 0xd0, 0x08, 0x02, 0xfe, 0x32, 0x0a, 0xfe, 0x36, 0x1c, 0xfe, 0xe7, 0x10, 0xfe, - 0x2b, 0xf0, 0xb0, 0xfe, 0x6b, 0x18, 0x1a, 0xfe, 0x00, 0xfe, 0xdb, 0xc3, 0xfe, 0xd2, 0xf0, 0xb0, - 0xfe, 0x76, 0x18, 0x1a, 0x18, 0x1b, 0xb0, 0x04, 0xe1, 0x1a, 0x06, 0x1b, 0xb0, 0xa5, 0x77, 0xa6, - 0x77, 0xfe, 0x34, 0x1c, 0xfe, 0x36, 0x1c, 0xfe, 0xb1, 0x10, 0x8b, 0x59, 0x39, 0x17, 0xa2, 0x01, - 0x38, 0x12, 0xfe, 0x35, 0x00, 0x33, 0x4b, 0x12, 0x8c, 0x02, 0x4b, 0xfe, 0x74, 0x18, 0x1a, 0xfe, - 0x00, 0xf8, 0x1b, 0x77, 0x51, 0x1e, 0x01, 0xfe, 0x42, 0x0d, 0xd2, 0x08, 0x1c, 0x07, 0x3f, 0x01, - 0x6a, 0x22, 0x2d, 0x3c, 0x51, 0x18, 0x02, 0x77, 0xfe, 0x98, 0x80, 0xd8, 0x0c, 0x27, 0xfe, 0x14, - 0x0a, 0x0b, 0x09, 0x6f, 0xfe, 0x82, 0x12, 0x0b, 0x09, 0x19, 0xfe, 0x66, 0x13, 0x22, 0x60, 0x68, - 0xc6, 0xfe, 0x83, 0x80, 0xfe, 0xc8, 0x44, 0xfe, 0x2e, 0x13, 0xfe, 0x04, 0x91, 0xfe, 0x86, 0x91, - 0x62, 0x2d, 0xfe, 0x40, 0x59, 0xfe, 0xc1, 0x59, 0x73, 0xfb, 0x04, 0x5c, 0x2e, 0x5d, 0x0f, 0xaa, - 0x13, 0x8c, 0x9b, 0x5c, 0x9c, 0x5d, 0x01, 0xb7, 0xab, 0x62, 0x2d, 0x16, 0x60, 0xa0, 0x3e, 0x67, - 0x57, 0x63, 0x5e, 0x30, 0x5f, 0xe7, 0xfe, 0xe5, 0x55, 0xfe, 0x04, 0xfa, 0x3e, 0xfe, 0x05, 0xfa, - 0x57, 0x01, 0xf6, 0xfe, 0x36, 0x10, 0x29, 0x0f, 0xaa, 0x0f, 0x8c, 0x63, 0x5e, 0x30, 0x5f, 0xa7, - 0x0b, 0x09, 0x19, 0x1b, 0xfb, 0x63, 0x41, 0x30, 0x42, 0x0b, 0x09, 0xfe, 0xf7, 0x00, 0x37, 0x04, - 0x5a, 0x2e, 0x5b, 0xfe, 0x10, 0x58, 0xfe, 0x91, 0x58, 0xfe, 0x14, 0x59, 0xfe, 0x95, 0x59, 0x02, - 0x77, 0x0b, 0x09, 0x19, 0x1b, 0xfb, 0x0b, 0x09, 0xfe, 0xf7, 0x00, 0x37, 0xfe, 0x3a, 0x55, 0xfe, - 0x19, 0x81, 0x79, 0xfe, 0x10, 0x90, 0xfe, 0x92, 0x90, 0xfe, 0xd7, 0x10, 0x3d, 0x05, 0xbf, 0x1b, - 0xfe, 0xcc, 0x08, 0x11, 0xbf, 0xfe, 0x98, 0x80, 0xd8, 0x0c, 0xfe, 0x14, 0x13, 0x04, 0x41, 0x2e, - 0x42, 0x73, 0xfe, 0xcc, 0x08, 0xfe, 0x0c, 0x58, 0xfe, 0x8d, 0x58, 0x02, 0x77, 0x29, 0x4e, 0xfe, - 0x19, 0x80, 0xfe, 0xf1, 0x10, 0x0b, 0x09, 0x0c, 0xa3, 0xfe, 0x6c, 0x19, 0xfe, 0x19, 0x41, 0xfe, - 0x94, 0x10, 0xfe, 0x6c, 0x19, 0x9b, 0x41, 0xfe, 0xed, 0x19, 0x9c, 0x42, 0xfe, 0x0c, 0x51, 0xfe, - 0x8e, 0x51, 0xfe, 0x6b, 0x18, 0x1a, 0xfe, 0x00, 0xff, 0x2f, 0xfe, 0x7a, 0x10, 0xc3, 0xfe, 0xd2, - 0xf0, 0xfe, 0xac, 0x0a, 0xfe, 0x76, 0x18, 0x1a, 0x18, 0xce, 0x04, 0xe1, 0x1a, 0x06, 0x83, 0x12, - 0xfe, 0x16, 0x00, 0x02, 0x4b, 0xfe, 0xd1, 0xf0, 0xfe, 0xe2, 0x0a, 0x17, 0xa1, 0x01, 0x38, 0x12, - 0xd6, 0xfe, 0x48, 0x10, 0xfe, 0xce, 0xf0, 0xfe, 0xca, 0x0a, 0x12, 0xfe, 0x21, 0x00, 0x02, 0x4b, - 0xfe, 0xcd, 0xf0, 0xfe, 0xd6, 0x0a, 0x12, 0xfe, 0x22, 0x00, 0x02, 0x4b, 0xfe, 0xcb, 0xf0, 0xfe, - 0xe2, 0x0a, 0x12, 0xfe, 0x24, 0x00, 0x02, 0x4b, 0xfe, 0xd0, 0xf0, 0xfe, 0xec, 0x0a, 0x12, 0x88, - 0xd9, 0xfe, 0xcf, 0xf0, 0xfe, 0xf6, 0x0a, 0x12, 0x89, 0xd4, 0xfe, 0xcc, 0xf0, 0xc9, 0xfe, 0x84, - 0x80, 0xd8, 0x19, 0xfe, 0xd5, 0x12, 0x12, 0xfe, 0x12, 0x00, 0x2c, 0xc9, 0x1f, 0x31, 0xa5, 0x25, - 0xa6, 0x25, 0x35, 0xf3, 0x2c, 0xfe, 0x1a, 0x0b, 0x1f, 0x31, 0x80, 0xfe, 0x36, 0x0b, 0x69, 0x8e, - 0xa5, 0xfe, 0xf0, 0x07, 0xa6, 0xfe, 0xf0, 0x07, 0x02, 0x25, 0x01, 0x4a, 0xfe, 0xdb, 0x10, 0x11, - 0xfe, 0xe8, 0x00, 0x8b, 0x81, 0x6d, 0xfe, 0x89, 0xf0, 0x25, 0x23, 0x28, 0xfe, 0xe9, 0x09, 0x01, - 0x0a, 0x81, 0x6d, 0x20, 0x25, 0x23, 0x28, 0x93, 0x33, 0xfe, 0x6e, 0x0b, 0x1f, 0x31, 0x02, 0xfe, - 0x62, 0x0b, 0xc2, 0x48, 0x12, 0xfe, 0x42, 0x00, 0x02, 0x4b, 0x9f, 0x06, 0xfe, 0x81, 0x49, 0xfe, - 0xcc, 0x12, 0x0b, 0x09, 0x0c, 0xfe, 0x5a, 0x13, 0x12, 0x00, 0x58, 0x0c, 0xfe, 0x6a, 0x12, 0x58, - 0xfe, 0x28, 0x00, 0x27, 0xfe, 0xb4, 0x0c, 0x0e, 0x7c, 0x01, 0x14, 0x05, 0x00, 0x83, 0x34, 0xfe, - 0x28, 0x00, 0x02, 0xfe, 0xb4, 0x0c, 0x01, 0x95, 0x01, 0x98, 0x0e, 0xbd, 0x01, 0xfe, 0x10, 0x0e, - 0xaf, 0x08, 0x3b, 0x07, 0x99, 0x01, 0x40, 0x11, 0x43, 0x08, 0x1c, 0x07, 0x3f, 0x01, 0x76, 0x02, - 0x26, 0x12, 0xfe, 0x44, 0x00, 0x58, 0x0c, 0xa3, 0x34, 0x0c, 0xfe, 0xc0, 0x10, 0x01, 0xb6, 0x34, - 0x0c, 0xfe, 0xb6, 0x10, 0x01, 0xb6, 0xfe, 0x19, 0x82, 0xfe, 0x34, 0x46, 0xfe, 0x0a, 0x13, 0x34, - 0x0c, 0x12, 0xfe, 0x43, 0x00, 0xfe, 0xa2, 0x10, 0x0b, 0x49, 0x0c, 0x37, 0x01, 0x95, 0x01, 0x98, - 0xaf, 0x08, 0x3b, 0x07, 0x99, 0x01, 0x40, 0x11, 0x43, 0x08, 0x1c, 0x07, 0x3f, 0x01, 0x76, 0x51, - 0x0c, 0xaf, 0x1d, 0xca, 0x02, 0xfe, 0x48, 0x03, 0x0b, 0x09, 0x0c, 0xce, 0x34, 0x0c, 0x12, 0x00, - 0xfe, 0x54, 0x10, 0x66, 0x09, 0x1e, 0xfe, 0x50, 0x12, 0x0b, 0x09, 0x1e, 0xfe, 0x48, 0x13, 0xfe, - 0x1c, 0x1c, 0xfe, 0x9d, 0xf0, 0xfe, 0x72, 0x0c, 0xfe, 0x1c, 0x1c, 0xfe, 0x9d, 0xf0, 0xfe, 0x78, - 0x0c, 0x0b, 0x49, 0x1e, 0x37, 0xfe, 0x95, 0x10, 0x12, 0xfe, 0x15, 0x00, 0xfe, 0x04, 0xe6, 0x0c, - 0x79, 0xfe, 0x26, 0x10, 0x12, 0xfe, 0x13, 0x00, 0xd4, 0x12, 0xfe, 0x47, 0x00, 0xa4, 0x12, 0xfe, - 0x41, 0x00, 0x9d, 0x12, 0xfe, 0x24, 0x00, 0x04, 0x7b, 0x2a, 0x27, 0xeb, 0x79, 0xfe, 0x04, 0xe6, - 0x1e, 0xfe, 0x9d, 0x41, 0xfe, 0x1c, 0x42, 0xaf, 0x01, 0xd7, 0x02, 0x26, 0xd5, 0x17, 0x0c, 0x47, - 0xf2, 0xdf, 0x17, 0xfe, 0x31, 0x00, 0x47, 0xba, 0x01, 0xfe, 0x1c, 0x0f, 0x02, 0xfc, 0x1d, 0xfe, - 0x06, 0xec, 0xf7, 0x85, 0x34, 0x36, 0xbc, 0x2f, 0x1d, 0xfe, 0x06, 0xea, 0xf7, 0xfe, 0x47, 0x4b, - 0x7a, 0xfe, 0x75, 0x57, 0x04, 0x55, 0xfe, 0x98, 0x56, 0xfe, 0x28, 0x12, 0x0e, 0x7c, 0xfe, 0xfa, - 0x14, 0x4e, 0xe5, 0x0e, 0xbd, 0xfe, 0xf0, 0x14, 0xfe, 0x49, 0x54, 0x91, 0xfe, 0x28, 0x0d, 0x0e, - 0x1c, 0xfe, 0xe4, 0x14, 0xfe, 0x44, 0x48, 0x02, 0xfe, 0x48, 0x03, 0x0e, 0x55, 0xfe, 0xc8, 0x14, - 0x85, 0x34, 0x36, 0xbc, 0x2f, 0x1d, 0xfe, 0xce, 0x47, 0xfe, 0xbd, 0x13, 0x02, 0x26, 0x22, 0x2b, - 0x05, 0x10, 0xfe, 0x78, 0x12, 0x29, 0x16, 0x54, 0x16, 0xa9, 0x22, 0x43, 0x4e, 0x47, 0x43, 0xc2, - 0xfe, 0x0c, 0x13, 0xfe, 0xbc, 0xf0, 0xfe, 0xc4, 0x0d, 0x08, 0x06, 0x16, 0x54, 0x01, 0xfe, 0xd0, - 0x15, 0x04, 0xfe, 0x38, 0x01, 0x2e, 0xfe, 0x3a, 0x01, 0x73, 0xfe, 0xc8, 0x0d, 0x04, 0xfe, 0x38, - 0x01, 0x1a, 0xfe, 0xf0, 0xff, 0x0f, 0xfe, 0x60, 0x01, 0x04, 0xfe, 0x3a, 0x01, 0x0f, 0xfe, 0x62, - 0x01, 0x21, 0x06, 0x16, 0x43, 0xfe, 0x04, 0xec, 0x2b, 0x08, 0x2b, 0x07, 0x3a, 0x1d, 0x01, 0x40, - 0x7f, 0xfe, 0x05, 0xf6, 0xfe, 0x34, 0x01, 0x01, 0xfe, 0x40, 0x16, 0x11, 0x43, 0xca, 0x08, 0x06, - 0x03, 0x29, 0x03, 0x22, 0x54, 0xfe, 0xf7, 0x12, 0x22, 0xa9, 0x68, 0x16, 0xa9, 0x05, 0xa1, 0xfe, - 0x93, 0x13, 0xfe, 0x24, 0x1c, 0x17, 0x18, 0x47, 0xf2, 0xdf, 0xfe, 0xd9, 0x10, 0x94, 0xfe, 0x03, - 0xdc, 0xfe, 0x73, 0x57, 0xfe, 0x80, 0x5d, 0x03, 0x94, 0xfe, 0x03, 0xdc, 0x29, 0xfe, 0x70, 0x57, - 0xfe, 0x33, 0x54, 0xfe, 0x3b, 0x54, 0xfe, 0x80, 0x5d, 0x03, 0xfe, 0x03, 0x57, 0x94, 0x29, 0xfe, - 0x00, 0xcc, 0x03, 0xfe, 0x03, 0x57, 0x94, 0x7d, 0x03, 0x01, 0xfe, 0x70, 0x16, 0x3d, 0x05, 0x43, - 0xfe, 0x0a, 0x13, 0x08, 0x1c, 0x07, 0x3f, 0xd4, 0x01, 0x95, 0x01, 0x98, 0x08, 0x3b, 0x07, 0x99, - 0x01, 0x40, 0x11, 0xfe, 0xe9, 0x00, 0x0b, 0x09, 0x89, 0xfe, 0x52, 0x13, 0x01, 0xfe, 0x02, 0x16, - 0xfe, 0x1e, 0x1c, 0xfe, 0x14, 0x90, 0x0f, 0xfe, 0x64, 0x01, 0xfe, 0x16, 0x90, 0x0f, 0xfe, 0x66, - 0x01, 0x0b, 0x09, 0x74, 0x8d, 0xfe, 0x03, 0x80, 0x6b, 0x3c, 0x11, 0x75, 0x08, 0x2b, 0x07, 0x3a, - 0x1d, 0x92, 0x01, 0x6a, 0xfe, 0x62, 0x08, 0x68, 0x3c, 0x11, 0x75, 0x08, 0x2b, 0x07, 0x3a, 0x1d, - 0x92, 0x01, 0x6a, 0x62, 0x2d, 0x11, 0x75, 0x08, 0x2b, 0x07, 0x3a, 0x1d, 0x92, 0x01, 0x76, 0x03, - 0xfe, 0x08, 0x1c, 0x04, 0xfe, 0xac, 0x00, 0xfe, 0x06, 0x58, 0x04, 0xfe, 0xae, 0x00, 0xfe, 0x07, - 0x58, 0x04, 0xfe, 0xb0, 0x00, 0xfe, 0x08, 0x58, 0x04, 0xfe, 0xb2, 0x00, 0xfe, 0x09, 0x58, 0xfe, - 0x0a, 0x1c, 0x21, 0x87, 0x16, 0xf7, 0x29, 0x0f, 0x52, 0x0f, 0x4d, 0x21, 0x10, 0x16, 0x2b, 0x16, - 0x3a, 0x56, 0x9f, 0xd6, 0x08, 0x2b, 0x07, 0x3a, 0x1d, 0x01, 0x76, 0x7f, 0x11, 0x75, 0xfe, 0x14, - 0x56, 0xfe, 0xd6, 0xf0, 0xfe, 0xf6, 0x0e, 0xd5, 0x8b, 0xfe, 0x14, 0x1c, 0xfe, 0x10, 0x1c, 0xfe, - 0x18, 0x1c, 0x03, 0x1d, 0xfe, 0x0c, 0x14, 0x85, 0xfe, 0x07, 0xe6, 0x36, 0xfe, 0xce, 0x47, 0xfe, - 0xf5, 0x13, 0x03, 0x01, 0xb6, 0x0e, 0x3b, 0x01, 0x14, 0x05, 0x10, 0xd3, 0x0e, 0x1c, 0x01, 0x14, - 0x05, 0x10, 0xdb, 0xfe, 0x44, 0x58, 0x3c, 0xfe, 0x01, 0xec, 0xba, 0xfe, 0x9e, 0x40, 0xfe, 0x9d, - 0xe7, 0x00, 0xfe, 0x9c, 0xe7, 0x1e, 0x9e, 0x2d, 0x01, 0xd7, 0xfe, 0xc9, 0x10, 0x03, 0x35, 0x81, - 0x6d, 0x23, 0x28, 0xb1, 0x05, 0x1e, 0xfe, 0x48, 0x12, 0x05, 0x0c, 0xfe, 0x4c, 0x12, 0x05, 0x18, - 0x38, 0x05, 0xcc, 0x1b, 0xfe, 0xc0, 0x10, 0x05, 0xfe, 0x23, 0x00, 0x1b, 0xfe, 0xcc, 0x10, 0x05, - 0x06, 0x1b, 0xfe, 0x2a, 0x11, 0x05, 0x19, 0xfe, 0x12, 0x12, 0x05, 0x00, 0x1b, 0x25, 0x17, 0xcc, - 0x01, 0x38, 0xc4, 0x39, 0x01, 0x0a, 0x80, 0x4a, 0x03, 0x39, 0x11, 0xfe, 0xcc, 0x00, 0x02, 0x26, - 0x39, 0x3d, 0x05, 0xbf, 0xfe, 0xe3, 0x13, 0x63, 0x41, 0x30, 0x42, 0x73, 0xfe, 0x7e, 0x10, 0x0b, - 0x09, 0x6f, 0xfe, 0x72, 0x12, 0xa0, 0x3e, 0x67, 0x57, 0xe7, 0xfe, 0xe5, 0x55, 0x91, 0xfe, 0x48, - 0x10, 0x22, 0x60, 0xfe, 0x26, 0x13, 0x04, 0xaa, 0x2e, 0x8c, 0x73, 0xfe, 0x98, 0x0c, 0x0f, 0x5c, - 0x13, 0x5d, 0x29, 0x0f, 0xaa, 0x0f, 0x8c, 0x01, 0xb7, 0x21, 0x87, 0x6b, 0x16, 0x60, 0x01, 0xf6, - 0xa0, 0x3e, 0x67, 0x57, 0xfe, 0x04, 0x55, 0xfe, 0xa5, 0x55, 0xfe, 0x04, 0xfa, 0x3e, 0xfe, 0x05, - 0xfa, 0x57, 0xfe, 0x91, 0x10, 0x04, 0x5e, 0x2e, 0x5f, 0xfe, 0x40, 0x56, 0xfe, 0xe1, 0x56, 0x0f, - 0x5e, 0x13, 0x5f, 0xd1, 0xa0, 0x3e, 0x67, 0x57, 0xe7, 0xfe, 0xe5, 0x55, 0x04, 0x5a, 0x2e, 0x5b, - 0xfe, 0x00, 0x56, 0xfe, 0xa1, 0x56, 0x0f, 0x5a, 0x13, 0x5b, 0x0b, 0x09, 0x6f, 0xfe, 0x1e, 0x12, - 0x22, 0x60, 0xfe, 0x1f, 0x40, 0x04, 0x5c, 0x2e, 0x5d, 0xfe, 0x2c, 0x50, 0xfe, 0xae, 0x50, 0x04, - 0x5e, 0x2e, 0x5f, 0xfe, 0x34, 0x50, 0xfe, 0xb6, 0x50, 0x04, 0x5a, 0x2e, 0x5b, 0xfe, 0x08, 0x50, - 0xfe, 0x8a, 0x50, 0x04, 0x41, 0x2e, 0x42, 0xfe, 0x28, 0x50, 0xfe, 0xaa, 0x50, 0x02, 0x97, 0x21, - 0x06, 0x16, 0xf1, 0x02, 0x78, 0x39, 0x01, 0x0a, 0x20, 0x4c, 0x23, 0x28, 0xb1, 0x05, 0x06, 0x27, - 0x4c, 0x3d, 0x05, 0xbf, 0x27, 0x78, 0x01, 0xee, 0x1a, 0x4f, 0x1b, 0x4c, 0x0b, 0x09, 0x0c, 0xde, - 0x63, 0x41, 0x30, 0x42, 0xfe, 0x0a, 0x55, 0x2f, 0xfe, 0x8b, 0x55, 0x9b, 0x41, 0x9c, 0x42, 0xfe, - 0x0c, 0x51, 0xfe, 0x8e, 0x51, 0x02, 0x78, 0xfe, 0x19, 0x81, 0xfe, 0x0a, 0x45, 0xfe, 0x19, 0x41, - 0x02, 0x78, 0x39, 0x01, 0x0a, 0x20, 0xfe, 0xc2, 0x0f, 0x23, 0x28, 0xfe, 0xe9, 0x09, 0x58, 0x18, - 0xfe, 0x94, 0x12, 0x58, 0x0c, 0x50, 0x02, 0x4c, 0x2c, 0xfe, 0x4a, 0x11, 0x1f, 0x31, 0x20, 0xfe, - 0xc2, 0x0f, 0x23, 0x28, 0x93, 0x05, 0x18, 0x27, 0x4c, 0x01, 0x0a, 0x20, 0xfe, 0xc2, 0x0f, 0x23, - 0x28, 0xfe, 0xe8, 0x09, 0x56, 0x04, 0xfe, 0x9c, 0x00, 0x2a, 0x2f, 0xfe, 0xbb, 0x45, 0x58, 0x00, - 0x48, 0x34, 0x06, 0x9f, 0x4f, 0xfe, 0xc0, 0x14, 0xfe, 0xf8, 0x14, 0xa8, 0x3d, 0x05, 0xbe, 0xfe, - 0x16, 0x13, 0x04, 0xf4, 0x2a, 0xce, 0x04, 0x4d, 0x2a, 0x2f, 0x59, 0x02, 0x78, 0xfe, 0xc0, 0x5d, - 0xfe, 0xe4, 0x14, 0xfe, 0x03, 0x17, 0x04, 0x52, 0xb9, 0x0f, 0x52, 0x59, 0x39, 0x01, 0x0a, 0x24, - 0x97, 0x01, 0xfe, 0xf0, 0x13, 0x02, 0x97, 0x2c, 0xfe, 0xd4, 0x11, 0x1f, 0x31, 0x20, 0x4c, 0x23, - 0x28, 0x93, 0x05, 0x06, 0x27, 0x4c, 0xfe, 0xf6, 0x14, 0xfe, 0x42, 0x58, 0xfe, 0x70, 0x14, 0xfe, - 0x92, 0x14, 0xa8, 0xfe, 0x4a, 0xf4, 0x0c, 0x1b, 0x4c, 0xfe, 0x4a, 0xf4, 0x06, 0xd2, 0x3d, 0x05, - 0xbe, 0xc7, 0x02, 0x78, 0x04, 0x4d, 0xb9, 0x0f, 0x4d, 0x59, 0x39, 0x01, 0x0a, 0x24, 0x97, 0x01, - 0xfe, 0x1e, 0x14, 0x02, 0x97, 0x24, 0xfe, 0x3c, 0x12, 0x71, 0xef, 0x71, 0x03, 0x33, 0x8d, 0x69, - 0x8d, 0x59, 0x39, 0x01, 0x0a, 0xfe, 0xe3, 0x10, 0x08, 0x61, 0xff, 0x02, 0x00, 0x57, 0x64, 0x7e, - 0x1a, 0xfe, 0xff, 0x7f, 0xfe, 0x30, 0x56, 0xfe, 0x00, 0x5c, 0x03, 0x08, 0x61, 0xff, 0x02, 0x00, - 0x57, 0x64, 0x7e, 0x1a, 0x4f, 0xfe, 0x30, 0x56, 0xfe, 0x00, 0x5c, 0x03, 0x08, 0x61, 0xff, 0x02, - 0x00, 0x57, 0x64, 0x7e, 0x03, 0x08, 0x61, 0xff, 0x02, 0x00, 0x57, 0x64, 0x7e, 0xfe, 0x0b, 0x58, - 0x03, 0x0e, 0x52, 0x01, 0x9a, 0x0e, 0x4d, 0x01, 0x9a, 0x03, 0xc6, 0x1a, 0x10, 0xff, 0x03, 0x00, - 0x54, 0xfe, 0x00, 0xf4, 0x19, 0x64, 0xfe, 0x00, 0x7d, 0xfe, 0x01, 0x7d, 0xfe, 0x02, 0x7d, 0xfe, - 0x03, 0x7c, 0x62, 0x2d, 0x0f, 0x5a, 0x13, 0x5b, 0x9b, 0x5e, 0x9c, 0x5f, 0x03, 0xfe, 0x62, 0x18, - 0xfe, 0x82, 0x5a, 0xfe, 0xe1, 0x1a, 0xb4, 0xfe, 0x02, 0x58, 0x03, 0x01, 0xfe, 0x60, 0x18, 0xfe, - 0x42, 0x48, 0x79, 0x56, 0x7a, 0x01, 0x0a, 0x20, 0xfe, 0xe8, 0x13, 0x23, 0x28, 0xfe, 0xe9, 0x09, - 0xfe, 0xc1, 0x59, 0x01, 0x0a, 0x20, 0xfe, 0xe8, 0x13, 0x23, 0x28, 0xfe, 0xe8, 0x0a, 0x04, 0xf4, - 0x2a, 0xfe, 0xc2, 0x12, 0x29, 0xad, 0x1e, 0xde, 0x58, 0xcd, 0x72, 0xfe, 0x38, 0x13, 0x50, 0x08, - 0x06, 0x07, 0xcd, 0x9f, 0xfe, 0x00, 0x10, 0xfe, 0x78, 0x10, 0xff, 0x02, 0x83, 0x55, 0xa4, 0xff, - 0x02, 0x83, 0x55, 0xad, 0x18, 0xfe, 0x12, 0x13, 0x70, 0xfe, 0x30, 0x00, 0x91, 0xf0, 0x07, 0x84, - 0x08, 0x06, 0xfe, 0x56, 0x10, 0xad, 0x0c, 0xfe, 0x16, 0x13, 0x70, 0xfe, 0x64, 0x00, 0x91, 0xf0, - 0x0e, 0xfe, 0x64, 0x00, 0x07, 0x88, 0x08, 0x06, 0xfe, 0x28, 0x10, 0xad, 0x06, 0xfe, 0x5e, 0x13, - 0x70, 0xfe, 0xc8, 0x00, 0x91, 0xf0, 0x0e, 0xfe, 0xc8, 0x00, 0x07, 0x54, 0x08, 0x06, 0xd1, 0x70, - 0xfe, 0x90, 0x01, 0xea, 0xfe, 0x9e, 0x13, 0x7a, 0xa7, 0xfe, 0x43, 0xf4, 0xa9, 0xfe, 0x56, 0xf0, - 0xfe, 0xb0, 0x13, 0xfe, 0x04, 0xf4, 0x61, 0xfe, 0x43, 0xf4, 0x88, 0xfe, 0xf3, 0x10, 0xac, 0x01, - 0xfe, 0x7a, 0x12, 0x1a, 0x4f, 0xd3, 0xfe, 0x00, 0x17, 0xfe, 0x4d, 0xe4, 0x87, 0xea, 0xfe, 0xe2, - 0x13, 0x7a, 0xfe, 0x14, 0x10, 0xfe, 0x00, 0x17, 0xfe, 0x4d, 0xe4, 0x19, 0xea, 0xfe, 0xe2, 0x13, - 0xc8, 0x19, 0x9d, 0x56, 0x7a, 0x08, 0x06, 0xfe, 0xb4, 0x56, 0xfe, 0xc3, 0x58, 0x03, 0x56, 0x08, - 0x0c, 0x03, 0x15, 0x06, 0x01, 0x0a, 0x24, 0xdc, 0x15, 0x0c, 0x01, 0x0a, 0x24, 0xdc, 0x15, 0x18, - 0x01, 0x0a, 0x24, 0xdc, 0x71, 0xfe, 0x89, 0x49, 0x01, 0x0a, 0x03, 0x15, 0x06, 0x01, 0x0a, 0x24, - 0x90, 0x15, 0x18, 0x01, 0x0a, 0x24, 0x90, 0x15, 0x06, 0x01, 0x0a, 0x24, 0x90, 0xfe, 0x89, 0x49, - 0x01, 0x0a, 0x24, 0x90, 0x71, 0xfe, 0x89, 0x4a, 0x01, 0x0a, 0x03, 0x56, 0x03, 0x22, 0xe2, 0x05, - 0x06, 0xfe, 0x44, 0x13, 0xab, 0x16, 0xe2, 0xfe, 0x49, 0xf4, 0x00, 0x50, 0x71, 0xc4, 0x59, 0xfe, - 0x01, 0xec, 0xfe, 0x27, 0x01, 0xef, 0x01, 0x0a, 0x3d, 0x05, 0xfe, 0xe3, 0x00, 0xfe, 0x20, 0x13, - 0x20, 0xfe, 0xa0, 0x14, 0x29, 0x16, 0xf1, 0x01, 0x4a, 0x22, 0xf1, 0x05, 0x06, 0x48, 0x0b, 0x49, - 0x06, 0x37, 0x03, 0x0f, 0x53, 0x13, 0x8a, 0xfe, 0x43, 0x58, 0x01, 0x14, 0x05, 0x10, 0xfe, 0x1e, - 0x12, 0x45, 0xe6, 0x8f, 0x01, 0x44, 0xfe, 0x90, 0x4d, 0xe0, 0x10, 0xfe, 0xc5, 0x59, 0x01, 0x44, - 0xfe, 0x8d, 0x56, 0xb4, 0x45, 0x03, 0x45, 0x30, 0x8a, 0x01, 0x14, 0x45, 0x8f, 0x01, 0x44, 0xe4, - 0x10, 0xe0, 0x10, 0x30, 0x53, 0x70, 0x1c, 0x83, 0x0e, 0x55, 0x01, 0xc0, 0x03, 0x0f, 0x53, 0x13, - 0x8a, 0xfe, 0xc3, 0x58, 0x01, 0x14, 0x05, 0x10, 0xfe, 0x1a, 0x12, 0x45, 0xe6, 0x8f, 0x01, 0x44, - 0xe4, 0x10, 0xfe, 0x80, 0x4d, 0xfe, 0xc5, 0x59, 0x01, 0x44, 0x45, 0x03, 0x45, 0x30, 0x53, 0x01, - 0x14, 0x45, 0x8f, 0x01, 0x44, 0xe4, 0x10, 0xe0, 0x10, 0x30, 0x53, 0x70, 0x1c, 0x83, 0x0e, 0x55, - 0x01, 0xc0, 0x03, 0x0f, 0x53, 0x13, 0x8a, 0xfe, 0x43, 0x58, 0x01, 0x14, 0xfe, 0x42, 0x48, 0x8f, - 0x01, 0x44, 0xfe, 0xc0, 0x5a, 0xac, 0xfe, 0x00, 0xcd, 0xfe, 0x01, 0xcc, 0xfe, 0x4a, 0x46, 0xde, - 0x94, 0x7d, 0x05, 0x10, 0xfe, 0x2e, 0x13, 0x67, 0x53, 0xfe, 0x4d, 0xf4, 0x1c, 0xfe, 0x1c, 0x13, - 0x0e, 0x55, 0x01, 0x9a, 0xa7, 0xfe, 0x40, 0x4c, 0xfe, 0xc5, 0x58, 0x01, 0x44, 0xfe, 0x00, 0x07, - 0x7d, 0x05, 0x10, 0x83, 0x67, 0x8a, 0xfe, 0x05, 0x57, 0xfe, 0x08, 0x10, 0xfe, 0x45, 0x58, 0x01, - 0x44, 0xfe, 0x8d, 0x56, 0xb4, 0xfe, 0x80, 0x4c, 0xfe, 0x05, 0x17, 0x03, 0x07, 0x10, 0x6e, 0x65, - 0xfe, 0x60, 0x01, 0xfe, 0x18, 0xdf, 0xfe, 0x19, 0xde, 0xfe, 0x24, 0x1c, 0xdd, 0x36, 0x96, 0xfe, - 0xe4, 0x15, 0x01, 0xfe, 0xea, 0x16, 0xfe, 0x0c, 0x13, 0x86, 0x36, 0x65, 0xfe, 0x2c, 0x01, 0xfe, - 0x2f, 0x19, 0x03, 0xb5, 0x27, 0xfe, 0xd4, 0x15, 0xfe, 0xda, 0x10, 0x07, 0x10, 0x6e, 0x04, 0xfe, - 0x64, 0x01, 0xfe, 0x00, 0xf4, 0x19, 0xfe, 0x18, 0x58, 0x04, 0xfe, 0x66, 0x01, 0xfe, 0x19, 0x58, - 0x86, 0x19, 0xfe, 0x3c, 0x90, 0xfe, 0x30, 0xf4, 0x06, 0xfe, 0x3c, 0x50, 0x65, 0xfe, 0x38, 0x00, - 0xfe, 0x0f, 0x79, 0xfe, 0x1c, 0xf7, 0x19, 0x96, 0xfe, 0x2e, 0x16, 0xfe, 0xb6, 0x14, 0x2f, 0x03, - 0xb5, 0x27, 0xfe, 0x06, 0x16, 0xfe, 0x9c, 0x10, 0x07, 0x10, 0x6e, 0xb4, 0xfe, 0x18, 0xdf, 0xfe, - 0x19, 0xdf, 0xdd, 0x3e, 0x96, 0xfe, 0x50, 0x16, 0xfe, 0x94, 0x14, 0xfe, 0x10, 0x13, 0x86, 0x3e, - 0x65, 0x1e, 0xfe, 0xaf, 0x19, 0xfe, 0x98, 0xe7, 0x00, 0x03, 0xb5, 0x27, 0xfe, 0x44, 0x16, 0xfe, - 0x6c, 0x10, 0x07, 0x10, 0x6e, 0xfe, 0x30, 0xbc, 0xfe, 0xb2, 0xbc, 0x86, 0xda, 0x65, 0x1e, 0xfe, - 0x0f, 0x79, 0xfe, 0x1c, 0xf7, 0xda, 0x96, 0xfe, 0x88, 0x16, 0xfe, 0x5c, 0x14, 0x2f, 0x03, 0xb5, - 0x27, 0xfe, 0x74, 0x16, 0xfe, 0x42, 0x10, 0xfe, 0x02, 0xf6, 0x10, 0x6e, 0xfe, 0x18, 0xfe, 0x5c, - 0xfe, 0x19, 0xfe, 0x5d, 0xc6, 0xdd, 0x74, 0x96, 0xfe, 0xae, 0x16, 0xfe, 0x36, 0x14, 0xfe, 0x1c, - 0x13, 0x86, 0x74, 0x4e, 0xfe, 0x83, 0x58, 0xfe, 0xaf, 0x19, 0xfe, 0x80, 0xe7, 0x10, 0xfe, 0x81, - 0xe7, 0x10, 0x11, 0xfe, 0xdd, 0x00, 0x62, 0x2d, 0x03, 0x62, 0x2d, 0xfe, 0x12, 0x45, 0x27, 0xfe, - 0x9e, 0x16, 0x17, 0x06, 0x47, 0xf2, 0xdf, 0x02, 0x26, 0xfe, 0x39, 0xf0, 0xfe, 0xf2, 0x16, 0x29, - 0x03, 0xfe, 0x7e, 0x18, 0x1a, 0x18, 0x82, 0x08, 0x0d, 0x03, 0x6e, 0x04, 0xe1, 0x1a, 0x06, 0xfe, - 0xef, 0x12, 0xfe, 0xe1, 0x10, 0x1d, 0x0e, 0x1c, 0x01, 0x14, 0x05, 0x10, 0x48, 0x3c, 0xfe, 0x78, - 0x14, 0xfe, 0x34, 0x12, 0x4f, 0x85, 0x34, 0x36, 0xbc, 0xfe, 0xe9, 0x13, 0x1d, 0x0e, 0x3b, 0x01, - 0x14, 0x05, 0x10, 0x48, 0x3c, 0x90, 0xe3, 0x4f, 0x85, 0x34, 0x36, 0xbc, 0xfe, 0xe9, 0x13, 0x07, - 0x0c, 0x03, 0xfe, 0x9c, 0xe7, 0x0c, 0x12, 0xfe, 0x15, 0x00, 0x92, 0x9e, 0x2d, 0x01, 0xd7, 0x07, - 0x06, 0x03, 0x0b, 0x49, 0x36, 0x37, 0x08, 0x3b, 0x07, 0x99, 0x01, 0x40, 0x11, 0x43, 0x08, 0x1c, - 0x07, 0x3f, 0x01, 0x76, 0x07, 0x06, 0x03, 0xfe, 0x38, 0x90, 0xfe, 0xba, 0x90, 0x63, 0xf5, 0x30, - 0x75, 0xfe, 0x48, 0x55, 0x2f, 0xfe, 0xc9, 0x55, 0x03, 0x22, 0xbb, 0x6b, 0x16, 0xbb, 0x03, 0x0e, - 0xbd, 0x01, 0x14, 0xe5, 0x0e, 0x7c, 0x01, 0x14, 0xfe, 0x49, 0x44, 0x27, 0xfe, 0xe8, 0x17, 0x0e, - 0x1c, 0x01, 0x14, 0x05, 0x10, 0x48, 0x0e, 0x55, 0x01, 0xc0, 0x0e, 0x7c, 0x01, 0x14, 0x6b, 0x7d, - 0x03, 0xfe, 0x40, 0x5e, 0xfe, 0xe2, 0x08, 0xfe, 0xc0, 0x4c, 0x22, 0x3a, 0x05, 0x10, 0xfe, 0x52, - 0x12, 0x3c, 0x05, 0x00, 0xfe, 0x18, 0x12, 0xfe, 0xe1, 0x18, 0xfe, 0x19, 0xf4, 0xfe, 0x7f, 0x00, - 0xfe, 0x10, 0x13, 0xfe, 0xe2, 0x08, 0x6b, 0x3c, 0x3d, 0x05, 0x75, 0xa3, 0xfe, 0x82, 0x48, 0xfe, - 0x01, 0x80, 0xfe, 0xd7, 0x10, 0xfe, 0xc4, 0x48, 0x08, 0x2b, 0x07, 0x3a, 0xfe, 0x40, 0x5f, 0x1d, - 0x01, 0x40, 0x11, 0xfe, 0xdd, 0x00, 0xfe, 0x14, 0x46, 0x08, 0x2b, 0x07, 0x3a, 0x01, 0x40, 0x11, - 0xfe, 0xdd, 0x00, 0xfe, 0x40, 0x4a, 0x68, 0xfe, 0x06, 0x17, 0xfe, 0x01, 0x07, 0xfe, 0x82, 0x48, - 0xfe, 0x04, 0x17, 0x03, 0xe9, 0x18, 0x72, 0xfe, 0x70, 0x18, 0x04, 0xfe, 0x90, 0x00, 0xfe, 0x3a, - 0x45, 0xfe, 0x2c, 0x10, 0xe9, 0xcc, 0x72, 0xfe, 0x82, 0x18, 0x04, 0xfe, 0x92, 0x00, 0xc5, 0x1e, - 0xd9, 0xe9, 0xfe, 0x0b, 0x00, 0x72, 0xfe, 0x94, 0x18, 0x04, 0xfe, 0x94, 0x00, 0xc5, 0x19, 0xfe, - 0x08, 0x10, 0x04, 0xfe, 0x96, 0x00, 0xc5, 0x84, 0xfe, 0x4e, 0x45, 0xd2, 0xfe, 0x0a, 0x45, 0xff, - 0x04, 0x68, 0x54, 0xfe, 0xf1, 0x10, 0x1a, 0x87, 0x03, 0x05, 0xa1, 0xfe, 0x5a, 0xf0, 0xfe, 0xc0, - 0x18, 0x21, 0xfe, 0x09, 0x00, 0xfe, 0x34, 0x10, 0x05, 0x1e, 0xfe, 0x5a, 0xf0, 0xfe, 0xce, 0x18, - 0x21, 0xcd, 0xfe, 0x26, 0x10, 0x05, 0x18, 0x82, 0x21, 0x84, 0xd9, 0x05, 0x0c, 0x82, 0x21, 0x88, - 0xfe, 0x0e, 0x10, 0x05, 0x06, 0x82, 0x21, 0x54, 0xc4, 0xab, 0x03, 0x17, 0xfe, 0x09, 0x00, 0x01, - 0x38, 0x2c, 0xfe, 0xfe, 0x18, 0x04, 0x6d, 0xac, 0x03, 0x1f, 0xfe, 0x16, 0x19, 0xfe, 0x14, 0xf0, - 0x0a, 0x2c, 0xfe, 0x12, 0x19, 0x03, 0xff, 0x34, 0x00, 0x00,}; + 0x00, 0x00, 0x00, 0xf2, 0x00, 0xf0, 0x00, 0x16, 0x00, 0xfc, 0x48, 0xe4, 0x01, 0x00, 0x18, 0xe4, + 0x00, 0xf6, 0x01, 0xf6, 0x18, 0x80, 0x48, 0x19, 0x02, 0x00, 0xff, 0xff, 0x03, 0xf6, 0x00, 0xfa, + 0xff, 0x00, 0x82, 0xe7, 0x01, 0xfa, 0x9e, 0xe7, 0x09, 0xe7, 0x3a, 0x0e, 0x00, 0xea, 0x01, 0xe6, + 0x55, 0xf0, 0x03, 0x00, 0x08, 0x00, 0x18, 0xf4, 0x3e, 0x01, 0x3e, 0x57, 0x04, 0x00, 0x85, 0xf0, + 0x00, 0xe6, 0x00, 0xec, 0x1e, 0xf0, 0x32, 0xf0, 0x34, 0x19, 0x86, 0xf0, 0xd0, 0x01, 0xd5, 0xf0, + 0xde, 0x0c, 0x98, 0x57, 0xbc, 0x00, 0x0c, 0x1c, 0x0e, 0x13, 0x38, 0x54, 0xb1, 0xf0, 0xb4, 0x00, + 0x01, 0xfc, 0x03, 0xfc, 0xd8, 0x0c, 0x00, 0x57, 0x01, 0xf0, 0x02, 0x13, 0x03, 0xe6, 0x10, 0x00, + 0x18, 0x40, 0x3e, 0x1c, 0x6c, 0x01, 0x6e, 0x01, 0xbd, 0x00, 0xe0, 0x00, 0x02, 0x48, 0x02, 0x80, + 0x08, 0x12, 0x30, 0xe4, 0x3c, 0x00, 0x4e, 0x01, 0x64, 0x12, 0x80, 0x00, 0x9c, 0x15, 0xbb, 0x00, + 0x00, 0x4e, 0x01, 0x01, 0x01, 0xea, 0x04, 0x12, 0x9e, 0x0f, 0xb6, 0x00, 0xb9, 0x54, 0xe2, 0x0f, + 0x00, 0x80, 0x06, 0xf7, 0x10, 0x44, 0x24, 0x01, 0x28, 0x01, 0x32, 0x00, 0x3c, 0x01, 0x3c, 0x56, + 0x3e, 0x00, 0x4b, 0xe4, 0x4c, 0x1c, 0x68, 0x01, 0x6a, 0x01, 0x70, 0x01, 0x72, 0x01, 0x74, 0x01, + 0x76, 0x01, 0x78, 0x01, 0xe2, 0x0c, 0x00, 0x01, 0x02, 0xee, 0x02, 0xfc, 0x03, 0x58, 0x03, 0xf7, + 0x04, 0x80, 0x05, 0xfc, 0x08, 0x44, 0x09, 0xf0, 0x0f, 0x00, 0x1b, 0x80, 0x20, 0x01, 0x38, 0x1c, + 0x40, 0x00, 0x40, 0x15, 0x4b, 0xf4, 0x4e, 0x1c, 0x5b, 0xf0, 0x5d, 0xf0, 0xaa, 0x00, 0xbb, 0x55, + 0xbe, 0x00, 0xc0, 0x00, 0xe0, 0x08, 0xe0, 0x14, 0xec, 0x0f, 0x00, 0x4c, 0x00, 0xdc, 0x02, 0x4a, + 0x05, 0x00, 0x05, 0xf0, 0x05, 0xf8, 0x06, 0x13, 0x08, 0x13, 0x0c, 0x00, 0x0e, 0x47, 0x0e, 0xf7, + 0x19, 0x00, 0x20, 0x00, 0x2a, 0x01, 0x30, 0x0e, 0x32, 0x1c, 0x36, 0x00, 0x45, 0x5a, 0x59, 0xf0, + 0x62, 0x0a, 0x69, 0x08, 0x72, 0x0b, 0x83, 0x59, 0xb8, 0xf0, 0xbd, 0x56, 0xcc, 0x12, 0xec, 0x17, + 0xee, 0x0f, 0xf0, 0x00, 0xf8, 0x17, 0x01, 0x48, 0x02, 0xfa, 0x03, 0xfa, 0x04, 0x10, 0x04, 0xea, + 0x04, 0xf6, 0x04, 0xfc, 0x05, 0x80, 0x05, 0xe6, 0x06, 0x00, 0x06, 0x12, 0x0a, 0x10, 0x0b, 0xf0, + 0x0c, 0x10, 0x0c, 0xf0, 0x12, 0x10, 0x26, 0x0e, 0x30, 0x1c, 0x33, 0x00, 0x34, 0x00, 0x38, 0x44, + 0x40, 0x5c, 0x4a, 0xe4, 0x62, 0x1a, 0x68, 0x08, 0x68, 0x54, 0x83, 0x55, 0x83, 0x5a, 0x8c, 0x14, + 0x8e, 0x0a, 0x90, 0x14, 0x91, 0x44, 0xa4, 0x00, 0xb0, 0x57, 0xb5, 0x00, 0xba, 0x00, 0xce, 0x45, + 0xd0, 0x00, 0xd8, 0x16, 0xe1, 0x00, 0xe7, 0x00, 0x00, 0x54, 0x01, 0x58, 0x02, 0x10, 0x02, 0xe6, + 0x03, 0xa1, 0x04, 0x13, 0x06, 0x83, 0x06, 0xf0, 0x07, 0x00, 0x0a, 0x00, 0x0a, 0x12, 0x0a, 0xf0, + 0x0c, 0x04, 0x0c, 0x12, 0x0c, 0x90, 0x10, 0x10, 0x10, 0x13, 0x12, 0x1c, 0x17, 0x00, 0x19, 0xe4, + 0x1a, 0x10, 0x1c, 0x00, 0x1c, 0x12, 0x1d, 0xf7, 0x1e, 0x13, 0x20, 0x1c, 0x20, 0xe7, 0x22, 0x01, + 0x26, 0x01, 0x2a, 0x12, 0x30, 0xe7, 0x34, 0x1c, 0x36, 0x1c, 0x38, 0x12, 0x41, 0x58, 0x43, 0x48, + 0x44, 0x55, 0x46, 0x1c, 0x4c, 0x0e, 0x4e, 0xe4, 0x52, 0x14, 0x5c, 0xf0, 0x72, 0x02, 0x74, 0x03, + 0x77, 0x57, 0x89, 0x48, 0x8e, 0x90, 0x99, 0x00, 0x9b, 0x00, 0x9c, 0x32, 0x9e, 0x00, 0xa8, 0x00, + 0xb9, 0x00, 0xba, 0x06, 0xbc, 0x12, 0xbf, 0x57, 0xc0, 0x01, 0xfe, 0x9c, 0xf0, 0x26, 0x02, 0xfe, + 0x00, 0x0d, 0xff, 0x10, 0x00, 0x00, 0xfe, 0xc2, 0x01, 0xfe, 0x56, 0x19, 0x00, 0xfc, 0xfe, 0x80, + 0x01, 0xff, 0x03, 0x00, 0x00, 0xfe, 0x6a, 0x13, 0xfe, 0x05, 0x05, 0xff, 0x40, 0x00, 0x00, 0x0d, + 0xff, 0x09, 0x00, 0x00, 0xff, 0x08, 0x01, 0x01, 0xff, 0x10, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, + 0xff, 0x10, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xfe, 0x78, 0x56, 0xfe, 0x34, 0x12, 0xff, 0x21, + 0x00, 0x00, 0xfe, 0x04, 0xf7, 0xfc, 0x2b, 0x51, 0x0c, 0x01, 0xfe, 0xea, 0x0e, 0xfe, 0x04, 0xf7, + 0xfc, 0x51, 0x0c, 0x1d, 0x2b, 0xfe, 0x3d, 0xf0, 0xfe, 0xf8, 0x01, 0xfe, 0x20, 0xf0, 0xd0, 0x04, + 0x56, 0x4b, 0x02, 0xfe, 0x1c, 0x0d, 0x01, 0xfe, 0x7c, 0x0d, 0xfe, 0xe9, 0x12, 0x02, 0xfe, 0x04, + 0x03, 0xfe, 0x28, 0x1c, 0x04, 0xfe, 0xa6, 0x00, 0xfe, 0xdd, 0x12, 0x4e, 0x12, 0xfe, 0xa6, 0x00, + 0xc5, 0xfe, 0x48, 0xf0, 0xfe, 0x7c, 0x02, 0xfe, 0x49, 0xf0, 0xfe, 0x96, 0x02, 0xfe, 0x4a, 0xf0, + 0xfe, 0xb4, 0x02, 0xfe, 0x46, 0xf0, 0xfe, 0x46, 0x02, 0xfe, 0x47, 0xf0, 0xfe, 0x4c, 0x02, 0xfe, + 0x43, 0xf0, 0xfe, 0x3a, 0x02, 0xfe, 0x44, 0xf0, 0xfe, 0x3e, 0x02, 0xfe, 0x45, 0xf0, 0xfe, 0x42, + 0x02, 0x09, 0x0c, 0x9e, 0x09, 0x06, 0x12, 0xbb, 0x02, 0x26, 0xfe, 0x00, 0x1c, 0xfe, 0xf1, 0x10, + 0xfe, 0x02, 0x1c, 0xfe, 0xed, 0x10, 0xfe, 0x1e, 0x1c, 0xfe, 0xe9, 0x10, 0x01, 0xfe, 0x4c, 0x17, + 0xfe, 0xe7, 0x10, 0xfe, 0x06, 0xfc, 0xf7, 0x0e, 0x78, 0x01, 0xab, 0x02, 0x26, 0x17, 0x55, 0x4a, + 0xbd, 0x01, 0xfe, 0x60, 0x0f, 0x0e, 0x78, 0x01, 0x8b, 0xfe, 0xbd, 0x10, 0x0e, 0x78, 0x01, 0x8b, + 0xfe, 0xad, 0x10, 0xfe, 0x16, 0x1c, 0xfe, 0x58, 0x1c, 0x09, 0x06, 0x12, 0xbb, 0x2b, 0x22, 0x26, + 0xfe, 0x3d, 0xf0, 0xfe, 0xf8, 0x01, 0x27, 0xfe, 0x8a, 0x02, 0xfe, 0x5a, 0x1c, 0xd5, 0xfe, 0x14, + 0x1c, 0x17, 0xfe, 0x30, 0x00, 0x4a, 0xbd, 0x01, 0xfe, 0x50, 0x0f, 0x09, 0x06, 0x12, 0xbb, 0x02, + 0xfe, 0xc2, 0x01, 0x21, 0x2a, 0x05, 0x10, 0x35, 0xfe, 0x69, 0x10, 0x09, 0x06, 0x12, 0xbb, 0xfe, + 0x04, 0xec, 0x2a, 0x08, 0x2a, 0x09, 0x3c, 0x1d, 0x01, 0x46, 0x7f, 0xfe, 0x05, 0xf6, 0xf7, 0x01, + 0xfe, 0x76, 0x16, 0x0a, 0x41, 0x89, 0x38, 0x11, 0x47, 0x1d, 0xca, 0x08, 0x1c, 0x09, 0x43, 0x01, + 0x71, 0x02, 0x26, 0x0e, 0x3d, 0x01, 0x15, 0x05, 0x10, 0x2c, 0x08, 0x1c, 0x09, 0x43, 0x01, 0x7b, + 0xfe, 0x28, 0x10, 0x0e, 0xc0, 0x01, 0x15, 0xe6, 0x0e, 0x79, 0x01, 0x15, 0xfe, 0x49, 0x54, 0x74, + 0xfe, 0x12, 0x03, 0x08, 0x1c, 0x09, 0x43, 0x01, 0x71, 0x02, 0x26, 0x2b, 0x7f, 0xfe, 0x02, 0xe8, + 0x2f, 0xfb, 0xfe, 0x9e, 0x43, 0xf0, 0xfe, 0x07, 0x4b, 0xfe, 0x20, 0xf0, 0xd0, 0xfe, 0x40, 0x1c, + 0x22, 0xef, 0xfe, 0x26, 0xf0, 0xfe, 0x70, 0x03, 0xfe, 0xa0, 0xf0, 0xfe, 0x5e, 0x03, 0xfe, 0x11, + 0xf0, 0xd0, 0xfe, 0x0e, 0x10, 0xfe, 0x9f, 0xf0, 0xfe, 0x7e, 0x03, 0xe9, 0x13, 0xfe, 0x11, 0x00, + 0x02, 0x62, 0x2b, 0xfe, 0x48, 0x1c, 0xe9, 0x22, 0xef, 0x34, 0xef, 0xfe, 0x82, 0xf0, 0xfe, 0x84, + 0x03, 0x2d, 0x21, 0xbe, 0x6a, 0x16, 0xbe, 0x0e, 0x79, 0x01, 0x15, 0x6a, 0x7d, 0x08, 0x1c, 0x09, + 0x43, 0x01, 0x46, 0x11, 0x3d, 0x08, 0x3d, 0x09, 0x99, 0x01, 0x71, 0xf5, 0x11, 0xfe, 0xe4, 0x00, + 0x2e, 0xfe, 0xca, 0x03, 0x22, 0x32, 0x1f, 0xfe, 0xda, 0x03, 0x01, 0x4c, 0xcb, 0xfe, 0xea, 0x03, + 0x6b, 0x92, 0xcf, 0xfe, 0xaa, 0x06, 0x02, 0x28, 0x04, 0x78, 0x29, 0x18, 0xfe, 0x1c, 0x05, 0x17, + 0x85, 0x01, 0x44, 0x01, 0x97, 0x01, 0x9a, 0x34, 0xfe, 0x5c, 0x02, 0x02, 0xee, 0xe9, 0x2b, 0x51, + 0x19, 0xfe, 0x67, 0x1b, 0xfb, 0xf0, 0xfe, 0x48, 0x1c, 0x8c, 0x01, 0xfa, 0xac, 0xfe, 0x96, 0xf0, + 0xfe, 0x24, 0x04, 0x2e, 0xfe, 0x28, 0x04, 0x34, 0x26, 0x0e, 0x3d, 0x01, 0x15, 0x05, 0x10, 0x18, + 0xfe, 0x08, 0x05, 0x3e, 0x90, 0x9f, 0x2f, 0x82, 0x6e, 0x22, 0x32, 0x1f, 0x28, 0x04, 0x78, 0x29, + 0xfe, 0x10, 0x12, 0x17, 0x85, 0x01, 0x44, 0x34, 0xfe, 0x5c, 0x02, 0x02, 0xee, 0x31, 0xfe, 0xa0, + 0x00, 0xfe, 0x9b, 0x57, 0xfe, 0x5e, 0x12, 0x0a, 0x07, 0x06, 0xfe, 0x56, 0x12, 0x23, 0x24, 0x91, + 0x01, 0x0b, 0x82, 0x6e, 0x1f, 0xfe, 0xd8, 0x04, 0x23, 0x24, 0x91, 0x01, 0x0b, 0x1f, 0x28, 0x23, + 0x24, 0xb3, 0xfe, 0x4c, 0x44, 0xfe, 0x32, 0x12, 0x57, 0xfe, 0x44, 0x48, 0x08, 0xd6, 0xfe, 0x4c, + 0x54, 0x74, 0xfe, 0x08, 0x05, 0x7f, 0x9f, 0x2f, 0xfe, 0x06, 0x80, 0xfe, 0x48, 0x47, 0xfe, 0x48, + 0x13, 0x3f, 0x05, 0xfe, 0xcc, 0x00, 0xfe, 0x40, 0x13, 0x0a, 0x07, 0x06, 0xe5, 0xfe, 0x06, 0x10, + 0x23, 0x24, 0xb3, 0x0a, 0x07, 0x37, 0xda, 0x17, 0xa4, 0x0a, 0x07, 0x06, 0x4b, 0x17, 0xfe, 0x0d, + 0x00, 0x01, 0x44, 0x34, 0xfe, 0xc0, 0x0c, 0x02, 0x28, 0x39, 0x11, 0xfe, 0xe6, 0x00, 0xfe, 0x1c, + 0x90, 0xb0, 0x03, 0x17, 0xa4, 0x01, 0x44, 0x34, 0x26, 0x22, 0x26, 0x02, 0xfe, 0x10, 0x05, 0xfe, + 0x42, 0x5b, 0x51, 0x19, 0xfe, 0x46, 0x59, 0xfb, 0xf0, 0x17, 0x45, 0xfe, 0x07, 0x80, 0xfe, 0x31, + 0x44, 0x0a, 0x07, 0x0c, 0xfe, 0x78, 0x13, 0xfe, 0x20, 0x80, 0x05, 0x19, 0xfe, 0x70, 0x12, 0x6d, + 0x07, 0x06, 0xfe, 0x60, 0x13, 0x04, 0xfe, 0xa2, 0x00, 0x29, 0x18, 0xfe, 0xa8, 0x05, 0xfe, 0x31, + 0xe4, 0x70, 0x6d, 0x07, 0x0c, 0xfe, 0x4a, 0x13, 0x04, 0xfe, 0xa0, 0x00, 0x29, 0xfe, 0x42, 0x12, + 0x5a, 0x2e, 0xfe, 0x68, 0x05, 0x22, 0x32, 0xf1, 0x01, 0x0b, 0x25, 0xfe, 0xc0, 0x05, 0x11, 0xfe, + 0xe3, 0x00, 0x2d, 0x6d, 0xfe, 0x4a, 0xf0, 0xfe, 0x92, 0x05, 0xfe, 0x49, 0xf0, 0xfe, 0x8c, 0x05, + 0xa8, 0x20, 0xfe, 0x21, 0x00, 0xa6, 0x20, 0xfe, 0x22, 0x00, 0x9e, 0x20, 0x89, 0xfe, 0x09, 0x48, + 0x01, 0x0b, 0x25, 0xfe, 0xc0, 0x05, 0xfe, 0xe2, 0x08, 0x6d, 0x07, 0xd9, 0x4b, 0x01, 0x96, 0x20, + 0x06, 0x16, 0xe0, 0x4a, 0xfe, 0x27, 0x01, 0x0a, 0x07, 0x37, 0xe1, 0x4e, 0x01, 0xb9, 0x17, 0xa4, + 0x0a, 0x07, 0x06, 0x4b, 0x17, 0xfe, 0x0d, 0x00, 0x01, 0x44, 0x01, 0x97, 0x01, 0x9a, 0x34, 0xfe, + 0xc0, 0x0c, 0x02, 0x28, 0x04, 0xfe, 0x9c, 0x00, 0x29, 0xfe, 0x3e, 0x12, 0x04, 0x53, 0x29, 0xfe, + 0x36, 0x13, 0x4e, 0x01, 0xb9, 0x25, 0xfe, 0x38, 0x06, 0x0e, 0x06, 0x6d, 0x07, 0x1a, 0xfe, 0x02, + 0x12, 0x77, 0x01, 0xfe, 0x26, 0x14, 0x1f, 0xfe, 0x2e, 0x06, 0x11, 0xc2, 0x01, 0x4c, 0x11, 0xfe, + 0xe5, 0x00, 0x04, 0x53, 0xbc, 0x0f, 0x53, 0x04, 0xf6, 0x29, 0xfe, 0x62, 0x12, 0x04, 0x4d, 0x29, + 0xfe, 0x5a, 0x13, 0x01, 0xfe, 0x9e, 0x18, 0x01, 0xfe, 0xf0, 0x18, 0xe7, 0xa3, 0x1a, 0x08, 0x63, + 0xff, 0x02, 0x00, 0x57, 0x66, 0x7e, 0x1b, 0x50, 0xc9, 0xa3, 0x6c, 0x4e, 0x01, 0xb9, 0x25, 0xfe, + 0xa2, 0x06, 0x6d, 0x07, 0x1e, 0xa5, 0x95, 0x0e, 0x55, 0x01, 0xfe, 0x54, 0x14, 0x1f, 0xfe, 0x98, + 0x06, 0x11, 0xc2, 0x01, 0x4c, 0x11, 0xfe, 0xe5, 0x00, 0x04, 0x4d, 0xbc, 0x0f, 0x4d, 0x09, 0x06, + 0x01, 0xb9, 0xf5, 0x73, 0x8c, 0x01, 0xfa, 0xac, 0x11, 0xfe, 0xe2, 0x00, 0x2e, 0xf9, 0x22, 0x32, + 0xcf, 0xfe, 0xd6, 0x06, 0x81, 0xfe, 0x74, 0x07, 0xcb, 0xfe, 0x7c, 0x07, 0x6b, 0x92, 0x02, 0x28, + 0x0a, 0x07, 0x0c, 0xfe, 0x2e, 0x12, 0x14, 0x19, 0x01, 0x0b, 0x14, 0x00, 0x01, 0x0b, 0x14, 0x00, + 0x01, 0x0b, 0x14, 0x00, 0x01, 0x0b, 0xfe, 0x99, 0xa4, 0x01, 0x0b, 0x14, 0x00, 0x02, 0xfe, 0x4c, + 0x08, 0x68, 0x07, 0x1e, 0xe5, 0x0a, 0x07, 0x1e, 0xfe, 0x30, 0x13, 0x14, 0xfe, 0x1b, 0x00, 0x01, + 0x0b, 0x14, 0x00, 0x01, 0x0b, 0x14, 0x00, 0x01, 0x0b, 0x14, 0x00, 0x01, 0x0b, 0x14, 0x06, 0x01, + 0x0b, 0x14, 0x00, 0x02, 0xfe, 0x2a, 0x0b, 0x77, 0xfe, 0x9a, 0x81, 0x67, 0x89, 0xfe, 0x09, 0x6f, + 0xfe, 0x93, 0x45, 0x18, 0xfe, 0x84, 0x07, 0x2e, 0xfe, 0x5c, 0x07, 0x22, 0x32, 0xcf, 0xfe, 0x54, + 0x07, 0x6b, 0x92, 0x81, 0xfe, 0x74, 0x07, 0x02, 0x28, 0x01, 0x4c, 0x02, 0xf9, 0x14, 0x1a, 0x02, + 0xf9, 0xfe, 0x9c, 0xf7, 0xfe, 0xec, 0x07, 0xfe, 0x2c, 0x90, 0xfe, 0xae, 0x90, 0x75, 0xfe, 0xd2, + 0x07, 0x0f, 0x5d, 0x12, 0x5e, 0x0a, 0x41, 0x70, 0x38, 0x01, 0xfe, 0x34, 0x18, 0x05, 0x10, 0x83, + 0xfe, 0x83, 0xe7, 0x88, 0xa6, 0xfe, 0x03, 0x40, 0x0a, 0x41, 0x45, 0x38, 0x01, 0xc1, 0xaf, 0xfe, + 0x1f, 0x40, 0x16, 0x61, 0x01, 0xfe, 0xde, 0x12, 0xfe, 0x08, 0x50, 0xfe, 0x8a, 0x50, 0xfe, 0x34, + 0x51, 0xfe, 0xb6, 0x51, 0xfe, 0x08, 0x90, 0xfe, 0x8a, 0x90, 0x0f, 0x5b, 0x12, 0x5c, 0xd2, 0xf2, + 0x0f, 0x3a, 0x12, 0x3b, 0xfe, 0x60, 0x10, 0x0a, 0x07, 0x70, 0xe1, 0xfe, 0x2c, 0x90, 0xfe, 0xae, + 0x90, 0x0f, 0x5d, 0x12, 0x5e, 0x0a, 0x07, 0x45, 0xc9, 0x01, 0xc1, 0xfe, 0x1f, 0x80, 0x16, 0x61, + 0xfe, 0x34, 0x90, 0xfe, 0xb6, 0x90, 0x0f, 0x5f, 0x12, 0x60, 0xfe, 0x08, 0x90, 0xfe, 0x8a, 0x90, + 0x0f, 0x5b, 0x12, 0x5c, 0xa2, 0x07, 0x45, 0x2c, 0xd2, 0xf2, 0x0f, 0x3a, 0x12, 0x3b, 0xa8, 0xfe, + 0x28, 0x90, 0xfe, 0xaa, 0x90, 0x0f, 0x3a, 0x12, 0x3b, 0x0f, 0x42, 0x12, 0x58, 0x0a, 0x41, 0x1a, + 0x38, 0x2b, 0x08, 0x80, 0x2e, 0xfe, 0x62, 0x08, 0xfe, 0x9e, 0xf0, 0xfe, 0x76, 0x08, 0x9b, 0x18, + 0x32, 0x2b, 0x52, 0xfe, 0xed, 0x10, 0xa7, 0xfe, 0x9a, 0x08, 0xa9, 0xfe, 0xb6, 0x08, 0x81, 0xfe, + 0x8e, 0x08, 0xcb, 0xfe, 0x94, 0x08, 0x6b, 0x92, 0x02, 0x28, 0x01, 0x4c, 0xfe, 0xc9, 0x10, 0x14, + 0x1a, 0xfe, 0xc9, 0x10, 0x68, 0x07, 0x06, 0xfe, 0x10, 0x12, 0x68, 0x07, 0x0c, 0x40, 0x0a, 0x07, + 0x0c, 0xfe, 0x7e, 0x12, 0xfe, 0x2e, 0x1c, 0xaa, 0x68, 0x07, 0x06, 0x40, 0x68, 0x07, 0x0c, 0xfe, + 0x6a, 0x12, 0xfe, 0x2c, 0x1c, 0xa2, 0x07, 0x45, 0xd4, 0xa2, 0x41, 0x45, 0xfe, 0x05, 0x40, 0xd2, + 0xf2, 0xfe, 0x28, 0x50, 0xfe, 0xaa, 0x50, 0xfe, 0xaa, 0xf0, 0xfe, 0x4e, 0x09, 0xfe, 0xac, 0xf0, + 0xfe, 0xee, 0x08, 0xfe, 0x92, 0x10, 0xe3, 0xfe, 0xf3, 0x10, 0xfe, 0xad, 0xf0, 0xfe, 0xfa, 0x08, + 0x02, 0xfe, 0x5c, 0x0a, 0xe4, 0xfe, 0xe7, 0x10, 0xfe, 0x2b, 0xf0, 0xb8, 0xfe, 0x6b, 0x18, 0x1b, + 0xfe, 0x00, 0xfe, 0xda, 0xc5, 0xfe, 0xd2, 0xf0, 0xb8, 0xfe, 0x76, 0x18, 0x1b, 0x19, 0x18, 0xb8, + 0x04, 0xdf, 0x1b, 0x06, 0x18, 0xb8, 0xa7, 0x7a, 0xa9, 0x7a, 0xe3, 0xe4, 0xfe, 0xb1, 0x10, 0x8c, + 0x5a, 0x39, 0x17, 0xa4, 0x01, 0x44, 0x13, 0xfe, 0x35, 0x00, 0x34, 0x62, 0x13, 0x8d, 0x02, 0x62, + 0xfe, 0x74, 0x18, 0x1b, 0xfe, 0x00, 0xf8, 0x18, 0x7a, 0x51, 0x1e, 0x01, 0xfe, 0x7c, 0x0d, 0xd1, + 0x08, 0x1c, 0x09, 0x43, 0x01, 0x71, 0x21, 0x2f, 0x3e, 0x51, 0x19, 0x02, 0x7a, 0xfe, 0x98, 0x80, + 0xd7, 0x0c, 0x27, 0xfe, 0x3e, 0x0a, 0x0a, 0x07, 0x70, 0xfe, 0x82, 0x12, 0x0a, 0x07, 0x1a, 0xfe, + 0x66, 0x13, 0x21, 0x61, 0x6a, 0xc8, 0xfe, 0x83, 0x80, 0xfe, 0xc8, 0x44, 0xfe, 0x2e, 0x13, 0xfe, + 0x04, 0x91, 0xfe, 0x86, 0x91, 0x64, 0x2f, 0xfe, 0x40, 0x59, 0xfe, 0xc1, 0x59, 0x75, 0xfe, 0xea, + 0x08, 0x04, 0x5d, 0x30, 0x5e, 0x0f, 0xae, 0x12, 0x8d, 0x9c, 0x5d, 0x9d, 0x5e, 0x01, 0xc1, 0xaf, + 0x64, 0x2f, 0x16, 0x61, 0xa1, 0x42, 0x69, 0x58, 0x65, 0x5f, 0x31, 0x60, 0xe8, 0xfe, 0xe5, 0x55, + 0xfe, 0x04, 0xfa, 0x42, 0xfe, 0x05, 0xfa, 0x58, 0x01, 0xfe, 0xde, 0x12, 0xfe, 0x36, 0x10, 0x2d, + 0x0f, 0xae, 0x0f, 0x8d, 0x65, 0x5f, 0x31, 0x60, 0xaa, 0x0a, 0x07, 0x1a, 0x18, 0xfe, 0xea, 0x08, + 0x65, 0x3a, 0x31, 0x3b, 0x0a, 0x07, 0xfe, 0xf7, 0x00, 0x38, 0x04, 0x5b, 0x30, 0x5c, 0xfe, 0x10, + 0x58, 0xfe, 0x91, 0x58, 0xfe, 0x14, 0x59, 0xfe, 0x95, 0x59, 0x02, 0x7a, 0x0a, 0x07, 0x1a, 0x18, + 0xfe, 0xea, 0x08, 0x0a, 0x07, 0xfe, 0xf7, 0x00, 0x38, 0xfe, 0x3a, 0x55, 0xfe, 0x19, 0x81, 0x77, + 0xfe, 0x10, 0x90, 0xfe, 0x92, 0x90, 0xfe, 0xd7, 0x10, 0x3f, 0x05, 0xc3, 0x18, 0xfe, 0xf6, 0x08, + 0x11, 0xc3, 0xfe, 0x98, 0x80, 0xd7, 0x0c, 0xfe, 0x14, 0x13, 0x04, 0x3a, 0x30, 0x3b, 0x75, 0xfe, + 0xf6, 0x08, 0xfe, 0x0c, 0x58, 0xfe, 0x8d, 0x58, 0x02, 0x7a, 0x2d, 0x4e, 0xfe, 0x19, 0x80, 0xfe, + 0xf1, 0x10, 0x0a, 0x07, 0x0c, 0xa5, 0xfe, 0x6c, 0x19, 0xfe, 0x19, 0x41, 0xfe, 0x8e, 0x10, 0xfe, + 0x6c, 0x19, 0x9c, 0x3a, 0xfe, 0xed, 0x19, 0x9d, 0x3b, 0xfe, 0x0c, 0x51, 0xfe, 0x8e, 0x51, 0xfe, + 0x6b, 0x18, 0x1b, 0xfe, 0x00, 0xff, 0x35, 0xfe, 0x74, 0x10, 0xc5, 0xfe, 0xd2, 0xf0, 0xfe, 0xd6, + 0x0a, 0xfe, 0x76, 0x18, 0x1b, 0x19, 0xce, 0x04, 0xdf, 0x1b, 0x06, 0x84, 0x13, 0xfe, 0x16, 0x00, + 0x02, 0x62, 0xfe, 0xd1, 0xf0, 0xfe, 0xe8, 0x0a, 0x17, 0x80, 0x01, 0x44, 0x13, 0xd6, 0xfe, 0x42, + 0x10, 0xfe, 0xce, 0xf0, 0xfe, 0xee, 0x0a, 0xfe, 0x3c, 0x10, 0xfe, 0xcd, 0xf0, 0xfe, 0xfa, 0x0a, + 0x13, 0xfe, 0x22, 0x00, 0x02, 0x62, 0xfe, 0xcb, 0xf0, 0xfe, 0x06, 0x0b, 0x13, 0xfe, 0x24, 0x00, + 0x02, 0x62, 0xfe, 0xd0, 0xf0, 0xfe, 0x10, 0x0b, 0x13, 0x88, 0xd8, 0xfe, 0xcf, 0xf0, 0xfe, 0x1a, + 0x0b, 0x13, 0x89, 0xd3, 0xfe, 0xcc, 0xf0, 0xfe, 0x2a, 0x0b, 0xfe, 0x84, 0x80, 0xd7, 0x1a, 0x4b, + 0x13, 0xfe, 0x12, 0x00, 0x2b, 0x08, 0x80, 0x2e, 0xfe, 0x30, 0x0b, 0xfe, 0x9e, 0xf0, 0xfe, 0x44, + 0x0b, 0x9b, 0x18, 0x32, 0x2b, 0x52, 0xfe, 0xed, 0x10, 0xa7, 0x28, 0xa9, 0x28, 0x2b, 0xf5, 0x2e, + 0xfe, 0x50, 0x0b, 0x22, 0x32, 0x81, 0xfe, 0x6c, 0x0b, 0x6b, 0x92, 0xa7, 0xfe, 0xec, 0x07, 0xa9, + 0xfe, 0xec, 0x07, 0x02, 0x28, 0x01, 0x4c, 0xfe, 0xdb, 0x10, 0x11, 0xfe, 0xe8, 0x00, 0xe3, 0xe4, + 0x8c, 0x82, 0x6e, 0xfe, 0x89, 0xf0, 0x28, 0x23, 0x24, 0xfe, 0xe9, 0x09, 0x01, 0x0b, 0x82, 0x6e, + 0x1f, 0x28, 0x23, 0x24, 0x91, 0x34, 0xfe, 0xa8, 0x0b, 0x22, 0x32, 0x02, 0xfe, 0x9c, 0x0b, 0x9b, + 0x40, 0x13, 0xfe, 0x42, 0x00, 0x02, 0x62, 0xa0, 0x06, 0xfe, 0x81, 0x49, 0x96, 0x0a, 0x07, 0x0c, + 0xfe, 0x5a, 0x13, 0x13, 0x00, 0x59, 0x0c, 0xfe, 0x6a, 0x12, 0x59, 0xfe, 0x28, 0x00, 0x27, 0xfe, + 0xee, 0x0c, 0x0e, 0x79, 0x01, 0x15, 0x05, 0x00, 0x84, 0x36, 0xfe, 0x28, 0x00, 0x02, 0xfe, 0xee, + 0x0c, 0x01, 0x97, 0x01, 0x9a, 0x0e, 0xc0, 0x01, 0xfe, 0x44, 0x0e, 0xb2, 0x08, 0x3d, 0x09, 0x99, + 0x01, 0x46, 0x11, 0x47, 0x08, 0x1c, 0x09, 0x43, 0x01, 0x7b, 0x02, 0x26, 0x13, 0xfe, 0x44, 0x00, + 0x59, 0x0c, 0xa5, 0x36, 0x0c, 0xfe, 0xc0, 0x10, 0x01, 0x96, 0x36, 0x0c, 0xfe, 0xb6, 0x10, 0x01, + 0x96, 0xfe, 0x19, 0x82, 0xfe, 0x34, 0x46, 0xfe, 0x0a, 0x13, 0x36, 0x0c, 0x13, 0xfe, 0x43, 0x00, + 0xfe, 0xa2, 0x10, 0x0a, 0x41, 0x0c, 0x38, 0x01, 0x97, 0x01, 0x9a, 0xb2, 0x08, 0x3d, 0x09, 0x99, + 0x01, 0x46, 0x11, 0x47, 0x08, 0x1c, 0x09, 0x43, 0x01, 0x7b, 0x51, 0x0c, 0xb2, 0x1d, 0xca, 0x02, + 0xfe, 0x48, 0x03, 0x0a, 0x07, 0x0c, 0xce, 0x36, 0x0c, 0x13, 0x00, 0xfe, 0x54, 0x10, 0x68, 0x07, + 0x1e, 0xfe, 0x50, 0x12, 0x0a, 0x07, 0x1e, 0xfe, 0x48, 0x13, 0xfe, 0x1c, 0x1c, 0xfe, 0x9d, 0xf0, + 0xfe, 0xac, 0x0c, 0xfe, 0x1c, 0x1c, 0xfe, 0x9d, 0xf0, 0xfe, 0xb2, 0x0c, 0x0a, 0x41, 0x1e, 0x38, + 0xfe, 0x95, 0x10, 0x13, 0xfe, 0x15, 0x00, 0xfe, 0x04, 0xe6, 0x0c, 0x77, 0xfe, 0x26, 0x10, 0x13, + 0xfe, 0x13, 0x00, 0xd3, 0x13, 0xfe, 0x47, 0x00, 0xa6, 0x13, 0xfe, 0x41, 0x00, 0x9e, 0x13, 0xfe, + 0x24, 0x00, 0x04, 0x78, 0x29, 0x27, 0xee, 0x77, 0xfe, 0x04, 0xe6, 0x1e, 0xfe, 0x9d, 0x41, 0xfe, + 0x1c, 0x42, 0xb2, 0x01, 0xea, 0x02, 0x26, 0xd5, 0x17, 0x0c, 0x4a, 0xf4, 0xdd, 0x17, 0xfe, 0x31, + 0x00, 0x4a, 0xbd, 0x01, 0xfe, 0x50, 0x0f, 0x02, 0xfe, 0xc2, 0x01, 0x1d, 0xfe, 0x06, 0xec, 0xf8, + 0x86, 0x36, 0x37, 0xbf, 0x35, 0x1d, 0xfe, 0x06, 0xea, 0xf8, 0xfe, 0x47, 0x4b, 0x95, 0xfe, 0x75, + 0x57, 0x04, 0x56, 0xfe, 0x98, 0x56, 0xfe, 0x28, 0x12, 0x0e, 0x79, 0xfe, 0xf4, 0x14, 0x4e, 0xe6, + 0x0e, 0xc0, 0xfe, 0xea, 0x14, 0xfe, 0x49, 0x54, 0x8f, 0xfe, 0x62, 0x0d, 0x0e, 0x1c, 0xfe, 0xde, + 0x14, 0xfe, 0x44, 0x48, 0x02, 0xfe, 0x48, 0x03, 0x0e, 0x56, 0xfe, 0xc8, 0x14, 0x86, 0x36, 0x37, + 0xbf, 0x35, 0x1d, 0xfe, 0xce, 0x47, 0xfe, 0xbd, 0x13, 0x02, 0x26, 0x21, 0x2a, 0x05, 0x10, 0xfe, + 0x78, 0x12, 0x2d, 0x16, 0x55, 0x16, 0xad, 0x21, 0x47, 0x4e, 0x4a, 0x47, 0x9b, 0xfe, 0x0c, 0x13, + 0xfe, 0xbc, 0xf0, 0xfe, 0xfe, 0x0d, 0x08, 0x06, 0x16, 0x55, 0x01, 0xfe, 0x06, 0x16, 0x04, 0xfe, + 0x38, 0x01, 0x30, 0xfe, 0x3a, 0x01, 0x75, 0xfe, 0x02, 0x0e, 0x04, 0xfe, 0x38, 0x01, 0x1b, 0xfe, + 0xf0, 0xff, 0x0f, 0xfe, 0x60, 0x01, 0x04, 0xfe, 0x3a, 0x01, 0x0f, 0xfe, 0x62, 0x01, 0x20, 0x06, + 0x16, 0x47, 0xfe, 0x04, 0xec, 0x2a, 0x08, 0x2a, 0x09, 0x3c, 0x1d, 0x01, 0x46, 0x7f, 0xfe, 0x05, + 0xf6, 0xfe, 0x34, 0x01, 0x01, 0xfe, 0x76, 0x16, 0x11, 0x47, 0xca, 0x08, 0x06, 0x03, 0x2d, 0x03, + 0x21, 0x55, 0xfe, 0xf7, 0x12, 0x21, 0xad, 0x6a, 0x16, 0xad, 0x05, 0x80, 0xfe, 0x93, 0x13, 0xfe, + 0x24, 0x1c, 0x17, 0x19, 0x4a, 0xf4, 0xdd, 0xfe, 0xd9, 0x10, 0x93, 0xfe, 0x03, 0xdc, 0xfe, 0x73, + 0x57, 0xfe, 0x80, 0x5d, 0x03, 0x93, 0xfe, 0x03, 0xdc, 0xfe, 0x5b, 0x57, 0xfe, 0x80, 0x5d, 0x03, + 0xfe, 0x03, 0x57, 0x93, 0x2d, 0xfe, 0x00, 0xcc, 0x03, 0xfe, 0x03, 0x57, 0x93, 0x7d, 0x03, 0x01, + 0xfe, 0xae, 0x16, 0x3f, 0x05, 0x47, 0xfe, 0x0a, 0x13, 0x08, 0x1c, 0x09, 0x43, 0xd3, 0x01, 0x97, + 0x01, 0x9a, 0x08, 0x3d, 0x09, 0x99, 0x01, 0x46, 0x11, 0xfe, 0xe9, 0x00, 0x0a, 0x07, 0x89, 0xfe, + 0x52, 0x13, 0x01, 0xfe, 0x38, 0x16, 0xfe, 0x1e, 0x1c, 0xfe, 0x14, 0x90, 0x0f, 0xfe, 0x64, 0x01, + 0xfe, 0x16, 0x90, 0x0f, 0xfe, 0x66, 0x01, 0x0a, 0x07, 0x45, 0xe5, 0xfe, 0x03, 0x80, 0x52, 0x3e, + 0x11, 0x76, 0x08, 0x2a, 0x09, 0x3c, 0x1d, 0x90, 0x01, 0x71, 0xfe, 0x62, 0x08, 0x6a, 0x3e, 0x11, + 0x76, 0x08, 0x2a, 0x09, 0x3c, 0x1d, 0x90, 0x01, 0x71, 0x64, 0x2f, 0x11, 0x76, 0x08, 0x2a, 0x09, + 0x3c, 0x1d, 0x90, 0x01, 0x7b, 0x03, 0xfe, 0x08, 0x1c, 0x04, 0xfe, 0xac, 0x00, 0xfe, 0x06, 0x58, + 0x04, 0xfe, 0xae, 0x00, 0xfe, 0x07, 0x58, 0x04, 0xfe, 0xb0, 0x00, 0xfe, 0x08, 0x58, 0x04, 0xfe, + 0xb2, 0x00, 0xfe, 0x09, 0x58, 0xfe, 0x0a, 0x1c, 0x20, 0x6c, 0x16, 0xf8, 0x2d, 0x0f, 0x53, 0x0f, + 0x4d, 0x20, 0x10, 0x16, 0x2a, 0x16, 0x3c, 0x57, 0xa0, 0xd6, 0x08, 0x2a, 0x09, 0x3c, 0x1d, 0x01, + 0x7b, 0x7f, 0x11, 0x76, 0xfe, 0x14, 0x56, 0xfe, 0xd6, 0xf0, 0xfe, 0x2a, 0x0f, 0xd5, 0x8c, 0xfe, + 0x14, 0x1c, 0xfe, 0x10, 0x1c, 0xfe, 0x18, 0x1c, 0x03, 0x1d, 0xfe, 0x0c, 0x14, 0x86, 0xfe, 0x07, + 0xe6, 0x37, 0xfe, 0xce, 0x47, 0xfe, 0xf5, 0x13, 0x03, 0x01, 0x96, 0x0e, 0x3d, 0x01, 0x15, 0x05, + 0x10, 0x2c, 0x0e, 0x1c, 0x01, 0x15, 0x05, 0x10, 0xda, 0xfe, 0x44, 0x58, 0x3e, 0xfe, 0x01, 0xec, + 0xbd, 0xfe, 0x9e, 0x40, 0xfe, 0x9d, 0xe7, 0x00, 0xfe, 0x9c, 0xe7, 0x1e, 0x9f, 0x2f, 0x01, 0xea, + 0xfe, 0xc9, 0x10, 0x03, 0x2b, 0x82, 0x6e, 0x23, 0x24, 0xb3, 0x05, 0x1e, 0xfe, 0x48, 0x12, 0x05, + 0x0c, 0xfe, 0x4c, 0x12, 0x05, 0x19, 0xfe, 0x30, 0x12, 0x05, 0xcc, 0x18, 0xfe, 0xf4, 0x10, 0x05, + 0xfe, 0x23, 0x00, 0x18, 0xfe, 0x00, 0x11, 0x05, 0x06, 0x18, 0xfe, 0x5e, 0x11, 0x05, 0x1a, 0xfe, + 0x12, 0x12, 0x05, 0x00, 0x18, 0x28, 0x17, 0xcc, 0x01, 0x44, 0xc6, 0x39, 0x01, 0x0b, 0x81, 0x4c, + 0x03, 0x39, 0x11, 0xfe, 0xcc, 0x00, 0x02, 0x26, 0x39, 0x3f, 0x05, 0xc3, 0xfe, 0xe3, 0x13, 0x65, + 0x3a, 0x31, 0x3b, 0x75, 0xfe, 0xb2, 0x10, 0x0a, 0x07, 0x70, 0xfe, 0x72, 0x12, 0xa1, 0x42, 0x69, + 0x58, 0xe8, 0xfe, 0xe5, 0x55, 0x8f, 0xfe, 0x7c, 0x10, 0x21, 0x61, 0xfe, 0x26, 0x13, 0x04, 0xae, + 0x30, 0x8d, 0x75, 0xfe, 0xd2, 0x0c, 0x0f, 0x5d, 0x12, 0x5e, 0x2d, 0x0f, 0xae, 0x0f, 0x8d, 0x01, + 0xc1, 0x20, 0x6c, 0x52, 0x16, 0x61, 0x01, 0xfe, 0xde, 0x12, 0xa1, 0x42, 0x69, 0x58, 0xfe, 0x04, + 0x55, 0xfe, 0xa5, 0x55, 0xfe, 0x04, 0xfa, 0x42, 0xfe, 0x05, 0xfa, 0x58, 0xfe, 0x91, 0x10, 0x04, + 0x5f, 0x30, 0x60, 0xfe, 0x40, 0x56, 0xfe, 0xe1, 0x56, 0x0f, 0x5f, 0x12, 0x60, 0xa8, 0xa1, 0x42, + 0x69, 0x58, 0xe8, 0xfe, 0xe5, 0x55, 0x04, 0x5b, 0x30, 0x5c, 0xfe, 0x00, 0x56, 0xfe, 0xa1, 0x56, + 0x0f, 0x5b, 0x12, 0x5c, 0x0a, 0x07, 0x70, 0xfe, 0x1e, 0x12, 0x21, 0x61, 0xfe, 0x1f, 0x40, 0x04, + 0x5d, 0x30, 0x5e, 0xfe, 0x2c, 0x50, 0xfe, 0xae, 0x50, 0x04, 0x5f, 0x30, 0x60, 0xfe, 0x34, 0x50, + 0xfe, 0xb6, 0x50, 0x04, 0x5b, 0x30, 0x5c, 0xfe, 0x08, 0x50, 0xfe, 0x8a, 0x50, 0x04, 0x3a, 0x30, + 0x3b, 0xfe, 0x28, 0x50, 0xfe, 0xaa, 0x50, 0x02, 0x98, 0x20, 0x06, 0x16, 0xf3, 0x02, 0x7c, 0x39, + 0x01, 0x0b, 0x1f, 0x4f, 0x23, 0x24, 0xb3, 0x05, 0x06, 0x27, 0x4f, 0x3f, 0x05, 0xc3, 0x27, 0x7c, + 0x01, 0xfa, 0x1b, 0x50, 0x18, 0x4f, 0x0a, 0x07, 0x0c, 0xdc, 0x65, 0x3a, 0x31, 0x3b, 0xfe, 0x0a, + 0x55, 0x35, 0xfe, 0x8b, 0x55, 0x9c, 0x3a, 0x9d, 0x3b, 0xfe, 0x0c, 0x51, 0xfe, 0x8e, 0x51, 0x02, + 0x7c, 0xfe, 0x19, 0x81, 0xfe, 0x0a, 0x45, 0xfe, 0x19, 0x41, 0x02, 0x7c, 0x39, 0x01, 0x0b, 0x1f, + 0xfe, 0xf6, 0x0f, 0x23, 0x24, 0xfe, 0xe9, 0x09, 0x59, 0x19, 0xfe, 0x94, 0x12, 0x59, 0x0c, 0x4b, + 0x02, 0x4f, 0x2e, 0xfe, 0x7e, 0x11, 0x22, 0x32, 0x1f, 0xfe, 0xf6, 0x0f, 0x23, 0x24, 0x91, 0x05, + 0x19, 0x27, 0x4f, 0x01, 0x0b, 0x1f, 0xfe, 0xf6, 0x0f, 0x23, 0x24, 0xfe, 0xe8, 0x09, 0x57, 0x04, + 0xfe, 0x9c, 0x00, 0x29, 0x35, 0xfe, 0xbb, 0x45, 0x59, 0x00, 0x40, 0x36, 0x06, 0xa0, 0x50, 0xfe, + 0xc0, 0x14, 0xfe, 0xf8, 0x14, 0xac, 0x3f, 0x05, 0xc2, 0xfe, 0x16, 0x13, 0x04, 0xf6, 0x29, 0xce, + 0x04, 0x4d, 0x29, 0x35, 0x5a, 0x02, 0x7c, 0xfe, 0xc0, 0x5d, 0xfe, 0xe4, 0x14, 0xfe, 0x03, 0x17, + 0x04, 0x53, 0xbc, 0x0f, 0x53, 0x5a, 0x39, 0x01, 0x0b, 0x25, 0x98, 0x01, 0xfe, 0x26, 0x14, 0x02, + 0x98, 0x2e, 0x40, 0x22, 0x32, 0x1f, 0x4f, 0x23, 0x24, 0x91, 0x05, 0x06, 0x27, 0x4f, 0xfe, 0xf6, + 0x14, 0xfe, 0x42, 0x58, 0xfe, 0x70, 0x14, 0xfe, 0x92, 0x14, 0xac, 0xfe, 0x4a, 0xf4, 0x0c, 0x18, + 0x4f, 0xfe, 0x4a, 0xf4, 0x06, 0xd1, 0x3f, 0x05, 0xc2, 0xc9, 0x02, 0x7c, 0x04, 0x4d, 0xbc, 0x0f, + 0x4d, 0x5a, 0x39, 0x01, 0x0b, 0x25, 0x98, 0x01, 0xfe, 0x54, 0x14, 0x02, 0x98, 0x25, 0xfe, 0x70, + 0x12, 0x73, 0xf1, 0x73, 0x03, 0x34, 0xfe, 0x6c, 0x12, 0x6b, 0xfe, 0x6c, 0x12, 0x5a, 0x39, 0x01, + 0x0b, 0xfe, 0xe3, 0x10, 0x08, 0x63, 0xff, 0x02, 0x00, 0x57, 0x66, 0x7e, 0x1b, 0xfe, 0xff, 0x7f, + 0xfe, 0x30, 0x56, 0xfe, 0x00, 0x5c, 0x03, 0x08, 0x63, 0xff, 0x02, 0x00, 0x57, 0x66, 0x7e, 0x1b, + 0x50, 0xfe, 0x30, 0x56, 0xfe, 0x00, 0x5c, 0x03, 0x08, 0x63, 0xff, 0x02, 0x00, 0x57, 0x66, 0x7e, + 0x03, 0x08, 0x63, 0xff, 0x02, 0x00, 0x57, 0x66, 0x7e, 0xfe, 0x0b, 0x58, 0x03, 0x0e, 0x53, 0x01, + 0x8b, 0x0e, 0x4d, 0x01, 0x8b, 0x03, 0xc8, 0x1b, 0x10, 0xff, 0x03, 0x00, 0x54, 0xfe, 0x00, 0xf4, + 0x1a, 0x66, 0xfe, 0x00, 0x7d, 0xfe, 0x01, 0x7d, 0xfe, 0x02, 0x7d, 0xfe, 0x03, 0x7c, 0x64, 0x2f, + 0x0f, 0x5b, 0x12, 0x5c, 0x9c, 0x5f, 0x9d, 0x60, 0x03, 0xfe, 0x62, 0x18, 0xfe, 0x82, 0x5a, 0xfe, + 0xe1, 0x1a, 0xb6, 0xfe, 0x02, 0x58, 0x03, 0x01, 0xfe, 0x9e, 0x18, 0xfe, 0x42, 0x48, 0x77, 0x57, + 0x95, 0x01, 0x0b, 0x1f, 0xfe, 0x1e, 0x14, 0x23, 0x24, 0xfe, 0xe9, 0x09, 0xfe, 0xc1, 0x59, 0x01, + 0x0b, 0x1f, 0xfe, 0x1e, 0x14, 0x23, 0x24, 0xfe, 0xe8, 0x0a, 0x04, 0xf6, 0x29, 0xfe, 0xc4, 0x12, + 0x2d, 0xb1, 0x1e, 0xdc, 0x59, 0xcd, 0x74, 0xfe, 0x6c, 0x13, 0x4b, 0x08, 0x06, 0x09, 0xcd, 0xa0, + 0xfe, 0x00, 0x10, 0xfe, 0x78, 0x10, 0xff, 0x02, 0x83, 0x55, 0xa6, 0xff, 0x02, 0x83, 0x55, 0xb1, + 0x19, 0xfe, 0x12, 0x13, 0x72, 0xfe, 0x30, 0x00, 0x8f, 0xfe, 0xc6, 0x13, 0x09, 0x85, 0x08, 0x06, + 0xfe, 0x56, 0x10, 0xb1, 0x0c, 0xfe, 0x16, 0x13, 0x72, 0xfe, 0x64, 0x00, 0x8f, 0xfe, 0xc6, 0x13, + 0x0e, 0xfe, 0x64, 0x00, 0x09, 0x88, 0x08, 0x06, 0xfe, 0x28, 0x10, 0xb1, 0x06, 0xfe, 0x60, 0x13, + 0x72, 0xfe, 0xc8, 0x00, 0x8f, 0xfe, 0xc6, 0x13, 0x0e, 0xfe, 0xc8, 0x00, 0x09, 0x55, 0x08, 0x06, + 0xa8, 0x72, 0xfe, 0x90, 0x01, 0xed, 0xfe, 0xd2, 0x13, 0x95, 0xaa, 0xfe, 0x43, 0xf4, 0xad, 0xfe, + 0x56, 0xf0, 0xfe, 0xe4, 0x13, 0xfe, 0x04, 0xf4, 0x63, 0xfe, 0x43, 0xf4, 0x88, 0xfe, 0xf3, 0x10, + 0xb0, 0x01, 0xfe, 0xae, 0x12, 0x1b, 0x50, 0xd4, 0xfe, 0x00, 0x17, 0xfe, 0x4d, 0xe4, 0x6c, 0xed, + 0xfe, 0x18, 0x14, 0xa3, 0x6c, 0xfe, 0x14, 0x10, 0xfe, 0x00, 0x17, 0xfe, 0x4d, 0xe4, 0x1a, 0xed, + 0xfe, 0x18, 0x14, 0xa3, 0x1a, 0x9e, 0x57, 0x95, 0x08, 0x06, 0xfe, 0xb4, 0x56, 0xfe, 0xc3, 0x58, + 0x03, 0x57, 0x08, 0x0c, 0x03, 0x14, 0x06, 0x01, 0x0b, 0x25, 0xec, 0x14, 0x0c, 0x01, 0x0b, 0x25, + 0xec, 0x14, 0x19, 0x01, 0x0b, 0x25, 0xec, 0x73, 0xfe, 0x89, 0x49, 0x01, 0x0b, 0x03, 0x14, 0x06, + 0x01, 0x0b, 0x25, 0xb7, 0x14, 0x19, 0x01, 0x0b, 0x25, 0xb7, 0x14, 0x06, 0x01, 0x0b, 0x25, 0xb7, + 0xfe, 0x89, 0x49, 0x01, 0x0b, 0x25, 0xb7, 0x73, 0xfe, 0x89, 0x4a, 0x01, 0x0b, 0x03, 0x57, 0x03, + 0x21, 0xe0, 0x05, 0x06, 0xfe, 0x44, 0x13, 0xaf, 0x16, 0xe0, 0xfe, 0x49, 0xf4, 0x00, 0x4b, 0x73, + 0xc6, 0x5a, 0xfe, 0x01, 0xec, 0xfe, 0x27, 0x01, 0xf1, 0x01, 0x0b, 0x3f, 0x05, 0xfe, 0xe3, 0x00, + 0xfe, 0x20, 0x13, 0x1f, 0xfe, 0xd6, 0x14, 0x2d, 0x16, 0xf3, 0x01, 0x4c, 0x21, 0xf3, 0x05, 0x06, + 0x40, 0x0a, 0x41, 0x06, 0x38, 0x03, 0x0f, 0x54, 0x12, 0x8a, 0xfe, 0x43, 0x58, 0x01, 0x15, 0x05, + 0x10, 0xfe, 0x1e, 0x12, 0x48, 0xe7, 0x8e, 0x01, 0x2c, 0xfe, 0x90, 0x4d, 0xde, 0x10, 0xfe, 0xc5, + 0x59, 0x01, 0x2c, 0xfe, 0x8d, 0x56, 0xb6, 0x48, 0x03, 0x48, 0x31, 0x8a, 0x01, 0x15, 0x48, 0x8e, + 0x01, 0x2c, 0xe2, 0x10, 0xde, 0x10, 0x31, 0x54, 0x72, 0x1c, 0x84, 0x0e, 0x56, 0x01, 0xab, 0x03, + 0x0f, 0x54, 0x12, 0x8a, 0xfe, 0xc3, 0x58, 0x01, 0x15, 0x05, 0x10, 0xfe, 0x1a, 0x12, 0x48, 0xe7, + 0x8e, 0x01, 0x2c, 0xe2, 0x10, 0xfe, 0x80, 0x4d, 0xfe, 0xc5, 0x59, 0x01, 0x2c, 0x48, 0x03, 0x48, + 0x31, 0x54, 0x01, 0x15, 0x48, 0x8e, 0x01, 0x2c, 0xe2, 0x10, 0xde, 0x10, 0x31, 0x54, 0x72, 0x1c, + 0x84, 0x0e, 0x56, 0x01, 0xab, 0x03, 0x0f, 0x54, 0x12, 0x8a, 0xfe, 0x43, 0x58, 0x01, 0x15, 0xfe, + 0x42, 0x48, 0x8e, 0x01, 0x2c, 0xfe, 0xc0, 0x5a, 0xb0, 0xfe, 0x00, 0xcd, 0xfe, 0x01, 0xcc, 0xfe, + 0x4a, 0x46, 0xdc, 0x93, 0x7d, 0x05, 0x10, 0xfe, 0x2e, 0x13, 0x69, 0x54, 0xfe, 0x4d, 0xf4, 0x1c, + 0xfe, 0x1c, 0x13, 0x0e, 0x56, 0x01, 0x8b, 0xaa, 0xfe, 0x40, 0x4c, 0xfe, 0xc5, 0x58, 0x01, 0x2c, + 0xfe, 0x00, 0x07, 0x7d, 0x05, 0x10, 0x84, 0x69, 0x8a, 0xfe, 0x05, 0x57, 0xfe, 0x08, 0x10, 0xfe, + 0x45, 0x58, 0x01, 0x2c, 0xfe, 0x8d, 0x56, 0xb6, 0xfe, 0x80, 0x4c, 0xfe, 0x05, 0x17, 0x03, 0x09, + 0x10, 0x6f, 0x67, 0xfe, 0x60, 0x01, 0xfe, 0x18, 0xdf, 0xfe, 0x19, 0xde, 0xfe, 0x24, 0x1c, 0xdb, + 0x37, 0x94, 0xfe, 0x1a, 0x16, 0x01, 0xfe, 0x28, 0x17, 0xfe, 0x0c, 0x13, 0x87, 0x37, 0x67, 0xfe, + 0x2c, 0x01, 0xfe, 0x2f, 0x19, 0x03, 0xba, 0x27, 0xfe, 0x0a, 0x16, 0xfe, 0xe2, 0x10, 0x09, 0x10, + 0x6f, 0x04, 0xfe, 0x64, 0x01, 0xfe, 0x00, 0xf4, 0x1a, 0xfe, 0x18, 0x58, 0x04, 0xfe, 0x66, 0x01, + 0xfe, 0x19, 0x58, 0x87, 0x1a, 0xfe, 0x3c, 0x90, 0xfe, 0x30, 0xf4, 0x06, 0xfe, 0x3c, 0x50, 0x67, + 0xfe, 0x38, 0x00, 0xfe, 0x0f, 0x79, 0xfe, 0x1c, 0xf7, 0x1a, 0x94, 0xfe, 0x64, 0x16, 0xfe, 0xbe, + 0x14, 0x35, 0x03, 0xba, 0x27, 0xfe, 0x3c, 0x16, 0xfe, 0xa4, 0x10, 0x09, 0x10, 0x6f, 0xb6, 0xfe, + 0x18, 0xdf, 0xfe, 0x19, 0xdf, 0xdb, 0x42, 0x94, 0xfe, 0x86, 0x16, 0xfe, 0x9c, 0x14, 0xfe, 0x18, + 0x13, 0x87, 0x42, 0x67, 0x1e, 0xfe, 0xaf, 0x19, 0xfe, 0x98, 0xe7, 0x00, 0xa2, 0x07, 0xfe, 0x7f, + 0x00, 0xfe, 0x05, 0x40, 0x03, 0xba, 0x27, 0xfe, 0x7a, 0x16, 0xfe, 0x6c, 0x10, 0x09, 0x10, 0x6f, + 0xfe, 0x30, 0xbc, 0xfe, 0xb2, 0xbc, 0x87, 0xd9, 0x67, 0x1e, 0xfe, 0x0f, 0x79, 0xfe, 0x1c, 0xf7, + 0xd9, 0x94, 0xfe, 0xc6, 0x16, 0xfe, 0x5c, 0x14, 0x35, 0x03, 0xba, 0x27, 0xfe, 0xb2, 0x16, 0xfe, + 0x42, 0x10, 0xfe, 0x02, 0xf6, 0x10, 0x6f, 0xfe, 0x18, 0xfe, 0x5d, 0xfe, 0x19, 0xfe, 0x5e, 0xc8, + 0xdb, 0x45, 0x94, 0xfe, 0xec, 0x16, 0xfe, 0x36, 0x14, 0xfe, 0x1c, 0x13, 0x87, 0x45, 0x4e, 0xfe, + 0x83, 0x58, 0xfe, 0xaf, 0x19, 0xfe, 0x80, 0xe7, 0x10, 0xfe, 0x81, 0xe7, 0x10, 0x11, 0xfe, 0xdd, + 0x00, 0x64, 0x2f, 0x03, 0x64, 0x2f, 0xfe, 0x12, 0x45, 0x27, 0xfe, 0xdc, 0x16, 0x17, 0x06, 0x4a, + 0xf4, 0xdd, 0x02, 0x26, 0xfe, 0x39, 0xf0, 0xfe, 0x30, 0x17, 0x2d, 0x03, 0xfe, 0x7e, 0x18, 0x1b, + 0x19, 0x83, 0x08, 0x0d, 0x03, 0x6f, 0x04, 0xdf, 0x1b, 0x06, 0xfe, 0xef, 0x12, 0xfe, 0xe1, 0x10, + 0x1d, 0x0e, 0x1c, 0x01, 0x15, 0x05, 0x10, 0x40, 0x3e, 0xfe, 0x78, 0x14, 0xfe, 0x34, 0x12, 0x50, + 0x86, 0x36, 0x37, 0xbf, 0xfe, 0xe9, 0x13, 0x1d, 0x0e, 0x3d, 0x01, 0x15, 0x05, 0x10, 0x40, 0x3e, + 0xfe, 0x56, 0x14, 0xe1, 0x50, 0x86, 0x36, 0x37, 0xbf, 0xfe, 0xe9, 0x13, 0x09, 0x0c, 0x03, 0xfe, + 0x9c, 0xe7, 0x0c, 0x13, 0xfe, 0x15, 0x00, 0x90, 0x9f, 0x2f, 0x01, 0xea, 0x09, 0x06, 0x03, 0x0a, + 0x41, 0x37, 0x38, 0x08, 0x3d, 0x09, 0x99, 0x01, 0x46, 0x11, 0x47, 0x08, 0x1c, 0x09, 0x43, 0x01, + 0x7b, 0x09, 0x06, 0x03, 0xfe, 0x38, 0x90, 0xfe, 0xba, 0x90, 0x65, 0xf7, 0x31, 0x76, 0xfe, 0x48, + 0x55, 0x35, 0xfe, 0xc9, 0x55, 0x03, 0x21, 0xbe, 0x52, 0x16, 0xbe, 0x03, 0x0e, 0xc0, 0x01, 0x15, + 0xe6, 0x0e, 0x79, 0x01, 0x15, 0xfe, 0x49, 0x44, 0x27, 0xfe, 0x26, 0x18, 0x0e, 0x1c, 0x01, 0x15, + 0x05, 0x10, 0x40, 0x0e, 0x56, 0x01, 0xab, 0x0e, 0x79, 0x01, 0x15, 0x52, 0x7d, 0x03, 0xfe, 0x40, + 0x5e, 0xfe, 0xe2, 0x08, 0xfe, 0xc0, 0x4c, 0x21, 0x3c, 0x05, 0x10, 0xfe, 0x52, 0x12, 0x3e, 0x05, + 0x00, 0xfe, 0x18, 0x12, 0xfe, 0xe1, 0x18, 0xfe, 0x19, 0xf4, 0xfe, 0x7f, 0x00, 0xd4, 0xfe, 0xe2, + 0x08, 0x52, 0x3e, 0x3f, 0x05, 0x76, 0xa5, 0xfe, 0x82, 0x48, 0xfe, 0x01, 0x80, 0xfe, 0xd7, 0x10, + 0xfe, 0xc4, 0x48, 0x08, 0x2a, 0x09, 0x3c, 0xfe, 0x40, 0x5f, 0x1d, 0x01, 0x46, 0x11, 0xfe, 0xdd, + 0x00, 0xfe, 0x14, 0x46, 0x08, 0x2a, 0x09, 0x3c, 0x01, 0x46, 0x11, 0xfe, 0xdd, 0x00, 0xfe, 0x40, + 0x4a, 0x6a, 0xfe, 0x06, 0x17, 0xfe, 0x01, 0x07, 0xfe, 0x82, 0x48, 0xfe, 0x04, 0x17, 0x03, 0xeb, + 0x19, 0x74, 0xfe, 0xae, 0x18, 0x04, 0xfe, 0x90, 0x00, 0xfe, 0x3a, 0x45, 0xfe, 0x2c, 0x10, 0xeb, + 0xcc, 0x74, 0xfe, 0xc0, 0x18, 0x04, 0xfe, 0x92, 0x00, 0xc7, 0x1e, 0xd8, 0xeb, 0xfe, 0x0b, 0x00, + 0x74, 0xfe, 0xd2, 0x18, 0x04, 0xfe, 0x94, 0x00, 0xc7, 0x1a, 0xfe, 0x08, 0x10, 0x04, 0xfe, 0x96, + 0x00, 0xc7, 0x85, 0xfe, 0x4e, 0x45, 0xd1, 0xfe, 0x0a, 0x45, 0xff, 0x04, 0x68, 0x54, 0xfe, 0xf1, + 0x10, 0x1b, 0x6c, 0x03, 0x05, 0x80, 0xfe, 0x5a, 0xf0, 0xfe, 0xfe, 0x18, 0x20, 0xfe, 0x09, 0x00, + 0xfe, 0x34, 0x10, 0x05, 0x1e, 0xfe, 0x5a, 0xf0, 0xfe, 0x0c, 0x19, 0x20, 0xcd, 0xfe, 0x26, 0x10, + 0x05, 0x19, 0x83, 0x20, 0x85, 0xd8, 0x05, 0x0c, 0x83, 0x20, 0x88, 0xfe, 0x0e, 0x10, 0x05, 0x06, + 0x83, 0x20, 0x55, 0xc6, 0xaf, 0x03, 0x17, 0xfe, 0x09, 0x00, 0x01, 0x44, 0x2e, 0xfe, 0x3c, 0x19, + 0x04, 0x6e, 0xb0, 0x03, 0x22, 0xfe, 0x54, 0x19, 0xfe, 0x14, 0xf0, 0x0b, 0x2e, 0xfe, 0x50, 0x19, + 0x03, 0xff, 0x15, 0x00, 0x00, +}; STATIC unsigned short _adv_asc3550_size = - sizeof(_adv_asc3550_buf); /* 0x13AA */ -STATIC unsigned long _adv_asc3550_chksum = - 0x04F4788EUL; /* Expanded checksum. */ + sizeof(_adv_asc3550_buf); /* 0x13E5 */ +STATIC ADV_DCNT _adv_asc3550_chksum = + 0x04FFFF0E; /* Expanded checksum. */ STATIC unsigned char _adv_asc38C0800_buf[] = { - 0x00, 0x00, 0x00, 0xf2, 0x00, 0xf0, 0x00, 0x16, 0x00, 0xfc, 0x48, 0xe4, 0x01, 0x00, 0x00, 0xf6, - 0x18, 0xe4, 0x01, 0xf6, 0x18, 0x80, 0x02, 0x00, 0x02, 0x1a, 0xff, 0xff, 0x00, 0xfa, 0x03, 0xf6, - 0xff, 0x00, 0x82, 0xe7, 0x01, 0xfa, 0x9e, 0xe7, 0x09, 0xe7, 0xe6, 0x0e, 0x00, 0xea, 0x01, 0xe6, - 0x03, 0x00, 0x1e, 0xf0, 0x55, 0xf0, 0x18, 0xf4, 0x3e, 0x57, 0x04, 0x00, 0x3e, 0x01, 0x85, 0xf0, - 0x00, 0xe6, 0x03, 0xfc, 0x08, 0x00, 0x32, 0xf0, 0x38, 0x54, 0x84, 0x0d, 0x86, 0xf0, 0xd4, 0x01, - 0xd5, 0xf0, 0xee, 0x19, 0x00, 0xec, 0x01, 0xfc, 0x98, 0x57, 0xbc, 0x00, 0x10, 0x13, 0xb1, 0xf0, - 0x02, 0x13, 0x3c, 0x00, 0x7e, 0x0d, 0xb4, 0x00, 0x00, 0x57, 0x01, 0xf0, 0x02, 0xfc, 0x03, 0xe6, - 0x0c, 0x1c, 0x10, 0x00, 0x18, 0x40, 0x3e, 0x1c, 0xbd, 0x00, 0xe0, 0x00, 0x02, 0x80, 0x3e, 0x00, - 0x46, 0x16, 0x4a, 0x10, 0x6c, 0x01, 0x6e, 0x01, 0x74, 0x01, 0x76, 0x01, 0xb9, 0x54, 0xba, 0x13, - 0xbb, 0x00, 0x00, 0x4e, 0x01, 0x01, 0x01, 0xea, 0x02, 0x48, 0x02, 0xfa, 0x08, 0x12, 0x30, 0xe4, - 0x3c, 0x56, 0x4e, 0x01, 0x5d, 0xf0, 0x7a, 0x01, 0x88, 0x0d, 0x8e, 0x10, 0xb6, 0x00, 0xc4, 0x08, - 0x00, 0x80, 0x04, 0x12, 0x05, 0xfc, 0x24, 0x01, 0x28, 0x01, 0x32, 0x00, 0x3c, 0x01, 0x40, 0x00, - 0x4b, 0xe4, 0x4b, 0xf4, 0x4c, 0x1c, 0x68, 0x01, 0x6a, 0x01, 0x70, 0x01, 0x72, 0x01, 0x78, 0x01, - 0x7c, 0x01, 0xbb, 0x55, 0x00, 0x01, 0x02, 0xee, 0x03, 0x58, 0x03, 0xf7, 0x03, 0xfa, 0x04, 0x80, - 0x08, 0x44, 0x09, 0xf0, 0x10, 0x44, 0x1b, 0x80, 0x20, 0x01, 0x38, 0x1c, 0x4e, 0x1c, 0x5b, 0xf0, - 0x80, 0x00, 0x8a, 0x15, 0x98, 0x10, 0xaa, 0x00, 0xbd, 0x56, 0xbe, 0x00, 0xc0, 0x00, 0x00, 0x4c, - 0x00, 0xdc, 0x02, 0x4a, 0x04, 0xfc, 0x05, 0xf0, 0x05, 0xf8, 0x06, 0x13, 0x06, 0xf7, 0x08, 0x13, - 0x0c, 0x00, 0x0e, 0x47, 0x0e, 0xf7, 0x0f, 0x00, 0x1c, 0x0c, 0x20, 0x00, 0x2a, 0x01, 0x32, 0x1c, - 0x36, 0x00, 0x42, 0x54, 0x44, 0x0b, 0x44, 0x55, 0x45, 0x5a, 0x59, 0xf0, 0x5c, 0xf0, 0x62, 0x0a, - 0x69, 0x08, 0x78, 0x13, 0x83, 0x59, 0x8e, 0x18, 0x9a, 0x10, 0x9a, 0x18, 0xb8, 0xf0, 0xd6, 0x0e, - 0xea, 0x15, 0xf0, 0x00, 0x04, 0x10, 0x04, 0xea, 0x04, 0xf6, 0x05, 0x00, 0x06, 0x00, 0x06, 0x12, - 0x0a, 0x10, 0x0a, 0x12, 0x0b, 0xf0, 0x0c, 0x10, 0x0c, 0xf0, 0x12, 0x10, 0x19, 0x00, 0x19, 0xe4, - 0x2a, 0x12, 0x30, 0x1c, 0x33, 0x00, 0x34, 0x00, 0x36, 0x15, 0x38, 0x44, 0x3a, 0x15, 0x40, 0x5c, - 0x4a, 0xe4, 0x62, 0x1a, 0x68, 0x08, 0x68, 0x54, 0x7a, 0x17, 0x83, 0x55, 0x83, 0x5a, 0x91, 0x44, - 0xa2, 0x10, 0xa4, 0x00, 0xb0, 0x57, 0xb5, 0x00, 0xba, 0x00, 0xcc, 0x0e, 0xce, 0x45, 0xd0, 0x00, - 0xe1, 0x00, 0xe5, 0x55, 0xe7, 0x00, 0x00, 0x54, 0x01, 0x48, 0x01, 0x58, 0x02, 0x10, 0x02, 0xe6, - 0x03, 0xa1, 0x04, 0x13, 0x05, 0xe6, 0x06, 0x83, 0x06, 0xf0, 0x07, 0x00, 0x0a, 0x00, 0x0a, 0xf0, - 0x0c, 0x12, 0x0c, 0x13, 0x0e, 0x13, 0x10, 0x04, 0x10, 0x10, 0x12, 0x1c, 0x19, 0x81, 0x1a, 0x10, - 0x1c, 0x00, 0x1c, 0x12, 0x1c, 0x13, 0x1d, 0xf7, 0x1e, 0x13, 0x20, 0x1c, 0x20, 0xe7, 0x22, 0x01, - 0x26, 0x01, 0x30, 0xe7, 0x38, 0x12, 0x3a, 0x55, 0x3f, 0x00, 0x41, 0x58, 0x43, 0x48, 0x46, 0x1c, - 0x4e, 0xe4, 0x5a, 0x13, 0x68, 0x13, 0x72, 0x14, 0x76, 0x02, 0x77, 0x57, 0x78, 0x03, 0x89, 0x48, - 0x8a, 0x13, 0x98, 0x80, 0x99, 0x00, 0x9b, 0x00, 0x9c, 0x32, 0xfe, 0x9c, 0xf0, 0x27, 0x02, 0xfe, - 0xa6, 0x0d, 0xff, 0x10, 0x00, 0x00, 0xfe, 0xc6, 0x01, 0xfe, 0x18, 0x1a, 0x00, 0xfe, 0xc4, 0x01, - 0xfe, 0x84, 0x01, 0xff, 0x03, 0x00, 0x00, 0x30, 0xfe, 0x01, 0x05, 0xff, 0x40, 0x00, 0x00, 0x0d, - 0xff, 0x09, 0x00, 0x00, 0xff, 0x08, 0x01, 0x01, 0xff, 0x10, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, - 0xff, 0x10, 0xff, 0xff, 0xff, 0x11, 0x00, 0x00, 0xfe, 0x78, 0x56, 0xfe, 0x34, 0x12, 0xff, 0x21, - 0x00, 0x00, 0xfe, 0x04, 0xf7, 0xfe, 0xc4, 0x01, 0x38, 0x86, 0x0b, 0x01, 0xfe, 0x96, 0x0f, 0xfe, - 0x04, 0xf7, 0xfe, 0xc4, 0x01, 0x86, 0x0b, 0x1c, 0x38, 0xfe, 0x3d, 0xf0, 0xfe, 0xfc, 0x01, 0xfe, - 0x20, 0xf0, 0xdb, 0x04, 0x5e, 0x59, 0x02, 0xfe, 0xc2, 0x0d, 0x01, 0xfe, 0x22, 0x0e, 0xfe, 0xe9, - 0x12, 0x02, 0xfe, 0x08, 0x03, 0xfe, 0x28, 0x1c, 0x04, 0xfe, 0xa6, 0x00, 0xfe, 0xdd, 0x12, 0x46, - 0x12, 0xfe, 0xa6, 0x00, 0xcd, 0xfe, 0x48, 0xf0, 0xfe, 0x80, 0x02, 0xfe, 0x49, 0xf0, 0xfe, 0x9a, - 0x02, 0xfe, 0x4a, 0xf0, 0xfe, 0xb8, 0x02, 0xfe, 0x46, 0xf0, 0xfe, 0x4a, 0x02, 0xfe, 0x47, 0xf0, - 0xfe, 0x50, 0x02, 0xfe, 0x43, 0xf0, 0xfe, 0x3e, 0x02, 0xfe, 0x44, 0xf0, 0xfe, 0x42, 0x02, 0xfe, - 0x45, 0xf0, 0xfe, 0x46, 0x02, 0x09, 0x0b, 0xa2, 0x09, 0x06, 0x12, 0xc1, 0x02, 0x27, 0xfe, 0x00, - 0x1c, 0xfe, 0xf1, 0x10, 0xfe, 0x02, 0x1c, 0xfe, 0xed, 0x10, 0xfe, 0x1e, 0x1c, 0xfe, 0xe9, 0x10, - 0x01, 0xfe, 0xee, 0x17, 0xfe, 0xe7, 0x10, 0xfe, 0x06, 0xfc, 0xfe, 0xa8, 0x00, 0x0f, 0x7d, 0x01, - 0xc5, 0x02, 0x27, 0x17, 0x5d, 0x4b, 0xc3, 0x01, 0xab, 0x0f, 0x7d, 0x01, 0x9f, 0xfe, 0xbd, 0x10, - 0x0f, 0x7d, 0x01, 0x9f, 0xfe, 0xad, 0x10, 0xfe, 0x16, 0x1c, 0xfe, 0x58, 0x1c, 0x09, 0x06, 0x12, - 0xc1, 0x38, 0x19, 0x27, 0xfe, 0x3d, 0xf0, 0xfe, 0xfc, 0x01, 0x28, 0xfe, 0x8e, 0x02, 0xfe, 0x5a, - 0x1c, 0xdd, 0xfe, 0x14, 0x1c, 0x17, 0xfe, 0x30, 0x00, 0x4b, 0xc3, 0x01, 0xfe, 0xfc, 0x0f, 0x09, - 0x06, 0x12, 0xc1, 0x02, 0xfe, 0xc6, 0x01, 0x2a, 0x2d, 0x05, 0x10, 0x30, 0xfe, 0x69, 0x10, 0x09, - 0x06, 0x12, 0xc1, 0xfe, 0x04, 0xec, 0x2d, 0x07, 0x2d, 0x09, 0x3c, 0x1c, 0x01, 0x40, 0x81, 0xfe, - 0x05, 0xf6, 0xfe, 0xa8, 0x00, 0x01, 0xfe, 0x20, 0x17, 0x0a, 0x4f, 0x8d, 0x3a, 0x11, 0x48, 0x1c, - 0xd3, 0x07, 0x1e, 0x09, 0x51, 0x01, 0xa0, 0x02, 0x27, 0x0f, 0x3d, 0x01, 0x15, 0x05, 0x10, 0xda, - 0x07, 0x1e, 0x09, 0x51, 0x01, 0x79, 0xfe, 0x28, 0x10, 0x0f, 0xc7, 0x01, 0x15, 0xed, 0x0f, 0x7e, - 0x01, 0x15, 0xfe, 0x49, 0x54, 0x77, 0xfe, 0x16, 0x03, 0x07, 0x1e, 0x09, 0x51, 0x01, 0xa0, 0x02, - 0x27, 0x38, 0x81, 0xfe, 0x02, 0xe8, 0x33, 0xfe, 0xbf, 0x57, 0xfe, 0x9e, 0x43, 0xf5, 0xfe, 0x07, - 0x4b, 0xfe, 0x20, 0xf0, 0xdb, 0xfe, 0x40, 0x1c, 0x19, 0xf6, 0xfe, 0x26, 0xf0, 0xfe, 0x74, 0x03, - 0xfe, 0xa0, 0xf0, 0xfe, 0x62, 0x03, 0xfe, 0x11, 0xf0, 0xdb, 0xfe, 0x0e, 0x10, 0xfe, 0x9f, 0xf0, - 0xfe, 0x82, 0x03, 0xef, 0x13, 0xfe, 0x11, 0x00, 0x02, 0x54, 0x38, 0xfe, 0x48, 0x1c, 0xef, 0x19, - 0xf6, 0x35, 0xf6, 0xfe, 0x82, 0xf0, 0xfe, 0x88, 0x03, 0x24, 0x2a, 0xc4, 0x70, 0x16, 0xc4, 0x0f, - 0x7e, 0x01, 0x15, 0x70, 0x7f, 0x07, 0x1e, 0x09, 0x51, 0x01, 0x40, 0x11, 0x3d, 0x07, 0x3d, 0x09, - 0xa1, 0x01, 0xa0, 0xfc, 0x11, 0xfe, 0xe4, 0x00, 0x2f, 0xfe, 0xce, 0x03, 0x19, 0x32, 0x1f, 0xfe, - 0xde, 0x03, 0x01, 0x41, 0xd4, 0xfe, 0xee, 0x03, 0x71, 0x8c, 0xd7, 0xfe, 0xae, 0x06, 0x02, 0x25, - 0x04, 0x7d, 0x2c, 0x1a, 0xfe, 0x20, 0x05, 0x17, 0x88, 0x01, 0x2e, 0x01, 0x9b, 0x01, 0x9d, 0x35, - 0xfe, 0x60, 0x02, 0x02, 0xf4, 0xef, 0x38, 0x86, 0x18, 0xfe, 0x67, 0x1b, 0xfe, 0xbf, 0x57, 0xf5, - 0xfe, 0x48, 0x1c, 0x8f, 0x01, 0xf2, 0xb1, 0xfe, 0x96, 0xf0, 0xfe, 0x28, 0x04, 0x2f, 0xfe, 0x2c, - 0x04, 0x35, 0x27, 0x0f, 0x3d, 0x01, 0x15, 0x05, 0x10, 0x1a, 0xfe, 0x0c, 0x05, 0x4c, 0x97, 0xa3, - 0x33, 0x84, 0x74, 0x19, 0x32, 0x1f, 0x25, 0x04, 0x7d, 0x2c, 0xfe, 0x10, 0x12, 0x17, 0x88, 0x01, - 0x2e, 0x35, 0xfe, 0x60, 0x02, 0x02, 0xf4, 0x21, 0xfe, 0xa0, 0x00, 0xfe, 0x9b, 0x57, 0xfe, 0x5e, - 0x12, 0x0a, 0x08, 0x06, 0xfe, 0x56, 0x12, 0x23, 0x29, 0x98, 0x01, 0x0c, 0x84, 0x74, 0x1f, 0xfe, - 0xdc, 0x04, 0x23, 0x29, 0x98, 0x01, 0x0c, 0x1f, 0x25, 0x23, 0x29, 0xba, 0xfe, 0x4c, 0x44, 0xfe, - 0x32, 0x12, 0x50, 0xfe, 0x44, 0x48, 0x07, 0xfe, 0x93, 0x00, 0xfe, 0x4c, 0x54, 0x77, 0xfe, 0x0c, - 0x05, 0x81, 0xa3, 0x33, 0xfe, 0x06, 0x80, 0xfe, 0x48, 0x47, 0xfe, 0x48, 0x13, 0x3e, 0x05, 0xfe, - 0xcc, 0x00, 0xfe, 0x40, 0x13, 0x0a, 0x08, 0x06, 0xea, 0xfe, 0x06, 0x10, 0x23, 0x29, 0xba, 0x0a, - 0x08, 0x39, 0xe1, 0x17, 0xa6, 0x0a, 0x08, 0x06, 0x59, 0x17, 0xfe, 0x0d, 0x00, 0x01, 0x2e, 0x35, - 0xfe, 0x66, 0x0d, 0x02, 0x25, 0x3b, 0x11, 0xfe, 0xe6, 0x00, 0xfe, 0x1c, 0x90, 0xb7, 0x03, 0x17, - 0xa6, 0x01, 0x2e, 0x35, 0x27, 0x19, 0x27, 0x02, 0xfe, 0x14, 0x05, 0xfe, 0x42, 0x5b, 0x86, 0x18, - 0xfe, 0x46, 0x59, 0xfe, 0xbf, 0x57, 0xf5, 0x17, 0x78, 0xfe, 0x07, 0x80, 0xfe, 0x31, 0x44, 0x0a, - 0x08, 0x0b, 0x99, 0xfe, 0x20, 0x80, 0x05, 0x18, 0xfe, 0x70, 0x12, 0x73, 0x08, 0x06, 0xfe, 0x60, - 0x13, 0x04, 0xfe, 0xa2, 0x00, 0x2c, 0x1a, 0xfe, 0xac, 0x05, 0xfe, 0x31, 0xe4, 0x5f, 0x73, 0x08, - 0x0b, 0xfe, 0x4a, 0x13, 0x04, 0xfe, 0xa0, 0x00, 0x2c, 0xfe, 0x42, 0x12, 0x62, 0x2f, 0xfe, 0x6c, - 0x05, 0x19, 0x32, 0xf7, 0x01, 0x0c, 0x26, 0xfe, 0xc4, 0x05, 0x11, 0xfe, 0xe3, 0x00, 0x24, 0x73, - 0xfe, 0x4a, 0xf0, 0xfe, 0x96, 0x05, 0xfe, 0x49, 0xf0, 0xfe, 0x90, 0x05, 0xab, 0x20, 0xfe, 0x21, - 0x00, 0xa8, 0x20, 0xfe, 0x22, 0x00, 0xa2, 0x20, 0x8d, 0xfe, 0x09, 0x48, 0x01, 0x0c, 0x26, 0xfe, - 0xc4, 0x05, 0xfe, 0xe2, 0x08, 0x73, 0x08, 0xe0, 0x59, 0x01, 0x99, 0x20, 0x06, 0x16, 0xe8, 0x4b, - 0xfe, 0x27, 0x01, 0x0a, 0x08, 0x39, 0xb0, 0x46, 0x01, 0xb6, 0x17, 0xa6, 0x0a, 0x08, 0x06, 0x59, - 0x17, 0xfe, 0x0d, 0x00, 0x01, 0x2e, 0x01, 0x9b, 0x01, 0x9d, 0x35, 0xfe, 0x66, 0x0d, 0x02, 0x25, - 0x04, 0xfe, 0x9c, 0x00, 0x2c, 0xfe, 0x3e, 0x12, 0x04, 0x5b, 0x2c, 0xfe, 0x36, 0x13, 0x46, 0x01, - 0xb6, 0x26, 0xfe, 0x3c, 0x06, 0x0f, 0x06, 0x73, 0x08, 0x22, 0xfe, 0x02, 0x12, 0x69, 0x01, 0xfe, - 0xd0, 0x14, 0x1f, 0xfe, 0x32, 0x06, 0x11, 0xc8, 0x01, 0x41, 0x11, 0xfe, 0xe5, 0x00, 0x04, 0x5b, - 0xc2, 0x0e, 0x5b, 0x04, 0xfe, 0x9e, 0x00, 0x2c, 0xfe, 0x62, 0x12, 0x04, 0x56, 0x2c, 0xf1, 0x01, - 0xfe, 0x40, 0x19, 0x01, 0xfe, 0xaa, 0x19, 0xee, 0xd2, 0xec, 0x07, 0x6a, 0xff, 0x02, 0x00, 0x57, - 0x6c, 0x80, 0x1b, 0x58, 0xd1, 0xd2, 0x8b, 0x46, 0x01, 0xb6, 0x26, 0xfe, 0xa6, 0x06, 0x73, 0x08, - 0x1d, 0xa7, 0x7c, 0x0f, 0x5d, 0x01, 0xfe, 0xfe, 0x14, 0x1f, 0xfe, 0x9c, 0x06, 0x11, 0xc8, 0x01, - 0x41, 0x11, 0xfe, 0xe5, 0x00, 0x04, 0x56, 0xc2, 0x0e, 0x56, 0x09, 0x06, 0x01, 0xb6, 0xfc, 0x76, - 0x8f, 0x01, 0xf2, 0xb1, 0x11, 0xfe, 0xe2, 0x00, 0x2f, 0xfe, 0xbe, 0x06, 0x19, 0x32, 0xd7, 0xfe, - 0xda, 0x06, 0x83, 0xfe, 0x78, 0x07, 0xd4, 0xfe, 0x80, 0x07, 0x71, 0x8c, 0x02, 0x25, 0x0a, 0x08, + 0x00, 0x00, 0x00, 0xf2, 0x00, 0xf0, 0x00, 0x16, 0x00, 0xfc, 0x48, 0xe4, 0x01, 0x00, 0x18, 0xe4, + 0x00, 0xf6, 0x01, 0xf6, 0x18, 0x80, 0x02, 0x00, 0x40, 0x1a, 0x00, 0xfa, 0xff, 0xff, 0x03, 0xf6, + 0xff, 0x00, 0x82, 0xe7, 0x01, 0xfa, 0x9e, 0xe7, 0x09, 0xe7, 0x1a, 0x0f, 0x00, 0xea, 0x01, 0xe6, + 0x03, 0x00, 0x55, 0xf0, 0x18, 0xf4, 0x1e, 0xf0, 0x3e, 0x57, 0x04, 0x00, 0x3e, 0x01, 0x85, 0xf0, + 0x00, 0xe6, 0x03, 0xfc, 0x08, 0x00, 0x2c, 0x1a, 0x32, 0xf0, 0x86, 0xf0, 0xbe, 0x0d, 0xd4, 0x01, + 0xd5, 0xf0, 0x00, 0xec, 0x01, 0xfc, 0x38, 0x54, 0x98, 0x57, 0xbc, 0x00, 0x0c, 0x1c, 0xb1, 0xf0, + 0x3c, 0x00, 0xb4, 0x00, 0xb8, 0x0d, 0x00, 0x57, 0x01, 0xf0, 0x02, 0x13, 0x02, 0xfc, 0x03, 0xe6, + 0x10, 0x00, 0x18, 0x40, 0x3e, 0x1c, 0x44, 0x13, 0x6c, 0x01, 0x6e, 0x01, 0xbd, 0x00, 0xe0, 0x00, + 0x02, 0x80, 0x30, 0xe4, 0x3e, 0x00, 0x74, 0x01, 0x76, 0x01, 0x7c, 0x16, 0x80, 0x00, 0xb9, 0x54, + 0xbb, 0x00, 0xee, 0x13, 0x00, 0x4e, 0x01, 0x01, 0x01, 0xea, 0x02, 0x48, 0x02, 0xfa, 0x04, 0x12, + 0x08, 0x12, 0x3c, 0x56, 0x4e, 0x01, 0x5d, 0xf0, 0x7a, 0x01, 0x7e, 0x10, 0xb6, 0x00, 0xc2, 0x10, + 0xee, 0x08, 0x00, 0x80, 0x05, 0xfc, 0x10, 0x44, 0x24, 0x01, 0x28, 0x01, 0x32, 0x00, 0x3c, 0x01, + 0x40, 0x00, 0x4b, 0xe4, 0x4b, 0xf4, 0x4c, 0x1c, 0x68, 0x01, 0x6a, 0x01, 0x70, 0x01, 0x72, 0x01, + 0x78, 0x01, 0x7c, 0x01, 0xbb, 0x55, 0xc2, 0x0d, 0x00, 0x01, 0x02, 0xee, 0x03, 0x58, 0x03, 0xf7, + 0x03, 0xfa, 0x04, 0x80, 0x08, 0x44, 0x09, 0xf0, 0x0f, 0x00, 0x1b, 0x80, 0x20, 0x01, 0x38, 0x1c, + 0x4e, 0x1c, 0x5b, 0xf0, 0x62, 0x0a, 0xaa, 0x00, 0xbe, 0x00, 0xc0, 0x00, 0xc0, 0x15, 0xcc, 0x10, + 0x00, 0x4c, 0x00, 0xdc, 0x02, 0x4a, 0x04, 0xfc, 0x05, 0x00, 0x05, 0xf0, 0x05, 0xf8, 0x06, 0x13, + 0x06, 0xf7, 0x08, 0x13, 0x0a, 0x10, 0x0c, 0x00, 0x0e, 0x47, 0x0e, 0xf7, 0x10, 0x0f, 0x20, 0x00, + 0x20, 0x16, 0x2a, 0x01, 0x32, 0x1c, 0x36, 0x00, 0x42, 0x54, 0x44, 0x55, 0x45, 0x5a, 0x52, 0x0c, + 0x59, 0xf0, 0x5c, 0xf0, 0x69, 0x08, 0x6e, 0x0b, 0x83, 0x59, 0xb8, 0xf0, 0xbd, 0x56, 0xcc, 0x18, + 0xce, 0x10, 0xd8, 0x18, 0xf0, 0x00, 0x01, 0x48, 0x04, 0x10, 0x04, 0xea, 0x04, 0xf6, 0x05, 0x80, + 0x05, 0xe6, 0x06, 0x00, 0x06, 0x0f, 0x06, 0x12, 0x0b, 0xf0, 0x0c, 0x10, 0x0c, 0xf0, 0x10, 0x13, + 0x12, 0x10, 0x19, 0x00, 0x19, 0xe4, 0x30, 0x1c, 0x33, 0x00, 0x34, 0x00, 0x38, 0x44, 0x40, 0x5c, + 0x4a, 0xe4, 0x62, 0x1a, 0x68, 0x08, 0x68, 0x54, 0x6c, 0x15, 0x70, 0x15, 0x83, 0x55, 0x83, 0x5a, + 0x91, 0x44, 0xa4, 0x00, 0xac, 0x13, 0xb0, 0x57, 0xb5, 0x00, 0xb8, 0x17, 0xba, 0x00, 0xce, 0x45, + 0xd0, 0x00, 0xe1, 0x00, 0xe5, 0x55, 0xe7, 0x00, 0x00, 0x54, 0x01, 0x58, 0x02, 0x10, 0x02, 0xe6, + 0x03, 0xa1, 0x04, 0x13, 0x06, 0x83, 0x06, 0xf0, 0x07, 0x00, 0x0a, 0x00, 0x0a, 0x12, 0x0a, 0xf0, + 0x0c, 0x12, 0x0c, 0x13, 0x0c, 0x90, 0x0e, 0x13, 0x10, 0x04, 0x10, 0x10, 0x12, 0x1c, 0x19, 0x81, + 0x1a, 0x10, 0x1c, 0x00, 0x1c, 0x12, 0x1d, 0xf7, 0x1e, 0x13, 0x20, 0x1c, 0x20, 0xe7, 0x22, 0x01, + 0x26, 0x01, 0x2a, 0x12, 0x2c, 0x0f, 0x30, 0xe7, 0x32, 0x15, 0x34, 0x1c, 0x36, 0x1c, 0x38, 0x12, + 0x3a, 0x55, 0x3f, 0x00, 0x41, 0x58, 0x43, 0x48, 0x46, 0x1c, 0x4e, 0xe4, 0x76, 0x02, 0x77, 0x57, + 0x78, 0x03, 0x89, 0x48, 0x8e, 0x90, 0x98, 0x80, 0x99, 0x00, 0xfe, 0x9c, 0xf0, 0x27, 0x02, 0xfe, + 0xe0, 0x0d, 0xff, 0x10, 0x00, 0x00, 0xfe, 0xc6, 0x01, 0xfe, 0x56, 0x1a, 0x00, 0xfe, 0xc4, 0x01, + 0xfe, 0x84, 0x01, 0xff, 0x03, 0x00, 0x00, 0xfe, 0x6a, 0x13, 0xfe, 0x05, 0x05, 0xff, 0x40, 0x00, + 0x00, 0x0e, 0xff, 0x09, 0x00, 0x00, 0xff, 0x08, 0x01, 0x01, 0xff, 0x10, 0xff, 0xff, 0xff, 0x1f, + 0x00, 0x00, 0xff, 0x10, 0xff, 0xff, 0xff, 0x11, 0x00, 0x00, 0xfe, 0x78, 0x56, 0xfe, 0x34, 0x12, + 0xff, 0x21, 0x00, 0x00, 0xfe, 0x04, 0xf7, 0xfe, 0xc4, 0x01, 0x2e, 0x88, 0x0b, 0x01, 0xfe, 0xca, + 0x0f, 0xfe, 0x04, 0xf7, 0xfe, 0xc4, 0x01, 0x88, 0x0b, 0x1c, 0x2e, 0xfe, 0x3d, 0xf0, 0xfe, 0xfc, + 0x01, 0xfe, 0x20, 0xf0, 0xdc, 0x04, 0x5f, 0x4f, 0x02, 0xfe, 0xfc, 0x0d, 0x01, 0xfe, 0x5c, 0x0e, + 0xfe, 0xe9, 0x12, 0x02, 0xfe, 0x08, 0x03, 0xfe, 0x28, 0x1c, 0x04, 0xfe, 0xa6, 0x00, 0xfe, 0xdd, + 0x12, 0x47, 0x12, 0xfe, 0xa6, 0x00, 0xcd, 0xfe, 0x48, 0xf0, 0xfe, 0x80, 0x02, 0xfe, 0x49, 0xf0, + 0xfe, 0x9a, 0x02, 0xfe, 0x4a, 0xf0, 0xfe, 0xb8, 0x02, 0xfe, 0x46, 0xf0, 0xfe, 0x4a, 0x02, 0xfe, + 0x47, 0xf0, 0xfe, 0x50, 0x02, 0xfe, 0x43, 0xf0, 0xfe, 0x3e, 0x02, 0xfe, 0x44, 0xf0, 0xfe, 0x42, + 0x02, 0xfe, 0x45, 0xf0, 0xfe, 0x46, 0x02, 0x09, 0x0b, 0xa4, 0x09, 0x06, 0x12, 0xc1, 0x02, 0x27, + 0xfe, 0x00, 0x1c, 0xfe, 0xf1, 0x10, 0xfe, 0x02, 0x1c, 0xfe, 0xed, 0x10, 0xfe, 0x1e, 0x1c, 0xfe, + 0xe9, 0x10, 0x01, 0xfe, 0x2c, 0x18, 0xfe, 0xe7, 0x10, 0xfe, 0x06, 0xfc, 0xfe, 0xa8, 0x00, 0x0f, + 0x7c, 0x01, 0xaa, 0x02, 0x27, 0x17, 0x5e, 0x4c, 0xc4, 0x01, 0xfe, 0x40, 0x10, 0x0f, 0x7c, 0x01, + 0x8e, 0xfe, 0xbd, 0x10, 0x0f, 0x7c, 0x01, 0x8e, 0xfe, 0xad, 0x10, 0xfe, 0x16, 0x1c, 0xfe, 0x58, + 0x1c, 0x09, 0x06, 0x12, 0xc1, 0x2e, 0x1b, 0x27, 0xfe, 0x3d, 0xf0, 0xfe, 0xfc, 0x01, 0x28, 0xfe, + 0x8e, 0x02, 0xfe, 0x5a, 0x1c, 0xde, 0xfe, 0x14, 0x1c, 0x17, 0xfe, 0x30, 0x00, 0x4c, 0xc4, 0x01, + 0xfe, 0x30, 0x10, 0x09, 0x06, 0x12, 0xc1, 0x02, 0xfe, 0xc6, 0x01, 0x29, 0x2d, 0x05, 0x10, 0x35, + 0xfe, 0x69, 0x10, 0x09, 0x06, 0x12, 0xc1, 0xfe, 0x04, 0xec, 0x2d, 0x08, 0x2d, 0x09, 0x3e, 0x1c, + 0x01, 0x45, 0x82, 0xfe, 0x05, 0xf6, 0xfe, 0xa8, 0x00, 0x01, 0xfe, 0x56, 0x17, 0x0a, 0x41, 0x8f, + 0x39, 0x11, 0x48, 0x1c, 0xd2, 0x08, 0x1e, 0x09, 0x52, 0x01, 0x90, 0x02, 0x27, 0x0f, 0x3f, 0x01, + 0x15, 0x05, 0x10, 0xdb, 0x08, 0x1e, 0x09, 0x52, 0x01, 0x7e, 0xfe, 0x28, 0x10, 0x0f, 0xc8, 0x01, + 0x15, 0xf2, 0x0f, 0x7d, 0x01, 0x15, 0xfe, 0x49, 0x54, 0x79, 0xfe, 0x16, 0x03, 0x08, 0x1e, 0x09, + 0x52, 0x01, 0x90, 0x02, 0x27, 0x2e, 0x82, 0xfe, 0x02, 0xe8, 0x31, 0xfe, 0xbf, 0x57, 0xfe, 0x9e, + 0x43, 0xf7, 0xfe, 0x07, 0x4b, 0xfe, 0x20, 0xf0, 0xdc, 0xfe, 0x40, 0x1c, 0x1b, 0xf8, 0xfe, 0x26, + 0xf0, 0xfe, 0x74, 0x03, 0xfe, 0xa0, 0xf0, 0xfe, 0x62, 0x03, 0xfe, 0x11, 0xf0, 0xdc, 0xfe, 0x0e, + 0x10, 0xfe, 0x9f, 0xf0, 0xfe, 0x82, 0x03, 0xf4, 0x13, 0xfe, 0x11, 0x00, 0x02, 0x6b, 0x2e, 0xfe, + 0x48, 0x1c, 0xf4, 0x1b, 0xf8, 0x34, 0xf8, 0xfe, 0x82, 0xf0, 0xfe, 0x88, 0x03, 0x2b, 0x29, 0xc6, + 0x72, 0x16, 0xc6, 0x0f, 0x7d, 0x01, 0x15, 0x72, 0x80, 0x08, 0x1e, 0x09, 0x52, 0x01, 0x45, 0x11, + 0x3f, 0x08, 0x3f, 0x09, 0xa2, 0x01, 0x90, 0xfe, 0x9c, 0x32, 0x11, 0xfe, 0xe4, 0x00, 0x2f, 0xfe, + 0xce, 0x03, 0x1b, 0x32, 0x1f, 0xfe, 0xde, 0x03, 0x01, 0x55, 0xd3, 0xfe, 0xee, 0x03, 0x73, 0x97, + 0xd7, 0xfe, 0xae, 0x06, 0x02, 0x26, 0x04, 0x7c, 0x2c, 0x19, 0xfe, 0x20, 0x05, 0x17, 0x8b, 0x01, + 0x3b, 0x01, 0x9f, 0x01, 0xa1, 0x34, 0xfe, 0x60, 0x02, 0x02, 0xf6, 0xf4, 0x2e, 0x88, 0x18, 0xfe, + 0x67, 0x1b, 0xfe, 0xbf, 0x57, 0xf7, 0xfe, 0x48, 0x1c, 0x92, 0x01, 0xfe, 0x9c, 0x13, 0xb3, 0xfe, + 0x96, 0xf0, 0xfe, 0x28, 0x04, 0x2f, 0xfe, 0x2c, 0x04, 0x34, 0x27, 0x0f, 0x3f, 0x01, 0x15, 0x05, + 0x10, 0x19, 0xfe, 0x0c, 0x05, 0x4d, 0x7a, 0xa5, 0x31, 0x86, 0x76, 0x1b, 0x32, 0x1f, 0x26, 0x04, + 0x7c, 0x2c, 0xfe, 0x10, 0x12, 0x17, 0x8b, 0x01, 0x3b, 0x34, 0xfe, 0x60, 0x02, 0x02, 0xf6, 0x21, + 0xfe, 0xa0, 0x00, 0xfe, 0x9b, 0x57, 0xfe, 0x5e, 0x12, 0x0a, 0x07, 0x06, 0xfe, 0x56, 0x12, 0x24, + 0x23, 0x9a, 0x01, 0x0c, 0x86, 0x76, 0x1f, 0xfe, 0xdc, 0x04, 0x24, 0x23, 0x9a, 0x01, 0x0c, 0x1f, + 0x26, 0x24, 0x23, 0xba, 0xfe, 0x4c, 0x44, 0xfe, 0x32, 0x12, 0x51, 0xfe, 0x44, 0x48, 0x08, 0xfe, + 0x93, 0x00, 0xfe, 0x4c, 0x54, 0x79, 0xfe, 0x0c, 0x05, 0x82, 0xa5, 0x31, 0xfe, 0x06, 0x80, 0xfe, + 0x48, 0x47, 0xfe, 0x48, 0x13, 0x40, 0x05, 0xfe, 0xcc, 0x00, 0xfe, 0x40, 0x13, 0x0a, 0x07, 0x06, + 0xef, 0xfe, 0x06, 0x10, 0x24, 0x23, 0xba, 0x0a, 0x07, 0x38, 0xe2, 0x17, 0xa9, 0x0a, 0x07, 0x06, + 0x4f, 0x17, 0xfe, 0x0d, 0x00, 0x01, 0x3b, 0x34, 0xfe, 0xa0, 0x0d, 0x02, 0x26, 0x3a, 0x11, 0xfe, + 0xe6, 0x00, 0xfe, 0x1c, 0x90, 0xb7, 0x03, 0x17, 0xa9, 0x01, 0x3b, 0x34, 0x27, 0x1b, 0x27, 0x02, + 0xfe, 0x14, 0x05, 0xfe, 0x42, 0x5b, 0x88, 0x18, 0xfe, 0x46, 0x59, 0xfe, 0xbf, 0x57, 0xf7, 0x17, + 0x46, 0xfe, 0x07, 0x80, 0xfe, 0x31, 0x44, 0x0a, 0x07, 0x0b, 0xfe, 0x78, 0x13, 0xfe, 0x20, 0x80, + 0x05, 0x18, 0xfe, 0x70, 0x12, 0x75, 0x07, 0x06, 0xfe, 0x60, 0x13, 0x04, 0xfe, 0xa2, 0x00, 0x2c, + 0x19, 0xfe, 0xac, 0x05, 0xfe, 0x31, 0xe4, 0x60, 0x75, 0x07, 0x0b, 0xfe, 0x4a, 0x13, 0x04, 0xfe, + 0xa0, 0x00, 0x2c, 0xfe, 0x42, 0x12, 0x63, 0x2f, 0xfe, 0x6c, 0x05, 0x1b, 0x32, 0xf9, 0x01, 0x0c, + 0x25, 0xfe, 0xc4, 0x05, 0x11, 0xfe, 0xe3, 0x00, 0x2b, 0x75, 0xfe, 0x4a, 0xf0, 0xfe, 0x96, 0x05, + 0xfe, 0x49, 0xf0, 0xfe, 0x90, 0x05, 0xad, 0x20, 0xfe, 0x21, 0x00, 0x8a, 0x20, 0xfe, 0x22, 0x00, + 0xa4, 0x20, 0x8f, 0xfe, 0x09, 0x48, 0x01, 0x0c, 0x25, 0xfe, 0xc4, 0x05, 0xfe, 0xe2, 0x08, 0x75, + 0x07, 0xe1, 0x4f, 0x01, 0xc2, 0x20, 0x06, 0x16, 0xe8, 0x4c, 0xfe, 0x27, 0x01, 0x0a, 0x07, 0x38, + 0xe9, 0x47, 0x01, 0xbd, 0x17, 0xa9, 0x0a, 0x07, 0x06, 0x4f, 0x17, 0xfe, 0x0d, 0x00, 0x01, 0x3b, + 0x01, 0x9f, 0x01, 0xa1, 0x34, 0xfe, 0xa0, 0x0d, 0x02, 0x26, 0x04, 0xfe, 0x9c, 0x00, 0x2c, 0xfe, + 0x3e, 0x12, 0x04, 0x5c, 0x2c, 0xfe, 0x36, 0x13, 0x47, 0x01, 0xbd, 0x25, 0xfe, 0x3c, 0x06, 0x0f, + 0x06, 0x75, 0x07, 0x22, 0xfe, 0x02, 0x12, 0x6a, 0x01, 0xfe, 0x06, 0x15, 0x1f, 0xfe, 0x32, 0x06, + 0x11, 0xc9, 0x01, 0x55, 0x11, 0xfe, 0xe5, 0x00, 0x04, 0x5c, 0xc3, 0x0d, 0x5c, 0x04, 0xfe, 0x9e, + 0x00, 0x2c, 0xfe, 0x62, 0x12, 0x04, 0x56, 0x2c, 0xfe, 0x5a, 0x13, 0x01, 0xfe, 0x7e, 0x19, 0x01, + 0xfe, 0xe8, 0x19, 0xf3, 0xa8, 0xf1, 0x08, 0x6c, 0xff, 0x02, 0x00, 0x57, 0x6e, 0x81, 0x1a, 0x59, + 0xd1, 0xa8, 0x74, 0x47, 0x01, 0xbd, 0x25, 0xfe, 0xa6, 0x06, 0x75, 0x07, 0x1d, 0xab, 0x9e, 0x0f, + 0x5e, 0x01, 0xfe, 0x34, 0x15, 0x1f, 0xfe, 0x9c, 0x06, 0x11, 0xc9, 0x01, 0x55, 0x11, 0xfe, 0xe5, + 0x00, 0x04, 0x56, 0xc3, 0x0d, 0x56, 0x09, 0x06, 0x01, 0xbd, 0xfe, 0x9c, 0x32, 0x78, 0x92, 0x01, + 0xfe, 0x9c, 0x13, 0xb3, 0x11, 0xfe, 0xe2, 0x00, 0x2f, 0xfe, 0xbe, 0x06, 0x1b, 0x32, 0xd7, 0xfe, + 0xda, 0x06, 0x85, 0xfe, 0x78, 0x07, 0xd3, 0xfe, 0x80, 0x07, 0x73, 0x97, 0x02, 0x26, 0x0a, 0x07, 0x0b, 0xfe, 0x2e, 0x12, 0x14, 0x18, 0x01, 0x0c, 0x14, 0x00, 0x01, 0x0c, 0x14, 0x00, 0x01, 0x0c, - 0x14, 0x00, 0x01, 0x0c, 0xfe, 0x99, 0xa4, 0x01, 0x0c, 0x14, 0x00, 0x02, 0xfe, 0x3e, 0x08, 0x6f, - 0x08, 0x1d, 0xea, 0x0a, 0x08, 0x1d, 0xfe, 0x30, 0x13, 0x14, 0xfe, 0x1b, 0x00, 0x01, 0x0c, 0x14, + 0x14, 0x00, 0x01, 0x0c, 0xfe, 0x99, 0xa4, 0x01, 0x0c, 0x14, 0x00, 0x02, 0xfe, 0x50, 0x08, 0x71, + 0x07, 0x1d, 0xef, 0x0a, 0x07, 0x1d, 0xfe, 0x30, 0x13, 0x14, 0xfe, 0x1b, 0x00, 0x01, 0x0c, 0x14, 0x00, 0x01, 0x0c, 0x14, 0x00, 0x01, 0x0c, 0x14, 0x00, 0x01, 0x0c, 0x14, 0x06, 0x01, 0x0c, 0x14, - 0x00, 0x02, 0xfe, 0xe6, 0x0b, 0x69, 0xfe, 0x9a, 0x81, 0x6d, 0x8d, 0xfe, 0x09, 0x6f, 0xfe, 0x93, - 0x45, 0x1a, 0xfe, 0x88, 0x07, 0x2f, 0xfe, 0x60, 0x07, 0x19, 0x32, 0xd7, 0xfe, 0x58, 0x07, 0x71, - 0x8c, 0x83, 0xfe, 0x78, 0x07, 0x02, 0x25, 0x01, 0x41, 0x02, 0xfe, 0xbe, 0x06, 0x14, 0x22, 0x02, - 0xfe, 0xbe, 0x06, 0xfe, 0x9c, 0xf7, 0xfe, 0xf4, 0x07, 0xfe, 0x2c, 0x90, 0xfe, 0xae, 0x90, 0x52, - 0xfe, 0xd6, 0x07, 0x0e, 0x65, 0x12, 0x66, 0x0a, 0x4f, 0x5f, 0x3a, 0x01, 0xfe, 0xd6, 0x18, 0x05, - 0x10, 0x85, 0xfe, 0x83, 0xe7, 0xfe, 0x95, 0x00, 0xa8, 0xfe, 0x03, 0x40, 0x0a, 0x4f, 0x78, 0x3a, - 0x01, 0xbc, 0xb5, 0xfe, 0x1f, 0x40, 0x16, 0x67, 0x01, 0xf8, 0xfe, 0x08, 0x50, 0xfe, 0x8a, 0x50, - 0xfe, 0x34, 0x51, 0xfe, 0xb6, 0x51, 0xfe, 0x08, 0x90, 0xfe, 0x8a, 0x90, 0x0e, 0x63, 0x12, 0x64, - 0xfe, 0x0c, 0x90, 0xfe, 0x8e, 0x90, 0xfe, 0x28, 0x50, 0xfe, 0xaa, 0x50, 0x0e, 0x42, 0x12, 0x43, - 0x41, 0x0a, 0x08, 0x5f, 0xb0, 0xfe, 0x2c, 0x90, 0xfe, 0xae, 0x90, 0x0e, 0x65, 0x12, 0x66, 0x0a, - 0x08, 0x78, 0xd1, 0x01, 0xbc, 0xfe, 0x1f, 0x80, 0x16, 0x67, 0xfe, 0x34, 0x90, 0xfe, 0xb6, 0x90, - 0x0e, 0x44, 0x12, 0x45, 0xfe, 0x08, 0x90, 0xfe, 0x8a, 0x90, 0x0e, 0x63, 0x12, 0x64, 0xfe, 0x28, - 0x90, 0xfe, 0xaa, 0x90, 0x0e, 0x42, 0x12, 0x43, 0x0e, 0x31, 0x12, 0x3f, 0x24, 0x0e, 0x53, 0x0e, - 0x68, 0x0a, 0x4f, 0x22, 0x3a, 0x38, 0x07, 0xa5, 0x2f, 0xfe, 0x5e, 0x08, 0xfe, 0x9e, 0xf0, 0xfe, - 0x72, 0x08, 0xcc, 0x1a, 0x32, 0x38, 0x72, 0xfe, 0xed, 0x10, 0xaa, 0xfe, 0x96, 0x08, 0xac, 0xfe, - 0xb2, 0x08, 0x83, 0xfe, 0x8a, 0x08, 0xd4, 0xfe, 0x90, 0x08, 0x71, 0x8c, 0x02, 0x25, 0x01, 0x41, - 0xfe, 0xc9, 0x10, 0x14, 0x22, 0xfe, 0xc9, 0x10, 0x6f, 0x08, 0x06, 0xfe, 0x10, 0x12, 0x6f, 0x08, - 0x0b, 0x4e, 0x0a, 0x08, 0x0b, 0xfe, 0x8e, 0x12, 0xfe, 0x2e, 0x1c, 0xad, 0x6f, 0x08, 0x06, 0x4e, - 0x6f, 0x08, 0x0b, 0xfe, 0x7a, 0x12, 0xfe, 0x2c, 0x1c, 0xfe, 0xaa, 0xf0, 0xfe, 0xcc, 0x09, 0xfe, - 0xac, 0xf0, 0xfe, 0xfa, 0x08, 0x02, 0xfe, 0xd8, 0x09, 0xfe, 0xb7, 0xf0, 0xfe, 0xf6, 0x08, 0xfe, - 0x02, 0xf6, 0x1d, 0x69, 0xfe, 0x70, 0x18, 0xfe, 0xf1, 0x18, 0xfe, 0x40, 0x55, 0xfe, 0xe1, 0x55, - 0xfe, 0x10, 0x58, 0xfe, 0x91, 0x58, 0xfe, 0x14, 0x59, 0xfe, 0x95, 0x59, 0x19, 0x92, 0xfe, 0x8c, - 0xf0, 0xfe, 0xf6, 0x08, 0xfe, 0xac, 0xf0, 0xfe, 0xea, 0x08, 0xfe, 0x34, 0x1c, 0xfe, 0xcb, 0x10, - 0xfe, 0xad, 0xf0, 0xfe, 0x06, 0x09, 0x02, 0xfe, 0x12, 0x0b, 0xfe, 0x36, 0x1c, 0xfe, 0xbf, 0x10, - 0xfe, 0x2b, 0xf0, 0x92, 0xfe, 0x6b, 0x18, 0x1b, 0xfe, 0x00, 0xfe, 0xe1, 0xcd, 0xfe, 0xd2, 0xf0, - 0x92, 0xfe, 0x76, 0x18, 0x1b, 0x18, 0x1a, 0x92, 0x04, 0xe7, 0x1b, 0x06, 0x1a, 0x92, 0xaa, 0x57, - 0xac, 0x57, 0xfe, 0x34, 0x1c, 0xfe, 0x36, 0x1c, 0xfe, 0x89, 0x10, 0x8f, 0x62, 0x3b, 0x17, 0xa6, - 0x01, 0x2e, 0x13, 0xfe, 0x35, 0x00, 0x35, 0x54, 0x13, 0x90, 0x02, 0x54, 0xf9, 0xaf, 0x0b, 0xfe, - 0x1a, 0x12, 0x50, 0xfe, 0x19, 0x82, 0xfe, 0x6c, 0x18, 0xfe, 0x44, 0x54, 0xeb, 0xde, 0xfe, 0x74, - 0x18, 0x91, 0x93, 0x1a, 0xfe, 0xc8, 0x08, 0x02, 0x57, 0x0a, 0x08, 0x5f, 0x2e, 0x04, 0x31, 0x2b, - 0x3f, 0x0e, 0x44, 0x12, 0x45, 0x82, 0x31, 0x5a, 0x3f, 0xfe, 0x6c, 0x18, 0xfe, 0xed, 0x18, 0xfe, - 0x44, 0x54, 0xfe, 0xe5, 0x54, 0x36, 0x44, 0x21, 0x45, 0x04, 0x53, 0x2b, 0x68, 0x91, 0xfe, 0xe3, - 0x54, 0xfe, 0x74, 0x18, 0xfe, 0xf5, 0x18, 0x91, 0xfe, 0xe3, 0x54, 0x93, 0xc9, 0x52, 0xfe, 0xc8, - 0x08, 0x02, 0x57, 0xfe, 0x37, 0xf0, 0xfe, 0xd4, 0x09, 0xfe, 0x8b, 0xf0, 0xfe, 0x5a, 0x09, 0x02, - 0x57, 0xf9, 0xaf, 0x0b, 0x28, 0xfe, 0xf4, 0x0a, 0x36, 0x53, 0x21, 0x68, 0x52, 0xfe, 0x38, 0x0a, - 0x07, 0xfe, 0xc0, 0x07, 0x46, 0x61, 0x00, 0xd9, 0xfe, 0x01, 0x59, 0xfe, 0x52, 0xf0, 0xfe, 0x06, - 0x0a, 0x91, 0x96, 0xfe, 0x1e, 0x0a, 0x36, 0x53, 0x91, 0xfe, 0xe3, 0x54, 0x4d, 0x53, 0x6e, 0x68, - 0xfe, 0x14, 0x58, 0xfe, 0x95, 0x58, 0x02, 0x57, 0x36, 0x53, 0x21, 0x68, 0xfe, 0x14, 0x59, 0xfe, - 0x95, 0x59, 0xeb, 0x4d, 0x53, 0x4d, 0x68, 0x02, 0x57, 0x0a, 0x08, 0x5f, 0xfe, 0x82, 0x12, 0x0a, - 0x08, 0x22, 0xfe, 0x66, 0x13, 0x2a, 0x67, 0x70, 0xd0, 0xfe, 0x83, 0x80, 0xfe, 0xc8, 0x44, 0xfe, - 0x2e, 0x13, 0xfe, 0x04, 0x91, 0xfe, 0x86, 0x91, 0x6b, 0x33, 0xfe, 0x40, 0x59, 0xfe, 0xc1, 0x59, - 0x52, 0xfe, 0xd0, 0x08, 0x04, 0x65, 0x2b, 0x66, 0x0e, 0xb3, 0x12, 0x90, 0x4d, 0x65, 0x6e, 0x66, - 0x01, 0xbc, 0xb5, 0x6b, 0x33, 0x16, 0x67, 0x82, 0x31, 0x5a, 0x3f, 0x36, 0x44, 0x21, 0x45, 0x93, - 0xc9, 0xfe, 0x04, 0xfa, 0x31, 0xfe, 0x05, 0xfa, 0x3f, 0x01, 0xf8, 0xfe, 0x36, 0x10, 0x24, 0x0e, - 0xb3, 0x0e, 0x90, 0x36, 0x44, 0x21, 0x45, 0xad, 0x0a, 0x08, 0x22, 0x1a, 0xfe, 0xd0, 0x08, 0x36, - 0x42, 0x21, 0x43, 0x0a, 0x08, 0xfe, 0xf7, 0x00, 0x3a, 0x04, 0x63, 0x2b, 0x64, 0xfe, 0x10, 0x58, - 0xfe, 0x91, 0x58, 0x4d, 0x53, 0x6e, 0x68, 0x02, 0xfe, 0xee, 0x09, 0x0a, 0x08, 0x22, 0x1a, 0xfe, - 0xd0, 0x08, 0x0a, 0x08, 0xfe, 0xf7, 0x00, 0x3a, 0xeb, 0xde, 0x69, 0xfe, 0x10, 0x90, 0xfe, 0x92, - 0x90, 0xfe, 0xd3, 0x10, 0x3e, 0x05, 0xca, 0x1a, 0xfe, 0x02, 0x09, 0x11, 0xca, 0xf9, 0xaf, 0x0b, - 0xfe, 0x14, 0x13, 0x04, 0x42, 0x2b, 0x43, 0x52, 0xfe, 0x02, 0x09, 0xfe, 0x0c, 0x58, 0xfe, 0x8d, - 0x58, 0x02, 0x57, 0x24, 0x46, 0xfe, 0x19, 0x80, 0xfe, 0xf1, 0x10, 0x0a, 0x08, 0x0b, 0xa7, 0xfe, - 0x6c, 0x19, 0xfe, 0x19, 0x41, 0xfe, 0x94, 0x10, 0xfe, 0x6c, 0x19, 0x4d, 0x42, 0xfe, 0xed, 0x19, - 0x6e, 0x43, 0xfe, 0x0c, 0x51, 0xfe, 0x8e, 0x51, 0xfe, 0x6b, 0x18, 0x1b, 0xfe, 0x00, 0xff, 0x30, - 0xfe, 0x7a, 0x10, 0xcd, 0xfe, 0xd2, 0xf0, 0xfe, 0x8c, 0x0b, 0xfe, 0x76, 0x18, 0x1b, 0x18, 0xa9, - 0x04, 0xe7, 0x1b, 0x06, 0x87, 0x13, 0xfe, 0x16, 0x00, 0x02, 0x54, 0xfe, 0xd1, 0xf0, 0xfe, 0xc2, - 0x0b, 0x17, 0xa5, 0x01, 0x2e, 0x13, 0xfe, 0x17, 0x00, 0xfe, 0x48, 0x10, 0xfe, 0xce, 0xf0, 0xfe, - 0xaa, 0x0b, 0x13, 0xfe, 0x21, 0x00, 0x02, 0x54, 0xfe, 0xcd, 0xf0, 0xfe, 0xb6, 0x0b, 0x13, 0xfe, - 0x22, 0x00, 0x02, 0x54, 0xfe, 0xcb, 0xf0, 0xfe, 0xc2, 0x0b, 0x13, 0xfe, 0x24, 0x00, 0x02, 0x54, - 0xfe, 0xd0, 0xf0, 0xfe, 0xcc, 0x0b, 0x13, 0xae, 0xdf, 0xfe, 0xcf, 0xf0, 0xfe, 0xd6, 0x0b, 0x13, - 0x8d, 0xdc, 0xfe, 0xcc, 0xf0, 0xfe, 0xe6, 0x0b, 0xfe, 0x84, 0x80, 0xaf, 0x22, 0xfe, 0xd5, 0x12, - 0x13, 0xfe, 0x12, 0x00, 0x2f, 0xfe, 0xe6, 0x0b, 0x19, 0x32, 0xaa, 0x25, 0xac, 0x25, 0x38, 0xfc, - 0x2f, 0xfe, 0xfa, 0x0b, 0x19, 0x32, 0x83, 0xfe, 0x16, 0x0c, 0x71, 0x8c, 0xaa, 0xfe, 0xf4, 0x07, - 0xac, 0xfe, 0xf4, 0x07, 0x02, 0x25, 0x01, 0x41, 0xfe, 0xdb, 0x10, 0x11, 0xfe, 0xe8, 0x00, 0x8f, - 0x84, 0x74, 0xfe, 0x89, 0xf0, 0x25, 0x23, 0x29, 0xfe, 0xe9, 0x09, 0x01, 0x0c, 0x84, 0x74, 0x1f, - 0x25, 0x23, 0x29, 0x98, 0x35, 0xfe, 0x4e, 0x0c, 0x19, 0x32, 0x02, 0xfe, 0x42, 0x0c, 0xcc, 0x4e, - 0x13, 0xfe, 0x42, 0x00, 0x02, 0x54, 0xa4, 0x06, 0xfe, 0x81, 0x49, 0xfe, 0xcc, 0x12, 0x0a, 0x08, - 0x0b, 0xf1, 0x13, 0x00, 0x60, 0x0b, 0xfe, 0x6a, 0x12, 0x60, 0xfe, 0x28, 0x00, 0x28, 0xfe, 0x94, - 0x0d, 0x0f, 0x7e, 0x01, 0x15, 0x05, 0x00, 0x87, 0x37, 0xfe, 0x28, 0x00, 0x02, 0xfe, 0x94, 0x0d, - 0x01, 0x9b, 0x01, 0x9d, 0x0f, 0xc7, 0x01, 0xfe, 0xf0, 0x0e, 0xb9, 0x07, 0x3d, 0x09, 0xa1, 0x01, - 0x40, 0x11, 0x48, 0x07, 0x1e, 0x09, 0x51, 0x01, 0x79, 0x02, 0x27, 0x13, 0xfe, 0x44, 0x00, 0x60, - 0x0b, 0xa7, 0x37, 0x0b, 0xfe, 0xc0, 0x10, 0x01, 0x99, 0x37, 0x0b, 0xfe, 0xb6, 0x10, 0x01, 0x99, - 0xfe, 0x19, 0x82, 0xfe, 0x34, 0x46, 0xfe, 0x0a, 0x13, 0x37, 0x0b, 0x13, 0xfe, 0x43, 0x00, 0xc0, - 0x0a, 0x4f, 0x0b, 0x3a, 0x01, 0x9b, 0x01, 0x9d, 0xb9, 0x07, 0x3d, 0x09, 0xa1, 0x01, 0x40, 0x11, - 0x48, 0x07, 0x1e, 0x09, 0x51, 0x01, 0x79, 0x86, 0x0b, 0xb9, 0x1c, 0xd3, 0x02, 0xfe, 0x4c, 0x03, - 0x0a, 0x08, 0x0b, 0xa9, 0x37, 0x0b, 0x13, 0x00, 0xfe, 0x54, 0x10, 0x6f, 0x08, 0x1d, 0xfe, 0x50, - 0x12, 0x0a, 0x08, 0x1d, 0xfe, 0x48, 0x13, 0xfe, 0x1c, 0x1c, 0xfe, 0x9d, 0xf0, 0xfe, 0x52, 0x0d, - 0xfe, 0x1c, 0x1c, 0xfe, 0x9d, 0xf0, 0xfe, 0x58, 0x0d, 0x0a, 0x4f, 0x1d, 0x3a, 0xfe, 0x95, 0x10, - 0x13, 0xfe, 0x15, 0x00, 0xfe, 0x04, 0xe6, 0x0b, 0x69, 0xfe, 0x26, 0x10, 0x13, 0xfe, 0x13, 0x00, - 0xdc, 0x13, 0xfe, 0x47, 0x00, 0xa8, 0x13, 0xfe, 0x41, 0x00, 0xa2, 0x13, 0xfe, 0x24, 0x00, 0x04, - 0x7d, 0x2c, 0x28, 0xf4, 0x69, 0xfe, 0x04, 0xe6, 0x1d, 0xfe, 0x9d, 0x41, 0xfe, 0x1c, 0x42, 0xb9, - 0x01, 0xfe, 0xf8, 0x0e, 0x02, 0x27, 0xdd, 0x17, 0x0b, 0x4b, 0xfb, 0xe5, 0x17, 0xfe, 0x31, 0x00, - 0x4b, 0xc3, 0x01, 0xfe, 0xfc, 0x0f, 0x02, 0xfe, 0xc6, 0x01, 0x1c, 0xfe, 0x06, 0xec, 0xfe, 0xb9, - 0x00, 0x89, 0x37, 0x39, 0xc6, 0x30, 0x1c, 0xfe, 0x06, 0xea, 0xfe, 0xb9, 0x00, 0xfe, 0x47, 0x4b, - 0x7c, 0xfe, 0x75, 0x57, 0x04, 0x5e, 0xfe, 0x98, 0x56, 0xfe, 0x28, 0x12, 0x0f, 0x7e, 0xfe, 0xfa, - 0x14, 0x46, 0xed, 0x0f, 0xc7, 0xfe, 0xf0, 0x14, 0xfe, 0x49, 0x54, 0x95, 0xfe, 0x08, 0x0e, 0x0f, - 0x1e, 0xfe, 0xe4, 0x14, 0xfe, 0x44, 0x48, 0x02, 0xfe, 0x4c, 0x03, 0x0f, 0x5e, 0xfe, 0xc8, 0x14, - 0x89, 0x37, 0x39, 0xc6, 0x30, 0x1c, 0xfe, 0xce, 0x47, 0xfe, 0xbd, 0x13, 0x02, 0x27, 0x2a, 0x2d, - 0x05, 0x10, 0xfe, 0x78, 0x12, 0x24, 0x16, 0x5d, 0x16, 0xb2, 0x2a, 0x48, 0x46, 0x4b, 0x48, 0xcc, - 0xd9, 0xfe, 0xbc, 0xf0, 0xfe, 0xa4, 0x0e, 0x07, 0x06, 0x16, 0x5d, 0x01, 0xfe, 0xb0, 0x16, 0x04, - 0xfe, 0x38, 0x01, 0x2b, 0xfe, 0x3a, 0x01, 0x52, 0xfe, 0xa8, 0x0e, 0x04, 0xfe, 0x38, 0x01, 0x1b, - 0xfe, 0xf0, 0xff, 0x0e, 0xfe, 0x60, 0x01, 0x04, 0xfe, 0x3a, 0x01, 0x0e, 0xfe, 0x62, 0x01, 0x20, - 0x06, 0x16, 0x48, 0xfe, 0x04, 0xec, 0x2d, 0x07, 0x2d, 0x09, 0x3c, 0x1c, 0x01, 0x40, 0x81, 0xfe, - 0x05, 0xf6, 0xfe, 0x34, 0x01, 0x01, 0xfe, 0x20, 0x17, 0x11, 0x48, 0xd3, 0x07, 0x06, 0x03, 0x24, - 0x03, 0x2a, 0x5d, 0xfe, 0xf7, 0x12, 0x2a, 0xb2, 0x70, 0x16, 0xb2, 0x05, 0xa5, 0xfe, 0x93, 0x13, - 0xfe, 0x24, 0x1c, 0x17, 0x18, 0x4b, 0xfb, 0xe5, 0xfe, 0xd9, 0x10, 0x9a, 0xfe, 0x03, 0xdc, 0xfe, - 0x73, 0x57, 0xfe, 0x80, 0x5d, 0x03, 0x9a, 0xfe, 0x03, 0xdc, 0x24, 0xfe, 0x70, 0x57, 0xfe, 0x33, - 0x54, 0xfe, 0x3b, 0x54, 0xfe, 0x80, 0x5d, 0x03, 0xfe, 0x03, 0x57, 0x9a, 0x24, 0xfe, 0x00, 0xcc, - 0x03, 0xfe, 0x03, 0x57, 0x9a, 0x7f, 0x03, 0x01, 0xfe, 0x50, 0x17, 0x3e, 0x05, 0x48, 0xfe, 0x0a, - 0x13, 0x07, 0x1e, 0x09, 0x51, 0xdc, 0x01, 0x9b, 0x01, 0x9d, 0x07, 0x3d, 0x09, 0xa1, 0x01, 0x40, - 0x11, 0xfe, 0xe9, 0x00, 0x0a, 0x08, 0x8d, 0xfe, 0x52, 0x13, 0x01, 0xfe, 0xe2, 0x16, 0xfe, 0x1e, - 0x1c, 0xfe, 0x14, 0x90, 0x0e, 0xfe, 0x64, 0x01, 0xfe, 0x16, 0x90, 0x0e, 0xfe, 0x66, 0x01, 0x0a, - 0x08, 0x78, 0xea, 0xfe, 0x03, 0x80, 0x72, 0x4c, 0x11, 0x7b, 0x07, 0x2d, 0x09, 0x3c, 0x1c, 0x97, - 0x01, 0xa0, 0xfe, 0x62, 0x08, 0x70, 0x4c, 0x11, 0x7b, 0x07, 0x2d, 0x09, 0x3c, 0x1c, 0x97, 0x01, - 0xa0, 0x6b, 0x33, 0x11, 0x7b, 0x07, 0x2d, 0x09, 0x3c, 0x1c, 0x97, 0x01, 0x79, 0x03, 0xfe, 0x08, - 0x1c, 0x04, 0xfe, 0xac, 0x00, 0xfe, 0x06, 0x58, 0x04, 0xfe, 0xae, 0x00, 0xfe, 0x07, 0x58, 0x04, - 0xfe, 0xb0, 0x00, 0xfe, 0x08, 0x58, 0x04, 0xfe, 0xb2, 0x00, 0xfe, 0x09, 0x58, 0xfe, 0x0a, 0x1c, - 0x20, 0x8b, 0x16, 0xfe, 0xb9, 0x00, 0x24, 0x0e, 0x5b, 0x0e, 0x56, 0x20, 0x10, 0x16, 0x2d, 0x16, - 0x3c, 0x50, 0xa4, 0xfe, 0x93, 0x00, 0x07, 0x2d, 0x09, 0x3c, 0x1c, 0x01, 0x79, 0x81, 0x11, 0x7b, - 0xfe, 0x14, 0x56, 0xfe, 0xd6, 0xf0, 0xfe, 0xd6, 0x0f, 0xdd, 0x8f, 0xfe, 0x14, 0x1c, 0xfe, 0x10, - 0x1c, 0xfe, 0x18, 0x1c, 0x03, 0x1c, 0xfe, 0x0c, 0x14, 0x89, 0xfe, 0x07, 0xe6, 0x39, 0xfe, 0xce, - 0x47, 0xfe, 0xf5, 0x13, 0x03, 0x01, 0x99, 0x0f, 0x3d, 0x01, 0x15, 0x05, 0x10, 0xda, 0x0f, 0x1e, - 0x01, 0x15, 0x05, 0x10, 0xe1, 0xfe, 0x44, 0x58, 0x4c, 0xfe, 0x01, 0xec, 0xc3, 0xfe, 0x9e, 0x40, - 0xfe, 0x9d, 0xe7, 0x00, 0xfe, 0x9c, 0xe7, 0x1d, 0xa3, 0x33, 0x01, 0xfe, 0xf8, 0x0e, 0xfe, 0xc9, - 0x10, 0x03, 0x38, 0x84, 0x74, 0x23, 0x29, 0xba, 0x05, 0x1d, 0xfe, 0x48, 0x12, 0x05, 0x0b, 0xfe, - 0x4c, 0x12, 0x05, 0x18, 0xfe, 0x30, 0x12, 0x05, 0xd5, 0x1a, 0xfe, 0xa0, 0x11, 0x05, 0xfe, 0x23, - 0x00, 0x1a, 0xfe, 0xac, 0x11, 0x05, 0x06, 0x1a, 0xa9, 0x05, 0x22, 0xfe, 0x12, 0x12, 0x05, 0x00, - 0x1a, 0x25, 0x17, 0xd5, 0x01, 0x2e, 0xce, 0x3b, 0x01, 0x0c, 0x83, 0x41, 0x03, 0x3b, 0x11, 0xfe, - 0xcc, 0x00, 0x02, 0x27, 0x3b, 0x3e, 0x05, 0xca, 0xfe, 0xe3, 0x13, 0x36, 0x42, 0x21, 0x43, 0x52, - 0xfe, 0x5e, 0x11, 0x0a, 0x08, 0x5f, 0xfe, 0x72, 0x12, 0x82, 0x31, 0x5a, 0x3f, 0x93, 0xc9, 0x95, - 0xfe, 0x28, 0x11, 0x2a, 0x67, 0xfe, 0x26, 0x13, 0x04, 0xb3, 0x2b, 0x90, 0x52, 0xfe, 0x78, 0x0d, - 0x0e, 0x65, 0x12, 0x66, 0x24, 0x0e, 0xb3, 0x0e, 0x90, 0x01, 0xbc, 0x20, 0x8b, 0x72, 0x16, 0x67, - 0x01, 0xf8, 0x82, 0x31, 0x5a, 0x3f, 0xfe, 0x04, 0x55, 0xfe, 0xa5, 0x55, 0xfe, 0x04, 0xfa, 0x31, - 0xfe, 0x05, 0xfa, 0x3f, 0xfe, 0x91, 0x10, 0x04, 0x44, 0x2b, 0x45, 0xfe, 0x40, 0x56, 0xfe, 0xe1, - 0x56, 0x0e, 0x44, 0x12, 0x45, 0xab, 0x82, 0x31, 0x5a, 0x3f, 0x93, 0xc9, 0x04, 0x63, 0x2b, 0x64, - 0xfe, 0x00, 0x56, 0xfe, 0xa1, 0x56, 0x0e, 0x63, 0x12, 0x64, 0x0a, 0x08, 0x5f, 0xfe, 0x1e, 0x12, - 0x2a, 0x67, 0xfe, 0x1f, 0x40, 0x04, 0x65, 0x2b, 0x66, 0xfe, 0x2c, 0x50, 0xfe, 0xae, 0x50, 0x04, - 0x44, 0x2b, 0x45, 0xfe, 0x34, 0x50, 0xfe, 0xb6, 0x50, 0x04, 0x63, 0x2b, 0x64, 0xfe, 0x08, 0x50, - 0xfe, 0x8a, 0x50, 0x04, 0x42, 0x2b, 0x43, 0xfe, 0x28, 0x50, 0xfe, 0xaa, 0x50, 0x02, 0x9c, 0x20, - 0x06, 0x16, 0xfa, 0x02, 0x7a, 0x3b, 0x01, 0x0c, 0x1f, 0x55, 0x23, 0x29, 0xba, 0x05, 0x06, 0x28, - 0x55, 0x3e, 0x05, 0xca, 0x28, 0x7a, 0x01, 0xf2, 0x1b, 0x58, 0x1a, 0x55, 0x0a, 0x08, 0x0b, 0xe4, - 0x36, 0x42, 0x21, 0x43, 0xfe, 0x0a, 0x55, 0x30, 0xfe, 0x8b, 0x55, 0x4d, 0x42, 0x6e, 0x43, 0xfe, - 0x0c, 0x51, 0xfe, 0x8e, 0x51, 0x02, 0x7a, 0xde, 0xfe, 0x0a, 0x45, 0xfe, 0x19, 0x41, 0x02, 0x7a, - 0x3b, 0x01, 0x0c, 0x1f, 0xc0, 0x23, 0x29, 0xfe, 0xe9, 0x09, 0x60, 0x18, 0xfe, 0x94, 0x12, 0x60, - 0x0b, 0x59, 0x02, 0x55, 0x2f, 0xb0, 0x19, 0x32, 0x1f, 0xc0, 0x23, 0x29, 0x98, 0x05, 0x18, 0x28, - 0x55, 0x01, 0x0c, 0x1f, 0xc0, 0x23, 0x29, 0xfe, 0xe8, 0x09, 0x50, 0x04, 0xfe, 0x9c, 0x00, 0x2c, - 0x30, 0xfe, 0xbb, 0x45, 0x60, 0x00, 0x4e, 0x37, 0x06, 0xa4, 0x58, 0xfe, 0xc0, 0x14, 0xfe, 0xf8, - 0x14, 0xb1, 0x3e, 0x05, 0xc8, 0xfe, 0x16, 0x13, 0x04, 0xfe, 0x9e, 0x00, 0x2c, 0xa9, 0x04, 0x56, - 0x2c, 0x30, 0x62, 0x02, 0x7a, 0xfe, 0xc0, 0x5d, 0xfe, 0xe4, 0x14, 0xfe, 0x03, 0x17, 0x04, 0x5b, - 0xc2, 0x0e, 0x5b, 0x62, 0x3b, 0x01, 0x0c, 0x26, 0x9c, 0x01, 0xfe, 0xd0, 0x14, 0x02, 0x9c, 0x2f, - 0xfe, 0xb4, 0x12, 0x19, 0x32, 0x1f, 0x55, 0x23, 0x29, 0x98, 0x05, 0x06, 0x28, 0x55, 0xfe, 0xf6, - 0x14, 0xfe, 0x42, 0x58, 0xfe, 0x70, 0x14, 0xfe, 0x92, 0x14, 0xb1, 0xfe, 0x4a, 0xf4, 0x0b, 0x1a, - 0x55, 0xfe, 0x4a, 0xf4, 0x06, 0xd8, 0x3e, 0x05, 0xc8, 0xd1, 0x02, 0x7a, 0x04, 0x56, 0xc2, 0x0e, - 0x56, 0x62, 0x3b, 0x01, 0x0c, 0x26, 0x9c, 0x01, 0xfe, 0xfe, 0x14, 0x02, 0x9c, 0x26, 0xe2, 0x76, - 0xf7, 0x76, 0x03, 0x35, 0xfe, 0x18, 0x13, 0x71, 0xfe, 0x18, 0x13, 0x62, 0x3b, 0x01, 0x0c, 0xfe, - 0xe3, 0x10, 0x07, 0x6a, 0xff, 0x02, 0x00, 0x57, 0x6c, 0x80, 0x1b, 0xfe, 0xff, 0x7f, 0xfe, 0x30, - 0x56, 0xfe, 0x00, 0x5c, 0x03, 0x07, 0x6a, 0xff, 0x02, 0x00, 0x57, 0x6c, 0x80, 0x1b, 0x58, 0xfe, - 0x30, 0x56, 0xfe, 0x00, 0x5c, 0x03, 0x07, 0x6a, 0xff, 0x02, 0x00, 0x57, 0x6c, 0x80, 0x03, 0x07, - 0x6a, 0xff, 0x02, 0x00, 0x57, 0x6c, 0x80, 0xfe, 0x0b, 0x58, 0x03, 0x0f, 0x5b, 0x01, 0x9f, 0x0f, - 0x56, 0x01, 0x9f, 0x03, 0xd0, 0x1b, 0x10, 0xff, 0x03, 0x00, 0x54, 0xfe, 0x00, 0xf4, 0x22, 0x6c, - 0xfe, 0x00, 0x7d, 0xfe, 0x01, 0x7d, 0xfe, 0x02, 0x7d, 0xfe, 0x03, 0x7c, 0x6b, 0x33, 0x0e, 0x63, - 0x12, 0x64, 0x4d, 0x44, 0x6e, 0x45, 0x03, 0xfe, 0x62, 0x18, 0xfe, 0x82, 0x5a, 0xfe, 0xe1, 0x1a, - 0xbe, 0xfe, 0x02, 0x58, 0x03, 0x01, 0xfe, 0x40, 0x19, 0xfe, 0x42, 0x48, 0x69, 0x50, 0x7c, 0x01, - 0x0c, 0x1f, 0xfe, 0xc8, 0x14, 0x23, 0x29, 0xfe, 0xe9, 0x09, 0xfe, 0xc1, 0x59, 0x01, 0x0c, 0x1f, - 0xfe, 0xc8, 0x14, 0x23, 0x29, 0xfe, 0xe8, 0x0a, 0x04, 0xfe, 0x9e, 0x00, 0x2c, 0xfe, 0xc2, 0x12, - 0x24, 0xb8, 0x1d, 0xe4, 0x60, 0xd6, 0x77, 0xfe, 0x18, 0x14, 0x59, 0x07, 0x06, 0x09, 0xd6, 0xa4, - 0xfe, 0x00, 0x10, 0xfe, 0x78, 0x10, 0xff, 0x02, 0x83, 0x55, 0xa8, 0xff, 0x02, 0x83, 0x55, 0xb8, - 0x18, 0xfe, 0x12, 0x13, 0x61, 0xfe, 0x30, 0x00, 0x95, 0xf3, 0x09, 0x88, 0x07, 0x06, 0xfe, 0x56, - 0x10, 0xb8, 0x0b, 0xfe, 0x16, 0x13, 0x61, 0xfe, 0x64, 0x00, 0x95, 0xf3, 0x0f, 0xfe, 0x64, 0x00, - 0x09, 0xae, 0x07, 0x06, 0xfe, 0x28, 0x10, 0xb8, 0x06, 0xfe, 0x5e, 0x13, 0x61, 0xfe, 0xc8, 0x00, - 0x95, 0xf3, 0x0f, 0xfe, 0xc8, 0x00, 0x09, 0x5d, 0x07, 0x06, 0xab, 0x61, 0xfe, 0x90, 0x01, 0x96, - 0xfe, 0x7e, 0x14, 0x7c, 0xad, 0xfe, 0x43, 0xf4, 0xb2, 0xfe, 0x56, 0xf0, 0xfe, 0x90, 0x14, 0xfe, - 0x04, 0xf4, 0x6a, 0xfe, 0x43, 0xf4, 0xae, 0xfe, 0xf3, 0x10, 0xb7, 0x01, 0xf1, 0x1b, 0x58, 0xda, - 0xfe, 0x00, 0x17, 0xfe, 0x4d, 0xe4, 0x8b, 0x96, 0xfe, 0xc2, 0x14, 0x7c, 0xfe, 0x14, 0x10, 0xfe, - 0x00, 0x17, 0xfe, 0x4d, 0xe4, 0xec, 0x96, 0xfe, 0xc2, 0x14, 0xd2, 0xec, 0xa2, 0x50, 0x7c, 0x07, - 0x06, 0xfe, 0xb4, 0x56, 0xfe, 0xc3, 0x58, 0x03, 0x50, 0x07, 0x0b, 0x03, 0x14, 0x06, 0x01, 0x0c, - 0x26, 0xfe, 0xfc, 0x14, 0x14, 0x0b, 0x01, 0x0c, 0x26, 0xfe, 0xfc, 0x14, 0x14, 0x18, 0x01, 0x0c, - 0x26, 0xfe, 0xfc, 0x14, 0x76, 0xfe, 0x89, 0x49, 0x01, 0x0c, 0x03, 0x14, 0x06, 0x01, 0x0c, 0x26, - 0xb4, 0x14, 0x18, 0x01, 0x0c, 0x26, 0xb4, 0x14, 0x06, 0x01, 0x0c, 0x26, 0xb4, 0xfe, 0x89, 0x49, - 0x01, 0x0c, 0x26, 0xb4, 0x76, 0xfe, 0x89, 0x4a, 0x01, 0x0c, 0x03, 0x50, 0x03, 0x2a, 0xe8, 0x05, - 0x06, 0xfe, 0x44, 0x13, 0xb5, 0x16, 0xe8, 0xfe, 0x49, 0xf4, 0x00, 0x59, 0x76, 0xce, 0x62, 0xfe, - 0x01, 0xec, 0xfe, 0x27, 0x01, 0xf7, 0x01, 0x0c, 0x3e, 0x05, 0xfe, 0xe3, 0x00, 0xfe, 0x20, 0x13, - 0x1f, 0xfe, 0x80, 0x15, 0x24, 0x16, 0xfa, 0x01, 0x41, 0x2a, 0xfa, 0x05, 0x06, 0x4e, 0x0a, 0x4f, - 0x06, 0x3a, 0x03, 0x0e, 0x5c, 0x12, 0x8e, 0xfe, 0x43, 0x58, 0x01, 0x15, 0x05, 0x10, 0xfe, 0x1e, - 0x12, 0x49, 0xee, 0x94, 0x01, 0x47, 0xfe, 0x90, 0x4d, 0xe6, 0x10, 0xfe, 0xc5, 0x59, 0x01, 0x47, - 0xfe, 0x8d, 0x56, 0xbe, 0x49, 0x03, 0x49, 0x21, 0x8e, 0x01, 0x15, 0x49, 0x94, 0x01, 0x47, 0xe9, - 0x10, 0xe6, 0x10, 0x21, 0x5c, 0x61, 0x1e, 0x87, 0x0f, 0x5e, 0x01, 0xc5, 0x03, 0x0e, 0x5c, 0x12, - 0x8e, 0xfe, 0xc3, 0x58, 0x01, 0x15, 0x05, 0x10, 0xfe, 0x1a, 0x12, 0x49, 0xee, 0x94, 0x01, 0x47, - 0xe9, 0x10, 0xfe, 0x80, 0x4d, 0xfe, 0xc5, 0x59, 0x01, 0x47, 0x49, 0x03, 0x49, 0x21, 0x5c, 0x01, - 0x15, 0x49, 0x94, 0x01, 0x47, 0xe9, 0x10, 0xe6, 0x10, 0x21, 0x5c, 0x61, 0x1e, 0x87, 0x0f, 0x5e, - 0x01, 0xc5, 0x03, 0x0e, 0x5c, 0x12, 0x8e, 0xfe, 0x43, 0x58, 0x01, 0x15, 0xfe, 0x42, 0x48, 0x94, - 0x01, 0x47, 0xfe, 0xc0, 0x5a, 0xb7, 0xfe, 0x00, 0xcd, 0xfe, 0x01, 0xcc, 0xfe, 0x4a, 0x46, 0xe4, - 0x9a, 0x7f, 0x05, 0x10, 0xfe, 0x2e, 0x13, 0x5a, 0x5c, 0xfe, 0x4d, 0xf4, 0x1e, 0xe2, 0x0f, 0x5e, - 0x01, 0x9f, 0xad, 0xfe, 0x40, 0x4c, 0xfe, 0xc5, 0x58, 0x01, 0x47, 0xfe, 0x00, 0x07, 0x7f, 0x05, - 0x10, 0x87, 0x5a, 0x8e, 0xfe, 0x05, 0x57, 0xfe, 0x08, 0x10, 0xfe, 0x45, 0x58, 0x01, 0x47, 0xfe, - 0x8d, 0x56, 0xbe, 0xfe, 0x80, 0x4c, 0xfe, 0x05, 0x17, 0x03, 0x09, 0x10, 0x75, 0x6d, 0xfe, 0x60, - 0x01, 0xfe, 0x18, 0xdf, 0xfe, 0x19, 0xde, 0xfe, 0x24, 0x1c, 0xe3, 0x39, 0x9e, 0xfe, 0xc4, 0x16, - 0x01, 0xfe, 0xca, 0x17, 0xd9, 0x8a, 0x39, 0x6d, 0xfe, 0x2c, 0x01, 0xfe, 0x2f, 0x19, 0x03, 0xbf, - 0x28, 0xfe, 0xb4, 0x16, 0xfe, 0xda, 0x10, 0x09, 0x10, 0x75, 0x04, 0xfe, 0x64, 0x01, 0xfe, 0x00, - 0xf4, 0x22, 0xfe, 0x18, 0x58, 0x04, 0xfe, 0x66, 0x01, 0xfe, 0x19, 0x58, 0x8a, 0x22, 0xfe, 0x3c, - 0x90, 0xfe, 0x30, 0xf4, 0x06, 0xfe, 0x3c, 0x50, 0x6d, 0xfe, 0x38, 0x00, 0xfe, 0x0f, 0x79, 0xfe, - 0x1c, 0xf7, 0x22, 0x9e, 0xfe, 0x0e, 0x17, 0xfe, 0xb6, 0x14, 0x30, 0x03, 0xbf, 0x28, 0xfe, 0xe6, - 0x16, 0xfe, 0x9c, 0x10, 0x09, 0x10, 0x75, 0xbe, 0xfe, 0x18, 0xdf, 0xfe, 0x19, 0xdf, 0xe3, 0x31, - 0x9e, 0xfe, 0x30, 0x17, 0xfe, 0x94, 0x14, 0x2e, 0x8a, 0x31, 0x6d, 0x1d, 0xfe, 0xaf, 0x19, 0xfe, - 0x98, 0xe7, 0x00, 0x03, 0xbf, 0x28, 0xfe, 0x24, 0x17, 0xfe, 0x6c, 0x10, 0x09, 0x10, 0x75, 0xfe, - 0x30, 0xbc, 0xfe, 0xb2, 0xbc, 0x8a, 0xe0, 0x6d, 0x1d, 0xfe, 0x0f, 0x79, 0xfe, 0x1c, 0xf7, 0xe0, - 0x9e, 0xfe, 0x68, 0x17, 0xfe, 0x5c, 0x14, 0x30, 0x03, 0xbf, 0x28, 0xfe, 0x54, 0x17, 0xfe, 0x42, - 0x10, 0xfe, 0x02, 0xf6, 0x10, 0x75, 0xfe, 0x18, 0xfe, 0x65, 0xfe, 0x19, 0xfe, 0x66, 0xd0, 0xe3, - 0x78, 0x9e, 0xfe, 0x8e, 0x17, 0xfe, 0x36, 0x14, 0xe2, 0x8a, 0x78, 0x46, 0xfe, 0x83, 0x58, 0xfe, - 0xaf, 0x19, 0xfe, 0x80, 0xe7, 0x10, 0xfe, 0x81, 0xe7, 0x10, 0x11, 0xfe, 0xdd, 0x00, 0x6b, 0x33, - 0x03, 0x6b, 0x33, 0xfe, 0x12, 0x45, 0x28, 0xfe, 0x7e, 0x17, 0x17, 0x06, 0x4b, 0xfb, 0xe5, 0x02, - 0x27, 0xfe, 0x39, 0xf0, 0xfe, 0xd2, 0x17, 0x24, 0x03, 0xfe, 0x7e, 0x18, 0x1b, 0x18, 0x85, 0x07, - 0x0d, 0x03, 0x75, 0x04, 0xe7, 0x1b, 0x06, 0xfe, 0xef, 0x12, 0xfe, 0xe1, 0x10, 0x1c, 0x0f, 0x1e, - 0x01, 0x15, 0x05, 0x10, 0x4e, 0x4c, 0xfe, 0x78, 0x14, 0xfe, 0x34, 0x12, 0x58, 0x89, 0x37, 0x39, - 0xc6, 0xfe, 0xe9, 0x13, 0x1c, 0x0f, 0x3d, 0x01, 0x15, 0x05, 0x10, 0x4e, 0x4c, 0xfe, 0x56, 0x14, - 0xb0, 0x58, 0x89, 0x37, 0x39, 0xc6, 0xfe, 0xe9, 0x13, 0x09, 0x0b, 0x03, 0xfe, 0x9c, 0xe7, 0x0b, - 0x13, 0xfe, 0x15, 0x00, 0x97, 0xa3, 0x33, 0x01, 0xfe, 0xf8, 0x0e, 0x09, 0x06, 0x03, 0x0a, 0x4f, - 0x39, 0x3a, 0x07, 0x3d, 0x09, 0xa1, 0x01, 0x40, 0x11, 0x48, 0x07, 0x1e, 0x09, 0x51, 0x01, 0x79, - 0x09, 0x06, 0x03, 0xfe, 0x38, 0x90, 0xfe, 0xba, 0x90, 0x36, 0xfe, 0xa8, 0x00, 0x21, 0x7b, 0xfe, - 0x48, 0x55, 0x30, 0xfe, 0xc9, 0x55, 0x03, 0x2a, 0xc4, 0x72, 0x16, 0xc4, 0x03, 0x0f, 0xc7, 0x01, - 0x15, 0xed, 0x0f, 0x7e, 0x01, 0x15, 0xfe, 0x49, 0x44, 0x28, 0xfe, 0xc8, 0x18, 0x0f, 0x1e, 0x01, - 0x15, 0x05, 0x10, 0x4e, 0x0f, 0x5e, 0x01, 0xc5, 0x0f, 0x7e, 0x01, 0x15, 0x72, 0x7f, 0x03, 0xfe, - 0x40, 0x5e, 0xfe, 0xe2, 0x08, 0xfe, 0xc0, 0x4c, 0x2a, 0x3c, 0x05, 0x10, 0xfe, 0x52, 0x12, 0x4c, - 0x05, 0x00, 0xfe, 0x18, 0x12, 0xfe, 0xe1, 0x18, 0xfe, 0x19, 0xf4, 0xfe, 0x7f, 0x00, 0x2e, 0xfe, - 0xe2, 0x08, 0x72, 0x4c, 0x3e, 0x05, 0x7b, 0xa7, 0xfe, 0x82, 0x48, 0xfe, 0x01, 0x80, 0xfe, 0xd7, - 0x10, 0xfe, 0xc4, 0x48, 0x07, 0x2d, 0x09, 0x3c, 0xfe, 0x40, 0x5f, 0x1c, 0x01, 0x40, 0x11, 0xfe, - 0xdd, 0x00, 0xfe, 0x14, 0x46, 0x07, 0x2d, 0x09, 0x3c, 0x01, 0x40, 0x11, 0xfe, 0xdd, 0x00, 0xfe, - 0x40, 0x4a, 0x70, 0xfe, 0x06, 0x17, 0xfe, 0x01, 0x07, 0xfe, 0x82, 0x48, 0xfe, 0x04, 0x17, 0x03, - 0xf0, 0x18, 0x77, 0xfe, 0x50, 0x19, 0x04, 0xfe, 0x90, 0x00, 0xfe, 0x3a, 0x45, 0xfe, 0x2c, 0x10, - 0xf0, 0xd5, 0x77, 0xfe, 0x62, 0x19, 0x04, 0xfe, 0x92, 0x00, 0xcf, 0x1d, 0xdf, 0xf0, 0xfe, 0x0b, - 0x00, 0x77, 0xfe, 0x74, 0x19, 0x04, 0xfe, 0x94, 0x00, 0xcf, 0x22, 0xfe, 0x08, 0x10, 0x04, 0xfe, - 0x96, 0x00, 0xcf, 0x88, 0xfe, 0x4e, 0x45, 0xd8, 0xfe, 0x0a, 0x45, 0xff, 0x04, 0x68, 0x54, 0xfe, - 0xf1, 0x10, 0x1b, 0x8b, 0xfe, 0x08, 0x1c, 0xfe, 0x67, 0x19, 0xfe, 0x0a, 0x1c, 0xfe, 0x1a, 0xf4, - 0xfe, 0x00, 0x04, 0xd8, 0xfe, 0x48, 0xf4, 0x18, 0x96, 0xfe, 0xa8, 0x19, 0x07, 0x18, 0x03, 0x05, - 0xa5, 0xfe, 0x5a, 0xf0, 0xfe, 0xb8, 0x19, 0x20, 0xfe, 0x09, 0x00, 0xfe, 0x34, 0x10, 0x05, 0x1d, - 0xfe, 0x5a, 0xf0, 0xfe, 0xc6, 0x19, 0x20, 0xd6, 0xfe, 0x26, 0x10, 0x05, 0x18, 0x85, 0x20, 0x88, - 0xdf, 0x05, 0x0b, 0x85, 0x20, 0xae, 0xfe, 0x0e, 0x10, 0x05, 0x06, 0x85, 0x20, 0x5d, 0xce, 0xb5, - 0x03, 0x17, 0xfe, 0x09, 0x00, 0x01, 0x2e, 0x2f, 0xfe, 0xf6, 0x19, 0x04, 0x74, 0xb7, 0x03, 0x19, - 0xfe, 0x16, 0x1a, 0xfe, 0x14, 0xf0, 0x0c, 0x2f, 0xfe, 0x0a, 0x1a, 0x19, 0xfe, 0x16, 0x1a, 0xfe, - 0x82, 0xf0, 0xfe, 0x0e, 0x1a, 0x03, 0xff, 0x34, 0x00, 0x00,}; + 0x00, 0x02, 0xfe, 0x0a, 0x0c, 0x6a, 0xfe, 0x9a, 0x81, 0x6f, 0x8f, 0xfe, 0x09, 0x6f, 0xfe, 0x93, + 0x45, 0x19, 0xfe, 0x88, 0x07, 0x2f, 0xfe, 0x60, 0x07, 0x1b, 0x32, 0xd7, 0xfe, 0x58, 0x07, 0x73, + 0x97, 0x85, 0xfe, 0x78, 0x07, 0x02, 0x26, 0x01, 0x55, 0x02, 0xfe, 0xbe, 0x06, 0x14, 0x22, 0x02, + 0xfe, 0xbe, 0x06, 0xfe, 0x9c, 0xf7, 0xfe, 0xf0, 0x07, 0xfe, 0x2c, 0x90, 0xfe, 0xae, 0x90, 0x53, + 0xfe, 0xd6, 0x07, 0x0d, 0x66, 0x12, 0x67, 0x0a, 0x41, 0x60, 0x39, 0x01, 0xfe, 0x14, 0x19, 0x05, + 0x10, 0x87, 0xfe, 0x83, 0xe7, 0xfe, 0x95, 0x00, 0x8a, 0xfe, 0x03, 0x40, 0x0a, 0x41, 0x46, 0x39, + 0x01, 0xc5, 0xb6, 0xfe, 0x1f, 0x40, 0x16, 0x68, 0x01, 0xfe, 0xbe, 0x13, 0xfe, 0x08, 0x50, 0xfe, + 0x8a, 0x50, 0xfe, 0x34, 0x51, 0xfe, 0xb6, 0x51, 0xfe, 0x08, 0x90, 0xfe, 0x8a, 0x90, 0x0d, 0x64, + 0x12, 0x65, 0xda, 0xfa, 0x0d, 0x3c, 0x12, 0x3d, 0xfe, 0x60, 0x10, 0x0a, 0x07, 0x60, 0xe9, 0xfe, + 0x2c, 0x90, 0xfe, 0xae, 0x90, 0x0d, 0x66, 0x12, 0x67, 0x0a, 0x07, 0x46, 0xd1, 0x01, 0xc5, 0xfe, + 0x1f, 0x80, 0x16, 0x68, 0xfe, 0x34, 0x90, 0xfe, 0xb6, 0x90, 0x0d, 0x43, 0x12, 0x44, 0xfe, 0x08, + 0x90, 0xfe, 0x8a, 0x90, 0x0d, 0x64, 0x12, 0x65, 0xa7, 0x07, 0x46, 0xdb, 0xda, 0xfa, 0x0d, 0x3c, + 0x12, 0x3d, 0xad, 0xfe, 0x28, 0x90, 0xfe, 0xaa, 0x90, 0x0d, 0x3c, 0x12, 0x3d, 0x0d, 0x30, 0x12, + 0x42, 0x2b, 0x0d, 0x54, 0x0d, 0x69, 0x0a, 0x41, 0x22, 0x39, 0x2e, 0x08, 0x84, 0x2f, 0xfe, 0x70, + 0x08, 0xfe, 0x9e, 0xf0, 0xfe, 0x84, 0x08, 0xa3, 0x19, 0x32, 0x2e, 0x5b, 0xfe, 0xed, 0x10, 0xac, + 0xfe, 0xa8, 0x08, 0xae, 0xfe, 0xc4, 0x08, 0x85, 0xfe, 0x9c, 0x08, 0xd3, 0xfe, 0xa2, 0x08, 0x73, + 0x97, 0x02, 0x26, 0x01, 0x55, 0xfe, 0xc9, 0x10, 0x14, 0x22, 0xfe, 0xc9, 0x10, 0x71, 0x07, 0x06, + 0xfe, 0x10, 0x12, 0x71, 0x07, 0x0b, 0x50, 0x0a, 0x07, 0x0b, 0xfe, 0xa6, 0x12, 0xfe, 0x2e, 0x1c, + 0xb0, 0x71, 0x07, 0x06, 0x50, 0x71, 0x07, 0x0b, 0xfe, 0x92, 0x12, 0xfe, 0x2c, 0x1c, 0xa7, 0x07, + 0x46, 0xaf, 0xa7, 0x41, 0x46, 0xfe, 0x05, 0x40, 0xda, 0xfa, 0xfe, 0x28, 0x50, 0xfe, 0xaa, 0x50, + 0xfe, 0xaa, 0xf0, 0xfe, 0xf6, 0x09, 0xfe, 0xac, 0xf0, 0xfe, 0x24, 0x09, 0x02, 0xfe, 0x02, 0x0a, + 0xfe, 0xb7, 0xf0, 0xfe, 0x20, 0x09, 0xfe, 0x02, 0xf6, 0x1d, 0x6a, 0xfe, 0x70, 0x18, 0xfe, 0xf1, + 0x18, 0xfe, 0x40, 0x55, 0xfe, 0xe1, 0x55, 0xfe, 0x10, 0x58, 0xfe, 0x91, 0x58, 0xfe, 0x14, 0x59, + 0xfe, 0x95, 0x59, 0x1b, 0x9b, 0xfe, 0x8c, 0xf0, 0xfe, 0x20, 0x09, 0xfe, 0xac, 0xf0, 0xfe, 0x14, + 0x09, 0xed, 0xfe, 0xcb, 0x10, 0xfe, 0xad, 0xf0, 0xfe, 0x30, 0x09, 0x02, 0xfe, 0x3c, 0x0b, 0xee, + 0xfe, 0xbf, 0x10, 0xfe, 0x2b, 0xf0, 0x9b, 0xfe, 0x6b, 0x18, 0x1a, 0xfe, 0x00, 0xfe, 0xe2, 0xcd, + 0xfe, 0xd2, 0xf0, 0x9b, 0xfe, 0x76, 0x18, 0x1a, 0x18, 0x19, 0x9b, 0x04, 0xe7, 0x1a, 0x06, 0x19, + 0x9b, 0xac, 0x58, 0xae, 0x58, 0xed, 0xee, 0xfe, 0x89, 0x10, 0x92, 0x63, 0x3a, 0x17, 0xa9, 0x01, + 0x3b, 0x13, 0xfe, 0x35, 0x00, 0x34, 0x6b, 0x13, 0x93, 0x02, 0x6b, 0xfb, 0xb2, 0x0b, 0xfe, 0x1a, + 0x12, 0x51, 0xfe, 0x19, 0x82, 0xfe, 0x6c, 0x18, 0xfe, 0x44, 0x54, 0xf0, 0xdf, 0xfe, 0x74, 0x18, + 0x94, 0x95, 0x19, 0xfe, 0xf2, 0x08, 0x02, 0x58, 0x0a, 0x07, 0x60, 0xaf, 0x04, 0x30, 0x2a, 0x42, + 0x0d, 0x43, 0x12, 0x44, 0x83, 0x30, 0x5a, 0x42, 0xfe, 0x6c, 0x18, 0xfe, 0xed, 0x18, 0xfe, 0x44, + 0x54, 0xfe, 0xe5, 0x54, 0x36, 0x43, 0x21, 0x44, 0x04, 0x54, 0x2a, 0x69, 0x94, 0xfe, 0xe3, 0x54, + 0xfe, 0x74, 0x18, 0xfe, 0xf5, 0x18, 0x94, 0xfe, 0xe3, 0x54, 0x95, 0xca, 0x53, 0xfe, 0xf2, 0x08, + 0x02, 0x58, 0xfe, 0x37, 0xf0, 0xfe, 0xfe, 0x09, 0xfe, 0x8b, 0xf0, 0xfe, 0x84, 0x09, 0x02, 0x58, + 0xfb, 0xb2, 0x0b, 0x28, 0xfe, 0x1e, 0x0b, 0x36, 0x54, 0x21, 0x69, 0x53, 0x7a, 0x08, 0xfe, 0xc0, + 0x07, 0x47, 0x62, 0x00, 0xd9, 0xfe, 0x01, 0x59, 0xfe, 0x52, 0xf0, 0xfe, 0x30, 0x0a, 0x94, 0x99, + 0xfe, 0x48, 0x0a, 0x36, 0x54, 0x94, 0xfe, 0xe3, 0x54, 0x4e, 0x54, 0x70, 0x69, 0xfe, 0x14, 0x58, + 0xfe, 0x95, 0x58, 0x02, 0x58, 0x36, 0x54, 0x21, 0x69, 0xfe, 0x14, 0x59, 0xfe, 0x95, 0x59, 0xf0, + 0x4e, 0x54, 0x4e, 0x69, 0x02, 0x58, 0x0a, 0x07, 0x60, 0xfe, 0x82, 0x12, 0x0a, 0x07, 0x22, 0xfe, + 0x66, 0x13, 0x29, 0x68, 0x72, 0xd0, 0xfe, 0x83, 0x80, 0xfe, 0xc8, 0x44, 0xfe, 0x2e, 0x13, 0xfe, + 0x04, 0x91, 0xfe, 0x86, 0x91, 0x6d, 0x31, 0xfe, 0x40, 0x59, 0xfe, 0xc1, 0x59, 0x53, 0xfe, 0xfa, + 0x08, 0x04, 0x66, 0x2a, 0x67, 0x0d, 0xb5, 0x12, 0x93, 0x4e, 0x66, 0x70, 0x67, 0x01, 0xc5, 0xb6, + 0x6d, 0x31, 0x16, 0x68, 0x83, 0x30, 0x5a, 0x42, 0x36, 0x43, 0x21, 0x44, 0x95, 0xca, 0xfe, 0x04, + 0xfa, 0x30, 0xfe, 0x05, 0xfa, 0x42, 0x01, 0xfe, 0xbe, 0x13, 0xfe, 0x36, 0x10, 0x2b, 0x0d, 0xb5, + 0x0d, 0x93, 0x36, 0x43, 0x21, 0x44, 0xb0, 0x0a, 0x07, 0x22, 0x19, 0xfe, 0xfa, 0x08, 0x36, 0x3c, + 0x21, 0x3d, 0x0a, 0x07, 0xfe, 0xf7, 0x00, 0x39, 0x04, 0x64, 0x2a, 0x65, 0xfe, 0x10, 0x58, 0xfe, + 0x91, 0x58, 0x4e, 0x54, 0x70, 0x69, 0x02, 0xfe, 0x18, 0x0a, 0x0a, 0x07, 0x22, 0x19, 0xfe, 0xfa, + 0x08, 0x0a, 0x07, 0xfe, 0xf7, 0x00, 0x39, 0xf0, 0xdf, 0x6a, 0xfe, 0x10, 0x90, 0xfe, 0x92, 0x90, + 0xfe, 0xd3, 0x10, 0x40, 0x05, 0xcb, 0x19, 0xfe, 0x2c, 0x09, 0x11, 0xcb, 0xfb, 0xb2, 0x0b, 0xfe, + 0x14, 0x13, 0x04, 0x3c, 0x2a, 0x3d, 0x53, 0xfe, 0x2c, 0x09, 0xfe, 0x0c, 0x58, 0xfe, 0x8d, 0x58, + 0x02, 0x58, 0x2b, 0x47, 0xfe, 0x19, 0x80, 0xfe, 0xf1, 0x10, 0x0a, 0x07, 0x0b, 0xab, 0xfe, 0x6c, + 0x19, 0xfe, 0x19, 0x41, 0xfe, 0x8e, 0x10, 0xfe, 0x6c, 0x19, 0x4e, 0x3c, 0xfe, 0xed, 0x19, 0x70, + 0x3d, 0xfe, 0x0c, 0x51, 0xfe, 0x8e, 0x51, 0xfe, 0x6b, 0x18, 0x1a, 0xfe, 0x00, 0xff, 0x35, 0xfe, + 0x74, 0x10, 0xcd, 0xfe, 0xd2, 0xf0, 0xfe, 0xb6, 0x0b, 0xfe, 0x76, 0x18, 0x1a, 0x18, 0xd6, 0x04, + 0xe7, 0x1a, 0x06, 0x89, 0x13, 0xfe, 0x16, 0x00, 0x02, 0x6b, 0xfe, 0xd1, 0xf0, 0xfe, 0xc8, 0x0b, + 0x17, 0x84, 0x01, 0x3b, 0x13, 0xfe, 0x17, 0x00, 0xfe, 0x42, 0x10, 0xfe, 0xce, 0xf0, 0xfe, 0xce, + 0x0b, 0xfe, 0x3c, 0x10, 0xfe, 0xcd, 0xf0, 0xfe, 0xda, 0x0b, 0x13, 0xfe, 0x22, 0x00, 0x02, 0x6b, + 0xfe, 0xcb, 0xf0, 0xfe, 0xe6, 0x0b, 0x13, 0xfe, 0x24, 0x00, 0x02, 0x6b, 0xfe, 0xd0, 0xf0, 0xfe, + 0xf0, 0x0b, 0x13, 0xb1, 0xe0, 0xfe, 0xcf, 0xf0, 0xfe, 0xfa, 0x0b, 0x13, 0x8f, 0xdd, 0xfe, 0xcc, + 0xf0, 0xfe, 0x0a, 0x0c, 0xfe, 0x84, 0x80, 0xb2, 0x22, 0x4f, 0x13, 0xfe, 0x12, 0x00, 0x2e, 0x08, + 0x84, 0x2f, 0xfe, 0x10, 0x0c, 0xfe, 0x9e, 0xf0, 0xfe, 0x24, 0x0c, 0xa3, 0x19, 0x32, 0x2e, 0x5b, + 0xfe, 0xed, 0x10, 0xac, 0x26, 0xae, 0x26, 0x2e, 0xfe, 0x9c, 0x32, 0x2f, 0xfe, 0x30, 0x0c, 0x1b, + 0x32, 0x85, 0xfe, 0x4c, 0x0c, 0x73, 0x97, 0xac, 0xfe, 0xf0, 0x07, 0xae, 0xfe, 0xf0, 0x07, 0x02, + 0x26, 0x01, 0x55, 0xfe, 0xdb, 0x10, 0x11, 0xfe, 0xe8, 0x00, 0xed, 0xee, 0x92, 0x86, 0x76, 0xfe, + 0x89, 0xf0, 0x26, 0x24, 0x23, 0xfe, 0xe9, 0x09, 0x01, 0x0c, 0x86, 0x76, 0x1f, 0x26, 0x24, 0x23, + 0x9a, 0x34, 0xfe, 0x88, 0x0c, 0x1b, 0x32, 0x02, 0xfe, 0x7c, 0x0c, 0xa3, 0x50, 0x13, 0xfe, 0x42, + 0x00, 0x02, 0x6b, 0xa6, 0x06, 0xfe, 0x81, 0x49, 0xfe, 0xcc, 0x12, 0x0a, 0x07, 0x0b, 0xfe, 0x5a, + 0x13, 0x13, 0x00, 0x61, 0x0b, 0xfe, 0x6a, 0x12, 0x61, 0xfe, 0x28, 0x00, 0x28, 0xfe, 0xce, 0x0d, + 0x0f, 0x7d, 0x01, 0x15, 0x05, 0x00, 0x89, 0x37, 0xfe, 0x28, 0x00, 0x02, 0xfe, 0xce, 0x0d, 0x01, + 0x9f, 0x01, 0xa1, 0x0f, 0xc8, 0x01, 0xfe, 0x24, 0x0f, 0xb9, 0x08, 0x3f, 0x09, 0xa2, 0x01, 0x45, + 0x11, 0x48, 0x08, 0x1e, 0x09, 0x52, 0x01, 0x7e, 0x02, 0x27, 0x13, 0xfe, 0x44, 0x00, 0x61, 0x0b, + 0xab, 0x37, 0x0b, 0xfe, 0xc0, 0x10, 0x01, 0xc2, 0x37, 0x0b, 0xfe, 0xb6, 0x10, 0x01, 0xc2, 0xfe, + 0x19, 0x82, 0xfe, 0x34, 0x46, 0xfe, 0x0a, 0x13, 0x37, 0x0b, 0x13, 0xfe, 0x43, 0x00, 0xfe, 0xa2, + 0x10, 0x0a, 0x41, 0x0b, 0x39, 0x01, 0x9f, 0x01, 0xa1, 0xb9, 0x08, 0x3f, 0x09, 0xa2, 0x01, 0x45, + 0x11, 0x48, 0x08, 0x1e, 0x09, 0x52, 0x01, 0x7e, 0x88, 0x0b, 0xb9, 0x1c, 0xd2, 0x02, 0xfe, 0x4c, + 0x03, 0x0a, 0x07, 0x0b, 0xd6, 0x37, 0x0b, 0x13, 0x00, 0xfe, 0x54, 0x10, 0x71, 0x07, 0x1d, 0xfe, + 0x50, 0x12, 0x0a, 0x07, 0x1d, 0xfe, 0x48, 0x13, 0xfe, 0x1c, 0x1c, 0xfe, 0x9d, 0xf0, 0xfe, 0x8c, + 0x0d, 0xfe, 0x1c, 0x1c, 0xfe, 0x9d, 0xf0, 0xfe, 0x92, 0x0d, 0x0a, 0x41, 0x1d, 0x39, 0xfe, 0x95, + 0x10, 0x13, 0xfe, 0x15, 0x00, 0xfe, 0x04, 0xe6, 0x0b, 0x6a, 0xfe, 0x26, 0x10, 0x13, 0xfe, 0x13, + 0x00, 0xdd, 0x13, 0xfe, 0x47, 0x00, 0x8a, 0x13, 0xfe, 0x41, 0x00, 0xa4, 0x13, 0xfe, 0x24, 0x00, + 0x04, 0x7c, 0x2c, 0x28, 0xf6, 0x6a, 0xfe, 0x04, 0xe6, 0x1d, 0xfe, 0x9d, 0x41, 0xfe, 0x1c, 0x42, + 0xb9, 0x01, 0xea, 0x02, 0x27, 0xde, 0x17, 0x0b, 0x4c, 0xfe, 0x9b, 0x00, 0xe5, 0x17, 0xfe, 0x31, + 0x00, 0x4c, 0xc4, 0x01, 0xfe, 0x30, 0x10, 0x02, 0xfe, 0xc6, 0x01, 0x1c, 0xfe, 0x06, 0xec, 0xfe, + 0xb9, 0x00, 0x8c, 0x37, 0x38, 0xc7, 0x35, 0x1c, 0xfe, 0x06, 0xea, 0xfe, 0xb9, 0x00, 0xfe, 0x47, + 0x4b, 0x9e, 0xfe, 0x75, 0x57, 0x04, 0x5f, 0xfe, 0x98, 0x56, 0xfe, 0x28, 0x12, 0x0f, 0x7d, 0xfe, + 0xf4, 0x14, 0x47, 0xf2, 0x0f, 0xc8, 0xfe, 0xea, 0x14, 0xfe, 0x49, 0x54, 0x98, 0xfe, 0x42, 0x0e, + 0x0f, 0x1e, 0xfe, 0xde, 0x14, 0xfe, 0x44, 0x48, 0x02, 0xfe, 0x4c, 0x03, 0x0f, 0x5f, 0xfe, 0xc8, + 0x14, 0x8c, 0x37, 0x38, 0xc7, 0x35, 0x1c, 0xfe, 0xce, 0x47, 0xfe, 0xbd, 0x13, 0x02, 0x27, 0x29, + 0x2d, 0x05, 0x10, 0xfe, 0x78, 0x12, 0x2b, 0x16, 0x5e, 0x16, 0xb4, 0x29, 0x48, 0x47, 0x4c, 0x48, + 0xa3, 0xd9, 0xfe, 0xbc, 0xf0, 0xfe, 0xde, 0x0e, 0x08, 0x06, 0x16, 0x5e, 0x01, 0xfe, 0xe6, 0x16, + 0x04, 0xfe, 0x38, 0x01, 0x2a, 0xfe, 0x3a, 0x01, 0x53, 0xfe, 0xe2, 0x0e, 0x04, 0xfe, 0x38, 0x01, + 0x1a, 0xfe, 0xf0, 0xff, 0x0d, 0xfe, 0x60, 0x01, 0x04, 0xfe, 0x3a, 0x01, 0x0d, 0xfe, 0x62, 0x01, + 0x20, 0x06, 0x16, 0x48, 0xfe, 0x04, 0xec, 0x2d, 0x08, 0x2d, 0x09, 0x3e, 0x1c, 0x01, 0x45, 0x82, + 0xfe, 0x05, 0xf6, 0xfe, 0x34, 0x01, 0x01, 0xfe, 0x56, 0x17, 0x11, 0x48, 0xd2, 0x08, 0x06, 0x03, + 0x2b, 0x03, 0x29, 0x5e, 0xfe, 0xf7, 0x12, 0x29, 0xb4, 0x72, 0x16, 0xb4, 0x05, 0x84, 0xfe, 0x93, + 0x13, 0xfe, 0x24, 0x1c, 0x17, 0x18, 0x4c, 0xfe, 0x9b, 0x00, 0xe5, 0xfe, 0xd9, 0x10, 0x9c, 0xfe, + 0x03, 0xdc, 0xfe, 0x73, 0x57, 0xfe, 0x80, 0x5d, 0x03, 0x9c, 0xfe, 0x03, 0xdc, 0xfe, 0x5b, 0x57, + 0xfe, 0x80, 0x5d, 0x03, 0xfe, 0x03, 0x57, 0x9c, 0x2b, 0xfe, 0x00, 0xcc, 0x03, 0xfe, 0x03, 0x57, + 0x9c, 0x80, 0x03, 0x01, 0xfe, 0x8e, 0x17, 0x40, 0x05, 0x48, 0xfe, 0x0a, 0x13, 0x08, 0x1e, 0x09, + 0x52, 0xdd, 0x01, 0x9f, 0x01, 0xa1, 0x08, 0x3f, 0x09, 0xa2, 0x01, 0x45, 0x11, 0xfe, 0xe9, 0x00, + 0x0a, 0x07, 0x8f, 0xfe, 0x52, 0x13, 0x01, 0xfe, 0x18, 0x17, 0xfe, 0x1e, 0x1c, 0xfe, 0x14, 0x90, + 0x0d, 0xfe, 0x64, 0x01, 0xfe, 0x16, 0x90, 0x0d, 0xfe, 0x66, 0x01, 0x0a, 0x07, 0x46, 0xef, 0xfe, + 0x03, 0x80, 0x5b, 0x4d, 0x11, 0x7b, 0x08, 0x2d, 0x09, 0x3e, 0x1c, 0x7a, 0x01, 0x90, 0xfe, 0x62, + 0x08, 0x72, 0x4d, 0x11, 0x7b, 0x08, 0x2d, 0x09, 0x3e, 0x1c, 0x7a, 0x01, 0x90, 0x6d, 0x31, 0x11, + 0x7b, 0x08, 0x2d, 0x09, 0x3e, 0x1c, 0x7a, 0x01, 0x7e, 0x03, 0xfe, 0x08, 0x1c, 0x04, 0xfe, 0xac, + 0x00, 0xfe, 0x06, 0x58, 0x04, 0xfe, 0xae, 0x00, 0xfe, 0x07, 0x58, 0x04, 0xfe, 0xb0, 0x00, 0xfe, + 0x08, 0x58, 0x04, 0xfe, 0xb2, 0x00, 0xfe, 0x09, 0x58, 0xfe, 0x0a, 0x1c, 0x20, 0x74, 0x16, 0xfe, + 0xb9, 0x00, 0x2b, 0x0d, 0x5c, 0x0d, 0x56, 0x20, 0x10, 0x16, 0x2d, 0x16, 0x3e, 0x51, 0xa6, 0xfe, + 0x93, 0x00, 0x08, 0x2d, 0x09, 0x3e, 0x1c, 0x01, 0x7e, 0x82, 0x11, 0x7b, 0xfe, 0x14, 0x56, 0xfe, + 0xd6, 0xf0, 0x8a, 0xde, 0x92, 0xfe, 0x14, 0x1c, 0xfe, 0x10, 0x1c, 0xfe, 0x18, 0x1c, 0x03, 0x1c, + 0xfe, 0x0c, 0x14, 0x8c, 0xfe, 0x07, 0xe6, 0x38, 0xfe, 0xce, 0x47, 0xfe, 0xf5, 0x13, 0x03, 0x01, + 0xc2, 0x0f, 0x3f, 0x01, 0x15, 0x05, 0x10, 0xdb, 0x0f, 0x1e, 0x01, 0x15, 0x05, 0x10, 0xe2, 0xfe, + 0x44, 0x58, 0x4d, 0xfe, 0x01, 0xec, 0xc4, 0xfe, 0x9e, 0x40, 0xfe, 0x9d, 0xe7, 0x00, 0xfe, 0x9c, + 0xe7, 0x1d, 0xa5, 0x31, 0x01, 0xea, 0xfe, 0xc9, 0x10, 0x03, 0x2e, 0x86, 0x76, 0x24, 0x23, 0xba, + 0x05, 0x1d, 0xfe, 0x48, 0x12, 0x05, 0x0b, 0xfe, 0x4c, 0x12, 0x05, 0x18, 0xfe, 0x30, 0x12, 0x05, + 0xd4, 0x19, 0xfe, 0xd4, 0x11, 0x05, 0xfe, 0x23, 0x00, 0x19, 0xfe, 0xe0, 0x11, 0x05, 0x06, 0x19, + 0xfe, 0x3e, 0x12, 0x05, 0x22, 0xfe, 0x12, 0x12, 0x05, 0x00, 0x19, 0x26, 0x17, 0xd4, 0x01, 0x3b, + 0xce, 0x3a, 0x01, 0x0c, 0x85, 0x55, 0x03, 0x3a, 0x11, 0xfe, 0xcc, 0x00, 0x02, 0x27, 0x3a, 0x40, + 0x05, 0xcb, 0xfe, 0xe3, 0x13, 0x36, 0x3c, 0x21, 0x3d, 0x53, 0xfe, 0x92, 0x11, 0x0a, 0x07, 0x60, + 0xfe, 0x72, 0x12, 0x83, 0x30, 0x5a, 0x42, 0x95, 0xca, 0x98, 0xfe, 0x5c, 0x11, 0x29, 0x68, 0xfe, + 0x26, 0x13, 0x04, 0xb5, 0x2a, 0x93, 0x53, 0xfe, 0xb2, 0x0d, 0x0d, 0x66, 0x12, 0x67, 0x2b, 0x0d, + 0xb5, 0x0d, 0x93, 0x01, 0xc5, 0x20, 0x74, 0x5b, 0x16, 0x68, 0x01, 0xfe, 0xbe, 0x13, 0x83, 0x30, + 0x5a, 0x42, 0xfe, 0x04, 0x55, 0xfe, 0xa5, 0x55, 0xfe, 0x04, 0xfa, 0x30, 0xfe, 0x05, 0xfa, 0x42, + 0xfe, 0x91, 0x10, 0x04, 0x43, 0x2a, 0x44, 0xfe, 0x40, 0x56, 0xfe, 0xe1, 0x56, 0x0d, 0x43, 0x12, + 0x44, 0xad, 0x83, 0x30, 0x5a, 0x42, 0x95, 0xca, 0x04, 0x64, 0x2a, 0x65, 0xfe, 0x00, 0x56, 0xfe, + 0xa1, 0x56, 0x0d, 0x64, 0x12, 0x65, 0x0a, 0x07, 0x60, 0xfe, 0x1e, 0x12, 0x29, 0x68, 0xfe, 0x1f, + 0x40, 0x04, 0x66, 0x2a, 0x67, 0xfe, 0x2c, 0x50, 0xfe, 0xae, 0x50, 0x04, 0x43, 0x2a, 0x44, 0xfe, + 0x34, 0x50, 0xfe, 0xb6, 0x50, 0x04, 0x64, 0x2a, 0x65, 0xfe, 0x08, 0x50, 0xfe, 0x8a, 0x50, 0x04, + 0x3c, 0x2a, 0x3d, 0xfe, 0x28, 0x50, 0xfe, 0xaa, 0x50, 0x02, 0xa0, 0x20, 0x06, 0x16, 0xfc, 0x02, + 0x7f, 0x3a, 0x01, 0x0c, 0x1f, 0x57, 0x24, 0x23, 0xba, 0x05, 0x06, 0x28, 0x57, 0x40, 0x05, 0xcb, + 0x28, 0x7f, 0x01, 0xfe, 0x9c, 0x13, 0x1a, 0x59, 0x19, 0x57, 0x0a, 0x07, 0x0b, 0xe4, 0x36, 0x3c, + 0x21, 0x3d, 0xfe, 0x0a, 0x55, 0x35, 0xfe, 0x8b, 0x55, 0x4e, 0x3c, 0x70, 0x3d, 0xfe, 0x0c, 0x51, + 0xfe, 0x8e, 0x51, 0x02, 0x7f, 0xdf, 0xfe, 0x0a, 0x45, 0xfe, 0x19, 0x41, 0x02, 0x7f, 0x3a, 0x01, + 0x0c, 0x1f, 0xfe, 0xd6, 0x10, 0x24, 0x23, 0xfe, 0xe9, 0x09, 0x61, 0x18, 0xfe, 0x94, 0x12, 0x61, + 0x0b, 0x4f, 0x02, 0x57, 0x2f, 0xfe, 0x5e, 0x12, 0x1b, 0x32, 0x1f, 0xfe, 0xd6, 0x10, 0x24, 0x23, + 0x9a, 0x05, 0x18, 0x28, 0x57, 0x01, 0x0c, 0x1f, 0xfe, 0xd6, 0x10, 0x24, 0x23, 0xfe, 0xe8, 0x09, + 0x51, 0x04, 0xfe, 0x9c, 0x00, 0x2c, 0x35, 0xfe, 0xbb, 0x45, 0x61, 0x00, 0x50, 0x37, 0x06, 0xa6, + 0x59, 0xfe, 0xc0, 0x14, 0xfe, 0xf8, 0x14, 0xb3, 0x40, 0x05, 0xc9, 0xfe, 0x16, 0x13, 0x04, 0xfe, + 0x9e, 0x00, 0x2c, 0xd6, 0x04, 0x56, 0x2c, 0x35, 0x63, 0x02, 0x7f, 0xfe, 0xc0, 0x5d, 0xfe, 0xe4, + 0x14, 0xfe, 0x03, 0x17, 0x04, 0x5c, 0xc3, 0x0d, 0x5c, 0x63, 0x3a, 0x01, 0x0c, 0x25, 0xa0, 0x01, + 0xfe, 0x06, 0x15, 0x02, 0xa0, 0x2f, 0xfe, 0xe8, 0x12, 0x1b, 0x32, 0x1f, 0x57, 0x24, 0x23, 0x9a, + 0x05, 0x06, 0x28, 0x57, 0xfe, 0xf6, 0x14, 0xfe, 0x42, 0x58, 0xfe, 0x70, 0x14, 0xfe, 0x92, 0x14, + 0xb3, 0xfe, 0x4a, 0xf4, 0x0b, 0x19, 0x57, 0xfe, 0x4a, 0xf4, 0x06, 0xd8, 0x40, 0x05, 0xc9, 0xd1, + 0x02, 0x7f, 0x04, 0x56, 0xc3, 0x0d, 0x56, 0x63, 0x3a, 0x01, 0x0c, 0x25, 0xa0, 0x01, 0xfe, 0x34, + 0x15, 0x02, 0xa0, 0x25, 0xfe, 0x50, 0x13, 0x78, 0xf9, 0x78, 0x03, 0x34, 0xfe, 0x4c, 0x13, 0x73, + 0xfe, 0x4c, 0x13, 0x63, 0x3a, 0x01, 0x0c, 0xfe, 0xe3, 0x10, 0x08, 0x6c, 0xff, 0x02, 0x00, 0x57, + 0x6e, 0x81, 0x1a, 0xfe, 0xff, 0x7f, 0xfe, 0x30, 0x56, 0xfe, 0x00, 0x5c, 0x03, 0x08, 0x6c, 0xff, + 0x02, 0x00, 0x57, 0x6e, 0x81, 0x1a, 0x59, 0xfe, 0x30, 0x56, 0xfe, 0x00, 0x5c, 0x03, 0x08, 0x6c, + 0xff, 0x02, 0x00, 0x57, 0x6e, 0x81, 0x03, 0x08, 0x6c, 0xff, 0x02, 0x00, 0x57, 0x6e, 0x81, 0xfe, + 0x0b, 0x58, 0x03, 0x0f, 0x5c, 0x01, 0x8e, 0x0f, 0x56, 0x01, 0x8e, 0x03, 0xd0, 0x1a, 0x10, 0xff, + 0x03, 0x00, 0x54, 0xfe, 0x00, 0xf4, 0x22, 0x6e, 0xfe, 0x00, 0x7d, 0xfe, 0x01, 0x7d, 0xfe, 0x02, + 0x7d, 0xfe, 0x03, 0x7c, 0x6d, 0x31, 0x0d, 0x64, 0x12, 0x65, 0x4e, 0x43, 0x70, 0x44, 0x03, 0xfe, + 0x62, 0x18, 0xfe, 0x82, 0x5a, 0xfe, 0xe1, 0x1a, 0xbf, 0xfe, 0x02, 0x58, 0x03, 0x01, 0xfe, 0x7e, + 0x19, 0xfe, 0x42, 0x48, 0x6a, 0x51, 0x9e, 0x01, 0x0c, 0x1f, 0xfe, 0xfe, 0x14, 0x24, 0x23, 0xfe, + 0xe9, 0x09, 0xfe, 0xc1, 0x59, 0x01, 0x0c, 0x1f, 0xfe, 0xfe, 0x14, 0x24, 0x23, 0xfe, 0xe8, 0x0a, + 0x04, 0xfe, 0x9e, 0x00, 0x2c, 0xfe, 0xc4, 0x12, 0x2b, 0xb8, 0x1d, 0xe4, 0x61, 0xd5, 0x79, 0xfe, + 0x4c, 0x14, 0x4f, 0x08, 0x06, 0x09, 0xd5, 0xa6, 0xfe, 0x00, 0x10, 0xfe, 0x78, 0x10, 0xff, 0x02, + 0x83, 0x55, 0x8a, 0xff, 0x02, 0x83, 0x55, 0xb8, 0x18, 0xfe, 0x12, 0x13, 0x62, 0xfe, 0x30, 0x00, + 0x98, 0xfe, 0xa6, 0x14, 0x09, 0x8b, 0x08, 0x06, 0xfe, 0x56, 0x10, 0xb8, 0x0b, 0xfe, 0x16, 0x13, + 0x62, 0xfe, 0x64, 0x00, 0x98, 0xfe, 0xa6, 0x14, 0x0f, 0xfe, 0x64, 0x00, 0x09, 0xb1, 0x08, 0x06, + 0xfe, 0x28, 0x10, 0xb8, 0x06, 0xfe, 0x60, 0x13, 0x62, 0xfe, 0xc8, 0x00, 0x98, 0xfe, 0xa6, 0x14, + 0x0f, 0xfe, 0xc8, 0x00, 0x09, 0x5e, 0x08, 0x06, 0xad, 0x62, 0xfe, 0x90, 0x01, 0x99, 0xfe, 0xb2, + 0x14, 0x9e, 0xb0, 0xfe, 0x43, 0xf4, 0xb4, 0xfe, 0x56, 0xf0, 0xfe, 0xc4, 0x14, 0xfe, 0x04, 0xf4, + 0x6c, 0xfe, 0x43, 0xf4, 0xb1, 0xfe, 0xf3, 0x10, 0xb7, 0x01, 0xfe, 0x8e, 0x13, 0x1a, 0x59, 0xaf, + 0xfe, 0x00, 0x17, 0xfe, 0x4d, 0xe4, 0x74, 0x99, 0xfe, 0xf8, 0x14, 0xa8, 0x74, 0xfe, 0x14, 0x10, + 0xfe, 0x00, 0x17, 0xfe, 0x4d, 0xe4, 0xf1, 0x99, 0xfe, 0xf8, 0x14, 0xa8, 0xf1, 0xa4, 0x51, 0x9e, + 0x08, 0x06, 0xfe, 0xb4, 0x56, 0xfe, 0xc3, 0x58, 0x03, 0x51, 0x08, 0x0b, 0x03, 0x14, 0x06, 0x01, + 0x0c, 0x25, 0xec, 0x14, 0x0b, 0x01, 0x0c, 0x25, 0xec, 0x14, 0x18, 0x01, 0x0c, 0x25, 0xec, 0x78, + 0xfe, 0x89, 0x49, 0x01, 0x0c, 0x03, 0x14, 0x06, 0x01, 0x0c, 0x25, 0xbc, 0x14, 0x18, 0x01, 0x0c, + 0x25, 0xbc, 0x14, 0x06, 0x01, 0x0c, 0x25, 0xbc, 0xfe, 0x89, 0x49, 0x01, 0x0c, 0x25, 0xbc, 0x78, + 0xfe, 0x89, 0x4a, 0x01, 0x0c, 0x03, 0x51, 0x03, 0x29, 0xe8, 0x05, 0x06, 0x3b, 0xb6, 0x16, 0xe8, + 0xfe, 0x49, 0xf4, 0x00, 0x4f, 0x78, 0xce, 0x63, 0xfe, 0x01, 0xec, 0xfe, 0x27, 0x01, 0xf9, 0x01, + 0x0c, 0x40, 0x05, 0xfe, 0xe3, 0x00, 0xfe, 0x20, 0x13, 0x1f, 0xfe, 0xb6, 0x15, 0x2b, 0x16, 0xfc, + 0x01, 0x55, 0x29, 0xfc, 0x05, 0x06, 0x50, 0x0a, 0x41, 0x06, 0x39, 0x03, 0x0d, 0x5d, 0x12, 0x91, + 0xfe, 0x43, 0x58, 0x01, 0x15, 0x05, 0x10, 0xfe, 0x1e, 0x12, 0x4a, 0xf3, 0x96, 0x01, 0x49, 0xfe, + 0x90, 0x4d, 0xe6, 0x10, 0xfe, 0xc5, 0x59, 0x01, 0x49, 0xfe, 0x8d, 0x56, 0xbf, 0x4a, 0x03, 0x4a, + 0x21, 0x91, 0x01, 0x15, 0x4a, 0x96, 0x01, 0x49, 0xeb, 0x10, 0xe6, 0x10, 0x21, 0x5d, 0x62, 0x1e, + 0x89, 0x0f, 0x5f, 0x01, 0xaa, 0x03, 0x0d, 0x5d, 0x12, 0x91, 0xfe, 0xc3, 0x58, 0x01, 0x15, 0x05, + 0x10, 0xfe, 0x1a, 0x12, 0x4a, 0xf3, 0x96, 0x01, 0x49, 0xeb, 0x10, 0xfe, 0x80, 0x4d, 0xfe, 0xc5, + 0x59, 0x01, 0x49, 0x4a, 0x03, 0x4a, 0x21, 0x5d, 0x01, 0x15, 0x4a, 0x96, 0x01, 0x49, 0xeb, 0x10, + 0xe6, 0x10, 0x21, 0x5d, 0x62, 0x1e, 0x89, 0x0f, 0x5f, 0x01, 0xaa, 0x03, 0x0d, 0x5d, 0x12, 0x91, + 0xfe, 0x43, 0x58, 0x01, 0x15, 0xfe, 0x42, 0x48, 0x96, 0x01, 0x49, 0xfe, 0xc0, 0x5a, 0xb7, 0xfe, + 0x00, 0xcd, 0xfe, 0x01, 0xcc, 0xfe, 0x4a, 0x46, 0xe4, 0x9c, 0x80, 0x05, 0x10, 0xfe, 0x2e, 0x13, + 0x5a, 0x5d, 0xfe, 0x4d, 0xf4, 0x1e, 0xfe, 0x1c, 0x13, 0x0f, 0x5f, 0x01, 0x8e, 0xb0, 0xfe, 0x40, + 0x4c, 0xfe, 0xc5, 0x58, 0x01, 0x49, 0xfe, 0x00, 0x07, 0x80, 0x05, 0x10, 0x89, 0x5a, 0x91, 0xfe, + 0x05, 0x57, 0xfe, 0x08, 0x10, 0xfe, 0x45, 0x58, 0x01, 0x49, 0xfe, 0x8d, 0x56, 0xbf, 0xfe, 0x80, + 0x4c, 0xfe, 0x05, 0x17, 0x03, 0x09, 0x10, 0x77, 0x6f, 0xfe, 0x60, 0x01, 0xfe, 0x18, 0xdf, 0xfe, + 0x19, 0xde, 0xfe, 0x24, 0x1c, 0xe3, 0x38, 0x9d, 0xfe, 0xfa, 0x16, 0x01, 0xfe, 0x08, 0x18, 0xd9, + 0x8d, 0x38, 0x6f, 0xfe, 0x2c, 0x01, 0xfe, 0x2f, 0x19, 0x03, 0xc0, 0x28, 0xfe, 0xea, 0x16, 0xfe, + 0xe2, 0x10, 0x09, 0x10, 0x77, 0x04, 0xfe, 0x64, 0x01, 0xfe, 0x00, 0xf4, 0x22, 0xfe, 0x18, 0x58, + 0x04, 0xfe, 0x66, 0x01, 0xfe, 0x19, 0x58, 0x8d, 0x22, 0xfe, 0x3c, 0x90, 0xfe, 0x30, 0xf4, 0x06, + 0xfe, 0x3c, 0x50, 0x6f, 0xfe, 0x38, 0x00, 0xfe, 0x0f, 0x79, 0xfe, 0x1c, 0xf7, 0x22, 0x9d, 0xfe, + 0x44, 0x17, 0xfe, 0xbe, 0x14, 0x35, 0x03, 0xc0, 0x28, 0xfe, 0x1c, 0x17, 0xfe, 0xa4, 0x10, 0x09, + 0x10, 0x77, 0xbf, 0xfe, 0x18, 0xdf, 0xfe, 0x19, 0xdf, 0xe3, 0x30, 0x9d, 0xfe, 0x66, 0x17, 0xfe, + 0x9c, 0x14, 0xfe, 0x18, 0x13, 0x8d, 0x30, 0x6f, 0x1d, 0xfe, 0xaf, 0x19, 0xfe, 0x98, 0xe7, 0x00, + 0xa7, 0x07, 0xfe, 0x7f, 0x00, 0xfe, 0x05, 0x40, 0x03, 0xc0, 0x28, 0xfe, 0x5a, 0x17, 0xfe, 0x6c, + 0x10, 0x09, 0x10, 0x77, 0xfe, 0x30, 0xbc, 0xfe, 0xb2, 0xbc, 0x8d, 0xe1, 0x6f, 0x1d, 0xfe, 0x0f, + 0x79, 0xfe, 0x1c, 0xf7, 0xe1, 0x9d, 0xfe, 0xa6, 0x17, 0xfe, 0x5c, 0x14, 0x35, 0x03, 0xc0, 0x28, + 0xfe, 0x92, 0x17, 0xfe, 0x42, 0x10, 0xfe, 0x02, 0xf6, 0x10, 0x77, 0xfe, 0x18, 0xfe, 0x66, 0xfe, + 0x19, 0xfe, 0x67, 0xd0, 0xe3, 0x46, 0x9d, 0xfe, 0xcc, 0x17, 0xfe, 0x36, 0x14, 0xfe, 0x1c, 0x13, + 0x8d, 0x46, 0x47, 0xfe, 0x83, 0x58, 0xfe, 0xaf, 0x19, 0xfe, 0x80, 0xe7, 0x10, 0xfe, 0x81, 0xe7, + 0x10, 0x11, 0xfe, 0xdd, 0x00, 0x6d, 0x31, 0x03, 0x6d, 0x31, 0xfe, 0x12, 0x45, 0x28, 0xfe, 0xbc, + 0x17, 0x17, 0x06, 0x4c, 0xfe, 0x9b, 0x00, 0xe5, 0x02, 0x27, 0xfe, 0x39, 0xf0, 0xfe, 0x10, 0x18, + 0x2b, 0x03, 0xfe, 0x7e, 0x18, 0x1a, 0x18, 0x87, 0x08, 0x0e, 0x03, 0x77, 0x04, 0xe7, 0x1a, 0x06, + 0xfe, 0xef, 0x12, 0xfe, 0xe1, 0x10, 0x1c, 0x0f, 0x1e, 0x01, 0x15, 0x05, 0x10, 0x50, 0x4d, 0xfe, + 0x78, 0x14, 0xfe, 0x34, 0x12, 0x59, 0x8c, 0x37, 0x38, 0xc7, 0xfe, 0xe9, 0x13, 0x1c, 0x0f, 0x3f, + 0x01, 0x15, 0x05, 0x10, 0x50, 0x4d, 0xfe, 0x56, 0x14, 0xe9, 0x59, 0x8c, 0x37, 0x38, 0xc7, 0xfe, + 0xe9, 0x13, 0x09, 0x0b, 0x03, 0xfe, 0x9c, 0xe7, 0x0b, 0x13, 0xfe, 0x15, 0x00, 0x7a, 0xa5, 0x31, + 0x01, 0xea, 0x09, 0x06, 0x03, 0x0a, 0x41, 0x38, 0x39, 0x08, 0x3f, 0x09, 0xa2, 0x01, 0x45, 0x11, + 0x48, 0x08, 0x1e, 0x09, 0x52, 0x01, 0x7e, 0x09, 0x06, 0x03, 0xfe, 0x38, 0x90, 0xfe, 0xba, 0x90, + 0x36, 0xfe, 0xa8, 0x00, 0x21, 0x7b, 0xfe, 0x48, 0x55, 0x35, 0xfe, 0xc9, 0x55, 0x03, 0x29, 0xc6, + 0x5b, 0x16, 0xc6, 0x03, 0x0f, 0xc8, 0x01, 0x15, 0xf2, 0x0f, 0x7d, 0x01, 0x15, 0xfe, 0x49, 0x44, + 0x28, 0xfe, 0x06, 0x19, 0x0f, 0x1e, 0x01, 0x15, 0x05, 0x10, 0x50, 0x0f, 0x5f, 0x01, 0xaa, 0x0f, + 0x7d, 0x01, 0x15, 0x5b, 0x80, 0x03, 0xfe, 0x40, 0x5e, 0xfe, 0xe2, 0x08, 0xfe, 0xc0, 0x4c, 0x29, + 0x3e, 0x05, 0x10, 0xfe, 0x52, 0x12, 0x4d, 0x05, 0x00, 0xfe, 0x18, 0x12, 0xfe, 0xe1, 0x18, 0xfe, + 0x19, 0xf4, 0xfe, 0x7f, 0x00, 0xaf, 0xfe, 0xe2, 0x08, 0x5b, 0x4d, 0x40, 0x05, 0x7b, 0xab, 0xfe, + 0x82, 0x48, 0xfe, 0x01, 0x80, 0xfe, 0xd7, 0x10, 0xfe, 0xc4, 0x48, 0x08, 0x2d, 0x09, 0x3e, 0xfe, + 0x40, 0x5f, 0x1c, 0x01, 0x45, 0x11, 0xfe, 0xdd, 0x00, 0xfe, 0x14, 0x46, 0x08, 0x2d, 0x09, 0x3e, + 0x01, 0x45, 0x11, 0xfe, 0xdd, 0x00, 0xfe, 0x40, 0x4a, 0x72, 0xfe, 0x06, 0x17, 0xfe, 0x01, 0x07, + 0xfe, 0x82, 0x48, 0xfe, 0x04, 0x17, 0x03, 0xf5, 0x18, 0x79, 0xfe, 0x8e, 0x19, 0x04, 0xfe, 0x90, + 0x00, 0xfe, 0x3a, 0x45, 0xfe, 0x2c, 0x10, 0xf5, 0xd4, 0x79, 0xfe, 0xa0, 0x19, 0x04, 0xfe, 0x92, + 0x00, 0xcf, 0x1d, 0xe0, 0xf5, 0xfe, 0x0b, 0x00, 0x79, 0xfe, 0xb2, 0x19, 0x04, 0xfe, 0x94, 0x00, + 0xcf, 0x22, 0xfe, 0x08, 0x10, 0x04, 0xfe, 0x96, 0x00, 0xcf, 0x8b, 0xfe, 0x4e, 0x45, 0xd8, 0xfe, + 0x0a, 0x45, 0xff, 0x04, 0x68, 0x54, 0xfe, 0xf1, 0x10, 0x1a, 0x74, 0xfe, 0x08, 0x1c, 0xfe, 0x67, + 0x19, 0xfe, 0x0a, 0x1c, 0xfe, 0x1a, 0xf4, 0xfe, 0x00, 0x04, 0xd8, 0xfe, 0x48, 0xf4, 0x18, 0x99, + 0xfe, 0xe6, 0x19, 0x08, 0x18, 0x03, 0x05, 0x84, 0xfe, 0x5a, 0xf0, 0xfe, 0xf6, 0x19, 0x20, 0xfe, + 0x09, 0x00, 0xfe, 0x34, 0x10, 0x05, 0x1d, 0xfe, 0x5a, 0xf0, 0xfe, 0x04, 0x1a, 0x20, 0xd5, 0xfe, + 0x26, 0x10, 0x05, 0x18, 0x87, 0x20, 0x8b, 0xe0, 0x05, 0x0b, 0x87, 0x20, 0xb1, 0xfe, 0x0e, 0x10, + 0x05, 0x06, 0x87, 0x20, 0x5e, 0xce, 0xb6, 0x03, 0x17, 0xfe, 0x09, 0x00, 0x01, 0x3b, 0x2f, 0xfe, + 0x34, 0x1a, 0x04, 0x76, 0xb7, 0x03, 0x1b, 0xfe, 0x54, 0x1a, 0xfe, 0x14, 0xf0, 0x0c, 0x2f, 0xfe, + 0x48, 0x1a, 0x1b, 0xfe, 0x54, 0x1a, 0xfe, 0x82, 0xf0, 0xfe, 0x4c, 0x1a, 0x03, 0xff, 0x15, 0x00, + 0x00, +}; STATIC unsigned short _adv_asc38C0800_size = - sizeof(_adv_asc38C0800_buf); /* 0x14AA */ -STATIC unsigned long _adv_asc38C0800_chksum = - 0x05297A65UL; /* Expanded checksum. */ + sizeof(_adv_asc38C0800_buf); /* 0x14F1 */ +STATIC ADV_DCNT _adv_asc38C0800_chksum = + 0x053503A5; /* Expanded checksum. */ /* a_init.c */ /* @@ -15384,8 +15456,8 @@ */ STATIC ADVEEP_3550_CONFIG Default_3550_EEPROM_Config ASC_INITDATA = { - ADV_EEPROM_BIOS_ENABLE, /* cfg_msw */ - 0x0000, /* cfg_lsw */ + ADV_EEPROM_BIOS_ENABLE, /* cfg_lsw */ + 0x0000, /* cfg_msw */ 0xFFFF, /* disc_enable */ 0xFFFF, /* wdtr_able */ 0xFFFF, /* sdtr_able */ @@ -15422,8 +15494,8 @@ STATIC ADVEEP_38C0800_CONFIG Default_38C0800_EEPROM_Config ASC_INITDATA = { - ADV_EEPROM_BIOS_ENABLE, /* 00 cfg_msw */ - 0x0000, /* 01 cfg_lsw */ + ADV_EEPROM_BIOS_ENABLE, /* 00 cfg_lsw */ + 0x0000, /* 01 cfg_msw */ 0xFFFF, /* 02 disc_enable */ 0xFFFF, /* 03 wdtr_able */ 0x4444, /* 04 sdtr_speed1 */ @@ -15509,6 +15581,9 @@ /* * PCI Command Register + * + * Note: AscPCICmdRegBits_BusMastering definition (0x0007) includes + * I/O Space Control, Memory Space Control and Bus Master Control bits. */ if (((pci_cmd_reg = DvcAdvReadPCIConfigByte(asc_dvc, @@ -15562,11 +15637,11 @@ asc_dvc->cfg->chip_version = AdvGetChipVersion(iop_base, asc_dvc->bus_type); - ASC_DBG2(1, "iopb_chip_id_1: %x %x\n", + ASC_DBG2(1, "AdvInitGetConfig: iopb_chip_id_1: %x %x\n", (ushort) AdvReadByteRegister(iop_base, IOPB_CHIP_ID_1), (ushort) ADV_CHIP_ID_BYTE); - ASC_DBG2(1, "iopw_chip_id_0: %x %x\n", + ASC_DBG2(1, "AdvInitGetConfig: iopw_chip_id_0: %x %x\n", (ushort) AdvReadWordRegister(iop_base, IOPW_CHIP_ID_0), (ushort) ADV_CHIP_ID_WORD); @@ -15631,7 +15706,7 @@ { AdvPortAddr iop_base; ushort warn_code; - ulong sum; + ADV_DCNT sum; int begin_addr; int end_addr; ushort code_sum; @@ -15639,9 +15714,9 @@ int j; int adv_asc3550_expanded_size; ADV_CARR_T *carrp; - ulong contig_len; - long buf_size; - ulong carr_paddr; + ADV_DCNT contig_len; + ADV_SDCNT buf_size; + ADV_PADDR carr_paddr; int i; ushort scsi_cfg1; uchar tid; @@ -15690,7 +15765,7 @@ bios_version = bios_mem[(ASC_MC_BIOS_VERSION - ASC_MC_BIOSMEM)/2]; major = (bios_version >> 12) & 0xF; minor = (bios_version >> 8) & 0xF; - if (major <= 3 || (major == 3 && minor == 1)) + if (major < 3 || (major == 3 && minor == 1)) { /* BIOS 3.1 and earlier location of 'wdtr_able' variable. */ AdvReadWordLram(iop_base, 0x120, wdtr_able); @@ -15735,21 +15810,24 @@ { for (j = 0; j < _adv_asc3550_buf[i + 1]; j++) { - AdvWriteWordAutoIncLram(iop_base, - *((ushort *) (&_adv_asc3550_buf[i + 2]))); + AdvWriteWordAutoIncLram(iop_base, (((ushort) + _adv_asc3550_buf[i + 3] << 8) | + _adv_asc3550_buf[i + 2])); word++; } - i += 3; + i += 3; } else if (_adv_asc3550_buf[i] == 0xfe) { - AdvWriteWordAutoIncLram(iop_base, - *((ushort *) (&_adv_asc3550_buf[i + 1]))); + AdvWriteWordAutoIncLram(iop_base, (((ushort) + _adv_asc3550_buf[i + 2] << 8) | + _adv_asc3550_buf[i + 1])); i += 2; word++; } else { - AdvWriteWordAutoIncLram(iop_base, - *((ushort *) &_adv_asc3550_buf[_adv_asc3550_buf[i] * 2])); + AdvWriteWordAutoIncLram(iop_base, (((ushort) + _adv_asc3550_buf[(_adv_asc3550_buf[i] * 2) + 1] << 8) | + _adv_asc3550_buf[_adv_asc3550_buf[i] * 2])); word++; } } @@ -15849,7 +15927,7 @@ * queuing will be set in AdvInquiryHandling() based on what a * device reports it is capable of in Inquiry byte 7. * - * If SCSI Bus Resets haev been disabled, then directly set + * If SCSI Bus Resets have been disabled, then directly set * SDTR and WDTR from the EEPROM configuration. This will allow * the BIOS and warm boot to work without a SCSI bus hang on * the Inquiry caused by host and target mismatched DTR values. @@ -16028,7 +16106,7 @@ * after it is started below. */ AdvWriteWordLram(iop_base, ASC_MC_DEFAULT_SCSI_CFG1, - FLTR_11_TO_20NS | scsi_cfg1); + FLTR_DISABLE | scsi_cfg1); /* * Set MEM_CFG Microcode Default Value @@ -16058,7 +16136,7 @@ * * Driver must have already allocated memory and set 'carrier_buf'. */ - ADV_ASSERT(asc_dvc->carrier_buf != NULL); + ASC_ASSERT(asc_dvc->carrier_buf != NULL); carrp = (ADV_CARR_T *) ADV_16BALIGN(asc_dvc->carrier_buf); asc_dvc->carr_freelist = NULL; @@ -16075,8 +16153,8 @@ * Get physical address of the carrier 'carrp'. */ contig_len = sizeof(ADV_CARR_T); - carr_paddr = DvcGetPhyAddr(asc_dvc, NULL, (uchar *) carrp, - (long *) &contig_len, ADV_IS_CARRIER_FLAG); + carr_paddr = cpu_to_le32(DvcGetPhyAddr(asc_dvc, NULL, (uchar *) carrp, + (ADV_SDCNT *) &contig_len, ADV_IS_CARRIER_FLAG)); buf_size -= sizeof(ADV_CARR_T); @@ -16092,12 +16170,12 @@ } carrp->carr_pa = carr_paddr; - carrp->carr_va = (ulong) carrp; + carrp->carr_va = ADV_VADDR_TO_U32(carrp); /* * Insert the carrier at the beginning of the freelist. */ - carrp->next_vpa = (ulong) asc_dvc->carr_freelist; + carrp->next_vpa = ADV_VADDR_TO_U32(asc_dvc->carr_freelist); asc_dvc->carr_freelist = carrp; carrp++; @@ -16113,7 +16191,8 @@ asc_dvc->err_code |= ASC_IERR_NO_CARRIER; return ADV_ERROR; } - asc_dvc->carr_freelist = (ADV_CARR_T *) asc_dvc->icq_sp->next_vpa; + asc_dvc->carr_freelist = + (ADV_CARR_T *) ADV_U32_TO_VADDR(asc_dvc->icq_sp->next_vpa); /* * The first command issued will be placed in the stopper carrier. @@ -16123,7 +16202,8 @@ /* * Set RISC ICQ physical address start value. */ - AdvWriteDWordLram(iop_base, ASC_MC_ICQ, asc_dvc->icq_sp->carr_pa); + AdvWriteDWordLram(iop_base, ASC_MC_ICQ, + cpu_to_le32(asc_dvc->icq_sp->carr_pa)); /* * Set-up the RISC->Host Initiator Response Queue (IRQ). @@ -16133,7 +16213,8 @@ asc_dvc->err_code |= ASC_IERR_NO_CARRIER; return ADV_ERROR; } - asc_dvc->carr_freelist = (ADV_CARR_T *) asc_dvc->irq_sp->next_vpa; + asc_dvc->carr_freelist = + (ADV_CARR_T *) ADV_U32_TO_VADDR(asc_dvc->irq_sp->next_vpa); /* * The first command completed by the RISC will be placed in @@ -16147,7 +16228,8 @@ /* * Set RISC IRQ physical address start value. */ - AdvWriteDWordLram(iop_base, ASC_MC_IRQ, asc_dvc->irq_sp->carr_pa); + AdvWriteDWordLram(iop_base, ASC_MC_IRQ, + cpu_to_le32(asc_dvc->irq_sp->carr_pa)); asc_dvc->carr_pending_cnt = 0; AdvWriteByteRegister(iop_base, IOPB_INTR_ENABLES, @@ -16214,7 +16296,7 @@ { AdvPortAddr iop_base; ushort warn_code; - ulong sum; + ADV_DCNT sum; int begin_addr; int end_addr; ushort code_sum; @@ -16222,9 +16304,9 @@ int j; int adv_asc38C0800_expanded_size; ADV_CARR_T *carrp; - ulong contig_len; - long buf_size; - ulong carr_paddr; + ADV_DCNT contig_len; + ADV_SDCNT buf_size; + ADV_PADDR carr_paddr; int i; ushort scsi_cfg1; uchar byte; @@ -16378,21 +16460,24 @@ { for (j = 0; j < _adv_asc38C0800_buf[i + 1]; j++) { - AdvWriteWordAutoIncLram(iop_base, - *((ushort *) (&_adv_asc38C0800_buf[i + 2]))); + AdvWriteWordAutoIncLram(iop_base, (((ushort) + _adv_asc38C0800_buf[i + 3] << 8) | + _adv_asc38C0800_buf[i + 2])); word++; } - i += 3; + i += 3; } else if (_adv_asc38C0800_buf[i] == 0xfe) { - AdvWriteWordAutoIncLram(iop_base, - *((ushort *) (&_adv_asc38C0800_buf[i + 1]))); + AdvWriteWordAutoIncLram(iop_base, (((ushort) + _adv_asc38C0800_buf[i + 2] << 8) | + _adv_asc38C0800_buf[i + 1])); i += 2; word++; } else { - AdvWriteWordAutoIncLram(iop_base, *((ushort *) - &_adv_asc38C0800_buf[_adv_asc38C0800_buf[i] * 2])); + AdvWriteWordAutoIncLram(iop_base, (((ushort) + _adv_asc38C0800_buf[(_adv_asc38C0800_buf[i] * 2) + 1] << 8) | + _adv_asc38C0800_buf[_adv_asc38C0800_buf[i] * 2])); word++; } } @@ -16422,6 +16507,11 @@ { sum += AdvReadWordAutoIncLram(iop_base); } + ASC_DBG2(1, "AdvInitAsc38C0800Driver: word %d, i %d\n", word, i); + + ASC_DBG2(1, + "AdvInitAsc38C0800Driver: sum 0x%lx, _adv_asc38C0800_chksum 0x%lx\n", + (ulong) sum, (ulong) _adv_asc38C0800_chksum); if (sum != _adv_asc38C0800_chksum) { @@ -16677,8 +16767,7 @@ * * Driver must have already allocated memory and set 'carrier_buf'. */ - - ADV_ASSERT(asc_dvc->carrier_buf != NULL); + ASC_ASSERT(asc_dvc->carrier_buf != NULL); carrp = (ADV_CARR_T *) ADV_16BALIGN(asc_dvc->carrier_buf); asc_dvc->carr_freelist = NULL; @@ -16695,8 +16784,8 @@ * Get physical address for the carrier 'carrp'. */ contig_len = sizeof(ADV_CARR_T); - carr_paddr = DvcGetPhyAddr(asc_dvc, NULL, (uchar *) carrp, - (long *) &contig_len, ADV_IS_CARRIER_FLAG); + carr_paddr = cpu_to_le32(DvcGetPhyAddr(asc_dvc, NULL, (uchar *) carrp, + (ADV_SDCNT *) &contig_len, ADV_IS_CARRIER_FLAG)); buf_size -= sizeof(ADV_CARR_T); @@ -16712,12 +16801,12 @@ } carrp->carr_pa = carr_paddr; - carrp->carr_va = (ulong) carrp; + carrp->carr_va = ADV_VADDR_TO_U32(carrp); /* * Insert the carrier at the beginning of the freelist. */ - carrp->next_vpa = (ulong) asc_dvc->carr_freelist; + carrp->next_vpa = ADV_VADDR_TO_U32(asc_dvc->carr_freelist); asc_dvc->carr_freelist = carrp; carrp++; @@ -16733,7 +16822,8 @@ asc_dvc->err_code |= ASC_IERR_NO_CARRIER; return ADV_ERROR; } - asc_dvc->carr_freelist = (ADV_CARR_T *) asc_dvc->icq_sp->next_vpa; + asc_dvc->carr_freelist = + (ADV_CARR_T *) ADV_U32_TO_VADDR(asc_dvc->icq_sp->next_vpa); /* * The first command issued will be placed in the stopper carrier. @@ -16743,7 +16833,8 @@ /* * Set RISC ICQ physical address start value. */ - AdvWriteDWordLram(iop_base, ASC_MC_ICQ, asc_dvc->icq_sp->carr_pa); + AdvWriteDWordLram(iop_base, ASC_MC_ICQ, + cpu_to_le32(asc_dvc->icq_sp->carr_pa)); /* * Set-up the RISC->Host Initiator Response Queue (IRQ). @@ -16753,7 +16844,8 @@ asc_dvc->err_code |= ASC_IERR_NO_CARRIER; return ADV_ERROR; } - asc_dvc->carr_freelist = (ADV_CARR_T *) asc_dvc->irq_sp->next_vpa; + asc_dvc->carr_freelist = + (ADV_CARR_T *) ADV_U32_TO_VADDR(asc_dvc->irq_sp->next_vpa); /* * The first command completed by the RISC will be placed in @@ -16767,7 +16859,8 @@ /* * Set RISC IRQ physical address start value. */ - AdvWriteDWordLram(iop_base, ASC_MC_IRQ, asc_dvc->irq_sp->carr_pa); + AdvWriteDWordLram(iop_base, ASC_MC_IRQ, + cpu_to_le32(asc_dvc->irq_sp->carr_pa)); asc_dvc->carr_pending_cnt = 0; AdvWriteByteRegister(iop_base, IOPB_INTR_ENABLES, @@ -16834,15 +16927,13 @@ */ ASC_INITFUNC( STATIC int, -AdvInitFrom38C0800EEP(ADV_DVC_VAR *asc_dvc) +AdvInitFrom3550EEP(ADV_DVC_VAR *asc_dvc) ) { - AdvPortAddr iop_base; - ushort warn_code; - ADVEEP_38C0800_CONFIG eep_config; - int i; - uchar tid, termination; - ushort sdtr_speed = 0; + AdvPortAddr iop_base; + ushort warn_code; + ADVEEP_3550_CONFIG eep_config; + int i; iop_base = asc_dvc->iop_base; @@ -16852,18 +16943,20 @@ * Read the board's EEPROM configuration. * * Set default values if a bad checksum is found. + * + * XXX - Don't handle big-endian access to EEPROM yet. */ - if (AdvGet38C0800EEPConfig(iop_base, &eep_config) != eep_config.check_sum) + if (AdvGet3550EEPConfig(iop_base, &eep_config) != eep_config.check_sum) { warn_code |= ASC_WARN_EEPROM_CHKSUM; /* * Set EEPROM default values. */ - for (i = 0; i < sizeof(ADVEEP_38C0800_CONFIG); i++) + for (i = 0; i < sizeof(ADVEEP_3550_CONFIG); i++) { *((uchar *) &eep_config + i) = - *((uchar *) &Default_38C0800_EEPROM_Config + i); + *((uchar *) &Default_3550_EEPROM_Config + i); } /* @@ -16880,19 +16973,17 @@ eep_config.serial_number_word1 = AdvReadEEPWord(iop_base, ASC_EEP_DVC_CFG_END - 3); - AdvSet38C0800EEPConfig(iop_base, &eep_config); + AdvSet3550EEPConfig(iop_base, &eep_config); } /* - * Set ADV_DVC_VAR and ADV_DVC_CFG variables from the + * Set ASC_DVC_VAR and ASC_DVC_CFG variables from the * EEPROM configuration that was read. * * This is the mapping of EEPROM fields to Adv Library fields. */ asc_dvc->wdtr_able = eep_config.wdtr_able; - asc_dvc->sdtr_speed1 = eep_config.sdtr_speed1; - asc_dvc->sdtr_speed2 = eep_config.sdtr_speed2; - asc_dvc->sdtr_speed3 = eep_config.sdtr_speed3; - asc_dvc->sdtr_speed4 = eep_config.sdtr_speed4; + asc_dvc->sdtr_able = eep_config.sdtr_able; + asc_dvc->ultra_able = eep_config.ultra_able; asc_dvc->tagqng_able = eep_config.tagqng_able; asc_dvc->cfg->disc_enable = eep_config.disc_enable; asc_dvc->max_host_qng = eep_config.max_host_qng; @@ -16907,33 +16998,6 @@ asc_dvc->cfg->serial3 = eep_config.serial_number_word3; /* - * For every Target ID if any of its 'sdtr_speed[1234]' bits - * are set, then set an 'sdtr_able' bit for it. - */ - asc_dvc->sdtr_able = 0; - for (tid = 0; tid <= ADV_MAX_TID; tid++) - { - if (tid == 0) - { - sdtr_speed = asc_dvc->sdtr_speed1; - } else if (tid == 4) - { - sdtr_speed = asc_dvc->sdtr_speed2; - } else if (tid == 8) - { - sdtr_speed = asc_dvc->sdtr_speed3; - } else if (tid == 12) - { - sdtr_speed = asc_dvc->sdtr_speed4; - } - if (sdtr_speed & ADV_MAX_TID) - { - asc_dvc->sdtr_able |= (1 << tid); - } - sdtr_speed >>= 4; - } - - /* * Set the host maximum queuing (max. 253, min. 16) and the per device * maximum queuing (max. 63, min. 4). */ @@ -16983,6 +17047,7 @@ asc_dvc->max_host_qng = eep_config.max_host_qng; asc_dvc->max_dvc_qng = eep_config.max_dvc_qng; + /* * If the EEPROM 'termination' field is set to automatic (0), then set * the ADV_DVC_CFG 'termination' field to automatic also. @@ -16991,63 +17056,32 @@ * value check that a legal value is set and set the ADV_DVC_CFG * 'termination' field appropriately. */ - if (eep_config.termination_se == 0) - { - termination = 0; /* auto termination for SE */ - } else - { - /* Enable manual control with low off / high off. */ - if (eep_config.termination_se == 1) - { - termination = 0; - - /* Enable manual control with low off / high on. */ - } else if (eep_config.termination_se == 2) - { - termination = TERM_SE_HI; - - /* Enable manual control with low on / high on. */ - } else if (eep_config.termination_se == 3) - { - termination = TERM_SE; - } else - { - /* - * The EEPROM 'termination_se' field contains a bad value. - * Use automatic termination instead. - */ - termination = 0; - warn_code |= ASC_WARN_EEPROM_TERMINATION; - } - } - - if (eep_config.termination_lvd == 0) + if (eep_config.termination == 0) { - asc_dvc->cfg->termination = termination; /* auto termination for LVD */ + asc_dvc->cfg->termination = 0; /* auto termination */ } else { /* Enable manual control with low off / high off. */ - if (eep_config.termination_lvd == 1) + if (eep_config.termination == 1) { - asc_dvc->cfg->termination = termination; + asc_dvc->cfg->termination = TERM_CTL_SEL; /* Enable manual control with low off / high on. */ - } else if (eep_config.termination_lvd == 2) + } else if (eep_config.termination == 2) { - asc_dvc->cfg->termination = termination | TERM_LVD_HI; + asc_dvc->cfg->termination = TERM_CTL_SEL | TERM_CTL_H; /* Enable manual control with low on / high on. */ - } else if (eep_config.termination_lvd == 3) + } else if (eep_config.termination == 3) { - asc_dvc->cfg->termination = - termination | TERM_LVD; + asc_dvc->cfg->termination = TERM_CTL_SEL | TERM_CTL_H | TERM_CTL_L; } else { /* - * The EEPROM 'termination_lvd' field contains a bad value. - * Use automatic termination instead. + * The EEPROM 'termination' field contains a bad value. Use + * automatic termination instead. */ - asc_dvc->cfg->termination = termination; + asc_dvc->cfg->termination = 0; warn_code |= ASC_WARN_EEPROM_TERMINATION; } } @@ -17069,13 +17103,15 @@ */ ASC_INITFUNC( STATIC int, -AdvInitFrom3550EEP(ADV_DVC_VAR *asc_dvc) +AdvInitFrom38C0800EEP(ADV_DVC_VAR *asc_dvc) ) { - AdvPortAddr iop_base; - ushort warn_code; - ADVEEP_3550_CONFIG eep_config; - int i; + AdvPortAddr iop_base; + ushort warn_code; + ADVEEP_38C0800_CONFIG eep_config; + int i; + uchar tid, termination; + ushort sdtr_speed = 0; iop_base = asc_dvc->iop_base; @@ -17085,18 +17121,20 @@ * Read the board's EEPROM configuration. * * Set default values if a bad checksum is found. + * + * XXX - Don't handle big-endian access to EEPROM yet. */ - if (AdvGet3550EEPConfig(iop_base, &eep_config) != eep_config.check_sum) + if (AdvGet38C0800EEPConfig(iop_base, &eep_config) != eep_config.check_sum) { warn_code |= ASC_WARN_EEPROM_CHKSUM; /* * Set EEPROM default values. */ - for (i = 0; i < sizeof(ADVEEP_3550_CONFIG); i++) + for (i = 0; i < sizeof(ADVEEP_38C0800_CONFIG); i++) { *((uchar *) &eep_config + i) = - *((uchar *) &Default_3550_EEPROM_Config + i); + *((uchar *) &Default_38C0800_EEPROM_Config + i); } /* @@ -17113,17 +17151,19 @@ eep_config.serial_number_word1 = AdvReadEEPWord(iop_base, ASC_EEP_DVC_CFG_END - 3); - AdvSet3550EEPConfig(iop_base, &eep_config); + AdvSet38C0800EEPConfig(iop_base, &eep_config); } /* - * Set ASC_DVC_VAR and ASC_DVC_CFG variables from the + * Set ADV_DVC_VAR and ADV_DVC_CFG variables from the * EEPROM configuration that was read. * * This is the mapping of EEPROM fields to Adv Library fields. */ asc_dvc->wdtr_able = eep_config.wdtr_able; - asc_dvc->sdtr_able = eep_config.sdtr_able; - asc_dvc->ultra_able = eep_config.ultra_able; + asc_dvc->sdtr_speed1 = eep_config.sdtr_speed1; + asc_dvc->sdtr_speed2 = eep_config.sdtr_speed2; + asc_dvc->sdtr_speed3 = eep_config.sdtr_speed3; + asc_dvc->sdtr_speed4 = eep_config.sdtr_speed4; asc_dvc->tagqng_able = eep_config.tagqng_able; asc_dvc->cfg->disc_enable = eep_config.disc_enable; asc_dvc->max_host_qng = eep_config.max_host_qng; @@ -17138,6 +17178,33 @@ asc_dvc->cfg->serial3 = eep_config.serial_number_word3; /* + * For every Target ID if any of its 'sdtr_speed[1234]' bits + * are set, then set an 'sdtr_able' bit for it. + */ + asc_dvc->sdtr_able = 0; + for (tid = 0; tid <= ADV_MAX_TID; tid++) + { + if (tid == 0) + { + sdtr_speed = asc_dvc->sdtr_speed1; + } else if (tid == 4) + { + sdtr_speed = asc_dvc->sdtr_speed2; + } else if (tid == 8) + { + sdtr_speed = asc_dvc->sdtr_speed3; + } else if (tid == 12) + { + sdtr_speed = asc_dvc->sdtr_speed4; + } + if (sdtr_speed & ADV_MAX_TID) + { + asc_dvc->sdtr_able |= (1 << tid); + } + sdtr_speed >>= 4; + } + + /* * Set the host maximum queuing (max. 253, min. 16) and the per device * maximum queuing (max. 63, min. 4). */ @@ -17187,7 +17254,6 @@ asc_dvc->max_host_qng = eep_config.max_host_qng; asc_dvc->max_dvc_qng = eep_config.max_dvc_qng; - /* * If the EEPROM 'termination' field is set to automatic (0), then set * the ADV_DVC_CFG 'termination' field to automatic also. @@ -17196,32 +17262,63 @@ * value check that a legal value is set and set the ADV_DVC_CFG * 'termination' field appropriately. */ - if (eep_config.termination == 0) + if (eep_config.termination_se == 0) { - asc_dvc->cfg->termination = 0; /* auto termination */ + termination = 0; /* auto termination for SE */ } else { /* Enable manual control with low off / high off. */ - if (eep_config.termination == 1) + if (eep_config.termination_se == 1) { - asc_dvc->cfg->termination = TERM_CTL_SEL; + termination = 0; /* Enable manual control with low off / high on. */ - } else if (eep_config.termination == 2) + } else if (eep_config.termination_se == 2) { - asc_dvc->cfg->termination = TERM_CTL_SEL | TERM_CTL_H; + termination = TERM_SE_HI; /* Enable manual control with low on / high on. */ - } else if (eep_config.termination == 3) + } else if (eep_config.termination_se == 3) { - asc_dvc->cfg->termination = TERM_CTL_SEL | TERM_CTL_H | TERM_CTL_L; + termination = TERM_SE; } else { /* - * The EEPROM 'termination' field contains a bad value. Use - * automatic termination instead. + * The EEPROM 'termination_se' field contains a bad value. + * Use automatic termination instead. */ - asc_dvc->cfg->termination = 0; + termination = 0; + warn_code |= ASC_WARN_EEPROM_TERMINATION; + } + } + + if (eep_config.termination_lvd == 0) + { + asc_dvc->cfg->termination = termination; /* auto termination for LVD */ + } else + { + /* Enable manual control with low off / high off. */ + if (eep_config.termination_lvd == 1) + { + asc_dvc->cfg->termination = termination; + + /* Enable manual control with low off / high on. */ + } else if (eep_config.termination_lvd == 2) + { + asc_dvc->cfg->termination = termination | TERM_LVD_HI; + + /* Enable manual control with low on / high on. */ + } else if (eep_config.termination_lvd == 3) + { + asc_dvc->cfg->termination = + termination | TERM_LVD; + } else + { + /* + * The EEPROM 'termination_lvd' field contains a bad value. + * Use automatic termination instead. + */ + asc_dvc->cfg->termination = termination; warn_code |= ASC_WARN_EEPROM_TERMINATION; } } @@ -17236,8 +17333,7 @@ */ ASC_INITFUNC( STATIC ushort, -AdvGet38C0800EEPConfig(AdvPortAddr iop_base, - ADVEEP_38C0800_CONFIG *cfg_buf) +AdvGet3550EEPConfig(AdvPortAddr iop_base, ADVEEP_3550_CONFIG *cfg_buf) ) { ushort wval, chksum; @@ -17266,7 +17362,6 @@ return chksum; } - /* * Read EEPROM configuration into the specified buffer. * @@ -17274,7 +17369,8 @@ */ ASC_INITFUNC( STATIC ushort, -AdvGet3550EEPConfig(AdvPortAddr iop_base, ADVEEP_3550_CONFIG *cfg_buf) +AdvGet38C0800EEPConfig(AdvPortAddr iop_base, + ADVEEP_38C0800_CONFIG *cfg_buf) ) { ushort wval, chksum; @@ -17303,6 +17399,7 @@ return chksum; } + /* * Read the EEPROM from specified location */ @@ -17337,7 +17434,7 @@ } if ((AdvReadWordRegister(iop_base, IOPW_EE_CMD) & ASC_EEP_CMD_DONE) == 0) { - ADV_ASSERT(0); + ASC_ASSERT(0); } return; } @@ -17456,6 +17553,10 @@ * If 'done_status' is not set to QD_DO_RETRY, then 'error_retry' will be * set to SCSI_MAX_RETRY. * + * Multi-byte fields in the ASC_SCSI_REQ_Q that are used by the microcode + * for DMA addresses or math operations are byte swapped to little-endian + * order. + * * Return: * ADV_SUCCESS(1) - The request was successfully queued. * ADV_BUSY(0) - Resource unavailable; Retry again after pending @@ -17469,11 +17570,11 @@ { int last_int_level; AdvPortAddr iop_base; - long req_size; - ulong req_paddr; + ADV_DCNT req_size; + ADV_PADDR req_paddr; ADV_CARR_T *new_carrp; - ADV_ASSERT(scsiq != NULL); /* 'scsiq' should never be NULL. */ + ASC_ASSERT(scsiq != NULL); /* 'scsiq' should never be NULL. */ /* * The ADV_SCSI_REQ_Q 'target_id' field should never exceed ADV_MAX_TID. @@ -17497,7 +17598,8 @@ { return ADV_BUSY; } - asc_dvc->carr_freelist = (ADV_CARR_T *) new_carrp->next_vpa; + asc_dvc->carr_freelist = + (ADV_CARR_T *) ADV_U32_TO_VADDR(new_carrp->next_vpa); asc_dvc->carr_pending_cnt++; /* @@ -17513,18 +17615,21 @@ scsiq->a_flag &= ~ADV_SCSIQ_DONE; req_size = sizeof(ADV_SCSI_REQ_Q); - req_paddr = DvcGetPhyAddr(asc_dvc, scsiq, (uchar *) scsiq, - (long *) &req_size, ADV_IS_SCSIQ_FLAG); + req_paddr = cpu_to_le32(DvcGetPhyAddr(asc_dvc, scsiq, (uchar *) scsiq, + (ADV_SDCNT *) &req_size, ADV_IS_SCSIQ_FLAG)); - ADV_ASSERT(ADV_DWALIGN(req_paddr) == req_paddr); - ADV_ASSERT(req_size >= sizeof(ADV_SCSI_REQ_Q)); + ASC_ASSERT(ADV_DWALIGN(req_paddr) == req_paddr); + ASC_ASSERT(req_size >= sizeof(ADV_SCSI_REQ_Q)); - /* Save virtual and physical address of ADV_SCSI_REQ_Q and Carrier. */ - scsiq->scsiq_ptr = (ADV_SCSI_REQ_Q *) scsiq; + /* Save virtual and physical address of ADV_SCSI_REQ_Q and carrier. */ + scsiq->scsiq_ptr = ADV_VADDR_TO_U32(scsiq); scsiq->scsiq_rptr = req_paddr; - /* XXX - Could have the RISC set these values. */ - scsiq->carr_va = (ulong) asc_dvc->icq_sp; + scsiq->carr_va = ADV_VADDR_TO_U32(asc_dvc->icq_sp); + /* + * Every ADV_CARR_T.carr_pa is byte swapped to little-endian + * order during initialization. + */ scsiq->carr_pa = asc_dvc->icq_sp->carr_pa; /* @@ -17532,7 +17637,7 @@ * the microcode. The newly allocated stopper will become the new * stopper. */ - asc_dvc->icq_sp->areq_vpa = (ulong) req_paddr; + asc_dvc->icq_sp->areq_vpa = req_paddr; /* * Set the 'next_vpa' pointer for the old stopper to be the @@ -17607,7 +17712,7 @@ return status; } - DvcSleepMilliSecond((ulong) asc_dvc->scsi_reset_wait * 1000); + DvcSleepMilliSecond((ADV_DCNT) asc_dvc->scsi_reset_wait * 1000); return status; } @@ -17727,7 +17832,7 @@ uchar int_stat; ushort target_bit; ADV_CARR_T *free_carrp; - ulong irq_next_vpa; + ADV_VADDR irq_next_vpa; int flags; ADV_SCSI_REQ_Q *scsiq; @@ -17778,8 +17883,13 @@ /* * Get a pointer to the newly completed ADV_SCSI_REQ_Q structure. * The RISC will have set 'areq_vpa' to a virtual address. + * + * The firmware will have copied the ASC_SCSI_REQ_Q.scsiq_ptr + * field to the carrier ADV_CARR_T.areq_vpa field. The conversion + * below complements the conversion of ASC_SCSI_REQ_Q.scsiq_ptr' + * in AdvExeScsiQueue(). */ - scsiq = (ADV_SCSI_REQ_Q *) asc_dvc->irq_sp->areq_vpa; + scsiq = (ADV_SCSI_REQ_Q *) ADV_U32_TO_VADDR(asc_dvc->irq_sp->areq_vpa); /* * Advance the stopper pointer to the next carrier @@ -17787,14 +17897,14 @@ * stopper carrier. */ free_carrp = asc_dvc->irq_sp; - asc_dvc->irq_sp = ASC_GET_CARRP(irq_next_vpa); + asc_dvc->irq_sp = (ADV_CARR_T *) + ADV_U32_TO_VADDR(ASC_GET_CARRP(irq_next_vpa)); - free_carrp->next_vpa = (ulong) asc_dvc->carr_freelist; - asc_dvc->carr_freelist = (ADV_CARR_T *) free_carrp; + free_carrp->next_vpa = ADV_VADDR_TO_U32(asc_dvc->carr_freelist); + asc_dvc->carr_freelist = free_carrp; asc_dvc->carr_pending_cnt--; - - ADV_ASSERT(scsiq != NULL); + ASC_ASSERT(scsiq != NULL); target_bit = ADV_TID_TO_TIDMASK(scsiq->target_id); /* @@ -17802,22 +17912,23 @@ */ scsiq->cntl = 0; +#if __BIG_ENDIAN /* - * Check Condition handling + * After the request completes the only field in the ASC_SCSI_REQ_Q + * structure needs to be byte swapped from little endian order to + * big endian order is the residual data count. */ - if ((scsiq->done_status == QD_WITH_ERROR) && - (scsiq->scsi_status == SS_CHK_CONDITION) - ) - { - } + scsiqp->data_cnt = le32_to_cpu(scsiqp->data_cnt); +#endif /* __BIG_ENDIAN */ + /* * If the command that completed was a SCSI INQUIRY and * LUN 0 was sent the command, then process the INQUIRY * command information for the device. */ - else if (scsiq->done_status == QD_NO_ERROR && - scsiq->cdb[0] == SCSICMD_Inquiry && - scsiq->target_lun == 0) + if (scsiq->done_status == QD_NO_ERROR && + scsiq->cdb[0] == SCSICMD_Inquiry && + scsiq->target_lun == 0) { AdvInquiryHandling(asc_dvc, scsiq); } @@ -17867,11 +17978,11 @@ STATIC int AdvSendIdleCmd(ADV_DVC_VAR *asc_dvc, ushort idle_cmd, - ulong idle_cmd_parameter) + ADV_DCNT idle_cmd_parameter) { int last_int_level; int result; - ulong i, j; + ADV_DCNT i, j; AdvPortAddr iop_base; last_int_level = DvcEnterCritical(); @@ -17926,7 +18037,7 @@ } } - ADV_ASSERT(0); /* The idle command should never timeout. */ + ASC_ASSERT(0); /* The idle command should never timeout. */ DvcLeaveCritical(last_int_level); return ADV_ERROR; } diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/advansys.h linux/drivers/scsi/advansys.h --- v2.2.13/linux/drivers/scsi/advansys.h Tue May 11 10:36:28 1999 +++ linux/drivers/scsi/advansys.h Tue Jan 4 10:12:19 2000 @@ -1,4 +1,4 @@ -/* $Id: advansys.h,v 1.17 1998/01/08 21:23:49 bobf Exp bobf $ */ +/* $Id: advansys.h,v 1.18 1999/11/29 21:47:16 bobf Exp bobf $ */ /* * advansys.h - Linux Host Driver for AdvanSys SCSI Adapters @@ -49,7 +49,9 @@ int advansys_biosparam(Disk *, int, int[]); #else /* version >= v1.3.0 */ int advansys_biosparam(Disk *, kdev_t, int[]); +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,3,28) extern struct proc_dir_entry proc_scsi_advansys; +#endif /* version < v2.3.28 */ int advansys_proc_info(char *, char **, off_t, int, int, int); #endif /* version >= v1.3.0 */ @@ -142,7 +144,7 @@ */ \ ENABLE_CLUSTERING, /* unsigned use_clustering:1 */ \ } -#else /* version >= v2.1.75 */ +#elif LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,3,28) #define ADVANSYS { \ proc_dir: &proc_scsi_advansys, \ proc_info: advansys_proc_info, \ @@ -154,7 +156,7 @@ queuecommand: advansys_queuecommand, \ abort: advansys_abort, \ reset: advansys_reset, \ - bios_param: advansys_biosparam, \ + bios_param: advansys_biosparam, \ /* \ * Because the driver may control an ISA adapter 'unchecked_isa_dma' \ * must be set. The flag will be cleared in advansys_detect for non-ISA \ @@ -170,5 +172,33 @@ */ \ use_clustering: ENABLE_CLUSTERING, \ } -#endif /* version >= v2.1.75 */ +#else /* version >= v2.3.28 */ +#define ADVANSYS { \ + proc_name: "advansys", \ + proc_info: advansys_proc_info, \ + name: "advansys", \ + detect: advansys_detect, \ + release: advansys_release, \ + info: advansys_info, \ + command: advansys_command, \ + queuecommand: advansys_queuecommand, \ + abort: advansys_abort, \ + reset: advansys_reset, \ + bios_param: advansys_biosparam, \ + /* \ + * Because the driver may control an ISA adapter 'unchecked_isa_dma' \ + * must be set. The flag will be cleared in advansys_detect for non-ISA \ + * adapters. Refer to the comment in scsi_module.c for more information. \ + */ \ + unchecked_isa_dma: 1, \ + /* \ + * All adapters controlled by this driver are capable of large \ + * scatter-gather lists. According to the mid-level SCSI documentation \ + * this obviates any performance gain provided by setting \ + * 'use_clustering'. But empirically while CPU utilization is increased \ + * by enabling clustering, I/O throughput increases as well. \ + */ \ + use_clustering: ENABLE_CLUSTERING, \ +} +#endif /* version >= v2.3.28 */ #endif /* _ADVANSYS_H */ diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/aha1542.c linux/drivers/scsi/aha1542.c --- v2.2.13/linux/drivers/scsi/aha1542.c Tue Jan 4 11:10:38 2000 +++ linux/drivers/scsi/aha1542.c Tue Jan 4 10:12:19 2000 @@ -482,7 +482,11 @@ } my_done = SCtmp->scsi_done; - if (SCtmp->host_scribble) scsi_free(SCtmp->host_scribble, 512); + if (SCtmp->host_scribble) + { + scsi_free(SCtmp->host_scribble, 512); + SCtmp->host_scribble = 0; + } /* Fetch the sense data, and tuck it away, in the required slot. The Adaptec automatically fetches it, and there is no guarantee that diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/aic7xxx/aic7xxx.seq linux/drivers/scsi/aic7xxx/aic7xxx.seq --- v2.2.13/linux/drivers/scsi/aic7xxx/aic7xxx.seq Wed Jun 9 16:59:34 1999 +++ linux/drivers/scsi/aic7xxx/aic7xxx.seq Tue Jan 4 10:12:19 2000 @@ -446,7 +446,8 @@ data_phase_reinit: if ((p->features & AHC_CMD_CHAN) != 0) { if ((p->features & AHC_ULTRA2) != 0) { - bmov HCNT, SCB_RESID_DCNT, 3; + bmov HADDR, SHADDR, 4; + bmov HCNT, SCB_RESID_DCNT, 3; } bmov STCNT, SCB_RESID_DCNT, 3; } else { @@ -658,11 +659,25 @@ test DFCNTRL, DIRECTION jnz ultra2_dmahalt; and DFCNTRL, ~SCSIEN; test DFCNTRL, SCSIEN jnz .; +ultra2_dmafifoflush: or DFCNTRL, FIFOFLUSH; test DFSTATUS, FIFOEMP jz . - 1; + /* + * hardware bug alert! This needless set of jumps is to + * protect against a FIFOEMP status bit glitch in the + * silicon. + */ + test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; + test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; + test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; + test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; + test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; + test DFSTATUS, MREQPEND jnz .; ultra2_dmahalt: - and DFCNTRL, ~(SCSIEN|HDMAEN); + and DFCNTRL, ~HDMAEN; test DFCNTRL, HDMAEN jnz .; + and DFCNTRL, ~SCSIEN; + test DFCNTRL, SCSIEN jnz .; bmov SCB_RESID_DCNT, STCNT, 3; mov SCB_RESID_SGCNT, SG_COUNT; or SXFRCTL0, CLRSTCNT|CLRCHN; @@ -701,8 +716,10 @@ test SSTAT0, DMADONE jnz p_command_ultra2_dma_done; test SSTAT1,PHASEMIS jz p_command_dma_loop; /* ie. underrun */ p_command_ultra2_dma_done: - and DFCNTRL, ~(SCSIEN|HDMAEN); + and DFCNTRL, ~HDMAEN; test DFCNTRL, HDMAEN jnz .; + and DFCNTRL, ~SCSIEN; + test DFCNTRL, SCSIEN jnz .; or SXFRCTL0, CLRSTCNT|CLRCHN; } jmp ITloop; diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c --- v2.2.13/linux/drivers/scsi/aic7xxx.c Tue Jan 4 11:10:38 2000 +++ linux/drivers/scsi/aic7xxx.c Tue Jan 4 10:12:19 2000 @@ -270,7 +270,7 @@ 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; -#define AIC7XXX_C_VERSION "5.1.20" +#define AIC7XXX_C_VERSION "5.1.21" #define NUMBER(arr) (sizeof(arr) / sizeof(arr[0])) #define MIN(a,b) (((a) < (b)) ? (a) : (b)) @@ -1362,6 +1362,18 @@ */ static int aic7xxx_scbram = 0; /* + * So that we can set how long each device is given as a selection timeout. + * The table of values goes like this: + * 0 - 256ms + * 1 - 128ms + * 2 - 64ms + * 3 - 32ms + * We default to 64ms because it's fast. Some old SCSI-I devices need a + * longer time. The final value has to be left shifted by 3, hence 0x10 + * is the final value. + */ +static int aic7xxx_seltime = 0x10; +/* * So that insmod can find the variable and make it point to something */ #ifdef MODULE @@ -1522,6 +1534,7 @@ { "dump_card", &aic7xxx_dump_card }, { "dump_sequencer", &aic7xxx_dump_sequencer }, { "scbram", &aic7xxx_scbram }, + { "seltime", &aic7xxx_seltime }, { "tag_info", NULL } }; @@ -1615,6 +1628,10 @@ else if (p[n] == ':') { *(options[i].flag) = simple_strtoul(p + n + 1, NULL, 0); + if(!strncmp(p, "seltime", n)) + { + *(options[i].flag) = (*(options[i].flag) % 4) << 3; + } } else if (!strncmp(p, "verbose", n)) { @@ -1623,6 +1640,10 @@ else { *(options[i].flag) = ~(*(options[i].flag)); + if(!strncmp(p, "seltime", n)) + { + *(options[i].flag) = (*(options[i].flag) % 4) << 3; + } } } } @@ -1857,10 +1878,13 @@ } } aic_outb(p, (instr.integer & 0xff), SEQRAM); + udelay(50); aic_outb(p, ((instr.integer >> 8) & 0xff), SEQRAM); + udelay(50); aic_outb(p, ((instr.integer >> 16) & 0xff), SEQRAM); + udelay(50); aic_outb(p, ((instr.integer >> 24) & 0xff), SEQRAM); - udelay(15); + udelay(50); break; default: @@ -8124,7 +8148,7 @@ aic_outb(p, p->scsi_id_b, SCSIID); scsi_conf = aic_inb(p, SCSICONF + 1); aic_outb(p, DFON | SPIOEN, SXFRCTL0); - aic_outb(p, (scsi_conf & ENSPCHK) | STIMESEL | term | + aic_outb(p, (scsi_conf & ENSPCHK) | aic7xxx_seltime | term | ENSTIMER | ACTNEGEN, SXFRCTL1); aic_outb(p, 0, SIMODE0); aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1); @@ -8148,7 +8172,7 @@ term = ((p->flags & (AHC_TERM_ENB_A|AHC_TERM_ENB_LVD)) ? STPWEN : 0); scsi_conf = aic_inb(p, SCSICONF); aic_outb(p, DFON | SPIOEN, SXFRCTL0); - aic_outb(p, (scsi_conf & ENSPCHK) | STIMESEL | term | + aic_outb(p, (scsi_conf & ENSPCHK) | aic7xxx_seltime | term | ENSTIMER | ACTNEGEN, SXFRCTL1); aic_outb(p, 0, SIMODE0); aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1); diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/aic7xxx_proc.c linux/drivers/scsi/aic7xxx_proc.c --- v2.2.13/linux/drivers/scsi/aic7xxx_proc.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/scsi/aic7xxx_proc.c Tue Jan 4 10:12:19 2000 @@ -224,6 +224,11 @@ { size += sprintf(BLS, " BIOS Memory Address: 0x%08x\n", p->bios_address); } + if( p->chip & AHC_PCI ) + { + size += sprintf(BLS, " PCI Bus 0x%02x Device 0x%02x\n", p->pci_bus, + p->pci_device_fn); + } size += sprintf(BLS, " Adapter SEEPROM Config: %s\n", (p->flags & AHC_SEEPROM_FOUND) ? "SEEPROM found and used." : ((p->flags & AHC_USEDEFAULTS) ? "SEEPROM not found, using defaults." : @@ -373,11 +378,9 @@ } else { - *start = &aic7xxx_buffer[offset]; /* Start of wanted data */ - if (size - offset < length) - { - length = size - offset; - } + *start = buffer; + length = MIN(length, size - offset); + memcpy(buffer, &aic7xxx_buffer[offset], length); } return (length); diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/aic7xxx_seq.c linux/drivers/scsi/aic7xxx_seq.c --- v2.2.13/linux/drivers/scsi/aic7xxx_seq.c Wed Jun 9 16:59:34 1999 +++ linux/drivers/scsi/aic7xxx_seq.c Tue Jan 4 10:12:19 2000 @@ -26,12 +26,12 @@ 0x00, 0x4d, 0x12, 0x70, 0x01, 0x4e, 0x9c, 0x18, 0xbf, 0x60, 0xc0, 0x08, - 0x00, 0x6a, 0x92, 0x5c, + 0x00, 0x6a, 0xa8, 0x5c, 0xff, 0x4e, 0xc8, 0x18, - 0x02, 0x6a, 0xa8, 0x5b, + 0x02, 0x6a, 0xbe, 0x5b, 0xff, 0x52, 0x20, 0x09, 0x0d, 0x6a, 0x6a, 0x00, - 0x00, 0x52, 0x1e, 0x5c, + 0x00, 0x52, 0x34, 0x5c, 0x03, 0xb0, 0x52, 0x31, 0xff, 0xb0, 0x52, 0x09, 0xff, 0xb1, 0x54, 0x09, @@ -76,7 +76,7 @@ 0x10, 0x03, 0xfc, 0x78, 0xff, 0x50, 0xc8, 0x08, 0x88, 0x6a, 0xcc, 0x00, - 0x49, 0x6a, 0x0e, 0x5c, + 0x49, 0x6a, 0x24, 0x5c, 0x01, 0x6a, 0x26, 0x01, 0xff, 0x6a, 0xca, 0x08, 0x08, 0x01, 0x02, 0x00, @@ -117,11 +117,11 @@ 0xff, 0x65, 0xca, 0x18, 0xff, 0x65, 0xd8, 0x68, 0x0a, 0x93, 0x26, 0x01, - 0x00, 0x65, 0x84, 0x5c, + 0x00, 0x65, 0x9a, 0x5c, 0x40, 0x51, 0xf0, 0x78, 0xe4, 0x6a, 0x06, 0x00, 0x08, 0x01, 0x02, 0x00, - 0x04, 0x6a, 0x40, 0x5b, + 0x04, 0x6a, 0x56, 0x5b, 0x01, 0x50, 0xa0, 0x18, 0x00, 0x50, 0xf6, 0xe0, 0xff, 0x6a, 0xa0, 0x08, @@ -147,13 +147,13 @@ 0x08, 0x6a, 0x66, 0x58, 0x80, 0x6a, 0x68, 0x00, 0x80, 0x36, 0x6c, 0x00, - 0x00, 0x65, 0xf2, 0x5b, + 0x00, 0x65, 0x08, 0x5c, 0xff, 0x3d, 0xc8, 0x08, - 0xbf, 0x64, 0x58, 0x79, - 0x80, 0x64, 0x0e, 0x72, - 0xa0, 0x64, 0x3a, 0x72, - 0xc0, 0x64, 0x32, 0x72, - 0xe0, 0x64, 0x7a, 0x72, + 0xbf, 0x64, 0x5a, 0x79, + 0x80, 0x64, 0x20, 0x72, + 0xa0, 0x64, 0x50, 0x72, + 0xc0, 0x64, 0x48, 0x72, + 0xe0, 0x64, 0x90, 0x72, 0x01, 0x6a, 0x22, 0x01, 0x00, 0x65, 0x22, 0x41, 0xf7, 0x11, 0x22, 0x08, @@ -169,16 +169,17 @@ 0xdf, 0x01, 0x02, 0x08, 0x01, 0x6a, 0x7a, 0x00, 0xff, 0x6a, 0x6c, 0x0c, + 0x04, 0x14, 0x10, 0x31, 0x03, 0xa9, 0x18, 0x31, 0x03, 0xa9, 0x10, 0x30, 0x08, 0x6a, 0xcc, 0x00, - 0xa9, 0x6a, 0x08, 0x5c, - 0x00, 0x65, 0x78, 0x41, + 0xa9, 0x6a, 0x1e, 0x5c, + 0x00, 0x65, 0x7a, 0x41, 0xa8, 0x6a, 0x6a, 0x00, 0x79, 0x6a, 0x6a, 0x00, - 0x40, 0x3d, 0x60, 0x69, + 0x40, 0x3d, 0x62, 0x69, 0x04, 0x35, 0x6a, 0x00, - 0x00, 0x65, 0x62, 0x5b, + 0x00, 0x65, 0x78, 0x5b, 0x80, 0x6a, 0xd4, 0x01, 0x10, 0x36, 0x4e, 0x69, 0x10, 0x36, 0x6c, 0x00, @@ -186,11 +187,11 @@ 0x03, 0x8c, 0x10, 0x30, 0x05, 0xa3, 0x70, 0x30, 0x88, 0x6a, 0xcc, 0x00, - 0xac, 0x6a, 0x00, 0x5c, - 0x00, 0x65, 0xfa, 0x5b, + 0xac, 0x6a, 0x16, 0x5c, + 0x00, 0x65, 0x10, 0x5c, 0x38, 0x6a, 0xcc, 0x00, - 0xa3, 0x6a, 0x04, 0x5c, - 0xff, 0x38, 0x88, 0x69, + 0xa3, 0x6a, 0x1a, 0x5c, + 0xff, 0x38, 0x8a, 0x69, 0x80, 0x02, 0x04, 0x00, 0xe7, 0x35, 0x6a, 0x08, 0x03, 0x69, 0x18, 0x31, @@ -198,51 +199,51 @@ 0xff, 0x6a, 0x10, 0x00, 0xff, 0x6a, 0x12, 0x00, 0xff, 0x6a, 0x14, 0x00, - 0x01, 0x38, 0x8c, 0x61, + 0x01, 0x38, 0x8e, 0x61, 0xbf, 0x35, 0x6a, 0x08, 0xff, 0x69, 0xca, 0x08, 0xff, 0x35, 0x26, 0x09, - 0x04, 0x0b, 0x90, 0x69, + 0x04, 0x0b, 0x92, 0x69, + 0x04, 0x0b, 0x9e, 0x69, + 0x10, 0x0c, 0x94, 0x79, 0x04, 0x0b, 0x9c, 0x69, - 0x10, 0x0c, 0x92, 0x79, - 0x04, 0x0b, 0x9a, 0x69, 0xff, 0x6a, 0xca, 0x08, - 0x00, 0x35, 0x4a, 0x5b, - 0x80, 0x02, 0xf0, 0x69, - 0xff, 0x65, 0xe0, 0x79, + 0x00, 0x35, 0x60, 0x5b, + 0x80, 0x02, 0xf2, 0x69, + 0xff, 0x65, 0xe2, 0x79, 0xff, 0x38, 0x70, 0x18, - 0xff, 0x38, 0xe0, 0x79, - 0x80, 0xea, 0xbc, 0x61, + 0xff, 0x38, 0xe2, 0x79, + 0x80, 0xea, 0xbe, 0x61, 0xef, 0x38, 0xc8, 0x18, 0x80, 0x6a, 0xc8, 0x00, - 0x00, 0x65, 0xae, 0x49, + 0x00, 0x65, 0xb0, 0x49, 0x33, 0x38, 0xc8, 0x28, 0xff, 0x64, 0xd0, 0x09, 0x04, 0x39, 0xc0, 0x31, 0x09, 0x6a, 0xd6, 0x01, - 0x80, 0xeb, 0xb4, 0x79, + 0x80, 0xeb, 0xb6, 0x79, 0xf7, 0xeb, 0xd6, 0x09, - 0x08, 0xeb, 0xb8, 0x69, + 0x08, 0xeb, 0xba, 0x69, 0x01, 0x6a, 0xd6, 0x01, 0x08, 0xe9, 0x10, 0x31, 0x03, 0x8c, 0x10, 0x30, 0x88, 0x6a, 0xcc, 0x00, - 0x39, 0x6a, 0x06, 0x5c, + 0x39, 0x6a, 0x1c, 0x5c, 0x08, 0x6a, 0x18, 0x01, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, 0x0d, 0x93, 0x26, 0x01, - 0x00, 0x65, 0x84, 0x5c, - 0x88, 0x6a, 0x74, 0x5c, - 0x00, 0x65, 0xfa, 0x5b, + 0x00, 0x65, 0x9a, 0x5c, + 0x88, 0x6a, 0x8a, 0x5c, + 0x00, 0x65, 0x10, 0x5c, 0xff, 0x6a, 0xc8, 0x08, 0x08, 0x39, 0x72, 0x18, 0x00, 0x3a, 0x74, 0x20, - 0x01, 0x0c, 0xd8, 0x79, - 0x10, 0x0c, 0x78, 0x79, + 0x01, 0x0c, 0xda, 0x79, + 0x10, 0x0c, 0x7a, 0x79, 0xff, 0x35, 0x26, 0x09, - 0x04, 0x0b, 0xde, 0x69, - 0x00, 0x65, 0xf8, 0x59, + 0x04, 0x0b, 0xe0, 0x69, + 0x00, 0x65, 0xfa, 0x59, 0x03, 0x08, 0x52, 0x31, 0xff, 0x38, 0x50, 0x09, 0xff, 0x08, 0x52, 0x09, @@ -250,265 +251,275 @@ 0xff, 0x0a, 0x56, 0x09, 0xff, 0x38, 0x50, 0x09, 0x00, 0x65, 0x22, 0x41, - 0x00, 0x65, 0xf8, 0x59, + 0x00, 0x65, 0xfa, 0x59, 0x7f, 0x02, 0x04, 0x08, 0xe1, 0x6a, 0x22, 0x01, 0x00, 0x65, 0x22, 0x41, - 0x04, 0x93, 0x02, 0x6a, + 0x04, 0x93, 0x10, 0x6a, 0xdf, 0x93, 0x26, 0x09, - 0x20, 0x93, 0xfc, 0x69, + 0x20, 0x93, 0xfe, 0x69, 0x02, 0x93, 0x26, 0x01, - 0x01, 0x94, 0xfe, 0x79, - 0xd7, 0x93, 0x26, 0x09, - 0x08, 0x93, 0x04, 0x6a, + 0x01, 0x94, 0x00, 0x7a, + 0x01, 0x94, 0x00, 0x7a, + 0x01, 0x94, 0x00, 0x7a, + 0x01, 0x94, 0x00, 0x7a, + 0x01, 0x94, 0x00, 0x7a, + 0x01, 0x94, 0x00, 0x7a, + 0x10, 0x94, 0x0e, 0x6a, + 0xf7, 0x93, 0x26, 0x09, + 0x08, 0x93, 0x12, 0x6a, + 0xdf, 0x93, 0x26, 0x09, + 0x20, 0x93, 0x16, 0x6a, 0x03, 0x08, 0x52, 0x31, 0xff, 0x38, 0x50, 0x09, 0x12, 0x01, 0x02, 0x00, 0xff, 0x6a, 0xd4, 0x0c, - 0x00, 0x65, 0x62, 0x5b, + 0x00, 0x65, 0x78, 0x5b, 0x05, 0xb4, 0x10, 0x31, 0x02, 0x6a, 0x1a, 0x31, 0x03, 0x8c, 0x10, 0x30, 0x88, 0x6a, 0xcc, 0x00, - 0xb4, 0x6a, 0x04, 0x5c, + 0xb4, 0x6a, 0x1a, 0x5c, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, - 0x00, 0x65, 0xfa, 0x5b, - 0x3d, 0x6a, 0x4a, 0x5b, + 0x00, 0x65, 0x10, 0x5c, + 0x3d, 0x6a, 0x60, 0x5b, 0xac, 0x6a, 0x26, 0x01, - 0x04, 0x0b, 0x24, 0x6a, - 0x01, 0x0b, 0x2a, 0x6a, - 0x10, 0x0c, 0x26, 0x7a, - 0xd7, 0x93, 0x26, 0x09, - 0x08, 0x93, 0x2c, 0x6a, + 0x04, 0x0b, 0x36, 0x6a, + 0x01, 0x0b, 0x3c, 0x6a, + 0x10, 0x0c, 0x38, 0x7a, + 0xf7, 0x93, 0x26, 0x09, + 0x08, 0x93, 0x3e, 0x6a, + 0xdf, 0x93, 0x26, 0x09, + 0x20, 0x93, 0x42, 0x6a, 0x12, 0x01, 0x02, 0x00, 0x00, 0x65, 0x22, 0x41, - 0x00, 0x65, 0x62, 0x5b, + 0x00, 0x65, 0x78, 0x5b, 0xff, 0x06, 0x44, 0x09, 0x00, 0x65, 0x22, 0x41, 0x10, 0x3d, 0x06, 0x00, 0xff, 0x34, 0xca, 0x08, - 0x80, 0x65, 0x5e, 0x62, + 0x80, 0x65, 0x74, 0x62, 0x0f, 0xa1, 0xca, 0x08, 0x07, 0xa1, 0xca, 0x08, 0x40, 0xa0, 0xc8, 0x08, 0x00, 0x65, 0xca, 0x00, 0x80, 0x65, 0xca, 0x00, - 0x80, 0xa0, 0x4e, 0x7a, + 0x80, 0xa0, 0x64, 0x7a, 0xff, 0x65, 0x0c, 0x08, - 0x00, 0x65, 0x60, 0x42, - 0x20, 0xa0, 0x66, 0x7a, + 0x00, 0x65, 0x76, 0x42, + 0x20, 0xa0, 0x7c, 0x7a, 0xff, 0x65, 0x0c, 0x08, - 0x00, 0x65, 0xf2, 0x5b, - 0xa0, 0x3d, 0x6e, 0x62, + 0x00, 0x65, 0x08, 0x5c, + 0xa0, 0x3d, 0x84, 0x62, 0x23, 0xa0, 0x0c, 0x08, - 0x00, 0x65, 0xf2, 0x5b, - 0xa0, 0x3d, 0x6e, 0x62, - 0x00, 0xb9, 0x66, 0x42, - 0xff, 0x65, 0x66, 0x62, + 0x00, 0x65, 0x08, 0x5c, + 0xa0, 0x3d, 0x84, 0x62, + 0x00, 0xb9, 0x7c, 0x42, + 0xff, 0x65, 0x7c, 0x62, 0xa1, 0x6a, 0x22, 0x01, 0xff, 0x6a, 0xd4, 0x08, - 0x10, 0x51, 0x6e, 0x72, + 0x10, 0x51, 0x84, 0x72, 0x40, 0x6a, 0x18, 0x00, 0xff, 0x65, 0x0c, 0x08, - 0x00, 0x65, 0xf2, 0x5b, - 0xa0, 0x3d, 0x38, 0x72, + 0x00, 0x65, 0x08, 0x5c, + 0xa0, 0x3d, 0x4e, 0x72, 0x40, 0x6a, 0x18, 0x00, 0xff, 0x34, 0xa6, 0x08, - 0x80, 0x34, 0x76, 0x62, + 0x80, 0x34, 0x8c, 0x62, 0x7f, 0xa0, 0x40, 0x09, 0x08, 0x6a, 0x68, 0x00, 0x00, 0x65, 0x22, 0x41, - 0x64, 0x6a, 0x3a, 0x5b, - 0x80, 0x64, 0xea, 0x6a, - 0x04, 0x64, 0xcc, 0x72, - 0x02, 0x64, 0xd2, 0x72, - 0x00, 0x6a, 0x94, 0x72, - 0x03, 0x64, 0xe6, 0x72, - 0x01, 0x64, 0xc8, 0x72, - 0x07, 0x64, 0x28, 0x73, - 0x08, 0x64, 0x90, 0x72, + 0x64, 0x6a, 0x50, 0x5b, + 0x80, 0x64, 0x00, 0x6b, + 0x04, 0x64, 0xe2, 0x72, + 0x02, 0x64, 0xe8, 0x72, + 0x00, 0x6a, 0xaa, 0x72, + 0x03, 0x64, 0xfc, 0x72, + 0x01, 0x64, 0xde, 0x72, + 0x07, 0x64, 0x3e, 0x73, + 0x08, 0x64, 0xa6, 0x72, 0x11, 0x6a, 0x22, 0x01, - 0x07, 0x6a, 0x2c, 0x5b, + 0x07, 0x6a, 0x42, 0x5b, 0xff, 0x06, 0xd4, 0x08, 0x00, 0x65, 0x22, 0x41, - 0xff, 0xa8, 0x98, 0x6a, - 0xff, 0xa2, 0xb0, 0x7a, + 0xff, 0xa8, 0xae, 0x6a, + 0xff, 0xa2, 0xc6, 0x7a, 0x01, 0x6a, 0x6a, 0x00, - 0x00, 0xb9, 0x1e, 0x5c, - 0xff, 0xa2, 0xb0, 0x7a, + 0x00, 0xb9, 0x34, 0x5c, + 0xff, 0xa2, 0xc6, 0x7a, 0x71, 0x6a, 0x22, 0x01, 0xff, 0x6a, 0xd4, 0x08, - 0x40, 0x51, 0xb0, 0x62, + 0x40, 0x51, 0xc6, 0x62, 0x0d, 0x6a, 0x6a, 0x00, - 0x00, 0xb9, 0x1e, 0x5c, + 0x00, 0xb9, 0x34, 0x5c, 0xff, 0x3e, 0x74, 0x09, 0xff, 0x90, 0x7c, 0x08, 0x00, 0x65, 0x50, 0x58, 0x00, 0x65, 0x34, 0x41, - 0x20, 0xa0, 0xb8, 0x6a, + 0x20, 0xa0, 0xce, 0x6a, 0xff, 0x37, 0xc8, 0x08, - 0x00, 0x6a, 0xc8, 0x5b, - 0xff, 0x6a, 0xde, 0x5b, + 0x00, 0x6a, 0xde, 0x5b, + 0xff, 0x6a, 0xf4, 0x5b, 0xff, 0xf8, 0xc8, 0x08, 0xff, 0x4f, 0xc8, 0x08, - 0x01, 0x6a, 0xc8, 0x5b, - 0x00, 0xb9, 0xde, 0x5b, + 0x01, 0x6a, 0xde, 0x5b, + 0x00, 0xb9, 0xf4, 0x5b, 0x01, 0x4f, 0x9e, 0x18, 0x02, 0x6a, 0x22, 0x01, - 0x00, 0x65, 0x8c, 0x5c, + 0x00, 0x65, 0xa2, 0x5c, 0x00, 0x65, 0x34, 0x41, 0x41, 0x6a, 0x22, 0x01, 0x00, 0x65, 0x22, 0x41, 0x04, 0xa0, 0x40, 0x01, - 0x00, 0x65, 0xa4, 0x5c, + 0x00, 0x65, 0xba, 0x5c, 0x00, 0x65, 0x34, 0x41, - 0x10, 0x36, 0x90, 0x7a, + 0x10, 0x36, 0xa6, 0x7a, 0x05, 0x38, 0x46, 0x31, 0x04, 0x14, 0x58, 0x31, 0x03, 0xa9, 0x60, 0x31, 0xa3, 0x6a, 0xcc, 0x00, - 0x38, 0x6a, 0x04, 0x5c, + 0x38, 0x6a, 0x1a, 0x5c, 0xac, 0x6a, 0xcc, 0x00, - 0x14, 0x6a, 0x06, 0x5c, - 0xa9, 0x6a, 0x08, 0x5c, - 0x00, 0x65, 0x90, 0x42, + 0x14, 0x6a, 0x1c, 0x5c, + 0xa9, 0x6a, 0x1e, 0x5c, + 0x00, 0x65, 0xa6, 0x42, 0xef, 0x36, 0x6c, 0x08, - 0x00, 0x65, 0x90, 0x42, + 0x00, 0x65, 0xa6, 0x42, 0x0f, 0x64, 0xc8, 0x08, 0x07, 0x64, 0xc8, 0x08, 0x00, 0x37, 0x6e, 0x00, 0xff, 0x6a, 0xa4, 0x00, - 0x00, 0x65, 0x98, 0x5b, - 0xff, 0x51, 0xfc, 0x72, - 0x20, 0x36, 0x06, 0x7b, - 0x00, 0x90, 0x86, 0x5b, - 0x00, 0x65, 0x08, 0x43, + 0x00, 0x65, 0xae, 0x5b, + 0xff, 0x51, 0x12, 0x73, + 0x20, 0x36, 0x1c, 0x7b, + 0x00, 0x90, 0x9c, 0x5b, + 0x00, 0x65, 0x1e, 0x43, 0xff, 0x06, 0xd4, 0x08, - 0x00, 0x65, 0xf2, 0x5b, - 0xe0, 0x3d, 0x22, 0x63, - 0x20, 0x12, 0x22, 0x63, - 0x51, 0x6a, 0x30, 0x5b, - 0x00, 0x65, 0x80, 0x5b, + 0x00, 0x65, 0x08, 0x5c, + 0xe0, 0x3d, 0x38, 0x63, + 0x20, 0x12, 0x38, 0x63, + 0x51, 0x6a, 0x46, 0x5b, + 0x00, 0x65, 0x96, 0x5b, 0xff, 0x37, 0xc8, 0x08, - 0x00, 0xa1, 0x1a, 0x63, - 0x04, 0xa0, 0x1a, 0x7b, + 0x00, 0xa1, 0x30, 0x63, + 0x04, 0xa0, 0x30, 0x7b, 0xfb, 0xa0, 0x40, 0x09, 0x80, 0x36, 0x6c, 0x00, - 0x80, 0xa0, 0x90, 0x7a, + 0x80, 0xa0, 0xa6, 0x7a, 0x7f, 0xa0, 0x40, 0x09, - 0xff, 0x6a, 0x2c, 0x5b, - 0x00, 0x65, 0x90, 0x42, - 0x04, 0xa0, 0x20, 0x7b, - 0x00, 0x65, 0xa4, 0x5c, - 0x00, 0x65, 0x22, 0x43, - 0x00, 0x65, 0x8c, 0x5c, + 0xff, 0x6a, 0x42, 0x5b, + 0x00, 0x65, 0xa6, 0x42, + 0x04, 0xa0, 0x36, 0x7b, + 0x00, 0x65, 0xba, 0x5c, + 0x00, 0x65, 0x38, 0x43, + 0x00, 0x65, 0xa2, 0x5c, 0x31, 0x6a, 0x22, 0x01, - 0x0c, 0x6a, 0x2c, 0x5b, - 0x00, 0x65, 0x90, 0x42, + 0x0c, 0x6a, 0x42, 0x5b, + 0x00, 0x65, 0xa6, 0x42, 0x61, 0x6a, 0x22, 0x01, - 0x00, 0x65, 0x90, 0x42, + 0x00, 0x65, 0xa6, 0x42, 0x10, 0x3d, 0x06, 0x00, 0xff, 0x65, 0x68, 0x0c, 0xff, 0x06, 0xd4, 0x08, - 0x01, 0x0c, 0x32, 0x7b, - 0x04, 0x0c, 0x32, 0x6b, + 0x01, 0x0c, 0x48, 0x7b, + 0x04, 0x0c, 0x48, 0x6b, 0xe0, 0x03, 0x7a, 0x08, - 0xe0, 0x3d, 0x46, 0x63, + 0xe0, 0x3d, 0x5c, 0x63, 0xff, 0x65, 0xcc, 0x08, 0xff, 0x12, 0xda, 0x0c, 0xff, 0x06, 0xd4, 0x0c, 0xff, 0x65, 0x0c, 0x08, - 0x02, 0x0b, 0x42, 0x7b, + 0x02, 0x0b, 0x58, 0x7b, 0xff, 0x6a, 0xd4, 0x0c, 0xd1, 0x6a, 0x22, 0x01, 0x00, 0x65, 0x22, 0x41, 0xff, 0x65, 0x26, 0x09, - 0x01, 0x0b, 0x5a, 0x6b, - 0x10, 0x0c, 0x4c, 0x7b, - 0x04, 0x0b, 0x54, 0x6b, + 0x01, 0x0b, 0x70, 0x6b, + 0x10, 0x0c, 0x62, 0x7b, + 0x04, 0x0b, 0x6a, 0x6b, 0xff, 0x6a, 0xca, 0x08, - 0x04, 0x93, 0x58, 0x6b, - 0x01, 0x94, 0x56, 0x7b, - 0x10, 0x94, 0x58, 0x6b, + 0x04, 0x93, 0x6e, 0x6b, + 0x01, 0x94, 0x6c, 0x7b, + 0x10, 0x94, 0x6e, 0x6b, 0xc7, 0x93, 0x26, 0x09, 0xff, 0x99, 0xd4, 0x08, - 0x38, 0x93, 0x5c, 0x6b, + 0x38, 0x93, 0x72, 0x6b, 0xff, 0x6a, 0xd4, 0x0c, - 0x80, 0x36, 0x60, 0x6b, + 0x80, 0x36, 0x76, 0x6b, 0x21, 0x6a, 0x22, 0x05, 0xff, 0x65, 0x20, 0x09, - 0xff, 0x51, 0x6e, 0x63, + 0xff, 0x51, 0x84, 0x63, 0xff, 0x37, 0xc8, 0x08, - 0xa1, 0x6a, 0x7a, 0x43, + 0xa1, 0x6a, 0x90, 0x43, 0xff, 0x51, 0xc8, 0x08, - 0xb9, 0x6a, 0x7a, 0x43, + 0xb9, 0x6a, 0x90, 0x43, 0xff, 0x90, 0xa4, 0x08, - 0xff, 0xba, 0x7e, 0x73, + 0xff, 0xba, 0x94, 0x73, 0xff, 0xba, 0x20, 0x09, 0xff, 0x65, 0xca, 0x18, - 0x00, 0x6c, 0x72, 0x63, + 0x00, 0x6c, 0x88, 0x63, 0xff, 0x90, 0xca, 0x0c, 0xff, 0x6a, 0xca, 0x04, - 0x20, 0x36, 0x92, 0x7b, - 0x00, 0x90, 0x66, 0x5b, - 0xff, 0x65, 0x92, 0x73, - 0xff, 0x52, 0x90, 0x73, + 0x20, 0x36, 0xa8, 0x7b, + 0x00, 0x90, 0x7c, 0x5b, + 0xff, 0x65, 0xa8, 0x73, + 0xff, 0x52, 0xa6, 0x73, 0xff, 0xba, 0xcc, 0x08, 0xff, 0x52, 0x20, 0x09, 0xff, 0x66, 0x74, 0x09, 0xff, 0x65, 0x20, 0x0d, 0xff, 0xba, 0x7e, 0x0c, - 0x00, 0x6a, 0x92, 0x5c, + 0x00, 0x6a, 0xa8, 0x5c, 0x0d, 0x6a, 0x6a, 0x00, - 0x00, 0x51, 0x1e, 0x44, - 0xff, 0x3f, 0xec, 0x73, + 0x00, 0x51, 0x34, 0x44, + 0xff, 0x3f, 0x02, 0x74, 0xff, 0x6a, 0xa2, 0x00, - 0x00, 0x3f, 0x66, 0x5b, - 0xff, 0x65, 0xec, 0x73, + 0x00, 0x3f, 0x7c, 0x5b, + 0xff, 0x65, 0x02, 0x74, 0x20, 0x36, 0x6c, 0x00, - 0x20, 0xa0, 0xa6, 0x6b, + 0x20, 0xa0, 0xbc, 0x6b, 0xff, 0xb9, 0xa2, 0x0c, 0xff, 0x6a, 0xa2, 0x04, 0xff, 0x65, 0xa4, 0x08, 0xe0, 0x6a, 0xcc, 0x00, - 0x45, 0x6a, 0x12, 0x5c, + 0x45, 0x6a, 0x28, 0x5c, 0x01, 0x6a, 0xd0, 0x01, 0x09, 0x6a, 0xd6, 0x01, - 0x80, 0xeb, 0xb2, 0x7b, + 0x80, 0xeb, 0xc8, 0x7b, 0x01, 0x6a, 0xd6, 0x01, 0x01, 0xe9, 0xa4, 0x34, 0x88, 0x6a, 0xcc, 0x00, - 0x45, 0x6a, 0x12, 0x5c, + 0x45, 0x6a, 0x28, 0x5c, 0x01, 0x6a, 0x18, 0x01, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, 0x0d, 0x6a, 0x26, 0x01, - 0x00, 0x65, 0x84, 0x5c, + 0x00, 0x65, 0x9a, 0x5c, 0xff, 0x99, 0xa4, 0x0c, 0xff, 0x65, 0xa4, 0x08, 0xe0, 0x6a, 0xcc, 0x00, - 0x45, 0x6a, 0x12, 0x5c, + 0x45, 0x6a, 0x28, 0x5c, 0x01, 0x6a, 0xd0, 0x01, 0x01, 0x6a, 0xdc, 0x05, 0x88, 0x6a, 0xcc, 0x00, - 0x45, 0x6a, 0x12, 0x5c, + 0x45, 0x6a, 0x28, 0x5c, 0x01, 0x6a, 0x18, 0x01, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, 0x01, 0x6a, 0x26, 0x05, 0x01, 0x65, 0xd8, 0x31, 0x09, 0xee, 0xdc, 0x01, - 0x80, 0xee, 0xe2, 0x7b, + 0x80, 0xee, 0xf8, 0x7b, 0xff, 0x6a, 0xdc, 0x0d, 0xff, 0x65, 0x32, 0x09, 0x0a, 0x93, 0x26, 0x01, - 0x00, 0x65, 0x84, 0x44, + 0x00, 0x65, 0x9a, 0x44, 0xff, 0x37, 0xc8, 0x08, - 0x00, 0x6a, 0xa8, 0x5b, + 0x00, 0x6a, 0xbe, 0x5b, 0xff, 0x52, 0xa2, 0x0c, - 0x01, 0x0c, 0xf2, 0x7b, - 0x04, 0x0c, 0xf2, 0x6b, + 0x01, 0x0c, 0x08, 0x7c, + 0x04, 0x0c, 0x08, 0x6c, 0xe0, 0x03, 0x06, 0x08, 0xe0, 0x03, 0x7a, 0x0c, 0xff, 0x8c, 0x10, 0x08, @@ -531,29 +542,29 @@ 0x00, 0x6c, 0xda, 0x24, 0xff, 0x65, 0xc8, 0x08, 0xe0, 0x6a, 0xcc, 0x00, - 0x41, 0x6a, 0x0e, 0x5c, + 0x41, 0x6a, 0x24, 0x5c, 0xff, 0x90, 0xe2, 0x09, 0x20, 0x6a, 0xd0, 0x01, - 0x04, 0x35, 0x30, 0x7c, + 0x04, 0x35, 0x46, 0x7c, 0x1d, 0x6a, 0xdc, 0x01, - 0xdc, 0xee, 0x2c, 0x64, - 0x00, 0x65, 0x3c, 0x44, + 0xdc, 0xee, 0x42, 0x64, + 0x00, 0x65, 0x52, 0x44, 0x01, 0x6a, 0xdc, 0x01, 0x20, 0xa0, 0xd8, 0x31, 0x09, 0xee, 0xdc, 0x01, - 0x80, 0xee, 0x36, 0x7c, + 0x80, 0xee, 0x4c, 0x7c, 0x19, 0x6a, 0xdc, 0x01, - 0xd8, 0xee, 0x3a, 0x64, + 0xd8, 0xee, 0x50, 0x64, 0xff, 0x6a, 0xdc, 0x09, - 0x18, 0xee, 0x3e, 0x6c, + 0x18, 0xee, 0x54, 0x6c, 0xff, 0x6a, 0xd4, 0x0c, 0x88, 0x6a, 0xcc, 0x00, - 0x41, 0x6a, 0x0e, 0x5c, + 0x41, 0x6a, 0x24, 0x5c, 0x20, 0x6a, 0x18, 0x01, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, 0xff, 0x35, 0x26, 0x09, - 0x04, 0x35, 0x68, 0x6c, + 0x04, 0x35, 0x7e, 0x6c, 0xa0, 0x6a, 0xca, 0x00, 0x20, 0x65, 0xc8, 0x18, 0xff, 0x6c, 0x32, 0x09, @@ -564,14 +575,14 @@ 0xff, 0x6c, 0x32, 0x09, 0xff, 0x6c, 0x32, 0x09, 0xff, 0x6c, 0x32, 0x09, - 0x00, 0x65, 0x54, 0x64, + 0x00, 0x65, 0x6a, 0x64, 0x0a, 0x93, 0x26, 0x01, - 0x00, 0x65, 0x84, 0x5c, - 0x04, 0x35, 0x60, 0x7b, - 0xa0, 0x6a, 0x74, 0x5c, - 0x00, 0x65, 0x76, 0x5c, - 0x00, 0x65, 0x76, 0x5c, - 0x00, 0x65, 0x76, 0x44, + 0x00, 0x65, 0x9a, 0x5c, + 0x04, 0x35, 0x76, 0x7b, + 0xa0, 0x6a, 0x8a, 0x5c, + 0x00, 0x65, 0x8c, 0x5c, + 0x00, 0x65, 0x8c, 0x5c, + 0x00, 0x65, 0x8c, 0x44, 0xff, 0x65, 0xcc, 0x08, 0xff, 0x99, 0xda, 0x08, 0xff, 0x99, 0xda, 0x08, @@ -580,19 +591,19 @@ 0xff, 0x99, 0xda, 0x08, 0xff, 0x99, 0xda, 0x08, 0xff, 0x99, 0xda, 0x0c, - 0x08, 0x94, 0x84, 0x7c, + 0x08, 0x94, 0x9a, 0x7c, 0xf7, 0x93, 0x26, 0x09, - 0x08, 0x93, 0x88, 0x6c, + 0x08, 0x93, 0x9e, 0x6c, 0xff, 0x6a, 0xd4, 0x0c, 0xff, 0x40, 0x74, 0x09, 0xff, 0x90, 0x80, 0x08, 0xff, 0x6a, 0x72, 0x05, - 0xff, 0x40, 0xa0, 0x64, - 0xff, 0x3f, 0x98, 0x64, + 0xff, 0x40, 0xb6, 0x64, + 0xff, 0x3f, 0xae, 0x64, 0xff, 0x6a, 0xca, 0x04, 0xff, 0x3f, 0x20, 0x09, 0x01, 0x6a, 0x6a, 0x00, - 0x00, 0xb9, 0x1e, 0x5c, + 0x00, 0xb9, 0x34, 0x5c, 0xff, 0xba, 0x7e, 0x0c, 0xff, 0x40, 0x20, 0x09, 0xff, 0xba, 0x80, 0x0c, @@ -739,60 +750,60 @@ { aic7xxx_patch1_func, 68, 60, 1 }, { aic7xxx_patch8_func, 162, 1, 2 }, { aic7xxx_patch0_func, 163, 2, 1 }, - { aic7xxx_patch2_func, 167, 2, 3 }, - { aic7xxx_patch8_func, 167, 1, 1 }, - { aic7xxx_patch0_func, 169, 2, 1 }, - { aic7xxx_patch8_func, 172, 1, 2 }, - { aic7xxx_patch0_func, 173, 1, 1 }, - { aic7xxx_patch2_func, 177, 1, 1 }, - { aic7xxx_patch2_func, 180, 3, 2 }, - { aic7xxx_patch0_func, 183, 5, 1 }, - { aic7xxx_patch2_func, 191, 2, 3 }, - { aic7xxx_patch8_func, 191, 1, 1 }, - { aic7xxx_patch0_func, 193, 3, 1 }, - { aic7xxx_patch10_func, 196, 2, 1 }, - { aic7xxx_patch8_func, 198, 7, 2 }, - { aic7xxx_patch0_func, 205, 1, 1 }, - { aic7xxx_patch2_func, 210, 14, 3 }, - { aic7xxx_patch10_func, 223, 1, 1 }, - { aic7xxx_patch0_func, 224, 9, 1 }, - { aic7xxx_patch8_func, 238, 2, 1 }, - { aic7xxx_patch8_func, 240, 1, 1 }, - { aic7xxx_patch10_func, 241, 6, 3 }, - { aic7xxx_patch2_func, 241, 2, 2 }, - { aic7xxx_patch0_func, 243, 4, 1 }, - { aic7xxx_patch8_func, 248, 1, 1 }, - { aic7xxx_patch8_func, 252, 11, 1 }, - { aic7xxx_patch2_func, 264, 3, 3 }, - { aic7xxx_patch10_func, 266, 1, 1 }, - { aic7xxx_patch0_func, 267, 5, 1 }, - { aic7xxx_patch10_func, 272, 1, 2 }, - { aic7xxx_patch0_func, 273, 7, 1 }, - { aic7xxx_patch11_func, 287, 1, 2 }, - { aic7xxx_patch0_func, 288, 1, 1 }, - { aic7xxx_patch5_func, 348, 1, 2 }, - { aic7xxx_patch0_func, 349, 1, 1 }, - { aic7xxx_patch3_func, 352, 1, 1 }, - { aic7xxx_patch2_func, 362, 3, 2 }, - { aic7xxx_patch0_func, 365, 5, 1 }, - { aic7xxx_patch11_func, 373, 1, 2 }, - { aic7xxx_patch0_func, 374, 1, 1 }, - { aic7xxx_patch6_func, 379, 1, 1 }, - { aic7xxx_patch1_func, 416, 3, 1 }, - { aic7xxx_patch10_func, 421, 11, 1 }, - { aic7xxx_patch2_func, 469, 7, 2 }, - { aic7xxx_patch0_func, 476, 8, 1 }, - { aic7xxx_patch2_func, 485, 4, 2 }, - { aic7xxx_patch0_func, 489, 6, 1 }, - { aic7xxx_patch2_func, 495, 4, 2 }, - { aic7xxx_patch0_func, 499, 3, 1 }, - { aic7xxx_patch12_func, 509, 10, 1 }, - { aic7xxx_patch2_func, 528, 17, 4 }, - { aic7xxx_patch13_func, 536, 4, 2 }, - { aic7xxx_patch0_func, 540, 2, 1 }, - { aic7xxx_patch0_func, 545, 33, 1 }, - { aic7xxx_patch12_func, 578, 4, 1 }, - { aic7xxx_patch6_func, 582, 2, 1 }, - { aic7xxx_patch6_func, 585, 9, 1 }, + { aic7xxx_patch2_func, 167, 3, 3 }, + { aic7xxx_patch8_func, 167, 2, 1 }, + { aic7xxx_patch0_func, 170, 2, 1 }, + { aic7xxx_patch8_func, 173, 1, 2 }, + { aic7xxx_patch0_func, 174, 1, 1 }, + { aic7xxx_patch2_func, 178, 1, 1 }, + { aic7xxx_patch2_func, 181, 3, 2 }, + { aic7xxx_patch0_func, 184, 5, 1 }, + { aic7xxx_patch2_func, 192, 2, 3 }, + { aic7xxx_patch8_func, 192, 1, 1 }, + { aic7xxx_patch0_func, 194, 3, 1 }, + { aic7xxx_patch10_func, 197, 2, 1 }, + { aic7xxx_patch8_func, 199, 7, 2 }, + { aic7xxx_patch0_func, 206, 1, 1 }, + { aic7xxx_patch2_func, 211, 14, 3 }, + { aic7xxx_patch10_func, 224, 1, 1 }, + { aic7xxx_patch0_func, 225, 9, 1 }, + { aic7xxx_patch8_func, 239, 2, 1 }, + { aic7xxx_patch8_func, 241, 1, 1 }, + { aic7xxx_patch10_func, 242, 6, 3 }, + { aic7xxx_patch2_func, 242, 2, 2 }, + { aic7xxx_patch0_func, 244, 4, 1 }, + { aic7xxx_patch8_func, 249, 1, 1 }, + { aic7xxx_patch8_func, 253, 19, 1 }, + { aic7xxx_patch2_func, 273, 3, 3 }, + { aic7xxx_patch10_func, 275, 1, 1 }, + { aic7xxx_patch0_func, 276, 5, 1 }, + { aic7xxx_patch10_func, 281, 1, 2 }, + { aic7xxx_patch0_func, 282, 9, 1 }, + { aic7xxx_patch11_func, 298, 1, 2 }, + { aic7xxx_patch0_func, 299, 1, 1 }, + { aic7xxx_patch5_func, 359, 1, 2 }, + { aic7xxx_patch0_func, 360, 1, 1 }, + { aic7xxx_patch3_func, 363, 1, 1 }, + { aic7xxx_patch2_func, 373, 3, 2 }, + { aic7xxx_patch0_func, 376, 5, 1 }, + { aic7xxx_patch11_func, 384, 1, 2 }, + { aic7xxx_patch0_func, 385, 1, 1 }, + { aic7xxx_patch6_func, 390, 1, 1 }, + { aic7xxx_patch1_func, 427, 3, 1 }, + { aic7xxx_patch10_func, 432, 11, 1 }, + { aic7xxx_patch2_func, 480, 7, 2 }, + { aic7xxx_patch0_func, 487, 8, 1 }, + { aic7xxx_patch2_func, 496, 4, 2 }, + { aic7xxx_patch0_func, 500, 6, 1 }, + { aic7xxx_patch2_func, 506, 4, 2 }, + { aic7xxx_patch0_func, 510, 3, 1 }, + { aic7xxx_patch12_func, 520, 10, 1 }, + { aic7xxx_patch2_func, 539, 17, 4 }, + { aic7xxx_patch13_func, 547, 4, 2 }, + { aic7xxx_patch0_func, 551, 2, 1 }, + { aic7xxx_patch0_func, 556, 33, 1 }, + { aic7xxx_patch12_func, 589, 4, 1 }, + { aic7xxx_patch6_func, 593, 2, 1 }, + { aic7xxx_patch6_func, 596, 9, 1 }, }; diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/atp870u.c linux/drivers/scsi/atp870u.c --- v2.2.13/linux/drivers/scsi/atp870u.c Tue Jan 4 11:10:38 2000 +++ linux/drivers/scsi/atp870u.c Tue Jan 4 10:12:19 2000 @@ -17,9 +17,9 @@ #include #include #include +#include #include #include -#include #include #include #include "scsi.h" @@ -42,47 +42,59 @@ * static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/atp870u.c,v 1.0 1997/05/07 15:22:00 root Exp root $"; */ -static unsigned char admaxu = 1, host_idu[2], chip_veru[2], scam_on[2], global_map[2]; -static unsigned short int active_idu[2], wide_idu[2], sync_idu, ultra_map[2]; -static int workingu[2] = {0, 0}; - -static Scsi_Cmnd *querequ[2][qcnt], *curr_req[2][16]; - -static unsigned char devspu[2][16] = { - {0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}, - {0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20} -}; +static unsigned char admaxu = 1; +static unsigned short int sync_idu; -static unsigned char dirctu[2][16], last_cmd[2], in_snd[2], in_int[2]; -static unsigned char ata_cdbu[2][16]; -static unsigned int ioportu[2] = {0, 0}; static unsigned int irqnumu[2] = {0, 0}; -static unsigned short int pciportu[2]; -static unsigned long prdaddru[2][16], tran_lenu[2][16], last_lenu[2][16]; -static unsigned char prd_tableu[2][16][1024]; -static unsigned char *prd_posu[2][16]; -static unsigned char quhdu[2], quendu[2]; -static unsigned char devtypeu[2][16] = +struct atp_unit { - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + unsigned long ioport; + unsigned long irq; + unsigned long pciport; + unsigned char last_cmd; + unsigned char in_snd; + unsigned char in_int; + unsigned char quhdu; + unsigned char quendu; + unsigned char scam_on; + unsigned char global_map; + unsigned char chip_veru; + unsigned char host_idu; + int working; + unsigned short wide_idu; + unsigned short active_idu; + unsigned short ultra_map; + unsigned char ata_cdbu[16]; + Scsi_Cmnd *querequ[qcnt]; + struct atp_id + { + unsigned char dirctu; + unsigned char devspu; + unsigned char devtypeu; + unsigned long prdaddru; + unsigned long tran_lenu; + unsigned long last_lenu; + unsigned char *prd_posu; + unsigned char *prd_tableu; + Scsi_Cmnd *curr_req; + } id[16]; }; static struct Scsi_Host *atp_host[2] = {NULL, NULL}; +static struct atp_unit atp_unit[2]; static void atp870u_intr_handle(int irq, void *dev_id, struct pt_regs *regs) { unsigned long flags; unsigned short int tmpcip, id; - unsigned char i, j, h, tarid, lun; + unsigned char i, j, h, target_id, lun; unsigned char *prd; Scsi_Cmnd *workrequ; unsigned int workportu, tmport; unsigned long adrcntu, k; int errstus; + struct atp_unit *dev = dev_id; for (h = 0; h < 2; h++) { if (irq == irqnumu[h]) { @@ -91,59 +103,84 @@ } return; irq_numok: - in_int[h] = 1; - workportu = ioportu[h]; + dev->in_int = 1; + workportu = dev->ioport; tmport = workportu; - - if (workingu[h] != 0) + + if (dev->working != 0) { tmport += 0x1f; j = inb(tmport); - tmpcip = pciportu[h]; + tmpcip = dev->pciport; if ((inb(tmpcip) & 0x08) != 0) { tmpcip += 0x2; - while ((inb(tmpcip) & 0x08) != 0); + for (k=0; k < 1000; k++) + { + if ((inb(tmpcip) & 0x08) == 0) + { + goto stop_dma; + } + if ((inb(tmpcip) & 0x01) == 0) + { + goto stop_dma; + } + } } - tmpcip = pciportu[h]; +stop_dma: + tmpcip = dev->pciport; outb(0x00, tmpcip); tmport -= 0x08; i = inb(tmport); if ((j & 0x40) == 0) { - if ((last_cmd[h] & 0x40) == 0) + if ((dev->last_cmd & 0x40) == 0) { - last_cmd[h] = 0xff; + dev->last_cmd = 0xff; } } - else last_cmd[h] |= 0x40; + else dev->last_cmd |= 0x40; tmport -= 0x02; - tarid = inb(tmport); + target_id = inb(tmport); tmport += 0x02; - if ((tarid & 0x40) != 0) { - tarid = (tarid & 0x07) | 0x08; + /* + * Remap wide devices onto id numbers + */ + + if ((target_id & 0x40) != 0) { + target_id = (target_id & 0x07) | 0x08; } else { - tarid &= 0x07; + target_id &= 0x07; } + if (i == 0x85) { - if (wide_idu[h] != 0) + /* + * Flip wide + */ + if (dev->wide_idu != 0) { tmport = workportu + 0x1b; j = inb(tmport) & 0x0e; j |= 0x01; outb(j, tmport); } - if (((quhdu[h] != quendu[h]) || (last_cmd[h] != 0xff)) && - (in_snd[h] == 0)) + /* + * Issue more commands + */ + if (((dev->quhdu != dev->quendu) || (dev->last_cmd != 0xff)) && + (dev->in_snd == 0)) { send_s870(h); } - in_int[h] = 0; + /* + * Done + */ + dev->in_int = 0; return; } if (i == 0x21) @@ -153,15 +190,15 @@ ((unsigned char *) &adrcntu)[2] = inb(tmport++); ((unsigned char *) &adrcntu)[1] = inb(tmport++); ((unsigned char *) &adrcntu)[0] = inb(tmport); - k = last_lenu[h][tarid]; + k = dev->id[target_id].last_lenu; k -= adrcntu; - tran_lenu[h][tarid] = k; - last_lenu[h][tarid] = adrcntu; + dev->id[target_id].tran_lenu = k; + dev->id[target_id].last_lenu = adrcntu; tmport -= 0x04; outb(0x41, tmport); tmport += 0x08; outb(0x08, tmport); - in_int[h] = 0; + dev->in_int = 0; return; } if ((i == 0x80) || (i == 0x8f)) @@ -169,7 +206,7 @@ lun = 0; tmport -= 0x07; j = inb(tmport); - if (j == 0x44) { + if (j == 0x44 || i==0x80) { tmport += 0x0d; lun = inb(tmport) & 0x07; } else { @@ -180,71 +217,80 @@ ((unsigned char *) &adrcntu)[2] = inb(tmport++); ((unsigned char *) &adrcntu)[1] = inb(tmport++); ((unsigned char *) &adrcntu)[0] = inb(tmport); - k = last_lenu[h][tarid]; + k = dev->id[target_id].last_lenu; k -= adrcntu; - tran_lenu[h][tarid] = k; - last_lenu[h][tarid] = adrcntu; + dev->id[target_id].tran_lenu = k; + dev->id[target_id].last_lenu = adrcntu; tmport += 0x04; outb(0x08, tmport); - in_int[h] = 0; + dev->in_int = 0; return; } else { outb(0x46, tmport); - dirctu[h][tarid] = 0x00; + dev->id[target_id].dirctu = 0x00; tmport += 0x02; outb(0x00, tmport++); outb(0x00, tmport++); outb(0x00, tmport++); tmport += 0x03; outb(0x08, tmport); - in_int[h] = 0; + dev->in_int = 0; return; } } tmport = workportu + 0x10; outb(0x45, tmport); tmport += 0x06; - tarid = inb(tmport); - if ((tarid & 0x10) != 0) + target_id = inb(tmport); + /* + * Remap wide identifiers + */ + if ((target_id & 0x10) != 0) { - tarid = (tarid & 0x07) | 0x08; + target_id = (target_id & 0x07) | 0x08; } else { - tarid &= 0x07; + target_id &= 0x07; } - workrequ = curr_req[h][tarid]; + workrequ = dev->id[target_id].curr_req; tmport = workportu + 0x0f; outb(lun, tmport); tmport += 0x02; - outb(devspu[h][tarid], tmport++); - adrcntu = tran_lenu[h][tarid]; - k = last_lenu[h][tarid]; + outb(dev->id[target_id].devspu, tmport++); + adrcntu = dev->id[target_id].tran_lenu; + k = dev->id[target_id].last_lenu; outb(((unsigned char *) &k)[2], tmport++); outb(((unsigned char *) &k)[1], tmport++); outb(((unsigned char *) &k)[0], tmport++); - j = tarid; - if (tarid > 7) { + /* Remap wide */ + j = target_id; + if (target_id > 7) { j = (j & 0x07) | 0x40; } - j |= dirctu[h][tarid]; + /* Add direction */ + j |= dev->id[target_id].dirctu; outb(j, tmport++); outb(0x80, tmport); tmport = workportu + 0x1b; j = inb(tmport) & 0x0e; id = 1; - id = id << tarid; - if ((id & wide_idu[h]) != 0) { + id = id << target_id; + /* + * Is this a wide device + */ + if ((id & dev->wide_idu) != 0) { j |= 0x01; } outb(j, tmport); - if (last_lenu[h][tarid] == 0) { + + if (dev->id[target_id].last_lenu == 0) { tmport = workportu + 0x18; outb(0x08, tmport); - in_int[h] = 0; + dev->in_int = 0; return; } - prd = prd_posu[h][tarid]; + prd = dev->id[target_id].prd_posu; while (adrcntu != 0) { id = ((unsigned short int *) (prd))[2]; @@ -258,35 +304,44 @@ (k - adrcntu); ((unsigned long *) (prd))[0] += adrcntu; adrcntu = 0; - prd_posu[h][tarid] = prd; + dev->id[target_id].prd_posu = prd; } else { adrcntu -= k; - prdaddru[h][tarid] += 0x08; + dev->id[target_id].prdaddru += 0x08; prd += 0x08; if (adrcntu == 0) { - prd_posu[h][tarid] = prd; + dev->id[target_id].prd_posu = prd; } } } - tmpcip = pciportu[h] + 0x04; - outl(prdaddru[h][tarid], tmpcip); + tmpcip = dev->pciport + 0x04; + outl(dev->id[target_id].prdaddru, tmpcip); tmpcip -= 0x02; outb(0x06, tmpcip); outb(0x00, tmpcip); tmpcip -= 0x02; tmport = workportu + 0x18; - if (dirctu[h][tarid] != 0) { + /* + * Check transfer direction + */ + if (dev->id[target_id].dirctu != 0) { outb(0x08, tmport); outb(0x01, tmpcip); - in_int[h] = 0; + dev->in_int = 0; return; } outb(0x08, tmport); outb(0x09, tmpcip); - in_int[h] = 0; + dev->in_int = 0; return; } - workrequ = curr_req[h][tarid]; + + /* + * Current scsi request on this target + */ + + workrequ = dev->id[target_id].curr_req; + if (i == 0x42) { errstus = 0x02; workrequ->result = errstus; @@ -299,24 +354,36 @@ errstus = inb(tmport); workrequ->result = errstus; go_42: + /* + * Complete the command + */ spin_lock_irqsave(&io_request_lock, flags); (*workrequ->scsi_done) (workrequ); spin_unlock_irqrestore(&io_request_lock, flags); - curr_req[h][tarid] = 0; - workingu[h]--; - if (wide_idu[h] != 0) { + /* + * Clear it off the queue + */ + dev->id[target_id].curr_req = 0; + dev->working--; + /* + * Take it back wide + */ + if (dev->wide_idu != 0) { tmport = workportu + 0x1b; j = inb(tmport) & 0x0e; j |= 0x01; outb(j, tmport); } - if (((last_cmd[h] != 0xff) || (quhdu[h] != quendu[h])) && - (in_snd[h] == 0)) + /* + * If there is stuff to send and nothing going then send it + */ + if (((dev->last_cmd != 0xff) || (dev->quhdu != dev->quendu)) && + (dev->in_snd == 0)) { send_s870(h); } - in_int[h] = 0; + dev->in_int = 0; return; } if (i == 0x4f) { @@ -325,23 +392,23 @@ i &= 0x0f; if (i == 0x09) { tmpcip = tmpcip + 4; - outl(prdaddru[h][tarid], tmpcip); + outl(dev->id[target_id].prdaddru, tmpcip); tmpcip = tmpcip - 2; outb(0x06, tmpcip); outb(0x00, tmpcip); tmpcip = tmpcip - 2; tmport = workportu + 0x10; outb(0x41, tmport); - dirctu[h][tarid] = 0x00; + dev->id[target_id].dirctu = 0x00; tmport += 0x08; outb(0x08, tmport); outb(0x09, tmpcip); - in_int[h] = 0; + dev->in_int = 0; return; } if (i == 0x08) { tmpcip = tmpcip + 4; - outl(prdaddru[h][tarid], tmpcip); + outl(dev->id[target_id].prdaddru, tmpcip); tmpcip = tmpcip - 2; outb(0x06, tmpcip); outb(0x00, tmpcip); @@ -350,11 +417,11 @@ outb(0x41, tmport); tmport += 0x05; outb((unsigned char) (inb(tmport) | 0x20), tmport); - dirctu[h][tarid] = 0x20; + dev->id[target_id].dirctu = 0x20; tmport += 0x03; outb(0x08, tmport); outb(0x01, tmpcip); - in_int[h] = 0; + dev->in_int = 0; return; } tmport -= 0x07; @@ -363,20 +430,20 @@ } else { outb(0x46, tmport); } - dirctu[h][tarid] = 0x00; + dev->id[target_id].dirctu = 0x00; tmport += 0x02; outb(0x00, tmport++); outb(0x00, tmport++); outb(0x00, tmport++); tmport += 0x03; outb(0x08, tmport); - in_int[h] = 0; + dev->in_int = 0; return; } else { tmport = workportu + 0x17; inb(tmport); - workingu[h] = 0; - in_int[h] = 0; + dev->working = 0; + dev->in_int = 0; return; } } @@ -387,6 +454,7 @@ unsigned long flags; unsigned short int m; unsigned int tmport; + struct atp_unit *dev; for (h = 0; h <= admaxu; h++) { if (req_p->host == atp_host[h]) { @@ -400,9 +468,15 @@ done(req_p); return 0; } + dev = &atp_unit[h]; m = 1; m = m << req_p->target; - if ((m & active_idu[h]) == 0) { + + /* + * Fake a timeout for missing targets + */ + + if ((m & dev->active_idu) == 0) { req_p->result = 0x00040000; done(req_p); return 0; @@ -410,30 +484,36 @@ if (done) { req_p->scsi_done = done; } else { - printk("atp870u_queuecommand: done can't be NULL\n"); + printk(KERN_WARNING "atp870u_queuecommand: done can't be NULL\n"); req_p->result = 0; done(req_p); return 0; } - quendu[h]++; - if (quendu[h] >= qcnt) { - quendu[h] = 0; + /* + * Count new command + */ + dev->quendu++; + if (dev->quendu >= qcnt) { + dev->quendu = 0; } + /* + * Check queue state + */ wait_que_empty: - if (quhdu[h] == quendu[h]) { + if (dev->quhdu == dev->quendu) { goto wait_que_empty; } save_flags(flags); cli(); - querequ[h][quendu[h]] = req_p; - if (quendu[h] == 0) { + dev->querequ[dev->quendu] = req_p; + if (dev->quendu == 0) { i = qcnt - 1; } else { - i = quendu[h] - 1; + i = dev->quendu - 1; } - tmport = ioportu[h] + 0x1c; + tmport = dev->ioport + 0x1c; restore_flags(flags); - if ((inb(tmport) == 0) && (in_int[h] == 0) && (in_snd[h] == 0)) { + if ((inb(tmport) == 0) && (dev->in_int == 0) && (dev->in_snd == 0)) { send_s870(h); } return 0; @@ -453,44 +533,45 @@ Scsi_Cmnd *workrequ; unsigned long flags; unsigned int i; - unsigned char j, tarid; + unsigned char j, target_id; unsigned char *prd; unsigned short int tmpcip, w; unsigned long l, bttl; unsigned int workportu; struct scatterlist *sgpnt; + struct atp_unit *dev = &atp_unit[h]; save_flags(flags); cli(); - if (in_snd[h] != 0) { + if (dev->in_snd != 0) { restore_flags(flags); return; } - in_snd[h] = 1; - if ((last_cmd[h] != 0xff) && ((last_cmd[h] & 0x40) != 0)) { - last_cmd[h] &= 0x0f; - workrequ = curr_req[h][last_cmd[h]]; + dev->in_snd = 1; + if ((dev->last_cmd != 0xff) && ((dev->last_cmd & 0x40) != 0)) { + dev->last_cmd &= 0x0f; + workrequ = dev->id[dev->last_cmd].curr_req; goto cmd_subp; } - workingu[h]++; - j = quhdu[h]; - quhdu[h]++; - if (quhdu[h] >= qcnt) { - quhdu[h] = 0; - } - workrequ = querequ[h][quhdu[h]]; - if (curr_req[h][workrequ->target] == 0) { - curr_req[h][workrequ->target] = workrequ; - last_cmd[h] = workrequ->target; + dev->working++; + j = dev->quhdu; + dev->quhdu++; + if (dev->quhdu >= qcnt) { + dev->quhdu = 0; + } + workrequ = dev->querequ[dev->quhdu]; + if (dev->id[workrequ->target].curr_req == 0) { + dev->id[workrequ->target].curr_req = workrequ; + dev->last_cmd = workrequ->target; goto cmd_subp; } - quhdu[h] = j; - workingu[h]--; - in_snd[h] = 0; + dev->quhdu = j; + dev->working--; + dev->in_snd = 0; restore_flags(flags); return; cmd_subp: - workportu = ioportu[h]; + workportu = dev->ioport; tmport = workportu + 0x1f; if ((inb(tmport) & 0xb0) != 0) { goto abortsnd; @@ -500,43 +581,63 @@ goto oktosend; } abortsnd: - last_cmd[h] |= 0x40; - in_snd[h] = 0; + dev->last_cmd |= 0x40; + dev->in_snd = 0; restore_flags(flags); return; oktosend: - memcpy(&ata_cdbu[h][0], &workrequ->cmnd[0], workrequ->cmd_len); - if (ata_cdbu[h][0] == 0x25) { + memcpy(&dev->ata_cdbu[0], &workrequ->cmnd[0], workrequ->cmd_len); + if (dev->ata_cdbu[0] == READ_CAPACITY) { if (workrequ->request_bufflen > 8) { workrequ->request_bufflen = 0x08; } } - if (ata_cdbu[h][0] == 0x12) { + /* + * Why limit this ???? + */ + if (dev->ata_cdbu[0] == INQUIRY) { if (workrequ->request_bufflen > 0x24) { workrequ->request_bufflen = 0x24; - ata_cdbu[h][4] = 0x24; + dev->ata_cdbu[4] = 0x24; } } + tmport = workportu + 0x1b; j = inb(tmport) & 0x0e; - tarid = workrequ->target; + target_id = workrequ->target; + + /* + * Wide ? + */ w = 1; - w = w << tarid; - if ((w & wide_idu[h]) != 0) { + w = w << target_id; + if ((w & dev->wide_idu) != 0) { j |= 0x01; - } + } outb(j, tmport); + + /* + * Write the command + */ + tmport = workportu; outb(workrequ->cmd_len, tmport++); outb(0x2c, tmport++); outb(0xcf, tmport++); for (i = 0; i < workrequ->cmd_len; i++) { - outb(ata_cdbu[h][i], tmport++); + outb(dev->ata_cdbu[i], tmport++); } tmport = workportu + 0x0f; - outb(0x00, tmport); + outb(workrequ->lun, tmport); tmport += 0x02; - outb(devspu[h][tarid], tmport++); + /* + * Write the target + */ + outb(dev->id[target_id].devspu, tmport++); + + /* + * Figure out the transfer size + */ if (workrequ->use_sg) { l = 0; @@ -552,38 +653,54 @@ } else { l = workrequ->request_bufflen; } + /* + * Write transfer size + */ outb((unsigned char) (((unsigned char *) (&l))[2]), tmport++); outb((unsigned char) (((unsigned char *) (&l))[1]), tmport++); outb((unsigned char) (((unsigned char *) (&l))[0]), tmport++); - j = tarid; - last_lenu[h][j] = l; - tran_lenu[h][j] = 0; + j = target_id; + dev->id[j].last_lenu = l; + dev->id[j].tran_lenu = 0; + /* + * Flip the wide bits + */ if ((j & 0x08) != 0) { j = (j & 0x07) | 0x40; } - if ((ata_cdbu[h][0] == 0x0a) || (ata_cdbu[h][0] == 0x2a) || - (ata_cdbu[h][0] == 0xaa) || (ata_cdbu[h][0] == 0x15)) { + /* + * Check transfer direction + */ + if ((dev->ata_cdbu[0] == WRITE_6) || (dev->ata_cdbu[0] == WRITE_10) || + (dev->ata_cdbu[0] == WRITE_12) || (dev->ata_cdbu[0] == MODE_SELECT)) { outb((unsigned char) (j | 0x20), tmport++); } else { outb(j, tmport++); } + outb((unsigned char)(inb(tmport) | 0x80),tmport); outb(0x80, tmport); tmport = workportu + 0x1c; - dirctu[h][tarid] = 0; + dev->id[target_id].dirctu = 0; if (l == 0) { if (inb(tmport) == 0) { tmport = workportu + 0x18; outb(0x08, tmport); } else { - last_cmd[h] |= 0x40; + dev->last_cmd |= 0x40; } - in_snd[h] = 0; + dev->in_snd = 0; restore_flags(flags); return; } - tmpcip = pciportu[h]; - prd = &prd_tableu[h][tarid][0]; - prd_posu[h][tarid] = prd; + tmpcip = dev->pciport; + prd = dev->id[target_id].prd_tableu; + dev->id[target_id].prd_posu = prd; + + /* + * Now write the request list. Either as scatter/gather or as + * a linear chain. + */ + if (workrequ->use_sg) { sgpnt = (struct scatterlist *) workrequ->request_buffer; @@ -596,6 +713,9 @@ } (unsigned short int) (((unsigned short int *) (prd))[i - 1]) = 0x8000; } else { + /* + * For a linear request write a chain of blocks + */ bttl = virt_to_bus(workrequ->request_buffer); l = workrequ->request_bufflen; i = 0; @@ -612,24 +732,24 @@ (unsigned long) (((unsigned long *) (prd))[i >> 1]) = bttl; } tmpcip = tmpcip + 4; - prdaddru[h][tarid] = virt_to_bus(&prd_tableu[h][tarid][0]); - outl(prdaddru[h][tarid], tmpcip); + dev->id[target_id].prdaddru = virt_to_bus(dev->id[target_id].prd_tableu); + outl(dev->id[target_id].prdaddru, tmpcip); tmpcip = tmpcip - 2; outb(0x06, tmpcip); outb(0x00, tmpcip); tmpcip = tmpcip - 2; - if ((ata_cdbu[h][0] == 0x0a) || (ata_cdbu[h][0] == 0x2a) || - (ata_cdbu[h][0] == 0xaa) || (ata_cdbu[h][0] == 0x15)) + if ((dev->ata_cdbu[0] == WRITE_6) || (dev->ata_cdbu[0] == WRITE_10) || + (dev->ata_cdbu[0] == WRITE_12) || (dev->ata_cdbu[0] == MODE_SELECT)) { - dirctu[h][tarid] = 0x20; + dev->id[target_id].dirctu = 0x20; if (inb(tmport) == 0) { tmport = workportu + 0x18; outb(0x08, tmport); outb(0x01, tmpcip); } else { - last_cmd[h] |= 0x40; + dev->last_cmd |= 0x40; } - in_snd[h] = 0; + dev->in_snd = 0; restore_flags(flags); return; } @@ -639,9 +759,9 @@ outb(0x08, tmport); outb(0x09, tmpcip); } else { - last_cmd[h] |= 0x40; + dev->last_cmd |= 0x40; } - in_snd[h] = 0; + dev->in_snd = 0; restore_flags(flags); return; @@ -663,13 +783,13 @@ return SCpnt->result; } -unsigned char fun_scam(unsigned char host, unsigned short int *val) +unsigned char fun_scam(struct atp_unit *dev, unsigned short int *val) { unsigned int tmport; unsigned short int i, k; unsigned char j; - tmport = ioportu[host] + 0x1c; + tmport = dev->ioport + 0x1c; outw(*val, tmport); FUN_D7: for (i = 0; i < 10; i++) { /* stable >= bus settle delay(400 ns) */ @@ -712,32 +832,34 @@ unsigned long n; unsigned short int m, assignid_map, val; unsigned char mbuf[33], quintet[2]; - static unsigned char g2q_tab[8] = - {0x38, 0x31, 0x32, 0x2b, 0x34, 0x2d, 0x2e, 0x27}; + struct atp_unit *dev = &atp_unit[host]; + static unsigned char g2q_tab[8] = { + 0x38, 0x31, 0x32, 0x2b, 0x34, 0x2d, 0x2e, 0x27 + }; for (i = 0; i < 0x10; i++) { mydlyu(0xffff); } - tmport = ioportu[host] + 1; + tmport = dev->ioport + 1; outb(0x08, tmport++); outb(0x7f, tmport); - tmport = ioportu[host] + 0x11; + tmport = dev->ioport + 0x11; outb(0x20, tmport); - if ((scam_on[host] & 0x40) == 0) { + if ((dev->scam_on & 0x40) == 0) { return; } m = 1; - m <<= host_idu[host]; + m <<= dev->host_idu; j = 16; - if (chip_veru[host] < 4) { + if (dev->chip_veru < 4) { m |= 0xff00; j = 8; } assignid_map = m; - tmport = ioportu[host] + 0x02; + tmport = dev->ioport + 0x02; outb(0x02, tmport++); /* 2*2=4ms,3EH 2/32*3E=3.9ms */ outb(0, tmport++); outb(0, tmport++); @@ -752,7 +874,7 @@ if ((m & assignid_map) != 0) { continue; } - tmport = ioportu[host] + 0x0f; + tmport = dev->ioport + 0x0f; outb(0, tmport++); tmport += 0x02; outb(0, tmport++); @@ -764,14 +886,14 @@ k = i; } outb(k, tmport++); - tmport = ioportu[host] + 0x1b; - if (chip_veru[host] == 4) { + tmport = dev->ioport + 0x1b; + if (dev->chip_veru == 4) { outb((unsigned char) ((inb(tmport) & 0x0e) | 0x01), tmport); } else { outb((unsigned char) (inb(tmport) & 0x0e), tmport); } wait_rdyok: - tmport = ioportu[host] + 0x18; + tmport = dev->ioport + 0x18; outb(0x09, tmport); tmport += 0x07; @@ -782,22 +904,22 @@ if ((k == 0x85) || (k == 0x42)) { continue; } - tmport = ioportu[host] + 0x10; + tmport = dev->ioport + 0x10; outb(0x41, tmport); goto wait_rdyok; } assignid_map |= m; } - tmport = ioportu[host] + 0x02; + tmport = dev->ioport + 0x02; outb(0x7f, tmport); - tmport = ioportu[host] + 0x1b; + tmport = dev->ioport + 0x1b; outb(0x02, tmport); outb(0, 0x80); val = 0x0080; /* bsy */ - tmport = ioportu[host] + 0x1c; + tmport = dev->ioport + 0x1c; outw(val, tmport); val |= 0x0040; /* sel */ outw(val, tmport); @@ -842,7 +964,7 @@ if ((inb(tmport) & 0x80) == 0x00) { /* bsy ? */ outw(0, tmport--); outb(0, tmport); - tmport = ioportu[host] + 0x15; + tmport = dev->ioport + 0x15; outb(0, tmport); tmport += 0x03; outb(0x09, tmport); @@ -854,11 +976,11 @@ } val &= 0x00ff; /* synchronization */ val |= 0x3f00; - fun_scam(host, &val); + fun_scam(dev, &val); outb(3, 0x80); val &= 0x00ff; /* isolation */ val |= 0x2000; - fun_scam(host, &val); + fun_scam(dev, &val); outb(4, 0x80); i = 8; j = 0; @@ -869,7 +991,7 @@ outb(5, 0x80); val &= 0x00ff; /* get ID_STRING */ val |= 0x2000; - k = fun_scam(host, &val); + k = fun_scam(dev, &val); if ((k & 0x03) == 0) { goto TCM_5; } @@ -933,11 +1055,11 @@ val &= 0x00ff; /* AssignID 1stQuintet,AH=001xxxxx */ m = quintet[0] << 8; val |= m; - fun_scam(host, &val); + fun_scam(dev, &val); val &= 0x00ff; /* AssignID 2ndQuintet,AH=001xxxxx */ m = quintet[1] << 8; val |= m; - fun_scam(host, &val); + fun_scam(dev, &val); goto TCM_SYNC; @@ -955,25 +1077,26 @@ static unsigned char synu[6] = {0x80, 1, 3, 1, 0x0c, 0x0e}; static unsigned char synw[6] = {0x80, 1, 3, 1, 0x0c, 0x07}; static unsigned char wide[6] = {0x80, 1, 2, 3, 1, 0}; + struct atp_unit *dev = &atp_unit[host]; sync_idu = 0; tmport = wkport + 0x3a; outb((unsigned char) (inb(tmport) | 0x10), tmport); for (i = 0; i < 16; i++) { - if ((chip_veru[host] != 4) && (i > 7)) { + if ((dev->chip_veru != 4) && (i > 7)) { break; } m = 1; m = m << i; - if ((m & active_idu[host]) != 0) { + if ((m & dev->active_idu) != 0) { continue; } - if (i == host_idu[host]) { - printk(" ID: %2d Host Adapter\n", host_idu[host]); + if (i == dev->host_idu) { + printk(KERN_INFO " ID: %2d Host Adapter\n", dev->host_idu); continue; } - if (chip_veru[host] == 4) { + if (dev->chip_veru == 4) { tmport = wkport + 0x1b; j = (inb(tmport) & 0x0e) | 0x01; outb(j, tmport); @@ -990,7 +1113,7 @@ tmport += 0x06; outb(0, tmport); tmport += 0x02; - outb(devspu[host][i], tmport++); + outb(dev->id[i].devspu, tmport++); outb(0, tmport++); outb(satn[6], tmport++); outb(satn[7], tmport++); @@ -1009,7 +1132,7 @@ continue; } while (inb(tmport) != 0x8e); - active_idu[host] |= m; + dev->active_idu |= m; tmport = wkport + 0x10; outb(0x30, tmport); @@ -1039,7 +1162,7 @@ tmport += 0x07; outb(0, tmport); tmport += 0x02; - outb(devspu[host][i], tmport++); + outb(dev->id[i].devspu, tmport++); outb(0, tmport++); outb(inqd[6], tmport++); outb(inqd[7], tmport++); @@ -1052,7 +1175,7 @@ continue; } while (inb(tmport) != 0x8e); - if (chip_veru[host] == 4) { + if (dev->chip_veru == 4) { tmport = wkport + 0x1b; j = inb(tmport) & 0x0e; outb(j, tmport); @@ -1093,16 +1216,16 @@ } inq_ok: mbuf[36] = 0; - printk(" ID: %2d %s\n", i, &mbuf[8]); - devtypeu[host][i] = mbuf[0]; + printk(KERN_INFO " ID: %2d %s\n", i, &mbuf[8]); + dev->id[i].devtypeu = mbuf[0]; rmb = mbuf[1]; - if (chip_veru[host] != 4) { + if (dev->chip_veru != 4) { goto not_wide; } if ((mbuf[7] & 0x60) == 0) { goto not_wide; } - if ((global_map[host] & 0x20) == 0) { + if ((dev->global_map & 0x20) == 0) { goto not_wide; } tmport = wkport + 0x1b; @@ -1118,7 +1241,7 @@ tmport += 0x06; outb(0, tmport); tmport += 0x02; - outb(devspu[host][i], tmport++); + outb(dev->id[i].devspu, tmport++); outb(0, tmport++); outb(satn[6], tmport++); outb(satn[7], tmport++); @@ -1244,16 +1367,16 @@ } m = 1; m = m << i; - wide_idu[host] |= m; + dev->wide_idu |= m; not_wide: - if ((devtypeu[host][i] == 0x00) || (devtypeu[host][i] == 0x07)) { + if ((dev->id[i].devtypeu == 0x00) || (dev->id[i].devtypeu == 0x07)) { goto set_sync; } continue; set_sync: tmport = wkport + 0x1b; j = inb(tmport) & 0x0e; - if ((m & wide_idu[host]) != 0) { + if ((m & dev->wide_idu) != 0) { j |= 0x01; } outb(j, tmport); @@ -1267,7 +1390,7 @@ tmport += 0x06; outb(0, tmport); tmport += 0x02; - outb(devspu[host][i], tmport++); + outb(dev->id[i].devspu, tmport++); outb(0, tmport++); outb(satn[6], tmport++); outb(satn[7], tmport++); @@ -1295,10 +1418,10 @@ if (rmb != 0) { outb(synn[j++], tmport); } else { - if ((m & wide_idu[host]) != 0) { + if ((m & dev->wide_idu) != 0) { outb(synw[j++], tmport); } else { - if ((m & ultra_map[host]) != 0) { + if ((m & dev->ultra_map) != 0) { outb(synu[j++], tmport); } else { outb(synn[j++], tmport); @@ -1413,7 +1536,7 @@ if (mbuf[4] > 0x0c) { mbuf[4] = 0x0c; } - devspu[host][i] = mbuf[4]; + dev->id[i].devspu = mbuf[4]; if ((mbuf[3] < 0x0d) && (rmb == 0)) { j = 0xa0; goto set_syn_ok; @@ -1432,7 +1555,7 @@ } j = 0x60; set_syn_ok: - devspu[host][i] = (devspu[host][i] & 0x0f) | j; + dev->id[i].devspu = (dev->id[i].devspu & 0x0f) | j; } tmport = wkport + 0x3a; outb((unsigned char) (inb(tmport) & 0xef), tmport); @@ -1445,124 +1568,123 @@ unsigned long flags; unsigned int base_io, error, tmport; unsigned short index = 0; - unsigned char pci_bus[3], pci_device_fn[3], chip_ver[3], host_id; + struct pci_dev *pdev[3]; + unsigned char chip_ver[3], host_id; struct Scsi_Host *shpnt = NULL; + int tmpcnt = 0; int count = 0; - static unsigned short devid[7] = - {0x8002, 0x8010, 0x8020, 0x8030, 0x8040, 0x8050, 0}; - static struct pci_dev *pdev = NULL, *acard_pdev[3]; + int result; + + static unsigned short devid[7] = { + 0x8002, 0x8010, 0x8020, 0x8030, 0x8040, 0x8050, 0 + }; - printk("aec671x_detect: \n"); + printk(KERN_INFO "aec671x_detect: \n"); if (!pci_present()) { - printk(" NO BIOS32 SUPPORT.\n"); + printk(KERN_INFO" NO PCI SUPPORT.\n"); return count; } - tpnt->proc_dir = &proc_scsi_atp870u; for (h = 0; h < 2; h++) { - active_idu[h] = 0; - wide_idu[h] = 0; - host_idu[h] = 0x07; - quhdu[h] = 0; - quendu[h] = 0; - pci_bus[h] = 0; - pci_device_fn[h] = 0xff; - chip_ver[h] = 0; - last_cmd[h] = 0xff; - in_snd[h] = 0; - in_int[h] = 0; + struct atp_unit *dev = &atp_unit[h]; + for(k=0;k<16;k++) + { + dev->id[k].prd_tableu = kmalloc(1024, GFP_KERNEL); + dev->id[k].devspu=0x20; + dev->id[k].devtypeu = 0; + dev->id[k].curr_req = NULL; + } + dev->active_idu = 0; + dev->wide_idu = 0; + dev->host_idu = 0x07; + dev->quhdu = 0; + dev->quendu = 0; + pdev[h]=NULL; + pdev[2]=NULL; + dev->chip_veru = 0; + dev->last_cmd = 0xff; + dev->in_snd = 0; + dev->in_int = 0; for (k = 0; k < qcnt; k++) { - querequ[h][k] = 0; + dev->querequ[k] = 0; } for (k = 0; k < 16; k++) { - curr_req[h][k] = 0; + dev->id[k].curr_req = 0; } } h = 0; while (devid[h] != 0) { - pdev = pci_find_device(0x1191, devid[h], pdev); - if (pdev == NULL) { + pdev[2] = pci_find_device(0x1191, devid[h], pdev[2]); + if (pdev[2] == NULL) { h++; index = 0; continue; } chip_ver[2] = 0; - /* To avoid messing with the things below... */ - acard_pdev[2] = pdev; - pci_device_fn[2] = pdev->devfn; - pci_bus[2] = pdev->bus->number; - if (devid[h] == 0x8002) { - error = pci_read_config_byte(pdev, 0x08, &chip_ver[2]); + error = pci_read_config_byte(pdev[2], 0x08, &chip_ver[2]); if (chip_ver[2] < 2) { goto nxt_devfn; } } - if (devid[h] == 0x8010) { + if (devid[h] == 0x8010 || devid[h] == 0x8050) { chip_ver[2] = 0x04; } - if (pci_device_fn[2] < pci_device_fn[0]) { - acard_pdev[1] = acard_pdev[0]; - pci_bus[1] = pci_bus[0]; - pci_device_fn[1] = pci_device_fn[0]; - chip_ver[1] = chip_ver[0]; - acard_pdev[0] = acard_pdev[2]; - pci_bus[0] = pci_bus[2]; - pci_device_fn[0] = pci_device_fn[2]; - chip_ver[0] = chip_ver[2]; - } else if (pci_device_fn[2] < pci_device_fn[1]) { - acard_pdev[1] = acard_pdev[2]; - pci_bus[1] = pci_bus[2]; - pci_device_fn[1] = pci_device_fn[2]; - chip_ver[1] = chip_ver[2]; - } + pdev[tmpcnt] = pdev[2]; + chip_ver[tmpcnt] = chip_ver[2]; + tmpcnt++; nxt_devfn: index++; if (index > 3) { index = 0; h++; } + if(tmpcnt>1) + break; } for (h = 0; h < 2; h++) { - if (pci_device_fn[h] == 0xff) { + struct atp_unit *dev=&atp_unit[h]; + if (pdev[h]==NULL) { return count; } - pdev = acard_pdev[h]; - pdev->devfn = pci_device_fn[h]; - pdev->bus->number = pci_bus[h]; /* Found an atp870u/w. */ - error = pci_read_config_dword(pdev, 0x10, &base_io); - error += pci_read_config_byte(pdev, 0x3c, &irq); - error += pci_read_config_byte(pdev, 0x49, &host_id); + base_io = pdev[h]->base_address[0]; + irq = pdev[h]->irq; + error = pci_read_config_byte(pdev[h],0x49,&host_id); base_io &= 0xfffffff8; - printk(" ACARD AEC-671X PCI Ultra/W SCSI-3 Host Adapter: %d IO:%x, IRQ:%d.\n" + + if (check_region(base_io,0x40) != 0) + { + return 0; + } + printk(KERN_INFO " ACARD AEC-671X PCI Ultra/W SCSI-3 Host Adapter: %d IO:%x, IRQ:%d.\n" ,h, base_io, irq); - ioportu[h] = base_io; - pciportu[h] = base_io + 0x20; + dev->ioport = base_io; + dev->pciport = base_io + 0x20; irqnumu[h] = irq; host_id &= 0x07; - host_idu[h] = host_id; - chip_veru[h] = chip_ver[h]; + dev->host_idu = host_id; + dev->chip_veru = chip_ver[h]; tmport = base_io + 0x22; - scam_on[h] = inb(tmport); + dev->scam_on = inb(tmport); tmport += 0x0b; - global_map[h] = inb(tmport++); - ultra_map[h] = inw(tmport); - if (ultra_map[h] == 0) { - scam_on[h] = 0x00; - global_map[h] = 0x20; - ultra_map[h] = 0xffff; + dev->global_map = inb(tmport++); + dev->ultra_map = inw(tmport); + if (dev->ultra_map == 0) { + dev->scam_on = 0x00; + dev->global_map = 0x20; + dev->ultra_map = 0xffff; } shpnt = scsi_register(tpnt, 4); save_flags(flags); cli(); - if (request_irq(irq, atp870u_intr_handle, 0, "atp870u", NULL)) { - printk("Unable to allocate IRQ for Acard controller.\n"); + if (request_irq(irq, atp870u_intr_handle, SA_SHIRQ, "atp870u", dev)) { + printk(KERN_ERR "Unable to allocate IRQ for Acard controller.\n"); goto unregister; } tmport = base_io + 0x3a; @@ -1590,9 +1712,11 @@ is870(h, base_io); tmport = base_io + 0x3a; outb((inb(tmport) & 0xef), tmport); + tmport++; + outb((inb(tmport) | 0x20),tmport); atp_host[h] = shpnt; - if (chip_ver[h] == 4) { + if (dev->chip_veru == 4) { shpnt->max_id = 16; } shpnt->this_id = host_id; @@ -1623,7 +1747,7 @@ { unsigned char h, j; unsigned int tmport; -/* printk(" atp870u_abort: \n"); */ + struct atp_unit *dev; for (h = 0; h <= admaxu; h++) { if (SCpnt->host == atp_host[h]) { goto find_adp; @@ -1631,20 +1755,23 @@ } panic("Abort host not found !"); find_adp: - printk(" workingu=%x last_cmd=%x ", workingu[h], last_cmd[h]); - printk(" quhdu=%x quendu=%x ", quhdu[h], quendu[h]); - tmport = ioportu[h]; + dev=&atp_unit[h]; + printk(KERN_DEBUG "working=%x last_cmd=%x ", dev->working, dev->last_cmd); + printk(" quhdu=%x quendu=%x ", dev->quhdu, dev->quendu); + tmport = dev->ioport; for (j = 0; j < 0x17; j++) { printk(" r%2x=%2x", j, inb(tmport++)); } tmport += 0x05; printk(" r1c=%2x", inb(tmport)); tmport += 0x03; - printk(" r1f=%2x in_snd=%2x ", inb(tmport), in_snd[h]); + printk(" r1f=%2x in_snd=%2x ", inb(tmport), dev->in_snd); tmport++; printk(" r20=%2x", inb(tmport)); tmport += 0x02; - printk(" r22=%2x \n", inb(tmport)); + printk(" r22=%2x", inb(tmport)); + tmport += 0x18; + printk(" r3a=%2x \n",inb(tmport)); return (SCSI_ABORT_SNOOZE); } @@ -1654,7 +1781,6 @@ /* * See if a bus reset was suggested. */ -/* printk("atp870u_reset: \n"); */ for (h = 0; h <= admaxu; h++) { if (SCpnt->host == atp_host[h]) { goto find_host; @@ -1664,9 +1790,9 @@ find_host: /* SCpnt->result = 0x00080000; SCpnt->scsi_done(SCpnt); - workingu[h]=0; - quhdu[h]=0; - quendu[h]=0; + dev->working=0; + dev->quhdu=0; + dev->quendu=0; return (SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET); */ return (SCSI_RESET_SNOOZE); } @@ -1675,14 +1801,14 @@ { static char buffer[128]; - strcpy(buffer, "ACARD AEC-6710/6712 PCI Ultra/W SCSI-3 Adapter Driver V1.0 "); + strcpy(buffer, "ACARD AEC-6710/6712 PCI Ultra/W SCSI-3 Adapter Driver V2.0+ac "); return buffer; } int atp870u_set_info(char *buffer, int length, struct Scsi_Host *HBAptr) { - return (-ENOSYS); /* Currently this is a no-op */ + return -ENOSYS; /* Currently this is a no-op */ } #define BLS buffer + len + size @@ -1720,7 +1846,7 @@ if (offset == 0) { memset(buff, 0, sizeof(buff)); } - size += sprintf(BLS, "ACARD AEC-671X Driver Version: 1.0\n"); + size += sprintf(BLS, "ACARD AEC-671X Driver Version: 2.0+ac\n"); len += size; pos = begin + len; size = 0; @@ -1733,7 +1859,7 @@ pos = begin + len; size = 0; - stop_output: +stop_output: *start = buffer + (offset - begin); /* Start of wanted data */ len -= (offset - begin); /* Start slop */ if (len > length) { @@ -1762,6 +1888,26 @@ ip[2] = cylinders; return 0; +} + + +int atp870u_release (struct Scsi_Host *pshost) +{ + int h; + for (h = 0; h <= admaxu; h++) + { + if (pshost == atp_host[h]) { + int k; + free_irq (pshost->irq, &atp_unit[h]); + release_region (pshost->io_port, pshost->n_io_port); + scsi_unregister(pshost); + for(k=0;k<16;k++) + kfree(atp_unit[h].id[k].prd_tableu); + return 0; + } + } + panic("atp870u: bad scsi host passed.\n"); + } #ifdef MODULE diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/atp870u.h linux/drivers/scsi/atp870u.h --- v2.2.13/linux/drivers/scsi/atp870u.h Tue Jan 4 11:10:38 2000 +++ linux/drivers/scsi/atp870u.h Tue Jan 4 10:12:19 2000 @@ -24,49 +24,50 @@ int atp870u_abort(Scsi_Cmnd *); int atp870u_reset(Scsi_Cmnd *, unsigned int); int atp870u_biosparam(Disk *, kdev_t, int *); +int atp870u_release(struct Scsi_Host *); void send_s870(unsigned char); -#define qcnt 32 -#define ATP870U_SCATTER 127 +#define qcnt 32 +#define ATP870U_SCATTER 128 #define ATP870U_CMDLUN 1 #ifndef NULL #define NULL 0 #endif -extern struct proc_dir_entry proc_scsi_atp870u; - extern const char *atp870u_info(struct Scsi_Host *); extern int atp870u_proc_info(char *, char **, off_t, int, int, int); +extern struct proc_dir_entry proc_scsi_atp870u; -#define ATP870U { \ - next: NULL, \ - module: NULL, \ +#define ATP870U { \ + next: NULL, \ + module: NULL, \ proc_dir: &proc_scsi_atp870u, \ - proc_info: atp870u_proc_info, \ - name: NULL, \ - detect: atp870u_detect, \ - release: NULL, \ - info: atp870u_info, \ - command: atp870u_command, \ - queuecommand: atp870u_queuecommand, \ - eh_strategy_handler: NULL, \ - eh_abort_handler: NULL, \ - eh_device_reset_handler: NULL, \ - eh_bus_reset_handler: NULL, \ - eh_host_reset_handler: NULL, \ - abort: atp870u_abort, \ - reset: atp870u_reset, \ - slave_attach: NULL, \ - bios_param: atp870u_biosparam, \ - can_queue: qcnt, \ - this_id: 1, \ - sg_tablesize: ATP870U_SCATTER, \ - cmd_per_lun: ATP870U_CMDLUN, \ - present: 0, \ - unchecked_isa_dma: 0, \ - use_clustering: ENABLE_CLUSTERING, \ - use_new_eh_code: 0 \ + proc_info: atp870u_proc_info, \ + name: NULL, \ + detect: atp870u_detect, \ + release: atp870u_release, \ + info: atp870u_info, \ + command: atp870u_command, \ + queuecommand: atp870u_queuecommand, \ + eh_strategy_handler: NULL, \ + eh_abort_handler: NULL, \ + eh_device_reset_handler: NULL, \ + eh_bus_reset_handler: NULL, \ + eh_host_reset_handler: NULL, \ + abort: atp870u_abort, \ + reset: atp870u_reset, \ + slave_attach: NULL, \ + bios_param: atp870u_biosparam, \ + can_queue: qcnt, /* max simultaneous cmds */\ + this_id: 7, /* scsi id of host adapter */\ + sg_tablesize: ATP870U_SCATTER, /* max scatter-gather cmds */\ + cmd_per_lun: ATP870U_CMDLUN, /* cmds per lun (linked cmds) */\ + present: 0, /* number of 7xxx's present */\ + unchecked_isa_dma: 0, /* no memory DMA restrictions */\ + use_clustering: ENABLE_CLUSTERING, \ + use_new_eh_code: 0 \ } + #endif diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/eata.c linux/drivers/scsi/eata.c --- v2.2.13/linux/drivers/scsi/eata.c Tue Jan 4 11:10:38 2000 +++ linux/drivers/scsi/eata.c Tue Jan 4 10:12:19 2000 @@ -1,6 +1,13 @@ /* * eata.c - Low-level driver for EATA/DMA SCSI host adapters. * + * 16 Sep 1999 Rev. 5.11 for linux 2.2.12 and 2.3.18 + * + Updated to the new __setup interface for boot command line options. + * + When loaded as a module, accepts the new parameter boot_options + * which value is a string with the same format of the kernel boot + * command line options. A valid example is: + * modprobe eata 'boot_options=\"0x7410,0x230,lc:y,tc:n,mq:4\"' + * * 9 Sep 1999 Rev. 5.10 for linux 2.2.12 and 2.3.17 * + 64bit cleanup for Linux/Alpha platform support * (contribution from H.J. Lu). @@ -368,6 +375,7 @@ #if defined(MODULE) #include +MODULE_PARM(boot_options, "s"); MODULE_PARM(io_port, "1-" __MODULE_STRING(MAX_INT_PARAM) "i"); MODULE_PARM(linked_comm, "i"); MODULE_PARM(tagged_comm, "i"); @@ -401,7 +409,13 @@ #include #include #include +#include + +#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18) #include +#else +#include +#endif #define SPIN_FLAGS unsigned long spin_flags; #define SPIN_LOCK spin_lock_irq(&io_request_lock); @@ -410,8 +424,6 @@ #define SPIN_UNLOCK_RESTORE \ spin_unlock_irqrestore(&io_request_lock, spin_flags); -static int use_new_eh_code = TRUE; - struct proc_dir_entry proc_scsi_eata2x = { PROC_SCSI_EATA2X, 6, "eata2x", S_IFDIR | S_IRUGO | S_IXUGO, 2 @@ -494,7 +506,7 @@ #define ASOK 0x00 #define ASST 0x01 -#if !defined(ARRAY_SIZE) +#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18) #define ARRAY_SIZE(x) (sizeof (x) / sizeof((x)[0])) #endif @@ -646,7 +658,7 @@ /* Initialize num_boards so that ihdlr can work while detect is in progress */ static unsigned int num_boards = MAX_BOARDS; -static unsigned long io_port[] __initdata = { +static unsigned long io_port[] = { /* Space for MAX_INT_PARAM ports usable while loading as a module */ SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, @@ -686,6 +698,8 @@ static int tag_mode = TAG_MIXED; static int ext_tran = FALSE; static int rev_scan = TRUE; +static int use_new_eh_code = TRUE; +static char *boot_options = NULL; #if defined(CONFIG_SCSI_EATA_TAGGED_QUEUE) static int tagged_comm = TRUE; @@ -820,12 +834,6 @@ if (!(dev = pci_find_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) break; -#if 0 - /* Don't bother if PCI vendor and/or device don't match. */ - if (dev->vendor != PCI_VENDOR_ID_DPT || dev->device != PCI_DEVICE_ID_DPT) - continue; -#endif - if (pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &addr)) continue; #if defined(DEBUG_PCI_DETECT) @@ -857,12 +865,6 @@ while((dev = pci_find_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) { -#if 0 - /* Don't bother if PCI vendor and/or device don't match. */ - if (dev->vendor != PCI_VENDOR_ID_DPT || dev->device != PCI_DEVICE_ID_DPT) - continue; -#endif - if (pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &addr)) continue; #if defined(DEBUG_PCI_DETECT) @@ -913,7 +915,7 @@ if (info.sign != EATA_SIGNATURE) return FALSE; if (DEV2H(info.data_len) < EATA_2_0A_SIZE) { - printk("%s: config structure size (%d bytes) too short, detaching.\n", + printk("%s: config structure size (%ld bytes) too short, detaching.\n", name, DEV2H(info.data_len)); return FALSE; } @@ -1166,7 +1168,7 @@ return TRUE; } -void eata2x_setup(char *str, int *ints) { +static void internal_setup(char *str, int *ints) { int i, argc = ints[0]; char *cur = str, *pc; @@ -1202,6 +1204,22 @@ return; } +static int option_setup(char *str) { + int ints[MAX_INT_PARAM]; + char *cur = str; + int i = 1; + + while (cur && isdigit(*cur) && i <= MAX_INT_PARAM) { + ints[i++] = simple_strtoul(cur, NULL, 0); + + if ((cur = strchr(cur, ',')) != NULL) cur++; + } + + ints[0] = i - 1; + internal_setup(cur, ints); + return 0; +} + static void add_pci_ports(void) { #if defined(CONFIG_PCI) @@ -1216,12 +1234,6 @@ if (!(dev = pci_find_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) break; -#if 0 - /* Don't bother if PCI vendor and/or device don't match. */ - if (dev->vendor != PCI_VENDOR_ID_DPT || dev->device != PCI_DEVICE_ID_DPT) - continue; -#endif - if (pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &addr)) continue; #if defined(DEBUG_PCI_DETECT) @@ -1247,6 +1259,8 @@ tpnt->proc_dir = &proc_scsi_eata2x; + if(boot_options) option_setup(boot_options); + #if defined(MODULE) /* io_port could have been modified when loading as a module */ if(io_port[0] != SKIP) { @@ -2281,4 +2295,15 @@ Scsi_Host_Template driver_template = EATA; #include "scsi_module.c" + +#else + +#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18) +void eata2x_setup(char *str, int *ints) { + internal_setup(str, ints); +} +#else +__setup("eata=", option_setup); #endif + +#endif /* end MODULE */ diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/eata.h linux/drivers/scsi/eata.h --- v2.2.13/linux/drivers/scsi/eata.h Tue Jan 4 11:10:38 2000 +++ linux/drivers/scsi/eata.h Tue Jan 4 10:12:19 2000 @@ -16,9 +16,7 @@ int eata2x_old_reset(Scsi_Cmnd *, unsigned int); int eata2x_biosparam(Disk *, kdev_t, int *); -#define EATA_VERSION "5.10.00" - -#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) +#define EATA_VERSION "5.11.01" #define EATA { \ name: "EATA/DMA 2.0x rev. " EATA_VERSION " ", \ diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/esp.c linux/drivers/scsi/esp.c --- v2.2.13/linux/drivers/scsi/esp.c Tue Jan 4 11:10:38 2000 +++ linux/drivers/scsi/esp.c Tue Jan 4 10:12:20 2000 @@ -1519,8 +1519,11 @@ */ if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) || (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) { - /* But we are nice and allow tapes to disconnect. */ - if(SDptr->type == TYPE_TAPE) + /* But we are nice and allow tapes and removable + * disks (but not CDROMs) to disconnect. + */ + if(SDptr->type == TYPE_TAPE || + (SDptr->type != TYPE_ROM && SDptr->removable)) SDptr->disconnect = 1; else SDptr->disconnect = 0; @@ -1538,7 +1541,8 @@ */ if(esp->erev == fashme && !SDptr->wide) { if(!SDptr->borken && - SDptr->type != TYPE_ROM) { + SDptr->type != TYPE_ROM && + SDptr->removable == 0) { build_wide_nego_msg(esp, 16); SDptr->wide = 1; esp->wnip = 1; @@ -1556,6 +1560,11 @@ "CDROM.\n", esp->esp_id)); cdrom_hwbug_wkaround = 1; build_sync_nego_msg(esp, 0, 0); + } else if (SDptr->removable != 0) { + ESPMISC(("esp%d: Not negotiating sync/wide but " + "allowing disconnect for removable media.\n", + esp->esp_id)); + build_sync_nego_msg(esp, 0, 0); } else { build_sync_nego_msg(esp, esp->sync_defp, 15); } @@ -1589,7 +1598,9 @@ * Therefore _no_ disconnects for SCSI1 targets * thank you very much. ;-) */ - if(((SDptr->scsi_level < 3) && (SDptr->type != TYPE_TAPE)) || + if(((SDptr->scsi_level < 3) && + (SDptr->type != TYPE_TAPE) && + SDptr->removable == 0) || cdrom_hwbug_wkaround || SDptr->borken) { ESPMISC((KERN_INFO "esp%d: Disabling DISCONNECT for target %d " "lun %d\n", esp->esp_id, SCptr->target, SCptr->lun)); diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/fdomain.c linux/drivers/scsi/fdomain.c --- v2.2.13/linux/drivers/scsi/fdomain.c Tue Dec 29 11:44:53 1998 +++ linux/drivers/scsi/fdomain.c Tue Jan 4 10:12:20 2000 @@ -875,6 +875,8 @@ int retcode; struct Scsi_Host *shpnt; #if DO_DETECT + int i = 0; + int j = 0; const int buflen = 255; Scsi_Cmnd SCinit; unsigned char do_inquiry[] = { INQUIRY, 0, 0, 0, buflen, 0 }; diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/g_NCR5380.c linux/drivers/scsi/g_NCR5380.c --- v2.2.13/linux/drivers/scsi/g_NCR5380.c Tue Jan 4 11:10:38 2000 +++ linux/drivers/scsi/g_NCR5380.c Tue Jan 4 10:12:20 2000 @@ -499,7 +499,7 @@ dst[start+i] = NCR5380_read(C400_HOST_BUFFER); #else /* implies CONFIG_SCSI_G_NCR5380_MEM */ - memcpy(dst+start,NCR53C400_host_buffer+NCR5380_map_name,128); + memcpy_fromio(dst+start,NCR53C400_host_buffer+NCR5380_map_name,128); #endif start+=128; blocks--; @@ -520,7 +520,7 @@ dst[start+i] = NCR5380_read(C400_HOST_BUFFER); #else /* implies CONFIG_SCSI_G_NCR5380_MEM */ - memcpy(dst+start,NCR53C400_host_buffer+NCR5380_map_name,128); + memcpy_fromio(dst+start,NCR53C400_host_buffer+NCR5380_map_name,128); #endif start+=128; blocks--; @@ -607,7 +607,7 @@ NCR5380_write(C400_HOST_BUFFER, src[start+i]); #else /* implies CONFIG_SCSI_G_NCR5380_MEM */ - memcpy(NCR53C400_host_buffer+NCR5380_map_name,src+start,128); + memcpy_toio(NCR53C400_host_buffer+NCR5380_map_name,src+start,128); #endif start+=128; blocks--; @@ -627,7 +627,7 @@ NCR5380_write(C400_HOST_BUFFER, src[start+i]); #else /* implies CONFIG_SCSI_G_NCR5380_MEM */ - memcpy(NCR53C400_host_buffer+NCR5380_map_name,src+start,128); + memcpy_toio(NCR53C400_host_buffer+NCR5380_map_name,src+start,128); #endif start+=128; blocks--; diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/gdth.c linux/drivers/scsi/gdth.c --- v2.2.13/linux/drivers/scsi/gdth.c Mon Apr 12 09:56:16 1999 +++ linux/drivers/scsi/gdth.c Tue Jan 4 10:12:20 2000 @@ -20,9 +20,32 @@ * along with this kernel; if not, write to the Free Software * * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * * - * Tested with Linux 1.2.13, ..., 2.2.4 * + * Tested with Linux 1.2.13, ..., 2.2.12 * * * * $Log: gdth.c,v $ + * Revision 1.30 1999/11/02 13:42:39 achim + * ARRAY_DRV_LIST2 implemented + * Now 255 log. and 100 host drives supported + * + * Revision 1.29 1999/10/05 13:28:47 achim + * GDT_CLUST_RESET added + * + * Revision 1.28 1999/08/12 13:44:54 achim + * MOUNTALL removed + * Cluster drives -> removeable drives + * + * Revision 1.27 1999/06/22 07:22:38 achim + * Small changes + * + * Revision 1.26 1999/06/10 16:09:12 achim + * Cluster Host Drive support: Bugfixes + * + * Revision 1.25 1999/06/01 16:03:56 achim + * gdth_init_pci(): Manipulate config. space to start RP controller + * + * Revision 1.24 1999/05/26 11:53:06 achim + * Cluster Host Drive support added + * * Revision 1.23 1999/03/26 09:12:31 achim * Default value for hdr_channel set to 0 * @@ -120,7 +143,7 @@ * Initial revision * ************************************************************************/ -#ident "$Id: gdth.c,v 1.23 1999/03/26 09:12:31 achim Exp $" +#ident "$Id: gdth.c,v 1.30 1999/11/02 13:42:39 achim Exp $" /* All GDT Disk Array Controllers are fully supported by this driver. * This includes the PCI/EISA/ISA SCSI Disk Array Controllers and the @@ -581,18 +604,18 @@ { *cyls = size /HEADS/SECS; if (*cyls <= MAXCYLS) { - *heads = HEADS; - *secs = SECS; - } else { /* too high for 64*32 */ - *cyls = size /MEDHEADS/MEDSECS; - if (*cyls <= MAXCYLS) { - *heads = MEDHEADS; - *secs = MEDSECS; - } else { /* too high for 127*63 */ - *cyls = size /BIGHEADS/BIGSECS; - *heads = BIGHEADS; - *secs = BIGSECS; - } + *heads = HEADS; + *secs = SECS; + } else { /* too high for 64*32 */ + *cyls = size /MEDHEADS/MEDSECS; + if (*cyls <= MAXCYLS) { + *heads = MEDHEADS; + *secs = MEDSECS; + } else { /* too high for 127*63 */ + *cyls = size /BIGHEADS/BIGSECS; + *heads = BIGHEADS; + *secs = BIGSECS; + } } } @@ -686,7 +709,7 @@ } TRACE2(("Controller found at %d/%d, irq %d, dpmem 0x%x\n", pcistr[cnt].bus, PCI_SLOT(pcistr[cnt].device_fn), - pcistr[cnt].irq, pcistr[cnt].dpmem)); + pcistr[cnt].irq, (int)pcistr[cnt].dpmem)); cnt++; } #else @@ -981,7 +1004,11 @@ register gdt6m_dpram_str *dp6m_ptr; ulong32 retries; unchar prot_ver; + ushort command; int i, found = FALSE; +#if LINUX_VERSION_CODE < 0x2015C + int rom_addr; +#endif TRACE(("gdth_init_pci()\n")); @@ -1137,6 +1164,36 @@ return 0; } + /* manipulate config. space to enable DPMEM, start RP controller */ +#if LINUX_VERSION_CODE >= 0x2015C + pci_read_config_word(pcistr->pdev, PCI_COMMAND, &command); + command |= 6; + pci_write_config_word(pcistr->pdev, PCI_COMMAND, command); + if (pcistr->pdev->rom_address == 1UL) + pcistr->pdev->rom_address = 0UL; + i = 0xFEFF0001UL; + pci_write_config_dword(pcistr->pdev, PCI_ROM_ADDRESS, i); + gdth_delay(1); + pci_write_config_dword(pcistr->pdev, PCI_ROM_ADDRESS, + pcistr->pdev->rom_address); +#else + pcibios_read_config_word(pcistr->bus, pcistr->device_fn, + PCI_COMMAND, &command); + command |= 6; + pcibios_write_config_word(pcistr->bus, pcistr->device_fn, + PCI_COMMAND, command); + pcibios_read_config_dword(pcistr->bus, pcistr->device_fn, + PCI_ROM_ADDRESS, &rom_addr); + if (rom_addr == 1UL) + rom_addr = 0UL; + i = 0xFEFF0001UL; + pcibios_write_config_dword(pcistr->bus, pcistr->device_fn, + PCI_ROM_ADDRESS, i); + gdth_delay(1); + pcibios_write_config_dword(pcistr->bus, pcistr->device_fn, + PCI_ROM_ADDRESS, rom_addr); +#endif + /* check and reset interface area */ dp6m_ptr = (gdt6m_dpram_str *)ha->brd; gdth_writel(DPMEM_MAGIC, &dp6m_ptr->u); @@ -1585,7 +1642,8 @@ gdth_drlist_str *drl; gdth_iochan_str *ioc; gdth_raw_iochan_str *iocr; - gdth_arraylist_str *alst; + gdth_arcdl_str *alst; + gdth_alist_str *alst2; TRACE(("gdth_search_drives() hanum %d\n",hanum)); ha = HADATA(gdth_ctr_tab[hanum]); @@ -1607,19 +1665,6 @@ TRACE2(("gdth_search_drives(): CACHESERVICE initialized\n")); cdev_cnt = (ushort)ha->info; - /* mount all cache devices */ - gdth_internal_cmd(hanum,CACHESERVICE,GDT_MOUNT,0xffff,1,0); - TRACE2(("gdth_search_drives(): mountall CACHESERVICE OK\n")); - - /* initialize cache service after mountall */ - if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_INIT,LINUX_OS,0,0)) { - printk("GDT: Initialization error cache service (code %d)\n", - ha->status); - return 0; - } - TRACE2(("gdth_search_drives() CACHES. init. after mountall\n")); - cdev_cnt = (ushort)ha->info; - /* detect number of buses - try new IOCTL */ iocr = (gdth_raw_iochan_str *)ha->pscratch; iocr->hdr.version = 0xffffffff; @@ -1747,22 +1792,37 @@ INVALID_CHANNEL,drv_cnt * sizeof(ulong32))) { for (j = 0; j < drv_cnt; ++j) { drv_no = ((ulong32 *)ha->pscratch)[j]; - if (drv_no < MAX_HDRIVES) { + if (drv_no < MAX_LDRIVES) { ha->hdr[drv_no].is_logdrv = TRUE; TRACE2(("Drive %d is log. drive\n",drv_no)); } } } + alst = (gdth_arcdl_str *)ha->pscratch; + alst->entries_avail = MAX_LDRIVES; + alst->first_entry = 0; + alst->list_offset = GDTOFFSOF(gdth_arcdl_str, list[0]); if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL, - ARRAY_DRV_LIST | LA_CTRL_PATTERN, - 0, 35 * sizeof(gdth_arraylist_str))) { + ARRAY_DRV_LIST2 | LA_CTRL_PATTERN, + INVALID_CHANNEL, sizeof(gdth_arcdl_str) + + (alst->entries_avail-1) * sizeof(gdth_alist_str))) { + for (j = 0; j < alst->entries_init; ++j) { + ha->hdr[j].is_arraydrv = alst->list[j].is_arrayd; + ha->hdr[j].is_master = alst->list[j].is_master; + ha->hdr[j].is_parity = alst->list[j].is_parity; + ha->hdr[j].is_hotfix = alst->list[j].is_hotfix; + ha->hdr[j].master_no = alst->list[j].cd_handle; + } + } else if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL, + ARRAY_DRV_LIST | LA_CTRL_PATTERN, + 0, 35 * sizeof(gdth_alist_str))) { for (j = 0; j < 35; ++j) { - alst = &((gdth_arraylist_str *)ha->pscratch)[j]; - ha->hdr[j].is_arraydrv = alst->is_arrayd; - ha->hdr[j].is_master = alst->is_master; - ha->hdr[j].is_parity = alst->is_parity; - ha->hdr[j].is_hotfix = alst->is_hotfix; - ha->hdr[j].master_no = alst->cd_handle; + alst2 = &((gdth_alist_str *)ha->pscratch)[j]; + ha->hdr[j].is_arraydrv = alst2->is_arrayd; + ha->hdr[j].is_master = alst2->is_master; + ha->hdr[j].is_parity = alst2->is_parity; + ha->hdr[j].is_hotfix = alst2->is_hotfix; + ha->hdr[j].master_no = alst2->cd_handle; } } } @@ -1825,17 +1885,13 @@ for (i=0; ibus_cnt,i)); - ha->hdr[i].present = TRUE; ha->hdr[i].size = ha->info; /* evaluate mapping (sectors per head, heads per cylinder) */ ha->hdr[i].size &= ~SECS32; if (ha->info2 == 0) { - gdth_eval_mapping(ha->hdr[i].size,&drv_cyls,&drv_hds,&drv_secs); + gdth_eval_mapping(ha->hdr[i].size,&drv_cyls,&drv_hds,&drv_secs); } else { drv_hds = ha->info2 & 0xff; drv_secs = (ha->info2 >> 8) & 0xff; @@ -1851,10 +1907,30 @@ /* get informations about device */ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_DEVTYPE,i, 0,0)) { - TRACE(("gdth_search_dr() cache drive %d devtype %d\n", + TRACE2(("gdth_search_dr() cache drive %d devtype %d\n", i,ha->info)); ha->hdr[i].devtype = (ushort)ha->info; } + + /* cluster info */ + if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_CLUST_INFO,i, + 0,0)) { + TRACE2(("gdth_search_dr() cache drive %d cluster info %d\n", + i,ha->info)); + ha->hdr[i].cluster_type = (unchar)ha->info; + } else { + ha->hdr[i].cluster_type = 0; + } + + /* R/W attributes */ + if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_RW_ATTRIBS,i, + 0,0)) { + TRACE2(("gdth_search_dr() cache drive %d r/w attrib. %d\n", + i,ha->info)); + ha->hdr[i].rw_attribs = (unchar)ha->info; + } else { + ha->hdr[i].rw_attribs = 0; + } } } @@ -1969,11 +2045,11 @@ } #if LINUX_VERSION_CODE >= 0x010300 - if (nscp->done != gdth_scsi_done) + if (nscp->done != gdth_scsi_done || nscp->cmnd[0] != 0xff) #endif { if (nscp->SCp.phase == -1) { - nscp->SCp.phase = SCSIRAWSERVICE; /* default: raw svc. */ + nscp->SCp.phase = CACHESERVICE; /* default: cache svc. */ if (nscp->cmnd[0] == TEST_UNIT_READY) { TRACE2(("TEST_UNIT_READY Bus %d Id %d LUN %d\n", b, t, nscp->lun)); @@ -1987,7 +2063,8 @@ if (b == 0 && ((t == 0 && nscp->lun == 1) || (t == 1 && nscp->lun == 0))) { nscp->SCp.Status = GDT_SCAN_START; - nscp->SCp.phase |= ((ha->scan_mode & 0x10 ? 1:0) << 8); + nscp->SCp.phase = ((ha->scan_mode & 0x10 ? 1:0) << 8) + | SCSIRAWSERVICE; ha->scan_mode = 0x12; TRACE2(("Scan mode: 0x%x (SCAN_START)\n", ha->scan_mode)); @@ -1997,6 +2074,7 @@ } } else if (ha->scan_mode == 0x12) { if (b == ha->bus_cnt && t == ha->tid_cnt-1) { + nscp->SCp.phase = SCSIRAWSERVICE; nscp->SCp.Status = GDT_SCAN_END; ha->scan_mode &= 0x10; TRACE2(("Scan mode: 0x%x (SCAN_END)\n", @@ -2004,19 +2082,55 @@ } } } + if (b == ha->virt_bus && nscp->cmnd[0] != INQUIRY && + nscp->cmnd[0] != READ_CAPACITY && nscp->cmnd[0] != MODE_SENSE && + (ha->hdr[t].cluster_type & CLUSTER_DRIVE)) { + if (!(ha->hdr[t].cluster_type & CLUSTER_MOUNTED)) { + /* cluster drive NOT MOUNTED */ + if (!(ha->hdr[t].cluster_type & CLUSTER_RESERVED)) { + /* cluster drive NOT RESERVED */ + nscp->SCp.Status = GDT_MOUNT; + } else { + /* cluster drive RESERVED (on the other node) */ + nscp->SCp.Status = GDT_CLUST_INFO; + } + } else { + if (!(ha->hdr[t].cluster_type & CLUSTER_RESERVED)) { + /* cluster drive MOUNTED and not RESERVED */ + nscp->SCp.Status = GDT_CLUST_INFO; + } + } + } } } if (nscp->SCp.Status != -1) { - if ((nscp->SCp.phase & 0xff) == SCSIRAWSERVICE) { + if ((nscp->SCp.phase & 0xff) == CACHESERVICE) { + if (!(cmd_index=gdth_fill_cache_cmd(hanum,nscp,t))) + this_cmd = FALSE; + next_cmd = FALSE; + } else if ((nscp->SCp.phase & 0xff) == SCSIRAWSERVICE) { if (!(cmd_index=gdth_fill_raw_cmd(hanum,nscp,BUS_L2P(ha,b)))) this_cmd = FALSE; next_cmd = FALSE; + } else { + memset((char*)nscp->sense_buffer,0,16); + nscp->sense_buffer[0] = 0x70; + nscp->sense_buffer[2] = NOT_READY; + nscp->result = (DID_OK << 16) | (CHECK_CONDITION << 1); + if (!nscp->SCp.have_data_in) + nscp->SCp.have_data_in++; + else { + GDTH_UNLOCK_HA(ha,flags); + /* io_request_lock already active ! */ + nscp->scsi_done(nscp); + GDTH_LOCK_HA(ha,flags); + } } } else #if LINUX_VERSION_CODE >= 0x010300 - if (nscp->done == gdth_scsi_done) { + if (nscp->done == gdth_scsi_done && nscp->cmnd[0] == 0xff) { if (!(cmd_index=gdth_special_cmd(hanum,nscp))) this_cmd = FALSE; next_cmd = FALSE; @@ -2080,6 +2194,14 @@ } break; + case RESERVE: + case RELEASE: + TRACE2(("cache cmd %s\n",nscp->cmnd[0] == RESERVE ? + "RESERVE" : "RELEASE")); + if (!(cmd_index=gdth_fill_cache_cmd(hanum,nscp,t))) + this_cmd = FALSE; + break; + case READ_6: case WRITE_6: case READ_10: @@ -2184,7 +2306,10 @@ inq.type_qual = (ha->hdr[t].devtype&4) ? TYPE_ROM:TYPE_DISK; /* you can here set all disks to removable, if you want to do a flush using the ALLOW_MEDIUM_REMOVAL command */ - inq.modif_rmb = ha->hdr[t].devtype&1 ? 0x80:0x00; + inq.modif_rmb = 0x00; + if ((ha->hdr[t].devtype & 1) || + (ha->hdr[t].cluster_type & CLUSTER_DRIVE)) + inq.modif_rmb = 0x80; inq.version = 2; inq.resp_aenc = 2; inq.add_length= 32; @@ -2243,7 +2368,7 @@ register gdth_cmd_str *cmdp; struct scatterlist *sl; ushort i; - int cmd_index; + int cmd_index, read_write; ha = HADATA(gdth_ctr_tab[hanum]); cmdp = ha->pccb; @@ -2265,31 +2390,38 @@ gdth_set_sema0(hanum); /* fill command */ - if (scp->cmnd[0]==ALLOW_MEDIUM_REMOVAL) { + read_write = FALSE; + if (scp->SCp.Status != -1) + cmdp->OpCode = scp->SCp.Status; /* special cache cmd. */ + else if (scp->cmnd[0] == RESERVE) + cmdp->OpCode = GDT_RESERVE_DRV; + else if (scp->cmnd[0] == RELEASE) + cmdp->OpCode = GDT_RELEASE_DRV; + else if (scp->cmnd[0] == ALLOW_MEDIUM_REMOVAL) { if (scp->cmnd[4] & 1) /* prevent ? */ - cmdp->OpCode = GDT_MOUNT; + cmdp->OpCode = GDT_MOUNT; else if (scp->cmnd[3] & 1) /* removable drive ? */ - cmdp->OpCode = GDT_UNMOUNT; + cmdp->OpCode = GDT_UNMOUNT; else - cmdp->OpCode = GDT_FLUSH; + cmdp->OpCode = GDT_FLUSH; + } else if (scp->cmnd[0] == WRITE_6 || scp->cmnd[0] == WRITE_10) { + read_write = TRUE; + if (gdth_write_through || ((ha->hdr[hdrive].rw_attribs & 1) && + (ha->cache_feat & GDT_WR_THROUGH))) + cmdp->OpCode = GDT_WRITE_THR; + else + cmdp->OpCode = GDT_WRITE; } else { - if (scp->cmnd[0]==WRITE_6 || scp->cmnd[0]==WRITE_10) { - if (gdth_write_through) - cmdp->OpCode = GDT_WRITE_THR; - else - cmdp->OpCode = GDT_WRITE; - } else { - cmdp->OpCode = GDT_READ; - } + read_write = TRUE; + cmdp->OpCode = GDT_READ; } + + cmdp->BoardNode = LOCALBOARD; + cmdp->u.cache.DeviceNo = hdrive; + cmdp->u.cache.BlockNo = 1; + cmdp->u.cache.sg_canz = 0; - cmdp->BoardNode = LOCALBOARD; - cmdp->u.cache.DeviceNo = hdrive; - - if (scp->cmnd[0]==ALLOW_MEDIUM_REMOVAL) { - cmdp->u.cache.BlockNo = 1; - cmdp->u.cache.sg_canz = 0; - } else { + if (read_write) { if (scp->cmd_len != 6) { cmdp->u.cache.BlockNo = ntohl(*(ulong32*)&scp->cmnd[2]); cmdp->u.cache.BlockCnt= (ulong32)ntohs(*(ushort*)&scp->cmnd[7]); @@ -2942,37 +3074,92 @@ } /* cache or raw service */ if (ha->status == S_OK) { - scp->SCp.Message = S_OK; + scp->SCp.Message = (int)(ha->info<<16|S_OK); if (scp->SCp.Status != -1) { TRACE2(("gdth_sync_event(): special cmd 0x%x OK\n", scp->SCp.Status)); - scp->SCp.Status = -1; - scp->SCp.this_residual = HIGH_PRI; - return 2; + /* special commands GDT_CLUST_INFO/GDT_MOUNT ? */ + if (scp->SCp.Status == GDT_CLUST_INFO) { + ha->hdr[scp->target].cluster_type = (unchar)ha->info; + if (!(ha->hdr[scp->target].cluster_type & + CLUSTER_MOUNTED)) { + /* NOT MOUNTED -> MOUNT */ + if (!(ha->hdr[scp->target].cluster_type & + CLUSTER_RESERVED)) { + /* cluster drive NOT RESERVED */ + scp->SCp.Status = GDT_MOUNT; + } else { + /* cluster drive RESERVED (on the other node) */ + scp->SCp.Status = GDT_MOUNT; + scp->SCp.phase = -2; /* reservation conflict */ + } + } else { + scp->SCp.Status = -1; + } + /* retry */ + scp->SCp.this_residual = HIGH_PRI; + return 2; + } else if (scp->SCp.Status == GDT_MOUNT) { + ha->hdr[scp->target].cluster_type |= CLUSTER_MOUNTED; + scp->SCp.Status = -1; + /* return UNIT_ATTENTION */ + memset((char*)scp->sense_buffer,0,16); + scp->sense_buffer[0] = 0x70; + scp->sense_buffer[2] = UNIT_ATTENTION; + scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1); + } else { + scp->SCp.Status = -1; + /* retry */ + scp->SCp.this_residual = HIGH_PRI; + return 2; + } + } else { + /* RESERVE/RELEASE ? */ + if (scp->cmnd[0] == RESERVE) { + ha->hdr[scp->target].cluster_type |= CLUSTER_RESERVED; + } else if (scp->cmnd[0] == RELEASE) { + ha->hdr[scp->target].cluster_type &= ~CLUSTER_RESERVED; + } + scp->result = DID_OK << 16; } - scp->result = DID_OK << 16; } else if (ha->status == S_BSY) { TRACE2(("Controller busy -> retry !\n")); - scp->SCp.Message = S_BSY; + scp->SCp.Message = (int)(ha->info<<16|S_BSY); + if (scp->SCp.Status == GDT_MOUNT) + scp->SCp.Status = GDT_CLUST_INFO; + /* retry */ return 2; } else { scp->SCp.Message = (int)((ha->info<<16)|ha->status); + memset((char*)scp->sense_buffer,0,16); + scp->sense_buffer[0] = 0x70; + scp->sense_buffer[2] = NOT_READY; + if (scp->SCp.Status != -1) { TRACE2(("gdth_sync_event(): special cmd 0x%x error 0x%x\n", scp->SCp.Status, ha->status)); - scp->SCp.Status = -1; - scp->SCp.this_residual = HIGH_PRI; - return 2; - } - if (service == CACHESERVICE) { - memset((char*)scp->sense_buffer,0,16); - scp->sense_buffer[0] = 0x70; - scp->sense_buffer[2] = NOT_READY; + if (scp->SCp.Status == GDT_SCAN_START || + scp->SCp.Status == GDT_SCAN_END) { + scp->SCp.Status = -1; + /* retry */ + scp->SCp.this_residual = HIGH_PRI; + return 2; + } + scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1); + } else if (scp->cmnd[0] == RESERVE || + scp->cmnd[0] == RELEASE) { + scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1); + } else if (service == CACHESERVICE) { + if (ha->status == S_CACHE_UNKNOWN && + (ha->hdr[scp->target].cluster_type & + CLUSTER_RESERVE_STATE) == CLUSTER_RESERVE_STATE) { + /* bus reset -> force GDT_CLUST_INFO */ + ha->hdr[scp->target].cluster_type &= ~CLUSTER_RESERVED; + } scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1); - #if LINUX_VERSION_CODE >= 0x010300 if (scp->done != gdth_scsi_done) -#endif +#endif { dvr.size = sizeof(dvr.eu.sync); dvr.eu.sync.ionode = hanum; @@ -3754,24 +3941,46 @@ TRACE2(("gdth_eh_bus_reset()\n")); hanum = NUMDATA(scp->host)->hanum; ha = HADATA(gdth_ctr_tab[hanum]); - if (scp->channel == ha->virt_bus) - return FAILED; + /* clear command tab */ GDTH_LOCK_HA(ha, flags); - for (i = 0; i < MAXID; ++i) - ha->raw[BUS_L2P(ha,scp->channel)].io_cnt[i] = 0; for (i = 0; i < GDTH_MAXCMDS; ++i) { cmnd = ha->cmd_tab[i].cmnd; if (!SPECIAL_SCP(cmnd) && cmnd->channel == scp->channel) ha->cmd_tab[i].cmnd = UNUSED_CMND; } - gdth_polling = TRUE; - while (gdth_test_busy(hanum)) - gdth_delay(0); - gdth_internal_cmd(hanum, SCSIRAWSERVICE, GDT_RESET_BUS, - BUS_L2P(ha,scp->channel), 0, 0); - gdth_polling = FALSE; GDTH_UNLOCK_HA(ha, flags); + + if (scp->channel == ha->virt_bus) { + /* host drives */ + for (i = 0; i < MAX_HDRIVES; ++i) { + if (ha->hdr[i].present && + (ha->hdr[i].cluster_type & CLUSTER_RESERVED) + == CLUSTER_RESERVED) { + GDTH_LOCK_HA(ha, flags); + gdth_polling = TRUE; + while (gdth_test_busy(hanum)) + gdth_delay(0); + if (gdth_internal_cmd(hanum, CACHESERVICE, + GDT_CLUST_RESET, i, 0, 0)) + ha->hdr[i].cluster_type &= ~CLUSTER_RESERVED; + gdth_polling = FALSE; + GDTH_UNLOCK_HA(ha, flags); + } + } + } else { + /* raw devices */ + GDTH_LOCK_HA(ha, flags); + for (i = 0; i < MAXID; ++i) + ha->raw[BUS_L2P(ha,scp->channel)].io_cnt[i] = 0; + gdth_polling = TRUE; + while (gdth_test_busy(hanum)) + gdth_delay(0); + gdth_internal_cmd(hanum, SCSIRAWSERVICE, GDT_RESET_BUS, + BUS_L2P(ha,scp->channel), 0, 0); + gdth_polling = FALSE; + GDTH_UNLOCK_HA(ha, flags); + } return SUCCESS; } @@ -3800,12 +4009,12 @@ if (disk->device->channel != ha->virt_bus || ha->hdr[t].heads == 0) { /* raw device or host drive without mapping information */ - TRACE2(("Evaluate mapping\n")); - gdth_eval_mapping(disk->capacity,&ip[2],&ip[0],&ip[1]); + TRACE2(("Evaluate mapping\n")); + gdth_eval_mapping(disk->capacity,&ip[2],&ip[0],&ip[1]); } else { - ip[0] = ha->hdr[t].heads; - ip[1] = ha->hdr[t].secs; - ip[2] = disk->capacity / ip[0] / ip[1]; + ip[0] = ha->hdr[t].heads; + ip[1] = ha->hdr[t].secs; + ip[2] = disk->capacity / ip[0] / ip[1]; } TRACE2(("gdth_bios_param(): %d heads, %d secs, %d cyls\n", @@ -3869,18 +4078,16 @@ Scsi_Cmnd scp; Scsi_Device sdev; gdth_cmd_str gdtcmd; + char cmnd[12]; TRACE2(("gdth_flush() hanum %d\n",hanum)); ha = HADATA(gdth_ctr_tab[hanum]); memset(&sdev,0,sizeof(Scsi_Device)); memset(&scp, 0,sizeof(Scsi_Cmnd)); - sdev.host = gdth_ctr_tab[hanum]; - sdev.id = sdev.host->this_id; - scp.cmd_len = 12; - scp.host = gdth_ctr_tab[hanum]; - scp.target = sdev.host->this_id; + memset(cmnd, 0xff, 12); + sdev.host = scp.host = gdth_ctr_tab[hanum]; + sdev.id = scp.target = sdev.host->this_id; scp.device = &sdev; - scp.use_sg = 0; for (i = 0; i < MAX_HDRIVES; ++i) { if (ha->hdr[i].present) { @@ -3891,7 +4098,7 @@ gdtcmd.u.cache.BlockNo = 1; gdtcmd.u.cache.sg_canz = 0; TRACE2(("gdth_flush(): flush ha %d drive %d\n", hanum, i)); - gdth_do_cmd(&scp, &gdtcmd, 30); + gdth_do_cmd(&scp, &gdtcmd, cmnd, 30); } } } @@ -3908,10 +4115,11 @@ Scsi_Cmnd scp; Scsi_Device sdev; gdth_cmd_str gdtcmd; + char cmnd[12]; #endif #if LINUX_VERSION_CODE >= 0x020100 - TRACE2(("gdth_halt() event %d\n",event)); + TRACE2(("gdth_halt() event %d\n",(int)event)); if (event != SYS_RESTART && event != SYS_HALT && event != SYS_POWER_OFF) return NOTIFY_DONE; #else @@ -3930,19 +4138,16 @@ /* controller reset */ memset(&sdev,0,sizeof(Scsi_Device)); memset(&scp, 0,sizeof(Scsi_Cmnd)); - sdev.host = gdth_ctr_tab[hanum]; - sdev.id = sdev.host->this_id; - scp.cmd_len = 12; - scp.host = gdth_ctr_tab[hanum]; - scp.target = sdev.host->this_id; + memset(cmnd, 0xff, 12); + sdev.host = scp.host = gdth_ctr_tab[hanum]; + sdev.id = scp.target = sdev.host->this_id; scp.device = &sdev; - scp.use_sg = 0; gdtcmd.BoardNode = LOCALBOARD; gdtcmd.Service = CACHESERVICE; gdtcmd.OpCode = GDT_RESET; TRACE2(("gdth_halt(): reset controller %d\n", hanum)); - gdth_do_cmd(&scp, &gdtcmd, 10); + gdth_do_cmd(&scp, &gdtcmd, cmnd, 10); #endif } printk("Done.\n"); diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/gdth.h linux/drivers/scsi/gdth.h --- v2.2.13/linux/drivers/scsi/gdth.h Tue May 11 10:36:36 1999 +++ linux/drivers/scsi/gdth.h Tue Jan 4 10:12:20 2000 @@ -10,7 +10,7 @@ * * * - * $Id: gdth.h,v 1.21 1999/03/26 09:12:24 achim Exp $ + * $Id: gdth.h,v 1.24 1999/11/02 13:43:49 achim Exp $ */ #include @@ -29,9 +29,9 @@ /* defines, macros */ /* driver version */ -#define GDTH_VERSION_STR "1.14" +#define GDTH_VERSION_STR "1.17" #define GDTH_VERSION 1 -#define GDTH_SUBVERSION 14 +#define GDTH_SUBVERSION 17 /* protocol version */ #define PROTOCOL_VERSION 1 @@ -135,7 +135,8 @@ #define MAXID 127 #define MAXLUN 8 #define MAXBUS 6 -#define MAX_HDRIVES 35 /* max. host drive count */ +#define MAX_HDRIVES 100 /* max. host drive count */ +#define MAX_LDRIVES 255 /* max. log. drive count */ #define MAX_EVENTS 100 /* event buffer count */ #define MAX_RES_ARGS 40 /* device reservation, must be a multiple of 4 */ @@ -173,6 +174,12 @@ #define IC_QUEUE_BYTES 4 #define DPMEM_COMMAND_OFFSET IC_HEADER_BYTES+IC_QUEUE_BYTES*MAXOFFSETS +/* cluster_type constants */ +#define CLUSTER_DRIVE 1 +#define CLUSTER_MOUNTED 2 +#define CLUSTER_RESERVED 4 +#define CLUSTER_RESERVE_STATE (CLUSTER_DRIVE|CLUSTER_MOUNTED|CLUSTER_RESERVED) + /* cache/raw service commands */ #define GDT_INIT 0 /* service initialization */ #define GDT_READ 1 /* read command */ @@ -189,6 +196,11 @@ #define GDT_READ_THR 17 /* read through */ #define GDT_EXT_INFO 18 /* extended info */ #define GDT_RESET 19 /* controller reset */ +#define GDT_RESERVE_DRV 20 /* reserve host drive */ +#define GDT_RELEASE_DRV 21 /* release host drive */ +#define GDT_CLUST_INFO 22 /* cluster info */ +#define GDT_RW_ATTRIBS 23 /* R/W attribs (write thru,..)*/ +#define GDT_CLUST_RESET 24 /* releases the cluster drives*/ /* additional raw service commands */ #define GDT_RESERVE 14 /* reserve dev. to raw serv. */ @@ -206,10 +218,11 @@ #define SCSI_DEF_CNT 0x15 /* grown/primary defects */ #define DSK_STATISTICS 0x4b /* SCSI disk statistics */ #define IOCHAN_DESC 0x5d /* description of IO channel */ -#define IOCHAN_RAW_DESC 0x5e /* description of raw IO channel */ +#define IOCHAN_RAW_DESC 0x5e /* description of raw IO chn. */ #define L_CTRL_PATTERN 0x20000000L /* SCSI IOCTL mask */ #define ARRAY_INFO 0x12 /* array drive info */ #define ARRAY_DRV_LIST 0x0f /* array drive list */ +#define ARRAY_DRV_LIST2 0x34 /* array drive list (new) */ #define LA_CTRL_PATTERN 0x10000000L /* array IOCTL mask */ #define CACHE_DRV_CNT 0x01 /* cache drive count */ #define CACHE_DRV_LIST 0x02 /* cache drive list */ @@ -235,6 +248,7 @@ /* service errors */ #define S_OK 1 /* no error */ #define S_BSY 7 /* controller busy */ +#define S_CACHE_UNKNOWN 12 /* cache serv.: drive unknown */ #define S_RAW_SCSI 12 /* raw serv.: target error */ #define S_RAW_ILL 0xff /* raw serv.: illegal */ @@ -249,8 +263,10 @@ #define HIGH_PRI 0x08 /* data directions */ +#ifndef HOSTS_C #define DATA_IN 0x01000000L /* data from target */ #define DATA_OUT 0x00000000L /* data to target */ +#endif /* BMIC registers (EISA controllers) */ #define ID0REG 0x0c80 /* board ID */ @@ -298,7 +314,7 @@ unchar revision[4]; /* revision */ ulong32 sy_rate; /* current rate for sync. tr. */ ulong32 sy_max_rate; /* max. rate for sync. tr. */ - ulong32 no_ldrive; /* belongs to this logical drv.*/ + ulong32 no_ldrive; /* belongs to this log. drv.*/ ulong32 blkcnt; /* number of blocks */ ushort blksize; /* size of block in bytes */ unchar available; /* flag: access is available */ @@ -453,7 +469,15 @@ unchar is_parity; /* Flag: is parity drive? */ unchar is_hotfix; /* Flag: is hotfix drive? */ unchar res[3]; -} PACKED gdth_arraylist_str; +} PACKED gdth_alist_str; + +typedef struct { + ulong32 entries_avail; /* allocated entries */ + ulong32 entries_init; /* returned entries */ + ulong32 first_entry; /* first entry number */ + ulong32 list_offset; /* offset of following list */ + gdth_alist_str list[1]; /* list */ +} PACKED gdth_arcdl_str; /* cache info/config IOCTL */ typedef struct { @@ -862,13 +886,15 @@ ulong32 size; /* capacity */ unchar ldr_no; /* log. drive no. */ unchar rw_attribs; /* r/w attributes */ + unchar cluster_type; /* cluster properties */ + unchar reserved; ulong32 start_sec; /* start sector */ - } hdr[MAX_HDRIVES]; /* host drives */ + } hdr[MAX_LDRIVES]; /* host drives */ struct { unchar lock; /* channel locked? (hot plug) */ unchar pdev_cnt; /* physical device count */ unchar local_no; /* local channel number */ - unchar io_cnt[MAXID]; /* current IO count */ + unchar io_cnt[MAXID]; /* current IO count */ ulong32 address; /* channel address */ ulong32 id_list[MAXID]; /* IDs of the phys. devices */ } raw[MAXBUS]; /* SCSI channels */ diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/gdth_ioctl.h linux/drivers/scsi/gdth_ioctl.h --- v2.2.13/linux/drivers/scsi/gdth_ioctl.h Mon Jan 11 10:17:20 1999 +++ linux/drivers/scsi/gdth_ioctl.h Tue Jan 4 10:12:20 2000 @@ -2,7 +2,7 @@ #define _GDTH_IOCTL_H /* gdth_ioctl.h - * $Id: gdth_ioctl.h,v 1.2 1998/12/17 15:42:49 achim Exp $ + * $Id: gdth_ioctl.h,v 1.3 1999/05/26 11:49:57 achim Exp $ */ /* IOCTLs */ @@ -11,10 +11,13 @@ #define GDTIOCTL_DRVERS (GDTIOCTL_MASK | 1) /* get driver version */ #define GDTIOCTL_CTRTYPE (GDTIOCTL_MASK | 2) /* get controller type */ #define GDTIOCTL_OSVERS (GDTIOCTL_MASK | 3) /* get OS version */ +#define GDTIOCTL_HDRLIST (GDTIOCTL_MASK | 4) /* get host drive list */ #define GDTIOCTL_CTRCNT (GDTIOCTL_MASK | 5) /* get controller count */ #define GDTIOCTL_LOCKDRV (GDTIOCTL_MASK | 6) /* lock host drive */ #define GDTIOCTL_LOCKCHN (GDTIOCTL_MASK | 7) /* lock channel */ #define GDTIOCTL_EVENT (GDTIOCTL_MASK | 8) /* read controller events */ +#define GDTIOCTL_SCSI (GDTIOCTL_MASK | 9) /* SCSI command */ +#define GDTIOCTL_RESET_BUS (GDTIOCTL_MASK |10) /* reset SCSI bus */ #define GDTIOCTL_MAGIC 0xaffe0001UL @@ -45,6 +48,13 @@ int handle; unchar evt[34]; /* event structure */ } event; + struct { + unchar bus; /* SCSI bus */ + unchar target; /* target ID */ + unchar lun; /* LUN */ + unchar cmd_len; /* command length */ + unchar cmd[12]; /* SCSI command */ + } scsi; } iu; } gdth_iowr_str; @@ -79,6 +89,12 @@ int handle; unchar evt[34]; /* event structure */ } event; + struct { + unchar bus; /* SCSI bus, 0xff: invalid */ + unchar target; /* target ID */ + unchar lun; /* LUN */ + unchar cluster_type; /* cluster properties */ + } hdr_list[35]; /* index is host drive number */ } iu; } gdth_iord_str; diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/gdth_proc.c linux/drivers/scsi/gdth_proc.c --- v2.2.13/linux/drivers/scsi/gdth_proc.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/scsi/gdth_proc.c Tue Jan 4 10:12:20 2000 @@ -1,9 +1,8 @@ /* gdth_proc.c - * $Id: gdth_proc.c,v 1.13 1999/03/22 16:12:53 achim Exp $ + * $Id: gdth_proc.c,v 1.16 1999/11/02 13:44:11 achim Exp $ */ #include "gdth_ioctl.h" -#include int gdth_proc_info(char *buffer,char **start,off_t offset,int length, int hostno,int inout) @@ -41,13 +40,9 @@ memset(&sdev,0,sizeof(Scsi_Device)); memset(&scp, 0,sizeof(Scsi_Cmnd)); - sdev.host = gdth_ctr_vtab[vh]; - sdev.id = sdev.host->this_id; - scp.cmd_len = 12; - scp.host = gdth_ctr_vtab[vh]; - scp.target = sdev.host->this_id; + sdev.host = scp.host = gdth_ctr_vtab[vh]; + sdev.id = scp.target = sdev.host->this_id; scp.device = &sdev; - scp.use_sg = 0; if (length >= 4) { if (strncmp(buffer,"gdth",4) == 0) { @@ -73,6 +68,7 @@ gdth_ha_str *ha; gdth_cmd_str gdtcmd; gdth_cpar_str *pcpar; + char cmnd[12]; TRACE2(("gdth_set_asc_info() ha %d\n",hanum)); ha = HADATA(gdth_ctr_tab[hanum]); @@ -80,6 +76,7 @@ drive = -1; wb_mode = 0; found = FALSE; + memset(cmnd, 0xff, 12); if (length >= 5 && strncmp(buffer,"flush",5)==0) { buffer += 6; @@ -106,7 +103,7 @@ gdtcmd.u.cache.DeviceNo = i; gdtcmd.u.cache.BlockNo = 1; gdtcmd.u.cache.sg_canz = 0; - gdth_do_cmd(&scp, &gdtcmd, 30); + gdth_do_cmd(&scp, &gdtcmd, cmnd, 30); } } if (!found) @@ -159,7 +156,7 @@ gdtcmd.u.ioctl.subfunc = CACHE_CONFIG; gdtcmd.u.ioctl.channel = INVALID_CHANNEL; pcpar->write_back = wb_mode==1 ? 0:1; - gdth_do_cmd(&scp, &gdtcmd, 30); + gdth_do_cmd(&scp, &gdtcmd, cmnd, 30); gdth_ioctl_free(hanum); printk("Done.\n"); return(orig_length); @@ -180,6 +177,8 @@ ulong32 *ppadd, add_size; ulong32 *ppadd2, add_size2; ulong flags; + char cmnd[12]; + gdth_cmd_str gdtcmd; TRACE2(("gdth_set_bin_info() ha %d\n",hanum)); ha = HADATA(gdth_ctr_tab[hanum]); @@ -188,6 +187,7 @@ pcmd = NULL; ppadd = ppadd2 = NULL; add_size = add_size2 = 0; + memset(cmnd, 0xff, 12); if (length < GDTOFFSOF(gdth_iowr_str,iu)) return(-EINVAL); @@ -242,7 +242,7 @@ *ppadd2 = virt_to_bus(piord->iu.general.data+add_size); } /* do IOCTL */ - gdth_do_cmd(&scp, pcmd, piowr->timeout); + gdth_do_cmd(&scp, pcmd, cmnd, piowr->timeout); piord->status = (ulong32)scp.SCp.Message; break; @@ -363,10 +363,10 @@ pevt->event_data.size = sizeof(pevt->event_data.eu.async); gdth_log_event(&pevt->event_data, NULL); } - GDTH_LOCK_HA(ha, flags); + GDTH_LOCK_HA(ha, flags); gdth_store_event(ha, pevt->event_source, pevt->event_idx, &pevt->event_data); - GDTH_UNLOCK_HA(ha, flags); + GDTH_UNLOCK_HA(ha, flags); } else if (piowr->iu.event.erase == 0xfe) { gdth_clear_events(); } else if (piowr->iu.event.erase == 0) { @@ -382,6 +382,58 @@ piord->status = S_OK; break; + case GDTIOCTL_SCSI: + if (!gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) )) + return(-EBUSY); + piord = (gdth_iord_str *)ha->pscratch; + piord->size = sizeof(gdth_iord_str); + scp.target = scp.device->id = piowr->iu.scsi.target; + scp.channel = scp.device->channel = piowr->iu.scsi.bus; + memcpy(cmnd, piowr->iu.scsi.cmd, 12); + scp.cmd_len = piowr->iu.scsi.cmd_len; + gdth_do_cmd(&scp, pcmd, cmnd, piowr->timeout); + piord->status = (ulong32)scp.result; + break; + + case GDTIOCTL_RESET_BUS: + if (!gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) )) + return(-EBUSY); + piord = (gdth_iord_str *)ha->pscratch; + piord->size = sizeof(gdth_iord_str); + scp.channel = scp.device->channel = piowr->iu.scsi.bus; + piord->status = (ulong32)gdth_eh_bus_reset( &scp ); + break; + + case GDTIOCTL_HDRLIST: + if (!gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) )) + return(-EBUSY); + piord = (gdth_iord_str *)ha->pscratch; + piord->size = sizeof(gdth_iord_str); + piord->status = S_OK; + for (i = 0; i < MAX_HDRIVES; ++i) { + if (ha->hdr[i].present) { + piord->iu.hdr_list[i].bus = ha->virt_bus; + piord->iu.hdr_list[i].target = i; + piord->iu.hdr_list[i].lun = 0; + piord->iu.hdr_list[i].cluster_type = ha->hdr[i].cluster_type; + if (ha->hdr[i].cluster_type & CLUSTER_DRIVE) { + gdtcmd.BoardNode = LOCALBOARD; + gdtcmd.Service = CACHESERVICE; + gdtcmd.OpCode = GDT_CLUST_INFO; + gdtcmd.u.cache.DeviceNo = i; + gdtcmd.u.cache.BlockNo = 0; + gdtcmd.u.cache.sg_canz = 0; + gdth_do_cmd(&scp, &gdtcmd, cmnd, 30); + if ((scp.SCp.Message & 0xffff) == S_OK) + piord->iu.hdr_list[i].cluster_type = + (unchar)(scp.SCp.Message>>16); + } + } else { + piord->iu.hdr_list[i].bus = 0xff; + } + } + break; + default: return(-EINVAL); } @@ -404,6 +456,7 @@ gdth_evt_str estr; Scsi_Cmnd scp; Scsi_Device sdev; + char cmnd[12]; char hrec[161]; struct timeval tv; @@ -420,13 +473,10 @@ memset(&sdev,0,sizeof(Scsi_Device)); memset(&scp, 0,sizeof(Scsi_Cmnd)); - sdev.host = gdth_ctr_vtab[vh]; - sdev.id = sdev.host->this_id; - scp.cmd_len = 12; - scp.host = gdth_ctr_vtab[vh]; - scp.target = sdev.host->this_id; + memset(cmnd, 0xff, 12); + sdev.host = scp.host = gdth_ctr_vtab[vh]; + sdev.id = scp.target = sdev.host->this_id; scp.device = &sdev; - scp.use_sg = 0; /* look for buffer ID in length */ if (id > 1) { @@ -514,7 +564,7 @@ /* 2. about physical devices */ size = sprintf(buffer+len,"\nPhysical Devices:"); len += size; pos = begin + len; - flag = FALSE; + flag = FALSE; if (!gdth_ioctl_alloc(hanum, GDTH_SCRATCH)) goto stop_output; @@ -536,8 +586,8 @@ sizeof(pds->list[0]); if (pds->entries > cnt) pds->entries = cnt; - gdth_do_cmd(&scp, &gdtcmd, 30); - if (scp.SCp.Message != S_OK) + gdth_do_cmd(&scp, &gdtcmd, cmnd, 30); + if ((scp.SCp.Message & 0xffff) != S_OK) pds->count = 0; TRACE2(("pdr_statistics() entries %d status %d\n", pds->count, scp.SCp.Message)); @@ -556,8 +606,8 @@ gdtcmd.u.ioctl.subfunc = SCSI_DR_INFO | L_CTRL_PATTERN; gdtcmd.u.ioctl.channel = ha->raw[i].address | ha->raw[i].id_list[j]; - gdth_do_cmd(&scp, &gdtcmd, 30); - if (scp.SCp.Message == S_OK) { + gdth_do_cmd(&scp, &gdtcmd, cmnd, 30); + if ((scp.SCp.Message & 0xffff) == S_OK) { strncpy(hrec,pdi->vendor,8); strncpy(hrec+8,pdi->product,16); strncpy(hrec+24,pdi->revision,4); @@ -566,7 +616,7 @@ "\n Chn/ID/LUN: \t%c/%02d/%d \tName: \t%s\n", 'A'+i,pdi->target_id,pdi->lun,hrec); len += size; pos = begin + len; - flag = TRUE; + flag = TRUE; pdi->no_ldrive &= 0xffff; if (pdi->no_ldrive == 0xffff) strcpy(hrec,"--"); @@ -607,8 +657,8 @@ gdtcmd.u.ioctl.channel = ha->raw[i].address | ha->raw[i].id_list[j]; pdef->sddc_type = 0x08; - gdth_do_cmd(&scp, &gdtcmd, 30); - if (scp.SCp.Message == S_OK) { + gdth_do_cmd(&scp, &gdtcmd, cmnd, 30); + if ((scp.SCp.Message & 0xffff) == S_OK) { size = sprintf(buffer+len, " Grown Defects:\t%d\n", pdef->sddc_cnt); @@ -619,10 +669,10 @@ } gdth_ioctl_free(hanum); - if (!flag) { - size = sprintf(buffer+len, "\n --\n"); - len += size; pos = begin + len; - } + if (!flag) { + size = sprintf(buffer+len, "\n --\n"); + len += size; pos = begin + len; + } if (pos < offset) { len = 0; begin = pos; @@ -633,11 +683,11 @@ /* 3. about logical drives */ size = sprintf(buffer+len,"\nLogical Drives:"); len += size; pos = begin + len; - flag = FALSE; + flag = FALSE; if (!gdth_ioctl_alloc(hanum, GDTH_SCRATCH)) goto stop_output; - for (i = 0; i < MAX_HDRIVES; ++i) { + for (i = 0; i < MAX_LDRIVES; ++i) { if (!ha->hdr[i].is_logdrv) continue; drv_no = i; @@ -654,8 +704,8 @@ gdtcmd.u.ioctl.param_size = sizeof(gdth_cdrinfo_str); gdtcmd.u.ioctl.subfunc = CACHE_DRV_INFO; gdtcmd.u.ioctl.channel = drv_no; - gdth_do_cmd(&scp, &gdtcmd, 30); - if (scp.SCp.Message != S_OK) + gdth_do_cmd(&scp, &gdtcmd, cmnd, 30); + if ((scp.SCp.Message & 0xffff) != S_OK) break; pcdi->ld_dtype >>= 16; j++; @@ -675,7 +725,7 @@ "\n Number: \t%-2d \tStatus: \t%s\n", drv_no, hrec); len += size; pos = begin + len; - flag = TRUE; + flag = TRUE; no_mdrv = pcdi->cd_ldcnt; if (no_mdrv > 1 || pcdi->ld_slave != -1) { is_mirr = TRUE; @@ -720,10 +770,10 @@ } gdth_ioctl_free(hanum); - if (!flag) { - size = sprintf(buffer+len, "\n --\n"); - len += size; pos = begin + len; - } + if (!flag) { + size = sprintf(buffer+len, "\n --\n"); + len += size; pos = begin + len; + } if (pos < offset) { len = 0; begin = pos; @@ -734,11 +784,11 @@ /* 4. about array drives */ size = sprintf(buffer+len,"\nArray Drives:"); len += size; pos = begin + len; - flag = FALSE; + flag = FALSE; if (!gdth_ioctl_alloc(hanum, GDTH_SCRATCH)) goto stop_output; - for (i = 0; i < MAX_HDRIVES; ++i) { + for (i = 0; i < MAX_LDRIVES; ++i) { if (!(ha->hdr[i].is_arraydrv && ha->hdr[i].is_master)) continue; /* 4.a array drive info */ @@ -751,8 +801,8 @@ gdtcmd.u.ioctl.param_size = sizeof(gdth_arrayinf_str); gdtcmd.u.ioctl.subfunc = ARRAY_INFO | LA_CTRL_PATTERN; gdtcmd.u.ioctl.channel = i; - gdth_do_cmd(&scp, &gdtcmd, 30); - if (scp.SCp.Message == S_OK) { + gdth_do_cmd(&scp, &gdtcmd, cmnd, 30); + if ((scp.SCp.Message & 0xffff) == S_OK) { if (pai->ai_state == 0) strcpy(hrec, "idle"); else if (pai->ai_state == 2) @@ -792,10 +842,10 @@ } gdth_ioctl_free(hanum); - if (!flag) { - size = sprintf(buffer+len, "\n --\n"); - len += size; pos = begin + len; - } + if (!flag) { + size = sprintf(buffer+len, "\n --\n"); + len += size; pos = begin + len; + } if (pos < offset) { len = 0; begin = pos; @@ -806,11 +856,11 @@ /* 5. about host drives */ size = sprintf(buffer+len,"\nHost Drives:"); len += size; pos = begin + len; - flag = FALSE; + flag = FALSE; if (!gdth_ioctl_alloc(hanum, GDTH_SCRATCH)) goto stop_output; - for (i = 0; i < MAX_HDRIVES; ++i) { + for (i = 0; i < MAX_LDRIVES; ++i) { if (!ha->hdr[i].is_logdrv || (ha->hdr[i].is_arraydrv && !ha->hdr[i].is_master)) continue; @@ -826,15 +876,15 @@ gdtcmd.u.ioctl.channel = i; phg->entries = MAX_HDRIVES; phg->offset = GDTOFFSOF(gdth_hget_str, entry[0]); - gdth_do_cmd(&scp, &gdtcmd, 30); - if (scp.SCp.Message != S_OK) { + gdth_do_cmd(&scp, &gdtcmd, cmnd, 30); + if ((scp.SCp.Message & 0xffff) != S_OK) { ha->hdr[i].ldr_no = i; ha->hdr[i].rw_attribs = 0; ha->hdr[i].start_sec = 0; } else { for (j = 0; j < phg->entries; ++j) { k = phg->entry[j].host_drive; - if (k >= MAX_HDRIVES) + if (k >= MAX_LDRIVES) continue; ha->hdr[k].ldr_no = phg->entry[j].log_drive; ha->hdr[k].rw_attribs = phg->entry[j].rw_attribs; @@ -854,7 +904,7 @@ "\n Number: \t%-2d \tArr/Log. Drive:\t%d\n", i, ha->hdr[i].ldr_no); len += size; pos = begin + len; - flag = TRUE; + flag = TRUE; size = sprintf(buffer+len, " Capacity [MB]:\t%-6d \tStart Sector: \t%d\n", @@ -862,10 +912,10 @@ len += size; pos = begin + len; } - if (!flag) { - size = sprintf(buffer+len, "\n --\n"); - len += size; pos = begin + len; - } + if (!flag) { + size = sprintf(buffer+len, "\n --\n"); + len += size; pos = begin + len; + } if (pos < offset) { len = 0; begin = pos; @@ -887,7 +937,7 @@ gdth_log_event(&estr.event_data, hrec); do_gettimeofday(&tv); sec = (int)(tv.tv_sec - estr.first_stamp); - if (sec < 0) sec = 0; + if (sec < 0) sec = 0; size = sprintf(buffer+len," date- %02d:%02d:%02d\t%s\n", sec/3600, sec%3600/60, sec%60, hrec); len += size; pos = begin + len; @@ -929,19 +979,24 @@ return(len); } -static void gdth_do_cmd(Scsi_Cmnd *scp,gdth_cmd_str *gdtcmd,int timeout) +static void gdth_do_cmd(Scsi_Cmnd *scp, gdth_cmd_str *gdtcmd, + char *cmnd, int timeout) { - char cmnd[12]; struct semaphore sem = MUTEX_LOCKED; + unsigned bufflen; TRACE2(("gdth_do_cmd()\n")); - memset(cmnd, 0, 12); + if (gdtcmd != NULL) { + scp->SCp.this_residual = IOCTL_PRI; + bufflen = sizeof(gdth_cmd_str); + } else { + scp->SCp.this_residual = DEFAULT_PRI; + bufflen = 0; + } scp->request.rq_status = RQ_SCSI_BUSY; scp->request.sem = &sem; - scp->SCp.this_residual = IOCTL_PRI; GDTH_LOCK_SCSI_DOCMD(); - scsi_do_cmd(scp, cmnd, gdtcmd, sizeof(gdth_cmd_str), - gdth_scsi_done, timeout*HZ, 1); + scsi_do_cmd(scp, cmnd, gdtcmd, bufflen, gdth_scsi_done, timeout*HZ, 1); GDTH_UNLOCK_SCSI_DOCMD(); down(&sem); } diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/gdth_proc.h linux/drivers/scsi/gdth_proc.h --- v2.2.13/linux/drivers/scsi/gdth_proc.h Mon Apr 12 09:56:16 1999 +++ linux/drivers/scsi/gdth_proc.h Tue Jan 4 10:12:20 2000 @@ -2,7 +2,7 @@ #define _GDTH_PROC_H /* gdth_proc.h - * $Id: gdth_proc.h,v 1.6 1999/03/05 14:32:36 achim Exp $ + * $Id: gdth_proc.h,v 1.7 1999/05/26 11:49:32 achim Exp $ */ static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum); @@ -18,7 +18,8 @@ static void gdth_start_timeout(int hanum, int busnum, int id); static int gdth_update_timeout(int hanum, Scsi_Cmnd *scp, int timeout); -static void gdth_do_cmd(Scsi_Cmnd *scp,gdth_cmd_str *cmd,int timeout); +static void gdth_do_cmd(Scsi_Cmnd *scp, gdth_cmd_str *cmd, + char *cmnd, int timeout); void gdth_scsi_done(Scsi_Cmnd *scp); #endif diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/ide-scsi.c linux/drivers/scsi/ide-scsi.c --- v2.2.13/linux/drivers/scsi/ide-scsi.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/scsi/ide-scsi.c Tue Jan 4 10:12:20 2000 @@ -302,13 +302,13 @@ static inline unsigned long get_timeout(idescsi_pc_t *pc) { - return IDE_MAX(WAIT_CMD, pc->timeout - jiffies); + return IDE_MAX((30 * HZ), pc->timeout - jiffies); /* CD-RW drives need long timeouts */ } /* * Our interrupt handler. */ -static void idescsi_pc_intr (ide_drive_t *drive) +static ide_startstop_t idescsi_pc_intr (ide_drive_t *drive) { idescsi_scsi_t *scsi = drive->driver_data; byte status, ireason; @@ -338,15 +338,14 @@ if (status & ERR_STAT) rq->errors++; idescsi_end_request (1, HWGROUP(drive)); - return; + return ide_stopped; } bcount = IN_BYTE (IDE_BCOUNTH_REG) << 8 | IN_BYTE (IDE_BCOUNTL_REG); ireason = IN_BYTE (IDE_IREASON_REG); if (ireason & IDESCSI_IREASON_COD) { printk (KERN_ERR "ide-scsi: CoD != 0 in idescsi_pc_intr\n"); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } if (ireason & IDESCSI_IREASON_IO) { temp = pc->actually_transferred + bcount; @@ -366,7 +365,7 @@ pc->current_position += temp; idescsi_discard_data (drive,bcount - temp); ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc)); - return; + return ide_started; } #if IDESCSI_DEBUG_LOG printk (KERN_NOTICE "ide-scsi: The scsi wants to send us more data than expected - allowing transfer\n"); @@ -390,32 +389,34 @@ pc->current_position+=bcount; ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc)); /* And set the interrupt handler again */ + return ide_started; } -static void idescsi_transfer_pc (ide_drive_t *drive) +static ide_startstop_t idescsi_transfer_pc (ide_drive_t *drive) { idescsi_scsi_t *scsi = drive->driver_data; idescsi_pc_t *pc = scsi->pc; byte ireason; + ide_startstop_t startstop; - if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { + if (ide_wait_stat (&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { printk (KERN_ERR "ide-scsi: Strange, packet command initiated yet DRQ isn't asserted\n"); - return; + return startstop; } ireason = IN_BYTE (IDE_IREASON_REG); if ((ireason & (IDESCSI_IREASON_IO | IDESCSI_IREASON_COD)) != IDESCSI_IREASON_COD) { printk (KERN_ERR "ide-scsi: (IO,CoD) != (0,1) while issuing a packet command\n"); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc)); /* Set the interrupt routine */ atapi_output_bytes (drive, scsi->pc->c, 12); /* Send the actual packet */ + return ide_started; } /* * Issue a packet command */ -static void idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *pc) +static ide_startstop_t idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *pc) { idescsi_scsi_t *scsi = drive->driver_data; int bcount; @@ -443,16 +444,17 @@ if (test_bit (IDESCSI_DRQ_INTERRUPT, &scsi->flags)) { ide_set_handler (drive, &idescsi_transfer_pc, get_timeout(pc)); OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* Issue the packet command */ + return ide_started; } else { OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); - idescsi_transfer_pc (drive); + return idescsi_transfer_pc (drive); } } /* * idescsi_do_request is our request handling function. */ -static void idescsi_do_request (ide_drive_t *drive, struct request *rq, unsigned long block) +static ide_startstop_t idescsi_do_request (ide_drive_t *drive, struct request *rq, unsigned long block) { #if IDESCSI_DEBUG_LOG printk (KERN_INFO "rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors); @@ -460,11 +462,11 @@ #endif /* IDESCSI_DEBUG_LOG */ if (rq->cmd == IDESCSI_PC_RQ) { - idescsi_issue_pc (drive, (idescsi_pc_t *) rq->buffer); - return; + return idescsi_issue_pc (drive, (idescsi_pc_t *) rq->buffer); } printk (KERN_ERR "ide-scsi: %s: unsupported command in request queue (%x)\n", drive->name, rq->cmd); idescsi_end_request (0,HWGROUP (drive)); + return ide_stopped; } static int idescsi_open (struct inode *inode, struct file *filp, ide_drive_t *drive) diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/in2000.h linux/drivers/scsi/in2000.h --- v2.2.13/linux/drivers/scsi/in2000.h Mon Aug 9 16:05:56 1999 +++ linux/drivers/scsi/in2000.h Tue Jan 4 10:12:20 2000 @@ -62,6 +62,8 @@ */ #define FAST_READ2_IO() \ +({ \ +int __dummy_1,__dummy_2; \ __asm__ __volatile__ ("\n \ cld \n \ orl %%ecx, %%ecx \n \ @@ -69,11 +71,14 @@ rep \n \ insw (%%dx),%%es:(%%edi) \n \ 1: " \ - : "=D" (sp) /* output */ \ - : "d" (f), "D" (sp), "c" (i) /* input */ \ - : "edx", "ecx", "edi" ) /* trashed */ + : "=D" (sp) ,"=c" (__dummy_1) ,"=d" (__dummy_2) /* output */ \ + : "2" (f), "0" (sp), "1" (i) /* input */ \ + ); /* trashed */ \ +}) #define FAST_WRITE2_IO() \ +({ \ +int __dummy_1,__dummy_2; \ __asm__ __volatile__ ("\n \ cld \n \ orl %%ecx, %%ecx \n \ @@ -81,10 +86,10 @@ rep \n \ outsw %%ds:(%%esi),(%%dx) \n \ 1: " \ - : "=S" (sp) /* output */ \ - : "d" (f), "S" (sp), "c" (i) /* input */ \ - : "edx", "ecx", "esi" ) /* trashed */ - + : "=S" (sp) ,"=c" (__dummy_1) ,"=d" (__dummy_2)/* output */ \ + : "2" (f), "0" (sp), "1" (i) /* input */ \ + ); /* trashed */ \ +}) /* IN2000 io_port offsets */ #define IO_WD_ASR 0x00 /* R - 3393 auxstat reg */ diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/ips.c linux/drivers/scsi/ips.c --- v2.2.13/linux/drivers/scsi/ips.c Tue Jan 4 11:10:38 2000 +++ linux/drivers/scsi/ips.c Tue Jan 4 10:12:20 2000 @@ -60,6 +60,12 @@ /* 0.99.05 - Fix an oops when we get certain passthru commands */ /* 1.00.00 - Initial Public Release */ /* Functionally equivalent to 0.99.05 */ +/* 3.60.00 - Bump max commands to 128 for use with ServeRAID firmware 3.60 */ +/* - Change version to 3.60 to coincide with ServeRAID release */ +/* numbering. */ +/* 3.60.01 - Remove bogus error check in passthru routine */ +/* 3.60.02 - Make DCDB direction based on lookup table */ +/* - Only allow one DCDB command to a SCSI ID at a time */ /* */ /*****************************************************************************/ @@ -111,8 +117,8 @@ /* * DRIVER_VER */ -#define IPS_VERSION_HIGH "1.00" /* MUST be 4 chars */ -#define IPS_VERSION_LOW ".00 " /* MUST be 4 chars */ +#define IPS_VERSION_HIGH "3.60" /* MUST be 4 chars */ +#define IPS_VERSION_LOW ".02 " /* MUST be 4 chars */ struct proc_dir_entry proc_scsi_ips = { #if !defined(PROC_SCSI_IPS) @@ -170,6 +176,63 @@ }; /* + * Direction table + */ +static char ips_command_direction[] = { +IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT, +IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_UNK, +IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT, +IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_OUT, +IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_OUT, +IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_IN, +IPS_DATA_UNK, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_UNK, +IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, +IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_NONE, +IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, +IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_OUT, +IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_NONE, +IPS_DATA_UNK, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_UNK, +IPS_DATA_NONE, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_NONE, +IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_OUT, +IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_NONE, +IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_IN, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_OUT, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, +IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK +}; + +/* * Function prototypes */ int ips_detect(Scsi_Host_Template *); @@ -1351,7 +1414,7 @@ (scb->cmd.basic_io.op_code == DIRECT_CDB_SCATTER_GATHER)) return (0); - if (pt->CmdBSize && pt->CmdBuffer) { + if (pt->CmdBSize) { scb->data_busaddr = VIRT_TO_BUS(scb->scsi_cmd->request_buffer + sizeof(ips_passthru_t)); } else { scb->data_busaddr = 0L; @@ -1675,8 +1738,10 @@ /* set controller IDs */ ha->ha_id[0] = IPS_ADAPTER_ID; - for (i = 1; i < ha->nbus; i++) + for (i = 1; i < ha->nbus; i++) { ha->ha_id[i] = ha->conf->init_id[i-1] & 0x1f; + ha->dcdb_active[i-1] = 0; + } return (1); } @@ -1696,6 +1761,7 @@ ips_next(ips_ha_t *ha) { ips_scb_t *scb; Scsi_Cmnd *SC; + Scsi_Cmnd *p; int ret; DBG("ips_next"); @@ -1768,9 +1834,15 @@ /* * Send "Normal" I/O commands */ - while ((ha->scb_waitlist.head) && - (scb = ips_getscb(ha))) { - SC = ips_removeq_wait_head(&ha->scb_waitlist); + p = ha->scb_waitlist.head; + while ((p) && (scb = ips_getscb(ha))) { + if ((p->channel > 0) && (ha->dcdb_active[p->channel-1] & (1 << p->target))) { + ips_freescb(ha, scb); + p = (Scsi_Cmnd *) p->host_scribble; + continue; + } + + SC = ips_removeq_wait(&ha->scb_waitlist, p); SC->result = DID_OK; SC->host_scribble = NULL; @@ -1785,7 +1857,7 @@ scb->data_len = 0; scb->callback = ipsintr_done; scb->timeout = ips_cmd_timeout; - memset(&scb->cmd, 0, 4); + memset(&scb->cmd, 0, 16); /* copy in the CDB */ memcpy(scb->cdb, SC->cmnd, SC->cmd_len); @@ -1843,11 +1915,11 @@ } - if ((scb->scsi_cmd->request.cmd == READ) && (SC->request_bufflen)) - scb->dcdb.cmd_attribute |= DATA_IN; + scb->dcdb.cmd_attribute |= + ips_command_direction[scb->scsi_cmd->cmnd[0]]; - if ((scb->scsi_cmd->request.cmd == WRITE) && (SC->request_bufflen)) - scb->dcdb.cmd_attribute |= DATA_OUT; + if (!scb->dcdb.cmd_attribute & 0x3) + scb->dcdb.transfer_length = 0; if (scb->data_len >= IPS_MAX_XFER) { scb->dcdb.cmd_attribute |= TRANSFER_64K; @@ -1866,16 +1938,25 @@ scb->scsi_cmd->scsi_done(scb->scsi_cmd); } + if (scb->bus) + ha->dcdb_active[scb->bus-1] &= ~(1 << scb->target_id); + ips_freescb(ha, scb); break; case IPS_SUCCESS_IMM: if (scb->scsi_cmd) scb->scsi_cmd->scsi_done(scb->scsi_cmd); + + if (scb->bus) + ha->dcdb_active[scb->bus-1] &= ~(1 << scb->target_id); + ips_freescb(ha, scb); break; default: break; } /* end case */ + + p = (Scsi_Cmnd *) p->host_scribble; } /* end while */ } @@ -2277,11 +2358,11 @@ scb->sg_len = 0; } - if ((scb->scsi_cmd->request.cmd == READ) && (scb->data_len)) - scb->dcdb.cmd_attribute |= DATA_IN; + scb->dcdb.cmd_attribute |= + ips_command_direction[scb->scsi_cmd->cmnd[0]]; - if ((scb->scsi_cmd->request.cmd == WRITE) && (scb->data_len)) - scb->dcdb.cmd_attribute |= DATA_OUT; + if (!scb->dcdb.cmd_attribute & 0x3) + scb->dcdb.transfer_length = 0; if (scb->data_len >= IPS_MAX_XFER) { scb->dcdb.cmd_attribute |= TRANSFER_64K; @@ -2317,6 +2398,9 @@ } /* end if passthru */ #endif + if (scb->bus) + ha->dcdb_active[scb->bus-1] &= ~(1 << scb->target_id); + /* call back to SCSI layer */ scb->scsi_cmd->scsi_done(scb->scsi_cmd); ips_freescb(ha, scb); @@ -2341,6 +2425,16 @@ DBG("ips_map_status"); if (scb->bus) { +#if IPS_DEBUG >= 10 + printk(KERN_NOTICE "(%s) Physical device error: %x %x, Sense Key: %x, ASC: %x, ASCQ: %x\n", + ips_name, + scb->basic_status, + scb->extended_status, + scb->dcdb.sense_info[2] & 0xf, + scb->dcdb.sense_info[12], + scb->dcdb.sense_info[13]); +#endif + /* copy SCSI status and sense data for DCDB commands */ memcpy(scb->scsi_cmd->sense_buffer, scb->dcdb.sense_info, sizeof(scb->scsi_cmd->sense_buffer)); @@ -2687,6 +2781,7 @@ else scb->cmd.dcdb.op_code = DIRECT_CDB_SCATTER_GATHER; + ha->dcdb_active[scb->bus-1] |= (1 << scb->target_id); scb->cmd.dcdb.command_id = IPS_COMMAND_ID(ha, scb); scb->cmd.dcdb.dcdb_address = VIRT_TO_BUS(&scb->dcdb); scb->cmd.dcdb.reserved = 0; diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/ips.h linux/drivers/scsi/ips.h --- v2.2.13/linux/drivers/scsi/ips.h Tue Jan 4 11:10:38 2000 +++ linux/drivers/scsi/ips.h Tue Jan 4 10:12:20 2000 @@ -76,102 +76,102 @@ #define UDELAY udelay #define MDELAY mdelay - #define verify_area_20(t,a,sz) (0) /* success */ - #define PUT_USER put_user - #define __PUT_USER __put_user - #define PUT_USER_RET put_user_ret - #define GET_USER get_user - #define __GET_USER __get_user - #define GET_USER_RET get_user_ret + #define verify_area_20(t,a,sz) (0) /* success */ + #define PUT_USER put_user + #define __PUT_USER __put_user + #define PUT_USER_RET put_user_ret + #define GET_USER get_user + #define __GET_USER __get_user + #define GET_USER_RET get_user_ret /* * Adapter address map equates */ - #define HISR 0x08 /* Host Interrupt Status Reg */ - #define CCSAR 0x10 /* Cmd Channel System Addr Reg */ - #define CCCR 0x14 /* Cmd Channel Control Reg */ - #define SQHR 0x20 /* Status Q Head Reg */ - #define SQTR 0x24 /* Status Q Tail Reg */ - #define SQER 0x28 /* Status Q End Reg */ - #define SQSR 0x2C /* Status Q Start Reg */ - #define SCPR 0x05 /* Subsystem control port reg */ - #define ISPR 0x06 /* interrupt status port reg */ - #define CBSP 0x07 /* CBSP register */ + #define HISR 0x08 /* Host Interrupt Status Reg */ + #define CCSAR 0x10 /* Cmd Channel System Addr Reg */ + #define CCCR 0x14 /* Cmd Channel Control Reg */ + #define SQHR 0x20 /* Status Q Head Reg */ + #define SQTR 0x24 /* Status Q Tail Reg */ + #define SQER 0x28 /* Status Q End Reg */ + #define SQSR 0x2C /* Status Q Start Reg */ + #define SCPR 0x05 /* Subsystem control port reg */ + #define ISPR 0x06 /* interrupt status port reg */ + #define CBSP 0x07 /* CBSP register */ /* * Adapter register bit equates */ - #define GHI 0x04 /* HISR General Host Interrupt */ - #define SQO 0x02 /* HISR Status Q Overflow */ - #define SCE 0x01 /* HISR Status Channel Enqueue */ - #define SEMAPHORE 0x08 /* CCCR Semaphore Bit */ - #define ILE 0x10 /* CCCR ILE Bit */ - #define START_COMMAND 0x101A /* CCCR Start Command Channel */ - #define START_STOP_BIT 0x0002 /* CCCR Start/Stop Bit */ - #define RST 0x80 /* SCPR Reset Bit */ - #define EBM 0x02 /* SCPR Enable Bus Master */ - #define EI 0x80 /* HISR Enable Interrupts */ - #define OP 0x01 /* OP bit in CBSP */ + #define GHI 0x04 /* HISR General Host Interrupt */ + #define SQO 0x02 /* HISR Status Q Overflow */ + #define SCE 0x01 /* HISR Status Channel Enqueue */ + #define SEMAPHORE 0x08 /* CCCR Semaphore Bit */ + #define ILE 0x10 /* CCCR ILE Bit */ + #define START_COMMAND 0x101A /* CCCR Start Command Channel */ + #define START_STOP_BIT 0x0002 /* CCCR Start/Stop Bit */ + #define RST 0x80 /* SCPR Reset Bit */ + #define EBM 0x02 /* SCPR Enable Bus Master */ + #define EI 0x80 /* HISR Enable Interrupts */ + #define OP 0x01 /* OP bit in CBSP */ /* * Adapter Command ID Equates */ - #define GET_LOGICAL_DRIVE_INFO 0x19 - #define GET_SUBSYS_PARAM 0x40 - #define READ_NVRAM_CONFIGURATION 0x38 - #define RW_NVRAM_PAGE 0xBC - #define IPS_READ 0x02 - #define IPS_WRITE 0x03 - #define ENQUIRY 0x05 - #define FLUSH_CACHE 0x0A - #define NORM_STATE 0x00 - #define READ_SCATTER_GATHER 0x82 - #define WRITE_SCATTER_GATHER 0x83 - #define DIRECT_CDB 0x04 - #define DIRECT_CDB_SCATTER_GATHER 0x84 - #define CONFIG_SYNC 0x58 - #define POCL 0x30 - #define GET_ERASE_ERROR_TABLE 0x17 - #define RESET_CHANNEL 0x1A - #define CSL 0xFF - #define ADAPT_RESET 0xFF + #define GET_LOGICAL_DRIVE_INFO 0x19 + #define GET_SUBSYS_PARAM 0x40 + #define READ_NVRAM_CONFIGURATION 0x38 + #define RW_NVRAM_PAGE 0xBC + #define IPS_READ 0x02 + #define IPS_WRITE 0x03 + #define ENQUIRY 0x05 + #define FLUSH_CACHE 0x0A + #define NORM_STATE 0x00 + #define READ_SCATTER_GATHER 0x82 + #define WRITE_SCATTER_GATHER 0x83 + #define DIRECT_CDB 0x04 + #define DIRECT_CDB_SCATTER_GATHER 0x84 + #define CONFIG_SYNC 0x58 + #define POCL 0x30 + #define GET_ERASE_ERROR_TABLE 0x17 + #define RESET_CHANNEL 0x1A + #define CSL 0xFF + #define ADAPT_RESET 0xFF /* * Adapter Equates */ - #define IPS_MAX_ADAPTERS 16 - #define IPS_MAX_IOCTL 1 - #define IPS_MAX_IOCTL_QUEUE 8 - #define IPS_MAX_QUEUE 128 - #define IPS_BLKSIZE 512 - #define MAX_SG_ELEMENTS 17 - #define MAX_LOGICAL_DRIVES 8 - #define MAX_CHANNELS 3 - #define MAX_TARGETS 15 - #define MAX_CHUNKS 16 - #define MAX_CMDS 64 - #define IPS_MAX_XFER 0x10000 - #define COMP_MODE_HEADS 128 - #define COMP_MODE_SECTORS 32 - #define NORM_MODE_HEADS 254 - #define NORM_MODE_SECTORS 63 - #define NVRAM_PAGE5_SIGNATURE 0xFFDDBB99 - #define MAX_POST_BYTES 0x02 - #define MAX_CONFIG_BYTES 0x02 - #define GOOD_POST_BASIC_STATUS 0x80 - #define SEMAPHORE_TIMEOUT 2000 - #define IPS_INTR_OFF 0 - #define IPS_INTR_ON 1 - #define IPS_ADAPTER_ID 0xF - #define IPS_VENDORID 0x1014 - #define IPS_DEVICEID 0x002E - #define TIMEOUT_10 0x10 - #define TIMEOUT_60 0x20 - #define TIMEOUT_20M 0x30 - #define STATUS_SIZE 4 - #define STATUS_Q_SIZE (MAX_CMDS+1) * STATUS_SIZE - #define ONE_MSEC 1 - #define ONE_SEC 1000 + #define IPS_MAX_ADAPTERS 16 + #define IPS_MAX_IOCTL 1 + #define IPS_MAX_IOCTL_QUEUE 8 + #define IPS_MAX_QUEUE 128 + #define IPS_BLKSIZE 512 + #define MAX_SG_ELEMENTS 17 + #define MAX_LOGICAL_DRIVES 8 + #define MAX_CHANNELS 3 + #define MAX_TARGETS 15 + #define MAX_CHUNKS 16 + #define MAX_CMDS 128 + #define IPS_MAX_XFER 0x10000 + #define COMP_MODE_HEADS 128 + #define COMP_MODE_SECTORS 32 + #define NORM_MODE_HEADS 254 + #define NORM_MODE_SECTORS 63 + #define NVRAM_PAGE5_SIGNATURE 0xFFDDBB99 + #define MAX_POST_BYTES 0x02 + #define MAX_CONFIG_BYTES 0x02 + #define GOOD_POST_BASIC_STATUS 0x80 + #define SEMAPHORE_TIMEOUT 2000 + #define IPS_INTR_OFF 0 + #define IPS_INTR_ON 1 + #define IPS_ADAPTER_ID 0xF + #define IPS_VENDORID 0x1014 + #define IPS_DEVICEID 0x002E + #define TIMEOUT_10 0x10 + #define TIMEOUT_60 0x20 + #define TIMEOUT_20M 0x30 + #define STATUS_SIZE 4 + #define STATUS_Q_SIZE (MAX_CMDS+1) * STATUS_SIZE + #define ONE_MSEC 1 + #define ONE_SEC 1000 /* * Adapter Basic Status Codes @@ -195,24 +195,24 @@ /* * Adapter Extended Status Equates */ - #define SELECTION_TIMEOUT 0xF0 - #define DATA_OVER_UNDER_RUN 0xF2 - #define EXT_HOST_RESET 0xF7 - #define EXT_DEVICE_RESET 0xF8 - #define EXT_RECOVERY 0xFC - #define EXT_CHECK_CONDITION 0xFF + #define SELECTION_TIMEOUT 0xF0 + #define DATA_OVER_UNDER_RUN 0xF2 + #define EXT_HOST_RESET 0xF7 + #define EXT_DEVICE_RESET 0xF8 + #define EXT_RECOVERY 0xFC + #define EXT_CHECK_CONDITION 0xFF /* * Operating System Defines */ - #define OS_WINDOWS_NT 0x01 - #define OS_NETWARE 0x02 - #define OS_OPENSERVER 0x03 - #define OS_UNIXWARE 0x04 - #define OS_SOLARIS 0x05 - #define OS_OS2 0x06 - #define OS_LINUX 0x07 - #define OS_FREEBSD 0x08 + #define OS_WINDOWS_NT 0x01 + #define OS_NETWARE 0x02 + #define OS_OPENSERVER 0x03 + #define OS_UNIXWARE 0x04 + #define OS_SOLARIS 0x05 + #define OS_OS2 0x06 + #define OS_LINUX 0x07 + #define OS_FREEBSD 0x08 /* * Adapter Command/Status Packet Definitions @@ -224,26 +224,28 @@ /* * Logical Drive Equates */ - #define OFF_LINE 0x02 - #define OKAY 0x03 - #define FREE 0x00 - #define SYS 0x06 - #define CRS 0x24 + #define OFF_LINE 0x02 + #define OKAY 0x03 + #define FREE 0x00 + #define SYS 0x06 + #define CRS 0x24 /* * DCDB Table Equates */ +#ifndef HOSTS_C #define NO_DISCONNECT 0x00 #define DISCONNECT_ALLOWED 0x80 #define NO_AUTO_REQUEST_SENSE 0x40 - #define DATA_IN 0x01 - #define DATA_OUT 0x02 + #define IPS_DATA_NONE 0x00 + #define IPS_DATA_UNK 0x00 + #define IPS_DATA_IN 0x01 + #define IPS_DATA_OUT 0x02 #define TRANSFER_64K 0x08 #define NOTIMEOUT 0x00 #define TIMEOUT10 0x10 #define TIMEOUT60 0x20 #define TIMEOUT20M 0x30 - /* * Host adapter Flags (bit numbers) */ @@ -256,7 +258,7 @@ */ #define SCB_ACTIVE 0x00001 #define SCB_WAITING 0x00002 - +#endif /* HOSTS_C */ /* * Passthru stuff */ @@ -590,6 +592,8 @@ /* * Inquiry Data Format */ +#ifndef HOSTS_C + typedef struct { u8 DeviceType:5; u8 DeviceTypeQualifier:3; @@ -614,6 +618,7 @@ u8 Reserved3[40]; } INQUIRYDATA, *PINQUIRYDATA; +#endif /* * Read Capacity Data Format */ @@ -734,6 +739,7 @@ typedef struct ips_ha { u8 ha_id[MAX_CHANNELS+1]; + u32 dcdb_active[MAX_CHANNELS]; u32 io_addr; /* Base I/O address */ u8 irq; /* IRQ for adapter */ u8 ntargets; /* Number of targets */ diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/megaraid.c linux/drivers/scsi/megaraid.c --- v2.2.13/linux/drivers/scsi/megaraid.c Tue Oct 19 17:10:38 1999 +++ linux/drivers/scsi/megaraid.c Tue Jan 4 10:12:20 2000 @@ -9,7 +9,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Version : 1.04 + * Version : 1.05 * * Description: Linux device driver for AMI MegaRAID controller * @@ -111,6 +111,16 @@ * The addtional 32 bit field for 64bit address in the newly defined * mailbox64 structure is set to 0 at this point. * + * Version 1.05 + * Changed the queing implementation for handling SCBs and completed + * commands. + * Added spinlocks in the interrupt service routine to enable the dirver + * function in the SMP environment. + * Fixed the problem of unnecessary aborts in the abort entry point, which + * also enables the driver to handle large amount of I/O requests for + * long duration of time. + * + * * BUGS: * Some older 2.1 kernels (eg. 2.1.90) have a bug in pci.c that * fails to detect the controller as a pci device on the system. @@ -125,7 +135,8 @@ #define CRLFSTR "\n" #define IOCTL_CMD_NEW 0x81 -#define MEGARAID_VERSION "v1.04 (August 16, 1999)" +#define MEGARAID_VERSION "v1.05 (October 27, 1999)" + #include #include @@ -145,8 +156,6 @@ #include #include #include -#include -#include #include #include #include @@ -209,31 +218,6 @@ #define MAX_SERBUF 160 #define COM_BASE 0x2f8 -#define ENQUEUE(obj,type,list,next) \ -{ type **node; long cpuflag; \ - spin_lock_irqsave(&mega_lock,cpuflag);\ - for(node=&(list); *node; node=(type **)&(*node)->##next); \ - (*node) = obj; \ - (*node)->##next = NULL; \ - spin_unlock_irqrestore(&mega_lock,cpuflag);\ -} - -/* a non-locking version (if we already have the lock) */ -#define ENQUEUE_NL(obj,type,list,next) \ -{ type **node; \ - for(node=&(list); *node; node=(type **)&(*node)->##next); \ - (*node) = obj; \ - (*node)->##next = NULL; \ -} - -#define DEQUEUE(obj,type,list,next) \ -{ long cpuflag; \ - spin_lock_irqsave(&mega_lock,cpuflag);\ - if ((obj=list) != NULL) {\ - list = (type *)(list)->##next; \ - } \ - spin_unlock_irqrestore(&mega_lock,cpuflag);\ -}; u32 RDINDOOR (mega_host_config * megaCfg) { @@ -265,19 +249,67 @@ u_char * mboxData, mega_scb * scb, int intr); -static int build_sglist (mega_host_config * megaCfg, mega_scb * scb, +static int mega_build_sglist (mega_host_config * megaCfg, mega_scb * scb, u32 * buffer, u32 * length); static int mega_busyWaitMbox(mega_host_config *); static void mega_runpendq (mega_host_config *); -static void mega_rundoneq (void); +static void mega_rundoneq (mega_host_config *); static void mega_cmd_done (mega_host_config *, mega_scb *, int); static mega_scb *mega_ioctl (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt); -static inline void freeSgList(mega_host_config *megaCfg); +static inline void mega_freeSgList(mega_host_config *megaCfg); static void mega_Convert8ldTo40ld( mega_RAIDINQ *inquiry, mega_Enquiry3 *enquiry3, megaRaidProductInfo *productInfo ); + + + + +#if LINUX_VERSION_CODE > 0x020100 +# include +# include +# define cpuid smp_processor_id() +# if LINUX_VERSION_CODE < 0x020195 +# define DRIVER_LOCK_T unsigned long cpu_flags = 0; +# define DRIVER_LOCK_INIT(p) \ + spin_lock_init(&p->mega_lock); +# define DRIVER_LOCK(p) \ + if(!p->cpu_lock_count[cpuid]) { \ + spin_lock_irqsave(&p->mega_lock, cpu_flags); \ + p->cpu_lock_count[cpuid]++; \ + } else { \ + p->cpu_lock_count[cpuid]++; \ + } +# define DRIVER_UNLOCK(p) \ + if(--p->cpu_lock_count[cpuid] == 0) \ + spin_unlock_irqrestore(&p->mega_lock, cpu_flags); +# define IO_LOCK(p) spin_lock_irqsave(&io_request_lock,cpu_flags); +# define IO_UNLOCK(p) spin_unlock_irqrestore(&io_request_lock,cpu_flags); +# else +# define DRIVER_LOCK_T +# define DRIVER_LOCK_INIT(p) +# define DRIVER_LOCK(p) +# define DRIVER_UNLOCK(p) +# define IO_LOCK_T unsigned long io_flags = 0; +# define IO_LOCK spin_lock_irqsave(&io_request_lock,io_flags); +# define IO_UNLOCK spin_unlock_irqrestore(&io_request_lock,io_flags); +# endif +#else +# define cpuid 0 +# define DRIVER_LOCK_T long cpu_flags; +# define DRIVER_LOCK_INIT(p) +# define DRIVER_LOCK(p) \ + save_flags(cpu_flags); \ + cli(); +# define DRIVER_UNLOCK(p) \ + restore_flags(cpu_flags); +# define IO_LOCK(p) DRIVER_LOCK(p) +# define IO_UNLOCK(p) DRIVER_UNLOCK(p) +# define le32_to_cpu(x) (x) +# define cpu_to_le32(x) (x) +#endif + /* set SERDEBUG to 1 to enable serial debugging */ #define SERDEBUG 0 #if SERDEBUG @@ -314,13 +346,11 @@ static mega_scb *pLastScb = NULL; -/* Queue of pending/completed SCBs */ -static Scsi_Cmnd *qCompleted = NULL; #if SERDEBUG volatile static spinlock_t serial_lock; #endif -volatile static spinlock_t mega_lock; +//volatile static spinlock_t mega_lock; struct proc_dir_entry proc_scsi_megaraid = { @@ -386,7 +416,7 @@ #define TRACE(A) #endif -void callDone (Scsi_Cmnd * SCpnt) +static void callDone (Scsi_Cmnd * SCpnt) { if (SCpnt->result) { TRACE (("*** %.08lx %.02x <%d.%d.%d> = %x\n", SCpnt->serial_number, @@ -406,37 +436,66 @@ * Free a SCB structure *======================= */ -static void freeSCB (mega_host_config *megaCfg, mega_scb * pScb) +static void mega_freeSCB (mega_host_config *megaCfg, mega_scb * pScb) { - mega_scb **ppScb; + + mega_scb *pScbtmp; + + if ((pScb == NULL) || (pScb->idx >= 0xFE)) { + return ; + } /* Unlink from pending queue */ - for(ppScb=&megaCfg->qPending; *ppScb; ppScb=&(*ppScb)->next) { - if (*ppScb == pScb) { - *ppScb = pScb->next; + + if(pScb == megaCfg->qPendingH) { + if(megaCfg->qPendingH == megaCfg->qPendingT ) + megaCfg->qPendingH = megaCfg->qPendingT = NULL; + else { + megaCfg->qPendingH = megaCfg->qPendingH->next; + } + megaCfg->qPcnt--; + } + else { + for(pScbtmp=megaCfg->qPendingH; pScbtmp; pScbtmp=pScbtmp->next) { + if (pScbtmp->next == pScb) { + pScbtmp->next = pScb->next; + if(pScb == megaCfg->qPendingT) { + megaCfg->qPendingT = pScbtmp; + } + megaCfg->qPcnt--; break; } } + } - /* Link back into list */ + /* Link back into free list */ pScb->state = SCB_FREE; pScb->SCpnt = NULL; - pScb->next = megaCfg->qFree; - megaCfg->qFree = pScb; + if(megaCfg->qFreeH == (mega_scb *) NULL ) { + megaCfg->qFreeH = megaCfg->qFreeT = pScb; + } + else { + megaCfg->qFreeT->next = pScb; + megaCfg->qFreeT = pScb; + } + megaCfg->qFreeT->next = NULL; + megaCfg->qFcnt++; + } /*=========================== * Allocate a SCB structure *=========================== */ -static mega_scb * allocateSCB (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt) +static mega_scb * mega_allocateSCB (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt) { mega_scb *pScb; /* Unlink command from Free List */ - if ((pScb = megaCfg->qFree) != NULL) { - megaCfg->qFree = pScb->next; + if ((pScb = megaCfg->qFreeH) != NULL) { + megaCfg->qFreeH = pScb->next; + megaCfg->qFcnt--; pScb->isrcount = jiffies; pScb->next = NULL; @@ -455,62 +514,72 @@ * Initialize SCB structures *================================================ */ -static int initSCB (mega_host_config * megaCfg) +static int mega_initSCB (mega_host_config * megaCfg) { int idx; - megaCfg->qFree = NULL; + megaCfg->qFreeH = NULL; + megaCfg->qFcnt = 0; +#if DEBUG +if(megaCfg->max_cmds >= MAX_COMMANDS) { +printk("megaraid:ctlr max cmds = %x : MAX_CMDS = %x", megaCfg->max_cmds, MAX_COMMANDS); +} +#endif + for (idx = megaCfg->max_cmds-1; idx >= 0; idx--) { megaCfg->scbList[idx].idx = idx; megaCfg->scbList[idx].sgList = kmalloc(sizeof(mega_sglist) * MAX_SGLIST, GFP_ATOMIC | GFP_DMA); if (megaCfg->scbList[idx].sgList == NULL) { printk(KERN_WARNING "Can't allocate sglist for id %d\n",idx); - freeSgList(megaCfg); + mega_freeSgList(megaCfg); return -1; } if (idx < MAX_COMMANDS) { /* Link to free list */ - freeSCB(megaCfg, &megaCfg->scbList[idx]); + mega_freeSCB(megaCfg, &megaCfg->scbList[idx]); } } return 0; } /* Run through the list of completed requests */ -static void mega_rundoneq () +static void mega_rundoneq (mega_host_config *megaCfg) { Scsi_Cmnd *SCpnt; - while (1) { - DEQUEUE (SCpnt, Scsi_Cmnd, qCompleted, host_scribble); - if (SCpnt == NULL) - return; + while ((SCpnt = megaCfg->qCompletedH) != NULL) { + megaCfg->qCompletedH = (Scsi_Cmnd *)SCpnt->host_scribble; + megaCfg->qCcnt--; + SCpnt->host_scribble = (unsigned char *) NULL ; // XC : sep 14 /* Callback */ callDone (SCpnt); } + megaCfg->qCompletedH = megaCfg->qCompletedT = NULL; } /* - Runs through the list of pending requests - Assumes that mega_lock spin_lock has been acquired. -*/ + * Runs through the list of pending requests + * Assumes that mega_lock spin_lock has been acquired. + */ static void mega_runpendq(mega_host_config *megaCfg) { mega_scb *pScb; /* Issue any pending commands to the card */ - for(pScb=megaCfg->qPending; pScb; pScb=pScb->next) { + for(pScb=megaCfg->qPendingH; pScb; pScb=pScb->next) { if (pScb->state == SCB_ACTIVE) { - megaIssueCmd(megaCfg, pScb->mboxData, pScb, 1); + if(megaIssueCmd(megaCfg, pScb->mboxData, pScb, 1)) + return; } } } /* Add command to the list of completed requests */ -static void mega_cmd_done (mega_host_config * megaCfg, mega_scb * pScb, +static void +mega_cmd_done (mega_host_config * megaCfg, mega_scb * pScb, int status) { int islogical; @@ -533,7 +602,7 @@ TRACE(("pScb->idx = ",pScb->idx)); TRACE(("pScb->state = ",pScb->state)); TRACE(("pScb->state = ",pScb->state)); - printk("Problem...!\n"); + printk("megaraid:Problem...!\n"); while(1); } @@ -588,10 +657,18 @@ /* not IOCTL_CMD_NEW SCB, freeSCB()*/ /* For IOCTL_CMD_NEW SCB, delay freeSCB() in megaraid_queue() * after copy data back to user space*/ - freeSCB(megaCfg, pScb); + mega_freeSCB(megaCfg, pScb); /* Add Scsi_Command to end of completed queue */ - ENQUEUE_NL(SCpnt, Scsi_Cmnd, qCompleted, host_scribble); + if( megaCfg->qCompletedH == NULL ) { + megaCfg->qCompletedH = megaCfg->qCompletedT = SCpnt; + } + else { + megaCfg->qCompletedT->host_scribble = (unsigned char *) SCpnt; + megaCfg->qCompletedT = SCpnt; + } + megaCfg->qCompletedT->host_scribble = (unsigned char *) NULL; + megaCfg->qCcnt++; } /*------------------------------------------------------------------- @@ -661,7 +738,7 @@ case READ_CAPACITY: case INQUIRY: /* Allocate a SCB and initialize passthru */ - if ((pScb = allocateSCB (megaCfg, SCpnt)) == NULL) { + if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) { SCpnt->result = (DID_ERROR << 16); callDone (SCpnt); return NULL; @@ -692,7 +769,7 @@ case READ_10: case WRITE_10: /* Allocate a SCB and initialize mailbox */ - if ((pScb = allocateSCB (megaCfg, SCpnt)) == NULL) { + if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) { SCpnt->result = (DID_ERROR << 16); callDone (SCpnt); return NULL; @@ -728,7 +805,7 @@ } /* Calculate Scatter-Gather info */ - mbox->numsgelements = build_sglist (megaCfg, pScb, + mbox->numsgelements = mega_build_sglist (megaCfg, pScb, (u32 *) & mbox->xferaddr, (u32 *) & seg); @@ -747,7 +824,7 @@ *-----------------------------------------------------*/ else { /* Allocate a SCB and initialize passthru */ - if ((pScb = allocateSCB (megaCfg, SCpnt)) == NULL) { + if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) { SCpnt->result = (DID_ERROR << 16); callDone (SCpnt); return NULL; @@ -768,7 +845,7 @@ pthru->cdblen = SCpnt->cmd_len; memcpy (pthru->cdb, SCpnt->cmnd, SCpnt->cmd_len); - pthru->numsgelements = build_sglist (megaCfg, pScb, + pthru->numsgelements = mega_build_sglist (megaCfg, pScb, (u32 *) & pthru->dataxferaddr, (u32 *) & pthru->dataxferlen); @@ -795,19 +872,12 @@ unsigned char *data = (unsigned char *)SCpnt->request_buffer; int i; - if ((pScb = allocateSCB (megaCfg, SCpnt)) == NULL) { + if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) { SCpnt->result = (DID_ERROR << 16); callDone (SCpnt); return NULL; } -#if 0 - printk("\nBUF: "); - for (i=0;i<18;i++) { - printk(" %x",data[i]); - } - printk("......\n"); -#endif mboxdata = (u8 *) & pScb->mboxData; mbox = (mega_ioctl_mbox *) & pScb->mboxData; @@ -831,7 +901,7 @@ mailbox->cmd = MEGA_MBOXCMD_PASSTHRU; mailbox->xferaddr = virt_to_bus (pthru); - pthru->numsgelements = build_sglist (megaCfg, pScb, + pthru->numsgelements = mega_build_sglist (megaCfg, pScb, (u32 *) & pthru->dataxferaddr, (u32 *) & pthru->dataxferlen); @@ -890,7 +960,7 @@ } else { - mbox->numsgelements = build_sglist (megaCfg, pScb, + mbox->numsgelements = mega_build_sglist (megaCfg, pScb, (u32 *) & mbox->xferaddr, (u32 *) & seg); @@ -918,32 +988,35 @@ } #endif +#if DEBUG +static unsigned int cum_time = 0; +static unsigned int cum_time_cnt = 0; +#endif + /*-------------------------------------------------------------------- * Interrupt service routine *--------------------------------------------------------------------*/ static void megaraid_isr (int irq, void *devp, struct pt_regs *regs) { + IO_LOCK_T mega_host_config *megaCfg; u_char byte, idx, sIdx, tmpBox[MAILBOX_SIZE]; - u32 dword; + u32 dword=0; mega_mailbox *mbox; mega_scb *pScb; - long flags; - int qCnt, qStatus; + u_char qCnt, qStatus; + u_char completed[MAX_FIRMWARE_STATUS]; + Scsi_Cmnd *SCpnt; megaCfg = (mega_host_config *) devp; mbox = (mega_mailbox *)tmpBox; -#if LINUX_VERSION_CODE >= 0x20100 - spin_lock_irqsave (&io_request_lock, flags); -#endif - - while (megaCfg->host->irq == irq) { - spin_lock_irqsave (&mega_lock, flags); + if (megaCfg->host->irq == irq) { if (megaCfg->flag & IN_ISR) { TRACE (("ISR called reentrantly!!\n")); + printk ("ISR called reentrantly!!\n"); } megaCfg->flag |= IN_ISR; @@ -952,48 +1025,75 @@ printk(KERN_WARNING "Error: mailbox busy in isr!\n"); } - /* Check if a valid interrupt is pending */ if (megaCfg->flag & BOARD_QUARTZ) { dword = RDOUTDOOR (megaCfg); if (dword != 0x10001234) { /* Spurious interrupt */ megaCfg->flag &= ~IN_ISR; - spin_unlock_irqrestore (&mega_lock, flags); - break; +//#if LINUX_VERSION_CODE >= 0x20100 +// IO_UNLOCK; +//#endif +// break; + return; } - WROUTDOOR (megaCfg, dword); - - /* Copy to temp location */ - memcpy(tmpBox, (mega_mailbox *)megaCfg->mbox, MAILBOX_SIZE); - - /* Acknowledge interrupt */ - WRINDOOR (megaCfg, virt_to_bus (megaCfg->mbox) | 0x2); - while (RDINDOOR (megaCfg) & 0x02); } else { byte = READ_PORT (megaCfg->host->io_port, INTR_PORT); if ((byte & VALID_INTR_BYTE) == 0) { /* Spurious interrupt */ megaCfg->flag &= ~IN_ISR; - spin_unlock_irqrestore (&mega_lock, flags); - break; +//#if LINUX_VERSION_CODE >= 0x20100 +// IO_UNLOCK; +//#endif +// break; + return; } WRITE_PORT (megaCfg->host->io_port, INTR_PORT, byte); + } + + for(idx=0;idx= 0x20100 + IO_LOCK; +#endif + + qCnt = 0xff; + while ((qCnt = megaCfg->mbox->numstatus) == 0xFF) + ; - /* Copy to temp location */ - memcpy(tmpBox, (mega_mailbox *)megaCfg->mbox, MAILBOX_SIZE); + qStatus = 0xff; + while ((qStatus = megaCfg->mbox->status) == 0xFF) + ; + /* Get list of completed requests */ + for (idx = 0; idxmbox->completed[idx]) == 0xFF) { + printk("p"); + } + completed[idx] = sIdx; + sIdx = 0xFF; + } + + if (megaCfg->flag & BOARD_QUARTZ) { + WROUTDOOR (megaCfg, dword); /* Acknowledge interrupt */ + WRINDOOR (megaCfg, virt_to_bus (megaCfg->mbox) | 0x2); + while (RDINDOOR (megaCfg) & 0x02); + } + else { CLEAR_INTR (megaCfg->host->io_port); } - qCnt = mbox->numstatus; - qStatus = mbox->status; +#if DEBUG + if(qCnt >= MAX_FIRMWARE_STATUS) { + printk("megaraid_isr: cmplt=%d ", qCnt); + } +#endif for (idx = 0; idx < qCnt; idx++) { - sIdx = mbox->completed[idx]; - if (sIdx > 0) { + sIdx = completed[idx]; + if ((sIdx > 0) && (sIdx <= MAX_COMMANDS)) { pScb = &megaCfg->scbList[sIdx - 1]; /* ASSERT(pScb->state == SCB_ISSUED); */ @@ -1001,12 +1101,33 @@ #if DEBUG if (((jiffies) - pScb->isrcount) > maxCmdTime) { maxCmdTime = (jiffies) - pScb->isrcount; - printk("cmd time = %u\n", maxCmdTime); + printk("megaraid_isr : cmd time = %u\n", maxCmdTime); } #endif - +/* + * Assuming that the scsi command, for which an abort request was received + * earlier has completed. + */ if (pScb->state == SCB_ABORTED) { - printk("Received aborted SCB! %u\n", (int)((jiffies)-pScb->isrcount)); + SCpnt = pScb->SCpnt; +#if DEBUG +printk("megaraid_isr:fcnt=%d, pcnt=%d, qcnt=%d\n",megaCfg->qFcnt, megaCfg->qPcnt, megaCfg->qCcnt); +#endif + } + if (pScb->state == SCB_RESET) { + SCpnt = pScb->SCpnt; + mega_freeSCB (megaCfg, pScb); + SCpnt->result = (DID_RESET << 16) ; + if( megaCfg->qCompletedH == NULL ) { + megaCfg->qCompletedH = megaCfg->qCompletedT = SCpnt; + } + else { + megaCfg->qCompletedT->host_scribble = (unsigned char *) SCpnt; + megaCfg->qCompletedT = SCpnt; + } + megaCfg->qCompletedT->host_scribble = (unsigned char *) NULL; + megaCfg->qCcnt++; + continue; } if (*(pScb->SCpnt->cmnd)==IOCTL_CMD_NEW) @@ -1017,24 +1138,23 @@ mega_cmd_done(megaCfg, pScb, qStatus); } - + else { + printk("megaraid: wrong cmd id completed from firmware:id=%x\n",sIdx); + for(;;); + } } - spin_unlock_irqrestore (&mega_lock, flags); - megaCfg->flag &= ~IN_ISR; - - mega_rundoneq(); + mega_rundoneq(megaCfg); + megaCfg->flag &= ~IN_ISR; /* Loop through any pending requests */ - spin_lock_irqsave(&mega_lock, flags); mega_runpendq(megaCfg); - spin_unlock_irqrestore(&mega_lock,flags); +#if LINUX_VERSION_CODE >= 0x20100 + IO_UNLOCK; +#endif } -#if LINUX_VERSION_CODE >= 0x20100 - spin_unlock_irqrestore (&io_request_lock, flags); -#endif } /*==================================================*/ @@ -1078,7 +1198,6 @@ mega_mailbox *mbox = (mega_mailbox *) megaCfg->mbox; u_char byte; u32 cmdDone; - Scsi_Cmnd *SCpnt; u32 phys_mbox; u8 retval=-1; @@ -1087,18 +1206,15 @@ phys_mbox = virt_to_bus (megaCfg->mbox); -#if 0 - if (intr && mbox->busy) { - return 0; - } -#endif - #if DEBUG showMbox(pScb); #endif /* Wait until mailbox is free */ - while (mega_busyWaitMbox (megaCfg)) { +#if 0 + while (mega_busyWaitMbox (megaCfg)) +#endif + if (mega_busyWaitMbox (megaCfg)) { printk("Blocked mailbox......!!\n"); udelay(1000); @@ -1108,14 +1224,10 @@ /* Abort command */ if (pScb == NULL) { - printk("NULL pScb in megaIssue\n"); TRACE(("NULL pScb in megaIssue\n")); + printk("NULL pScb in megaIssue\n"); } - SCpnt = pScb->SCpnt; - freeSCB(megaCfg, pScb); - - SCpnt->result = (DID_ABORT << 16); - callDone(SCpnt); + mega_cmd_done (megaCfg, pScb, 0x08); return -1; } @@ -1154,7 +1266,7 @@ if (pScb) { mega_cmd_done (megaCfg, pScb, mbox->status); - mega_rundoneq (); +// mega_rundoneq (megaCfg); } WRINDOOR (megaCfg, phys_mbox | 0x2); @@ -1168,26 +1280,26 @@ while (!((byte = READ_PORT (megaCfg->host->io_port, INTR_PORT)) & INTR_VALID)); WRITE_PORT (megaCfg->host->io_port, INTR_PORT, byte); - ENABLE_INTR (megaCfg->host->io_port); CLEAR_INTR (megaCfg->host->io_port); if (pScb) { mega_cmd_done (megaCfg, pScb, mbox->status); - mega_rundoneq (); +// mega_rundoneq (megaCfg); } else { TRACE (("Error: NULL pScb!\n")); } - } enable_irq(megaCfg->host->irq); retval=mbox->status; } +#if DEBUG while (mega_busyWaitMbox (megaCfg)) { printk("Blocked mailbox on exit......!\n"); udelay(1000); } +#endif return retval; } @@ -1195,7 +1307,7 @@ /*------------------------------------------------------------------- * Copies data to SGLIST *-------------------------------------------------------------------*/ -static int build_sglist (mega_host_config * megaCfg, mega_scb * scb, +static int mega_build_sglist (mega_host_config * megaCfg, mega_scb * scb, u32 * buffer, u32 * length) { struct scatterlist *sgList; @@ -1254,9 +1366,7 @@ paddr = (paddr + 4 + 16) & 0xfffffff0; /* Register mailbox area with the firmware */ - if (megaCfg->flag & BOARD_QUARTZ) { - } - else { + if (!(megaCfg->flag & BOARD_QUARTZ)) { WRITE_PORT (megaCfg->host->io_port, MBOX_PORT0, paddr & 0xFF); WRITE_PORT (megaCfg->host->io_port, MBOX_PORT1, (paddr >> 8) & 0xFF); WRITE_PORT (megaCfg->host->io_port, MBOX_PORT2, (paddr >> 16) & 0xFF); @@ -1314,7 +1424,6 @@ u32 paddr; u8 retval; - spin_lock_init (&mega_lock); /* Initialize adapter inquiry mailbox*/ paddr = virt_to_bus (megaCfg->mega_buffer); @@ -1375,9 +1484,17 @@ /*(megaCfg->flag & BOARD_40LD)?FC_MAX_TARGETS_PER_CHANNEL:MAX_TARGET+1;*/ megaCfg->host->max_lun = /* max lun */ (megaCfg->flag & BOARD_40LD) ? FC_MAX_LOGICAL_DRIVES : MAX_LOGICAL_DRIVES; + megaCfg->host->cmd_per_lun = MAX_CMD_PER_LUN; megaCfg->numldrv = enquiry3Pnt->numLDrv; megaCfg->max_cmds = megaCfg->productInfo.MaxConcCmds; + if(megaCfg->max_cmds > MAX_COMMANDS) megaCfg->max_cmds = MAX_COMMANDS - 1; + + megaCfg->host->can_queue = megaCfg->max_cmds; + + if (megaCfg->host->can_queue >= MAX_COMMANDS) { + megaCfg->host->can_queue = MAX_COMMANDS-1; + } #if 0 int i; @@ -1443,7 +1560,7 @@ return 0; } -int findCard (Scsi_Host_Template * pHostTmpl, +int mega_findCard (Scsi_Host_Template * pHostTmpl, u16 pciVendor, u16 pciDev, long flag) { @@ -1518,8 +1635,15 @@ host->host_no, (u_int) megaBase, megaIrq); /* Copy resource info into structure */ - megaCfg->qPending = NULL; - megaCfg->qFree = NULL; + megaCfg->qCompletedH = NULL; + megaCfg->qCompletedT = NULL; + megaCfg->qPendingH = NULL; + megaCfg->qPendingT = NULL; + megaCfg->qFreeH = NULL; + megaCfg->qFreeT = NULL; + megaCfg->qFcnt = 0; + megaCfg->qPcnt = 0; + megaCfg->qCcnt = 0; megaCfg->flag = flag; megaCfg->host = host; megaCfg->base = megaBase; @@ -1551,7 +1675,7 @@ mega_i_query_adapter (megaCfg); /* Initialize SCBs */ - if (initSCB (megaCfg)) { + if (mega_initSCB (megaCfg)) { scsi_unregister (host); continue; } @@ -1589,9 +1713,9 @@ printk ("megaraid: " MEGARAID_VERSION CRLFSTR); - count += findCard (pHostTmpl, 0x101E, 0x9010, 0); - count += findCard (pHostTmpl, 0x101E, 0x9060, 0); - count += findCard (pHostTmpl, 0x8086, 0x1960, BOARD_QUARTZ); + count += mega_findCard (pHostTmpl, 0x101E, 0x9010, 0); + count += mega_findCard (pHostTmpl, 0x101E, 0x9060, 0); + count += mega_findCard (pHostTmpl, 0x8086, 0x1960, BOARD_QUARTZ); return count; } @@ -1626,13 +1750,13 @@ release_region (megaCfg->host->io_port, 16); } - freeSgList(megaCfg); + mega_freeSgList(megaCfg); scsi_unregister (pSHost); return 0; } -static inline void freeSgList(mega_host_config *megaCfg) +static inline void mega_freeSgList(mega_host_config *megaCfg) { int i; @@ -1678,13 +1802,12 @@ *-----------------------------------------------------------------*/ int megaraid_queue (Scsi_Cmnd * SCpnt, void (*pktComp) (Scsi_Cmnd *)) { + DRIVER_LOCK_T mega_host_config *megaCfg; mega_scb *pScb; - long flags; - - spin_lock_irqsave(&mega_lock,flags); megaCfg = (mega_host_config *) SCpnt->host->hostdata; + DRIVER_LOCK(megaCfg); if (!(megaCfg->flag & (1L << SCpnt->channel))) { if (SCpnt->channel < SCpnt->host->max_channel) @@ -1701,33 +1824,69 @@ /* If driver in abort or reset.. cancel this command */ if (megaCfg->flag & IN_ABORT) { +#if DEBUG +printk("mq: got a request while in abort\n"); +#endif SCpnt->result = (DID_ABORT << 16); - ENQUEUE_NL(SCpnt, Scsi_Cmnd, qCompleted, host_scribble); + /* Add Scsi_Command to end of completed queue */ + if( megaCfg->qCompletedH == NULL ) { + megaCfg->qCompletedH = megaCfg->qCompletedT = SCpnt; + } + else { + megaCfg->qCompletedT->host_scribble = (unsigned char *) SCpnt; + megaCfg->qCompletedT = SCpnt; + } + megaCfg->qCompletedT->host_scribble = (unsigned char *) NULL; + megaCfg->qCcnt++; - spin_unlock_irqrestore(&mega_lock,flags); + DRIVER_UNLOCK(megaCfg); return 0; } else if (megaCfg->flag & IN_RESET) { +#if DEBUG +printk("mq: got a request while in reset\n"); +#endif SCpnt->result = (DID_RESET << 16); - ENQUEUE_NL(SCpnt, Scsi_Cmnd, qCompleted, host_scribble); + /* Add Scsi_Command to end of completed queue */ + if( megaCfg->qCompletedH == NULL ) { + megaCfg->qCompletedH = megaCfg->qCompletedT = SCpnt; + } + else { + megaCfg->qCompletedT->host_scribble = (unsigned char *) SCpnt; + megaCfg->qCompletedT = SCpnt; + } + megaCfg->qCompletedT->host_scribble = (unsigned char *) NULL; + megaCfg->qCcnt++; - spin_unlock_irqrestore(&mega_lock,flags); + DRIVER_UNLOCK(megaCfg); return 0; } + megaCfg->flag |= IN_QUEUE; /* Allocate and build a SCB request */ if ((pScb = mega_build_cmd (megaCfg, SCpnt)) != NULL) { /*build SCpnt for IOCTL_CMD_NEW cmd in mega_ioctl()*/ /* Add SCB to the head of the pending queue */ - ENQUEUE_NL (pScb, mega_scb, megaCfg->qPending, next); - - /* Issue any pending command to the card if not in ISR */ - if (!(megaCfg->flag & IN_ISR)) { - mega_runpendq(megaCfg); + /* Add SCB to the head of the pending queue */ + if( megaCfg->qPendingH == NULL ) { + megaCfg->qPendingH = megaCfg->qPendingT = pScb; } else { - printk("IRQ pend...\n"); + megaCfg->qPendingT->next = pScb; + megaCfg->qPendingT = pScb; } + megaCfg->qPendingT->next = NULL; + megaCfg->qPcnt++; + + + /* Issue any pending command to the card if not in ISR */ +// if (!(megaCfg->flag & IN_ISR)) { + mega_runpendq(megaCfg); +// } +/* + * try running the pend queue, irrespective of the driver's context. + * -cn + */ if ( SCpnt->cmnd[0]==IOCTL_CMD_NEW ) { /* user data from external user buffer */ @@ -1744,12 +1903,12 @@ kfree(pScb->kern_area); - freeSCB(megaCfg, pScb); + mega_freeSCB(megaCfg, pScb); } - } - spin_unlock_irqrestore(&mega_lock,flags); + megaCfg->flag &= ~IN_QUEUE; + DRIVER_UNLOCK(megaCfg); return 0; } @@ -1787,41 +1946,70 @@ /*--------------------------------------------------------------------- * Abort a previous SCSI request *---------------------------------------------------------------------*/ -int megaraid_abort (Scsi_Cmnd * SCpnt) +int +megaraid_abort (Scsi_Cmnd * SCpnt) { mega_host_config *megaCfg; - int rc, idx; - long flags; + int rc; //, idx; mega_scb *pScb; - rc = SCSI_ABORT_SUCCESS; - - spin_lock_irqsave (&mega_lock, flags); + rc = SCSI_ABORT_NOT_RUNNING; megaCfg = (mega_host_config *) SCpnt->host->hostdata; megaCfg->flag |= IN_ABORT; - for(pScb=megaCfg->qPending; pScb; pScb=pScb->next) { +#if DEBUG +printk("ma:fcnt=%d, pcnt=%d, qcnt=%d\n",megaCfg->qFcnt, megaCfg->qPcnt, megaCfg->qCcnt); +#endif + for(pScb=megaCfg->qPendingH; pScb; pScb=pScb->next) { if (pScb->SCpnt == SCpnt) { /* Found an aborting command */ #if DEBUG showMbox(pScb); #endif - printk("Abort: %d %u\n", - SCpnt->timeout_per_command, - (uint)((jiffies) - pScb->isrcount)); +/* + * If the command is queued to be issued to the firmware, abort the scsi cmd, + * If the command is already aborted in a previous call to the _abort entry + * point, return SCSI_ABORT_SNOOZE, suggesting a reset. + * If the command is issued to the firmware, which might complete after + * some time, we will mark the scb as aborted, and return to the mid layer, + * that abort could not be done. + * In the ISR, when this command actually completes, we will perform a normal + * completion. + * + * Oct 27, 1999 + */ switch(pScb->state) { case SCB_ABORTED: /* Already aborted */ rc = SCSI_ABORT_SNOOZE; break; case SCB_ISSUED: /* Waiting on ISR result */ - rc = SCSI_ABORT_PENDING; + rc = SCSI_ABORT_NOT_RUNNING; pScb->state = SCB_ABORTED; break; + case SCB_ACTIVE: /* still on the pending queue */ + mega_freeSCB (megaCfg, pScb); + SCpnt->result = (DID_ABORT << 16) ; + if( megaCfg->qCompletedH == NULL ) { + megaCfg->qCompletedH = megaCfg->qCompletedT = SCpnt; + } + else { + megaCfg->qCompletedT->host_scribble = (unsigned char *) SCpnt; + megaCfg->qCompletedT = SCpnt; + } + megaCfg->qCompletedT->host_scribble = (unsigned char *) NULL; + megaCfg->qCcnt++; + rc = SCSI_ABORT_SUCCESS; + break; + default: + printk("megaraid_abort: unknown command state!!\n"); + rc = SCSI_ABORT_NOT_RUNNING; + break; } + break; } } @@ -1839,26 +2027,29 @@ } } #endif - - /* - * Walk list of SCBs for any that are still outstanding - */ - for (idx = 0; idx < megaCfg->max_cmds; idx++) { - if (megaCfg->scbList[idx].state != SCB_FREE) { - if (megaCfg->scbList[idx].SCpnt == SCpnt) { - freeSCB (megaCfg, &megaCfg->scbList[idx]); - - SCpnt->result = (DID_ABORT << 16) | (SUGGEST_RETRY << 24); - ENQUEUE_NL(SCpnt, Scsi_Cmnd, qCompleted, host_scribble); - } - } - } - megaCfg->flag &= ~IN_ABORT; - spin_unlock_irqrestore (&mega_lock, flags); +#if DEBUG +if(megaCfg->flag & IN_QUEUE) printk("ma:flag is in queue\n"); +if(megaCfg->qCompletedH == NULL) printk("ma:qchead == null\n"); +#endif + +/* + * This is required here to complete any completed requests to be communicated + * over to the mid layer. + * Calling just mega_rundoneq() did not work. + */ +if(megaCfg->qCompletedH) { + SCpnt = megaCfg->qCompletedH; + megaCfg->qCompletedH = (Scsi_Cmnd *)SCpnt->host_scribble; + megaCfg->qCcnt--; + + SCpnt->host_scribble = (unsigned char *) NULL ; // XC : sep 14 + /* Callback */ + callDone (SCpnt); +} + mega_rundoneq(megaCfg); - mega_rundoneq(); return rc; } @@ -1870,14 +2061,18 @@ { mega_host_config *megaCfg; int idx; - long flags; - - spin_lock_irqsave (&mega_lock, flags); + int rc; + mega_scb *pScb; + rc = SCSI_RESET_NOT_RUNNING; megaCfg = (mega_host_config *) SCpnt->host->hostdata; megaCfg->flag |= IN_RESET; + printk ("megaraid_RESET: %.08lx cmd=%.02x , flag = %x\n", + SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel, SCpnt->target, + SCpnt->lun, rstflags); + TRACE (("RESET: %.08lx %.02x <%d.%d.%d>\n", SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel, SCpnt->target, SCpnt->lun)); @@ -1888,20 +2083,18 @@ for (idx = 0; idx < megaCfg->max_cmds; idx++) { if (megaCfg->scbList[idx].state != SCB_FREE) { SCpnt = megaCfg->scbList[idx].SCpnt; + pScb = &megaCfg->scbList[idx]; if (SCpnt != NULL) { - freeSCB (megaCfg, &megaCfg->scbList[idx]); - SCpnt->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24); - ENQUEUE_NL(SCpnt, Scsi_Cmnd, qCompleted, host_scribble); + pScb->state = SCB_RESET; + break; } } } megaCfg->flag &= ~IN_RESET; - spin_unlock_irqrestore (&mega_lock, flags); - - mega_rundoneq(); - return SCSI_RESET_PUNT; + mega_rundoneq(megaCfg); + return rc; } /*------------------------------------------------------------- @@ -1947,4 +2140,3 @@ #include "scsi_module.c" #endif - diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/megaraid.h linux/drivers/scsi/megaraid.h --- v2.2.13/linux/drivers/scsi/megaraid.h Tue Oct 19 17:10:38 1999 +++ linux/drivers/scsi/megaraid.h Tue Jan 4 10:12:20 2000 @@ -8,9 +8,11 @@ #define IN_ISR 0x80000000L #define IN_ABORT 0x40000000L #define IN_RESET 0x20000000L +#define IN_QUEUE 0x10000000L #define BOARD_QUARTZ 0x08000000L #define BOARD_40LD 0x04000000L +#ifndef HOSTS_C #define SCB_FREE 0x0 #define SCB_ACTIVE 0x1 #define SCB_WAITQ 0x2 @@ -18,12 +20,19 @@ #define SCB_COMPLETE 0x4 #define SCB_ABORTED 0x5 #define SCB_RESET 0x6 +#endif #define MEGA_CMD_TIMEOUT 10 -#define MAX_SGLIST 17 -#define MAX_COMMANDS 250 +/* Feel free to fiddle with these.. max values are: + SGLIST 0..26 + COMMANDS 0..253 + CMDPERLUN 0..63 +*/ +#define MAX_SGLIST 0x1A +#define MAX_COMMANDS 127 #define MAX_CMD_PER_LUN 63 +#define MAX_FIRMWARE_STATUS 46 #define MAX_LOGICAL_DRIVES 8 #define MAX_CHANNEL 5 @@ -505,7 +514,7 @@ u32 dataxferlen; } mega_passthru; -typedef struct _mega_mailbox { +struct _mega_mailbox { /* 0x0 */ u8 cmd; /* 0x1 */ u8 cmdid; /* 0x2 */ u16 numsectors; @@ -520,8 +529,9 @@ /* 0x12 */ u8 completed[46]; u8 mraid_poll; u8 mraid_ack; - u8 pad[16]; -} mega_mailbox; + u8 pad[16]; /* for alignment purposes */ +}__attribute__((packed)); +typedef struct _mega_mailbox mega_mailbox; typedef struct { u32 xferSegment; /* for 64-bit controllers */ @@ -575,8 +585,16 @@ u32 flag; u32 base; - mega_scb *qFree; - mega_scb *qPending; + mega_scb *qFreeH; + mega_scb *qFreeT; + mega_scb *qPendingH; + mega_scb *qPendingT; + + Scsi_Cmnd *qCompletedH; + Scsi_Cmnd *qCompletedT; + u32 qFcnt; + u32 qPcnt; + u32 qCcnt; u32 nReads[FC_MAX_LOGICAL_DRIVES]; u32 nWrites[FC_MAX_LOGICAL_DRIVES]; diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/mesh.c linux/drivers/scsi/mesh.c --- v2.2.13/linux/drivers/scsi/mesh.c Tue Jan 4 11:10:38 2000 +++ linux/drivers/scsi/mesh.c Tue Jan 4 10:12:20 2000 @@ -29,6 +29,10 @@ #include #include #include +#ifdef CONFIG_PMAC_PBOOK +#include +#include +#endif #include "scsi.h" #include "hosts.h" @@ -154,6 +158,8 @@ struct dbdma_cmd *dma_cmds; /* space for dbdma commands, aligned */ int clk_freq; struct mesh_target tgts[8]; + struct device_node *dnode; + unsigned char *mio_base; #ifndef MESH_NEW_STYLE_EH Scsi_Cmnd *completed_q; Scsi_Cmnd *completed_qtail; @@ -166,6 +172,14 @@ #endif }; +#ifdef CONFIG_PMAC_PBOOK +static int mesh_notify_sleep(struct pmu_sleep_notifier *self, int when); +static struct pmu_sleep_notifier mesh_sleep_notifier = { + mesh_notify_sleep, + SLEEP_LEVEL_BLOCK, +}; +#endif + #ifdef MESH_DBG static void dlog(struct mesh_state *ms, char *fmt, int a); @@ -213,6 +227,7 @@ static void halt_dma(struct mesh_state *); static int data_goes_out(Scsi_Cmnd *); static void do_abort(struct mesh_state *ms); +static void set_mesh_power(struct mesh_state *ms, int state); static struct notifier_block mesh_notifier = { mesh_notify_reboot, @@ -247,6 +262,7 @@ if (mesh == 0) mesh = find_compatible_devices("scsi", "chrp,mesh0"); for (; mesh != 0; mesh = mesh->next) { + struct device_node *mio; if (mesh->n_addrs != 2 || mesh->n_intrs != 2) { printk(KERN_ERR "mesh: expected 2 addrs and 2 intrs" " (got %d,%d)", mesh->n_addrs, mesh->n_intrs); @@ -267,6 +283,7 @@ panic("no mesh state"); memset(ms, 0, sizeof(*ms)); ms->host = mesh_host; + ms->dnode = mesh; ms->mesh = (volatile struct mesh_regs *) ioremap(mesh->addrs[0].address, 0x1000); ms->dma = (volatile struct dbdma_regs *) @@ -310,9 +327,16 @@ if (mesh_sync_period < minper) mesh_sync_period = minper; - feature_set(mesh, FEATURE_MESH_enable); - mdelay(200); + ms->mio_base = 0; + for (mio = ms->dnode->parent; mio; mio = mio->parent) + if (strcmp(mio->name, "mac-io") == 0 + && mio->n_addrs > 0) + break; + if (mio) + ms->mio_base = (unsigned char *) + ioremap(mio->addrs[0].address, 0x40); + set_mesh_power(ms, 1); mesh_init(ms); if (request_irq(ms->meshintr, do_mesh_interrupt, 0, "MESH", ms)) { @@ -322,11 +346,73 @@ ++nmeshes; } - if ((_machine == _MACH_Pmac) && (nmeshes > 0)) - register_reboot_notifier(&mesh_notifier); + if ((_machine == _MACH_Pmac) && (nmeshes > 0)) { +#ifdef CONFIG_PMAC_PBOOK + pmu_register_sleep_notifier(&mesh_sleep_notifier); +#endif /* CONFIG_PMAC_PBOOK */ + register_reboot_notifier(&mesh_notifier); + } return nmeshes; } + +static void +set_mesh_power(struct mesh_state *ms, int state) +{ + if (_machine != _MACH_Pmac || ms->mio_base == 0) + return; + + if (state) { + feature_set(ms->dnode, FEATURE_MESH_enable); + /* This seems to enable the termination power. strangely + this doesn't fully agree with OF, but with MacOS */ + if (ms->mio_base) + out_8(ms->mio_base + 0x36, 0x70); + mdelay(200); + } else { + feature_clear(ms->dnode, FEATURE_MESH_enable); + if (ms->mio_base) + out_8(ms->mio_base + 0x36, 0x34); + mdelay(10); + } +} + +#ifdef CONFIG_PMAC_PBOOK +/* + * notify clients before sleep and reset bus afterwards + */ +int +mesh_notify_sleep(struct pmu_sleep_notifier *self, int when) +{ + struct mesh_state *ms; + + switch (when) { + case PBOOK_SLEEP_REQUEST: + /* XXX We should wait for current transactions and queue + * new ones that would be posted beyond this point + */ + break; + case PBOOK_SLEEP_REJECT: + break; + + case PBOOK_SLEEP_NOW: + for (ms = all_meshes; ms != 0; ms = ms->next) { + disable_irq(ms->meshintr); + set_mesh_power(ms, 0); + } + break; + case PBOOK_WAKE: + for (ms = all_meshes; ms != 0; ms = ms->next) { + set_mesh_power(ms, 1); + mesh_init(ms); + enable_irq(ms->meshintr); + } + break; + } + return PBOOK_SLEEP_OK; +} +#endif /* CONFIG_PMAC_PBOOK */ + int mesh_queue(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/ncr53c8xx.c linux/drivers/scsi/ncr53c8xx.c --- v2.2.13/linux/drivers/scsi/ncr53c8xx.c Tue Jan 4 11:10:38 2000 +++ linux/drivers/scsi/ncr53c8xx.c Tue Jan 4 10:12:20 2000 @@ -1763,6 +1763,8 @@ */ u_short device_id; /* PCI device id */ u_char revision_id; /* PCI device revision id */ + u_char pci_bus; /* PCI bus number */ + u_char pci_devfn; /* PCI device and function */ u_long port; /* IO space base address */ u_int irq; /* IRQ level */ u_int features; /* Chip features map */ @@ -4393,6 +4395,8 @@ sprintf(np->inst_name, "ncr53c%s-%d", np->chip_name, np->unit); np->device_id = device->chip.device_id; np->revision_id = device->chip.revision_id; + np->pci_bus = device->slot.bus; + np->pci_devfn = device->slot.device_fn; np->features = device->chip.features; np->clock_divn = device->chip.nr_divisor; np->maxoffs = device->chip.offset_max; @@ -10088,7 +10092,15 @@ */ const char *ncr53c8xx_info (struct Scsi_Host *host) { +#ifdef __sparc__ + /* Ok to do this on all archs? */ + static char buffer[80]; + ncb_p np = ((struct host_data *) host->hostdata)->ncb; + sprintf (buffer, "%s\nPCI bus %02x device %02x", SCSI_NCR_DRIVER_NAME, np->pci_bus, np->pci_devfn); + return buffer; +#else return SCSI_NCR_DRIVER_NAME; +#endif } /* @@ -10673,6 +10685,8 @@ copy_info(&info, " IO port address 0x%lx, ", (u_long) np->port); #ifdef __sparc__ copy_info(&info, "IRQ number %s\n", __irq_itoa(np->irq)); + /* Ok to do this on all archs? */ + copy_info(&info, "PCI bus %02x device %02x\n", np->pci_bus, np->pci_devfn); #else copy_info(&info, "IRQ number %d\n", (int) np->irq); #endif diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/psi240i.h linux/drivers/scsi/psi240i.h --- v2.2.13/linux/drivers/scsi/psi240i.h Sun Dec 27 22:19:22 1998 +++ linux/drivers/scsi/psi240i.h Tue Jan 4 10:12:20 2000 @@ -211,6 +211,8 @@ } READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA; // SCSI inquiry data +#ifndef HOSTS_C + typedef struct _INQUIRYDATA { UCHAR DeviceType :5; @@ -235,6 +237,7 @@ UCHAR VendorSpecific[20]; UCHAR Reserved3[40]; } INQUIRYDATA, *PINQUIRYDATA; +#endif // IDE IDENTIFY data typedef struct _IDENTIFY_DATA diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/qlogicfc.c linux/drivers/scsi/qlogicfc.c --- v2.2.13/linux/drivers/scsi/qlogicfc.c Tue Jan 4 11:10:38 2000 +++ linux/drivers/scsi/qlogicfc.c Tue Jan 4 10:12:20 2000 @@ -758,7 +758,7 @@ hostdata->control_block.firm_opts = 0x0108; hostdata->control_block.max_frame_len = 2048; hostdata->control_block.max_iocb = 256; - hostdata->control_block.exec_throttle = 8; + hostdata->control_block.exec_throttle = QLOGICFC_CMD_PER_LUN; hostdata->control_block.retry_delay = 5; hostdata->control_block.retry_cnt = 1; hostdata->control_block.node_name[0] = 0x0020; @@ -1287,8 +1287,9 @@ /* scsi.c expects sense info in a different buffer */ cmd->dataseg[0].d_base = virt_to_bus_low32(Cmnd->sense_buffer); #if BITS_PER_LONG > 32 - cmd->dataseg[0].d_base_hi = virt_to_bus_high32(Cmnd->request_buffer); + cmd->dataseg[0].d_base_hi = virt_to_bus_high32(Cmnd->sense_buffer); #endif + cmd->dataseg[0].d_count = sizeof(Cmnd->sense_buffer); cmd->segment_cnt = 1; cmd->control_flags = CFLAG_READ; break; @@ -1855,7 +1856,7 @@ static int isp2x00_init(struct Scsi_Host *sh) { - u_int io_base; + u_long io_base; struct isp2x00_hostdata *hostdata; u_char revision; u_int irq; diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/qlogicisp.c linux/drivers/scsi/qlogicisp.c --- v2.2.13/linux/drivers/scsi/qlogicisp.c Tue Jan 4 11:10:38 2000 +++ linux/drivers/scsi/qlogicisp.c Tue Jan 4 10:12:20 2000 @@ -1262,6 +1262,13 @@ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, latency_timer); } #endif +#ifdef __alpha__ + /* Force ALPHA to use bus I/O and not bus MEM. + This is to avoid having to use HAE_MEM registers, + which is broken on some platforms and with SMP. + */ + command &= ~PCI_COMMAND_MEMORY; +#endif if ((command & PCI_COMMAND_MEMORY) && ((mem_base & 1) == 0)) { diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/qlogicpti.c linux/drivers/scsi/qlogicpti.c --- v2.2.13/linux/drivers/scsi/qlogicpti.c Tue Jan 4 11:10:38 2000 +++ linux/drivers/scsi/qlogicpti.c Tue Jan 4 10:12:20 2000 @@ -43,7 +43,7 @@ #include #define MAX_TARGETS 16 -#define MAX_LUNS 8 +#define MAX_LUNS 8 /* 32 for 1.31 F/W */ #define DEFAULT_LOOP_COUNT 10000 @@ -244,6 +244,46 @@ return 0; } +static inline void qlogicpti_set_hostdev_defaults(struct qlogicpti *qpti) +{ + int i; + + qpti->host_param.initiator_scsi_id = qpti->scsi_id; + qpti->host_param.bus_reset_delay = 3; + qpti->host_param.retry_count = 0; + qpti->host_param.retry_delay = 5; + qpti->host_param.async_data_setup_time = 3; + qpti->host_param.req_ack_active_negation = 1; + qpti->host_param.data_line_active_negation = 1; + qpti->host_param.data_dma_burst_enable = 1; + qpti->host_param.command_dma_burst_enable = 1; + qpti->host_param.tag_aging = 8; + qpti->host_param.selection_timeout = 250; + qpti->host_param.max_queue_depth = 256; + + for(i = 0; i < MAX_TARGETS; i++) { + /* + * disconnect, parity, arq, reneg on reset, and, oddly enough + * tags...the midlayer's notion of tagged support has to match + * our device settings, and since we base whether we enable a + * tag on a per-cmnd basis upon what the midlayer sez, we + * actually enable the capability here. + */ + qpti->dev_param[i].device_flags = 0xcd; + qpti->dev_param[i].execution_throttle = 16; + if (qpti->ultra) { + qpti->dev_param[i].synchronous_period = 12; + qpti->dev_param[i].synchronous_offset = 8; + } else { + qpti->dev_param[i].synchronous_period = 25; + qpti->dev_param[i].synchronous_offset = 12; + } + qpti->dev_param[i].device_enable = 1; + } + /* this is very important to set! */ + qpti->sbits = 1 << qpti->scsi_id; +} + static int qlogicpti_reset_hardware(struct Scsi_Host *host) { struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata; @@ -288,6 +328,10 @@ qregs->risc_mtreg = (RISC_MTREG_P0DFLT | RISC_MTREG_P1DFLT); } + /* reset adapter and per-device default values. */ + /* do it after finding out whether we're ultra mode capable */ + qlogicpti_set_hostdev_defaults(qpti); + /* Release the RISC processor. */ qregs->hcctrl = HCCTRL_REL; @@ -365,9 +409,11 @@ param[0] = MBOX_SET_TARGET_PARAMS; param[1] = (i << 8); param[2] = (qpti->dev_param[i].device_flags << 8); - if (qpti->is_pti == 0) /* really, is it 1.31 f/w or later? */ - param[2] |= 0xc0; - param[3] = 0; /* no sync mode at first */ + /* + * Since we're now loading 1.31 f/w, force narrow/async. + */ + param[2] |= 0xc0; + param[3] = 0; /* no offset, we do not have sync mode yet */ qlogicpti_mbox_command(qpti, param, 0); } @@ -392,9 +438,6 @@ unsigned short param[6]; unsigned short *risc_code, risc_code_addr, risc_code_length; unsigned long flags; -#if !defined(MODULE) && !defined(__sparc_v9__) - unsigned long dvma_addr; -#endif int i, timeout; risc_code = &sbus_risc_code01[0]; @@ -409,6 +452,7 @@ for(i = 0; i < risc_code_length; i++) csum += risc_code[i]; if(csum) { + restore_flags(flags); printk(KERN_EMERG "qlogicpti%d: Aieee, firmware checksum failed!", qpti->qpti_id); return 1; @@ -420,6 +464,7 @@ while(--timeout && (qregs->sbus_ctrl & SBUS_CTRL_RESET)) udelay(20); if(!timeout) { + restore_flags(flags); printk(KERN_EMERG "qlogicpti%d: Cannot reset the ISP.", qpti->qpti_id); return 1; } @@ -449,6 +494,9 @@ qpti->differential = 0; qregs->hcctrl = HCCTRL_REL; + /* This shouldn't be necessary- we've reset things so we should be + running from the ROM now.. */ + param[0] = MBOX_STOP_FIRMWARE; param[1] = param[2] = param[3] = param[4] = param[5] = 0; if(qlogicpti_mbox_command(qpti, param, 1)) { @@ -459,38 +507,18 @@ } /* Load the firmware. */ -#if !defined(MODULE) && !defined(__sparc_v9__) - if (sparc_cpu_model != sun4d) { - dvma_addr = (unsigned long) mmu_lockarea((char *)&risc_code[0], - (sizeof(u_short) * risc_code_length)); - param[0] = MBOX_LOAD_RAM; - param[1] = risc_code_addr; - param[2] = (dvma_addr >> 16); - param[3] = (dvma_addr & 0xffff); - param[4] = (sizeof(u_short) * risc_code_length); + for(i = 0; i < risc_code_length; i++) { + param[0] = MBOX_WRITE_RAM_WORD; + param[1] = risc_code_addr + i; + param[2] = risc_code[i]; if(qlogicpti_mbox_command(qpti, param, 1) || - (param[0] != MBOX_COMMAND_COMPLETE)) { - printk(KERN_EMERG "qlogicpti%d: Firmware dload failed, I'm bolixed!\n", + param[0] != MBOX_COMMAND_COMPLETE) { + printk("qlogicpti%d: Firmware dload failed, I'm bolixed!\n", qpti->qpti_id); restore_flags(flags); return 1; } - mmu_unlockarea((char *)dvma_addr, (sizeof(u_short) * risc_code_length)); - } else -#endif - /* We need to do it this slow way always on Ultra, SS[12]000. */ - for(i = 0; i < risc_code_length; i++) { - param[0] = MBOX_WRITE_RAM_WORD; - param[1] = risc_code_addr + i; - param[2] = risc_code[i]; - if(qlogicpti_mbox_command(qpti, param, 1) || - param[0] != MBOX_COMMAND_COMPLETE) { - printk("qlogicpti%d: Firmware dload failed, I'm bolixed!\n", - qpti->qpti_id); - restore_flags(flags); - return 1; - } - } + } /* Reset the ISP again. */ qregs->hcctrl = HCCTRL_RESET; @@ -530,6 +558,17 @@ qpti->fware_minrev = param[2]; qpti->fware_micrev = param[3]; + /* Set the clock rate */ + param[0] = MBOX_SET_CLOCK_RATE; + param[1] = qpti->clock; + if(qlogicpti_mbox_command(qpti, param, 1) || + (param[0] != MBOX_COMMAND_COMPLETE)) { + printk(KERN_EMERG "qlogicpti%d: could not set clock rate.\n", + qpti->qpti_id); + restore_flags(flags); + return 1; + } + if(qpti->is_pti != 0) { /* Load scsi initiator ID and interrupt level into sbus static ram. */ param[0] = MBOX_WRITE_RAM_WORD; @@ -578,39 +617,6 @@ return 0; } -static inline void qlogicpti_set_hostdev_defaults(struct qlogicpti *qpti) -{ - int i; - - qpti->host_param.initiator_scsi_id = qpti->scsi_id; - qpti->host_param.bus_reset_delay = 3; - qpti->host_param.retry_count = 0; - qpti->host_param.retry_delay = 5; - qpti->host_param.async_data_setup_time = 3; - qpti->host_param.req_ack_active_negation = 1; - qpti->host_param.data_line_active_negation = 1; - qpti->host_param.data_dma_burst_enable = 1; - qpti->host_param.command_dma_burst_enable = 1; - qpti->host_param.tag_aging = 8; - qpti->host_param.selection_timeout = 250; - qpti->host_param.max_queue_depth = 256; - - for(i = 0; i < MAX_TARGETS; i++) { - /* disconnect, parity, arq, reneg on reset */ - qpti->dev_param[i].device_flags = 0xc5; - qpti->dev_param[i].execution_throttle = 16; - if (qpti->ultra) { - qpti->dev_param[i].synchronous_period = 12; - qpti->dev_param[i].synchronous_offset = 8; - } else { - qpti->dev_param[i].synchronous_period = 16; - qpti->dev_param[i].synchronous_offset = 12; - } - qpti->dev_param[i].device_enable = 1; - } - qpti->sbits = 1 << qpti->host_param.initiator_scsi_id; -} - static void do_qlogicpti_intr_handler(int irq, void *dev_id, struct pt_regs *regs); #ifndef __sparc_v9__ static void do_qlogicpti_intr_handler_sun4m(int irq, void *dev_id, struct pt_regs *regs); @@ -629,6 +635,7 @@ int nqptis = 0, nqptis_in_use = 0; int qpti_node; int is_pti; + unsigned int cfreq; tpnt->proc_dir = &proc_scsi_qlogicpti; qptichain = 0; @@ -735,6 +742,7 @@ qpti->qdev->reg_addrs[0].reg_size; qpti_host->irq = qpti->irq = qpti->qdev->irqs[0]; + qpti->gotirq = 0; /* On Ultra and S{S1,C2}000 we must always call request_irq for each * qpti, so that imap registers get setup etc. @@ -757,6 +765,7 @@ /* XXX Unmap regs, unregister scsi host, free things. */ continue; } + qpti->gotirq = 1; qpti_irq_acquired: printk("qpti%d: IRQ %s ", qpti->qpti_id, __irq_itoa(qpti->qhost->irq)); @@ -790,6 +799,15 @@ bsizes = (DMA_BURST32 - 1); qpti->bursts = bsizes; + /* Check for what the clock input to this card is. + * Default to 40Mhz. + */ + cfreq = prom_getintdefault(qpti->prom_node,"clock-frequency",40000000); + qpti->clock = (cfreq + 500000)/1000000; + if (qpti->clock == 0) /* bullshit */ + qpti->clock = 40; + + /* Clear out Scsi_Cmnd array. */ memset(qpti->cmd_slots, 0, sizeof(qpti->cmd_slots)); @@ -809,9 +827,6 @@ #undef QSIZE - /* Set adapter and per-device default values. */ - qlogicpti_set_hostdev_defaults(qpti); - /* Load the firmware. */ if(qlogicpti_load_firmware(qpti)) panic("SBUS Qlogic/ISP firmware load failed"); @@ -856,17 +871,15 @@ { struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata; struct qlogicpti_regs *qregs = qpti->qregs; - /* Shut up the card. */ qregs->sbus_ctrl = 0; - /* Free IRQ handler and unmap Qlogic,ISP and PTI status regs. */ - free_irq(host->irq, qpti); + if (qpti->gotirq) + free_irq(host->irq, qpti); unmapioaddr((unsigned long)qregs); /* QLGC,isp doesn't have status reg */ if (strcmp (qpti->prom_name, "QLGC,isp")) unmapioaddr((unsigned long)qpti->sreg); - return 0; } @@ -1019,30 +1032,28 @@ Cmnd->host_scribble = NULL; if ((qpti->sbits & (1 << tgt)) == 0) { - if (Cmnd->cmnd[0] == 0x12 && host_byte(Cmnd->result) == DID_OK) { + int ok = host_byte(Cmnd->result) == DID_OK; + if (Cmnd->cmnd[0] == 0x12 && ok) { unsigned char *iqd; if (Cmnd->use_sg == 0) { iqd = ((unsigned char *)Cmnd->buffer); } else { iqd = ((struct scatterlist *) Cmnd->request_buffer)->address; } - /* Tags? */ - if (iqd[7] & 0x2) { - qpti->dev_param[tgt].device_flags |= 0x8; - } - /* Sync Mode? */ + /* tags handled in midlayer */ + /* enable sync mode? */ if (iqd[7] & 0x10) { qpti->dev_param[tgt].device_flags |= 0x10; } else { qpti->dev_param[tgt].synchronous_offset = 0; qpti->dev_param[tgt].synchronous_period = 0; } - /* Wide Capable? */ + /* are we wide capable? */ if (iqd[7] & 0x20) { qpti->dev_param[tgt].device_flags |= 0x20; } qpti->sbits |= (1 << tgt); - } else if (host_byte(Cmnd->result) != DID_OK) { + } else if (!ok) { qpti->sbits |= (1 << tgt); } } @@ -1051,28 +1062,64 @@ int qlogicpti_queuecommand_slow(Scsi_Cmnd *Cmnd, void (*done)(Scsi_Cmnd *)) { + unsigned long flags; struct qlogicpti *qpti = (struct qlogicpti *) Cmnd->host->hostdata; - /* check to see if we're done scanning */ + + /* + * done checking this host adapter? + * If not, then rewrite the command + * to finish through ourdone so we + * can peek at Inquiry data results. + */ + if (qpti->sbits && qpti->sbits != 0xffff) { + Cmnd->host_scribble = (char *) done; + return qlogicpti_queuecommand(Cmnd, ourdone); + } + save_flags(flags); cli(); + + /* + * We've peeked at all targets for this bus- time + * to set parameters for devices for real now. + */ if (qpti->sbits == 0xffff) { int i; - unsigned long flags; - save_flags(flags); cli(); for(i = 0; i < MAX_TARGETS; i++) { u_short param[6]; param[0] = MBOX_SET_TARGET_PARAMS; param[1] = (i << 8); param[2] = (qpti->dev_param[i].device_flags << 8); - param[3] = (qpti->dev_param[i].synchronous_offset << 8)| - qpti->dev_param[i].synchronous_period; - qlogicpti_mbox_command(qpti, param, 0); + if (qpti->dev_param[i].device_flags & 0x10) { + param[3] = (qpti->dev_param[i].synchronous_offset << 8) | + qpti->dev_param[i].synchronous_period; + } else { + param[3] = 0; + } + (void) qlogicpti_mbox_command(qpti, param, 0); + } + /* + * set to zero so any traverse through ourdone + * doesn't start the whole process again, + */ + qpti->sbits = 0; + } + + /* check to see if we're done with all adapters... */ + for (qpti = qptichain; qpti != NULL; qpti = qpti->next) { + if (qpti->sbits) { + break; } - restore_flags(flags); - Cmnd->host->hostt->queuecommand = qlogicpti_queuecommand; - return qlogicpti_queuecommand(Cmnd, done); - } else { - Cmnd->host_scribble = (char *) done; - return qlogicpti_queuecommand(Cmnd, ourdone); } + + /* + * if we hit the end of the chain w/o finding adapters still + * capability-configuring, then we're done with all adapters + * and can rock on.. + */ + if (qpti == NULL) + Cmnd->host->hostt->queuecommand = qlogicpti_queuecommand; + + restore_flags(flags); + return qlogicpti_queuecommand(Cmnd, done); } /* @@ -1140,7 +1187,7 @@ return 0; } -static int qlogicpti_return_status(struct Status_Entry *sts) +static int qlogicpti_return_status(struct Status_Entry *sts, int id) { int host_status = DID_ERROR; @@ -1195,8 +1242,8 @@ host_status = DID_OK; break; default: - printk(KERN_EMERG "qlogicpti : unknown completion status 0x%04x\n", - sts->completion_status); + printk(KERN_EMERG "qpti%d: unknown completion status 0x%04x\n", + id, sts->completion_status); host_status = DID_ERROR; break; } @@ -1259,7 +1306,8 @@ sizeof(Cmnd->sense_buffer)); if(sts->hdr.entry_type == ENTRY_STATUS) - Cmnd->result = qlogicpti_return_status(sts); + Cmnd->result = + qlogicpti_return_status(sts, qpti->qpti_id); else Cmnd->result = DID_ERROR << 16; diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/qlogicpti.h linux/drivers/scsi/qlogicpti.h --- v2.2.13/linux/drivers/scsi/qlogicpti.h Tue Jan 4 11:10:38 2000 +++ linux/drivers/scsi/qlogicpti.h Tue Jan 4 10:12:20 2000 @@ -483,7 +483,7 @@ int prom_node; char prom_name[64]; int irq; - char differential, ultra; + char differential, ultra, clock; unsigned char bursts; struct host_param host_param; struct dev_param dev_param[MAX_TARGETS]; @@ -496,8 +496,10 @@ #define SREG_IMASK 0x0c /* Interrupt level */ #define SREG_SPMASK 0x03 /* Mask for switch pack */ unsigned char swsreg; - unsigned char is_pti; /* Non-zero if this is a PTI board. */ - unsigned short sbits; + unsigned int + gotirq : 1, /* this instance got an irq */ + is_pti : 1, /* Non-zero if this is a PTI board. */ + sbits : 16; /* syncmode known bits */ }; /* How to twiddle them bits... */ diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/script_asm.pl linux/drivers/scsi/script_asm.pl --- v2.2.13/linux/drivers/scsi/script_asm.pl Mon Jun 16 16:35:56 1997 +++ linux/drivers/scsi/script_asm.pl Tue Jan 4 10:12:20 2000 @@ -896,7 +896,7 @@ open (OUTPUT, ">$output") || die "$0 : can't open $output for writing\n"; open (OUTPUTU, ">$outputu") || die "$0 : can't open $outputu for writing\n"; -print OUTPUT "u32 ".$prefix."SCRIPT[] = {\n"; +print OUTPUT "static u32 ".$prefix."SCRIPT[] = {\n"; $instructions = 0; for ($i = 0; $i < $#code; ) { if ($list_in_array) { @@ -935,7 +935,7 @@ } printf OUTPUTU "#undef A_$i\n"; - printf OUTPUT "u32 A_".$i."_used\[\] = {\n"; + printf OUTPUT "static u32 A_".$i."_used\[\] __attribute((unused)) = {\n"; printf STDERR "$i is used $symbol_references{$i}\n" if ($debug); foreach $j (split (/\s+/,$symbol_references{$i})) { $j =~ /(ABS|REL),(.*),(.*)/; @@ -957,15 +957,15 @@ # NCR assembler outputs label patches in the form of indices into # the code. # -printf OUTPUT "u32 ".$prefix."LABELPATCHES[] = {\n"; +printf OUTPUT "static u32 ".$prefix."LABELPATCHES[] __attribute((unused)) = {\n"; for $patch (sort {$a <=> $b} @label_patches) { printf OUTPUT "\t0x%08x,\n", $patch; } printf OUTPUT "};\n\n"; $num_external_patches = 0; -printf OUTPUT "struct {\n\tu32\toffset;\n\tvoid\t\t*address;\n". - "} ".$prefix."EXTERNAL_PATCHES[] = {\n"; +printf OUTPUT "static struct {\n\tu32\toffset;\n\tvoid\t\t*address;\n". + "} ".$prefix."EXTERNAL_PATCHES[] __attribute((unused)) = {\n"; while ($ident = pop(@external_patches)) { $off = pop(@external_patches); printf OUTPUT "\t{0x%08x, &%s},\n", $off, $ident; @@ -973,11 +973,11 @@ } printf OUTPUT "};\n\n"; -printf OUTPUT "u32 ".$prefix."INSTRUCTIONS\t= %d;\n", +printf OUTPUT "static u32 ".$prefix."INSTRUCTIONS __attribute((unused))\t= %d;\n", $instructions; -printf OUTPUT "u32 ".$prefix."PATCHES\t= %d;\n", +printf OUTPUT "static u32 ".$prefix."PATCHES __attribute((unused))\t= %d;\n", $#label_patches+1; -printf OUTPUT "u32 ".$prefix."EXTERNAL_PATCHES_LEN\t= %d;\n", +printf OUTPUT "static u32 ".$prefix."EXTERNAL_PATCHES_LEN __attribute((unused))\t= %d;\n", $num_external_patches; close OUTPUT; close OUTPUTU; diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c --- v2.2.13/linux/drivers/scsi/scsi.c Tue Jan 4 11:10:39 2000 +++ linux/drivers/scsi/scsi.c Tue Jan 4 10:12:21 2000 @@ -135,6 +135,12 @@ static unsigned char ** dma_malloc_pages = NULL; /* + * get rid of callers having to aquire the io_request_lock before + * calling scsi_malloc and scsi_free + */ +spinlock_t scsi_malloc_lock = SPIN_LOCK_UNLOCKED; + +/* * Note - the initial logging level can be set here to log events at boot time. * After the system is up, you may enable logging via the /proc interface. */ @@ -258,6 +264,7 @@ {"TEAC","CD-ROM","1.06", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1 * for seagate controller, which causes * SCSI code to reset bus.*/ + {"TEAC", "MT-2ST/45S2-27", "RV M", BLIST_NOLUN}, /* Responds to all lun */ {"TEXEL","CD-ROM","1.06", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1 * for seagate controller, which causes * SCSI code to reset bus.*/ @@ -268,7 +275,8 @@ {"HP", "C1750A", "3226", BLIST_NOLUN}, /* scanjet iic */ {"HP", "C1790A", "", BLIST_NOLUN}, /* scanjet iip */ {"HP", "C2500A", "", BLIST_NOLUN}, /* scanjet iicx */ -{"YAMAHA", "CDR102", "1.00", BLIST_NOLUN}, /* extra reset */ +{"YAMAHA","CDR100","1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ +{"YAMAHA","CDR102","1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ {"RELISYS", "Scorpio", "*", BLIST_NOLUN}, /* responds to all LUN */ /* @@ -292,12 +300,13 @@ {"nCipher","Fastness Crypto","*", BLIST_FORCELUN}, {"NEC","PD-1 ODX654P","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"MATSHITA","PD-1","*", BLIST_FORCELUN | BLIST_SINGLELUN}, -{"YAMAHA","CDR100","1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ -{"YAMAHA","CDR102","1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ {"iomega","jaz 1GB","J.86", BLIST_NOTQ | BLIST_NOLUN}, {"CREATIVE","DVD-RAM RAM","*", BLIST_GHOST}, {"MATSHITA","PD-2 LF-D100","*", BLIST_GHOST}, +{"HITACHI","GF-1050","*", BLIST_GHOST}, /* Hitachi SCSI DVD-RAM */ {"TOSHIBA","CDROM","*", BLIST_ISROM}, +{"TOSHIBA","DVD-RAM SD-W1101","*", BLIST_GHOST}, +{"TOSHIBA","DVD-RAM SD-W1111","*", BLIST_GHOST}, /* * Must be at end of list... */ @@ -1887,6 +1896,7 @@ void *scsi_malloc(unsigned int len) { unsigned int nbits, mask; + unsigned long flags; int i, j; if(len % SECTOR_SIZE != 0 || len > PAGE_SIZE) return NULL; @@ -1894,6 +1904,7 @@ nbits = len >> 9; mask = (1 << nbits) - 1; + spin_lock_irqsave(&scsi_malloc_lock, flags); for(i=0;i < dma_sectors / SECTORS_PER_PAGE; i++) for(j=0; j<=SECTORS_PER_PAGE - nbits; j++){ if ((dma_malloc_freelist[i] & (mask << j)) == 0){ @@ -1903,15 +1914,18 @@ SCSI_LOG_MLQUEUE(3,printk("SMalloc: %d %p [From:%p]\n",len, dma_malloc_pages[i] + (j << 9))); printk("SMalloc: %d %p [From:%p]\n",len, dma_malloc_pages[i] + (j << 9)); #endif + spin_unlock_irqrestore(&scsi_malloc_lock, flags); return (void *) ((unsigned long) dma_malloc_pages[i] + (j << 9)); } } + spin_unlock_irqrestore(&scsi_malloc_lock, flags); return NULL; /* Nope. No more */ } int scsi_free(void *obj, unsigned int len) { unsigned int page, sector, nbits, mask; + unsigned long flags; #ifdef DEBUG unsigned long ret = 0; @@ -1925,6 +1939,7 @@ SCSI_LOG_MLQUEUE(3,printk("SFree: %p %d\n",obj, len)); #endif + spin_lock_irqsave(&scsi_malloc_lock, flags); for (page = 0; page < dma_sectors / SECTORS_PER_PAGE; page++) { unsigned long page_addr = (unsigned long) dma_malloc_pages[page]; if ((unsigned long) obj >= page_addr && @@ -1948,9 +1963,11 @@ } scsi_dma_free_sectors += nbits; dma_malloc_freelist[page] &= ~(mask << sector); + spin_unlock_irqrestore(&scsi_malloc_lock, flags); return 0; } } + spin_unlock_irqrestore(&scsi_malloc_lock, flags); panic("scsi_free:Bad offset"); } diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h --- v2.2.13/linux/drivers/scsi/scsi.h Mon Aug 9 16:05:57 1999 +++ linux/drivers/scsi/scsi.h Tue Jan 4 10:12:21 2000 @@ -41,6 +41,7 @@ #define MAX_SCSI_DEVICE_CODE 14 extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE]; +extern spinlock_t scsi_malloc_lock; #ifdef DEBUG #define SCSI_TIMEOUT (5*HZ) @@ -654,8 +655,8 @@ req->nr_sectors -= bh->b_size >> 9; req->sector += bh->b_size >> 9; bh->b_reqnext = NULL; - bh->b_end_io(bh, uptodate); sectors -= bh->b_size >> 9; + bh->b_end_io(bh, uptodate); if ((bh = req->bh) != NULL) { req->current_nr_sectors = bh->b_size >> 9; if (req->nr_sectors < req->current_nr_sectors) { diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/scsi_ioctl.c linux/drivers/scsi/scsi_ioctl.c --- v2.2.13/linux/drivers/scsi/scsi_ioctl.c Thu Apr 29 11:53:41 1999 +++ linux/drivers/scsi/scsi_ioctl.c Tue Jan 4 10:12:21 2000 @@ -227,10 +227,8 @@ if(buf_needed){ buf_needed = (buf_needed + 511) & ~511; if (buf_needed > MAX_BUF) buf_needed = MAX_BUF; - spin_lock_irqsave(&io_request_lock, flags); - buf = (char *) scsi_malloc(buf_needed); - spin_unlock_irqrestore(&io_request_lock, flags); - if (!buf) return -ENOMEM; + if ((buf = (char *) scsi_malloc(buf_needed)) == NULL) + return -ENOMEM; memset(buf, 0, buf_needed); } else buf = NULL; diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c --- v2.2.13/linux/drivers/scsi/sd.c Tue Jan 4 11:10:39 2000 +++ linux/drivers/scsi/sd.c Tue Jan 4 10:12:21 2000 @@ -1239,13 +1239,16 @@ spintime = jiffies; } - time1 = jiffies + HZ; spin_unlock_irq(&io_request_lock); - while(jiffies < time1); /* Wait 1 second for next try */ + time1 = HZ; + do { + current->state = TASK_UNINTERRUPTIBLE; + time1 = schedule_timeout(time1); + } while (time1); /* Wait 1 second for next try */ printk( "." ); spin_lock_irq(&io_request_lock); } - } while(the_result && spintime && spintime+100*HZ > jiffies); + } while(the_result && spintime && time_before(jiffies, spintime+100*HZ)); if (spintime) { if (the_result) printk( "not responding...\n" ); @@ -1315,6 +1318,7 @@ printk("%s : block size assumed to be 512 bytes, disk size 1GB. \n", nbuff); + rscsi_disks[i].capacity = 0x1fffff; rscsi_disks[i].sector_size = 512; diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/seagate.c linux/drivers/scsi/seagate.c --- v2.2.13/linux/drivers/scsi/seagate.c Tue Dec 29 11:40:35 1998 +++ linux/drivers/scsi/seagate.c Tue Jan 4 10:12:21 2000 @@ -250,6 +250,10 @@ boards */ static int irq = IRQ; +MODULE_PARM(base_address, "i"); +MODULE_PARM(controller_type, "b"); +MODULE_PARM(irq, "i"); + #define retcode(result) (((result) << 16) | (message << 8) | status) #define STATUS ((u8) readb(st0x_cr_sr)) #define DATA ((u8) readb(st0x_dr)) @@ -1216,6 +1220,9 @@ /* SJT: Start. Slow Write. */ #ifdef SEAGATE_USE_ASM + +int __dummy_1,__dummy_2; + /* * We loop as long as we are in a data out phase, there is data to send, * and BSY is still active. @@ -1246,9 +1253,9 @@ "movb %%al, (%%edi)\n\t" "loop 1b\n\t" "2:\n" -/* output */ : "=S" (data), "=c" (len) -/* input */ : "0" (data), "1" (len), "b" (phys_to_virt(st0x_cr_sr)), "D" (phys_to_virt(st0x_dr)) -/* clobbered */ : "eax", "ebx", "edi"); +/* output */ : "=S" (data), "=c" (len) ,"=b" (__dummy_1) ,"=D" (__dummy_2) +/* input */ : "0" (data), "1" (len), "2" (phys_to_virt(st0x_cr_sr)), "3" (phys_to_virt(st0x_dr)) +/* clobbered */ : "eax"); #else /* SEAGATE_USE_ASM */ while (len) { @@ -1374,6 +1381,11 @@ /* SJT: Start. */ #ifdef SEAGATE_USE_ASM + +int __dummy_3,__dummy_4; + +/* Dummy clobbering variables for the new gcc-2.95 */ + /* * We loop as long as we are in a data in phase, there is room to read, * and BSY is still active @@ -1405,9 +1417,9 @@ "stosb\n\t" "loop 1b\n\t" "2:\n" -/* output */ : "=D" (data), "=c" (len) -/* input */ : "0" (data), "1" (len), "S" (phys_to_virt(st0x_cr_sr)), "b" (phys_to_virt(st0x_dr)) -/* clobbered */ : "eax","ebx", "esi"); +/* output */ : "=D" (data), "=c" (len) ,"=S" (__dummy_3) ,"=b" (__dummy_4) +/* input */ : "0" (data), "1" (len), "2" (phys_to_virt(st0x_cr_sr)), "3" (phys_to_virt(st0x_dr)) +/* clobbered */ : "eax" ); #else /* SEAGATE_USE_ASM */ while (len) { diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/sg.c linux/drivers/scsi/sg.c --- v2.2.13/linux/drivers/scsi/sg.c Mon Jun 7 16:27:06 1999 +++ linux/drivers/scsi/sg.c Tue Jan 4 10:12:21 2000 @@ -16,41 +16,28 @@ * * Borrows code from st driver. Thanks to Alessandro Rubini's "dd" book. */ - static char * sg_version_str = "Version: 2.1.34 (990603)"; - static int sg_version_num = 20134; /* 2 digits for each component */ + static char * sg_version_str = "Version: 2.1.36 (991218)"; + static int sg_version_num = 20136; /* 2 digits for each component */ /* * D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au), notes: * - scsi logging is available via SCSI_LOG_TIMEOUT macros. First * the kernel/module needs to be built with CONFIG_SCSI_LOGGING - * (otherwise the macros compile to empty statements), then do - * something like: 'echo "scsi log all" > /proc/scsi/scsi' to log - * everything or 'echo "scsi log {token} #N" > /proc/scsi/scsi' - * where {token} is one of [error,timeout,scan,mlqueue,mlcomplete, - * llqueue,llcomplete,hlqueue,hlcomplete,ioctl] and #N is 0...7 - * (with 0 meaning off). For example: 'scsi log timeout 7 > - * /proc/scsi/scsi' to get all logging messages from this driver. - * Should use hlcomplete but it is too "noisy" (sd uses it). - * - * - This driver obtains memory (heap) for the low-level driver to - * transfer/dma to and from. It is obtained from up to 3 sources: - * - obtain heap via get_free_pages() - * - obtain heap from the shared scsi dma pool - * - obtain heap from kernel directly (kmalloc) [last choice] - * Each open() attempts to obtain a "reserve" buffer of - * SG_DEF_RESERVED_SIZE bytes (or 0 bytes if opened O_RDONLY). The - * amount actually obtained [which could be 0 bytes] can be found from - * the SG_GET_RESERVED_SIZE ioctl(). This reserved buffer size can - * be changed by calling the SG_SET_RESERVED_SIZE ioctl(). Since this - * is an ambit claim, it should be followed by a SG_GET_RESERVED_SIZE - * ioctl() to find out how much was actually obtained. - * A subsequent write() to this file descriptor will use the - * reserved buffer unless: - * - it is already in use (eg during command queuing) - * - or the write() needs a buffer size larger than the - * reserved size - * In these cases the write() will attempt to get the required memory - * for the duration of this request but, if memory is low, it may - * fail with ENOMEM. + * (otherwise the macros compile to empty statements). + * Then before running the program to be debugged enter: + * # echo "scsi log timeout 7" > /proc/scsi/scsi + * This will send copious output to the console and the log which + * is usually /var/log/messages. To turn off debugging enter: + * # echo "scsi log timeout 0" > /proc/scsi/scsi + * The 'timeout' token was chosen because it is relatively unused. + * The token 'hlcomplete' should be used but that triggers too + * much output from the sd device driver. To dump the current + * state of the SCSI mid level data structures enter: + * # echo "scsi dump 1" > /proc/scsi/scsi + * To dump the state of sg's data structures get the 'sg_debug' + * program from the utilities and enter: + * # sg_debug /dev/sga + * or any valid sg device name. The state of _all_ sg devices + * will be sent to the console and the log. * * - The 'alt_address' field in the scatter_list structure and the * related 'mem_src' indicate the source of the heap allocation. @@ -158,7 +145,7 @@ char closed; /* 1 -> fd closed but request(s) outstanding */ char my_mem_src; /* heap whereabouts of this Sg_fd object */ char cmd_q; /* 1 -> allow command queuing, 0 -> don't */ - char underrun_flag; /* 1 -> flag underruns, 0 -> don't, 2 -> test */ + char underrun_flag; /* 1 -> flag underruns, 0 -> don't flag underruns */ char next_cmd_len; /* 0 -> automatic (def), >0 -> use on next write() */ } Sg_fd; /* 1208 bytes long on i386 */ @@ -212,7 +199,6 @@ static const int size_sg_header = sizeof(struct sg_header); - static int sg_open(struct inode * inode, struct file * filp) { int dev = MINOR(inode->i_rdev); @@ -316,7 +302,6 @@ Sg_fd * sfp; Sg_request * srp; int req_pack_id = -1; - struct sg_header * shp = (struct sg_header *)buf; if ((! (sfp = (Sg_fd *)filp->private_data)) || (! (sdp = sfp->parentdp))) return -ENXIO; @@ -329,8 +314,11 @@ ; /* FIXME: Hmm. Seek to the right place, or fail? */ if ((k = verify_area(VERIFY_WRITE, buf, count))) return k; - if (sfp->force_packid && (count >= size_sg_header)) - req_pack_id = shp->pack_id; + if (sfp->force_packid && (count >= size_sg_header)) { + struct sg_header hdr; + copy_from_user(&hdr, buf, size_sg_header); + req_pack_id = hdr.pack_id; + } srp = sg_get_request(sfp, req_pack_id); if (! srp) { /* now wait on packet to arrive */ if (filp->f_flags & O_NONBLOCK) @@ -342,8 +330,7 @@ if (res) return res; /* -ERESTARTSYS because signal hit process */ } - if (2 != sfp->underrun_flag) - srp->header.pack_len = srp->header.reply_len; /* Why ????? */ + srp->header.pack_len = srp->header.reply_len; /* Why ????? */ /* Now copy the result back to the user buffer. */ if (count >= size_sg_header) { @@ -391,9 +378,8 @@ if (count < (size_sg_header + 6)) return -EIO; /* The minimum scsi command length is 6 bytes. */ - srp = sg_add_request(sfp); - if (! srp) { - SCSI_LOG_TIMEOUT(1, printk("sg_write: queue full, domain error\n")); + if (! (srp = sg_add_request(sfp))) { + SCSI_LOG_TIMEOUT(1, printk("sg_write: queue full\n")); return -EDOM; } __copy_from_user(&srp->header, buf, size_sg_header); @@ -404,7 +390,8 @@ if (sfp->next_cmd_len > MAX_COMMAND_SIZE) { SCSI_LOG_TIMEOUT(1, printk("sg_write: command length too long\n")); sfp->next_cmd_len = 0; - return -EDOM; + sg_remove_request(sfp, srp); + return -EIO; } cmd_size = sfp->next_cmd_len; sfp->next_cmd_len = 0; /* reset so only this write() effected */ @@ -422,9 +409,9 @@ srp->header.reply_len; mxsize -= size_sg_header; input_size -= size_sg_header; - if (input_size < 0) { + if ((input_size < 0) || (srp->header.reply_len < 0)) { sg_remove_request(sfp, srp); - return -EIO; /* User did not pass enough bytes for this command. */ + return -EIO; /* Count too small or reply_len negative. */ } if ((k = sg_start_req(srp, mxsize, buf + cmd_size, input_size))) { SCSI_LOG_TIMEOUT(1, printk("sg_write: build err=%d\n", k)); @@ -432,8 +419,10 @@ return k; /* probably out of space --> ENOMEM */ } /* SCSI_LOG_TIMEOUT(7, printk("sg_write: allocating device\n")); */ - if (! (SCpnt = scsi_allocate_device(NULL, sdp->device, - !(filp->f_flags & O_NONBLOCK)))) { + spin_lock_irqsave(&io_request_lock, flags); + SCpnt = scsi_allocate_device(NULL, sdp->device, ! (filp->f_flags & O_NONBLOCK)); + spin_unlock_irqrestore(&io_request_lock, flags); + if (! SCpnt) { sg_finish_rem_req(srp, NULL, 0); return -EAGAIN; /* No available command blocks at the moment */ } @@ -490,7 +479,12 @@ switch(cmd_in) { case SG_SET_TIMEOUT: - return get_user(sfp->timeout, (int *)arg); + result = get_user(val, (int *)arg); + if (result) return result; + if (val < 0) + return -EIO; + sfp->timeout = val; + return 0; case SG_GET_TIMEOUT: /* N.B. User receives timeout as return value */ return sfp->timeout; /* strange ..., for backward compatibility */ case SG_SET_FORCE_LOW_DMA: @@ -514,14 +508,19 @@ if (result) return result; else { Sg_scsi_id * sg_idp = (Sg_scsi_id *)arg; - __put_user((int)sdp->device->host->host_no, &sg_idp->host_no); + struct Scsi_Host * hostp = sdp->device->host; + + __put_user((int)hostp->host_no, &sg_idp->host_no); __put_user((int)sdp->device->channel, &sg_idp->channel); __put_user((int)sdp->device->id, &sg_idp->scsi_id); __put_user((int)sdp->device->lun, &sg_idp->lun); __put_user((int)sdp->device->type, &sg_idp->scsi_type); + __put_user(hostp->cmd_per_lun ? hostp->cmd_per_lun : + hostp->hostt->cmd_per_lun, &sg_idp->h_cmd_per_lun); + __put_user((short)sdp->device->queue_depth, + &sg_idp->d_queue_depth); __put_user(0, &sg_idp->unused1); __put_user(0, &sg_idp->unused2); - __put_user(0, &sg_idp->unused3); return 0; } case SG_SET_FORCE_PACK_ID: @@ -605,6 +604,13 @@ return put_user(sg_version_num, (int *)arg); case SG_EMULATED_HOST: return put_user(sdp->device->host->hostt->emulated, (int *)arg); + case SG_SCSI_RESET: + if (! scsi_block_when_processing_errors(sdp->device)) + return -EBUSY; + result = get_user(val, (int *)arg); + if (result) return result; + /* Don't do anything till scsi mod level visibility */ + return 0; case SCSI_IOCTL_SEND_COMMAND: /* Allow SCSI_IOCTL_SEND_COMMAND without checking suser() since the user already has read/write access to the generic device and so @@ -685,10 +691,13 @@ Sg_device * sdp; Sg_fd * sfp; Sg_request * srp = NULL; - int closed = 0; + static const int min_sb_len = + SG_MAX_SENSE > sizeof(SCpnt->sense_buffer) ? + sizeof(SCpnt->sense_buffer) : SG_MAX_SENSE; if ((NULL == sg_dev_arr) || (dev < 0) || (dev >= sg_template.dev_max)) { SCSI_LOG_TIMEOUT(1, printk("sg__done: bad args dev=%d\n", dev)); + wake_up(&SCpnt->device->device_wait); scsi_release_command(SCpnt); SCpnt = NULL; return; @@ -711,6 +720,7 @@ } if (! srp) { SCSI_LOG_TIMEOUT(1, printk("sg__done: req missing, dev=%d\n", dev)); + wake_up(&SCpnt->device->device_wait); scsi_release_command(SCpnt); SCpnt = NULL; return; @@ -720,15 +730,12 @@ srp->data.sglist_len = SCpnt->sglist_len; srp->data.bufflen = SCpnt->bufflen; srp->data.buffer = SCpnt->buffer; - if (2 == sfp->underrun_flag) - srp->header.pack_len = SCpnt->underflow; sg_clr_scpnt(SCpnt); srp->my_cmdp = NULL; SCSI_LOG_TIMEOUT(4, printk("sg__done: dev=%d, scsi_stat=%d, res=0x%x\n", dev, (int)status_byte(SCpnt->result), (int)SCpnt->result)); - memcpy(srp->header.sense_buffer, SCpnt->sense_buffer, - sizeof(SCpnt->sense_buffer)); + memcpy(srp->header.sense_buffer, SCpnt->sense_buffer, min_sb_len); switch (host_byte(SCpnt->result)) { /* This setup of 'result' is for backward compatibility and is best ignored by the user who should use target, host + driver status */ @@ -781,24 +788,28 @@ srp->header.host_status = host_byte(SCpnt->result); srp->header.driver_status = driver_byte(SCpnt->result); + wake_up(&SCpnt->device->device_wait); scsi_release_command(SCpnt); SCpnt = NULL; if (sfp->closed) { /* whoops this fd already released, cleanup */ - closed = 1; SCSI_LOG_TIMEOUT(1, printk("sg__done: already closed, freeing ...\n")); /* should check if module is unloaded <<<<<<< */ sg_finish_rem_req(srp, NULL, 0); + srp = NULL; if (NULL == sfp->headrp) { SCSI_LOG_TIMEOUT(1, printk("sg__done: already closed, final cleanup\n")); sg_remove_sfp(sdp, sfp); + sfp = NULL; } } /* Now wake up any sg_read() that is waiting for this packet. */ - wake_up_interruptible(&sfp->read_wait); - if ((sfp->async_qp) && (! closed)) - kill_fasync(sfp->async_qp, SIGPOLL); + if (sfp && srp) { + wake_up_interruptible(&sfp->read_wait); + if (sfp->async_qp) + kill_fasync(sfp->async_qp, SIGPOLL); + } } static void sg_debug_all(const Sg_fd * sfp) diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/sim710_d.h linux/drivers/scsi/sim710_d.h --- v2.2.13/linux/drivers/scsi/sim710_d.h Tue Jan 4 11:10:39 2000 +++ linux/drivers/scsi/sim710_d.h Tue Jan 4 10:12:21 2000 @@ -1,4 +1,4 @@ -u32 SCRIPT[] = { +static u32 SCRIPT[] = { /* @@ -1776,12 +1776,12 @@ }; #define A_dsa_cmnd 0x00000010 -u32 A_dsa_cmnd_used[] = { +static u32 A_dsa_cmnd_used[] __attribute((unused)) = { 0x0000001d, }; #define A_dsa_datain 0x00000028 -u32 A_dsa_datain_used[] = { +static u32 A_dsa_datain_used[] __attribute((unused)) = { 0x0000003d, 0x0000003f, 0x00000041, @@ -1913,7 +1913,7 @@ }; #define A_dsa_dataout 0x00000428 -u32 A_dsa_dataout_used[] = { +static u32 A_dsa_dataout_used[] __attribute((unused)) = { 0x00000143, 0x00000145, 0x00000147, @@ -2045,12 +2045,12 @@ }; #define A_dsa_msgin 0x00000020 -u32 A_dsa_msgin_used[] = { +static u32 A_dsa_msgin_used[] __attribute((unused)) = { 0x0000002f, }; #define A_dsa_msgout 0x00000008 -u32 A_dsa_msgout_used[] = { +static u32 A_dsa_msgout_used[] __attribute((unused)) = { 0x00000013, 0x00000285, 0x000002c5, @@ -2058,50 +2058,50 @@ }; #define A_dsa_select 0x00000000 -u32 A_dsa_select_used[] = { +static u32 A_dsa_select_used[] __attribute((unused)) = { 0x00000006, }; #define A_dsa_size 0x00000828 -u32 A_dsa_size_used[] = { +static u32 A_dsa_size_used[] __attribute((unused)) = { }; #define A_dsa_status 0x00000018 -u32 A_dsa_status_used[] = { +static u32 A_dsa_status_used[] __attribute((unused)) = { 0x0000002b, }; #define A_had_cmdout 0x00000004 -u32 A_had_cmdout_used[] = { +static u32 A_had_cmdout_used[] __attribute((unused)) = { 0x0000001a, }; #define A_had_datain 0x00000008 -u32 A_had_datain_used[] = { +static u32 A_had_datain_used[] __attribute((unused)) = { 0x00000038, }; #define A_had_dataout 0x00000010 -u32 A_had_dataout_used[] = { +static u32 A_had_dataout_used[] __attribute((unused)) = { 0x0000013e, }; #define A_had_extmsg 0x00000080 -u32 A_had_extmsg_used[] = { +static u32 A_had_extmsg_used[] __attribute((unused)) = { 0x0000025a, 0x0000029a, 0x000002da, }; #define A_had_msgin 0x00000040 -u32 A_had_msgin_used[] = { +static u32 A_had_msgin_used[] __attribute((unused)) = { 0x00000248, 0x00000288, 0x000002c8, }; #define A_had_msgout 0x00000002 -u32 A_had_msgout_used[] = { +static u32 A_had_msgout_used[] __attribute((unused)) = { 0x00000010, 0x00000282, 0x000002c2, @@ -2109,161 +2109,161 @@ }; #define A_had_select 0x00000001 -u32 A_had_select_used[] = { +static u32 A_had_select_used[] __attribute((unused)) = { 0x0000000c, }; #define A_had_status 0x00000020 -u32 A_had_status_used[] = { +static u32 A_had_status_used[] __attribute((unused)) = { }; #define A_int_bad_extmsg1a 0xab930000 -u32 A_int_bad_extmsg1a_used[] = { +static u32 A_int_bad_extmsg1a_used[] __attribute((unused)) = { 0x00000263, }; #define A_int_bad_extmsg1b 0xab930001 -u32 A_int_bad_extmsg1b_used[] = { +static u32 A_int_bad_extmsg1b_used[] __attribute((unused)) = { 0x0000026b, }; #define A_int_bad_extmsg2a 0xab930002 -u32 A_int_bad_extmsg2a_used[] = { +static u32 A_int_bad_extmsg2a_used[] __attribute((unused)) = { 0x000002a3, }; #define A_int_bad_extmsg2b 0xab930003 -u32 A_int_bad_extmsg2b_used[] = { +static u32 A_int_bad_extmsg2b_used[] __attribute((unused)) = { 0x000002ab, }; #define A_int_bad_extmsg3a 0xab930004 -u32 A_int_bad_extmsg3a_used[] = { +static u32 A_int_bad_extmsg3a_used[] __attribute((unused)) = { 0x000002e3, }; #define A_int_bad_extmsg3b 0xab930005 -u32 A_int_bad_extmsg3b_used[] = { +static u32 A_int_bad_extmsg3b_used[] __attribute((unused)) = { 0x000002eb, }; #define A_int_bad_msg1 0xab930006 -u32 A_int_bad_msg1_used[] = { +static u32 A_int_bad_msg1_used[] __attribute((unused)) = { 0x00000255, }; #define A_int_bad_msg2 0xab930007 -u32 A_int_bad_msg2_used[] = { +static u32 A_int_bad_msg2_used[] __attribute((unused)) = { 0x00000295, }; #define A_int_bad_msg3 0xab930008 -u32 A_int_bad_msg3_used[] = { +static u32 A_int_bad_msg3_used[] __attribute((unused)) = { 0x000002d5, }; #define A_int_cmd_bad_phase 0xab930009 -u32 A_int_cmd_bad_phase_used[] = { +static u32 A_int_cmd_bad_phase_used[] __attribute((unused)) = { 0x00000027, }; #define A_int_cmd_complete 0xab93000a -u32 A_int_cmd_complete_used[] = { +static u32 A_int_cmd_complete_used[] __attribute((unused)) = { 0x00000037, }; #define A_int_data_bad_phase 0xab93000b -u32 A_int_data_bad_phase_used[] = { +static u32 A_int_data_bad_phase_used[] __attribute((unused)) = { 0x00000247, }; #define A_int_disc1 0xab930019 -u32 A_int_disc1_used[] = { +static u32 A_int_disc1_used[] __attribute((unused)) = { 0x00000277, }; #define A_int_disc2 0xab93001a -u32 A_int_disc2_used[] = { +static u32 A_int_disc2_used[] __attribute((unused)) = { 0x000002b7, }; #define A_int_disc3 0xab93001b -u32 A_int_disc3_used[] = { +static u32 A_int_disc3_used[] __attribute((unused)) = { 0x000002f7, }; #define A_int_msg_sdtr1 0xab93000c -u32 A_int_msg_sdtr1_used[] = { +static u32 A_int_msg_sdtr1_used[] __attribute((unused)) = { 0x00000271, }; #define A_int_msg_sdtr2 0xab93000d -u32 A_int_msg_sdtr2_used[] = { +static u32 A_int_msg_sdtr2_used[] __attribute((unused)) = { 0x000002b1, }; #define A_int_msg_sdtr3 0xab93000e -u32 A_int_msg_sdtr3_used[] = { +static u32 A_int_msg_sdtr3_used[] __attribute((unused)) = { 0x000002f1, }; #define A_int_no_msgout1 0xab93000f -u32 A_int_no_msgout1_used[] = { +static u32 A_int_no_msgout1_used[] __attribute((unused)) = { 0x00000281, }; #define A_int_no_msgout2 0xab930010 -u32 A_int_no_msgout2_used[] = { +static u32 A_int_no_msgout2_used[] __attribute((unused)) = { 0x000002c1, }; #define A_int_no_msgout3 0xab930011 -u32 A_int_no_msgout3_used[] = { +static u32 A_int_no_msgout3_used[] __attribute((unused)) = { 0x00000301, }; #define A_int_not_cmd_complete 0xab930012 -u32 A_int_not_cmd_complete_used[] = { +static u32 A_int_not_cmd_complete_used[] __attribute((unused)) = { 0x00000031, }; #define A_int_not_rej 0xab93001c -u32 A_int_not_rej_used[] = { +static u32 A_int_not_rej_used[] __attribute((unused)) = { 0x0000030d, }; #define A_int_resel_not_msgin 0xab930016 -u32 A_int_resel_not_msgin_used[] = { +static u32 A_int_resel_not_msgin_used[] __attribute((unused)) = { 0x00000317, }; #define A_int_reselected 0xab930017 -u32 A_int_reselected_used[] = { +static u32 A_int_reselected_used[] __attribute((unused)) = { 0x0000031b, }; #define A_int_sel_no_ident 0xab930013 -u32 A_int_sel_no_ident_used[] = { +static u32 A_int_sel_no_ident_used[] __attribute((unused)) = { 0x0000000f, }; #define A_int_sel_not_cmd 0xab930014 -u32 A_int_sel_not_cmd_used[] = { +static u32 A_int_sel_not_cmd_used[] __attribute((unused)) = { 0x00000019, }; #define A_int_selected 0xab930018 -u32 A_int_selected_used[] = { +static u32 A_int_selected_used[] __attribute((unused)) = { 0x0000032d, }; #define A_int_status_not_msgin 0xab930015 -u32 A_int_status_not_msgin_used[] = { +static u32 A_int_status_not_msgin_used[] __attribute((unused)) = { 0x0000002d, }; #define A_msgin_buf 0x00000000 -u32 A_msgin_buf_used[] = { +static u32 A_msgin_buf_used[] __attribute((unused)) = { 0x0000024b, 0x0000025f, 0x00000267, @@ -2280,7 +2280,7 @@ }; #define A_reselected_identify 0x00000000 -u32 A_reselected_identify_used[] = { +static u32 A_reselected_identify_used[] __attribute((unused)) = { 0x00000319, }; @@ -2304,7 +2304,7 @@ #define Ent_wait_disc2 0x00000ad0 #define Ent_wait_disc3 0x00000bd0 #define Ent_wait_disc_complete 0x000000d0 -u32 LABELPATCHES[] = { +static u32 LABELPATCHES[] __attribute((unused)) = { 0x00000007, 0x00000009, 0x00000015, @@ -2349,12 +2349,12 @@ 0x0000032b, }; -struct { +static struct { u32 offset; void *address; -} EXTERNAL_PATCHES[] = { +} EXTERNAL_PATCHES[] __attribute((unused)) = { }; -u32 INSTRUCTIONS = 407; -u32 PATCHES = 42; -u32 EXTERNAL_PATCHES_LEN = 0; +static u32 INSTRUCTIONS __attribute((unused)) = 407; +static u32 PATCHES __attribute((unused)) = 42; +static u32 EXTERNAL_PATCHES_LEN __attribute((unused)) = 0; diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/sr_ioctl.c linux/drivers/scsi/sr_ioctl.c --- v2.2.13/linux/drivers/scsi/sr_ioctl.c Fri May 14 16:04:16 1999 +++ linux/drivers/scsi/sr_ioctl.c Tue Jan 4 10:12:21 2000 @@ -241,7 +241,6 @@ u_char sr_cmd[10]; char * buffer; int result; - unsigned long flags; sr_cmd[0] = SCMD_READ_SUBCHANNEL; sr_cmd[1] = ((scsi_CDs[MINOR(cdi->dev)].device->lun) << 5); @@ -253,20 +252,15 @@ sr_cmd[8] = 24; sr_cmd[9] = 0; - spin_lock_irqsave(&io_request_lock, flags); - buffer = (unsigned char*) scsi_malloc(512); - spin_unlock_irqrestore(&io_request_lock, flags); - if(!buffer) return -ENOMEM; + if ((buffer = (unsigned char*) scsi_malloc(512)) == NULL) + return -ENOMEM; result = sr_do_ioctl(MINOR(cdi->dev), sr_cmd, buffer, 24, 0); memcpy (mcn->medium_catalog_number, buffer + 9, 13); mcn->medium_catalog_number[13] = 0; - spin_lock_irqsave(&io_request_lock, flags); scsi_free(buffer, 512); - spin_unlock_irqrestore(&io_request_lock, flags); - return result; } @@ -395,7 +389,6 @@ { struct cdrom_tochdr* tochdr = (struct cdrom_tochdr*)arg; char * buffer; - unsigned long flags; sr_cmd[0] = SCMD_READ_TOC; sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5); @@ -405,19 +398,15 @@ sr_cmd[8] = 12; /* LSB of length */ sr_cmd[9] = 0; - spin_lock_irqsave(&io_request_lock, flags); - buffer = (unsigned char *) scsi_malloc(512); - spin_unlock_irqrestore(&io_request_lock, flags); - if(!buffer) return -ENOMEM; + if ((buffer = (unsigned char *) scsi_malloc(512)) == NULL) + return -ENOMEM; result = sr_do_ioctl(target, sr_cmd, buffer, 12, 1); tochdr->cdth_trk0 = buffer[2]; tochdr->cdth_trk1 = buffer[3]; - spin_lock_irqsave(&io_request_lock, flags); scsi_free(buffer, 512); - spin_unlock_irqrestore(&io_request_lock, flags); break; } @@ -425,7 +414,6 @@ { struct cdrom_tocentry* tocentry = (struct cdrom_tocentry*)arg; unsigned char * buffer; - unsigned long flags; sr_cmd[0] = SCMD_READ_TOC; sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | @@ -436,10 +424,8 @@ sr_cmd[8] = 12; /* LSB of length */ sr_cmd[9] = 0; - spin_lock_irqsave(&io_request_lock, flags); - buffer = (unsigned char *) scsi_malloc(512); - spin_unlock_irqrestore(&io_request_lock, flags); - if(!buffer) return -ENOMEM; + if ((buffer = (unsigned char *) scsi_malloc(512)) == NULL) + return -ENOMEM; result = sr_do_ioctl (target, sr_cmd, buffer, 12, 0); @@ -454,9 +440,7 @@ tocentry->cdte_addr.lba = (((((buffer[8] << 8) + buffer[9]) << 8) + buffer[10]) << 8) + buffer[11]; - spin_lock_irqsave(&io_request_lock, flags); scsi_free(buffer, 512); - spin_unlock_irqrestore(&io_request_lock, flags); break; } @@ -482,7 +466,6 @@ { char * buffer, * mask; struct cdrom_volctrl* volctrl = (struct cdrom_volctrl*)arg; - unsigned long flags; /* First we get the current params so we can just twiddle the volume */ @@ -493,16 +476,12 @@ sr_cmd[4] = 28; sr_cmd[5] = 0; - spin_lock_irqsave(&io_request_lock, flags); - buffer = (unsigned char *) scsi_malloc(512); - spin_unlock_irqrestore(&io_request_lock, flags); - if(!buffer) return -ENOMEM; + if ((buffer = (unsigned char *) scsi_malloc(512)) == NULL) + return -ENOMEM; if ((result = sr_do_ioctl (target, sr_cmd, buffer, 28, 0))) { printk ("Hosed while obtaining audio mode page\n"); - spin_lock_irqsave(&io_request_lock, flags); scsi_free(buffer, 512); - spin_unlock_irqrestore(&io_request_lock, flags); break; } @@ -513,23 +492,17 @@ sr_cmd[4] = 28; sr_cmd[5] = 0; - spin_lock_irqsave(&io_request_lock, flags); mask = (unsigned char *) scsi_malloc(512); - spin_unlock_irqrestore(&io_request_lock, flags); if(!mask) { - spin_lock_irqsave(&io_request_lock, flags); scsi_free(buffer, 512); - spin_unlock_irqrestore(&io_request_lock, flags); result = -ENOMEM; break; }; if ((result = sr_do_ioctl (target, sr_cmd, mask, 28, 0))) { printk ("Hosed while obtaining mask for audio mode page\n"); - spin_lock_irqsave(&io_request_lock, flags); scsi_free(buffer, 512); scsi_free(mask, 512); - spin_unlock_irqrestore(&io_request_lock, flags); break; } @@ -548,10 +521,8 @@ sr_cmd[5] = 0; result = sr_do_ioctl (target, sr_cmd, buffer, 28, 0); - spin_lock_irqsave(&io_request_lock, flags); scsi_free(buffer, 512); scsi_free(mask, 512); - spin_unlock_irqrestore(&io_request_lock, flags); break; } @@ -559,7 +530,6 @@ { char * buffer; struct cdrom_volctrl* volctrl = (struct cdrom_volctrl*)arg; - unsigned long flags; /* Get the current params */ @@ -570,16 +540,12 @@ sr_cmd[4] = 28; sr_cmd[5] = 0; - spin_lock_irqsave(&io_request_lock, flags); - buffer = (unsigned char *) scsi_malloc(512); - spin_unlock_irqrestore(&io_request_lock, flags); - if(!buffer) return -ENOMEM; + if ((buffer = (unsigned char *) scsi_malloc(512)) == NULL) + return -ENOMEM; if ((result = sr_do_ioctl (target, sr_cmd, buffer, 28, 0))) { printk ("(CDROMVOLREAD) Hosed while obtaining audio mode page\n"); - spin_lock_irqsave(&io_request_lock, flags); scsi_free(buffer, 512); - spin_unlock_irqrestore(&io_request_lock, flags); break; } @@ -588,9 +554,7 @@ volctrl->channel2 = buffer[25]; volctrl->channel3 = buffer[27]; - spin_lock_irqsave(&io_request_lock, flags); scsi_free(buffer, 512); - spin_unlock_irqrestore(&io_request_lock, flags); break; } @@ -598,7 +562,6 @@ { struct cdrom_subchnl* subchnl = (struct cdrom_subchnl*)arg; char * buffer; - unsigned long flags; sr_cmd[0] = SCMD_READ_SUBCHANNEL; sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | 0x02; /* MSF format */ @@ -610,10 +573,8 @@ sr_cmd[8] = 16; sr_cmd[9] = 0; - spin_lock_irqsave(&io_request_lock, flags); - buffer = (unsigned char *) scsi_malloc(512); - spin_unlock_irqrestore(&io_request_lock, flags); - if(!buffer) return -ENOMEM; + if ((buffer = (unsigned char *) scsi_malloc(512)) == NULL) + return -ENOMEM; result = sr_do_ioctl(target, sr_cmd, buffer, 16, 0); @@ -630,9 +591,7 @@ subchnl->cdsc_absaddr.msf.second = buffer[10]; subchnl->cdsc_absaddr.msf.frame = buffer[11]; - spin_lock_irqsave(&io_request_lock, flags); scsi_free(buffer, 512); - spin_unlock_irqrestore(&io_request_lock, flags); break; } default: @@ -740,15 +699,13 @@ { unsigned char *raw_sector; int is_xa; - unsigned long flags; if (!xa_test) return 0; - spin_lock_irqsave(&io_request_lock, flags); - raw_sector = (unsigned char *) scsi_malloc(2048+512); - spin_unlock_irqrestore(&io_request_lock, flags); - if (!raw_sector) return -ENOMEM; + if ((raw_sector = (unsigned char *) scsi_malloc(2048+512)) == NULL) + return -ENOMEM; + if (0 == sr_read_sector(minor,scsi_CDs[minor].ms_offset+16, CD_FRAMESIZE_RAW1,raw_sector)) { is_xa = (raw_sector[3] == 0x02) ? 1 : 0; @@ -756,9 +713,7 @@ /* read a raw sector failed for some reason. */ is_xa = -1; } - spin_lock_irqsave(&io_request_lock, flags); scsi_free(raw_sector, 2048+512); - spin_unlock_irqrestore(&io_request_lock, flags); #ifdef DEBUG printk("sr%d: sr_is_xa: %d\n",minor,is_xa); #endif @@ -781,7 +736,6 @@ struct cdrom_msf msf; int lba, rc; int blocksize = 2048; - unsigned long flags; switch (cmd) { case CDROMREADMODE2: blocksize = CD_FRAMESIZE_RAW0; break; /* 2336 */ @@ -790,25 +744,24 @@ if (copy_from_user(&msf,(void*)arg,sizeof(msf))) return -EFAULT; - spin_lock_irqsave(&io_request_lock, flags); - raw = scsi_malloc(2048+512); - spin_unlock_irqrestore(&io_request_lock, flags); - if (!(raw)) - return -ENOMEM; + + if ((raw = scsi_malloc(2048+512)) == NULL) + return -ENOMEM; lba = (((msf.cdmsf_min0 * CD_SECS) + msf.cdmsf_sec0) * CD_FRAMES + msf.cdmsf_frame0) - CD_MSF_OFFSET; if (lba < 0 || lba >= scsi_CDs[target].capacity) + { + scsi_free(raw, 2048+512); return -EINVAL; + } rc = sr_read_sector(target, lba, blocksize, raw); if (!rc) if (copy_to_user((void*)arg, raw, blocksize)) rc = -EFAULT; - spin_lock_irqsave(&io_request_lock, flags); scsi_free(raw,2048+512); - spin_unlock_irqrestore(&io_request_lock, flags); return rc; } case CDROMREADAUDIO: @@ -816,7 +769,6 @@ unsigned char *raw; int lba, rc=0; struct cdrom_read_audio ra; - unsigned long flags; if (!scsi_CDs[target].readcd_known || !scsi_CDs[target].readcd_cdda) return -EINVAL; /* -EDRIVE_DOES_NOT_SUPPORT_THIS ? */ @@ -832,11 +784,8 @@ if (lba < 0 || lba >= scsi_CDs[target].capacity) return -EINVAL; - spin_lock_irqsave(&io_request_lock, flags); - raw = scsi_malloc(2048+512); - spin_unlock_irqrestore(&io_request_lock, flags); - if (!(raw)) - return -ENOMEM; + if ((raw = scsi_malloc(2048+512)) == NULL) + return -ENOMEM; while (ra.nframes > 0) { rc = sr_read_cd(target, raw, lba, 1, CD_FRAMESIZE_RAW); @@ -850,9 +799,7 @@ ra.nframes -= 1; lba++; } - spin_lock_irqsave(&io_request_lock, flags); scsi_free(raw,2048+512); - spin_unlock_irqrestore(&io_request_lock, flags); return rc; } case BLKRAGET: diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/sr_vendor.c linux/drivers/scsi/sr_vendor.c --- v2.2.13/linux/drivers/scsi/sr_vendor.c Mon May 10 13:01:21 1999 +++ linux/drivers/scsi/sr_vendor.c Tue Jan 4 10:12:21 2000 @@ -117,8 +117,8 @@ density = (blocklength > 2048) ? 0x81 : 0x83; #endif - buffer = (unsigned char *) scsi_malloc(512); - if (!buffer) return -ENOMEM; + if ((buffer = (unsigned char *) scsi_malloc(512)) == NULL) + return -ENOMEM; #ifdef DEBUG printk("sr%d: MODE SELECT 0x%x/%d\n",minor,density,blocklength); @@ -160,8 +160,8 @@ if (scsi_CDs[minor].cdi.mask & CDC_MULTI_SESSION) return 0; - buffer = (unsigned char *) scsi_malloc(512); - if(!buffer) return -ENOMEM; + if ((buffer = (unsigned char *) scsi_malloc(512)) == NULL) + return -ENOMEM; sector = 0; /* the multisession sector offset goes here */ no_multi = 0; /* flag: the drive can't handle multisession */ diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/sym53c416.h linux/drivers/scsi/sym53c416.h --- v2.2.13/linux/drivers/scsi/sym53c416.h Tue May 11 10:36:35 1999 +++ linux/drivers/scsi/sym53c416.h Tue Jan 4 10:12:21 2000 @@ -22,7 +22,9 @@ #include #endif +#ifndef LinuxVersionCode #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) +#endif #include #include diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/sym53c8xx.c linux/drivers/scsi/sym53c8xx.c --- v2.2.13/linux/drivers/scsi/sym53c8xx.c Tue Jan 4 11:10:39 2000 +++ linux/drivers/scsi/sym53c8xx.c Tue Jan 4 10:12:21 2000 @@ -300,7 +300,7 @@ ** **========================================================== */ -#if !defined(__i386__) +#if !defined(__i386__) && !defined(__sparc__) #define SCSI_NCR_PCI_MEM_NOT_SUPPORTED #endif @@ -586,10 +586,11 @@ #endif #ifdef __sparc__ +# include # define ioremap(base, size) ((u_long) __va(base)) # define iounmap(vaddr) # define pcivtobus(p) ((p) & pci_dvma_mask) -# define memcpy_to_pci(a, b, c) memcpy_toio((u_long) (a), (b), (c)) +# define memcpy_to_pci(a, b, c) memcpy_toio((void *) (a), (b), (c)) #elif defined(__alpha__) # define pcivtobus(p) ((p) & 0xfffffffful) # define memcpy_to_pci(a, b, c) memcpy_toio((a), (b), (c)) @@ -1819,6 +1820,8 @@ */ u_short device_id; /* PCI device id */ u_char revision_id; /* PCI device revision id */ + u_char pci_bus; /* PCI bus number */ + u_char pci_devfn; /* PCI device and function */ u_int features; /* Chip features map */ u_char myaddr; /* SCSI id of the adapter */ u_char maxburst; /* log base 2 of dwords burst */ @@ -4550,6 +4553,8 @@ sprintf(np->inst_name, NAME53C "%s-%d", np->chip_name, np->unit); np->device_id = device->chip.device_id; np->revision_id = device->chip.revision_id; + np->pci_bus = device->slot.bus; + np->pci_devfn = device->slot.device_fn; np->features = device->chip.features; np->clock_divn = device->chip.nr_divisor; np->maxoffs = device->chip.offset_max; @@ -9476,6 +9481,8 @@ if (np->multiplier != mult || (scntl3 & 7) < 3 || !(scntl3 & 1)) { unsigned f2; + OUTB(nc_istat, SRST); UDELAY (5); OUTB(nc_istat, 0); + (void) ncrgetfreq (np, 11); /* throw away first result */ f1 = ncrgetfreq (np, 11); f2 = ncrgetfreq (np, 11); @@ -10261,6 +10268,8 @@ if (!cache_line_size) suggested_cache_line_size = 16; + driver_setup.pci_fix_up |= 0x7; + #endif /* __sparc__ */ #if defined(__i386__) && !defined(MODULE) @@ -10574,7 +10583,15 @@ */ const char *sym53c8xx_info (struct Scsi_Host *host) { +#ifdef __sparc__ + /* Ok to do this on all archs? */ + static char buffer[80]; + ncb_p np = ((struct host_data *) host->hostdata)->ncb; + sprintf (buffer, "%s\nPCI bus %02x device %02x", SCSI_NCR_DRIVER_NAME, np->pci_bus, np->pci_devfn); + return buffer; +#else return SCSI_NCR_DRIVER_NAME; +#endif } /* @@ -11159,7 +11176,13 @@ copy_info(&info, "revision id 0x%x\n", np->revision_id); copy_info(&info, " IO port address 0x%lx, ", (u_long) np->base_io); +#ifdef __sparc__ + copy_info(&info, "IRQ number %s\n", __irq_itoa(np->irq)); + /* Ok to do this on all archs? */ + copy_info(&info, "PCI bus %02x device %02x\n", np->pci_bus, np->pci_devfn); +#else copy_info(&info, "IRQ number %d\n", (int) np->irq); +#endif #ifndef NCR_IOMAPPED if (np->reg) diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/sym53c8xx_defs.h linux/drivers/scsi/sym53c8xx_defs.h --- v2.2.13/linux/drivers/scsi/sym53c8xx_defs.h Tue Jan 4 11:10:39 2000 +++ linux/drivers/scsi/sym53c8xx_defs.h Tue Jan 4 10:12:21 2000 @@ -66,8 +66,9 @@ #endif #include +#ifndef LinuxVersionCode #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) - +#endif /* * NCR PQS/PDS special device support. */ diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/u14-34f.c linux/drivers/scsi/u14-34f.c --- v2.2.13/linux/drivers/scsi/u14-34f.c Fri Oct 9 11:56:59 1998 +++ linux/drivers/scsi/u14-34f.c Tue Jan 4 10:12:21 2000 @@ -1,6 +1,16 @@ /* * u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters. * + * 16 Sep 1999 Rev. 5.11 for linux 2.2.12 and 2.3.18 + * + Updated to the new __setup interface for boot command line options. + * + When loaded as a module, accepts the new parameter boot_options + * which value is a string with the same format of the kernel boot + * command line options. A valid example is: + * modprobe u14-34f 'boot_options=\"0x230,0x340,lc:y,mq:4\"' + * + * 22 Jul 1999 Rev. 5.00 for linux 2.2.10 and 2.3.11 + * + Removed pre-2.2 source code compatibility. + * * 26 Jul 1998 Rev. 4.33 for linux 2.0.35 and 2.1.111 * Added command line option (et:[y|n]) to use the existing * translation (returned by scsicam_bios_param) as disk geometry. @@ -159,7 +169,7 @@ * * Multiple U14F and/or U34F host adapters are supported. * - * Copyright (C) 1994-1998 Dario Ballabio (dario@milano.europe.dg.com) + * Copyright (C) 1994-1999 Dario Ballabio (dario@milano.europe.dg.com) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that redistributions of source @@ -251,7 +261,7 @@ * After the optional list of detection probes, other possible command line * options are: * - * eh:y use new scsi code (linux 2.2 only); + * eh:y use new scsi code; * eh:n use old scsi code; * et:y use disk geometry returned by scsicam_bios_param; * et:n use disk geometry jumpered on the board; @@ -318,7 +328,7 @@ #if defined(MODULE) #include -#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,26) +MODULE_PARM(boot_options, "s"); MODULE_PARM(io_port, "1-" __MODULE_STRING(MAX_INT_PARAM) "i"); MODULE_PARM(linked_comm, "i"); MODULE_PARM(have_old_firmware, "i"); @@ -327,7 +337,6 @@ MODULE_PARM(use_new_eh_code, "i"); MODULE_PARM(ext_tran, "i"); MODULE_AUTHOR("Dario Ballabio"); -#endif #endif @@ -349,42 +358,21 @@ #include "u14-34f.h" #include #include - -#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,36) #include +#include + +#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18) +#include #else -#define __initfunc(A) A -#define __initdata -#define __init +#include #endif -#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101) -#include -#define IRQ_FLAGS -#define IRQ_LOCK -#define IRQ_LOCK_SAVE -#define IRQ_UNLOCK -#define IRQ_UNLOCK_RESTORE #define SPIN_FLAGS unsigned long spin_flags; #define SPIN_LOCK spin_lock_irq(&io_request_lock); #define SPIN_LOCK_SAVE spin_lock_irqsave(&io_request_lock, spin_flags); #define SPIN_UNLOCK spin_unlock_irq(&io_request_lock); #define SPIN_UNLOCK_RESTORE \ spin_unlock_irqrestore(&io_request_lock, spin_flags); -static int use_new_eh_code = TRUE; -#else -#define IRQ_FLAGS unsigned long irq_flags; -#define IRQ_LOCK cli(); -#define IRQ_LOCK_SAVE do {save_flags(irq_flags); cli();} while (0); -#define IRQ_UNLOCK sti(); -#define IRQ_UNLOCK_RESTORE do {restore_flags(irq_flags);} while (0); -#define SPIN_FLAGS -#define SPIN_LOCK -#define SPIN_LOCK_SAVE -#define SPIN_UNLOCK -#define SPIN_UNLOCK_RESTORE -static int use_new_eh_code = FALSE; -#endif struct proc_dir_entry proc_scsi_u14_34f = { PROC_SCSI_U14_34F, 6, "u14_34f", @@ -467,7 +455,10 @@ #define ASOK 0x00 #define ASST 0x91 -#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0]) +#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18) +#define ARRAY_SIZE(x) (sizeof (x) / sizeof((x)[0])) +#endif + #define YESNO(a) ((a) ? 'y' : 'n') #define TLDEV(type) ((type) == TYPE_DISK || (type) == TYPE_ROM) @@ -532,7 +523,7 @@ /* Initialize num_boards so that ihdlr can work while detect is in progress */ static unsigned int num_boards = MAX_BOARDS; -static unsigned long io_port[] __initdata = { +static unsigned long io_port[] = { /* Space for MAX_INT_PARAM ports usable while loading as a module */ SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, @@ -570,6 +561,8 @@ static int setup_done = FALSE; static int link_statistics = 0; static int ext_tran = FALSE; +static int use_new_eh_code = TRUE; +static char *boot_options = NULL; #if defined(HAVE_OLD_UX4F_FIRMWARE) static int have_old_firmware = TRUE; @@ -592,9 +585,7 @@ static void select_queue_depths(struct Scsi_Host *host, Scsi_Device *devlist) { Scsi_Device *dev; int j, ntag = 0, nuntag = 0, tqd, utqd; - IRQ_FLAGS - IRQ_LOCK_SAVE j = ((struct hostdata *) host->hostdata)->board_number; for(dev = devlist; dev; dev = dev->next) { @@ -642,7 +633,6 @@ dev->queue_depth, link_suffix, tag_suffix); } - IRQ_UNLOCK_RESTORE return; } @@ -686,10 +676,8 @@ outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR); SPIN_UNLOCK - IRQ_UNLOCK time = jiffies; while ((jiffies - time) < HZ && limit++ < 20000) udelay(100L); - IRQ_LOCK SPIN_LOCK if (cpp->adapter_status || HD(j)->cp_stat[0] != FREE) { @@ -701,8 +689,8 @@ return FALSE; } -__initfunc (static inline int port_detect \ - (unsigned long port_base, unsigned int j, Scsi_Host_Template *tpnt)) { +static inline int port_detect \ + (unsigned long port_base, unsigned int j, Scsi_Host_Template *tpnt) { unsigned char irq, dma_channel, subversion, i; unsigned char in_byte; char *bus_type, dma_name[16]; @@ -747,7 +735,9 @@ sprintf(name, "%s%d", driver_name, j); if(check_region(port_base, REGION_SIZE)) { +#if defined(DEBUG_DETECT) printk("%s: address 0x%03lx in use, skipping probe.\n", name, port_base); +#endif return FALSE; } @@ -781,11 +771,7 @@ if (have_old_firmware) tpnt->use_clustering = DISABLE_CLUSTERING; -#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101) tpnt->use_new_eh_code = use_new_eh_code; -#else - use_new_eh_code = FALSE; -#endif sh[j] = scsi_register(tpnt, sizeof(struct hostdata)); @@ -894,7 +880,7 @@ if (max_queue_depth < MAX_CMD_PER_LUN) max_queue_depth = MAX_CMD_PER_LUN; if (j == 0) { - printk("UltraStor 14F/34F: Copyright (C) 1994-1998 Dario Ballabio.\n"); + printk("UltraStor 14F/34F: Copyright (C) 1994-1999 Dario Ballabio.\n"); printk("%s config options -> of:%c, lc:%c, mq:%d, eh:%c, et:%c.\n", driver_name, YESNO(have_old_firmware), YESNO(linked_comm), max_queue_depth, YESNO(use_new_eh_code), YESNO(ext_tran)); @@ -915,7 +901,7 @@ return TRUE; } -__initfunc (void u14_34f_setup(char *str, int *ints)) { +static void internal_setup(char *str, int *ints) { int i, argc = ints[0]; char *cur = str, *pc; @@ -949,13 +935,30 @@ return; } -__initfunc (int u14_34f_detect(Scsi_Host_Template *tpnt)) { +static int option_setup(char *str) { + int ints[MAX_INT_PARAM]; + char *cur = str; + int i = 1; + + while (cur && isdigit(*cur) && i <= MAX_INT_PARAM) { + ints[i++] = simple_strtoul(cur, NULL, 0); + + if ((cur = strchr(cur, ',')) != NULL) cur++; + } + + ints[0] = i - 1; + internal_setup(cur, ints); + return 0; +} + +int u14_34f_detect(Scsi_Host_Template *tpnt) +{ unsigned int j = 0, k; - IRQ_FLAGS - IRQ_LOCK_SAVE tpnt->proc_dir = &proc_scsi_u14_34f; + if(boot_options) option_setup(boot_options); + #if defined(MODULE) /* io_port could have been modified when loading as a module */ if(io_port[0] != SKIP) { @@ -974,7 +977,6 @@ } num_boards = j; - IRQ_UNLOCK_RESTORE return j; } @@ -1110,11 +1112,8 @@ int u14_34f_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) { int rtn; - IRQ_FLAGS - IRQ_LOCK_SAVE rtn = do_qcomm(SCpnt, done); - IRQ_UNLOCK_RESTORE return rtn; } @@ -1186,16 +1185,11 @@ int u14_34f_old_abort(Scsi_Cmnd *SCarg) { int rtn; - IRQ_FLAGS - IRQ_LOCK_SAVE rtn = do_old_abort(SCarg); - IRQ_UNLOCK_RESTORE return rtn; } -#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101) - static inline int do_abort(Scsi_Cmnd *SCarg) { unsigned int i, j; @@ -1273,8 +1267,6 @@ return do_abort(SCarg); } -#endif /* new_eh_code */ - static inline int do_old_reset(Scsi_Cmnd *SCarg) { unsigned int i, j, time, k, c, limit = 0; int arg_done = FALSE; @@ -1362,10 +1354,8 @@ HD(j)->in_reset = TRUE; SPIN_UNLOCK - IRQ_UNLOCK time = jiffies; while ((jiffies - time) < (10 * HZ) && limit++ < 200000) udelay(100L); - IRQ_LOCK SPIN_LOCK printk("%s: reset, interrupts disabled, loops %d.\n", BN(j), limit); @@ -1401,7 +1391,6 @@ continue; SCpnt->scsi_done(SCpnt); - IRQ_LOCK } HD(j)->in_reset = FALSE; @@ -1419,16 +1408,11 @@ int u14_34f_old_reset(Scsi_Cmnd *SCarg, unsigned int reset_flags) { int rtn; - IRQ_FLAGS - IRQ_LOCK_SAVE rtn = do_old_reset(SCarg); - IRQ_UNLOCK_RESTORE return rtn; } -#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101) - static inline int do_reset(Scsi_Cmnd *SCarg) { unsigned int i, j, time, k, c, limit = 0; int arg_done = FALSE; @@ -1510,10 +1494,8 @@ HD(j)->in_reset = TRUE; SPIN_UNLOCK - IRQ_UNLOCK time = jiffies; while ((jiffies - time) < (10 * HZ) && limit++ < 200000) udelay(100L); - IRQ_LOCK SPIN_LOCK printk("%s: reset, interrupts disabled, loops %d.\n", BN(j), limit); @@ -1549,7 +1531,6 @@ continue; SCpnt->scsi_done(SCpnt); - IRQ_LOCK } HD(j)->in_reset = FALSE; @@ -1566,8 +1547,6 @@ return do_reset(SCarg); } -#endif /* new_eh_code */ - int u14_34f_biosparam(Disk *disk, kdev_t dev, int *dkinfo) { unsigned int j = 0; int size = disk->capacity; @@ -1956,24 +1935,18 @@ static void do_interrupt_handler(int irq, void *shap, struct pt_regs *regs) { unsigned int j; - IRQ_FLAGS SPIN_FLAGS /* Check if the interrupt must be processed by this handler */ if ((j = (unsigned int)((char *)shap - sha)) >= num_boards) return; SPIN_LOCK_SAVE - IRQ_LOCK_SAVE ihdlr(irq, j); - IRQ_UNLOCK_RESTORE SPIN_UNLOCK_RESTORE } int u14_34f_release(struct Scsi_Host *shpnt) { unsigned int i, j; - IRQ_FLAGS - - IRQ_LOCK_SAVE for (j = 0; sh[j] != NULL && sh[j] != shpnt; j++); @@ -1989,7 +1962,6 @@ release_region(sh[j]->io_port, sh[j]->n_io_port); scsi_unregister(sh[j]); - IRQ_UNLOCK_RESTORE return FALSE; } @@ -1997,4 +1969,15 @@ Scsi_Host_Template driver_template = ULTRASTOR_14_34F; #include "scsi_module.c" + +#else + +#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18) +void u14_34f_setup(char *str, int *ints) { + internal_setup(str, ints); +} +#else +__setup("u14-34f=", option_setup); #endif + +#endif /* end MODULE */ diff -u --recursive --new-file v2.2.13/linux/drivers/scsi/u14-34f.h linux/drivers/scsi/u14-34f.h --- v2.2.13/linux/drivers/scsi/u14-34f.h Tue May 11 10:36:31 1999 +++ linux/drivers/scsi/u14-34f.h Tue Jan 4 10:12:21 2000 @@ -16,11 +16,7 @@ int u14_34f_old_reset(Scsi_Cmnd *, unsigned int); int u14_34f_biosparam(Disk *, kdev_t, int *); -#define U14_34F_VERSION "4.33.00" - -#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) - -#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101) +#define U14_34F_VERSION "5.11.01" #define ULTRASTOR_14_34F { \ name: "UltraStor 14F/34F rev. " U14_34F_VERSION " ", \ @@ -39,22 +35,5 @@ use_clustering: ENABLE_CLUSTERING, \ use_new_eh_code: 1 /* Enable new error code */ \ } - -#else /* Use old scsi code */ - -#define ULTRASTOR_14_34F { \ - name: "UltraStor 14F/34F rev. " U14_34F_VERSION " ", \ - detect: u14_34f_detect, \ - release: u14_34f_release, \ - queuecommand: u14_34f_queuecommand, \ - abort: u14_34f_old_abort, \ - reset: u14_34f_old_reset, \ - bios_param: u14_34f_biosparam, \ - this_id: 7, \ - unchecked_isa_dma: 1, \ - use_clustering: ENABLE_CLUSTERING \ - } - -#endif #endif diff -u --recursive --new-file v2.2.13/linux/drivers/sound/Config.in linux/drivers/sound/Config.in --- v2.2.13/linux/drivers/sound/Config.in Tue Jan 4 11:10:39 2000 +++ linux/drivers/sound/Config.in Tue Jan 4 10:12:21 2000 @@ -28,6 +28,9 @@ hex 'Gameport I/O 200,208,210,218' CONFIG_SOUND_ES1371_GAMEPORT 200 fi fi + +dep_tristate 'ESS Maestro' CONFIG_SOUND_MAESTRO $CONFIG_SOUND + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate 'ESS Solo1 (Experimental)' CONFIG_SOUND_ESSSOLO1 $CONFIG_SOUND fi @@ -114,8 +117,6 @@ comment 'Enter -1 to the following question if you have something else such as SB16/32.' int 'SB MPU401 IRQ (Jazz16, SM Wave and ES1688) Check from manual of the card' CONFIG_SB_MPU_IRQ -1 fi - - dep_tristate 'Generic OPL2/OPL3 FM synthesizer support' CONFIG_SOUND_ADLIB $CONFIG_SOUND_OSS dep_tristate 'Gravis Ultrasound support' CONFIG_SOUND_GUS $CONFIG_SOUND_OSS if [ "$CONFIG_SOUND_GUS" = "y" -o "$CONFIG_SOUND_GUS" = "m" ]; then diff -u --recursive --new-file v2.2.13/linux/drivers/sound/Makefile linux/drivers/sound/Makefile --- v2.2.13/linux/drivers/sound/Makefile Tue Jan 4 11:10:39 2000 +++ linux/drivers/sound/Makefile Tue Jan 4 10:12:21 2000 @@ -43,7 +43,6 @@ obj-$(CONFIG_SOUND) += soundcore.o obj-$(CONFIG_DMASOUND) += dmasound.o obj-$(CONFIG_SOUND_OSS) += sound.o -obj-$(CONFIG_SOUND_ADLIB) += adlib_card.o opl3.o obj-$(CONFIG_SOUND_CS4232) += cs4232.o ad1848.o # In theory, there's probably no reason to include the uart401 code @@ -84,6 +83,7 @@ obj-$(CONFIG_SOUND_ES1370) += es1370.o obj-$(CONFIG_SOUND_ES1371) += es1371.o obj-$(CONFIG_SOUND_ESSSOLO1) += esssolo1.o +obj-$(CONFIG_SOUND_MAESTRO) += maestro.o obj-$(CONFIG_SOUND_SONICVIBES) += sonicvibes.o # Declare multi-part drivers. diff -u --recursive --new-file v2.2.13/linux/drivers/sound/ac97.c linux/drivers/sound/ac97.c --- v2.2.13/linux/drivers/sound/ac97.c Tue Jan 4 11:10:39 2000 +++ linux/drivers/sound/ac97.c Tue Jan 4 10:12:21 2000 @@ -5,6 +5,10 @@ /* And for stereo. */ #define ST 1 +/* Whether or not the bits in the channel are inverted. */ +#define INV 1 +#define NINV 0 + static struct ac97_chn_desc { int ac97_regnum; int oss_channel; @@ -13,21 +17,22 @@ int oss_mask; int recordNum; u16 regmask; + int is_inverted; } mixerRegs[] = { - { AC97_MASTER_VOL_STEREO, SOUND_MIXER_VOLUME, 0x3f, ST, SOUND_MASK_VOLUME, 5, 0x0000 }, - { AC97_MASTER_VOL_MONO, SOUND_MIXER_PHONEOUT, 0x3f, MO, SOUND_MASK_PHONEOUT, 6, 0x0000 }, - { AC97_MASTER_TONE, SOUND_MIXER_TREBLE, 0x0f, MO, SOUND_MASK_TREBLE, -1, 0x00ff }, - { AC97_MASTER_TONE, SOUND_MIXER_BASS, 0x0f, MO, SOUND_MASK_BASS, -1, 0xff00 }, - { AC97_PCBEEP_VOL, SOUND_MIXER_SPEAKER, 0x0f, MO, SOUND_MASK_SPEAKER, -1, 0x001e }, - { AC97_PHONE_VOL, SOUND_MIXER_PHONEIN, 0x1f, MO, SOUND_MASK_PHONEIN, 7, 0x0000 }, - { AC97_MIC_VOL, SOUND_MIXER_MIC, 0x1f, MO, SOUND_MASK_MIC, 0, 0x0000 }, - { AC97_LINEIN_VOL, SOUND_MIXER_LINE, 0x1f, ST, SOUND_MASK_LINE, 4, 0x0000 }, - { AC97_CD_VOL, SOUND_MIXER_CD, 0x1f, ST, SOUND_MASK_CD, 1, 0x0000 }, - { AC97_VIDEO_VOL, SOUND_MIXER_VIDEO, 0x1f, ST, SOUND_MASK_VIDEO, 2, 0x0000 }, - { AC97_AUX_VOL, SOUND_MIXER_LINE1, 0x1f, ST, SOUND_MASK_LINE1, 3, 0x0000 }, - { AC97_PCMOUT_VOL, SOUND_MIXER_PCM, 0x1f, ST, SOUND_MASK_PCM, -1, 0x0000 }, - { AC97_RECORD_GAIN, SOUND_MIXER_IGAIN, 0x0f, ST, SOUND_MASK_IGAIN, -1, 0x0000 }, - { -1, -1, 0xff, 0, 0, -1, 0x0000 }, + { AC97_MASTER_VOL_STEREO, SOUND_MIXER_VOLUME, 0x3f, ST, SOUND_MASK_VOLUME, 5, 0x0000, INV }, + { AC97_MASTER_VOL_MONO, SOUND_MIXER_PHONEOUT, 0x3f, MO, SOUND_MASK_PHONEOUT, 6, 0x0000, INV }, + { AC97_MASTER_TONE, SOUND_MIXER_TREBLE, 0x0f, MO, SOUND_MASK_TREBLE, -1, 0x00ff, INV }, + { AC97_MASTER_TONE, SOUND_MIXER_BASS, 0x0f, MO, SOUND_MASK_BASS, -1, 0xff00, INV }, + { AC97_PCBEEP_VOL, SOUND_MIXER_SPEAKER, 0x0f, MO, SOUND_MASK_SPEAKER, -1, 0x001e, INV }, + { AC97_PHONE_VOL, SOUND_MIXER_PHONEIN, 0x1f, MO, SOUND_MASK_PHONEIN, 7, 0x0000, INV }, + { AC97_MIC_VOL, SOUND_MIXER_MIC, 0x1f, MO, SOUND_MASK_MIC, 0, 0x0000, INV }, + { AC97_LINEIN_VOL, SOUND_MIXER_LINE, 0x1f, ST, SOUND_MASK_LINE, 4, 0x0000, INV }, + { AC97_CD_VOL, SOUND_MIXER_CD, 0x1f, ST, SOUND_MASK_CD, 1, 0x0000, INV }, + { AC97_VIDEO_VOL, SOUND_MIXER_VIDEO, 0x1f, ST, SOUND_MASK_VIDEO, 2, 0x0000, INV }, + { AC97_AUX_VOL, SOUND_MIXER_LINE1, 0x1f, ST, SOUND_MASK_LINE1, 3, 0x0000, INV }, + { AC97_PCMOUT_VOL, SOUND_MIXER_PCM, 0x1f, ST, SOUND_MASK_PCM, -1, 0x0000, INV }, + { AC97_RECORD_GAIN, SOUND_MIXER_IGAIN, 0x0f, ST, SOUND_MASK_IGAIN, -1, 0x0000, NINV }, + { -1, -1, 0xff, 0, 0, -1, 0x0000, 0 }, }; static struct ac97_chn_desc * @@ -104,6 +109,25 @@ return 0; } +/* Reset the mixer to the currently saved settings. */ +int +ac97_reset (struct ac97_hwint *dev) +{ + int x; + + if (dev->reset_device (dev)) + return -1; + + /* Now set the registers back to their last-written values. */ + for (x = 0; mixerRegs[x].ac97_regnum != -1; x++) { + int regnum = mixerRegs[x].ac97_regnum; + int value = dev->last_written_mixer_values [regnum / 2]; + if (value >= 0) + ac97_put_register (dev, regnum, value); + } + return 0; +} + /* Return the contents of register REG; use the cache if the value in it is valid. Returns a negative error code on failure. */ int @@ -156,38 +180,45 @@ scaled value on success. */ static int -ac97_scale_to_oss_val (int value, int maxval, int is_stereo) +ac97_scale_to_oss_val (int value, int maxval, int is_stereo, int inv) { /* Muted? */ if (value & AC97_MUTE) return 0; if (is_stereo) - return (ac97_scale_to_oss_val (value & 255, maxval, 0) << 8) - | (ac97_scale_to_oss_val ((value >> 8) & 255, maxval, 0) << 0); + return (ac97_scale_to_oss_val (value & 255, maxval, 0, inv) << 8) + | (ac97_scale_to_oss_val ((value >> 8) & 255, maxval, 0, inv) << 0); else { int i; /* Inverted. */ - value = maxval - value; + if (inv) + value = maxval - value; i = (value * 100 + (maxval / 2)) / maxval; if (i > 100) i = 100; + if (i < 0) + i = 0; return i; } } static int -ac97_scale_from_oss_val (int value, int maxval, int is_stereo) +ac97_scale_from_oss_val (int value, int maxval, int is_stereo, int inv) { if (is_stereo) - return (ac97_scale_from_oss_val (value & 255, maxval, 0) << 8) - | (ac97_scale_from_oss_val ((value >> 8) & 255, maxval, 0) << 0); + return (ac97_scale_from_oss_val (value & 255, maxval, 0, inv) << 8) + | (ac97_scale_from_oss_val ((value >> 8) & 255, maxval, 0, inv) << 0); else { - int i = maxval - ((value & 255) * maxval + 50) / 100; + int i = ((value & 255) * maxval + 50) / 100; + if (inv) + i = maxval - i; if (i < 0) i = 0; + if (i > maxval) + i = maxval; return i; } } @@ -204,7 +235,8 @@ if (! ac97_is_valid_channel (dev, channel)) return -ENODEV; scaled_value = ac97_scale_from_oss_val (oss_value, channel->maxval, - channel->is_stereo); + channel->is_stereo, + channel->is_inverted); if (scaled_value < 0) return scaled_value; @@ -253,7 +285,8 @@ regval >>= 1; } return ac97_scale_to_oss_val (regval, channel->maxval, - channel->is_stereo); + channel->is_stereo, + channel->is_inverted); } int @@ -383,8 +416,9 @@ else ret = -EFAULT; } - if (ret >= 0) { - if (dev->last_written_OSS_values[channel] == AC97_REGVAL_UNKNOWN) + if (ret >= 0 && (dir & _IOC_READ)) { + if (dev->last_written_OSS_values[channel] + == AC97_REGVAL_UNKNOWN) dev->last_written_OSS_values[channel] = ac97_get_mixer_scaled (dev, channel); ret = dev->last_written_OSS_values[channel]; diff -u --recursive --new-file v2.2.13/linux/drivers/sound/ac97.h linux/drivers/sound/ac97.h --- v2.2.13/linux/drivers/sound/ac97.h Tue Jan 4 11:10:39 2000 +++ linux/drivers/sound/ac97.h Tue Jan 4 10:12:21 2000 @@ -10,7 +10,7 @@ #include "sound_config.h" #include "sound_calls.h" -#define AC97_RESET 0x0000 // */ +#define AC97_RESET 0x0000 // #define AC97_MASTER_VOL_STEREO 0x0002 // Line Out #define AC97_HEADPHONE_VOL 0x0004 // #define AC97_MASTER_VOL_MONO 0x0006 // TAD Output @@ -33,6 +33,32 @@ /* registers 0x0028 - 0x0058 are reserved */ +/* AC'97 2.0 */ +#define AC97_EXTENDED_ID 0x0028 /* Extended Audio ID */ +#define AC97_EXTENDED_STATUS 0x002A /* Extended Audio Status */ +#define AC97_PCM_FRONT_DAC_RATE 0x002C /* PCM Front DAC Rate */ +#define AC97_PCM_SURR_DAC_RATE 0x002E /* PCM Surround DAC Rate */ +#define AC97_PCM_LFE_DAC_RATE 0x0030 /* PCM LFE DAC Rate */ +#define AC97_PCM_LR_DAC_RATE 0x0032 /* PCM LR DAC Rate */ +#define AC97_PCM_MIC_ADC_RATE 0x0034 /* PCM MIC ADC Rate */ +#define AC97_CENTER_LFE_MASTER 0x0036 /* Center + LFE Master Volume */ +#define AC97_SURROUND_MASTER 0x0038 /* Surround (Rear) Master Volume */ +#define AC97_RESERVED_3A 0x003A /* Reserved */ +/* range 0x3c-0x58 - MODEM */ + +/* AC'97 2.0 */ +#define AC97_EXTENDED_ID 0x0028 /* Extended Audio ID */ +#define AC97_EXTENDED_STATUS 0x002A /* Extended Audio Status */ +#define AC97_PCM_FRONT_DAC_RATE 0x002C /* PCM Front DAC Rate */ +#define AC97_PCM_SURR_DAC_RATE 0x002E /* PCM Surround DAC Rate */ +#define AC97_PCM_LFE_DAC_RATE 0x0030 /* PCM LFE DAC Rate */ +#define AC97_PCM_LR_DAC_RATE 0x0032 /* PCM LR DAC Rate */ +#define AC97_PCM_MIC_ADC_RATE 0x0034 /* PCM MIC ADC Rate */ +#define AC97_CENTER_LFE_MASTER 0x0036 /* Center + LFE Master Volume */ +#define AC97_SURROUND_MASTER 0x0038 /* Surround (Rear) Master Volume */ +#define AC97_RESERVED_3A 0x003A /* Reserved */ +/* range 0x3c-0x58 - MODEM */ + /* registers 0x005a - 0x007a are vendor reserved */ #define AC97_VENDOR_ID1 0x007c @@ -194,6 +220,10 @@ /* Default ioctl. */ extern int ac97_mixer_ioctl (struct ac97_hwint *dev, unsigned int cmd, caddr_t arg); + +/* Do a complete reset on the AC97 mixer, restoring all mixer registers to + the current values. Normally used after an APM resume event. */ +extern int ac97_reset (struct ac97_hwint *dev); #endif /* diff -u --recursive --new-file v2.2.13/linux/drivers/sound/ad1848.c linux/drivers/sound/ad1848.c --- v2.2.13/linux/drivers/sound/ad1848.c Tue Jan 4 11:10:39 2000 +++ linux/drivers/sound/ad1848.c Tue Jan 4 10:12:21 2000 @@ -71,7 +71,8 @@ #define MD_4232 5 #define MD_C930 6 #define MD_IWAVE 7 -#define MD_4235 8 /* Crystal Audio CS4235 */ +#define MD_4235 8 /* Crystal Audio CS4235 */ +#define MD_1845_SSCAPE 9 /* Ensoniq Soundscape PNP*/ /* Mixer parameters */ int recmask; @@ -100,6 +101,7 @@ static int nr_ad1848_devs = 0; int deskpro_xl = 0; +int deskpro_m = 0; #ifdef CONFIG_SOUND_SPRO int soundpro = 1; #else @@ -117,7 +119,7 @@ #endif -static int ad_format_mask[9 /*devc->model */ ] = +static int ad_format_mask[10 /*devc->model */ ] = { 0, AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, @@ -127,7 +129,8 @@ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, - AFMT_U8 | AFMT_S16_LE /* CS4235 */ + AFMT_U8 | AFMT_S16_LE /* CS4235 */, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW /* Ensoniq Soundscape*/ }; static ad1848_info adev_info[MAX_AUDIO_DEV]; @@ -140,7 +143,7 @@ static struct { unsigned char flags; #define CAP_F_TIMER 0x01 -} capabilities [9 /*devc->model */ ] = { +} capabilities [10 /*devc->model */ ] = { {0} ,{0} /* MD_1848 */ ,{CAP_F_TIMER} /* MD_4231 */ @@ -149,7 +152,8 @@ ,{CAP_F_TIMER} /* MD_4232 */ ,{0} /* MD_C930 */ ,{CAP_F_TIMER} /* MD_IWAVE */ - ,{0} /* MD_4235 */ + ,{0} /* MD_4235 */ + ,{CAP_F_TIMER} /* MD_1845_SSCAPE */ }; static int ad1848_open(int dev, int mode); @@ -231,7 +235,7 @@ while (timeout > 0 && (ad_read(devc, 11) & 0x20)) timeout--; if (ad_read(devc, 11) & 0x20) - if (devc->model != MD_1845) + if ( (devc->model != MD_1845) || (devc->model != MD_1845_SSCAPE)) printk(KERN_WARNING "ad1848: Auto calibration timed out(3).\n"); } @@ -555,6 +559,7 @@ case MD_4231: case MD_4231A: case MD_1845: + case MD_1845_SSCAPE: devc->supported_devices = MODE2_MIXER_DEVICES; break; @@ -751,7 +756,7 @@ if (arg <= 0) return portc->speed; - if (devc->model == MD_1845) /* AD1845 has different timer than others */ + if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) /* AD1845 has different timer than others */ { if (arg < 4000) arg = 4000; @@ -1087,7 +1092,7 @@ ad_enter_MCE(devc); /* Enables changes to the format select reg */ - if (devc->model == MD_1845) /* Use alternate speed select registers */ + if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) /* Use alternate speed select registers */ { fs &= 0xf0; /* Mask off the rate select bits */ @@ -1157,7 +1162,7 @@ ad_enter_MCE(devc); /* Enables changes to the format select reg */ - if (devc->model == MD_1845) /* Use alternate speed select registers */ + if ((devc->model == MD_1845) || (devc->model == MD_1845_SSCAPE)) /* Use alternate speed select registers */ { fs &= 0xf0; /* Mask off the rate select bits */ @@ -1193,7 +1198,7 @@ while (timeout < 10000 && inb(devc->base) == 0x80) timeout++; - if (devc->model != MD_1848 && devc->model != MD_1845) + if (devc->model != MD_1848 && devc->model != MD_1845 && devc->model != MD_1845_SSCAPE) { /* * CS4231 compatible devices don't have separate sampling rate selection @@ -1405,13 +1410,17 @@ if (devc->model > MD_1848) { - ad_write(devc, 12, ad_read(devc, 12) | 0x40); /* Mode2 = enabled */ + if (devc->model == MD_1845_SSCAPE) + ad_write(devc, 12, ad_read(devc, 12) | 0x50); + else + ad_write(devc, 12, ad_read(devc, 12) | 0x40); /* Mode2 = enabled */ if (devc->model == MD_IWAVE) ad_write(devc, 12, 0x6c); /* Select codec mode 3 */ - for (i = 16; i < 32; i++) - ad_write(devc, i, init_values[i]); + if (devc-> model != MD_1845_SSCAPE) + for (i = 16; i < 32; i++) + ad_write(devc, i, init_values[i]); if (devc->model == MD_IWAVE) ad_write(devc, 16, 0x30); /* Playback and capture counters enabled */ @@ -1423,7 +1432,7 @@ else ad_write(devc, 9, ad_read(devc, 9) | 0x04); /* Single DMA mode */ - if (devc->model == MD_1845) + if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) ad_write(devc, 27, ad_read(devc, 27) | 0x08); /* Alternate freq select enabled */ if (devc->model == MD_IWAVE) @@ -1462,6 +1471,7 @@ int interwave = 0; int ad1847_flag = 0; int cs4248_flag = 0; + int sscape_flag = 0; int i; @@ -1474,6 +1484,13 @@ interwave = 1; *ad_flags = 0; } + + if (*ad_flags == 0x87654321) + { + sscape_flag = 1; + *ad_flags = 0; + } + if (*ad_flags == 0x12345677) { cs4248_flag = 1; @@ -1821,6 +1838,9 @@ devc->chip_name = "AD1847"; + if (sscape_flag == 1) + devc->model = MD_1845_SSCAPE; + return 1; } @@ -1979,7 +1999,7 @@ switch (cmd) { case AD1848_SET_XTAL: /* Change clock frequency of AD1845 (only ) */ - if (devc->model != MD_1845) + if (devc->model != MD_1845 || devc->model != MD_1845_SSCAPE) return -EINVAL; ad_enter_MCE(devc); ad_write(devc, 29, (ad_read(devc, 29) & 0x1f) | (arg << 5)); @@ -2146,6 +2166,34 @@ /* * Experimental initialization sequence for the integrated sound system + * of the Compaq Deskpro M. + */ + +static int init_deskpro_m(struct address_info *hw_config) +{ + unsigned char tmp; + + if ((tmp = inb(0xc44)) == 0xff) + { + DDB(printk("init_deskpro_m: Dead port 0xc44\n")); + return 0; + } + + outb(0x10, 0xc44); + outb(0x40, 0xc45); + outb(0x00, 0xc46); + outb(0xe8, 0xc47); + outb(0x14, 0xc44); + outb(0x40, 0xc45); + outb(0x00, 0xc46); + outb(0xe8, 0xc47); + outb(0x10, 0xc44); + + return 1; +} + +/* + * Experimental initialization sequence for the integrated sound system * of Compaq Deskpro XL. */ @@ -2370,6 +2418,12 @@ return 0; } + if (deskpro_m) /* Compaq Deskpro M */ + { + if (!init_deskpro_m(hw_config)) + return 0; + } + /* * Check if the IO port returns valid signature. The original MS Sound * system returns 0x04 while some cards (AudioTrix Pro for example) @@ -2558,7 +2612,7 @@ * the timer divider. */ - if (devc->model == MD_1845) + if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) xtal_nsecs = 10050; else if (ad_read(devc, 8) & 0x01) xtal_nsecs = 9920; @@ -2659,6 +2713,7 @@ MODULE_PARM(dma2, "i"); /* Second DMA channel */ MODULE_PARM(type, "i"); /* Card type */ MODULE_PARM(deskpro_xl, "i"); /* Special magic for Deskpro XL boxen */ +MODULE_PARM(deskpro_m, "i"); /* Special magic for Deskpro M box */ MODULE_PARM(soundpro, "i"); /* More special magic for SoundPro chips */ int io = -1; diff -u --recursive --new-file v2.2.13/linux/drivers/sound/adlib_card.c linux/drivers/sound/adlib_card.c --- v2.2.13/linux/drivers/sound/adlib_card.c Thu May 14 10:33:17 1998 +++ linux/drivers/sound/adlib_card.c Tue Jan 4 10:12:21 2000 @@ -16,8 +16,6 @@ #include "sound_config.h" #include "soundmodule.h" -#ifdef CONFIG_YM3812 - void attach_adlib_card(struct address_info *hw_config) { hw_config->slots[0] = opl3_init(hw_config->io_base, hw_config->osp); @@ -69,5 +67,4 @@ SOUND_LOCK_END; } -#endif #endif diff -u --recursive --new-file v2.2.13/linux/drivers/sound/dmasound.c linux/drivers/sound/dmasound.c --- v2.2.13/linux/drivers/sound/dmasound.c Tue Oct 19 17:10:38 1999 +++ linux/drivers/sound/dmasound.c Tue Jan 4 10:12:21 2000 @@ -89,6 +89,7 @@ #include #include #include +#include #if defined(__mc68000__) || defined(CONFIG_APUS) #include @@ -233,7 +234,7 @@ -269, -245, -218, -187, -153, -117, -79, -40, }; -#define BEEP_SPEED 2 /* 22050 Hz sample rate */ +#define BEEP_SRATE 22050 /* 22050 Hz sample rate */ #define BEEP_BUFLEN 512 #define BEEP_VOLUME 15 /* 0 - 100 */ @@ -3003,8 +3004,9 @@ static int __init PMacIrqInit(void) { - if (request_irq(awacs_irq, pmac_awacs_intr, 0, "AWACS", 0) - || request_irq(awacs_tx_irq, pmac_awacs_tx_intr, 0, "AWACS out", 0)) + if (request_irq(awacs_irq, pmac_awacs_intr, 0, "AWACS", (void *) awacs) + || request_irq(awacs_tx_irq, pmac_awacs_tx_intr, 0, + "AWACS out", (void *) awacs)) return 0; return 1; } @@ -3016,8 +3018,8 @@ out_le32(&awacs_txdma->control, RUN<<16); /* disable interrupts from awacs interface */ out_le32(&awacs->control, in_le32(&awacs->control) & 0xfff); - free_irq(awacs_irq, pmac_awacs_intr); - free_irq(awacs_tx_irq, pmac_awacs_tx_intr); + free_irq(awacs_irq, (void *) awacs); + free_irq(awacs_tx_irq, (void *) awacs); kfree(awacs_tx_cmd_space); if (beep_buf) kfree(beep_buf); @@ -3037,6 +3039,7 @@ static int awacs_freqs[8] = { 44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350 }; +static int awacs_freqs_ok[8] = { 1, 1, 1, 1, 1, 1, 1, 1 }; static void PMacInit(void) { @@ -3060,10 +3063,13 @@ * Otherwise choose the next higher rate. * N.B.: burgundy awacs (iMac and later) only works at 44100 Hz. */ - i = (awacs_revision >= AWACS_BURGUNDY)? 1: 8; + i = 8; do { tolerance = catchRadius * awacs_freqs[--i] / 100; - } while (sound.soft.speed > awacs_freqs[i] + tolerance && i > 0); + if (awacs_freqs_ok[i] + && sound.soft.speed <= awacs_freqs[i] + tolerance) + break; + } while (i > 0); if (sound.soft.speed >= awacs_freqs[i] - tolerance) sound.trans = &transAwacsNormal; else @@ -3171,7 +3177,7 @@ out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); out_le32(&awacs->control, (in_le32(&awacs->control) & ~0x1f00) - || (awacs_rate_index << 8)); + | (awacs_rate_index << 8)); out_le32(&awacs->byteswap, sound.hard.format != AFMT_S16_BE); beep_playing = 0; } @@ -3259,6 +3265,11 @@ save_flags(flags); cli(); if (beep_playing) { st_le16(&beep_dbdma_cmd->command, DBDMA_STOP); + out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); + out_le32(&awacs->control, + (in_le32(&awacs->control) & ~0x1f00) + | (awacs_rate_index << 8)); + out_le32(&awacs->byteswap, sound.hard.format != AFMT_S16_BE); beep_playing = 0; } restore_flags(flags); @@ -3271,8 +3282,8 @@ static void awacs_mksound(unsigned int hz, unsigned int ticks) { unsigned long flags; - int beep_speed = (awacs_revision < AWACS_BURGUNDY)? BEEP_SPEED: 0; - int srate = awacs_freqs[beep_speed]; + int beep_speed = 0; + int srate; int period, ncycles, nsamples; int i, j, f; short *p; @@ -3280,6 +3291,11 @@ static int beep_nsamples_cache; static int beep_volume_cache; + for (i = 0; i < 8 && awacs_freqs[i] >= BEEP_SRATE; ++i) + if (awacs_freqs_ok[i]) + beep_speed = i; + srate = awacs_freqs[beep_speed]; + if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) { #if 1 /* this is a hack for broken X server code */ @@ -3366,6 +3382,12 @@ out_le32(&awacs->byteswap, sound.hard.format != AFMT_S16_BE); enable_irq(awacs_irq); enable_irq(awacs_tx_irq); + if (awacs_revision == 3) { + mdelay(100); + awacs_write(0x6000); + mdelay(2); + awacs_write(awacs_reg[1] | MASK_ADDR1); + } } return PBOOK_SLEEP_OK; } @@ -4944,12 +4966,41 @@ np = find_devices("davbus"); sound = find_devices("sound"); if (sound != 0 && sound->parent == np) { - int *sfprop; - sfprop = (int *) get_property(sound, "sub-frame", 0); - if (sfprop != 0 && *sfprop >= 0 && *sfprop < 16) - awacs_subframe = *sfprop; + unsigned int *prop, l, i; + prop = (unsigned int *) + get_property(sound, "sub-frame", 0); + if (prop != 0 && *prop >= 0 && *prop < 16) + awacs_subframe = *prop; if (device_is_compatible(sound, "burgundy")) awacs_revision = AWACS_BURGUNDY; + + /* look for a property saying what sample rates + are available */ + for (i = 0; i < 8; ++i) + awacs_freqs_ok[i] = 0; + prop = (unsigned int *) get_property + (sound, "sample-rates", &l); + if (prop == 0) + prop = (unsigned int *) get_property + (sound, "output-frame-rates", &l); + if (prop != 0) { + for (l /= sizeof(int); l > 0; --l) { + /* sometimes the rate is in the + high-order 16 bits (?) */ + unsigned int r = *prop++; + if (r >= 0x10000) + r >>= 16; + for (i = 0; i < 8; ++i) { + if (r == awacs_freqs[i]) { + awacs_freqs_ok[i] = 1; + break; + } + } + } + } else { + /* assume just 44.1k is OK */ + awacs_freqs_ok[0] = 1; + } } } if (np != NULL && np->n_addrs >= 3 && np->n_intrs >= 3) { diff -u --recursive --new-file v2.2.13/linux/drivers/sound/es1370.c linux/drivers/sound/es1370.c --- v2.2.13/linux/drivers/sound/es1370.c Mon Aug 9 16:05:57 1999 +++ linux/drivers/sound/es1370.c Tue Jan 4 10:12:21 2000 @@ -101,6 +101,17 @@ * 15.06.99 0.23 Fix bad allocation bug. * Thanks to Deti Fliegl * 28.06.99 0.24 Add pci_set_master + * 02.08.99 0.25 Added workaround for the "phantom write" bug first + * documented by Dave Sharpless from Anchor Games + * 03.08.99 0.26 adapt to Linus' new __setup/__initcall + * added kernel command line option "es1370=joystick[,lineout[,micbias]]" + * removed CONFIG_SOUND_ES1370_JOYPORT_BOOT kludge + * 12.08.99 0.27 module_init/__setup fixes + * 19.08.99 0.28 SOUND_MIXER_IMIX fixes, reported by Gianluca + * 31.08.99 0.29 add spin_lock_init + * __initlocaldata to fix gcc 2.7.x problems + * 03.09.99 0.30 change read semantics for MIDI to match + * OSS more closely; remove possible wakeup race * * some important things missing in Ensoniq documentation: * @@ -123,7 +134,6 @@ /*****************************************************************************/ -#include #include #include #include @@ -175,12 +185,14 @@ #define ES1370_REG_DAC2_SCOUNT 0x28 #define ES1370_REG_ADC_SCOUNT 0x2c -#define ES1370_REG_DAC1_FRAMEADR 0xc30 -#define ES1370_REG_DAC1_FRAMECNT 0xc34 -#define ES1370_REG_DAC2_FRAMEADR 0xc38 -#define ES1370_REG_DAC2_FRAMECNT 0xc3c -#define ES1370_REG_ADC_FRAMEADR 0xd30 -#define ES1370_REG_ADC_FRAMECNT 0xd34 +#define ES1370_REG_DAC1_FRAMEADR 0xc30 +#define ES1370_REG_DAC1_FRAMECNT 0xc34 +#define ES1370_REG_DAC2_FRAMEADR 0xc38 +#define ES1370_REG_DAC2_FRAMECNT 0xc3c +#define ES1370_REG_ADC_FRAMEADR 0xd30 +#define ES1370_REG_ADC_FRAMECNT 0xd34 +#define ES1370_REG_PHANTOM_FRAMEADR 0xd38 +#define ES1370_REG_PHANTOM_FRAMECNT 0xd3c #define ES1370_FMT_U8_MONO 0 #define ES1370_FMT_U8_STEREO 1 @@ -360,6 +372,13 @@ static struct es1370_state *devs = NULL; +/* + * The following buffer is used to point the phantom write channel to, + * so that it cannot wreak havoc. The attribute makes sure it doesn't + * cross a page boundary and ensures dword alignment for the DMA engine + */ +static unsigned char bugbuf[16] __attribute__ ((aligned (16))); + /* --------------------------------------------------------------------- */ extern inline unsigned ld2(unsigned int x) @@ -779,10 +798,36 @@ [SOUND_MIXER_OGAIN] = { 9, 0xf, 0x0, 0, 0x0000, 1 } /* mono out */ }; +static void set_recsrc(struct es1370_state *s, unsigned int val) +{ + unsigned int i, j; + + for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!(val & (1 << i))) + continue; + if (!mixtable[i].recmask) { + val &= ~(1 << i); + continue; + } + j |= mixtable[i].recmask; + } + s->mix.recsrc = val; + wrcodec(s, 0x12, j & 0xd5); + wrcodec(s, 0x13, j & 0xaa); + wrcodec(s, 0x14, (j >> 8) & 0x17); + wrcodec(s, 0x15, (j >> 8) & 0x0f); + i = (j & 0x37f) | ((j << 1) & 0x3000) | 0xc60; + if (!s->mix.imix) { + i &= 0xff60; /* mute record and line monitor */ + } + wrcodec(s, 0x10, i); + wrcodec(s, 0x11, i >> 8); +} + static int mixer_ioctl(struct es1370_state *s, unsigned int cmd, unsigned long arg) { unsigned long flags; - int i, val, j; + int i, val; unsigned char l, r, rl, rr; VALIDATE_STATE(s); @@ -887,34 +932,13 @@ switch (_IOC_NR(cmd)) { case SOUND_MIXER_IMIX: - if (arg == 0) - return -EFAULT; - get_user_ret(s->mix.imix,(int *)arg, -EFAULT); - val = s->mix.recsrc; - /* fall through */ + get_user_ret(s->mix.imix, (int *)arg, -EFAULT); + set_recsrc(s, s->mix.recsrc); + return 0; case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ get_user_ret(val, (int *)arg, -EFAULT); - for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - if (!(val & (1 << i))) - continue; - if (!mixtable[i].recmask) { - val &= ~(1 << i); - continue; - } - j |= mixtable[i].recmask; - } - s->mix.recsrc = val; - wrcodec(s, 0x12, j & 0xd5); - wrcodec(s, 0x13, j & 0xaa); - wrcodec(s, 0x14, (j >> 8) & 0x17); - wrcodec(s, 0x15, (j >> 8) & 0x0f); - i = (j & 0x37f) | ((j << 1) & 0x3000) | 0xc60; - if (!s->mix.imix) { - i &= 0xff60; /* mute record and line monitor */ - } - wrcodec(s, 0x10, i); - wrcodec(s, 0x11, i >> 8); + set_recsrc(s, val); return 0; default: @@ -1033,7 +1057,7 @@ static int drain_dac1(struct es1370_state *s, int nonblock) { - struct wait_queue wait = { current, NULL }; + struct wait_queue wait = { current, NULL }; unsigned long flags; int count, tmo; @@ -1054,9 +1078,10 @@ current->state = TASK_RUNNING; return -EBUSY; } - tmo = (count * HZ) / dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL]; + tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2 + / dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL]; tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; - if (!schedule_timeout(tmo ? : 1) && tmo) + if (!schedule_timeout(tmo + 1)) DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");) } remove_wait_queue(&s->dma_dac1.wait, &wait); @@ -1089,9 +1114,10 @@ current->state = TASK_RUNNING; return -EBUSY; } - tmo = (count * HZ) / DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV); + tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2 + / DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV); tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; - if (!schedule_timeout(tmo ? : 1) && tmo) + if (!schedule_timeout(tmo + 1)) DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");) } remove_wait_queue(&s->dma_dac2.wait, &wait); @@ -2049,6 +2075,7 @@ static ssize_t es1370_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct es1370_state *s = (struct es1370_state *)file->private_data; + struct wait_queue wait = { current, NULL }; ssize_t ret; unsigned long flags; unsigned ptr; @@ -2059,7 +2086,10 @@ return -ESPIPE; if (!access_ok(VERIFY_WRITE, buffer, count)) return -EFAULT; + if (count == 0) + return 0; ret = 0; + add_wait_queue(&s->midi.iwait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); ptr = s->midi.ird; @@ -2070,15 +2100,25 @@ if (cnt > count) cnt = count; if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->midi.iwait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) - return ret ? ret : -EFAULT; + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } ptr = (ptr + cnt) % MIDIINBUF; spin_lock_irqsave(&s->lock, flags); s->midi.ird = ptr; @@ -2087,13 +2127,17 @@ count -= cnt; buffer += cnt; ret += cnt; + break; } + current->state = TASK_RUNNING; + remove_wait_queue(&s->midi.iwait, &wait); return ret; } static ssize_t es1370_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct es1370_state *s = (struct es1370_state *)file->private_data; + struct wait_queue wait = { current, NULL }; ssize_t ret; unsigned long flags; unsigned ptr; @@ -2104,7 +2148,10 @@ return -ESPIPE; if (!access_ok(VERIFY_READ, buffer, count)) return -EFAULT; + if (count == 0) + return 0; ret = 0; + add_wait_queue(&s->midi.owait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); ptr = s->midi.owr; @@ -2117,15 +2164,25 @@ if (cnt > count) cnt = count; if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->midi.owait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) - return ret ? ret : -EFAULT; + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } ptr = (ptr + cnt) % MIDIOUTBUF; spin_lock_irqsave(&s->lock, flags); s->midi.owr = ptr; @@ -2138,6 +2195,8 @@ es1370_handle_midi(s); spin_unlock_irqrestore(&s->lock, flags); } + current->state = TASK_RUNNING; + remove_wait_queue(&s->midi.owait, &wait); return ret; } @@ -2217,7 +2276,7 @@ static int es1370_midi_release(struct inode *inode, struct file *file) { struct es1370_state *s = (struct es1370_state *)file->private_data; - struct wait_queue wait = { current, NULL }; + struct wait_queue wait = { current, NULL }; unsigned long flags; unsigned count, tmo; @@ -2321,7 +2380,7 @@ if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "es1370: version v0.24 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "es1370: version v0.30 time " __TIME__ " " __DATE__ "\n"); while (index < NR_DEVICE && (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, pcidev))) { if (pcidev->base_address[0] == 0 || @@ -2341,6 +2400,7 @@ init_waitqueue(&s->midi.iwait); init_waitqueue(&s->midi.owait); s->open_sem = MUTEX; + spin_lock_init(&s->lock); s->magic = ES1370_MAGIC; s->io = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; s->irq = pcidev->irq; @@ -2385,6 +2445,10 @@ /* initialize the chips */ outl(s->ctrl, s->io+ES1370_REG_CONTROL); outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + /* point phantom write channel to "bugbuf" */ + outl((ES1370_REG_PHANTOM_FRAMEADR >> 8) & 15, s->io+ES1370_REG_MEMPAGE); + outl(virt_to_bus(bugbuf), s->io+(ES1370_REG_PHANTOM_FRAMEADR & 0xff)); + outl(0, s->io+(ES1370_REG_PHANTOM_FRAMECNT & 0xff)); pci_set_master(pcidev); /* enable bus mastering */ wrcodec(s, 0x16, 3); /* no RST, PD */ wrcodec(s, 0x17, 0); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off the LRCLK2 PLL; program DAC_SYNC=0!! */ diff -u --recursive --new-file v2.2.13/linux/drivers/sound/es1371.c linux/drivers/sound/es1371.c --- v2.2.13/linux/drivers/sound/es1371.c Mon Aug 9 16:05:57 1999 +++ linux/drivers/sound/es1371.c Tue Jan 4 10:12:21 2000 @@ -3,7 +3,7 @@ /* * es1371.c -- Creative Ensoniq ES1371. * - * Copyright (C) 1998 Thomas Sailer (sailer@ife.ee.ethz.ch) + * Copyright (C) 1998-1999 Thomas Sailer (sailer@ife.ee.ethz.ch) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -68,14 +68,31 @@ * 15.06.99 0.12 Fix bad allocation bug. * Thanks to Deti Fliegl * 28.06.99 0.13 Add pci_set_master - * 21.07.99 0.14 S/PDIF module option for cards revision >= 4. Initial version - * by Dave Platt . + * 03.08.99 0.14 adapt to Linus' new __setup/__initcall + * added kernel command line option "es1371=joystickaddr" + * removed CONFIG_SOUND_ES1371_JOYPORT_BOOT kludge + * 10.08.99 0.15 (Re)added S/PDIF module option for cards revision >= 4. + * Initial version by Dave Platt . + * module_init/__setup fixes + * 08.16.99 0.16 Joe Cotellese + * Added detection for ES1371 revision ID so that we can + * detect the ES1373 and later parts. + * added AC97 #defines for readability + * added a /proc file system for dumping hardware state + * updated SRC and CODEC w/r functions to accomodate bugs + * in some versions of the ES137x chips. + * 31.08.99 0.17 add spin_lock_init + * __initlocaldata to fix gcc 2.7.x problems + * replaced current->state = x with set_current_state(x) + * 03.09.99 0.18 change read semantics for MIDI to match + * OSS more closely; remove possible wakeup race + * 21.10.99 0.19 Round sampling rates, requested by + * Kasamatsu Kenichi * */ /*****************************************************************************/ -#include #include #include #include @@ -86,17 +103,21 @@ #include #include #include -#include -#include #include #include +#include +#include #include +#include +#include #include #include +#include "ac97.h" /* --------------------------------------------------------------------- */ #undef OSS_DOCUMENTED_MIXER_SEMANTICS +#undef ES1371_DEBUG /* --------------------------------------------------------------------- */ @@ -107,6 +128,18 @@ #define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371 #endif +/* ES1371 chip ID */ +/* This is a little confusing because all ES1371 compatible chips have the + same DEVICE_ID, the only thing differentiating them is the REV_ID field. + This is only significant if you want to enable features on the later parts. + Yes, I know it's stupid and why didn't we use the sub IDs? +*/ +#define ES1371REV_ES1373_A 0x04 +#define ES1371REV_ES1373_B 0x06 +#define ES1371REV_CT5880_A 0x07 +#define ES1371REV_ES1371_B 0x09 + + #define ES1371_MAGIC ((PCI_VENDOR_ID_ENSONIQ<<16)|PCI_DEVICE_ID_ENSONIQ_ES1371) #define ES1371_EXTENT 0x40 @@ -206,14 +239,22 @@ #define UCTRL_CNTRL_SWR 0x03 /* software reset command */ /* sample rate converter */ +#define SRC_OKSTATE 1 + #define SRC_RAMADDR_MASK 0xfe000000 #define SRC_RAMADDR_SHIFT 25 +#define SRC_DAC1FREEZE (1UL << 21) +#define SRC_DAC2FREEZE (1UL << 20) +#define SRC_ADCFREEZE (1UL << 19) + + #define SRC_WE 0x01000000 /* read/write control for SRC RAM */ #define SRC_BUSY 0x00800000 /* SRC busy */ #define SRC_DIS 0x00400000 /* 1 = disable SRC */ #define SRC_DDAC1 0x00200000 /* 1 = disable accum update for DAC1 */ #define SRC_DDAC2 0x00100000 /* 1 = disable accum update for DAC2 */ #define SRC_DADC 0x00080000 /* 1 = disable accum update for ADC2 */ +#define SRC_CTLMASK 0x00780000 #define SRC_RAMDATA_MASK 0x0000ffff #define SRC_RAMDATA_SHIFT 0 @@ -293,7 +334,7 @@ /* misc stuff */ - +#define POLL_COUNT 0x1000 #define FMODE_DAC 4 /* slight misuse of mode_t */ /* MIDI buffer sizes */ @@ -352,8 +393,13 @@ /* hardware resources */ unsigned long io; /* long for SPARC */ unsigned int irq; - - /* mixer registers; there is no HW readback */ + u8 rev; /* the chip revision */ + +#ifdef ES1371_DEBUG + /* debug /proc entry */ + struct proc_dir_entry *ps; +#endif /* ES1371_DEBUG */ + /* mixer registers; there is no HW readback */ struct { unsigned short codec_id; unsigned int modcnt; @@ -438,31 +484,12 @@ } /* --------------------------------------------------------------------- */ -/* - * hweightN: returns the hamming weight (i.e. the number - * of bits set) of a N-bit word - */ - -#ifdef hweight32 -#undef hweight32 -#endif - -extern __inline__ unsigned int hweight32(unsigned int w) -{ - unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); - res = (res & 0x33333333) + ((res >> 2) & 0x33333333); - res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); - res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); - return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); -} - -/* --------------------------------------------------------------------- */ static unsigned wait_src_ready(struct es1371_state *s) { unsigned int t, r; - for (t = 0; t < 1000; t++) { + for (t = 0; t < POLL_COUNT; t++) { if (!((r = inl(s->io + ES1371_REG_SRCONV)) & SRC_BUSY)) return r; udelay(1); @@ -473,29 +500,53 @@ static unsigned src_read(struct es1371_state *s, unsigned reg) { - unsigned int r; + unsigned int temp,i,orig; - r = wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC); - r |= (reg << SRC_RAMADDR_SHIFT) & SRC_RAMADDR_MASK; - outl(r, s->io + ES1371_REG_SRCONV); - return (wait_src_ready(s) & SRC_RAMDATA_MASK) >> SRC_RAMDATA_SHIFT; -} + /* wait for ready */ + temp = wait_src_ready (s); + + /* we can only access the SRC at certain times, make sure + we're allowed to before we read */ + + orig = temp; + /* expose the SRC state bits */ + outl ( (temp & SRC_CTLMASK) | (reg << SRC_RAMADDR_SHIFT) | 0x10000UL, + s->io + ES1371_REG_SRCONV); + + /* now, wait for busy and the correct time to read */ + temp = wait_src_ready (s); + + if ( (temp & 0x00870000UL ) != ( SRC_OKSTATE << 16 )){ + /* wait for the right state */ + for (i=0; iio + ES1371_REG_SRCONV); + if ( (temp & 0x00870000UL ) == ( SRC_OKSTATE << 16 )) + break; + } + } + /* hide the state bits */ + outl ((orig & SRC_CTLMASK) | (reg << SRC_RAMADDR_SHIFT), s->io + ES1371_REG_SRCONV); + return temp; + + +} static void src_write(struct es1371_state *s, unsigned reg, unsigned data) { + unsigned int r; r = wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC); r |= (reg << SRC_RAMADDR_SHIFT) & SRC_RAMADDR_MASK; r |= (data << SRC_RAMDATA_SHIFT) & SRC_RAMDATA_MASK; outl(r | SRC_WE, s->io + ES1371_REG_SRCONV); + } /* --------------------------------------------------------------------- */ /* most of the following here is black magic */ - static void set_adc_rate(struct es1371_state *s, unsigned rate) { unsigned long flags; @@ -532,6 +583,7 @@ spin_unlock_irqrestore(&s->lock, flags); } + static void set_dac1_rate(struct es1371_state *s, unsigned rate) { unsigned long flags; @@ -541,8 +593,8 @@ rate = 48000; if (rate < 4000) rate = 4000; - freq = (rate << 15) / 3000; - s->dac1rate = (freq * 3000) >> 15; + freq = ((rate << 15) + 1500) / 3000; + s->dac1rate = (freq * 3000 + 16384) >> 15; spin_lock_irqsave(&s->lock, flags); r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC2 | SRC_DADC)) | SRC_DDAC1; outl(r, s->io + ES1371_REG_SRCONV); @@ -564,8 +616,8 @@ rate = 48000; if (rate < 4000) rate = 4000; - freq = (rate << 15) / 3000; - s->dac2rate = (freq * 3000) >> 15; + freq = ((rate << 15) + 1500) / 3000; + s->dac2rate = (freq * 3000 + 16384) >> 15; spin_lock_irqsave(&s->lock, flags); r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DADC)) | SRC_DDAC2; outl(r, s->io + ES1371_REG_SRCONV); @@ -580,26 +632,80 @@ /* --------------------------------------------------------------------- */ +static void __init src_init(struct es1371_state *s) +{ + unsigned int i; + + /* before we enable or disable the SRC we need + to wait for it to become ready */ + wait_src_ready(s); + + outl(SRC_DIS, s->io + ES1371_REG_SRCONV); + + for (i = 0; i < 0x80; i++) + src_write(s, i, 0); + + src_write(s, SRCREG_DAC1+SRCREG_TRUNC_N, 16 << 4); + src_write(s, SRCREG_DAC1+SRCREG_INT_REGS, 16 << 10); + src_write(s, SRCREG_DAC2+SRCREG_TRUNC_N, 16 << 4); + src_write(s, SRCREG_DAC2+SRCREG_INT_REGS, 16 << 10); + src_write(s, SRCREG_VOL_ADC, 1 << 12); + src_write(s, SRCREG_VOL_ADC+1, 1 << 12); + src_write(s, SRCREG_VOL_DAC1, 1 << 12); + src_write(s, SRCREG_VOL_DAC1+1, 1 << 12); + src_write(s, SRCREG_VOL_DAC2, 1 << 12); + src_write(s, SRCREG_VOL_DAC2+1, 1 << 12); + set_adc_rate(s, 22050); + set_dac1_rate(s, 22050); + set_dac2_rate(s, 22050); + + /* WARNING: + * enabling the sample rate converter without properly programming + * its parameters causes the chip to lock up (the SRC busy bit will + * be stuck high, and I've found no way to rectify this other than + * power cycle) + */ + wait_src_ready(s); + outl(0, s->io+ES1371_REG_SRCONV); +} + +/* --------------------------------------------------------------------- */ + static void wrcodec(struct es1371_state *s, unsigned addr, unsigned data) { unsigned long flags; unsigned t, x; - - for (t = 0; t < 0x1000; t++) + + for (t = 0; t < POLL_COUNT; t++) if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP)) break; spin_lock_irqsave(&s->lock, flags); - /* save the current state for later */ - x = inl(s->io+ES1371_REG_SRCONV); - /* enable SRC state data in SRC mux */ - outl((wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)) | 0x00010000, + + /* save the current state for later */ + x = wait_src_ready(s); + + /* enable SRC state data in SRC mux */ + outl(( x & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)) | 0x00010000, s->io+ES1371_REG_SRCONV); - /* wait for a SAFE time to write addr/data and then do it, dammit */ - for (t = 0; t < 0x1000; t++) - if ((inl(s->io+ES1371_REG_SRCONV) & 0x00070000) == 0x00010000) - break; + + /* wait for not busy (state 0) first to avoid + transition states */ + for (t=0; tio+ES1371_REG_SRCONV) & 0x00870000) ==0 ) + break; + udelay(1); + } + + /* wait for a SAFE time to write addr/data and then do it, dammit */ + for (t=0; tio+ES1371_REG_SRCONV) & 0x00870000) ==0x00010000) + break; + udelay(1); + } + outl(((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | ((data << CODEC_PODAT_SHIFT) & CODEC_PODAT_MASK), s->io+ES1371_REG_CODEC); + /* restore SRC reg */ wait_src_ready(s); outl(x, s->io+ES1371_REG_SRCONV); @@ -611,28 +717,50 @@ unsigned long flags; unsigned t, x; + /* wait for WIP to go away */ for (t = 0; t < 0x1000; t++) if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP)) break; spin_lock_irqsave(&s->lock, flags); + /* save the current state for later */ - x = inl(s->io+ES1371_REG_SRCONV); + x = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)); + /* enable SRC state data in SRC mux */ - outl((wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)) | 0x00010000, - s->io+ES1371_REG_SRCONV); - /* wait for a SAFE time to write addr/data and then do it, dammit */ - for (t = 0; t < 0x1000; t++) - if ((inl(s->io+ES1371_REG_SRCONV) & 0x00070000) == 0x00010000) - break; + outl( x | 0x00010000, + s->io+ES1371_REG_SRCONV); + + /* wait for not busy (state 0) first to avoid + transition states */ + for (t=0; tio+ES1371_REG_SRCONV) & 0x00870000) ==0 ) + break; + udelay(1); + } + + /* wait for a SAFE time to write addr/data and then do it, dammit */ + for (t=0; tio+ES1371_REG_SRCONV) & 0x00870000) ==0x00010000) + break; + udelay(1); + } + outl(((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | CODEC_PORD, s->io+ES1371_REG_CODEC); /* restore SRC reg */ wait_src_ready(s); outl(x, s->io+ES1371_REG_SRCONV); spin_unlock_irqrestore(&s->lock, flags); - /* now wait for the stinkin' data (RDY) */ + + /* wait for WIP again */ for (t = 0; t < 0x1000; t++) + if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP)) + break; + + /* now wait for the stinkin' data (RDY) */ + for (t = 0; t < POLL_COUNT; t++) if ((x = inl(s->io+ES1371_REG_CODEC)) & CODEC_RDY) break; + return ((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT); } @@ -993,6 +1121,21 @@ /* --------------------------------------------------------------------- */ +/* + * AC97 Mixer Register to Connections mapping of the Concert 97 board + * + * AC97_MASTER_VOL_STEREO Line Out + * AC97_MASTER_VOL_MONO TAD Output + * AC97_PCBEEP_VOL none + * AC97_PHONE_VOL TAD Input (mono) + * AC97_MIC_VOL MIC Input (mono) + * AC97_LINEIN_VOL Line Input (stereo) + * AC97_CD_VOL CD Input (stereo) + * AC97_VIDEO_VOL none + * AC97_AUX_VOL Aux Input (stereo) + * AC97_PCMOUT_VOL Wave Output (stereo) + */ + #define AC97_PESSIMISTIC /* @@ -1018,25 +1161,25 @@ static const unsigned char volreg[SOUND_MIXER_NRDEVICES] = { /* 5 bit stereo */ - [SOUND_MIXER_LINE] = 0x10, - [SOUND_MIXER_CD] = 0x12, - [SOUND_MIXER_VIDEO] = 0x14, - [SOUND_MIXER_LINE1] = 0x16, - [SOUND_MIXER_PCM] = 0x18, + [SOUND_MIXER_LINE] = AC97_LINEIN_VOL, + [SOUND_MIXER_CD] = AC97_CD_VOL, + [SOUND_MIXER_VIDEO] = AC97_VIDEO_VOL, + [SOUND_MIXER_LINE1] = AC97_AUX_VOL, + [SOUND_MIXER_PCM] = AC97_PCMOUT_VOL, /* 6 bit stereo */ - [SOUND_MIXER_VOLUME] = 0x02, - [SOUND_MIXER_PHONEOUT] = 0x04, + [SOUND_MIXER_VOLUME] = AC97_MASTER_VOL_STEREO, + [SOUND_MIXER_PHONEOUT] = AC97_HEADPHONE_VOL, /* 6 bit mono */ - [SOUND_MIXER_OGAIN] = 0x06, - [SOUND_MIXER_PHONEIN] = 0x0c, + [SOUND_MIXER_OGAIN] = AC97_MASTER_VOL_MONO, + [SOUND_MIXER_PHONEIN] = AC97_PHONE_VOL, /* 4 bit mono but shifted by 1 */ - [SOUND_MIXER_SPEAKER] = 0x08, + [SOUND_MIXER_SPEAKER] = AC97_MASTER_TONE, /* 6 bit mono + preamp */ - [SOUND_MIXER_MIC] = 0x0e, + [SOUND_MIXER_MIC] = AC97_MIC_VOL, /* 4 bit stereo */ - [SOUND_MIXER_RECLEV] = 0x1c, + [SOUND_MIXER_RECLEV] = AC97_RECORD_GAIN, /* 4 bit mono */ - [SOUND_MIXER_IGAIN] = 0x1e + [SOUND_MIXER_IGAIN] = AC97_RECORD_GAIN_MIC }; #ifdef OSS_DOCUMENTED_MIXER_SEMANTICS @@ -1049,8 +1192,8 @@ switch (ch) { case SOUND_MIXER_MIC: - j = rdcodec(s, 0x0e); - if (j & 0x8000) + j = rdcodec(s, AC97_MIC_VOL); + if (j & AC97_MUTE) return put_user(0, (int *)arg); #ifdef AC97_PESSIMISTIC return put_user(0x4949 - 0x202 * (j & 0x1f) + ((j & 0x40) ? 0x1b1b : 0), (int *)arg); @@ -1061,7 +1204,7 @@ case SOUND_MIXER_OGAIN: case SOUND_MIXER_PHONEIN: j = rdcodec(s, volreg[ch]); - if (j & 0x8000) + if (j & AC97_MUTE) return put_user(0, (int *)arg); #ifdef AC97_PESSIMISTIC return put_user(0x6464 - 0x303 * (j & 0x1f), (int *)arg); @@ -1075,7 +1218,7 @@ /* fall through */ case SOUND_MIXER_VOLUME: j = rdcodec(s, volreg[ch]); - if (j & 0x8000) + if (j & AC97_MUTE) return put_user(0, (int *)arg); #ifdef AC97_PESSIMISTIC return put_user(0x6464 - (swab(j) & 0x1f1f) * 3, (int *)arg); @@ -1084,8 +1227,8 @@ #endif /* AC97_PESSIMISTIC */ case SOUND_MIXER_SPEAKER: - j = rdcodec(s, 0x0a); - if (j & 0x8000) + j = rdcodec(s, AC97_PCBEEP_VOL); + if (j & AC97_MUTE return put_user(0, (int *)arg); return put_user(0x6464 - ((j >> 1) & 0xf) * 0x606, (int *)arg); @@ -1095,7 +1238,7 @@ case SOUND_MIXER_LINE1: case SOUND_MIXER_PCM: j = rdcodec(s, volreg[ch]); - if (j & 0x8000) + if (j & AC97_MUTE) return put_user(0, (int *)arg); return put_user(0x6464 - (swab(j) & 0x1f1f) * 3, (int *)arg); @@ -1103,23 +1246,23 @@ case SOUND_MIXER_TREBLE: if (!(s->mix.codec_id & CODEC_ID_BASSTREBLE)) return -EINVAL; - j = rdcodec(s, 0x08); + j = rdcodec(s, AC97_MASTER_TONE); if (ch == SOUND_MIXER_BASS) j >>= 8; return put_user((((j & 15) * 100) / 15) * 0x101, (int *)arg); /* SOUND_MIXER_RECLEV and SOUND_MIXER_IGAIN specify gain */ case SOUND_MIXER_RECLEV: - j = rdcodec(s, 0x1c); - if (j & 0x8000) + j = rdcodec(s, AC97_RECORD_GAIN); + if (j & AC97_MUTE) return put_user(0, (int *)arg); return put_user((swab(j) & 0xf0f) * 6 + 0xa0a, (int *)arg); case SOUND_MIXER_IGAIN: if (!(s->mix.codec_id & CODEC_ID_DEDICATEDMIC)) return -EINVAL; - j = rdcodec(s, 0x1e); - if (j & 0x8000) + j = rdcodec(s, AC97_RECORD_GAIN_MIC); + if (j & AC97_MUTE) return put_user(0, (int *)arg); return put_user((j & 0xf) * 0x606 + 0xa0a, (int *)arg); @@ -1174,7 +1317,7 @@ case SOUND_MIXER_LINE1: case SOUND_MIXER_PCM: if (l1 < 7 && r1 < 7) { - wrcodec(s, volreg[ch], 0x8000); + wrcodec(s, volreg[ch], AC97_MUTE); return 0; } if (l1 < 7) @@ -1191,7 +1334,7 @@ case SOUND_MIXER_VOLUME: #ifdef AC97_PESSIMISTIC if (l1 < 7 && r1 < 7) { - wrcodec(s, volreg[ch], 0x8000); + wrcodec(s, volreg[ch], AC97_MUTE); return 0; } if (l1 < 7) @@ -1202,7 +1345,7 @@ return 0; #else /* AC97_PESSIMISTIC */ if (l1 < 4 && r1 < 4) { - wrcodec(s, volreg[ch], 0x8000); + wrcodec(s, volreg[ch], AC97_MUTE); return 0; } if (l1 < 4) @@ -1216,21 +1359,21 @@ case SOUND_MIXER_OGAIN: case SOUND_MIXER_PHONEIN: #ifdef AC97_PESSIMISTIC - wrcodec(s, volreg[ch], (l1 < 7) ? 0x8000 : (100 - l1) / 3); + wrcodec(s, volreg[ch], (l1 < 7) ? AC97_MUTE : (100 - l1) / 3); return 0; #else /* AC97_PESSIMISTIC */ - wrcodec(s, volreg[ch], (l1 < 4) ? 0x8000 : (2 * (100 - l1) / 3)); + wrcodec(s, volreg[ch], (l1 < 4) ? AC97_MUTE : (2 * (100 - l1) / 3)); return 0; #endif /* AC97_PESSIMISTIC */ case SOUND_MIXER_SPEAKER: - wrcodec(s, 0x0a, (l1 < 10) ? 0x8000 : ((100 - l1) / 6) << 1); + wrcodec(s, AC97_PCBEEP_VOL, (l1 < 10) ? AC97_MUTE : ((100 - l1) / 6) << 1); return 0; case SOUND_MIXER_MIC: #ifdef AC97_PESSIMISTIC if (l1 < 11) { - wrcodec(s, 0x0e, 0x8000); + wrcodec(s, AC97_MIC_VOL, AC97_MUTE); return 0; } i = 0; @@ -1240,11 +1383,11 @@ } if (l1 < 11) l1 = 11; - wrcodec(s, 0x0e, ((73 - l1) / 2) | i); + wrcodec(s, AC97_MIC_VOL, ((73 - l1) / 2) | i); return 0; #else /* AC97_PESSIMISTIC */ if (l1 < 9) { - wrcodec(s, 0x0e, 0x8000); + wrcodec(s, AC97_MIC_VOL, AC97_MUTE); return 0; } i = 0; @@ -1254,37 +1397,37 @@ } if (l1 < 9) l1 = 9; - wrcodec(s, 0x0e, (((87 - l1) * 4) / 5) | i); + wrcodec(s, AC97_MIC_VOL, (((87 - l1) * 4) / 5) | i); return 0; #endif /* AC97_PESSIMISTIC */ case SOUND_MIXER_BASS: val = ((l1 * 15) / 100) & 0xf; - wrcodec(s, 0x08, (rdcodec(s, 0x08) & 0x00ff) | (val << 8)); + wrcodec(s, AC97_MASTER_TONE, (rdcodec(s, AC97_MASTER_TONE) & 0x00ff) | (val << 8)); return 0; case SOUND_MIXER_TREBLE: val = ((l1 * 15) / 100) & 0xf; - wrcodec(s, 0x08, (rdcodec(s, 0x08) & 0xff00) | val); + wrcodec(s, AC97_MASTER_TONE, (rdcodec(s, AC97_MASTER_TONE) & 0xff00) | val); return 0; /* SOUND_MIXER_RECLEV and SOUND_MIXER_IGAIN specify gain */ case SOUND_MIXER_RECLEV: if (l1 < 10 || r1 < 10) { - wrcodec(s, 0x1c, 0x8000); + wrcodec(s, AC97_RECORD_GAIN, AC97_MUTE); return 0; } if (l1 < 10) l1 = 10; if (r1 < 10) r1 = 10; - wrcodec(s, 0x1c, (((l1 - 10) / 6) << 8) | ((r1 - 10) / 6)); + wrcodec(s, AC97_RECORD_GAIN, (((l1 - 10) / 6) << 8) | ((r1 - 10) / 6)); return 0; case SOUND_MIXER_IGAIN: if (!(s->mix.codec_id & CODEC_ID_DEDICATEDMIC)) return -EINVAL; - wrcodec(s, 0x1e, (l1 < 10) ? 0x8000 : ((l1 - 10) / 6) & 0xf); + wrcodec(s, AC97_RECORD_GAIN_MIC, (l1 < 10) ? AC97_MUTE : ((l1 - 10) / 6) & 0xf); return 0; default: @@ -1302,8 +1445,8 @@ return -EINVAL; get_user_ret(val, (int *)arg, -EFAULT); if (val & 1) - wrcodec(s, 0x22, ((val << 3) & 0xf00) | ((val >> 1) & 0xf)); - val = rdcodec(s, 0x22); + wrcodec(s, AC97_3D_CONTROL, ((val << 3) & 0xf00) | ((val >> 1) & 0xf)); + val = rdcodec(s, AC97_3D_CONTROL); return put_user(((val & 0xf) << 1) | ((val & 0xf00) >> 3), (int *)arg); } if (cmd == SOUND_MIXER_INFO) { @@ -1330,7 +1473,7 @@ if (_IOC_DIR(cmd) == _IOC_READ) { switch (_IOC_NR(cmd)) { case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ - return put_user(recsrc[rdcodec(s, 0x1a) & 7], (int *)arg); + return put_user(recsrc[rdcodec(s, AC97_RECORD_SELECT) & 7], (int *)arg); case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ return put_user(SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_VIDEO | @@ -1377,10 +1520,10 @@ if (i == 0) return 0; /*val = mixer_recmask(s);*/ else if (i > 1) - val &= ~recsrc[rdcodec(s, 0x1a) & 7]; + val &= ~recsrc[rdcodec(s, AC97_RECORD_SELECT) & 7]; for (i = 0; i < 8; i++) { if (val & recsrc[i]) { - wrcodec(s, 0x1a, 0x101 * i); + wrcodec(s, AC97_RECORD_SELECT, 0x101 * i); return 0; } } @@ -1485,10 +1628,10 @@ current->state = TASK_RUNNING; return -EBUSY; } - tmo = (count * HZ) / s->dac1rate; + tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2 / s->dac1rate; tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; - if (!schedule_timeout(tmo ? : 1) && tmo) - printk(KERN_DEBUG "es1371: dma timed out??\n"); + if (!schedule_timeout(tmo + 1)) + printk(KERN_DEBUG "es1371: dac1 dma timed out??\n"); } remove_wait_queue(&s->dma_dac1.wait, &wait); current->state = TASK_RUNNING; @@ -1520,10 +1663,10 @@ current->state = TASK_RUNNING; return -EBUSY; } - tmo = (count * HZ) / s->dac2rate; + tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2 / s->dac2rate; tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; - if (!schedule_timeout(tmo ? : 1) && tmo) - printk(KERN_DEBUG "es1371: dma timed out??\n"); + if (!schedule_timeout(tmo + 1)) + printk(KERN_DEBUG "es1371: dac2 dma timed out??\n"); } remove_wait_queue(&s->dma_dac2.wait, &wait); current->state = TASK_RUNNING; @@ -2467,6 +2610,7 @@ static ssize_t es1371_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct es1371_state *s = (struct es1371_state *)file->private_data; + struct wait_queue wait = { current, NULL }; ssize_t ret; unsigned long flags; unsigned ptr; @@ -2477,7 +2621,10 @@ return -ESPIPE; if (!access_ok(VERIFY_WRITE, buffer, count)) return -EFAULT; + if (count == 0) + return 0; ret = 0; + add_wait_queue(&s->midi.iwait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); ptr = s->midi.ird; @@ -2488,15 +2635,25 @@ if (cnt > count) cnt = count; if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->midi.iwait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) - return ret ? ret : -EFAULT; + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } ptr = (ptr + cnt) % MIDIINBUF; spin_lock_irqsave(&s->lock, flags); s->midi.ird = ptr; @@ -2505,13 +2662,17 @@ count -= cnt; buffer += cnt; ret += cnt; + break; } + current->state = TASK_RUNNING; + remove_wait_queue(&s->midi.iwait, &wait); return ret; } static ssize_t es1371_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct es1371_state *s = (struct es1371_state *)file->private_data; + struct wait_queue wait = { current, NULL }; ssize_t ret; unsigned long flags; unsigned ptr; @@ -2522,7 +2683,10 @@ return -ESPIPE; if (!access_ok(VERIFY_READ, buffer, count)) return -EFAULT; + if (count == 0) + return 0; ret = 0; + add_wait_queue(&s->midi.owait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); ptr = s->midi.owr; @@ -2535,15 +2699,25 @@ if (cnt > count) cnt = count; if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->midi.owait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) - return ret ? ret : -EFAULT; + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } ptr = (ptr + cnt) % MIDIOUTBUF; spin_lock_irqsave(&s->lock, flags); s->midi.owr = ptr; @@ -2556,6 +2730,8 @@ es1371_handle_midi(s); spin_unlock_irqrestore(&s->lock, flags); } + current->state = TASK_RUNNING; + remove_wait_queue(&s->midi.owait, &wait); return ret; } @@ -2697,6 +2873,44 @@ /* --------------------------------------------------------------------- */ +/* + * for debugging purposes, we'll create a proc device that dumps the + * CODEC chipstate + */ + +#ifdef ES1371_DEBUG +static int proc_es1371_dump (char *buf, char **start, off_t fpos, int length, int *eof, void *data) +{ + int len = 0; + + struct es1371_state *s = devs; + int cnt; + + /* print out header */ + len += sprintf(buf + len, "\t\tCreative ES137x Debug Dump-o-matic\n"); + + /* print out CODEC state */ + len += sprintf (buf + len, "AC97 CODEC state\n"); + + for (cnt=0; cnt <= 0x7e; cnt = cnt +2) + len+= sprintf (buf + len, "reg:0x%02x val:0x%04x\n", cnt, rdcodec(s , cnt)); + + if (fpos >=len){ + *start = buf; + *eof =1; + return 0; + } + *start = buf + fpos; + if ((len -= fpos) > length) + return length; + *eof =1; + return len; + +} +#endif /* ES1371_DEBUG */ + +/* --------------------------------------------------------------------- */ + /* maximum number of devices */ #define NR_DEVICE 5 @@ -2740,12 +2954,11 @@ struct pci_dev *pcidev = NULL; mm_segment_t fs; int i, val, val2, index = 0; - u8 revision; unsigned cssr; if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "es1371: version v0.13 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "es1371: version v0.19 time " __TIME__ " " __DATE__ "\n"); while (index < NR_DEVICE && (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1371, pcidev))) { if (pcidev->base_address[0] == 0 || @@ -2765,9 +2978,11 @@ init_waitqueue(&s->midi.iwait); init_waitqueue(&s->midi.owait); s->open_sem = MUTEX; + spin_lock_init(&s->lock); s->magic = ES1371_MAGIC; s->io = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; s->irq = pcidev->irq; + pci_read_config_byte(pcidev, PCI_REVISION_ID, &s->rev); if (check_region(s->io, ES1371_EXTENT)) { printk(KERN_ERR "es1371: io ports %#lx-%#lx in use\n", s->io, s->io+ES1371_EXTENT-1); goto err_region; @@ -2788,6 +3003,13 @@ goto err_dev3; if ((s->dev_midi = register_sound_midi(&es1371_midi_fops, -1)) < 0) goto err_dev4; +#ifdef ES1371_DEBUG + /* intialize the debug proc device */ + s->ps = create_proc_entry("es1371", S_IFREG | S_IRUGO, NULL); + if (s->ps) + s->ps->read_proc = proc_es1371_dump; +#endif /* ES1371_DEBUG */ + /* initialize codec registers */ s->ctrl = 0; if ((joystick[index] & ~0x18) == 0x200) { @@ -2800,14 +3022,13 @@ s->sctrl = 0; cssr = 0; /* check to see if s/pdif mode is being requested */ - pci_read_config_byte(pcidev, PCI_REVISION_ID, &revision); if (spdif[index]) { - if (revision >= 4) { + if (s->rev >= 4) { printk(KERN_INFO "es1371: enabling S/PDIF output\n"); cssr |= STAT_EN_SPDIF; s->ctrl |= CTRL_SPDIFEN_B; } else { - printk(KERN_ERR "es1371: revision %d does not support S/PDIF\n", revision); + printk(KERN_ERR "es1371: revision %d does not support S/PDIF\n", s->rev); } } /* initialize the chips */ @@ -2820,35 +3041,12 @@ udelay(2); outl(s->ctrl, s->io+ES1371_REG_CONTROL); /* init the sample rate converter */ - outl(SRC_DIS, s->io + ES1371_REG_SRCONV); - for (val = 0; val < 0x80; val++) - src_write(s, val, 0); - src_write(s, SRCREG_DAC1+SRCREG_TRUNC_N, 16 << 4); - src_write(s, SRCREG_DAC1+SRCREG_INT_REGS, 16 << 10); - src_write(s, SRCREG_DAC2+SRCREG_TRUNC_N, 16 << 4); - src_write(s, SRCREG_DAC2+SRCREG_INT_REGS, 16 << 10); - src_write(s, SRCREG_VOL_ADC, 1 << 12); - src_write(s, SRCREG_VOL_ADC+1, 1 << 12); - src_write(s, SRCREG_VOL_DAC1, 1 << 12); - src_write(s, SRCREG_VOL_DAC1+1, 1 << 12); - src_write(s, SRCREG_VOL_DAC2, 1 << 12); - src_write(s, SRCREG_VOL_DAC2+1, 1 << 12); - set_adc_rate(s, 22050); - set_dac1_rate(s, 22050); - set_dac2_rate(s, 22050); - /* WARNING: - * enabling the sample rate converter without properly programming - * its parameters causes the chip to lock up (the SRC busy bit will - * be stuck high, and I've found no way to rectify this other than - * power cycle) - */ - wait_src_ready(s); - outl(0, s->io+ES1371_REG_SRCONV); + src_init(s); /* codec init */ - wrcodec(s, 0x00, 0); /* reset codec */ - s->mix.codec_id = rdcodec(s, 0x00); /* get codec ID */ - val = rdcodec(s, 0x7c); - val2 = rdcodec(s, 0x7e); + wrcodec(s, AC97_RESET, 0); /* reset codec */ + s->mix.codec_id = rdcodec(s, AC97_RESET); /* get codec ID */ + val = rdcodec(s, AC97_VENDOR_ID1); + val2 = rdcodec(s, AC97_VENDOR_ID2); printk(KERN_INFO "es1371: codec vendor %c%c%c revision %d\n", (val >> 8) & 0xff, val & 0xff, (val2 >> 8) & 0xff, val2 & 0xff); printk(KERN_INFO "es1371: codec features"); @@ -2875,6 +3073,7 @@ printk("%s\n", (s->mix.codec_id & 0x3ff) ? "" : " none"); val = (s->mix.codec_id >> CODEC_ID_SESHIFT) & CODEC_ID_SEMASK; printk(KERN_INFO "es1371: stereo enhancement: %s\n", (val <= 20) ? stereo_enhancement[val] : "unknown"); + fs = get_fs(); set_fs(KERNEL_DS); val = SOUND_MASK_LINE; @@ -2929,6 +3128,10 @@ while ((s = devs)) { devs = devs->next; +#ifdef ES1371_DEBUG + if (s->ps) + remove_proc_entry("es1371", NULL); +#endif /* ES1371_DEBUG */ outl(0, s->io+ES1371_REG_CONTROL); /* switch everything off */ outl(0, s->io+ES1371_REG_SERIAL_CONTROL); /* clear serial interrupts */ synchronize_irq(); diff -u --recursive --new-file v2.2.13/linux/drivers/sound/esssolo1.c linux/drivers/sound/esssolo1.c --- v2.2.13/linux/drivers/sound/esssolo1.c Tue Jan 4 11:10:39 2000 +++ linux/drivers/sound/esssolo1.c Tue Jan 4 10:12:22 2000 @@ -54,12 +54,17 @@ * The fun part is that the Windows Solo1 driver doesn't * seem to do these tricks. * Bugs remaining: plops and clicks when starting/stopping playback + * 31.08.99 0.7 add spin_lock_init + * replaced current->state = x with set_current_state(x) + * 03.09.99 0.8 change read semantics for MIDI to match + * OSS more closely; remove possible wakeup race + * 07.10.99 0.9 Fix initialization; complain if sequencer writes time out + * Revised resource grabbing for the FM synthesizer * */ /*****************************************************************************/ -#include #include #include #include @@ -96,7 +101,7 @@ #define SOLO1_MAGIC ((PCI_VENDOR_ID_ESS<<16)|PCI_DEVICE_ID_ESS_SOLO1) -#define DDMABASE_OFFSET 0x10 /* chip bug workaround kludge */ +#define DDMABASE_OFFSET 0 /* chip bug workaround kludge */ #define DDMABASE_EXTENT 16 #define IOBASE_EXTENT 16 @@ -105,6 +110,7 @@ #define MPUBASE_EXTENT 4 #define GPBASE_EXTENT 4 +#define FMSYNTH_EXTENT 4 /* MIDI buffer sizes */ @@ -137,6 +143,8 @@ #define wait_queue_head_t struct wait_queue * #define init_waitqueue_head(w) *(w) = 0 #define init_MUTEX(m) *(m) = MUTEX +#define __set_current_state(x) do { current->state = (x); } while (0) +#define set_current_state(x) __set_current_state(x) #endif /* --------------------------------------------------------------------- */ @@ -226,7 +234,7 @@ { int i; unsigned long flags; - + /* the __cli stunt is to send the data within the command window */ for (i = 0; i < 0xffff; i++) { __save_flags(flags); @@ -238,6 +246,8 @@ } __restore_flags(flags); } + printk(KERN_ERR "esssolo1: write_seq timeout\n"); + outb(data, s->sbbase+0xc); } extern inline int read_seq(struct solo1_state *s, unsigned char *data) @@ -251,6 +261,7 @@ *data = inb(s->sbbase+0xa); return 1; } + printk(KERN_ERR "esssolo1: read_seq timeout\n"); return 0; } @@ -952,7 +963,7 @@ if (s->dma_dac.mapped) return 0; - current->state = TASK_INTERRUPTIBLE; + __set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&s->dma_dac.wait, &wait); for (;;) { spin_lock_irqsave(&s->lock, flags); @@ -964,7 +975,7 @@ break; if (nonblock) { remove_wait_queue(&s->dma_dac.wait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); return -EBUSY; } tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->rate; @@ -976,7 +987,7 @@ printk(KERN_DEBUG "solo1: dma timed out??\n"); } remove_wait_queue(&s->dma_dac.wait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; return 0; @@ -1622,6 +1633,7 @@ static ssize_t solo1_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct solo1_state *s = (struct solo1_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); ssize_t ret; unsigned long flags; unsigned ptr; @@ -1632,7 +1644,10 @@ return -ESPIPE; if (!access_ok(VERIFY_WRITE, buffer, count)) return -EFAULT; + if (count == 0) + return 0; ret = 0; + add_wait_queue(&s->midi.iwait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); ptr = s->midi.ird; @@ -1643,15 +1658,25 @@ if (cnt > count) cnt = count; if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->midi.iwait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + __set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) - return ret ? ret : -EFAULT; + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } ptr = (ptr + cnt) % MIDIINBUF; spin_lock_irqsave(&s->lock, flags); s->midi.ird = ptr; @@ -1660,13 +1685,17 @@ count -= cnt; buffer += cnt; ret += cnt; + break; } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.iwait, &wait); return ret; } static ssize_t solo1_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct solo1_state *s = (struct solo1_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); ssize_t ret; unsigned long flags; unsigned ptr; @@ -1677,7 +1706,10 @@ return -ESPIPE; if (!access_ok(VERIFY_READ, buffer, count)) return -EFAULT; + if (count == 0) + return 0; ret = 0; + add_wait_queue(&s->midi.owait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); ptr = s->midi.owr; @@ -1690,15 +1722,25 @@ if (cnt > count) cnt = count; if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->midi.owait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + __set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) - return ret ? ret : -EFAULT; + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } ptr = (ptr + cnt) % MIDIOUTBUF; spin_lock_irqsave(&s->lock, flags); s->midi.owr = ptr; @@ -1711,6 +1753,8 @@ solo1_handle_midi(s); spin_unlock_irqrestore(&s->lock, flags); } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.owait, &wait); return ret; } @@ -1979,6 +2023,12 @@ return -ERESTARTSYS; down(&s->open_sem); } + if (check_region(s->sbbase, FMSYNTH_EXTENT)) { + up(&s->open_sem); + printk(KERN_ERR "solo1: FM synth io ports in use, opl3 loaded?\n"); + return -EBUSY; + } + request_region(s->sbbase, FMSYNTH_EXTENT, "ESS Solo1"); /* init the stuff */ outb(1, s->sbbase); outb(0x20, s->sbbase+1); /* enable waveforms */ @@ -2006,6 +2056,7 @@ outb(regb, s->sbbase+2); outb(0, s->sbbase+3); } + release_region(s->sbbase, FMSYNTH_EXTENT); up(&s->open_sem); wake_up(&s->open_wait); MOD_DEC_USE_COUNT; @@ -2062,7 +2113,7 @@ if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "solo1: version v0.6 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "solo1: version v0.9 time " __TIME__ " " __DATE__ "\n"); while (index < NR_DEVICE && (pcidev = pci_find_device(PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_SOLO1, pcidev))) { if (pcidev->base_address[0] == 0 || @@ -2087,6 +2138,7 @@ init_waitqueue_head(&s->midi.iwait); init_waitqueue_head(&s->midi.owait); init_MUTEX(&s->open_sem); + spin_lock_init(&s->lock); s->magic = SOLO1_MAGIC; s->pcidev = pcidev; s->iobase = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; @@ -2097,14 +2149,14 @@ s->gpbase = pcidev->base_address[4] & PCI_BASE_ADDRESS_IO_MASK; s->irq = pcidev->irq; if (check_region(s->iobase, IOBASE_EXTENT) || - check_region(s->sbbase+4, SBBASE_EXTENT-4) || + check_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT) || check_region(s->ddmabase, DDMABASE_EXTENT) || check_region(s->mpubase, MPUBASE_EXTENT)) { printk(KERN_ERR "solo1: io ports in use\n"); goto err_region; } request_region(s->iobase, IOBASE_EXTENT, "ESS Solo1"); - request_region(s->sbbase+4, SBBASE_EXTENT-4, "ESS Solo1"); /* allow OPL3 synth module */ + request_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT, "ESS Solo1"); /* allow OPL3 synth module */ request_region(s->ddmabase, DDMABASE_EXTENT, "ESS Solo1"); request_region(s->mpubase, MPUBASE_EXTENT, "ESS Solo1"); if (request_irq(s->irq, solo1_interrupt, SA_SHIRQ, "ESS Solo1", s)) { @@ -2131,6 +2183,10 @@ if ((s->dev_dmfm = register_sound_special(&solo1_dmfm_fops, 15 /* ?? */)) < 0) goto err_dev4; /* initialize the chips */ + if (!reset_ctrl(s)) { + printk(KERN_ERR "esssolo1: cannot reset controller\n"); + goto err; + } outb(0xb0, s->iobase+7); /* enable A1, A2, MPU irq's */ /* initialize mixer regs */ @@ -2165,6 +2221,8 @@ index++; continue; + err: + unregister_sound_dsp(s->dev_dmfm); err_dev4: unregister_sound_dsp(s->dev_midi); err_dev3: @@ -2176,7 +2234,7 @@ free_irq(s->irq, s); err_irq: release_region(s->iobase, IOBASE_EXTENT); - release_region(s->sbbase+4, SBBASE_EXTENT-4); + release_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT); release_region(s->ddmabase, DDMABASE_EXTENT); release_region(s->mpubase, MPUBASE_EXTENT); err_region: @@ -2206,7 +2264,7 @@ pci_write_config_word(s->pcidev, 0x60, 0); /* turn off DDMA controller address space */ free_irq(s->irq, s); release_region(s->iobase, IOBASE_EXTENT); - release_region(s->sbbase+4, SBBASE_EXTENT-4); + release_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT); release_region(s->ddmabase, DDMABASE_EXTENT); release_region(s->mpubase, MPUBASE_EXTENT); unregister_sound_dsp(s->dev_audio); @@ -2222,5 +2280,4 @@ module_init(init_solo1); module_exit(cleanup_solo1); - diff -u --recursive --new-file v2.2.13/linux/drivers/sound/lowlevel/Config.in linux/drivers/sound/lowlevel/Config.in --- v2.2.13/linux/drivers/sound/lowlevel/Config.in Sun Dec 27 10:53:45 1998 +++ linux/drivers/sound/lowlevel/Config.in Tue Jan 4 10:12:22 2000 @@ -1,4 +1,5 @@ dep_tristate 'ACI mixer (miroPCM12)' CONFIG_ACI_MIXER $CONFIG_SOUND_OSS +dep_tristate 'MSP3400 Audio for BT848' CONFIG_VIDEO_MSP3400 $CONFIG_VIDEO_BT848 $CONFIG_SOUND dep_tristate 'AWE32 synth' CONFIG_AWE32_SYNTH $CONFIG_SOUND_OSS diff -u --recursive --new-file v2.2.13/linux/drivers/sound/lowlevel/miroaci.h linux/drivers/sound/lowlevel/miroaci.h --- v2.2.13/linux/drivers/sound/lowlevel/miroaci.h Sun Aug 23 13:32:25 1998 +++ linux/drivers/sound/lowlevel/miroaci.h Tue Jan 4 10:12:22 2000 @@ -1,6 +1,11 @@ +#if defined(CONFIG_ACI_MIXER) || (defined(CONFIG_ACI_MIXER_MODULE) && defined(MODULE)) extern int aci_implied_cmd(unsigned char opcode); extern int aci_write_cmd(unsigned char opcode, unsigned char parameter); extern int aci_write_cmd_d(unsigned char opcode, unsigned char parameter, unsigned char parameter2); extern int aci_read_cmd(unsigned char opcode, int length, unsigned char *parameter); extern int aci_indexed_cmd(unsigned char opcode, unsigned char index, unsigned char *parameter); +#else +#error Compiling a driver that needs the ACI-mixer but without ACI-mixer support + +#endif diff -u --recursive --new-file v2.2.13/linux/drivers/sound/maestro.c linux/drivers/sound/maestro.c --- v2.2.13/linux/drivers/sound/maestro.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/sound/maestro.c Tue Jan 4 10:12:22 2000 @@ -0,0 +1,3556 @@ +/***************************************************************************** + * + * ESS Maestro/Maestro-2/Maestro-2E driver for Linux 2.[23].x + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * (c) Copyright 1999 Alan Cox + * + * Based heavily on SonicVibes.c: + * Copyright (C) 1998-1999 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * Heavily modified by Zach Brown based on lunch + * with ESS engineers. Many thanks to Howard Kim for providing + * contacts and hardware. Honorable mention goes to Eric + * Brombaugh for all sorts of things. Best regards to the + * proprietors of Hack Central for fine lodging. + * + * Supported devices: + * /dev/dsp0-3 standard /dev/dsp device, (mostly) OSS compatible + * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible + * + * Hardware Description + * + * A working Maestro setup contains the Maestro chip wired to a + * codec or 2. In the Maestro we have the APUs, the ASSP, and the + * Wavecache. The APUs can be though of as virtual audio routing + * channels. They can take data from a number of sources and perform + * basic encodings of the data. The wavecache is a storehouse for + * PCM data. Typically it deals with PCI and interracts with the + * APUs. The ASSP is a wacky DSP like device that ESS is loth + * to release docs on. Thankfully it isn't required on the Maestro + * until you start doing insane things like FM emulation and surround + * encoding. The codecs are almost always AC-97 compliant codecs, + * but it appears that early Maestros may have had PT101 (an ESS + * part?) wired to them. The only real difference in the Maestro + * families is external goop like docking capability, memory for + * the ASSP, and initialization differences. + * + * Driver Operation + * + * We only drive the APU/Wavecache as typical DACs and drive the + * mixers in the codecs. There are 64 APUs. We assign 6 to each + * /dev/dsp? device. 2 channels for output, and 4 channels for + * input. + * + * Each APU can do a number of things, but we only really use + * 3 basic functions. For playback we use them to convert PCM + * data fetched over PCI by the wavecahche into analog data that + * is handed to the codec. One APU for mono, and a pair for stereo. + * When in stereo, the combination of smarts in the APU and Wavecache + * decide which wavecache gets the left or right channel. + * + * For record we still use the old overly mono system. For each in + * coming channel the data comes in from the codec, through a 'input' + * APU, through another rate converter APU, and then into memory via + * the wavecache and PCI. If its stereo, we mash it back into LRLR in + * software. The pass between the 2 APUs is supposedly what requires us + * to have a 512 byte buffer sitting around in wavecache/memory. + * + * The wavecache makes our life even more fun. First off, it can + * only address the first 28 bits of PCI address space, making it + * useless on quite a few architectures. Secondly, its insane. + * It claims to fetch from 4 regions of PCI space, each 4 meg in length. + * But that doesn't really work. You can only use 1 region. So all our + * allocations have to be in 4meg of each other. Booo. Hiss. + * So we have a module parameter, dsps_order, that is the order of + * the number of dsps to provide. All their buffer space is allocated + * on open time. The sonicvibes OSS routines we inherited really want + * power of 2 buffers, so we have all those next to each other, then + * 512 byte regions for the recording wavecaches. This ends up + * wasting quite a bit of memory. The only fixes I can see would be + * getting a kernel allocator that could work in zones, or figuring out + * just how to coerce the WP into doing what we want. + * + * The indirection of the various registers means we have to spinlock + * nearly all register accesses. We have the main register indirection + * like the wave cache, maestro registers, etc. Then we have beasts + * like the APU interface that is indirect registers gotten at through + * the main maestro indirection. Ouch. We spinlock around the actual + * ports on a per card basis. This means spinlock activity at each IO + * operation, but the only IO operation clusters are in non critical + * paths and it makes the code far easier to follow. Interrupts are + * blocked while holding the locks because the int handler has to + * get at some of them :(. The mixer interface doesn't, however. + * We also have an OSS state lock that is thrown around in a few + * places. + * + * This driver has brute force APM suspend support. We catch suspend + * notifications and stop all work being done on the chip. Any people + * that try between this shutdown and the real suspend operation will + * be put to sleep. When we resume we restore our software state on + * the chip and wake up the people that were using it. The code thats + * being used now is quite dirty and assumes we're on a uni-processor + * machine. Much of it will need to be cleaned up for SMP ACPI or + * similar. + * + * History + * v0.13 - Nov 18 1999 - Zach Brown + * fix nec Versas? man would that be cool. + * v0.12 - Nov 12 1999 - Zach Brown + * brown bag volume max fix.. + * v0.11 - Nov 11 1999 - Zach Brown + * use proper stereo apu decoding, mmap/write should work. + * make volume sliders more useful, tweak rate calculation. + * fix lame 8bit format reporting bug. duh. apm apu saving buglet also + * fix maestro 1 clock freq "bug", remove pt101 support + * v0.10 - Oct 28 1999 - Zach Brown + * aha, so, sometimes the WP writes a status word to offset 0 + * from one of the PCMBARs. rearrange allocation accordingly.. + * cheers again to Eric for being a good hacker in investigating this. + * Jeroen Hoogervorst submits 7500 fix out of nowhere. yay. :) + * v0.09 - Oct 23 1999 - Zach Brown + * added APM support. + * re-order something such that some 2Es now work. Magic! + * new codec reset routine. made some codecs come to life. + * fix clear_advance, sync some control with ESS. + * now write to all base regs to be paranoid. + * v0.08 - Oct 20 1999 - Zach Brown + * Fix initial buflen bug. I am so smart. also smp compiling.. + * I owe Eric yet another beer: fixed recmask, igain, + * muting, and adc sync consistency. Go Team. + * v0.07 - Oct 4 1999 - Zach Brown + * tweak adc/dac, formating, and stuff to allow full duplex + * allocate dsps memory at open() so we can fit in the wavecache window + * fix wavecache braindamage. again. no more scribbling? + * fix ess 1921 codec bug on some laptops. + * fix dumb pci scanning bug + * started 2.3 cleanup, redid spinlocks, little cleanups + * v0.06 - Sep 20 1999 - Zach Brown + * fix wavecache thinkos. limit to 1 /dev/dsp. + * eric is wearing his thinking toque this week. + * spotted apu mode bugs and gain ramping problem + * don't touch weird mixer regs, make recmask optional + * fixed igain inversion, defaults for mixers, clean up rec_start + * make mono recording work. + * report subsystem stuff, please send reports. + * littles: parallel out, amp now + * v0.05 - Sep 17 1999 - Zach Brown + * merged and fixed up Eric's initial recording code + * munged format handling to catch misuse, needs rewrite. + * revert ring bus init, fixup shared int, add pci busmaster setting + * fix mixer oss interface, fix mic mute and recmask + * mask off unsupported mixers, reset with all 1s, modularize defaults + * make sure bob is running while we need it + * got rid of device limit, initial minimal apm hooks + * pull out dead code/includes, only allow multimedia/audio maestros + * v0.04 - Sep 01 1999 - Zach Brown + * copied memory leak fix from sonicvibes driver + * different ac97 reset, play with 2.0 ac97, simplify ring bus setup + * bob freq code, region sanity, jitter sync fix; all from Eric + * + * TODO + * some people get indir reg timeouts? + * fix bob frequency + * endianness + * do smart things with ac97 2.0 bits. + * docking and dual codecs and 978? + * leave 54->61 open + * + * it also would be fun to have a mode that would not use pci dma at all + * but would copy into the wavecache on board memory and use that + * on architectures that don't like the maestro's pci dma ickiness. + */ + +/*****************************************************************************/ + +#include +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) + + #ifdef MODULE + #ifdef MODVERSIONS + #include + #endif + #endif + #define DECLARE_WAITQUEUE(QUEUE,INIT) struct wait_queue QUEUE = {INIT, NULL} + #define wait_queue_head_t struct wait_queue * + #define SILLY_PCI_BASE_ADDRESS(PCIDEV) (PCIDEV->base_address[0] & PCI_BASE_ADDRESS_IO_MASK) + #define SILLY_INIT_SEM(SEM) SEM=MUTEX; + #define init_waitqueue_head init_waitqueue + #define SILLY_MAKE_INIT(FUNC) __initfunc(FUNC) + +#else + + #define SILLY_PCI_BASE_ADDRESS(PCIDEV) (PCIDEV->resource[0].start) + #define SILLY_INIT_SEM(SEM) init_MUTEX(&SEM) + #define SILLY_MAKE_INIT(FUNC) __init FUNC + +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_APM +#include +static int maestro_apm_callback(apm_event_t ae); +static int in_suspend=0; +wait_queue_head_t suspend_queue; +static void check_suspend(void); +#define CHECK_SUSPEND check_suspend(); +#else +#define CHECK_SUSPEND +#endif + +#include "maestro.h" + +/* --------------------------------------------------------------------- */ + +#define M_DEBUG 1 + +#ifdef M_DEBUG +static int debug=0; +static int dsps_order=0; +#define M_printk(args...) {if (debug) printk(args);} +#else +#define M_printk(x) +#endif + +/* --------------------------------------------------------------------- */ +#define DRIVER_VERSION "0.13" + +#ifndef PCI_VENDOR_ESS +#define PCI_VENDOR_ESS 0x125D +#define PCI_DEVICE_ID_ESS_ESS1968 0x1968 /* Maestro 2 */ +#define PCI_DEVICE_ID_ESS_ESS1978 0x1978 /* Maestro 2E */ + +#define PCI_VENDOR_ESS_OLD 0x1285 /* Platform Tech, + the people the maestro + was bought from */ +#define PCI_DEVICE_ID_ESS_ESS0100 0x0100 /* maestro 1 */ +#endif /* PCI_VENDOR_ESS */ + +#define ESS_CHAN_HARD 0x100 + +/* NEC Versas ? */ +#define NEC_VERSA_SUBID1 0x80581033 +#define NEC_VERSA_SUBID2 0x803c1033 + + +/* changed so that I could actually find all the + references and fix them up. its a little more readable now. */ +#define ESS_FMT_STEREO 0x01 +#define ESS_FMT_16BIT 0x02 +#define ESS_FMT_MASK 0x03 +#define ESS_DAC_SHIFT 0 +#define ESS_ADC_SHIFT 4 + +#define ESS_STATE_MAGIC 0x125D1968 +#define ESS_CARD_MAGIC 0x19283746 + +#define DAC_RUNNING 1 +#define ADC_RUNNING 2 + +#define MAX_DSP_ORDER 2 +#define MAX_DSPS (1<src buffer page */ + void *mixbuf; +}; + +struct ess_card { + unsigned int magic; + + /* We keep maestro cards in a linked list */ + struct ess_card *next; + + int dev_mixer; + + int card_type; + + /* as most of this is static, + perhaps it should be a pointer to a global struct */ + struct mixer_goo { + int modcnt; + int supported_mixers; + int stereo_mixers; + int record_sources; + /* the caller must guarantee arg sanity before calling these */ +/* int (*read_mixer)(struct ess_card *card, int index);*/ + void (*write_mixer)(struct ess_card *card,int mixer, unsigned int left,unsigned int right); + int (*recmask_io)(struct ess_card *card,int rw,int mask); + unsigned int mixer_state[SOUND_MIXER_NRDEVICES]; + } mix; + + struct ess_state channels[MAX_DSPS]; + u16 maestro_map[NR_IDRS]; /* Register map */ +#ifdef CONFIG_APM + /* we have to store this junk so that we can come back from a + suspend */ + u16 apu_map[NR_APUS][NR_APU_REGS]; /* contents of apu regs */ +#endif + + /* this locks around the physical registers on the card */ + spinlock_t lock; + + /* memory for this card.. wavecache limited :(*/ + void *dmapages; + int dmaorder; + + /* hardware resources */ + struct pci_dev pcidev; /* uck.. */ + u32 iobase; + u32 irq; + + int bob_freq; + char dsps_open; +}; + +static unsigned +ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + + +/* --------------------------------------------------------------------- */ + +static struct ess_card *devs = NULL; + +/* --------------------------------------------------------------------- */ + + +/* + * ESS Maestro AC97 codec programming interface. + */ + +static void maestro_ac97_set(int io, u8 cmd, u16 val) +{ + int i; + /* + * Wait for the codec bus to be free + */ + + CHECK_SUSPEND; + + for(i=0;i<10000;i++) + { + if(!(inb(io+ESS_AC97_INDEX)&1)) + break; + } + /* + * Write the bus + */ + outw(val, io+ESS_AC97_DATA); + mdelay(1); + outb(cmd, io+ESS_AC97_INDEX); + mdelay(1); +} + +static u16 maestro_ac97_get(int io, u8 cmd) +{ + int sanity=10000; + u16 data; + int i; + + CHECK_SUSPEND; + /* + * Wait for the codec bus to be free + */ + + for(i=0;i<10000;i++) + { + if(!(inb(io+ESS_AC97_INDEX)&1)) + break; + } + + outb(cmd|0x80, io+ESS_AC97_INDEX); + mdelay(1); + + while(inb(io+ESS_AC97_INDEX)&1) + { + sanity--; + if(!sanity) + { + printk(KERN_ERR "maestro: ac97 codec timeout reading 0x%x.\n",cmd); + return 0; + } + } + data=inw(io+ESS_AC97_DATA); + mdelay(1); + return data; +} + +/* OSS interface to the ac97s.. */ + +#define AC97_STEREO_MASK (SOUND_MASK_VOLUME|\ + SOUND_MASK_PCM|SOUND_MASK_LINE|SOUND_MASK_CD|\ + SOUND_MASK_VIDEO|SOUND_MASK_LINE1|SOUND_MASK_IGAIN) + +#define AC97_SUPPORTED_MASK (AC97_STEREO_MASK | \ + SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_MIC|\ + SOUND_MASK_SPEAKER) + +#define AC97_RECORD_MASK (SOUND_MASK_MIC|\ + SOUND_MASK_CD| SOUND_MASK_VIDEO| SOUND_MASK_LINE1| SOUND_MASK_LINE|\ + SOUND_MASK_PHONEIN) + +#define supported_mixer(CARD,FOO) ( CARD->mix.supported_mixers & (1<iobase , mh->offset); + + if(AC97_STEREO_MASK & (1<> 8) & 0x7f; + right = val & 0x7f; + + if (mixer == SOUND_MIXER_IGAIN) { + right = (right * 100) / mh->scale; + left = (left * 100) / mh->scale; + else { + right = 100 - ((right * 100) / mh->scale); + left = 100 - ((left * 100) / mh->scale); + } + + ret = left | (right << 8); + } else if (mixer == SOUND_MIXER_SPEAKER) { + ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale); + } else if (mixer == SOUND_MIXER_MIC) { + ret = 100 - (((val & 0x1f) * 100) / mh->scale); + /* the low bit is optional in the tone sliders and masking + it lets is avoid the 0xf 'bypass'.. */ + } else if (mixer == SOUND_MIXER_BASS) { + ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale); + } else if (mixer == SOUND_MIXER_TREBLE) { + ret = 100 - (((val & 0xe) * 100) / mh->scale); + } + + M_printk("read mixer %d (0x%x) %x -> %x\n",mixer,mh->offset,val,ret); + + return ret; +} +#endif + +/* write the OSS encoded volume to the given OSS encoded mixer, + again caller's job to make sure all is well in arg land, + call with spinlock held */ + +/* linear scale -> log */ +unsigned char lin2log[101] = +{ +0, 0 , 15 , 23 , 30 , 34 , 38 , 42 , 45 , 47 , +50 , 52 , 53 , 55 , 57 , 58 , 60 , 61 , 62 , +63 , 65 , 66 , 67 , 68 , 69 , 69 , 70 , 71 , +72 , 73 , 73 , 74 , 75 , 75 , 76 , 77 , 77 , +78 , 78 , 79 , 80 , 80 , 81 , 81 , 82 , 82 , +83 , 83 , 84 , 84 , 84 , 85 , 85 , 86 , 86 , +87 , 87 , 87 , 88 , 88 , 88 , 89 , 89 , 89 , +90 , 90 , 90 , 91 , 91 , 91 , 92 , 92 , 92 , +93 , 93 , 93 , 94 , 94 , 94 , 94 , 95 , 95 , +95 , 95 , 96 , 96 , 96 , 96 , 97 , 97 , 97 , +97 , 98 , 98 , 98 , 98 , 99 , 99 , 99 , 99 , 99 +}; + +static void ac97_write_mixer(struct ess_card *card,int mixer, unsigned int left, unsigned int right) +{ + u16 val=0; + struct ac97_mixer_hw *mh = &ac97_hw[mixer]; + + M_printk("wrote mixer %d (0x%x) %d,%d",mixer,mh->offset,left,right); + + if(AC97_STEREO_MASK & (1<scale) / 100; + left = (left * mh->scale) / 100; + if ((left == 0) && (right == 0)) + val |= 0x8000; + } else { + /* log conversion for the stereo controls */ + if((left == 0) && (right == 0)) + val = 0x8000; + right = ((100 - lin2log[right]) * mh->scale) / 100; + left = ((100 - lin2log[left]) * mh->scale) / 100; + } + + val |= (left << 8) | right; + + } else if (mixer == SOUND_MIXER_SPEAKER) { + val = (((100 - left) * mh->scale) / 100) << 1; + } else if (mixer == SOUND_MIXER_MIC) { + val = maestro_ac97_get(card->iobase , mh->offset) & ~0x801f; + val |= (((100 - left) * mh->scale) / 100); + /* the low bit is optional in the tone sliders and masking + it lets is avoid the 0xf 'bypass'.. */ + } else if (mixer == SOUND_MIXER_BASS) { + val = maestro_ac97_get(card->iobase , mh->offset) & ~0x0f00; + val |= ((((100 - left) * mh->scale) / 100) << 8) & 0x0e00; + } else if (mixer == SOUND_MIXER_TREBLE) { + val = maestro_ac97_get(card->iobase , mh->offset) & ~0x000f; + val |= (((100 - left) * mh->scale) / 100) & 0x000e; + } + + maestro_ac97_set(card->iobase , mh->offset, val); + + M_printk(" -> %x\n",val); +} + +/* the following tables allow us to go from + OSS <-> ac97 quickly. */ + +enum ac97_recsettings { + AC97_REC_MIC=0, + AC97_REC_CD, + AC97_REC_VIDEO, + AC97_REC_AUX, + AC97_REC_LINE, + AC97_REC_STEREO, /* combination of all enabled outputs.. */ + AC97_REC_MONO, /*.. or the mono equivalent */ + AC97_REC_PHONE +}; + +static unsigned int ac97_oss_mask[] = { + [AC97_REC_MIC] = SOUND_MASK_MIC, + [AC97_REC_CD] = SOUND_MASK_CD, + [AC97_REC_VIDEO] = SOUND_MASK_VIDEO, + [AC97_REC_AUX] = SOUND_MASK_LINE1, + [AC97_REC_LINE] = SOUND_MASK_LINE, + [AC97_REC_PHONE] = SOUND_MASK_PHONEIN +}; + +/* indexed by bit position */ +static unsigned int ac97_oss_rm[] = { + [SOUND_MIXER_MIC] = AC97_REC_MIC, + [SOUND_MIXER_CD] = AC97_REC_CD, + [SOUND_MIXER_VIDEO] = AC97_REC_VIDEO, + [SOUND_MIXER_LINE1] = AC97_REC_AUX, + [SOUND_MIXER_LINE] = AC97_REC_LINE, + [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE +}; + +/* read or write the recmask + the ac97 can really have left and right recording + inputs independantly set, but OSS doesn't seem to + want us to express that to the user. + the caller guarantees that we have a supported bit set, + and they must be holding the card's spinlock */ +static int +ac97_recmask_io(struct ess_card *card, int read, int mask) +{ + unsigned int val = ac97_oss_mask[ maestro_ac97_get(card->iobase, 0x1a) & 0x7 ]; + + if (read) return val; + + /* oss can have many inputs, maestro cant. try + to pick the 'new' one */ + + if (mask != val) mask &= ~val; + + val = ffs(mask) - 1; + val = ac97_oss_rm[val]; + val |= val << 8; /* set both channels */ + + M_printk("maestro: setting ac97 recmask to 0x%x\n",val); + + maestro_ac97_set(card->iobase,0x1a,val); + + return 0; +}; + +/* + * The Maestro can be wired to a standard AC97 compliant codec + * (see www.intel.com for the pdf's on this), or to a PT101 codec + * which appears to be the ES1918 (data sheet on the esstech.com.tw site) + * + * The PT101 setup is untested. + */ + +static u16 maestro_ac97_init(struct ess_card *card, int iobase) +{ + u16 vend1, vend2, caps; + + card->mix.supported_mixers = AC97_SUPPORTED_MASK; + card->mix.stereo_mixers = AC97_STEREO_MASK; + card->mix.record_sources = AC97_RECORD_MASK; +/* card->mix.read_mixer = ac97_read_mixer;*/ + card->mix.write_mixer = ac97_write_mixer; + card->mix.recmask_io = ac97_recmask_io; + + vend1 = maestro_ac97_get(iobase, 0x7c); + vend2 = maestro_ac97_get(iobase, 0x7e); + + caps = maestro_ac97_get(iobase, 0x00); + + printk(KERN_INFO "maestro: AC97 Codec detected: v: 0x%2x%2x caps: 0x%x pwr: 0x%x\n", + vend1,vend2,caps,maestro_ac97_get(iobase,0x26) & 0xf); + + if (! (caps & 0x4) ) { + /* no bass/treble nobs */ + card->mix.supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE); + } + + /* XXX endianness, dork head. */ + /* vendor specifc bits.. */ + switch ((long)(vend1 << 16) | vend2) { + case 0x545200ff: /* TriTech */ + /* no idea what this does */ + maestro_ac97_set(iobase,0x2a,0x0001); + maestro_ac97_set(iobase,0x2c,0x0000); + maestro_ac97_set(iobase,0x2c,0xffff); + break; + case 0x83847609: /* ESS 1921 */ + /* writing to 0xe (mic) or 0x1a (recmask) seems + to hang this codec */ + card->mix.supported_mixers &= ~(SOUND_MASK_MIC); + card->mix.record_sources = 0; + card->mix.recmask_io = NULL; +#if 0 /* don't ask. I have yet to see what these actually do. */ + maestro_ac97_set(iobase,0x76,0xABBA); /* o/~ Take a chance on me o/~ */ + udelay(20); + maestro_ac97_set(iobase,0x78,0x3002); + udelay(20); + maestro_ac97_set(iobase,0x78,0x3802); + udelay(20); +#endif + break; + default: break; + } + + maestro_ac97_set(iobase, 0x1E, 0x0404); + /* null misc stuff */ + maestro_ac97_set(iobase, 0x20, 0x0000); + + return 0; +} + +#if 0 /* there has been 1 person on the planet with a pt101 that we + know of. If they care, they can put this back in :) */ +static u16 maestro_pt101_init(struct ess_card *card,int iobase) +{ + printk(KERN_INFO "maestro: PT101 Codec detected, initializing but _not_ installing mixer device.\n"); + /* who knows.. */ + maestro_ac97_set(iobase, 0x2A, 0x0001); + maestro_ac97_set(iobase, 0x2C, 0x0000); + maestro_ac97_set(iobase, 0x2C, 0xFFFF); + maestro_ac97_set(iobase, 0x10, 0x9F1F); + maestro_ac97_set(iobase, 0x12, 0x0808); + maestro_ac97_set(iobase, 0x14, 0x9F1F); + maestro_ac97_set(iobase, 0x16, 0x9F1F); + maestro_ac97_set(iobase, 0x18, 0x0404); + maestro_ac97_set(iobase, 0x1A, 0x0000); + maestro_ac97_set(iobase, 0x1C, 0x0000); + maestro_ac97_set(iobase, 0x02, 0x0404); + maestro_ac97_set(iobase, 0x04, 0x0808); + maestro_ac97_set(iobase, 0x0C, 0x801F); + maestro_ac97_set(iobase, 0x0E, 0x801F); + return 0; +} +#endif + +/* this is very magic, and very slow.. */ +static void +maestro_ac97_reset(int ioaddr, struct pci_dev *pcidev) +{ + u16 save_68; + u16 w; + u32 vend; + + outw( inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); + outw( inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); + outw( inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); + + /* reset the first codec */ + outw(0x0000, ioaddr+0x36); + save_68 = inw(ioaddr+0x68); + pci_read_config_word(pcidev, 0x58, &w); /* something magical with gpio and bus arb. */ + pci_read_config_dword(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &vend); + if( w & 0x1) + save_68 |= 0x10; + outw(0xfffe, ioaddr + 0x64); /* tickly gpio 0.. */ + outw(0x0001, ioaddr + 0x68); + outw(0x0000, ioaddr + 0x60); + udelay(20); + outw(0x0001, ioaddr + 0x60); + mdelay(20); + + outw(save_68 | 0x1, ioaddr + 0x68); /* now restore .. */ + outw( (inw(ioaddr + 0x38) & 0xfffc)|0x1, ioaddr + 0x38); + outw( (inw(ioaddr + 0x3a) & 0xfffc)|0x1, ioaddr + 0x3a); + outw( (inw(ioaddr + 0x3c) & 0xfffc)|0x1, ioaddr + 0x3c); + + /* now the second codec */ + outw(0x0000, ioaddr+0x36); + outw(0xfff7, ioaddr + 0x64); + save_68 = inw(ioaddr+0x68); + outw(0x0009, ioaddr + 0x68); + outw(0x0001, ioaddr + 0x60); + udelay(20); + outw(0x0009, ioaddr + 0x60); + mdelay(500); /* .. ouch.. */ + outw( inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); + outw( inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); + outw( inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); + +#if 0 /* the loop here needs to be much better if we want it.. */ + M_printk("trying software reset\n"); + /* try and do a software reset */ + outb(0x80|0x7c, ioaddr + 0x30); + for (w=0; ; w++) { + if ((inw(ioaddr+ 0x30) & 1) == 0) { + if(inb(ioaddr + 0x32) !=0) break; + + outb(0x80|0x7d, ioaddr + 0x30); + if (((inw(ioaddr+ 0x30) & 1) == 0) && (inb(ioaddr + 0x32) !=0)) break; + outb(0x80|0x7f, ioaddr + 0x30); + if (((inw(ioaddr+ 0x30) & 1) == 0) && (inb(ioaddr + 0x32) !=0)) break; + } + + if( w > 10000) { + outb( inb(ioaddr + 0x37) | 0x08, ioaddr + 0x37); /* do a software reset */ + mdelay(500); /* oh my.. */ + outb( inb(ioaddr + 0x37) & ~0x08, ioaddr + 0x37); + udelay(1); + outw( 0x80, ioaddr+0x30); + for(w = 0 ; w < 10000; w++) { + if((inw(ioaddr + 0x30) & 1) ==0) break; + } + } + } +#endif + if ( vend == NEC_VERSA_SUBID1 || vend == NEC_VERSA_SUBID2) { + /* turn on external amp? */ + outw(0xf9ff, ioaddr + 0x64); + outw(inw(ioaddr+0x68) | 0x600, ioaddr + 0x68); + outw(0x0209, ioaddr + 0x60); + } +} +/* + * Indirect register access. Not all registers are readable so we + * need to keep register state ourselves + */ + +#define WRITEABLE_MAP 0xEFFFFF +#define READABLE_MAP 0x64003F + +/* + * The Maestro engineers were a little indirection happy. These indirected + * registers themselves include indirect registers at another layer + */ + +static void __maestro_write(struct ess_card *card, u16 reg, u16 data) +{ + long ioaddr = card->iobase; + + outw(reg, ioaddr+0x02); + outw(data, ioaddr+0x00); + if( reg >= NR_IDRS) printk("maestro: IDR %d out of bounds!\n",reg); + else card->maestro_map[reg]=data; + +} + +static void maestro_write(struct ess_state *s, u16 reg, u16 data) +{ + unsigned long flags; + + CHECK_SUSPEND; + spin_lock_irqsave(&s->card->lock,flags); + + __maestro_write(s->card,reg,data); + + spin_unlock_irqrestore(&s->card->lock,flags); +} + +static u16 __maestro_read(struct ess_card *card, u16 reg) +{ + long ioaddr = card->iobase; + + outw(reg, ioaddr+0x02); + return card->maestro_map[reg]=inw(ioaddr+0x00); +} + +static u16 maestro_read(struct ess_state *s, u16 reg) +{ + if(READABLE_MAP & (1<card->lock,flags); + + __maestro_read(s->card,reg); + + spin_unlock_irqrestore(&s->card->lock,flags); + } + return s->card->maestro_map[reg]; +} + +/* + * These routines handle accessing the second level indirections to the + * wave ram. + */ + +/* + * The register names are the ones ESS uses (see 104T31.ZIP) + */ + +#define IDR0_DATA_PORT 0x00 +#define IDR1_CRAM_POINTER 0x01 +#define IDR2_CRAM_DATA 0x02 +#define IDR3_WAVE_DATA 0x03 +#define IDR4_WAVE_PTR_LOW 0x04 +#define IDR5_WAVE_PTR_HI 0x05 +#define IDR6_TIMER_CTRL 0x06 +#define IDR7_WAVE_ROMRAM 0x07 + +static void apu_index_set(struct ess_card *card, u16 index) +{ + int i; + __maestro_write(card, IDR1_CRAM_POINTER, index); + for(i=0;i<1000;i++) + if(__maestro_read(card, IDR1_CRAM_POINTER)==index) + return; + printk(KERN_WARNING "maestro: APU register select failed.\n"); +} + +static void apu_data_set(struct ess_card *card, u16 data) +{ + int i; + for(i=0;i<1000;i++) + { + if(__maestro_read(card, IDR0_DATA_PORT)==data) + return; + __maestro_write(card, IDR0_DATA_PORT, data); + } +} + +/* + * This is the public interface for APU manipulation. It handles the + * interlock to avoid two APU writes in parallel etc. Don't diddle + * directly with the stuff above. + */ + +static void apu_set_register(struct ess_state *s, u16 channel, u8 reg, u16 data) +{ + unsigned long flags; + + CHECK_SUSPEND; + + if(channel&ESS_CHAN_HARD) + channel&=~ESS_CHAN_HARD; + else + { + if(channel>5) + printk("BAD CHANNEL %d.\n",channel); + else + channel = s->apu[channel]; +#ifdef CONFIG_APM + /* store based on real hardware apu/reg */ + s->card->apu_map[channel][reg]=data; +#endif + } + reg|=(channel<<4); + + /* hooray for double indirection!! */ + spin_lock_irqsave(&s->card->lock,flags); + + apu_index_set(s->card, reg); + apu_data_set(s->card, data); + + spin_unlock_irqrestore(&s->card->lock,flags); +} + +static u16 apu_get_register(struct ess_state *s, u16 channel, u8 reg) +{ + unsigned long flags; + u16 v; + + CHECK_SUSPEND; + + if(channel&ESS_CHAN_HARD) + channel&=~ESS_CHAN_HARD; + else + channel = s->apu[channel]; + + reg|=(channel<<4); + + spin_lock_irqsave(&s->card->lock,flags); + + apu_index_set(s->card, reg); + v=__maestro_read(s->card, IDR0_DATA_PORT); + + spin_unlock_irqrestore(&s->card->lock,flags); + return v; +} + + +/* + * The wavecache buffers between the APUs and + * pci bus mastering + */ + +static void wave_set_register(struct ess_state *s, u16 reg, u16 value) +{ + long ioaddr = s->card->iobase; + unsigned long flags; + CHECK_SUSPEND; + + spin_lock_irqsave(&s->card->lock,flags); + + outw(reg, ioaddr+0x10); + outw(value, ioaddr+0x12); + + spin_unlock_irqrestore(&s->card->lock,flags); +} + +static u16 wave_get_register(struct ess_state *s, u16 reg) +{ + long ioaddr = s->card->iobase; + unsigned long flags; + u16 value; + CHECK_SUSPEND; + + spin_lock_irqsave(&s->card->lock,flags); + outw(reg, ioaddr+0x10); + value=inw(ioaddr+0x12); + spin_unlock_irqrestore(&s->card->lock,flags); + + return value; +} + +static void sound_reset(int ioaddr) +{ + outw(0x2000, 0x18+ioaddr); + udelay(1); + outw(0x0000, 0x18+ioaddr); + udelay(1); +} + +/* sets the play formats of these apus, should be passed the already shifted format */ +static void set_apu_fmt(struct ess_state *s, int apu, int mode) +{ + int apu_fmt = 0x10; + + if(!(mode&ESS_FMT_16BIT)) apu_fmt+=0x20; + if((mode&ESS_FMT_STEREO)) apu_fmt+=0x10; + s->apu_mode[apu] = apu_fmt; + s->apu_mode[apu+1] = apu_fmt; +} + +/* this only fixes the output apu mode to be later set by start_dac and + company. output apu modes are set in ess_rec_setup */ +static void set_fmt(struct ess_state *s, unsigned char mask, unsigned char data) +{ + s->fmt = (s->fmt & mask) | data; + set_apu_fmt(s, 0, (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK); +} + +/* this is off by a little bit.. */ +static u32 compute_rate(struct ess_state *s, u32 freq) +{ + u32 clock = clock_freq[s->card->card_type]; + + if (freq == 48000) return 0x10000; + + return ((freq / clock) <<16 )+ + (((freq % clock) << 16) / clock); +} + +static void set_dac_rate(struct ess_state *s, unsigned int rate) +{ + u32 freq; + int fmt = (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + + s->ratedac = rate; + + if(! (fmt & ESS_FMT_16BIT) && !(fmt & ESS_FMT_STEREO)) + rate >>= 1; + +/* M_printk("computing dac rate %d with mode %d\n",rate,s->fmt);*/ + + freq = compute_rate(s, rate); + + /* Load the frequency, turn on 6dB */ + apu_set_register(s, 0, 2,(apu_get_register(s, 0, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 0, 3, freq>>8); + apu_set_register(s, 1, 2,(apu_get_register(s, 1, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 1, 3, freq>>8); +} + +static void set_adc_rate(struct ess_state *s, unsigned rate) +{ + u32 freq; + + /* Sample Rate conversion APUs don't like 0x10000 for their rate */ + if (rate > 47999) + rate = 47999; + if (rate < 4000) + rate = 4000; + + s->rateadc = rate; + + freq = compute_rate(s, rate); + + /* Load the frequency, turn on 6dB */ + apu_set_register(s, 2, 2,(apu_get_register(s, 2, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 2, 3, freq>>8); + apu_set_register(s, 3, 2,(apu_get_register(s, 3, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 3, 3, freq>>8); + + /* fix mixer rate at 48khz. and its _must_ be 0x10000. */ + freq = 0x10000; + + apu_set_register(s, 4, 2,(apu_get_register(s, 4, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 4, 3, freq>>8); + apu_set_register(s, 5, 2,(apu_get_register(s, 5, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 5, 3, freq>>8); +} + +/* Stop our host of recording apus */ +extern inline void stop_adc(struct ess_state *s) +{ + /* XXX lets hope we don't have to lock around this */ + if (! (s->enable & ADC_RUNNING)) return; + + s->enable &= ~ADC_RUNNING; + apu_set_register(s, 2, 0, apu_get_register(s, 2, 0)&0xFF0F); + apu_set_register(s, 3, 0, apu_get_register(s, 3, 0)&0xFF0F); + apu_set_register(s, 4, 0, apu_get_register(s, 2, 0)&0xFF0F); + apu_set_register(s, 5, 0, apu_get_register(s, 3, 0)&0xFF0F); +} + +/* stop output apus */ +extern inline void stop_dac(struct ess_state *s) +{ + /* XXX have to lock around this? */ + if (! (s->enable & DAC_RUNNING)) return; + + s->enable &= ~DAC_RUNNING; + apu_set_register(s, 0, 0, apu_get_register(s, 0, 0)&0xFF0F); + apu_set_register(s, 1, 0, apu_get_register(s, 1, 0)&0xFF0F); +} + +static void start_dac(struct ess_state *s) +{ + /* XXX locks? */ + if ( (s->dma_dac.mapped || s->dma_dac.count > 0) && + s->dma_dac.ready && + (! (s->enable & DAC_RUNNING)) ) { + + s->enable |= DAC_RUNNING; + + apu_set_register(s, 0, 0, + (apu_get_register(s, 0, 0)&0xFF0F)|s->apu_mode[0]); + + if((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_STEREO) + apu_set_register(s, 1, 0, + (apu_get_register(s, 1, 0)&0xFF0F)|s->apu_mode[1]); + } +} + +static void start_adc(struct ess_state *s) +{ + /* XXX locks? */ + if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) + && s->dma_adc.ready && (! (s->enable & ADC_RUNNING)) ) { + + s->enable |= ADC_RUNNING; + apu_set_register(s, 2, 0, + (apu_get_register(s, 2, 0)&0xFF0F)|s->apu_mode[2]); + apu_set_register(s, 4, 0, + (apu_get_register(s, 4, 0)&0xFF0F)|s->apu_mode[4]); + + if( s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) { + apu_set_register(s, 3, 0, + (apu_get_register(s, 3, 0)&0xFF0F)|s->apu_mode[3]); + apu_set_register(s, 5, 0, + (apu_get_register(s, 5, 0)&0xFF0F)|s->apu_mode[5]); + } + + } +} + + +/* + * Native play back driver + */ + +/* the mode passed should be already shifted and masked */ +static void +ess_play_setup(struct ess_state *ess, int mode, u32 rate, void *buffer, int size) +{ + u32 pa; + u32 tmpval; + int high_apu = 0; + int channel; + + M_printk("mode=%d rate=%d buf=%p len=%d.\n", + mode, rate, buffer, size); + + /* all maestro sizes are in 16bit words */ + size >>=1; + + if(mode&ESS_FMT_STEREO) { + high_apu++; + /* only 16/stereo gets size divided */ + if(mode&ESS_FMT_16BIT) + size>>=1; + } + + for(channel=0; channel <= high_apu; channel++) + { + pa = virt_to_bus(buffer); + + /* set the wavecache control reg */ + tmpval = (pa - 0x10) & 0xFFF8; + if(!(mode & ESS_FMT_16BIT)) tmpval |= 4; + if(mode & ESS_FMT_STEREO) tmpval |= 2; + ess->apu_base[channel]=tmpval; + wave_set_register(ess, ess->apu[channel]<<3, tmpval); + + pa -= virt_to_bus(ess->card->dmapages); + pa>>=1; /* words */ + + /* base offset of dma calcs when reading the pointer + on the left one */ + if(!channel) ess->dma_dac.base = pa&0xFFFF; + + pa|=0x00400000; /* System RAM */ + + /* XXX the 16bit here might not be needed.. */ + if((mode & ESS_FMT_STEREO) && (mode & ESS_FMT_16BIT)) { + if(channel) + pa|=0x00800000; /* Stereo */ + pa>>=1; + } + +/* XXX think about endianess when writing these registers */ + M_printk("maestro: ess_play_setup: APU[%d] pa = 0x%x\n", ess->apu[channel], pa); + /* Load the buffer into the wave engine */ + apu_set_register(ess, channel, 4, ((pa>>16)&0xFF)<<8); + apu_set_register(ess, channel, 5, pa&0xFFFF); + apu_set_register(ess, channel, 6, (pa+size)&0xFFFF); + /* setting loop == sample len */ + apu_set_register(ess, channel, 7, size); + + /* clear effects/env.. */ + apu_set_register(ess, channel, 8, 0x0000); + /* set amp now to 0xd0 (?), low byte is 'amplitude dest'? */ + apu_set_register(ess, channel, 9, 0xD000); + + /* clear routing stuff */ + apu_set_register(ess, channel, 11, 0x0000); + /* dma on, no envelopes, filter to all 1s) */ + apu_set_register(ess, channel, 0, 0x400F); + + if(mode&ESS_FMT_16BIT) + ess->apu_mode[channel]=0x10; + else + ess->apu_mode[channel]=0x30; + + if(mode&ESS_FMT_STEREO) { + /* set panning: left or right */ + apu_set_register(ess, channel, 10, 0x8F00 | (channel ? 0x10 : 0)); + ess->apu_mode[channel] += 0x10; + } else + apu_set_register(ess, channel, 10, 0x8F08); + } + + /* clear WP interupts */ + outw(1, ess->card->iobase+0x04); + /* enable WP ints */ + outw(inw(ess->card->iobase+0x18)|4, ess->card->iobase+0x18); + + /* go team! */ + set_dac_rate(ess,rate); + start_dac(ess); +} + +/* + * Native record driver + */ + +/* again, passed mode is alrady shifted/masked */ +static void +ess_rec_setup(struct ess_state *ess, int mode, u32 rate, void *buffer, int size) +{ + int apu_step = 2; + int channel; + + M_printk("maestro: ess_rec_setup: mode=%d rate=%d buf=0x%p len=%d.\n", + mode, rate, buffer, size); + + /* all maestro sizes are in 16bit words */ + size >>=1; + + /* we're given the full size of the buffer, but + in stereo each channel will only use its half */ + if(mode&ESS_FMT_STEREO) { + size >>=1; + apu_step = 1; + } + + /* APU assignments: 2 = mono/left SRC + 3 = right SRC + 4 = mono/left Input Mixer + 5 = right Input Mixer */ + for(channel=2;channel<6;channel+=apu_step) + { + int i; + int bsize, route; + u32 pa; + u32 tmpval; + + /* data seems to flow from the codec, through an apu into + the 'mixbuf' bit of page, then through the SRC apu + and out to the real 'buffer'. ok. sure. */ + + if(channel & 0x04) { + /* ok, we're an input mixer going from adc + through the mixbuf to the other apus */ + + if(!(channel & 0x01)) { + pa = virt_to_bus(ess->mixbuf); + } else { + pa = virt_to_bus(ess->mixbuf + (PAGE_SIZE >> 4)); + } + + /* we source from a 'magic' apu */ + bsize = PAGE_SIZE >> 5; /* half of this channels alloc, in words */ + route = 0x14 + (channel - 4); /* parallel in crap, see maestro reg 0xC [8-11] */ + ess->apu_mode[channel] = 0x90; /* Input Mixer */ + + } else { + /* we're a rate converter taking + input from the input apus and outputing it to + system memory */ + if(!(channel & 0x01)) { + pa = virt_to_bus(buffer); + } else { + /* right channel records its split half. + *2 accomodates for rampant shifting earlier */ + pa = virt_to_bus(buffer + size*2); + } + + ess->apu_mode[channel] = 0xB0; /* Sample Rate Converter */ + + bsize = size; + /* get input from inputing apu */ + route = channel + 2; + } + + M_printk("maestro: ess_rec_setup: getting pa 0x%x from %d\n",pa,channel); + + /* set the wavecache control reg */ + tmpval = (pa - 0x10) & 0xFFF8; + ess->apu_base[channel]=tmpval; + wave_set_register(ess, ess->apu[channel]<<3, tmpval); + + pa -= virt_to_bus(ess->card->dmapages); + pa>>=1; /* words */ + + /* base offset of dma calcs when reading the pointer + on this left one */ + if(channel==2) ess->dma_adc.base = pa&0xFFFF; + + pa|=0x00400000; /* bit 22 -> System RAM */ + + M_printk("maestro: ess_rec_setup: APU[%d] pa = 0x%x size = 0x%x route = 0x%x\n", + ess->apu[channel], pa, bsize, route); + + /* Begin loading the APU */ + for(i=0;i<15;i++) /* clear all PBRs */ + apu_set_register(ess, channel, i, 0x0000); + + apu_set_register(ess, channel, 0, 0x400F); + + /* need to enable subgroups.. and we should probably + have different groups for different /dev/dsps.. */ + apu_set_register(ess, channel, 2, 0x8); + + /* Load the buffer into the wave engine */ + apu_set_register(ess, channel, 4, ((pa>>16)&0xFF)<<8); + /* XXX reg is little endian.. */ + apu_set_register(ess, channel, 5, pa&0xFFFF); + apu_set_register(ess, channel, 6, (pa+bsize)&0xFFFF); + apu_set_register(ess, channel, 7, bsize); + + /* clear effects/env.. */ + apu_set_register(ess, channel, 8, 0x00F0); + + /* amplitude now? sure. why not. */ + apu_set_register(ess, channel, 9, 0x0000); + + /* set filter tune, radius, polar pan */ + apu_set_register(ess, channel, 10, 0x8F08); + + /* route input */ + apu_set_register(ess, channel, 11, route); + } + + /* clear WP interupts */ + outw(1, ess->card->iobase+0x04); + /* enable WP ints */ + outw(inw(ess->card->iobase+0x18)|4, ess->card->iobase+0x18); + + /* let 'er rip */ + set_adc_rate(ess,rate); + start_adc(ess); +} +/* --------------------------------------------------------------------- */ + +static void set_dmaa(struct ess_state *s, unsigned int addr, unsigned int count) +{ + M_printk("set_dmaa??\n"); +} + +static void set_dmac(struct ess_state *s, unsigned int addr, unsigned int count) +{ + M_printk("set_dmac??\n"); +} + +/* Playback pointer */ +extern __inline__ unsigned get_dmaa(struct ess_state *s) +{ + int offset; + + offset = apu_get_register(s,0,5); + +/* M_printk("dmaa: offset: %d, base: %d\n",offset,s->dma_dac.base); */ + + offset-=s->dma_dac.base; + + return (offset&0xFFFE)<<1; /* hardware is in words */ +} + +/* Record pointer */ +extern __inline__ unsigned get_dmac(struct ess_state *s) +{ + int offset; + + offset = apu_get_register(s,2,5); + +/* M_printk("dmac: offset: %d, base: %d\n",offset,s->dma_adc.base); */ + + /* The offset is an address not a position relative to base */ + offset-=s->dma_adc.base; + + return (offset&0xFFFE)<<1; /* hardware is in words */ +} + +/* + * Meet Bob, the timer... + */ + +static void ess_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +static void stop_bob(struct ess_state *s) +{ + /* Mask IDR 11,17 */ + maestro_write(s, 0x11, maestro_read(s, 0x11)&~1); + maestro_write(s, 0x17, maestro_read(s, 0x17)&~1); +} + +/* eventually we could be clever and limit bob ints + to the frequency at which our smallest duration + chunks may expire */ +#define ESS_SYSCLK 50000000 +static void start_bob(struct ess_state *s) +{ + int prescale; + int divide; + + /* XXX make freq selector much smarter, see calc_bob_rate */ + int freq = 150; /* requested frequency - calculate what we want here. */ + + /* compute ideal interrupt frequency for buffer size & play rate */ + /* first, find best prescaler value to match freq */ + for(prescale=5;prescale<12;prescale++) + if(freq > (ESS_SYSCLK>>(prescale+9))) + break; + + /* next, back off prescaler whilst getting divider into optimum range */ + divide=1; + while((prescale > 5) && (divide<32)) + { + prescale--; + divide <<=1; + } + divide>>=1; + + /* now fine-tune the divider for best match */ + for(;divide<31;divide++) + if(freq >= ((ESS_SYSCLK>>(prescale+9))/(divide+1))) + break; + + /* divide = 0 is illegal, but don't let prescale = 4! */ + if(divide == 0) + { + divide++; + if(prescale>5) + prescale--; + } + + maestro_write(s, 6, 0x9000 | (prescale<<5) | divide); /* set reg */ + + /* Now set IDR 11/17 */ + maestro_write(s, 0x11, maestro_read(s, 0x11)|1); + maestro_write(s, 0x17, maestro_read(s, 0x17)|1); +} +/* --------------------------------------------------------------------- */ + +/* this quickly calculates the frequency needed for bob + and sets it if its different than what bob is + currently running at. its called often so + needs to be fairly quick. */ +#define BOB_MIN 50 +#define BOB_MAX 400 +static void calc_bob_rate(struct ess_state *s) { +#if 0 /* this thing tries to set the frequency of bob such that + there are 2 interrupts / buffer walked by the dac/adc. That + is probably very wrong for people who actually care about + mid buffer positioning. it should be calculated as bytes/interrupt + and that needs to be decided :) so for now just use the static 150 + in start_bob.*/ + + unsigned int dac_rate=2,adc_rate=1,newrate; + static int israte=-1; + + if (s->dma_dac.fragsize == 0) dac_rate = BOB_MIN; + else { + dac_rate = (2 * s->ratedac * sample_size[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]) / + (s->dma_dac.fragsize) ; + } + + if (s->dma_adc.fragsize == 0) adc_rate = BOB_MIN; + else { + adc_rate = (2 * s->rateadc * sample_size[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]) / + (s->dma_adc.fragsize) ; + } + + if(dac_rate > adc_rate) newrate = adc_rate; + else newrate=dac_rate; + + if(newrate > BOB_MAX) newrate = BOB_MAX; + else { + if(newrate < BOB_MIN) + newrate = BOB_MIN; + } + + if( israte != newrate) { + printk("dac: %d adc: %d rate: %d\n",dac_rate,adc_rate,israte); + israte=newrate; + } +#endif + +} + +static int +prog_dmabuf(struct ess_state *s, unsigned rec) +{ + struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; + unsigned rate = rec ? s->rateadc : s->ratedac; + unsigned bytepersec; + unsigned bufs; + unsigned char fmt; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + fmt = s->fmt; + if (rec) { + stop_adc(s); + fmt >>= ESS_ADC_SHIFT; + } else { + stop_dac(s); + fmt >>= ESS_DAC_SHIFT; + } + spin_unlock_irqrestore(&s->lock, flags); + fmt &= ESS_FMT_MASK; + + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + + bytepersec = rate << sample_shift[fmt]; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + + M_printk("maestro: setup oss: numfrag: %d fragsize: %d dmasize: %d\n",db->numfrag,db->fragsize,db->dmasize); + + memset(db->rawbuf, (fmt & ESS_FMT_16BIT) ? 0 : 0x80, db->dmasize); + + spin_lock_irqsave(&s->lock, flags); + if (rec) { + ess_rec_setup(s, fmt, s->rateadc, + db->rawbuf, db->numfrag << db->fragshift); + } else { + ess_play_setup(s, fmt, s->ratedac, + db->rawbuf, db->numfrag << db->fragshift); + } + spin_unlock_irqrestore(&s->lock, flags); + db->ready = 1; + + return 0; +} + +static __inline__ void +clear_advance(struct ess_state *s) +{ + unsigned char c = ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_16BIT) ? 0 : 0x80; + + unsigned char *buf = s->dma_dac.rawbuf; + unsigned bsize = s->dma_dac.dmasize; + unsigned bptr = s->dma_dac.swptr; + unsigned len = s->dma_dac.fragsize; + + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(buf + bptr, c, x); + /* account for wrapping? */ + bptr = 0; + len -= x; + } + memset(buf + bptr, c, len); +} + +/* call with spinlock held! */ +static void +ess_update_ptr(struct ess_state *s) +{ + unsigned hwptr; + int diff; + + /* update ADC pointer */ + if (s->dma_adc.ready) { + /* oh boy should this all be re-written. everything in the current code paths think + that the various counters/pointers are expressed in bytes to the user but we have + two apus doing stereo stuff so we fix it up here.. it propogates to all the various + counters from here. Notice that this means that mono recording is very very + broken right now. */ + if ( s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) { + hwptr = (get_dmac(s)*2) % s->dma_adc.dmasize; + } else { + hwptr = get_dmac(s) % s->dma_adc.dmasize; + } + diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + if (!s->dma_adc.mapped) { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + /* FILL ME + wrindir(s, SV_CIENABLE, s->enable); */ + stop_adc(s); + /* brute force everyone back in sync, sigh */ + s->dma_adc.count = 0; + s->dma_adc.swptr = 0; + s->dma_adc.hwptr = 0; + s->dma_adc.error++; + } + } + } + /* update DAC pointer */ + if (s->dma_dac.ready) { + hwptr = get_dmaa(s) % s->dma_dac.dmasize; + /* the apu only reports the length it has seen, not the + length of the memory that has been used (the WP + knows that */ + if ( ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK) == (ESS_FMT_STEREO|ESS_FMT_16BIT)) + hwptr<<=1; + + diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; +/* M_printk("updating dac: hwptr: %d diff: %d\n",hwptr,diff);*/ + s->dma_dac.hwptr = hwptr; + s->dma_dac.total_bytes += diff; + if (s->dma_dac.mapped) { + s->dma_dac.count += diff; + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) { + wake_up(&s->dma_dac.wait); + } + } else { + s->dma_dac.count -= diff; +/* M_printk("maestro: ess_update_ptr: diff: %d, count: %d\n", diff, s->dma_dac.count); */ + if (s->dma_dac.count <= 0) { + /* FILL ME + wrindir(s, SV_CIENABLE, s->enable); */ + /* XXX how on earth can calling this with the lock held work.. */ + stop_dac(s); + /* brute force everyone back in sync, sigh */ + s->dma_dac.count = 0; + s->dma_dac.swptr = 0; + s->dma_dac.hwptr = 0; + s->dma_dac.error++; + } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { + clear_advance(s); + s->dma_dac.endcleared = 1; + } + if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) { + wake_up(&s->dma_dac.wait); + } + } + } +} + +static void +ess_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct ess_state *s; + struct ess_card *c = (struct ess_card *)dev_id; + int i; + u32 event; + + if ( ! (event = inb(c->iobase+0x1A)) ) return; + + outw(inw(c->iobase+4)&1, c->iobase+4); + +/* M_printk("maestro int: %x\n",event);*/ + + if(event&(1<<6)) + { + /* XXX if we have a hw volume control int enable + all the ints? doesn't make sense.. */ + event = inw(c->iobase+0x18); + outb(0xFF, c->iobase+0x1A); + } + else + { + /* else ack 'em all, i imagine */ + outb(0xFF, c->iobase+0x1A); + } + + /* + * Update the pointers for all APU's we are running. + */ + for(i=0;ichannels[i]; + if(s->dev_audio == -1) + break; + spin_lock(&s->lock); + ess_update_ptr(s); + spin_unlock(&s->lock); + } +} + + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT "maestro: invalid magic value in %s\n"; + +#define VALIDATE_MAGIC(FOO,MAG) \ +({ \ + if (!(FOO) || (FOO)->magic != MAG) { \ + printk(invalid_magic,__FUNCTION__); \ + return -ENXIO; \ + } \ +}) + +#define VALIDATE_STATE(a) VALIDATE_MAGIC(a,ESS_STATE_MAGIC) +#define VALIDATE_CARD(a) VALIDATE_MAGIC(a,ESS_CARD_MAGIC) + +static void set_mixer(struct ess_card *card,unsigned int mixer, unsigned int val ) +{ + unsigned int left,right; + /* cleanse input a little */ + right = ((val >> 8) & 0xff) ; + left = (val & 0xff) ; + + if(right > 100) right = 100; + if(left > 100) left = 100; + + card->mix.mixer_state[mixer]=(right << 8) | left; + card->mix.write_mixer(card,mixer,left,right); +} + +static void +mixer_push_state(struct ess_card *card) +{ + int i; + for(i = 0 ; i < SOUND_MIXER_NRDEVICES ; i++) { + if( ! supported_mixer(card,i)) continue; + + set_mixer(card,i,card->mix.mixer_state[i]); + } +} + +static int mixer_ioctl(struct ess_card *card, unsigned int cmd, unsigned long arg) +{ + int i, val=0; + + VALIDATE_CARD(card); + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, card_names[card->card_type], sizeof(info.id)); + strncpy(info.name,card_names[card->card_type],sizeof(info.name)); + info.modify_counter = card->mix.modcnt; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strncpy(info.id, card_names[card->card_type], sizeof(info.id)); + strncpy(info.name,card_names[card->card_type],sizeof(info.name)); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + + if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + + if (_IOC_DIR(cmd) == _IOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* give them the current record source */ + + if(!card->mix.recmask_io) { + val = 0; + } else { + spin_lock(&card->lock); + val = card->mix.recmask_io(card,1,0); + spin_unlock(&card->lock); + } + break; + + case SOUND_MIXER_DEVMASK: /* give them the supported mixers */ + val = card->mix.supported_mixers; + break; + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + val = card->mix.record_sources; + break; + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + val = card->mix.stereo_mixers; + break; + + case SOUND_MIXER_CAPS: + val = SOUND_CAP_EXCL_INPUT; + break; + + default: /* read a specific mixer */ + i = _IOC_NR(cmd); + + if ( ! supported_mixer(card,i)) + return -EINVAL; + + /* do we ever want to touch the hardware? */ +/* spin_lock(&card->lock); + val = card->mix.read_mixer(card,i); + spin_unlock(&card->lock);*/ + + val = card->mix.mixer_state[i]; +/* M_printk("returned 0x%x for mixer %d\n",val,i);*/ + + break; + } + return put_user(val,(int *)arg); + } + + if (_IOC_DIR(cmd) != (_IOC_WRITE|_IOC_READ)) + return -EINVAL; + + card->mix.modcnt++; + + get_user_ret(val, (int *)arg, -EFAULT); + + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + + if (!card->mix.recmask_io) return -EINVAL; + if(!val) return 0; + if(! (val &= card->mix.record_sources)) return -EINVAL; + + spin_lock(&card->lock); + card->mix.recmask_io(card,0,val); + spin_unlock(&card->lock); + return 0; + + default: + i = _IOC_NR(cmd); + + if ( ! supported_mixer(card,i)) + return -EINVAL; + + spin_lock(&card->lock); + set_mixer(card,i,val); + spin_unlock(&card->lock); + + return 0; + } +} + +/* --------------------------------------------------------------------- */ + +static loff_t ess_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +/* --------------------------------------------------------------------- */ + +static int ess_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct ess_card *card = devs; + + while (card && card->dev_mixer != minor) + card = card->next; + if (!card) + return -ENODEV; + + file->private_data = card; + MOD_INC_USE_COUNT; + return 0; +} + +static int ess_release_mixdev(struct inode *inode, struct file *file) +{ + struct ess_card *card = (struct ess_card *)file->private_data; + + VALIDATE_CARD(card); + + MOD_DEC_USE_COUNT; + return 0; +} + +static int ess_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct ess_card *card = (struct ess_card *)file->private_data; + + VALIDATE_CARD(card); + + return mixer_ioctl(card, cmd, arg); +} + +static /*const*/ struct file_operations ess_mixer_fops = { + &ess_llseek, + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + &ess_ioctl_mixdev, + NULL, /* mmap */ + &ess_open_mixdev, + NULL, /* flush */ + &ess_release_mixdev, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct ess_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait,current); + unsigned long flags; + int count; + signed long tmo; + + if (s->dma_dac.mapped || !s->dma_dac.ready) + return 0; + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&s->dma_dac.wait, &wait); + for (;;) { + /* XXX uhm.. questionable locking*/ + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / s->ratedac; + tmo >>= sample_shift[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]; + /* XXX this is just broken. someone is waking us up alot, or schedule_timeout is broken. + or something. who cares. - zach */ + if (!schedule_timeout(tmo ? tmo : 1) && tmo) + M_printk(KERN_DEBUG "maestro: dma timed out?? %ld\n",jiffies); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ +/* Zach sez: "god this is gross.." */ +static int +comb_stereo(unsigned char *real_buffer,unsigned char *tmp_buffer, int offset, + int count, int bufsize) +{ + /* No such thing as stereo recording, so we + use dual input mixers. which means we have to + combine mono to stereo buffer. yuck. + + but we don't have to be able to work a byte at a time..*/ + + unsigned char *so,*left,*right; + int i; + + so = tmp_buffer; + left = real_buffer + offset; + right = real_buffer + bufsize/2 + offset; + +/* M_printk("comb_stereo writing %d to %p from %p and %p, offset: %d size: %d\n",count/2, tmp_buffer,left,right,offset,bufsize);*/ + + for(i=count/4; i ; i--) { + (*(so+2)) = *(right++); + (*(so+3)) = *(right++); + (*so) = *(left++); + (*(so+1)) = *(left++); + so+=4; + } + + return 0; +} + +/* in this loop, dma_adc.count signifies the amount of data thats waiting + to be copied to the user's buffer. it is filled by the interrupt + handler and drained by this loop. */ +static ssize_t +ess_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + unsigned char *combbuf = NULL; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + if(!(combbuf = kmalloc(count,GFP_KERNEL))) + return -ENOMEM; + ret = 0; + + calc_bob_rate(s); + + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + /* remember, all these things are expressed in bytes to be + sent to the user.. hence the evil / 2 down below */ + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + spin_unlock_irqrestore(&s->lock, flags); + + if (cnt > count) + cnt = count; + + if ( cnt > 0 ) cnt &= ~3; + + if (cnt <= 0) { + start_adc(s); + if (file->f_flags & O_NONBLOCK) + { + ret = ret ? ret : -EAGAIN; + goto rec_return_free; + } + if (!interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ)) { +#ifdef CONFIG_APM + if(! in_suspend) +#endif + printk(KERN_DEBUG "maestro: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, + s->dma_adc.hwptr, s->dma_adc.swptr); + stop_adc(s); + spin_lock_irqsave(&s->lock, flags); + set_dmac(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.numfrag << s->dma_adc.fragshift); + /* program enhanced mode registers */ + /* FILL ME */ +/* wrindir(s, SV_CIDMACBASECOUNT1, (s->dma_adc.fragsamples-1) >> 8); + wrindir(s, SV_CIDMACBASECOUNT0, s->dma_adc.fragsamples-1); */ + s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; + spin_unlock_irqrestore(&s->lock, flags); + } + if (signal_pending(current)) + { + ret = ret ? ret : -ERESTARTSYS; + goto rec_return_free; + } + continue; + } + + if(s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) { + /* swptr/2 so that we know the real offset in each apu's buffer */ + comb_stereo(s->dma_adc.rawbuf,combbuf,swptr/2,cnt,s->dma_adc.dmasize); + if (copy_to_user(buffer, combbuf, cnt)) { + ret = ret ? ret : -EFAULT; + goto rec_return_free; + } + } else { + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { + ret = ret ? ret : -EFAULT; + goto rec_return_free; + } + } + + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_adc(s); + } + +rec_return_free: + if(combbuf) kfree(combbuf); + return ret; +} + +static ssize_t +ess_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac.mapped) + return -ENXIO; + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + + calc_bob_rate(s); + + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + swptr = s->dma_dac.swptr; + + cnt = s->dma_dac.dmasize-swptr; + + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.count; + + spin_unlock_irqrestore(&s->lock, flags); + + if (cnt > count) + cnt = count; + + if (cnt <= 0) { + start_dac(s); + if (file->f_flags & O_NONBLOCK) { + if(!ret) ret = -EAGAIN; + goto return_free; + } + if (!interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ)) { +#ifdef CONFIG_APM + if(! in_suspend) +#endif + printk(KERN_DEBUG "maestro: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, + s->dma_dac.hwptr, s->dma_dac.swptr); + stop_dac(s); + spin_lock_irqsave(&s->lock, flags); + set_dmaa(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.numfrag << s->dma_dac.fragshift); + /* program enhanced mode registers */ +/* wrindir(s, SV_CIDMAABASECOUNT1, (s->dma_dac.fragsamples-1) >> 8); + wrindir(s, SV_CIDMAABASECOUNT0, s->dma_dac.fragsamples-1); */ + /* FILL ME */ + s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; + spin_unlock_irqrestore(&s->lock, flags); + } + if (signal_pending(current)) { + if (!ret) ret = -ERESTARTSYS; + goto return_free; + } + continue; + } + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { + if (!ret) ret = -EFAULT; + goto return_free; + } + + swptr = (swptr + cnt) % s->dma_dac.dmasize; + + spin_lock_irqsave(&s->lock, flags); + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac(s); + } +return_free: + return ret; +} + +static unsigned int ess_poll(struct file *file, struct poll_table_struct *wait) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->dma_dac.wait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->dma_adc.wait, wait); + spin_lock_irqsave(&s->lock, flags); + ess_update_ptr(s); + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int ess_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + struct dmabuf *db; + int ret; + unsigned long size; + + VALIDATE_STATE(s); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf(s, 1)) != 0) + return ret; + db = &s->dma_dac; + } else +#if 0 + /* if we can have the wp/wc do the combining + we can turn this back on. */ + if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf(s, 0)) != 0) + return ret; + db = &s->dma_adc; + } else +#endif + return -EINVAL; + if (vma->vm_offset != 0) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + return -EINVAL; + if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) + return -EAGAIN; + db->mapped = 1; + return 0; +} + +static int ess_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + unsigned char fmtm, fmtd; + +/* printk("maestro: ess_ioctl: cmd %d\n", cmd);*/ + + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(s, file->f_flags & O_NONBLOCK); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + /* XXX fix */ + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + get_user_ret(val, (int *)arg, -EFAULT); + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + set_adc_rate(s, val); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + set_dac_rate(s, val); + } + } + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + + case SNDCTL_DSP_STEREO: + get_user_ret(val, (int *)arg, -EFAULT); + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val) + fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val) + fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT); + } + set_fmt(s, fmtm, fmtd); + return 0; + + case SNDCTL_DSP_CHANNELS: + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 0) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val >= 2) + fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val >= 2) + fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT); + } + set_fmt(s, fmtm, fmtd); + } + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) + : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_U8|AFMT_S16_LE, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + get_user_ret(val, (int *)arg, -EFAULT); + if (val != AFMT_QUERY) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + /* fixed at 16bit for now */ + fmtd |= ESS_FMT_16BIT << ESS_ADC_SHIFT; +#if 0 + if (val == AFMT_S16_LE) + fmtd |= ESS_FMT_16BIT << ESS_ADC_SHIFT; + else + fmtm &= ~(ESS_FMT_16BIT << ESS_ADC_SHIFT); +#endif + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= ESS_FMT_16BIT << ESS_DAC_SHIFT; + else + fmtm &= ~(ESS_FMT_16BIT << ESS_DAC_SHIFT); + } + set_fmt(s, fmtm, fmtd); + } + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? + (ESS_FMT_16BIT << ESS_ADC_SHIFT) + : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? + AFMT_S16_LE : + AFMT_U8, + (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if ((file->f_mode & FMODE_READ) && (s->enable & ADC_RUNNING)) + val |= PCM_ENABLE_INPUT; + if ((file->f_mode & FMODE_WRITE) && (s->enable & DAC_RUNNING)) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + start_adc(s); + } else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + start_dac(s); + } else + stop_dac(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!(s->enable & DAC_RUNNING) && (val = prog_dmabuf(s, 0)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + ess_update_ptr(s); + abinfo.fragsize = s->dma_dac.fragsize; + abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!(s->enable & ADC_RUNNING) && (val = prog_dmabuf(s, 1)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + ess_update_ptr(s); + abinfo.fragsize = s->dma_adc.fragsize; + abinfo.bytes = s->dma_adc.count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + ess_update_ptr(s); + val = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + ess_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + ess_update_ptr(s); + cinfo.bytes = s->dma_dac.total_bytes; + cinfo.blocks = s->dma_dac.count >> s->dma_dac.fragshift; + cinfo.ptr = s->dma_dac.hwptr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf(s, 0))) + return val; + return put_user(s->dma_dac.fragsize, (int *)arg); + } + if ((val = prog_dmabuf(s, 1))) + return val; + return put_user(s->dma_adc.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = val & 0xffff; + s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac.ossfragshift < 4) + s->dma_dac.ossfragshift = 4; + if (s->dma_dac.ossfragshift > 15) + s->dma_dac.ossfragshift = 15; + if (s->dma_dac.ossmaxfrags < 4) + s->dma_dac.ossmaxfrags = 4; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) + : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_16BIT << ESS_ADC_SHIFT) + : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? 16 : 8, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return -EINVAL; +} + +static void +set_base_registers(struct ess_state *s,void *vaddr) +{ + unsigned long packed_phys = virt_to_bus(vaddr)>>12; + wave_set_register(s, 0x01FC , packed_phys); + wave_set_register(s, 0x01FD , packed_phys); + wave_set_register(s, 0x01FE , packed_phys); + wave_set_register(s, 0x01FF , packed_phys); +} + +/* we allocate a large power of two for all our memory. + this is cut up into (not to scale :): + |silly fifo word | 512byte mixbuf per adc | dac/adc * channels | +*/ +static int +allocate_buffers(struct ess_state *s) +{ + void *rawbuf=NULL; + int order,i; + unsigned long mapend,map; + + /* alloc as big a chunk as we can */ + for (order = (dsps_order + (15-PAGE_SHIFT) + 1); order >= (dsps_order + 2 + 1); order--) + if((rawbuf = (void *)__get_free_pages(GFP_KERNEL|GFP_DMA, order))) + break; + + if (!rawbuf) + return 1; + + M_printk("maestro: allocated %ld (%d) bytes at %p\n",PAGE_SIZE<card->dmapages = rawbuf; + s->card->dmaorder = order; + + /* play bufs are in the same first region as record bufs */ + set_base_registers(s,rawbuf); + + M_printk("maestro: writing %lx (%lx) to the wp\n",virt_to_bus(rawbuf), + ((virt_to_bus(rawbuf))&0xFFE00000)>>12); + + for(i=0;icard->channels[i]; + + if(ess->dev_audio == -1) + continue; + + ess->dma_dac.ready = s->dma_dac.mapped = 0; + ess->dma_adc.ready = s->dma_adc.mapped = 0; + ess->dma_adc.buforder = ess->dma_dac.buforder = order - 1 - dsps_order - 1; + + /* offset dac and adc buffers starting half way through and then at each [da][ad]c's + order's intervals.. */ + ess->dma_dac.rawbuf = rawbuf + (PAGE_SIZE<<(order-1)) + (i * ( PAGE_SIZE << (ess->dma_dac.buforder + 1 ))); + ess->dma_adc.rawbuf = ess->dma_dac.rawbuf + ( PAGE_SIZE << ess->dma_dac.buforder); + /* offset mixbuf by a mixbuf so that the lame status fifo can + happily scribble away.. */ + ess->mixbuf = rawbuf + (512 * (i+1)); + + M_printk("maestro: setup apu %d: %p %p %p\n",i,ess->dma_dac.rawbuf, + ess->dma_adc.rawbuf, ess->mixbuf); + + } + + /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ + mapend = MAP_NR(rawbuf + (PAGE_SIZE << order) - 1); + for (map = MAP_NR(rawbuf); map <= mapend; map++) { + set_bit(PG_reserved, &mem_map[map].flags); + } + + return 0; +} +static void +free_buffers(struct ess_state *s) +{ + unsigned long map, mapend; + + s->dma_dac.rawbuf = s->dma_adc.rawbuf = NULL; + s->dma_dac.mapped = s->dma_adc.mapped = 0; + s->dma_dac.ready = s->dma_adc.ready = 0; + + M_printk("maestro: freeing %p\n",s->card->dmapages); + /* undo marking the pages as reserved */ + + mapend = MAP_NR(s->card->dmapages + (PAGE_SIZE << s->card->dmaorder) - 1); + for (map = MAP_NR(s->card->dmapages); map <= mapend; map++) + clear_bit(PG_reserved, &mem_map[map].flags); + + free_pages((unsigned long)s->card->dmapages,s->card->dmaorder); + s->card->dmapages = NULL; +} + +static int +ess_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct ess_card *c = devs; + struct ess_state *s = NULL, *sp; + int i; + unsigned char fmtm = ~0, fmts = 0; + + /* + * Scan the cards and find the channel. We only + * do this at open time so it is ok + */ + + while (c!=NULL) + { + for(i=0;ichannels[i]; + if(sp->dev_audio < 0) + continue; + if((sp->dev_audio ^ minor) & ~0xf) + continue; + s=sp; + } + c=c->next; + } + + if (!s) + return -ENODEV; + + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EWOULDBLOCK; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + + /* under semaphore.. */ + if ((s->card->dmapages==NULL) && allocate_buffers(s)) { + up(&s->open_sem); + return -ENOMEM; + } + + if (file->f_mode & FMODE_READ) { +/* + fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_ADC_SHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= ESS_FMT_16BIT << ESS_ADC_SHIFT; */ + + fmtm &= ~((ESS_FMT_STEREO|ESS_FMT_16BIT) << ESS_ADC_SHIFT); + fmts = (ESS_FMT_STEREO|ESS_FMT_16BIT) << ESS_ADC_SHIFT; + + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + set_adc_rate(s, 8000); + } + if (file->f_mode & FMODE_WRITE) { + fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_DAC_SHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= ESS_FMT_16BIT << ESS_DAC_SHIFT; + + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; + set_dac_rate(s, 8000); + } + set_fmt(s, fmtm, fmts); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + + /* we're covered by the open_sem */ + if( ! s->card->dsps_open ) { + start_bob(s); + } + s->card->dsps_open++; + M_printk("maestro: open, %d bobs now\n",s->card->dsps_open); + + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + +static int +ess_release(struct inode *inode, struct file *file) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + drain_dac(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + } + + s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + /* we're covered by the open_sem */ + M_printk("maestro: %d dsps now alive\n",s->card->dsps_open-1); + if( --s->card->dsps_open <= 0) { + stop_bob(s); + free_buffers(s); + } + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +static struct file_operations ess_audio_fops = { + &ess_llseek, + &ess_read, + &ess_write, + NULL, /* readdir */ + &ess_poll, + &ess_ioctl, + &ess_mmap, + &ess_open, + NULL, /* flush */ + &ess_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +static int +maestro_config(struct ess_card *card) +{ + struct pci_dev *pcidev = &card->pcidev; + struct ess_state *ess = &card->channels[0]; + int apu,iobase = card->iobase; + u16 w; + u32 n; + + /* + * Disable ACPI + */ + + pci_write_config_dword(pcidev, 0x54, 0x00000000); + pci_write_config_dword(pcidev, 0x56, 0x00000000); + + /* + * Use TDMA for now. TDMA works on all boards, so while its + * not the most efficient its the simplest. + */ + + pci_read_config_word(pcidev, 0x50, &w); + + /* Clear DMA bits */ + w&=~(1<<10|1<<9|1<<8); + + /* TDMA on */ + w|= (1<<8); + + /* + * Some of these are undocumented bits + */ + + w&=~(1<<13)|(1<<14); /* PIC Snoop mode bits */ + w&=~(1<<11); /* Safeguard off */ + w|= (1<<7); /* Posted write */ + w|= (1<<6); /* ISA timing on */ + /* XXX huh? claims to be reserved.. */ + w&=~(1<<5); /* Don't swap left/right */ + w&=~(1<<1); /* Subtractive decode off */ + + pci_write_config_word(pcidev, 0x50, w); + + pci_read_config_word(pcidev, 0x52, &w); + w&=~(1<<15); /* Turn off internal clock multiplier */ + /* XXX how do we know which to use? */ + w&=~(1<<14); /* External clock */ + + w&=~(1<<7); /* HWV off */ + w&=~(1<<6); /* Debounce off */ + w&=~(1<<5); /* GPIO 4:5 */ + w|= (1<<4); /* Disconnect from the CHI. Enabling this made a dell 7500 work. */ + w&=~(1<<3); /* IDMA off (undocumented) */ + w&=~(1<<2); /* MIDI fix off (undoc) */ + w&=~(1<<1); /* reserved, always write 0 */ + w&=~(1<<0); /* IRQ to ISA off (undoc) */ + pci_write_config_word(pcidev, 0x52, w); + + /* + * DDMA off + */ + + pci_read_config_word(pcidev, 0x60, &w); + w&=~(1<<0); + pci_write_config_word(pcidev, 0x60, w); + + /* + * Legacy mode + */ + + pci_read_config_word(pcidev, 0x40, &w); + w|=(1<<15); /* legacy decode off */ + w&=~(1<<14); /* Disable SIRQ */ + w&=~(0x1f); /* disable mpu irq/io, game port, fm, SB */ + + pci_write_config_word(pcidev, 0x40, w); + + /* stake our claim on the iospace */ + request_region(iobase, 256, card_names[card->card_type]); + + sound_reset(iobase); + + /* + * Ring Bus Setup + */ + + /* setup usual 0x34 stuff.. 0x36 may be chip specific */ + outw(0xC090, iobase+0x34); /* direct sound, stereo */ + udelay(20); + outw(0x3000, iobase+0x36); /* direct sound, stereo */ + udelay(20); + + + /* + * Reset the CODEC + */ + + maestro_ac97_reset(iobase,pcidev); + + /* + * Ring Bus Setup + */ + + n=inl(iobase+0x34); + n&=~0xF000; + n|=12<<12; /* Direct Sound, Stereo */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x0F00; /* Modem off */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x00F0; + n|=9<<4; /* DAC, Stereo */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x000F; /* ASSP off */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n|=(1<<29); /* Enable ring bus */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n|=(1<<28); /* Enable serial bus */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x00F00000; /* MIC off */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x000F0000; /* I2S off */ + outl(n, iobase+0x34); + + + w=inw(iobase+0x18); + w&=~(1<<7); /* ClkRun off */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w&=~(1<<6); /* Harpo off */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w&=~(1<<4); /* ASSP irq off */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w&=~(1<<3); /* ISDN irq off */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w|=(1<<2); /* Direct Sound IRQ on */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w&=~(1<<1); /* MPU401 IRQ off */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w|=(1<<0); /* SB IRQ on */ + outw(w, iobase+0x18); + + /* it appears some maestros (dell 7500) only work if these are set, + regardless of wether we use the assp or not. */ + + outb(0, iobase+0xA4); + outb(3, iobase+0xA2); + outb(0, iobase+0xA6); + + for(apu=0;apu<16;apu++) + { + /* Write 0 into the buffer area 0x1E0->1EF */ + outw(0x01E0+apu, 0x10+iobase); + outw(0x0000, 0x12+iobase); + + /* + * The 1.10 test program seem to write 0 into the buffer area + * 0x1D0-0x1DF too. + */ + outw(0x01D0+apu, 0x10+iobase); + outw(0x0000, 0x12+iobase); + } + +#if 1 + wave_set_register(ess, IDR7_WAVE_ROMRAM, + (wave_get_register(ess, IDR7_WAVE_ROMRAM)&0xFF00)); + wave_set_register(ess, IDR7_WAVE_ROMRAM, + wave_get_register(ess, IDR7_WAVE_ROMRAM)|0x100); + wave_set_register(ess, IDR7_WAVE_ROMRAM, + wave_get_register(ess, IDR7_WAVE_ROMRAM)&~0x200); + wave_set_register(ess, IDR7_WAVE_ROMRAM, + wave_get_register(ess, IDR7_WAVE_ROMRAM)|~0x400); +#else + maestro_write(ess, IDR7_WAVE_ROMRAM, + (maestro_read(ess, IDR7_WAVE_ROMRAM)&0xFF00)); + maestro_write(ess, IDR7_WAVE_ROMRAM, + maestro_read(ess, IDR7_WAVE_ROMRAM)|0x100); + maestro_write(ess, IDR7_WAVE_ROMRAM, + maestro_read(ess, IDR7_WAVE_ROMRAM)&~0x200); + maestro_write(ess, IDR7_WAVE_ROMRAM, + maestro_read(ess, IDR7_WAVE_ROMRAM)|0x400); +#endif + + maestro_write(ess, IDR2_CRAM_DATA, 0x0000); + maestro_write(ess, 0x08, 0xB004); + /* Now back to the DirectSound stuff */ + maestro_write(ess, 0x09, 0x001B); + maestro_write(ess, 0x0A, 0x8000); + maestro_write(ess, 0x0B, 0x3F37); + maestro_write(ess, 0x0C, 0x0098); + + /* parallel out ?? */ + maestro_write(ess, 0x0C, + (maestro_read(ess, 0x0C)&~0xF000)|0x8000); + /* parallel in, has something to do with recording :) */ + maestro_write(ess, 0x0C, + (maestro_read(ess, 0x0C)&~0x0F00)|0x0500); + + maestro_write(ess, 0x0D, 0x7632); + + /* Wave cache control on - test off, sg off, + enable, enable extra chans 1Mb */ + + outw(inw(0x14+iobase)|(1<<8),0x14+iobase); + outw(inw(0x14+iobase)&0xFE03,0x14+iobase); + outw((inw(0x14+iobase)&0xFFFC), 0x14+iobase); + outw(inw(0x14+iobase)|(1<<7),0x14+iobase); + + outw(0xA1A0, 0x14+iobase); /* 0300 ? */ + + /* Now clear the APU control ram */ + for(apu=0;apuclass >> 8) & 0xffff) != PCI_CLASS_MULTIMEDIA_AUDIO) + return 0; + + iobase = SILLY_PCI_BASE_ADDRESS(pcidev); + + if(check_region(iobase, 256)) + { + printk(KERN_WARNING "maestro: can't allocate 256 bytes I/O at 0x%4.4x\n", iobase); + return 0; + } + + /* this was tripping up some machines */ + if(pcidev->irq == 0) + { + printk(KERN_WARNING "maestro: pci subsystem reports irq 0, this might not be correct.\n"); + } + + /* just to be sure */ + pci_set_master(pcidev); + + card = kmalloc(sizeof(struct ess_card), GFP_KERNEL); + if(card == NULL) + { + printk(KERN_WARNING "maestro: out of memory\n"); + return 0; + } + + memset(card, 0, sizeof(*card)); + memcpy(&card->pcidev,pcidev,sizeof(card->pcidev)); + +#ifdef CONFIG_APM + if (apm_register_callback(maestro_apm_callback)) { + printk(KERN_WARNING "maestro: apm suspend might not work.\n"); + } +#endif + + card->iobase = iobase; + card->card_type = card_type; + card->irq = pcidev->irq; + card->next = devs; + card->magic = ESS_CARD_MAGIC; + spin_lock_init(&card->lock); + devs = card; + + /* init our groups of 6 apus */ + for(i=0;ichannels[i]; + + s->index = i; + + s->card = card; + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + spin_lock_init(&s->lock); + SILLY_INIT_SEM(s->open_sem); + s->magic = ESS_STATE_MAGIC; + + s->apu[0] = 6*i; + s->apu[1] = (6*i)+1; + s->apu[2] = (6*i)+2; + s->apu[3] = (6*i)+3; + s->apu[4] = (6*i)+4; + s->apu[5] = (6*i)+5; + + if(s->dma_adc.ready || s->dma_dac.ready || s->dma_adc.rawbuf) + printk("maestro: BOTCH!\n"); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&ess_audio_fops, -1)) < 0) + break; + } + + num = i; + + /* clear the rest if we ran out of slots to register */ + for(;ichannels[i]; + s->dev_audio = -1; + } + + ess = &card->channels[0]; + + /* + * Ok card ready. Begin setup proper + */ + + printk(KERN_INFO "maestro: Configuring %s found at IO 0x%04X IRQ %d\n", + card_names[card_type],iobase,card->irq); + pci_read_config_dword(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &n); + printk(KERN_INFO "maestro: subvendor id: 0x%08x\n",n); + + maestro_config(card); + + if(maestro_ac97_get(iobase, 0x00)==0x0080) { + printk(KERN_ERR "maestro: my goodness! you seem to have a pt101 codec, which is quite rare.\n" + "\tyou should tell someone about this.\n"); + } else { + maestro_ac97_init(card,iobase); + } + + if ((card->dev_mixer = register_sound_mixer(&ess_mixer_fops, -1)) < 0) { + printk("maestro: couldn't register mixer!\n"); + } else { + memcpy(card->mix.mixer_state,mixer_defaults,sizeof(card->mix.mixer_state)); + mixer_push_state(card); + } + + if(request_irq(card->irq, ess_interrupt, SA_SHIRQ, card_names[card_type], card)) + { + printk(KERN_ERR "maestro: unable to allocate irq %d,\n", card->irq); + unregister_sound_mixer(card->dev_mixer); + for(i=0;ichannels[i]; + if(s->dev_audio != -1) + unregister_sound_dsp(s->dev_audio); + } + release_region(card->iobase, 256); + kfree(card); + return 0; + } + + printk(KERN_INFO "maestro: %d channels configured.\n", num); + return 1; +} + +#ifdef MODULE +int init_module(void) +#else +int SILLY_MAKE_INIT(init_maestro(void)) +#endif +{ + struct pci_dev *pcidev = NULL; + int foundone = 0; + + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk(KERN_INFO "maestro: version " DRIVER_VERSION " time " __TIME__ " " __DATE__ "\n"); + + pcidev = NULL; + + if (dsps_order < 0) { + dsps_order = 1; + printk(KERN_WARNING "maestro: clipping dsps_order to %d\n",dsps_order); + } + else if (dsps_order > MAX_DSP_ORDER) { + dsps_order = MAX_DSP_ORDER; + printk(KERN_WARNING "maestro: clipping dsps_order to %d\n",dsps_order); + } + +#ifdef CONFIG_APM + init_waitqueue_head(&suspend_queue); +#endif + + /* + * Find the ESS Maestro 2. + */ + + while( (pcidev = pci_find_device(PCI_VENDOR_ESS, PCI_DEVICE_ID_ESS_ESS1968, pcidev))!=NULL ) { + if (maestro_install(pcidev, TYPE_MAESTRO2)) + foundone=1; + } + + /* + * Find the ESS Maestro 2E + */ + + while( (pcidev = pci_find_device(PCI_VENDOR_ESS, PCI_DEVICE_ID_ESS_ESS1978, pcidev))!=NULL) { + if (maestro_install(pcidev, TYPE_MAESTRO2E)) + foundone=1; + } + + /* + * ESS Maestro 1 + */ + + while((pcidev = pci_find_device(PCI_VENDOR_ESS_OLD, PCI_DEVICE_ID_ESS_ESS0100, pcidev))!=NULL) { + if (maestro_install(pcidev, TYPE_MAESTRO)) + foundone=1; + } + if( ! foundone ) { + printk("maestro: no devices found.\n"); + return -ENODEV; + } + return 0; +} + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE +MODULE_AUTHOR("Zach Brown , Alan Cox "); +MODULE_DESCRIPTION("ESS Maestro Driver"); +#ifdef M_DEBUG +MODULE_PARM(debug,"i"); +#endif +MODULE_PARM(dsps_order,"i"); + +void cleanup_module(void) +{ + struct ess_card *s; + +#ifdef CONFIG_APM + apm_unregister_callback(maestro_apm_callback); +#endif + while ((s = devs)) { + int i; + devs = devs->next; + + /* XXX maybe should force stop bob, but should be all + stopped by _release by now */ + free_irq(s->irq, s); + unregister_sound_mixer(s->dev_mixer); + for(i=0;ichannels[i]; + if(ess->dev_audio != -1) + unregister_sound_dsp(ess->dev_audio); + } + release_region(s->iobase, 256); + kfree(s); + } + M_printk("maestro: unloading\n"); +} + +#endif /* MODULE */ +#ifdef CONFIG_APM + +void +check_suspend(void) +{ + DECLARE_WAITQUEUE(wait, current); + + if(!in_suspend) return; + + in_suspend++; + add_wait_queue(&suspend_queue, &wait); + current->state = TASK_UNINTERRUPTIBLE; + schedule(); + remove_wait_queue(&suspend_queue, &wait); + current->state = TASK_RUNNING; +} + +static int +maestro_suspend(void) +{ + struct ess_card *card; + unsigned long flags; + + save_flags(flags); + cli(); + + for (card = devs; card ; card = card->next) { + int i,j; + + M_printk("maestro: apm in dev %p\n",card); + + for(i=0;ichannels[i]; + + if(s->dev_audio == -1) + continue; + + M_printk("maestro: stopping apus for device %d\n",i); + stop_dac(s); + stop_adc(s); + for(j=0;j<6;j++) + card->apu_map[s->apu[j]][5]=apu_get_register(s,j,5); + + } + + /* get rid of interrupts? */ + if( card->dsps_open > 0) + stop_bob(&card->channels[0]); + } + in_suspend=1; + + restore_flags(flags); + + /* we'll let the bios do the rest of the power down.. */ + + return 0; +} +static int +maestro_resume(void) +{ + struct ess_card *card; + unsigned long flags; + + save_flags(flags); + cli(); + in_suspend=0; + M_printk("maestro: resuming\n"); + + /* first lets just bring everything back. .*/ + for (card = devs; card ; card = card->next) { + int i; + + M_printk("maestro: apm in dev %p\n",card); + + maestro_config(card); + /* need to restore the base pointers.. */ + if(card->dmapages) + set_base_registers(&card->channels[0],card->dmapages); + + mixer_push_state(card); + + for(i=0;ichannels[i]; + int chan,reg; + + if(s->dev_audio == -1) + continue; + + for(chan = 0 ; chan < 6 ; chan++) { + wave_set_register(s,s->apu[chan]<<3,s->apu_base[chan]); + for(reg = 1 ; reg < NR_APU_REGS ; reg++) + apu_set_register(s,chan,reg,s->card->apu_map[s->apu[chan]][reg]); + } + for(chan = 0 ; chan < 6 ; chan++) + apu_set_register(s,chan,0,s->card->apu_map[s->apu[chan]][0] & 0xFF0F); + } + } + + /* now we flip on the music */ + for (card = devs; card ; card = card->next) { + int i; + + M_printk("maestro: apm in dev %p\n",card); + + for(i=0;ichannels[i]; + + /* these use the apu_mode, and can handle + spurious calls */ + start_dac(s); + start_adc(s); + } + if( card->dsps_open > 0) + start_bob(&card->channels[0]); + } + + restore_flags(flags); + + wake_up(&suspend_queue); + + return 0; +} + +int +maestro_apm_callback(apm_event_t ae) { + + M_printk("maestro: apm event received: 0x%x\n",ae); + + switch(ae) { + case APM_SYS_SUSPEND: + case APM_CRITICAL_SUSPEND: + case APM_USER_SUSPEND: + maestro_suspend();break; + case APM_NORMAL_RESUME: + case APM_CRITICAL_RESUME: + case APM_STANDBY_RESUME: + maestro_resume();break; + default: break; + } + + return 0; +} +#endif diff -u --recursive --new-file v2.2.13/linux/drivers/sound/maestro.h linux/drivers/sound/maestro.h --- v2.2.13/linux/drivers/sound/maestro.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/sound/maestro.h Tue Jan 4 10:12:22 2000 @@ -0,0 +1,60 @@ +/* + * Registers for the ESS PCI cards + */ + +/* + * Memory access + */ + +#define ESS_MEM_DATA 0x00 +#define ESS_MEM_INDEX 0x02 + +/* + * AC-97 Codec port. Delay 1uS after each write. This is used to + * talk AC-97 (see intel.com). Write data then register. + */ + +#define ESS_AC97_INDEX 0x30 /* byte wide */ +#define ESS_AC97_DATA 0x32 + +/* + * Reading is a bit different. You write register|0x80 to ubdex + * delay 1uS poll the low bit of index, when it clears read the + * data value. + */ + +/* + * Control port. Not yet fully understood + * The value 0xC090 gets loaded to it then 0x0000 and 0x2800 + * to the data port. Then after 4uS the value 0x300 is written + */ + +#define RING_BUS_CTRL_L 0x34 +#define RING_BUS_CTRL_H 0x36 + +/* + * This is also used during setup. The value 0x17 is written to it + */ + +#define ESS_SETUP_18 0x18 + +/* + * And this one gets 0x000b + */ + +#define ESS_SETUP_A2 0xA2 + +/* + * And this 0x0000 + */ + +#define ESS_SETUP_A4 0xA4 +#define ESS_SETUP_A6 0xA6 + +/* + * Stuff to do with Harpo - the wave stuff + */ + +#define ESS_WAVETABLE_SIZE 0x14 +#define ESS_WAVETABLE_2M 0xA180 + diff -u --recursive --new-file v2.2.13/linux/drivers/sound/msnd_pinnacle.c linux/drivers/sound/msnd_pinnacle.c --- v2.2.13/linux/drivers/sound/msnd_pinnacle.c Tue Jan 4 11:10:39 2000 +++ linux/drivers/sound/msnd_pinnacle.c Tue Jan 4 10:12:22 2000 @@ -46,6 +46,7 @@ # include #endif #include +#include #include "sound_config.h" #include "sound_firmware.h" #ifdef MSND_CLASSIC diff -u --recursive --new-file v2.2.13/linux/drivers/sound/nm256.h linux/drivers/sound/nm256.h --- v2.2.13/linux/drivers/sound/nm256.h Tue Jan 4 11:10:39 2000 +++ linux/drivers/sound/nm256.h Tue Jan 4 10:12:22 2000 @@ -3,10 +3,12 @@ #include "ac97.h" +/* The revisions that we currently handle. */ enum nm256rev { REV_NM256AV, REV_NM256ZX }; +/* Per-card structure. */ struct nm256_info { /* Magic number used to verify that this struct is valid. */ @@ -34,10 +36,12 @@ /* The mixer device. */ int mixer_oss_dev; - /* Can only be opened once for each operation. These aren't set - until an actual I/O operation is performed; this allows one - device to be open for read/write without inhibiting I/O to - the other device. */ + /* + * Can only be opened once for each operation. These aren't set + * until an actual I/O operation is performed; this allows one + * device to be open for read/write without inhibiting I/O to + * the other device. + */ int is_open_play; int is_open_record; @@ -46,25 +50,42 @@ /* Ditto for recording a sample. */ int recording; - /* The two memory ports. */ - char *ports[2]; - - /* Starting offset of the port1 area mapped into memory. */ - u32 port1_start; - /* Ending offset. */ - u32 port1_end; - /* The offset of the end of the actual buffer area. */ - u32 bufend; + /* The two memory ports. */ + struct nm256_ports { + /* Physical address of the port. */ + u32 physaddr; + /* Our mapped-in pointer. */ + char *ptr; + /* PTR's offset within the physical port. */ + u32 start_offset; + /* And the offset of the end of the buffer. */ + u32 end_offset; + } port[2]; /* The following are offsets within memory port 1. */ u32 coeffBuf; u32 allCoeffBuf; + /* Record and playback buffers. */ u32 abuf1, abuf2; /* Offset of the AC97 mixer in memory port 2. */ u32 mixer; + /* Offset of the mixer status register in memory port 2. */ + u32 mixer_status_offset; + + /* Non-zero if we have written initial values to the mixer. */ + u8 mixer_values_init; + + u16 playRingsize; + + /* + * Status mask bit; (*mixer_status_loc & mixer_status_mask) == 0 means + * it's ready. + */ + u16 mixer_status_mask; + /* The sizes of the playback and record ring buffers. */ u32 playbackBufferSize; u32 recordBufferSize; @@ -77,7 +98,7 @@ /* The start of the block currently playing. */ u32 curPlayPos; - /* The amount of data we requested to record. */ + /* The amount of data we were requested to record. */ u32 requestedRecAmt; /* The offset of the currently-recording block. */ u32 curRecPos; @@ -107,10 +128,17 @@ /* Debug flag--bigger numbers mean more output. */ extern int nm256_debug; -/* Size of the second memory port. */ +/* The BIOS signature. */ +#define NM_SIGNATURE 0x4e4d0000 +/* Signature mask. */ +#define NM_SIG_MASK 0xffff0000 + +/* Size of the second memory area. */ #define NM_PORT2_SIZE 4096 -/* The location of the mixer. */ -#define NM_MIXER_BASE 0x600 + +/* The base offset of the mixer in the second memory area. */ +#define NM_MIXER_OFFSET 0x600 + /* The maximum size of a coefficient entry. */ #define NM_MAX_COEFFICIENT 0x5000 @@ -123,21 +151,33 @@ #define NM_MISC_INT_2 0x1 #define NM_ACK_INT(CARD, X) nm256_writePort16((CARD), 2, NM_INT_REG, (X) << 1) -/* For the second revision. It uses the same interrupt register, but it - holds 32 bits instead of 16. */ +/* The AV's "mixer ready" status bit and location. */ +#define NM_MIXER_STATUS_OFFSET 0xa04 +#define NM_MIXER_READY_MASK 0x0800 +#define NM_MIXER_PRESENCE 0xa06 +#define NM_PRESENCE_MASK 0x0050 +#define NM_PRESENCE_VALUE 0x0040 + +/* + * For the ZX. It uses the same interrupt register, but it holds 32 + * bits instead of 16. + */ #define NM2_PLAYBACK_INT 0x10000 #define NM2_RECORD_INT 0x80000 #define NM2_MISC_INT_1 0x8 #define NM2_MISC_INT_2 0x2 #define NM2_ACK_INT(CARD, X) nm256_writePort32((CARD), 2, NM_INT_REG, (X)) +/* The ZX's "mixer ready" status bit and location. */ +#define NM2_MIXER_STATUS_OFFSET 0xa06 +#define NM2_MIXER_READY_MASK 0x0800 + /* The playback registers start from here. */ #define NM_PLAYBACK_REG_OFFSET 0x0 /* The record registers start from here. */ #define NM_RECORD_REG_OFFSET 0x200 -/* The rate register is located 2 bytes from the start of the register - area. */ +/* The rate register is located 2 bytes from the start of the register area. */ #define NM_RATE_REG_OFFSET 2 /* Mono/stereo flag, number of bits on playback, and rate mask. */ @@ -156,7 +196,7 @@ #define NM_AUDIO_MUTE_LEFT 0x8000 #define NM_AUDIO_MUTE_RIGHT 0x0080 -/* Recording enable register */ +/* Recording enable register. */ #define NM_RECORD_ENABLE_REG (NM_RECORD_REG_OFFSET + 0) #define NM_RECORD_ENABLE_FLAG 1 #define NM_RECORD_FREERUN 2 @@ -179,26 +219,25 @@ if (port < 1 || port > 2 || card == NULL) \ return -1; \ \ - if (port == 1) { \ - if (offset < card->port1_start || offset >= card->port1_end) { \ - printk (KERN_ERR "Bad port request port 1:0x%x\n", offset); \ - return -1; \ - } \ - offset -= card->port1_start; \ - } else if (offset < 0 || offset > 4096) { \ - printk (KERN_ERR "Bad port request port 2: 0x%x\n", offset); \ - return -1; \ - } + if (offset < card->port[port - 1].start_offset \ + || offset >= card->port[port - 1].end_offset) { \ + printk (KERN_ERR "Bad access: port %d, offset 0x%x\n", port, offset); \ + return -1; \ + } \ + offset -= card->port[port - 1].start_offset; #define DEFwritePortX(X, func) \ static inline int nm256_writePort##X (struct nm256_info *card,\ - int port, int offset, int value)\ + int port, int offset, int value)\ {\ u##X *addr;\ \ + if (nm256_debug > 1)\ + printk (KERN_DEBUG "Writing 0x%x to %d:0x%x\n", value, port, offset);\ +\ NM_FIX_PORT;\ \ - addr = (u##X *)(card->ports[port - 1] + offset);\ + addr = (u##X *)(card->port[port - 1].ptr + offset);\ func (value, addr);\ return 0;\ } @@ -207,29 +246,28 @@ DEFwritePortX (16, writew) DEFwritePortX (32, writel) -#define DEFreadPortX(X) \ +#define DEFreadPortX(X, func) \ static inline u##X nm256_readPort##X (struct nm256_info *card,\ int port, int offset)\ {\ - u##X *addr, res;\ + u##X *addr;\ \ NM_FIX_PORT\ \ - addr = (u##X *)(card->ports[port - 1] + offset);\ - memcpy_fromio (&res, addr, sizeof (res));\ - return res;\ + addr = (u##X *)(card->port[port - 1].ptr + offset);\ + return func(addr);\ } -DEFreadPortX (8) -DEFreadPortX (16) -DEFreadPortX (32) +DEFreadPortX (8, readb) +DEFreadPortX (16, readw) +DEFreadPortX (32, readl) static inline int nm256_writeBuffer8 (struct nm256_info *card, u8 *src, int port, int offset, int amt) { NM_FIX_PORT; - memcpy_toio (card->ports[port - 1] + offset, src, amt); + memcpy_toio (card->port[port - 1].ptr + offset, src, amt); return 0; } @@ -238,7 +276,7 @@ int amt) { NM_FIX_PORT; - memcpy_fromio (dst, card->ports[port - 1] + offset, amt); + memcpy_fromio (dst, card->port[port - 1].ptr + offset, amt); return 0; } diff -u --recursive --new-file v2.2.13/linux/drivers/sound/nm256_audio.c linux/drivers/sound/nm256_audio.c --- v2.2.13/linux/drivers/sound/nm256_audio.c Tue Jan 4 11:10:39 2000 +++ linux/drivers/sound/nm256_audio.c Tue Jan 4 10:12:22 2000 @@ -1,27 +1,38 @@ -/* Audio driver for the NeoMagic 256AV and 256ZX chipsets in native - mode, with AC97 mixer support. - - Overall design and parts of this code stolen from vidc_*.c and - skeleton.c. - - Yeah, there are a lot of magic constants in here. You tell ME what - they are. I just get this stuff psychically, remember? - - This driver was written by someone who wishes to remain anonymous. - It is in the public domain, so share and enjoy. Try to make a profit - off of it; go on, I dare you. */ - +/* + * Audio driver for the NeoMagic 256AV and 256ZX chipsets in native + * mode, with AC97 mixer support. + * + * Overall design and parts of this code stolen from vidc_*.c and + * skeleton.c. + * + * Yeah, there are a lot of magic constants in here. You tell ME what + * they are. I just get this stuff psychically, remember? + * + * This driver was written by someone who wishes to remain anonymous. + * It is in the public domain, so share and enjoy. Try to make a profit + * off of it; go on, I dare you. + */ + #include +#include #include #include +#include +#include + #include "sound_config.h" #include "soundmodule.h" #include "nm256.h" #include "nm256_coeff.h" int nm256_debug = 0; +static int force_load = 0; -/* The size of the playback reserve. */ +/* + * The size of the playback reserve. When the playback buffer has less + * than NM256_PLAY_WMARK_SIZE bytes to output, we request a new + * buffer. + */ #define NM256_PLAY_WMARK_SIZE 512 static struct audio_driver nm256_audio_driver; @@ -29,16 +40,74 @@ static int nm256_grabInterrupt (struct nm256_info *card); static int nm256_releaseInterrupt (struct nm256_info *card); static void nm256_interrupt (int irq, void *dev_id, struct pt_regs *dummy); -static void nm256_interrupt_zx (int irq, void *dev_id, - struct pt_regs *dummy); +static void nm256_interrupt_zx (int irq, void *dev_id, struct pt_regs *dummy); /* These belong in linux/pci.h. */ #define PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO 0x8005 #define PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO 0x8006 +/* eeeew. */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) +#define RSRCADDRESS(dev,num) ((dev)->resource[(num)].start) +#else +#define RSRCADDRESS(dev,num) ((dev)->base_address[(num)] \ + & PCI_BASE_ADDRESS_MEM_MASK) + +#endif + /* List of cards. */ static struct nm256_info *nmcard_list; +/* Release the mapped-in memory for CARD. */ +static void +nm256_release_ports (struct nm256_info *card) +{ + int x; + + for (x = 0; x < 2; x++) { + if (card->port[x].ptr != NULL) { + u32 size = + card->port[x].end_offset - card->port[x].start_offset; + release_region ((unsigned long) card->port[x].ptr, size); + card->port[x].ptr = NULL; + } + } +} + +/* + * Map in the memory ports for CARD, if they aren't already mapped in + * and have been configured. If successful, a zero value is returned; + * otherwise any previously mapped-in areas are released and a non-zero + * value is returned. + * + * This is invoked twice, once for each port. Ideally it would only be + * called once, but we now need to map in the second port in order to + * check how much memory the card has on the 256ZX. + */ +static int +nm256_remap_ports (struct nm256_info *card) +{ + int x; + + for (x = 0; x < 2; x++) { + if (card->port[x].ptr == NULL && card->port[x].end_offset > 0) { + u32 physaddr + = card->port[x].physaddr + card->port[x].start_offset; + u32 size + = card->port[x].end_offset - card->port[x].start_offset; + + card->port[x].ptr = ioremap_nocache (physaddr, size); + + if (card->port[x].ptr == NULL) { + printk (KERN_ERR "NM256: Unable to remap port %d\n", x + 1); + nm256_release_ports (card); + return -1; + } + } + } + return 0; +} + /* Locate the card in our list. */ static struct nm256_info * nm256_find_card (int dev) @@ -52,8 +121,10 @@ return NULL; } -/* Ditto, but find the card struct corresponding to the mixer device DEV - instead. */ +/* + * Ditto, but find the card struct corresponding to the mixer device DEV + * instead. + */ static struct nm256_info * nm256_find_card_for_mixer (int dev) { @@ -81,11 +152,13 @@ 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 99999999 }; -/* Set the card samplerate, word size and stereo mode to correspond to - the settings in the CARD struct for the specified device in DEV. - We keep two separate sets of information, one for each device; the - hardware is not actually configured until a read or write is - attempted. */ +/* + * Set the card samplerate, word size and stereo mode to correspond to + * the settings in the CARD struct for the specified device in DEV. + * We keep two separate sets of information, one for each device; the + * hardware is not actually configured until a read or write is + * attempted. + */ int nm256_setInfo (int dev, struct nm256_info *card) @@ -113,24 +186,33 @@ break; if (x < 8) { - u8 speedbits = ((x << 4) & NM_RATE_MASK) - | (card->sinfo[w].bits == 16 ? NM_RATE_BITS_16: 0) - | (card->sinfo[w].stereo ? NM_RATE_STEREO : 0); + u8 ratebits = ((x << 4) & NM_RATE_MASK); + if (card->sinfo[w].bits == 16) + ratebits |= NM_RATE_BITS_16; + if (card->sinfo[w].stereo) + ratebits |= NM_RATE_STEREO; card->sinfo[w].samplerate = samplerates[x]; + if (card->dev_for_play == dev && card->playing) { + if (nm256_debug) + printk (KERN_DEBUG "Setting play ratebits to 0x%x\n", + ratebits); nm256_loadCoefficient (card, 0, x); nm256_writePort8 (card, 2, - NM_PLAYBACK_REG_OFFSET + NM_RATE_REG_OFFSET, - speedbits); + NM_PLAYBACK_REG_OFFSET + NM_RATE_REG_OFFSET, + ratebits); } if (card->dev_for_record == dev && card->recording) { + if (nm256_debug) + printk (KERN_DEBUG "Setting record ratebits to 0x%x\n", + ratebits); nm256_loadCoefficient (card, 1, x); - nm256_writePort8 (card, 2, - NM_RECORD_REG_OFFSET + NM_RATE_REG_OFFSET, - speedbits); + nm256_writePort8 (card, 2, + NM_RECORD_REG_OFFSET + NM_RATE_REG_OFFSET, + ratebits); } return 0; } @@ -149,7 +231,7 @@ /* Enable playback engine and interrupts. */ nm256_writePort8 (card, 2, NM_PLAYBACK_ENABLE_REG, - NM_PLAYBACK_ENABLE_FLAG | NM_PLAYBACK_FREERUN); + NM_PLAYBACK_ENABLE_FLAG | NM_PLAYBACK_FREERUN); /* Enable both channels. */ nm256_writePort16 (card, 2, NM_AUDIO_MUTE_REG, 0x0); @@ -157,9 +239,11 @@ } } -/* Request one chunk of AMT bytes from the recording device. When the - operation is complete, the data will be copied into BUFFER and the - function DMAbuf_inputintr will be invoked. */ +/* + * Request one chunk of AMT bytes from the recording device. When the + * operation is complete, the data will be copied into BUFFER and the + * function DMAbuf_inputintr will be invoked. + */ static void nm256_startRecording (struct nm256_info *card, char *buffer, u32 amt) @@ -167,11 +251,14 @@ u32 endpos; int enableEngine = 0; u32 ringsize = card->recordBufferSize; + unsigned long flags; if (amt > (ringsize / 2)) { - /* Of course this won't actually work right, because the - caller is going to assume we will give what we got asked - for. */ + /* + * Of course this won't actually work right, because the + * caller is going to assume we will give what we got asked + * for. + */ printk (KERN_ERR "NM256: Read request too large: %d\n", amt); amt = ringsize / 2; } @@ -181,8 +268,12 @@ return; } - /* If we're not currently recording, set up the start and end registers - for the recording engine. */ + save_flags (flags); + cli (); + /* + * If we're not currently recording, set up the start and end registers + * for the recording engine. + */ if (! card->recording) { card->recording = 1; if (nm256_grabInterrupt (card) == 0) { @@ -198,10 +289,16 @@ } else { /* Not sure what else to do here. */ + restore_flags (flags); return; } } + /* + * If we happen to go past the end of the buffer a bit (due to a + * delayed interrupt) it's OK. So might as well set the watermark + * right at the end of the data we want. + */ endpos = card->abuf2 + ((card->curRecPos + amt) % ringsize); card->recBuf = buffer; @@ -211,6 +308,8 @@ if (enableEngine) nm256_writePort8 (card, 2, NM_RECORD_ENABLE_REG, NM_RECORD_ENABLE_FLAG | NM_RECORD_FREERUN); + + restore_flags (flags); } /* Stop the play engine. */ @@ -219,7 +318,7 @@ { /* Shut off sound from both channels. */ nm256_writePort16 (card, 2, NM_AUDIO_MUTE_REG, - NM_AUDIO_MUTE_LEFT | NM_AUDIO_MUTE_RIGHT); + NM_AUDIO_MUTE_LEFT | NM_AUDIO_MUTE_RIGHT); /* Disable play engine. */ nm256_writePort8 (card, 2, NM_PLAYBACK_ENABLE_REG, 0); if (card->playing) { @@ -246,18 +345,22 @@ } } -/* Ring buffers, man. That's where the hip-hop, wild-n-wooly action's at. - 1972? - - Write AMT bytes of BUFFER to the playback ring buffer, and start the - playback engine running. It will only accept up to 1/2 of the total - size of the ring buffer. */ +/* + * Ring buffers, man. That's where the hip-hop, wild-n-wooly action's at. + * 1972? (Well, I suppose it was cheep-n-easy to implement.) + * + * Write AMT bytes of BUFFER to the playback ring buffer, and start the + * playback engine running. It will only accept up to 1/2 of the total + * size of the ring buffer. No check is made that we're about to overwrite + * the currently-playing sample. + */ static void nm256_write_block (struct nm256_info *card, char *buffer, u32 amt) { u32 ringsize = card->playbackBufferSize; u32 endstop; + unsigned long flags; if (amt > (ringsize / 2)) { printk (KERN_ERR "NM256: Write request too large: %d\n", amt); @@ -273,48 +376,58 @@ card->requested_amt = amt; + save_flags (flags); + cli (); + if ((card->curPlayPos + amt) >= ringsize) { u32 rem = ringsize - card->curPlayPos; nm256_writeBuffer8 (card, buffer, 1, - card->abuf1 + card->curPlayPos, - rem); + card->abuf1 + card->curPlayPos, + rem); if (amt > rem) - nm256_writeBuffer8 (card, buffer, 1, card->abuf1, - amt - rem); + nm256_writeBuffer8 (card, buffer + rem, 1, card->abuf1, + amt - rem); } else nm256_writeBuffer8 (card, buffer, 1, card->abuf1 + card->curPlayPos, amt); - /* Setup the start-n-stop-n-limit registers, and start that engine - goin'. - - Normally we just let it wrap around to avoid the click-click - action scene. */ + /* + * Setup the start-n-stop-n-limit registers, and start that engine + * goin'. + * + * Normally we just let it wrap around to avoid the click-click + * action scene. + */ if (! card->playing) { - /* The PBUFFER_END register in this case points to one "word" + /* The PBUFFER_END register in this case points to one sample before the end of the buffer. */ int w = (card->dev_for_play == card->dev[0] ? 0 : 1); - int wordsize = (card->sinfo[w].bits == 16 ? 2 : 1) - * (card->sinfo[w].stereo ? 2 : 1); + int sampsize = (card->sinfo[w].bits == 16 ? 2 : 1); + + if (card->sinfo[w].stereo) + sampsize *= 2; /* Need to set the not-normally-changing-registers up. */ nm256_writePort32 (card, 2, NM_PBUFFER_START, card->abuf1 + card->curPlayPos); nm256_writePort32 (card, 2, NM_PBUFFER_END, - card->abuf1 + ringsize - wordsize); + card->abuf1 + ringsize - sampsize); nm256_writePort32 (card, 2, NM_PBUFFER_CURRP, card->abuf1 + card->curPlayPos); } endstop = (card->curPlayPos + amt - NM256_PLAY_WMARK_SIZE) % ringsize; nm256_writePort32 (card, 2, NM_PBUFFER_WMARK, card->abuf1 + endstop); + if (! card->playing) startPlay (card); + + restore_flags (flags); } -/* We just got a card playback interrupt; process it. */ +/* We just got a card playback interrupt; process it. */ static void nm256_get_new_block (struct nm256_info *card) { @@ -332,13 +445,14 @@ amt -= card->curPlayPos; if (card->requested_amt > (amt + NM256_PLAY_WMARK_SIZE)) { - u32 endstop = + u32 endstop = card->curPlayPos + card->requested_amt - NM256_PLAY_WMARK_SIZE; nm256_writePort32 (card, 2, NM_PBUFFER_WMARK, card->abuf1 + endstop); - } else { + } + else { card->curPlayPos += card->requested_amt; /* Get a new block to write. This will eventually invoke - nm256_write_block (). */ + nm256_write_block () or stopPlay (). */ DMAbuf_outputintr (card->dev_for_play, 1); } } @@ -346,9 +460,11 @@ /* Ultra cheez-whiz. But I'm too lazy to grep headers. */ #define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) -/* Read the last-recorded block from the ring buffer, copy it into the - saved buffer pointer, and invoke DMAuf_inputintr() with the recording - device. */ +/* + * Read the last-recorded block from the ring buffer, copy it into the + * saved buffer pointer, and invoke DMAuf_inputintr() with the recording + * device. + */ static void nm256_read_block (struct nm256_info *card) @@ -363,8 +479,10 @@ currptr = 0; } - /* This test is probably redundant; we shouldn't be here unless - it's true. */ + /* + * This test is probably redundant; we shouldn't be here unless + * it's true. + */ if (card->recording) { /* If we wrapped around, copy everything from the start of our recording buffer to the end of the buffer. */ @@ -394,52 +512,28 @@ } #undef MIN -/* Initialize the hardware and various other card data we'll need - later. */ +/* + * Initialize the hardware. + */ static void nm256_initHw (struct nm256_info *card) { - int x; - - card->playbackBufferSize = 16384; - card->recordBufferSize = 16384; - - card->coeffBuf = card->bufend - NM_MAX_COEFFICIENT; - card->abuf2 = card->coeffBuf - card->recordBufferSize; - card->abuf1 = card->abuf2 - card->playbackBufferSize; - card->allCoeffBuf = card->abuf2 - (NM_TOTAL_COEFF_COUNT * 4); - - /* Fixed setting. */ - card->mixer = NM_MIXER_BASE; - - card->playing = 0; - card->is_open_play = 0; - card->curPlayPos = 0; - - card->recording = 0; - card->is_open_record = 0; - card->curRecPos = 0; - - card->coeffsCurrent = 0; - - card->opencnt[0] = 0; card->opencnt[1] = 0; - /* Reset everything. */ - nm256_writePort8 (card, 2, 0, 0x11); - - /* Disable recording. */ - nm256_writePort8 (card, 2, NM_RECORD_ENABLE_REG, 0); + nm256_writePort8 (card, 2, 0x0, 0x11); nm256_writePort16 (card, 2, 0x214, 0); - /* Reasonable default settings, but largely unnecessary. */ - for (x = 0; x < 2; x++) { - card->sinfo[x].bits = 8; - card->sinfo[x].stereo = 0; - card->sinfo[x].samplerate = 8000; - } + stopRecord (card); + stopPlay (card); } -/* Handle a potential interrupt for the device referred to by DEV_ID. */ +/* + * Handle a potential interrupt for the device referred to by DEV_ID. + * + * I don't like the cut-n-paste job here either between the two routines, + * but there are sufficient differences between the two interrupt handlers + * that parameterizing it isn't all that great either. (Could use a macro, + * I suppose...yucky bleah.) + */ static void nm256_interrupt (int irq, void *dev_id, struct pt_regs *dummy) @@ -458,14 +552,32 @@ /* Not ours. */ if (status == 0) { if (badintrcount++ > 1000) { - printk (KERN_ERR "NM256: Releasing interrupt, over 1000 invalid interrupts\n"); - nm256_releaseInterrupt (card); + /* + * I'm not sure if the best thing is to stop the card from + * playing or just release the interrupt (after all, we're in + * a bad situation, so doing fancy stuff may not be such a good + * idea). + * + * I worry about the card engine continuing to play noise + * over and over, however--that could become a very + * obnoxious problem. And we know that when this usually + * happens things are fairly safe, it just means the user's + * inserted a PCMCIA card and someone's spamming us with IRQ 9s. + */ + + if (card->playing) + stopPlay (card); + if (card->recording) + stopRecord (card); + badintrcount = 0; } return; } badintrcount = 0; + /* Rather boring; check for individual interrupts and process them. */ + if (status & NM_PLAYBACK_INT) { status &= ~NM_PLAYBACK_INT; NM_ACK_INT (card, NM_PLAYBACK_INT); @@ -503,6 +615,7 @@ nm256_writePort8 (card, 2, 0x400, cbyte & ~2); } + /* Unknown interrupt. */ if (status) { printk (KERN_ERR "NM256: Fire in the hole! Unknown status 0x%x\n", status); @@ -511,8 +624,11 @@ } } -/* Handle a potential interrupt for the device referred to by DEV_ID. - This handler is for the 256ZX. */ +/* + * Handle a potential interrupt for the device referred to by DEV_ID. + * This handler is for the 256ZX, and is very similar to the non-ZX + * routine. + */ static void nm256_interrupt_zx (int irq, void *dev_id, struct pt_regs *dummy) @@ -532,13 +648,33 @@ if (status == 0) { if (badintrcount++ > 1000) { printk (KERN_ERR "NM256: Releasing interrupt, over 1000 invalid interrupts\n"); - nm256_releaseInterrupt (card); + /* + * I'm not sure if the best thing is to stop the card from + * playing or just release the interrupt (after all, we're in + * a bad situation, so doing fancy stuff may not be such a good + * idea). + * + * I worry about the card engine continuing to play noise + * over and over, however--that could become a very + * obnoxious problem. And we know that when this usually + * happens things are fairly safe, it just means the user's + * inserted a PCMCIA card and someone's spamming us with + * IRQ 9s. + */ + + if (card->playing) + stopPlay (card); + if (card->recording) + stopRecord (card); + badintrcount = 0; } return; } badintrcount = 0; + /* Rather boring; check for individual interrupts and process them. */ + if (status & NM2_PLAYBACK_INT) { status &= ~NM2_PLAYBACK_INT; NM2_ACK_INT (card, NM2_PLAYBACK_INT); @@ -575,6 +711,7 @@ nm256_writePort8 (card, 2, 0x400, cbyte & ~2); } + /* Unknown interrupt. */ if (status) { printk (KERN_ERR "NM256: Fire in the hole! Unknown status 0x%x\n", status); @@ -583,7 +720,9 @@ } } -/* Request our interrupt. */ +/* + * Request our interrupt. + */ static int nm256_grabInterrupt (struct nm256_info *card) { @@ -597,7 +736,9 @@ return 0; } -/* Release our interrupt. */ +/* + * Release our interrupt. + */ static int nm256_releaseInterrupt (struct nm256_info *card) { @@ -612,6 +753,11 @@ return 0; } +/* + * Waits for the mixer to become ready to be written; returns a zero value + * if it timed out. + */ + static int nm256_isReady (struct ac97_hwint *dev) { @@ -626,26 +772,25 @@ return 0; } - if (card->rev == REV_NM256AV) { - testaddr = 0xa06; - testb = 0x0100; - } else if (card->rev == REV_NM256ZX) { - testaddr = 0xa08; - testb = 0x0800; - } else { - return -1; - } + testaddr = card->mixer_status_offset; + testb = card->mixer_status_mask; - while (t2-- > 0) { - if ((nm256_readPort16 (card, 2, testaddr) & testb) == 0) { + /* + * Loop around waiting for the mixer to become ready. + */ + while (! done && t2-- > 0) { + if ((nm256_readPort16 (card, 2, testaddr) & testb) == 0) done = 1; - break; - } - udelay (100); + else + udelay (100); } return done; } +/* + * Return the contents of the AC97 mixer register REG. Returns a positive + * value if successful, or a negative error code. + */ static int nm256_readAC97Reg (struct ac97_hwint *dev, u8 reg) { @@ -661,6 +806,7 @@ nm256_isReady (dev); res = nm256_readPort16 (card, 2, card->mixer + reg); + /* Magic delay. Bleah yucky. */ udelay (1000); return res; } @@ -668,6 +814,10 @@ return -EINVAL; } +/* + * Writes VALUE to AC97 mixer register REG. Returns 0 if successful, or + * a negative error code. + */ static int nm256_writeAC97Reg (struct ac97_hwint *dev, u8 reg, u16 value) { @@ -706,6 +856,13 @@ return ! done; } +/* + * Initial register values to be written to the AC97 mixer. + * While most of these are identical to the reset values, we do this + * so that we have most of the register contents cached--this avoids + * reading from the mixer directly (which seems to be problematic, + * probably due to ignorance). + */ struct initialValues { unsigned short port; @@ -714,23 +871,24 @@ static struct initialValues nm256_ac97_initial_values[] = { - { 0x0002, 0x8000 }, - { 0x0004, 0x0000 }, - { 0x0006, 0x0000 }, - { 0x000A, 0x0000 }, - { 0x000C, 0x0008 }, - { 0x000E, 0x8008 }, - { 0x0010, 0x8808 }, - { 0x0012, 0x8808 }, - { 0x0014, 0x8808 }, - { 0x0016, 0x8808 }, - { 0x0018, 0x0808 }, - { 0x001A, 0x0000 }, - { 0x001C, 0x0B0B }, - { 0x0020, 0x0000 }, + { AC97_MASTER_VOL_STEREO, 0x8000 }, + { AC97_HEADPHONE_VOL, 0x8000 }, + { AC97_MASTER_VOL_MONO, 0x0000 }, + { AC97_PCBEEP_VOL, 0x0000 }, + { AC97_PHONE_VOL, 0x0008 }, + { AC97_MIC_VOL, 0x8000 }, + { AC97_LINEIN_VOL, 0x8808 }, + { AC97_CD_VOL, 0x8808 }, + { AC97_VIDEO_VOL, 0x8808 }, + { AC97_AUX_VOL, 0x8808 }, + { AC97_PCMOUT_VOL, 0x0808 }, + { AC97_RECORD_SELECT, 0x0000 }, + { AC97_RECORD_GAIN, 0x0B0B }, + { AC97_GENERAL_PURPOSE, 0x0000 }, { 0xffff, 0xffff } }; +/* Initialize the AC97 into a known state. */ static int nm256_resetAC97 (struct ac97_hwint *dev) { @@ -742,22 +900,28 @@ return -EINVAL; } - /* Reset the card. 'Tis magic! */ + /* Reset the mixer. 'Tis magic! */ nm256_writePort8 (card, 2, 0x6c0, 1); nm256_writePort8 (card, 2, 0x6cc, 0x87); nm256_writePort8 (card, 2, 0x6cc, 0x80); nm256_writePort8 (card, 2, 0x6cc, 0x0); - for (x = 0; nm256_ac97_initial_values[x].port != 0xffff; x++) { - ac97_put_register (dev, - nm256_ac97_initial_values[x].port, - nm256_ac97_initial_values[x].value); + if (! card->mixer_values_init) { + for (x = 0; nm256_ac97_initial_values[x].port != 0xffff; x++) { + ac97_put_register (dev, + nm256_ac97_initial_values[x].port, + nm256_ac97_initial_values[x].value); + card->mixer_values_init = 1; + } } return 0; } -/* We don't do anything special here. */ +/* + * We don't do anything particularly special here; it just passes the + * mixer ioctl to the AC97 driver. + */ static int nm256_default_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) { @@ -774,7 +938,12 @@ nm256_default_mixer_ioctl }; -/* I "love" C sometimes. Got braces? */ +/* + * Default settings for the OSS mixer. These are set last, after the + * mixer is initialized. + * + * I "love" C sometimes. Got braces? + */ static struct ac97_mixer_value_list mixer_defaults[] = { { SOUND_MIXER_VOLUME, { { 85, 85 } } }, { SOUND_MIXER_SPEAKER, { { 100 } } }, @@ -783,6 +952,8 @@ { -1, { { 0, 0 } } } }; + +/* Installs the AC97 mixer into CARD. */ static int nm256_install_mixer (struct nm256_info *card) { @@ -812,36 +983,67 @@ return 0; } -/* See if the signature left by the NM256 BIOS is intact; if so, we use - the associated address as the end of our buffer. */ +/* Perform a full reset on the hardware; this is invoked when an APM + resume event occurs. */ +static void +nm256_full_reset (struct nm256_info *card) +{ + nm256_initHw (card); + ac97_reset (&(card->mdev)); +} + +/* + * See if the signature left by the NM256 BIOS is intact; if so, we use + * the associated address as the end of our audio buffer in the video + * RAM. + */ + static void -nm256_peek_for_sig (struct nm256_info *card, u32 port1addr) +nm256_peek_for_sig (struct nm256_info *card) { - char *temp = ioremap_nocache (port1addr + card->port1_end - 0x0400, 16); + u32 port1offset + = card->port[0].physaddr + card->port[0].end_offset - 0x0400; + /* The signature is located 1K below the end of video RAM. */ + char *temp = ioremap_nocache (port1offset, 16); + /* Default buffer end is 5120 bytes below the top of RAM. */ + u32 default_value = card->port[0].end_offset - 0x1400; u32 sig; + /* Install the default value first, so we don't have to repeatedly + do it if there is a problem. */ + card->port[0].end_offset = default_value; + if (temp == NULL) { printk (KERN_ERR "NM256: Unable to scan for card signature in video RAM\n"); return; } - memcpy_fromio (&sig, temp, sizeof (u32)); - if ((sig & 0xffff0000) == 0x4e4d0000) { - memcpy_fromio (&(card->bufend), temp + 4, sizeof (u32)); + sig = readl (temp); + if ((sig & NM_SIG_MASK) == NM_SIGNATURE) { + u32 pointer = readl (temp + 4); + + /* + * If it's obviously invalid, don't use it (the port already has a + * suitable default value set). + */ + if (pointer != 0xffffffff) + card->port[0].end_offset = pointer; + printk (KERN_INFO "NM256: Found card signature in video RAM: 0x%x\n", - card->bufend); + pointer); } release_region ((unsigned long) temp, 16); } -/* Install a driver for the soundcard referenced by PCIDEV. */ +/* + * Install a driver for the PCI device referenced by PCIDEV. + * VERSTR is a human-readable version string. + */ static int nm256_install(struct pci_dev *pcidev, enum nm256rev rev, char *verstr) { struct nm256_info *card; - u32 port1addr = (pcidev->base_address[0] & PCI_BASE_ADDRESS_MEM_MASK); - u32 port2addr = (pcidev->base_address[1] & PCI_BASE_ADDRESS_MEM_MASK); int x; card = kmalloc (sizeof (struct nm256_info), GFP_KERNEL); @@ -855,52 +1057,85 @@ card->recording = 0; card->rev = rev; - /* The NM256 has two memory ports. The first port is nothing - more than a chunk of video RAM, which is used as the I/O ring - buffer. The second port has the actual juicy stuff (like the - mixer and the playback engine control registers). */ - - card->ports[1] = ioremap_nocache (port2addr, NM_PORT2_SIZE); + /* Init the memory port info. */ + for (x = 0; x < 2; x++) { + card->port[x].physaddr = RSRCADDRESS (pcidev, x); + card->port[x].ptr = NULL; + card->port[x].start_offset = 0; + card->port[x].end_offset = 0; + } - if (card->ports[1] == NULL) { - printk (KERN_ERR "NM256: Unable to remap port 2\n"); + /* Port 2 is easy. */ + card->port[1].start_offset = 0; + card->port[1].end_offset = NM_PORT2_SIZE; + + /* Yuck. But we have to map in port 2 so we can check how much RAM the + card has. */ + if (nm256_remap_ports (card)) { kfree_s (card, sizeof (struct nm256_info)); return 0; } + /* + * The NM256 has two memory ports. The first port is nothing + * more than a chunk of video RAM, which is used as the I/O ring + * buffer. The second port has the actual juicy stuff (like the + * mixer and the playback engine control registers). + */ + if (card->rev == REV_NM256AV) { - card->port1_end = 2560 * 1024; + /* Ok, try to see if this is a non-AC97 version of the hardware. */ + int pval = nm256_readPort16 (card, 2, NM_MIXER_PRESENCE); + if ((pval & NM_PRESENCE_MASK) != NM_PRESENCE_VALUE) { + if (! force_load) { + printk (KERN_ERR "NM256: This doesn't look to me like the AC97-compatible version.\n"); + printk (KERN_ERR " You can force the driver to load by passing in the module\n"); + printk (KERN_ERR " parameter:\n"); + printk (KERN_ERR " force_ac97 = 1\n"); + printk (KERN_ERR "\n"); + printk (KERN_ERR " More likely, you should be using the appropriate SB-16 or\n"); + printk (KERN_ERR " CS4232 driver instead. (If your BIOS has settings for\n"); + printk (KERN_ERR " IRQ and/or DMA for the sound card, this is *not* the correct\n"); + printk (KERN_ERR " driver to use.)\n"); + nm256_release_ports (card); + kfree_s (card, sizeof (struct nm256_info)); + return 0; + } + else { + printk (KERN_INFO "NM256: Forcing driver load as per user request.\n"); + } + } + else { + printk (KERN_INFO "NM256: Congratulations. You're not running Eunice.\n"); + } + card->port[0].end_offset = 2560 * 1024; card->introutine = nm256_interrupt; + card->mixer_status_offset = NM_MIXER_STATUS_OFFSET; + card->mixer_status_mask = NM_MIXER_READY_MASK; } else { + /* Not sure if there is any relevant detect for the ZX or not. */ if (nm256_readPort8 (card, 2, 0xa0b) != 0) - card->port1_end = 6144 * 1024; + card->port[0].end_offset = 6144 * 1024; else - card->port1_end = 4096 * 1024; + card->port[0].end_offset = 4096 * 1024; card->introutine = nm256_interrupt_zx; + card->mixer_status_offset = NM2_MIXER_STATUS_OFFSET; + card->mixer_status_mask = NM2_MIXER_READY_MASK; } - /* Default value. */ - card->bufend = card->port1_end - 0x1400; - - if (buffertop >= 98304 && buffertop < card->port1_end) - card->bufend = buffertop; + if (buffertop >= 98304 && buffertop < card->port[0].end_offset) + card->port[0].end_offset = buffertop; else - nm256_peek_for_sig (card, port1addr); + nm256_peek_for_sig (card); - card->port1_start = card->bufend - 98304; + card->port[0].start_offset = card->port[0].end_offset - 98304; printk (KERN_INFO "NM256: Mapping port 1 from 0x%x - 0x%x\n", - card->port1_start, card->port1_end); + card->port[0].start_offset, card->port[0].end_offset); - card->ports[0] = - ioremap_nocache (port1addr + card->port1_start, - card->port1_end - card->port1_start); - - if (card->ports[0] == NULL) { - printk (KERN_ERR "NM256: Unable to remap port 1\n"); - release_region ((unsigned long) card->ports[1], NM_PORT2_SIZE); + if (nm256_remap_ports (card)) { kfree_s (card, sizeof (struct nm256_info)); return 0; } @@ -911,9 +1146,7 @@ card->has_irq = 0; if (nm256_grabInterrupt (card) != 0) { - release_region ((unsigned long) card->ports[0], - card->port1_end - card->port1_start); - release_region ((unsigned long) card->ports[1], NM_PORT2_SIZE); + nm256_release_ports (card); kfree_s (card, sizeof (struct nm256_info)); return 0; } @@ -924,10 +1157,36 @@ * Init the board. */ + card->playbackBufferSize = 16384; + card->recordBufferSize = 16384; + + card->coeffBuf = card->port[0].end_offset - NM_MAX_COEFFICIENT; + card->abuf2 = card->coeffBuf - card->recordBufferSize; + card->abuf1 = card->abuf2 - card->playbackBufferSize; + card->allCoeffBuf = card->abuf2 - (NM_TOTAL_COEFF_COUNT * 4); + + /* Fixed setting. */ + card->mixer = NM_MIXER_OFFSET; + card->mixer_values_init = 0; + + card->is_open_play = 0; + card->is_open_record = 0; + + card->coeffsCurrent = 0; + + card->opencnt[0] = 0; card->opencnt[1] = 0; + + /* Reasonable default settings, but largely unnecessary. */ + for (x = 0; x < 2; x++) { + card->sinfo[x].bits = 8; + card->sinfo[x].stereo = 0; + card->sinfo[x].samplerate = 8000; + } + nm256_initHw (card); for (x = 0; x < 2; x++) { - if ((card->dev[x] = + if ((card->dev[x] = sound_install_audiodrv(AUDIO_DRIVER_VERSION, "NM256", &nm256_audio_driver, sizeof(struct audio_driver), @@ -940,9 +1199,7 @@ } else { printk(KERN_ERR "NM256: Too many PCM devices available\n"); - release_region ((unsigned long) card->ports[0], - card->port1_end - card->port1_start); - release_region ((unsigned long) card->ports[1], NM_PORT2_SIZE); + nm256_release_ports (card); kfree_s (card, sizeof (struct nm256_info)); return 0; } @@ -964,6 +1221,48 @@ return 1; } + +#ifdef CONFIG_APM +/* + * APM event handler, so the card is properly reinitialized after a power + * event. + */ +static int +handle_apm_event (apm_event_t event) +{ + static int down = 0; + + switch (event) + { + case APM_SYS_SUSPEND: + case APM_USER_SUSPEND: + down++; + break; + case APM_NORMAL_RESUME: + case APM_CRITICAL_RESUME: + if (down) + { + struct nm256_info *crd; + + down = 0; + for (crd = nmcard_list; crd != NULL; crd = crd->next_card) + { + int playing = crd->playing; + nm256_full_reset (crd); + /* + * A little ugly, but that's ok; pretend the + * block we were playing is done. + */ + if (playing) + DMAbuf_outputintr (crd->dev_for_play, 1); + } + } + break; + } + return 0; +} +#endif + /* * This loop walks the PCI configuration database and finds where * the sound cards are. @@ -993,6 +1292,10 @@ if (count == 0) return -ENODEV; +#ifdef CONFIG_APM + apm_register_callback (&handle_apm_event); +#endif + printk (KERN_INFO "Done installing NM256 audio driver.\n"); return 0; } @@ -1028,10 +1331,13 @@ if (! ((mode & OPEN_READ) || (mode & OPEN_WRITE))) return -EIO; - /* If it's open for both read and write, and the card's currently - being read or written to, then do the opposite of what has - already been done. Otherwise, don't specify any mode until the - user actually tries to do I/O. */ + /* + * If it's open for both read and write, and the card's currently + * being read or written to, then do the opposite of what has + * already been done. Otherwise, don't specify any mode until the + * user actually tries to do I/O. (Some programs open the device + * for both read and write, but only actually do reading or writing.) + */ if ((mode & OPEN_WRITE) && (mode & OPEN_READ)) { if (card->is_open_play) @@ -1105,6 +1411,7 @@ } } +/* Standard ioctl handler. */ static int nm256_audio_ioctl(int dev, unsigned int cmd, caddr_t arg) { @@ -1122,6 +1429,11 @@ else w = 1; + /* + * The code here is messy. There are probably better ways to do + * it. (It should be possible to handle it the same way the AC97 mixer + * is done.) + */ switch (cmd) { case SOUND_PCM_WRITE_RATE: @@ -1193,8 +1505,12 @@ return put_user(ret, (int *) arg); } -/* Given the dev DEV and an associated physical buffer PHYSBUF, return - a pointer to the actual buffer in kernel space. */ +/* + * Given the sound device DEV and an associated physical buffer PHYSBUF, + * return a pointer to the actual buffer in kernel space. + * + * This routine should exist as part of the soundcore routines. + */ static char * nm256_getDMAbuffer (int dev, unsigned long physbuf) @@ -1238,9 +1554,10 @@ } } +/* Ditto, but do recording instead. */ static void nm256_audio_start_input(int dev, unsigned long physbuf, int count, - int intrflag) + int intrflag) { struct nm256_info *card = nm256_find_card (dev); @@ -1252,6 +1569,12 @@ } } +/* + * Prepare for inputting samples to DEV. + * Each requested buffer will be BSIZE byes long, with a total of + * BCOUNT buffers. + */ + static int nm256_audio_prepare_for_input(int dev, int bsize, int bcount) { @@ -1278,6 +1601,7 @@ * 2. We get a write buffer without dma_mode setup (dmabuf.c:1152) * 3. We restart a transfer (dmabuf.c:1324) */ + static int nm256_audio_prepare_for_output(int dev, int bsize, int bcount) { @@ -1342,12 +1666,13 @@ MODULE_PARM (usecache, "i"); MODULE_PARM (buffertop, "i"); MODULE_PARM (nm256_debug, "i"); +MODULE_PARM (force_load, "i"); int init_module (void) { nmcard_list = NULL; - printk (KERN_INFO "NeoMagic 256AV/256ZX audio driver, version 1.0\n"); + printk (KERN_INFO "NeoMagic 256AV/256ZX audio driver, version 1.1\n"); if (init_nm256 () == 0) { SOUND_LOCK; @@ -1372,9 +1697,7 @@ stopRecord (card); if (card->has_irq) free_irq (card->irq, card); - release_region ((unsigned long) card->ports[0], - card->port1_end - card->port1_start); - release_region ((unsigned long) card->ports[1], NM_PORT2_SIZE); + nm256_release_ports (card); sound_unload_mixerdev (card->mixer_oss_dev); sound_unload_audiodev (card->dev[0]); sound_unload_audiodev (card->dev[1]); @@ -1383,6 +1706,9 @@ } nmcard_list = NULL; } +#ifdef CONFIG_APM + apm_unregister_callback (&handle_apm_event); +#endif } #endif diff -u --recursive --new-file v2.2.13/linux/drivers/sound/nm256_coeff.h linux/drivers/sound/nm256_coeff.h --- v2.2.13/linux/drivers/sound/nm256_coeff.h Tue Jan 4 11:10:39 2000 +++ linux/drivers/sound/nm256_coeff.h Tue Jan 4 10:12:22 2000 @@ -4622,7 +4622,8 @@ } static void -nm256_loadOneCoefficient (struct nm256_info *card, u32 port, u16 which) +nm256_loadOneCoefficient (struct nm256_info *card, int devnum, u32 port, + u16 which) { u32 coeffBuf = (which < 8) ? card->coeffBuf : card->allCoeffBuf; u16 offset = nm256_getStartOffset (which); @@ -4631,11 +4632,14 @@ card->coeffsCurrent = 0; if (nm256_debug) - printk (KERN_INFO "NM256: Loading coefficient buffer 0x%x-0x%x with coefficient %d\n", - coeffBuf, coeffBuf + size - 1, which); + printk (KERN_INFO "NM256: Loading coefficient buffer 0x%x-0x%x with coefficient %d, size %d, port 0x%x\n", + coeffBuf, coeffBuf + size - 1, which, size, port); nm256_writeBuffer8 (card, coefficients + offset, 1, coeffBuf, size); nm256_writePort32 (card, 2, port + 0, coeffBuf); - nm256_writePort32 (card, 2, port + 4, coeffBuf + size - 1); + /* ??? Record seems to behave differently than playback. */ + if (devnum == 0) + size--; + nm256_writePort32 (card, 2, port + 4, coeffBuf + size); } static void @@ -4663,7 +4667,7 @@ number += 8; if (! nm256_cachedCoefficients (card)) - nm256_loadOneCoefficient (card, addrs[which], number); + nm256_loadOneCoefficient (card, which, addrs[which], number); else { u32 base = card->allCoeffBuf; u32 offset = nm256_getStartOffset (number); diff -u --recursive --new-file v2.2.13/linux/drivers/sound/opl3.c linux/drivers/sound/opl3.c --- v2.2.13/linux/drivers/sound/opl3.c Wed Aug 19 14:48:30 1998 +++ linux/drivers/sound/opl3.c Tue Jan 4 10:12:22 2000 @@ -32,8 +32,6 @@ #include "sound_config.h" #include "soundmodule.h" -#ifdef CONFIG_YM3812 - #include "opl3.h" #define MAX_VOICE 18 @@ -1223,5 +1221,3 @@ EXPORT_SYMBOL(opl3_init); EXPORT_SYMBOL(opl3_detect); MODULE_PARM(io, "i"); - -#endif diff -u --recursive --new-file v2.2.13/linux/drivers/sound/sb_audio.c linux/drivers/sound/sb_audio.c --- v2.2.13/linux/drivers/sound/sb_audio.c Thu Jan 14 22:59:47 1999 +++ linux/drivers/sound/sb_audio.c Tue Jan 4 10:12:22 2000 @@ -460,7 +460,7 @@ speed = 44100; if (devc->opened & OPEN_READ && speed > 15000) speed = 15000; - devc->tconst = ((65536 - ((256000000 + s / 2) / s)) >> 8) & 0xff; + devc->tconst = (256 - ((1000000 + s / 2) / s)) & 0xff; tmp = 256 - devc->tconst; speed = ((1000000 + tmp / 2) / tmp) / devc->channels; @@ -591,7 +591,7 @@ if (speed > 44100) speed = 44100; - devc->tconst = ((65536 - ((256000000 + s / 2) / s)) >> 8) & 0xff; + devc->tconst = (256 - ((1000000 + s / 2) / s)) & 0xff; tmp = 256 - devc->tconst; speed = ((1000000 + tmp / 2) / tmp) / devc->channels; diff -u --recursive --new-file v2.2.13/linux/drivers/sound/sb_ess.c linux/drivers/sound/sb_ess.c --- v2.2.13/linux/drivers/sound/sb_ess.c Tue Oct 19 17:10:38 1999 +++ linux/drivers/sound/sb_ess.c Tue Jan 4 10:12:22 2000 @@ -10,29 +10,29 @@ * * History: * - * Rolf Fokkens (Dec 20 1998): ES188x recording level support on a per + * Rolf Fokkens (Dec 20 1998): ES188x recording level support on a per * fokkensr@vertis.nl input basis. - * (Dec 24 1998): Recognition of ES1788, ES1887, ES1888, + * (Dec 24 1998): Recognition of ES1788, ES1887, ES1888, * ES1868, ES1869 and ES1878. Could be used for * specific handling in the future. All except * ES1887 and ES1888 and ES688 are handled like * ES1688. - * (Dec 27 1998): RECLEV for all (?) ES1688+ chips. ES188x now + * (Dec 27 1998): RECLEV for all (?) ES1688+ chips. ES188x now * have the "Dec 20" support + RECLEV - * (Jan 2 1999): Preparation for Full Duplex. This means + * (Jan 2 1999): Preparation for Full Duplex. This means * Audio 2 is now used for playback when dma16 * is specified. The next step would be to use * Audio 1 and Audio 2 at the same time. - * (Jan 9 1999): Put all ESS stuff into sb_ess.[ch], this + * (Jan 9 1999): Put all ESS stuff into sb_ess.[ch], this * includes both the ESS stuff that has been in * sb_*[ch] before I touched it and the ESS support * I added later - * (Jan 23 1999): Full Duplex seems to work. I wrote a small + * (Jan 23 1999): Full Duplex seems to work. I wrote a small * test proggy which works OK. Haven't found * any applications to test it though. So why did * I bother to create it anyway?? :) Just for * fun. - * (May 2 1999): I tried to be too smart by "introducing" + * (May 2 1999): I tried to be too smart by "introducing" * ess_calc_best_speed (). The idea was that two * dividers could be used to setup a samplerate, * ess_calc_best_speed () would choose the best. @@ -40,10 +40,12 @@ * recording problems for high samplerates. I * fixed this by removing ess_calc_best_speed () * and just doing what the documentation says. - * Andy Sloane (June 4 1999): Stole some code from ALSA to fix the playback + * Andy Sloane (Jun 4 1999): Stole some code from ALSA to fix the playback * andy@guildsoftware.com speed on ES1869, ES1879, ES1887, and ES1888. * 1879's were previously ignored by this driver; * added (untested) support for those. + * Cvetan Ivanov (Oct 27 1999): Fixed ess_dsp_init to call ess_set_dma_hw for + * zezo@inet.bg _ALL_ ESS models, not only ES1887 * * This files contains ESS chip specifics. It's based on the existing ESS * handling as it resided in sb_common.c, sb_mixer.c and sb_audio.c. This @@ -52,7 +54,7 @@ * - RECLEV support for ES1688 and later * - 6 bits playback level support chips later than ES1688 * - Recording level support on a per-device basis for ES1887 - * - Full-Duplex for ES1887 (under development) + * - Full-Duplex for ES1887 * * Full duplex is enabled by specifying dma16. While the normal dma must * be one of 0, 1 or 3, dma16 can be one of 0, 1, 3 or 5. DMA 5 is a 16 bit @@ -1305,6 +1307,13 @@ int ess_dsp_init (sb_devc *devc, struct address_info *hw_config) { /* + * Caller also checks this, but anyway + */ + if (devc->model != MDL_ESS) { + printk (KERN_INFO "ess_dsp_init for non ESS chip\n"); + return 1; + } + /* * This for ES1887 to run Full Duplex. Actually ES1888 * is allowed to do so too. I have no idea yet if this * will work for ES1888 however. @@ -1324,15 +1333,12 @@ if (devc->dma8 != devc->dma16 && devc->dma16 != -1) { devc->duplex = 1; } - - if (!ess_set_dma_hw (devc)) { - free_irq(devc->irq, devc); - return 0; - } - return 1; - } else { - return -1; } + if (!ess_set_dma_hw (devc)) { + free_irq(devc->irq, devc); + return 0; + } + return 1; } /**************************************************************************** diff -u --recursive --new-file v2.2.13/linux/drivers/sound/sonicvibes.c linux/drivers/sound/sonicvibes.c --- v2.2.13/linux/drivers/sound/sonicvibes.c Tue Jan 4 11:10:39 2000 +++ linux/drivers/sound/sonicvibes.c Tue Jan 4 10:12:22 2000 @@ -71,6 +71,14 @@ * 15.06.99 0.15 Fix bad allocation bug. * Thanks to Deti Fliegl * 28.06.99 0.16 Add pci_set_master + * 03.08.99 0.17 adapt to Linus' new __setup/__initcall + * added kernel command line options "sonicvibes=reverb" and "sonicvibesdmaio=dmaioaddr" + * 12.08.99 0.18 module_init/__setup fixes + * 24.08.99 0.19 get rid of the dmaio kludge, replace with allocate_resource + * 31.08.99 0.20 add spin_lock_init + * __initlocaldata to fix gcc 2.7.x problems + * 03.09.99 0.21 change read semantics for MIDI to match + * OSS more closely; remove possible wakeup race * */ @@ -1265,9 +1273,9 @@ current->state = TASK_RUNNING; return -EBUSY; } - tmo = (count * HZ) / s->ratedac; + tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->ratedac; tmo >>= sample_shift[(s->fmt >> SV_CFMT_ASHIFT) & SV_CFMT_MASK]; - if (!schedule_timeout(tmo ? : 1) && tmo) + if (!schedule_timeout(tmo + 1)) printk(KERN_DEBUG "sv: dma timed out??\n"); } remove_wait_queue(&s->dma_dac.wait, &wait); @@ -1870,6 +1878,7 @@ static ssize_t sv_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct sv_state *s = (struct sv_state *)file->private_data; + struct wait_queue wait = { current, NULL }; ssize_t ret; unsigned long flags; unsigned ptr; @@ -1880,7 +1889,10 @@ return -ESPIPE; if (!access_ok(VERIFY_WRITE, buffer, count)) return -EFAULT; + if (count == 0) + return 0; ret = 0; + add_wait_queue(&s->midi.iwait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); ptr = s->midi.ird; @@ -1891,15 +1903,25 @@ if (cnt > count) cnt = count; if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->midi.iwait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) - return ret ? ret : -EFAULT; + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } ptr = (ptr + cnt) % MIDIINBUF; spin_lock_irqsave(&s->lock, flags); s->midi.ird = ptr; @@ -1908,13 +1930,17 @@ count -= cnt; buffer += cnt; ret += cnt; + break; } + current->state = TASK_RUNNING; + remove_wait_queue(&s->midi.iwait, &wait); return ret; } static ssize_t sv_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct sv_state *s = (struct sv_state *)file->private_data; + struct wait_queue wait = { current, NULL }; ssize_t ret; unsigned long flags; unsigned ptr; @@ -1925,7 +1951,10 @@ return -ESPIPE; if (!access_ok(VERIFY_READ, buffer, count)) return -EFAULT; + if (count == 0) + return 0; ret = 0; + add_wait_queue(&s->midi.owait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); ptr = s->midi.owr; @@ -1938,15 +1967,25 @@ if (cnt > count) cnt = count; if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->midi.owait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) - return ret ? ret : -EFAULT; + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } ptr = (ptr + cnt) % MIDIOUTBUF; spin_lock_irqsave(&s->lock, flags); s->midi.owr = ptr; @@ -1959,6 +1998,8 @@ sv_handle_midi(s); spin_unlock_irqrestore(&s->lock, flags); } + current->state = TASK_RUNNING; + remove_wait_queue(&s->midi.owait, &wait); return ret; } @@ -2324,7 +2365,7 @@ if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "sv: version v0.16 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "sv: version v0.21 time " __TIME__ " " __DATE__ "\n"); #if 0 if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT))) printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n"); @@ -2353,6 +2394,7 @@ init_waitqueue(&s->midi.iwait); init_waitqueue(&s->midi.owait); s->open_sem = MUTEX; + spin_lock_init(&s->lock); s->magic = SV_MAGIC; s->iosb = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; s->ioenh = pcidev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK; @@ -2423,8 +2465,8 @@ wrindir(s, SV_CIDRIVECONTROL, 0); /* drive current 16mA */ wrindir(s, SV_CIENABLE, s->enable = 0); /* disable DMAA and DMAC */ outb(~(SV_CINTMASK_DMAA | SV_CINTMASK_DMAC), s->ioenh + SV_CODEC_INTMASK); - //outb(0xff, s->iodmaa + SV_DMA_RESET); - //outb(0xff, s->iodmac + SV_DMA_RESET); + /* outb(0xff, s->iodmaa + SV_DMA_RESET); */ + /* outb(0xff, s->iodmac + SV_DMA_RESET); */ inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */ wrindir(s, SV_CIADCCLKSOURCE, 0); /* use pll as ADC clock source */ wrindir(s, SV_CIANALOGPWRDOWN, 0); /* power up the analog parts of the device */ @@ -2524,8 +2566,8 @@ synchronize_irq(); inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */ wrindir(s, SV_CIENABLE, 0); /* disable DMAA and DMAC */ - //outb(0, s->iodmaa + SV_DMA_RESET); - //outb(0, s->iodmac + SV_DMA_RESET); + /*outb(0, s->iodmaa + SV_DMA_RESET);*/ + /*outb(0, s->iodmac + SV_DMA_RESET);*/ free_irq(s->irq, s); release_region(s->iodmac, SV_EXTENT_DMA); release_region(s->iodmaa, SV_EXTENT_DMA); diff -u --recursive --new-file v2.2.13/linux/drivers/sound/sound_core.c linux/drivers/sound/sound_core.c --- v2.2.13/linux/drivers/sound/sound_core.c Tue Oct 19 17:10:38 1999 +++ linux/drivers/sound/sound_core.c Tue Jan 4 10:12:22 2000 @@ -55,6 +55,9 @@ #ifdef CONFIG_SOUND_SONICVIBES extern int init_sonicvibes(void); #endif +#ifdef CONFIG_SOUND_MAESTRO +extern int init_maestro(void); +#endif #ifdef CONFIG_SOUND_ES1370 extern int init_es1370(void); #endif @@ -409,6 +412,9 @@ #endif #ifdef CONFIG_SOUND_ES1371 init_es1371(); +#endif +#ifdef CONFIG_SOUND_MAESTRO + init_maestro(); #endif #ifdef CONFIG_SOUND_MSNDCLAS msnd_classic_init(); diff -u --recursive --new-file v2.2.13/linux/drivers/sound/sscape.c linux/drivers/sound/sscape.c --- v2.2.13/linux/drivers/sound/sscape.c Mon Apr 12 16:18:27 1999 +++ linux/drivers/sound/sscape.c Tue Jan 4 10:12:22 2000 @@ -3,7 +3,6 @@ * * Low level driver for Ensoniq SoundScape */ - /* * Copyright (C) by Hannu Savolainen 1993-1997 * @@ -11,9 +10,10 @@ * Version 2 (June 1991). See the "COPYING" file distributed with this software * for more info. */ - + /* - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Sergey Smitienko : ensoniq p'n'p support */ #include @@ -21,6 +21,26 @@ #include "sound_config.h" #include "soundmodule.h" +#include "sound_firmware.h" + +#include +#include +#include +#include +#include +#include +#include +#ifdef __KERNEL__ +#include +#include +#include +#include +#include +#include +#endif /* __KERNEL__ */ +#include +#include + #ifdef CONFIG_SSCAPE @@ -71,10 +91,10 @@ #define CMD_GET_BOARD_TYPE 0x82 #define CMD_SET_CONTROL 0x88 /* Old firmware only */ #define CMD_GET_CONTROL 0x89 /* Old firmware only */ -#define CTL_MASTER_VOL 0 -#define CTL_MIC_MODE 2 -#define CTL_SYNTH_VOL 4 -#define CTL_WAVE_VOL 7 +#define CTL_MASTER_VOL 0 +#define CTL_MIC_MODE 2 +#define CTL_SYNTH_VOL 4 +#define CTL_WAVE_VOL 7 #define CMD_SET_EXTMIDI 0x8a #define CMD_GET_EXTMIDI 0x8b #define CMD_SET_MT32 0x8c @@ -82,9 +102,20 @@ #define CMD_ACK 0x80 +#define IC_ODIE 1 +#define IC_OPUS 2 + typedef struct sscape_info { int base, irq, dma; + + int codec, codec_irq; /* required to setup pnp cards*/ + int codec_type; + int ic_type; + char* raw_buf; + unsigned long raw_buf_phys; + int buffsize; /* -------------------------- */ + int ok; /* Properly detected */ int failed; int dma_allocated; @@ -152,6 +183,31 @@ restore_flags(flags); } +static unsigned char sscape_pnp_read_codec(sscape_info* devc, unsigned char reg) +{ + unsigned char res; + unsigned long flags; + + save_flags(flags); + cli(); + outb( reg, devc -> codec); + res = inb (devc -> codec + 1); + restore_flags(flags); + return res; + +} + +static void sscape_pnp_write_codec(sscape_info* devc, unsigned char reg, unsigned char data) +{ + unsigned long flags; + + save_flags(flags); + cli(); + outb( reg, devc -> codec); + outb( data, devc -> codec + 1); + restore_flags(flags); +} + static void host_open(struct sscape_info *devc) { outb((0x00), PORT(HOST_CTRL)); /* Put the board to the host mode */ @@ -218,6 +274,13 @@ return data; } +static int host_command1(struct sscape_info *devc, int cmd) +{ + unsigned char buf[10]; + buf[0] = (unsigned char) (cmd & 0xff); + return host_write(devc, buf, 1); +} + static int host_command2(struct sscape_info *devc, int cmd, int parm1) { @@ -544,6 +607,7 @@ }; static int sscape_detected = 0; +static int sscape_is_pnp = 0; void attach_sscape(struct address_info *hw_config) { @@ -563,14 +627,14 @@ */ #define SSCAPE_REGS { \ /* I0 */ 0x00, \ - 0xf0, /* Note! Ignored. Set always to 0xf0 */ \ - 0x20, /* Note! Ignored. Set always to 0x20 */ \ - 0x20, /* Note! Ignored. Set always to 0x20 */ \ - 0xf5, /* Ignored */ \ - 0x10, \ - 0x00, \ - 0x2e, /* I7 MEM config A. Likely to vary between models */ \ - 0x00, /* I8 MEM config B. Likely to vary between models */ \ +/* I1 */ 0xf0, /* Note! Ignored. Set always to 0xf0 */ \ +/* I2 */ 0x20, /* Note! Ignored. Set always to 0x20 */ \ +/* I3 */ 0x20, /* Note! Ignored. Set always to 0x20 */ \ +/* I4 */ 0xf5, /* Ignored */ \ +/* I5 */ 0x10, \ +/* I6 */ 0x00, \ +/* I7 */ 0x2e, /* I7 MEM config A. Likely to vary between models */ \ +/* I8 */ 0x00, /* I8 MEM config B. Likely to vary between models */ \ /* I9 */ 0x40 /* Ignored */ \ } #endif @@ -605,11 +669,13 @@ printk(KERN_ERR "Invalid IRQ%d\n", hw_config->irq); return; } - save_flags(flags); - cli(); - - for (i = 1; i < 10; i++) - { + + if (sscape_is_pnp == 0) { + + save_flags(flags); + cli(); + for (i = 1; i < 10; i++) + { switch (i) { case 1: /* Host interrupt enable */ @@ -640,9 +706,9 @@ default: sscape_write(devc, i, regs[i]); } + } + restore_flags(flags); } - restore_flags(flags); - #ifdef SSCAPE_DEBUG2 /* * Temporary debugging aid. Print contents of the registers after @@ -733,6 +799,452 @@ return 1; } +static int sscape_read_host_ctrl(sscape_info* devc) +{ + return host_read(devc); +} + +static void sscape_write_host_ctrl2(sscape_info *devc, int a, int b) +{ + host_command2(devc, a, b); +} + +static int sscape_alloc_dma(sscape_info *devc) +{ + char *start_addr, *end_addr; + int i, dma_pagesize; + int sz, size; + + if (devc->raw_buf != NULL) return 0; /* Already done */ + dma_pagesize = (devc->dma < 4) ? (64 * 1024) : (128 * 1024); + devc->raw_buf = NULL; + devc->buffsize = 8192*4; + if (devc->buffsize > dma_pagesize) devc->buffsize = dma_pagesize; + start_addr = NULL; + /* + * Now loop until we get a free buffer. Try to get smaller buffer if + * it fails. Don't accept smaller than 8k buffer for performance + * reasons. + */ + while (start_addr == NULL && devc->buffsize > PAGE_SIZE) { + for (sz = 0, size = PAGE_SIZE; size < devc->buffsize; sz++, size <<= 1); + devc->buffsize = PAGE_SIZE * (1 << sz); + start_addr = (char *) __get_free_pages(GFP_ATOMIC|GFP_DMA, sz); + if (start_addr == NULL) devc->buffsize /= 2; + } + + if (start_addr == NULL) { + printk(KERN_ERR "sscape pnp init error: Couldn't allocate DMA buffer\n"); + return 0; + } else { + /* make some checks */ + end_addr = start_addr + devc->buffsize - 1; + /* now check if it fits into the same dma-pagesize */ + + if (((long) start_addr & ~(dma_pagesize - 1)) != ((long) end_addr & ~(dma_pagesize - 1)) + || end_addr >= (char *) (MAX_DMA_ADDRESS)) { + printk(KERN_ERR "sscape pnp: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, devc->buffsize); + return 0; + } + } + devc->raw_buf = start_addr; + devc->raw_buf_phys = virt_to_bus(start_addr); + + for (i = MAP_NR(start_addr); i <= MAP_NR(end_addr); i++) + set_bit(PG_reserved, &mem_map[i].flags);; + return 1; +} + +static void sscape_free_dma(sscape_info *devc) +{ + int sz, size, i; + unsigned long start_addr, end_addr; + + if (devc->raw_buf == NULL) return; + for (sz = 0, size = PAGE_SIZE; size < devc->buffsize; sz++, size <<= 1); + start_addr = (unsigned long) devc->raw_buf; + end_addr = start_addr + devc->buffsize; + + for (i = MAP_NR(start_addr); i <= MAP_NR(end_addr); i++) + clear_bit(PG_reserved, &mem_map[i].flags);; + + free_pages((unsigned long) devc->raw_buf, sz); + devc->raw_buf = NULL; +} + +/* Intel version !!!!!!!!! */ + +static int sscape_start_dma(int chan, unsigned long physaddr, int count, int dma_mode) +{ + unsigned long flags; + + flags = claim_dma_lock(); + disable_dma(chan); + clear_dma_ff(chan); + set_dma_mode(chan, dma_mode); + set_dma_addr(chan, physaddr); + set_dma_count(chan, count); + enable_dma(chan); + release_dma_lock(flags); + return 0; +} + +static void sscape_pnp_start_dma(sscape_info* devc, int arg ) +{ + int reg; + if (arg == 0) reg = 2; + else reg = 3; + + sscape_write(devc, reg, sscape_read( devc, reg) | 0x01); + sscape_write(devc, reg, sscape_read( devc, reg) & 0xFE); +} + +static int sscape_pnp_wait_dma (sscape_info* devc, int arg ) +{ + int reg; + unsigned long i; + unsigned char d; + + if (arg == 0) reg = 2; + else reg = 3; + + sleep ( 1 ); + i = 0; + do { + d = sscape_read(devc, reg) & 1; + if ( d == 1) break; + i++; + } while (i < 500000); + d = sscape_read(devc, reg) & 1; + return d; +} + +static int sscape_pnp_alloc_dma(sscape_info* devc) +{ + /* printk(KERN_INFO "sscape: requesting dma\n"); */ + if (request_dma(devc -> dma, "sscape")) return 0; + /* printk(KERN_INFO "sscape: dma channel allocated\n"); */ + if (!sscape_alloc_dma(devc)) { + free_dma(devc -> dma); + return 0; + }; + return 1; +} + +static void sscape_pnp_free_dma(sscape_info* devc) +{ + sscape_free_dma( devc); + free_dma(devc -> dma ); + /* printk(KERN_INFO "sscape: dma released\n"); */ +} + +static int sscape_pnp_upload_file(sscape_info* devc, char* fn) +{ + int done = 0; + int timeout_val; + char* data,*dt; + int len,l; + unsigned long flags; + + sscape_write( devc, 9, sscape_read(devc, 9 ) & 0x3F ); + sscape_write( devc, 2, (devc -> dma << 4) | 0x80 ); + sscape_write( devc, 3, 0x20 ); + sscape_write( devc, 9, sscape_read( devc, 9 ) | 0x80 ); + + len = mod_firmware_load(fn, &data); + if (len == 0) { + printk(KERN_ERR "sscape: file not found: %s\n", fn); + return 0; + } + dt = data; + save_flags(flags); + cli(); + while ( len > 0 ) { + if (len > devc -> buffsize) l = devc->buffsize; + else l = len; + len -= l; + memcpy(devc->raw_buf, dt, l); dt += l; + sscape_start_dma(devc->dma, devc->raw_buf_phys, l, 0x48); + sscape_pnp_start_dma ( devc, 0 ); + if (sscape_pnp_wait_dma ( devc, 0 ) == 0) return 0; + } + + restore_flags(flags); + vfree(data); + + outb(0, devc -> base + 2); + outb(0, devc -> base); + + sscape_write ( devc, 9, sscape_read( devc, 9 ) | 0x40); + + timeout_val = 5 * HZ; + while (!done && timeout_val-- > 0) + { + unsigned char x; + sleep(1); + x = inb( devc -> base + 3); + if (x == 0xff || x == 0xfe) /* OBP startup acknowledge */ + { + //printk(KERN_ERR "Soundscape: Acknowledge = %x\n", x); + done = 1; + } + } + timeout_val = 5 * HZ; + done = 0; + while (!done && timeout_val-- > 0) + { + unsigned char x; + sleep(1); + x = inb( devc -> base + 3); + if (x == 0xfe) /* OBP startup acknowledge */ + { + //printk(KERN_ERR "Soundscape: Acknowledge = %x\n", x); + done = 1; + } + } + + if ( !done ) printk(KERN_ERR "soundscape: OBP Initialization failed.\n"); + + sscape_write( devc, 2, devc->ic_type == IC_ODIE ? 0x70 : 0x40); + sscape_write( devc, 3, (devc -> dma << 4) + 0x80); + return 1; +} + +static void sscape_pnp_init_hw(sscape_info* devc) +{ + unsigned char midi_irq = 0, sb_irq = 0; + unsigned i; + static char code_file_name[23] = "/sndscape/sndscape.cox"; + + int sscape_sb_enable = 0; + int sscape_joystic_enable = 0x7f; + int sscape_mic_enable = 0; + int sscape_ext_midi = 0; + + if ( !sscape_pnp_alloc_dma(devc) ) { + printk(KERN_ERR "sscape: faild to allocate dma\n"); + return; + } + + for (i = 0; i < 4; i++) { + if ( devc -> irq == valid_interrupts[i] ) + midi_irq = i; + if ( devc -> codec_irq == valid_interrupts[i] ) + sb_irq = i; + } + + sscape_write( devc, 5, 0x50); + sscape_write( devc, 7, 0x2e); + sscape_write( devc, 8, 0x00); + + sscape_write( devc, 2, devc->ic_type == IC_ODIE ? 0x70 : 0x40); + sscape_write( devc, 3, ( devc -> dma << 4) | 0x80); + + if ( sscape_sb_enable ) + sscape_write (devc, 4, 0xF0 | (sb_irq << 2) | midi_irq); + else + sscape_write (devc, 4, 0xF0 | (midi_irq<<2) | midi_irq); + + i = 0x10; //sscape_read(devc, 9) & (devc->ic_type == IC_ODIE ? 0xf0 : 0xc0); + if ( sscape_sb_enable ) + i |= devc->ic_type == IC_ODIE ? 0x05 : 0x07; + if (sscape_joystic_enable) i |= 8; + + sscape_write (devc, 9, i); + sscape_write (devc, 6, 0x80); + sscape_write (devc, 1, 0x80); + + if (devc -> codec_type == 2) { + sscape_pnp_write_codec( devc, 0x0C, 0x50); + sscape_pnp_write_codec( devc, 0x10, sscape_pnp_read_codec( devc, 0x10) & 0x3F); + sscape_pnp_write_codec( devc, 0x11, sscape_pnp_read_codec( devc, 0x11) | 0xC0); + sscape_pnp_write_codec( devc, 29, 0x20); + } + + if (sscape_pnp_upload_file(devc, "/sndscape/scope.cod") == 0 ) { + printk(KERN_ERR "sscape: faild to upload file /sndscape/scope.cod\n"); + sscape_pnp_free_dma(devc); + return; + } + + i = sscape_read_host_ctrl( devc ); + + if ( (i & 0x0F) > 7 ) { + printk(KERN_ERR "sscape: scope.cod faild\n"); + sscape_pnp_free_dma(devc); + return; + } + if ( i & 0x10 ) sscape_write( devc, 7, 0x2F); + code_file_name[21] = (char) ( i & 0x0F) + 0x30; + if (sscape_pnp_upload_file( devc, code_file_name) == 0) { + printk(KERN_ERR "sscape: faild to upload file %s\n", code_file_name); + sscape_pnp_free_dma(devc); + return; + } + + if (devc->ic_type != IC_ODIE) { + sscape_pnp_write_codec( devc, 10, (sscape_pnp_read_codec(devc, 10) & 0x7f) | + ( sscape_mic_enable == 0 ? 0x00 : 0x80) ); + } + sscape_write_host_ctrl2( devc, 0x84, 0x32 ); + sscape_write_host_ctrl2( devc, 0x86, 0x32 ); + sscape_write_host_ctrl2( devc, 0x8A, sscape_ext_midi); + + sscape_pnp_write_codec ( devc, 6, 0x3f ); //WAV_VOL + sscape_pnp_write_codec ( devc, 7, 0x3f ); //WAV_VOL + sscape_pnp_write_codec ( devc, 2, 0x1F ); //WD_CDXVOLL + sscape_pnp_write_codec ( devc, 3, 0x1F ); //WD_CDXVOLR + + if (devc -> codec_type == 1) { + sscape_pnp_write_codec ( devc, 4, 0x1F ); + sscape_pnp_write_codec ( devc, 5, 0x1F ); + sscape_write_host_ctrl2( devc, 0x88, sscape_mic_enable); + } else { + int t; + sscape_pnp_write_codec ( devc, 0x10, 0x1F << 1); + sscape_pnp_write_codec ( devc, 0x11, 0xC0 | (0x1F << 1)); + + t = sscape_pnp_read_codec( devc, 0x00) & 0xDF; + if ( (sscape_mic_enable == 0)) t |= 0; + else t |= 0x20; + sscape_pnp_write_codec ( devc, 0x00, t); + t = sscape_pnp_read_codec( devc, 0x01) & 0xDF; + if ( (sscape_mic_enable == 0) ) t |= 0; + else t |= 0x20; + sscape_pnp_write_codec ( devc, 0x01, t); + sscape_pnp_write_codec ( devc, 0x40 | 29 , 0x20); + outb(0, devc -> codec); + } + if (devc -> ic_type == IC_OPUS ) { + int i = sscape_read( devc, 9 ); + sscape_write( devc, 9, i | 3 ); + sscape_write( devc, 3, 0x40); + + if (check_region(0x228, 1)) { + outb(0, 0x228); + release_region(0x228,1); + } + sscape_write( devc, 3, (devc -> dma << 4) | 0x80); + sscape_write( devc, 9, i ); + } + + host_close ( devc ); + sscape_pnp_free_dma(devc); +} + +static int detect_sscape_pnp(sscape_info* devc) +{ + long i, irq_bits = 0xff; + unsigned int d; + + DDB(printk("Entered detect_sscape_pnp(%x)\n", devc->base)); + + if (check_region(devc->base, 8)) { + printk(KERN_ERR "detect_sscape_pnp: port %x is not free\n", devc->base); + return 0; + } + + if (check_region(devc->codec, 2)) { + printk(KERN_ERR "detect_sscape_pnp: port %x is not free\n", devc->codec); + return 0; + } + + if ( (inb( devc -> base + 2) & 0x78) != 0) return 0; + + d = inb ( devc -> base + 4) & 0xF0; + if ( (d & 0x80) != 0) return 0; + + if (d == 0) { + devc->codec_type = 1; + devc->ic_type = IC_ODIE; + } + else if ( (d & 0x60) != 0) { + devc->codec_type = 2; + devc->ic_type = IC_OPUS; + } + else if ( (d & 0x40) != 0) { + devc->codec_type = 2; + devc->ic_type = IC_ODIE; + } + else return 0; + + sscape_is_pnp = 1; + + outb(0xFA, devc -> base+4); + if ((inb( devc -> base+4) & 0x9F) != 0x0A) + return 0; + outb(0xFE, devc -> base+4); + if ( (inb(devc -> base+4) & 0x9F) != 0x0E) + return 0; + if ( (inb(devc -> base+5) & 0x9F) != 0x0E) + return 0; + + if (devc->codec_type == 2) { + if (devc -> codec != devc -> base + 8) + printk("soundscape warning: incorrect codec port specified\n"); + devc -> codec = devc -> base + 8; + d = 0x10 | (sscape_read(devc, 9) & 0xCF); + sscape_write(devc, 9, d); + sscape_write(devc, 6, 0x80); + } else { + //todo: check codec is not base + 8 + } + + d = (sscape_read(devc, 9) & 0x3F) | 0xC0; + sscape_write(devc, 9, d); + + for (i = 0; i < 550000; i++) + if ( !(inb(devc -> codec) & 0x80) ) break; + + d = inb(devc -> codec); + if (d & 0x80) + return 0; + if ( inb(devc -> codec + 2) == 0xFF) + return 0; + + sscape_write(devc, 9, sscape_read(devc, 9) & 0x3F ); + + d = inb(devc -> codec) & 0x80; + if ( d == 0) { + printk(KERN_INFO "soundscape: hardware detected\n"); + valid_interrupts = valid_interrupts_new; + } else { + printk(KERN_INFO "soundscape: board looks like media fx\n"); + valid_interrupts = valid_interrupts_old; + old_hardware = 1; + } + + sscape_write( devc, 9, 0xC0 | (sscape_read(devc, 9) & 0x3F) ); + + for (i = 0; i < 550000; i++) + if ( !(inb(devc -> codec) & 0x80)) + break; + + sscape_pnp_init_hw(devc); + + for (i = 0; i < sizeof(valid_interrupts); i++) + { + if (devc->codec_irq == valid_interrupts[i]) { + irq_bits = i; + break; + } + } + sscape_write(devc, GA_INTENA_REG, 0x00); + sscape_write(devc, GA_DMACFG_REG, 0x50); + sscape_write(devc, GA_DMAA_REG, 0x70); + sscape_write(devc, GA_DMAB_REG, 0x20); + sscape_write(devc, GA_INTCFG_REG, 0xf0); + sscape_write(devc, GA_CDCFG_REG, 0x89 | (devc->dma << 4) | (irq_bits << 1)); + + sscape_pnp_write_codec( devc, 0, sscape_pnp_read_codec( devc, 0) | 0x20); + sscape_pnp_write_codec( devc, 0, sscape_pnp_read_codec( devc, 1) | 0x20); + + release_region(devc->codec, 2); + release_region(devc->base, 8); + + return 1; +} + int probe_sscape(struct address_info *hw_config) { @@ -758,8 +1270,13 @@ #endif devc->failed = 1; - if (!detect_ga(devc)) - return 0; + if (!detect_ga(devc)) { + if (detect_sscape_pnp(devc)) { + sscape_detected = hw_config->io_base; + return 1; + } + else return 0; + } if (old_hardware) /* Check that it's really an old Spea/Reveal card. */ { @@ -781,11 +1298,11 @@ { int i, irq_bits = 0xff; int ad_flags = 0; - + if (devc->failed) { - printk(KERN_ERR "soundscape: Card not detected\n"); - return 0; + printk(KERN_ERR "soundscape: Card not detected\n"); + return 0; } if (devc->ok == 0) { @@ -805,9 +1322,19 @@ printk(KERN_ERR "soundscape: Invalid MSS IRQ%d\n", hw_config->irq); return 0; } - if (old_hardware) - ad_flags = 0x12345677; /* Tell that we may have a CS4248 chip (Spea-V7 Media FX) */ - return ad1848_detect(hw_config->io_base, &ad_flags, hw_config->osp); + + if (!sscape_is_pnp) { + if (old_hardware) + ad_flags = 0x12345677; /* Tell that we may have a CS4248 chip (Spea-V7 Media FX) */ + return ad1848_detect(hw_config->io_base, &ad_flags, hw_config->osp); + } + else { + if (old_hardware) + ad_flags = 0x12345677; /* Tell that we may have a CS4248 chip (Spea-V7 Media FX) */ + else + ad_flags = 0x87654321; /* Tell that we have a soundscape pnp with 1845 chip */ + return ad1848_detect(hw_config->io_base, &ad_flags, hw_config->osp); + } } void attach_ss_ms_sound(struct address_info *hw_config) @@ -820,44 +1347,52 @@ int i, irq_bits = 0xff; - hw_config->dma = devc->dma; /* Share the DMA with the ODIE/OPUS chip */ - - /* - * Setup the DMA polarity. - */ - - sscape_write(devc, GA_DMACFG_REG, 0x50); - - /* - * Take the gate-array off of the DMA channel. - */ - - sscape_write(devc, GA_DMAB_REG, 0x20); - - /* - * Init the AD1848 (CD-ROM) config reg. - */ - - for (i = 0; i < sizeof(valid_interrupts); i++) - { - if (hw_config->irq == valid_interrupts[i]) - { - irq_bits = i; - break; - } - } - sscape_write(devc, GA_CDCFG_REG, 0x89 | (hw_config->dma << 4) | (irq_bits << 1)); - - if (hw_config->irq == devc->irq) - printk(KERN_WARNING "soundscape: Warning! The WSS mode can't share IRQ with MIDI\n"); - - hw_config->slots[0] = ad1848_init("SoundScape", hw_config->io_base, - hw_config->irq, - hw_config->dma, - hw_config->dma, - 0, - devc->osp); - + + if (!sscape_is_pnp) /*pnp is already setup*/ + { + /* + * Setup the DMA polarity. + */ + sscape_write(devc, GA_DMACFG_REG, 0x50); + + /* + * Take the gate-array off of the DMA channel. + */ + sscape_write(devc, GA_DMAB_REG, 0x20); + + /* + * Init the AD1848 (CD-ROM) config reg. + */ + for (i = 0; i < sizeof(valid_interrupts); i++) + { + if (hw_config->irq == valid_interrupts[i]) + { + irq_bits = i; + break; + } + } + sscape_write(devc, GA_CDCFG_REG, 0x89 | (hw_config->dma << 4) | (irq_bits << 1)); + } + + if (hw_config->irq == devc->irq) + printk(KERN_WARNING "soundscape: Warning! The WSS mode can't share IRQ with MIDI\n"); + + if (! sscape_is_pnp ) + hw_config->slots[0] = ad1848_init("SoundScape", hw_config->io_base, + hw_config->irq, + hw_config->dma, + hw_config->dma, + 0, + devc->osp); + + else + hw_config->slots[0] = ad1848_init("SoundScape PNP", hw_config->io_base, + hw_config->irq, + hw_config->dma, + hw_config->dma, + 0, + devc->osp); + if (hw_config->slots[0] != -1) /* The AD1848 driver installed itself */ { audio_devs[hw_config->slots[0]]->coproc = &sscape_coproc_operations; @@ -867,6 +1402,7 @@ /* Set proper routings here (what are they) */ AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE); } + #ifdef SSCAPE_DEBUG5 /* * Temporary debugging aid. Print contents of the registers @@ -937,6 +1473,13 @@ printk(KERN_ERR "CONFIG_MPU_IRQ must be specified if CONFIG_MPU_IO is set.\n"); return -EINVAL; } + + devc->codec = io; + devc->codec_irq = irq; + devc->codec_type = 0; + devc->ic_type = 0; + devc->raw_buf = NULL; + config.irq = irq; config.dma = dma; config.io_base = io; diff -u --recursive --new-file v2.2.13/linux/drivers/telephony/Config.in linux/drivers/telephony/Config.in --- v2.2.13/linux/drivers/telephony/Config.in Wed Dec 31 16:00:00 1969 +++ linux/drivers/telephony/Config.in Tue Jan 4 10:12:22 2000 @@ -0,0 +1,9 @@ +# +# Telephony device configuration +# +mainmenu_option next_comment +comment 'Telephony Support' + +tristate 'Linux telephony support' CONFIG_PHONE +dep_tristate 'QuickNet Internet LineJack/PhoneJack support' CONFIG_PHONE_IXJ $CONFIG_PHONE +endmenu diff -u --recursive --new-file v2.2.13/linux/drivers/telephony/Makefile linux/drivers/telephony/Makefile --- v2.2.13/linux/drivers/telephony/Makefile Wed Dec 31 16:00:00 1969 +++ linux/drivers/telephony/Makefile Tue Jan 4 10:12:22 2000 @@ -0,0 +1,35 @@ +# +# Makefile for the kernel miscellaneous drivers. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now inherited from the +# parent makes.. + +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) + +L_TARGET := telephony.a +MX_OBJS := +M_OBJS := + +ifeq ($(CONFIG_PHONE),y) + LX_OBJS += phonedev.o +else + ifeq ($(CONFIG_PHONE),m) + MX_OBJS += phonedev.o + endif +endif + +ifeq ($(CONFIG_PHONE_IXJ),y) + L_OBJS += ixj.o +else + ifeq ($(CONFIG_PHONE_IXJ),m) + M_OBJS += ixj.o + endif +endif + +include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.2.13/linux/drivers/telephony/ixj.c linux/drivers/telephony/ixj.c --- v2.2.13/linux/drivers/telephony/ixj.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/telephony/ixj.c Tue Jan 4 10:12:22 2000 @@ -0,0 +1,7790 @@ +/* + * ixj.c + * + * Device Driver for the Internet PhoneJACK and + * Internet LineJACK Telephony Cards. + * + * (c) Copyright 1999 Quicknet Technologies, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Author: Ed Okerson, + * + * Contributors: Greg Herlein, + * David W. Erhart, + * John Sellers, + * Mike Preston, + * + * Fixes: + * + * More information about the hardware related to this driver can be found + * at our website: http://www.quicknet.net + * + */ + +static char ixj_c_rcsid[] = "$Id: ixj.c,v 3.4 1999/12/16 22:18:36 root Exp root $"; + +//#define PERFMON_STATS +#define IXJDEBUG 0 +#define MAXRINGS 5 + +#include + +#include +#include +#include /* printk() */ +#include /* everything... */ +#include /* error codes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_ISAPNP +#include "../pnp/pnpio.h" +#include "../pnp/isapnp.h" +#endif + +#include "ixj.h" + +#define TYPE(dev) (MINOR(dev) >> 4) +#define NUM(dev) (MINOR(dev) & 0xf) + +static int ixjdebug = 0; +static int hertz = HZ; +static int samplerate = 100; + +MODULE_PARM(ixjdebug, "i"); + +static IXJ ixj[IXJMAX]; + +static struct timer_list ixj_timer; + +int ixj_convert_loaded = 0; + +/************************************************************************ +* +* These are function definitions to allow external modules to register +* enhanced functionality call backs. +* +************************************************************************/ + +static int Stub(IXJ * J, unsigned long arg) +{ + return 0; +} + +static IXJ_REGFUNC ixj_DownloadG729 = &Stub; +static IXJ_REGFUNC ixj_DownloadTS85 = &Stub; +static IXJ_REGFUNC ixj_PreRead = &Stub; +static IXJ_REGFUNC ixj_PostRead = &Stub; +static IXJ_REGFUNC ixj_PreWrite = &Stub; +static IXJ_REGFUNC ixj_PostWrite = &Stub; +static IXJ_REGFUNC ixj_PreIoctl = &Stub; +static IXJ_REGFUNC ixj_PostIoctl = &Stub; + +static void ixj_read_frame(int board); +static void ixj_write_frame(int board); +static void ixj_init_timer(void); +static void ixj_add_timer(void); +static void ixj_del_timer(void); +static void ixj_timeout(unsigned long ptr); +static int read_filters(int board); +static int LineMonitor(int board); +static int ixj_fasync(int fd, struct file *, int mode); +static int ixj_hookstate(int board); +static int ixj_record_start(int board); +static void ixj_record_stop(int board); +static int ixj_play_start(int board); +static void ixj_play_stop(int board); +static int ixj_set_tone_on(unsigned short arg, int board); +static int ixj_set_tone_off(unsigned short, int board); +static int ixj_play_tone(int board, char tone); +static int idle(int board); +static void ixj_ring_on(int board); +static void ixj_ring_off(int board); +static void aec_stop(int board); +static void ixj_ringback(int board); +static void ixj_busytone(int board); +static void ixj_dialtone(int board); +static void ixj_cpt_stop(int board); +static char daa_int_read(int board); +static int daa_set_mode(int board, int mode); +static int ixj_linetest(int board); +static int ixj_daa_cid_read(int board); +static void DAA_Coeff_US(int board); +static void DAA_Coeff_UK(int board); +static void DAA_Coeff_France(int board); +static void DAA_Coeff_Germany(int board); +static void DAA_Coeff_Australia(int board); +static void DAA_Coeff_Japan(int board); +static int ixj_init_filter(int board, IXJ_FILTER * jf); +static int ixj_init_tone(int board, IXJ_TONE * ti); +static int ixj_build_cadence(int board, IXJ_CADENCE * cp); +// Serial Control Interface funtions +static int SCI_Control(int board, int control); +static int SCI_Prepare(int board); +static int SCI_WaitHighSCI(int board); +static int SCI_WaitLowSCI(int board); +static DWORD PCIEE_GetSerialNumber(WORD wAddress); + +/************************************************************************ +CT8020/CT8021 Host Programmers Model +Host address Function Access +DSPbase + +0-1 Aux Software Status Register (reserved) Read Only +2-3 Software Status Register Read Only +4-5 Aux Software Control Register (reserved) Read Write +6-7 Software Control Register Read Write +8-9 Hardware Status Register Read Only +A-B Hardware Control Register Read Write +C-D Host Transmit (Write) Data Buffer Access Port (buffer input)Write Only +E-F Host Recieve (Read) Data Buffer Access Port (buffer input) Read Only +************************************************************************/ + +extern __inline__ void ixj_read_HSR(int board) +{ + ixj[board].hsr.bytes.low = inb_p(ixj[board].DSPbase + 8); + ixj[board].hsr.bytes.high = inb_p(ixj[board].DSPbase + 9); +} +extern __inline__ int IsControlReady(int board) +{ + ixj_read_HSR(board); + return ixj[board].hsr.bits.controlrdy ? 1 : 0; +} + +extern __inline__ int IsStatusReady(int board) +{ + ixj_read_HSR(board); + return ixj[board].hsr.bits.statusrdy ? 1 : 0; +} + +extern __inline__ int IsRxReady(int board) +{ + ixj_read_HSR(board); + return ixj[board].hsr.bits.rxrdy ? 1 : 0; +} + +extern __inline__ int IsTxReady(int board) +{ + ixj_read_HSR(board); + return ixj[board].hsr.bits.txrdy ? 1 : 0; +} + +extern __inline__ BYTE SLIC_GetState(int board) +{ + IXJ *j = &ixj[board]; + + j->pld_slicr.byte = inb_p(j->XILINXbase + 0x01); + + return j->pld_slicr.bits.state; +} + +static BOOL SLIC_SetState(BYTE byState, int board) +{ + BOOL fRetVal = FALSE; + IXJ *j = &ixj[board]; + + // Set the C1, C2, C3 & B2EN signals. + switch (byState) { + case PLD_SLIC_STATE_OC: + j->pld_slicw.bits.c1 = 0; + j->pld_slicw.bits.c2 = 0; + j->pld_slicw.bits.c3 = 0; + j->pld_slicw.bits.b2en = 0; + outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01); + fRetVal = TRUE; + break; + case PLD_SLIC_STATE_RINGING: + j->pld_slicw.bits.c1 = 1; + j->pld_slicw.bits.c2 = 0; + j->pld_slicw.bits.c3 = 0; + j->pld_slicw.bits.b2en = 1; + outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01); + fRetVal = TRUE; + break; + case PLD_SLIC_STATE_ACTIVE: + j->pld_slicw.bits.c1 = 0; + j->pld_slicw.bits.c2 = 1; + j->pld_slicw.bits.c3 = 0; + j->pld_slicw.bits.b2en = 0; + outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01); + fRetVal = TRUE; + break; + case PLD_SLIC_STATE_OHT: // On-hook transmit + + j->pld_slicw.bits.c1 = 1; + j->pld_slicw.bits.c2 = 1; + j->pld_slicw.bits.c3 = 0; + j->pld_slicw.bits.b2en = 0; + outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01); + fRetVal = TRUE; + break; + case PLD_SLIC_STATE_TIPOPEN: + j->pld_slicw.bits.c1 = 0; + j->pld_slicw.bits.c2 = 0; + j->pld_slicw.bits.c3 = 1; + j->pld_slicw.bits.b2en = 0; + outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01); + fRetVal = TRUE; + break; + case PLD_SLIC_STATE_STANDBY: + j->pld_slicw.bits.c1 = 1; + j->pld_slicw.bits.c2 = 0; + j->pld_slicw.bits.c3 = 1; + j->pld_slicw.bits.b2en = 1; + outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01); + fRetVal = TRUE; + break; + case PLD_SLIC_STATE_APR: // Active polarity reversal + + j->pld_slicw.bits.c1 = 0; + j->pld_slicw.bits.c2 = 1; + j->pld_slicw.bits.c3 = 1; + j->pld_slicw.bits.b2en = 0; + outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01); + fRetVal = TRUE; + break; + case PLD_SLIC_STATE_OHTPR: // OHT polarity reversal + + j->pld_slicw.bits.c1 = 1; + j->pld_slicw.bits.c2 = 1; + j->pld_slicw.bits.c3 = 1; + j->pld_slicw.bits.b2en = 0; + outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01); + fRetVal = TRUE; + break; + default: + fRetVal = FALSE; + break; + } + + return fRetVal; +} + +int ixj_register(int index, IXJ_REGFUNC regfunc) +{ + int cnt; + int retval = 0; + switch (index) { + case G729LOADER: + ixj_DownloadG729 = regfunc; + for (cnt = 0; cnt < IXJMAX; cnt++) + ixj_DownloadG729(&ixj[cnt], 0L); + break; + case TS85LOADER: + ixj_DownloadTS85 = regfunc; + for (cnt = 0; cnt < IXJMAX; cnt++) + ixj_DownloadTS85(&ixj[cnt], 0L); + break; + case PRE_READ: + ixj_PreRead = regfunc; + break; + case POST_READ: + ixj_PostRead = regfunc; + break; + case PRE_WRITE: + ixj_PreWrite = regfunc; + break; + case POST_WRITE: + ixj_PostWrite = regfunc; + break; + case PRE_IOCTL: + ixj_PreIoctl = regfunc; + break; + case POST_IOCTL: + ixj_PostIoctl = regfunc; + break; + default: + retval = 1; + } + return retval; +} + +int ixj_unregister(int index) +{ + int retval = 0; + switch (index) { + case G729LOADER: + ixj_DownloadG729 = &Stub; + break; + case TS85LOADER: + ixj_DownloadTS85 = &Stub; + break; + case PRE_READ: + ixj_PreRead = &Stub; + break; + case POST_READ: + ixj_PostRead = &Stub; + break; + case PRE_WRITE: + ixj_PreWrite = &Stub; + break; + case POST_WRITE: + ixj_PostWrite = &Stub; + break; + case PRE_IOCTL: + ixj_PreIoctl = &Stub; + break; + case POST_IOCTL: + ixj_PostIoctl = &Stub; + break; + default: + retval = 1; + } + return retval; +} + +static void ixj_init_timer(void) +{ + init_timer(&ixj_timer); + ixj_timer.function = ixj_timeout; + ixj_timer.data = (int) NULL; +} + +static void ixj_add_timer(void) +{ + ixj_timer.expires = jiffies + (hertz / samplerate); + add_timer(&ixj_timer); +} + +static void ixj_del_timer(void) +{ + del_timer(&ixj_timer); +} + +static void ixj_tone_timeout(int board) +{ + IXJ *j = &ixj[board]; + IXJ_TONE ti; + + j->tone_state++; + if (j->tone_state == 3) { + j->tone_state = 0; + if (j->cadence_t) { + j->tone_cadence_state++; + if (j->tone_cadence_state >= j->cadence_t->elements_used) + { + switch (j->cadence_t->termination) + { + case PLAY_ONCE: + ixj_cpt_stop(board); + break; + case REPEAT_LAST_ELEMENT: + j->tone_cadence_state--; + ixj_play_tone(board, j->cadence_t->ce[j->tone_cadence_state].index); + break; + case REPEAT_ALL: + j->tone_cadence_state = 0; + if (j->cadence_t->ce[j->tone_cadence_state].freq0) + { + ti.tone_index = j->cadence_t->ce[j->tone_cadence_state].index; + ti.freq0 = j->cadence_t->ce[j->tone_cadence_state].freq0; + ti.gain0 = j->cadence_t->ce[j->tone_cadence_state].gain0; + ti.freq1 = j->cadence_t->ce[j->tone_cadence_state].freq1; + ti.gain1 = j->cadence_t->ce[j->tone_cadence_state].gain1; + ixj_init_tone(board, &ti); + } + ixj_set_tone_on(j->cadence_t->ce[0].tone_on_time, board); + ixj_set_tone_off(j->cadence_t->ce[0].tone_off_time, board); + ixj_play_tone(board, j->cadence_t->ce[0].index); + break; + } + } else { + if (j->cadence_t->ce[j->tone_cadence_state].gain0) + { + ti.tone_index = j->cadence_t->ce[j->tone_cadence_state].index; + ti.freq0 = j->cadence_t->ce[j->tone_cadence_state].freq0; + ti.gain0 = j->cadence_t->ce[j->tone_cadence_state].gain0; + ti.freq1 = j->cadence_t->ce[j->tone_cadence_state].freq1; + ti.gain1 = j->cadence_t->ce[j->tone_cadence_state].gain1; + ixj_init_tone(board, &ti); + } + ixj_set_tone_on(j->cadence_t->ce[j->tone_cadence_state].tone_on_time, board); + ixj_set_tone_off(j->cadence_t->ce[j->tone_cadence_state].tone_off_time, board); + ixj_play_tone(board, j->cadence_t->ce[j->tone_cadence_state].index); + } + } + } +} + +static void ixj_timeout(unsigned long ptr) +{ + int board; + unsigned long jifon; + IXJ *j; + + for (board = 0; board < IXJMAX; board++) + { + j = &ixj[board]; + + if (j->DSPbase) + { +#ifdef PERFMON_STATS + j->timerchecks++; +#endif + if (j->tone_state) + { + if (!ixj_hookstate(board)) + { + ixj_cpt_stop(board); + if (j->m_hook) + { + j->m_hook = 0; + j->ex.bits.hookstate = 1; + if (j->async_queue) + kill_fasync(j->async_queue, SIGIO); // Send apps notice of change + } + goto timer_end; + } + if (j->tone_state == 1) + jifon = (hertz * j->tone_on_time * 25 / 100000); + else + jifon = (hertz * j->tone_on_time * 25 / 100000) + + (hertz * j->tone_off_time * 25 / 100000); + if (jiffies < j->tone_start_jif + jifon) { + if (j->tone_state == 1) { + ixj_play_tone(board, j->tone_index); + if (j->dsp.low == 0x20) { + goto timer_end; + } + } else { + ixj_play_tone(board, 0); + if (j->dsp.low == 0x20) { + goto timer_end; + } + } + } else { + ixj_tone_timeout(board); + if (j->flags.dialtone) { + ixj_dialtone(board); + } + if (j->flags.busytone) { + ixj_busytone(board); + if (j->dsp.low == 0x20) { + goto timer_end; + } + } + if (j->flags.ringback) { + ixj_ringback(board); + if (j->dsp.low == 0x20) { + goto timer_end; + } + } + if (!j->tone_state) { + if (j->dsp.low == 0x20 || (j->play_mode == -1 && j->rec_mode == -1)) + idle(board); + if (j->dsp.low == 0x20 && j->play_mode != -1) + ixj_play_start(board); + if (j->dsp.low == 0x20 && j->rec_mode != -1) + ixj_record_start(board); + } + } + } + if (!j->tone_state || j->dsp.low != 0x20) { + if (IsRxReady(board)) { + ixj_read_frame(board); + } + if (IsTxReady(board)) { + ixj_write_frame(board); + } + } + if (j->flags.cringing) { + if (ixj_hookstate(board) & 1) { + j->flags.cringing = 0; + ixj_ring_off(board); + } else { + if (jiffies - j->ring_cadence_jif >= (.5 * hertz)) { + j->ring_cadence_t--; + if (j->ring_cadence_t == -1) + j->ring_cadence_t = 15; + j->ring_cadence_jif = jiffies; + } + if (j->ring_cadence & 1 << j->ring_cadence_t) { + ixj_ring_on(board); + } else { + ixj_ring_off(board); + } + goto timer_end; + } + } + if (!j->flags.ringing) { + if (ixj_hookstate(board)) { + if ((j->dsp.low == 0x21 || j->dsp.low == 0x22 )&& + j->pld_slicr.bits.state != PLD_SLIC_STATE_ACTIVE) + // Internet LineJACK + { + SLIC_SetState(PLD_SLIC_STATE_ACTIVE, board); + } + LineMonitor(board); + read_filters(board); + ixj_WriteDSPCommand(0x511B, board); + j->proc_load = j->ssr.high << 8 | j->ssr.low; + if (!j->m_hook) { + j->m_hook = j->ex.bits.hookstate = 1; + if (j->async_queue) + kill_fasync(j->async_queue, SIGIO); // Send apps notice of change + } + } else { + if ((j->dsp.low == 0x21 || j->dsp.low == 0x22) && + j->pld_slicr.bits.state == PLD_SLIC_STATE_ACTIVE) + // Internet LineJACK + { + SLIC_SetState(PLD_SLIC_STATE_STANDBY, board); + } + if (j->ex.bits.dtmf_ready) { + j->dtmf_wp = j->dtmf_rp = j->ex.bits.dtmf_ready = 0; + } + if (j->m_hook) { + j->m_hook = 0; + j->ex.bits.hookstate = 1; + if (j->async_queue) + kill_fasync(j->async_queue, SIGIO); // Send apps notice of change + } + } + } + if (j->cardtype == 300) { + if (j->flags.pstn_present) { + j->pld_scrr.byte = inb_p(j->XILINXbase); + if (jiffies >= j->pstn_sleeptil && j->pld_scrr.bits.daaflag) { + daa_int_read(board); + if (j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.bitreg.RING) { + if (!j->flags.pstn_ringing) { + j->flags.pstn_ringing = 1; + if (j->daa_mode != SOP_PU_RINGING) + daa_set_mode(board, SOP_PU_RINGING); + } + } + if (j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.bitreg.VDD_OK) { + j->pstn_winkstart = 0; + if (j->flags.pstn_ringing && !j->pstn_envelope) { + j->ex.bits.pstn_ring = 0; + j->pstn_envelope = 1; + j->pstn_ring_start = jiffies; + } + } else { + if (j->flags.pstn_ringing && j->pstn_envelope && + jiffies > j->pstn_ring_start + ((hertz * 15) / 10)) { + j->ex.bits.pstn_ring = 1; + j->pstn_envelope = 0; + } else if (j->daa_mode == SOP_PU_CONVERSATION) { + if (!j->pstn_winkstart) { + j->pstn_winkstart = jiffies; + } else if (jiffies > j->pstn_winkstart + (hertz * j->winktime / 1000)) { + daa_set_mode(board, SOP_PU_SLEEP); + j->pstn_winkstart = 0; + j->ex.bits.pstn_wink = 1; + } + } else { + j->ex.bits.pstn_ring = 0; + } + } + if (j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.bitreg.Cadence) { + if (j->daa_mode == SOP_PU_RINGING) { + daa_set_mode(board, SOP_PU_SLEEP); + j->flags.pstn_ringing = 0; + j->ex.bits.pstn_ring = 0; + } + } + if (j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.bitreg.Caller_ID) { + if (j->daa_mode == SOP_PU_RINGING && j->flags.pstn_ringing) { + j->pstn_cid_intr = 1; + j->pstn_cid_recieved = jiffies; + } + } + } else { + if (j->pld_scrr.bits.daaflag) { + daa_int_read(board); + } + j->ex.bits.pstn_ring = 0; + if (j->pstn_cid_intr && jiffies > j->pstn_cid_recieved + (hertz * 3)) { + if (j->daa_mode == SOP_PU_RINGING) { + ixj_daa_cid_read(board); + j->ex.bits.caller_id = 1; + } + j->pstn_cid_intr = 0; + } else { + j->ex.bits.caller_id = 0; + } + if (!j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.bitreg.VDD_OK) { + if (j->flags.pstn_ringing && j->pstn_envelope) { + j->ex.bits.pstn_ring = 1; + j->pstn_envelope = 0; + } else if (j->daa_mode == SOP_PU_CONVERSATION) { + if (!j->pstn_winkstart) { + j->pstn_winkstart = jiffies; + } else if (jiffies > j->pstn_winkstart + (hertz * 320 / 1000)) { + daa_set_mode(board, SOP_PU_SLEEP); + j->pstn_winkstart = 0; + j->ex.bits.pstn_wink = 1; + } + } + } + } + } + } + if ((j->ex.bits.f0 || j->ex.bits.f1 || j->ex.bits.f2 || j->ex.bits.f3) + && j->filter_cadence) { + } + if (j->ex.bytes) { + wake_up_interruptible(&j->poll_q); // Wake any blocked selects + if (j->async_queue) + kill_fasync(j->async_queue, SIGIO); // Send apps notice of change + } + } else { + break; + } + } +timer_end: + ixj_add_timer(); +} + +static int ixj_status_wait(int board) +{ + unsigned long jif; + + jif = jiffies; + while (!IsStatusReady(board)) { + if (jiffies - jif > (60 * (hertz / 100))) { + return -1; + } + } + return 0; +} + +int ixj_WriteDSPCommand(unsigned short cmd, int board) +{ + BYTES bytes; + unsigned long jif; + + bytes.high = (cmd & 0xFF00) >> 8; + bytes.low = cmd & 0x00FF; + jif = jiffies; + while (!IsControlReady(board)) { + if (jiffies - jif > (60 * (hertz / 100))) { + return -1; + } + } + outb_p(bytes.low, ixj[board].DSPbase + 6); + outb_p(bytes.high, ixj[board].DSPbase + 7); + + if (ixj_status_wait(board)) { + ixj[board].ssr.low = 0xFF; + ixj[board].ssr.high = 0xFF; + return -1; + } +/* Read Software Status Register */ + ixj[board].ssr.low = inb_p(ixj[board].DSPbase + 2); + ixj[board].ssr.high = inb_p(ixj[board].DSPbase + 3); + return 0; +} + +/*************************************************************************** +* +* General Purpose IO Register read routine +* +***************************************************************************/ +extern __inline__ int ixj_gpio_read(int board) +{ + if (ixj_WriteDSPCommand(0x5143, board)) + return -1; + + ixj[board].gpio.bytes.low = ixj[board].ssr.low; + ixj[board].gpio.bytes.high = ixj[board].ssr.high; + + return 0; +} + +extern __inline__ void LED_SetState(int state, int board) +{ + if (ixj[board].dsp.low == 0x21) { + ixj[board].pld_scrw.bits.led1 = state & 0x1 ? 1 : 0; + ixj[board].pld_scrw.bits.led2 = state & 0x2 ? 1 : 0; + ixj[board].pld_scrw.bits.led3 = state & 0x4 ? 1 : 0; + ixj[board].pld_scrw.bits.led4 = state & 0x8 ? 1 : 0; + + outb_p(ixj[board].pld_scrw.byte, ixj[board].XILINXbase); + } +} + +/********************************************************************* +* GPIO Pins are configured as follows on the Quicknet Internet +* PhoneJACK Telephony Cards +* +* POTS Select GPIO_6=0 GPIO_7=0 +* Mic/Speaker Select GPIO_6=0 GPIO_7=1 +* Handset Select GPIO_6=1 GPIO_7=0 +* +* SLIC Active GPIO_1=0 GPIO_2=1 GPIO_5=0 +* SLIC Ringing GPIO_1=1 GPIO_2=1 GPIO_5=0 +* SLIC Open Circuit GPIO_1=0 GPIO_2=0 GPIO_5=0 +* +* Hook Switch changes reported on GPIO_3 +*********************************************************************/ +static int ixj_set_port(int board, int arg) +{ + IXJ *j = &ixj[board]; + + if (j->cardtype == 400) { + if (arg != PORT_POTS) + return 10; + else + return 0; + } + switch (arg) { + case PORT_POTS: + j->port = PORT_POTS; + switch (j->cardtype) { + case 500: + j->pld_slicw.pcib.mic = 0; + j->pld_slicw.pcib.spk = 0; + outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01); + break; + case 300: + if (ixj_WriteDSPCommand(0xC528, board)) /* Write CODEC config to + Software Control Register */ + return 2; + j->pld_scrw.bits.daafsyncen = 0; // Turn off DAA Frame Sync + + outb_p(j->pld_scrw.byte, j->XILINXbase); + j->pld_clock.byte = 0; + outb_p(j->pld_clock.byte, j->XILINXbase + 0x04); + j->pld_slicw.bits.rly1 = 1; + j->pld_slicw.bits.spken = 0; + outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01); + SLIC_SetState(PLD_SLIC_STATE_STANDBY, board); + break; + case 100: + j->gpio.bytes.high = 0x0B; + j->gpio.bits.gpio6 = 0; + j->gpio.bits.gpio7 = 0; + ixj_WriteDSPCommand(j->gpio.word, board); + break; + } + break; + case PORT_PSTN: + if (j->cardtype == 300) { + ixj_WriteDSPCommand(0xC534, board); /* Write CODEC config to Software Control Register */ + + j->pld_slicw.bits.rly3 = 0; + j->pld_slicw.bits.rly1 = 1; + j->pld_slicw.bits.spken = 0; + outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01); + j->port = PORT_PSTN; + } else { + return 4; + } + break; + case PORT_SPEAKER: + j->port = PORT_SPEAKER; + switch (j->cardtype) { + case 500: + j->pld_slicw.pcib.mic = 1; + j->pld_slicw.pcib.spk = 1; + outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01); + break; + case 300: + break; + case 100: + j->gpio.bytes.high = 0x0B; + j->gpio.bits.gpio6 = 0; + j->gpio.bits.gpio7 = 1; + ixj_WriteDSPCommand(j->gpio.word, board); + break; + } + break; + case PORT_HANDSET: + if (j->cardtype == 300 || j->cardtype == 500) { + return 5; + } else { + j->gpio.bytes.high = 0x0B; + j->gpio.bits.gpio6 = 1; + j->gpio.bits.gpio7 = 0; + ixj_WriteDSPCommand(j->gpio.word, board); + j->port = PORT_HANDSET; + } + break; + default: + return 6; + break; + } + return 0; +} + +static int ixj_set_pots(int board, int arg) +{ + IXJ *j = &ixj[board]; + + if (j->cardtype == 300) { + if (arg) { + if (j->port == PORT_PSTN) { + j->pld_slicw.bits.rly1 = 0; + outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01); + return 1; + } else { + return 0; + } + } else { + j->pld_slicw.bits.rly1 = 1; + outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01); + return 1; + } + } else { + return 0; + } +} + +static void ixj_ring_on(int board) +{ + IXJ *j = &ixj[board]; + if (j->dsp.low == 0x20) // Internet PhoneJACK + { + if (ixjdebug > 0) + printk(KERN_INFO "IXJ Ring On /dev/phone%d\n", board); + + j->gpio.bytes.high = 0x0B; + j->gpio.bytes.low = 0x00; + j->gpio.bits.gpio1 = 1; + j->gpio.bits.gpio2 = 1; + j->gpio.bits.gpio5 = 0; + ixj_WriteDSPCommand(j->gpio.word, board); /* send the ring signal */ + } else // Internet LineJACK, Internet PhoneJACK Lite or + // Internet PhoneJACK PCI + { + if (ixjdebug > 0) + printk(KERN_INFO "IXJ Ring On /dev/phone%d\n", board); + + SLIC_SetState(PLD_SLIC_STATE_RINGING, board); + } +} + +static int ixj_hookstate(int board) +{ + unsigned long det; + IXJ *j = &ixj[board]; + int fOffHook = 0; + + switch (j->cardtype) { + case 100: + ixj_gpio_read(board); + fOffHook = j->gpio.bits.gpio3read ? 1 : 0; + break; + case 300: + case 400: + case 500: + SLIC_GetState(board); + if (j->pld_slicr.bits.state == PLD_SLIC_STATE_ACTIVE || + j->pld_slicr.bits.state == PLD_SLIC_STATE_STANDBY) + { + if (j->flags.ringing) + { + if(!in_interrupt()) + { + det = jiffies + (hertz / 50); + while (time_before(jiffies, det)) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + } + SLIC_GetState(board); + if (j->pld_slicr.bits.state == PLD_SLIC_STATE_RINGING) { + ixj_ring_on(board); + } + } + if (j->cardtype == 500) { + j->pld_scrr.byte = inb_p(j->XILINXbase); + fOffHook = j->pld_scrr.pcib.det ? 1 : 0; + } else + fOffHook = j->pld_slicr.bits.det ? 1 : 0; + } + break; + } + if (j->r_hook != fOffHook) { + j->r_hook = fOffHook; + if (j->port != PORT_POTS) { + j->ex.bits.hookstate = 1; + if (j->async_queue) + kill_fasync(j->async_queue, SIGIO); // Send apps notice of change + + } + } + if (j->port == PORT_PSTN && j->daa_mode == SOP_PU_CONVERSATION) + fOffHook |= 2; + + if (j->port == PORT_SPEAKER) + fOffHook |= 2; + + if (j->port == PORT_HANDSET) + fOffHook |= 2; + + return fOffHook; +} + +static void ixj_ring_off(board) +{ + IXJ *j = &ixj[board]; + + if (j->dsp.low == 0x20) // Internet PhoneJACK + { + if (ixjdebug > 0) + printk(KERN_INFO "IXJ Ring Off\n"); + j->gpio.bytes.high = 0x0B; + j->gpio.bytes.low = 0x00; + j->gpio.bits.gpio1 = 0; + j->gpio.bits.gpio2 = 1; + j->gpio.bits.gpio5 = 0; + ixj_WriteDSPCommand(j->gpio.word, board); + } else // Internet LineJACK + { + if (ixjdebug > 0) + printk(KERN_INFO "IXJ Ring Off\n"); + + SLIC_SetState(PLD_SLIC_STATE_STANDBY, board); + + SLIC_GetState(board); + } +} + +static void ixj_ring_start(int board) +{ + IXJ *j = &ixj[board]; + + j->flags.cringing = 1; + if (ixj_hookstate(board) & 1) { + if (j->port == PORT_POTS) + ixj_ring_off(board); + j->flags.cringing = 0; + } else { + j->ring_cadence_jif = jiffies; + j->ring_cadence_t = 15; + if (j->ring_cadence & 1 << j->ring_cadence_t) { + ixj_ring_on(board); + } else { + ixj_ring_off(board); + } + } +} + +static int ixj_ring(int board) +{ + char cntr; + unsigned long jif, det; + IXJ *j = &ixj[board]; + + j->flags.ringing = 1; + if (ixj_hookstate(board) & 1) { + ixj_ring_off(board); + j->flags.ringing = 0; + return 1; + } + det = 0; + for (cntr = 0; cntr < j->maxrings; cntr++) { + jif = jiffies + (1 * hertz); + ixj_ring_on(board); + while (time_before(jiffies, jif)) { + if (ixj_hookstate(board) & 1) { + ixj_ring_off(board); + j->flags.ringing = 0; + return 1; + } + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + if(signal_pending(current)) + break; + } + jif = jiffies + (3 * hertz); + ixj_ring_off(board); + while (time_before(jiffies, jif)) { + if (ixj_hookstate(board) & 1) { + det = jiffies + (hertz / 100); + while (time_before(jiffies, det)) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + if(signal_pending(current)) + break; + } + if (ixj_hookstate(board) & 1) { + j->flags.ringing = 0; + return 1; + } + } + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + if(signal_pending(current)) + break; + } + } + ixj_ring_off(board); + j->flags.ringing = 0; + return 0; +} + +int ixj_open(struct phone_device *p, struct file *file_p) +{ + IXJ *j = &ixj[p->board]; + + if (!j->DSPbase) + return -ENODEV; + + if (file_p->f_mode & FMODE_READ) + j->readers++; + if (file_p->f_mode & FMODE_WRITE) + j->writers++; + + MOD_INC_USE_COUNT; + + if (ixjdebug > 0) +// printk(KERN_INFO "Opening board %d\n", NUM(inode->i_rdev)); + printk(KERN_INFO "Opening board %d\n", p->board); + + return 0; +} + +int ixj_release(struct inode *inode, struct file *file_p) +{ + IXJ_TONE ti; + int board = NUM(inode->i_rdev); + IXJ *j = &ixj[board]; + + if (ixjdebug > 0) + printk(KERN_INFO "Closing board %d\n", NUM(inode->i_rdev)); + + daa_set_mode(board, SOP_PU_SLEEP); + ixj_set_port(board, PORT_POTS); + aec_stop(board); + ixj_play_stop(board); + ixj_record_stop(board); + + ti.tone_index = 10; + ti.gain0 = 1; + ti.freq0 = hz941; + ti.gain1 = 0; + ti.freq1 = hz1209; + ti.tone_index = 11; + ti.gain0 = 1; + ti.freq0 = hz941; + ti.gain1 = 0; + ti.freq1 = hz1336; + ti.tone_index = 12; + ti.gain0 = 1; + ti.freq0 = hz941; + ti.gain1 = 0; + ti.freq1 = hz1477; + ti.tone_index = 13; + ti.gain0 = 1; + ti.freq0 = hz800; + ti.gain1 = 0; + ti.freq1 = 0; + ixj_init_tone(board, &ti); + ti.tone_index = 14; + ti.gain0 = 1; + ti.freq0 = hz1000; + ti.gain1 = 0; + ti.freq1 = 0; + ixj_init_tone(board, &ti); + ti.tone_index = 15; + ti.gain0 = 1; + ti.freq0 = hz1250; + ti.gain1 = 0; + ti.freq1 = 0; + ixj_init_tone(board, &ti); + ti.tone_index = 16; + ti.gain0 = 1; + ti.freq0 = hz950; + ti.gain1 = 0; + ti.freq1 = 0; + ixj_init_tone(board, &ti); + ti.tone_index = 17; + ti.gain0 = 1; + ti.freq0 = hz1100; + ti.gain1 = 0; + ti.freq1 = 0; + ixj_init_tone(board, &ti); + ti.tone_index = 18; + ti.gain0 = 1; + ti.freq0 = hz1400; + ti.gain1 = 0; + ti.freq1 = 0; + ixj_init_tone(board, &ti); + ti.tone_index = 19; + ti.gain0 = 1; + ti.freq0 = hz1500; + ti.gain1 = 0; + ti.freq1 = 0; + ixj_init_tone(board, &ti); + ti.tone_index = 20; + ti.gain0 = 1; + ti.freq0 = hz1600; + ti.gain1 = 0; + ti.freq1 = 0; + ixj_init_tone(board, &ti); + ti.tone_index = 21; + ti.gain0 = 1; + ti.freq0 = hz1800; + ti.gain1 = 0; + ti.freq1 = 0; + ixj_init_tone(board, &ti); + ti.tone_index = 22; + ti.gain0 = 1; + ti.freq0 = hz2100; + ti.gain1 = 0; + ti.freq1 = 0; + ixj_init_tone(board, &ti); + ti.tone_index = 23; + ti.gain0 = 1; + ti.freq0 = hz1300; + ti.gain1 = 0; + ti.freq1 = 0; + ixj_init_tone(board, &ti); + ti.tone_index = 24; + ti.gain0 = 1; + ti.freq0 = hz2450; + ti.gain1 = 0; + ti.freq1 = 0; + ixj_init_tone(board, &ti); + ti.tone_index = 25; + ti.gain0 = 1; + ti.freq0 = hz350; + ti.gain1 = 0; + ti.freq1 = hz440; + ixj_init_tone(board, &ti); + ti.tone_index = 26; + ti.gain0 = 1; + ti.freq0 = hz440; + ti.gain1 = 0; + ti.freq1 = hz480; + ixj_init_tone(board, &ti); + ti.tone_index = 27; + ti.gain0 = 1; + ti.freq0 = hz480; + ti.gain1 = 0; + ti.freq1 = hz620; + ixj_init_tone(board, &ti); + + idle(board); + + if (file_p->f_mode & FMODE_READ) + j->readers--; + if (file_p->f_mode & FMODE_WRITE) + j->writers--; + + if (j->read_buffer && !j->readers) { + kfree(j->read_buffer); + j->read_buffer = NULL; + j->read_buffer_size = 0; + } + if (j->write_buffer && !j->writers) { + kfree(j->write_buffer); + j->write_buffer = NULL; + j->write_buffer_size = 0; + } + j->rec_codec = j->play_codec = 0; + j->rec_frame_size = j->play_frame_size = 0; + ixj_fasync(-1, file_p, 0); // remove from list of async notification + + MOD_DEC_USE_COUNT; + return 0; +} + +static int read_filters(int board) +{ + unsigned short fc, cnt; + IXJ *j = &ixj[board]; + + if (ixj_WriteDSPCommand(0x5144, board)) + return -1; + + fc = j->ssr.high << 8 | j->ssr.low; + if (fc == j->frame_count) + return 1; + + j->frame_count = fc; + + for (cnt = 0; cnt < 4; cnt++) { + if (ixj_WriteDSPCommand(0x5154 + cnt, board)) + return -1; + + if (ixj_WriteDSPCommand(0x515C, board)) + return -1; + + j->filter_hist[cnt] = j->ssr.high << 8 | j->ssr.low; + if ((j->filter_hist[cnt] & 1 && !(j->filter_hist[cnt] & 2)) || + (j->filter_hist[cnt] & 2 && !(j->filter_hist[cnt] & 1))) { + switch (cnt) { + case 0: + j->ex.bits.f0 = 1; + break; + case 1: + j->ex.bits.f1 = 1; + break; + case 2: + j->ex.bits.f2 = 1; + break; + case 3: + j->ex.bits.f3 = 1; + break; + } + } + } + return 0; +} + +static int LineMonitor(int board) +{ + IXJ *j = &ixj[board]; + + if (j->dtmf_proc) { + return -1; + } + j->dtmf_proc = 1; + + if (ixj_WriteDSPCommand(0x7000, board)) // Line Monitor + + return -1; + + j->dtmf.bytes.high = j->ssr.high; + j->dtmf.bytes.low = j->ssr.low; + if (!j->dtmf_state && j->dtmf.bits.dtmf_valid) { + j->dtmf_state = 1; + j->dtmf_current = j->dtmf.bits.digit; + } + if (j->dtmf_state && !j->dtmf.bits.dtmf_valid) // && j->dtmf_wp != j->dtmf_rp) + { + j->dtmfbuffer[j->dtmf_wp] = j->dtmf_current; + j->dtmf_wp++; + if (j->dtmf_wp == 79) + j->dtmf_wp = 0; + j->ex.bits.dtmf_ready = 1; + j->dtmf_state = 0; + } + j->dtmf_proc = 0; + + return 0; +} + +ssize_t ixj_read(struct file * file_p, char *buf, size_t length, loff_t * ppos) +{ + unsigned long i = *ppos; + IXJ *j = &ixj[NUM(file_p->f_dentry->d_inode->i_rdev)]; + struct wait_queue wait = + {current, NULL}; + + add_wait_queue(&j->read_q, &wait); + current->state = TASK_INTERRUPTIBLE; + mb(); + + while (!j->read_buffer_ready || (j->dtmf_state && j->flags.dtmf_oob)) { + ++j->read_wait; + if (file_p->f_flags & O_NONBLOCK) { + current->state = TASK_RUNNING; + remove_wait_queue(&j->read_q, &wait); + return -EAGAIN; + } + if (!ixj_hookstate(NUM(file_p->f_dentry->d_inode->i_rdev))) { + current->state = TASK_RUNNING; + remove_wait_queue(&j->read_q, &wait); + return 0; + } + interruptible_sleep_on(&j->read_q); + if (signal_pending(current)) { + current->state = TASK_RUNNING; + remove_wait_queue(&j->read_q, &wait); + return -EINTR; + } + } + + remove_wait_queue(&j->read_q, &wait); + current->state = TASK_RUNNING; + /* Don't ever copy more than the user asks */ + i = copy_to_user(buf, j->read_buffer, min(length, j->read_buffer_size)); + j->read_buffer_ready = 0; + if (i) + return -EFAULT; + else + return min(length, j->read_buffer_size); +} + +ssize_t ixj_enhanced_read(struct file * file_p, char *buf, size_t length, + loff_t * ppos) +{ + int pre_retval; + ssize_t read_retval = 0; + IXJ *j = &ixj[NUM(file_p->f_dentry->d_inode->i_rdev)]; + + pre_retval = ixj_PreRead(j, 0L); + switch (pre_retval) { + case NORMAL: + read_retval = ixj_read(file_p, buf, length, ppos); + ixj_PostRead(j, 0L); + break; + case NOPOST: + read_retval = ixj_read(file_p, buf, length, ppos); + break; + case POSTONLY: + ixj_PostRead(j, 0L); + break; + default: + read_retval = pre_retval; + } + return read_retval; +} + +ssize_t ixj_write(struct file * file_p, const char *buf, size_t count, loff_t * ppos) +{ + unsigned long i = *ppos; + int board = NUM(file_p->f_dentry->d_inode->i_rdev); + IXJ *j = &ixj[board]; + struct wait_queue wait = + {current, NULL}; + + add_wait_queue(&j->read_q, &wait); + current->state = TASK_INTERRUPTIBLE; + mb(); + + + while (!j->write_buffers_empty) { + ++j->write_wait; + if (file_p->f_flags & O_NONBLOCK) { + current->state = TASK_RUNNING; + remove_wait_queue(&j->read_q, &wait); + return -EAGAIN; + } + if (!ixj_hookstate(NUM(file_p->f_dentry->d_inode->i_rdev))) { + current->state = TASK_RUNNING; + remove_wait_queue(&j->read_q, &wait); + return 0; + } + interruptible_sleep_on(&j->write_q); + if (signal_pending(current)) { + current->state = TASK_RUNNING; + remove_wait_queue(&j->read_q, &wait); + return -EINTR; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&j->read_q, &wait); + if (j->write_buffer_wp + count >= j->write_buffer_end) + j->write_buffer_wp = j->write_buffer; + i = copy_from_user(j->write_buffer_wp, buf, min(count, j->write_buffer_size)); + if (i) + return -EFAULT; + + return min(count, j->write_buffer_size); +} + +ssize_t ixj_enhanced_write(struct file * file_p, const char *buf, size_t count, + loff_t * ppos) +{ + int pre_retval; + ssize_t write_retval = 0; + IXJ *j = &ixj[NUM(file_p->f_dentry->d_inode->i_rdev)]; + + pre_retval = ixj_PreWrite(j, 0L); + switch (pre_retval) { + case NORMAL: + write_retval = ixj_write(file_p, buf, count, ppos); + if (write_retval != -EFAULT) { + ixj_PostWrite(j, 0L); + j->write_buffer_wp += count; + j->write_buffers_empty--; + } + break; + case NOPOST: + write_retval = ixj_write(file_p, buf, count, ppos); + if (write_retval != -EFAULT) { + j->write_buffer_wp += count; + j->write_buffers_empty--; + } + break; + case POSTONLY: + ixj_PostWrite(j, 0L); + break; + default: + write_retval = pre_retval; + } + return write_retval; +} + +static void ixj_read_frame(int board) +{ + int cnt, dly; + IXJ *j = &ixj[board]; + + if (j->read_buffer) { + for (cnt = 0; cnt < j->rec_frame_size * 2; cnt += 2) { + if (!(cnt % 16) && !IsRxReady(board)) { + dly = 0; + while (!IsRxReady(board)) { + if (dly++ > 5) { + dly = 0; + break; + } + udelay(10); + } + } + // Throw away word 0 of the 8021 compressed format to get standard G.729. + if (j->rec_codec == G729 && (cnt == 0 || cnt == 5 || cnt == 10)) { + inb_p(j->DSPbase + 0x0E); + inb_p(j->DSPbase + 0x0F); + } + *(j->read_buffer + cnt) = inb_p(j->DSPbase + 0x0E); + *(j->read_buffer + cnt + 1) = inb_p(j->DSPbase + 0x0F); + } +#ifdef PERFMON_STATS + ++j->framesread; +#endif + if (j->intercom != -1) { + if (IsTxReady(j->intercom)) { + for (cnt = 0; cnt < j->rec_frame_size * 2; cnt += 2) { + if (!(cnt % 16) && !IsTxReady(board)) { + dly = 0; + while (!IsTxReady(board)) { + if (dly++ > 5) { + dly = 0; + break; + } + udelay(10); + } + } + outb_p(*(j->read_buffer + cnt), ixj[j->intercom].DSPbase + 0x0C); + outb_p(*(j->read_buffer + cnt + 1), ixj[j->intercom].DSPbase + 0x0D); + } +#ifdef PERFMON_STATS + ++ixj[j->intercom].frameswritten; +#endif + } + } else { + j->read_buffer_ready = 1; + wake_up_interruptible(&j->read_q); // Wake any blocked readers + + wake_up_interruptible(&j->poll_q); // Wake any blocked selects + + if (j->async_queue) + kill_fasync(j->async_queue, SIGIO); // Send apps notice of frame + + } + } +} + +static void ixj_write_frame(int board) +{ + int cnt, frame_count, dly; + BYTES blankword; + IXJ *j = &ixj[board]; + + frame_count = 0; + if (j->write_buffer && j->write_buffers_empty < 2) { + if (j->write_buffer_wp > j->write_buffer_rp) { + frame_count = + (j->write_buffer_wp - j->write_buffer_rp) / (j->play_frame_size * 2); + } + if (j->write_buffer_rp > j->write_buffer_wp) { + frame_count = + (j->write_buffer_wp - j->write_buffer) / (j->play_frame_size * 2) + + (j->write_buffer_end - j->write_buffer_rp) / (j->play_frame_size * 2); + } + if (frame_count >= 1) { + if (j->ver.low == 0x12 && j->play_mode && j->flags.play_first_frame) { + switch (j->play_mode) { + case PLAYBACK_MODE_ULAW: + case PLAYBACK_MODE_ALAW: + blankword.low = blankword.high = 0xFF; + break; + case PLAYBACK_MODE_8LINEAR: + case PLAYBACK_MODE_16LINEAR: + blankword.low = blankword.high = 0x00; + break; + case PLAYBACK_MODE_8LINEAR_WSS: + blankword.low = blankword.high = 0x80; + break; + } + for (cnt = 0; cnt < 16; cnt++) { + if (!(cnt % 16) && !IsTxReady(board)) { + dly = 0; + while (!IsTxReady(board)) { + if (dly++ > 5) { + dly = 0; + break; + } + udelay(10); + } + } + outb_p((blankword.low), j->DSPbase + 0x0C); + outb_p((blankword.high), j->DSPbase + 0x0D); + } + j->flags.play_first_frame = 0; + } + for (cnt = 0; cnt < j->play_frame_size * 2; cnt += 2) { + if (!(cnt % 16) && !IsTxReady(board)) { + dly = 0; + while (!IsTxReady(board)) { + if (dly++ > 5) { + dly = 0; + break; + } + udelay(10); + } + } +// Add word 0 to G.729 frames for the 8021. Right now we don't do VAD/CNG + // so all frames are type 1. + if (j->play_codec == G729 && (cnt == 0 || cnt == 5 || cnt == 10)) { + outb_p(0x01, j->DSPbase + 0x0C); + outb_p(0x00, j->DSPbase + 0x0D); + } + outb_p(*(j->write_buffer_rp + cnt), j->DSPbase + 0x0C); + outb_p(*(j->write_buffer_rp + cnt + 1), j->DSPbase + 0x0D); + *(j->write_buffer_rp + cnt) = 0; + *(j->write_buffer_rp + cnt + 1) = 0; + } + j->write_buffer_rp += j->play_frame_size * 2; + if (j->write_buffer_rp >= j->write_buffer_end) { + j->write_buffer_rp = j->write_buffer; + } + j->write_buffers_empty++; + wake_up_interruptible(&(j->write_q)); // Wake any blocked writers + + wake_up_interruptible(&j->poll_q); // Wake any blocked selects + + if (j->async_queue) + kill_fasync(j->async_queue, SIGIO); // Send apps notice of empty buffer +#ifdef PERFMON_STATS + ++j->frameswritten; +#endif + } + } else { + j->drybuffer++; + } +} + +static int idle(int board) +{ + IXJ *j = &ixj[board]; + + if (ixj_WriteDSPCommand(0x0000, board)) // DSP Idle + + return 0; + if (j->ssr.high || j->ssr.low) + return 0; + else + return 1; +} + +static int set_base_frame(int board, int size) +{ + unsigned short cmd; + int cnt; + IXJ *j = &ixj[board]; + + aec_stop(board); + for (cnt = 0; cnt < 10; cnt++) { + if (idle(board)) + break; + } + if (j->ssr.high || j->ssr.low) + return -1; + if (j->dsp.low != 0x20) { + switch (size) { + case 30: + cmd = 0x07F0; + /* Set Base Frame Size to 240 pg9-10 8021 */ + break; + case 20: + cmd = 0x07A0; + /* Set Base Frame Size to 160 pg9-10 8021 */ + break; + case 10: + cmd = 0x0750; + /* Set Base Frame Size to 80 pg9-10 8021 */ + break; + default: + return -1; + } + } else { + if (size == 30) + return size; + else + return -1; + } + if (ixj_WriteDSPCommand(cmd, board)) { + j->baseframe.high = j->baseframe.low = 0xFF; + return -1; + } else { + j->baseframe.high = j->ssr.high; + j->baseframe.low = j->ssr.low; + } + return size; +} + +static int set_rec_codec(int board, int rate) +{ + int retval = 0; + IXJ *j = &ixj[board]; + + j->rec_codec = rate; + + switch (rate) { + case G723_63: + if (j->ver.low != 0x12 || ixj_convert_loaded) { + j->rec_frame_size = 12; + j->rec_mode = 0; + } else { + retval = 1; + } + break; + case G723_53: + if (j->ver.low != 0x12 || ixj_convert_loaded) { + j->rec_frame_size = 10; + j->rec_mode = 0; + } else { + retval = 1; + } + break; + case TS85: + if (j->dsp.low == 0x20 || j->flags.ts85_loaded) { + j->rec_frame_size = 16; + j->rec_mode = 0; + } else { + retval = 1; + } + break; + case TS48: + if (j->ver.low != 0x12 || ixj_convert_loaded) { + j->rec_frame_size = 9; + j->rec_mode = 0; + } else { + retval = 1; + } + break; + case TS41: + if (j->ver.low != 0x12 || ixj_convert_loaded) { + j->rec_frame_size = 8; + j->rec_mode = 0; + } else { + retval = 1; + } + break; + case G728: + if (j->dsp.low != 0x20) { + j->rec_frame_size = 48; + j->rec_mode = 0; + } else { + retval = 1; + } + break; + case G729: + if (j->dsp.low != 0x20) { + if (!j->flags.g729_loaded) { + retval = 1; + break; + } + switch (j->baseframe.low) { + case 0xA0: + j->rec_frame_size = 10; + break; + case 0x50: + j->rec_frame_size = 5; + break; + default: + j->rec_frame_size = 15; + break; + } + j->rec_mode = 0; + } else { + retval = 1; + } + break; + case ULAW: + switch (j->baseframe.low) { + case 0xA0: + j->rec_frame_size = 80; + break; + case 0x50: + j->rec_frame_size = 40; + break; + default: + j->rec_frame_size = 120; + break; + } + j->rec_mode = 4; + break; + case ALAW: + switch (j->baseframe.low) { + case 0xA0: + j->rec_frame_size = 80; + break; + case 0x50: + j->rec_frame_size = 40; + break; + default: + j->rec_frame_size = 120; + break; + } + j->rec_mode = 4; + break; + case LINEAR16: + switch (j->baseframe.low) { + case 0xA0: + j->rec_frame_size = 160; + break; + case 0x50: + j->rec_frame_size = 80; + break; + default: + j->rec_frame_size = 240; + break; + } + j->rec_mode = 5; + break; + case LINEAR8: + switch (j->baseframe.low) { + case 0xA0: + j->rec_frame_size = 80; + break; + case 0x50: + j->rec_frame_size = 40; + break; + default: + j->rec_frame_size = 120; + break; + } + j->rec_mode = 6; + break; + case WSS: + switch (j->baseframe.low) { + case 0xA0: + j->rec_frame_size = 80; + break; + case 0x50: + j->rec_frame_size = 40; + break; + default: + j->rec_frame_size = 120; + break; + } + j->rec_mode = 7; + break; + default: + j->rec_frame_size = 0; + j->rec_mode = -1; + if (j->read_buffer) { + kfree(j->read_buffer); + j->read_buffer = NULL; + j->read_buffer_size = 0; + } + retval = 1; + break; + } + return retval; +} + +static int ixj_record_start(int board) +{ + unsigned short cmd = 0x0000; + IXJ *j = &ixj[board]; + + if (!j->rec_mode) { + switch (j->rec_codec) { + case G723_63: + cmd = 0x5131; + break; + case G723_53: + cmd = 0x5132; + break; + case TS85: + cmd = 0x5130; // TrueSpeech 8.5 + + break; + case TS48: + cmd = 0x5133; // TrueSpeech 4.8 + + break; + case TS41: + cmd = 0x5134; // TrueSpeech 4.1 + + break; + case G728: + cmd = 0x5135; + break; + case G729: + cmd = 0x5136; + break; + default: + return 1; + } + if (ixj_WriteDSPCommand(cmd, board)) + return -1; + } + if (!j->read_buffer) { + if (!j->read_buffer) + j->read_buffer = kmalloc(j->rec_frame_size * 2, GFP_ATOMIC); + if (!j->read_buffer) { + printk("Read buffer allocation for ixj board %d failed!\n", board); + return -ENOMEM; + } + } + j->read_buffer_size = j->rec_frame_size * 2; + + if (ixj_WriteDSPCommand(0x5102, board)) // Set Poll sync mode + + return -1; + + switch (j->rec_mode) { + case 0: + cmd = 0x1C03; // Record C1 + + break; + case 4: + if (j->ver.low == 0x12) { + cmd = 0x1E03; // Record C1 + + } else { + cmd = 0x1E01; // Record C1 + + } + break; + case 5: + if (j->ver.low == 0x12) { + cmd = 0x1E83; // Record C1 + + } else { + cmd = 0x1E81; // Record C1 + + } + break; + case 6: + if (j->ver.low == 0x12) { + cmd = 0x1F03; // Record C1 + + } else { + cmd = 0x1F01; // Record C1 + + } + break; + case 7: + if (j->ver.low == 0x12) { + cmd = 0x1F83; // Record C1 + + } else { + cmd = 0x1F81; // Record C1 + + } + break; + } + if (ixj_WriteDSPCommand(cmd, board)) + return -1; + + return 0; +} + +static void ixj_record_stop(int board) +{ + IXJ *j = &ixj[board]; + + if (j->rec_mode > -1) { + ixj_WriteDSPCommand(0x5120, board); + j->rec_mode = -1; + } +} + +static void set_rec_depth(int board, int depth) +{ + if (depth > 60) + depth = 60; + if (depth < 0) + depth = 0; + ixj_WriteDSPCommand(0x5180 + depth, board); +} + +static void set_rec_volume(int board, int volume) +{ + ixj_WriteDSPCommand(0xCF03, board); + ixj_WriteDSPCommand(volume, board); +} + +static int get_rec_level(int board) +{ + IXJ *j = &ixj[board]; + + ixj_WriteDSPCommand(0xCF88, board); + + return j->ssr.high << 8 | j->ssr.low; +} + +static void ixj_aec_start(int board, int level) +{ + IXJ *j = &ixj[board]; + + j->aec_level = level; + if (!level) { + ixj_WriteDSPCommand(0xB002, board); + } else { + if (j->rec_codec == G729 || j->play_codec == G729) { + ixj_WriteDSPCommand(0xE022, board); // Move AEC filter buffer + + ixj_WriteDSPCommand(0x0300, board); + } + ixj_WriteDSPCommand(0xB001, board); // AEC On + + ixj_WriteDSPCommand(0xE013, board); // Advanced AEC C1 + + switch (level) { + case 1: + ixj_WriteDSPCommand(0x0000, board); // Advanced AEC C2 = off + + ixj_WriteDSPCommand(0xE011, board); + ixj_WriteDSPCommand(0xFFFF, board); + break; + + case 2: + ixj_WriteDSPCommand(0x0600, board); // Advanced AEC C2 = on medium + + ixj_WriteDSPCommand(0xE011, board); + ixj_WriteDSPCommand(0x0080, board); + break; + + case 3: + ixj_WriteDSPCommand(0x0C00, board); // Advanced AEC C2 = on high + + ixj_WriteDSPCommand(0xE011, board); + ixj_WriteDSPCommand(0x0080, board); + break; + } + } +} + +static void aec_stop(int board) +{ + IXJ *j = &ixj[board]; + + if (j->rec_codec == G729 || j->play_codec == G729) { + ixj_WriteDSPCommand(0xE022, board); // Move AEC filter buffer back + + ixj_WriteDSPCommand(0x0700, board); + } + if (ixj[board].play_mode != -1 && ixj[board].rec_mode != -1); + { + ixj_WriteDSPCommand(0xB002, board); // AEC Stop + + } +} + +static int set_play_codec(int board, int rate) +{ + int retval = 0; + IXJ *j = &ixj[board]; + + j->play_codec = rate; + + switch (rate) { + case G723_63: + if (j->ver.low != 0x12 || ixj_convert_loaded) { + j->play_frame_size = 12; + j->play_mode = 0; + } else { + retval = 1; + } + break; + case G723_53: + if (j->ver.low != 0x12 || ixj_convert_loaded) { + j->play_frame_size = 10; + j->play_mode = 0; + } else { + retval = 1; + } + break; + case TS85: + if (j->dsp.low == 0x20 || j->flags.ts85_loaded) { + j->play_frame_size = 16; + j->play_mode = 0; + } else { + retval = 1; + } + break; + case TS48: + if (j->ver.low != 0x12 || ixj_convert_loaded) { + j->play_frame_size = 9; + j->play_mode = 0; + } else { + retval = 1; + } + break; + case TS41: + if (j->ver.low != 0x12 || ixj_convert_loaded) { + j->play_frame_size = 8; + j->play_mode = 0; + } else { + retval = 1; + } + break; + case G728: + if (j->dsp.low != 0x20) { + j->play_frame_size = 48; + j->play_mode = 0; + } else { + retval = 1; + } + break; + case G729: + if (j->dsp.low != 0x20) { + if (!j->flags.g729_loaded) { + retval = 1; + break; + } + switch (j->baseframe.low) { + case 0xA0: + j->play_frame_size = 10; + break; + case 0x50: + j->play_frame_size = 5; + break; + default: + j->play_frame_size = 15; + break; + } + j->play_mode = 0; + } else { + retval = 1; + } + break; + case ULAW: + switch (j->baseframe.low) { + case 0xA0: + j->play_frame_size = 80; + break; + case 0x50: + j->play_frame_size = 40; + break; + default: + j->play_frame_size = 120; + break; + } + j->play_mode = 2; + break; + case ALAW: + switch (j->baseframe.low) { + case 0xA0: + j->play_frame_size = 80; + break; + case 0x50: + j->play_frame_size = 40; + break; + default: + j->play_frame_size = 120; + break; + } + j->play_mode = 2; + break; + case LINEAR16: + switch (j->baseframe.low) { + case 0xA0: + j->play_frame_size = 160; + break; + case 0x50: + j->play_frame_size = 80; + break; + default: + j->play_frame_size = 240; + break; + } + j->play_mode = 6; + break; + case LINEAR8: + switch (j->baseframe.low) { + case 0xA0: + j->play_frame_size = 80; + break; + case 0x50: + j->play_frame_size = 40; + break; + default: + j->play_frame_size = 120; + break; + } + j->play_mode = 4; + break; + case WSS: + switch (j->baseframe.low) { + case 0xA0: + j->play_frame_size = 80; + break; + case 0x50: + j->play_frame_size = 40; + break; + default: + j->play_frame_size = 120; + break; + } + j->play_mode = 5; + break; + default: + j->play_frame_size = 0; + j->play_mode = -1; + if (j->write_buffer) { + kfree(j->write_buffer); + j->write_buffer = NULL; + j->write_buffer_size = 0; + } + retval = 1; + break; + } + return retval; +} + +static int ixj_play_start(int board) +{ + unsigned short cmd = 0x0000; + IXJ *j = &ixj[board]; + + j->flags.play_first_frame = 1; + j->drybuffer = 0; + + if (!j->play_mode) { + switch (j->play_codec) { + case G723_63: + cmd = 0x5231; + break; + case G723_53: + cmd = 0x5232; + break; + case TS85: + cmd = 0x5230; // TrueSpeech 8.5 + + break; + case TS48: + cmd = 0x5233; // TrueSpeech 4.8 + + break; + case TS41: + cmd = 0x5234; // TrueSpeech 4.1 + + break; + case G728: + cmd = 0x5235; + break; + case G729: + cmd = 0x5236; + break; + default: + return 1; + } + if (ixj_WriteDSPCommand(cmd, board)) + return -1; + } + if (!j->write_buffer) { + j->write_buffer = kmalloc(j->play_frame_size * 2, GFP_ATOMIC); + if (!j->write_buffer) { + printk("Write buffer allocation for ixj board %d failed!\n", board); + return -ENOMEM; + } + } + j->write_buffers_empty = 2; + j->write_buffer_size = j->play_frame_size * 2; + j->write_buffer_end = j->write_buffer + j->play_frame_size * 2; + j->write_buffer_rp = j->write_buffer_wp = j->write_buffer; + + if (ixj_WriteDSPCommand(0x5202, board)) // Set Poll sync mode + + return -1; + + switch (j->play_mode) { + case 0: + cmd = 0x2C03; + break; + case 2: + if (j->ver.low == 0x12) { + cmd = 0x2C23; + } else { + cmd = 0x2C21; + } + break; + case 4: + if (j->ver.low == 0x12) { + cmd = 0x2C43; + } else { + cmd = 0x2C41; + } + break; + case 5: + if (j->ver.low == 0x12) { + cmd = 0x2C53; + } else { + cmd = 0x2C51; + } + break; + case 6: + if (j->ver.low == 0x12) { + cmd = 0x2C63; + } else { + cmd = 0x2C61; + } + break; + } + if (ixj_WriteDSPCommand(cmd, board)) + return -1; + + if (ixj_WriteDSPCommand(0x2000, board)) // Playback C2 + + return -1; + + if (ixj_WriteDSPCommand(0x2000 + ixj[board].play_frame_size, board)) // Playback C3 + + return -1; + + return 0; +} + +static void ixj_play_stop(int board) +{ + IXJ *j = &ixj[board]; + + if (j->play_mode > -1) { + ixj_WriteDSPCommand(0x5221, board); // Stop playback + + j->play_mode = -1; + } +} + +extern __inline__ void set_play_depth(int board, int depth) +{ + if (depth > 60) + depth = 60; + if (depth < 0) + depth = 0; + ixj_WriteDSPCommand(0x5280 + depth, board); +} + +extern __inline__ void set_play_volume(int board, int volume) +{ + ixj_WriteDSPCommand(0xCF02, board); + ixj_WriteDSPCommand(volume, board); +} + +extern __inline__ int get_play_level(int board) +{ + ixj_WriteDSPCommand(0xCF8F, board); + return ixj[board].ssr.high << 8 | ixj[board].ssr.low; +} + +static unsigned int ixj_poll(struct file *file_p, poll_table * wait) +{ + unsigned int mask = 0; + IXJ *j = &ixj[NUM(file_p->f_dentry->d_inode->i_rdev)]; + + poll_wait(file_p, &(j->poll_q), wait); + if (j->read_buffer_ready > 0) + mask |= POLLIN | POLLRDNORM; /* readable */ + if (j->write_buffers_empty > 0) + mask |= POLLOUT | POLLWRNORM; /* writable */ + if (j->ex.bytes) + mask |= POLLPRI; + return mask; +} + +static int ixj_play_tone(int board, char tone) +{ + IXJ *j = &ixj[board]; + + if (!j->tone_state) + idle(board); + + j->tone_index = tone; + if (ixj_WriteDSPCommand(0x6000 + j->tone_index, board)) + return -1; + + if (!j->tone_state) { + j->tone_start_jif = jiffies; + + j->tone_state = 1; + } + return 0; +} + +static int ixj_set_tone_on(unsigned short arg, int board) +{ + IXJ *j = &ixj[board]; + + j->tone_on_time = arg; + + if (ixj_WriteDSPCommand(0x6E04, board)) // Set Tone On Period + + return -1; + + if (ixj_WriteDSPCommand(arg, board)) + return -1; + + return 0; +} + +static int SCI_WaitHighSCI(int board) +{ + int cnt; + IXJ *j = &ixj[board]; + + j->pld_scrr.byte = inb_p(j->XILINXbase); + if (!j->pld_scrr.bits.sci) { + for (cnt = 0; cnt < 10; cnt++) { + udelay(32); + j->pld_scrr.byte = inb_p(j->XILINXbase); + + if ((j->pld_scrr.bits.sci)) + return 1; + } + if (ixjdebug > 1) + printk(KERN_INFO "SCI Wait High failed %x\n", j->pld_scrr.byte); + return 0; + } else + return 1; +} + +static int SCI_WaitLowSCI(int board) +{ + int cnt; + IXJ *j = &ixj[board]; + + j->pld_scrr.byte = inb_p(j->XILINXbase); + if (j->pld_scrr.bits.sci) { + for (cnt = 0; cnt < 10; cnt++) { + udelay(32); + j->pld_scrr.byte = inb_p(j->XILINXbase); + + if (!(j->pld_scrr.bits.sci)) + return 1; + } + if (ixjdebug > 1) + printk(KERN_INFO "SCI Wait Low failed %x\n", j->pld_scrr.byte); + return 0; + } else + return 1; +} + +static int SCI_Control(int board, int control) +{ + IXJ *j = &ixj[board]; + + switch (control) { + case SCI_End: + j->pld_scrw.bits.c0 = 0; // Set PLD Serial control interface + + j->pld_scrw.bits.c1 = 0; // to no selection + + break; + case SCI_Enable_DAA: + j->pld_scrw.bits.c0 = 1; // Set PLD Serial control interface + + j->pld_scrw.bits.c1 = 0; // to write to DAA + + break; + case SCI_Enable_Mixer: + j->pld_scrw.bits.c0 = 0; // Set PLD Serial control interface + + j->pld_scrw.bits.c1 = 1; // to write to mixer + + break; + case SCI_Enable_EEPROM: + j->pld_scrw.bits.c0 = 1; // Set PLD Serial control interface + + j->pld_scrw.bits.c1 = 1; // to write to EEPROM + + break; + default: + return 0; + break; + } + outb_p(j->pld_scrw.byte, j->XILINXbase); + + switch (control) { + case SCI_End: + return 1; + break; + case SCI_Enable_DAA: + case SCI_Enable_Mixer: + case SCI_Enable_EEPROM: + if (!SCI_WaitHighSCI(board)) + return 0; + break; + default: + return 0; + break; + } + return 1; +} + +static int SCI_Prepare(int board) +{ + if (!SCI_Control(board, SCI_End)) + return 0; + + if (!SCI_WaitLowSCI(board)) + return 0; + + return 1; +} + +static int ixj_mixer(long val, int board) +{ + BYTES bytes; + IXJ *j = &ixj[board]; + + bytes.high = (val & 0xFF00) >> 8; + bytes.low = val & 0x00FF; + + outb_p(bytes.high & 0x1F, j->XILINXbase + 0x03); // Load Mixer Address + + outb_p(bytes.low, j->XILINXbase + 0x02); // Load Mixer Data + + SCI_Control(board, SCI_Enable_Mixer); + + SCI_Control(board, SCI_End); + + return 0; +} + +static int daa_load(BYTES * p_bytes, int board) +{ + IXJ *j = &ixj[board]; + + outb_p(p_bytes->high, j->XILINXbase + 0x03); + outb_p(p_bytes->low, j->XILINXbase + 0x02); + if (!SCI_Control(board, SCI_Enable_DAA)) + return 0; + else + return 1; +} + +static int ixj_daa_cr4(int board, char reg) +{ + IXJ *j = &ixj[board]; + BYTES bytes; + + switch (j->daa_mode) { + case SOP_PU_SLEEP: + bytes.high = 0x14; + break; + case SOP_PU_RINGING: + bytes.high = 0x54; + break; + case SOP_PU_CONVERSATION: + bytes.high = 0x94; + break; + case SOP_PU_PULSEDIALING: + bytes.high = 0xD4; + break; + } + + switch (j->m_DAAShadowRegs.SOP_REGS.SOP.cr4.bitreg.AGX) { + case 0: + j->m_DAAShadowRegs.SOP_REGS.SOP.cr4.bitreg.AGR_Z = 0; + break; + case 1: + j->m_DAAShadowRegs.SOP_REGS.SOP.cr4.bitreg.AGR_Z = 2; + break; + case 2: + j->m_DAAShadowRegs.SOP_REGS.SOP.cr4.bitreg.AGR_Z = 1; + break; + case 3: + j->m_DAAShadowRegs.SOP_REGS.SOP.cr4.bitreg.AGR_Z = 3; + break; + } + + bytes.low = j->m_DAAShadowRegs.SOP_REGS.SOP.cr4.reg; + + if (!daa_load(&bytes, board)) + return 0; + + if (!SCI_Prepare(board)) + return 0; + + return 1; +} + +static char daa_int_read(int board) +{ + BYTES bytes; + IXJ *j = &ixj[board]; + + if (!SCI_Prepare(board)) + return 0; + + bytes.high = 0x38; + bytes.low = 0x00; + outb_p(bytes.high, j->XILINXbase + 0x03); + outb_p(bytes.low, j->XILINXbase + 0x02); + + if (!SCI_Control(board, SCI_Enable_DAA)) + return 0; + + bytes.high = inb_p(j->XILINXbase + 0x03); + bytes.low = inb_p(j->XILINXbase + 0x02); + if (bytes.low != ALISDAA_ID_BYTE) { + if (ixjdebug > 0) + printk("Cannot read DAA ID Byte high = %d low = %d\n", bytes.high, bytes.low); + return 0; + } + if (!SCI_Control(board, SCI_Enable_DAA)) + return 0; + if (!SCI_Control(board, SCI_End)) + return 0; + + bytes.high = inb_p(j->XILINXbase + 0x03); + bytes.low = inb_p(j->XILINXbase + 0x02); + + j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.reg = bytes.high; + return 1; +} + +static int ixj_daa_cid_reset(int board) +{ + int i; + BYTES bytes; + IXJ *j = &ixj[board]; + + if (!SCI_Prepare(board)) + return 0; + + bytes.high = 0x58; + bytes.low = 0x00; + outb_p(bytes.high, j->XILINXbase + 0x03); + outb_p(bytes.low, j->XILINXbase + 0x02); + + if (!SCI_Control(board, SCI_Enable_DAA)) + return 0; + + if (!SCI_WaitHighSCI(board)) + return 0; + + for (i = 0; i < ALISDAA_CALLERID_SIZE - 1; i += 2) { + bytes.high = bytes.low = 0x00; + outb_p(bytes.high, j->XILINXbase + 0x03); + + if (i < ALISDAA_CALLERID_SIZE - 1) + outb_p(bytes.low, j->XILINXbase + 0x02); + + if (!SCI_Control(board, SCI_Enable_DAA)) + return 0; + + if (!SCI_WaitHighSCI(board)) + return 0; + + } + + if (!SCI_Control(board, SCI_End)) + return 0; + + return 1; +} + +static int ixj_daa_cid_read(int board) +{ + int i; + BYTES bytes; + char CID[ALISDAA_CALLERID_SIZE], mContinue; + char *pIn, *pOut; + IXJ *j = &ixj[board]; + + if (!SCI_Prepare(board)) + return 0; + + bytes.high = 0x78; + bytes.low = 0x00; + outb_p(bytes.high, j->XILINXbase + 0x03); + outb_p(bytes.low, j->XILINXbase + 0x02); + + if (!SCI_Control(board, SCI_Enable_DAA)) + return 0; + + if (!SCI_WaitHighSCI(board)) + return 0; + + bytes.high = inb_p(j->XILINXbase + 0x03); + bytes.low = inb_p(j->XILINXbase + 0x02); + if (bytes.low != ALISDAA_ID_BYTE) { + if (ixjdebug > 0) + printk("DAA Get Version Cannot read DAA ID Byte high = %d low = %d\n", bytes.high, bytes.low); + return 0; + } + for (i = 0; i < ALISDAA_CALLERID_SIZE; i += 2) { + bytes.high = bytes.low = 0x00; + outb_p(bytes.high, j->XILINXbase + 0x03); + outb_p(bytes.low, j->XILINXbase + 0x02); + + if (!SCI_Control(board, SCI_Enable_DAA)) + return 0; + + if (!SCI_WaitHighSCI(board)) + return 0; + + CID[i + 0] = inb_p(j->XILINXbase + 0x03); + CID[i + 1] = inb_p(j->XILINXbase + 0x02); + } + + if (!SCI_Control(board, SCI_End)) + return 0; + + pIn = CID; + pOut = j->m_DAAShadowRegs.CAO_REGS.CAO.CallerID; + mContinue = 1; + while (mContinue) { + if ((pIn[1] & 0x03) == 0x01) { + pOut[0] = pIn[0]; + } + if ((pIn[2] & 0x0c) == 0x04) { + pOut[1] = ((pIn[2] & 0x03) << 6) | ((pIn[1] & 0xfc) >> 2); + } + if ((pIn[3] & 0x30) == 0x10) { + pOut[2] = ((pIn[3] & 0x0f) << 4) | ((pIn[2] & 0xf0) >> 4); + } + if ((pIn[4] & 0xc0) == 0x40) { + pOut[3] = ((pIn[4] & 0x3f) << 2) | ((pIn[3] & 0xc0) >> 6); + } else { + mContinue = FALSE; + } + pIn += 5, pOut += 4; + } + memset(&j->cid, 0, sizeof(IXJ_CID)); + pOut = j->m_DAAShadowRegs.CAO_REGS.CAO.CallerID; + pOut += 4; + strncpy(j->cid.month, pOut, 2); + pOut += 2; + strncpy(j->cid.day, pOut, 2); + pOut += 2; + strncpy(j->cid.hour, pOut, 2); + pOut += 2; + strncpy(j->cid.min, pOut, 2); + pOut += 3; + j->cid.numlen = *pOut; + pOut += 1; + strncpy(j->cid.number, pOut, j->cid.numlen); + pOut += j->cid.numlen + 1; + j->cid.namelen = *pOut; + pOut += 1; + strncpy(j->cid.name, pOut, j->cid.namelen); + + ixj_daa_cid_reset(board); + return 1; +} + +static char daa_get_version(int board) +{ + BYTES bytes; + IXJ *j = &ixj[board]; + + if (!SCI_Prepare(board)) + return 0; + + bytes.high = 0x35; + bytes.low = 0x00; + outb_p(bytes.high, j->XILINXbase + 0x03); + outb_p(bytes.low, j->XILINXbase + 0x02); + + if (!SCI_Control(board, SCI_Enable_DAA)) + return 0; + + bytes.high = inb_p(j->XILINXbase + 0x03); + bytes.low = inb_p(j->XILINXbase + 0x02); + if (bytes.low != ALISDAA_ID_BYTE) { + if (ixjdebug > 0) + printk("DAA Get Version Cannot read DAA ID Byte high = %d low = %d\n", bytes.high, bytes.low); + return 0; + } + if (!SCI_Control(board, SCI_Enable_DAA)) + return 0; + + if (!SCI_Control(board, SCI_End)) + return 0; + + bytes.high = inb_p(j->XILINXbase + 0x03); + bytes.low = inb_p(j->XILINXbase + 0x02); + if (ixjdebug > 0) + printk("DAA CR5 Byte high = 0x%x low = 0x%x\n", bytes.high, bytes.low); + j->m_DAAShadowRegs.SOP_REGS.SOP.cr5.reg = bytes.high; + return bytes.high; +} + +static int daa_set_mode(int board, int mode) +{ + // NOTE: + // The DAA *MUST* be in the conversation mode if the + // PSTN line is to be seized (PSTN line off-hook). + // Taking the PSTN line off-hook while the DAA is in + // a mode other than conversation mode will cause a + // hardware failure of the ALIS-A part. + + // NOTE: + // The DAA can only go to SLEEP, RINGING or PULSEDIALING modes + // if the PSTN line is on-hook. Failure to have the PSTN line + // in the on-hook state WILL CAUSE A HARDWARE FAILURE OF THE + // ALIS-A part. + // + + + BYTES bytes; + IXJ *j = &ixj[board]; + + if (!SCI_Prepare(board)) + return 0; + + switch (mode) { + case SOP_PU_SLEEP: + j->pld_scrw.bits.daafsyncen = 0; // Turn off DAA Frame Sync + + outb_p(j->pld_scrw.byte, j->XILINXbase); + j->pld_slicw.bits.rly2 = 0; + outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01); + bytes.high = 0x10; + bytes.low = j->m_DAAShadowRegs.SOP_REGS.SOP.cr0.reg; + daa_load(&bytes, board); + if (!SCI_Prepare(board)) + return 0; + j->daa_mode = SOP_PU_SLEEP; + j->flags.pstn_ringing = 0; + j->pstn_sleeptil = jiffies + (hertz * 3); + break; + case SOP_PU_RINGING: + j->pld_scrw.bits.daafsyncen = 0; // Turn off DAA Frame Sync + + outb_p(j->pld_scrw.byte, j->XILINXbase); + j->pld_slicw.bits.rly2 = 0; + outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01); + bytes.high = 0x50; + bytes.low = j->m_DAAShadowRegs.SOP_REGS.SOP.cr0.reg; + daa_load(&bytes, board); + if (!SCI_Prepare(board)) + return 0; + j->daa_mode = SOP_PU_RINGING; + break; + case SOP_PU_CONVERSATION: + bytes.high = 0x90; + bytes.low = j->m_DAAShadowRegs.SOP_REGS.SOP.cr0.reg; + daa_load(&bytes, board); + if (!SCI_Prepare(board)) + return 0; + j->pld_slicw.bits.rly2 = 1; + outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01); + j->pld_scrw.bits.daafsyncen = 1; // Turn on DAA Frame Sync + + outb_p(j->pld_scrw.byte, j->XILINXbase); + j->daa_mode = SOP_PU_CONVERSATION; + j->flags.pstn_ringing = 0; + j->ex.bits.pstn_ring = 0; + break; + case SOP_PU_PULSEDIALING: + j->pld_scrw.bits.daafsyncen = 0; // Turn off DAA Frame Sync + + outb_p(j->pld_scrw.byte, j->XILINXbase); + j->pld_slicw.bits.rly2 = 0; + outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01); + bytes.high = 0xD0; + bytes.low = j->m_DAAShadowRegs.SOP_REGS.SOP.cr0.reg; + daa_load(&bytes, board); + if (!SCI_Prepare(board)) + return 0; + j->daa_mode = SOP_PU_PULSEDIALING; + break; + default: + break; + } + return 1; +} + +static int ixj_daa_write(int board) +{ + BYTES bytes; + IXJ *j = &ixj[board]; + + if (!SCI_Prepare(board)) + return 0; + + bytes.high = 0x14; + bytes.low = j->m_DAAShadowRegs.SOP_REGS.SOP.cr4.reg; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.SOP_REGS.SOP.cr3.reg; + bytes.low = j->m_DAAShadowRegs.SOP_REGS.SOP.cr2.reg; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.SOP_REGS.SOP.cr1.reg; + bytes.low = j->m_DAAShadowRegs.SOP_REGS.SOP.cr0.reg; + if (!daa_load(&bytes, board)) + return 0; + + if (!SCI_Prepare(board)) + return 0; + + bytes.high = 0x1F; + bytes.low = j->m_DAAShadowRegs.XOP_REGS.XOP.xr7.reg; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.XOP_xr6_W.reg; + bytes.low = j->m_DAAShadowRegs.XOP_REGS.XOP.xr5.reg; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.XOP_REGS.XOP.xr4.reg; + bytes.low = j->m_DAAShadowRegs.XOP_REGS.XOP.xr3.reg; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.XOP_REGS.XOP.xr2.reg; + bytes.low = j->m_DAAShadowRegs.XOP_REGS.XOP.xr1.reg; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.XOP_xr0_W.reg; + bytes.low = 0x00; + if (!daa_load(&bytes, board)) + return 0; + + if (!SCI_Prepare(board)) + return 0; + + bytes.high = 0x00; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[7]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[6]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[5]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[4]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[3]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[2]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[1]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[0]; + bytes.low = 0x00; + if (!daa_load(&bytes, board)) + return 0; + + if (!SCI_Control(board, SCI_End)) + return 0; + if (!SCI_WaitLowSCI(board)) + return 0; + + bytes.high = 0x01; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[7]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[6]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[5]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[4]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[3]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[2]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[1]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[0]; + bytes.low = 0x00; + if (!daa_load(&bytes, board)) + return 0; + + if (!SCI_Control(board, SCI_End)) + return 0; + if (!SCI_WaitLowSCI(board)) + return 0; + + bytes.high = 0x02; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[7]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[6]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[5]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[4]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[3]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[2]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[1]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[0]; + bytes.low = 0x00; + if (!daa_load(&bytes, board)) + return 0; + + if (!SCI_Control(board, SCI_End)) + return 0; + if (!SCI_WaitLowSCI(board)) + return 0; + + bytes.high = 0x03; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[7]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[6]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[5]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[4]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[3]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[2]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[1]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[0]; + bytes.low = 0x00; + if (!daa_load(&bytes, board)) + return 0; + + if (!SCI_Control(board, SCI_End)) + return 0; + if (!SCI_WaitLowSCI(board)) + return 0; + + bytes.high = 0x04; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[7]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[6]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[5]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[4]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[3]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[2]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[1]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[0]; + bytes.low = 0x00; + if (!daa_load(&bytes, board)) + return 0; + + if (!SCI_Control(board, SCI_End)) + return 0; + if (!SCI_WaitLowSCI(board)) + return 0; + + bytes.high = 0x05; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[7]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[6]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[5]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[4]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[3]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[2]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[1]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[0]; + bytes.low = 0x00; + if (!daa_load(&bytes, board)) + return 0; + + if (!SCI_Control(board, SCI_End)) + return 0; + if (!SCI_WaitLowSCI(board)) + return 0; + + bytes.high = 0x06; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[7]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[6]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[5]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[4]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[3]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[2]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[1]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[0]; + bytes.low = 0x00; + if (!daa_load(&bytes, board)) + return 0; + + if (!SCI_Control(board, SCI_End)) + return 0; + if (!SCI_WaitLowSCI(board)) + return 0; + + bytes.high = 0x07; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[7]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[6]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[5]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[4]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[3]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[2]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[1]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[0]; + bytes.low = 0x00; + if (!daa_load(&bytes, board)) + return 0; + + if (!SCI_Control(board, SCI_End)) + return 0; + if (!SCI_WaitLowSCI(board)) + return 0; + + bytes.high = 0x08; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[7]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[6]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[5]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[4]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[3]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[2]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[1]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[0]; + bytes.low = 0x00; + if (!daa_load(&bytes, board)) + return 0; + + if (!SCI_Control(board, SCI_End)) + return 0; + if (!SCI_WaitLowSCI(board)) + return 0; + + bytes.high = 0x09; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[3]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[2]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[1]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[0]; + bytes.low = 0x00; + if (!daa_load(&bytes, board)) + return 0; + + if (!SCI_Control(board, SCI_End)) + return 0; + if (!SCI_WaitLowSCI(board)) + return 0; + + bytes.high = 0x0A; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[3]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[2]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[1]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[0]; + bytes.low = 0x00; + if (!daa_load(&bytes, board)) + return 0; + + if (!SCI_Control(board, SCI_End)) + return 0; + if (!SCI_WaitLowSCI(board)) + return 0; + + bytes.high = 0x0B; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[3]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[2]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[1]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[0]; + bytes.low = 0x00; + if (!daa_load(&bytes, board)) + return 0; + + if (!SCI_Control(board, SCI_End)) + return 0; + if (!SCI_WaitLowSCI(board)) + return 0; + + bytes.high = 0x0C; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[3]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[2]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[1]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[0]; + bytes.low = 0x00; + if (!daa_load(&bytes, board)) + return 0; + + if (!SCI_Control(board, SCI_End)) + return 0; + if (!SCI_WaitLowSCI(board)) + return 0; + + bytes.high = 0x0D; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[3]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[2]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[1]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[0]; + bytes.low = 0x00; + if (!daa_load(&bytes, board)) + return 0; + + if (!SCI_Control(board, SCI_End)) + return 0; + if (!SCI_WaitLowSCI(board)) + return 0; + + bytes.high = 0x0E; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[7]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[6]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[5]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[4]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[3]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[2]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[1]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[0]; + bytes.low = 0x00; + if (!daa_load(&bytes, board)) + return 0; + + if (!SCI_Control(board, SCI_End)) + return 0; + if (!SCI_WaitLowSCI(board)) + return 0; + + bytes.high = 0x0F; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[7]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[6]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[5]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[4]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[3]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[2]; + bytes.low = j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[1]; + if (!daa_load(&bytes, board)) + return 0; + + bytes.high = j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[0]; + bytes.low = 0x00; + if (!daa_load(&bytes, board)) + return 0; + + udelay(32); + j->pld_scrr.byte = inb_p(j->XILINXbase); + if (!SCI_Control(board, SCI_End)) + return 0; + + return 1; +} + +int ixj_set_tone_off(unsigned short arg, int board) +{ + ixj[board].tone_off_time = arg; + + if (ixj_WriteDSPCommand(0x6E05, board)) // Set Tone Off Period + + return -1; + + if (ixj_WriteDSPCommand(arg, board)) + return -1; + + return 0; +} + +static int ixj_get_tone_on(int board) +{ + if (ixj_WriteDSPCommand(0x6E06, board)) // Get Tone On Period + + return -1; + + return 0; +} + +static int ixj_get_tone_off(int board) +{ + if (ixj_WriteDSPCommand(0x6E07, board)) // Get Tone Off Period + + return -1; + + return 0; +} + +static void ixj_busytone(int board) +{ + ixj[board].flags.ringback = 0; + ixj[board].flags.dialtone = 0; + ixj[board].flags.busytone = 1; + + ixj_set_tone_on(0x07D0, board); + ixj_set_tone_off(0x07D0, board); + ixj_play_tone(board, 27); +} + +static void ixj_dialtone(int board) +{ + ixj[board].flags.ringback = 0; + ixj[board].flags.dialtone = 1; + ixj[board].flags.busytone = 0; + + if (ixj[board].dsp.low == 0x20) { + return; + } else { + ixj_set_tone_on(0xFFFF, board); + ixj_set_tone_off(0x0000, board); + + ixj_play_tone(board, 25); + } +} + +static void ixj_cpt_stop(board) +{ + IXJ *j = &ixj[board]; + + j->flags.dialtone = 0; + j->flags.busytone = 0; + j->flags.ringback = 0; + + ixj_set_tone_on(0x0001, board); + ixj_set_tone_off(0x0000, board); + + ixj_play_tone(board, 0); + + j->tone_state = 0; + + ixj_del_timer(); + if (j->cadence_t) { + if (j->cadence_t->ce) { + kfree(j->cadence_t->ce); + } + kfree(j->cadence_t); + j->cadence_t = NULL; + } + ixj_add_timer(); + if (j->dsp.low == 0x20 || (j->play_mode == -1 && j->rec_mode == -1)) + idle(board); + if (j->play_mode != -1) + ixj_play_start(board); + if (j->rec_mode != -1) + ixj_record_start(board); +} + +static void ixj_ringback(int board) +{ + ixj[board].flags.busytone = 0; + ixj[board].flags.dialtone = 0; + ixj[board].flags.ringback = 1; + + ixj_set_tone_on(0x0FA0, board); + ixj_set_tone_off(0x2EE0, board); + ixj_play_tone(board, 26); +} + +static void ixj_testram(int board) +{ + ixj_WriteDSPCommand(0x3001, board); /* Test External SRAM */ +} + +static int ixj_build_cadence(int board, IXJ_CADENCE * cp) +{ + IXJ_CADENCE *lcp; + IXJ_CADENCE_ELEMENT *lcep; + IXJ_TONE ti; + IXJ *j = &ixj[board]; + + lcp = kmalloc(sizeof(IXJ_CADENCE), GFP_KERNEL); + if (lcp == NULL) + return -ENOMEM; + + if (copy_from_user(lcp, (char *) cp, sizeof(IXJ_CADENCE))) + return -EFAULT; + + lcep = kmalloc(sizeof(IXJ_CADENCE_ELEMENT) * lcp->elements_used, GFP_KERNEL); + if (lcep == NULL) { + kfree(lcp); + return -ENOMEM; + } + if (copy_from_user(lcep, lcp->ce, sizeof(IXJ_CADENCE_ELEMENT) * lcp->elements_used)) + return -EFAULT; + + if(j->cadence_t) + { + kfree(j->cadence_t->ce); + kfree(j->cadence_t); + } + + lcp->ce = (void *) lcep; + j->cadence_t = lcp; + j->tone_cadence_state = 0; + ixj_set_tone_on(lcp->ce[0].tone_on_time, board); + ixj_set_tone_off(lcp->ce[0].tone_off_time, board); + if (j->cadence_t->ce[j->tone_cadence_state].freq0) { + ti.tone_index = j->cadence_t->ce[j->tone_cadence_state].index; + ti.freq0 = j->cadence_t->ce[j->tone_cadence_state].freq0; + ti.gain0 = j->cadence_t->ce[j->tone_cadence_state].gain0; + ti.freq1 = j->cadence_t->ce[j->tone_cadence_state].freq1; + ti.gain1 = j->cadence_t->ce[j->tone_cadence_state].gain1; + ixj_init_tone(board, &ti); + } + ixj_play_tone(board, lcp->ce[0].index); + + return 1; +} + +static void add_caps(int board) +{ + IXJ *j = &ixj[board]; + j->caps = 0; + + j->caplist[j->caps].cap = vendor; + strcpy(j->caplist[j->caps].desc, "Quicknet Technologies, Inc. (www.quicknet.net)"); + j->caplist[j->caps].captype = vendor; + j->caplist[j->caps].handle = j->caps++; + j->caplist[j->caps].captype = device; + switch (j->cardtype) { + case 100: + strcpy(j->caplist[j->caps].desc, "Quicknet Internet PhoneJACK"); + j->caplist[j->caps].cap = 100; + break; + case 300: + strcpy(j->caplist[j->caps].desc, "Quicknet Internet LineJACK"); + j->caplist[j->caps].cap = 300; + break; + case 400: + strcpy(j->caplist[j->caps].desc, "Quicknet Internet PhoneJACK Lite"); + j->caplist[j->caps].cap = 400; + break; + case 500: + strcpy(j->caplist[j->caps].desc, "Quicknet Internet PhoneJACK PCI"); + j->caplist[j->caps].cap = 500; + break; + } + j->caplist[j->caps].handle = j->caps++; + strcpy(j->caplist[j->caps].desc, "POTS"); + j->caplist[j->caps].captype = port; + j->caplist[j->caps].cap = pots; + j->caplist[j->caps].handle = j->caps++; + switch (ixj[board].cardtype) { + case 100: + strcpy(j->caplist[j->caps].desc, "SPEAKER"); + j->caplist[j->caps].captype = port; + j->caplist[j->caps].cap = speaker; + j->caplist[j->caps].handle = j->caps++; + strcpy(j->caplist[j->caps].desc, "HANDSET"); + j->caplist[j->caps].captype = port; + j->caplist[j->caps].cap = handset; + j->caplist[j->caps].handle = j->caps++; + break; + case 300: + strcpy(j->caplist[j->caps].desc, "SPEAKER"); + j->caplist[j->caps].captype = port; + j->caplist[j->caps].cap = speaker; + j->caplist[j->caps].handle = j->caps++; + strcpy(j->caplist[j->caps].desc, "PSTN"); + j->caplist[j->caps].captype = port; + j->caplist[j->caps].cap = pstn; + j->caplist[j->caps].handle = j->caps++; + break; + } + strcpy(j->caplist[j->caps].desc, "ULAW"); + j->caplist[j->caps].captype = codec; + j->caplist[j->caps].cap = ULAW; + j->caplist[j->caps].handle = j->caps++; + strcpy(j->caplist[j->caps].desc, "LINEAR 16 bit"); + j->caplist[j->caps].captype = codec; + j->caplist[j->caps].cap = LINEAR16; + j->caplist[j->caps].handle = j->caps++; + strcpy(j->caplist[j->caps].desc, "LINEAR 8 bit"); + j->caplist[j->caps].captype = codec; + j->caplist[j->caps].cap = LINEAR8; + j->caplist[j->caps].handle = j->caps++; + strcpy(j->caplist[j->caps].desc, "Windows Sound System"); + j->caplist[j->caps].captype = codec; + j->caplist[j->caps].cap = WSS; + j->caplist[j->caps].handle = j->caps++; + if (j->ver.low != 0x12) { + strcpy(j->caplist[j->caps].desc, "G.723.1 6.3Kbps"); + j->caplist[j->caps].captype = codec; + j->caplist[j->caps].cap = G723_63; + j->caplist[j->caps].handle = j->caps++; + strcpy(j->caplist[j->caps].desc, "G.723.1 5.3Kbps"); + j->caplist[j->caps].captype = codec; + j->caplist[j->caps].cap = G723_53; + j->caplist[j->caps].handle = j->caps++; + strcpy(j->caplist[j->caps].desc, "TrueSpeech 4.8Kbps"); + j->caplist[j->caps].captype = codec; + j->caplist[j->caps].cap = TS48; + j->caplist[j->caps].handle = j->caps++; + strcpy(j->caplist[j->caps].desc, "TrueSpeech 4.1Kbps"); + j->caplist[j->caps].captype = codec; + j->caplist[j->caps].cap = TS41; + j->caplist[j->caps].handle = j->caps++; + } + if (j->cardtype == 100) { + strcpy(j->caplist[j->caps].desc, "TrueSpeech 8.5Kbps"); + j->caplist[j->caps].captype = codec; + j->caplist[j->caps].cap = TS85; + j->caplist[j->caps].handle = j->caps++; + } +} +static int capabilities_check(int board, struct phone_capability *pcreq) +{ + int cnt; + IXJ *j = &ixj[board]; + int retval = 0; + + for (cnt = 0; cnt < j->caps; cnt++) { + if (pcreq->captype == j->caplist[cnt].captype && + pcreq->cap == j->caplist[cnt].cap) { + retval = 1; + break; + } + } + return retval; +} + +int ixj_ioctl(struct inode *inode, struct file *file_p, + unsigned int cmd, unsigned long arg) +{ + IXJ_TONE ti; + IXJ_FILTER jf; + unsigned int minor = MINOR(inode->i_rdev); + int board = NUM(inode->i_rdev); + IXJ *j = &ixj[NUM(inode->i_rdev)]; + int retval = 0; + + if (ixjdebug > 1) + printk(KERN_DEBUG "phone%d ioctl, cmd: 0x%x, arg: 0x%lx\n", minor, cmd, arg); + if (minor >= IXJMAX) + return -ENODEV; + + /* + * Check ioctls only root can use. + */ + + if (!capable(CAP_SYS_ADMIN)) { + switch (cmd) { + case IXJCTL_TESTRAM: + case IXJCTL_HZ: + return -EPERM; + } + } + switch (cmd) { + case IXJCTL_TESTRAM: + ixj_testram(board); + retval = (j->ssr.high << 8) + j->ssr.low; + break; + case IXJCTL_CARDTYPE: + retval = j->cardtype; + break; + case IXJCTL_SERIAL: + retval = j->serial; + break; + case PHONE_RING_CADENCE: + j->ring_cadence = arg; + break; + case PHONE_RING_START: + ixj_ring_start(board); + break; + case PHONE_RING_STOP: + j->flags.cringing = 0; + ixj_ring_off(board); + break; + case PHONE_RING: + retval = ixj_ring(board); + break; + case PHONE_EXCEPTION: + retval = j->ex.bytes; + j->ex.bytes &= 0x03; + break; + case PHONE_HOOKSTATE: + j->ex.bits.hookstate = 0; + retval = j->r_hook; + break; + case IXJCTL_SET_LED: + LED_SetState(arg, board); + break; + case PHONE_FRAME: + retval = set_base_frame(board, arg); + break; + case PHONE_REC_CODEC: + retval = set_rec_codec(board, arg); + break; + case PHONE_REC_START: + ixj_record_start(board); + break; + case PHONE_REC_STOP: + ixj_record_stop(board); + break; + case PHONE_REC_DEPTH: + set_rec_depth(board, arg); + break; + case PHONE_REC_VOLUME: + set_rec_volume(board, arg); + break; + case PHONE_REC_LEVEL: + retval = get_rec_level(board); + break; + case IXJCTL_AEC_START: + ixj_aec_start(board, arg); + break; + case IXJCTL_AEC_STOP: + aec_stop(board); + break; + case IXJCTL_AEC_GET_LEVEL: + retval = j->aec_level; + break; + case PHONE_PLAY_CODEC: + retval = set_play_codec(board, arg); + break; + case PHONE_PLAY_START: + ixj_play_start(board); + break; + case PHONE_PLAY_STOP: + ixj_play_stop(board); + break; + case PHONE_PLAY_DEPTH: + set_play_depth(board, arg); + break; + case PHONE_PLAY_VOLUME: + set_play_volume(board, arg); + break; + case PHONE_PLAY_LEVEL: + retval = get_play_level(board); + break; + case IXJCTL_DSP_TYPE: + retval = (j->dsp.high << 8) + j->dsp.low; + break; + case IXJCTL_DSP_VERSION: + retval = (j->ver.high << 8) + j->ver.low; + break; + case IXJCTL_HZ: + hertz = arg; + break; + case IXJCTL_RATE: + if (arg > hertz) + retval = -1; + else + samplerate = arg; + break; + case IXJCTL_DRYBUFFER_READ: + put_user(j->drybuffer, (unsigned long *) arg); + break; + case IXJCTL_DRYBUFFER_CLEAR: + j->drybuffer = 0; + break; + case IXJCTL_FRAMES_READ: + put_user(j->framesread, (unsigned long *) arg); + break; + case IXJCTL_FRAMES_WRITTEN: + put_user(j->frameswritten, (unsigned long *) arg); + break; + case IXJCTL_READ_WAIT: + put_user(j->read_wait, (unsigned long *) arg); + break; + case IXJCTL_WRITE_WAIT: + put_user(j->write_wait, (unsigned long *) arg); + break; + case PHONE_MAXRINGS: + j->maxrings = arg; + break; + case PHONE_SET_TONE_ON_TIME: + ixj_set_tone_on(arg, board); + break; + case PHONE_SET_TONE_OFF_TIME: + ixj_set_tone_off(arg, board); + break; + case PHONE_GET_TONE_ON_TIME: + if (ixj_get_tone_on(board)) { + retval = -1; + } else { + retval = (j->ssr.high << 8) + j->ssr.low; + } + break; + case PHONE_GET_TONE_OFF_TIME: + if (ixj_get_tone_off(board)) { + retval = -1; + } else { + retval = (j->ssr.high << 8) + j->ssr.low; + } + break; + case PHONE_PLAY_TONE: + if (!j->tone_state) + ixj_play_tone(board, arg); + break; + case PHONE_GET_TONE_STATE: + retval = j->tone_state; + break; + case PHONE_DTMF_READY: + retval = j->ex.bits.dtmf_ready; + break; + case PHONE_GET_DTMF: + if (ixj_hookstate(board)) { + if (j->dtmf_rp != j->dtmf_wp) { + retval = j->dtmfbuffer[j->dtmf_rp]; + j->dtmf_rp++; + if (j->dtmf_rp == 79) + j->dtmf_rp = 0; + if (j->dtmf_rp == j->dtmf_wp) { + j->ex.bits.dtmf_ready = j->dtmf_rp = j->dtmf_wp = 0; + } + } + } + break; + case PHONE_GET_DTMF_ASCII: + if (ixj_hookstate(board)) { + if (j->dtmf_rp != j->dtmf_wp) { + switch (j->dtmfbuffer[j->dtmf_rp]) { + case 10: + retval = 42; //'*'; + + break; + case 11: + retval = 48; //'0'; + + break; + case 12: + retval = 35; //'#'; + + break; + case 28: + retval = 65; //'A'; + + break; + case 29: + retval = 66; //'B'; + + break; + case 30: + retval = 67; //'C'; + + break; + case 31: + retval = 68; //'D'; + + break; + default: + retval = 48 + j->dtmfbuffer[j->dtmf_rp]; + break; + } + j->dtmf_rp++; + if (j->dtmf_rp == 79) + j->dtmf_rp = 0; +// if(j->dtmf_rp == j->dtmf_wp) + { + j->ex.bits.dtmf_ready = j->dtmf_rp = j->dtmf_wp = 0; + } + } + } + break; + case PHONE_DTMF_OOB: + j->flags.dtmf_oob = arg; + break; + case PHONE_DIALTONE: + ixj_dialtone(board); + break; + case PHONE_BUSY: + ixj_busytone(board); + break; + case PHONE_RINGBACK: + ixj_ringback(board); + break; + case PHONE_CPT_STOP: + ixj_cpt_stop(board); + break; + case IXJCTL_DSP_IDLE: + idle(board); + break; + case IXJCTL_MIXER: + ixj_mixer(arg, board); + break; + case IXJCTL_DAA_COEFF_SET: + switch (arg) { + case DAA_US: + DAA_Coeff_US(board); + ixj_daa_write(board); + break; + case DAA_UK: + DAA_Coeff_UK(board); + ixj_daa_write(board); + break; + case DAA_FRANCE: + DAA_Coeff_France(board); + ixj_daa_write(board); + break; + case DAA_GERMANY: + DAA_Coeff_Germany(board); + ixj_daa_write(board); + break; + case DAA_AUSTRALIA: + DAA_Coeff_Australia(board); + ixj_daa_write(board); + break; + case DAA_JAPAN: + DAA_Coeff_Japan(board); + ixj_daa_write(board); + break; + default: + break; + } + break; + case IXJCTL_DAA_AGAIN: + ixj_daa_cr4(board, arg | 0x02); + break; + case IXJCTL_PSTN_LINETEST: + retval = ixj_linetest(board); + break; + case IXJCTL_CID: + if (copy_to_user((char *) arg, &j->cid, sizeof(IXJ_CID))) + return -EFAULT; + j->ex.bits.caller_id = 0; + break; + case IXJCTL_WINK_DURATION: + j->winktime = arg; + break; + case IXJCTL_PORT: + if (arg) + retval = ixj_set_port(board, arg); + else + retval = j->port; + break; + case IXJCTL_POTS_PSTN: + retval = ixj_set_pots(board, arg); + break; + case PHONE_CAPABILITIES: + retval = j->caps; + break; + case PHONE_CAPABILITIES_LIST: + if (copy_to_user((char *) arg, j->caplist, sizeof(struct phone_capability) * j->caps)) + return -EFAULT; + break; + case PHONE_CAPABILITIES_CHECK: + retval = capabilities_check(board, (struct phone_capability *) arg); + break; + case PHONE_PSTN_SET_STATE: + daa_set_mode(board, arg); + break; + case PHONE_PSTN_GET_STATE: + retval = j->daa_mode; + j->ex.bits.pstn_ring = 0; + break; + case IXJCTL_SET_FILTER: + if (copy_from_user(&jf, (char *) arg, sizeof(ti))) + return -EFAULT; + retval = ixj_init_filter(board, &jf); + break; + case IXJCTL_GET_FILTER_HIST: + retval = j->filter_hist[arg]; + break; + case IXJCTL_INIT_TONE: + copy_from_user(&ti, (char *) arg, sizeof(ti)); + retval = ixj_init_tone(board, &ti); + break; + case IXJCTL_TONE_CADENCE: + retval = ixj_build_cadence(board, (IXJ_CADENCE *) arg); + break; + case IXJCTL_INTERCOM_STOP: + ixj[board].intercom = -1; + ixj[arg].intercom = -1; + ixj_record_stop(board); + ixj_record_stop(arg); + ixj_play_stop(board); + ixj_play_stop(arg); + idle(board); + idle(arg); + break; + case IXJCTL_INTERCOM_START: + ixj[board].intercom = arg; + ixj[arg].intercom = board; + ixj_play_start(arg); + ixj_record_start(board); + ixj_play_start(board); + ixj_record_start(arg); + idle(board); + idle(arg); + break; + } + return retval; +} + +static int ixj_fasync(int fd, struct file *file_p, int mode) +{ + IXJ *j = &ixj[NUM(file_p->f_dentry->d_inode->i_rdev)]; + return fasync_helper(fd, file_p, mode, &j->async_queue); +} + +struct file_operations ixj_fops = +{ + NULL, /* ixj_lseek */ + ixj_enhanced_read, + ixj_enhanced_write, + NULL, /* ixj_readdir */ + ixj_poll, + ixj_ioctl, + NULL, /* ixj_mmap */ +// ixj_open, + NULL, /* ixj_open */ + NULL, /* ixj_flush */ + ixj_release, + NULL, /* ixj_fsync */ + ixj_fasync, /* ixj_fasync */ + NULL, /* media change */ + NULL, /* revalidate */ + NULL /* lock */ +}; + +static int ixj_linetest(int board) +{ + unsigned long jifwait; + IXJ *j = &ixj[board]; + + if (!j->flags.pots_correct) { + j->flags.pots_correct = 1; // Testing + + daa_int_read(board); //Clear DAA Interrupt flags + // + // Hold all relays in the normally de-energized position. + // + + j->pld_slicw.bits.rly1 = 0; + j->pld_slicw.bits.rly2 = 0; + j->pld_slicw.bits.rly3 = 0; + outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01); + j->pld_scrw.bits.daafsyncen = 0; // Turn off DAA Frame Sync + + outb_p(j->pld_scrw.byte, j->XILINXbase); + j->pld_slicr.byte = inb_p(j->XILINXbase + 0x01); + if (j->pld_slicr.bits.potspstn) { + j->flags.pots_pstn = 1; + j->flags.pots_correct = 0; + LED_SetState(0x4, board); + } else { + j->flags.pots_pstn = 0; + j->pld_slicw.bits.rly1 = 0; + j->pld_slicw.bits.rly2 = 0; + j->pld_slicw.bits.rly3 = 1; + outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01); + j->pld_scrw.bits.daafsyncen = 0; // Turn off DAA Frame Sync + + outb_p(j->pld_scrw.byte, j->XILINXbase); + daa_set_mode(board, SOP_PU_CONVERSATION); + jifwait = jiffies + hertz; + while (time_before(jiffies, jifwait)) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + daa_int_read(board); + daa_set_mode(board, SOP_PU_SLEEP); + if (j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.bitreg.VDD_OK) { + j->flags.pots_correct = 0; // Should not be line voltage on POTS port. + + LED_SetState(0x4, board); + j->pld_slicw.bits.rly3 = 0; + outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01); + } else { + j->flags.pots_correct = 1; + LED_SetState(0x8, board); + j->pld_slicw.bits.rly1 = 1; + j->pld_slicw.bits.rly2 = 0; + j->pld_slicw.bits.rly3 = 0; + outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01); + } + } + } + if (!j->flags.pstn_present) { + j->pld_slicw.bits.rly3 = 0; + outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01); + daa_set_mode(board, SOP_PU_CONVERSATION); + jifwait = jiffies + hertz; + while (time_before(jiffies, jifwait)) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + daa_int_read(board); + daa_set_mode(board, SOP_PU_SLEEP); + if (j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.bitreg.VDD_OK) { + j->flags.pstn_present = 1; + } else { + j->flags.pstn_present = 0; + } + } + if (j->flags.pstn_present) { + if (j->flags.pots_correct) { + LED_SetState(0xA, board); + } else { + LED_SetState(0x6, board); + } + } else { + if (j->flags.pots_correct) { + LED_SetState(0x9, board); + } else { + LED_SetState(0x5, board); + } + } + return j->flags.pstn_present; +} + +static int ixj_selfprobe(int board) +{ + unsigned short cmd; + unsigned long jif; + BYTES bytes; + IXJ *j = &ixj[board]; + + if (ixjdebug > 0) + printk(KERN_INFO "Write IDLE to Software Control Register\n"); + + if (ixj_WriteDSPCommand(0x0000, board)) /* Write IDLE to Software Control Register */ + return -1; + +// The read values of the SSR should be 0x00 for the IDLE command + if (j->ssr.low || j->ssr.high) + return -1; + + if (ixjdebug > 0) + printk(KERN_INFO "Get Device ID Code\n"); + + if (ixj_WriteDSPCommand(0x3400, board)) /* Get Device ID Code */ + return -1; + + j->dsp.low = j->ssr.low; + j->dsp.high = j->ssr.high; + + if (ixjdebug > 0) + printk(KERN_INFO "Get Device Version Code\n"); + + if (ixj_WriteDSPCommand(0x3800, board)) /* Get Device Version Code */ + return -1; + + j->ver.low = j->ssr.low; + j->ver.high = j->ssr.high; + + if (!j->cardtype) { + if (j->dsp.low == 0x21) { +// j->XILINXbase = j->DSPbase + 0x10; + bytes.high = bytes.low = inb_p(j->XILINXbase + 0x02); + outb_p(bytes.low ^ 0xFF, j->XILINXbase + 0x02); + // Test for Internet LineJACK or Internet PhoneJACK Lite + bytes.low = inb_p(j->XILINXbase + 0x02); + if (bytes.low == bytes.high) // Register is read only on + // Internet PhoneJack Lite + { + j->cardtype = 400; // Internet PhoneJACK Lite + + if (check_region(j->XILINXbase, 4)) { + printk(KERN_INFO "ixj: can't get I/O address 0x%x\n", j->XILINXbase); + return -1; + } + request_region(j->XILINXbase, 4, "ixj control"); + j->pld_slicw.pcib.e1 = 1; + outb_p(j->pld_slicw.byte, j->XILINXbase); + } else { + j->cardtype = 300; // Internet LineJACK + + if (check_region(j->XILINXbase, 8)) { + printk(KERN_INFO "ixj: can't get I/O address 0x%x\n", j->XILINXbase); + return -1; + } + request_region(j->XILINXbase, 8, "ixj control"); + } + } else if (j->dsp.low == 0x22) { + j->cardtype = 500; // Internet PhoneJACK PCI + + request_region(j->XILINXbase, 4, "ixj control"); + j->pld_slicw.pcib.e1 = 1; + outb_p(j->pld_slicw.byte, j->XILINXbase); + } else + j->cardtype = 100; // Internet PhoneJACK + + } else { + switch (j->cardtype) { + case 100: // Internet PhoneJACK + + if (!j->dsp.low != 0x20) { + j->dsp.high = 0x80; + j->dsp.low = 0x20; + ixj_WriteDSPCommand(0x3800, board); + j->ver.low = j->ssr.low; + j->ver.high = j->ssr.high; + } + break; + case 300: // Internet LineJACK + + if (check_region(j->XILINXbase, 8)) { + printk(KERN_INFO "ixj: can't get I/O address 0x%x\n", j->XILINXbase); + return -1; + } + request_region(j->XILINXbase, 8, "ixj control"); + break; + case 400: //Internet PhoneJACK Lite + + case 500: //Internet PhoneJACK PCI + + if (check_region(j->XILINXbase, 4)) { + printk(KERN_INFO "ixj: can't get I/O address 0x%x\n", j->XILINXbase); + return -1; + } + request_region(j->XILINXbase, 4, "ixj control"); + j->pld_slicw.pcib.e1 = 1; + outb_p(j->pld_slicw.byte, j->XILINXbase); + break; + } + } + if (j->dsp.low == 0x20 || j->cardtype == 400 || j->cardtype == 500) { + if (ixjdebug > 0) + printk(KERN_INFO "Write CODEC config to Software Control Register\n"); + + if (ixj_WriteDSPCommand(0xC462, board)) /* Write CODEC config to Software Control Register */ + return -1; + + if (ixjdebug > 0) + printk(KERN_INFO "Write CODEC timing to Software Control Register\n"); + + if (j->cardtype == 100) { + cmd = 0x9FF2; + } else { + cmd = 0x9FF5; + } + if (ixj_WriteDSPCommand(cmd, board)) /* Write CODEC timing to Software Control Register */ + return -1; + } else { + if (set_base_frame(board, 30) != 30) + return -1; + + if (j->cardtype == 300) { + if (ixjdebug > 0) + printk(KERN_INFO "Write CODEC config to Software Control Register\n"); + + if (ixj_WriteDSPCommand(0xC528, board)) /* Write CODEC config to Software Control Register */ + return -1; + + if (ixjdebug > 0) + printk(KERN_INFO "Turn on the PLD Clock at 8Khz\n"); + + j->pld_clock.byte = 0; + outb_p(j->pld_clock.byte, j->XILINXbase + 0x04); + } + } + + if (j->dsp.low == 0x20) { + if (ixjdebug > 0) + printk(KERN_INFO "Configure GPIO pins\n"); + + j->gpio.bytes.high = 0x09; +/* bytes.low = 0xEF; 0xF7 */ + j->gpio.bits.gpio1 = 1; + j->gpio.bits.gpio2 = 1; + j->gpio.bits.gpio3 = 0; + j->gpio.bits.gpio4 = 1; + j->gpio.bits.gpio5 = 1; + j->gpio.bits.gpio6 = 1; + j->gpio.bits.gpio7 = 1; + ixj_WriteDSPCommand(ixj[board].gpio.word, board); /* Set GPIO pin directions */ + + if (ixjdebug > 0) + printk(KERN_INFO "Enable SLIC\n"); + + j->gpio.bytes.high = 0x0B; + j->gpio.bytes.low = 0x00; + j->gpio.bits.gpio1 = 0; + j->gpio.bits.gpio2 = 1; + j->gpio.bits.gpio5 = 0; + ixj_WriteDSPCommand(ixj[board].gpio.word, board); /* send the ring stop signal */ + j->port = PORT_POTS; + } else { + if (j->cardtype == 300) { + LED_SetState(0x1, board); + jif = jiffies + (hertz / 10); + while (time_before(jiffies, jif)) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + LED_SetState(0x2, board); + jif = jiffies + (hertz / 10); + while (time_before(jiffies, jif)) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + LED_SetState(0x4, board); + jif = jiffies + (hertz / 10); + while (time_before(jiffies, jif)) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + LED_SetState(0x8, board); + jif = jiffies + (hertz / 10); + while (time_before(jiffies, jif)) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + LED_SetState(0x0, board); + + daa_get_version(board); + + if (ixjdebug > 0) + printk("Loading DAA Coefficients\n"); + + DAA_Coeff_US(board); + if (!ixj_daa_write(board)) + printk("DAA write failed on board %d\n", board); + + ixj_daa_cid_reset(board); + + j->flags.pots_correct = 0; + j->flags.pstn_present = 0; + + ixj_linetest(board); + + if (j->flags.pots_correct) { + j->pld_scrw.bits.daafsyncen = 0; // Turn off DAA Frame Sync + + outb_p(j->pld_scrw.byte, j->XILINXbase); + j->pld_slicw.bits.rly1 = 1; + j->pld_slicw.bits.spken = 1; + outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01); + SLIC_SetState(PLD_SLIC_STATE_STANDBY, board); + j->port = PORT_POTS; + } + if (ixjdebug > 0) + printk(KERN_INFO "Enable Mixer\n"); + + ixj_mixer(0x0000, board); //Master Volume Left unmute 0db + + ixj_mixer(0x0100, board); //Master Volume Right unmute 0db + + ixj_mixer(0x0F00, board); //Mono Out Volume unmute 0db + + ixj_mixer(0x0C00, board); //Mono1 Volume unmute 0db + + ixj_mixer(0x0200, board); //Voice Left Volume unmute 0db + + ixj_mixer(0x0300, board); //Voice Right Volume unmute 0db + + ixj_mixer(0x110C, board); //Voice Left and Right out + + ixj_mixer(0x1401, board); //Mono1 switch on mixer left + + ixj_mixer(0x1501, board); //Mono1 switch on mixer right + + ixj_mixer(0x1700, board); //Clock select + + ixj_mixer(0x1800, board); //ADC Source select + + } else { + j->port = PORT_POTS; + SLIC_SetState(PLD_SLIC_STATE_STANDBY, board); + } + } + + j->intercom = -1; + j->framesread = j->frameswritten = 0; + j->rxreadycheck = j->txreadycheck = 0; + + if (ixj_WriteDSPCommand(0x0000, board)) /* Write IDLE to Software Control Register */ + return -1; + +// The read values of the SSR should be 0x00 for the IDLE command + if (j->ssr.low || j->ssr.high) + return -1; + + if (ixjdebug > 0) + printk(KERN_INFO "Enable Line Monitor\n"); + + if (ixjdebug > 0) + printk(KERN_INFO "Set Line Monitor to Asyncronous Mode\n"); + + if (ixj_WriteDSPCommand(0x7E01, board)) // Asynchronous Line Monitor + + return -1; + + if (ixjdebug > 0) + printk(KERN_INFO "Enable DTMF Detectors\n"); + + if (ixj_WriteDSPCommand(0x5151, board)) // Enable DTMF detection + + return -1; + + if (ixj_WriteDSPCommand(0x6E01, board)) // Set Asyncronous Tone Generation + + return -1; + + set_rec_depth(board, 2); // Set Record Channel Limit to 2 frames + + set_play_depth(board, 2); // Set Playback Channel Limit to 2 frames + + j->ex.bits.dtmf_ready = 0; + j->dtmf_state = 0; + j->dtmf_wp = ixj[board].dtmf_rp = 0; + + j->rec_mode = ixj[board].play_mode = -1; + j->flags.ringing = 0; + j->maxrings = MAXRINGS; + j->ring_cadence = USA_RING_CADENCE; + j->drybuffer = 0; + j->winktime = 320; + j->flags.dtmf_oob = 0; + + /* must be a device on the specified address */ + /* Register with the Telephony for Linux subsystem */ + j->p.f_op = &ixj_fops; + j->p.open = ixj_open; + phone_register_device(&j->p, PHONE_UNIT_ANY); + + add_caps(board); + + return 0; +} + +int ixj_read_proc(char *buf, char **start, off_t offset, int len, int unused) +{ + int cnt; + IXJ *j; + len = 0; + + len += sprintf(buf + len, "\n%s", ixj_c_rcsid); + len += sprintf(buf + len, "\n%s", ixj_h_rcsid); + len += sprintf(buf + len, "\n%s", ixjuser_h_rcsid); + + for (cnt = 0; cnt < IXJMAX; cnt++) { + j = &ixj[cnt]; + if (j->DSPbase) { + len += sprintf(buf + len, "\nCard Num %d", cnt); + len += sprintf(buf + len, "\nDSP Base Address 0x%4.4x", j->DSPbase); + if (j->cardtype != 100) + len += sprintf(buf + len, "\nXILINX Base Address 0x%4.4x", j->XILINXbase); + len += sprintf(buf + len, "\nDSP Type %2.2x%2.2x", j->dsp.high, j->dsp.low); + len += sprintf(buf + len, "\nDSP Version %2.2x.%2.2x", j->ver.high, j->ver.low); + len += sprintf(buf + len, "\nSerial Number %8.8x", j->serial); + switch (j->cardtype) { + case (100): + len += sprintf(buf + len, "\nCard Type = Internet PhoneJACK"); + break; + case (300): + len += sprintf(buf + len, "\nCard Type = Internet LineJACK"); + if (j->flags.g729_loaded) + len += sprintf(buf + len, " w/G.729 A/B"); + break; + case (400): + len += sprintf(buf + len, "\nCard Type = Internet PhoneJACK Lite"); + if (j->flags.g729_loaded) + len += sprintf(buf + len, " w/G.729 A/B"); + break; + case (500): + len += sprintf(buf + len, "\nCard Type = Internet PhoneJACK PCI"); + if (j->flags.g729_loaded) + len += sprintf(buf + len, " w/G.729 A/B"); + break; + default: + len += sprintf(buf + len, "\nCard Type = %d", j->cardtype); + break; + } + len += sprintf(buf + len, "\nCapabilities %d", j->caps); + if (j->dsp.low != 0x20) + len += sprintf(buf + len, "\nDSP Processor load %d", j->proc_load); +// if(j->intercom != -1) + // len += sprintf(buf+len, "\nIntercom call to board %2.2x", j->intercom); + +// len += sprintf(buf+len, "\nRead buffer size %d", j->read_buffer_size); + // len += sprintf(buf+len, "\nWrite buffer size %d", j->write_buffer_size); + len += sprintf(buf + len, "\nPlay CODEC "); + switch (j->play_codec) { + case G723_63: + len += sprintf(buf + len, "G.723.1 6.3"); + break; + case G723_53: + len += sprintf(buf + len, "G.723.1 5.3"); + break; + case TS85: + len += sprintf(buf + len, "TrueSpeech 8.5"); + break; + case TS48: + len += sprintf(buf + len, "TrueSpeech 4.8"); + break; + case TS41: + len += sprintf(buf + len, "TrueSpeech 4.1"); + break; + case G728: + len += sprintf(buf + len, "G.728"); + break; + case G729: + len += sprintf(buf + len, "G.729"); + break; + case ULAW: + len += sprintf(buf + len, "uLaw"); + break; + case ALAW: + len += sprintf(buf + len, "aLaw"); + break; + case LINEAR16: + len += sprintf(buf + len, "16 bit Linear"); + break; + case LINEAR8: + len += sprintf(buf + len, "8 bit Linear"); + break; + case WSS: + len += sprintf(buf + len, "Windows Sound System"); + break; + default: + len += sprintf(buf + len, "NO CODEC CHOSEN"); + break; + } +// len += sprintf(buf+len, "\nPlay Frame Size %d", j->play_frame_size); + // len += sprintf(buf+len, "\nInfo_write.convert_mode = %d", j->Info_write.convert_mode); + // len += sprintf(buf+len, "\nInfo_write.convert_dir = %d", j->Info_write.convert_dir); + len += sprintf(buf + len, "\nRecord CODEC "); + switch (j->rec_codec) { + case G723_63: + len += sprintf(buf + len, "G.723.1 6.3"); + break; + case G723_53: + len += sprintf(buf + len, "G.723.1 5.3"); + break; + case TS85: + len += sprintf(buf + len, "TrueSpeech 8.5"); + break; + case TS48: + len += sprintf(buf + len, "TrueSpeech 4.8"); + break; + case TS41: + len += sprintf(buf + len, "TrueSpeech 4.1"); + break; + case G728: + len += sprintf(buf + len, "G.728"); + break; + case G729: + len += sprintf(buf + len, "G.729"); + break; + case ULAW: + len += sprintf(buf + len, "uLaw"); + break; + case ALAW: + len += sprintf(buf + len, "aLaw"); + break; + case LINEAR16: + len += sprintf(buf + len, "16 bit Linear"); + break; + case LINEAR8: + len += sprintf(buf + len, "8 bit Linear"); + break; + case WSS: + len += sprintf(buf + len, "Windows Sound System"); + break; + default: + len += sprintf(buf + len, "NO CODEC CHOSEN"); + break; + } +// len += sprintf(buf+len, "\nRecord Frame Size %d", j->rec_frame_size); + switch (j->aec_level) { + case AEC_OFF: + len += sprintf(buf + len, "\n AEC OFF"); + break; + case AEC_LOW: + len += sprintf(buf + len, "\n AEC LOW"); + break; + case AEC_MED: + len += sprintf(buf + len, "\n AEC MED"); + break; + case AEC_HIGH: + len += sprintf(buf + len, "\n AEC HIGH"); + break; + } +// len += sprintf(buf+len, "\nInfo_read.convert_mode = %d", j->Info_read.convert_mode); + // len += sprintf(buf+len, "\nInfo_read.convert_dir = %d", j->Info_read.convert_dir); + len += sprintf(buf + len, "\nHook state %d", j->r_hook); // ixj_hookstate(cnt)); + + if (j->cardtype == 300) { + len += sprintf(buf + len, "\nPOTS Correct %d", j->flags.pots_correct); + len += sprintf(buf + len, "\nPSTN Present %d", j->flags.pstn_present); + len += sprintf(buf + len, "\nPOTS to PSTN %d", j->flags.pots_pstn); +// len += sprintf(buf+len, "\nDAA Interrupt flags %d", j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.reg); + // len += sprintf(buf+len, "\nDAA version %d", j->m_DAAShadowRegs.SOP_REGS.SOP.cr5.reg); + switch (j->daa_mode) { + case SOP_PU_SLEEP: + len += sprintf(buf + len, "\nDAA PSTN On Hook"); + break; + case SOP_PU_RINGING: + len += sprintf(buf + len, "\nDAA PSTN Ringing"); + break; + case SOP_PU_CONVERSATION: + len += sprintf(buf + len, "\nDAA PSTN Off Hook"); + break; + case SOP_PU_PULSEDIALING: + len += sprintf(buf + len, "\nDAA PSTN Pulse Dialing"); + break; + } +// len += sprintf(buf+len, "\nDAA PSTN Ring flag = %d", j->pstn_ringing); + } + switch (j->port) { + case PORT_POTS: + len += sprintf(buf + len, "\nPort POTS"); + break; + case PORT_PSTN: + len += sprintf(buf + len, "\nPort PSTN"); + break; + case PORT_SPEAKER: + len += sprintf(buf + len, "\nPort SPEAKER/MIC"); + break; + case PORT_HANDSET: + len += sprintf(buf + len, "\nPort HANDSET"); + break; + } + if (j->dsp.low == 0x21 || j->dsp.low == 0x22) { + len += sprintf(buf + len, "\nSLIC state "); + switch (SLIC_GetState(cnt)) { + case PLD_SLIC_STATE_OC: + len += sprintf(buf + len, "OC"); + break; + case PLD_SLIC_STATE_RINGING: + len += sprintf(buf + len, "RINGING"); + break; + case PLD_SLIC_STATE_ACTIVE: + len += sprintf(buf + len, "ACTIVE"); + break; + case PLD_SLIC_STATE_OHT: // On-hook transmit + + len += sprintf(buf + len, "OHT"); + break; + case PLD_SLIC_STATE_TIPOPEN: + len += sprintf(buf + len, "TIPOPEN"); + break; + case PLD_SLIC_STATE_STANDBY: + len += sprintf(buf + len, "STANDBY"); + break; + case PLD_SLIC_STATE_APR: // Active polarity reversal + + len += sprintf(buf + len, "APR"); + break; + case PLD_SLIC_STATE_OHTPR: // OHT polarity reversal + + len += sprintf(buf + len, "OHTPR"); + break; + default: + len += sprintf(buf + len, "%d", SLIC_GetState(cnt)); + break; + } + } +// len += sprintf(buf+len, "\nRead buffers ready %d", j->read_buffer_ready); + // len += sprintf(buf+len, "\nWrite buffers empty %d",j->write_buffers_empty); + +// len += sprintf(buf+len, "\nDTMF ready %d", j->ex.bits.dtmf_ready); + // len += sprintf(buf+len, "\nDTMF proc %d", j->dtmf_proc); + // len += sprintf(buf+len, "\nDTMF wp %d", j->dtmf_wp); + // len += sprintf(buf+len, "\nDTMF rp %d", j->dtmf_rp); + // len += sprintf(buf+len, "\nDTMF digits "); + // for(x=j->dtmf_rp; x dtmf_wp; x++) + // { + // if(x == 79) + // x = 0; + // len += sprintf(buf+len, "%x", j->dtmfbuffer[x]); + // } +#ifdef PERFMON_STATS + len += sprintf(buf + len, "\nTimer Checks %ld", j->timerchecks); + + len += sprintf(buf + len, "\nRX Ready Checks %ld", j->rxreadycheck); + len += sprintf(buf + len, "\nTX Ready Checks %ld", j->txreadycheck); + len += sprintf(buf + len, "\nBase Frame %2.2x.%2.2x", j->baseframe.high, j->baseframe.low); + len += sprintf(buf + len, "\nFrames Read %ld", j->framesread); + len += sprintf(buf + len, "\nFrames Written %ld", j->frameswritten); + len += sprintf(buf + len, "\nDry Buffer %ld", j->drybuffer); + len += sprintf(buf + len, "\nRead Waits %ld", j->read_wait); + len += sprintf(buf + len, "\nWrite Waits %ld", j->write_wait); +#endif + len += sprintf(buf + len, "\n"); + } + } + return len; +} + +static struct proc_dir_entry ixj_proc_entry = +{ + 0, + 3, "ixj", + S_IFREG | S_IRUGO, + 1, 0, 0, + 0, + NULL, + &ixj_read_proc +}; + +static void cleanup(void) +{ + int cnt; + + del_timer(&ixj_timer); +// if (ixj_major) + // unregister_chrdev(ixj_major, "ixj"); + for (cnt = 0; cnt < IXJMAX; cnt++) { + if (ixj[cnt].cardtype == 300) { + ixj[cnt].pld_scrw.bits.daafsyncen = 0; // Turn off DAA Frame Sync + + outb_p(ixj[cnt].pld_scrw.byte, ixj[cnt].XILINXbase); + ixj[cnt].pld_slicw.bits.rly1 = 0; + ixj[cnt].pld_slicw.bits.rly2 = 0; + ixj[cnt].pld_slicw.bits.rly3 = 0; + outb_p(ixj[cnt].pld_slicw.byte, ixj[cnt].XILINXbase + 0x01); + LED_SetState(0x0, cnt); + + release_region(ixj[cnt].XILINXbase, 8); + } + if (ixj[cnt].cardtype == 400 || ixj[cnt].cardtype == 500) { + release_region(ixj[cnt].XILINXbase, 4); + } + if (ixj[cnt].DSPbase) { + release_region(ixj[cnt].DSPbase, 16); + phone_unregister_device(&ixj[cnt].p); + } + if (ixj[cnt].read_buffer) + kfree(ixj[cnt].read_buffer); + if (ixj[cnt].write_buffer) + kfree(ixj[cnt].write_buffer); +#ifdef CONFIG_ISAPNP + if (ixj[cnt].dev) + ixj[cnt].dev->deactivate(ixj[cnt].dev); +#endif + } + proc_unregister(&proc_root, ixj_proc_entry.low_ino); +} + + +// Typedefs +typedef struct { + BYTE length; + DWORD bits; +} DATABLOCK; + +static void PCIEE_WriteBit(WORD wEEPROMAddress, BYTE lastLCC, BYTE byData) +{ + lastLCC = lastLCC & 0xfb; + lastLCC = lastLCC | (byData ? 4 : 0); + outb(lastLCC, wEEPROMAddress); //set data out bit as appropriate + + udelay(1000); + lastLCC = lastLCC | 0x01; + outb(lastLCC, wEEPROMAddress); //SK rising edge + + byData = byData << 1; + lastLCC = lastLCC & 0xfe; + + udelay(1000); + outb(lastLCC, wEEPROMAddress); //after delay, SK falling edge + +} + +static BYTE PCIEE_ReadBit(WORD wEEPROMAddress, BYTE lastLCC) +{ + udelay(1000); + lastLCC = lastLCC | 0x01; + outb(lastLCC, wEEPROMAddress); //SK rising edge + + lastLCC = lastLCC & 0xfe; + udelay(1000); + outb(lastLCC, wEEPROMAddress); //after delay, SK falling edge + + return ((inb(wEEPROMAddress) >> 3) & 1); +} + +static BOOL PCIEE_ReadWord(WORD wAddress, WORD wLoc, WORD * pwResult) +{ + BYTE lastLCC; + WORD wEEPROMAddress = wAddress + 3; + DWORD i; + BYTE byResult; + + *pwResult = 0; + + lastLCC = inb(wEEPROMAddress); + + lastLCC = lastLCC | 0x02; + lastLCC = lastLCC & 0xfe; + outb(lastLCC, wEEPROMAddress); // CS hi, SK lo + + udelay(1000); // delay + + PCIEE_WriteBit(wEEPROMAddress, lastLCC, 1); + PCIEE_WriteBit(wEEPROMAddress, lastLCC, 1); + PCIEE_WriteBit(wEEPROMAddress, lastLCC, 0); + + for (i = 0; i < 8; i++) { + PCIEE_WriteBit(wEEPROMAddress, lastLCC, wLoc & 0x80 ? 1 : 0); + wLoc <<= 1; + } + + for (i = 0; i < 16; i++) { + byResult = PCIEE_ReadBit(wEEPROMAddress, lastLCC); + *pwResult = (*pwResult << 1) | byResult; + } + + udelay(1000); // another delay + + lastLCC = lastLCC & 0xfd; + outb(lastLCC, wEEPROMAddress); // negate CS + + return 0; +} + +static DWORD PCIEE_GetSerialNumber(WORD wAddress) +{ + WORD wLo, wHi; + + if (PCIEE_ReadWord(wAddress, 62, &wLo)) + return 0; + + if (PCIEE_ReadWord(wAddress, 63, &wHi)) + return 0; + + return (((DWORD) wHi << 16) | wLo); +} + +static int dspio[IXJMAX + 1] = {0,}; +static int xio[IXJMAX + 1] = {0,}; + +MODULE_DESCRIPTION("Internet PhoneJACK/Internet LineJACK module - www.quicknet.net"); +MODULE_AUTHOR("Ed Okerson "); + +MODULE_PARM(dspio, "1-" __MODULE_STRING(IXJMAX) "i"); +MODULE_PARM(xio, "1-" __MODULE_STRING(IXJMAX) "i"); + +#ifdef MODULE + +void cleanup_module(void) +{ + cleanup(); +} + +int init_module(void) +#else +int __init ixj_init(void) +#endif +{ + int result; + + int func = 0x110, i = 0; + int cnt = 0; + int probe = 0; + struct pnp_dev *dev = NULL, *old_dev = NULL; + struct pci_dev *pci = NULL; + +#ifdef CONFIG_ISAPNP + while (1) { + do { + old_dev = dev; + dev = isapnp_find_dev(NULL, ISAPNP_VENDOR('Q', 'T', 'I'), + ISAPNP_FUNCTION(func), old_dev); +//(old_dev==NULL?NULL:old_dev)); + if (!dev) + break; + printk("preparing %x\n", func); + result = dev->prepare(dev); + if (result < 0) { + printk("preparing failed %d \n", result); + break; + } + if (!(dev->resource[0].flags & IORESOURCE_IO)) + return -ENODEV; + dev->resource[0].flags |= IORESOURCE_AUTO; + if (func != 0x110) + dev->resource[1].flags |= IORESOURCE_AUTO; + if (dev->activate(dev) < 0) { + printk("isapnp configure failed (out of resources?)\n"); + return -ENOMEM; + } + ixj[cnt].DSPbase = dev->resource[0].start; /* get real port */ + if (func != 0x110) + ixj[cnt].XILINXbase = dev->resource[1].start; /* get real port */ + + result = check_region(ixj[cnt].DSPbase, 16); + if (result) { + printk(KERN_INFO "ixj: can't get I/O address 0x%x\n", ixj[cnt].DSPbase); + cleanup(); + return result; + } + request_region(ixj[cnt].DSPbase, 16, "ixj DSP"); + switch (func) { + case (0x110): + ixj[cnt].cardtype = 100; + break; + case (0x310): + ixj[cnt].cardtype = 300; + break; + case (0x410): + ixj[cnt].cardtype = 400; + break; + } + probe = ixj_selfprobe(cnt); + + ixj[cnt].serial = dev->bus->serial; + ixj[cnt].dev = dev; + printk(KERN_INFO "ixj: found card at 0x%x\n", ixj[cnt].DSPbase); + cnt++; + } while (dev); + + if (func == 0x410) + break; + if (func == 0x310) + func = 0x410; + if (func == 0x110) + func = 0x310; + dev = NULL; + } +#else //CONFIG_ISAPNP + /* Use passed parameters for older kernels without PnP */ + + for (cnt = 0; cnt < IXJMAX; cnt++) { + if (dspio[cnt]) { + ixj[cnt].DSPbase = dspio[cnt]; + ixj[cnt].XILINXbase = xio[cnt]; + ixj[cnt].cardtype = 0; + result = check_region(ixj[cnt].DSPbase, 16); + if (result) { + printk(KERN_INFO "ixj: can't get I/O address 0x%x\n", ixj[cnt].DSPbase); + cleanup(); + return result; + } + request_region(ixj[cnt].DSPbase, 16, "ixj DSP"); + probe = ixj_selfprobe(cnt); + ixj[cnt].dev = NULL; + } + } +#endif +#ifdef CONFIG_PCI + if (pci_present()) { + for (i = 0; i < IXJMAX - cnt; i++) { + pci = pci_find_device(0x15E2, 0x0500, pci); + if (!pci) + break; + { + ixj[cnt].DSPbase = pci->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; + ixj[cnt].XILINXbase = ixj[cnt].DSPbase + 0x10; + ixj[cnt].serial = PCIEE_GetSerialNumber(pci->base_address[2] & PCI_BASE_ADDRESS_IO_MASK); + + result = check_region(ixj[cnt].DSPbase, 16); + if (result) { + printk(KERN_INFO "ixj: can't get I/O address 0x%x\n", ixj[cnt].DSPbase); + cleanup(); + return result; + } + request_region(ixj[cnt].DSPbase, 16, "ixj DSP"); + ixj[cnt].cardtype = 500; + probe = ixj_selfprobe(cnt); + cnt++; + } + } + } +#endif + printk("%s\n", ixj_c_rcsid); + +// result = register_chrdev(ixj_major, "ixj", &ixj_fops); + // if (result < 0) { + // printk(KERN_INFO "ixj: can't get major number\n"); + // cleanup(); + // return result; + // } + // if (ixj_major == 0) + // ixj_major = result; /* dynamic */ + + proc_register(&proc_root, &ixj_proc_entry); + + ixj_init_timer(); + ixj_add_timer(); + return probe; +} + +static void DAA_Coeff_US(int board) +{ + IXJ *j = &ixj[board]; + + int i; + + //----------------------------------------------- + // CAO + for (i = 0; i < ALISDAA_CALLERID_SIZE; i++) { + j->m_DAAShadowRegs.CAO_REGS.CAO.CallerID[i] = 0; + } + + // Bytes for IM-filter part 1 (04): 0E,32,E2,2F,C2,5A,C0,00 + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[7] = 0x0E; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[6] = 0x32; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[5] = 0xE2; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[4] = 0x2F; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[3] = 0xC2; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[2] = 0x5A; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[1] = 0xC0; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[0] = 0x00; + +// Bytes for IM-filter part 2 (05): 72,85,00,0E,2B,3A,D0,08 + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[7] = 0x72; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[6] = 0x85; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[5] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[4] = 0x0E; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[3] = 0x2B; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[2] = 0x3A; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[1] = 0xD0; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[0] = 0x08; + +// Bytes for FRX-filter (08): 03,8F,48,F2,8F,48,70,08 + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[7] = 0x03; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[6] = 0x8F; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[5] = 0x48; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[4] = 0xF2; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[3] = 0x8F; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[2] = 0x48; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[1] = 0x70; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[0] = 0x08; + +// Bytes for FRR-filter (07): 04,8F,38,7F,9B,EA,B0,08 + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[7] = 0x04; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[6] = 0x8F; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[5] = 0x38; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[4] = 0x7F; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[3] = 0x9B; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[2] = 0xEA; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[1] = 0xB0; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[0] = 0x08; + +// Bytes for AX-filter (0A): 16,55,DD,CA + j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[3] = 0x16; + j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[2] = 0x55; + j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[1] = 0xDD; + j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[0] = 0xCA; + +// Bytes for AR-filter (09): 52,D3,11,42 + j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[3] = 0x52; + j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[2] = 0xD3; + j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[1] = 0x11; + j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[0] = 0x42; + +// Bytes for TH-filter part 1 (00): 00,42,48,81,B3,80,00,98 + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[7] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[6] = 0x42; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[5] = 0x48; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[4] = 0x81; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[3] = 0xB3; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[2] = 0x80; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[1] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[0] = 0x98; + +// Bytes for TH-filter part 2 (01): 02,F2,33,A0,68,AB,8A,AD + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[7] = 0x02; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[6] = 0xF2; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[5] = 0x33; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[4] = 0xA0; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[3] = 0x68; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[2] = 0xAB; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[1] = 0x8A; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[0] = 0xAD; + +// Bytes for TH-filter part 3 (02): 00,88,DA,54,A4,BA,2D,BB + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[7] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[6] = 0x88; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[5] = 0xDA; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[4] = 0x54; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[3] = 0xA4; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[2] = 0xBA; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[1] = 0x2D; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[0] = 0xBB; + +// ; (10K, 0.68uF) + // + // Bytes for Ringing part 1 (03):1B,3B,9B,BA,D4,1C,B3,23 + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[7] = 0x1B; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[6] = 0x3B; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[5] = 0x9B; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[4] = 0xBA; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[3] = 0xD4; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[2] = 0x1C; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[1] = 0xB3; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[0] = 0x23; + +// Bytes for Ringing part 2 (06):13,42,A6,BA,D4,73,CA,D5 + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[7] = 0x13; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[6] = 0x42; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[5] = 0xA6; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[4] = 0xBA; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[3] = 0xD4; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[2] = 0x73; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[1] = 0xCA; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[0] = 0xD5; + +// + // Levelmetering Ringing (0D):B2,45,0F,8E + j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[3] = 0xB2; + j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[2] = 0x45; + j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[1] = 0x0F; + j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[0] = 0x8E; + +// Caller ID 1st Tone (0E):CA,0E,CA,09,99,99,99,99 + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[7] = 0xCA; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[6] = 0x0E; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[5] = 0xCA; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[4] = 0x09; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[3] = 0x99; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[2] = 0x99; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[1] = 0x99; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[0] = 0x99; + +// Caller ID 2nd Tone (0F):FD,B5,BA,07,DA,00,00,00 + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[7] = 0xFD; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[6] = 0xB5; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[5] = 0xBA; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[4] = 0x07; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[3] = 0xDA; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[2] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[1] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[0] = 0x00; + +// + // ;CR Registers + // Config. Reg. 0 (filters) (cr0):FE ; CLK gen. by crystal + j->m_DAAShadowRegs.SOP_REGS.SOP.cr0.reg = 0xFE; + +// Config. Reg. 1 (dialing) (cr1):05 + j->m_DAAShadowRegs.SOP_REGS.SOP.cr1.reg = 0x05; + +// Config. Reg. 2 (caller ID) (cr2):04 + j->m_DAAShadowRegs.SOP_REGS.SOP.cr2.reg = 0x04; + +// Config. Reg. 3 (testloops) (cr3):03 ; SEL Bit==0, HP-disabled + j->m_DAAShadowRegs.SOP_REGS.SOP.cr3.reg = 0x03; + +// Config. Reg. 4 (analog gain) (cr4):01 + j->m_DAAShadowRegs.SOP_REGS.SOP.cr4.reg = 0x02; //0x01; + +// Config. Reg. 5 (Version) (cr5):02 + // Config. Reg. 6 (Reserved) (cr6):00 + // Config. Reg. 7 (Reserved) (cr7):00 + // + +// ;xr Registers + // Ext. Reg. 0 (Interrupt Reg.) (xr0):02 + j->m_DAAShadowRegs.XOP_xr0_W.reg = 0x02; // SO_1 set to '1' because it is inverted. + +// Ext. Reg. 1 (Interrupt enable) (xr1):1C // Cadence, RING, Caller ID, VDD_OK + j->m_DAAShadowRegs.XOP_REGS.XOP.xr1.reg = 0x3C; + +// Ext. Reg. 2 (Cadence Time Out) (xr2):7D + j->m_DAAShadowRegs.XOP_REGS.XOP.xr2.reg = 0x7D; + +// Ext. Reg. 3 (DC Char) (xr3):32 ; B-Filter Off == 1 + j->m_DAAShadowRegs.XOP_REGS.XOP.xr3.reg = 0x12; //0x32; + +// Ext. Reg. 4 (Cadence) (xr4):00 + j->m_DAAShadowRegs.XOP_REGS.XOP.xr4.reg = 0x00; + +// Ext. Reg. 5 (Ring timer) (xr5):22 + j->m_DAAShadowRegs.XOP_REGS.XOP.xr5.reg = 0x22; + +// Ext. Reg. 6 (Power State) (xr6):00 + j->m_DAAShadowRegs.XOP_xr6_W.reg = 0x00; + +// Ext. Reg. 7 (Vdd) (xr7):40 + j->m_DAAShadowRegs.XOP_REGS.XOP.xr7.reg = 0x40; // 0x40 ??? Should it be 0x00? + +// + // DTMF Tone 1 (0B): 11,B3,5A,2C ; 697 Hz + // 12,33,5A,C3 ; 770 Hz + // 13,3C,5B,32 ; 852 Hz + // 1D,1B,5C,CC ; 941 Hz + j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[3] = 0x11; + j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[2] = 0xB3; + j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[1] = 0x5A; + j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[0] = 0x2C; + +// DTMF Tone 2 (0C): 32,32,52,B3 ; 1209 Hz + // EC,1D,52,22 ; 1336 Hz + // AA,AC,51,D2 ; 1477 Hz + // 9B,3B,51,25 ; 1633 Hz + j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[3] = 0x32; + j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[2] = 0x32; + j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[1] = 0x52; + j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[0] = 0xB3; + +} + +static void DAA_Coeff_UK(int board) +{ + IXJ *j = &ixj[board]; + + int i; + + //----------------------------------------------- + // CAO + for (i = 0; i < ALISDAA_CALLERID_SIZE; i++) { + j->m_DAAShadowRegs.CAO_REGS.CAO.CallerID[i] = 0; + } + + // Bytes for IM-filter part 1 (04): 00,C2,BB,A8,CB,81,A0,00 + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[7] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[6] = 0xC2; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[5] = 0xBB; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[4] = 0xA8; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[3] = 0xCB; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[2] = 0x81; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[1] = 0xA0; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[0] = 0x00; + + // Bytes for IM-filter part 2 (05): 40,00,00,0A,A4,33,E0,08 + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[7] = 0x40; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[6] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[5] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[4] = 0x0A; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[3] = 0xA4; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[2] = 0x33; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[1] = 0xE0; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[0] = 0x08; + +// Bytes for FRX-filter (08): 07,9B,ED,24,B2,A2,A0,08 + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[7] = 0x07; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[6] = 0x9B; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[5] = 0xED; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[4] = 0x24; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[3] = 0xB2; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[2] = 0xA2; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[1] = 0xA0; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[0] = 0x08; + +// Bytes for FRR-filter (07): 0F,92,F2,B2,87,D2,30,08 + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[7] = 0x0F; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[6] = 0x92; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[5] = 0xF2; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[4] = 0xB2; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[3] = 0x87; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[2] = 0xD2; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[1] = 0x30; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[0] = 0x08; + +// Bytes for AX-filter (0A): 1B,A5,DD,CA + j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[3] = 0x1B; + j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[2] = 0xA5; + j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[1] = 0xDD; + j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[0] = 0xCA; + +// Bytes for AR-filter (09): E2,27,10,D6 + j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[3] = 0xE2; + j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[2] = 0x27; + j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[1] = 0x10; + j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[0] = 0xD6; + +// Bytes for TH-filter part 1 (00): 80,2D,38,8B,D0,00,00,98 + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[7] = 0x80; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[6] = 0x2D; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[5] = 0x38; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[4] = 0x8B; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[3] = 0xD0; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[2] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[1] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[0] = 0x98; + +// Bytes for TH-filter part 2 (01): 02,5A,53,F0,0B,5F,84,D4 + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[7] = 0x02; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[6] = 0x5A; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[5] = 0x53; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[4] = 0xF0; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[3] = 0x0B; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[2] = 0x5F; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[1] = 0x84; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[0] = 0xD4; + +// Bytes for TH-filter part 3 (02): 00,88,6A,A4,8F,52,F5,32 + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[7] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[6] = 0x88; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[5] = 0x6A; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[4] = 0xA4; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[3] = 0x8F; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[2] = 0x52; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[1] = 0xF5; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[0] = 0x32; + +// ; idle + +// Bytes for Ringing part 1 (03):1B,3C,93,3A,22,12,A3,23 + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[7] = 0x1B; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[6] = 0x3C; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[5] = 0x93; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[4] = 0x3A; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[3] = 0x22; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[2] = 0x12; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[1] = 0xA3; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[0] = 0x23; + +// Bytes for Ringing part 2 (06):12,A2,A6,BA,22,7A,0A,D5 + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[7] = 0x12; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[6] = 0xA2; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[5] = 0xA6; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[4] = 0xBA; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[3] = 0x22; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[2] = 0x7A; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[1] = 0x0A; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[0] = 0xD5; + +// Levelmetering Ringing (0D):AA,35,0F,8E ; 25Hz 30V less possible? + j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[3] = 0xAA; + j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[2] = 0x35; + j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[1] = 0x0F; + j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[0] = 0x8E; + +// Caller ID 1st Tone (0E):CA,0E,CA,09,99,99,99,99 + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[7] = 0xCA; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[6] = 0x0E; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[5] = 0xCA; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[4] = 0x09; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[3] = 0x99; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[2] = 0x99; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[1] = 0x99; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[0] = 0x99; + +// Caller ID 2nd Tone (0F):FD,B5,BA,07,DA,00,00,00 + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[7] = 0xFD; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[6] = 0xB5; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[5] = 0xBA; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[4] = 0x07; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[3] = 0xDA; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[2] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[1] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[0] = 0x00; + +// ;CR Registers + // Config. Reg. 0 (filters) (cr0):FF + j->m_DAAShadowRegs.SOP_REGS.SOP.cr0.reg = 0xFF; //0xFE; + +// Config. Reg. 1 (dialing) (cr1):05 + j->m_DAAShadowRegs.SOP_REGS.SOP.cr1.reg = 0x05; + +// Config. Reg. 2 (caller ID) (cr2):04 + j->m_DAAShadowRegs.SOP_REGS.SOP.cr2.reg = 0x04; + +// Config. Reg. 3 (testloops) (cr3):00 ; + j->m_DAAShadowRegs.SOP_REGS.SOP.cr3.reg = 0x00; + +// Config. Reg. 4 (analog gain) (cr4):01 + j->m_DAAShadowRegs.SOP_REGS.SOP.cr4.reg = 0x02; //0x01; + +// Config. Reg. 5 (Version) (cr5):02 + // Config. Reg. 6 (Reserved) (cr6):00 + // Config. Reg. 7 (Reserved) (cr7):00 + +// ;xr Registers + // Ext. Reg. 0 (Interrupt Reg.) (xr0):02 + j->m_DAAShadowRegs.XOP_xr0_W.reg = 0x02; // SO_1 set to '1' because it is inverted. + +// Ext. Reg. 1 (Interrupt enable) (xr1):1C + j->m_DAAShadowRegs.XOP_REGS.XOP.xr1.reg = 0x1C; // RING, Caller ID, VDD_OK + +// Ext. Reg. 2 (Cadence Time Out) (xr2):7D + j->m_DAAShadowRegs.XOP_REGS.XOP.xr2.reg = 0x7D; + +// Ext. Reg. 3 (DC Char) (xr3):36 ; + j->m_DAAShadowRegs.XOP_REGS.XOP.xr3.reg = 0x36; + +// Ext. Reg. 4 (Cadence) (xr4):00 + j->m_DAAShadowRegs.XOP_REGS.XOP.xr4.reg = 0x00; + +// Ext. Reg. 5 (Ring timer) (xr5):22 + j->m_DAAShadowRegs.XOP_REGS.XOP.xr5.reg = 0x22; + +// Ext. Reg. 6 (Power State) (xr6):00 + j->m_DAAShadowRegs.XOP_xr6_W.reg = 0x00; + +// Ext. Reg. 7 (Vdd) (xr7):46 + j->m_DAAShadowRegs.XOP_REGS.XOP.xr7.reg = 0x46; // 0x46 ??? Should it be 0x00? + +// DTMF Tone 1 (0B): 11,B3,5A,2C ; 697 Hz + // 12,33,5A,C3 ; 770 Hz + // 13,3C,5B,32 ; 852 Hz + // 1D,1B,5C,CC ; 941 Hz + j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[3] = 0x11; + j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[2] = 0xB3; + j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[1] = 0x5A; + j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[0] = 0x2C; + +// DTMF Tone 2 (0C): 32,32,52,B3 ; 1209 Hz + // EC,1D,52,22 ; 1336 Hz + // AA,AC,51,D2 ; 1477 Hz + // 9B,3B,51,25 ; 1633 Hz + j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[3] = 0x32; + j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[2] = 0x32; + j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[1] = 0x52; + j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[0] = 0xB3; + +} + + +static void DAA_Coeff_France(int board) +{ + IXJ *j = &ixj[board]; + + int i; + + //----------------------------------------------- + // CAO + for (i = 0; i < ALISDAA_CALLERID_SIZE; i++) { + j->m_DAAShadowRegs.CAO_REGS.CAO.CallerID[i] = 0; + } + + // Bytes for IM-filter part 1 (04): 02,A2,43,2C,22,AF,A0,00 + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[7] = 0x02; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[6] = 0xA2; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[5] = 0x43; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[4] = 0x2C; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[3] = 0x22; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[2] = 0xAF; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[1] = 0xA0; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[0] = 0x00; + +// Bytes for IM-filter part 2 (05): 67,CE,00,0C,22,33,E0,08 + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[7] = 0x67; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[6] = 0xCE; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[5] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[4] = 0x2C; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[3] = 0x22; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[2] = 0x33; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[1] = 0xE0; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[0] = 0x08; + +// Bytes for FRX-filter (08): 07,9A,28,F6,23,4A,B0,08 + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[7] = 0x07; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[6] = 0x9A; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[5] = 0x28; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[4] = 0xF6; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[3] = 0x23; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[2] = 0x4A; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[1] = 0xB0; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[0] = 0x08; + +// Bytes for FRR-filter (07): 03,8F,F9,2F,9E,FA,20,08 + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[7] = 0x03; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[6] = 0x8F; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[5] = 0xF9; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[4] = 0x2F; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[3] = 0x9E; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[2] = 0xFA; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[1] = 0x20; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[0] = 0x08; + +// Bytes for AX-filter (0A): 16,B5,DD,CA + j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[3] = 0x16; + j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[2] = 0xB5; + j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[1] = 0xDD; + j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[0] = 0xCA; + +// Bytes for AR-filter (09): 52,C7,10,D6 + j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[3] = 0xE2; + j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[2] = 0xC7; + j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[1] = 0x10; + j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[0] = 0xD6; + +// Bytes for TH-filter part 1 (00): 00,42,48,81,A6,80,00,98 + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[7] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[6] = 0x42; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[5] = 0x48; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[4] = 0x81; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[3] = 0xA6; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[2] = 0x80; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[1] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[0] = 0x98; + +// Bytes for TH-filter part 2 (01): 02,AC,2A,30,78,AC,8A,2C + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[7] = 0x02; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[6] = 0xAC; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[5] = 0x2A; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[4] = 0x30; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[3] = 0x78; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[2] = 0xAC; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[1] = 0x8A; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[0] = 0x2C; + +// Bytes for TH-filter part 3 (02): 00,88,DA,A5,22,BA,2C,45 + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[7] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[6] = 0x88; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[5] = 0xDA; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[4] = 0xA5; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[3] = 0x22; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[2] = 0xBA; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[1] = 0x2C; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[0] = 0x45; + +// ; idle + +// Bytes for Ringing part 1 (03):1B,3C,93,3A,22,12,A3,23 + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[7] = 0x1B; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[6] = 0x3C; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[5] = 0x93; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[4] = 0x3A; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[3] = 0x22; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[2] = 0x12; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[1] = 0xA3; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[0] = 0x23; + +// Bytes for Ringing part 2 (06):12,A2,A6,BA,22,7A,0A,D5 + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[7] = 0x12; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[6] = 0xA2; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[5] = 0xA6; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[4] = 0xBA; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[3] = 0x22; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[2] = 0x7A; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[1] = 0x0A; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[0] = 0xD5; + +// Levelmetering Ringing (0D):32,45,B5,84 ; 50Hz 20V + j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[3] = 0x32; + j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[2] = 0x45; + j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[1] = 0xB5; + j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[0] = 0x84; + +// Caller ID 1st Tone (0E):CA,0E,CA,09,99,99,99,99 + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[7] = 0xCA; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[6] = 0x0E; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[5] = 0xCA; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[4] = 0x09; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[3] = 0x99; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[2] = 0x99; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[1] = 0x99; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[0] = 0x99; + +// Caller ID 2nd Tone (0F):FD,B5,BA,07,DA,00,00,00 + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[7] = 0xFD; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[6] = 0xB5; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[5] = 0xBA; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[4] = 0x07; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[3] = 0xDA; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[2] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[1] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[0] = 0x00; + +// ;CR Registers + // Config. Reg. 0 (filters) (cr0):FF + j->m_DAAShadowRegs.SOP_REGS.SOP.cr0.reg = 0xFF; + +// Config. Reg. 1 (dialing) (cr1):05 + j->m_DAAShadowRegs.SOP_REGS.SOP.cr1.reg = 0x05; + +// Config. Reg. 2 (caller ID) (cr2):04 + j->m_DAAShadowRegs.SOP_REGS.SOP.cr2.reg = 0x04; + +// Config. Reg. 3 (testloops) (cr3):00 ; + j->m_DAAShadowRegs.SOP_REGS.SOP.cr3.reg = 0x00; + +// Config. Reg. 4 (analog gain) (cr4):01 + j->m_DAAShadowRegs.SOP_REGS.SOP.cr4.reg = 0x02; //0x01; + +// Config. Reg. 5 (Version) (cr5):02 + // Config. Reg. 6 (Reserved) (cr6):00 + // Config. Reg. 7 (Reserved) (cr7):00 + +// ;xr Registers + // Ext. Reg. 0 (Interrupt Reg.) (xr0):02 + j->m_DAAShadowRegs.XOP_xr0_W.reg = 0x02; // SO_1 set to '1' because it is inverted. + +// Ext. Reg. 1 (Interrupt enable) (xr1):1C + j->m_DAAShadowRegs.XOP_REGS.XOP.xr1.reg = 0x1C; // RING, Caller ID, VDD_OK + +// Ext. Reg. 2 (Cadence Time Out) (xr2):7D + j->m_DAAShadowRegs.XOP_REGS.XOP.xr2.reg = 0x7D; + +// Ext. Reg. 3 (DC Char) (xr3):36 ; + j->m_DAAShadowRegs.XOP_REGS.XOP.xr3.reg = 0x36; + +// Ext. Reg. 4 (Cadence) (xr4):00 + j->m_DAAShadowRegs.XOP_REGS.XOP.xr4.reg = 0x00; + +// Ext. Reg. 5 (Ring timer) (xr5):22 + j->m_DAAShadowRegs.XOP_REGS.XOP.xr5.reg = 0x22; + +// Ext. Reg. 6 (Power State) (xr6):00 + j->m_DAAShadowRegs.XOP_xr6_W.reg = 0x00; + +// Ext. Reg. 7 (Vdd) (xr7):46 + j->m_DAAShadowRegs.XOP_REGS.XOP.xr7.reg = 0x46; // 0x46 ??? Should it be 0x00? + +// DTMF Tone 1 (0B): 11,B3,5A,2C ; 697 Hz + // 12,33,5A,C3 ; 770 Hz + // 13,3C,5B,32 ; 852 Hz + // 1D,1B,5C,CC ; 941 Hz + j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[3] = 0x11; + j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[2] = 0xB3; + j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[1] = 0x5A; + j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[0] = 0x2C; + +// DTMF Tone 2 (0C): 32,32,52,B3 ; 1209 Hz + // EC,1D,52,22 ; 1336 Hz + // AA,AC,51,D2 ; 1477 Hz + // 9B,3B,51,25 ; 1633 Hz + j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[3] = 0x32; + j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[2] = 0x32; + j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[1] = 0x52; + j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[0] = 0xB3; + +} + + +static void DAA_Coeff_Germany(int board) +{ + IXJ *j = &ixj[board]; + + int i; + + //----------------------------------------------- + // CAO + for (i = 0; i < ALISDAA_CALLERID_SIZE; i++) { + j->m_DAAShadowRegs.CAO_REGS.CAO.CallerID[i] = 0; + } + + // Bytes for IM-filter part 1 (04): 00,CE,BB,B8,D2,81,B0,00 + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[7] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[6] = 0xCE; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[5] = 0xBB; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[4] = 0xB8; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[3] = 0xD2; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[2] = 0x81; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[1] = 0xB0; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[0] = 0x00; + +// Bytes for IM-filter part 2 (05): 45,8F,00,0C,D2,3A,D0,08 + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[7] = 0x45; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[6] = 0x8F; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[5] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[4] = 0x0C; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[3] = 0xD2; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[2] = 0x3A; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[1] = 0xD0; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[0] = 0x08; + +// Bytes for FRX-filter (08): 07,AA,E2,34,24,89,20,08 + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[7] = 0x07; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[6] = 0xAA; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[5] = 0xE2; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[4] = 0x34; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[3] = 0x24; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[2] = 0x89; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[1] = 0x20; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[0] = 0x08; + +// Bytes for FRR-filter (07): 02,87,FA,37,9A,CA,B0,08 + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[7] = 0x02; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[6] = 0x87; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[5] = 0xFA; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[4] = 0x37; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[3] = 0x9A; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[2] = 0xCA; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[1] = 0xB0; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[0] = 0x08; + +// Bytes for AX-filter (0A): 72,D5,DD,CA + j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[3] = 0x72; + j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[2] = 0xD5; + j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[1] = 0xDD; + j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[0] = 0xCA; + +// Bytes for AR-filter (09): 72,42,13,4B + j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[3] = 0x72; + j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[2] = 0x42; + j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[1] = 0x13; + j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[0] = 0x4B; + +// Bytes for TH-filter part 1 (00): 80,52,48,81,AD,80,00,98 + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[7] = 0x80; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[6] = 0x52; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[5] = 0x48; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[4] = 0x81; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[3] = 0xAD; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[2] = 0x80; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[1] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[0] = 0x98; + +// Bytes for TH-filter part 2 (01): 02,42,5A,20,E8,1A,81,27 + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[7] = 0x02; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[6] = 0x42; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[5] = 0x5A; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[4] = 0x20; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[3] = 0xE8; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[2] = 0x1A; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[1] = 0x81; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[0] = 0x27; + +// Bytes for TH-filter part 3 (02): 00,88,63,26,BD,4B,A3,C2 + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[7] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[6] = 0x88; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[5] = 0x63; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[4] = 0x26; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[3] = 0xBD; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[2] = 0x4B; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[1] = 0xA3; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[0] = 0xC2; + +// ; (10K, 0.68uF) + +// Bytes for Ringing part 1 (03):1B,3B,9B,BA,D4,1C,B3,23 + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[7] = 0x1B; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[6] = 0x3B; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[5] = 0x9B; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[4] = 0xBA; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[3] = 0xD4; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[2] = 0x1C; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[1] = 0xB3; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[0] = 0x23; + +// Bytes for Ringing part 2 (06):13,42,A6,BA,D4,73,CA,D5 + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[7] = 0x13; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[6] = 0x42; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[5] = 0xA6; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[4] = 0xBA; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[3] = 0xD4; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[2] = 0x73; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[1] = 0xCA; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[0] = 0xD5; + +// Levelmetering Ringing (0D):B2,45,0F,8E + j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[3] = 0xB2; + j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[2] = 0x45; + j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[1] = 0x0F; + j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[0] = 0x8E; + +// Caller ID 1st Tone (0E):CA,0E,CA,09,99,99,99,99 + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[7] = 0xCA; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[6] = 0x0E; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[5] = 0xCA; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[4] = 0x09; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[3] = 0x99; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[2] = 0x99; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[1] = 0x99; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[0] = 0x99; + +// Caller ID 2nd Tone (0F):FD,B5,BA,07,DA,00,00,00 + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[7] = 0xFD; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[6] = 0xB5; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[5] = 0xBA; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[4] = 0x07; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[3] = 0xDA; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[2] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[1] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[0] = 0x00; + +// ;CR Registers + // Config. Reg. 0 (filters) (cr0):FF ; all Filters enabled, CLK from ext. source + j->m_DAAShadowRegs.SOP_REGS.SOP.cr0.reg = 0xFF; + +// Config. Reg. 1 (dialing) (cr1):05 ; Manual Ring, Ring metering enabled + j->m_DAAShadowRegs.SOP_REGS.SOP.cr1.reg = 0x05; + +// Config. Reg. 2 (caller ID) (cr2):04 ; Analog Gain 0dB, FSC internal + j->m_DAAShadowRegs.SOP_REGS.SOP.cr2.reg = 0x04; + +// Config. Reg. 3 (testloops) (cr3):00 ; SEL Bit==0, HP-enabled + j->m_DAAShadowRegs.SOP_REGS.SOP.cr3.reg = 0x00; + +// Config. Reg. 4 (analog gain) (cr4):01 + j->m_DAAShadowRegs.SOP_REGS.SOP.cr4.reg = 0x02; //0x01; + +// Config. Reg. 5 (Version) (cr5):02 + // Config. Reg. 6 (Reserved) (cr6):00 + // Config. Reg. 7 (Reserved) (cr7):00 + +// ;xr Registers + // Ext. Reg. 0 (Interrupt Reg.) (xr0):02 + j->m_DAAShadowRegs.XOP_xr0_W.reg = 0x02; // SO_1 set to '1' because it is inverted. + +// Ext. Reg. 1 (Interrupt enable) (xr1):1C ; Ring, CID, VDDOK Interrupts enabled + j->m_DAAShadowRegs.XOP_REGS.XOP.xr1.reg = 0x1C; // RING, Caller ID, VDD_OK + +// Ext. Reg. 2 (Cadence Time Out) (xr2):7D + j->m_DAAShadowRegs.XOP_REGS.XOP.xr2.reg = 0x7D; + +// Ext. Reg. 3 (DC Char) (xr3):32 ; B-Filter Off==1, U0=3.5V, R=200Ohm + j->m_DAAShadowRegs.XOP_REGS.XOP.xr3.reg = 0x32; + +// Ext. Reg. 4 (Cadence) (xr4):00 + j->m_DAAShadowRegs.XOP_REGS.XOP.xr4.reg = 0x00; + +// Ext. Reg. 5 (Ring timer) (xr5):22 + j->m_DAAShadowRegs.XOP_REGS.XOP.xr5.reg = 0x22; + +// Ext. Reg. 6 (Power State) (xr6):00 + j->m_DAAShadowRegs.XOP_xr6_W.reg = 0x00; + +// Ext. Reg. 7 (Vdd) (xr7):40 ; VDD=4.25 V + j->m_DAAShadowRegs.XOP_REGS.XOP.xr7.reg = 0x40; // 0x40 ??? Should it be 0x00? + +// DTMF Tone 1 (0B): 11,B3,5A,2C ; 697 Hz + // 12,33,5A,C3 ; 770 Hz + // 13,3C,5B,32 ; 852 Hz + // 1D,1B,5C,CC ; 941 Hz + j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[3] = 0x11; + j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[2] = 0xB3; + j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[1] = 0x5A; + j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[0] = 0x2C; + +// DTMF Tone 2 (0C): 32,32,52,B3 ; 1209 Hz + // EC,1D,52,22 ; 1336 Hz + // AA,AC,51,D2 ; 1477 Hz + // 9B,3B,51,25 ; 1633 Hz + j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[3] = 0x32; + j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[2] = 0x32; + j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[1] = 0x52; + j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[0] = 0xB3; + +} + + +static void DAA_Coeff_Australia(int board) +{ + IXJ *j = &ixj[board]; + + int i; + + //----------------------------------------------- + // CAO + for (i = 0; i < ALISDAA_CALLERID_SIZE; i++) { + j->m_DAAShadowRegs.CAO_REGS.CAO.CallerID[i] = 0; + } + + // Bytes for IM-filter part 1 (04): 00,A3,AA,28,B3,82,D0,00 + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[7] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[6] = 0xA3; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[5] = 0xAA; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[4] = 0x28; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[3] = 0xB3; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[2] = 0x82; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[1] = 0xD0; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[0] = 0x00; + +// Bytes for IM-filter part 2 (05): 70,96,00,09,32,6B,C0,08 + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[7] = 0x70; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[6] = 0x96; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[5] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[4] = 0x09; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[3] = 0x32; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[2] = 0x6B; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[1] = 0xC0; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[0] = 0x08; + +// Bytes for FRX-filter (08): 07,96,E2,34,32,9B,30,08 + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[7] = 0x07; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[6] = 0x96; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[5] = 0xE2; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[4] = 0x34; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[3] = 0x32; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[2] = 0x9B; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[1] = 0x30; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[0] = 0x08; + +// Bytes for FRR-filter (07): 0F,9A,E9,2F,22,CC,A0,08 + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[7] = 0x0F; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[6] = 0x9A; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[5] = 0xE9; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[4] = 0x2F; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[3] = 0x22; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[2] = 0xCC; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[1] = 0xA0; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[0] = 0x08; + +// Bytes for AX-filter (0A): CB,45,DD,CA + j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[3] = 0xCB; + j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[2] = 0x45; + j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[1] = 0xDD; + j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[0] = 0xCA; + +// Bytes for AR-filter (09): 1B,67,10,D6 + j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[3] = 0x1B; + j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[2] = 0x67; + j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[1] = 0x10; + j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[0] = 0xD6; + +// Bytes for TH-filter part 1 (00): 80,52,48,81,AF,80,00,98 + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[7] = 0x80; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[6] = 0x52; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[5] = 0x48; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[4] = 0x81; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[3] = 0xAF; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[2] = 0x80; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[1] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[0] = 0x98; + +// Bytes for TH-filter part 2 (01): 02,DB,52,B0,38,01,82,AC + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[7] = 0x02; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[6] = 0xDB; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[5] = 0x52; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[4] = 0xB0; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[3] = 0x38; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[2] = 0x01; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[1] = 0x82; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[0] = 0xAC; + +// Bytes for TH-filter part 3 (02): 00,88,4A,3E,2C,3B,24,46 + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[7] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[6] = 0x88; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[5] = 0x4A; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[4] = 0x3E; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[3] = 0x2C; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[2] = 0x3B; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[1] = 0x24; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[0] = 0x46; + +// ; idle + +// Bytes for Ringing part 1 (03):1B,3C,93,3A,22,12,A3,23 + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[7] = 0x1B; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[6] = 0x3C; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[5] = 0x93; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[4] = 0x3A; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[3] = 0x22; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[2] = 0x12; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[1] = 0xA3; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[0] = 0x23; + +// Bytes for Ringing part 2 (06):12,A2,A6,BA,22,7A,0A,D5 + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[7] = 0x12; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[6] = 0xA2; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[5] = 0xA6; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[4] = 0xBA; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[3] = 0x22; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[2] = 0x7A; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[1] = 0x0A; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[0] = 0xD5; + +// Levelmetering Ringing (0D):32,45,B5,84 ; 50Hz 20V + j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[3] = 0x32; + j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[2] = 0x45; + j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[1] = 0xB5; + j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[0] = 0x84; + +// Caller ID 1st Tone (0E):CA,0E,CA,09,99,99,99,99 + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[7] = 0xCA; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[6] = 0x0E; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[5] = 0xCA; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[4] = 0x09; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[3] = 0x99; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[2] = 0x99; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[1] = 0x99; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[0] = 0x99; + +// Caller ID 2nd Tone (0F):FD,B5,BA,07,DA,00,00,00 + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[7] = 0xFD; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[6] = 0xB5; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[5] = 0xBA; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[4] = 0x07; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[3] = 0xDA; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[2] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[1] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[0] = 0x00; + +// ;CR Registers + // Config. Reg. 0 (filters) (cr0):FF + j->m_DAAShadowRegs.SOP_REGS.SOP.cr0.reg = 0xFF; + +// Config. Reg. 1 (dialing) (cr1):05 + j->m_DAAShadowRegs.SOP_REGS.SOP.cr1.reg = 0x05; + +// Config. Reg. 2 (caller ID) (cr2):04 + j->m_DAAShadowRegs.SOP_REGS.SOP.cr2.reg = 0x04; + +// Config. Reg. 3 (testloops) (cr3):00 ; + j->m_DAAShadowRegs.SOP_REGS.SOP.cr3.reg = 0x00; + +// Config. Reg. 4 (analog gain) (cr4):01 + j->m_DAAShadowRegs.SOP_REGS.SOP.cr4.reg = 0x02; //0x01; + +// Config. Reg. 5 (Version) (cr5):02 + // Config. Reg. 6 (Reserved) (cr6):00 + // Config. Reg. 7 (Reserved) (cr7):00 + +// ;xr Registers + // Ext. Reg. 0 (Interrupt Reg.) (xr0):02 + j->m_DAAShadowRegs.XOP_xr0_W.reg = 0x02; // SO_1 set to '1' because it is inverted. + +// Ext. Reg. 1 (Interrupt enable) (xr1):1C + j->m_DAAShadowRegs.XOP_REGS.XOP.xr1.reg = 0x1C; // RING, Caller ID, VDD_OK + +// Ext. Reg. 2 (Cadence Time Out) (xr2):7D + j->m_DAAShadowRegs.XOP_REGS.XOP.xr2.reg = 0x7D; + +// Ext. Reg. 3 (DC Char) (xr3):2B ; + j->m_DAAShadowRegs.XOP_REGS.XOP.xr3.reg = 0x2B; + +// Ext. Reg. 4 (Cadence) (xr4):00 + j->m_DAAShadowRegs.XOP_REGS.XOP.xr4.reg = 0x00; + +// Ext. Reg. 5 (Ring timer) (xr5):22 + j->m_DAAShadowRegs.XOP_REGS.XOP.xr5.reg = 0x22; + +// Ext. Reg. 6 (Power State) (xr6):00 + j->m_DAAShadowRegs.XOP_xr6_W.reg = 0x00; + +// Ext. Reg. 7 (Vdd) (xr7):40 + j->m_DAAShadowRegs.XOP_REGS.XOP.xr7.reg = 0x40; // 0x40 ??? Should it be 0x00? + +// DTMF Tone 1 (0B): 11,B3,5A,2C ; 697 Hz + // 12,33,5A,C3 ; 770 Hz + // 13,3C,5B,32 ; 852 Hz + // 1D,1B,5C,CC ; 941 Hz + j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[3] = 0x11; + j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[2] = 0xB3; + j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[1] = 0x5A; + j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[0] = 0x2C; + +// DTMF Tone 2 (0C): 32,32,52,B3 ; 1209 Hz + // EC,1D,52,22 ; 1336 Hz + // AA,AC,51,D2 ; 1477 Hz + // 9B,3B,51,25 ; 1633 Hz + j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[3] = 0x32; + j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[2] = 0x32; + j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[1] = 0x52; + j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[0] = 0xB3; + +} + +static void DAA_Coeff_Japan(int board) +{ + IXJ *j = &ixj[board]; + + int i; + + //----------------------------------------------- + // CAO + for (i = 0; i < ALISDAA_CALLERID_SIZE; i++) { + j->m_DAAShadowRegs.CAO_REGS.CAO.CallerID[i] = 0; + } + + // Bytes for IM-filter part 1 (04): 06,BD,E2,2D,BA,F9,A0,00 + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[7] = 0x06; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[6] = 0xBD; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[5] = 0xE2; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[4] = 0x2D; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[3] = 0xBA; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[2] = 0xF9; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[1] = 0xA0; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_1[0] = 0x00; + +// Bytes for IM-filter part 2 (05): 6F,F7,00,0E,34,33,E0,08 + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[7] = 0x6F; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[6] = 0xF7; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[5] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[4] = 0x0E; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[3] = 0x34; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[2] = 0x33; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[1] = 0xE0; + j->m_DAAShadowRegs.COP_REGS.COP.IMFilterCoeff_2[0] = 0x08; + +// Bytes for FRX-filter (08): 02,8F,68,77,9C,58,F0,08 + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[7] = 0x02; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[6] = 0x8F; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[5] = 0x68; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[4] = 0x77; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[3] = 0x9C; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[2] = 0x58; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[1] = 0xF0; + j->m_DAAShadowRegs.COP_REGS.COP.FRXFilterCoeff[0] = 0x08; + +// Bytes for FRR-filter (07): 03,8F,38,73,87,EA,20,08 + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[7] = 0x03; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[6] = 0x8F; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[5] = 0x38; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[4] = 0x73; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[3] = 0x87; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[2] = 0xEA; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[1] = 0x20; + j->m_DAAShadowRegs.COP_REGS.COP.FRRFilterCoeff[0] = 0x08; + +// Bytes for AX-filter (0A): 51,C5,DD,CA + j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[3] = 0x51; + j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[2] = 0xC5; + j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[1] = 0xDD; + j->m_DAAShadowRegs.COP_REGS.COP.AXFilterCoeff[0] = 0xCA; + +// Bytes for AR-filter (09): 25,A7,10,D6 + j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[3] = 0x25; + j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[2] = 0xA7; + j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[1] = 0x10; + j->m_DAAShadowRegs.COP_REGS.COP.ARFilterCoeff[0] = 0xD6; + +// Bytes for TH-filter part 1 (00): 00,42,48,81,AE,80,00,98 + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[7] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[6] = 0x42; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[5] = 0x48; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[4] = 0x81; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[3] = 0xAE; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[2] = 0x80; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[1] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_1[0] = 0x98; + +// Bytes for TH-filter part 2 (01): 02,AB,2A,20,99,5B,89,28 + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[7] = 0x02; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[6] = 0xAB; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[5] = 0x2A; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[4] = 0x20; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[3] = 0x99; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[2] = 0x5B; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[1] = 0x89; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_2[0] = 0x28; + +// Bytes for TH-filter part 3 (02): 00,88,DA,25,34,C5,4C,BA + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[7] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[6] = 0x88; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[5] = 0xDA; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[4] = 0x25; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[3] = 0x34; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[2] = 0xC5; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[1] = 0x4C; + j->m_DAAShadowRegs.COP_REGS.COP.THFilterCoeff_3[0] = 0xBA; + +// ; idle + +// Bytes for Ringing part 1 (03):1B,3C,93,3A,22,12,A3,23 + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[7] = 0x1B; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[6] = 0x3C; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[5] = 0x93; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[4] = 0x3A; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[3] = 0x22; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[2] = 0x12; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[1] = 0xA3; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_1[0] = 0x23; + +// Bytes for Ringing part 2 (06):12,A2,A6,BA,22,7A,0A,D5 + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[7] = 0x12; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[6] = 0xA2; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[5] = 0xA6; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[4] = 0xBA; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[3] = 0x22; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[2] = 0x7A; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[1] = 0x0A; + j->m_DAAShadowRegs.COP_REGS.COP.RingerImpendance_2[0] = 0xD5; + +// Levelmetering Ringing (0D):AA,35,0F,8E ; 25Hz 30V ????????? + j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[3] = 0xAA; + j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[2] = 0x35; + j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[1] = 0x0F; + j->m_DAAShadowRegs.COP_REGS.COP.LevelmeteringRinging[0] = 0x8E; + +// Caller ID 1st Tone (0E):CA,0E,CA,09,99,99,99,99 + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[7] = 0xCA; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[6] = 0x0E; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[5] = 0xCA; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[4] = 0x09; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[3] = 0x99; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[2] = 0x99; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[1] = 0x99; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID1stTone[0] = 0x99; + +// Caller ID 2nd Tone (0F):FD,B5,BA,07,DA,00,00,00 + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[7] = 0xFD; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[6] = 0xB5; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[5] = 0xBA; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[4] = 0x07; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[3] = 0xDA; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[2] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[1] = 0x00; + j->m_DAAShadowRegs.COP_REGS.COP.CallerID2ndTone[0] = 0x00; + +// ;CR Registers + // Config. Reg. 0 (filters) (cr0):FF + j->m_DAAShadowRegs.SOP_REGS.SOP.cr0.reg = 0xFF; + +// Config. Reg. 1 (dialing) (cr1):05 + j->m_DAAShadowRegs.SOP_REGS.SOP.cr1.reg = 0x05; + +// Config. Reg. 2 (caller ID) (cr2):04 + j->m_DAAShadowRegs.SOP_REGS.SOP.cr2.reg = 0x04; + +// Config. Reg. 3 (testloops) (cr3):00 ; + j->m_DAAShadowRegs.SOP_REGS.SOP.cr3.reg = 0x00; + +// Config. Reg. 4 (analog gain) (cr4):01 + j->m_DAAShadowRegs.SOP_REGS.SOP.cr4.reg = 0x02; //0x01; + +// Config. Reg. 5 (Version) (cr5):02 + // Config. Reg. 6 (Reserved) (cr6):00 + // Config. Reg. 7 (Reserved) (cr7):00 + +// ;xr Registers + // Ext. Reg. 0 (Interrupt Reg.) (xr0):02 + j->m_DAAShadowRegs.XOP_xr0_W.reg = 0x02; // SO_1 set to '1' because it is inverted. + +// Ext. Reg. 1 (Interrupt enable) (xr1):1C + j->m_DAAShadowRegs.XOP_REGS.XOP.xr1.reg = 0x1C; // RING, Caller ID, VDD_OK + +// Ext. Reg. 2 (Cadence Time Out) (xr2):7D + j->m_DAAShadowRegs.XOP_REGS.XOP.xr2.reg = 0x7D; + +// Ext. Reg. 3 (DC Char) (xr3):22 ; + j->m_DAAShadowRegs.XOP_REGS.XOP.xr3.reg = 0x22; + +// Ext. Reg. 4 (Cadence) (xr4):00 + j->m_DAAShadowRegs.XOP_REGS.XOP.xr4.reg = 0x00; + +// Ext. Reg. 5 (Ring timer) (xr5):22 + j->m_DAAShadowRegs.XOP_REGS.XOP.xr5.reg = 0x22; + +// Ext. Reg. 6 (Power State) (xr6):00 + j->m_DAAShadowRegs.XOP_xr6_W.reg = 0x00; + +// Ext. Reg. 7 (Vdd) (xr7):40 + j->m_DAAShadowRegs.XOP_REGS.XOP.xr7.reg = 0x40; // 0x40 ??? Should it be 0x00? + +// DTMF Tone 1 (0B): 11,B3,5A,2C ; 697 Hz + // 12,33,5A,C3 ; 770 Hz + // 13,3C,5B,32 ; 852 Hz + // 1D,1B,5C,CC ; 941 Hz + j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[3] = 0x11; + j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[2] = 0xB3; + j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[1] = 0x5A; + j->m_DAAShadowRegs.COP_REGS.COP.Tone1Coeff[0] = 0x2C; + +// DTMF Tone 2 (0C): 32,32,52,B3 ; 1209 Hz + // EC,1D,52,22 ; 1336 Hz + // AA,AC,51,D2 ; 1477 Hz + // 9B,3B,51,25 ; 1633 Hz + + j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[3] = 0x32; + j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[2] = 0x32; + j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[1] = 0x52; + j->m_DAAShadowRegs.COP_REGS.COP.Tone2Coeff[0] = 0xB3; + +} + +static s16 tone_table[][19] = +{ + { // f20_50[] + 32538, // A1 = 1.985962 + -32325, // A2 = -0.986511 + -343, // B2 = -0.010493 + 0, // B1 = 0 + 343, // B0 = 0.010493 + 32619, // A1 = 1.990906 + -32520, // A2 = -0.992462 + 19179, // B2 = 0.585327 + -19178, // B1 = -1.170593 + 19179, // B0 = 0.585327 + 32723, // A1 = 1.997314 + -32686, // A2 = -0.997528 + 9973, // B2 = 0.304352 + -9955, // B1 = -0.607605 + 9973, // B0 = 0.304352 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f133_200[] + 32072, // A1 = 1.95752 + -31896, // A2 = -0.973419 + -435, // B2 = -0.013294 + 0, // B1 = 0 + 435, // B0 = 0.013294 + 32188, // A1 = 1.9646 + -32400, // A2 = -0.98877 + 15139, // B2 = 0.462036 + -14882, // B1 = -0.908356 + 15139, // B0 = 0.462036 + 32473, // A1 = 1.981995 + -32524, // A2 = -0.992584 + 23200, // B2 = 0.708008 + -23113, // B1 = -1.410706 + 23200, // B0 = 0.708008 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 300.txt + 31769, // A1 = -1.939026 + -32584, // A2 = 0.994385 + -475, // B2 = -0.014522 + 0, // B1 = 0.000000 + 475, // B0 = 0.014522 + 31789, // A1 = -1.940247 + -32679, // A2 = 0.997284 + 17280, // B2 = 0.527344 + -16865, // B1 = -1.029358 + 17280, // B0 = 0.527344 + 31841, // A1 = -1.943481 + -32681, // A2 = 0.997345 + 543, // B2 = 0.016579 + -525, // B1 = -0.032097 + 543, // B0 = 0.016579 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f300_420[] + 30750, // A1 = 1.876892 + -31212, // A2 = -0.952515 + -804, // B2 = -0.024541 + 0, // B1 = 0 + 804, // B0 = 0.024541 + 30686, // A1 = 1.872925 + -32145, // A2 = -0.980988 + 14747, // B2 = 0.450043 + -13703, // B1 = -0.836395 + 14747, // B0 = 0.450043 + 31651, // A1 = 1.931824 + -32321, // A2 = -0.986389 + 24425, // B2 = 0.745422 + -23914, // B1 = -1.459595 + 24427, // B0 = 0.745483 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 330.txt + 31613, // A1 = -1.929565 + -32646, // A2 = 0.996277 + -185, // B2 = -0.005657 + 0, // B1 = 0.000000 + 185, // B0 = 0.005657 + 31620, // A1 = -1.929932 + -32713, // A2 = 0.998352 + 19253, // B2 = 0.587585 + -18566, // B1 = -1.133179 + 19253, // B0 = 0.587585 + 31674, // A1 = -1.933228 + -32715, // A2 = 0.998413 + 2575, // B2 = 0.078590 + -2495, // B1 = -0.152283 + 2575, // B0 = 0.078590 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f300_425[] + 30741, // A1 = 1.876282 + -31475, // A2 = -0.960541 + -703, // B2 = -0.021484 + 0, // B1 = 0 + 703, // B0 = 0.021484 + 30688, // A1 = 1.873047 + -32248, // A2 = -0.984161 + 14542, // B2 = 0.443787 + -13523, // B1 = -0.825439 + 14542, // B0 = 0.443817 + 31494, // A1 = 1.922302 + -32366, // A2 = -0.987762 + 21577, // B2 = 0.658508 + -21013, // B1 = -1.282532 + 21577, // B0 = 0.658508 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f330_440[] + 30627, // A1 = 1.869324 + -31338, // A2 = -0.95636 + -843, // B2 = -0.025749 + 0, // B1 = 0 + 843, // B0 = 0.025749 + 30550, // A1 = 1.864685 + -32221, // A2 = -0.983337 + 13594, // B2 = 0.414886 + -12589, // B1 = -0.768402 + 13594, // B0 = 0.414886 + 31488, // A1 = 1.921936 + -32358, // A2 = -0.987518 + 24684, // B2 = 0.753296 + -24029, // B1 = -1.466614 + 24684, // B0 = 0.753296 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 340.txt + 31546, // A1 = -1.925476 + -32646, // A2 = 0.996277 + -445, // B2 = -0.013588 + 0, // B1 = 0.000000 + 445, // B0 = 0.013588 + 31551, // A1 = -1.925781 + -32713, // A2 = 0.998352 + 23884, // B2 = 0.728882 + -22979, // B1 = -1.402527 + 23884, // B0 = 0.728882 + 31606, // A1 = -1.929138 + -32715, // A2 = 0.998413 + 863, // B2 = 0.026367 + -835, // B1 = -0.050985 + 863, // B0 = 0.026367 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f350_400[] + 31006, // A1 = 1.892517 + -32029, // A2 = -0.977448 + -461, // B2 = -0.014096 + 0, // B1 = 0 + 461, // B0 = 0.014096 + 30999, // A1 = 1.892029 + -32487, // A2 = -0.991455 + 11325, // B2 = 0.345612 + -10682, // B1 = -0.651978 + 11325, // B0 = 0.345612 + 31441, // A1 = 1.919067 + -32526, // A2 = -0.992615 + 24324, // B2 = 0.74231 + -23535, // B1 = -1.436523 + 24324, // B0 = 0.74231 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f350_440[] + 30634, // A1 = 1.869751 + -31533, // A2 = -0.962341 + -680, // B2 = -0.020782 + 0, // B1 = 0 + 680, // B0 = 0.020782 + 30571, // A1 = 1.865906 + -32277, // A2 = -0.985016 + 12894, // B2 = 0.393524 + -11945, // B1 = -0.729065 + 12894, // B0 = 0.393524 + 31367, // A1 = 1.91449 + -32379, // A2 = -0.988129 + 23820, // B2 = 0.726929 + -23104, // B1 = -1.410217 + 23820, // B0 = 0.726929 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f350_450[] + 30552, // A1 = 1.864807 + -31434, // A2 = -0.95929 + -690, // B2 = -0.021066 + 0, // B1 = 0 + 690, // B0 = 0.021066 + 30472, // A1 = 1.859924 + -32248, // A2 = -0.984161 + 13385, // B2 = 0.408478 + -12357, // B1 = -0.754242 + 13385, // B0 = 0.408478 + 31358, // A1 = 1.914001 + -32366, // A2 = -0.987732 + 26488, // B2 = 0.80835 + -25692, // B1 = -1.568176 + 26490, // B0 = 0.808411 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 360.txt + 31397, // A1 = -1.916321 + -32623, // A2 = 0.995605 + -117, // B2 = -0.003598 + 0, // B1 = 0.000000 + 117, // B0 = 0.003598 + 31403, // A1 = -1.916687 + -32700, // A2 = 0.997925 + 3388, // B2 = 0.103401 + -3240, // B1 = -0.197784 + 3388, // B0 = 0.103401 + 31463, // A1 = -1.920410 + -32702, // A2 = 0.997986 + 13346, // B2 = 0.407288 + -12863, // B1 = -0.785126 + 13346, // B0 = 0.407288 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f380_420[] + 30831, // A1 = 1.881775 + -32064, // A2 = -0.978546 + -367, // B2 = -0.01122 + 0, // B1 = 0 + 367, // B0 = 0.01122 + 30813, // A1 = 1.880737 + -32456, // A2 = -0.990509 + 11068, // B2 = 0.337769 + -10338, // B1 = -0.631042 + 11068, // B0 = 0.337769 + 31214, // A1 = 1.905212 + -32491, // A2 = -0.991577 + 16374, // B2 = 0.499695 + -15781, // B1 = -0.963196 + 16374, // B0 = 0.499695 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 392.txt + 31152, // A1 = -1.901428 + -32613, // A2 = 0.995300 + -314, // B2 = -0.009605 + 0, // B1 = 0.000000 + 314, // B0 = 0.009605 + 31156, // A1 = -1.901672 + -32694, // A2 = 0.997742 + 28847, // B2 = 0.880371 + -2734, // B1 = -0.166901 + 28847, // B0 = 0.880371 + 31225, // A1 = -1.905823 + -32696, // A2 = 0.997803 + 462, // B2 = 0.014108 + -442, // B1 = -0.027019 + 462, // B0 = 0.014108 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f400_425[] + 30836, // A1 = 1.882141 + -32296, // A2 = -0.985596 + -324, // B2 = -0.009903 + 0, // B1 = 0 + 324, // B0 = 0.009903 + 30825, // A1 = 1.881409 + -32570, // A2 = -0.993958 + 16847, // B2 = 0.51416 + -15792, // B1 = -0.963898 + 16847, // B0 = 0.51416 + 31106, // A1 = 1.89856 + -32584, // A2 = -0.994415 + 9579, // B2 = 0.292328 + -9164, // B1 = -0.559357 + 9579, // B0 = 0.292328 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f400_440[] + 30702, // A1 = 1.873962 + -32134, // A2 = -0.980682 + -517, // B2 = -0.015793 + 0, // B1 = 0 + 517, // B0 = 0.015793 + 30676, // A1 = 1.872375 + -32520, // A2 = -0.992462 + 8144, // B2 = 0.24855 + -7596, // B1 = -0.463684 + 8144, // B0 = 0.24855 + 31084, // A1 = 1.897217 + -32547, // A2 = -0.993256 + 22713, // B2 = 0.693176 + -21734, // B1 = -1.326599 + 22713, // B0 = 0.693176 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f400_450[] + 30613, // A1 = 1.86853 + -32031, // A2 = -0.977509 + -618, // B2 = -0.018866 + 0, // B1 = 0 + 618, // B0 = 0.018866 + 30577, // A1 = 1.866272 + -32491, // A2 = -0.991577 + 9612, // B2 = 0.293335 + -8935, // B1 = -0.54541 + 9612, // B0 = 0.293335 + 31071, // A1 = 1.896484 + -32524, // A2 = -0.992584 + 21596, // B2 = 0.659058 + -20667, // B1 = -1.261414 + 21596, // B0 = 0.659058 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 420.txt + 30914, // A1 = -1.886841 + -32584, // A2 = 0.994385 + -426, // B2 = -0.013020 + 0, // B1 = 0.000000 + 426, // B0 = 0.013020 + 30914, // A1 = -1.886841 + -32679, // A2 = 0.997314 + 17520, // B2 = 0.534668 + -16471, // B1 = -1.005310 + 17520, // B0 = 0.534668 + 31004, // A1 = -1.892334 + -32683, // A2 = 0.997406 + 819, // B2 = 0.025023 + -780, // B1 = -0.047619 + 819, // B0 = 0.025023 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 425.txt + 30881, // A1 = -1.884827 + -32603, // A2 = 0.994965 + -496, // B2 = -0.015144 + 0, // B1 = 0.000000 + 496, // B0 = 0.015144 + 30880, // A1 = -1.884766 + -32692, // A2 = 0.997711 + 24767, // B2 = 0.755859 + -23290, // B1 = -1.421509 + 24767, // B0 = 0.755859 + 30967, // A1 = -1.890076 + -32694, // A2 = 0.997772 + 728, // B2 = 0.022232 + -691, // B1 = -0.042194 + 728, // B0 = 0.022232 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f425_450[] + 30646, // A1 = 1.870544 + -32327, // A2 = -0.986572 + -287, // B2 = -0.008769 + 0, // B1 = 0 + 287, // B0 = 0.008769 + 30627, // A1 = 1.869324 + -32607, // A2 = -0.995087 + 13269, // B2 = 0.404968 + -12376, // B1 = -0.755432 + 13269, // B0 = 0.404968 + 30924, // A1 = 1.887512 + -32619, // A2 = -0.995453 + 19950, // B2 = 0.608826 + -18940, // B1 = -1.156006 + 19950, // B0 = 0.608826 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f425_475[] + 30396, // A1 = 1.855225 + -32014, // A2 = -0.97699 + -395, // B2 = -0.012055 + 0, // B1 = 0 + 395, // B0 = 0.012055 + 30343, // A1 = 1.85199 + -32482, // A2 = -0.991302 + 17823, // B2 = 0.543945 + -16431, // B1 = -1.002869 + 17823, // B0 = 0.543945 + 30872, // A1 = 1.884338 + -32516, // A2 = -0.99231 + 18124, // B2 = 0.553101 + -17246, // B1 = -1.052673 + 18124, // B0 = 0.553101 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 435.txt + 30796, // A1 = -1.879639 + -32603, // A2 = 0.994965 + -254, // B2 = -0.007762 + 0, // B1 = 0.000000 + 254, // B0 = 0.007762 + 30793, // A1 = -1.879456 + -32692, // A2 = 0.997711 + 18934, // B2 = 0.577820 + -17751, // B1 = -1.083496 + 18934, // B0 = 0.577820 + 30882, // A1 = -1.884888 + -32694, // A2 = 0.997772 + 1858, // B2 = 0.056713 + -1758, // B1 = -0.107357 + 1858, // B0 = 0.056713 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f440_450[] + 30641, // A1 = 1.870239 + -32458, // A2 = -0.99057 + -155, // B2 = -0.004735 + 0, // B1 = 0 + 155, // B0 = 0.004735 + 30631, // A1 = 1.869568 + -32630, // A2 = -0.995789 + 11453, // B2 = 0.349548 + -10666, // B1 = -0.651001 + 11453, // B0 = 0.349548 + 30810, // A1 = 1.880554 + -32634, // A2 = -0.995941 + 12237, // B2 = 0.373474 + -11588, // B1 = -0.707336 + 12237, // B0 = 0.373474 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f440_480[] + 30367, // A1 = 1.853455 + -32147, // A2 = -0.981079 + -495, // B2 = -0.015113 + 0, // B1 = 0 + 495, // B0 = 0.015113 + 30322, // A1 = 1.850769 + -32543, // A2 = -0.993134 + 10031, // B2 = 0.306152 + -9252, // B1 = -0.564728 + 10031, // B0 = 0.306152 + 30770, // A1 = 1.878052 + -32563, // A2 = -0.993774 + 22674, // B2 = 0.691956 + -21465, // B1 = -1.31012 + 22674, // B0 = 0.691956 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 445.txt + 30709, // A1 = -1.874329 + -32603, // A2 = 0.994965 + -83, // B2 = -0.002545 + 0, // B1 = 0.000000 + 83, // B0 = 0.002545 + 30704, // A1 = -1.874084 + -32692, // A2 = 0.997711 + 10641, // B2 = 0.324738 + -9947, // B1 = -0.607147 + 10641, // B0 = 0.324738 + 30796, // A1 = -1.879639 + -32694, // A2 = 0.997772 + 10079, // B2 = 0.307587 + 9513, // B1 = 0.580688 + 10079, // B0 = 0.307587 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 450.txt + 30664, // A1 = -1.871643 + -32603, // A2 = 0.994965 + -164, // B2 = -0.005029 + 0, // B1 = 0.000000 + 164, // B0 = 0.005029 + 30661, // A1 = -1.871399 + -32692, // A2 = 0.997711 + 15294, // B2 = 0.466736 + -14275, // B1 = -0.871307 + 15294, // B0 = 0.466736 + 30751, // A1 = -1.876953 + -32694, // A2 = 0.997772 + 3548, // B2 = 0.108284 + -3344, // B1 = -0.204155 + 3548, // B0 = 0.108284 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 452.txt + 30653, // A1 = -1.870911 + -32615, // A2 = 0.995361 + -209, // B2 = -0.006382 + 0, // B1 = 0.000000 + 209, // B0 = 0.006382 + 30647, // A1 = -1.870605 + -32702, // A2 = 0.997986 + 18971, // B2 = 0.578979 + -17716, // B1 = -1.081299 + 18971, // B0 = 0.578979 + 30738, // A1 = -1.876099 + -32702, // A2 = 0.998016 + 2967, // B2 = 0.090561 + -2793, // B1 = -0.170502 + 2967, // B0 = 0.090561 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 475.txt + 30437, // A1 = -1.857727 + -32603, // A2 = 0.994965 + -264, // B2 = -0.008062 + 0, // B1 = 0.000000 + 264, // B0 = 0.008062 + 30430, // A1 = -1.857300 + -32692, // A2 = 0.997711 + 21681, // B2 = 0.661682 + -20082, // B1 = -1.225708 + 21681, // B0 = 0.661682 + 30526, // A1 = -1.863220 + -32694, // A2 = 0.997742 + 1559, // B2 = 0.047600 + -1459, // B1 = -0.089096 + 1559, // B0 = 0.047600 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f480_620[] + 28975, // A1 = 1.768494 + -30955, // A2 = -0.944672 + -1026, // B2 = -0.03133 + 0, // B1 = 0 + 1026, // B0 = 0.03133 + 28613, // A1 = 1.746399 + -32089, // A2 = -0.979309 + 14214, // B2 = 0.433807 + -12202, // B1 = -0.744812 + 14214, // B0 = 0.433807 + 30243, // A1 = 1.845947 + -32238, // A2 = -0.983856 + 24825, // B2 = 0.757629 + -23402, // B1 = -1.428345 + 24825, // B0 = 0.757629 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 494.txt + 30257, // A1 = -1.846741 + -32605, // A2 = 0.995056 + -249, // B2 = -0.007625 + 0, // B1 = 0.000000 + 249, // B0 = 0.007625 + 30247, // A1 = -1.846191 + -32694, // A2 = 0.997772 + 18088, // B2 = 0.552002 + -16652, // B1 = -1.016418 + 18088, // B0 = 0.552002 + 30348, // A1 = -1.852295 + -32696, // A2 = 0.997803 + 2099, // B2 = 0.064064 + -1953, // B1 = -0.119202 + 2099, // B0 = 0.064064 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 500.txt + 30202, // A1 = -1.843431 + -32624, // A2 = 0.995622 + -413, // B2 = -0.012622 + 0, // B1 = 0.000000 + 413, // B0 = 0.012622 + 30191, // A1 = -1.842721 + -32714, // A2 = 0.998364 + 25954, // B2 = 0.792057 + -23890, // B1 = -1.458131 + 25954, // B0 = 0.792057 + 30296, // A1 = -1.849172 + -32715, // A2 = 0.998397 + 2007, // B2 = 0.061264 + -1860, // B1 = -0.113568 + 2007, // B0 = 0.061264 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 520.txt + 30001, // A1 = -1.831116 + -32613, // A2 = 0.995270 + -155, // B2 = -0.004750 + 0, // B1 = 0.000000 + 155, // B0 = 0.004750 + 29985, // A1 = -1.830200 + -32710, // A2 = 0.998260 + 6584, // B2 = 0.200928 + -6018, // B1 = -0.367355 + 6584, // B0 = 0.200928 + 30105, // A1 = -1.837524 + -32712, // A2 = 0.998291 + 23812, // B2 = 0.726685 + -21936, // B1 = -1.338928 + 23812, // B0 = 0.726685 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 523.txt + 29964, // A1 = -1.828918 + -32601, // A2 = 0.994904 + -101, // B2 = -0.003110 + 0, // B1 = 0.000000 + 101, // B0 = 0.003110 + 29949, // A1 = -1.827942 + -32700, // A2 = 0.997925 + 11041, // B2 = 0.336975 + -10075, // B1 = -0.614960 + 11041, // B0 = 0.336975 + 30070, // A1 = -1.835388 + -32702, // A2 = 0.997986 + 16762, // B2 = 0.511536 + -15437, // B1 = -0.942230 + 16762, // B0 = 0.511536 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 525.txt + 29936, // A1 = -1.827209 + -32584, // A2 = 0.994415 + -91, // B2 = -0.002806 + 0, // B1 = 0.000000 + 91, // B0 = 0.002806 + 29921, // A1 = -1.826233 + -32688, // A2 = 0.997559 + 11449, // B2 = 0.349396 + -10426, // B1 = -0.636383 + 11449, // B0 = 0.349396 + 30045, // A1 = -1.833862 + -32688, // A2 = 0.997589 + 13055, // B2 = 0.398407 + -12028, // B1 = -0.734161 + 13055, // B0 = 0.398407 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f540_660[] + 28499, // A1 = 1.739441 + -31129, // A2 = -0.949982 + -849, // B2 = -0.025922 + 0, // B1 = 0 + 849, // B0 = 0.025922 + 28128, // A1 = 1.716797 + -32130, // A2 = -0.98056 + 14556, // B2 = 0.444214 + -12251, // B1 = -0.747772 + 14556, // B0 = 0.444244 + 29667, // A1 = 1.81073 + -32244, // A2 = -0.984039 + 23038, // B2 = 0.703064 + -21358, // B1 = -1.303589 + 23040, // B0 = 0.703125 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 587.txt + 29271, // A1 = -1.786560 + -32599, // A2 = 0.994873 + -490, // B2 = -0.014957 + 0, // B1 = 0.000000 + 490, // B0 = 0.014957 + 29246, // A1 = -1.785095 + -32700, // A2 = 0.997925 + 28961, // B2 = 0.883850 + -25796, // B1 = -1.574463 + 28961, // B0 = 0.883850 + 29383, // A1 = -1.793396 + -32700, // A2 = 0.997955 + 1299, // B2 = 0.039650 + -1169, // B1 = -0.071396 + 1299, // B0 = 0.039650 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 590.txt + 29230, // A1 = -1.784058 + -32584, // A2 = 0.994415 + -418, // B2 = -0.012757 + 0, // B1 = 0.000000 + 418, // B0 = 0.012757 + 29206, // A1 = -1.782593 + -32688, // A2 = 0.997559 + 36556, // B2 = 1.115601 + -32478, // B1 = -1.982300 + 36556, // B0 = 1.115601 + 29345, // A1 = -1.791077 + -32688, // A2 = 0.997589 + 897, // B2 = 0.027397 + -808, // B1 = -0.049334 + 897, // B0 = 0.027397 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 600.txt + 29116, // A1 = -1.777100 + -32603, // A2 = 0.994965 + -165, // B2 = -0.005039 + 0, // B1 = 0.000000 + 165, // B0 = 0.005039 + 29089, // A1 = -1.775452 + -32708, // A2 = 0.998199 + 6963, // B2 = 0.212494 + -6172, // B1 = -0.376770 + 6963, // B0 = 0.212494 + 29237, // A1 = -1.784485 + -32710, // A2 = 0.998230 + 24197, // B2 = 0.738464 + -21657, // B1 = -1.321899 + 24197, // B0 = 0.738464 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 660.txt + 28376, // A1 = -1.731934 + -32567, // A2 = 0.993896 + -363, // B2 = -0.011102 + 0, // B1 = 0.000000 + 363, // B0 = 0.011102 + 28337, // A1 = -1.729614 + -32683, // A2 = 0.997434 + 21766, // B2 = 0.664246 + -18761, // B1 = -1.145081 + 21766, // B0 = 0.664246 + 28513, // A1 = -1.740356 + -32686, // A2 = 0.997498 + 2509, // B2 = 0.076584 + -2196, // B1 = -0.134041 + 2509, // B0 = 0.076584 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 700.txt + 27844, // A1 = -1.699463 + -32563, // A2 = 0.993744 + -366, // B2 = -0.011187 + 0, // B1 = 0.000000 + 366, // B0 = 0.011187 + 27797, // A1 = -1.696655 + -32686, // A2 = 0.997498 + 22748, // B2 = 0.694214 + -19235, // B1 = -1.174072 + 22748, // B0 = 0.694214 + 27995, // A1 = -1.708740 + -32688, // A2 = 0.997559 + 2964, // B2 = 0.090477 + -2546, // B1 = -0.155449 + 2964, // B0 = 0.090477 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 740.txt + 27297, // A1 = -1.666077 + -32551, // A2 = 0.993408 + -345, // B2 = -0.010540 + 0, // B1 = 0.000000 + 345, // B0 = 0.010540 + 27240, // A1 = -1.662598 + -32683, // A2 = 0.997406 + 22560, // B2 = 0.688477 + -18688, // B1 = -1.140625 + 22560, // B0 = 0.688477 + 27461, // A1 = -1.676147 + -32684, // A2 = 0.997467 + 3541, // B2 = 0.108086 + -2985, // B1 = -0.182220 + 3541, // B0 = 0.108086 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 750.txt + 27155, // A1 = -1.657410 + -32551, // A2 = 0.993408 + -462, // B2 = -0.014117 + 0, // B1 = 0.000000 + 462, // B0 = 0.014117 + 27097, // A1 = -1.653870 + -32683, // A2 = 0.997406 + 32495, // B2 = 0.991699 + -26776, // B1 = -1.634338 + 32495, // B0 = 0.991699 + 27321, // A1 = -1.667542 + -32684, // A2 = 0.997467 + 1835, // B2 = 0.056007 + -1539, // B1 = -0.093948 + 1835, // B0 = 0.056007 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f750_1450[] + 19298, // A1 = 1.177917 + -24471, // A2 = -0.746796 + -4152, // B2 = -0.126709 + 0, // B1 = 0 + 4152, // B0 = 0.126709 + 12902, // A1 = 0.787476 + -29091, // A2 = -0.887817 + 12491, // B2 = 0.38121 + -1794, // B1 = -0.109528 + 12494, // B0 = 0.381317 + 26291, // A1 = 1.604736 + -30470, // A2 = -0.929901 + 28859, // B2 = 0.880737 + -26084, // B1 = -1.592102 + 28861, // B0 = 0.880798 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 770.txt + 26867, // A1 = -1.639832 + -32551, // A2 = 0.993408 + -123, // B2 = -0.003755 + 0, // B1 = 0.000000 + 123, // B0 = 0.003755 + 26805, // A1 = -1.636108 + -32683, // A2 = 0.997406 + 17297, // B2 = 0.527863 + -14096, // B1 = -0.860382 + 17297, // B0 = 0.527863 + 27034, // A1 = -1.650085 + -32684, // A2 = 0.997467 + 12958, // B2 = 0.395477 + -10756, // B1 = -0.656525 + 12958, // B0 = 0.395477 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 800.txt + 26413, // A1 = -1.612122 + -32547, // A2 = 0.993286 + -223, // B2 = -0.006825 + 0, // B1 = 0.000000 + 223, // B0 = 0.006825 + 26342, // A1 = -1.607849 + -32686, // A2 = 0.997498 + 6391, // B2 = 0.195053 + -5120, // B1 = -0.312531 + 6391, // B0 = 0.195053 + 26593, // A1 = -1.623108 + -32688, // A2 = 0.997559 + 23681, // B2 = 0.722717 + -19328, // B1 = -1.179688 + 23681, // B0 = 0.722717 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 816.txt + 26168, // A1 = -1.597209 + -32528, // A2 = 0.992706 + -235, // B2 = -0.007182 + 0, // B1 = 0.000000 + 235, // B0 = 0.007182 + 26092, // A1 = -1.592590 + -32675, // A2 = 0.997192 + 20823, // B2 = 0.635498 + -16510, // B1 = -1.007751 + 20823, // B0 = 0.635498 + 26363, // A1 = -1.609070 + -32677, // A2 = 0.997253 + 6739, // B2 = 0.205688 + -5459, // B1 = -0.333206 + 6739, // B0 = 0.205688 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 850.txt + 25641, // A1 = -1.565063 + -32536, // A2 = 0.992950 + -121, // B2 = -0.003707 + 0, // B1 = 0.000000 + 121, // B0 = 0.003707 + 25560, // A1 = -1.560059 + -32684, // A2 = 0.997437 + 18341, // B2 = 0.559753 + -14252, // B1 = -0.869904 + 18341, // B0 = 0.559753 + 25837, // A1 = -1.577026 + -32684, // A2 = 0.997467 + 16679, // B2 = 0.509003 + -13232, // B1 = -0.807648 + 16679, // B0 = 0.509003 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f857_1645[] + 16415, // A1 = 1.001953 + -23669, // A2 = -0.722321 + -4549, // B2 = -0.138847 + 0, // B1 = 0 + 4549, // B0 = 0.138847 + 8456, // A1 = 0.516174 + -28996, // A2 = -0.884918 + 13753, // B2 = 0.419724 + -12, // B1 = -0.000763 + 13757, // B0 = 0.419846 + 24632, // A1 = 1.503418 + -30271, // A2 = -0.923828 + 29070, // B2 = 0.887146 + -25265, // B1 = -1.542114 + 29073, // B0 = 0.887268 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 900.txt + 24806, // A1 = -1.514099 + -32501, // A2 = 0.991852 + -326, // B2 = -0.009969 + 0, // B1 = 0.000000 + 326, // B0 = 0.009969 + 24709, // A1 = -1.508118 + -32659, // A2 = 0.996674 + 20277, // B2 = 0.618835 + -15182, // B1 = -0.926636 + 20277, // B0 = 0.618835 + 25022, // A1 = -1.527222 + -32661, // A2 = 0.996735 + 4320, // B2 = 0.131836 + -3331, // B1 = -0.203339 + 4320, // B0 = 0.131836 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f900_1300[] + 19776, // A1 = 1.207092 + -27437, // A2 = -0.837341 + -2666, // B2 = -0.081371 + 0, // B1 = 0 + 2666, // B0 = 0.081371 + 16302, // A1 = 0.995026 + -30354, // A2 = -0.926361 + 10389, // B2 = 0.317062 + -3327, // B1 = -0.203064 + 10389, // B0 = 0.317062 + 24299, // A1 = 1.483154 + -30930, // A2 = -0.943909 + 25016, // B2 = 0.763428 + -21171, // B1 = -1.292236 + 25016, // B0 = 0.763428 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f935_1215[] + 20554, // A1 = 1.254517 + -28764, // A2 = -0.877838 + -2048, // B2 = -0.062515 + 0, // B1 = 0 + 2048, // B0 = 0.062515 + 18209, // A1 = 1.11145 + -30951, // A2 = -0.94458 + 9390, // B2 = 0.286575 + -3955, // B1 = -0.241455 + 9390, // B0 = 0.286575 + 23902, // A1 = 1.458923 + -31286, // A2 = -0.954803 + 23252, // B2 = 0.709595 + -19132, // B1 = -1.167725 + 23252, // B0 = 0.709595 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f941_1477[] + 17543, // A1 = 1.07074 + -26220, // A2 = -0.800201 + -3298, // B2 = -0.100647 + 0, // B1 = 0 + 3298, // B0 = 0.100647 + 12423, // A1 = 0.75827 + -30036, // A2 = -0.916626 + 12651, // B2 = 0.386078 + -2444, // B1 = -0.14917 + 12653, // B0 = 0.386154 + 23518, // A1 = 1.435425 + -30745, // A2 = -0.938293 + 27282, // B2 = 0.832581 + -22529, // B1 = -1.375122 + 27286, // B0 = 0.832703 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 942.txt + 24104, // A1 = -1.471252 + -32507, // A2 = 0.992065 + -351, // B2 = -0.010722 + 0, // B1 = 0.000000 + 351, // B0 = 0.010722 + 23996, // A1 = -1.464600 + -32671, // A2 = 0.997040 + 22848, // B2 = 0.697266 + -16639, // B1 = -1.015564 + 22848, // B0 = 0.697266 + 24332, // A1 = -1.485168 + -32673, // A2 = 0.997101 + 4906, // B2 = 0.149727 + -3672, // B1 = -0.224174 + 4906, // B0 = 0.149727 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 950.txt + 23967, // A1 = -1.462830 + -32507, // A2 = 0.992065 + -518, // B2 = -0.015821 + 0, // B1 = 0.000000 + 518, // B0 = 0.015821 + 23856, // A1 = -1.456055 + -32671, // A2 = 0.997040 + 26287, // B2 = 0.802246 + -19031, // B1 = -1.161560 + 26287, // B0 = 0.802246 + 24195, // A1 = -1.476746 + -32673, // A2 = 0.997101 + 2890, // B2 = 0.088196 + -2151, // B1 = -0.131317 + 2890, // B0 = 0.088196 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f950_1400[] + 18294, // A1 = 1.116638 + -26962, // A2 = -0.822845 + -2914, // B2 = -0.088936 + 0, // B1 = 0 + 2914, // B0 = 0.088936 + 14119, // A1 = 0.861786 + -30227, // A2 = -0.922455 + 11466, // B2 = 0.349945 + -2833, // B1 = -0.172943 + 11466, // B0 = 0.349945 + 23431, // A1 = 1.430115 + -30828, // A2 = -0.940796 + 25331, // B2 = 0.773071 + -20911, // B1 = -1.276367 + 25331, // B0 = 0.773071 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 975.txt + 23521, // A1 = -1.435608 + -32489, // A2 = 0.991516 + -193, // B2 = -0.005915 + 0, // B1 = 0.000000 + 193, // B0 = 0.005915 + 23404, // A1 = -1.428467 + -32655, // A2 = 0.996582 + 17740, // B2 = 0.541412 + -12567, // B1 = -0.767029 + 17740, // B0 = 0.541412 + 23753, // A1 = -1.449829 + -32657, // A2 = 0.996613 + 9090, // B2 = 0.277405 + -6662, // B1 = -0.406647 + 9090, // B0 = 0.277405 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 1000.txt + 23071, // A1 = -1.408203 + -32489, // A2 = 0.991516 + -293, // B2 = -0.008965 + 0, // B1 = 0.000000 + 293, // B0 = 0.008965 + 22951, // A1 = -1.400818 + -32655, // A2 = 0.996582 + 5689, // B2 = 0.173645 + -3951, // B1 = -0.241150 + 5689, // B0 = 0.173645 + 23307, // A1 = -1.422607 + -32657, // A2 = 0.996613 + 18692, // B2 = 0.570435 + -13447, // B1 = -0.820770 + 18692, // B0 = 0.570435 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 1020.txt + 22701, // A1 = -1.385620 + -32474, // A2 = 0.991058 + -292, // B2 = -0.008933 + 0, //163840 , // B1 = 10.000000 + 292, // B0 = 0.008933 + 22564, // A1 = -1.377258 + -32655, // A2 = 0.996552 + 20756, // B2 = 0.633423 + -14176, // B1 = -0.865295 + 20756, // B0 = 0.633423 + 22960, // A1 = -1.401428 + -32657, // A2 = 0.996613 + 6520, // B2 = 0.198990 + -4619, // B1 = -0.281937 + 6520, // B0 = 0.198990 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 1050.txt + 22142, // A1 = -1.351501 + -32474, // A2 = 0.991058 + -147, // B2 = -0.004493 + 0, // B1 = 0.000000 + 147, // B0 = 0.004493 + 22000, // A1 = -1.342834 + -32655, // A2 = 0.996552 + 15379, // B2 = 0.469360 + -10237, // B1 = -0.624847 + 15379, // B0 = 0.469360 + 22406, // A1 = -1.367554 + -32657, // A2 = 0.996613 + 17491, // B2 = 0.533783 + -12096, // B1 = -0.738312 + 17491, // B0 = 0.533783 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f1100_1750[] + 12973, // A1 = 0.79184 + -24916, // A2 = -0.760376 + 6655, // B2 = 0.203102 + 367, // B1 = 0.0224 + 6657, // B0 = 0.203171 + 5915, // A1 = 0.361053 + -29560, // A2 = -0.90213 + -7777, // B2 = -0.23735 + 0, // B1 = 0 + 7777, // B0 = 0.23735 + 20510, // A1 = 1.251892 + -30260, // A2 = -0.923462 + 26662, // B2 = 0.81366 + -20573, // B1 = -1.255737 + 26668, // B0 = 0.813843 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 1140.txt + 20392, // A1 = -1.244629 + -32460, // A2 = 0.990601 + -270, // B2 = -0.008240 + 0, // B1 = 0.000000 + 270, // B0 = 0.008240 + 20218, // A1 = -1.234009 + -32655, // A2 = 0.996582 + 21337, // B2 = 0.651154 + -13044, // B1 = -0.796143 + 21337, // B0 = 0.651154 + 20684, // A1 = -1.262512 + -32657, // A2 = 0.996643 + 8572, // B2 = 0.261612 + -5476, // B1 = -0.334244 + 8572, // B0 = 0.261612 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 1200.txt + 19159, // A1 = -1.169373 + -32456, // A2 = 0.990509 + -335, // B2 = -0.010252 + 0, // B1 = 0.000000 + 335, // B0 = 0.010252 + 18966, // A1 = -1.157593 + -32661, // A2 = 0.996735 + 6802, // B2 = 0.207588 + -3900, // B1 = -0.238098 + 6802, // B0 = 0.207588 + 19467, // A1 = -1.188232 + -32661, // A2 = 0.996765 + 25035, // B2 = 0.764008 + -15049, // B1 = -0.918579 + 25035, // B0 = 0.764008 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 1209.txt + 18976, // A1 = -1.158264 + -32439, // A2 = 0.989990 + -183, // B2 = -0.005588 + 0, // B1 = 0.000000 + 183, // B0 = 0.005588 + 18774, // A1 = -1.145874 + -32650, // A2 = 0.996429 + 15468, // B2 = 0.472076 + -8768, // B1 = -0.535217 + 15468, // B0 = 0.472076 + 19300, // A1 = -1.177979 + -32652, // A2 = 0.996490 + 19840, // B2 = 0.605499 + -11842, // B1 = -0.722809 + 19840, // B0 = 0.605499 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 1330.txt + 16357, // A1 = -0.998413 + -32368, // A2 = 0.987793 + -217, // B2 = -0.006652 + 0, // B1 = 0.000000 + 217, // B0 = 0.006652 + 16107, // A1 = -0.983126 + -32601, // A2 = 0.994904 + 11602, // B2 = 0.354065 + -5555, // B1 = -0.339111 + 11602, // B0 = 0.354065 + 16722, // A1 = -1.020630 + -32603, // A2 = 0.994965 + 15574, // B2 = 0.475311 + -8176, // B1 = -0.499069 + 15574, // B0 = 0.475311 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 1336.txt + 16234, // A1 = -0.990875 + 32404, // A2 = -0.988922 + -193, // B2 = -0.005908 + 0, // B1 = 0.000000 + 193, // B0 = 0.005908 + 15986, // A1 = -0.975769 + -32632, // A2 = 0.995880 + 18051, // B2 = 0.550903 + -8658, // B1 = -0.528473 + 18051, // B0 = 0.550903 + 16591, // A1 = -1.012695 + -32634, // A2 = 0.995941 + 15736, // B2 = 0.480240 + -8125, // B1 = -0.495926 + 15736, // B0 = 0.480240 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 1366.txt + 15564, // A1 = -0.949982 + -32404, // A2 = 0.988922 + -269, // B2 = -0.008216 + 0, // B1 = 0.000000 + 269, // B0 = 0.008216 + 15310, // A1 = -0.934479 + -32632, // A2 = 0.995880 + 10815, // B2 = 0.330063 + -4962, // B1 = -0.302887 + 10815, // B0 = 0.330063 + 15924, // A1 = -0.971924 + -32634, // A2 = 0.995941 + 18880, // B2 = 0.576172 + -9364, // B1 = -0.571594 + 18880, // B0 = 0.576172 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 1380.txt + 15247, // A1 = -0.930603 + -32397, // A2 = 0.988708 + -244, // B2 = -0.007451 + 0, // B1 = 0.000000 + 244, // B0 = 0.007451 + 14989, // A1 = -0.914886 + -32627, // A2 = 0.995697 + 18961, // B2 = 0.578644 + -8498, // B1 = -0.518707 + 18961, // B0 = 0.578644 + 15608, // A1 = -0.952667 + -32628, // A2 = 0.995758 + 11145, // B2 = 0.340134 + -5430, // B1 = -0.331467 + 11145, // B0 = 0.340134 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 1400.txt + 14780, // A1 = -0.902130 + -32393, // A2 = 0.988586 + -396, // B2 = -0.012086 + 0, // B1 = 0.000000 + 396, // B0 = 0.012086 + 14510, // A1 = -0.885651 + -32630, // A2 = 0.995819 + 6326, // B2 = 0.193069 + -2747, // B1 = -0.167671 + 6326, // B0 = 0.193069 + 15154, // A1 = -0.924957 + -32632, // A2 = 0.995850 + 23235, // B2 = 0.709076 + -10983, // B1 = -0.670380 + 23235, // B0 = 0.709076 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 1477.txt + 13005, // A1 = -0.793793 + -32368, // A2 = 0.987823 + -500, // B2 = -0.015265 + 0, // B1 = 0.000000 + 500, // B0 = 0.015265 + 12708, // A1 = -0.775665 + -32615, // A2 = 0.995331 + 11420, // B2 = 0.348526 + -4306, // B1 = -0.262833 + 11420, // B0 = 0.348526 + 13397, // A1 = -0.817688 + -32615, // A2 = 0.995361 + 9454, // B2 = 0.288528 + -3981, // B1 = -0.243027 + 9454, // B0 = 0.288528 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 1600.txt + 10046, // A1 = -0.613190 + -32331, // A2 = 0.986694 + -455, // B2 = -0.013915 + 0, // B1 = 0.000000 + 455, // B0 = 0.013915 + 9694, // A1 = -0.591705 + -32601, // A2 = 0.994934 + 6023, // B2 = 0.183815 + -1708, // B1 = -0.104279 + 6023, // B0 = 0.183815 + 10478, // A1 = -0.639587 + -32603, // A2 = 0.994965 + 22031, // B2 = 0.672333 + -7342, // B1 = -0.448151 + 22031, // B0 = 0.672333 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // f1633_1638[] + 9181, // A1 = 0.560394 + -32256, // A2 = -0.984375 + -556, // B2 = -0.016975 + 0, // B1 = 0 + 556, // B0 = 0.016975 + 8757, // A1 = 0.534515 + -32574, // A2 = -0.99408 + 8443, // B2 = 0.25769 + -2135, // B1 = -0.130341 + 8443, // B0 = 0.25769 + 9691, // A1 = 0.591522 + -32574, // A2 = -0.99411 + 15446, // B2 = 0.471375 + -4809, // B1 = -0.293579 + 15446, // B0 = 0.471375 + 7, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 1800.txt + 5076, // A1 = -0.309875 + -32304, // A2 = 0.985840 + -508, // B2 = -0.015503 + 0, // B1 = 0.000000 + 508, // B0 = 0.015503 + 4646, // A1 = -0.283600 + -32605, // A2 = 0.995026 + 6742, // B2 = 0.205780 + -878, // B1 = -0.053635 + 6742, // B0 = 0.205780 + 5552, // A1 = -0.338928 + -32605, // A2 = 0.995056 + 23667, // B2 = 0.722260 + -4297, // B1 = -0.262329 + 23667, // B0 = 0.722260 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + }, + { // 1860.txt + 3569, // A1 = -0.217865 + -32292, // A2 = 0.985504 + -239, // B2 = -0.007322 + 0, // B1 = 0.000000 + 239, // B0 = 0.007322 + 3117, // A1 = -0.190277 + -32603, // A2 = 0.994965 + 18658, // B2 = 0.569427 + -1557, // B1 = -0.095032 + 18658, // B0 = 0.569427 + 4054, // A1 = -0.247437 + -32603, // A2 = 0.994965 + 18886, // B2 = 0.576385 + -2566, // B1 = -0.156647 + 18886, // B0 = 0.576385 + 5, // Internal filter scaling + 159, // Minimum in-band energy threshold + 21, // 21/32 in-band to broad-band ratio + 0x0FF5 // shift-mask 0x0FF (look at 16 half-frames) bit count = 5 + },}; + +static int ixj_init_filter(int board, IXJ_FILTER * jf) +{ + unsigned short cmd; + int cnt, max; + IXJ *j = &ixj[board]; + + if (jf->filter > 3) { + return -1; + } + if (ixj_WriteDSPCommand(0x5154 + jf->filter, board)) // Select Filter + + return -1; + + if (!jf->enable) { + if (ixj_WriteDSPCommand(0x5152, board)) // Disable Filter + + return -1; + else + return 0; + } else { + if (ixj_WriteDSPCommand(0x5153, board)) // Enable Filter + + return -1; + + // Select the filter (f0 - f3) to use. + if (ixj_WriteDSPCommand(0x5154 + jf->filter, board)) + return -1; + } + if (jf->freq < 12 && jf->freq > 3) { + // Select the frequency for the selected filter. + if (ixj_WriteDSPCommand(0x5170 + jf->freq, board)) + return -1; + } else if (jf->freq > 11) { + // We need to load a programmable filter set for undefined + // frequencies. So we will point the filter to a programmable set. + // Since there are only 4 filters and 4 programmable sets, we will + // just point the filter to the same number set and program it for the + // frequency we want. + if (ixj_WriteDSPCommand(0x5170 + jf->filter, board)) + return -1; + + if (j->ver.low != 0x12) { + cmd = 0x515B; + max = 19; + } else { + cmd = 0x515E; + max = 15; + } + if (ixj_WriteDSPCommand(cmd, board)) + return -1; + + for (cnt = 0; cnt < max; cnt++) { + if (ixj_WriteDSPCommand(tone_table[jf->freq][cnt], board)) + return -1; + } +/* if(j->ver.low != 0x12) + { + if(ixj_WriteDSPCommand(7, board)) + return -1; + if(ixj_WriteDSPCommand(159, board)) + return -1; + if(ixj_WriteDSPCommand(21, board)) + return -1; + if(ixj_WriteDSPCommand(0x0FF5, board)) + return -1; + } */ + } + return 0; +} + +static int ixj_init_tone(int board, IXJ_TONE * ti) +{ + int freq0, freq1; + unsigned short data; + + if (ti->freq0) { + freq0 = ti->freq0; + } else { + freq0 = 0x7FFF; + } + + if (ti->freq1) { + freq1 = ti->freq1; + } else { + freq1 = 0x7FFF; + } + +// if(ti->tone_index > 12 && ti->tone_index < 28) + { + if (ixj_WriteDSPCommand(0x6800 + ti->tone_index, board)) + return -1; + + if (ixj_WriteDSPCommand(0x6000 + (ti->gain0 << 4) + ti->gain1, board)) + return -1; + + data = freq0; + if (ixj_WriteDSPCommand(data, board)) + return -1; + + data = freq1; + if (ixj_WriteDSPCommand(data, board)) + return -1; + } + return freq0; +} diff -u --recursive --new-file v2.2.13/linux/drivers/telephony/ixj.h linux/drivers/telephony/ixj.h --- v2.2.13/linux/drivers/telephony/ixj.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/telephony/ixj.h Tue Jan 4 10:12:22 2000 @@ -0,0 +1,973 @@ +/* + * ixj.h + * + * Device Driver for the Internet PhoneJACK and + * Internet LineJACK Telephony Cards. + * + * (c) Copyright 1999 Quicknet Technologies, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Author: Ed Okerson, + * + * Contributors: Greg Herlein, + * David W. Erhart, + * John Sellers, + * Mike Preston, + * + * More information about the hardware related to this driver can be found + * at our website: http://www.quicknet.net + * + * Fixes: + */ +static char ixj_h_rcsid[] = "$Id: ixj.h,v 3.4 1999/12/16 22:18:36 root Exp root $"; + +#ifndef _I386_TYPES_H +#include +#endif + +#include +#include + +typedef __u16 WORD; +typedef __u32 DWORD; +typedef __u8 BYTE; +typedef __u8 BOOL; + +#define IXJMAX 16 + +#define TRUE 1 +#define FALSE 0 + +#ifndef min +#define min(a,b) (((a)<(b))?(a):(b)) +#endif +#ifndef max +#define max(a,b) (((a)>(b))?(a):(b)) +#endif + +/****************************************************************************** +* +* This structure when unioned with the structures below makes simple byte +* access to the registers easier. +* +******************************************************************************/ +typedef struct { + unsigned char low; + unsigned char high; +} BYTES; + +int ixj_WriteDSPCommand(unsigned short, int board); + +/****************************************************************************** +* +* This structure represents the Hardware Control Register of the CT8020/8021 +* The CT8020 is used in the Internet PhoneJACK, and the 8021 in the +* Internet LineJACK +* +******************************************************************************/ +typedef struct { + unsigned int rxrdy:1; + unsigned int txrdy:1; + unsigned int status:1; + unsigned int auxstatus:1; + unsigned int rxdma:1; + unsigned int txdma:1; + unsigned int rxburst:1; + unsigned int txburst:1; + unsigned int dmadir:1; + unsigned int cont:1; + unsigned int irqn:1; + unsigned int t:5; +} HCRBIT; + +typedef union { + HCRBIT bits; + BYTES bytes; +} HCR; + +/****************************************************************************** +* +* This structure represents the Hardware Status Register of the CT8020/8021 +* The CT8020 is used in the Internet PhoneJACK, and the 8021 in the +* Internet LineJACK +* +******************************************************************************/ +typedef struct { + unsigned int controlrdy:1; + unsigned int auxctlrdy:1; + unsigned int statusrdy:1; + unsigned int auxstatusrdy:1; + unsigned int rxrdy:1; + unsigned int txrdy:1; + unsigned int restart:1; + unsigned int irqn:1; + unsigned int rxdma:1; + unsigned int txdma:1; + unsigned int cohostshutdown:1; + unsigned int t:5; +} HSRBIT; + +typedef union { + HSRBIT bits; + BYTES bytes; +} HSR; + +/****************************************************************************** +* +* This structure represents the General Purpose IO Register of the CT8020/8021 +* The CT8020 is used in the Internet PhoneJACK, and the 8021 in the +* Internet LineJACK +* +******************************************************************************/ +typedef struct { + unsigned int x:1; + unsigned int gpio1:1; + unsigned int gpio2:1; + unsigned int gpio3:1; + unsigned int gpio4:1; + unsigned int gpio5:1; + unsigned int gpio6:1; + unsigned int gpio7:1; + unsigned int xread:1; + unsigned int gpio1read:1; + unsigned int gpio2read:1; + unsigned int gpio3read:1; + unsigned int gpio4read:1; + unsigned int gpio5read:1; + unsigned int gpio6read:1; + unsigned int gpio7read:1; +} GPIOBIT; + +typedef union { + GPIOBIT bits; + BYTES bytes; + unsigned short word; +} GPIO; + +/****************************************************************************** +* +* This structure represents the Line Monitor status response +* +******************************************************************************/ +typedef struct { + unsigned int digit:4; + unsigned int cpf_valid:1; + unsigned int dtmf_valid:1; + unsigned int peak:1; + unsigned int z:1; + unsigned int f0:1; + unsigned int f1:1; + unsigned int f2:1; + unsigned int f3:1; + unsigned int frame:4; +} LMON; + +typedef union { + LMON bits; + BYTES bytes; +} DTMF; + +typedef struct { + unsigned int z:7; + unsigned int dtmf_en:1; + unsigned int y:4; + unsigned int F3:1; + unsigned int F2:1; + unsigned int F1:1; + unsigned int F0:1; +} CP; + +typedef union { + CP bits; + BYTES bytes; +} CPTF; + +/****************************************************************************** +* +* This structure represents the Status Control Register on the Internet +* LineJACK +* +******************************************************************************/ +typedef struct { + unsigned int c0:1; + unsigned int c1:1; + unsigned int stereo:1; + unsigned int daafsyncen:1; + unsigned int led1:1; + unsigned int led2:1; + unsigned int led3:1; + unsigned int led4:1; +} PSCRWI; // Internet LineJACK and Internet PhoneJACK Lite + +typedef struct { + unsigned int eidp:1; + unsigned int eisd:1; + unsigned int x:6; +} PSCRWP; // Internet PhoneJACK PCI + +typedef union { + PSCRWI bits; + PSCRWP pcib; + char byte; +} PLD_SCRW; + +typedef struct { + unsigned int c0:1; + unsigned int c1:1; + unsigned int x:1; + unsigned int d0ee:1; + unsigned int mixerbusy:1; + unsigned int sci:1; + unsigned int dspflag:1; + unsigned int daaflag:1; +} PSCRRI; + +typedef struct { + unsigned int eidp:1; + unsigned int eisd:1; + unsigned int x:4; + unsigned int dspflag:1; + unsigned int det:1; +} PSCRRP; + +typedef union { + PSCRRI bits; + PSCRRP pcib; + char byte; +} PLD_SCRR; + +/****************************************************************************** +* +* These structures represents the SLIC Control Register on the +* Internet LineJACK +* +******************************************************************************/ +typedef struct { + unsigned int c1:1; + unsigned int c2:1; + unsigned int c3:1; + unsigned int b2en:1; + unsigned int spken:1; + unsigned int rly1:1; + unsigned int rly2:1; + unsigned int rly3:1; +} PSLICWRITE; + +typedef struct { + unsigned int state:3; + unsigned int b2en:1; + unsigned int spken:1; + unsigned int c3:1; + unsigned int potspstn:1; + unsigned int det:1; +} PSLICREAD; + +typedef struct { + unsigned int c1:1; + unsigned int c2:1; + unsigned int c3:1; + unsigned int b2en:1; + unsigned int e1:1; + unsigned int mic:1; + unsigned int spk:1; + unsigned int x:1; +} PSLICPCI; + +typedef union { + PSLICPCI pcib; + PSLICWRITE bits; + PSLICREAD slic; + char byte; +} PLD_SLICW; + +typedef union { + PSLICPCI pcib; + PSLICREAD bits; + char byte; +} PLD_SLICR; + +/****************************************************************************** +* +* These structures represents the Clock Control Register on the +* Internet LineJACK +* +******************************************************************************/ +typedef struct { + unsigned int clk0:1; + unsigned int clk1:1; + unsigned int clk2:1; + unsigned int x0:1; + unsigned int slic_e1:1; + unsigned int x1:1; + unsigned int x2:1; + unsigned int x3:1; +} PCLOCK; + +typedef union { + PCLOCK bits; + char byte; +} PLD_CLOCK; + +/****************************************************************************** +* +* These structures deal with the mixer on the Internet LineJACK +* +******************************************************************************/ + +typedef struct { + unsigned short vol[10]; + unsigned int recsrc; + unsigned int modcnt; + unsigned short micpreamp; +} MIX; + +/****************************************************************************** +* +* These structures deal with the DAA on the Internet LineJACK +* +******************************************************************************/ + +typedef struct _DAA_REGS { + //----------------------------------------------- + // SOP Registers + // + BYTE bySOP; + + union _SOP_REGS { + struct _SOP { + union // SOP - CR0 Register + { + BYTE reg; + struct _CR0_BITREGS { + BYTE CLK_EXT:1; // cr0[0:0] + + BYTE RIP:1; // cr0[1:1] + + BYTE AR:1; // cr0[2:2] + + BYTE AX:1; // cr0[3:3] + + BYTE FRR:1; // cr0[4:4] + + BYTE FRX:1; // cr0[5:5] + + BYTE IM:1; // cr0[6:6] + + BYTE TH:1; // cr0[7:7] + + } bitreg; + } cr0; + + union // SOP - CR1 Register + { + BYTE reg; + struct _CR1_REGS { + BYTE RM:1; // cr1[0:0] + + BYTE RMR:1; // cr1[1:1] + + BYTE No_auto:1; // cr1[2:2] + + BYTE Pulse:1; // cr1[3:3] + + BYTE P_Tone1:1; // cr1[4:4] + + BYTE P_Tone2:1; // cr1[5:5] + + BYTE E_Tone1:1; // cr1[6:6] + + BYTE E_Tone2:1; // cr1[7:7] + + } bitreg; + } cr1; + + union // SOP - CR2 Register + { + BYTE reg; + struct _CR2_REGS { + BYTE Call_II:1; // CR2[0:0] + + BYTE Call_I:1; // CR2[1:1] + + BYTE Call_en:1; // CR2[2:2] + + BYTE Call_pon:1; // CR2[3:3] + + BYTE IDR:1; // CR2[4:4] + + BYTE COT_R:3; // CR2[5:7] + + } bitreg; + } cr2; + + union // SOP - CR3 Register + { + BYTE reg; + struct _CR3_REGS { + BYTE DHP_X:1; // CR3[0:0] + + BYTE DHP_R:1; // CR3[1:1] + + BYTE Cal_pctl:1; // CR3[2:2] + + BYTE SEL:1; // CR3[3:3] + + BYTE TestLoops:4; // CR3[4:7] + + } bitreg; + } cr3; + + union // SOP - CR4 Register + { + BYTE reg; + struct _CR4_REGS { + BYTE Fsc_en:1; // CR4[0:0] + + BYTE Int_en:1; // CR4[1:1] + + BYTE AGX:2; // CR4[2:3] + + BYTE AGR_R:2; // CR4[4:5] + + BYTE AGR_Z:2; // CR4[6:7] + + } bitreg; + } cr4; + + union // SOP - CR5 Register + { + BYTE reg; + struct _CR5_REGS { + BYTE V_0:1; // CR5[0:0] + + BYTE V_1:1; // CR5[1:1] + + BYTE V_2:1; // CR5[2:2] + + BYTE V_3:1; // CR5[3:3] + + BYTE V_4:1; // CR5[4:4] + + BYTE V_5:1; // CR5[5:5] + + BYTE V_6:1; // CR5[6:6] + + BYTE V_7:1; // CR5[7:7] + + } bitreg; + } cr5; + + union // SOP - CR6 Register + { + BYTE reg; + struct _CR6_REGS { + BYTE reserved:8; // CR6[0:7] + + } bitreg; + } cr6; + + union // SOP - CR7 Register + { + BYTE reg; + struct _CR7_REGS { + BYTE reserved:8; // CR7[0:7] + + } bitreg; + } cr7; + } SOP; + + BYTE ByteRegs[sizeof(struct _SOP)]; + + } SOP_REGS; + + // DAA_REGS.SOP_REGS.SOP.CR5.reg + // DAA_REGS.SOP_REGS.SOP.CR5.bitreg + // DAA_REGS.SOP_REGS.SOP.CR5.bitreg.V_2 + // DAA_REGS.SOP_REGS.ByteRegs[5] + + //----------------------------------------------- + // XOP Registers + // + BYTE byXOP; + + union _XOP_REGS { + struct _XOP { + union // XOP - XR0 Register - Read values + { + BYTE reg; + struct _XR0_BITREGS { + BYTE SI_0:1; // XR0[0:0] - Read + + BYTE SI_1:1; // XR0[1:1] - Read + + BYTE VDD_OK:1; // XR0[2:2] - Read + + BYTE Caller_ID:1; // XR0[3:3] - Read + + BYTE RING:1; // XR0[4:4] - Read + + BYTE Cadence:1; // XR0[5:5] - Read + + BYTE Wake_up:1; // XR0[6:6] - Read + + BYTE unused:1; // XR0[7:7] - Read + + } bitreg; + } xr0; + + union // XOP - XR1 Register + { + BYTE reg; + struct _XR1_BITREGS { + BYTE M_SI_0:1; // XR1[0:0] + + BYTE M_SI_1:1; // XR1[1:1] + + BYTE M_VDD_OK:1; // XR1[2:2] + + BYTE M_Caller_ID:1; // XR1[3:3] + + BYTE M_RING:1; // XR1[4:4] + + BYTE M_Cadence:1; // XR1[5:5] + + BYTE M_Wake_up:1; // XR1[6:6] + + BYTE unused:1; // XR1[7:7] + + } bitreg; + } xr1; + + union // XOP - XR2 Register + { + BYTE reg; + struct _XR2_BITREGS { + BYTE CTO0:1; // XR2[0:0] + + BYTE CTO1:1; // XR2[1:1] + + BYTE CTO2:1; // XR2[2:2] + + BYTE CTO3:1; // XR2[3:3] + + BYTE CTO4:1; // XR2[4:4] + + BYTE CTO5:1; // XR2[5:5] + + BYTE CTO6:1; // XR2[6:6] + + BYTE CTO7:1; // XR2[7:7] + + } bitreg; + } xr2; + + union // XOP - XR3 Register + { + BYTE reg; + struct _XR3_BITREGS { + BYTE DCR0:1; // XR3[0:0] + + BYTE DCR1:1; // XR3[1:1] + + BYTE DCI:1; // XR3[2:2] + + BYTE DCU0:1; // XR3[3:3] + + BYTE DCU1:1; // XR3[4:4] + + BYTE B_off:1; // XR3[5:5] + + BYTE AGB0:1; // XR3[6:6] + + BYTE AGB1:1; // XR3[7:7] + + } bitreg; + } xr3; + + union // XOP - XR4 Register + { + BYTE reg; + struct _XR4_BITREGS { + BYTE C_0:1; // XR4[0:0] + + BYTE C_1:1; // XR4[1:1] + + BYTE C_2:1; // XR4[2:2] + + BYTE C_3:1; // XR4[3:3] + + BYTE C_4:1; // XR4[4:4] + + BYTE C_5:1; // XR4[5:5] + + BYTE C_6:1; // XR4[6:6] + + BYTE C_7:1; // XR4[7:7] + + } bitreg; + } xr4; + + union // XOP - XR5 Register + { + BYTE reg; + struct _XR5_BITREGS { + BYTE T_0:1; // XR5[0:0] + + BYTE T_1:1; // XR5[1:1] + + BYTE T_2:1; // XR5[2:2] + + BYTE T_3:1; // XR5[3:3] + + BYTE T_4:1; // XR5[4:4] + + BYTE T_5:1; // XR5[5:5] + + BYTE T_6:1; // XR5[6:6] + + BYTE T_7:1; // XR5[7:7] + + } bitreg; + } xr5; + + union // XOP - XR6 Register - Read Values + { + BYTE reg; + struct _XR6_BITREGS { + BYTE CPS0:1; // XR6[0:0] + + BYTE CPS1:1; // XR6[1:1] + + BYTE unused1:2; // XR6[2:3] + + BYTE CLK_OFF:1; // XR6[4:4] + + BYTE unused2:3; // XR6[5:7] + + } bitreg; + } xr6; + + union // XOP - XR7 Register + { + BYTE reg; + struct _XR7_BITREGS { + BYTE unused1:1; // XR7[0:0] + + BYTE Vdd0:1; // XR7[1:1] + + BYTE Vdd1:1; // XR7[2:2] + + BYTE unused2:5; // XR7[3:7] + + } bitreg; + } xr7; + } XOP; + + BYTE ByteRegs[sizeof(struct _XOP)]; + + } XOP_REGS; + + // DAA_REGS.XOP_REGS.XOP.XR7.reg + // DAA_REGS.XOP_REGS.XOP.XR7.bitreg + // DAA_REGS.XOP_REGS.XOP.XR7.bitreg.Vdd0 + // DAA_REGS.XOP_REGS.ByteRegs[7] + + //----------------------------------------------- + // COP Registers + // + BYTE byCOP; + + union _COP_REGS { + struct _COP { + BYTE THFilterCoeff_1[8]; // COP - TH Filter Coefficients, CODE=0, Part 1 + + BYTE THFilterCoeff_2[8]; // COP - TH Filter Coefficients, CODE=1, Part 2 + + BYTE THFilterCoeff_3[8]; // COP - TH Filter Coefficients, CODE=2, Part 3 + + BYTE RingerImpendance_1[8]; // COP - Ringer Impendance Coefficients, CODE=3, Part 1 + + BYTE IMFilterCoeff_1[8]; // COP - IM Filter Coefficients, CODE=4, Part 1 + + BYTE IMFilterCoeff_2[8]; // COP - IM Filter Coefficients, CODE=5, Part 2 + + BYTE RingerImpendance_2[8]; // COP - Ringer Impendance Coefficients, CODE=6, Part 2 + + BYTE FRRFilterCoeff[8]; // COP - FRR Filter Coefficients, CODE=7 + + BYTE FRXFilterCoeff[8]; // COP - FRX Filter Coefficients, CODE=8 + + BYTE ARFilterCoeff[4]; // COP - AR Filter Coefficients, CODE=9 + + BYTE AXFilterCoeff[4]; // COP - AX Filter Coefficients, CODE=10 + + BYTE Tone1Coeff[4]; // COP - Tone1 Coefficients, CODE=11 + + BYTE Tone2Coeff[4]; // COP - Tone2 Coefficients, CODE=12 + + BYTE LevelmeteringRinging[4]; // COP - Levelmetering Ringing, CODE=13 + + BYTE CallerID1stTone[8]; // COP - Caller ID 1st Tone, CODE=14 + + BYTE CallerID2ndTone[8]; // COP - Caller ID 2nd Tone, CODE=15 + + } COP; + + BYTE ByteRegs[sizeof(struct _COP)]; + + } COP_REGS; + + // DAA_REGS.COP_REGS.COP.XR7.Tone1Coeff[3] + // DAA_REGS.COP_REGS.COP.XR7.bitreg + // DAA_REGS.COP_REGS.COP.XR7.bitreg.Vdd0 + // DAA_REGS.COP_REGS.ByteRegs[57] + + //----------------------------------------------- + // CAO Registers + // + BYTE byCAO; + + union _CAO_REGS { + struct _CAO { + BYTE CallerID[512]; // CAO - Caller ID Bytes + + } CAO; + + BYTE ByteRegs[sizeof(struct _CAO)]; + } CAO_REGS; + + union // XOP - XR0 Register - Write values + { + BYTE reg; + struct _XR0_BITREGSW { + BYTE SO_0:1; // XR1[0:0] - Write + + BYTE SO_1:1; // XR1[1:1] - Write + + BYTE SO_2:1; // XR1[2:2] - Write + + BYTE unused:5; // XR1[3:7] - Write + + } bitreg; + } XOP_xr0_W; + + union // XOP - XR6 Register - Write values + { + BYTE reg; + struct _XR6_BITREGSW { + BYTE unused1:4; // XR6[0:3] + + BYTE CLK_OFF:1; // XR6[4:4] + + BYTE unused2:3; // XR6[5:7] + + } bitreg; + } XOP_xr6_W; + +} DAA_REGS; + +#define ALISDAA_ID_BYTE 0x81 +#define ALISDAA_CALLERID_SIZE 512 + +//------------------------------ +// +// Misc definitions +// + +// Power Up Operation +#define SOP_PU_SLEEP 0 +#define SOP_PU_RINGING 1 +#define SOP_PU_CONVERSATION 2 +#define SOP_PU_PULSEDIALING 3 + +#define ALISDAA_CALLERID_SIZE 512 + +#define PLAYBACK_MODE_COMPRESSED 0 // Selects: Compressed modes, TrueSpeech 8.5-4.1, G.723.1, G.722, G.728, G.729 +#define PLAYBACK_MODE_TRUESPEECH_V40 0 // Selects: TrueSpeech 8.5, 6.3, 5.3, 4.8 or 4.1 Kbps +#define PLAYBACK_MODE_TRUESPEECH 8 // Selects: TrueSpeech 8.5, 6.3, 5.3, 4.8 or 4.1 Kbps Version 5.1 +#define PLAYBACK_MODE_ULAW 2 // Selects: 64 Kbit/sec MuA-law PCM +#define PLAYBACK_MODE_ALAW 10 // Selects: 64 Kbit/sec A-law PCM +#define PLAYBACK_MODE_16LINEAR 6 // Selects: 128 Kbit/sec 16-bit linear +#define PLAYBACK_MODE_8LINEAR 4 // Selects: 64 Kbit/sec 8-bit signed linear +#define PLAYBACK_MODE_8LINEAR_WSS 5 // Selects: 64 Kbit/sec WSS 8-bit unsigned linear + +#define RECORD_MODE_COMPRESSED 0 // Selects: Compressed modes, TrueSpeech 8.5-4.1, G.723.1, G.722, G.728, G.729 +#define RECORD_MODE_TRUESPEECH 0 // Selects: TrueSpeech 8.5, 6.3, 5.3, 4.8 or 4.1 Kbps +#define RECORD_MODE_ULAW 4 // Selects: 64 Kbit/sec Mu-law PCM +#define RECORD_MODE_ALAW 12 // Selects: 64 Kbit/sec A-law PCM +#define RECORD_MODE_16LINEAR 5 // Selects: 128 Kbit/sec 16-bit linear +#define RECORD_MODE_8LINEAR 6 // Selects: 64 Kbit/sec 8-bit signed linear +#define RECORD_MODE_8LINEAR_WSS 7 // Selects: 64 Kbit/sec WSS 8-bit unsigned linear + +enum SLIC_STATES { + PLD_SLIC_STATE_OC = 0, + PLD_SLIC_STATE_RINGING, + PLD_SLIC_STATE_ACTIVE, + PLD_SLIC_STATE_OHT, + PLD_SLIC_STATE_TIPOPEN, + PLD_SLIC_STATE_STANDBY, + PLD_SLIC_STATE_APR, + PLD_SLIC_STATE_OHTPR +}; + +enum SCI_CONTROL { + SCI_End = 0, + SCI_Enable_DAA, + SCI_Enable_Mixer, + SCI_Enable_EEPROM +}; + +enum Mode { + T63, T53, T48, T40 +}; +enum Dir { + V3_TO_V4, V4_TO_V3, V4_TO_V5, V5_TO_V4 +}; + +typedef struct Proc_Info_Tag { + enum Mode convert_mode; + enum Dir convert_dir; + int Prev_Frame_Type; + int Current_Frame_Type; +} Proc_Info_Type; + +enum PREVAL { + NORMAL = 0, + NOPOST, + POSTONLY, + PREERROR +}; + +enum IXJ_EXTENSIONS { + G729LOADER = 0, + TS85LOADER, + PRE_READ, + POST_READ, + PRE_WRITE, + POST_WRITE, + PRE_IOCTL, + POST_IOCTL +}; + +typedef struct { + unsigned int busytone:1; + unsigned int dialtone:1; + unsigned int ringback:1; + unsigned int ringing:1; + unsigned int cringing:1; + unsigned int play_first_frame:1; + unsigned int pstn_present:1; + unsigned int pstn_ringing:1; + unsigned int pots_correct:1; + unsigned int pots_pstn:1; + unsigned int g729_loaded:1; + unsigned int ts85_loaded:1; + unsigned int dtmf_oob:1; // DTMF Out-Of-Band + +} IXJ_FLAGS; + +/****************************************************************************** +* +* This structure represents the Internet PhoneJACK and Internet LineJACK +* +******************************************************************************/ + +typedef struct { + struct phone_device p; + unsigned int board; + unsigned int DSPbase; + unsigned int XILINXbase; + unsigned int serial; + struct phone_capability caplist[30]; + unsigned int caps; + struct pnp_dev *dev; + unsigned int cardtype; + unsigned int rec_codec; + char rec_mode; + unsigned int play_codec; + char play_mode; + IXJ_FLAGS flags; + unsigned int rec_frame_size; + unsigned int play_frame_size; + int aec_level; + int readers, writers; + struct wait_queue *poll_q; + struct wait_queue *read_q; + char *read_buffer, *read_buffer_end; + char *read_convert_buffer; + unsigned int read_buffer_size; + unsigned int read_buffer_ready; + struct wait_queue *write_q; + char *write_buffer, *write_buffer_end; + char *write_convert_buffer; + unsigned int write_buffer_size; + unsigned int write_buffers_empty; + unsigned long drybuffer; + char *write_buffer_rp, *write_buffer_wp; + char dtmfbuffer[80]; + char dtmf_current; + int dtmf_wp, dtmf_rp, dtmf_state, dtmf_proc; + int tone_off_time, tone_on_time; + struct fasync_struct *async_queue; + unsigned long tone_start_jif; + char tone_index; + char tone_state; + char maxrings; + IXJ_CADENCE *cadence_t; + int tone_cadence_state; + DTMF dtmf; + CPTF cptf; + BYTES dsp; + BYTES ver; + BYTES scr; + BYTES ssr; + BYTES baseframe; + HSR hsr; + GPIO gpio; + PLD_SCRR pld_scrr; + PLD_SCRW pld_scrw; + PLD_SLICW pld_slicw; + PLD_SLICR pld_slicr; + PLD_CLOCK pld_clock; + MIX mix; + unsigned short ring_cadence; + int ring_cadence_t; + unsigned long ring_cadence_jif; + int intercom; + int m_hook; + int r_hook; + char pstn_envelope; + char pstn_cid_intr; + unsigned pstn_cid_recieved; + IXJ_CID cid; + unsigned long pstn_ring_start; + unsigned long pstn_winkstart; + unsigned int winktime; + char port; + union telephony_exception ex; + char daa_mode; + unsigned long pstn_sleeptil; + DAA_REGS m_DAAShadowRegs; + Proc_Info_Type Info_read; + Proc_Info_Type Info_write; + unsigned short frame_count; + unsigned int filter_cadence; + unsigned int filter_hist[4]; + unsigned short proc_load; + unsigned long framesread; + unsigned long frameswritten; + unsigned long read_wait; + unsigned long write_wait; + unsigned long timerchecks; + unsigned long txreadycheck; + unsigned long rxreadycheck; +} IXJ; + +typedef int (*IXJ_REGFUNC) (IXJ * j, unsigned long arg); + +int ixj_register(int index, IXJ_REGFUNC regfunc); +int ixj_unregister(int index); diff -u --recursive --new-file v2.2.13/linux/drivers/telephony/phonedev.c linux/drivers/telephony/phonedev.c --- v2.2.13/linux/drivers/telephony/phonedev.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/telephony/phonedev.c Tue Jan 4 10:12:22 2000 @@ -0,0 +1,167 @@ +/* + * Telephony registration for Linux + * + * (c) Copyright 1999 Red Hat Software Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Author: Alan Cox, + * + * Fixes: + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#define PHONE_NUM_DEVICES 256 + +/* + * Active devices + */ + +static struct phone_device *phone_device[PHONE_NUM_DEVICES]; + +/* + * Open a phone device. + */ + +static int phone_open(struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR(inode->i_rdev); + int err; + struct phone_device *p; + + if (minor >= PHONE_NUM_DEVICES) + return -ENODEV; + + p = phone_device[minor]; + if (p == NULL) { + char modname[32]; + + sprintf(modname, "char-major-%d-%d", PHONE_MAJOR, minor); + request_module(modname); + p = phone_device[minor]; + if (p == NULL) + return -ENODEV; + } + if (p->open) { + err = p->open(p, file); /* Tell the device it is open */ + if (err) + return err; + } + file->f_op = p->f_op; + return 0; +} + +/* + * Telephony For Linux device drivers request registration here. + */ + +int phone_register_device(struct phone_device *p, int unit) +{ + int base; + int end; + int i; + + base = 0; + end = PHONE_NUM_DEVICES - 1; + + if (unit != PHONE_UNIT_ANY) { + base = unit; + end = unit; + } + for (i = base; i < end; i++) { + if (phone_device[i] == NULL) { + phone_device[i] = p; + p->minor = i; + MOD_INC_USE_COUNT; + return 0; + } + } + return -ENFILE; +} + +/* + * Unregister an unused Telephony for linux device + */ + +void phone_unregister_device(struct phone_device *pfd) +{ + if (phone_device[pfd->minor] != pfd) + panic("phone: bad unregister"); + phone_device[pfd->minor] = NULL; + MOD_DEC_USE_COUNT; +} + + +static struct file_operations phone_fops = +{ + NULL, + NULL, + NULL, + NULL, /* readdir */ + NULL, + NULL, + NULL, + phone_open, + NULL, /* flush */ + NULL +}; + +/* + * Board init functions + */ + +extern int ixj_init(void); + +/* + * Initialise Telephony for linux + */ + +int telephony_init(void) +{ + printk(KERN_INFO "Linux telephony interface: v1.00\n"); + if (register_chrdev(PHONE_MAJOR, "telephony", &phone_fops)) { + printk("phonedev: unable to get major %d\n", PHONE_MAJOR); + return -EIO; + } + /* + * Init kernel installed drivers + */ +#ifdef CONFIG_PHONE_IXJ + ixj_init(); +#endif + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + return telephony_init(); +} + +void cleanup_module(void) +{ + unregister_chrdev(PHONE_MAJOR, "telephony"); +} + +#endif + +EXPORT_SYMBOL(phone_register_device); +EXPORT_SYMBOL(phone_unregister_device); diff -u --recursive --new-file v2.2.13/linux/drivers/video/Config.in linux/drivers/video/Config.in --- v2.2.13/linux/drivers/video/Config.in Tue Oct 19 17:10:38 1999 +++ linux/drivers/video/Config.in Tue Jan 4 10:12:22 2000 @@ -57,7 +57,6 @@ bool 'Apple "control" display support' CONFIG_FB_CONTROL bool 'Apple "platinum" display support' CONFIG_FB_PLATINUM bool 'Apple "valkyrie" display support' CONFIG_FB_VALKYRIE - bool 'ATI Mach64 display support' CONFIG_FB_ATY bool 'IMS Twin Turbo display support' CONFIG_FB_IMSTT bool 'Chips 65550 display support' CONFIG_FB_CT65550 bool 'S3 Trio display support' CONFIG_FB_S3TRIO diff -u --recursive --new-file v2.2.13/linux/drivers/video/atyfb.c linux/drivers/video/atyfb.c --- v2.2.13/linux/drivers/video/atyfb.c Tue Jan 4 11:10:39 2000 +++ linux/drivers/video/atyfb.c Tue Jan 4 10:12:22 2000 @@ -1,4 +1,4 @@ -/* $Id: atyfb.c,v 1.106.2.4 1999/09/02 06:34:46 paulus Exp $ +/* $Id: atyfb.c,v 1.106.2.6 1999/10/14 08:44:47 davem Exp $ * linux/drivers/video/atyfb.c -- Frame buffer device for ATI Mach64 * * Copyright (C) 1997-1998 Geert Uytterhoeven @@ -89,7 +89,6 @@ */ #undef DEBUG - #define GUI_RESERVE 0x00001000 @@ -227,8 +226,8 @@ } fbcon_cmap; u8 blitter_may_be_busy; #ifdef __sparc__ - u8 open; u8 mmaped; + int open; int vtconsole; int consolecnt; #endif @@ -1896,10 +1895,8 @@ struct fb_info_aty *fb = (struct fb_info_aty *)info; if (user) { - if (fb->open) - return -EBUSY; + fb->open++; fb->mmaped = 0; - fb->open = 1; fb->vtconsole = -1; } else { fb->consolecnt++; @@ -1909,17 +1906,54 @@ return(0); } +struct fb_var_screeninfo default_var = { + /* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */ + 640, 480, 640, 480, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, 39722, 48, 16, 33, 10, 96, 2, + 0, FB_VMODE_NONINTERLACED +}; + static int atyfb_release(struct fb_info *info, int user) { #ifdef __sparc__ struct fb_info_aty *fb = (struct fb_info_aty *)info; if (user) { - if (fb->vtconsole != -1) - vt_cons[fb->vtconsole]->vc_mode = KD_TEXT; - fb->open = 0; - fb->mmaped = 0; - fb->vtconsole = -1; + fb->open--; + udelay(1000); + wait_for_idle(fb); + if (!fb->open) { + int was_mmaped = fb->mmaped; + + fb->mmaped = 0; + if (fb->vtconsole != -1) + vt_cons[fb->vtconsole]->vc_mode = KD_TEXT; + fb->vtconsole = -1; + + if (was_mmaped) { + struct fb_var_screeninfo var; + + /* Now reset the default display config, we have no + * idea what the program(s) which mmap'd the chip did + * to the configuration, nor whether it restored it + * correctly. + */ + var = default_var; + if (noaccel) + var.accel_flags &= ~FB_ACCELF_TEXT; + else + var.accel_flags |= FB_ACCELF_TEXT; + if (var.yres == var.yres_virtual) { + u32 vram = (fb->total_vram - (PAGE_SIZE << 2)); + var.yres_virtual = ((vram * 8) / var.bits_per_pixel) / + var.xres_virtual; + if (var.yres_virtual < var.yres) + var.yres_virtual = var.yres; + } + atyfb_set_var(&var, -1, &fb->fb_info); + } + } } else { fb->consolecnt--; } @@ -1982,15 +2016,6 @@ } -struct fb_var_screeninfo default_var = { - /* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */ - 640, 480, 640, 480, 0, 0, 8, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 39722, 48, 16, 33, 10, 96, 2, - 0, FB_VMODE_NONINTERLACED -}; - - /* * Get the Fixed Part of the Display */ @@ -3909,47 +3934,65 @@ { struct fb_info_aty *info; unsigned int pm; - + for (info = first_display; info != NULL; info = info->next) { struct fb_fix_screeninfo fix; int nb; - + atyfb_get_fix(&fix, fg_console, (struct fb_info *)info); nb = fb_display[fg_console].var.yres * fix.line_length; switch (when) { + case PBOOK_SLEEP_REQUEST: + info->save_framebuffer = vmalloc(nb); + break; + case PBOOK_SLEEP_REJECT: + if (info->save_framebuffer) { + vfree(info->save_framebuffer); + info->save_framebuffer = 0; + } + break; case PBOOK_SLEEP_NOW: + if (info->blitter_may_be_busy) + wait_for_idle(info); /* Stop accel engine (stop bus mastering) */ if (info->current_par.accel_flags & FB_ACCELF_TEXT) reset_engine(info); -#if 1 + /* Backup fb content */ - info->save_framebuffer = vmalloc(nb); if (info->save_framebuffer) memcpy(info->save_framebuffer, (void *)info->frame_buffer, nb); -#endif - /* Blank display and LCD */ - atyfbcon_blank(VESA_POWERDOWN+1, (struct fb_info *)info); - - /* Set chip to "suspend" mode. Note: There's an HW bug in the - chip which prevents proper resync on wakeup with automatic - power management, we handle suspend manually using the - following (weird) sequence described by ATI. Note2: + + /* Blank display and LCD */ + atyfbcon_blank(VESA_POWERDOWN+1, (struct fb_info *)info); + + /* Set chip to "suspend" mode. Note: There's a HW bug + in the chip which prevents proper resync on wakeup + with automatic power management, we handle suspend + manually using the following (weird) sequence + described by ATI. + Note2: We could enable this for all Rage LT Pro chip ids */ - if ((Gx == LG_CHIP_ID) || (Gx == LT_CHIP_ID) || (Gx == LP_CHIP_ID)) { + if ((Gx == LG_CHIP_ID) || (Gx == LT_CHIP_ID) + || (Gx == LP_CHIP_ID)) { pm = aty_ld_le32(POWER_MANAGEMENT, info); pm &= ~PWR_MGT_ON; aty_st_le32(POWER_MANAGEMENT, pm, info); pm = aty_ld_le32(POWER_MANAGEMENT, info); + mdelay(1); pm &= ~(PWR_BLON | AUTO_PWR_UP); pm |= SUSPEND_NOW; aty_st_le32(POWER_MANAGEMENT, pm, info); pm = aty_ld_le32(POWER_MANAGEMENT, info); + mdelay(1); pm |= PWR_MGT_ON; aty_st_le32(POWER_MANAGEMENT, pm, info); do { pm = aty_ld_le32(POWER_MANAGEMENT, info); + /* Fix a problem with revision 4c50 of the chip */ + if (Gx == LP_CHIP_ID) + break; } while ((pm & PWR_MGT_STATUS_MASK) != PWR_MGT_STATUS_SUSPEND); mdelay(500); } @@ -3961,18 +4004,23 @@ pm &= ~PWR_MGT_ON; aty_st_le32(POWER_MANAGEMENT, pm, info); pm = aty_ld_le32(POWER_MANAGEMENT, info); + mdelay(1); pm |= (PWR_BLON | AUTO_PWR_UP); pm &= ~SUSPEND_NOW; aty_st_le32(POWER_MANAGEMENT, pm, info); pm = aty_ld_le32(POWER_MANAGEMENT, info); + mdelay(1); pm |= PWR_MGT_ON; aty_st_le32(POWER_MANAGEMENT, pm, info); do { pm = aty_ld_le32(POWER_MANAGEMENT, info); + /* Fix a problem with revision 4c50 of the chip */ + if (Gx == LP_CHIP_ID) + break; } while ((pm & PWR_MGT_STATUS_MASK) != 0); mdelay(500); } -#if 1 + /* Restore fb content */ if (info->save_framebuffer) { memcpy((void *)info->frame_buffer, @@ -3980,7 +4028,7 @@ vfree(info->save_framebuffer); info->save_framebuffer = 0; } -#endif + /* Restore display */ atyfb_set_par(&info->current_par, info); atyfbcon_blank(0, (struct fb_info *)info); diff -u --recursive --new-file v2.2.13/linux/drivers/video/bwtwofb.c linux/drivers/video/bwtwofb.c --- v2.2.13/linux/drivers/video/bwtwofb.c Tue Jan 4 11:10:39 2000 +++ linux/drivers/video/bwtwofb.c Tue Jan 4 10:12:22 2000 @@ -1,4 +1,4 @@ -/* $Id: bwtwofb.c,v 1.7.2.1 1999/08/26 05:21:13 shadow Exp $ +/* $Id: bwtwofb.c,v 1.7.2.3 1999/10/06 10:52:36 anton Exp $ * bwtwofb.c: BWtwo frame buffer driver * * Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz) @@ -224,7 +224,9 @@ #endif fb->margins = bw2_margins; +#ifndef CONFIG_SUN4 fb->physbase = __get_phys(fb->sbdp->sbus_vaddrs[0]); +#endif fb->mmap_map = bw2_mmap_map; #ifdef __sparc_v9__ diff -u --recursive --new-file v2.2.13/linux/drivers/video/cgfourteenfb.c linux/drivers/video/cgfourteenfb.c --- v2.2.13/linux/drivers/video/cgfourteenfb.c Tue Jan 4 11:10:39 2000 +++ linux/drivers/video/cgfourteenfb.c Tue Jan 4 10:12:22 2000 @@ -1,4 +1,4 @@ -/* $Id: cgfourteenfb.c,v 1.4 1999/01/26 10:55:03 jj Exp $ +/* $Id: cgfourteenfb.c,v 1.4.2.1 1999/09/28 15:59:58 davem Exp $ * cgfourteenfb.c: CGfourteen frame buffer driver * * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz) diff -u --recursive --new-file v2.2.13/linux/drivers/video/cgsixfb.c linux/drivers/video/cgsixfb.c --- v2.2.13/linux/drivers/video/cgsixfb.c Tue Jan 4 11:10:39 2000 +++ linux/drivers/video/cgsixfb.c Tue Jan 4 10:12:22 2000 @@ -1,4 +1,4 @@ -/* $Id: cgsixfb.c,v 1.16.2.1 1999/05/25 00:59:35 davem Exp $ +/* $Id: cgsixfb.c,v 1.16.2.2 1999/09/28 15:59:55 davem Exp $ * cgsixfb.c: CGsix (GX,GXplus) frame buffer driver * * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz) diff -u --recursive --new-file v2.2.13/linux/drivers/video/cgthreefb.c linux/drivers/video/cgthreefb.c --- v2.2.13/linux/drivers/video/cgthreefb.c Tue Jan 4 11:10:39 2000 +++ linux/drivers/video/cgthreefb.c Tue Jan 4 10:12:22 2000 @@ -1,4 +1,4 @@ -/* $Id: cgthreefb.c,v 1.4 1999/01/26 10:55:01 jj Exp $ +/* $Id: cgthreefb.c,v 1.4.2.1 1999/09/28 16:00:08 davem Exp $ * cgthreefb.c: CGthree frame buffer driver * * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz) diff -u --recursive --new-file v2.2.13/linux/drivers/video/chipsfb.c linux/drivers/video/chipsfb.c --- v2.2.13/linux/drivers/video/chipsfb.c Tue Jan 4 11:10:39 2000 +++ linux/drivers/video/chipsfb.c Tue Jan 4 10:12:22 2000 @@ -726,9 +726,18 @@ int nb = p->var.yres * p->fix.line_length; switch (when) { + case PBOOK_SLEEP_REQUEST: + p->save_framebuffer = vmalloc(nb); + break; + case PBOOK_SLEEP_REJECT: + if (p->save_framebuffer) { + vfree(p->save_framebuffer); + p->save_framebuffer = 0; + } + break; + case PBOOK_SLEEP_NOW: chipsfb_blank(1, (struct fb_info *)p); - p->save_framebuffer = vmalloc(nb); if (p->save_framebuffer) memcpy(p->save_framebuffer, p->frame_buffer, nb); diff -u --recursive --new-file v2.2.13/linux/drivers/video/creatorfb.c linux/drivers/video/creatorfb.c --- v2.2.13/linux/drivers/video/creatorfb.c Tue Jan 4 11:10:39 2000 +++ linux/drivers/video/creatorfb.c Tue Jan 4 10:12:22 2000 @@ -1,4 +1,4 @@ -/* $Id: creatorfb.c,v 1.27 1999/03/28 12:37:12 jj Exp $ +/* $Id: creatorfb.c,v 1.27.2.2 1999/11/05 09:07:56 davem Exp $ * creatorfb.c: Creator/Creator3D frame buffer driver * * Copyright (C) 1997,1998,1999 Jakub Jelinek (jj@ultra.linux.cz) @@ -539,6 +539,36 @@ /* Not used if hw cursor */ } +#if 0 +static void ffb_blank(struct fb_info_sbusfb *fb) +{ + struct ffb_dac *dac = fb->s.ffb.dac; + unsigned long flags; + u32 tmp; + + spin_lock_irqsave(&fb->lock, flags); + dac->type = 0x6000; + tmp = (dac->value & ~0x1); + dac->type = 0x6000; + dac->value = tmp; + spin_unlock_irqrestore(&fb->lock, flags); +} +#endif + +static void ffb_unblank(struct fb_info_sbusfb *fb) +{ + struct ffb_dac *dac = fb->s.ffb.dac; + unsigned long flags; + u32 tmp; + + spin_lock_irqsave(&fb->lock, flags); + dac->type = 0x6000; + tmp = (dac->value | 0x1); + dac->type = 0x6000; + dac->value = tmp; + spin_unlock_irqrestore(&fb->lock, flags); +} + static void ffb_loadcmap (struct fb_info_sbusfb *fb, struct display *p, int index, int count) { struct ffb_dac *dac = fb->s.ffb.dac; @@ -726,6 +756,16 @@ fb->setcurshape = ffb_setcurshape; fb->switch_from_graph = ffb_switch_from_graph; fb->fill = ffb_fill; +#if 0 + /* XXX Can't enable this for now, I've seen cases + * XXX where the VC was blanked, and Xsun24 was started + * XXX via a remote login, the sunfb code did not + * XXX unblank creator when it was mmap'd for some + * XXX reason, investigate later... -DaveM + */ + fb->blank = ffb_blank; + fb->unblank = ffb_unblank; +#endif /* If there are any read errors or fifo overflow conditions, * clear them now. @@ -755,5 +795,12 @@ if (afb) fb->s.ffb.dac_rev = 10; + /* Unblank it just to be sure. When there are multiple + * FFB/AFB cards in the system, or it is not the OBP + * chosen console, it will have video outputs off in + * the DAC. + */ + ffb_unblank(fb); + return idstring; } diff -u --recursive --new-file v2.2.13/linux/drivers/video/fbmem.c linux/drivers/video/fbmem.c --- v2.2.13/linux/drivers/video/fbmem.c Mon Aug 9 16:05:57 1999 +++ linux/drivers/video/fbmem.c Tue Jan 4 10:12:22 2000 @@ -217,7 +217,7 @@ static int PROC_CONSOLE(struct fb_info *info) { int fgc; - + if (info->display_fg != NULL) fgc = info->display_fg->vc_num; else @@ -353,6 +353,20 @@ /* tell console var has changed */ if (newfb->changevar) newfb->changevar(unit); + /* fixme: this really needs lock protection */ + conp->vc_display_fg = &newfb->display_fg; + if (!newfb->display_fg) + newfb->display_fg = conp; + if (oldfb != newfb && oldfb->display_fg == conp) { + int i; + for (i = 0; i < MAX_NR_CONSOLES; i++) + if (fb_display[i].conp && con2fb_map[i] == oldidx) { + oldfb->display_fg = fb_display[i].conp; + break; + } + if (i == MAX_NR_CONSOLES) + oldfb->display_fg = NULL; + } } } @@ -447,6 +461,11 @@ for (i = 0; i < MAX_NR_CONSOLES; i++) set_con2fb_map(i, con2fb.framebuffer); return 0; + case FBIOBLANK: + if (info->blank == 0) + return -EINVAL; + (*info->blank)(arg, info); + return 0; default: return fb->fb_ioctl(inode, file, cmd, arg, PROC_CONSOLE(info), info); @@ -468,12 +487,16 @@ return -ENODEV; if (fb->fb_mmap) return fb->fb_mmap(info, file, vma); +#if defined(__sparc__) && !defined(__sparc_v9__) + /* Should never get here, all fb drivers should have their own + mmap routines */ + return -EINVAL; +#else fb->fb_get_fix(&fix, PROC_CONSOLE(info), info); /* frame buffer memory */ start = (unsigned long)fix.smem_start; len = (start & ~PAGE_MASK)+fix.smem_len; - start &= PAGE_MASK; len = (len+~PAGE_MASK) & PAGE_MASK; if (vma->vm_offset >= len) { /* memory mapped io */ @@ -483,14 +506,37 @@ return -EINVAL; start = (unsigned long)fix.mmio_start; len = (start & ~PAGE_MASK)+fix.mmio_len; - start &= PAGE_MASK; len = (len+~PAGE_MASK) & PAGE_MASK; } + start &= PAGE_MASK; if ((vma->vm_end - vma->vm_start + vma->vm_offset) > len) return -EINVAL; vma->vm_offset += start; if (vma->vm_offset & ~PAGE_MASK) return -ENXIO; +#if defined(__sparc_v9__) + vma->vm_flags |= (VM_SHM | VM_LOCKED); + { + unsigned long align, j; + for (align = 0x400000; align > PAGE_SIZE; align >>= 3) + if (len >= align && !((start & ~PAGE_MASK) & (align - 1))) + break; + if (align > PAGE_SIZE && vma->vm_start & (align - 1)) { + /* align as much as possible */ + struct vm_area_struct *vmm; + j = (-vma->vm_start) & (align - 1); + vmm = find_vma(current->mm, vma->vm_start); + if (!vmm || vmm->vm_start >= vma->vm_end + j) { + vma->vm_start += j; + vma->vm_end += j; + } + } + } + if (io_remap_page_range(vma->vm_start, vma->vm_offset, + vma->vm_end - vma->vm_start, vma->vm_page_prot, 0)) + return -EAGAIN; + vma->vm_flags |= VM_IO; +#else #if defined(__mc68000__) if (CPU_IS_020_OR_030) pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE030; @@ -503,9 +549,6 @@ pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE|_PAGE_GUARDED; #elif defined(__alpha__) /* Caching is off in the I/O space quadrant by design. */ -#elif defined(__sparc__) - /* Should never get here, all fb drivers should have their own - mmap routines */ #elif defined(__i386__) if (boot_cpu_data.x86 > 3) pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; @@ -525,7 +568,9 @@ if (remap_page_range(vma->vm_start, vma->vm_offset, vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; +#endif /* !__sparc_v9__ */ return 0; +#endif /* ! sparc32 */ } static int diff -u --recursive --new-file v2.2.13/linux/drivers/video/leofb.c linux/drivers/video/leofb.c --- v2.2.13/linux/drivers/video/leofb.c Tue Jan 4 11:10:39 2000 +++ linux/drivers/video/leofb.c Tue Jan 4 10:12:22 2000 @@ -1,4 +1,4 @@ -/* $Id: leofb.c,v 1.6 1999/04/01 13:03:25 jj Exp $ +/* $Id: leofb.c,v 1.6.2.1 1999/09/28 16:00:03 davem Exp $ * leofb.c: Leo (ZX) 24/8bit frame buffer driver * * Copyright (C) 1996-1999 Jakub Jelinek (jj@ultra.linux.cz) diff -u --recursive --new-file v2.2.13/linux/drivers/video/matroxfb.c linux/drivers/video/matroxfb.c --- v2.2.13/linux/drivers/video/matroxfb.c Thu Apr 29 12:53:48 1999 +++ linux/drivers/video/matroxfb.c Tue Jan 4 10:12:22 2000 @@ -1,10 +1,10 @@ /* * - * Hardware accelerated Matrox Millennium I, II, Mystique and G200 + * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200 and G400 * * (c) 1998,1999 Petr Vandrovec * - * Version: 1.15 1999/04/19 + * Version: 1.19a 1999/08/12 (2.2.x branch) * * MTRR stuff: 1998 Tom Rini * @@ -14,8 +14,8 @@ * "Kurt Garloff" * Betatesting, fixes, ideas, videomodes, videomodes timmings * - * "Tom Rini" - * MTRR stuff, betatesting, fixes, ideas + * "Tom Rini" + * MTRR stuff, PPC cleanups, betatesting, fixes, ideas * * "Bibek Sahu" * Access device through readb|w|l and write b|w|l @@ -60,6 +60,12 @@ * "Cort Dougan" * CHRP fixes and PReP cleanup * + * "Mark Vojkovich" + * G400 support + * + * "Samuel Hocevar" + * Fixes + * * (following author is not in any relation with this code, but his code * is included in this driver) * @@ -87,6 +93,11 @@ /* Debug register calls, too? */ #undef MATROXFB_DEBUG_REG +/* Log reentrancy attempts - you must have printstate() patch applied */ +#undef MATROXFB_DEBUG_REENTER +/* you must define DEBUG_REENTER to get debugged CONSOLEBH... */ +#undef MATROXFB_DEBUG_CONSOLEBH + #include #include #include @@ -127,12 +138,15 @@ #include