From a74f4d0db123b78e3bab3ddf852c3b32eb312468 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 23 Nov 2007 15:20:07 -0500 Subject: [PATCH] Linux 2.2.14pre1 o Update Alpha architecture slightly (Jay Estabrook) o Multi-apic support (Ingo Molnar) o Fix FC4 compile glitch (Alan Cox) o PCNet 32 speed ups (HP, Alan Cox) o Update oldpci tables o Qlogic Alpha fixes (Jay Estabrook) o Audio compile fixes o knfsd 0.4.7 (HJ Lu et al) o Hashing scale by memory (Dave Miller) o Revert a.out change, switch from timers to (Several people) counts o Support very big disks (Andries Brouwer) o Compile cleanups (Arjan van de Ven) o Fix enabled flag on transparent proxy on (Alan Cox) o SIS900 driver update (SIS) o Small PPC update (Cort Dougan) o Increase loader heap for uncompressing (HJ Lu) o Future domain build fix (Anthony Barbachan) o Bring PCI sound drivers in 2.2.x in line with 2.3.x enhancements (Thomas Sailer) o Documentation fixes (Thierry Vignaud) o Bridge update (more of a rewrite) (Matthew Grant) o Masquerade fix (Juanjo Ciarlante) o Changes file update (Chris Ricker) o NFS zero padding cleanup (Trond Myklebust) o Clean up OPL3 configuration and surplus options (Thomas Molina) o GCC 2.95 patches (Bero Rosenkraenzer) | If you hit problems please verify them with an | older gcc, but you should now be able to build | with 2.95.* to go looking for them... --- CREDITS | 4 +- Documentation/Changes | 102 +-- Documentation/Configure.help | 24 +- Documentation/kernel-parameters.txt | 52 +- Makefile | 8 +- arch/alpha/kernel/process.c | 133 ++- arch/alpha/kernel/proto.h | 1 + arch/alpha/math-emu/fp-emul.c | 12 +- arch/alpha/math-emu/sfp-machine.h | 20 +- arch/i386/boot/compressed/Makefile | 2 +- arch/i386/boot/compressed/misc.c | 2 +- arch/i386/kernel/bios32.c | 1 + arch/i386/kernel/io_apic.c | 165 ++-- arch/i386/kernel/irq.h | 5 + arch/i386/kernel/setup.c | 11 +- arch/i386/kernel/smp.c | 40 +- arch/ppc/common_defconfig | 38 +- arch/ppc/defconfig | 115 ++- arch/ppc/kernel/chrp_pci.c | 7 +- arch/ppc/kernel/head.S | 48 +- arch/ppc/kernel/setup.c | 32 +- arch/ppc/mm/init.c | 32 +- arch/ppc/xmon/xmon.c | 17 +- drivers/block/Config.in | 4 +- drivers/block/genhd.c | 6 + drivers/block/ide-disk.c | 87 +- drivers/block/ide-proc.c | 6 +- drivers/char/bttv.c | 4 +- drivers/char/buz.c | 4 +- drivers/char/radio-aimslab.c | 2 +- drivers/char/radio-cadet.c | 1 + drivers/char/radio-rtrack2.c | 2 +- drivers/fc4/Makefile | 2 - drivers/isdn/avmb1/capi.c | 1 + drivers/macintosh/macserial.c | 565 ++++++++++-- drivers/macintosh/macserial.h | 49 +- drivers/macintosh/mediabay.c | 61 +- drivers/net/Config.in | 4 +- drivers/net/arlan-proc.c | 9 +- drivers/net/hamradio/Config.in | 2 +- drivers/net/pcnet32.c | 164 +++- drivers/net/sis900.c | 1254 +++++++++------------------ drivers/net/sis900.h | 256 ++++++ drivers/pci/oldproc.c | 8 +- drivers/scsi/fdomain.c | 2 + drivers/scsi/gdth.h | 2 + drivers/scsi/ips.h | 7 +- drivers/scsi/megaraid.h | 2 + drivers/scsi/psi240i.h | 3 + drivers/scsi/qlogicfc.c | 2 +- drivers/scsi/qlogicisp.c | 7 + drivers/scsi/sym53c416.h | 2 + drivers/scsi/sym53c8xx_defs.h | 3 +- drivers/scsi/u14-34f.h | 2 + drivers/sound/Config.in | 2 - drivers/sound/Makefile | 1 - drivers/sound/ac97.h | 13 + drivers/sound/adlib_card.c | 3 - drivers/sound/es1370.c | 172 ++-- drivers/sound/es1371.c | 515 +++++++---- drivers/sound/esssolo1.c | 109 ++- drivers/sound/lowlevel/miroaci.h | 5 + drivers/sound/msnd_pinnacle.c | 1 + drivers/sound/nm256_audio.c | 2 + drivers/sound/opl3.c | 4 - drivers/sound/sonicvibes.c | 84 +- fs/binfmt_aout.c | 74 +- fs/buffer.c | 28 +- fs/dcache.c | 46 +- fs/ext2/ialloc.c | 18 +- fs/ext2/inode.c | 5 + fs/ext2/ioctl.c | 1 + fs/filesystems.c | 39 +- fs/inode.c | 52 ++ fs/lockd/lockd_syms.c | 2 + fs/lockd/svc.c | 26 + fs/locks.c | 5 +- fs/nfs/inode.c | 4 + fs/nfsd/nfsctl.c | 80 +- fs/nfsd/nfsfh.c | 382 ++++---- fs/nfsd/nfsproc.c | 6 +- fs/nfsd/nfssvc.c | 13 +- fs/nfsd/stats.c | 5 +- fs/nfsd/vfs.c | 77 +- include/asm-alpha/delay.h | 12 +- include/asm-alpha/io.h | 2 + include/asm-i386/fixmap.h | 5 +- include/linux/fs.h | 1 + include/linux/genhd.h | 2 +- include/linux/lockd/syscall.h | 26 + include/linux/netdevice.h | 3 + include/linux/nfsd/nfsfh.h | 6 + include/linux/nfsd/stats.h | 1 + include/linux/nfsd/syscall.h | 7 +- include/linux/pagemap.h | 16 +- include/linux/pci.h | 14 +- include/linux/proc_fs.h | 3 +- include/linux/sunrpc/svc.h | 3 +- include/linux/sunrpc/svcsock.h | 3 +- include/net/br.h | 36 +- include/net/tcp.h | 25 +- init/main.c | 1 + ipc/sem.c | 2 +- kernel/ksyms.c | 7 + kernel/sysctl.c | 2 + mm/filemap.c | 35 +- net/802/fc.c | 5 +- net/Config.in | 3 + net/bridge/br.c | 687 ++++++++++++++- net/bridge/br_tree.c | 3 +- net/core/dev.c | 14 +- net/ipv4/ip_forward.c | 4 +- net/ipv4/ip_fw.c | 3 + net/ipv4/tcp.c | 41 + net/ipv4/tcp_input.c | 2 +- net/ipv4/tcp_ipv4.c | 32 +- net/ipv4/tcp_timer.c | 8 +- net/ipv6/tcp_ipv6.c | 14 +- net/irda/irlan/irlan_eth.c | 2 + net/netsyms.c | 8 +- net/sunrpc/clnt.c | 3 + net/sunrpc/svc.c | 10 +- net/sunrpc/svcsock.c | 15 +- 123 files changed, 4244 insertions(+), 1990 deletions(-) create mode 100644 drivers/net/sis900.h create mode 100644 include/linux/lockd/syscall.h diff --git a/CREDITS b/CREDITS index 00aab7cb70f5..6aa113c223c1 100644 --- a/CREDITS +++ b/CREDITS @@ -789,12 +789,12 @@ E: ajh@primag.co.uk 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 diff --git a/Documentation/Changes b/Documentation/Changes index 85f40bb4446e..77dc7ee7e00b 100644 --- a/Documentation/Changes +++ b/Documentation/Changes @@ -43,7 +43,7 @@ obtener la traducci 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 @@ running, the suggested command should tell you. - 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 @@ your system, you don't have to upgrade just so the kernel will work 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 @@ Memory ====== 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 @@ Where to get the files Binutils ======== -The 2.8.1.0.23 release: -ftp://tsx-11.mit.edu/pub/linux/packages/GCC/binutils-2.8.1.0.23.bin.tar.gz -ftp://metalab.unc.edu/pub/Linux/GCC/binutils-2.8.1.0.23.bin.tar.gz +The 2.9.1.0.25 release: +ftp://ftp.varesearch.com/pub/support/hjl/binutils/2.9.1/binutils-2.9.1.0.25.tar.gz Installation notes: -ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.binutils-2.8.1.0.23 -ftp://metalab.unc.edu/pub/Linux/GCC/release.binutils-2.8.1.0.23 - -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 +ftp://ftp.varesearch.com/pub/support/hjl/binutils/2.9.1/release.binutils-2.9.1.0.25 + +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 @@ ftp://tsx-11.mit.edu/pub/linux/packages/GCC/bin86-0.4.tar.gz 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 @@ ftp://ftp.kernel.org/pub/linux/kernel/v2.1/modutils-2.1.121.tar.gz 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 @@ DOSEMU ====== 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 @@ Util-linux ========== 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 @@ The user-land 2.2beta40 release: 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 @@ ftp://ftp.samba.org/pub/samba/samba-2.0.5a.tar.gz 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 @@ ftp://metalab.unc.edu/pub/Linux/system/serial/setserial-2.15.tar.gz 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 @@ http://juanjox.linuxhq.com/ipmasqadm-0.4.2.tar.gz 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 @@ ftp://tsx-11.mit.edu/pub/linux/BETA/ibcs2/ibcs-2.1-981105-ALPHA.tar.gz 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 @@ ftp://ftp.uk.linux.org/pub/linux/sct/quota/quota-1.55-10.src.rpm 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 --git a/Documentation/Configure.help b/Documentation/Configure.help index b9173039238d..90d47b5e26ae 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -9422,20 +9422,6 @@ CONFIG_SOUND_SB 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 +9701,15 @@ CONFIG_SOUND_MIDI 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 --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index e1392c277afc..d5ead50f2bd5 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -50,7 +50,7 @@ be entered as an environment variable, whereas its absence indicates that 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] @@ -68,6 +68,8 @@ running once the system is up. arcrimi= [HW,NET] + ataflop= [HW, M68k] + atamouse= [HW,MOUSE] Atari Mouse. atascsi= [HW,SCSI] Atari SCSI. @@ -96,22 +98,22 @@ running once the system is up. 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 @@ running once the system is up. 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 @@ running once the system is up. floppy= [HW] - ftape= [HW] Floppy Tape subsystem. + ftape= [HW] Floppy Tape subsystem debugging options. gdth= [HW,SCSI] @@ -137,7 +139,8 @@ running once the system is up. 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 @@ running once the system is up. 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 @@ running once the system is up. kbd-reset [VT] - load_ramdisk= [RAM] + load_ramdisk= [RAM] initrd loading boolean lp= [LPT] Parallel Printer. @@ -204,7 +215,7 @@ running once the system is up. mcdx= [HW,CD] - md= [HW] + md= [HW] RAID subsystems devices and level mdacon= [MDA] @@ -224,6 +235,8 @@ running once the system is up. 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 @@ running once the system is up. 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 @@ running once the system is up. 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 @@ running once the system is up. pg. [PARIDE] - pirq= [SMP,APIC] + pirq= [SMP,APIC] mp-table plip= [LP,NET] Parallel port network link. @@ -275,13 +294,14 @@ running once the system is up. 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 @@ running once the system is up. 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 @@ running once the system is up. wdt= [HW] - xd= [HW,XT] + xd= [HW,XT] Original XT 8bit disk controllers xd_geo= [HW,XT] diff --git a/Makefile b/Makefile index b5cddda99765..a5cc3f5ce344 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 2 -SUBLEVEL = 13 -EXTRAVERSION = +SUBLEVEL = 14 +EXTRAVERSION = pre1 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) @@ -381,6 +381,10 @@ mrproper: clean archmrproper 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 --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c index 19109d12e1a1..86dcb2dc3c50 100644 --- a/arch/alpha/kernel/process.c +++ b/arch/alpha/kernel/process.c @@ -117,58 +117,44 @@ sys_idle(void) } } -void -generic_kill_arch (int mode, char *restart_cmd) +struct halt_info { + int mode; + char * restart_cmd; +}; + +static void +halt_processor(void * generic_ptr) { - /* 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. */ + struct percpu_struct * cpup; + struct halt_info * how = (struct halt_info *)generic_ptr; + unsigned long *flags; + int cpuid = smp_processor_id(); - 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(); + /* No point in taking interrupts anymore. */ + __cli(); - reset_for_srm(); - set_hae(srm_hae); + cpup = (struct percpu_struct *) + ((unsigned long)hwrpb + hwrpb->processor_offset + + hwrpb->processor_size * cpuid); + flags = &cpup->flags; -#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 + /* 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,24 +163,75 @@ generic_kill_arch (int mode, char *restart_cmd) control |= RTC_PIE; CMOS_WRITE(control, RTC_CONTROL); CMOS_READ(RTC_INTR_FLAGS); - - sti(); } -#endif +#endif /* CONFIG_RTC */ + + 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" */ - if (!alpha_using_srm && mode != LINUX_REBOOT_CMD_RESTART) { +#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 machine_restart(char *restart_cmd) { diff --git a/arch/alpha/kernel/proto.h b/arch/alpha/kernel/proto.h index 12f181b98c3d..743fc1d64eb4 100644 --- a/arch/alpha/kernel/proto.h +++ b/arch/alpha/kernel/proto.h @@ -153,6 +153,7 @@ extern int smp_info(char *buffer); 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 --git a/arch/alpha/math-emu/fp-emul.c b/arch/alpha/math-emu/fp-emul.c index 9ab3b710b2bc..16651b09643b 100644 --- a/arch/alpha/math-emu/fp-emul.c +++ b/arch/alpha/math-emu/fp-emul.c @@ -85,15 +85,15 @@ void cleanup_module(void) /* 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 @@ udiv128(unsigned long divisor_f0, unsigned long divisor_f1, } out: - __real__ ret = quo_f1; - __imag__ ret = rem_f1; - return ret; + *quot = quo_f1; + *remd = rem_f1; + return; } /* diff --git a/arch/alpha/math-emu/sfp-machine.h b/arch/alpha/math-emu/sfp-machine.h index 9557f5aa6dda..710eade038d9 100644 --- a/arch/alpha/math-emu/sfp-machine.h +++ b/arch/alpha/math-emu/sfp-machine.h @@ -552,15 +552,17 @@ do { \ : "r" ((UDItype)(u)), \ "r" ((UDItype)(v))) -extern __complex__ unsigned long udiv128(unsigned long, unsigned long, - unsigned long, unsigned long); - -#define udiv_qrnnd(q, r, n1, n0, d) \ - do { \ - __complex__ unsigned long x_; \ - x_ = udiv128((n0), (n1), 0, (d)); \ - (q) = __real__ x_; \ - (r) = __imag__ x_; \ +extern void udiv128(unsigned long, unsigned long, + unsigned long, unsigned long, + unsigned long *, + unsigned long *); + +#define udiv_qrnnd(q, r, n1, n0, d) \ + do { \ + unsigned long xr, xi; \ + udiv128((n0), (n1), 0, (d), &xr, &xi); \ + (q) = xr; \ + (r) = xi; \ } while (0) #define UDIV_NEEDS_NORMALIZATION 1 diff --git a/arch/i386/boot/compressed/Makefile b/arch/i386/boot/compressed/Makefile index 30404dc3772b..6a3fda06dcc7 100644 --- a/arch/i386/boot/compressed/Makefile +++ b/arch/i386/boot/compressed/Makefile @@ -37,7 +37,7 @@ piggy.o: $(SYSTEM) 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 --git a/arch/i386/boot/compressed/misc.c b/arch/i386/boot/compressed/misc.c index ad78c419d988..89beaa70fb2a 100644 --- a/arch/i386/boot/compressed/misc.c +++ b/arch/i386/boot/compressed/misc.c @@ -104,7 +104,7 @@ static long free_mem_end_ptr = 0x90000; #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 --git a/arch/i386/kernel/bios32.c b/arch/i386/kernel/bios32.c index 91d338b2c2f4..44a8b61f3c04 100644 --- a/arch/i386/kernel/bios32.c +++ b/arch/i386/kernel/bios32.c @@ -1097,6 +1097,7 @@ static void __init pcibios_scan_buglist(struct pci_bus *b) extern int skip_ioapic_setup; +extern int skip_ioapic_setup; /* defined in arch/i386/kernel/smp.c */ static void __init pcibios_fixup_devices(void) { struct pci_dev *dev; diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c index b56b379bf514..76854d4553b3 100644 --- a/arch/i386/kernel/io_apic.c +++ b/arch/i386/kernel/io_apic.c @@ -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 @@ struct IO_APIC_reg_02 { /* * # 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 @@ enum mp_irq_source_types { 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 @@ int mpc_default_type = 0; /* non-0 if default (table-less) * 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 @@ static inline void io_apic_sync(void) #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 @@ static struct irq_pin_list { * 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 @@ static void add_pin_to_irq(unsigned int irq, int pin) if (++first_free_entry >= PIN_MAP_SIZE) panic("io_apic.c: whoops"); } + entry->apic = apic; entry->pin = pin; } @@ -183,9 +186,9 @@ static void name##_IO_APIC_irq(unsigned int irq) \ 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 @@ static void name##_IO_APIC_irq(unsigned int irq) \ * 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 @@ static void clear_IO_APIC_pin(unsigned int pin) */ 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 @@ void __init ioapic_pirq_setup(char *str, int *ints) /* * 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 @@ static int __init find_timer_pin(int type) * 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 @@ static inline int irq_trigger(int idx) 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 @@ static int __init pin_2_irq(int idx, int pin) 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 int __init pin_2_irq(int idx, int pin) 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 @@ static int __init assign_irq_vector(int irq) 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 @@ void __init setup_IO_APIC_irqs(void) 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 @@ void __init setup_IO_APIC_irqs(void) 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 @@ void __init setup_IO_APIC_irqs(void) /* * 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 @@ void __init setup_ExtINT_pin(unsigned int pin, int irq) 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 UNEXPECTED_IO_APIC(void) 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 @@ void __init print_IO_APIC(void) */ 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 @@ void __init print_IO_APIC(void) 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 @@ void __init print_IO_APIC(void) 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 @@ static void __init init_sym_mode(void) */ { 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 @@ static void __init setup_ioapic_id(void) /* * 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 @@ static inline void check_timer(void) 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 @@ static inline void check_timer(void) * 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 @@ void __init setup_IO_APIC(void) * - 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 --git a/arch/i386/kernel/irq.h b/arch/i386/kernel/irq.h index 5187df93bca5..6d1c31212cb3 100644 --- a/arch/i386/kernel/irq.h +++ b/arch/i386/kernel/irq.h @@ -112,6 +112,11 @@ extern unsigned long io_apic_irqs; 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 --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 98a22a275f1d..b225637b8c37 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -366,7 +366,7 @@ __initfunc(void setup_arch(char **cmdline_p, /* 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 +755,8 @@ static struct cpu_model_info cpu_models[] __initdata = { { 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", @@ -1019,8 +1019,9 @@ int get_cpuinfo(char * buffer) 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 --git a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c index b087e6bf2029..3a52d51138a7 100644 --- a/arch/i386/kernel/smp.c +++ b/arch/i386/kernel/smp.c @@ -128,6 +128,8 @@ volatile unsigned long ipi_count; /* Number of IPIs delivered */ 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 @@ static int __init smp_read_mpc(struct mp_config_table *mpc) 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 @@ static int __init smp_read_mpc(struct mp_config_table *mpc) } } } - 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 @@ unsigned long __init init_smp_mappings(unsigned long memory_start) #ifdef CONFIG_X86_IO_APIC { - unsigned long ioapic_phys; - - 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; + unsigned long ioapic_phys, idx = FIX_IO_APIC_BASE_0; + int i; + + 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 --git a/arch/ppc/common_defconfig b/arch/ppc/common_defconfig index f0ae093fa61a..2fef5a644c58 100644 --- a/arch/ppc/common_defconfig +++ b/arch/ppc/common_defconfig @@ -91,8 +91,10 @@ CONFIG_BLK_DEV_LOOP=y 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_BLK_DEV_SD=y CONFIG_CHR_DEV_ST=y CONFIG_BLK_DEV_SR=y CONFIG_BLK_DEV_SR_VENDOR=y -# CONFIG_CHR_DEV_SG is not set +CONFIG_CHR_DEV_SG=y # # Some SCSI devices (e.g. CD jukebox) support multiple LUNs @@ -226,10 +228,19 @@ CONFIG_SCSI_MAC53C94=y # 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_BMAC=y # 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_DE4X5=y # 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_PPP=y # # 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=y 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_DEVPTS_FS=y 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_CODEPAGE_437=y # 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_SOUND=y 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 --git a/arch/ppc/defconfig b/arch/ppc/defconfig index c63ca94797f9..2fef5a644c58 100644 --- a/arch/ppc/defconfig +++ b/arch/ppc/defconfig @@ -9,14 +9,13 @@ CONFIG_PPC=y CONFIG_6xx=y # CONFIG_PPC64 is not set # CONFIG_8xx is not set -CONFIG_PMAC=y +# CONFIG_PMAC is not set # CONFIG_PREP is not set # CONFIG_CHRP is not set -# CONFIG_ALL_PPC is not set +CONFIG_ALL_PPC=y # CONFIG_APUS is not set # CONFIG_MBX is not set # CONFIG_SMP is not set -CONFIG_MACH_SPECIFIC=y CONFIG_6xx=y # @@ -24,7 +23,7 @@ CONFIG_6xx=y # 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_SYSVIPC=y # 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_PROC_DEVICETREE=y # 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 @@ CONFIG_BOOTX_TEXT=y # # 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_IDEFLOPPY=y # 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_RAM=y 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_IP_MULTICAST=y # 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_BLK_DEV_SD=y CONFIG_CHR_DEV_ST=y CONFIG_BLK_DEV_SR=y CONFIG_BLK_DEV_SR_VENDOR=y -# CONFIG_CHR_DEV_SG is not set +CONFIG_CHR_DEV_SG=y # # Some SCSI devices (e.g. CD jukebox) support multiple LUNs @@ -200,14 +191,14 @@ CONFIG_AIC7XXX_RESET_DELAY=15 # 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_SYNC=20 # CONFIG_SCSI_NCR53C8XX_PROFILE is not set # CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set # CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set -CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT=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_BMAC=y # 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_DE4X5=y # 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_OF=y 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_FONT_SUN12x22=y # 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 @@ CONFIG_NVRAM=y # 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_AUTOFS_FS=y # 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_CODEPAGE_437=y # 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_DMASOUND=y # 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 --git a/arch/ppc/kernel/chrp_pci.c b/arch/ppc/kernel/chrp_pci.c index 8fc5c6d3dc1c..771c51364fd7 100644 --- a/arch/ppc/kernel/chrp_pci.c +++ b/arch/ppc/kernel/chrp_pci.c @@ -287,9 +287,6 @@ chrp_pcibios_fixup(void) { 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 @@ chrp_pcibios_fixup(void) 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 --git a/arch/ppc/kernel/head.S b/arch/ppc/kernel/head.S index b5c1e9328730..47a1777a5a67 100644 --- a/arch/ppc/kernel/head.S +++ b/arch/ppc/kernel/head.S @@ -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 @@ LG_CACHE_LINE_SIZE = 4 /* 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 @@ __secondary_start: 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 @@ hash_page: #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 @@ swapper_pg_dir: .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 --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c index 5990fe036723..71aa08b8a2ff 100644 --- a/arch/ppc/kernel/setup.c +++ b/arch/ppc/kernel/setup.c @@ -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 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, 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 } __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 @@ __initfunc(void setup_arch(char **cmdline_p, #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 @@ __initfunc(void setup_arch(char **cmdline_p, /* 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 --git a/arch/ppc/mm/init.c b/arch/ppc/mm/init.c index cd338558eaf6..bff308e2bfe2 100644 --- a/arch/ppc/mm/init.c +++ b/arch/ppc/mm/init.c @@ -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 @@ unsigned long inline p_mapped_by_bats(unsigned long); */ 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 @@ __initfunc(unsigned long *pmac_find_end_of_memory(void)) 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 @@ __initfunc(unsigned long *pmac_find_end_of_memory(void)) * 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 @@ __initfunc(unsigned long *pmac_find_end_of_memory(void)) 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 --git a/arch/ppc/xmon/xmon.c b/arch/ppc/xmon/xmon.c index 57bfb9a49f07..f9a485371869 100644 --- a/arch/ppc/xmon/xmon.c +++ b/arch/ppc/xmon/xmon.c @@ -115,9 +115,13 @@ xmon(struct pt_regs *excp) { 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 @@ at_breakpoint(unsigned pc) 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 @@ insert_bpts() 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 @@ remove_bpts() 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 --git a/drivers/block/Config.in b/drivers/block/Config.in index 38d7a475ccf4..3c9dee4de4bc 100644 --- a/drivers/block/Config.in +++ b/drivers/block/Config.in @@ -131,7 +131,9 @@ dep_tristate 'Parallel port IDE device support' CONFIG_PARIDE $CONFIG_PARIDE_PAR 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 --git a/drivers/block/genhd.c b/drivers/block/genhd.c index 63dba9e5db41..dacaaecf133a 100644 --- a/drivers/block/genhd.c +++ b/drivers/block/genhd.c @@ -784,8 +784,11 @@ static int sun_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sec 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 @@ static int sgi_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sec 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 --git a/drivers/block/ide-disk.c b/drivers/block/ide-disk.c index 14ad6269ec2f..cd37e6a720e2 100644 --- a/drivers/block/ide-disk.c +++ b/drivers/block/ide-disk.c @@ -88,30 +88,35 @@ static inline void idedisk_output_data (ide_drive_t *drive, void *buffer, unsign */ 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 */ } /* @@ -446,7 +451,6 @@ static unsigned long idedisk_capacity (ide_drive_t *drive) /* 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; } @@ -721,9 +725,9 @@ static void idedisk_setup (ide_drive_t *drive) 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 +737,19 @@ static void idedisk_setup (ide_drive_t *drive) * 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 */ @@ -771,19 +773,6 @@ static void idedisk_setup (ide_drive_t *drive) } 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) { #if 1 /* original, pre IDE-NFG, per request of AC */ diff --git a/drivers/block/ide-proc.c b/drivers/block/ide-proc.c index 26a56e740690..a2bc04c47798 100644 --- a/drivers/block/ide-proc.c +++ b/drivers/block/ide-proc.c @@ -516,8 +516,10 @@ int proc_ide_read_geometry 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 --git a/drivers/char/bttv.c b/drivers/char/bttv.c index 68c2640774c6..c5be768fa4fb 100644 --- a/drivers/char/bttv.c +++ b/drivers/char/bttv.c @@ -403,7 +403,7 @@ static void writeee(struct i2c_bus *bus, unsigned char *eedata) } } -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 attach_inform(struct i2c_bus *bus, int id) } } -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 --git a/drivers/char/buz.c b/drivers/char/buz.c index 34628a13b94f..447767ab0c38 100644 --- a/drivers/char/buz.c +++ b/drivers/char/buz.c @@ -374,13 +374,13 @@ static int i2c_getdataline(struct i2c_bus *bus) 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 --git a/drivers/char/radio-aimslab.c b/drivers/char/radio-aimslab.c index 99c1b92bcaf3..48c7b163e708 100644 --- a/drivers/char/radio-aimslab.c +++ b/drivers/char/radio-aimslab.c @@ -195,7 +195,7 @@ static int rt_setfreq(struct rt_device *dev, unsigned long freq) 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 --git a/drivers/char/radio-cadet.c b/drivers/char/radio-cadet.c index 37ae10a2a40c..7404083be2bf 100644 --- a/drivers/char/radio-cadet.c +++ b/drivers/char/radio-cadet.c @@ -39,6 +39,7 @@ struct timer_list tunertimer,rdstimer,readtimer; 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 --git a/drivers/char/radio-rtrack2.c b/drivers/char/radio-rtrack2.c index 793548839803..29912a62e663 100644 --- a/drivers/char/radio-rtrack2.c +++ b/drivers/char/radio-rtrack2.c @@ -89,7 +89,7 @@ static int rt_setfreq(struct rt_device *dev, unsigned long freq) 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 --git a/drivers/fc4/Makefile b/drivers/fc4/Makefile index 540167bbfc03..7b88ecc149d2 100644 --- a/drivers/fc4/Makefile +++ b/drivers/fc4/Makefile @@ -7,8 +7,6 @@ L_TARGET := fc4.a M_OBJS := MOD_LIST_NAME := FC4_MODULES -include ../../.config - ifeq ($(CONFIG_FC4),y) FC4 = fc.o ifeq ($(CONFIG_MODULES),y) diff --git a/drivers/isdn/avmb1/capi.c b/drivers/isdn/avmb1/capi.c index 2fb1b6cbb37c..e2942e133fc2 100644 --- a/drivers/isdn/avmb1/capi.c +++ b/drivers/isdn/avmb1/capi.c @@ -109,6 +109,7 @@ */ #include +#include #include #include #include diff --git a/drivers/macintosh/macserial.c b/drivers/macintosh/macserial.c index 576b9e2003c7..4553616d2520 100644 --- a/drivers/macintosh/macserial.c +++ b/drivers/macintosh/macserial.c @@ -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 @@ static struct pmu_sleep_notifier serial_sleep_notifier = { }; #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 change_speed(struct mac_serial *info, struct termios *old); 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 @@ static struct semaphore tmp_buf_sem = MUTEX; __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 @@ static inline int serial_paranoia_check(struct mac_serial *info, * 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 unsigned char read_zsreg(struct mac_zschannel *channel, } static inline void write_zsreg(struct mac_zschannel *channel, - unsigned char reg, unsigned char value) + unsigned char reg, unsigned char value) { unsigned long flags; @@ -289,6 +302,39 @@ static inline void rs_recv_clear(struct mac_zschannel *zsc) write_zsreg(zsc, 0, RES_H_IUS); /* XXX this is unnecessary */ } +/* + * 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); +} + /* * ---------------------------------------------------------------------- * @@ -312,6 +358,22 @@ static _INLINE_ void rs_sched_event(struct mac_serial *info, 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 @@ static _INLINE_ void receive_chars(struct mac_serial *info, 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 @@ static _INLINE_ void status_handle(struct mac_serial *info) 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 @@ static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs) 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 @@ static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs) 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 @@ static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs) } } +/* 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 do_softint(void *private_) } } -static void rs_timer(void) -{ -} - static int startup(struct mac_serial * info, int can_sleep) { int delay; @@ -631,6 +749,10 @@ static int startup(struct mac_serial * info, int can_sleep) 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 @@ static int startup(struct mac_serial * info, int can_sleep) 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; @@ -668,6 +971,12 @@ static int setup_scc(struct mac_serial * info) ZS_CLEARFIFO(info->zs_channel); info->xmit_fifo_size = 1; + /* + * Reset DMAs + */ + if (info->has_dma) + dma_init(info); + /* * Clear the interrupt registers. */ @@ -682,7 +991,23 @@ static int setup_scc(struct mac_serial * info) /* * 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 @@ static int setup_scc(struct mac_serial * info) 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 @@ static void shutdown(struct mac_serial * info) 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 @@ static void shutdown(struct mac_serial * info) 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 @@ static int rs_write(struct tty_struct * tty, int from_user, 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 @@ static int rs_write(struct tty_struct * tty, int from_user, 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 @@ static int rs_write(struct tty_struct * tty, int from_user, 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 @@ static int rs_write(struct tty_struct * tty, int from_user, 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 int rs_chars_in_buffer(struct tty_struct *tty) 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 @@ static void rs_throttle(struct tty_struct * tty) 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 @@ static void rs_unthrottle(struct tty_struct * tty) 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 @@ static void rs_close(struct tty_struct *tty, struct file * filp) save_flags(flags); cli(); if (tty_hung_up_p(filp)) { + MOD_DEC_USE_COUNT; restore_flags(flags); return; } @@ -1498,6 +1844,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) info->count = 0; } if (info->count) { + MOD_DEC_USE_COUNT; restore_flags(flags); return; } @@ -1518,8 +1865,12 @@ static void rs_close(struct tty_struct *tty, struct file * filp) 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 @@ static void rs_close(struct tty_struct *tty, struct file * filp) #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 @@ static void rs_close(struct tty_struct *tty, struct file * filp) info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE| ZILOG_CLOSING); wake_up_interruptible(&info->close_wait); + MOD_DEC_USE_COUNT; } /* @@ -1603,7 +1957,6 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) 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 @@ static int rs_open(struct tty_struct *tty, struct file * filp) 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 int rs_open(struct tty_struct *tty, struct file * filp) 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 @@ probe_sccs() { 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 @@ int macserial_init(void) /* 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 @@ int macserial_init(void) /* 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 @@ void cleanup_module(void) 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 @@ __initfunc(static int serial_console_setup(struct console *co, char *options)) 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 @@ __initfunc(void zs_kgdb_hook(int tty_num)) 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 --git a/drivers/macintosh/macserial.h b/drivers/macintosh/macserial.h index 39b55a4d635a..188589d1955d 100644 --- a/drivers/macintosh/macserial.h +++ b/drivers/macintosh/macserial.h @@ -92,6 +92,13 @@ struct mac_zschannel { 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 mac_serial { 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 @@ struct mac_serial { #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 @@ struct mac_serial { /* 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 @@ struct mac_serial { #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 @@ struct mac_serial { #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 --git a/drivers/macintosh/mediabay.c b/drivers/macintosh/mediabay.c index 5220588d8ee3..19d5a73a1aa6 100644 --- a/drivers/macintosh/mediabay.c +++ b/drivers/macintosh/mediabay.c @@ -94,6 +94,12 @@ int media_bay_count = 0; */ #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 @@ media_bay_task(void *x) #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 @@ media_bay_task(void *x) } 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 @@ mb_notify_sleep(struct pmu_sleep_notifier *self, int when) 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 --git a/drivers/net/Config.in b/drivers/net/Config.in index 61716407532a..205bd758acb5 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -165,7 +165,9 @@ if [ "$CONFIG_FDDI" = "y" ]; then 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 diff --git a/drivers/net/arlan-proc.c b/drivers/net/arlan-proc.c index 2d48479e0ca7..fedeb277924e 100644 --- a/drivers/net/arlan-proc.c +++ b/drivers/net/arlan-proc.c @@ -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 @@ static ctl_table arlan_table[MAX_ARLANS + 1] = {0} }; #endif +#else +static ctl_table arlan_table[MAX_ARLANS + 1] = +{ + {0} +}; #endif static int mmtu = 1234; diff --git a/drivers/net/hamradio/Config.in b/drivers/net/hamradio/Config.in index 1809dadf2e7a..869c3710078b 100644 --- a/drivers/net/hamradio/Config.in +++ b/drivers/net/hamradio/Config.in @@ -29,4 +29,4 @@ if [ "$CONFIG_SOUNDMODEM" != "n" ]; then 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 --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index 68e87ebd408b..ac64e3759890 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -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 @@ static const char *version = "pcnet32.c:v1.23 6.7.1999 tsbogend@alpha.franken.de #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 @@ static int full_duplex[MAX_UNITS] = {0, }; * 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 @@ static int full_duplex[MAX_UNITS] = {0, }; */ #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 pcnet32_private { 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 @@ static struct pcnet32_pci_id_info pcnet32_tbl[] = { 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 __init pcnet32_probe (struct device *dev) 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 @@ pcnet32_probe1(struct device *dev, unsigned long ioaddr, unsigned char irq_line, { 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 @@ pcnet32_probe1(struct device *dev, unsigned long ioaddr, unsigned char irq_line, 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 @@ pcnet32_probe1(struct device *dev, unsigned long ioaddr, unsigned char irq_line, 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 @@ pcnet32_probe1(struct device *dev, unsigned long ioaddr, unsigned char irq_line, 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 @@ pcnet32_probe1(struct device *dev, unsigned long ioaddr, unsigned char irq_line, 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 @@ pcnet32_open(struct device *dev) 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 @@ pcnet32_open(struct device *dev) 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 @@ pcnet32_start_xmit(struct sk_buff *skb, struct device *dev) { 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 @@ pcnet32_start_xmit(struct sk_buff *skb, struct device *dev) 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 @@ pcnet32_start_xmit(struct sk_buff *skb, struct device *dev) 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 @@ pcnet32_start_xmit(struct sk_buff *skb, struct device *dev) 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 @@ pcnet32_interrupt(int irq, void *dev_id, struct pt_regs * regs) 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_interrupt(int irq, void *dev_id, struct pt_regs * regs) 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 @@ pcnet32_interrupt(int irq, void *dev_id, struct pt_regs * regs) 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 @@ pcnet32_interrupt(int irq, void *dev_id, struct pt_regs * regs) dev->name, lp->a.read_csr (ioaddr, 0)); dev->interrupt = 0; + + spin_unlock(&lp->lock); return; } @@ -1252,12 +1375,11 @@ pcnet32_get_stats(struct device *dev) 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 @@ static int pcnet32_mii_ioctl(struct device *dev, struct ifreq *rq, int cmd) 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 --git a/drivers/net/sis900.c b/drivers/net/sis900.c index 402da03e6b84..fa751547394a 100644 --- a/drivers/net/sis900.c +++ b/drivers/net/sis900.c @@ -1,58 +1,28 @@ -/*****************************************************************************/ -/* 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 + + Ollie Lho (ollie@sis.com.tw) + 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 @@ -70,293 +40,59 @@ static int full_duplex[MAX_UNITS] = {1, 1, 1, 1, 1, 1, 1, 1}; #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.05 8/07/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; + +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (4*HZ) -struct pci_id_info { +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_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 device * sis900_mac_probe (struct mac_chip_info * mac, struct pci_dev * pci_dev, + struct device * net_dev); + +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 struct mii_chip_info { + const char * name; + u16 phy_id0; + u16 phy_id1; +} mii_chip_table[] = { + {"SiS 900 Internal MII PHY", 0x001d, 0x8000}, + {"SiS 7014 Physical Layer Solution", 0x0016, 0xf830}, + {"AMD 79C901 10BASE-T PHY", 0x0000, 0x35b9}, + {"AMD 79C901 HomePNA PHY", 0x0000, 0x35c8}, + {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; @@ -368,19 +104,15 @@ typedef struct _EuphLiteDesc { } EuphLiteDesc; 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. */ + struct pci_dev * pci_dev; + + struct mac_chip_info * mac; + struct mii_phy * mii; + + 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(). */ @@ -388,15 +120,12 @@ struct sis900_private { 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. */ + unsigned char *tx_bufs; /* Tx bounce buffer region. */ + 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; }; @@ -414,8 +143,10 @@ MODULE_PARM(debug, "i"); #endif static int sis900_open(struct device *dev); +static int sis900_mii_probe (struct device * dev); +static void sis900_init_rxfilter (struct device * dev); static u16 read_eeprom(long ioaddr, int location); -static int mdio_read(struct device *dev, int phy_id, int location); +static u16 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 void sis900_timer(unsigned long data); static void sis900_tx_timeout(struct device *dev); @@ -431,314 +162,199 @@ 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 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); /* 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) +/* 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 cards_found = 0; - int pci_index = 0; - unsigned char pci_bus, pci_device_fn; - - if ( ! pcibios_present()) - return -ENODEV; - - for (;pci_index < 0xff; pci_index++) { - u16 vendor, device, pci_command, new_command; - int chip_idx, irq; - long ioaddr; - - if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, - pci_index, - &pci_bus, &pci_device_fn) - != PCIBIOS_SUCCESSFUL) { - break; - } - pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, - &vendor); - pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, - &device); - - for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++) - if (vendor == pci_tbl[chip_idx].vendor_id && - (device & pci_tbl[chip_idx].device_id_mask) == - pci_tbl[chip_idx].device_id) - break; - if (pci_tbl[chip_idx].vendor_id == 0) /* Compiled out! */ - continue; - - { -#if defined(PCI_SUPPORT_VER2) - struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn); - ioaddr = pdev->base_address[0] & ~3; - irq = pdev->irq; -#else - u32 pci_ioaddr; - u8 pci_irq_line; - pcibios_read_config_byte(pci_bus, pci_device_fn, - PCI_INTERRUPT_LINE, &pci_irq_line); - pcibios_read_config_dword(pci_bus, pci_device_fn, - PCI_BASE_ADDRESS_0, &pci_ioaddr); - ioaddr = pci_ioaddr & ~3; - irq = pci_irq_line; -#endif - } - - if ((pci_tbl[chip_idx].flags & PCI_USES_IO) && - check_region(ioaddr, pci_tbl[chip_idx].io_size)) - continue; - - /* Activate the card: fix for brain-damaged Win98 BIOSes. */ - pcibios_read_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, &pci_command); - new_command = pci_command | (pci_tbl[chip_idx].flags & 7); - if (pci_command != new_command) { - printk(KERN_INFO " The PCI BIOS has not enabled the" - " device at %d/%d!" - "Updating PCI command %4.4x->%4.4x.\n", - pci_bus, pci_device_fn, - pci_command, new_command); - - pcibios_write_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, new_command); - } - - 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; + 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; } -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 struct device * sis900_mac_probe (struct mac_chip_info * mac, struct pci_dev * pci_dev, + struct device * net_dev) { - 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; - - tp->next_module = root_sis900_dev; - root_sis900_dev = dev; - - 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; - } - } + 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 "Error EERPOM read %x\n", signature); + 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; - } + if ((net_dev = init_etherdev(net_dev, 0)) == NULL) + return NULL; + + printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ", net_dev->name, mac->name, + ioaddr, irq); + + /* 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) + /* FIXME: possible mem leak here */ + return NULL; + + sis_priv = net_dev->priv; + memset(sis_priv, 0, sizeof(struct sis900_private)); + + /* 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) { + /* FIXME: how to clean up this */ + release_region (ioaddr, mac->io_size); + return NULL; + } - 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); - } - } + sis_priv->next_module = root_sis900_dev; + root_sis900_dev = net_dev; - if (tp->pmd_status & MIISTAT_LINK) - tp->LinkOn = TRUE; - else - tp->LinkOn = FALSE; + /* 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; - tp->LinkChange = FALSE; - - } + return net_dev; +} - 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); - } +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 address */ + 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, quit searching (table) */ + break; + } + } + + if (sis_priv->mii == NULL) { + printk(KERN_INFO "%s: No MII transceivers found!\n", net_dev->name); + return 0; } - /* 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; + /* FIXME: AMD stuff should be added */ + /* auto negotiate FIXME: not completed */ + elSetCapability(net_dev, sis_priv->mii->phy_addr, 1, 100); + sis_priv->mii->status = elAutoNegotiate(net_dev, sis_priv->mii->phy_addr, + &sis_priv->full_duplex, + &sis_priv->speeds); + + if (sis_priv->mii->status & MIISTAT_LINK) + sis_priv->LinkOn = TRUE; + else + sis_priv->LinkOn = FALSE; + + sis_priv->LinkChange = FALSE; + + return 1; } -/* 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. */ - -/* Delay between EEPROM clock transitions. - No extra delay is needed with 33Mhz PCI, but 66Mhz may change this. - */ - +/* Delay between EEPROM clock transitions. */ #define eeprom_delay() inl(ee_addr) -/* 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 - +/* 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 +364,7 @@ static u16 read_eeprom(long ioaddr, int location) 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 +375,7 @@ static u16 read_eeprom(long ioaddr, int location) 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 +384,21 @@ static u16 read_eeprom(long ioaddr, int location) 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 +420,11 @@ static void mdio_reset(long mdio_addr) return; } -static int mdio_read(struct device *dev, int phy_id, int location) +static u16 mdio_read(struct device *dev, int phy_id, int location) { long mdio_addr = 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(); @@ -858,7 +459,7 @@ static void mdio_write(struct device *dev, int phy_id, int location, int value) mdio_idle(mdio_addr); /* Shift the command bits out. */ - for (i = 31; i >= 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 +467,17 @@ static void mdio_write(struct device *dev, int phy_id, int location, int value) 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); @@ -879,110 +491,62 @@ static void mdio_write(struct device *dev, int phy_id, int location, int value) static int sis900_open(struct device *dev) { - struct sis900_private *tp = (struct sis900_private *)dev->priv; + struct sis900_private *sis_priv = (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); + int i = 0; + u32 status = TxRCMP | RxRCMP; /* Soft reset the chip. */ - outl(0, ioaddr + imr); - outl(0, ioaddr + ier); - outl(0, ioaddr + rfcr); - outl(RESET | RxRESET | TxRESET, ioaddr + cr); + sis900_reset(dev); - if (request_irq(dev->irq, &sis900_interrupt, SA_SHIRQ, dev->name, dev)) - { + if (request_irq(dev->irq, &sis900_interrupt, SA_SHIRQ, dev->name, dev)) { return -EAGAIN; } 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; - } + if ((sis_priv->tx_bufs = kmalloc(TX_BUF_SIZE * NUM_TX_DESC, GFP_KERNEL)) == NULL) { + printk(KERN_ERR "%s: Can't allocate a %d byte TX Bufs.\n", + dev->name, TX_BUF_SIZE * NUM_TX_DESC); + return -ENOMEM; + } + if ((sis_priv->rx_bufs = kmalloc(RX_BUF_SIZE * NUM_RX_DESC, GFP_KERNEL)) == NULL) { + kfree (sis_priv->tx_buf); + 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_rxfilter(dev); - sis900_init_ring(dev); - outl((u32)tp->tx_buf[0].physAddr, ioaddr + txdp); - outl((u32)tp->rx_buf[0].physAddr, ioaddr + rxdp); + sis900_reset_tx_ring(dev); + sis900_reset_rx_ring(dev); 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); - } + printk(KERN_INFO "%s: txdp:%8.8x\n", dev->name, inl(ioaddr + txdp)); + + /* Check that the chip has finished the reset. */ + while (status && (i++ < 30000)) { + status ^= (inl(isr + ioaddr) & status); } outl(PESEL, ioaddr + cfg); - /* Must enable Tx/Rx before setting transfer thresholds! */ - /* - * #define TX_DMA_BURST 0 - * #define RX_DMA_BURST 0 - * #define TX_FIFO_THRESH 16 - * #define TxDRNT_100 (1536>>5) - * #define TxDRNT_10 (1536>>5) - * #define RxDRNT_100 (1536>>5) - * #define RxDRNT_10 (1536>>5) - */ - outl((RX_DMA_BURST<<20) | (RxDRNT_10 << 1), ioaddr+rxcfg); - outl(TxATP | (TX_DMA_BURST << 20) | (TX_FIFO_THRESH<<8) | TxDRNT_10, - ioaddr + txcfg); - if (sis900_debug > 1) - { - if (tp->LinkOn) { - printk(KERN_INFO"%s: Media Type %s%s-duplex.\n", - dev->name, - tp->speeds==HW_SPEED_100_MBPS ? - "100mbps " : "10mbps ", - tp->full_duplex== FDX_CAPABLE_FULL_SELECTED ? - "full" : "half"); - } - else - { - printk(KERN_INFO"%s: Media Link Off\n", dev->name); - } + /* FIXME: should be removed, and replaced by AutoNeogotiate stuff */ + outl((RX_DMA_BURST << RxMXDMA_shift) | (RxDRNT_10 << RxDRNT_shift), + ioaddr + rxcfg); + outl(TxATP | (TX_DMA_BURST << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift) | TxDRNT_10, + ioaddr + txcfg); + + if (sis_priv->LinkOn) { + printk(KERN_INFO "%s: Media Type %s%s-duplex.\n", + dev->name, + sis_priv->speeds == HW_SPEED_100_MBPS ? "100mbps " : "10mbps ", + sis_priv->full_duplex == FDX_CAPABLE_FULL_SELECTED ? "full" : "half"); + } else { + printk(KERN_INFO "%s: Media Link Off\n", dev->name); } + set_rx_mode(dev); dev->tbusy = 0; @@ -990,56 +554,77 @@ sis900_open(struct device *dev) dev->start = 1; /* Enable all known interrupts by setting the interrupt mask. */ - outl((RxOK|RxERR|RxORN|RxSOVR|TxOK|TxERR|TxURN), ioaddr + imr); + outl((RxRCMP|RxOK|RxERR|RxORN|RxSOVR|TxOK|TxERR|TxURN), 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); - /* 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 + 2*HZ; + sis_priv->timer.data = (unsigned long)dev; + sis_priv->timer.function = &sis900_timer; + add_timer(&sis_priv->timer); return 0; } +/* set receive filter address to our MAC address */ +static void +sis900_init_rxfilter (struct device * net_dev) +{ + long ioaddr = net_dev->base_addr; + u32 rfcrSave; + u32 i; + + rfcrSave = inl(rfcr + ioaddr); + + /* disable packet filtering before setting filter */ + outl(rfcrSave & ~RFEN, rfcr); + + 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 > 4) { + printk(KERN_INFO "%s: Receive Filter Addrss[%d]=%x\n", + net_dev->name, i, inl(ioaddr + rfdr)); + } + } + outl(rfcrSave, rfcr + ioaddr); +} + static void sis900_timer(unsigned long data) { struct device *dev = (struct device *)data; struct sis900_private *tp = (struct sis900_private *)dev->priv; - int next_tick = 0; + int next_tick = 2*HZ; u16 status; + /* FIXME: call auto negotiate routine to detect link status */ if (!tp->LinkOn) { - status = mdio_read(dev, tp->phys[tp->phy_idx], MII_STATUS); + status = mdio_read(dev, tp->mii->phy_addr, MII_STATUS); if (status & MIISTAT_LINK) { - elPMDreadMode(dev, tp->phys[tp->phy_idx], - &tp->speeds, &tp->full_duplex); + elPMDreadMode(dev, tp->mii->phy_addr, + &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"); + printk(KERN_INFO "%s: Media Link On %s%s-duplex \n", 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); + status = mdio_read(dev, tp->mii->phy_addr, MII_STATUS); if (!(status & MIISTAT_LINK)) { tp->LinkOn = FALSE; printk(KERN_INFO "%s: Media Link Off\n", dev->name); } } - next_tick = 2*HZ; if (next_tick) { - tp->timer.expires = RUN_AT(next_tick); + tp->timer.expires = jiffies + next_tick; add_timer(&tp->timer); } } @@ -1068,20 +653,9 @@ static void sis900_tx_timeout(struct device *dev) " (queue head)" : ""); } - /* 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; - */ 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; @@ -1095,11 +669,6 @@ static void sis900_tx_timeout(struct device *dev) 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; @@ -1116,28 +685,27 @@ static void sis900_tx_timeout(struct device *dev) 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); + outl((RxRCMP|RxOK|RxERR|RxORN|RxSOVR|TxOK|TxERR|TxURN), ioaddr + imr); return; } - -/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +/* Reset (Initialize) the Tx rings, along with various 'dev' bits. */ static void -sis900_init_ring(struct device *dev) +sis900_reset_tx_ring(struct device *dev) { struct sis900_private *tp = (struct sis900_private *)dev->priv; + long ioaddr = dev->base_addr; int i; - + tp->tx_full = 0; - tp->cur_rx = 0; tp->dirty_tx = tp->cur_tx = 0; - + /* 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_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]); + virt_to_bus(&tp->tx_bufs[i*TX_BUF_SIZE]); } /* Tx Descriptor */ @@ -1152,6 +720,19 @@ sis900_init_ring(struct device *dev) tp->tx_buf[i].cmdsts=0; } + outl((u32)tp->tx_buf[0].physAddr, ioaddr + txdp); +} + +/* Reset (Initialize) the Rx rings, along with various 'dev' bits. */ +static void +sis900_reset_rx_ring(struct device *dev) +{ + struct sis900_private *tp = (struct sis900_private *)dev->priv; + long ioaddr = dev->base_addr; + int i; + + tp->cur_rx = 0; + /* Rx Buffer */ for (i = 0; i < NUM_RX_DESC; i++) { tp->rx_buf[i].buf = &tp->rx_bufs[i*RX_BUF_SIZE]; @@ -1170,6 +751,8 @@ sis900_init_ring(struct device *dev) virt_to_bus(&(tp->rx_buf[i].plink)); tp->rx_buf[i].cmdsts=RX_BUF_SIZE; } + + outl((u32)tp->rx_buf[0].physAddr, ioaddr + rxdp); } static int @@ -1241,7 +824,8 @@ static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs) #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*)&dev->interrupt) +) { printk(KERN_INFO "%s: SMP simultaneous entry of " "an interrupt handler.\n", dev->name); dev->interrupt = 0; /* Avoid halting machine. */ @@ -1258,8 +842,6 @@ static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs) 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 " @@ -1267,6 +849,7 @@ static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs) dev->name, status, inl(ioaddr + isr)); if ((status & (TxURN|TxERR|TxOK | RxORN|RxERR|RxOK)) == 0) { + /* nothing intresting happened */ break; } @@ -1354,7 +937,7 @@ static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs) /* Free the original skb. */ if (sis900_debug > 2) printk(KERN_INFO "Free original skb\n"); - dev_free_skb(tp->tx_skbuff[entry]); + dev_kfree_skb(tp->tx_skbuff[entry]); tp->tx_skbuff[entry] = 0; } // for dirty @@ -1419,8 +1002,6 @@ static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs) 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; @@ -1533,6 +1114,7 @@ static int sis900_rx(struct device *dev) dev->name, cur_rx, inb(ioaddr + cr)); tp->cur_rx = cur_rx; + outl( RxENA , ioaddr + cr ); /* LCS */ return 0; } @@ -1546,10 +1128,6 @@ sis900_close(struct device *dev) 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)); - /* Disable interrupts by clearing the interrupt mask. */ outl(0x0000, ioaddr + imr); @@ -1562,7 +1140,7 @@ sis900_close(struct device *dev) for (i = 0; i < NUM_TX_DESC; i++) { if (tp->tx_skbuff[i]) - dev_free_skb(tp->tx_skbuff[i]); + dev_kfree_skb(tp->tx_skbuff[i]); tp->tx_skbuff[i] = 0; } kfree(tp->rx_bufs); @@ -1581,13 +1159,13 @@ static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd) 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] = tp->mii->phy_addr; /* Fall Through */ - case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ + case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ data[3] = mdio_read(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]); @@ -1727,17 +1305,17 @@ static u16 elAutoNegotiate(struct device *dev, int phy_id, int *duplex, int *spe retnVal = elMIIpollBit(dev, phy_id, MII_CONTROL, MIICNTL_RST_AUTO, FALSE,&status); if (!retnVal) { - printk(KERN_INFO "Not wait for Reset Complete\n"); + printk(KERN_INFO "%s: Not wait for Reset Complete\n", dev->name); } retnVal = elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_AUTO_DONE, TRUE, &status); if (!retnVal) { - printk(KERN_INFO "Not wait for AutoNego Complete\n"); + printk(KERN_INFO "%s: Not wait for AutoNego Complete\n", dev->name); } retnVal = elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_LINK, TRUE, &status); if (!retnVal) { - printk(KERN_INFO "Not wait for Link Complete\n"); + printk(KERN_INFO "%s: Not wait for Link Complete\n", dev->name); } if (status & MIISTAT_LINK) { elPMDreadMode(dev, phy_id, speed, duplex); @@ -1814,9 +1392,8 @@ static void set_rx_mode(struct device *dev) 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; + || (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; } else { @@ -1875,49 +1452,40 @@ static void set_rx_mode(struct device *dev) static void sis900_reset(struct device *dev) { long ioaddr = dev->base_addr; - + outl(0, ioaddr + ier); outl(0, ioaddr + imr); outl(0, ioaddr + rfcr); outl(RxRESET | TxRESET | RESET, ioaddr + cr); outl(PESEL, ioaddr + cfg); - + set_rx_mode(dev); } #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 *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; + /* 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; + struct device *next_dev = tp->next_module; + + unregister_netdev(root_sis900_dev); + release_region(root_sis900_dev->base_addr, + tp->mac->io_size); + kfree(tp); + kfree(root_sis900_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 --git a/drivers/net/sis900.h b/drivers/net/sis900.h new file mode 100644 index 000000000000..e3adaaeff785 --- /dev/null +++ b/drivers/net/sis900.h @@ -0,0 +1,256 @@ +/* sis900.h Definitions for SiS ethernet controllers including 7014/7016 and 900 + * Copyrigth 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 +#define TxFILLT_shift 8 +#define TxDRNT_shift 0 +#define TxDRNT_100 (1536>>5) +#define TxDRNT_10 16 + +enum sis900_transmit_config_register_bits { + TxCSI = 0x80000000, TxHBI = 0x40000000, TxMLB = 0x20000000, + TxATP = 0x10000000, TxIFG = 0x0C000000, TxFILLT = 0x00003F00, + TxDRNT = 0x0000003F +}; + +/* recevie FFIFO thresholds */ +#define RxDRNT_shift 1 +#define RxDRNT_100 8 +#define RxDRNT_10 8 + +enum sis900_reveive_config_register_bits { + RxAEP = 0x80000000, RxARP = 0x40000000, RxATP = 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 */ +#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 RXSTS_shift 18 + +/* 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 + +#define TX_BUF_SIZE 1536 +#define RX_BUF_SIZE 1536 + +#define NUM_TX_DESC 16 /* Number of Tx descriptor registers. */ +#define NUM_RX_DESC 8 /* 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 --git a/drivers/pci/oldproc.c b/drivers/pci/oldproc.c index 7fef134f5d47..66e489a9a07f 100644 --- a/drivers/pci/oldproc.c +++ b/drivers/pci/oldproc.c @@ -526,6 +526,7 @@ struct pci_dev_info dev_info[] = { 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 @@ struct pci_dev_info dev_info[] = { 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 --git a/drivers/scsi/fdomain.c b/drivers/scsi/fdomain.c index 760b3fbc5617..b7b6d64b9575 100644 --- a/drivers/scsi/fdomain.c +++ b/drivers/scsi/fdomain.c @@ -875,6 +875,8 @@ int fdomain_16x0_detect( Scsi_Host_Template *tpnt ) 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 --git a/drivers/scsi/gdth.h b/drivers/scsi/gdth.h index 340adf5aeaf5..6d77ef1ae9c7 100644 --- a/drivers/scsi/gdth.h +++ b/drivers/scsi/gdth.h @@ -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 --git a/drivers/scsi/ips.h b/drivers/scsi/ips.h index 964cd432f673..af3e7f1129f6 100644 --- a/drivers/scsi/ips.h +++ b/drivers/scsi/ips.h @@ -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 @@ typedef struct _SUBSYS_PARAM { /* * Inquiry Data Format */ +#ifndef HOSTS_C + typedef struct { u8 DeviceType:5; u8 DeviceTypeQualifier:3; @@ -614,6 +616,7 @@ typedef struct { u8 Reserved3[40]; } INQUIRYDATA, *PINQUIRYDATA; +#endif /* * Read Capacity Data Format */ diff --git a/drivers/scsi/megaraid.h b/drivers/scsi/megaraid.h index 83fe02785b47..8a801c8336ef 100644 --- a/drivers/scsi/megaraid.h +++ b/drivers/scsi/megaraid.h @@ -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 --git a/drivers/scsi/psi240i.h b/drivers/scsi/psi240i.h index 1aed96ec7d47..306dad4fe3aa 100644 --- a/drivers/scsi/psi240i.h +++ b/drivers/scsi/psi240i.h @@ -211,6 +211,8 @@ typedef struct _READ_CAPACITY_DATA } READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA; // SCSI inquiry data +#ifndef HOSTS_C + typedef struct _INQUIRYDATA { UCHAR DeviceType :5; @@ -235,6 +237,7 @@ typedef struct _INQUIRYDATA UCHAR VendorSpecific[20]; UCHAR Reserved3[40]; } INQUIRYDATA, *PINQUIRYDATA; +#endif // IDE IDENTIFY data typedef struct _IDENTIFY_DATA diff --git a/drivers/scsi/qlogicfc.c b/drivers/scsi/qlogicfc.c index cf00d442efb9..7f91fa4bd8fb 100644 --- a/drivers/scsi/qlogicfc.c +++ b/drivers/scsi/qlogicfc.c @@ -1855,7 +1855,7 @@ static int isp2x00_get_nvram_defaults(struct Scsi_Host *host, struct init_cb *co 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 --git a/drivers/scsi/qlogicisp.c b/drivers/scsi/qlogicisp.c index 8744bb8ab1c6..27837d71710a 100644 --- a/drivers/scsi/qlogicisp.c +++ b/drivers/scsi/qlogicisp.c @@ -1262,6 +1262,13 @@ static int isp1020_init(struct Scsi_Host *sh) 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 --git a/drivers/scsi/sym53c416.h b/drivers/scsi/sym53c416.h index 49abc83cac7a..67e6240aa45d 100644 --- a/drivers/scsi/sym53c416.h +++ b/drivers/scsi/sym53c416.h @@ -22,7 +22,9 @@ #include #endif +#ifndef LinuxVersionCode #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) +#endif #include #include diff --git a/drivers/scsi/sym53c8xx_defs.h b/drivers/scsi/sym53c8xx_defs.h index f45cd0824b31..c1e43efec1a7 100644 --- a/drivers/scsi/sym53c8xx_defs.h +++ b/drivers/scsi/sym53c8xx_defs.h @@ -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 --git a/drivers/scsi/u14-34f.h b/drivers/scsi/u14-34f.h index 943b8cbb3cc8..c97d29bcb6d5 100644 --- a/drivers/scsi/u14-34f.h +++ b/drivers/scsi/u14-34f.h @@ -18,7 +18,9 @@ int u14_34f_biosparam(Disk *, kdev_t, int *); #define U14_34F_VERSION "4.33.00" +#ifndef LinuxVersionCode #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) +#endif #if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101) diff --git a/drivers/sound/Config.in b/drivers/sound/Config.in index 5d2a848c9751..d4bcd1ae2951 100644 --- a/drivers/sound/Config.in +++ b/drivers/sound/Config.in @@ -115,8 +115,6 @@ if [ "$CONFIG_SOUND_OSS" = "y" -o "$CONFIG_SOUND_OSS" = "m" ]; then 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 bool '16 bit sampling option of GUS (_NOT_ GUS MAX)' CONFIG_GUS16 diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile index 255b6793b117..fe8ae5e59986 100644 --- a/drivers/sound/Makefile +++ b/drivers/sound/Makefile @@ -43,7 +43,6 @@ obj- := 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 diff --git a/drivers/sound/ac97.h b/drivers/sound/ac97.h index 3269337d510b..490d69f88073 100644 --- a/drivers/sound/ac97.h +++ b/drivers/sound/ac97.h @@ -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 --git a/drivers/sound/adlib_card.c b/drivers/sound/adlib_card.c index b21d7316eed7..9bd6cc1c53cc 100644 --- a/drivers/sound/adlib_card.c +++ b/drivers/sound/adlib_card.c @@ -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); @@ -70,4 +68,3 @@ void cleanup_module(void) } #endif -#endif diff --git a/drivers/sound/es1370.c b/drivers/sound/es1370.c index 7f53acc68a05..4b72ef1bf8c4 100644 --- a/drivers/sound/es1370.c +++ b/drivers/sound/es1370.c @@ -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 @@ struct es1370_state { 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 @@ static const struct { [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 @@ static int mixer_ioctl(struct es1370_state *s, unsigned int cmd, unsigned long a 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 /*const*/ struct file_operations es1370_mixer_fops = { 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 @@ static int drain_dac1(struct es1370_state *s, int nonblock) current->state = TASK_RUNNING; return -EBUSY; } - tmo = (count * HZ) / dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL]; + tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2 + / dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL]; tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; - if (!schedule_timeout(tmo ? : 1) && tmo) + if (!schedule_timeout(tmo + 1)) DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");) } remove_wait_queue(&s->dma_dac1.wait, &wait); @@ -1089,9 +1114,10 @@ static int drain_dac2(struct es1370_state *s, int nonblock) current->state = TASK_RUNNING; return -EBUSY; } - tmo = (count * HZ) / DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV); + tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2 + / DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV); tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; - if (!schedule_timeout(tmo ? : 1) && tmo) + if (!schedule_timeout(tmo + 1)) DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");) } remove_wait_queue(&s->dma_dac2.wait, &wait); @@ -2049,6 +2075,7 @@ static /*const*/ struct file_operations es1370_dac_fops = { 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 @@ static ssize_t es1370_midi_read(struct file *file, char *buffer, size_t count, l 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 @@ static ssize_t es1370_midi_read(struct file *file, char *buffer, size_t count, l 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 @@ static ssize_t es1370_midi_read(struct file *file, char *buffer, size_t count, l 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 @@ static ssize_t es1370_midi_write(struct file *file, const char *buffer, size_t c 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 @@ static ssize_t es1370_midi_write(struct file *file, const char *buffer, size_t c 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 @@ static ssize_t es1370_midi_write(struct file *file, const char *buffer, size_t c 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_open(struct inode *inode, struct file *file) 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 @@ __initfunc(int init_es1370(void)) 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 @@ __initfunc(int init_es1370(void)) 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 @@ __initfunc(int init_es1370(void)) /* 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 --git a/drivers/sound/es1371.c b/drivers/sound/es1371.c index cc7dfadfa951..68c3a34af10d 100644 --- a/drivers/sound/es1371.c +++ b/drivers/sound/es1371.c @@ -3,7 +3,7 @@ /* * es1371.c -- Creative Ensoniq ES1371. * - * Copyright (C) 1998 Thomas Sailer (sailer@ife.ee.ethz.ch) + * Copyright (C) 1998-1999 Thomas Sailer (sailer@ife.ee.ethz.ch) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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 @@ static const unsigned sample_shift[] = { 0, 1, 1, 2 }; #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 @@ static const unsigned sample_shift[] = { 0, 1, 1, 2 }; /* misc stuff */ - +#define POLL_COUNT 0x1000 #define FMODE_DAC 4 /* slight misuse of mode_t */ /* MIDI buffer sizes */ @@ -352,8 +393,13 @@ struct es1371_state { /* 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; @@ -437,32 +483,13 @@ extern inline unsigned ld2(unsigned int x) return r; } -/* --------------------------------------------------------------------- */ -/* - * 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 wait_src_ready(struct es1371_state *s) static unsigned src_read(struct es1371_state *s, unsigned reg) { - unsigned int r; + unsigned int temp,i,orig; + + /* 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; + } + } - 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; + /* 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 @@ static void set_adc_rate(struct es1371_state *s, unsigned rate) spin_unlock_irqrestore(&s->lock, flags); } + static void set_dac1_rate(struct es1371_state *s, unsigned rate) { unsigned long flags; @@ -541,8 +593,8 @@ static void set_dac1_rate(struct es1371_state *s, unsigned rate) 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 @@ static void set_dac2_rate(struct es1371_state *s, unsigned rate) 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 set_dac2_rate(struct es1371_state *s, unsigned rate) /* --------------------------------------------------------------------- */ +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 @@ static unsigned rdcodec(struct es1371_state *s, unsigned addr) 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 @@ static const char invalid_magic[] = KERN_CRIT "es1371: invalid magic value\n"; /* --------------------------------------------------------------------- */ +/* + * 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 int recsrc[8] = 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 @@ static int mixer_rdch(struct es1371_state *s, unsigned int ch, int *arg) 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 @@ static int mixer_rdch(struct es1371_state *s, unsigned int ch, int *arg) 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 @@ static int mixer_rdch(struct es1371_state *s, unsigned int ch, int *arg) /* 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 @@ static int mixer_rdch(struct es1371_state *s, unsigned int ch, int *arg) #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 @@ static int mixer_rdch(struct es1371_state *s, unsigned int ch, int *arg) 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 @@ static int mixer_rdch(struct es1371_state *s, unsigned int ch, int *arg) 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 @@ static int mixer_wrch(struct es1371_state *s, unsigned int ch, int val) 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 @@ static int mixer_wrch(struct es1371_state *s, unsigned int ch, int val) 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 @@ static int mixer_wrch(struct es1371_state *s, unsigned int ch, int val) 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 @@ static int mixer_wrch(struct es1371_state *s, unsigned int ch, int val) 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 @@ static int mixer_wrch(struct es1371_state *s, unsigned int ch, int val) } 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 @@ static int mixer_wrch(struct es1371_state *s, unsigned int ch, int val) } 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 @@ static int mixer_ioctl(struct es1371_state *s, unsigned int cmd, unsigned long a 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 @@ static int mixer_ioctl(struct es1371_state *s, unsigned int cmd, unsigned long a 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 @@ static int mixer_ioctl(struct es1371_state *s, unsigned int cmd, unsigned long a 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 @@ static int drain_dac1(struct es1371_state *s, int nonblock) current->state = TASK_RUNNING; return -EBUSY; } - tmo = (count * HZ) / s->dac1rate; + tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2 / s->dac1rate; tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; - if (!schedule_timeout(tmo ? : 1) && tmo) - 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 @@ static int drain_dac2(struct es1371_state *s, int nonblock) current->state = TASK_RUNNING; return -EBUSY; } - tmo = (count * HZ) / s->dac2rate; + tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2 / s->dac2rate; tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; - if (!schedule_timeout(tmo ? : 1) && tmo) - 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 /*const*/ struct file_operations es1371_dac_fops = { 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 @@ static ssize_t es1371_midi_read(struct file *file, char *buffer, size_t count, l 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 @@ static ssize_t es1371_midi_read(struct file *file, char *buffer, size_t count, l 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 @@ static ssize_t es1371_midi_read(struct file *file, char *buffer, size_t count, l 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 @@ static ssize_t es1371_midi_write(struct file *file, const char *buffer, size_t c 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 @@ static ssize_t es1371_midi_write(struct file *file, const char *buffer, size_t c 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 @@ static ssize_t es1371_midi_write(struct file *file, const char *buffer, size_t c 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 @@ static /*const*/ struct file_operations es1371_midi_fops = { /* --------------------------------------------------------------------- */ +/* + * 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 @@ __initfunc(int init_es1371(void)) 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 @@ __initfunc(int init_es1371(void)) 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 @@ __initfunc(int init_es1371(void)) 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 @@ __initfunc(int init_es1371(void)) s->sctrl = 0; cssr = 0; /* check to see if s/pdif mode is being requested */ - pci_read_config_byte(pcidev, PCI_REVISION_ID, &revision); if (spdif[index]) { - if (revision >= 4) { + 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 @@ __initfunc(int init_es1371(void)) 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 @@ __initfunc(int init_es1371(void)) 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 @@ void cleanup_module(void) 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 --git a/drivers/sound/esssolo1.c b/drivers/sound/esssolo1.c index c39fc95cc872..6b602e77f6b0 100644 --- a/drivers/sound/esssolo1.c +++ b/drivers/sound/esssolo1.c @@ -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 @@ extern inline void write_seq(struct solo1_state *s, unsigned char data) { 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 @@ extern inline void write_seq(struct solo1_state *s, unsigned char data) } __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 @@ extern inline int read_seq(struct solo1_state *s, unsigned char *data) *data = inb(s->sbbase+0xa); return 1; } + printk(KERN_ERR "esssolo1: read_seq timeout\n"); return 0; } @@ -952,7 +963,7 @@ static int drain_dac(struct solo1_state *s, int nonblock) 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 @@ static int drain_dac(struct solo1_state *s, int nonblock) 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 @@ static int drain_dac(struct solo1_state *s, int nonblock) 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 void solo1_midi_timer(unsigned long data) 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 @@ static ssize_t solo1_midi_read(struct file *file, char *buffer, size_t count, lo 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 @@ static ssize_t solo1_midi_read(struct file *file, char *buffer, size_t count, lo 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 @@ static ssize_t solo1_midi_read(struct file *file, char *buffer, size_t count, lo 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 @@ static ssize_t solo1_midi_write(struct file *file, const char *buffer, size_t co 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 @@ static ssize_t solo1_midi_write(struct file *file, const char *buffer, size_t co 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 @@ static ssize_t solo1_midi_write(struct file *file, const char *buffer, size_t co 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 @@ static int solo1_dmfm_open(struct inode *inode, struct file *file) 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 @@ static int solo1_dmfm_release(struct inode *inode, struct file *file) 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 @@ static struct initvol { 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 @@ static struct initvol { 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 @@ static struct initvol { 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 @@ static struct initvol { 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 @@ static struct initvol { index++; continue; + err: + unregister_sound_dsp(s->dev_dmfm); err_dev4: unregister_sound_dsp(s->dev_midi); err_dev3: @@ -2176,7 +2234,7 @@ static struct initvol { 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 @@ static void __exit cleanup_solo1(void) 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); @@ -2223,4 +2281,3 @@ static void __exit cleanup_solo1(void) module_init(init_solo1); module_exit(cleanup_solo1); - diff --git a/drivers/sound/lowlevel/miroaci.h b/drivers/sound/lowlevel/miroaci.h index 9d64eaa1e360..f3547adf2b64 100644 --- a/drivers/sound/lowlevel/miroaci.h +++ b/drivers/sound/lowlevel/miroaci.h @@ -1,6 +1,11 @@ +#ifdef CONFIG_ACI_MIXER 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 --git a/drivers/sound/msnd_pinnacle.c b/drivers/sound/msnd_pinnacle.c index c70d8d94d0ac..962f20e02a00 100644 --- a/drivers/sound/msnd_pinnacle.c +++ b/drivers/sound/msnd_pinnacle.c @@ -46,6 +46,7 @@ # include #endif #include +#include #include "sound_config.h" #include "sound_firmware.h" #ifdef MSND_CLASSIC diff --git a/drivers/sound/nm256_audio.c b/drivers/sound/nm256_audio.c index bbdd483d298d..387c44416da9 100644 --- a/drivers/sound/nm256_audio.c +++ b/drivers/sound/nm256_audio.c @@ -14,6 +14,8 @@ #include #include #include +#include + #include "sound_config.h" #include "soundmodule.h" #include "nm256.h" diff --git a/drivers/sound/opl3.c b/drivers/sound/opl3.c index 8898c655a15f..a36909b93584 100644 --- a/drivers/sound/opl3.c +++ b/drivers/sound/opl3.c @@ -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 @@ void cleanup_module(void) EXPORT_SYMBOL(opl3_init); EXPORT_SYMBOL(opl3_detect); MODULE_PARM(io, "i"); - -#endif diff --git a/drivers/sound/sonicvibes.c b/drivers/sound/sonicvibes.c index cdf4a6955286..d9c710e596f5 100644 --- a/drivers/sound/sonicvibes.c +++ b/drivers/sound/sonicvibes.c @@ -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 @@ static int drain_dac(struct sv_state *s, int nonblock) current->state = TASK_RUNNING; return -EBUSY; } - tmo = (count * HZ) / s->ratedac; + tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->ratedac; tmo >>= sample_shift[(s->fmt >> SV_CFMT_ASHIFT) & SV_CFMT_MASK]; - if (!schedule_timeout(tmo ? : 1) && tmo) + if (!schedule_timeout(tmo + 1)) printk(KERN_DEBUG "sv: dma timed out??\n"); } remove_wait_queue(&s->dma_dac.wait, &wait); @@ -1870,6 +1878,7 @@ static /*const*/ struct file_operations sv_audio_fops = { 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 @@ static ssize_t sv_midi_read(struct file *file, char *buffer, size_t count, loff_ 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 @@ static ssize_t sv_midi_read(struct file *file, char *buffer, size_t count, loff_ 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 @@ static ssize_t sv_midi_read(struct file *file, char *buffer, size_t count, loff_ 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 @@ static ssize_t sv_midi_write(struct file *file, const char *buffer, size_t count 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 @@ static ssize_t sv_midi_write(struct file *file, const char *buffer, size_t count 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 @@ static ssize_t sv_midi_write(struct file *file, const char *buffer, size_t count 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 @@ __initfunc(int init_sonicvibes(void)) 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 @@ __initfunc(int init_sonicvibes(void)) 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 @@ __initfunc(int init_sonicvibes(void)) 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 @@ void cleanup_module(void) 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 --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index ec8a2e396b91..e8a53f6d4c09 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -311,6 +311,7 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs unsigned long fd_offset; unsigned long rlim; int retval; + static int warnings = 0; ex = *((struct exec *) bprm->buf); /* exec-header */ if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC && @@ -322,6 +323,23 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs fd_offset = N_TXTOFF(ex); +#ifdef __i386__ + if (N_MAGIC(ex) == ZMAGIC && fd_offset != BLOCK_SIZE) { + if(warnings++<10) + printk(KERN_NOTICE "N_TXTOFF != BLOCK_SIZE. See a.out.h.\n"); + return -ENOEXEC; + } + + if (N_MAGIC(ex) == ZMAGIC && ex.a_text && + bprm->dentry->d_inode->i_op && + bprm->dentry->d_inode->i_op->bmap && + (fd_offset < bprm->dentry->d_inode->i_sb->s_blocksize)) { + if(warnings++<10) + printk(KERN_NOTICE "N_TXTOFF < BLOCK_SIZE. Please convert binary.\n"); + return -ENOEXEC; + } +#endif + /* Check initial limits. This avoids letting people circumvent * size limits imposed on them by creating programs with large * arrays in the data or bss. @@ -389,33 +407,19 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs flush_icache_range((unsigned long) 0, (unsigned long) ex.a_text+ex.a_data); } else { - static unsigned long error_time, error_time2; if ((ex.a_text & 0xfff || ex.a_data & 0xfff) && - (N_MAGIC(ex) != NMAGIC) && (jiffies-error_time2) > 5*HZ) - { - printk(KERN_NOTICE "executable not page aligned\n"); - error_time2 = jiffies; - } + (N_MAGIC(ex) != NMAGIC)) + if(warnings++<10) + printk(KERN_NOTICE "executable not page aligned\n"); fd = open_dentry(bprm->dentry, O_RDONLY); if (fd < 0) return fd; - file = fget(fd); - - if ((fd_offset & ~PAGE_MASK) != 0 && - (jiffies-error_time) > 5*HZ) - { - printk(KERN_WARNING - "fd_offset is not page aligned. Please convert program: %s\n", - file->f_dentry->d_name.name - ); - error_time = jiffies; - } + file = fcheck(fd); - if (!file->f_op || !file->f_op->mmap || ((fd_offset & ~PAGE_MASK) != 0)) { - fput(file); + if (!file->f_op || !file->f_op->mmap) { sys_close(fd); - do_mmap(NULL, N_TXTADDR(ex), ex.a_text+ex.a_data, + do_mmap(NULL, 0, ex.a_text+ex.a_data, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0); read_exec(bprm->dentry, fd_offset, @@ -432,7 +436,6 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs fd_offset); if (error != N_TXTADDR(ex)) { - fput(file); sys_close(fd); send_sig(SIGKILL, current, 0); return error; @@ -442,7 +445,6 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE, fd_offset + ex.a_text); - fput(file); sys_close(fd); if (error != N_DATADDR(ex)) { send_sig(SIGKILL, current, 0); @@ -497,6 +499,7 @@ do_load_aout_library(int fd) unsigned long error; int retval; loff_t offset = 0; + static int warnings = 0; struct exec ex; retval = -EACCES; @@ -522,6 +525,13 @@ do_load_aout_library(int fd) goto out_putf; } + if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) && + (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) { + if(warnings++<10) + printk("N_TXTOFF < BLOCK_SIZE. Please convert library\n"); + goto out_putf; + } + if (N_FLAGS(ex)) goto out_putf; @@ -530,26 +540,6 @@ do_load_aout_library(int fd) start_addr = ex.a_entry & 0xfffff000; - if ((N_TXTOFF(ex) & ~PAGE_MASK) != 0) { - static unsigned long error_time; - - if ((jiffies-error_time) > 5*HZ) - { - printk(KERN_WARNING - "N_TXTOFF is not page aligned. Please convert library: %s\n", - file->f_dentry->d_name.name); - error_time = jiffies; - } - do_mmap(NULL, start_addr, ex.a_text + ex.a_data, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_FIXED| MAP_PRIVATE, 0); - read_exec(file->f_dentry, N_TXTOFF(ex), - (char *)start_addr, ex.a_text + ex.a_data, 0); - flush_icache_range((unsigned long) start_addr, - (unsigned long) start_addr + ex.a_text + ex.a_data); - retval = 0; - goto out_putf; - } /* Now use mmap to map the library into memory. */ error = do_mmap(file, start_addr, ex.a_text + ex.a_data, PROT_READ | PROT_WRITE | PROT_EXEC, diff --git a/fs/buffer.c b/fs/buffer.c index 1f76051878cf..e57f678f4d33 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -58,11 +58,12 @@ static char buffersize_index[65] = /* * Hash table mask.. */ -static unsigned long bh_hash_mask = 0; +static unsigned int bh_hash_mask = 0; +static unsigned int bh_hash_shift = 0; +static struct buffer_head ** hash_table = NULL; static int grow_buffers(int size); -static struct buffer_head ** hash_table; static struct buffer_head * lru_list[NR_LIST] = {NULL, }; static struct buffer_head * free_list[NR_SIZES] = {NULL, }; @@ -420,8 +421,13 @@ void invalidate_buffers(kdev_t dev) } } -#define _hashfn(dev,block) (((unsigned)(HASHDEV(dev)^block)) & bh_hash_mask) -#define hash(dev,block) hash_table[_hashfn(dev,block)] +/* After several hours of tedious analysis, the following hash + * function won. Do not mess with it... -DaveM + */ +#define _hashfn(dev,block) \ + ((((dev)<<(bh_hash_shift - 6)) ^ ((dev)<<(bh_hash_shift - 9))) ^ \ + (((block)<<(bh_hash_shift - 6)) ^ ((block) >> 13) ^ ((block) << (bh_hash_shift - 12)))) +#define hash(dev,block) hash_table[_hashfn(dev,block) & bh_hash_mask] static inline void remove_from_hash_queue(struct buffer_head * bh) { @@ -1512,16 +1518,26 @@ void __init buffer_init(unsigned long memory_size) for something that is really too small */ do { + unsigned long tmp; + nr_hash = (1UL << order) * PAGE_SIZE / sizeof(struct buffer_head *); + bh_hash_mask = (nr_hash - 1); + + tmp = nr_hash; + bh_hash_shift = 0; + while((tmp >>= 1UL) != 0UL) + bh_hash_shift++; + hash_table = (struct buffer_head **) __get_free_pages(GFP_ATOMIC, order); - } while (hash_table == NULL && --order > 4); + } while (hash_table == NULL && --order >= 0); + printk("Buffer-cache hash table entries: %d (order: %d, %ld bytes)\n", + nr_hash, order, (1UL< #include +#include #define DCACHE_PARANOIA 1 /* #define DCACHE_DEBUG 1 */ @@ -41,11 +42,12 @@ kmem_cache_t *dentry_cache; * This hash-function tries to avoid losing too many bits of hash * information, yet avoid using a prime hash-size or similar. */ -#define D_HASHBITS 10 -#define D_HASHSIZE (1UL << D_HASHBITS) -#define D_HASHMASK (D_HASHSIZE-1) +#define D_HASHBITS d_hash_shift +#define D_HASHMASK d_hash_mask -static struct list_head dentry_hashtable[D_HASHSIZE]; +static unsigned int d_hash_mask = 0; +static unsigned int d_hash_shift = 0; +static struct list_head *dentry_hashtable = NULL; static LIST_HEAD(dentry_unused); struct { @@ -563,7 +565,7 @@ struct dentry * d_alloc_root(struct inode * root_inode, struct dentry *old_root) static inline struct list_head * d_hash(struct dentry * parent, unsigned long hash) { - hash += (unsigned long) parent; + hash += (unsigned long) parent / L1_CACHE_BYTES; hash = hash ^ (hash >> D_HASHBITS) ^ (hash >> D_HASHBITS*2); return dentry_hashtable + (hash & D_HASHMASK); } @@ -902,8 +904,10 @@ out: void __init dcache_init(void) { - int i; - struct list_head *d = dentry_hashtable; + int i, order; + struct list_head *d; + unsigned int nr_hash; + unsigned long memory_size; /* * A constructor could be added for stable state like the lists, @@ -921,7 +925,33 @@ void __init dcache_init(void) if (!dentry_cache) panic("Cannot create dentry cache"); - i = D_HASHSIZE; + memory_size = num_physpages << PAGE_SHIFT; + for (order = 5; (1UL << order) < memory_size; order++) + ; + + do { + unsigned long tmp; + + nr_hash = (1UL << order) * PAGE_SIZE / + sizeof(struct list_head); + d_hash_mask = (nr_hash - 1); + + tmp = nr_hash; + d_hash_shift = 0; + while((tmp >>= 1UL) != 0UL) + d_hash_shift++; + + dentry_hashtable = (struct list_head *) + __get_free_pages(GFP_ATOMIC, order); + } while(dentry_hashtable == NULL && --order >= 0); + printk("DENTRY hash table entries: %d (order: %d, %ld bytes)\n", + nr_hash, order, (1UL<u.ext2_i.i_version++; - mark_inode_dirty(inode); - - return; -} - /* * There are two policies for allocating an inode. If the new inode is * a directory, then a forward search is made for a block group with both @@ -493,8 +478,9 @@ repeat: if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL) inode->i_flags |= MS_SYNCHRONOUS; insert_inode_hash(inode); + inode->i_generation = inode_generation_count++; + inode->u.ext2_i.i_version = inode->i_generation; mark_inode_dirty(inode); - inc_inode_version (inode, gdp, mode); unlock_super (sb); if(DQUOT_ALLOC_INODE(sb, inode)) { diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 315d71b0f656..5629f728a802 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -51,6 +51,10 @@ void ext2_delete_inode (struct inode * inode) inode->i_ino == EXT2_ACL_DATA_INO) return; inode->u.ext2_i.i_dtime = CURRENT_TIME; + /* When we delete an inode, we increment its i_version. If it + is ever read in from disk again, it will have a different + i_version. */ + inode->u.ext2_i.i_version++; mark_inode_dirty(inode); ext2_update_inode(inode, IS_SYNC(inode)); inode->i_size = 0; @@ -538,6 +542,7 @@ void ext2_read_inode (struct inode * inode) #endif } inode->u.ext2_i.i_version = le32_to_cpu(raw_inode->i_version); + inode->i_generation = inode->u.ext2_i.i_version; inode->u.ext2_i.i_block_group = block_group; inode->u.ext2_i.i_next_alloc_block = 0; inode->u.ext2_i.i_next_alloc_goal = 0; diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c index 773d0c6eb880..443cdd941fe5 100644 --- a/fs/ext2/ioctl.c +++ b/fs/ext2/ioctl.c @@ -76,6 +76,7 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, return -EROFS; if (get_user(inode->u.ext2_i.i_version, (int *) arg)) return -EFAULT; + inode->i_generation = inode->u.ext2_i.i_version; inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); return 0; diff --git a/fs/filesystems.c b/fs/filesystems.c index ee3f32c672a8..d8c4d6e6b40f 100644 --- a/fs/filesystems.c +++ b/fs/filesystems.c @@ -37,6 +37,7 @@ #endif #include #include +#include #include #include @@ -159,28 +160,48 @@ void __init filesystem_setup(void) #ifdef CONFIG_NFSD_MODULE int (*do_nfsservctl)(int, void *, void *) = NULL; #endif + +#ifdef CONFIG_LOCKD_MODULE +int (*do_lockdctl)(int, void *, void *) = NULL; +#endif + int asmlinkage sys_nfsservctl(int cmd, void *argp, void *resp) { -#ifndef CONFIG_NFSD_MODULE - return -ENOSYS; -#else int ret = -ENOSYS; + if (cmd >= NFSCTL_LOCKD) { +#if defined(CONFIG_LOCKD) || defined(CONFIG_LOCKD_MODULE) + lock_kernel(); +#ifdef CONFIG_LOCKD + ret = lockdctl(cmd, argp, resp); +#else + if (do_lockdctl) + ret = do_lockdctl(cmd, argp, resp); +#ifdef CONFIG_KMOD + else if (request_module ("lockd") == 0) { + if (do_lockdctl) + ret = do_lockdctl(cmd, argp, resp); + } +#endif +#endif + unlock_kernel(); +#endif + return ret; + } + +#ifdef CONFIG_NFSD_MODULE lock_kernel(); - if (do_nfsservctl) { + if (do_nfsservctl) ret = do_nfsservctl(cmd, argp, resp); - goto out; - } #ifdef CONFIG_KMOD - if (request_module ("nfsd") == 0) { + else if (request_module ("nfsd") == 0) { if (do_nfsservctl) ret = do_nfsservctl(cmd, argp, resp); } #endif /* CONFIG_KMOD */ -out: unlock_kernel(); - return ret; #endif /* CONFIG_NFSD_MODULE */ + return ret; } #endif /* CONFIG_NFSD */ diff --git a/fs/inode.c b/fs/inode.c index a72ef4815138..66c1dd0e382f 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -10,6 +10,7 @@ #include #include #include +#include /* * New inode.c implementation. @@ -48,6 +49,8 @@ LIST_HEAD(inode_in_use); static LIST_HEAD(inode_unused); static struct list_head inode_hashtable[HASH_SIZE]; +__u32 inode_generation_count = 0; + /* * A simple spinlock to protect the list manipulations. * @@ -809,6 +812,10 @@ void __init inode_init(void) if (max > MAX_INODE) max = MAX_INODE; max_inodes = max; + + /* Get a random number. */ + get_random_bytes (&inode_generation_count, + sizeof (inode_generation_count)); } /* This belongs in file_table.c, not here... */ @@ -844,3 +851,48 @@ void update_atime (struct inode *inode) inode->i_atime = CURRENT_TIME; mark_inode_dirty (inode); } /* End Function update_atime */ + +/* This function is called by nfsd. */ +struct inode *iget_in_use(struct super_block *sb, unsigned long ino) +{ + struct list_head * head = inode_hashtable + hash(sb,ino); + struct inode * inode; + + spin_lock(&inode_lock); + inode = find_inode(sb, ino, head); + if (inode) { + spin_unlock(&inode_lock); + wait_on_inode(inode); + } + else + inode = get_new_inode (sb, ino, head); + + /* When we get the inode, we have to check if it is in use. We + have to release it if it is not. */ + if (inode) { + spin_lock(&inode_lock); + if (inode->i_nlink == 0 && inode->i_count == 1) { + --inode->i_count; + list_del(&inode->i_hash); + INIT_LIST_HEAD(&inode->i_hash); + list_del(&inode->i_list); + INIT_LIST_HEAD(&inode->i_list); + if (list_empty(&inode->i_hash)) { + list_del(&inode->i_list); + INIT_LIST_HEAD(&inode->i_list); + spin_unlock(&inode_lock); + clear_inode(inode); + spin_lock(&inode_lock); + list_add(&inode->i_list, &inode_unused); + inodes_stat.nr_free_inodes++; + } + else if (!(inode->i_state & I_DIRTY)) { + list_del(&inode->i_list); + list_add(&inode->i_list, &inode_in_use); + } + inode = NULL; + } + spin_unlock(&inode_lock); + } + return inode; +} diff --git a/fs/lockd/lockd_syms.c b/fs/lockd/lockd_syms.c index b3990ed9893f..1bcb4f824bbd 100644 --- a/fs/lockd/lockd_syms.c +++ b/fs/lockd/lockd_syms.c @@ -23,6 +23,7 @@ #include #include #include +#include /* Start/stop the daemon */ EXPORT_SYMBOL(lockd_up); @@ -34,6 +35,7 @@ EXPORT_SYMBOL(nlmclnt_proc); /* NFS server entry points/hooks */ EXPORT_SYMBOL(nlmsvc_invalidate_client); EXPORT_SYMBOL(nlmsvc_ops); +EXPORT_SYMBOL(lockdctl); /* Configuration at insmod time */ EXPORT_SYMBOL(nlmsvc_grace_period); diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index d61db4302b47..80dcc1adba9e 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #define NLMDBG_FACILITY NLMDBG_SVC @@ -315,6 +316,9 @@ out: MODULE_PARM(nlm_grace_period, "10-240l"); MODULE_PARM(nlm_timeout, "3-20l"); #endif + +extern int (*do_lockdctl)(int, void *, void *); + int init_module(void) { @@ -324,6 +328,7 @@ init_module(void) nlmsvc_pid = 0; lockd_exit = NULL; nlmxdr_init(); + do_lockdctl = lockdctl; return 0; } @@ -332,6 +337,7 @@ cleanup_module(void) { /* FIXME: delete all NLM clients */ nlm_shutdown_hosts(); + do_lockdctl = NULL; } #endif @@ -370,3 +376,23 @@ struct svc_program nlmsvc_program = { "lockd", /* service name */ &nlmsvc_stats, /* stats table */ }; + +int +lockdctl(int cmd, void *opaque_argp, void *opaque_resp) +{ + int err; + + MOD_INC_USE_COUNT; + + switch(cmd) { + case LOCKDCTL_SVC: + err = lockd_up (); + break; + default: + err = -EINVAL; + } + + MOD_DEC_USE_COUNT; + + return err; +} diff --git a/fs/locks.c b/fs/locks.c index 87926addb5ee..ce456cac4031 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -688,8 +688,11 @@ static int posix_make_lock(struct file *filp, struct file_lock *fl, if (((start += l->l_start) < 0) || (l->l_len < 0)) return (0); + fl->fl_end = start + l->l_len - 1; + if (l->l_len > 0 && fl->fl_end < 0) + return (0); fl->fl_start = start; /* we record the absolute position */ - if ((l->l_len == 0) || ((fl->fl_end = start + l->l_len - 1) < 0)) + if (l->l_len == 0) fl->fl_end = OFFSET_MAX; fl->fl_file = filp; diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index afa07f00179c..194cd98888ea 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -134,8 +134,10 @@ nfs_put_super(struct super_block *sb) if ((rpc = server->client) != NULL) rpc_shutdown_client(rpc); +#if 0 if (!(server->flags & NFS_MOUNT_NONLM)) lockd_down(); /* release rpc.lockd */ +#endif rpciod_down(); /* release rpciod */ /* * Invalidate the dircache for this superblock. @@ -311,9 +313,11 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent) /* We're airborne */ unlock_super(sb); +#if 0 /* Check whether to start the lockd process */ if (!(server->flags & NFS_MOUNT_NONLM)) lockd_up(); +#endif return sb; /* Yargs. It didn't work out. */ diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index aca7e982c3dd..e95d32b01e89 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include #include @@ -29,6 +29,7 @@ #include #include #include +#include #if LINUX_VERSION_CODE >= 0x020100 #include @@ -40,7 +41,6 @@ #include #include -extern void nfsd_fh_init(void); extern long sys_call_table[]; static int nfsctl_svc(struct nfsctl_svc *data); @@ -57,15 +57,72 @@ static int initialized = 0; int exp_procfs_exports(char *buffer, char **start, off_t offset, int length, int *eof, void *data); -void proc_export_init(void) +struct long_maxmin { - struct proc_dir_entry *nfs_export_ent = NULL; + long *value; + long min_value; + long max_value; +}; - if (!(nfs_export_ent = create_proc_entry("fs/nfs", S_IFDIR, 0))) +/* In seconds. */ +#define TIME_DIFF_MARGIN 10 +#define MIN_TIME_DIFF_MARGIN 0 +#define MAX_TIME_DIFF_MARGIN 600 + +long nfsd_time_diff_margin = TIME_DIFF_MARGIN; + +static struct long_maxmin time_diff_margin = +{ + &nfsd_time_diff_margin, + MIN_TIME_DIFF_MARGIN, + MAX_TIME_DIFF_MARGIN +}; + +static int +nfsd_proc_read_long_maxmin(char *buffer, char **start, off_t offset, + int length, int *eof, void *data) +{ + int len; + struct long_maxmin *x = (struct long_maxmin *) data; + len = sprintf(buffer, "%ld\n", *x->value) - offset; + if (len < length) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = length; + *start = buffer + offset; + return len; +} + +static int +nfsd_proc_write_long_maxmin(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + long v; + struct long_maxmin *x = (struct long_maxmin *) data; + v = simple_strtoul(buffer, NULL, 10); + if (v < x->min_value || v > x->max_value) + return -EINVAL; + *x->value = v; + return count; +} + +static void nfsd_proc_init(void) +{ + struct proc_dir_entry *nfsd_ent = NULL; + + if (!(nfsd_ent = create_proc_entry("fs/nfs", S_IFDIR, 0))) return; - if (!(nfs_export_ent = create_proc_entry("fs/nfs/exports", 0, 0))) + if (!(nfsd_ent = create_proc_entry("fs/nfs/exports", 0, 0))) return; - nfs_export_ent->read_proc = exp_procfs_exports; + nfsd_ent->read_proc = exp_procfs_exports; + if (!(nfsd_ent = create_proc_entry("fs/nfs/time-diff-margin", + S_IFREG|S_IRUGO|S_IWUSR, 0))) + return; + nfsd_ent->read_proc = nfsd_proc_read_long_maxmin; + nfsd_ent->write_proc = nfsd_proc_write_long_maxmin; + nfsd_ent->data = (void *) &time_diff_margin; } @@ -80,8 +137,7 @@ nfsd_init(void) nfsd_cache_init(); /* RPC reply cache */ nfsd_export_init(); /* Exports table */ nfsd_lockd_init(); /* lockd->nfsd callbacks */ - nfsd_fh_init(); /* FH table */ - proc_export_init(); + nfsd_proc_init(); initialized = 1; } @@ -209,6 +265,11 @@ asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp) goto done; } + if (cmd >= NFSCTL_LOCKD) { + err = lockdctl(cmd, argp, resp); + goto done; + } + switch(cmd) { case NFSCTL_SVC: err = nfsctl_svc(&arg->ca_svc); @@ -303,6 +364,7 @@ cleanup_module(void) nfsd_export_shutdown(); nfsd_cache_shutdown(); nfsd_fh_free(); + remove_proc_entry("fs/nfs/time-diff-margin", NULL); remove_proc_entry("fs/nfs/exports", NULL); remove_proc_entry("fs/nfs", NULL); nfsd_stat_shutdown(); diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 2dcf3e354189..cf7817617d6e 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -22,6 +22,7 @@ #define NFSDDBG_FACILITY NFSDDBG_FH #define NFSD_PARANOIA 1 /* #define NFSD_DEBUG_VERBOSE 1 */ +/* #define NFSD_DEBUG_VERY_VERBOSE 1 */ extern unsigned long max_mapnr; @@ -34,16 +35,16 @@ struct fh_entry { kdev_t dev; }; -#define NFSD_MAXFH PAGE_SIZE/sizeof(struct fh_entry) -static struct fh_entry filetable[NFSD_MAXFH]; -static struct fh_entry dirstable[NFSD_MAXFH]; +#define NFSD_MAXFH \ + (((nfsd_nservers + 1) >> 1) * PAGE_SIZE/sizeof(struct fh_entry)) +static struct fh_entry *filetable = NULL; +static struct fh_entry *dirstable = NULL; static int nfsd_nr_verified = 0; static int nfsd_nr_put = 0; static unsigned long nfsd_next_expire = 0; static int add_to_fhcache(struct dentry *, int); -static int nfsd_d_validate(struct dentry *); struct dentry * lookup_inode(kdev_t, ino_t, ino_t); static LIST_HEAD(fixup_head); @@ -51,15 +52,16 @@ static LIST_HEAD(path_inuse); static int nfsd_nr_fixups = 0; static int nfsd_nr_paths = 0; #define NFSD_MAX_PATHS 500 -#define NFSD_MAX_FIXUPAGE 60*HZ +#define NFSD_MAX_FIXUPS 500 +#define NFSD_MAX_FIXUP_AGE 30*HZ struct nfsd_fixup { struct list_head lru; - ino_t dir; + unsigned long reftime; + ino_t dirino; ino_t ino; kdev_t dev; - struct dentry *dentry; - unsigned long reftime; + ino_t new_dirino; }; struct nfsd_path { @@ -71,7 +73,8 @@ struct nfsd_path { char name[1]; }; -static struct nfsd_fixup * find_cached_lookup(kdev_t dev, ino_t dir, ino_t ino) +static struct nfsd_fixup * +find_cached_lookup(kdev_t dev, ino_t dirino, ino_t ino) { struct list_head *tmp = fixup_head.next; @@ -79,32 +82,43 @@ static struct nfsd_fixup * find_cached_lookup(kdev_t dev, ino_t dir, ino_t ino) struct nfsd_fixup *fp; fp = list_entry(tmp, struct nfsd_fixup, lru); +#ifdef NFSD_DEBUG_VERY_VERBOSE +printk("fixup %lu %lu, %lu %lu %s %s\n", + fp->ino, ino, + fp->dirino, dirino, + kdevname(fp->dev), kdevname(dev)); +#endif if (fp->ino != ino) continue; - if (fp->dir != dir) + if (fp->dirino != dirino) continue; if (fp->dev != dev) continue; + fp->reftime = jiffies; list_del(tmp); list_add(tmp, &fixup_head); - fp->reftime = jiffies; return fp; } return NULL; } /* - * Save the dentry pointer from a successful lookup. + * Save the dirino from a rename. */ -static void add_to_lookup_cache(struct dentry *dentry, struct knfs_fh *fh) +void +add_to_rename_cache(ino_t new_dirino, + kdev_t dev, ino_t dirino, ino_t ino) { struct nfsd_fixup *fp; - fp = find_cached_lookup(u32_to_kdev_t(fh->fh_dev), - u32_to_ino_t(fh->fh_dirino), - u32_to_ino_t(fh->fh_ino)); + if (dirino == new_dirino) + return; + + fp = find_cached_lookup(dev, + dirino, + ino); if (fp) { - fp->dentry = dentry; + fp->new_dirino = new_dirino; return; } @@ -115,19 +129,30 @@ static void add_to_lookup_cache(struct dentry *dentry, struct knfs_fh *fh) */ fp = kmalloc(sizeof(struct nfsd_fixup), GFP_KERNEL); if (fp) { - fp->dir = u32_to_kdev_t(fh->fh_dirino); - fp->ino = u32_to_ino_t(fh->fh_ino); - fp->dev = u32_to_ino_t(fh->fh_dev); - fp->dentry = dentry; - fp->reftime = jiffies; + fp->dirino = dirino; + fp->ino = ino; + fp->dev = dev; + fp->new_dirino = new_dirino; list_add(&fp->lru, &fixup_head); nfsd_nr_fixups++; } } +/* + * Save the dentry pointer from a successful lookup. + */ + static void free_fixup_entry(struct nfsd_fixup *fp) { list_del(&fp->lru); +#ifdef NFSD_DEBUG_VERY_VERBOSE +printk("free_rename_entry: %lu->%lu %lu/%s\n", + fp->dirino, + fp->new_dirino, + fp->ino, + kdevname(fp->dev), + (jiffies - fp->reftime)); +#endif kfree(fp); nfsd_nr_fixups--; } @@ -212,9 +237,9 @@ restart: if (new) { new->users = 0; new->reftime = jiffies; - new->ino = inode->i_ino; - new->dev = inode->i_dev; - result = copy_path(new->name, dentry, len); + new->ino = inode->i_ino; + new->dev = inode->i_dev; + result = copy_path(new->name, dentry, len); if (!result) goto retry; list_add(&new->lru, &path_inuse); @@ -301,8 +326,8 @@ static int filldir_one(void * __buf, const char * name, int len, int result = 0; buf->sequence++; -#ifdef NFSD_DEBUG_VERBOSE -printk("filldir_one: seq=%d, ino=%ld, name=%s\n", buf->sequence, ino, name); +#ifdef NFSD_DEBUG_VERY_VERBOSE +printk("filldir_one: seq=%d, ino=%lu, name=%s\n", buf->sequence, ino, name); #endif if (buf->sequence == 2) { buf->dirino = ino; @@ -311,7 +336,7 @@ printk("filldir_one: seq=%d, ino=%ld, name=%s\n", buf->sequence, ino, name); if (dirent->ino == ino) { dirent->len = len; memcpy(dirent->name, name, len); - dirent->name[len] = 0; + dirent->name[len] = '\0'; buf->found = 1; result = -1; } @@ -431,7 +456,7 @@ struct dentry * lookup_inode(kdev_t dev, ino_t dirino, ino_t ino) } result = ERR_PTR(-ENOENT); - dir = iget(sb, dirino); + dir = iget_in_use(sb, dirino); if (!dir) goto out_root; dentry = d_alloc_root(dir, NULL); @@ -483,7 +508,7 @@ out_iput: iput(dir); out_root: dput(root); - goto out; + goto out_page; } /* @@ -521,7 +546,8 @@ static void expire_fhe(struct fh_entry *empty, int cache) struct dentry *dentry = empty->dentry; #ifdef NFSD_DEBUG_VERBOSE -printk("expire_fhe: expiring %s/%s, d_count=%d, ino=%ld\n", +printk("expire_fhe: expiring %s %s/%s, d_count=%d, ino=%lu\n", +(cache == NFSD_FILE_CACHE) ? "file" : "dir", dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count,empty->ino); #endif empty->dentry = NULL; /* no dentry */ @@ -576,11 +602,10 @@ out: */ static void expire_old(int cache, int age) { - struct list_head *tmp; struct fh_entry *fhe; int i; -#ifdef NFSD_DEBUG_VERBOSE +#ifdef NFSD_DEBUG_VERY_VERBOSE printk("expire_old: expiring %s older than %d\n", (cache == NFSD_FILE_CACHE) ? "file" : "dir", age); #endif @@ -593,12 +618,12 @@ printk("expire_old: expiring %s older than %d\n", } /* - * Remove old entries from the patch-up cache. + * Trim the fixup cache ... */ - while ((tmp = fixup_head.prev) != &fixup_head) { + while (nfsd_nr_fixups > NFSD_MAX_FIXUPS) { struct nfsd_fixup *fp; - fp = list_entry(tmp, struct nfsd_fixup, lru); - if ((jiffies - fp->reftime) < NFSD_MAX_FIXUPAGE) + fp = list_entry(fixup_head.prev, struct nfsd_fixup, lru); + if ((jiffies - fp->reftime) < NFSD_MAX_FIXUP_AGE) break; free_fixup_entry(fp); } @@ -736,15 +761,21 @@ dentry->d_parent->d_name.name, dentry->d_name.name, ino); dget(dentry); nfsd_nr_verified++; } + put_path(pe); } else { dput(res); + put_path(pe); + /* We should delete it from the cache. */ + free_path_entry(pe); } } else { #ifdef NFSD_PARANOIA printk("find_dentry_by_ino: %s lookup failed\n", pe->name); #endif + put_path(pe); + /* We should delete it from the cache. */ + free_path_entry(pe); } - put_path(pe); } out: return dentry; @@ -757,6 +788,8 @@ out: */ static struct dentry *find_dentry_in_fhcache(struct knfs_fh *fh) { +/* FIXME: this must use the dev/ino/dir_ino triple. */ +#if 0 struct fh_entry * fhe; fhe = find_fhe(fh->fh_dcookie, NFSD_FILE_CACHE, NULL); @@ -794,6 +827,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name); return dentry; } out: +#endif return NULL; } @@ -822,7 +856,7 @@ printk("lookup_by_inode: ino %ld not found in %s\n", ino, parent->d_name.name); printk("lookup_by_inode: found %s\n", dirent.name); #endif - dentry = lookup_dentry(dirent.name, dget(parent), 0); + dentry = lookup_dentry(dirent.name, parent, 0); if (!IS_ERR(dentry)) { if (dentry->d_inode && dentry->d_inode->i_ino == ino) goto out; @@ -842,13 +876,12 @@ no_entry: dentry = NULL; out: return dentry; - } /* * Search the fix-up list for a dentry from a prior lookup. */ -static struct dentry *nfsd_cached_lookup(struct knfs_fh *fh) +static ino_t nfsd_cached_lookup(struct knfs_fh *fh) { struct nfsd_fixup *fp; @@ -856,8 +889,8 @@ static struct dentry *nfsd_cached_lookup(struct knfs_fh *fh) u32_to_ino_t(fh->fh_dirino), u32_to_ino_t(fh->fh_ino)); if (fp) - return fp->dentry; - return NULL; + return fp->new_dirino; + return 0; } void @@ -916,8 +949,12 @@ expire_by_dentry(struct dentry *dentry) static struct dentry * find_fh_dentry(struct knfs_fh *fh) { + struct super_block *sb; struct dentry *dentry, *parent; + struct inode * inode; + struct list_head *lst; int looked_up = 0, retry = 0; + ino_t dirino; /* * Stage 1: Look for the dentry in the short-term fhcache. @@ -927,50 +964,73 @@ find_fh_dentry(struct knfs_fh *fh) nfsdstats.fh_cached++; goto out; } - /* - * Stage 2: Attempt to validate the dentry in the file handle. + * Stage 2: Attempt to find the inode. */ - dentry = fh->fh_dcookie; + sb = get_super(fh->fh_dev); + if (NULL == sb) { + printk("find_fh_dentry: No SuperBlock for device %s.", + kdevname(fh->fh_dev)); + dentry = NULL; + goto out; + } + + dirino = u32_to_ino_t(fh->fh_dirino); + inode = iget_in_use(sb, fh->fh_ino); + if (!inode) { + dprintk("find_fh_dentry: No inode found.\n"); + goto out_five; + } + goto check; recheck: - if (nfsd_d_validate(dentry)) { - struct inode * dir = dentry->d_parent->d_inode; + if (!inode) { + dprintk("find_fh_dentry: No inode found.\n"); + goto out_three; + } +check: + for (lst = inode->i_dentry.next; + lst != &inode->i_dentry; + lst = lst->next) { + dentry = list_entry(lst, struct dentry, d_alias); + +/* if we are looking up a directory then we don't need the parent! */ + if (!dentry || + !dentry->d_parent || + !dentry->d_parent->d_inode) { +printk("find_fh_dentry: Found a useless inode %lu\n", inode->i_ino); + continue; + } + if (dentry->d_parent->d_inode->i_ino != dirino) + continue; - if (dir->i_ino == u32_to_ino_t(fh->fh_dirino) && - dir->i_dev == u32_to_kdev_t(fh->fh_dev)) { - struct inode * inode = dentry->d_inode; - /* - * NFS file handles must always have an inode, - * so we won't accept a negative dentry. - */ - if (inode && inode->i_ino == u32_to_ino_t(fh->fh_ino)) { - dget(dentry); -#ifdef NFSD_DEBUG_VERBOSE -printk("find_fh_dentry: validated %s/%s, ino=%ld\n", -dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino); -#endif - if (!retry) - nfsdstats.fh_valid++; - else { - nfsdstats.fh_fixup++; + dget(dentry); + iput(inode); #ifdef NFSD_DEBUG_VERBOSE -printk("find_fh_dentry: retried validation successful\n"); + printk("find_fh_dentry: Found%s %s/%s filehandle dirino = %lu, %lu\n", + retry ? " Renamed" : "", + dentry->d_parent->d_name.name, + dentry->d_name.name, + dentry->d_parent->d_inode->i_ino, + dirino); #endif - } - goto out; - } - } - } + goto out; + } /* for inode->i_dentry */ /* - * Before proceeding to a lookup, check whether we cached a - * prior lookup. If so, try to validate that dentry ... + * Before proceeding to a lookup, check for a rename */ - if (!retry && (dentry = nfsd_cached_lookup(fh)) != NULL) { + if (!retry && (dirino = nfsd_cached_lookup(fh))) { + dprintk("find_fh_dentry: retry with %lu\n", dirino); retry = 1; goto recheck; } + iput(inode); + + dprintk("find_fh_dentry: dirino not found %lu\n", dirino); + +out_three: + /* * Stage 3: Look up the dentry based on the inode and parent inode * numbers. This should work for all Unix-like filesystems. @@ -983,7 +1043,7 @@ printk("find_fh_dentry: retried validation successful\n"); struct inode * inode = dentry->d_inode; #ifdef NFSD_DEBUG_VERBOSE printk("find_fh_dentry: looked up %s/%s\n", -dentry->d_parent->d_name.name, dentry->d_name.name); + dentry->d_parent->d_name.name, dentry->d_name.name); #endif if (inode && inode->i_ino == u32_to_ino_t(fh->fh_ino)) { nfsdstats.fh_lookup++; @@ -991,7 +1051,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name); } #ifdef NFSD_PARANOIA printk("find_fh_dentry: %s/%s lookup mismatch!\n", -dentry->d_parent->d_name.name, dentry->d_name.name); + dentry->d_parent->d_name.name, dentry->d_name.name); #endif dput(dentry); } @@ -1005,29 +1065,27 @@ dentry->d_parent->d_name.name, dentry->d_name.name); /* * ... then search for the inode in the parent directory. */ + dget(parent); dentry = lookup_by_inode(parent, u32_to_ino_t(fh->fh_ino)); dput(parent); if (dentry) goto out; } +out_five: + /* - * Stage 5: Search the whole volume. + * Stage 5: Search the whole volume, Yea Right. */ #ifdef NFSD_PARANOIA -printk("find_fh_dentry: %s, %u/%u not found -- need full search!\n", -kdevname(u32_to_kdev_t(fh->fh_dev)), fh->fh_dirino, fh->fh_ino); +printk("find_fh_dentry: %s/%u dir/%u not found!\n", + kdevname(u32_to_kdev_t(fh->fh_dev)), fh->fh_ino, fh->fh_dirino); #endif dentry = NULL; nfsdstats.fh_stale++; out: - if (looked_up && dentry) { - add_to_lookup_cache(dentry, fh); - } - expire_all(); - return dentry; } @@ -1046,8 +1104,12 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) struct inode *inode; u32 error = 0; - dprintk("nfsd: fh_verify(exp %x/%u cookie %p)\n", - fh->fh_xdev, fh->fh_xino, fh->fh_dcookie); + dprintk("nfsd: fh_verify(exp %s/%u file (%s/%u dir %u)\n", + kdevname(fh->fh_xdev), + fh->fh_xino, + kdevname(fh->fh_dev), + fh->fh_ino, + fh->fh_dirino); if (fhp->fh_dverified) goto check_type; @@ -1056,10 +1118,13 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) */ error = nfserr_stale; exp = exp_get(rqstp->rq_client, - u32_to_kdev_t(fh->fh_xdev), - u32_to_ino_t(fh->fh_xino)); - if (!exp) /* export entry revoked */ + u32_to_kdev_t(fh->fh_xdev), + u32_to_ino_t(fh->fh_xino)); + if (!exp) { + /* export entry revoked */ + nfsdstats.fh_stale++; goto out; + } /* Check if the request originated from a secure port. */ error = nfserr_perm; @@ -1079,8 +1144,13 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) */ error = nfserr_noent; dentry = find_fh_dentry(fh); - if (!dentry) + if (!dentry) { + goto out; + } + if (IS_ERR(dentry)) { + error = nfserrno(-PTR_ERR(dentry)); goto out; + } /* * Note: it's possible the returned dentry won't be the one in the @@ -1102,6 +1172,35 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) check_type: dentry = fhp->fh_dentry; inode = dentry->d_inode; + error = nfserr_stale; + /* On a heavily loaded SMP machine, more than one identical + requests may run at the same time on different processors. + One thread may get here with unfinished fh after another + thread just fetched the inode. It doesn't make any senses + to check fh->fh_generation here since it has not been set + yet. In that case, we shouldn't send back the stale + filehandle to the client. We use fh->fh_dcookie to indicate + if fh->fh_generation is set or not. If fh->fh_dcookie is + not set, don't return stale filehandle. */ + if (inode->i_generation != fh->fh_generation) { + if (fh->fh_dcookie) { + dprintk("fh_verify: Bad version %u %u %u: 0x%x, 0x%x\n", + inode->i_ino, + inode->i_generation, + fh->fh_generation, + type, access); + nfsdstats.fh_stale++; + goto out; + } + else { + /* We get here when inode is fetched by other + threads. We just use what is in there. */ + fh->fh_ino = ino_t_to_u32(inode->i_ino); + fh->fh_generation = inode->i_generation; + fh->fh_dcookie = (struct dentry *)0xfeebbaca; + nfsdstats.fh_concurrent++; + } + } exp = fhp->fh_export; if (type > 0 && (inode->i_mode & S_IFMT) != type) { error = (type == S_IFDIR)? nfserr_notdir : nfserr_isdir; @@ -1117,9 +1216,10 @@ check_type: */ error = 0; if (fh->fh_dev != fh->fh_xdev) { - printk("fh_verify: Security: export on other device" - " (%d, %d).\n", fh->fh_dev, fh->fh_xdev); + printk("fh_verify: Security: export on other device (%s, %s).\n", + kdevname(fh->fh_dev), kdevname(fh->fh_xdev)); error = nfserr_stale; + nfsdstats.fh_stale++; } else if (exp->ex_dentry != dentry) { struct dentry *tdentry = dentry; @@ -1128,19 +1228,21 @@ check_type: if (exp->ex_dentry == tdentry) break; /* executable only by root and we can't be root */ - if (current->fsuid && - !(tdentry->d_inode->i_uid && - (tdentry->d_inode->i_mode & S_IXUSR)) && - !(tdentry->d_inode->i_gid && - (tdentry->d_inode->i_mode & S_IXGRP)) && - !(tdentry->d_inode->i_mode & S_IXOTH) && - (exp->ex_flags & NFSEXP_ROOTSQUASH)) { + if (current->fsuid + && !(tdentry->d_inode->i_uid + && (tdentry->d_inode->i_mode & S_IXUSR)) + && !(tdentry->d_inode->i_gid + && (tdentry->d_inode->i_mode & S_IXGRP)) + && !(tdentry->d_inode->i_mode & S_IXOTH) + && (exp->ex_flags & NFSEXP_ROOTSQUASH)) { error = nfserr_stale; + nfsdstats.fh_stale++; dprintk("fh_verify: no root_squashed access.\n"); } } while ((tdentry != tdentry->d_parent)); if (exp->ex_dentry != tdentry) { error = nfserr_stale; + nfsdstats.fh_stale++; printk("nfsd Security: %s/%s bad export.\n", dentry->d_parent->d_name.name, dentry->d_name.name); @@ -1194,6 +1296,7 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry) if (inode) { fhp->fh_handle.fh_ino = ino_t_to_u32(inode->i_ino); fhp->fh_handle.fh_generation = inode->i_generation; + fhp->fh_handle.fh_dcookie = (struct dentry *)0xfeebbaca; } fhp->fh_handle.fh_dirino = ino_t_to_u32(parent->d_inode->i_ino); fhp->fh_handle.fh_dev = kdev_t_to_u32(parent->d_inode->i_dev); @@ -1226,7 +1329,8 @@ fh_update(struct svc_fh *fhp) goto out_negative; fhp->fh_handle.fh_ino = ino_t_to_u32(inode->i_ino); fhp->fh_handle.fh_generation = inode->i_generation; -out: + fhp->fh_handle.fh_dcookie = (struct dentry *)0xfeebbaca; + out: return; out_bad: @@ -1264,51 +1368,6 @@ out_bad: return; } -/* - * Verify that the FH dentry is still a valid dentry pointer. - * After making some preliminary checks, we ask VFS to verify - * that it is indeed a dentry. - */ -static int nfsd_d_validate(struct dentry *dentry) -{ - unsigned long dent_addr = (unsigned long) dentry; - unsigned long min_addr = PAGE_OFFSET; - unsigned long max_addr = min_addr + (max_mapnr << PAGE_SHIFT); - unsigned long align_mask = 0x0F; - unsigned int len; - int valid = 0; - - if (dent_addr < min_addr) - goto bad_addr; - if (dent_addr > max_addr - sizeof(struct dentry)) - goto bad_addr; - if ((dent_addr & ~align_mask) != dent_addr) - goto bad_align; - if (!kern_addr_valid(dent_addr)) - goto bad_addr; - /* - * Looks safe enough to dereference ... - */ - len = dentry->d_name.len; - if (len > NFS_MAXNAMLEN) - goto out; - /* - * Note: d_validate doesn't dereference the parent pointer ... - * just combines it with the name hash to find the hash chain. - */ - valid = d_validate(dentry, dentry->d_parent, dentry->d_name.hash, len); - -out: - return valid; - -bad_addr: - printk(KERN_DEBUG "nfsd_d_validate: invalid address %lx\n", dent_addr); - goto out; -bad_align: - printk(KERN_DEBUG "nfsd_d_validate: unaligned address %lx\n", dent_addr); - goto out; -} - /* * Flush any cached dentries for the specified device * or for all devices. @@ -1338,7 +1397,7 @@ void nfsd_fh_flush(kdev_t dev) } /* - * Free the dentry and path caches. + * Free the rename and path caches. */ void nfsd_fh_free(void) { @@ -1375,18 +1434,33 @@ void nfsd_fh_free(void) void nfsd_fh_init(void) { - /* Sanity check */ extern void __my_nfsfh_is_too_big(void); + + if (filetable) + return; + + /* Sanity check */ if (sizeof(struct nfs_fhbase) > 32) __my_nfsfh_is_too_big(); + filetable = kmalloc(sizeof(struct fh_entry) * NFSD_MAXFH, + GFP_KERNEL); + dirstable = kmalloc(sizeof(struct fh_entry) * NFSD_MAXFH, + GFP_KERNEL); + + if (filetable == NULL || dirstable == NULL) { + printk(KERN_WARNING "nfsd_fh_init : Could not allocate fhcache\n"); + nfsd_nservers = 0; + return; + } + memset(filetable, 0, NFSD_MAXFH*sizeof(struct fh_entry)); memset(dirstable, 0, NFSD_MAXFH*sizeof(struct fh_entry)); INIT_LIST_HEAD(&path_inuse); INIT_LIST_HEAD(&fixup_head); printk(KERN_DEBUG - "nfsd_init: initialized fhcache, entries=%lu\n", NFSD_MAXFH); + "nfsd_fh_init : initialized fhcache, entries=%lu\n", NFSD_MAXFH); /* * Display a warning if the ino_t is larger than 32 bits. */ @@ -1395,3 +1469,15 @@ void nfsd_fh_init(void) "NFSD: ino_t is %d bytes, using lower 4 bytes\n", sizeof(ino_t)); } + +void +nfsd_fh_shutdown(void) +{ + if (!filetable) + return; + printk(KERN_DEBUG + "nfsd_fh_shutdown : freeing %d fhcache entries.\n", NFSD_MAXFH); + kfree(filetable); + kfree(dirstable); + filetable = dirstable = NULL; +} diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index 87de16aad5ed..971bab42088a 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -270,7 +270,7 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp, rdev = (dev_t) size; if (type != S_IFBLK && type != S_IFCHR) { rdev = 0; - } else if (type == S_IFCHR && size == ~(u32) 0) { + } else if (type == S_IFCHR && !(attr->ia_valid & ATTR_SIZE)) { /* If you think you've seen the worst, grok this. */ attr->ia_mode = S_IFIFO | mode; type = S_IFIFO; @@ -288,6 +288,10 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp, if (inode && (type != (inode->i_mode & S_IFMT) || (is_borc && inode->i_rdev != rdev))) goto out_unlock; + + /* invalidate size because only (type == S_IFREG) has + size. */ + attr->ia_valid &= ~ATTR_SIZE; } nfserr = 0; diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index b7fa534e065b..452829812a1d 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -5,7 +5,7 @@ * * Authors: Olaf Kirch (okir@monad.swb.de) * - * Copyright (C) 1995, 1996, 1997 Olaf Kirch + * Copyright (C) 1995-1999 Olaf Kirch */ #define __NO_VERSION__ @@ -63,6 +63,11 @@ nfsd_svc(unsigned short port, int nrservs) nrservs = NFSD_MAXSERVS; nfsd_nservers = nrservs; + error = -ENOMEM; + nfsd_fh_init(); /* NFS dentry cache */ + if (nfsd_nservers == 0) + goto out; + error = -ENOMEM; nfsd_racache_init(); /* Readahead param cache */ if (nfsd_nservers == 0) @@ -119,7 +124,9 @@ nfsd(struct svc_rqst *rqstp) nfssvc_boot = xtime; /* record boot time */ first = 1; } +#if 0 lockd_up(); /* start lockd */ +#endif /* * The main request loop @@ -177,12 +184,16 @@ nfsd(struct svc_rqst *rqstp) printk(KERN_WARNING "nfsd: terminating on signal %d\n", signo); } +#if 0 /* Release lockd */ lockd_down(); +#endif if (!--nfsd_active) { printk("nfsd: last server exiting\n"); /* revoke all exports */ nfsd_export_shutdown(); + /* release fhcache */ + nfsd_fh_shutdown (); /* release read-ahead cache */ nfsd_racache_shutdown(); } diff --git a/fs/nfsd/stats.c b/fs/nfsd/stats.c index accd0aadc3df..ca9e7ae23177 100644 --- a/fs/nfsd/stats.c +++ b/fs/nfsd/stats.c @@ -32,7 +32,7 @@ nfsd_proc_read(char *buffer, char **start, off_t offset, int count, { int len; - len = sprintf(buffer, "rc %d %d %d %d %d %d %d %d\n", + len = sprintf(buffer, "rc %d %d %d %d %d %d %d %d %d\n", nfsdstats.rchits, nfsdstats.rcmisses, nfsdstats.rcnocache, @@ -40,7 +40,8 @@ nfsd_proc_read(char *buffer, char **start, off_t offset, int count, nfsdstats.fh_valid, nfsdstats.fh_fixup, nfsdstats.fh_lookup, - nfsdstats.fh_stale); + nfsdstats.fh_stale, + nfsdstats.fh_concurrent); /* Assume we haven't hit EOF yet. Will be set by svc_proc_read. */ *eof = 0; diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index ab577897e62b..c3e3e0db55ea 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -48,9 +47,15 @@ /* Hack until we have a macro check for mandatory locks. */ #ifndef IS_ISMNDLK -#define IS_ISMNDLK(i) (((i)->i_mode & (S_ISGID|S_IXGRP)) == S_ISGID) +#define IS_ISMNDLK(i) (((i)->i_mode & (S_ISGID|S_IXGRP|S_IFMT)) \ + == (S_ISGID|S_IFREG)) #endif +/* Time difference margin in seconds for comparison. It is a + dynamically-tunable parameter via /proc/fs/nfs/time-diff-margin. + */ +extern long nfsd_time_diff_margin; + /* Check for dir entries '.' and '..' */ #define isdotent(n, l) (l < 3 && n[0] == '.' && (l == 1 || n[1] == '.')) @@ -236,8 +241,30 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap) inode = dentry->d_inode; err = inode_change_ok(inode, iap); - if (err) + if (err) { + /* It is very tricky. When you are not the file owner, + but have the write permission, you should be allowed + to set atime and mtime to the current time on the + server. However, the NFS V2 protocol doesn't support + it. It has been fixed in V3. Here we do this: if the + current server time and atime/mtime are close enough, + we use the current server time. */ +#define CURRENT_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET) + if (iap->ia_mtime == iap->ia_atime + && ((iap->ia_valid & (CURRENT_TIME_SET)) + == CURRENT_TIME_SET)) { + time_t now = CURRENT_TIME; + time_t delta = iap->ia_atime - now; + if (delta < 0) delta = -delta; + if (delta <= nfsd_time_diff_margin) { + iap->ia_valid &= ~CURRENT_TIME_SET; + goto current_time_ok; + } + } goto out_nfserr; + } + +current_time_ok: /* The size case is special... */ if (iap->ia_valid & ATTR_SIZE) { @@ -248,15 +275,19 @@ printk("nfsd_setattr: size change??\n"); if (err) goto out; } + DQUOT_INIT(inode); err = get_write_access(inode); - if (err) + if (err) { + DQUOT_DROP(inode); goto out_nfserr; + } /* N.B. Should we update the inode cache here? */ inode->i_size = iap->ia_size; if (inode->i_op && inode->i_op->truncate) inode->i_op->truncate(inode); mark_inode_dirty(inode); put_write_access(inode); + DQUOT_DROP(inode); iap->ia_valid &= ~ATTR_SIZE; iap->ia_valid |= ATTR_MTIME; iap->ia_mtime = CURRENT_TIME; @@ -554,7 +585,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, /* clear setuid/setgid flag after write */ if (err >= 0 && (inode->i_mode & (S_ISUID | S_ISGID))) { struct iattr ia; - kernel_cap_t saved_cap; + kernel_cap_t saved_cap = 0; ia.ia_valid = ATTR_MODE; ia.ia_mode = inode->i_mode & ~(S_ISUID | S_ISGID); @@ -658,10 +689,6 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, if (IS_ERR(dchild)) goto out_nfserr; fh_compose(resfhp, fhp->fh_export, dchild); - /* Lock the parent and check for errors ... */ - err = fh_lock_parent(fhp, dchild); - if (err) - goto out; } else { dchild = resfhp->fh_dentry; if (!fhp->fh_locked) @@ -670,6 +697,15 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, dentry->d_parent->d_name.name, dentry->d_name.name); } + err = nfserr_exist; + if (dchild->d_inode) + goto out; + if (!fhp->fh_locked) { + /* Lock the parent and check for errors ... */ + err = fh_lock_parent(fhp, dchild); + if (err) + goto out; + } /* * Make sure the child dentry is still negative ... */ @@ -695,8 +731,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, case S_IFCHR: case S_IFBLK: /* The client is _NOT_ required to do security enforcement */ - if(!capable(CAP_SYS_ADMIN)) - { + if(!capable(CAP_SYS_ADMIN)) { err = -EPERM; goto out; } @@ -759,7 +794,7 @@ nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size) struct inode *inode; struct iattr newattrs; int err; - kernel_cap_t saved_cap; + kernel_cap_t saved_cap = 0; err = fh_verify(rqstp, fhp, S_IFREG, MAY_WRITE | MAY_TRUNC); if (err) @@ -899,6 +934,7 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, /* Compose the fh so the dentry will be freed ... */ out_compose: fh_compose(resfhp, fhp->fh_export, dnew); + out: return err; @@ -1091,6 +1127,15 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, DQUOT_DROP(tdir); nfsd_double_up(&tdir->i_sem, &fdir->i_sem); + + if (!err && odentry->d_inode) { + add_to_rename_cache(tdir->i_ino, + odentry->d_inode->i_dev, + fdir->i_ino, + odentry->d_inode->i_ino); + } else { + printk(": no inode in rename or err: %d.\n", err); + } dput(ndentry); out_dput_old: @@ -1132,7 +1177,6 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, err = PTR_ERR(rdentry); if (IS_ERR(rdentry)) goto out_nfserr; - if (!rdentry->d_inode) { dput(rdentry); err = nfserr_noent; @@ -1154,7 +1198,7 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, fh_unlock(fhp); dput(rdentry); - + expire_by_dentry(rdentry); } else { /* It's RMDIR */ /* See comments in fs/namei.c:do_rmdir */ @@ -1183,6 +1227,7 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, goto out_nfserr; if (EX_ISSYNC(fhp->fh_export)) write_inode_now(dirp); + out: return err; @@ -1314,7 +1359,7 @@ nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc) { struct inode *inode = dentry->d_inode; int err; - kernel_cap_t saved_cap; + kernel_cap_t saved_cap = 0; if (acc == MAY_NOP) return 0; @@ -1334,7 +1379,7 @@ nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc) inode->i_uid, inode->i_gid, current->fsuid, current->fsgid); #endif #ifndef CONFIG_NFSD_SUN - if (dentry->d_mounts != dentry) { + if (dentry->d_mounts != dentry) { return nfserr_perm; } #endif diff --git a/include/asm-alpha/delay.h b/include/asm-alpha/delay.h index a55752abb0e9..b8b69d90f8f2 100644 --- a/include/asm-alpha/delay.h +++ b/include/asm-alpha/delay.h @@ -9,12 +9,18 @@ * Delay routines, using a pre-computed "loops_per_second" value. */ +/* We can make the delay loop inline, but we have to be very careful + * WRT scheduling for EV6 machines, to keep it consistent for all locations + * of invocations. This is a reasonable compromise. + */ + extern __inline__ void __delay(unsigned long loops) { - __asm__ __volatile__(".align 3\n" - "1:\tsubq %0,1,%0\n\t" - "bge %0,1b": "=r" (loops) : "0" (loops)); + __asm__ __volatile__(".align 4\n" + "1:\tsubq %0,1,%0\n\t" + "bge %0,1b\n\t" + "nop": "=r" (loops) : "0" (loops)); } /* diff --git a/include/asm-alpha/io.h b/include/asm-alpha/io.h index 7d8bdb94b2b7..5eb7823def6e 100644 --- a/include/asm-alpha/io.h +++ b/include/asm-alpha/io.h @@ -261,6 +261,8 @@ static inline void iounmap(void *addr) { } +#define ioremap_nocache(offset, size) ioremap((offset),(size)) + #ifndef readb # define readb(a) _readb((unsigned long)(a)) #endif diff --git a/include/asm-i386/fixmap.h b/include/asm-i386/fixmap.h index c259a45ee837..ee7b04db991e 100644 --- a/include/asm-i386/fixmap.h +++ b/include/asm-i386/fixmap.h @@ -45,7 +45,10 @@ enum fixed_addresses { FIX_APIC_BASE, /* local (CPU) APIC) -- required for SMP or not */ #endif #ifdef CONFIG_X86_IO_APIC - FIX_IO_APIC_BASE, + FIX_IO_APIC_BASE_0, /* IF YOU CHANGE THIS, PLEASE ALSO CHANGE */ + FIX_IO_APIC_BASE_1, /* MAX_IO_APICS in arch/i386/irq.h */ + FIX_IO_APIC_BASE_2, + FIX_IO_APIC_BASE_3, #endif #ifdef CONFIG_X86_VISWS_APIC FIX_CO_CPU, /* Cobalt timer */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 93a39537ac5c..32bd71827d6a 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -837,6 +837,7 @@ extern void iput(struct inode *); extern struct inode * igrab(struct inode *inode); extern ino_t iunique(struct super_block *, ino_t); extern struct inode * iget(struct super_block *, unsigned long); +extern struct inode * iget_in_use (struct super_block *, unsigned long); extern void clear_inode(struct inode *); extern struct inode * get_empty_inode(void); diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 45a6dc35be73..1b6253190f50 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -42,7 +42,7 @@ #endif #define DM6_PARTITION 0x54 /* has DDO: use xlated geom & offset */ -#define EZD_PARTITION 0x55 /* EZ-DRIVE: same as DM6 (we think) */ +#define EZD_PARTITION 0x55 /* EZ-DRIVE: remap sector 1 to 0 */ #define DM6_AUX1PARTITION 0x51 /* no DDO: use xlated geom */ #define DM6_AUX3PARTITION 0x53 /* no DDO: use xlated geom */ diff --git a/include/linux/lockd/syscall.h b/include/linux/lockd/syscall.h new file mode 100644 index 000000000000..fd06d70a4f45 --- /dev/null +++ b/include/linux/lockd/syscall.h @@ -0,0 +1,26 @@ +/* + * include/linux/lockd/syscall.h + * + * This file holds all declarations for the knfsd/lockd syscall + * interface. + * + */ + +#ifndef LOCKD_SYSCALL_H +#define LOCKD_SYSCALL_H + +#include + +#define LOCKDCTL_SVC NFSCTL_LOCKD + +#ifdef __KERNEL__ +/* + * Kernel syscall implementation. + */ +#if defined(CONFIG_LOCKD) || defined(CONFIG_LOCKD_MODULE) +extern int lockdctl(int, void *, void *); +#endif + +#endif /* __KERNEL__ */ + +#endif /* LOCKD_SYSCALL_H */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 6d43f98d7a0c..2acec5295f43 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -266,6 +266,9 @@ struct device struct Qdisc *qdisc_list; unsigned long tx_queue_len; /* Max frames per queue allowed */ + /* Bridge stuff */ + int bridge_port_id; + /* Pointers to interface service routines. */ int (*open)(struct device *dev); int (*stop)(struct device *dev); diff --git a/include/linux/nfsd/nfsfh.h b/include/linux/nfsd/nfsfh.h index a0ba9587f4c2..ba7fef44fd67 100644 --- a/include/linux/nfsd/nfsfh.h +++ b/include/linux/nfsd/nfsfh.h @@ -107,6 +107,7 @@ void fh_update(struct svc_fh *); void fh_put(struct svc_fh *); void nfsd_fh_flush(kdev_t); void nfsd_fh_init(void); +void nfsd_fh_shutdown(void); void nfsd_fh_free(void); void expire_all(void); @@ -182,6 +183,11 @@ fh_unlock(struct svc_fh *fhp) } } +/* + * This is a long term cache to help find renamed files. + */ +void add_to_rename_cache(ino_t new_dirino, kdev_t dev, ino_t dirino, ino_t ino); + #endif /* __KERNEL__ */ #endif /* NFSD_FH_H */ diff --git a/include/linux/nfsd/stats.h b/include/linux/nfsd/stats.h index 4ce3f06cdc77..194919e566cc 100644 --- a/include/linux/nfsd/stats.h +++ b/include/linux/nfsd/stats.h @@ -18,6 +18,7 @@ struct nfsd_stats { unsigned int fh_fixup; /* dentry fixup validated */ unsigned int fh_lookup; /* new lookup required */ unsigned int fh_stale; /* FH stale error */ + unsigned int fh_concurrent; /* concurrent request */ }; #ifdef __KERNEL__ diff --git a/include/linux/nfsd/syscall.h b/include/linux/nfsd/syscall.h index 019d33f7664e..f7bd1174dd96 100644 --- a/include/linux/nfsd/syscall.h +++ b/include/linux/nfsd/syscall.h @@ -35,6 +35,9 @@ #define NFSCTL_GETFH 6 /* get an fh by ino (used by mountd) */ #define NFSCTL_GETFD 7 /* get an fh by path (used by mountd) */ +/* Above this is for lockd. */ +#define NFSCTL_LOCKD 0x10000 + /* SVC */ struct nfsctl_svc { unsigned short svc_port; @@ -121,11 +124,7 @@ union nfsctl_res { /* * Kernel syscall implementation. */ -#if defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE) extern asmlinkage int sys_nfsservctl(int, void *, void *); -#else -#define sys_nfsservctl sys_ni_syscall -#endif extern int exp_addclient(struct nfsctl_client *ncp); extern int exp_delclient(struct nfsctl_client *ncp); extern int exp_export(struct nfsctl_export *nxp); diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 1f5a62dd9926..9bf5efd806e4 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -38,11 +38,15 @@ static inline unsigned long page_address(struct page * page) */ #define page_cache_entry(x) (mem_map + MAP_NR(x)) -#define PAGE_HASH_BITS 12 -#define PAGE_HASH_SIZE (1 << PAGE_HASH_BITS) +#define PAGE_HASH_BITS page_hash_bits +#define PAGE_HASH_MASK page_hash_mask extern unsigned long page_cache_size; /* # of pages currently in the hash table */ -extern struct page * page_hash_table[PAGE_HASH_SIZE]; +extern unsigned int page_hash_bits; +extern unsigned int page_hash_mask; +extern struct page **page_hash_table; + +extern void page_cache_init(unsigned long); /* * We use a power-of-two hash table to avoid a modulus, @@ -53,12 +57,10 @@ extern struct page * page_hash_table[PAGE_HASH_SIZE]; static inline unsigned long _page_hashfn(struct inode * inode, unsigned long offset) { #define i (((unsigned long) inode)/(sizeof(struct inode) & ~ (sizeof(struct inode) - 1))) -#define o (offset >> PAGE_SHIFT) -#define s(x) ((x)+((x)>>PAGE_HASH_BITS)) - return s(i+o) & (PAGE_HASH_SIZE-1); +#define o ((offset >> PAGE_SHIFT) + (offset & ~PAGE_MASK)) + return ((i+o) & PAGE_HASH_MASK); #undef i #undef o -#undef s } #define page_hash(inode,offset) (page_hash_table+_page_hashfn(inode,offset)) diff --git a/include/linux/pci.h b/include/linux/pci.h index 8f5eda0c6c34..2efb9438c802 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1067,6 +1067,7 @@ #define PCI_DEVICE_ID_INTEL_82441 0x1237 #define PCI_DEVICE_ID_INTEL_82380FB 0x124b #define PCI_DEVICE_ID_INTEL_82439 0x1250 +#define PCI_DEVICE_ID_INTEL_MEGARAID 0x1960 #define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000 #define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010 #define PCI_DEVICE_ID_INTEL_82371SB_2 0x7020 @@ -1076,17 +1077,19 @@ #define PCI_DEVICE_ID_INTEL_82371AB 0x7111 #define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112 #define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 -#define PCI_VENDOR_ID_COMPUTONE 0x8e0e -#define PCI_DEVICE_ID_COMPUTONE_IP2EX 0x0291 - #define PCI_DEVICE_ID_INTEL_82443LX_0 0x7180 #define PCI_DEVICE_ID_INTEL_82443LX_1 0x7181 #define PCI_DEVICE_ID_INTEL_82443BX_0 0x7190 #define PCI_DEVICE_ID_INTEL_82443BX_1 0x7191 #define PCI_DEVICE_ID_INTEL_82443BX_2 0x7192 #define PCI_DEVICE_ID_INTEL_P6 0x84c4 -#define PCI_DEVICE_ID_INTEL_82450GX 0x84c5 +#define PCI_DEVICE_ID_INTEL_82450GX 0x84c4 +#define PCI_DEVICE_ID_INTEL_82453GX 0x84c5 #define PCI_DEVICE_ID_INTEL_82451NX 0x84ca +#define PCI_DEVICE_ID_INTEL_82454NX 0x84cb + +#define PCI_VENDOR_ID_COMPUTONE 0x8e0e +#define PCI_DEVICE_ID_COMPUTONE_IP2EX 0x0291 #define PCI_VENDOR_ID_KTI 0x8e2e #define PCI_DEVICE_ID_KTI_ET32P2 0x3000 @@ -1318,6 +1321,9 @@ extern inline struct pci_dev *pci_find_class(unsigned int class, struct pci_dev extern inline struct pci_dev *pci_find_slot(unsigned int bus, unsigned int devfn) { return NULL; } +extern inline void pci_set_master(struct pci_dev *dev) +{ return; } + #endif /* !CONFIG_PCI */ #endif /* __KERNEL__ */ diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index e1b0a89363e3..77d7d7413636 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -466,6 +466,7 @@ extern inline void remove_proc_entry(const char *name, struct proc_dir_entry *pa extern inline void proc_tty_register_driver(struct tty_driver *driver) {}; extern inline void proc_tty_unregister_driver(struct tty_driver *driver) {}; +extern struct proc_dir_entry proc_root; -#endif +#endif /* CONFIG_PROC_FS */ #endif /* _LINUX_PROC_FS_H */ diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 4ab59ed1c500..4558fa1f3323 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -13,6 +13,7 @@ #include #include #include +#include /* * RPC service. @@ -29,7 +30,7 @@ struct svc_serv { struct svc_sock * sv_sockets; /* pending sockets */ struct svc_program * sv_program; /* RPC program */ struct svc_stat * sv_stats; /* RPC statistics */ - unsigned int sv_nrthreads; /* # of server threads */ + atomic_t sv_nrthreads; /* # of server threads */ unsigned int sv_bufsz; /* datagram buffer size */ unsigned int sv_xdrsize; /* XDR buffer size */ diff --git a/include/linux/sunrpc/svcsock.h b/include/linux/sunrpc/svcsock.h index 5f8dc87689f1..9e6f3a76aba7 100644 --- a/include/linux/sunrpc/svcsock.h +++ b/include/linux/sunrpc/svcsock.h @@ -10,6 +10,7 @@ #define SUNRPC_SVCSOCK_H #include +#include /* * RPC server socket. @@ -23,7 +24,7 @@ struct svc_sock { struct sock * sk_sk; /* INET layer */ struct svc_serv * sk_server; /* service for this socket */ - unsigned char sk_inuse; /* use count */ + atomic_t sk_inuse; /* use count */ unsigned char sk_busy; /* enqueued/receiving */ unsigned char sk_conn; /* conn pending */ unsigned char sk_close; /* dead or dying */ diff --git a/include/net/br.h b/include/net/br.h index 4d79fc6277d1..cbf4b4c3c472 100644 --- a/include/net/br.h +++ b/include/net/br.h @@ -19,7 +19,16 @@ #define Forwarding 3 /* (4 4 4) */ #define Blocking 4 /* (4.4.1) */ -#define No_of_ports 8 + +/* MAG Yich! Easiest way of giving a configurable number of ports + * If you want more than 32, change BR_MAX_PORTS and recompile brcfg! + */ +#define BR_MAX_PORTS (32) +#if CONFIG_BRIDGE_NUM_PORTS > BR_MAX_PORTS +#undef CONFIG_BRIDGE_NUM_PORTS +#define CONFIG_BRIDGE_NUM_PORTS BR_MAX_PORTS +#endif +#define No_of_ports CONFIG_BRIDGE_NUM_PORTS /* arbitrary choice, to allow the code below to compile */ #define All_ports (No_of_ports + 1) @@ -147,6 +156,7 @@ typedef struct { unsigned int top_change; /* (4.5.3.12) */ unsigned short topology_change_time; /* (4.5.3.13) */ unsigned short hold_time; /* (4.5.3.14) */ + unsigned int instance; } Bridge_data; /** Port Parameters (4.5.5) **/ @@ -160,7 +170,10 @@ typedef struct { unsigned short designated_port; /* (4.5.5.7) */ unsigned int top_change_ack; /* (4.5.5.8) */ unsigned int config_pending; /* (4.5.5.9) */ - struct device *dev; + bridge_id_t ifmac; + unsigned int admin_state; + char ifname[IFNAMSIZ]; /* Make life easier for brcfg */ + struct device *dev; struct fdb *fdb; /* head of per port fdb chain */ } Port_data; @@ -245,13 +258,14 @@ typedef struct { struct br_stat { unsigned int flags; Bridge_data bridge_data; - Port_data port_data[No_of_ports]; unsigned int policy; unsigned int exempt_protocols; unsigned short protocols[BR_MAX_PROTOCOLS]; unsigned short prot_id[BR_MAX_PROT_STATS]; /* Protocol encountered */ unsigned int prot_counter[BR_MAX_PROT_STATS]; /* How many packets ? */ br_stats_counter packet_cnts; + unsigned int num_ports; + Port_data port_data[BR_MAX_PORTS + 1]; }; /* defined flags for br_stat.flags */ @@ -274,7 +288,7 @@ struct br_cf { #define BRCMD_SET_BRIDGE_PRIORITY 5 /* arg1 = priority */ #define BRCMD_SET_PORT_PRIORITY 6 /* arg1 = port, arg2 = priority */ #define BRCMD_SET_PATH_COST 7 /* arg1 = port, arg2 = cost */ -#define BRCMD_DISPLAY_FDB 8 /* arg1 = port */ +#define BRCMD_DISPLAY_FDB 8 #define BRCMD_ENABLE_DEBUG 9 #define BRCMD_DISABLE_DEBUG 10 #define BRCMD_SET_POLICY 11 /* arg1 = default policy (1==bridge all) */ @@ -283,14 +297,19 @@ struct br_cf { #define BRCMD_DISABLE_PROT_STATS 14 #define BRCMD_ZERO_PROT_STATS 15 #define BRCMD_TOGGLE_STP 16 +#define BRCMD_IF_ENABLE 17 /* arg1 = if_index */ +#define BRCMD_IF_DISABLE 18 /* arg1 = if_index */ +#define BRCMD_SET_IF_PRIORITY 19 /* arg1 = if_index, arg2 = priority */ +#define BRCMD_SET_IF_PATH_COST 20 /* arg1 = if_index, arg2 = cost */ /* prototypes of exported bridging functions... */ +#ifdef __KERNEL__ void br_init(void); int br_receive_frame(struct sk_buff *skb); /* 3.5 */ int br_tx_frame(struct sk_buff *skb); +int brg_init(void); int br_ioctl(unsigned int cmd, void *arg); -int br_protocol_ok(unsigned short protocol); void requeue_fdb(struct fdb *node, int new_port); struct fdb *br_avl_find_addr(unsigned char addr[6]); @@ -298,8 +317,15 @@ struct fdb *br_avl_insert (struct fdb * new_node); void sprintf_avl (char **pbuffer, struct fdb * tree, off_t *pos,int* len, off_t offset, int length); int br_tree_get_info(char *buffer, char **start, off_t offset, int length, int dummy); void br_avl_delete_by_port(int port); +int br_call_bridge(struct sk_buff *skb, unsigned short type); +void br_spacedevice_register(void); + /* externs */ extern struct br_stat br_stats; extern Port_data port_info[]; +#endif + + + diff --git a/include/net/tcp.h b/include/net/tcp.h index 8b050c63a8a3..0e7e4bb6e37d 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -24,22 +24,18 @@ #include /* This is for all connections with a full identity, no wildcards. - * New scheme, half the table is for TIME_WAIT, the other half is - * for the rest. I'll experiment with dynamic table growth later. + * Half of the table is for TIME_WAIT, the other half is for the + * rest. + * + * This needs to be shared by v4 and v6 because the lookup and hashing + * code needs to work with different AF's yet the port space is + * shared. */ -#define TCP_HTABLE_SIZE 512 +extern unsigned int tcp_ehash_size; +extern struct sock **tcp_ehash; /* This is for listening sockets, thus all sockets which possess wildcards. */ #define TCP_LHTABLE_SIZE 32 /* Yes, really, this is all you need. */ - -/* This is for all sockets, to keep track of the local port allocations. */ -#define TCP_BHTABLE_SIZE 512 - -/* tcp_ipv4.c: These need to be shared by v4 and v6 because the lookup - * and hashing code needs to work with different AF's yet - * the port space is shared. - */ -extern struct sock *tcp_established_hash[TCP_HTABLE_SIZE]; extern struct sock *tcp_listening_hash[TCP_LHTABLE_SIZE]; /* There are a few simple rules, which allow for local port reuse by @@ -81,7 +77,8 @@ struct tcp_bind_bucket { struct tcp_bind_bucket **pprev; }; -extern struct tcp_bind_bucket *tcp_bound_hash[TCP_BHTABLE_SIZE]; +extern unsigned int tcp_bhash_size; +extern struct tcp_bind_bucket **tcp_bhash; extern kmem_cache_t *tcp_bucket_cachep; extern struct tcp_bind_bucket *tcp_bucket_create(unsigned short snum); extern void tcp_bucket_unlock(struct sock *sk); @@ -109,7 +106,7 @@ static __inline__ void tcp_reg_zap(struct sock *sk) /* These are AF independent. */ static __inline__ int tcp_bhashfn(__u16 lport) { - return (lport & (TCP_BHTABLE_SIZE - 1)); + return (lport & (tcp_bhash_size - 1)); } /* This is a TIME_WAIT bucket. It works around the memory consumption diff --git a/init/main.c b/init/main.c index ed3643f87d9e..cb9861ebf83f 100644 --- a/init/main.c +++ b/init/main.c @@ -1211,6 +1211,7 @@ asmlinkage void __init start_kernel(void) dcache_init(); vma_init(); buffer_init(memory_end-memory_start); + page_cache_init(memory_end-memory_start); signals_init(); inode_init(); file_table_init(); diff --git a/ipc/sem.c b/ipc/sem.c index 5c67a540952f..c617c0d8431e 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -694,7 +694,7 @@ asmlinkage int sys_semop (int semid, struct sembuf *tsops, unsigned nsops) interruptible_sleep_on(&queue.sleeper); /* - * If queue.status == 1 we where woken up and + * If queue.status == 1 we were woken up and * have to retry else we simply return. * If an interrupt occurred we have to clean up the * queue diff --git a/kernel/ksyms.c b/kernel/ksyms.c index 14a1cbaba1fa..f08a62ea87e7 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -54,6 +54,9 @@ extern int blkdev_release(struct inode * inode); #if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE) extern int (*do_nfsservctl)(int, void *, void *); #endif +#if !defined(CONFIG_LOCKD) && defined(CONFIG_LOCKD_MODULE) +extern int (*do_lockdctl)(int, void *, void *); +#endif extern void *sys_call_table; @@ -121,6 +124,7 @@ EXPORT_SYMBOL(__fput); EXPORT_SYMBOL(igrab); EXPORT_SYMBOL(iunique); EXPORT_SYMBOL(iget); +EXPORT_SYMBOL(iget_in_use); EXPORT_SYMBOL(iput); EXPORT_SYMBOL(__namei); EXPORT_SYMBOL(lookup_dentry); @@ -193,6 +197,9 @@ EXPORT_SYMBOL(ROOT_DEV); #if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE) EXPORT_SYMBOL(do_nfsservctl); #endif +#if !defined(CONFIG_LOCKD) && defined(CONFIG_LOCKD_MODULE) +EXPORT_SYMBOL(do_lockdctl); +#endif /* device registration */ EXPORT_SYMBOL(register_chrdev); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index a474f38ac436..882e61311c42 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -169,8 +169,10 @@ static ctl_table kern_table[] = { 0644, NULL, &proc_doutsstring, &sysctl_string}, {KERN_PANIC, "panic", &panic_timeout, sizeof(int), 0644, NULL, &proc_dointvec}, +#ifdef CONFIG_PROC_FS {KERN_CAP_BSET, "cap-bound", &cap_bset, sizeof(kernel_cap_t), 0600, NULL, &proc_dointvec_bset}, +#endif #ifdef CONFIG_BLK_DEV_INITRD {KERN_REALROOTDEV, "real-root-dev", &real_root_dev, sizeof(int), 0644, NULL, &proc_dointvec}, diff --git a/mm/filemap.c b/mm/filemap.c index 9d9b1f271d2a..68e3f8c2b67d 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -32,7 +33,8 @@ */ unsigned long page_cache_size = 0; -struct page * page_hash_table[PAGE_HASH_SIZE]; +unsigned int page_hash_bits, page_hash_mask; +struct page **page_hash_table; /* * Define a request structure for outstanding page write requests @@ -1757,3 +1759,34 @@ int kpiod(void * unused) } } } + +void __init page_cache_init(unsigned long memory_size) +{ + unsigned long htable_size; + long order; + + htable_size = memory_size >> PAGE_SHIFT; + htable_size *= sizeof(struct page *); + for(order = 0; (PAGE_SIZE << order) < htable_size; order++) + ; + + do { + unsigned long tmp = (PAGE_SIZE << order) / sizeof(struct page *); + + page_hash_mask = (tmp - 1UL); + + page_hash_bits = 0; + while((tmp >>= 1UL) != 0UL) + page_hash_bits++; + + page_hash_table = (struct page **) + __get_free_pages(GFP_ATOMIC, order); + } while(page_hash_table == NULL && --order >= 0L); + + printk("Page-cache hash table entries: %d (order: %ld, %ld bytes)\n", + (1 << page_hash_bits), order, (PAGE_SIZE << order)); + if (!page_hash_table) + panic("Failed to allocate page hash table\n"); + memset(page_hash_table, 0, + (PAGE_HASH_MASK + 1UL) * sizeof(struct page *)); +} diff --git a/net/802/fc.c b/net/802/fc.c index eb0a0203f4d3..0afca8c0fb75 100644 --- a/net/802/fc.c +++ b/net/802/fc.c @@ -90,7 +90,10 @@ int fc_rebuild_header(struct sk_buff *skb) printk("fc_rebuild_header: Don't know how to resolve type %04X addresses ?\n",(unsigned int)htons(fcllc->ethertype)); return 0; } - +#ifdef CONFIG_INET return arp_find(fch->daddr, skb); +#else + return 0; /* Cannot happen because of ETH_P_IP test */ +#endif } diff --git a/net/Config.in b/net/Config.in index ed8510209a73..a5c6446380b7 100644 --- a/net/Config.in +++ b/net/Config.in @@ -39,6 +39,9 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25 tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB bool 'Bridging (EXPERIMENTAL)' CONFIG_BRIDGE + if [ "$CONFIG_BRIDGE" != "n" ]; then + int ' Maximum number of bridged interfaces' CONFIG_BRIDGE_NUM_PORTS 8 + fi bool '802.2 LLC (EXPERIMENTAL)' CONFIG_LLC # if [ "$CONFIG_LLC" = "y" ]; then # bool 'Netbeui (EXPERIMENTAL)' CONFIG_NETBEUI diff --git a/net/bridge/br.c b/net/bridge/br.c index 50ed9e84916b..aa5e2e8dbd51 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -40,10 +40,19 @@ * so blame me first if its broken ;) * * Robert Pintarelli: fixed bug in bpdu time values + * + * Matthew Grant: start ports disabled. + * auto-promiscuous mode on port enable/disable + * fleshed out interface event handling, interfaces + * now register with bridge on module load as well as ifup + * port control ioctls with ifindex support + * brg0 logical ethernet interface + * reworked brcfg to take interface arguments + * added support for changing the hardware address + * generally made bridge a lot more usable. * * Todo: - * Don't bring up devices automatically. Start ports disabled - * and use a netlink notifier so a daemon can maintain the bridge + * Use a netlink notifier so a daemon can maintain the bridge * port group (could we also do multiple groups ????). * A nice /proc file interface. * Put the path costs in the port info and devices. @@ -53,6 +62,7 @@ */ #include +#include #include #include #include @@ -61,10 +71,13 @@ #include #include #include +#include #include #include #include #include +#include +#include #include #include #include @@ -72,6 +85,7 @@ #include #include #include +#include #include #include @@ -145,11 +159,20 @@ static void br_add_local_mac(unsigned char *mac); static int br_flood(struct sk_buff *skb, int port); static int br_drop(struct sk_buff *skb); static int br_learn(struct sk_buff *skb, int port); /* 3.8 */ +static int br_protocol_ok(unsigned short protocol); +static int br_find_port(int ifindex); +static void br_get_ifnames(void); +static int brg_rx(struct sk_buff *skb, int port); static unsigned char bridge_ula[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; static Bridge_data bridge_info; /* (4.5.3) */ Port_data port_info[All_ports]; /* (4.5.5) */ +/* MAG: Maximum port registered - used to speed up flooding and to make + * have a large ports array more efficient + */ +static int max_port_used = 0; + /* JRP: fdb cache 1/port save kmalloc/kfree on every frame */ struct fdb *newfdb[All_ports]; int allocated_fdb_cnt = 0; @@ -190,6 +213,30 @@ static struct notifier_block br_dev_notifier={ 0 }; + +/* + * the following data is for the bridge network device + */ +struct brg_if { + struct device dev; + char name[IFNAMSIZ]; +}; +static struct brg_if brg_if; + +/* + * Here to save linkage? problems + */ + +static inline int find_port(struct device *dev) +{ + int i; + + for (i = One; i <= No_of_ports; i++) + if (port_info[i].dev == dev) + return(i); + return(0); +} + /* * Implementation of Protocol specific bridging * @@ -202,7 +249,7 @@ static struct notifier_block br_dev_notifier={ /* Checks if that protocol type is to be bridged */ -int br_protocol_ok(unsigned short protocol) +static int inline br_protocol_ok(unsigned short protocol) { unsigned x; @@ -843,8 +890,14 @@ __initfunc(void br_init(void)) { /* (4.8.1) */ int port_no; - printk(KERN_INFO "NET4: Ethernet Bridge 005 for NET4.0\n"); + printk(KERN_INFO "NET4: Ethernet Bridge 007 for NET4.0\n"); + /* Set up brg device information */ + bridge_info.instance = 0; + brg_init(); + + max_port_used = 0; + /* * Form initial topology change time. * The topology change timer is only used if this is the root bridge. @@ -874,8 +927,8 @@ __initfunc(void br_init(void)) stop_topology_change_timer(); memset(newfdb, 0, sizeof(newfdb)); for (port_no = One; port_no <= No_of_ports; port_no++) { /* (4.8.1.4) */ - /* initial state = Enable */ - user_port_state[port_no] = ~Disabled; + /* initial state = Disable */ + user_port_state[port_no] = Disabled; port_priority[port_no] = 128; br_init_port(port_no); disable_port(port_no); @@ -1306,6 +1359,9 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v if (dev->flags & IFF_LOOPBACK) return(NOTIFY_DONE); + if (dev == &brg_if.dev) + return(NOTIFY_DONE); /* Don't attach the brg device to a port! */ + switch (event) { case NETDEV_DOWN: @@ -1335,6 +1391,9 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v { port_info[i].dev = dev; port_info[i].port_id = i; + dev->bridge_port_id = i; + if( i > max_port_used ) + max_port_used = i; /* set bridge addr from 1st device addr */ if (((htonl(bridge_info.bridge_id.BRIDGE_ID[0])&0xffff) == 0) && (bridge_info.bridge_id.BRIDGE_ID[1] == 0)) @@ -1344,7 +1403,10 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v bridge_info.bridge_id.BRIDGE_PRIORITY = htons(32768); set_bridge_priority(&bridge_info.bridge_id); } + /* Add local MAC address */ br_add_local_mac(dev->dev_addr); + /* Save MAC address for latter change address events */ + memcpy(port_info[i].ifmac.BRIDGE_ID_ULA, dev->dev_addr, 6); if((br_stats.flags & BR_UP) && (user_port_state[i] != Disabled)) { @@ -1362,19 +1424,122 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v } } break; + case NETDEV_REGISTER: + if (br_stats.flags & BR_DEBUG) + printk(KERN_DEBUG "br_device_event: NETDEV_REGISTER...\n"); + /* printk(KERN_ERR "br_device_event: NETDEV_REGISTER...\n"); */ + /* printk(KERN_ERR "br_device_event: dev->type: 0x%X\n", dev->type); */ + /* Only handle ethernet ports */ + if(dev->type!=ARPHRD_ETHER && dev->type!=ARPHRD_LOOPBACK) + return NOTIFY_DONE; + /* printk(KERN_ERR "br_device_event: Looking for port...\n"); */ + for (i = One; i <= No_of_ports; i++) + { + if (port_info[i].dev == NULL || port_info[i].dev == dev) + { + /* printk(KERN_ERR "br_device_event: Found port %d\n", i); */ + port_info[i].dev = dev; + port_info[i].port_id = i; + dev->bridge_port_id = i; + if( i > max_port_used ) + max_port_used = i; + /* handle local MAC address minuplations */ + br_add_local_mac(dev->dev_addr); + memcpy(port_info[i].ifmac.BRIDGE_ID_ULA, dev->dev_addr, 6); + return NOTIFY_DONE; + break; + } + } + break; case NETDEV_UNREGISTER: if (br_stats.flags & BR_DEBUG) printk(KERN_DEBUG "br_device_event: NETDEV_UNREGISTER...\n"); i = find_port(dev); if (i > 0) { br_avl_delete_by_port(i); + memset(port_info[i].ifmac.BRIDGE_ID_ULA, 0, 6); port_info[i].dev = NULL; } break; + case NETDEV_CHANGEADDR: + if (br_stats.flags & BR_DEBUG) + printk(KERN_DEBUG "br_device_event: NETDEV_CHANGEADDR...\n"); + i = find_port(dev); + if (i <= 0) + break; + if (memcmp(port_info[i].ifmac.BRIDGE_ID_ULA, dev->dev_addr, 6) != 0) + break; /* Don't worry about a change of hardware broadcast address! */ + if (dev->start) { + printk(KERN_CRIT "br_device_event: NETDEV_CHANGEADDR on busy device %s - FIX DRIVER!\n", + dev->name); + /* return NOTIFY_BAD; It SHOULD be this, but I want to be friendly... */ + return NOTIFY_DONE; + } + br_avl_delete_by_port(i); + memset(port_info[i].ifmac.BRIDGE_ID_ULA, 0, 6); + break; } return NOTIFY_DONE; } +/* Routine to loop over device list and register + * interfaces to bridge. Called from last part of net_dev_init just before + * bootp/rarp interface setup + */ +void br_spacedevice_register(void) +{ + struct device *dev; + for( dev = dev_base; dev != NULL; dev = dev->next) + { + br_device_event(NULL, NETDEV_REGISTER, dev); + } +} + + +/* This is for SPEED in the kernel in net_bh.c */ + +int br_call_bridge(struct sk_buff *skb, unsigned short type) +{ + int port; + struct device *dev; + +#if 0 /* Checked first in handle_bridge to save expense of function call */ + if(!(br_stats.flags & BR_UP)) + return 0; +#endif + + dev = skb->dev; + + /* Check for brg0 device + */ + if (dev == &brg_if.dev) + return 0; + + port = dev->bridge_port_id; + + if(!port) + return 0; + + /* Sanity - make sure we are not leaping off into fairy space! */ + if ( port < 0 || port > max_port_used || port_info[port].dev != dev) { + if (net_ratelimit()) + printk(KERN_CRIT "br_call_bridge: device %s has invalid port ID %d!\n", + dev->name, + dev->bridge_port_id); + return 0; + } + + if(user_port_state[port] == Disabled) + return 0; + + if (!br_protocol_ok(ntohs(type))) + return 0; + + return 1; + +} + + /* * following routine is called when a frame is received * from an interface, it returns 1 when it consumes the @@ -1383,9 +1548,10 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v int br_receive_frame(struct sk_buff *skb) /* 3.5 */ { - int port; + int port, ret; Port_data *p; struct ethhdr *eth; + struct device *dev; /* sanity */ if (!skb) { @@ -1393,17 +1559,32 @@ int br_receive_frame(struct sk_buff *skb) /* 3.5 */ return(1); } + dev = skb->dev; + + /* Check for brg0 device + */ + if (dev == &brg_if.dev) + return 0; + skb->pkt_bridged = IS_BRIDGED; /* check for loopback */ - if (skb->dev->flags & IFF_LOOPBACK) + if (dev->flags & IFF_LOOPBACK) return 0 ; - port = find_port(skb->dev); +#if 0 + port = find_port(dev); +#else + port = dev->bridge_port_id; +#endif if(!port) return 0; + /* Hand off to brg_rx BEFORE we screw up the skb */ + if(brg_rx(skb, port)) + return(1); + skb->nh.raw = skb->mac.raw; eth = skb->mac.ethernet; p = &port_info[port]; @@ -1435,9 +1616,9 @@ int br_receive_frame(struct sk_buff *skb) /* 3.5 */ switch (p->state) { case Learning: - if(br_learn(skb, port)) + if((ret = br_learn(skb, port))) { /* 3.8 */ - ++br_stats_cnt.drop_multicast; + if (ret > 0) ++br_stats_cnt.drop_multicast; return br_drop(skb); } /* fall through */ @@ -1452,8 +1633,8 @@ int br_receive_frame(struct sk_buff *skb) /* 3.5 */ */ break; case Forwarding: - if(br_learn(skb, port)) { /* 3.8 */ - ++br_stats_cnt.drop_multicast; + if((ret = br_learn(skb, port))) { /* 3.8 */ + if (ret > 0) ++br_stats_cnt.drop_multicast; return br_drop(skb); } /* Now this frame came from one of bridged @@ -1577,7 +1758,11 @@ static inline int mcast_quench(struct fdb *f) /* * this routine returns 0 when it learns (or updates) from the - * frame, and 1 if we must dropped the frame. + * frame, and 1 if we must dropped the frame due to multicast + * limitations, or -1 because of not enough memory. + * + * NB Can be called when skb->nh.raw is NOT set up when + * receiving frames on brg0 via brg_rx */ static int br_learn(struct sk_buff *skb, int port) /* 3.8 */ @@ -1600,8 +1785,8 @@ static int br_learn(struct sk_buff *skb, int port) /* 3.8 */ newfdb[port] = f = (struct fdb *)kmalloc(sizeof(struct fdb), GFP_ATOMIC); if (!f) { - printk(KERN_DEBUG "br_learn: unable to malloc fdb\n"); - return(-1); /* this drop the frame */ + printk(KERN_WARNING "br_learn: unable to malloc fdb\n"); + return(-1); /* this drops the frame */ } } f->port = port; /* source port */ @@ -1819,6 +2004,9 @@ static int br_flood(struct sk_buff *skb, int port) { if (i == port) /* don't send back where we got it */ continue; + if (i > max_port_used) + /* Don't go scanning empty port entries */ + break; if (port_info[i].state == Forwarding) { nskb = skb_clone(skb, GFP_ATOMIC); @@ -1840,16 +2028,6 @@ static int br_flood(struct sk_buff *skb, int port) return(0); } -static int find_port(struct device *dev) -{ - int i; - - for (i = One; i <= No_of_ports; i++) - if (port_info[i].dev == dev) - return(i); - return(0); -} - /* * FIXME: This needs to come from the device structs, eg for * 10,100,1Gbit ethernet. @@ -1956,17 +2134,62 @@ struct fdb_info *get_fdb_info(int user_buf_size, int *copied,int *notcopied) return fdbis; } + +/* Fill in interface names in port_info structure + */ +static void br_get_ifdata(void) { + int i; + + for(i=One;i<=No_of_ports; i++) { + + port_info[i].admin_state = user_port_state[i]; + + /* memset IS needed. Kernel strncpy does NOT NULL terminate + * strings when limit reached + */ + memset(port_info[i].ifname, 0, IFNAMSIZ); + if( port_info[i].dev == 0 ) + continue; + strncpy(port_info[i].ifname, port_info[i].dev->name, IFNAMSIZ-1); + /* Being paranoid */ + port_info[i].ifname[IFNAMSIZ-1] = '\0'; + } +} + +/* Given an interface index, loop over port array to see if configured. If + so, return port number, otherwise error */ +static int br_find_port(int ifindex) +{ + int i; + + for(i=1; i <= No_of_ports; i++) { + if (port_info[i].dev == 0) + continue; + if (port_info[i].dev->ifindex == ifindex) + return(i); + } + + return -EUNATCH; /* Tell me if this is incorrect error code for this case */ +} + + int br_ioctl(unsigned int cmd, void *arg) { - int err, i; + int err, i, ifflags; struct br_cf bcf; bridge_id_t new_id; - + struct device *dev; + switch(cmd) { case SIOCGIFBR: /* get bridging control blocks */ memcpy(&br_stats.bridge_data, &bridge_info, sizeof(Bridge_data)); - memcpy(&br_stats.port_data, &port_info, sizeof(Port_data)*No_of_ports); + + /* Fill in interface names in port_info*/ + br_get_ifdata(); + + br_stats.num_ports = No_of_ports; + memcpy(&br_stats.port_data, &port_info, sizeof(Port_data)*All_ports); err = copy_to_user(arg, &br_stats, sizeof(struct br_stat)); if (err) @@ -2032,16 +2255,28 @@ int br_ioctl(unsigned int cmd, void *arg) } br_stats.flags ^= BR_STP_DISABLED; break; + case BRCMD_IF_ENABLE: + bcf.arg1 = br_find_port(bcf.arg1); + if (bcf.arg1 < 0) + return(bcf.arg1); case BRCMD_PORT_ENABLE: if (port_info[bcf.arg1].dev == 0) return(-EINVAL); if (user_port_state[bcf.arg1] != Disabled) return(-EALREADY); printk(KERN_DEBUG "br: enabling port %i\n",bcf.arg1); + dev = port_info[bcf.arg1].dev; + ifflags = (dev->flags&~(IFF_PROMISC|IFF_ALLMULTI)) + |(dev->gflags&(IFF_PROMISC|IFF_ALLMULTI)); + dev_change_flags(dev, ifflags|IFF_PROMISC); user_port_state[bcf.arg1] = ~Disabled; if(br_stats.flags & BR_UP) enable_port(bcf.arg1); break; + case BRCMD_IF_DISABLE: + bcf.arg1 = br_find_port(bcf.arg1); + if (bcf.arg1 < 0) + return(bcf.arg1); case BRCMD_PORT_DISABLE: if (port_info[bcf.arg1].dev == 0) return(-EINVAL); @@ -2051,12 +2286,20 @@ int br_ioctl(unsigned int cmd, void *arg) user_port_state[bcf.arg1] = Disabled; if(br_stats.flags & BR_UP) disable_port(bcf.arg1); + dev = port_info[bcf.arg1].dev; + ifflags = (dev->flags&~(IFF_PROMISC|IFF_ALLMULTI)) + |(dev->gflags&(IFF_PROMISC|IFF_ALLMULTI)); + dev_change_flags(port_info[bcf.arg1].dev, ifflags & ~IFF_PROMISC); break; case BRCMD_SET_BRIDGE_PRIORITY: new_id = bridge_info.bridge_id; new_id.BRIDGE_PRIORITY = htons(bcf.arg1); set_bridge_priority(&new_id); break; + case BRCMD_SET_IF_PRIORITY: + bcf.arg1 = br_find_port(bcf.arg1); + if (bcf.arg1 < 0) + return(bcf.arg1); case BRCMD_SET_PORT_PRIORITY: if((port_info[bcf.arg1].dev == 0) || (bcf.arg2 & ~0xff)) @@ -2064,6 +2307,10 @@ int br_ioctl(unsigned int cmd, void *arg) port_priority[bcf.arg1] = bcf.arg2; set_port_priority(bcf.arg1); break; + case BRCMD_SET_IF_PATH_COST: + bcf.arg1 = br_find_port(bcf.arg1); + if (bcf.arg1 < 0) + return(bcf.arg1); case BRCMD_SET_PATH_COST: if (port_info[bcf.arg1].dev == 0) return(-EINVAL); @@ -2145,3 +2392,385 @@ static int br_cmp(unsigned int *a, unsigned int *b) } return(0); } + + + + +/* -------------------------------------------------------------------------------- + * + * + * Bridge network device here for future modularization - device structures + * must be 'static' inside bridge instance + * Modelled after sch_teql.c + * + */ + + + +/* + * Index to functions. + */ + +int brg_probe(struct device *dev); +static int brg_open(struct device *dev); +static int brg_start_xmit(struct sk_buff *skb, struct device *dev); +static int brg_close(struct device *dev); +static struct net_device_stats *brg_get_stats(struct device *dev); +static void brg_set_multicast_list(struct device *dev); + +/* + * Board-specific info in dev->priv. + */ + +struct net_local +{ + __u32 groups; + struct net_device_stats stats; +}; + + + + +/* + * To call this a probe is a bit misleading, however for real + * hardware it would have to check what was present. + */ + +__initfunc(int brg_probe(struct device *dev)) +{ + unsigned int bogomips; + struct timeval utime; + + printk(KERN_INFO "%s: network interface for Ethernet Bridge 007/NET4.0\n", dev->name); + + /* + * Initialize the device structure. + */ + + dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); + if (dev->priv == NULL) + return -ENOMEM; + memset(dev->priv, 0, sizeof(struct net_local)); + + /* Set up MAC address based on BogoMIPs figure for first CPU and time + */ + bogomips = (boot_cpu_data.loops_per_sec+2500)/500000 ; + get_fast_time(&utime); + + /* Ummmm.... YES! */ + dev->dev_addr[0] = '\xFE'; + dev->dev_addr[1] = '\xFD'; + dev->dev_addr[2] = (bridge_info.instance & 0x0F) << 4; + dev->dev_addr[2] |= ((utime.tv_sec & 0x000F0000) >> 16); + dev->dev_addr[3] = bogomips & 0xFF; + dev->dev_addr[4] = (utime.tv_sec & 0x0000FF00) >> 8; + dev->dev_addr[5] = (utime.tv_sec & 0x000000FF); + + printk(KERN_INFO "%s: generated MAC address %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n", + dev->name, + dev->dev_addr[0], + dev->dev_addr[1], + dev->dev_addr[2], + dev->dev_addr[3], + dev->dev_addr[4], + dev->dev_addr[5]); + + + printk(KERN_INFO "%s: attached to bridge instance %lu\n", dev->name, dev->base_addr); + + /* + * The brg specific entries in the device structure. + */ + + dev->open = brg_open; + dev->hard_start_xmit = brg_start_xmit; + dev->stop = brg_close; + dev->get_stats = brg_get_stats; + dev->set_multicast_list = brg_set_multicast_list; + + /* + * Setup the generic properties + */ + + ether_setup(dev); + + dev->tx_queue_len = 0; + + return 0; +} + +/* + * Open/initialize the board. + */ + +static int brg_open(struct device *dev) +{ + if (br_stats.flags & BR_DEBUG) + printk(KERN_DEBUG "%s: Doing brg_open()...", dev->name); + + if (memcmp(dev->dev_addr, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0) + return -EFAULT; + + dev->start = 1; + dev->tbusy = 0; + return 0; +} + +static unsigned brg_mc_hash(__u8 *dest) +{ + unsigned idx = 0; + idx ^= dest[0]; + idx ^= dest[1]; + idx ^= dest[2]; + idx ^= dest[3]; + idx ^= dest[4]; + idx ^= dest[5]; + return 1U << (idx&0x1F); +} + +static void brg_set_multicast_list(struct device *dev) +{ + unsigned groups = ~0; + struct net_local *lp = (struct net_local *)dev->priv; + + if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI))) { + struct dev_mc_list *dmi; + + groups = brg_mc_hash(dev->broadcast); + + for (dmi=dev->mc_list; dmi; dmi=dmi->next) { + if (dmi->dmi_addrlen != 6) + continue; + groups |= brg_mc_hash(dmi->dmi_addr); + } + } + lp->groups = groups; +} + +/* + * We transmit by throwing the packet at the bridge. + */ + +static int brg_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + struct ethhdr *eth = (struct ethhdr*)skb->data; + int port; + + /* Deal with the bridge being disabled */ + if(!(br_stats.flags & BR_UP)) { + /* Either this */ + /* lp->stats.tx_errors++; */ /* this condition is NOT an error */ + /* or this (implied by RFC 2233) */ + lp->stats.tx_dropped++; + dev_kfree_skb(skb); + return 0; + } + + lp->stats.tx_bytes+=skb->len; + lp->stats.tx_packets++; + +#if 0 + ++br_stats_cnt.port_not_disable; +#endif + skb->mac.raw = skb->nh.raw = skb->data; + eth = skb->mac.ethernet; + port = 0; /* an impossible port (locally generated) */ + + if (br_stats.flags & BR_DEBUG) + printk("%s: brg_start_xmit - src %02x:%02x:%02x:%02x:%02x:%02x" + " dest %02x:%02x:%02x:%02x:%02x:%02x\n", + dev->name, + eth->h_source[0], + eth->h_source[1], + eth->h_source[2], + eth->h_source[3], + eth->h_source[4], + eth->h_source[5], + eth->h_dest[0], + eth->h_dest[1], + eth->h_dest[2], + eth->h_dest[3], + eth->h_dest[4], + eth->h_dest[5]); + + /* Forward the packet ! */ + if(br_forward(skb, port)) + return(0); + + /* Throw packet initially */ + dev_kfree_skb(skb); + return 0; +} + + +/* + * The typical workload of the driver: + * Handle the ether interface interrupts. + * + * (In this case handle the packets posted from the bridge) + */ + +static int brg_rx(struct sk_buff *skb, int port) +{ + struct device *dev = &brg_if.dev; + struct net_local *lp = (struct net_local *)dev->priv; + struct ethhdr *eth = (struct ethhdr*)(skb->data); + int len = skb->len; + int clone = 0; + + if (br_stats.flags & BR_DEBUG) + printk(KERN_DEBUG "%s: brg_rx()\n", dev->name); + + /* Get out of here if the bridge interface is not up + */ + if(!(dev->flags & IFF_UP)) + return(0); + + /* Check that the port that this thing came off is in the forwarding state + * We sould only listen to the same address scope we will transmit to. + */ + if(port_info[port].state != Forwarding) + return(0); + + /* Is this for us? - broadcast/mulitcast/promiscuous packets need cloning, + * with uni-cast we eat the packet + */ + clone = 0; + if (dev->flags & IFF_PROMISC) { + clone = 1; + } + else if (eth->h_dest[0]&1) { + if (!(dev->flags&(IFF_ALLMULTI)) + && !(brg_mc_hash(eth->h_dest)&lp->groups)) + return(0); + clone = 1; + } + else if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN) != 0) { + return(0); + } + + /* Clone things here - we want to be transparent before we check packet data + * integrity + */ + if(clone) { + struct sk_buff *skb2 = skb; + skb = skb_clone(skb2, GFP_KERNEL); + if (skb == NULL) { + return(0); + } + + } + else { + /* Learn source addresses for unicast non-promiscuous + * frames for brg0 + */ + if(br_learn(skb, port)) { + return br_drop(skb); + } + } + + /* Check packet length + */ + if (len < 16) { + printk(KERN_DEBUG "%s : rx len = %d\n", dev->name, len); + kfree_skb(skb); + return(!clone); + } + + if (br_stats.flags & BR_DEBUG) + printk("%s: brg_rx - src %02x:%02x:%02x:%02x:%02x:%02x" + " dest %02x:%02x:%02x:%02x:%02x:%02x\n", + dev->name, + eth->h_source[0], + eth->h_source[1], + eth->h_source[2], + eth->h_source[3], + eth->h_source[4], + eth->h_source[5], + eth->h_dest[0], + eth->h_dest[1], + eth->h_dest[2], + eth->h_dest[3], + eth->h_dest[4], + eth->h_dest[5]); + + /* Do it */ + skb->pkt_type = PACKET_HOST; + skb->dev = dev; + skb->protocol=eth_type_trans(skb,dev); + memset(skb->cb, 0, sizeof(skb->cb)); + lp->stats.rx_packets++; + lp->stats.rx_bytes+=len; + netif_rx(skb); + return(!clone); +} + +static int brg_close(struct device *dev) +{ + if (br_stats.flags & BR_DEBUG) + printk(KERN_DEBUG "%s: Shutting down.\n", dev->name); + + dev->tbusy = 1; + dev->start = 0; + + return 0; +} + +static struct net_device_stats *brg_get_stats(struct device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + return &lp->stats; +} + +/* + * + */ + +__initfunc(int brg_init(void)) +{ + int err; + + memset(&brg_if, 0, sizeof(brg_if)); + + rtnl_lock(); + + brg_if.dev.base_addr = bridge_info.instance; + sprintf (brg_if.name, "brg%d", bridge_info.instance); + brg_if.dev.name = (void*)&brg_if.name; + if(dev_get(brg_if.name)) { + printk(KERN_INFO "%s already loaded.\n", brg_if.name); + return -EBUSY; + } + brg_if.dev.init = brg_probe; + + err = register_netdevice(&brg_if.dev); + rtnl_unlock(); + return err; +} + + +#if 0 /* Its here if we ever need it... */ +#ifdef MODULE + +void cleanup_module(void) +{ + + /* + * Unregister the device + */ + rtnl_lock(); + unregister_netdevice(&the_master.dev); + rtnl_unlock(); + + /* + * Free up the private structure. + */ + + kfree(brg_if.dev.priv); + brg_if.dev.priv = NULL; /* gets re-allocated by brg_probe */ +} + +#endif /* MODULE */ + +#endif diff --git a/net/bridge/br_tree.c b/net/bridge/br_tree.c index c1ed82f10875..67efa8f1ba2b 100644 --- a/net/bridge/br_tree.c +++ b/net/bridge/br_tree.c @@ -492,7 +492,8 @@ void br_avl_delete_by_port(int port) port_info[port].fdb = NULL; /* remove the local mac too */ - next = br_avl_find_addr(port_info[port].dev->dev_addr); +/* next = br_avl_find_addr(port_info[port].dev->dev_addr); */ + next = br_avl_find_addr(port_info[port].ifmac.BRIDGE_ID_ULA); if (next != NULL) br_avl_remove(next); diff --git a/net/core/dev.c b/net/core/dev.c index 75fae54a8fe9..963304594845 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -796,7 +796,11 @@ void netif_rx(struct sk_buff *skb) #ifdef CONFIG_BRIDGE static inline void handle_bridge(struct sk_buff *skb, unsigned short type) { - if (br_stats.flags & BR_UP && br_protocol_ok(ntohs(type))) + /* + * The br_stats.flags is checked here to save the expense of a + * function call. + */ + if ((br_stats.flags & BR_UP) && br_call_bridge(skb, type)) { /* * We pass the bridge a complete frame. This means @@ -820,7 +824,6 @@ static inline void handle_bridge(struct sk_buff *skb, unsigned short type) } #endif - /* * When we are called the queue is ready to grab, the interrupts are * on and hardware can interrupt and queue to the receive queue as we @@ -2018,6 +2021,13 @@ __initfunc(int net_dev_init(void)) dev_mcast_init(); +#ifdef CONFIG_BRIDGE + /* + * Register any statically linked ethernet devices with the bridge + */ + br_spacedevice_register(); +#endif + #ifdef CONFIG_IP_PNP ip_auto_config(); #endif diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index 08ebbc2f1e76..3aa790da4b2e 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -176,7 +176,7 @@ int ip_forward(struct sk_buff *skb) (icmph->type==ICMP_TIME_EXCEEDED)) { #endif - maddr = inet_select_addr(dev2, rt->rt_gateway, RT_SCOPE_UNIVERSE); + maddr = rt->rt_src; fw_res = ip_fw_masq_icmp(&skb, maddr); if (fw_res < 0) { kfree_skb(skb); @@ -226,7 +226,7 @@ skip_call_fw_firewall: if (maddr == 0) #endif - maddr = inet_select_addr(dev2, rt->rt_gateway, RT_SCOPE_UNIVERSE); + maddr = rt->rt_src; if (ip_fw_masquerade(&skb, maddr) < 0) { kfree_skb(skb); diff --git a/net/ipv4/ip_fw.c b/net/ipv4/ip_fw.c index 6319b89e73ba..4edd083c3d78 100644 --- a/net/ipv4/ip_fw.c +++ b/net/ipv4/ip_fw.c @@ -488,7 +488,10 @@ static int find_special(ip_chainlabel label, int *answer) static int enabled = 0; if(!enabled) + { + enabled=1; sysctl_ip_always_defrag++; + } *answer = FW_REDIRECT; return 1; #endif diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 657632150c96..dd7615e5a013 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1798,6 +1798,8 @@ extern void __skb_cb_too_small_for_tcp(int, int); void __init tcp_init(void) { struct sk_buff *skb = NULL; + unsigned long goal; + int order; if(sizeof(struct tcp_skb_cb) > sizeof(skb->cb)) __skb_cb_too_small_for_tcp(sizeof(struct tcp_skb_cb), @@ -1823,4 +1825,43 @@ void __init tcp_init(void) NULL, NULL); if(!tcp_timewait_cachep) panic("tcp_init: Cannot alloc tcp_tw_bucket cache."); + + /* Size and allocate TCP hash tables. */ + goal = num_physpages >> (20 - PAGE_SHIFT); + for (order = 0; (1UL << order) < goal; order++) + ; + do { + tcp_ehash_size = (1UL << order) * PAGE_SIZE / + sizeof(struct sock *); + tcp_ehash = (struct sock **) + __get_free_pages(GFP_ATOMIC, order); + } while (tcp_ehash == NULL && --order >= 0); + + if (!tcp_ehash) + panic("Failed to allocate TCP established hash table\n"); + memset(tcp_ehash, 0, tcp_ehash_size * sizeof(struct sock *)); + + goal = (((1UL << order) * PAGE_SIZE) / sizeof(struct tcp_bind_bucket *)); + if (goal > (64 * 1024)) { + /* Don't size the bind-hash larger than the port + * space, that is just silly. + */ + goal = (((64 * 1024) * sizeof(struct tcp_bind_bucket *)) / PAGE_SIZE); + for (order = 0; (1UL << order) < goal; order++) + ; + } + + do { + tcp_bhash_size = (1UL << order) * PAGE_SIZE / + sizeof(struct tcp_bind_bucket *); + tcp_bhash = (struct tcp_bind_bucket **) + __get_free_pages(GFP_ATOMIC, order); + } while (tcp_bhash == NULL && --order >= 0); + + if (!tcp_bhash) + panic("Failed to allocate TCP bind hash table\n"); + memset(tcp_bhash, 0, tcp_bhash_size * sizeof(struct tcp_bind_bucket *)); + + printk("TCP: Hash tables configured (ehash %d bhash %d)\n", + tcp_ehash_size, tcp_bhash_size); } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index a753b128243d..a58382ef57d2 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -1043,7 +1043,7 @@ static __inline__ void tcp_tw_hashdance(struct sock *sk, struct tcp_tw_bucket *t sk->prot->inuse--; /* Step 4: Hash TW into TIMEWAIT half of established hash table. */ - head = &tcp_established_hash[sk->hashent + (TCP_HTABLE_SIZE/2)]; + head = &tcp_ehash[sk->hashent + (tcp_ehash_size/2)]; sktw = (struct sock *)tw; if((sktw->next = *head) != NULL) (*head)->pprev = &sktw->next; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 904f8b5d3117..4436ac0d5add 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -90,12 +90,14 @@ void tcp_v4_send_check(struct sock *sk, struct tcphdr *th, int len, * First half of the table is for sockets not in TIME_WAIT, second half * is for TIME_WAIT sockets only. */ -struct sock *tcp_established_hash[TCP_HTABLE_SIZE]; +unsigned int tcp_ehash_size; +struct sock **tcp_ehash; /* Ok, let's try this, I give up, we do need a local binding * TCP hash as well as the others for fast bind/connect. */ -struct tcp_bind_bucket *tcp_bound_hash[TCP_BHTABLE_SIZE]; +unsigned int tcp_bhash_size; +struct tcp_bind_bucket **tcp_bhash; /* All sockets in TCP_LISTEN state will be in here. This is the only table * where wildcard'd TCP sockets can exist. Hash function here is just local @@ -117,7 +119,7 @@ int tcp_port_rover = (1024 - 1); static __inline__ int tcp_hashfn(__u32 laddr, __u16 lport, __u32 faddr, __u16 fport) { - return ((laddr ^ lport) ^ (faddr ^ fport)) & ((TCP_HTABLE_SIZE/2) - 1); + return ((laddr ^ lport) ^ (faddr ^ fport)) & ((tcp_ehash_size/2) - 1); } static __inline__ int tcp_sk_hashfn(struct sock *sk) @@ -140,7 +142,7 @@ struct tcp_bind_bucket *tcp_bucket_create(unsigned short snum) tb = kmem_cache_alloc(tcp_bucket_cachep, SLAB_ATOMIC); if(tb != NULL) { struct tcp_bind_bucket **head = - &tcp_bound_hash[tcp_bhashfn(snum)]; + &tcp_bhash[tcp_bhashfn(snum)]; tb->port = snum; tb->fastreuse = 0; tb->owners = NULL; @@ -162,7 +164,7 @@ static __inline__ int __tcp_bucket_check(unsigned short snum) { struct tcp_bind_bucket *tb; - tb = tcp_bound_hash[tcp_bhashfn(snum)]; + tb = tcp_bhash[tcp_bhashfn(snum)]; for( ; (tb && (tb->port != snum)); tb = tb->next) ; if (tb == NULL) { @@ -181,7 +183,7 @@ static __inline__ void __tcp_inherit_port(struct sock *sk, struct sock *child) #ifdef CONFIG_IP_TRANSPARENT_PROXY if (child->num != sk->num) { unsigned short snum = ntohs(child->num); - for(tb = tcp_bound_hash[tcp_bhashfn(snum)]; + for(tb = tcp_bhash[tcp_bhashfn(snum)]; tb && tb->port != snum; tb = tb->next) ; @@ -220,7 +222,7 @@ static int tcp_v4_get_port(struct sock *sk, unsigned short snum) do { rover++; if ((rover < low) || (rover > high)) rover = low; - tb = tcp_bound_hash[tcp_bhashfn(rover)]; + tb = tcp_bhash[tcp_bhashfn(rover)]; for ( ; tb; tb = tb->next) if (tb->port == rover) goto next; @@ -237,7 +239,7 @@ static int tcp_v4_get_port(struct sock *sk, unsigned short snum) snum = rover; tb = NULL; } else { - for (tb = tcp_bound_hash[tcp_bhashfn(snum)]; + for (tb = tcp_bhash[tcp_bhashfn(snum)]; tb != NULL; tb = tb->next) if (tb->port == snum) @@ -328,7 +330,7 @@ static __inline__ void __tcp_v4_hash(struct sock *sk) if(sk->state == TCP_LISTEN) skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)]; else - skp = &tcp_established_hash[(sk->hashent = tcp_sk_hashfn(sk))]; + skp = &tcp_ehash[(sk->hashent = tcp_sk_hashfn(sk))]; if((sk->next = *skp) != NULL) (*skp)->pprev = &sk->next; @@ -421,7 +423,7 @@ static inline struct sock *__tcp_v4_lookup(struct tcphdr *th, * have wildcards anyways. */ hash = tcp_hashfn(daddr, hnum, saddr, sport); - for(sk = tcp_established_hash[hash]; sk; sk = sk->next) { + for(sk = tcp_ehash[hash]; sk; sk = sk->next) { if(TCP_IPV4_MATCH(sk, acookie, saddr, daddr, ports, dif)) { if (sk->state == TCP_ESTABLISHED) TCP_RHASH(sport) = sk; @@ -429,7 +431,7 @@ static inline struct sock *__tcp_v4_lookup(struct tcphdr *th, } } /* Must check for a TIME_WAIT'er before going to listener hash. */ - for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next) + for(sk = tcp_ehash[hash+(tcp_ehash_size/2)]; sk; sk = sk->next) if(TCP_IPV4_MATCH(sk, acookie, saddr, daddr, ports, dif)) goto hit; sk = tcp_v4_lookup_listener(daddr, hnum, dif); @@ -469,7 +471,7 @@ static struct sock *tcp_v4_proxy_lookup(unsigned short num, unsigned long raddr, /* This code must run only from NET_BH. */ { - struct tcp_bind_bucket *tb = tcp_bound_hash[tcp_bhashfn(hnum)]; + struct tcp_bind_bucket *tb = tcp_bhash[tcp_bhashfn(hnum)]; for( ; (tb && tb->port != hnum); tb = tb->next) ; if(tb == NULL) @@ -510,7 +512,7 @@ pass2: } next: if(firstpass--) { - struct tcp_bind_bucket *tb = tcp_bound_hash[tcp_bhashfn(hpnum)]; + struct tcp_bind_bucket *tb = tcp_bhash[tcp_bhashfn(hpnum)]; for( ; (tb && tb->port != hpnum); tb = tb->next) ; if(tb) { @@ -547,7 +549,7 @@ static int tcp_v4_unique_address(struct sock *sk) /* Freeze the hash while we snoop around. */ SOCKHASH_LOCK(); - tb = tcp_bound_hash[tcp_bhashfn(snum)]; + tb = tcp_bhash[tcp_bhashfn(snum)]; for(; tb; tb = tb->next) { if(tb->port == snum && tb->owners != NULL) { /* Almost certainly the re-use port case, search the real hashes @@ -1793,7 +1795,7 @@ do_time_wait: static void __tcp_v4_rehash(struct sock *sk) { - struct sock **skp = &tcp_established_hash[(sk->hashent = tcp_sk_hashfn(sk))]; + struct sock **skp = &tcp_ehash[(sk->hashent = tcp_sk_hashfn(sk))]; SOCKHASH_LOCK(); if(sk->pprev) { diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 21029f8ecbd0..ccb52088609e 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -363,8 +363,8 @@ static void tcp_keepalive(unsigned long data) int count = 0; int i; - for(i = chain_start; i < (chain_start + ((TCP_HTABLE_SIZE/2) >> 2)); i++) { - struct sock *sk = tcp_established_hash[i]; + for(i = chain_start; i < (chain_start + ((tcp_ehash_size/2) >> 2)); i++) { + struct sock *sk = tcp_ehash[i]; while(sk) { if(!atomic_read(&sk->sock_readers) && sk->keepopen) { count += tcp_keepopen_proc(sk); @@ -375,8 +375,8 @@ static void tcp_keepalive(unsigned long data) } } out: - chain_start = ((chain_start + ((TCP_HTABLE_SIZE/2)>>2)) & - ((TCP_HTABLE_SIZE/2) - 1)); + chain_start = ((chain_start + ((tcp_ehash_size/2)>>2)) & + ((tcp_ehash_size/2) - 1)); } /* diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 1bd6181dec4d..54aef23dbe01 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -67,7 +67,7 @@ static __inline__ int tcp_v6_hashfn(struct in6_addr *laddr, u16 lport, int hashent = (lport ^ fport); hashent ^= (laddr->s6_addr32[3] ^ faddr->s6_addr32[3]); - return (hashent & ((TCP_HTABLE_SIZE/2) - 1)); + return (hashent & ((tcp_ehash_size/2) - 1)); } static __inline__ int tcp_v6_sk_hashfn(struct sock *sk) @@ -98,7 +98,7 @@ static int tcp_v6_get_port(struct sock *sk, unsigned short snum) do { rover++; if ((rover < low) || (rover > high)) rover = low; - tb = tcp_bound_hash[tcp_bhashfn(rover)]; + tb = tcp_bhash[tcp_bhashfn(rover)]; for ( ; tb; tb = tb->next) if (tb->port == rover) goto next; @@ -115,7 +115,7 @@ static int tcp_v6_get_port(struct sock *sk, unsigned short snum) snum = rover; tb = NULL; } else { - for (tb = tcp_bound_hash[tcp_bhashfn(snum)]; + for (tb = tcp_bhash[tcp_bhashfn(snum)]; tb != NULL; tb = tb->next) if (tb->port == snum) @@ -194,7 +194,7 @@ static void tcp_v6_hash(struct sock *sk) if(sk->state == TCP_LISTEN) skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)]; else - skp = &tcp_established_hash[(sk->hashent = tcp_v6_sk_hashfn(sk))]; + skp = &tcp_ehash[(sk->hashent = tcp_v6_sk_hashfn(sk))]; SOCKHASH_LOCK(); if((sk->next = *skp) != NULL) @@ -276,7 +276,7 @@ static inline struct sock *__tcp_v6_lookup(struct tcphdr *th, * have wildcards anyways. */ hash = tcp_v6_hashfn(daddr, hnum, saddr, sport); - for(sk = tcp_established_hash[hash]; sk; sk = sk->next) { + for(sk = tcp_ehash[hash]; sk; sk = sk->next) { /* For IPV6 do the cheaper port and family tests first. */ if(TCP_IPV6_MATCH(sk, saddr, daddr, ports, dif)) { if (sk->state == TCP_ESTABLISHED) @@ -285,7 +285,7 @@ static inline struct sock *__tcp_v6_lookup(struct tcphdr *th, } } /* Must check for a TIME_WAIT'er before going to listener hash. */ - for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next) { + for(sk = tcp_ehash[hash+(tcp_ehash_size/2)]; sk; sk = sk->next) { if(*((__u32 *)&(sk->dport)) == ports && sk->family == PF_INET6) { struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sk; @@ -336,7 +336,7 @@ static int tcp_v6_unique_address(struct sock *sk) /* Freeze the hash while we snoop around. */ SOCKHASH_LOCK(); - tb = tcp_bound_hash[tcp_bhashfn(snum)]; + tb = tcp_bhash[tcp_bhashfn(snum)]; for(; tb; tb = tb->next) { if(tb->port == snum && tb->owners != NULL) { /* Almost certainly the re-use port case, search the real hashes diff --git a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c index c2e2453db64e..a3fa6d271ff1 100644 --- a/net/irda/irlan/irlan_eth.c +++ b/net/irda/irlan/irlan_eth.c @@ -345,6 +345,7 @@ void irlan_eth_send_gratuitous_arp(struct device *dev) * is useful if we have changed access points on the same * subnet. */ +#ifdef CONFIG_INET DEBUG(4, "IrLAN: Sending gratuitous ARP\n"); in_dev = dev->ip_ptr; arp_send(ARPOP_REQUEST, ETH_P_ARP, @@ -352,6 +353,7 @@ void irlan_eth_send_gratuitous_arp(struct device *dev) dev, in_dev->ifa_list->ifa_address, NULL, dev->dev_addr, NULL); +#endif /* CONFIG_INET */ } /* diff --git a/net/netsyms.c b/net/netsyms.c index 1dfaad196320..031527426e4e 100644 --- a/net/netsyms.c +++ b/net/netsyms.c @@ -277,9 +277,11 @@ EXPORT_SYMBOL(inet_sendmsg); EXPORT_SYMBOL(inet_recvmsg); /* Socket demultiplexing. */ -EXPORT_SYMBOL(tcp_established_hash); +EXPORT_SYMBOL(tcp_ehash_size); +EXPORT_SYMBOL(tcp_ehash); EXPORT_SYMBOL(tcp_listening_hash); -EXPORT_SYMBOL(tcp_bound_hash); +EXPORT_SYMBOL(tcp_bhash_size); +EXPORT_SYMBOL(tcp_bhash); EXPORT_SYMBOL(udp_hash); EXPORT_SYMBOL(destroy_sock); @@ -496,8 +498,10 @@ EXPORT_SYMBOL(init_hippi_dev); EXPORT_SYMBOL(unregister_hipdev); #endif +#ifdef CONFIG_INET EXPORT_SYMBOL(sysctl_wmem_max); EXPORT_SYMBOL(sysctl_rmem_max); +#endif #if defined(CONFIG_ATALK) || defined(CONFIG_ATALK_MODULE) #include diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index dc06be6b0ed3..635ce705169a 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -467,6 +467,9 @@ call_encode(struct rpc_task *task) return; } + /* Zero buffer so we have automatic zero-padding of opaque & string */ + memset(task->tk_buffer, 0, bufsiz); + /* Encode header and provided arguments */ encode = rpcproc_encode(clnt, task->tk_proc); if (!(p = call_header(task))) { diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 143b6153fc2c..e879053011bb 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -38,7 +38,7 @@ svc_create(struct svc_program *prog, unsigned int bufsize, unsigned int xdrsize) memset(serv, 0, sizeof(*serv)); serv->sv_program = prog; - serv->sv_nrthreads = 1; + atomic_set(&serv->sv_nrthreads, 1); serv->sv_stats = prog->pg_stats; serv->sv_bufsz = bufsize? bufsize : 4096; serv->sv_xdrsize = xdrsize; @@ -61,10 +61,10 @@ svc_destroy(struct svc_serv *serv) dprintk("RPC: svc_destroy(%s, %d)\n", serv->sv_program->pg_name, - serv->sv_nrthreads); + atomic_read (&serv->sv_nrthreads)); - if (serv->sv_nrthreads) { - if (--(serv->sv_nrthreads) != 0) + if (atomic_read (&serv->sv_nrthreads)) { + if (!atomic_dec_and_test (&serv->sv_nrthreads)) return; } else printk("svc_destroy: no threads for serv=%p!\n", serv); @@ -128,7 +128,7 @@ svc_create_thread(svc_thread_fn func, struct svc_serv *serv) || !svc_init_buffer(&rqstp->rq_defbuf, serv->sv_bufsz)) goto out_thread; - serv->sv_nrthreads++; + atomic_inc(&serv->sv_nrthreads); rqstp->rq_server = serv; error = kernel_thread((int (*)(void *)) func, rqstp, 0); if (error < 0) diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 18db6ffc6fd2..266f389bb8b3 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -124,7 +124,7 @@ svc_sock_enqueue(struct svc_sock *svsk) "svc_sock_enqueue: server %p, rq_sock=%p!\n", rqstp, rqstp->rq_sock); rqstp->rq_sock = svsk; - svsk->sk_inuse++; + atomic_inc(&svsk->sk_inuse); wake_up(&rqstp->rq_wait); } else { dprintk("svc: socket %p put into queue\n", svsk->sk_sk); @@ -148,7 +148,7 @@ svc_sock_dequeue(struct svc_serv *serv) if (svsk) { dprintk("svc: socket %p dequeued, inuse=%d\n", - svsk->sk_sk, svsk->sk_inuse); + svsk->sk_sk, atomic_read(&svsk->sk_inuse)); svsk->sk_qued = 0; } @@ -206,7 +206,7 @@ svc_sock_release(struct svc_rqst *rqstp) return; svc_release_skb(rqstp); rqstp->rq_sock = NULL; - if (!--(svsk->sk_inuse) && svsk->sk_dead) { + if (atomic_dec_and_test(&svsk->sk_inuse) && svsk->sk_dead) { dprintk("svc: releasing dead socket\n"); sock_release(svsk->sk_sock); kfree(svsk); @@ -766,7 +766,7 @@ again: start_bh_atomic(); if ((svsk = svc_sock_dequeue(serv)) != NULL) { rqstp->rq_sock = svsk; - svsk->sk_inuse++; + atomic_inc(&svsk->sk_inuse); } else { /* No data pending. Go to sleep */ svc_serv_enqueue(serv, rqstp); @@ -793,7 +793,7 @@ again: end_bh_atomic(); dprintk("svc: server %p, socket %p, inuse=%d\n", - rqstp, svsk, svsk->sk_inuse); + rqstp, svsk, atomic_read(&svsk->sk_inuse)); len = svsk->sk_recvfrom(rqstp); dprintk("svc: got len=%d\n", len); @@ -988,11 +988,12 @@ svc_delete_socket(struct svc_sock *svsk) rpc_remove_list(&serv->sv_sockets, svsk); svsk->sk_dead = 1; - if (!svsk->sk_inuse) { + if (!atomic_read(&svsk->sk_inuse)) { sock_release(svsk->sk_sock); kfree(svsk); } else { - printk(KERN_NOTICE "svc: server socket destroy delayed\n"); + printk(KERN_NOTICE "svc: server socket destroy delayed (sk_inuse: %d)\n", + atomic_read(&svsk->sk_inuse)); /* svsk->sk_server = NULL; */ } } -- 2.39.5