diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/CREDITS linux.14p4/CREDITS --- linux.vanilla/CREDITS Wed Oct 20 01:12:32 1999 +++ linux.14p4/CREDITS Fri Oct 29 23:39:48 1999 @@ -789,12 +789,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 @@ -1713,8 +1713,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 @@ -2122,9 +2122,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 --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/Changes linux.14p4/Documentation/Changes --- linux.vanilla/Documentation/Changes Sat Aug 14 02:27:12 1999 +++ linux.14p4/Documentation/Changes Fri Oct 22 22:44:33 1999 @@ -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,19 @@ - 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 Upgrade notes ************* @@ -226,7 +226,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. @@ -284,8 +284,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 =================== @@ -519,23 +519,15 @@ 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 +539,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 +586,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 +613,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 +633,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 +648,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 +695,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 +708,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 +727,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 +742,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,9 +780,9 @@ 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 ===== diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/Configure.help linux.14p4/Documentation/Configure.help --- linux.vanilla/Documentation/Configure.help Wed Oct 20 01:12:32 1999 +++ linux.14p4/Documentation/Configure.help Thu Nov 4 23:48:51 1999 @@ -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 @@ -5591,6 +5575,77 @@ you say N, the PPP support will not be included in the driver (saves about 16 KB of kernel memory). +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 Ethernet (also called IEEE 802.3 or ISO 8802-2) is the most common @@ -8884,9 +8939,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 +8960,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 @@ -9422,20 +9481,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 +9760,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 diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/kernel-docs.txt linux.14p4/Documentation/kernel-docs.txt --- linux.vanilla/Documentation/kernel-docs.txt Sat Aug 14 02:26:08 1999 +++ linux.14p4/Documentation/kernel-docs.txt Wed Oct 27 18:53:51 1999 @@ -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,411 @@ 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.leo.org/pub/comp/os/unix/linux/sunsite/docs/linux-doc-p + roject/module-programming-guide/index.html + Keywords: modules, free book, /proc, ioctls, system calls, + interrupt handlers . + Description: Very nice 92 pages free 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://lwn.net + 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 Wed Oct 27 17:14:03 CEST 1999 diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/kernel-parameters.txt linux.14p4/Documentation/kernel-parameters.txt --- linux.vanilla/Documentation/kernel-parameters.txt Sat Aug 14 02:26:45 1999 +++ linux.14p4/Documentation/kernel-parameters.txt Fri Oct 29 23:39:48 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/networking/comx.txt linux.14p4/Documentation/networking/comx.txt --- linux.vanilla/Documentation/networking/comx.txt Thu Jan 1 01:00:00 1970 +++ linux.14p4/Documentation/networking/comx.txt Wed Nov 3 00:18:55 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/proc.txt linux.14p4/Documentation/proc.txt --- linux.vanilla/Documentation/proc.txt Sat Feb 6 20:46:20 1999 +++ linux.14p4/Documentation/proc.txt Fri Oct 29 23:36:11 1999 @@ -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, diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/sound/Introduction linux.14p4/Documentation/sound/Introduction --- linux.vanilla/Documentation/sound/Introduction Sat Aug 14 02:27:12 1999 +++ linux.14p4/Documentation/sound/Introduction Sun Oct 31 23:00:45 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/Documentation/sound/PSS linux.14p4/Documentation/sound/PSS --- linux.vanilla/Documentation/sound/PSS Thu Jan 1 01:00:00 1970 +++ linux.14p4/Documentation/sound/PSS Sun Oct 31 23:02:27 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/MAINTAINERS linux.14p4/MAINTAINERS --- linux.vanilla/MAINTAINERS Wed Oct 20 01:12:33 1999 +++ linux.14p4/MAINTAINERS Wed Nov 3 00:25:58 1999 @@ -121,9 +121,10 @@ APM DRIVER P: Stephen Rothwell -M: Stephen.Rothwell@canb.auug.org.au +M: sfr@linuxcare.com L: linux-laptop@vger.rutgers.edu -S: Maintained +W: http://www.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 @@ -573,7 +579,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 +591,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 diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/Makefile linux.14p4/Makefile --- linux.vanilla/Makefile Wed Oct 20 01:12:33 1999 +++ linux.14p4/Makefile Wed Nov 3 00:49:50 1999 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 2 -SUBLEVEL = 13 -EXTRAVERSION = +SUBLEVEL = 14 +EXTRAVERSION = pre4 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) @@ -339,7 +339,7 @@ if [ -f FC4_MODULES ]; then inst_mod FC4_MODULES fc4; fi; \ if [ -f IRDA_MODULES ]; then inst_mod IRDA_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 +381,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 --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/alpha/kernel/process.c linux.14p4/arch/alpha/kernel/process.c --- linux.vanilla/arch/alpha/kernel/process.c Sat Aug 28 20:00:53 1999 +++ linux.14p4/arch/alpha/kernel/process.c Fri Oct 22 21:20:22 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/alpha/kernel/proto.h linux.14p4/arch/alpha/kernel/proto.h --- linux.vanilla/arch/alpha/kernel/proto.h Sat Aug 14 02:27:12 1999 +++ linux.14p4/arch/alpha/kernel/proto.h Fri Oct 22 21:20:22 1999 @@ -153,6 +153,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 --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/alpha/kernel/ptrace.c linux.14p4/arch/alpha/kernel/ptrace.c --- linux.vanilla/arch/alpha/kernel/ptrace.c Wed Dec 30 00:17:00 1998 +++ linux.14p4/arch/alpha/kernel/ptrace.c Sat Oct 23 23:02:34 1999 @@ -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, current); + 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, current); + 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, current); + 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, current); + 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, current); + 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, current); + 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, current); + return; } /* This is a hack for non-kernel-mapped video buffers and similar. */ diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/alpha/kernel/signal.c linux.14p4/arch/alpha/kernel/signal.c --- linux.vanilla/arch/alpha/kernel/signal.c Sat Aug 14 02:27:12 1999 +++ linux.14p4/arch/alpha/kernel/signal.c Sat Oct 23 23:02:34 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/alpha/math-emu/fp-emul.c linux.14p4/arch/alpha/math-emu/fp-emul.c --- linux.vanilla/arch/alpha/math-emu/fp-emul.c Sat Aug 28 20:00:53 1999 +++ linux.14p4/arch/alpha/math-emu/fp-emul.c Fri Oct 22 22:30:50 1999 @@ -85,15 +85,15 @@ /* For 128-bit division. */ -__complex__ unsigned long +void udiv128(unsigned long divisor_f0, unsigned long divisor_f1, - unsigned long dividend_f0, unsigned long dividend_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; - __complex__ unsigned long ret; _FP_FRAC_SET_2(rem, _FP_ZEROFRAC_2); _FP_FRAC_SET_2(quo, _FP_ZEROFRAC_2); @@ -139,9 +139,9 @@ } out: - __real__ ret = quo_f1; - __imag__ ret = rem_f1; - return ret; + *quot = quo_f1; + *remd = rem_f1; + return; } /* diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/alpha/math-emu/sfp-machine.h linux.14p4/arch/alpha/math-emu/sfp-machine.h --- linux.vanilla/arch/alpha/math-emu/sfp-machine.h Sat Aug 28 20:00:53 1999 +++ linux.14p4/arch/alpha/math-emu/sfp-machine.h Fri Oct 22 22:30:50 1999 @@ -552,15 +552,17 @@ : "r" ((UDItype)(u)), \ "r" ((UDItype)(v))) -extern __complex__ unsigned long udiv128(unsigned long, unsigned long, - unsigned long, unsigned long); +extern void udiv128(unsigned long, unsigned long, + 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_; \ +#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 diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/alpha/mm/fault.c linux.14p4/arch/alpha/mm/fault.c --- linux.vanilla/arch/alpha/mm/fault.c Sat Aug 14 02:27:13 1999 +++ linux.14p4/arch/alpha/mm/fault.c Sat Oct 23 23:02:34 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/boot/compressed/Makefile linux.14p4/arch/i386/boot/compressed/Makefile --- linux.vanilla/arch/i386/boot/compressed/Makefile Wed Oct 20 01:12:33 1999 +++ linux.14p4/arch/i386/boot/compressed/Makefile Fri Oct 22 22:27:46 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/boot/compressed/misc.c linux.14p4/arch/i386/boot/compressed/misc.c --- linux.vanilla/arch/i386/boot/compressed/misc.c Wed Jun 24 22:30:08 1998 +++ linux.14p4/arch/i386/boot/compressed/misc.c Fri Oct 22 22:27:14 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/kernel/apm.c linux.14p4/arch/i386/kernel/apm.c --- linux.vanilla/arch/i386/kernel/apm.c Wed Oct 20 01:12:33 1999 +++ linux.14p4/arch/i386/kernel/apm.c Fri Oct 29 23:39:48 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/kernel/bios32.c linux.14p4/arch/i386/kernel/bios32.c --- linux.vanilla/arch/i386/kernel/bios32.c Sat Aug 14 02:25:55 1999 +++ linux.14p4/arch/i386/kernel/bios32.c Wed Oct 27 18:53:06 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/kernel/io_apic.c linux.14p4/arch/i386/kernel/io_apic.c --- linux.vanilla/arch/i386/kernel/io_apic.c Wed Oct 20 01:12:33 1999 +++ linux.14p4/arch/i386/kernel/io_apic.c Fri Oct 22 21:21:27 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/kernel/irq.h linux.14p4/arch/i386/kernel/irq.h --- linux.vanilla/arch/i386/kernel/irq.h Sat Aug 14 02:27:13 1999 +++ linux.14p4/arch/i386/kernel/irq.h Fri Oct 22 23:30:31 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/kernel/ptrace.c linux.14p4/arch/i386/kernel/ptrace.c --- linux.vanilla/arch/i386/kernel/ptrace.c Wed Mar 24 21:18:46 1999 +++ linux.14p4/arch/i386/kernel/ptrace.c Sat Oct 23 23:02:34 1999 @@ -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, current); + 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, current); + 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, current); + 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, current); + 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, current); + 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, current); + 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, current); + return; } /* this is a hack for non-kernel-mapped video buffers and similar */ if (MAP_NR(page) < max_mapnr) diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/kernel/setup.c linux.14p4/arch/i386/kernel/setup.c --- linux.vanilla/arch/i386/kernel/setup.c Wed Oct 20 01:12:33 1999 +++ linux.14p4/arch/i386/kernel/setup.c Fri Oct 29 23:41:43 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/kernel/signal.c linux.14p4/arch/i386/kernel/signal.c --- linux.vanilla/arch/i386/kernel/signal.c Sat Aug 14 02:26:46 1999 +++ linux.14p4/arch/i386/kernel/signal.c Sat Oct 23 23:02:34 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/kernel/smp.c linux.14p4/arch/i386/kernel/smp.c --- linux.vanilla/arch/i386/kernel/smp.c Wed Oct 20 01:12:33 1999 +++ linux.14p4/arch/i386/kernel/smp.c Fri Oct 22 21:23:03 1999 @@ -128,6 +128,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 +369,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 +403,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; } @@ -772,18 +772,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 --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/math-emu/README linux.14p4/arch/i386/math-emu/README --- linux.vanilla/arch/i386/math-emu/README Wed Jun 24 22:30:08 1998 +++ linux.14p4/arch/i386/math-emu/README Thu Nov 4 23:43:12 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/math-emu/fpu_trig.c linux.14p4/arch/i386/math-emu/fpu_trig.c --- linux.vanilla/arch/i386/math-emu/fpu_trig.c Wed Dec 10 01:57:09 1997 +++ linux.14p4/arch/i386/math-emu/fpu_trig.c Thu Nov 4 23:43:12 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/math-emu/poly.h linux.14p4/arch/i386/math-emu/poly.h --- linux.vanilla/arch/i386/math-emu/poly.h Tue Apr 7 15:48:54 1998 +++ linux.14p4/arch/i386/math-emu/poly.h Thu Nov 4 23:43:12 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/math-emu/poly_sin.c linux.14p4/arch/i386/math-emu/poly_sin.c --- linux.vanilla/arch/i386/math-emu/poly_sin.c Wed Dec 10 01:57:09 1997 +++ linux.14p4/arch/i386/math-emu/poly_sin.c Thu Nov 4 23:43:12 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/math-emu/poly_tan.c linux.14p4/arch/i386/math-emu/poly_tan.c --- linux.vanilla/arch/i386/math-emu/poly_tan.c Wed Dec 10 01:57:09 1997 +++ linux.14p4/arch/i386/math-emu/poly_tan.c Thu Nov 4 23:43:12 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/math-emu/reg_round.S linux.14p4/arch/i386/math-emu/reg_round.S --- linux.vanilla/arch/i386/math-emu/reg_round.S Wed Dec 10 01:57:09 1997 +++ linux.14p4/arch/i386/math-emu/reg_round.S Thu Nov 4 23:43:12 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/i386/mm/fault.c linux.14p4/arch/i386/mm/fault.c --- linux.vanilla/arch/i386/mm/fault.c Sun Nov 22 17:38:19 1998 +++ linux.14p4/arch/i386/mm/fault.c Sat Oct 23 23:02:34 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/common_defconfig linux.14p4/arch/ppc/common_defconfig --- linux.vanilla/arch/ppc/common_defconfig Sat Aug 14 02:27:15 1999 +++ linux.14p4/arch/ppc/common_defconfig Fri Oct 22 22:26:41 1999 @@ -91,8 +91,10 @@ 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 # @@ -155,7 +157,7 @@ CONFIG_CHR_DEV_ST=y CONFIG_BLK_DEV_SR=y CONFIG_BLK_DEV_SR_VENDOR=y -# CONFIG_CHR_DEV_SG is not set +CONFIG_CHR_DEV_SG=y # # Some SCSI devices (e.g. CD jukebox) support multiple LUNs @@ -226,10 +228,19 @@ # 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 @@ -238,11 +249,12 @@ # 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_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 @@ -261,7 +273,10 @@ # 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 @@ -272,11 +287,22 @@ # # 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_RCPCI is not set +# CONFIG_SEALEVEL_4021 is not set +# CONFIG_DLCI is not set # # Amateur Radio support @@ -311,7 +337,6 @@ 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_VIRTUAL is not set # CONFIG_FBCON_ADVANCED is not set CONFIG_FBCON_CFB8=y @@ -407,6 +432,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 +484,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 @@ -468,6 +495,7 @@ CONFIG_DMASOUND=y # CONFIG_SOUND_ES1370 is not set # CONFIG_SOUND_ES1371 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 diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/defconfig linux.14p4/arch/ppc/defconfig --- linux.vanilla/arch/ppc/defconfig Wed Oct 20 01:12:33 1999 +++ linux.14p4/arch/ppc/defconfig Fri Oct 22 22:26:41 1999 @@ -9,14 +9,13 @@ 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_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,7 +60,7 @@ # # Block devices # -# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_FD=y CONFIG_BLK_DEV_IDE=y # @@ -75,16 +74,8 @@ # 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 @@ -101,7 +92,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,7 +116,7 @@ # CONFIG_NET_IPGRE is not set # CONFIG_IP_MROUTE is not set CONFIG_IP_ALIAS=y -# CONFIG_SYN_COOKIES is not set +CONFIG_SYN_COOKIES=y # # (it is safe to leave these untouched) @@ -166,7 +157,7 @@ CONFIG_CHR_DEV_ST=y CONFIG_BLK_DEV_SR=y CONFIG_BLK_DEV_SR_VENDOR=y -# CONFIG_CHR_DEV_SG is not set +CONFIG_CHR_DEV_SG=y # # Some SCSI devices (e.g. CD jukebox) support multiple LUNs @@ -200,14 +191,14 @@ # 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_PPA is not set -# CONFIG_SCSI_IMM is not set # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_SYM53C416 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 +206,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 @@ -262,7 +253,7 @@ # 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 @@ -289,7 +280,6 @@ # CONFIG_LTPC is not set # CONFIG_COPS is not set # CONFIG_IPDDP is not set -# CONFIG_PLIP is not set CONFIG_PPP=y # @@ -338,12 +328,15 @@ CONFIG_FB_CONTROL=y CONFIG_FB_PLATINUM=y CONFIG_FB_VALKYRIE=y -CONFIG_FB_ATY=y +# CONFIG_FB_ATY is not set CONFIG_FB_IMSTT=y CONFIG_FB_CT65550=y # CONFIG_FB_S3TRIO is not set -# CONFIG_FB_MATROX is not set -CONFIG_FB_ATY=y +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_VIRTUAL is not set # CONFIG_FBCON_ADVANCED is not set CONFIG_FBCON_CFB8=y @@ -365,13 +358,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 @@ -392,6 +394,14 @@ # 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 @@ -406,10 +416,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 +485,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 # @@ -489,7 +499,34 @@ # 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_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_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 + +# +# Additional low level sound drivers +# +# CONFIG_LOWLEVEL_SOUND is not set # # Kernel hacking diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/kernel/chrp_pci.c linux.14p4/arch/ppc/kernel/chrp_pci.c --- linux.vanilla/arch/ppc/kernel/chrp_pci.c Wed Oct 20 01:12:33 1999 +++ linux.14p4/arch/ppc/kernel/chrp_pci.c Fri Oct 22 22:26:41 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/kernel/head.S linux.14p4/arch/ppc/kernel/head.S --- linux.vanilla/arch/ppc/kernel/head.S Sat Aug 28 20:00:53 1999 +++ linux.14p4/arch/ppc/kernel/head.S Fri Oct 22 22:26:41 1999 @@ -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) @@ -112,6 +112,10 @@ /* 601 only have IBAT cr0.eq is set on 601 when using this macro */ #define LOAD_BAT(n, offset, reg, RA, RB) \ + /* see the comment for clear_bats() -- Cort */ \ + li RA,0; \ + mtspr IBAT##n##U,RA; \ + mtspr DBAT##n##U,RA; \ lwz RA,offset+0(reg); \ lwz RB,offset+4(reg); \ mtspr IBAT##n##U,RA; \ @@ -285,6 +289,14 @@ clrldi r16,r16,63 mtsdr1 r16 #else /* CONFIG_PPC64 */ + /* + * If the MMU is off clear the bats. See clear_bat() -- Cort + */ + mfmsr r20 + andi. r20,r20,MSR_DR + bne 100f + bl clear_bats +100: /* * allow secondary cpus to get at all of ram in early bootup * since their init_task may be up there -- Cort @@ -1312,7 +1324,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 */ @@ -2733,3 +2744,36 @@ .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: + li r20,0 + + mtspr DBAT0U,r20 + mtspr DBAT0L,r20 + mtspr IBAT0U,r20 + mtspr IBAT0L,r20 + + mtspr DBAT1U,r20 + mtspr DBAT1L,r20 + mtspr IBAT1U,r20 + mtspr IBAT1L,r20 + + mtspr DBAT2U,r20 + mtspr DBAT2L,r20 + mtspr IBAT2U,r20 + mtspr IBAT2L,r20 + + mtspr DBAT3U,r20 + mtspr DBAT3L,r20 + mtspr IBAT3U,r20 + mtspr IBAT3L,r20 + + blr diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/kernel/setup.c linux.14p4/arch/ppc/kernel/setup.c --- linux.vanilla/arch/ppc/kernel/setup.c Wed Oct 20 01:12:33 1999 +++ linux.14p4/arch/ppc/kernel/setup.c Fri Oct 22 22:26:41 1999 @@ -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. */ @@ -497,6 +497,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 +544,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 +554,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 +572,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 --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/mm/init.c linux.14p4/arch/ppc/mm/init.c --- linux.vanilla/arch/ppc/mm/init.c Wed Oct 20 01:12:33 1999 +++ linux.14p4/arch/ppc/mm/init.c Fri Oct 22 22:26:41 1999 @@ -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) @@ -149,6 +149,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! */ @@ -1296,7 +1299,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 +1332,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 +1350,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; } /* diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/arch/ppc/xmon/xmon.c linux.14p4/arch/ppc/xmon/xmon.c --- linux.vanilla/arch/ppc/xmon/xmon.c Sat Aug 28 20:00:54 1999 +++ linux.14p4/arch/ppc/xmon/xmon.c Fri Oct 22 22:26:41 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/Config.in linux.14p4/drivers/block/Config.in --- linux.vanilla/drivers/block/Config.in Sat Aug 14 02:27:16 1999 +++ linux.14p4/drivers/block/Config.in Fri Oct 22 22:18:35 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/genhd.c linux.14p4/drivers/block/genhd.c --- linux.vanilla/drivers/block/genhd.c Wed Oct 20 01:12:34 1999 +++ linux.14p4/drivers/block/genhd.c Fri Oct 22 22:15:20 1999 @@ -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; } diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/ide-cd.c linux.14p4/drivers/block/ide-cd.c --- linux.vanilla/drivers/block/ide-cd.c Wed Oct 20 01:12:34 1999 +++ linux.14p4/drivers/block/ide-cd.c Thu Nov 4 23:38:28 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/ide-disk.c linux.14p4/drivers/block/ide-disk.c --- linux.vanilla/drivers/block/ide-disk.c Thu Mar 11 01:49:43 1999 +++ linux.14p4/drivers/block/ide-disk.c Wed Oct 27 18:56:40 1999 @@ -88,36 +88,41 @@ */ 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; @@ -125,8 +130,7 @@ struct request *rq; 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); } msect = drive->mult_count; @@ -138,12 +142,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", @@ -160,21 +158,24 @@ 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, @@ -191,25 +192,28 @@ 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); + printk("%s: write_intr error2: nr_sectors=%ld, stat=0x%02x\n", drive->name, rq->nr_sectors, stat); + } + 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); + struct request *rq = &hwgroup->wrq; do { + unsigned long flags; unsigned int nsect = rq->current_nr_sectors; if (nsect > mcount) nsect = mcount; @@ -221,6 +225,7 @@ drive->name, rq->sector, (unsigned long) rq->buffer, nsect, rq->nr_sectors - nsect); #endif + spin_lock_irqsave(&io_request_lock, flags); /* Is this really necessary? */ if ((rq->nr_sectors -= nsect) <= 0) break; if ((rq->current_nr_sectors -= nsect) == 0) { @@ -228,32 +233,36 @@ 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; + spin_unlock_irqrestore(&io_request_lock, flags); + printk("%s: buffer list corrupted\n", drive->name); + ide_end_request(0, hwgroup); + return 1; } } else { 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) { 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 (!rq->nr_sectors) { /* all done? */ @@ -262,20 +271,17 @@ 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_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 +292,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 +324,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 +370,61 @@ } #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?). + */ + 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 +471,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 +478,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 +504,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 +628,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 +747,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 +759,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 +786,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 +794,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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/ide-dma.c linux.14p4/drivers/block/ide-dma.c --- linux.vanilla/drivers/block/ide-dma.c Sat Aug 14 02:26:27 1999 +++ linux.14p4/drivers/block/ide-dma.c Wed Oct 27 18:56:40 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/ide-floppy.c linux.14p4/drivers/block/ide-floppy.c --- linux.vanilla/drivers/block/ide-floppy.c Sat Aug 14 02:27:16 1999 +++ linux.14p4/drivers/block/ide-floppy.c Wed Oct 27 18:56:40 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/ide-probe.c linux.14p4/drivers/block/ide-probe.c --- linux.vanilla/drivers/block/ide-probe.c Mon Mar 22 20:44:18 1999 +++ linux.14p4/drivers/block/ide-probe.c Wed Oct 27 18:56:40 1999 @@ -560,10 +560,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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/ide-proc.c linux.14p4/drivers/block/ide-proc.c --- linux.vanilla/drivers/block/ide-proc.c Wed May 6 22:42:53 1998 +++ linux.14p4/drivers/block/ide-proc.c Fri Oct 22 22:15:20 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/ide-tape.c linux.14p4/drivers/block/ide-tape.c --- linux.vanilla/drivers/block/ide-tape.c Sat Aug 14 02:27:16 1999 +++ linux.14p4/drivers/block/ide-tape.c Wed Oct 27 18:56:40 1999 @@ -522,7 +522,7 @@ 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 */ } idetape_pc_t; @@ -1656,7 +1656,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 +1670,7 @@ printk (KERN_ERR "Error in REQUEST SENSE itself - Aborting request!\n"); idetape_end_request (0,HWGROUP (drive)); } + return ide_stopped; } /* @@ -1701,7 +1702,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 +1715,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 +1726,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 +1782,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 +1792,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 +1812,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 +1826,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 +1848,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 +1894,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 +1919,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 +1955,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 +1989,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 +2006,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 +2015,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 +2030,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 +2050,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 +2169,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 +2199,7 @@ } } else idetape_end_request (0,HWGROUP (drive)); + return ide_stopped; } static void idetape_create_read_position_cmd (idetape_pc_t *pc) @@ -2213,7 +2213,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 +2229,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 +2265,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 +2289,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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/ide.c linux.14p4/drivers/block/ide.c --- linux.vanilla/drivers/block/ide.c Wed Oct 20 01:12:34 1999 +++ linux.14p4/drivers/block/ide.c Wed Oct 27 18:56:41 1999 @@ -433,7 +433,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 +471,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 +514,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 +522,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 +535,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 +551,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 +560,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 +592,7 @@ } } hwgroup->poll_timeout = 0; /* done polling */ + return ide_stopped; } static void pre_reset (ide_drive_t *drive) @@ -623,7 +622,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 +641,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 +669,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 +703,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 +797,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 +818,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 +840,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 +864,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 +881,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 +904,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 +923,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 +937,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 +955,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 +963,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 +977,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 +987,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 +1031,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 +1112,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) { 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 +1169,34 @@ 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. + * + * Mmmm.. note we also do hwgroup->busy=0 below, which means + * we will also be woken up if somebody enqueues another + * request against this hwgroup while we're snoozing. + * We could "fix" that by setting hwgroup->busy=1 instead, + * but that would make the error handling more complicated + * in ide_timer_expiry() -- this is good enough for now. + */ if (0 < (signed long)(jiffies + WAIT_MIN_SLEEP - sleep)) sleep = jiffies + WAIT_MIN_SLEEP; -#if 1 - if (hwgroup->timer.next || hwgroup->timer.prev) - printk("ide_set_handler: timer already active\n"); -#endif + hwgroup->sleeping = 1; /* so that ide_timer_expiry knows what to do */ 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 +1207,13 @@ 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); - - 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); - if (hwif->irq != masked_irq) - enable_irq(hwif->irq); + spin_unlock(&io_request_lock); + if (!hwif->serialized) /* play it safe with buggy hardware */ + ide__sti(); + startstop = start_request(drive); + spin_lock_irq(&io_request_lock); + if (startstop == ide_stopped) + hwgroup->busy = 0; } } @@ -1190,134 +1227,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); } #if MAX_HWIFS > 1 void do_ide1_request (void) { - unlock_do_hwgroup_request (ide_hwifs[1].hwgroup); + ide_do_request (ide_hwifs[1].hwgroup); } #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); } #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); } #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); } #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); } #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); + spin_unlock_irqrestore(&io_request_lock, flags); } /* @@ -1377,9 +1392,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 +1419,63 @@ */ (void)ide_ack_intr(hwif->io_ports[IDE_STATUS_OFFSET], hwif->io_ports[IDE_IRQ_OFFSET]); unexpected_intr(irq, hwgroup); + } 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); + 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); + } else { + printk("%s: ide_intr: huh? expected NULL handler on exit\n", drive->name); + } + } + spin_unlock_irqrestore(&io_request_lock, flags); } /* @@ -1521,7 +1558,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 +1573,13 @@ rq->next = cur_rq->next; cur_rq->next = rq; } + ide_do_request(hwgroup); 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 +1602,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 +1995,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 +2008,7 @@ val = *((u32 *) setting->data); break; } - spin_unlock_irqrestore(&HWGROUP(drive)->spinlock, flags); + spin_unlock_irqrestore(&io_request_lock, flags); } return val; } @@ -1985,19 +2018,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 +2072,7 @@ *p = val; break; } - spin_unlock_irqrestore(&HWGROUP(drive)->spinlock, flags); + spin_unlock_irqrestore(&io_request_lock, flags); return 0; } @@ -2844,9 +2886,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 +2927,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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/ide.h linux.14p4/drivers/block/ide.h --- linux.vanilla/drivers/block/ide.h Sun Mar 28 19:03:35 1999 +++ linux.14p4/drivers/block/ide.h Wed Nov 3 00:53:01 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/block/pdc4030.c linux.14p4/drivers/block/pdc4030.c --- linux.vanilla/drivers/block/pdc4030.c Tue Dec 29 19:24:57 1998 +++ linux.14p4/drivers/block/pdc4030.c Wed Oct 27 18:56:41 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/char/Config.in linux.14p4/drivers/char/Config.in --- linux.vanilla/drivers/char/Config.in Wed Oct 20 01:12:34 1999 +++ linux.14p4/drivers/char/Config.in Wed Nov 3 00:18:55 1999 @@ -101,6 +101,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 diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/char/Makefile linux.14p4/drivers/char/Makefile --- linux.vanilla/drivers/char/Makefile Wed Oct 20 01:12:34 1999 +++ linux.14p4/drivers/char/Makefile Wed Nov 3 00:18:55 1999 @@ -270,6 +270,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 @@ -380,9 +388,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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/char/bttv.c linux.14p4/drivers/char/bttv.c --- linux.vanilla/drivers/char/bttv.c Wed Oct 20 01:12:35 1999 +++ linux.14p4/drivers/char/bttv.c Fri Oct 22 22:16:43 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/char/buz.c linux.14p4/drivers/char/buz.c --- linux.vanilla/drivers/char/buz.c Wed Oct 20 01:12:35 1999 +++ linux.14p4/drivers/char/buz.c Fri Oct 22 22:16:43 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/char/hfmodem/refclock.c linux.14p4/drivers/char/hfmodem/refclock.c --- linux.vanilla/drivers/char/hfmodem/refclock.c Sat Jul 18 19:47:49 1998 +++ linux.14p4/drivers/char/hfmodem/refclock.c Fri Oct 29 23:36:36 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/char/mixcomwd.c linux.14p4/drivers/char/mixcomwd.c --- linux.vanilla/drivers/char/mixcomwd.c Thu Jan 1 01:00:00 1970 +++ linux.14p4/drivers/char/mixcomwd.c Wed Nov 3 00:18:55 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/char/radio-aimslab.c linux.14p4/drivers/char/radio-aimslab.c --- linux.vanilla/drivers/char/radio-aimslab.c Sat Aug 14 02:25:39 1999 +++ linux.14p4/drivers/char/radio-aimslab.c Fri Oct 22 22:18:35 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/char/radio-cadet.c linux.14p4/drivers/char/radio-cadet.c --- linux.vanilla/drivers/char/radio-cadet.c Sat Aug 14 02:27:17 1999 +++ linux.14p4/drivers/char/radio-cadet.c Fri Oct 22 22:16:43 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/char/radio-rtrack2.c linux.14p4/drivers/char/radio-rtrack2.c --- linux.vanilla/drivers/char/radio-rtrack2.c Sat Aug 14 02:25:40 1999 +++ linux.14p4/drivers/char/radio-rtrack2.c Fri Oct 22 22:18:35 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/fc4/Makefile linux.14p4/drivers/fc4/Makefile --- linux.vanilla/drivers/fc4/Makefile Tue Mar 16 00:11:29 1999 +++ linux.14p4/drivers/fc4/Makefile Fri Oct 22 21:24:05 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/avmb1/capi.c linux.14p4/drivers/isdn/avmb1/capi.c --- linux.vanilla/drivers/isdn/avmb1/capi.c Wed Oct 20 01:12:36 1999 +++ linux.14p4/drivers/isdn/avmb1/capi.c Fri Oct 22 22:18:35 1999 @@ -109,6 +109,7 @@ */ #include +#include #include #include #include diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/isdn/isdn_ppp.c linux.14p4/drivers/isdn/isdn_ppp.c --- linux.vanilla/drivers/isdn/isdn_ppp.c Wed Oct 20 01:12:38 1999 +++ linux.14p4/drivers/isdn/isdn_ppp.c Fri Oct 29 23:19:29 1999 @@ -1540,7 +1540,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; @@ -2667,9 +2673,10 @@ } /* Allow for at least 150 % expansion (for now) */ - skb_out = dev_alloc_skb(skb_in->len + skb_in->len/2 + 32); + skb_out = dev_alloc_skb(skb_in->len + skb_in->len/2 + 32 + skb_headroom(skb_in)); 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) { diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/macintosh/macserial.c linux.14p4/drivers/macintosh/macserial.c --- linux.vanilla/drivers/macintosh/macserial.c Wed Oct 20 01:12:38 1999 +++ linux.14p4/drivers/macintosh/macserial.c Fri Oct 22 22:26:41 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/macintosh/macserial.h linux.14p4/drivers/macintosh/macserial.h --- linux.vanilla/drivers/macintosh/macserial.h Wed Oct 20 01:12:38 1999 +++ linux.14p4/drivers/macintosh/macserial.h Fri Oct 22 22:26:41 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/macintosh/mediabay.c linux.14p4/drivers/macintosh/mediabay.c --- linux.vanilla/drivers/macintosh/mediabay.c Wed Oct 20 01:12:38 1999 +++ linux.14p4/drivers/macintosh/mediabay.c Fri Oct 22 22:26:41 1999 @@ -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,25 +416,25 @@ 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; + if (bay->cd_timer) + bay->cd_timer = MB_IDE_WAIT; } break; } diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/misc/parport_pc.c linux.14p4/drivers/misc/parport_pc.c --- linux.vanilla/drivers/misc/parport_pc.c Sat Aug 14 02:27:27 1999 +++ linux.14p4/drivers/misc/parport_pc.c Wed Oct 27 19:05:02 1999 @@ -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 int vendor; + unsigned int device; + unsigned int numports; + struct { + unsigned long lo; + unsigned long 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++) { + unsigned long lo = cards[i].addr[n].lo; + unsigned long 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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/misc/parport_share.c linux.14p4/drivers/misc/parport_share.c --- linux.vanilla/drivers/misc/parport_share.c Sat Aug 14 02:26:48 1999 +++ linux.14p4/drivers/misc/parport_share.c Wed Oct 27 19:05:02 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/Config.in linux.14p4/drivers/net/Config.in --- linux.vanilla/drivers/net/Config.in Wed Oct 20 01:12:38 1999 +++ linux.14p4/drivers/net/Config.in Wed Nov 3 00:18:55 1999 @@ -165,7 +165,9 @@ 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 @@ -262,6 +264,24 @@ # 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 + dep_tristate ' Support for COMX/CMX/HiCOMX boards' CONFIG_COMX_HW_COMX $CONFIG_COMX + 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 @@ -288,7 +308,6 @@ fi endmenu - # # X.25 network drivers diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/Makefile linux.14p4/drivers/net/Makefile --- linux.vanilla/drivers/net/Makefile Wed Oct 20 01:12:38 1999 +++ linux.14p4/drivers/net/Makefile Wed Nov 3 00:18:55 1999 @@ -836,14 +836,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 +904,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 diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/arlan-proc.c linux.14p4/drivers/net/arlan-proc.c --- linux.vanilla/drivers/net/arlan-proc.c Sat Aug 14 02:27:28 1999 +++ linux.14p4/drivers/net/arlan-proc.c Fri Oct 22 22:16:55 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/comx-hw-comx.c linux.14p4/drivers/net/comx-hw-comx.c --- linux.vanilla/drivers/net/comx-hw-comx.c Thu Jan 1 01:00:00 1970 +++ linux.14p4/drivers/net/comx-hw-comx.c Wed Nov 3 00:18:55 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/comx-hw-locomx.c linux.14p4/drivers/net/comx-hw-locomx.c --- linux.vanilla/drivers/net/comx-hw-locomx.c Thu Jan 1 01:00:00 1970 +++ linux.14p4/drivers/net/comx-hw-locomx.c Wed Nov 3 00:18:55 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/comx-hw-mixcom.c linux.14p4/drivers/net/comx-hw-mixcom.c --- linux.vanilla/drivers/net/comx-hw-mixcom.c Thu Jan 1 01:00:00 1970 +++ linux.14p4/drivers/net/comx-hw-mixcom.c Wed Nov 3 00:18:55 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/comx-proto-fr.c linux.14p4/drivers/net/comx-proto-fr.c --- linux.vanilla/drivers/net/comx-proto-fr.c Thu Jan 1 01:00:00 1970 +++ linux.14p4/drivers/net/comx-proto-fr.c Wed Nov 3 00:18:55 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/comx-proto-lapb.c linux.14p4/drivers/net/comx-proto-lapb.c --- linux.vanilla/drivers/net/comx-proto-lapb.c Thu Jan 1 01:00:00 1970 +++ linux.14p4/drivers/net/comx-proto-lapb.c Wed Nov 3 00:18:55 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/comx-proto-ppp.c linux.14p4/drivers/net/comx-proto-ppp.c --- linux.vanilla/drivers/net/comx-proto-ppp.c Thu Jan 1 01:00:00 1970 +++ linux.14p4/drivers/net/comx-proto-ppp.c Wed Nov 3 00:18:55 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/comx.c linux.14p4/drivers/net/comx.c --- linux.vanilla/drivers/net/comx.c Thu Jan 1 01:00:00 1970 +++ linux.14p4/drivers/net/comx.c Wed Nov 3 00:18:55 1999 @@ -0,0 +1,1230 @@ +/* + * 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 + +#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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/comx.h linux.14p4/drivers/net/comx.h --- linux.vanilla/drivers/net/comx.h Thu Jan 1 01:00:00 1970 +++ linux.14p4/drivers/net/comx.h Wed Nov 3 00:18:55 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/comxhw.h linux.14p4/drivers/net/comxhw.h --- linux.vanilla/drivers/net/comxhw.h Thu Jan 1 01:00:00 1970 +++ linux.14p4/drivers/net/comxhw.h Wed Nov 3 00:18:55 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/hamradio/Config.in linux.14p4/drivers/net/hamradio/Config.in --- linux.vanilla/drivers/net/hamradio/Config.in Sat Aug 14 02:27:29 1999 +++ linux.14p4/drivers/net/hamradio/Config.in Wed Oct 27 18:50:24 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/hscx.h linux.14p4/drivers/net/hscx.h --- linux.vanilla/drivers/net/hscx.h Thu Jan 1 01:00:00 1970 +++ linux.14p4/drivers/net/hscx.h Wed Nov 3 00:18:55 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/mixcom.h linux.14p4/drivers/net/mixcom.h --- linux.vanilla/drivers/net/mixcom.h Thu Jan 1 01:00:00 1970 +++ linux.14p4/drivers/net/mixcom.h Wed Nov 3 00:18:55 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/pcnet32.c linux.14p4/drivers/net/pcnet32.c --- linux.vanilla/drivers/net/pcnet32.c Wed Oct 20 01:12:39 1999 +++ linux.14p4/drivers/net/pcnet32.c Fri Oct 22 21:24:24 1999 @@ -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; @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/sis900.c linux.14p4/drivers/net/sis900.c --- linux.vanilla/drivers/net/sis900.c Wed Oct 20 01:12:39 1999 +++ linux.14p4/drivers/net/sis900.c Thu Nov 4 23:46:11 1999 @@ -1,58 +1,29 @@ -/*****************************************************************************/ -/* 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. + 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 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 +36,320 @@ #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 11/04/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 - } +/* 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; +} - 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); +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); + + /* check to see if we have sane EEPROM */ + signature = (u16) read_eeprom(ioaddr, EEPROMSignature); + if (signature == 0xffff || signature == 0x0000) { + printk (KERN_INFO "%s: Error EERPOM read %x\n", + net_dev->name, signature); + return NULL; + } - pcibios_write_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, new_command); - } + if ((net_dev = init_etherdev(net_dev, 0)) == NULL) + 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 +359,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 +370,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 +379,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 +414,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 +443,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 +461,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 +483,366 @@ } 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; - } - - { - 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_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); - } - } + sis900_init_rxfilter(net_dev); - outl(PESEL, ioaddr + cfg); + sis900_init_tx_ring(net_dev); + sis900_init_rx_ring(net_dev); - /* 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); + set_rx_mode(net_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|TxOK), 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; - /* 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; + sis_priv->cur_rx = 0; + net_dev->trans_start = jiffies; + sis_priv->stats.tx_errors++; - 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|TxOK), 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 +851,264 @@ 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 (sis900_debug > 3) + printk(KERN_INFO "%s: entering interrupt, " + "original status = %#8.8x, " + "new status = %#8.8x.\n", + net_dev->name, status, inl(ioaddr + isr)); - if ((status & (TxURN|TxERR|TxOK | RxORN|RxERR|RxOK)) == 0) { + if ((status & (HIBERR|TxURN|TxERR|TxOK|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 | TxOK)) + /* 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; + + 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) ! */ + 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 */ + if (sis900_debug > 3) + printk(KERN_INFO "Tx Transmit OK\n"); + 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 +1117,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 +1140,140 @@ } 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) -{ - u16 status, OurCap; - - *speed = HW_SPEED_10_MBPS; - *duplex = FDX_CAPABLE_HALF_SELECTED; - - 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); -} - -static u16 elAutoNegotiate(struct device *dev, int phy_id, int *duplex, int *speed) +sis900_get_stats(struct device *net_dev) { - u16 status, retnVal; + struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; - 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); + return &sis_priv->stats; } -static void elSetCapability(struct device *dev, int phy_id, - int duplex, int speed) +/* 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) { - 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"); +/* what is the correct value of the POLYNOMIAL ?? + Donald Becker use 0x04C11DB7U */ +#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; } } - - 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 packet */ + rx_mode = RFAAB | RFAAM; + for (i = 0; i < 8; i++) + mc_filter[i] = 0xffff; } else { + /* Accept Broadcast packet, destination address matchs our MAC address, + use Receive Filter to reject unwanted MCAST packet */ 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 ??, I don't know, UNDOCUMENT FEATURE ?? */ + 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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/sis900.h linux.14p4/drivers/net/sis900.h --- linux.vanilla/drivers/net/sis900.h Thu Jan 1 01:00:00 1970 +++ linux.14p4/drivers/net/sis900.h Thu Nov 4 23:46:11 1999 @@ -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 24 /* 3/4 FIFO size */ +#define RxDRNT_10 16 /* 1/2 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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/syncppp.c linux.14p4/drivers/net/syncppp.c --- linux.vanilla/drivers/net/syncppp.c Wed Oct 20 01:12:39 1999 +++ linux.14p4/drivers/net/syncppp.c Wed Nov 3 00:18:55 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/syncppp.h linux.14p4/drivers/net/syncppp.h --- linux.vanilla/drivers/net/syncppp.h Sat Feb 6 20:46:21 1999 +++ linux.14p4/drivers/net/syncppp.h Wed Nov 3 00:18:55 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/tulip.c linux.14p4/drivers/net/tulip.c --- linux.vanilla/drivers/net/tulip.c Sat Aug 14 02:27:30 1999 +++ linux.14p4/drivers/net/tulip.c Fri Oct 29 23:35:05 1999 @@ -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, }; @@ -327,10 +439,6 @@ 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,130 @@ 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 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_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 +649,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 +712,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 +722,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 +763,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 +780,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 +841,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 +921,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 +945,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 +1000,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 +1043,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 +1083,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 +1099,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 +1133,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 +1150,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 +1186,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 +1194,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 +1203,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 +1278,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 +1336,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 +1349,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 +1392,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 +1405,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 +1467,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 +1485,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 +1584,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 +1599,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 +1617,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 +1680,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 +1706,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 +1714,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 +1752,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 +1764,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 +1799,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 +1880,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 +1902,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 +1990,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 +2002,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 +2017,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 +2069,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 +2218,6 @@ outl(tp->csr6 | 0x2002, ioaddr + CSR6); } } - static void mxic_timer(unsigned long data) { @@ -1892,57 +2236,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 +2339,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; @@ -2078,38 +2486,37 @@ 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]); + 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,78 +2535,64 @@ 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; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; int csr5, work_budget = max_interrupt_work; - if (dev == NULL) { - printk (KERN_ERR" tulip_interrupt(): irq %d for unknown device.\n", - irq); +#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 do { @@ -2223,10 +2616,10 @@ 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; @@ -2253,18 +2646,14 @@ 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; } @@ -2281,15 +2670,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 +2686,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) @@ -2310,47 +2701,48 @@ 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) - && tp->chip_id == DC21142) { - if (tulip_debug > 1) - printk(KERN_INFO"%s: 21142 link change, CSR5 = %8.8x.\n", + if (tulip_debug > 2) + printk(KERN_ERR "%s: Re-enabling interrupts, %8.8x.\n", dev->name, csr5); - t21142_lnk_change(dev); + outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7); + } + if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)) { + if (tp->link_change) + (tp->link_change)(dev, csr5); } /* 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, " + printk(KERN_WARNING "%s: Too much work during an 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 + /* Clear all interrupting sources, set timer to re-enable. */ + outl(((~csr5) & 0x0001ebef) | AbnormalIntr | TimerInt, + ioaddr + CSR7); + outl(12, ioaddr + CSR11); break; } } while (1); - if (tulip_debug > 3) + 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_rx(struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; int entry = tp->cur_rx % RX_RING_SIZE; @@ -2360,74 +2752,76 @@ 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++; @@ -2443,25 +2837,20 @@ 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); + 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 + tp->rx_ring[entry].buffer1 = virt_to_le32desc(skb->tail); work_done++; } - tp->rx_ring[entry].status = 0x80000000; + tp->rx_ring[entry].status = cpu_to_le32(DescOwned); } return work_done; } -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 +2865,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 +2891,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 +2933,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 +2969,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 +2998,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 +3014,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 +3123,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 +3154,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 +3177,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 +3234,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 +3252,7 @@ #endif } -void -cleanup_module(void) +void cleanup_module(void) { struct device *next_dev; @@ -2832,9 +3262,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 +3276,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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/wavelan.c linux.14p4/drivers/net/wavelan.c --- linux.vanilla/drivers/net/wavelan.c Wed Oct 20 01:12:39 1999 +++ linux.14p4/drivers/net/wavelan.c Wed Nov 3 00:22:30 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/wavelan.p.h linux.14p4/drivers/net/wavelan.p.h --- linux.vanilla/drivers/net/wavelan.p.h Sat Aug 14 02:27:30 1999 +++ linux.14p4/drivers/net/wavelan.p.h Wed Nov 3 01:06:59 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/z85230.c linux.14p4/drivers/net/z85230.c --- linux.vanilla/drivers/net/z85230.c Sat Aug 14 02:27:30 1999 +++ linux.14p4/drivers/net/z85230.c Wed Nov 3 00:18:55 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/net/z85230.h linux.14p4/drivers/net/z85230.h --- linux.vanilla/drivers/net/z85230.h Sat Oct 17 23:33:45 1998 +++ linux.14p4/drivers/net/z85230.h Wed Nov 3 00:18:55 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/pci/oldproc.c linux.14p4/drivers/pci/oldproc.c --- linux.vanilla/drivers/pci/oldproc.c Wed Oct 20 01:12:39 1999 +++ linux.14p4/drivers/pci/oldproc.c Fri Oct 22 21:24:50 1999 @@ -526,6 +526,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 +537,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"), diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/eata.c linux.14p4/drivers/scsi/eata.c --- linux.vanilla/drivers/scsi/eata.c Wed Oct 20 01:12:41 1999 +++ linux.14p4/drivers/scsi/eata.c Tue Nov 2 08:58:40 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/eata.h linux.14p4/drivers/scsi/eata.h --- linux.vanilla/drivers/scsi/eata.h Wed Oct 20 01:12:41 1999 +++ linux.14p4/drivers/scsi/eata.h Tue Nov 2 08:58:40 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/fdomain.c linux.14p4/drivers/scsi/fdomain.c --- linux.vanilla/drivers/scsi/fdomain.c Tue Dec 29 19:44:53 1998 +++ linux.14p4/drivers/scsi/fdomain.c Fri Oct 22 22:32:09 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/gdth.h linux.14p4/drivers/scsi/gdth.h --- linux.vanilla/drivers/scsi/gdth.h Sat Aug 14 02:25:41 1999 +++ linux.14p4/drivers/scsi/gdth.h Wed Nov 3 00:54:02 1999 @@ -249,8 +249,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 */ diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/ide-scsi.c linux.14p4/drivers/scsi/ide-scsi.c --- linux.vanilla/drivers/scsi/ide-scsi.c Sat Aug 14 02:27:31 1999 +++ linux.14p4/drivers/scsi/ide-scsi.c Wed Oct 27 18:56:41 1999 @@ -308,7 +308,7 @@ /* * 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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/ips.h linux.14p4/drivers/scsi/ips.h --- linux.vanilla/drivers/scsi/ips.h Wed Oct 20 01:12:41 1999 +++ linux.14p4/drivers/scsi/ips.h Wed Nov 3 00:54:02 1999 @@ -233,6 +233,7 @@ /* * DCDB Table Equates */ +#ifndef HOSTS_C #define NO_DISCONNECT 0x00 #define DISCONNECT_ALLOWED 0x80 #define NO_AUTO_REQUEST_SENSE 0x40 @@ -243,7 +244,6 @@ #define TIMEOUT10 0x10 #define TIMEOUT60 0x20 #define TIMEOUT20M 0x30 - /* * Host adapter Flags (bit numbers) */ @@ -256,7 +256,7 @@ */ #define SCB_ACTIVE 0x00001 #define SCB_WAITING 0x00002 - +#endif /* HOSTS_C */ /* * Passthru stuff */ @@ -590,6 +590,8 @@ /* * Inquiry Data Format */ +#ifndef HOSTS_C + typedef struct { u8 DeviceType:5; u8 DeviceTypeQualifier:3; @@ -614,6 +616,7 @@ u8 Reserved3[40]; } INQUIRYDATA, *PINQUIRYDATA; +#endif /* * Read Capacity Data Format */ diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/megaraid.h linux.14p4/drivers/scsi/megaraid.h --- linux.vanilla/drivers/scsi/megaraid.h Sat Aug 28 20:00:57 1999 +++ linux.14p4/drivers/scsi/megaraid.h Wed Nov 3 00:54:02 1999 @@ -11,6 +11,7 @@ #define BOARD_QUARTZ 0x08000000L #define BOARD_40LD 0x04000000L +#ifndef HOSTS_C #define SCB_FREE 0x0 #define SCB_ACTIVE 0x1 #define SCB_WAITQ 0x2 @@ -18,6 +19,7 @@ #define SCB_COMPLETE 0x4 #define SCB_ABORTED 0x5 #define SCB_RESET 0x6 +#endif #define MEGA_CMD_TIMEOUT 10 diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/psi240i.h linux.14p4/drivers/scsi/psi240i.h --- linux.vanilla/drivers/scsi/psi240i.h Mon Dec 28 06:19:22 1998 +++ linux.14p4/drivers/scsi/psi240i.h Fri Oct 22 23:26:35 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/qlogicfc.c linux.14p4/drivers/scsi/qlogicfc.c --- linux.vanilla/drivers/scsi/qlogicfc.c Wed Oct 20 01:12:41 1999 +++ linux.14p4/drivers/scsi/qlogicfc.c Fri Oct 22 21:25:11 1999 @@ -1855,7 +1855,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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/qlogicisp.c linux.14p4/drivers/scsi/qlogicisp.c --- linux.vanilla/drivers/scsi/qlogicisp.c Wed Oct 20 01:12:42 1999 +++ linux.14p4/drivers/scsi/qlogicisp.c Fri Oct 22 21:25:11 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/sg.c linux.14p4/drivers/scsi/sg.c --- linux.vanilla/drivers/scsi/sg.c Sat Aug 14 02:26:50 1999 +++ linux.14p4/drivers/scsi/sg.c Wed Nov 3 00:27:23 1999 @@ -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 (991008)"; + 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 @@ -686,9 +692,13 @@ 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 +721,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 +731,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,6 +789,7 @@ 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 */ diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/sym53c416.h linux.14p4/drivers/scsi/sym53c416.h --- linux.vanilla/drivers/scsi/sym53c416.h Sun Mar 28 19:04:11 1999 +++ linux.14p4/drivers/scsi/sym53c416.h Wed Nov 3 00:54:02 1999 @@ -22,7 +22,9 @@ #include #endif +#ifndef LinuxVersionCode #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) +#endif #include #include diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/sym53c8xx_defs.h linux.14p4/drivers/scsi/sym53c8xx_defs.h --- linux.vanilla/drivers/scsi/sym53c8xx_defs.h Wed Oct 20 01:12:47 1999 +++ linux.14p4/drivers/scsi/sym53c8xx_defs.h Wed Nov 3 00:54:02 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/u14-34f.c linux.14p4/drivers/scsi/u14-34f.c --- linux.vanilla/drivers/scsi/u14-34f.c Fri Oct 9 19:56:59 1998 +++ linux.14p4/drivers/scsi/u14-34f.c Tue Nov 2 08:59:03 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/scsi/u14-34f.h linux.14p4/drivers/scsi/u14-34f.h --- linux.vanilla/drivers/scsi/u14-34f.h Sun Mar 28 19:04:08 1999 +++ linux.14p4/drivers/scsi/u14-34f.h Tue Nov 2 08:58:40 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/Config.in linux.14p4/drivers/sound/Config.in --- linux.vanilla/drivers/sound/Config.in Wed Oct 20 01:12:47 1999 +++ linux.14p4/drivers/sound/Config.in Fri Oct 29 23:23:20 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/Makefile linux.14p4/drivers/sound/Makefile --- linux.vanilla/drivers/sound/Makefile Wed Oct 20 01:12:47 1999 +++ linux.14p4/drivers/sound/Makefile Fri Oct 29 23:22:40 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/ac97.h linux.14p4/drivers/sound/ac97.h --- linux.vanilla/drivers/sound/ac97.h Wed Oct 20 01:12:47 1999 +++ linux.14p4/drivers/sound/ac97.h Wed Nov 3 01:09:15 1999 @@ -33,6 +33,19 @@ /* 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 */ + /* registers 0x005a - 0x007a are vendor reserved */ #define AC97_VENDOR_ID1 0x007c diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/adlib_card.c linux.14p4/drivers/sound/adlib_card.c --- linux.vanilla/drivers/sound/adlib_card.c Thu May 14 18:33:17 1998 +++ linux.14p4/drivers/sound/adlib_card.c Fri Oct 22 22:52:53 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/es1370.c linux.14p4/drivers/sound/es1370.c --- linux.vanilla/drivers/sound/es1370.c Sat Aug 14 02:27:34 1999 +++ linux.14p4/drivers/sound/es1370.c Fri Oct 22 22:34:17 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/es1371.c linux.14p4/drivers/sound/es1371.c --- linux.vanilla/drivers/sound/es1371.c Sat Aug 14 02:27:34 1999 +++ linux.14p4/drivers/sound/es1371.c Fri Oct 22 22:34:18 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/esssolo1.c linux.14p4/drivers/sound/esssolo1.c --- linux.vanilla/drivers/sound/esssolo1.c Wed Oct 20 01:12:48 1999 +++ linux.14p4/drivers/sound/esssolo1.c Fri Oct 22 22:34:17 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/lowlevel/miroaci.h linux.14p4/drivers/sound/lowlevel/miroaci.h --- linux.vanilla/drivers/sound/lowlevel/miroaci.h Sun Aug 23 21:32:25 1998 +++ linux.14p4/drivers/sound/lowlevel/miroaci.h Fri Oct 29 23:43:20 1999 @@ -1,6 +1,11 @@ +#if defined(CONFIG_ACI_MIXER) || defined(CONFIG_ACI_MIXER_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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/maestro.c linux.14p4/drivers/sound/maestro.c --- linux.vanilla/drivers/sound/maestro.c Thu Jan 1 01:00:00 1970 +++ linux.14p4/drivers/sound/maestro.c Mon Nov 1 12:38:54 1999 @@ -0,0 +1,3577 @@ +/***************************************************************************** + * + * ESS Maestro/Maestro-2/Maestro-2E driver for Linux 2.2.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-7 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 ASP, 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 ASP 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 ASP, 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. + * + * For output we maintain a ring buffer of data that we are DMAing + * to the card. In mono operation this is nice and easy. When + * we receive data we tack it onto the ring buffer and make sure + * the APU assigned to it is playing over the data. When we fill + * the ring buffer we put the client to sleep until there is + * room again. Easy. + * + * However, this starts to stink when we use stereo. The APUs + * supposedly can decode LRLR packed stereo data, but it + * doesn't work. So we're forced to use dual mono APUs walking over + * mono encoded data. This requires us to split the input from + * the client and complicates the buffer maths tremendously. Ick. + * + * This also pollutes the recording paths as well. We have to use + * 2 L/R incoming APUs that are fixed at 16bit/48khz. We then pipe + * these through 2 rate converion apus that mix them down to the + * requested frequency and write them to memory through the wavecache. + * We also apparently need a 512byte region thats used as temp space + * between the incoming APUs and the rate converters. + * + * 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.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.. + * 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? + * anyone have a pt101 codec? + * mmap(), but beware stereo encoding nastiness. + * actually post pci writes + * fix bob frequency + * do smart things with ac97 2.0 bits. + * ugh.. non aligned writes in the middle of a data stream.. ugh + * sort out 0x34->0x36 crap in init + * docking and dual codecs and 978? + * pcm_sync? + * actually use LRLR + * + * 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) + + #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.10" + +#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 + + +/* 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 3 +#define MAX_DSPS (1<<3) +#define NR_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 */ +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 { + right = ((100 - right) * mh->scale) / 100; + left = ((100 - left) * mh->scale) / 100; + if((left == mh->scale) && (right == mh->scale)) + val |= 0x8000; + } + + 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; +} + +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; +} + +/* this is very magic, and very slow.. */ +static void +maestro_ac97_reset(int ioaddr, struct pci_dev *pcidev) +{ + u16 save_68; + u16 w; + + 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. */ + 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 +} +/* + * 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) +{ + if(mode&ESS_FMT_16BIT) { + s->apu_mode[apu] = 0x10; + s->apu_mode[apu+1] = 0x10; + } else { + s->apu_mode[apu] = 0x30; + s->apu_mode[apu+1] = 0x30; + } +} + +/* 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); +} + +static u16 compute_rate(u32 freq) +{ + if(freq==48000) + return 0xFFFF; + freq<<=16; + freq/=48000; + return freq; +} + +static void set_dac_rate(struct ess_state *s, unsigned rate) +{ + u32 freq; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + + s->ratedac = rate; + + if(!((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_16BIT)) + rate >>= 1; /* who knows */ + +/* M_printk("computing dac rate %d with mode %d\n",rate,s->fmt);*/ + + freq = compute_rate(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; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + + s->rateadc = rate; + + freq = compute_rate(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; + + /* we're given the full size of the buffer, but + in stereo each channel will only play its half */ + if(mode&ESS_FMT_STEREO) { + size >>=1; + high_apu++; + } + + for(channel=0; channel <= high_apu; channel++) + { + int i; + + if(!channel) + pa = virt_to_bus(buffer); + else + /* right channel plays its split half. + *2 accomodates for rampant shifting earlier */ + pa = virt_to_bus(buffer + size*2); + + /* set the wavecache control reg */ + tmpval = (pa - 0x10) & 0xFFF8; + if(!(mode & 2)) tmpval |= 4; /* 8bit */ + 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) ess->dma_dac.base = pa&0xFFFF; + + pa|=0x00400000; /* System RAM */ + + /* Begin loading the APU */ + for(i=0;i<15;i++) /* clear all PBRs */ + apu_set_register(ess, channel, i, 0x0000); + +/* 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_STEREO) + /* set panning: left or right */ + apu_set_register(ess, channel, 10, 0x8F00 | (channel ? 0x10 : 0)); + else + apu_set_register(ess, channel, 10, 0x8F08); + + if(mode&ESS_FMT_16BIT) + ess->apu_mode[channel]=0x10; + else + ess->apu_mode[channel]=0x30; + } + + /* 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; + /* swptr is always in bytes as read from an apu.. */ + unsigned bptr = s->dma_dac.swptr; + unsigned len = s->dma_dac.fragsize; + int i=1; + + if((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_STEREO) { + i++; + bsize >>=1; + } + + for ( ;i; i-- , buf += bsize) { + + 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) { + /* this is so gross. */ + hwptr = (/*s->dma_dac.dmasize -*/ get_dmaa(s)) % s->dma_dac.dmasize; + 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; +} + +/* god this is gross..*/ +/* again, the mode passed is shifted/masked */ +static int +split_stereo(unsigned char *real_buffer,unsigned char *tmp_buffer, int offset, + int count, int bufsize, int mode) +{ + /* oh, bother. stereo decoding APU's don't work in 16bit so we + use dual linear decoders. which means we have to hack up stereo + buffer's we're given. yuck. */ + + unsigned char *so,*left,*right; + int i; + + so = tmp_buffer; + left = real_buffer + offset; + right = real_buffer + bufsize/2 + offset; + + if(mode & ESS_FMT_16BIT) { + for(i=count/4; i ; i--) { + *(right++) = (*(so+2)); + *(right++) = (*(so+3)); + *(left++) = (*so); + *(left++) = (*(so+1)); + so+=4; + } + } else { + for(i=count/2; i ; i--) { + *(right++) = (*(so+1)); + *(left++) = (*so); + so+=2; + } + } + + return 0; +} + +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; + unsigned char *splitbuf = NULL; + int cnt; + int mode = (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK; + + 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; + /* XXX be more clever than this.. */ + if (!(splitbuf = kmalloc(count,GFP_KERNEL))) + return -ENOMEM; + 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; + + if(mode & ESS_FMT_STEREO) { + /* in stereo we have the 'dual' buffers.. */ + cnt = ((s->dma_dac.dmasize/2)-swptr)*2; + } else { + 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; + + /* our goofball stereo splitter can only deal in mults of 4 */ + if (cnt > 0) + cnt &= ~3; + + 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(mode & ESS_FMT_STEREO) { + if (copy_from_user(splitbuf, buffer, cnt)) { + if (!ret) ret = -EFAULT; + goto return_free; + } + split_stereo(s->dma_dac.rawbuf,splitbuf,swptr,cnt,s->dma_dac.dmasize, + mode); + } else { + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { + if (!ret) ret = -EFAULT; + goto return_free; + } + } + + if(mode & ESS_FMT_STEREO) { + /* again with the weird pointer magic */ + swptr = (swptr + (cnt/2)) % (s->dma_dac.dmasize/2); + } else { + 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: + if (splitbuf) kfree(splitbuf); + 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; +} + +/* this needs to be fixed to deal with the dual apus/buffers */ +#if 0 +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 (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf(s, 0)) != 0) + return ret; + db = &s->dma_adc; + } else + 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; +} +#endif + +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_S8|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_S8, + (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, + NULL, /* XXX &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 in 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) { + maestro_pt101_init(card,iobase); + } 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_apm_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[i]][5]=apu_get_register(s,i,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_apm_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_apm_suspend();break; + case APM_NORMAL_RESUME: + case APM_CRITICAL_RESUME: + case APM_STANDBY_RESUME: + maestro_apm_resume();break; + default: break; + } + + return 0; +} +#endif diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/maestro.h linux.14p4/drivers/sound/maestro.h --- linux.vanilla/drivers/sound/maestro.h Thu Jan 1 01:00:00 1970 +++ linux.14p4/drivers/sound/maestro.h Thu Oct 28 21:38:46 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/msnd_pinnacle.c linux.14p4/drivers/sound/msnd_pinnacle.c --- linux.vanilla/drivers/sound/msnd_pinnacle.c Wed Oct 20 01:12:48 1999 +++ linux.14p4/drivers/sound/msnd_pinnacle.c Fri Oct 22 21:25:36 1999 @@ -46,6 +46,7 @@ # include #endif #include +#include #include "sound_config.h" #include "sound_firmware.h" #ifdef MSND_CLASSIC diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/nm256_audio.c linux.14p4/drivers/sound/nm256_audio.c --- linux.vanilla/drivers/sound/nm256_audio.c Wed Oct 20 01:12:48 1999 +++ linux.14p4/drivers/sound/nm256_audio.c Fri Oct 22 21:25:36 1999 @@ -14,6 +14,8 @@ #include #include #include +#include + #include "sound_config.h" #include "soundmodule.h" #include "nm256.h" diff -u --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/opl3.c linux.14p4/drivers/sound/opl3.c --- linux.vanilla/drivers/sound/opl3.c Wed Aug 19 22:48:30 1998 +++ linux.14p4/drivers/sound/opl3.c Fri Oct 22 22:52:53 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/sb_ess.c linux.14p4/drivers/sound/sb_ess.c --- linux.vanilla/drivers/sound/sb_ess.c Sat Aug 28 20:00:58 1999 +++ linux.14p4/drivers/sound/sb_ess.c Fri Oct 29 23:29:02 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/sonicvibes.c linux.14p4/drivers/sound/sonicvibes.c --- linux.vanilla/drivers/sound/sonicvibes.c Wed Oct 20 01:12:48 1999 +++ linux.14p4/drivers/sound/sonicvibes.c Fri Oct 22 22:34:17 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/sound/sound_core.c linux.14p4/drivers/sound/sound_core.c --- linux.vanilla/drivers/sound/sound_core.c Sat Aug 28 20:00:58 1999 +++ linux.14p4/drivers/sound/sound_core.c Fri Oct 29 23:25:13 1999 @@ -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 --exclude-from ../exclude --new-file --recursive linux.vanilla/drivers/video/matroxfb.c linux.14p4/drivers/video/matroxfb.c --- linux.vanilla/drivers/video/matroxfb.c Sat Aug 14 02:26:17 1999 +++ linux.14p4/drivers/video/matroxfb.c Wed Oct 27 18:41:36 1999 @@ -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,9 @@ * "Cort Dougan" * CHRP fixes and PReP cleanup * + * "Mark Vojkovich" + * G400 support + * * (following author is not in any relation with this code, but his code * is included in this driver) * @@ -87,6 +90,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 +135,15 @@ #include