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
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).
- 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
*************
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.
======
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
===================
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
=====
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
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
================
======
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
==========
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
======
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
=========
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
=========
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
===========
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
====
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
=====
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
=====
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
###
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
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]
arcrimi= [HW,NET]
+ ataflop= [HW, M68k]
+
atamouse= [HW,MOUSE] Atari Mouse.
atascsi= [HW,SCSI] Atari SCSI.
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]
edb= [HW,PS2]
- ether= [HW,NET] Ethernet.
+ ether= [HW,NET] Ethernet cards parameters (iomem,irq,dev_name).
fd_mcs= [HW,SCSI]
floppy= [HW]
- ftape= [HW] Floppy Tape subsystem.
+ ftape= [HW] Floppy Tape subsystem debugging options.
gdth= [HW,SCSI]
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]
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]
kbd-reset [VT]
- load_ramdisk= [RAM]
+ load_ramdisk= [RAM] initrd loading boolean
lp= [LPT] Parallel Printer.
mcdx= [HW,CD]
- md= [HW]
+ md= [HW] RAID subsystems devices and level
mdacon= [MDA]
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.
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
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=
pg. [PARIDE]
- pirq= [SMP,APIC]
+ pirq= [SMP,APIC] mp-table
plip= [LP,NET] Parallel port network link.
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.
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]
wdt= [HW]
- xd= [HW,XT]
+ xd= [HW,XT] Original XT 8bit disk controllers
xd_geo= [HW,XT]
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/)
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
}
}
-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);
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)
{
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);
/* 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);
}
out:
- __real__ ret = quo_f1;
- __imag__ ret = rem_f1;
- return ret;
+ *quot = quo_f1;
+ *remd = rem_f1;
+ return;
}
/*
: "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
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
#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)*/;
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;
* 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:
/*
* # of IRQ routing registers
*/
-int nr_ioapic_registers = 0;
+int nr_ioapic_registers[MAX_IO_APICS];
enum ioapic_irq_destination_types {
dest_Fixed = 0,
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 */
* 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);
}
/*
#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];
/*
* 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;
if (++first_free_entry >= PIN_MAP_SIZE)
panic("io_apic.c: whoops");
}
+ entry->apic = apic;
entry->pin = pin;
}
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; \
* 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;
*/
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);
}
/*
/*
* 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;
* 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;
}
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;
/*
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:
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
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:
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;
}
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)
/*
* 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;
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)
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
*/
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)
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,
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;
*/
{
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;
+ }
}
/*
/*
* 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");
}
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);
}
* 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);
* - 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");
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 {
/* 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;
{ 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",
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:
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;
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);
}
}
}
- 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;
}
#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
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
#
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
# Network device support
#
CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
# CONFIG_ARCNET is not set
# CONFIG_DUMMY is not set
# CONFIG_EQUALIZER is not set
# CONFIG_ETHERTAP is not set
+# CONFIG_NET_SB1000 is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
CONFIG_NET_ETHERNET=y
CONFIG_MACE=y
CONFIG_BMAC=y
# CONFIG_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
# CONFIG_NET_POCKET is not set
# CONFIG_FDDI is not set
# CONFIG_HIPPI is not set
-# CONFIG_DLCI is not set
+
+#
+# Appletalk devices
+#
# CONFIG_LTPC is not set
# CONFIG_COPS is not set
# CONFIG_IPDDP is not set
#
# CONFIG_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
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
CONFIG_EXT2_FS=y
# CONFIG_SYSV_FS is not set
# CONFIG_UFS_FS is not set
+# CONFIG_EFS_FS is not set
#
# Network File Systems
# CONFIG_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
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
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
#
#
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
# 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
# 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
#
# Block devices
#
-# CONFIG_BLK_DEV_FD is not set
+CONFIG_BLK_DEV_FD=y
CONFIG_BLK_DEV_IDE=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
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
# 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)
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
# 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
# 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
# CONFIG_YELLOWFIN is not set
# CONFIG_NET_ISA is not set
CONFIG_NET_EISA=y
-# CONFIG_PCNET32 is not set
+CONFIG_PCNET32=y
# CONFIG_ACENIC is not set
# CONFIG_AC3200 is not set
# CONFIG_APRICOT is not set
# CONFIG_LTPC is not set
# CONFIG_COPS is not set
# CONFIG_IPDDP is not set
-# CONFIG_PLIP is not set
CONFIG_PPP=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
#
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
# Ftape, the floppy tape device driver
#
# CONFIG_FTAPE is not set
+# CONFIG_FT_NORMAL_DEBUG is not set
+# CONFIG_FT_FULL_DEBUG is not set
+# CONFIG_FT_NO_TRACE is not set
+# CONFIG_FT_NO_TRACE_AT_ALL is not set
+# CONFIG_FT_STD_FDC is not set
+# CONFIG_FT_MACH2 is not set
+# CONFIG_FT_PROBE_FC10 is not set
+# CONFIG_FT_ALT_FDC is not set
#
# USB drivers - not for the faint of heart
# CONFIG_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
# 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
#
# 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
{
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 )
{
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);
}
}
/*
* 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)
/* 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; \
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
#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 */
.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
/*
- * $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.
*/
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;
}
}
__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[];
#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 */
/* Save unparsed command line copy for /proc/cmdline */
strcpy(saved_command_line, cmd_line);
+
*cmdline_p = cmd_line;
*memory_start_p = find_available_memory();
/*
- * $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)
*/
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! */
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) {
* 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) {
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;
}
/*
{
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\
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)
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);
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);
}
}
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
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;
}
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;
}
*/
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 */
}
/*
/* 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;
}
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 */
* 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 */
}
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 */
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);
}
}
}
-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;
}
}
-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;
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));
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;
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
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;
M_OBJS :=
MOD_LIST_NAME := FC4_MODULES
-include ../../.config
-
ifeq ($(CONFIG_FC4),y)
FC4 = fc.o
ifeq ($(CONFIG_MODULES),y)
*/
#include <linux/module.h>
+#include <linux/version.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/major.h>
* 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 <toe@unlserve.unl.edu>.
+ *
+ * $Id: macserial.c,v 1.24.2.4 1999/10/19 04:36:42 paulus Exp $
*/
#include <linux/config.h>
#ifdef CONFIG_SERIAL_CONSOLE
#include <linux/console.h>
#endif
+#include <linux/slab.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#ifdef CONFIG_KGDB
#include <asm/kgdb.h>
#endif
+#include <asm/dbdma.h>
#include "macserial.h"
};
#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
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];
__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 =
* 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;
}
static inline void write_zsreg(struct mac_zschannel *channel,
- unsigned char reg, unsigned char value)
+ unsigned char reg, unsigned char value)
{
unsigned long flags;
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);
+}
+
/*
* ----------------------------------------------------------------------
*
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)
{
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);
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
*/
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
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)
}
}
+/* 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.
}
}
-static void rs_timer(void)
-{
-}
-
static int startup(struct mac_serial * info, int can_sleep)
{
int delay;
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) {
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;
ZS_CLEARFIFO(info->zs_channel);
info->xmit_fifo_size = 1;
+ /*
+ * Reset DMAs
+ */
+ if (info->has_dma)
+ dma_init(info);
+
/*
* Clear the interrupt registers.
*/
/*
* 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];
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;
}
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;
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));
if (!tty || !info->xmit_buf || !tmp_buf)
return 0;
- save_flags(flags);
if (from_user) {
down(&tmp_buf_sem);
while (1) {
ret = -EFAULT;
break;
}
+ save_flags(flags);
cli();
c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
SERIAL_XMIT_SIZE - info->xmit_head));
up(&tmp_buf_sem);
} else {
while (1) {
+ save_flags(flags);
cli();
c = MIN(count,
MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
if (info->xmit_cnt && !tty->stopped && !info->tx_stopped
&& !info->tx_active)
transmit_chars(info);
- restore_flags(flags);
return ret;
}
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)
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
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
save_flags(flags); cli();
if (tty_hung_up_p(filp)) {
+ MOD_DEC_USE_COUNT;
restore_flags(flags);
return;
}
info->count = 0;
}
if (info->count) {
+ MOD_DEC_USE_COUNT;
restore_flags(flags);
return;
}
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.
#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);
info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE|
ZILOG_CLOSING);
wake_up_interruptible(&info->close_wait);
+ MOD_DEC_USE_COUNT;
}
/*
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;
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;
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 */
{
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)
/* 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)
/* 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",
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);
if (zs_chain == 0)
return -1;
+ set_scc_power(info, 1);
+
/* Reset the channel */
write_zsreg(info->zs_channel, R9, CHRA);
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);
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 */
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)
};
#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) */
/* 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) */
#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 */
#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 */
*/
#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 *);
#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);
}
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;
+ }
}
}
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;
}
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
#include <linux/config.h>
#include "arlan.h"
+#include <linux/sysctl.h>
+#include <linux/version.h>
#ifdef CONFIG_PROC_FS
-#include <linux/sysctl.h>
-#include <linux/version.h>
/* void enableReceive(struct device* dev);
*/
{0}
};
#endif
+#else
+static ctl_table arlan_table[MAX_ARLANS + 1] =
+{
+ {0}
+};
#endif
static int mmtu = 1234;
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
* 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 <linux/config.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
+#include <asm/spinlock.h>
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
* Michael Richard <mcr@solidum.com>)
* added chip id for 79c973/975 (thanks to Zach Brown <zab@zabbo.net>)
* 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. <kaf@fc.hp.com>
+ * v1.24ac Added SMP spinlocking - Alan Cox <alan@redhat.com>
+ * v1.25kf Added No Interrupt on successful Tx for some Tx's <kaf@fc.hp.com>
*/
*/
#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))
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
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,
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) &&
{
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;
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";
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";
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);
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;
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;
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;
{
struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
unsigned int ioaddr = dev->base_addr;
+ u16 status;
int entry;
unsigned long flags;
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 */
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;
clear_bit (0, (void *)&dev->tbusy);
else
lp->tx_full = 1;
- restore_flags(flags);
+ spin_unlock_irqrestore(&lp->lock, flags);
return 0;
}
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);
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;
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++;
dev->name, lp->a.read_csr (ioaddr, 0));
dev->interrupt = 0;
+
+ spin_unlock(&lp->lock);
return;
}
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;
}
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);
-/*****************************************************************************/
-/* 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 <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <asm/processor.h> /* Processor type for cache alignment. */
#include <asm/bitops.h>
#include <asm/io.h>
-
-#define RUN_AT(x) (jiffies + (x))
-
#include <linux/delay.h>
-#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 <linux/bios32.h>
-#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;
} 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(). */
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;
};
#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);
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;
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);
outb(EECS, ee_addr);
eeprom_delay();
+ /* read the 16-bits data in */
for (i = 16; i > 0; i--) {
outl(EECS, ee_addr);
eeprom_delay();
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);
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<<MIIpmdShift)|(location<<MIIregShift);
- int retval = 0;
+ u16 retval = 0;
int i;
mdio_reset(mdio_addr);
for (i = 15; i >= 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();
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();
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);
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;
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);
}
}
" (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;
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;
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 */
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];
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
#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. */
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 "
dev->name, status, inl(ioaddr + isr));
if ((status & (TxURN|TxERR|TxOK | RxORN|RxERR|RxOK)) == 0) {
+ /* nothing intresting happened */
break;
}
/* 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
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;
dev->name, cur_rx,
inb(ioaddr + cr));
tp->cur_rx = cur_rx;
+ outl( RxENA , ioaddr + cr ); /* LCS */
return 0;
}
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);
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);
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]);
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);
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 {
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:
- */
--- /dev/null
+/* 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 */
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"),
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"),
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 };
#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 */
/*
* DCDB Table Equates
*/
+#ifndef HOSTS_C
#define NO_DISCONNECT 0x00
#define DISCONNECT_ALLOWED 0x80
#define NO_AUTO_REQUEST_SENSE 0x40
#define TIMEOUT10 0x10
#define TIMEOUT60 0x20
#define TIMEOUT20M 0x30
-
/*
* Host adapter Flags (bit numbers)
*/
*/
#define SCB_ACTIVE 0x00001
#define SCB_WAITING 0x00002
-
+#endif /* HOSTS_C */
/*
* Passthru stuff
*/
/*
* Inquiry Data Format
*/
+#ifndef HOSTS_C
+
typedef struct {
u8 DeviceType:5;
u8 DeviceTypeQualifier:3;
u8 Reserved3[40];
} INQUIRYDATA, *PINQUIRYDATA;
+#endif
/*
* Read Capacity Data Format
*/
#define BOARD_QUARTZ 0x08000000L
#define BOARD_40LD 0x04000000L
+#ifndef HOSTS_C
#define SCB_FREE 0x0
#define SCB_ACTIVE 0x1
#define SCB_WAITQ 0x2
#define SCB_COMPLETE 0x4
#define SCB_ABORTED 0x5
#define SCB_RESET 0x6
+#endif
#define MEGA_CMD_TIMEOUT 10
} READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA;
// SCSI inquiry data
+#ifndef HOSTS_C
+
typedef struct _INQUIRYDATA
{
UCHAR DeviceType :5;
UCHAR VendorSpecific[20];
UCHAR Reserved3[40];
} INQUIRYDATA, *PINQUIRYDATA;
+#endif
// IDE IDENTIFY data
typedef struct _IDENTIFY_DATA
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;
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)) {
#include <linux/version.h>
#endif
+#ifndef LinuxVersionCode
#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+#endif
#include <linux/types.h>
#include <linux/kdev_t.h>
#endif
#include <linux/config.h>
+#ifndef LinuxVersionCode
#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
-
+#endif
/*
* NCR PQS/PDS special device support.
*/
#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)
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
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
/* 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
#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);
}
#endif
-#endif
* 15.06.99 0.23 Fix bad allocation bug.
* Thanks to Deti Fliegl <fliegl@in.tum.de>
* 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 <gialluca@mail.tiscalinet.it>
+ * 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:
*
/*****************************************************************************/
-#include <linux/config.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/string.h>
#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
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)
[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);
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:
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;
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);
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);
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;
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;
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;
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;
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;
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;
es1370_handle_midi(s);
spin_unlock_irqrestore(&s->lock, flags);
}
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&s->midi.owait, &wait);
return ret;
}
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;
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 ||
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;
/* 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!! */
/*
* 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
* 15.06.99 0.12 Fix bad allocation bug.
* Thanks to Deti Fliegl <fliegl@in.tum.de>
* 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 <dplatt@snulbug.mtview.ca.us>.
+ * 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 <dplatt@snulbug.mtview.ca.us>.
+ * module_init/__setup fixes
+ * 08.16.99 0.16 Joe Cotellese <joec@ensoniq.com>
+ * 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 <t29w0267@ip.media.kyoto-u.ac.jp>
*
*/
/*****************************************************************************/
-#include <linux/config.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/malloc.h>
#include <linux/soundcard.h>
#include <linux/pci.h>
-#include <asm/io.h>
-#include <asm/dma.h>
#include <linux/init.h>
#include <linux/poll.h>
+#include <linux/bitops.h>
+#include <linux/proc_fs.h>
#include <asm/spinlock.h>
+#include <asm/io.h>
+#include <asm/dma.h>
#include <asm/uaccess.h>
#include <asm/hardirq.h>
+#include "ac97.h"
/* --------------------------------------------------------------------- */
#undef OSS_DOCUMENTED_MIXER_SEMANTICS
+#undef ES1371_DEBUG
/* --------------------------------------------------------------------- */
#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
#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
/* misc stuff */
-
+#define POLL_COUNT 0x1000
#define FMODE_DAC 4 /* slight misuse of mode_t */
/* MIDI buffer sizes */
/* 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;
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);
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; i<POLL_COUNT; i++){
+ temp = inl (s->io + 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;
spin_unlock_irqrestore(&s->lock, flags);
}
+
static void set_dac1_rate(struct es1371_state *s, unsigned rate)
{
unsigned long flags;
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);
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);
/* --------------------------------------------------------------------- */
+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; t<POLL_COUNT; t++){
+ if((inl(s->io+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; t<POLL_COUNT; t++){
+ if((inl(s->io+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);
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; t<POLL_COUNT; t++){
+ if((inl(s->io+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; t<POLL_COUNT; t++){
+ if((inl(s->io+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);
}
/* --------------------------------------------------------------------- */
+/*
+ * 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
/*
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
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);
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);
/* 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);
#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);
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);
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);
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)
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)
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)
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;
}
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;
}
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:
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) {
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 |
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;
}
}
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;
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;
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;
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;
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;
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;
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;
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;
es1371_handle_midi(s);
spin_unlock_irqrestore(&s->lock, flags);
}
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&s->midi.owait, &wait);
return ret;
}
/* --------------------------------------------------------------------- */
+/*
+ * 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
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 ||
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;
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) {
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 */
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");
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;
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();
* 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 <linux/config.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/string.h>
#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
#define MPUBASE_EXTENT 4
#define GPBASE_EXTENT 4
+#define FMSYNTH_EXTENT 4
/* MIDI buffer sizes */
#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
/* --------------------------------------------------------------------- */
{
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);
}
__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)
*data = inb(s->sbbase+0xa);
return 1;
}
+ printk(KERN_ERR "esssolo1: read_seq timeout\n");
return 0;
}
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);
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;
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;
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;
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;
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;
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;
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;
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;
solo1_handle_midi(s);
spin_unlock_irqrestore(&s->lock, flags);
}
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&s->midi.owait, &wait);
return ret;
}
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 */
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;
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 ||
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;
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)) {
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 */
index++;
continue;
+ err:
+ unregister_sound_dsp(s->dev_dmfm);
err_dev4:
unregister_sound_dsp(s->dev_midi);
err_dev3:
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:
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);
module_init(init_solo1);
module_exit(cleanup_solo1);
-
+#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
# include <linux/init.h>
#endif
#include <asm/irq.h>
+#include <asm/io.h>
#include "sound_config.h"
#include "sound_firmware.h"
#ifdef MSND_CLASSIC
#include <linux/config.h>
#include <linux/pci.h>
#include <linux/module.h>
+#include <linux/delay.h>
+
#include "sound_config.h"
#include "soundmodule.h"
#include "nm256.h"
#include "sound_config.h"
#include "soundmodule.h"
-#ifdef CONFIG_YM3812
-
#include "opl3.h"
#define MAX_VOICE 18
EXPORT_SYMBOL(opl3_init);
EXPORT_SYMBOL(opl3_detect);
MODULE_PARM(io, "i");
-
-#endif
* 15.06.99 0.15 Fix bad allocation bug.
* Thanks to Deti Fliegl <fliegl@in.tum.de>
* 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
*
*/
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);
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;
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;
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;
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;
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;
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;
sv_handle_midi(s);
spin_unlock_irqrestore(&s->lock, flags);
}
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&s->midi.owait, &wait);
return ret;
}
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");
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;
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 */
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);
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 &&
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.
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,
fd_offset);
if (error != N_TXTADDR(ex)) {
- fput(file);
sys_close(fd);
send_sig(SIGKILL, current, 0);
return error;
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);
unsigned long error;
int retval;
loff_t offset = 0;
+ static int warnings = 0;
struct exec ex;
retval = -EACCES;
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;
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,
/*
* 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, };
}
}
-#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)
{
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<<order) * PAGE_SIZE);
if (!hash_table)
panic("Failed to allocate buffer hash table\n");
memset(hash_table, 0, nr_hash * sizeof(struct buffer_head *));
- bh_hash_mask = nr_hash-1;
bh_cachep = kmem_cache_create("buffer_head",
sizeof(struct buffer_head),
#include <linux/init.h>
#include <asm/uaccess.h>
+#include <asm/cache.h>
#define DCACHE_PARANOIA 1
/* #define DCACHE_DEBUG 1 */
* 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 {
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);
}
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,
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<<order) * PAGE_SIZE);
+
+ if (!dentry_hashtable)
+ panic("Failed to allocate dcache hash table\n");
+
+ d = dentry_hashtable;
+ i = nr_hash;
do {
INIT_LIST_HEAD(d);
d++;
unlock_super (sb);
}
-/*
- * This function increments the inode version number
- *
- * This may be used one day by the NFS server
- */
-static void inc_inode_version (struct inode * inode,
- struct ext2_group_desc *gdp,
- int mode)
-{
- inode->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
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)) {
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;
#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;
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;
#endif
#include <linux/lockd/bind.h>
#include <linux/lockd/xdr.h>
+#include <linux/lockd/syscall.h>
#include <linux/init.h>
#include <linux/nls.h>
#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 */
#include <linux/dcache.h>
#include <linux/init.h>
#include <linux/quotaops.h>
+#include <linux/random.h>
/*
* New inode.c implementation.
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.
*
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... */
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;
+}
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/svc.h>
#include <linux/lockd/lockd.h>
+#include <linux/lockd/syscall.h>
/* Start/stop the daemon */
EXPORT_SYMBOL(lockd_up);
/* 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);
#include <linux/sunrpc/svc.h>
#include <linux/sunrpc/svcsock.h>
#include <linux/lockd/lockd.h>
+#include <linux/lockd/syscall.h>
#include <linux/nfs.h>
#define NLMDBG_FACILITY NLMDBG_SVC
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)
{
nlmsvc_pid = 0;
lockd_exit = NULL;
nlmxdr_init();
+ do_lockdctl = lockdctl;
return 0;
}
{
/* FIXME: delete all NLM clients */
nlm_shutdown_hosts();
+ do_lockdctl = NULL;
}
#endif
"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;
+}
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;
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.
/* 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. */
#include <linux/module.h>
#include <linux/version.h>
-#include <linux/linkage.h>
+#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/nfsd/cache.h>
#include <linux/nfsd/xdr.h>
#include <linux/nfsd/syscall.h>
+#include <linux/lockd/syscall.h>
#if LINUX_VERSION_CODE >= 0x020100
#include <asm/uaccess.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
-extern void nfsd_fh_init(void);
extern long sys_call_table[];
static int nfsctl_svc(struct nfsctl_svc *data);
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;
}
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;
}
goto done;
}
+ if (cmd >= NFSCTL_LOCKD) {
+ err = lockdctl(cmd, argp, resp);
+ goto done;
+ }
+
switch(cmd) {
case NFSCTL_SVC:
err = nfsctl_svc(&arg->ca_svc);
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();
#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;
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);
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 {
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;
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;
}
*/
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--;
}
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);
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;
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;
}
}
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);
iput(dir);
out_root:
dput(root);
- goto out;
+ goto out_page;
}
/*
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 */
*/
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
}
/*
- * 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);
}
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;
*/
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);
return dentry;
}
out:
+#endif
return NULL;
}
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;
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;
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
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.
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.
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++;
}
#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);
}
/*
* ... 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;
}
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;
*/
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;
*/
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
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;
*/
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;
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);
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);
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:
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.
}
/*
- * Free the dentry and path caches.
+ * Free the rename and path caches.
*/
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.
*/
"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;
+}
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;
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;
*
* Authors: Olaf Kirch (okir@monad.swb.de)
*
- * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995-1999 Olaf Kirch <okir@monad.swb.de>
*/
#define __NO_VERSION__
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)
nfssvc_boot = xtime; /* record boot time */
first = 1;
}
+#if 0
lockd_up(); /* start lockd */
+#endif
/*
* The main request loop
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();
}
{
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,
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;
#include <linux/locks.h>
#include <linux/fs.h>
#include <linux/major.h>
-#include <linux/ext2_fs.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
/* 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] == '.'))
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) {
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;
/* 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);
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)
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 ...
*/
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;
}
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)
/* Compose the fh so the dentry will be freed ... */
out_compose:
fh_compose(resfhp, fhp->fh_export, dnew);
+
out:
return err;
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:
err = PTR_ERR(rdentry);
if (IS_ERR(rdentry))
goto out_nfserr;
-
if (!rdentry->d_inode) {
dput(rdentry);
err = nfserr_noent;
fh_unlock(fhp);
dput(rdentry);
-
+ expire_by_dentry(rdentry);
} else {
/* It's RMDIR */
/* See comments in fs/namei.c:do_rmdir */
goto out_nfserr;
if (EX_ISSYNC(fhp->fh_export))
write_inode_now(dirp);
+
out:
return err;
{
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;
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
* 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));
}
/*
{
}
+#define ioremap_nocache(offset, size) ioremap((offset),(size))
+
#ifndef readb
# define readb(a) _readb((unsigned long)(a))
#endif
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 */
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);
#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 */
--- /dev/null
+/*
+ * 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 <linux/nfsd/syscall.h>
+
+#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 */
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);
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);
}
}
+/*
+ * 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 */
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__
#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;
/*
* 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);
*/
#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,
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))
#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
#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
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__ */
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 */
#include <linux/sunrpc/types.h>
#include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/svcauth.h>
+#include <asm/atomic.h>
/*
* RPC service.
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 */
#define SUNRPC_SVCSOCK_H
#include <linux/sunrpc/svc.h>
+#include <asm/atomic.h>
/*
* RPC server socket.
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 */
#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)
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) **/
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;
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 */
#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) */
#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]);
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
+
+
+
#include <net/checksum.h>
/* 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
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);
/* 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
dcache_init();
vma_init();
buffer_init(memory_end-memory_start);
+ page_cache_init(memory_end-memory_start);
signals_init();
inode_init();
file_table_init();
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
#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;
EXPORT_SYMBOL(igrab);
EXPORT_SYMBOL(iunique);
EXPORT_SYMBOL(iget);
+EXPORT_SYMBOL(iget_in_use);
EXPORT_SYMBOL(iput);
EXPORT_SYMBOL(__namei);
EXPORT_SYMBOL(lookup_dentry);
#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);
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},
#include <linux/file.h>
#include <linux/swapctl.h>
#include <linux/slab.h>
+#include <linux/init.h>
#include <asm/pgtable.h>
#include <asm/uaccess.h>
*/
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
}
}
}
+
+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 *));
+}
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
}
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
* 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.
*/
#include <linux/config.h>
+#include <linux/module.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
+#include <linux/malloc.h>
#include <linux/string.h>
#include <linux/net.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/if_arp.h>
#include <linux/ip.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <asm/system.h>
+#include <linux/rtnetlink.h>
#include <net/br.h>
#include <linux/proc_fs.h>
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;
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
*
/* 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;
{ /* (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.
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);
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:
{
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))
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))
{
}
}
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
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) {
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];
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 */
*/
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
/*
* 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 */
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 */
{
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);
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.
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)
}
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);
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))
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);
}
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
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);
#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
}
#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
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
(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);
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);
static int enabled = 0;
if(!enabled)
+ {
+ enabled=1;
sysctl_ip_always_defrag++;
+ }
*answer = FW_REDIRECT;
return 1;
#endif
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),
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);
}
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;
* 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
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)
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;
{
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) {
#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)
;
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;
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)
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;
* 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;
}
}
/* 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);
/* 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)
}
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) {
/* 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
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) {
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);
}
}
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));
}
/*
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)
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;
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)
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)
* 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)
}
}
/* 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;
/* 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
* 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,
dev,
in_dev->ifa_list->ifa_address,
NULL, dev->dev_addr, NULL);
+#endif /* CONFIG_INET */
}
/*
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);
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<linux/if_ltalk.h>
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))) {
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;
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);
|| !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)
"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);
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;
}
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);
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);
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);
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; */
}
}