diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/alpha/config.in linux-2.4.27-leo/arch/alpha/config.in
--- linux-2.4.27/arch/alpha/config.in	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.27-leo/arch/alpha/config.in	2004-09-17 03:19:57.000000000 +0100
@@ -468,3 +468,12 @@
 
 source crypto/Config.in
 source lib/Config.in
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+	source grsecurity/Config.in
+fi
+endmenu
+
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/alpha/kernel/osf_sys.c linux-2.4.27-leo/arch/alpha/kernel/osf_sys.c
--- linux-2.4.27/arch/alpha/kernel/osf_sys.c	2003-06-13 15:51:29.000000000 +0100
+++ linux-2.4.27-leo/arch/alpha/kernel/osf_sys.c	2004-09-17 03:19:57.000000000 +0100
@@ -33,6 +33,7 @@
 #include <linux/file.h>
 #include <linux/types.h>
 #include <linux/ipc.h>
+#include <linux/grsecurity.h>
 
 #include <asm/fpu.h>
 #include <asm/io.h>
@@ -230,6 +231,11 @@
 	struct file *file = NULL;
 	unsigned long ret = -EBADF;
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (flags & MAP_MIRROR)
+		return -EINVAL;
+#endif
+
 #if 0
 	if (flags & (_MAP_HASSEMAPHORE | _MAP_INHERIT | _MAP_UNALIGNED))
 		printk("%s: unimplemented OSF mmap flags %04lx\n", 
@@ -240,6 +246,13 @@
 		if (!file)
 			goto out;
 	}
+
+	if(gr_handle_mmap(file, prot)) {
+		fput(file);
+		ret = -EACCES;
+		goto out;
+	}
+
 	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 	down_write(&current->mm->mmap_sem);
 	ret = do_mmap(file, addr, len, prot, flags, off);
@@ -1357,6 +1370,10 @@
 	   merely specific addresses, but regions of memory -- perhaps
 	   this feature should be incorporated into all ports?  */
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDMMAP
+	if (!(current->flags & PF_PAX_RANDMMAP) || !filp)
+#endif
+
 	if (addr) {
 		addr = arch_get_unmapped_area_1 (PAGE_ALIGN(addr), len, limit);
 		if (addr != -ENOMEM)
@@ -1364,8 +1381,15 @@
 	}
 
 	/* Next, try allocating at TASK_UNMAPPED_BASE.  */
-	addr = arch_get_unmapped_area_1 (PAGE_ALIGN(TASK_UNMAPPED_BASE),
-					 len, limit);
+
+	addr = TASK_UNMAPPED_BASE;
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDMMAP
+	if (current->flags & PF_PAX_RANDMMAP)
+		addr += current->mm->delta_mmap;
+#endif
+
+	addr = arch_get_unmapped_area_1 (PAGE_ALIGN(addr), len, limit);
 	if (addr != -ENOMEM)
 		return addr;
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/alpha/kernel/process.c linux-2.4.27-leo/arch/alpha/kernel/process.c
--- linux-2.4.27/arch/alpha/kernel/process.c	2003-08-25 12:44:39.000000000 +0100
+++ linux-2.4.27-leo/arch/alpha/kernel/process.c	2004-09-17 03:19:36.000000000 +0100
@@ -186,6 +186,7 @@
 	args.mode = mode;
 	args.restart_cmd = restart_cmd;
 #ifdef CONFIG_SMP
+	preempt_disable();
 	smp_call_function(common_shutdown_1, &args, 1, 0);
 #endif
 	common_shutdown_1(&args);
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/alpha/kernel/ptrace.c linux-2.4.27-leo/arch/alpha/kernel/ptrace.c
--- linux-2.4.27/arch/alpha/kernel/ptrace.c	2003-06-13 15:51:29.000000000 +0100
+++ linux-2.4.27-leo/arch/alpha/kernel/ptrace.c	2004-09-17 03:19:57.000000000 +0100
@@ -13,6 +13,7 @@
 #include <linux/ptrace.h>
 #include <linux/user.h>
 #include <linux/slab.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
@@ -275,6 +276,10 @@
 	read_unlock(&tasklist_lock);
 	if (!child)
 		goto out_notsk;
+
+	if(gr_handle_ptrace(child, request))
+		goto out;
+
 	if (request == PTRACE_ATTACH) {
 		ret = ptrace_attach(child);
 		goto out;
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/alpha/mm/fault.c linux-2.4.27-leo/arch/alpha/mm/fault.c
--- linux-2.4.27/arch/alpha/mm/fault.c	2002-11-28 23:53:08.000000000 +0000
+++ linux-2.4.27-leo/arch/alpha/mm/fault.c	2004-09-17 03:19:57.000000000 +0100
@@ -53,6 +53,139 @@
 	__reload_thread(&current->thread);
 }
 
+/*
+ * PaX: decide what to do with offenders (regs->pc = fault address)
+ *
+ * returns 1 when task should be killed
+ *         2 when patched PLT trampoline was detected
+ *         3 when unpatched PLT trampoline was detected
+ *	   4 when legitimate ET_EXEC was detected
+ */
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+static int pax_handle_fetch_fault(struct pt_regs *regs)
+{
+	int err;
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (current->flags & PF_PAX_RANDEXEC) {
+		if (regs->pc >= current->mm->start_code &&
+		    regs->pc < current->mm->end_code)
+		{
+			if (regs->r26 == regs->pc)
+				return 1;
+			regs->pc += current->mm->delta_exec;
+			return 4;
+		}
+	}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUPLT
+	do { /* PaX: patched PLT emulation #1 */
+		unsigned int ldah, ldq, jmp;
+
+		err = get_user(ldah, (unsigned int *)regs->pc);
+		err |= get_user(ldq, (unsigned int *)(regs->pc+4));
+		err |= get_user(jmp, (unsigned int *)(regs->pc+8));
+
+		if (err)
+			break;
+
+		if ((ldah & 0xFFFF0000U)== 0x277B0000U &&
+		    (ldq & 0xFFFF0000U) == 0xA77B0000U &&
+		    jmp == 0x6BFB0000U)
+		{
+			unsigned long r27, addr;
+			unsigned long addrh = (ldah | 0xFFFFFFFFFFFF0000UL) << 16;
+			unsigned long addrl = ldq | 0xFFFFFFFFFFFF0000UL;
+
+			addr = regs->r27 + ((addrh ^ 0x80000000UL) + 0x80000000UL) + ((addrl ^ 0x8000UL) + 0x8000UL);
+			err = get_user(r27, (unsigned long*)addr);
+			if (err)
+				break;
+
+			regs->r27 = r27;
+			regs->pc = r27;
+			return 2;
+		}
+	} while (0);
+
+	do { /* PaX: patched PLT emulation #2 */
+		unsigned int ldah, lda, br;
+
+		err = get_user(ldah, (unsigned int *)regs->pc);
+		err |= get_user(lda, (unsigned int *)(regs->pc+4));
+		err |= get_user(br, (unsigned int *)(regs->pc+8));
+
+		if (err)
+			break;
+
+		if ((ldah & 0xFFFF0000U)== 0x277B0000U &&
+		    (lda & 0xFFFF0000U) == 0xA77B0000U &&
+		    (br & 0xFFE00000U) == 0xC3E00000U)
+		{
+			unsigned long addr = br | 0xFFFFFFFFFFE00000UL;
+			unsigned long addrh = (ldah | 0xFFFFFFFFFFFF0000UL) << 16;
+			unsigned long addrl = lda | 0xFFFFFFFFFFFF0000UL;
+
+			regs->r27 += ((addrh ^ 0x80000000UL) + 0x80000000UL) + ((addrl ^ 0x8000UL) + 0x8000UL);
+			regs->pc += 12 + (((addr ^ 0x00100000UL) + 0x00100000UL) << 2);
+			return 2;
+		}
+	} while (0);
+
+	do { /* PaX: unpatched PLT emulation */
+		unsigned int br;
+
+		err = get_user(br, (unsigned int *)regs->pc);
+
+		if (!err && (br & 0xFFE00000U) == 0xC3800000U) {
+			unsigned int br2, ldq, nop, jmp;
+			unsigned long addr = br | 0xFFFFFFFFFFE00000UL, resolver;
+
+			addr = regs->pc + 4 + (((addr ^ 0x00100000UL) + 0x00100000UL) << 2);
+			err = get_user(br2, (unsigned int *)addr);  
+			err |= get_user(ldq, (unsigned int *)(addr+4));
+			err |= get_user(nop, (unsigned int *)(addr+8));
+			err |= get_user(jmp, (unsigned int *)(addr+12));
+			err |= get_user(resolver, (unsigned long *)(addr+16));
+
+			if (err)
+				break;
+
+			if (br2 == 0xC3600000U &&
+			    ldq == 0xA77B000CU &&
+			    nop == 0x47FF041FU &&
+			    jmp == 0x6B7B0000U)
+			{
+				regs->r28 = regs->pc+4;
+				regs->r27 = addr+16;
+				regs->pc = resolver;
+				return 3;
+			}
+		}
+	} while (0);
+#endif
+
+	return 1;
+}
+
+void pax_report_insns(void *pc)
+{
+	unsigned long i;
+
+	printk(KERN_ERR "PAX: bytes at PC: ");
+	for (i = 0; i < 5; i++) {
+		unsigned int c;
+		if (get_user(c, (unsigned int*)pc+i)) {
+			printk("<invalid address>.");
+			break;
+		}
+		printk("%08x ", c);
+	}
+	printk("\n");
+}
+#endif
+
 
 /*
  * This routine handles page faults.  It determines the address,
@@ -133,8 +266,32 @@
 good_area:
 	info.si_code = SEGV_ACCERR;
 	if (cause < 0) {
-		if (!(vma->vm_flags & VM_EXEC))
+		if (!(vma->vm_flags & VM_EXEC)) {
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+			if (!(current->flags & PF_PAX_PAGEEXEC) || address != regs->pc)
+				goto bad_area;
+
+			up_read(&mm->mmap_sem);
+			switch(pax_handle_fetch_fault(regs)) {
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUPLT
+			case 2:
+			case 3:
+				return;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+			case 4:
+				return;
+#endif
+			}
+			pax_report_fault(regs, (void*)regs->pc, (void*)rdusp());
+			do_exit(SIGKILL);
+#else
 			goto bad_area;
+#endif
+		}
 	} else if (!cause) {
 		/* Allow reads even for write-only mappings */
 		if (!(vma->vm_flags & (VM_READ | VM_WRITE)))
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/arm/config.in linux-2.4.27-leo/arch/arm/config.in
--- linux-2.4.27/arch/arm/config.in	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.27-leo/arch/arm/config.in	2004-09-17 03:19:57.000000000 +0100
@@ -736,3 +736,11 @@
 
 source crypto/Config.in
 source lib/Config.in
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+	source grsecurity/Config.in
+fi
+endmenu
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/cris/config.in linux-2.4.27-leo/arch/cris/config.in
--- linux-2.4.27/arch/cris/config.in	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.27-leo/arch/cris/config.in	2004-09-17 03:19:57.000000000 +0100
@@ -276,3 +276,12 @@
 source crypto/Config.in
 source lib/Config.in
 endmenu
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+    source grsecurity/Config.in
+fi
+endmenu
+
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/boot/bootsect.S linux-2.4.27-leo/arch/i386/boot/bootsect.S
--- linux-2.4.27/arch/i386/boot/bootsect.S	2003-08-25 12:44:39.000000000 +0100
+++ linux-2.4.27-leo/arch/i386/boot/bootsect.S	2004-09-17 03:19:57.000000000 +0100
@@ -237,7 +237,7 @@
 #ifdef __BIG_KERNEL__
 					# look in setup.S for bootsect_kludge
 	bootsect_kludge = 0x220		# 0x200 + 0x20 which is the size of the
-	lcall	bootsect_kludge		# bootsector + bootsect_kludge offset
+	lcall	*bootsect_kludge	# bootsector + bootsect_kludge offset
 #else
 	movw	%es, %ax
 	subw	$SYSSEG, %ax
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/boot/setup.S linux-2.4.27-leo/arch/i386/boot/setup.S
--- linux-2.4.27/arch/i386/boot/setup.S	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.27-leo/arch/i386/boot/setup.S	2004-09-17 03:19:57.000000000 +0100
@@ -637,7 +637,7 @@
 	cmpw	$0, %cs:realmode_swtch
 	jz	rmodeswtch_normal
 
-	lcall	%cs:realmode_swtch
+	lcall	*%cs:realmode_swtch
 
 	jmp	rmodeswtch_end
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/config.in linux-2.4.27-leo/arch/i386/config.in
--- linux-2.4.27/arch/i386/config.in	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.27-leo/arch/i386/config.in	2004-09-17 03:19:57.000000000 +0100
@@ -99,6 +99,7 @@
 fi
 if [ "$CONFIG_M686" = "y" ]; then
    define_int  CONFIG_X86_L1_CACHE_SHIFT 5
+   define_bool CONFIG_X86_ALIGNMENT_16 y
    define_bool CONFIG_X86_HAS_TSC y
    define_bool CONFIG_X86_GOOD_APIC y
    bool 'PGE extensions (not for Cyrix/Transmeta)' CONFIG_X86_PGE
@@ -108,6 +109,7 @@
 fi
 if [ "$CONFIG_MPENTIUMIII" = "y" ]; then
    define_int  CONFIG_X86_L1_CACHE_SHIFT 5
+   define_bool CONFIG_X86_ALIGNMENT_16 y
    define_bool CONFIG_X86_HAS_TSC y
    define_bool CONFIG_X86_GOOD_APIC y
    define_bool CONFIG_X86_PGE y
@@ -116,6 +118,7 @@
 fi
 if [ "$CONFIG_MPENTIUM4" = "y" ]; then
    define_int  CONFIG_X86_L1_CACHE_SHIFT 7
+   define_bool CONFIG_X86_ALIGNMENT_16 y
    define_bool CONFIG_X86_HAS_TSC y
    define_bool CONFIG_X86_GOOD_APIC y
    define_bool CONFIG_X86_PGE y
@@ -135,6 +138,7 @@
 fi
 if [ "$CONFIG_MK7" = "y" ]; then
    define_int  CONFIG_X86_L1_CACHE_SHIFT 6
+   define_bool CONFIG_X86_ALIGNMENT_16 y
    define_bool CONFIG_X86_HAS_TSC y
    define_bool CONFIG_X86_GOOD_APIC y
    define_bool CONFIG_X86_USE_3DNOW y
@@ -225,6 +229,7 @@
 bool 'Math emulation' CONFIG_MATH_EMULATION
 bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR
 bool 'Symmetric multi-processing support' CONFIG_SMP
+bool 'Preemptible Kernel' CONFIG_PREEMPT
 if [ "$CONFIG_SMP" != "y" ]; then
    bool 'Local APIC support on uniprocessors' CONFIG_X86_UP_APIC
    dep_bool 'IO-APIC support on uniprocessors' CONFIG_X86_UP_IOAPIC $CONFIG_X86_UP_APIC
@@ -258,9 +263,12 @@
    fi
 fi
 
-if [ "$CONFIG_SMP" = "y" -a "$CONFIG_X86_CMPXCHG" = "y" ]; then
-   define_bool CONFIG_HAVE_DEC_LOCK y
+if [ "$CONFIG_SMP" = "y" -o "$CONFIG_PREEMPT" = "y" ]; then
+   if [ "$CONFIG_X86_CMPXCHG" = "y" ]; then
+      define_bool CONFIG_HAVE_DEC_LOCK y
+   fi
 fi
+
 endmenu
 
 mainmenu_option next_comment
@@ -487,3 +495,11 @@
 
 source crypto/Config.in
 source lib/Config.in
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+	source grsecurity/Config.in
+fi
+endmenu
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/kernel/apm.c linux-2.4.27-leo/arch/i386/kernel/apm.c
--- linux-2.4.27/arch/i386/kernel/apm.c	2003-08-25 12:44:39.000000000 +0100
+++ linux-2.4.27-leo/arch/i386/kernel/apm.c	2004-09-17 03:19:57.000000000 +0100
@@ -614,7 +614,7 @@
 	__asm__ __volatile__(APM_DO_ZERO_SEGS
 		"pushl %%edi\n\t"
 		"pushl %%ebp\n\t"
-		"lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"
+		"lcall *%%ss:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"
 		"setc %%al\n\t"
 		"popl %%ebp\n\t"
 		"popl %%edi\n\t"
@@ -666,7 +666,7 @@
 		__asm__ __volatile__(APM_DO_ZERO_SEGS
 			"pushl %%edi\n\t"
 			"pushl %%ebp\n\t"
-			"lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"
+			"lcall *%%ss:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"
 			"setc %%bl\n\t"
 			"popl %%ebp\n\t"
 			"popl %%edi\n\t"
@@ -1985,6 +1985,12 @@
 		 __va((unsigned long)0x40 << 4));
 	_set_limit((char *)&gdt[APM_40 >> 3], 4095 - (0x40 << 4));
 
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	set_base(gdt2[APM_40 >> 3],
+		__va((unsigned long)0x40 << 4));
+	_set_limit((char *)&gdt2[APM_40 >> 3], 4095 - (0x40 << 4));
+#endif
+
 	apm_bios_entry.offset = apm_info.bios.offset;
 	apm_bios_entry.segment = APM_CS;
 	set_base(gdt[APM_CS >> 3],
@@ -1993,6 +1999,16 @@
 		 __va((unsigned long)apm_info.bios.cseg_16 << 4));
 	set_base(gdt[APM_DS >> 3],
 		 __va((unsigned long)apm_info.bios.dseg << 4));
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	set_base(gdt2[APM_CS >> 3],
+		__va((unsigned long)apm_info.bios.cseg << 4));
+	set_base(gdt2[APM_CS_16 >> 3],
+		__va((unsigned long)apm_info.bios.cseg_16 << 4));
+	set_base(gdt2[APM_DS >> 3],
+		__va((unsigned long)apm_info.bios.dseg << 4));
+#endif
+
 #ifndef APM_RELAX_SEGMENTS
 	if (apm_info.bios.version == 0x100) {
 #endif
@@ -2002,6 +2018,13 @@
 		_set_limit((char *)&gdt[APM_CS_16 >> 3], 64 * 1024 - 1);
 		/* For the DEC Hinote Ultra CT475 (and others?) */
 		_set_limit((char *)&gdt[APM_DS >> 3], 64 * 1024 - 1);
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+		_set_limit((char *)&gdt2[APM_CS >> 3], 64 * 1024 - 1);
+		_set_limit((char *)&gdt2[APM_CS_16 >> 3], 64 * 1024 - 1);
+		_set_limit((char *)&gdt2[APM_DS >> 3], 64 * 1024 - 1);
+#endif
+
 #ifndef APM_RELAX_SEGMENTS
 	} else {
 		_set_limit((char *)&gdt[APM_CS >> 3],
@@ -2010,6 +2033,16 @@
 			(apm_info.bios.cseg_16_len - 1) & 0xffff);
 		_set_limit((char *)&gdt[APM_DS >> 3],
 			(apm_info.bios.dseg_len - 1) & 0xffff);
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+		_set_limit((char *)&gdt2[APM_CS >> 3],
+			(apm_info.bios.cseg_len - 1) & 0xffff);
+		_set_limit((char *)&gdt2[APM_CS_16 >> 3],
+			(apm_info.bios.cseg_16_len - 1) & 0xffff);
+		_set_limit((char *)&gdt2[APM_DS >> 3],
+			(apm_info.bios.dseg_len - 1) & 0xffff);
+#endif
+
 	}
 #endif
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/kernel/cpuid.c linux-2.4.27-leo/arch/i386/kernel/cpuid.c
--- linux-2.4.27/arch/i386/kernel/cpuid.c	2001-10-11 17:04:57.000000000 +0100
+++ linux-2.4.27-leo/arch/i386/kernel/cpuid.c	2004-09-17 03:19:36.000000000 +0100
@@ -60,7 +60,8 @@
 static inline void do_cpuid(int cpu, u32 reg, u32 *data)
 {
   struct cpuid_command cmd;
-  
+
+  preempt_disable();
   if ( cpu == smp_processor_id() ) {
     cpuid(reg, &data[0], &data[1], &data[2], &data[3]);
   } else {
@@ -70,6 +71,7 @@
     
     smp_call_function(cpuid_smp_cpuid, &cmd, 1, 1);
   }
+  preempt_enable();
 }
 #else /* ! CONFIG_SMP */
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/kernel/dmi_scan.c linux-2.4.27-leo/arch/i386/kernel/dmi_scan.c
--- linux-2.4.27/arch/i386/kernel/dmi_scan.c	2004-05-19 21:34:36.000000000 +0100
+++ linux-2.4.27-leo/arch/i386/kernel/dmi_scan.c	2004-09-20 21:34:47.000000000 +0100
@@ -15,6 +15,7 @@
 #include "pci-i386.h"
 
 unsigned long dmi_broken;
+int is_unsafe_smbus;
 int is_sony_vaio_laptop;
 
 struct dmi_header
@@ -371,6 +372,19 @@
 }		
 
 /*
+ * Don't access SMBus on IBM systems which get corrupted eeproms 
+ */
+
+static __init int disable_smbus(struct dmi_blacklist *d)
+{   
+	if (is_unsafe_smbus == 0) {
+		is_unsafe_smbus = 1;
+		printk(KERN_INFO "%s machine detected. Disabling SMBus accesses.\n", d->ident);
+	}
+	return 0;
+}
+
+/*
  * Check for a Sony Vaio system
  *
  * On a Sony system we want to enable the use of the sonypi
@@ -675,6 +689,10 @@
 			NO_MATCH, NO_MATCH,
 			} },
 
+	{ disable_smbus, "IBM", {
+			MATCH(DMI_SYS_VENDOR, "IBM"),
+			NO_MATCH, NO_MATCH, NO_MATCH
+			} },
 	{ sony_vaio_laptop, "Sony Vaio", { /* This is a Sony Vaio laptop */
 			MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
 			MATCH(DMI_PRODUCT_NAME, "PCG-"),
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/kernel/entry.S linux-2.4.27-leo/arch/i386/kernel/entry.S
--- linux-2.4.27/arch/i386/kernel/entry.S	2003-06-13 15:51:29.000000000 +0100
+++ linux-2.4.27-leo/arch/i386/kernel/entry.S	2004-09-17 03:19:57.000000000 +0100
@@ -73,7 +73,7 @@
  * these are offsets into the task-struct.
  */
 state		=  0
-flags		=  4
+preempt_count	=  4
 sigpending	=  8
 addr_limit	= 12
 exec_domain	= 16
@@ -81,8 +81,28 @@
 tsk_ptrace	= 24
 processor	= 52
 
+/* These are offsets into the irq_stat structure
+ * There is one per cpu and it is aligned to 32
+ * byte boundry (we put that here as a shift count)
+ */
+irq_array_shift                 = CONFIG_X86_L1_CACHE_SHIFT
+
+irq_stat_local_irq_count        = 4
+irq_stat_local_bh_count         = 8
+
 ENOSYS = 38
 
+#ifdef CONFIG_SMP
+#define GET_CPU_INDX	movl processor(%ebx),%eax;  \
+                        shll $irq_array_shift,%eax
+#define GET_CURRENT_CPU_INDX GET_CURRENT(%ebx); \
+                             GET_CPU_INDX
+#define CPU_INDX (,%eax)
+#else
+#define GET_CPU_INDX
+#define GET_CURRENT_CPU_INDX GET_CURRENT(%ebx)
+#define CPU_INDX
+#endif
 
 #define SAVE_ALL \
 	cld; \
@@ -209,6 +229,17 @@
 	jae badsys
 	call *SYMBOL_NAME(sys_call_table)(,%eax,4)
 	movl %eax,EAX(%esp)		# save the return value
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDKSTACK
+	cli                             # need_resched and signals atomic test
+	cmpl $0,need_resched(%ebx)
+	jne reschedule
+	cmpl $0,sigpending(%ebx)
+	jne signal_return
+	call SYMBOL_NAME(pax_randomize_kstack)
+	jmp restore_all
+#endif
+
 ENTRY(ret_from_sys_call)
 	cli				# need_resched and signals atomic test
 	cmpl $0,need_resched(%ebx)
@@ -255,12 +286,30 @@
 	ALIGN
 ENTRY(ret_from_intr)
 	GET_CURRENT(%ebx)
+#ifdef CONFIG_PREEMPT
+	cli
+	decl preempt_count(%ebx)
+#endif
 ret_from_exception:
 	movl EFLAGS(%esp),%eax		# mix EFLAGS and CS
 	movb CS(%esp),%al
 	testl $(VM_MASK | 3),%eax	# return to VM86 mode or non-supervisor?
 	jne ret_from_sys_call
+#ifdef CONFIG_PREEMPT
+	cmpl $0,preempt_count(%ebx)
+	jnz restore_all
+	cmpl $0,need_resched(%ebx)
+	jz restore_all
+	movl SYMBOL_NAME(irq_stat)+irq_stat_local_bh_count CPU_INDX,%ecx
+	addl SYMBOL_NAME(irq_stat)+irq_stat_local_irq_count CPU_INDX,%ecx
+	jnz restore_all
+	incl preempt_count(%ebx)
+	sti
+	call SYMBOL_NAME(preempt_schedule)
+	jmp ret_from_intr
+#else
 	jmp restore_all
+#endif
 
 	ALIGN
 reschedule:
@@ -297,6 +346,9 @@
 	GET_CURRENT(%ebx)
 	call *%edi
 	addl $8,%esp
+#ifdef CONFIG_PREEMPT
+	cli
+#endif
 	jmp ret_from_exception
 
 ENTRY(coprocessor_error)
@@ -316,12 +368,18 @@
 	movl %cr0,%eax
 	testl $0x4,%eax			# EM (math emulation bit)
 	jne device_not_available_emulate
+#ifdef CONFIG_PREEMPT
+	cli
+#endif
 	call SYMBOL_NAME(math_state_restore)
 	jmp ret_from_exception
 device_not_available_emulate:
 	pushl $0		# temporary storage for ORIG_EIP
 	call  SYMBOL_NAME(math_emulate)
 	addl $4,%esp
+#ifdef CONFIG_PREEMPT
+	cli
+#endif
 	jmp ret_from_exception
 
 ENTRY(debug)
@@ -389,8 +447,56 @@
 	jmp error_code
 
 ENTRY(page_fault)
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+	ALIGN
+	pushl $ SYMBOL_NAME(pax_do_page_fault)
+#else
 	pushl $ SYMBOL_NAME(do_page_fault)
+#endif
+
+#ifndef CONFIG_GRKERNSEC_PAX_EMUTRAMP
 	jmp error_code
+#else
+	pushl %ds
+	pushl %eax
+	xorl %eax,%eax
+	pushl %ebp
+	pushl %edi
+	pushl %esi
+	pushl %edx
+	decl %eax			# eax = -1
+	pushl %ecx
+	pushl %ebx
+	cld
+	movl %es,%ecx
+	movl ORIG_EAX(%esp), %esi	# get the error code
+	movl ES(%esp), %edi		# get the function address
+	movl %eax, ORIG_EAX(%esp)
+	movl %ecx, ES(%esp)
+	movl %esp,%edx
+	pushl %esi			# push the error code
+	pushl %edx			# push the pt_regs pointer
+	movl $(__KERNEL_DS),%edx
+	movl %edx,%ds
+	movl %edx,%es
+	GET_CURRENT(%ebx)
+	call *%edi
+	addl $8,%esp
+	decl %eax
+	jnz ret_from_exception
+
+	popl %ebx
+	popl %ecx
+	popl %edx
+	popl %esi
+	popl %edi
+	popl %ebp
+	popl %eax
+	popl %ds
+	popl %es
+	addl $4,%esp
+	jmp system_call
+#endif
 
 ENTRY(machine_check)
 	pushl $0
@@ -402,7 +508,7 @@
 	pushl $ SYMBOL_NAME(do_spurious_interrupt_bug)
 	jmp error_code
 
-.data
+.section .rodata, "a",@progbits
 ENTRY(sys_call_table)
 	.long SYMBOL_NAME(sys_ni_syscall)	/* 0  -  old "setup()" system call*/
 	.long SYMBOL_NAME(sys_exit)
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/kernel/head.S linux-2.4.27-leo/arch/i386/kernel/head.S
--- linux-2.4.27/arch/i386/kernel/head.S	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.27-leo/arch/i386/kernel/head.S	2004-09-17 03:19:57.000000000 +0100
@@ -37,10 +37,17 @@
 #define X86_VENDOR_ID	CPU_PARAMS+36	/* tied to NCAPINTS in cpufeature.h */
 
 /*
+ * Real beginning of normal "text" segment
+ */
+ENTRY(stext)
+ENTRY(_stext)
+
+/*
  * swapper_pg_dir is the main page directory, address 0x00101000
  *
  * On entry, %esi points to the real-mode code as a 32-bit pointer.
  */
+.global startup_32
 startup_32:
 /*
  * Set segments to known values
@@ -86,7 +93,7 @@
 				   PRESENT+RW+USER */
 2:	stosl
 	add $0x1000,%eax
-	cmp $empty_zero_page-__PAGE_OFFSET,%edi
+	cmp $0x00c00007,%eax
 	jne 2b
 
 /*
@@ -100,9 +107,19 @@
 	movl %eax,%cr0		/* ..and set paging (PG) bit */
 	jmp 1f			/* flush the prefetch-queue */
 1:
+
+#if !defined(CONFIG_GRKERNSEC_PAX_KERNEXEC) || defined(CONFIG_SMP)
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	orw  %bx,%bx
+	jz  1f
+#endif
+
 	movl $1f,%eax
 	jmp *%eax		/* make sure eip is relocated */
 1:
+#endif
+
 	/* Set up the stack pointer */
 	lss stack_start,%esp
 
@@ -121,7 +138,7 @@
  */
 	xorl %eax,%eax
 	movl $ SYMBOL_NAME(__bss_start),%edi
-	movl $ SYMBOL_NAME(_end),%ecx
+	movl $ SYMBOL_NAME(__bss_end),%ecx
 	subl %edi,%ecx
 	rep
 	stosb
@@ -272,8 +289,6 @@
 	jmp L6			# main should never return here, but
 				# just in case, we know what happens.
 
-ready:	.byte 0
-
 /*
  * We depend on ET to be correct. This checks for 287/387.
  */
@@ -319,13 +334,6 @@
 	jne rp_sidt
 	ret
 
-ENTRY(stack_start)
-	.long SYMBOL_NAME(init_task_union)+8192
-	.long __KERNEL_DS
-
-/* This is the default interrupt "handler" :-) */
-int_msg:
-	.asciz "Unknown interrupt\n"
 	ALIGN
 ignore_int:
 	cld
@@ -347,6 +355,18 @@
 	popl %eax
 	iret
 
+.data
+ready:	.byte 0
+
+ENTRY(stack_start)
+	.long SYMBOL_NAME(init_task_union)+8192
+	.long __KERNEL_DS
+
+.section .rodata,"a"
+/* This is the default interrupt "handler" :-) */
+int_msg:
+	.asciz "Unknown interrupt\n"
+
 /*
  * The interrupt descriptor table has room for 256 idt's,
  * the global descriptor table is dependent on the number
@@ -372,54 +392,77 @@
 SYMBOL_NAME(gdt):
 	.long SYMBOL_NAME(gdt_table)
 
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+.globl SYMBOL_NAME(gdt2)
+	.word 0
+gdt_descr2:
+	.word GDT_ENTRIES*8-1
+SYMBOL_NAME(gdt2):
+	.long SYMBOL_NAME(gdt_table2)
+#endif
+
 /*
  * This is initialized to create an identity-mapping at 0-8M (for bootup
  * purposes) and another mapping of the 0-8M area at virtual address
  * PAGE_OFFSET.
  */
-.org 0x1000
+.section .data.swapper_pg_dir,"a",@progbits
 ENTRY(swapper_pg_dir)
-	.long 0x00102007
-	.long 0x00103007
-	.fill BOOT_USER_PGD_PTRS-2,4,0
+	.long pg0-__PAGE_OFFSET+7
+	.long pg1-__PAGE_OFFSET+7
+	.long pg2-__PAGE_OFFSET+7
+	.fill BOOT_USER_PGD_PTRS-3,4,0
 	/* default: 766 entries */
-	.long 0x00102007
-	.long 0x00103007
+	.long pg0-__PAGE_OFFSET+7
+	.long pg1-__PAGE_OFFSET+7
+	.long pg2-__PAGE_OFFSET+7
 	/* default: 254 entries */
-	.fill BOOT_KERNEL_PGD_PTRS-2,4,0
+	.fill BOOT_KERNEL_PGD_PTRS-3,4,0
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+ENTRY(kernexec_pg_dir)
+	.fill 1024,4,0
+#endif
 
 /*
  * The page tables are initialized to only 8MB here - the final page
  * tables are set up later depending on memory size.
  */
-.org 0x2000
+.section .data.pg0,"a",@progbits
 ENTRY(pg0)
+	.fill 1024,4,0
 
-.org 0x3000
+.section .data.pg1,"a",@progbits
 ENTRY(pg1)
+	.fill 1024,4,0
+
+.section .data.pg2,"a",@progbits
+ENTRY(pg2)
+	.fill 1024,4,0
 
 /*
  * empty_zero_page must immediately follow the page tables ! (The
  * initialization loop counts until empty_zero_page)
  */
-
-.org 0x4000
+.section .rodata.empty_zero_page,"a",@progbits
 ENTRY(empty_zero_page)
-
-.org 0x5000
+	.fill 1024,4,0
 
 /*
- * Real beginning of normal "text" segment
+ * The IDT has to be page-aligned to simplify the Pentium
+ * F0 0F bug workaround.  We have a special link segment
+ * for this.
  */
-ENTRY(stext)
-ENTRY(_stext)
+.section .rodata.idt,"a",@progbits
+ENTRY(idt_table)
+	.fill 256,8,0
 
 /*
  * This starts the data section. Note that the above is all
  * in the text section because it has alignment requirements
  * that we cannot fulfill any other way.
  */
-.data
+.section .rodata,"a",@progbits
 
 ALIGN
 /*
@@ -430,19 +473,61 @@
  */
 ENTRY(gdt_table)
 	.quad 0x0000000000000000	/* NULL descriptor */
-	.quad 0x0000000000000000	/* not used */
-	.quad 0x00cf9a000000ffff	/* 0x10 kernel 4GB code at 0x00000000 */
-	.quad 0x00cf92000000ffff	/* 0x18 kernel 4GB data at 0x00000000 */
-	.quad 0x00cffa000000ffff	/* 0x23 user   4GB code at 0x00000000 */
-	.quad 0x00cff2000000ffff	/* 0x2b user   4GB data at 0x00000000 */
+
+#if defined(CONFIG_GRKERNSEC_PAX_KERNEXEC) && defined(CONFIG_PCI_BIOS)
+	.quad 0x00cf9b000000ffff        /* 0x08 kernel 4GB code at 0x00000000 */
+#else
+	.quad 0x0000000000000000        /* not used */
+#endif
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	.quad 0xc0cf9b400000ffff        /* 0x10 kernel 4GB code at 0xc0400000 */
+#else
+	.quad 0x00cf9b000000ffff        /* 0x10 kernel 4GB code at 0x00000000 */
+#endif
+
+	.quad 0x00cf93000000ffff	/* 0x18 kernel 4GB data at 0x00000000 */
+	.quad 0x00cffb000000ffff	/* 0x23 user   4GB code at 0x00000000 */
+	.quad 0x00cff3000000ffff	/* 0x2b user   4GB data at 0x00000000 */
+	.quad 0x0000000000000000        /* not used */
+	.quad 0x0000000000000000        /* not used */
+	/*
+	 * The APM segments have byte granularity and their bases
+	 * and limits are set at run time.
+	 */
+	.quad 0x0040930000000000        /* 0x40 APM set up for bad BIOS's */
+	.quad 0x00409b0000000000        /* 0x48 APM CS    code */
+	.quad 0x00009b0000000000        /* 0x50 APM CS 16 code (16 bit) */
+	.quad 0x0040930000000000        /* 0x58 APM DS    data */
+	.fill NR_CPUS*4,8,0             /* space for TSS's and LDT's */
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+ENTRY(gdt_table2)
+	.quad 0x0000000000000000        /* NULL descriptor */
+
+#if defined(CONFIG_GRKERNSEC_PAX_KERNEXEC) && defined(CONFIG_PCI_BIOS)
+	.quad 0x00cf9b000000ffff        /* 0x08 kernel 4GB code at 0x00000000 */
+#else
+	.quad 0x0000000000000000        /* not used */
+#endif
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	.quad 0xc0cf9b400000ffff        /* 0x10 kernel 4GB code at 0xc0400000 */
+#else
+	.quad 0x00cf9b000000ffff        /* 0x10 kernel 4GB code at 0x00000000 */
+#endif
+
+	.quad 0x00cf93000000ffff        /* 0x18 kernel 4GB data at 0x00000000 */
+	.quad 0x60c5fb000000ffff        /* 0x23 user 1.5GB code at 0x60000000 */
+	.quad 0x00c5f3000000ffff        /* 0x2b user 1.5GB data at 0x00000000 */
+
 	.quad 0x0000000000000000	/* not used */
 	.quad 0x0000000000000000	/* not used */
 	/*
 	 * The APM segments have byte granularity and their bases
 	 * and limits are set at run time.
 	 */
-	.quad 0x0040920000000000	/* 0x40 APM set up for bad BIOS's */
-	.quad 0x00409a0000000000	/* 0x48 APM CS    code */
-	.quad 0x00009a0000000000	/* 0x50 APM CS 16 code (16 bit) */
-	.quad 0x0040920000000000	/* 0x58 APM DS    data */
+	.quad 0x0040930000000000	/* 0x40 APM set up for bad BIOS's */
+	.quad 0x00409b0000000000	/* 0x48 APM CS    code */
+	.quad 0x00009b0000000000	/* 0x50 APM CS 16 code (16 bit) */
+	.quad 0x0040930000000000	/* 0x58 APM DS    data */
 	.fill NR_CPUS*4,8,0		/* space for TSS's and LDT's */
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/kernel/i386_ksyms.c linux-2.4.27-leo/arch/i386/kernel/i386_ksyms.c
--- linux-2.4.27/arch/i386/kernel/i386_ksyms.c	2004-05-19 21:34:36.000000000 +0100
+++ linux-2.4.27-leo/arch/i386/kernel/i386_ksyms.c	2004-09-20 21:34:47.000000000 +0100
@@ -74,6 +74,9 @@
 EXPORT_SYMBOL(get_cmos_time);
 EXPORT_SYMBOL(apm_info);
 EXPORT_SYMBOL(gdt);
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+EXPORT_SYMBOL(gdt2);
+#endif
 EXPORT_SYMBOL(empty_zero_page);
 
 #ifdef CONFIG_DEBUG_IOVIRT
@@ -179,6 +182,9 @@
 EXPORT_SYMBOL(atomic_dec_and_lock);
 #endif
 
+extern int is_unsafe_smbus;
+EXPORT_SYMBOL(is_unsafe_smbus);
+
 extern int is_sony_vaio_laptop;
 EXPORT_SYMBOL(is_sony_vaio_laptop);
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/kernel/i387.c linux-2.4.27-leo/arch/i386/kernel/i387.c
--- linux-2.4.27/arch/i386/kernel/i387.c	2003-08-25 12:44:39.000000000 +0100
+++ linux-2.4.27-leo/arch/i386/kernel/i387.c	2004-09-17 03:19:36.000000000 +0100
@@ -10,6 +10,7 @@
 
 #include <linux/config.h>
 #include <linux/sched.h>
+#include <linux/spinlock.h>
 #include <linux/init.h>
 #include <asm/processor.h>
 #include <asm/i387.h>
@@ -89,6 +90,8 @@
 {
 	struct task_struct *tsk = current;
 
+	preempt_disable();
+	
 	if (tsk->flags & PF_USEDFPU) {
 		__save_init_fpu(tsk);
 		return;
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/kernel/init_task.c linux-2.4.27-leo/arch/i386/kernel/init_task.c
--- linux-2.4.27/arch/i386/kernel/init_task.c	2001-09-17 23:29:09.000000000 +0100
+++ linux-2.4.27-leo/arch/i386/kernel/init_task.c	2004-09-17 03:19:57.000000000 +0100
@@ -29,5 +29,9 @@
  * section. Since TSS's are completely CPU-local, we want them
  * on exact cacheline boundaries, to eliminate cacheline ping-pong.
  */ 
-struct tss_struct init_tss[NR_CPUS] __cacheline_aligned = { [0 ... NR_CPUS-1] = INIT_TSS };
 
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+struct tss_struct init_tss[NR_CPUS] __attribute__((__aligned__(SMP_CACHE_BYTES), __section__(".rodata"))) = { [0 ... NR_CPUS-1] = INIT_TSS };
+#else
+struct tss_struct init_tss[NR_CPUS] __cacheline_aligned = { [0 ... NR_CPUS-1] = INIT_TSS };
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/kernel/ioport.c linux-2.4.27-leo/arch/i386/kernel/ioport.c
--- linux-2.4.27/arch/i386/kernel/ioport.c	2003-06-13 15:51:29.000000000 +0100
+++ linux-2.4.27-leo/arch/i386/kernel/ioport.c	2004-09-17 03:19:57.000000000 +0100
@@ -14,6 +14,8 @@
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
 #include <linux/stddef.h>
+#include <linux/grsecurity.h>
+#include <asm/desc.h>
 
 /* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */
 static void set_bitmap(unsigned long *bitmap, short base, short extent, int new_value)
@@ -55,17 +57,31 @@
 asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on)
 {
 	struct thread_struct * t = &current->thread;
-	struct tss_struct * tss = init_tss + smp_processor_id();
+	struct tss_struct * tss;
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	unsigned long flags, cr3;
+#endif
 
 	if ((from + num <= from) || (from + num > IO_BITMAP_SIZE*32))
 		return -EINVAL;
+#ifdef CONFIG_GRKERNSEC_IO
+	if (turn_on) {
+		gr_handle_ioperm();
+#else
 	if (turn_on && !capable(CAP_SYS_RAWIO))
+#endif
 		return -EPERM;
+#ifdef CONFIG_GRKERNSEC_IO
+	}
+#endif
 	/*
 	 * If it's the first ioperm() call in this thread's lifetime, set the
 	 * IO bitmap up. ioperm() is much less timing critical than clone(),
 	 * this is why we delay this operation until now:
 	 */
+	preempt_disable();
+	tss = init_tss + smp_processor_id();
 	if (!t->ioperm) {
 		/*
 		 * just in case ...
@@ -78,12 +94,22 @@
 	 * do it in the per-thread copy and in the TSS ...
 	 */
 	set_bitmap(t->io_bitmap, from, num, !turn_on);
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	pax_open_kernel(flags, cr3);
+#endif
+
 	if (tss->bitmap == IO_BITMAP_OFFSET) { /* already active? */
 		set_bitmap(tss->io_bitmap, from, num, !turn_on);
 	} else {
 		memcpy(tss->io_bitmap, t->io_bitmap, IO_BITMAP_BYTES);
 		tss->bitmap = IO_BITMAP_OFFSET; /* Activate it in the TSS */
 	}
+	preempt_enable();
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	pax_close_kernel(flags, cr3);
+#endif
 
 	return 0;
 }
@@ -109,8 +135,13 @@
 		return -EINVAL;
 	/* Trying to gain more privileges? */
 	if (level > old) {
+#ifdef CONFIG_GRKERNSEC_IO
+		gr_handle_iopl();
+		return -EPERM;
+#else
 		if (!capable(CAP_SYS_RAWIO))
 			return -EPERM;
+#endif
 	}
 	regs->eflags = (regs->eflags & 0xffffcfff) | (level << 12);
 	return 0;
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/kernel/irq.c linux-2.4.27-leo/arch/i386/kernel/irq.c
--- linux-2.4.27/arch/i386/kernel/irq.c	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.27-leo/arch/i386/kernel/irq.c	2004-09-17 03:19:36.000000000 +0100
@@ -284,9 +284,11 @@
 				show("wait_on_irq");
 				count = ~0;
 			}
+			preempt_disable();
 			__sti();
 			SYNC_OTHER_CORES(cpu);
 			__cli();
+			preempt_enable_no_resched();
 			if (irqs_running())
 				continue;
 			if (global_irq_lock)
@@ -360,8 +362,9 @@
 
 	__save_flags(flags);
 	if (flags & (1 << EFLAGS_IF_SHIFT)) {
-		int cpu = smp_processor_id();
+		int cpu;
 		__cli();
+		cpu = smp_processor_id();
 		if (!local_irq_count(cpu))
 			get_irqlock(cpu);
 	}
@@ -369,11 +372,14 @@
 
 void __global_sti(void)
 {
-	int cpu = smp_processor_id();
+	int cpu;
 
+	preempt_disable();
+	cpu = smp_processor_id();
 	if (!local_irq_count(cpu))
 		release_irqlock(cpu);
 	__sti();
+	preempt_enable();
 }
 
 /*
@@ -388,13 +394,15 @@
 	int retval;
 	int local_enabled;
 	unsigned long flags;
-	int cpu = smp_processor_id();
+	int cpu;
 
 	__save_flags(flags);
 	local_enabled = (flags >> EFLAGS_IF_SHIFT) & 1;
 	/* default to local */
 	retval = 2 + local_enabled;
 
+	preempt_disable();
+	cpu = smp_processor_id();
 	/* check for global flags if we're not in an interrupt */
 	if (!local_irq_count(cpu)) {
 		if (local_enabled)
@@ -402,6 +410,7 @@
 		if (global_irq_holder == cpu)
 			retval = 0;
 	}
+	preempt_enable();
 	return retval;
 }
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/kernel/ldt.c linux-2.4.27-leo/arch/i386/kernel/ldt.c
--- linux-2.4.27/arch/i386/kernel/ldt.c	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.27-leo/arch/i386/kernel/ldt.c	2004-09-17 03:19:57.000000000 +0100
@@ -151,7 +151,7 @@
 {
 	int err;
 	unsigned long size;
-	void *address;
+	const void *address;
 
 	err = 0;
 	address = &default_ldt[0];
@@ -214,6 +214,13 @@
 		}
 	}
 
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	if ((current->flags & PF_PAX_SEGMEXEC) && (ldt_info.contents & 2)) {
+		error = -EINVAL;
+		goto out_unlock;
+	}
+#endif
+
 	entry_1 = ((ldt_info.base_addr & 0x0000ffff) << 16) |
 		  (ldt_info.limit & 0x0ffff);
 	entry_2 = (ldt_info.base_addr & 0xff000000) |
@@ -224,7 +231,7 @@
 		  ((ldt_info.seg_not_present ^ 1) << 15) |
 		  (ldt_info.seg_32bit << 22) |
 		  (ldt_info.limit_in_pages << 23) |
-		  0x7000;
+		  0x7100;
 	if (!oldmode)
 		entry_2 |= (ldt_info.useable << 20);
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/kernel/microcode.c linux-2.4.27-leo/arch/i386/kernel/microcode.c
--- linux-2.4.27/arch/i386/kernel/microcode.c	2004-05-19 21:34:36.000000000 +0100
+++ linux-2.4.27-leo/arch/i386/kernel/microcode.c	2004-09-17 03:19:36.000000000 +0100
@@ -397,15 +397,18 @@
 {
 	int i, error;
 
+	preempt_disable();
 	if (smp_call_function(collect_cpu_info, NULL, 1, 1) != 0) {
 		printk(KERN_ERR "microcode: Error! Could not run on all processors\n");
 		error = -EIO;
+		preempt_enable();
 		goto out;
 	}
 	collect_cpu_info(NULL);
 
 	if ((error = find_matching_ucodes())) {
 		printk(KERN_ERR "microcode: Error in the microcode data\n");
+		preempt_enable();
 		goto out_free;
 	}
 
@@ -414,6 +417,7 @@
 		error = -EIO;
 	}
 	do_update_one(NULL);
+	preempt_enable();
 
 out_free:
 	for (i = 0; i < smp_num_cpus; i++) {
@@ -428,6 +432,7 @@
 		}
 	}
 out:
+
 	return error;
 }
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/kernel/msr.c linux-2.4.27-leo/arch/i386/kernel/msr.c
--- linux-2.4.27/arch/i386/kernel/msr.c	2001-10-11 17:04:57.000000000 +0100
+++ linux-2.4.27-leo/arch/i386/kernel/msr.c	2004-09-17 03:19:36.000000000 +0100
@@ -114,8 +114,9 @@
 {
   struct msr_command cmd;
 
+  preempt_disable();
   if ( cpu == smp_processor_id() ) {
-    return wrmsr_eio(reg, eax, edx);
+    cmd.err = wrmsr_eio(reg, eax, edx);
   } else {
     cmd.cpu = cpu;
     cmd.reg = reg;
@@ -123,16 +124,19 @@
     cmd.data[1] = edx;
     
     smp_call_function(msr_smp_wrmsr, &cmd, 1, 1);
-    return cmd.err;
   }
+
+  preempt_enable();
+  return cmd.err;
 }
 
 static inline int do_rdmsr(int cpu, u32 reg, u32 *eax, u32 *edx)
 {
   struct msr_command cmd;
 
+  preempt_disable();
   if ( cpu == smp_processor_id() ) {
-    return rdmsr_eio(reg, eax, edx);
+    cmd.err = rdmsr_eio(reg, eax, edx);
   } else {
     cmd.cpu = cpu;
     cmd.reg = reg;
@@ -141,9 +145,10 @@
     
     *eax = cmd.data[0];
     *edx = cmd.data[1];
-
-    return cmd.err;
   }
+
+  preempt_enable();
+  return cmd.err;
 }
 
 #else /* ! CONFIG_SMP */
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/kernel/mtrr.c linux-2.4.27-leo/arch/i386/kernel/mtrr.c
--- linux-2.4.27/arch/i386/kernel/mtrr.c	2004-09-17 02:38:42.000000000 +0100
+++ linux-2.4.27-leo/arch/i386/kernel/mtrr.c	2004-09-17 03:19:36.000000000 +0100
@@ -1065,6 +1065,9 @@
     wait_barrier_execute = TRUE;
     wait_barrier_cache_enable = TRUE;
     atomic_set (&undone_count, smp_num_cpus - 1);
+
+    preempt_disable();
+
     /*  Start the ball rolling on other CPUs  */
     if (smp_call_function (ipi_handler, &data, 1, 0) != 0)
 	panic ("mtrr: timed out waiting for other CPUs\n");
@@ -1090,6 +1093,9 @@
 	then enable the local cache and return  */
     wait_barrier_cache_enable = FALSE;
     set_mtrr_done (&ctxt);
+
+    preempt_enable();
+
 }   /*  End Function set_mtrr_smp  */
 
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/kernel/pci-pc.c linux-2.4.27-leo/arch/i386/kernel/pci-pc.c
--- linux-2.4.27/arch/i386/kernel/pci-pc.c	2004-09-17 02:38:42.000000000 +0100
+++ linux-2.4.27-leo/arch/i386/kernel/pci-pc.c	2004-09-17 03:19:57.000000000 +0100
@@ -17,6 +17,7 @@
 #include <asm/io.h>
 #include <asm/smp.h>
 #include <asm/smpboot.h>
+#include <asm/desc.h>
 
 #include "pci-i386.h"
 
@@ -575,10 +576,16 @@
  * the array there.
  */
 
+#if defined(CONFIG_GRKERNSEC_PAX_KERNEXEC) && defined(CONFIG_PCI_BIOS)
+#define __FLAT_KERNEL_CS 0x08
+#else
+#define __FLAT_KERNEL_CS __KERNEL_CS
+#endif
+
 static struct {
 	unsigned long address;
 	unsigned short segment;
-} bios32_indirect = { 0, __KERNEL_CS };
+} bios32_indirect = { 0, __FLAT_KERNEL_CS };
 
 /*
  * Returns the entry point for the given service, NULL on error
@@ -619,7 +626,9 @@
 static struct {
 	unsigned long address;
 	unsigned short segment;
-} pci_indirect = { 0, __KERNEL_CS };
+} pci_indirect = { 0, __FLAT_KERNEL_CS };
+
+#undef __FLAT_KERNEL_CS
 
 static int pci_bios_present;
 
@@ -1490,6 +1499,7 @@
 	if ((pci_probe & PCI_BIOS_SORT) && !(pci_probe & PCI_NO_SORT))
 		pcibios_sort();
 #endif
+
 }
 
 char * __devinit  pcibios_setup(char *str)
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/kernel/process.c linux-2.4.27-leo/arch/i386/kernel/process.c
--- linux-2.4.27/arch/i386/kernel/process.c	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.27-leo/arch/i386/kernel/process.c	2004-09-17 03:19:57.000000000 +0100
@@ -209,18 +209,18 @@
    doesn't work with at least one type of 486 motherboard.  It is easy
    to stop this code working; hence the copious comments. */
 
-static unsigned long long
+static const unsigned long long
 real_mode_gdt_entries [3] =
 {
 	0x0000000000000000ULL,	/* Null descriptor */
-	0x00009a000000ffffULL,	/* 16-bit real-mode 64k code at 0x00000000 */
-	0x000092000100ffffULL	/* 16-bit real-mode 64k data at 0x00000100 */
+	0x00009b000000ffffULL,	/* 16-bit real-mode 64k code at 0x00000000 */
+	0x000093000100ffffULL	/* 16-bit real-mode 64k data at 0x00000100 */
 };
 
 static struct
 {
 	unsigned short       size __attribute__ ((packed));
-	unsigned long long * base __attribute__ ((packed));
+	const unsigned long long * base __attribute__ ((packed));
 }
 real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, real_mode_gdt_entries },
 real_mode_idt = { 0x3ff, 0 },
@@ -552,7 +552,7 @@
 {
 	struct pt_regs * childregs;
 
-	childregs = ((struct pt_regs *) (THREAD_SIZE + (unsigned long) p)) - 1;
+	childregs = ((struct pt_regs *) (THREAD_SIZE + (unsigned long) p - sizeof(unsigned long))) - 1;
 	struct_cpy(childregs, regs);
 	childregs->eax = 0;
 	childregs->esp = esp;
@@ -613,6 +613,16 @@
 	dump->u_fpvalid = dump_fpu (regs, &dump->i387);
 }
 
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+void pax_switch_segments(struct task_struct * tsk)
+{
+	if (tsk->flags & PF_PAX_SEGMEXEC)
+		__asm__ __volatile__("lgdt %0": "=m" (gdt_descr2));
+	else
+		__asm__ __volatile__("lgdt %0": "=m" (gdt_descr));
+}
+#endif
+
 /*
  * This special macro can be used to load a debugging register
  */
@@ -650,12 +660,15 @@
 				 *next = &next_p->thread;
 	struct tss_struct *tss = init_tss + smp_processor_id();
 
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	unsigned long flags, cr3;
+#endif
+
 	unlazy_fpu(prev_p);
 
-	/*
-	 * Reload esp0, LDT and the page table pointer:
-	 */
-	tss->esp0 = next->esp0;
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	pax_switch_segments(next_p);
+#endif
 
 	/*
 	 * Save away %fs and %gs. No need to save %es and %ds, as
@@ -683,6 +696,15 @@
 		loaddebug(next, 7);
 	}
 
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	pax_open_kernel(flags, cr3);
+#endif
+
+	/*
+	 * Reload esp0, LDT and the page table pointer:
+	 */
+	tss->esp0 = next->esp0;
+
 	if (prev->ioperm || next->ioperm) {
 		if (next->ioperm) {
 			/*
@@ -705,6 +727,11 @@
 			 */
 			tss->bitmap = INVALID_IO_BITMAP_OFFSET;
 	}
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	pax_close_kernel(flags, cr3);
+#endif
+
 }
 
 asmlinkage int sys_fork(struct pt_regs regs)
@@ -792,3 +819,43 @@
 }
 #undef last_sched
 #undef first_sched
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDKSTACK
+asmlinkage void pax_randomize_kstack(void)
+{
+	struct tss_struct *tss = init_tss + smp_processor_id();
+	unsigned long time;
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	unsigned long flags, cr3;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_SOFTMODE
+	if (!pax_aslr)
+		return;
+#endif
+
+	rdtscl(time);
+
+	/* P4 seems to return a 0 LSB, ignore it */
+#ifdef CONFIG_MPENTIUM4
+	time &= 0x3EUL;
+	time <<= 1;
+#else
+	time &= 0x1FUL;
+	time <<= 2;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	pax_open_kernel(flags, cr3);
+#endif
+
+	tss->esp0 ^= time;
+	current->thread.esp0 = tss->esp0;
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	pax_close_kernel(flags, cr3);
+#endif
+
+}
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/kernel/ptrace.c linux-2.4.27-leo/arch/i386/kernel/ptrace.c
--- linux-2.4.27/arch/i386/kernel/ptrace.c	2002-08-03 01:39:42.000000000 +0100
+++ linux-2.4.27-leo/arch/i386/kernel/ptrace.c	2004-09-17 03:19:57.000000000 +0100
@@ -13,6 +13,7 @@
 #include <linux/errno.h>
 #include <linux/ptrace.h>
 #include <linux/user.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
@@ -177,6 +178,9 @@
 	if (pid == 1)		/* you may not mess with init */
 		goto out_tsk;
 
+	if(gr_handle_ptrace(child, request))
+		goto out_tsk;
+
 	if (request == PTRACE_ATTACH) {
 		ret = ptrace_attach(child);
 		goto out_tsk;
@@ -256,6 +260,17 @@
 			  if(addr < (long) &dummy->u_debugreg[4] &&
 			     ((unsigned long) data) >= TASK_SIZE-3) break;
 			  
+#ifdef CONFIG_GRKERNSEC
+			  if(addr >= (long) &dummy->u_debugreg[0] &&
+			     addr <= (long) &dummy->u_debugreg[3]){
+				  long reg   = (addr - (long) &dummy->u_debugreg[0]) >> 2;
+				  long type  = (child->thread.debugreg[7] >> (DR_CONTROL_SHIFT + 4*reg)) & 3;
+				  long align = (child->thread.debugreg[7] >> (DR_CONTROL_SHIFT + 2 + 4*reg)) & 3;
+				  if((type & 1) && (data & align))
+					break;
+			  }
+#endif
+
 			  if(addr == (long) &dummy->u_debugreg[7]) {
 				  data &= ~DR_CONTROL_RESERVED;
 				  for(i=0; i<4; i++)
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/kernel/setup.c linux-2.4.27-leo/arch/i386/kernel/setup.c
--- linux-2.4.27/arch/i386/kernel/setup.c	2004-09-17 02:38:42.000000000 +0100
+++ linux-2.4.27-leo/arch/i386/kernel/setup.c	2004-09-17 03:19:57.000000000 +0100
@@ -170,7 +170,7 @@
 extern void mcheck_init(struct cpuinfo_x86 *c);
 extern void dmi_scan_machine(void);
 extern int root_mountflags;
-extern char _text, _etext, _edata, _end;
+extern char _text, _etext, _data, _edata, _end;
 
 static int have_cpuid_p(void) __init;
 
@@ -1215,7 +1215,7 @@
 
 	code_resource.start = virt_to_bus(&_text);
 	code_resource.end = virt_to_bus(&_etext)-1;
-	data_resource.start = virt_to_bus(&_etext);
+	data_resource.start = virt_to_bus(&_data);
 	data_resource.end = virt_to_bus(&_edata)-1;
 
 	parse_cmdline_early(cmdline_p);
@@ -3221,7 +3221,7 @@
 	set_tss_desc(nr,t);
 	gdt_table[__TSS(nr)].b &= 0xfffffdff;
 	load_TR(nr);
-	load_LDT(&init_mm.context);
+	_load_LDT(&init_mm.context);
 
 	/*
 	 * Clear all 6 debug registers:
@@ -3287,7 +3287,16 @@
 	printk(KERN_INFO "Your Pentium Pro seems ok.\n");
 	return 0;
 }
-	
+
+#ifdef CONFIG_GRKERNSEC_PAX_SOFTMODE
+static int __init setup_pax_softmode(char *str)
+{
+	get_option (&str, &pax_softmode);
+	return 1;
+}
+__setup("pax_softmode=", setup_pax_softmode);
+#endif
+
 /*
  * Local Variables:
  * mode:c
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/kernel/smp.c linux-2.4.27-leo/arch/i386/kernel/smp.c
--- linux-2.4.27/arch/i386/kernel/smp.c	2003-06-13 15:51:29.000000000 +0100
+++ linux-2.4.27-leo/arch/i386/kernel/smp.c	2004-09-17 03:19:36.000000000 +0100
@@ -360,10 +360,14 @@
 
 asmlinkage void smp_invalidate_interrupt (void)
 {
-	unsigned long cpu = smp_processor_id();
+	unsigned long cpu;
+
+	preempt_disable();
+
+	cpu = smp_processor_id();
 
 	if (!test_bit(cpu, &flush_cpumask))
-		return;
+		goto out;
 		/* 
 		 * This was a BUG() but until someone can quote me the
 		 * line from the intel manual that guarantees an IPI to
@@ -384,6 +388,8 @@
 	}
 	ack_APIC_irq();
 	clear_bit(cpu, &flush_cpumask);
+out:
+	preempt_enable();
 }
 
 static void flush_tlb_others (unsigned long cpumask, struct mm_struct *mm,
@@ -433,17 +439,22 @@
 void flush_tlb_current_task(void)
 {
 	struct mm_struct *mm = current->mm;
-	unsigned long cpu_mask = mm->cpu_vm_mask & ~(1 << smp_processor_id());
+	unsigned long cpu_mask;
 
+	preempt_disable();
+	cpu_mask = mm->cpu_vm_mask & ~(1UL << smp_processor_id());
 	local_flush_tlb();
 	if (cpu_mask)
 		flush_tlb_others(cpu_mask, mm, FLUSH_ALL);
+	preempt_enable();
 }
 
 void flush_tlb_mm (struct mm_struct * mm)
 {
-	unsigned long cpu_mask = mm->cpu_vm_mask & ~(1 << smp_processor_id());
+	unsigned long cpu_mask;
 
+	preempt_disable();
+	cpu_mask = mm->cpu_vm_mask & ~(1UL << smp_processor_id());
 	if (current->active_mm == mm) {
 		if (current->mm)
 			local_flush_tlb();
@@ -452,13 +463,16 @@
 	}
 	if (cpu_mask)
 		flush_tlb_others(cpu_mask, mm, FLUSH_ALL);
+	preempt_enable();
 }
 
 void flush_tlb_page(struct vm_area_struct * vma, unsigned long va)
 {
 	struct mm_struct *mm = vma->vm_mm;
-	unsigned long cpu_mask = mm->cpu_vm_mask & ~(1 << smp_processor_id());
+	unsigned long cpu_mask;
 
+	preempt_disable();
+	cpu_mask = mm->cpu_vm_mask & ~(1UL << smp_processor_id());
 	if (current->active_mm == mm) {
 		if(current->mm)
 			__flush_tlb_one(va);
@@ -468,6 +482,7 @@
 
 	if (cpu_mask)
 		flush_tlb_others(cpu_mask, mm, va);
+	preempt_enable();
 }
 
 static inline void do_flush_tlb_all_local(void)
@@ -486,9 +501,11 @@
 
 void flush_tlb_all(void)
 {
+	preempt_disable();
 	smp_call_function (flush_tlb_all_ipi,0,1,1);
 
 	do_flush_tlb_all_local();
+	preempt_enable();
 }
 
 /*
@@ -572,7 +589,7 @@
 static void stop_this_cpu (void * dummy)
 {
 	/*
-	 * Remove this CPU:
+	 * Remove this CPU: assumes preemption is disabled
 	 */
 	clear_bit(smp_processor_id(), &cpu_online_map);
 	__cli();
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/kernel/sys_i386.c linux-2.4.27-leo/arch/i386/kernel/sys_i386.c
--- linux-2.4.27/arch/i386/kernel/sys_i386.c	2003-08-25 12:44:39.000000000 +0100
+++ linux-2.4.27-leo/arch/i386/kernel/sys_i386.c	2004-09-17 03:19:57.000000000 +0100
@@ -18,6 +18,7 @@
 #include <linux/mman.h>
 #include <linux/file.h>
 #include <linux/utsname.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 #include <asm/ipc.h>
@@ -48,6 +49,11 @@
 	int error = -EBADF;
 	struct file * file = NULL;
 
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+	if (flags & MAP_MIRROR)
+		return -EINVAL;
+#endif
+
 	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 	if (!(flags & MAP_ANONYMOUS)) {
 		file = fget(fd);
@@ -55,6 +61,12 @@
 			goto out;
 	}
 
+	if(gr_handle_mmap(file, prot)) {
+		fput(file);
+		error = -EACCES;
+		goto out;
+	}
+
 	down_write(&current->mm->mmap_sem);
 	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
 	up_write(&current->mm->mmap_sem);
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/kernel/trampoline.S linux-2.4.27-leo/arch/i386/kernel/trampoline.S
--- linux-2.4.27/arch/i386/kernel/trampoline.S	2002-11-28 23:53:09.000000000 +0000
+++ linux-2.4.27-leo/arch/i386/kernel/trampoline.S	2004-09-17 03:19:57.000000000 +0100
@@ -54,7 +54,7 @@
 	lmsw	%ax		# into protected mode
 	jmp	flush_instr
 flush_instr:
-	ljmpl	$__KERNEL_CS, $0x00100000
+	ljmpl	$__KERNEL_CS, $SYMBOL_NAME(startup_32)-__PAGE_OFFSET
 			# jump to startup_32 in arch/i386/kernel/head.S
 
 idt_48:
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/kernel/traps.c linux-2.4.27-leo/arch/i386/kernel/traps.c
--- linux-2.4.27/arch/i386/kernel/traps.c	2002-11-28 23:53:09.000000000 +0000
+++ linux-2.4.27-leo/arch/i386/kernel/traps.c	2004-09-17 03:19:57.000000000 +0100
@@ -54,15 +54,10 @@
 asmlinkage void lcall7(void);
 asmlinkage void lcall27(void);
 
-struct desc_struct default_ldt[] = { { 0, 0 }, { 0, 0 }, { 0, 0 },
+const struct desc_struct default_ldt[] = { { 0, 0 }, { 0, 0 }, { 0, 0 },
 		{ 0, 0 }, { 0, 0 } };
 
-/*
- * The IDT has to be page-aligned to simplify the Pentium
- * F0 0F bug workaround.. We have a special link segment
- * for this.
- */
-struct desc_struct idt_table[256] __attribute__((__section__(".data.idt"))) = { {0, 0}, };
+extern struct desc_struct idt_table[256];
 
 asmlinkage void divide_error(void);
 asmlinkage void debug(void);
@@ -228,14 +223,23 @@
 		show_stack((unsigned long*)esp);
 
 		printk("\nCode: ");
+
+#ifndef CONFIG_GRKERNSEC_PAX_KERNEXEC
 		if(regs->eip < PAGE_OFFSET)
 			goto bad;
+#endif
 
 		for(i=0;i<20;i++)
 		{
 			unsigned char c;
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+			if(__get_user(c, &((unsigned char*)regs->eip)[i+__KERNEL_TEXT_OFFSET])) {
+#else
 			if(__get_user(c, &((unsigned char*)regs->eip)[i])) {
 bad:
+#endif
+
 				printk(" Bad EIP value.");
 				break;
 			}
@@ -258,8 +262,13 @@
 
 	eip = regs->eip;
 
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	eip += __KERNEL_TEXT_OFFSET;
+#else
 	if (eip < PAGE_OFFSET)
 		goto no_bug;
+#endif
+
 	if (__get_user(ud2, (unsigned short *)eip))
 		goto no_bug;
 	if (ud2 != 0x0b0f)
@@ -267,7 +276,13 @@
 	if (__get_user(line, (unsigned short *)(eip + 2)))
 		goto bug;
 	if (__get_user(file, (char **)(eip + 4)) ||
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+		__get_user(c, file + __KERNEL_TEXT_OFFSET))
+#else
 		(unsigned long)file < PAGE_OFFSET || __get_user(c, file))
+#endif
+
 		file = "<bad filename>";
 
 	printk("kernel BUG at %s:%d!\n", file, line);
@@ -422,6 +437,13 @@
 			regs->eip = fixup;
 			return;
 		}
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+		if ((regs->xcs & 0xFFFF) == __KERNEL_CS)
+				die("PAX: suspicious general protection fault", regs, error_code);
+		else
+#endif
+
 		die("general protection fault", regs, error_code);
 	}
 }
@@ -527,13 +549,12 @@
 {
 	unsigned int condition;
 	struct task_struct *tsk = current;
-	unsigned long eip = regs->eip;
 	siginfo_t info;
 
 	__asm__ __volatile__("movl %%db6,%0" : "=r" (condition));
 
 	/* If the user set TF, it's simplest to clear it right away. */
-	if ((eip >=PAGE_OFFSET) && (regs->eflags & TF_MASK))
+	if (!(regs->xcs & 3) && (regs->eflags & TF_MASK) && !(regs->eflags & VM_MASK))
 		goto clear_TF;
 
 	/* Mask out spurious debug traps due to lazy DR7 setting */
@@ -751,6 +772,8 @@
  *
  * Careful.. There are problems with IBM-designed IRQ13 behaviour.
  * Don't touch unless you *really* know how it works.
+ *
+ * Must be called with kernel preemption disabled.
  */
 asmlinkage void math_state_restore(struct pt_regs regs)
 {
@@ -826,7 +849,7 @@
 	_set_gate(idt_table+n,15,3,addr);
 }
 
-static void __init set_call_gate(void *a, void *addr)
+static void __init set_call_gate(const void *a, void *addr)
 {
 	_set_gate(a,12,3,addr);
 }
@@ -852,14 +875,45 @@
 	"rorl $16,%%eax" \
 	: "=m"(*(n)) : "a" (addr), "r"(n), "ir"(limit), "i"(type))
 
-void set_tss_desc(unsigned int n, void *addr)
+void set_tss_desc(unsigned int n, const void *addr)
 {
 	_set_tssldt_desc(gdt_table+__TSS(n), (int)addr, 235, 0x89);
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	_set_tssldt_desc(gdt_table2+__TSS(n), (int)addr, 235, 0x89);
+#endif
+
 }
 
-void set_ldt_desc(unsigned int n, void *addr, unsigned int size)
+void __set_ldt_desc(unsigned int n, const void *addr, unsigned int size)
 {
 	_set_tssldt_desc(gdt_table+__LDT(n), (int)addr, ((size << 3)-1), 0x82);
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	_set_tssldt_desc(gdt_table2+__LDT(n), (int)addr, ((size << 3)-1), 0x82);
+#endif
+
+}
+
+void set_ldt_desc(unsigned int n, const void *addr, unsigned int size)
+{
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	unsigned long flags, cr3;
+
+	pax_open_kernel(flags, cr3);
+#endif
+
+	_set_tssldt_desc(gdt_table+__LDT(n), (int)addr, ((size << 3)-1), 0x82);
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	_set_tssldt_desc(gdt_table2+__LDT(n), (int)addr, ((size << 3)-1), 0x82);
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	pax_close_kernel(flags, cr3);
+#endif
+
 }
 
 #ifdef CONFIG_X86_VISWS_APIC
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/kernel/vm86.c linux-2.4.27-leo/arch/i386/kernel/vm86.c
--- linux-2.4.27/arch/i386/kernel/vm86.c	2003-08-25 12:44:39.000000000 +0100
+++ linux-2.4.27-leo/arch/i386/kernel/vm86.c	2004-09-17 03:19:57.000000000 +0100
@@ -44,6 +44,7 @@
 #include <asm/pgalloc.h>
 #include <asm/io.h>
 #include <asm/irq.h>
+#include <asm/desc.h>
 
 /*
  * Known problems:
@@ -97,6 +98,10 @@
 	struct pt_regs *ret;
 	unsigned long tmp;
 
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	unsigned long flags, cr3;
+#endif
+
 	if (!current->thread.vm86_info) {
 		printk("no vm86_info: BAD\n");
 		do_exit(SIGSEGV);
@@ -111,7 +116,17 @@
 		do_exit(SIGSEGV);
 	}
 	tss = init_tss + smp_processor_id();
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	pax_open_kernel(flags, cr3);
+#endif
+
 	tss->esp0 = current->thread.esp0 = current->thread.saved_esp0;
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	pax_close_kernel(flags, cr3);
+#endif
+
 	current->thread.saved_esp0 = 0;
 	ret = KVM86->regs32;
 	return ret;
@@ -237,6 +252,11 @@
 static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk)
 {
 	struct tss_struct *tss;
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	unsigned long flags, cr3;
+#endif
+
 /*
  * make sure the vm86() system call doesn't try to do anything silly
  */
@@ -278,8 +298,17 @@
 	info->regs32->eax = 0;
 	tsk->thread.saved_esp0 = tsk->thread.esp0;
 	tss = init_tss + smp_processor_id();
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	pax_open_kernel(flags, cr3);
+#endif
+
 	tss->esp0 = tsk->thread.esp0 = (unsigned long) &info->VM86_TSS_ESP0;
 
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	pax_close_kernel(flags, cr3);
+#endif
+
 	tsk->thread.screen_bitmap = info->screen_bitmap;
 	if (info->flags & VM86_SCREEN_BITMAP)
 		mark_screen_rdonly(tsk);
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/lib/dec_and_lock.c linux-2.4.27-leo/arch/i386/lib/dec_and_lock.c
--- linux-2.4.27/arch/i386/lib/dec_and_lock.c	2000-07-08 02:20:16.000000000 +0100
+++ linux-2.4.27-leo/arch/i386/lib/dec_and_lock.c	2004-09-17 03:19:36.000000000 +0100
@@ -8,6 +8,7 @@
  */
 
 #include <linux/spinlock.h>
+#include <linux/sched.h>
 #include <asm/atomic.h>
 
 int atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock)
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/Makefile linux-2.4.27-leo/arch/i386/Makefile
--- linux-2.4.27/arch/i386/Makefile	2003-06-13 15:51:29.000000000 +0100
+++ linux-2.4.27-leo/arch/i386/Makefile	2004-09-17 03:19:57.000000000 +0100
@@ -114,6 +114,9 @@
 
 MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
 
+arch/i386/vmlinux.lds: arch/i386/vmlinux.lds.S FORCE
+	$(CPP) -C -P -I$(HPATH) -D__KERNEL__ -imacros $(HPATH)/linux/config.h -imacros $(HPATH)/asm-i386/segment.h -imacros $(HPATH)/asm-i386/page.h -Ui386 arch/i386/vmlinux.lds.S >arch/i386/vmlinux.lds
+
 vmlinux: arch/i386/vmlinux.lds
 
 FORCE: ;
@@ -150,6 +153,7 @@
 	@$(MAKEBOOT) clean
 
 archmrproper:
+	rm -f arch/i386/vmlinux.lds
 
 archdep:
 	@$(MAKEBOOT) dep
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/mm/fault.c linux-2.4.27-leo/arch/i386/mm/fault.c
--- linux-2.4.27/arch/i386/mm/fault.c	2004-09-17 02:38:42.000000000 +0100
+++ linux-2.4.27-leo/arch/i386/mm/fault.c	2004-09-17 03:19:57.000000000 +0100
@@ -19,6 +19,8 @@
 #include <linux/init.h>
 #include <linux/tty.h>
 #include <linux/vt_kern.h>		/* For unblank_screen() */
+#include <linux/unistd.h>
+#include <linux/compiler.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -127,6 +129,10 @@
 asmlinkage void do_invalid_op(struct pt_regs *, unsigned long);
 extern unsigned long idt;
 
+#if defined(CONFIG_GRKERNSEC_PAX_PAGEEXEC) || defined(CONFIG_GRKERNSEC_PAX_EMUTRAMP) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+static int pax_handle_fetch_fault(struct pt_regs *regs);
+#endif
+
 /*
  * This routine handles page faults.  It determines the address,
  * and the problem, and then passes it off to one of the appropriate
@@ -137,23 +143,31 @@
  *	bit 1 == 0 means read, 1 means write
  *	bit 2 == 0 means kernel, 1 means user-mode
  */
-asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+static int do_page_fault(struct pt_regs *regs, unsigned long error_code, unsigned long address)
+#else
+asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long error_code)
+#endif
 {
 	struct task_struct *tsk;
 	struct mm_struct *mm;
 	struct vm_area_struct * vma;
+#ifndef CONFIG_GRKERNSEC_PAX_PAGEEXEC
 	unsigned long address;
+#endif
 	unsigned long page;
 	unsigned long fixup;
 	int write;
 	siginfo_t info;
 
+#ifndef CONFIG_GRKERNSEC_PAX_PAGEEXEC
 	/* get the address */
 	__asm__("movl %%cr2,%0":"=r" (address));
 
 	/* It's safe to allow irq's after cr2 has been saved */
 	if (regs->eflags & X86_EFLAGS_IF)
 		local_irq_enable();
+#endif
 
 	tsk = current;
 
@@ -258,7 +272,7 @@
 			tsk->thread.screen_bitmap |= 1 << bit;
 	}
 	up_read(&mm->mmap_sem);
-	return;
+	return 0;
 
 /*
  * Something tried to access memory that isn't in our memory map..
@@ -269,6 +283,37 @@
 
 	/* User mode accesses just cause a SIGSEGV */
 	if (error_code & 4) {
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+		if (current->flags & PF_PAX_SEGMEXEC) {
+
+#if defined(CONFIG_GRKERNSEC_PAX_EMUTRAMP) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+		if ((error_code == 4) && (regs->eip + SEGMEXEC_TASK_SIZE == address)) {
+			switch (pax_handle_fetch_fault(regs)) {
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+			case 5:
+				return 0;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUTRAMP
+			case 4:
+				return 0;
+			case 3:
+			case 2:
+				return 1;
+#endif
+
+			}
+		}
+#endif
+
+			if (address >= SEGMEXEC_TASK_SIZE) {
+				pax_report_fault(regs, (void*)regs->eip, (void*)regs->esp);
+				do_exit(SIGKILL);
+			}
+		}
+#endif
+
 		tsk->thread.cr2 = address;
 		/* Kernel addresses are always protection faults */
 		tsk->thread.error_code = error_code | (address >= TASK_SIZE);
@@ -278,7 +323,7 @@
 		/* info.si_code has been set above */
 		info.si_addr = (void *)address;
 		force_sig_info(SIGSEGV, &info, tsk);
-		return;
+		return 0;
 	}
 
 	/*
@@ -291,7 +336,7 @@
 
 		if (nr == 6) {
 			do_invalid_op(regs, 0);
-			return;
+			return 0;
 		}
 	}
 
@@ -299,7 +344,7 @@
 	/* Are we prepared to handle this kernel fault?  */
 	if ((fixup = search_exception_table(regs->eip)) != 0) {
 		regs->eip = fixup;
-		return;
+		return 0;
 	}
 
 /*
@@ -311,6 +356,18 @@
 
 	if (address < PAGE_SIZE)
 		printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	else if (init_mm.start_code + __KERNEL_TEXT_OFFSET <= address && address < init_mm.end_code + __KERNEL_TEXT_OFFSET) {
+		if (tsk->curr_ip)
+			printk(KERN_ERR "PAX: From %u.%u.%u.%u: %s:%d, uid/euid: %u/%u, attempted to modify kernel code",
+					 NIPQUAD(tsk->curr_ip), tsk->comm, tsk->pid, tsk->uid, tsk->euid);
+		else
+			printk(KERN_ERR "PAX: %s:%d, uid/euid: %u/%u, attempted to modify kernel code",
+					 tsk->comm, tsk->pid, tsk->uid, tsk->euid);
+	}
+#endif
+
 	else
 		printk(KERN_ALERT "Unable to handle kernel paging request");
 	printk(" at virtual address %08lx\n",address);
@@ -363,7 +420,7 @@
 	/* Kernel mode? Handle exceptions or die */
 	if (!(error_code & 4))
 		goto no_context;
-	return;
+	return 0;
 
 vmalloc_fault:
 	{
@@ -396,6 +453,448 @@
 		pte_k = pte_offset(pmd_k, address);
 		if (!pte_present(*pte_k))
 			goto no_context;
-		return;
+		return 0;
 	}
 }
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+/* PaX: called with the page_table_lock spinlock held */
+static inline pte_t * pax_get_pte(struct mm_struct *mm, unsigned long address)
+{
+	pgd_t *pgd;
+	pmd_t *pmd;
+
+	pgd = pgd_offset(mm, address);
+	if (!pgd || !pgd_present(*pgd))
+		return 0;
+	pmd = pmd_offset(pgd, address);
+	if (!pmd || !pmd_present(*pmd))
+		return 0;
+	return pte_offset(pmd, address);
+}
+#endif
+
+/*
+ * PaX: decide what to do with offenders (regs->eip = fault address)
+ *
+ * returns 1 when task should be killed
+ *         2 when sigreturn trampoline was detected
+ *         3 when rt_sigreturn trampoline was detected
+ *         4 when gcc trampoline was detected
+ *	   5 when legitimate ET_EXEC was detected
+ */
+#if defined(CONFIG_GRKERNSEC_PAX_PAGEEXEC) || defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC)
+static int pax_handle_fetch_fault(struct pt_regs *regs)
+{
+#ifdef CONFIG_GRKERNSEC_PAX_EMUTRAMP
+	static const unsigned char trans[8] = {6, 1, 2, 0, 13, 5, 3, 4};
+#endif
+	int err;
+	
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (current->flags & PF_PAX_RANDEXEC) {
+		unsigned long esp_4;
+		if (regs->eip >= current->mm->start_code &&
+		    regs->eip < current->mm->end_code)
+		{
+			err = get_user(esp_4, (unsigned long*)(regs->esp-4UL));
+			if (err || esp_4 == regs->eip)
+				return 1;
+			regs->eip += current->mm->delta_exec;
+			return 5;
+		}
+	}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUTRAMP
+
+#ifndef CONFIG_GRKERNSEC_PAX_EMUSIGRT
+	if (!(current->flags & PF_PAX_EMUTRAMP))
+		return 1;
+#endif
+
+	do { /* PaX: sigreturn emulation */
+		unsigned char pop, mov;
+		unsigned short sys;
+		unsigned long nr;
+
+		err = get_user(pop, (unsigned char *)(regs->eip));
+		err |= get_user(mov, (unsigned char *)(regs->eip + 1));
+		err |= get_user(nr, (unsigned long *)(regs->eip + 2));
+		err |= get_user(sys, (unsigned short *)(regs->eip + 6));
+
+		if (err)
+			break;
+
+		if (pop == 0x58 &&
+		    mov == 0xb8 &&
+		    nr == __NR_sigreturn &&
+		    sys == 0x80cd)
+		{
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUSIGRT
+			int sig;
+			struct k_sigaction *ka;
+			__sighandler_t handler;
+
+			if (get_user(sig, (int *)regs->esp))
+				return 1;
+			if (sig < 1 || sig > _NSIG || sig == SIGKILL || sig == SIGSTOP)
+				return 1;
+			ka = &current->sig->action[sig-1];
+			handler = ka->sa.sa_handler;
+			if (handler == SIG_DFL || handler == SIG_IGN) {
+				if (!(current->flags & PF_PAX_EMUTRAMP))
+					return 1;
+			} else if (ka->sa.sa_flags & SA_SIGINFO)
+				return 1;
+#endif
+
+			regs->esp += 4;
+			regs->eax = nr;
+			regs->eip += 8;
+			return 2;
+		}
+	} while (0);
+
+	do { /* PaX: rt_sigreturn emulation */
+		unsigned char mov;
+		unsigned short sys;
+		unsigned long nr;
+
+		err = get_user(mov, (unsigned char *)(regs->eip));
+		err |= get_user(nr, (unsigned long *)(regs->eip + 1));
+		err |= get_user(sys, (unsigned short *)(regs->eip + 5));
+
+		if (err)
+			break;
+
+		if (mov == 0xb8 &&
+		    nr == __NR_rt_sigreturn &&
+		    sys == 0x80cd)
+		{
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUSIGRT
+			int sig;
+			struct k_sigaction *ka;
+			__sighandler_t handler;
+
+			if (get_user(sig, (int *)regs->esp))
+				return 1;
+			if (sig < 1 || sig > _NSIG || sig == SIGKILL || sig == SIGSTOP)
+				return 1;
+			ka = &current->sig->action[sig-1];
+			handler = ka->sa.sa_handler;
+			if (handler == SIG_DFL || handler == SIG_IGN) {
+				if (!(current->flags & PF_PAX_EMUTRAMP))
+					return 1;
+			} else if (!(ka->sa.sa_flags & SA_SIGINFO))
+				return 1;
+#endif
+
+			regs->eax = nr;
+			regs->eip += 7;
+			return 3;
+		}
+	} while (0);
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUSIGRT
+	if (!(current->flags & PF_PAX_EMUTRAMP))
+		return 1;
+#endif
+
+	do { /* PaX: gcc trampoline emulation #1 */
+		unsigned char mov1, mov2;
+		unsigned short jmp;
+		unsigned long addr1, addr2, ret;
+		unsigned short call;
+
+		err = get_user(mov1, (unsigned char *)regs->eip);
+		err |= get_user(addr1, (unsigned long *)(regs->eip + 1));
+		err |= get_user(mov2, (unsigned char *)(regs->eip + 5));
+		err |= get_user(addr2, (unsigned long *)(regs->eip + 6));
+		err |= get_user(jmp, (unsigned short *)(regs->eip + 10));
+		err |= get_user(ret, (unsigned long *)regs->esp);
+
+		if (err)
+			break;
+
+		err = get_user(call, (unsigned short *)(ret-2));
+		if (err)
+			break;
+
+		if ((mov1 & 0xF8) == 0xB8 &&
+		    (mov2 & 0xF8) == 0xB8 &&
+		    (mov1 & 0x07) != (mov2 & 0x07) &&
+		    (jmp & 0xF8FF) == 0xE0FF &&
+		    (mov2 & 0x07) == ((jmp>>8) & 0x07) &&
+		    (call & 0xF8FF) == 0xD0FF &&
+		    regs->eip == ((unsigned long*)regs)[trans[(call>>8) & 0x07]])
+		{
+			((unsigned long *)regs)[trans[mov1 & 0x07]] = addr1;
+			((unsigned long *)regs)[trans[mov2 & 0x07]] = addr2;
+			regs->eip = addr2;
+			return 4;
+		}
+	} while (0);
+
+	do { /* PaX: gcc trampoline emulation #2 */
+		unsigned char mov, jmp;
+		unsigned long addr1, addr2, ret;
+		unsigned short call;
+
+		err = get_user(mov, (unsigned char *)regs->eip);
+		err |= get_user(addr1, (unsigned long *)(regs->eip + 1));
+		err |= get_user(jmp, (unsigned char *)(regs->eip + 5));
+		err |= get_user(addr2, (unsigned long *)(regs->eip + 6));
+		err |= get_user(ret, (unsigned long *)regs->esp);
+
+		if (err)
+			break;
+
+		err = get_user(call, (unsigned short *)(ret-2));
+		if (err)
+			break;
+
+		if ((mov & 0xF8) == 0xB8 &&
+		    jmp == 0xE9 &&
+		    (call & 0xF8FF) == 0xD0FF &&
+		    regs->eip == ((unsigned long*)regs)[trans[(call>>8) & 0x07]])
+		{
+			((unsigned long *)regs)[trans[mov & 0x07]] = addr1;
+			regs->eip += addr2 + 10;
+			return 4;
+		}
+	} while (0);
+
+	do { /* PaX: gcc trampoline emulation #3 */
+		unsigned char mov, jmp;
+		char offset;
+		unsigned long addr1, addr2, ret;
+		unsigned short call;
+
+		err = get_user(mov, (unsigned char *)regs->eip);
+		err |= get_user(addr1, (unsigned long *)(regs->eip + 1));
+		err |= get_user(jmp, (unsigned char *)(regs->eip + 5));
+		err |= get_user(addr2, (unsigned long *)(regs->eip + 6));
+		err |= get_user(ret, (unsigned long *)regs->esp);
+
+		if (err)
+			break;
+
+		err = get_user(call, (unsigned short *)(ret-3));
+		err |= get_user(offset, (char *)(ret-1));
+		if (err)
+			break;
+
+		if ((mov & 0xF8) == 0xB8 &&
+		    jmp == 0xE9 &&
+		    call == 0x55FF)
+		{
+			unsigned long addr;
+
+			err = get_user(addr, (unsigned long*)(regs->ebp + (unsigned long)(long)offset));
+			if (err || regs->eip != addr)
+				break;
+
+			((unsigned long *)regs)[trans[mov & 0x07]] = addr1;
+			regs->eip += addr2 + 10;
+			return 4;
+		}
+	} while (0);
+
+	do { /* PaX: gcc trampoline emulation #4 */
+		unsigned char mov, jmp, sib;
+		char offset;
+		unsigned long addr1, addr2, ret;
+		unsigned short call;
+
+		err = get_user(mov, (unsigned char *)regs->eip);
+		err |= get_user(addr1, (unsigned long *)(regs->eip + 1));
+		err |= get_user(jmp, (unsigned char *)(regs->eip + 5));
+		err |= get_user(addr2, (unsigned long *)(regs->eip + 6));
+		err |= get_user(ret, (unsigned long *)regs->esp);
+
+		if (err)
+			break;
+
+		err = get_user(call, (unsigned short *)(ret-4));
+		err |= get_user(sib, (unsigned char *)(ret-2));
+		err |= get_user(offset, (char *)(ret-1));
+		if (err)
+			break;
+
+		if ((mov & 0xF8) == 0xB8 &&
+		    jmp == 0xE9 &&
+		    call == 0x54FF &&
+		    sib == 0x24)
+		{
+			unsigned long addr;
+
+			err = get_user(addr, (unsigned long*)(regs->esp + 4 + (unsigned long)(long)offset));
+			if (err || regs->eip != addr)
+				break;
+
+			((unsigned long *)regs)[trans[mov & 0x07]] = addr1;
+			regs->eip += addr2 + 10;
+			return 4;
+		}
+	} while (0);
+
+	do { /* PaX: gcc trampoline emulation #5 */
+		unsigned char mov, jmp, sib;
+		unsigned long addr1, addr2, ret, offset;
+		unsigned short call;
+
+		err = get_user(mov, (unsigned char *)regs->eip);
+		err |= get_user(addr1, (unsigned long *)(regs->eip + 1));
+		err |= get_user(jmp, (unsigned char *)(regs->eip + 5));
+		err |= get_user(addr2, (unsigned long *)(regs->eip + 6));
+		err |= get_user(ret, (unsigned long *)regs->esp);
+
+		if (err)
+			break;
+
+		err = get_user(call, (unsigned short *)(ret-7));
+		err |= get_user(sib, (unsigned char *)(ret-5));
+		err |= get_user(offset, (unsigned long *)(ret-4));
+		if (err)
+			break;
+
+		if ((mov & 0xF8) == 0xB8 &&
+		    jmp == 0xE9 &&
+		    call == 0x94FF &&
+		    sib == 0x24)
+		{
+			unsigned long addr;
+
+			err = get_user(addr, (unsigned long*)(regs->esp + 4 + offset));
+			if (err || regs->eip != addr)
+				break;
+
+			((unsigned long *)regs)[trans[mov & 0x07]] = addr1;
+			regs->eip += addr2 + 10;
+			return 4;
+		}
+	} while (0);
+#endif
+
+	return 1; /* PaX in action */
+}
+#endif
+
+#if defined(CONFIG_GRKERNSEC_PAX_PAGEEXEC) || defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC)
+void pax_report_insns(void *pc)
+{
+	unsigned long i;
+
+	printk(KERN_ERR "PAX: bytes at PC: ");
+	for (i = 0; i < 20; i++) {
+		unsigned char c;
+		if (get_user(c, (unsigned char*)pc+i)) {
+			printk("<invalid address>.");
+			break;
+		}
+		printk("%02x ", c);
+	}
+	printk("\n");
+}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+/*
+ * PaX: handle the extra page faults or pass it down to the original handler
+ *
+ * returns 0 when nothing special was detected
+ *         1 when sigreturn trampoline (syscall) has to be emulated
+ */
+asmlinkage int pax_do_page_fault(struct pt_regs *regs, unsigned long error_code)
+{
+	struct mm_struct *mm = current->mm;
+	unsigned long address;
+	pte_t *pte;
+	unsigned char pte_mask;
+	int ret;
+
+	__asm__("movl %%cr2,%0":"=r" (address));
+
+	/* It's safe to allow irq's after cr2 has been saved */
+	if (likely(regs->eflags & X86_EFLAGS_IF))
+		local_irq_enable();
+
+	if (unlikely((error_code & 5) != 5 ||
+		     address >= TASK_SIZE ||
+		     !(current->flags & PF_PAX_PAGEEXEC)))
+		return do_page_fault(regs, error_code, address);
+
+	/* PaX: it's our fault, let's handle it if we can */
+
+	/* PaX: take a look at read faults before acquiring any locks */
+	if (unlikely((error_code == 5) && (regs->eip == address))) { 
+		/* instruction fetch attempt from a protected page in user mode */
+		ret = pax_handle_fetch_fault(regs);
+		switch (ret) {
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+		case 5:
+			return 0;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUTRAMP
+		case 4:
+			return 0;
+		case 3:
+		case 2:
+			return 1;
+#endif
+		}
+		pax_report_fault(regs, (void*)regs->eip, (void*)regs->esp);
+		do_exit(SIGKILL);
+	}
+
+	pte_mask = _PAGE_ACCESSED | _PAGE_USER | ((error_code & 2) << (_PAGE_BIT_DIRTY-1));
+
+	spin_lock(&mm->page_table_lock);
+	pte = pax_get_pte(mm, address);
+	if (unlikely(!pte || !(pte_val(*pte) & _PAGE_PRESENT) || pte_exec(*pte))) {
+		spin_unlock(&mm->page_table_lock);
+		do_page_fault(regs, error_code, address);
+		return 0;
+	}
+
+	if (unlikely((error_code == 7) && !pte_write(*pte))) {
+		/* write attempt to a protected page in user mode */
+		spin_unlock(&mm->page_table_lock);
+		do_page_fault(regs, error_code, address);
+		return 0;
+	}
+
+	/*
+	 * PaX: fill DTLB with user rights and retry
+	 */
+	__asm__ __volatile__ (
+		"orb %2,%1\n"
+#if defined(CONFIG_M586) || defined(CONFIG_M586TSC)
+/*   
+ * PaX: let this uncommented 'invlpg' remind us on the behaviour of Intel's   
+ * (and AMD's) TLBs. namely, they do not cache PTEs that would raise *any*
+ * page fault when examined during a TLB load attempt. this is true not only
+ * for PTEs holding a non-present entry but also present entries that will
+ * raise a page fault (such as those set up by PaX, or the copy-on-write
+ * mechanism). in effect it means that we do *not* need to flush the TLBs
+ * for our target pages since their PTEs are simply not in the TLBs at all.
+ * the best thing in omitting it is that we gain around 15-20% speed in the
+ * fast path of the page fault handler and can get rid of tracing since we
+ * can no longer flush unintended entries.
+ */
+
+		"invlpg %0\n"
+#endif
+
+		"testb $0,%0\n"
+		"xorb %3,%1\n"
+		:
+		: "m" (*(char*)address), "m" (*(char*)pte) , "q" (pte_mask) , "i" (_PAGE_USER)
+		: "memory", "cc");
+	spin_unlock(&mm->page_table_lock);
+	return 0;
+}
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/mm/init.c linux-2.4.27-leo/arch/i386/mm/init.c
--- linux-2.4.27/arch/i386/mm/init.c	2004-05-19 21:34:36.000000000 +0100
+++ linux-2.4.27-leo/arch/i386/mm/init.c	2004-09-17 03:19:57.000000000 +0100
@@ -37,6 +37,7 @@
 #include <asm/e820.h>
 #include <asm/apic.h>
 #include <asm/tlb.h>
+#include <asm/desc.h>
 
 mmu_gather_t mmu_gathers[NR_CPUS];
 unsigned long highstart_pfn, highend_pfn;
@@ -46,6 +47,7 @@
 int do_check_pgt_cache(int low, int high)
 {
 	int freed = 0;
+	preempt_disable();
 	if(pgtable_cache_size > high) {
 		do {
 			if (pgd_quicklist) {
@@ -62,6 +64,7 @@
 			}
 		} while(pgtable_cache_size > low);
 	}
+	preempt_enable();
 	return freed;
 }
 
@@ -122,7 +125,7 @@
 
 /* References to section boundaries */
 
-extern char _text, _etext, _edata, __bss_start, _end;
+extern char _text, _etext, _data, _edata, __bss_start, _end;
 extern char __init_begin, __init_end;
 
 static inline void set_pte_phys (unsigned long vaddr,
@@ -365,6 +368,10 @@
 
 	__flush_tlb_all();
 
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	memcpy(kernexec_pg_dir, swapper_pg_dir, sizeof(kernexec_pg_dir));
+#endif
+
 #ifdef CONFIG_HIGHMEM
 	kmap_init();
 #endif
@@ -529,7 +536,7 @@
 	reservedpages = free_pages_init();
 
 	codesize =  (unsigned long) &_etext - (unsigned long) &_text;
-	datasize =  (unsigned long) &_edata - (unsigned long) &_etext;
+	datasize =  (unsigned long) &_edata - (unsigned long) &_data;
 	initsize =  (unsigned long) &__init_end - (unsigned long) &__init_begin;
 
 	printk(KERN_INFO "Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init, %ldk highmem)\n",
@@ -597,6 +604,43 @@
 		totalram_pages++;
 	}
 	printk (KERN_INFO "Freeing unused kernel memory: %dk freed\n", (&__init_end - &__init_begin) >> 10);
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+	/* PaX: limit KERNEL_CS to actual size */
+	{
+		unsigned long limit;
+		pgd_t *pgd;
+		pmd_t *pmd;
+
+		limit = (unsigned long)&_etext >> PAGE_SHIFT;
+		gdt_table[2].a = (gdt_table[2].a & 0xFFFF0000UL) | (limit & 0x0FFFFUL);
+		gdt_table[2].b = (gdt_table[2].b & 0xFFF0FFFFUL) | (limit & 0xF0000UL);
+
+#ifdef CONFIG_PCI_BIOS
+		printk(KERN_INFO "PAX: warning, PCI BIOS might still be in use, keeping flat KERNEL_CS.\n");
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+		gdt_table2[2].a = (gdt_table2[2].a & 0xFFFF0000UL) | (limit & 0x0FFFFUL);
+		gdt_table2[2].b = (gdt_table2[2].b & 0xFFF0FFFFUL) | (limit & 0xF0000UL);
+#endif
+
+	/* PaX: make KERNEL_CS read-only */
+		for (addr = __KERNEL_TEXT_OFFSET; addr < __KERNEL_TEXT_OFFSET + 0x00400000UL; addr += (1UL << PMD_SHIFT)) {
+			pgd = pgd_offset_k(addr);
+			pmd = pmd_offset(pgd, addr);
+			set_pmd(pmd, __pmd(pmd_val(*pmd) & ~_PAGE_GLOBAL));
+		}
+		memcpy(kernexec_pg_dir, swapper_pg_dir, sizeof(kernexec_pg_dir));
+		for (addr = __KERNEL_TEXT_OFFSET; addr < __KERNEL_TEXT_OFFSET + 0x00400000UL; addr += (1UL << PMD_SHIFT)) {
+			pgd = pgd_offset_k(addr);
+			pmd = pmd_offset(pgd, addr);
+			set_pmd(pmd, __pmd(pmd_val(*pmd) & ~_PAGE_RW));
+		}
+		flush_tlb_all();
+	}
+#endif
+
 }
 
 #ifdef CONFIG_BLK_DEV_INITRD
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/mm/ioremap.c linux-2.4.27-leo/arch/i386/mm/ioremap.c
--- linux-2.4.27/arch/i386/mm/ioremap.c	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.27-leo/arch/i386/mm/ioremap.c	2004-09-17 03:19:57.000000000 +0100
@@ -49,7 +49,7 @@
 	if (address >= end)
 		BUG();
 	do {
-		pte_t * pte = pte_alloc(&init_mm, pmd, address);
+		pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address);
 		if (!pte)
 			return -ENOMEM;
 		remap_area_pte(pte, address, end - address, address + phys_addr, flags);
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/vmlinux.lds linux-2.4.27-leo/arch/i386/vmlinux.lds
--- linux-2.4.27/arch/i386/vmlinux.lds	2002-02-25 19:37:53.000000000 +0000
+++ linux-2.4.27-leo/arch/i386/vmlinux.lds	1970-01-01 01:00:00.000000000 +0100
@@ -1,82 +0,0 @@
-/* ld script to make i386 Linux kernel
- * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>;
- */
-OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
-OUTPUT_ARCH(i386)
-ENTRY(_start)
-SECTIONS
-{
-  . = 0xC0000000 + 0x100000;
-  _text = .;			/* Text and read-only data */
-  .text : {
-	*(.text)
-	*(.fixup)
-	*(.gnu.warning)
-	} = 0x9090
-
-  _etext = .;			/* End of text section */
-
-  .rodata : { *(.rodata) *(.rodata.*) }
-  .kstrtab : { *(.kstrtab) }
-
-  . = ALIGN(16);		/* Exception table */
-  __start___ex_table = .;
-  __ex_table : { *(__ex_table) }
-  __stop___ex_table = .;
-
-  __start___ksymtab = .;	/* Kernel symbol table */
-  __ksymtab : { *(__ksymtab) }
-  __stop___ksymtab = .;
-
-  .data : {			/* Data */
-	*(.data)
-	CONSTRUCTORS
-	}
-
-  _edata = .;			/* End of data section */
-
-  . = ALIGN(8192);		/* init_task */
-  .data.init_task : { *(.data.init_task) }
-
-  . = ALIGN(4096);		/* Init code and data */
-  __init_begin = .;
-  .text.init : { *(.text.init) }
-  .data.init : { *(.data.init) }
-  . = ALIGN(16);
-  __setup_start = .;
-  .setup.init : { *(.setup.init) }
-  __setup_end = .;
-  __initcall_start = .;
-  .initcall.init : { *(.initcall.init) }
-  __initcall_end = .;
-  . = ALIGN(4096);
-  __init_end = .;
-
-  . = ALIGN(4096);
-  .data.page_aligned : { *(.data.idt) }
-
-  . = ALIGN(32);
-  .data.cacheline_aligned : { *(.data.cacheline_aligned) }
-
-  __bss_start = .;		/* BSS */
-  .bss : {
-	*(.bss)
-	}
-  _end = . ;
-
-  /* Sections to be discarded */
-  /DISCARD/ : {
-	*(.text.exit)
-	*(.data.exit)
-	*(.exitcall.exit)
-	}
-
-  /* Stabs debugging sections.  */
-  .stab 0 : { *(.stab) }
-  .stabstr 0 : { *(.stabstr) }
-  .stab.excl 0 : { *(.stab.excl) }
-  .stab.exclstr 0 : { *(.stab.exclstr) }
-  .stab.index 0 : { *(.stab.index) }
-  .stab.indexstr 0 : { *(.stab.indexstr) }
-  .comment 0 : { *(.comment) }
-}
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/i386/vmlinux.lds.S linux-2.4.27-leo/arch/i386/vmlinux.lds.S
--- linux-2.4.27/arch/i386/vmlinux.lds.S	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/arch/i386/vmlinux.lds.S	2004-09-17 03:19:57.000000000 +0100
@@ -0,0 +1,125 @@
+/* ld script to make i386 Linux kernel
+ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>;
+ */
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+SECTIONS
+{
+  . = __PAGE_OFFSET + 0x100000;
+  .text.startup : {
+	BYTE(0xEA) /* jmp far */
+	LONG(startup_32 + __KERNEL_TEXT_OFFSET - __PAGE_OFFSET)
+	SHORT(__KERNEL_CS)
+	}
+
+  . = ALIGN(32);
+  _data = .;
+  .data : {			/* Data */
+	*(.data)
+	CONSTRUCTORS
+	}
+
+  . = ALIGN(32);
+  .data.cacheline_aligned : { *(.data.cacheline_aligned) }
+
+  . = ALIGN(8192);
+  .data.init_task : { *(.data.init_task) }
+
+  . = ALIGN(4096);
+  .data.page_aligned : {
+	*(.data.swapper_pg_dir)
+	*(.data.pg0)
+	*(.data.pg1)
+	*(.data.pg2)
+	}
+
+  _edata = .;			/* End of data section */
+
+  __bss_start = .;		/* BSS */
+  .bss : {
+	*(.bss)
+	LONG(0)
+	} 
+  __bss_end = . ;
+
+  . = ALIGN(4096);		/* Init code and data */
+  __init_begin = .;
+  .data.init : { *(.data.init) }
+  . = ALIGN(16);
+  __setup_start = .;
+  .setup.init : { *(.setup.init) }
+  __setup_end = .;
+  __initcall_start = .;
+  .initcall.init : { *(.initcall.init) }
+  __initcall_end = .;
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+  __text_init_start = .;
+  .text.init (. - __KERNEL_TEXT_OFFSET) : AT (__text_init_start) {
+	*(.text.init)
+	. = ALIGN(4*1024*1024) - 1;
+	BYTE(0)
+	}
+  __init_end = . + __KERNEL_TEXT_OFFSET;
+
+/*
+ * PaX: this must be kept in synch with the KERNEL_CS base
+ * in the GDTs in arch/i386/kernel/head.S
+ */
+  _text = .;			/* Text and read-only data */
+  .text : AT (. + __KERNEL_TEXT_OFFSET) {
+#else
+  .text.init : { *(.text.init) }
+  . = ALIGN(4096);
+  __init_end = .;
+  _text = .;			/* Text and read-only data */
+  .text : {
+#endif
+
+	*(.text)
+	*(.fixup)
+	*(.gnu.warning)
+	} = 0x9090
+
+  _etext = .;			/* End of text section */
+  . = ALIGN(4096);
+  . += __KERNEL_TEXT_OFFSET;
+  .rodata.page_aligned : {
+	*(.rodata.empty_zero_page)
+	*(.rodata.idt)
+	}
+  .rodata : { *(.rodata) *(.rodata.*) }
+  .kstrtab : { *(.kstrtab) }
+
+  . = ALIGN(16);		/* Exception table */
+  __start___ex_table = .;
+  __ex_table : { *(__ex_table) }
+  __stop___ex_table = .;
+
+  __start___ksymtab = .;	/* Kernel symbol table */
+  __ksymtab : { *(__ksymtab) }
+  __stop___ksymtab = .;
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+  _end = ALIGN(4*1024*1024);
+#else
+  _end = .;
+#endif
+
+  /* Sections to be discarded */
+  /DISCARD/ : {
+	*(.text.exit)
+	*(.data.exit)
+	*(.exitcall.exit)
+	}
+
+  /* Stabs debugging sections.  */
+  .stab 0 : { *(.stab) }
+  .stabstr 0 : { *(.stabstr) }
+  .stab.excl 0 : { *(.stab.excl) }
+  .stab.exclstr 0 : { *(.stab.exclstr) }
+  .stab.index 0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment 0 : { *(.comment) }
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/ia64/config.in linux-2.4.27-leo/arch/ia64/config.in
--- linux-2.4.27/arch/ia64/config.in	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.27-leo/arch/ia64/config.in	2004-09-17 03:19:58.000000000 +0100
@@ -319,3 +319,12 @@
 int 'Kernel messages buffer length shift (0 = default)' CONFIG_LOG_BUF_SHIFT 0
 
 endmenu
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+    source grsecurity/Config.in
+fi
+endmenu
+
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/ia64/kernel/ptrace.c linux-2.4.27-leo/arch/ia64/kernel/ptrace.c
--- linux-2.4.27/arch/ia64/kernel/ptrace.c	2004-05-19 21:34:36.000000000 +0100
+++ linux-2.4.27-leo/arch/ia64/kernel/ptrace.c	2004-09-17 03:19:58.000000000 +0100
@@ -16,6 +16,7 @@
 #include <linux/ptrace.h>
 #include <linux/smp_lock.h>
 #include <linux/user.h>
+#include <linux/grsecurity.h>
 
 #include <asm/pgtable.h>
 #include <asm/processor.h>
@@ -1299,6 +1300,9 @@
 	if (pid == 1)		/* no messing around with init! */
 		goto out_tsk;
 
+	if (gr_handle_ptrace(child, request))
+		goto out_tsk;
+
 	if (request == PTRACE_ATTACH) {
 		ret = ptrace_attach(child);
 		goto out_tsk;
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/ia64/kernel/sys_ia64.c linux-2.4.27-leo/arch/ia64/kernel/sys_ia64.c
--- linux-2.4.27/arch/ia64/kernel/sys_ia64.c	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.27-leo/arch/ia64/kernel/sys_ia64.c	2004-09-17 03:19:58.000000000 +0100
@@ -16,6 +16,7 @@
 #include <linux/smp_lock.h>
 #include <linux/highuid.h>
 #include <linux/hugetlb.h>
+#include <linux/grsecurity.h>
 
 #include <asm/shmparam.h>
 #include <asm/uaccess.h>
@@ -211,6 +212,11 @@
 		goto out;
 	}
 
+	if (gr_handle_mmap(file, prot)) {
+		addr = -EACCES;
+		goto out;
+	}
+
 	down_write(&current->mm->mmap_sem);
 	addr = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
 	up_write(&current->mm->mmap_sem);
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/m68k/config.in linux-2.4.27-leo/arch/m68k/config.in
--- linux-2.4.27/arch/m68k/config.in	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.27-leo/arch/m68k/config.in	2004-09-17 03:19:58.000000000 +0100
@@ -557,3 +557,11 @@
 
 source crypto/Config.in
 source lib/Config.in
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+	source grsecurity/Config.in
+fi
+endmenu
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/mips/config.in linux-2.4.27-leo/arch/mips/config.in
--- linux-2.4.27/arch/mips/config.in	2002-11-28 23:53:09.000000000 +0000
+++ linux-2.4.27-leo/arch/mips/config.in	2004-09-17 03:19:58.000000000 +0100
@@ -7,3 +7,11 @@
 define_bool CONFIG_MIPS64 n
 
 source arch/mips/config-shared.in
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+        source grsecurity/Config.in
+fi
+endmenu
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/mips/config-shared.in linux-2.4.27-leo/arch/mips/config-shared.in
--- linux-2.4.27/arch/mips/config-shared.in	2004-02-20 14:11:37.000000000 +0000
+++ linux-2.4.27-leo/arch/mips/config-shared.in	2004-09-17 03:19:36.000000000 +0100
@@ -937,6 +937,7 @@
    define_bool CONFIG_HOTPLUG_PCI n
 fi
 
+dep_bool 'Preemptible Kernel' CONFIG_PREEMPT $CONFIG_NEW_IRQ
 bool 'System V IPC' CONFIG_SYSVIPC
 bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT
 bool 'Sysctl support' CONFIG_SYSCTL
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/mips/kernel/i8259.c linux-2.4.27-leo/arch/mips/kernel/i8259.c
--- linux-2.4.27/arch/mips/kernel/i8259.c	2003-08-25 12:44:40.000000000 +0100
+++ linux-2.4.27-leo/arch/mips/kernel/i8259.c	2004-09-17 03:19:36.000000000 +0100
@@ -8,6 +8,7 @@
  * Copyright (C) 1992 Linus Torvalds
  * Copyright (C) 1994 - 2000 Ralf Baechle
  */
+#include <linux/sched.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/ioport.h>
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/mips/kernel/irq.c linux-2.4.27-leo/arch/mips/kernel/irq.c
--- linux-2.4.27/arch/mips/kernel/irq.c	2004-02-20 14:11:38.000000000 +0000
+++ linux-2.4.27-leo/arch/mips/kernel/irq.c	2004-09-17 03:19:36.000000000 +0100
@@ -8,6 +8,8 @@
  * Copyright (C) 1992 Linus Torvalds
  * Copyright (C) 1994 - 2000 Ralf Baechle
  */
+
+#include <linux/sched.h>
 #include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
@@ -19,11 +21,13 @@
 #include <linux/slab.h>
 #include <linux/mm.h>
 #include <linux/random.h>
-#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/ptrace.h>
 
 #include <asm/atomic.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
+#include <asm/debug.h>
 
 /*
  * Controller mappings for all interrupt sources:
@@ -429,6 +433,8 @@
 	struct irqaction * action;
 	unsigned int status;
 
+	preempt_disable();
+
 	kstat.irqs[cpu][irq]++;
 	spin_lock(&desc->lock);
 	desc->handler->ack(irq);
@@ -490,6 +496,27 @@
 
 	if (softirq_pending(cpu))
 		do_softirq();
+
+#if defined(CONFIG_PREEMPT)
+	while (--current->preempt_count == 0) {
+		db_assert(intr_off());
+		db_assert(!in_interrupt());
+
+		if (current->need_resched == 0) {
+			break;
+		}
+
+		current->preempt_count ++;
+		sti();
+		if (user_mode(regs)) {
+			schedule();
+		} else {
+			preempt_schedule();
+		}
+		cli();
+	}
+#endif
+
 	return 1;
 }
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/mips/mm/extable.c linux-2.4.27-leo/arch/mips/mm/extable.c
--- linux-2.4.27/arch/mips/mm/extable.c	2002-11-28 23:53:10.000000000 +0000
+++ linux-2.4.27-leo/arch/mips/mm/extable.c	2004-09-17 03:19:36.000000000 +0100
@@ -3,6 +3,7 @@
  */
 #include <linux/config.h>
 #include <linux/module.h>
+#include <linux/sched.h>
 #include <linux/spinlock.h>
 #include <asm/uaccess.h>
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/mips/mm/fault.c linux-2.4.27-leo/arch/mips/mm/fault.c
--- linux-2.4.27/arch/mips/mm/fault.c	2003-08-25 12:44:40.000000000 +0100
+++ linux-2.4.27-leo/arch/mips/mm/fault.c	2004-09-17 03:19:58.000000000 +0100
@@ -69,6 +69,24 @@
 	}
 }
 
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+void pax_report_insns(void *pc)
+{
+	unsigned long i;
+
+	printk(KERN_ERR "PAX: bytes at PC: ");
+	for (i = 0; i < 5; i++) {
+		unsigned int c;
+		if (get_user(c, (unsigned int*)pc+i)) {
+			printk("<invalid address>.");
+			break;
+		}
+		printk("%08x ", c);
+	}
+	printk("\n");
+}
+#endif
+
 /*
  * This routine handles page faults.  It determines the address,
  * and the problem, and then passes it off to one of the appropriate
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/mips64/config.in linux-2.4.27-leo/arch/mips64/config.in
--- linux-2.4.27/arch/mips64/config.in	2002-11-28 23:53:10.000000000 +0000
+++ linux-2.4.27-leo/arch/mips64/config.in	2004-09-17 03:19:58.000000000 +0100
@@ -7,3 +7,11 @@
 define_bool CONFIG_MIPS64 y
 
 source arch/mips/config-shared.in
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+        source grsecurity/Config.in
+fi
+endmenu
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/mips64/kernel/ioctl32.c linux-2.4.27-leo/arch/mips64/kernel/ioctl32.c
--- linux-2.4.27/arch/mips64/kernel/ioctl32.c	2004-02-20 14:11:38.000000000 +0000
+++ linux-2.4.27-leo/arch/mips64/kernel/ioctl32.c	2004-09-17 03:19:08.000000000 +0100
@@ -62,6 +62,7 @@
 
 #include <linux/mtd/mtd.h>
 #include <linux/serial.h>
+#include <linux/dm-ioctl.h>
 
 #ifdef CONFIG_SIBYTE_TBPROF
 #include <asm/sibyte/trace_prof.h>
@@ -2327,6 +2328,22 @@
 	IOCTL32_DEFAULT(RESTART_ARRAY_RW),
 #endif /* CONFIG_MD */
 
+#if defined(CONFIG_BLK_DEV_DM) || defined(CONFIG_BLK_DEV_DM_MODULE)
+	IOCTL32_DEFAULT(DM_VERSION),
+	IOCTL32_DEFAULT(DM_REMOVE_ALL),
+	IOCTL32_DEFAULT(DM_DEV_CREATE),
+	IOCTL32_DEFAULT(DM_DEV_REMOVE),
+	IOCTL32_DEFAULT(DM_TABLE_LOAD),
+	IOCTL32_DEFAULT(DM_DEV_SUSPEND),
+	IOCTL32_DEFAULT(DM_DEV_RENAME),
+	IOCTL32_DEFAULT(DM_TABLE_DEPS),
+	IOCTL32_DEFAULT(DM_DEV_STATUS),
+	IOCTL32_DEFAULT(DM_TABLE_STATUS),
+	IOCTL32_DEFAULT(DM_DEV_WAIT),
+	IOCTL32_DEFAULT(DM_LIST_DEVICES),
+	IOCTL32_DEFAULT(DM_TABLE_CLEAR),
+#endif /* CONFIG_BLK_DEV_DM */
+
 #ifdef CONFIG_SIBYTE_TBPROF
 	IOCTL32_DEFAULT(SBPROF_ZBSTART),
 	IOCTL32_DEFAULT(SBPROF_ZBSTOP),
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/mips64/mm/fault.c linux-2.4.27-leo/arch/mips64/mm/fault.c
--- linux-2.4.27/arch/mips64/mm/fault.c	2004-02-20 14:11:38.000000000 +0000
+++ linux-2.4.27-leo/arch/mips64/mm/fault.c	2004-09-17 03:19:58.000000000 +0100
@@ -90,6 +90,24 @@
 	}
 }
 
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+void pax_report_insns(void *pc)
+{
+	unsigned long i;
+
+	printk(KERN_ERR "PAX: bytes at PC: ");
+	for (i = 0; i < 5; i++) {
+		unsigned int c;
+		if (get_user(c, (unsigned int*)pc+i)) {
+			printk("<invalid address>.");
+			break;
+		}
+		printk("%08x ", c);
+	}
+	printk("\n");
+}
+#endif
+
 /*
  * This routine handles page faults.  It determines the address,
  * and the problem, and then passes it off to one of the appropriate
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/parisc/config.in linux-2.4.27-leo/arch/parisc/config.in
--- linux-2.4.27/arch/parisc/config.in	2004-02-20 14:11:38.000000000 +0000
+++ linux-2.4.27-leo/arch/parisc/config.in	2004-09-17 03:19:58.000000000 +0100
@@ -204,3 +204,11 @@
 
 source crypto/Config.in
 source lib/Config.in
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+	source grsecurity/Config.in
+fi
+endmenu
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/parisc/kernel/ioctl32.c linux-2.4.27-leo/arch/parisc/kernel/ioctl32.c
--- linux-2.4.27/arch/parisc/kernel/ioctl32.c	2003-08-25 12:44:40.000000000 +0100
+++ linux-2.4.27-leo/arch/parisc/kernel/ioctl32.c	2004-09-17 03:19:58.000000000 +0100
@@ -55,6 +55,7 @@
 #define max max */
 #include <linux/lvm.h>
 #endif /* LVM */
+#include <linux/dm-ioctl.h>
 
 #include <scsi/scsi.h>
 /* Ugly hack. */
@@ -1434,7 +1435,11 @@
 	 * To have permissions to do most of the vt ioctls, we either have
 	 * to be the owner of the tty, or super-user.
 	 */
+#ifdef CONFIG_GRKERNSEC
+	if (current->tty == tty || capable(CAP_SYS_TTY_CONFIG))
+#else
 	if (current->tty == tty || suser())
+#endif
 		return 1;
 	return 0;                                                    
 }
@@ -3423,6 +3428,22 @@
 COMPATIBLE_IOCTL(LV_BMAP)
 COMPATIBLE_IOCTL(LV_SNAPSHOT_USE_RATE)
 #endif /* LVM */
+/* Device-Mapper */
+#if defined(CONFIG_BLK_DEV_DM) || defined(CONFIG_BLK_DEV_DM_MODULE)
+COMPATIBLE_IOCTL(DM_VERSION)
+COMPATIBLE_IOCTL(DM_REMOVE_ALL)
+COMPATIBLE_IOCTL(DM_DEV_CREATE)
+COMPATIBLE_IOCTL(DM_DEV_REMOVE)
+COMPATIBLE_IOCTL(DM_TABLE_LOAD)
+COMPATIBLE_IOCTL(DM_DEV_SUSPEND)
+COMPATIBLE_IOCTL(DM_DEV_RENAME)
+COMPATIBLE_IOCTL(DM_TABLE_DEPS)
+COMPATIBLE_IOCTL(DM_DEV_STATUS)
+COMPATIBLE_IOCTL(DM_TABLE_STATUS)
+COMPATIBLE_IOCTL(DM_DEV_WAIT)
+COMPATIBLE_IOCTL(DM_LIST_DEVICES)
+COMPATIBLE_IOCTL(DM_TABLE_CLEAR)
+#endif /* CONFIG_BLK_DEV_DM */
 #if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE)
 COMPATIBLE_IOCTL(DRM_IOCTL_GET_MAGIC)
 COMPATIBLE_IOCTL(DRM_IOCTL_IRQ_BUSID)
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/parisc/kernel/ptrace.c linux-2.4.27-leo/arch/parisc/kernel/ptrace.c
--- linux-2.4.27/arch/parisc/kernel/ptrace.c	2002-11-28 23:53:10.000000000 +0000
+++ linux-2.4.27-leo/arch/parisc/kernel/ptrace.c	2004-09-17 03:19:58.000000000 +0100
@@ -15,7 +15,7 @@
 #include <linux/ptrace.h>
 #include <linux/user.h>
 #include <linux/personality.h>
-
+#include <linux/grsecurity.h>
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 #include <asm/system.h>
@@ -119,6 +119,9 @@
 	if (pid == 1)		/* no messing around with init! */
 		goto out_tsk;
 
+	if (gr_handle_ptrace(child, request))
+		goto out_tsk;
+
 	if (request == PTRACE_ATTACH) {
 		ret = ptrace_attach(child);
 		goto out_tsk;
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/parisc/kernel/sys_parisc32.c linux-2.4.27-leo/arch/parisc/kernel/sys_parisc32.c
--- linux-2.4.27/arch/parisc/kernel/sys_parisc32.c	2003-06-13 15:51:31.000000000 +0100
+++ linux-2.4.27-leo/arch/parisc/kernel/sys_parisc32.c	2004-09-17 03:19:58.000000000 +0100
@@ -50,6 +50,7 @@
 #include <linux/highmem.h>
 #include <linux/highuid.h>
 #include <linux/mman.h>
+#include <linux/grsecurity.h>
 
 #include <asm/types.h>
 #include <asm/uaccess.h>
@@ -177,6 +178,11 @@
 	struct file *file;
 	int retval;
 	int i;
+#ifdef CONFIG_GRKERNSEC
+	struct file *old_exec_file;
+	struct acl_subject_label *old_acl;
+	struct rlimit old_rlim[RLIM_NLIMITS];
+#endif
 
 	file = open_exec(filename);
 
@@ -184,7 +190,26 @@
 	if (IS_ERR(file))
 		return retval;
 
+	gr_learn_resource(current, RLIMIT_NPROC, atomic_read(&current->user->processes), 1);
+
+	if (gr_handle_nproc()) {
+		allow_write_access(file);
+		fput(file);
+		return -EAGAIN;
+	}
+
+	if (!gr_acl_handle_execve(file->f_dentry, file->f_vfsmnt)) {
+		allow_write_access(file);
+		fput(file);
+		return -EACCES;
+	}
+
 	bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDUSTACK
+	bprm.p -= (get_random_long() & ~(sizeof(void *)-1)) & ~PAGE_MASK;
+#endif
+
 	memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page[0]));
 
 	DBG(("do_execve32(%s, %p, %p, %p)\n", filename, argv, envp, regs));
@@ -209,11 +234,24 @@
 	if (retval < 0)
 		goto out;
 	
+	if (!gr_tpe_allow(file)) {
+		retval = -EACCES;
+		goto out;
+	}
+
+	if (gr_check_crash_exec(file)) {
+		retval = -EACCES;
+		goto out;
+	}
+
 	retval = copy_strings_kernel(1, &bprm.filename, &bprm);
 	if (retval < 0)
 		goto out;
 
 	bprm.exec = bprm.p;
+
+	gr_log_chroot_exec(file->f_dentry, file->f_vfsmnt);
+
 	retval = copy_strings32(bprm.envc, envp, &bprm);
 	if (retval < 0)
 		goto out;
@@ -222,11 +260,32 @@
 	if (retval < 0)
 		goto out;
 
+#ifdef CONFIG_GRKERNSEC
+	old_acl = current->acl;
+	memcpy(old_rlim, current->rlim, sizeof(old_rlim));
+	old_exec_file = current->exec_file;
+	get_file(file);
+	current->exec_file = file;
+#endif
+
+	gr_set_proc_label(file->f_dentry, file->f_vfsmnt);
+
 	retval = search_binary_handler(&bprm,regs);
-	if (retval >= 0)
+	if (retval >= 0) {
+#ifdef CONFIG_GRKERNSEC
+		if (old_exec_file)
+			fput(old_exec_file);
+#endif
 		/* execve success */
 		return retval;
+	}
 
+#ifdef CONFIG_GRKERNSEC
+	current->acl = old_acl;
+	memcpy(current->rlim, old_rlim, sizeof(old_rlim));
+	fput(current->exec_file);
+	current->exec_file = old_exec_file;
+#endif
 out:
 	/* Something went wrong, return the inode and free the argument pages*/
 	allow_write_access(bprm.file);
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/parisc/kernel/sys_parisc.c linux-2.4.27-leo/arch/parisc/kernel/sys_parisc.c
--- linux-2.4.27/arch/parisc/kernel/sys_parisc.c	2002-11-28 23:53:10.000000000 +0000
+++ linux-2.4.27-leo/arch/parisc/kernel/sys_parisc.c	2004-09-17 03:19:58.000000000 +0100
@@ -12,6 +12,7 @@
 #include <linux/mman.h>
 #include <linux/shm.h>
 #include <linux/smp_lock.h>
+#include <linux/grsecurity.h>
 
 int sys_pipe(int *fildes)
 {
@@ -90,6 +91,11 @@
 		inode = filp->f_dentry->d_inode;
 	}
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDMMAP
+	if ((current->flags & PF_PAX_RANDMMAP) && (!addr || filp))
+		addr = TASK_UNMAPPED_BASE + current->mm->delta_mmap;
+#endif
+
 	if (inode && (flags & MAP_SHARED) && (inode->i_mapping->i_mmap_shared)) {
 		addr = get_shared_area(inode, addr, len, pgoff);
 	} else {
@@ -104,12 +110,23 @@
 {
 	struct file * file = NULL;
 	unsigned long error = -EBADF;
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (flags & MAP_MIRROR)
+		return -EINVAL;
+#endif
+
 	if (!(flags & MAP_ANONYMOUS)) {
 		file = fget(fd);
 		if (!file)
 			goto out;
 	}
 
+	if (gr_handle_mmap(file, prot)) {
+		fput(file);
+		return -EACCES;
+	}
+
 	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 
 	down_write(&current->mm->mmap_sem);
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/parisc/kernel/traps.c linux-2.4.27-leo/arch/parisc/kernel/traps.c
--- linux-2.4.27/arch/parisc/kernel/traps.c	2003-08-25 12:44:40.000000000 +0100
+++ linux-2.4.27-leo/arch/parisc/kernel/traps.c	2004-09-17 03:19:58.000000000 +0100
@@ -637,9 +637,7 @@
 
 			down_read(&current->mm->mmap_sem);
 			vma = find_vma(current->mm,regs->iaoq[0]);
-			if (vma && (regs->iaoq[0] >= vma->vm_start)
-				&& (vma->vm_flags & VM_EXEC)) {
-
+			if (vma && (regs->iaoq[0] >= vma->vm_start)) {
 				fault_address = regs->iaoq[0];
 				fault_space = regs->iasq[0];
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/parisc/mm/fault.c linux-2.4.27-leo/arch/parisc/mm/fault.c
--- linux-2.4.27/arch/parisc/mm/fault.c	2003-06-13 15:51:31.000000000 +0100
+++ linux-2.4.27-leo/arch/parisc/mm/fault.c	2004-09-17 03:19:58.000000000 +0100
@@ -15,6 +15,7 @@
 #include <linux/ptrace.h>
 #include <linux/sched.h>
 #include <linux/interrupt.h>
+#include <linux/unistd.h>
 
 #include <asm/uaccess.h>
 #include <asm/traps.h>
@@ -53,7 +54,7 @@
 static unsigned long
 parisc_acctyp(unsigned long code, unsigned int inst)
 {
-	if (code == 6 || code == 16)
+	if (code == 6 || code == 7 || code == 16)
 	    return VM_EXEC;
 
 	switch (inst & 0xf0000000) {
@@ -139,6 +140,136 @@
 			}
 #endif
 
+/*
+ * PaX: decide what to do with offenders (instruction_pointer(regs) = fault address)
+ *
+ * returns 1 when task should be killed 
+ *	   2 when rt_sigreturn trampoline was detected
+ *	   3 when unpatched PLT trampoline was detected
+ *	   4 when legitimate ET_EXEC was detected
+ */
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC  
+static int pax_handle_fetch_fault(struct pt_regs *regs)
+{
+	int err;
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (current->flags & PF_PAX_RANDEXEC) {
+		if (instruction_pointer(regs) >= current->mm->start_code &&
+		    instruction_pointer(regs) < current->mm->end_code)
+		{
+#if 0
+			/* PaX: this needs fixing */
+			if ((regs->gr[2] & ~3UL) == instruction_pointer(regs))
+				return 1;
+#endif
+			regs->iaoq[0] += current->mm->delta_exec;
+			if ((regs->iaoq[1] & ~3UL) >= current->mm->start_code &&
+			    (regs->iaoq[1] & ~3UL) < current->mm->end_code)
+				regs->iaoq[1] += current->mm->delta_exec;
+			return 4;
+		}
+	}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUPLT
+	do { /* PaX: unpatched PLT emulation */
+		unsigned int bl, depwi;
+
+		err = get_user(bl, (unsigned int*)instruction_pointer(regs));
+		err |= get_user(depwi, (unsigned int*)(instruction_pointer(regs)+4));
+
+		if (err)
+			break;
+
+		if (bl == 0xEA9F1FDDU && depwi == 0xD6801C1EU) {
+			unsigned int ldw, bv, ldw2, addr = instruction_pointer(regs)-12;
+
+			err = get_user(ldw, (unsigned int*)addr);
+			err |= get_user(bv, (unsigned int*)(addr+4));
+			err |= get_user(ldw2, (unsigned int*)(addr+8));
+
+			if (err)
+				break;
+
+			if (ldw == 0x0E801096U &&
+			    bv == 0xEAC0C000U &&
+			    ldw2 == 0x0E881095U)
+			{
+				unsigned int resolver, map;
+
+				err = get_user(resolver, (unsigned int*)(instruction_pointer(regs)+8));
+				err |= get_user(map, (unsigned int*)(instruction_pointer(regs)+12));
+				if (err)
+					break;
+
+				regs->gr[20] = instruction_pointer(regs)+8;
+				regs->gr[21] = map;
+				regs->gr[22] = resolver;
+				regs->iaoq[0] = resolver | 3UL;
+				regs->iaoq[1] = regs->iaoq[0] + 4;
+				return 3;
+			}
+		}
+	} while (0);
+#endif
+ 
+#ifdef CONFIG_GRKERNSEC_PAX_EMUTRAMP
+
+#ifndef CONFIG_GRKERNSEC_PAX_EMUSIGRT
+	if (!(current->flags & PF_PAX_EMUTRAMP))
+		return 1;
+#endif
+
+	do { /* PaX: rt_sigreturn emulation */
+		unsigned int ldi1, ldi2, bel, nop;
+
+		err = get_user(ldi1, (unsigned int *)instruction_pointer(regs));
+		err |= get_user(ldi2, (unsigned int *)(instruction_pointer(regs)+4));
+		err |= get_user(bel, (unsigned int *)(instruction_pointer(regs)+8));
+		err |= get_user(nop, (unsigned int *)(instruction_pointer(regs)+12));
+
+		if (err)
+			break;
+
+                if ((ldi1 == 0x34190000U || ldi1 == 0x34190002U) &&
+		    ldi2 == 0x3414015AU &&
+		    bel == 0xE4008200U &&
+		    nop == 0x08000240U)
+		{
+			regs->gr[25] = (ldi1 & 2) >> 1;
+			regs->gr[20] = __NR_rt_sigreturn;
+			regs->gr[31] = regs->iaoq[1] + 16;
+			regs->sr[0] = regs->iasq[1];
+			regs->iaoq[0] = 0x100UL;
+			regs->iaoq[1] = regs->iaoq[0] + 4;
+			regs->iasq[0] = regs->sr[2];
+			regs->iasq[1] = regs->sr[2];
+			return 2;
+		}
+	} while (0);
+#endif
+
+	return 1;
+}
+
+void pax_report_insns(void *pc)
+{
+	unsigned long i;
+
+	printk(KERN_ERR "PAX: bytes at PC: ");
+	for (i = 0; i < 5; i++) {
+		unsigned int c;
+		if (get_user(c, (unsigned int*)pc+i)) {
+			printk("<invalid address>.");
+			break;
+		}
+		printk("%08x ", c);
+	}
+	printk("\n");
+}
+#endif
+
 void do_page_fault(struct pt_regs *regs, unsigned long code,
 			      unsigned long address)
 {
@@ -164,8 +295,38 @@
 
 	acc_type = parisc_acctyp(code,regs->iir);
 
-	if ((vma->vm_flags & acc_type) != acc_type)
+	if ((vma->vm_flags & acc_type) != acc_type) {
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+		if ((current->flags & PF_PAX_PAGEEXEC) && (acc_type & VM_EXEC) &&
+		    (address & ~3UL) == instruction_pointer(regs))
+		   {
+			up_read(&mm->mmap_sem);
+			switch(pax_handle_fetch_fault(regs)) {
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+			case 4:
+				return;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUPLT
+			case 3:
+				return;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUTRAMP
+			case 2:
+				return;
+#endif
+
+			}
+			pax_report_fault(regs, (void*)instruction_pointer(regs), (void*)regs->gr[30]);
+			do_exit(SIGKILL);
+		}
+#endif
+
 		goto bad_area;
+	}
 
 	/*
 	 * If for any reason at all we couldn't handle the fault, make
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/ppc/config.in linux-2.4.27-leo/arch/ppc/config.in
--- linux-2.4.27/arch/ppc/config.in	2004-09-17 02:38:42.000000000 +0100
+++ linux-2.4.27-leo/arch/ppc/config.in	2004-09-17 03:19:58.000000000 +0100
@@ -167,6 +167,8 @@
   int  'Maximum number of CPUs (2-32)' CONFIG_NR_CPUS 32
 fi
 
+bool 'Preemptible kernel support' CONFIG_PREEMPT
+
 if [ "$CONFIG_6xx" = "y" -a "$CONFIG_8260" = "n" ];then
   bool 'AltiVec Support' CONFIG_ALTIVEC
   bool 'Thermal Management Support' CONFIG_TAU
@@ -666,3 +668,12 @@
 int 'Kernel messages buffer length shift (0 = default)' CONFIG_LOG_BUF_SHIFT 0
 
 endmenu
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+    source grsecurity/Config.in
+fi
+endmenu
+
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/ppc/kernel/entry.S linux-2.4.27-leo/arch/ppc/kernel/entry.S
--- linux-2.4.27/arch/ppc/kernel/entry.S	2004-05-19 21:34:36.000000000 +0100
+++ linux-2.4.27-leo/arch/ppc/kernel/entry.S	2004-09-17 03:19:36.000000000 +0100
@@ -287,6 +287,46 @@
 	 */
 	cmpi	0,r3,0
 	beq	restore
+#ifdef CONFIG_PREEMPT
+	lwz	r3,PREEMPT_COUNT(r2)
+	cmpi	0,r3,1
+	bge	ret_from_except
+	lwz	r5,_MSR(r1)
+	andi.	r5,r5,MSR_PR
+	bne	do_signal_ret
+	lwz	r5,NEED_RESCHED(r2)
+	cmpi	0,r5,0
+	beq	ret_from_except
+	lis	r3,irq_stat@h
+	ori	r3,r3,irq_stat@l
+#ifdef CONFIG_SMP
+	lwz     r5,CPU(r2)
+	rlwinm  r5,r5,5,0,26
+	add     r3,r3,r5
+#endif
+	lwz	r5,4(r3)
+	lwz	r3,8(r3)
+	add	r3,r3,r5
+	cmpi	0,r3,0
+	bne	ret_from_except
+	lwz	r3,PREEMPT_COUNT(r2)
+	addi	r3,r3,1
+	stw	r3,PREEMPT_COUNT(r2)
+	mfmsr	r0
+	ori	r0,r0,MSR_EE
+	mtmsr	r0
+	sync
+	bl	preempt_schedule
+	mfmsr	r0
+	rlwinm	r0,r0,0,17,15
+	mtmsr	r0
+	sync
+	lwz	r3,PREEMPT_COUNT(r2)
+	subi	r3,r3,1
+	stw	r3,PREEMPT_COUNT(r2)
+	li	r3,1
+	b	ret_from_intercept
+#endif /* CONFIG_PREEMPT */
 	.globl	ret_from_except
 ret_from_except:
 	lwz	r3,_MSR(r1)	/* Returning to user mode? */
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/ppc/kernel/head_4xx.S linux-2.4.27-leo/arch/ppc/kernel/head_4xx.S
--- linux-2.4.27/arch/ppc/kernel/head_4xx.S	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.27-leo/arch/ppc/kernel/head_4xx.S	2004-09-17 03:19:58.000000000 +0100
@@ -296,15 +296,12 @@
 
 	/* Most of the Linux PTE is ready to load into the TLB LO.
 	 * We set ZSEL, where only the LS-bit determines user access.
-	 * We set execute, because we don't have the granularity to
-	 * properly set this at the page level (Linux problem).
 	 * If shared is set, we cause a zero PID->TID load.
 	 * Many of these bits are software only.  Bits we don't set
 	 * here we (properly should) assume have the appropriate value.
 	 */
 	li	r22, 0x0ce2
 	andc	r21, r21, r22		/* Make sure 20, 21 are zero */
-	ori	r21, r21, _PAGE_HWEXEC	/* make it executable */
 
 	/* find the TLB index that caused the fault.  It has to be here.
 	*/
@@ -783,7 +780,6 @@
 	stw	r23, tlb_4xx_index@l(0)
 
 6:
-	ori	r21, r21, _PAGE_HWEXEC		/* make it executable */
 	tlbwe	r21, r23, TLB_DATA		/* Load TLB LO */
 
 	/* Create EPN.  This is the faulting address plus a static
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/ppc/kernel/irq.c linux-2.4.27-leo/arch/ppc/kernel/irq.c
--- linux-2.4.27/arch/ppc/kernel/irq.c	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.27-leo/arch/ppc/kernel/irq.c	2004-09-17 03:19:36.000000000 +0100
@@ -551,6 +551,34 @@
 	return 1; /* lets ret_from_int know we can do checks */
 }
 
+#ifdef CONFIG_PREEMPT
+int
+preempt_intercept(struct pt_regs *regs)
+{
+	int ret;
+
+	preempt_disable();
+
+	switch(regs->trap) {
+	case 0x500:
+		ret = do_IRQ(regs);
+		break;
+#ifndef CONFIG_4xx
+	case 0x900:
+#else
+	case 0x1000:
+#endif
+		ret = timer_interrupt(regs);
+		break;
+	default:
+		BUG();
+	}
+
+	preempt_enable();
+	return ret;
+}
+#endif /* CONFIG_PREEMPT */
+
 unsigned long probe_irq_on (void)
 {
 	return 0;
@@ -647,11 +675,13 @@
 				show("wait_on_irq");
 				count = ~0;
 			}
+			preempt_disable();
 			__sti();
 			/* don't worry about the lock race Linus found
 			 * on intel here. -- Cort
 			 */
 			__cli();
+			preempt_enable_no_resched();
 			if (atomic_read(&global_irq_count))
 				continue;
 			if (global_irq_lock)
@@ -727,6 +757,8 @@
 	global_irq_holder = cpu;
 }
 
+#define	EFLAGS_IF_SHIFT	15
+
 /*
  * A global "cli()" while in an interrupt context
  * turns into just a local cli(). Interrupts
@@ -744,9 +776,10 @@
 	unsigned long flags;
 
 	__save_flags(flags);
-	if (flags & (1 << 15)) {
-		int cpu = smp_processor_id();
+	if (flags & (1 << EFLAGS_IF_SHIFT)) {
+		int cpu;
 		__cli();
+		cpu = smp_processor_id();
 		if (!local_irq_count(cpu))
 			get_irqlock(cpu);
 	}
@@ -754,11 +787,14 @@
 
 void __global_sti(void)
 {
-	int cpu = smp_processor_id();
+	int cpu;
 
+	preempt_disable();
+	cpu = smp_processor_id();
 	if (!local_irq_count(cpu))
 		release_irqlock(cpu);
 	__sti();
+	preempt_enable();
 }
 
 /*
@@ -773,19 +809,23 @@
 	int retval;
 	int local_enabled;
 	unsigned long flags;
+	int cpu;
 
 	__save_flags(flags);
-	local_enabled = (flags >> 15) & 1;
+	local_enabled = (flags >> EFLAGS_IF_SHIFT) & 1;
 	/* default to local */
 	retval = 2 + local_enabled;
 
 	/* check for global flags if we're not in an interrupt */
-	if (!local_irq_count(smp_processor_id())) {
+	preempt_disable();
+	cpu = smp_processor_id();
+	if (!local_irq_count(cpu)) {
 		if (local_enabled)
 			retval = 1;
-		if (global_irq_holder == (unsigned char) smp_processor_id())
+		if (global_irq_holder == cpu)
 			retval = 0;
 	}
+	preempt_enable();
 	return retval;
 }
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/ppc/kernel/mk_defs.c linux-2.4.27-leo/arch/ppc/kernel/mk_defs.c
--- linux-2.4.27/arch/ppc/kernel/mk_defs.c	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.27-leo/arch/ppc/kernel/mk_defs.c	2004-09-17 03:19:36.000000000 +0100
@@ -39,6 +39,9 @@
 	DEFINE(SIGPENDING, offsetof(struct task_struct, sigpending));
 	DEFINE(THREAD, offsetof(struct task_struct, thread));
 	DEFINE(MM, offsetof(struct task_struct, mm));
+#ifdef CONFIG_PREEMPT
+	DEFINE(PREEMPT_COUNT, offsetof(struct task_struct, preempt_count));
+#endif
 	DEFINE(ACTIVE_MM, offsetof(struct task_struct, active_mm));
 	DEFINE(TASK_STRUCT_SIZE, sizeof(struct task_struct));
 	DEFINE(KSP, offsetof(struct thread_struct, ksp));
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/ppc/kernel/open_pic.c linux-2.4.27-leo/arch/ppc/kernel/open_pic.c
--- linux-2.4.27/arch/ppc/kernel/open_pic.c	2004-02-20 14:11:39.000000000 +0000
+++ linux-2.4.27-leo/arch/ppc/kernel/open_pic.c	2004-09-17 03:19:36.000000000 +0100
@@ -601,19 +601,24 @@
 void __init do_openpic_setup_cpu(void)
 {
  	int i;
-	u32 msk = 1 << smp_hw_index[smp_processor_id()];
+#ifdef CONFIG_IRQ_ALL_CPUS
+	u32 msk;
+#endif /* CONFIG_IRQ_ALL_CPUS */
 
 	spin_lock(&openpic_setup_lock);
 
 #ifdef CONFIG_IRQ_ALL_CPUS
+	msk = 1 << smp_hw_index[smp_processor_id()];
+
  	/* let the openpic know we want intrs. default affinity
  	 * is 0xffffffff until changed via /proc
  	 * That's how it's done on x86. If we want it differently, then
  	 * we should make sure we also change the default values of irq_affinity
  	 * in irq.c.
  	 */
- 	for (i = 0; i < NumSources; i++)
+ 	for (i = 0; i < NumSources; i++) {
 		openpic_mapirq(i, msk, ~0U);
+	}
 #endif /* CONFIG_IRQ_ALL_CPUS */
  	openpic_set_priority(0);
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/ppc/kernel/ptrace.c linux-2.4.27-leo/arch/ppc/kernel/ptrace.c
--- linux-2.4.27/arch/ppc/kernel/ptrace.c	2003-08-25 12:44:40.000000000 +0100
+++ linux-2.4.27-leo/arch/ppc/kernel/ptrace.c	2004-09-17 03:19:58.000000000 +0100
@@ -24,6 +24,7 @@
 #include <linux/errno.h>
 #include <linux/ptrace.h>
 #include <linux/user.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 #include <asm/page.h>
@@ -195,6 +196,9 @@
 	if (pid == 1)		/* you may not mess with init */
 		goto out_tsk;
 
+	if (gr_handle_ptrace(child, request))
+		goto out_tsk;
+
 	if (request == PTRACE_ATTACH) {
 		ret = ptrace_attach(child);
 		goto out_tsk;
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/ppc/kernel/setup.c linux-2.4.27-leo/arch/ppc/kernel/setup.c
--- linux-2.4.27/arch/ppc/kernel/setup.c	2004-05-19 21:34:36.000000000 +0100
+++ linux-2.4.27-leo/arch/ppc/kernel/setup.c	2004-09-17 03:19:36.000000000 +0100
@@ -502,6 +502,20 @@
 	strcpy(cmd_line, CONFIG_CMDLINE);
 #endif /* CONFIG_CMDLINE */
 
+#ifdef CONFIG_PREEMPT
+	/* Override the irq routines for external & timer interrupts here,
+	 * as the MMU has only been minimally setup at this point and
+	 * there are no protections on page zero.
+	 */
+	{
+		extern int preempt_intercept(struct pt_regs *);
+	
+		do_IRQ_intercept = (unsigned long) &preempt_intercept;
+		timer_interrupt_intercept = (unsigned long) &preempt_intercept;
+
+	}
+#endif /* CONFIG_PREEMPT */
+
 	platform_init(r3, r4, r5, r6, r7);
 
 	if (ppc_md.progress)
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/ppc/kernel/syscalls.c linux-2.4.27-leo/arch/ppc/kernel/syscalls.c
--- linux-2.4.27/arch/ppc/kernel/syscalls.c	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.27-leo/arch/ppc/kernel/syscalls.c	2004-09-17 03:19:58.000000000 +0100
@@ -35,6 +35,7 @@
 #include <linux/ipc.h>
 #include <linux/utsname.h>
 #include <linux/file.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 #include <asm/ipc.h>
@@ -191,12 +192,23 @@
 	struct file * file = NULL;
 	int ret = -EBADF;
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (flags & MAP_MIRROR)
+		return -EINVAL;
+#endif
+
 	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 	if (!(flags & MAP_ANONYMOUS)) {
 		if (!(file = fget(fd)))
 			goto out;
 	}
 
+	if (gr_handle_mmap(file, prot)) {
+		fput(file);
+		ret = -EACCES;
+		goto out;
+	}
+
 	ret = -EINVAL;
 	if ((! allow_mmap_address(addr)) && (flags & MAP_FIXED))
 		goto out;
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/ppc/kernel/temp.c linux-2.4.27-leo/arch/ppc/kernel/temp.c
--- linux-2.4.27/arch/ppc/kernel/temp.c	2003-08-25 12:44:40.000000000 +0100
+++ linux-2.4.27-leo/arch/ppc/kernel/temp.c	2004-09-17 03:19:36.000000000 +0100
@@ -138,7 +138,7 @@
 
 static void tau_timeout(void * info)
 {
-	unsigned long cpu = smp_processor_id();
+	unsigned long cpu;
 	unsigned long flags;
 	int size;
 	int shrink;
@@ -146,6 +146,8 @@
 	/* disabling interrupts *should* be okay */
 	save_flags(flags); cli();
 
+	cpu = smp_processor_id();
+
 #ifndef CONFIG_TAU_INT
 	TAUupdate(cpu);
 #endif
@@ -191,13 +193,15 @@
 
 static void tau_timeout_smp(unsigned long unused)
 {
-
 	/* schedule ourselves to be run again */
 	mod_timer(&tau_timer, jiffies + shrink_timer) ;
+
+	preempt_disable();
 #ifdef CONFIG_SMP
 	smp_call_function(tau_timeout, NULL, 1, 0);
 #endif
 	tau_timeout(NULL);
+	preempt_enable();
 }
 
 /*
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/ppc/lib/dec_and_lock.c linux-2.4.27-leo/arch/ppc/lib/dec_and_lock.c
--- linux-2.4.27/arch/ppc/lib/dec_and_lock.c	2001-11-16 18:10:08.000000000 +0000
+++ linux-2.4.27-leo/arch/ppc/lib/dec_and_lock.c	2004-09-17 03:19:36.000000000 +0100
@@ -1,4 +1,5 @@
 #include <linux/module.h>
+#include <linux/sched.h>
 #include <linux/spinlock.h>
 #include <asm/atomic.h>
 #include <asm/system.h>
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/ppc/mm/fault.c linux-2.4.27-leo/arch/ppc/mm/fault.c
--- linux-2.4.27/arch/ppc/mm/fault.c	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.27-leo/arch/ppc/mm/fault.c	2004-09-17 03:19:58.000000000 +0100
@@ -26,6 +26,9 @@
 #include <linux/mman.h>
 #include <linux/mm.h>
 #include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/compiler.h>
 
 #include <asm/page.h>
 #include <asm/pgtable.h>
@@ -52,6 +55,360 @@
 void bad_page_fault(struct pt_regs *, unsigned long, int sig);
 void do_page_fault(struct pt_regs *, unsigned long, unsigned long);
 
+#ifdef CONFIG_GRKERNSEC_PAX_EMUSIGRT
+void pax_syscall_close(struct vm_area_struct * vma)
+{
+	vma->vm_mm->call_syscall = 0UL;
+}
+
+static struct page* pax_syscall_nopage(struct vm_area_struct *vma, unsigned long address, int write_access)
+{
+	struct page* page;
+	unsigned int *kaddr;
+
+	page = alloc_page(GFP_HIGHUSER);
+	if (!page)
+		return page;
+
+	kaddr = kmap(page);
+	memset(kaddr, 0, PAGE_SIZE);
+	kaddr[0] = 0x44000002U; /* sc */
+	__flush_dcache_icache(kaddr);
+	kunmap(page);
+	return page;
+}
+
+static struct vm_operations_struct pax_vm_ops = {
+	close:		pax_syscall_close,
+	nopage:		pax_syscall_nopage,
+};
+
+static void pax_insert_vma(struct vm_area_struct *vma, unsigned long addr)
+{
+	vma->vm_mm = current->mm;
+	vma->vm_start = addr;
+	vma->vm_end = addr + PAGE_SIZE;
+	vma->vm_flags = VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYEXEC;
+	vma->vm_page_prot = protection_map[vma->vm_flags & 0x0f];
+	vma->vm_ops = &pax_vm_ops;
+	vma->vm_pgoff = 0UL;
+	vma->vm_file = NULL;
+	vma->vm_private_data = NULL;
+	insert_vm_struct(current->mm, vma);
+	++current->mm->total_vm;
+}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+/*
+ * PaX: decide what to do with offenders (regs->nip = fault address)
+ *
+ * returns 1 when task should be killed
+ *         2 when patched GOT trampoline was detected
+ *         3 when patched PLT trampoline was detected
+ *         4 when unpatched PLT trampoline was detected
+ *         5 when legitimate ET_EXEC was detected
+ *         6 when sigreturn trampoline was detected
+ *         7 when rt_sigreturn trampoline was detected
+ */
+static int pax_handle_fetch_fault(struct pt_regs *regs)
+{
+	int err;
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (current->flags & PF_PAX_RANDEXEC) {
+		if (regs->nip >= current->mm->start_code &&
+		    regs->nip < current->mm->end_code)
+		{
+			if (regs->link == regs->nip)
+				return 1;
+
+			regs->nip += current->mm->delta_exec;
+			return 5;
+		}
+	}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUPLT
+	do { /* PaX: patched GOT emulation */
+		unsigned int blrl;
+
+		err = get_user(blrl, (unsigned int*)regs->nip);
+
+		if (!err && blrl == 0x4E800021U) {
+			unsigned long temp = regs->nip;
+
+			regs->nip = regs->link & 0xFFFFFFFCUL;
+			regs->link = temp + 4UL;
+			return 2;
+		}
+	} while (0);
+
+	do { /* PaX: patched PLT emulation #1 */
+		unsigned int b;
+
+		err = get_user(b, (unsigned int *)regs->nip);
+
+		if (!err && (b & 0xFC000003U) == 0x48000000U) {
+			regs->nip += (((b | 0xFC000000UL) ^ 0x02000000UL) + 0x02000000UL);
+			return 3;
+		}
+	} while (0);
+
+	do { /* PaX: unpatched PLT emulation #1 */
+		unsigned int li, b;
+
+		err = get_user(li, (unsigned int *)regs->nip);
+		err |= get_user(b, (unsigned int *)(regs->nip+4));
+
+		if (!err && (li & 0xFFFF0000U) == 0x39600000U && (b & 0xFC000003U) == 0x48000000U) {
+			unsigned int rlwinm, add, li2, addis2, mtctr, li3, addis3, bctr;
+                        unsigned long addr = b | 0xFC000000UL;
+
+                        addr = regs->nip + 4 + ((addr ^ 0x02000000UL) + 0x02000000UL);
+			err = get_user(rlwinm, (unsigned int*)addr);
+			err |= get_user(add, (unsigned int*)(addr+4));
+			err |= get_user(li2, (unsigned int*)(addr+8));
+			err |= get_user(addis2, (unsigned int*)(addr+12));
+			err |= get_user(mtctr, (unsigned int*)(addr+16));
+			err |= get_user(li3, (unsigned int*)(addr+20));
+			err |= get_user(addis3, (unsigned int*)(addr+24));
+			err |= get_user(bctr, (unsigned int*)(addr+28));
+
+			if (err)
+				break;
+
+			if (rlwinm == 0x556C083CU &&
+			    add == 0x7D6C5A14U &&
+			    (li2 & 0xFFFF0000U) == 0x39800000U &&
+			    (addis2 & 0xFFFF0000U) == 0x3D8C0000U &&
+			    mtctr == 0x7D8903A6U &&
+			    (li3 & 0xFFFF0000U) == 0x39800000U &&
+			    (addis3 & 0xFFFF0000U) == 0x3D8C0000U &&
+			    bctr == 0x4E800420U)
+			{
+				regs->gpr[PT_R11] = 3 * (((li | 0xFFFF0000UL) ^ 0x00008000UL) + 0x00008000UL);
+				regs->gpr[PT_R12] = (((li3 | 0xFFFF0000UL) ^ 0x00008000UL) + 0x00008000UL);
+				regs->gpr[PT_R12] += (addis3 & 0xFFFFU) << 16;
+				regs->ctr = (((li2 | 0xFFFF0000UL) ^ 0x00008000UL) + 0x00008000UL);
+				regs->ctr += (addis2 & 0xFFFFU) << 16;
+				regs->nip = regs->ctr;
+				return 4;
+			}
+		}
+	} while (0);
+
+#if 0
+	do { /* PaX: unpatched PLT emulation #2 */
+		unsigned int lis, lwzu, b, bctr;
+
+		err = get_user(lis, (unsigned int *)regs->nip);
+		err |= get_user(lwzu, (unsigned int *)(regs->nip+4));
+		err |= get_user(b, (unsigned int *)(regs->nip+8));
+		err |= get_user(bctr, (unsigned int *)(regs->nip+12));
+
+		if (err)
+			break;
+
+		if ((lis & 0xFFFF0000U) == 0x39600000U &&
+		    (lwzu & 0xU) == 0xU &&
+		    (b & 0xFC000003U) == 0x48000000U &&
+		    bctr == 0x4E800420U)
+		{
+			unsigned int addis, addi, rlwinm, add, li2, addis2, mtctr, li3, addis3, bctr;
+                        unsigned long addr = b | 0xFC000000UL;
+
+                        addr = regs->nip + 12 + ((addr ^ 0x02000000UL) + 0x02000000UL);
+			err = get_user(addis, (unsigned int*)addr);
+			err |= get_user(addi, (unsigned int*)(addr+4));
+			err |= get_user(rlwinm, (unsigned int*)(addr+8));
+			err |= get_user(add, (unsigned int*)(addr+12));
+			err |= get_user(li2, (unsigned int*)(addr+16));
+			err |= get_user(addis2, (unsigned int*)(addr+20));
+			err |= get_user(mtctr, (unsigned int*)(addr+24));
+			err |= get_user(li3, (unsigned int*)(addr+28));
+			err |= get_user(addis3, (unsigned int*)(addr+32));
+			err |= get_user(bctr, (unsigned int*)(addr+36));
+
+			if (err)
+				break;
+
+			if ((addis & 0xFFFF0000U) == 0x3D6B0000U &&
+			    (addi & 0xFFFF0000U) == 0x396B0000U &&
+			    rlwinm == 0x556C083CU &&
+			    add == 0x7D6C5A14U &&
+			    (li2 & 0xFFFF0000U) == 0x39800000U &&
+			    (addis2 & 0xFFFF0000U) == 0x3D8C0000U &&
+			    mtctr == 0x7D8903A6U &&
+			    (li3 & 0xFFFF0000U) == 0x39800000U &&
+			    (addis3 & 0xFFFF0000U) == 0x3D8C0000U &&
+			    bctr == 0x4E800420U)
+			{
+				regs->gpr[PT_R11] = 
+				regs->gpr[PT_R11] = 3 * (((li | 0xFFFF0000UL) ^ 0x00008000UL) + 0x00008000UL);
+				regs->gpr[PT_R12] = (((li3 | 0xFFFF0000UL) ^ 0x00008000UL) + 0x00008000UL);
+				regs->gpr[PT_R12] += (addis3 & 0xFFFFU) << 16;
+				regs->ctr = (((li2 | 0xFFFF0000UL) ^ 0x00008000UL) + 0x00008000UL);
+				regs->ctr += (addis2 & 0xFFFFU) << 16;
+				regs->nip = regs->ctr;
+				return 4;
+			}
+		}
+	} while (0);
+#endif
+
+	do { /* PaX: unpatched PLT emulation #3 */
+		unsigned int li, b;
+
+		err = get_user(li, (unsigned int *)regs->nip);
+		err |= get_user(b, (unsigned int *)(regs->nip+4));
+
+		if (!err && (li & 0xFFFF0000U) == 0x39600000U && (b & 0xFC000003U) == 0x48000000U) {
+			unsigned int addis, lwz, mtctr, bctr;
+			unsigned long addr = b | 0xFC000000UL;
+
+			addr = regs->nip + 4 + ((addr ^ 0x02000000UL) + 0x02000000UL);
+			err = get_user(addis, (unsigned int*)addr);
+			err |= get_user(lwz, (unsigned int*)(addr+4));
+			err |= get_user(mtctr, (unsigned int*)(addr+8));
+			err |= get_user(bctr, (unsigned int*)(addr+12));
+
+			if (err)
+				break;
+
+			if ((addis & 0xFFFF0000U) == 0x3D6B0000U &&
+			    (lwz & 0xFFFF0000U) == 0x816B0000U &&
+			    mtctr == 0x7D6903A6U &&
+			    bctr == 0x4E800420U)
+			{
+				unsigned int r11;
+
+				addr = (addis << 16) + (((li | 0xFFFF0000UL) ^ 0x00008000UL) + 0x00008000UL);
+				addr += (((lwz | 0xFFFF0000UL) ^ 0x00008000UL) + 0x00008000UL);
+
+				err = get_user(r11, (unsigned int*)addr);
+				if (err)
+					break;
+
+				regs->gpr[PT_R11] = r11;
+				regs->ctr = r11;
+				regs->nip = r11;
+				return 4;
+			}
+		}
+	} while (0);
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUSIGRT
+	do { /* PaX: sigreturn emulation */
+		unsigned int li, sc;
+
+		err = get_user(li, (unsigned int *)regs->nip);
+		err |= get_user(sc, (unsigned int *)(regs->nip+4));
+
+		if (!err && li == 0x38007777U && sc == 0x44000002U) {
+			struct vm_area_struct *vma;
+			unsigned long call_syscall;
+
+			down_read(&current->mm->mmap_sem);
+			call_syscall = current->mm->call_syscall;
+			up_read(&current->mm->mmap_sem);
+			if (likely(call_syscall))
+				goto emulate;
+
+			vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+
+			down_write(&current->mm->mmap_sem);
+			if (current->mm->call_syscall) {
+				call_syscall = current->mm->call_syscall;
+				up_write(&current->mm->mmap_sem);
+				if (vma) kmem_cache_free(vm_area_cachep, vma);
+				goto emulate;
+			}
+
+			call_syscall = get_unmapped_area(NULL, 0UL, PAGE_SIZE, 0UL, MAP_PRIVATE);
+			if (!vma || (call_syscall & ~PAGE_MASK)) {
+				up_write(&current->mm->mmap_sem);
+				if (vma) kmem_cache_free(vm_area_cachep, vma);
+				return 1;
+			}
+
+			pax_insert_vma(vma, call_syscall);
+			current->mm->call_syscall = call_syscall;
+			up_write(&current->mm->mmap_sem);
+
+emulate:
+			regs->gpr[PT_R0] = 0x7777UL;
+			regs->nip = call_syscall;
+			return 6;
+		}
+	} while (0);
+
+	do { /* PaX: rt_sigreturn emulation */
+		unsigned int li, sc;
+
+		err = get_user(li, (unsigned int *)regs->nip);
+		err |= get_user(sc, (unsigned int *)(regs->nip+4));
+
+		if (!err && li == 0x38006666U && sc == 0x44000002U) {
+			struct vm_area_struct *vma;
+			unsigned int call_syscall;
+
+			down_read(&current->mm->mmap_sem);
+			call_syscall = current->mm->call_syscall;
+			up_read(&current->mm->mmap_sem);
+			if (likely(call_syscall))
+				goto rt_emulate;
+
+			vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+
+			down_write(&current->mm->mmap_sem);
+			if (current->mm->call_syscall) {
+				call_syscall = current->mm->call_syscall;
+				up_write(&current->mm->mmap_sem);
+				if (vma) kmem_cache_free(vm_area_cachep, vma);
+				goto rt_emulate;
+			}
+
+			call_syscall = get_unmapped_area(NULL, 0UL, PAGE_SIZE, 0UL, MAP_PRIVATE);
+			if (!vma || (call_syscall & ~PAGE_MASK)) {
+				up_write(&current->mm->mmap_sem);
+				if (vma) kmem_cache_free(vm_area_cachep, vma);
+				return 1;
+			}
+
+			pax_insert_vma(vma, call_syscall);
+			current->mm->call_syscall = call_syscall;
+			up_write(&current->mm->mmap_sem);
+
+rt_emulate:
+			regs->gpr[PT_R0] = 0x6666UL;
+			regs->nip = call_syscall;
+			return 7;
+		}
+	} while (0);
+#endif
+
+        return 1;
+}
+
+void pax_report_insns(void *pc)
+{
+	unsigned long i;
+
+	printk(KERN_ERR "PAX: bytes at PC: ");
+	for (i = 0; i < 5; i++) {
+		unsigned int c;
+		if (get_user(c, (unsigned int*)pc+i)) {
+			printk("<invalid address>.");
+			break;
+		}
+		printk("%08x ", c);
+	}
+	printk("\n");
+}
+#endif
+
 /*
  * Check whether the instruction at regs->nip is a store using
  * an update addressing form which will update r1.
@@ -112,7 +469,7 @@
 	 * indicate errors in DSISR but can validly be set in SRR1.
 	 */
 	if (regs->trap == 0x400)
-		error_code &= 0x48200000;
+		error_code &= 0x58200000;
 	else
 		is_write = error_code & 0x02000000;
 #endif /* CONFIG_4xx || CONFIG_BOOKE */
@@ -245,6 +602,38 @@
 
 	/* User mode accesses cause a SIGSEGV */
 	if (user_mode(regs)) {
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+		if (current->flags & PF_PAX_PAGEEXEC) {
+			if ((regs->trap == 0x400) && (regs->nip == address)) {
+				switch (pax_handle_fetch_fault(regs)) {
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUPLT
+				case 2:
+				case 3:
+				case 4:
+					return;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+				case 5:
+					return;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUSIGRT
+				case 6:
+				case 7:
+					return;
+#endif
+
+				}
+
+				pax_report_fault(regs, (void*)regs->nip, (void*)regs->gpr[1]);
+				do_exit(SIGKILL);
+			}
+		}
+#endif
+
 		info.si_signo = SIGSEGV;
 		info.si_errno = 0;
 		info.si_code = code;
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/ppc/mm/init.c linux-2.4.27-leo/arch/ppc/mm/init.c
--- linux-2.4.27/arch/ppc/mm/init.c	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.27-leo/arch/ppc/mm/init.c	2004-09-17 03:19:36.000000000 +0100
@@ -126,6 +126,9 @@
 int do_check_pgt_cache(int low, int high)
 {
 	int freed = 0;
+
+	preempt_disable();
+
 	if (pgtable_cache_size > high) {
 		do {
                         if (pgd_quicklist) {
@@ -138,6 +141,9 @@
 			}
 		} while (pgtable_cache_size > low);
 	}
+
+	preempt_enable();
+
 	return freed;
 }
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/ppc/mm/tlb.c linux-2.4.27-leo/arch/ppc/mm/tlb.c
--- linux-2.4.27/arch/ppc/mm/tlb.c	2003-08-25 12:44:40.000000000 +0100
+++ linux-2.4.27-leo/arch/ppc/mm/tlb.c	2004-09-17 03:19:36.000000000 +0100
@@ -58,11 +58,14 @@
 	 * we can and should dispense with flush_tlb_all().
 	 *  -- paulus.
 	 */
+
+	preempt_disable();
 	local_flush_tlb_range(&init_mm, TASK_SIZE, ~0UL);
 
 #ifdef CONFIG_SMP
 	smp_send_tlb_invalidate(0);
 #endif /* CONFIG_SMP */
+	preempt_enable();
 }
 
 /*
@@ -73,8 +76,10 @@
 void
 local_flush_tlb_mm(struct mm_struct *mm)
 {
+	preempt_disable();
 	if (Hash == 0) {
 		_tlbia();
+		preempt_enable();
 		return;
 	}
 
@@ -88,6 +93,7 @@
 #ifdef CONFIG_SMP
 	smp_send_tlb_invalidate(0);
 #endif
+	preempt_enable();
 }
 
 void
@@ -97,8 +103,10 @@
 	pmd_t *pmd;
 	pte_t *pte;
 
+	preempt_disable();
 	if (Hash == 0) {
 		_tlbie(vmaddr);
+		preempt_enable();
 		return;
 	}
 	mm = (vmaddr < TASK_SIZE)? vma->vm_mm: &init_mm;
@@ -111,6 +119,7 @@
 #ifdef CONFIG_SMP
 	smp_send_tlb_invalidate(0);
 #endif
+	preempt_enable();
 }
 
 
@@ -127,13 +136,17 @@
 	unsigned long pmd_end;
 	unsigned int ctx = mm->context;
 
+	preempt_disable();
 	if (Hash == 0) {
 		_tlbia();
+		preempt_enable();
 		return;
 	}
 	start &= PAGE_MASK;
-	if (start >= end)
+	if (start >= end) {
+		preempt_enable();
 		return;
+	}
 	pmd = pmd_offset(pgd_offset(mm, start), start);
 	do {
 		pmd_end = (start + PGDIR_SIZE) & PGDIR_MASK;
@@ -156,4 +169,5 @@
 #ifdef CONFIG_SMP
 	smp_send_tlb_invalidate(0);
 #endif
+	preempt_enable();
 }
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/ppc64/kernel/ioctl32.c linux-2.4.27-leo/arch/ppc64/kernel/ioctl32.c
--- linux-2.4.27/arch/ppc64/kernel/ioctl32.c	2004-02-20 14:11:39.000000000 +0000
+++ linux-2.4.27-leo/arch/ppc64/kernel/ioctl32.c	2004-09-17 03:19:58.000000000 +0100
@@ -66,6 +66,7 @@
 #if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE)
 #include <linux/lvm.h>
 #endif /* LVM */
+#include <linux/dm-ioctl.h>
 
 #include <scsi/scsi.h>
 /* Ugly hack. */
@@ -1824,7 +1825,11 @@
 	 * To have permissions to do most of the vt ioctls, we either have
 	 * to be the owner of the tty, or super-user.
 	 */
+#ifdef CONFIG_GRKERNSEC
+	if (current->tty == tty || capable(CAP_SYS_TTY_CONFIG))
+#else
 	if (current->tty == tty || suser())
+#endif
 		return 1;
 	return 0;                                                    
 }
@@ -4408,6 +4413,22 @@
 COMPATIBLE_IOCTL(NBD_PRINT_DEBUG),
 COMPATIBLE_IOCTL(NBD_SET_SIZE_BLOCKS),
 COMPATIBLE_IOCTL(NBD_DISCONNECT),
+/* device-mapper */
+#if defined(CONFIG_BLK_DEV_DM) || defined(CONFIG_BLK_DEV_DM_MODULE)
+COMPATIBLE_IOCTL(DM_VERSION),
+COMPATIBLE_IOCTL(DM_REMOVE_ALL),
+COMPATIBLE_IOCTL(DM_DEV_CREATE),
+COMPATIBLE_IOCTL(DM_DEV_REMOVE),
+COMPATIBLE_IOCTL(DM_TABLE_LOAD),
+COMPATIBLE_IOCTL(DM_DEV_SUSPEND),
+COMPATIBLE_IOCTL(DM_DEV_RENAME),
+COMPATIBLE_IOCTL(DM_TABLE_DEPS),
+COMPATIBLE_IOCTL(DM_DEV_STATUS),
+COMPATIBLE_IOCTL(DM_TABLE_STATUS),
+COMPATIBLE_IOCTL(DM_DEV_WAIT),
+COMPATIBLE_IOCTL(DM_LIST_DEVICES),
+COMPATIBLE_IOCTL(DM_TABLE_CLEAR),
+#endif /* CONFIG_BLK_DEV_DM */
 /* Remove *PRIVATE in 2.5 */
 COMPATIBLE_IOCTL(SIOCDEVPRIVATE),
 COMPATIBLE_IOCTL(SIOCDEVPRIVATE+1),
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/s390/config.in linux-2.4.27-leo/arch/s390/config.in
--- linux-2.4.27/arch/s390/config.in	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.27-leo/arch/s390/config.in	2004-09-17 03:19:58.000000000 +0100
@@ -87,3 +87,11 @@
 
 source crypto/Config.in
 source lib/Config.in
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+	source grsecurity/Config.in
+fi
+endmenu
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/s390x/config.in linux-2.4.27-leo/arch/s390x/config.in
--- linux-2.4.27/arch/s390x/config.in	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.27-leo/arch/s390x/config.in	2004-09-17 03:19:58.000000000 +0100
@@ -91,3 +91,11 @@
 
 source crypto/Config.in
 source lib/Config.in
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+	source grsecurity/Config.in
+fi
+endmenu
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/s390x/kernel/ioctl32.c linux-2.4.27-leo/arch/s390x/kernel/ioctl32.c
--- linux-2.4.27/arch/s390x/kernel/ioctl32.c	2003-08-25 12:44:40.000000000 +0100
+++ linux-2.4.27-leo/arch/s390x/kernel/ioctl32.c	2004-09-17 03:19:08.000000000 +0100
@@ -30,6 +30,7 @@
 #include <linux/blk.h>
 #include <linux/elevator.h>
 #include <linux/raw.h>
+#include <linux/dm-ioctl.h>
 #include <asm/types.h>
 #include <asm/uaccess.h>
 #include <asm/dasd.h>
@@ -627,6 +628,20 @@
 
 	IOCTL32_DEFAULT(SIOCGSTAMP),
 
+	IOCTL32_DEFAULT(DM_VERSION),
+	IOCTL32_DEFAULT(DM_REMOVE_ALL),
+	IOCTL32_DEFAULT(DM_DEV_CREATE),
+	IOCTL32_DEFAULT(DM_DEV_REMOVE),
+	IOCTL32_DEFAULT(DM_TABLE_LOAD),
+	IOCTL32_DEFAULT(DM_DEV_SUSPEND),
+	IOCTL32_DEFAULT(DM_DEV_RENAME),
+	IOCTL32_DEFAULT(DM_TABLE_DEPS),
+	IOCTL32_DEFAULT(DM_DEV_STATUS),
+	IOCTL32_DEFAULT(DM_TABLE_STATUS),
+	IOCTL32_DEFAULT(DM_DEV_WAIT),
+	IOCTL32_DEFAULT(DM_LIST_DEVICES),
+	IOCTL32_DEFAULT(DM_TABLE_CLEAR),
+
 	IOCTL32_DEFAULT(LOOP_SET_FD),
 	IOCTL32_DEFAULT(LOOP_CLR_FD),
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/sh/config.in linux-2.4.27-leo/arch/sh/config.in
--- linux-2.4.27/arch/sh/config.in	2004-02-20 14:11:39.000000000 +0000
+++ linux-2.4.27-leo/arch/sh/config.in	2004-09-17 03:19:58.000000000 +0100
@@ -493,3 +493,11 @@
 
 source crypto/Config.in
 source lib/Config.in
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+	source grsecurity/Config.in
+fi
+endmenu
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/sparc/boot/Makefile linux-2.4.27-leo/arch/sparc/boot/Makefile
--- linux-2.4.27/arch/sparc/boot/Makefile	2002-08-03 01:39:43.000000000 +0100
+++ linux-2.4.27-leo/arch/sparc/boot/Makefile	2004-09-17 03:19:58.000000000 +0100
@@ -24,7 +24,7 @@
 
 BTOBJS := $(HEAD) init/main.o init/version.o init/do_mounts.o
 BTLIBS := $(CORE_FILES_NO_BTFIX) $(FILESYSTEMS) \
-	$(DRIVERS) $(NETWORKS)
+	$(DRIVERS) $(NETWORKS) $(GRSECURITY)
 
 # I wanted to make this depend upon BTOBJS so that a parallel
 # build would work, but this fails because $(HEAD) cannot work
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/sparc/config.in linux-2.4.27-leo/arch/sparc/config.in
--- linux-2.4.27/arch/sparc/config.in	2004-02-20 14:11:40.000000000 +0000
+++ linux-2.4.27-leo/arch/sparc/config.in	2004-09-17 03:19:58.000000000 +0100
@@ -282,3 +282,11 @@
 
 source crypto/Config.in
 source lib/Config.in
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+	source grsecurity/Config.in
+fi
+endmenu
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/sparc/kernel/ptrace.c linux-2.4.27-leo/arch/sparc/kernel/ptrace.c
--- linux-2.4.27/arch/sparc/kernel/ptrace.c	2002-08-03 01:39:43.000000000 +0100
+++ linux-2.4.27-leo/arch/sparc/kernel/ptrace.c	2004-09-17 03:19:58.000000000 +0100
@@ -17,6 +17,7 @@
 #include <linux/user.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
+#include <linux/grsecurity.h>
 
 #include <asm/pgtable.h>
 #include <asm/system.h>
@@ -310,6 +311,9 @@
 		goto out;
 	}
 
+	if(gr_handle_ptrace(child, request))
+		goto out_tsk;
+
 	if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH)
 	    || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) {
 		if (ptrace_attach(child)) {
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/sparc/kernel/sys_sparc.c linux-2.4.27-leo/arch/sparc/kernel/sys_sparc.c
--- linux-2.4.27/arch/sparc/kernel/sys_sparc.c	2003-08-25 12:44:40.000000000 +0100
+++ linux-2.4.27-leo/arch/sparc/kernel/sys_sparc.c	2004-09-17 03:19:58.000000000 +0100
@@ -20,6 +20,7 @@
 #include <linux/utsname.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 #include <asm/ipc.h>
@@ -54,6 +55,13 @@
 		return -ENOMEM;
 	if (ARCH_SUN4C_SUN4 && len > 0x20000000)
 		return -ENOMEM;
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDMMAP
+	if ((current->flags & PF_PAX_RANDMMAP) && (!addr || filp))
+		addr = TASK_UNMAPPED_BASE + current->mm->delta_mmap;
+	else
+#endif
+
 	if (!addr)
 		addr = TASK_UNMAPPED_BASE;
 
@@ -225,6 +233,11 @@
 	struct file * file = NULL;
 	unsigned long retval = -EBADF;
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (flags & MAP_MIRROR)
+		return -EINVAL;
+#endif
+
 	if (!(flags & MAP_ANONYMOUS)) {
 		file = fget(fd);
 		if (!file)
@@ -243,6 +256,12 @@
 	if (len > TASK_SIZE - PAGE_SIZE || addr + len > TASK_SIZE - PAGE_SIZE)
 		goto out_putf;
 
+	if (gr_handle_mmap(file, prot)) {
+		fput(file);
+		retval = -EACCES;
+		goto out;
+	}
+
 	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 
 	down_write(&current->mm->mmap_sem);
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/sparc/kernel/sys_sunos.c linux-2.4.27-leo/arch/sparc/kernel/sys_sunos.c
--- linux-2.4.27/arch/sparc/kernel/sys_sunos.c	2004-05-19 21:34:36.000000000 +0100
+++ linux-2.4.27-leo/arch/sparc/kernel/sys_sunos.c	2004-09-17 03:19:58.000000000 +0100
@@ -68,6 +68,11 @@
 	struct file * file = NULL;
 	unsigned long retval, ret_type;
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (flags & MAP_MIRROR)
+		return -EINVAL;
+#endif
+
 	if(flags & MAP_NORESERVE) {
 		static int cnt;
 		if (cnt++ < 10)
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/sparc/mm/fault.c linux-2.4.27-leo/arch/sparc/mm/fault.c
--- linux-2.4.27/arch/sparc/mm/fault.c	2004-09-17 02:38:43.000000000 +0100
+++ linux-2.4.27-leo/arch/sparc/mm/fault.c	2004-09-17 03:19:58.000000000 +0100
@@ -19,6 +19,9 @@
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
 #include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/compiler.h>
 
 #include <asm/system.h>
 #include <asm/segment.h>
@@ -219,6 +222,264 @@
 	return safe_compute_effective_address(regs, insn);
 }
 
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+void pax_emuplt_close(struct vm_area_struct * vma)
+{
+	vma->vm_mm->call_dl_resolve = 0UL;
+}
+
+static struct page* pax_emuplt_nopage(struct vm_area_struct *vma, unsigned long address, int write_access)
+{
+	struct page* page;
+	unsigned int *kaddr;
+
+	page = alloc_page(GFP_HIGHUSER);
+	if (!page)
+		return page;
+
+	kaddr = kmap(page);
+	memset(kaddr, 0, PAGE_SIZE);
+	kaddr[0] = 0x9DE3BFA8U; /* save */
+	flush_dcache_page(page);
+	kunmap(page);
+	return page;
+}
+
+static struct vm_operations_struct pax_vm_ops = {
+	close:		pax_emuplt_close,
+	nopage:		pax_emuplt_nopage,
+};
+
+static void pax_insert_vma(struct vm_area_struct *vma, unsigned long addr)
+{
+	vma->vm_mm = current->mm;
+	vma->vm_start = addr;
+	vma->vm_end = addr + PAGE_SIZE;
+	vma->vm_flags = VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYEXEC;
+	vma->vm_page_prot = protection_map[vma->vm_flags & 0x0f];
+	vma->vm_ops = &pax_vm_ops;
+	vma->vm_pgoff = 0UL;
+	vma->vm_file = NULL;
+	vma->vm_private_data = NULL;
+	insert_vm_struct(current->mm, vma);
+	++current->mm->total_vm;
+}
+
+/*
+ * PaX: decide what to do with offenders (regs->pc = fault address)
+ *
+ * returns 1 when task should be killed
+ *         2 when patched PLT trampoline was detected
+ *         3 when unpatched PLT trampoline was detected
+ *	   4 when legitimate ET_EXEC was detected
+ */
+static int pax_handle_fetch_fault(struct pt_regs *regs)
+{
+	int err;
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (current->flags & PF_PAX_RANDEXEC) {
+		if (regs->pc >= current->mm->start_code &&
+		    regs->pc < current->mm->end_code)
+		{
+			if (regs->u_regs[UREG_RETPC] + 8UL == regs->pc)
+				return 1;
+
+			regs->pc += current->mm->delta_exec;
+			if (regs->npc >= current->mm->start_code &&
+			    regs->npc < current->mm->end_code)
+				regs->npc += current->mm->delta_exec;
+			return 4;
+		}
+		if (regs->pc >= current->mm->start_code + current->mm->delta_exec &&
+		    regs->pc < current->mm->end_code + current->mm->delta_exec)
+		{
+			regs->pc -= current->mm->delta_exec;
+			if (regs->npc >= current->mm->start_code + current->mm->delta_exec &&
+			    regs->npc < current->mm->end_code + current->mm->delta_exec)
+				regs->npc -= current->mm->delta_exec;
+		}
+	}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUPLT
+	do { /* PaX: patched PLT emulation #1 */
+		unsigned int sethi1, sethi2, jmpl;
+
+		err = get_user(sethi1, (unsigned int *)regs->pc);
+		err |= get_user(sethi2, (unsigned int *)(regs->pc+4));
+		err |= get_user(jmpl, (unsigned int *)(regs->pc+8));
+
+		if (err)
+			break;
+
+		if ((sethi1 & 0xFFC00000U) == 0x03000000U &&
+		    (sethi2 & 0xFFC00000U) == 0x03000000U &&
+		    (jmpl & 0xFFFFE000U) == 0x81C06000U)
+		{
+			unsigned int addr;
+
+			regs->u_regs[UREG_G1] = (sethi2 & 0x003FFFFFU) << 10;
+			addr = regs->u_regs[UREG_G1];
+			addr += (((jmpl | 0xFFFFE000U) ^ 0x00001000U) + 0x00001000U);
+			regs->pc = addr;
+			regs->npc = addr+4;
+			return 2;
+		}
+	} while (0);
+
+	{ /* PaX: patched PLT emulation #2 */
+		unsigned int ba;
+
+		err = get_user(ba, (unsigned int *)regs->pc);
+
+		if (!err && (ba & 0xFFC00000U) == 0x30800000U) {
+			unsigned int addr;
+
+			addr = regs->pc + 4 + (((ba | 0xFFC00000U) ^ 0x00200000U) + 0x00200000U);
+			regs->pc = addr;
+			regs->npc = addr+4;
+			return 2;
+		}
+	}
+
+	do { /* PaX: patched PLT emulation #3 */
+		unsigned int sethi, jmpl, nop;
+
+		err = get_user(sethi, (unsigned int*)regs->pc);
+		err |= get_user(jmpl, (unsigned int*)(regs->pc+4));
+		err |= get_user(nop, (unsigned int*)(regs->pc+8));
+
+		if (err)
+			break;
+		if ((sethi & 0xFFC00000U) == 0x03000000U &&
+		    (jmpl & 0xFFFFE000U) == 0x81C06000U &&
+		    nop == 0x01000000U)
+		{
+			unsigned int addr;
+
+			addr = (sethi & 0x003FFFFFU) << 10;
+			regs->u_regs[UREG_G1] = addr;
+			addr += (((jmpl | 0xFFFFE000U) ^ 0x00001000U) + 0x00001000U);
+			regs->pc = addr;
+			regs->npc = addr+4;
+			return 2;
+		}
+	} while (0);
+
+	do { /* PaX: unpatched PLT emulation step 1 */
+		unsigned int sethi, ba, nop;
+
+		err = get_user(sethi, (unsigned int *)regs->pc);
+		err |= get_user(ba, (unsigned int *)(regs->pc+4));
+		err |= get_user(nop, (unsigned int *)(regs->pc+8));
+
+		if (err)
+			break;
+		if ((sethi & 0xFFC00000U) == 0x03000000U &&
+		    ((ba & 0xFFC00000U) == 0x30800000U || (ba & 0xFFF80000U) == 0x30680000U) &&
+		    nop == 0x01000000U)
+		{
+			unsigned int addr, save, call;
+
+			if ((ba & 0xFFC00000U) == 0x30800000U)
+				addr = regs->pc + 4 + ((((ba | 0xFFC00000U) ^ 0x00200000U) + 0x00200000U) << 2);
+			else
+				addr = regs->pc + 4 + ((((ba | 0xFFF80000U) ^ 0x00040000U) + 0x00040000U) << 2);
+
+			err = get_user(save, (unsigned int *)addr);
+			err |= get_user(call, (unsigned int *)(addr+4));
+			err |= get_user(nop, (unsigned int *)(addr+8)); 
+			if (err)
+				break;
+
+			if (save == 0x9DE3BFA8U &&
+			    (call & 0xC0000000U) == 0x40000000U &&
+			    nop == 0x01000000U)
+			{
+				struct vm_area_struct *vma;
+				unsigned long call_dl_resolve;
+
+				down_read(&current->mm->mmap_sem);
+				call_dl_resolve = current->mm->call_dl_resolve;
+				up_read(&current->mm->mmap_sem);
+				if (likely(call_dl_resolve))
+					goto emulate;
+
+				vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+
+				down_write(&current->mm->mmap_sem);
+				if (current->mm->call_dl_resolve) {
+					call_dl_resolve = current->mm->call_dl_resolve;
+					up_write(&current->mm->mmap_sem);
+					if (vma) kmem_cache_free(vm_area_cachep, vma);
+					goto emulate;
+				}
+
+				call_dl_resolve = get_unmapped_area(NULL, 0UL, PAGE_SIZE, 0UL, MAP_PRIVATE);
+				if (!vma || (call_dl_resolve & ~PAGE_MASK)) {
+					up_write(&current->mm->mmap_sem);
+					if (vma) kmem_cache_free(vm_area_cachep, vma);
+					return 1;
+				}
+
+				pax_insert_vma(vma, call_dl_resolve);
+				current->mm->call_dl_resolve = call_dl_resolve;
+				up_write(&current->mm->mmap_sem);
+
+emulate:
+				regs->u_regs[UREG_G1] = (sethi & 0x003FFFFFU) << 10;
+				regs->pc = call_dl_resolve;
+				regs->npc = addr+4;
+				return 3;
+			}
+		}
+	} while (0);
+
+	do { /* PaX: unpatched PLT emulation step 2 */
+		unsigned int save, call, nop;
+
+		err = get_user(save, (unsigned int*)(regs->pc-4));
+		err |= get_user(call, (unsigned int*)regs->pc);
+		err |= get_user(nop, (unsigned int*)(regs->pc+4));
+		if (err)
+			break;
+
+		if (save == 0x9DE3BFA8U &&
+		    (call & 0xC0000000U) == 0x40000000U &&
+		    nop == 0x01000000U)
+		{
+			unsigned int dl_resolve = regs->pc + ((((call | 0xC0000000U) ^ 0x20000000U) + 0x20000000U) << 2);
+
+			regs->u_regs[UREG_RETPC] = regs->pc;
+			regs->pc = dl_resolve;
+			regs->npc = dl_resolve+4;
+			return 3;
+		}
+	} while (0);
+
+#endif
+
+	return 1;
+}
+
+void pax_report_insns(void *pc)
+{
+	unsigned long i;
+
+	printk(KERN_ERR "PAX: bytes at PC: ");
+	for (i = 0; i < 5; i++) {
+		unsigned int c;
+		if (get_user(c, (unsigned int*)pc+i)) {
+			printk("<invalid address>.");
+			break;
+		}
+		printk("%08x ", c);
+	}
+	printk("\n");
+}
+#endif
+
 asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write,
 			       unsigned long address)
 {
@@ -282,6 +543,29 @@
 		if(!(vma->vm_flags & VM_WRITE))
 			goto bad_area;
 	} else {
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+		if ((current->flags & PF_PAX_PAGEEXEC) && text_fault && !(vma->vm_flags & VM_EXEC)) {
+			up_read(&mm->mmap_sem);
+			switch (pax_handle_fetch_fault(regs)) {
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUPLT
+			case 2:
+			case 3:
+				return;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+			case 4:
+				return;
+#endif
+
+			}
+			pax_report_fault(regs, (void*)regs->pc, (void*)regs->u_regs[UREG_FP]);
+			do_exit(SIGKILL);
+		}
+#endif
+
 		/* Allow reads even for write-only mappings */
 		if(!(vma->vm_flags & (VM_READ | VM_EXEC)))
 			goto bad_area;
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/sparc/mm/init.c linux-2.4.27-leo/arch/sparc/mm/init.c
--- linux-2.4.27/arch/sparc/mm/init.c	2002-11-28 23:53:12.000000000 +0000
+++ linux-2.4.27-leo/arch/sparc/mm/init.c	2004-09-17 03:19:58.000000000 +0100
@@ -350,17 +350,17 @@
 
 	/* Initialize the protection map with non-constant, MMU dependent values. */
 	protection_map[0] = PAGE_NONE;
-	protection_map[1] = PAGE_READONLY;
-	protection_map[2] = PAGE_COPY;
-	protection_map[3] = PAGE_COPY;
+	protection_map[1] = PAGE_READONLY_NOEXEC;
+	protection_map[2] = PAGE_COPY_NOEXEC;
+	protection_map[3] = PAGE_COPY_NOEXEC;
 	protection_map[4] = PAGE_READONLY;
 	protection_map[5] = PAGE_READONLY;
 	protection_map[6] = PAGE_COPY;
 	protection_map[7] = PAGE_COPY;
 	protection_map[8] = PAGE_NONE;
-	protection_map[9] = PAGE_READONLY;
-	protection_map[10] = PAGE_SHARED;
-	protection_map[11] = PAGE_SHARED;
+	protection_map[9] = PAGE_READONLY_NOEXEC;
+	protection_map[10] = PAGE_SHARED_NOEXEC;
+	protection_map[11] = PAGE_SHARED_NOEXEC;
 	protection_map[12] = PAGE_READONLY;
 	protection_map[13] = PAGE_READONLY;
 	protection_map[14] = PAGE_SHARED;
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/sparc/mm/srmmu.c linux-2.4.27-leo/arch/sparc/mm/srmmu.c
--- linux-2.4.27/arch/sparc/mm/srmmu.c	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.27-leo/arch/sparc/mm/srmmu.c	2004-09-17 03:19:58.000000000 +0100
@@ -2047,6 +2047,13 @@
 	BTFIXUPSET_INT(page_shared, pgprot_val(SRMMU_PAGE_SHARED));
 	BTFIXUPSET_INT(page_copy, pgprot_val(SRMMU_PAGE_COPY));
 	BTFIXUPSET_INT(page_readonly, pgprot_val(SRMMU_PAGE_RDONLY));
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+	BTFIXUPSET_INT(page_shared_noexec, pgprot_val(SRMMU_PAGE_SHARED_NOEXEC));
+	BTFIXUPSET_INT(page_copy_noexec, pgprot_val(SRMMU_PAGE_COPY_NOEXEC));
+	BTFIXUPSET_INT(page_readonly_noexec, pgprot_val(SRMMU_PAGE_RDONLY_NOEXEC));
+#endif
+
 	BTFIXUPSET_INT(page_kernel, pgprot_val(SRMMU_PAGE_KERNEL));
 	page_kernel = pgprot_val(SRMMU_PAGE_KERNEL);
 	pg_iobits = SRMMU_VALID | SRMMU_WRITE | SRMMU_REF;
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/sparc64/config.in linux-2.4.27-leo/arch/sparc64/config.in
--- linux-2.4.27/arch/sparc64/config.in	2004-09-17 02:38:43.000000000 +0100
+++ linux-2.4.27-leo/arch/sparc64/config.in	2004-09-17 03:19:58.000000000 +0100
@@ -320,3 +320,11 @@
 
 source crypto/Config.in
 source lib/Config.in
+
+mainmenu_option next_comment
+comment 'Grsecurity'
+bool 'Grsecurity' CONFIG_GRKERNSEC
+if [ "$CONFIG_GRKERNSEC" = "y" ]; then
+	source grsecurity/Config.in
+fi
+endmenu
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/sparc64/kernel/ioctl32.c linux-2.4.27-leo/arch/sparc64/kernel/ioctl32.c
--- linux-2.4.27/arch/sparc64/kernel/ioctl32.c	2004-09-17 02:38:43.000000000 +0100
+++ linux-2.4.27-leo/arch/sparc64/kernel/ioctl32.c	2004-09-17 03:19:58.000000000 +0100
@@ -56,6 +56,7 @@
 #if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE)
 #include <linux/lvm.h>
 #endif /* LVM */
+#include <linux/dm-ioctl.h>
 
 #include <scsi/scsi.h>
 /* Ugly hack. */
@@ -2048,7 +2049,11 @@
 	 * To have permissions to do most of the vt ioctls, we either have
 	 * to be the owner of the tty, or super-user.
 	 */
+#ifdef CONFIG_GRKERNSEC
+	if (current->tty == tty || capable(CAP_SYS_TTY_CONFIG))
+#else
 	if (current->tty == tty || suser())
+#endif
 		return 1;
 	return 0;                                                    
 }
@@ -5090,6 +5095,22 @@
 COMPATIBLE_IOCTL(NBD_PRINT_DEBUG)
 COMPATIBLE_IOCTL(NBD_SET_SIZE_BLOCKS)
 COMPATIBLE_IOCTL(NBD_DISCONNECT)
+/* device-mapper */
+#if defined(CONFIG_BLK_DEV_DM) || defined(CONFIG_BLK_DEV_DM_MODULE)
+COMPATIBLE_IOCTL(DM_VERSION)
+COMPATIBLE_IOCTL(DM_REMOVE_ALL)
+COMPATIBLE_IOCTL(DM_DEV_CREATE)
+COMPATIBLE_IOCTL(DM_DEV_REMOVE)
+COMPATIBLE_IOCTL(DM_TABLE_LOAD)
+COMPATIBLE_IOCTL(DM_DEV_SUSPEND)
+COMPATIBLE_IOCTL(DM_DEV_RENAME)
+COMPATIBLE_IOCTL(DM_TABLE_DEPS)
+COMPATIBLE_IOCTL(DM_DEV_STATUS)
+COMPATIBLE_IOCTL(DM_TABLE_STATUS)
+COMPATIBLE_IOCTL(DM_DEV_WAIT)
+COMPATIBLE_IOCTL(DM_LIST_DEVICES)
+COMPATIBLE_IOCTL(DM_TABLE_CLEAR)
+#endif /* CONFIG_BLK_DEV_DM */
 /* Linux-1394 */
 #if defined(CONFIG_IEEE1394) || defined(CONFIG_IEEE1394_MODULE)
 COMPATIBLE_IOCTL(AMDTP_IOC_CHANNEL)
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/sparc64/kernel/itlb_base.S linux-2.4.27-leo/arch/sparc64/kernel/itlb_base.S
--- linux-2.4.27/arch/sparc64/kernel/itlb_base.S	2003-06-13 15:51:32.000000000 +0100
+++ linux-2.4.27-leo/arch/sparc64/kernel/itlb_base.S	2004-09-17 03:19:58.000000000 +0100
@@ -41,7 +41,9 @@
 	CREATE_VPTE_OFFSET2(%g4, %g6)			! Create VPTE offset
 	ldxa		[%g3 + %g6] ASI_P, %g5		! Load VPTE
 1:	brgez,pn	%g5, 3f				! Not valid, branch out
-	 nop						! Delay-slot
+	and		%g5, _PAGE_EXEC, %g4
+	brz,pn		%g4, 3f				! Not executable, branch out
+	nop						! Delay-slot
 2:	stxa		%g5, [%g0] ASI_ITLB_DATA_IN	! Load PTE into TLB
 	retry						! Trap return
 3:	rdpr		%pstate, %g4			! Move into alternate globals
@@ -74,8 +76,6 @@
 	nop
 	nop
 	nop
-	nop
-	nop
 	CREATE_VPTE_NOP
 
 #undef CREATE_VPTE_OFFSET1
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/sparc64/kernel/ptrace.c linux-2.4.27-leo/arch/sparc64/kernel/ptrace.c
--- linux-2.4.27/arch/sparc64/kernel/ptrace.c	2002-11-28 23:53:12.000000000 +0000
+++ linux-2.4.27-leo/arch/sparc64/kernel/ptrace.c	2004-09-17 03:19:58.000000000 +0100
@@ -18,6 +18,7 @@
 #include <linux/user.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
+#include <linux/grsecurity.h>
 
 #include <asm/asi.h>
 #include <asm/pgtable.h>
@@ -161,6 +162,11 @@
 		goto out;
 	}
 
+	if (gr_handle_ptrace(child, (long)request)) {
+		pt_error_return(regs, EPERM);
+		goto out_tsk;
+	}
+
 	if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH)
 	    || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) {
 		if (ptrace_attach(child)) {
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/sparc64/kernel/sys_sparc32.c linux-2.4.27-leo/arch/sparc64/kernel/sys_sparc32.c
--- linux-2.4.27/arch/sparc64/kernel/sys_sparc32.c	2004-09-17 02:38:43.000000000 +0100
+++ linux-2.4.27-leo/arch/sparc64/kernel/sys_sparc32.c	2004-09-17 03:19:58.000000000 +0100
@@ -52,6 +52,8 @@
 #include <linux/sysctl.h>
 #include <linux/dnotify.h>
 #include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/random.h>
+#include <linux/grsecurity.h>
 
 #include <asm/types.h>
 #include <asm/ipc.h>
@@ -3239,8 +3241,18 @@
 	struct file * file;
 	int retval;
 	int i;
+#ifdef CONFIG_GRKERNSEC
+	struct file *old_exec_file;
+	struct acl_subject_label *old_acl;
+	struct rlimit old_rlim[RLIM_NLIMITS];
+#endif
 
 	bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDUSTACK
+	bprm.p -= (get_random_long() & ~(sizeof(void *)-1)) & ~PAGE_MASK;
+#endif
+
 	memset(bprm.page, 0, MAX_ARG_PAGES * sizeof(bprm.page[0]));
 
 	file = open_exec(filename);
@@ -3249,6 +3261,20 @@
 	if (IS_ERR(file))
 		return retval;
 
+	gr_learn_resource(current, RLIMIT_NPROC, atomic_read(&current->user->processes), 1);
+
+	if (gr_handle_nproc()) {
+		allow_write_access(file);
+		fput(file);
+		return -EAGAIN;
+	}
+
+	if (!gr_acl_handle_execve(file->f_dentry, file->f_vfsmnt)) {
+		allow_write_access(file);
+		fput(file);
+		return -EACCES;
+	}
+
 	bprm.file = file;
 	bprm.filename = filename;
 	bprm.sh_bang = 0;
@@ -3269,11 +3295,24 @@
 	if (retval < 0)
 		goto out;
 	
+	if(!gr_tpe_allow(file)) {
+		retval = -EACCES;
+		goto out;
+	}
+
+	if (gr_check_crash_exec(file)) {
+		retval = -EACCES;
+		goto out;
+	}
+
 	retval = copy_strings_kernel(1, &bprm.filename, &bprm);
 	if (retval < 0)
 		goto out;
 
 	bprm.exec = bprm.p;
+
+	gr_log_chroot_exec(file->f_dentry, file->f_vfsmnt);
+
 	retval = copy_strings32(bprm.envc, envp, &bprm);
 	if (retval < 0)
 		goto out;
@@ -3282,11 +3321,32 @@
 	if (retval < 0)
 		goto out;
 
+#ifdef CONFIG_GRKERNSEC
+	old_acl = current->acl;
+	memcpy(old_rlim, current->rlim, sizeof(old_rlim));
+	old_exec_file = current->exec_file;
+	get_file(file);
+	current->exec_file = file;
+#endif
+
+        gr_set_proc_label(file->f_dentry, file->f_vfsmnt);
+
 	retval = search_binary_handler(&bprm, regs);
-	if (retval >= 0)
+	if (retval >= 0) {
+#ifdef CONFIG_GRKERNSEC
+		if (old_exec_file)
+			fput(old_exec_file);
+#endif
 		/* execve success */
 		return retval;
+	}
 
+#ifdef CONFIG_GRKERNSEC
+	current->acl = old_acl;
+	memcpy(current->rlim, old_rlim, sizeof(old_rlim));
+	fput(current->exec_file);
+	current->exec_file = old_exec_file;
+#endif
 out:
 	/* Something went wrong, return the inode and free the argument pages*/
 	allow_write_access(bprm.file);
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/sparc64/kernel/sys_sparc.c linux-2.4.27-leo/arch/sparc64/kernel/sys_sparc.c
--- linux-2.4.27/arch/sparc64/kernel/sys_sparc.c	2003-08-25 12:44:40.000000000 +0100
+++ linux-2.4.27-leo/arch/sparc64/kernel/sys_sparc.c	2004-09-17 03:19:58.000000000 +0100
@@ -24,6 +24,7 @@
 #include <linux/slab.h>
 #include <linux/ipc.h>
 #include <linux/personality.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 #include <asm/ipc.h>
@@ -63,6 +64,13 @@
 		task_size = 0xf0000000UL;
 	if (len > task_size || len > -PAGE_OFFSET)
 		return -ENOMEM;
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDMMAP
+	if ((current->flags & PF_PAX_RANDMMAP) && (!addr || filp))
+		addr = TASK_UNMAPPED_BASE + current->mm->delta_mmap;
+	else
+#endif
+
 	if (!addr)
 		addr = TASK_UNMAPPED_BASE;
 
@@ -289,11 +297,22 @@
 	struct file * file = NULL;
 	unsigned long retval = -EBADF;
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (flags & MAP_MIRROR)
+		return -EINVAL;
+#endif
+
 	if (!(flags & MAP_ANONYMOUS)) {
 		file = fget(fd);
 		if (!file)
 			goto out;
 	}
+
+	if (gr_handle_mmap(file, prot)) {
+		retval = -EACCES;
+		goto out_putf;
+	}
+
 	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 	len = PAGE_ALIGN(len);
 	retval = -EINVAL;
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/sparc64/kernel/sys_sunos32.c linux-2.4.27-leo/arch/sparc64/kernel/sys_sunos32.c
--- linux-2.4.27/arch/sparc64/kernel/sys_sunos32.c	2004-09-17 02:38:43.000000000 +0100
+++ linux-2.4.27-leo/arch/sparc64/kernel/sys_sunos32.c	2004-09-17 03:19:58.000000000 +0100
@@ -68,6 +68,11 @@
 	struct file *file = NULL;
 	unsigned long retval, ret_type;
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (flags & MAP_MIRROR)
+		return -EINVAL;
+#endif
+
 	if(flags & MAP_NORESERVE) {
 		static int cnt;
 		if (cnt++ < 10)
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/sparc64/mm/fault.c linux-2.4.27-leo/arch/sparc64/mm/fault.c
--- linux-2.4.27/arch/sparc64/mm/fault.c	2004-09-17 02:38:43.000000000 +0100
+++ linux-2.4.27-leo/arch/sparc64/mm/fault.c	2004-09-17 03:19:58.000000000 +0100
@@ -16,6 +16,9 @@
 #include <linux/smp_lock.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/compiler.h>
 
 #include <asm/page.h>
 #include <asm/pgtable.h>
@@ -306,6 +309,363 @@
 	unhandled_fault (address, current, regs);
 }
 
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+#ifdef CONFIG_GRKERNSEC_PAX_EMUPLT
+static void pax_emuplt_close(struct vm_area_struct * vma)
+{
+	vma->vm_mm->call_dl_resolve = 0UL;
+}
+
+static struct page* pax_emuplt_nopage(struct vm_area_struct *vma, unsigned long address, int write_access)
+{
+	struct page* page;
+	unsigned int *kaddr;
+
+	page = alloc_page(GFP_HIGHUSER);
+	if (!page)
+		return page;
+
+	kaddr = kmap(page);
+	memset(kaddr, 0, PAGE_SIZE);
+	kaddr[0] = 0x9DE3BFA8U; /* save */
+	flush_dcache_page(page);
+	kunmap(page);
+	return page;
+}
+
+static struct vm_operations_struct pax_vm_ops = {
+	close:		pax_emuplt_close,
+	nopage:		pax_emuplt_nopage,
+};
+
+static void pax_insert_vma(struct vm_area_struct *vma, unsigned long addr)
+{
+	vma->vm_mm = current->mm;
+	vma->vm_start = addr;
+	vma->vm_end = addr + PAGE_SIZE;
+	vma->vm_flags = VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYEXEC;
+	vma->vm_page_prot = protection_map[vma->vm_flags & 0x0f];
+	vma->vm_ops = &pax_vm_ops;
+	vma->vm_pgoff = 0UL; 
+	vma->vm_file = NULL;
+	vma->vm_private_data = NULL;
+	insert_vm_struct(current->mm, vma);
+	++current->mm->total_vm;
+}
+#endif
+
+/*
+ * PaX: decide what to do with offenders (regs->tpc = fault address)
+ *
+ * returns 1 when task should be killed
+ *         2 when patched PLT trampoline was detected
+ *         3 when unpatched PLT trampoline was detected
+ *	   4 when legitimate ET_EXEC was detected
+ */
+static int pax_handle_fetch_fault(struct pt_regs *regs)
+{
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUPLT
+	int err;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (current->flags & PF_PAX_RANDEXEC) {
+		if (regs->tpc >= current->mm->start_code &&
+		    regs->tpc < current->mm->end_code)
+		{
+			if (regs->u_regs[UREG_RETPC] + 8UL == regs->tpc)
+				return 1;
+
+			regs->tpc += current->mm->delta_exec;
+			if (regs->tnpc >= current->mm->start_code &&
+			    regs->tnpc < current->mm->end_code)
+				regs->tnpc += current->mm->delta_exec;
+			return 4;
+		}
+		if (regs->tpc >= current->mm->start_code + current->mm->delta_exec &&
+		    regs->tpc < current->mm->end_code + current->mm->delta_exec)
+		{
+			regs->tpc -= current->mm->delta_exec;
+			if (regs->tnpc >= current->mm->start_code + current->mm->delta_exec &&
+			    regs->tnpc < current->mm->end_code + current->mm->delta_exec)
+				regs->tnpc -= current->mm->delta_exec;
+		}
+	}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUPLT
+	do { /* PaX: patched PLT emulation #1 */
+		unsigned int sethi1, sethi2, jmpl;
+
+		err = get_user(sethi1, (unsigned int*)regs->tpc);
+		err |= get_user(sethi2, (unsigned int*)(regs->tpc+4));
+		err |= get_user(jmpl, (unsigned int*)(regs->tpc+8));
+
+		if (err)
+			break;
+
+		if ((sethi1 & 0xFFC00000U) == 0x03000000U &&
+		    (sethi2 & 0xFFC00000U) == 0x03000000U &&
+		    (jmpl & 0xFFFFE000U) == 0x81C06000U)
+		{
+			unsigned long addr;
+
+			regs->u_regs[UREG_G1] = (sethi2 & 0x003FFFFFU) << 10;
+			addr = regs->u_regs[UREG_G1];
+			addr += (((jmpl | 0xFFFFFFFFFFFFE000UL) ^ 0x00001000UL) + 0x00001000UL);
+			regs->tpc = addr;
+			regs->tnpc = addr+4;
+			return 2;
+		}
+	} while (0);
+
+	{ /* PaX: patched PLT emulation #2 */
+		unsigned int ba;
+
+		err = get_user(ba, (unsigned int*)regs->tpc);
+
+		if (!err && (ba & 0xFFC00000U) == 0x30800000U) {
+			unsigned long addr;
+
+			addr = regs->tpc + 4 + (((ba | 0xFFFFFFFFFFC00000UL) ^ 0x00200000UL) + 0x00200000UL);
+			regs->tpc = addr;
+			regs->tnpc = addr+4;
+			return 2;
+		}
+	}
+
+	do { /* PaX: patched PLT emulation #3 */
+		unsigned int sethi, jmpl, nop;
+
+		err = get_user(sethi, (unsigned int*)regs->tpc);
+		err |= get_user(jmpl, (unsigned int*)(regs->tpc+4));
+		err |= get_user(nop, (unsigned int*)(regs->tpc+8));
+
+		if (err)
+			break;
+
+		if ((sethi & 0xFFC00000U) == 0x03000000U &&
+		    (jmpl & 0xFFFFE000U) == 0x81C06000U &&
+		    nop == 0x01000000U)
+		{
+			unsigned long addr;
+
+			addr = (sethi & 0x003FFFFFU) << 10;
+			regs->u_regs[UREG_G1] = addr;
+			addr += (((jmpl | 0xFFFFFFFFFFFFE000UL) ^ 0x00001000UL) + 0x00001000UL);
+			regs->tpc = addr;
+			regs->tnpc = addr+4;
+			return 2;
+		}
+	} while (0);
+
+	do { /* PaX: patched PLT emulation #4 */
+		unsigned int mov1, call, mov2;
+
+		err = get_user(mov1, (unsigned int*)regs->tpc);
+		err |= get_user(call, (unsigned int*)(regs->tpc+4));
+		err |= get_user(mov2, (unsigned int*)(regs->tpc+8));
+
+		if (err)
+			break;
+
+		if (mov1 == 0x8210000FU &&
+		    (call & 0xC0000000U) == 0x40000000U &&
+		    mov2 == 0x9E100001U)
+		{
+			unsigned long addr;
+
+			regs->u_regs[UREG_G1] = regs->u_regs[UREG_RETPC];
+			addr = regs->tpc + 4 + ((((call | 0xFFFFFFFFC0000000UL) ^ 0x20000000UL) + 0x20000000UL) << 2);
+			regs->tpc = addr;
+			regs->tnpc = addr+4;
+			return 2;
+		}
+	} while (0);
+
+	do { /* PaX: patched PLT emulation #5 */
+		unsigned int sethi1, sethi2, or1, or2, sllx, jmpl, nop;
+
+		err = get_user(sethi1, (unsigned int*)regs->tpc);
+		err |= get_user(sethi2, (unsigned int*)(regs->tpc+4));
+		err |= get_user(or1, (unsigned int*)(regs->tpc+8));
+		err |= get_user(or2, (unsigned int*)(regs->tpc+12));
+		err |= get_user(sllx, (unsigned int*)(regs->tpc+16));
+		err |= get_user(jmpl, (unsigned int*)(regs->tpc+20));
+		err |= get_user(nop, (unsigned int*)(regs->tpc+24));
+
+		if (err)
+			break;
+
+		if ((sethi1 & 0xFFC00000U) == 0x03000000U &&
+		    (sethi2 & 0xFFC00000U) == 0x0B000000U &&
+		    (or1 & 0xFFFFE000U) == 0x82106000U &&
+		    (or2 & 0xFFFFE000U) == 0x8A116000U &&
+		    sllx == 0x83287020 &&
+		    jmpl == 0x81C04005U &&
+		    nop == 0x01000000U)
+		{
+			unsigned long addr;
+
+			regs->u_regs[UREG_G1] = ((sethi1 & 0x003FFFFFU) << 10) | (or1 & 0x000003FFU);
+			regs->u_regs[UREG_G1] <<= 32;
+			regs->u_regs[UREG_G5] = ((sethi2 & 0x003FFFFFU) << 10) | (or2 & 0x000003FFU);
+			addr = regs->u_regs[UREG_G1] + regs->u_regs[UREG_G5];
+			regs->tpc = addr;
+			regs->tnpc = addr+4;
+			return 2;
+		}
+	} while (0);
+
+	do { /* PaX: patched PLT emulation #6 */
+		unsigned int sethi1, sethi2, sllx, or,  jmpl, nop;
+
+		err = get_user(sethi1, (unsigned int*)regs->tpc);
+		err |= get_user(sethi2, (unsigned int*)(regs->tpc+4));
+		err |= get_user(sllx, (unsigned int*)(regs->tpc+8));
+		err |= get_user(or, (unsigned int*)(regs->tpc+12));
+		err |= get_user(jmpl, (unsigned int*)(regs->tpc+16));
+		err |= get_user(nop, (unsigned int*)(regs->tpc+20));
+
+		if (err)
+			break;
+
+		if ((sethi1 & 0xFFC00000U) == 0x03000000U &&
+		    (sethi2 & 0xFFC00000U) == 0x0B000000U &&
+		    sllx == 0x83287020 &&
+		    (or & 0xFFFFE000U) == 0x8A116000U &&
+		    jmpl == 0x81C04005U &&
+		    nop == 0x01000000U)
+		{
+			unsigned long addr;
+
+			regs->u_regs[UREG_G1] = (sethi1 & 0x003FFFFFU) << 10;
+			regs->u_regs[UREG_G1] <<= 32;
+			regs->u_regs[UREG_G5] = ((sethi2 & 0x003FFFFFU) << 10) | (or & 0x3FFU);
+			addr = regs->u_regs[UREG_G1] + regs->u_regs[UREG_G5];
+			regs->tpc = addr;
+			regs->tnpc = addr+4;
+			return 2;
+		}
+	} while (0);
+
+	do { /* PaX: unpatched PLT emulation step 1 */
+		unsigned int sethi, ba, nop;
+
+		err = get_user(sethi, (unsigned int*)regs->tpc);
+		err |= get_user(ba, (unsigned int*)(regs->tpc+4));
+		err |= get_user(nop, (unsigned int*)(regs->tpc+8));
+
+		if (err)
+			break;
+
+		if ((sethi & 0xFFC00000U) == 0x03000000U &&
+		    ((ba & 0xFFC00000U) == 0x30800000U || (ba & 0xFFF80000U) == 0x30680000U) &&
+		    nop == 0x01000000U)
+		{
+			unsigned long addr;
+			unsigned int save, call;
+
+			if ((ba & 0xFFC00000U) == 0x30800000U)
+				addr = regs->tpc + 4 + ((((ba | 0xFFFFFFFFFFC00000UL) ^ 0x00200000UL) + 0x00200000UL) << 2);
+			else
+				addr = regs->tpc + 4 + ((((ba | 0xFFFFFFFFFFF80000UL) ^ 0x00040000UL) + 0x00040000UL) << 2);
+
+			err = get_user(save, (unsigned int*)addr);
+			err |= get_user(call, (unsigned int*)(addr+4));
+			err |= get_user(nop, (unsigned int*)(addr+8));
+
+			if (err)
+				break;
+
+			if (save == 0x9DE3BFA8U &&
+			    (call & 0xC0000000U) == 0x40000000U &&
+			    nop == 0x01000000U)
+			{
+				struct vm_area_struct *vma;
+				unsigned long call_dl_resolve;
+
+				down_read(&current->mm->mmap_sem);
+				call_dl_resolve = current->mm->call_dl_resolve;
+				up_read(&current->mm->mmap_sem);
+				if (likely(call_dl_resolve))
+					goto emulate;
+
+				vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+
+				down_write(&current->mm->mmap_sem);
+				if (current->mm->call_dl_resolve) {
+					call_dl_resolve = current->mm->call_dl_resolve;
+					up_write(&current->mm->mmap_sem);
+					if (vma) kmem_cache_free(vm_area_cachep, vma);
+					goto emulate;
+				}
+
+				call_dl_resolve = get_unmapped_area(NULL, 0UL, PAGE_SIZE, 0UL, MAP_PRIVATE);
+				if (!vma || (call_dl_resolve & ~PAGE_MASK)) {
+					up_write(&current->mm->mmap_sem);
+					if (vma) kmem_cache_free(vm_area_cachep, vma);
+					return 1;
+				}
+
+				pax_insert_vma(vma, call_dl_resolve);
+				current->mm->call_dl_resolve = call_dl_resolve;
+				up_write(&current->mm->mmap_sem);
+
+emulate:
+				regs->u_regs[UREG_G1] = (sethi & 0x003FFFFFU) << 10;
+				regs->tpc = call_dl_resolve;
+				regs->tnpc = addr+4;
+				return 3;
+			}
+		}
+	} while (0);
+
+	do { /* PaX: unpatched PLT emulation step 2 */
+		unsigned int save, call, nop;
+
+		err = get_user(save, (unsigned int*)(regs->tpc-4));
+		err |= get_user(call, (unsigned int*)regs->tpc);
+		err |= get_user(nop, (unsigned int*)(regs->tpc+4));
+		if (err)
+			break;
+
+		if (save == 0x9DE3BFA8U &&
+		    (call & 0xC0000000U) == 0x40000000U &&
+		    nop == 0x01000000U)
+		{
+			unsigned long dl_resolve = regs->tpc + ((((call | 0xFFFFFFFFC0000000UL) ^ 0x20000000UL) + 0x20000000UL) << 2);
+
+			regs->u_regs[UREG_RETPC] = regs->tpc;
+			regs->tpc = dl_resolve;
+			regs->tnpc = dl_resolve+4;
+			return 3;
+		}
+	} while (0);
+#endif
+
+	return 1;
+}
+
+void pax_report_insns(void *pc)
+{
+	unsigned long i;
+
+	printk(KERN_ERR "PAX: bytes at PC: ");
+	for (i = 0; i < 5; i++) {
+		unsigned int c;
+		if (get_user(c, (unsigned int*)pc+i)) {
+			printk("<invalid address>.");
+			break;
+		}
+		printk("%08x ", c);
+	}
+	printk("\n");
+}
+#endif  
+
+
 asmlinkage void do_sparc64_fault(struct pt_regs *regs)
 {
 	struct mm_struct *mm = current->mm;
@@ -345,6 +705,7 @@
 
 	if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) {
 		regs->tpc &= 0xffffffff;
+		regs->tnpc &= 0xffffffff;
 		address &= 0xffffffff;
 	}
 
@@ -353,6 +714,34 @@
 	if (!vma)
 		goto bad_area;
 
+	/* PaX: detect ITLB misses on non-exec pages */
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+	if ((current->flags & PF_PAX_PAGEEXEC) && vma->vm_start <= address && 
+	    !(vma->vm_flags & VM_EXEC) && (fault_code & FAULT_CODE_ITLB))
+	{
+		if (address != regs->tpc)
+			goto good_area;
+
+		up_read(&mm->mmap_sem);
+		switch (pax_handle_fetch_fault(regs)) {
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUPLT
+		case 2:
+		case 3:
+			goto fault_done;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+		case 4:
+			goto fault_done;
+#endif
+
+		}
+		pax_report_fault(regs, (void*)regs->tpc, (void*)(regs->u_regs[UREG_FP] + STACK_BIAS));
+		do_exit(SIGKILL);
+	}
+#endif
+
 	/* Pure DTLB misses do not tell us whether the fault causing
 	 * load/store/atomic was a write or not, it only says that there
 	 * was no match.  So in such a case we (carefully) read the
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/sparc64/solaris/misc.c linux-2.4.27-leo/arch/sparc64/solaris/misc.c
--- linux-2.4.27/arch/sparc64/solaris/misc.c	2002-11-28 23:53:12.000000000 +0000
+++ linux-2.4.27-leo/arch/sparc64/solaris/misc.c	2004-09-17 03:19:58.000000000 +0100
@@ -53,6 +53,11 @@
 	struct file *file = NULL;
 	unsigned long retval, ret_type;
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (flags & MAP_MIRROR)
+		return -EINVAL;
+#endif
+
 	/* Do we need it here? */
 	set_personality(PER_SVR4);
 	if (flags & MAP_NORESERVE) {
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/x86_64/ia32/ia32_ioctl.c linux-2.4.27-leo/arch/x86_64/ia32/ia32_ioctl.c
--- linux-2.4.27/arch/x86_64/ia32/ia32_ioctl.c	2004-05-19 21:34:37.000000000 +0100
+++ linux-2.4.27-leo/arch/x86_64/ia32/ia32_ioctl.c	2004-09-17 03:19:58.000000000 +0100
@@ -68,6 +68,7 @@
 #define max max
 #include <linux/lvm.h>
 #endif /* LVM */
+#include <linux/dm-ioctl.h>
 
 #include <scsi/scsi.h>
 /* Ugly hack. */
@@ -1950,7 +1951,11 @@
 	 * To have permissions to do most of the vt ioctls, we either have
 	 * to be the owner of the tty, or super-user.
 	 */
+#ifdef CONFIG_GRKERNSEC
+	if (current->tty == tty || capable(CAP_SYS_TTY_CONFIG))
+#else
 	if (current->tty == tty || suser())
+#endif
 		return 1;
 	return 0;                                                    
 }
@@ -4140,6 +4145,22 @@
 COMPATIBLE_IOCTL(LV_BMAP)
 COMPATIBLE_IOCTL(LV_SNAPSHOT_USE_RATE)
 #endif /* LVM */
+/* Device-Mapper */
+#if defined(CONFIG_BLK_DEV_DM) || defined(CONFIG_BLK_DEV_DM_MODULE)
+COMPATIBLE_IOCTL(DM_VERSION)
+COMPATIBLE_IOCTL(DM_REMOVE_ALL)
+COMPATIBLE_IOCTL(DM_DEV_CREATE)
+COMPATIBLE_IOCTL(DM_DEV_REMOVE)
+COMPATIBLE_IOCTL(DM_TABLE_LOAD)
+COMPATIBLE_IOCTL(DM_DEV_SUSPEND)
+COMPATIBLE_IOCTL(DM_DEV_RENAME)
+COMPATIBLE_IOCTL(DM_TABLE_DEPS)
+COMPATIBLE_IOCTL(DM_DEV_STATUS)
+COMPATIBLE_IOCTL(DM_TABLE_STATUS)
+COMPATIBLE_IOCTL(DM_DEV_WAIT)
+COMPATIBLE_IOCTL(DM_LIST_DEVICES)
+COMPATIBLE_IOCTL(DM_TABLE_CLEAR)
+#endif /* CONFIG_BLK_DEV_DM */
 #ifdef CONFIG_AUTOFS_FS
 COMPATIBLE_IOCTL(AUTOFS_IOC_READY)
 COMPATIBLE_IOCTL(AUTOFS_IOC_FAIL)
diff -urN --exclude-from=diff-exclude linux-2.4.27/arch/x86_64/ia32/sys_ia32.c linux-2.4.27-leo/arch/x86_64/ia32/sys_ia32.c
--- linux-2.4.27/arch/x86_64/ia32/sys_ia32.c	2004-05-19 21:34:37.000000000 +0100
+++ linux-2.4.27-leo/arch/x86_64/ia32/sys_ia32.c	2004-09-17 03:19:58.000000000 +0100
@@ -327,6 +327,11 @@
 	if (a.offset & ~PAGE_MASK)
 		return -EINVAL; 
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (a.flags & MAP_MIRROR)
+		return -EINVAL;
+#endif
+
 	if (!(a.flags & MAP_ANONYMOUS)) {
 		file = fget(a.fd);
 		if (!file)
diff -urN --exclude-from=diff-exclude linux-2.4.27/CREDITS linux-2.4.27-leo/CREDITS
--- linux-2.4.27/CREDITS	2004-09-17 02:38:42.000000000 +0100
+++ linux-2.4.27-leo/CREDITS	2004-09-17 03:19:36.000000000 +0100
@@ -999,8 +999,8 @@
 
 N: Nigel Gamble
 E: nigel@nrg.org
-E: nigel@sgi.com
 D: Interrupt-driven printer driver
+D: Preemptible kernel
 S: 120 Alley Way
 S: Mountain View, California 94040
 S: USA
diff -urN --exclude-from=diff-exclude linux-2.4.27/crypto/Config.in linux-2.4.27-leo/crypto/Config.in
--- linux-2.4.27/crypto/Config.in	2004-09-17 02:38:43.000000000 +0100
+++ linux-2.4.27-leo/crypto/Config.in	2004-09-17 03:19:04.000000000 +0100
@@ -4,7 +4,9 @@
 mainmenu_option next_comment
 comment 'Cryptographic options'
 
-if [ "$CONFIG_INET_AH" = "y" -o \
+if [ "$CONFIG_BLK_DEV_CRYPTOLOOP" = "y" -o \
+     "$CONFIG_BLK_DEV_CRYPTOLOOP" = "m" -o \
+     "$CONFIG_INET_AH" = "y" -o \
      "$CONFIG_INET_AH" = "m" -o \
      "$CONFIG_INET_ESP" = "y" -o \
      "$CONFIG_INET_ESP" = "m" -o \
diff -urN --exclude-from=diff-exclude linux-2.4.27/Documentation/Configure.help linux-2.4.27-leo/Documentation/Configure.help
--- linux-2.4.27/Documentation/Configure.help	2004-09-17 02:38:42.000000000 +0100
+++ linux-2.4.27-leo/Documentation/Configure.help	2004-09-20 21:34:47.000000000 +0100
@@ -296,6 +296,17 @@
   If you have a system with several CPUs, you do not need to say Y
   here: the local APIC will be used automatically.
 
+Preemptible Kernel
+CONFIG_PREEMPT
+  This option reduces the latency of the kernel when reacting to
+  real-time or interactive events by allowing a low priority process to
+  be preempted even if it is in kernel mode executing a system call.
+  This allows applications to run more reliably even when the system is
+  under load.
+
+  Say Y here if you are building a kernel for a desktop, embedded or
+  real-time system.  Say N if you are unsure.
+
 Kernel math emulation
 CONFIG_MATH_EMULATION
   Linux can emulate a math coprocessor (used for floating point
@@ -1953,6 +1964,20 @@
   want), say M here and read <file:Documentation/modules.txt>.  The
   module will be called lvm-mod.o.
 
+Device-mapper support
+CONFIG_BLK_DEV_DM
+  Device-mapper is a low level volume manager.  It works by allowing
+  people to specify mappings for ranges of logical sectors.  Various
+  mapping types are available, in addition people may write their own
+  modules containing custom mappings if they wish.
+
+  Higher level volume managers such as LVM2 use this driver.
+
+  If you want to compile this as a module, say M here and read 
+  <file:Documentation/modules.txt>.  The module will be called dm-mod.o.
+
+  If unsure, say N.
+
 Multiple devices driver support (RAID and LVM)
 CONFIG_MD
   Support multiple physical spindles through a single logical device.
@@ -2061,6 +2086,18 @@
 
   If unsure, say Y.
 
+Verbose startup / shutdown messages
+CONFIG_MD_VERBMSG
+  If set, display verbose messages on kernel startup about RAID
+  device initialisation. This can slow down startup by a few seconds,
+  especially on framebuffer terminal types, or if a lot of RAID
+  devices are present, and also fills up space in the dmesg buffer
+  that could be useful elsewhere. However, it aids tracking down 
+  certain RAID problems. It adds about 1k to the kernel size.
+
+  If unsure, say Y. Even if un-set, error messages are still
+  reported
+
 Multipath I/O support
 CONFIG_MD_MULTIPATH
   Multipath-IO is the ability of certain devices to address the same
@@ -2874,6 +2911,20 @@
   If you want to compile it as a module, say M here and read
   Documentation/modules.txt.  If unsure, say `N'.
 
+stealth networking support
+CONFIG_IP_NF_MATCH_STEALTH
+  Enabling this option will drop all syn packets coming to unserved tcp
+  ports as well as all packets coming to unserved udp ports.  If you
+  are using your system to route any type of packets (ie. via NAT)
+  you should put this module at the end of your ruleset, since it will 
+  drop packets that aren't going to ports that are listening on your 
+  machine itself, it doesn't take into account that the packet might be 
+  destined for someone on your internal network if you're using NAT for 
+  instance.
+
+  If you want to compile it as a module, say M here and read
+  Documentation/modules.txt.  If unsure, say `N'.
+
 MAC address match support
 CONFIG_IP_NF_MATCH_MAC
   MAC matching allows you to match packets based on the source
@@ -19326,6 +19377,16 @@
   <file:Documentation/modules.txt>.
   The module will be called i2c-velleman.o.
 
+Basic I2C on Parallel Port adapter
+CONFIG_I2C_PPORT
+  This supports directly connecting I2C devices to the parallel port.
+  See <file:Documentation/i2c/i2c-pport> for more information.
+
+  This driver is also available as a module.  If you want to compile
+  it as a module, say M here and read
+  <file:Documentation/modules.txt>.
+  The module will be called i2c-pport.o.
+
 I2C PCF 8584 interfaces
 CONFIG_I2C_ALGOPCF
   This allows you to use a range of I2C adapters called PCF adapters.
@@ -19347,6 +19408,15 @@
   <file:Documentation/modules.txt>.
   The module will be called i2c-elektor.o.
 
+PCF on the EPP Parallel Port
+CONFIG_I2C_PCFEPP
+  This supports the PCF8584 connected to the parallel port.
+
+  This driver is also available as a module.  If you want to compile
+  it as a module, say M here and read
+  <file:Documentation/modules.txt>.
+  The module will be called i2c-pcf-epp.o.
+
 ITE I2C Algorithm
 CONFIG_ITE_I2C_ALGO
   This supports the use the ITE8172 I2C interface found on some MIPS
@@ -19384,6 +19454,51 @@
   Supports the SGI interfaces like the ones found on SGI Indy VINO
   or SGI O2 MACE.
 
+Motorola 8xx I2C algorithm
+CONFIG_I2C_ALGO8XX
+  This is the algorithm that allows you to use Motorola 8xx I2C adapters.
+
+  This driver is also available as a module.  If you want to compile
+  it as a module, say M here and read
+  <file:Documentation/modules.txt>.
+  The module will be called i2c-algo-8xx.o.
+
+Motorola 8xx I2C interface
+CONFIG_I2C_RPXLITE
+  This supports the Motorola 8xx I2C device.
+
+  This driver is also available as a module.  If you want to compile
+  it as a module, say M here and read
+  <file:Documentation/modules.txt>.
+  The module will be called i2c-rpx.o.
+
+IBM 405 I2C algorithm
+CONFIG_I2C_IBM_OCP_ALGO
+  This is the algorithm that allows you to use IBM 405 I2C adapters.
+
+  This driver is also available as a module.  If you want to compile
+  it as a module, say M here and read
+  <file:Documentation/modules.txt>.
+  The module will be called i2c-algo-ibm_ocp.o.
+
+IBM 405 I2C interface
+CONFIG_I2C_IBM_OCP_ADAP
+  This supports the IBM 405 I2C device.
+
+  This driver is also available as a module.  If you want to compile
+  it as a module, say M here and read
+  <file:Documentation/modules.txt>.
+  The module will be called i2c-adap-ibm_ocp.o.
+
+StrongARM SA-1110 interface
+CONFIG_I2C_FRODO
+  This supports the StrongARM SA-1110 Development Board.
+
+  This driver is also available as a module.  If you want to compile
+  it as a module, say M here and read
+  <file:Documentation/modules.txt>.
+  The module will be called i2c-frodo.o.
+
 I2C device interface
 CONFIG_I2C_CHARDEV
   Say Y here to use i2c-* device files, usually found in the /dev
@@ -23388,6 +23503,946 @@
 
   "Area6" will work for most boards. For ADX, select "Area5".
 
+Grsecurity
+CONFIG_GRKERNSEC
+  If you say Y here, you will be able to configure many features that
+  will enhance the security of your system.  It is highly recommended
+  that you say Y here and read through the help for each option so
+  you fully understand the features and can evaluate their usefulness
+  for your machine.
+
+Additional security levels
+CONFIG_GRKERNSEC_LOW
+
+  Low additional security
+  -----------------------------------------------------------------------
+  If you choose this option, several of the grsecurity options will
+  be enabled that will give you greater protection against a number
+  of attacks, while assuring that none of your software will have any 
+  conflicts with the additional security measures.  If you run a lot of 
+  unusual software, or you are having problems with the higher security 
+  levels, you should say Y here.  With this option, the following features
+  are enabled:
+  
+  linking restrictions
+  fifo restrictions
+  random pids
+  enforcing nproc on execve()
+  restricted dmesg
+  random ip ids
+  enforced chdir("/") on chroot
+
+  Medium additional security
+  -----------------------------------------------------------------------
+  If you say Y here, several features in addition to those included in the 
+  low additional security level will be enabled.  These features provide
+  even more security to your system, though in rare cases they may
+  be incompatible with very old or poorly written software.  If you 
+  enable this option, make sure that your auth service (identd) is 
+  running as gid 10 (usually group wheel). With this option the following 
+  features (in addition to those provided in the low additional security 
+  level) will be enabled:
+
+  random tcp source ports
+  failed fork logging
+  time change logging
+  signal logging
+  deny mounts in chroot
+  deny double chrooting
+  deny sysctl writes in chroot
+  deny mknod in chroot
+  deny access to abstract AF_UNIX sockets out of chroot
+  deny pivot_root in chroot
+  denied writes of /dev/kmem, /dev/mem, and /dev/port
+  /proc restrictions with special gid set to 10 (usually wheel)
+  address space layout randomization
+  removal of addresses from /proc/<pid>/[maps|stat]
+
+  High additional security
+  ----------------------------------------------------------------------
+  If you say Y here, many of the features of grsecurity will be enabled,
+  that will protect you against many kinds of attacks against
+  your system.  The heightened security comes at a cost of an 
+  increased chance of incompatibilities with rare software on your 
+  machine.  Since this security level enables PaX, you should view 
+  <http://pax.grsecurity.net> and read about the PaX project.  While 
+  you are there, download chpax and run it on binaries that cause 
+  problems with PaX.  Also remember that since the /proc restrictions are 
+  enabled, you must run your identd as group wheel (gid 10).  
+  This security level enables the following features in addition to those
+  listed in the low and medium security levels:
+
+  additional /proc restrictions
+  chmod restrictions in chroot
+  no signals, ptrace, or viewing processes outside of chroot
+  capability restrictions in chroot
+  deny fchdir out of chroot
+  priority restrictions in chroot
+  segmentation-based implementation of PaX
+  mprotect restrictions
+  kernel stack randomization
+  mount/unmount/remount logging
+  kernel symbol hiding
+
+Customized additional security
+CONFIG_GRKERNSEC_CUSTOM
+  If you say Y here, you will be able to configure every grsecurity 
+  option, which allows you to enable many more features that aren't 
+  covered in the basic security levels.  These additional features include 
+  TPE, socket restrictions, and the sysctl system for grsecurity.  It is 
+  advised that you read through the help for each option to determine its 
+  usefulness in your situation.
+
+Support soft mode
+CONFIG_GRKERNSEC_PAX_SOFTMODE
+  Enabling this option will allow you to run PaX in soft mode, that
+  is, PaX features will not be enforced by default, only on executables
+  marked explicitly.  You must also enable PT_PAX_FLAGS support as it
+  is the only way to mark executables for soft mode use.
+
+  Soft mode can be activated by using the "pax_softmode=1" kernel command
+  line option on boot.  Furthermore you can control various PaX features
+  at runtime via the entries in /proc/sys/kernel/pax.
+
+Use legacy ELF header marking
+CONFIG_GRKERNSEC_PAX_EI_PAX
+  Enabling this option will allow you to control PaX features on
+  a per executable basis via the 'chpax' utility available at
+  http://pax.grsecurity.net/.  The control flags will be read from
+  an otherwise reserved part of the ELF header.  This marking has
+  numerous drawbacks (no support for soft-mode, toolchain does not
+  know about the non-standard use of the ELF header) therefore it
+  has been deprecated in favour of PT_PAX_FLAGS support.
+
+  You should enable this option only if your toolchain does not yet
+  support the new control flag location (PT_PAX_FLAGS) or you still
+  have applications not marked by PT_PAX_FLAGS.
+
+  Note that if you enable PT_PAX_FLAGS marking support as well,
+  it will override the legacy EI_PAX marks.
+
+Use ELF program header marking
+CONFIG_GRKERNSEC_PAX_PT_PAX_FLAGS
+  Enabling this option will allow you to control PaX features on
+  a per executable basis via the 'paxctl' utility available at
+  http://pax.grsecurity.net/.  The control flags will be read from
+  a PaX specific ELF program header (PT_PAX_FLAGS).  This marking
+  has the benefits of supporting both soft mode and being fully
+  integrated into the toolchain (the binutils patch is available
+  from http://pax.grsecurity.net).
+
+  Note that if you enable the legacy EI_PAX marking support as well,
+  it will be overridden by the PT_PAX_FLAGS marking.
+
+MAC system integration
+CONFIG_GRKERNSEC_PAX_NO_ACL_FLAGS
+  Mandatory Access Control systems have the option of controlling
+  PaX flags on a per executable basis, choose the method supported
+  by your particular system.
+
+  - "none": if your MAC system does not interact with PaX,
+  - "direct": if your MAC system defines pax_set_flags() itself,
+  - "hook": if your MAC system uses the pax_set_flags_func callback.
+
+  NOTE: this option is for developers/integrators only.
+
+Enforce non-executable pages
+CONFIG_GRKERNSEC_PAX_NOEXEC
+  By design some architectures do not allow for protecting memory
+  pages against execution or even if they do, Linux does not make
+  use of this feature.  In practice this means that if a page is
+  readable (such as the stack or heap) it is also executable.
+
+  There is a well known exploit technique that makes use of this
+  fact and a common programming mistake where an attacker can
+  introduce code of his choice somewhere in the attacked program's
+  memory (typically the stack or the heap) and then execute it.
+
+  If the attacked program was running with different (typically
+  higher) privileges than that of the attacker, then he can elevate
+  his own privilege level (e.g. get a root shell, write to files for
+  which he does not have write access to, etc).
+
+  Enabling this option will let you choose from various features
+  that prevent the injection and execution of 'foreign' code in
+  a program.
+
+  This will also break programs that rely on the old behaviour and
+  expect that dynamically allocated memory via the malloc() family
+  of functions is executable (which it is not).  Notable examples
+  are the XFree86 4.x server, the java runtime and wine.
+
+Paging based non-executable pages
+CONFIG_GRKERNSEC_PAX_PAGEEXEC
+  This implementation is based on the paging feature of the CPU.
+  On i386 it has a variable performance impact on applications
+  depending on their memory usage pattern.  You should carefully
+  test your applications before using this feature in production.
+  On alpha, parisc, sparc and sparc64 there is no performance
+  impact.  On ppc there is a slight performance impact.
+
+Segmentation based non-executable pages
+CONFIG_GRKERNSEC_PAX_SEGMEXEC
+  This implementation is based on the segmentation feature of the
+  CPU and has little performance impact, however applications will
+  be limited to a 1.5 GB address space instead of the normal 3 GB.
+
+Emulate trampolines
+CONFIG_GRKERNSEC_PAX_EMUTRAMP
+  There are some programs and libraries that for one reason or
+  another attempt to execute special small code snippets from
+  non-executable memory pages.  Most notable examples are the
+  signal handler return code generated by the kernel itself and
+  the GCC trampolines.
+
+  If you enabled CONFIG_GRKERNSEC_PAX_PAGEEXEC or 
+  CONFIG_GRKERNSEC_PAX_SEGMEXEC then such programs will no longer
+  work under your kernel.
+
+  As a remedy you can say Y here and use the 'chpax' or 'paxctl'
+  utilities to enable trampoline emulation for the affected programs
+  yet still have the protection provided by the non-executable pages.
+
+  On parisc and ppc you MUST enable this option and EMUSIGRT as
+  well, otherwise your system will not even boot.
+
+  Alternatively you can say N here and use the 'chpax' or 'paxctl'
+  utilities to disable CONFIG_GRKERNSEC_PAX_PAGEEXEC and 
+  CONFIG_GRKERNSEC_PAX_SEGMEXEC for the affected files.
+
+  NOTE: enabling this feature *may* open up a loophole in the
+  protection provided by non-executable pages that an attacker
+  could abuse.  Therefore the best solution is to not have any
+  files on your system that would require this option.  This can
+  be achieved by not using libc5 (which relies on the kernel
+  signal handler return code) and not using or rewriting programs
+  that make use of the nested function implementation of GCC.
+  Skilled users can just fix GCC itself so that it implements
+  nested function calls in a way that does not interfere with PaX.
+
+Automatically emulate sigreturn trampolines
+CONFIG_GRKERNSEC_PAX_EMUSIGRT
+  Enabling this option will have the kernel automatically detect
+  and emulate signal return trampolines executing on the stack
+  that would otherwise lead to task termination.
+
+  This solution is intended as a temporary one for users with
+  legacy versions of libc (libc5, glibc 2.0, uClibc before 0.9.17,
+  Modula-3 runtime, etc) or executables linked to such, basically
+  everything that does not specify its own SA_RESTORER function in
+  normal executable memory like glibc 2.1+ does.
+
+  On parisc and ppc you MUST enable this option, otherwise your
+  system will not even boot.
+
+  NOTE: this feature cannot be disabled on a per executable basis
+  and since it *does* open up a loophole in the protection provided
+  by non-executable pages, the best solution is to not have any
+  files on your system that would require this option.
+
+Restrict mprotect()
+CONFIG_GRKERNSEC_PAX_MPROTECT
+  Enabling this option will prevent programs from
+   - changing the executable status of memory pages that were
+     not originally created as executable,
+   - making read-only executable pages writable again,
+   - creating executable pages from anonymous memory.
+
+  You should say Y here to complete the protection provided by
+  the enforcement of non-executable pages.
+
+  NOTE: you can use the 'chpax' utility to control this
+  feature on a per file basis. chpax is available at
+  <http://pax.grsecurity.net>
+
+Disallow ELF text relocations
+CONFIG_GRKERNSEC_PAX_NOELFRELOCS
+  Non-executable pages and mprotect() restrictions are effective
+  in preventing the introduction of new executable code into an
+  attacked task's address space.  There remain only two venues
+  for this kind of attack: if the attacker can execute already
+  existing code in the attacked task then he can either have it
+  create and mmap() a file containing his code or have it mmap()
+  an already existing ELF library that does not have position
+  independent code in it and use mprotect() on it to make it
+  writable and copy his code there.  While protecting against
+  the former approach is beyond PaX, the latter can be prevented
+  by having only PIC ELF libraries on one's system (which do not
+  need to relocate their code).  If you are sure this is your case,
+  then enable this option otherwise be careful as you may not even
+  be able to boot or log on your system (for example, some PAM
+  modules are erroneously compiled as non-PIC by default).
+
+  NOTE: if you are using dynamic ELF executables (as suggested
+  when using ASLR) then you must have made sure that you linked
+  your files using the PIC version of crt1 (the et_dyn.zip package
+  referenced there has already been updated to support this).
+
+Enforce non-executable kernel pages
+CONFIG_GRKERNSEC_PAX_KERNEXEC
+  This is the kernel land equivalent of PAGEEXEC and MPROTECT,
+  that is, enabling this option will make it harder to inject
+  and execute 'foreign' code in kernel memory itself.
+
+Address Space Layout Randomization
+CONFIG_GRKERNSEC_PAX_ASLR
+  Many if not most exploit techniques rely on the knowledge of
+  certain addresses in the attacked program.  The following options
+  will allow the kernel to apply a certain amount of randomization
+  to specific parts of the program thereby forcing an attacker to
+  guess them in most cases.  Any failed guess will most likely crash
+  the attacked program which allows the kernel to detect such attempts
+  and react on them.  PaX itself provides no reaction mechanisms,
+  instead it is strongly encouraged that you make use of grsecurity's
+  built-in crash detection features or develop one yourself.
+
+  By saying Y here you can choose to randomize the following areas:
+   - top of the task's kernel stack
+   - top of the task's userland stack
+   - base address for mmap() requests that do not specify one
+     (this includes all libraries)
+   - base address of the main executable
+
+  It is strongly recommended to say Y here as address space layout
+  randomization has negligible impact on performance yet it provides
+  a very effective protection.
+
+  NOTE: you can use the 'chpax' or 'paxctl' utilities to control most
+  of these features on a per file basis.
+
+Randomize kernel stack base
+CONFIG_GRKERNSEC_PAX_RANDKSTACK
+  By saying Y here the kernel will randomize every task's kernel
+  stack on every system call.  This will not only force an attacker
+  to guess it but also prevent him from making use of possible
+  leaked information about it.
+
+  Since the kernel stack is a rather scarce resource, randomization
+  may cause unexpected stack overflows, therefore you should very
+  carefully test your system.  Note that once enabled in the kernel
+  configuration, this feature cannot be disabled on a per file basis.
+
+Randomize user stack base
+CONFIG_GRKERNSEC_PAX_RANDUSTACK
+  By saying Y here the kernel will randomize every task's userland
+  stack.  The randomization is done in two steps where the second
+  one may apply a big amount of shift to the top of the stack and
+  cause problems for programs that want to use lots of memory (more
+  than 2.5 GB if SEGMEXEC is not active, or 1.25 GB when it is).
+  For this reason the second step can be controlled by 'chpax' or
+  'paxctl' on a per file basis.
+
+Randomize ET_EXEC base
+CONFIG_GRKERNSEC_PAX_RANDEXEC     
+  By saying Y here the kernel will randomize the base address of normal
+  ET_EXEC ELF executables as well.  This is accomplished by mapping the
+  executable in memory in a special way which also allows for detecting
+  attackers who attempt to execute its code for their purposes.  Since
+  this special mapping causes performance degradation and the attack
+  detection may create false alarms as well, you should carefully test
+  your executables when this feature is enabled.
+
+  This solution is intended only as a temporary one until you relink
+  your programs as a dynamic ELF file.
+
+  NOTE: you can use the 'chpax' or 'paxctl' utilities to control
+  this feature on a per file basis.
+
+Allow ELF ET_EXEC text relocations
+CONFIG_GRKERNSEC_PAX_ETEXECRELOCS
+  On some architectures like the alpha there are incorrectly
+  created applications that require text relocations and would
+  not work without enabling this option.  If you are an alpha
+  user, you should enable this option and disable it once you
+  have made sure that none of your applications need it.
+
+Automatically emulate ELF PLT
+CONFIG_GRKERNSEC_PAX_EMUPLT
+  Enabling this option will have the kernel automatically detect
+  and emulate the Procedure Linkage Table entries in ELF files.
+  On some architectures such entries are in writable memory, and
+  become non-executable leading to task termination.  Therefore
+  it is mandatory that you enable this option on alpha, parisc, ppc,
+  sparc and sparc64, otherwise your system would not even boot.
+
+  NOTE: this feature *does* open up a loophole in the protection
+  provided by the non-executable pages, therefore the proper
+  solution is to modify the toolchain to produce a PLT that does
+  not need to be writable.
+
+Randomize mmap() base
+CONFIG_GRKERNSEC_PAX_RANDMMAP
+  By saying Y here the kernel will use a randomized base address for
+  mmap() requests that do not specify one themselves.  As a result
+  all dynamically loaded libraries will appear at random addresses
+  and therefore be harder to exploit by a technique where an attacker
+  attempts to execute library code for his purposes (e.g. spawn a
+  shell from an exploited program that is running at an elevated
+  privilege level).
+
+  Furthermore, if a program is relinked as a dynamic ELF file, its
+  base address will be randomized as well, completing the full
+  randomization of the address space layout.  Attacking such programs
+  becomes a guess game.  You can find an example of doing this at
+  <http://pax.grsecurity.net/et_dyn.zip> and practical samples at
+  <http://www.grsecurity.net/grsec-gcc-specs.tar.gz> .
+
+  NOTE: you can use the 'chpax' or 'paxctl' utilities to control this
+  feature on a per file basis.
+
+Deny writing to /dev/kmem and /dev/mem
+CONFIG_GRKERNSEC_KMEM
+  If you say Y here, /dev/kmem and /dev/mem won't be allowed to
+  be written to via mmap or otherwise to modify the running kernel.
+  If you have module support disabled, enabling this, along with /dev/port
+  (below) will close up four ways that are currently used to insert 
+  malicious code into the running kernel. Even with all these features 
+  enabled, we still highly recommend that you use the ACL system, as it
+  is still possible for an attacker to modify the running kernel 
+  through privileged I/O granted by ioperm/iopl. 
+  If you are not using XFree86, you may be able to stop this additional
+  case by enabling the 'Disable privileged I/O' option. Though nothing
+  legitimately writes to /dev/kmem, XFree86 does need to write to /dev/mem,
+  but only to video memory, which is the only writing we allow in this
+  case.  If /dev/kmem or /dev/mem are mmaped without PROT_WRITE, they will
+  not be allowed to mprotect it with PROT_WRITE later.
+  Enabling this feature could make certain apps like VMWare stop working,
+  as they need to write to other locations in /dev/mem.
+  It is highly recommended that you say Y here if you meet all the 
+  conditions above.
+
+Deny access to /dev/port
+CONFIG_GRKERNSEC_PORT
+  If you say Y here, /dev/port will not be able to be opened. This option
+  is normally used in conjunction with /dev/kmem (above). It is seperated
+  here because it breaks certain utilities (for example, kbdrate).
+  Is is highly recommended that you say Y here.
+
+Disable privileged I/O
+CONFIG_GRKERNSEC_IO
+  If you say Y here, all ioperm and iopl calls will return an error.
+  Ioperm and iopl can be used to modify the running kernel.
+  Unfortunately, some programs need this access to operate properly,
+  the most notable of which are XFree86 and hwclock.  hwclock can be
+  remedied by having RTC support in the kernel, so CONFIG_RTC is
+  enabled if this option is enabled, to ensure that hwclock operates
+  correctly.  XFree86 still will not operate correctly with this option
+  enabled, so DO NOT CHOOSE Y IF YOU USE XFree86.  If you use XFree86
+  and you still want to protect your kernel against modification,
+  use the ACL system.
+
+Hide kernel symbols
+CONFIG_GRKERNSEC_HIDESYM
+  If you say Y here, getting information on loaded modules, and 
+  displaying all kernel symbols through a syscall will be restricted
+  to users with CAP_SYS_MODULE.  This option is only effective 
+  provided the following conditions are met:
+  1) The kernel using grsecurity is not precompiled by some distribution
+  2) You are using the ACL system and hiding other files such as your
+     kernel image and System.map
+  3) You have the additional /proc restrictions enabled, which removes
+     /proc/kcore
+  If the above conditions are met, this option will aid to provide a
+  useful protection against local and remote kernel exploitation of
+  overflows and arbitrary read/write vulnerabilities.
+
+Deter exploit bruteforcing
+CONFIG_GRKERNSEC_BRUTE
+  If you say Y here, attempts to bruteforce exploits against forking
+  daemons such as apache or sshd will be deterred.  When a child of a 
+  forking daemon is killed by PaX or crashes due to an illegal 
+  instruction, the parent process will be delayed 30 seconds upon every
+  subsequent fork until the administrator is able to assess the 
+  situation and restart the daemon.  It is recommended that you also 
+  enable signal logging in the auditing section so that logs are 
+  generated when a process performs an illegal instruction.
+
+/proc/<pid>/ipaddr support
+CONFIG_GRKERNSEC_PROC_IPADDR
+  If you say Y here, a new entry will be added to each /proc/<pid>
+  directory that contains the IP address of the person using the task.
+  The IP is carried across local TCP and AF_UNIX stream sockets.
+  This information can be useful for IDS/IPSes to perform remote response
+  to a local attack.  The entry is readable by only the owner of the 
+  process (and root if he has CAP_DAC_OVERRIDE, which can be removed via
+  the RBAC system), and thus does not create privacy concerns.
+
+Proc Restrictions
+CONFIG_GRKERNSEC_PROC
+  If you say Y here, the permissions of the /proc filesystem
+  will be altered to enhance system security and privacy.  Depending
+  upon the options you choose, you can either restrict users to see
+  only the processes they themselves run, or choose a group that can
+  view all processes and files normally restricted to root if you choose
+  the "restrict to user only" option.  NOTE: If you're running identd as 
+  a non-root user, you will have to run it as the group you specify here.
+
+Restrict /proc to user only
+CONFIG_GRKERNSEC_PROC_USER
+  If you say Y here, non-root users will only be able to view their own 
+  processes, and restricts them from viewing network-related information,  
+  and viewing kernel symbol and module information.
+
+Restrict /proc to user and group
+CONFIG_GRKERNSEC_PROC_USERGROUP
+  If you say Y here, you will be able to select a group that will be
+  able to view all processes, network-related information, and
+  kernel and symbol information.  This option is useful if you want
+  to run identd as a non-root user.
+
+Remove addresses from /proc/pid/[maps|stat]
+CONFIG_GRKERNSEC_PROC_MEMMAP
+  If you say Y here, the /proc/<pid>/maps and /proc/<pid>/stat files will
+  give no information about the addresses of its mappings if
+  PaX features that rely on random addresses are enabled on the task.
+  If you use PaX it is greatly recommended that you say Y here as it 
+  closes up a hole that makes the full ASLR useless for suid 
+  binaries.
+
+Additional proc restrictions
+CONFIG_GRKERNSEC_PROC_ADD
+  If you say Y here, additional restrictions will be placed on
+  /proc that keep normal users from viewing cpu and device information.
+
+Dmesg(8) Restriction
+CONFIG_GRKERNSEC_DMESG
+  If you say Y here, non-root users will not be able to use dmesg(8)
+  to view up to the last 4kb of messages in the kernel's log buffer.
+  If the sysctl option is enabled, a sysctl option with name "dmesg" is 
+  created.
+
+Linking restrictions
+CONFIG_GRKERNSEC_LINK
+  If you say Y here, /tmp race exploits will be prevented, since users
+  will no longer be able to follow symlinks owned by other users in 
+  world-writable +t directories (i.e. /tmp), unless the owner of the 
+  symlink is the owner of the directory. users will also not be
+  able to hardlink to files they do not own.  If the sysctl option is
+  enabled, a sysctl option with name "linking_restrictions" is created.
+
+FIFO restrictions
+CONFIG_GRKERNSEC_FIFO
+  If you say Y here, users will not be able to write to FIFOs they don't
+  own in world-writable +t directories (i.e. /tmp), unless the owner of
+  the FIFO is the same owner of the directory it's held in.  If the sysctl
+  option is enabled, a sysctl option with name "fifo_restrictions" is 
+  created.
+
+Enforce RLIMIT_NPROC on execs
+CONFIG_GRKERNSEC_EXECVE
+  If you say Y here, users with a resource limit on processes will
+  have the value checked during execve() calls.  The current system
+  only checks the system limit during fork() calls.  If the sysctl option
+  is enabled, a sysctl option with name "execve_limiting" is created.
+
+Single group for auditing
+CONFIG_GRKERNSEC_AUDIT_GROUP
+  If you say Y here, the exec, chdir, (un)mount, and ipc logging features
+  will only operate on a group you specify.  This option is recommended
+  if you only want to watch certain users instead of having a large
+  amount of logs from the entire system.  If the sysctl option is enabled,
+  a sysctl option with name "audit_group" is created.
+
+GID for auditing
+CONFIG_GRKERNSEC_AUDIT_GID
+  Here you can choose the GID that will be the target of kernel auditing.
+  Remember to add the users you want to log to the GID specified here.
+  If the sysctl option is enabled, whatever you choose here won't matter. 
+  You'll have to specify the GID in your bootup script by echoing the GID 
+  to the proper /proc entry.  View the help on the sysctl option for more 
+  information.  If the sysctl option is enabled, a sysctl option with name 
+  "audit_gid" is created.
+
+Chdir logging
+CONFIG_GRKERNSEC_AUDIT_CHDIR
+  If you say Y here, all chdir() calls will be logged.  If the sysctl 
+  option is enabled, a sysctl option with name "audit_chdir" is created.
+
+(Un)Mount logging
+CONFIG_GRKERNSEC_AUDIT_MOUNT
+  If you say Y here, all mounts and unmounts will be logged.  If the 
+  sysctl option is enabled, a sysctl option with name "audit_mount" is 
+  created.
+
+IPC logging
+CONFIG_GRKERNSEC_AUDIT_IPC
+  If you say Y here, creation and removal of message queues, semaphores,
+  and shared memory will be logged.  If the sysctl option is enabled, a
+  sysctl option with name "audit_ipc" is created.
+
+Exec logging
+CONFIG_GRKERNSEC_EXECLOG
+  If you say Y here, all execve() calls will be logged (since the
+  other exec*() calls are frontends to execve(), all execution
+  will be logged).  Useful for shell-servers that like to keep track
+  of their users.  If the sysctl option is enabled, a sysctl option with
+  name "exec_logging" is created.
+  WARNING: This option when enabled will produce a LOT of logs, especially
+  on an active system.
+
+Resource logging
+CONFIG_GRKERNSEC_RESLOG
+  If you say Y here, all attempts to overstep resource limits will
+  be logged with the resource name, the requested size, and the current
+  limit.  It is highly recommended that you say Y here.
+
+Signal logging
+CONFIG_GRKERNSEC_SIGNAL
+  If you say Y here, certain important signals will be logged, such as
+  SIGSEGV, which will as a result inform you of when a error in a program
+  occurred, which in some cases could mean a possible exploit attempt.
+  If the sysctl option is enabled, a sysctl option with name 
+  "signal_logging" is created.
+
+Fork failure logging
+CONFIG_GRKERNSEC_FORKFAIL
+  If you say Y here, all failed fork() attempts will be logged.
+  This could suggest a fork bomb, or someone attempting to overstep
+  their process limit.  If the sysctl option is enabled, a sysctl option
+  with name "forkfail_logging" is created.
+
+Time change logging
+CONFIG_GRKERNSEC_TIME
+  If you say Y here, any changes of the system clock will be logged.
+  If the sysctl option is enabled, a sysctl option with name 
+  "timechange_logging" is created.
+
+ELF text relocations logging
+CONFIG_GRKERNSEC_AUDIT_TEXTREL
+  If you say Y here, text relocations will be logged with the filename 
+  of the offending library or binary.  The purpose of the feature is 
+  to help Linux distribution developers get rid of libraries and 
+  binaries that need text relocations which hinder the future progress 
+  of PaX.  Only Linux distribution developers should say Y here, and 
+  never on a production machine, as this option creates an information 
+  leak that could aid an attacker in defeating the randomization of
+  a single memory region.  If the sysctl option is enabled, a sysctl
+  option with name "audit_textrel" is created.
+
+Chroot jail restrictions
+CONFIG_GRKERNSEC_CHROOT
+  If you say Y here, you will be able to choose several options that will
+  make breaking out of a chrooted jail much more difficult.  If you
+  encounter no software incompatibilities with the following options, it
+  is recommended that you enable each one.
+
+Deny access to abstract AF_UNIX sockets out of chroot
+CONFIG_GRKERNSEC_CHROOT_UNIX
+  If you say Y here, processes inside a chroot will not be able to
+  connect to abstract (meaning not belonging to a filesystem) Unix
+  domain sockets that were bound outside of a chroot.  It is recommended
+  that you say Y here.  If the sysctl option is enabled, a sysctl option
+  with name "chroot_deny_unix" is created.
+
+Deny shmat() out of chroot
+CONFIG_GRKERNSEC_CHROOT_SHMAT
+  If you say Y here, processes inside a chroot will not be able to attach
+  to shared memory segments that were created outside of the chroot jail.
+  It is recommended that you say Y here.  If the sysctl option is enabled,
+  a sysctl option with name "chroot_deny_shmat" is created.
+
+Protect outside processes
+CONFIG_GRKERNSEC_CHROOT_FINDTASK
+  If you say Y here, processes inside a chroot will not be able to
+  kill, send signals with fcntl, ptrace, capget, setpgid, getpgid, 
+  getsid, or view any process outside of the chroot.  If the sysctl 
+  option is enabled, a sysctl option with name "chroot_findtask" is 
+  created.
+
+Deny mounts in chroot
+CONFIG_GRKERNSEC_CHROOT_MOUNT
+  If you say Y here, processes inside a chroot will not be able to
+  mount or remount filesystems.  If the sysctl option is enabled, a 
+  sysctl option with name "chroot_deny_mount" is created.
+
+Deny pivot_root in chroot
+CONFIG_GRKERNSEC_CHROOT_PIVOT
+  If you say Y here, processes inside a chroot will not be able to use
+  a function called pivot_root() that was introduced in Linux 2.3.41.  It 
+  works similar to chroot in that it changes the root filesystem.  This 
+  function could be misused in a chrooted process to attempt to break out 
+  of the chroot, and therefore should not be allowed.  If the sysctl 
+  option is enabled, a sysctl option with name "chroot_deny_pivot" is 
+  created.
+
+Deny double-chroots
+CONFIG_GRKERNSEC_CHROOT_DOUBLE
+  If you say Y here, processes inside a chroot will not be able to chroot
+  again outside of the chroot.  This is a widely used method of breaking 
+  out of a chroot jail and should not be allowed.  If the sysctl option 
+  is enabled, a sysctl option with name "chroot_deny_chroot" is created.
+
+Deny fchdir outside of chroot
+CONFIG_GRKERNSEC_CHROOT_FCHDIR
+  If you say Y here, a well-known method of breaking chroots by fchdir'ing
+  to a file descriptor of the chrooting process that points to a directory
+  outside the filesystem will be stopped.  If the sysctl option
+  is enabled, a sysctl option with name "chroot_deny_fchdir" is created.
+
+Enforce chdir("/") on all chroots
+CONFIG_GRKERNSEC_CHROOT_CHDIR
+  If you say Y here, the current working directory of all newly-chrooted
+  applications will be set to the the root directory of the chroot.
+  The man page on chroot(2) states:
+  Note that this call does not change  the  current  working
+  directory,  so  that `.' can be outside the tree rooted at
+  `/'.  In particular, the  super-user  can  escape  from  a
+  `chroot jail' by doing `mkdir foo; chroot foo; cd ..'.  
+
+  It is recommended that you say Y here, since it's not known to break
+  any software.  If the sysctl option is enabled, a sysctl option with
+  name "chroot_enforce_chdir" is created.
+
+Deny (f)chmod +s in chroot
+CONFIG_GRKERNSEC_CHROOT_CHMOD
+  If you say Y here, processes inside a chroot will not be able to chmod
+  or fchmod files to make them have suid or sgid bits.  This protects 
+  against another published method of breaking a chroot.  If the sysctl 
+  option is enabled, a sysctl option with name "chroot_deny_chmod" is
+  created.
+
+Deny mknod in chroot
+CONFIG_GRKERNSEC_CHROOT_MKNOD
+  If you say Y here, processes inside a chroot will not be allowed to
+  mknod.  The problem with using mknod inside a chroot is that it
+  would allow an attacker to create a device entry that is the same
+  as one on the physical root of your system, which could range from
+  anything from the console device to a device for your harddrive (which
+  they could then use to wipe the drive or steal data).  It is recommended
+  that you say Y here, unless you run into software incompatibilities.
+  If the sysctl option is enabled, a sysctl option with name
+  "chroot_deny_mknod" is created.
+
+Restrict priority changes in chroot
+CONFIG_GRKERNSEC_CHROOT_NICE
+  If you say Y here, processes inside a chroot will not be able to raise
+  the priority of processes in the chroot, or alter the priority of 
+  processes outside the chroot.  This provides more security than simply
+  removing CAP_SYS_NICE from the process' capability set.  If the
+  sysctl option is enabled, a sysctl option with name "chroot_restrict_nice"
+  is created.
+
+Log all execs within chroot
+CONFIG_GRKERNSEC_CHROOT_EXECLOG
+  If you say Y here, all executions inside a chroot jail will be logged 
+  to syslog.  This can cause a large amount of logs if certain
+  applications (eg. djb's daemontools) are installed on the system, and
+  is therefore left as an option.  If the sysctl option is enabled, a 
+  sysctl option with name "chroot_execlog" is created.
+
+Deny sysctl writes in chroot
+CONFIG_GRKERNSEC_CHROOT_SYSCTL
+  If you say Y here, an attacker in a chroot will not be able to
+  write to sysctl entries, either by sysctl(2) or through a /proc
+  interface.  It is strongly recommended that you say Y here. If the
+  sysctl option is enabled, a sysctl option with name 
+  "chroot_deny_sysctl" is created.
+
+Chroot jail capability restrictions
+CONFIG_GRKERNSEC_CHROOT_CAPS
+  If you say Y here, the capabilities on all root processes within a
+  chroot jail will be lowered to stop module insertion, raw i/o,
+  system and net admin tasks, rebooting the system, modifying immutable 
+  files, modifying IPC owned by another, and changing the system time.
+  This is left an option because it can break some apps.  Disable this
+  if your chrooted apps are having problems performing those kinds of
+  tasks.  If the sysctl option is enabled, a sysctl option with
+  name "chroot_caps" is created.
+
+Trusted path execution
+CONFIG_GRKERNSEC_TPE
+  If you say Y here, you will be able to choose a gid to add to the
+  supplementary groups of users you want to mark as "untrusted."
+  These users will not be able to execute any files that are not in
+  root-owned directories writable only by root.  If the sysctl option
+  is enabled, a sysctl option with name "tpe" is created.
+
+Group for trusted path execution
+CONFIG_GRKERNSEC_TPE_GID
+  Here you can choose the GID to enable trusted path protection for.
+  Remember to add the users you want protection enabled for to the GID 
+  specified here.  If the sysctl option is enabled, whatever you choose
+  here won't matter. You'll have to specify the GID in your bootup 
+  script by echoing the GID to the proper /proc entry.  View the help
+  on the sysctl option for more information.  If the sysctl option is
+  enabled, a sysctl option with name "tpe_gid" is created.
+
+Partially restrict non-root users
+CONFIG_GRKERNSEC_TPE_ALL
+  If you say Y here, All non-root users other than the ones in the 
+  group specified in the main TPE option will only be allowed to
+  execute files in directories they own that are not group or
+  world-writable, or in directories owned by root and writable only by
+  root.  If the sysctl option is enabled, a sysctl option with name 
+  "tpe_restrict_all" is created.
+
+Randomized PIDs
+CONFIG_GRKERNSEC_RANDPID
+  If you say Y here, all PIDs created on the system will be
+  pseudo-randomly generated.  This is extremely effective along
+  with the /proc restrictions to disallow an attacker from guessing
+  pids of daemons, etc.  PIDs are also used in some cases as part
+  of a naming system for temporary files, so this option would keep
+  those filenames from being predicted as well.  We also use code
+  to make sure that PID numbers aren't reused too soon.  If the sysctl
+  option is enabled, a sysctl option with name "rand_pids" is created.
+
+Larger entropy pools
+CONFIG_GRKERNSEC_RANDNET
+  If you say Y here, the entropy pools used for many features of Linux
+  and grsecurity will be doubled in size.  Since several grsecurity 
+  features use additional randomness, it is recommended that you say Y 
+  here.  Saying Y here has a similar effect as modifying
+  /proc/sys/kernel/random/poolsize.
+
+Truly random TCP ISN selection
+CONFIG_GRKERNSEC_RANDISN
+  If you say Y here, Linux's default selection of TCP Initial Sequence
+  Numbers (ISNs) will be replaced with that of OpenBSD.  Linux uses
+  an MD4 hash based on the connection plus a time value to create the
+  ISN, while OpenBSD's selection is random.  If the sysctl option is 
+  enabled, a sysctl option with name "rand_isns" is created.
+
+Randomized IP IDs
+CONFIG_GRKERNSEC_RANDID
+  If you say Y here, all the id field on all outgoing packets
+  will be randomized.  This hinders os fingerprinters and
+  keeps your machine from being used as a bounce for an untraceable
+  portscan.  Ids are used for fragmented packets, fragments belonging
+  to the same packet have the same id.  By default linux only
+  increments the id value on each packet sent to an individual host.
+  We use a port of the OpenBSD random ip id code to achieve the
+  randomness, while keeping the possibility of id duplicates to
+  near none.  If the sysctl option is enabled, a sysctl option with name
+  "rand_ip_ids" is created.
+
+Randomized TCP source ports
+CONFIG_GRKERNSEC_RANDSRC
+  If you say Y here, situations where a source port is generated on the
+  fly for the TCP protocol (ie. with connect() ) will be altered so that
+  the source port is generated at random, instead of a simple incrementing
+  algorithm.  If the sysctl option is enabled, a sysctl option with name
+  "rand_tcp_src_ports" is created.
+
+Randomized RPC XIDs
+CONFIG_GRKERNSEC_RANDRPC
+  If you say Y here, the method of determining XIDs for RPC requests will
+  be randomized, instead of using linux's default behavior of simply
+  incrementing the XID.  If you want your RPC connections to be more
+  secure, say Y here.  If the sysctl option is enabled, a sysctl option 
+  with name "rand_rpc" is created.
+
+Socket restrictions
+CONFIG_GRKERNSEC_SOCKET
+  If you say Y here, you will be able to choose from several options.
+  If you assign a GID on your system and add it to the supplementary
+  groups of users you want to restrict socket access to, this patch
+  will perform up to three things, based on the option(s) you choose.
+
+Deny all socket access
+CONFIG_GRKERNSEC_SOCKET_ALL
+  If you say Y here, you will be able to choose a GID of whose users will
+  be unable to connect to other hosts from your machine or run server
+  applications from your machine.  If the sysctl option is enabled, a
+  sysctl option with name "socket_all" is created.
+
+Group for disabled socket access
+CONFIG_GRKERNSEC_SOCKET_ALL_GID
+  Here you can choose the GID to disable socket access for. Remember to 
+  add the users you want socket access disabled for to the GID 
+  specified here.  If the sysctl option is enabled, whatever you choose
+  here won't matter. You'll have to specify the GID in your bootup 
+  script by echoing the GID to the proper /proc entry.  View the help
+  on the sysctl option for more information.  If the sysctl option is
+  enabled, a sysctl option with name "socket_all_gid" is created.
+
+Deny all client socket access
+CONFIG_GRKERNSEC_SOCKET_CLIENT
+  If you say Y here, you will be able to choose a GID of whose users will
+  be unable to connect to other hosts from your machine, but will be
+  able to run servers.  If this option is enabled, all users in the group
+  you specify will have to use passive mode when initiating ftp transfers
+  from the shell on your machine.  If the sysctl option is enabled, a
+  sysctl option with name "socket_client" is created.
+
+Group for disabled client socket access
+CONFIG_GRKERNSEC_SOCKET_CLIENT_GID
+  Here you can choose the GID to disable client socket access for. 
+  Remember to add the users you want client socket access disabled for to 
+  the GID specified here.  If the sysctl option is enabled, whatever you 
+  choose here won't matter. You'll have to specify the GID in your bootup 
+  script by echoing the GID to the proper /proc entry.  View the help
+  on the sysctl option for more information.  If the sysctl option is
+  enabled, a sysctl option with name "socket_client_gid" is created.
+
+Deny all server socket access
+CONFIG_GRKERNSEC_SOCKET_SERVER
+  If you say Y here, you will be able to choose a GID of whose users will
+  be unable to run server applications from your machine.  If the sysctl 
+  option is enabled, a sysctl option with name "socket_server" is created.
+
+Group for disabled server socket access
+CONFIG_GRKERNSEC_SOCKET_SERVER_GID
+  Here you can choose the GID to disable server socket access for. 
+  Remember to add the users you want server socket access disabled for to 
+  the GID specified here.  If the sysctl option is enabled, whatever you 
+  choose here won't matter. You'll have to specify the GID in your bootup 
+  script by echoing the GID to the proper /proc entry.  View the help
+  on the sysctl option for more information.  If the sysctl option is
+  enabled, a sysctl option with name "socket_server_gid" is created.
+
+Sysctl support
+CONFIG_GRKERNSEC_SYSCTL
+  If you say Y here, you will be able to change the options that
+  grsecurity runs with at bootup, without having to recompile your
+  kernel.  You can echo values to files in /proc/sys/kernel/grsecurity
+  to enable (1) or disable (0) various features.  All the sysctl entries
+  are mutable until the "grsec_lock" entry is set to a non-zero value.
+  All features are disabled by default. Please note that this option could 
+  reduce the effectiveness of the added security of this patch if an ACL 
+  system is not put in place.  Your init scripts should be read-only, and 
+  root should not have access to adding modules or performing raw i/o 
+  operations.  All options should be set at startup, and the grsec_lock 
+  entry should be set to a non-zero value after all the options are set.  
+  *THIS IS EXTREMELY IMPORTANT*
+
+Number of burst messages
+CONFIG_GRKERNSEC_FLOODBURST
+  This option allows you to choose the maximum number of messages allowed
+  within the flood time interval you chose in a separate option.  The 
+  default should be suitable for most people, however if you find that 
+  many of your logs are being interpreted as flooding, you may want to 
+  raise this value.
+
+Seconds in between log messages
+CONFIG_GRKERNSEC_FLOODTIME
+  This option allows you to enforce the number of seconds between
+  grsecurity log messages.  The default should be suitable for most 
+  people, however, if you choose to change it, choose a value small enough
+  to allow informative logs to be produced, but large enough to
+  prevent flooding.
+
+Hide kernel processes
+CONFIG_GRKERNSEC_ACL_HIDEKERN
+  If you say Y here, when the ACL system is enabled via gradm -E,
+  an additional ACL will be passed to the kernel that hides all kernel
+  processes.  These processes will only be viewable by the authenticated
+  admin, or processes that have viewing access set.
+
+Maximum tries before password lockout
+CONFIG_GRKERNSEC_ACL_MAXTRIES
+  This option enforces the maximum number of times a user can attempt
+  to authorize themselves with the grsecurity ACL system before being
+  denied the ability to attempt authorization again for a specified time.  
+  The lower the number, the harder it will be to brute-force a password.
+
+Time to wait after max password tries, in seconds
+CONFIG_GRKERNSEC_ACL_TIMEOUT
+  This option specifies the time the user must wait after attempting to 
+  authorize to the ACL system with the maximum number of invalid 
+  passwords.  The higher the number, the harder it will be to brute-force 
+  a password.
+
 Disable data cache
 CONFIG_DCACHE_DISABLE
   This option allows you to run the kernel with data cache disabled.
@@ -29304,4 +30359,505 @@
 # fill-prefix:"  "
 # adaptive-fill:nil
 # fill-column:70
+I2C mainboard interfaces
+CONFIG_I2C_MAINBOARD
+  Many modern mainboards have some kind of I2C interface integrated. This
+  is often in the form of a SMBus, or System Management Bus, which is
+  basically the same as I2C but which uses only a subset of the I2C
+  protocol.
+
+  You will also want the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Acer Labs ALI 1535
+CONFIG_I2C_ALI1535
+  If you say yes to this option, support will be included for the Acer
+  Labs ALI 1535 mainboard I2C interface. This can also be 
+  built as a module.
+
+Acer Labs ALI 1533 and 1543C
+CONFIG_I2C_ALI15X3
+  If you say yes to this option, support will be included for the Acer
+  Labs ALI 1533 and 1543C mainboard I2C interfaces. This can also be 
+  built as a module which can be inserted and removed while the kernel
+  is running.
+
+AMD 756/766/768/8111
+CONFIG_I2C_AMD756
+  If you say yes to this option, support will be included for the AMD
+  756/766/768/8111 mainboard I2C interfaces. This can also be 
+  built as a module which can be inserted and removed while the kernel
+  is running.
+
+AMD 8111 SMBus 2.0
+CONFIG_I2C_AMD8111
+  If you say yes to this option, support will be included for the AMD
+  8111 mainboard SMBus 2.0 interface. This can also be 
+  built as a module which can be inserted and removed while the kernel
+  is running.
+
+Apple Hydra Mac I/O
+CONFIG_I2C_HYDRA
+  If you say yes to this option, support will be included for the 
+  Hydra mainboard I2C interface. This can also be built as a module 
+  which can be inserted and removed while the kernel is running.
+
+Intel I801
+CONFIG_I2C_I801
+  If you say yes to this option, support will be included for the 
+  Intel I801 mainboard I2C interfaces. "I810" mainboard sensor chips are
+  generally located on the I801's I2C bus. This can also be
+  built as a module which can be inserted and removed while the kernel
+  is running.
+
+Intel I810/I815 based Mainboard
+CONFIG_I2C_I810
+  If you say yes to this option, support will be included for the 
+  Intel I810/I815 mainboard I2C interfaces. The I2C busses these chips
+  are generally used only for video devices. For "810" mainboard sensor
+  chips, use the I801 I2C driver instead. This can also be
+  built as a module which can be inserted and removed while the kernel
+  is running.
+
+Intel 82371AB PIIX4(E) / ServerWorks OSB4 and CSB5
+CONFIG_I2C_PIIX4
+  If you say yes to this option, support will be included for the 
+  Intel PIIX4, PIIX4E, and 443MX, Serverworks OSB4/CSB5,
+  and SMSC Victory66 mainboard
+  I2C interfaces. This can also be
+  built as a module which can be inserted and removed while the kernel
+  is running.
+
+Nvidia Nforce2 based Mainboard
+CONFIG_I2C_NFORCE2
+  If you say yes to this option, support will be included for the 
+  Nvidia Nforce2 family of mainboard I2C interfaces. This can also be
+  built as a module which can be inserted and removed while the kernel
+  is running.
+
+Silicon Integrated Systems Corp. SiS5595 based Mainboard
+CONFIG_I2C_SIS5595
+  If you say yes to this option, support will be included for the 
+  SiS5595 mainboard I2C interfaces. For integrated sensors on the
+  Sis5595, use CONFIG_SENSORS_SIS5595. This can also be
+  built as a module which can be inserted and removed while the kernel
+  is running.
+
+Silicon Integrated Systems Corp. SiS630/730 based Mainboard
+CONFIG_I2C_SIS630
+  If you say yes to this option, support will be included for the SiS 630
+  and 730 mainboard I2C interfaces. This can also be built as a module 
+  which can be inserted and removed while the kernel is running.
+
+Silicon Integrated Systems Corp. SiS645/961,645DX/961,735 based Mainboard
+CONFIG_I2C_SIS645
+  If you say yes to this option, support will be included for the SiS 645/961,
+  645DX/961 and 735 mainboard I2C interfaces. This can also be built as a module
+  which can be inserted and removed while the kernel is running.
+
+VIA Technologies, Inc. VT82C586B
+CONFIG_I2C_VIA
+  If you say yes to this option, support will be included for the VIA
+  Technologies I2C adapter found on some motherboards. This can also 
+  be built as a module which can be inserted and removed while the 
+  kernel is running.
+
+VIA Technologies, Inc. VT82C596, 596B, 686A/B, 8233, 8235
+CONFIG_I2C_VIAPRO
+  If you say yes to this option, support will be included for the VIA
+  Technologies I2C adapter on these chips. For integrated sensors on the
+  Via 686A/B, use CONFIG_SENSORS_VIA686A. This can also be
+  be built as a module which can be inserted and removed while the 
+  kernel is running.
+
+3DFX Banshee / Voodoo3
+CONFIG_I2C_VOODOO3
+  If you say yes to this option, support will be included for the 
+  3DFX Banshee and Voodoo3 I2C interfaces. The I2C busses on the these
+  chips are generally used only for video devices.
+  This can also be
+  built as a module which can be inserted and removed while the kernel
+  is running.
+
+DEC Tsunami 21272
+CONFIG_I2C_TSUNAMI
+  If you say yes to this option, support will be included for the DEC
+  Tsunami chipset I2C adapter. Requires the Alpha architecture;
+  do not enable otherwise. This can also be built as a module which
+  can be inserted and removed while the kernel is running.
+
+Pseudo ISA adapter (for hardware sensors modules)
+CONFIG_I2C_ISA
+  This provides support for accessing some hardware sensor chips over
+  the ISA bus rather than the I2C or SMBus. If you want to do this, 
+  say yes here. This feature can also be built as a module which can 
+  be inserted and removed while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Analog Devices ADM1021 and compatibles
+CONFIG_SENSORS_ADM1021 
+  If you say yes here you get support for Analog Devices ADM1021 
+  and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A,
+  Genesys Logic GL523SM, National Semi LM84, TI THMC10, Onsemi MC1066
+  and the XEON processor built-in sensor. This can also 
+  be built as a module which can be inserted and removed while the 
+  kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Analog Devices ADM1024
+CONFIG_SENSORS_ADM1024
+  If you say yes here you get support for Analog Devices ADM1024 sensor
+  chips.  This can also be built as a module.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Analog Devices ADM1025
+CONFIG_SENSORS_ADM1025
+  If you say yes here you get support for Analog Devices ADM1025 sensor
+  chips.  This can also be built as a module which can be inserted and
+  removed while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Analog Devices ADM1026
+CONFIG_SENSORS_ADM1026
+  If you say yes here you get support for Analog Devices ADM1026 sensor
+  chips.  This can also be built as a module which can be inserted and
+  removed while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Analog Devices ADM9240 and compatibles
+CONFIG_SENSORS_ADM9240
+  If you say yes here you get support for Analog Devices ADM9240 
+  sensor chips and clones: the Dallas Semiconductor DS1780 and
+  the National Semiconductor LM81. This can also be built as a 
+  module which can be inserted and removed while the kernel is
+  running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Asus ASB100
+CONFIG_SENSORS_ASB100
+  If you say yes here you get support for the Asus ASB100 (aka
+  "Bach") sensor chip.  This can also be built as a module.
+
+  You will also need the latest user-space utilities: you can find
+  them in the lm_sensors package, which you can download at
+  http://www.lm-sensors.nu/
+
+Dallas DS1621 and DS1625
+CONFIG_SENSORS_DS1621
+  If you say yes here you get support for the Dallas DS1621 and DS1625x
+  sensor chips.  This can also be built as a module.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Fujitsu-Siemens Poseidon
+CONFIG_SENSORS_FSCPOS
+  If you say yes here you get support for the Fujitsu-Siemens Poseidon
+  sensor chip.  This can also be built as a module.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Fujitsu-Siemens Scylla
+CONFIG_SENSORS_FSCSCY
+  If you say yes here you get support for the Fujitsu-Siemens Scylla
+  sensor chip.  This can also be built as a module. This driver may/should
+  also work with the following Fujitsu-Siemens chips: "Poseidon",
+  "Poseidon II" and "Hydra". You may have to force loading of the module
+  for motherboards in these cases. Be careful - those motherboards have
+  not been tested with this driver.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Genesys Logic GL518SM
+CONFIG_SENSORS_GL518SM
+  If you say yes here you get support for Genesys Logic GL518SM sensor
+  chips.  This can also be built as a module which can be inserted and
+  removed while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Genesys Logic GL520SM
+CONFIG_SENSORS_GL520SM
+  If you say yes here you get support for Genesys Logic GL518SM sensor
+  chips.  This can also be built as a module which can be inserted and
+  removed while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+HP Maxilife
+CONFIG_SENSORS_MAXILIFE
+  If you say yes here you get support for the HP Maxilife
+  sensor chip.  This can also be built as a module.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+ITE 8705, 8712, Sis950
+CONFIG_SENSORS_IT87
+  If you say yes here you get support for the ITE 8705 and 8712 and
+  SiS950 sensor chips.  This can also be built as a module.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Maxim MAX6650, MAX6651
+CONFIG_SENSORS_MAX6650
+  If you say yes here you get support for the Maxim MAX6650 and
+  MAX6651 sensor chips.  This can also be built as a module.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Myson MTP008
+CONFIG_SENSORS_MTP008
+  If you say yes here you get support for the Myson MTP008
+  sensor chip.  This can also be built as a module.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+National Semiconductor LM75 and compatibles
+CONFIG_SENSORS_LM75 
+  If you say yes here you get support for National Semiconductor LM75
+  sensor chips and clones: Dallas Semi DS75 and DS1775, TelCon
+  TCN75, and National Semi LM77. This can also be built as a module which
+  can be inserted and removed while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+National Semiconductor LM78
+CONFIG_SENSORS_LM78
+  If you say yes here you get support for National Semiconductor LM78
+  sensor chips family: the LM78-J and LM79. Many clone chips will
+  also work at least somewhat with this driver. This can also be built
+  as a module which can be inserted and removed while the kernel is 
+  running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+National Semiconductor LM80
+CONFIG_SENSORS_LM80
+  If you say yes here you get support for National Semiconductor LM80
+  sensor chips. This can also be built as a module which can be 
+  inserted and removed while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+National Semiconductor LM83
+CONFIG_SENSORS_LM83
+  If you say yes here you get support for the National Semiconductor
+  LM83 sensor chip.  This can also be built as a module.
+
+  You will also need the latest user-space utilities: you can find
+  them in the lm_sensors package, which you can download at
+  http://www.lm-sensors.nu/
+
+National Semiconductor LM85
+CONFIG_SENSORS_LM85
+  If you say yes here you get support for National Semiconductor LM85
+  sensor chips and compatibles.  Compatible chips include the Analog
+  Devices ADM1027 and ADT7463 and SMSC EMC6D100 and EMC6D101.  This
+  can also be built as a module which can be inserted and removed
+  while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+National Semiconductor LM87
+CONFIG_SENSORS_LM87
+  If you say yes here you get support for National Semiconductor LM87
+  sensor chips. This can also be built as a module which can be 
+  inserted and removed while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+National Semiconductor LM90
+CONFIG_SENSORS_LM90
+  If you say yes here you get support for the National Semiconductor
+  LM90, LM89 and LM99, and Analog Devices ADM1032 sensor chips.  This
+  can also be built as a module.
+
+  You will also need the latest user-space utilities: you can find
+  them in the lm_sensors package, which you can download at
+  http://www.lm-sensors.nu/
+
+National Semiconductor LM92
+CONFIG_SENSORS_LM92
+  If you say yes here you get support for National Semiconductor LM92
+  sensor chips. This can also be built as a module which can be 
+  inserted and removed while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Philips PCF8574
+CONFIG_SENSORS_PCF8574
+  If you say yes here you get support for the Philips PCF8574
+  I2C 8-bit Parallel I/O device.
+  This can also be built as a module which can be 
+  inserted and removed while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Philips PCF8591
+CONFIG_SENSORS_PCF8591
+  If you say yes here you get support for the Philips PCF8591
+  I2C Quad D/A + Single A/D I/O device.
+  This can also be built as a module which can be 
+  inserted and removed while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Silicon Integrated Systems Corp. SiS5595 Sensor
+CONFIG_SENSORS_SIS5595
+  If you say yes here you get support for the integrated sensors in 
+  SiS5595 South Bridges. This can also be built as a module 
+  which can be inserted and removed while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+SMSC47M1xx Super I/O Fan Support
+CONFIG_SENSORS_SMSC47M1
+  If you say yes here you get support for the integrated fan
+  monitoring and control in the SMSC 47M1xx Super I/O chips.
+  This can also be built as a module 
+  which can be inserted and removed while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Texas Instruments THMC50 / Analog Devices ADM1022
+CONFIG_SENSORS_THMC50
+  If you say yes here you get support for Texas Instruments THMC50
+  sensor chips and clones: the Analog Devices ADM1022.
+  This can also be built as a module which
+  can be inserted and removed while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Via VT82C686A/B
+CONFIG_SENSORS_VIA686A
+  If you say yes here you get support for the integrated sensors in 
+  Via 686A/B South Bridges. This can also be built as a module 
+  which can be inserted and removed while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Via VT1211 Sensors
+CONFIG_SENSORS_VT1211
+  If you say yes here you get support for the integrated sensors in 
+  the Via VT1211 Super I/O device. This can also be built as a module 
+  which can be inserted and removed while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Via VT8231 Sensors
+CONFIG_SENSORS_VT8231
+  If you say yes here you get support for the integrated sensors in 
+  the Via VT8231 device. This can also be built as a module 
+  which can be inserted and removed while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Winbond W83781D, W83782D, W83783S, W83627HF, AS99127F
+CONFIG_SENSORS_W83781D
+  If you say yes here you get support for the Winbond W8378x series 
+  of sensor chips: the W83781D, W83782D, W83783S and W83682HF,
+  and the similar Asus AS99127F. This
+  can also be built as a module which can be inserted and removed
+  while the kernel is running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
+Winbond W83627HF, W83627THF, W83697HF
+CONFIG_SENSORS_W83627HF
+  If you say yes here you get support for the Winbond W836x7 series 
+  of sensor chips: the Winbond W83627HF, W83627THF and W83697HF. This
+  can also be built as a module which can be inserted and removed
+  while the kernel is running.
+
+  You will also need the latest user-space utilities: you can find
+  them in the lm_sensors package, which you can download at
+  http://www.lm-sensors.nu/
+
+Winbond W83L785TS-S
+CONFIG_SENSORS_W83L785TS
+  If you say yes here you get support for the Winbond W83L785TS-S
+  sensor chip.  This can also be built as a module.
+
+  You will also need the latest user-space utilities: you can find
+  them in the lm_sensors package, which you can download at
+  http://www.lm-sensors.nu/
+
+EEprom (DIMM) reader
+CONFIG_SENSORS_EEPROM
+  If you say yes here you get read-only access to the EEPROM data 
+  available on modern memory DIMMs, and which could theoretically
+  also be available on other devices. This can also be built as a 
+  module which can be inserted and removed while the kernel is 
+  running.
+
+  You will also need the latest user-space utilties: you can find them
+  in the lm_sensors package, which you can download at 
+  http://www.lm-sensors.nu
+
 # End:
diff -urN --exclude-from=diff-exclude linux-2.4.27/Documentation/i2c/dev-interface linux-2.4.27-leo/Documentation/i2c/dev-interface
--- linux-2.4.27/Documentation/i2c/dev-interface	2004-02-20 14:11:36.000000000 +0000
+++ linux-2.4.27-leo/Documentation/i2c/dev-interface	2004-09-20 21:34:38.000000000 +0100
@@ -89,6 +89,11 @@
   Selects ten bit addresses if select not equals 0, selects normal 7 bit
   addresses if select equals 0. Default 0.
 
+ioctl(file,I2C_PEC,long select)
+  Selects SMBus PEC (packet error checking) generation and verification
+  if select not equals 0, disables if select equals 0. Default 0.
+  Used only for SMBus transactions.
+
 ioctl(file,I2C_FUNCS,unsigned long *funcs)
   Gets the adapter functionality and puts it in *funcs.
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/Documentation/i2c/i2c-pport linux-2.4.27-leo/Documentation/i2c/i2c-pport
--- linux-2.4.27/Documentation/i2c/i2c-pport	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/Documentation/i2c/i2c-pport	2004-09-20 21:34:38.000000000 +0100
@@ -0,0 +1,67 @@
+Parallel Port Adapters
+----------------------
+If you are installing parallel port adapters it means you are probably messing 
+around with wires and IC's and the like. If you have purchased a card that 
+provides an external i2c/smbus this will require combined algorithm and 
+adapter code in a single module.
+If you are doing it yourself by using the parallel port there 
+are basically 2 options.
+
+1) Using the parallel port and using the i2c-pport adapter module and the 
+i2c-algo-bit algorithm module together to enable you to wire up your parallel 
+port to act as an i2c/smbus. This provides a bus that will enable most 
+sensors to work but doesn't support the entire i2c/smbus capability.
+
+2) Using the parallel port to interface to a Philips PCF8584 parallel to i2c 
+adapter chip. You will need to build a bit of a circuit to do this. This 
+configuration needs the i2c-pcf-epp adapter module and the i2c-algo-pcf 
+algorithm module. This support almost all of the i2c/smbus capabilities.
+
+
+i2c-pport Documentation
+-----------------------
+This is a primitive parallel port driver for the i2c bus, which exploits 
+features of modern bidirectional parallel ports. 
+
+Bidirectional ports have particular bits connected in following way:
+   
+                        |
+            /-----|     R
+         --o|     |-----|
+      read  \-----|     /------- Out pin
+                      |/
+                   - -|\
+                write   V
+                        |
+                       ---  
+
+
+It means when output is set to 1 we can read the port. Therefore 
+we can use 2 pins of parallel port as SDA and SCL for i2c bus. It 
+is not necessary to add any external - additional parts, we can 
+read and write the same port simultaneously.
+	I only use register base+2 so it is possible to use all 
+8 data bits of parallel port for other applications (I have 
+connected EEPROM and LCD display). I do not use bit Enable Bi-directional
+ Port. The only disadvantage is we can only support 5V chips.
+
+Layout:
+
+Cannon 25 pin
+
+SDA - connect to pin 14 (Auto Linefeed)
+SCL - connect to pin 16 (Initialize Printer)
+GND - connect to pin 18-25
++5V - use external supply (I use 5V from 3.5" floppy connector)
+      
+no pullups  requied
+
+Module parameters:
+
+base = 0xXXX
+XXX - 278 or 378
+
+That's all.
+
+Daniel Smolik
+marvin@sitour.cz
diff -urN --exclude-from=diff-exclude linux-2.4.27/Documentation/i2c/i2c-protocol linux-2.4.27-leo/Documentation/i2c/i2c-protocol
--- linux-2.4.27/Documentation/i2c/i2c-protocol	2004-02-20 14:11:36.000000000 +0000
+++ linux-2.4.27-leo/Documentation/i2c/i2c-protocol	2004-09-20 21:34:38.000000000 +0100
@@ -65,3 +65,12 @@
     need to emit an Rd instead of a Wr, or vice versa, you set this
     flag. For example:
       S Addr Rd [A] Data [A] Data [A] ... [A] Data [A] P
+
+  Flags I2C_M_IGNORE_NAK
+    Normally message is interrupted immediately if there is [NA] from the
+    client. Setting this flag treats any [NA] as [A], and all of
+    message is sent.
+    These messages may still fail to SCL lo->hi timeout.
+
+  Flags I2C_M_NO_RD_ACK
+    In a read message, master A/NA bit is skipped.
diff -urN --exclude-from=diff-exclude linux-2.4.27/Documentation/i2c/summary linux-2.4.27-leo/Documentation/i2c/summary
--- linux-2.4.27/Documentation/i2c/summary	2004-02-20 14:11:36.000000000 +0000
+++ linux-2.4.27-leo/Documentation/i2c/summary	2004-09-20 21:34:38.000000000 +0100
@@ -59,16 +59,16 @@
 i2c-algo-8xx:    An algorithm for CPM's I2C device in Motorola 8xx processors (NOT BUILT BY DEFAULT)
 i2c-algo-bit:    A bit-banging algorithm
 i2c-algo-pcf:    A PCF 8584 style algorithm
-i2c-algo-ppc405: An algorithm for the I2C device in IBM 405xx processors (NOT BUILT BY DEFAULT)
+i2c-algo-ibm_ocp: An algorithm for the I2C device in IBM 4xx processors (NOT BUILT BY DEFAULT)
 
 Adapter drivers
 ---------------
 
 i2c-elektor:     Elektor ISA card (uses i2c-algo-pcf)
 i2c-elv:         ELV parallel port adapter (uses i2c-algo-bit)
-i2c-pcf-epp:     PCF8584 on a EPP parallel port (uses i2c-algo-pcf) (BROKEN - missing i2c-pcf-epp.h)
+i2c-pcf-epp:     PCF8584 on a EPP parallel port (uses i2c-algo-pcf) (NOT mkpatched)
 i2c-philips-par: Philips style parallel port adapter (uses i2c-algo-bit)
-i2c-ppc405:      IBM 405xx processor I2C device (uses i2c-algo-ppc405) (NOT BUILT BY DEFAULT)
+i2c-adap-ibm_ocp: IBM 4xx processor I2C device (uses i2c-algo-ibm_ocp) (NOT BUILT BY DEFAULT)
 i2c-pport:       Primitive parallel port adapter (uses i2c-algo-bit)
 i2c-rpx:         RPX board Motorola 8xx I2C device (uses i2c-algo-8xx) (NOT BUILT BY DEFAULT)
 i2c-velleman:    Velleman K8000 parallel port adapter (uses i2c-algo-bit)
diff -urN --exclude-from=diff-exclude linux-2.4.27/Documentation/i2c/writing-clients linux-2.4.27-leo/Documentation/i2c/writing-clients
--- linux-2.4.27/Documentation/i2c/writing-clients	2004-02-20 14:11:36.000000000 +0000
+++ linux-2.4.27-leo/Documentation/i2c/writing-clients	2004-09-20 21:34:38.000000000 +0100
@@ -24,16 +24,14 @@
 routines, a client structure specific information like the actual I2C
 address.
 
-  struct i2c_driver foo_driver
-  {  
-    /* name           */  "Foo version 2.3 and later driver",
-    /* id             */  I2C_DRIVERID_FOO,
-    /* flags          */  I2C_DF_NOTIFY,
-    /* attach_adapter */  &foo_attach_adapter,
-    /* detach_client  */  &foo_detach_client,
-    /* command        */  &foo_command,   /* May be NULL */
-    /* inc_use        */  &foo_inc_use,   /* May be NULL */
-    /* dec_use        */  &foo_dec_use    /* May be NULL */
+  static struct i2c_driver foo_driver = {
+    .owner          = THIS_MODULE,
+    .name           = "Foo version 2.3 driver",
+    .id             = I2C_DRIVERID_FOO, /* usually from i2c-id.h */
+    .flags          = I2C_DF_NOTIFY,
+    .attach_adapter = &foo_attach_adapter,
+    .detach_client  = &foo_detach_client,
+    .command        = &foo_command /* may be NULL */
   }
  
 The name can be chosen freely, and may be upto 40 characters long. Please
@@ -50,43 +48,8 @@
 All other fields are for call-back functions which will be explained 
 below.
 
-
-Module usage count
-==================
-
-If your driver can also be compiled as a module, there are moments at 
-which the module can not be removed from memory. For example, when you
-are doing a lengthy transaction, or when you create a /proc directory,
-and some process has entered that directory (this last case is the
-main reason why these call-backs were introduced).
-
-To increase or decrease the module usage count, you can use the
-MOD_{INC,DEC}_USE_COUNT macros. They must be called from the module
-which needs to get its usage count changed; that is why each driver
-module has to implement its own callback.
-
-  void foo_inc_use (struct i2c_client *client)
-  {
-  #ifdef MODULE
-    MOD_INC_USE_COUNT;
-  #endif
-  }
-
-  void foo_dec_use (struct i2c_client *client)
-  {
-  #ifdef MODULE
-    MOD_DEC_USE_COUNT;
-  #endif
-  }
-
-Do not call these call-back functions directly; instead, use one of the
-following functions defined in i2c.h:
-  void i2c_inc_use_client(struct i2c_client *);
-  void i2c_dec_use_client(struct i2c_client *);
-
-You should *not* increase the module count just because a device is
-detected and a client created. This would make it impossible to remove
-an adapter driver! 
+There use to be two additional fields in this structure, inc_use et dec_use,
+for module usage count, but these fields were obsoleted and removed.
 
 
 Extra client data
diff -urN --exclude-from=diff-exclude linux-2.4.27/Documentation/preempt-locking.txt linux-2.4.27-leo/Documentation/preempt-locking.txt
--- linux-2.4.27/Documentation/preempt-locking.txt	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/Documentation/preempt-locking.txt	2004-09-17 03:19:36.000000000 +0100
@@ -0,0 +1,104 @@
+		  Proper Locking Under a Preemptible Kernel:
+		       Keeping Kernel Code Preempt-Safe
+			  Robert Love <rml@tech9.net>
+			   Last Updated: 22 Jan 2002
+
+
+INTRODUCTION
+
+
+A preemptible kernel creates new locking issues.  The issues are the same as
+those under SMP: concurrency and reentrancy.  Thankfully, the Linux preemptible
+kernel model leverages existing SMP locking mechanisms.  Thus, the kernel
+requires explicit additional locking for very few additional situations.
+
+This document is for all kernel hackers.  Developing code in the kernel
+requires protecting these situations.
+ 
+
+RULE #1: Per-CPU data structures need explicit protection
+
+
+Two similar problems arise. An example code snippet:
+
+	struct this_needs_locking tux[NR_CPUS];
+	tux[smp_processor_id()] = some_value;
+	/* task is preempted here... */
+	something = tux[smp_processor_id()];
+
+First, since the data is per-CPU, it may not have explicit SMP locking, but
+require it otherwise.  Second, when a preempted task is finally rescheduled,
+the previous value of smp_processor_id may not equal the current.  You must
+protect these situations by disabling preemption around them.
+
+
+RULE #2: CPU state must be protected.
+
+
+Under preemption, the state of the CPU must be protected.  This is arch-
+dependent, but includes CPU structures and state not preserved over a context
+switch.  For example, on x86, entering and exiting FPU mode is now a critical
+section that must occur while preemption is disabled.  Think what would happen
+if the kernel is executing a floating-point instruction and is then preempted.
+Remember, the kernel does not save FPU state except for user tasks.  Therefore,
+upon preemption, the FPU registers will be sold to the lowest bidder.  Thus,
+preemption must be disabled around such regions.
+
+Note, some FPU functions are already explicitly preempt safe.  For example,
+kernel_fpu_begin and kernel_fpu_end will disable and enable preemption.
+However, math_state_restore must be called with preemption disabled.
+
+
+RULE #3: Lock acquire and release must be performed by same task
+
+
+A lock acquired in one task must be released by the same task.  This
+means you can't do oddball things like acquire a lock and go off to
+play while another task releases it.  If you want to do something
+like this, acquire and release the task in the same code path and
+have the caller wait on an event by the other task.
+
+
+SOLUTION
+
+
+Data protection under preemption is achieved by disabling preemption for the
+duration of the critical region.
+
+preempt_enable()		decrement the preempt counter
+preempt_disable()		increment the preempt counter
+preempt_enable_no_resched()	decrement, but do not immediately preempt
+preempt_get_count()		return the preempt counter
+
+The functions are nestable.  In other words, you can call preempt_disable
+n-times in a code path, and preemption will not be reenabled until the n-th
+call to preempt_enable.  The preempt statements define to nothing if
+preemption is not enabled.
+
+Note that you do not need to explicitly prevent preemption if you are holding
+any locks or interrupts are disabled, since preemption is implicitly disabled
+in those cases.
+
+Example:
+
+	cpucache_t *cc; /* this is per-CPU */
+	preempt_disable();
+	cc = cc_data(searchp);
+	if (cc && cc->avail) {
+		__free_block(searchp, cc_entry(cc), cc->avail);
+		cc->avail = 0;
+	}
+	preempt_enable();
+	return 0;
+
+Notice how the preemption statements must encompass every reference of the
+critical variables.  Another example:
+
+	int buf[NR_CPUS];
+	set_cpu_val(buf);
+	if (buf[smp_processor_id()] == -1) printf(KERN_INFO "wee!\n");
+	spin_lock(&buf_lock);
+	/* ... */
+
+This code is not preempt-safe, but see how easily we can fix it by simply
+moving the spin_lock up two lines.
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/block/Config.in linux-2.4.27-leo/drivers/block/Config.in
--- linux-2.4.27/drivers/block/Config.in	2004-09-17 02:38:43.000000000 +0100
+++ linux-2.4.27-leo/drivers/block/Config.in	2004-09-17 03:19:04.000000000 +0100
@@ -42,6 +42,7 @@
 dep_tristate 'Promise SATA SX8 support' CONFIG_BLK_DEV_SX8 $CONFIG_PCI
 
 tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP
+dep_tristate '  Cryptoloop support' CONFIG_BLK_DEV_CRYPTOLOOP $CONFIG_BLK_DEV_LOOP
 dep_tristate 'Network block device support' CONFIG_BLK_DEV_NBD $CONFIG_NET
 
 tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/block/cryptoloop.c linux-2.4.27-leo/drivers/block/cryptoloop.c
--- linux-2.4.27/drivers/block/cryptoloop.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/block/cryptoloop.c	2004-09-17 03:19:04.000000000 +0100
@@ -0,0 +1,179 @@
+/*
+   Linux loop encryption enabling module
+
+   Copyright (C)  2002 Herbert Valerio Riedel <hvr@gnu.org>
+   Copyright (C)  2003 Fruhwirth Clemens <clemens@endorphin.org>
+
+   This module is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This module is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this module; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/module.h>
+
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/crypto.h>
+#include <linux/blkdev.h>
+#include <linux/loop.h>
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+#include <asm/scatterlist.h>
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("loop blockdevice transferfunction adaptor / CryptoAPI");
+MODULE_AUTHOR("Herbert Valerio Riedel <hvr@gnu.org>");
+
+static int
+cryptoloop_init(struct loop_device *lo, /* const */ struct loop_info *info)
+{
+	int err = -EINVAL;
+	char cms[LO_NAME_SIZE];			/* cipher-mode string */
+	char *cipher;
+	char *mode;
+	char *cmsp = cms;			/* c-m string pointer */
+	struct crypto_tfm *tfm = NULL;
+
+	/* encryption breaks for non sector aligned offsets */
+
+	if (info->lo_offset % LOOP_IV_SECTOR_SIZE)
+		goto out;
+
+	strncpy(cms, info->lo_name, LO_NAME_SIZE);
+	cms[LO_NAME_SIZE - 1] = 0;
+	cipher = strsep(&cmsp, "-");
+	mode = strsep(&cmsp, "-");
+
+	if (mode == NULL || strcmp(mode, "cbc") == 0)
+		tfm = crypto_alloc_tfm(cipher, CRYPTO_TFM_MODE_CBC);
+	else if (strcmp(mode, "ecb") == 0)
+		tfm = crypto_alloc_tfm(cipher, CRYPTO_TFM_MODE_ECB);
+	if (tfm == NULL)
+		return -EINVAL;
+
+	err = tfm->crt_u.cipher.cit_setkey(tfm, info->lo_encrypt_key,
+					   info->lo_encrypt_key_size);
+	
+	if (err != 0)
+		goto out_free_tfm;
+
+	lo->key_data = tfm;
+	return 0;
+
+ out_free_tfm:
+	crypto_free_tfm(tfm);
+
+ out:
+	return err;
+}
+
+typedef int (*encdec_t)(struct crypto_tfm *tfm,
+			struct scatterlist *sg_out,
+			struct scatterlist *sg_in,
+			unsigned int nsg, u8 *iv);
+
+static int
+cryptoloop_transfer(struct loop_device *lo, int cmd, char *raw_buf,
+		     char *loop_buf, int size, sector_t IV)
+{
+	struct crypto_tfm *tfm = (struct crypto_tfm *) lo->key_data;
+	struct scatterlist sg_out = { 0, };
+	struct scatterlist sg_in = { 0, };
+
+	encdec_t encdecfunc;
+	char const *in;
+	char *out;
+
+	if (cmd == READ) {
+		in = raw_buf;
+		out = loop_buf;
+		encdecfunc = tfm->crt_u.cipher.cit_decrypt_iv;
+	} else {
+		in = loop_buf;
+		out = raw_buf;
+		encdecfunc = tfm->crt_u.cipher.cit_encrypt_iv;
+	}
+
+	while (size > 0) {
+		const int sz = min(size, LOOP_IV_SECTOR_SIZE);
+		u32 iv[4] = { 0, };
+		iv[0] = cpu_to_le32(IV & 0xffffffff);
+
+		sg_in.page = virt_to_page(in);
+		sg_in.offset = (unsigned long)in & ~PAGE_MASK;
+		sg_in.length = sz;
+
+		sg_out.page = virt_to_page(out);
+		sg_out.offset = (unsigned long)out & ~PAGE_MASK;
+		sg_out.length = sz;
+
+		encdecfunc(tfm, &sg_out, &sg_in, sz, (u8 *)iv);
+
+		IV++;
+		size -= sz;
+		in += sz;
+		out += sz;
+	}
+
+	return 0;
+}
+
+
+static int
+cryptoloop_ioctl(struct loop_device *lo, int cmd, unsigned long arg)
+{
+	return -EINVAL;
+}
+
+static int
+cryptoloop_release(struct loop_device *lo)
+{
+	struct crypto_tfm *tfm = (struct crypto_tfm *) lo->key_data;
+	if (tfm != NULL) {
+		crypto_free_tfm(tfm);
+		lo->key_data = NULL;
+		return 0;
+	}
+	printk(KERN_ERR "cryptoloop_release(): tfm == NULL?\n");
+	return -EINVAL;
+}
+
+static struct loop_func_table cryptoloop_funcs = {
+	.number = LO_CRYPT_CRYPTOAPI,
+	.init = cryptoloop_init,
+	.ioctl = cryptoloop_ioctl,
+	.transfer = cryptoloop_transfer,
+	.release = cryptoloop_release,
+	/* .owner = THIS_MODULE */
+};
+
+static int __init
+init_cryptoloop(void)
+{
+	int rc = loop_register_transfer(&cryptoloop_funcs);
+
+	if (rc)
+		printk(KERN_ERR "cryptoloop: loop_register_transfer failed\n");
+	return rc;
+}
+
+static void __exit
+cleanup_cryptoloop(void)
+{
+	if (loop_unregister_transfer(LO_CRYPT_CRYPTOAPI))
+		printk(KERN_ERR
+			"cryptoloop: loop_unregister_transfer failed\n");
+}
+
+module_init(init_cryptoloop);
+module_exit(cleanup_cryptoloop);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/block/loop.c linux-2.4.27-leo/drivers/block/loop.c
--- linux-2.4.27/drivers/block/loop.c	2003-08-25 12:44:41.000000000 +0100
+++ linux-2.4.27-leo/drivers/block/loop.c	2004-09-17 03:19:04.000000000 +0100
@@ -88,7 +88,7 @@
  * Transfer functions
  */
 static int transfer_none(struct loop_device *lo, int cmd, char *raw_buf,
-			 char *loop_buf, int size, int real_block)
+			 char *loop_buf, int size, sector_t IV)
 {
 	if (raw_buf != loop_buf) {
 		if (cmd == READ)
@@ -101,7 +101,7 @@
 }
 
 static int transfer_xor(struct loop_device *lo, int cmd, char *raw_buf,
-			char *loop_buf, int size, int real_block)
+			char *loop_buf, int size, sector_t IV)
 {
 	char	*in, *out, *key;
 	int	i, keysize;
@@ -189,7 +189,7 @@
 	len = bh->b_size;
 	data = bh->b_data;
 	while (len > 0) {
-		int IV = index * (PAGE_CACHE_SIZE/bsize) + offset/bsize;
+		const sector_t IV = (index << (PAGE_CACHE_SHIFT - LOOP_IV_SECTOR_BITS)) + (offset >> LOOP_IV_SECTOR_BITS);
 		int transfer_result;
 
 		size = PAGE_CACHE_SIZE - offset;
@@ -249,7 +249,7 @@
 	unsigned long count = desc->count;
 	struct lo_read_data *p = (struct lo_read_data*)desc->buf;
 	struct loop_device *lo = p->lo;
-	int IV = page->index * (PAGE_CACHE_SIZE/p->bsize) + offset/p->bsize;
+	const sector_t IV = (page->index << (PAGE_CACHE_SHIFT - LOOP_IV_SECTOR_BITS)) + (offset >> LOOP_IV_SECTOR_BITS);
 
 	if (size > count)
 		size = count;
@@ -301,20 +301,6 @@
 	return bs;
 }
 
-static inline unsigned long loop_get_iv(struct loop_device *lo,
-					unsigned long sector)
-{
-	int bs = loop_get_bs(lo);
-	unsigned long offset, IV;
-
-	IV = sector / (bs >> 9) + lo->lo_offset / bs;
-	offset = ((sector % (bs >> 9)) << 9) + lo->lo_offset % bs;
-	if (offset >= bs)
-		IV++;
-
-	return IV;
-}
-
 static int do_bh_filebacked(struct loop_device *lo, struct buffer_head *bh, int rw)
 {
 	loff_t pos;
@@ -462,7 +448,7 @@
 {
 	struct buffer_head *bh = NULL;
 	struct loop_device *lo;
-	unsigned long IV;
+	sector_t IV;
 
 	if (!buffer_locked(rbh))
 		BUG();
@@ -507,7 +493,7 @@
 	 * piggy old buffer on original, and submit for I/O
 	 */
 	bh = loop_get_buffer(lo, rbh);
-	IV = loop_get_iv(lo, rbh->b_rsector);
+	IV = rbh->b_rsector + (lo->lo_offset >> LOOP_IV_SECTOR_BITS);
 	if (rw == WRITE) {
 		set_bit(BH_Dirty, &bh->b_state);
 		if (lo_do_transfer(lo, WRITE, bh->b_data, rbh->b_data,
@@ -544,7 +530,7 @@
 		bh->b_end_io(bh, !ret);
 	} else {
 		struct buffer_head *rbh = bh->b_private;
-		unsigned long IV = loop_get_iv(lo, rbh->b_rsector);
+		const sector_t IV = rbh->b_rsector + (lo->lo_offset >> LOOP_IV_SECTOR_BITS);
 
 		ret = lo_do_transfer(lo, READ, bh->b_data, rbh->b_data,
 				     bh->b_size, IV);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/block/Makefile linux-2.4.27-leo/drivers/block/Makefile
--- linux-2.4.27/drivers/block/Makefile	2004-09-17 02:59:19.000000000 +0100
+++ linux-2.4.27-leo/drivers/block/Makefile	2004-09-17 03:19:04.000000000 +0100
@@ -33,6 +33,7 @@
 obj-$(CONFIG_BLK_DEV_NBD)	+= nbd.o
 obj-$(CONFIG_BLK_DEV_SX8)	+= sx8.o
 obj-$(CONFIG_BLK_DEV_CRYPTOLOOP) += cryptoloop.o
+obj-$(CONFIG_BLK_DEV_CRYPTOLOOP) += cryptoloop.o
 
 subdir-$(CONFIG_PARIDE) += paride
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/char/Config.in linux-2.4.27-leo/drivers/char/Config.in
--- linux-2.4.27/drivers/char/Config.in	2004-09-17 02:38:43.000000000 +0100
+++ linux-2.4.27-leo/drivers/char/Config.in	2004-09-20 21:34:47.000000000 +0100
@@ -190,6 +190,8 @@
 
 source drivers/i2c/Config.in
 
+source drivers/sensors/Config.in
+
 mainmenu_option next_comment
 comment 'Mice'
 tristate 'Bus Mouse Support' CONFIG_BUSMOUSE
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/char/keyboard.c linux-2.4.27-leo/drivers/char/keyboard.c
--- linux-2.4.27/drivers/char/keyboard.c	2003-11-28 18:26:20.000000000 +0000
+++ linux-2.4.27-leo/drivers/char/keyboard.c	2004-09-17 03:19:58.000000000 +0100
@@ -545,6 +545,16 @@
 	if ((kbd->kbdmode == VC_RAW || kbd->kbdmode == VC_MEDIUMRAW) &&
 	    !(SPECIALS_ALLOWED_IN_RAW_MODE & (1 << value)))
 		return;
+
+#if defined(CONFIG_GRKERNSEC_PROC) || defined(CONFIG_GRKERNSEC_PROC_MEMMAP)
+	{
+		void *func = spec_fn_table[value];
+		if (func == show_state || func == show_ptregs ||
+		    func == show_mem)
+			return;
+	}
+#endif
+
 	spec_fn_table[value]();
 }
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/char/mem.c linux-2.4.27-leo/drivers/char/mem.c
--- linux-2.4.27/drivers/char/mem.c	2004-09-17 02:38:44.000000000 +0100
+++ linux-2.4.27-leo/drivers/char/mem.c	2004-09-17 03:20:32.000000000 +0100
@@ -22,6 +22,7 @@
 #include <linux/tty.h>
 #include <linux/capability.h>
 #include <linux/ptrace.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -42,6 +43,10 @@
 #if defined(CONFIG_S390_TAPE) && defined(CONFIG_S390_TAPE_CHAR)
 extern void tapechar_init(void);
 #endif
+
+#ifdef CONFIG_GRKERNSEC
+extern struct file_operations grsec_fops;
+#endif
      
 static ssize_t do_write_mem(struct file * file, void *p, unsigned long realp,
 			    const char * buf, size_t count, loff_t *ppos)
@@ -115,6 +120,11 @@
 	unsigned long p = *ppos;
 	unsigned long end_mem;
 
+#ifdef CONFIG_GRKERNSEC_KMEM
+	gr_handle_mem_write();
+	return -EPERM;
+#endif
+
 	end_mem = __pa(high_memory);
 	if (p >= end_mem)
 		return 0;
@@ -187,6 +197,12 @@
 {
 	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
 
+#ifdef CONFIG_GRKERNSEC_KMEM
+	if (gr_handle_mem_mmap(offset, vma))
+		return -EPERM;
+#endif
+
+
 	/*
 	 * Accessing memory above the top the kernel knows about or
 	 * through a file pointer that was marked O_SYNC will be
@@ -286,6 +302,11 @@
 	ssize_t virtr = 0;
 	char * kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */
 
+#ifdef CONFIG_GRKERNSEC_KMEM
+	gr_handle_kmem_write();
+	return -EPERM;
+#endif
+
 	if (p < (unsigned long) high_memory) {
 		wrote = count;
 		if (count > (unsigned long) high_memory - p)
@@ -402,7 +423,23 @@
 			count = size;
 
 		zap_page_range(mm, addr, count);
-        	zeromap_page_range(addr, count, PAGE_COPY);
+	        zeromap_page_range(addr, count, vma->vm_page_prot); 
+
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+		if (vma->vm_flags & VM_MIRROR) {
+			unsigned long addr_m;
+			struct vm_area_struct * vma_m;
+
+			addr_m = vma->vm_start + (unsigned long)vma->vm_private_data;
+			vma_m = find_vma(mm, addr_m);
+			if (vma_m && vma_m->vm_start == addr_m && (vma_m->vm_flags & VM_MIRROR)) {
+				addr_m = addr + (unsigned long)vma->vm_private_data;
+				zap_page_range(mm, addr_m, count);
+			} else
+				printk(KERN_ERR "PAX: VMMIRROR: read_zero bug, %08lx, %08lx\n",
+				       addr, vma->vm_start);
+		}
+#endif
 
 		size -= count;
 		buf += count;
@@ -525,6 +562,15 @@
 
 static int open_port(struct inode * inode, struct file * filp)
 {
+#ifdef CONFIG_GRKERNSEC_PORT
+	gr_handle_open_port();
+	return -EPERM;
+#endif
+	return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
+}
+
+static int open_mem(struct inode * inode, struct file * filp)
+{
 	return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
 }
 
@@ -582,6 +628,11 @@
 	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
 	unsigned long size = vma->vm_end - vma->vm_start;
 
+#ifdef CONFIG_GRKERNSEC_KMEM
+	if (gr_handle_mem_mmap(offset, vma))
+		return -EPERM;
+#endif
+
 	/*
 	 * If the user is not attempting to mmap a high memory address then
 	 * the standard mmap_mem mechanism will work.  High memory addresses
@@ -617,7 +668,6 @@
 #define full_lseek      null_lseek
 #define write_zero	write_null
 #define read_full       read_zero
-#define open_mem	open_port
 #define open_kmem	open_mem
 
 static struct file_operations mem_fops = {
@@ -693,6 +743,11 @@
 		case 9:
 			filp->f_op = &urandom_fops;
 			break;
+#ifdef CONFIG_GRKERNSEC
+		case 12:
+			filp->f_op = &grsec_fops;
+			break;
+#endif
 		default:
 			return -ENXIO;
 	}
@@ -719,7 +774,10 @@
 	{5, "zero",    S_IRUGO | S_IWUGO,           &zero_fops},
 	{7, "full",    S_IRUGO | S_IWUGO,           &full_fops},
 	{8, "random",  S_IRUGO | S_IWUSR,           &random_fops},
-	{9, "urandom", S_IRUGO | S_IWUSR,           &urandom_fops}
+	{9, "urandom", S_IRUGO | S_IWUSR,           &urandom_fops},
+#ifdef CONFIG_GRKERNSEC
+	{12,"grsec",   S_IRUSR | S_IWUGO,	    &grsec_fops}
+#endif
     };
     int i;
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/char/random.c linux-2.4.27-leo/drivers/char/random.c
--- linux-2.4.27/drivers/char/random.c	2004-02-20 14:11:41.000000000 +0000
+++ linux-2.4.27-leo/drivers/char/random.c	2004-09-17 03:19:58.000000000 +0100
@@ -262,9 +262,15 @@
 /*
  * Configuration information
  */
+#ifdef CONFIG_GRKERNSEC_RANDNET
+#define DEFAULT_POOL_SIZE 1024
+#define SECONDARY_POOL_SIZE 256
+#define BATCH_ENTROPY_SIZE 512
+#else
 #define DEFAULT_POOL_SIZE 512
 #define SECONDARY_POOL_SIZE 128
 #define BATCH_ENTROPY_SIZE 256
+#endif
 #define USE_SHA
 
 /*
@@ -389,6 +395,7 @@
 /*
  * Static global variables
  */
+
 static struct entropy_store *random_state; /* The default global store */
 static struct entropy_store *sec_random_state; /* secondary store */
 static DECLARE_WAIT_QUEUE_HEAD(random_read_wait);
@@ -2210,6 +2217,29 @@
 	return halfMD4Transform(hash, keyptr->secret);
 }
 
+#ifdef CONFIG_GRKERNSEC
+/* the following function is provided by PaX under the GPL */
+unsigned long get_random_long(void)
+{       
+	static time_t rekey_time;
+	static __u32 secret[12];
+	time_t t;
+
+	/*
+	 * Pick a random secret every REKEY_INTERVAL seconds
+	 */
+	t = CURRENT_TIME;
+	if (!rekey_time || (t - rekey_time) > REKEY_INTERVAL) {
+		rekey_time = t;
+		get_random_bytes(secret, sizeof(secret));
+	}
+
+	secret[1] = halfMD4Transform(secret+8, secret);
+	secret[0] = halfMD4Transform(secret+8, secret);
+	return *(unsigned long *)secret;
+}
+#endif
+
 #ifdef CONFIG_SYN_COOKIES
 /*
  * Secure SYN cookie computation. This is the algorithm worked out by
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/char/tty_io.c linux-2.4.27-leo/drivers/char/tty_io.c
--- linux-2.4.27/drivers/char/tty_io.c	2004-05-19 21:34:38.000000000 +0100
+++ linux-2.4.27-leo/drivers/char/tty_io.c	2004-09-17 03:19:58.000000000 +0100
@@ -1404,7 +1404,11 @@
 		retval = -ENODEV;
 	filp->f_flags = saved_flags;
 
+#ifdef CONFIG_GRKERNSEC
+	if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_TTY_CONFIG))
+#else
 	if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && !suser())
+#endif
 		retval = -EBUSY;
 
 	if (retval) {
@@ -1506,7 +1510,11 @@
 {
 	char ch, mbz = 0;
 
+#ifdef CONFIG_GRKERNSEC
+	if ((current->tty != tty) && !capable(CAP_SYS_TTY_CONFIG))
+#else
 	if ((current->tty != tty) && !suser())
+#endif
 		return -EPERM;
 	if (get_user(ch, arg))
 		return -EFAULT;
@@ -1544,7 +1552,11 @@
 	if (inode->i_rdev == SYSCONS_DEV ||
 	    inode->i_rdev == CONSOLE_DEV) {
 		struct file *f;
+#ifdef CONFIG_GRKERNSEC
+		if (!capable(CAP_SYS_TTY_CONFIG))
+#else
 		if (!suser())
+#endif
 			return -EPERM;
 		spin_lock(&redirect_lock);
 		f = redirect;
@@ -1596,7 +1608,11 @@
 		 * This tty is already the controlling
 		 * tty for another session group!
 		 */
+#ifdef CONFIG_GRKERNSEC
+		if ((arg == 1) && capable(CAP_SYS_ADMIN)) {
+#else
 		if ((arg == 1) && suser()) {
+#endif
 			/*
 			 * Steal it away
 			 */
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/char/vt.c linux-2.4.27-leo/drivers/char/vt.c
--- linux-2.4.27/drivers/char/vt.c	2002-11-28 23:53:12.000000000 +0000
+++ linux-2.4.27-leo/drivers/char/vt.c	2004-09-17 03:19:58.000000000 +0100
@@ -179,6 +179,11 @@
 	case KDSKBENT:
 		if (!perm)
 			return -EPERM;
+#ifdef CONFIG_GRKERNSEC
+		if (!capable(CAP_SYS_TTY_CONFIG))
+			return -EPERM;
+#endif
+
 		if (!i && v == K_NOSUCHMAP) {
 			/* disallocate map */
 			key_map = key_maps[s];
@@ -301,6 +306,11 @@
 		if (!perm)
 			return -EPERM;
 
+#ifdef CONFIG_GRKERNSEC
+		if (!capable(CAP_SYS_TTY_CONFIG))
+			return -EPERM;
+#endif
+
 		q = func_table[i];
 		first_free = funcbufptr + (funcbufsize - funcbufleft);
 		for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++) 
@@ -443,7 +453,11 @@
 	 * to be the owner of the tty, or super-user.
 	 */
 	perm = 0;
+#ifdef CONFIG_GRKERNSEC
+	if (current->tty == tty || capable(CAP_SYS_TTY_CONFIG))
+#else
 	if (current->tty == tty || suser())
+#endif
 		perm = 1;
  
 	kbd = kbd_table + console;
@@ -1038,12 +1052,20 @@
 		return do_unimap_ioctl(cmd, (struct unimapdesc *)arg, perm);
 
 	case VT_LOCKSWITCH:
+#ifdef CONFIG_GRKERNSEC
+		if (!capable(CAP_SYS_TTY_CONFIG))
+#else
 		if (!suser())
+#endif
 		   return -EPERM;
 		vt_dont_switch = 1;
 		return 0;
 	case VT_UNLOCKSWITCH:
+#ifdef CONFIG_GRKERNSEC
+		if (!capable(CAP_SYS_TTY_CONFIG))
+#else
 		if (!suser())
+#endif
 		   return -EPERM;
 		vt_dont_switch = 0;
 		return 0;
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/Config.in linux-2.4.27-leo/drivers/i2c/Config.in
--- linux-2.4.27/drivers/i2c/Config.in	2004-05-19 21:34:38.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/Config.in	2004-09-20 21:34:47.000000000 +0100
@@ -59,6 +59,30 @@
    fi
  
 # This is needed for automatic patch generation: sensors code starts here
+  bool 'I2C mainboard interfaces' CONFIG_I2C_MAINBOARD 
+  if [ "$CONFIG_I2C_MAINBOARD" = "y" ]; then
+    dep_tristate '  Acer Labs ALI 1535' CONFIG_I2C_ALI1535 $CONFIG_I2C
+    dep_tristate '  Acer Labs ALI 1533 and 1543C' CONFIG_I2C_ALI15X3 $CONFIG_I2C
+    dep_tristate '  Apple Hydra Mac I/O' CONFIG_I2C_HYDRA $CONFIG_I2C_ALGOBIT
+    dep_tristate '  AMD 756/766/768/8111' CONFIG_I2C_AMD756 $CONFIG_I2C
+    dep_tristate '  AMD 8111 SMBus 2.0' CONFIG_I2C_AMD8111 $CONFIG_I2C
+    if [ "$CONFIG_ALPHA" = "y" ]; then
+      dep_tristate '  DEC Tsunami I2C interface' CONFIG_I2C_TSUNAMI $CONFIG_I2C_ALGOBIT
+    fi
+    dep_tristate '  Intel 82801AA, AB, BA, DB' CONFIG_I2C_I801 $CONFIG_I2C
+    dep_tristate '  Intel i810AA/AB/E and i815' CONFIG_I2C_I810 $CONFIG_I2C_ALGOBIT
+    dep_tristate '  Intel 82371AB PIIX4(E), 443MX, ServerWorks OSB4/CSB5, SMSC Victory66' CONFIG_I2C_PIIX4 $CONFIG_I2C
+    dep_tristate '  Nvidia Nforce2' CONFIG_I2C_NFORCE2 $CONFIG_I2C
+    dep_tristate '  SiS 5595' CONFIG_I2C_SIS5595 $CONFIG_I2C
+    dep_tristate '  SiS 630/730' CONFIG_I2C_SIS630 $CONFIG_I2C
+    dep_tristate '  SiS 645/961,645DX/961,735' CONFIG_I2C_SIS645 $CONFIG_I2C $CONFIG_HOTPLUG
+    dep_tristate '  Savage 4' CONFIG_I2C_SAVAGE4 $CONFIG_I2C_ALGOBIT
+    dep_tristate '  VIA Technologies, Inc. VT82C586B' CONFIG_I2C_VIA $CONFIG_I2C_ALGOBIT
+    dep_tristate '  VIA Technologies, Inc. VT596A/B, 686A/B, 8231, 8233, 8233A, 8235' CONFIG_I2C_VIAPRO $CONFIG_I2C
+    dep_tristate '  Voodoo3 I2C interface' CONFIG_I2C_VOODOO3 $CONFIG_I2C_ALGOBIT
+    dep_tristate '  Pseudo ISA adapter (for some hardware sensors)' CONFIG_I2C_ISA $CONFIG_I2C
+  fi
+
 # This is needed for automatic patch generation: sensors code ends here
 
    dep_tristate 'I2C device interface' CONFIG_I2C_CHARDEV $CONFIG_I2C
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-adap-ibm_ocp.c linux-2.4.27-leo/drivers/i2c/i2c-adap-ibm_ocp.c
--- linux-2.4.27/drivers/i2c/i2c-adap-ibm_ocp.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-adap-ibm_ocp.c	2004-09-20 21:34:38.000000000 +0100
@@ -0,0 +1,346 @@
+/*
+   -------------------------------------------------------------------------
+   i2c-adap-ibm_ocp.c i2c-hw access for the IIC peripheral on the IBM PPC 405
+   -------------------------------------------------------------------------
+  
+   Ian DaSilva, MontaVista Software, Inc.
+   idasilva@mvista.com or source@mvista.com
+
+   Copyright 2000 MontaVista Software Inc.
+
+   Changes made to support the IIC peripheral on the IBM PPC 405 
+
+
+   ----------------------------------------------------------------------------
+   This file was highly leveraged from i2c-elektor.c, which was created
+   by Simon G. Vogl and Hans Berglund:
+
+ 
+     Copyright (C) 1995-97 Simon G. Vogl
+                   1998-99 Hans Berglund
+
+   With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
+   Frodo Looijaard <frodol@dds.nl>
+
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   ----------------------------------------------------------------------------
+
+   History: 01/20/12 - Armin
+   	akuster@mvista.com
+   	ported up to 2.4.16+	
+
+   Version 02/03/25 - Armin
+       converted to ocp format
+       removed commented out or #if 0 code
+
+   TODO: convert to ocp_register
+         add PM hooks
+
+*/
+
+
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-ibm_ocp.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/ocp.h>
+
+/*
+ * This next section is configurable, and it is used to set the number
+ * of i2c controllers in the system.  The default number of instances is 1,
+ * however, this should be changed to reflect your system's configuration.
+ */ 
+
+/*
+ * The STB03xxx, with a PPC405 core, has two i2c controllers.
+ */
+//(sizeof(IIC_ADDR)/sizeof(struct iic_regs))
+extern iic_t *IIC_ADDR[];
+static struct iic_ibm iic_ibmocp_adaps[IIC_NUMS][5];
+
+static struct i2c_algo_iic_data *iic_ibmocp_data[IIC_NUMS];
+static struct i2c_adapter *iic_ibmocp_ops[IIC_NUMS];
+
+static int i2c_debug=0;
+static wait_queue_head_t iic_wait[IIC_NUMS];
+static int iic_pending;
+static spinlock_t irq_driver_lock = SPIN_LOCK_UNLOCKED;
+
+
+/* ----- global defines -----------------------------------------------	*/
+#define DEB(x)	if (i2c_debug>=1) x
+#define DEB2(x) if (i2c_debug>=2) x
+#define DEB3(x) if (i2c_debug>=3) x
+#define DEBE(x)	x	/* error messages 				*/
+
+/* ----- local functions ----------------------------------------------	*/
+
+//
+// Description: Write a byte to IIC hardware
+//
+static void iic_ibmocp_setbyte(void *data, int ctl, int val)
+{
+   // writeb resolves to a write to the specified memory location
+   // plus a call to eieio.  eieio ensures that all instructions
+   // preceding it are completed before any further stores are
+   // completed.
+   // Delays at this level (to protect writes) are not needed here.
+   writeb(val, ctl);
+}
+
+
+//
+// Description: Read a byte from IIC hardware
+//
+static int iic_ibmocp_getbyte(void *data, int ctl)
+{
+   int val;
+
+   val = readb(ctl);
+   return (val);
+}
+
+
+//
+// Description: Return our slave address.  This is the address
+// put on the I2C bus when another master on the bus wants to address us
+// as a slave
+//
+static int iic_ibmocp_getown(void *data)
+{
+   return(((struct iic_ibm *)(data))->iic_own);
+}
+
+
+//
+// Description: Return the clock rate
+//
+static int iic_ibmocp_getclock(void *data)
+{
+   return(((struct iic_ibm *)(data))->iic_clock);
+}
+
+
+
+//
+// Description:  Put this process to sleep.  We will wake up when the
+// IIC controller interrupts.
+//
+static void iic_ibmocp_waitforpin(void *data) {
+
+   int timeout = 2;
+   struct iic_ibm *priv_data = data;
+
+   //
+   // If interrupts are enabled (which they are), then put the process to
+   // sleep.  This process will be awakened by two events -- either the
+   // the IIC peripheral interrupts or the timeout expires. 
+   //
+   if (priv_data->iic_irq > 0) {
+      spin_lock_irq(&irq_driver_lock);
+      if (iic_pending == 0) {
+  	 interruptible_sleep_on_timeout(&(iic_wait[priv_data->index]), timeout*HZ );
+      } else
+ 	 iic_pending = 0;
+      spin_unlock_irq(&irq_driver_lock);
+   } else {
+      //
+      // If interrupts are not enabled then delay for a reasonable amount
+      // of time and return.  We expect that by time we return to the calling
+      // function that the IIC has finished our requested transaction and
+      // the status bit reflects this.
+      //
+      // udelay is probably not the best choice for this since it is
+      // the equivalent of a busy wait
+      //
+      udelay(100);
+   }
+   //printk("iic_ibmocp_waitforpin: exitting\n");
+}
+
+
+//
+// Description: The registered interrupt handler
+//
+static void iic_ibmocp_handler(int this_irq, void *dev_id, struct pt_regs *regs) 
+{
+   int ret;
+   struct iic_regs *iic;
+   struct iic_ibm *priv_data = dev_id;
+   iic = (struct iic_regs *) priv_data->iic_base;
+   iic_pending = 1;
+   DEB2(printk("iic_ibmocp_handler: in interrupt handler\n"));
+   // Read status register
+   ret = readb((int) &(iic->sts));
+   DEB2(printk("iic_ibmocp_handler: status = %x\n", ret));
+   // Clear status register.  See IBM PPC 405 reference manual for details
+   writeb(0x0a, (int) &(iic->sts));
+   wake_up_interruptible(&(iic_wait[priv_data->index]));
+}
+
+
+//
+// Description: This function is very hardware dependent.  First, we lock
+// the region of memory where out registers exist.  Next, we request our
+// interrupt line and register its associated handler.  Our IIC peripheral
+// uses interrupt number 2, as specified by the 405 reference manual.
+//
+static int iic_hw_resrc_init(int instance)
+{
+
+   DEB(printk("iic_hw_resrc_init: Physical Base address: 0x%x\n", (u32) IIC_ADDR[instance] ));
+   iic_ibmocp_adaps[instance]->iic_base = (u32)ioremap((unsigned long)IIC_ADDR[instance],PAGE_SIZE);
+
+   DEB(printk("iic_hw_resrc_init: ioremapped base address: 0x%x\n", iic_ibmocp_adaps[instance]->iic_base));
+
+   if (iic_ibmocp_adaps[instance]->iic_irq > 0) {
+	
+      if (request_irq(iic_ibmocp_adaps[instance]->iic_irq, iic_ibmocp_handler,
+       0, "IBM OCP IIC", iic_ibmocp_adaps[instance]) < 0) {
+         printk(KERN_ERR "iic_hw_resrc_init: Request irq%d failed\n",
+          iic_ibmocp_adaps[instance]->iic_irq);
+	 iic_ibmocp_adaps[instance]->iic_irq = 0;
+      } else {
+         DEB3(printk("iic_hw_resrc_init: Enabled interrupt\n"));
+      }
+   }
+   return 0;
+}
+
+
+//
+// Description: Release irq and memory
+//
+static void iic_ibmocp_release(void)
+{
+   int i;
+
+   for(i=0; i<IIC_NUMS; i++) {
+      struct iic_ibm *priv_data = (struct iic_ibm *)iic_ibmocp_data[i]->data;
+      if (priv_data->iic_irq > 0) {
+         disable_irq(priv_data->iic_irq);
+         free_irq(priv_data->iic_irq, 0);
+      }
+      kfree(iic_ibmocp_data[i]);
+      kfree(iic_ibmocp_ops[i]);
+   }
+}
+
+
+//
+// Description: Called when the module is loaded.  This function starts the
+// cascade of calls up through the heirarchy of i2c modules (i.e. up to the
+//  algorithm layer and into to the core layer)
+//
+static int __init iic_ibmocp_init(void) 
+{
+   int i;
+
+   printk(KERN_INFO "iic_ibmocp_init: IBM on-chip iic adapter module\n");
+ 
+   for(i=0; i<IIC_NUMS; i++) {
+      iic_ibmocp_data[i] = kmalloc(sizeof(struct i2c_algo_iic_data),GFP_KERNEL);
+      if(iic_ibmocp_data[i] == NULL) {
+         return -ENOMEM;
+      }
+      memset(iic_ibmocp_data[i], 0, sizeof(struct i2c_algo_iic_data));
+      
+      switch (i) {
+	      case 0:
+	       iic_ibmocp_adaps[i]->iic_irq = IIC_IRQ(0);
+	      break;
+	      case 1:
+	       iic_ibmocp_adaps[i]->iic_irq = IIC_IRQ(1);
+	      break;
+      }
+      iic_ibmocp_adaps[i]->iic_clock = IIC_CLOCK;
+      iic_ibmocp_adaps[i]->iic_own = IIC_OWN; 
+      iic_ibmocp_adaps[i]->index = i;
+ 
+      DEB(printk("irq %x\n", iic_ibmocp_adaps[i]->iic_irq));
+      DEB(printk("clock %x\n", iic_ibmocp_adaps[i]->iic_clock));
+      DEB(printk("own %x\n", iic_ibmocp_adaps[i]->iic_own));
+      DEB(printk("index %x\n", iic_ibmocp_adaps[i]->index));
+
+
+      iic_ibmocp_data[i]->data = (struct iic_regs *)iic_ibmocp_adaps[i]; 
+      iic_ibmocp_data[i]->setiic = iic_ibmocp_setbyte;
+      iic_ibmocp_data[i]->getiic = iic_ibmocp_getbyte;
+      iic_ibmocp_data[i]->getown = iic_ibmocp_getown;
+      iic_ibmocp_data[i]->getclock = iic_ibmocp_getclock;
+      iic_ibmocp_data[i]->waitforpin = iic_ibmocp_waitforpin;
+      iic_ibmocp_data[i]->udelay = 80;
+      iic_ibmocp_data[i]->mdelay = 80;
+      iic_ibmocp_data[i]->timeout = HZ;
+      
+            iic_ibmocp_ops[i] = kmalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
+      if(iic_ibmocp_ops[i] == NULL) {
+         return -ENOMEM;
+      }
+      memset(iic_ibmocp_ops[i], 0, sizeof(struct i2c_adapter));
+      strcpy(iic_ibmocp_ops[i]->name, "IBM OCP IIC adapter");
+      iic_ibmocp_ops[i]->owner = THIS_MODULE;
+      iic_ibmocp_ops[i]->id = I2C_HW_OCP;
+      iic_ibmocp_ops[i]->algo = NULL;
+      iic_ibmocp_ops[i]->algo_data = iic_ibmocp_data[i];
+       
+      
+      init_waitqueue_head(&(iic_wait[i]));
+      if (iic_hw_resrc_init(i) == 0) {
+         if (i2c_ocp_add_bus(iic_ibmocp_ops[i]) < 0)
+         return -ENODEV;
+      } else {
+         return -ENODEV;
+      }
+      DEB(printk(KERN_INFO "iic_ibmocp_init: found device at %#x.\n\n", iic_ibmocp_adaps[i]->iic_base));
+   }
+   return 0;
+}
+
+
+static void __exit iic_ibmocp_exit(void)
+{
+   int i;
+
+   for(i=0; i<IIC_NUMS; i++) {
+      i2c_ocp_del_bus(iic_ibmocp_ops[i]);
+   }
+   iic_ibmocp_release();
+}
+
+//
+// If modules is NOT defined when this file is compiled, then the MODULE_*
+// macros will resolve to nothing
+//
+MODULE_AUTHOR("MontaVista Software <www.mvista.com>");
+MODULE_DESCRIPTION("I2C-Bus adapter routines for PPC 405 IIC bus adapter");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(base, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(clock, "i");
+MODULE_PARM(own, "i");
+MODULE_PARM(i2c_debug,"i");
+
+
+module_init(iic_ibmocp_init);
+module_exit(iic_ibmocp_exit); 
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-algo-8xx.c linux-2.4.27-leo/drivers/i2c/i2c-algo-8xx.c
--- linux-2.4.27/drivers/i2c/i2c-algo-8xx.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-algo-8xx.c	2004-09-20 21:34:38.000000000 +0100
@@ -0,0 +1,616 @@
+/*
+ * i2c-algo-8xx.c i2x driver algorithms for MPC8XX CPM
+ * Copyright (c) 1999 Dan Malek (dmalek@jlc.net).
+ *
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * moved into proper i2c interface; separated out platform specific 
+ * parts into i2c-rpx.c
+ * Brad Parker (brad@heeltoe.com)
+ */
+
+// XXX todo
+// timeout sleep?
+
+/* $Id: i2c-algo-8xx.c,v 1.14 2003/07/25 07:56:42 khali Exp $ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-8xx.h>
+#include <asm/mpc8xx.h>
+#include <asm/commproc.h>
+
+
+#define CPM_MAX_READ	513
+/* #define I2C_CHIP_ERRATA */ /* Try uncomment this if you have an older CPU(earlier than rev D4) */
+static wait_queue_head_t iic_wait;
+static ushort r_tbase, r_rbase;
+
+int cpm_debug = 0;
+
+static  void
+cpm_iic_interrupt(void *dev_id, struct pt_regs *regs)
+{
+	volatile i2c8xx_t *i2c = (i2c8xx_t *)dev_id;
+	if (cpm_debug > 1)
+		printk("cpm_iic_interrupt(dev_id=%p)\n", dev_id);
+#if 0
+	/* Chip errata, clear enable. This is not needed on rev D4 CPUs */
+        /* This should probably be removed and replaced by I2C_CHIP_ERRATA stuff */
+        /* Someone with a buggy CPU needs to confirm that */
+	i2c->i2c_i2mod &= ~1;
+#endif
+	/* Clear interrupt.
+	*/
+	i2c->i2c_i2cer = 0xff;
+
+	/* Get 'me going again.
+	*/
+	wake_up_interruptible(&iic_wait);
+}
+
+static void
+cpm_iic_init(struct i2c_algo_8xx_data *cpm)
+{
+	volatile iic_t		*iip = cpm->iip;
+	volatile i2c8xx_t	*i2c = cpm->i2c;
+	unsigned char brg;
+	bd_t *bd = (bd_t *)__res;
+
+	if (cpm_debug) printk(KERN_DEBUG "cpm_iic_init()\n");
+
+	/* Initialize the parameter ram.
+	 * We need to make sure many things are initialized to zero,
+	 * especially in the case of a microcode patch.
+	 */
+	iip->iic_rstate = 0;
+	iip->iic_rdp = 0;
+	iip->iic_rbptr = 0;
+	iip->iic_rbc = 0;
+	iip->iic_rxtmp = 0;
+	iip->iic_tstate = 0;
+	iip->iic_tdp = 0;
+	iip->iic_tbptr = 0;
+	iip->iic_tbc = 0;
+	iip->iic_txtmp = 0;
+
+	/* Set up the IIC parameters in the parameter ram.
+	*/
+	iip->iic_tbase = r_tbase = cpm->dp_addr;
+	iip->iic_rbase = r_rbase = cpm->dp_addr + sizeof(cbd_t)*2;
+
+	iip->iic_tfcr = SMC_EB;
+	iip->iic_rfcr = SMC_EB;
+
+	/* Set maximum receive size.
+	*/
+	iip->iic_mrblr = CPM_MAX_READ;
+
+	/* Initialize Tx/Rx parameters.
+	*/
+	if (cpm->reloc == 0) {
+		volatile cpm8xx_t *cp = cpm->cp;
+
+		cp->cp_cpcr =
+			mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_INIT_TRX) | CPM_CR_FLG;
+		while (cp->cp_cpcr & CPM_CR_FLG);
+	} else {
+		iip->iic_rbptr = iip->iic_rbase;
+		iip->iic_tbptr = iip->iic_tbase;
+		iip->iic_rstate	= 0;
+		iip->iic_tstate	= 0;
+	}
+
+	/* Select an arbitrary address.  Just make sure it is unique.
+	*/
+	i2c->i2c_i2add = 0xfe;
+
+	/* Make clock run at 60 KHz.
+	*/
+	brg = (unsigned char) (bd->bi_intfreq/(32*2*60000) -3);
+	i2c->i2c_i2brg = brg;
+
+	i2c->i2c_i2mod = 0x00; 
+	i2c->i2c_i2com = 0x01; /* Master mode */
+
+	/* Disable interrupts.
+	*/
+	i2c->i2c_i2cmr = 0;
+	i2c->i2c_i2cer = 0xff;
+
+	init_waitqueue_head(&iic_wait);
+
+	/* Install interrupt handler.
+	*/
+	if (cpm_debug) {
+		printk ("%s[%d] Install ISR for IRQ %d\n",
+			__func__,__LINE__, CPMVEC_I2C);
+	}
+	(*cpm->setisr)(CPMVEC_I2C, cpm_iic_interrupt, (void *)i2c);
+}
+
+
+static int
+cpm_iic_shutdown(struct i2c_algo_8xx_data *cpm)
+{
+	volatile i2c8xx_t *i2c = cpm->i2c;
+
+	/* Shut down IIC.
+	*/
+	i2c->i2c_i2mod &= ~1;
+	i2c->i2c_i2cmr = 0;
+	i2c->i2c_i2cer = 0xff;
+
+	return(0);
+}
+
+static void 
+cpm_reset_iic_params(volatile iic_t *iip)
+{
+	iip->iic_tbase = r_tbase;
+	iip->iic_rbase = r_rbase;
+
+	iip->iic_tfcr = SMC_EB;
+	iip->iic_rfcr = SMC_EB;
+
+	iip->iic_mrblr = CPM_MAX_READ;
+
+	iip->iic_rstate = 0;
+	iip->iic_rdp = 0;
+	iip->iic_rbptr = iip->iic_rbase;
+	iip->iic_rbc = 0;
+	iip->iic_rxtmp = 0;
+	iip->iic_tstate = 0;
+	iip->iic_tdp = 0;
+	iip->iic_tbptr = iip->iic_tbase;
+	iip->iic_tbc = 0;
+	iip->iic_txtmp = 0;
+}
+
+#define BD_SC_NAK		((ushort)0x0004) /* NAK - did not respond */
+#define BD_SC_OV		((ushort)0x0002) /* OV - receive overrun */
+#define CPM_CR_CLOSE_RXBD	((ushort)0x0007)
+
+static void force_close(struct i2c_algo_8xx_data *cpm)
+{
+	volatile i2c8xx_t *i2c = cpm->i2c;
+	if (cpm->reloc == 0) { /* micro code disabled */
+		volatile cpm8xx_t *cp = cpm->cp;
+
+		if (cpm_debug) printk("force_close()\n");
+		cp->cp_cpcr =
+			mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_CLOSE_RXBD) |
+			CPM_CR_FLG;
+
+		while (cp->cp_cpcr & CPM_CR_FLG);
+	}
+	i2c->i2c_i2cmr = 0x00;	/* Disable all interrupts */
+	i2c->i2c_i2cer = 0xff; 
+}
+
+
+/* Read from IIC...
+ * abyte = address byte, with r/w flag already set
+ */
+static int
+cpm_iic_read(struct i2c_algo_8xx_data *cpm, u_char abyte, char *buf, int count)
+{
+	volatile iic_t *iip = cpm->iip;
+	volatile i2c8xx_t *i2c = cpm->i2c;
+	volatile cpm8xx_t *cp = cpm->cp;
+	volatile cbd_t	*tbdf, *rbdf;
+	u_char *tb;
+	unsigned long flags, tmo;
+
+	if (count >= CPM_MAX_READ)
+		return -EINVAL;
+
+	/* check for and use a microcode relocation patch */
+	if (cpm->reloc) {
+		cpm_reset_iic_params(iip);
+	}
+
+	tbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_tbase];
+	rbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_rbase];
+
+	/* To read, we need an empty buffer of the proper length.
+	 * All that is used is the first byte for address, the remainder
+	 * is just used for timing (and doesn't really have to exist).
+	 */
+	tb = cpm->temp;
+	tb = (u_char *)(((uint)tb + 15) & ~15);
+	tb[0] = abyte;		/* Device address byte w/rw flag */
+
+	flush_dcache_range((unsigned long) tb, (unsigned long) (tb+1));
+
+	if (cpm_debug) printk("cpm_iic_read(abyte=0x%x)\n", abyte);
+
+	tbdf->cbd_bufaddr = __pa(tb);
+	tbdf->cbd_datlen = count + 1;
+	tbdf->cbd_sc =
+		BD_SC_READY | BD_SC_LAST |
+		BD_SC_WRAP | BD_IIC_START;
+
+	iip->iic_mrblr = count +1; /* prevent excessive read, +1
+				      is needed otherwise will the
+				      RXB interrupt come too early */
+
+	/* flush will invalidate too. */
+	flush_dcache_range((unsigned long) buf, (unsigned long) (buf+count));
+
+	rbdf->cbd_datlen = 0;
+	rbdf->cbd_bufaddr = __pa(buf);
+	rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP| BD_SC_INTRPT;
+	if(count > 16){
+		/* Chip bug, set enable here */
+		local_irq_save(flags);
+		i2c->i2c_i2cmr = 0x13;	/* Enable some interupts */
+		i2c->i2c_i2cer = 0xff;
+		i2c->i2c_i2mod |= 1;	/* Enable */
+		i2c->i2c_i2com |= 0x80;	/* Begin transmission */
+
+		/* Wait for IIC transfer */
+		tmo = interruptible_sleep_on_timeout(&iic_wait,1*HZ);
+		local_irq_restore(flags);
+	} else { /* busy wait for small transfers, its faster */
+		i2c->i2c_i2cmr = 0x00;	/* Disable I2C interupts */
+		i2c->i2c_i2cer = 0xff;
+		i2c->i2c_i2mod |= 1;	/* Enable */
+		i2c->i2c_i2com |= 0x80;	/* Begin transmission */
+		tmo = jiffies + 1*HZ; 
+		while(!(i2c->i2c_i2cer & 0x11 || time_after(jiffies, tmo))); /* Busy wait, with a timeout */
+	}		
+
+	if (signal_pending(current) || !tmo){
+		force_close(cpm);
+		if(cpm_debug) 
+			printk("IIC read: timeout!\n");
+		return -EIO;
+	}
+#ifdef I2C_CHIP_ERRATA
+	/* Chip errata, clear enable. This is not needed on rev D4 CPUs.
+	 Disabling I2C too early may cause too short stop condition */
+	udelay(4);
+	i2c->i2c_i2mod &= ~1;
+#endif
+	if (cpm_debug) {
+		printk("tx sc %04x, rx sc %04x\n",
+		       tbdf->cbd_sc, rbdf->cbd_sc);
+	}
+
+	if (tbdf->cbd_sc & BD_SC_READY) {
+		printk("IIC read; complete but tbuf ready\n");
+		force_close(cpm);
+		printk("tx sc %04x, rx sc %04x\n",
+		       tbdf->cbd_sc, rbdf->cbd_sc);
+	}
+
+	if (tbdf->cbd_sc & BD_SC_NAK) {
+		if (cpm_debug)
+			printk("IIC read; no ack\n");
+		return -EREMOTEIO;
+	}
+
+	if (rbdf->cbd_sc & BD_SC_EMPTY) {
+		/* force_close(cpm); */
+		if (cpm_debug){
+			printk("IIC read; complete but rbuf empty\n");
+			printk("tx sc %04x, rx sc %04x\n",
+			       tbdf->cbd_sc, rbdf->cbd_sc);
+		}
+		return -EREMOTEIO;
+	}
+
+	if (rbdf->cbd_sc & BD_SC_OV) {
+		if (cpm_debug)
+			printk("IIC read; Overrun\n");
+		return -EREMOTEIO;;
+	}
+
+	if (cpm_debug) printk("read %d bytes\n", rbdf->cbd_datlen);
+
+	if (rbdf->cbd_datlen < count) {
+		if (cpm_debug)
+			printk("IIC read; short, wanted %d got %d\n",
+			       count, rbdf->cbd_datlen);
+		return 0;
+	}
+
+	return count;
+}
+
+/* Write to IIC...
+ * addr = address byte, with r/w flag already set
+ */
+static int
+cpm_iic_write(struct i2c_algo_8xx_data *cpm, u_char abyte, char *buf,int count)
+{
+	volatile iic_t *iip = cpm->iip;
+	volatile i2c8xx_t *i2c = cpm->i2c;
+	volatile cpm8xx_t *cp = cpm->cp;
+	volatile cbd_t	*tbdf;
+	u_char *tb;
+	unsigned long flags, tmo;
+
+	/* check for and use a microcode relocation patch */
+	if (cpm->reloc) {
+		cpm_reset_iic_params(iip);
+	}
+	tb = cpm->temp;
+	tb = (u_char *)(((uint)tb + 15) & ~15);
+	*tb = abyte;		/* Device address byte w/rw flag */
+
+	flush_dcache_range((unsigned long) tb, (unsigned long) (tb+1));
+	flush_dcache_range((unsigned long) buf, (unsigned long) (buf+count));
+
+	if (cpm_debug) printk("cpm_iic_write(abyte=0x%x)\n", abyte);
+
+	/* set up 2 descriptors */
+	tbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_tbase];
+
+	tbdf[0].cbd_bufaddr = __pa(tb);
+	tbdf[0].cbd_datlen = 1;
+	tbdf[0].cbd_sc = BD_SC_READY | BD_IIC_START;
+
+	tbdf[1].cbd_bufaddr = __pa(buf);
+	tbdf[1].cbd_datlen = count;
+	tbdf[1].cbd_sc = BD_SC_READY | BD_SC_INTRPT | BD_SC_LAST | BD_SC_WRAP;
+
+	if(count > 16){
+		/* Chip bug, set enable here */
+		local_irq_save(flags);
+		i2c->i2c_i2cmr = 0x13;	/* Enable some interupts */
+		i2c->i2c_i2cer = 0xff;
+		i2c->i2c_i2mod |= 1;	/* Enable */
+		i2c->i2c_i2com |= 0x80;	/* Begin transmission */
+		
+		/* Wait for IIC transfer */
+		tmo = interruptible_sleep_on_timeout(&iic_wait,1*HZ);
+		local_irq_restore(flags);
+	} else {  /* busy wait for small transfers, its faster */
+		i2c->i2c_i2cmr = 0x00;	/* Disable I2C interupts */
+		i2c->i2c_i2cer = 0xff;
+		i2c->i2c_i2mod |= 1;	/* Enable */
+		i2c->i2c_i2com |= 0x80;	/* Begin transmission */
+		tmo = jiffies + 1*HZ; 
+		while(!(i2c->i2c_i2cer & 0x12 || time_after(jiffies, tmo))); /* Busy wait, with a timeout */
+	}		
+
+	if (signal_pending(current) || !tmo){
+		force_close(cpm);
+		if(cpm_debug && !tmo) 
+			printk("IIC write: timeout!\n");
+		return -EIO;
+	}
+	
+#if I2C_CHIP_ERRATA
+	/* Chip errata, clear enable. This is not needed on rev D4 CPUs.
+	 Disabling I2C too early may cause too short stop condition */
+	udelay(4);
+	i2c->i2c_i2mod &= ~1;
+#endif
+	if (cpm_debug) {
+		printk("tx0 sc %04x, tx1 sc %04x\n",
+		       tbdf[0].cbd_sc, tbdf[1].cbd_sc);
+	}
+
+	if (tbdf->cbd_sc & BD_SC_NAK) {
+		if (cpm_debug) 
+			printk("IIC write; no ack\n");
+		return 0;
+	}
+	  
+	if (tbdf->cbd_sc & BD_SC_READY) {
+		if (cpm_debug)
+			printk("IIC write; complete but tbuf ready\n");
+		return 0;
+	}
+
+	return count;
+}
+
+/* See if an IIC address exists..
+ * addr = 7 bit address, unshifted
+ */
+static int
+cpm_iic_tryaddress(struct i2c_algo_8xx_data *cpm, int addr)
+{
+	volatile iic_t *iip = cpm->iip;
+	volatile i2c8xx_t *i2c = cpm->i2c;
+	volatile cpm8xx_t *cp = cpm->cp;
+	volatile cbd_t *tbdf, *rbdf;
+	u_char *tb;
+	unsigned long flags, len, tmo;
+
+	if (cpm_debug > 1)
+		printk("cpm_iic_tryaddress(cpm=%p,addr=%d)\n", cpm, addr);
+
+	/* check for and use a microcode relocation patch */
+	if (cpm->reloc) {
+		cpm_reset_iic_params(iip);
+	}
+
+	if (cpm_debug && addr == 0) {
+		printk("iip %p, dp_addr 0x%x\n", cpm->iip, cpm->dp_addr);
+		printk("iic_tbase %d, r_tbase %d\n", iip->iic_tbase, r_tbase);
+	}
+
+	tbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_tbase];
+	rbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_rbase];
+
+	tb = cpm->temp;
+	tb = (u_char *)(((uint)tb + 15) & ~15);
+
+	/* do a simple read */
+	tb[0] = (addr << 1) | 1;	/* device address (+ read) */
+	len = 2;
+
+	flush_dcache_range((unsigned long) tb, (unsigned long) (tb+2));
+
+	tbdf->cbd_bufaddr = __pa(tb);
+	tbdf->cbd_datlen = len;
+	tbdf->cbd_sc =
+		BD_SC_READY | BD_SC_LAST |
+		BD_SC_WRAP | BD_IIC_START;
+
+	rbdf->cbd_datlen = 0;
+	rbdf->cbd_bufaddr = __pa(tb+2);
+	rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP | BD_SC_INTRPT;
+
+	local_irq_save(flags);
+	i2c->i2c_i2cmr = 0x13;	/* Enable some interupts */
+	i2c->i2c_i2cer = 0xff;
+	i2c->i2c_i2mod |= 1;	/* Enable */
+	i2c->i2c_i2com |= 0x80;	/* Begin transmission */
+
+	if (cpm_debug > 1) printk("about to sleep\n");
+
+	/* wait for IIC transfer */
+	tmo = interruptible_sleep_on_timeout(&iic_wait,1*HZ);
+	local_irq_restore(flags);
+
+#ifdef I2C_CHIP_ERRATA
+	/* Chip errata, clear enable. This is not needed on rev D4 CPUs.
+	 Disabling I2C too early may cause too short stop condition */
+	udelay(4);
+	i2c->i2c_i2mod &= ~1;
+#endif
+
+	if (signal_pending(current) || !tmo){
+		force_close(cpm);
+		if(cpm_debug && !tmo) 
+			printk("IIC tryaddress: timeout!\n");
+		return -EIO;
+	}
+
+	if (cpm_debug > 1) printk("back from sleep\n");
+
+	if (tbdf->cbd_sc & BD_SC_NAK) {
+		if (cpm_debug > 1) printk("IIC try; no ack\n");
+		return 0;
+	}
+	  
+	if (tbdf->cbd_sc & BD_SC_READY) {
+		printk("IIC try; complete but tbuf ready\n");
+	}
+	
+	return 1;
+}
+
+static int cpm_xfer(struct i2c_adapter *adap,
+		    struct i2c_msg msgs[], 
+		    int num)
+{
+	struct i2c_algo_8xx_data *cpm = adap->algo_data;
+	struct i2c_msg *pmsg;
+	int i, ret;
+	u_char addr;
+    
+	for (i = 0; i < num; i++) {
+		pmsg = &msgs[i];
+
+		if (cpm_debug)
+			printk("i2c-algo-8xx.o: "
+			       "#%d addr=0x%x flags=0x%x len=%d\n buf=%lx\n",
+			       i, pmsg->addr, pmsg->flags, pmsg->len, (unsigned long)pmsg->buf);
+
+		addr = pmsg->addr << 1;
+		if (pmsg->flags & I2C_M_RD )
+			addr |= 1;
+		if (pmsg->flags & I2C_M_REV_DIR_ADDR )
+			addr ^= 1;
+    
+		if (!(pmsg->flags & I2C_M_NOSTART)) {
+		}
+		if (pmsg->flags & I2C_M_RD ) {
+			/* read bytes into buffer*/
+			ret = cpm_iic_read(cpm, addr, pmsg->buf, pmsg->len);
+			if (cpm_debug)
+				printk("i2c-algo-8xx.o: read %d bytes\n", ret);
+			if (ret < pmsg->len ) {
+				return (ret<0)? ret : -EREMOTEIO;
+			}
+		} else {
+			/* write bytes from buffer */
+			ret = cpm_iic_write(cpm, addr, pmsg->buf, pmsg->len);
+			if (cpm_debug)
+				printk("i2c-algo-8xx.o: wrote %d\n", ret);
+			if (ret < pmsg->len ) {
+				return (ret<0) ? ret : -EREMOTEIO;
+			}
+		}
+	}
+	return (num);
+}
+
+static u32 cpm_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | 
+	       I2C_FUNC_PROTOCOL_MANGLING; 
+}
+
+/* -----exported algorithm data: -------------------------------------	*/
+
+static struct i2c_algorithm cpm_algo = {
+	.owner		= THIS_MODULE,
+	.name		= "MPC8xx CPM algorithm",
+	.id		= I2C_ALGO_MPC8XX,
+	.master_xfer	= cpm_xfer,
+	.functionality	= cpm_func,
+};
+
+/* 
+ * registering functions to load algorithms at runtime 
+ */
+int i2c_8xx_add_bus(struct i2c_adapter *adap)
+{
+	int i;
+	struct i2c_algo_8xx_data *cpm = adap->algo_data;
+
+	if (cpm_debug)
+		printk("i2c-algo-8xx.o: hw routines for %s registered.\n",
+		       adap->name);
+
+	/* register new adapter to i2c module... */
+
+	adap->id |= cpm_algo.id;
+	adap->algo = &cpm_algo;
+
+	i2c_add_adapter(adap);
+	cpm_iic_init(cpm);
+}
+
+
+int i2c_8xx_del_bus(struct i2c_adapter *adap)
+{
+	struct i2c_algo_8xx_data *cpm = adap->algo_data;
+
+	cpm_iic_shutdown(cpm);
+
+	return i2c_del_adapter(adap);
+}
+
+EXPORT_SYMBOL(i2c_8xx_add_bus);
+EXPORT_SYMBOL(i2c_8xx_del_bus);
+
+MODULE_AUTHOR("Brad Parker <brad@heeltoe.com>");
+MODULE_DESCRIPTION("I2C-Bus MPC8XX algorithm");
+MODULE_LICENSE("GPL");
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-algo-bit.c linux-2.4.27-leo/drivers/i2c/i2c-algo-bit.c
--- linux-2.4.27/drivers/i2c/i2c-algo-bit.c	2004-02-20 14:11:41.000000000 +0000
+++ linux-2.4.27-leo/drivers/i2c/i2c-algo-bit.c	2004-09-20 21:34:38.000000000 +0100
@@ -18,24 +18,22 @@
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.		     */
 /* ------------------------------------------------------------------------- */
 
-/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
-   Frodo Looijaard <frodol@dds.nl> */
+/* With some changes from Frodo Looijaard <frodol@dds.nl>, Kyösti Mälkki
+   <kmalkki@cc.hut.fi> and Jean Delvare <khali@linux-fr.org> */
 
-/* $Id: i2c-algo-bit.c,v 1.30 2001/07/29 02:44:25 mds Exp $ */
+/* $Id: i2c-algo-bit.c,v 1.50 2003/12/22 20:03:39 khali Exp $ */
 
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/init.h>
-#include <asm/uaccess.h>
-#include <linux/ioport.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
-
 #include <linux/i2c.h>
 #include <linux/i2c-algo-bit.h>
 
+
 /* ----- global defines ----------------------------------------------- */
 #define DEB(x) if (i2c_debug>=1) x;
 #define DEB2(x) if (i2c_debug>=2) x;
@@ -43,27 +41,13 @@
 #define DEBPROTO(x) if (i2c_debug>=9) { x; }
  	/* debug the protocol by showing transferred bits */
 
-/* debugging - slow down transfer to have a look at the data .. 	*/
-/* I use this with two leds&resistors, each one connected to sda,scl 	*/
-/* respectively. This makes sure that the algorithm works. Some chips   */
-/* might not like this, as they have an internal timeout of some mils	*/
-/*
-#define SLO_IO      jif=jiffies;while(time_before_eq(jiffies, jif+i2c_table[minor].veryslow))\
-                        if (need_resched) schedule();
-*/
-
 
 /* ----- global variables ---------------------------------------------	*/
 
-#ifdef SLO_IO
-	int jif;
-#endif
-
 /* module parameters:
  */
 static int i2c_debug;
 static int bit_test;	/* see if the line-setting functions work	*/
-static int bit_scan;	/* have a look at what's hanging 'round		*/
 
 /* --- setting states on the bus with the right timing: ---------------	*/
 
@@ -88,9 +72,6 @@
 {
 	setscl(adap,0);
 	udelay(adap->udelay);
-#ifdef SLO_IO
-	SLO_IO
-#endif
 }
 
 /*
@@ -99,33 +80,35 @@
  */
 static inline int sclhi(struct i2c_algo_bit_data *adap)
 {
-	int start=jiffies;
+	int start;
 
 	setscl(adap,1);
 
-	udelay(adap->udelay);
-
 	/* Not all adapters have scl sense line... */
-	if (adap->getscl == NULL )
+	if (adap->getscl == NULL ) {
+		udelay(adap->udelay);
 		return 0;
+	}
 
- 	while (! getscl(adap) ) {	
+	start=jiffies;
+	while (! getscl(adap) ) {	
  		/* the hw knows how to read the clock line,
  		 * so we wait until it actually gets high.
  		 * This is safer as some chips may hold it low
  		 * while they are processing data internally. 
  		 */
-		setscl(adap,1);
 		if (time_after_eq(jiffies, start+adap->timeout)) {
 			return -ETIMEDOUT;
 		}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
 		if (current->need_resched)
 			schedule();
+#else
+		cond_resched();
+#endif
 	}
 	DEBSTAT(printk(KERN_DEBUG "needed %ld jiffies\n", jiffies-start));
-#ifdef SLO_IO
-	SLO_IO
-#endif
+	udelay(adap->udelay);
 	return 0;
 } 
 
@@ -144,7 +127,7 @@
 	/* scl, sda may not be high */
 	DEBPROTO(printk(" Sr "));
 	setsda(adap,1);
-	setscl(adap,1);
+	sclhi(adap);
 	udelay(adap->udelay);
 	
 	sdalo(adap);
@@ -178,7 +161,6 @@
 	struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
 
 	/* assert: scl is low */
-	DEB2(printk(KERN_DEBUG " i2c_outb:%2.2X\n",c&0xff));
 	for ( i=7 ; i>=0 ; i-- ) {
 		sb = c & ( 1 << i );
 		setsda(adap,sb);
@@ -186,6 +168,7 @@
 		DEBPROTO(printk(KERN_DEBUG "%d",sb!=0));
 		if (sclhi(adap)<0) { /* timed out */
 			sdahi(adap); /* we don't want to block the net */
+			DEB2(printk(KERN_DEBUG " i2c_outb: 0x%02x, timeout at bit #%d\n", c&0xff, i));
 			return -ETIMEDOUT;
 		};
 		/* do arbitration here: 
@@ -196,11 +179,12 @@
 	}
 	sdahi(adap);
 	if (sclhi(adap)<0){ /* timeout */
+		DEB2(printk(KERN_DEBUG " i2c_outb: 0x%02x, timeout at ack\n", c&0xff));
 		return -ETIMEDOUT;
 	};
 	/* read ack: SDA should be pulled down by slave */
 	ack=getsda(adap);	/* ack: sda is pulled low ->success.	 */
-	DEB2(printk(KERN_DEBUG " i2c_outb: getsda() =  0x%2.2x\n", ~ack ));
+	DEB2(printk(KERN_DEBUG " i2c_outb: 0x%02x , getsda() = %d\n", c & 0xff, ack));
 
 	DEBPROTO( printk(KERN_DEBUG "[%2.2x]",c&0xff) );
 	DEBPROTO(if (0==ack){ printk(KERN_DEBUG " A ");} else printk(KERN_DEBUG " NA ") );
@@ -219,11 +203,10 @@
 	struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
 
 	/* assert: scl is low */
-	DEB2(printk(KERN_DEBUG "i2c_inb.\n"));
-
 	sdahi(adap);
 	for (i=0;i<8;i++) {
 		if (sclhi(adap)<0) { /* timeout */
+			DEB2(printk(KERN_DEBUG " i2c_inb: timeout at bit #%d\n", 7-i));
 			return -ETIMEDOUT;
 		};
 		indata *= 2;
@@ -232,6 +215,8 @@
 		scllo(adap);
 	}
 	/* assert: scl is low */
+	DEB2(printk(KERN_DEBUG "i2c_inb: 0x%02x\n", indata & 0xff));
+
 	DEBPROTO(printk(KERN_DEBUG " 0x%02x", indata & 0xff));
 	return (int) (indata & 0xff);
 }
@@ -242,71 +227,75 @@
  */
 static int test_bus(struct i2c_algo_bit_data *adap, char* name) {
 	int scl,sda;
+
+	if (adap->getscl==NULL)
+		printk(KERN_INFO "i2c-algo-bit.o: Testing SDA only, "
+			"SCL is not readable.\n");
+
 	sda=getsda(adap);
-	if (adap->getscl==NULL) {
-		printk("i2c-algo-bit.o: Warning: Adapter can't read from clock line - skipping test.\n");
-		return 0;		
-	}
-	scl=getscl(adap);
-	printk("i2c-algo-bit.o: Adapter: %s scl: %d  sda: %d -- testing...\n",
-	       name,getscl(adap),getsda(adap));
+	scl=(adap->getscl==NULL?1:getscl(adap));
+	printk(KERN_DEBUG "i2c-algo-bit.o: (0) scl=%d, sda=%d\n",scl,sda);
 	if (!scl || !sda ) {
-		printk("i2c-algo-bit.o: %s seems to be busy.\n",name);
+		printk(KERN_WARNING "i2c-algo-bit.o: %s seems to be busy.\n", name);
 		goto bailout;
 	}
+
 	sdalo(adap);
-	printk("i2c-algo-bit.o:1 scl: %d  sda: %d \n",getscl(adap),
-	       getsda(adap));
-	if ( 0 != getsda(adap) ) {
-		printk("i2c-algo-bit.o: %s SDA stuck high!\n",name);
-		sdahi(adap);
+	sda=getsda(adap);
+	scl=(adap->getscl==NULL?1:getscl(adap));
+	printk(KERN_DEBUG "i2c-algo-bit.o: (1) scl=%d, sda=%d\n",scl,sda);
+	if ( 0 != sda ) {
+		printk(KERN_WARNING "i2c-algo-bit.o: SDA stuck high!\n");
 		goto bailout;
 	}
-	if ( 0 == getscl(adap) ) {
-		printk("i2c-algo-bit.o: %s SCL unexpected low while pulling SDA low!\n",
-			name);
+	if ( 0 == scl ) {
+		printk(KERN_WARNING "i2c-algo-bit.o: SCL unexpected low "
+			"while pulling SDA low!\n");
 		goto bailout;
 	}		
+
 	sdahi(adap);
-	printk("i2c-algo-bit.o:2 scl: %d  sda: %d \n",getscl(adap),
-	       getsda(adap));
-	if ( 0 == getsda(adap) ) {
-		printk("i2c-algo-bit.o: %s SDA stuck low!\n",name);
-		sdahi(adap);
+	sda=getsda(adap);
+	scl=(adap->getscl==NULL?1:getscl(adap));
+	printk(KERN_DEBUG "i2c-algo-bit.o: (2) scl=%d, sda=%d\n",scl,sda);
+	if ( 0 == sda ) {
+		printk(KERN_WARNING "i2c-algo-bit.o: SDA stuck low!\n");
 		goto bailout;
 	}
-	if ( 0 == getscl(adap) ) {
-		printk("i2c-algo-bit.o: %s SCL unexpected low while SDA high!\n",
-		       name);
-	goto bailout;
+	if ( 0 == scl ) {
+		printk(KERN_WARNING "i2c-algo-bit.o: SCL unexpected low "
+			"while pulling SDA high!\n");
+		goto bailout;
 	}
+
 	scllo(adap);
-	printk("i2c-algo-bit.o:3 scl: %d  sda: %d \n",getscl(adap),
-	       getsda(adap));
-	if ( 0 != getscl(adap) ) {
-		printk("i2c-algo-bit.o: %s SCL stuck high!\n",name);
-		sclhi(adap);
+	sda=getsda(adap);
+	scl=(adap->getscl==NULL?0:getscl(adap));
+	printk(KERN_DEBUG "i2c-algo-bit.o: (3) scl=%d, sda=%d\n",scl,sda);
+	if ( 0 != scl ) {
+		printk(KERN_WARNING "i2c-algo-bit.o: SCL stuck high!\n");
 		goto bailout;
 	}
-	if ( 0 == getsda(adap) ) {
-		printk("i2c-algo-bit.o: %s SDA unexpected low while pulling SCL low!\n",
-			name);
+	if ( 0 == sda ) {
+		printk(KERN_WARNING "i2c-algo-bit.o: SDA unexpected low "
+			"while pulling SCL low!\n");
 		goto bailout;
 	}
+	
 	sclhi(adap);
-	printk("i2c-algo-bit.o:4 scl: %d  sda: %d \n",getscl(adap),
-	       getsda(adap));
-	if ( 0 == getscl(adap) ) {
-		printk("i2c-algo-bit.o: %s SCL stuck low!\n",name);
-		sclhi(adap);
+	sda=getsda(adap);
+	scl=(adap->getscl==NULL?1:getscl(adap));
+	printk(KERN_DEBUG "i2c-algo-bit.o: (4) scl=%d, sda=%d\n",scl,sda);
+	if ( 0 == scl ) {
+		printk(KERN_WARNING "i2c-algo-bit.o: SCL stuck low!\n");
 		goto bailout;
 	}
-	if ( 0 == getsda(adap) ) {
-		printk("i2c-algo-bit.o: %s SDA unexpected low while SCL high!\n",
-			name);
+	if ( 0 == sda ) {
+		printk(KERN_WARNING "i2c-algo-bit.o: SDA unexpected low "
+			"while pulling SCL high!\n");
 		goto bailout;
 	}
-	printk("i2c-algo-bit.o: %s passed test.\n",name);
+	printk(KERN_INFO "i2c-algo-bit.o: %s passed test.\n",name);
 	return 0;
 bailout:
 	sdahi(adap);
@@ -340,16 +329,21 @@
 		i2c_start(adap);
 		udelay(adap->udelay);
 	}
-	DEB2(if (i) printk(KERN_DEBUG "i2c-algo-bit.o: needed %d retries for %d\n",
-	                   i,addr));
+	DEB2(if (i)
+	     printk(KERN_DEBUG "i2c-algo-bit.o: Used %d tries to %s client at 0x%02x : %s\n",
+		    i+1, addr & 1 ? "read" : "write", addr>>1,
+		    ret==1 ? "success" : ret==0 ? "no ack" : "failed, timeout?" )
+	    );
 	return ret;
 }
 
-static int sendbytes(struct i2c_adapter *i2c_adap,const char *buf, int count)
+static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
 {
 	struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
 	char c;
-	const char *temp = buf;
+	const char *temp = msg->buf;
+	int count = msg->len;
+	unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK; 
 	int retval;
 	int wrcount=0;
 
@@ -358,7 +352,7 @@
 		DEB2(printk(KERN_DEBUG "i2c-algo-bit.o: %s sendbytes: writing %2.2X\n",
 			    i2c_adap->name, c&0xff));
 		retval = i2c_outb(i2c_adap,c);
-		if (retval>0) {
+		if ((retval>0) || (nak_ok && (retval==0)))  { /* ok or ignored NAK */
 			count--; 
 			temp++;
 			wrcount++;
@@ -377,12 +371,18 @@
 	return wrcount;
 }
 
-static inline int readbytes(struct i2c_adapter *i2c_adap,char *buf,int count)
+static inline int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
 {
-	char *temp = buf;
 	int inval;
 	int rdcount=0;   	/* counts bytes read */
 	struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
+	char *temp = msg->buf;
+	int count = msg->len;
+	int recv_len = 0;
+
+	/* Receive [Count] for I2C_SMBUS_BLOCK_DATA or I2C_SMBUS_BLOCK_PROC_CALL protocol */
+	if (msg->flags & I2C_M_RECV_LEN)
+		recv_len = 1;
 
 	while (count > 0) {
 		inval = i2c_inb(i2c_adap);
@@ -395,6 +395,20 @@
 			break;
 		}
 
+		if (recv_len) {
+			recv_len = 0;
+			/* [Count] should be between 1 and 31 (I2C_SMBUS_BLOCK_MAX - 1). */
+			if (inval > 0 && inval < I2C_SMBUS_BLOCK_MAX) {
+				count = inval + 1;	/* plus one for [Count] itself */
+				msg->len = count;
+				if (msg->flags & I2C_M_RECV_PEC)
+					count++; /* plus one for PEC */
+			} else {
+				printk(KERN_ERR "i2c-algo-bit.o: readbytes: bad block count (%d).\n", inval);
+				break;
+			}
+		}
+		
 		if ( count > 1 ) {		/* send ack */
 			sdalo(adap);
 			DEBPROTO(printk(" Am "));
@@ -419,31 +433,34 @@
  * try_address) and transmits the address in the necessary format to handle
  * reads, writes as well as 10bit-addresses.
  * returns:
- *  0 everything went okay, the chip ack'ed
+ *  0 everything went okay, the chip ack'ed, or IGNORE_NAK flag was set
  * -x an error occurred (like: -EREMOTEIO if the device did not answer, or
  *	-ETIMEDOUT, for example if the lines are stuck...) 
  */
-static inline int bit_doAddress(struct i2c_adapter *i2c_adap,
-                                struct i2c_msg *msg, int retries) 
+static inline int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) 
 {
 	unsigned short flags = msg->flags;
+	unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK;
 	struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
 
 	unsigned char addr;
-	int ret;
+	int ret, retries;
+
+	retries = nak_ok ? 0 : i2c_adap->retries;
+	
 	if ( (flags & I2C_M_TEN)  ) { 
 		/* a ten bit address */
 		addr = 0xf0 | (( msg->addr >> 7) & 0x03);
 		DEB2(printk(KERN_DEBUG "addr0: %d\n",addr));
 		/* try extended address code...*/
 		ret = try_address(i2c_adap, addr, retries);
-		if (ret!=1) {
+		if ((ret != 1) && !nak_ok)  {
 			printk(KERN_ERR "died at extended address code.\n");
 			return -EREMOTEIO;
 		}
 		/* the remaining 8 bit address */
 		ret = i2c_outb(i2c_adap,msg->addr & 0x7f);
-		if (ret != 1) {
+		if ((ret != 1) && !nak_ok) {
 			/* the chip did not ack / xmission error occurred */
 			printk(KERN_ERR "died at 2nd address code.\n");
 			return -EREMOTEIO;
@@ -453,7 +470,7 @@
 			/* okay, now switch into reading mode */
 			addr |= 0x01;
 			ret = try_address(i2c_adap, addr, retries);
-			if (ret!=1) {
+			if ((ret!=1) && !nak_ok) {
 				printk(KERN_ERR "died at extended address code.\n");
 				return -EREMOTEIO;
 			}
@@ -465,10 +482,10 @@
 		if (flags & I2C_M_REV_DIR_ADDR )
 			addr ^= 1;
 		ret = try_address(i2c_adap, addr, retries);
-		if (ret!=1) {
+		if ((ret!=1) && !nak_ok)
 			return -EREMOTEIO;
-		}
 	}
+
 	return 0;
 }
 
@@ -479,16 +496,18 @@
 	struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
 	
 	int i,ret;
+	unsigned short nak_ok;
 
 	i2c_start(adap);
 	for (i=0;i<num;i++) {
 		pmsg = &msgs[i];
+		nak_ok = pmsg->flags & I2C_M_IGNORE_NAK; 
 		if (!(pmsg->flags & I2C_M_NOSTART)) {
 			if (i) {
 				i2c_repstart(adap);
 			}
-			ret = bit_doAddress(i2c_adap,pmsg,i2c_adap->retries);
-			if (ret != 0) {
+			ret = bit_doAddress(i2c_adap, pmsg);
+			if ((ret != 0) && !nak_ok) {
 				DEB2(printk(KERN_DEBUG "i2c-algo-bit.o: NAK from device addr %2.2x msg #%d\n",
 					msgs[i].addr,i));
 				return (ret<0) ? ret : -EREMOTEIO;
@@ -496,14 +515,14 @@
 		}
 		if (pmsg->flags & I2C_M_RD ) {
 			/* read bytes into buffer*/
-			ret = readbytes(i2c_adap,pmsg->buf,pmsg->len);
+			ret = readbytes(i2c_adap, pmsg);
 			DEB2(printk(KERN_DEBUG "i2c-algo-bit.o: read %d bytes.\n",ret));
 			if (ret < pmsg->len ) {
 				return (ret<0)? ret : -EREMOTEIO;
 			}
 		} else {
 			/* write bytes from buffer */
-			ret = sendbytes(i2c_adap,pmsg->buf,pmsg->len);
+			ret = sendbytes(i2c_adap, pmsg);
 			DEB2(printk(KERN_DEBUG "i2c-algo-bit.o: wrote %d bytes.\n",ret));
 			if (ret < pmsg->len ) {
 				return (ret<0) ? ret : -EREMOTEIO;
@@ -514,30 +533,25 @@
 	return num;
 }
 
-static int algo_control(struct i2c_adapter *adapter, 
-	unsigned int cmd, unsigned long arg)
-{
-	return 0;
-}
-
-static u32 bit_func(struct i2c_adapter *adap)
+static u32 bit_func(struct i2c_adapter *i2c_adap)
 {
 	return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | 
-	       I2C_FUNC_PROTOCOL_MANGLING;
+	       I2C_FUNC_PROTOCOL_MANGLING |
+	       I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
+	       I2C_FUNC_SMBUS_READ_BLOCK_DATA |
+	       I2C_FUNC_SMBUS_BLOCK_PROC_CALL_PEC |
+	       I2C_FUNC_SMBUS_READ_BLOCK_DATA_PEC;
 }
 
 
 /* -----exported algorithm data: -------------------------------------	*/
 
 static struct i2c_algorithm i2c_bit_algo = {
-	"Bit-shift algorithm",
-	I2C_ALGO_BIT,
-	bit_xfer,
-	NULL,
-	NULL,				/* slave_xmit		*/
-	NULL,				/* slave_recv		*/
-	algo_control,			/* ioctl		*/
-	bit_func,			/* functionality	*/
+	.owner		= THIS_MODULE,
+	.name		= "Bit-shift algorithm",
+	.id		= I2C_ALGO_BIT,
+	.master_xfer	= bit_xfer,
+	.functionality	= bit_func,
 };
 
 /* 
@@ -545,7 +559,6 @@
  */
 int i2c_bit_add_bus(struct i2c_adapter *adap)
 {
-	int i;
 	struct i2c_algo_bit_data *bit_adap = adap->algo_data;
 
 	if (bit_test) {
@@ -565,78 +578,26 @@
 	adap->timeout = 100;	/* default values, should	*/
 	adap->retries = 3;	/* be replaced by defines	*/
 
-	/* scan bus */
-	if (bit_scan) {
-		int ack;
-		printk(KERN_INFO " i2c-algo-bit.o: scanning bus %s.\n",
-		       adap->name);
-		for (i = 0x00; i < 0xff; i+=2) {
-			i2c_start(bit_adap);
-			ack = i2c_outb(adap,i);
-			i2c_stop(bit_adap);
-			if (ack>0) {
-				printk("(%02x)",i>>1); 
-			} else 
-				printk("."); 
-		}
-		printk("\n");
-	}
-
-#ifdef MODULE
-	MOD_INC_USE_COUNT;
-#endif
 	i2c_add_adapter(adap);
-
 	return 0;
 }
 
 
 int i2c_bit_del_bus(struct i2c_adapter *adap)
 {
-	int res;
-
-	if ((res = i2c_del_adapter(adap)) < 0)
-		return res;
-
-	DEB2(printk("i2c-algo-bit.o: adapter unregistered: %s\n",adap->name));
-
-#ifdef MODULE
-	MOD_DEC_USE_COUNT;
-#endif
-	return 0;
-}
-
-int __init i2c_algo_bit_init (void)
-{
-	printk(KERN_INFO "i2c-algo-bit.o: i2c bit algorithm module\n");
-	return 0;
+	return i2c_del_adapter(adap);
 }
 
-
-
 EXPORT_SYMBOL(i2c_bit_add_bus);
 EXPORT_SYMBOL(i2c_bit_del_bus);
 
-#ifdef MODULE
 MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>");
 MODULE_DESCRIPTION("I2C-Bus bit-banging algorithm");
 MODULE_LICENSE("GPL");
 
 MODULE_PARM(bit_test, "i");
-MODULE_PARM(bit_scan, "i");
 MODULE_PARM(i2c_debug,"i");
 
 MODULE_PARM_DESC(bit_test, "Test the lines of the bus to see if it is stuck");
-MODULE_PARM_DESC(bit_scan, "Scan for active chips on the bus");
 MODULE_PARM_DESC(i2c_debug,
             "debug level - 0 off; 1 normal; 2,3 more verbose; 9 bit-protocol");
-
-int init_module(void) 
-{
-	return i2c_algo_bit_init();
-}
-
-void cleanup_module(void) 
-{
-}
-#endif
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-algo-ibm_ocp.c linux-2.4.27-leo/drivers/i2c/i2c-algo-ibm_ocp.c
--- linux-2.4.27/drivers/i2c/i2c-algo-ibm_ocp.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-algo-ibm_ocp.c	2004-09-20 21:34:38.000000000 +0100
@@ -0,0 +1,901 @@
+/*
+   -------------------------------------------------------------------------
+   i2c-algo-ibm_ocp.c i2c driver algorithms for IBM PPC 405 adapters	    
+   -------------------------------------------------------------------------
+      
+   Ian DaSilva, MontaVista Software, Inc.
+   idasilva@mvista.com or source@mvista.com
+
+   Copyright 2000 MontaVista Software Inc.
+
+   Changes made to support the IIC peripheral on the IBM PPC 405
+
+
+   ---------------------------------------------------------------------------
+   This file was highly leveraged from i2c-algo-pcf.c, which was created
+   by Simon G. Vogl and Hans Berglund:
+
+
+     Copyright (C) 1995-1997 Simon G. Vogl
+                   1998-2000 Hans Berglund
+
+   With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and 
+   Frodo Looijaard <frodol@dds.nl> ,and also from Martin Bailey
+   <mbailey@littlefeet-inc.com>
+
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   ---------------------------------------------------------------------------
+
+   History: 01/20/12 - Armin
+   	akuster@mvista.com
+   	ported up to 2.4.16+	
+
+   Version 02/03/25 - Armin
+       converted to ocp format
+       removed commented out or #if 0 code
+       added Gérard Basler's fix to iic_combined_transaction() such that it 
+       returns the number of successfully completed transfers .
+*/
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-ibm_ocp.h>
+#include <asm/ocp.h>
+
+
+/* ----- global defines ----------------------------------------------- */
+#define DEB(x) if (i2c_debug>=1) x
+#define DEB2(x) if (i2c_debug>=2) x
+#define DEB3(x) if (i2c_debug>=3) x /* print several statistical values*/
+#define DEBPROTO(x) if (i2c_debug>=9) x;
+ 	/* debug the protocol by showing transferred bits */
+#define DEF_TIMEOUT 5
+
+
+/* ----- global variables ---------------------------------------------	*/
+
+
+/* module parameters:
+ */
+static int i2c_debug=0;
+
+/* --- setting states on the bus with the right timing: ---------------	*/
+
+#define iic_outb(adap, reg, val) adap->setiic(adap->data, (int) &(reg), val)
+#define iic_inb(adap, reg) adap->getiic(adap->data, (int) &(reg))
+
+#define IICO_I2C_SDAHIGH	0x0780
+#define IICO_I2C_SDALOW		0x0781
+#define IICO_I2C_SCLHIGH	0x0782
+#define IICO_I2C_SCLLOW		0x0783
+#define IICO_I2C_LINEREAD	0x0784
+
+#define IIC_SINGLE_XFER		0
+#define IIC_COMBINED_XFER	1
+
+#define IIC_ERR_LOST_ARB        -2
+#define IIC_ERR_INCOMPLETE_XFR  -3
+#define IIC_ERR_NACK            -1
+
+/* --- other auxiliary functions --------------------------------------	*/
+
+
+//
+// Description: Puts this process to sleep for a period equal to timeout 
+//
+static inline void iic_sleep(unsigned long timeout)
+{
+	schedule_timeout( timeout * HZ);
+}
+
+
+//
+// Description: This performs the IBM PPC 405 IIC initialization sequence
+// as described in the PPC405GP data book.
+//
+static int iic_init (struct i2c_algo_iic_data *adap)
+{
+	struct iic_regs *iic;	
+	struct iic_ibm *adap_priv_data = adap->data;
+	unsigned short	retval;
+	iic = (struct iic_regs *) adap_priv_data->iic_base;
+
+        /* Clear master low master address */
+        iic_outb(adap,iic->lmadr, 0);
+
+        /* Clear high master address */
+        iic_outb(adap,iic->hmadr, 0);
+
+        /* Clear low slave address */
+        iic_outb(adap,iic->lsadr, 0);
+
+        /* Clear high slave address */
+        iic_outb(adap,iic->hsadr, 0);
+
+        /* Clear status */
+        iic_outb(adap,iic->sts, 0x0a);
+
+        /* Clear extended status */
+        iic_outb(adap,iic->extsts, 0x8f);
+
+        /* Set clock division */
+        iic_outb(adap,iic->clkdiv, 0x04);
+
+	retval = iic_inb(adap, iic->clkdiv);
+	DEB(printk("iic_init: CLKDIV register = %x\n", retval));
+
+        /* Enable interrupts on Requested Master Transfer Complete */
+        iic_outb(adap,iic->intmsk, 0x01);
+
+        /* Clear transfer count */
+        iic_outb(adap,iic->xfrcnt, 0x0);
+
+        /* Clear extended control and status */
+        iic_outb(adap,iic->xtcntlss, 0xf0);
+
+        /* Set mode control (flush master data buf, enable hold SCL, exit */
+        /* unknown state.                                                 */
+        iic_outb(adap,iic->mdcntl, 0x47);
+
+        /* Clear control register */
+        iic_outb(adap,iic->cntl, 0x0);
+
+        DEB2(printk(KERN_DEBUG "iic_init: Initialized IIC on PPC 405\n"));
+        return 0;
+}
+
+
+//
+// Description: After we issue a transaction on the IIC bus, this function
+// is called.  It puts this process to sleep until we get an interrupt from
+// from the controller telling us that the transaction we requested in complete.
+//
+static int wait_for_pin(struct i2c_algo_iic_data *adap, int *status) 
+{
+
+	int timeout = DEF_TIMEOUT;
+	int retval;
+	struct iic_regs *iic;
+	struct iic_ibm *adap_priv_data = adap->data;
+	iic = (struct iic_regs *) adap_priv_data->iic_base;
+
+
+	*status = iic_inb(adap, iic->sts);
+#ifndef STUB_I2C
+
+	while (timeout-- && (*status & 0x01)) {
+	   adap->waitforpin(adap->data);
+	   *status = iic_inb(adap, iic->sts);
+	}
+#endif
+	if (timeout <= 0) {
+	   /* Issue stop signal on the bus, and force an interrupt */
+           retval = iic_inb(adap, iic->cntl);
+           iic_outb(adap, iic->cntl, retval | 0x80);
+           /* Clear status register */
+	   iic_outb(adap, iic->sts, 0x0a);
+	   /* Exit unknown bus state */
+	   retval = iic_inb(adap, iic->mdcntl);
+	   iic_outb(adap, iic->mdcntl, (retval | 0x02));
+
+	   // Check the status of the controller.  Does it still see a
+	   // pending transfer, even though we've tried to stop any
+	   // ongoing transaction?
+           retval = iic_inb(adap, iic->sts);
+           retval = retval & 0x01;
+           if(retval) {
+	      // The iic controller is hosed.  It is not responding to any
+	      // of our commands.  We have already tried to force it into
+	      // a known state, but it has not worked.  Our only choice now
+	      // is a soft reset, which will clear all registers, and force
+	      // us to re-initialize the controller.
+	      /* Soft reset */
+              iic_outb(adap, iic->xtcntlss, 0x01);
+              udelay(500);
+              iic_init(adap);
+	      /* Is the pending transfer bit in the sts reg finally cleared? */
+              retval = iic_inb(adap, iic->sts);
+              retval = retval & 0x01;
+              if(retval) {
+                 printk(KERN_CRIT "The IIC Controller is hosed.  A processor reset is required\n");
+              }
+	      // For some reason, even though the interrupt bit in this
+	      // register was set during iic_init, it didn't take.  We
+	      // need to set it again.  Don't ask me why....this is just what
+	      // I saw when testing timeouts.
+              iic_outb(adap, iic->intmsk, 0x01);
+           }
+	   return(-1);
+	}
+	else
+	   return(0);
+}
+
+
+//------------------------------------
+// Utility functions
+//
+
+
+//
+// Description: Look at the status register to see if there was an error
+// in the requested transaction.  If there is, look at the extended status
+// register and determine the exact cause.
+//
+int analyze_status(struct i2c_algo_iic_data *adap, int *error_code)
+{
+   int ret;
+   struct iic_regs *iic;
+   struct iic_ibm *adap_priv_data = adap->data;
+   iic = (struct iic_regs *) adap_priv_data->iic_base;
+
+	
+   ret = iic_inb(adap, iic->sts);
+   if(ret & 0x04) {
+      // Error occurred
+      ret = iic_inb(adap, iic->extsts);
+      if(ret & 0x04) {
+         // Lost arbitration
+         *error_code =  IIC_ERR_LOST_ARB;
+      }
+      if(ret & 0x02) {
+         // Incomplete transfer
+         *error_code = IIC_ERR_INCOMPLETE_XFR;
+      }
+      if(ret & 0x01) {
+         // Master transfer aborted by a NACK during the transfer of the 
+	 // address byte
+         *error_code = IIC_ERR_NACK;
+      }
+      return -1;
+   }
+   return 0;
+}
+
+
+//
+// Description: This function is called by the upper layers to do the
+// grunt work for a master send transaction
+//
+static int iic_sendbytes(struct i2c_adapter *i2c_adap,const char *buf,
+                         int count, int xfer_flag)
+{
+	struct iic_regs *iic;
+	struct i2c_algo_iic_data *adap = i2c_adap->algo_data;
+	struct iic_ibm *adap_priv_data = adap->data;
+	int wrcount, status, timeout;
+	int loops, remainder, i, j;
+	int ret, error_code;
+  	iic = (struct iic_regs *) adap_priv_data->iic_base;
+
+ 
+	if( count == 0 ) return 0;
+	wrcount = 0;
+	loops =  count / 4;
+	remainder = count % 4;
+
+	if((loops > 1) && (remainder == 0)) {
+	   for(i=0; i<(loops-1); i++) {
+       	      //
+   	      // Write four bytes to master data buffer
+	      //
+	      for(j=0; j<4; j++) {
+   	         iic_outb(adap, iic->mdbuf, 
+	         buf[wrcount++]);
+  	      }
+	      //
+	      // Issue command to IICO device to begin transmission
+	      //
+	      iic_outb(adap, iic->cntl, 0x35);
+	      //
+	      // Wait for transmission to complete.  When it does, 
+	      //loop to the top of the for statement and write the 
+	      // next four bytes.
+	      //
+	      timeout = wait_for_pin(adap, &status);
+	      if(timeout < 0) {
+	         //
+	         // Error handling
+	         //
+                 //printk(KERN_ERR "Error: write timeout\n");
+                 return wrcount;
+	      }
+	      ret = analyze_status(adap, &error_code);
+	      if(ret < 0) {
+                 if(error_code == IIC_ERR_INCOMPLETE_XFR) {
+                    // Return the number of bytes transferred
+                    ret = iic_inb(adap, iic->xfrcnt);
+                    ret = ret & 0x07;
+                    return (wrcount-4+ret);
+                 }
+                 else return error_code;
+              }
+           }
+	}
+	else if((loops >= 1) && (remainder > 0)){
+	   //printk(KERN_DEBUG "iic_sendbytes: (loops >= 1)\n");
+	   for(i=0; i<loops; i++) {
+              //
+              // Write four bytes to master data buffer
+              //
+              for(j=0; j<4; j++) {
+                 iic_outb(adap, iic->mdbuf,
+                 buf[wrcount++]);
+              }
+              //
+              // Issue command to IICO device to begin transmission
+              //
+              iic_outb(adap, iic->cntl, 0x35);
+              //
+              // Wait for transmission to complete.  When it does,
+              //loop to the top of the for statement and write the
+              // next four bytes.
+              //
+              timeout = wait_for_pin(adap, &status);
+              if(timeout < 0) {
+                 //
+                 // Error handling
+                 //
+                 //printk(KERN_ERR "Error: write timeout\n");
+                 return wrcount;
+              }
+              ret = analyze_status(adap, &error_code);
+              if(ret < 0) {
+                 if(error_code == IIC_ERR_INCOMPLETE_XFR) {
+                    // Return the number of bytes transferred
+                    ret = iic_inb(adap, iic->xfrcnt);
+                    ret = ret & 0x07;
+                    return (wrcount-4+ret);
+                 }
+                 else return error_code;
+              }
+           }
+        }
+
+	//printk(KERN_DEBUG "iic_sendbytes: expedite write\n");
+	if(remainder == 0) remainder = 4;
+	// remainder = remainder - 1;
+	//
+	// Write the remaining bytes (less than or equal to 4)
+	//
+	for(i=0; i<remainder; i++) {
+	   iic_outb(adap, iic->mdbuf, buf[wrcount++]);
+	   //printk(KERN_DEBUG "iic_sendbytes:  data transferred = %x, wrcount = %d\n", buf[wrcount-1], (wrcount-1));
+	}
+        //printk(KERN_DEBUG "iic_sendbytes: Issuing write\n");
+
+        if(xfer_flag == IIC_COMBINED_XFER) {
+           iic_outb(adap, iic->cntl, (0x09 | ((remainder-1) << 4)));
+        }
+	else {
+           iic_outb(adap, iic->cntl, (0x01 | ((remainder-1) << 4)));
+        }
+	DEB2(printk(KERN_DEBUG "iic_sendbytes: Waiting for interrupt\n"));
+	timeout = wait_for_pin(adap, &status);
+        if(timeout < 0) {
+       	   //
+           // Error handling
+           //
+           //printk(KERN_ERR "Error: write timeout\n");
+           return wrcount;
+        }
+        ret = analyze_status(adap, &error_code);
+        if(ret < 0) {
+           if(error_code == IIC_ERR_INCOMPLETE_XFR) {
+              // Return the number of bytes transferred
+              ret = iic_inb(adap, iic->xfrcnt);
+              ret = ret & 0x07;
+              return (wrcount-4+ret);
+           }
+           else return error_code;
+        }
+	DEB2(printk(KERN_DEBUG "iic_sendbytes: Got interrupt\n"));
+	return wrcount;
+}
+
+
+//
+// Description: Called by the upper layers to do the grunt work for
+// a master read transaction.
+//
+static int iic_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count, int xfer_type)
+{
+	struct iic_regs *iic;
+	int rdcount=0, i, status, timeout;
+	struct i2c_algo_iic_data *adap = i2c_adap->algo_data;
+	struct iic_ibm *adap_priv_data = adap->data;
+	int loops, remainder, j;
+        int ret, error_code;
+	iic = (struct iic_regs *) adap_priv_data->iic_base;
+
+	if(count == 0) return 0;
+	loops = count / 4;
+	remainder = count % 4;
+
+	//printk(KERN_DEBUG "iic_readbytes: loops = %d, remainder = %d\n", loops, remainder);
+
+	if((loops > 1) && (remainder == 0)) {
+	//printk(KERN_DEBUG "iic_readbytes: (loops > 1) && (remainder == 0)\n");
+	   for(i=0; i<(loops-1); i++) {
+	      //
+              // Issue command to begin master read (4 bytes maximum)
+              //
+	      //printk(KERN_DEBUG "--->Issued read command\n");
+	      iic_outb(adap, iic->cntl, 0x37);
+	      //
+              // Wait for transmission to complete.  When it does,
+              // loop to the top of the for statement and write the
+              // next four bytes.
+              //
+	      //printk(KERN_DEBUG "--->Waiting for interrupt\n");
+              timeout = wait_for_pin(adap, &status);
+              if(timeout < 0) {
+	         // Error Handler
+		 //printk(KERN_ERR "Error: read timed out\n");
+                 return rdcount;
+	      }
+              //printk(KERN_DEBUG "--->Got interrupt\n");
+
+              ret = analyze_status(adap, &error_code);
+	      if(ret < 0) {
+                 if(error_code == IIC_ERR_INCOMPLETE_XFR)
+                    return rdcount;
+                 else
+                    return error_code;
+              }
+
+	      for(j=0; j<4; j++) {
+                 // Wait for data to shuffle to top of data buffer
+                 // This value needs to optimized.
+		 udelay(1);
+	         buf[rdcount] = iic_inb(adap, iic->mdbuf);
+	         rdcount++;
+		 //printk(KERN_DEBUG "--->Read one byte\n");
+              }
+           }
+	}
+
+	else if((loops >= 1) && (remainder > 0)){
+	//printk(KERN_DEBUG "iic_readbytes: (loops >=1) && (remainder > 0)\n");
+	   for(i=0; i<loops; i++) {
+              //
+              // Issue command to begin master read (4 bytes maximum)
+              //
+              //printk(KERN_DEBUG "--->Issued read command\n");
+              iic_outb(adap, iic->cntl, 0x37);
+              //
+              // Wait for transmission to complete.  When it does,
+              // loop to the top of the for statement and write the
+              // next four bytes.
+              //
+              //printk(KERN_DEBUG "--->Waiting for interrupt\n");
+              timeout = wait_for_pin(adap, &status);
+              if(timeout < 0) {
+                 // Error Handler
+                 //printk(KERN_ERR "Error: read timed out\n");
+                 return rdcount;
+              }
+              //printk(KERN_DEBUG "--->Got interrupt\n");
+
+              ret = analyze_status(adap, &error_code);
+              if(ret < 0) {
+                 if(error_code == IIC_ERR_INCOMPLETE_XFR)
+                    return rdcount;
+                 else
+                    return error_code;
+              }
+
+              for(j=0; j<4; j++) {
+                 // Wait for data to shuffle to top of data buffer
+                 // This value needs to optimized.
+                 udelay(1);
+                 buf[rdcount] = iic_inb(adap, iic->mdbuf);
+                 rdcount++;
+                 //printk(KERN_DEBUG "--->Read one byte\n");
+              }
+           }
+        }
+
+	//printk(KERN_DEBUG "iic_readbytes: expedite read\n");
+	if(remainder == 0) remainder = 4;
+	DEB2(printk(KERN_DEBUG "iic_readbytes: writing %x to IICO_CNTL\n", (0x03 | ((remainder-1) << 4))));
+
+	if(xfer_type == IIC_COMBINED_XFER) {
+	   iic_outb(adap, iic->cntl, (0x0b | ((remainder-1) << 4)));
+        }
+        else {
+	   iic_outb(adap, iic->cntl, (0x03 | ((remainder-1) << 4)));
+        }
+	DEB2(printk(KERN_DEBUG "iic_readbytes: Wait for pin\n"));
+        timeout = wait_for_pin(adap, &status);
+	DEB2(printk(KERN_DEBUG "iic_readbytes: Got the interrupt\n"));
+        if(timeout < 0) {
+           // Error Handler
+	   //printk(KERN_ERR "Error: read timed out\n");
+           return rdcount;
+        }
+
+        ret = analyze_status(adap, &error_code);
+        if(ret < 0) {
+           if(error_code == IIC_ERR_INCOMPLETE_XFR)
+              return rdcount;
+           else
+              return error_code;
+        }
+
+	//printk(KERN_DEBUG "iic_readbyte: Begin reading data buffer\n");
+	for(i=0; i<remainder; i++) {
+	   buf[rdcount] = iic_inb(adap, iic->mdbuf);
+	   // printk(KERN_DEBUG "iic_readbytes:  Character read = %x\n", buf[rdcount]);
+           rdcount++;
+	}
+
+	return rdcount;
+}
+
+
+//
+// Description:  This function implements combined transactions.  Combined
+// transactions consist of combinations of reading and writing blocks of data.
+// Each transfer (i.e. a read or a write) is separated by a repeated start
+// condition.
+//
+static int iic_combined_transaction(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) 
+{
+   int i;
+   struct i2c_msg *pmsg;
+   int ret;
+
+   DEB2(printk(KERN_DEBUG "Beginning combined transaction\n"));
+	for(i=0; i < num; i++) {
+		pmsg = &msgs[i];
+		if(pmsg->flags & I2C_M_RD) {
+
+			// Last read or write segment needs to be terminated with a stop
+			if(i < num-1) {
+				DEB2(printk(KERN_DEBUG "This one is a read\n"));
+			}
+			else {
+				DEB2(printk(KERN_DEBUG "Doing the last read\n"));
+			}
+			ret = iic_readbytes(i2c_adap, pmsg->buf, pmsg->len, (i < num-1) ? IIC_COMBINED_XFER : IIC_SINGLE_XFER);
+
+			if (ret != pmsg->len) {
+				DEB2(printk("i2c-algo-ppc405.o: fail: "
+							"only read %d bytes.\n",ret));
+				return i;
+			}
+			else {
+				DEB2(printk("i2c-algo-ppc405.o: read %d bytes.\n",ret));
+			}
+		}
+		else if(!(pmsg->flags & I2C_M_RD)) {
+
+			// Last read or write segment needs to be terminated with a stop
+			if(i < num-1) {
+				DEB2(printk(KERN_DEBUG "This one is a write\n"));
+			}
+			else {
+				DEB2(printk(KERN_DEBUG "Doing the last write\n"));
+			}
+			ret = iic_sendbytes(i2c_adap, pmsg->buf, pmsg->len, (i < num-1) ? IIC_COMBINED_XFER : IIC_SINGLE_XFER);
+
+			if (ret != pmsg->len) {
+				DEB2(printk("i2c-algo-ppc405.o: fail: "
+							"only wrote %d bytes.\n",ret));
+				return i;
+			}
+			else {
+				DEB2(printk("i2c-algo-ppc405.o: wrote %d bytes.\n",ret));
+			}
+		}
+	}
+ 
+	return num;
+}
+
+
+//
+// Description: Whenever we initiate a transaction, the first byte clocked
+// onto the bus after the start condition is the address (7 bit) of the
+// device we want to talk to.  This function manipulates the address specified
+// so that it makes sense to the hardware when written to the IIC peripheral.
+//
+// Note: 10 bit addresses are not supported in this driver, although they are
+// supported by the hardware.  This functionality needs to be implemented.
+//
+static inline int iic_doAddress(struct i2c_algo_iic_data *adap,
+                                struct i2c_msg *msg, int retries) 
+{
+	struct iic_regs *iic;
+	unsigned short flags = msg->flags;
+	unsigned char addr;
+	struct iic_ibm *adap_priv_data = adap->data;
+	iic = (struct iic_regs *) adap_priv_data->iic_base;
+
+//
+// The following segment for 10 bit addresses needs to be ported
+//
+/* Ten bit addresses not supported right now
+	if ( (flags & I2C_M_TEN)  ) { 
+		// a ten bit address
+		addr = 0xf0 | (( msg->addr >> 7) & 0x03);
+		DEB2(printk(KERN_DEBUG "addr0: %d\n",addr));
+		// try extended address code...
+		ret = try_address(adap, addr, retries);
+		if (ret!=1) {
+			printk(KERN_ERR "iic_doAddress: died at extended address code.\n");
+			return -EREMOTEIO;
+		}
+		// the remaining 8 bit address
+		iic_outb(adap,msg->addr & 0x7f);
+		// Status check comes here
+		if (ret != 1) {
+			printk(KERN_ERR "iic_doAddress: died at 2nd address code.\n");
+			return -EREMOTEIO;
+		}
+		if ( flags & I2C_M_RD ) {
+			i2c_repstart(adap);
+			// okay, now switch into reading mode
+			addr |= 0x01;
+			ret = try_address(adap, addr, retries);
+			if (ret!=1) {
+				printk(KERN_ERR "iic_doAddress: died at extended address code.\n");
+				return -EREMOTEIO;
+			}
+		}
+	} else ----------> // normal 7 bit address
+
+Ten bit addresses not supported yet */
+
+	addr = ( msg->addr << 1 );
+	if (flags & I2C_M_RD )
+		addr |= 1;
+	if (flags & I2C_M_REV_DIR_ADDR )
+		addr ^= 1;
+	//
+	// Write to the low slave address
+	//
+	iic_outb(adap, iic->lmadr, addr);
+	//
+	// Write zero to the high slave register since we are
+	// only using 7 bit addresses
+	//
+	iic_outb(adap, iic->hmadr, 0);
+
+	return 0;
+}
+
+
+//
+// Description: Prepares the controller for a transaction (clearing status
+// registers, data buffers, etc), and then calls either iic_readbytes or
+// iic_sendbytes to do the actual transaction.
+//
+static int iic_xfer(struct i2c_adapter *i2c_adap,
+		    struct i2c_msg msgs[], 
+		    int num)
+{
+	struct iic_regs *iic;
+	struct i2c_algo_iic_data *adap = i2c_adap->algo_data;
+	struct iic_ibm *adap_priv_data = adap->data;
+	struct i2c_msg *pmsg;
+	int i = 0;
+	int ret;
+	iic = (struct iic_regs *) adap_priv_data->iic_base;
+
+	pmsg = &msgs[i];
+
+	//
+	// Clear status register
+	//
+	DEB2(printk(KERN_DEBUG "iic_xfer: iic_xfer: Clearing status register\n"));
+	iic_outb(adap, iic->sts, 0x0a);
+
+	//
+	// Wait for any pending transfers to complete
+	//
+	DEB2(printk(KERN_DEBUG "iic_xfer: Waiting for any pending transfers to complete\n"));
+	while((ret = iic_inb(adap, iic->sts)) == 0x01) {
+		;
+	}
+
+	//
+	// Flush master data buf
+	//
+	DEB2(printk(KERN_DEBUG "iic_xfer: Clearing master data buffer\n"));		
+	ret = iic_inb(adap, iic->mdcntl);
+	iic_outb(adap, iic->mdcntl, ret | 0x40);
+
+	//
+	// Load slave address
+	//
+	DEB2(printk(KERN_DEBUG "iic_xfer: Loading slave address\n"));
+	ret = iic_doAddress(adap, pmsg, i2c_adap->retries);
+
+        //
+        // Check to see if the bus is busy
+        //
+        ret = iic_inb(adap, iic->extsts);
+        // Mask off the irrelevent bits
+        ret = ret & 0x70;
+        // When the bus is free, the BCS bits in the EXTSTS register are 0b100
+        if(ret != 0x40) return IIC_ERR_LOST_ARB;
+
+	//
+	// Combined transaction (read and write)
+	//
+	if(num > 1) {
+           DEB2(printk(KERN_DEBUG "iic_xfer: Call combined transaction\n"));
+           ret = iic_combined_transaction(i2c_adap, msgs, num);
+        }
+	//
+	// Read only
+	//
+	else if((num == 1) && (pmsg->flags & I2C_M_RD)) {
+	   //
+	   // Tell device to begin reading data from the  master data 
+	   //
+	   DEB2(printk(KERN_DEBUG "iic_xfer: Call adapter's read\n"));
+	   ret = iic_readbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_SINGLE_XFER);
+	} 
+        //
+	// Write only
+	//
+	else if((num == 1 ) && (!(pmsg->flags & I2C_M_RD))) {
+	   //
+	   // Write data to master data buffers and tell our device
+	   // to begin transmitting
+	   //
+	   DEB2(printk(KERN_DEBUG "iic_xfer: Call adapter's write\n"));
+	   ret = iic_sendbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_SINGLE_XFER);
+	}	
+
+	return ret;   
+}
+
+
+//
+// Description: Implements device specific ioctls.  Higher level ioctls can
+// be found in i2c-core.c and are typical of any i2c controller (specifying
+// slave address, timeouts, etc).  These ioctls take advantage of any hardware
+// features built into the controller for which this algorithm-adapter set
+// was written.  These ioctls allow you to take control of the data and clock
+// lines on the IBM PPC 405 IIC controller and set the either high or low,
+// similar to a GPIO pin.
+//
+static int algo_control(struct i2c_adapter *adapter, 
+	unsigned int cmd, unsigned long arg)
+{
+	struct iic_regs *iic;
+	struct i2c_algo_iic_data *adap = adapter->algo_data;
+	struct iic_ibm *adap_priv_data = adap->data;
+	int ret=0;
+	int lines;
+	iic = (struct iic_regs *) adap_priv_data->iic_base;
+
+	lines = iic_inb(adap, iic->directcntl);
+
+	if (cmd == IICO_I2C_SDAHIGH) {
+	      lines = lines & 0x01;
+	      if( lines ) lines = 0x04;
+	      else lines = 0;
+	      iic_outb(adap, iic->directcntl,(0x08|lines));
+	}
+	else if (cmd == IICO_I2C_SDALOW) {
+	      lines = lines & 0x01;
+	      if( lines ) lines = 0x04;
+              else lines = 0;
+              iic_outb(adap, iic->directcntl,(0x00|lines));
+	}
+	else if (cmd == IICO_I2C_SCLHIGH) {
+              lines = lines & 0x02;
+              if( lines ) lines = 0x08;
+              else lines = 0;
+              iic_outb(adap, iic->directcntl,(0x04|lines));
+	}
+	else if (cmd == IICO_I2C_SCLLOW) {
+              lines = lines & 0x02;
+	      if( lines ) lines = 0x08;
+              else lines = 0;
+              iic_outb(adap, iic->directcntl,(0x00|lines));
+	}
+	else if (cmd == IICO_I2C_LINEREAD) {
+	      ret = lines;
+	}
+	return ret;
+}
+
+
+static u32 iic_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | 
+	       I2C_FUNC_PROTOCOL_MANGLING; 
+}
+
+
+/* -----exported algorithm data: -------------------------------------	*/
+
+static struct i2c_algorithm iic_algo = {
+	.owner		= THIS_MODULE,
+	.name		= "IBM on-chip IIC algorithm",
+	.id		= I2C_ALGO_OCP,
+	.master_xfer	= iic_xfer,
+	.algo_control	= algo_control,
+	.functionality	= iic_func,
+};
+
+/* 
+ * registering functions to load algorithms at runtime 
+ */
+
+
+//
+// Description: Register bus structure
+//
+int i2c_ocp_add_bus(struct i2c_adapter *adap)
+{
+	struct i2c_algo_iic_data *iic_adap = adap->algo_data;
+
+	DEB2(printk(KERN_DEBUG "i2c-algo-iic.o: hw routines for %s registered.\n",
+	            adap->name));
+
+	/* register new adapter to i2c module... */
+
+	adap->id |= iic_algo.id;
+	adap->algo = &iic_algo;
+
+	adap->timeout = 100;	/* default values, should	*/
+	adap->retries = 3;		/* be replaced by defines	*/
+
+	iic_init(iic_adap);
+	i2c_add_adapter(adap);
+	return 0;
+}
+
+
+//
+// Done
+//
+int i2c_ocp_del_bus(struct i2c_adapter *adap)
+{
+	return i2c_del_adapter(adap);
+}
+
+
+EXPORT_SYMBOL(i2c_ocp_add_bus);
+EXPORT_SYMBOL(i2c_ocp_del_bus);
+
+//
+// The MODULE_* macros resolve to nothing if MODULES is not defined
+// when this file is compiled.
+//
+MODULE_AUTHOR("MontaVista Software <www.mvista.com>");
+MODULE_DESCRIPTION("PPC 405 iic algorithm");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(i2c_debug,"i");
+
+MODULE_PARM_DESC(i2c_debug,
+        "debug level - 0 off; 1 normal; 2,3 more verbose; 9 iic-protocol");
+
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-algo-pcf.c linux-2.4.27-leo/drivers/i2c/i2c-algo-pcf.c
--- linux-2.4.27/drivers/i2c/i2c-algo-pcf.c	2004-02-20 14:11:41.000000000 +0000
+++ linux-2.4.27-leo/drivers/i2c/i2c-algo-pcf.c	2004-09-20 21:34:38.000000000 +0100
@@ -32,14 +32,11 @@
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/init.h>
-#include <asm/uaccess.h>
-#include <linux/ioport.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
-
 #include <linux/i2c.h>
 #include <linux/i2c-algo-pcf.h>
-#include "i2c-pcf8584.h"
+
 
 /* ----- global defines ----------------------------------------------- */
 #define DEB(x) if (i2c_debug>=1) x
@@ -52,7 +49,6 @@
 /* module parameters:
  */
 static int i2c_debug=0;
-static int pcf_scan=0;	/* have a look at what's hanging 'round		*/
 
 /* --- setting states on the bus with the right timing: ---------------	*/
 
@@ -149,8 +145,7 @@
 	set_pcf(adap, 1, I2C_PCF_PIN);
 	/* check to see S1 now used as R/W ctrl -
 	   PCF8584 does that when ESO is zero */
-	/* PCF also resets PIN bit */
-	if ((temp = get_pcf(adap, 1)) != (0)) {
+	if (((temp = get_pcf(adap, 1)) & 0x7f) != (0)) {
 		DEB2(printk(KERN_ERR "i2c-algo-pcf.o: PCF detection failed -- can't select S0 (0x%02x).\n", temp));
 		return -ENXIO; /* definetly not PCF8584 */
 	}
@@ -166,7 +161,7 @@
 	/* S1=0xA0, next byte in S2					*/
 	set_pcf(adap, 1, I2C_PCF_PIN | I2C_PCF_ES1);
 	/* check to see S2 now selected */
-	if ((temp = get_pcf(adap, 1)) != I2C_PCF_ES1) {
+	if (((temp = get_pcf(adap, 1)) & 0x7f) != I2C_PCF_ES1) {
 		DEB2(printk(KERN_ERR "i2c-algo-pcf.o: PCF detection failed -- can't select S2 (0x%02x).\n", temp));
 		return -ENXIO;
 	}
@@ -427,12 +422,6 @@
 	return (i);
 }
 
-static int algo_control(struct i2c_adapter *adapter, 
-	unsigned int cmd, unsigned long arg)
-{
-	return 0;
-}
-
 static u32 pcf_func(struct i2c_adapter *adap)
 {
 	return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | 
@@ -442,14 +431,11 @@
 /* -----exported algorithm data: -------------------------------------	*/
 
 static struct i2c_algorithm pcf_algo = {
-	"PCF8584 algorithm",
-	I2C_ALGO_PCF,
-	pcf_xfer,
-	NULL,
-	NULL,				/* slave_xmit		*/
-	NULL,				/* slave_recv		*/
-	algo_control,			/* ioctl		*/
-	pcf_func,			/* functionality	*/
+	.owner		= THIS_MODULE,
+	.name		= "PCF8584 algorithm",
+	.id		= I2C_ALGO_PCF,
+	.master_xfer	= pcf_xfer,
+	.functionality	= pcf_func,
 };
 
 /* 
@@ -457,7 +443,7 @@
  */
 int i2c_pcf_add_bus(struct i2c_adapter *adap)
 {
-	int i, status;
+	int i;
 	struct i2c_algo_pcf_data *pcf_adap = adap->algo_data;
 
 	DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: hw routines for %s registered.\n",
@@ -475,81 +461,23 @@
 		return i;
 	}
 
-#ifdef MODULE
-	MOD_INC_USE_COUNT;
-#endif
-
 	i2c_add_adapter(adap);
-
-	/* scan bus */
-	if (pcf_scan) {
-		printk(KERN_INFO " i2c-algo-pcf.o: scanning bus %s.\n",
-		       adap->name);
-		for (i = 0x00; i < 0xff; i+=2) {
-			if (wait_for_bb(pcf_adap)) {
-    			printk(KERN_INFO " i2c-algo-pcf.o: scanning bus %s - TIMEOUTed.\n",
-		           adap->name);
-			    break;
-			}
-			i2c_outb(pcf_adap, i);
-			i2c_start(pcf_adap);
-			if ((wait_for_pin(pcf_adap, &status) >= 0) && 
-			    ((status & I2C_PCF_LRB) == 0)) { 
-				printk("(%02x)",i>>1); 
-			} else {
-				printk("."); 
-			}
-			i2c_stop(pcf_adap);
-			udelay(pcf_adap->udelay);
-		}
-		printk("\n");
-	}
 	return 0;
 }
 
 
 int i2c_pcf_del_bus(struct i2c_adapter *adap)
 {
-	int res;
-	if ((res = i2c_del_adapter(adap)) < 0)
-		return res;
-	DEB2(printk("i2c-algo-pcf.o: adapter unregistered: %s\n",adap->name));
-
-#ifdef MODULE
-	MOD_DEC_USE_COUNT;
-#endif
-	return 0;
-}
-
-int __init i2c_algo_pcf_init (void)
-{
-	printk("i2c-algo-pcf.o: i2c pcf8584 algorithm module\n");
-	return 0;
+	return i2c_del_adapter(adap);
 }
 
-
 EXPORT_SYMBOL(i2c_pcf_add_bus);
 EXPORT_SYMBOL(i2c_pcf_del_bus);
 
-#ifdef MODULE
 MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>");
 MODULE_DESCRIPTION("I2C-Bus PCF8584 algorithm");
 MODULE_LICENSE("GPL");
 
-MODULE_PARM(pcf_scan, "i");
 MODULE_PARM(i2c_debug,"i");
-
-MODULE_PARM_DESC(pcf_scan, "Scan for active chips on the bus");
 MODULE_PARM_DESC(i2c_debug,
         "debug level - 0 off; 1 normal; 2,3 more verbose; 9 pcf-protocol");
-
-
-int init_module(void) 
-{
-	return i2c_algo_pcf_init();
-}
-
-void cleanup_module(void) 
-{
-}
-#endif
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-ali1535.c linux-2.4.27-leo/drivers/i2c/i2c-ali1535.c
--- linux-2.4.27/drivers/i2c/i2c-ali1535.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-ali1535.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,601 @@
+/*
+    i2c-ali1535.c - Part of lm_sensors, Linux kernel modules for hardware
+                    monitoring
+    Copyright (c) 2000  Frodo Looijaard <frodol@dds.nl>, 
+                        Philip Edelbrock <phil@netroedge.com>, 
+                        Mark D. Studebaker <mdsxyz123@yahoo.com>,
+                        Dan Eaton <dan.eaton@rocketlogix.com> and 
+                        Stephen Rousset<stephen.rousset@rocketlogix.com> 
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+    This is the driver for the SMB Host controller on
+    Acer Labs Inc. (ALI) M1535 South Bridge.
+
+    The M1535 is a South bridge for portable systems.
+    It is very similar to the M15x3 South bridges also produced
+    by Acer Labs Inc.  Some of the registers within the part
+    have moved and some have been redefined slightly. Additionally,
+    the sequencing of the SMBus transactions has been modified
+    to be more consistent with the sequencing recommended by
+    the manufacturer and observed through testing.  These
+    changes are reflected in this driver and can be identified
+    by comparing this driver to the i2c-ali15x3 driver.
+    For an overview of these chips see http://www.acerlabs.com
+
+    The SMB controller is part of the 7101 device, which is an
+    ACPI-compliant Power Management Unit (PMU).
+
+    The whole 7101 device has to be enabled for the SMB to work.
+    You can't just enable the SMB alone.
+    The SMB and the ACPI have separate I/O spaces.
+    We make sure that the SMB is enabled. We leave the ACPI alone.
+
+    This driver controls the SMB Host only.
+
+    This driver does not use interrupts.
+*/
+
+
+/* Note: we assume there can only be one ALI1535, with one SMBus interface */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/semaphore.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+
+/* ALI1535 SMBus address offsets */
+#define SMBHSTSTS (0 + ali1535_smba)
+#define SMBHSTTYP (1 + ali1535_smba)
+#define SMBHSTPORT (2 + ali1535_smba)
+#define SMBHSTCMD (7 + ali1535_smba)
+#define SMBHSTADD (3 + ali1535_smba)
+#define SMBHSTDAT0 (4 + ali1535_smba)
+#define SMBHSTDAT1 (5 + ali1535_smba)
+#define SMBBLKDAT (6 + ali1535_smba)
+
+/* PCI Address Constants */
+#define SMBCOM    0x004
+#define SMBREV    0x008
+#define SMBCFG    0x0D1
+#define SMBBA     0x0E2
+#define SMBHSTCFG 0x0F0
+#define SMBCLK    0x0F2
+
+/* Other settings */
+#define MAX_TIMEOUT 500		/* times 1/100 sec */
+#define ALI1535_SMB_IOSIZE 32
+
+/* 
+*/
+#define ALI1535_SMB_DEFAULTBASE 0x8040
+
+/* ALI1535 address lock bits */
+#define ALI1535_LOCK	0x06 < dwe >
+
+/* ALI1535 command constants */
+#define ALI1535_QUICK      0x00
+#define ALI1535_BYTE       0x10
+#define ALI1535_BYTE_DATA  0x20
+#define ALI1535_WORD_DATA  0x30
+#define ALI1535_BLOCK_DATA 0x40
+#define ALI1535_I2C_READ   0x60
+
+#define	ALI1535_DEV10B_EN	0x80	/* Enable 10-bit addressing in */
+                                        /*  I2C read                   */
+#define	ALI1535_T_OUT		0x08	/* Time-out Command (write)    */
+#define	ALI1535_A_HIGH_BIT9	0x08	/* Bit 9 of 10-bit address in  */
+                                        /* Alert-Response-Address      */
+                                        /* (read)                      */
+#define	ALI1535_KILL		0x04	/* Kill Command (write)        */
+#define	ALI1535_A_HIGH_BIT8	0x04	/* Bit 8 of 10-bit address in  */
+                                        /*  Alert-Response-Address     */
+                                        /*  (read)                     */
+
+#define	ALI1535_D_HI_MASK	0x03	/* Mask for isolating bits 9-8 */
+                                        /*  of 10-bit address in I2C   */ 
+                                        /*  Read Command               */
+
+/* ALI1535 status register bits */
+#define ALI1535_STS_IDLE	0x04
+#define ALI1535_STS_BUSY	0x08	/* host busy */
+#define ALI1535_STS_DONE	0x10	/* transaction complete */
+#define ALI1535_STS_DEV		0x20	/* device error */
+#define ALI1535_STS_BUSERR	0x40	/* bus error    */
+#define ALI1535_STS_FAIL	0x80    /* failed bus transaction */
+#define ALI1535_STS_ERR		0xE0	/* all the bad error bits */
+
+#define ALI1535_BLOCK_CLR	0x04	/* reset block data index */
+
+/* ALI1535 device address register bits */
+#define	ALI1535_RD_ADDR		0x01	/* Read/Write Bit in Device    */
+                                        /*  Address field              */
+                                        /*  -> Write = 0               */
+                                        /*  -> Read  = 1               */
+#define	ALI1535_SMBIO_EN	0x04	/* SMB I/O Space enable        */
+
+static int ali1535_transaction(void);
+
+static unsigned short ali1535_smba = 0;
+DECLARE_MUTEX(i2c_ali1535_sem);
+
+
+/* Detect whether a ALI1535 can be found, and initialize it, where necessary.
+   Note the differences between kernels with the old PCI BIOS interface and
+   newer kernels with the real PCI interface. In compat.h some things are
+   defined to make the transition easier. */
+int ali1535_setup(struct pci_dev *ALI1535_dev)
+{
+	int error_return = 0;
+	unsigned char temp;
+
+/* Check the following things:
+	- SMB I/O address is initialized
+	- Device is enabled
+	- We can use the addresses
+*/
+
+/* Determine the address of the SMBus area */
+	pci_read_config_word(ALI1535_dev, SMBBA, &ali1535_smba);
+	ali1535_smba &= (0xffff & ~(ALI1535_SMB_IOSIZE - 1));
+	if (ali1535_smba == 0) {
+		printk
+		    ("i2c-ali1535.o: ALI1535_smb region uninitialized - upgrade BIOS?\n");
+		error_return = -ENODEV;
+	}
+
+	if (error_return == -ENODEV)
+		goto END;
+
+	if (check_region(ali1535_smba, ALI1535_SMB_IOSIZE)) {
+		printk
+		    ("i2c-ali1535.o: ALI1535_smb region 0x%x already in use!\n",
+		     ali1535_smba);
+		error_return = -ENODEV;
+	}
+
+	if (error_return == -ENODEV)
+		goto END;
+
+	/* check if whole device is enabled */
+	pci_read_config_byte(ALI1535_dev, SMBCFG, &temp);
+	if ((temp & ALI1535_SMBIO_EN) == 0) {
+		printk
+		    ("i2c-ali1535.o: SMB device not enabled - upgrade BIOS?\n");
+		error_return = -ENODEV;
+		goto END;
+	}
+
+/* Is SMB Host controller enabled? */
+	pci_read_config_byte(ALI1535_dev, SMBHSTCFG, &temp);
+	if ((temp & 1) == 0) {
+		printk
+		    ("i2c-ali1535.o: SMBus controller not enabled - upgrade BIOS?\n");
+		error_return = -ENODEV;
+		goto END;
+	}
+
+/* set SMB clock to 74KHz as recommended in data sheet */
+	pci_write_config_byte(ALI1535_dev, SMBCLK, 0x20);
+
+	/* Everything is happy, let's grab the memory and set things up. */
+	request_region(ali1535_smba, ALI1535_SMB_IOSIZE, "ali1535-smb");
+
+#ifdef DEBUG
+/*
+  The interrupt routing for SMB is set up in register 0x77 in the
+  1533 ISA Bridge device, NOT in the 7101 device.
+  Don't bother with finding the 1533 device and reading the register.
+  if ((....... & 0x0F) == 1)
+     printk("i2c-ali1535.o: ALI1535 using Interrupt 9 for SMBus.\n");
+*/
+	pci_read_config_byte(ALI1535_dev, SMBREV, &temp);
+	printk("i2c-ali1535.o: SMBREV = 0x%X\n", temp);
+	printk("i2c-ali1535.o: ALI1535_smba = 0x%X\n", ali1535_smba);
+#endif				/* DEBUG */
+
+      END:
+	return error_return;
+}
+
+
+/* Another internally used function */
+int ali1535_transaction(void)
+{
+	int temp;
+	int result = 0;
+	int timeout = 0;
+
+#ifdef DEBUG
+	printk
+	    ("i2c-ali1535.o: Transaction (pre): STS=%02x, TYP=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, "
+	     "DAT1=%02x\n", inb_p(SMBHSTSTS), inb_p(SMBHSTTYP),
+	     inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
+	     inb_p(SMBHSTDAT1));
+#endif
+
+	/* get status */
+	temp = inb_p(SMBHSTSTS);
+
+	/* Make sure the SMBus host is ready to start transmitting */
+	/* Check the busy bit first */
+	if (temp & ALI1535_STS_BUSY) {
+/*
+   If the host controller is still busy, it may have timed out in the previous transaction,
+   resulting in a "SMBus Timeout" printk.
+   I've tried the following to reset a stuck busy bit.
+	1. Reset the controller with an KILL command.
+	   (this doesn't seem to clear the controller if an external device is hung)
+	2. Reset the controller and the other SMBus devices with a T_OUT command.
+	   (this clears the host busy bit if an external device is hung,
+	   but it comes back upon a new access to a device)
+	3. Disable and reenable the controller in SMBHSTCFG
+   Worst case, nothing seems to work except power reset.
+*/
+/* Abort - reset the host controller */
+/*
+#ifdef DEBUG
+    printk("i2c-ali1535.o: Resetting host controller to clear busy condition\n",temp);
+#endif
+    outb_p(ALI1535_KILL, SMBHSTTYP);
+    temp = inb_p(SMBHSTSTS);
+    if (temp & ALI1535_STS_BUSY) {
+*/
+
+/*
+   Try resetting entire SMB bus, including other devices -
+   This may not work either - it clears the BUSY bit but
+   then the BUSY bit may come back on when you try and use the chip again.
+   If that's the case you are stuck.
+*/
+		printk
+		    ("i2c-ali1535.o: Resetting entire SMB Bus to clear busy condition (%02x)\n",
+		     temp);
+		outb_p(ALI1535_T_OUT, SMBHSTTYP);
+		temp = inb_p(SMBHSTSTS);
+	}
+/*
+  }
+*/
+
+	/* now check the error bits and the busy bit */
+	if (temp & (ALI1535_STS_ERR | ALI1535_STS_BUSY)) {
+		/* do a clear-on-write */
+		outb_p(0xFF, SMBHSTSTS);
+		if ((temp = inb_p(SMBHSTSTS)) &
+		    (ALI1535_STS_ERR | ALI1535_STS_BUSY)) {
+			/* this is probably going to be correctable only by a power reset
+			   as one of the bits now appears to be stuck */
+			/* This may be a bus or device with electrical problems. */
+			printk
+			    ("i2c-ali1535.o: SMBus reset failed! (0x%02x) - controller or device on bus is probably hung\n",
+			     temp);
+			return -1;
+		}
+	} else {
+		/* check and clear done bit */
+		if (temp & ALI1535_STS_DONE) {
+			outb_p(temp, SMBHSTSTS);
+		}
+	}
+
+	/* start the transaction by writing anything to the start register */
+	outb_p(0xFF, SMBHSTPORT);
+
+	/* We will always wait for a fraction of a second! */
+	timeout = 0;
+	do {
+		i2c_delay(1);
+		temp = inb_p(SMBHSTSTS);
+	} while (((temp & ALI1535_STS_BUSY) && !(temp & ALI1535_STS_IDLE))
+		 && (timeout++ < MAX_TIMEOUT));
+
+	/* If the SMBus is still busy, we give up */
+	if (timeout >= MAX_TIMEOUT) {
+		result = -1;
+		printk("i2c-ali1535.o: SMBus Timeout!\n");
+	}
+
+	if (temp & ALI1535_STS_FAIL) {
+		result = -1;
+#ifdef DEBUG
+		printk("i2c-ali1535.o: Error: Failed bus transaction\n");
+#endif
+	}
+
+/*
+  Unfortunately the ALI SMB controller maps "no response" and "bus collision"
+  into a single bit. No reponse is the usual case so don't do a printk.
+  This means that bus collisions go unreported.
+*/
+	if (temp & ALI1535_STS_BUSERR) {
+		result = -1;
+#ifdef DEBUG
+		printk
+		    ("i2c-ali1535.o: Error: no response or bus collision ADD=%02x\n",
+		     inb_p(SMBHSTADD));
+#endif
+	}
+
+/* haven't ever seen this */
+	if (temp & ALI1535_STS_DEV) {
+		result = -1;
+		printk("i2c-ali1535.o: Error: device error\n");
+	}
+
+/* 
+   check to see if the "command complete" indication is set
+ */
+	if (!(temp & ALI1535_STS_DONE)) {
+		result = -1;
+		printk("i2c-ali1535.o: Error: command never completed\n");
+	}
+#ifdef DEBUG
+	printk
+	    ("i2c-ali1535.o: Transaction (post): STS=%02x, TYP=%02x, CMD=%02x, ADD=%02x, "
+	     "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS), inb_p(SMBHSTTYP),
+	     inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
+	     inb_p(SMBHSTDAT1));
+#endif
+
+/* 
+    take consequent actions for error conditions
+ */
+        if (!(temp & ALI1535_STS_DONE)) {
+	  /* issue "kill" to reset host controller */
+	  outb_p(ALI1535_KILL,SMBHSTTYP);
+	  outb_p(0xFF,SMBHSTSTS);
+	}	  
+	else if (temp & ALI1535_STS_ERR) {
+	  /* issue "timeout" to reset all devices on bus */
+	  outb_p(ALI1535_T_OUT,SMBHSTTYP);
+	  outb_p(0xFF,SMBHSTSTS);
+	}
+        
+	return result;
+}
+
+/* Return -1 on error. */
+s32 ali1535_access(struct i2c_adapter * adap, u16 addr,
+		   unsigned short flags, char read_write, u8 command,
+		   int size, union i2c_smbus_data * data)
+{
+	int i, len;
+	int temp;
+	int timeout;
+	s32 result = 0;
+
+	down(&i2c_ali1535_sem);
+/* make sure SMBus is idle */
+	temp = inb_p(SMBHSTSTS);
+	for (timeout = 0;
+	     (timeout < MAX_TIMEOUT) && !(temp & ALI1535_STS_IDLE);
+	     timeout++) {
+		i2c_delay(1);
+		temp = inb_p(SMBHSTSTS);
+	}
+	if (timeout >= MAX_TIMEOUT) {
+		printk("i2c-ali1535.o: Idle wait Timeout! STS=0x%02x\n",
+		       temp);
+	}
+
+/* clear status register (clear-on-write) */
+	outb_p(0xFF, SMBHSTSTS);
+
+	switch (size) {
+	case I2C_SMBUS_QUICK:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		size = ALI1535_QUICK;
+                outb_p(size, SMBHSTTYP);	/* output command */
+                break;
+	case I2C_SMBUS_BYTE:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		size = ALI1535_BYTE;
+                outb_p(size, SMBHSTTYP);	/* output command */
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(command, SMBHSTCMD);
+		break;
+	case I2C_SMBUS_BYTE_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		size = ALI1535_BYTE_DATA;
+                outb_p(size, SMBHSTTYP);	/* output command */
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(data->byte, SMBHSTDAT0);
+		break;
+	case I2C_SMBUS_WORD_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		size = ALI1535_WORD_DATA;
+                outb_p(size, SMBHSTTYP);	/* output command */
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE) {
+			outb_p(data->word & 0xff, SMBHSTDAT0);
+			outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
+		}
+		break;
+	case I2C_SMBUS_BLOCK_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		size = ALI1535_BLOCK_DATA;
+                outb_p(size, SMBHSTTYP);	/* output command */
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE) {
+			len = data->block[0];
+			if (len < 0) {
+				len = 0;
+				data->block[0] = len;
+			}
+			if (len > 32) {
+				len = 32;
+				data->block[0] = len;
+			}
+			outb_p(len, SMBHSTDAT0);
+			outb_p(inb_p(SMBHSTTYP) | ALI1535_BLOCK_CLR, SMBHSTTYP);	/* Reset SMBBLKDAT */
+			for (i = 1; i <= len; i++)
+				outb_p(data->block[i], SMBBLKDAT);
+		}
+		break;
+	default:
+		printk
+		    (KERN_WARNING "i2c-ali1535.o: Unsupported transaction %d\n", size);
+		result = -1;
+		goto EXIT;
+	}
+
+	if (ali1535_transaction())	/* Error in transaction */
+	  {
+		result = -1;
+		goto EXIT;
+          }
+
+	if ((read_write == I2C_SMBUS_WRITE) || (size == ALI1535_QUICK))
+	  {
+		result = 0;
+		goto EXIT;
+          }
+
+	switch (size) {
+	case ALI1535_BYTE:	/* Result put in SMBHSTDAT0 */
+		data->byte = inb_p(SMBHSTDAT0);
+		break;
+	case ALI1535_BYTE_DATA:
+		data->byte = inb_p(SMBHSTDAT0);
+		break;
+	case ALI1535_WORD_DATA:
+		data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
+		break;
+	case ALI1535_BLOCK_DATA:
+		len = inb_p(SMBHSTDAT0);
+		if (len > 32)
+			len = 32;
+		data->block[0] = len;
+		outb_p(inb_p(SMBHSTTYP) | ALI1535_BLOCK_CLR, SMBHSTTYP);	/* Reset SMBBLKDAT */
+		for (i = 1; i <= data->block[0]; i++) {
+			data->block[i] = inb_p(SMBBLKDAT);
+#ifdef DEBUG
+			printk
+			    ("i2c-ali1535.o: Blk: len=%d, i=%d, data=%02x\n",
+			     len, i, data->block[i]);
+#endif	/* DEBUG */
+		}
+		break;
+	}
+EXIT:
+	up(&i2c_ali1535_sem);
+	return result;
+}
+
+
+u32 ali1535_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	    I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	    I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static struct i2c_algorithm smbus_algorithm = {
+	.name		= "Non-i2c SMBus adapter",
+	.id		= I2C_ALGO_SMBUS,
+	.smbus_xfer	= ali1535_access,
+	.functionality	= ali1535_func,
+};
+
+static struct i2c_adapter ali1535_adapter = {
+	.owner		= THIS_MODULE,
+	.name		= "unset",
+	.id		= I2C_ALGO_SMBUS | I2C_HW_SMBUS_ALI1535,
+	.algo		= &smbus_algorithm,
+};
+
+
+static struct pci_device_id ali1535_ids[] __devinitdata = {
+	{
+	.vendor =	PCI_VENDOR_ID_AL,
+	.device =	PCI_DEVICE_ID_AL_M7101,
+	.subvendor =	PCI_ANY_ID,
+	.subdevice =	PCI_ANY_ID,
+	},
+	{ 0, }
+};
+
+static int __devinit ali1535_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	if (ali1535_setup(dev)) {
+		printk
+		    ("i2c-ali1535.o: ALI1535 not detected, module not inserted.\n");
+		return -ENODEV;
+	}
+
+	sprintf(ali1535_adapter.name, "SMBus ALI1535 adapter at %04x",
+		ali1535_smba);
+	return i2c_add_adapter(&ali1535_adapter);
+}
+
+static void __devexit ali1535_remove(struct pci_dev *dev)
+{
+	i2c_del_adapter(&ali1535_adapter);
+	release_region(ali1535_smba, ALI1535_SMB_IOSIZE);
+}
+
+
+static struct pci_driver ali1535_driver = {
+	.name		= "ali1535 smbus",
+	.id_table	= ali1535_ids,
+	.probe		= ali1535_probe,
+	.remove		= __devexit_p(ali1535_remove),
+};
+
+static int __init i2c_ali1535_init(void)
+{
+	printk("i2c-ali1535.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return pci_module_init(&ali1535_driver);
+}
+
+
+static void __exit i2c_ali1535_exit(void)
+{
+	pci_unregister_driver(&ali1535_driver);
+}
+
+#ifdef RLX
+EXPORT_SYMBOL(ali1535_smba);
+EXPORT_SYMBOL(ali1535_access);
+EXPORT_SYMBOL(i2c_ali1535_sem);
+#endif
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, "
+     "Mark D. Studebaker <mdsxyz123@yahoo.com> and Dan Eaton <dan.eaton@rocketlogix.com>");
+MODULE_DESCRIPTION("ALI1535 SMBus driver");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_ali1535_init);
+module_exit(i2c_ali1535_exit);
+
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-ali15x3.c linux-2.4.27-leo/drivers/i2c/i2c-ali15x3.c
--- linux-2.4.27/drivers/i2c/i2c-ali15x3.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-ali15x3.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,533 @@
+/*
+    ali15x3.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+    Copyright (c) 1999  Frodo Looijaard <frodol@dds.nl> and
+    Philip Edelbrock <phil@netroedge.com> and
+    Mark D. Studebaker <mdsxyz123@yahoo.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+    This is the driver for the SMB Host controller on
+    Acer Labs Inc. (ALI) M1541 and M1543C South Bridges.
+
+    The M1543C is a South bridge for desktop systems.
+    The M1533 is a South bridge for portable systems.
+    They are part of the following ALI chipsets:
+       "Aladdin Pro 2": Includes the M1621 Slot 1 North bridge
+       with AGP and 100MHz CPU Front Side bus
+       "Aladdin V": Includes the M1541 Socket 7 North bridge
+       with AGP and 100MHz CPU Front Side bus
+       "Aladdin IV": Includes the M1541 Socket 7 North bridge
+       with host bus up to 83.3 MHz.
+    For an overview of these chips see http://www.acerlabs.com
+
+    The M1533/M1543C devices appear as FOUR separate devices
+    on the PCI bus. An output of lspci will show something similar
+    to the following:
+
+	00:02.0 USB Controller: Acer Laboratories Inc. M5237
+	00:03.0 Bridge: Acer Laboratories Inc. M7101
+	00:07.0 ISA bridge: Acer Laboratories Inc. M1533
+	00:0f.0 IDE interface: Acer Laboratories Inc. M5229
+
+    The SMB controller is part of the 7101 device, which is an
+    ACPI-compliant Power Management Unit (PMU).
+
+    The whole 7101 device has to be enabled for the SMB to work.
+    You can't just enable the SMB alone.
+    The SMB and the ACPI have separate I/O spaces.
+    We make sure that the SMB is enabled. We leave the ACPI alone.
+
+    This driver controls the SMB Host only.
+    The SMB Slave controller on the M15X3 is not enabled.
+
+    This driver does not use interrupts.
+*/
+
+/* Note: we assume there can only be one ALI15X3, with one SMBus interface */
+
+/* #define DEBUG 1 */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+#include <linux/sensors_compat.h>
+
+/* ALI15X3 SMBus address offsets */
+#define SMBHSTSTS	(0 + ali15x3_smba)
+#define SMBHSTCNT	(1 + ali15x3_smba)
+#define SMBHSTSTART	(2 + ali15x3_smba)
+#define SMBHSTCMD	(7 + ali15x3_smba)
+#define SMBHSTADD	(3 + ali15x3_smba)
+#define SMBHSTDAT0	(4 + ali15x3_smba)
+#define SMBHSTDAT1	(5 + ali15x3_smba)
+#define SMBBLKDAT	(6 + ali15x3_smba)
+
+/* PCI Address Constants */
+#define SMBCOM		0x004
+#define SMBBA		0x014
+#define SMBATPC		0x05B	/* used to unlock xxxBA registers */
+#define SMBHSTCFG	0x0E0
+#define SMBSLVC		0x0E1
+#define SMBCLK		0x0E2
+#define SMBREV		0x008
+
+/* Other settings */
+#define MAX_TIMEOUT		200	/* times 1/100 sec */
+#define ALI15X3_SMB_IOSIZE	32
+
+/* this is what the Award 1004 BIOS sets them to on a ASUS P5A MB.
+   We don't use these here. If the bases aren't set to some value we
+   tell user to upgrade BIOS and we fail.
+*/
+#define ALI15X3_SMB_DEFAULTBASE	0xE800
+
+/* ALI15X3 address lock bits */
+#define ALI15X3_LOCK		0x06
+
+/* ALI15X3 command constants */
+#define ALI15X3_ABORT		0x02
+#define ALI15X3_T_OUT		0x04
+#define ALI15X3_QUICK		0x00
+#define ALI15X3_BYTE		0x10
+#define ALI15X3_BYTE_DATA	0x20
+#define ALI15X3_WORD_DATA	0x30
+#define ALI15X3_BLOCK_DATA	0x40
+#define ALI15X3_BLOCK_CLR	0x80
+
+/* ALI15X3 status register bits */
+#define ALI15X3_STS_IDLE	0x04
+#define ALI15X3_STS_BUSY	0x08
+#define ALI15X3_STS_DONE	0x10
+#define ALI15X3_STS_DEV		0x20	/* device error */
+#define ALI15X3_STS_COLL	0x40	/* collision or no response */
+#define ALI15X3_STS_TERM	0x80	/* terminated by abort */
+#define ALI15X3_STS_ERR		0xE0	/* all the bad error bits */
+
+
+/* If force_addr is set to anything different from 0, we forcibly enable
+   the device at the given address. */
+static int force_addr = 0;
+MODULE_PARM(force_addr, "i");
+MODULE_PARM_DESC(force_addr,
+		 "Initialize the base address of the i2c controller");
+
+static unsigned short ali15x3_smba = 0;
+
+static int ali15x3_setup(struct pci_dev *ALI15X3_dev)
+{
+	u16 a;
+	unsigned char temp;
+
+	/* Check the following things:
+		- SMB I/O address is initialized
+		- Device is enabled
+		- We can use the addresses
+	*/
+
+	/* Unlock the register.
+	   The data sheet says that the address registers are read-only
+	   if the lock bits are 1, but in fact the address registers
+	   are zero unless you clear the lock bits.
+	*/
+	pci_read_config_byte(ALI15X3_dev, SMBATPC, &temp);
+	if (temp & ALI15X3_LOCK) {
+		temp &= ~ALI15X3_LOCK;
+		pci_write_config_byte(ALI15X3_dev, SMBATPC, temp);
+	}
+
+	/* Determine the address of the SMBus area */
+	pci_read_config_word(ALI15X3_dev, SMBBA, &ali15x3_smba);
+	ali15x3_smba &= (0xffff & ~(ALI15X3_SMB_IOSIZE - 1));
+	if (ali15x3_smba == 0 && force_addr == 0) {
+		dev_err(ALI15X3_dev, "ALI15X3_smb region uninitialized "
+			"- upgrade BIOS or use force_addr=0xaddr\n");
+		return -ENODEV;
+	}
+
+	if(force_addr)
+		ali15x3_smba = force_addr & ~(ALI15X3_SMB_IOSIZE - 1);
+
+	if (!request_region(ali15x3_smba, ALI15X3_SMB_IOSIZE, "ali15x3-smb")) {
+		dev_err(ALI15X3_dev,
+			"ALI15X3_smb region 0x%x already in use!\n",
+			ali15x3_smba);
+		return -ENODEV;
+	}
+
+	if(force_addr) {
+		dev_info(ALI15X3_dev, "forcing ISA address 0x%04X\n",
+			ali15x3_smba);
+		if (PCIBIOS_SUCCESSFUL !=
+		    pci_write_config_word(ALI15X3_dev, SMBBA, ali15x3_smba))
+			return -ENODEV;
+		if (PCIBIOS_SUCCESSFUL !=
+		    pci_read_config_word(ALI15X3_dev, SMBBA, &a))
+			return -ENODEV;
+		if ((a & ~(ALI15X3_SMB_IOSIZE - 1)) != ali15x3_smba) {
+			/* make sure it works */
+			dev_err(ALI15X3_dev,
+				"force address failed - not supported?\n");
+			return -ENODEV;
+		}
+	}
+	/* check if whole device is enabled */
+	pci_read_config_byte(ALI15X3_dev, SMBCOM, &temp);
+	if ((temp & 1) == 0) {
+		dev_info(ALI15X3_dev, "enabling SMBus device\n");
+		pci_write_config_byte(ALI15X3_dev, SMBCOM, temp | 0x01);
+	}
+
+	/* Is SMB Host controller enabled? */
+	pci_read_config_byte(ALI15X3_dev, SMBHSTCFG, &temp);
+	if ((temp & 1) == 0) {
+		dev_info(ALI15X3_dev, "enabling SMBus controller\n");
+		pci_write_config_byte(ALI15X3_dev, SMBHSTCFG, temp | 0x01);
+	}
+
+	/* set SMB clock to 74KHz as recommended in data sheet */
+	pci_write_config_byte(ALI15X3_dev, SMBCLK, 0x20);
+
+	/*
+	  The interrupt routing for SMB is set up in register 0x77 in the
+	  1533 ISA Bridge device, NOT in the 7101 device.
+	  Don't bother with finding the 1533 device and reading the register.
+	if ((....... & 0x0F) == 1)
+		dev_dbg(ALI15X3_dev, "ALI15X3 using Interrupt 9 for SMBus.\n");
+	*/
+	pci_read_config_byte(ALI15X3_dev, SMBREV, &temp);
+	dev_dbg(ALI15X3_dev, "SMBREV = 0x%X\n", temp);
+	dev_dbg(ALI15X3_dev, "iALI15X3_smba = 0x%X\n", ali15x3_smba);
+
+	return 0;
+}
+
+/* Another internally used function */
+static int ali15x3_transaction(struct i2c_adapter *adap)
+{
+	int temp;
+	int result = 0;
+	int timeout = 0;
+
+	dev_dbg(adap, "Transaction (pre): STS=%02x, CNT=%02x, CMD=%02x, "
+		"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS),
+		inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
+		inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1));
+
+	/* get status */
+	temp = inb_p(SMBHSTSTS);
+
+	/* Make sure the SMBus host is ready to start transmitting */
+	/* Check the busy bit first */
+	if (temp & ALI15X3_STS_BUSY) {
+	/*
+	   If the host controller is still busy, it may have timed out in the
+	   previous transaction, resulting in a "SMBus Timeout" Dev.
+	   I've tried the following to reset a stuck busy bit.
+		1. Reset the controller with an ABORT command.
+		   (this doesn't seem to clear the controller if an external
+		   device is hung)
+		2. Reset the controller and the other SMBus devices with a
+		   T_OUT command.  (this clears the host busy bit if an
+		   external device is hung, but it comes back upon a new access
+		   to a device)
+		3. Disable and reenable the controller in SMBHSTCFG
+	   Worst case, nothing seems to work except power reset.
+	*/
+	/* Abort - reset the host controller */
+	/*
+	   Try resetting entire SMB bus, including other devices -
+	   This may not work either - it clears the BUSY bit but
+	   then the BUSY bit may come back on when you try and use the chip again.
+	   If that's the case you are stuck.
+	*/
+		dev_info(adap, "Resetting entire SMB Bus to "
+			"clear busy condition (%02x)\n", temp);
+		outb_p(ALI15X3_T_OUT, SMBHSTCNT);
+		temp = inb_p(SMBHSTSTS);
+	}
+
+	/* now check the error bits and the busy bit */
+	if (temp & (ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) {
+		/* do a clear-on-write */
+		outb_p(0xFF, SMBHSTSTS);
+		if ((temp = inb_p(SMBHSTSTS)) &
+		    (ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) {
+			/* this is probably going to be correctable only by a power reset
+			   as one of the bits now appears to be stuck */
+			/* This may be a bus or device with electrical problems. */
+			dev_err(adap, "SMBus reset failed! (0x%02x) - "
+				"controller or device on bus is probably hung\n",
+				temp);
+			return -1;
+		}
+	} else {
+		/* check and clear done bit */
+		if (temp & ALI15X3_STS_DONE) {
+			outb_p(temp, SMBHSTSTS);
+		}
+	}
+
+	/* start the transaction by writing anything to the start register */
+	outb_p(0xFF, SMBHSTSTART);
+
+	/* We will always wait for a fraction of a second! */
+	timeout = 0;
+	do {
+		i2c_delay(1);
+		temp = inb_p(SMBHSTSTS);
+	} while ((!(temp & (ALI15X3_STS_ERR | ALI15X3_STS_DONE)))
+		 && (timeout++ < MAX_TIMEOUT));
+
+	/* If the SMBus is still busy, we give up */
+	if (timeout >= MAX_TIMEOUT) {
+		result = -1;
+		dev_err(adap, "SMBus Timeout!\n");
+	}
+
+	if (temp & ALI15X3_STS_TERM) {
+		result = -1;
+		dev_dbg(adap, "Error: Failed bus transaction\n");
+	}
+
+	/*
+	  Unfortunately the ALI SMB controller maps "no response" and "bus
+	  collision" into a single bit. No reponse is the usual case so don't
+	  do a printk.
+	  This means that bus collisions go unreported.
+	*/
+	if (temp & ALI15X3_STS_COLL) {
+		result = -1;
+		dev_dbg(adap,
+			"Error: no response or bus collision ADD=%02x\n",
+			inb_p(SMBHSTADD));
+	}
+
+	/* haven't ever seen this */
+	if (temp & ALI15X3_STS_DEV) {
+		result = -1;
+		dev_err(adap, "Error: device error\n");
+	}
+	dev_dbg(adap, "Transaction (post): STS=%02x, CNT=%02x, CMD=%02x, "
+		"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS),
+		inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
+		inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1));
+	return result;
+}
+
+/* Return -1 on error. */
+static s32 ali15x3_access(struct i2c_adapter * adap, u16 addr,
+		   unsigned short flags, char read_write, u8 command,
+		   int size, union i2c_smbus_data * data)
+{
+	int i, len;
+	int temp;
+	int timeout;
+
+	/* clear all the bits (clear-on-write) */
+	outb_p(0xFF, SMBHSTSTS);
+	/* make sure SMBus is idle */
+	temp = inb_p(SMBHSTSTS);
+	for (timeout = 0;
+	     (timeout < MAX_TIMEOUT) && !(temp & ALI15X3_STS_IDLE);
+	     timeout++) {
+		i2c_delay(1);
+		temp = inb_p(SMBHSTSTS);
+	}
+	if (timeout >= MAX_TIMEOUT) {
+		dev_err(adap, "Idle wait Timeout! STS=0x%02x\n", temp);
+	}
+
+	switch (size) {
+	case I2C_SMBUS_QUICK:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		size = ALI15X3_QUICK;
+		break;
+	case I2C_SMBUS_BYTE:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(command, SMBHSTCMD);
+		size = ALI15X3_BYTE;
+		break;
+	case I2C_SMBUS_BYTE_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(data->byte, SMBHSTDAT0);
+		size = ALI15X3_BYTE_DATA;
+		break;
+	case I2C_SMBUS_WORD_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE) {
+			outb_p(data->word & 0xff, SMBHSTDAT0);
+			outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
+		}
+		size = ALI15X3_WORD_DATA;
+		break;
+	case I2C_SMBUS_BLOCK_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE) {
+			len = data->block[0];
+			if (len < 0) {
+				len = 0;
+				data->block[0] = len;
+			}
+			if (len > 32) {
+				len = 32;
+				data->block[0] = len;
+			}
+			outb_p(len, SMBHSTDAT0);
+			/* Reset SMBBLKDAT */
+			outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT);
+			for (i = 1; i <= len; i++)
+				outb_p(data->block[i], SMBBLKDAT);
+		}
+		size = ALI15X3_BLOCK_DATA;
+		break;
+	default:
+		printk
+		    (KERN_WARNING "i2c-ali15x3.o: Unsupported transaction %d\n", size);
+		return -1;
+	}
+
+	outb_p(size, SMBHSTCNT);	/* output command */
+
+	if (ali15x3_transaction(adap))	/* Error in transaction */
+		return -1;
+
+	if ((read_write == I2C_SMBUS_WRITE) || (size == ALI15X3_QUICK))
+		return 0;
+
+
+	switch (size) {
+	case ALI15X3_BYTE:	/* Result put in SMBHSTDAT0 */
+		data->byte = inb_p(SMBHSTDAT0);
+		break;
+	case ALI15X3_BYTE_DATA:
+		data->byte = inb_p(SMBHSTDAT0);
+		break;
+	case ALI15X3_WORD_DATA:
+		data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
+		break;
+	case ALI15X3_BLOCK_DATA:
+		len = inb_p(SMBHSTDAT0);
+		if (len > 32)
+			len = 32;
+		data->block[0] = len;
+		/* Reset SMBBLKDAT */
+		outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT);
+		for (i = 1; i <= data->block[0]; i++) {
+			data->block[i] = inb_p(SMBBLKDAT);
+			dev_dbg(adap, "Blk: len=%d, i=%d, data=%02x\n",
+				len, i, data->block[i]);
+		}
+		break;
+	}
+	return 0;
+}
+
+static u32 ali15x3_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	    I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	    I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static struct i2c_algorithm smbus_algorithm = {
+	.name		= "Non-I2C SMBus adapter",
+	.id		= I2C_ALGO_SMBUS,
+	.smbus_xfer	= ali15x3_access,
+	.functionality	= ali15x3_func,
+};
+
+static struct i2c_adapter ali15x3_adapter = {
+	.owner		= THIS_MODULE,
+	.id		= I2C_ALGO_SMBUS | I2C_HW_SMBUS_ALI15X3,
+	.algo		= &smbus_algorithm,
+	.name	= "unset",
+};
+
+static struct pci_device_id ali15x3_ids[] __devinitdata = {
+	{
+	.vendor =	PCI_VENDOR_ID_AL,
+	.device =	PCI_DEVICE_ID_AL_M7101,
+	.subvendor =	PCI_ANY_ID,
+	.subdevice =	PCI_ANY_ID,
+	},
+	{ 0, }
+};
+
+static int __devinit ali15x3_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	if (ali15x3_setup(dev)) {
+		dev_err(dev,
+			"ALI15X3 not detected, module not inserted.\n");
+		return -ENODEV;
+	}
+
+	snprintf(ali15x3_adapter.name, 32,
+		"SMBus ALI15X3 adapter at %04x", ali15x3_smba);
+	return i2c_add_adapter(&ali15x3_adapter);
+}
+
+static void __devexit ali15x3_remove(struct pci_dev *dev)
+{
+	i2c_del_adapter(&ali15x3_adapter);
+	release_region(ali15x3_smba, ALI15X3_SMB_IOSIZE);
+}
+
+static struct pci_driver ali15x3_driver = {
+	.name		= "ali15x3 smbus",
+	.id_table	= ali15x3_ids,
+	.probe		= ali15x3_probe,
+	.remove		= __devexit_p(ali15x3_remove),
+};
+
+static int __init i2c_ali15x3_init(void)
+{
+	printk("i2c-ali15x3.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return pci_module_init(&ali15x3_driver);
+}
+
+static void __exit i2c_ali15x3_exit(void)
+{
+	pci_unregister_driver(&ali15x3_driver);
+}
+
+MODULE_AUTHOR ("Frodo Looijaard <frodol@dds.nl>, "
+		"Philip Edelbrock <phil@netroedge.com>, "
+		"and Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("ALI15X3 SMBus driver");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_ali15x3_init);
+module_exit(i2c_ali15x3_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-amd756.c linux-2.4.27-leo/drivers/i2c/i2c-amd756.c
--- linux-2.4.27/drivers/i2c/i2c-amd756.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-amd756.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,425 @@
+/*
+    amd756.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+
+    Copyright (c) 1999-2002 Merlin Hughes <merlin@merlin.org>
+
+    Shamelessly ripped from i2c-piix4.c:
+
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl> and
+    Philip Edelbrock <phil@netroedge.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+    2002-04-08: Added nForce support. (Csaba Halasz)
+    2002-10-03: Fixed nForce PnP I/O port. (Michael Steil)
+    2002-12-28: Rewritten into something that resembles a Linux driver (hch)
+    2003-11-29: Added back AMD8111 removed by the previous rewrite.
+                (Philip Pokorny)
+    2004-02-15: Don't register driver to avoid driver conflicts.
+                (Daniel Rune Jensen)
+*/
+
+/*
+   Supports AMD756, AMD766, AMD768, AMD8111 and nVidia nForce
+   Note: we assume there can only be one device, with one SMBus interface.
+*/
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+#define DRV_NAME	"i2c-amd756"
+
+/* AMD756 SMBus address offsets */
+#define SMB_ADDR_OFFSET        0xE0
+#define SMB_IOSIZE             16
+#define SMB_GLOBAL_STATUS      (0x0 + amd756_ioport)
+#define SMB_GLOBAL_ENABLE      (0x2 + amd756_ioport)
+#define SMB_HOST_ADDRESS       (0x4 + amd756_ioport)
+#define SMB_HOST_DATA          (0x6 + amd756_ioport)
+#define SMB_HOST_COMMAND       (0x8 + amd756_ioport)
+#define SMB_HOST_BLOCK_DATA    (0x9 + amd756_ioport)
+#define SMB_HAS_DATA           (0xA + amd756_ioport)
+#define SMB_HAS_DEVICE_ADDRESS (0xC + amd756_ioport)
+#define SMB_HAS_HOST_ADDRESS   (0xE + amd756_ioport)
+#define SMB_SNOOP_ADDRESS      (0xF + amd756_ioport)
+
+/* PCI Address Constants */
+
+/* address of I/O space */
+#define SMBBA     0x058		/* mh */
+#define SMBBANFORCE     0x014
+
+/* general configuration */
+#define SMBGCFG   0x041		/* mh */
+
+/* silicon revision code */
+#define SMBREV    0x008
+
+/* Other settings */
+#define MAX_TIMEOUT 500
+
+/* AMD756 constants */
+#define AMD756_QUICK        0x00
+#define AMD756_BYTE         0x01
+#define AMD756_BYTE_DATA    0x02
+#define AMD756_WORD_DATA    0x03
+#define AMD756_PROCESS_CALL 0x04
+#define AMD756_BLOCK_DATA   0x05
+
+
+static unsigned short amd756_ioport = 0;
+
+/* 
+  SMBUS event = I/O 28-29 bit 11
+     see E0 for the status bits and enabled in E2
+     
+*/
+
+#define GS_ABRT_STS (1 << 0)
+#define GS_COL_STS (1 << 1)
+#define GS_PRERR_STS (1 << 2)
+#define GS_HST_STS (1 << 3)
+#define GS_HCYC_STS (1 << 4)
+#define GS_TO_STS (1 << 5)
+#define GS_SMB_STS (1 << 11)
+
+#define GS_CLEAR_STS (GS_ABRT_STS | GS_COL_STS | GS_PRERR_STS | \
+  GS_HCYC_STS | GS_TO_STS )
+
+#define GE_CYC_TYPE_MASK (7)
+#define GE_HOST_STC (1 << 3)
+#define GE_ABORT (1 << 5)
+
+
+static int amd756_transaction(void)
+{
+	int temp;
+	int result = 0;
+	int timeout = 0;
+
+	pr_debug(DRV_NAME
+	       ": Transaction (pre): GS=%04x, GE=%04x, ADD=%04x, DAT=%04x\n",
+	       inw_p(SMB_GLOBAL_STATUS), inw_p(SMB_GLOBAL_ENABLE),
+	       inw_p(SMB_HOST_ADDRESS), inb_p(SMB_HOST_DATA));
+
+	/* Make sure the SMBus host is ready to start transmitting */
+	if ((temp = inw_p(SMB_GLOBAL_STATUS)) & (GS_HST_STS | GS_SMB_STS)) {
+		pr_debug(DRV_NAME ": SMBus busy (%04x). Waiting... \n", temp);
+		do {
+			i2c_delay(1);
+			temp = inw_p(SMB_GLOBAL_STATUS);
+		} while ((temp & (GS_HST_STS | GS_SMB_STS)) &&
+		         (timeout++ < MAX_TIMEOUT));
+		/* If the SMBus is still busy, we give up */
+		if (timeout >= MAX_TIMEOUT) {
+			pr_debug(DRV_NAME ": Busy wait timeout (%04x)\n", temp);
+			goto abort;
+		}
+		timeout = 0;
+	}
+
+	/* start the transaction by setting the start bit */
+	outw_p(inw(SMB_GLOBAL_ENABLE) | GE_HOST_STC, SMB_GLOBAL_ENABLE);
+
+	/* We will always wait for a fraction of a second! */
+	do {
+		i2c_delay(1);
+		temp = inw_p(SMB_GLOBAL_STATUS);
+	} while ((temp & GS_HST_STS) && (timeout++ < MAX_TIMEOUT));
+
+	/* If the SMBus is still busy, we give up */
+	if (timeout >= MAX_TIMEOUT) {
+		pr_debug(DRV_NAME ": Completion timeout!\n");
+		goto abort;
+	}
+
+	if (temp & GS_PRERR_STS) {
+		result = -1;
+		pr_debug(DRV_NAME ": SMBus Protocol error (no response)!\n");
+	}
+
+	if (temp & GS_COL_STS) {
+		result = -1;
+		printk(KERN_WARNING DRV_NAME ": SMBus collision!\n");
+	}
+
+	if (temp & GS_TO_STS) {
+		result = -1;
+		pr_debug(DRV_NAME ": SMBus protocol timeout!\n");
+	}
+
+	if (temp & GS_HCYC_STS)
+		pr_debug(DRV_NAME ": SMBus protocol success!\n");
+
+	outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS);
+
+#ifdef DEBUG
+	if (((temp = inw_p(SMB_GLOBAL_STATUS)) & GS_CLEAR_STS) != 0x00) {
+		pr_debug(DRV_NAME
+		         ": Failed reset at end of transaction (%04x)\n", temp);
+	}
+
+	pr_debug(DRV_NAME
+		 ": Transaction (post): GS=%04x, GE=%04x, ADD=%04x, DAT=%04x\n",
+		 inw_p(SMB_GLOBAL_STATUS), inw_p(SMB_GLOBAL_ENABLE),
+		 inw_p(SMB_HOST_ADDRESS), inb_p(SMB_HOST_DATA));
+#endif
+
+	return result;
+
+ abort:
+	printk(KERN_WARNING DRV_NAME ": Sending abort.\n");
+	outw_p(inw(SMB_GLOBAL_ENABLE) | GE_ABORT, SMB_GLOBAL_ENABLE);
+	i2c_delay(100);
+	outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS);
+	return -1;
+}
+
+/* Return -1 on error. */
+
+static s32 amd756_access(struct i2c_adapter * adap, u16 addr,
+		  unsigned short flags, char read_write,
+		  u8 command, int size, union i2c_smbus_data * data)
+{
+	int i, len;
+
+	/** TODO: Should I supporte the 10-bit transfers? */
+	switch (size) {
+	/* TODO: proc call is supported, I'm just not sure what to do here... */
+	case I2C_SMBUS_QUICK:
+		outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMB_HOST_ADDRESS);
+		size = AMD756_QUICK;
+		break;
+	case I2C_SMBUS_BYTE:
+		outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMB_HOST_ADDRESS);
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(command, SMB_HOST_DATA);
+		size = AMD756_BYTE;
+		break;
+	case I2C_SMBUS_BYTE_DATA:
+		outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMB_HOST_ADDRESS);
+		outb_p(command, SMB_HOST_COMMAND);
+		if (read_write == I2C_SMBUS_WRITE)
+			outw_p(data->byte, SMB_HOST_DATA);
+		size = AMD756_BYTE_DATA;
+		break;
+	case I2C_SMBUS_WORD_DATA:
+		outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMB_HOST_ADDRESS);
+		outb_p(command, SMB_HOST_COMMAND);
+		if (read_write == I2C_SMBUS_WRITE)
+			outw_p(data->word, SMB_HOST_DATA);	/* TODO: endian???? */
+		size = AMD756_WORD_DATA;
+		break;
+	case I2C_SMBUS_BLOCK_DATA:
+		outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMB_HOST_ADDRESS);
+		outb_p(command, SMB_HOST_COMMAND);
+		if (read_write == I2C_SMBUS_WRITE) {
+			len = data->block[0];
+			if (len < 0)
+				len = 0;
+			if (len > 32)
+				len = 32;
+			outw_p(len, SMB_HOST_DATA);
+			/* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */
+			for (i = 1; i <= len; i++)
+				outb_p(data->block[i],
+				       SMB_HOST_BLOCK_DATA);
+		}
+		size = AMD756_BLOCK_DATA;
+		break;
+	default:
+		printk
+		    (KERN_WARNING "i2c-amd756.o: Unsupported transaction %d\n", size);
+		return -1;
+	}
+
+	/* How about enabling interrupts... */
+	outw_p(size & GE_CYC_TYPE_MASK, SMB_GLOBAL_ENABLE);
+
+	if (amd756_transaction())	/* Error in transaction */
+		return -1;
+
+	if ((read_write == I2C_SMBUS_WRITE) || (size == AMD756_QUICK))
+		return 0;
+
+
+	switch (size) {
+	case AMD756_BYTE:
+		data->byte = inw_p(SMB_HOST_DATA);
+		break;
+	case AMD756_BYTE_DATA:
+		data->byte = inw_p(SMB_HOST_DATA);
+		break;
+	case AMD756_WORD_DATA:
+		data->word = inw_p(SMB_HOST_DATA);	/* TODO: endian???? */
+		break;
+	case AMD756_BLOCK_DATA:
+		data->block[0] = inw_p(SMB_HOST_DATA) & 0x3f;
+		if(data->block[0] > 32)
+			data->block[0] = 32;
+		/* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */
+		for (i = 1; i <= data->block[0]; i++)
+			data->block[i] = inb_p(SMB_HOST_BLOCK_DATA);
+		break;
+	}
+
+	return 0;
+}
+
+static u32 amd756_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	    I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	    I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL;
+}
+
+static struct i2c_algorithm smbus_algorithm = {
+	.name		= "Non-I2C SMBus adapter",
+	.id		= I2C_ALGO_SMBUS,
+	.smbus_xfer	= amd756_access,
+	.functionality	= amd756_func,
+};
+
+static struct i2c_adapter amd756_adapter = {
+	.owner		= THIS_MODULE,
+	.name		= "unset",
+	.id		= I2C_ALGO_SMBUS | I2C_HW_SMBUS_AMD756,
+	.algo		= &smbus_algorithm,
+};
+
+enum chiptype { AMD756, AMD766, AMD768, NFORCE, AMD8111 };
+
+static struct pci_device_id amd756_ids[] __devinitdata = {
+	{PCI_VENDOR_ID_AMD, 0x740B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD756 },
+	{PCI_VENDOR_ID_AMD, 0x7413, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD766 },
+	{PCI_VENDOR_ID_AMD, 0x7443, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD768 },
+	{PCI_VENDOR_ID_AMD, 0x746B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD8111 },
+	{PCI_VENDOR_ID_NVIDIA, 0x01B4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE },
+	{ 0, }
+};
+
+static int __devinit amd756_probe(struct pci_dev *pdev,
+				  const struct pci_device_id *id)
+{
+	int nforce = (id->driver_data == NFORCE);
+	int error;
+	u8 temp;
+	
+	if (amd756_ioport) {
+		printk(KERN_ERR DRV_NAME ": Only one device supported. "
+		       "(you have a strange motherboard, btw..)\n");
+		return -ENODEV;
+	}
+
+	if (nforce) {
+		if (PCI_FUNC(pdev->devfn) != 1)
+			return -ENODEV;
+
+		pci_read_config_word(pdev, SMBBANFORCE, &amd756_ioport);
+		amd756_ioport &= 0xfffc;
+	} else { /* amd */
+		if (PCI_FUNC(pdev->devfn) != 3)
+			return -ENODEV;
+
+		pci_read_config_byte(pdev, SMBGCFG, &temp);
+		if ((temp & 128) == 0) {
+			printk(KERN_ERR DRV_NAME
+			       ": Error: SMBus controller I/O not enabled!\n");
+			return -ENODEV;
+		}
+
+		/* Determine the address of the SMBus areas */
+		/* Technically it is a dword but... */
+		pci_read_config_word(pdev, SMBBA, &amd756_ioport);
+		amd756_ioport &= 0xff00;
+		amd756_ioport += SMB_ADDR_OFFSET;
+	}
+
+	if (!request_region(amd756_ioport, SMB_IOSIZE, "amd756-smbus")) {
+		printk(KERN_ERR DRV_NAME
+		       ": SMB region 0x%x already in use!\n", amd756_ioport);
+		return -ENODEV;
+	}
+
+#ifdef DEBUG
+	pci_read_config_byte(pdev, SMBREV, &temp);
+	printk(KERN_DEBUG DRV_NAME ": SMBREV = 0x%X\n", temp);
+	printk(KERN_DEBUG DRV_NAME ": AMD756_smba = 0x%X\n", amd756_ioport);
+#endif
+
+	sprintf(amd756_adapter.name,
+		"SMBus AMD756 adapter at %04x", amd756_ioport);
+
+	error = i2c_add_adapter(&amd756_adapter);
+	if (error) {
+		printk(KERN_ERR DRV_NAME
+		       ": Adapter registration failed, module not inserted.\n");
+		goto out_err;
+	}
+
+	return 0;
+
+ out_err:
+	release_region(amd756_ioport, SMB_IOSIZE);
+	return error;
+}
+
+
+static int __init i2c_amd756_init(void)
+{
+	struct pci_dev *dev;
+	const struct pci_device_id *id;
+
+	printk(KERN_INFO "i2c-amd756.o version %s (%s)\n", LM_VERSION, LM_DATE);
+
+ 	pci_for_each_dev(dev) {
+		id = pci_match_device(amd756_ids, dev);
+		if (id && amd756_probe(dev, id) >= 0)
+			return 0; 
+	}
+
+	return -ENODEV;
+}
+
+
+static void __exit i2c_amd756_exit(void)
+{
+	i2c_del_adapter(&amd756_adapter);
+	release_region(amd756_ioport, SMB_IOSIZE);
+}
+
+MODULE_AUTHOR("Merlin Hughes <merlin@merlin.org>");
+MODULE_DESCRIPTION("AMD756/766/768/8111 and nVidia nForce SMBus driver");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_amd756_init)
+module_exit(i2c_amd756_exit)
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-amd8111.c linux-2.4.27-leo/drivers/i2c/i2c-amd8111.c
--- linux-2.4.27/drivers/i2c/i2c-amd8111.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-amd8111.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,421 @@
+/*
+ * SMBus 2.0 driver for AMD-8111 IO-Hub.
+ *
+ * Copyright (c) 2002 Vojtech Pavlik
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2.
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+#ifndef I2C_HW_SMBUS_AMD8111
+#error Your i2c is too old - i2c-2.7.0 or greater required!
+#endif
+
+/* kernel 2.4.9 needs this */
+#ifndef min_t
+#define min_t(type,x,y) min(type,x,y)
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR ("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("AMD8111 SMBus 2.0 driver");
+
+struct amd_smbus {
+	struct pci_dev *dev;
+	struct i2c_adapter adapter;
+	int base;
+	int size;
+};
+
+/*
+ * AMD PCI control registers definitions.
+ */
+
+#define AMD_PCI_MISC	0x48
+
+#define AMD_PCI_MISC_SCI	0x04	/* deliver SCI */
+#define AMD_PCI_MISC_INT	0x02	/* deliver PCI IRQ */
+#define AMD_PCI_MISC_SPEEDUP	0x01	/* 16x clock speedup */
+
+/*
+ * ACPI 2.0 chapter 13 PCI interface definitions.
+ */
+
+#define AMD_EC_DATA	0x00	/* data register */
+#define AMD_EC_SC	0x04	/* status of controller */
+#define AMD_EC_CMD	0x04	/* command register */
+#define AMD_EC_ICR	0x08	/* interrupt control register */
+
+#define AMD_EC_SC_SMI	0x04	/* smi event pending */
+#define AMD_EC_SC_SCI	0x02	/* sci event pending */
+#define AMD_EC_SC_BURST	0x01	/* burst mode enabled */
+#define AMD_EC_SC_CMD	0x08	/* byte in data reg is command */
+#define AMD_EC_SC_IBF	0x02	/* data ready for embedded controller */
+#define AMD_EC_SC_OBF	0x01	/* data ready for host */
+
+#define AMD_EC_CMD_RD	0x80	/* read EC */
+#define AMD_EC_CMD_WR	0x81	/* write EC */
+#define AMD_EC_CMD_BE	0x82	/* enable burst mode */
+#define AMD_EC_CMD_BD	0x83	/* disable burst mode */
+#define AMD_EC_CMD_QR	0x84	/* query EC */
+
+/*
+ * ACPI 2.0 chapter 13 access of registers of the EC
+ */
+
+unsigned int amd_ec_wait_write(struct amd_smbus *smbus)
+{
+	int timeout = 500;
+
+	while (timeout-- && (inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_IBF))
+		udelay(1);
+
+	if (!timeout) {
+		printk(KERN_WARNING "i2c-amd8111.c: Timeout while waiting for IBF to clear\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+unsigned int amd_ec_wait_read(struct amd_smbus *smbus)
+{
+	int timeout = 500;
+
+	while (timeout-- && (~inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_OBF))
+		udelay(1);
+
+	if (!timeout) {
+		printk(KERN_WARNING "i2c-amd8111.c: Timeout while waiting for OBF to set\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+unsigned int amd_ec_read(struct amd_smbus *smbus, unsigned char address, unsigned char *data)
+{
+	if (amd_ec_wait_write(smbus))
+		return -1;
+	outb(AMD_EC_CMD_RD, smbus->base + AMD_EC_CMD);
+
+	if (amd_ec_wait_write(smbus))
+		return -1;
+	outb(address, smbus->base + AMD_EC_DATA);
+
+	if (amd_ec_wait_read(smbus))
+		return -1;
+	*data = inb(smbus->base + AMD_EC_DATA);
+
+	return 0;
+}
+
+unsigned int amd_ec_write(struct amd_smbus *smbus, unsigned char address, unsigned char data)
+{
+	if (amd_ec_wait_write(smbus))
+		return -1;
+	outb(AMD_EC_CMD_WR, smbus->base + AMD_EC_CMD);
+
+	if (amd_ec_wait_write(smbus))
+		return -1;
+	outb(address, smbus->base + AMD_EC_DATA);
+
+	if (amd_ec_wait_write(smbus))
+		return -1;
+	outb(data, smbus->base + AMD_EC_DATA);
+
+	return 0;
+}
+
+/*
+ * ACPI 2.0 chapter 13 SMBus 2.0 EC register model
+ */
+
+#define AMD_SMB_PRTCL	0x00	/* protocol, PEC */
+#define AMD_SMB_STS	0x01	/* status */
+#define AMD_SMB_ADDR	0x02	/* address */
+#define AMD_SMB_CMD	0x03	/* command */
+#define AMD_SMB_DATA	0x04	/* 32 data registers */
+#define AMD_SMB_BCNT	0x24	/* number of data bytes */
+#define AMD_SMB_ALRM_A	0x25	/* alarm address */
+#define AMD_SMB_ALRM_D	0x26	/* 2 bytes alarm data */
+
+#define AMD_SMB_STS_DONE	0x80
+#define AMD_SMB_STS_ALRM	0x40
+#define AMD_SMB_STS_RES		0x20
+#define AMD_SMB_STS_STATUS	0x1f
+
+#define AMD_SMB_STATUS_OK	0x00
+#define AMD_SMB_STATUS_FAIL	0x07
+#define AMD_SMB_STATUS_DNAK	0x10
+#define AMD_SMB_STATUS_DERR	0x11
+#define AMD_SMB_STATUS_CMD_DENY	0x12
+#define AMD_SMB_STATUS_UNKNOWN	0x13
+#define AMD_SMB_STATUS_ACC_DENY	0x17
+#define AMD_SMB_STATUS_TIMEOUT	0x18
+#define AMD_SMB_STATUS_NOTSUP	0x19
+#define AMD_SMB_STATUS_BUSY	0x1A
+#define AMD_SMB_STATUS_PEC	0x1F
+
+#define AMD_SMB_PRTCL_WRITE		0x00
+#define AMD_SMB_PRTCL_READ		0x01
+#define AMD_SMB_PRTCL_QUICK		0x02
+#define AMD_SMB_PRTCL_BYTE		0x04
+#define AMD_SMB_PRTCL_BYTE_DATA		0x06
+#define AMD_SMB_PRTCL_WORD_DATA		0x08
+#define AMD_SMB_PRTCL_BLOCK_DATA	0x0a
+#define AMD_SMB_PRTCL_PROC_CALL		0x0c
+#define AMD_SMB_PRTCL_BLOCK_PROC_CALL	0x0d
+#define AMD_SMB_PRTCL_I2C_BLOCK_DATA	0x4a
+#define AMD_SMB_PRTCL_PEC		0x80
+
+
+s32 amd8111_access(struct i2c_adapter * adap, u16 addr, unsigned short flags,
+		char read_write, u8 command, int size, union i2c_smbus_data * data)
+{
+	struct amd_smbus *smbus = adap->algo_data;
+	unsigned char protocol, len, pec, temp[2];
+	int i;
+
+	protocol = (read_write == I2C_SMBUS_READ) ? AMD_SMB_PRTCL_READ : AMD_SMB_PRTCL_WRITE;
+	pec = (flags & I2C_CLIENT_PEC) ? AMD_SMB_PRTCL_PEC : 0;
+
+	switch (size) {
+
+		case I2C_SMBUS_QUICK:
+			protocol |= AMD_SMB_PRTCL_QUICK;
+			read_write = I2C_SMBUS_WRITE;
+			break;
+
+		case I2C_SMBUS_BYTE:
+			if (read_write == I2C_SMBUS_WRITE)
+				amd_ec_write(smbus, AMD_SMB_CMD, command);
+			protocol |= AMD_SMB_PRTCL_BYTE;
+			break;
+
+		case I2C_SMBUS_BYTE_DATA:
+			amd_ec_write(smbus, AMD_SMB_CMD, command);
+			if (read_write == I2C_SMBUS_WRITE)
+				amd_ec_write(smbus, AMD_SMB_DATA, data->byte);
+			protocol |= AMD_SMB_PRTCL_BYTE_DATA;
+			break;
+
+		case I2C_SMBUS_WORD_DATA:
+			amd_ec_write(smbus, AMD_SMB_CMD, command);
+			if (read_write == I2C_SMBUS_WRITE) {
+				amd_ec_write(smbus, AMD_SMB_DATA, data->word);
+				amd_ec_write(smbus, AMD_SMB_DATA + 1, data->word >> 8);
+			}
+			protocol |= AMD_SMB_PRTCL_WORD_DATA | pec;
+			break;
+
+		case I2C_SMBUS_BLOCK_DATA:
+			amd_ec_write(smbus, AMD_SMB_CMD, command);
+			if (read_write == I2C_SMBUS_WRITE) {
+				len = min_t(u8, data->block[0], 32);
+				amd_ec_write(smbus, AMD_SMB_BCNT, len);
+				for (i = 0; i < len; i++)
+					amd_ec_write(smbus, AMD_SMB_DATA + i, data->block[i + 1]);
+			}
+			protocol |= AMD_SMB_PRTCL_BLOCK_DATA | pec;
+			break;
+
+		case I2C_SMBUS_I2C_BLOCK_DATA:
+			len = min_t(u8, data->block[0], 32);
+			amd_ec_write(smbus, AMD_SMB_CMD, command);
+			amd_ec_write(smbus, AMD_SMB_BCNT, len);
+			if (read_write == I2C_SMBUS_WRITE)
+				for (i = 0; i < len; i++)
+					amd_ec_write(smbus, AMD_SMB_DATA + i, data->block[i + 1]);
+			protocol |= AMD_SMB_PRTCL_I2C_BLOCK_DATA;
+			break;
+
+		case I2C_SMBUS_PROC_CALL:
+			amd_ec_write(smbus, AMD_SMB_CMD, command);
+			amd_ec_write(smbus, AMD_SMB_DATA, data->word);
+			amd_ec_write(smbus, AMD_SMB_DATA + 1, data->word >> 8);
+			protocol = AMD_SMB_PRTCL_PROC_CALL | pec;
+			read_write = I2C_SMBUS_READ;
+			break;
+
+		case I2C_SMBUS_BLOCK_PROC_CALL:
+			protocol |= pec;
+			len = min_t(u8, data->block[0], 31);
+			amd_ec_write(smbus, AMD_SMB_CMD, command);
+			amd_ec_write(smbus, AMD_SMB_BCNT, len);
+			for (i = 0; i < len; i++)
+				amd_ec_write(smbus, AMD_SMB_DATA + i, data->block[i + 1]);
+			protocol = AMD_SMB_PRTCL_BLOCK_PROC_CALL | pec;
+			read_write = I2C_SMBUS_READ;
+			break;
+
+		case I2C_SMBUS_WORD_DATA_PEC:
+		case I2C_SMBUS_BLOCK_DATA_PEC:
+		case I2C_SMBUS_PROC_CALL_PEC:
+		case I2C_SMBUS_BLOCK_PROC_CALL_PEC:
+			printk(KERN_WARNING "i2c-amd8111.c: Unexpected software PEC transaction %d\n.", size);
+			return -1;
+
+		default:
+			printk(KERN_WARNING "i2c-amd8111.c: Unsupported transaction %d\n", size);
+			return -1;
+	}
+
+	amd_ec_write(smbus, AMD_SMB_ADDR, addr << 1);
+	amd_ec_write(smbus, AMD_SMB_PRTCL, protocol);
+
+	amd_ec_read(smbus, AMD_SMB_STS, temp + 0);
+
+	if (~temp[0] & AMD_SMB_STS_DONE) {
+		udelay(500);
+		amd_ec_read(smbus, AMD_SMB_STS, temp + 0);
+	}
+
+	if (~temp[0] & AMD_SMB_STS_DONE) {
+		i2c_delay(HZ/100);
+		amd_ec_read(smbus, AMD_SMB_STS, temp + 0);
+	}
+
+	if ((~temp[0] & AMD_SMB_STS_DONE) || (temp[0] & AMD_SMB_STS_STATUS))
+		return -1;
+
+	if (read_write == I2C_SMBUS_WRITE)
+		return 0;
+
+	switch (size) {
+
+		case I2C_SMBUS_BYTE:
+		case I2C_SMBUS_BYTE_DATA:
+			amd_ec_read(smbus, AMD_SMB_DATA, &data->byte);
+			break;
+
+		case I2C_SMBUS_WORD_DATA:
+		case I2C_SMBUS_PROC_CALL:
+			amd_ec_read(smbus, AMD_SMB_DATA, temp + 0);
+			amd_ec_read(smbus, AMD_SMB_DATA + 1, temp + 1);
+			data->word = (temp[1] << 8) | temp[0];
+			break;
+
+		case I2C_SMBUS_BLOCK_DATA:
+		case I2C_SMBUS_BLOCK_PROC_CALL:
+			amd_ec_read(smbus, AMD_SMB_BCNT, &len);
+			len = min_t(u8, len, 32);
+		case I2C_SMBUS_I2C_BLOCK_DATA:
+			for (i = 0; i < len; i++)
+				amd_ec_read(smbus, AMD_SMB_DATA + i, data->block + i + 1);
+			data->block[0] = len;
+			break;
+	}
+
+	return 0;
+}
+
+
+u32 amd8111_func(struct i2c_adapter *adapter)
+{
+	return	I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
+		I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA |
+		I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
+		I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_HWPEC_CALC;
+}
+
+static struct i2c_algorithm smbus_algorithm = {
+	.name = "Non-I2C SMBus 2.0 adapter",
+	.id = I2C_ALGO_SMBUS,
+	.smbus_xfer = amd8111_access,
+	.functionality = amd8111_func,
+};
+
+
+static struct pci_device_id amd8111_ids[] __devinitdata = {
+	{ 0x1022, 0x746a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+	{ 0, }
+};
+
+static int __devinit amd8111_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	struct amd_smbus *smbus;
+	int error;
+
+	if (~pci_resource_flags(dev, 0) & IORESOURCE_IO)
+		return -1;
+
+	if (!(smbus = (void*)kmalloc(sizeof(struct amd_smbus), GFP_KERNEL)))
+		return -1;
+	memset(smbus, 0, sizeof(struct amd_smbus));
+
+	pci_set_drvdata(dev, smbus);
+	smbus->dev = dev;
+	smbus->base = pci_resource_start(dev, 0);
+	smbus->size = pci_resource_len(dev, 0);
+
+	if (!request_region(smbus->base, smbus->size, "amd8111 SMBus 2.0")) {
+		kfree(smbus);
+		return -1;
+	}
+
+	smbus->adapter.owner = THIS_MODULE;
+	sprintf(smbus->adapter.name, "SMBus2 AMD8111 adapter at %04x", smbus->base);
+	smbus->adapter.id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_AMD8111;
+	smbus->adapter.algo = &smbus_algorithm;
+	smbus->adapter.algo_data = smbus;
+
+	error = i2c_add_adapter(&smbus->adapter);
+	if (error) {
+		printk(KERN_WARNING "i2c-amd8111.c: Failed to register adapter.\n");
+		release_region(smbus->base, smbus->size);
+		kfree(smbus);
+		return -1;
+	}
+
+	pci_write_config_dword(smbus->dev, AMD_PCI_MISC, 0);
+
+	printk(KERN_INFO "i2c-amd8111.c: AMD8111 SMBus 2.0 adapter at %#x\n", smbus->base);
+	return 0;
+}
+
+
+static void __devexit amd8111_remove(struct pci_dev *dev)
+{
+	struct amd_smbus *smbus = (void*) pci_get_drvdata(dev);
+	i2c_del_adapter(&smbus->adapter);
+	release_region(smbus->base, smbus->size);
+	kfree(smbus);
+}
+
+static struct pci_driver amd8111_driver = {
+	.name		= "amd8111 smbus 2.0",
+	.id_table	= amd8111_ids,
+	.probe		= amd8111_probe,
+	.remove		= __devexit_p(amd8111_remove),
+};
+
+static int __init i2c_amd8111_init(void)
+{
+	printk(KERN_INFO "i2c-amd8111.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return pci_module_init(&amd8111_driver);
+}
+
+
+static void __exit i2c_amd8111_exit(void)
+{
+	pci_unregister_driver(&amd8111_driver);
+}
+
+module_init(i2c_amd8111_init);
+module_exit(i2c_amd8111_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-core.c linux-2.4.27-leo/drivers/i2c/i2c-core.c
--- linux-2.4.27/drivers/i2c/i2c-core.c	2004-02-20 14:11:41.000000000 +0000
+++ linux-2.4.27-leo/drivers/i2c/i2c-core.c	2004-09-20 21:34:38.000000000 +0100
@@ -18,56 +18,33 @@
 /* ------------------------------------------------------------------------- */
 
 /* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi>.
-   All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl> */
+   All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl>
+   SMBus 2.0 support by Mark Studebaker <mdsxyz123@yahoo.com>                */
 
-/* $Id: i2c-core.c,v 1.64 2001/08/13 01:35:56 mds Exp $ */
+/* i2c-core.c,v 1.91.2.2 2003/01/21 10:00:19 kmalkki Exp */
 
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/slab.h>
 #include <linux/proc_fs.h>
-#include <linux/config.h>
-
-#include <linux/i2c.h>
-
-/* ----- compatibility stuff ----------------------------------------------- */
-
 #include <linux/init.h>
-
+#include <linux/i2c.h>
 #include <asm/uaccess.h>
 
 /* ----- global defines ---------------------------------------------------- */
 
-/* exclusive access to the bus */
-#define I2C_LOCK(adap) down(&adap->lock)
-#define I2C_UNLOCK(adap) up(&adap->lock) 
-
-#define ADAP_LOCK()	down(&adap_lock)
-#define ADAP_UNLOCK()	up(&adap_lock)
-
-#define DRV_LOCK()	down(&driver_lock)
-#define DRV_UNLOCK()	up(&driver_lock)
-
 #define DEB(x) if (i2c_debug>=1) x;
 #define DEB2(x) if (i2c_debug>=2) x;
 
 /* ----- global variables -------------------------------------------------- */
 
-/**** lock for writing to global variables: the adapter & driver list */
-struct semaphore adap_lock;
-struct semaphore driver_lock;
-
-/**** adapter list */
+DECLARE_MUTEX(core_lists);
 static struct i2c_adapter *adapters[I2C_ADAP_MAX];
-static int adap_count;
-
-/**** drivers list */
 static struct i2c_driver *drivers[I2C_DRIVER_MAX];
-static int driver_count;
 
 /**** debug level */
-static int i2c_debug=1;
+static int i2c_debug;
 
 /* ---------------------------------------------------
  * /proc entry declarations
@@ -75,10 +52,6 @@
  */
 
 #ifdef CONFIG_PROC_FS
-
-static int i2cproc_init(void);
-static int i2cproc_cleanup(void);
-
 static ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, 
                                 loff_t *ppos);
 static int read_bus_i2c(char *buf, char **start, off_t offset, int len,
@@ -87,15 +60,11 @@
 /* To implement the dynamic /proc/bus/i2c-? files, we need our own 
    implementation of the read hook */
 static struct file_operations i2cproc_operations = {
-	read:		i2cproc_bus_read,
+	.read		= i2cproc_bus_read,
 };
 
-static int i2cproc_initialized = 0;
-
-#else /* undef CONFIG_PROC_FS */
-
-#define i2cproc_init() 0
-#define i2cproc_cleanup() 0
+static int i2cproc_register(struct i2c_adapter *adap, int bus);
+static void i2cproc_remove(int bus);
 
 #endif /* CONFIG_PROC_FS */
 
@@ -112,9 +81,9 @@
  */
 int i2c_add_adapter(struct i2c_adapter *adap)
 {
-	int i,j,res;
+	int i,j,res = 0;
 
-	ADAP_LOCK();
+	down(&core_lists);
 	for (i = 0; i < I2C_ADAP_MAX; i++)
 		if (NULL == adapters[i])
 			break;
@@ -125,68 +94,39 @@
 		res = -ENOMEM;
 		goto ERROR0;
 	}
+	
+#ifdef CONFIG_PROC_FS
+	res = i2cproc_register(adap, i);
+	if (res<0)
+	    goto ERROR0;
+#endif /* def CONFIG_PROC_FS */
 
 	adapters[i] = adap;
-	adap_count++;
-	ADAP_UNLOCK();
 	
 	/* init data types */
-	init_MUTEX(&adap->lock);
-
-#ifdef CONFIG_PROC_FS
-
-	if (i2cproc_initialized) {
-		char name[8];
-		struct proc_dir_entry *proc_entry;
-
-		sprintf(name,"i2c-%d", i);
-
-		proc_entry = create_proc_entry(name,0,proc_bus);
-		if (! proc_entry) {
-			printk("i2c-core.o: Could not create /proc/bus/%s\n",
-			       name);
-			res = -ENOENT;
-			goto ERROR1;
-		}
-
-		proc_entry->proc_fops = &i2cproc_operations;
-		proc_entry->owner = THIS_MODULE;
-		adap->inode = proc_entry->low_ino;
-	}
-
-#endif /* def CONFIG_PROC_FS */
+	init_MUTEX(&adap->bus);
+	init_MUTEX(&adap->list);
 
 	/* inform drivers of new adapters */
-	DRV_LOCK();	
 	for (j=0;j<I2C_DRIVER_MAX;j++)
 		if (drivers[j]!=NULL && 
 		    (drivers[j]->flags&(I2C_DF_NOTIFY|I2C_DF_DUMMY)))
 			/* We ignore the return code; if it fails, too bad */
 			drivers[j]->attach_adapter(adap);
-	DRV_UNLOCK();
 	
 	DEB(printk(KERN_DEBUG "i2c-core.o: adapter %s registered as adapter %d.\n",
 	           adap->name,i));
-
-	return 0;	
-
-
-ERROR1:
-	ADAP_LOCK();
-	adapters[i] = NULL;
-	adap_count--;
 ERROR0:
-	ADAP_UNLOCK();
+	up(&core_lists);
 	return res;
 }
 
 
 int i2c_del_adapter(struct i2c_adapter *adap)
 {
-	int i,j,res;
-
-	ADAP_LOCK();
+	int i,j,res = 0;
 
+	down(&core_lists);
 	for (i = 0; i < I2C_ADAP_MAX; i++)
 		if (adap == adapters[i])
 			break;
@@ -202,20 +142,17 @@
 	 * *detach* it! Of course, each dummy driver should know about
 	 * this or hell will break loose...
 	 */
-	DRV_LOCK();
 	for (j = 0; j < I2C_DRIVER_MAX; j++) 
 		if (drivers[j] && (drivers[j]->flags & I2C_DF_DUMMY))
 			if ((res = drivers[j]->attach_adapter(adap))) {
 				printk(KERN_WARNING "i2c-core.o: can't detach adapter %s "
 				       "while detaching driver %s: driver not "
 				       "detached!",adap->name,drivers[j]->name);
-				goto ERROR1;	
+				goto ERROR0;
 			}
-	DRV_UNLOCK();
-
 
 	/* detach any active clients. This must be done first, because
-	 * it can fail; in which case we give upp. */
+	 * it can fail; in which case we give up. */
 	for (j=0;j<I2C_CLIENT_MAX;j++) {
 		struct i2c_client *client = adap->clients[j];
 		if (client!=NULL)
@@ -231,26 +168,15 @@
 				goto ERROR0;
 			}
 	}
+
 #ifdef CONFIG_PROC_FS
-	if (i2cproc_initialized) {
-		char name[8];
-		sprintf(name,"i2c-%d", i);
-		remove_proc_entry(name,proc_bus);
-	}
+	i2cproc_remove(i);
 #endif /* def CONFIG_PROC_FS */
 
 	adapters[i] = NULL;
-	adap_count--;
-	
-	ADAP_UNLOCK();	
 	DEB(printk(KERN_DEBUG "i2c-core.o: adapter unregistered: %s\n",adap->name));
-	return 0;
-
 ERROR0:
-	ADAP_UNLOCK();
-	return res;
-ERROR1:
-	DRV_UNLOCK();
+	up(&core_lists);
 	return res;
 }
 
@@ -264,7 +190,8 @@
 int i2c_add_driver(struct i2c_driver *driver)
 {
 	int i;
-	DRV_LOCK();
+
+	down(&core_lists);
 	for (i = 0; i < I2C_DRIVER_MAX; i++)
 		if (NULL == drivers[i])
 			break;
@@ -273,19 +200,12 @@
 		       " i2c-core.o: register_driver(%s) "
 		       "- enlarge I2C_DRIVER_MAX.\n",
 			driver->name);
-		DRV_UNLOCK();
+		up(&core_lists);
 		return -ENOMEM;
 	}
-
 	drivers[i] = driver;
-	driver_count++;
-	
-	DRV_UNLOCK();	/* driver was successfully added */
-	
 	DEB(printk(KERN_DEBUG "i2c-core.o: driver %s registered.\n",driver->name));
 	
-	ADAP_LOCK();
-
 	/* now look for instances of driver on our adapters
 	 */
 	if (driver->flags& (I2C_DF_NOTIFY|I2C_DF_DUMMY)) {
@@ -294,15 +214,15 @@
 				/* Ignore errors */
 				driver->attach_adapter(adapters[i]);
 	}
-	ADAP_UNLOCK();
+	up(&core_lists);
 	return 0;
 }
 
 int i2c_del_driver(struct i2c_driver *driver)
 {
-	int i,j,k,res;
+	int i,j,k,res = 0;
 
-	DRV_LOCK();
+	down(&core_lists);
 	for (i = 0; i < I2C_DRIVER_MAX; i++)
 		if (driver == drivers[i])
 			break;
@@ -310,7 +230,7 @@
 		printk(KERN_WARNING " i2c-core.o: unregister_driver: "
 				    "[%s] not found\n",
 			driver->name);
-		DRV_UNLOCK();
+		up(&core_lists);
 		return -ENODEV;
 	}
 	/* Have a look at each adapter, if clients of this driver are still
@@ -322,7 +242,6 @@
 	 * invalid operation might (will!) result, when using stale client
 	 * pointers.
 	 */
-	ADAP_LOCK(); /* should be moved inside the if statement... */
 	for (k=0;k<I2C_ADAP_MAX;k++) {
 		struct i2c_adapter *adap = adapters[k];
 		if (adap == NULL) /* skip empty entries. */
@@ -341,8 +260,7 @@
 				       "not be detached properly; driver "
 				       "not unloaded!",driver->name,
 				       adap->name);
-				ADAP_UNLOCK();
-				return res;
+				goto ERROR0;
 			}
 		} else {
 			for (j=0;j<I2C_CLIENT_MAX;j++) { 
@@ -359,37 +277,47 @@
 						       "unregistering driver "
 						       "`%s', the client at "
 						       "address %02x of "
-						       "adapter `%s' could not"
-						       "be detached; driver"
+						       "adapter `%s' could not "
+						       "be detached; driver "
 						       "not unloaded!",
 						       driver->name,
 						       client->addr,
 						       adap->name);
-						ADAP_UNLOCK();
-						return res;
+						goto ERROR0;
 					}
 				}
 			}
 		}
 	}
-	ADAP_UNLOCK();
 	drivers[i] = NULL;
-	driver_count--;
-	DRV_UNLOCK();
-	
 	DEB(printk(KERN_DEBUG "i2c-core.o: driver unregistered: %s\n",driver->name));
-	return 0;
+
+ERROR0:
+	up(&core_lists);
+	return res;
 }
 
-int i2c_check_addr (struct i2c_adapter *adapter, int addr)
+static int __i2c_check_addr (struct i2c_adapter *adapter, int addr)
 {
 	int i;
 	for (i = 0; i < I2C_CLIENT_MAX ; i++) 
 		if (adapter->clients[i] && (adapter->clients[i]->addr == addr))
 			return -EBUSY;
+
 	return 0;
 }
 
+int i2c_check_addr (struct i2c_adapter *adapter, int addr)
+{
+	int rval;
+
+	down(&adapter->list);
+	rval = __i2c_check_addr(adapter, addr);
+	up(&adapter->list);
+
+	return rval;
+}
+
 int i2c_attach_client(struct i2c_client *client)
 {
 	struct i2c_adapter *adapter = client->adapter;
@@ -398,6 +326,7 @@
 	if (i2c_check_addr(client->adapter,client->addr))
 		return -EBUSY;
 
+	down(&adapter->list);
 	for (i = 0; i < I2C_CLIENT_MAX; i++)
 		if (NULL == adapter->clients[i])
 			break;
@@ -405,11 +334,11 @@
 		printk(KERN_WARNING 
 		       " i2c-core.o: attach_client(%s) - enlarge I2C_CLIENT_MAX.\n",
 			client->name);
+		up(&adapter->list);
 		return -ENOMEM;
 	}
-
 	adapter->clients[i] = client;
-	adapter->client_count++;
+	up(&adapter->list);
 	
 	if (adapter->client_register) 
 		if (adapter->client_register(client)) 
@@ -431,16 +360,6 @@
 	struct i2c_adapter *adapter = client->adapter;
 	int i,res;
 
-	for (i = 0; i < I2C_CLIENT_MAX; i++)
-		if (client == adapter->clients[i])
-			break;
-	if (I2C_CLIENT_MAX == i) {
-		printk(KERN_WARNING " i2c-core.o: unregister_client "
-				    "[%s] not found\n",
-			client->name);
-		return -ENODEV;
-	}
-	
 	if( (client->flags & I2C_CLIENT_ALLOW_USE) && 
 	    (client->usage_count>0))
 		return -EBUSY;
@@ -452,33 +371,41 @@
 			return res;
 		}
 
+	down(&adapter->list);
+	for (i = 0; i < I2C_CLIENT_MAX; i++)
+		if (client == adapter->clients[i])
+			break;
+	if (I2C_CLIENT_MAX == i) {
+		printk(KERN_WARNING " i2c-core.o: unregister_client "
+				    "[%s] not found\n",
+			client->name);
+		up(&adapter->list);
+		return -ENODEV;
+	}
 	adapter->clients[i] = NULL;
-	adapter->client_count--;
+	up(&adapter->list);
 
 	DEB(printk(KERN_DEBUG "i2c-core.o: client [%s] unregistered.\n",client->name));
 	return 0;
 }
 
-void i2c_inc_use_client(struct i2c_client *client)
+static void i2c_inc_use_client(struct i2c_client *client)
 {
-
-	if (client->driver->inc_use != NULL)
-		client->driver->inc_use(client);
-
-	if (client->adapter->inc_use != NULL)
-		client->adapter->inc_use(client->adapter);
+	if(client->driver->owner)
+		__MOD_INC_USE_COUNT(client->driver->owner);
+	if(client->adapter->owner)
+		__MOD_INC_USE_COUNT(client->adapter->owner);
 }
 
-void i2c_dec_use_client(struct i2c_client *client)
+static void i2c_dec_use_client(struct i2c_client *client)
 {
-	
-	if (client->driver->dec_use != NULL)
-		client->driver->dec_use(client);
-
-	if (client->adapter->dec_use != NULL)
-		client->adapter->dec_use(client->adapter);
+	if(client->driver->owner)
+		__MOD_DEC_USE_COUNT(client->driver->owner);
+	if(client->adapter->owner)
+		__MOD_DEC_USE_COUNT(client->adapter->owner);
 }
 
+#if 0 /* just forget about this for now --km */
 struct i2c_client *i2c_get_client(int driver_id, int adapter_id, 
 					struct i2c_client *prev)
 {
@@ -545,18 +472,17 @@
 
 	return 0;
 }
+#endif
 
 int i2c_use_client(struct i2c_client *client)
 {
-	if(client->flags & I2C_CLIENT_ALLOW_USE) {
-		if (client->flags & I2C_CLIENT_ALLOW_MULTIPLE_USE) 
+	if (client->flags & I2C_CLIENT_ALLOW_USE) {
+		if (client->flags & I2C_CLIENT_ALLOW_MULTIPLE_USE)
+			client->usage_count++;
+		else if (client->usage_count > 0) 
+			return -EBUSY;
+		else 
 			client->usage_count++;
-		else {
-			if(client->usage_count > 0) 
-				return -EBUSY;
-			 else 
-				client->usage_count++;
-		}
 	}
 
 	i2c_inc_use_client(client);
@@ -589,12 +515,13 @@
 #ifdef CONFIG_PROC_FS
 
 /* This function generates the output for /proc/bus/i2c */
-int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof, 
+static int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof, 
                  void *private)
 {
 	int i;
 	int nr = 0;
 	/* Note that it is safe to write a `little' beyond len. Yes, really. */
+	down(&core_lists);
 	for (i = 0; (i < I2C_ADAP_MAX) && (nr < len); i++)
 		if (adapters[i]) {
 			nr += sprintf(buf+nr, "i2c-%d\t", i);
@@ -611,6 +538,7 @@
 			              adapters[i]->name,
 			              adapters[i]->algo->name);
 		}
+	up(&core_lists);
 	return nr;
 }
 
@@ -621,98 +549,125 @@
 	struct inode * inode = file->f_dentry->d_inode;
 	char *kbuf;
 	struct i2c_client *client;
+	struct i2c_adapter *adap;
 	int i,j,k,order_nr,len=0;
 	size_t len_total;
 	int order[I2C_CLIENT_MAX];
+#define OUTPUT_LENGTH_PER_LINE 70
 
-	if (count > 4000)
-		return -EINVAL; 
 	len_total = file->f_pos + count;
-	/* Too bad if this gets longer (unlikely) */
-	if (len_total > 4000)
-		len_total = 4000;
-	for (i = 0; i < I2C_ADAP_MAX; i++)
-		if (adapters[i]->inode == inode->i_ino) {
-		/* We need a bit of slack in the kernel buffer; this makes the
-		   sprintf safe. */
-			if (! (kbuf = kmalloc(count + 80,GFP_KERNEL)))
-				return -ENOMEM;
-			/* Order will hold the indexes of the clients
-			   sorted by address */
-			order_nr=0;
-			for (j = 0; j < I2C_CLIENT_MAX; j++) {
-				if ((client = adapters[i]->clients[j]) && 
-				    (client->driver->id != I2C_DRIVERID_I2CDEV))  {
-					for(k = order_nr; 
-					    (k > 0) && 
-					    adapters[i]->clients[order[k-1]]->
-					             addr > client->addr; 
-					    k--)
-						order[k] = order[k-1];
-					order[k] = j;
-					order_nr++;
-				}
-			}
-
-
-			for (j = 0; (j < order_nr) && (len < len_total); j++) {
-				client = adapters[i]->clients[order[j]];
-				len += sprintf(kbuf+len,"%02x\t%-32s\t%-32s\n",
-				              client->addr,
-				              client->name,
-				              client->driver->name);
-			}
-			len = len - file->f_pos;
-			if (len > count)
-				len = count;
-			if (len < 0) 
-				len = 0;
-			if (copy_to_user (buf,kbuf+file->f_pos, len)) {
-				kfree(kbuf);
-				return -EFAULT;
-			}
-			file->f_pos += len;
-			kfree(kbuf);
-			return len;
-		}
-	return -ENOENT;
+	if (len_total > (I2C_CLIENT_MAX * OUTPUT_LENGTH_PER_LINE) )
+		/* adjust to maximum file size */
+		len_total = (I2C_CLIENT_MAX * OUTPUT_LENGTH_PER_LINE);
+
+	down(&core_lists);
+	/* adap = file->private_data; ?? --km */
+	for (i = 0; i < I2C_ADAP_MAX; i++) {
+	    adap = adapters[i];
+	    if (adap && (adap->inode == inode->i_ino))
+		break;
+	}
+	if ( I2C_ADAP_MAX == i ) {
+	    up(&core_lists);
+	    return -ENOENT;
+	}
+
+	/* We need a bit of slack in the kernel buffer; this makes the
+	   sprintf safe. */
+	if (! (kbuf = kmalloc(len_total +
+			      OUTPUT_LENGTH_PER_LINE,
+			      GFP_KERNEL)))
+	    return -ENOMEM;
+
+	/* Order will hold the indexes of the clients
+	   sorted by address */
+	order_nr=0;
+	down(&adap->list);
+	for (j = 0; j < I2C_CLIENT_MAX; j++) {
+	    if ((client = adap->clients[j]) && 
+		(client->driver->id != I2C_DRIVERID_I2CDEV))  {
+		for(k = order_nr; 
+		    (k > 0) && 
+			adap->clients[order[k-1]]->
+			addr > client->addr; 
+		    k--)
+		    order[k] = order[k-1];
+		order[k] = j;
+		order_nr++;
+	    }
+	}
+
+
+	for (j = 0; (j < order_nr) && (len < len_total); j++) {
+	    client = adap->clients[order[j]];
+	    len += sprintf(kbuf+len,"%02x\t%-32s\t%-32s\n",
+			   client->addr,
+			   client->name,
+			   client->driver->name);
+	}
+	up(&adap->list);
+	up(&core_lists);
+	
+	len = len - file->f_pos;
+	if (len > count)
+	    len = count;
+	if (len < 0) 
+	    len = 0;
+	if (copy_to_user (buf,kbuf+file->f_pos, len)) {
+	    kfree(kbuf);
+	    return -EFAULT;
+	}
+	file->f_pos += len;
+	kfree(kbuf);
+	return len;
+}
+
+static int i2cproc_register(struct i2c_adapter *adap, int bus)
+{
+	char name[8];
+	struct proc_dir_entry *proc_entry;
+
+	sprintf(name,"i2c-%d", bus);
+	proc_entry = create_proc_entry(name,0,proc_bus);
+	if (! proc_entry) {
+		printk(KERN_ERR "i2c-core.o: Could not create /proc/bus/%s\n",
+		       name);
+		return -ENOENT;
+	}
+	    
+	proc_entry->proc_fops = &i2cproc_operations;
+	proc_entry->owner = adap->owner;
+	adap->inode = proc_entry->low_ino;
+	return 0;
 }
 
-int i2cproc_init(void)
+static void i2cproc_remove(int bus)
 {
+	char name[8];
+	sprintf(name,"i2c-%d", bus);
+	remove_proc_entry(name, proc_bus);
+}
 
+static int __init i2cproc_init(void)
+{
 	struct proc_dir_entry *proc_bus_i2c;
 
-	i2cproc_initialized = 0;
-
-	if (! proc_bus) {
-		printk("i2c-core.o: /proc/bus/ does not exist");
-		i2cproc_cleanup();
-		return -ENOENT;
- 	} 
 	proc_bus_i2c = create_proc_entry("i2c",0,proc_bus);
 	if (!proc_bus_i2c) {
 		printk(KERN_ERR "i2c-core.o: Could not create /proc/bus/i2c");
-		i2cproc_cleanup();
 		return -ENOENT;
  	}
+
 	proc_bus_i2c->read_proc = &read_bus_i2c;
 	proc_bus_i2c->owner = THIS_MODULE;
-	i2cproc_initialized += 2;
 	return 0;
 }
 
-int i2cproc_cleanup(void)
+static void __exit i2cproc_cleanup(void)
 {
-
-	if (i2cproc_initialized >= 1) {
-		remove_proc_entry("i2c",proc_bus);
-		i2cproc_initialized -= 2;
-	}
-	return 0;
+	remove_proc_entry("i2c",proc_bus);
 }
 
-
 #endif /* def CONFIG_PROC_FS */
 
 /* ----------------------------------------------------
@@ -728,9 +683,9 @@
  	 	DEB2(printk(KERN_DEBUG "i2c-core.o: master_xfer: %s with %d msgs.\n",
 		            adap->name,num));
 
-		I2C_LOCK(adap);
+		down(&adap->bus);
 		ret = adap->algo->master_xfer(adap,msgs,num);
-		I2C_UNLOCK(adap);
+		up(&adap->bus);
 
 		return ret;
 	} else {
@@ -755,9 +710,9 @@
 		DEB2(printk(KERN_DEBUG "i2c-core.o: master_send: writing %d bytes on %s.\n",
 			count,client->adapter->name));
 	
-		I2C_LOCK(adap);
+		down(&adap->bus);
 		ret = adap->algo->master_xfer(adap,&msg,1);
-		I2C_UNLOCK(adap);
+		up(&adap->bus);
 
 		/* if everything went ok (i.e. 1 msg transmitted), return #bytes
 		 * transmitted, else error code.
@@ -785,9 +740,9 @@
 		DEB2(printk(KERN_DEBUG "i2c-core.o: master_recv: reading %d bytes on %s.\n",
 			count,client->adapter->name));
 	
-		I2C_LOCK(adap);
+		down(&adap->bus);
 		ret = adap->algo->master_xfer(adap,&msg,1);
-		I2C_UNLOCK(adap);
+		up(&adap->bus);
 	
 		DEB2(printk(KERN_DEBUG "i2c-core.o: master_recv: return:%d (count:%d, addr:0x%02x)\n",
 			ret, count, client->addr));
@@ -965,6 +920,123 @@
 
 /* The SMBus parts */
 
+#define POLY    (0x1070U << 3) 
+static u8
+crc8(u16 data)
+{
+	int i;
+  
+	for(i = 0; i < 8; i++) {
+		if (data & 0x8000) 
+			data = data ^ POLY;
+		data = data << 1;
+	}
+	return (u8)(data >> 8);
+}
+
+/* CRC over count bytes in the first array plus the bytes in the rest
+   array if it is non-null. rest[0] is the (length of rest) - 1
+   and is included. */
+u8 i2c_smbus_partial_pec(u8 crc, int count, u8 *first, u8 *rest)
+{
+	int i;
+
+	for(i = 0; i < count; i++)
+		crc = crc8((crc ^ first[i]) << 8);
+	if(rest != NULL)
+		for(i = 0; i <= rest[0]; i++)
+			crc = crc8((crc ^ rest[i]) << 8);
+	return crc;
+}
+
+u8 i2c_smbus_pec(int count, u8 *first, u8 *rest)
+{
+	return i2c_smbus_partial_pec(0, count, first, rest);
+}
+
+/* Returns new "size" (transaction type)
+   Note that we convert byte to byte_data and byte_data to word_data
+   rather than invent new xxx_PEC transactions. */
+int i2c_smbus_add_pec(u16 addr, u8 command, int size,
+                      union i2c_smbus_data *data)
+{
+	u8 buf[3];
+
+	buf[0] = addr << 1;
+	buf[1] = command;
+	switch(size) {
+		case I2C_SMBUS_BYTE:
+			data->byte = i2c_smbus_pec(2, buf, NULL);
+			size = I2C_SMBUS_BYTE_DATA;
+			break;
+		case I2C_SMBUS_BYTE_DATA:
+			buf[2] = data->byte;
+			data->word = buf[2] ||
+			            (i2c_smbus_pec(3, buf, NULL) << 8);
+			size = I2C_SMBUS_WORD_DATA;
+			break;
+		case I2C_SMBUS_WORD_DATA:
+			/* unsupported */
+			break;
+		case I2C_SMBUS_BLOCK_DATA:
+			data->block[data->block[0] + 1] =
+			             i2c_smbus_pec(2, buf, data->block);
+			size = I2C_SMBUS_BLOCK_DATA_PEC;
+			break;
+	}
+	return size;	
+}
+
+int i2c_smbus_check_pec(u16 addr, u8 command, int size, u8 partial,
+                        union i2c_smbus_data *data)
+{
+	u8 buf[3], rpec, cpec;
+
+	buf[1] = command;
+	switch(size) {
+		case I2C_SMBUS_BYTE_DATA:
+			buf[0] = (addr << 1) | 1;
+			cpec = i2c_smbus_pec(2, buf, NULL);
+			rpec = data->byte;
+			break;
+		case I2C_SMBUS_WORD_DATA:
+			buf[0] = (addr << 1) | 1;
+			buf[2] = data->word & 0xff;
+			cpec = i2c_smbus_pec(3, buf, NULL);
+			rpec = data->word >> 8;
+			break;
+		case I2C_SMBUS_WORD_DATA_PEC:
+			/* unsupported */
+			cpec = rpec = 0;
+			break;
+		case I2C_SMBUS_PROC_CALL_PEC:
+			/* unsupported */
+			cpec = rpec = 0;
+			break;
+		case I2C_SMBUS_BLOCK_DATA_PEC:
+			buf[0] = (addr << 1);
+			buf[2] = (addr << 1) | 1;
+			cpec = i2c_smbus_pec(3, buf, data->block);
+			rpec = data->block[data->block[0] + 1];
+			break;
+		case I2C_SMBUS_BLOCK_PROC_CALL_PEC:
+			buf[0] = (addr << 1) | 1;
+			rpec = i2c_smbus_partial_pec(partial, 1,
+			                             buf, data->block);
+			cpec = data->block[data->block[0] + 1];
+			break;
+		default:
+			cpec = rpec = 0;
+			break;
+	}
+	if(rpec != cpec) {
+		DEB(printk(KERN_DEBUG "i2c-core.o: Bad PEC 0x%02x vs. 0x%02x\n",
+		           rpec, cpec));
+		return -1;
+	}
+	return 0;	
+}
+
 extern s32 i2c_smbus_write_quick(struct i2c_client * client, u8 value)
 {
 	return i2c_smbus_xfer(client->adapter,client->addr,client->flags,
@@ -983,8 +1055,9 @@
 
 extern s32 i2c_smbus_write_byte(struct i2c_client * client, u8 value)
 {
+	union i2c_smbus_data data;	/* only for PEC */
 	return i2c_smbus_xfer(client->adapter,client->addr,client->flags,
-	                      I2C_SMBUS_WRITE,value, I2C_SMBUS_BYTE,NULL);
+	                      I2C_SMBUS_WRITE,value, I2C_SMBUS_BYTE,&data);
 }
 
 extern s32 i2c_smbus_read_byte_data(struct i2c_client * client, u8 command)
@@ -1072,6 +1145,43 @@
 	                      I2C_SMBUS_BLOCK_DATA,&data);
 }
 
+/* Returns the number of read bytes */
+extern s32 i2c_smbus_block_process_call(struct i2c_client * client,
+                                        u8 command, u8 length, u8 *values)
+{
+	union i2c_smbus_data data;
+	int i;
+	if (length > I2C_SMBUS_BLOCK_MAX - 1)
+		return -1;
+	data.block[0] = length;
+	for (i = 1; i <= length; i++)
+		data.block[i] = values[i-1];
+	if(i2c_smbus_xfer(client->adapter,client->addr,client->flags,
+	                  I2C_SMBUS_WRITE, command,
+	                  I2C_SMBUS_BLOCK_PROC_CALL, &data))
+		return -1;
+	for (i = 1; i <= data.block[0]; i++)
+		values[i-1] = data.block[i];
+	return data.block[0];
+}
+
+/* Returns the number of read bytes */
+extern s32 i2c_smbus_read_i2c_block_data(struct i2c_client * client,
+                                         u8 command, u8 *values)
+{
+	union i2c_smbus_data data;
+	int i;
+	if (i2c_smbus_xfer(client->adapter,client->addr,client->flags,
+	                      I2C_SMBUS_READ,command,
+	                      I2C_SMBUS_I2C_BLOCK_DATA,&data))
+		return -1;
+	else {
+		for (i = 1; i <= data.block[0]; i++)
+			values[i-1] = data.block[i];
+		return data.block[0];
+	}
+}
+
 extern s32 i2c_smbus_write_i2c_block_data(struct i2c_client * client,
                                           u8 command, u8 length, u8 *values)
 {
@@ -1098,13 +1208,13 @@
 	  need to use only one message; when reading, we need two. We initialize
 	  most things with sane defaults, to keep the code below somewhat
 	  simpler. */
-	unsigned char msgbuf0[34];
-	unsigned char msgbuf1[34];
+	unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+2];
+	unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];
 	int num = read_write == I2C_SMBUS_READ?2:1;
 	struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 }, 
 	                          { addr, flags | I2C_M_RD, 0, msgbuf1 }
 	                        };
-	int i;
+	int i, len;
 
 	msgbuf0[0] = command;
 	switch(size) {
@@ -1140,16 +1250,30 @@
 		break;
 	case I2C_SMBUS_PROC_CALL:
 		num = 2; /* Special case */
+		read_write = I2C_SMBUS_READ;
 		msg[0].len = 3;
 		msg[1].len = 2;
 		msgbuf0[1] = data->word & 0xff;
 		msgbuf0[2] = (data->word >> 8) & 0xff;
 		break;
 	case I2C_SMBUS_BLOCK_DATA:
+	case I2C_SMBUS_BLOCK_DATA_PEC:
 		if (read_write == I2C_SMBUS_READ) {
-			printk(KERN_ERR "i2c-core.o: Block read not supported "
-			       "under I2C emulation!\n");
-			return -1;
+			/* I2C_FUNC_SMBUS_EMUL doesn't include I2C_FUNC_SMBUS_READ_BLOCK_DATA */
+			if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BLOCK_DATA)) {
+				printk(KERN_ERR "i2c-core.o: Block read not supported "
+				       "under I2C emulation!\n");
+				return -1;
+			}
+			/* set send message */
+			msg[0].len = 1;
+			/* set recv message */
+			msg[1].flags |= I2C_M_RECV_LEN;
+			msg[1].len = I2C_SMBUS_BLOCK_MAX + 1;
+			if (size == I2C_SMBUS_BLOCK_DATA_PEC) {
+				msg[1].len++;
+				msg[1].flags |= I2C_M_RECV_PEC;
+			}
 		} else {
 			msg[0].len = data->block[0] + 2;
 			if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) {
@@ -1158,10 +1282,57 @@
 				       data->block[0]);
 				return -1;
 			}
+			if(size == I2C_SMBUS_BLOCK_DATA_PEC)
+				(msg[0].len)++;
 			for (i = 1; i <= msg[0].len; i++)
 				msgbuf0[i] = data->block[i-1];
 		}
 		break;
+	case I2C_SMBUS_BLOCK_PROC_CALL:
+	case I2C_SMBUS_BLOCK_PROC_CALL_PEC:
+		/* I2C_FUNC_SMBUS_EMUL doesn't include I2C_FUNC_SMBUS_BLOCK_PROC_CALL */
+		if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BLOCK_PROC_CALL)) {
+			printk(KERN_ERR "i2c-core.o: adapter doesn't support block process call!\n");
+			return -1;
+		}
+
+		/* Another special case */
+		num = 2;
+		read_write = I2C_SMBUS_READ;
+		
+		/* set send message */
+		msg[0].len = data->block[0] + 2;
+		if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) {
+			printk(KERN_ERR "i2c-core.o: smbus_access called with "
+				"invalid block write size (%d)\n", data->block[0]);
+			return -1;
+		}
+		for (i = 1; i <= msg[0].len; i++)
+			msgbuf0[i] = data->block[i-1];
+		
+		/* set recv message */
+		msg[1].flags |= I2C_M_RECV_LEN;
+		msg[1].len = I2C_SMBUS_BLOCK_MAX + 1;
+		if (size == I2C_SMBUS_BLOCK_PROC_CALL_PEC) {
+			msg[1].len++;
+			msg[1].flags |= I2C_M_RECV_PEC;
+		}
+		break;
+	case I2C_SMBUS_I2C_BLOCK_DATA:
+		if (read_write == I2C_SMBUS_READ) {
+			msg[1].len = I2C_SMBUS_I2C_BLOCK_MAX;
+		} else {
+			msg[0].len = data->block[0] + 1;
+			if (msg[0].len > I2C_SMBUS_I2C_BLOCK_MAX + 1) {
+				printk("i2c-core.o: i2c_smbus_xfer_emulated called with "
+				       "invalid block write size (%d)\n",
+				       data->block[0]);
+				return -1;
+			}
+			for (i = 1; i <= data->block[0]; i++)
+				msgbuf0[i] = data->block[i];
+		}
+		break;
 	default:
 		printk(KERN_ERR "i2c-core.o: smbus_access called with invalid size (%d)\n",
 		       size);
@@ -1183,25 +1354,72 @@
 			case I2C_SMBUS_PROC_CALL:
 				data->word = msgbuf1[0] | (msgbuf1[1] << 8);
 				break;
+			case I2C_SMBUS_I2C_BLOCK_DATA:
+				/* fixed at 32 for now */
+				data->block[0] = I2C_SMBUS_I2C_BLOCK_MAX;
+				for (i = 0; i < I2C_SMBUS_I2C_BLOCK_MAX; i++)
+					data->block[i+1] = msgbuf1[i];
+				break;
+			case I2C_SMBUS_BLOCK_DATA:
+			case I2C_SMBUS_BLOCK_PROC_CALL:
+			case I2C_SMBUS_BLOCK_DATA_PEC:
+			case I2C_SMBUS_BLOCK_PROC_CALL_PEC:
+				len = msgbuf1[0] + 1;
+				if(size == I2C_SMBUS_BLOCK_DATA_PEC ||
+				   size == I2C_SMBUS_BLOCK_PROC_CALL_PEC)
+					len++;
+				for (i = 0; i < len; i++)
+					data->block[i] = msgbuf1[i];
+				break;
 		}
 	return 0;
 }
 
 
-s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags,
+s32 i2c_smbus_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags,
                    char read_write, u8 command, int size, 
                    union i2c_smbus_data * data)
 {
 	s32 res;
-	flags = flags & I2C_M_TEN;
-	if (adapter->algo->smbus_xfer) {
-		I2C_LOCK(adapter);
-		res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write,
+	int swpec = 0;
+	u8 partial = 0;
+
+	flags &= I2C_M_TEN | I2C_CLIENT_PEC;
+	if((flags & I2C_CLIENT_PEC) &&
+	   !(i2c_check_functionality(adap, I2C_FUNC_SMBUS_HWPEC_CALC))) {
+		swpec = 1;
+		if(read_write == I2C_SMBUS_READ &&
+		   size == I2C_SMBUS_BLOCK_DATA)
+			size = I2C_SMBUS_BLOCK_DATA_PEC;
+		else if(size == I2C_SMBUS_PROC_CALL)
+			size = I2C_SMBUS_PROC_CALL_PEC;
+		else if(size == I2C_SMBUS_BLOCK_PROC_CALL) {
+			i2c_smbus_add_pec(addr, command,
+		                          I2C_SMBUS_BLOCK_DATA, data);
+			partial = data->block[data->block[0] + 1];
+			size = I2C_SMBUS_BLOCK_PROC_CALL_PEC;
+		} else if(read_write == I2C_SMBUS_WRITE &&
+		          size != I2C_SMBUS_QUICK &&
+		          size != I2C_SMBUS_I2C_BLOCK_DATA)
+			size = i2c_smbus_add_pec(addr, command, size, data);
+	}
+
+	if (adap->algo->smbus_xfer) {
+		down(&adap->bus);
+		res = adap->algo->smbus_xfer(adap,addr,flags,read_write,
 		                                command,size,data);
-		I2C_UNLOCK(adapter);
+		up(&adap->bus);
 	} else
-		res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,
+		res = i2c_smbus_xfer_emulated(adap,addr,flags,read_write,
 	                                      command,size,data);
+
+	if(res >= 0 && swpec &&
+	   size != I2C_SMBUS_QUICK && size != I2C_SMBUS_I2C_BLOCK_DATA &&
+	   (read_write == I2C_SMBUS_READ || size == I2C_SMBUS_PROC_CALL_PEC ||
+	    size == I2C_SMBUS_BLOCK_PROC_CALL_PEC)) {
+		if(i2c_smbus_check_pec(addr, command, size, partial, data))
+			return -1;
+	}
 	return res;
 }
 
@@ -1228,136 +1446,37 @@
 	printk(KERN_INFO "i2c-core.o: i2c core module version %s (%s)\n", I2C_VERSION, I2C_DATE);
 	memset(adapters,0,sizeof(adapters));
 	memset(drivers,0,sizeof(drivers));
-	adap_count=0;
-	driver_count=0;
 
-	init_MUTEX(&adap_lock);
-	init_MUTEX(&driver_lock);
-	
-	i2cproc_init();
-	
+#ifdef CONFIG_PROC_FS
+	return i2cproc_init();
+#else
 	return 0;
-}
-
-#ifndef MODULE
-#ifdef CONFIG_I2C_CHARDEV
-	extern int i2c_dev_init(void);
-#endif
-#ifdef CONFIG_I2C_ALGOBIT
-	extern int i2c_algo_bit_init(void);
-#endif
-#ifdef CONFIG_I2C_PHILIPSPAR
-	extern int i2c_bitlp_init(void);
-#endif
-#ifdef CONFIG_I2C_ELV
-	extern int i2c_bitelv_init(void);
-#endif
-#ifdef CONFIG_I2C_VELLEMAN
-	extern int i2c_bitvelle_init(void);
-#endif
-#ifdef CONFIG_I2C_BITVIA
-	extern int i2c_bitvia_init(void);
-#endif
-
-#ifdef CONFIG_I2C_ALGOPCF
-	extern int i2c_algo_pcf_init(void);	
-#endif
-#ifdef CONFIG_I2C_ELEKTOR
-	extern int i2c_pcfisa_init(void);
-#endif
-
-#ifdef CONFIG_I2C_ALGO8XX
-	extern int i2c_algo_8xx_init(void);
-#endif
-#ifdef CONFIG_I2C_RPXLITE
-	extern int i2c_rpx_init(void);
-#endif
-
-#ifdef CONFIG_I2C_ALGO_SIBYTE
-	extern int i2c_algo_sibyte_init(void);
-	extern int i2c_sibyte_init(void);
-#endif
-#ifdef CONFIG_I2C_MAX1617
-	extern int i2c_max1617_init(void);
 #endif
+}
 
-#ifdef CONFIG_I2C_PROC
-	extern int sensors_init(void);
+static void __exit i2c_exit(void) 
+{
+#ifdef CONFIG_PROC_FS
+	i2cproc_cleanup();
 #endif
+}
 
-/* This is needed for automatic patch generation: sensors code starts here */
-/* This is needed for automatic patch generation: sensors code ends here   */
-
+/* leave this in for now simply to make patching easier so we don't have
+   to remove the call in drivers/char/mem.c */
 int __init i2c_init_all(void)
 {
-	/* --------------------- global ----- */
-	i2c_init();
-
-#ifdef CONFIG_I2C_CHARDEV
-	i2c_dev_init();
-#endif
-	/* --------------------- bit -------- */
-#ifdef CONFIG_I2C_ALGOBIT
-	i2c_algo_bit_init();
-#endif
-#ifdef CONFIG_I2C_PHILIPSPAR
-	i2c_bitlp_init();
-#endif
-#ifdef CONFIG_I2C_ELV
-	i2c_bitelv_init();
-#endif
-#ifdef CONFIG_I2C_VELLEMAN
-	i2c_bitvelle_init();
-#endif
-
-	/* --------------------- pcf -------- */
-#ifdef CONFIG_I2C_ALGOPCF
-	i2c_algo_pcf_init();	
-#endif
-#ifdef CONFIG_I2C_ELEKTOR
-	i2c_pcfisa_init();
-#endif
-
-	/* --------------------- 8xx -------- */
-#ifdef CONFIG_I2C_ALGO8XX
-	i2c_algo_8xx_init();
-#endif
-#ifdef CONFIG_I2C_RPXLITE
-	i2c_rpx_init();
-#endif
-
-	/* --------------------- SiByte -------- */
-#ifdef CONFIG_I2C_ALGO_SIBYTE
-	i2c_algo_sibyte_init();
-	i2c_sibyte_init();
-#endif
-#ifdef CONFIG_I2C_MAX1617
-	i2c_max1617_init();
-#endif
-
-	/* -------------- proc interface ---- */
-#ifdef CONFIG_I2C_PROC
-	sensors_init();
-#endif
-/* This is needed for automatic patch generation: sensors code starts here */
-/* This is needed for automatic patch generation: sensors code ends here */
-
 	return 0;
 }
 
-#endif
-
-
-
 EXPORT_SYMBOL(i2c_add_adapter);
 EXPORT_SYMBOL(i2c_del_adapter);
 EXPORT_SYMBOL(i2c_add_driver);
 EXPORT_SYMBOL(i2c_del_driver);
 EXPORT_SYMBOL(i2c_attach_client);
 EXPORT_SYMBOL(i2c_detach_client);
-EXPORT_SYMBOL(i2c_inc_use_client);
-EXPORT_SYMBOL(i2c_dec_use_client);
+#if 0
 EXPORT_SYMBOL(i2c_get_client);
+#endif
 EXPORT_SYMBOL(i2c_use_client);
 EXPORT_SYMBOL(i2c_release_client);
 EXPORT_SYMBOL(i2c_check_addr);
@@ -1381,11 +1500,12 @@
 EXPORT_SYMBOL(i2c_smbus_process_call);
 EXPORT_SYMBOL(i2c_smbus_read_block_data);
 EXPORT_SYMBOL(i2c_smbus_write_block_data);
+EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data);
+EXPORT_SYMBOL(i2c_smbus_write_i2c_block_data);
 
 EXPORT_SYMBOL(i2c_get_functionality);
 EXPORT_SYMBOL(i2c_check_functionality);
 
-#ifdef MODULE
 MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>");
 MODULE_DESCRIPTION("I2C-Bus main module");
 MODULE_LICENSE("GPL");
@@ -1393,13 +1513,5 @@
 MODULE_PARM(i2c_debug, "i");
 MODULE_PARM_DESC(i2c_debug,"debug level");
 
-int init_module(void) 
-{
-	return i2c_init();
-}
-
-void cleanup_module(void) 
-{
-	i2cproc_cleanup();
-}
-#endif
+module_init(i2c_init);
+module_exit(i2c_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-dev.c linux-2.4.27-leo/drivers/i2c/i2c-dev.c
--- linux-2.4.27/drivers/i2c/i2c-dev.c	2004-02-20 14:11:41.000000000 +0000
+++ linux-2.4.27-leo/drivers/i2c/i2c-dev.c	2004-09-20 21:34:38.000000000 +0100
@@ -28,9 +28,8 @@
 /* The devfs code is contributed by Philipp Matthias Hahn 
    <pmhahn@titan.lahn.de> */
 
-/* $Id: i2c-dev.c,v 1.40 2001/08/25 01:28:01 mds Exp $ */
+/* $Id: i2c-dev.c,v 1.57 2003/12/22 20:03:39 khali Exp $ */
 
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/fs.h>
@@ -39,21 +38,14 @@
 #ifdef CONFIG_DEVFS_FS
 #include <linux/devfs_fs_kernel.h>
 #endif
-
-
-/* If you want debugging uncomment: */
-/* #define DEBUG */
-
 #include <linux/init.h>
-#include <asm/uaccess.h>
-
 #include <linux/i2c.h>
 #include <linux/i2c-dev.h>
+#include <asm/uaccess.h>
+
+/* If you want debugging uncomment: */
+/* #define DEBUG */
 
-#ifdef MODULE
-extern int init_module(void);
-extern int cleanup_module(void);
-#endif /* def MODULE */
 
 /* struct file_operations changed too often in the 2.1 series for nice code */
 
@@ -73,22 +65,14 @@
 static int i2cdev_command(struct i2c_client *client, unsigned int cmd,
                            void *arg);
 
-#ifdef MODULE
-static
-#else
-extern
-#endif
-       int __init i2c_dev_init(void);
-static int i2cdev_cleanup(void);
-
 static struct file_operations i2cdev_fops = {
-	owner:		THIS_MODULE,
-	llseek:		no_llseek,
-	read:		i2cdev_read,
-	write:		i2cdev_write,
-	ioctl:		i2cdev_ioctl,
-	open:		i2cdev_open,
-	release:	i2cdev_release,
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.read		= i2cdev_read,
+	.write		= i2cdev_write,
+	.ioctl		= i2cdev_ioctl,
+	.open		= i2cdev_open,
+	.release	= i2cdev_release,
 };
 
 #define I2CDEV_ADAPS_MAX I2C_ADAP_MAX
@@ -99,28 +83,22 @@
 #endif
 
 static struct i2c_driver i2cdev_driver = {
-	name:		"i2c-dev dummy driver",
-	id:		I2C_DRIVERID_I2CDEV,
-	flags:		I2C_DF_DUMMY,
-	attach_adapter:	i2cdev_attach_adapter,
-	detach_client:	i2cdev_detach_client,
-	command:	i2cdev_command,
-/*	inc_use:	NULL,
-	dec_use:	NULL, */
+	.owner		= THIS_MODULE, /* not really used */
+	.name		= "i2c-dev dummy driver",
+	.id		= I2C_DRIVERID_I2CDEV,
+	.flags		= I2C_DF_DUMMY,
+	.attach_adapter	= i2cdev_attach_adapter,
+	.detach_client	= i2cdev_detach_client,
+	.command	= i2cdev_command,
 };
 
 static struct i2c_client i2cdev_client_template = {
-	name:		"I2C /dev entry",
-	id:		1,
-	flags:		0,
-	addr:		-1,
-/*	adapter:	NULL, */
-	driver:		&i2cdev_driver,
-/*	data:		NULL */
+	.name		= "I2C /dev entry",
+	.id		= 1,
+	.addr		= -1,
+	.driver		= &i2cdev_driver,
 };
 
-static int i2cdev_initialized;
-
 static ssize_t i2cdev_read (struct file *file, char *buf, size_t count,
                             loff_t *offset)
 {
@@ -142,7 +120,7 @@
 		return -ENOMEM;
 
 #ifdef DEBUG
-	printk(KERN_DEBUG "i2c-dev.o: i2c-%d reading %d bytes.\n",MINOR(inode->i_rdev),
+	printk(KERN_DEBUG "i2c-dev.o: i2c-%d reading %d bytes.\n",minor(inode->i_rdev),
 	       count);
 #endif
 
@@ -177,7 +155,7 @@
 	}
 
 #ifdef DEBUG
-	printk(KERN_DEBUG "i2c-dev.o: i2c-%d writing %d bytes.\n",MINOR(inode->i_rdev),
+	printk(KERN_DEBUG "i2c-dev.o: i2c-%d writing %d bytes.\n",minor(inode->i_rdev),
 	       count);
 #endif
 	ret = i2c_master_send(client,tmp,count);
@@ -199,7 +177,7 @@
 
 #ifdef DEBUG
 	printk(KERN_DEBUG "i2c-dev.o: i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n", 
-	       MINOR(inode->i_rdev),cmd, arg);
+	       minor(inode->i_rdev),cmd, arg);
 #endif /* DEBUG */
 
 	switch ( cmd ) {
@@ -218,6 +196,12 @@
 		else
 			client->flags &= ~I2C_M_TEN;
 		return 0;
+	case I2C_PEC:
+		if (arg)
+			client->flags |= I2C_CLIENT_PEC;
+		else
+			client->flags &= ~I2C_CLIENT_PEC;
+		return 0;
 	case I2C_FUNCS:
 		funcs = i2c_get_functionality(client->adapter);
 		return (copy_to_user((unsigned long *)arg,&funcs,
@@ -318,7 +302,8 @@
 		    (data_arg.size != I2C_SMBUS_WORD_DATA) &&
 		    (data_arg.size != I2C_SMBUS_PROC_CALL) &&
 		    (data_arg.size != I2C_SMBUS_BLOCK_DATA) &&
-		    (data_arg.size != I2C_SMBUS_I2C_BLOCK_DATA)) {
+		    (data_arg.size != I2C_SMBUS_I2C_BLOCK_DATA) &&
+		    (data_arg.size != I2C_SMBUS_BLOCK_PROC_CALL)) {
 #ifdef DEBUG
 			printk(KERN_DEBUG "i2c-dev.o: size out of range (%x) in ioctl I2C_SMBUS.\n",
 			       data_arg.size);
@@ -361,10 +346,11 @@
 		else if ((data_arg.size == I2C_SMBUS_WORD_DATA) || 
 		         (data_arg.size == I2C_SMBUS_PROC_CALL))
 			datasize = sizeof(data_arg.data->word);
-		else /* size == I2C_SMBUS_BLOCK_DATA */
+		else /* size == smbus block, i2c block, or block proc. call */
 			datasize = sizeof(data_arg.data->block);
 
 		if ((data_arg.size == I2C_SMBUS_PROC_CALL) || 
+		    (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) || 
 		    (data_arg.read_write == I2C_SMBUS_WRITE)) {
 			if (copy_from_user(&temp, data_arg.data, datasize))
 				return -EFAULT;
@@ -373,6 +359,7 @@
 		      data_arg.read_write,
 		      data_arg.command,data_arg.size,&temp);
 		if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) || 
+		              (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) || 
 			      (data_arg.read_write == I2C_SMBUS_READ))) {
 			if (copy_to_user(data_arg.data, &temp, datasize))
 				return -EFAULT;
@@ -387,7 +374,7 @@
 
 int i2cdev_open (struct inode *inode, struct file *file)
 {
-	unsigned int minor = MINOR(inode->i_rdev);
+	unsigned int minor = minor(inode->i_rdev);
 	struct i2c_client *client;
 
 	if ((minor >= I2CDEV_ADAPS_MAX) || ! (i2cdev_adaps[minor])) {
@@ -403,11 +390,13 @@
 	if(! (client = kmalloc(sizeof(struct i2c_client),GFP_KERNEL)))
 		return -ENOMEM;
 	memcpy(client,&i2cdev_client_template,sizeof(struct i2c_client));
+
+	/* registered with adapter, passed as client to user */
 	client->adapter = i2cdev_adaps[minor];
 	file->private_data = client;
 
-	if (i2cdev_adaps[minor]->inc_use)
-		i2cdev_adaps[minor]->inc_use(i2cdev_adaps[minor]);
+	if(client->adapter->owner)
+		__MOD_INC_USE_COUNT(client->adapter->owner);
 
 #ifdef DEBUG
 	printk(KERN_DEBUG "i2c-dev.o: opened i2c-%d\n",minor);
@@ -417,16 +406,19 @@
 
 static int i2cdev_release (struct inode *inode, struct file *file)
 {
-	unsigned int minor = MINOR(inode->i_rdev);
-	kfree(file->private_data);
-	file->private_data=NULL;
+	struct i2c_client *client;
+#ifdef DEBUG
+	unsigned int minor = minor(inode->i_rdev);
+#endif
+
+	client = file->private_data;
+	file->private_data = NULL;
+	if(client->adapter->owner)
+		__MOD_DEC_USE_COUNT(client->adapter->owner);
+	kfree(client);
 #ifdef DEBUG
 	printk(KERN_DEBUG "i2c-dev.o: Closed: i2c-%d\n", minor);
 #endif
-	lock_kernel();
-	if (i2cdev_adaps[minor]->dec_use)
-		i2cdev_adaps[minor]->dec_use(i2cdev_adaps[minor]);
-	unlock_kernel();
 	return 0;
 }
 
@@ -451,7 +443,7 @@
 		devfs_i2c[i] = devfs_register (devfs_handle, name,
 			DEVFS_FL_DEFAULT, I2C_MAJOR, i,
 			S_IFCHR | S_IRUSR | S_IWUSR,
-			&i2cdev_fops, NULL);
+			&i2cdev_fops, adap);
 #endif
 		printk(KERN_DEBUG "i2c-dev.o: Registered '%s' as minor %d\n",adap->name,i);
 	} else {
@@ -479,13 +471,12 @@
 	return -1;
 }
 
-int __init i2c_dev_init(void)
+static int __init i2c_dev_init(void)
 {
 	int res;
 
 	printk(KERN_INFO "i2c-dev.o: i2c /dev entries driver module version %s (%s)\n", I2C_VERSION, I2C_DATE);
 
-	i2cdev_initialized = 0;
 #ifdef CONFIG_DEVFS_FS
 	if (devfs_register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops)) {
 #else
@@ -498,63 +489,31 @@
 #ifdef CONFIG_DEVFS_FS
 	devfs_handle = devfs_mk_dir(NULL, "i2c", NULL);
 #endif
-	i2cdev_initialized ++;
-
 	if ((res = i2c_add_driver(&i2cdev_driver))) {
 		printk(KERN_ERR "i2c-dev.o: Driver registration failed, module not inserted.\n");
-		i2cdev_cleanup();
+#ifdef CONFIG_DEVFS_FS
+		devfs_unregister(devfs_handle);
+#endif
+		unregister_chrdev(I2C_MAJOR,"i2c");
 		return res;
 	}
-	i2cdev_initialized ++;
 	return 0;
 }
 
-int i2cdev_cleanup(void)
+static void __exit i2c_dev_exit(void)
 {
-	int res;
-
-	if (i2cdev_initialized >= 2) {
-		if ((res = i2c_del_driver(&i2cdev_driver))) {
-			printk("i2c-dev.o: Driver deregistration failed, "
-			       "module not removed.\n");
-			return res;
-		}
-	i2cdev_initialized --;
-	}
-
-	if (i2cdev_initialized >= 1) {
+	i2c_del_driver(&i2cdev_driver);
 #ifdef CONFIG_DEVFS_FS
-		devfs_unregister(devfs_handle);
-		if ((res = devfs_unregister_chrdev(I2C_MAJOR, "i2c"))) {
-#else
-		if ((res = unregister_chrdev(I2C_MAJOR,"i2c"))) {
+	devfs_unregister(devfs_handle);
 #endif
-			printk("i2c-dev.o: unable to release major %d for i2c bus\n",
-			       I2C_MAJOR);
-			return res;
-		}
-		i2cdev_initialized --;
-	}
-	return 0;
+	unregister_chrdev(I2C_MAJOR,"i2c");
 }
 
 EXPORT_NO_SYMBOLS;
 
-#ifdef MODULE
-
 MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and Simon G. Vogl <simon@tk.uni-linz.ac.at>");
 MODULE_DESCRIPTION("I2C /dev entries driver");
 MODULE_LICENSE("GPL");
 
-int init_module(void)
-{
-	return i2c_dev_init();
-}
-
-int cleanup_module(void)
-{
-	return i2cdev_cleanup();
-}
-
-#endif /* def MODULE */
-
+module_init(i2c_dev_init);
+module_exit(i2c_dev_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-elektor.c linux-2.4.27-leo/drivers/i2c/i2c-elektor.c
--- linux-2.4.27/drivers/i2c/i2c-elektor.c	2004-02-20 14:11:41.000000000 +0000
+++ linux-2.4.27-leo/drivers/i2c/i2c-elektor.c	2004-09-20 21:34:38.000000000 +0100
@@ -31,23 +31,22 @@
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/init.h>
+#include <linux/interrupt.h>
 #include <linux/pci.h>
-#include <asm/irq.h>
-#include <asm/io.h>
-
+#include <linux/wait.h>
 #include <linux/i2c.h>
 #include <linux/i2c-algo-pcf.h>
-#include <linux/i2c-elektor.h>
-#include "i2c-pcf8584.h"
+#include <asm/io.h>
+#include <asm/irq.h>
 
 #define DEFAULT_BASE 0x330
 
-static int base   = 0;
-static int irq    = 0;
+static int base;
+static int irq;
 static int clock  = 0x1c;
 static int own    = 0x55;
-static int mmapped = 0;
-static int i2c_debug = 0;
+static int mmapped;
+static int i2c_debug;
 
 /* vdovikin: removed static struct i2c_pcf_isa gpi; code - 
   this module in real supports only one device, due to missing arguments
@@ -56,6 +55,7 @@
 
 static wait_queue_head_t pcf_wait;
 static int pcf_pending;
+static spinlock_t irq_driver_lock = SPIN_LOCK_UNLOCKED;
 
 /* ----- global defines -----------------------------------------------	*/
 #define DEB(x)	if (i2c_debug>=1) x
@@ -63,13 +63,24 @@
 #define DEB3(x) if (i2c_debug>=3) x
 #define DEBE(x)	x	/* error messages 				*/
 
+
+/* compatibility */
+#ifndef isa_readb
+#define isa_readb readb
+#endif
+
+#ifndef isa_writeb
+#define isa_writeb writeb
+#endif
+
 /* ----- local functions ----------------------------------------------	*/
 
 static void pcf_isa_setbyte(void *data, int ctl, int val)
 {
 	int address = ctl ? (base + 1) : base;
 
-	if (ctl && irq) {
+	/* enable irq if any specified for serial operation */
+	if (ctl && irq && (val & I2C_PCF_ESO)) {
 		val |= I2C_PCF_ENI;
 	}
 
@@ -81,10 +92,10 @@
 		break;
 	case 2: /* double mapped I/O needed for UP2000 board,
                    I don't know why this... */
-		writeb(val, address);
+		isa_writeb(val, address);
 		/* fall */
 	case 1: /* memory mapped I/O */
-		writeb(val, address);
+		isa_writeb(val, address);
 		break;
 	}
 }
@@ -92,7 +103,7 @@
 static int pcf_isa_getbyte(void *data, int ctl)
 {
 	int address = ctl ? (base + 1) : base;
-	int val = mmapped ? readb(address) : inb(address);
+	int val = mmapped ? isa_readb(address) : inb(address);
 
 	DEB3(printk(KERN_DEBUG "i2c-elektor.o: Read 0x%X 0x%02X\n", address, val));
 
@@ -115,12 +126,12 @@
 	int timeout = 2;
 
 	if (irq > 0) {
-		cli();
+		spin_lock_irq(&irq_driver_lock);
 		if (pcf_pending == 0) {
 			interruptible_sleep_on_timeout(&pcf_wait, timeout*HZ );
 		} else
 			pcf_pending = 0;
-		sti();
+		spin_unlock_irq(&irq_driver_lock);
 	} else {
 		udelay(100);
 	}
@@ -136,13 +147,11 @@
 static int pcf_isa_init(void)
 {
 	if (!mmapped) {
-		if (check_region(base, 2) < 0 ) {
+		if (!request_region(base, 2, "i2c (isa bus adapter)")) {
 			printk(KERN_ERR
 			       "i2c-elektor.o: requested I/O region (0x%X:2) "
 			       "is in use.\n", base);
 			return -ENODEV;
-		} else {
-			request_region(base, 2, "i2c (isa bus adapter)");
 		}
 	}
 	if (irq > 0) {
@@ -156,70 +165,29 @@
 }
 
 
-static void __exit pcf_isa_exit(void)
-{
-	if (irq > 0) {
-		disable_irq(irq);
-		free_irq(irq, 0);
-	}
-	if (!mmapped) {
-		release_region(base , 2);
-	}
-}
-
-
-static int pcf_isa_reg(struct i2c_client *client)
-{
-	return 0;
-}
-
-
-static int pcf_isa_unreg(struct i2c_client *client)
-{
-	return 0;
-}
-
-static void pcf_isa_inc_use(struct i2c_adapter *adap)
-{
-#ifdef MODULE
-	MOD_INC_USE_COUNT;
-#endif
-}
-
-static void pcf_isa_dec_use(struct i2c_adapter *adap)
-{
-#ifdef MODULE
-	MOD_DEC_USE_COUNT;
-#endif
-}
-
-
 /* ------------------------------------------------------------------------
  * Encapsulate the above functions in the correct operations structure.
  * This is only done when more than one hardware adapter is supported.
  */
 static struct i2c_algo_pcf_data pcf_isa_data = {
-	NULL,
-	pcf_isa_setbyte,
-	pcf_isa_getbyte,
-	pcf_isa_getown,
-	pcf_isa_getclock,
-	pcf_isa_waitforpin,
-	10, 10, 100,		/*	waits, timeout */
+	.setpcf	    = pcf_isa_setbyte,
+	.getpcf	    = pcf_isa_getbyte,
+	.getown	    = pcf_isa_getown,
+	.getclock   = pcf_isa_getclock,
+	.waitforpin = pcf_isa_waitforpin,
+	.udelay	    = 10,
+	.mdelay	    = 10,
+	.timeout    = HZ,
 };
 
 static struct i2c_adapter pcf_isa_ops = {
-	"PCF8584 ISA adapter",
-	I2C_HW_P_ELEK,
-	NULL,
-	&pcf_isa_data,
-	pcf_isa_inc_use,
-	pcf_isa_dec_use,
-	pcf_isa_reg,
-	pcf_isa_unreg,
+	.owner		   = THIS_MODULE,
+	.name		   = "PCF8584 ISA adapter",
+	.id		   = I2C_HW_P_ELEK,
+	.algo_data	   = &pcf_isa_data,
 };
 
-int __init i2c_pcfisa_init(void) 
+static int __init i2c_pcfisa_init(void) 
 {
 #ifdef __alpha__
 	/* check to see we have memory mapped PCF8584 connected to the 
@@ -277,22 +245,41 @@
 	}
 
 	init_waitqueue_head(&pcf_wait);
-	if (pcf_isa_init() == 0) {
-		if (i2c_pcf_add_bus(&pcf_isa_ops) < 0)
-			return -ENODEV;
-	} else {
+	if (pcf_isa_init())
 		return -ENODEV;
-	}
+	if (i2c_pcf_add_bus(&pcf_isa_ops) < 0)
+		goto fail;
 	
 	printk(KERN_DEBUG "i2c-elektor.o: found device at %#x.\n", base);
 
 	return 0;
+
+ fail:
+	if (irq > 0) {
+		disable_irq(irq);
+		free_irq(irq, 0);
+	}
+
+	if (!mmapped)
+		release_region(base , 2);
+	return -ENODEV;
 }
 
+static void __exit i2c_pcfisa_exit(void)
+{
+	i2c_pcf_del_bus(&pcf_isa_ops);
+
+	if (irq > 0) {
+		disable_irq(irq);
+		free_irq(irq, 0);
+	}
+
+	if (!mmapped)
+		release_region(base , 2);
+}
 
 EXPORT_NO_SYMBOLS;
 
-#ifdef MODULE
 MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>");
 MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter");
 MODULE_LICENSE("GPL");
@@ -304,15 +291,5 @@
 MODULE_PARM(mmapped, "i");
 MODULE_PARM(i2c_debug, "i");
 
-int init_module(void) 
-{
-	return i2c_pcfisa_init();
-}
-
-void cleanup_module(void) 
-{
-	i2c_pcf_del_bus(&pcf_isa_ops);
-	pcf_isa_exit();
-}
-
-#endif
+module_init(i2c_pcfisa_init);
+module_exit(i2c_pcfisa_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-elv.c linux-2.4.27-leo/drivers/i2c/i2c-elv.c
--- linux-2.4.27/drivers/i2c/i2c-elv.c	2004-02-20 14:11:41.000000000 +0000
+++ linux-2.4.27-leo/drivers/i2c/i2c-elv.c	2004-09-20 21:34:38.000000000 +0100
@@ -21,21 +21,18 @@
 /* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
    Frodo Looijaard <frodol@dds.nl> */
 
-/* $Id: i2c-elv.c,v 1.17 2001/07/29 02:44:25 mds Exp $ */
+/* $Id: i2c-elv.c,v 1.29 2003/12/22 20:03:11 khali Exp $ */
 
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/init.h>
-
-#include <asm/uaccess.h>
-
 #include <linux/ioport.h>
-#include <asm/io.h>
 #include <linux/errno.h>
 #include <linux/i2c.h>
 #include <linux/i2c-algo-bit.h>
+#include <asm/io.h>
 
 #define DEFAULT_BASE 0x378
 static int base=0;
@@ -89,58 +86,31 @@
 
 static int bit_elv_init(void)
 {
-	if (check_region(base,(base == 0x3bc)? 3 : 8) < 0 ) {
-		return -ENODEV;	
-	} else {
-						/* test for ELV adap. 	*/
-		if (inb(base+1) & 0x80) {	/* BUSY should be high	*/
-			DEBINIT(printk(KERN_DEBUG "i2c-elv.o: Busy was low.\n"));
-			return -ENODEV;
-		} else {
-			outb(0x0c,base+2);	/* SLCT auf low		*/
-			udelay(400);
-			if ( !(inb(base+1) && 0x10) ) {
-				outb(0x04,base+2);
-				DEBINIT(printk(KERN_DEBUG "i2c-elv.o: Select was high.\n"));
-				return -ENODEV;
-			}
-		}
-		request_region(base,(base == 0x3bc)? 3 : 8,
-			"i2c (ELV adapter)");
-		PortData = 0;
-		bit_elv_setsda((void*)base,1);
-		bit_elv_setscl((void*)base,1);
+	if (!request_region(base, (base == 0x3bc) ? 3 : 8,
+				"i2c (ELV adapter)"))
+		return -ENODEV;
+
+	if (inb(base+1) & 0x80) {	/* BUSY should be high	*/
+		DEBINIT(printk(KERN_DEBUG "i2c-elv.o: Busy was low.\n"));
+		goto fail;
+	} 
+
+	outb(0x0c,base+2);	/* SLCT auf low		*/
+	udelay(400);
+	if (!(inb(base+1) && 0x10)) {
+		outb(0x04,base+2);
+		DEBINIT(printk(KERN_DEBUG "i2c-elv.o: Select was high.\n"));
+		goto fail;
 	}
-	return 0;
-}
-
-static void __exit bit_elv_exit(void)
-{
-	release_region( base , (base == 0x3bc)? 3 : 8 );
-}
-
-static int bit_elv_reg(struct i2c_client *client)
-{
-	return 0;
-}
 
-static int bit_elv_unreg(struct i2c_client *client)
-{
+	PortData = 0;
+	bit_elv_setsda((void*)base,1);
+	bit_elv_setscl((void*)base,1);
 	return 0;
-}
 
-static void bit_elv_inc_use(struct i2c_adapter *adap)
-{
-#ifdef MODULE
-	MOD_INC_USE_COUNT;
-#endif
-}
-
-static void bit_elv_dec_use(struct i2c_adapter *adap)
-{
-#ifdef MODULE
-	MOD_DEC_USE_COUNT;
-#endif
+fail:
+	release_region(base , (base == 0x3bc) ? 3 : 8);
+	return -ENODEV;
 }
 
 /* ------------------------------------------------------------------------
@@ -148,26 +118,23 @@
  * This is only done when more than one hardware adapter is supported.
  */
 static struct i2c_algo_bit_data bit_elv_data = {
-	NULL,
-	bit_elv_setsda,
-	bit_elv_setscl,
-	bit_elv_getsda,
-	bit_elv_getscl,
-	80, 80, 100,		/*	waits, timeout */
+	.setsda		= bit_elv_setsda,
+	.setscl		= bit_elv_setscl,
+	.getsda		= bit_elv_getsda,
+	.getscl		= bit_elv_getscl,
+	.udelay		= 80,
+	.mdelay		= 80,
+	.timeout	= HZ
 };
 
 static struct i2c_adapter bit_elv_ops = {
-	"ELV Parallel port adaptor",
-	I2C_HW_B_ELV,
-	NULL,
-	&bit_elv_data,
-	bit_elv_inc_use,
-	bit_elv_dec_use,
-	bit_elv_reg,
-	bit_elv_unreg,	
+	.owner		= THIS_MODULE,
+	.name		= "ELV Parallel port adaptor",
+	.id		= I2C_HW_B_ELV,
+	.algo_data	= &bit_elv_data,
 };
 
-int __init i2c_bitelv_init(void)
+static int __init i2c_bitelv_init(void)
 {
 	printk(KERN_INFO "i2c-elv.o: i2c ELV parallel port adapter module version %s (%s)\n", I2C_VERSION, I2C_DATE);
 	if (base==0) {
@@ -193,25 +160,19 @@
 	return 0;
 }
 
+static void __exit i2c_bitelv_exit(void)
+{
+	i2c_bit_del_bus(&bit_elv_ops);
+	release_region(base, (base == 0x3bc) ? 3 : 8);
+}
 
 EXPORT_NO_SYMBOLS;
 
-#ifdef MODULE
 MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>");
 MODULE_DESCRIPTION("I2C-Bus adapter routines for ELV parallel port adapter");
 MODULE_LICENSE("GPL");
 
 MODULE_PARM(base, "i");
 
-int init_module(void)
-{
-	return i2c_bitelv_init();
-}
-
-void cleanup_module(void)
-{
-	i2c_bit_del_bus(&bit_elv_ops);
-	bit_elv_exit();
-}
-
-#endif
+module_init(i2c_bitelv_init);
+module_exit(i2c_bitelv_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-frodo.c linux-2.4.27-leo/drivers/i2c/i2c-frodo.c
--- linux-2.4.27/drivers/i2c/i2c-frodo.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-frodo.c	2004-09-20 21:34:38.000000000 +0100
@@ -0,0 +1,83 @@
+
+/*
+ * linux/drivers/i2c/i2c-frodo.c
+ *
+ * Author: Abraham van der Merwe <abraham@2d3d.co.za>
+ *
+ * An I2C adapter driver for the 2d3D, Inc. StrongARM SA-1110
+ * Development board (Frodo).
+ *
+ * This source code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <asm/hardware.h>
+
+
+static void frodo_setsda (void *data,int state)
+{
+	if (state)
+		FRODO_CPLD_I2C |= FRODO_I2C_SDA_OUT;
+	else
+		FRODO_CPLD_I2C &= ~FRODO_I2C_SDA_OUT;
+}
+
+static void frodo_setscl (void *data,int state)
+{
+	if (state)
+		FRODO_CPLD_I2C |= FRODO_I2C_SCL_OUT;
+	else
+		FRODO_CPLD_I2C &= ~FRODO_I2C_SCL_OUT;
+}
+
+static int frodo_getsda (void *data)
+{
+	return ((FRODO_CPLD_I2C & FRODO_I2C_SDA_IN) != 0);
+}
+
+static int frodo_getscl (void *data)
+{
+	return ((FRODO_CPLD_I2C & FRODO_I2C_SCL_IN) != 0);
+}
+
+static struct i2c_algo_bit_data bit_frodo_data = {
+	.setsda		= frodo_setsda,
+	.setscl		= frodo_setscl,
+	.getsda		= frodo_getsda,
+	.getscl		= frodo_getscl,
+	.udelay		= 80,
+	.mdelay		= 80,
+	.timeout	= HZ
+};
+
+static struct i2c_adapter frodo_ops = {
+	.owner			= THIS_MODULE,
+	.name			= "Frodo adapter driver",
+	.id			= I2C_HW_B_FRODO,
+	.algo_data		= &bit_frodo_data,
+};
+
+static int __init i2c_frodo_init (void)
+{
+	return i2c_bit_add_bus(&frodo_ops);
+}
+
+static void __exit i2c_frodo_exit (void)
+{
+	i2c_bit_del_bus(&frodo_ops);
+}
+
+MODULE_AUTHOR ("Abraham van der Merwe <abraham@2d3d.co.za>");
+MODULE_DESCRIPTION ("I2C-Bus adapter routines for Frodo");
+MODULE_LICENSE ("GPL");
+
+module_init (i2c_frodo_init);
+module_exit (i2c_frodo_exit);
+
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-hydra.c linux-2.4.27-leo/drivers/i2c/i2c-hydra.c
--- linux-2.4.27/drivers/i2c/i2c-hydra.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-hydra.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,175 @@
+/*
+    i2c-hydra.c - Part of lm_sensors,  Linux kernel modules
+                  for hardware monitoring
+
+    i2c Support for the Apple `Hydra' Mac I/O
+
+    Copyright (c) 1999 Geert Uytterhoeven <geert@linux-m68k.org>
+
+    Based on i2c Support for Via Technologies 82C586B South Bridge
+    Copyright (c) 1998, 1999 Kyösti Mälkki <kmalkki@cc.hut.fi>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/param.h>	/* for HZ */
+
+MODULE_LICENSE("GPL");
+
+
+#define HYDRA_CACHE_PD	0x00000030
+
+#define HYDRA_CPD_PD0	0x00000001	/* CachePD lines */
+#define HYDRA_CPD_PD1	0x00000002
+#define HYDRA_CPD_PD2	0x00000004
+#define HYDRA_CPD_PD3	0x00000008
+
+#define HYDRA_SCLK	HYDRA_CPD_PD0
+#define HYDRA_SDAT	HYDRA_CPD_PD1
+#define HYDRA_SCLK_OE	0x00000010
+#define HYDRA_SDAT_OE	0x00000020
+
+static unsigned long hydra_base;
+
+static inline void pdregw(u32 val)
+{
+	writel(val, hydra_base + HYDRA_CACHE_PD);
+}
+
+static inline u32 pdregr(void)
+{
+	u32 val = readl(hydra_base + HYDRA_CACHE_PD);
+	return val;
+}
+
+static void bit_hydra_setscl(void *data, int state)
+{
+	u32 val = pdregr();
+	if (state)
+		val &= ~HYDRA_SCLK_OE;
+	else {
+		val &= ~HYDRA_SCLK;
+		val |= HYDRA_SCLK_OE;
+	}
+	pdregw(val);
+	pdregr();	/* flush posted write */
+}
+
+static void bit_hydra_setsda(void *data, int state)
+{
+	u32 val = pdregr();
+	if (state)
+		val &= ~HYDRA_SDAT_OE;
+	else {
+		val &= ~HYDRA_SDAT;
+		val |= HYDRA_SDAT_OE;
+	}
+	pdregw(val);
+	pdregr();	/* flush posted write */
+}
+
+static int bit_hydra_getscl(void *data)
+{
+	return (pdregr() & HYDRA_SCLK) != 0;
+}
+
+static int bit_hydra_getsda(void *data)
+{
+	return (pdregr() & HYDRA_SDAT) != 0;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static struct i2c_algo_bit_data bit_hydra_data = {
+	.setsda		= bit_hydra_setsda,
+	.setscl		= bit_hydra_setscl,
+	.getsda		= bit_hydra_getsda,
+	.getscl		= bit_hydra_getscl,
+	.udelay		= 5,
+	.mdelay		= 5,
+	.timeout	= HZ
+};
+
+static struct i2c_adapter bit_hydra_ops = {
+	.owner		= THIS_MODULE,
+	.name		= "Hydra i2c",
+	.id		= I2C_HW_B_HYDRA,
+	.algo_data		= &bit_hydra_data,
+};
+
+static struct pci_device_id hydra_ids[] __devinitdata = {
+	{
+		.vendor =	PCI_VENDOR_ID_APPLE,
+		.device =	PCI_DEVICE_ID_APPLE_HYDRA,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{ 0, }
+};
+
+static int __devinit hydra_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	unsigned int base_addr;
+
+	base_addr = dev->resource[0].start;
+	hydra_base = (unsigned long) ioremap(base_addr, 0x100);
+
+	pdregw(0);		/* clear SCLK_OE and SDAT_OE */
+ 	return i2c_bit_add_bus(&bit_hydra_ops);
+}
+
+static void __devexit hydra_remove(struct pci_dev *dev)
+{
+	pdregw(0);	/* clear SCLK_OE and SDAT_OE */
+	i2c_bit_del_bus(&bit_hydra_ops);
+	iounmap((void *) hydra_base);
+}
+
+
+static struct pci_driver hydra_driver = {
+	.name		= "hydra smbus",
+	.id_table	= hydra_ids,
+	.probe		= hydra_probe,
+	.remove		= __devexit_p(hydra_remove),
+};
+
+static int __init i2c_hydra_init(void)
+{
+	return pci_module_init(&hydra_driver);
+}
+
+
+static void __exit i2c_hydra_exit(void)
+{
+	pci_unregister_driver(&hydra_driver);
+}
+
+
+
+MODULE_AUTHOR("Geert Uytterhoeven <geert@linux-m68k.org>");
+MODULE_DESCRIPTION("i2c for Apple Hydra Mac I/O");
+
+module_init(i2c_hydra_init);
+module_exit(i2c_hydra_exit);
+
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-i801.c linux-2.4.27-leo/drivers/i2c/i2c-i801.c
--- linux-2.4.27/drivers/i2c/i2c-i801.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-i801.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,656 @@
+/*
+    i801.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+    Copyright (c) 1998 - 2002  Frodo Looijaard <frodol@dds.nl>,
+    Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker
+    <mdsxyz123@yahoo.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+    SUPPORTED DEVICES	PCI ID
+    82801AA		2413           
+    82801AB		2423           
+    82801BA		2443           
+    82801CA/CAM		2483           
+    82801DB		24C3   (HW PEC supported, 32 byte buffer not supported)
+    82801EB		24D3   (HW PEC supported, 32 byte buffer not supported)
+    6300ESB		25A4   ("")
+    ICH6		266A
+    This driver supports several versions of Intel's I/O Controller Hubs (ICH).
+    For SMBus support, they are similar to the PIIX4 and are part
+    of Intel's '810' and other chipsets.
+    See the doc/busses/i2c-i801 file for details.
+    I2C Block Read and Process Call are not supported.
+*/
+
+/* Note: we assume there can only be one I801, with one SMBus interface */
+
+/* #define DEBUG 1 */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <asm/io.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+#include <linux/sensors_compat.h>
+
+/* 82801DB is undefined before kernel 2.4.19 */
+#ifndef PCI_DEVICE_ID_INTEL_82801DB_3
+#define PCI_DEVICE_ID_INTEL_82801DB_3      0x24c3
+#endif
+
+#ifdef I2C_FUNC_SMBUS_BLOCK_DATA_PEC
+#define HAVE_PEC
+#endif
+
+/* I801 SMBus address offsets */
+#define SMBHSTSTS	(0 + i801_smba)
+#define SMBHSTCNT	(2 + i801_smba)
+#define SMBHSTCMD	(3 + i801_smba)
+#define SMBHSTADD	(4 + i801_smba)
+#define SMBHSTDAT0	(5 + i801_smba)
+#define SMBHSTDAT1	(6 + i801_smba)
+#define SMBBLKDAT	(7 + i801_smba)
+#define SMBPEC		(8 + i801_smba)	/* ICH4 only */
+#define SMBAUXSTS	(12 + i801_smba)	/* ICH4 only */
+#define SMBAUXCTL	(13 + i801_smba)	/* ICH4 only */
+
+/* PCI Address Constants */
+#define SMBBA		0x020
+#define SMBHSTCFG	0x040
+#define SMBREV		0x008
+
+/* Host configuration bits for SMBHSTCFG */
+#define SMBHSTCFG_HST_EN	1
+#define SMBHSTCFG_SMB_SMI_EN	2
+#define SMBHSTCFG_I2C_EN	4
+
+/* Other settings */
+#define MAX_TIMEOUT		100
+#define ENABLE_INT9		0	/* set to 0x01 to enable - untested */
+
+/* I801 command constants */
+#define I801_QUICK		0x00
+#define I801_BYTE		0x04
+#define I801_BYTE_DATA		0x08
+#define I801_WORD_DATA		0x0C
+#define I801_PROC_CALL		0x10	/* later chips only, unimplemented */
+#define I801_BLOCK_DATA		0x14
+#define I801_I2C_BLOCK_DATA	0x18	/* unimplemented */
+#define I801_BLOCK_LAST		0x34
+#define I801_I2C_BLOCK_LAST	0x38	/* unimplemented */
+#define I801_START		0x40
+#define I801_PEC_EN		0x80	/* ICH4 only */
+
+/* insmod parameters */
+
+/* If force_addr is set to anything different from 0, we forcibly enable
+   the I801 at the given address. VERY DANGEROUS! */
+static int force_addr = 0;
+MODULE_PARM(force_addr, "i");
+MODULE_PARM_DESC(force_addr,
+		 "Forcibly enable the I801 at the given address. "
+		 "EXTREMELY DANGEROUS!");
+
+static int i801_transaction(void);
+static int i801_block_transaction(union i2c_smbus_data *data,
+				  char read_write, int command);
+
+static unsigned short i801_smba;
+static struct pci_dev *I801_dev;
+static int isich4;
+
+static int i801_setup(struct pci_dev *dev)
+{
+	int error_return = 0;
+	unsigned char temp;
+
+	/* Note: we keep on searching until we have found 'function 3' */
+	if(PCI_FUNC(dev->devfn) != 3)
+		return -ENODEV;
+
+	I801_dev = dev;
+	if (dev->device == PCI_DEVICE_ID_INTEL_82801DB_3 ||
+	    dev->device == 0x24d3 ||
+	    dev->device == 0x25a4)
+		isich4 = 1;
+	else
+		isich4 = 0;
+
+	/* Determine the address of the SMBus areas */
+	if (force_addr) {
+		i801_smba = force_addr & 0xfff0;
+	} else {
+		pci_read_config_word(I801_dev, SMBBA, &i801_smba);
+		i801_smba &= 0xfff0;
+		if(i801_smba == 0) {
+			dev_err(dev, "SMB base address uninitialized"
+				"- upgrade BIOS or use force_addr=0xaddr\n");
+			return -ENODEV;
+		}
+	}
+
+	if (!request_region(i801_smba, (isich4 ? 16 : 8), "i801-smbus")) {
+		dev_err(dev, "I801_smb region 0x%x already in use!\n",
+			i801_smba);
+		error_return = -EBUSY;
+		goto END;
+	}
+
+	pci_read_config_byte(I801_dev, SMBHSTCFG, &temp);
+	temp &= ~SMBHSTCFG_I2C_EN;	/* SMBus timing */
+	pci_write_config_byte(I801_dev, SMBHSTCFG, temp);
+
+	/* If force_addr is set, we program the new address here. Just to make
+	   sure, we disable the device first. */
+	if (force_addr) {
+		pci_write_config_byte(I801_dev, SMBHSTCFG, temp & 0xfe);
+		pci_write_config_word(I801_dev, SMBBA, i801_smba);
+		pci_write_config_byte(I801_dev, SMBHSTCFG, temp | 0x01);
+		dev_warn(dev, "WARNING: I801 SMBus interface set to "
+			"new address %04x!\n", i801_smba);
+	} else if ((temp & 1) == 0) {
+		pci_write_config_byte(I801_dev, SMBHSTCFG, temp | 1);
+		dev_warn(dev, "enabling SMBus device\n");
+	}
+
+	if (temp & 0x02)
+		dev_dbg(dev, "I801 using Interrupt SMI# for SMBus.\n");
+	else
+		dev_dbg(dev, "I801 using PCI Interrupt for SMBus.\n");
+
+	pci_read_config_byte(I801_dev, SMBREV, &temp);
+	dev_dbg(dev, "SMBREV = 0x%X\n", temp);
+	dev_dbg(dev, "I801_smba = 0x%X\n", i801_smba);
+
+END:
+	return error_return;
+}
+
+
+static int i801_transaction(void)
+{
+	int temp;
+	int result = 0;
+	int timeout = 0;
+
+	dev_dbg(I801_dev, "Transaction (pre): CNT=%02x, CMD=%02x,"
+		"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
+		inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
+		inb_p(SMBHSTDAT1));
+
+	/* Make sure the SMBus host is ready to start transmitting */
+	/* 0x1f = Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
+	if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
+		dev_dbg(I801_dev, "SMBus busy (%02x). Resetting... \n",
+			temp);
+		outb_p(temp, SMBHSTSTS);
+		if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
+			dev_dbg(I801_dev, "Failed! (%02x)\n", temp);
+			return -1;
+		} else {
+			dev_dbg(I801_dev, "Successfull!\n");
+		}
+	}
+
+	outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
+
+	/* We will always wait for a fraction of a second! */
+	do {
+		i2c_delay(1);
+		temp = inb_p(SMBHSTSTS);
+	} while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT));
+
+	/* If the SMBus is still busy, we give up */
+	if (timeout >= MAX_TIMEOUT) {
+		dev_dbg(I801_dev, "SMBus Timeout!\n");
+		result = -1;
+	}
+
+	if (temp & 0x10) {
+		result = -1;
+		dev_dbg(I801_dev, "Error: Failed bus transaction\n");
+	}
+
+	if (temp & 0x08) {
+		result = -1;
+		dev_err(I801_dev, "Bus collision! SMBus may be locked "
+			"until next hard reset. (sorry!)\n");
+		/* Clock stops and slave is stuck in mid-transmission */
+	}
+
+	if (temp & 0x04) {
+		result = -1;
+		dev_dbg(I801_dev, "Error: no response!\n");
+	}
+
+	if ((inb_p(SMBHSTSTS) & 0x1f) != 0x00)
+		outb_p(inb(SMBHSTSTS), SMBHSTSTS);
+
+	if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
+		dev_dbg(I801_dev, "Failed reset at end of transaction"
+			"(%02x)\n", temp);
+	}
+	dev_dbg(I801_dev, "Transaction (post): CNT=%02x, CMD=%02x, "
+		"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
+		inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
+		inb_p(SMBHSTDAT1));
+	return result;
+}
+
+/* All-inclusive block transaction function */
+static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
+				  int command)
+{
+	int i, len;
+	int smbcmd;
+	int temp;
+	int result = 0;
+	int timeout;
+	unsigned char hostc, errmask;
+
+	if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
+		if (read_write == I2C_SMBUS_WRITE) {
+			/* set I2C_EN bit in configuration register */
+			pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc);
+			pci_write_config_byte(I801_dev, SMBHSTCFG,
+					      hostc | SMBHSTCFG_I2C_EN);
+		} else {
+			dev_err(I801_dev,
+				"I2C_SMBUS_I2C_BLOCK_READ not DB!\n");
+			return -1;
+		}
+	}
+
+	if (read_write == I2C_SMBUS_WRITE) {
+		len = data->block[0];
+		if (len < 1)
+			len = 1;
+		if (len > 32)
+			len = 32;
+		outb_p(len, SMBHSTDAT0);
+		outb_p(data->block[1], SMBBLKDAT);
+	} else {
+		len = 32;	/* max for reads */
+	}
+
+	if(isich4 && command != I2C_SMBUS_I2C_BLOCK_DATA) {
+		/* set 32 byte buffer */
+	}
+
+	for (i = 1; i <= len; i++) {
+		if (i == len && read_write == I2C_SMBUS_READ)
+			smbcmd = I801_BLOCK_LAST;
+		else
+			smbcmd = I801_BLOCK_DATA;
+		outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT);
+
+		dev_dbg(I801_dev, "Block (pre %d): CNT=%02x, CMD=%02x, "
+			"ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i,
+			inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
+			inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
+
+		/* Make sure the SMBus host is ready to start transmitting */
+		temp = inb_p(SMBHSTSTS);
+		if (i == 1) {
+			/* Erronenous conditions before transaction: 
+			 * Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
+			errmask=0x9f; 
+		} else {
+			/* Erronenous conditions during transaction: 
+			 * Failed, Bus_Err, Dev_Err, Intr */
+			errmask=0x1e; 
+		}
+		if (temp & errmask) {
+			dev_dbg(I801_dev, "SMBus busy (%02x). "
+				"Resetting... \n", temp);
+			outb_p(temp, SMBHSTSTS);
+			if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) {
+				dev_err(I801_dev,
+					"Reset failed! (%02x)\n", temp);
+				result = -1;
+                                goto END;
+			}
+			if (i != 1) {
+				/* if die in middle of block transaction, fail */
+				result = -1;
+				goto END;
+			}
+		}
+
+		if (i == 1)
+			outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
+
+		/* We will always wait for a fraction of a second! */
+		timeout = 0;
+		do {
+			temp = inb_p(SMBHSTSTS);
+			i2c_delay(1);
+		}
+		    while ((!(temp & 0x80))
+			   && (timeout++ < MAX_TIMEOUT));
+
+		/* If the SMBus is still busy, we give up */
+		if (timeout >= MAX_TIMEOUT) {
+			result = -1;
+			dev_dbg(I801_dev, "SMBus Timeout!\n");
+		}
+
+		if (temp & 0x10) {
+			result = -1;
+			dev_dbg(I801_dev,
+				"Error: Failed bus transaction\n");
+		} else if (temp & 0x08) {
+			result = -1;
+			dev_err(I801_dev, "Bus collision!\n");
+		} else if (temp & 0x04) {
+			result = -1;
+			dev_dbg(I801_dev, "Error: no response!\n");
+		}
+
+		if (i == 1 && read_write == I2C_SMBUS_READ) {
+			len = inb_p(SMBHSTDAT0);
+			if (len < 1)
+				len = 1;
+			if (len > 32)
+				len = 32;
+			data->block[0] = len;
+		}
+
+		/* Retrieve/store value in SMBBLKDAT */
+		if (read_write == I2C_SMBUS_READ)
+			data->block[i] = inb_p(SMBBLKDAT);
+		if (read_write == I2C_SMBUS_WRITE && i+1 <= len)
+			outb_p(data->block[i+1], SMBBLKDAT);
+		if ((temp & 0x9e) != 0x00)
+			outb_p(temp, SMBHSTSTS);  /* signals SMBBLKDAT ready */
+
+		if ((temp = (0x1e & inb_p(SMBHSTSTS))) != 0x00) {
+			dev_dbg(I801_dev,
+				"Bad status (%02x) at end of transaction\n",
+				temp);
+		}
+		dev_dbg(I801_dev, "Block (post %d): CNT=%02x, CMD=%02x, "
+			"ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i,
+			inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
+			inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
+
+		if (result < 0)
+			goto END;
+	}
+
+#ifdef HAVE_PEC
+	if(isich4 && command == I2C_SMBUS_BLOCK_DATA_PEC) {
+		/* wait for INTR bit as advised by Intel */
+		timeout = 0;
+		do {
+			temp = inb_p(SMBHSTSTS);
+			i2c_delay(1);
+		} while ((!(temp & 0x02))
+			   && (timeout++ < MAX_TIMEOUT));
+
+		if (timeout >= MAX_TIMEOUT) {
+			dev_dbg(I801_dev, "PEC Timeout!\n");
+		}
+		outb_p(temp, SMBHSTSTS); 
+	}
+#endif
+	result = 0;
+END:
+	if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
+		/* restore saved configuration register value */
+		pci_write_config_byte(I801_dev, SMBHSTCFG, hostc);
+	}
+	return result;
+}
+
+/* Return -1 on error. */
+static s32 i801_access(struct i2c_adapter * adap, u16 addr,
+		       unsigned short flags, char read_write, u8 command,
+		       int size, union i2c_smbus_data * data)
+{
+	int hwpec = 0;
+	int block = 0;
+	int ret, xact = 0;
+
+#ifdef HAVE_PEC
+	if(isich4)
+		hwpec = (flags & I2C_CLIENT_PEC) != 0;
+#endif
+
+	switch (size) {
+	case I2C_SMBUS_QUICK:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		xact = I801_QUICK;
+		break;
+	case I2C_SMBUS_BYTE:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(command, SMBHSTCMD);
+		xact = I801_BYTE;
+		break;
+	case I2C_SMBUS_BYTE_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(data->byte, SMBHSTDAT0);
+		xact = I801_BYTE_DATA;
+		break;
+	case I2C_SMBUS_WORD_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE) {
+			outb_p(data->word & 0xff, SMBHSTDAT0);
+			outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
+		}
+		xact = I801_WORD_DATA;
+		break;
+	case I2C_SMBUS_BLOCK_DATA:
+	case I2C_SMBUS_I2C_BLOCK_DATA:
+#ifdef HAVE_PEC
+	case I2C_SMBUS_BLOCK_DATA_PEC:
+		if(hwpec && size == I2C_SMBUS_BLOCK_DATA)
+			size = I2C_SMBUS_BLOCK_DATA_PEC;
+#endif
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		outb_p(command, SMBHSTCMD);
+		block = 1;
+		break;
+	case I2C_SMBUS_PROC_CALL:
+	default:
+		dev_err(I801_dev, "Unsupported transaction %d\n", size);
+		return -1;
+	}
+
+#ifdef HAVE_PEC
+	if(isich4 && hwpec) {
+		if(size != I2C_SMBUS_QUICK &&
+		   size != I2C_SMBUS_I2C_BLOCK_DATA)
+			outb_p(1, SMBAUXCTL);	/* enable HW PEC */
+	}
+#endif
+	if(block)
+		ret = i801_block_transaction(data, read_write, size);
+	else {
+		outb_p(xact | ENABLE_INT9, SMBHSTCNT);
+		ret = i801_transaction();
+	}
+
+#ifdef HAVE_PEC
+	if(isich4 && hwpec) {
+		if(size != I2C_SMBUS_QUICK &&
+		   size != I2C_SMBUS_I2C_BLOCK_DATA)
+			outb_p(0, SMBAUXCTL);
+	}
+#endif
+
+	if(block)
+		return ret;
+	if(ret)
+		return -1;
+	if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK))
+		return 0;
+
+	switch (xact & 0x7f) {
+	case I801_BYTE:	/* Result put in SMBHSTDAT0 */
+	case I801_BYTE_DATA:
+		data->byte = inb_p(SMBHSTDAT0);
+		break;
+	case I801_WORD_DATA:
+		data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
+		break;
+	}
+	return 0;
+}
+
+
+static u32 i801_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	    I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	    I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK
+#ifdef HAVE_PEC
+	     | (isich4 ? I2C_FUNC_SMBUS_BLOCK_DATA_PEC |
+	                 I2C_FUNC_SMBUS_HWPEC_CALC
+	               : 0)
+#endif
+	    ;
+}
+
+static struct i2c_algorithm smbus_algorithm = {
+	.name		= "Non-I2C SMBus adapter",
+	.id		= I2C_ALGO_SMBUS,
+	.smbus_xfer	= i801_access,
+	.functionality	= i801_func,
+};
+
+static struct i2c_adapter i801_adapter = {
+	.owner		= THIS_MODULE,
+	.id		= I2C_ALGO_SMBUS | I2C_HW_SMBUS_I801,
+	.algo		= &smbus_algorithm,
+	.name	= "unset",
+};
+
+static struct pci_device_id i801_ids[] __devinitdata = {
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_82801AA_3,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_82801AB_3,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_82801BA_2,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_82801CA_3,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_82801DB_3,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	0x24d3,	/* 82801EB ICH5 */
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	0x25a4, /* PCI_DEVICE_ID_INTEL_ESB_4 */
+		.subvendor =	PCI_ANY_ID,
+		.subdevice = 	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	0x266a, /* PCI_DEVICE_ID_INTEL_ICH6_16 */
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+  	},
+	{ 0, }
+};
+
+static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+
+	if (i801_setup(dev)) {
+		dev_warn(dev,
+			"I801 not detected, module not inserted.\n");
+		return -ENODEV;
+	}
+
+	snprintf(i801_adapter.name, 32,
+		"SMBus I801 adapter at %04x", i801_smba);
+	return i2c_add_adapter(&i801_adapter);
+}
+
+static void __devexit i801_remove(struct pci_dev *dev)
+{
+	i2c_del_adapter(&i801_adapter);
+	release_region(i801_smba, (isich4 ? 16 : 8));
+}
+
+static struct pci_driver i801_driver = {
+	.name		= "i801 smbus",
+	.id_table	= i801_ids,
+	.probe		= i801_probe,
+	.remove		= __devexit_p(i801_remove),
+};
+
+static int __init i2c_i801_init(void)
+{
+	printk(KERN_INFO "i2c-i801 version %s (%s)\n", LM_VERSION, LM_DATE);
+	return pci_module_init(&i801_driver);
+}
+
+static void __exit i2c_i801_exit(void)
+{
+	pci_unregister_driver(&i801_driver);
+}
+
+MODULE_AUTHOR ("Frodo Looijaard <frodol@dds.nl>, "
+		"Philip Edelbrock <phil@netroedge.com>, "
+		"and Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("I801 SMBus driver");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_i801_init);
+module_exit(i2c_i801_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-i810.c linux-2.4.27-leo/drivers/i2c/i2c-i810.c
--- linux-2.4.27/drivers/i2c/i2c-i810.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-i810.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,310 @@
+/*
+    i2c-i810.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+    Copyright (c) 1998, 1999, 2000  Frodo Looijaard <frodol@dds.nl>,
+    Philip Edelbrock <phil@netroedge.com>,
+    Ralph Metzler <rjkm@thp.uni-koeln.de>, and
+    Mark D. Studebaker <mdsxyz123@yahoo.com>
+    
+    Based on code written by Ralph Metzler <rjkm@thp.uni-koeln.de> and
+    Simon Vogl
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/*
+   This interfaces to the I810/I815 to provide access to
+   the DDC Bus and the I2C Bus.
+
+   SUPPORTED DEVICES	PCI ID
+   i810AA		7121           
+   i810AB		7123           
+   i810E		7125           
+   i815			1132           
+*/
+
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/param.h>	/* for HZ */
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+MODULE_LICENSE("GPL");
+
+#ifndef PCI_DEVICE_ID_INTEL_82815_2
+#define PCI_DEVICE_ID_INTEL_82815_2   0x1132
+#endif
+
+/* GPIO register locations */
+#define I810_IOCONTROL_OFFSET 0x5000
+#define I810_HVSYNC	0x00	/* not used */
+#define I810_GPIOA	0x10
+#define I810_GPIOB	0x14
+
+/* bit locations in the registers */
+#define SCL_DIR_MASK	0x0001
+#define SCL_DIR		0x0002
+#define SCL_VAL_MASK	0x0004
+#define SCL_VAL_OUT	0x0008
+#define SCL_VAL_IN	0x0010
+#define SDA_DIR_MASK	0x0100
+#define SDA_DIR		0x0200
+#define SDA_VAL_MASK	0x0400
+#define SDA_VAL_OUT	0x0800
+#define SDA_VAL_IN	0x1000
+
+/* initialization states */
+#define INIT1	0x1
+#define INIT2	0x2
+#define INIT3	0x4
+
+/* delays */
+#define CYCLE_DELAY		10
+#define TIMEOUT			(HZ / 2)
+
+
+static void config_i810(struct pci_dev *dev);
+
+
+static unsigned long ioaddr;
+
+/* The i810 GPIO registers have individual masks for each bit
+   so we never have to read before writing. Nice. */
+
+static void bit_i810i2c_setscl(void *data, int val)
+{
+	writel((val ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK,
+	     ioaddr + I810_GPIOB);
+	readl(ioaddr + I810_GPIOB);	/* flush posted write */
+}
+
+static void bit_i810i2c_setsda(void *data, int val)
+{
+ 	writel((val ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK,
+	     ioaddr + I810_GPIOB);
+	readl(ioaddr + I810_GPIOB);	/* flush posted write */
+}
+
+/* The GPIO pins are open drain, so the pins could always remain outputs.
+   However, some chip versions don't latch the inputs unless they
+   are set as inputs.
+   We rely on the i2c-algo-bit routines to set the pins high before
+   reading the input from other chips. Following guidance in the 815
+   prog. ref. guide, we do a "dummy write" of 0 to the register before
+   reading which forces the input value to be latched. We presume this
+   applies to the 810 as well; shouldn't hurt anyway. This is necessary to get
+   i2c_algo_bit bit_test=1 to pass. */
+
+static int bit_i810i2c_getscl(void *data)
+{
+	writel(SCL_DIR_MASK, ioaddr + I810_GPIOB);
+	writel(0, ioaddr + I810_GPIOB);
+	return (0 != (readl(ioaddr + I810_GPIOB) & SCL_VAL_IN));
+}
+
+static int bit_i810i2c_getsda(void *data)
+{
+	writel(SDA_DIR_MASK, ioaddr + I810_GPIOB);
+	writel(0, ioaddr + I810_GPIOB);
+	return (0 != (readl(ioaddr + I810_GPIOB) & SDA_VAL_IN));
+}
+
+static void bit_i810ddc_setscl(void *data, int val)
+{
+	writel((val ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK,
+	     ioaddr + I810_GPIOA);
+	readl(ioaddr + I810_GPIOA);	/* flush posted write */
+}
+
+static void bit_i810ddc_setsda(void *data, int val)
+{
+ 	writel((val ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK,
+	     ioaddr + I810_GPIOA);
+	readl(ioaddr + I810_GPIOA);	/* flush posted write */
+}
+
+static int bit_i810ddc_getscl(void *data)
+{
+	writel(SCL_DIR_MASK, ioaddr + I810_GPIOA);
+	writel(0, ioaddr + I810_GPIOA);
+	return (0 != (readl(ioaddr + I810_GPIOA) & SCL_VAL_IN));
+}
+
+static int bit_i810ddc_getsda(void *data)
+{
+	writel(SDA_DIR_MASK, ioaddr + I810_GPIOA);
+	writel(0, ioaddr + I810_GPIOA);
+	return (0 != (readl(ioaddr + I810_GPIOA) & SDA_VAL_IN));
+}
+
+
+/* Configures the chip */
+void config_i810(struct pci_dev *dev)
+{
+	unsigned long cadr;
+
+	/* map I810 memory */
+	cadr = dev->resource[1].start;
+	cadr += I810_IOCONTROL_OFFSET;
+	cadr &= PCI_BASE_ADDRESS_MEM_MASK;
+	ioaddr = (unsigned long)ioremap_nocache(cadr, 0x1000);
+	if(ioaddr) {
+		bit_i810i2c_setscl(NULL, 1);
+		bit_i810i2c_setsda(NULL, 1);
+		bit_i810ddc_setscl(NULL, 1);
+		bit_i810ddc_setsda(NULL, 1);
+	}
+}
+
+
+static struct i2c_algo_bit_data i810_i2c_bit_data = {
+	.setsda		= bit_i810i2c_setsda,
+	.setscl		= bit_i810i2c_setscl,
+	.getsda		= bit_i810i2c_getsda,
+	.getscl		= bit_i810i2c_getscl,
+	.udelay		= CYCLE_DELAY,
+	.mdelay		= CYCLE_DELAY,
+	.timeout	= TIMEOUT,
+};
+
+static struct i2c_adapter i810_i2c_adapter = {
+	.owner		= THIS_MODULE,
+	.name		= "I810/I815 I2C Adapter",
+	.id		= I2C_HW_B_I810,
+	.algo_data	= &i810_i2c_bit_data,
+};
+
+static struct i2c_algo_bit_data i810_ddc_bit_data = {
+	.setsda		= bit_i810ddc_setsda,
+	.setscl		= bit_i810ddc_setscl,
+	.getsda		= bit_i810ddc_getsda,
+	.getscl		= bit_i810ddc_getscl,
+	.udelay		= CYCLE_DELAY,
+	.mdelay		= CYCLE_DELAY,
+	.timeout	= TIMEOUT,
+};
+
+static struct i2c_adapter i810_ddc_adapter = {
+	.owner		= THIS_MODULE,
+	.name		= "I810/I815 DDC Adapter",
+	.id		= I2C_HW_B_I810,
+	.algo_data	= &i810_ddc_bit_data,
+};
+
+
+static struct pci_device_id i810_ids[] __devinitdata = {
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_82810_IG1,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_82810_IG3,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	0x7125,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_82815_2,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	0x2562,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{ 0, }
+};
+
+static int __devinit i810_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	int retval;
+
+	config_i810(dev);
+	printk("i2c-i810.o: i810/i815 found.\n");
+
+	retval = i2c_bit_add_bus(&i810_i2c_adapter);
+	if(retval)
+		return retval;
+	retval = i2c_bit_add_bus(&i810_ddc_adapter);
+	if(retval)
+		i2c_bit_del_bus(&i810_i2c_adapter);
+	return retval;
+}
+
+static void __devexit i810_remove(struct pci_dev *dev)
+{
+	i2c_bit_del_bus(&i810_ddc_adapter);
+	i2c_bit_del_bus(&i810_i2c_adapter);
+}
+
+
+/* Don't register driver to avoid driver conflicts */
+/*
+static struct pci_driver i810_driver = {
+	.name		= "i810 smbus",
+	.id_table	= i810_ids,
+	.probe		= i810_probe,
+	.remove		= __devexit_p(i810_remove),
+};
+*/
+
+static int __init i2c_i810_init(void)
+{
+	struct pci_dev *dev;
+	const struct pci_device_id *id;
+
+	printk("i2c-i810.o version %s (%s)\n", LM_VERSION, LM_DATE);
+/*
+	return pci_module_init(&i810_driver);
+*/
+	pci_for_each_dev(dev) {
+		id = pci_match_device(i810_ids, dev);
+		if(id)
+			if(i810_probe(dev, id) >= 0)
+				return 0;
+	}
+	return -ENODEV;
+}
+
+static void __exit i2c_i810_exit(void)
+{
+/*
+	pci_unregister_driver(&i810_driver);
+*/
+	i810_remove(NULL);
+	iounmap((void *)ioaddr);
+}
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, Ralph Metzler <rjkm@thp.uni-koeln.de>, and Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("I810/I815 I2C/DDC driver");
+
+module_init(i2c_i810_init);
+module_exit(i2c_i810_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-isa.c linux-2.4.27-leo/drivers/i2c/i2c-isa.c
--- linux-2.4.27/drivers/i2c/i2c-isa.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-isa.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,74 @@
+/*
+    i2c-isa.c - Part of lm_sensors, Linux kernel modules for hardware
+            monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl> 
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* This implements an i2c algorithm/adapter for ISA bus. Not that this is
+   on first sight very useful; almost no functionality is preserved.
+   Except that it makes writing drivers for chips which can be on both
+   the SMBus and the ISA bus very much easier. See lm78.c for an example
+   of this. */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+static u32 isa_func(struct i2c_adapter *adapter);
+
+/* This is the actual algorithm we define */
+static struct i2c_algorithm isa_algorithm = {
+	.name		= "ISA bus algorithm",
+	.id		= I2C_ALGO_ISA,
+	.functionality	= isa_func,
+};
+
+/* There can only be one... */
+static struct i2c_adapter isa_adapter = {
+	.owner		= THIS_MODULE,
+	.name		= "ISA main adapter",
+	.id		= I2C_ALGO_ISA | I2C_HW_ISA,
+	.algo		= &isa_algorithm,
+};
+
+/* We can't do a thing... */
+static u32 isa_func(struct i2c_adapter *adapter)
+{
+	return 0;
+}
+
+static int __init i2c_isa_init(void)
+{
+	printk("i2c-isa.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_adapter(&isa_adapter);
+}
+
+static void __exit i2c_isa_exit(void)
+{
+	i2c_del_adapter(&isa_adapter);
+}
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
+MODULE_DESCRIPTION("ISA bus access through i2c");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_isa_init);
+module_exit(i2c_isa_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-nforce2.c linux-2.4.27-leo/drivers/i2c/i2c-nforce2.c
--- linux-2.4.27/drivers/i2c/i2c-nforce2.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-nforce2.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,409 @@
+/*
+    SMBus driver for nVidia nForce2 MCP
+
+    Copyright (c) 2003  Hans-Frieder Vogt <hfvogt@arcor.de>,
+    Based on
+    SMBus 2.0 driver for AMD-8111 IO-Hub
+    Copyright (c) 2002 Vojtech Pavlik
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+    SUPPORTED DEVICES	PCI ID
+    nForce2 MCP		0064       
+
+    This driver supports the 2 SMBuses that are included in the MCP2 of the
+    nForce2 chipset.
+*/
+
+/* Note: we assume there can only be one nForce2, with two SMBus interfaces */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+/* kernel 2.4.9 needs this */
+#ifndef min_t
+#define min_t(type,x,y) min(type,x,y)
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR ("Hans-Frieder Vogt <hfvogt@arcor.de>");
+MODULE_DESCRIPTION("nForce2 SMBus driver");
+
+#ifndef PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS
+#define PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS   0x0064
+#endif
+
+
+struct nforce2_smbus {
+	struct pci_dev *dev;
+	struct i2c_adapter adapter;
+	int base;
+	int size;
+};
+
+
+/*
+ * nVidia nForce2 SMBus control register definitions
+ */
+#define NFORCE_PCI_SMB1	0x50
+#define NFORCE_PCI_SMB2	0x54
+
+
+/*
+ * ACPI 2.0 chapter 13 SMBus 2.0 EC register model
+ */
+#define NVIDIA_SMB_PRTCL	(smbus->base + 0x00)	/* protocol, PEC */
+#define NVIDIA_SMB_STS		(smbus->base + 0x01)	/* status */
+#define NVIDIA_SMB_ADDR		(smbus->base + 0x02)	/* address */
+#define NVIDIA_SMB_CMD		(smbus->base + 0x03)	/* command */
+#define NVIDIA_SMB_DATA		(smbus->base + 0x04)	/* 32 data registers */
+#define NVIDIA_SMB_BCNT		(smbus->base + 0x24)	/* number of data bytes */
+#define NVIDIA_SMB_ALRM_A	(smbus->base + 0x25)	/* alarm address */
+#define NVIDIA_SMB_ALRM_D	(smbus->base + 0x26)	/* 2 bytes alarm data */
+
+#define NVIDIA_SMB_STS_DONE	0x80
+#define NVIDIA_SMB_STS_ALRM	0x40
+#define NVIDIA_SMB_STS_RES	0x20
+#define NVIDIA_SMB_STS_STATUS	0x1f
+
+#define NVIDIA_SMB_PRTCL_WRITE			0x00
+#define NVIDIA_SMB_PRTCL_READ			0x01
+#define NVIDIA_SMB_PRTCL_QUICK			0x02
+#define NVIDIA_SMB_PRTCL_BYTE			0x04
+#define NVIDIA_SMB_PRTCL_BYTE_DATA		0x06
+#define NVIDIA_SMB_PRTCL_WORD_DATA		0x08
+#define NVIDIA_SMB_PRTCL_BLOCK_DATA		0x0a
+#define NVIDIA_SMB_PRTCL_PROC_CALL		0x0c
+#define NVIDIA_SMB_PRTCL_BLOCK_PROC_CALL	0x0d
+#define NVIDIA_SMB_PRTCL_I2C_BLOCK_DATA		0x4a
+#define NVIDIA_SMB_PRTCL_PEC			0x80
+
+
+/* Other settings */
+#define MAX_TIMEOUT 256
+
+
+
+static s32 nforce2_access(struct i2c_adapter *adap, u16 addr,
+		       unsigned short flags, char read_write,
+		       u8 command, int size, union i2c_smbus_data *data);
+/*
+static int nforce2_block_transaction(union i2c_smbus_data *data,
+				  char read_write, int i2c_enable);
+ */
+static u32 nforce2_func(struct i2c_adapter *adapter);
+
+
+static struct i2c_algorithm smbus_algorithm = {
+	.name = "Non-I2C SMBus adapter",
+	.id = I2C_ALGO_SMBUS,
+	.smbus_xfer = nforce2_access,
+	.functionality = nforce2_func,
+};
+
+/* Return -1 on error. See smbus.h for more information */
+s32 nforce2_access(struct i2c_adapter * adap, u16 addr, unsigned short flags,
+		char read_write, u8 command, int size,
+		union i2c_smbus_data * data)
+{
+	struct nforce2_smbus *smbus = adap->algo_data;
+	unsigned char protocol, pec, temp;
+	unsigned char len = 0; /* to keep the compiler quiet */
+	int timeout = 0;
+	int i;
+
+	protocol = (read_write == I2C_SMBUS_READ) ? NVIDIA_SMB_PRTCL_READ : NVIDIA_SMB_PRTCL_WRITE;
+	pec = (flags & I2C_CLIENT_PEC) ? NVIDIA_SMB_PRTCL_PEC : 0;
+
+	switch (size) {
+
+		case I2C_SMBUS_QUICK:
+			protocol |= NVIDIA_SMB_PRTCL_QUICK;
+			read_write = I2C_SMBUS_WRITE;
+			break;
+
+		case I2C_SMBUS_BYTE:
+			if (read_write == I2C_SMBUS_WRITE)
+				outb_p(command, NVIDIA_SMB_CMD);
+			protocol |= NVIDIA_SMB_PRTCL_BYTE;
+			break;
+
+		case I2C_SMBUS_BYTE_DATA:
+			outb_p(command, NVIDIA_SMB_CMD);
+			if (read_write == I2C_SMBUS_WRITE)
+				outb_p(data->byte, NVIDIA_SMB_DATA);
+			protocol |= NVIDIA_SMB_PRTCL_BYTE_DATA;
+			break;
+
+		case I2C_SMBUS_WORD_DATA:
+			outb_p(command, NVIDIA_SMB_CMD);
+			if (read_write == I2C_SMBUS_WRITE) {
+				 outb_p(data->word, NVIDIA_SMB_DATA);
+				 outb_p(data->word >> 8, NVIDIA_SMB_DATA+1);
+			}
+			protocol |= NVIDIA_SMB_PRTCL_WORD_DATA | pec;
+			break;
+
+		case I2C_SMBUS_BLOCK_DATA:
+			outb_p(command, NVIDIA_SMB_CMD);
+			if (read_write == I2C_SMBUS_WRITE) {
+				len = min_t(u8, data->block[0], 32);
+				outb_p(len, NVIDIA_SMB_BCNT);
+				for (i = 0; i < len; i++)
+					outb_p(data->block[i + 1], NVIDIA_SMB_DATA+i);
+			}
+			protocol |= NVIDIA_SMB_PRTCL_BLOCK_DATA | pec;
+			break;
+
+		case I2C_SMBUS_I2C_BLOCK_DATA:
+			len = min_t(u8, data->block[0], 32);
+			outb_p(command, NVIDIA_SMB_CMD);
+			outb_p(len, NVIDIA_SMB_BCNT);
+			if (read_write == I2C_SMBUS_WRITE)
+				for (i = 0; i < len; i++)
+					outb_p(data->block[i + 1], NVIDIA_SMB_DATA+i);
+			protocol |= NVIDIA_SMB_PRTCL_I2C_BLOCK_DATA;
+			break;
+
+		case I2C_SMBUS_PROC_CALL:
+			printk(KERN_WARNING "i2c-nforce2.o: I2C_SMBUS_PROC_CALL not supported!\n");
+			return -1;
+			/*
+			outb_p(command, NVIDIA_SMB_CMD);
+			outb_p(data->word, NVIDIA_SMB_DATA);
+			outb_p(data->word >> 8, NVIDIA_SMB_DATA + 1);
+			protocol = NVIDIA_SMB_PRTCL_PROC_CALL | pec;
+			read_write = I2C_SMBUS_READ;
+			break;
+			 */
+
+		case I2C_SMBUS_BLOCK_PROC_CALL:
+			printk(KERN_WARNING "i2c-nforce2.o: I2C_SMBUS_BLOCK_PROC_CALL not supported!\n");
+			return -1;
+			/*
+			protocol |= pec;
+			len = min_t(u8, data->block[0], 31);
+			outb_p(command, NVIDIA_SMB_CMD);
+			outb_p(len, NVIDIA_SMB_BCNT);
+			for (i = 0; i < len; i++)
+				outb_p(data->block[i + 1], NVIDIA_SMB_DATA + i);
+			protocol = NVIDIA_SMB_PRTCL_BLOCK_PROC_CALL | pec;
+			read_write = I2C_SMBUS_READ;
+			break;
+			*/
+
+		case I2C_SMBUS_WORD_DATA_PEC:
+		case I2C_SMBUS_BLOCK_DATA_PEC:
+		case I2C_SMBUS_PROC_CALL_PEC:
+		case I2C_SMBUS_BLOCK_PROC_CALL_PEC:
+			printk(KERN_WARNING "i2c-nforce2.c: Unexpected software PEC transaction %d\n.", size);
+			return -1;
+
+		default:
+			printk(KERN_WARNING "i2c-nforce2.c: Unsupported transaction %d\n", size);
+			return -1;
+	}
+
+	outb_p((addr & 0x7f) << 1, NVIDIA_SMB_ADDR);
+	outb_p(protocol, NVIDIA_SMB_PRTCL);
+
+	temp = inb_p(NVIDIA_SMB_STS);
+
+#if 0
+	do {
+		i2c_delay(1);
+		temp = inb_p(NVIDIA_SMB_STS);
+	} while (((temp & NVIDIA_SMB_STS_DONE) == 0) && (timeout++ < MAX_TIMEOUT));
+#endif
+	if (~temp & NVIDIA_SMB_STS_DONE) {
+		udelay(500);
+		temp = inb_p(NVIDIA_SMB_STS);
+	}
+	if (~temp & NVIDIA_SMB_STS_DONE) {
+		i2c_delay(HZ/100);
+		temp = inb_p(NVIDIA_SMB_STS);
+	}
+
+	if ((timeout >= MAX_TIMEOUT) || (~temp & NVIDIA_SMB_STS_DONE) || (temp & NVIDIA_SMB_STS_STATUS))
+		return -1;
+
+	if (read_write == I2C_SMBUS_WRITE)
+		return 0;
+
+	switch (size) {
+
+		case I2C_SMBUS_BYTE:
+		case I2C_SMBUS_BYTE_DATA:
+			data->byte = inb_p(NVIDIA_SMB_DATA);
+			break;
+
+		case I2C_SMBUS_WORD_DATA:
+		/* case I2C_SMBUS_PROC_CALL: not supported */
+			data->word = inb_p(NVIDIA_SMB_DATA) | (inb_p(NVIDIA_SMB_DATA+1) << 8);
+			break;
+
+		case I2C_SMBUS_BLOCK_DATA:
+		/* case I2C_SMBUS_BLOCK_PROC_CALL: not supported */
+			len = inb_p(NVIDIA_SMB_BCNT);
+			len = min_t(u8, len, 32);
+		case I2C_SMBUS_I2C_BLOCK_DATA:
+			for (i = 0; i < len; i++)
+				data->block[i+1] = inb_p(NVIDIA_SMB_DATA + i);
+			data->block[0] = len;
+			break;
+	}
+
+	return 0;
+}
+
+
+u32 nforce2_func(struct i2c_adapter *adapter)
+{
+	/* other functionality might be possible, but is not tested */
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	    I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA /* |
+	    I2C_FUNC_SMBUS_BLOCK_DATA */;
+}
+
+
+static struct pci_device_id nforce2_ids[] = {
+	{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS,
+	       	PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+	{ 0 }
+};
+
+
+static int __devinit nforce2_probe_smb (struct pci_dev *dev, int reg, struct nforce2_smbus *smbus, char *name)
+{
+	u16 iobase;
+	int error;
+
+	if (pci_read_config_word(dev, reg, &iobase) != PCIBIOS_SUCCESSFUL) {
+		printk (KERN_ERR "i2c-nforce2.o: Error reading PCI config for %s\n", name);
+		return -1;
+	}
+	smbus->dev  = dev;
+	smbus->base = iobase & 0xfffc;
+	smbus->size = 8;
+
+	if (!request_region(smbus->base, smbus->size, "nForce2 SMBus")) {
+		printk (KERN_ERR "i2c-nforce2.o: Error requesting region %02x .. %02X for %s\n", smbus->base, smbus->base+smbus->size-1, name);
+		return -1;
+	}
+
+	/* TODO: find a better way to find out whether this file is compiled
+	 * with i2c 2.7.0 of earlier
+	 */
+#ifdef I2C_HW_SMBUS_AMD8111
+	smbus->adapter.owner = THIS_MODULE;
+#endif
+	sprintf(smbus->adapter.name, "SMBus nForce2 adapter at %04x", smbus->base);
+	smbus->adapter.id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_NFORCE2;
+	smbus->adapter.algo = &smbus_algorithm;
+	smbus->adapter.algo_data = smbus;
+
+	error = i2c_add_adapter(&smbus->adapter);
+	if (error) {
+		printk(KERN_WARNING "i2c-nforce2.o: Failed to register adapter.\n");
+		release_region(smbus->base, smbus->size);
+		return -1;
+	}
+	printk(KERN_INFO "i2c-nforce2.o: nForce2 SMBus adapter at %#x\n", smbus->base);
+	return 0;
+}
+
+
+static int __devinit nforce2_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	struct nforce2_smbus *smbuses;
+	int res1, res2;
+
+	/* we support 2 SMBus adapters */
+	if (!(smbuses = (void *)kmalloc(2*sizeof(struct nforce2_smbus),
+				       	GFP_KERNEL)))
+		return -ENOMEM;
+	memset (smbuses, 0, 2*sizeof(struct nforce2_smbus));
+	pci_set_drvdata(dev, smbuses);
+
+	/* SMBus adapter 1 */
+	res1 = nforce2_probe_smb (dev, NFORCE_PCI_SMB1, &smbuses[0], "SMB1");
+	if (res1 < 0) {
+		printk (KERN_ERR "i2c-nforce2.o: Error probing SMB1.\n");
+		smbuses[0].base = 0;	/* to have a check value */
+	}
+	res2 = nforce2_probe_smb (dev, NFORCE_PCI_SMB2, &smbuses[1], "SMB2");
+	if (res2 < 0) {
+		printk (KERN_ERR "i2c-nforce2.o: Error probing SMB2.\n");
+		smbuses[1].base = 0;	/* to have a check value */
+	}
+	if ((res1 < 0) && (res2 < 0)) {
+		/* we did not find even one of the SMBuses, so we give up */
+		kfree(smbuses);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+
+static void __devexit nforce2_remove(struct pci_dev *dev)
+{
+	struct nforce2_smbus *smbuses = (void*) pci_get_drvdata(dev);
+
+	if (smbuses[0].base) {
+		i2c_del_adapter(&smbuses[0].adapter);
+		release_region(smbuses[0].base, smbuses[0].size);
+	}
+	if (smbuses[1].base) {
+		i2c_del_adapter(&smbuses[1].adapter);
+		release_region(smbuses[1].base, smbuses[1].size);
+	}
+	kfree(smbuses);
+}
+
+static struct pci_driver nforce2_driver = {
+	.name		= "nForce2 SMBus",
+	.id_table	= nforce2_ids,
+	.probe		= nforce2_probe,
+	.remove		= __devexit_p(nforce2_remove),
+};
+
+int __init nforce2_init(void)
+{
+	printk(KERN_INFO "i2c-nforce2.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return pci_module_init(&nforce2_driver);
+}
+
+void __exit nforce2_exit(void)
+{
+	pci_unregister_driver(&nforce2_driver);
+}
+
+module_init(nforce2_init);
+module_exit(nforce2_exit);
+
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-pcf-epp.c linux-2.4.27-leo/drivers/i2c/i2c-pcf-epp.c
--- linux-2.4.27/drivers/i2c/i2c-pcf-epp.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-pcf-epp.c	2004-09-20 21:34:38.000000000 +0100
@@ -0,0 +1,281 @@
+/* ------------------------------------------------------------------------- */
+/* i2c-pcf-epp.c i2c-hw access for PCF8584 style EPP parallel port adapters  */
+/* ------------------------------------------------------------------------- */
+/*   Copyright (C) 1998-99 Hans Berglund
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.		     */
+/* ------------------------------------------------------------------------- */
+
+/* With some changes from Ryosuke Tajima <rosk@jsk.t.u-tokyo.ac.jp> */
+
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/parport.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-pcf.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+
+
+struct  i2c_pcf_epp {
+  int pe_base;
+  int pe_irq;
+  int pe_clock;
+  int pe_own;
+} ;
+
+#define DEFAULT_BASE 0x378
+#define DEFAULT_IRQ      7
+#define DEFAULT_CLOCK 0x1c
+#define DEFAULT_OWN   0x55
+
+static int base  = 0;
+static int irq   = 0;
+static int clock = 0;
+static int own   = 0;
+static int i2c_debug=0;
+static struct i2c_pcf_epp gpe;
+static wait_queue_head_t pcf_wait;
+static int pcf_pending;
+static spinlock_t irq_driver_lock = SPIN_LOCK_UNLOCKED;
+
+/* ----- global defines -----------------------------------------------	*/
+#define DEB(x)	if (i2c_debug>=1) x
+#define DEB2(x) if (i2c_debug>=2) x
+#define DEB3(x) if (i2c_debug>=3) x
+#define DEBE(x)	x	/* error messages 				*/
+
+/* --- Convenience defines for the EPP/SPP port:			*/
+#define BASE	((struct i2c_pcf_epp *)(data))->pe_base
+// #define DATA	BASE			/* SPP data port */
+#define STAT	(BASE+1)		/* SPP status port */
+#define CTRL	(BASE+2)		/* SPP control port */
+#define EADD	(BASE+3)		/* EPP address port */
+#define EDAT	(BASE+4)		/* EPP data port */
+
+/* ----- local functions ----------------------------------------------	*/
+
+static void pcf_epp_setbyte(void *data, int ctl, int val)
+{
+  if (ctl) {
+    if (gpe.pe_irq > 0) {
+      DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: Write control 0x%x\n",
+		  val|I2C_PCF_ENI));
+      // set A0 pin HIGH
+      outb(inb(CTRL) | PARPORT_CONTROL_INIT, CTRL);
+      // DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: CTRL port = 0x%x\n", inb(CTRL)));
+      // DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: STAT port = 0x%x\n", inb(STAT)));
+      
+      // EPP write data cycle
+      outb(val | I2C_PCF_ENI, EDAT);
+    } else {
+      DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: Write control 0x%x\n", val));
+      // set A0 pin HIGH
+      outb(inb(CTRL) | PARPORT_CONTROL_INIT, CTRL);
+      outb(val, CTRL);
+    }
+  } else {
+    DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: Write data 0x%x\n", val));
+    // set A0 pin LO
+    outb(inb(CTRL) & ~PARPORT_CONTROL_INIT, CTRL);
+    // DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: CTRL port = 0x%x\n", inb(CTRL)));
+    // DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: STAT port = 0x%x\n", inb(STAT)));
+    outb(val, EDAT);
+  }
+}
+
+static int pcf_epp_getbyte(void *data, int ctl)
+{
+  int val;
+
+  if (ctl) {
+    // set A0 pin HIGH
+    outb(inb(CTRL) | PARPORT_CONTROL_INIT, CTRL);
+    val = inb(EDAT);
+    DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: Read control 0x%x\n", val));
+  } else {
+    // set A0 pin LOW
+    outb(inb(CTRL) & ~PARPORT_CONTROL_INIT, CTRL);
+    val = inb(EDAT);
+    DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: Read data 0x%x\n", val));
+  }
+  return (val);
+}
+
+static int pcf_epp_getown(void *data)
+{
+  return (gpe.pe_own);
+}
+
+
+static int pcf_epp_getclock(void *data)
+{
+  return (gpe.pe_clock);
+}
+
+#if 0
+static void pcf_epp_sleep(unsigned long timeout)
+{
+  schedule_timeout( timeout * HZ);
+}
+#endif
+
+static void pcf_epp_waitforpin(void) {
+  int timeout = 10;
+
+  if (gpe.pe_irq > 0) {
+    spin_lock_irq(&irq_driver_lock);
+    if (pcf_pending == 0) {
+      interruptible_sleep_on_timeout(&pcf_wait, timeout*HZ);
+      //udelay(100);
+    } else {
+      pcf_pending = 0;
+    }
+    spin_unlock_irq(&irq_driver_lock);
+  } else {
+    udelay(100);
+  }
+}
+
+static void pcf_epp_handler(int this_irq, void *dev_id, struct pt_regs *regs) {
+  pcf_pending = 1;
+  wake_up_interruptible(&pcf_wait);
+  DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: in interrupt handler.\n"));
+}
+
+
+static int pcf_epp_init(void *data)
+{
+  if (check_region(gpe.pe_base, 5) < 0 ) {
+    
+    printk(KERN_WARNING "Could not request port region with base 0x%x\n", gpe.pe_base);
+    return -ENODEV;
+  } else {
+    request_region(gpe.pe_base, 5, "i2c (EPP parallel port adapter)");
+  }
+
+  DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: init status port = 0x%x\n", inb(0x379)));
+  
+  if (gpe.pe_irq > 0) {
+    if (request_irq(gpe.pe_irq, pcf_epp_handler, 0, "PCF8584", 0) < 0) {
+      printk(KERN_NOTICE "i2c-pcf-epp.o: Request irq%d failed\n", gpe.pe_irq);
+      gpe.pe_irq = 0;
+    } else
+      disable_irq(gpe.pe_irq);
+      enable_irq(gpe.pe_irq);
+  }
+  // EPP mode initialize
+  // enable interrupt from nINTR pin
+  outb(inb(CTRL)|0x14, CTRL);
+  // clear ERROR bit of STAT
+  outb(inb(STAT)|0x01, STAT);
+  outb(inb(STAT)&~0x01,STAT);
+  
+  return 0;
+}
+
+/* ------------------------------------------------------------------------
+ * Encapsulate the above functions in the correct operations structure.
+ * This is only done when more than one hardware adapter is supported.
+ */
+static struct i2c_algo_pcf_data pcf_epp_data = {
+	.setpcf	    = pcf_epp_setbyte,
+	.getpcf	    = pcf_epp_getbyte,
+	.getown	    = pcf_epp_getown,
+	.getclock   = pcf_epp_getclock,
+	.waitforpin = pcf_epp_waitforpin,
+	.udelay	    = 80,
+	.mdelay	    = 80,
+	.timeout    = HZ,
+};
+
+static struct i2c_adapter pcf_epp_ops = {
+	.owner		= THIS_MODULE,
+	.name		= "PCF8584 EPP adapter",
+	.id		= I2C_HW_P_LP,
+	.algo_data	= &pcf_epp_data,
+};
+
+static int __init i2c_pcfepp_init(void) 
+{
+  struct i2c_pcf_epp *pepp = &gpe;
+
+  printk(KERN_DEBUG "i2c-pcf-epp.o: i2c pcf8584-epp adapter module version %s (%s)\n", I2C_VERSION, I2C_DATE);
+  if (base == 0)
+    pepp->pe_base = DEFAULT_BASE;
+  else
+    pepp->pe_base = base;
+
+  if (irq == 0)
+    pepp->pe_irq = DEFAULT_IRQ;
+  else if (irq<0) {
+    // switch off irq
+    pepp->pe_irq=0;
+  } else {
+    pepp->pe_irq = irq;
+  }
+  if (clock == 0)
+    pepp->pe_clock = DEFAULT_CLOCK;
+  else
+    pepp->pe_clock = clock;
+
+  if (own == 0)
+    pepp->pe_own = DEFAULT_OWN;
+  else
+    pepp->pe_own = own;
+
+  pcf_epp_data.data = (void *)pepp;
+  init_waitqueue_head(&pcf_wait);
+  if (pcf_epp_init(pepp) == 0) {
+    int ret;
+    if ( (ret = i2c_pcf_add_bus(&pcf_epp_ops)) < 0) {
+      printk(KERN_WARNING "i2c_pcf_add_bus caused an error: %d\n",ret);
+      release_region(pepp->pe_base , 5);
+      return ret;
+    }
+  } else {
+    
+    return -ENODEV;
+  }
+  printk(KERN_DEBUG "i2c-pcf-epp.o: found device at %#x.\n", pepp->pe_base);
+  return 0;
+}
+
+static void __exit pcf_epp_exit(void)
+{
+  i2c_pcf_del_bus(&pcf_epp_ops);
+  if (gpe.pe_irq > 0) {
+    disable_irq(gpe.pe_irq);
+    free_irq(gpe.pe_irq, 0);
+  }
+  release_region(gpe.pe_base , 5);
+}
+
+MODULE_AUTHOR("Hans Berglund <hb@spacetec.no> \n modified by Ryosuke Tajima <rosk@jsk.t.u-tokyo.ac.jp>");
+MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 EPP parallel port adapter");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(base, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(clock, "i");
+MODULE_PARM(own, "i");
+MODULE_PARM(i2c_debug, "i");
+
+module_init(i2c_pcfepp_init);
+module_exit(pcf_epp_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-philips-par.c linux-2.4.27-leo/drivers/i2c/i2c-philips-par.c
--- linux-2.4.27/drivers/i2c/i2c-philips-par.c	2004-02-20 14:11:41.000000000 +0000
+++ linux-2.4.27-leo/drivers/i2c/i2c-philips-par.c	2004-09-20 21:34:38.000000000 +0100
@@ -21,7 +21,7 @@
 /* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
    Frodo Looijaard <frodol@dds.nl> */
 
-/* $Id: i2c-philips-par.c,v 1.18 2000/07/06 19:21:49 frodo Exp $ */
+/* $Id: i2c-philips-par.c,v 1.33 2004/01/23 20:22:53 khali Exp $ */
 
 #include <linux/kernel.h>
 #include <linux/ioport.h>
@@ -29,14 +29,10 @@
 #include <linux/init.h>
 #include <linux/stddef.h>
 #include <linux/parport.h>
-
+#include <linux/slab.h>
 #include <linux/i2c.h>
 #include <linux/i2c-algo-bit.h>
 
-#ifndef __exit
-#define __exit __init
-#endif
-
 static int type;
 
 struct i2c_par
@@ -130,59 +126,34 @@
 			             PARPORT_STATUS_BUSY) ? 0 : 1;
 }
 
-static int bit_lp_reg(struct i2c_client *client)
-{
-	return 0;
-}
-
-static int bit_lp_unreg(struct i2c_client *client)
-{
-	return 0;
-}
-
-static void bit_lp_inc_use(struct i2c_adapter *adap)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void bit_lp_dec_use(struct i2c_adapter *adap)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 /* ------------------------------------------------------------------------
  * Encapsulate the above functions in the correct operations structure.
  * This is only done when more than one hardware adapter is supported.
  */
  
 static struct i2c_algo_bit_data bit_lp_data = {
-	NULL,
-	bit_lp_setsda,
-	bit_lp_setscl,
-	bit_lp_getsda,
-	bit_lp_getscl,
-	80, 80, 100,		/*	waits, timeout */
+	.setsda		= bit_lp_setsda,
+	.setscl		= bit_lp_setscl,
+	.getsda		= bit_lp_getsda,
+	.getscl		= bit_lp_getscl,
+	.udelay		= 80,
+	.mdelay		= 80,
+	.timeout	= HZ
 }; 
 
 static struct i2c_algo_bit_data bit_lp_data2 = {
-	NULL,
-	bit_lp_setsda2,
-	bit_lp_setscl2,
-	bit_lp_getsda2,
-	NULL,
-	80, 80, 100,		/*	waits, timeout */
+	.setsda		= bit_lp_setsda2,
+	.setscl		= bit_lp_setscl2,
+	.getsda		= bit_lp_getsda2,
+	.udelay		= 80,
+	.mdelay		= 80,
+	.timeout	= HZ
 }; 
 
 static struct i2c_adapter bit_lp_ops = {
-	"Philips Parallel port adapter",
-	I2C_HW_B_LP,
-	NULL,
-	NULL,
-	bit_lp_inc_use,
-	bit_lp_dec_use,
-	bit_lp_reg,
-
-	bit_lp_unreg,
+	.owner		= THIS_MODULE,
+	.name		= "Philips Parallel port adapter",
+	.id		= I2C_HW_B_LP,
 };
 
 static void i2c_parport_attach (struct parport *port)
@@ -202,6 +173,7 @@
 						NULL);
 	if (!adapter->pdev) {
 		printk(KERN_ERR "i2c-philips-par: Unable to register with parport.\n");
+		kfree(adapter);
 		return;
 	}
 
@@ -210,8 +182,12 @@
 	adapter->bit_lp_data = type ? bit_lp_data2 : bit_lp_data;
 	adapter->bit_lp_data.data = port;
 
+	if (parport_claim_or_block(adapter->pdev) < 0 ) {
+		printk(KERN_ERR "i2c-philips-par: Could not claim parallel port.\n");
+		kfree(adapter);
+		return;
+	}
 	/* reset hardware to sane state */
-	parport_claim_or_block(adapter->pdev);
 	adapter->bit_lp_data.setsda(port, 1);
 	adapter->bit_lp_data.setscl(port, 1);
 	parport_release(adapter->pdev);
@@ -257,7 +233,7 @@
 	NULL
 };
 
-int __init i2c_bitlp_init(void)
+static int __init i2c_bitlp_init(void)
 {
 	printk(KERN_INFO "i2c-philips-par.o: i2c Philips parallel port adapter module version %s (%s)\n", I2C_VERSION, I2C_DATE);
 
@@ -266,7 +242,7 @@
 	return 0;
 }
 
-void __exit i2c_bitlp_exit(void)
+static void __exit i2c_bitlp_exit(void)
 {
 	parport_unregister_driver(&i2c_driver);
 }
@@ -279,14 +255,5 @@
 
 MODULE_PARM(type, "i");
 
-#ifdef MODULE
-int init_module(void)
-{
-	return i2c_bitlp_init();
-}
-
-void cleanup_module(void)
-{
-	i2c_bitlp_exit();
-}
-#endif
+module_init(i2c_bitlp_init);
+module_exit(i2c_bitlp_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-piix4.c linux-2.4.27-leo/drivers/i2c/i2c-piix4.c
--- linux-2.4.27/drivers/i2c/i2c-piix4.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-piix4.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,549 @@
+/*
+    piix4.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+    Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl> and
+    Philip Edelbrock <phil@netroedge.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+   Supports:
+	Intel PIIX4, 440MX
+	Serverworks OSB4, CSB5, CSB6
+	SMSC Victory66
+
+   Note: we assume there can only be one device, with one SMBus interface.
+*/
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/apm_bios.h>
+#include <asm/io.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+
+struct sd {
+	const unsigned short mfr;
+	const unsigned short dev;
+	const unsigned char fn;
+	const char *name;
+};
+
+/* PIIX4 SMBus address offsets */
+#define SMBHSTSTS (0 + piix4_smba)
+#define SMBHSLVSTS (1 + piix4_smba)
+#define SMBHSTCNT (2 + piix4_smba)
+#define SMBHSTCMD (3 + piix4_smba)
+#define SMBHSTADD (4 + piix4_smba)
+#define SMBHSTDAT0 (5 + piix4_smba)
+#define SMBHSTDAT1 (6 + piix4_smba)
+#define SMBBLKDAT (7 + piix4_smba)
+#define SMBSLVCNT (8 + piix4_smba)
+#define SMBSHDWCMD (9 + piix4_smba)
+#define SMBSLVEVT (0xA + piix4_smba)
+#define SMBSLVDAT (0xC + piix4_smba)
+
+/* count for request_region */
+#define SMBIOSIZE 8
+
+/* PCI Address Constants */
+#define SMBBA     0x090
+#define SMBHSTCFG 0x0D2
+#define SMBSLVC   0x0D3
+#define SMBSHDW1  0x0D4
+#define SMBSHDW2  0x0D5
+#define SMBREV    0x0D6
+
+/* Other settings */
+#define MAX_TIMEOUT 500
+#define  ENABLE_INT9 0
+
+/* PIIX4 constants */
+#define PIIX4_QUICK      0x00
+#define PIIX4_BYTE       0x04
+#define PIIX4_BYTE_DATA  0x08
+#define PIIX4_WORD_DATA  0x0C
+#define PIIX4_BLOCK_DATA 0x14
+
+/* insmod parameters */
+
+/* If force is set to anything different from 0, we forcibly enable the
+   PIIX4. DANGEROUS! */
+static int force = 0;
+MODULE_PARM(force, "i");
+MODULE_PARM_DESC(force, "Forcibly enable the PIIX4. DANGEROUS!");
+
+/* If force_addr is set to anything different from 0, we forcibly enable
+   the PIIX4 at the given address. VERY DANGEROUS! */
+static int force_addr = 0;
+MODULE_PARM(force_addr, "i");
+MODULE_PARM_DESC(force_addr,
+		 "Forcibly enable the PIIX4 at the given address. "
+		 "EXTREMELY DANGEROUS!");
+
+static int fix_hstcfg = 0;
+MODULE_PARM(fix_hstcfg, "i");
+MODULE_PARM_DESC(fix_hstcfg,
+		 "Fix config register. Needed on some boards (Force CPCI735).");
+
+static int piix4_transaction(void);
+
+static unsigned short piix4_smba = 0;
+
+#ifdef CONFIG_X86
+/*
+ * Get DMI information.
+ */
+
+static int __devinit ibm_dmi_probe(void)
+{
+	extern int is_unsafe_smbus;
+	return is_unsafe_smbus;
+}
+#endif
+
+/* Detect whether a PIIX4 can be found, and initialize it, where necessary.
+   Note the differences between kernels with the old PCI BIOS interface and
+   newer kernels with the real PCI interface. In compat.h some things are
+   defined to make the transition easier. */
+static int __devinit piix4_setup(struct pci_dev *PIIX4_dev,
+				const struct pci_device_id *id)
+{
+	unsigned char temp;
+
+	/* match up the function */
+	if (PCI_FUNC(PIIX4_dev->devfn) != id->driver_data)
+		return -ENODEV;
+
+	printk(KERN_INFO "Found %s device\n", PIIX4_dev->name);
+
+#ifdef CONFIG_X86
+	if(ibm_dmi_probe() && PIIX4_dev->vendor == PCI_VENDOR_ID_INTEL) {
+		printk(KERN_ERR "i2c-piix4.o: IBM Laptop detected; this module "
+			"may corrupt your serial eeprom! Refusing to load "
+			"module!\n");
+		return -EPERM;
+	}
+#endif
+
+	/* Determine the address of the SMBus areas */
+	if (force_addr) {
+		piix4_smba = force_addr & 0xfff0;
+		force = 0;
+	} else {
+		pci_read_config_word(PIIX4_dev, SMBBA, &piix4_smba);
+		piix4_smba &= 0xfff0;
+		if(piix4_smba == 0) {
+			printk(KERN_ERR "i2c-piix4.o: SMB base address "
+				"uninitialized - upgrade BIOS or use "
+				"force_addr=0xaddr\n");
+			return -ENODEV;
+		}
+	}
+
+	if (!request_region(piix4_smba, SMBIOSIZE, "piix4-smbus")) {
+		printk(KERN_ERR "i2c-piix4.o: SMB region 0x%x already in "
+			"use!\n", piix4_smba);
+		return -ENODEV;
+	}
+
+	pci_read_config_byte(PIIX4_dev, SMBHSTCFG, &temp);
+
+	/* Some BIOS will set up the chipset incorrectly and leave a register
+	   in an undefined state (causing I2C to act very strangely). */
+	if (temp & 0x02) {
+		if (fix_hstcfg) {
+			printk(KERN_INFO "i2c-piix4.o: Working around buggy "
+				"BIOS (I2C)\n");
+			temp &= 0xfd;
+			pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp);
+		} else {
+			printk(KERN_INFO "i2c-piix4.o: Unusual config register "
+				"value\n");
+			printk(KERN_INFO "i2c-piix4.o: Try using fix_hstcfg=1 "
+				"if you experience problems\n");
+		}
+	}
+
+	/* If force_addr is set, we program the new address here. Just to make
+	   sure, we disable the PIIX4 first. */
+	if (force_addr) {
+		pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp & 0xfe);
+		pci_write_config_word(PIIX4_dev, SMBBA, piix4_smba);
+		pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp | 0x01);
+		printk(KERN_INFO "i2c-piix4.o: WARNING: SMBus interface set to "
+			"new address %04x!\n", piix4_smba);
+	} else if ((temp & 1) == 0) {
+		if (force) {
+			/* This should never need to be done, but has been
+			 * noted that many Dell machines have the SMBus
+			 * interface on the PIIX4 disabled!? NOTE: This assumes
+			 * I/O space and other allocations WERE done by the
+			 * Bios!  Don't complain if your hardware does weird
+			 * things after enabling this. :') Check for Bios
+			 * updates before resorting to this.
+			 */
+			pci_write_config_byte(PIIX4_dev, SMBHSTCFG,
+					      temp | 1);
+			printk(KERN_NOTICE "i2c-piix4.o: WARNING: SMBus "
+				"interface has been FORCEFULLY ENABLED!\n");
+		} else {
+			printk(KERN_ERR "i2c-piix4.o: Host SMBus controller "
+				"not enabled!\n");
+			release_region(piix4_smba, SMBIOSIZE);
+			piix4_smba = 0;
+			return -ENODEV;
+		}
+	}
+
+#ifdef DEBUG
+	if ((temp & 0x0E) == 8)
+		printk(KERN_DEBUG "i2c-piix4.o: Using Interrupt 9 for "
+			"SMBus.\n");
+	else if ((temp & 0x0E) == 0)
+		printk(KERN_DEBUG "i2c-piix4.o: Using Interrupt SMI# "
+			"for SMBus.\n");
+	else
+		printk(KERN_ERR "i2c-piix4.o: Illegal Interrupt configuration "
+			"(or code out of date)!\n");
+
+	pci_read_config_byte(PIIX4_dev, SMBREV, &temp);
+	printk(KERN_DEBUG "i2c-piix4.o: SMBREV = 0x%X\n", temp);
+	printk(KERN_DEBUG "i2c-piix4.o: SMBA = 0x%X\n", piix4_smba);
+#endif				/* DEBUG */
+
+	return 0;
+}
+
+
+/* Another internally used function */
+int piix4_transaction(void)
+{
+	int temp;
+	int result = 0;
+	int timeout = 0;
+
+#ifdef DEBUG
+	printk
+	    (KERN_DEBUG "i2c-piix4.o: Transaction (pre): CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, "
+	     "DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD),
+	     inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1));
+#endif
+
+	/* Make sure the SMBus host is ready to start transmitting */
+	if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
+#ifdef DEBUG
+		printk(KERN_DEBUG "i2c-piix4.o: SMBus busy (%02x). Resetting... \n",
+		       temp);
+#endif
+		outb_p(temp, SMBHSTSTS);
+		if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
+#ifdef DEBUG
+			printk(KERN_ERR "i2c-piix4.o: Failed! (%02x)\n", temp);
+#endif
+			return -1;
+		} else {
+#ifdef DEBUG
+			printk(KERN_DEBUG "i2c-piix4.o: Successfull!\n");
+#endif
+		}
+	}
+
+	/* start the transaction by setting bit 6 */
+	outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT);
+
+	/* We will always wait for a fraction of a second! (See PIIX4 docs errata) */
+	do {
+		i2c_delay(1);
+		temp = inb_p(SMBHSTSTS);
+	} while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT));
+
+#ifdef DEBUG
+	/* If the SMBus is still busy, we give up */
+	if (timeout >= MAX_TIMEOUT) {
+		printk(KERN_ERR "i2c-piix4.o: SMBus Timeout!\n");
+		result = -1;
+	}
+#endif
+
+	if (temp & 0x10) {
+		result = -1;
+#ifdef DEBUG
+		printk(KERN_ERR "i2c-piix4.o: Error: Failed bus transaction\n");
+#endif
+	}
+
+	if (temp & 0x08) {
+		result = -1;
+		printk
+		    (KERN_ERR "i2c-piix4.o: Bus collision! SMBus may be locked until next hard\n"
+		     "reset. (sorry!)\n");
+		/* Clock stops and slave is stuck in mid-transmission */
+	}
+
+	if (temp & 0x04) {
+		result = -1;
+#ifdef DEBUG
+		printk(KERN_ERR "i2c-piix4.o: Error: no response!\n");
+#endif
+	}
+
+	if (inb_p(SMBHSTSTS) != 0x00)
+		outb_p(inb(SMBHSTSTS), SMBHSTSTS);
+
+#ifdef DEBUG
+	if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
+		printk
+		    (KERN_ERR "i2c-piix4.o: Failed reset at end of transaction (%02x)\n",
+		     temp);
+	}
+	printk
+	    (KERN_DEBUG "i2c-piix4.o: Transaction (post): CNT=%02x, CMD=%02x, ADD=%02x, "
+	     "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD),
+	     inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1));
+#endif
+	return result;
+}
+
+/* Return -1 on error. */
+s32 piix4_access(struct i2c_adapter * adap, u16 addr,
+		 unsigned short flags, char read_write,
+		 u8 command, int size, union i2c_smbus_data * data)
+{
+	int i, len;
+
+	switch (size) {
+	case I2C_SMBUS_QUICK:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		size = PIIX4_QUICK;
+		break;
+	case I2C_SMBUS_BYTE:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(command, SMBHSTCMD);
+		size = PIIX4_BYTE;
+		break;
+	case I2C_SMBUS_BYTE_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(data->byte, SMBHSTDAT0);
+		size = PIIX4_BYTE_DATA;
+		break;
+	case I2C_SMBUS_WORD_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE) {
+			outb_p(data->word & 0xff, SMBHSTDAT0);
+			outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
+		}
+		size = PIIX4_WORD_DATA;
+		break;
+	case I2C_SMBUS_BLOCK_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE) {
+			len = data->block[0];
+			if (len < 0)
+				len = 0;
+			if (len > 32)
+				len = 32;
+			outb_p(len, SMBHSTDAT0);
+			i = inb_p(SMBHSTCNT);	/* Reset SMBBLKDAT */
+			for (i = 1; i <= len; i++)
+				outb_p(data->block[i], SMBBLKDAT);
+		}
+		size = PIIX4_BLOCK_DATA;
+		break;
+	default:
+		printk
+		    (KERN_WARNING "i2c-piix4.o: Unsupported transaction %d\n", size);
+		return -1;
+	}
+
+	outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT);
+
+	if (piix4_transaction())	/* Error in transaction */
+		return -1;
+
+	if ((read_write == I2C_SMBUS_WRITE) || (size == PIIX4_QUICK))
+		return 0;
+
+
+	switch (size) {
+	case PIIX4_BYTE:	/* Where is the result put? I assume here it is in
+				   SMBHSTDAT0 but it might just as well be in the
+				   SMBHSTCMD. No clue in the docs */
+
+		data->byte = inb_p(SMBHSTDAT0);
+		break;
+	case PIIX4_BYTE_DATA:
+		data->byte = inb_p(SMBHSTDAT0);
+		break;
+	case PIIX4_WORD_DATA:
+		data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
+		break;
+	case PIIX4_BLOCK_DATA:
+		data->block[0] = inb_p(SMBHSTDAT0);
+		i = inb_p(SMBHSTCNT);	/* Reset SMBBLKDAT */
+		for (i = 1; i <= data->block[0]; i++)
+			data->block[i] = inb_p(SMBBLKDAT);
+		break;
+	}
+	return 0;
+}
+
+
+u32 piix4_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	    I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	    I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static struct i2c_algorithm smbus_algorithm = {
+	.name		= "Non-I2C SMBus adapter",
+	.id		= I2C_ALGO_SMBUS,
+	.smbus_xfer	= piix4_access,
+	.functionality	= piix4_func,
+};
+
+static struct i2c_adapter piix4_adapter = {
+	.owner		= THIS_MODULE,
+	.name		= "unset",
+	.id		= I2C_ALGO_SMBUS | I2C_HW_SMBUS_PIIX4,
+	.algo		= &smbus_algorithm,
+};
+
+#ifndef PCI_DEVICE_ID_SERVERWORKS_CSB6
+#define PCI_DEVICE_ID_SERVERWORKS_CSB6 0x0203
+#endif
+
+static struct pci_device_id piix4_ids[] __devinitdata = {
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_82371AB_3,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+		.driver_data =	3
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_SERVERWORKS,
+		.device =	PCI_DEVICE_ID_SERVERWORKS_OSB4,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+		.driver_data =	0,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_SERVERWORKS,
+		.device =	PCI_DEVICE_ID_SERVERWORKS_CSB5,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+		.driver_data =	0,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_SERVERWORKS,
+		.device =	PCI_DEVICE_ID_SERVERWORKS_CSB6,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+		.driver_data =	0,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	PCI_DEVICE_ID_INTEL_82443MX_3,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+		.driver_data =	3,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_EFAR,
+		.device =	PCI_DEVICE_ID_EFAR_SLC90E66_3,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+		.driver_data =	0,
+	},
+	{ 0, }
+};
+
+static int __devinit piix4_probe(struct pci_dev *dev,
+				const struct pci_device_id *id)
+{
+	int retval;
+
+	retval = piix4_setup(dev, id);
+	if (retval)
+		return retval;
+
+	sprintf(piix4_adapter.name, "SMBus PIIX4 adapter at %04x",
+		piix4_smba);
+
+	if ((retval = i2c_add_adapter(&piix4_adapter))) {
+		printk(KERN_ERR "i2c-piix4.o: Couldn't register adapter!\n");
+		release_region(piix4_smba, SMBIOSIZE);
+		piix4_smba = 0;
+	}
+
+	return retval;
+}
+
+static void __devexit piix4_remove(struct pci_dev *dev)
+{
+	if (piix4_smba) {
+		i2c_del_adapter(&piix4_adapter);
+		release_region(piix4_smba, SMBIOSIZE);
+		piix4_smba = 0;
+	}
+}
+
+static struct pci_driver piix4_driver = {
+	.name		= "piix4 smbus",
+	.id_table	= piix4_ids,
+	.probe		= piix4_probe,
+	.remove		= __devexit_p(piix4_remove),
+};
+
+static int __init i2c_piix4_init(void)
+{
+	printk("i2c-piix4.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return pci_module_init(&piix4_driver);
+}
+
+static void __exit i2c_piix4_exit(void)
+{
+	pci_unregister_driver(&piix4_driver);
+}
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and "
+		"Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("PIIX4 SMBus driver");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_piix4_init);
+module_exit(i2c_piix4_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-pport.c linux-2.4.27-leo/drivers/i2c/i2c-pport.c
--- linux-2.4.27/drivers/i2c/i2c-pport.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-pport.c	2004-09-20 21:34:38.000000000 +0100
@@ -0,0 +1,205 @@
+/* ------------------------------------------------------------------------- */
+/* i2c-pport.c i2c-hw access  for primitive i2c par. port adapter	     */
+/* ------------------------------------------------------------------------- */
+/*   Copyright (C) 2001    Daniel Smolik
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.		     */
+/* ------------------------------------------------------------------------- */
+
+/*
+	See doc/i2c-pport for instructions on wiring to the
+	parallel port connector.
+
+	Cut & paste :-)  based on Velleman K8000 driver by Simon G. Vogl
+*/
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <asm/io.h>
+
+
+#define DEFAULT_BASE 0x378
+static int base=0;
+static unsigned char PortData = 0;
+
+/* ----- global defines -----------------------------------------------	*/
+#define DEB(x)		/* should be reasonable open, close &c. 	*/
+#define DEB2(x) 	/* low level debugging - very slow 		*/
+#define DEBE(x)	x	/* error messages 				*/
+#define DEBINIT(x) x	/* detection status messages			*/
+
+/* --- Convenience defines for the parallel port:			*/
+#define BASE	(unsigned int)(data)
+#define DATA	BASE			/* Centronics data port		*/
+#define STAT	(BASE+1)		/* Centronics status port	*/
+#define CTRL	(BASE+2)		/* Centronics control port	*/
+
+/* we will use SDA  - Auto Linefeed(14)   bit 1  POUT   */
+/* we will use SCL - Initialize printer(16)    BUSY bit 2*/
+
+#define  SET_SCL    | 0x04
+#define  CLR_SCL    & 0xFB
+
+
+
+
+#define  SET_SDA    & 0x04
+#define  CLR_SDA    | 0x02
+
+
+/* ----- local functions ----------------------------------------------	*/
+
+
+static void bit_pport_setscl(void *data, int state)
+{
+	if (state) {
+		//high
+		PortData = PortData SET_SCL;
+	} else {
+		//low
+		PortData = PortData CLR_SCL; 
+	}
+	outb(PortData, CTRL);
+}
+
+static void bit_pport_setsda(void *data, int state)
+{
+	if (state) {
+		
+		PortData = PortData SET_SDA;
+	} else {
+
+		PortData = PortData CLR_SDA;
+	}
+	outb(PortData, CTRL);
+} 
+
+static int bit_pport_getscl(void *data)
+{
+
+	return ( 4 == ( (inb_p(CTRL)) & 0x04 ) );
+}
+
+static int bit_pport_getsda(void *data)
+{
+	return ( 0 == ( (inb_p(CTRL)) & 0x02 ) );
+}
+
+static int bit_pport_init(void)
+{
+	if (!request_region((base+2),1, "i2c (PPORT adapter)")) {
+		return -ENODEV;	
+	} else {
+		/* test for PPORT adap. 	*/
+	
+
+		PortData=inb(base+2);
+		PortData= (PortData SET_SDA) SET_SCL;
+		outb(PortData,base+2);				
+
+		if (!(inb(base+2) | 0x06)) {	/* SDA and SCL will be high	*/
+			DEBINIT(printk("i2c-pport.o: SDA and SCL was low.\n"));
+			return -ENODEV;
+		} else {
+		
+			/*SCL high and SDA low*/
+			PortData = PortData SET_SCL CLR_SDA;
+			outb(PortData,base+2);	
+			schedule_timeout(400);
+			if ( !(inb(base+2) | 0x4) ) {
+				//outb(0x04,base+2);
+				DEBINIT(printk("i2c-port.o: SDA was high.\n"));
+				return -ENODEV;
+			}
+		}
+		bit_pport_setsda((void*)base,1);
+		bit_pport_setscl((void*)base,1);
+	}
+	return 0;
+}
+
+
+/* ------------------------------------------------------------------------
+ * Encapsulate the above functions in the correct operations structure.
+ * This is only done when more than one hardware adapter is supported.
+ */
+static struct i2c_algo_bit_data bit_pport_data = {
+	.setsda		= bit_pport_setsda,
+	.setscl		= bit_pport_setscl,
+	.getsda		= bit_pport_getsda,
+	.getscl		= bit_pport_getscl,
+	.udelay		= 40,
+	.mdelay		= 80,
+	.timeout	= HZ
+};
+
+static struct i2c_adapter bit_pport_ops = {
+	.owner		= THIS_MODULE,
+	.name		= "Primitive Parallel port adaptor",
+	.id		= I2C_HW_B_PPORT,
+	.algo_data	= &bit_pport_data,
+};
+
+static int __init i2c_bitpport_init(void)
+{
+	printk("i2c-pport.o: i2c Primitive parallel port adapter module version %s (%s)\n", I2C_VERSION, I2C_DATE);
+
+	if (base==0) {
+		/* probe some values */
+		base=DEFAULT_BASE;
+		bit_pport_data.data=(void*)DEFAULT_BASE;
+		if (bit_pport_init()==0) {
+			if(i2c_bit_add_bus(&bit_pport_ops) < 0)
+				return -ENODEV;
+		} else {
+			return -ENODEV;
+		}
+	} else {
+		bit_pport_data.data=(void*)base;
+		if (bit_pport_init()==0) {
+			if(i2c_bit_add_bus(&bit_pport_ops) < 0)
+				return -ENODEV;
+		} else {
+			return -ENODEV;
+		}
+	}
+	printk("i2c-pport.o: found device at %#x.\n",base);
+	return 0;
+}
+
+static void __exit i2c_bitpport_exit(void)
+{
+	i2c_bit_del_bus(&bit_pport_ops);
+	release_region((base+2),1);
+}
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Daniel Smolik <marvin@sitour.cz>");
+MODULE_DESCRIPTION("I2C-Bus adapter routines for Primitive parallel port adapter");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(base, "i");
+
+module_init(i2c_bitpport_init);
+module_exit(i2c_bitpport_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-proc.c linux-2.4.27-leo/drivers/i2c/i2c-proc.c
--- linux-2.4.27/drivers/i2c/i2c-proc.c	2004-02-20 14:11:41.000000000 +0000
+++ linux-2.4.27-leo/drivers/i2c/i2c-proc.c	2004-09-20 21:34:38.000000000 +0100
@@ -23,29 +23,26 @@
     This driver puts entries in /proc/sys/dev/sensors for each I2C device
 */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/ctype.h>
 #include <linux/sysctl.h>
 #include <linux/proc_fs.h>
+#include <linux/init.h>
 #include <linux/ioport.h>
-#include <asm/uaccess.h>
-
 #include <linux/i2c.h>
 #include <linux/i2c-proc.h>
+#include <asm/uaccess.h>
 
-#include <linux/init.h>
-
-#ifndef THIS_MODULE
-#define THIS_MODULE NULL
+#ifndef CONFIG_SYSCTL
+#error Your kernel lacks sysctl support (CONFIG_SYSCTL)!
 #endif
 
-static int i2c_create_name(char **name, const char *prefix,
-			       struct i2c_adapter *adapter, int addr);
 static int i2c_parse_reals(int *nrels, void *buffer, int bufsize,
 			       long *results, int magnitude);
-static int i2c_write_reals(int nrels, void *buffer, int *bufsize,
+static int i2c_write_reals(int nrels, void *buffer, size_t *bufsize,
 			       long *results, int magnitude);
 static int i2c_proc_chips(ctl_table * ctl, int write,
 			      struct file *filp, void *buffer,
@@ -55,22 +52,10 @@
 				void *newval, size_t newlen,
 				void **context);
 
-int __init sensors_init(void);
-
 #define SENSORS_ENTRY_MAX 20
 static struct ctl_table_header *i2c_entries[SENSORS_ENTRY_MAX];
 
 static struct i2c_client *i2c_clients[SENSORS_ENTRY_MAX];
-static unsigned short i2c_inodes[SENSORS_ENTRY_MAX];
-
-static ctl_table sysctl_table[] = {
-	{CTL_DEV, "dev", NULL, 0, 0555},
-	{0},
-	{DEV_SENSORS, "sensors", NULL, 0, 0555},
-	{0},
-	{0, NULL, NULL, 0, 0555},
-	{0}
-};
 
 static ctl_table i2c_proc_dev_sensors[] = {
 	{SENSORS_CHIPS, "chips", NULL, 0, 0644, NULL, &i2c_proc_chips,
@@ -91,31 +76,45 @@
 
 
 static struct ctl_table_header *i2c_proc_header;
-static int i2c_initialized;
 
 /* This returns a nice name for a new directory; for example lm78-isa-0310
    (for a LM78 chip on the ISA bus at port 0x310), or lm75-i2c-3-4e (for
    a LM75 chip on the third i2c bus at address 0x4e).  
    name is allocated first. */
-int i2c_create_name(char **name, const char *prefix,
-			struct i2c_adapter *adapter, int addr)
+static char *generate_name(struct i2c_client *client, const char *prefix)
 {
-	char name_buffer[50];
-	int id;
-	if (i2c_is_isa_adapter(adapter))
+	struct i2c_adapter *adapter = client->adapter;
+	int addr = client->addr;
+	char name_buffer[50], *name;
+
+	if (i2c_is_isa_adapter(adapter)) {
 		sprintf(name_buffer, "%s-isa-%04x", prefix, addr);
-	else {
-		if ((id = i2c_adapter_id(adapter)) < 0)
-			return -ENOENT;
+	} else if (adapter->algo->smbus_xfer || adapter->algo->master_xfer) {
+		int id = i2c_adapter_id(adapter);
+		if (id < 0)
+			return ERR_PTR(-ENOENT);
 		sprintf(name_buffer, "%s-i2c-%d-%02x", prefix, id, addr);
+	} else {	/* dummy adapter, generate prefix */
+		int end, i;
+
+		sprintf(name_buffer, "%s-", prefix);
+		end = strlen(name_buffer);
+
+		for (i = 0; i < 32; i++) {
+			if (adapter->algo->name[i] == ' ')
+				break;
+			name_buffer[end++] = tolower(adapter->algo->name[i]);
+		}
+
+		name_buffer[end] = 0;
+		sprintf(name_buffer + end, "-%04x", addr);
 	}
-	*name = kmalloc(strlen(name_buffer) + 1, GFP_KERNEL);
-	if (!*name) {
-		printk (KERN_WARNING "i2c_create_name: not enough memory\n");
-		return -ENOMEM;
-	}
-	strcpy(*name, name_buffer);
-	return 0;
+
+	name = kmalloc(strlen(name_buffer) + 1, GFP_KERNEL);
+	if (!name)
+		return ERR_PTR(-ENOMEM);
+	strcpy(name, name_buffer);
+	return name;
 }
 
 /* This rather complex function must be called when you want to add an entry
@@ -124,139 +123,91 @@
    ctl_template should be a template of the newly created directory. It is
    copied in memory. The extra2 field of each file is set to point to client.
    If any driver wants subdirectories within the newly created directory,
-   this function must be updated! 
-   controlling_mod is the controlling module. It should usually be
-   THIS_MODULE when calling. Note that this symbol is not defined in
-   kernels before 2.3.13; define it to NULL in that case. We will not use it
-   for anything older than 2.3.27 anyway. */
+   this function must be updated!  */
 int i2c_register_entry(struct i2c_client *client, const char *prefix,
-			   ctl_table * ctl_template,
-			   struct module *controlling_mod)
+		       struct ctl_table *ctl_template)
 {
-	int i, res, len, id;
-	ctl_table *new_table;
-	char *name;
-	struct ctl_table_header *new_header;
+	struct { struct ctl_table root[2], dev[2], sensors[2]; } *tbl;
+	struct ctl_table_header *hdr;
+	struct ctl_table *tmp, *leaf;
+	const char *name;
+	int id, len = 0;
 
-	if ((res = i2c_create_name(&name, prefix, client->adapter,
-				       client->addr))) return res;
+	name = generate_name(client, prefix);
+	if (IS_ERR(name))
+		return PTR_ERR(name);
 
-	for (id = 0; id < SENSORS_ENTRY_MAX; id++)
-		if (!i2c_entries[id]) {
-			break;
-		}
-	if (id == SENSORS_ENTRY_MAX) {
-		kfree(name);
-		return -ENOMEM;
+	for (id = 0; id < SENSORS_ENTRY_MAX; id++) {
+		if (!i2c_entries[id])
+			goto free_slot;
 	}
-	id += 256;
 
-	len = 0;
+	goto out_free_name;
+
+ free_slot:
 	while (ctl_template[len].procname)
 		len++;
-	len += 7;
-	if (!(new_table = kmalloc(sizeof(ctl_table) * len, GFP_KERNEL))) {
-		kfree(name);
-		return -ENOMEM;
-	}
-
-	memcpy(new_table, sysctl_table, 6 * sizeof(ctl_table));
-	new_table[0].child = &new_table[2];
-	new_table[2].child = &new_table[4];
-	new_table[4].child = &new_table[6];
-	new_table[4].procname = name;
-	new_table[4].ctl_name = id;
-	memcpy(new_table + 6, ctl_template, (len - 6) * sizeof(ctl_table));
-	for (i = 6; i < len; i++)
-		new_table[i].extra2 = client;
-
-	if (!(new_header = register_sysctl_table(new_table, 0))) {
-		kfree(new_table);
-		kfree(name);
-		return -ENOMEM;
-	}
-
-	i2c_entries[id - 256] = new_header;
-
-	i2c_clients[id - 256] = client;
-#ifdef DEBUG
-	if (!new_header || !new_header->ctl_table ||
-	    !new_header->ctl_table->child ||
-	    !new_header->ctl_table->child->child ||
-	    !new_header->ctl_table->child->child->de) {
-		printk
-		    ("i2c-proc.o: NULL pointer when trying to install fill_inode fix!\n");
-		return id;
-	}
-#endif				/* DEBUG */
-	i2c_inodes[id - 256] =
-	    new_header->ctl_table->child->child->de->low_ino;
-	new_header->ctl_table->child->child->de->owner = controlling_mod;
-
-	return id;
+	tbl = kmalloc(sizeof(*tbl) + sizeof(ctl_table) * (len + 1), 
+				  GFP_KERNEL);
+	if (!tbl)
+		goto out_free_name;
+	memset(tbl, 0, sizeof(*tbl));
+
+	/* The client sysctls */
+	leaf = (struct ctl_table *) (tbl + 1);
+	memcpy(leaf, ctl_template, sizeof(ctl_table) * (len+1));
+	for (tmp = leaf; tmp->ctl_name; tmp++)
+		tmp->extra2 = client;
+
+	tbl->sensors->ctl_name = id+256;
+	tbl->sensors->procname = name;
+	tbl->sensors->mode = 0555;
+	tbl->sensors->child = leaf;
+
+	tbl->dev->ctl_name = DEV_SENSORS;
+	tbl->dev->procname = "sensors";
+	tbl->dev->mode = 0555;
+	tbl->dev->child = tbl->sensors;
+
+	tbl->root->ctl_name = CTL_DEV;
+	tbl->root->procname = "dev";
+	tbl->root->mode = 0555;
+	tbl->root->child = tbl->dev;
+
+	hdr = register_sysctl_table(tbl->root, 0);
+	if (!hdr)
+		goto out_free_tbl;
+
+	i2c_entries[id] = hdr;
+	i2c_clients[id] = client;
+
+	return (id + 256);	/* XXX(hch) why?? */
+
+ out_free_tbl:
+	kfree(tbl);
+ out_free_name:
+	kfree(name);
+	return -ENOMEM;
 }
 
 void i2c_deregister_entry(int id)
 {
-	ctl_table *table;
-	char *temp;
 	id -= 256;
-	if (i2c_entries[id]) {
-		table = i2c_entries[id]->ctl_table;
-		unregister_sysctl_table(i2c_entries[id]);
-		/* 2-step kfree needed to keep gcc happy about const points */
-		(const char *) temp = table[4].procname;
-		kfree(temp);
-		kfree(table);
-		i2c_entries[id] = NULL;
-		i2c_clients[id] = NULL;
-	}
-}
 
-/* Monitor access for /proc/sys/dev/sensors; make unloading i2c-proc.o 
-   impossible if some process still uses it or some file in it */
-void i2c_fill_inode(struct inode *inode, int fill)
-{
-	if (fill)
-		MOD_INC_USE_COUNT;
-	else
-		MOD_DEC_USE_COUNT;
-}
-
-/* Monitor access for /proc/sys/dev/sensors/ directories; make unloading
-   the corresponding module impossible if some process still uses it or
-   some file in it */
-void i2c_dir_fill_inode(struct inode *inode, int fill)
-{
-	int i;
-	struct i2c_client *client;
+	if (i2c_entries[id]) {
+		struct ctl_table_header *hdr = i2c_entries[id];
+		struct ctl_table *tbl = hdr->ctl_table;
 
-#ifdef DEBUG
-	if (!inode) {
-		printk("i2c-proc.o: Warning: inode NULL in fill_inode()\n");
-		return;
+		unregister_sysctl_table(hdr);
+		kfree(tbl->child->child->procname);
+		kfree(tbl); /* actually the whole anonymous struct */
 	}
-#endif				/* def DEBUG */
 
-	for (i = 0; i < SENSORS_ENTRY_MAX; i++)
-		if (i2c_clients[i]
-		    && (i2c_inodes[i] == inode->i_ino)) break;
-#ifdef DEBUG
-	if (i == SENSORS_ENTRY_MAX) {
-		printk
-		    ("i2c-proc.o: Warning: inode (%ld) not found in fill_inode()\n",
-		     inode->i_ino);
-		return;
-	}
-#endif				/* def DEBUG */
-	client = i2c_clients[i];
-	if (fill)
-		client->driver->inc_use(client);
-	else
-		client->driver->dec_use(client);
+	i2c_entries[id] = NULL;
+	i2c_clients[id] = NULL;
 }
 
-int i2c_proc_chips(ctl_table * ctl, int write, struct file *filp,
+static int i2c_proc_chips(ctl_table * ctl, int write, struct file *filp,
 		       void *buffer, size_t * lenp)
 {
 	char BUF[SENSORS_PREFIX_MAX + 30];
@@ -294,7 +245,7 @@
 	return 0;
 }
 
-int i2c_sysctl_chips(ctl_table * table, int *name, int nlen,
+static int i2c_sysctl_chips(ctl_table * table, int *name, int nlen,
 			 void *oldval, size_t * oldlenp, void *newval,
 			 size_t newlen, void **context)
 {
@@ -456,7 +407,7 @@
    WARNING! This is tricky code. I have tested it, but there may still be
             hidden bugs in it, even leading to crashes and things!
 */
-int i2c_parse_reals(int *nrels, void *buffer, int bufsize,
+static int i2c_parse_reals(int *nrels, void *buffer, int bufsize,
 			 long *results, int magnitude)
 {
 	int maxels, min, mag;
@@ -557,7 +508,7 @@
 	return 0;
 }
 
-int i2c_write_reals(int nrels, void *buffer, int *bufsize,
+static int i2c_write_reals(int nrels, void *buffer, size_t *bufsize,
 			 long *results, int magnitude)
 {
 #define BUFLEN 20
@@ -646,6 +597,7 @@
 					I2C_FUNC_SMBUS_QUICK)) return -1;
 
 	for (addr = 0x00; addr <= (is_isa ? 0xffff : 0x7f); addr++) {
+		/* XXX: WTF is going on here??? */
 		if ((is_isa && check_region(addr, 1)) ||
 		    (!is_isa && i2c_check_addr(adapter, addr)))
 			continue;
@@ -846,46 +798,33 @@
 	return 0;
 }
 
-int __init sensors_init(void)
+static int __init i2c_proc_init(void)
 {
 	printk(KERN_INFO "i2c-proc.o version %s (%s)\n", I2C_VERSION, I2C_DATE);
-	i2c_initialized = 0;
 	if (!
 	    (i2c_proc_header =
-	     register_sysctl_table(i2c_proc, 0))) return -ENOMEM;
+	     register_sysctl_table(i2c_proc, 0))) {
+		printk(KERN_ERR "i2c-proc.o: error: sysctl interface not supported by kernel!\n");
+		return -EPERM;
+	}
 	i2c_proc_header->ctl_table->child->de->owner = THIS_MODULE;
-	i2c_initialized++;
 	return 0;
 }
 
+static void __exit i2c_proc_exit(void)
+{
+	unregister_sysctl_table(i2c_proc_header);
+}
+
+EXPORT_SYMBOL(i2c_register_entry);
 EXPORT_SYMBOL(i2c_deregister_entry);
-EXPORT_SYMBOL(i2c_detect);
 EXPORT_SYMBOL(i2c_proc_real);
-EXPORT_SYMBOL(i2c_register_entry);
 EXPORT_SYMBOL(i2c_sysctl_real);
-
-#ifdef MODULE
+EXPORT_SYMBOL(i2c_detect);
 
 MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
 MODULE_DESCRIPTION("i2c-proc driver");
 MODULE_LICENSE("GPL");
 
-int i2c_cleanup(void)
-{
-	if (i2c_initialized >= 1) {
-		unregister_sysctl_table(i2c_proc_header);
-		i2c_initialized--;
-	}
-	return 0;
-}
-
-int init_module(void)
-{
-	return sensors_init();
-}
-
-int cleanup_module(void)
-{
-	return i2c_cleanup();
-}
-#endif				/* MODULE */
+module_init(i2c_proc_init);
+module_exit(i2c_proc_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-rpx.c linux-2.4.27-leo/drivers/i2c/i2c-rpx.c
--- linux-2.4.27/drivers/i2c/i2c-rpx.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-rpx.c	2004-09-20 21:34:38.000000000 +0100
@@ -0,0 +1,101 @@
+/*
+ * Embedded Planet RPX Lite MPC8xx CPM I2C interface.
+ * Copyright (c) 1999 Dan Malek (dmalek@jlc.net).
+ *
+ * moved into proper i2c interface;
+ * Brad Parker (brad@heeltoe.com)
+ *
+ * RPX lite specific parts of the i2c interface
+ * Update:  There actually isn't anything RPXLite-specific about this module.
+ * This should work for most any 8xx board.  The console messages have been 
+ * changed to eliminate RPXLite references.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/stddef.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-8xx.h>
+#include <asm/mpc8xx.h>
+#include <asm/commproc.h>
+
+
+static void
+rpx_iic_init(struct i2c_algo_8xx_data *data)
+{
+	volatile cpm8xx_t *cp;
+	volatile immap_t *immap;
+
+	cp = cpmp;	/* Get pointer to Communication Processor */
+	immap = (immap_t *)IMAP_ADDR;	/* and to internal registers */
+
+	data->iip = (iic_t *)&cp->cp_dparam[PROFF_IIC];
+
+	/* Check for and use a microcode relocation patch.
+	*/
+	if ((data->reloc = data->iip->iic_rpbase))
+		data->iip = (iic_t *)&cp->cp_dpmem[data->iip->iic_rpbase];
+		
+	data->i2c = (i2c8xx_t *)&(immap->im_i2c);
+	data->cp = cp;
+
+	/* Initialize Port B IIC pins.
+	*/
+	cp->cp_pbpar |= 0x00000030;
+	cp->cp_pbdir |= 0x00000030;
+	cp->cp_pbodr |= 0x00000030;
+
+	/* Allocate space for two transmit and two receive buffer
+	 * descriptors in the DP ram.
+	 */
+	data->dp_addr = m8xx_cpm_dpalloc(sizeof(cbd_t) * 4);
+
+	/* ptr to i2c area */
+	data->i2c = (i2c8xx_t *)&(((immap_t *)IMAP_ADDR)->im_i2c);
+}
+
+static int rpx_install_isr(int irq, void (*func)(void *, void *), void *data)
+{
+	/* install interrupt handler */
+	cpm_install_handler(irq, (void (*)(void *, struct pt_regs *)) func, data);
+
+	return 0;
+}
+
+static struct i2c_algo_8xx_data rpx_data = {
+	.setisr = rpx_install_isr
+};
+
+static struct i2c_adapter rpx_ops = {
+	.owner		= THIS_MODULE,
+	.name		= "m8xx",
+	.id		= I2C_HW_MPC8XX_EPON,
+	.algo_data	= &rpx_data,
+};
+
+static int __init i2c_rpx_init(void)
+{
+	printk("i2c-rpx.o: i2c MPC8xx module version %s (%s)\n", I2C_VERSION, I2C_DATE);
+
+	/* reset hardware to sane state */
+	rpx_iic_init(&rpx_data);
+
+	if (i2c_8xx_add_bus(&rpx_ops) < 0) {
+		printk("i2c-rpx: Unable to register with I2C\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void __exit i2c_rpx_exit(void)
+{
+	i2c_8xx_del_bus(&rpx_ops);
+}
+
+MODULE_AUTHOR("Dan Malek <dmalek@jlc.net>");
+MODULE_DESCRIPTION("I2C-Bus adapter routines for MPC8xx boards");
+
+module_init(i2c_rpx_init);
+module_exit(i2c_rpx_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-savage4.c linux-2.4.27-leo/drivers/i2c/i2c-savage4.c
--- linux-2.4.27/drivers/i2c/i2c-savage4.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-savage4.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,229 @@
+/*
+    i2c-savage4.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+    Copyright (C) 1998-2003  The LM Sensors Team
+    Alexander Wold <awold@bigfoot.com>
+    Mark D. Studebaker <mdsxyz123@yahoo.com>
+    
+    Based on i2c-voodoo3.c.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* This interfaces to the I2C bus of the Savage4 to gain access to
+   the BT869 and possibly other I2C devices. The DDC bus is not
+   yet supported because its register is not memory-mapped.
+   However we leave the DDC code here, commented out, to make
+   it easier to add later.
+*/
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/param.h> /* for HZ */
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+/* 3DFX defines */
+/* #define PCI_VENDOR_ID_S3		0x5333 */
+#define PCI_CHIP_SAVAGE3D	0x8A20
+#define PCI_CHIP_SAVAGE3D_MV	0x8A21
+#define PCI_CHIP_SAVAGE4	0x8A22
+#define PCI_CHIP_SAVAGE2000	0x9102
+#define PCI_CHIP_PROSAVAGE_PM	0x8A25
+#define PCI_CHIP_PROSAVAGE_KM	0x8A26
+#define PCI_CHIP_SAVAGE_MX_MV	0x8c10
+#define PCI_CHIP_SAVAGE_MX	0x8c11
+#define PCI_CHIP_SAVAGE_IX_MV	0x8c12
+#define PCI_CHIP_SAVAGE_IX	0x8c13
+
+#define REG 0xff20	/* Serial Port 1 Register */
+
+/* bit locations in the register */
+//#define DDC_ENAB	0x00040000
+//#define DDC_SCL_OUT	0x00080000
+//#define DDC_SDA_OUT	0x00100000
+//#define DDC_SCL_IN	0x00200000
+//#define DDC_SDA_IN	0x00400000
+#define I2C_ENAB	0x00000020
+#define I2C_SCL_OUT	0x00000001
+#define I2C_SDA_OUT	0x00000002
+#define I2C_SCL_IN	0x00000008
+#define I2C_SDA_IN	0x00000010
+
+/* initialization states */
+#define INIT2	0x20
+/* #define INIT3	0x4 */
+
+/* delays */
+#define CYCLE_DELAY	10
+#define TIMEOUT		(HZ / 2)
+
+
+static void config_s4(struct pci_dev *dev);
+
+static unsigned long ioaddr;
+
+/* The sav GPIO registers don't have individual masks for each bit
+   so we always have to read before writing. */
+
+static void bit_savi2c_setscl(void *data, int val)
+{
+	unsigned int r;
+	r = readl(ioaddr + REG);
+	if(val)
+		r |= I2C_SCL_OUT;
+	else
+		r &= ~I2C_SCL_OUT;
+	writel(r, ioaddr + REG);
+	readl(ioaddr + REG);	/* flush posted write */
+}
+
+static void bit_savi2c_setsda(void *data, int val)
+{
+	unsigned int r;
+	r = readl(ioaddr + REG);
+	if(val)
+		r |= I2C_SDA_OUT;
+	else
+		r &= ~I2C_SDA_OUT;
+	writel(r, ioaddr + REG);
+	readl(ioaddr + REG);	/* flush posted write */
+}
+
+/* The GPIO pins are open drain, so the pins always remain outputs.
+   We rely on the i2c-algo-bit routines to set the pins high before
+   reading the input from other chips. */
+
+static int bit_savi2c_getscl(void *data)
+{
+	return (0 != (readl(ioaddr + REG) & I2C_SCL_IN));
+}
+
+static int bit_savi2c_getsda(void *data)
+{
+	return (0 != (readl(ioaddr + REG) & I2C_SDA_IN));
+}
+
+/* Configures the chip */
+
+void config_s4(struct pci_dev *dev)
+{
+	unsigned int cadr;
+
+	/* map memory */
+	cadr = dev->resource[0].start;
+	cadr &= PCI_BASE_ADDRESS_MEM_MASK;
+	ioaddr = (unsigned long)ioremap_nocache(cadr, 0x0080000);
+	if(ioaddr) {
+//		writel(0x8160, ioaddr + REG2);
+		writel(0x00000020, ioaddr + REG);
+		printk("i2c-savage4: Using Savage4 at 0x%lx\n", ioaddr);
+	}
+}
+
+
+static struct i2c_algo_bit_data sav_i2c_bit_data = {
+	.setsda		= bit_savi2c_setsda,
+	.setscl		= bit_savi2c_setscl,
+	.getsda		= bit_savi2c_getsda,
+	.getscl		= bit_savi2c_getscl,
+	.udelay		= CYCLE_DELAY,
+	.mdelay		= CYCLE_DELAY,
+	.timeout	= TIMEOUT
+};
+
+static struct i2c_adapter savage4_i2c_adapter = {
+	.owner		= THIS_MODULE,
+	.name		= "I2C Savage4 adapter",
+	.id		= I2C_HW_B_SAVG,
+	.algo_data	= &sav_i2c_bit_data,
+};
+
+static struct pci_device_id savage4_ids[] __devinitdata = {
+	{
+		.vendor =	PCI_VENDOR_ID_S3,
+		.device =	PCI_CHIP_SAVAGE4,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_S3,
+		.device =	PCI_CHIP_SAVAGE2000,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{ 0, }
+};
+
+static int __devinit savage4_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	config_s4(dev);
+	return i2c_bit_add_bus(&savage4_i2c_adapter);
+}
+
+static void __devexit savage4_remove(struct pci_dev *dev)
+{
+	i2c_bit_del_bus(&savage4_i2c_adapter);
+}
+
+
+/* Don't register driver to avoid driver conflicts */
+/*
+static struct pci_driver savage4_driver = {
+	.name		= "savage4 smbus",
+	.id_table	= savage4_ids,
+	.probe		= savage4_probe,
+	.remove		= __devexit_p(savage4_remove),
+};
+*/
+
+static int __init i2c_savage4_init(void)
+{
+	struct pci_dev *dev;
+	const struct pci_device_id *id;
+
+	printk("i2c-savage4.o version %s (%s)\n", LM_VERSION, LM_DATE);
+/*
+	return pci_module_init(&savage4_driver);
+*/
+	pci_for_each_dev(dev) {
+		id = pci_match_device(savage4_ids, dev);
+		if(id)
+			if(savage4_probe(dev, id) >= 0)
+				return 0;
+	}
+	return -ENODEV;
+}
+
+static void __exit i2c_savage4_exit(void)
+{
+/*
+	pci_unregister_driver(&savage4_driver);
+*/
+	savage4_remove(NULL);
+	iounmap((void *)ioaddr);
+}
+
+MODULE_AUTHOR("Alexander Wold <awold@bigfoot.com> "
+		"and Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("Savage4 I2C/SMBus driver");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_savage4_init);
+module_exit(i2c_savage4_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-sis5595.c linux-2.4.27-leo/drivers/i2c/i2c-sis5595.c
--- linux-2.4.27/drivers/i2c/i2c-sis5595.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-sis5595.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,481 @@
+/*
+    sis5595.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl> and
+    Philip Edelbrock <phil@netroedge.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* Note: we assume there can only be one SIS5595 with one SMBus interface */
+
+/*
+   Note: all have mfr. ID 0x1039.
+   SUPPORTED		PCI ID		
+	5595		0008
+
+   Note: these chips contain a 0008 device which is incompatible with the
+         5595. We recognize these by the presence of the listed
+         "blacklist" PCI ID and refuse to load.
+
+   NOT SUPPORTED	PCI ID		BLACKLIST PCI ID	
+	 540		0008		0540
+	 550		0008		0550
+	5513		0008		5511
+	5581		0008		5597
+	5582		0008		5597
+	5597		0008		5597
+	5598		0008		5597/5598
+	 630		0008		0630
+	 645		0008		0645
+	 646		0008		0646
+	 648		0008		0648
+	 650		0008		0650
+	 651		0008		0651
+	 730		0008		0730
+	 735		0008		0735
+	 745		0008		0745
+	 746		0008		0746
+*/
+
+/* TO DO: 
+ * Add Block Transfers (ugly, but supported by the adapter)
+ * Add adapter resets
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+MODULE_LICENSE("GPL");
+
+static int blacklist[] = {
+			PCI_DEVICE_ID_SI_540,
+			PCI_DEVICE_ID_SI_550,
+			PCI_DEVICE_ID_SI_630,
+			PCI_DEVICE_ID_SI_730,
+			PCI_DEVICE_ID_SI_5511, /* 5513 chip has the 0008 device but
+						  that ID shows up in other chips so we
+						  use the 5511 ID for recognition */
+			PCI_DEVICE_ID_SI_5597,
+			PCI_DEVICE_ID_SI_5598,
+			0x645,
+			0x646,
+			0x648,
+			0x650,
+			0x651,
+			0x735,
+			0x745,
+			0x746,
+			0 };
+
+/* Length of ISA address segment */
+#define SIS5595_EXTENT 8
+/* SIS5595 SMBus registers */
+#define SMB_STS_LO 0x00
+#define SMB_STS_HI 0x01
+#define SMB_CTL_LO 0x02
+#define SMB_CTL_HI 0x03
+#define SMB_ADDR   0x04
+#define SMB_CMD    0x05
+#define SMB_PCNT   0x06
+#define SMB_CNT    0x07
+#define SMB_BYTE   0x08
+#define SMB_DEV    0x10
+#define SMB_DB0    0x11
+#define SMB_DB1    0x12
+#define SMB_HAA    0x13
+
+/* PCI Address Constants */
+#define SMB_INDEX  0x38
+#define SMB_DAT    0x39
+#define SIS5595_ENABLE_REG 0x40
+#define ACPI_BASE  0x90
+
+/* Other settings */
+#define MAX_TIMEOUT 500
+
+/* SIS5595 constants */
+#define SIS5595_QUICK      0x00
+#define SIS5595_BYTE       0x02
+#define SIS5595_BYTE_DATA  0x04
+#define SIS5595_WORD_DATA  0x06
+#define SIS5595_PROC_CALL  0x08
+#define SIS5595_BLOCK_DATA 0x0A
+
+/* insmod parameters */
+
+/* If force_addr is set to anything different from 0, we forcibly enable
+   the device at the given address. */
+static int force_addr = 0;
+MODULE_PARM(force_addr, "i");
+MODULE_PARM_DESC(force_addr,
+		 "Initialize the base address of the i2c controller");
+
+static int sis5595_transaction(void);
+
+static unsigned short sis5595_base = 0;
+
+static u8 sis5595_read(u8 reg)
+{
+	outb(reg, sis5595_base + SMB_INDEX);
+	return inb(sis5595_base + SMB_DAT);
+}
+
+static void sis5595_write(u8 reg, u8 data)
+{
+	outb(reg, sis5595_base + SMB_INDEX);
+	outb(data, sis5595_base + SMB_DAT);
+}
+
+
+/* Detect whether a SIS5595 can be found, and initialize it, where necessary.
+   Note the differences between kernels with the old PCI BIOS interface and
+   newer kernels with the real PCI interface. In compat.h some things are
+   defined to make the transition easier. */
+int sis5595_setup(struct pci_dev *SIS5595_dev)
+{
+	u16 a;
+	u8 val;
+	int *i;
+
+	/* Look for imposters */
+	for(i = blacklist; *i != 0; i++) {
+		if (pci_find_device(PCI_VENDOR_ID_SI, *i, NULL)) {
+			printk("i2c-sis5595.o: Error: Looked for SIS5595 but found unsupported device %.4X\n", *i);
+			return -ENODEV;
+		}
+	}
+
+/* Determine the address of the SMBus areas */
+	pci_read_config_word(SIS5595_dev, ACPI_BASE, &sis5595_base);
+	if(sis5595_base == 0 && force_addr == 0) {
+		printk("i2c-sis5595.o: ACPI base address uninitialized - upgrade BIOS or use force_addr=0xaddr\n");
+		return -ENODEV;
+	}
+
+	if(force_addr)
+		sis5595_base = force_addr & ~(SIS5595_EXTENT - 1);
+#ifdef DEBUG
+	printk("ACPI Base address: %04x\n", sis5595_base);
+#endif
+	/* NB: We grab just the two SMBus registers here, but this may still
+	 * interfere with ACPI :-(  */
+	if (check_region(sis5595_base + SMB_INDEX, 2)) {
+		printk
+		    ("i2c-sis5595.o: SMBus registers 0x%04x-0x%04x already in use!\n",
+		     sis5595_base + SMB_INDEX,
+		     sis5595_base + SMB_INDEX + 1);
+		return -ENODEV;
+	}
+
+	if(force_addr) {
+		printk("i2c-sis5595.o: forcing ISA address 0x%04X\n", sis5595_base);
+		if (PCIBIOS_SUCCESSFUL !=
+		    pci_write_config_word(SIS5595_dev, ACPI_BASE, sis5595_base))
+			return -ENODEV;
+		if (PCIBIOS_SUCCESSFUL !=
+		    pci_read_config_word(SIS5595_dev, ACPI_BASE, &a))
+			return -ENODEV;
+		if ((a & ~(SIS5595_EXTENT - 1)) != sis5595_base) {
+			/* doesn't work for some chips! */
+			printk("i2c-sis5595.o: force address failed - not supported?\n");
+			return -ENODEV;
+		}
+	}
+
+	if (PCIBIOS_SUCCESSFUL !=
+	    pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val))
+		return -ENODEV;
+	if((val & 0x80) == 0) {
+		printk("sis5595.o: enabling ACPI\n");
+		if (PCIBIOS_SUCCESSFUL !=
+		    pci_write_config_byte(SIS5595_dev, SIS5595_ENABLE_REG,
+		                      val | 0x80))
+			return -ENODEV;
+		if (PCIBIOS_SUCCESSFUL !=
+		    pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val))
+			return -ENODEV;
+		if((val & 0x80) == 0) {	/* doesn't work for some chips? */
+			printk("sis5595.o: ACPI enable failed - not supported?\n");
+			return -ENODEV;
+		}
+	}
+
+	/* Everything is happy, let's grab the memory and set things up. */
+	request_region(sis5595_base + SMB_INDEX, 2, "sis5595-smbus");
+	return(0);
+}
+
+
+/* Another internally used function */
+int sis5595_transaction(void)
+{
+	int temp;
+	int result = 0;
+	int timeout = 0;
+
+	/* Make sure the SMBus host is ready to start transmitting */
+	if (
+	    (temp =
+	     sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) !=
+	    0x00) {
+#ifdef DEBUG
+		printk("i2c-sis5595.o: SMBus busy (%04x). Resetting... \n",
+		       temp);
+#endif
+		sis5595_write(SMB_STS_LO, temp & 0xff);
+		sis5595_write(SMB_STS_HI, temp >> 8);
+		if (
+		    (temp =
+		     sis5595_read(SMB_STS_LO) +
+		     (sis5595_read(SMB_STS_HI) << 8)) != 0x00) {
+#ifdef DEBUG
+			printk("i2c-sis5595.o: Failed! (%02x)\n", temp);
+#endif
+			return -1;
+		} else {
+#ifdef DEBUG
+			printk("i2c-sis5595.o: Successfull!\n");
+#endif
+		}
+	}
+
+	/* start the transaction by setting bit 4 */
+	sis5595_write(SMB_CTL_LO, sis5595_read(SMB_CTL_LO) | 0x10);
+
+	/* We will always wait for a fraction of a second! */
+	do {
+		i2c_delay(1);
+		temp = sis5595_read(SMB_STS_LO);
+	} while (!(temp & 0x40) && (timeout++ < MAX_TIMEOUT));
+
+	/* If the SMBus is still busy, we give up */
+	if (timeout >= MAX_TIMEOUT) {
+#ifdef DEBUG
+		printk("i2c-sis5595.o: SMBus Timeout!\n");
+#endif
+		result = -1;
+	}
+
+	if (temp & 0x10) {
+		result = -1;
+#ifdef DEBUG
+		printk("i2c-sis5595.o: Error: Failed bus transaction\n");
+#endif
+	}
+
+	if (temp & 0x20) {
+		result = -1;
+		printk
+		    ("i2c-sis5595.o: Bus collision! SMBus may be locked until next hard\n"
+		     "reset (or not...)\n");
+		/* Clock stops and slave is stuck in mid-transmission */
+	}
+
+	if (
+	    (temp =
+	     sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) !=
+	    0x00) {
+		sis5595_write(SMB_STS_LO, temp & 0xff);
+		sis5595_write(SMB_STS_HI, temp >> 8);
+	}
+
+	if (
+	    (temp =
+	     sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) !=
+	    0x00) {
+
+#ifdef DEBUG
+		printk
+		    ("i2c-sis5595.o: Failed reset at end of transaction (%02x)\n",
+		     temp);
+#endif
+	}
+	return result;
+}
+
+/* Return -1 on error. */
+s32 sis5595_access(struct i2c_adapter * adap, u16 addr,
+		   unsigned short flags, char read_write,
+		   u8 command, int size, union i2c_smbus_data * data)
+{
+	switch (size) {
+	case I2C_SMBUS_QUICK:
+		sis5595_write(SMB_ADDR,
+			      ((addr & 0x7f) << 1) | (read_write & 0x01));
+		size = SIS5595_QUICK;
+		break;
+	case I2C_SMBUS_BYTE:
+		sis5595_write(SMB_ADDR,
+			      ((addr & 0x7f) << 1) | (read_write & 0x01));
+		if (read_write == I2C_SMBUS_WRITE)
+			sis5595_write(SMB_CMD, command);
+		size = SIS5595_BYTE;
+		break;
+	case I2C_SMBUS_BYTE_DATA:
+		sis5595_write(SMB_ADDR,
+			      ((addr & 0x7f) << 1) | (read_write & 0x01));
+		sis5595_write(SMB_CMD, command);
+		if (read_write == I2C_SMBUS_WRITE)
+			sis5595_write(SMB_BYTE, data->byte);
+		size = SIS5595_BYTE_DATA;
+		break;
+	case I2C_SMBUS_PROC_CALL:
+	case I2C_SMBUS_WORD_DATA:
+		sis5595_write(SMB_ADDR,
+			      ((addr & 0x7f) << 1) | (read_write & 0x01));
+		sis5595_write(SMB_CMD, command);
+		if (read_write == I2C_SMBUS_WRITE) {
+			sis5595_write(SMB_BYTE, data->word & 0xff);
+			sis5595_write(SMB_BYTE + 1,
+				      (data->word & 0xff00) >> 8);
+		}
+		size =
+		    (size ==
+		     I2C_SMBUS_PROC_CALL) ? SIS5595_PROC_CALL :
+		    SIS5595_WORD_DATA;
+		break;
+/*
+	case I2C_SMBUS_BLOCK_DATA:
+		printk("sis5595.o: Block data not yet implemented!\n");
+		return -1;
+		break;
+*/
+	default:
+		printk
+		    (KERN_WARNING "sis5595.o: Unsupported transaction %d\n", size);
+		return -1;
+	}
+
+	sis5595_write(SMB_CTL_LO, ((size & 0x0E)));
+
+	if (sis5595_transaction())	/* Error in transaction */
+		return -1;
+
+	if ((size != SIS5595_PROC_CALL) &&
+	    ((read_write == I2C_SMBUS_WRITE) || (size == SIS5595_QUICK)))
+		return 0;
+
+
+	switch (size) {
+	case SIS5595_BYTE:	/* Where is the result put? I assume here it is in
+				   SMB_DATA but it might just as well be in the
+				   SMB_CMD. No clue in the docs */
+	case SIS5595_BYTE_DATA:
+		data->byte = sis5595_read(SMB_BYTE);
+		break;
+	case SIS5595_WORD_DATA:
+	case SIS5595_PROC_CALL:
+		data->word =
+		    sis5595_read(SMB_BYTE) +
+		    (sis5595_read(SMB_BYTE + 1) << 8);
+		break;
+	}
+	return 0;
+}
+
+
+u32 sis5595_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	    I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	    I2C_FUNC_SMBUS_PROC_CALL;
+}
+
+
+static struct i2c_algorithm smbus_algorithm = {
+	.name		= "Non-I2C SMBus adapter",
+	.id		= I2C_ALGO_SMBUS,
+	.smbus_xfer	= sis5595_access,
+	.functionality	= sis5595_func,
+};
+
+static struct i2c_adapter sis5595_adapter = {
+	.owner		= THIS_MODULE,
+	.name		= "unset",
+	.id		= I2C_ALGO_SMBUS | I2C_HW_SMBUS_SIS5595,
+	.algo		= &smbus_algorithm,
+};
+
+
+static struct pci_device_id sis5595_ids[] __devinitdata = {
+	{
+		.vendor =	PCI_VENDOR_ID_SI,
+		.device =	PCI_DEVICE_ID_SI_503,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{ 0, }
+};
+
+static int __devinit sis5595_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+
+	if (sis5595_setup(dev)) {
+		printk
+		    ("i2c-sis5595.o: SIS5595 not detected, module not inserted.\n");
+
+		return -ENODEV;
+	}
+
+	sprintf(sis5595_adapter.name, "SMBus SIS5595 adapter at %04x",
+		sis5595_base + SMB_INDEX);
+	i2c_add_adapter(&sis5595_adapter);
+
+	return 0;
+}
+
+static void __devexit sis5595_remove(struct pci_dev *dev)
+{
+	i2c_del_adapter(&sis5595_adapter);
+	release_region(sis5595_base + SMB_INDEX, 2);
+}
+
+
+static struct pci_driver sis5595_driver = {
+	.name		= "sis5595 smbus",
+	.id_table	= sis5595_ids,
+	.probe		= sis5595_probe,
+	.remove		= __devexit_p(sis5595_remove),
+};
+
+static int __init i2c_sis5595_init(void)
+{
+	printk("i2c-sis5595.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return pci_module_init(&sis5595_driver);
+}
+
+
+static void __exit i2c_sis5595_exit(void)
+{
+	pci_unregister_driver(&sis5595_driver);
+}
+
+
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
+MODULE_DESCRIPTION("SIS5595 SMBus driver");
+
+module_init(i2c_sis5595_init);
+module_exit(i2c_sis5595_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-sis630.c linux-2.4.27-leo/drivers/i2c/i2c-sis630.c
--- linux-2.4.27/drivers/i2c/i2c-sis630.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-sis630.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,527 @@
+/*
+    i2c-sis630.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+
+    Copyright (c) 2002,2003 Alexander Malysh <amalysh@web.de>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+   Changes:
+   24.08.2002
+   	Fixed the typo in sis630_access (Thanks to Mark M. Hoffman)
+	Changed sis630_transaction.(Thanks to Mark M. Hoffman)
+   18.09.2002
+	Added SIS730 as supported.
+   21.09.2002
+	Added high_clock module option.If this option is set
+	used Host Master Clock 56KHz (default 14KHz).For now we save old Host
+	Master Clock and after transaction completed restore (otherwise
+	it's confuse BIOS and hung Machine).
+   24.09.2002
+	Fixed typo in sis630_access
+	Fixed logical error by restoring of Host Master Clock
+   31.07.2003
+   	Added block data read/write support.
+*/
+
+/*
+   Status: beta
+
+   Supports:
+	SIS 630
+	SIS 730
+
+   Note: we assume there can only be one device, with one SMBus interface.
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+
+#ifdef DEBUG
+#define DBG(x...) printk(KERN_DEBUG "i2c-sis630.o: " x)
+#else
+#define DBG(x...)
+#endif
+
+/* SIS630 SMBus registers */
+#define SMB_STS     0x80 /* status */
+#define SMB_EN      0x81 /* status enable */
+#define SMB_CNT     0x82
+#define SMBHOST_CNT 0x83
+#define SMB_ADDR    0x84
+#define SMB_CMD     0x85
+#define SMB_PCOUNT  0x86 /* processed count */
+#define SMB_COUNT   0x87
+#define SMB_BYTE    0x88 /* ~0x8F data byte field */
+#define SMBDEV_ADDR 0x90
+#define SMB_DB0     0x91
+#define SMB_DB1     0x92
+#define SMB_SAA     0x93
+
+/* register count for request_region */
+#define SIS630_SMB_IOREGION 20
+
+/* PCI address constants */
+/* acpi base address register  */
+#define SIS630_ACPI_BASE_REG 0x74
+/* bios control register */
+#define SIS630_BIOS_CTL_REG  0x40
+
+/* Other settings */
+#define MAX_TIMEOUT   500
+
+/* SIS630 constants */
+#define SIS630_QUICK      0x00
+#define SIS630_BYTE       0x01
+#define SIS630_BYTE_DATA  0x02
+#define SIS630_WORD_DATA  0x03
+#define SIS630_PCALL      0x04
+#define SIS630_BLOCK_DATA 0x05
+
+/* insmod parameters */
+static int high_clock = 0;
+static int force = 0;
+MODULE_PARM(high_clock, "i");
+MODULE_PARM_DESC(high_clock, "Set Host Master Clock to 56KHz (default 14KHz).");
+MODULE_PARM(force, "i");
+MODULE_PARM_DESC(force, "Forcibly enable the SIS630. DANGEROUS!");
+
+/* acpi base address */
+static unsigned short acpi_base = 0;
+
+/* supported chips */
+static int supported[] = {
+	PCI_DEVICE_ID_SI_630,
+	PCI_DEVICE_ID_SI_730,
+	0 /* terminates the list */
+};
+
+static inline u8 sis630_read(u8 reg) {
+	return inb(acpi_base + reg);
+}
+
+static inline void sis630_write(u8 reg, u8 data) {
+	outb(data, acpi_base + reg);
+}
+
+static int sis630_transaction_start(int size, u8 *oldclock) {
+        int temp;
+
+        /*
+	  Make sure the SMBus host is ready to start transmitting.
+	*/
+	if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) {
+                DBG("SMBus busy (%02x).Resetting...\n",temp);
+		/* kill smbus transaction */
+		sis630_write(SMBHOST_CNT, 0x20);
+
+		if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) {
+                        DBG("Failed! (%02x)\n", temp);
+			return -1;
+                } else {
+                        DBG("Successfull!\n");
+		}
+        }
+
+	/* save old clock, so we can prevent machine for hung */
+	*oldclock = sis630_read(SMB_CNT);
+
+	DBG("saved clock 0x%02x\n", *oldclock);
+
+	/* disable timeout interrupt , set Host Master Clock to 56KHz if requested */
+	if (high_clock > 0)
+		sis630_write(SMB_CNT, 0x20);
+	else
+		sis630_write(SMB_CNT, (*oldclock & ~0x40));
+
+	/* clear all sticky bits */
+	temp = sis630_read(SMB_STS);
+	sis630_write(SMB_STS, temp & 0x1e);
+
+	/* start the transaction by setting bit 4 and size */
+	sis630_write(SMBHOST_CNT,0x10 | (size & 0x07));
+
+	return 0;
+}
+
+static int sis630_transaction_wait(int size) {
+        int temp, result = 0, timeout = 0;
+
+        /* We will always wait for a fraction of a second! */
+        do {
+                i2c_delay(1);
+                temp = sis630_read(SMB_STS);
+		/* check if block transmitted */
+		if (size == SIS630_BLOCK_DATA && (temp & 0x10))
+		    break;
+        } while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT));
+
+        /* If the SMBus is still busy, we give up */
+        if (timeout >= MAX_TIMEOUT) {
+                DBG("SMBus Timeout!\n");
+		result = -1;
+        }
+
+        if (temp & 0x02) {
+                result = -1;
+                DBG("Error: Failed bus transaction\n");
+	}
+
+        if (temp & 0x04) {
+                result = -1;
+                printk(KERN_ERR "i2c-sis630.o: Bus collision!\n");
+		/*
+		   TBD: Datasheet say:
+		   	the software should clear this bit and restart SMBUS operation.
+			Should we do it or user start request again?
+		*/
+        }
+
+	return result;
+}
+
+static void sis630_transaction_end(u8 oldclock) {
+        int temp = 0;
+
+        /* clear all status "sticky" bits */
+	sis630_write(SMB_STS, temp);
+
+	DBG("SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT));
+
+	/*
+	* restore old Host Master Clock if high_clock is set
+	* and oldclock was not 56KHz
+	*/
+	if (high_clock > 0 && !(oldclock & 0x20))
+		sis630_write(SMB_CNT,(sis630_read(SMB_CNT) & ~0x20));
+
+	DBG("SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT));
+}
+
+static int sis630_transaction(int size) {
+        int result = 0;
+	u8 oldclock = 0;
+
+	if (!(result = sis630_transaction_start(size, &oldclock))) {
+		result = sis630_transaction_wait(size);
+		sis630_transaction_end(oldclock);
+	}
+
+        return result;
+}
+
+static int sis630_block_data(union i2c_smbus_data * data, int read_write) {
+	int i, len = 0, rc = 0;
+	u8 oldclock = 0;
+
+	if (read_write == I2C_SMBUS_WRITE) {
+		len = data->block[0];
+		if (len < 0)
+			len = 0;
+		else if (len > 32)
+			len = 32;
+		sis630_write(SMB_COUNT, len);
+		for (i=1; i <= len; i++) {
+			DBG("set data 0x%02x\n", data->block[i]);
+			/* set data */
+			sis630_write(SMB_BYTE+(i-1)%8, data->block[i]);
+			if (i==8 || (len<8 && i==len)) {
+				DBG("start trans len=%d i=%d\n",len ,i);
+				/* first transaction */
+				if (sis630_transaction_start(SIS630_BLOCK_DATA, &oldclock))
+					return -1;
+			}
+			else if ((i-1)%8 == 7 || i==len) {
+				DBG("trans_wait len=%d i=%d\n",len,i);
+				if (i>8) {
+					DBG("clear smbary_sts len=%d i=%d\n",len,i);
+					/*
+					   If this is not first transaction,
+					   we must clear sticky bit.
+					   clear SMBARY_STS
+					*/
+					sis630_write(SMB_STS,0x10);
+				}
+				if (sis630_transaction_wait(SIS630_BLOCK_DATA)) {
+					DBG("trans_wait failed\n");
+					rc = -1;
+					break;
+				}
+
+			}
+		}
+	}
+	else { /* read request */
+		data->block[0] = len = 0;
+		if (sis630_transaction_start(SIS630_BLOCK_DATA, &oldclock)) {
+			return -1;
+		}
+		do {
+			if (sis630_transaction_wait(SIS630_BLOCK_DATA)) {
+				DBG("trans_wait failed\n");
+				rc = -1;
+				break;
+			}
+			/* if this first transaction then read byte count */
+			if (len == 0)
+				data->block[0] = sis630_read(SMB_COUNT);
+
+			/* just to be sure */
+			if (data->block[0] > 32)
+				data->block[0] = 32;
+
+			DBG("block data read len=0x%x\n", data->block[0]);
+
+			for (i=0; i < 8 && len < data->block[0]; i++,len++) {
+				DBG("read i=%d len=%d\n", i, len);
+				data->block[len+1] = sis630_read(SMB_BYTE+i);
+			}
+
+			DBG("clear smbary_sts len=%d i=%d\n",len,i);
+
+			/* clear SMBARY_STS */
+			sis630_write(SMB_STS,0x10);
+		} while(len < data->block[0]);
+	}
+
+	sis630_transaction_end(oldclock);
+
+	return rc;
+}
+
+/* Return -1 on error. */
+static s32 sis630_access(struct i2c_adapter * adap, u16 addr,
+		   unsigned short flags, char read_write,
+		   u8 command, int size, union i2c_smbus_data * data)
+{
+
+	switch (size) {
+		case I2C_SMBUS_QUICK:
+			sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
+			size = SIS630_QUICK;
+			break;
+		case I2C_SMBUS_BYTE:
+			sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
+			if (read_write == I2C_SMBUS_WRITE)
+				sis630_write(SMB_CMD, command);
+			size = SIS630_BYTE;
+			break;
+		case I2C_SMBUS_BYTE_DATA:
+			sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
+			sis630_write(SMB_CMD, command);
+			if (read_write == I2C_SMBUS_WRITE)
+				sis630_write(SMB_BYTE, data->byte);
+			size = SIS630_BYTE_DATA;
+			break;
+		case I2C_SMBUS_PROC_CALL:
+		case I2C_SMBUS_WORD_DATA:
+			sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
+			sis630_write(SMB_CMD, command);
+			if (read_write == I2C_SMBUS_WRITE) {
+				sis630_write(SMB_BYTE, data->word & 0xff);
+				sis630_write(SMB_BYTE + 1,(data->word & 0xff00) >> 8);
+			}
+			size = (size == I2C_SMBUS_PROC_CALL ? SIS630_PCALL : SIS630_WORD_DATA);
+			break;
+		case I2C_SMBUS_BLOCK_DATA:
+			sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
+			sis630_write(SMB_CMD, command);
+			size = SIS630_BLOCK_DATA;
+			return sis630_block_data(data, read_write);
+		default:
+			printk("Unsupported I2C size\n");
+			return -1;
+			break;
+	}
+
+
+	if (sis630_transaction(size))
+		return -1;
+
+        if ((size != SIS630_PCALL) &&
+		((read_write == I2C_SMBUS_WRITE) || (size == SIS630_QUICK))) {
+                return 0;
+	}
+
+	switch(size) {
+		case SIS630_BYTE:
+		case SIS630_BYTE_DATA:
+			data->byte = sis630_read(SMB_BYTE);
+			break;
+		case SIS630_PCALL:
+		case SIS630_WORD_DATA:
+			data->word = sis630_read(SMB_BYTE) + (sis630_read(SMB_BYTE + 1) << 8);
+			break;
+		default:
+			return -1;
+			break;
+	}
+
+	return 0;
+}
+
+
+static u32 sis630_func(struct i2c_adapter *adapter) {
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
+		I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL |
+		I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static int __devinit sis630_setup(struct pci_dev *sis630_dev) {
+	unsigned char b;
+	struct pci_dev *dummy = NULL;
+	int i;
+
+	/* check for supported SiS devices */
+	for (i=0; supported[i] > 0; i++) {
+		if ((dummy = pci_find_device(PCI_VENDOR_ID_SI, supported[i], dummy)))
+			break; /* found */
+	}
+
+	if (!dummy && force > 0) {
+		printk(KERN_ERR "i2c-sis630.o: WARNING: Can't detect SIS630 compatible device, but "
+			"loading because of force option enabled\n");
+	}
+	else if (!dummy) {
+		return -ENODEV;
+	}
+
+	/*
+	   Enable ACPI first , so we can accsess reg 74-75
+	   in acpi io space and read acpi base addr
+	*/
+	if (PCIBIOS_SUCCESSFUL !=
+	    pci_read_config_byte(sis630_dev, SIS630_BIOS_CTL_REG,&b)) {
+		printk(KERN_ERR "i2c-sis630.o: Error: Can't read bios ctl reg\n");
+		return -ENODEV;
+	}
+
+	/* if ACPI already enabled , do nothing */
+	if (!(b & 0x80) &&
+	    PCIBIOS_SUCCESSFUL !=
+	    pci_write_config_byte(sis630_dev,SIS630_BIOS_CTL_REG,b|0x80)) {
+		printk(KERN_ERR "i2c-sis630.o: Error: Can't enable ACPI\n");
+		return -ENODEV;
+	}
+	/* Determine the ACPI base address */
+	if (PCIBIOS_SUCCESSFUL !=
+	    pci_read_config_word(sis630_dev,SIS630_ACPI_BASE_REG,&acpi_base)) {
+		printk(KERN_ERR "i2c-sis630.o: Error: Can't determine ACPI base address\n");
+		return -ENODEV;
+	}
+
+	DBG("ACPI base at 0x%04x\n", acpi_base);
+
+	/* Everything is happy, let's grab the memory and set things up. */
+	if (!request_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION, "sis630-smbus")){
+		printk(KERN_ERR "i2c-sis630.o: SMBus registers 0x%04x-0x%04x "
+			"already in use!\n",acpi_base + SMB_STS, acpi_base + SMB_SAA);
+		acpi_base = 0; /* reset acpi_base */
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+
+static struct i2c_algorithm smbus_algorithm = {
+	.name		= "Non-I2C SMBus adapter",
+	.id		= I2C_ALGO_SMBUS,
+	.smbus_xfer	= sis630_access,
+	.functionality	= sis630_func,
+};
+
+static struct i2c_adapter sis630_adapter = {
+	.owner		= THIS_MODULE,
+	.name		= "unset",
+	.id		= I2C_ALGO_SMBUS | I2C_HW_SMBUS_SIS630,
+	.algo		= &smbus_algorithm,
+};
+
+
+static struct pci_device_id sis630_ids[] __devinitdata = {
+	{
+		.vendor =	PCI_VENDOR_ID_SI,
+		.device =	PCI_DEVICE_ID_SI_503,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{ 0, }
+};
+
+static int __devinit sis630_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	if (sis630_setup(dev)) {
+		printk(KERN_ERR "i2c-sis630.o: SIS630 comp. bus not detected, module not inserted.\n");
+		return -ENODEV;
+	}
+
+	sprintf(sis630_adapter.name, "SMBus SIS630 adapter at %04x",
+		acpi_base + SMB_STS);
+
+	return i2c_add_adapter(&sis630_adapter);
+}
+
+static void __devexit sis630_remove(struct pci_dev *dev)
+{
+	if (acpi_base) {
+		i2c_del_adapter(&sis630_adapter);
+		release_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION);
+		acpi_base = 0;
+	}
+}
+
+
+static struct pci_driver sis630_driver = {
+	.name		= "sis630 smbus",
+	.id_table	= sis630_ids,
+	.probe		= sis630_probe,
+	.remove		= __devexit_p(sis630_remove),
+};
+
+static int __init i2c_sis630_init(void)
+{
+	printk("i2c-sis630.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return pci_module_init(&sis630_driver);
+}
+
+
+static void __exit i2c_sis630_exit(void)
+{
+	pci_unregister_driver(&sis630_driver);
+}
+
+
+
+
+MODULE_LICENSE("GPL");
+
+MODULE_AUTHOR("Alexander Malysh <amalysh@web.de>");
+MODULE_DESCRIPTION("SIS630 SMBus driver");
+
+module_init(i2c_sis630_init);
+module_exit(i2c_sis630_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-sis645.c linux-2.4.27-leo/drivers/i2c/i2c-sis645.c
--- linux-2.4.27/drivers/i2c/i2c-sis645.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-sis645.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,590 @@
+/*
+    sis645.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+
+    Copyright (c) 2003 Mark M. Hoffman <mhoffman@lightlink.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+    This module must be considered BETA unless and until
+    the chipset manufacturer releases a datasheet.
+
+    The register definitions are based on the SiS630.
+    The method for *finding* the registers is based on trial and error.
+
+    A history of changes to this file is available by anonymous CVS:
+    http://www2.lm-sensors.nu/~lm78/download.html
+*/
+
+/*   25th March 2004
+     Support for Sis655 chipsets added by Ken Healy
+*/
+
+/*
+    Note: we assume there can only be one SiS645 with one SMBus interface
+*/
+
+/* #define DEBUG 1 */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <asm/io.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+#include <linux/sensors_compat.h>
+
+#define DRV_NAME "i2c-sis645"
+
+/* SiS645DX north bridge (defined in 2.4.21) */
+#ifndef PCI_DEVICE_ID_SI_646
+#define PCI_DEVICE_ID_SI_646 0x0646
+#endif
+
+/* SiS648 north bridge (defined in 2.4.21) */
+#ifndef PCI_DEVICE_ID_SI_648
+#define PCI_DEVICE_ID_SI_648 0x0648
+#endif
+
+/* SiS650 north bridge (defined in 2.4.19) */
+#ifndef PCI_DEVICE_ID_SI_650
+#define PCI_DEVICE_ID_SI_650 0x0650
+#endif
+
+/* SiS651 north bridge (defined in 2.4.21)*/
+#ifndef PCI_DEVICE_ID_SI_651
+#define PCI_DEVICE_ID_SI_651 0x0651
+#endif
+
+/* SiS655 north bridge (defined in 2.4.22)*/
+#ifndef PCI_DEVICE_ID_SI_655
+#define PCI_DEVICE_ID_SI_655 0x0655
+#endif
+
+/* SiS746 north bridge (defined in 2.4.21) */
+#ifndef PCI_DEVICE_ID_SI_746
+#define PCI_DEVICE_ID_SI_746 0x0746
+#endif
+
+/* SiS85C503/5513 (LPC Bridge) */
+#ifndef PCI_DEVICE_ID_SI_LPC
+#define PCI_DEVICE_ID_SI_LPC 0x0018
+#endif
+
+/* SiS961 south bridge */
+#ifndef PCI_DEVICE_ID_SI_961
+#define PCI_DEVICE_ID_SI_961 0x0961
+#endif
+
+/* SiS962 south bridge */
+#ifndef PCI_DEVICE_ID_SI_962
+#define PCI_DEVICE_ID_SI_962 0x0962
+#endif
+
+/* SiS963 south bridge */
+#ifndef PCI_DEVICE_ID_SI_963
+#define PCI_DEVICE_ID_SI_963 0x0963
+#endif
+
+/* SMBus ID */
+#ifndef PCI_DEVICE_ID_SI_SMBUS
+#define PCI_DEVICE_ID_SI_SMBUS 0x16
+#endif
+
+/* base address register in PCI config space */
+#define SIS645_BAR 0x04
+
+/* SiS645 SMBus registers */
+#define SMB_STS      0x00
+#define SMB_EN       0x01
+#define SMB_CNT      0x02
+#define SMB_HOST_CNT 0x03
+#define SMB_ADDR     0x04
+#define SMB_CMD      0x05
+#define SMB_PCOUNT   0x06
+#define SMB_COUNT    0x07
+#define SMB_BYTE     0x08
+#define SMB_DEV_ADDR 0x10
+#define SMB_DB0      0x11
+#define SMB_DB1      0x12
+#define SMB_SAA      0x13
+
+/* register count for request_region */
+#define SMB_IOSIZE 0x20
+
+/* Other settings */
+#define MAX_TIMEOUT 500
+
+/* SiS645 SMBus constants */
+#define SIS645_QUICK      0x00
+#define SIS645_BYTE       0x01
+#define SIS645_BYTE_DATA  0x02
+#define SIS645_WORD_DATA  0x03
+#define SIS645_PROC_CALL  0x04
+#define SIS645_BLOCK_DATA 0x05
+
+static struct i2c_adapter sis645_adapter;
+static u16 sis645_smbus_base = 0;
+
+static inline u8 sis645_read(u8 reg)
+{
+	return inb(sis645_smbus_base + reg) ;
+}
+
+static inline void sis645_write(u8 reg, u8 data)
+{
+	outb(data, sis645_smbus_base + reg) ;
+}
+
+#ifdef CONFIG_HOTPLUG
+
+/* Turns on SMBus device if it is not; return 0 iff successful
+ */
+static int __devinit sis645_enable_smbus(struct pci_dev *dev)
+{
+	u8 val = 0;
+
+	pci_read_config_byte(dev, 0x77, &val);
+
+#ifdef DEBUG
+	printk(KERN_DEBUG DRV_NAME ": Config byte was 0x%02x.\n", val);
+#endif
+
+	pci_write_config_byte(dev, 0x77, val & ~0x10);
+
+	pci_read_config_byte(dev, 0x77, &val);
+
+	if (val & 0x10) {
+#ifdef DEBUG
+		printk(KERN_DEBUG DRV_NAME ": Config byte stuck!\n");
+#endif
+		return -1;
+	}
+
+	return 0;
+}
+
+/* Builds the basic pci_dev for SiS645 SMBus
+ */
+static int __devinit sis645_build_dev(struct pci_dev **smbus_dev,
+			    struct pci_dev *bridge_dev)
+{
+	struct pci_dev temp_dev;
+	u16 vid = 0, did = 0;
+	int ret;
+
+	/* fill in the device structure for search */
+	memset(&temp_dev, 0, sizeof(temp_dev));
+	temp_dev.bus = bridge_dev->bus;
+	temp_dev.sysdata = bridge_dev->bus->sysdata;
+	temp_dev.hdr_type = PCI_HEADER_TYPE_NORMAL;
+
+	/* the SMBus device is function 1 on the same unit as the ISA bridge */
+	temp_dev.devfn = bridge_dev->devfn + 1;
+
+	/* query to make sure */
+	ret = pci_read_config_word(&temp_dev, PCI_VENDOR_ID, &vid);
+	if (ret || PCI_VENDOR_ID_SI != vid) {
+		printk(KERN_ERR DRV_NAME ": Couldn't find SMBus device!\n");
+		return ret;
+	}
+	temp_dev.vendor = vid;
+
+	ret = pci_read_config_word(&temp_dev, PCI_DEVICE_ID, &did);
+	if (ret || PCI_DEVICE_ID_SI_SMBUS != did) {
+		printk(KERN_ERR DRV_NAME ": Couldn't find SMBus device!\n");
+		return ret;
+	}
+	temp_dev.device = did;
+
+	/* ok, we've got it... request some memory and finish it off */
+	*smbus_dev = kmalloc(sizeof(**smbus_dev), GFP_ATOMIC);
+	if (NULL == *smbus_dev) {
+		printk(KERN_ERR DRV_NAME ": Out of memory!\n");
+		return -ENOMEM;
+	}
+
+	**smbus_dev = temp_dev;
+	
+	ret = pci_setup_device(*smbus_dev);
+	if (ret) {
+		printk(KERN_ERR DRV_NAME ": pci_setup_device failed (0x%08x)\n",ret);
+	}
+	return ret;
+}
+
+/* See if a SMBus can be found, and enable it if possible.
+ */
+static int __devinit sis645_hotplug_smbus(void)
+{
+	int ret;
+	struct pci_dev *smbus_dev, *bridge_dev ;
+
+	if ((bridge_dev = pci_find_device(PCI_VENDOR_ID_SI,
+			PCI_DEVICE_ID_SI_961, NULL)))
+		printk(KERN_INFO DRV_NAME ": Found SiS961 south bridge.\n");
+
+	else if ((bridge_dev = pci_find_device(PCI_VENDOR_ID_SI,
+			PCI_DEVICE_ID_SI_962, NULL)))
+		printk(KERN_INFO DRV_NAME ": Found SiS962 [MuTIOL Media IO].\n");
+
+	else if ((bridge_dev = pci_find_device(PCI_VENDOR_ID_SI,
+			PCI_DEVICE_ID_SI_963, NULL)))
+		printk(KERN_INFO DRV_NAME ": Found SiS963 [MuTIOL Media IO].\n");
+		
+	else if ((bridge_dev = pci_find_device(PCI_VENDOR_ID_SI,
+			PCI_DEVICE_ID_SI_503, NULL)) ||
+		(bridge_dev = pci_find_device(PCI_VENDOR_ID_SI,
+			PCI_DEVICE_ID_SI_LPC, NULL))) {
+
+		printk(KERN_INFO DRV_NAME ": Found SiS south bridge in compatability mode(?)\n");
+
+		/* look for known compatible north bridges */
+		if ((NULL == pci_find_device(PCI_VENDOR_ID_SI, 
+				PCI_DEVICE_ID_SI_645, NULL))
+			&& (NULL == pci_find_device(PCI_VENDOR_ID_SI,
+				PCI_DEVICE_ID_SI_646, NULL))
+			&& (NULL == pci_find_device(PCI_VENDOR_ID_SI,
+				PCI_DEVICE_ID_SI_648, NULL))
+			&& (NULL == pci_find_device(PCI_VENDOR_ID_SI,
+				PCI_DEVICE_ID_SI_650, NULL))
+			&& (NULL == pci_find_device(PCI_VENDOR_ID_SI,
+				PCI_DEVICE_ID_SI_651, NULL))
+			&& (NULL == pci_find_device(PCI_VENDOR_ID_SI,
+				PCI_DEVICE_ID_SI_655, NULL))
+			&& (NULL == pci_find_device(PCI_VENDOR_ID_SI,
+				PCI_DEVICE_ID_SI_735, NULL))
+			&& (NULL == pci_find_device(PCI_VENDOR_ID_SI,
+				PCI_DEVICE_ID_SI_745, NULL))
+			&& (NULL == pci_find_device(PCI_VENDOR_ID_SI,
+				PCI_DEVICE_ID_SI_746, NULL))) {
+			printk(KERN_ERR DRV_NAME ": Can't find suitable host bridge!\n");
+			return -ENODEV;
+		}
+	} else {
+		printk(KERN_ERR DRV_NAME ": Can't find suitable south bridge!\n");
+		return -ENODEV;
+	}
+
+	/* if we get this far, we think the smbus device is present */
+
+	if ((ret = sis645_enable_smbus(bridge_dev)))
+		return ret;
+
+	if ((ret = sis645_build_dev(&smbus_dev, bridge_dev)))
+		return ret;
+
+	if ((ret = pci_enable_device(smbus_dev))) {
+		printk(KERN_ERR DRV_NAME ": Can't pci_enable SMBus device!"
+			" (0x%08x)\n", ret);
+		return ret;
+	}
+
+	pci_insert_device(smbus_dev, smbus_dev->bus);
+
+	return 0;
+}
+#endif /* CONFIG_HOTPLUG */
+
+/* Execute a SMBus transaction.
+   int size is from SIS645_QUICK to SIS645_BLOCK_DATA
+ */
+static int sis645_transaction(int size)
+{
+	int temp;
+	int result = 0;
+	int timeout = 0;
+
+	/* Make sure the SMBus host is ready to start transmitting */
+	if (((temp = sis645_read(SMB_CNT)) & 0x03) != 0x00) {
+#ifdef DEBUG
+		printk(KERN_DEBUG DRV_NAME ": SMBus busy (0x%02x). Resetting...\n",
+				temp);
+#endif
+
+		/* kill the transaction */
+		sis645_write(SMB_HOST_CNT, 0x20);
+
+		/* check it again */
+		if (((temp = sis645_read(SMB_CNT)) & 0x03) != 0x00) {
+#ifdef DEBUG
+			printk(KERN_DEBUG DRV_NAME ": Failed! (0x%02x)\n", temp);
+#endif
+			return -1;
+		} else {
+#ifdef DEBUG
+			printk(KERN_DEBUG DRV_NAME ": Successful!\n");
+#endif
+		}
+	}
+
+	/* Turn off timeout interrupts, set fast host clock */
+	sis645_write(SMB_CNT, 0x20);
+
+	/* clear all (sticky) status flags */
+	temp = sis645_read(SMB_STS);
+	sis645_write(SMB_STS, temp & 0x1e);
+
+	/* start the transaction by setting bit 4 and size bits */
+	sis645_write(SMB_HOST_CNT, 0x10 | (size & 0x07));
+
+	/* We will always wait for a fraction of a second! */
+	do {
+		i2c_delay(1);
+		temp = sis645_read(SMB_STS);
+	} while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT));
+
+	/* If the SMBus is still busy, we give up */
+	if (timeout >= MAX_TIMEOUT) {
+		printk(KERN_DEBUG DRV_NAME ": SMBus Timeout! (0x%02x)\n",temp);
+		result = -1;
+	}
+
+	/* device error - probably missing ACK */
+	if (temp & 0x02) {
+#ifdef DEBUG
+		printk(KERN_DEBUG DRV_NAME ": Failed bus transaction!\n");
+#endif
+		result = -1;
+	}
+
+	/* bus collision */
+	if (temp & 0x04) {
+#ifdef DEBUG
+		printk(KERN_DEBUG DRV_NAME ": Bus collision!\n");
+#endif
+		result = -1;
+	}
+
+	/* Finish up by resetting the bus */
+	sis645_write(SMB_STS, temp);
+	if ((temp = sis645_read(SMB_STS))) {
+#ifdef DEBUG
+		printk(KERN_DEBUG DRV_NAME ": Failed reset at end of transaction!"
+				" (0x%02x)\n", temp);
+#endif
+	}
+
+	return result;
+}
+
+/* Return -1 on error. */
+static s32 sis645_access(struct i2c_adapter * adap, u16 addr,
+		   unsigned short flags, char read_write,
+		   u8 command, int size, union i2c_smbus_data * data)
+{
+
+	switch (size) {
+	case I2C_SMBUS_QUICK:
+		sis645_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
+		size = SIS645_QUICK;
+		break;
+
+	case I2C_SMBUS_BYTE:
+		sis645_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
+		if (read_write == I2C_SMBUS_WRITE)
+			sis645_write(SMB_CMD, command);
+		size = SIS645_BYTE;
+		break;
+
+	case I2C_SMBUS_BYTE_DATA:
+		sis645_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
+		sis645_write(SMB_CMD, command);
+		if (read_write == I2C_SMBUS_WRITE)
+			sis645_write(SMB_BYTE, data->byte);
+		size = SIS645_BYTE_DATA;
+		break;
+
+	case I2C_SMBUS_PROC_CALL:
+	case I2C_SMBUS_WORD_DATA:
+		sis645_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
+		sis645_write(SMB_CMD, command);
+		if (read_write == I2C_SMBUS_WRITE) {
+			sis645_write(SMB_BYTE, data->word & 0xff);
+			sis645_write(SMB_BYTE + 1, (data->word & 0xff00) >> 8);
+		}
+		size = (size == I2C_SMBUS_PROC_CALL ? SIS645_PROC_CALL : SIS645_WORD_DATA);
+		break;
+
+	case I2C_SMBUS_BLOCK_DATA:
+		/* TO DO: */
+		printk(KERN_INFO DRV_NAME ": SMBus block not implemented!\n");
+		return -1;
+		break;
+
+	default:
+		printk(KERN_INFO DRV_NAME ": Unsupported I2C size\n");
+		return -1;
+		break;
+	}
+
+	if (sis645_transaction(size))
+		return -1;
+
+	if ((size != SIS645_PROC_CALL) &&
+			((read_write == I2C_SMBUS_WRITE) || (size == SIS645_QUICK)))
+		return 0;
+
+	switch (size) {
+	case SIS645_BYTE:
+	case SIS645_BYTE_DATA:
+		data->byte = sis645_read(SMB_BYTE);
+		break;
+
+	case SIS645_WORD_DATA:
+	case SIS645_PROC_CALL:
+		data->word = sis645_read(SMB_BYTE) +
+				(sis645_read(SMB_BYTE + 1) << 8);
+		break;
+	}
+	return 0;
+}
+
+static u32 sis645_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	    I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	    I2C_FUNC_SMBUS_PROC_CALL;
+}
+
+static struct i2c_algorithm smbus_algorithm = {
+	.name		= "Non-I2C SMBus adapter",
+	.id		= I2C_ALGO_SMBUS,
+	.smbus_xfer	= sis645_access,
+	.functionality	= sis645_func,
+};
+
+static struct i2c_adapter sis645_adapter = {
+	.owner		= THIS_MODULE,
+	.name		= "unset",
+	.id		= I2C_ALGO_SMBUS | I2C_HW_SMBUS_SIS645,
+	.algo		= &smbus_algorithm,
+};
+
+static struct pci_device_id sis645_ids[] __devinitdata = {
+	{
+		.vendor = PCI_VENDOR_ID_SI,
+		.device = PCI_DEVICE_ID_SI_SMBUS,
+		.subvendor = PCI_ANY_ID,
+		.subdevice = PCI_ANY_ID,
+	},
+	{ 0, }
+};
+
+static int __devinit sis645_probe(struct pci_dev *dev,
+				const struct pci_device_id *id)
+{
+	u16 ww = 0;
+	int retval;
+
+	if (sis645_smbus_base) {
+		dev_err(dev, "Only one device supported.\n");
+		return -EBUSY;
+	}
+
+	pci_read_config_word(dev, PCI_CLASS_DEVICE, &ww);
+	if (PCI_CLASS_SERIAL_SMBUS != ww) {
+		dev_err(dev, "Unsupported device class 0x%04x!\n", ww);
+		return -ENODEV;
+	}
+
+	sis645_smbus_base = pci_resource_start(dev, SIS645_BAR);
+	if (!sis645_smbus_base) {
+		dev_err(dev, "SiS645 SMBus base address "
+			"not initialized!\n");
+		return -EINVAL;
+	}
+	dev_info(dev, "SiS645 SMBus base address: 0x%04x\n",
+			sis645_smbus_base);
+
+	/* Everything is happy, let's grab the memory and set things up. */
+	if (!request_region(sis645_smbus_base, SMB_IOSIZE, "sis645-smbus")) {
+		dev_err(dev, "SMBus registers 0x%04x-0x%04x "
+			"already in use!\n", sis645_smbus_base,
+			sis645_smbus_base + SMB_IOSIZE - 1);
+
+		sis645_smbus_base = 0;
+		return -EINVAL;
+	}
+
+	sprintf(sis645_adapter.name, "SiS645 SMBus adapter at 0x%04x",
+			sis645_smbus_base);
+
+	if ((retval = i2c_add_adapter(&sis645_adapter))) {
+		dev_err(dev, "Couldn't register adapter!\n");
+		release_region(sis645_smbus_base, SMB_IOSIZE);
+		sis645_smbus_base = 0;
+	}
+
+	return retval;
+}
+
+static void __devexit sis645_remove(struct pci_dev *dev)
+{
+	if (sis645_smbus_base) {
+		i2c_del_adapter(&sis645_adapter);
+		release_region(sis645_smbus_base, SMB_IOSIZE);
+		sis645_smbus_base = 0;
+	}
+}
+
+static struct pci_driver sis645_driver = {
+	.name		= "sis645 smbus",
+	.id_table	= sis645_ids,
+	.probe		= sis645_probe,
+	.remove		= __devexit_p(sis645_remove),
+};
+
+static int __init i2c_sis645_init(void)
+{
+	printk(KERN_INFO DRV_NAME ".o version %s (%s)\n", LM_VERSION, LM_DATE);
+
+	/* if the required device id is not present, try to HOTPLUG it first */
+	if (!pci_find_device(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_SMBUS, NULL)) {
+
+		printk(KERN_INFO DRV_NAME ": "
+			"Attempting to enable SiS645 SMBus device\n");
+
+#ifdef CONFIG_HOTPLUG
+		sis645_hotplug_smbus();
+#else
+		printk(KERN_INFO DRV_NAME ": "
+			"Requires kernel with CONFIG_HOTPLUG, sorry!\n");
+#endif
+	}
+
+	return pci_module_init(&sis645_driver);
+}
+
+static void __exit i2c_sis645_exit(void)
+{
+	pci_unregister_driver(&sis645_driver);
+}
+
+MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>");
+MODULE_DESCRIPTION("SiS645 SMBus driver");
+MODULE_LICENSE("GPL");
+
+/* Register initialization functions using helper macros */
+module_init(i2c_sis645_init);
+module_exit(i2c_sis645_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-tsunami.c linux-2.4.27-leo/drivers/i2c/i2c-tsunami.c
--- linux-2.4.27/drivers/i2c/i2c-tsunami.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-tsunami.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,155 @@
+/*
+    i2c-tsunami.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+    Copyright (c) 2001  Oleg Vdovikin <vdovikin@jscc.ru>
+    
+    Based on code written by Ralph Metzler <rjkm@thp.uni-koeln.de> and
+    Simon Vogl
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* This interfaces to the I2C bus of the Tsunami/Typhoon 21272 chipsets 
+   to gain access to the on-board I2C devices. 
+
+   For more information refer to Compaq's 
+	"Tsunami/Typhoon 21272 Chipset Hardware Reference Manual"
+	Order Number: DS-0025-TE
+*/ 
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/hwrpb.h>
+#include <asm/core_tsunami.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+MODULE_LICENSE("GPL");
+
+/* Memory Presence Detect Register (MPD-RW) bits 
+   with except of reserved RAZ bits */
+
+#define MPD_DR	0x8	/* Data receive - RO */
+#define MPD_CKR	0x4	/* Clock receive - RO */
+#define MPD_DS	0x2	/* Data send - Must be a 1 to receive - WO */
+#define MPD_CKS	0x1	/* Clock send - WO */
+
+static inline void writempd(unsigned long value)
+{
+	TSUNAMI_cchip->mpd.csr = value;
+	mb();
+}
+
+static inline unsigned long readmpd(void)
+{
+	return TSUNAMI_cchip->mpd.csr;
+}
+
+static void bit_tsunami_setscl(void *data, int val)
+{
+	/* read currently setted bits to modify them */
+	unsigned long bits = readmpd() >> 2; /* assume output == input */
+
+	if (val)
+		bits |= MPD_CKS;
+	else
+		bits &= ~MPD_CKS;
+
+	writempd(bits);
+}
+
+static void bit_tsunami_setsda(void *data, int val)
+{
+	/* read currently setted bits to modify them */
+	unsigned long bits = readmpd() >> 2; /* assume output == input */
+
+	if (val)
+		bits |= MPD_DS;
+	else
+		bits &= ~MPD_DS;
+
+	writempd(bits);
+}
+
+/* The MPD pins are open drain, so the pins always remain outputs.
+   We rely on the i2c-algo-bit routines to set the pins high before
+   reading the input from other chips. */
+
+static int bit_tsunami_getscl(void *data)
+{
+	return (0 != (readmpd() & MPD_CKR));
+}
+
+static int bit_tsunami_getsda(void *data)
+{
+	return (0 != (readmpd() & MPD_DR));
+}
+
+static struct i2c_algo_bit_data tsunami_i2c_bit_data = {
+	.setsda		= bit_tsunami_setsda,
+	.setscl		= bit_tsunami_setscl,
+	.getsda		= bit_tsunami_getsda,
+	.getscl		= bit_tsunami_getscl,
+	.udelay		= 10,
+	.mdelay		= 10,
+	.timeout	= HZ/2
+};
+
+static struct i2c_adapter tsunami_i2c_adapter = {
+	.owner		= THIS_MODULE,
+	.name		= "I2C Tsunami/Typhoon adapter",
+	.id		= I2C_HW_B_TSUNA,
+	.algo_data	= &tsunami_i2c_bit_data,
+};
+
+
+#if 0
+static struct pci_driver tsunami_driver = {
+	.name		= "tsunami smbus",
+	.id_table	= tsunami_ids,
+	.probe		= tsunami_probe,
+	.remove		= __devexit_p(tsunami_remove),
+};
+#endif
+
+static int __init i2c_tsunami_init(void)
+{
+	printk("i2c-tsunami.o version %s (%s)\n", LM_VERSION, LM_DATE);
+
+	if (hwrpb->sys_type != ST_DEC_TSUNAMI) {
+		printk("i2c-tsunami.o: not Tsunami based system (%ld), module not inserted.\n", hwrpb->sys_type);
+		return -ENXIO;
+	} else {
+		printk("i2c-tsunami.o: using Cchip MPD at 0x%lx.\n", (long) &TSUNAMI_cchip->mpd);
+	}
+	return i2c_bit_add_bus(&tsunami_i2c_adapter);
+}
+
+
+static void __exit i2c_tsunami_exit(void)
+{
+	i2c_bit_del_bus(&tsunami_i2c_adapter);
+}
+
+
+
+MODULE_AUTHOR("Oleg I. Vdovikin <vdovikin@jscc.ru>");
+MODULE_DESCRIPTION("Tsunami I2C/SMBus driver");
+
+module_init(i2c_tsunami_init);
+module_exit(i2c_tsunami_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-velleman.c linux-2.4.27-leo/drivers/i2c/i2c-velleman.c
--- linux-2.4.27/drivers/i2c/i2c-velleman.c	2004-02-20 14:11:41.000000000 +0000
+++ linux-2.4.27-leo/drivers/i2c/i2c-velleman.c	2004-09-20 21:34:38.000000000 +0100
@@ -18,18 +18,18 @@
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.		     */
 /* ------------------------------------------------------------------------- */
 
-/* $Id: i2c-velleman.c,v 1.19 2000/01/24 02:06:33 mds Exp $ */
+/* $Id: i2c-velleman.c,v 1.33 2003/12/24 17:49:44 khali Exp $ */
 
 #include <linux/kernel.h>
 #include <linux/ioport.h>
 #include <linux/module.h>
 #include <linux/init.h>
-#include <linux/string.h>  /* for 2.0 kernels to get NULL   */
-#include <asm/errno.h>     /* for 2.0 kernels to get ENODEV */
-#include <asm/io.h>
-
+#include <linux/errno.h>
+#include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/i2c-algo-bit.h>
+#include <asm/io.h>
+#include <asm/param.h> /* for HZ */
 
 /* ----- global defines -----------------------------------------------	*/
 #define DEB(x)		/* should be reasonable open, close &c. 	*/
@@ -90,75 +90,38 @@
 
 static int bit_velle_init(void)
 {
-	if (check_region(base,(base == 0x3bc)? 3 : 8) < 0 ) {
-		DEBE(printk("i2c-velleman.o: Port %#x already in use.\n",
-		     base));
+	if (!request_region(base, (base == 0x3bc) ? 3 : 8, 
+			"i2c (Vellemann adapter)"))
 		return -ENODEV;
-	} else {
-		request_region(base, (base == 0x3bc)? 3 : 8, 
-			"i2c (Vellemann adapter)");
-		bit_velle_setsda((void*)base,1);
-		bit_velle_setscl((void*)base,1);
-	}
-	return 0;
-}
 
-static void __exit bit_velle_exit(void)
-{	
-	release_region( base , (base == 0x3bc)? 3 : 8 );
-}
-
-
-static int bit_velle_reg(struct i2c_client *client)
-{
-	return 0;
-}
-
-static int bit_velle_unreg(struct i2c_client *client)
-{
+	bit_velle_setsda((void*)base,1);
+	bit_velle_setscl((void*)base,1);
 	return 0;
 }
 
-static void bit_velle_inc_use(struct i2c_adapter *adap)
-{
-#ifdef MODULE
-	MOD_INC_USE_COUNT;
-#endif
-}
-
-static void bit_velle_dec_use(struct i2c_adapter *adap)
-{
-#ifdef MODULE
-	MOD_DEC_USE_COUNT;
-#endif
-}
-
 /* ------------------------------------------------------------------------
  * Encapsulate the above functions in the correct operations structure.
  * This is only done when more than one hardware adapter is supported.
  */
 
 static struct i2c_algo_bit_data bit_velle_data = {
-	NULL,
-	bit_velle_setsda,
-	bit_velle_setscl,
-	bit_velle_getsda,
-	bit_velle_getscl,
-	10, 10, 100,		/*	waits, timeout */
+	.setsda		= bit_velle_setsda,
+	.setscl		= bit_velle_setscl,
+	.getsda		= bit_velle_getsda,
+	.getscl		= bit_velle_getscl,
+	.udelay		= 10,
+	.mdelay		= 10,
+	.timeout	= HZ
 };
 
 static struct i2c_adapter bit_velle_ops = {
-	"Velleman K8000",
-	I2C_HW_B_VELLE,
-	NULL,
-	&bit_velle_data,
-	bit_velle_inc_use,
-	bit_velle_dec_use,
-	bit_velle_reg,
-	bit_velle_unreg,
+	.owner		= THIS_MODULE,
+	.name		= "Velleman K8000",
+	.id		= I2C_HW_B_VELLE,
+	.algo_data	= &bit_velle_data,
 };
 
-int __init  i2c_bitvelle_init(void)
+static int __init i2c_bitvelle_init(void)
 {
 	printk(KERN_INFO "i2c-velleman.o: i2c Velleman K8000 adapter module version %s (%s)\n", I2C_VERSION, I2C_DATE);
 	if (base==0) {
@@ -184,24 +147,19 @@
 	return 0;
 }
 
+static void __exit i2c_bitvelle_exit(void)
+{	
+	i2c_bit_del_bus(&bit_velle_ops);
+	release_region(base, (base == 0x3bc) ? 3 : 8);
+}
+
 EXPORT_NO_SYMBOLS;
 
-#ifdef MODULE
 MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>");
 MODULE_DESCRIPTION("I2C-Bus adapter routines for Velleman K8000 adapter");
 MODULE_LICENSE("GPL");
 
 MODULE_PARM(base, "i");
 
-int init_module(void) 
-{
-	return i2c_bitvelle_init();
-}
-
-void cleanup_module(void) 
-{
-	i2c_bit_del_bus(&bit_velle_ops);
-	bit_velle_exit();
-}
-
-#endif
+module_init(i2c_bitvelle_init);
+module_exit(i2c_bitvelle_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-via.c linux-2.4.27-leo/drivers/i2c/i2c-via.c
--- linux-2.4.27/drivers/i2c/i2c-via.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-via.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,207 @@
+/*
+    i2c-via.c - Part of lm_sensors,  Linux kernel modules
+                for hardware monitoring
+
+    i2c Support for Via Technologies 82C586B South Bridge
+
+    Copyright (c) 1998, 1999 Kyösti Mälkki <kmalkki@cc.hut.fi>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/param.h>	/* for HZ */
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+/* Power management registers */
+
+#define PM_CFG_REVID    0x08	/* silicon revision code */
+#define PM_CFG_IOBASE0  0x20
+#define PM_CFG_IOBASE1  0x48
+
+#define I2C_DIR		(pm_io_base+0x40)
+#define I2C_OUT		(pm_io_base+0x42)
+#define I2C_IN		(pm_io_base+0x44)
+#define I2C_SCL		0x02	/* clock bit in DIR/OUT/IN register */
+#define I2C_SDA		0x04
+
+/* io-region reservation */
+#define IOSPACE		0x06
+#define IOTEXT		"via-i2c"
+
+static u16 pm_io_base = 0;
+
+/*
+   It does not appear from the datasheet that the GPIO pins are
+   open drain. So a we set a low value by setting the direction to
+   output and a high value by setting the direction to input and
+   relying on the required I2C pullup. The data value is initialized
+   to 0 in via_init() and never changed.
+*/
+
+static void bit_via_setscl(void *data, int state)
+{
+	outb(state ? inb(I2C_DIR) & ~I2C_SCL : inb(I2C_DIR) | I2C_SCL,
+	     I2C_DIR);
+}
+
+static void bit_via_setsda(void *data, int state)
+{
+	outb(state ? inb(I2C_DIR) & ~I2C_SDA : inb(I2C_DIR) | I2C_SDA,
+	     I2C_DIR);
+}
+
+static int bit_via_getscl(void *data)
+{
+	return (0 != (inb(I2C_IN) & I2C_SCL));
+}
+
+static int bit_via_getsda(void *data)
+{
+	return (0 != (inb(I2C_IN) & I2C_SDA));
+}
+
+
+static struct i2c_algo_bit_data bit_data = {
+	.setsda		= bit_via_setsda,
+	.setscl		= bit_via_setscl,
+	.getsda		= bit_via_getsda,
+	.getscl		= bit_via_getscl,
+	.udelay		= 5,
+	.mdelay		= 5,
+	.timeout	= HZ
+};
+
+static struct i2c_adapter vt586b_adapter = {
+	.owner		= THIS_MODULE,
+	.name		= "VIA i2c",
+	.id		= I2C_HW_B_VIA,
+	.algo_data	= &bit_data,
+};
+
+
+static struct pci_device_id vt586b_ids[] __devinitdata = {
+	{ PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+	{ 0, }
+};
+
+static int __devinit vt586b_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	u16 base;
+	u8 rev;
+	int res;
+
+	if (pm_io_base) {
+		printk(KERN_ERR "i2c-via.o: Will only support one host\n");
+		return -EBUSY;
+	}
+
+	pci_read_config_byte(dev, PM_CFG_REVID, &rev);
+
+	switch (rev) {
+	case 0x00:
+		base = PM_CFG_IOBASE0;
+		break;
+	case 0x01:
+	case 0x10:
+		base = PM_CFG_IOBASE1;
+		break;
+
+	default:
+		base = PM_CFG_IOBASE1;
+		/* later revision */
+	}
+
+	pci_read_config_word(dev, base, &pm_io_base);
+	pm_io_base &= (0xff << 8);
+
+	if (! request_region(I2C_DIR, IOSPACE, IOTEXT)) {
+	    printk("i2c-via.o: IO 0x%x-0x%x already in use\n",
+		   I2C_DIR, I2C_DIR + IOSPACE);
+	    return -EBUSY;
+	}
+	outb(inb(I2C_DIR) & ~(I2C_SDA | I2C_SCL), I2C_DIR);
+	outb(inb(I2C_OUT) & ~(I2C_SDA | I2C_SCL), I2C_OUT);
+	
+	res = i2c_bit_add_bus(&vt586b_adapter);
+	if ( res < 0 ) {
+		release_region(I2C_DIR, IOSPACE);
+		pm_io_base = 0;
+		return res;
+	}
+	return 0;
+}
+
+static void __devexit vt586b_remove(struct pci_dev *dev)
+{
+	i2c_bit_del_bus(&vt586b_adapter);
+	release_region(I2C_DIR, IOSPACE);
+	pm_io_base = 0;
+}
+
+
+/* Don't register driver to avoid driver conflicts */
+/*
+static struct pci_driver vt586b_driver = {
+	.name		= "vt586b smbus",
+	.id_table	= vt586b_ids,
+	.probe		= vt586b_probe,
+	.remove		= __devexit_p(vt586b_remove),
+};
+*/
+
+static int __init i2c_vt586b_init(void)
+{
+	struct pci_dev *dev;
+	const struct pci_device_id *id;
+
+	printk("i2c-via.o version %s (%s)\n", LM_VERSION, LM_DATE);
+/*
+	return pci_module_init(&vt586b_driver);
+*/
+	pci_for_each_dev(dev) {
+		id = pci_match_device(vt586b_ids, dev);
+		if(id)
+			if(vt586b_probe(dev, id) >= 0)
+				return 0;
+	}
+	return -ENODEV;
+}
+
+
+static void __exit i2c_vt586b_exit(void)
+{
+/*
+	pci_unregister_driver(&vt586b_driver);
+*/
+	vt586b_remove(NULL);
+}
+
+
+MODULE_AUTHOR("Kyösti Mälkki <kmalkki@cc.hut.fi>");
+MODULE_DESCRIPTION("i2c for Via vt82c586b southbridge");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_vt586b_init);
+module_exit(i2c_vt586b_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-viapro.c linux-2.4.27-leo/drivers/i2c/i2c-viapro.c
--- linux-2.4.27/drivers/i2c/i2c-viapro.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-viapro.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,514 @@
+/*
+    i2c-viapro.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+    Copyright (c) 1998 - 2002  Frodo Looijaard <frodol@dds.nl>, 
+    Philip Edelbrock <phil@netroedge.com>, Kyösti Mälkki <kmalkki@cc.hut.fi>,
+    Mark D. Studebaker <mdsxyz123@yahoo.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+   Supports Via devices:
+	82C596A/B (0x3050)
+	82C596B (0x3051)
+	82C686A/B
+	8231
+	8233
+	8233A (0x3147 and 0x3177)
+	8235
+	8237
+   Note: we assume there can only be one device, with one SMBus interface.
+*/
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+#include <linux/sensors_compat.h>
+
+#define SMBBA1	   	 0x90
+#define SMBBA2     	 0x80
+#define SMBBA3     	 0xD0
+
+/* SMBus address offsets */
+static unsigned short vt596_smba;
+#define SMBHSTSTS	(vt596_smba + 0)
+#define SMBHSLVSTS	(vt596_smba + 1)
+#define SMBHSTCNT	(vt596_smba + 2)
+#define SMBHSTCMD	(vt596_smba + 3)
+#define SMBHSTADD	(vt596_smba + 4)
+#define SMBHSTDAT0	(vt596_smba + 5)
+#define SMBHSTDAT1	(vt596_smba + 6)
+#define SMBBLKDAT	(vt596_smba + 7)
+#define SMBSLVCNT	(vt596_smba + 8)
+#define SMBSHDWCMD	(vt596_smba + 9)
+#define SMBSLVEVT	(vt596_smba + 0xA)
+#define SMBSLVDAT	(vt596_smba + 0xC)
+
+/* PCI Address Constants */
+
+/* SMBus data in configuration space can be found in two places,
+   We try to select the better one*/
+
+static unsigned short smb_cf_hstcfg = 0xD2;
+
+#define SMBHSTCFG   (smb_cf_hstcfg)
+#define SMBSLVC     (smb_cf_hstcfg + 1)
+#define SMBSHDW1    (smb_cf_hstcfg + 2)
+#define SMBSHDW2    (smb_cf_hstcfg + 3)
+#define SMBREV      (smb_cf_hstcfg + 4)
+
+/* Other settings */
+#define MAX_TIMEOUT	500
+#define ENABLE_INT9	0
+
+/* VT82C596 constants */
+#define VT596_QUICK      0x00
+#define VT596_BYTE       0x04
+#define VT596_BYTE_DATA  0x08
+#define VT596_WORD_DATA  0x0C
+#define VT596_BLOCK_DATA 0x14
+
+
+/* If force is set to anything different from 0, we forcibly enable the
+   VT596. DANGEROUS! */
+static int force;
+MODULE_PARM(force, "i");
+MODULE_PARM_DESC(force, "Forcibly enable the SMBus. DANGEROUS!");
+
+/* If force_addr is set to anything different from 0, we forcibly enable
+   the VT596 at the given address. VERY DANGEROUS! */
+static int force_addr;
+MODULE_PARM(force_addr, "i");
+MODULE_PARM_DESC(force_addr,
+		 "Forcibly enable the SMBus at the given address. "
+		 "EXTREMELY DANGEROUS!");
+
+
+static struct i2c_adapter vt596_adapter;
+
+/* Another internally used function */
+static int vt596_transaction(void)
+{
+	int temp;
+	int result = 0;
+	int timeout = 0;
+
+	dev_dbg(&vt596_adapter, "Transaction (pre): CNT=%02x, CMD=%02x, "
+		"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), 
+		inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), 
+		inb_p(SMBHSTDAT1));
+
+	/* Make sure the SMBus host is ready to start transmitting */
+	if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
+		dev_dbg(&vt596_adapter, "SMBus busy (0x%02x). "
+				"Resetting...\n", temp);
+		
+		outb_p(temp, SMBHSTSTS);
+		if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
+			dev_dbg(&vt596_adapter, "Failed! (0x%02x)\n", temp);
+			
+			return -1;
+		} else {
+			dev_dbg(&vt596_adapter, "Successfull!\n");
+		}
+	}
+
+	/* start the transaction by setting bit 6 */
+	outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT);
+
+	/* We will always wait for a fraction of a second! 
+	   I don't know if VIA needs this, Intel did  */
+	do {
+		i2c_delay(1);
+		temp = inb_p(SMBHSTSTS);
+	} while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT));
+
+	/* If the SMBus is still busy, we give up */
+	if (timeout >= MAX_TIMEOUT) {
+		result = -1;
+		dev_dbg(&vt596_adapter, "SMBus Timeout!\n");
+	}
+
+	if (temp & 0x10) {
+		result = -1;
+		dev_dbg(&vt596_adapter, "Error: Failed bus transaction\n");
+	}
+
+	if (temp & 0x08) {
+		result = -1;
+		dev_info(&vt596_adapter, "Bus collision! SMBus may be "
+			"locked until next hard\nreset. (sorry!)\n");
+		/* Clock stops and slave is stuck in mid-transmission */
+	}
+
+	if (temp & 0x04) {
+		result = -1;
+		dev_dbg(&vt596_adapter, "Error: no response!\n");
+	}
+
+	if (inb_p(SMBHSTSTS) != 0x00)
+		outb_p(inb(SMBHSTSTS), SMBHSTSTS);
+
+	if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
+		dev_dbg(&vt596_adapter, "Failed reset at end of "
+			"transaction (%02x)\n", temp);
+	}
+	dev_dbg(&vt596_adapter, "Transaction (post): CNT=%02x, CMD=%02x, "
+		"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
+		inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), 
+		inb_p(SMBHSTDAT1));
+	
+	return result;
+}
+
+/* Return -1 on error. */
+static s32 vt596_access(struct i2c_adapter *adap, u16 addr,
+		unsigned short flags,  char read_write, u8 command,
+		int size,  union i2c_smbus_data *data)
+{
+	int i, len;
+
+	switch (size) {
+	case I2C_SMBUS_QUICK:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		size = VT596_QUICK;
+		break;
+	case I2C_SMBUS_BYTE:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(command, SMBHSTCMD);
+		size = VT596_BYTE;
+		break;
+	case I2C_SMBUS_BYTE_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(data->byte, SMBHSTDAT0);
+		size = VT596_BYTE_DATA;
+		break;
+	case I2C_SMBUS_WORD_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE) {
+			outb_p(data->word & 0xff, SMBHSTDAT0);
+			outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
+		}
+		size = VT596_WORD_DATA;
+		break;
+	case I2C_SMBUS_BLOCK_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE) {
+			len = data->block[0];
+			if (len < 0)
+				len = 0;
+			if (len > 32)
+				len = 32;
+			outb_p(len, SMBHSTDAT0);
+			i = inb_p(SMBHSTCNT);	/* Reset SMBBLKDAT */
+			for (i = 1; i <= len; i++)
+				outb_p(data->block[i], SMBBLKDAT);
+		}
+		size = VT596_BLOCK_DATA;
+		break;
+	default:
+		dev_warn(&vt596_adapter, "Unsupported transaction %d\n", size);
+		return -1;
+	}
+
+	outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT);
+
+	if (vt596_transaction()) /* Error in transaction */
+		return -1;
+
+	if ((read_write == I2C_SMBUS_WRITE) || (size == VT596_QUICK))
+		return 0;
+
+	switch (size) {
+	case VT596_BYTE:
+		/* Where is the result put? I assume here it is in
+		 * SMBHSTDAT0 but it might just as well be in the
+		 * SMBHSTCMD. No clue in the docs 
+		 */
+		data->byte = inb_p(SMBHSTDAT0);
+		break;
+	case VT596_BYTE_DATA:
+		data->byte = inb_p(SMBHSTDAT0);
+		break;
+	case VT596_WORD_DATA:
+		data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
+		break;
+	case VT596_BLOCK_DATA:
+		data->block[0] = inb_p(SMBHSTDAT0);
+		if (data->block[0] > 32)
+			data->block[0] = 32;
+		i = inb_p(SMBHSTCNT);	/* Reset SMBBLKDAT */
+		for (i = 1; i <= data->block[0]; i++)
+			data->block[i] = inb_p(SMBBLKDAT);
+		break;
+	}
+	return 0;
+}
+
+static u32 vt596_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	    I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	    I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static struct i2c_algorithm smbus_algorithm = {
+	.name		= "Non-I2C SMBus adapter",
+	.id		= I2C_ALGO_SMBUS,
+	.smbus_xfer	= vt596_access,
+	.functionality	= vt596_func,
+};
+
+static struct i2c_adapter vt596_adapter = {
+	.owner		= THIS_MODULE,
+	.id		= I2C_ALGO_SMBUS | I2C_HW_SMBUS_VIA2,
+	.algo		= &smbus_algorithm,
+	.name	= "unset",
+};
+
+static int __devinit vt596_probe(struct pci_dev *pdev,
+				 const struct pci_device_id *id)
+{
+	unsigned char temp;
+	int error = -ENODEV;
+	
+	/* Determine the address of the SMBus areas */
+	if (force_addr) {
+		vt596_smba = force_addr & 0xfff0;
+		force = 0;
+		goto found;
+	}
+
+	if ((pci_read_config_word(pdev, id->driver_data, &vt596_smba)) ||
+	    !(vt596_smba & 0x1)) {
+		/* try 2nd address and config reg. for 596 */
+		if (id->device == PCI_DEVICE_ID_VIA_82C596_3 &&
+		    !pci_read_config_word(pdev, SMBBA2, &vt596_smba) &&
+		    (vt596_smba & 0x1)) {
+			smb_cf_hstcfg = 0x84;
+		} else {
+			/* no matches at all */
+			dev_err(pdev, "Cannot configure "
+				"SMBus I/O Base address\n");
+			return -ENODEV;
+		}
+	}
+
+	vt596_smba &= 0xfff0;
+	if (vt596_smba == 0) {
+		dev_err(pdev, "SMBus base address "
+			"uninitialized - upgrade BIOS or use "
+			"force_addr=0xaddr\n");
+		return -ENODEV;
+	}
+
+ found:
+	if (!request_region(vt596_smba, 8, "viapro-smbus")) {
+		dev_err(pdev, "SMBus region 0x%x already in use!\n",
+		        vt596_smba);
+		return -ENODEV;
+	}
+
+	pci_read_config_byte(pdev, SMBHSTCFG, &temp);
+	/* If force_addr is set, we program the new address here. Just to make
+	   sure, we disable the VT596 first. */
+	if (force_addr) {
+		pci_write_config_byte(pdev, SMBHSTCFG, temp & 0xfe);
+		pci_write_config_word(pdev, id->driver_data, vt596_smba);
+		pci_write_config_byte(pdev, SMBHSTCFG, temp | 0x01);
+		dev_warn(pdev, "WARNING: SMBus interface set to new "
+		     "address 0x%04x!\n", vt596_smba);
+	} else if ((temp & 1) == 0) {
+		if (force) {
+			/* NOTE: This assumes I/O space and other allocations 
+			 * WERE done by the Bios!  Don't complain if your 
+			 * hardware does weird things after enabling this. 
+			 * :') Check for Bios updates before resorting to 
+			 * this.
+			 */
+			pci_write_config_byte(pdev, SMBHSTCFG, temp | 1);
+			dev_info(pdev, "Enabling SMBus device\n");
+		} else {
+			dev_err(pdev, "SMBUS: Error: Host SMBus "
+				"controller not enabled! - upgrade BIOS or "
+				"use force=1\n");
+			goto release_region;
+		}
+	}
+
+	if ((temp & 0x0E) == 8)
+		dev_dbg(pdev, "using Interrupt 9 for SMBus.\n");
+	else if ((temp & 0x0E) == 0)
+		dev_dbg(pdev, "using Interrupt SMI# for SMBus.\n");
+	else
+		dev_dbg(pdev, "Illegal Interrupt configuration "
+			"(or code out of date)!\n");
+
+	pci_read_config_byte(pdev, SMBREV, &temp);
+	dev_dbg(pdev, "SMBREV = 0x%X\n", temp);
+	dev_dbg(pdev, "VT596_smba = 0x%X\n", vt596_smba);
+
+	snprintf(vt596_adapter.name, 32,
+			"SMBus Via Pro adapter at %04x", vt596_smba);
+	
+	return i2c_add_adapter(&vt596_adapter);
+
+ release_region:
+	release_region(vt596_smba, 8);
+	return error;
+}
+
+static void __devexit vt596_remove(struct pci_dev *pdev)
+{
+	i2c_del_adapter(&vt596_adapter);
+	release_region(vt596_smba, 8);
+}
+
+/* 8233A is undefined before kernel 2.4.19 */
+#ifndef PCI_DEVICE_ID_VIA_8233A
+#define PCI_DEVICE_ID_VIA_8233A	0x3147
+#endif
+/* 8235 is undefined before kernel 2.4.20 */
+#ifndef PCI_DEVICE_ID_VIA_8235
+#define PCI_DEVICE_ID_VIA_8235	0x3177
+#endif
+/* 8237 is undefined before kernel 2.4.21 */
+#ifndef PCI_DEVICE_ID_VIA_8237
+#define PCI_DEVICE_ID_VIA_8237	0x3227
+#endif
+static struct pci_device_id vt596_ids[] __devinitdata = {
+	{
+		.vendor		= PCI_VENDOR_ID_VIA,
+		.device 	= PCI_DEVICE_ID_VIA_82C596_3,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SMBBA1,
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_VIA,
+		.device		= PCI_DEVICE_ID_VIA_82C596B_3,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SMBBA1,
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_VIA,
+		.device 	= PCI_DEVICE_ID_VIA_82C686_4,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SMBBA1,
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_VIA,
+		.device 	= PCI_DEVICE_ID_VIA_8233_0,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SMBBA3
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_VIA,
+		.device 	= PCI_DEVICE_ID_VIA_8233A,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SMBBA3,
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_VIA,
+		.device 	= PCI_DEVICE_ID_VIA_8235,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SMBBA3
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_VIA,
+		.device 	= PCI_DEVICE_ID_VIA_8237,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SMBBA3
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_VIA,
+		.device 	= PCI_DEVICE_ID_VIA_8231_4,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SMBBA1,
+	},
+	{ 0, }
+};
+
+/* Don't register driver to avoid driver conflicts */
+/*
+static struct pci_driver vt596_driver = {
+	.name		= "vt596 smbus",
+	.id_table	= vt596_ids,
+	.probe		= vt596_probe,
+	.remove		= __devexit_p(vt596_remove),
+};
+*/
+
+static int __init i2c_vt596_init(void)
+{
+	struct pci_dev *dev;
+	const struct pci_device_id *id;
+
+	printk("i2c-viapro.o version %s (%s)\n", LM_VERSION, LM_DATE);
+/*
+	return pci_module_init(&vt596_driver);
+*/
+	pci_for_each_dev(dev) {
+		id = pci_match_device(vt596_ids, dev);
+		if(id)
+			if(vt596_probe(dev, id) >= 0)
+				return 0;
+	}
+	return -ENODEV;
+}
+
+
+static void __exit i2c_vt596_exit(void)
+{
+/*
+	pci_unregister_driver(&vt596_driver);
+*/
+	vt596_remove(NULL);
+}
+
+MODULE_AUTHOR(
+    "Frodo Looijaard <frodol@dds.nl> and "
+    "Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("vt82c596 SMBus driver");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_vt596_init);
+module_exit(i2c_vt596_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/i2c-voodoo3.c linux-2.4.27-leo/drivers/i2c/i2c-voodoo3.c
--- linux-2.4.27/drivers/i2c/i2c-voodoo3.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/i2c/i2c-voodoo3.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,281 @@
+/*
+    voodoo3.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>,
+    Philip Edelbrock <phil@netroedge.com>,
+    Ralph Metzler <rjkm@thp.uni-koeln.de>, and
+    Mark D. Studebaker <mdsxyz123@yahoo.com>
+    
+    Based on code written by Ralph Metzler <rjkm@thp.uni-koeln.de> and
+    Simon Vogl
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* This interfaces to the I2C bus of the Voodoo3 to gain access to
+    the BT869 and possibly other I2C devices. */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/param.h>	/* for HZ */
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+MODULE_LICENSE("GPL");
+
+/* the only registers we use */
+#define REG	0x78
+#define REG2 	0x70
+
+/* bit locations in the register */
+#define DDC_ENAB	0x00040000
+#define DDC_SCL_OUT	0x00080000
+#define DDC_SDA_OUT	0x00100000
+#define DDC_SCL_IN	0x00200000
+#define DDC_SDA_IN	0x00400000
+#define I2C_ENAB	0x00800000
+#define I2C_SCL_OUT	0x01000000
+#define I2C_SDA_OUT	0x02000000
+#define I2C_SCL_IN	0x04000000
+#define I2C_SDA_IN	0x08000000
+
+/* initialization states */
+#define INIT2	0x2
+#define INIT3	0x4
+
+/* delays */
+#define CYCLE_DELAY	10
+#define TIMEOUT		(HZ / 2)
+
+
+static void config_v3(struct pci_dev *dev);
+
+static unsigned long ioaddr;
+
+/* The voo GPIO registers don't have individual masks for each bit
+   so we always have to read before writing. */
+
+static void bit_vooi2c_setscl(void *data, int val)
+{
+	unsigned int r;
+	r = readl(ioaddr + REG);
+	if(val)
+		r |= I2C_SCL_OUT;
+	else
+		r &= ~I2C_SCL_OUT;
+	writel(r, ioaddr + REG);
+	readl(ioaddr + REG);	/* flush posted write */
+}
+
+static void bit_vooi2c_setsda(void *data, int val)
+{
+	unsigned int r;
+	r = readl(ioaddr + REG);
+	if(val)
+		r |= I2C_SDA_OUT;
+	else
+		r &= ~I2C_SDA_OUT;
+	writel(r, ioaddr + REG);
+	readl(ioaddr + REG);	/* flush posted write */
+}
+
+/* The GPIO pins are open drain, so the pins always remain outputs.
+   We rely on the i2c-algo-bit routines to set the pins high before
+   reading the input from other chips. */
+
+static int bit_vooi2c_getscl(void *data)
+{
+	return (0 != (readl(ioaddr + REG) & I2C_SCL_IN));
+}
+
+static int bit_vooi2c_getsda(void *data)
+{
+	return (0 != (readl(ioaddr + REG) & I2C_SDA_IN));
+}
+
+static void bit_vooddc_setscl(void *data, int val)
+{
+	unsigned int r;
+	r = readl(ioaddr + REG);
+	if(val)
+		r |= DDC_SCL_OUT;
+	else
+		r &= ~DDC_SCL_OUT;
+	writel(r, ioaddr + REG);
+	readl(ioaddr + REG);	/* flush posted write */
+}
+
+static void bit_vooddc_setsda(void *data, int val)
+{
+	unsigned int r;
+	r = readl(ioaddr + REG);
+	if(val)
+		r |= DDC_SDA_OUT;
+	else
+		r &= ~DDC_SDA_OUT;
+	writel(r, ioaddr + REG);
+	readl(ioaddr + REG);	/* flush posted write */
+}
+
+static int bit_vooddc_getscl(void *data)
+{
+	return (0 != (readl(ioaddr + REG) & DDC_SCL_IN));
+}
+
+static int bit_vooddc_getsda(void *data)
+{
+	return (0 != (readl(ioaddr + REG) & DDC_SDA_IN));
+}
+
+
+/* Configures the chip */
+
+void config_v3(struct pci_dev *dev)
+{
+	unsigned int cadr;
+
+	/* map Voodoo3 memory */
+	cadr = dev->resource[0].start;
+	cadr &= PCI_BASE_ADDRESS_MEM_MASK;
+	ioaddr = (unsigned long)ioremap_nocache(cadr, 0x1000);
+	if(ioaddr) {
+		writel(0x8160, ioaddr + REG2);
+		writel(0xcffc0020, ioaddr + REG);
+		printk("i2c-voodoo3: Using Banshee/Voodoo3 at 0x%lx\n", ioaddr);
+	}
+}
+
+static struct i2c_algo_bit_data voo_i2c_bit_data = {
+	.setsda		= bit_vooi2c_setsda,
+	.setscl		= bit_vooi2c_setscl,
+	.getsda		= bit_vooi2c_getsda,
+	.getscl		= bit_vooi2c_getscl,
+	.udelay		= CYCLE_DELAY,
+	.mdelay		= CYCLE_DELAY,
+	.timeout	= TIMEOUT
+};
+
+static struct i2c_adapter voodoo3_i2c_adapter = {
+	.owner		= THIS_MODULE,
+	.name		= "I2C Voodoo3/Banshee adapter",
+	.id		= I2C_HW_B_VOO,
+	.algo_data		= &voo_i2c_bit_data,
+};
+
+static struct i2c_algo_bit_data voo_ddc_bit_data = {
+	.setsda		= bit_vooddc_setsda,
+	.setscl		= bit_vooddc_setscl,
+	.getsda		= bit_vooddc_getsda,
+	.getscl		= bit_vooddc_getscl,
+	.udelay		= CYCLE_DELAY,
+	.mdelay		= CYCLE_DELAY,
+	.timeout	= TIMEOUT
+};
+
+static struct i2c_adapter voodoo3_ddc_adapter = {
+	.owner		= THIS_MODULE,
+	.name		= "DDC Voodoo3/Banshee adapter",
+	.id		= I2C_HW_B_VOO,
+	.algo_data	= &voo_ddc_bit_data,
+};
+
+
+static struct pci_device_id voodoo3_ids[] __devinitdata = {
+	{
+		.vendor =	PCI_VENDOR_ID_3DFX,
+		.device =	PCI_DEVICE_ID_3DFX_VOODOO3,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_3DFX,
+		.device =	PCI_DEVICE_ID_3DFX_BANSHEE,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+	},
+	{ 0, }
+};
+
+static int __devinit voodoo3_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	int retval;
+
+	printk("voodoo3: in probe\n");
+	config_v3(dev);
+	retval = i2c_bit_add_bus(&voodoo3_i2c_adapter);
+	if(retval)
+		return retval;
+	retval = i2c_bit_add_bus(&voodoo3_ddc_adapter);
+	if(retval)
+		i2c_bit_del_bus(&voodoo3_i2c_adapter);
+	return retval;
+}
+
+static void __devexit voodoo3_remove(struct pci_dev *dev)
+{
+	i2c_bit_del_bus(&voodoo3_i2c_adapter);
+ 	i2c_bit_del_bus(&voodoo3_ddc_adapter);
+}
+
+
+/* Don't register driver to avoid driver conflicts */
+/*
+static struct pci_driver voodoo3_driver = {
+	.name		= "voodoo3 smbus",
+	.id_table	= voodoo3_ids,
+	.probe		= voodoo3_probe,
+	.remove		= __devexit_p(voodoo3_remove),
+};
+*/
+
+static int __init i2c_voodoo3_init(void)
+{
+	struct pci_dev *dev;
+	const struct pci_device_id *id;
+
+	printk("i2c-voodoo3.o version %s (%s)\n", LM_VERSION, LM_DATE);
+/*
+	return pci_module_init(&voodoo3_driver);
+*/
+	pci_for_each_dev(dev) {
+		id = pci_match_device(voodoo3_ids, dev);
+		if(id)
+			if(voodoo3_probe(dev, id) >= 0)
+				return 0;
+	}
+	return -ENODEV;
+}
+
+
+static void __exit i2c_voodoo3_exit(void)
+{
+/*
+	pci_unregister_driver(&voodoo3_driver);
+*/
+	voodoo3_remove(NULL);
+	iounmap((void *)ioaddr);
+}
+
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, Ralph Metzler <rjkm@thp.uni-koeln.de>, and Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("Voodoo3 I2C/SMBus driver");
+
+module_init(i2c_voodoo3_init);
+module_exit(i2c_voodoo3_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/i2c/Makefile linux-2.4.27-leo/drivers/i2c/Makefile
--- linux-2.4.27/drivers/i2c/Makefile	2004-02-20 14:11:41.000000000 +0000
+++ linux-2.4.27-leo/drivers/i2c/Makefile	2004-09-20 21:34:47.000000000 +0100
@@ -27,6 +27,24 @@
 obj-$(CONFIG_I2C_ALGO_SGI)	+= i2c-algo-sgi.o
 
 # This is needed for automatic patch generation: sensors code starts here
+obj-$(CONFIG_I2C_ALI1535)		+= i2c-ali1535.o
+obj-$(CONFIG_I2C_ALI15X3)		+= i2c-ali15x3.o
+obj-$(CONFIG_I2C_AMD756)		+= i2c-amd756.o
+obj-$(CONFIG_I2C_AMD8111)		+= i2c-amd8111.o
+obj-$(CONFIG_I2C_HYDRA)			+= i2c-hydra.o
+obj-$(CONFIG_I2C_I801)			+= i2c-i801.o
+obj-$(CONFIG_I2C_I810)			+= i2c-i810.o
+obj-$(CONFIG_I2C_ISA)			+= i2c-isa.o
+obj-$(CONFIG_I2C_NFORCE2)		+= i2c-nforce2.o
+obj-$(CONFIG_I2C_PIIX4)			+= i2c-piix4.o
+obj-$(CONFIG_I2C_SIS5595)		+= i2c-sis5595.o
+obj-$(CONFIG_I2C_SIS630)		+= i2c-sis630.o
+obj-$(CONFIG_I2C_SIS645)		+= i2c-sis645.o
+obj-$(CONFIG_I2C_SAVAGE4)		+= i2c-savage4.o
+obj-$(CONFIG_I2C_TSUNAMI)		+= i2c-tsunami.o
+obj-$(CONFIG_I2C_VIA)			+= i2c-via.o
+obj-$(CONFIG_I2C_VIAPRO)		+= i2c-viapro.o
+obj-$(CONFIG_I2C_VOODOO3)		+= i2c-voodoo3.o
 # This is needed for automatic patch generation: sensors code ends here
 
 include $(TOPDIR)/Rules.make
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/ieee1394/csr.c linux-2.4.27-leo/drivers/ieee1394/csr.c
--- linux-2.4.27/drivers/ieee1394/csr.c	2004-02-20 14:11:42.000000000 +0000
+++ linux-2.4.27-leo/drivers/ieee1394/csr.c	2004-09-17 03:19:36.000000000 +0100
@@ -18,6 +18,7 @@
  */
 
 #include <linux/string.h>
+#include <linux/sched.h>
 #include <linux/module.h> /* needed for MODULE_PARM */
 #include <linux/param.h>
 #include <linux/spinlock.h>
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/Makefile linux-2.4.27-leo/drivers/Makefile
--- linux-2.4.27/drivers/Makefile	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.27-leo/drivers/Makefile	2004-09-20 21:34:47.000000000 +0100
@@ -8,7 +8,7 @@
 
 mod-subdirs :=	dio hil mtd sbus video macintosh usb input telephony ide \
 		message/i2o message/fusion scsi md ieee1394 pnp isdn atm \
-		fc4 net/hamradio i2c acpi bluetooth usb/gadget
+		fc4 net/hamradio i2c acpi bluetooth usb/gadget sensors
 
 subdir-y :=	parport char block net sound misc media cdrom hotplug
 subdir-m :=	$(subdir-y)
@@ -45,6 +45,7 @@
 # CONFIG_HAMRADIO can be set without CONFIG_NETDEVICE being set  -- ch
 subdir-$(CONFIG_HAMRADIO)	+= net/hamradio
 subdir-$(CONFIG_I2C)		+= i2c
+subdir-$(CONFIG_SENSORS) 	+= sensors
 subdir-$(CONFIG_ACPI_BOOT)	+= acpi
 
 subdir-$(CONFIG_BLUEZ)		+= bluetooth
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/Config.in linux-2.4.27-leo/drivers/md/Config.in
--- linux-2.4.27/drivers/md/Config.in	2001-09-14 22:22:18.000000000 +0100
+++ linux-2.4.27-leo/drivers/md/Config.in	2004-09-17 03:19:32.000000000 +0100
@@ -12,7 +12,11 @@
 dep_tristate '  RAID-1 (mirroring) mode' CONFIG_MD_RAID1 $CONFIG_BLK_DEV_MD
 dep_tristate '  RAID-4/RAID-5 mode' CONFIG_MD_RAID5 $CONFIG_BLK_DEV_MD
 dep_tristate '  Multipath I/O support' CONFIG_MD_MULTIPATH $CONFIG_BLK_DEV_MD
+dep_bool     '  Verbose startup messages' CONFIG_MD_VERBMSG $CONFIG_BLK_DEV_MD
+
 
 dep_tristate ' Logical volume manager (LVM) support' CONFIG_BLK_DEV_LVM $CONFIG_MD
+dep_tristate ' Device-mapper support' CONFIG_BLK_DEV_DM $CONFIG_MD
+dep_tristate '  Mirror (RAID-1) support' CONFIG_BLK_DEV_DM_MIRROR $CONFIG_BLK_DEV_DM
 
 endmenu
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/dm.c linux-2.4.27-leo/drivers/md/dm.c
--- linux-2.4.27/drivers/md/dm.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/md/dm.c	2004-09-17 03:19:08.000000000 +0100
@@ -0,0 +1,1115 @@
+/*
+ * Copyright (C) 2001, 2002 Sistina Software (UK) Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm.h"
+#include "kcopyd.h"
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/blk.h>
+#include <linux/blkpg.h>
+#include <linux/mempool.h>
+#include <linux/slab.h>
+#include <linux/major.h>
+#include <linux/kdev_t.h>
+#include <linux/lvm.h>
+
+#include <asm/uaccess.h>
+
+static const char *_name = DM_NAME;
+#define DEFAULT_READ_AHEAD 64
+
+struct dm_io {
+	struct mapped_device *md;
+
+	struct dm_target *ti;
+	int rw;
+	union map_info map_context;
+	void (*end_io) (struct buffer_head * bh, int uptodate);
+	void *context;
+};
+
+struct deferred_io {
+	int rw;
+	struct buffer_head *bh;
+	struct deferred_io *next;
+};
+
+/*
+ * Bits for the md->flags field.
+ */
+#define DMF_BLOCK_IO 0
+#define DMF_SUSPENDED 1
+
+struct mapped_device {
+	struct rw_semaphore lock;
+	atomic_t holders;
+
+	kdev_t dev;
+	unsigned long flags;
+
+	/*
+	 * A list of ios that arrived while we were suspended.
+	 */
+	atomic_t pending;
+	wait_queue_head_t wait;
+	struct deferred_io *deferred;
+
+	/*
+	 * The current mapping.
+	 */
+	struct dm_table *map;
+
+	/*
+	 * io objects are allocated from here.
+	 */
+	mempool_t *io_pool;
+
+	/*
+	 * Event handling.
+	 */
+	uint32_t event_nr;
+	wait_queue_head_t eventq;
+};
+
+#define MIN_IOS 256
+static kmem_cache_t *_io_cache;
+
+static struct mapped_device *get_kdev(kdev_t dev);
+static int dm_request(request_queue_t *q, int rw, struct buffer_head *bh);
+static int dm_user_bmap(struct inode *inode, struct lv_bmap *lvb);
+
+/*-----------------------------------------------------------------
+ * In order to avoid the 256 minor number limit we are going to
+ * register more major numbers as neccessary.
+ *---------------------------------------------------------------*/
+#define MAX_MINORS (1 << MINORBITS)
+
+struct major_details {
+	unsigned int major;
+
+	int transient;
+	struct list_head transient_list;
+
+	unsigned int first_free_minor;
+	int nr_free_minors;
+
+	struct mapped_device *mds[MAX_MINORS];
+	int blk_size[MAX_MINORS];
+	int blksize_size[MAX_MINORS];
+	int hardsect_size[MAX_MINORS];
+};
+
+static struct rw_semaphore _dev_lock;
+static struct major_details *_majors[MAX_BLKDEV];
+
+/*
+ * This holds a list of majors that non-specified device numbers
+ * may be allocated from.  Only majors with free minors appear on
+ * this list.
+ */
+static LIST_HEAD(_transients_free);
+
+static int __alloc_major(unsigned int major, struct major_details **result)
+{
+	int r;
+	unsigned int transient = !major;
+	struct major_details *maj;
+
+	/* Major already allocated? */
+	if (major && _majors[major])
+		return 0;
+
+	maj = kmalloc(sizeof(*maj), GFP_KERNEL);
+	if (!maj)
+		return -ENOMEM;
+
+	memset(maj, 0, sizeof(*maj));
+	INIT_LIST_HEAD(&maj->transient_list);
+
+	maj->nr_free_minors = MAX_MINORS;
+
+	r = register_blkdev(major, _name, &dm_blk_dops);
+	if (r < 0) {
+		DMERR("register_blkdev failed for %d", major);
+		kfree(maj);
+		return r;
+	}
+	if (r > 0)
+		major = r;
+
+	maj->major = major;
+
+	if (transient) {
+		maj->transient = transient;
+		list_add_tail(&maj->transient_list, &_transients_free);
+	}
+
+	_majors[major] = maj;
+
+	blk_size[major] = maj->blk_size;
+	blksize_size[major] = maj->blksize_size;
+	hardsect_size[major] = maj->hardsect_size;
+	read_ahead[major] = DEFAULT_READ_AHEAD;
+
+	blk_queue_make_request(BLK_DEFAULT_QUEUE(major), dm_request);
+
+	*result = maj;
+	return 0;
+}
+
+static void __free_major(struct major_details *maj)
+{
+	unsigned int major = maj->major;
+
+	list_del(&maj->transient_list);
+
+	read_ahead[major] = 0;
+	blk_size[major] = NULL;
+	blksize_size[major] = NULL;
+	hardsect_size[major] = NULL;
+
+	_majors[major] = NULL;
+	kfree(maj);
+
+	if (unregister_blkdev(major, _name) < 0)
+		DMERR("devfs_unregister_blkdev failed");
+}
+
+static void free_all_majors(void)
+{
+	unsigned int major = ARRAY_SIZE(_majors);
+
+	down_write(&_dev_lock);
+
+	while (major--)
+		if (_majors[major])
+			__free_major(_majors[major]);
+
+	up_write(&_dev_lock);
+}
+
+static void free_dev(kdev_t dev)
+{
+	unsigned int major = major(dev);
+	unsigned int minor = minor(dev);
+	struct major_details *maj;
+
+	down_write(&_dev_lock);
+
+	maj = _majors[major];
+	if (!maj)
+		goto out;
+
+	maj->mds[minor] = NULL;
+	maj->nr_free_minors++;
+
+	if (maj->nr_free_minors == MAX_MINORS) {
+		__free_major(maj);
+		goto out;
+	}
+
+	if (!maj->transient)
+		goto out;
+
+	if (maj->nr_free_minors == 1)
+		list_add_tail(&maj->transient_list, &_transients_free);
+
+	if (minor < maj->first_free_minor)
+		maj->first_free_minor = minor;
+
+      out:
+	up_write(&_dev_lock);
+}
+
+static void __alloc_minor(struct major_details *maj, unsigned int minor,
+			  struct mapped_device *md)
+{
+	maj->mds[minor] = md;
+	md->dev = mk_kdev(maj->major, minor);
+	maj->nr_free_minors--;
+
+	if (maj->transient && !maj->nr_free_minors)
+		list_del_init(&maj->transient_list);
+}
+
+/*
+ * See if requested kdev_t is available.
+ */
+static int specific_dev(kdev_t dev, struct mapped_device *md)
+{
+	int r = 0;
+	unsigned int major = major(dev);
+	unsigned int minor = minor(dev);
+	struct major_details *maj;
+
+	if (!major || (major > MAX_BLKDEV) || (minor >= MAX_MINORS)) {
+		DMWARN("device number requested out of range (%d, %d)",
+		       major, minor);
+		return -EINVAL;
+	}
+
+	down_write(&_dev_lock);
+	maj = _majors[major];
+
+	/* Register requested major? */
+	if (!maj) {
+		r = __alloc_major(major, &maj);
+		if (r)
+			goto out;
+
+		major = maj->major;
+	}
+
+	if (maj->mds[minor]) {
+		r = -EBUSY;
+		goto out;
+	}
+
+	__alloc_minor(maj, minor, md);
+
+      out:
+	up_write(&_dev_lock);
+
+	return r;
+}
+
+/*
+ * Find first unused device number, requesting a new major number if required.
+ */
+static int first_free_dev(struct mapped_device *md)
+{
+	int r = 0;
+	struct major_details *maj;
+
+	down_write(&_dev_lock);
+
+	if (list_empty(&_transients_free)) {
+		r = __alloc_major(0, &maj);
+		if (r)
+			goto out;
+	} else
+		maj = list_entry(_transients_free.next, struct major_details,
+				 transient_list);
+
+	while (maj->mds[maj->first_free_minor++])
+		;
+
+	__alloc_minor(maj, maj->first_free_minor - 1, md);
+
+      out:
+	up_write(&_dev_lock);
+
+	return r;
+}
+
+static struct mapped_device *get_kdev(kdev_t dev)
+{
+	struct mapped_device *md;
+	struct major_details *maj;
+
+	down_read(&_dev_lock);
+	maj = _majors[major(dev)];
+	if (!maj) {
+		md = NULL;
+		goto out;
+	}
+	md = maj->mds[minor(dev)];
+	if (md)
+		dm_get(md);
+      out:
+	up_read(&_dev_lock);
+
+	return md;
+}
+
+/*-----------------------------------------------------------------
+ * init/exit code
+ *---------------------------------------------------------------*/
+
+static __init int local_init(void)
+{
+	init_rwsem(&_dev_lock);
+
+	/* allocate a slab for the dm_ios */
+	_io_cache = kmem_cache_create("dm io",
+				      sizeof(struct dm_io), 0, 0, NULL, NULL);
+
+	if (!_io_cache)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void local_exit(void)
+{
+	kmem_cache_destroy(_io_cache);
+	free_all_majors();
+
+	DMINFO("cleaned up");
+}
+
+/*
+ * We have a lot of init/exit functions, so it seems easier to
+ * store them in an array.  The disposable macro 'xx'
+ * expands a prefix into a pair of function names.
+ */
+static struct {
+	int (*init) (void);
+	void (*exit) (void);
+
+} _inits[] = {
+#define xx(n) {n ## _init, n ## _exit},
+	xx(local)
+	xx(kcopyd)
+	xx(dm_target)
+	xx(dm_linear)
+	xx(dm_stripe)
+	xx(dm_snapshot)
+	xx(dm_interface)
+#undef xx
+};
+
+static int __init dm_init(void)
+{
+	const int count = ARRAY_SIZE(_inits);
+
+	int r, i;
+
+	for (i = 0; i < count; i++) {
+		r = _inits[i].init();
+		if (r)
+			goto bad;
+	}
+
+	return 0;
+
+      bad:
+	while (i--)
+		_inits[i].exit();
+
+	return r;
+}
+
+static void __exit dm_exit(void)
+{
+	int i = ARRAY_SIZE(_inits);
+
+	while (i--)
+		_inits[i].exit();
+}
+
+/*
+ * Block device functions
+ */
+static int dm_blk_open(struct inode *inode, struct file *file)
+{
+	struct mapped_device *md;
+
+	md = get_kdev(inode->i_rdev);
+	if (!md)
+		return -ENXIO;
+
+	return 0;
+}
+
+static int dm_blk_close(struct inode *inode, struct file *file)
+{
+	struct mapped_device *md;
+
+	md = get_kdev(inode->i_rdev);
+	dm_put(md);		/* put the reference gained by dm_blk_open */
+	dm_put(md);
+	return 0;
+}
+
+static inline struct dm_io *alloc_io(struct mapped_device *md)
+{
+	return mempool_alloc(md->io_pool, GFP_NOIO);
+}
+
+static inline void free_io(struct mapped_device *md, struct dm_io *io)
+{
+	mempool_free(io, md->io_pool);
+}
+
+static inline struct deferred_io *alloc_deferred(void)
+{
+	return kmalloc(sizeof(struct deferred_io), GFP_NOIO);
+}
+
+static inline void free_deferred(struct deferred_io *di)
+{
+	kfree(di);
+}
+
+static inline sector_t volume_size(kdev_t dev)
+{
+	return blk_size[major(dev)][minor(dev)] << 1;
+}
+
+/* FIXME: check this */
+static int dm_blk_ioctl(struct inode *inode, struct file *file,
+			unsigned int command, unsigned long a)
+{
+	kdev_t dev = inode->i_rdev;
+	long size;
+
+	switch (command) {
+	case BLKROSET:
+	case BLKROGET:
+	case BLKRASET:
+	case BLKRAGET:
+	case BLKFLSBUF:
+	case BLKSSZGET:
+		//case BLKRRPART: /* Re-read partition tables */
+		//case BLKPG:
+	case BLKELVGET:
+	case BLKELVSET:
+	case BLKBSZGET:
+	case BLKBSZSET:
+		return blk_ioctl(dev, command, a);
+		break;
+
+	case BLKGETSIZE:
+		size = volume_size(dev);
+		if (copy_to_user((void *) a, &size, sizeof(long)))
+			return -EFAULT;
+		break;
+
+	case BLKGETSIZE64:
+		size = volume_size(dev);
+		if (put_user((u64) ((u64) size) << 9, (u64 *) a))
+			return -EFAULT;
+		break;
+
+	case BLKRRPART:
+		return -ENOTTY;
+
+	case LV_BMAP:
+		return dm_user_bmap(inode, (struct lv_bmap *) a);
+
+	default:
+		DMWARN("unknown block ioctl 0x%x", command);
+		return -ENOTTY;
+	}
+
+	return 0;
+}
+
+/*
+ * Add the buffer to the list of deferred io.
+ */
+static int queue_io(struct mapped_device *md, struct buffer_head *bh, int rw)
+{
+	struct deferred_io *di;
+
+	di = alloc_deferred();
+	if (!di)
+		return -ENOMEM;
+
+	down_write(&md->lock);
+
+	if (!test_bit(DMF_BLOCK_IO, &md->flags)) {
+		up_write(&md->lock);
+		free_deferred(di);
+		return 1;
+	}
+
+	di->bh = bh;
+	di->rw = rw;
+	di->next = md->deferred;
+	md->deferred = di;
+
+	up_write(&md->lock);
+	return 0;		/* deferred successfully */
+}
+
+/*
+ * bh->b_end_io routine that decrements the pending count
+ * and then calls the original bh->b_end_io fn.
+ */
+static void dec_pending(struct buffer_head *bh, int uptodate)
+{
+	int r;
+	struct dm_io *io = bh->b_private;
+	dm_endio_fn endio = io->ti->type->end_io;
+
+	if (endio) {
+		r = endio(io->ti, bh, io->rw, uptodate ? 0 : -EIO,
+			  &io->map_context);
+		if (r < 0)
+			uptodate = 0;
+
+		else if (r > 0)
+			/* the target wants another shot at the io */
+			return;
+	}
+
+	if (atomic_dec_and_test(&io->md->pending))
+		/* nudge anyone waiting on suspend queue */
+		wake_up(&io->md->wait);
+
+	bh->b_end_io = io->end_io;
+	bh->b_private = io->context;
+	free_io(io->md, io);
+
+	bh->b_end_io(bh, uptodate);
+}
+
+/*
+ * Do the bh mapping for a given leaf
+ */
+static inline int __map_buffer(struct mapped_device *md, int rw,
+			       struct buffer_head *bh, struct dm_io *io)
+{
+	struct dm_target *ti;
+
+	if (!md->map)
+		return -EINVAL;
+
+	ti = dm_table_find_target(md->map, bh->b_rsector);
+	if (!ti->type)
+		return -EINVAL;
+
+	/* hook the end io request fn */
+	atomic_inc(&md->pending);
+	io->md = md;
+	io->ti = ti;
+	io->rw = rw;
+	io->end_io = bh->b_end_io;
+	io->context = bh->b_private;
+	bh->b_end_io = dec_pending;
+	bh->b_private = io;
+
+	return ti->type->map(ti, bh, rw, &io->map_context);
+}
+
+/*
+ * Checks to see if we should be deferring io, if so it queues it
+ * and returns 1.
+ */
+static inline int __deferring(struct mapped_device *md, int rw,
+			      struct buffer_head *bh)
+{
+	int r;
+
+	/*
+	 * If we're suspended we have to queue this io for later.
+	 */
+	while (test_bit(DMF_BLOCK_IO, &md->flags)) {
+		up_read(&md->lock);
+
+		/*
+		 * There's no point deferring a read ahead
+		 * request, just drop it.
+		 */
+		if (rw == READA) {
+			down_read(&md->lock);
+			return -EIO;
+		}
+
+		r = queue_io(md, bh, rw);
+		down_read(&md->lock);
+
+		if (r < 0)
+			return r;
+
+		if (r == 0)
+			return 1;	/* deferred successfully */
+
+	}
+
+	return 0;
+}
+
+static int dm_request(request_queue_t *q, int rw, struct buffer_head *bh)
+{
+	int r;
+	struct dm_io *io;
+	struct mapped_device *md;
+
+	md = get_kdev(bh->b_rdev);
+	if (!md) {
+		buffer_IO_error(bh);
+		return 0;
+	}
+
+	io = alloc_io(md);
+	down_read(&md->lock);
+
+	r = __deferring(md, rw, bh);
+	if (r < 0)
+		goto bad;
+
+	else if (!r) {
+		/* not deferring */
+		r = __map_buffer(md, rw, bh, io);
+		if (r < 0)
+			goto bad;
+	} else
+		r = 0;
+
+	up_read(&md->lock);
+	dm_put(md);
+	return r;
+
+      bad:
+	buffer_IO_error(bh);
+	up_read(&md->lock);
+	dm_put(md);
+	return 0;
+}
+
+static int check_dev_size(kdev_t dev, unsigned long block)
+{
+	unsigned int major = major(dev);
+	unsigned int minor = minor(dev);
+
+	/* FIXME: check this */
+	unsigned long max_sector = (blk_size[major][minor] << 1) + 1;
+	unsigned long sector = (block + 1) * (blksize_size[major][minor] >> 9);
+
+	return (sector > max_sector) ? 0 : 1;
+}
+
+/*
+ * Creates a dummy buffer head and maps it (for lilo).
+ */
+static int __bmap(struct mapped_device *md, kdev_t dev, unsigned long block,
+		  kdev_t *r_dev, unsigned long *r_block)
+{
+	struct buffer_head bh;
+	struct dm_target *ti;
+	union map_info map_context;
+	int r;
+
+	if (test_bit(DMF_BLOCK_IO, &md->flags)) {
+		return -EPERM;
+	}
+
+	if (!check_dev_size(dev, block)) {
+		return -EINVAL;
+	}
+
+	if (!md->map)
+		return -EINVAL;
+
+	/* setup dummy bh */
+	memset(&bh, 0, sizeof(bh));
+	bh.b_blocknr = block;
+	bh.b_dev = bh.b_rdev = dev;
+	bh.b_size = blksize_size[major(dev)][minor(dev)];
+	bh.b_rsector = block * (bh.b_size >> 9);
+
+	/* find target */
+	ti = dm_table_find_target(md->map, bh.b_rsector);
+
+	/* do the mapping */
+	r = ti->type->map(ti, &bh, READ, &map_context);
+	ti->type->end_io(ti, &bh, READ, 0, &map_context);
+
+	if (!r) {
+		*r_dev = bh.b_rdev;
+		*r_block = bh.b_rsector / (bh.b_size >> 9);
+	}
+
+	return r;
+}
+
+/*
+ * Marshals arguments and results between user and kernel space.
+ */
+static int dm_user_bmap(struct inode *inode, struct lv_bmap *lvb)
+{
+	struct mapped_device *md;
+	unsigned long block, r_block;
+	kdev_t r_dev;
+	int r;
+
+	if (get_user(block, &lvb->lv_block))
+		return -EFAULT;
+
+	md = get_kdev(inode->i_rdev);
+	if (!md)
+		return -ENXIO;
+
+	down_read(&md->lock);
+	r = __bmap(md, inode->i_rdev, block, &r_dev, &r_block);
+	up_read(&md->lock);
+	dm_put(md);
+
+	if (!r && (put_user(kdev_t_to_nr(r_dev), &lvb->lv_dev) ||
+		   put_user(r_block, &lvb->lv_block)))
+		r = -EFAULT;
+
+	return r;
+}
+
+static void free_md(struct mapped_device *md)
+{
+	free_dev(md->dev);
+	mempool_destroy(md->io_pool);
+	kfree(md);
+}
+
+/*
+ * Allocate and initialise a blank device with a given minor.
+ */
+static struct mapped_device *alloc_md(kdev_t dev)
+{
+	int r;
+	struct mapped_device *md = kmalloc(sizeof(*md), GFP_KERNEL);
+
+	if (!md) {
+		DMWARN("unable to allocate device, out of memory.");
+		return NULL;
+	}
+
+	memset(md, 0, sizeof(*md));
+
+	/* Allocate suitable device number */
+	if (!dev)
+		r = first_free_dev(md);
+	else
+		r = specific_dev(dev, md);
+
+	if (r) {
+		kfree(md);
+		return NULL;
+	}
+
+	md->io_pool = mempool_create(MIN_IOS, mempool_alloc_slab,
+				     mempool_free_slab, _io_cache);
+	if (!md->io_pool) {
+		free_md(md);
+		kfree(md);
+		return NULL;
+	}
+
+	init_rwsem(&md->lock);
+	atomic_set(&md->holders, 1);
+	atomic_set(&md->pending, 0);
+	init_waitqueue_head(&md->wait);
+	init_waitqueue_head(&md->eventq);
+
+	return md;
+}
+
+/*
+ * The hardsect size for a mapped device is the largest hardsect size
+ * from the devices it maps onto.
+ */
+static int __find_hardsect_size(struct list_head *devices)
+{
+	int result = 512, size;
+	struct list_head *tmp;
+
+	list_for_each (tmp, devices) {
+		struct dm_dev *dd = list_entry(tmp, struct dm_dev, list);
+		size = get_hardsect_size(dd->dev);
+		if (size > result)
+			result = size;
+	}
+
+	return result;
+}
+
+/*
+ * Bind a table to the device.
+ */
+static void event_callback(void *context)
+{
+	struct mapped_device *md = (struct mapped_device *) context;
+
+	down_write(&md->lock);
+	md->event_nr++;
+	wake_up_interruptible(&md->eventq);
+	up_write(&md->lock);
+}
+
+static int __bind(struct mapped_device *md, struct dm_table *t)
+{
+	unsigned int minor = minor(md->dev);
+	unsigned int major = major(md->dev);
+	md->map = t;
+
+	/* in k */
+	blk_size[major][minor] = dm_table_get_size(t) >> 1;
+	blksize_size[major][minor] = BLOCK_SIZE;
+	hardsect_size[major][minor] =
+	    __find_hardsect_size(dm_table_get_devices(t));
+	register_disk(NULL, md->dev, 1, &dm_blk_dops, blk_size[major][minor]);
+
+	dm_table_event_callback(md->map, event_callback, md);
+	dm_table_get(t);
+	return 0;
+}
+
+static void __unbind(struct mapped_device *md)
+{
+	unsigned int minor = minor(md->dev);
+	unsigned int major = major(md->dev);
+
+	if (md->map) {
+		dm_table_event_callback(md->map, NULL, NULL);
+		dm_table_put(md->map);
+		md->map = NULL;
+
+	}
+
+	blk_size[major][minor] = 0;
+	blksize_size[major][minor] = 0;
+	hardsect_size[major][minor] = 0;
+}
+
+/*
+ * Constructor for a new device.
+ */
+int dm_create(kdev_t dev, struct mapped_device **result)
+{
+	struct mapped_device *md;
+
+	md = alloc_md(dev);
+	if (!md)
+		return -ENXIO;
+
+	__unbind(md);	/* Ensure zero device size */
+
+	*result = md;
+	return 0;
+}
+
+void dm_get(struct mapped_device *md)
+{
+	atomic_inc(&md->holders);
+}
+
+void dm_put(struct mapped_device *md)
+{
+	if (atomic_dec_and_test(&md->holders)) {
+		if (md->map)
+			dm_table_suspend_targets(md->map);
+		__unbind(md);
+		free_md(md);
+	}
+}
+
+/*
+ * Requeue the deferred io by calling generic_make_request.
+ */
+static void flush_deferred_io(struct deferred_io *c)
+{
+	struct deferred_io *n;
+
+	while (c) {
+		n = c->next;
+		generic_make_request(c->rw, c->bh);
+		free_deferred(c);
+		c = n;
+	}
+}
+
+/*
+ * Swap in a new table (destroying old one).
+ */
+int dm_swap_table(struct mapped_device *md, struct dm_table *table)
+{
+	int r;
+
+	down_write(&md->lock);
+
+	/*
+	 * The device must be suspended, or have no table bound yet.
+	 */
+	if (md->map && !test_bit(DMF_SUSPENDED, &md->flags)) {
+		up_write(&md->lock);
+		return -EPERM;
+	}
+
+	__unbind(md);
+	r = __bind(md, table);
+	if (r)
+		return r;
+
+	up_write(&md->lock);
+	return 0;
+}
+
+/*
+ * We need to be able to change a mapping table under a mounted
+ * filesystem.  For example we might want to move some data in
+ * the background.  Before the table can be swapped with
+ * dm_bind_table, dm_suspend must be called to flush any in
+ * flight io and ensure that any further io gets deferred.
+ */
+int dm_suspend(struct mapped_device *md)
+{
+	int r = 0;
+	DECLARE_WAITQUEUE(wait, current);
+
+	down_write(&md->lock);
+
+	/*
+	 * First we set the BLOCK_IO flag so no more ios will be
+	 * mapped.
+	 */
+	if (test_bit(DMF_BLOCK_IO, &md->flags)) {
+		up_write(&md->lock);
+		return -EINVAL;
+	}
+
+	set_bit(DMF_BLOCK_IO, &md->flags);
+	add_wait_queue(&md->wait, &wait);
+	up_write(&md->lock);
+
+	/*
+	 * Then we wait for the already mapped ios to
+	 * complete.
+	 */
+	run_task_queue(&tq_disk);
+	while (1) {
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		if (!atomic_read(&md->pending) || signal_pending(current))
+			break;
+
+		schedule();
+	}
+	set_current_state(TASK_RUNNING);
+
+	down_write(&md->lock);
+	remove_wait_queue(&md->wait, &wait);
+
+	/* did we flush everything ? */
+	if (atomic_read(&md->pending)) {
+		clear_bit(DMF_BLOCK_IO, &md->flags);
+		r = -EINTR;
+	} else {
+		set_bit(DMF_SUSPENDED, &md->flags);
+		if (md->map)
+			dm_table_suspend_targets(md->map);
+	}
+	up_write(&md->lock);
+
+	return r;
+}
+
+int dm_resume(struct mapped_device *md)
+{
+	struct deferred_io *def;
+
+	down_write(&md->lock);
+	if (!test_bit(DMF_SUSPENDED, &md->flags)) {
+		up_write(&md->lock);
+		return -EINVAL;
+	}
+
+	if (md->map)
+		dm_table_resume_targets(md->map);
+
+	clear_bit(DMF_SUSPENDED, &md->flags);
+	clear_bit(DMF_BLOCK_IO, &md->flags);
+	def = md->deferred;
+	md->deferred = NULL;
+	up_write(&md->lock);
+
+	flush_deferred_io(def);
+	run_task_queue(&tq_disk);
+
+	return 0;
+}
+
+struct dm_table *dm_get_table(struct mapped_device *md)
+{
+	struct dm_table *t;
+
+	down_read(&md->lock);
+	t = md->map;
+	if (t)
+		dm_table_get(t);
+	up_read(&md->lock);
+
+	return t;
+}
+
+/*-----------------------------------------------------------------
+ * Event notification.
+ *---------------------------------------------------------------*/
+uint32_t dm_get_event_nr(struct mapped_device *md)
+{
+	uint32_t r;
+
+	down_read(&md->lock);
+	r = md->event_nr;
+	up_read(&md->lock);
+
+	return r;
+}
+
+int dm_add_wait_queue(struct mapped_device *md, wait_queue_t *wq,
+		      uint32_t event_nr)
+{
+	down_write(&md->lock);
+	if (event_nr != md->event_nr) {
+		up_write(&md->lock);
+		return 1;
+	}
+
+	add_wait_queue(&md->eventq, wq);
+	up_write(&md->lock);
+
+	return 0;
+}
+
+const char *dm_kdevname(kdev_t dev)
+{
+	static char buffer[32];
+	sprintf(buffer, "%03d:%03d", MAJOR(dev), MINOR(dev));
+	return buffer;
+}
+
+void dm_remove_wait_queue(struct mapped_device *md, wait_queue_t *wq)
+{
+	down_write(&md->lock);
+	remove_wait_queue(&md->eventq, wq);
+	up_write(&md->lock);
+}
+
+kdev_t dm_kdev(struct mapped_device *md)
+{
+	kdev_t dev;
+
+	down_read(&md->lock);
+	dev = md->dev;
+	up_read(&md->lock);
+
+	return dev;
+}
+
+int dm_suspended(struct mapped_device *md)
+{
+	return test_bit(DMF_SUSPENDED, &md->flags);
+}
+
+struct block_device_operations dm_blk_dops = {
+	.open = dm_blk_open,
+	.release = dm_blk_close,
+	.ioctl = dm_blk_ioctl,
+	.owner = THIS_MODULE
+};
+
+/*
+ * module hooks
+ */
+module_init(dm_init);
+module_exit(dm_exit);
+
+MODULE_DESCRIPTION(DM_NAME " driver");
+MODULE_AUTHOR("Joe Thornber <thornber@sistina.com>");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(dm_kdevname);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/dm-daemon.c linux-2.4.27-leo/drivers/md/dm-daemon.c
--- linux-2.4.27/drivers/md/dm-daemon.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/md/dm-daemon.c	2004-09-17 03:19:08.000000000 +0100
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2003 Sistina Software
+ *
+ * This file is released under the LGPL.
+ */
+
+#include "dm.h"
+#include "dm-daemon.h"
+
+#include <linux/module.h>
+#include <linux/sched.h>
+
+static int daemon(void *arg)
+{
+	struct dm_daemon *dd = (struct dm_daemon *) arg;
+	DECLARE_WAITQUEUE(wq, current);
+
+	daemonize();
+	reparent_to_init();
+
+	/* block all signals */
+	spin_lock_irq(&current->sigmask_lock);
+	sigfillset(&current->blocked);
+	flush_signals(current);
+	spin_unlock_irq(&current->sigmask_lock);
+
+	strcpy(current->comm, dd->name);
+	atomic_set(&dd->please_die, 0);
+
+	add_wait_queue(&dd->job_queue, &wq);
+
+	down(&dd->run_lock);
+	up(&dd->start_lock);
+
+	/*
+	 * dd->fn() could do anything, very likely it will
+	 * suspend.  So we can't set the state to
+	 * TASK_INTERRUPTIBLE before calling it.  In order to
+	 * prevent a race with a waking thread we do this little
+	 * dance with the dd->woken variable.
+	 */
+	while (1) {
+		do {
+			set_current_state(TASK_RUNNING);
+
+			if (atomic_read(&dd->please_die))
+				goto out;
+
+			atomic_set(&dd->woken, 0);
+			dd->fn();
+			yield();
+
+			set_current_state(TASK_INTERRUPTIBLE);
+		} while (atomic_read(&dd->woken));
+
+		schedule();
+	}
+
+ out:
+	remove_wait_queue(&dd->job_queue, &wq);
+	up(&dd->run_lock);
+	return 0;
+}
+
+int dm_daemon_start(struct dm_daemon *dd, const char *name, void (*fn)(void))
+{
+	pid_t pid = 0;
+
+	/*
+	 * Initialise the dm_daemon.
+	 */
+	dd->fn = fn;
+	strncpy(dd->name, name, sizeof(dd->name) - 1);
+	sema_init(&dd->start_lock, 1);
+	sema_init(&dd->run_lock, 1);
+	init_waitqueue_head(&dd->job_queue);
+
+	/*
+	 * Start the new thread.
+	 */
+	down(&dd->start_lock);
+	pid = kernel_thread(daemon, dd, 0);
+	if (pid <= 0) {
+		DMERR("Failed to start %s thread", name);
+		return -EAGAIN;
+	}
+
+	/*
+	 * wait for the daemon to up this mutex.
+	 */
+	down(&dd->start_lock);
+	up(&dd->start_lock);
+
+	return 0;
+}
+
+void dm_daemon_stop(struct dm_daemon *dd)
+{
+	atomic_set(&dd->please_die, 1);
+	dm_daemon_wake(dd);
+	down(&dd->run_lock);
+	up(&dd->run_lock);
+}
+
+void dm_daemon_wake(struct dm_daemon *dd)
+{
+	atomic_set(&dd->woken, 1);
+	wake_up_interruptible(&dd->job_queue);
+}
+
+EXPORT_SYMBOL(dm_daemon_start);
+EXPORT_SYMBOL(dm_daemon_stop);
+EXPORT_SYMBOL(dm_daemon_wake);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/dm-daemon.h linux-2.4.27-leo/drivers/md/dm-daemon.h
--- linux-2.4.27/drivers/md/dm-daemon.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/md/dm-daemon.h	2004-09-20 21:40:22.000000000 +0100
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2003 Sistina Software
+ *
+ * This file is released under the LGPL.
+ */
+
+#ifndef DM_DAEMON_H
+#define DM_DAEMON_H
+
+#include <asm/atomic.h>
+#include <asm/semaphore.h>
+
+struct dm_daemon {
+	void (*fn)(void);
+	char name[16];
+	atomic_t please_die;
+	struct semaphore start_lock;
+	struct semaphore run_lock;
+
+	atomic_t woken;
+	wait_queue_head_t job_queue;
+};
+
+int dm_daemon_start(struct dm_daemon *dd, const char *name, void (*fn)(void));
+void dm_daemon_stop(struct dm_daemon *dd);
+void dm_daemon_wake(struct dm_daemon *dd);
+int dm_daemon_running(struct dm_daemon *dd);
+
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/dm-exception-store.c linux-2.4.27-leo/drivers/md/dm-exception-store.c
--- linux-2.4.27/drivers/md/dm-exception-store.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/md/dm-exception-store.c	2004-09-17 03:19:08.000000000 +0100
@@ -0,0 +1,673 @@
+/*
+ * dm-snapshot.c
+ *
+ * Copyright (C) 2001-2002 Sistina Software (UK) Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-snapshot.h"
+#include "dm-io.h"
+#include "kcopyd.h"
+
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+
+/*-----------------------------------------------------------------
+ * Persistent snapshots, by persistent we mean that the snapshot
+ * will survive a reboot.
+ *---------------------------------------------------------------*/
+
+/*
+ * We need to store a record of which parts of the origin have
+ * been copied to the snapshot device.  The snapshot code
+ * requires that we copy exception chunks to chunk aligned areas
+ * of the COW store.  It makes sense therefore, to store the
+ * metadata in chunk size blocks.
+ *
+ * There is no backward or forward compatibility implemented,
+ * snapshots with different disk versions than the kernel will
+ * not be usable.  It is expected that "lvcreate" will blank out
+ * the start of a fresh COW device before calling the snapshot
+ * constructor.
+ *
+ * The first chunk of the COW device just contains the header.
+ * After this there is a chunk filled with exception metadata,
+ * followed by as many exception chunks as can fit in the
+ * metadata areas.
+ *
+ * All on disk structures are in little-endian format.  The end
+ * of the exceptions info is indicated by an exception with a
+ * new_chunk of 0, which is invalid since it would point to the
+ * header chunk.
+ */
+
+/*
+ * Magic for persistent snapshots: "SnAp" - Feeble isn't it.
+ */
+#define SNAP_MAGIC 0x70416e53
+
+/*
+ * The on-disk version of the metadata.
+ */
+#define SNAPSHOT_DISK_VERSION 1
+
+struct disk_header {
+	uint32_t magic;
+
+	/*
+	 * Is this snapshot valid.  There is no way of recovering
+	 * an invalid snapshot.
+	 */
+	uint32_t valid;
+
+	/*
+	 * Simple, incrementing version. no backward
+	 * compatibility.
+	 */
+	uint32_t version;
+
+	/* In sectors */
+	uint32_t chunk_size;
+};
+
+struct disk_exception {
+	uint64_t old_chunk;
+	uint64_t new_chunk;
+};
+
+struct commit_callback {
+	void (*callback)(void *, int success);
+	void *context;
+};
+
+/*
+ * The top level structure for a persistent exception store.
+ */
+struct pstore {
+	struct dm_snapshot *snap;	/* up pointer to my snapshot */
+	int version;
+	int valid;
+	uint32_t chunk_size;
+	uint32_t exceptions_per_area;
+
+	/*
+	 * Now that we have an asynchronous kcopyd there is no
+	 * need for large chunk sizes, so it wont hurt to have a
+	 * whole chunks worth of metadata in memory at once.
+	 */
+	void *area;
+
+	/*
+	 * Used to keep track of which metadata area the data in
+	 * 'chunk' refers to.
+	 */
+	uint32_t current_area;
+
+	/*
+	 * The next free chunk for an exception.
+	 */
+	uint32_t next_free;
+
+	/*
+	 * The index of next free exception in the current
+	 * metadata area.
+	 */
+	uint32_t current_committed;
+
+	atomic_t pending_count;
+	uint32_t callback_count;
+	struct commit_callback *callbacks;
+};
+
+static inline unsigned int sectors_to_pages(unsigned int sectors)
+{
+	return sectors / (PAGE_SIZE / SECTOR_SIZE);
+}
+
+static int alloc_area(struct pstore *ps)
+{
+	int r = -ENOMEM;
+	size_t i, len, nr_pages;
+	struct page *page, *last = NULL;
+
+	len = ps->chunk_size << SECTOR_SHIFT;
+
+	/*
+	 * Allocate the chunk_size block of memory that will hold
+	 * a single metadata area.
+	 */
+	ps->area = vmalloc(len);
+	if (!ps->area)
+		return r;
+
+	nr_pages = sectors_to_pages(ps->chunk_size);
+
+	/*
+	 * We lock the pages for ps->area into memory since
+	 * they'll be doing a lot of io.  We also chain them
+	 * together ready for dm-io.
+	 */
+	for (i = 0; i < nr_pages; i++) {
+		page = vmalloc_to_page(ps->area + (i * PAGE_SIZE));
+		LockPage(page);
+		if (last)
+			last->list.next = &page->list;
+		last = page;
+	}
+
+	return 0;
+}
+
+static void free_area(struct pstore *ps)
+{
+	size_t i, nr_pages;
+	struct page *page;
+
+	nr_pages = sectors_to_pages(ps->chunk_size);
+	for (i = 0; i < nr_pages; i++) {
+		page = vmalloc_to_page(ps->area + (i * PAGE_SIZE));
+		page->list.next = NULL;
+		UnlockPage(page);
+	}
+
+	vfree(ps->area);
+}
+
+/*
+ * Read or write a chunk aligned and sized block of data from a device.
+ */
+static int chunk_io(struct pstore *ps, uint32_t chunk, int rw)
+{
+	struct io_region where;
+	unsigned int bits;
+
+	where.dev = ps->snap->cow->dev;
+	where.sector = ps->chunk_size * chunk;
+	where.count = ps->chunk_size;
+
+	return dm_io_sync(1, &where, rw, vmalloc_to_page(ps->area), 0, &bits);
+}
+
+/*
+ * Read or write a metadata area.  Remembering to skip the first
+ * chunk which holds the header.
+ */
+static int area_io(struct pstore *ps, uint32_t area, int rw)
+{
+	int r;
+	uint32_t chunk;
+
+	/* convert a metadata area index to a chunk index */
+	chunk = 1 + ((ps->exceptions_per_area + 1) * area);
+
+	r = chunk_io(ps, chunk, rw);
+	if (r)
+		return r;
+
+	ps->current_area = area;
+	return 0;
+}
+
+static int zero_area(struct pstore *ps, uint32_t area)
+{
+	memset(ps->area, 0, ps->chunk_size << SECTOR_SHIFT);
+	return area_io(ps, area, WRITE);
+}
+
+static int read_header(struct pstore *ps, int *new_snapshot)
+{
+	int r;
+	struct disk_header *dh;
+
+	r = chunk_io(ps, 0, READ);
+	if (r)
+		return r;
+
+	dh = (struct disk_header *) ps->area;
+
+	if (le32_to_cpu(dh->magic) == 0) {
+		*new_snapshot = 1;
+
+	} else if (le32_to_cpu(dh->magic) == SNAP_MAGIC) {
+		*new_snapshot = 0;
+		ps->valid = le32_to_cpu(dh->valid);
+		ps->version = le32_to_cpu(dh->version);
+		ps->chunk_size = le32_to_cpu(dh->chunk_size);
+
+	} else {
+		DMWARN("Invalid/corrupt snapshot");
+		r = -ENXIO;
+	}
+
+	return r;
+}
+
+static int write_header(struct pstore *ps)
+{
+	struct disk_header *dh;
+
+	memset(ps->area, 0, ps->chunk_size << SECTOR_SHIFT);
+
+	dh = (struct disk_header *) ps->area;
+	dh->magic = cpu_to_le32(SNAP_MAGIC);
+	dh->valid = cpu_to_le32(ps->valid);
+	dh->version = cpu_to_le32(ps->version);
+	dh->chunk_size = cpu_to_le32(ps->chunk_size);
+
+	return chunk_io(ps, 0, WRITE);
+}
+
+/*
+ * Access functions for the disk exceptions, these do the endian conversions.
+ */
+static struct disk_exception *get_exception(struct pstore *ps, uint32_t index)
+{
+	if (index >= ps->exceptions_per_area)
+		return NULL;
+
+	return ((struct disk_exception *) ps->area) + index;
+}
+
+static int read_exception(struct pstore *ps,
+			  uint32_t index, struct disk_exception *result)
+{
+	struct disk_exception *e;
+
+	e = get_exception(ps, index);
+	if (!e)
+		return -EINVAL;
+
+	/* copy it */
+	result->old_chunk = le64_to_cpu(e->old_chunk);
+	result->new_chunk = le64_to_cpu(e->new_chunk);
+
+	return 0;
+}
+
+static int write_exception(struct pstore *ps,
+			   uint32_t index, struct disk_exception *de)
+{
+	struct disk_exception *e;
+
+	e = get_exception(ps, index);
+	if (!e)
+		return -EINVAL;
+
+	/* copy it */
+	e->old_chunk = cpu_to_le64(de->old_chunk);
+	e->new_chunk = cpu_to_le64(de->new_chunk);
+
+	return 0;
+}
+
+/*
+ * Registers the exceptions that are present in the current area.
+ * 'full' is filled in to indicate if the area has been
+ * filled.
+ */
+static int insert_exceptions(struct pstore *ps, int *full)
+{
+	int r;
+	unsigned int i;
+	struct disk_exception de;
+
+	/* presume the area is full */
+	*full = 1;
+
+	for (i = 0; i < ps->exceptions_per_area; i++) {
+		r = read_exception(ps, i, &de);
+
+		if (r)
+			return r;
+
+		/*
+		 * If the new_chunk is pointing at the start of
+		 * the COW device, where the first metadata area
+		 * is we know that we've hit the end of the
+		 * exceptions.  Therefore the area is not full.
+		 */
+		if (de.new_chunk == 0LL) {
+			ps->current_committed = i;
+			*full = 0;
+			break;
+		}
+
+		/*
+		 * Keep track of the start of the free chunks.
+		 */
+		if (ps->next_free <= de.new_chunk)
+			ps->next_free = de.new_chunk + 1;
+
+		/*
+		 * Otherwise we add the exception to the snapshot.
+		 */
+		r = dm_add_exception(ps->snap, de.old_chunk, de.new_chunk);
+		if (r)
+			return r;
+	}
+
+	return 0;
+}
+
+static int read_exceptions(struct pstore *ps)
+{
+	uint32_t area;
+	int r, full = 1;
+
+	/*
+	 * Keeping reading chunks and inserting exceptions until
+	 * we find a partially full area.
+	 */
+	for (area = 0; full; area++) {
+		r = area_io(ps, area, READ);
+		if (r)
+			return r;
+
+		r = insert_exceptions(ps, &full);
+		if (r)
+			return r;
+	}
+
+	return 0;
+}
+
+static inline struct pstore *get_info(struct exception_store *store)
+{
+	return (struct pstore *) store->context;
+}
+
+static void persistent_fraction_full(struct exception_store *store,
+				     sector_t *numerator, sector_t *denominator)
+{
+	*numerator = get_info(store)->next_free * store->snap->chunk_size;
+	*denominator = get_dev_size(store->snap->cow->dev);
+}
+
+static void persistent_destroy(struct exception_store *store)
+{
+	struct pstore *ps = get_info(store);
+
+	dm_io_put(sectors_to_pages(ps->chunk_size));
+	vfree(ps->callbacks);
+	free_area(ps);
+	kfree(ps);
+}
+
+static int persistent_read_metadata(struct exception_store *store)
+{
+	int r, new_snapshot;
+	struct pstore *ps = get_info(store);
+
+	/*
+	 * Read the snapshot header.
+	 */
+	r = read_header(ps, &new_snapshot);
+	if (r)
+		return r;
+
+	/*
+	 * Do we need to setup a new snapshot ?
+	 */
+	if (new_snapshot) {
+		r = write_header(ps);
+		if (r) {
+			DMWARN("write_header failed");
+			return r;
+		}
+
+		r = zero_area(ps, 0);
+		if (r) {
+			DMWARN("zero_area(0) failed");
+			return r;
+		}
+
+	} else {
+		/*
+		 * Sanity checks.
+		 */
+		if (!ps->valid) {
+			DMWARN("snapshot is marked invalid");
+			return -EINVAL;
+		}
+
+		if (ps->version != SNAPSHOT_DISK_VERSION) {
+			DMWARN("unable to handle snapshot disk version %d",
+			       ps->version);
+			return -EINVAL;
+		}
+
+		/*
+		 * Read the metadata.
+		 */
+		r = read_exceptions(ps);
+		if (r)
+			return r;
+	}
+
+	return 0;
+}
+
+static int persistent_prepare(struct exception_store *store,
+			      struct exception *e)
+{
+	struct pstore *ps = get_info(store);
+	uint32_t stride;
+	sector_t size = get_dev_size(store->snap->cow->dev);
+
+	/* Is there enough room ? */
+	if (size < ((ps->next_free + 1) * store->snap->chunk_size))
+		return -ENOSPC;
+
+	e->new_chunk = ps->next_free;
+
+	/*
+	 * Move onto the next free pending, making sure to take
+	 * into account the location of the metadata chunks.
+	 */
+	stride = (ps->exceptions_per_area + 1);
+	if ((++ps->next_free % stride) == 1)
+		ps->next_free++;
+
+	atomic_inc(&ps->pending_count);
+	return 0;
+}
+
+static void persistent_commit(struct exception_store *store,
+			      struct exception *e,
+			      void (*callback) (void *, int success),
+			      void *callback_context)
+{
+	int r;
+	unsigned int i;
+	struct pstore *ps = get_info(store);
+	struct disk_exception de;
+	struct commit_callback *cb;
+
+	de.old_chunk = e->old_chunk;
+	de.new_chunk = e->new_chunk;
+	write_exception(ps, ps->current_committed++, &de);
+
+	/*
+	 * Add the callback to the back of the array.  This code
+	 * is the only place where the callback array is
+	 * manipulated, and we know that it will never be called
+	 * multiple times concurrently.
+	 */
+	cb = ps->callbacks + ps->callback_count++;
+	cb->callback = callback;
+	cb->context = callback_context;
+
+	/*
+	 * If there are no more exceptions in flight, or we have
+	 * filled this metadata area we commit the exceptions to
+	 * disk.
+	 */
+	if (atomic_dec_and_test(&ps->pending_count) ||
+	    (ps->current_committed == ps->exceptions_per_area)) {
+		r = area_io(ps, ps->current_area, WRITE);
+		if (r)
+			ps->valid = 0;
+
+		for (i = 0; i < ps->callback_count; i++) {
+			cb = ps->callbacks + i;
+			cb->callback(cb->context, r == 0 ? 1 : 0);
+		}
+
+		ps->callback_count = 0;
+	}
+
+	/*
+	 * Have we completely filled the current area ?
+	 */
+	if (ps->current_committed == ps->exceptions_per_area) {
+		ps->current_committed = 0;
+		r = zero_area(ps, ps->current_area + 1);
+		if (r)
+			ps->valid = 0;
+	}
+}
+
+static void persistent_drop(struct exception_store *store)
+{
+	struct pstore *ps = get_info(store);
+
+	ps->valid = 0;
+	if (write_header(ps))
+		DMWARN("write header failed");
+}
+
+int dm_create_persistent(struct exception_store *store, uint32_t chunk_size)
+{
+	int r;
+	struct pstore *ps;
+
+	r = dm_io_get(sectors_to_pages(chunk_size));
+	if (r)
+		return r;
+
+	/* allocate the pstore */
+	ps = kmalloc(sizeof(*ps), GFP_KERNEL);
+	if (!ps) {
+		r = -ENOMEM;
+		goto bad;
+	}
+
+	ps->snap = store->snap;
+	ps->valid = 1;
+	ps->version = SNAPSHOT_DISK_VERSION;
+	ps->chunk_size = chunk_size;
+	ps->exceptions_per_area = (chunk_size << SECTOR_SHIFT) /
+	    sizeof(struct disk_exception);
+	ps->next_free = 2;	/* skipping the header and first area */
+	ps->current_committed = 0;
+
+	r = alloc_area(ps);
+	if (r)
+		goto bad;
+
+	/*
+	 * Allocate space for all the callbacks.
+	 */
+	ps->callback_count = 0;
+	atomic_set(&ps->pending_count, 0);
+	ps->callbacks = vcalloc(ps->exceptions_per_area,
+				sizeof(*ps->callbacks));
+
+	if (!ps->callbacks) {
+		r = -ENOMEM;
+		goto bad;
+	}
+
+	store->destroy = persistent_destroy;
+	store->read_metadata = persistent_read_metadata;
+	store->prepare_exception = persistent_prepare;
+	store->commit_exception = persistent_commit;
+	store->drop_snapshot = persistent_drop;
+	store->fraction_full = persistent_fraction_full;
+	store->context = ps;
+
+	return 0;
+
+      bad:
+	dm_io_put(sectors_to_pages(chunk_size));
+	if (ps) {
+		if (ps->callbacks)
+			vfree(ps->callbacks);
+
+		kfree(ps);
+	}
+	return r;
+}
+
+/*-----------------------------------------------------------------
+ * Implementation of the store for non-persistent snapshots.
+ *---------------------------------------------------------------*/
+struct transient_c {
+	sector_t next_free;
+};
+
+void transient_destroy(struct exception_store *store)
+{
+	kfree(store->context);
+}
+
+int transient_read_metadata(struct exception_store *store)
+{
+	return 0;
+}
+
+int transient_prepare(struct exception_store *store, struct exception *e)
+{
+	struct transient_c *tc = (struct transient_c *) store->context;
+	sector_t size = get_dev_size(store->snap->cow->dev);
+
+	if (size < (tc->next_free + store->snap->chunk_size))
+		return -1;
+
+	e->new_chunk = sector_to_chunk(store->snap, tc->next_free);
+	tc->next_free += store->snap->chunk_size;
+
+	return 0;
+}
+
+void transient_commit(struct exception_store *store,
+		      struct exception *e,
+		      void (*callback) (void *, int success),
+		      void *callback_context)
+{
+	/* Just succeed */
+	callback(callback_context, 1);
+}
+
+static void transient_fraction_full(struct exception_store *store,
+				    sector_t *numerator, sector_t *denominator)
+{
+	*numerator = ((struct transient_c *) store->context)->next_free;
+	*denominator = get_dev_size(store->snap->cow->dev);
+}
+
+int dm_create_transient(struct exception_store *store,
+			struct dm_snapshot *s, int blocksize)
+{
+	struct transient_c *tc;
+
+	memset(store, 0, sizeof(*store));
+	store->destroy = transient_destroy;
+	store->read_metadata = transient_read_metadata;
+	store->prepare_exception = transient_prepare;
+	store->commit_exception = transient_commit;
+	store->fraction_full = transient_fraction_full;
+	store->snap = s;
+
+	tc = kmalloc(sizeof(struct transient_c), GFP_KERNEL);
+	if (!tc)
+		return -ENOMEM;
+
+	tc->next_free = 0;
+	store->context = tc;
+
+	return 0;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/dm.h linux-2.4.27-leo/drivers/md/dm.h
--- linux-2.4.27/drivers/md/dm.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/md/dm.h	2004-09-20 21:40:18.000000000 +0100
@@ -0,0 +1,175 @@
+/*
+ * Internal header file for device mapper
+ *
+ * Copyright (C) 2001, 2002 Sistina Software
+ *
+ * This file is released under the LGPL.
+ */
+
+#ifndef DM_INTERNAL_H
+#define DM_INTERNAL_H
+
+#include <linux/fs.h>
+#include <linux/device-mapper.h>
+#include <linux/list.h>
+#include <linux/blkdev.h>
+
+#define DM_NAME "device-mapper"
+#define DMWARN(f, x...) printk(KERN_WARNING DM_NAME ": " f "\n" , ## x)
+#define DMERR(f, x...) printk(KERN_ERR DM_NAME ": " f "\n" , ## x)
+#define DMINFO(f, x...) printk(KERN_INFO DM_NAME ": " f "\n" , ## x)
+
+/*
+ * FIXME: I think this should be with the definition of sector_t
+ * in types.h.
+ */
+#ifdef CONFIG_LBD
+#define SECTOR_FORMAT "%Lu"
+#else
+#define SECTOR_FORMAT "%lu"
+#endif
+
+#define SECTOR_SHIFT 9
+#define SECTOR_SIZE (1 << SECTOR_SHIFT)
+
+extern struct block_device_operations dm_blk_dops;
+
+/*
+ * List of devices that a metadevice uses and should open/close.
+ */
+struct dm_dev {
+	struct list_head list;
+
+	atomic_t count;
+	int mode;
+	kdev_t dev;
+	struct block_device *bdev;
+};
+
+struct dm_table;
+struct mapped_device;
+
+/*-----------------------------------------------------------------
+ * Functions for manipulating a struct mapped_device.
+ * Drop the reference with dm_put when you finish with the object.
+ *---------------------------------------------------------------*/
+int dm_create(kdev_t dev, struct mapped_device **md);
+
+/*
+ * Reference counting for md.
+ */
+void dm_get(struct mapped_device *md);
+void dm_put(struct mapped_device *md);
+
+/*
+ * A device can still be used while suspended, but I/O is deferred.
+ */
+int dm_suspend(struct mapped_device *md);
+int dm_resume(struct mapped_device *md);
+
+/*
+ * The device must be suspended before calling this method.
+ */
+int dm_swap_table(struct mapped_device *md, struct dm_table *t);
+
+/*
+ * Drop a reference on the table when you've finished with the
+ * result.
+ */
+struct dm_table *dm_get_table(struct mapped_device *md);
+
+/*
+ * Event functions.
+ */
+uint32_t dm_get_event_nr(struct mapped_device *md);
+int dm_add_wait_queue(struct mapped_device *md, wait_queue_t *wq,
+		      uint32_t event_nr);
+void dm_remove_wait_queue(struct mapped_device *md, wait_queue_t *wq);
+
+/*
+ * Info functions.
+ */
+kdev_t dm_kdev(struct mapped_device *md);
+int dm_suspended(struct mapped_device *md);
+
+/*-----------------------------------------------------------------
+ * Functions for manipulating a table.  Tables are also reference
+ * counted.
+ *---------------------------------------------------------------*/
+int dm_table_create(struct dm_table **result, int mode, unsigned num_targets);
+
+void dm_table_get(struct dm_table *t);
+void dm_table_put(struct dm_table *t);
+
+int dm_table_add_target(struct dm_table *t, const char *type,
+			sector_t start,	sector_t len, char *params);
+int dm_table_complete(struct dm_table *t);
+void dm_table_event_callback(struct dm_table *t,
+			     void (*fn)(void *), void *context);
+void dm_table_event(struct dm_table *t);
+sector_t dm_table_get_size(struct dm_table *t);
+struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index);
+struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector);
+unsigned int dm_table_get_num_targets(struct dm_table *t);
+struct list_head *dm_table_get_devices(struct dm_table *t);
+int dm_table_get_mode(struct dm_table *t);
+void dm_table_suspend_targets(struct dm_table *t);
+void dm_table_resume_targets(struct dm_table *t);
+
+/*-----------------------------------------------------------------
+ * A registry of target types.
+ *---------------------------------------------------------------*/
+int dm_target_init(void);
+void dm_target_exit(void);
+struct target_type *dm_get_target_type(const char *name);
+void dm_put_target_type(struct target_type *t);
+
+
+/*-----------------------------------------------------------------
+ * Useful inlines.
+ *---------------------------------------------------------------*/
+static inline int array_too_big(unsigned long fixed, unsigned long obj,
+				unsigned long num)
+{
+	return (num > (ULONG_MAX - fixed) / obj);
+}
+
+/*
+ * ceiling(n / size) * size
+ */
+static inline unsigned long dm_round_up(unsigned long n, unsigned long size)
+{
+	unsigned long r = n % size;
+	return n + (r ? (size - r) : 0);
+}
+
+/*
+ * Ceiling(n / size)
+ */
+static inline unsigned long dm_div_up(unsigned long n, unsigned long size)
+{
+	return dm_round_up(n, size) / size;
+}
+
+const char *dm_kdevname(kdev_t dev);
+
+/*
+ * The device-mapper can be driven through one of two interfaces;
+ * ioctl or filesystem, depending which patch you have applied.
+ */
+int dm_interface_init(void);
+void dm_interface_exit(void);
+
+/*
+ * Targets for linear and striped mappings
+ */
+int dm_linear_init(void);
+void dm_linear_exit(void);
+
+int dm_stripe_init(void);
+void dm_stripe_exit(void);
+
+int dm_snapshot_init(void);
+void dm_snapshot_exit(void);
+
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/dm-io.c linux-2.4.27-leo/drivers/md/dm-io.c
--- linux-2.4.27/drivers/md/dm-io.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/md/dm-io.c	2004-09-17 03:19:08.000000000 +0100
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2003 Sistina Software
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-io.h"
+
+#include <linux/mempool.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/bitops.h>
+
+/* FIXME: can we shrink this ? */
+struct io_context {
+	int rw;
+	unsigned int error;
+	atomic_t count;
+	struct task_struct *sleeper;
+	io_notify_fn callback;
+	void *context;
+};
+
+/*
+ * We maintain a pool of buffer heads for dispatching the io.
+ */
+static unsigned int _num_bhs;
+static mempool_t *_buffer_pool;
+
+/*
+ * io contexts are only dynamically allocated for asynchronous
+ * io.  Since async io is likely to be the majority of io we'll
+ * have the same number of io contexts as buffer heads ! (FIXME:
+ * must reduce this).
+ */
+mempool_t *_io_pool;
+
+static void *alloc_bh(int gfp_mask, void *pool_data)
+{
+	struct buffer_head *bh;
+
+	bh = kmem_cache_alloc(bh_cachep, gfp_mask);
+	if (bh) {
+		bh->b_reqnext = NULL;
+		init_waitqueue_head(&bh->b_wait);
+		INIT_LIST_HEAD(&bh->b_inode_buffers);
+	}
+
+	return bh;
+}
+
+static void *alloc_io(int gfp_mask, void *pool_data)
+{
+	return kmalloc(sizeof(struct io_context), gfp_mask);
+}
+
+static void free_io(void *element, void *pool_data)
+{
+	kfree(element);
+}
+
+static unsigned int pages_to_buffers(unsigned int pages)
+{
+	return 4 * pages;	/* too many ? */
+}
+
+static int resize_pool(unsigned int new_bhs)
+{
+	int r = 0;
+
+	if (_buffer_pool) {
+		if (new_bhs == 0) {
+			/* free off the pools */
+			mempool_destroy(_buffer_pool);
+			mempool_destroy(_io_pool);
+			_buffer_pool = _io_pool = NULL;
+		} else {
+			/* resize the pools */
+			r = mempool_resize(_buffer_pool, new_bhs, GFP_KERNEL);
+			if (!r)
+				r = mempool_resize(_io_pool,
+						   new_bhs, GFP_KERNEL);
+		}
+	} else {
+		/* create new pools */
+		_buffer_pool = mempool_create(new_bhs, alloc_bh,
+					      mempool_free_slab, bh_cachep);
+		if (!_buffer_pool)
+			r = -ENOMEM;
+
+		_io_pool = mempool_create(new_bhs, alloc_io, free_io, NULL);
+		if (!_io_pool) {
+			mempool_destroy(_buffer_pool);
+			_buffer_pool = NULL;
+			r = -ENOMEM;
+		}
+	}
+
+	if (!r)
+		_num_bhs = new_bhs;
+
+	return r;
+}
+
+int dm_io_get(unsigned int num_pages)
+{
+	return resize_pool(_num_bhs + pages_to_buffers(num_pages));
+}
+
+void dm_io_put(unsigned int num_pages)
+{
+	resize_pool(_num_bhs - pages_to_buffers(num_pages));
+}
+
+/*-----------------------------------------------------------------
+ * We need to keep track of which region a buffer is doing io
+ * for.  In order to save a memory allocation we store this in an
+ * unused field of the buffer head, and provide these access
+ * functions.
+ *
+ * FIXME: add compile time check that an unsigned int can fit
+ * into a pointer.
+ *
+ *---------------------------------------------------------------*/
+static inline void bh_set_region(struct buffer_head *bh, unsigned int region)
+{
+	bh->b_journal_head = (void *) region;
+}
+
+static inline int bh_get_region(struct buffer_head *bh)
+{
+	return (unsigned int) bh->b_journal_head;
+}
+
+/*-----------------------------------------------------------------
+ * We need an io object to keep track of the number of bhs that
+ * have been dispatched for a particular io.
+ *---------------------------------------------------------------*/
+static void dec_count(struct io_context *io, unsigned int region, int error)
+{
+	if (error)
+		set_bit(region, &io->error);
+
+	if (atomic_dec_and_test(&io->count)) {
+		if (io->sleeper)
+			wake_up_process(io->sleeper);
+
+		else {
+			int r = io->error;
+			io_notify_fn fn = io->callback;
+			void *context = io->context;
+
+			mempool_free(io, _io_pool);
+			fn(r, context);
+		}
+	}
+}
+
+static void endio(struct buffer_head *bh, int uptodate)
+{
+	struct io_context *io = (struct io_context *) bh->b_private;
+
+	if (!uptodate && io->rw != WRITE) {
+		/*
+		 * We need to zero this region, otherwise people
+		 * like kcopyd may write the arbitrary contents
+		 * of the page.
+		 */
+		memset(bh->b_data, 0, bh->b_size);
+	}
+
+	dec_count((struct io_context *) bh->b_private,
+		  bh_get_region(bh), !uptodate);
+	mempool_free(bh, _buffer_pool);
+}
+
+/*
+ * Primitives for alignment calculations.
+ */
+int fls(unsigned n)
+{
+	return generic_fls32(n);
+}
+
+static inline int log2_floor(unsigned n)
+{
+	return ffs(n) - 1;
+}
+
+static inline int log2_align(unsigned n)
+{
+	return fls(n) - 1;
+}
+
+/*
+ * Returns the next block for io.
+ */
+static int do_page(kdev_t dev, sector_t *block, sector_t end_block,
+		   unsigned int block_size,
+		   struct page *p, unsigned int offset,
+		   unsigned int region, struct io_context *io)
+{
+	struct buffer_head *bh;
+	sector_t b = *block;
+	sector_t blocks_per_page = PAGE_SIZE / block_size;
+	unsigned int this_size; /* holds the size of the current io */
+	sector_t len;
+
+	if (!blocks_per_page) {
+		DMERR("dm-io: PAGE_SIZE (%lu) < block_size (%u) unsupported",
+		      PAGE_SIZE, block_size);
+		return 0;
+	}
+
+	while ((offset < PAGE_SIZE) && (b != end_block)) {
+		bh = mempool_alloc(_buffer_pool, GFP_NOIO);
+		init_buffer(bh, endio, io);
+		bh_set_region(bh, region);
+
+		/*
+		 * Block size must be a power of 2 and aligned
+		 * correctly.
+		 */
+
+		len = min(end_block - b, blocks_per_page);
+		len = min(len, blocks_per_page - offset / block_size);
+
+		if (!len) {
+			DMERR("dm-io: Invalid offset/block_size (%u/%u).",
+			      offset, block_size);
+			return 0;
+		}
+
+		this_size = 1 << log2_align(len);
+		if (b)
+			this_size = min(this_size,
+					(unsigned) 1 << log2_floor(b));
+
+		/*
+		 * Add in the job offset.
+		 */
+		bh->b_blocknr = (b / this_size);
+		bh->b_size = block_size * this_size;
+		set_bh_page(bh, p, offset);
+		bh->b_this_page = bh;
+
+		bh->b_dev = dev;
+		atomic_set(&bh->b_count, 1);
+
+		bh->b_state = ((1 << BH_Uptodate) | (1 << BH_Mapped) |
+			       (1 << BH_Lock));
+
+		if (io->rw == WRITE)
+			clear_bit(BH_Dirty, &bh->b_state);
+
+		atomic_inc(&io->count);
+		submit_bh(io->rw, bh);
+
+		b += this_size;
+		offset += block_size * this_size;
+	}
+
+	*block = b;
+	return (b == end_block);
+}
+
+static void do_region(unsigned int region, struct io_region *where,
+		      struct page *page, unsigned int offset,
+		      struct io_context *io)
+{
+	unsigned int block_size = get_hardsect_size(where->dev);
+	unsigned int sblock_size = block_size >> 9;
+	sector_t block = where->sector / sblock_size;
+	sector_t end_block = (where->sector + where->count) / sblock_size;
+
+	while (1) {
+		if (do_page(where->dev, &block, end_block, block_size,
+			    page, offset, region, io))
+			break;
+
+		offset = 0;	/* only offset the first page */
+
+		page = list_entry(page->list.next, struct page, list);
+	}
+}
+
+static void dispatch_io(unsigned int num_regions, struct io_region *where,
+			struct page *pages, unsigned int offset,
+			struct io_context *io)
+{
+	int i;
+
+	for (i = 0; i < num_regions; i++)
+		if (where[i].count)
+			do_region(i, where + i, pages, offset, io);
+
+	/*
+	 * Drop the extra refence that we were holding to avoid
+	 * the io being completed too early.
+	 */
+	dec_count(io, 0, 0);
+}
+
+/*
+ * Synchronous io
+ */
+int dm_io_sync(unsigned int num_regions, struct io_region *where,
+	       int rw, struct page *pages, unsigned int offset,
+	       unsigned int *error_bits)
+{
+	struct io_context io;
+
+	BUG_ON(num_regions > 1 && rw != WRITE);
+
+	io.rw = rw;
+	io.error = 0;
+	atomic_set(&io.count, 1); /* see dispatch_io() */
+	io.sleeper = current;
+
+	dispatch_io(num_regions, where, pages, offset, &io);
+	run_task_queue(&tq_disk);
+
+	while (1) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+
+		if (!atomic_read(&io.count))
+			break;
+
+		schedule();
+	}
+	set_current_state(TASK_RUNNING);
+
+	*error_bits = io.error;
+	return io.error ? -EIO : 0;
+}
+
+/*
+ * Asynchronous io
+ */
+int dm_io_async(unsigned int num_regions, struct io_region *where, int rw,
+		struct page *pages, unsigned int offset,
+		io_notify_fn fn, void *context)
+{
+	struct io_context *io = mempool_alloc(_io_pool, GFP_NOIO);
+
+	io->rw = rw;
+	io->error = 0;
+	atomic_set(&io->count, 1); /* see dispatch_io() */
+	io->sleeper = NULL;
+	io->callback = fn;
+	io->context = context;
+
+	dispatch_io(num_regions, where, pages, offset, io);
+	return 0;
+}
+
+EXPORT_SYMBOL(dm_io_get);
+EXPORT_SYMBOL(dm_io_put);
+EXPORT_SYMBOL(dm_io_sync);
+EXPORT_SYMBOL(dm_io_async);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/dm-ioctl.c linux-2.4.27-leo/drivers/md/dm-ioctl.c
--- linux-2.4.27/drivers/md/dm-ioctl.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/md/dm-ioctl.c	2004-09-17 03:19:08.000000000 +0100
@@ -0,0 +1,1284 @@
+/*
+ * Copyright (C) 2001, 2002 Sistina Software (UK) Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm.h"
+
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/miscdevice.h>
+#include <linux/dm-ioctl.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/blk.h>
+#include <linux/slab.h>
+
+#include <asm/uaccess.h>
+
+#define DM_DRIVER_EMAIL "dm@uk.sistina.com"
+
+/*-----------------------------------------------------------------
+ * The ioctl interface needs to be able to look up devices by
+ * name or uuid.
+ *---------------------------------------------------------------*/
+struct hash_cell {
+	struct list_head name_list;
+	struct list_head uuid_list;
+
+	char *name;
+	char *uuid;
+	struct mapped_device *md;
+	struct dm_table *new_map;
+
+	/* I hate devfs */
+	devfs_handle_t devfs_entry;
+};
+
+#define NUM_BUCKETS 64
+#define MASK_BUCKETS (NUM_BUCKETS - 1)
+static struct list_head _name_buckets[NUM_BUCKETS];
+static struct list_head _uuid_buckets[NUM_BUCKETS];
+
+static devfs_handle_t _dev_dir;
+void dm_hash_remove_all(void);
+
+/*
+ * Guards access to both hash tables.
+ */
+static DECLARE_RWSEM(_hash_lock);
+
+static void init_buckets(struct list_head *buckets)
+{
+	unsigned int i;
+
+	for (i = 0; i < NUM_BUCKETS; i++)
+		INIT_LIST_HEAD(buckets + i);
+}
+
+int dm_hash_init(void)
+{
+	init_buckets(_name_buckets);
+	init_buckets(_uuid_buckets);
+	_dev_dir = devfs_mk_dir(0, DM_DIR, NULL);
+	return 0;
+}
+
+void dm_hash_exit(void)
+{
+	dm_hash_remove_all();
+	devfs_unregister(_dev_dir);
+}
+
+/*-----------------------------------------------------------------
+ * Hash function:
+ * We're not really concerned with the str hash function being
+ * fast since it's only used by the ioctl interface.
+ *---------------------------------------------------------------*/
+static unsigned int hash_str(const char *str)
+{
+	const unsigned int hash_mult = 2654435387U;
+	unsigned int h = 0;
+
+	while (*str)
+		h = (h + (unsigned int) *str++) * hash_mult;
+
+	return h & MASK_BUCKETS;
+}
+
+/*-----------------------------------------------------------------
+ * Code for looking up a device by name
+ *---------------------------------------------------------------*/
+static struct hash_cell *__get_name_cell(const char *str)
+{
+	struct list_head *tmp;
+	struct hash_cell *hc;
+	unsigned int h = hash_str(str);
+
+	list_for_each (tmp, _name_buckets + h) {
+		hc = list_entry(tmp, struct hash_cell, name_list);
+		if (!strcmp(hc->name, str))
+			return hc;
+	}
+
+	return NULL;
+}
+
+static struct hash_cell *__get_uuid_cell(const char *str)
+{
+	struct list_head *tmp;
+	struct hash_cell *hc;
+	unsigned int h = hash_str(str);
+
+	list_for_each (tmp, _uuid_buckets + h) {
+		hc = list_entry(tmp, struct hash_cell, uuid_list);
+		if (!strcmp(hc->uuid, str))
+			return hc;
+	}
+
+	return NULL;
+}
+
+/*-----------------------------------------------------------------
+ * Inserting, removing and renaming a device.
+ *---------------------------------------------------------------*/
+static inline char *kstrdup(const char *str)
+{
+	char *r = kmalloc(strlen(str) + 1, GFP_KERNEL);
+	if (r)
+		strcpy(r, str);
+	return r;
+}
+
+static struct hash_cell *alloc_cell(const char *name, const char *uuid,
+				    struct mapped_device *md)
+{
+	struct hash_cell *hc;
+
+	hc = kmalloc(sizeof(*hc), GFP_KERNEL);
+	if (!hc)
+		return NULL;
+
+	hc->name = kstrdup(name);
+	if (!hc->name) {
+		kfree(hc);
+		return NULL;
+	}
+
+	if (!uuid)
+		hc->uuid = NULL;
+
+	else {
+		hc->uuid = kstrdup(uuid);
+		if (!hc->uuid) {
+			kfree(hc->name);
+			kfree(hc);
+			return NULL;
+		}
+	}
+
+	INIT_LIST_HEAD(&hc->name_list);
+	INIT_LIST_HEAD(&hc->uuid_list);
+	hc->md = md;
+	hc->new_map = NULL;
+	return hc;
+}
+
+static void free_cell(struct hash_cell *hc)
+{
+	if (hc) {
+		kfree(hc->name);
+		kfree(hc->uuid);
+		kfree(hc);
+	}
+}
+
+/*
+ * devfs stuff.
+ */
+static int register_with_devfs(struct hash_cell *hc)
+{
+	kdev_t dev = dm_kdev(hc->md);
+
+	hc->devfs_entry =
+	    devfs_register(_dev_dir, hc->name, DEVFS_FL_CURRENT_OWNER,
+			   major(dev), minor(dev),
+			   S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP,
+			   &dm_blk_dops, NULL);
+
+	return 0;
+}
+
+static int unregister_with_devfs(struct hash_cell *hc)
+{
+	devfs_unregister(hc->devfs_entry);
+	return 0;
+}
+
+/*
+ * The kdev_t and uuid of a device can never change once it is
+ * initially inserted.
+ */
+int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md)
+{
+	struct hash_cell *cell;
+
+	/*
+	 * Allocate the new cells.
+	 */
+	cell = alloc_cell(name, uuid, md);
+	if (!cell)
+		return -ENOMEM;
+
+	/*
+	 * Insert the cell into both hash tables.
+	 */
+	down_write(&_hash_lock);
+	if (__get_name_cell(name))
+		goto bad;
+
+	list_add(&cell->name_list, _name_buckets + hash_str(name));
+
+	if (uuid) {
+		if (__get_uuid_cell(uuid)) {
+			list_del(&cell->name_list);
+			goto bad;
+		}
+		list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid));
+	}
+	register_with_devfs(cell);
+	dm_get(md);
+	up_write(&_hash_lock);
+
+	return 0;
+
+      bad:
+	up_write(&_hash_lock);
+	free_cell(cell);
+	return -EBUSY;
+}
+
+void __hash_remove(struct hash_cell *hc)
+{
+	/* remove from the dev hash */
+	list_del(&hc->uuid_list);
+	list_del(&hc->name_list);
+	unregister_with_devfs(hc);
+	dm_put(hc->md);
+	if (hc->new_map)
+		dm_table_put(hc->new_map);
+	free_cell(hc);
+}
+
+void dm_hash_remove_all(void)
+{
+	int i;
+	struct hash_cell *hc;
+	struct list_head *tmp, *n;
+
+	down_write(&_hash_lock);
+	for (i = 0; i < NUM_BUCKETS; i++) {
+		list_for_each_safe (tmp, n, _name_buckets + i) {
+			hc = list_entry(tmp, struct hash_cell, name_list);
+			__hash_remove(hc);
+		}
+	}
+	up_write(&_hash_lock);
+}
+
+int dm_hash_rename(const char *old, const char *new)
+{
+	char *new_name, *old_name;
+	struct hash_cell *hc;
+
+	/*
+	 * duplicate new.
+	 */
+	new_name = kstrdup(new);
+	if (!new_name)
+		return -ENOMEM;
+
+	down_write(&_hash_lock);
+
+	/*
+	 * Is new free ?
+	 */
+	hc = __get_name_cell(new);
+	if (hc) {
+		DMWARN("asked to rename to an already existing name %s -> %s",
+		       old, new);
+		up_write(&_hash_lock);
+		kfree(new_name);
+		return -EBUSY;
+	}
+
+	/*
+	 * Is there such a device as 'old' ?
+	 */
+	hc = __get_name_cell(old);
+	if (!hc) {
+		DMWARN("asked to rename a non existent device %s -> %s",
+		       old, new);
+		up_write(&_hash_lock);
+		kfree(new_name);
+		return -ENXIO;
+	}
+
+	/*
+	 * rename and move the name cell.
+	 */
+	list_del(&hc->name_list);
+	old_name = hc->name;
+	hc->name = new_name;
+	list_add(&hc->name_list, _name_buckets + hash_str(new_name));
+
+	/* rename the device node in devfs */
+	unregister_with_devfs(hc);
+	register_with_devfs(hc);
+
+	up_write(&_hash_lock);
+	kfree(old_name);
+	return 0;
+}
+
+/*-----------------------------------------------------------------
+ * Implementation of the ioctl commands
+ *---------------------------------------------------------------*/
+/*
+ * All the ioctl commands get dispatched to functions with this
+ * prototype.
+ */
+typedef int (*ioctl_fn)(struct dm_ioctl *param, size_t param_size);
+
+static int remove_all(struct dm_ioctl *param, size_t param_size)
+{
+	dm_hash_remove_all();
+	param->data_size = 0;
+	return 0;
+}
+
+/*
+ * Round up the ptr to an 8-byte boundary.
+ */
+#define ALIGN_MASK 7
+static inline void *align_ptr(void *ptr)
+{
+	return (void *) (((size_t) (ptr + ALIGN_MASK)) & ~ALIGN_MASK);
+}
+
+/*
+ * Retrieves the data payload buffer from an already allocated
+ * struct dm_ioctl.
+ */
+static void *get_result_buffer(struct dm_ioctl *param, size_t param_size,
+			       size_t *len)
+{
+	param->data_start = align_ptr(param + 1) - (void *) param;
+
+	if (param->data_start < param_size)
+		*len = param_size - param->data_start;
+	else
+		*len = 0;
+
+	return ((void *) param) + param->data_start;
+}
+
+static int list_devices(struct dm_ioctl *param, size_t param_size)
+{
+	unsigned int i;
+	struct hash_cell *hc;
+	size_t len, needed = 0;
+	struct dm_name_list *nl, *old_nl = NULL;
+
+	down_write(&_hash_lock);
+
+	/*
+	 * Loop through all the devices working out how much
+	 * space we need.
+	 */
+	for (i = 0; i < NUM_BUCKETS; i++) {
+		list_for_each_entry (hc, _name_buckets + i, name_list) {
+			needed += sizeof(struct dm_name_list);
+			needed += strlen(hc->name);
+			needed += ALIGN_MASK;
+		}
+	}
+
+	/*
+	 * Grab our output buffer.
+	 */
+	nl = get_result_buffer(param, param_size, &len);
+	if (len < needed) {
+		param->flags |= DM_BUFFER_FULL_FLAG;
+		goto out;
+	}
+	param->data_size = param->data_start + needed;
+
+	nl->dev = 0;	/* Flags no data */
+
+	/*
+	 * Now loop through filling out the names.
+	 */
+	for (i = 0; i < NUM_BUCKETS; i++) {
+		list_for_each_entry (hc, _name_buckets + i, name_list) {
+			if (old_nl)
+				old_nl->next = (uint32_t) ((void *) nl -
+							   (void *) old_nl);
+
+			nl->dev = dm_kdev(hc->md);
+			nl->next = 0;
+			strcpy(nl->name, hc->name);
+
+			old_nl = nl;
+			nl = align_ptr(((void *) ++nl) + strlen(hc->name) + 1);
+		}
+	}
+
+ out:
+	up_write(&_hash_lock);
+	return 0;
+}
+
+static int check_name(const char *name)
+{
+	if (strchr(name, '/')) {
+		DMWARN("invalid device name");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * Fills in a dm_ioctl structure, ready for sending back to
+ * userland.
+ */
+static int __dev_status(struct mapped_device *md, struct dm_ioctl *param)
+{
+	kdev_t dev = dm_kdev(md);
+	struct dm_table *table;
+	struct block_device *bdev;
+
+	param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG |
+			  DM_ACTIVE_PRESENT_FLAG);
+
+	if (dm_suspended(md))
+		param->flags |= DM_SUSPEND_FLAG;
+
+	param->dev = kdev_t_to_nr(dev);
+
+	if (is_read_only(dev))
+		param->flags |= DM_READONLY_FLAG;
+
+	param->event_nr = dm_get_event_nr(md);
+
+	table = dm_get_table(md);
+	if (table) {
+		param->flags |= DM_ACTIVE_PRESENT_FLAG;
+		param->target_count = dm_table_get_num_targets(table);
+		dm_table_put(table);
+	} else
+		param->target_count = 0;
+
+	bdev = bdget(param->dev);
+	if (!bdev)
+		return -ENXIO;
+	param->open_count = bdev->bd_openers;
+	bdput(bdev);
+
+	return 0;
+}
+
+static int dev_create(struct dm_ioctl *param, size_t param_size)
+{
+	int r;
+	kdev_t dev = 0;
+	struct mapped_device *md;
+
+	r = check_name(param->name);
+	if (r)
+		return r;
+
+	if (param->flags & DM_PERSISTENT_DEV_FLAG)
+		dev = to_kdev_t(param->dev);
+
+	r = dm_create(dev, &md);
+	if (r)
+		return r;
+
+	r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md);
+	if (r) {
+		dm_put(md);
+		return r;
+	}
+
+	param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
+
+	r = __dev_status(md, param);
+	dm_put(md);
+
+	return r;
+}
+
+/*
+ * Always use UUID for lookups if it's present, otherwise use name.
+ */
+static inline struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param)
+{
+	return *param->uuid ?
+	    __get_uuid_cell(param->uuid) : __get_name_cell(param->name);
+}
+
+static inline struct mapped_device *find_device(struct dm_ioctl *param)
+{
+	struct hash_cell *hc;
+	struct mapped_device *md = NULL;
+
+	down_read(&_hash_lock);
+	hc = __find_device_hash_cell(param);
+	if (hc) {
+		md = hc->md;
+
+		/*
+		 * Sneakily write in both the name and the uuid
+		 * while we have the cell.
+		 */
+		strncpy(param->name, hc->name, sizeof(param->name));
+		if (hc->uuid)
+			strncpy(param->uuid, hc->uuid, sizeof(param->uuid) - 1);
+		else
+			param->uuid[0] = '\0';
+
+		if (hc->new_map)
+			param->flags |= DM_INACTIVE_PRESENT_FLAG;
+		else
+			param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
+
+		dm_get(md);
+	}
+	up_read(&_hash_lock);
+
+	return md;
+}
+
+static int dev_remove(struct dm_ioctl *param, size_t param_size)
+{
+	struct hash_cell *hc;
+
+	down_write(&_hash_lock);
+	hc = __find_device_hash_cell(param);
+
+	if (!hc) {
+		DMWARN("device doesn't appear to be in the dev hash table.");
+		up_write(&_hash_lock);
+		return -ENXIO;
+	}
+
+	__hash_remove(hc);
+	up_write(&_hash_lock);
+	param->data_size = 0;
+	return 0;
+}
+
+/*
+ * Check a string doesn't overrun the chunk of
+ * memory we copied from userland.
+ */
+static int invalid_str(char *str, void *end)
+{
+	while ((void *) str < end)
+		if (!*str++)
+			return 0;
+
+	return -EINVAL;
+}
+
+static int dev_rename(struct dm_ioctl *param, size_t param_size)
+{
+	int r;
+	char *new_name = (char *) param + param->data_start;
+
+	if (new_name < (char *) (param + 1) ||
+	    invalid_str(new_name, (void *) param + param_size)) {
+		DMWARN("Invalid new logical volume name supplied.");
+		return -EINVAL;
+	}
+
+	r = check_name(new_name);
+	if (r)
+		return r;
+
+	param->data_size = 0;
+	return dm_hash_rename(param->name, new_name);
+}
+
+static int do_suspend(struct dm_ioctl *param)
+{
+	int r = 0;
+	struct mapped_device *md;
+
+	md = find_device(param);
+	if (!md)
+		return -ENXIO;
+
+	if (!dm_suspended(md))
+		r = dm_suspend(md);
+
+	if (!r)
+		r = __dev_status(md, param);
+
+	dm_put(md);
+	return r;
+}
+
+static int do_resume(struct dm_ioctl *param)
+{
+	int r = 0;
+	struct hash_cell *hc;
+	struct mapped_device *md;
+	struct dm_table *new_map;
+
+	down_write(&_hash_lock);
+
+	hc = __find_device_hash_cell(param);
+	if (!hc) {
+		DMWARN("device doesn't appear to be in the dev hash table.");
+		up_write(&_hash_lock);
+		return -ENXIO;
+	}
+
+	md = hc->md;
+	dm_get(md);
+
+	new_map = hc->new_map;
+	hc->new_map = NULL;
+	param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
+
+	up_write(&_hash_lock);
+
+	/* Do we need to load a new map ? */
+	if (new_map) {
+		/* Suspend if it isn't already suspended */
+		if (!dm_suspended(md))
+			dm_suspend(md);
+
+		r = dm_swap_table(md, new_map);
+		if (r) {
+			dm_put(md);
+			dm_table_put(new_map);
+			return r;
+		}
+
+		if (dm_table_get_mode(new_map) & FMODE_WRITE)
+			set_device_ro(dm_kdev(md), 0);
+		else
+			set_device_ro(dm_kdev(md), 1);
+
+		dm_table_put(new_map);
+	}
+
+	if (dm_suspended(md))
+		r = dm_resume(md);
+
+	if (!r)
+		r = __dev_status(md, param);
+
+	dm_put(md);
+	return r;
+}
+
+/*
+ * Set or unset the suspension state of a device.
+ * If the device already is in the requested state we just return its status.
+ */
+static int dev_suspend(struct dm_ioctl *param, size_t param_size)
+{
+	if (param->flags & DM_SUSPEND_FLAG)
+		return do_suspend(param);
+
+	return do_resume(param);
+}
+
+/*
+ * Copies device info back to user space, used by
+ * the create and info ioctls.
+ */
+static int dev_status(struct dm_ioctl *param, size_t param_size)
+{
+	int r;
+	struct mapped_device *md;
+
+	md = find_device(param);
+	if (!md)
+		return -ENXIO;
+
+	r = __dev_status(md, param);
+	dm_put(md);
+	return r;
+}
+
+/*
+ * Build up the status struct for each target
+ */
+static void retrieve_status(struct dm_table *table, struct dm_ioctl *param,
+			    size_t param_size)
+{
+	unsigned int i, num_targets;
+	struct dm_target_spec *spec;
+	char *outbuf, *outptr;
+	status_type_t type;
+	size_t remaining, len, used = 0;
+
+	outptr = outbuf = get_result_buffer(param, param_size, &len);
+
+	if (param->flags & DM_STATUS_TABLE_FLAG)
+		type = STATUSTYPE_TABLE;
+	else
+		type = STATUSTYPE_INFO;
+
+	/* Get all the target info */
+	num_targets = dm_table_get_num_targets(table);
+	for (i = 0; i < num_targets; i++) {
+		struct dm_target *ti = dm_table_get_target(table, i);
+
+		remaining = len - (outptr - outbuf);
+		if (remaining < sizeof(struct dm_target_spec)) {
+			param->flags |= DM_BUFFER_FULL_FLAG;
+			break;
+		}
+
+		spec = (struct dm_target_spec *) outptr;
+
+		spec->status = 0;
+		spec->sector_start = ti->begin;
+		spec->length = ti->len;
+		strncpy(spec->target_type, ti->type->name,
+			sizeof(spec->target_type));
+
+		outptr += sizeof(struct dm_target_spec);
+		remaining = len - (outptr - outbuf);
+
+		/* Get the status/table string from the target driver */
+		if (ti->type->status) {
+			if (ti->type->status(ti, type, outptr, remaining)) {
+				param->flags |= DM_BUFFER_FULL_FLAG;
+				break;
+			}
+		} else
+			outptr[0] = '\0';
+
+		outptr += strlen(outptr) + 1;
+		used = param->data_start + (outptr - outbuf);
+
+		align_ptr(outptr);
+		spec->next = outptr - outbuf;
+	}
+
+	if (used)
+		param->data_size = used;
+
+	param->target_count = num_targets;
+}
+
+/*
+ * Wait for a device to report an event
+ */
+static int dev_wait(struct dm_ioctl *param, size_t param_size)
+{
+	int r;
+	struct mapped_device *md;
+	struct dm_table *table;
+	DECLARE_WAITQUEUE(wq, current);
+
+	md = find_device(param);
+	if (!md)
+		return -ENXIO;
+
+	/*
+	 * Wait for a notification event
+	 */
+	set_current_state(TASK_INTERRUPTIBLE);
+	if (!dm_add_wait_queue(md, &wq, param->event_nr)) {
+		schedule();
+		dm_remove_wait_queue(md, &wq);
+	}
+	set_current_state(TASK_RUNNING);
+
+	/*
+	 * The userland program is going to want to know what
+	 * changed to trigger the event, so we may as well tell
+	 * him and save an ioctl.
+	 */
+	r = __dev_status(md, param);
+	if (r)
+		goto out;
+
+	table = dm_get_table(md);
+	if (table) {
+		retrieve_status(table, param, param_size);
+		dm_table_put(table);
+	}
+
+ out:
+	dm_put(md);
+	return r;
+}
+
+static inline int get_mode(struct dm_ioctl *param)
+{
+	int mode = FMODE_READ | FMODE_WRITE;
+
+	if (param->flags & DM_READONLY_FLAG)
+		mode = FMODE_READ;
+
+	return mode;
+}
+
+static int next_target(struct dm_target_spec *last, uint32_t next, void *end,
+		       struct dm_target_spec **spec, char **target_params)
+{
+	*spec = (struct dm_target_spec *) ((unsigned char *) last + next);
+	*target_params = (char *) (*spec + 1);
+
+	if (*spec < (last + 1))
+		return -EINVAL;
+
+	return invalid_str(*target_params, end);
+}
+
+static int populate_table(struct dm_table *table, struct dm_ioctl *param,
+			  size_t param_size)
+{
+	int r;
+	unsigned int i = 0;
+	struct dm_target_spec *spec = (struct dm_target_spec *) param;
+	uint32_t next = param->data_start;
+	void *end = (void *) param + param_size;
+	char *target_params;
+
+	if (!param->target_count) {
+		DMWARN("populate_table: no targets specified");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < param->target_count; i++) {
+
+		r = next_target(spec, next, end, &spec, &target_params);
+		if (r) {
+			DMWARN("unable to find target");
+			return r;
+		}
+
+		r = dm_table_add_target(table, spec->target_type,
+					(sector_t) spec->sector_start,
+					(sector_t) spec->length,
+					target_params);
+		if (r) {
+			DMWARN("error adding target to table");
+			return r;
+		}
+
+		next = spec->next;
+	}
+
+	return dm_table_complete(table);
+}
+
+static int table_load(struct dm_ioctl *param, size_t param_size)
+{
+	int r;
+	struct hash_cell *hc;
+	struct dm_table *t;
+
+	r = dm_table_create(&t, get_mode(param), param->target_count);
+	if (r)
+		return r;
+
+	r = populate_table(t, param, param_size);
+	if (r) {
+		dm_table_put(t);
+		return r;
+	}
+
+	down_write(&_hash_lock);
+	hc = __find_device_hash_cell(param);
+	if (!hc) {
+		DMWARN("device doesn't appear to be in the dev hash table.");
+		up_write(&_hash_lock);
+		return -ENXIO;
+	}
+
+	if (hc->new_map)
+		dm_table_put(hc->new_map);
+	hc->new_map = t;
+	param->flags |= DM_INACTIVE_PRESENT_FLAG;
+
+	r = __dev_status(hc->md, param);
+	up_write(&_hash_lock);
+	return r;
+}
+
+static int table_clear(struct dm_ioctl *param, size_t param_size)
+{
+	int r;
+	struct hash_cell *hc;
+
+	down_write(&_hash_lock);
+
+	hc = __find_device_hash_cell(param);
+	if (!hc) {
+		DMWARN("device doesn't appear to be in the dev hash table.");
+		up_write(&_hash_lock);
+		return -ENXIO;
+	}
+
+	if (hc->new_map) {
+		dm_table_put(hc->new_map);
+		hc->new_map = NULL;
+	}
+
+	param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
+
+	r = __dev_status(hc->md, param);
+	up_write(&_hash_lock);
+	return r;
+}
+
+/*
+ * Retrieves a list of devices used by a particular dm device.
+ */
+static void retrieve_deps(struct dm_table *table, struct dm_ioctl *param,
+			  size_t param_size)
+{
+	unsigned int count = 0;
+	struct list_head *tmp;
+	size_t len, needed;
+	struct dm_target_deps *deps;
+
+	deps = get_result_buffer(param, param_size, &len);
+
+	/*
+	 * Count the devices.
+	 */
+	list_for_each(tmp, dm_table_get_devices(table))
+		count++;
+
+	/*
+	 * Check we have enough space.
+	 */
+	needed = sizeof(*deps) + (sizeof(*deps->dev) * count);
+	if (len < needed) {
+		param->flags |= DM_BUFFER_FULL_FLAG;
+		return;
+	}
+
+	/*
+	 * Fill in the devices.
+	 */
+	deps->count = count;
+	count = 0;
+	list_for_each(tmp, dm_table_get_devices(table)) {
+		struct dm_dev *dd = list_entry(tmp, struct dm_dev, list);
+		deps->dev[count++] = dd->bdev->bd_dev;
+	}
+
+	param->data_size = param->data_start + needed;
+}
+
+static int table_deps(struct dm_ioctl *param, size_t param_size)
+{
+	int r;
+	struct mapped_device *md;
+	struct dm_table *table;
+
+	md = find_device(param);
+	if (!md)
+		return -ENXIO;
+
+	r = __dev_status(md, param);
+	if (r)
+		goto out;
+
+	table = dm_get_table(md);
+	if (table) {
+		retrieve_deps(table, param, param_size);
+		dm_table_put(table);
+	}
+
+ out:
+	dm_put(md);
+	return r;
+}
+
+/*
+ * Return the status of a device as a text string for each
+ * target.
+ */
+static int table_status(struct dm_ioctl *param, size_t param_size)
+{
+	int r;
+	struct mapped_device *md;
+	struct dm_table *table;
+
+	md = find_device(param);
+	if (!md)
+		return -ENXIO;
+
+	r = __dev_status(md, param);
+	if (r)
+		goto out;
+ 
+	table = dm_get_table(md);
+	if (table) {
+		retrieve_status(table, param, param_size);
+		dm_table_put(table);
+	}
+
+ out:
+	dm_put(md);
+	return r;
+}
+
+/*-----------------------------------------------------------------
+ * Implementation of open/close/ioctl on the special char
+ * device.
+ *---------------------------------------------------------------*/
+static ioctl_fn lookup_ioctl(unsigned int cmd)
+{
+	static struct {
+		int cmd;
+		ioctl_fn fn;
+	} _ioctls[] = {
+		{DM_VERSION_CMD, NULL},	/* version is dealt with elsewhere */
+		{DM_REMOVE_ALL_CMD, remove_all},
+		{DM_LIST_DEVICES_CMD, list_devices},
+
+		{DM_DEV_CREATE_CMD, dev_create},
+		{DM_DEV_REMOVE_CMD, dev_remove},
+		{DM_DEV_RENAME_CMD, dev_rename},
+		{DM_DEV_SUSPEND_CMD, dev_suspend},
+		{DM_DEV_STATUS_CMD, dev_status},
+		{DM_DEV_WAIT_CMD, dev_wait},
+
+		{DM_TABLE_LOAD_CMD, table_load},
+		{DM_TABLE_CLEAR_CMD, table_clear},
+		{DM_TABLE_DEPS_CMD, table_deps},
+		{DM_TABLE_STATUS_CMD, table_status}
+	};
+
+	return (cmd >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[cmd].fn;
+}
+
+/*
+ * As well as checking the version compatibility this always
+ * copies the kernel interface version out.
+ */
+static int check_version(unsigned int cmd, struct dm_ioctl *user)
+{
+	uint32_t version[3];
+	int r = 0;
+
+	if (copy_from_user(version, user->version, sizeof(version)))
+		return -EFAULT;
+
+	if ((DM_VERSION_MAJOR != version[0]) ||
+	    (DM_VERSION_MINOR < version[1])) {
+		DMWARN("ioctl interface mismatch: "
+		       "kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)",
+		       DM_VERSION_MAJOR, DM_VERSION_MINOR,
+		       DM_VERSION_PATCHLEVEL,
+		       version[0], version[1], version[2], cmd);
+		r = -EINVAL;
+	}
+
+	/*
+	 * Fill in the kernel version.
+	 */
+	version[0] = DM_VERSION_MAJOR;
+	version[1] = DM_VERSION_MINOR;
+	version[2] = DM_VERSION_PATCHLEVEL;
+	if (copy_to_user(user->version, version, sizeof(version)))
+		return -EFAULT;
+
+	return r;
+}
+
+static void free_params(struct dm_ioctl *param)
+{
+	vfree(param);
+}
+
+static int copy_params(struct dm_ioctl *user, struct dm_ioctl **param)
+{
+	struct dm_ioctl tmp, *dmi;
+
+	if (copy_from_user(&tmp, user, sizeof(tmp)))
+		return -EFAULT;
+
+	if (tmp.data_size < sizeof(tmp))
+		return -EINVAL;
+
+	dmi = (struct dm_ioctl *) vmalloc(tmp.data_size);
+	if (!dmi)
+		return -ENOMEM;
+
+	if (copy_from_user(dmi, user, tmp.data_size)) {
+		vfree(dmi);
+		return -EFAULT;
+	}
+
+	*param = dmi;
+	return 0;
+}
+
+static int validate_params(uint cmd, struct dm_ioctl *param)
+{
+	/* Always clear this flag */
+	param->flags &= ~DM_BUFFER_FULL_FLAG;
+
+	/* Ignores parameters */
+	if (cmd == DM_REMOVE_ALL_CMD || cmd == DM_LIST_DEVICES_CMD)
+		return 0;
+
+	/* Unless creating, either name or uuid but not both */
+	if (cmd != DM_DEV_CREATE_CMD) {
+		if ((!*param->uuid && !*param->name) ||
+		    (*param->uuid && *param->name)) {
+			DMWARN("one of name or uuid must be supplied, cmd(%u)",
+			       cmd);
+			return -EINVAL;
+		}
+	}
+
+	/* Ensure strings are terminated */
+	param->name[DM_NAME_LEN - 1] = '\0';
+	param->uuid[DM_UUID_LEN - 1] = '\0';
+
+	return 0;
+}
+
+static int ctl_ioctl(struct inode *inode, struct file *file,
+		     uint command, ulong u)
+{
+	int r = 0;
+	unsigned int cmd;
+	struct dm_ioctl *param;
+	struct dm_ioctl *user = (struct dm_ioctl *) u;
+	ioctl_fn fn = NULL;
+	size_t param_size;
+
+	/* only root can play with this */
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	if (_IOC_TYPE(command) != DM_IOCTL)
+		return -ENOTTY;
+
+	cmd = _IOC_NR(command);
+
+	/*
+	 * Check the interface version passed in.  This also
+	 * writes out the kernel's interface version.
+	 */
+	r = check_version(cmd, user);
+	if (r)
+		return r;
+
+	/*
+	 * Nothing more to do for the version command.
+	 */
+	if (cmd == DM_VERSION_CMD)
+		return 0;
+
+	fn = lookup_ioctl(cmd);
+	if (!fn) {
+		DMWARN("dm_ctl_ioctl: unknown command 0x%x", command);
+		return -ENOTTY;
+	}
+
+	/*
+	 * FIXME: I don't like this, we're trying to avoid low
+	 * memory issues when a device is suspended.
+	 */
+	current->flags |= PF_MEMALLOC;
+
+	/*
+	 * Copy the parameters into kernel space.
+	 */
+	r = copy_params(user, &param);
+	if (r) {
+		current->flags &= ~PF_MEMALLOC;
+		return r;
+	}
+
+	r = validate_params(cmd, param);
+	if (r)
+		goto out;
+
+	param_size = param->data_size;
+	param->data_size = sizeof(*param);
+	r = fn(param, param_size);
+
+	/*
+	 * Copy the results back to userland.
+	 */
+	if (!r && copy_to_user(user, param, param->data_size))
+		r = -EFAULT;
+
+ out:
+	free_params(param);
+	current->flags &= ~PF_MEMALLOC;
+	return r;
+}
+
+static struct file_operations _ctl_fops = {
+	.ioctl	 = ctl_ioctl,
+	.owner	 = THIS_MODULE,
+};
+
+static devfs_handle_t _ctl_handle;
+
+static struct miscdevice _dm_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name  = DM_NAME,
+	.fops  = &_ctl_fops
+};
+
+/*
+ * Create misc character device and link to DM_DIR/control.
+ */
+int __init dm_interface_init(void)
+{
+	int r;
+	char rname[64];
+
+	r = dm_hash_init();
+	if (r)
+		return r;
+
+	r = misc_register(&_dm_misc);
+	if (r) {
+		DMERR("misc_register failed for control device");
+		dm_hash_exit();
+		return r;
+	}
+
+	r = devfs_generate_path(_dm_misc.devfs_handle, rname + 3,
+				sizeof rname - 3);
+	if (r == -ENOSYS)
+		goto done;	/* devfs not present */
+
+	if (r < 0) {
+		DMERR("devfs_generate_path failed for control device");
+		goto failed;
+	}
+
+	strncpy(rname + r, "../", 3);
+	r = devfs_mk_symlink(NULL, DM_DIR "/control",
+			     DEVFS_FL_DEFAULT, rname + r, &_ctl_handle, NULL);
+	if (r) {
+		DMERR("devfs_mk_symlink failed for control device");
+		goto failed;
+	}
+	devfs_auto_unregister(_dm_misc.devfs_handle, _ctl_handle);
+
+      done:
+	DMINFO("%d.%d.%d%s initialised: %s", DM_VERSION_MAJOR,
+	       DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, DM_VERSION_EXTRA,
+	       DM_DRIVER_EMAIL);
+	return 0;
+
+      failed:
+	misc_deregister(&_dm_misc);
+	dm_hash_exit();
+	return r;
+}
+
+void dm_interface_exit(void)
+{
+	if (misc_deregister(&_dm_misc) < 0)
+		DMERR("misc_deregister failed for control device");
+
+	dm_hash_exit();
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/dm-io.h linux-2.4.27-leo/drivers/md/dm-io.h
--- linux-2.4.27/drivers/md/dm-io.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/md/dm-io.h	2004-09-20 21:40:18.000000000 +0100
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2003 Sistina Software
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef _DM_IO_H
+#define _DM_IO_H
+
+#include "dm.h"
+
+#include <linux/list.h>
+
+/* Move these to bitops.h eventually */
+/* Improved generic_fls algorithm (in 2.4 there is no generic_fls so far) */
+/* (c) 2002, D.Phillips and Sistina Software */
+/* Licensed under Version 2 of the GPL */
+
+static unsigned generic_fls8(unsigned n)
+{
+	return n & 0xf0 ?
+	    n & 0xc0 ? (n >> 7) + 7 : (n >> 5) + 5:
+	    n & 0x0c ? (n >> 3) + 3 : n - ((n + 1) >> 2);
+}
+
+static inline unsigned generic_fls16(unsigned n)
+{
+	return	n & 0xff00? generic_fls8(n >> 8) + 8 : generic_fls8(n);
+}
+
+static inline unsigned generic_fls32(unsigned n)
+{
+	return	n & 0xffff0000 ? generic_fls16(n >> 16) + 16 : generic_fls16(n);
+}
+
+/* FIXME make this configurable */
+#define DM_MAX_IO_REGIONS 8
+
+struct io_region {
+	kdev_t dev;
+	sector_t sector;
+	sector_t count;
+};
+
+
+/*
+ * 'error' is a bitset, with each bit indicating whether an error
+ * occurred doing io to the corresponding region.
+ */
+typedef void (*io_notify_fn)(unsigned int error, void *context);
+
+
+/*
+ * Before anyone uses the IO interface they should call
+ * dm_io_get(), specifying roughly how many pages they are
+ * expecting to perform io on concurrently.
+ *
+ * This function may block.
+ */
+int dm_io_get(unsigned int num_pages);
+void dm_io_put(unsigned int num_pages);
+
+
+/*
+ * Synchronous IO.
+ *
+ * Please ensure that the rw flag in the next two functions is
+ * either READ or WRITE, ie. we don't take READA.  Any
+ * regions with a zero count field will be ignored.
+ */
+int dm_io_sync(unsigned int num_regions, struct io_region *where, int rw,
+	       struct page *pages, unsigned int offset,
+	       unsigned int *error_bits);
+
+
+/*
+ * Aynchronous IO.
+ *
+ * The 'where' array may be safely allocated on the stack since
+ * the function takes a copy.
+ */
+int dm_io_async(unsigned int num_regions, struct io_region *where, int rw,
+		struct page *pages, unsigned int offset,
+		io_notify_fn fn, void *context);
+
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/dm-linear.c linux-2.4.27-leo/drivers/md/dm-linear.c
--- linux-2.4.27/drivers/md/dm-linear.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/md/dm-linear.c	2004-09-17 03:19:08.000000000 +0100
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2001 Sistina Software (UK) Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm.h"
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/slab.h>
+
+/*
+ * Linear: maps a linear range of a device.
+ */
+struct linear_c {
+	struct dm_dev *dev;
+	sector_t start;
+};
+
+/*
+ * Construct a linear mapping: <dev_path> <offset>
+ */
+static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+	struct linear_c *lc;
+
+	if (argc != 2) {
+		ti->error = "dm-linear: Invalid argument count";
+		return -EINVAL;
+	}
+
+	lc = kmalloc(sizeof(*lc), GFP_KERNEL);
+	if (lc == NULL) {
+		ti->error = "dm-linear: Cannot allocate linear context";
+		return -ENOMEM;
+	}
+
+	if (sscanf(argv[1], SECTOR_FORMAT, &lc->start) != 1) {
+		ti->error = "dm-linear: Invalid device sector";
+		goto bad;
+	}
+
+	if (dm_get_device(ti, argv[0], lc->start, ti->len,
+			  dm_table_get_mode(ti->table), &lc->dev)) {
+		ti->error = "dm-linear: Device lookup failed";
+		goto bad;
+	}
+
+	ti->private = lc;
+	return 0;
+
+      bad:
+	kfree(lc);
+	return -EINVAL;
+}
+
+static void linear_dtr(struct dm_target *ti)
+{
+	struct linear_c *lc = (struct linear_c *) ti->private;
+
+	dm_put_device(ti, lc->dev);
+	kfree(lc);
+}
+
+static int linear_map(struct dm_target *ti, struct buffer_head *bh, int rw,
+		      union map_info *map_context)
+{
+	struct linear_c *lc = (struct linear_c *) ti->private;
+
+	bh->b_rdev = lc->dev->dev;
+	bh->b_rsector = lc->start + (bh->b_rsector - ti->begin);
+
+	return 1;
+}
+
+static int linear_status(struct dm_target *ti, status_type_t type,
+			 char *result, unsigned int maxlen)
+{
+	struct linear_c *lc = (struct linear_c *) ti->private;
+	kdev_t kdev;
+
+	switch (type) {
+	case STATUSTYPE_INFO:
+		result[0] = '\0';
+		break;
+
+	case STATUSTYPE_TABLE:
+		kdev = to_kdev_t(lc->dev->bdev->bd_dev);
+		snprintf(result, maxlen, "%s " SECTOR_FORMAT,
+			 dm_kdevname(kdev), lc->start);
+		break;
+	}
+	return 0;
+}
+
+static struct target_type linear_target = {
+	.name   = "linear",
+	.module = THIS_MODULE,
+	.ctr    = linear_ctr,
+	.dtr    = linear_dtr,
+	.map    = linear_map,
+	.status = linear_status,
+};
+
+int __init dm_linear_init(void)
+{
+	int r = dm_register_target(&linear_target);
+
+	if (r < 0)
+		DMERR("linear: register failed %d", r);
+
+	return r;
+}
+
+void dm_linear_exit(void)
+{
+	int r = dm_unregister_target(&linear_target);
+
+	if (r < 0)
+		DMERR("linear: unregister failed %d", r);
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/dm-log.c linux-2.4.27-leo/drivers/md/dm-log.c
--- linux-2.4.27/drivers/md/dm-log.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/md/dm-log.c	2004-09-17 03:19:08.000000000 +0100
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2003 Sistina Software
+ *
+ * This file is released under the LGPL.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+
+#include "dm-log.h"
+#include "dm-io.h"
+
+static LIST_HEAD(_log_types);
+static spinlock_t _lock = SPIN_LOCK_UNLOCKED;
+
+int dm_register_dirty_log_type(struct dirty_log_type *type)
+{
+	spin_lock(&_lock);
+	type->use_count = 0;
+	if (type->module)
+		__MOD_INC_USE_COUNT(type->module);
+
+	list_add(&type->list, &_log_types);
+	spin_unlock(&_lock);
+
+	return 0;
+}
+
+int dm_unregister_dirty_log_type(struct dirty_log_type *type)
+{
+	spin_lock(&_lock);
+
+	if (type->use_count)
+		DMWARN("Attempt to unregister a log type that is still in use");
+	else {
+		list_del(&type->list);
+		if (type->module)
+			__MOD_DEC_USE_COUNT(type->module);
+	}
+
+	spin_unlock(&_lock);
+
+	return 0;
+}
+
+static struct dirty_log_type *get_type(const char *type_name)
+{
+	struct dirty_log_type *type;
+	struct list_head *tmp;
+
+	spin_lock(&_lock);
+	list_for_each (tmp, &_log_types) {
+		type = list_entry(tmp, struct dirty_log_type, list);
+		if (!strcmp(type_name, type->name)) {
+			type->use_count++;
+			spin_unlock(&_lock);
+			return type;
+		}
+	}
+
+	spin_unlock(&_lock);
+	return NULL;
+}
+
+static void put_type(struct dirty_log_type *type)
+{
+	spin_lock(&_lock);
+	type->use_count--;
+	spin_unlock(&_lock);
+}
+
+struct dirty_log *dm_create_dirty_log(const char *type_name, sector_t dev_size,
+				      unsigned int argc, char **argv)
+{
+	struct dirty_log_type *type;
+	struct dirty_log *log;
+
+	log = kmalloc(sizeof(*log), GFP_KERNEL);
+	if (!log)
+		return NULL;
+
+	type = get_type(type_name);
+	if (!type) {
+		kfree(log);
+		return NULL;
+	}
+
+	log->type = type;
+	if (type->ctr(log, dev_size, argc, argv)) {
+		kfree(log);
+		put_type(type);
+		return NULL;
+	}
+
+	return log;
+}
+
+void dm_destroy_dirty_log(struct dirty_log *log)
+{
+	log->type->dtr(log);
+	put_type(log->type);
+	kfree(log);
+}
+
+
+/*-----------------------------------------------------------------
+ * In core log, ie. trivial, non-persistent
+ *
+ * For now we'll keep this simple and just have 2 bitsets, one
+ * for clean/dirty, the other for sync/nosync.  The sync bitset
+ * will be freed when everything is in sync.
+ *
+ * FIXME: problems with a 64bit sector_t
+ *---------------------------------------------------------------*/
+struct core_log {
+	sector_t region_size;
+	unsigned int region_count;
+	unsigned long *clean_bits;
+	unsigned long *sync_bits;
+	unsigned long *recovering_bits;	/* FIXME: this seems excessive */
+
+	int sync_search;
+};
+
+#define BYTE_SHIFT 3
+
+static int core_ctr(struct dirty_log *log, sector_t dev_size,
+		    unsigned int argc, char **argv)
+{
+	struct core_log *clog;
+	sector_t region_size;
+	unsigned int region_count;
+	size_t bitset_size;
+
+	if (argc != 1) {
+		DMWARN("wrong number of arguments to core_log");
+		return -EINVAL;
+	}
+
+	if (sscanf(argv[0], SECTOR_FORMAT, &region_size) != 1) {
+		DMWARN("invalid region size string");
+		return -EINVAL;
+	}
+
+	region_count = dm_div_up(dev_size, region_size);
+
+	clog = kmalloc(sizeof(*clog), GFP_KERNEL);
+	if (!clog) {
+		DMWARN("couldn't allocate core log");
+		return -ENOMEM;
+	}
+
+	clog->region_size = region_size;
+	clog->region_count = region_count;
+
+	/*
+ 	 * Work out how many words we need to hold the bitset.
+ 	 */
+	bitset_size = dm_round_up(region_count,
+				  sizeof(*clog->clean_bits) << BYTE_SHIFT);
+	bitset_size >>= BYTE_SHIFT;
+
+	clog->clean_bits = vmalloc(bitset_size);
+	if (!clog->clean_bits) {
+		DMWARN("couldn't allocate clean bitset");
+		kfree(clog);
+		return -ENOMEM;
+	}
+	memset(clog->clean_bits, -1, bitset_size);
+
+	clog->sync_bits = vmalloc(bitset_size);
+	if (!clog->sync_bits) {
+		DMWARN("couldn't allocate sync bitset");
+		vfree(clog->clean_bits);
+		kfree(clog);
+		return -ENOMEM;
+	}
+	memset(clog->sync_bits, 0, bitset_size);
+
+	clog->recovering_bits = vmalloc(bitset_size);
+	if (!clog->recovering_bits) {
+		DMWARN("couldn't allocate sync bitset");
+		vfree(clog->sync_bits);
+		vfree(clog->clean_bits);
+		kfree(clog);
+		return -ENOMEM;
+	}
+	memset(clog->recovering_bits, 0, bitset_size);
+	clog->sync_search = 0;
+	log->context = clog;
+	return 0;
+}
+
+static void core_dtr(struct dirty_log *log)
+{
+	struct core_log *clog = (struct core_log *) log->context;
+	vfree(clog->clean_bits);
+	vfree(clog->sync_bits);
+	vfree(clog->recovering_bits);
+	kfree(clog);
+}
+
+static sector_t core_get_region_size(struct dirty_log *log)
+{
+	struct core_log *clog = (struct core_log *) log->context;
+	return clog->region_size;
+}
+
+static int core_is_clean(struct dirty_log *log, region_t region)
+{
+	struct core_log *clog = (struct core_log *) log->context;
+	return test_bit(region, clog->clean_bits);
+}
+
+static int core_in_sync(struct dirty_log *log, region_t region, int block)
+{
+	struct core_log *clog = (struct core_log *) log->context;
+
+	return test_bit(region, clog->sync_bits) ? 1 : 0;
+}
+
+static int core_flush(struct dirty_log *log)
+{
+	/* no op */
+	return 0;
+}
+
+static void core_mark_region(struct dirty_log *log, region_t region)
+{
+	struct core_log *clog = (struct core_log *) log->context;
+	clear_bit(region, clog->clean_bits);
+}
+
+static void core_clear_region(struct dirty_log *log, region_t region)
+{
+	struct core_log *clog = (struct core_log *) log->context;
+	set_bit(region, clog->clean_bits);
+}
+
+static int core_get_resync_work(struct dirty_log *log, region_t *region)
+{
+	struct core_log *clog = (struct core_log *) log->context;
+
+	if (clog->sync_search >= clog->region_count)
+		return 0;
+
+	do {
+		*region = find_next_zero_bit(clog->sync_bits,
+					     clog->region_count,
+					     clog->sync_search);
+		clog->sync_search = *region + 1;
+
+		if (*region == clog->region_count)
+			return 0;
+
+	} while (test_bit(*region, clog->recovering_bits));
+
+	set_bit(*region, clog->recovering_bits);
+	return 1;
+}
+
+static void core_complete_resync_work(struct dirty_log *log, region_t region,
+				      int success)
+{
+	struct core_log *clog = (struct core_log *) log->context;
+
+	clear_bit(region, clog->recovering_bits);
+	if (success)
+		set_bit(region, clog->sync_bits);
+}
+
+static struct dirty_log_type _core_type = {
+	.name = "core",
+
+	.ctr = core_ctr,
+	.dtr = core_dtr,
+	.get_region_size = core_get_region_size,
+	.is_clean = core_is_clean,
+	.in_sync = core_in_sync,
+	.flush = core_flush,
+	.mark_region = core_mark_region,
+	.clear_region = core_clear_region,
+	.get_resync_work = core_get_resync_work,
+	.complete_resync_work = core_complete_resync_work
+};
+
+__init int dm_dirty_log_init(void)
+{
+	int r;
+
+	r = dm_register_dirty_log_type(&_core_type);
+	if (r)
+		DMWARN("couldn't register core log");
+
+	return r;
+}
+
+void dm_dirty_log_exit(void)
+{
+	dm_unregister_dirty_log_type(&_core_type);
+}
+
+EXPORT_SYMBOL(dm_register_dirty_log_type);
+EXPORT_SYMBOL(dm_unregister_dirty_log_type);
+EXPORT_SYMBOL(dm_dirty_log_init);
+EXPORT_SYMBOL(dm_dirty_log_exit);
+EXPORT_SYMBOL(dm_create_dirty_log);
+EXPORT_SYMBOL(dm_destroy_dirty_log);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/dm-log.h linux-2.4.27-leo/drivers/md/dm-log.h
--- linux-2.4.27/drivers/md/dm-log.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/md/dm-log.h	2004-09-20 21:40:24.000000000 +0100
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2003 Sistina Software
+ *
+ * This file is released under the LGPL.
+ */
+
+#ifndef DM_DIRTY_LOG
+#define DM_DIRTY_LOG
+
+#include "dm.h"
+
+typedef sector_t region_t;
+
+struct dirty_log_type;
+
+struct dirty_log {
+	struct dirty_log_type *type;
+	void *context;
+};
+
+struct dirty_log_type {
+	struct list_head list;
+	const char *name;
+	struct module *module;
+	unsigned int use_count;
+
+	int (*ctr)(struct dirty_log *log, sector_t dev_size,
+		   unsigned int argc, char **argv);
+	void (*dtr)(struct dirty_log *log);
+
+	/*
+	 * Retrieves the smallest size of region that the log can
+	 * deal with.
+	 */
+	sector_t (*get_region_size)(struct dirty_log *log);
+
+        /*
+	 * A predicate to say whether a region is clean or not.
+	 * May block.
+	 */
+	int (*is_clean)(struct dirty_log *log, region_t region);
+
+	/*
+	 *  Returns: 0, 1, -EWOULDBLOCK, < 0
+	 *
+	 * A predicate function to check the area given by
+	 * [sector, sector + len) is in sync.
+	 *
+	 * If -EWOULDBLOCK is returned the state of the region is
+	 * unknown, typically this will result in a read being
+	 * passed to a daemon to deal with, since a daemon is
+	 * allowed to block.
+	 */
+	int (*in_sync)(struct dirty_log *log, region_t region, int can_block);
+
+	/*
+	 * Flush the current log state (eg, to disk).  This
+	 * function may block.
+	 */
+	int (*flush)(struct dirty_log *log);
+
+	/*
+	 * Mark an area as clean or dirty.  These functions may
+	 * block, though for performance reasons blocking should
+	 * be extremely rare (eg, allocating another chunk of
+	 * memory for some reason).
+	 */
+	void (*mark_region)(struct dirty_log *log, region_t region);
+	void (*clear_region)(struct dirty_log *log, region_t region);
+
+	/*
+	 * Returns: <0 (error), 0 (no region), 1 (region)
+	 *
+	 * The mirrord will need perform recovery on regions of
+	 * the mirror that are in the NOSYNC state.  This
+	 * function asks the log to tell the caller about the
+	 * next region that this machine should recover.
+	 *
+	 * Do not confuse this function with 'in_sync()', one
+	 * tells you if an area is synchronised, the other
+	 * assigns recovery work.
+	*/
+	int (*get_resync_work)(struct dirty_log *log, region_t *region);
+
+	/*
+	 * This notifies the log that the resync of an area has
+	 * been completed.  The log should then mark this region
+	 * as CLEAN.
+	 */
+	void (*complete_resync_work)(struct dirty_log *log,
+				     region_t region, int success);
+};
+
+int dm_register_dirty_log_type(struct dirty_log_type *type);
+int dm_unregister_dirty_log_type(struct dirty_log_type *type);
+
+
+/*
+ * Make sure you use these two functions, rather than calling
+ * type->constructor/destructor() directly.
+ */
+struct dirty_log *dm_create_dirty_log(const char *type_name, sector_t dev_size,
+				      unsigned int argc, char **argv);
+void dm_destroy_dirty_log(struct dirty_log *log);
+
+/*
+ * init/exit functions.
+ */
+int dm_dirty_log_init(void);
+void dm_dirty_log_exit(void);
+
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/dm-raid1.c linux-2.4.27-leo/drivers/md/dm-raid1.c
--- linux-2.4.27/drivers/md/dm-raid1.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/md/dm-raid1.c	2004-09-17 03:19:08.000000000 +0100
@@ -0,0 +1,1294 @@
+/*
+ * Copyright (C) 2003 Sistina Software Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm.h"
+#include "dm-daemon.h"
+#include "dm-io.h"
+#include "dm-log.h"
+#include "kcopyd.h"
+
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <linux/mempool.h>
+#include <linux/module.h>
+#include <linux/pagemap.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/vmalloc.h>
+
+static struct dm_daemon _kmirrord;
+
+/*-----------------------------------------------------------------
+ * buffer lists:
+ *
+ * We play with singly linked lists of buffers, but we want to be
+ * careful to add new buffers to the back of the list, to avoid
+ * buffers being starved of attention.
+ *---------------------------------------------------------------*/
+struct buffer_list {
+	struct buffer_head *head;
+	struct buffer_head *tail;
+};
+
+static inline void buffer_list_init(struct buffer_list *bl)
+{
+	bl->head = bl->tail = NULL;
+}
+
+static inline void buffer_list_add(struct buffer_list *bl,
+				   struct buffer_head *bh)
+{
+	bh->b_reqnext = NULL;
+
+	if (bl->tail) {
+		bl->tail->b_reqnext = bh;
+		bl->tail = bh;
+	} else
+		bl->head = bl->tail = bh;
+}
+
+static struct buffer_head *buffer_list_pop(struct buffer_list *bl)
+{
+	struct buffer_head *bh = bl->head;
+
+	if (bh) {
+		bl->head = bl->head->b_reqnext;
+		if (!bl->head)
+			bl->tail = NULL;
+
+		bh->b_reqnext = NULL;
+	}
+
+	return bh;
+}
+
+/*-----------------------------------------------------------------
+ * Region hash
+ *
+ * The mirror splits itself up into discrete regions.  Each
+ * region can be in one of three states: clean, dirty,
+ * nosync.  There is no need to put clean regions in the hash.
+ *
+ * In addition to being present in the hash table a region _may_
+ * be present on one of three lists.
+ *
+ *   clean_regions: Regions on this list have no io pending to
+ *   them, they are in sync, we are no longer interested in them,
+ *   they are dull.  rh_update_states() will remove them from the
+ *   hash table.
+ *
+ *   quiesced_regions: These regions have been spun down, ready
+ *   for recovery.  rh_recovery_start() will remove regions from
+ *   this list and hand them to kmirrord, which will schedule the
+ *   recovery io with kcopyd.
+ *
+ *   recovered_regions: Regions that kcopyd has successfully
+ *   recovered.  rh_update_states() will now schedule any delayed
+ *   io, up the recovery_count, and remove the region from the
+ *   hash.
+ *
+ * There are 2 locks:
+ *   A rw spin lock 'hash_lock' protects just the hash table,
+ *   this is never held in write mode from interrupt context,
+ *   which I believe means that we only have to disable irqs when
+ *   doing a write lock.
+ *
+ *   An ordinary spin lock 'region_lock' that protects the three
+ *   lists in the region_hash, with the 'state', 'list' and
+ *   'bhs_delayed' fields of the regions.  This is used from irq
+ *   context, so all other uses will have to suspend local irqs.
+ *---------------------------------------------------------------*/
+struct mirror_set;
+struct region_hash {
+	struct mirror_set *ms;
+	sector_t region_size;
+
+	/* holds persistent region state */
+	struct dirty_log *log;
+
+	/* hash table */
+	rwlock_t hash_lock;
+	mempool_t *region_pool;
+	unsigned int mask;
+	unsigned int nr_buckets;
+	struct list_head *buckets;
+
+	spinlock_t region_lock;
+	struct semaphore recovery_count;
+	struct list_head clean_regions;
+	struct list_head quiesced_regions;
+	struct list_head recovered_regions;
+};
+
+enum {
+	RH_CLEAN,
+	RH_DIRTY,
+	RH_NOSYNC,
+	RH_RECOVERING
+};
+
+struct region {
+	struct region_hash *rh;	/* FIXME: can we get rid of this ? */
+	region_t key;
+	int state;
+
+	struct list_head hash_list;
+	struct list_head list;
+
+	atomic_t pending;
+	struct buffer_head *delayed_bhs;
+};
+
+/*
+ * Conversion fns
+ */
+static inline region_t bh_to_region(struct region_hash *rh,
+				    struct buffer_head *bh)
+{
+	return bh->b_rsector / rh->region_size;
+}
+
+static inline sector_t region_to_sector(struct region_hash *rh, region_t region)
+{
+	return region * rh->region_size;
+}
+
+/* FIXME move this */
+static void queue_bh(struct mirror_set *ms, struct buffer_head *bh, int rw);
+
+static void *region_alloc(int gfp_mask, void *pool_data)
+{
+	return kmalloc(sizeof(struct region), gfp_mask);
+}
+
+static void region_free(void *element, void *pool_data)
+{
+	kfree(element);
+}
+
+#define MIN_REGIONS 64
+#define MAX_RECOVERY 1
+static int rh_init(struct region_hash *rh, struct mirror_set *ms,
+		   struct dirty_log *log, sector_t region_size,
+		   region_t nr_regions)
+{
+	unsigned int nr_buckets, max_buckets;
+	size_t i;
+
+	/*
+	 * Calculate a suitable number of buckets for our hash
+	 * table.
+	 */
+	max_buckets = nr_regions >> 6;
+	for (nr_buckets = 128u; nr_buckets < max_buckets; nr_buckets <<= 1)
+		;
+	nr_buckets >>= 1;
+
+	rh->ms = ms;
+	rh->log = log;
+	rh->region_size = region_size;
+	rwlock_init(&rh->hash_lock);
+	rh->mask = nr_buckets - 1;
+	rh->nr_buckets = nr_buckets;
+
+	rh->buckets = vmalloc(nr_buckets * sizeof(*rh->buckets));
+	if (!rh->buckets) {
+		DMERR("unable to allocate region hash memory");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < nr_buckets; i++)
+		INIT_LIST_HEAD(rh->buckets + i);
+
+	spin_lock_init(&rh->region_lock);
+	sema_init(&rh->recovery_count, 0);
+	INIT_LIST_HEAD(&rh->clean_regions);
+	INIT_LIST_HEAD(&rh->quiesced_regions);
+	INIT_LIST_HEAD(&rh->recovered_regions);
+
+	rh->region_pool = mempool_create(MIN_REGIONS, region_alloc,
+					 region_free, NULL);
+	if (!rh->region_pool) {
+		vfree(rh->buckets);
+		rh->buckets = NULL;
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void rh_exit(struct region_hash *rh)
+{
+	unsigned int h;
+	struct region *reg;
+	struct list_head *tmp, *tmp2;
+
+	BUG_ON(!list_empty(&rh->quiesced_regions));
+	for (h = 0; h < rh->nr_buckets; h++) {
+		list_for_each_safe (tmp, tmp2, rh->buckets + h) {
+			reg = list_entry(tmp, struct region, hash_list);
+			BUG_ON(atomic_read(&reg->pending));
+			mempool_free(reg, rh->region_pool);
+		}
+	}
+
+	if (rh->log)
+		dm_destroy_dirty_log(rh->log);
+	if (rh->region_pool)
+		mempool_destroy(rh->region_pool);
+	vfree(rh->buckets);
+}
+
+#define RH_HASH_MULT 2654435387U
+
+static inline unsigned int rh_hash(struct region_hash *rh, region_t region)
+{
+	return (unsigned int) ((region * RH_HASH_MULT) >> 12) & rh->mask;
+}
+
+static struct region *__rh_lookup(struct region_hash *rh, region_t region)
+{
+	struct region *reg;
+
+	list_for_each_entry (reg, rh->buckets + rh_hash(rh, region), hash_list)
+		if (reg->key == region)
+			return reg;
+
+	return NULL;
+}
+
+static void __rh_insert(struct region_hash *rh, struct region *reg)
+{
+	unsigned int h = rh_hash(rh, reg->key);
+	list_add(&reg->hash_list, rh->buckets + h);
+}
+
+static struct region *__rh_alloc(struct region_hash *rh, region_t region)
+{
+	struct region *reg, *nreg;
+
+	read_unlock(&rh->hash_lock);
+	nreg = mempool_alloc(rh->region_pool, GFP_NOIO);
+	nreg->state = rh->log->type->in_sync(rh->log, region, 1) ?
+		RH_CLEAN : RH_NOSYNC;
+	nreg->rh = rh;
+	nreg->key = region;
+
+	INIT_LIST_HEAD(&nreg->list);
+
+	atomic_set(&nreg->pending, 0);
+	nreg->delayed_bhs = NULL;
+	write_lock_irq(&rh->hash_lock);
+
+	reg = __rh_lookup(rh, region);
+	if (reg)
+		/* we lost the race */
+		mempool_free(nreg, rh->region_pool);
+
+	else {
+		__rh_insert(rh, nreg);
+		if (nreg->state == RH_CLEAN) {
+			spin_lock_irq(&rh->region_lock);
+			list_add(&nreg->list, &rh->clean_regions);
+			spin_unlock_irq(&rh->region_lock);
+		}
+		reg = nreg;
+	}
+	write_unlock_irq(&rh->hash_lock);
+	read_lock(&rh->hash_lock);
+
+	return reg;
+}
+
+static inline struct region *__rh_find(struct region_hash *rh, region_t region)
+{
+	struct region *reg;
+
+	reg = __rh_lookup(rh, region);
+	if (!reg)
+		reg = __rh_alloc(rh, region);
+
+	return reg;
+}
+
+static int rh_state(struct region_hash *rh, region_t region, int may_block)
+{
+	int r;
+	struct region *reg;
+
+	read_lock(&rh->hash_lock);
+	reg = __rh_lookup(rh, region);
+	read_unlock(&rh->hash_lock);
+
+	if (reg)
+		return reg->state;
+
+	/*
+	 * The region wasn't in the hash, so we fall back to the
+	 * dirty log.
+	 */
+	r = rh->log->type->in_sync(rh->log, region, may_block);
+
+	/*
+	 * Any error from the dirty log (eg. -EWOULDBLOCK) gets
+	 * taken as a RH_NOSYNC
+	 */
+	return r == 1 ? RH_CLEAN : RH_NOSYNC;
+}
+
+static inline int rh_in_sync(struct region_hash *rh,
+			     region_t region, int may_block)
+{
+	int state = rh_state(rh, region, may_block);
+	return state == RH_CLEAN || state == RH_DIRTY;
+}
+
+static void dispatch_buffers(struct mirror_set *ms, struct buffer_head *bh)
+{
+	struct buffer_head *nbh;
+
+	while (bh) {
+		nbh = bh->b_reqnext;
+		queue_bh(ms, bh, WRITE);
+		bh = nbh;
+	}
+}
+
+static void rh_update_states(struct region_hash *rh)
+{
+	struct list_head *tmp, *tmp2;
+	struct region *reg;
+
+	LIST_HEAD(clean);
+	LIST_HEAD(recovered);
+
+	/*
+	 * Quickly grab the lists.
+	 */
+	write_lock_irq(&rh->hash_lock);
+	spin_lock(&rh->region_lock);
+	if (!list_empty(&rh->clean_regions)) {
+		list_splice(&rh->clean_regions, &clean);
+		INIT_LIST_HEAD(&rh->clean_regions);
+
+		list_for_each_entry (reg, &clean, list) {
+			rh->log->type->clear_region(rh->log, reg->key);
+			list_del(&reg->hash_list);
+		}
+	}
+
+	if (!list_empty(&rh->recovered_regions)) {
+		list_splice(&rh->recovered_regions, &recovered);
+		INIT_LIST_HEAD(&rh->recovered_regions);
+
+		list_for_each_entry (reg, &recovered, list)
+			list_del(&reg->hash_list);
+	}
+	spin_unlock(&rh->region_lock);
+	write_unlock_irq(&rh->hash_lock);
+
+	/*
+	 * All the regions on the recovered and clean lists have
+	 * now been pulled out of the system, so no need to do
+	 * any more locking.
+	 */
+	list_for_each_safe (tmp, tmp2, &recovered) {
+		reg = list_entry(tmp, struct region, list);
+
+		rh->log->type->complete_resync_work(rh->log, reg->key, 1);
+		dispatch_buffers(rh->ms, reg->delayed_bhs);
+		up(&rh->recovery_count);
+		mempool_free(reg, rh->region_pool);
+	}
+
+	list_for_each_safe (tmp, tmp2, &clean) {
+		reg = list_entry(tmp, struct region, list);
+		mempool_free(reg, rh->region_pool);
+	}
+}
+
+static void rh_inc(struct region_hash *rh, region_t region)
+{
+	struct region *reg;
+
+	read_lock(&rh->hash_lock);
+	reg = __rh_find(rh, region);
+	if (reg->state == RH_CLEAN) {
+		rh->log->type->mark_region(rh->log, reg->key);
+
+		spin_lock_irq(&rh->region_lock);
+		reg->state = RH_DIRTY;
+		list_del_init(&reg->list);	/* take off the clean list */
+		spin_unlock_irq(&rh->region_lock);
+	}
+
+	atomic_inc(&reg->pending);
+	read_unlock(&rh->hash_lock);
+}
+
+static void rh_inc_pending(struct region_hash *rh, struct buffer_list *buffers)
+{
+	struct buffer_head *bh;
+
+	for (bh = buffers->head; bh; bh = bh->b_reqnext)
+		rh_inc(rh, bh_to_region(rh, bh));
+}
+
+static void rh_dec(struct region_hash *rh, region_t region)
+{
+	unsigned long flags;
+	struct region *reg;
+	int wake = 0;
+
+	read_lock(&rh->hash_lock);
+	reg = __rh_lookup(rh, region);
+	read_unlock(&rh->hash_lock);
+
+	if (atomic_dec_and_test(&reg->pending)) {
+		spin_lock_irqsave(&rh->region_lock, flags);
+		if (reg->state == RH_RECOVERING) {
+			list_add_tail(&reg->list, &rh->quiesced_regions);
+		} else {
+			reg->state = RH_CLEAN;
+			list_add(&reg->list, &rh->clean_regions);
+		}
+		spin_unlock_irqrestore(&rh->region_lock, flags);
+		wake = 1;
+	}
+
+	if (wake)
+		dm_daemon_wake(&_kmirrord);
+}
+
+/*
+ * Starts quiescing a region in preparation for recovery.
+ */
+static int __rh_recovery_prepare(struct region_hash *rh)
+{
+	int r;
+	struct region *reg;
+	region_t region;
+
+	/*
+	 * Ask the dirty log what's next.
+	 */
+	r = rh->log->type->get_resync_work(rh->log, &region);
+	if (r <= 0)
+		return r;
+
+	/*
+	 * Get this region, and start it quiescing by setting the
+	 * recovering flag.
+	 */
+	read_lock(&rh->hash_lock);
+	reg = __rh_find(rh, region);
+	read_unlock(&rh->hash_lock);
+
+	spin_lock_irq(&rh->region_lock);
+	reg->state = RH_RECOVERING;
+
+	/* Already quiesced ? */
+	if (atomic_read(&reg->pending))
+		list_del_init(&reg->list);
+
+	else {
+		list_del_init(&reg->list);
+		list_add(&reg->list, &rh->quiesced_regions);
+	}
+	spin_unlock_irq(&rh->region_lock);
+
+	return 1;
+}
+
+static void rh_recovery_prepare(struct region_hash *rh)
+{
+	while (!down_trylock(&rh->recovery_count))
+		if (__rh_recovery_prepare(rh) <= 0) {
+			up(&rh->recovery_count);
+			break;
+		}
+}
+
+/*
+ * Returns any quiesced regions.
+ */
+static struct region *rh_recovery_start(struct region_hash *rh)
+{
+	struct region *reg = NULL;
+
+	spin_lock_irq(&rh->region_lock);
+	if (!list_empty(&rh->quiesced_regions)) {
+		reg = list_entry(rh->quiesced_regions.next,
+				 struct region, list);
+		list_del_init(&reg->list);	/* remove from the quiesced list */
+	}
+	spin_unlock_irq(&rh->region_lock);
+
+	return reg;
+}
+
+/* FIXME: success ignored for now */
+static void rh_recovery_end(struct region *reg, int success)
+{
+	struct region_hash *rh = reg->rh;
+
+	spin_lock_irq(&rh->region_lock);
+	list_add(&reg->list, &reg->rh->recovered_regions);
+	spin_unlock_irq(&rh->region_lock);
+
+	dm_daemon_wake(&_kmirrord);
+}
+
+static void rh_flush(struct region_hash *rh)
+{
+	rh->log->type->flush(rh->log);
+}
+
+static void rh_delay(struct region_hash *rh, struct buffer_head *bh)
+{
+	struct region *reg;
+
+	read_lock(&rh->hash_lock);
+	reg = __rh_find(rh, bh_to_region(rh, bh));
+	bh->b_reqnext = reg->delayed_bhs;
+	reg->delayed_bhs = bh;
+	read_unlock(&rh->hash_lock);
+}
+
+static void rh_stop_recovery(struct region_hash *rh)
+{
+	int i;
+
+	/* wait for any recovering regions */
+	for (i = 0; i < MAX_RECOVERY; i++)
+		down(&rh->recovery_count);
+}
+
+static void rh_start_recovery(struct region_hash *rh)
+{
+	int i;
+
+	for (i = 0; i < MAX_RECOVERY; i++)
+		up(&rh->recovery_count);
+
+	dm_daemon_wake(&_kmirrord);
+}
+
+/*-----------------------------------------------------------------
+ * Mirror set structures.
+ *---------------------------------------------------------------*/
+struct mirror {
+	atomic_t error_count;
+	struct dm_dev *dev;
+	sector_t offset;
+};
+
+struct mirror_set {
+	struct dm_target *ti;
+	struct list_head list;
+	struct region_hash rh;
+	struct kcopyd_client *kcopyd_client;
+
+	spinlock_t lock;	/* protects the next two lists */
+	struct buffer_list reads;
+	struct buffer_list writes;
+
+	/* recovery */
+	region_t nr_regions;
+	region_t sync_count;
+
+	unsigned int nr_mirrors;
+	struct mirror mirror[0];
+};
+
+/*
+ * Every mirror should look like this one.
+ */
+#define DEFAULT_MIRROR 0
+
+/*
+ * This is yucky.  We squirrel the mirror_set struct away inside
+ * b_reqnext for write buffers.  This is safe since the bh
+ * doesn't get submitted to the lower levels of block layer.
+ */
+static struct mirror_set *bh_get_ms(struct buffer_head *bh)
+{
+	return (struct mirror_set *) bh->b_reqnext;
+}
+
+static void bh_set_ms(struct buffer_head *bh, struct mirror_set *ms)
+{
+	bh->b_reqnext = (struct buffer_head *) ms;
+}
+
+/*-----------------------------------------------------------------
+ * Recovery.
+ *
+ * When a mirror is first activated we may find that some regions
+ * are in the no-sync state.  We have to recover these by
+ * recopying from the default mirror to all the others.
+ *---------------------------------------------------------------*/
+static void recovery_complete(int read_err, unsigned int write_err,
+			      void *context)
+{
+	struct region *reg = (struct region *) context;
+	struct mirror_set *ms = reg->rh->ms;
+
+	/* FIXME: better error handling */
+	rh_recovery_end(reg, read_err || write_err);
+	if (++ms->sync_count == ms->nr_regions)
+		/* the sync is complete */
+		dm_table_event(ms->ti->table);
+}
+
+static int recover(struct mirror_set *ms, struct region *reg)
+{
+	int r;
+	unsigned int i;
+	struct io_region from, to[ms->nr_mirrors - 1], *dest;
+	struct mirror *m;
+	unsigned int flags = 0;
+
+	/* fill in the source */
+	m = ms->mirror + DEFAULT_MIRROR;
+	from.dev = m->dev->dev;
+	from.sector = m->offset + region_to_sector(reg->rh, reg->key);
+	if (reg->key == (ms->nr_regions - 1)) {
+		/*
+		 * The final region may be smaller than
+		 * region_size.
+		 */
+		from.count = ms->ti->len & (reg->rh->region_size - 1);
+		if (!from.count)
+			from.count = reg->rh->region_size;
+	} else
+		from.count = reg->rh->region_size;
+
+	/* fill in the destinations */
+	for (i = 1; i < ms->nr_mirrors; i++) {
+		m = ms->mirror + i;
+		dest = to + (i - 1);
+
+		dest->dev = m->dev->dev;
+		dest->sector = m->offset + region_to_sector(reg->rh, reg->key);
+		dest->count = from.count;
+	}
+
+	/* hand to kcopyd */
+	set_bit(KCOPYD_IGNORE_ERROR, &flags);
+	r = kcopyd_copy(ms->kcopyd_client, &from, ms->nr_mirrors - 1, to, flags,
+			recovery_complete, reg);
+
+	return r;
+}
+
+static void do_recovery(struct mirror_set *ms)
+{
+	int r;
+	struct region *reg;
+
+	/*
+	 * Start quiescing some regions.
+	 */
+	rh_recovery_prepare(&ms->rh);
+
+	/*
+	 * Copy any already quiesced regions.
+	 */
+	while ((reg = rh_recovery_start(&ms->rh))) {
+		r = recover(ms, reg);
+		if (r)
+			rh_recovery_end(reg, 0);
+	}
+}
+
+/*-----------------------------------------------------------------
+ * Reads
+ *---------------------------------------------------------------*/
+static struct mirror *choose_mirror(struct mirror_set *ms, sector_t sector)
+{
+	/* FIXME: add read balancing */
+	return ms->mirror + DEFAULT_MIRROR;
+}
+
+/*
+ * remap a buffer to a particular mirror.
+ */
+static void map_buffer(struct mirror_set *ms,
+		       struct mirror *m, struct buffer_head *bh)
+{
+	bh->b_rdev = m->dev->dev;
+	bh->b_rsector = m->offset + (bh->b_rsector - ms->ti->begin);
+}
+
+static void do_reads(struct mirror_set *ms, struct buffer_list *reads)
+{
+	region_t region;
+	struct buffer_head *bh;
+	struct mirror *m;
+
+	while ((bh = buffer_list_pop(reads))) {
+		region = bh_to_region(&ms->rh, bh);
+
+		/*
+		 * We can only read balance if the region is in sync.
+		 */
+		if (rh_in_sync(&ms->rh, region, 0))
+			m = choose_mirror(ms, bh->b_rsector);
+		else
+			m = ms->mirror + DEFAULT_MIRROR;
+
+		map_buffer(ms, m, bh);
+		generic_make_request(READ, bh);
+	}
+}
+
+/*-----------------------------------------------------------------
+ * Writes.
+ *
+ * We do different things with the write io depending on the
+ * state of the region that it's in:
+ *
+ * SYNC: 	increment pending, use kcopyd to write to *all* mirrors
+ * RECOVERING:	delay the io until recovery completes
+ * NOSYNC:	increment pending, just write to the default mirror
+ *---------------------------------------------------------------*/
+static void write_callback(unsigned int error, void *context)
+{
+	unsigned int i;
+	int uptodate = 1;
+	struct buffer_head *bh = (struct buffer_head *) context;
+	struct mirror_set *ms;
+
+	ms = bh_get_ms(bh);
+	bh_set_ms(bh, NULL);
+
+	/*
+	 * NOTE: We don't decrement the pending count here,
+	 * instead it is done by the targets endio function.
+	 * This way we handle both writes to SYNC and NOSYNC
+	 * regions with the same code.
+	 */
+
+	if (error) {
+		/*
+		 * only error the io if all mirrors failed.
+		 * FIXME: bogus
+		 */
+		uptodate = 0;
+		for (i = 0; i < ms->nr_mirrors; i++)
+			if (!test_bit(i, &error)) {
+				uptodate = 1;
+				break;
+			}
+	}
+	bh->b_end_io(bh, uptodate);
+}
+
+static void do_write(struct mirror_set *ms, struct buffer_head *bh)
+{
+	unsigned int i;
+	struct io_region io[ms->nr_mirrors];
+	struct mirror *m;
+
+	for (i = 0; i < ms->nr_mirrors; i++) {
+		m = ms->mirror + i;
+
+		io[i].dev = m->dev->dev;
+		io[i].sector = m->offset + (bh->b_rsector - ms->ti->begin);
+		io[i].count = bh->b_size >> 9;
+	}
+
+	bh_set_ms(bh, ms);
+	dm_io_async(ms->nr_mirrors, io, WRITE, bh->b_page,
+		    (unsigned int) bh->b_data & ~PAGE_MASK, write_callback, bh);
+}
+
+static void do_writes(struct mirror_set *ms, struct buffer_list *writes)
+{
+	int state;
+	struct buffer_head *bh;
+	struct buffer_list sync, nosync, recover, *this_list = NULL;
+
+	if (!writes->head)
+		return;
+
+	/*
+	 * Classify each write.
+	 */
+	buffer_list_init(&sync);
+	buffer_list_init(&nosync);
+	buffer_list_init(&recover);
+
+	while ((bh = buffer_list_pop(writes))) {
+		state = rh_state(&ms->rh, bh_to_region(&ms->rh, bh), 1);
+		switch (state) {
+		case RH_CLEAN:
+		case RH_DIRTY:
+			this_list = &sync;
+			break;
+
+		case RH_NOSYNC:
+			this_list = &nosync;
+			break;
+
+		case RH_RECOVERING:
+			this_list = &recover;
+			break;
+		}
+
+		buffer_list_add(this_list, bh);
+	}
+
+	/*
+	 * Increment the pending counts for any regions that will
+	 * be written to (writes to recover regions are going to
+	 * be delayed).
+	 */
+	rh_inc_pending(&ms->rh, &sync);
+	rh_inc_pending(&ms->rh, &nosync);
+	rh_flush(&ms->rh);
+
+	/*
+	 * Dispatch io.
+	 */
+	while ((bh = buffer_list_pop(&sync)))
+		do_write(ms, bh);
+
+	while ((bh = buffer_list_pop(&recover)))
+		rh_delay(&ms->rh, bh);
+
+	while ((bh = buffer_list_pop(&nosync))) {
+		map_buffer(ms, ms->mirror + DEFAULT_MIRROR, bh);
+		generic_make_request(WRITE, bh);
+	}
+}
+
+/*-----------------------------------------------------------------
+ * kmirrord
+ *---------------------------------------------------------------*/
+static LIST_HEAD(_mirror_sets);
+static DECLARE_RWSEM(_mirror_sets_lock);
+
+static void do_mirror(struct mirror_set *ms)
+{
+	struct buffer_list reads, writes;
+
+	spin_lock(&ms->lock);
+	memcpy(&reads, &ms->reads, sizeof(reads));
+	buffer_list_init(&ms->reads);
+	memcpy(&writes, &ms->writes, sizeof(writes));
+	buffer_list_init(&ms->writes);
+	spin_unlock(&ms->lock);
+
+	rh_update_states(&ms->rh);
+	do_recovery(ms);
+	do_reads(ms, &reads);
+	do_writes(ms, &writes);
+	run_task_queue(&tq_disk);
+}
+
+static void do_work(void)
+{
+	struct mirror_set *ms;
+
+	down_read(&_mirror_sets_lock);
+	list_for_each_entry (ms, &_mirror_sets, list)
+		do_mirror(ms);
+	up_read(&_mirror_sets_lock);
+}
+
+/*-----------------------------------------------------------------
+ * Target functions
+ *---------------------------------------------------------------*/
+static struct mirror_set *alloc_context(unsigned int nr_mirrors,
+					sector_t region_size,
+					struct dm_target *ti,
+					struct dirty_log *dl)
+{
+	size_t len;
+	struct mirror_set *ms = NULL;
+
+	if (array_too_big(sizeof(*ms), sizeof(ms->mirror[0]), nr_mirrors))
+		return NULL;
+
+	len = sizeof(*ms) + (sizeof(ms->mirror[0]) * nr_mirrors);
+
+	ms = kmalloc(len, GFP_KERNEL);
+	if (!ms) {
+		ti->error = "dm-mirror: Cannot allocate mirror context";
+		return NULL;
+	}
+
+	memset(ms, 0, len);
+	spin_lock_init(&ms->lock);
+
+	ms->ti = ti;
+	ms->nr_mirrors = nr_mirrors;
+	ms->nr_regions = dm_div_up(ti->len, region_size);
+
+	if (rh_init(&ms->rh, ms, dl, region_size, ms->nr_regions)) {
+		ti->error = "dm-mirror: Error creating dirty region hash";
+		kfree(ms);
+		return NULL;
+	}
+
+	return ms;
+}
+
+static void free_context(struct mirror_set *ms, struct dm_target *ti,
+			 unsigned int m)
+{
+	while (m--)
+		dm_put_device(ti, ms->mirror[m].dev);
+
+	rh_exit(&ms->rh);
+	kfree(ms);
+}
+
+static inline int _check_region_size(struct dm_target *ti, sector_t size)
+{
+	return !(size % (PAGE_SIZE >> 9) || (size & (size - 1)) ||
+		 size > ti->len);
+}
+
+static int get_mirror(struct mirror_set *ms, struct dm_target *ti,
+		      unsigned int mirror, char **argv)
+{
+	sector_t offset;
+
+	if (sscanf(argv[1], SECTOR_FORMAT, &offset) != 1) {
+		ti->error = "dm-mirror: Invalid offset";
+		return -EINVAL;
+	}
+
+	if (dm_get_device(ti, argv[0], offset, ti->len,
+			  dm_table_get_mode(ti->table),
+			  &ms->mirror[mirror].dev)) {
+		ti->error = "dm-mirror: Device lookup failure";
+		return -ENXIO;
+	}
+
+	ms->mirror[mirror].offset = offset;
+
+	return 0;
+}
+
+static int add_mirror_set(struct mirror_set *ms)
+{
+	down_write(&_mirror_sets_lock);
+	list_add_tail(&ms->list, &_mirror_sets);
+	up_write(&_mirror_sets_lock);
+	dm_daemon_wake(&_kmirrord);
+
+	return 0;
+}
+
+static void del_mirror_set(struct mirror_set *ms)
+{
+	down_write(&_mirror_sets_lock);
+	list_del(&ms->list);
+	up_write(&_mirror_sets_lock);
+}
+
+/*
+ * Create dirty log: log_type #log_params <log_params>
+ */
+static struct dirty_log *create_dirty_log(struct dm_target *ti,
+					  unsigned int argc, char **argv,
+					  unsigned int *args_used)
+{
+	unsigned int param_count;
+	struct dirty_log *dl;
+
+	if (argc < 2) {
+		ti->error = "dm-mirror: Insufficient mirror log arguments";
+		return NULL;
+	}
+
+	if (sscanf(argv[1], "%u", &param_count) != 1 || param_count != 1) {
+		ti->error = "dm-mirror: Invalid mirror log argument count";
+		return NULL;
+	}
+
+	*args_used = 2 + param_count;
+
+	if (argc < *args_used) {
+		ti->error = "dm-mirror: Insufficient mirror log arguments";
+		return NULL;
+	}
+
+	dl = dm_create_dirty_log(argv[0], ti->len, param_count, argv + 2);
+	if (!dl) {
+		ti->error = "dm-mirror: Error creating mirror dirty log";
+		return NULL;
+	}
+
+	if (!_check_region_size(ti, dl->type->get_region_size(dl))) {
+		ti->error = "dm-mirror: Invalid region size";
+		dm_destroy_dirty_log(dl);
+		return NULL;
+	}
+
+	return dl;
+}
+
+/*
+ * Construct a mirror mapping:
+ *
+ * log_type #log_params <log_params>
+ * #mirrors [mirror_path offset]{2,}
+ *
+ * For now, #log_params = 1, log_type = "core"
+ *
+ */
+#define DM_IO_PAGES 64
+static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+	int r;
+	unsigned int nr_mirrors, m, args_used;
+	struct mirror_set *ms;
+	struct dirty_log *dl;
+
+	dl = create_dirty_log(ti, argc, argv, &args_used);
+	if (!dl)
+		return -EINVAL;
+
+	argv += args_used;
+	argc -= args_used;
+
+	if (!argc || sscanf(argv[0], "%u", &nr_mirrors) != 1 ||
+	    nr_mirrors < 2) {
+		ti->error = "dm-mirror: Invalid number of mirrors";
+		dm_destroy_dirty_log(dl);
+		return -EINVAL;
+	}
+
+	argv++, argc--;
+
+	if (argc != nr_mirrors * 2) {
+		ti->error = "dm-mirror: Wrong number of mirror arguments";
+		dm_destroy_dirty_log(dl);
+		return -EINVAL;
+	}
+
+	ms = alloc_context(nr_mirrors, dl->type->get_region_size(dl), ti, dl);
+	if (!ms) {
+		dm_destroy_dirty_log(dl);
+		return -ENOMEM;
+	}
+
+	/* Get the mirror parameter sets */
+	for (m = 0; m < nr_mirrors; m++) {
+		r = get_mirror(ms, ti, m, argv);
+		if (r) {
+			free_context(ms, ti, m);
+			return r;
+		}
+		argv += 2;
+		argc -= 2;
+	}
+
+	ti->private = ms;
+
+	r = kcopyd_client_create(DM_IO_PAGES, &ms->kcopyd_client);
+	if (r) {
+		free_context(ms, ti, ms->nr_mirrors);
+		return r;
+	}
+
+	add_mirror_set(ms);
+	return 0;
+}
+
+static void mirror_dtr(struct dm_target *ti)
+{
+	struct mirror_set *ms = (struct mirror_set *) ti->private;
+
+	del_mirror_set(ms);
+	kcopyd_client_destroy(ms->kcopyd_client);
+	free_context(ms, ti, ms->nr_mirrors);
+}
+
+static void queue_bh(struct mirror_set *ms, struct buffer_head *bh, int rw)
+{
+	int wake = 0;
+	struct buffer_list *bl;
+
+	bl = (rw == WRITE) ? &ms->writes : &ms->reads;
+	spin_lock(&ms->lock);
+	wake = !(bl->head);
+	buffer_list_add(bl, bh);
+	spin_unlock(&ms->lock);
+
+	if (wake)
+		dm_daemon_wake(&_kmirrord);
+}
+
+/*
+ * Mirror mapping function
+ */
+static int mirror_map(struct dm_target *ti, struct buffer_head *bh,
+		      int rw, union map_info *map_context)
+{
+	int r;
+	struct mirror *m;
+	struct mirror_set *ms = ti->private;
+
+	/* FIXME: nasty hack, 32 bit sector_t only */
+	map_context->ll = bh->b_rsector / ms->rh.region_size;
+
+	if (rw == WRITE) {
+		queue_bh(ms, bh, rw);
+		return 0;
+	}
+
+	r = ms->rh.log->type->in_sync(ms->rh.log, bh_to_region(&ms->rh, bh), 0);
+	if (r < 0 && r != -EWOULDBLOCK)
+		return r;
+
+	if (r == -EWOULDBLOCK)	/* FIXME: ugly */
+		r = 0;
+
+	/*
+	 * We don't want to fast track a recovery just for a read
+	 * ahead.  So we just let it silently fail.
+	 * FIXME: get rid of this.
+	 */
+	if (!r && rw == READA)
+		return -EIO;
+
+	if (!r) {
+		/* Pass this io over to the daemon */
+		queue_bh(ms, bh, rw);
+		return 0;
+	}
+
+	m = choose_mirror(ms, bh->b_rsector);
+	if (!m)
+		return -EIO;
+
+	map_buffer(ms, m, bh);
+	return 1;
+}
+
+static int mirror_end_io(struct dm_target *ti, struct buffer_head *bh,
+			 int rw, int error, union map_info *map_context)
+{
+	struct mirror_set *ms = (struct mirror_set *) ti->private;
+	region_t region = map_context->ll;
+
+	/*
+	 * We need to dec pending if this was a write.
+	 */
+	if (rw == WRITE)
+		rh_dec(&ms->rh, region);
+
+	return 0;
+}
+
+static void mirror_suspend(struct dm_target *ti)
+{
+	struct mirror_set *ms = (struct mirror_set *) ti->private;
+	rh_stop_recovery(&ms->rh);
+}
+
+static void mirror_resume(struct dm_target *ti)
+{
+	struct mirror_set *ms = (struct mirror_set *) ti->private;
+	rh_start_recovery(&ms->rh);
+}
+
+static int mirror_status(struct dm_target *ti, status_type_t type,
+			 char *result, unsigned int maxlen)
+{
+	unsigned int m, sz = 0;
+	struct mirror_set *ms = (struct mirror_set *) ti->private;
+
+	switch (type) {
+	case STATUSTYPE_INFO:
+		sz += snprintf(result + sz, maxlen - sz, "%d ", ms->nr_mirrors);
+
+		for (m = 0; m < ms->nr_mirrors; m++)
+			sz += snprintf(result + sz, maxlen - sz, "%s ",
+				       dm_kdevname(ms->mirror[m].dev->dev));
+
+		sz += snprintf(result + sz, maxlen - sz, "%lu/%lu",
+			       ms->sync_count, ms->nr_regions);
+		break;
+
+	case STATUSTYPE_TABLE:
+		sz += snprintf(result + sz, maxlen - sz,
+			       "%s 1 " SECTOR_FORMAT " %d ",
+			       ms->rh.log->type->name, ms->rh.region_size,
+			       ms->nr_mirrors);
+
+		for (m = 0; m < ms->nr_mirrors; m++)
+			sz += snprintf(result + sz, maxlen - sz, "%s %ld ",
+				       dm_kdevname(ms->mirror[m].dev->dev),
+				       ms->mirror[m].offset);
+	}
+
+	return 0;
+}
+
+static struct target_type mirror_target = {
+	.name	 = "mirror",
+	.module	 = THIS_MODULE,
+	.ctr	 = mirror_ctr,
+	.dtr	 = mirror_dtr,
+	.map	 = mirror_map,
+	.end_io	 = mirror_end_io,
+	.suspend = mirror_suspend,
+	.resume	 = mirror_resume,
+	.status	 = mirror_status,
+};
+
+static int __init dm_mirror_init(void)
+{
+	int r;
+
+	r = dm_dirty_log_init();
+	if (r)
+		return r;
+
+	r = dm_daemon_start(&_kmirrord, "kmirrord", do_work);
+	if (r) {
+		DMERR("couldn't start kmirrord");
+		dm_dirty_log_exit();
+		return r;
+	}
+
+	r = dm_register_target(&mirror_target);
+	if (r < 0) {
+		DMERR("%s: Failed to register mirror target",
+		      mirror_target.name);
+		dm_dirty_log_exit();
+		dm_daemon_stop(&_kmirrord);
+	}
+
+	return r;
+}
+
+static void __exit dm_mirror_exit(void)
+{
+	int r;
+
+	r = dm_unregister_target(&mirror_target);
+	if (r < 0)
+		DMERR("%s: unregister failed %d", mirror_target.name, r);
+
+	dm_daemon_stop(&_kmirrord);
+	dm_dirty_log_exit();
+}
+
+/* Module hooks */
+module_init(dm_mirror_init);
+module_exit(dm_mirror_exit);
+
+MODULE_DESCRIPTION(DM_NAME " mirror target");
+MODULE_AUTHOR("Heinz Mauelshagen <mge@sistina.com>");
+MODULE_LICENSE("GPL");
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/dm-snapshot.c linux-2.4.27-leo/drivers/md/dm-snapshot.c
--- linux-2.4.27/drivers/md/dm-snapshot.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/md/dm-snapshot.c	2004-09-17 03:19:08.000000000 +0100
@@ -0,0 +1,1237 @@
+/*
+ * dm-snapshot.c
+ *
+ * Copyright (C) 2001-2002 Sistina Software (UK) Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#include <linux/config.h>
+#include <linux/ctype.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/fs.h>
+#include <linux/blkdev.h>
+#include <linux/mempool.h>
+#include <linux/device-mapper.h>
+#include <linux/vmalloc.h>
+
+#include "dm-snapshot.h"
+#include "kcopyd.h"
+
+/*
+ * FIXME: Remove this before release.
+ */
+#if 0
+#define DMDEBUG(x...) DMWARN( ## x)
+#else
+#define DMDEBUG(x...)
+#endif
+
+/*
+ * The percentage increment we will wake up users at
+ */
+#define WAKE_UP_PERCENT 5
+
+/*
+ * kcopyd priority of snapshot operations
+ */
+#define SNAPSHOT_COPY_PRIORITY 2
+
+/*
+ * Each snapshot reserves this many pages for io
+ * FIXME: calculate this
+ */
+#define SNAPSHOT_PAGES 256
+
+struct pending_exception {
+	struct exception e;
+
+	/*
+	 * Origin buffers waiting for this to complete are held
+	 * in a list (using b_reqnext).
+	 */
+	struct buffer_head *origin_bhs;
+	struct buffer_head *snapshot_bhs;
+
+	/*
+	 * Other pending_exceptions that are processing this
+	 * chunk.  When this list is empty, we know we can
+	 * complete the origins.
+	 */
+	struct list_head siblings;
+
+	/* Pointer back to snapshot context */
+	struct dm_snapshot *snap;
+
+	/*
+	 * 1 indicates the exception has already been sent to
+	 * kcopyd.
+	 */
+	int started;
+};
+
+/*
+ * Hash table mapping origin volumes to lists of snapshots and
+ * a lock to protect it
+ */
+static kmem_cache_t *exception_cache;
+static kmem_cache_t *pending_cache;
+static mempool_t *pending_pool;
+
+/*
+ * One of these per registered origin, held in the snapshot_origins hash
+ */
+struct origin {
+	/* The origin device */
+	kdev_t dev;
+
+	struct list_head hash_list;
+
+	/* List of snapshots for this origin */
+	struct list_head snapshots;
+};
+
+/*
+ * Size of the hash table for origin volumes. If we make this
+ * the size of the minors list then it should be nearly perfect
+ */
+#define ORIGIN_HASH_SIZE 256
+#define ORIGIN_MASK      0xFF
+static struct list_head *_origins;
+static struct rw_semaphore _origins_lock;
+
+static int init_origin_hash(void)
+{
+	int i;
+
+	_origins = kmalloc(ORIGIN_HASH_SIZE * sizeof(struct list_head),
+			   GFP_KERNEL);
+	if (!_origins) {
+		DMERR("Device mapper: Snapshot: unable to allocate memory");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < ORIGIN_HASH_SIZE; i++)
+		INIT_LIST_HEAD(_origins + i);
+	init_rwsem(&_origins_lock);
+
+	return 0;
+}
+
+static void exit_origin_hash(void)
+{
+	kfree(_origins);
+}
+
+static inline unsigned int origin_hash(kdev_t dev)
+{
+	return MINOR(dev) & ORIGIN_MASK;
+}
+
+static struct origin *__lookup_origin(kdev_t origin)
+{
+	struct list_head *slist;
+	struct list_head *ol;
+	struct origin *o;
+
+	ol = &_origins[origin_hash(origin)];
+	list_for_each(slist, ol) {
+		o = list_entry(slist, struct origin, hash_list);
+
+		if (o->dev == origin)
+			return o;
+	}
+
+	return NULL;
+}
+
+static void __insert_origin(struct origin *o)
+{
+	struct list_head *sl = &_origins[origin_hash(o->dev)];
+	list_add_tail(&o->hash_list, sl);
+}
+
+/*
+ * Make a note of the snapshot and its origin so we can look it
+ * up when the origin has a write on it.
+ */
+static int register_snapshot(struct dm_snapshot *snap)
+{
+	struct origin *o;
+	kdev_t dev = snap->origin->dev;
+
+	down_write(&_origins_lock);
+	o = __lookup_origin(dev);
+
+	if (!o) {
+		/* New origin */
+		o = kmalloc(sizeof(*o), GFP_KERNEL);
+		if (!o) {
+			up_write(&_origins_lock);
+			return -ENOMEM;
+		}
+
+		/* Initialise the struct */
+		INIT_LIST_HEAD(&o->snapshots);
+		o->dev = dev;
+
+		__insert_origin(o);
+	}
+
+	list_add_tail(&snap->list, &o->snapshots);
+
+	up_write(&_origins_lock);
+	return 0;
+}
+
+static void unregister_snapshot(struct dm_snapshot *s)
+{
+	struct origin *o;
+
+	down_write(&_origins_lock);
+	o = __lookup_origin(s->origin->dev);
+
+	list_del(&s->list);
+	if (list_empty(&o->snapshots)) {
+		list_del(&o->hash_list);
+		kfree(o);
+	}
+
+	up_write(&_origins_lock);
+}
+
+/*
+ * Implementation of the exception hash tables.
+ */
+static int init_exception_table(struct exception_table *et, uint32_t size)
+{
+	unsigned int i;
+
+	et->hash_mask = size - 1;
+	et->table = vcalloc(size, sizeof(struct list_head));
+	if (!et->table)
+		return -ENOMEM;
+
+	for (i = 0; i < size; i++)
+		INIT_LIST_HEAD(et->table + i);
+
+	return 0;
+}
+
+static void exit_exception_table(struct exception_table *et, kmem_cache_t *mem)
+{
+	struct list_head *slot, *entry, *temp;
+	struct exception *ex;
+	int i, size;
+
+	size = et->hash_mask + 1;
+	for (i = 0; i < size; i++) {
+		slot = et->table + i;
+
+		list_for_each_safe(entry, temp, slot) {
+			ex = list_entry(entry, struct exception, hash_list);
+			kmem_cache_free(mem, ex);
+		}
+	}
+
+	vfree(et->table);
+}
+
+/*
+ * FIXME: check how this hash fn is performing.
+ */
+static inline uint32_t exception_hash(struct exception_table *et, chunk_t chunk)
+{
+	return chunk & et->hash_mask;
+}
+
+static void insert_exception(struct exception_table *eh, struct exception *e)
+{
+	struct list_head *l = &eh->table[exception_hash(eh, e->old_chunk)];
+	list_add(&e->hash_list, l);
+}
+
+static inline void remove_exception(struct exception *e)
+{
+	list_del(&e->hash_list);
+}
+
+/*
+ * Return the exception data for a sector, or NULL if not
+ * remapped.
+ */
+static struct exception *lookup_exception(struct exception_table *et,
+					  chunk_t chunk)
+{
+	struct list_head *slot, *el;
+	struct exception *e;
+
+	slot = &et->table[exception_hash(et, chunk)];
+	list_for_each(el, slot) {
+		e = list_entry(el, struct exception, hash_list);
+		if (e->old_chunk == chunk)
+			return e;
+	}
+
+	return NULL;
+}
+
+static inline struct exception *alloc_exception(void)
+{
+	struct exception *e;
+
+	e = kmem_cache_alloc(exception_cache, GFP_NOIO);
+	if (!e)
+		e = kmem_cache_alloc(exception_cache, GFP_ATOMIC);
+
+	return e;
+}
+
+static inline void free_exception(struct exception *e)
+{
+	kmem_cache_free(exception_cache, e);
+}
+
+static inline struct pending_exception *alloc_pending_exception(void)
+{
+	return mempool_alloc(pending_pool, GFP_NOIO);
+}
+
+static inline void free_pending_exception(struct pending_exception *pe)
+{
+	mempool_free(pe, pending_pool);
+}
+
+int dm_add_exception(struct dm_snapshot *s, chunk_t old, chunk_t new)
+{
+	struct exception *e;
+
+	e = alloc_exception();
+	if (!e)
+		return -ENOMEM;
+
+	e->old_chunk = old;
+	e->new_chunk = new;
+	insert_exception(&s->complete, e);
+	return 0;
+}
+
+/*
+ * Hard coded magic.
+ */
+static int calc_max_buckets(void)
+{
+	unsigned long mem;
+
+	mem = num_physpages << PAGE_SHIFT;
+	mem /= 50;
+	mem /= sizeof(struct list_head);
+
+	return mem;
+}
+
+/*
+ * Rounds a number down to a power of 2.
+ */
+static inline uint32_t round_down(uint32_t n)
+{
+	while (n & (n - 1))
+		n &= (n - 1);
+	return n;
+}
+
+/*
+ * Allocate room for a suitable hash table.
+ */
+static int init_hash_tables(struct dm_snapshot *s)
+{
+	sector_t hash_size, cow_dev_size, origin_dev_size, max_buckets;
+
+	/*
+	 * Calculate based on the size of the original volume or
+	 * the COW volume...
+	 */
+	cow_dev_size = get_dev_size(s->cow->dev);
+	origin_dev_size = get_dev_size(s->origin->dev);
+	max_buckets = calc_max_buckets();
+
+	hash_size = min(origin_dev_size, cow_dev_size) / s->chunk_size;
+	hash_size = min(hash_size, max_buckets);
+
+	/* Round it down to a power of 2 */
+	hash_size = round_down(hash_size);
+	if (init_exception_table(&s->complete, hash_size))
+		return -ENOMEM;
+
+	/*
+	 * Allocate hash table for in-flight exceptions
+	 * Make this smaller than the real hash table
+	 */
+	hash_size >>= 3;
+	if (!hash_size)
+		hash_size = 64;
+
+	if (init_exception_table(&s->pending, hash_size)) {
+		exit_exception_table(&s->complete, exception_cache);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/*
+ * Round a number up to the nearest 'size' boundary.  size must
+ * be a power of 2.
+ */
+static inline ulong round_up(ulong n, ulong size)
+{
+	size--;
+	return (n + size) & ~size;
+}
+
+/*
+ * Construct a snapshot mapping: <origin_dev> <COW-dev> <p/n> <chunk-size>
+ */
+static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+	struct dm_snapshot *s;
+	unsigned long chunk_size;
+	int r = -EINVAL;
+	char persistent;
+	char *origin_path;
+	char *cow_path;
+	char *value;
+	int blocksize;
+
+	if (argc < 4) {
+		ti->error = "dm-snapshot: requires exactly 4 arguments";
+		r = -EINVAL;
+		goto bad1;
+	}
+
+	origin_path = argv[0];
+	cow_path = argv[1];
+	persistent = toupper(*argv[2]);
+
+	if (persistent != 'P' && persistent != 'N') {
+		ti->error = "Persistent flag is not P or N";
+		r = -EINVAL;
+		goto bad1;
+	}
+
+	chunk_size = simple_strtoul(argv[3], &value, 10);
+	if (chunk_size == 0 || value == NULL) {
+		ti->error = "Invalid chunk size";
+		r = -EINVAL;
+		goto bad1;
+	}
+
+	s = kmalloc(sizeof(*s), GFP_KERNEL);
+	if (s == NULL) {
+		ti->error = "Cannot allocate snapshot context private "
+		    "structure";
+		r = -ENOMEM;
+		goto bad1;
+	}
+
+	r = dm_get_device(ti, origin_path, 0, ti->len, FMODE_READ, &s->origin);
+	if (r) {
+		ti->error = "Cannot get origin device";
+		goto bad2;
+	}
+
+	/* FIXME: get cow length */
+	r = dm_get_device(ti, cow_path, 0, 0,
+			  FMODE_READ | FMODE_WRITE, &s->cow);
+	if (r) {
+		dm_put_device(ti, s->origin);
+		ti->error = "Cannot get COW device";
+		goto bad2;
+	}
+
+	/*
+	 * Chunk size must be multiple of page size.  Silently
+	 * round up if it's not.
+	 */
+	chunk_size = round_up(chunk_size, PAGE_SIZE / SECTOR_SIZE);
+
+	/* Validate the chunk size against the device block size */
+	blocksize = get_hardsect_size(s->cow->dev);
+	if (chunk_size % (blocksize / SECTOR_SIZE)) {
+		ti->error = "Chunk size is not a multiple of device blocksize";
+		r = -EINVAL;
+		goto bad3;
+	}
+
+	/* Check the sizes are small enough to fit in one kiovec */
+	if (chunk_size > KIO_MAX_SECTORS) {
+		ti->error = "Chunk size is too big";
+		r = -EINVAL;
+		goto bad3;
+	}
+
+	/* Check chunk_size is a power of 2 */
+	if (chunk_size & (chunk_size - 1)) {
+		ti->error = "Chunk size is not a power of 2";
+		r = -EINVAL;
+		goto bad3;
+	}
+
+	s->chunk_size = chunk_size;
+	s->chunk_mask = chunk_size - 1;
+	s->type = persistent;
+	for (s->chunk_shift = 0; chunk_size;
+	     s->chunk_shift++, chunk_size >>= 1)
+		;
+	s->chunk_shift--;
+
+	s->valid = 1;
+	s->have_metadata = 0;
+	s->last_percent = 0;
+	init_rwsem(&s->lock);
+	s->table = ti->table;
+
+	/* Allocate hash table for COW data */
+	if (init_hash_tables(s)) {
+		ti->error = "Unable to allocate hash table space";
+		r = -ENOMEM;
+		goto bad3;
+	}
+
+	/*
+	 * Check the persistent flag - done here because we need the iobuf
+	 * to check the LV header
+	 */
+	s->store.snap = s;
+
+	if (persistent == 'P')
+		r = dm_create_persistent(&s->store, s->chunk_size);
+	else
+		r = dm_create_transient(&s->store, s, blocksize);
+
+	if (r) {
+		ti->error = "Couldn't create exception store";
+		r = -EINVAL;
+		goto bad4;
+	}
+
+	r = kcopyd_client_create(SNAPSHOT_PAGES, &s->kcopyd_client);
+	if (r) {
+		ti->error = "Could not create kcopyd client";
+		goto bad5;
+	}
+
+	/* Flush IO to the origin device */
+	fsync_dev_lockfs(s->origin->dev);
+
+	/* Add snapshot to the list of snapshots for this origin */
+	if (register_snapshot(s)) {
+		r = -EINVAL;
+		ti->error = "Cannot register snapshot origin";
+		goto bad6;
+	}
+	unlockfs(s->origin->dev);
+
+	ti->private = s;
+	return 0;
+
+ bad6:
+	unlockfs(s->origin->dev);
+	kcopyd_client_destroy(s->kcopyd_client);
+
+ bad5:
+	s->store.destroy(&s->store);
+
+ bad4:
+	exit_exception_table(&s->pending, pending_cache);
+	exit_exception_table(&s->complete, exception_cache);
+
+ bad3:
+	dm_put_device(ti, s->cow);
+	dm_put_device(ti, s->origin);
+
+ bad2:
+	kfree(s);
+
+ bad1:
+	return r;
+}
+
+static void snapshot_dtr(struct dm_target *ti)
+{
+	struct dm_snapshot *s = (struct dm_snapshot *) ti->private;
+
+	dm_table_event(ti->table);
+
+	unregister_snapshot(s);
+
+	exit_exception_table(&s->pending, pending_cache);
+	exit_exception_table(&s->complete, exception_cache);
+
+	/* Deallocate memory used */
+	s->store.destroy(&s->store);
+
+	dm_put_device(ti, s->origin);
+	dm_put_device(ti, s->cow);
+	kcopyd_client_destroy(s->kcopyd_client);
+	kfree(s);
+}
+
+/*
+ * We hold lists of buffer_heads, using the b_reqnext field.
+ */
+static void queue_buffer(struct buffer_head **queue, struct buffer_head *bh)
+{
+	bh->b_reqnext = *queue;
+	*queue = bh;
+}
+
+/*
+ * FIXME: inefficient.
+ */
+static void queue_buffers(struct buffer_head **queue, struct buffer_head *bhs)
+{
+	while (*queue)
+		queue = &((*queue)->b_reqnext);
+
+	*queue = bhs;
+}
+
+/*
+ * Flush a list of buffers.
+ */
+static void flush_buffers(struct buffer_head *bh)
+{
+	struct buffer_head *n;
+
+	DMDEBUG("begin flush");
+	while (bh) {
+		n = bh->b_reqnext;
+		bh->b_reqnext = NULL;
+		DMDEBUG("flushing %p", bh);
+		generic_make_request(WRITE, bh);
+		bh = n;
+	}
+
+	run_task_queue(&tq_disk);
+}
+
+/*
+ * Error a list of buffers.
+ */
+static void error_buffers(struct buffer_head *bh)
+{
+	struct buffer_head *n;
+
+	while (bh) {
+		n = bh->b_reqnext;
+		bh->b_reqnext = NULL;
+		buffer_IO_error(bh);
+		bh = n;
+	}
+}
+
+static struct buffer_head *__flush_bhs(struct pending_exception *pe)
+{
+	struct pending_exception *sibling;
+
+	if (list_empty(&pe->siblings))
+		return pe->origin_bhs;
+
+	sibling = list_entry(pe->siblings.next,
+			     struct pending_exception, siblings);
+
+	list_del(&pe->siblings);
+
+	/* FIXME: I think there's a race on SMP machines here, add spin lock */
+	queue_buffers(&sibling->origin_bhs, pe->origin_bhs);
+
+	return NULL;
+}
+
+static void pending_complete(struct pending_exception *pe, int success)
+{
+	struct exception *e;
+	struct dm_snapshot *s = pe->snap;
+	struct buffer_head *flush = NULL;
+
+	if (success) {
+		e = alloc_exception();
+		if (!e) {
+			DMWARN("Unable to allocate exception.");
+			down_write(&s->lock);
+			s->store.drop_snapshot(&s->store);
+			s->valid = 0;
+			flush = __flush_bhs(pe);
+			up_write(&s->lock);
+
+			error_buffers(pe->snapshot_bhs);
+			goto out;
+		}
+
+		/*
+		 * Add a proper exception, and remove the
+		 * in-flight exception from the list.
+		 */
+		down_write(&s->lock);
+
+		memcpy(e, &pe->e, sizeof(*e));
+		insert_exception(&s->complete, e);
+		remove_exception(&pe->e);
+		flush = __flush_bhs(pe);
+
+		/* Submit any pending write BHs */
+		up_write(&s->lock);
+
+		flush_buffers(pe->snapshot_bhs);
+		DMDEBUG("Exception completed successfully.");
+
+		/* Notify any interested parties */
+		if (s->store.fraction_full) {
+			sector_t numerator, denominator;
+			int pc;
+
+			s->store.fraction_full(&s->store, &numerator,
+					       &denominator);
+			pc = numerator * 100 / denominator;
+
+			if (pc >= s->last_percent + WAKE_UP_PERCENT) {
+				dm_table_event(s->table);
+				s->last_percent = pc - pc % WAKE_UP_PERCENT;
+			}
+		}
+
+	} else {
+		/* Read/write error - snapshot is unusable */
+		down_write(&s->lock);
+		if (s->valid)
+			DMERR("Error reading/writing snapshot");
+		s->store.drop_snapshot(&s->store);
+		s->valid = 0;
+		remove_exception(&pe->e);
+		flush = __flush_bhs(pe);
+		up_write(&s->lock);
+
+		error_buffers(pe->snapshot_bhs);
+
+		dm_table_event(s->table);
+		DMDEBUG("Exception failed.");
+	}
+
+ out:
+	if (flush)
+		flush_buffers(flush);
+
+	free_pending_exception(pe);
+}
+
+static void commit_callback(void *context, int success)
+{
+	struct pending_exception *pe = (struct pending_exception *) context;
+	pending_complete(pe, success);
+}
+
+/*
+ * Called when the copy I/O has finished.  kcopyd actually runs
+ * this code so don't block.
+ */
+static void copy_callback(int read_err, unsigned int write_err, void *context)
+{
+	struct pending_exception *pe = (struct pending_exception *) context;
+	struct dm_snapshot *s = pe->snap;
+
+	if (read_err || write_err)
+		pending_complete(pe, 0);
+
+	else
+		/* Update the metadata if we are persistent */
+		s->store.commit_exception(&s->store, &pe->e, commit_callback,
+					  pe);
+}
+
+/*
+ * Dispatches the copy operation to kcopyd.
+ */
+static inline void start_copy(struct pending_exception *pe)
+{
+	struct dm_snapshot *s = pe->snap;
+	struct io_region src, dest;
+	kdev_t dev = s->origin->dev;
+	int *sizes = blk_size[major(dev)];
+	sector_t dev_size = (sector_t) -1;
+
+	if (pe->started)
+		return;
+
+	/* this is protected by snap->lock */
+	pe->started = 1;
+
+	if (sizes && sizes[minor(dev)])
+		dev_size = sizes[minor(dev)] << 1;
+
+	src.dev = dev;
+	src.sector = chunk_to_sector(s, pe->e.old_chunk);
+	src.count = min(s->chunk_size, dev_size - src.sector);
+
+	dest.dev = s->cow->dev;
+	dest.sector = chunk_to_sector(s, pe->e.new_chunk);
+	dest.count = src.count;
+
+	/* Hand over to kcopyd */
+	kcopyd_copy(s->kcopyd_client,
+		    &src, 1, &dest, 0, copy_callback, pe);
+}
+
+/*
+ * Looks to see if this snapshot already has a pending exception
+ * for this chunk, otherwise it allocates a new one and inserts
+ * it into the pending table.
+ */
+static struct pending_exception *find_pending_exception(struct dm_snapshot *s,
+							struct buffer_head *bh)
+{
+	struct exception *e;
+	struct pending_exception *pe;
+	chunk_t chunk = sector_to_chunk(s, bh->b_rsector);
+
+	/*
+	 * Is there a pending exception for this already ?
+	 */
+	e = lookup_exception(&s->pending, chunk);
+	if (e) {
+		/* cast the exception to a pending exception */
+		pe = list_entry(e, struct pending_exception, e);
+
+	} else {
+		/* Create a new pending exception */
+		pe = alloc_pending_exception();
+		pe->e.old_chunk = chunk;
+		pe->origin_bhs = pe->snapshot_bhs = NULL;
+		INIT_LIST_HEAD(&pe->siblings);
+		pe->snap = s;
+		pe->started = 0;
+
+		if (s->store.prepare_exception(&s->store, &pe->e)) {
+			free_pending_exception(pe);
+			s->valid = 0;
+			return NULL;
+		}
+
+		insert_exception(&s->pending, &pe->e);
+	}
+
+	return pe;
+}
+
+static inline void remap_exception(struct dm_snapshot *s, struct exception *e,
+				   struct buffer_head *bh)
+{
+	bh->b_rdev = s->cow->dev;
+	bh->b_rsector = chunk_to_sector(s, e->new_chunk) +
+	    (bh->b_rsector & s->chunk_mask);
+}
+
+static int snapshot_map(struct dm_target *ti, struct buffer_head *bh, int rw,
+			union map_info *map_context)
+{
+	struct exception *e;
+	struct dm_snapshot *s = (struct dm_snapshot *) ti->private;
+	int r = 1;
+	chunk_t chunk;
+	struct pending_exception *pe;
+
+	chunk = sector_to_chunk(s, bh->b_rsector);
+
+	/* Full snapshots are not usable */
+	if (!s->valid)
+		return -1;
+
+	/*
+	 * Write to snapshot - higher level takes care of RW/RO
+	 * flags so we should only get this if we are
+	 * writeable.
+	 */
+	if (rw == WRITE) {
+
+		down_write(&s->lock);
+
+		/* If the block is already remapped - use that, else remap it */
+		e = lookup_exception(&s->complete, chunk);
+		if (e)
+			remap_exception(s, e, bh);
+
+		else {
+			pe = find_pending_exception(s, bh);
+
+			if (!pe) {
+				s->store.drop_snapshot(&s->store);
+				s->valid = 0;
+				r = -EIO;
+			} else {
+				remap_exception(s, &pe->e, bh);
+				queue_buffer(&pe->snapshot_bhs, bh);
+				start_copy(pe);
+				r = 0;
+			}
+		}
+
+		up_write(&s->lock);
+
+	} else {
+		/*
+		 * FIXME: this read path scares me because we
+		 * always use the origin when we have a pending
+		 * exception.  However I can't think of a
+		 * situation where this is wrong - ejt.
+		 */
+
+		/* Do reads */
+		down_read(&s->lock);
+
+		/* See if it it has been remapped */
+		e = lookup_exception(&s->complete, chunk);
+		if (e)
+			remap_exception(s, e, bh);
+		else
+			bh->b_rdev = s->origin->dev;
+
+		up_read(&s->lock);
+	}
+
+	return r;
+}
+
+void snapshot_resume(struct dm_target *ti)
+{
+	struct dm_snapshot *s = (struct dm_snapshot *) ti->private;
+
+	if (s->have_metadata)
+		return;
+
+	if (s->store.read_metadata(&s->store)) {
+		down_write(&s->lock);
+		s->valid = 0;
+		up_write(&s->lock);
+	}
+
+	s->have_metadata = 1;
+}
+
+static int snapshot_status(struct dm_target *ti, status_type_t type,
+			   char *result, unsigned int maxlen)
+{
+	struct dm_snapshot *snap = (struct dm_snapshot *) ti->private;
+	char cow[16];
+	char org[16];
+
+	switch (type) {
+	case STATUSTYPE_INFO:
+		if (!snap->valid)
+			snprintf(result, maxlen, "Invalid");
+		else {
+			if (snap->store.fraction_full) {
+				sector_t numerator, denominator;
+				snap->store.fraction_full(&snap->store,
+							  &numerator,
+							  &denominator);
+				snprintf(result, maxlen,
+					 SECTOR_FORMAT "/" SECTOR_FORMAT,
+					 numerator, denominator);
+			}
+			else
+				snprintf(result, maxlen, "Unknown");
+		}
+		break;
+
+	case STATUSTYPE_TABLE:
+		/*
+		 * kdevname returns a static pointer so we need
+		 * to make private copies if the output is to
+		 * make sense.
+		 */
+		strncpy(cow, dm_kdevname(snap->cow->dev), sizeof(cow));
+		strncpy(org, dm_kdevname(snap->origin->dev), sizeof(org));
+		snprintf(result, maxlen, "%s %s %c %ld", org, cow,
+			 snap->type, snap->chunk_size);
+		break;
+	}
+
+	return 0;
+}
+
+/*-----------------------------------------------------------------
+ * Origin methods
+ *---------------------------------------------------------------*/
+static void list_merge(struct list_head *l1, struct list_head *l2)
+{
+	struct list_head *l1_n, *l2_p;
+
+	l1_n = l1->next;
+	l2_p = l2->prev;
+
+	l1->next = l2;
+	l2->prev = l1;
+
+	l2_p->next = l1_n;
+	l1_n->prev = l2_p;
+}
+
+static int __origin_write(struct list_head *snapshots, struct buffer_head *bh)
+{
+	int r = 1, first = 1;
+	struct list_head *sl;
+	struct dm_snapshot *snap;
+	struct exception *e;
+	struct pending_exception *pe, *last = NULL;
+	chunk_t chunk;
+
+	/* Do all the snapshots on this origin */
+	list_for_each(sl, snapshots) {
+		snap = list_entry(sl, struct dm_snapshot, list);
+
+		/* Only deal with valid snapshots */
+		if (!snap->valid)
+			continue;
+
+		down_write(&snap->lock);
+
+		/*
+		 * Remember, different snapshots can have
+		 * different chunk sizes.
+		 */
+		chunk = sector_to_chunk(snap, bh->b_rsector);
+
+		/*
+		 * Check exception table to see if block
+		 * is already remapped in this snapshot
+		 * and trigger an exception if not.
+		 */
+		e = lookup_exception(&snap->complete, chunk);
+		if (!e) {
+			pe = find_pending_exception(snap, bh);
+			if (!pe) {
+				snap->store.drop_snapshot(&snap->store);
+				snap->valid = 0;
+
+			} else {
+				if (last)
+					list_merge(&pe->siblings,
+						   &last->siblings);
+
+				last = pe;
+				r = 0;
+			}
+		}
+
+		up_write(&snap->lock);
+	}
+
+	/*
+	 * Now that we have a complete pe list we can start the copying.
+	 */
+	if (last) {
+		pe = last;
+		do {
+			down_write(&pe->snap->lock);
+			if (first)
+				queue_buffer(&pe->origin_bhs, bh);
+			start_copy(pe);
+			up_write(&pe->snap->lock);
+			first = 0;
+			pe = list_entry(pe->siblings.next,
+					struct pending_exception, siblings);
+
+		} while (pe != last);
+	}
+
+	return r;
+}
+
+/*
+ * Called on a write from the origin driver.
+ */
+int do_origin(struct dm_dev *origin, struct buffer_head *bh)
+{
+	struct origin *o;
+	int r;
+
+	down_read(&_origins_lock);
+	o = __lookup_origin(origin->dev);
+	if (!o)
+		BUG();
+
+	r = __origin_write(&o->snapshots, bh);
+	up_read(&_origins_lock);
+
+	return r;
+}
+
+/*
+ * Origin: maps a linear range of a device, with hooks for snapshotting.
+ */
+
+/*
+ * Construct an origin mapping: <dev_path>
+ * The context for an origin is merely a 'struct dm_dev *'
+ * pointing to the real device.
+ */
+static int origin_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+	int r;
+	struct dm_dev *dev;
+
+	if (argc != 1) {
+		ti->error = "dm-origin: incorrect number of arguments";
+		return -EINVAL;
+	}
+
+	r = dm_get_device(ti, argv[0], 0, ti->len,
+			  dm_table_get_mode(ti->table), &dev);
+	if (r) {
+		ti->error = "Cannot get target device";
+		return r;
+	}
+
+	ti->private = dev;
+	return 0;
+}
+
+static void origin_dtr(struct dm_target *ti)
+{
+	struct dm_dev *dev = (struct dm_dev *) ti->private;
+	dm_put_device(ti, dev);
+}
+
+static int origin_map(struct dm_target *ti, struct buffer_head *bh, int rw,
+		      union map_info *map_context)
+{
+	struct dm_dev *dev = (struct dm_dev *) ti->private;
+	bh->b_rdev = dev->dev;
+
+	/* Only tell snapshots if this is a write */
+	return (rw == WRITE) ? do_origin(dev, bh) : 1;
+}
+
+static int origin_status(struct dm_target *ti, status_type_t type, char *result,
+			 unsigned int maxlen)
+{
+	struct dm_dev *dev = (struct dm_dev *) ti->private;
+
+	switch (type) {
+	case STATUSTYPE_INFO:
+		result[0] = '\0';
+		break;
+
+	case STATUSTYPE_TABLE:
+		snprintf(result, maxlen, "%s", dm_kdevname(dev->dev));
+		break;
+	}
+
+	return 0;
+}
+
+static struct target_type origin_target = {
+	name:	"snapshot-origin",
+	module:	THIS_MODULE,
+	ctr:	origin_ctr,
+	dtr:	origin_dtr,
+	map:	origin_map,
+	status:	origin_status,
+};
+
+static struct target_type snapshot_target = {
+	name:	"snapshot",
+	module:	THIS_MODULE,
+	ctr:	snapshot_ctr,
+	dtr:	snapshot_dtr,
+	map:	snapshot_map,
+	resume: snapshot_resume,
+	status:	snapshot_status,
+};
+
+int __init dm_snapshot_init(void)
+{
+	int r;
+
+	r = dm_register_target(&snapshot_target);
+	if (r) {
+		DMERR("snapshot target register failed %d", r);
+		return r;
+	}
+
+	r = dm_register_target(&origin_target);
+	if (r < 0) {
+		DMERR("Device mapper: Origin: register failed %d\n", r);
+		goto bad1;
+	}
+
+	r = init_origin_hash();
+	if (r) {
+		DMERR("init_origin_hash failed.");
+		goto bad2;
+	}
+
+	exception_cache = kmem_cache_create("dm-snapshot-ex",
+					    sizeof(struct exception),
+					    __alignof__(struct exception),
+					    0, NULL, NULL);
+	if (!exception_cache) {
+		DMERR("Couldn't create exception cache.");
+		r = -ENOMEM;
+		goto bad3;
+	}
+
+	pending_cache =
+	    kmem_cache_create("dm-snapshot-in",
+			      sizeof(struct pending_exception),
+			      __alignof__(struct pending_exception),
+			      0, NULL, NULL);
+	if (!pending_cache) {
+		DMERR("Couldn't create pending cache.");
+		r = -ENOMEM;
+		goto bad4;
+	}
+
+	pending_pool = mempool_create(128, mempool_alloc_slab,
+				      mempool_free_slab, pending_cache);
+	if (!pending_pool) {
+		DMERR("Couldn't create pending pool.");
+		r = -ENOMEM;
+		goto bad5;
+	}
+
+	return 0;
+
+      bad5:
+	kmem_cache_destroy(pending_cache);
+      bad4:
+	kmem_cache_destroy(exception_cache);
+      bad3:
+	exit_origin_hash();
+      bad2:
+	dm_unregister_target(&origin_target);
+      bad1:
+	dm_unregister_target(&snapshot_target);
+	return r;
+}
+
+void dm_snapshot_exit(void)
+{
+	int r;
+
+	r = dm_unregister_target(&snapshot_target);
+	if (r)
+		DMERR("snapshot unregister failed %d", r);
+
+	r = dm_unregister_target(&origin_target);
+	if (r)
+		DMERR("origin unregister failed %d", r);
+
+	exit_origin_hash();
+	mempool_destroy(pending_pool);
+	kmem_cache_destroy(pending_cache);
+	kmem_cache_destroy(exception_cache);
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/dm-snapshot.h linux-2.4.27-leo/drivers/md/dm-snapshot.h
--- linux-2.4.27/drivers/md/dm-snapshot.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/md/dm-snapshot.h	2004-09-20 21:40:21.000000000 +0100
@@ -0,0 +1,158 @@
+/*
+ * dm-snapshot.c
+ *
+ * Copyright (C) 2001-2002 Sistina Software (UK) Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef DM_SNAPSHOT_H
+#define DM_SNAPSHOT_H
+
+#include "dm.h"
+#include <linux/blkdev.h>
+
+struct exception_table {
+	uint32_t hash_mask;
+	struct list_head *table;
+};
+
+/*
+ * The snapshot code deals with largish chunks of the disk at a
+ * time. Typically 64k - 256k.
+ */
+/* FIXME: can we get away with limiting these to a uint32_t ? */
+typedef sector_t chunk_t;
+
+/*
+ * An exception is used where an old chunk of data has been
+ * replaced by a new one.
+ */
+struct exception {
+	struct list_head hash_list;
+
+	chunk_t old_chunk;
+	chunk_t new_chunk;
+};
+
+/*
+ * Abstraction to handle the meta/layout of exception stores (the
+ * COW device).
+ */
+struct exception_store {
+
+	/*
+	 * Destroys this object when you've finished with it.
+	 */
+	void (*destroy) (struct exception_store *store);
+
+	/*
+	 * The target shouldn't read the COW device until this is
+	 * called.
+	 */
+	int (*read_metadata) (struct exception_store *store);
+
+	/*
+	 * Find somewhere to store the next exception.
+	 */
+	int (*prepare_exception) (struct exception_store *store,
+				  struct exception *e);
+
+	/*
+	 * Update the metadata with this exception.
+	 */
+	void (*commit_exception) (struct exception_store *store,
+				  struct exception *e,
+				  void (*callback) (void *, int success),
+				  void *callback_context);
+
+	/*
+	 * The snapshot is invalid, note this in the metadata.
+	 */
+	void (*drop_snapshot) (struct exception_store *store);
+
+	/*
+	 * Return how full the snapshot is.
+	 */
+	void (*fraction_full) (struct exception_store *store,
+			       sector_t *numerator,
+			       sector_t *denominator);
+
+	struct dm_snapshot *snap;
+	void *context;
+};
+
+struct dm_snapshot {
+	struct rw_semaphore lock;
+	struct dm_table *table;
+
+	struct dm_dev *origin;
+	struct dm_dev *cow;
+
+	/* List of snapshots per Origin */
+	struct list_head list;
+
+	/* Size of data blocks saved - must be a power of 2 */
+	chunk_t chunk_size;
+	chunk_t chunk_mask;
+	chunk_t chunk_shift;
+
+	/* You can't use a snapshot if this is 0 (e.g. if full) */
+	int valid;
+	int have_metadata;
+
+	/* Used for display of table */
+	char type;
+
+	/* The last percentage we notified */
+	int last_percent;
+
+	struct exception_table pending;
+	struct exception_table complete;
+
+	/* The on disk metadata handler */
+	struct exception_store store;
+
+	struct kcopyd_client *kcopyd_client;
+};
+
+/*
+ * Used by the exception stores to load exceptions hen
+ * initialising.
+ */
+int dm_add_exception(struct dm_snapshot *s, chunk_t old, chunk_t new);
+
+/*
+ * Constructor and destructor for the default persistent
+ * store.
+ */
+int dm_create_persistent(struct exception_store *store, uint32_t chunk_size);
+
+int dm_create_transient(struct exception_store *store,
+			struct dm_snapshot *s, int blocksize);
+
+/*
+ * Return the number of sectors in the device.
+ */
+static inline sector_t get_dev_size(kdev_t dev)
+{
+	int *sizes;
+
+	sizes = blk_size[MAJOR(dev)];
+	if (sizes)
+		return sizes[MINOR(dev)] << 1;
+
+	return 0;
+}
+
+static inline chunk_t sector_to_chunk(struct dm_snapshot *s, sector_t sector)
+{
+	return (sector & ~s->chunk_mask) >> s->chunk_shift;
+}
+
+static inline sector_t chunk_to_sector(struct dm_snapshot *s, chunk_t chunk)
+{
+	return chunk << s->chunk_shift;
+}
+
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/dm-stripe.c linux-2.4.27-leo/drivers/md/dm-stripe.c
--- linux-2.4.27/drivers/md/dm-stripe.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/md/dm-stripe.c	2004-09-17 03:19:08.000000000 +0100
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2001 Sistina Software (UK) Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm.h"
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/slab.h>
+
+struct stripe {
+	struct dm_dev *dev;
+	sector_t physical_start;
+};
+
+struct stripe_c {
+	uint32_t stripes;
+
+	/* The size of this target / num. stripes */
+	uint32_t stripe_width;
+
+	/* stripe chunk size */
+	uint32_t chunk_shift;
+	sector_t chunk_mask;
+
+	struct stripe stripe[0];
+};
+
+static inline struct stripe_c *alloc_context(unsigned int stripes)
+{
+	size_t len;
+
+	if (array_too_big(sizeof(struct stripe_c), sizeof(struct stripe),
+			  stripes))
+		return NULL;
+
+	len = sizeof(struct stripe_c) + (sizeof(struct stripe) * stripes);
+
+	return kmalloc(len, GFP_KERNEL);
+}
+
+/*
+ * Parse a single <dev> <sector> pair
+ */
+static int get_stripe(struct dm_target *ti, struct stripe_c *sc,
+		      unsigned int stripe, char **argv)
+{
+	sector_t start;
+
+	if (sscanf(argv[1], SECTOR_FORMAT, &start) != 1)
+		return -EINVAL;
+
+	if (dm_get_device(ti, argv[0], start, sc->stripe_width,
+			  dm_table_get_mode(ti->table),
+			  &sc->stripe[stripe].dev))
+		return -ENXIO;
+
+	sc->stripe[stripe].physical_start = start;
+	return 0;
+}
+
+/*
+ * FIXME: Nasty function, only present because we can't link
+ * against __moddi3 and __divdi3.
+ *
+ * returns a == b * n
+ */
+static int multiple(sector_t a, sector_t b, sector_t *n)
+{
+	sector_t acc, prev, i;
+
+	*n = 0;
+	while (a >= b) {
+		for (acc = b, prev = 0, i = 1;
+		     acc <= a;
+		     prev = acc, acc <<= 1, i <<= 1)
+			;
+
+		a -= prev;
+		*n += i >> 1;
+	}
+
+	return a == 0;
+}
+
+/*
+ * Construct a striped mapping.
+ * <number of stripes> <chunk size (2^^n)> [<dev_path> <offset>]+
+ */
+static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+	struct stripe_c *sc;
+	sector_t width;
+	uint32_t stripes;
+	uint32_t chunk_size;
+	char *end;
+	int r;
+	unsigned int i;
+
+	if (argc < 2) {
+		ti->error = "dm-stripe: Not enough arguments";
+		return -EINVAL;
+	}
+
+	stripes = simple_strtoul(argv[0], &end, 10);
+	if (*end) {
+		ti->error = "dm-stripe: Invalid stripe count";
+		return -EINVAL;
+	}
+
+	chunk_size = simple_strtoul(argv[1], &end, 10);
+	if (*end) {
+		ti->error = "dm-stripe: Invalid chunk_size";
+		return -EINVAL;
+	}
+
+	/*
+	 * chunk_size is a power of two
+	 */
+	if (!chunk_size || (chunk_size & (chunk_size - 1))) {
+		ti->error = "dm-stripe: Invalid chunk size";
+		return -EINVAL;
+	}
+
+	if (!multiple(ti->len, stripes, &width)) {
+		ti->error = "dm-stripe: Target length not divisable by "
+		    "number of stripes";
+		return -EINVAL;
+	}
+
+	/*
+	 * Do we have enough arguments for that many stripes ?
+	 */
+	if (argc != (2 + 2 * stripes)) {
+		ti->error = "dm-stripe: Not enough destinations specified";
+		return -EINVAL;
+	}
+
+	sc = alloc_context(stripes);
+	if (!sc) {
+		ti->error = "dm-stripe: Memory allocation for striped context "
+		    "failed";
+		return -ENOMEM;
+	}
+
+	sc->stripes = stripes;
+	sc->stripe_width = width;
+
+	sc->chunk_mask = ((sector_t) chunk_size) - 1;
+	for (sc->chunk_shift = 0; chunk_size; sc->chunk_shift++)
+		chunk_size >>= 1;
+	sc->chunk_shift--;
+
+	/*
+	 * Get the stripe destinations.
+	 */
+	for (i = 0; i < stripes; i++) {
+		argv += 2;
+
+		r = get_stripe(ti, sc, i, argv);
+		if (r < 0) {
+			ti->error = "dm-stripe: Couldn't parse stripe "
+			    "destination";
+			while (i--)
+				dm_put_device(ti, sc->stripe[i].dev);
+			kfree(sc);
+			return r;
+		}
+	}
+
+	ti->private = sc;
+	return 0;
+}
+
+static void stripe_dtr(struct dm_target *ti)
+{
+	unsigned int i;
+	struct stripe_c *sc = (struct stripe_c *) ti->private;
+
+	for (i = 0; i < sc->stripes; i++)
+		dm_put_device(ti, sc->stripe[i].dev);
+
+	kfree(sc);
+}
+
+static int stripe_map(struct dm_target *ti, struct buffer_head *bh, int rw,
+		      union map_info *context)
+{
+	struct stripe_c *sc = (struct stripe_c *) ti->private;
+
+	sector_t offset = bh->b_rsector - ti->begin;
+	uint32_t chunk = (uint32_t) (offset >> sc->chunk_shift);
+	uint32_t stripe = chunk % sc->stripes;	/* 32bit modulus */
+	chunk = chunk / sc->stripes;
+
+	bh->b_rdev = sc->stripe[stripe].dev->dev;
+	bh->b_rsector = sc->stripe[stripe].physical_start +
+	    (chunk << sc->chunk_shift) + (offset & sc->chunk_mask);
+	return 1;
+}
+
+static int stripe_status(struct dm_target *ti, status_type_t type,
+			 char *result, unsigned int maxlen)
+{
+	struct stripe_c *sc = (struct stripe_c *) ti->private;
+	int offset;
+	unsigned int i;
+
+	switch (type) {
+	case STATUSTYPE_INFO:
+		result[0] = '\0';
+		break;
+
+	case STATUSTYPE_TABLE:
+		offset = snprintf(result, maxlen, "%d " SECTOR_FORMAT,
+				  sc->stripes, sc->chunk_mask + 1);
+		for (i = 0; i < sc->stripes; i++) {
+			offset +=
+			    snprintf(result + offset, maxlen - offset,
+				     " %s " SECTOR_FORMAT,
+		       dm_kdevname(to_kdev_t(sc->stripe[i].dev->bdev->bd_dev)),
+				     sc->stripe[i].physical_start);
+		}
+		break;
+	}
+	return 0;
+}
+
+static struct target_type stripe_target = {
+	.name   = "striped",
+	.module = THIS_MODULE,
+	.ctr    = stripe_ctr,
+	.dtr    = stripe_dtr,
+	.map    = stripe_map,
+	.status = stripe_status,
+};
+
+int __init dm_stripe_init(void)
+{
+	int r;
+
+	r = dm_register_target(&stripe_target);
+	if (r < 0)
+		DMWARN("striped target registration failed");
+
+	return r;
+}
+
+void dm_stripe_exit(void)
+{
+	if (dm_unregister_target(&stripe_target))
+		DMWARN("striped target unregistration failed");
+
+	return;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/dm-table.c linux-2.4.27-leo/drivers/md/dm-table.c
--- linux-2.4.27/drivers/md/dm-table.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/md/dm-table.c	2004-09-17 03:19:08.000000000 +0100
@@ -0,0 +1,679 @@
+/*
+ * Copyright (C) 2001 Sistina Software (UK) Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm.h"
+
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/blkdev.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <asm/atomic.h>
+
+#define MAX_DEPTH 16
+#define NODE_SIZE L1_CACHE_BYTES
+#define KEYS_PER_NODE (NODE_SIZE / sizeof(sector_t))
+#define CHILDREN_PER_NODE (KEYS_PER_NODE + 1)
+
+struct dm_table {
+	atomic_t holders;
+
+	/* btree table */
+	unsigned int depth;
+	unsigned int counts[MAX_DEPTH];	/* in nodes */
+	sector_t *index[MAX_DEPTH];
+
+	unsigned int num_targets;
+	unsigned int num_allocated;
+	sector_t *highs;
+	struct dm_target *targets;
+
+	/*
+	 * Indicates the rw permissions for the new logical
+	 * device.  This should be a combination of FMODE_READ
+	 * and FMODE_WRITE.
+	 */
+	int mode;
+
+	/* a list of devices used by this table */
+	struct list_head devices;
+
+	/* events get handed up using this callback */
+	void (*event_fn)(void *);
+	void *event_context;
+};
+
+/*
+ * Similar to ceiling(log_size(n))
+ */
+static unsigned int int_log(unsigned long n, unsigned long base)
+{
+	int result = 0;
+
+	while (n > 1) {
+		n = dm_div_up(n, base);
+		result++;
+	}
+
+	return result;
+}
+
+/*
+ * Calculate the index of the child node of the n'th node k'th key.
+ */
+static inline unsigned int get_child(unsigned int n, unsigned int k)
+{
+	return (n * CHILDREN_PER_NODE) + k;
+}
+
+/*
+ * Return the n'th node of level l from table t.
+ */
+static inline sector_t *get_node(struct dm_table *t, unsigned int l,
+				 unsigned int n)
+{
+	return t->index[l] + (n * KEYS_PER_NODE);
+}
+
+/*
+ * Return the highest key that you could lookup from the n'th
+ * node on level l of the btree.
+ */
+static sector_t high(struct dm_table *t, unsigned int l, unsigned int n)
+{
+	for (; l < t->depth - 1; l++)
+		n = get_child(n, CHILDREN_PER_NODE - 1);
+
+	if (n >= t->counts[l])
+		return (sector_t) - 1;
+
+	return get_node(t, l, n)[KEYS_PER_NODE - 1];
+}
+
+/*
+ * Fills in a level of the btree based on the highs of the level
+ * below it.
+ */
+static int setup_btree_index(unsigned int l, struct dm_table *t)
+{
+	unsigned int n, k;
+	sector_t *node;
+
+	for (n = 0U; n < t->counts[l]; n++) {
+		node = get_node(t, l, n);
+
+		for (k = 0U; k < KEYS_PER_NODE; k++)
+			node[k] = high(t, l + 1, get_child(n, k));
+	}
+
+	return 0;
+}
+
+
+
+int dm_table_create(struct dm_table **result, int mode, unsigned num_targets)
+{
+	struct dm_table *t = kmalloc(sizeof(*t), GFP_KERNEL);
+
+	if (!t)
+		return -ENOMEM;
+
+	memset(t, 0, sizeof(*t));
+	INIT_LIST_HEAD(&t->devices);
+	atomic_set(&t->holders, 1);
+
+	num_targets = dm_round_up(num_targets, KEYS_PER_NODE);
+
+	/* Allocate both the target array and offset array at once. */
+	t->highs = (sector_t *) vcalloc(sizeof(struct dm_target) +
+					sizeof(sector_t), num_targets);
+	if (!t->highs) {
+		kfree(t);
+		return -ENOMEM;
+	}
+
+	memset(t->highs, -1, sizeof(*t->highs) * num_targets);
+
+	t->targets = (struct dm_target *) (t->highs + num_targets);
+	t->num_allocated = num_targets;
+	t->mode = mode;
+	*result = t;
+	return 0;
+}
+
+static void free_devices(struct list_head *devices)
+{
+	struct list_head *tmp, *next;
+
+	for (tmp = devices->next; tmp != devices; tmp = next) {
+		struct dm_dev *dd = list_entry(tmp, struct dm_dev, list);
+		next = tmp->next;
+		kfree(dd);
+	}
+}
+
+void table_destroy(struct dm_table *t)
+{
+	unsigned int i;
+
+	/* free the indexes (see dm_table_complete) */
+	if (t->depth >= 2)
+		vfree(t->index[t->depth - 2]);
+
+	/* free the targets */
+	for (i = 0; i < t->num_targets; i++) {
+		struct dm_target *tgt = t->targets + i;
+
+		if (tgt->type->dtr)
+			tgt->type->dtr(tgt);
+
+		dm_put_target_type(tgt->type);
+	}
+
+	vfree(t->highs);
+
+	/* free the device list */
+	if (t->devices.next != &t->devices) {
+		DMWARN("devices still present during destroy: "
+		       "dm_table_remove_device calls missing");
+
+		free_devices(&t->devices);
+	}
+
+	kfree(t);
+}
+
+void dm_table_get(struct dm_table *t)
+{
+	atomic_inc(&t->holders);
+}
+
+void dm_table_put(struct dm_table *t)
+{
+	if (atomic_dec_and_test(&t->holders))
+		table_destroy(t);
+}
+
+/*
+ * Convert a device path to a dev_t.
+ */
+static int lookup_device(const char *path, kdev_t *dev)
+{
+	int r;
+	struct nameidata nd;
+	struct inode *inode;
+
+	if (!path_init(path, LOOKUP_FOLLOW, &nd))
+		return 0;
+
+	if ((r = path_walk(path, &nd)))
+		goto out;
+
+	inode = nd.dentry->d_inode;
+	if (!inode) {
+		r = -ENOENT;
+		goto out;
+	}
+
+	if (!S_ISBLK(inode->i_mode)) {
+		r = -ENOTBLK;
+		goto out;
+	}
+
+	*dev = inode->i_rdev;
+
+      out:
+	path_release(&nd);
+	return r;
+}
+
+/*
+ * See if we've already got a device in the list.
+ */
+static struct dm_dev *find_device(struct list_head *l, kdev_t dev)
+{
+	struct list_head *tmp;
+
+	list_for_each(tmp, l) {
+		struct dm_dev *dd = list_entry(tmp, struct dm_dev, list);
+		if (kdev_same(dd->dev, dev))
+			return dd;
+	}
+
+	return NULL;
+}
+
+/*
+ * Open a device so we can use it as a map destination.
+ */
+static int open_dev(struct dm_dev *dd)
+{
+	if (dd->bdev)
+		BUG();
+
+	dd->bdev = bdget(kdev_t_to_nr(dd->dev));
+	if (!dd->bdev)
+		return -ENOMEM;
+
+	return blkdev_get(dd->bdev, dd->mode, 0, BDEV_RAW);
+}
+
+/*
+ * Close a device that we've been using.
+ */
+static void close_dev(struct dm_dev *dd)
+{
+	if (!dd->bdev)
+		return;
+
+	blkdev_put(dd->bdev, BDEV_RAW);
+	dd->bdev = NULL;
+}
+
+/*
+ * If possible (ie. blk_size[major] is set), this checks an area
+ * of a destination device is valid.
+ */
+static int check_device_area(kdev_t dev, sector_t start, sector_t len)
+{
+	int *sizes;
+	sector_t dev_size;
+
+	if (!(sizes = blk_size[major(dev)]) || !(dev_size = sizes[minor(dev)]))
+		/* we don't know the device details,
+		 * so give the benefit of the doubt */
+		return 1;
+
+	/* convert to 512-byte sectors */
+	dev_size <<= 1;
+
+	return ((start < dev_size) && (len <= (dev_size - start)));
+}
+
+/*
+ * This upgrades the mode on an already open dm_dev.  Being
+ * careful to leave things as they were if we fail to reopen the
+ * device.
+ */
+static int upgrade_mode(struct dm_dev *dd, int new_mode)
+{
+	int r;
+	struct dm_dev dd_copy;
+
+	memcpy(&dd_copy, dd, sizeof(dd_copy));
+
+	dd->mode |= new_mode;
+	dd->bdev = NULL;
+	r = open_dev(dd);
+	if (!r)
+		close_dev(&dd_copy);
+	else
+		memcpy(dd, &dd_copy, sizeof(dd_copy));
+
+	return r;
+}
+
+/*
+ * Add a device to the list, or just increment the usage count if
+ * it's already present.
+ */
+int dm_get_device(struct dm_target *ti, const char *path, sector_t start,
+		  sector_t len, int mode, struct dm_dev **result)
+{
+	int r;
+	kdev_t dev;
+	struct dm_dev *dd;
+	unsigned major, minor;
+	struct dm_table *t = ti->table;
+
+	if (!t)
+		BUG();
+
+	if (sscanf(path, "%u:%u", &major, &minor) == 2) {
+		/* Extract the major/minor numbers */
+		dev = mk_kdev(major, minor);
+	} else {
+		/* convert the path to a device */
+		if ((r = lookup_device(path, &dev)))
+			return r;
+	}
+
+	dd = find_device(&t->devices, dev);
+	if (!dd) {
+		dd = kmalloc(sizeof(*dd), GFP_KERNEL);
+		if (!dd)
+			return -ENOMEM;
+
+		dd->dev = dev;
+		dd->mode = mode;
+		dd->bdev = NULL;
+
+		if ((r = open_dev(dd))) {
+			kfree(dd);
+			return r;
+		}
+
+		atomic_set(&dd->count, 0);
+		list_add(&dd->list, &t->devices);
+
+	} else if (dd->mode != (mode | dd->mode)) {
+		r = upgrade_mode(dd, mode);
+		if (r)
+			return r;
+	}
+	atomic_inc(&dd->count);
+
+	if (!check_device_area(dd->dev, start, len)) {
+		DMWARN("device %s too small for target", path);
+		dm_put_device(ti, dd);
+		return -EINVAL;
+	}
+
+	*result = dd;
+
+	return 0;
+}
+
+/*
+ * Decrement a devices use count and remove it if neccessary.
+ */
+void dm_put_device(struct dm_target *ti, struct dm_dev *dd)
+{
+	if (atomic_dec_and_test(&dd->count)) {
+		close_dev(dd);
+		list_del(&dd->list);
+		kfree(dd);
+	}
+}
+
+/*
+ * Checks to see if the target joins onto the end of the table.
+ */
+static int adjoin(struct dm_table *table, struct dm_target *ti)
+{
+	struct dm_target *prev;
+
+	if (!table->num_targets)
+		return !ti->begin;
+
+	prev = &table->targets[table->num_targets - 1];
+	return (ti->begin == (prev->begin + prev->len));
+}
+
+/*
+ * Used to dynamically allocate the arg array.
+ */
+static char **realloc_argv(unsigned *array_size, char **old_argv)
+{
+	char **argv;
+	unsigned new_size;
+
+	new_size = *array_size ? *array_size * 2 : 64;
+	argv = kmalloc(new_size * sizeof(*argv), GFP_KERNEL);
+	if (argv) {
+		memcpy(argv, old_argv, *array_size * sizeof(*argv));
+		*array_size = new_size;
+	}
+
+	kfree(old_argv);
+	return argv;
+}
+
+/*
+ * Destructively splits up the argument list to pass to ctr.
+ */
+static int split_args(int *argc, char ***argvp, char *input)
+{
+	char *start, *end = input, *out, **argv = NULL;
+	unsigned array_size = 0;
+
+	*argc = 0;
+	argv = realloc_argv(&array_size, argv);
+	if (!argv)
+		return -ENOMEM;
+
+	while (1) {
+		start = end;
+
+		/* Skip whitespace */
+		while (*start && isspace(*start))
+			start++;
+
+		if (!*start)
+			break;	/* success, we hit the end */
+
+		/* 'out' is used to remove any back-quotes */
+		end = out = start;
+		while (*end) {
+			/* Everything apart from '\0' can be quoted */
+			if (*end == '\\' && *(end + 1)) {
+				*out++ = *(end + 1);
+				end += 2;
+				continue;
+			}
+
+			if (isspace(*end))
+				break;	/* end of token */
+
+			*out++ = *end++;
+		}
+
+		/* have we already filled the array ? */
+		if ((*argc + 1) > array_size) {
+			argv = realloc_argv(&array_size, argv);
+			if (!argv)
+				return -ENOMEM;
+		}
+
+		/* we know this is whitespace */
+		if (*end)
+			end++;
+
+		/* terminate the string and put it in the array */
+		*out = '\0';
+		argv[*argc] = start;
+		(*argc)++;
+	}
+
+	*argvp = argv;
+	return 0;
+}
+
+int dm_table_add_target(struct dm_table *t, const char *type,
+			sector_t start, sector_t len, char *params)
+{
+	int r = -EINVAL, argc;
+	char **argv;
+	struct dm_target *tgt;
+
+	if (t->num_targets >= t->num_allocated)
+		return -ENOMEM;
+
+	tgt = t->targets + t->num_targets;
+	memset(tgt, 0, sizeof(*tgt));
+
+	tgt->type = dm_get_target_type(type);
+	if (!tgt->type) {
+		tgt->error = "unknown target type";
+		return -EINVAL;
+	}
+
+	tgt->table = t;
+	tgt->begin = start;
+	tgt->len = len;
+	tgt->error = "Unknown error";
+
+	/*
+	 * Does this target adjoin the previous one ?
+	 */
+	if (!adjoin(t, tgt)) {
+		tgt->error = "Gap in table";
+		r = -EINVAL;
+		goto bad;
+	}
+
+	r = split_args(&argc, &argv, params);
+	if (r) {
+		tgt->error = "couldn't split parameters (insufficient memory)";
+		goto bad;
+	}
+
+	r = tgt->type->ctr(tgt, argc, argv);
+	kfree(argv);
+	if (r)
+		goto bad;
+
+	t->highs[t->num_targets++] = tgt->begin + tgt->len - 1;
+	return 0;
+
+      bad:
+	printk(KERN_ERR DM_NAME ": %s\n", tgt->error);
+	dm_put_target_type(tgt->type);
+	return r;
+}
+
+static int setup_indexes(struct dm_table *t)
+{
+	int i;
+	unsigned int total = 0;
+	sector_t *indexes;
+
+	/* allocate the space for *all* the indexes */
+	for (i = t->depth - 2; i >= 0; i--) {
+		t->counts[i] = dm_div_up(t->counts[i + 1], CHILDREN_PER_NODE);
+		total += t->counts[i];
+	}
+
+	indexes = (sector_t *) vcalloc(total, (unsigned long) NODE_SIZE);
+	if (!indexes)
+		return -ENOMEM;
+
+	/* set up internal nodes, bottom-up */
+	for (i = t->depth - 2, total = 0; i >= 0; i--) {
+		t->index[i] = indexes;
+		indexes += (KEYS_PER_NODE * t->counts[i]);
+		setup_btree_index(i, t);
+	}
+
+	return 0;
+}
+
+/*
+ * Builds the btree to index the map.
+ */
+int dm_table_complete(struct dm_table *t)
+{
+	int r = 0;
+	unsigned int leaf_nodes;
+
+	/* how many indexes will the btree have ? */
+	leaf_nodes = dm_div_up(t->num_targets, KEYS_PER_NODE);
+	t->depth = 1 + int_log(leaf_nodes, CHILDREN_PER_NODE);
+
+	/* leaf layer has already been set up */
+	t->counts[t->depth - 1] = leaf_nodes;
+	t->index[t->depth - 1] = t->highs;
+
+	if (t->depth >= 2)
+		r = setup_indexes(t);
+
+	return r;
+}
+
+static spinlock_t _event_lock = SPIN_LOCK_UNLOCKED;
+void dm_table_event_callback(struct dm_table *t,
+			     void (*fn)(void *), void *context)
+{
+	spin_lock_irq(&_event_lock);
+	t->event_fn = fn;
+	t->event_context = context;
+	spin_unlock_irq(&_event_lock);
+}
+
+void dm_table_event(struct dm_table *t)
+{
+	spin_lock(&_event_lock);
+	if (t->event_fn)
+		t->event_fn(t->event_context);
+	spin_unlock(&_event_lock);
+}
+
+sector_t dm_table_get_size(struct dm_table *t)
+{
+	return t->num_targets ? (t->highs[t->num_targets - 1] + 1) : 0;
+}
+
+struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index)
+{
+	if (index > t->num_targets)
+		return NULL;
+
+	return t->targets + index;
+}
+
+/*
+ * Search the btree for the correct target.
+ */
+struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector)
+{
+	unsigned int l, n = 0, k = 0;
+	sector_t *node;
+
+	for (l = 0; l < t->depth; l++) {
+		n = get_child(n, k);
+		node = get_node(t, l, n);
+
+		for (k = 0; k < KEYS_PER_NODE; k++)
+			if (node[k] >= sector)
+				break;
+	}
+
+	return &t->targets[(KEYS_PER_NODE * n) + k];
+}
+
+unsigned int dm_table_get_num_targets(struct dm_table *t)
+{
+	return t->num_targets;
+}
+
+struct list_head *dm_table_get_devices(struct dm_table *t)
+{
+	return &t->devices;
+}
+
+int dm_table_get_mode(struct dm_table *t)
+{
+	return t->mode;
+}
+
+void dm_table_suspend_targets(struct dm_table *t)
+{
+	int i;
+
+	for (i = 0; i < t->num_targets; i++) {
+		struct dm_target *ti = t->targets + i;
+
+		if (ti->type->suspend)
+			ti->type->suspend(ti);
+	}
+}
+
+void dm_table_resume_targets(struct dm_table *t)
+{
+	int i;
+
+	for (i = 0; i < t->num_targets; i++) {
+		struct dm_target *ti = t->targets + i;
+
+		if (ti->type->resume)
+			ti->type->resume(ti);
+	}
+}
+
+EXPORT_SYMBOL(dm_get_device);
+EXPORT_SYMBOL(dm_put_device);
+EXPORT_SYMBOL(dm_table_event);
+EXPORT_SYMBOL(dm_table_get_mode);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/dm-target.c linux-2.4.27-leo/drivers/md/dm-target.c
--- linux-2.4.27/drivers/md/dm-target.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/md/dm-target.c	2004-09-17 03:19:08.000000000 +0100
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2001 Sistina Software (UK) Limited
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm.h"
+
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+
+struct tt_internal {
+	struct target_type tt;
+
+	struct list_head list;
+	long use;
+};
+
+static LIST_HEAD(_targets);
+static DECLARE_RWSEM(_lock);
+
+#define DM_MOD_NAME_SIZE 32
+
+static inline struct tt_internal *__find_target_type(const char *name)
+{
+	struct list_head *tih;
+	struct tt_internal *ti;
+
+	list_for_each(tih, &_targets) {
+		ti = list_entry(tih, struct tt_internal, list);
+
+		if (!strcmp(name, ti->tt.name))
+			return ti;
+	}
+
+	return NULL;
+}
+
+static struct tt_internal *get_target_type(const char *name)
+{
+	struct tt_internal *ti;
+
+	down_read(&_lock);
+	ti = __find_target_type(name);
+
+	if (ti) {
+		if (ti->use == 0 && ti->tt.module)
+			__MOD_INC_USE_COUNT(ti->tt.module);
+		ti->use++;
+	}
+	up_read(&_lock);
+
+	return ti;
+}
+
+static void load_module(const char *name)
+{
+	char module_name[DM_MOD_NAME_SIZE] = "dm-";
+
+	/* Length check for strcat() below */
+	if (strlen(name) > (DM_MOD_NAME_SIZE - 4))
+		return;
+
+	strcat(module_name, name);
+	request_module(module_name);
+}
+
+struct target_type *dm_get_target_type(const char *name)
+{
+	struct tt_internal *ti = get_target_type(name);
+
+	if (!ti) {
+		load_module(name);
+		ti = get_target_type(name);
+	}
+
+	return ti ? &ti->tt : NULL;
+}
+
+void dm_put_target_type(struct target_type *t)
+{
+	struct tt_internal *ti = (struct tt_internal *) t;
+
+	down_read(&_lock);
+	if (--ti->use == 0 && ti->tt.module)
+		__MOD_DEC_USE_COUNT(ti->tt.module);
+
+	if (ti->use < 0)
+		BUG();
+	up_read(&_lock);
+
+	return;
+}
+
+static struct tt_internal *alloc_target(struct target_type *t)
+{
+	struct tt_internal *ti = kmalloc(sizeof(*ti), GFP_KERNEL);
+
+	if (ti) {
+		memset(ti, 0, sizeof(*ti));
+		ti->tt = *t;
+	}
+
+	return ti;
+}
+
+int dm_register_target(struct target_type *t)
+{
+	int rv = 0;
+	struct tt_internal *ti = alloc_target(t);
+
+	if (!ti)
+		return -ENOMEM;
+
+	down_write(&_lock);
+	if (__find_target_type(t->name)) {
+		kfree(ti);
+		rv = -EEXIST;
+	} else
+		list_add(&ti->list, &_targets);
+
+	up_write(&_lock);
+	return rv;
+}
+
+int dm_unregister_target(struct target_type *t)
+{
+	struct tt_internal *ti;
+
+	down_write(&_lock);
+	if (!(ti = __find_target_type(t->name))) {
+		up_write(&_lock);
+		return -EINVAL;
+	}
+
+	if (ti->use) {
+		up_write(&_lock);
+		return -ETXTBSY;
+	}
+
+	list_del(&ti->list);
+	kfree(ti);
+
+	up_write(&_lock);
+	return 0;
+}
+
+/*
+ * io-err: always fails an io, useful for bringing
+ * up LVs that have holes in them.
+ */
+static int io_err_ctr(struct dm_target *ti, unsigned int argc, char **args)
+{
+	return 0;
+}
+
+static void io_err_dtr(struct dm_target *ti)
+{
+	/* empty */
+}
+
+static int io_err_map(struct dm_target *ti, struct buffer_head *bh, int rw,
+		      union map_info *map_context)
+{
+	return -EIO;
+}
+
+static struct target_type error_target = {
+	.name = "error",
+	.ctr  = io_err_ctr,
+	.dtr  = io_err_dtr,
+	.map  = io_err_map,
+};
+
+int dm_target_init(void)
+{
+	return dm_register_target(&error_target);
+}
+
+void dm_target_exit(void)
+{
+	if (dm_unregister_target(&error_target))
+		DMWARN("error target unregistration failed");
+}
+
+EXPORT_SYMBOL(dm_register_target);
+EXPORT_SYMBOL(dm_unregister_target);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/kcopyd.c linux-2.4.27-leo/drivers/md/kcopyd.c
--- linux-2.4.27/drivers/md/kcopyd.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/md/kcopyd.c	2004-09-17 03:19:08.000000000 +0100
@@ -0,0 +1,666 @@
+/*
+ * Copyright (C) 2002 Sistina Software (UK) Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#include <asm/atomic.h>
+
+#include <linux/blkdev.h>
+#include <linux/config.h>
+#include <linux/device-mapper.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/locks.h>
+#include <linux/mempool.h>
+#include <linux/module.h>
+#include <linux/pagemap.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include "kcopyd.h"
+#include "dm-daemon.h"
+
+/* FIXME: this is only needed for the DMERR macros */
+#include "dm.h"
+
+static struct dm_daemon _kcopyd;
+
+#define SECTORS_PER_PAGE (PAGE_SIZE / SECTOR_SIZE)
+#define SUB_JOB_SIZE 128
+#define PAGES_PER_SUB_JOB (SUB_JOB_SIZE / SECTORS_PER_PAGE)
+#define SUB_JOB_COUNT 8
+
+/*-----------------------------------------------------------------
+ * Each kcopyd client has its own little pool of preallocated
+ * pages for kcopyd io.
+ *---------------------------------------------------------------*/
+struct kcopyd_client {
+	struct list_head list;
+
+	spinlock_t lock;
+	struct list_head pages;
+	unsigned int nr_pages;
+	unsigned int nr_free_pages;
+	unsigned int max_split;
+};
+
+static inline void __push_page(struct kcopyd_client *kc, struct page *p)
+{
+	list_add(&p->list, &kc->pages);
+	kc->nr_free_pages++;
+}
+
+static inline struct page *__pop_page(struct kcopyd_client *kc)
+{
+	struct page *p;
+
+	p = list_entry(kc->pages.next, struct page, list);
+	list_del(&p->list);
+	kc->nr_free_pages--;
+
+	return p;
+}
+
+static int kcopyd_get_pages(struct kcopyd_client *kc,
+			    unsigned int nr, struct list_head *pages)
+{
+	struct page *p;
+	INIT_LIST_HEAD(pages);
+
+	spin_lock(&kc->lock);
+	if (kc->nr_free_pages < nr) {
+		spin_unlock(&kc->lock);
+		return -ENOMEM;
+	}
+
+	while (nr--) {
+		p = __pop_page(kc);
+		list_add(&p->list, pages);
+	}
+	spin_unlock(&kc->lock);
+
+	return 0;
+}
+
+static void kcopyd_put_pages(struct kcopyd_client *kc, struct list_head *pages)
+{
+	struct list_head *tmp, *tmp2;
+
+	spin_lock(&kc->lock);
+	list_for_each_safe (tmp, tmp2, pages)
+		__push_page(kc, list_entry(tmp, struct page, list));
+	spin_unlock(&kc->lock);
+}
+
+/*
+ * These three functions resize the page pool.
+ */
+static void release_pages(struct list_head *pages)
+{
+	struct page *p;
+	struct list_head *tmp, *tmp2;
+
+	list_for_each_safe (tmp, tmp2, pages) {
+		p = list_entry(tmp, struct page, list);
+		UnlockPage(p);
+		__free_page(p);
+	}
+}
+
+static int client_alloc_pages(struct kcopyd_client *kc, unsigned int nr)
+{
+	unsigned int i;
+	struct page *p;
+	LIST_HEAD(new);
+
+	for (i = 0; i < nr; i++) {
+		p = alloc_page(GFP_KERNEL);
+		if (!p) {
+			release_pages(&new);
+			return -ENOMEM;
+		}
+
+		LockPage(p);
+		list_add(&p->list, &new);
+	}
+
+	kcopyd_put_pages(kc, &new);
+	kc->nr_pages += nr;
+	kc->max_split = kc->nr_pages / PAGES_PER_SUB_JOB;
+	if (kc->max_split > SUB_JOB_COUNT)
+		kc->max_split = SUB_JOB_COUNT;
+
+	return 0;
+}
+
+static void client_free_pages(struct kcopyd_client *kc)
+{
+	BUG_ON(kc->nr_free_pages != kc->nr_pages);
+	release_pages(&kc->pages);
+	kc->nr_free_pages = kc->nr_pages = 0;
+}
+
+/*-----------------------------------------------------------------
+ * kcopyd_jobs need to be allocated by the *clients* of kcopyd,
+ * for this reason we use a mempool to prevent the client from
+ * ever having to do io (which could cause a deadlock).
+ *---------------------------------------------------------------*/
+struct kcopyd_job {
+	struct kcopyd_client *kc;
+	struct list_head list;
+	unsigned int flags;
+
+	/*
+	 * Error state of the job.
+	 */
+	int read_err;
+	unsigned int write_err;
+
+	/*
+	 * Either READ or WRITE
+	 */
+	int rw;
+	struct io_region source;
+
+	/*
+	 * The destinations for the transfer.
+	 */
+	unsigned int num_dests;
+	struct io_region dests[KCOPYD_MAX_REGIONS];
+
+	sector_t offset;
+	unsigned int nr_pages;
+	struct list_head pages;
+
+	/*
+	 * Set this to ensure you are notified when the job has
+	 * completed.  'context' is for callback to use.
+	 */
+	kcopyd_notify_fn fn;
+	void *context;
+
+	/*
+	 * These fields are only used if the job has been split
+	 * into more manageable parts.
+	 */
+	struct semaphore lock;
+	atomic_t sub_jobs;
+	sector_t progress;
+};
+
+/* FIXME: this should scale with the number of pages */
+#define MIN_JOBS 512
+
+static kmem_cache_t *_job_cache;
+static mempool_t *_job_pool;
+
+/*
+ * We maintain three lists of jobs:
+ *
+ * i)   jobs waiting for pages
+ * ii)  jobs that have pages, and are waiting for the io to be issued.
+ * iii) jobs that have completed.
+ *
+ * All three of these are protected by job_lock.
+ */
+static spinlock_t _job_lock = SPIN_LOCK_UNLOCKED;
+
+static LIST_HEAD(_complete_jobs);
+static LIST_HEAD(_io_jobs);
+static LIST_HEAD(_pages_jobs);
+
+static int jobs_init(void)
+{
+	INIT_LIST_HEAD(&_complete_jobs);
+	INIT_LIST_HEAD(&_io_jobs);
+	INIT_LIST_HEAD(&_pages_jobs);
+
+	_job_cache = kmem_cache_create("kcopyd-jobs",
+				       sizeof(struct kcopyd_job),
+				       __alignof__(struct kcopyd_job),
+				       0, NULL, NULL);
+	if (!_job_cache)
+		return -ENOMEM;
+
+	_job_pool = mempool_create(MIN_JOBS, mempool_alloc_slab,
+				   mempool_free_slab, _job_cache);
+	if (!_job_pool) {
+		kmem_cache_destroy(_job_cache);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void jobs_exit(void)
+{
+	BUG_ON(!list_empty(&_complete_jobs));
+	BUG_ON(!list_empty(&_io_jobs));
+	BUG_ON(!list_empty(&_pages_jobs));
+
+	mempool_destroy(_job_pool);
+	kmem_cache_destroy(_job_cache);
+}
+
+/*
+ * Functions to push and pop a job onto the head of a given job
+ * list.
+ */
+static inline struct kcopyd_job *pop(struct list_head *jobs)
+{
+	struct kcopyd_job *job = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&_job_lock, flags);
+
+	if (!list_empty(jobs)) {
+		job = list_entry(jobs->next, struct kcopyd_job, list);
+		list_del(&job->list);
+	}
+	spin_unlock_irqrestore(&_job_lock, flags);
+
+	return job;
+}
+
+static inline void push(struct list_head *jobs, struct kcopyd_job *job)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&_job_lock, flags);
+	list_add_tail(&job->list, jobs);
+	spin_unlock_irqrestore(&_job_lock, flags);
+}
+
+/*
+ * These three functions process 1 item from the corresponding
+ * job list.
+ *
+ * They return:
+ * < 0: error
+ *   0: success
+ * > 0: can't process yet.
+ */
+static int run_complete_job(struct kcopyd_job *job)
+{
+	void *context = job->context;
+	int read_err = job->read_err;
+	unsigned int write_err = job->write_err;
+	kcopyd_notify_fn fn = job->fn;
+
+	kcopyd_put_pages(job->kc, &job->pages);
+	mempool_free(job, _job_pool);
+	fn(read_err, write_err, context);
+	return 0;
+}
+
+static void complete_io(unsigned int error, void *context)
+{
+	struct kcopyd_job *job = (struct kcopyd_job *) context;
+
+	if (error) {
+		if (job->rw == WRITE)
+			job->write_err &= error;
+		else
+			job->read_err = 1;
+
+		if (!test_bit(KCOPYD_IGNORE_ERROR, &job->flags)) {
+			push(&_complete_jobs, job);
+			dm_daemon_wake(&_kcopyd);
+			return;
+		}
+	}
+
+	if (job->rw == WRITE)
+		push(&_complete_jobs, job);
+
+	else {
+		job->rw = WRITE;
+		push(&_io_jobs, job);
+	}
+
+	dm_daemon_wake(&_kcopyd);
+}
+
+/*
+ * Request io on as many buffer heads as we can currently get for
+ * a particular job.
+ */
+static int run_io_job(struct kcopyd_job *job)
+{
+	int r;
+
+	if (job->rw == READ)
+		r = dm_io_async(1, &job->source, job->rw,
+				list_entry(job->pages.next, struct page, list),
+				job->offset, complete_io, job);
+
+	else
+		r = dm_io_async(job->num_dests, job->dests, job->rw,
+				list_entry(job->pages.next, struct page, list),
+				job->offset, complete_io, job);
+
+	return r;
+}
+
+static int run_pages_job(struct kcopyd_job *job)
+{
+	int r;
+
+	job->nr_pages = dm_div_up(job->dests[0].count + job->offset,
+				  SECTORS_PER_PAGE);
+	r = kcopyd_get_pages(job->kc, job->nr_pages, &job->pages);
+	if (!r) {
+		/* this job is ready for io */
+		push(&_io_jobs, job);
+		return 0;
+	}
+
+	if (r == -ENOMEM)
+		/* can't complete now */
+		return 1;
+
+	return r;
+}
+
+/*
+ * Run through a list for as long as possible.  Returns the count
+ * of successful jobs.
+ */
+static int process_jobs(struct list_head *jobs, int (*fn) (struct kcopyd_job *))
+{
+	struct kcopyd_job *job;
+	int r, count = 0;
+
+	while ((job = pop(jobs))) {
+
+		r = fn(job);
+
+		if (r < 0) {
+			/* error this rogue job */
+			if (job->rw == WRITE)
+				job->write_err = (unsigned int) -1;
+			else
+				job->read_err = 1;
+			push(&_complete_jobs, job);
+			break;
+		}
+
+		if (r > 0) {
+			/*
+			 * We couldn't service this job ATM, so
+			 * push this job back onto the list.
+			 */
+			push(jobs, job);
+			break;
+		}
+
+		count++;
+	}
+
+	return count;
+}
+
+/*
+ * kcopyd does this every time it's woken up.
+ */
+static void do_work(void)
+{
+	/*
+	 * The order that these are called is *very* important.
+	 * complete jobs can free some pages for pages jobs.
+	 * Pages jobs when successful will jump onto the io jobs
+	 * list.  io jobs call wake when they complete and it all
+	 * starts again.
+	 */
+	process_jobs(&_complete_jobs, run_complete_job);
+	process_jobs(&_pages_jobs, run_pages_job);
+	process_jobs(&_io_jobs, run_io_job);
+	run_task_queue(&tq_disk);
+}
+
+/*
+ * If we are copying a small region we just dispatch a single job
+ * to do the copy, otherwise the io has to be split up into many
+ * jobs.
+ */
+static void dispatch_job(struct kcopyd_job *job)
+{
+	push(&_pages_jobs, job);
+	dm_daemon_wake(&_kcopyd);
+}
+
+static void segment_complete(int read_err,
+			     unsigned int write_err, void *context)
+{
+	/* FIXME: tidy this function */
+	sector_t progress = 0;
+	sector_t count = 0;
+	struct kcopyd_job *job = (struct kcopyd_job *) context;
+
+	down(&job->lock);
+
+	/* update the error */
+	if (read_err)
+		job->read_err = 1;
+
+	if (write_err)
+		job->write_err &= write_err;
+
+	/*
+	 * Only dispatch more work if there hasn't been an error.
+	 */
+	if ((!job->read_err && !job->write_err) ||
+	    test_bit(KCOPYD_IGNORE_ERROR, &job->flags)) {
+		/* get the next chunk of work */
+		progress = job->progress;
+		count = job->source.count - progress;
+		if (count) {
+			if (count > SUB_JOB_SIZE)
+				count = SUB_JOB_SIZE;
+
+			job->progress += count;
+		}
+	}
+	up(&job->lock);
+
+	if (count) {
+		int i;
+		struct kcopyd_job *sub_job = mempool_alloc(_job_pool, GFP_NOIO);
+
+		memcpy(sub_job, job, sizeof(*job));
+		sub_job->source.sector += progress;
+		sub_job->source.count = count;
+
+		for (i = 0; i < job->num_dests; i++) {
+			sub_job->dests[i].sector += progress;
+			sub_job->dests[i].count = count;
+		}
+
+		sub_job->fn = segment_complete;
+		sub_job->context = job;
+		dispatch_job(sub_job);
+
+	} else if (atomic_dec_and_test(&job->sub_jobs)) {
+
+		/*
+		 * To avoid a race we must keep the job around
+		 * until after the notify function has completed.
+		 * Otherwise the client may try and stop the job
+		 * after we've completed.
+		 */
+		job->fn(read_err, write_err, job->context);
+		mempool_free(job, _job_pool);
+	}
+}
+
+/*
+ * Create some little jobs that will do the move between
+ * them.
+ */
+static void split_job(struct kcopyd_job *job)
+{
+	int nr;
+
+	nr = dm_div_up(job->source.count, SUB_JOB_SIZE);
+	if (nr > job->kc->max_split)
+		nr = job->kc->max_split;
+
+	atomic_set(&job->sub_jobs, nr);
+	while (nr--)
+		segment_complete(0, 0u, job);
+}
+
+int kcopyd_copy(struct kcopyd_client *kc, struct io_region *from,
+		unsigned int num_dests, struct io_region *dests,
+		unsigned int flags, kcopyd_notify_fn fn, void *context)
+{
+	struct kcopyd_job *job;
+
+	/*
+	 * Allocate a new job.
+	 */
+	job = mempool_alloc(_job_pool, GFP_NOIO);
+
+	/*
+	 * set up for the read.
+	 */
+	job->kc = kc;
+	job->flags = flags;
+	job->read_err = 0;
+	job->write_err = 0;
+	job->rw = READ;
+
+	memcpy(&job->source, from, sizeof(*from));
+
+	job->num_dests = num_dests;
+	memcpy(&job->dests, dests, sizeof(*dests) * num_dests);
+
+	job->offset = 0;
+	job->nr_pages = 0;
+	INIT_LIST_HEAD(&job->pages);
+
+	job->fn = fn;
+	job->context = context;
+
+	if (job->source.count < SUB_JOB_SIZE)
+		dispatch_job(job);
+
+	else {
+		init_MUTEX(&job->lock);
+		job->progress = 0;
+		split_job(job);
+	}
+
+	return 0;
+}
+
+/*
+ * Cancels a kcopyd job, eg. someone might be deactivating a
+ * mirror.
+ */
+int kcopyd_cancel(struct kcopyd_job *job, int block)
+{
+	/* FIXME: finish */
+	return -1;
+}
+
+/*-----------------------------------------------------------------
+ * Unit setup
+ *---------------------------------------------------------------*/
+static DECLARE_MUTEX(_client_lock);
+static LIST_HEAD(_clients);
+
+static int client_add(struct kcopyd_client *kc)
+{
+	down(&_client_lock);
+	list_add(&kc->list, &_clients);
+	up(&_client_lock);
+	return 0;
+}
+
+static void client_del(struct kcopyd_client *kc)
+{
+	down(&_client_lock);
+	list_del(&kc->list);
+	up(&_client_lock);
+}
+
+int kcopyd_client_create(unsigned int nr_pages, struct kcopyd_client **result)
+{
+	int r = 0;
+	struct kcopyd_client *kc;
+
+	if (nr_pages * SECTORS_PER_PAGE < SUB_JOB_SIZE) {
+		DMERR("kcopyd client requested %u pages: minimum is %lu",
+		      nr_pages, SUB_JOB_SIZE / SECTORS_PER_PAGE);
+		return -ENOMEM;
+	}
+
+	kc = kmalloc(sizeof(*kc), GFP_KERNEL);
+	if (!kc)
+		return -ENOMEM;
+
+	kc->lock = SPIN_LOCK_UNLOCKED;
+	INIT_LIST_HEAD(&kc->pages);
+	kc->nr_pages = kc->nr_free_pages = 0;
+	r = client_alloc_pages(kc, nr_pages);
+	if (r) {
+		kfree(kc);
+		return r;
+	}
+
+	r = dm_io_get(nr_pages);
+	if (r) {
+		client_free_pages(kc);
+		kfree(kc);
+		return r;
+	}
+
+	r = client_add(kc);
+	if (r) {
+		dm_io_put(nr_pages);
+		client_free_pages(kc);
+		kfree(kc);
+		return r;
+	}
+
+	*result = kc;
+	return 0;
+}
+
+void kcopyd_client_destroy(struct kcopyd_client *kc)
+{
+	dm_io_put(kc->nr_pages);
+	client_free_pages(kc);
+	client_del(kc);
+	kfree(kc);
+}
+
+
+int __init kcopyd_init(void)
+{
+	int r;
+
+	r = jobs_init();
+	if (r)
+		return r;
+
+	r = dm_daemon_start(&_kcopyd, "kcopyd", do_work);
+	if (r)
+		jobs_exit();
+
+	return r;
+}
+
+void kcopyd_exit(void)
+{
+	jobs_exit();
+	dm_daemon_stop(&_kcopyd);
+}
+
+EXPORT_SYMBOL(kcopyd_client_create);
+EXPORT_SYMBOL(kcopyd_client_destroy);
+EXPORT_SYMBOL(kcopyd_copy);
+EXPORT_SYMBOL(kcopyd_cancel);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/kcopyd.h linux-2.4.27-leo/drivers/md/kcopyd.h
--- linux-2.4.27/drivers/md/kcopyd.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/md/kcopyd.h	2004-09-20 21:40:18.000000000 +0100
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2001 Sistina Software
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef DM_KCOPYD_H
+#define DM_KCOPYD_H
+
+/*
+ * Needed for the definition of offset_t.
+ */
+#include <linux/device-mapper.h>
+#include <linux/iobuf.h>
+
+#include "dm-io.h"
+
+int kcopyd_init(void);
+void kcopyd_exit(void);
+
+/* FIXME: make this configurable */
+#define KCOPYD_MAX_REGIONS 8
+
+#define KCOPYD_IGNORE_ERROR 1
+
+/*
+ * To use kcopyd you must first create a kcopyd client object.
+ */
+struct kcopyd_client;
+int kcopyd_client_create(unsigned int num_pages, struct kcopyd_client **result);
+void kcopyd_client_destroy(struct kcopyd_client *kc);
+
+/*
+ * Submit a copy job to kcopyd.  This is built on top of the
+ * previous three fns.
+ *
+ * read_err is a boolean,
+ * write_err is a bitset, with 1 bit for each destination region
+ */
+typedef void (*kcopyd_notify_fn)(int read_err,
+				 unsigned int write_err, void *context);
+
+int kcopyd_copy(struct kcopyd_client *kc, struct io_region *from,
+		unsigned int num_dests, struct io_region *dests,
+		unsigned int flags, kcopyd_notify_fn fn, void *context);
+
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/lvm.c linux-2.4.27-leo/drivers/md/lvm.c
--- linux-2.4.27/drivers/md/lvm.c	2004-02-20 14:11:42.000000000 +0000
+++ linux-2.4.27-leo/drivers/md/lvm.c	2004-09-17 03:19:08.000000000 +0100
@@ -236,9 +236,6 @@
 #define DEVICE_OFF(device)
 #define LOCAL_END_REQUEST
 
-/* lvm_do_lv_create calls fsync_dev_lockfs()/unlockfs() */
-/* #define	LVM_VFS_ENHANCEMENT */
-
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -2250,12 +2247,8 @@
 	if (lv_ptr->lv_access & LV_SNAPSHOT) {
 		lv_t *org = lv_ptr->lv_snapshot_org, *last;
 
-		/* sync the original logical volume */
-		fsync_dev(org->lv_dev);
-#ifdef	LVM_VFS_ENHANCEMENT
 		/* VFS function call to sync and lock the filesystem */
 		fsync_dev_lockfs(org->lv_dev);
-#endif
 
 		down_write(&org->lv_lock);
 		org->lv_access |= LV_SNAPSHOT_ORG;
@@ -2281,11 +2274,9 @@
 	else
 		set_device_ro(lv_ptr->lv_dev, 1);
 
-#ifdef	LVM_VFS_ENHANCEMENT
 /* VFS function call to unlock the filesystem */
 	if (lv_ptr->lv_access & LV_SNAPSHOT)
 		unlockfs(lv_ptr->lv_snapshot_org->lv_dev);
-#endif
 
 	lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].de =
 	    lvm_fs_create_lv(vg_ptr, lv_ptr);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/Makefile linux-2.4.27-leo/drivers/md/Makefile
--- linux-2.4.27/drivers/md/Makefile	2001-11-11 18:09:32.000000000 +0000
+++ linux-2.4.27-leo/drivers/md/Makefile	2004-09-17 03:19:08.000000000 +0100
@@ -4,24 +4,41 @@
 
 O_TARGET	:= mddev.o
 
-export-objs	:= md.o xor.o
-list-multi	:= lvm-mod.o
+export-objs	:= md.o xor.o dm-table.o dm-target.o kcopyd.o dm-daemon.o \
+		   dm-log.o dm-io.o dm.o
+
+list-multi	:= lvm-mod.o dm-mod.o dm-mirror-mod.o
 lvm-mod-objs	:= lvm.o lvm-snap.o lvm-fs.o
+dm-mod-objs	:= dm.o dm-table.o dm-target.o dm-ioctl.o \
+		   dm-linear.o dm-stripe.o dm-snapshot.o dm-exception-store.o \
+		   kcopyd.o dm-daemon.o dm-io.o
+dm-mirror-mod-objs := dm-raid1.o dm-log.o
 
 # Note: link order is important.  All raid personalities
 # and xor.o must come before md.o, as they each initialise 
 # themselves, and md.o may use the personalities when it 
 # auto-initialised.
 
-obj-$(CONFIG_MD_LINEAR)		+= linear.o
-obj-$(CONFIG_MD_RAID0)		+= raid0.o
-obj-$(CONFIG_MD_RAID1)		+= raid1.o
-obj-$(CONFIG_MD_RAID5)		+= raid5.o xor.o
-obj-$(CONFIG_MD_MULTIPATH)	+= multipath.o
-obj-$(CONFIG_BLK_DEV_MD)	+= md.o
-obj-$(CONFIG_BLK_DEV_LVM)	+= lvm-mod.o
+obj-$(CONFIG_MD_LINEAR)			+= linear.o
+obj-$(CONFIG_MD_RAID0)			+= raid0.o
+obj-$(CONFIG_MD_RAID1)			+= raid1.o
+obj-$(CONFIG_MD_RAID5)			+= raid5.o xor.o
+obj-$(CONFIG_MD_MULTIPATH)		+= multipath.o
+obj-$(CONFIG_BLK_DEV_MD)		+= md.o
+
+obj-$(CONFIG_BLK_DEV_LVM)		+= lvm-mod.o
+
+obj-$(CONFIG_BLK_DEV_DM)		+= dm-mod.o
+obj-$(CONFIG_BLK_DEV_DM_MIRROR)		+= dm-mirror.o
 
 include $(TOPDIR)/Rules.make
 
 lvm-mod.o: $(lvm-mod-objs)
 	$(LD) -r -o $@ $(lvm-mod-objs)
+
+dm-mod.o: $(dm-mod-objs)
+	$(LD) -r -o $@ $(dm-mod-objs)
+
+dm-mirror.o: $(dm-mirror-mod-objs)
+	$(LD) -r -o $@ $(dm-mirror-mod-objs)
+
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/md.c linux-2.4.27-leo/drivers/md/md.c
--- linux-2.4.27/drivers/md/md.c	2003-08-25 12:44:42.000000000 +0100
+++ linux-2.4.27-leo/drivers/md/md.c	2004-09-17 03:19:32.000000000 +0100
@@ -12,6 +12,7 @@
    - kmod support by: Cyrus Durgin
    - RAID0 bugfixes: Mark Anthony Lisher <markal@iname.com>
    - Devfs support by Richard Gooch <rgooch@atnf.csiro.au>
+   - Verbose startup messages by Paul Evans <nerd@freeuk.com>
 
    - lots of fixes and improvements to the RAID1/RAID5 and generic
      RAID code (such as request based resynchronization):
@@ -524,7 +525,8 @@
 		printk(NO_SB,partition_name(dev));
 		return -EINVAL;
 	}
-	printk(KERN_INFO " [events: %08lx]\n", (unsigned long)rdev->sb->events_lo);
+	VERBMSG(printk(KERN_INFO " [events: %08lx]\n", 
+                        (unsigned long)rdev->sb->events_lo))
 	ret = 0;
 abort:
 	return ret;
@@ -633,7 +635,8 @@
 	md_list_add(&rdev->same_set, &mddev->disks);
 	rdev->mddev = mddev;
 	mddev->nb_dev++;
-	printk(KERN_INFO "md: bind<%s,%d>\n", partition_name(rdev->dev), mddev->nb_dev);
+	VERBMSG(printk(KERN_INFO "md: bind<%s,%d>\n", 
+                partition_name(rdev->dev), mddev->nb_dev))
 }
 
 static void unbind_rdev_from_array(mdk_rdev_t * rdev)
@@ -645,8 +648,8 @@
 	md_list_del(&rdev->same_set);
 	MD_INIT_LIST_HEAD(&rdev->same_set);
 	rdev->mddev->nb_dev--;
-	printk(KERN_INFO "md: unbind<%s,%d>\n", partition_name(rdev->dev),
-						 rdev->mddev->nb_dev);
+	VERBMSG(printk(KERN_INFO "md: unbind<%s,%d>\n", 
+                partition_name(rdev->dev), rdev->mddev->nb_dev))
 	rdev->mddev = NULL;
 }
 
@@ -949,7 +952,8 @@
 		goto skip;
 	}
 
-	printk(KERN_INFO "(write) %s's sb offset: %ld\n", partition_name(dev), sb_offset);
+	VERBMSG(printk(KERN_INFO "(write) %s's sb offset: %ld\n", 
+		partition_name(dev), sb_offset))
 
 	if (!sync_page_io(dev, sb_offset<<1, MD_SB_BYTES, rdev->sb_page, WRITE)) {
 		printk("md: write_disk_sb failed for device %s\n", partition_name(dev));
@@ -1009,6 +1013,7 @@
 	int err, count = 100;
 	struct md_list_head *tmp;
 	mdk_rdev_t *rdev;
+	char *part_name;
 
 	if (!mddev->sb_dirty) {
 		printk("hm, md_update_sb() called without ->sb_dirty == 1, from %p.\n", __builtin_return_address(0));
@@ -1038,29 +1043,30 @@
 	if (mddev->sb->not_persistent)
 		return 0;
 
-	printk(KERN_INFO "md: updating md%d RAID superblock on device\n",
-					mdidx(mddev));
+	VERBMSG(printk(KERN_INFO "md: updating md%d RAID superblock on device\n",
+					mdidx(mddev)))
 
 	err = 0;
 	ITERATE_RDEV(mddev,rdev,tmp) {
-		printk(KERN_INFO "md: ");
+		part_name = partition_name(rdev->dev);
 		if (rdev->faulty)
-			printk("(skipping faulty ");
+			printk(KERN_INFO "md: (skipping faulty %s)\n", part_name);
 		if (rdev->alias_device)
-			printk("(skipping alias ");
+			printk(KERN_INFO "md: (skipping alias %s)\n", part_name);
 		if (!rdev->faulty && disk_faulty(&rdev->sb->this_disk)) {
-			printk("(skipping new-faulty %s )\n",
-			       partition_name(rdev->dev));
+			printk(KERN_INFO "md: (skipping new-faulty %s)\n",
+			       part_name);
 			continue;
 		}
-		printk("%s ", partition_name(rdev->dev));
+		VERBMSG(printk(KERN_INFO "md: %s ", part_name))
+
 		if (!rdev->faulty && !rdev->alias_device) {
-			printk("[events: %08lx]",
-				(unsigned long)rdev->sb->events_lo);
+			VERBMSG(printk("[events: %08lx]",
+				(unsigned long)rdev->sb->events_lo))
 			err += write_disk_sb(rdev);
-		} else
-			printk(")\n");
+		}
 	}
+	VERBMSG(printk("\n"))
 	if (err) {
 		if (--count) {
 			printk(KERN_ERR "md: errors occurred during superblock update, repeating\n");
@@ -1246,9 +1252,9 @@
 					rdev->sb->events_hi--;
 		}
 
-		printk(KERN_INFO "md: %s's event counter: %08lx\n",
+		VERBMSG(printk(KERN_INFO "md: %s's event counter: %08lx\n",
 		       partition_name(rdev->dev),
-			(unsigned long)rdev->sb->events_lo);
+			(unsigned long)rdev->sb->events_lo))
 		if (!freshest) {
 			freshest = rdev;
 			continue;
@@ -1600,12 +1606,13 @@
 	}
 	md_maxreadahead[mdidx(mddev)] = readahead;
 
-	printk(KERN_INFO "md%d: max total readahead window set to %ldk\n",
-		mdidx(mddev), readahead*(PAGE_SIZE/1024));
+	VERBMSG(printk(KERN_INFO "md%d: max total readahead window set to %ldk\n",
+	        	mdidx(mddev), readahead*(PAGE_SIZE/1024));
+
+        	printk(KERN_INFO
+		        "md%d: %d data-disks, max readahead per data-disk: %ldk\n",
+			mdidx(mddev), data_disks, readahead/data_disks*(PAGE_SIZE/1024)))
 
-	printk(KERN_INFO
-		"md%d: %d data-disks, max readahead per data-disk: %ldk\n",
-			mdidx(mddev), data_disks, readahead/data_disks*(PAGE_SIZE/1024));
 	return 0;
 abort:
 	return 1;
@@ -1684,9 +1691,10 @@
 			return -EINVAL;
 		}
 	} else
-		if (chunk_size)
-			printk(KERN_INFO "md: RAID level %d does not need chunksize! Continuing anyway.\n",
-			       mddev->sb->level);
+		if (chunk_size) {
+			VERBMSG(printk(KERN_INFO "md: RAID level %d does not need chunksize! Continuing anyway.\n",
+			       mddev->sb->level))
+                }
 
 	if (pnum >= MAX_PERSONALITY) {
 		MD_BUG();
@@ -1857,7 +1865,7 @@
 			 * interrupted.
 			 */
 			if (!mddev->recovery_running && !resync_interrupted) {
-				printk(KERN_INFO "md: marking sb clean...\n");
+				VERBMSG(printk(KERN_INFO "md: marking sb clean...\n"))
 				mddev->sb->state |= 1 << MD_SB_CLEAN;
 			}
 			mddev->sb_dirty = 1;
@@ -1871,11 +1879,13 @@
 	 * Free resources if final stop
 	 */
 	if (!ro) {
-		printk(KERN_INFO "md: md%d stopped.\n", mdidx(mddev));
+		VERBMSG(printk(KERN_INFO "md: md%d stopped.\n", mdidx(mddev)))
 		free_mddev(mddev);
 
-	} else
-		printk(KERN_INFO "md: md%d switched to read-only mode.\n", mdidx(mddev));
+	} else {
+		VERBMSG(printk(KERN_INFO "md: md%d switched to read-only mode.\n", mdidx(mddev)))
+        }
+
 out:
 	return err;
 }
@@ -1898,8 +1908,6 @@
 
 static void autorun_array(mddev_t *mddev)
 {
-	mdk_rdev_t *rdev;
-	struct md_list_head *tmp;
 	int err;
 
 	if (mddev->disks.prev == &mddev->disks) {
@@ -1907,12 +1915,17 @@
 		return;
 	}
 
-	printk(KERN_INFO "md: running: ");
-
-	ITERATE_RDEV(mddev,rdev,tmp) {
-		printk("<%s>", partition_name(rdev->dev));
-	}
-	printk("\n");
+        VERBMSG({       /* remember; VERBMSG is just an #ifdef section */
+                mdk_rdev_t *rdev;
+                struct md_list_head *tmp;
+                
+        	printk(KERN_INFO "md: running: ");
+
+        	ITERATE_RDEV(mddev,rdev,tmp) {
+	        	printk("<%s>", partition_name(rdev->dev));
+        	}
+	        printk("\n");
+        })
 
 	err = do_md_run (mddev);
 	if (err) {
@@ -1946,12 +1959,13 @@
 	kdev_t md_kdev;
 
 
-	printk(KERN_INFO "md: autorun ...\n");
+	VERBMSG(printk(KERN_INFO "md: autorun ...\n"))
 	while (pending_raid_disks.next != &pending_raid_disks) {
 		rdev0 = md_list_entry(pending_raid_disks.next,
 					 mdk_rdev_t, pending);
 
-		printk(KERN_INFO "md: considering %s ...\n", partition_name(rdev0->dev));
+		VERBMSG(printk(KERN_INFO "md: considering %s ...\n", 
+                        partition_name(rdev0->dev)))
 		MD_INIT_LIST_HEAD(&candidates);
 		ITERATE_RDEV_PENDING(rdev,tmp) {
 			if (uuid_equal(rdev0, rdev)) {
@@ -1961,7 +1975,8 @@
 					       partition_name(rdev->dev), partition_name(rdev0->dev));
 					continue;
 				}
-				printk(KERN_INFO "md:  adding %s ...\n", partition_name(rdev->dev));
+				VERBMSG(printk(KERN_INFO "md:  adding %s ...\n", 
+                                        partition_name(rdev->dev)))
 				md_list_del(&rdev->pending);
 				md_list_add(&rdev->pending, &candidates);
 			}
@@ -1995,7 +2010,7 @@
 		}
 		autorun_array(mddev);
 	}
-	printk(KERN_INFO "md: ... autorun DONE.\n");
+	VERBMSG(printk(KERN_INFO "md: ... autorun DONE.\n"))
 }
 
 /*
@@ -3315,7 +3330,7 @@
 	}
 
 	pers[pnum] = p;
-	printk(KERN_INFO "md: %s personality registered as nr %d\n", p->name, pnum);
+	VERBMSG(printk(KERN_INFO "md: %s personality registered as nr %d\n", p->name, pnum))
 	return 0;
 }
 
@@ -3326,7 +3341,7 @@
 		return -EINVAL;
 	}
 
-	printk(KERN_INFO "md: %s personality unregistered\n", pers[pnum]->name);
+	VERBMSG(printk(KERN_INFO "md: %s personality unregistered\n", pers[pnum]->name))
 	pers[pnum] = NULL;
 	return 0;
 }
@@ -3690,6 +3705,7 @@
 		 */
 		md_mdelay(1000*1);
 	}
+
 	return NOTIFY_DONE;
 }
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/raid0.c linux-2.4.27-leo/drivers/md/raid0.c
--- linux-2.4.27/drivers/md/raid0.c	2003-06-13 15:51:34.000000000 +0100
+++ linux-2.4.27-leo/drivers/md/raid0.c	2004-09-17 03:19:32.000000000 +0100
@@ -38,12 +38,12 @@
 	conf->nr_strip_zones = 0;
  
 	ITERATE_RDEV_ORDERED(mddev,rdev1,j1) {
-		printk("raid0: looking at %s\n", partition_name(rdev1->dev));
+                VERBMSG(printk("raid0: looking at %s\n", partition_name(rdev1->dev)))
 		c = 0;
 		ITERATE_RDEV_ORDERED(mddev,rdev2,j2) {
-			printk("raid0:   comparing %s(%ld) with %s(%ld)\n", partition_name(rdev1->dev), rdev1->size, partition_name(rdev2->dev), rdev2->size);
+			VERBMSG(printk("raid0:   comparing %s(%ld) with %s(%ld)\n", partition_name(rdev1->dev), rdev1->size, partition_name(rdev2->dev), rdev2->size))
 			if (rdev2 == rdev1) {
-				printk("raid0:   END\n");
+				VERBMSG(printk("raid0:   END\n"))
 				break;
 			}
 			if (rdev2->size == rdev1->size)
@@ -52,19 +52,19 @@
 				 * Not unique, dont count it as a new
 				 * group
 				 */
-				printk("raid0:   EQUAL\n");
+				VERBMSG(printk("raid0:   EQUAL\n"))
 				c = 1;
 				break;
 			}
-			printk("raid0:   NOT EQUAL\n");
+			VERBMSG(printk("raid0:   NOT EQUAL\n"))
 		}
 		if (!c) {
-			printk("raid0:   ==> UNIQUE\n");
+			VERBMSG(printk("raid0:   ==> UNIQUE\n"))
 			conf->nr_strip_zones++;
-			printk("raid0: %d zones\n", conf->nr_strip_zones);
+			VERBMSG(printk("raid0: %d zones\n", conf->nr_strip_zones))
 		}
 	}
-		printk("raid0: FINAL %d zones\n", conf->nr_strip_zones);
+		VERBMSG(printk("raid0: FINAL %d zones\n", conf->nr_strip_zones))
 
 	conf->strip_zone = vmalloc(sizeof(struct strip_zone)*
 				conf->nr_strip_zones);
@@ -80,30 +80,31 @@
 	{
 		struct strip_zone *zone = conf->strip_zone + i;
 
-		printk("raid0: zone %d\n", i);
+		VERBMSG(printk("raid0: zone %d\n", i))
 		zone->dev_offset = current_offset;
 		smallest = NULL;
 		c = 0;
 
 		ITERATE_RDEV_ORDERED(mddev,rdev,j) {
 
-			printk("raid0: checking %s ...", partition_name(rdev->dev));
+			VERBMSG(printk("raid0: checking %s ...", partition_name(rdev->dev)))
 			if (rdev->size > current_offset)
 			{
-				printk(" contained as device %d\n", c);
+				VERBMSG(printk(" contained as device %d\n", c))
 				zone->dev[c] = rdev;
 				c++;
 				if (!smallest || (rdev->size <smallest->size)) {
 					smallest = rdev;
-					printk("  (%ld) is smallest!.\n", rdev->size);
+					VERBMSG(printk("  (%ld) is smallest!.\n", rdev->size))
 				}
-			} else
-				printk(" nope.\n");
+			} else {
+				VERBMSG(printk(" nope.\n"))
+                        }
 		}
 
 		zone->nb_dev = c;
 		zone->size = (smallest->size - current_offset) * c;
-		printk("raid0: zone->nb_dev: %d, size: %ld\n",zone->nb_dev,zone->size);
+		VERBMSG(printk("raid0: zone->nb_dev: %d, size: %ld\n",zone->nb_dev,zone->size))
 
 		if (!conf->smallest || (zone->size < conf->smallest->size))
 			conf->smallest = zone;
@@ -112,9 +113,9 @@
 		curr_zone_offset += zone->size;
 
 		current_offset = smallest->size;
-		printk("raid0: current zone offset: %ld\n", current_offset);
+		VERBMSG(printk("raid0: current zone offset: %ld\n", current_offset))
 	}
-	printk("raid0: done.\n");
+	VERBMSG(printk("raid0: done.\n"))
 	return 0;
 }
 
@@ -138,15 +139,15 @@
 	if (create_strip_zones (mddev)) 
 		goto out_free_conf;
 
-	printk("raid0 : md_size is %d blocks.\n", md_size[mdidx(mddev)]);
-	printk("raid0 : conf->smallest->size is %ld blocks.\n", conf->smallest->size);
+	VERBMSG(printk("raid0: md_size is %d blocks.\n", md_size[mdidx(mddev)]);
+	          printk("raid0: conf->smallest->size is %ld blocks.\n", conf->smallest->size))
 	nb_zone = md_size[mdidx(mddev)]/conf->smallest->size +
 			(md_size[mdidx(mddev)] % conf->smallest->size ? 1 : 0);
-	printk("raid0 : nb_zone is %ld.\n", nb_zone);
+	VERBMSG(printk("raid0: nb_zone is %ld.\n", nb_zone))
 	conf->nr_zones = nb_zone;
 
-	printk("raid0 : Allocating %ld bytes for hash.\n",
-				nb_zone*sizeof(struct raid0_hash));
+	VERBMSG(printk("raid0: Allocating %ld bytes for hash.\n",
+				nb_zone*sizeof(struct raid0_hash)))
 
 	conf->hash_table = vmalloc (sizeof (struct raid0_hash)*nb_zone);
 	if (!conf->hash_table)
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/raid1.c linux-2.4.27-leo/drivers/md/raid1.c
--- linux-2.4.27/drivers/md/raid1.c	2004-05-19 21:34:38.000000000 +0100
+++ linux-2.4.27-leo/drivers/md/raid1.c	2004-09-17 03:19:32.000000000 +0100
@@ -1627,8 +1627,9 @@
 					disk_idx);
 				continue;
 			}
-			printk(OPERATIONAL, partition_name(rdev->dev),
- 					disk_idx);
+			VERBMSG(printk(OPERATIONAL, 
+                                        partition_name(rdev->dev),
+ 					disk_idx))
 			disk->number = descriptor->number;
 			disk->raid_disk = disk_idx;
 			disk->dev = rdev->dev;
@@ -1643,7 +1644,7 @@
 		/*
 		 * Must be a spare disk ..
 		 */
-			printk(SPARE, partition_name(rdev->dev));
+			VERBMSG(printk(SPARE, partition_name(rdev->dev)))
 			disk->number = descriptor->number;
 			disk->raid_disk = disk_idx;
 			disk->dev = rdev->dev;
@@ -1766,7 +1767,8 @@
 		md_recover_arrays();
 
 
-	printk(ARRAY_IS_ACTIVE, mdidx(mddev), sb->active_disks, sb->raid_disks);
+	VERBMSG(printk(ARRAY_IS_ACTIVE, mdidx(mddev), 
+			sb->active_disks, sb->raid_disks))
 	/*
 	 * Ok, everything is just fine now
 	 */
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/md/raid5.c linux-2.4.27-leo/drivers/md/raid5.c
--- linux-2.4.27/drivers/md/raid5.c	2004-09-17 02:38:44.000000000 +0100
+++ linux-2.4.27-leo/drivers/md/raid5.c	2004-09-17 03:19:32.000000000 +0100
@@ -1448,7 +1448,8 @@
 				printk(KERN_ERR "raid5: disabled device %s (device %d already operational)\n", partition_name(rdev->dev), raid_disk);
 				continue;
 			}
-			printk(KERN_INFO "raid5: device %s operational as raid disk %d\n", partition_name(rdev->dev), raid_disk);
+			VERBMSG(printk(KERN_INFO "raid5: device %s operational as raid disk %d\n", 
+                                partition_name(rdev->dev), raid_disk))
 	
 			disk->number = desc->number;
 			disk->raid_disk = raid_disk;
@@ -1461,7 +1462,8 @@
 			/*
 			 * Must be a spare disk ..
 			 */
-			printk(KERN_INFO "raid5: spare disk %s\n", partition_name(rdev->dev));
+			VERBMSG(printk(KERN_INFO "raid5: spare disk %s\n", 
+                                partition_name(rdev->dev)))
 			disk->number = desc->number;
 			disk->raid_disk = raid_disk;
 			disk->dev = rdev->dev;
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/pci/proc.c linux-2.4.27-leo/drivers/pci/proc.c
--- linux-2.4.27/drivers/pci/proc.c	2004-09-17 02:38:45.000000000 +0100
+++ linux-2.4.27-leo/drivers/pci/proc.c	2004-09-17 03:19:58.000000000 +0100
@@ -564,7 +564,15 @@
 		pci_for_each_dev(dev) {
 			pci_proc_attach_device(dev);
 		}
+#ifdef CONFIG_GRKERNSEC_PROC_ADD
+#ifdef CONFIG_GRKERNSEC_PROC_USER
+		entry = create_proc_entry("pci", S_IRUSR, NULL);
+#elif CONFIG_GRKERNSEC_PROC_USERGROUP
+		entry = create_proc_entry("pci", S_IRUSR | S_IRGRP, NULL);
+#endif
+#else
 		entry = create_proc_entry("pci", 0, NULL);
+#endif
 		if (entry)
 			entry->proc_fops = &proc_pci_operations;
 	}
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/adm1021.c linux-2.4.27-leo/drivers/sensors/adm1021.c
--- linux-2.4.27/drivers/sensors/adm1021.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/adm1021.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,594 @@
+/*
+    adm1021.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl> and
+    Philip Edelbrock <phil@netroedge.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x18, 0x1a, 0x29, 0x2b,
+	0x4c, 0x4e, SENSORS_I2C_END
+};
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_8(adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm, mc1066);
+
+/* adm1021 constants specified below */
+
+/* The adm1021 registers */
+/* Read-only */
+#define ADM1021_REG_TEMP 0x00
+#define ADM1021_REG_REMOTE_TEMP 0x01
+#define ADM1021_REG_STATUS 0x02
+#define ADM1021_REG_MAN_ID 0x0FE	/* 0x41 = Analog Devices, 0x49 = TI,
+                                       0x4D = Maxim, 0x23 = Genesys , 0x54 = Onsemi*/
+#define ADM1021_REG_DEV_ID 0x0FF	/* ADM1021 = 0x0X, ADM1021A/ADM1023 = 0x3X */
+#define ADM1021_REG_DIE_CODE 0x0FF	/* MAX1617A */
+/* These use different addresses for reading/writing */
+#define ADM1021_REG_CONFIG_R 0x03
+#define ADM1021_REG_CONFIG_W 0x09
+#define ADM1021_REG_CONV_RATE_R 0x04
+#define ADM1021_REG_CONV_RATE_W 0x0A
+/* These are for the ADM1023's additional precision on the remote temp sensor */
+#define ADM1021_REG_REM_TEMP_PREC 0x010
+#define ADM1021_REG_REM_OFFSET 0x011
+#define ADM1021_REG_REM_OFFSET_PREC 0x012
+#define ADM1021_REG_REM_TOS_PREC 0x013
+#define ADM1021_REG_REM_THYST_PREC 0x014
+/* limits */
+#define ADM1021_REG_TOS_R 0x05
+#define ADM1021_REG_TOS_W 0x0B
+#define ADM1021_REG_REMOTE_TOS_R 0x07
+#define ADM1021_REG_REMOTE_TOS_W 0x0D
+#define ADM1021_REG_THYST_R 0x06
+#define ADM1021_REG_THYST_W 0x0C
+#define ADM1021_REG_REMOTE_THYST_R 0x08
+#define ADM1021_REG_REMOTE_THYST_W 0x0E
+/* write-only */
+#define ADM1021_REG_ONESHOT 0x0F
+
+#define ADM1021_ALARM_TEMP (ADM1021_ALARM_TEMP_HIGH | ADM1021_ALARM_TEMP_LOW)
+#define ADM1021_ALARM_RTEMP (ADM1021_ALARM_RTEMP_HIGH | ADM1021_ALARM_RTEMP_LOW\
+                             | ADM1021_ALARM_RTEMP_NA)
+#define ADM1021_ALARM_ALL  (ADM1021_ALARM_TEMP | ADM1021_ALARM_RTEMP)
+
+/* Conversions. Rounding and limit checking is only done on the TO_REG
+   variants. Note that you should be a bit careful with which arguments
+   these macros are called: arguments may be evaluated more than once.
+   Fixing this is just not worth it. */
+/* Conversions  note: 1021 uses normal integer signed-byte format*/
+#define TEMP_FROM_REG(val) (val > 127 ? val-256 : val)
+#define TEMP_TO_REG(val)   (SENSORS_LIMIT((val < 0 ? val+256 : val),0,255))
+
+/* Each client has this additional data */
+struct adm1021_data {
+	struct i2c_client client;
+	int sysctl_id;
+	enum chips type;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 temp, temp_os, temp_hyst;	/* Register values */
+	u8 remote_temp, remote_temp_os, remote_temp_hyst, alarms, die_code;
+	u8 fail;
+        /* Special values for ADM1023 only */
+	u8 remote_temp_prec, remote_temp_os_prec, remote_temp_hyst_prec, 
+	   remote_temp_offset, remote_temp_offset_prec;
+};
+
+static int adm1021_attach_adapter(struct i2c_adapter *adapter);
+static int adm1021_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static void adm1021_init_client(struct i2c_client *client);
+static int adm1021_detach_client(struct i2c_client *client);
+static int adm1021_read_value(struct i2c_client *client, u8 reg);
+static int adm1021_rd_good(u8 *val, struct i2c_client *client, u8 reg, u8 mask);
+static int adm1021_write_value(struct i2c_client *client, u8 reg,
+			       u16 value);
+static void adm1021_temp(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void adm1021_remote_temp(struct i2c_client *client, int operation,
+				int ctl_name, int *nrels_mag,
+				long *results);
+static void adm1021_alarms(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+static void adm1021_die_code(struct i2c_client *client, int operation,
+			     int ctl_name, int *nrels_mag, long *results);
+static void adm1021_update_client(struct i2c_client *client);
+
+/* (amalysh) read only mode, otherwise any limit's writing confuse BIOS */
+static int read_only = 0;
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver adm1021_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "ADM1021, MAX1617 sensor driver",
+	.id		= I2C_DRIVERID_ADM1021,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= adm1021_attach_adapter,
+	.detach_client	= adm1021_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+
+#define ADM1021_SYSCTL_TEMP 1200
+#define ADM1021_SYSCTL_REMOTE_TEMP 1201
+#define ADM1021_SYSCTL_DIE_CODE 1202
+#define ADM1021_SYSCTL_ALARMS 1203
+
+#define ADM1021_ALARM_TEMP_HIGH 0x40
+#define ADM1021_ALARM_TEMP_LOW 0x20
+#define ADM1021_ALARM_RTEMP_HIGH 0x10
+#define ADM1021_ALARM_RTEMP_LOW 0x08
+#define ADM1021_ALARM_RTEMP_NA 0x04
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected adm1021. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized
+   when a new copy is allocated. */
+static ctl_table adm1021_dir_table_template[] = {
+	{ADM1021_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1021_temp},
+	{ADM1021_SYSCTL_REMOTE_TEMP, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1021_remote_temp},
+	{ADM1021_SYSCTL_DIE_CODE, "die_code", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1021_die_code},
+	{ADM1021_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1021_alarms},
+	{0}
+};
+
+static ctl_table adm1021_max_dir_table_template[] = {
+	{ADM1021_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1021_temp},
+	{ADM1021_SYSCTL_REMOTE_TEMP, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1021_remote_temp},
+	{ADM1021_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1021_alarms},
+	{0}
+};
+
+static int adm1021_id = 0;
+
+static int adm1021_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, adm1021_detect);
+}
+
+static int adm1021_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct adm1021_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+
+	/* Make sure we aren't probing the ISA bus!! This is just a safety check
+	   at this moment; i2c_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("adm1021.o: adm1021_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		goto error0;
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access adm1021_{read,write}_value. */
+
+	if (!(data = kmalloc(sizeof(struct adm1021_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto error0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &adm1021_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+
+	if (kind < 0) {
+		if ((adm1021_read_value(new_client, ADM1021_REG_STATUS) & 0x03) != 0x00
+		 || (adm1021_read_value(new_client, ADM1021_REG_CONFIG_R) & 0x3F) != 0x00
+		 || (adm1021_read_value(new_client, ADM1021_REG_CONV_RATE_R) & 0xF8) != 0x00) {
+			err = -ENODEV;
+ 			goto error1;
+		}
+	}
+
+	/* Determine the chip type. */
+
+	if (kind <= 0) {
+		i = adm1021_read_value(new_client, ADM1021_REG_MAN_ID);
+		if (i == 0x41)
+		  if ((adm1021_read_value (new_client, ADM1021_REG_DEV_ID) & 0xF0) == 0x30)
+			kind = adm1023;
+		  else
+			kind = adm1021;
+		else if (i == 0x49)
+			kind = thmc10;
+		else if (i == 0x23)
+			kind = gl523sm;
+		else if ((i == 0x4d) &&
+			 (adm1021_read_value
+			  (new_client, ADM1021_REG_DEV_ID) == 0x01))
+			kind = max1617a;
+		else if (i == 0x54)
+			kind = mc1066;
+		/* LM84 Mfr ID in a different place, and it has more unused bits */
+		else if (adm1021_read_value(new_client, ADM1021_REG_CONV_RATE_R) == 0x00
+		      && (kind == 0 /* skip extra detection */
+		       || ((adm1021_read_value(new_client, ADM1021_REG_CONFIG_R) & 0x7F) == 0x00
+			&& (adm1021_read_value(new_client, ADM1021_REG_STATUS) & 0xAB) == 0x00)))
+			kind = lm84;
+		else
+			kind = max1617;
+	}
+
+	if (kind == max1617) {
+		type_name = "max1617";
+		client_name = "MAX1617 chip";
+	} else if (kind == max1617a) {
+		type_name = "max1617a";
+		client_name = "MAX1617A chip";
+	} else if (kind == adm1021) {
+		type_name = "adm1021";
+		client_name = "ADM1021 chip";
+	} else if (kind == adm1023) {
+		type_name = "adm1023";
+		client_name = "ADM1023 chip";
+	} else if (kind == thmc10) {
+		type_name = "thmc10";
+		client_name = "THMC10 chip";
+	} else if (kind == lm84) {
+		type_name = "lm84";
+		client_name = "LM84 chip";
+	} else if (kind == gl523sm) {
+		type_name = "gl523sm";
+		client_name = "GL523SM chip";
+	} else if (kind == mc1066) {
+		type_name = "mc1066";
+		client_name = "MC1066 chip";
+	}
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	data->type = kind;
+
+	new_client->id = adm1021_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto error3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client,	type_name,
+					data->type == adm1021 ? adm1021_dir_table_template :
+					adm1021_max_dir_table_template)) < 0) {
+		err = i;
+		goto error4;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the ADM1021 chip */
+	if (kind != lm84)
+		adm1021_init_client(new_client);
+	return 0;
+
+      error4:
+	i2c_detach_client(new_client);
+      error3:
+      error1:
+	kfree(data);
+      error0:
+	return err;
+}
+
+static void adm1021_init_client(struct i2c_client *client)
+{
+	/* Enable ADC and disable suspend mode */
+	adm1021_write_value(client, ADM1021_REG_CONFIG_W,
+		adm1021_read_value(client, ADM1021_REG_CONFIG_R) & 0xBF);
+	/* Set Conversion rate to 1/sec (this can be tinkered with) */
+	adm1021_write_value(client, ADM1021_REG_CONV_RATE_W, 0x04);
+}
+
+static int adm1021_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct adm1021_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("adm1021.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+
+/* All registers are byte-sized */
+static int adm1021_read_value(struct i2c_client *client, u8 reg)
+{
+	return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* only update value if read succeeded; set fail bit if failed */
+static int adm1021_rd_good(u8 *val, struct i2c_client *client, u8 reg, u8 mask)
+{
+	int i;
+	struct adm1021_data *data = client->data;
+
+	i = i2c_smbus_read_byte_data(client, reg);
+	if (i < 0) {
+		data->fail |= mask;
+		return i;
+	}
+	*val = i;
+	return 0;
+}
+
+static int adm1021_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+	if (read_only > 0)
+		return 0;
+
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static void adm1021_update_client(struct i2c_client *client)
+{
+	struct adm1021_data *data = client->data;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting adm1021 update\n");
+#endif
+
+		data->fail = 0;
+		adm1021_rd_good(&(data->temp), client, ADM1021_REG_TEMP,
+		                ADM1021_ALARM_TEMP);
+		adm1021_rd_good(&(data->temp_os), client, ADM1021_REG_TOS_R,
+		                ADM1021_ALARM_TEMP);
+		adm1021_rd_good(&(data->temp_hyst), client,
+		                ADM1021_REG_THYST_R, ADM1021_ALARM_TEMP);
+		adm1021_rd_good(&(data->remote_temp), client,
+		                ADM1021_REG_REMOTE_TEMP, ADM1021_ALARM_RTEMP);
+		adm1021_rd_good(&(data->remote_temp_os), client,
+		                ADM1021_REG_REMOTE_TOS_R, ADM1021_ALARM_RTEMP);
+		adm1021_rd_good(&(data->remote_temp_hyst), client,
+		                ADM1021_REG_REMOTE_THYST_R,
+		                ADM1021_ALARM_RTEMP);
+		data->alarms = ADM1021_ALARM_ALL;
+		if (!adm1021_rd_good(&(data->alarms), client,
+		                     ADM1021_REG_STATUS, 0))
+			data->alarms &= ADM1021_ALARM_ALL;
+		if (data->type == adm1021)
+			adm1021_rd_good(&(data->die_code), client,
+			                ADM1021_REG_DIE_CODE, 0);
+		if (data->type == adm1023) {
+			adm1021_rd_good(&(data->remote_temp_prec), client,
+			                ADM1021_REG_REM_TEMP_PREC,
+			                ADM1021_ALARM_TEMP);
+			adm1021_rd_good(&(data->remote_temp_os_prec), client,
+			                ADM1021_REG_REM_TOS_PREC,
+			                ADM1021_ALARM_RTEMP);
+			adm1021_rd_good(&(data->remote_temp_hyst_prec), client,
+			                ADM1021_REG_REM_THYST_PREC,
+			                ADM1021_ALARM_RTEMP);
+			adm1021_rd_good(&(data->remote_temp_offset), client,
+			                ADM1021_REG_REM_OFFSET,
+			                ADM1021_ALARM_RTEMP);
+			adm1021_rd_good(&(data->remote_temp_offset_prec),
+			                client, ADM1021_REG_REM_OFFSET_PREC,
+			                ADM1021_ALARM_RTEMP);
+		}
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+void adm1021_temp(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct adm1021_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1021_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_os);
+		results[1] = TEMP_FROM_REG(data->temp_hyst);
+		results[2] = TEMP_FROM_REG(data->temp);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_os = TEMP_TO_REG(results[0]);
+			adm1021_write_value(client, ADM1021_REG_TOS_W,
+					    data->temp_os);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_hyst = TEMP_TO_REG(results[1]);
+			adm1021_write_value(client, ADM1021_REG_THYST_W,
+					    data->temp_hyst);
+		}
+	}
+}
+
+void adm1021_remote_temp(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results)
+{
+	struct adm1021_data *data = client->data;
+	int prec = 0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		if (data->type == adm1023) { *nrels_mag = 3; }
+                 else { *nrels_mag = 0; }
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1021_update_client(client);
+		results[0] = TEMP_FROM_REG(data->remote_temp_os);
+		results[1] = TEMP_FROM_REG(data->remote_temp_hyst);
+		results[2] = TEMP_FROM_REG(data->remote_temp);
+		if (data->type == adm1023) {
+		  results[0]=results[0]*1000 + 
+		   ((data->remote_temp_os_prec >> 5) * 125);
+		  results[1]=results[1]*1000 + 
+		   ((data->remote_temp_hyst_prec >> 5) * 125);
+		  results[2]=(TEMP_FROM_REG(data->remote_temp_offset)*1000) + 
+                   ((data->remote_temp_offset_prec >> 5) * 125);
+		  results[3]=TEMP_FROM_REG(data->remote_temp)*1000 + 
+		   ((data->remote_temp_prec >> 5) * 125);
+ 		  *nrels_mag = 4;
+		} else {
+ 		  *nrels_mag = 3;
+		}
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			if (data->type == adm1023) {
+			  prec=((results[0]-((results[0]/1000)*1000))/125)<<5;
+			  adm1021_write_value(client,
+                                            ADM1021_REG_REM_TOS_PREC,
+                                            prec);
+			  results[0]=results[0]/1000;
+			  data->remote_temp_os_prec=prec;
+			}
+			data->remote_temp_os = TEMP_TO_REG(results[0]);
+			adm1021_write_value(client,
+					    ADM1021_REG_REMOTE_TOS_W,
+					    data->remote_temp_os);
+		}
+		if (*nrels_mag >= 2) {
+			if (data->type == adm1023) {
+			  prec=((results[1]-((results[1]/1000)*1000))/125)<<5;
+			  adm1021_write_value(client,
+                                            ADM1021_REG_REM_THYST_PREC,
+                                            prec);
+			  results[1]=results[1]/1000;
+			  data->remote_temp_hyst_prec=prec;
+			}
+			data->remote_temp_hyst = TEMP_TO_REG(results[1]);
+			adm1021_write_value(client,
+					    ADM1021_REG_REMOTE_THYST_W,
+					    data->remote_temp_hyst);
+		}
+		if (*nrels_mag >= 3) {
+			if (data->type == adm1023) {
+			  prec=((results[2]-((results[2]/1000)*1000))/125)<<5;
+			  adm1021_write_value(client,
+                                            ADM1021_REG_REM_OFFSET_PREC,
+                                            prec);
+			  results[2]=results[2]/1000;
+			  data->remote_temp_offset_prec=prec;
+			  data->remote_temp_offset=results[2];
+			  adm1021_write_value(client,
+                                            ADM1021_REG_REM_OFFSET,
+                                            data->remote_temp_offset);
+			}
+		}
+	}
+}
+
+void adm1021_die_code(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results)
+{
+	struct adm1021_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1021_update_client(client);
+		results[0] = data->die_code;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		/* Can't write to it */
+	}
+}
+
+void adm1021_alarms(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results)
+{
+	struct adm1021_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1021_update_client(client);
+		results[0] = data->alarms | data->fail;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		/* Can't write to it */
+	}
+}
+
+static int __init sm_adm1021_init(void)
+{
+	printk(KERN_INFO "adm1021.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&adm1021_driver);
+}
+
+static void __exit sm_adm1021_exit(void)
+{
+	i2c_del_driver(&adm1021_driver);
+}
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("adm1021 driver");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(read_only, "i");
+MODULE_PARM_DESC(read_only, "Don't set any values, read only mode");
+
+module_init(sm_adm1021_init)
+module_exit(sm_adm1021_exit)
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/adm1024.c linux-2.4.27-leo/drivers/sensors/adm1024.c
--- linux-2.4.27/drivers/sensors/adm1024.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/adm1024.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,782 @@
+/*
+    adm1024.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Add by Ken Bowley <ken@opnix.com> from the adm1025.c written by
+    Gordon Wu <gwu@esoft.com> and from adm9240.c written by
+    Copyright (c) 1999  Frodo Looijaard <frodol@dds.nl>
+    and Philip Edelbrock <phil@netroedge.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or 
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* Supports the Analog Devices ADM1024. See doc/chips/adm1024 for details */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x2c, 0x2e, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(adm1024);
+
+/* Many ADM1024 constants specified below */
+
+#define ADM1024_REG_IN_MAX(nr) (0x2b + (nr) * 2)
+#define ADM1024_REG_IN_MIN(nr) (0x2c + (nr) * 2)
+#define ADM1024_REG_IN(nr) (0x20 + (nr))
+
+/* The ADM1024 registers */
+#define ADM1024_REG_INT_TEMP_TRIP_SET 0x13
+#define ADM1024_REG_EXT_TEMP_TRIP_SET 0x14
+#define ADM1024_REG_TEST 0x15
+#define ADM1024_REG_CHANNEL_MODE 0x16
+#define ADM1024_REG_INT_TEMP_TRIP 0x17	/* read only */
+#define ADM1024_REG_EXT_TEMP_TRIP 0x18	/* read only */
+#define ADM1024_REG_ANALOG_OUT 0x19
+#define ADM1024_REG_AIN1_LOW_LIMIT 0x1A
+#define ADM1024_REG_AIN2_LOW_LIMIT 0x1B
+/* These are all read-only */
+#define ADM1024_REG_2_5V 0x20	/* 2.5V Measured Value/EXT Temp 2 */
+#define ADM1024_REG_VCCP1 0x21
+#define ADM1024_REG_3_3V 0x22	/* VCC Measured Value */
+#define ADM1024_REG_5V 0x23
+#define ADM1024_REG_12V 0x24
+#define ADM1024_REG_VCCP2 0x25
+#define ADM1024_REG_EXT_TEMP1 0x26
+#define ADM1024_REG_TEMP 0x27
+#define ADM1024_REG_FAN1 0x28	/* FAN1/AIN1 Value */
+#define ADM1024_REG_FAN2 0x29	/* FAN2/AIN2 Value */
+#define ADM1024_REG_COMPANY_ID 0x3E	/* 0x41 for ADM1024 */
+#define ADM1024_REG_DIE_REV 0x3F
+/* These are read/write */
+#define ADM1024_REG_2_5V_HIGH 0x2B	/* 2.5V/Ext Temp2 High Limit */
+#define ADM1024_REG_2_5V_LOW 0x2C	/* 2.5V/Ext Temp2 Low Limit */
+#define ADM1024_REG_VCCP1_HIGH 0x2D
+#define ADM1024_REG_VCCP1_LOW 0x2E
+#define ADM1024_REG_3_3V_HIGH 0x2F	/* VCC High Limit */
+#define ADM1024_REG_3_3V_LOW 0x30	/* VCC Low Limit */
+#define ADM1024_REG_5V_HIGH 0x31
+#define ADM1024_REG_5V_LOW 0x32
+#define ADM1024_REG_12V_HIGH 0x33
+#define ADM1024_REG_12V_LOW 0x34
+#define ADM1024_REG_VCCP2_HIGH 0x35
+#define ADM1024_REG_VCCP2_LOW 0x36
+#define ADM1024_REG_EXT_TEMP1_HIGH 0x37
+#define ADM1024_REG_EXT_TEMP1_LOW 0x38
+#define ADM1024_REG_TOS 0x39
+#define ADM1024_REG_THYST 0x3A
+#define ADM1024_REG_FAN1_MIN 0x3B
+#define ADM1024_REG_FAN2_MIN 0x3C
+
+#define ADM1024_REG_CONFIG 0x40
+#define ADM1024_REG_INT1_STAT 0x41
+#define ADM1024_REG_INT2_STAT 0x42
+#define ADM1024_REG_INT1_MASK 0x43
+#define ADM1024_REG_INT2_MASK 0x44
+
+#define ADM1024_REG_CHASSIS_CLEAR 0x46
+#define ADM1024_REG_VID_FAN_DIV 0x47
+#define ADM1024_REG_I2C_ADDR 0x48
+#define ADM1024_REG_VID4 0x49
+#define ADM1024_REG_CONFIG2 0x4A
+#define ADM1024_REG_TEMP_CONFIG 0x4B
+#define ADM1024_REG_EXTMODE1 0x4C	/* Interupt Status Register Mirror No. 1 */
+#define ADM1024_REG_EXTMODE2 0x4D	/* Interupt Status Register Mirror No. 2 */
+
+/* Conversions. Rounding and limit checking is only done on the TO_REG
+   variants. Note that you should be a bit careful with which arguments
+   these macros are called: arguments may be evaluated more than once.
+   Fixing this is just not worth it. */
+#define IN_TO_REG(val,nr) (SENSORS_LIMIT(((val) & 0xff),0,255))
+#define IN_FROM_REG(val,nr) (val)
+
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+	if (rpm == 0)
+		return 255;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1,
+			     254);
+}
+
+#define FAN_FROM_REG(val,div) ((val)==0?-1:\
+                               (val)==255?0:1350000/((div)*(val)))
+
+#define TEMP_FROM_REG(temp) \
+   ((temp)<256?((((temp)&0x1fe) >> 1) * 10)      + ((temp) & 1) * 5:  \
+               ((((temp)&0x1fe) >> 1) -255) * 10 - ((temp) & 1) * 5)  \
+
+#define EXT_TEMP_FROM_REG(temp) (((temp)>0x80?(temp)-0x100:(temp))*10)
+   
+
+#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10)
+
+#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT(((val)<0?(((val)-5)/10):\
+                                                      ((val)+5)/10), \
+                                             0,255)
+
+#define ALARMS_FROM_REG(val) (val)
+
+#define DIV_FROM_REG(val) (1 << (val))
+#define DIV_TO_REG(val) ((val)==1?0:((val)==8?3:((val)==4?2:1)))
+
+#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\
+                           205-(val)*5)
+
+/* For each registered ADM1024, we need to keep some data in memory. That
+   data is pointed to by adm1024_list[NR]->data. The structure itself is
+   dynamically allocated, at the same time when a new adm1024 client is
+   allocated. */
+struct adm1024_data {
+	struct i2c_client client;
+	int sysctl_id;
+	enum chips type;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 in[6];		/* Register value */
+	u8 in_max[6];		/* Register value */
+	u8 in_min[6];		/* Register value */
+	u8 fan[2];		/* Register value */
+	u8 fan_min[2];		/* Register value */
+	u8 fan_div[2];		/* Register encoding, shifted right */
+	int temp;		/* Temp, shifted right */
+	u8 temp_os_max;		/* Register value */
+	u8 temp_os_hyst;	/* Register value */
+	int temp1;		/* Ext Temp 1 */
+	u8 temp1_os_max;
+	u8 temp1_os_hyst;
+	int temp2;		/* Ext Temp 2 */
+	u8 temp2_os_max;
+	u8 temp2_os_hyst;
+	u16 alarms;		/* Register encoding, combined */
+	u8 analog_out;		/* Register value */
+	u8 vid;			/* Register value combined */
+};
+
+
+
+static int adm1024_attach_adapter(struct i2c_adapter *adapter);
+static int adm1024_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int adm1024_detach_client(struct i2c_client *client);
+
+static int adm1024_read_value(struct i2c_client *client, u8 register);
+static int adm1024_write_value(struct i2c_client *client, u8 register,
+			       u8 value);
+static void adm1024_update_client(struct i2c_client *client);
+static void adm1024_init_client(struct i2c_client *client);
+
+
+static void adm1024_in(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void adm1024_fan(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm1024_temp(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void adm1024_temp1(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void adm1024_temp2(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void adm1024_alarms(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+static void adm1024_fan_div(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void adm1024_analog_out(struct i2c_client *client, int operation,
+			       int ctl_name, int *nrels_mag,
+			       long *results);
+static void adm1024_vid(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+
+static int adm1024_id = 0;
+
+static struct i2c_driver adm1024_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "ADM1024 sensor driver",
+	.id		= I2C_DRIVERID_ADM1024,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= adm1024_attach_adapter,
+	.detach_client	= adm1024_detach_client,
+};
+
+/* The /proc/sys entries */
+/* -- SENSORS SYSCTL START -- */
+
+#define ADM1024_SYSCTL_IN0 1000	/* Volts * 100 */
+#define ADM1024_SYSCTL_IN1 1001
+#define ADM1024_SYSCTL_IN2 1002
+#define ADM1024_SYSCTL_IN3 1003
+#define ADM1024_SYSCTL_IN4 1004
+#define ADM1024_SYSCTL_IN5 1005
+#define ADM1024_SYSCTL_FAN1 1101	/* Rotations/min */
+#define ADM1024_SYSCTL_FAN2 1102
+#define ADM1024_SYSCTL_TEMP 1250	/* Degrees Celcius * 100 */
+#define ADM1024_SYSCTL_TEMP1 1290	/* Degrees Celcius */
+#define ADM1024_SYSCTL_TEMP2 1295	/* Degrees Celcius */
+#define ADM1024_SYSCTL_FAN_DIV 2000	/* 1, 2, 4 or 8 */
+#define ADM1024_SYSCTL_ALARMS 2001	/* bitvector */
+#define ADM1024_SYSCTL_ANALOG_OUT 2002
+#define ADM1024_SYSCTL_VID 2003
+
+#define ADM1024_ALARM_IN0 0x0001
+#define ADM1024_ALARM_IN1 0x0002
+#define ADM1024_ALARM_IN2 0x0004
+#define ADM1024_ALARM_IN3 0x0008
+#define ADM1024_ALARM_IN4 0x0100
+#define ADM1024_ALARM_IN5 0x0200
+#define ADM1024_ALARM_FAN1 0x0040
+#define ADM1024_ALARM_FAN2 0x0080
+#define ADM1024_ALARM_TEMP 0x0010
+#define ADM1024_ALARM_TEMP1 0x0020
+#define ADM1024_ALARM_TEMP2 0x0001
+#define ADM1024_ALARM_CHAS 0x1000
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected ADM1024. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized 
+   when a new copy is allocated. */
+static ctl_table adm1024_dir_table_template[] = {
+	{ADM1024_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1024_in},
+	{ADM1024_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1024_in},
+	{ADM1024_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1024_in},
+	{ADM1024_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1024_in},
+	{ADM1024_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1024_in},
+	{ADM1024_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1024_in},
+	{ADM1024_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1024_fan},
+	{ADM1024_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1024_fan},
+	{ADM1024_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1024_temp},
+	{ADM1024_SYSCTL_TEMP1, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1024_temp1},
+	{ADM1024_SYSCTL_TEMP2, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1024_temp2},
+	{ADM1024_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1024_fan_div},
+	{ADM1024_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1024_alarms},
+	{ADM1024_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1024_analog_out},
+	{ADM1024_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1024_vid},
+	{0}
+};
+
+static int adm1024_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, adm1024_detect);
+}
+
+static int adm1024_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct adm1024_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+
+	/* Make sure we aren't probing the ISA bus!! This is just a safety check
+	   at this moment; i2c_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("adm1024.o: adm1024_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		goto ERROR0;
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access adm1024_{read,write}_value. */
+
+	if (!(data = kmalloc(sizeof(struct adm1024_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &adm1024_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+
+	if (kind < 0) {
+		if((adm1024_read_value(new_client, ADM1024_REG_CONFIG) & 0x80) != 0x00)
+			goto ERROR1;
+	}
+
+	/* Determine the chip type. */
+	if (kind <= 0) {
+		i = adm1024_read_value(new_client, ADM1024_REG_COMPANY_ID);
+		if (i == 0x41)
+			kind = adm1024;
+		else {
+			if (kind == 0)
+				printk
+				    ("adm1024.o: Ignoring 'force' parameter for unknown chip at "
+				     "adapter %d, address 0x%02x\n",
+				     i2c_adapter_id(adapter), address);
+			goto ERROR1;
+		}
+	}
+
+	if (kind == adm1024) {
+		type_name = "adm1024";
+		client_name = "ADM1024 chip";
+	} else {
+#ifdef DEBUG
+		printk("adm1024.o: Internal error: unknown kind (%d)?!?",
+		       kind);
+#endif
+		goto ERROR1;
+	}
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	data->type = kind;
+
+	new_client->id = adm1024_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client,
+					type_name,
+					adm1024_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the ADM1024 chip */
+	adm1024_init_client(new_client);
+	return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+      ERROR1:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int adm1024_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct adm1024_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("adm1024.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+static int adm1024_read_value(struct i2c_client *client, u8 reg)
+{
+	return 0xFF & i2c_smbus_read_byte_data(client, reg);
+}
+
+static int adm1024_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static void adm1024_init_client(struct i2c_client *client)
+{
+	/* Enable temperature channel 2 */
+	adm1024_write_value(client, ADM1024_REG_CHANNEL_MODE, adm1024_read_value(client, ADM1024_REG_CHANNEL_MODE) | 0x04);
+
+	/* Start monitoring */
+	adm1024_write_value(client, ADM1024_REG_CONFIG, 0x07);
+}
+
+static void adm1024_update_client(struct i2c_client *client)
+{
+	struct adm1024_data *data = client->data;
+	u8 i;
+
+	down(&data->update_lock);
+
+	if (
+	    (jiffies - data->last_updated >
+	     (data->type == adm1024 ? HZ / 2 : HZ * 2))
+	    || (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting adm1024 update\n");
+#endif
+		for (i = 0; i <= 5; i++) {
+			data->in[i] =
+			    adm1024_read_value(client, ADM1024_REG_IN(i));
+			data->in_min[i] =
+			    adm1024_read_value(client,
+					       ADM1024_REG_IN_MIN(i));
+			data->in_max[i] =
+			    adm1024_read_value(client,
+					       ADM1024_REG_IN_MAX(i));
+		}
+		data->fan[0] =
+		    adm1024_read_value(client, ADM1024_REG_FAN1);
+		data->fan_min[0] =
+		    adm1024_read_value(client, ADM1024_REG_FAN1_MIN);
+		data->fan[1] =
+		    adm1024_read_value(client, ADM1024_REG_FAN2);
+		data->fan_min[1] =
+		    adm1024_read_value(client, ADM1024_REG_FAN2_MIN);
+		data->temp =
+		    (adm1024_read_value(client, ADM1024_REG_TEMP) << 1) +
+		    ((adm1024_read_value
+		      (client, ADM1024_REG_TEMP_CONFIG) & 0x80) >> 7);
+		data->temp_os_max =
+		    adm1024_read_value(client, ADM1024_REG_TOS);
+		data->temp_os_hyst =
+		    adm1024_read_value(client, ADM1024_REG_THYST);
+		data->temp1 =
+		    adm1024_read_value(client, ADM1024_REG_EXT_TEMP1);
+		data->temp1_os_max =
+		    adm1024_read_value(client, ADM1024_REG_EXT_TEMP1_HIGH);
+		data->temp1_os_hyst =
+		    adm1024_read_value(client, ADM1024_REG_EXT_TEMP1_LOW);
+		data->temp2 =
+		    adm1024_read_value(client, ADM1024_REG_2_5V);
+		data->temp2_os_max =
+		    adm1024_read_value(client, ADM1024_REG_2_5V_HIGH);
+		data->temp2_os_hyst =
+		    adm1024_read_value(client, ADM1024_REG_2_5V_LOW);
+
+		i = adm1024_read_value(client, ADM1024_REG_VID_FAN_DIV);
+		data->fan_div[0] = (i >> 4) & 0x03;
+		data->fan_div[1] = (i >> 6) & 0x03;
+		data->vid = i & 0x0f;
+		data->vid |=
+		    (adm1024_read_value(client, ADM1024_REG_VID4) & 0x01)
+		    << 4;
+
+		data->alarms =
+		    adm1024_read_value(client,
+				       ADM1024_REG_INT1_STAT) +
+		    (adm1024_read_value(client, ADM1024_REG_INT2_STAT) <<
+		     8);
+		data->analog_out =
+		    adm1024_read_value(client, ADM1024_REG_ANALOG_OUT);
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+/* The next few functions are the call-back functions of the /proc/sys and
+   sysctl files. Which function is used is defined in the ctl_table in
+   the extra1 field.
+   Each function must return the magnitude (power of 10 to divide the date
+   with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must
+   put a maximum of *nrels elements in results reflecting the data of this
+   file, and set *nrels to the number it actually put in it, if operation==
+   SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from
+   results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE.
+   Note that on SENSORS_PROC_REAL_READ, I do not check whether results is
+   large enough (by checking the incoming value of *nrels). This is not very
+   good practice, but as long as you put less than about 5 values in results,
+   you can assume it is large enough. */
+void adm1024_in(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+
+	int scales[6] = { 250, 225, 330, 500, 1200, 270 };
+
+	struct adm1024_data *data = client->data;
+	int nr = ctl_name - ADM1024_SYSCTL_IN0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1024_update_client(client);
+		results[0] =
+		    IN_FROM_REG(data->in_min[nr], nr) * scales[nr] / 192;
+		results[1] =
+		    IN_FROM_REG(data->in_max[nr], nr) * scales[nr] / 192;
+		results[2] =
+		    IN_FROM_REG(data->in[nr], nr) * scales[nr] / 192;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->in_min[nr] =
+			    IN_TO_REG((results[0] * 192) / scales[nr], nr);
+			adm1024_write_value(client, ADM1024_REG_IN_MIN(nr),
+					    data->in_min[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->in_max[nr] =
+			    IN_TO_REG((results[1] * 192) / scales[nr], nr);
+			adm1024_write_value(client, ADM1024_REG_IN_MAX(nr),
+					    data->in_max[nr]);
+		}
+	}
+}
+
+void adm1024_fan(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct adm1024_data *data = client->data;
+	int nr = ctl_name - ADM1024_SYSCTL_FAN1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1024_update_client(client);
+		results[0] = FAN_FROM_REG(data->fan_min[nr - 1],
+					  DIV_FROM_REG(data->
+						       fan_div[nr - 1]));
+		results[1] =
+		    FAN_FROM_REG(data->fan[nr - 1],
+				 DIV_FROM_REG(data->fan_div[nr - 1]));
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr - 1] = FAN_TO_REG(results[0],
+							   DIV_FROM_REG
+							   (data->
+							    fan_div[nr -
+								    1]));
+			adm1024_write_value(client,
+					    nr ==
+					    1 ? ADM1024_REG_FAN1_MIN :
+					    ADM1024_REG_FAN2_MIN,
+					    data->fan_min[nr - 1]);
+		}
+	}
+}
+
+
+void adm1024_temp(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct adm1024_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1024_update_client(client);
+		results[0] = TEMP_LIMIT_FROM_REG(data->temp_os_max);
+		results[1] = TEMP_LIMIT_FROM_REG(data->temp_os_hyst);
+		results[2] = TEMP_FROM_REG(data->temp);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_os_max = TEMP_LIMIT_TO_REG(results[0]);
+			adm1024_write_value(client, ADM1024_REG_TOS,
+					    data->temp_os_max);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_os_hyst = TEMP_LIMIT_TO_REG(results[1]);
+			adm1024_write_value(client, ADM1024_REG_THYST,
+					    data->temp_os_hyst);
+		}
+	}
+}
+
+void adm1024_temp1(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct adm1024_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1024_update_client(client);
+		results[0] = TEMP_LIMIT_FROM_REG(data->temp1_os_max);
+		results[1] = TEMP_LIMIT_FROM_REG(data->temp1_os_hyst);
+		results[2] = EXT_TEMP_FROM_REG(data->temp1);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp1_os_max = TEMP_LIMIT_TO_REG(results[0]);
+			adm1024_write_value(client, ADM1024_REG_EXT_TEMP1_HIGH,
+					    data->temp1_os_max);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp1_os_hyst = TEMP_LIMIT_TO_REG(results[1]);
+			adm1024_write_value(client, ADM1024_REG_EXT_TEMP1_LOW,
+					    data->temp1_os_hyst);
+		}
+	}
+}
+
+void adm1024_temp2(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct adm1024_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1024_update_client(client);
+		results[0] = TEMP_LIMIT_FROM_REG(data->temp2_os_max);
+		results[1] = TEMP_LIMIT_FROM_REG(data->temp2_os_hyst);
+		results[2] = EXT_TEMP_FROM_REG(data->temp2);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp2_os_max = TEMP_LIMIT_TO_REG(results[0]);
+			adm1024_write_value(client, ADM1024_REG_2_5V_HIGH,
+					    data->temp2_os_max);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp2_os_hyst = TEMP_LIMIT_TO_REG(results[1]);
+			adm1024_write_value(client, ADM1024_REG_2_5V_LOW,
+					    data->temp2_os_hyst);
+		}
+	}
+}
+
+void adm1024_alarms(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results)
+{
+	struct adm1024_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1024_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->alarms);
+		*nrels_mag = 1;
+	}
+}
+
+void adm1024_fan_div(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	struct adm1024_data *data = client->data;
+	int old;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1024_update_client(client);
+		results[0] = DIV_FROM_REG(data->fan_div[0]);
+		results[1] = DIV_FROM_REG(data->fan_div[1]);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		old = adm1024_read_value(client, ADM1024_REG_VID_FAN_DIV);
+		if (*nrels_mag >= 2) {
+			data->fan_div[1] = DIV_TO_REG(results[1]);
+			old = (old & 0x3f) | (data->fan_div[1] << 6);
+		}
+		if (*nrels_mag >= 1) {
+			data->fan_div[0] = DIV_TO_REG(results[0]);
+			old = (old & 0xcf) | (data->fan_div[0] << 4);
+			adm1024_write_value(client,
+					    ADM1024_REG_VID_FAN_DIV, old);
+		}
+	}
+}
+
+void adm1024_analog_out(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results)
+{
+	struct adm1024_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1024_update_client(client);
+		results[0] = data->analog_out;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->analog_out = results[0];
+			adm1024_write_value(client, ADM1024_REG_ANALOG_OUT,
+					    data->analog_out);
+		}
+	}
+}
+
+void adm1024_vid(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct adm1024_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1024_update_client(client);
+		results[0] = VID_FROM_REG(data->vid);
+		*nrels_mag = 1;
+	}
+}
+
+static int __init sm_adm1024_init(void)
+{
+	printk("adm1024.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&adm1024_driver);
+}
+
+static void __exit sm_adm1024_exit(void)
+{
+	i2c_del_driver(&adm1024_driver);
+}
+
+
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("ADM1024 driver");
+
+MODULE_LICENSE("GPL");
+
+module_init(sm_adm1024_init);
+module_exit(sm_adm1024_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/adm1025.c linux-2.4.27-leo/drivers/sensors/adm1025.c
--- linux-2.4.27/drivers/sensors/adm1025.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/adm1025.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,566 @@
+/*
+    adm1025.c - Part of lm_sensors, Linux kernel modules for hardware
+               monitoring
+    Copyright (c) 2000 Chen-Yuan Wu <gwu@esoft.com>
+    Copyright (c) 2003-2004 Jean Delvare <khali@linux-fr.org>
+
+    Based on the adm9240 driver.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or 
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* Supports the Analog Devices ADM1025 and the Philips NE1619.
+   See doc/chips/adm1025 for details */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+#include <linux/sensors_vid.h>
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x2c, 0x2e, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_2(adm1025, ne1619);
+
+/* Many ADM1025 constants specified below */
+
+
+/* The ADM1025 registers */
+
+/* These are all read-only */
+#define ADM1025_REG_2_5V        0x20 /* not used directly, see   */
+#define ADM1025_REG_VCCP1       0x21 /* ADM1025_REG_IN(nr) below */
+#define ADM1025_REG_3_3V        0x22
+#define ADM1025_REG_5V          0x23
+#define ADM1025_REG_12V         0x24
+#define ADM1025_REG_VCC         0x25
+
+#define ADM1025_REG_RTEMP       0x26 /* not used directly, see     */
+#define ADM1025_REG_LTEMP       0x27 /* ADM1025_REG_TEMP(nr) below */
+
+#define ADM1025_REG_COMPANY_ID  0x3E /* 0x41 for Analog Devices,
+                                        0xA1 for Philips */
+#define ADM1025_REG_DIE_REV     0x3F /* 0x20-0x2F for ADM1025 and compatible */
+
+#define ADM1025_REG_STATUS1     0x41
+#define ADM1025_REG_STATUS2     0x42
+
+#define ADM1025_REG_VID         0x47
+#define ADM1025_REG_VID4        0x49 /* actually R/W
+                                        but we don't write to it */
+
+/* These are read/write */
+#define ADM1025_REG_2_5V_HIGH   0x2B /* not used directly, see       */
+#define ADM1025_REG_2_5V_LOW    0x2C /* ADM1025_REG_IN_MAX(nr) and   */
+#define ADM1025_REG_VCCP1_HIGH  0x2D /* ADM1025_REG_IN_MIN(nr) below */
+#define ADM1025_REG_VCCP1_LOW   0x2E
+#define ADM1025_REG_3_3V_HIGH   0x2F
+#define ADM1025_REG_3_3V_LOW    0x30
+#define ADM1025_REG_5V_HIGH     0x31
+#define ADM1025_REG_5V_LOW      0x32
+#define ADM1025_REG_12V_HIGH    0x33
+#define ADM1025_REG_12V_LOW     0x34
+#define ADM1025_REG_VCC_HIGH    0x35
+#define ADM1025_REG_VCC_LOW     0x36
+
+#define ADM1025_REG_RTEMP_HIGH  0x37 /* not used directly, see         */
+#define ADM1025_REG_RTEMP_LOW   0x38 /* ADM1025_REG_TEMP_MAX(nr) and   */
+#define ADM1025_REG_LTEMP_HIGH  0x39 /* ADM1025_REG_TEMP_MIN(nr) below */
+#define ADM1025_REG_LTEMP_LOW   0x3A
+
+#define ADM1025_REG_CONFIG      0x40
+
+/* Useful macros */
+#define ADM1025_REG_IN(nr)        (ADM1025_REG_2_5V + (nr))
+#define ADM1025_REG_IN_MAX(nr)    (ADM1025_REG_2_5V_HIGH + (nr) * 2)
+#define ADM1025_REG_IN_MIN(nr)    (ADM1025_REG_2_5V_LOW + (nr) * 2)
+#define ADM1025_REG_TEMP(nr)      (ADM1025_REG_RTEMP + (nr))
+#define ADM1025_REG_TEMP_HIGH(nr) (ADM1025_REG_RTEMP_HIGH + (nr) * 2)
+#define ADM1025_REG_TEMP_LOW(nr)  (ADM1025_REG_RTEMP_LOW + (nr) * 2)
+
+/* Conversions. Rounding and limit checking is only done on the TO_REG
+   variants. Note that you should be a bit careful with which arguments
+   these macros are called: arguments may be evaluated more than once.
+   Fixing this is just not worth it. */
+#define IN_TO_REG(val) (SENSORS_LIMIT(((val) & 0xff),0,255))
+#define IN_FROM_REG(val) (val)
+
+#define TEMP_FROM_REG(val) (((val)>=0x80?(val)-0x100:(val))*10)
+#define TEMP_TO_REG(val) SENSORS_LIMIT(((val)<0?(((val)-5)/10):\
+                                                 ((val)+5)/10),0,255)
+
+#define ALARMS_FROM_REG(val) (val)
+
+/* For each registered ADM1025, we need to keep some data in memory. That
+   data is pointed to by adm1025_list[NR]->data. The structure itself is
+   dynamically allocated, at the same time when a new adm1025 client is
+   allocated. */
+struct adm1025_data {
+	struct i2c_client client;
+	int sysctl_id;
+	enum chips type;
+
+	struct semaphore update_lock;
+	char valid;	              /* !=0 if following fields are valid */
+	unsigned long last_updated;   /* In jiffies */
+
+	u8 in[6];               /* Register value */
+	u8 in_max[6];           /* Register value */
+	u8 in_min[6];           /* Register value */
+	u8 temp[2];             /* Register value */
+	u8 temp_high[2];        /* Register value */
+	u8 temp_low[2];         /* Register value */
+	u16 alarms;             /* Register encoding, combined */
+	u8 analog_out;          /* Register value */
+	u8 vid;                 /* Register value combined */
+	u8 vrm;
+};
+
+
+static int adm1025_attach_adapter(struct i2c_adapter *adapter);
+static int adm1025_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int adm1025_detach_client(struct i2c_client *client);
+static void adm1025_update_client(struct i2c_client *client);
+static void adm1025_init_client(struct i2c_client *client);
+
+
+static void adm1025_in(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void adm1025_temp(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void adm1025_alarms(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+static void adm1025_vid(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm1025_vrm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+
+static int adm1025_id = 0;
+
+static struct i2c_driver adm1025_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "ADM1025 sensor driver",
+	.id		= I2C_DRIVERID_ADM1025,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= adm1025_attach_adapter,
+	.detach_client	= adm1025_detach_client,
+};
+
+/* The /proc/sys entries */
+/* -- SENSORS SYSCTL START -- */
+
+#define ADM1025_SYSCTL_IN0     1000 /* Volts * 100 */
+#define ADM1025_SYSCTL_IN1     1001
+#define ADM1025_SYSCTL_IN2     1002
+#define ADM1025_SYSCTL_IN3     1003
+#define ADM1025_SYSCTL_IN4     1004
+#define ADM1025_SYSCTL_IN5     1005
+
+#define ADM1025_SYSCTL_RTEMP   1250 /* Degrees Celcius * 10 */
+#define ADM1025_SYSCTL_TEMP    1251
+
+#define ADM1025_SYSCTL_ALARMS  2001 /* bitvector */
+#define ADM1025_SYSCTL_VID     2003 /* Volts * 1000 */
+#define ADM1025_SYSCTL_VRM     2004
+
+#define ADM1025_ALARM_IN0     0x0001
+#define ADM1025_ALARM_IN1     0x0002
+#define ADM1025_ALARM_IN2     0x0004
+#define ADM1025_ALARM_IN3     0x0008
+#define ADM1025_ALARM_IN4     0x0100
+#define ADM1025_ALARM_IN5     0x0200
+#define ADM1025_ALARM_RTEMP   0x0020
+#define ADM1025_ALARM_TEMP    0x0010
+#define ADM1025_ALARM_RFAULT  0x4000
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected ADM1025. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized 
+   when a new copy is allocated. */
+static ctl_table adm1025_dir_table_template[] = {
+	{ADM1025_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1025_in},
+	{ADM1025_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1025_in},
+	{ADM1025_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1025_in},
+	{ADM1025_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1025_in},
+	{ADM1025_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1025_in},
+	{ADM1025_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1025_in},
+	{ADM1025_SYSCTL_RTEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1025_temp},
+	{ADM1025_SYSCTL_TEMP, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1025_temp},
+	{ADM1025_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1025_alarms},
+	{ADM1025_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1025_vid},
+	{ADM1025_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm1025_vrm},
+	{0}
+};
+
+static int adm1025_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, adm1025_detect);
+}
+
+static int adm1025_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct adm1025_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+
+	/* Make sure we aren't probing the ISA bus!! This is just a safety check
+	   at this moment; i2c_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("adm1025.o: adm1025_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		goto ERROR0;
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access adm1025_{read,write}_value. */
+
+	if (!(data = kmalloc(sizeof(struct adm1025_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &adm1025_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+
+	if (kind < 0) {
+		if((i2c_smbus_read_byte_data(new_client,ADM1025_REG_CONFIG) & 0x80) != 0x00)
+			goto ERROR1;
+	}
+
+	/* Determine the chip type. */
+	if (kind <= 0) {
+		u8 man_id, chip_id;
+		
+		man_id = i2c_smbus_read_byte_data(new_client,
+			 ADM1025_REG_COMPANY_ID);
+		chip_id = i2c_smbus_read_byte_data(new_client,
+			  ADM1025_REG_DIE_REV);
+		
+		if (man_id == 0x41) { /* Analog Devices */
+			if ((chip_id & 0xF0) == 0x20) /* ADM1025 */
+				kind = adm1025;
+		} else if (man_id == 0xA1) { /* Philips */
+			if (address != 0x2E
+			 && (chip_id & 0xF0) == 0x20) /* NE1619 */
+				kind = ne1619;
+		}
+	}
+
+	if (kind <= 0) { /* Identification failed */
+		printk("adm1025.o: Unsupported chip.\n");
+		goto ERROR1;
+	}
+
+	if (kind == adm1025) {
+		type_name = "adm1025";
+		client_name = "ADM1025 chip";
+	} else if (kind == ne1619) {
+		type_name = "ne1619";
+		client_name = "NE1619 chip";		
+	} else {
+#ifdef DEBUG
+		printk("adm1025.o: Internal error: unknown kind (%d)?!?",
+		       kind);
+#endif
+		goto ERROR1;
+	}
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	data->type = kind;
+
+	new_client->id = adm1025_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client,
+					type_name,
+					adm1025_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the ADM1025 chip */
+	adm1025_init_client(new_client);
+	return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+      ERROR1:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int adm1025_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct adm1025_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("adm1025.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+/* Called when we have found a new ADM1025. */
+static void adm1025_init_client(struct i2c_client *client)
+{
+	struct adm1025_data *data = client->data;
+	u8 reg;
+
+	data->vrm = DEFAULT_VRM;
+
+	/* Start monitoring */
+	reg = i2c_smbus_read_byte_data(client, ADM1025_REG_CONFIG);
+	i2c_smbus_write_byte_data(client, ADM1025_REG_CONFIG, (reg|0x01)&0x7F);
+}
+
+static void adm1025_update_client(struct i2c_client *client)
+{
+	struct adm1025_data *data = client->data;
+	u8 nr;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > 2 * HZ)
+	 || (jiffies < data->last_updated) || !data->valid) {
+#ifdef DEBUG
+		printk("Starting adm1025 update\n");
+#endif
+
+		/* Voltages */
+		for (nr = 0; nr < 6; nr++) {
+			data->in[nr] = i2c_smbus_read_byte_data(client, ADM1025_REG_IN(nr));
+/*			data->in_min[nr] = i2c_smbus_read_byte_data(client, ADM1025_REG_IN_MIN(nr));
+			data->in_max[nr] = i2c_smbus_read_byte_data(client, ADM1025_REG_IN_MAX(nr));*/
+		}
+
+		/* Temperatures */
+		for (nr = 0; nr < 2; nr++) {
+			data->temp[nr] = i2c_smbus_read_byte_data(client, ADM1025_REG_TEMP(nr));
+/*			data->temp_high[nr] = i2c_smbus_read_byte_data(client, ADM1025_REG_TEMP_HIGH(nr));
+			data->temp_low[nr] = i2c_smbus_read_byte_data(client, ADM1025_REG_TEMP_LOW(nr));*/
+		}
+
+		/* VID */
+		data->vid = (i2c_smbus_read_byte_data(client, ADM1025_REG_VID) & 0x0f)
+		         + ((i2c_smbus_read_byte_data(client, ADM1025_REG_VID4) & 0x01) << 4);
+
+		/* Alarms */
+		data->alarms = (i2c_smbus_read_byte_data(client, ADM1025_REG_STATUS1) & 0x3f)
+		            + ((i2c_smbus_read_byte_data(client, ADM1025_REG_STATUS2) & 0x43) << 8);
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+/* The next few functions are the call-back functions of the /proc/sys and
+   sysctl files. Which function is used is defined in the ctl_table in
+   the extra1 field.
+   Each function must return the magnitude (power of 10 to divide the data
+   with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must
+   put a maximum of *nrels elements in results reflecting the data of this
+   file, and set *nrels to the number it actually put in it, if operation==
+   SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from
+   results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE.
+   Note that on SENSORS_PROC_REAL_READ, I do not check whether results is
+   large enough (by checking the incoming value of *nrels). This is not very
+   good practice, but as long as you put less than about 5 values in results,
+   you can assume it is large enough. */
+void adm1025_in(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	int scales[6] = { 250, 225, 330, 500, 1200, 330 };
+
+	struct adm1025_data *data = client->data;
+	int nr = ctl_name - ADM1025_SYSCTL_IN0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1025_update_client(client);
+		results[0] = (IN_FROM_REG(data->in_min[nr]) * scales[nr] + 96) / 192;
+		results[1] = (IN_FROM_REG(data->in_max[nr]) * scales[nr] + 96) / 192;
+		results[2] = (IN_FROM_REG(data->in[nr]) * scales[nr] + 96) / 192;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->in_min[nr] = IN_TO_REG((results[0] * 192 + scales[nr] / 2)
+					   / scales[nr]);
+			i2c_smbus_write_byte_data(client, ADM1025_REG_IN_MIN(nr),
+					    data->in_min[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->in_max[nr] = IN_TO_REG((results[1] * 192 + scales[nr] / 2)
+					   / scales[nr]);
+			i2c_smbus_write_byte_data(client, ADM1025_REG_IN_MAX(nr),
+					    data->in_max[nr]);
+		}
+	}
+}
+
+void adm1025_temp(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct adm1025_data *data = client->data;
+	int nr = ctl_name - ADM1025_SYSCTL_RTEMP;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1025_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_high[nr]);
+		results[1] = TEMP_FROM_REG(data->temp_low[nr]);
+		results[2] = TEMP_FROM_REG(data->temp[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_high[nr] = TEMP_TO_REG(results[0]);
+			i2c_smbus_write_byte_data(client, ADM1025_REG_TEMP_HIGH(nr),
+					    data->temp_high[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_low[nr] = TEMP_TO_REG(results[1]);
+			i2c_smbus_write_byte_data(client, ADM1025_REG_TEMP_LOW(nr),
+					    data->temp_low[nr]);
+		}
+	}
+}
+
+void adm1025_alarms(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results)
+{
+	struct adm1025_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1025_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->alarms);
+		*nrels_mag = 1;
+	}
+}
+
+void adm1025_vid(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct adm1025_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 3;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1025_update_client(client);
+		results[0] = vid_from_reg(data->vid, data->vrm);
+		*nrels_mag = 1;
+	}
+}
+
+void adm1025_vrm(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct adm1025_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = data->vrm;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1)
+			data->vrm = results[0];
+	}
+}
+
+static int __init sm_adm1025_init(void)
+{
+	printk("adm1025.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&adm1025_driver);
+}
+
+static void __exit sm_adm1025_exit(void)
+{
+	i2c_del_driver(&adm1025_driver);
+}
+
+
+
+MODULE_AUTHOR("Chen-Yuan Wu <gwu@esoft.com>"
+	" and Jean Delvare <khali@linux-fr.org>");
+MODULE_DESCRIPTION("ADM1025 driver");
+
+module_init(sm_adm1025_init);
+module_exit(sm_adm1025_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/adm1026.c linux-2.4.27-leo/drivers/sensors/adm1026.c
--- linux-2.4.27/drivers/sensors/adm1026.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/adm1026.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,1745 @@
+/*
+    adm1026.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 2002, 2003  Philip Pokorny <ppokorny@penguincomputing.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+    CHANGELOG
+
+    2003-03-13   Initial development
+    2003-05-07   First Release.  Includes GPIO fixup and full
+                 functionality.
+    2003-05-18   Minor fixups and tweaks.
+                 Print GPIO config after fixup.
+                 Adjust fan MIN if DIV changes.
+    2003-05-21   Fix printing of FAN/GPIO config
+                 Fix silly bug in fan_div logic
+                 Fix fan_min handling so that 0xff is 0 is 0xff
+    2003-05-25   Fix more silly typos...
+    2003-06-11   Change FAN_xx_REG macros to use different scaling
+                 Most (all?) drivers assume two pulses per rev fans
+                 and the old scaling was producing double the RPM's
+                 Thanks to Jerome Hsiao @ Arima for pointing this out.
+	2004-01-27   Remove use of temporary ID.
+                 Define addresses as a range.
+*/
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+#include <linux/sensors_vid.h>
+
+#ifndef I2C_DRIVERID_ADM1026
+#define I2C_DRIVERID_ADM1026	1048
+#endif
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x2c, 0x2e, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(adm1026);
+
+static int gpio_input[17]  = { -1, -1, -1, -1, -1, -1, -1, -1, -1,
+				-1, -1, -1, -1, -1, -1, -1, -1 };
+static int gpio_output[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1,
+				-1, -1, -1, -1, -1, -1, -1, -1 };
+static int gpio_inverted[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1,
+				-1, -1, -1, -1, -1, -1, -1, -1 };
+static int gpio_normal[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1,
+				-1, -1, -1, -1, -1, -1, -1, -1 };
+static int gpio_fan[8] = { -1, -1, -1, -1, -1, -1, -1, -1 };
+MODULE_PARM(gpio_input,"1-17i");
+MODULE_PARM_DESC(gpio_input,"List of GPIO pins (0-16) to program as inputs");
+MODULE_PARM(gpio_output,"1-17i");
+MODULE_PARM_DESC(gpio_output,"List of GPIO pins (0-16) to program as outputs");
+MODULE_PARM(gpio_inverted,"1-17i");
+MODULE_PARM_DESC(gpio_inverted,"List of GPIO pins (0-16) to program as inverted");
+MODULE_PARM(gpio_normal,"1-17i");
+MODULE_PARM_DESC(gpio_normal,"List of GPIO pins (0-16) to program as normal/non-inverted");
+MODULE_PARM(gpio_fan,"1-8i");
+MODULE_PARM_DESC(gpio_fan,"List of GPIO pins (0-7) to program as fan tachs");
+
+/* Many ADM1026 constants specified below */
+
+/* The ADM1026 registers */
+#define ADM1026_REG_CONFIG1  (0x00)
+#define CFG1_MONITOR     (0x01)
+#define CFG1_INT_ENABLE  (0x02)
+#define CFG1_INT_CLEAR   (0x04)
+#define CFG1_AIN8_9      (0x08)
+#define CFG1_THERM_HOT   (0x10)
+#define CFG1_DAC_AFC     (0x20)
+#define CFG1_PWM_AFC     (0x40)
+#define CFG1_RESET       (0x80)
+#define ADM1026_REG_CONFIG2  (0x01)
+/* CONFIG2 controls FAN0/GPIO0 through FAN7/GPIO7 */
+#define ADM1026_REG_CONFIG3  (0x07)
+#define CFG3_GPIO16_ENABLE  (0x01)
+#define CFG3_CI_CLEAR  (0x02)
+#define CFG3_VREF_250  (0x04)
+#define CFG3_GPIO16_DIR  (0x40)
+#define CFG3_GPIO16_POL  (0x80)
+#define ADM1026_REG_E2CONFIG  (0x13)
+#define E2CFG_READ  (0x01)
+#define E2CFG_WRITE  (0x02)
+#define E2CFG_ERASE  (0x04)
+#define E2CFG_ROM  (0x08)
+#define E2CFG_CLK_EXT  (0x80)
+
+/* There are 10 general analog inputs and 7 dedicated inputs
+ * They are:
+ *    0 - 9  =  AIN0 - AIN9
+ *       10  =  Vbat
+ *       11  =  3.3V Standby
+ *       12  =  3.3V Main
+ *       13  =  +5V
+ *       14  =  Vccp (CPU core voltage)
+ *       15  =  +12V
+ *       16  =  -12V
+ */
+static u16 REG_IN[] = {
+		0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
+		0x36, 0x37, 0x27, 0x29, 0x26, 0x2a,
+		0x2b, 0x2c, 0x2d, 0x2e, 0x2f
+	};
+static u16 REG_IN_MIN[] = {
+		0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d,
+		0x5e, 0x5f, 0x6d, 0x49, 0x6b, 0x4a,
+		0x4b, 0x4c, 0x4d, 0x4e, 0x4f
+	};
+static u16 REG_IN_MAX[] = {
+		0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
+		0x56, 0x57, 0x6c, 0x41, 0x6a, 0x42,
+		0x43, 0x44, 0x45, 0x46, 0x47
+	};
+#define ADM1026_REG_IN(nr) (REG_IN[(nr)])
+#define ADM1026_REG_IN_MIN(nr) (REG_IN_MIN[(nr)])
+#define ADM1026_REG_IN_MAX(nr) (REG_IN_MAX[(nr)])
+
+/* Temperatures are:
+ *    0 - Internal
+ *    1 - External 1
+ *    2 - External 2
+ */
+static u16 REG_TEMP[] = { 0x1f, 0x28, 0x29 };
+static u16 REG_TEMP_MIN[] = { 0x69, 0x48, 0x49 };
+static u16 REG_TEMP_MAX[] = { 0x68, 0x40, 0x41 };
+static u16 REG_TEMP_TMIN[] = { 0x10, 0x11, 0x12 };
+static u16 REG_TEMP_THERM[] = { 0x0d, 0x0e, 0x0f };
+static u16 REG_TEMP_OFFSET[] = { 0x1e, 0x6e, 0x6f };
+#define ADM1026_REG_TEMP(nr) (REG_TEMP[(nr)])
+#define ADM1026_REG_TEMP_MIN(nr) (REG_TEMP_MIN[(nr)])
+#define ADM1026_REG_TEMP_MAX(nr) (REG_TEMP_MAX[(nr)])
+#define ADM1026_REG_TEMP_TMIN(nr) (REG_TEMP_TMIN[(nr)])
+#define ADM1026_REG_TEMP_THERM(nr) (REG_TEMP_THERM[(nr)])
+#define ADM1026_REG_TEMP_OFFSET(nr) (REG_TEMP_OFFSET[(nr)])
+
+#define ADM1026_REG_FAN(nr) (0x38 + (nr))
+#define ADM1026_REG_FAN_MIN(nr) (0x60 + (nr))
+#define ADM1026_REG_FAN_DIV_0_3 (0x02)
+#define ADM1026_REG_FAN_DIV_4_7 (0x03)
+
+#define ADM1026_REG_DAC  (0x04)
+#define ADM1026_REG_PWM  (0x05)
+
+#define ADM1026_REG_GPIO_CFG_0_3 (0x08)
+#define ADM1026_REG_GPIO_CFG_4_7 (0x09)
+#define ADM1026_REG_GPIO_CFG_8_11 (0x0a)
+#define ADM1026_REG_GPIO_CFG_12_15 (0x0b)
+/* CFG_16 in REG_CFG3 */
+#define ADM1026_REG_GPIO_STATUS_0_7 (0x24)
+#define ADM1026_REG_GPIO_STATUS_8_15 (0x25)
+/* STATUS_16 in REG_STATUS4 */
+#define ADM1026_REG_GPIO_MASK_0_7 (0x1c)
+#define ADM1026_REG_GPIO_MASK_8_15 (0x1d)
+/* MASK_16 in REG_MASK4 */
+
+#define ADM1026_REG_COMPANY 0x16
+#define ADM1026_REG_VERSTEP 0x17
+/* These are the recognized values for the above regs */
+#define ADM1026_COMPANY_ANALOG_DEV 0x41
+#define ADM1026_VERSTEP_GENERIC 0x40
+#define ADM1026_VERSTEP_ADM1026 0x44
+
+#define ADM1026_REG_MASK1 0x18
+#define ADM1026_REG_MASK2 0x19
+#define ADM1026_REG_MASK3 0x1a
+#define ADM1026_REG_MASK4 0x1b
+
+#define ADM1026_REG_STATUS1 0x20
+#define ADM1026_REG_STATUS2 0x21
+#define ADM1026_REG_STATUS3 0x22
+#define ADM1026_REG_STATUS4 0x23
+
+/* Conversions. Rounding and limit checking is only done on the TO_REG 
+   variants. Note that you should be a bit careful with which arguments
+   these macros are called: arguments may be evaluated more than once.
+ */
+
+/* IN are scaled acording to built-in resistors.  These are the
+ *   voltages corresponding to 3/4 of full scale (192 or 0xc0)
+ *   NOTE: The -12V input needs an additional factor to account
+ *      for the Vref pullup resistor.
+ *      NEG12_OFFSET = SCALE * Vref / V-192 - Vref
+ *                   = 13875 * 2.50 / 1.875 - 2500
+ *                   = 16000
+ */
+#if 1
+/* The values in this table are based on Table II, page 15 of the
+ *    datasheet.
+ */
+static int adm1026_scaling[] = {  /* .001 Volts */
+		2250, 2250, 2250, 2250, 2250, 2250, 
+		1875, 1875, 1875, 1875, 3000, 3330, 
+		3330, 4995, 2250, 12000, 13875
+	};
+#define NEG12_OFFSET  16000
+#else
+/* The values in this table are based on the resistors in 
+ *    Figure 5 on page 16.  But the 3.3V inputs are not in
+ *    the figure and the values for the 5V input are wrong.
+ *    For 5V, I'm guessing that R2 at 55.2k is right, but
+ *    the total resistance should be 1400 or 1449 like the
+ *    other inputs.  Using 1449, gives 4.922V at 192.
+ */
+static int adm1026_scaling[] = {  /* .001 Volts */
+		2249, 2249, 2249, 2249, 2249, 2249, 
+		1875, 1875, 1875, 1875, 3329, 3329, 
+		3329, 4922, 2249, 11969, 13889
+	};
+#define NEG12_OFFSET  16019
+#endif
+
+#define SCALE(val,from,to) (((val)*(to) + ((from)/2))/(from))
+#define INS_TO_REG(n,val)  (SENSORS_LIMIT(SCALE(val,adm1026_scaling[n],192),0,255))
+#if 0   /* If we have extended A/D bits */
+#define INSEXT_FROM_REG(n,val,ext) (SCALE((val)*4 + (ext),192*4,adm1026_scaling[n]))
+#define INS_FROM_REG(n,val) (INSEXT_FROM_REG(n,val,0))
+#else
+#define INS_FROM_REG(n,val) (SCALE(val,192,adm1026_scaling[n]))
+#endif
+
+/* FAN speed is measured using 22.5kHz clock and counts for 2 pulses
+ *   and we assume a 2 pulse-per-rev fan tach signal
+ *      22500 kHz * 60 (sec/min) * 2 (pulse) / 2 (pulse/rev) == 1350000
+ */
+#define FAN_TO_REG(val,div)  ((val)<=0 ? 0xff : SENSORS_LIMIT(1350000/((val)*(div)),1,254))
+#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==0xff ? 0 : 1350000/((val)*(div)))
+#define DIV_FROM_REG(val) (1<<(val))
+#define DIV_TO_REG(val) ((val)>=8 ? 3 : (val)>=4 ? 2 : (val)>=2 ? 1 : 0)
+
+/* Temperature is reported in 1 degC increments */
+#define TEMP_TO_REG(val) (SENSORS_LIMIT(val,-127,127))
+#define TEMP_FROM_REG(val) (val)
+#define OFFSET_TO_REG(val) (SENSORS_LIMIT(val,-127,127))
+#define OFFSET_FROM_REG(val) (val)
+
+#define PWM_TO_REG(val) (SENSORS_LIMIT(val,0,255))
+#define PWM_FROM_REG(val) (val)
+
+/* Analog output is a voltage, but it's used like a PWM
+ *   Seems like this should be scaled, but to be consistent
+ *   with other drivers, we do it this way.
+ */
+#define DAC_TO_REG(val) (SENSORS_LIMIT(val,0,255))
+#define DAC_FROM_REG(val) (val)
+
+/* sensors_vid.h defines vid_from_reg() */
+#define VID_FROM_REG(val,vrm) (vid_from_reg(val,vrm))
+
+#define ALARMS_FROM_REG(val) (val)
+
+/* Unlike some other drivers we DO NOT set initial limits.  Use
+ * the config file to set limits.
+ */
+
+/* Typically used with systems using a v9.1 VRM spec ? */
+#define ADM1026_INIT_VRM  91
+#define ADM1026_INIT_VID  -1
+
+/* Chip sampling rates
+ *
+ * Some sensors are not updated more frequently than once per second
+ *    so it doesn't make sense to read them more often than that.
+ *    We cache the results and return the saved data if the driver
+ *    is called again before a second has elapsed.
+ *
+ * Also, there is significant configuration data for this chip
+ *    So, we keep the config data up to date in the cache
+ *    when it is written and only sample it once every 5 *minutes*
+ */
+#define ADM1026_DATA_INTERVAL  (1 * HZ)
+#define ADM1026_CONFIG_INTERVAL  (5 * 60 * HZ)
+
+/* We allow for multiple chips in a single system.
+ *
+ * For each registered ADM1026, we need to keep state information
+ * at client->data. The adm1026_data structure is dynamically
+ * allocated, when a new client structure is allocated. */
+
+struct adm1026_data {
+	struct i2c_client client;
+	struct semaphore lock;
+	int sysctl_id;
+	enum chips type;
+
+	struct semaphore update_lock;
+	int valid;		/* !=0 if following fields are valid */
+	unsigned long last_reading;	/* In jiffies */
+	unsigned long last_config;	/* In jiffies */
+
+	u8 in[17];		/* Register value */
+	u8 in_max[17];		/* Register value */
+	u8 in_min[17];		/* Register value */
+	s8 temp[3];		/* Register value */
+	s8 temp_min[3];		/* Register value */
+	s8 temp_max[3];		/* Register value */
+	s8 temp_tmin[3];	/* Register value */
+	s8 temp_therm[3];	/* Register value */
+	s8 temp_offset[3];	/* Register value */
+	u8 fan[8];		/* Register value */
+	u8 fan_min[8];		/* Register value */
+	u8 fan_div[8];		/* Decoded value */
+	u8 pwm;			/* Register value */
+	u8 analog_out;		/* Register value */
+	int vid;		/* Decoded value */
+	u8 vrm;			/* VRM version */
+	long alarms;		/* Register encoding, combined */
+	long alarm_mask;	/* Register encoding, combined */
+	long gpio;		/* Register encoding, combined */
+	long gpio_mask;		/* Register encoding, combined */
+	u8 gpio_config[17];	/* Decoded value */
+	u8 config1;		/* Register value */
+	u8 config2;		/* Register value */
+	u8 config3;		/* Register value */
+};
+
+static int adm1026_attach_adapter(struct i2c_adapter *adapter);
+static int adm1026_detect(struct i2c_adapter *adapter, int address,
+			unsigned short flags, int kind);
+static int adm1026_detach_client(struct i2c_client *client);
+
+static int adm1026_read_value(struct i2c_client *client, u8 register);
+static int adm1026_write_value(struct i2c_client *client, u8 register, int value);
+static void adm1026_print_gpio(struct i2c_client *client);
+static void adm1026_fixup_gpio(struct i2c_client *client);
+static void adm1026_update_client(struct i2c_client *client);
+static void adm1026_init_client(struct i2c_client *client);
+
+
+static void adm1026_in(struct i2c_client *client, int operation, int ctl_name,
+			int *nrels_mag, long *results);
+static void adm1026_in16(struct i2c_client *client, int operation, int ctl_name,
+			int *nrels_mag, long *results);
+static void adm1026_fan(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm1026_fixup_fan_min(struct i2c_client *client,
+			 int fan, int old_div);
+static void adm1026_fan_div(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm1026_temp(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm1026_temp_offset(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm1026_temp_tmin(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm1026_temp_therm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm1026_vid(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm1026_vrm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm1026_alarms(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm1026_alarm_mask(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm1026_gpio(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm1026_gpio_mask(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm1026_pwm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm1026_analog_out(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm1026_afc(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+
+static struct i2c_driver adm1026_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "ADM1026 compatible sensor driver",
+	.id		= I2C_DRIVERID_ADM1026,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= &adm1026_attach_adapter,
+	.detach_client	= &adm1026_detach_client,
+};
+
+/* Unique ID assigned to each ADM1026 detected */
+static int adm1026_id = 0;
+
+/* -- SENSORS SYSCTL START -- */
+#define ADM1026_SYSCTL_FAN0                 1000
+#define ADM1026_SYSCTL_FAN1                 1001
+#define ADM1026_SYSCTL_FAN2                 1002
+#define ADM1026_SYSCTL_FAN3                 1003
+#define ADM1026_SYSCTL_FAN4                 1004
+#define ADM1026_SYSCTL_FAN5                 1005
+#define ADM1026_SYSCTL_FAN6                 1006
+#define ADM1026_SYSCTL_FAN7                 1007
+#define ADM1026_SYSCTL_FAN_DIV              1008
+#define ADM1026_SYSCTL_GPIO                 1009
+#define ADM1026_SYSCTL_GPIO_MASK            1010
+#define ADM1026_SYSCTL_ALARMS               1011
+#define ADM1026_SYSCTL_ALARM_MASK           1012
+#define ADM1026_SYSCTL_IN0                  1013
+#define ADM1026_SYSCTL_IN1                  1014
+#define ADM1026_SYSCTL_IN2                  1015
+#define ADM1026_SYSCTL_IN3                  1016
+#define ADM1026_SYSCTL_IN4                  1017
+#define ADM1026_SYSCTL_IN5                  1018
+#define ADM1026_SYSCTL_IN6                  1019
+#define ADM1026_SYSCTL_IN7                  1020
+#define ADM1026_SYSCTL_IN8                  1021
+#define ADM1026_SYSCTL_IN9                  1022
+#define ADM1026_SYSCTL_IN10                 1023
+#define ADM1026_SYSCTL_IN11                 1024
+#define ADM1026_SYSCTL_IN12                 1025
+#define ADM1026_SYSCTL_IN13                 1026
+#define ADM1026_SYSCTL_IN14                 1027
+#define ADM1026_SYSCTL_IN15                 1028
+#define ADM1026_SYSCTL_IN16                 1029
+#define ADM1026_SYSCTL_PWM                  1030
+#define ADM1026_SYSCTL_ANALOG_OUT           1031
+#define ADM1026_SYSCTL_AFC                  1032
+#define ADM1026_SYSCTL_TEMP1                1033
+#define ADM1026_SYSCTL_TEMP2                1034
+#define ADM1026_SYSCTL_TEMP3                1035
+#define ADM1026_SYSCTL_TEMP_OFFSET1         1036
+#define ADM1026_SYSCTL_TEMP_OFFSET2         1037
+#define ADM1026_SYSCTL_TEMP_OFFSET3         1038
+#define ADM1026_SYSCTL_TEMP_THERM1          1039
+#define ADM1026_SYSCTL_TEMP_THERM2          1040
+#define ADM1026_SYSCTL_TEMP_THERM3          1041
+#define ADM1026_SYSCTL_TEMP_TMIN1           1042
+#define ADM1026_SYSCTL_TEMP_TMIN2           1043
+#define ADM1026_SYSCTL_TEMP_TMIN3           1044
+#define ADM1026_SYSCTL_VID                  1045
+#define ADM1026_SYSCTL_VRM                  1046
+
+#define ADM1026_ALARM_TEMP2   (1L <<  0)
+#define ADM1026_ALARM_TEMP3   (1L <<  1)
+#define ADM1026_ALARM_IN9     (1L <<  1)
+#define ADM1026_ALARM_IN11    (1L <<  2)
+#define ADM1026_ALARM_IN12    (1L <<  3)
+#define ADM1026_ALARM_IN13    (1L <<  4)
+#define ADM1026_ALARM_IN14    (1L <<  5)
+#define ADM1026_ALARM_IN15    (1L <<  6)
+#define ADM1026_ALARM_IN16    (1L <<  7)
+#define ADM1026_ALARM_IN0     (1L <<  8)
+#define ADM1026_ALARM_IN1     (1L <<  9)
+#define ADM1026_ALARM_IN2     (1L << 10)
+#define ADM1026_ALARM_IN3     (1L << 11)
+#define ADM1026_ALARM_IN4     (1L << 12)
+#define ADM1026_ALARM_IN5     (1L << 13)
+#define ADM1026_ALARM_IN6     (1L << 14)
+#define ADM1026_ALARM_IN7     (1L << 15)
+#define ADM1026_ALARM_FAN0    (1L << 16)
+#define ADM1026_ALARM_FAN1    (1L << 17)
+#define ADM1026_ALARM_FAN2    (1L << 18)
+#define ADM1026_ALARM_FAN3    (1L << 19)
+#define ADM1026_ALARM_FAN4    (1L << 20)
+#define ADM1026_ALARM_FAN5    (1L << 21)
+#define ADM1026_ALARM_FAN6    (1L << 22)
+#define ADM1026_ALARM_FAN7    (1L << 23)
+#define ADM1026_ALARM_TEMP1   (1L << 24)
+#define ADM1026_ALARM_IN10    (1L << 25)
+#define ADM1026_ALARM_IN8     (1L << 26)
+#define ADM1026_ALARM_THERM   (1L << 27)
+#define ADM1026_ALARM_AFC_FAN (1L << 28)
+#define ADM1026_ALARM_UNUSED  (1L << 29)
+#define ADM1026_ALARM_CI      (1L << 30)
+/* -- SENSORS SYSCTL END -- */
+
+/* The /proc/sys entries */
+/* These files are created for each detected ADM1026. This is just a template;
+ *    The actual list is built from this and additional per-chip
+ *    custom lists below.  Note the XXX_LEN macros.  These must be
+ *    compile time constants because they will be used to allocate
+ *    space for the final template passed to i2c_register_entry.
+ *    We depend on the ability of GCC to evaluate expressions at
+ *    compile time to turn these expressions into compile time
+ *    constants, but this can generate a warning.
+ */
+static ctl_table adm1026_common[] = {
+	{ADM1026_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_in},
+	{ADM1026_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_in},
+	{ADM1026_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_in},
+	{ADM1026_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_in},
+	{ADM1026_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_in},
+	{ADM1026_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_in},
+	{ADM1026_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_in},
+	{ADM1026_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_in},
+	{ADM1026_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_in},
+	{ADM1026_SYSCTL_IN9, "in9", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_in},
+	{ADM1026_SYSCTL_IN10, "in10", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_in},
+	{ADM1026_SYSCTL_IN11, "in11", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_in},
+	{ADM1026_SYSCTL_IN12, "in12", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_in},
+	{ADM1026_SYSCTL_IN13, "in13", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_in},
+	{ADM1026_SYSCTL_IN14, "in14", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_in},
+	{ADM1026_SYSCTL_IN15, "in15", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_in},
+	{ADM1026_SYSCTL_IN16, "in16", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_in16},
+
+	{ADM1026_SYSCTL_FAN0, "fan0", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_fan},
+	{ADM1026_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_fan},
+	{ADM1026_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_fan},
+	{ADM1026_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_fan},
+	{ADM1026_SYSCTL_FAN4, "fan4", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_fan},
+	{ADM1026_SYSCTL_FAN5, "fan5", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_fan},
+	{ADM1026_SYSCTL_FAN6, "fan6", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_fan},
+	{ADM1026_SYSCTL_FAN7, "fan7", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_fan},
+	{ADM1026_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_fan_div},
+
+	{ADM1026_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_temp},
+	{ADM1026_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_temp},
+	{ADM1026_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_temp},
+	{ADM1026_SYSCTL_TEMP_OFFSET1, "temp1_offset", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_temp_offset},
+	{ADM1026_SYSCTL_TEMP_OFFSET2, "temp2_offset", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_temp_offset},
+	{ADM1026_SYSCTL_TEMP_OFFSET3, "temp3_offset", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_temp_offset},
+	{ADM1026_SYSCTL_TEMP_TMIN1, "temp1_tmin", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_temp_tmin},
+	{ADM1026_SYSCTL_TEMP_TMIN2, "temp2_tmin", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_temp_tmin},
+	{ADM1026_SYSCTL_TEMP_TMIN3, "temp3_tmin", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_temp_tmin},
+	{ADM1026_SYSCTL_TEMP_THERM1, "temp1_therm", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_temp_therm},
+	{ADM1026_SYSCTL_TEMP_THERM2, "temp2_therm", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_temp_therm},
+	{ADM1026_SYSCTL_TEMP_THERM3, "temp3_therm", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_temp_therm},
+
+	{ADM1026_SYSCTL_VID, "vid", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_vid},
+	{ADM1026_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_vrm},
+
+	{ADM1026_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_alarms},
+	{ADM1026_SYSCTL_ALARM_MASK, "alarm_mask", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_alarm_mask},
+
+	{ADM1026_SYSCTL_GPIO, "gpio", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_gpio},
+	{ADM1026_SYSCTL_GPIO_MASK, "gpio_mask", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_gpio_mask},
+
+	{ADM1026_SYSCTL_PWM, "pwm", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_pwm},
+	{ADM1026_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_analog_out},
+	{ADM1026_SYSCTL_AFC, "afc", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &adm1026_afc},
+
+	{0}
+};
+#define CTLTBL_COMMON (sizeof(adm1026_common)/sizeof(adm1026_common[0]))
+
+#define MAX2(a,b) ((a)>(b)?(a):(b))
+#define MAX3(a,b,c) ((a)>(b)?MAX2((a),(c)):MAX2((b),(c)))
+#define MAX4(a,b,c,d) ((a)>(b)?MAX3((a),(c),(d)):MAX3((b),(c),(d)))
+
+#define CTLTBL_MAX (CTLTBL_COMMON)
+
+/* This function is called when:
+     * the module is loaded
+     * a new adapter is loaded
+ */
+int adm1026_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, adm1026_detect);
+}
+
+/* This function is called by i2c_detect */
+int adm1026_detect(struct i2c_adapter *adapter, int address,
+		unsigned short flags, int kind)
+{
+	int i;
+	int company, verstep ;
+	struct i2c_client *new_client;
+	struct adm1026_data *data;
+	int err = 0;
+	const char *type_name = "";
+	struct ctl_table template[CTLTBL_MAX] ;
+	struct ctl_table * template_next = template ;
+
+	if (i2c_is_isa_adapter(adapter)) {
+		/* This chip has no ISA interface */
+		goto ERROR0 ;
+	}
+
+	if (!i2c_check_functionality(adapter,
+					I2C_FUNC_SMBUS_BYTE_DATA)) {
+		/* We need to be able to do byte I/O */
+		goto ERROR0 ;
+	}
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access adm1026_{read,write}_value. */
+
+	if (!(data = kmalloc(sizeof(struct adm1026_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &adm1026_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+
+	company = adm1026_read_value(new_client, ADM1026_REG_COMPANY);
+	verstep = adm1026_read_value(new_client, ADM1026_REG_VERSTEP);
+
+#ifdef DEBUG
+	printk("adm1026: Detecting device at %d,0x%02x with"
+		" COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
+		i2c_adapter_id(new_client->adapter), new_client->addr,
+		company, verstep
+	    );
+#endif
+
+	/* If auto-detecting, Determine the chip type. */
+	if (kind <= 0) {
+#ifdef DEBUG
+		printk("adm1026: Autodetecting device at %d,0x%02x ...\n",
+			i2c_adapter_id(adapter), address );
+#endif
+		if( company == ADM1026_COMPANY_ANALOG_DEV
+		    && verstep == ADM1026_VERSTEP_ADM1026 ) {
+			kind = adm1026 ;
+		} else if( company == ADM1026_COMPANY_ANALOG_DEV
+		    && (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC ) {
+			printk("adm1026: Unrecgonized stepping 0x%02x"
+			    " Defaulting to ADM1026.\n", verstep );
+			kind = adm1026 ;
+		} else if( (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC ) {
+			printk("adm1026: Found version/stepping 0x%02x"
+			    " Assuming generic ADM1026.\n", verstep );
+			kind = any_chip ;
+		} else {
+#ifdef DEBUG
+			printk("adm1026: Autodetection failed\n");
+#endif
+			/* Not an ADM1026 ... */
+			if( kind == 0 ) {  /* User used force=x,y */
+			    printk("adm1026: Generic ADM1026 Version 6 not"
+				" found at %d,0x%02x. Try force_adm1026.\n",
+				i2c_adapter_id(adapter), address );
+			}
+			err = 0 ;
+			goto ERROR1;
+		}
+	}
+
+	/* Fill in the chip specific driver values */
+	switch (kind) {
+	case any_chip :
+		type_name = "adm1026";
+		strcpy(new_client->name, "Generic ADM1026");
+		template_next = template ;  /* None used */
+		break ;
+	case adm1026 :
+		type_name = "adm1026";
+		strcpy(new_client->name, "Analog Devices ADM1026");
+		template_next = template ;
+		break ;
+#if 0
+	/* Example of another adm1026 "compatible" device */
+	case adx1000 :
+		type_name = "adx1000";
+		strcpy(new_client->name, "Compatible ADX1000");
+		memcpy( template, adx_specific, sizeof(adx_specific) );
+		template_next = template + CTLTBL_ADX1000 ;
+		break ;
+#endif
+	default :
+		printk("adm1026: Internal error, invalid kind (%d)!", kind);
+		err = -EFAULT ;
+		goto ERROR1;
+	}
+
+	/* Fill in the remaining client fields */
+	new_client->id = adm1026_id++;
+	printk("adm1026(%d): Assigning ID %d to %s at %d,0x%02x\n",
+		new_client->id, new_client->id, new_client->name,
+		i2c_adapter_id(new_client->adapter),
+		new_client->addr
+	    );
+
+	/* Housekeeping values */
+	data->type = kind;
+	data->valid = 0;
+
+	/* Set the VRM version */
+	data->vrm = ADM1026_INIT_VRM ;
+	data->vid = ADM1026_INIT_VID ;
+
+	init_MUTEX(&data->update_lock);
+
+	/* Initialize the ADM1026 chip */
+	adm1026_init_client(new_client);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR1;
+
+	/* Finish out the template */
+	memcpy(template_next, adm1026_common, sizeof(adm1026_common));
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client,
+					type_name,
+					template)) < 0) {
+		err = i;
+		goto ERROR2;
+	}
+	data->sysctl_id = i;
+
+	return 0;
+
+	/* Error out and cleanup code */
+    ERROR2:
+	i2c_detach_client(new_client);
+    ERROR1:
+	kfree(data);
+    ERROR0:
+	return err;
+}
+
+int adm1026_detach_client(struct i2c_client *client)
+{
+	int err;
+	int id ;
+
+	id = client->id;
+	i2c_deregister_entry(((struct adm1026_data *)(client->data))->sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk("adm1026(%d): Client deregistration failed,"
+			" client not detached.\n", id );
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+int adm1026_read_value(struct i2c_client *client, u8 reg)
+{
+	int res;
+
+	if( reg < 0x80 ) {
+		/* "RAM" locations */
+		res = i2c_smbus_read_byte_data(client, reg) & 0xff ;
+	} else {
+		/* EEPROM, do nothing */
+		res = 0 ;
+	}
+
+	return res ;
+}
+
+int adm1026_write_value(struct i2c_client *client, u8 reg, int value)
+{
+	int res ;
+
+	if( reg < 0x80 ) {
+		/* "RAM" locations */
+		res = i2c_smbus_write_byte_data(client, reg, value);
+	} else {
+		/* EEPROM, do nothing */
+		res = 0 ;
+	}
+
+	return res ;
+}
+
+/* Called when we have found a new ADM1026. */
+void adm1026_init_client(struct i2c_client *client)
+{
+	int value ;
+	int i;
+	struct adm1026_data *data = client->data;
+
+#ifdef DEBUG
+	printk("adm1026(%d): Initializing device\n", client->id);
+#endif
+
+	/* Read chip config */
+	data->config1 = adm1026_read_value(client, ADM1026_REG_CONFIG1);
+	data->config2 = adm1026_read_value(client, ADM1026_REG_CONFIG2);
+	data->config3 = adm1026_read_value(client, ADM1026_REG_CONFIG3);
+
+	/* Inform user of chip config */
+#ifdef DEBUG
+	printk("adm1026(%d): ADM1026_REG_CONFIG1 is: 0x%02x\n",
+		client->id, data->config1 );
+#endif
+	if( (data->config1 & CFG1_MONITOR) == 0 ) {
+		printk("adm1026(%d): Monitoring not currently enabled.\n",
+			    client->id );
+	}
+	if( data->config1 & CFG1_INT_ENABLE ) {
+		printk("adm1026(%d): SMBALERT interrupts are enabled.\n",
+			    client->id );
+	}
+	if( data->config1 & CFG1_AIN8_9 ) {
+		printk("adm1026(%d): in8 and in9 enabled.  temp3 disabled.\n",
+			    client->id );
+	} else {
+		printk("adm1026(%d): temp3 enabled.  in8 and in9 disabled.\n",
+			    client->id );
+	}
+	if( data->config1 & CFG1_THERM_HOT ) {
+		printk("adm1026(%d): Automatic THERM, PWM, and temp limits enabled.\n",
+			    client->id );
+	}
+
+	value = data->config3 ;
+	if( data->config3 & CFG3_GPIO16_ENABLE ) {
+		printk("adm1026(%d): GPIO16 enabled.  THERM pin disabled.\n",
+			    client->id );
+	} else {
+		printk("adm1026(%d): THERM pin enabled.  GPIO16 disabled.\n",
+			    client->id );
+	}
+	if( data->config3 & CFG3_VREF_250 ) {
+		printk("adm1026(%d): Vref is 2.50 Volts.\n", client->id );
+	} else {
+		printk("adm1026(%d): Vref is 1.82 Volts.\n", client->id );
+	}
+
+	/* Read and pick apart the existing GPIO configuration */
+	value = 0 ;
+	for( i = 0 ; i <= 15 ; ++i ) {
+		if( (i & 0x03) == 0 ) {
+			value = adm1026_read_value(client,
+					ADM1026_REG_GPIO_CFG_0_3 + i/4 );
+		}
+		data->gpio_config[i] = value & 0x03 ;
+		value >>= 2 ;
+	}
+	data->gpio_config[16] = (data->config3 >> 6) & 0x03 ;
+
+	/* ... and then print it */
+	adm1026_print_gpio(client);
+
+	/* If the user asks us to reprogram the GPIO config, then
+	 *   do it now.  But only if this is the first ADM1026.
+	 */
+	if( client->id == 0
+	    && (gpio_input[0] != -1 || gpio_output[0] != -1
+		|| gpio_inverted[0] != -1 || gpio_normal[0] != -1
+		|| gpio_fan[0] != -1 ) ) {
+		adm1026_fixup_gpio(client);
+	}
+
+	/* WE INTENTIONALLY make no changes to the limits,
+	 *   offsets, pwms and fans.  If they were
+	 *   configured, we don't want to mess with them.
+	 *   If they weren't, the default is generally safe
+	 *   and will suffice until 'sensors -s' can be run.
+	 */
+
+	/* Start monitoring */
+	value = adm1026_read_value(client, ADM1026_REG_CONFIG1);
+
+	/* Set MONITOR, clear interrupt acknowledge and s/w reset */
+	value = (value | CFG1_MONITOR) & (~CFG1_INT_CLEAR & ~CFG1_RESET) ;
+#ifdef DEBUG
+	printk("adm1026(%d): Setting CONFIG to: 0x%02x\n", client->id, value );
+#endif
+	data->config1 = value ;
+	adm1026_write_value(client, ADM1026_REG_CONFIG1, value);
+
+}
+
+void adm1026_print_gpio(struct i2c_client *client)
+{
+	struct adm1026_data *data = client->data;
+	int  i ;
+
+	printk("adm1026(%d): GPIO config is:\nadm1026(%d):",
+			    client->id, client->id );
+	for( i = 0 ; i <= 7 ; ++i ) {
+		if( data->config2 & (1 << i) ) {
+			printk( " %sGP%s%d",
+				data->gpio_config[i] & 0x02 ? "" : "!",
+				data->gpio_config[i] & 0x01 ? "OUT" : "IN",
+				i );
+		} else {
+			printk( " FAN%d", i );
+		}
+	}
+	printk( "\nadm1026(%d):", client->id );
+	for( i = 8 ; i <= 15 ; ++i ) {
+		printk( " %sGP%s%d",
+			data->gpio_config[i] & 0x02 ? "" : "!",
+			data->gpio_config[i] & 0x01 ? "OUT" : "IN",
+			i );
+	}
+	if( data->config3 & CFG3_GPIO16_ENABLE ) {
+		printk( " %sGP%s16\n",
+			data->gpio_config[16] & 0x02 ? "" : "!",
+			data->gpio_config[16] & 0x01 ? "OUT" : "IN" );
+	} else {
+		/* GPIO16 is THERM */
+		printk( " THERM\n" );
+	}
+}
+
+void adm1026_fixup_gpio(struct i2c_client *client)
+{
+	struct adm1026_data *data = client->data;
+	int  i ;
+	int  value ;
+
+	/* Make the changes requested. */
+	/* We may need to unlock/stop monitoring or soft-reset the
+	 *    chip before we can make changes.  This hasn't been
+	 *    tested much.  FIXME
+	 */
+
+	/* Make outputs */
+	for( i = 0 ; i <= 16 ; ++i ) {
+		if( gpio_output[i] >= 0 && gpio_output[i] <= 16 ) {
+			data->gpio_config[gpio_output[i]] |= 0x01 ;
+		}
+		/* if GPIO0-7 is output, it isn't a FAN tach */
+		if( gpio_output[i] >= 0 && gpio_output[i] <= 7 ) {
+			data->config2 |= 1 << gpio_output[i] ;
+		}
+	}
+
+	/* Input overrides output */
+	for( i = 0 ; i <= 16 ; ++i ) {
+		if( gpio_input[i] >= 0 && gpio_input[i] <= 16 ) {
+			data->gpio_config[gpio_input[i]] &= ~ 0x01 ;
+		}
+		/* if GPIO0-7 is input, it isn't a FAN tach */
+		if( gpio_input[i] >= 0 && gpio_input[i] <= 7 ) {
+			data->config2 |= 1 << gpio_input[i] ;
+		}
+	}
+
+	/* Inverted  */
+	for( i = 0 ; i <= 16 ; ++i ) {
+		if( gpio_inverted[i] >= 0 && gpio_inverted[i] <= 16 ) {
+			data->gpio_config[gpio_inverted[i]] &= ~ 0x02 ;
+		}
+	}
+
+	/* Normal overrides inverted  */
+	for( i = 0 ; i <= 16 ; ++i ) {
+		if( gpio_normal[i] >= 0 && gpio_normal[i] <= 16 ) {
+			data->gpio_config[gpio_normal[i]] |= 0x02 ;
+		}
+	}
+
+	/* Fan overrides input and output */
+	for( i = 0 ; i <= 7 ; ++i ) {
+		if( gpio_fan[i] >= 0 && gpio_fan[i] <= 7 ) {
+			data->config2 &= ~( 1 << gpio_fan[i] );
+		}
+	}
+
+	/* Write new configs to registers */
+	adm1026_write_value(client, ADM1026_REG_CONFIG2, data->config2);
+	data->config3 = (data->config3 & 0x3f)
+			| ((data->gpio_config[16] & 0x03) << 6) ;
+	adm1026_write_value(client, ADM1026_REG_CONFIG3, data->config3);
+	for( i = 15, value = 0 ; i >= 0 ; --i ) {
+		value <<= 2 ;
+		value |= data->gpio_config[i] & 0x03 ;
+		if( (i & 0x03) == 0 ) {
+			adm1026_write_value(client,
+					ADM1026_REG_GPIO_CFG_0_3 + i/4,
+					value );
+			value = 0 ;
+		}
+	}
+
+	/* Print the new config */
+	adm1026_print_gpio(client);
+}
+
+void adm1026_update_client(struct i2c_client *client)
+{
+	struct adm1026_data *data = client->data;
+	int i;
+	long value, alarms, gpio ;
+
+	down(&data->update_lock);
+
+	if (!data->valid
+	    || (jiffies - data->last_reading > ADM1026_DATA_INTERVAL )) {
+		/* Things that change quickly */
+
+#ifdef DEBUG
+		printk("adm1026(%d): Reading sensor values\n", client->id);
+#endif
+		for (i = 0 ; i <= 16 ; ++i) {
+			data->in[i] =
+			    adm1026_read_value(client, ADM1026_REG_IN(i));
+		}
+
+		for (i = 0 ; i <= 7 ; ++i) {
+			data->fan[i] =
+			    adm1026_read_value(client, ADM1026_REG_FAN(i));
+		}
+
+		for (i = 0 ; i <= 2 ; ++i) {
+			/* NOTE: temp[] is s8 and we assume 2's complement
+			 *   "conversion" in the assignment   */
+			data->temp[i] =
+			    adm1026_read_value(client, ADM1026_REG_TEMP(i));
+		}
+
+		data->pwm = adm1026_read_value(client, ADM1026_REG_PWM);
+		data->analog_out = adm1026_read_value(client, ADM1026_REG_DAC);
+
+		/* GPIO16 is MSbit of alarms, move it to gpio */
+		alarms  = adm1026_read_value(client, ADM1026_REG_STATUS4);
+		gpio = alarms & 0x80 ? 0x0100 : 0 ;  /* GPIO16 */
+		alarms &= 0x7f ;
+		alarms <<= 8 ;
+		alarms |= adm1026_read_value(client, ADM1026_REG_STATUS3);
+		alarms <<= 8 ;
+		alarms |= adm1026_read_value(client, ADM1026_REG_STATUS2);
+		alarms <<= 8 ;
+		alarms |= adm1026_read_value(client, ADM1026_REG_STATUS1);
+		data->alarms = alarms ;
+
+		/* Read the GPIO values */
+		gpio |= adm1026_read_value(client, ADM1026_REG_GPIO_STATUS_8_15);
+		gpio <<= 8 ;
+		gpio |= adm1026_read_value(client, ADM1026_REG_GPIO_STATUS_0_7);
+		data->gpio = gpio ;
+
+		data->last_reading = jiffies ;
+	};  /* last_reading */
+
+	if (!data->valid
+	    || (jiffies - data->last_config > ADM1026_CONFIG_INTERVAL) ) {
+		/* Things that don't change often */
+
+#ifdef DEBUG
+		printk("adm1026(%d): Reading config values\n", client->id);
+#endif
+		for (i = 0 ; i <= 16 ; ++i) {
+			data->in_min[i] =
+			    adm1026_read_value(client, ADM1026_REG_IN_MIN(i));
+			data->in_max[i] =
+			    adm1026_read_value(client, ADM1026_REG_IN_MAX(i));
+		}
+
+		value = adm1026_read_value(client, ADM1026_REG_FAN_DIV_0_3)
+			| (adm1026_read_value(client, ADM1026_REG_FAN_DIV_4_7) << 8);
+		for (i = 0 ; i <= 7 ; ++i) {
+			data->fan_min[i] =
+			    adm1026_read_value(client, ADM1026_REG_FAN_MIN(i));
+			data->fan_div[i] = DIV_FROM_REG(value & 0x03);
+			value >>= 2 ;
+		}
+
+		for (i = 0; i <= 2; ++i) {
+			/* NOTE: temp_xxx[] are s8 and we assume 2's complement
+			 *   "conversion" in the assignment   */
+			data->temp_min[i] =
+			    adm1026_read_value(client, ADM1026_REG_TEMP_MIN(i));
+			data->temp_max[i] =
+			    adm1026_read_value(client, ADM1026_REG_TEMP_MAX(i));
+			data->temp_tmin[i] =
+			    adm1026_read_value(client, ADM1026_REG_TEMP_TMIN(i));
+			data->temp_therm[i] =
+			    adm1026_read_value(client, ADM1026_REG_TEMP_THERM(i));
+			data->temp_offset[i] =
+			    adm1026_read_value(client, ADM1026_REG_TEMP_OFFSET(i));
+		}
+
+		/* Read the STATUS/alarm masks */
+		alarms  = adm1026_read_value(client, ADM1026_REG_MASK4);
+		gpio    = alarms & 0x80 ? 0x0100 : 0 ;  /* GPIO16 */
+		alarms  = (alarms & 0x7f) << 8 ;
+		alarms |= adm1026_read_value(client, ADM1026_REG_MASK3);
+		alarms <<= 8 ;
+		alarms |= adm1026_read_value(client, ADM1026_REG_MASK2);
+		alarms <<= 8 ;
+		alarms |= adm1026_read_value(client, ADM1026_REG_MASK1);
+		data->alarm_mask = alarms ;
+
+		/* Read the GPIO values */
+		gpio |= adm1026_read_value(client, ADM1026_REG_GPIO_MASK_8_15);
+		gpio <<= 8 ;
+		gpio |= adm1026_read_value(client, ADM1026_REG_GPIO_MASK_0_7);
+		data->gpio_mask = gpio ;
+
+		/* Read the GPIO config */
+		data->config2 = adm1026_read_value(client, ADM1026_REG_CONFIG2);
+		data->config3 = adm1026_read_value(client, ADM1026_REG_CONFIG3);
+		data->gpio_config[16] = (data->config3 >> 6) & 0x03 ;
+
+		value = 0 ;
+		for( i = 0 ; i <= 15 ; ++i ) {
+			if( (i & 0x03) == 0 ) {
+				value = adm1026_read_value(client,
+					    ADM1026_REG_GPIO_CFG_0_3 + i/4 );
+			}
+			data->gpio_config[i] = value & 0x03 ;
+			value >>= 2 ;
+		}
+
+		data->last_config = jiffies;
+	};  /* last_config */
+
+	/* We don't know where or even _if_ the VID might be on the GPIO
+	 *    pins.  But the datasheet gives an example config showing
+	 *    GPIO11-15 being used to monitor VID0-4, so we go with that
+	 *    but make the vid WRITEABLE so if it's wrong, the user can
+	 *    set it in /etc/sensors.conf perhaps using an expression or
+	 *    0 to trigger a re-read from the GPIO pins.
+	 */
+	if( data->vid == ADM1026_INIT_VID ) {
+		/* Hasn't been set yet, make a bold assumption */
+		printk("adm1026(%d): Setting VID from GPIO11-15.\n",
+			    client->id );
+		data->vid = (data->gpio >> 11) & 0x1f ;
+	}
+	
+	data->valid = 1;
+
+	up(&data->update_lock);
+}
+
+
+/* The following functions are the call-back functions of the /proc/sys and
+   sysctl files.  The appropriate function is referenced in the ctl_table
+   extra1 field.
+
+   Each function must return the magnitude (power of 10 to divide the
+   data with) if it is called with operation set to SENSORS_PROC_REAL_INFO.
+   It must put a maximum of *nrels elements in results reflecting the
+   data of this file, and set *nrels to the number it actually put in
+   it, if operation is SENSORS_PROC_REAL_READ.  Finally, it must get
+   up to *nrels elements from results and write them to the chip, if
+   operations is SENSORS_PROC_REAL_WRITE.
+ */
+void adm1026_in(struct i2c_client *client, int operation, int ctl_name,
+	     int *nrels_mag, long *results)
+{
+	struct adm1026_data *data = client->data;
+	int nr = ctl_name - ADM1026_SYSCTL_IN0;
+
+	/* We handle in0 - in15 here.  in16 (-12V) is handled below */
+	if (nr < 0 || nr > 15)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 3;  /* 1.000 */
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1026_update_client(client);
+		results[0] = INS_FROM_REG(nr,data->in_min[nr]);
+		results[1] = INS_FROM_REG(nr,data->in_max[nr]);
+		results[2] = INS_FROM_REG(nr,data->in[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 1) {
+			data->in_max[nr] = INS_TO_REG(nr,results[1]);
+			adm1026_write_value(client, ADM1026_REG_IN_MAX(nr),
+					 data->in_max[nr]);
+		}
+		if (*nrels_mag > 0) {
+			data->in_min[nr] = INS_TO_REG(nr,results[0]);
+			adm1026_write_value(client, ADM1026_REG_IN_MIN(nr),
+					 data->in_min[nr]);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void adm1026_in16(struct i2c_client *client, int operation, int ctl_name,
+	     int *nrels_mag, long *results)
+{
+	struct adm1026_data *data = client->data;
+	int nr = ctl_name - ADM1026_SYSCTL_IN0;
+
+	/* We handle in16 (-12V) here */
+	if (nr != 16)
+		return ;  /* ERROR */
+
+	/* Apply offset and swap min/max so that min is 90% of
+	 *    target and max is 110% of target.
+	 */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 3;  /* 1.000 */
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1026_update_client(client);
+		results[0] = INS_FROM_REG(nr,data->in_max[nr])-NEG12_OFFSET ;
+		results[1] = INS_FROM_REG(nr,data->in_min[nr])-NEG12_OFFSET ;
+		results[2] = INS_FROM_REG(nr,data->in[nr])-NEG12_OFFSET ;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 1) {
+			data->in_min[nr] = INS_TO_REG(nr,results[1]+NEG12_OFFSET);
+			adm1026_write_value(client, ADM1026_REG_IN_MIN(nr),
+					 data->in_min[nr]);
+		}
+		if (*nrels_mag > 0) {
+			data->in_max[nr] = INS_TO_REG(nr,results[0]+NEG12_OFFSET);
+			adm1026_write_value(client, ADM1026_REG_IN_MAX(nr),
+					 data->in_max[nr]);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void adm1026_fan(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct adm1026_data *data = client->data;
+	int nr = ctl_name - ADM1026_SYSCTL_FAN0 ;
+
+	if (nr < 0 || nr > 7)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1026_update_client(client);
+		results[0] = FAN_FROM_REG(data->fan_min[nr], data->fan_div[nr]);
+		results[1] = FAN_FROM_REG(data->fan[nr], data->fan_div[nr]);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 0) {
+			data->fan_min[nr] = FAN_TO_REG(results[0],
+							data->fan_div[nr]);
+			adm1026_write_value(client, ADM1026_REG_FAN_MIN(nr),
+					 data->fan_min[nr]);
+		}
+		up(&data->update_lock);
+	}
+}
+
+/* Adjust fan_min to account for new fan divisor */
+void adm1026_fixup_fan_min(struct i2c_client *client, int fan, int old_div)
+{
+	struct adm1026_data *data = client->data;
+	int  new_div = data->fan_div[fan] ;
+	int  new_min;
+
+	/* 0 and 0xff are special.  Don't adjust them */
+	if( data->fan_min[fan] == 0 || data->fan_min[fan] == 0xff ) {
+		return ;
+	}
+
+	new_min = data->fan_min[fan] * old_div / new_div ;
+	new_min = SENSORS_LIMIT(new_min, 1, 254);
+	data->fan_min[fan] = new_min ;
+	adm1026_write_value(client, ADM1026_REG_FAN_MIN(fan), new_min);
+}
+
+void adm1026_fan_div(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct adm1026_data *data = client->data;
+	int i ;
+	int value, div, old ;
+
+	if (ctl_name != ADM1026_SYSCTL_FAN_DIV)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1026_update_client(client);
+		for( i = 0 ; i <= 7 ; ++i ) {
+			results[i] = data->fan_div[i] ;
+		}
+		*nrels_mag = 8;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		value = 0 ;
+		for( i = 7 ; i >= 0 ; --i ) {
+			value <<= 2 ;
+			if (*nrels_mag > i) {
+				old = data->fan_div[i] ;
+				div = DIV_TO_REG(results[i]) ;
+				data->fan_div[i] = DIV_FROM_REG(div) ;
+				if( data->fan_div[i] != old ) {
+					adm1026_fixup_fan_min(client,i,old);
+				}
+			} else {
+				div = DIV_TO_REG(data->fan_div[i]) ;
+			}
+			value |= div ;
+		}
+		adm1026_write_value(client, ADM1026_REG_FAN_DIV_0_3,
+			value & 0xff);
+		adm1026_write_value(client, ADM1026_REG_FAN_DIV_4_7,
+			(value >> 8) & 0xff);
+		up(&data->update_lock);
+	}
+}
+
+void adm1026_temp(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct adm1026_data *data = client->data;
+	int nr = ctl_name - ADM1026_SYSCTL_TEMP1 ;
+
+	if (nr < 0 || nr > 2)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1026_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_min[nr]);
+		results[1] = TEMP_FROM_REG(data->temp_max[nr]);
+		results[2] = TEMP_FROM_REG(data->temp[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 1) {
+			data->temp_max[nr] = TEMP_TO_REG(results[1]);
+			adm1026_write_value(client, ADM1026_REG_TEMP_MAX(nr),
+					 data->temp_max[nr]);
+		}
+		if (*nrels_mag > 0) {
+			data->temp_min[nr] = TEMP_TO_REG(results[0]);
+			adm1026_write_value(client, ADM1026_REG_TEMP_MIN(nr),
+					 data->temp_min[nr]);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void adm1026_temp_offset(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct adm1026_data *data = client->data;
+	int nr = ctl_name - ADM1026_SYSCTL_TEMP_OFFSET1 ;
+
+	if (nr < 0 || nr > 2)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1026_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_offset[nr]);
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 0) {
+			data->temp_offset[nr] = TEMP_TO_REG(results[0]);
+			adm1026_write_value(client, ADM1026_REG_TEMP_OFFSET(nr),
+			    data->temp_offset[nr]);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void adm1026_temp_tmin(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct adm1026_data *data = client->data;
+	int nr = ctl_name - ADM1026_SYSCTL_TEMP_TMIN1 ;
+
+	if (nr < 0 || nr > 2)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1026_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_tmin[nr]);
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 0) {
+			data->temp_tmin[nr] = TEMP_TO_REG(results[0]);
+			adm1026_write_value(client, ADM1026_REG_TEMP_TMIN(nr),
+			    data->temp_tmin[nr]);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void adm1026_temp_therm(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct adm1026_data *data = client->data;
+	int nr = ctl_name - ADM1026_SYSCTL_TEMP_THERM1 ;
+
+	if (nr < 0 || nr > 2)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1026_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_therm[nr]);
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 0) {
+			data->temp_therm[nr] = TEMP_TO_REG(results[0]);
+			adm1026_write_value(client, ADM1026_REG_TEMP_THERM(nr),
+			    data->temp_therm[nr]);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void adm1026_pwm(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct adm1026_data *data = client->data;
+
+	if (ctl_name != ADM1026_SYSCTL_PWM)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1026_update_client(client);
+		results[0] = PWM_FROM_REG(data->pwm);
+		results[1] = 1 ;  /* Always enabled */
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		/* PWM enable is read-only */
+		if (*nrels_mag > 0) {
+			data->pwm = PWM_TO_REG(results[0]);
+			adm1026_write_value(client, ADM1026_REG_PWM,
+					 data->pwm);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void adm1026_analog_out(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct adm1026_data *data = client->data;
+
+	if (ctl_name != ADM1026_SYSCTL_ANALOG_OUT)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;  /* 0 - 255 */
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1026_update_client(client);
+		results[0] = DAC_FROM_REG(data->analog_out);
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 0) {
+			data->analog_out = DAC_TO_REG(results[0]);
+			adm1026_write_value(client, ADM1026_REG_DAC,
+					 data->analog_out);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void adm1026_afc(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct adm1026_data *data = client->data;
+
+	if (ctl_name != ADM1026_SYSCTL_AFC)
+		return ;  /* ERROR */
+
+	/* PWM auto fan control, DAC auto fan control */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1026_update_client(client);
+		results[0] = (data->config1 & CFG1_PWM_AFC) != 0 ;
+		results[1] = (data->config1 & CFG1_DAC_AFC) != 0 ;
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 1) {
+			data->config1 = (data->config1 & ~CFG1_DAC_AFC)
+				| (results[1] ? CFG1_DAC_AFC : 0) ;
+		}
+		if (*nrels_mag > 0) {
+			data->config1 = (data->config1 & ~CFG1_PWM_AFC)
+				| (results[0] ? CFG1_PWM_AFC : 0) ;
+			adm1026_write_value(client, ADM1026_REG_CONFIG1,
+					 data->config1);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void adm1026_vid(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct adm1026_data *data = client->data;
+
+	if( ctl_name != ADM1026_SYSCTL_VID )
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 3;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1026_update_client(client);
+		results[0] = VID_FROM_REG((data->vid)&0x3f,data->vrm);
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		/* Hmmm... There isn't a VID_TO_REG mapping */
+		if (*nrels_mag > 0) {
+			if( results[0] >= 0 ) {
+				data->vid = results[0] & 0x3f ;
+			} else {
+				data->vid = ADM1026_INIT_VID ;
+			}
+		}
+		up(&data->update_lock);
+	}
+
+}
+
+void adm1026_vrm(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct adm1026_data *data = client->data;
+
+	if( ctl_name != ADM1026_SYSCTL_VRM )
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = data->vrm ;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag > 0) {
+			data->vrm = results[0] ;
+		}
+	}
+}
+
+void adm1026_alarms(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct adm1026_data *data = client->data;
+
+	if( ctl_name != ADM1026_SYSCTL_ALARMS )
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1026_update_client(client);
+		results[0] = data->alarms ;
+		*nrels_mag = 1;
+	}
+	/* FIXME: Perhaps we should implement a write function
+	 *   to clear an alarm?
+	 */
+}
+
+void adm1026_alarm_mask(struct i2c_client *client, int operation,
+		int ctl_name, int *nrels_mag, long *results)
+{
+	struct adm1026_data *data = client->data;
+	unsigned long mask ;
+
+	if( ctl_name != ADM1026_SYSCTL_ALARM_MASK )
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1026_update_client(client);
+		results[0] = data->alarm_mask ;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 0) {
+			data->alarm_mask = results[0] & 0x7fffffff ;
+			mask = data->alarm_mask
+				| (data->gpio_mask & 0x10000 ? 0x80000000 : 0) ;
+			adm1026_write_value(client, ADM1026_REG_MASK1,
+					mask & 0xff);
+			mask >>= 8 ;
+			adm1026_write_value(client, ADM1026_REG_MASK2,
+					mask & 0xff);
+			mask >>= 8 ;
+			adm1026_write_value(client, ADM1026_REG_MASK3,
+					mask & 0xff);
+			mask >>= 8 ;
+			adm1026_write_value(client, ADM1026_REG_MASK4,
+					mask & 0xff);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void adm1026_gpio(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct adm1026_data *data = client->data;
+	long gpio ;
+
+	if( ctl_name != ADM1026_SYSCTL_GPIO )
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1026_update_client(client);
+		results[0] = data->gpio ;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 0) {
+			data->gpio = results[0] & 0x1ffff ;
+			gpio = data->gpio ;
+			adm1026_write_value(client,
+				ADM1026_REG_GPIO_STATUS_0_7,
+				gpio & 0xff );
+			gpio >>= 8 ;
+			adm1026_write_value(client,
+				ADM1026_REG_GPIO_STATUS_8_15,
+				gpio & 0xff );
+			gpio = ((gpio >> 1) & 0x80)
+				| (data->alarms >> 24 & 0x7f);
+			adm1026_write_value(client,
+				ADM1026_REG_STATUS4,
+				gpio & 0xff );
+		}
+		up(&data->update_lock);
+	}
+}
+
+void adm1026_gpio_mask(struct i2c_client *client, int operation,
+		int ctl_name, int *nrels_mag, long *results)
+{
+	struct adm1026_data *data = client->data;
+	long mask ;
+
+	if( ctl_name != ADM1026_SYSCTL_GPIO_MASK )
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm1026_update_client(client);
+		results[0] = data->gpio_mask ;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 0) {
+			data->gpio_mask = results[0] & 0x1ffff ;
+			mask = data->gpio_mask ;
+			adm1026_write_value(client, ADM1026_REG_GPIO_MASK_0_7,
+					mask & 0xff);
+			mask >>= 8 ;
+			adm1026_write_value(client, ADM1026_REG_GPIO_MASK_8_15,
+					mask & 0xff);
+			mask = ((mask >> 1) & 0x80)
+				| (data->alarm_mask >> 24 & 0x7f);
+			adm1026_write_value(client, ADM1026_REG_MASK1,
+					mask & 0xff);
+		}
+		up(&data->update_lock);
+	}
+}
+
+static int __init sm_adm1026_init(void)
+{
+	printk("adm1026: Version %s (%s)\n", LM_VERSION, LM_DATE);
+	printk("adm1026: See http://www.penguincomputing.com/lm_sensors for more info.\n" );
+	return i2c_add_driver(&adm1026_driver);
+}
+
+static void __exit sm_adm1026_exit(void)
+{
+	i2c_del_driver(&adm1026_driver);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Philip Pokorny <ppokorny@penguincomputing.com");
+MODULE_DESCRIPTION("ADM1026 driver");
+
+module_init(sm_adm1026_init);
+module_exit(sm_adm1026_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/adm9240.c linux-2.4.27-leo/drivers/sensors/adm9240.c
--- linux-2.4.27/drivers/sensors/adm9240.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/adm9240.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,729 @@
+/*
+    adm9240.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 1999  Frodo Looijaard <frodol@dds.nl>
+    and Philip Edelbrock <phil@netroedge.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or 
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* Supports ADM9240, DS1780, and LM81. See doc/chips/adm9240 for details */
+
+/* 
+	A couple notes about the ADM9240:
+
+* It claims to be 'LM7x' register compatible.  This must be in reference
+  to only the LM78, because it is missing stuff to emulate LM75's as well. 
+  (like the Winbond W83781 does)
+ 
+* This driver was written from rev. 0 of the PDF, but it seems well 
+  written and complete (unlike the W83781 which is horrible and has
+  supposidly gone through a few revisions.. rev 0 of that one must
+  have been in crayon on construction paper...)
+  
+* All analog inputs can range from 0 to 2.5, eventhough some inputs are
+  marked as being 5V, 12V, etc.  I don't have any real voltages going 
+  into my prototype, so I'm not sure that things are computed right, 
+  but at least the limits seem to be working OK.
+  
+* Another curiousity is that the fan_div seems to be read-only.  I.e.,
+  any written value to it doesn't seem to make any difference.  The
+  fan_div seems to be 'stuck' at 2 (which isn't a bad value in most cases).
+  
+  
+  --Phil
+
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x2c, 0x2f, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_3(adm9240, ds1780, lm81);
+
+/* Many ADM9240 constants specified below */
+
+#define ADM9240_REG_IN_MAX(nr) (0x2b + (nr) * 2)
+#define ADM9240_REG_IN_MIN(nr) (0x2c + (nr) * 2)
+#define ADM9240_REG_IN(nr) (0x20 + (nr))
+
+/* The ADM9240 registers */
+#define ADM9240_REG_TEST 0x15
+#define ADM9240_REG_ANALOG_OUT 0x19
+/* These are all read-only */
+#define ADM9240_REG_2_5V 0x20
+#define ADM9240_REG_VCCP1 0x21
+#define ADM9240_REG_3_3V 0x22
+#define ADM9240_REG_5V 0x23
+#define ADM9240_REG_12V 0x24
+#define ADM9240_REG_VCCP2 0x25
+#define ADM9240_REG_TEMP 0x27
+#define ADM9240_REG_FAN1 0x28
+#define ADM9240_REG_FAN2 0x29
+#define ADM9240_REG_COMPANY_ID 0x3E	/* 0x23 for ADM9240; 0xDA for DS1780 */
+				     /* 0x01 for LM81 */
+#define ADM9240_REG_DIE_REV 0x3F
+/* These are read/write */
+#define ADM9240_REG_2_5V_HIGH 0x2B
+#define ADM9240_REG_2_5V_LOW 0x2C
+#define ADM9240_REG_VCCP1_HIGH 0x2D
+#define ADM9240_REG_VCCP1_LOW 0x2E
+#define ADM9240_REG_3_3V_HIGH 0x2F
+#define ADM9240_REG_3_3V_LOW 0x30
+#define ADM9240_REG_5V_HIGH 0x31
+#define ADM9240_REG_5V_LOW 0x32
+#define ADM9240_REG_12V_HIGH 0x33
+#define ADM9240_REG_12V_LOW 0x34
+#define ADM9240_REG_VCCP2_HIGH 0x35
+#define ADM9240_REG_VCCP2_LOW 0x36
+#define ADM9240_REG_TCRIT_LIMIT 0x37	/* LM81 only - not supported */
+#define ADM9240_REG_LOW_LIMIT 0x38	/* LM81 only - not supported */
+#define ADM9240_REG_TOS 0x39
+#define ADM9240_REG_THYST 0x3A
+#define ADM9240_REG_FAN1_MIN 0x3B
+#define ADM9240_REG_FAN2_MIN 0x3C
+
+#define ADM9240_REG_CONFIG 0x40
+#define ADM9240_REG_INT1_STAT 0x41
+#define ADM9240_REG_INT2_STAT 0x42
+#define ADM9240_REG_INT1_MASK 0x43
+#define ADM9240_REG_INT2_MASK 0x44
+
+#define ADM9240_REG_COMPAT 0x45	/* dummy compat. register for other drivers? */
+#define ADM9240_REG_CHASSIS_CLEAR 0x46
+#define ADM9240_REG_VID_FAN_DIV 0x47
+#define ADM9240_REG_I2C_ADDR 0x48
+#define ADM9240_REG_VID4 0x49
+#define ADM9240_REG_TEMP_CONFIG 0x4B
+#define ADM9240_REG_EXTMODE1 0x4C	/* LM81 only - not supported */
+#define ADM9240_REG_EXTMODE2 0x4D	/* LM81 only - not supported */
+
+/* Conversions. Rounding and limit checking is only done on the TO_REG
+   variants. Note that you should be a bit careful with which arguments
+   these macros are called: arguments may be evaluated more than once.
+   Fixing this is just not worth it. */
+#define IN_TO_REG(val,nr) (SENSORS_LIMIT(((val) & 0xff),0,255))
+#define IN_FROM_REG(val,nr) (val)
+
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+	if (rpm == 0)
+		return 255;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1,
+			     254);
+}
+
+#define FAN_FROM_REG(val,div) ((val)==0?-1:\
+                               (val)==255?0:1350000/((div)*(val)))
+
+#define TEMP_FROM_REG(temp) \
+   ((temp)<256?((((temp)&0x1fe) >> 1) * 10)      + ((temp) & 1) * 5:  \
+               ((((temp)&0x1fe) >> 1) -255) * 10 - ((temp) & 1) * 5)  \
+
+#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10)
+
+#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT(((val)<0?(((val)-5)/10):\
+                                                      ((val)+5)/10), \
+                                             0,255)
+
+#define ALARMS_FROM_REG(val) (val)
+
+#define DIV_FROM_REG(val) (1 << (val))
+#define DIV_TO_REG(val) ((val)==1?0:((val)==8?3:((val)==4?2:1)))
+
+#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\
+                           205-(val)*5)
+
+/* For each registered ADM9240, we need to keep some data in memory. That
+   data is pointed to by adm9240_list[NR]->data. The structure itself is
+   dynamically allocated, at the same time when a new adm9240 client is
+   allocated. */
+struct adm9240_data {
+	struct i2c_client client;
+	int sysctl_id;
+	enum chips type;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 in[6];		/* Register value */
+	u8 in_max[6];		/* Register value */
+	u8 in_min[6];		/* Register value */
+	u8 fan[2];		/* Register value */
+	u8 fan_min[2];		/* Register value */
+	u8 fan_div[2];		/* Register encoding, shifted right */
+	int temp;		/* Temp, shifted right */
+	u8 temp_os_max;		/* Register value */
+	u8 temp_os_hyst;	/* Register value */
+	u16 alarms;		/* Register encoding, combined */
+	u8 analog_out;		/* Register value */
+	u8 vid;			/* Register value combined */
+};
+
+
+static int adm9240_attach_adapter(struct i2c_adapter *adapter);
+static int adm9240_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int adm9240_detach_client(struct i2c_client *client);
+
+static int adm9240_read_value(struct i2c_client *client, u8 register);
+static int adm9240_write_value(struct i2c_client *client, u8 register,
+			       u8 value);
+static void adm9240_update_client(struct i2c_client *client);
+static void adm9240_init_client(struct i2c_client *client);
+
+
+static void adm9240_in(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void adm9240_fan(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm9240_temp(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void adm9240_alarms(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+static void adm9240_fan_div(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void adm9240_analog_out(struct i2c_client *client, int operation,
+			       int ctl_name, int *nrels_mag,
+			       long *results);
+static void adm9240_vid(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+
+static int adm9240_id = 0;
+
+static struct i2c_driver adm9240_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "ADM9240 sensor driver",
+	.id		= I2C_DRIVERID_ADM9240,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= adm9240_attach_adapter,
+	.detach_client	= adm9240_detach_client,
+};
+
+/* The /proc/sys entries */
+
+/* -- SENSORS SYSCTL START -- */
+
+#define ADM9240_SYSCTL_IN0 1000	/* Volts * 100 */
+#define ADM9240_SYSCTL_IN1 1001
+#define ADM9240_SYSCTL_IN2 1002
+#define ADM9240_SYSCTL_IN3 1003
+#define ADM9240_SYSCTL_IN4 1004
+#define ADM9240_SYSCTL_IN5 1005
+#define ADM9240_SYSCTL_FAN1 1101	/* Rotations/min */
+#define ADM9240_SYSCTL_FAN2 1102
+#define ADM9240_SYSCTL_TEMP 1250	/* Degrees Celcius * 100 */
+#define ADM9240_SYSCTL_FAN_DIV 2000	/* 1, 2, 4 or 8 */
+#define ADM9240_SYSCTL_ALARMS 2001	/* bitvector */
+#define ADM9240_SYSCTL_ANALOG_OUT 2002
+#define ADM9240_SYSCTL_VID 2003
+
+#define ADM9240_ALARM_IN0 0x0001
+#define ADM9240_ALARM_IN1 0x0002
+#define ADM9240_ALARM_IN2 0x0004
+#define ADM9240_ALARM_IN3 0x0008
+#define ADM9240_ALARM_IN4 0x0100
+#define ADM9240_ALARM_IN5 0x0200
+#define ADM9240_ALARM_FAN1 0x0040
+#define ADM9240_ALARM_FAN2 0x0080
+#define ADM9240_ALARM_TEMP 0x0010
+#define ADM9240_ALARM_CHAS 0x1000
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected ADM9240. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized 
+   when a new copy is allocated. */
+static ctl_table adm9240_dir_table_template[] = {
+	{ADM9240_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm9240_in},
+	{ADM9240_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm9240_in},
+	{ADM9240_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm9240_in},
+	{ADM9240_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm9240_in},
+	{ADM9240_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm9240_in},
+	{ADM9240_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm9240_in},
+	{ADM9240_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm9240_fan},
+	{ADM9240_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm9240_fan},
+	{ADM9240_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm9240_temp},
+	{ADM9240_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm9240_fan_div},
+	{ADM9240_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm9240_alarms},
+	{ADM9240_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm9240_analog_out},
+	{ADM9240_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &adm9240_vid},
+	{0}
+};
+
+static int adm9240_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, adm9240_detect);
+}
+
+static int adm9240_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct adm9240_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+
+	/* Make sure we aren't probing the ISA bus!! This is just a safety check
+	   at this moment; i2c_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("adm9240.o: adm9240_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		goto ERROR0;
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access adm9240_{read,write}_value. */
+
+	if (!(data = kmalloc(sizeof(struct adm9240_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &adm9240_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+
+	if (kind < 0) {
+		if (
+		    ((adm9240_read_value
+		      (new_client, ADM9240_REG_CONFIG) & 0x80) != 0x00)
+		    ||
+		    (adm9240_read_value(new_client, ADM9240_REG_I2C_ADDR)
+		     != address))
+			goto ERROR1;
+	}
+
+	/* Determine the chip type. */
+	if (kind <= 0) {
+		i = adm9240_read_value(new_client, ADM9240_REG_COMPANY_ID);
+		if (i == 0x23)
+			kind = adm9240;
+		else if (i == 0xda)
+			kind = ds1780;
+		else if (i == 0x01)
+			kind = lm81;
+		else {
+			if (kind == 0)
+				printk
+				    ("adm9240.o: Ignoring 'force' parameter for unknown chip at "
+				     "adapter %d, address 0x%02x\n",
+				     i2c_adapter_id(adapter), address);
+			goto ERROR1;
+		}
+	}
+
+	if (kind == adm9240) {
+		type_name = "adm9240";
+		client_name = "ADM9240 chip";
+	} else if (kind == ds1780) {
+		type_name = "ds1780";
+		client_name = "DS1780 chip";
+	} else if (kind == lm81) {
+		type_name = "lm81";
+		client_name = "LM81 chip";
+	} else {
+#ifdef DEBUG
+		printk("adm9240.o: Internal error: unknown kind (%d)?!?",
+		       kind);
+#endif
+		goto ERROR1;
+	}
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	data->type = kind;
+
+	new_client->id = adm9240_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client,
+					type_name,
+					adm9240_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the ADM9240 chip */
+	adm9240_init_client(new_client);
+	return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+      ERROR1:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int adm9240_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct adm9240_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("adm9240.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+
+static int adm9240_read_value(struct i2c_client *client, u8 reg)
+{
+	return 0xFF & i2c_smbus_read_byte_data(client, reg);
+}
+
+static int adm9240_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* Called when we have found a new ADM9240. It should set limits, etc. */
+static void adm9240_init_client(struct i2c_client *client)
+{
+	/* Start monitoring */
+	adm9240_write_value(client, ADM9240_REG_CONFIG, 0x01);
+}
+
+static void adm9240_update_client(struct i2c_client *client)
+{
+	struct adm9240_data *data = client->data;
+	u8 i;
+
+	down(&data->update_lock);
+
+	if (
+	    (jiffies - data->last_updated >
+	     (data->type == adm9240 ? HZ / 2 : HZ * 2))
+	    || (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting adm9240 update\n");
+#endif
+		for (i = 0; i <= 5; i++) {
+			data->in[i] =
+			    adm9240_read_value(client, ADM9240_REG_IN(i));
+			data->in_min[i] =
+			    adm9240_read_value(client,
+					       ADM9240_REG_IN_MIN(i));
+			data->in_max[i] =
+			    adm9240_read_value(client,
+					       ADM9240_REG_IN_MAX(i));
+		}
+		data->fan[0] =
+		    adm9240_read_value(client, ADM9240_REG_FAN1);
+		data->fan_min[0] =
+		    adm9240_read_value(client, ADM9240_REG_FAN1_MIN);
+		data->fan[1] =
+		    adm9240_read_value(client, ADM9240_REG_FAN2);
+		data->fan_min[1] =
+		    adm9240_read_value(client, ADM9240_REG_FAN2_MIN);
+		data->temp =
+		    (adm9240_read_value(client, ADM9240_REG_TEMP) << 1) +
+		    ((adm9240_read_value
+		      (client, ADM9240_REG_TEMP_CONFIG) & 0x80) >> 7);
+		data->temp_os_max =
+		    adm9240_read_value(client, ADM9240_REG_TOS);
+		data->temp_os_hyst =
+		    adm9240_read_value(client, ADM9240_REG_THYST);
+
+		i = adm9240_read_value(client, ADM9240_REG_VID_FAN_DIV);
+		data->fan_div[0] = (i >> 4) & 0x03;
+		data->fan_div[1] = (i >> 6) & 0x03;
+		data->vid = i & 0x0f;
+		data->vid |=
+		    (adm9240_read_value(client, ADM9240_REG_VID4) & 0x01)
+		    << 4;
+
+		data->alarms =
+		    adm9240_read_value(client,
+				       ADM9240_REG_INT1_STAT) +
+		    (adm9240_read_value(client, ADM9240_REG_INT2_STAT) <<
+		     8);
+		data->analog_out =
+		    adm9240_read_value(client, ADM9240_REG_ANALOG_OUT);
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+/* The next few functions are the call-back functions of the /proc/sys and
+   sysctl files. Which function is used is defined in the ctl_table in
+   the extra1 field.
+   Each function must return the magnitude (power of 10 to divide the date
+   with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must
+   put a maximum of *nrels elements in results reflecting the data of this
+   file, and set *nrels to the number it actually put in it, if operation==
+   SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from
+   results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE.
+   Note that on SENSORS_PROC_REAL_READ, I do not check whether results is
+   large enough (by checking the incoming value of *nrels). This is not very
+   good practice, but as long as you put less than about 5 values in results,
+   you can assume it is large enough. */
+void adm9240_in(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+
+	int scales[6] = { 250, 270, 330, 500, 1200, 270 };
+
+	struct adm9240_data *data = client->data;
+	int nr = ctl_name - ADM9240_SYSCTL_IN0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm9240_update_client(client);
+		results[0] =
+		    IN_FROM_REG(data->in_min[nr], nr) * scales[nr] / 192;
+		results[1] =
+		    IN_FROM_REG(data->in_max[nr], nr) * scales[nr] / 192;
+		results[2] =
+		    IN_FROM_REG(data->in[nr], nr) * scales[nr] / 192;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->in_min[nr] =
+			    IN_TO_REG((results[0] * 192) / scales[nr], nr);
+			adm9240_write_value(client, ADM9240_REG_IN_MIN(nr),
+					    data->in_min[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->in_max[nr] =
+			    IN_TO_REG((results[1] * 192) / scales[nr], nr);
+			adm9240_write_value(client, ADM9240_REG_IN_MAX(nr),
+					    data->in_max[nr]);
+		}
+	}
+}
+
+void adm9240_fan(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct adm9240_data *data = client->data;
+	int nr = ctl_name - ADM9240_SYSCTL_FAN1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm9240_update_client(client);
+		results[0] = FAN_FROM_REG(data->fan_min[nr - 1],
+					  DIV_FROM_REG(data->
+						       fan_div[nr - 1]));
+		results[1] =
+		    FAN_FROM_REG(data->fan[nr - 1],
+				 DIV_FROM_REG(data->fan_div[nr - 1]));
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr - 1] = FAN_TO_REG(results[0],
+							   DIV_FROM_REG
+							   (data->
+							    fan_div[nr -
+								    1]));
+			adm9240_write_value(client,
+					    nr ==
+					    1 ? ADM9240_REG_FAN1_MIN :
+					    ADM9240_REG_FAN2_MIN,
+					    data->fan_min[nr - 1]);
+		}
+	}
+}
+
+
+void adm9240_temp(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct adm9240_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm9240_update_client(client);
+		results[0] = TEMP_LIMIT_FROM_REG(data->temp_os_max);
+		results[1] = TEMP_LIMIT_FROM_REG(data->temp_os_hyst);
+		results[2] = TEMP_FROM_REG(data->temp);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_os_max = TEMP_LIMIT_TO_REG(results[0]);
+			adm9240_write_value(client, ADM9240_REG_TOS,
+					    data->temp_os_max);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_os_hyst = TEMP_LIMIT_TO_REG(results[1]);
+			adm9240_write_value(client, ADM9240_REG_THYST,
+					    data->temp_os_hyst);
+		}
+	}
+}
+
+void adm9240_alarms(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results)
+{
+	struct adm9240_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm9240_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->alarms);
+		*nrels_mag = 1;
+	}
+}
+
+void adm9240_fan_div(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	struct adm9240_data *data = client->data;
+	int old;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm9240_update_client(client);
+		results[0] = DIV_FROM_REG(data->fan_div[0]);
+		results[1] = DIV_FROM_REG(data->fan_div[1]);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		old = adm9240_read_value(client, ADM9240_REG_VID_FAN_DIV);
+		if (*nrels_mag >= 2) {
+			data->fan_div[1] = DIV_TO_REG(results[1]);
+			old = (old & 0x3f) | (data->fan_div[1] << 6);
+		}
+		if (*nrels_mag >= 1) {
+			data->fan_div[0] = DIV_TO_REG(results[0]);
+			old = (old & 0xcf) | (data->fan_div[0] << 4);
+			adm9240_write_value(client,
+					    ADM9240_REG_VID_FAN_DIV, old);
+		}
+	}
+}
+
+void adm9240_analog_out(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results)
+{
+	struct adm9240_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm9240_update_client(client);
+		results[0] = data->analog_out;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->analog_out = results[0];
+			adm9240_write_value(client, ADM9240_REG_ANALOG_OUT,
+					    data->analog_out);
+		}
+	}
+}
+
+void adm9240_vid(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct adm9240_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		adm9240_update_client(client);
+		results[0] = VID_FROM_REG(data->vid);
+		*nrels_mag = 1;
+	}
+}
+
+static int __init sm_adm9240_init(void)
+{
+	printk("adm9240.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&adm9240_driver);
+}
+
+static void __exit sm_adm9240_exit(void)
+{
+	i2c_del_driver(&adm9240_driver);
+}
+
+
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("ADM9240 driver");
+
+module_init(sm_adm9240_init);
+module_exit(sm_adm9240_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/asb100.c linux-2.4.27-leo/drivers/sensors/asb100.c
--- linux-2.4.27/drivers/sensors/asb100.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/asb100.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,1025 @@
+/*
+    asb100.c - Part of lm_sensors, Linux kernel modules for hardware
+                monitoring
+
+    Copyright (c) 2003 Mark M. Hoffman <mhoffman@lightlink.com>
+
+	(derived from w83781d.c)
+
+    Copyright (c) 1998 - 2003  Frodo Looijaard <frodol@dds.nl>,
+    Philip Edelbrock <phil@netroedge.com>, and
+    Mark Studebaker <mdsxyz123@yahoo.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+    This driver supports the hardware sensor chips: Asus ASB100 and
+    ASB100-A "BACH".
+
+    ASB100-A supports pwm1, while plain ASB100 does not.  There is no known
+    way for the driver to tell which one is there.
+
+    Chip	#vin	#fanin	#pwm	#temp	wchipid	vendid	i2c	ISA
+    asb100	7	3	1	4	0x31	0x0694	yes	no
+*/
+
+//#define DEBUG 1
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+#include <linux/sensors_vid.h>
+#include "lm75.h"
+
+#ifndef I2C_DRIVERID_ASB100
+#define I2C_DRIVERID_ASB100		1043
+#endif
+
+/* I2C addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x28, 0x2f, SENSORS_I2C_END };
+
+/* ISA addresses to scan (none) */
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* default VRM to 9.0 instead of 8.2 */
+#define ASB100_DEFAULT_VRM 90
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(asb100);
+SENSORS_MODULE_PARM(force_subclients, "List of subclient addresses: " \
+	"{bus, clientaddr, subclientaddr1, subclientaddr2}");
+
+/* Voltage IN registers 0-6 */
+#define ASB100_REG_IN(nr)     (0x20 + (nr))
+#define ASB100_REG_IN_MAX(nr) (0x2b + (nr * 2))
+#define ASB100_REG_IN_MIN(nr) (0x2c + (nr * 2))
+
+/* FAN IN registers 1-3 */
+#define ASB100_REG_FAN(nr)     (0x27 + (nr))
+#define ASB100_REG_FAN_MIN(nr) (0x3a + (nr))
+
+/* TEMPERATURE registers 1-4 */
+static const u16 asb100_reg_temp[]	= {0, 0x27, 0x150, 0x250, 0x17};
+static const u16 asb100_reg_temp_max[]	= {0, 0x39, 0x155, 0x255, 0x18};
+static const u16 asb100_reg_temp_hyst[]	= {0, 0x3a, 0x153, 0x253, 0x19};
+
+#define ASB100_REG_TEMP(nr) (asb100_reg_temp[nr])
+#define ASB100_REG_TEMP_MAX(nr) (asb100_reg_temp_max[nr])
+#define ASB100_REG_TEMP_HYST(nr) (asb100_reg_temp_hyst[nr])
+
+#define ASB100_REG_TEMP2_CONFIG	0x0152
+#define ASB100_REG_TEMP3_CONFIG	0x0252
+
+
+#define ASB100_REG_CONFIG	0x40
+#define ASB100_REG_ALARM1	0x41
+#define ASB100_REG_ALARM2	0x42
+#define ASB100_REG_SMIM1	0x43
+#define ASB100_REG_SMIM2	0x44
+#define ASB100_REG_VID_FANDIV	0x47
+#define ASB100_REG_I2C_ADDR	0x48
+#define ASB100_REG_CHIPID	0x49
+#define ASB100_REG_I2C_SUBADDR	0x4a
+#define ASB100_REG_PIN		0x4b
+#define ASB100_REG_IRQ		0x4c
+#define ASB100_REG_BANK		0x4e
+#define ASB100_REG_CHIPMAN	0x4f
+
+#define ASB100_REG_WCHIPID	0x58
+
+/* bit 7 -> enable, bits 0-3 -> duty cycle */
+#define ASB100_REG_PWM1		0x59
+
+/* CONVERSIONS
+   Rounding and limit checking is only done on the TO_REG variants. */
+
+/* These constants are a guess, consistent w/ w83781d */
+#define ASB100_IN_MIN (  0)
+#define ASB100_IN_MAX (408)
+
+/* IN: 1/100 V (0V to 4.08V)
+   REG: 16mV/bit */
+static u8 IN_TO_REG(unsigned val)
+{
+	unsigned nval = SENSORS_LIMIT(val, ASB100_IN_MIN, ASB100_IN_MAX);
+	return (nval * 10 + 8) / 16;
+}
+
+static unsigned IN_FROM_REG(u8 reg)
+{
+	return (reg * 16 + 5) / 10;
+}
+
+static u8 FAN_TO_REG(long rpm, int div)
+{
+	if (rpm == 0)
+		return 255;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
+}
+
+static int FAN_FROM_REG(u8 val, int div)
+{
+	return val==0 ? -1 : val==255 ? 0 : 1350000/(val*div);
+}
+
+/* These constants are a guess, consistent w/ w83781d */
+#define ASB100_TEMP_MIN (-1280)
+#define ASB100_TEMP_MAX ( 1270)
+
+/* TEMP: 1/10 degrees C (-128C to +127C)
+   REG: 1C/bit, two's complement */
+static u8 TEMP_TO_REG(int temp)
+{
+	int ntemp = SENSORS_LIMIT(temp, ASB100_TEMP_MIN, ASB100_TEMP_MAX);
+	ntemp += (ntemp<0 ? -5 : 5);
+	return (u8)(ntemp / 10);
+}
+
+static int TEMP_FROM_REG(u8 reg)
+{
+	return (s8)reg * 10;
+}
+
+/* PWM: 0 - 255 per sensors documentation
+   REG: (6.25% duty cycle per bit) */
+static u8 ASB100_PWM_TO_REG(int pwm)
+{
+	pwm = SENSORS_LIMIT(pwm, 0, 255);
+	return (u8)(pwm / 16);
+}
+
+static int ASB100_PWM_FROM_REG(u8 reg)
+{
+	return reg * 16;
+}
+
+#define ALARMS_FROM_REG(val) (val)
+
+#define DIV_FROM_REG(val) (1 << (val))
+
+/* FAN DIV: 1, 2, 4, or 8 (defaults to 2)
+   REG: 0, 1, 2, or 3 (respectively) (defaults to 1) */
+static u8 DIV_TO_REG(long val)
+{
+	return val==8 ? 3 : val==4 ? 2 : val==1 ? 0 : 1;
+}
+
+/* For each registered client, we need to keep some data in memory. That
+   data is pointed to by client->data. The structure itself is
+   dynamically allocated, at the same time the client itself is allocated. */
+struct asb100_data {
+	struct i2c_client client;
+	struct semaphore lock;
+	int sysctl_id;
+	enum chips type;
+
+	struct semaphore update_lock;
+	unsigned long last_updated;	/* In jiffies */
+
+	/* array of 2 pointers to subclients */
+	struct i2c_client *lm75[2];
+
+	char valid;		/* !=0 if following fields are valid */
+	u8 in[7];		/* Register value */
+	u8 in_max[7];		/* Register value */
+	u8 in_min[7];		/* Register value */
+	u8 fan[3];		/* Register value */
+	u8 fan_min[3];		/* Register value */
+	u16 temp[4];		/* Register value (0 and 3 are u8 only) */
+	u16 temp_max[4];	/* Register value (0 and 3 are u8 only) */
+	u16 temp_hyst[4];	/* Register value (0 and 3 are u8 only) */
+	u8 fan_div[3];		/* Register encoding, right justified */
+	u8 pwm;			/* Register encoding */
+	u8 vid;			/* Register encoding, combined */
+	u32 alarms;		/* Register encoding, combined */
+	u8 vrm;
+};
+
+static int asb100_attach_adapter(struct i2c_adapter *adapter);
+static int asb100_detect(struct i2c_adapter *adapter, int address,
+		unsigned short flags, int kind);
+static int asb100_detach_client(struct i2c_client *client);
+
+static int asb100_read_value(struct i2c_client *client, u16 reg);
+static void asb100_write_value(struct i2c_client *client, u16 reg, u16 val);
+static void asb100_update_client(struct i2c_client *client);
+static void asb100_init_client(struct i2c_client *client);
+
+static void asb100_in(struct i2c_client *client, int operation,
+		int ctl_name, int *nrels_mag, long *results);
+static void asb100_fan(struct i2c_client *client, int operation,
+		int ctl_name, int *nrels_mag, long *results);
+static void asb100_temp(struct i2c_client *client, int operation,
+		int ctl_name, int *nrels_mag, long *results);
+static void asb100_temp_add(struct i2c_client *client, int operation,
+		int ctl_name, int *nrels_mag, long *results);
+static void asb100_vid(struct i2c_client *client, int operation,
+		int ctl_name, int *nrels_mag, long *results);
+static void asb100_vrm(struct i2c_client *client, int operation,
+		int ctl_name, int *nrels_mag, long *results);
+static void asb100_alarms(struct i2c_client *client, int operation,
+		int ctl_name, int *nrels_mag, long *results);
+static void asb100_fan_div(struct i2c_client *client, int operation,
+		int ctl_name, int *nrels_mag, long *results);
+static void asb100_pwm(struct i2c_client *client, int operation,
+		int ctl_name, int *nrels_mag, long *results);
+
+static struct i2c_driver asb100_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "asb100",
+	.id		= I2C_DRIVERID_ASB100,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= asb100_attach_adapter,
+	.detach_client	= asb100_detach_client,
+};
+
+/* The /proc/sys entries */
+/* -- SENSORS SYSCTL START -- */
+
+#define ASB100_SYSCTL_IN0	1000	/* Volts * 100 */
+#define ASB100_SYSCTL_IN1	1001
+#define ASB100_SYSCTL_IN2	1002
+#define ASB100_SYSCTL_IN3	1003
+#define ASB100_SYSCTL_IN4	1004
+#define ASB100_SYSCTL_IN5	1005
+#define ASB100_SYSCTL_IN6	1006
+
+#define ASB100_SYSCTL_FAN1	1101	/* Rotations/min */
+#define ASB100_SYSCTL_FAN2	1102
+#define ASB100_SYSCTL_FAN3	1103
+
+#define ASB100_SYSCTL_TEMP1	1200	/* Degrees Celcius * 10 */
+#define ASB100_SYSCTL_TEMP2	1201
+#define ASB100_SYSCTL_TEMP3	1202
+#define ASB100_SYSCTL_TEMP4	1203
+
+#define ASB100_SYSCTL_VID	1300	/* Volts * 1000 */
+#define ASB100_SYSCTL_VRM	1301
+
+#define ASB100_SYSCTL_PWM1	1401	/* 0-255 => 0-100% duty cycle */
+
+#define ASB100_SYSCTL_FAN_DIV	2000	/* 1, 2, 4 or 8 */
+#define ASB100_SYSCTL_ALARMS	2001	/* bitvector */
+
+#define ASB100_ALARM_IN0	0x0001	/* ? */
+#define ASB100_ALARM_IN1	0x0002	/* ? */
+#define ASB100_ALARM_IN2	0x0004
+#define ASB100_ALARM_IN3	0x0008
+#define ASB100_ALARM_TEMP1	0x0010
+#define ASB100_ALARM_TEMP2	0x0020
+#define ASB100_ALARM_FAN1	0x0040
+#define ASB100_ALARM_FAN2	0x0080
+#define ASB100_ALARM_IN4	0x0100
+#define ASB100_ALARM_IN5	0x0200	/* ? */
+#define ASB100_ALARM_IN6	0x0400	/* ? */
+#define ASB100_ALARM_FAN3	0x0800
+#define ASB100_ALARM_CHAS	0x1000
+#define ASB100_ALARM_TEMP3	0x2000
+
+#define ASB100_ALARM_IN7	0x10000 /* ? */
+#define ASB100_ALARM_IN8	0x20000	/* ? */
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected chip. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized 
+   when a new copy is allocated. */
+
+/* no datasheet - but we did get some hints from someone who 
+   claimed to have the datasheet */
+#define ASB100_SYSCTL_IN(nr) {ASB100_SYSCTL_IN##nr, "in" #nr, NULL, 0, \
+	0644, NULL, &i2c_proc_real, &i2c_sysctl_real, NULL, &asb100_in}
+#define ASB100_SYSCTL_FAN(nr) {ASB100_SYSCTL_FAN##nr, "fan" #nr, NULL, 0, \
+	0644, NULL, &i2c_proc_real, &i2c_sysctl_real, NULL, &asb100_fan}
+#define ASB100_SYSCTL_TEMP(nr, func) {ASB100_SYSCTL_TEMP##nr, "temp" #nr, \
+	NULL, 0, 0644, NULL, &i2c_proc_real, &i2c_sysctl_real, NULL, func}
+static ctl_table asb100_dir_table_template[] = {
+	ASB100_SYSCTL_IN(0),
+	ASB100_SYSCTL_IN(1),
+	ASB100_SYSCTL_IN(2),
+	ASB100_SYSCTL_IN(3),
+	ASB100_SYSCTL_IN(4),
+	ASB100_SYSCTL_IN(5),
+	ASB100_SYSCTL_IN(6),
+
+	ASB100_SYSCTL_FAN(1),
+	ASB100_SYSCTL_FAN(2),
+	ASB100_SYSCTL_FAN(3),
+
+	ASB100_SYSCTL_TEMP(1, &asb100_temp),
+	ASB100_SYSCTL_TEMP(2, &asb100_temp_add),
+	ASB100_SYSCTL_TEMP(3, &asb100_temp_add),
+	ASB100_SYSCTL_TEMP(4, &asb100_temp),
+
+	{ASB100_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &asb100_vid},
+	{ASB100_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &asb100_vrm},
+	{ASB100_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &asb100_fan_div},
+	{ASB100_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &asb100_alarms},
+	{ASB100_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &asb100_pwm},
+	{0}
+};
+
+/* This function is called when:
+	asb100_driver is inserted (when this module is loaded), for each
+		available adapter
+	when a new adapter is inserted (and asb100_driver is still present)
+ */
+static int asb100_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, asb100_detect);
+}
+
+static int asb100_detect_subclients(struct i2c_adapter *adapter, int address,
+		int kind, struct i2c_client *new_client)
+{
+	int i, id, err;
+	struct asb100_data *data = new_client->data;
+
+	data->lm75[0] = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+	if (!(data->lm75[0])) {
+		err = -ENOMEM;
+		goto ERROR_SC_0;
+	}
+	memset(data->lm75[0], 0x00, sizeof(struct i2c_client));
+
+	data->lm75[1] = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+	if (!(data->lm75[1])) {
+		err = -ENOMEM;
+		goto ERROR_SC_1;
+	}
+	memset(data->lm75[1], 0x00, sizeof(struct i2c_client));
+
+	id = i2c_adapter_id(adapter);
+
+	if (force_subclients[0] == id && force_subclients[1] == address) {
+		for (i = 2; i <= 3; i++) {
+			if (force_subclients[i] < 0x48 ||
+				force_subclients[i] > 0x4f) {
+				printk(KERN_ERR "asb100.o: invalid subclient "
+					"address %d; must be 0x48-0x4f\n",
+			        	force_subclients[i]);
+				err = -ENODEV;
+				goto ERROR_SC_2;
+			}
+		}
+		asb100_write_value(new_client, ASB100_REG_I2C_SUBADDR,
+		                    (force_subclients[2] & 0x07) |
+		                    ((force_subclients[3] & 0x07) <<4));
+		data->lm75[0]->addr = force_subclients[2];
+		data->lm75[1]->addr = force_subclients[3];
+	} else {
+		int val = asb100_read_value(new_client, ASB100_REG_I2C_SUBADDR);
+		data->lm75[0]->addr = 0x48 + (val & 0x07);
+		data->lm75[1]->addr = 0x48 + ((val >> 4) & 0x07);
+	}
+
+	if(data->lm75[0]->addr == data->lm75[1]->addr) {
+		printk(KERN_ERR "asb100.o: duplicate addresses 0x%x "
+				"for subclients\n", data->lm75[0]->addr);
+		err = -ENODEV;
+		goto ERROR_SC_2;
+	}
+
+	for (i = 0; i <= 1; i++) {
+		data->lm75[i]->data = NULL;
+		data->lm75[i]->adapter = adapter;
+		data->lm75[i]->driver = &asb100_driver;
+		data->lm75[i]->flags = 0;
+		strcpy(data->lm75[i]->name, "asb100 subclient");
+	}
+
+	if ((err = i2c_attach_client(data->lm75[0]))) {
+		printk(KERN_ERR "asb100.o: Subclient %d registration "
+			"at address 0x%x failed.\n", i, data->lm75[0]->addr);
+		goto ERROR_SC_2;
+	}
+
+	if ((err = i2c_attach_client(data->lm75[1]))) {
+		printk(KERN_ERR "asb100.o: Subclient %d registration "
+			"at address 0x%x failed.\n", i, data->lm75[1]->addr);
+		goto ERROR_SC_3;
+	}
+
+	return 0;
+
+/* Undo inits in case of errors */
+ERROR_SC_3:
+	i2c_detach_client(data->lm75[0]);
+ERROR_SC_2:
+	kfree(data->lm75[1]);
+ERROR_SC_1:
+	kfree(data->lm75[0]);
+ERROR_SC_0:
+	return err;
+}
+
+static int asb100_detect(struct i2c_adapter *adapter, int address,
+		unsigned short flags, int kind)
+{
+	int err;
+	struct i2c_client *new_client;
+	struct asb100_data *data;
+
+	/* asb100 is SMBus only */
+	if (i2c_is_isa_adapter(adapter)) {
+		pr_debug("asb100.o: detect failed, "
+				"cannot attach to legacy adapter!\n");
+		err = -ENODEV;
+		goto ERROR0;
+	}
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+		pr_debug("asb100.o: detect failed, "
+				"smbus byte data not supported!\n");
+		err = -ENODEV;
+		goto ERROR0;
+	}
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access asb100_{read,write}_value. */
+
+	if (!(data = kmalloc(sizeof(struct asb100_data), GFP_KERNEL))) {
+		pr_debug("asb100.o: detect failed, kmalloc failed!\n");
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	init_MUTEX(&data->lock);
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &asb100_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+
+	/* The chip may be stuck in some other bank than bank 0. This may
+	   make reading other information impossible. Specify a force=... or
+	   force_*=... parameter, and the chip will be reset to the right
+	   bank. */
+	if (kind < 0) {
+
+		int val1 = asb100_read_value(new_client, ASB100_REG_BANK);
+		int val2 = asb100_read_value(new_client, ASB100_REG_CHIPMAN);
+
+		/* If we're in bank 0 */
+		if ( (!(val1 & 0x07)) &&
+				/* Check for ASB100 ID (low byte) */
+				( ((!(val1 & 0x80)) && (val2 != 0x94)) ||
+				/* Check for ASB100 ID (high byte ) */
+				((val1 & 0x80) && (val2 != 0x06)) ) ) {
+			pr_debug("asb100.o: detect failed, "
+					"bad chip id 0x%02x!\n", val2);
+			err = -ENODEV;
+			goto ERROR1;
+		}
+
+	} /* kind < 0 */
+
+	/* We have either had a force parameter, or we have already detected
+	   Winbond. Put it now into bank 0 and Vendor ID High Byte */
+	asb100_write_value(new_client, ASB100_REG_BANK,
+		(asb100_read_value(new_client, ASB100_REG_BANK) & 0x78) | 0x80);
+
+	/* Determine the chip type. */
+	if (kind <= 0) {
+		int val1 = asb100_read_value(new_client, ASB100_REG_WCHIPID);
+		int val2 = asb100_read_value(new_client, ASB100_REG_CHIPMAN);
+
+		if ((val1 == 0x31) && (val2 == 0x06))
+			kind = asb100;
+		else {
+			if (kind == 0)
+				printk (KERN_WARNING "asb100.o: Ignoring "
+					"'force' parameter for unknown chip "
+					"at adapter %d, address 0x%02x.\n",
+					i2c_adapter_id(adapter), address);
+			err = -ENODEV;
+			goto ERROR1;
+		}
+	}
+
+	/* Fill in remaining client fields and put it into the global list */
+	strcpy(new_client->name, "ASB100 chip");
+	data->type = kind;
+
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR1;
+
+	/* Attach secondary lm75 clients */
+	if ((err = asb100_detect_subclients(adapter, address, kind,
+			new_client)))
+		goto ERROR2;
+
+	/* Initialize the chip */
+	asb100_init_client(new_client);
+
+	/* Register a new directory entry with module sensors */
+	if ((data->sysctl_id = i2c_register_entry(new_client, "asb100",
+			asb100_dir_table_template)) < 0) {
+		err = data->sysctl_id;
+		goto ERROR3;
+	}
+
+	return 0;
+
+ERROR3:
+	i2c_detach_client(data->lm75[0]);
+	kfree(data->lm75[1]);
+	kfree(data->lm75[0]);
+ERROR2:
+	i2c_detach_client(new_client);
+ERROR1:
+	kfree(data);
+ERROR0:
+	return err;
+}
+
+static int asb100_detach_client(struct i2c_client *client)
+{
+	int err;
+	struct asb100_data *data = client->data;
+
+	/* remove sysctl table (primary client only) */
+	if ((data))
+		i2c_deregister_entry(data->sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk (KERN_ERR "asb100.o: Client deregistration failed; "
+			"client not detached.\n");
+		return err;
+	}
+
+	if (data) {
+		/* primary client */
+		kfree(data);
+	} else {
+		/* subclients */
+		kfree(client);
+	}
+
+	return 0;
+}
+
+/* The SMBus locks itself, usually, but nothing may access the Winbond between
+   bank switches. ISA access must always be locked explicitly! 
+   We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks,
+   would slow down the W83781D access and should not be necessary. 
+   There are some ugly typecasts here, but the good news is - they should
+   nowhere else be necessary! */
+static int asb100_read_value(struct i2c_client *client, u16 reg)
+{
+	struct asb100_data *data = client->data;
+	struct i2c_client *cl;
+	int res, bank;
+
+	down(&data->lock);
+
+	bank = (reg >> 8) & 0x0f;
+	if (bank > 2)
+		/* switch banks */
+		i2c_smbus_write_byte_data(client, ASB100_REG_BANK, bank);
+
+	if (bank == 0 || bank > 2) {
+		res = i2c_smbus_read_byte_data(client, reg & 0xff);
+	} else {
+		/* switch to subclient */
+		cl = data->lm75[bank - 1];
+
+		/* convert from ISA to LM75 I2C addresses */
+		switch (reg & 0xff) {
+		case 0x50: /* TEMP */
+			res = swab16(i2c_smbus_read_word_data (cl, 0));
+			break;
+		case 0x52: /* CONFIG */
+			res = i2c_smbus_read_byte_data(cl, 1);
+			break;
+		case 0x53: /* HYST */
+			res = swab16(i2c_smbus_read_word_data (cl, 2));
+			break;
+		case 0x55: /* MAX */
+		default:
+			res = swab16(i2c_smbus_read_word_data (cl, 3));
+			break;
+		}
+	}
+
+	if (bank > 2)
+		i2c_smbus_write_byte_data(client, ASB100_REG_BANK, 0);
+
+	up(&data->lock);
+
+	return res;
+}
+
+static void asb100_write_value(struct i2c_client *client, u16 reg, u16 value)
+{
+	struct asb100_data *data = client->data;
+	struct i2c_client *cl;
+	int bank;
+
+	down(&data->lock);
+
+	bank = (reg >> 8) & 0x0f;
+	if (bank > 2)
+		/* switch banks */
+		i2c_smbus_write_byte_data(client, ASB100_REG_BANK, bank);
+
+	if (bank == 0 || bank > 2) {
+		i2c_smbus_write_byte_data(client, reg & 0xff, value & 0xff);
+	} else {
+		/* switch to subclient */
+		cl = data->lm75[bank - 1];
+
+		/* convert from ISA to LM75 I2C addresses */
+		switch (reg & 0xff) {
+		case 0x52: /* CONFIG */
+			i2c_smbus_write_byte_data(cl, 1, value & 0xff);
+			break;
+		case 0x53: /* HYST */
+			i2c_smbus_write_word_data(cl, 2, swab16(value));
+			break;
+		case 0x55: /* MAX */
+			i2c_smbus_write_word_data(cl, 3, swab16(value));
+			break;
+		}
+	}
+
+	if (bank > 2)
+		i2c_smbus_write_byte_data(client, ASB100_REG_BANK, 0);
+
+	up(&data->lock);
+}
+
+static void asb100_init_client(struct i2c_client *client)
+{
+	struct asb100_data *data = client->data;
+	int vid = 0;
+
+	vid = asb100_read_value(client, ASB100_REG_VID_FANDIV) & 0x0f;
+	vid |= (asb100_read_value(client, ASB100_REG_CHIPID) & 0x01) << 4;
+	data->vrm = ASB100_DEFAULT_VRM;
+	vid = vid_from_reg(vid, data->vrm);
+
+	/* Start monitoring */
+	asb100_write_value(client, ASB100_REG_CONFIG, 
+		(asb100_read_value(client, ASB100_REG_CONFIG) & 0xf7) | 0x01);
+}
+
+static void asb100_update_client(struct i2c_client *client)
+{
+	struct asb100_data *data = client->data;
+	int i;
+
+	down(&data->update_lock);
+
+	if (time_after(jiffies - data->last_updated, HZ + HZ / 2) ||
+		time_before(jiffies, data->last_updated) || !data->valid) {
+
+		pr_debug("asb100.o: starting device update...\n");
+
+		/* 7 voltage inputs */
+		for (i = 0; i < 7; i++) {
+			data->in[i] = asb100_read_value(client,
+				ASB100_REG_IN(i));
+			data->in_min[i] = asb100_read_value(client,
+				ASB100_REG_IN_MIN(i));
+			data->in_max[i] = asb100_read_value(client,
+				ASB100_REG_IN_MAX(i));
+		}
+
+		/* 3 fan inputs */
+		for (i = 1; i <= 3; i++) {
+			data->fan[i-1] = asb100_read_value(client,
+					ASB100_REG_FAN(i));
+			data->fan_min[i-1] = asb100_read_value(client,
+					ASB100_REG_FAN_MIN(i));
+		}
+
+		/* 4 temperature inputs */
+		for (i = 1; i <= 4; i++) {
+			data->temp[i-1] = asb100_read_value(client,
+					ASB100_REG_TEMP(i));
+			data->temp_max[i-1] = asb100_read_value(client,
+					ASB100_REG_TEMP_MAX(i));
+			data->temp_hyst[i-1] = asb100_read_value(client,
+					ASB100_REG_TEMP_HYST(i));
+		}
+
+		/* VID and fan divisors */
+		i = asb100_read_value(client, ASB100_REG_VID_FANDIV);
+		data->vid = i & 0x0f;
+		data->vid |= (asb100_read_value(client,
+				ASB100_REG_CHIPID) & 0x01) << 4;
+		data->fan_div[0] = (i >> 4) & 0x03;
+		data->fan_div[1] = (i >> 6) & 0x03;
+		data->fan_div[2] = (asb100_read_value(client,
+				ASB100_REG_PIN) >> 6) & 0x03;
+
+		/* PWM */
+		data->pwm = asb100_read_value(client, ASB100_REG_PWM1);
+
+		/* alarms */
+		data->alarms = asb100_read_value(client, ASB100_REG_ALARM1) +
+			(asb100_read_value(client, ASB100_REG_ALARM2) << 8);
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+
+		pr_debug("asb100.o: ... update complete.\n");
+	}
+
+	up(&data->update_lock);
+}
+
+
+/* The next few functions are the call-back functions of the /proc/sys and
+   sysctl files. Which function is used is defined in the ctl_table in
+   the extra1 field.
+   Each function must return the magnitude (power of 10 to divide the date
+   with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must
+   put a maximum of *nrels elements in results reflecting the data of this
+   file, and set *nrels to the number it actually put in it, if operation==
+   SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from
+   results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE.
+   Note that on SENSORS_PROC_REAL_READ, I do not check whether results is
+   large enough (by checking the incoming value of *nrels). This is not very
+   good practice, but as long as you put less than about 5 values in results,
+   you can assume it is large enough. */
+static void asb100_in(struct i2c_client *client, int operation, int ctl_name,
+               int *nrels_mag, long *results)
+{
+	struct asb100_data *data = client->data;
+	int nr = ctl_name - ASB100_SYSCTL_IN0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		asb100_update_client(client);
+		results[0] = IN_FROM_REG(data->in_min[nr]);
+		results[1] = IN_FROM_REG(data->in_max[nr]);
+		results[2] = IN_FROM_REG(data->in[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->in_min[nr] = IN_TO_REG(results[0]);
+			asb100_write_value(client, ASB100_REG_IN_MIN(nr),
+					    data->in_min[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->in_max[nr] = IN_TO_REG(results[1]);
+			asb100_write_value(client, ASB100_REG_IN_MAX(nr),
+					    data->in_max[nr]);
+		}
+	}
+}
+
+void asb100_fan(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct asb100_data *data = client->data;
+	int nr = ctl_name - ASB100_SYSCTL_FAN1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		asb100_update_client(client);
+		results[0] = FAN_FROM_REG(data->fan_min[nr - 1],
+				  DIV_FROM_REG(data->fan_div[nr - 1]));
+		results[1] = FAN_FROM_REG(data->fan[nr - 1],
+			          DIV_FROM_REG(data->fan_div[nr - 1]));
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr - 1] =
+			     FAN_TO_REG(results[0],
+			            DIV_FROM_REG(data->fan_div[nr-1]));
+			asb100_write_value(client,
+					    ASB100_REG_FAN_MIN(nr),
+					    data->fan_min[nr - 1]);
+		}
+	}
+}
+
+void asb100_temp(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct asb100_data *data = client->data;
+	int nr = ctl_name - ASB100_SYSCTL_TEMP1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		asb100_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_max[nr]);
+		results[1] = TEMP_FROM_REG(data->temp_hyst[nr]);
+		results[2] = TEMP_FROM_REG(data->temp[nr]);
+		*nrels_mag = 3;
+
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_max[nr] = TEMP_TO_REG(results[0]);
+			asb100_write_value(client, ASB100_REG_TEMP_MAX(nr+1),
+				data->temp_max[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_hyst[nr] = TEMP_TO_REG(results[1]);
+			asb100_write_value(client, ASB100_REG_TEMP_HYST(nr+1),
+				data->temp_hyst[nr]);
+		}
+	}
+}
+
+void asb100_temp_add(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results)
+{
+	struct asb100_data *data = client->data;
+	int nr = ctl_name - ASB100_SYSCTL_TEMP1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		asb100_update_client(client);
+
+		results[0] = LM75_TEMP_FROM_REG(data->temp_max[nr]);
+		results[1] = LM75_TEMP_FROM_REG(data->temp_hyst[nr]);
+		results[2] = LM75_TEMP_FROM_REG(data->temp[nr]);
+		*nrels_mag = 3;
+
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_max[nr] =
+				LM75_TEMP_TO_REG(results[0]);
+			asb100_write_value(client, ASB100_REG_TEMP_MAX(nr+1),
+				data->temp_max[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_hyst[nr] =
+				LM75_TEMP_TO_REG(results[1]);
+			asb100_write_value(client, ASB100_REG_TEMP_HYST(nr+1),
+				data->temp_hyst[nr]);
+		}
+	}
+}
+
+void asb100_vid(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct asb100_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 3;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		asb100_update_client(client);
+		results[0] = vid_from_reg(data->vid, data->vrm);
+		*nrels_mag = 1;
+	}
+}
+
+void asb100_vrm(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct asb100_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = data->vrm;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1)
+			data->vrm = results[0];
+	}
+}
+
+void asb100_alarms(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results)
+{
+	struct asb100_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		asb100_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->alarms);
+		*nrels_mag = 1;
+	}
+}
+
+void asb100_fan_div(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	struct asb100_data *data = client->data;
+	int old, old2;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		asb100_update_client(client);
+		results[0] = DIV_FROM_REG(data->fan_div[0]);
+		results[1] = DIV_FROM_REG(data->fan_div[1]);
+		results[2] = DIV_FROM_REG(data->fan_div[2]);
+		*nrels_mag = 3;
+
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		old = asb100_read_value(client, ASB100_REG_VID_FANDIV);
+		if (*nrels_mag >= 3) {
+			data->fan_div[2] = DIV_TO_REG(results[2]);
+			old2 = asb100_read_value(client, ASB100_REG_PIN);
+			old2 = (old2 & 0x3f) | ((data->fan_div[2] & 0x03) << 6);
+			asb100_write_value(client, ASB100_REG_PIN, old2);
+		}
+		if (*nrels_mag >= 2) {
+			data->fan_div[1] = DIV_TO_REG(results[1]);
+			old = (old & 0x3f) | ((data->fan_div[1] & 0x03) << 6);
+		}
+		if (*nrels_mag >= 1) {
+			data->fan_div[0] = DIV_TO_REG(results[0]);
+			old = (old & 0xcf) | ((data->fan_div[0] & 0x03) << 4);
+			asb100_write_value(client, ASB100_REG_VID_FANDIV, old);
+		}
+	}
+}
+
+void asb100_pwm(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct asb100_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		asb100_update_client(client);
+		results[0] = ASB100_PWM_FROM_REG(data->pwm & 0x0f);
+		results[1] = (data->pwm & 0x80) ? 1 : 0;
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		u8 val = data->pwm;
+		if (*nrels_mag >= 1) {
+			val = 0x0f & ASB100_PWM_TO_REG(results[0]);
+			if (*nrels_mag >= 2) {
+				if (results[1])
+					val |= 0x80;
+				else
+					val &= ~0x80;
+			}
+			asb100_write_value(client, ASB100_REG_PWM1, val);
+		}
+	}
+}
+
+static int __init asb100_init(void)
+{
+	printk(KERN_INFO "asb100.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&asb100_driver);
+}
+
+static void __exit asb100_exit(void)
+{
+	i2c_del_driver(&asb100_driver);
+}
+
+MODULE_AUTHOR(	"Mark M. Hoffman <mhoffman@lightlink.com>, "
+		"Frodo Looijaard <frodol@dds.nl>, "
+		"Philip Edelbrock <phil@netroedge.com>, and"
+		"Mark Studebaker <mdsxyz123@yahoo.com>");
+
+MODULE_DESCRIPTION("ASB100 'Bach' driver");
+MODULE_LICENSE("GPL");
+
+module_init(asb100_init);
+module_exit(asb100_exit);
+
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/bt869.c linux-2.4.27-leo/drivers/sensors/bt869.c
--- linux-2.4.27/drivers/sensors/bt869.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/bt869.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,891 @@
+/*
+    bt869.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+    Copyright (c) 2001, 2002  Stephen Davies  <steve@daviesfam.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+#define DEBUG 1
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+
+/* found only at 0x44 or 0x45 */
+static unsigned short normal_i2c_range[] = { 0x44, 0x45, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(bt869);
+
+/* Many bt869 constants specified below */
+
+/* The bt869 registers */
+/* Coming soon: Many, many registers */
+
+/* Conversions. Rounding and limit checking is only done on the TO_REG
+   variants. Note that you should be a bit careful with which arguments
+   these macros are called: arguments may be evaluated more than once.
+   Fixing this is just not worth it. */
+
+   /*none */
+
+/* Initial values */
+/*none*/
+
+/* Each client has this additional data */
+struct bt869_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 status[3];		/* Register values */
+	u16 res[2];		/* Resolution XxY */
+	u8 ntsc;		/* 1=NTSC, 0=PAL */
+	u8 half;		/* go half res */
+	u8 depth;		/* screen depth */
+	u8 colorbars;		/* turn on/off colorbar calibration screen */
+        u8 svideo;              /* output format: (2=RGB) 1=SVIDEO, 0=Composite */
+};
+
+static int bt869_attach_adapter(struct i2c_adapter *adapter);
+static int bt869_detect(struct i2c_adapter *adapter, int address,
+			unsigned short flags, int kind);
+static void bt869_init_client(struct i2c_client *client);
+static int bt869_detach_client(struct i2c_client *client);
+static int bt869_read_value(struct i2c_client *client, u8 reg);
+static int bt869_write_value(struct i2c_client *client, u8 reg, u16 value);
+static void bt869_write_values(struct i2c_client *client, u16 *values);
+static void bt869_status(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void bt869_ntsc(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void bt869_res(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void bt869_half(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void bt869_colorbars(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void bt869_svideo(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void bt869_depth(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void bt869_update_client(struct i2c_client *client);
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver bt869_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "BT869 video-output chip driver",
+	.id		= I2C_DRIVERID_BT869,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= bt869_attach_adapter,
+	.detach_client	= bt869_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+#define BT869_SYSCTL_STATUS 1000
+#define BT869_SYSCTL_NTSC   1001
+#define BT869_SYSCTL_HALF   1002
+#define BT869_SYSCTL_RES    1003
+#define BT869_SYSCTL_COLORBARS    1004
+#define BT869_SYSCTL_DEPTH  1005
+#define BT869_SYSCTL_SVIDEO 1006
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected bt869. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized
+   when a new copy is allocated. */
+static ctl_table bt869_dir_table_template[] = {
+	{BT869_SYSCTL_STATUS, "status", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &bt869_status},
+	{BT869_SYSCTL_NTSC, "ntsc", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &bt869_ntsc},
+	{BT869_SYSCTL_RES, "res", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &bt869_res},
+	{BT869_SYSCTL_HALF, "half", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &bt869_half},
+	{BT869_SYSCTL_COLORBARS, "colorbars", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &bt869_colorbars},
+	{BT869_SYSCTL_DEPTH, "depth", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &bt869_depth},
+	{BT869_SYSCTL_SVIDEO, "svideo", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &bt869_svideo},
+	{0}
+};
+
+/* ******************
+
+720x576, 27.5MHz, PAL, no overscan compensation.
+
+This mode should be use for digital video, DVD playback etc.
+
+NOTE: This mode for PAL, see 720x480 for an equivalent NTSC mode
+NOTE:    -- Steve Davies <steve@daviesfam.org>
+
+
+Compatible X modeline:
+
+    Mode        "720x576-BT869"
+        DotClock        27.5
+        HTimings        720 744 800 880
+        VTimings        576 581 583 625
+    EndMode
+
+
+625LINE=1							625 line output format
+BST_AMP[7:0]=x57 87						Burst ampl. multiplication factor (PAL std??)
+BY_PLL=0							Use the PLL
+CATTENUATE[2:0]=0						No chroma attenuation
+CCF1B1[7:0]=0							close caption stuff
+CCF1B2[7:0]=0							close caption stuff
+CCF2B1[7:0]=0							close caption stuff
+CCF2B2[7:0]=0							close caption stuff
+CCORING[2:0]=0							Bypass chroma coring
+CCR_START[8:0]=0 [CCR_START[8]=0; CCR_START[7:0]=0]		Close-caption clock runin start from hsync
+CC_ADD[11:0]=xD2 210  [CC_ADD[11:8]=0; CC_ADD[7:0]=xD2]		Close-caption DTO increment
+CHECK_STAT=0							Don't check monitor status
+CLPF[1:0]=0						  	Hoz chroma lowpass filter=Bypass
+DACDISA=1							Disable DACA
+DACDISB=0							Don't disable DACB
+DACDISC=0							Don't disable DACC
+DACOFF=0							Don't disable the DACs
+DATDLY = 0							normal
+DATSWP=0							normal
+DCHROMA=0							Don't blank chroma
+DIS_FFILT=1							Disable flickerfilter
+DIS_GMSHC=1							Disable chroma psuedo-gamma removal
+DIS_GMSHY=1							Disable luma pseudo gamma removal
+DIS_GMUSHC=1							Disable chroma anti-pseudo gamma removal
+DIS_GMUSHY=1							Disable luma anti-pseudo gamma removal
+DIS_SCRESET=0							Normal subcarrier phase resets
+DIS_YFLPF=0							Disable Luma initial hoz low pass filter
+DIV2=0								Input pixel rate not divided by 2
+ECBAR=0								No colour bars
+ECCF1=0								Disable closed caption
+ECCF2=0								Disable closed caption
+ECCGATE=0							Normal close caption encoding
+ECLIP=0								0=disable clipping
+EN_ASYNC=0							set to 0 for normal operation
+EN_BLANKO=0							BLANK is an input
+EN_DOT=0							Disables dot clock sync on BLANK pin
+EN_OUT=1							Allows outputs to be enabled
+EN_XCLK=1							Use CLKI pin as clock source
+ESTATUS[1:0]=0							Used to select readback register
+FIELDI=0							Logical 1 on FIELD indicates even field
+F_SELC[2:0]=0							5 line chroma flickerfilter
+F_SELY[2:0]=0							5 line luma flickerfilter
+HBURST_BEGIN[7:0]=x98 152					Chroma burst start point in clocks
+HBURST_END[7:0]=x58 88						Chroma burst end point in clocks - 128
+HSYNCI=0							Active low HSYNC
+HSYNC_WIDTH[7:0]=x80 128					Analogue sync width in clocks
+HSYNOFFSET[9:0]=0  [HSYNOFFSET[9:8]=0; HSYNOFFSET[7:0]=0]	hsync in "standard position"
+HSYNWIDTH[5:0]=2						2 pixel hsync width
+H_ACTIVE[9:0]=x2D0 720  [H_ACTIVE[9:8]=2; H_ACTIVE[7:0]=xD0]	Active pixels per line
+H_BLANKI[8:0]=x84 132  [H_BLANKI[8]=0; H_BLANKI[7:0]=x84]	End of blanking of input video
+H_BLANKO[9:0]=x120 288  [H_BLANKO[9:8]=1; H_BLANKO[7:0]=x20]	End of blanking from hoz sync leading edge
+H_CLKI[10:0]=x378 888  [H_CLKI[10:8]=3; H_CLKI[7:0]=x78]	Input line length total in clocks
+H_CLKO[11:0]=x6e0 1760  [H_CLKO[11:8]=6; H_CLKO[7:0]=xe0]	Output clocks per line
+H_FRACT[7:0]=0							0 fractional input clocks per line
+IN_MODE[2:0]=0							24Bit RGB muxed
+LUMADLY[1:0]=0							0 pixel delay on Y_DLY luma
+MCB[7:0]=x49 73							Mult factor for CB prior to subcarrier mod.
+MCR[7:0]=x82 130						Mult factor for CR prior to subcarrier mod.
+MODE2X=0							Don't divide clock input by 2
+MSC[31:0]=x2945E0B4 692445365  [MSC[31:24]=x29; MSC[23:16]=x45; MSC[15:8]=xE0; MSC[7:0]=xB4] Subcarrier incr.
+MY[7:0]=x8C 140							Mult factor for Y
+NI_OUT=0							Normal interlaced output
+OUT_MODE[1:0]=0							video0-3 is CVBS, Y, C, Y_DLY
+OUT_MUXA[1:0]=0							Don't care as DACA is disabled
+OUT_MUXB[1:0]=1							Output video[1] (Y) on DACB
+OUT_MUXC[1:0]=2							Output video[2] (C) on DACC
+PAL_MD=1							Video output in PAL mode
+PHASE_OFF[7:0]=0						Subcarrier phase offset
+PLL_FRACT[15:0]=x30 48  [PLL_FRACT[15:8]=0x0; PLL_FRACT[7:0]=x30] frac portion of pll multiplier
+PLL_INT[5:0]=0x0C 12						Int portion of pll multiplier
+SETUP=0								7.5-IRE setup disabled
+SLAVER=1							
+SRESET=0							Don't do a software reset
+SYNC_AMP[7:0]=xF0 240						Sync amp mult. factor (PAL std???)
+VBLANKDLY=0							Extra line of blanking in 2nd field?
+VSYNCI=0							Active low VSYNC
+VSYNC_DUR=0							2.5line VSYNC duration on output
+VSYNCOFFSET[10:0]=0  [VSYNOFFSET[10:8]=0; VSYNOFFSET[7:0]=0]	VSYNC in standard position
+VSYNWIDTH[2:0]=1						1 line of vsync width
+V_ACTIVEI[9:0]=x240 576  [V_ACTIVEI[9:0]=2; V_ACTIVEI[7:0]=x40]	Active input lines
+V_ACTIVEO[8:0]=x122 290  [V_ACTIVE0[8]=1; V_ACTIVEO[7:0]=x22]
+V_BLANKI[7:0]=x2A 42						Input lines from vsync to first active line
+V_BLANKO[7:0]=x16 22
+V_LINESI[9:0]=x271 625  [V_LINESI[9:8]=2; V_LINESI[7:0]=x71]	Number of input lines
+V_SCALE[13:0]=x1000 4096  [V_SCALE[13:8]=x10; V_SCALE[7:0]=0]	Vert scale coefficient="none"?
+YATTENUATE[2:0]=0						no luma attenuation
+YCORING[2:0]=0							Luma-coring bypass
+YLPF[1:0]=0							Luma hoz low pass filter=bypass
+
+***************** */
+
+static u16 registers_720_576[] =
+    {
+      0x6e, 0x00,	/* HSYNOFFSET[7:0]=0 */
+      0x70, 0x02,	/* HSYNOFFSET[9:8]=0; HSYNWIDTH[5:0]=2 */
+      0x72, 0x00,	/* VSYNOFFSET[7:0]=0 */
+      0x74, 0x01,	/* DATDLY = 0; DATSWP=0; VSYNOFFSET[10:8]=0; VSYNWIDTH[2:0]=1 */
+      0x76, 0xe0,	/* H_CLKO[7:0]=xe0 */
+      0x78, 0xd0,	/* H_ACTIVE[7:0]=xD0 */
+      0x7a, 0x80,	/* HSYNC_WIDTH[7:0]=x80 */
+      0x7c, 0x98,	/* HBURST_BEGIN[7:0]=x98 */
+      0x7e, 0x58,	/* HBURST_END[7:0]=x58 */
+      0x80, 0x20,	/* H_BLANKO[7:0]=x20 */
+      0x82, 0x16,	/* V_BLANKO[7:0]=x16 */
+      0x84, 0x22,	/* V_ACTIVEO[7:0]=x22 */
+      0x86, 0xa6,	/* V_ACTIVE0[8]=1; H_ACTIVE[9:8]=2; H_CLKO[11:8]=6 */
+      0x88, 0x00,	/* H_FRACT[7:0]=0 */
+      0x8a, 0x78,	/* H_CLKI[7:0]=x78 */
+      0x8c, 0x80,	/* H_BLANKI[7:0]=x84 */
+      0x8e, 0x03,	/* VBLANKDLY=0; H_BLANKI[8]=0; H_CLKI[10:8]=3 */
+      0x90, 0x71,	/* V_LINESI[7:0]=x71 */
+      0x92, 0x2a,	/* V_BLANKI[7:0]=x2A */
+      0x94, 0x40,	/* V_ACTIVEI[7:0]=x40 */
+      0x96, 0x0a,	/* CLPF[1:0]=0; YLPF[1:0]=0; V_ACTIVEI[9:0]=2; V_LINESI[9:8]=2 */
+      0x98, 0x00,	/* V_SCALE[7:0]=0 */
+      0x9a, 0x50,	/* H_BLANKO[9:8]=1; V_SCALE[13:8]=x10 */
+      0x9c, 0x30,	/* PLL_FRACT[7:0]=x30 */
+      0x9e, 0x0,	/* PLL_FRACT[15:8]=0x0 */
+      0xa0, 0x8c,	/* EN_XCLK=1; BY_PLL=0; PLL_INT[5:0]=0x0C */
+      0xa2, 0x24,	/* ECLIP=0; PAL_MD=1; DIS_SCRESET=0; VSYNC_DUR=0; 625LINE=1; SETUP=0; NI_OUT=0 */
+      0xa4, 0xf0,	/* SYNC_AMP[7:0]=xF0 */
+      0xa6, 0x57,	/* BST_AMP[7:0]=x57 */
+      0xa8, 0x82,	/* MCR[7:0]=x82 */
+      0xaa, 0x49,	/* MCB[7:0]=x49 */
+      0xac, 0x8c,	/* MY[7:0]=x8C */
+      0xae, 0xb4,	/* MSC[7:0]=xb4 */
+      0xb0, 0xe0,	/* MSC[15:8]=xe0 */
+      0xb2, 0x45,	/* MSC[23:16]=x45 */
+      0xb4, 0x29,	/* MSC[31:24]=x29 */
+      0xb6, 0x00,	/* PHASE_OFF[7:0]=0 */
+      //0xba, 0x21,	/* SRESET=0; CHECK_STAT=0; SLAVER=1; DACOFF=0; DACDISC=0; DACDISB=0; DACDISA=1 */
+      0xc4, 0x01,	/* ESTATUS[1:0]=0; ECCF2=0; ECCF1=0; ECCGATE=0; ECBAR=0; DCHROMA=0; EN_OUT=1 */
+      0xc6, 0x00,	/* EN_BLANKO=0; EN_DOT=0; FIELDI=0; VSYNCI=0; HSYNCI=0; IN_MODE[2:0]=0(24bRGB) */
+      0xc8, 0x40,	/* DIS_YFLPF=0; DIS_FFILT=1; F_SELC[2:0]=0; F_SELY[2:0]=0 */
+      0xca, 0xc0,	/* DIS_GMUSHY=1; DIS_GMSHY=1; YCORING[2:0]=0; YATTENUATE[2:0]=0 */
+      0xcc, 0xc0,	/* DIS_GMUSHC=1; DIS_GMSHC=1; CCORING[2:0]=0; CATTENUATE[2:0]=0 */
+      //0xce, 0x24,       /* OUT_MUXC=2 [C]; OUT_MUXB=1 [Y]; OUT_MUXA=0 [CVBS, but disabled]*/
+      //0xce, 0x04,       /* OUT_MUXC=0 [CVBS]; OUT_MUXB=1 [Y]; OUT_MUXA=0 [CVBS, but disabled]*/
+      0xd6, 0x00,	/* OUT_MODE[1:0]=0; LUMADLY[1:0]=0 */
+      0, 0
+    };
+
+
+/* ******************
+
+720x480, 27.5MHz, NTSC no overscan compensation.
+
+This mode should be use for digital video, DVD playback etc.
+
+NOTE: This mode for NTSC, see 720x576 for an equivalent PAL mode
+NOTE:    -- Steve Davies <steve@daviesfam.org>
+
+Compatible X modeline:
+
+    Mode        "720x480-BT869"
+        DotClock        27.5
+        HTimings        720 744 800 872
+        VTimings        480 483 485 525
+    EndMode
+
+
+625LINE=0							not 625 line output format
+BST_AMP[7:0]=x74 116						Burst ampl. multiplication factor (NTSC std??)
+BY_PLL=0							Use the PLL
+CATTENUATE[2:0]=0						No chroma attenuation
+CCF1B1[7:0]=0							close caption stuff
+CCF1B2[7:0]=0							close caption stuff
+CCF2B1[7:0]=0							close caption stuff
+CCF2B2[7:0]=0							close caption stuff
+CCORING[2:0]=0							Bypass chroma coring
+CCR_START[8:0]=0 [CCR_START[8]=0; CCR_START[7:0]=0]		Close-caption clock runin start from hsync
+CC_ADD[11:0]=xD2 210  [CC_ADD[11:8]=0; CC_ADD[7:0]=xD2]		Close-caption DTO increment
+CHECK_STAT=0							Don't check monitor status
+CLPF[1:0]=0						  	Hoz chroma lowpass filter=Bypass
+DACDISA=1							Disable DACA
+DACDISB=0							Don't disable DACB
+DACDISC=0							Don't disable DACC
+DACOFF=0							Don't disable the DACs
+DATDLY = 0							normal
+DATSWP=0							normal
+DCHROMA=0							Don't blank chroma
+DIS_FFILT=1							Disable flickerfilter
+DIS_GMSHC=1							Disable chroma psuedo-gamma removal
+DIS_GMSHY=1							Disable luma pseudo gamma removal
+DIS_GMUSHC=1							Disable chroma anti-pseudo gamma removal
+DIS_GMUSHY=1							Disable luma anti-pseudo gamma removal
+DIS_SCRESET=0							Normal subcarrier phase resets
+DIS_YFLPF=0							Disable Luma initial hoz low pass filter
+DIV2=0								Input pixel rate not divided by 2
+ECBAR=0								No colour bars
+ECCF1=0								Disable closed caption
+ECCF2=0								Disable closed caption
+ECCGATE=0							Normal close caption encoding
+ECLIP=0								0=disable clipping
+EN_ASYNC=0							set to 0 for normal operation
+EN_BLANKO=0							BLANK is an input
+EN_DOT=0							Disables dot clock sync on BLANK pin
+EN_OUT=1							Allows outputs to be enabled
+EN_XCLK=1							Use CLKI pin as clock source
+ESTATUS[1:0]=0							Used to select readback register
+FIELDI=0							Logical 1 on FIELD indicates even field
+F_SELC[2:0]=0							5 line chroma flickerfilter
+F_SELY[2:0]=0							5 line luma flickerfilter
+HBURST_BEGIN[7:0]=x92 146					Chroma burst start point in clocks
+HBURST_END[7:0]=x57 87						Chroma burst end point in clocks - 128
+HSYNCI=0							Active low HSYNC
+HSYNC_WIDTH[7:0]=x80 128					Analogue sync width in clocks
+HSYNOFFSET[9:0]=0  [HSYNOFFSET[9:8]=0; HSYNOFFSET[7:0]=0]	hsync in "standard position"
+HSYNWIDTH[5:0]=2						2 pixel hsync width
+H_ACTIVE[9:0]=x2D0 720  [H_ACTIVE[9:8]=2; H_ACTIVE[7:0]=xD0]	Active pixels per line
+H_BLANKI[8:0]=x80 128  [H_BLANKI[8]=0; H_BLANKI[7:0]=x80]	End of blanking of input video
+H_BLANKO[9:0]=x102 258  [H_BLANKO[9:8]=1; H_BLANKO[7:0]=x2]	End of blanking from hoz sync leading edge
+H_CLKI[10:0]=x368 872  [H_CLKI[10:8]=3; H_CLKI[7:0]=x68]	Input line length total in clocks
+H_CLKO[11:0]=x6d0 1744  [H_CLKO[11:8]=6; H_CLKO[7:0]=xD0]	Output clocks per line
+H_FRACT[7:0]=0							0 fractional input clocks per line
+IN_MODE[2:0]=0							24Bit RGB muxed
+LUMADLY[1:0]=0							0 pixel delay on Y_DLY luma
+MCB[7:0]=x43 67							Mult factor for CB prior to subcarrier mod.
+MCR[7:0]=x77 119						Mult factor for CR prior to subcarrier mod.
+MODE2X=0							Don't divide clock input by 2
+MSC[31:0]=x215282E5 559055589  [MSC[31:24]=x21; MSC[23:16]=x52; MSC[15:8]=x82; MSC[7:0]=xE5] Subcarrier incr.
+MY[7:0]=x85 133							Mult factor for Y
+NI_OUT=0							Normal interlaced output
+OUT_MODE[1:0]=0							video0-3 is CVBS, Y, C, Y_DLY
+OUT_MUXA[1:0]=0							Don't care as DACA is disabled
+OUT_MUXB[1:0]=1							Output video[1] (Y) on DACB
+OUT_MUXC[1:0]=2							Output video[2] (C) on DACC
+PAL_MD=0							Video output in PAL mode? No.
+PHASE_OFF[7:0]=0						Subcarrier phase offset
+PLL_FRACT[15:0]=x30 48  [PLL_FRACT[15:8]=0x0; PLL_FRACT[7:0]=x30] frac portion of pll multiplier
+PLL_INT[5:0]=0x0C 12						Int portion of pll multiplier
+SETUP=1								7.5-IRE enabled for NTSC
+SLAVER=1							
+SRESET=0							Don't do a software reset
+SYNC_AMP[7:0]=xE5 229						Sync amp mult. factor (PAL std???)
+VBLANKDLY=0							Extra line of blanking in 2nd field?
+VSYNCI=0							Active low VSYNC
+VSYNC_DUR=1							2.5line VSYNC duration on output (Yes for NTSC)
+VSYNCOFFSET[10:0]=0  [VSYNOFFSET[10:8]=0; VSYNOFFSET[7:0]=0]	VSYNC in standard position
+VSYNWIDTH[2:0]=1						1 line of vsync width
+V_ACTIVEI[9:0]=x1E0 480  [V_ACTIVEI[9:0]=1; V_ACTIVEI[7:0]=xE0]	Active input lines
+V_ACTIVEO[8:0]=xF0 240  [V_ACTIVE0[8]=0; V_ACTIVEO[7:0]=xF0]
+V_BLANKI[7:0]=x2A 42						Input lines from vsync to first active line
+V_BLANKO[7:0]=x16 22
+V_LINESI[9:0]=x20D 525  [V_LINESI[9:8]=2; V_LINESI[7:0]=x0D]	Number of input lines
+V_SCALE[13:0]=x1000 4096  [V_SCALE[13:8]=x10; V_SCALE[7:0]=0]	Vert scale coefficient="none"?
+YATTENUATE[2:0]=0						no luma attenuation
+YCORING[2:0]=0							Luma-coring bypass
+YLPF[1:0]=0							Luma hoz low pass filter=bypass
+
+***************** */
+
+static u16 registers_720_480[] =
+    {
+      0x6e, 0x00,	/* HSYNOFFSET[7:0]=0 */
+      0x70, 0x02,	/* HSYNOFFSET[9:8]=0; HSYNWIDTH[5:0]=2 */
+      0x72, 0x00,	/* VSYNOFFSET[7:0]=0 */
+      0x74, 0x01,	/* DATDLY = 0; DATSWP=0; VSYNOFFSET[10:8]=0; VSYNWIDTH[2:0]=1 */
+      0x76, 0xD0,	/* H_CLKO[7:0]=xD0 */
+      0x78, 0xD0,	/* H_ACTIVE[7:0]=xD0 */
+      0x7a, 0x80,	/* HSYNC_WIDTH[7:0]=x80 */
+      0x7c, 0x92,	/* HBURST_BEGIN[7:0]=x92 */
+      0x7e, 0x57,	/* HBURST_END[7:0]=x57 */
+      0x80, 0x02,	/* H_BLANKO[7:0]=x2 */
+      0x82, 0x16,	/* V_BLANKO[7:0]=x16 */
+      0x84, 0xF0,	/* V_ACTIVEO[7:0]=xF0 */
+      0x86, 0x26,	/* V_ACTIVE0[8]=0; H_ACTIVE[9:8]=2; H_CLKO[11:8]=6 */
+      0x88, 0x00,	/* H_FRACT[7:0]=0 */
+      0x8a, 0xD0,	/* H_CLKI[7:0]=xD0 */
+      0x8c, 0x80,	/* H_BLANKI[7:0]=x80 */
+      0x8e, 0x03,	/* VBLANKDLY=0; H_BLANKI[8]=0; H_CLKI[10:8]=3 */
+      0x90, 0x0D,	/* V_LINESI[7:0]=x0D */
+      0x92, 0x2A,	/* V_BLANKI[7:0]=x2A */
+      0x94, 0xE0,	/* V_ACTIVEI[7:0]=xE0 */
+      0x96, 0x06,	/* CLPF[1:0]=0; YLPF[1:0]=0; V_ACTIVEI[9:8]=1; V_LINESI[9:8]=2 */
+      0x98, 0x00,	/* V_SCALE[7:0]=0 */
+      0x9a, 0x50,	/* H_BLANKO[9:8]=1; V_SCALE[13:8]=x10 */
+      0x9c, 0x30,	/* PLL_FRACT[7:0]=x30 */
+      0x9e, 0x0,	/* PLL_FRACT[15:8]=0x0 */
+      0xa0, 0x8c,	/* EN_XCLK=1; BY_PLL=0; PLL_INT[5:0]=0x0C */
+      0xa2, 0x0A,	/* ECLIP=0; PAL_MD=0; DIS_SCRESET=0; VSYNC_DUR=1; 625LINE=0; SETUP=1; NI_OUT=0 */
+      0xa4, 0xE5,	/* SYNC_AMP[7:0]=xE5 */
+      0xa6, 0x74,	/* BST_AMP[7:0]=x74 */
+      0xa8, 0x77,	/* MCR[7:0]=x77 */
+      0xaa, 0x43,	/* MCB[7:0]=x43 */
+      0xac, 0x85,	/* MY[7:0]=x85 */
+      0xae, 0xE5,	/* MSC[7:0]=xE5 */
+      0xb0, 0x82,	/* MSC[15:8]=x82 */
+      0xb2, 0x52,	/* MSC[23:16]=x52 */
+      0xb4, 0x21,	/* MSC[31:24]=x21 */
+      0xb6, 0x00,	/* PHASE_OFF[7:0]=0 */
+      //0xba, 0x21,	/* SRESET=0; CHECK_STAT=0; SLAVER=1; DACOFF=0; DACDISC=0; DACDISB=0; DACDISA=1 */
+      0xc4, 0x01,	/* ESTATUS[1:0]=0; ECCF2=0; ECCF1=0; ECCGATE=0; ECBAR=0; DCHROMA=0; EN_OUT=1 */
+      0xc6, 0x00,	/* EN_BLANKO=0; EN_DOT=0; FIELDI=0; VSYNCI=0; HSYNCI=0; IN_MODE[2:0]=0(24bRGB) */
+      0xc8, 0x40,	/* DIS_YFLPF=0; DIS_FFILT=1; F_SELC[2:0]=0; F_SELY[2:0]=0 */
+      0xca, 0xc0,	/* DIS_GMUSHY=1; DIS_GMSHY=1; YCORING[2:0]=0; YATTENUATE[2:0]=0 */
+      0xcc, 0xc0,	/* DIS_GMUSHC=1; DIS_GMSHC=1; CCORING[2:0]=0; CATTENUATE[2:0]=0 */
+      //0xce, 0x24,       /* OUT_MUXC=2 [C]; OUT_MUXB=1 [Y]; OUT_MUXA=0 [CVBS, but disabled]*/
+      //0xce, 0x04,       /* OUT_MUXC=0 [CVBS]; OUT_MUXB=1 [Y]; OUT_MUXA=0 [CVBS, but disabled]*/
+      0xd6, 0x00,	/* OUT_MODE[1:0]=0; LUMADLY[1:0]=0 */
+      0, 0
+    };
+
+
+int bt869_id = 0;
+
+static int bt869_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, bt869_detect);
+}
+
+/* This function is called by i2c_detect */
+int bt869_detect(struct i2c_adapter *adapter, int address,
+		 unsigned short flags, int kind)
+{
+	int i, cur;
+	struct i2c_client *new_client;
+	struct bt869_data *data;
+	int err = 0;
+	const char *type_name, *client_name;
+
+
+	printk("bt869.o:  probing address %d .\n", address);
+	/* Make sure we aren't probing the ISA bus!! This is just a safety check
+	   at this moment; i2c_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("bt869.o: bt869_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE |
+				     I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+		    goto ERROR0;
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access bt869_{read,write}_value. */
+	if (!(data = kmalloc(sizeof(struct bt869_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &bt869_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. It is lousy. */
+	i2c_smbus_write_byte_data(new_client, 0xC4, 0);	/* set status bank 0 */
+	cur = i2c_smbus_read_byte(new_client);
+	printk("bt869.o: address 0x%X testing-->0x%X\n", address, cur);
+	if ((cur & 0xE0) != 0x20)
+		goto ERROR1;
+
+	/* Determine the chip type */
+	kind = ((cur & 0x20) >> 5);
+
+	if (kind) {
+		type_name = "bt869";
+		client_name = "bt869 chip";
+		printk("bt869.o: BT869 detected\n");
+	} else {
+		type_name = "bt868";
+		client_name = "bt868 chip";
+		printk("bt869.o: BT868 detected\n");
+	}
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+
+	new_client->id = bt869_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client, type_name,
+					bt869_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	bt869_init_client((struct i2c_client *) new_client);
+	return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+      ERROR1:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int bt869_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct bt869_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("bt869.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+
+/* All registers are byte-sized.
+   bt869 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+static int bt869_read_value(struct i2c_client *client, u8 reg)
+{
+	return i2c_smbus_read_byte(client);
+}
+
+/* All registers are byte-sized.
+   bt869 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+static int bt869_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+#ifdef DEBUG
+        printk("bt869.o: write_value(0x%X, 0x%X)\n", reg, value);
+#endif
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static void bt869_write_values(struct i2c_client *client, u16 *values)
+{
+  /* writes set of registers from array.  0,0 marks end of table */
+  while (*values) {
+    bt869_write_value(client, values[0], values[1]);
+    values += 2;
+  }
+}
+
+static void bt869_init_client(struct i2c_client *client)
+{
+	struct bt869_data *data = client->data;
+
+	/* Initialize the bt869 chip */
+	bt869_write_value(client, 0x0ba, 0x80);
+	//   bt869_write_value(client,0x0D6, 0x00);
+	/* Be a slave to the clock on the Voodoo3 */
+	bt869_write_value(client, 0xa0, 0x80);
+	bt869_write_value(client, 0xba, 0x20);
+	/* depth =16bpp */
+	bt869_write_value(client, 0x0C6, 0x001);
+	bt869_write_value(client, 0xC4, 1);
+	/* Flicker free enable and config */
+	bt869_write_value(client, 0xC8, 0);
+	data->res[0] = 640;
+	data->res[1] = 480;
+	data->ntsc = 1;
+	data->half = 0;
+	data->colorbars = 0;
+	data->svideo = 0;
+	data->depth = 16;
+
+}
+
+static void bt869_update_client(struct i2c_client *client)
+{
+	struct bt869_data *data = client->data;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+#ifdef DEBUG
+		printk("Starting bt869 update\n");
+#endif
+		if ((data->res[0] == 800) && (data->res[1] == 600)) {
+			/* 800x600 built-in mode */
+		        bt869_write_value(client, 0xB8,
+					  (2 + (!data->ntsc)));
+			bt869_write_value(client, 0xa0, 0x80 + 0x11);
+			printk("bt869.o: writing into config -->0x%X\n",
+			       (2 + (!data->ntsc)));
+		}
+		else if ((data->res[0] == 720) && (data->res[1] == 576)) {
+		        /* 720x576 no-overscan-compensation mode suitable for PAL DVD playback */
+		        data->ntsc = 0; /* This mode always PAL */
+		        bt869_write_values(client,  registers_720_576);
+		}
+		else if ((data->res[0] == 720) && (data->res[1] == 480)) {
+		        /* 720x480 no-overscan-compensation mode suitable for NTSC DVD playback */
+		        data->ntsc = 1; /* This mode always NTSC */
+		        bt869_write_values(client,  registers_720_480);
+		}
+		else {
+		        /* 640x480 built-in mode */
+		        bt869_write_value(client, 0xB8, (!data->ntsc));
+			bt869_write_value(client, 0xa0, 0x80 + 0x0C);
+			printk("bt869.o: writing into config -->0x%X\n",
+			       (0 + (!data->ntsc)));
+			if ((data->res[0] != 640) || (data->res[1] != 480)) {
+			  printk
+			    ("bt869.o:  Warning: arbitrary resolutions not supported yet.  Using 640x480.\n");
+			  data->res[0] = 640;
+			  data->res[1] = 480;
+			}
+		}
+		/* Set colour depth */
+		if ((data->depth != 24) && (data->depth != 16))
+			data->depth = 16;
+		if (data->depth == 16)
+			bt869_write_value(client, 0x0C6, 0x001);
+		if (data->depth == 24)
+			bt869_write_value(client, 0x0C6, 0x000);
+		/* set "half" resolution mode */
+		bt869_write_value(client, 0xd4, data->half << 6);
+		/* Set composite/svideo mode, also enable the right dacs */
+		switch (data->svideo) {
+		case 2:  /* RGB */
+		  /* requires hardware mod on Voodoo3 to get all outputs,
+		     untested in practice... Feedback to steve@daviesfam.org please */
+		  bt869_write_value(client, 0xd6, 0x0c);
+		  bt869_write_value(client, 0xce, 0x24);
+		  bt869_write_value(client, 0xba, 0x20);
+		  break;
+		case 1:  /* Svideo*/
+		  bt869_write_value(client, 0xce, 0x24);
+		  bt869_write_value(client, 0xba, 0x21);
+		  break;
+		default:  /* Composite */
+		  bt869_write_value(client, 0xce, 0x0);
+		  bt869_write_value(client, 0xba, 0x21);
+		  break;
+		}
+		/* Enable outputs */
+		bt869_write_value(client, 0xC4, 1);
+		/* Issue timing reset */
+		bt869_write_value(client, 0x6c, 0x80);
+
+/* Read back status registers */
+		bt869_write_value(client, 0xC4,
+				  1 | (data->colorbars << 2));
+		data->status[0] = bt869_read_value(client, 1);
+		bt869_write_value(client, 0xC4,
+				  0x41 | (data->colorbars << 2));
+		data->status[1] = bt869_read_value(client, 1);
+		bt869_write_value(client, 0xC4,
+				  0x81 | (data->colorbars << 2));
+		data->status[2] = bt869_read_value(client, 1);
+		bt869_write_value(client, 0xC4,
+				  0x0C1 | (data->colorbars << 2));
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+	up(&data->update_lock);
+}
+
+
+void bt869_status(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct bt869_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		bt869_update_client(client);
+		results[0] = data->status[0];
+		results[1] = data->status[1];
+		results[2] = data->status[2];
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		printk
+		    ("bt869.o: Warning: write was requested on read-only proc file: status\n");
+	}
+}
+
+
+void bt869_ntsc(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct bt869_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		bt869_update_client(client);
+		results[0] = data->ntsc;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->ntsc = (results[0] > 0);
+		}
+		bt869_update_client(client);
+	}
+}
+
+
+void bt869_svideo(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct bt869_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		bt869_update_client(client);
+		results[0] = data->svideo;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->svideo = results[0];
+		}
+		bt869_update_client(client);
+	}
+}
+
+
+void bt869_res(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct bt869_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		bt869_update_client(client);
+		results[0] = data->res[0];
+		results[1] = data->res[1];
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->res[0] = results[0];
+		}
+		if (*nrels_mag >= 2) {
+			data->res[1] = results[1];
+		}
+		bt869_update_client(client);
+	}
+}
+
+
+void bt869_half(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct bt869_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		bt869_update_client(client);
+		results[0] = data->half;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->half = (results[0] > 0);
+			bt869_update_client(client);
+		}
+	}
+}
+
+void bt869_colorbars(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	struct bt869_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		bt869_update_client(client);
+		results[0] = data->colorbars;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->colorbars = (results[0] > 0);
+			bt869_update_client(client);
+		}
+	}
+}
+
+void bt869_depth(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct bt869_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		bt869_update_client(client);
+		results[0] = data->depth;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->depth = results[0];
+			bt869_update_client(client);
+		}
+	}
+}
+
+static int __init sm_bt869_init(void)
+{
+	printk("bt869.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&bt869_driver);
+}
+
+static void __exit sm_bt869_exit(void)
+{
+	i2c_del_driver(&bt869_driver);
+}
+
+
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, Stephen Davies <steve@daviesfam.org>");
+MODULE_DESCRIPTION("bt869 driver");
+
+module_init(sm_bt869_init);
+module_exit(sm_bt869_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/Config.in linux-2.4.27-leo/drivers/sensors/Config.in
--- linux-2.4.27/drivers/sensors/Config.in	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/Config.in	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,60 @@
+#
+# Sensor device configuration
+# All depend on CONFIG_I2C_PROC.
+# ISA-only devices depend on CONFIG_I2C_ISA also.
+#
+
+if [ "$CONFIG_I2C" = "m" -o "$CONFIG_I2C" = "y" ] ; then
+if [ "$CONFIG_I2C_PROC" = "m" -o "$CONFIG_I2C_PROC" = "y" ] ; then
+  mainmenu_option next_comment
+  comment 'Hardware sensors support'
+  
+  dep_mbool 'Hardware sensors support' CONFIG_SENSORS $CONFIG_I2C $CONFIG_I2C_PROC
+  
+  if [ "$CONFIG_SENSORS" != "n" ]; then
+    dep_tristate '  Analog Devices ADM1021 and compatibles' CONFIG_SENSORS_ADM1021 $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  Analog Devices ADM1024' CONFIG_SENSORS_ADM1024 $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  Analog Devices ADM1025' CONFIG_SENSORS_ADM1025 $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  Analog Devices ADM1026' CONFIG_SENSORS_ADM1026 $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  Analog Devices ADM9240 and compatibles' CONFIG_SENSORS_ADM9240 $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  Asus ASB100' CONFIG_SENSORS_ASB100 $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  Dallas DS1621 and DS1625' CONFIG_SENSORS_DS1621 $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  Fujitsu-Siemens Poseidon' CONFIG_SENSORS_FSCPOS $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  Fujitsu-Siemens Scylla' CONFIG_SENSORS_FSCSCY $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  Genesys Logic GL518SM' CONFIG_SENSORS_GL518SM $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  Genesys Logic GL520SM' CONFIG_SENSORS_GL520SM $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  HP Maxilife' CONFIG_SENSORS_MAXILIFE $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  ITE 8705/8712, SiS950' CONFIG_SENSORS_IT87 $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  Maxim MAX6650, MAX6651' CONFIG_SENSORS_MAX6650 $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  Myson MTP008' CONFIG_SENSORS_MTP008 $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  National Semiconductor LM75 and compatibles' CONFIG_SENSORS_LM75 $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  National Semiconductor LM78' CONFIG_SENSORS_LM78 $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  National Semiconductor LM80' CONFIG_SENSORS_LM80 $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  National Semiconductor LM83' CONFIG_SENSORS_LM83 $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  National Semiconductor LM85, Analog Devices ADM1027' CONFIG_SENSORS_LM85 $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  National Semiconductor LM87' CONFIG_SENSORS_LM87 $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  National Semiconductor LM90 and compatibles' CONFIG_SENSORS_LM90 $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  National Semiconductor LM92' CONFIG_SENSORS_LM92 $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  Silicon Integrated Systems Corp. SiS5595' CONFIG_SENSORS_SIS5595 $CONFIG_I2C $CONFIG_I2C_PROC $CONFIG_I2C_ISA
+    dep_tristate '  SMSC47M1xx Integrated Sensors' CONFIG_SENSORS_SMSC47M1 $CONFIG_I2C $CONFIG_I2C_PROC $CONFIG_I2C_ISA
+    dep_tristate '  Texas Instruments THMC50 and compatibles' CONFIG_SENSORS_THMC50 $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  VIA 686a Integrated Hardware Monitor' CONFIG_SENSORS_VIA686A $CONFIG_I2C $CONFIG_I2C_PROC $CONFIG_I2C_ISA
+    dep_tristate '  VIA VT1211 Integrated Sensors' CONFIG_SENSORS_VT1211 $CONFIG_I2C $CONFIG_I2C_PROC $CONFIG_I2C_ISA
+    dep_tristate '  VIA VT8231 Integrated Sensors' CONFIG_SENSORS_VT8231 $CONFIG_I2C $CONFIG_I2C_PROC $CONFIG_I2C_ISA
+    dep_tristate '  Winbond W83781D, W83782D, W83783S, W83627HF, Asus AS99127F' CONFIG_SENSORS_W83781D $CONFIG_I2C $CONFIG_I2C_PROC
+    dep_tristate '  Winbond W83627HF, W83627THF, W83697HF' CONFIG_SENSORS_W83627HF $CONFIG_I2C $CONFIG_I2C_PROC $CONFIG_I2C_ISA
+    dep_tristate '  Winbond W83L785TS-S' CONFIG_SENSORS_W83L785TS $CONFIG_I2C $CONFIG_I2C_PROC
+    bool 'Other I2C devices' CONFIG_SENSORS_OTHER 
+    if [ "$CONFIG_SENSORS_OTHER" = "y" ] ; then
+      dep_tristate '  Brooktree BT869 Video Modulator' CONFIG_SENSORS_BT869 $CONFIG_I2C $CONFIG_I2C_PROC
+      dep_tristate '  DDC Monitor EDID EEPROM' CONFIG_SENSORS_DDCMON $CONFIG_I2C $CONFIG_I2C_PROC
+      dep_tristate '  EEprom (DIMM) reader ' CONFIG_SENSORS_EEPROM $CONFIG_I2C $CONFIG_I2C_PROC
+      dep_tristate '  Matrix-Orbital LCD Displays' CONFIG_SENSORS_MATORB $CONFIG_I2C $CONFIG_I2C_PROC
+      dep_tristate '  Philips PCF8574 Parallel I/O' CONFIG_SENSORS_PCF8574 $CONFIG_I2C $CONFIG_I2C_PROC
+      dep_tristate '  Philips PCF8591 D/A and A/D' CONFIG_SENSORS_PCF8591 $CONFIG_I2C $CONFIG_I2C_PROC
+    fi
+  fi
+  endmenu
+fi
+fi
+
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/ddcmon.c linux-2.4.27-leo/drivers/sensors/ddcmon.c
--- linux-2.4.27/drivers/sensors/ddcmon.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/ddcmon.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,591 @@
+/*
+    ddcmon.c - Part of lm_sensors, Linux kernel modules for hardware
+               monitoring
+    Copyright (c) 1998, 1999, 2000  Frodo Looijaard <frodol@dds.nl>,
+    Philip Edelbrock <phil@netroedge.com>,
+    and Mark Studebaker <mdsxyz123@yahoo.com>
+    Copyright (c) 2003  Jean Delvare <khali@linux-fr.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x50, SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(ddcmon);
+
+static int checksum = 0;
+MODULE_PARM(checksum, "i");
+MODULE_PARM_DESC(checksum, "Only accept eeproms whose checksum is correct");
+
+/* Many constants specified below */
+
+/* DDCMON registers */
+/* vendor section */
+#define DDCMON_REG_MAN_ID 0x08
+#define DDCMON_REG_PROD_ID 0x0A
+#define DDCMON_REG_SERIAL 0x0C
+#define DDCMON_REG_WEEK 0x10
+#define DDCMON_REG_YEAR 0x11
+/* EDID version */
+#define DDCMON_REG_EDID_VER 0x12
+#define DDCMON_REG_EDID_REV 0x13
+/* display information */
+#define DDCMON_REG_HORSIZE 0x15
+#define DDCMON_REG_VERSIZE 0x16
+#define DDCMON_REG_GAMMA 0x17
+#define DDCMON_REG_DPMS_FLAGS 0x18
+/* supported timings */
+#define DDCMON_REG_ESTABLISHED_TIMINGS 0x23
+#define DDCMON_REG_STANDARD_TIMINGS 0x26
+#define DDCMON_REG_TIMBASE 0x36
+#define DDCMON_REG_TIMINCR 18
+#define DDCMON_REG_TIMNUM   4
+
+#define DDCMON_REG_CHECKSUM 0x7f
+
+/* Size of DDCMON in bytes */
+#define DDCMON_SIZE 128
+
+/* Each client has this additional data */
+struct ddcmon_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 data[DDCMON_SIZE];	/* Register values */
+};
+
+
+static int ddcmon_attach_adapter(struct i2c_adapter *adapter);
+static int ddcmon_detect(struct i2c_adapter *adapter, int address,
+			 unsigned short flags, int kind);
+static int ddcmon_detach_client(struct i2c_client *client);
+
+static void ddcmon_idcall(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ddcmon_size(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ddcmon_sync(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ddcmon_maxclock(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ddcmon_timings(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ddcmon_serial(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ddcmon_time(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ddcmon_edid(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ddcmon_gamma(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ddcmon_dpms(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ddcmon_standard_timing(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ddcmon_update_client(struct i2c_client *client);
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver ddcmon_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "DDCMON READER",
+	.id		= I2C_DRIVERID_DDCMON,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= ddcmon_attach_adapter,
+	.detach_client	= ddcmon_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+
+#define DDCMON_SYSCTL_ID 1010
+#define DDCMON_SYSCTL_SIZE 1011
+#define DDCMON_SYSCTL_SYNC 1012
+#define DDCMON_SYSCTL_TIMINGS 1013
+#define DDCMON_SYSCTL_SERIAL 1014
+#define DDCMON_SYSCTL_TIME 1015
+#define DDCMON_SYSCTL_EDID 1016
+#define DDCMON_SYSCTL_GAMMA 1017
+#define DDCMON_SYSCTL_DPMS 1018
+#define DDCMON_SYSCTL_TIMING1 1021
+#define DDCMON_SYSCTL_TIMING2 1022
+#define DDCMON_SYSCTL_TIMING3 1023
+#define DDCMON_SYSCTL_TIMING4 1024
+#define DDCMON_SYSCTL_TIMING5 1025
+#define DDCMON_SYSCTL_TIMING6 1026
+#define DDCMON_SYSCTL_TIMING7 1027
+#define DDCMON_SYSCTL_TIMING8 1028
+#define DDCMON_SYSCTL_MAXCLOCK 1029
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected DDCMON. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized
+   when a new copy is allocated. */
+static ctl_table ddcmon_dir_table_template[] = {
+	{DDCMON_SYSCTL_ID, "id", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &ddcmon_idcall},
+	{DDCMON_SYSCTL_SIZE, "size", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &ddcmon_size},
+	{DDCMON_SYSCTL_SYNC, "sync", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_sync},
+	{DDCMON_SYSCTL_TIMINGS, "timings", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_timings},
+	{DDCMON_SYSCTL_SERIAL, "serial", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_serial},
+	{DDCMON_SYSCTL_TIME, "time", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_time},
+	{DDCMON_SYSCTL_EDID, "edid", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_edid},
+	{DDCMON_SYSCTL_GAMMA, "gamma", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_gamma},
+	{DDCMON_SYSCTL_DPMS, "dpms", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_dpms},
+	{DDCMON_SYSCTL_TIMING1, "timing1", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_standard_timing},
+	{DDCMON_SYSCTL_TIMING2, "timing2", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_standard_timing},
+	{DDCMON_SYSCTL_TIMING3, "timing3", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_standard_timing},
+	{DDCMON_SYSCTL_TIMING4, "timing4", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_standard_timing},
+	{DDCMON_SYSCTL_TIMING5, "timing5", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_standard_timing},
+	{DDCMON_SYSCTL_TIMING6, "timing6", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_standard_timing},
+	{DDCMON_SYSCTL_TIMING7, "timing7", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_standard_timing},
+	{DDCMON_SYSCTL_TIMING8, "timing8", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_standard_timing},
+	{DDCMON_SYSCTL_MAXCLOCK, "maxclock", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_maxclock},
+	{0}
+};
+
+static int ddcmon_id = 0;
+
+static int ddcmon_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, ddcmon_detect);
+}
+
+/* This function is called by i2c_detect */
+int ddcmon_detect(struct i2c_adapter *adapter, int address,
+		  unsigned short flags, int kind)
+{
+	int i, cs;
+	struct i2c_client *new_client;
+	struct ddcmon_data *data;
+	int err = 0;
+	const char *type_name, *client_name;
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		    goto ERROR0;
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access ddcmon_{read,write}_value. */
+	if (!(data = kmalloc(sizeof(struct ddcmon_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	memset(data->data, 0xff, DDCMON_SIZE);
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &ddcmon_driver;
+	new_client->flags = 0;
+
+	/* prevent 24RF08 corruption (just in case) */
+	i2c_smbus_write_quick(new_client, 0);
+
+	/* Now, we do the remaining detection. */
+	if (checksum) {
+		int cs = 0;
+		for (i = 0; i < 0x80; i++)
+			cs += i2c_smbus_read_byte_data(new_client, i);
+		if ((cs & 0xff) != 0)
+			goto ERROR1;
+	}
+
+	/* Verify the first 8 locations 0x00FFFFFFFFFFFF00 */
+	/* Allow force and force_ddcmon arguments */
+	if(kind < 0)
+	{
+		for(i = 0; i < 8; i++) {
+			cs = i2c_smbus_read_byte_data(new_client, i);
+			if(i == 0 || i == 7) {
+				if(cs != 0)
+					goto ERROR1;
+			} else if(cs != 0xff)
+				goto ERROR1;
+		}
+	}
+
+	type_name = "ddcmon";
+	client_name = "DDC Monitor";
+
+	/* Fill in the remaining client fields and put it in the global list */
+	strcpy(new_client->name, client_name);
+
+	new_client->id = ddcmon_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client, type_name,
+					ddcmon_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	return 0;
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+      ERROR1:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int ddcmon_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct ddcmon_data *) (client->data))->
+				 sysctl_id);
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("ddcmon.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+	kfree(client->data);
+	return 0;
+}
+
+static void ddcmon_update_client(struct i2c_client *client)
+{
+	struct ddcmon_data *data = client->data;
+	int i, j;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > 300 * HZ) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+		if (i2c_check_functionality(client->adapter,
+		                            I2C_FUNC_SMBUS_READ_I2C_BLOCK))
+		{
+			for (i=0; i<DDCMON_SIZE; i+=I2C_SMBUS_I2C_BLOCK_MAX)
+				if (i2c_smbus_read_i2c_block_data(client,
+				                           i, data->data + i)
+				                    != I2C_SMBUS_I2C_BLOCK_MAX) {
+					printk(KERN_WARNING "ddcmon.o: block read fail at 0x%.2x!\n", i);
+					goto DONE;
+				}
+		} else {
+			if (i2c_smbus_write_byte(client, 0)) {
+				printk(KERN_WARNING "ddcmon.o: read start fail at 0!\n");
+				goto DONE;
+			}
+			for (i = 0; i < DDCMON_SIZE; i++) {
+				j = i2c_smbus_read_byte(client);
+				if (j < 0) {
+					printk(KERN_WARNING "eeprom.o: read fail at 0x%.2x!\n", i);
+					goto DONE;
+				}
+				data->data[i] = (u8) j;
+			}
+		}
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+DONE:
+	up(&data->update_lock);
+}
+
+
+void ddcmon_idcall(struct i2c_client *client, int operation,
+		   int ctl_name, int *nrels_mag, long *results)
+{
+	struct ddcmon_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ddcmon_update_client(client);
+		results[0] = data->data[DDCMON_REG_MAN_ID + 1] |
+		             (data->data[DDCMON_REG_MAN_ID] << 8);
+		results[1] = data->data[DDCMON_REG_PROD_ID + 1] |
+		             (data->data[DDCMON_REG_PROD_ID] << 8);
+		*nrels_mag = 2;
+	}
+}
+
+void ddcmon_size(struct i2c_client *client, int operation,
+		 int ctl_name, int *nrels_mag, long *results)
+{
+	struct ddcmon_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ddcmon_update_client(client);
+		results[0] = data->data[DDCMON_REG_VERSIZE];
+		results[1] = data->data[DDCMON_REG_HORSIZE];
+		*nrels_mag = 2;
+	}
+}
+
+void ddcmon_sync(struct i2c_client *client, int operation,
+		    int ctl_name, int *nrels_mag, long *results)
+{
+	int i, j;
+	struct ddcmon_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ddcmon_update_client(client);
+		*nrels_mag = 4;
+		/* look for monitor limits entry */
+		for(i = DDCMON_REG_TIMBASE;
+		    i < DDCMON_REG_TIMBASE +
+		        (DDCMON_REG_TIMNUM * DDCMON_REG_TIMINCR);
+		    i += DDCMON_REG_TIMINCR) {
+			if (data->data[i] == 0x00
+			 && data->data[i + 1] == 0x00
+			 && data->data[i + 2] == 0x00
+			 && data->data[i + 3] == 0xfd) {
+				for(j = 0; j < 4; j++)
+					results[j] = data->data[i + j + 5];
+				return;
+			}
+		}
+		for(j = 0; j < 4; j++)
+			results[j] = 0;
+	}
+}
+
+void ddcmon_maxclock(struct i2c_client *client, int operation,
+		    int ctl_name, int *nrels_mag, long *results)
+{
+	int i;
+	struct ddcmon_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ddcmon_update_client(client);
+		*nrels_mag = 1;
+		/* look for monitor limits entry */
+		for(i = DDCMON_REG_TIMBASE;
+		    i < DDCMON_REG_TIMBASE +
+		        (DDCMON_REG_TIMNUM * DDCMON_REG_TIMINCR);
+		    i += DDCMON_REG_TIMINCR) {
+			if (data->data[i] == 0x00
+			 && data->data[i + 1] == 0x00
+			 && data->data[i + 2] == 0x00
+			 && data->data[i + 3] == 0xfd) {
+				results[0] = (data->data[i + 9] == 0xff ?
+				             0 : data->data[i + 9] * 10);
+				return;
+			}
+		}
+		results[0] = 0;
+	}
+}
+
+void ddcmon_timings(struct i2c_client *client, int operation,
+		    int ctl_name, int *nrels_mag, long *results)
+{
+	struct ddcmon_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ddcmon_update_client(client);
+		results[0] = data->data[DDCMON_REG_ESTABLISHED_TIMINGS] |
+		             (data->data[DDCMON_REG_ESTABLISHED_TIMINGS + 1] << 8) |
+		             (data->data[DDCMON_REG_ESTABLISHED_TIMINGS + 2] << 16);
+		*nrels_mag = 1;
+	}
+}
+
+void ddcmon_serial(struct i2c_client *client, int operation,
+		    int ctl_name, int *nrels_mag, long *results)
+{
+	struct ddcmon_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ddcmon_update_client(client);
+		results[0] = data->data[DDCMON_REG_SERIAL] |
+		             (data->data[DDCMON_REG_SERIAL + 1] << 8) |
+		             (data->data[DDCMON_REG_SERIAL + 2] << 16) |
+		             (data->data[DDCMON_REG_SERIAL + 3] << 24);
+		*nrels_mag = 1;
+	}
+}
+
+void ddcmon_time(struct i2c_client *client, int operation,
+		 int ctl_name, int *nrels_mag, long *results)
+{
+	struct ddcmon_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ddcmon_update_client(client);
+		results[0] = data->data[DDCMON_REG_YEAR] + 1990;
+		results[1] = data->data[DDCMON_REG_WEEK];
+		*nrels_mag = 2;
+	}
+}
+
+void ddcmon_edid(struct i2c_client *client, int operation,
+		 int ctl_name, int *nrels_mag, long *results)
+{
+	struct ddcmon_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ddcmon_update_client(client);
+		results[0] = data->data[DDCMON_REG_EDID_VER];
+		results[1] = data->data[DDCMON_REG_EDID_REV];
+		*nrels_mag = 2;
+	}
+}
+
+void ddcmon_gamma(struct i2c_client *client, int operation,
+		 int ctl_name, int *nrels_mag, long *results)
+{
+	struct ddcmon_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ddcmon_update_client(client);
+		results[0] = 100 + data->data[DDCMON_REG_GAMMA];
+		*nrels_mag = 1;
+	}
+}
+
+void ddcmon_dpms(struct i2c_client *client, int operation,
+		 int ctl_name, int *nrels_mag, long *results)
+{
+	struct ddcmon_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ddcmon_update_client(client);
+		results[0] = data->data[DDCMON_REG_DPMS_FLAGS];
+		*nrels_mag = 1;
+	}
+}
+
+void ddcmon_standard_timing(struct i2c_client *client, int operation,
+		 int ctl_name, int *nrels_mag, long *results)
+{
+	struct ddcmon_data *data = client->data;
+	int nr = ctl_name - DDCMON_SYSCTL_TIMING1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ddcmon_update_client(client);
+		/* If both bytes of the timing are 0x00 or 0x01, then the timing
+		   slot is unused. */
+		if ((data->data[DDCMON_REG_STANDARD_TIMINGS + nr * 2]
+		    | data->data[DDCMON_REG_STANDARD_TIMINGS + nr * 2 + 1]) & 0xfe) {
+			results[0] = (data->data[DDCMON_REG_STANDARD_TIMINGS + nr * 2] + 31) * 8;
+			switch (data->data[DDCMON_REG_STANDARD_TIMINGS + nr * 2 + 1] >> 6) {
+				/* We don't care about rounding issues there, it really
+				   should be OK without it. */
+				case 0x00:
+					results[1] = results[0]; /* unconfirmed */
+					break;
+				case 0x01:
+					results[1] = results[0] * 3 / 4;
+					break;
+				case 0x02:
+					results[1] = results[0] * 4 / 5;
+					break;
+				case 0x03:
+					results[1] = results[0] * 9 / 16;
+					break;
+			}
+			results[2] = (data->data[DDCMON_REG_STANDARD_TIMINGS + nr * 2 + 1] & 0x3f) + 60;
+		} else {
+			results[0] = 0;
+			results[1] = 0;
+			results[2] = 0;
+		}
+		*nrels_mag = 3;
+	}
+}
+
+static int __init sm_ddcmon_init(void)
+{
+	printk("ddcmon.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&ddcmon_driver);
+}
+
+static void __exit sm_ddcmon_exit(void)
+{
+	i2c_del_driver(&ddcmon_driver);
+}
+
+
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
+	      "Philip Edelbrock <phil@netroedge.com>, "
+	      "Mark Studebaker <mdsxyz123@yahoo.com> "
+		  "and Jean Delvare <khali@linux-fr.org>");
+MODULE_DESCRIPTION("DDCMON driver");
+
+module_init(sm_ddcmon_init);
+module_exit(sm_ddcmon_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/ds1621.c linux-2.4.27-leo/drivers/sensors/ds1621.c
--- linux-2.4.27/drivers/sensors/ds1621.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/ds1621.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,528 @@
+/*
+    ds1621.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Christian W. Zuckschwerdt  <zany@triq.net>  2000-11-23
+    based on lm75.c by Frodo Looijaard <frodol@dds.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* Supports DS1621. See doc/chips/ds1621 for details */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x48, 0x4f, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(ds1621);
+
+/* Many DS1621 constants specified below */
+
+/* Config register used for detection         */
+/*  7    6    5    4    3    2    1    0      */
+/* |Done|THF |TLF |NVB | 1  | 0  |POL |1SHOT| */
+#define DS1621_REG_CONFIG_MASK 0x0C
+#define DS1621_REG_CONFIG_VAL 0x08
+#define DS1621_REG_CONFIG_POLARITY 0x02
+#define DS1621_REG_CONFIG_1SHOT 0x01
+#define DS1621_REG_CONFIG_DONE 0x80
+
+/* Note: the done bit is always unset if continuous conversion is in progress.
+         We need to stop the continuous conversion or switch to single shot
+         before this bit becomes available!
+ */
+
+/* The DS1621 registers */
+#define DS1621_REG_TEMP 0xAA /* word, RO */
+#define DS1621_REG_TEMP_OVER 0xA1 /* word, RW */
+#define DS1621_REG_TEMP_HYST 0xA2 /* word, RW -- it's a low temp trigger */
+#define DS1621_REG_CONF 0xAC /* byte, RW */
+#define DS1621_REG_TEMP_COUNTER 0xA8 /* byte, RO */
+#define DS1621_REG_TEMP_SLOPE 0xA9 /* byte, RO */
+#define DS1621_COM_START 0xEE /* no data */
+#define DS1621_COM_STOP 0x22 /* no data */
+
+/* Conversions. Rounding and limit checking is only done on the TO_REG
+   variants. Note that you should be a bit careful with which arguments
+   these macros are called: arguments may be evaluated more than once.
+   Fixing this is just not worth it. */
+#define TEMP_FROM_REG(val) ((((val & 0x7fff) >> 7) * 5) | \
+                            ((val & 0x8000)?-256:0))
+#define TEMP_TO_REG(val)   (SENSORS_LIMIT((val<0 ? (0x200+((val)/5))<<7 : \
+                                          (((val) + 2) / 5) << 7),0,0xffff))
+#define ALARMS_FROM_REG(val) ((val) & \
+                              (DS1621_ALARM_TEMP_HIGH | DS1621_ALARM_TEMP_LOW))
+#define ITEMP_FROM_REG(val) ((((val & 0x7fff) >> 8)) | \
+                            ((val & 0x8000)?-256:0))
+
+/* Each client has this additional data */
+struct ds1621_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u16 temp, temp_over, temp_hyst;	/* Register values, word */
+	u8 conf;			/* Register encoding, combined */
+
+	char enable;	/* !=0 if we're expected to restart the conversion */
+	u8 temp_int, temp_counter, temp_slope;	/* Register values, byte */
+};
+
+static int ds1621_attach_adapter(struct i2c_adapter *adapter);
+static int ds1621_detect(struct i2c_adapter *adapter, int address,
+			 unsigned short flags, int kind);
+static void ds1621_init_client(struct i2c_client *client);
+static int ds1621_detach_client(struct i2c_client *client);
+
+static int ds1621_read_value(struct i2c_client *client, u8 reg);
+static int ds1621_write_value(struct i2c_client *client, u8 reg, u16 value);
+static void ds1621_temp(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void ds1621_alarms(struct i2c_client *client, int operation,
+			  int ctl_name, int *nrels_mag, long *results);
+static void ds1621_enable(struct i2c_client *client, int operation,
+			  int ctl_name, int *nrels_mag, long *results);
+static void ds1621_continuous(struct i2c_client *client, int operation,
+			      int ctl_name, int *nrels_mag, long *results);
+static void ds1621_polarity(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void ds1621_update_client(struct i2c_client *client);
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver ds1621_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "DS1621 sensor driver",
+	.id		= I2C_DRIVERID_DS1621,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= ds1621_attach_adapter,
+	.detach_client	= ds1621_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+#define DS1621_SYSCTL_TEMP 1200	/* Degrees Celcius * 10 */
+#define DS1621_SYSCTL_ALARMS 2001	/* bitvector */
+#define DS1621_ALARM_TEMP_HIGH 0x40
+#define DS1621_ALARM_TEMP_LOW 0x20
+#define DS1621_SYSCTL_ENABLE 2002
+#define DS1621_SYSCTL_CONTINUOUS 2003
+#define DS1621_SYSCTL_POLARITY 2004
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected DS1621. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized
+   when a new copy is allocated. */
+static ctl_table ds1621_dir_table_template[] = {
+	{DS1621_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_temp},
+	{DS1621_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_alarms},
+	{DS1621_SYSCTL_ENABLE, "enable", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_enable},
+	{DS1621_SYSCTL_CONTINUOUS, "continuous", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_continuous},
+	{DS1621_SYSCTL_POLARITY, "polarity", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_polarity},
+	{0}
+};
+
+static int ds1621_id = 0;
+
+static int ds1621_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, ds1621_detect);
+}
+
+/* This function is called by i2c_detect */
+int ds1621_detect(struct i2c_adapter *adapter, int address,
+		unsigned short flags, int kind)
+{
+	int i, conf;
+	struct i2c_client *new_client;
+	struct ds1621_data *data;
+	int err = 0;
+	const char *type_name, *client_name;
+
+	/* Make sure we aren't probing the ISA bus!! This is just a safety check
+	   at this moment; i2c_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		 ("ds1621.o: ds1621_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+				     I2C_FUNC_SMBUS_WORD_DATA |
+				     I2C_FUNC_SMBUS_WRITE_BYTE))
+		    goto ERROR0;
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access ds1621_{read,write}_value. */
+	if (!(data = kmalloc(sizeof(struct ds1621_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &ds1621_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. It is lousy. */
+	if (kind < 0) {
+		conf = i2c_smbus_read_byte_data(new_client,
+						DS1621_REG_CONF);
+		if ((conf & DS1621_REG_CONFIG_MASK)
+		    != DS1621_REG_CONFIG_VAL)
+			goto ERROR1;
+	}
+
+	/* Determine the chip type - only one kind supported! */
+	if (kind <= 0)
+		kind = ds1621;
+
+	if (kind == ds1621) {
+		type_name = "ds1621";
+		client_name = "DS1621 chip";
+	} else {
+#ifdef DEBUG
+		printk("ds1621.o: Internal error: unknown kind (%d)?!?",
+		       kind);
+#endif
+		goto ERROR1;
+	}
+
+	/* Fill in remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+
+	new_client->id = ds1621_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client, type_name,
+					ds1621_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	ds1621_init_client(new_client);
+	return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+      ERROR1:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int ds1621_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct ds1621_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+	   ("ds1621.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+
+/* All registers are word-sized, except for the configuration register.
+   DS1621 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+static int ds1621_read_value(struct i2c_client *client, u8 reg)
+{
+	if ((reg == DS1621_REG_CONF) || (reg == DS1621_REG_TEMP_COUNTER)
+	    || (reg == DS1621_REG_TEMP_SLOPE))
+		return i2c_smbus_read_byte_data(client, reg);
+	else
+		return swab16(i2c_smbus_read_word_data(client, reg));
+}
+
+/* All registers are word-sized, except for the configuration register.
+   DS1621 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+static int ds1621_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+	if ( (reg == DS1621_COM_START) || (reg == DS1621_COM_STOP) )
+		return i2c_smbus_write_byte(client, reg);
+	else
+	if ((reg == DS1621_REG_CONF) || (reg == DS1621_REG_TEMP_COUNTER)
+	    || (reg == DS1621_REG_TEMP_SLOPE))
+		return i2c_smbus_write_byte_data(client, reg, value);
+	else
+		return i2c_smbus_write_word_data(client, reg, swab16(value));
+}
+
+static void ds1621_init_client(struct i2c_client *client)
+{
+	int reg;
+
+	reg = ds1621_read_value(client, DS1621_REG_CONF);
+	/* start the continous conversion */
+	if(reg & 0x01)
+		ds1621_write_value(client, DS1621_REG_CONF, reg & 0xfe);
+}
+
+static void ds1621_update_client(struct i2c_client *client)
+{
+	struct ds1621_data *data = client->data;
+	u8 new_conf;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting ds1621 update\n");
+#endif
+
+		data->conf = ds1621_read_value(client, DS1621_REG_CONF);
+
+		data->temp = ds1621_read_value(client,
+					       DS1621_REG_TEMP);
+		data->temp_over = ds1621_read_value(client,
+		                                    DS1621_REG_TEMP_OVER);
+		data->temp_hyst = ds1621_read_value(client,
+						    DS1621_REG_TEMP_HYST);
+
+		/* wait for the DONE bit before reading extended values */
+
+		if (data->conf & DS1621_REG_CONFIG_DONE) {
+			data->temp_counter = ds1621_read_value(client,
+						     DS1621_REG_TEMP_COUNTER);
+			data->temp_slope = ds1621_read_value(client,
+						     DS1621_REG_TEMP_SLOPE);
+			data->temp_int = ITEMP_FROM_REG(data->temp);
+			/* restart the conversion */
+			if (data->enable)
+				ds1621_write_value(client, DS1621_COM_START, 0);
+		}
+
+		/* reset alarms if neccessary */
+		new_conf = data->conf;
+		if (data->temp < data->temp_over)
+			new_conf &= ~DS1621_ALARM_TEMP_HIGH;
+		if (data->temp > data->temp_hyst)
+			new_conf &= ~DS1621_ALARM_TEMP_LOW;
+		if (data->conf != new_conf)
+			ds1621_write_value(client, DS1621_REG_CONF,
+					   new_conf);
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+void ds1621_temp(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct ds1621_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		if (!(data->conf & DS1621_REG_CONFIG_DONE) ||
+		    (data->temp_counter > data->temp_slope) ||
+		    (data->temp_slope == 0)) {
+			*nrels_mag = 1;
+		} else {
+			*nrels_mag = 2;
+		}
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ds1621_update_client(client);
+		/* decide wether to calculate more precise temp */
+		if (!(data->conf & DS1621_REG_CONFIG_DONE) ||
+		    (data->temp_counter > data->temp_slope) ||
+		    (data->temp_slope == 0)) {
+			results[0] = TEMP_FROM_REG(data->temp_over);
+			results[1] = TEMP_FROM_REG(data->temp_hyst);
+			results[2] = TEMP_FROM_REG(data->temp);
+		} else {
+			results[0] = TEMP_FROM_REG(data->temp_over)*10;
+			results[1] = TEMP_FROM_REG(data->temp_hyst)*10;
+			results[2] = data->temp_int * 100 - 25 +
+				((data->temp_slope - data->temp_counter) *
+				 100 / data->temp_slope);
+		}
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_over = TEMP_TO_REG(results[0]);
+			ds1621_write_value(client, DS1621_REG_TEMP_OVER,
+					 data->temp_over);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_hyst = TEMP_TO_REG(results[1]);
+			ds1621_write_value(client, DS1621_REG_TEMP_HYST,
+					 data->temp_hyst);
+		}
+	}
+}
+
+void ds1621_alarms(struct i2c_client *client, int operation, int ctl_name,
+		   int *nrels_mag, long *results)
+{
+	struct ds1621_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ds1621_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->conf);
+		*nrels_mag = 1;
+	}
+}
+
+void ds1621_enable(struct i2c_client *client, int operation, int ctl_name,
+		   int *nrels_mag, long *results)
+{
+	/* If you really screw up your chip (like I did) this is */
+	/* sometimes needed to (re)start the continous conversion */
+	/* there is no data to read so this might hang your SMBus! */
+
+	struct ds1621_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ds1621_update_client(client);
+		results[0] = !(data->conf & DS1621_REG_CONFIG_DONE);
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			if (results[0]) {
+				ds1621_write_value(client, DS1621_COM_START, 0);
+				data->enable=1;
+			} else {
+				ds1621_write_value(client, DS1621_COM_STOP, 0);
+				data->enable=0;
+			}
+		} else {
+			ds1621_write_value(client, DS1621_COM_START, 0);
+			data->enable=1;
+		}
+	}
+}
+
+void ds1621_continuous(struct i2c_client *client, int operation, int ctl_name,
+		       int *nrels_mag, long *results)
+{
+	struct ds1621_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ds1621_update_client(client);
+		results[0] = !(data->conf & DS1621_REG_CONFIG_1SHOT);
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		ds1621_update_client(client);
+		if (*nrels_mag >= 1) {
+			if (results[0]) {
+				ds1621_write_value(client, DS1621_REG_CONF,
+						   data->conf & ~DS1621_REG_CONFIG_1SHOT);
+			} else {
+				ds1621_write_value(client, DS1621_REG_CONF,
+						   data->conf | DS1621_REG_CONFIG_1SHOT);
+			}
+		} else {
+			ds1621_write_value(client, DS1621_REG_CONF,
+					   data->conf & ~DS1621_REG_CONFIG_1SHOT);
+		}
+	}
+}
+
+void ds1621_polarity(struct i2c_client *client, int operation, int ctl_name,
+		     int *nrels_mag, long *results)
+{
+	struct ds1621_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		ds1621_update_client(client);
+		results[0] = !(!(data->conf & DS1621_REG_CONFIG_POLARITY));
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		ds1621_update_client(client);
+		if (*nrels_mag >= 1) {
+			if (results[0]) {
+				ds1621_write_value(client, DS1621_REG_CONF,
+						   data->conf | DS1621_REG_CONFIG_POLARITY);
+			} else {
+				ds1621_write_value(client, DS1621_REG_CONF,
+						   data->conf & ~DS1621_REG_CONFIG_POLARITY);
+			}
+		}
+	}
+}
+
+static int __init sm_ds1621_init(void)
+{
+	printk("ds1621.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&ds1621_driver);
+}
+
+static void __exit sm_ds1621_exit(void)
+{
+	i2c_del_driver(&ds1621_driver);
+}
+
+
+
+MODULE_AUTHOR("Christian W. Zuckschwerdt <zany@triq.net>");
+MODULE_DESCRIPTION("DS1621 driver");
+
+module_init(sm_ds1621_init);
+module_exit(sm_ds1621_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/eeprom.c linux-2.4.27-leo/drivers/sensors/eeprom.c
--- linux-2.4.27/drivers/sensors/eeprom.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/eeprom.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,418 @@
+/*
+    eeprom.c - Part of lm_sensors, Linux kernel modules for hardware
+               monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl> and
+    Philip Edelbrock <phil@netroedge.com>
+
+    2003-08-18  Jean Delvare <khali@linux-fr.org>
+    Divide the eeprom in 2-row (arbitrary) slices. This significantly
+    speeds sensors up, as well as various scripts using the eeprom
+    module.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include <linux/sched.h> /* for capable() */
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x50, 0x57, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(eeprom);
+
+static int checksum = 0;
+MODULE_PARM(checksum, "i");
+MODULE_PARM_DESC(checksum,
+		 "Only accept eeproms whose checksum is correct");
+
+
+/* Many constants specified below */
+
+/* EEPROM registers */
+#define EEPROM_REG_CHECKSUM 0x3f
+
+/* possible natures */
+#define NATURE_UNKNOWN 0
+#define NATURE_VAIO 1
+
+/* Size of EEPROM in bytes */
+#define EEPROM_SIZE 256
+
+/* Each client has this additional data */
+struct eeprom_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	u8 valid;		/* bitfield, bit!=0 if slice is valid */
+	unsigned long last_updated[8];	/* In jiffies, 8 slices */
+
+	u8 data[EEPROM_SIZE];	/* Register values */
+	u8 nature;
+};
+
+
+static int eeprom_attach_adapter(struct i2c_adapter *adapter);
+static int eeprom_detect(struct i2c_adapter *adapter, int address,
+			 unsigned short flags, int kind);
+static int eeprom_detach_client(struct i2c_client *client);
+
+#if 0
+static int eeprom_write_value(struct i2c_client *client, u8 reg,
+			      u8 value);
+#endif
+
+static void eeprom_contents(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void eeprom_update_client(struct i2c_client *client, u8 slice);
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver eeprom_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "EEPROM READER",
+	.id		= I2C_DRIVERID_EEPROM,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= eeprom_attach_adapter,
+	.detach_client	= eeprom_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+
+#define EEPROM_SYSCTL1 1000
+#define EEPROM_SYSCTL2 1001
+#define EEPROM_SYSCTL3 1002
+#define EEPROM_SYSCTL4 1003
+#define EEPROM_SYSCTL5 1004
+#define EEPROM_SYSCTL6 1005
+#define EEPROM_SYSCTL7 1006
+#define EEPROM_SYSCTL8 1007
+#define EEPROM_SYSCTL9 1008
+#define EEPROM_SYSCTL10 1009
+#define EEPROM_SYSCTL11 1010
+#define EEPROM_SYSCTL12 1011
+#define EEPROM_SYSCTL13 1012
+#define EEPROM_SYSCTL14 1013
+#define EEPROM_SYSCTL15 1014
+#define EEPROM_SYSCTL16 1015
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected EEPROM. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized
+   when a new copy is allocated. */
+static ctl_table eeprom_dir_table_template[] = {
+	{EEPROM_SYSCTL1, "00", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL2, "10", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL3, "20", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL4, "30", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL5, "40", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL6, "50", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL7, "60", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL8, "70", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL9, "80", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL10, "90", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL11, "a0", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL12, "b0", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL13, "c0", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL14, "d0", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL15, "e0", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{EEPROM_SYSCTL16, "f0", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &eeprom_contents},
+	{0}
+};
+
+static int eeprom_id = 0;
+
+static int eeprom_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, eeprom_detect);
+}
+
+/* This function is called by i2c_detect */
+int eeprom_detect(struct i2c_adapter *adapter, int address,
+		  unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct eeprom_data *data;
+	int err = 0;
+	const char *type_name, *client_name;
+
+	/* Make sure we aren't probing the ISA bus!! This is just a safety check
+	   at this moment; i2c_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("eeprom.o: eeprom_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		    goto ERROR0;
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access eeprom_{read,write}_value. */
+	if (!(data = kmalloc(sizeof(struct eeprom_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	memset(data->data, 0xff, EEPROM_SIZE);
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &eeprom_driver;
+	new_client->flags = 0;
+
+	/* prevent 24RF08 corruption */
+	i2c_smbus_write_quick(new_client, 0);
+
+	/* Now, we do the remaining detection. It is not there, unless you force
+	   the checksum to work out. */
+	if (checksum) {
+		int cs = 0;
+		for (i = 0; i <= 0x3e; i++)
+			cs += i2c_smbus_read_byte_data(new_client, i);
+		cs &= 0xff;
+		if (i2c_smbus_read_byte_data
+		    (new_client, EEPROM_REG_CHECKSUM) != cs)
+			goto ERROR1;
+	}
+
+	data->nature = NATURE_UNKNOWN;
+	/* Detect the Vaio nature of EEPROMs.
+	   We use the "PCG-" prefix as the signature. */
+	if (address == 0x57)
+	{
+		if (i2c_smbus_read_byte_data(new_client, 0x80) == 'P'
+		 && i2c_smbus_read_byte_data(new_client, 0x81) == 'C'
+		 && i2c_smbus_read_byte_data(new_client, 0x82) == 'G'
+		 && i2c_smbus_read_byte_data(new_client, 0x83) == '-')
+			data->nature = NATURE_VAIO;
+	}
+
+	/* Determine the chip type - only one kind supported! */
+	if (kind <= 0)
+		kind = eeprom;
+
+	if (kind == eeprom) {
+		type_name = "eeprom";
+		client_name = "EEPROM chip";
+	} else {
+#ifdef DEBUG
+		printk("eeprom.o: Internal error: unknown kind (%d)?!?",
+		       kind);
+#endif
+		goto ERROR1;
+	}
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+
+	new_client->id = eeprom_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client, type_name,
+					eeprom_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+      ERROR1:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int eeprom_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct eeprom_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("eeprom.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+
+#if 0
+/* No writes yet (PAE) */
+static int eeprom_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+#endif
+
+static void eeprom_update_client(struct i2c_client *client, u8 slice)
+{
+	struct eeprom_data *data = client->data;
+	int i, j;
+
+	down(&data->update_lock);
+
+	if (!(data->valid & (1 << slice))
+	 || (jiffies - data->last_updated[slice] > 300 * HZ)
+	 || (jiffies < data->last_updated[slice])) {
+
+#ifdef DEBUG
+		printk("Starting eeprom update, slice %u\n", slice);
+#endif
+
+		if (i2c_check_functionality(client->adapter,
+		                            I2C_FUNC_SMBUS_READ_I2C_BLOCK))
+		{
+			for (i = slice << 5; i < (slice + 1) << 5;
+			                            i += I2C_SMBUS_I2C_BLOCK_MAX)
+				if (i2c_smbus_read_i2c_block_data(client,
+				                           i, data->data + i)
+				                    != I2C_SMBUS_I2C_BLOCK_MAX) {
+					printk(KERN_WARNING "eeprom.o: block read fail at 0x%.2x!\n", i);
+					goto DONE;
+				}
+		} else {
+			if (i2c_smbus_write_byte(client, slice << 5)) {
+				printk(KERN_WARNING "eeprom.o: read start fail at 0x%.2x!\n", slice << 5);
+				goto DONE;
+			}
+			for (i = slice << 5; i < (slice + 1) << 5; i++) {
+				j = i2c_smbus_read_byte(client);
+				if (j < 0) {
+					printk(KERN_WARNING "eeprom.o: read fail at 0x%.2x!\n", i);
+					goto DONE;
+				}
+				data->data[i] = (u8) j;
+			}
+		}
+		data->last_updated[slice] = jiffies;
+		data->valid |= (1 << slice);
+	}
+DONE:
+	up(&data->update_lock);
+}
+
+
+void eeprom_contents(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	int i;
+	int nr = ctl_name - EEPROM_SYSCTL1;
+	struct eeprom_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		eeprom_update_client(client, nr >> 1);
+		/* Hide Vaio security settings to regular users */
+		if (nr == 0 && data->nature == NATURE_VAIO
+		 && !capable(CAP_SYS_ADMIN))
+			for (i = 0; i < 16; i++)
+				results[i] = 0;
+		else
+			for (i = 0; i < 16; i++)
+				results[i] = data->data[i + nr * 16];
+#ifdef DEBUG
+		printk("eeprom.o: 0x%X EEPROM contents (row %d):",
+		       client->addr, nr + 1);
+		if (nr == 0 && data->nature == NATURE_VAIO)
+		 	printk(" <hidden for security reasons>\n");
+		else {
+			for (i = 0; i < 16; i++)
+				printk(" 0x%02X", data->data[i + nr * 16]);
+			printk("\n");
+		}
+#endif
+		*nrels_mag = 16;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+
+/* No writes to the EEPROM (yet, anyway) (PAE) */
+		printk("eeprom.o: No writes to EEPROMs supported!\n");
+	}
+}
+
+static int __init sm_eeprom_init(void)
+{
+	printk("eeprom.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&eeprom_driver);
+}
+
+static void __exit sm_eeprom_exit(void)
+{
+	i2c_del_driver(&eeprom_driver);
+}
+
+
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("EEPROM driver");
+
+module_init(sm_eeprom_init);
+module_exit(sm_eeprom_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/fscpos.c linux-2.4.27-leo/drivers/sensors/fscpos.c
--- linux-2.4.27/drivers/sensors/fscpos.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/fscpos.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,690 @@
+/*
+    fscpos.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 2001 Hermann Jung <hej@odn.de>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* 
+    fujitsu siemens poseidon chip, 
+    module based on lm80.c 
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+    and Philip Edelbrock <phil@netroedge.com>
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x73, SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(fscpos);
+
+/* The FSCPOS registers */
+
+/* chip identification */
+#define FSCPOS_REG_IDENT_0    0x00
+#define FSCPOS_REG_IDENT_1    0x01
+#define FSCPOS_REG_IDENT_2    0x02
+#define FSCPOS_REG_REVISION   0x03
+
+/* global control and status */
+#define FSCPOS_REG_EVENT_STATE  0x04
+#define FSCPOS_REG_CONTROL       0x05
+
+/* watchdog */
+#define FSCPOS_REG_WDOG_PRESET      0x28
+#define FSCPOS_REG_WDOG_STATE       0x23
+#define FSCPOS_REG_WDOG_CONTROL     0x21
+
+/* fan 0  */
+#define FSCPOS_REG_FAN0_MIN      0x55
+#define FSCPOS_REG_FAN0_ACT      0x0e
+#define FSCPOS_REG_FAN0_STATE   0x0d
+#define FSCPOS_REG_FAN0_RIPPLE   0x0f
+
+/* fan 1  */
+#define FSCPOS_REG_FAN1_MIN      0x65
+#define FSCPOS_REG_FAN1_ACT      0x6b
+#define FSCPOS_REG_FAN1_STATE   0x62
+#define FSCPOS_REG_FAN1_RIPPLE   0x6f
+
+/* fan 2  */
+/* min speed fan2 not supported */
+#define FSCPOS_REG_FAN2_ACT      0xab
+#define FSCPOS_REG_FAN2_STATE   0xa2
+#define FSCPOS_REG_FAN2_RIPPLE   0x0af
+
+/* voltage supervision */
+#define FSCPOS_REG_VOLT_12       0x45
+#define FSCPOS_REG_VOLT_5        0x42
+#define FSCPOS_REG_VOLT_BATT     0x48
+
+/* temperatures */
+/* sensor 0 */
+#define FSCPOS_REG_TEMP0_ACT       0x64
+#define FSCPOS_REG_TEMP0_STATE    0x71
+
+/* sensor 1 */
+#define FSCPOS_REG_TEMP1_ACT       0x32
+#define FSCPOS_REG_TEMP1_STATE    0x81
+
+/* sensor 2 */
+#define FSCPOS_REG_TEMP2_ACT       0x35
+#define FSCPOS_REG_TEMP2_STATE    0x91
+
+
+
+
+/* Conversions. Rounding and limit checking is only done on the TO_REG
+   variants. Note that you should be a bit careful with which arguments
+   these macros are called: arguments may be evaluated more than once.
+   Fixing this is just not worth it. */
+
+#define IN_TO_REG(val,nr) (SENSORS_LIMIT((val),0,255))
+#define IN_FROM_REG(val,nr) (val)
+
+/* Initial limits */
+
+/* For each registered FSCPOS, we need to keep some data in memory. That
+   data is pointed to by fscpos_list[NR]->data. The structure itself is
+   dynamically allocated, at the same time when a new fscpos client is
+   allocated. */
+struct fscpos_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8  revision;        /* revision of chip */
+	u8  global_event;    /* global event status */
+	u8  global_control;  /* global control register */
+	u8  watchdog[3];     /* watchdog */
+	u8  volt[3];         /* 12, 5, battery current */ 
+	u8  temp_act[3];     /* temperature */
+	u8  temp_status[3];  /* status of sensor */
+	u8  fan_act[3];      /* fans revolutions per second */
+	u8  fan_status[3];   /* fan status */
+	u8  fan_min[3];      /* fan min value for rps */
+	u8  fan_ripple[3];   /* divider for rps */
+};
+
+
+static int fscpos_attach_adapter(struct i2c_adapter *adapter);
+static int fscpos_detect(struct i2c_adapter *adapter, int address,
+		       unsigned short flags, int kind);
+static int fscpos_detach_client(struct i2c_client *client);
+
+static int fscpos_read_value(struct i2c_client *client, u8 register);
+static int fscpos_write_value(struct i2c_client *client, u8 register,
+			    u8 value);
+static void fscpos_update_client(struct i2c_client *client);
+static void fscpos_init_client(struct i2c_client *client);
+
+
+static void fscpos_in(struct i2c_client *client, int operation, int ctl_name,
+		    	int *nrels_mag, long *results);
+static void fscpos_fan(struct i2c_client *client, int operation,
+		     	int ctl_name, int *nrels_mag, long *results);
+static void fscpos_fan_internal(struct i2c_client *client, int operation,
+		     	int ctl_name, int *nrels_mag, long *results, 
+		     	int nr, int reg_state, int reg_min, int res_ripple);
+static void fscpos_temp(struct i2c_client *client, int operation,
+		      	int ctl_name, int *nrels_mag, long *results);
+static void fscpos_volt(struct i2c_client *client, int operation,
+		      	int ctl_name, int *nrels_mag, long *results);
+static void fscpos_wdog(struct i2c_client *client, int operation,
+		      	int ctl_name, int *nrels_mag, long *results);
+
+static int fscpos_id = 0;
+
+static struct i2c_driver fscpos_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "FSCPOS sensor driver",
+	.id		= I2C_DRIVERID_FSCPOS,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= fscpos_attach_adapter,
+	.detach_client	= fscpos_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+#define FSCPOS_SYSCTL_VOLT0    1000       /* 12 volt supply */
+#define FSCPOS_SYSCTL_VOLT1    1001       /* 5 volt supply */
+#define FSCPOS_SYSCTL_VOLT2    1002       /* batterie voltage*/
+#define FSCPOS_SYSCTL_FAN0     1101       /* state, min, ripple, actual value fan 0 */
+#define FSCPOS_SYSCTL_FAN1     1102       /* state, min, ripple, actual value fan 1 */
+#define FSCPOS_SYSCTL_FAN2     1103       /* state, min, ripple, actual value fan 2 */
+#define FSCPOS_SYSCTL_TEMP0    1201       /* state and value of sensor 0, cpu die */
+#define FSCPOS_SYSCTL_TEMP1    1202       /* state and value of sensor 1, motherboard */
+#define FSCPOS_SYSCTL_TEMP2    1203       /* state and value of sensor 2, chassis */
+#define FSCPOS_SYSCTL_REV     2000        /* Revision */
+#define FSCPOS_SYSCTL_EVENT   2001        /* global event status */
+#define FSCPOS_SYSCTL_CONTROL 2002        /* global control byte */
+#define FSCPOS_SYSCTL_WDOG     2003       /* state, min, ripple, actual value fan 2 */
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected FSCPOS. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized 
+   when a new copy is allocated. */
+static ctl_table fscpos_dir_table_template[] = {
+	{FSCPOS_SYSCTL_REV, "rev", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_in},
+	{FSCPOS_SYSCTL_EVENT, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_in},
+	{FSCPOS_SYSCTL_CONTROL, "control", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_in},
+	{FSCPOS_SYSCTL_TEMP0, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_temp},
+	{FSCPOS_SYSCTL_TEMP1, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_temp},
+	{FSCPOS_SYSCTL_TEMP2, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_temp},
+	{FSCPOS_SYSCTL_VOLT0, "in0", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_volt},
+	{FSCPOS_SYSCTL_VOLT1, "in1", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_volt},
+	{FSCPOS_SYSCTL_VOLT2, "in2", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_volt},
+	{FSCPOS_SYSCTL_FAN0, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_fan},
+	{FSCPOS_SYSCTL_FAN1, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_fan},
+	{FSCPOS_SYSCTL_FAN2, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_fan},
+	{FSCPOS_SYSCTL_WDOG, "wdog", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscpos_wdog},
+	{0}
+};
+
+static int fscpos_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, fscpos_detect);
+}
+
+int fscpos_detect(struct i2c_adapter *adapter, int address,
+		unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct fscpos_data *data;
+	int err = 0;
+	const char *type_name, *client_name;
+
+	/* Make sure we aren't probing the ISA bus!! This is just a safety check
+	   at this moment; i2c_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("fscpos.o: fscpos_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		goto ERROR0;
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access fscpos_{read,write}_value. */
+	if (!(data = kmalloc(sizeof(struct fscpos_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &fscpos_driver;
+	new_client->flags = 0;
+
+	/* Do the remaining detection unless force or force_fscpos parameter */
+	if (kind < 0) {
+		if (fscpos_read_value(new_client, FSCPOS_REG_IDENT_0) != 0x50)
+			goto ERROR1;
+		if (fscpos_read_value(new_client, FSCPOS_REG_IDENT_1) != 0x45)
+			goto ERROR1;
+		if (fscpos_read_value(new_client, FSCPOS_REG_IDENT_2) != 0x47)
+			goto ERROR1;
+	}
+
+	kind = fscpos;
+
+	type_name = "fscpos";
+	client_name = "fsc poseidon chip";
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+
+	new_client->id = fscpos_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client, type_name,
+					fscpos_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	fscpos_init_client(new_client);
+	return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+      ERROR1:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int fscpos_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct fscpos_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("fscpos.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+static int fscpos_read_value(struct i2c_client *client, u8 reg)
+{
+#ifdef DEBUG
+	printk("fscpos: read reg 0x%02x\n",reg);
+#endif
+	return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int fscpos_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+#ifdef DEBUG
+	printk("fscpos: write reg 0x%02x, val 0x%02x\n",reg, value);
+#endif
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* Called when we have found a new FSCPOS. It should set limits, etc. */
+static void fscpos_init_client(struct i2c_client *client)
+{
+	struct fscpos_data *data = client->data;
+
+	/* read revision from chip */
+	data->revision =  fscpos_read_value(client,FSCPOS_REG_REVISION);
+	/* setup missing fan2_min value */
+	data->fan_min[2] = 0xff;
+}
+
+static void fscpos_update_client(struct i2c_client *client)
+{
+	struct fscpos_data *data = client->data;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > 2 * HZ) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting fscpos update\n");
+#endif
+		data->temp_act[0] = fscpos_read_value(client, FSCPOS_REG_TEMP0_ACT);
+		data->temp_act[1] = fscpos_read_value(client, FSCPOS_REG_TEMP1_ACT);
+		data->temp_act[2] = fscpos_read_value(client, FSCPOS_REG_TEMP2_ACT);
+		data->temp_status[0] = fscpos_read_value(client, FSCPOS_REG_TEMP0_STATE);
+		data->temp_status[1] = fscpos_read_value(client, FSCPOS_REG_TEMP1_STATE);
+		data->temp_status[2] = fscpos_read_value(client, FSCPOS_REG_TEMP2_STATE);
+
+		data->volt[0] = fscpos_read_value(client, FSCPOS_REG_VOLT_12);
+		data->volt[1] = fscpos_read_value(client, FSCPOS_REG_VOLT_5);
+		data->volt[2] = fscpos_read_value(client, FSCPOS_REG_VOLT_BATT);
+
+		data->fan_act[0] = fscpos_read_value(client, FSCPOS_REG_FAN0_ACT);
+		data->fan_act[1] = fscpos_read_value(client, FSCPOS_REG_FAN1_ACT);
+		data->fan_act[2] = fscpos_read_value(client, FSCPOS_REG_FAN2_ACT);
+		data->fan_status[0] = fscpos_read_value(client, FSCPOS_REG_FAN0_STATE);
+		data->fan_status[1] = fscpos_read_value(client, FSCPOS_REG_FAN1_STATE);
+		data->fan_status[2] = fscpos_read_value(client, FSCPOS_REG_FAN2_STATE);
+		data->fan_min[0] = fscpos_read_value(client, FSCPOS_REG_FAN0_MIN);
+		data->fan_min[1] = fscpos_read_value(client, FSCPOS_REG_FAN1_MIN);
+		/* fan2_min is not supported */
+		data->fan_ripple[0] = fscpos_read_value(client, FSCPOS_REG_FAN0_RIPPLE);
+		data->fan_ripple[1] = fscpos_read_value(client, FSCPOS_REG_FAN1_RIPPLE);
+		data->fan_ripple[2] = fscpos_read_value(client, FSCPOS_REG_FAN2_RIPPLE);
+
+		data->watchdog[0] = fscpos_read_value(client, FSCPOS_REG_WDOG_PRESET);
+		data->watchdog[1] = fscpos_read_value(client, FSCPOS_REG_WDOG_STATE);
+		data->watchdog[2] = fscpos_read_value(client, FSCPOS_REG_WDOG_CONTROL);
+
+		data->global_event = fscpos_read_value(client, FSCPOS_REG_EVENT_STATE);
+
+                data->last_updated = jiffies;
+                data->valid = 1;                 
+	}
+
+	up(&data->update_lock);
+}
+
+
+/* The next few functions are the call-back functions of the /proc/sys and
+   sysctl files. Which function is used is defined in the ctl_table in
+   the extra1 field.
+   Each function must return the magnitude (power of 10 to divide the date
+   with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must
+   put a maximum of *nrels elements in results reflecting the data of this
+   file, and set *nrels to the number it actually put in it, if operation==
+   SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from
+   results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE.
+   Note that on SENSORS_PROC_REAL_READ, I do not check whether results is
+   large enough (by checking the incoming value of *nrels). This is not very
+   good practice, but as long as you put less than about 5 values in results,
+   you can assume it is large enough. */
+void fscpos_in(struct i2c_client *client, int operation, int ctl_name,
+	     int *nrels_mag, long *results)
+{
+	struct fscpos_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		fscpos_update_client(client);
+		switch(ctl_name) {
+			case FSCPOS_SYSCTL_REV:
+				results[0] = data->revision ;
+				break;
+			case FSCPOS_SYSCTL_EVENT:
+				results[0] = data->global_event & 0x1f;
+				break;
+			case FSCPOS_SYSCTL_CONTROL:
+				results[0] = data->global_control & 0x01;
+				break;
+			default:
+				printk("fscpos: ctl_name %d not supported\n",
+					ctl_name);
+				*nrels_mag = 0;
+				return;
+		}
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if((ctl_name == FSCPOS_SYSCTL_CONTROL) && (*nrels_mag >= 1)) {
+			data->global_control = (results[0] & 0x01);
+			printk("fscpos: writing 0x%02x to global_control\n",
+				data->global_control);
+			fscpos_write_value(client,FSCPOS_REG_CONTROL,
+				data->global_control);
+		}
+		else
+			printk("fscpos: writing to chip not supported\n");
+	}
+}
+
+#define TEMP_FROM_REG(val)    (val-128)
+
+
+void fscpos_temp(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct fscpos_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		fscpos_update_client(client);
+		switch(ctl_name) {
+			case FSCPOS_SYSCTL_TEMP0:
+				results[0] = data->temp_status[0] & 0x03;
+				results[1] = TEMP_FROM_REG(data->temp_act[0]);
+				break;
+			case FSCPOS_SYSCTL_TEMP1:
+				results[0] = data->temp_status[1] & 0x03;
+				results[1] = TEMP_FROM_REG(data->temp_act[1]);
+				break;
+			case FSCPOS_SYSCTL_TEMP2:
+				results[0] = data->temp_status[2] & 0x03;
+				results[1] = TEMP_FROM_REG(data->temp_act[2]);
+				break;
+			default:
+				printk("fscpos: ctl_name %d not supported\n",
+					ctl_name);
+				*nrels_mag = 0;
+				return;
+		}
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if(*nrels_mag >= 1) {
+			switch(ctl_name) {
+				case FSCPOS_SYSCTL_TEMP0:
+					data->temp_status[0] = 
+						(data->temp_status[0] & ~0x02) 
+						| (results[0] & 0x02);
+					printk("fscpos: writing value 0x%02x "
+						"to temp0_status\n",
+						data->temp_status[0]);
+					fscpos_write_value(client,
+						FSCPOS_REG_TEMP0_STATE,
+						data->temp_status[0] & 0x02);
+					break;
+				case FSCPOS_SYSCTL_TEMP1:
+					data->temp_status[1] = (data->temp_status[1] & ~0x02) | (results[0] & 0x02);
+					printk("fscpos: writing value 0x%02x to temp1_status\n", data->temp_status[1]);
+					fscpos_write_value(client,FSCPOS_REG_TEMP1_STATE,
+						data->temp_status[1] & 0x02);
+					break;
+				case FSCPOS_SYSCTL_TEMP2:
+					data->temp_status[2] = (data->temp_status[2] & ~0x02) | (results[0] & 0x02);
+					printk("fscpos: writing value 0x%02x to temp2_status\n", data->temp_status[2]);
+					fscpos_write_value(client,FSCPOS_REG_TEMP2_STATE,
+						data->temp_status[2] & 0x02);
+					break;
+				default:
+					printk("fscpos: ctl_name %d not supported\n",ctl_name);
+			}
+		}
+		else
+			printk("fscpos: writing to chip not supported\n");
+	}
+}
+
+#define VOLT_FROM_REG(val,mult)    (val*mult/255)
+
+void fscpos_volt(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct fscpos_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		fscpos_update_client(client);
+		switch(ctl_name) {
+			case FSCPOS_SYSCTL_VOLT0:
+				results[0] = VOLT_FROM_REG(data->volt[0],1420);
+				break;
+			case FSCPOS_SYSCTL_VOLT1:
+				results[0] = VOLT_FROM_REG(data->volt[1],660);
+				break;
+			case FSCPOS_SYSCTL_VOLT2:
+				results[0] = VOLT_FROM_REG(data->volt[2],330);
+				break;
+			default:
+				printk("fscpos: ctl_name %d not supported\n",
+					ctl_name);
+				*nrels_mag = 0;
+				return;
+		}
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+			printk("fscpos: writing to chip not supported\n");
+	}
+}
+
+void fscpos_fan(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+
+	switch(ctl_name) {
+		case FSCPOS_SYSCTL_FAN0:
+			fscpos_fan_internal(client,operation,ctl_name,nrels_mag,results,
+				0,FSCPOS_REG_FAN0_STATE,FSCPOS_REG_FAN0_MIN,
+				FSCPOS_REG_FAN0_RIPPLE);
+			break;
+		case FSCPOS_SYSCTL_FAN1:
+			fscpos_fan_internal(client,operation,ctl_name,nrels_mag,results,
+				1,FSCPOS_REG_FAN1_STATE,FSCPOS_REG_FAN1_MIN,
+				FSCPOS_REG_FAN1_RIPPLE);
+			break;
+		case FSCPOS_SYSCTL_FAN2:
+			fscpos_fan_internal(client,operation,ctl_name,nrels_mag,results,
+				2,FSCPOS_REG_FAN2_STATE,0xff,
+				FSCPOS_REG_FAN2_RIPPLE);
+			break;
+		default:
+			printk("fscpos: illegal fan nr %d\n",ctl_name);
+	}
+}
+			
+#define RPM_FROM_REG(val)   (val*60)
+
+void fscpos_fan_internal(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results, int nr,
+	       int reg_state, int reg_min, int reg_ripple )
+{
+	struct fscpos_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		fscpos_update_client(client);
+		results[0] = data->fan_status[nr] & 0x04;
+		results[1] = data->fan_min[nr];
+		results[2] = data->fan_ripple[nr] & 0x03;
+		results[3] = RPM_FROM_REG(data->fan_act[nr]);
+		*nrels_mag = 4;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if(*nrels_mag >= 1) {
+			data->fan_status[nr] = results[0] & 0x04;
+			printk("fscpos: writing value 0x%02x to fan%d_status\n",
+				data->fan_status[nr],nr);
+			fscpos_write_value(client,reg_state,
+				data->fan_status[nr]);
+		}
+		if((*nrels_mag >= 2) && (nr < 2)) {  
+			/* minimal speed for fan2 not supported */
+			data->fan_min[nr] = results[1];
+			printk("fscpos: writing value 0x%02x to fan%d_min\n",
+				data->fan_min[nr],nr);
+			fscpos_write_value(client,reg_min,
+				data->fan_min[nr]);
+		}
+		if(*nrels_mag >= 3) {
+			if((results[2] & 0x03) == 0) {
+				printk("fscpos: fan%d ripple 0 not allowed\n",nr);
+				return;
+			}
+			data->fan_ripple[nr] = results[2] & 0x03;
+			printk("fscpos: writing value 0x%02x to fan%d_ripple\n",
+				data->fan_ripple[nr],nr);
+			fscpos_write_value(client,reg_ripple,
+				data->fan_ripple[nr]);
+		}	
+	}
+}
+
+void fscpos_wdog(struct i2c_client *client, int operation, int ctl_name,
+	     int *nrels_mag, long *results)
+{
+	struct fscpos_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		fscpos_update_client(client);
+		results[0] = data->watchdog[0] ;
+		results[1] = data->watchdog[1] & 0x02;
+		results[2] = data->watchdog[2] & 0xb0;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->watchdog[0] = results[0] & 0xff;
+			printk("fscpos: writing value 0x%02x to wdog_preset\n",
+				data->watchdog[0]); 
+			fscpos_write_value(client,FSCPOS_REG_WDOG_PRESET,
+				data->watchdog[0]);
+		} 
+		if (*nrels_mag >= 2) {
+			data->watchdog[1] = results[1] & 0x02;
+			printk("fscpos: writing value 0x%02x to wdog_state\n",
+				data->watchdog[1]); 
+			fscpos_write_value(client,FSCPOS_REG_WDOG_STATE,
+				data->watchdog[1]);
+		}
+		if (*nrels_mag >= 3) {
+			data->watchdog[2] = results[2] & 0xb0;
+			printk("fscpos: writing value 0x%02x to wdog_control\n",
+				data->watchdog[2]); 
+			fscpos_write_value(client,FSCPOS_REG_WDOG_CONTROL,
+				data->watchdog[2]);
+		}
+	}
+}
+
+static int __init sm_fscpos_init(void)
+{
+	printk("fscpos.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&fscpos_driver);
+}
+
+static void __exit sm_fscpos_exit(void)
+{
+	i2c_del_driver(&fscpos_driver);
+}
+
+
+
+MODULE_AUTHOR
+    ("Hermann Jung <hej@odn.de> based on work from Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("fujitsu siemens poseidon chip driver");
+MODULE_LICENSE("GPL");
+
+module_init(sm_fscpos_init);
+module_exit(sm_fscpos_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/fscscy.c linux-2.4.27-leo/drivers/sensors/fscscy.c
--- linux-2.4.27/drivers/sensors/fscscy.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/fscscy.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,915 @@
+/*
+    fscscy.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 2001 Martin Knoblauch <mkn@teraport.de, knobi@knobisoft.de>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* 
+    fujitsu siemens scylla chip, 
+    module based on lm80.c, fscpos.c
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+    and Philip Edelbrock <phil@netroedge.com>
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x73, SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(fscscy);
+
+/* The FSCSCY registers */
+
+/* chip identification */
+#define FSCSCY_REG_IDENT_0    0x00
+#define FSCSCY_REG_IDENT_1    0x01
+#define FSCSCY_REG_IDENT_2    0x02
+#define FSCSCY_REG_REVISION   0x03
+
+/* global control and status */
+#define FSCSCY_REG_EVENT_STATE  0x04
+#define FSCSCY_REG_CONTROL       0x05
+
+/* watchdog */
+#define FSCSCY_REG_WDOG_PRESET      0x28
+#define FSCSCY_REG_WDOG_STATE       0x23
+#define FSCSCY_REG_WDOG_CONTROL     0x21
+
+/*
+** Fan definitions
+**
+** _RPMMIN: Minimum speed. Can be set via interface, but only for three of the fans
+**          FAN1_RPMMIN is wired to Fan 0 (CPU Fans)
+**          FAN4_RPMMIN is wired to Fan 2 (PS Fans ??)
+**          FAN5_RPMMIN is wired to Fan 3 (AUX Fans ??)
+** _ACT:    Actual Fan Speed
+** _STATE:  Fan status register
+** _RIPPLE: Fan speed multiplier
+*/
+
+/* fan 0  */
+#define FSCSCY_REG_FAN0_RPMMIN	0x65
+#define FSCSCY_REG_FAN0_ACT	0x6b
+#define FSCSCY_REG_FAN0_STATE	0x62
+#define FSCSCY_REG_FAN0_RIPPLE	0x6f
+
+/* fan 1  */
+#define FSCSCY_REG_FAN1_RPMMIN     FSCSCY_REG_FAN0_RPMMIN
+#define FSCSCY_REG_FAN1_ACT     0x6c
+#define FSCSCY_REG_FAN1_STATE   0x61
+#define FSCSCY_REG_FAN1_RIPPLE  0x6f
+
+/* fan 2  */
+#define FSCSCY_REG_FAN2_RPMMIN     0x55
+#define FSCSCY_REG_FAN2_ACT     0x0e
+#define FSCSCY_REG_FAN2_STATE   0x0d
+#define FSCSCY_REG_FAN2_RIPPLE  0x0f
+
+/* fan 3  */
+#define FSCSCY_REG_FAN3_RPMMIN     0xa5
+#define FSCSCY_REG_FAN3_ACT     0xab
+#define FSCSCY_REG_FAN3_STATE   0xa2
+#define FSCSCY_REG_FAN3_RIPPLE  0xaf
+
+/* fan 4  */
+#define FSCSCY_REG_FAN4_RPMMIN     FSCSCY_REG_FAN2_RPMMIN
+#define FSCSCY_REG_FAN4_ACT	0x5c
+#define FSCSCY_REG_FAN4_STATE   0x52
+#define FSCSCY_REG_FAN4_RIPPLE  0x0f
+
+/* fan 5  */
+#define FSCSCY_REG_FAN5_RPMMIN     FSCSCY_REG_FAN3_RPMMIN
+#define FSCSCY_REG_FAN5_ACT     0xbb
+#define FSCSCY_REG_FAN5_STATE   0xb2
+#define FSCSCY_REG_FAN5_RIPPLE  0xbf
+
+/* voltage supervision */
+#define FSCSCY_REG_VOLT_12       0x45
+#define FSCSCY_REG_VOLT_5        0x42
+#define FSCSCY_REG_VOLT_BATT     0x48
+
+/* temperatures */
+/* sensor 0 */
+#define FSCSCY_REG_TEMP0_ACT	0x64
+#define FSCSCY_REG_TEMP0_STATE	0x71
+#define FSCSCY_REG_TEMP0_LIM	0x76
+
+/* sensor 1 */
+#define FSCSCY_REG_TEMP1_ACT	0xD0
+#define FSCSCY_REG_TEMP1_STATE	0xD1
+#define FSCSCY_REG_TEMP1_LIM	0xD6
+
+/* sensor 2 */
+#define FSCSCY_REG_TEMP2_ACT	0x32
+#define FSCSCY_REG_TEMP2_STATE	0x81
+#define FSCSCY_REG_TEMP2_LIM	0x86
+
+/* sensor3 */
+#define FSCSCY_REG_TEMP3_ACT	0x35
+#define FSCSCY_REG_TEMP3_STATE	0x91
+#define FSCSCY_REG_TEMP3_LIM	0x96
+
+/* PCI Load */
+#define FSCSCY_REG_PCILOAD	0x1a
+
+/* Intrusion Sensor */
+#define FSCSCY_REG_INTR_STATE	0x13
+#define FSCSCY_REG_INTR_CTRL	0x12
+
+/* Conversions. Rounding and limit checking is only done on the TO_REG
+   variants. Note that you should be a bit careful with which arguments
+   these macros are called: arguments may be evaluated more than once.
+   Fixing this is just not worth it. */
+
+#define IN_TO_REG(val,nr) (SENSORS_LIMIT((val),0,255))
+#define IN_FROM_REG(val,nr) (val)
+
+/* Initial limits */
+
+/* For each registered FSCSCY, we need to keep some data in memory. That
+   data is pointed to by fscscy_list[NR]->data. The structure itself is
+   dynamically allocated, at the same time when a new fscscy client is
+   allocated. */
+struct fscscy_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8  revision;        /* revision of chip */
+	u8  global_event;    /* global event status */
+	u8  global_control;  /* global control register */
+	u8  watchdog[3];     /* watchdog */
+	u8  volt[3];         /* 12, 5, battery current */ 
+	u8  volt_min[3];     /* minimum voltages over module "lifetime" */
+	u8  volt_max[3];     /* maximum voltages over module "lifetime" */
+	u8  temp_act[4];     /* temperature */
+	u8  temp_status[4];  /* status of temp. sensor */
+	u8  temp_lim[4];     /* limit temperature of temp. sensor */
+	u8  temp_min[4];     /* minimum of temp. sensor, this is just calculated by the module */
+	u8  temp_max[4];     /* maximum of temp. sensor, this is just calculsted by the module */
+	u8  fan_act[6];      /* fans revolutions per second */
+	u8  fan_status[6];   /* fan status */
+	u8  fan_rpmmin[6];   /* fan min value for rps */
+	u8  fan_ripple[6];   /* divider for rps */
+	u8  fan_min[6];      /* minimum RPM over module "lifetime" */
+	u8  fan_max[6];      /* maximum RPM over module "lifetime" */
+	u8  pciload;	     /* PCILoad value */
+	u8  intr_status;     /* Intrusion Status */
+	u8  intr_control;    /* Intrusion Control */
+};
+
+
+static int fscscy_attach_adapter(struct i2c_adapter *adapter);
+static int fscscy_detect(struct i2c_adapter *adapter, int address,
+		       unsigned short flags, int kind);
+static int fscscy_detach_client(struct i2c_client *client);
+
+static int fscscy_read_value(struct i2c_client *client, u8 register);
+static int fscscy_write_value(struct i2c_client *client, u8 register,
+			    u8 value);
+static void fscscy_update_client(struct i2c_client *client);
+static void fscscy_init_client(struct i2c_client *client);
+
+
+static void fscscy_in(struct i2c_client *client, int operation, int ctl_name,
+		    	int *nrels_mag, long *results);
+static void fscscy_fan(struct i2c_client *client, int operation,
+		     	int ctl_name, int *nrels_mag, long *results);
+static void fscscy_fan_internal(struct i2c_client *client, int operation,
+		     	int ctl_name, int *nrels_mag, long *results, 
+		     	int nr, int reg_state, int reg_min, int res_ripple);
+static void fscscy_temp(struct i2c_client *client, int operation,
+		      	int ctl_name, int *nrels_mag, long *results);
+static void fscscy_volt(struct i2c_client *client, int operation,
+		      	int ctl_name, int *nrels_mag, long *results);
+static void fscscy_wdog(struct i2c_client *client, int operation,
+		      	int ctl_name, int *nrels_mag, long *results);
+static void fscscy_pciload(struct i2c_client *client, int operation,
+		      	int ctl_name, int *nrels_mag, long *results);
+static void fscscy_intrusion(struct i2c_client *client, int operation,
+		      	int ctl_name, int *nrels_mag, long *results);
+
+static int fscscy_id = 0;
+
+static struct i2c_driver fscscy_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "FSCSCY sensor driver",
+	.id		= I2C_DRIVERID_FSCSCY,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= fscscy_attach_adapter,
+	.detach_client	= fscscy_detach_client,
+};
+
+/* The /proc/sys entries */
+
+/* -- SENSORS SYSCTL START -- */
+#define FSCSCY_SYSCTL_VOLT0    1000       /* 12 volt supply */
+#define FSCSCY_SYSCTL_VOLT1    1001       /* 5 volt supply */
+#define FSCSCY_SYSCTL_VOLT2    1002       /* batterie voltage*/
+#define FSCSCY_SYSCTL_FAN0     1101       /* state, min, ripple, actual value fan 0 */
+#define FSCSCY_SYSCTL_FAN1     1102       /* state, min, ripple, actual value fan 1 */
+#define FSCSCY_SYSCTL_FAN2     1103       /* state, min, ripple, actual value fan 2 */
+#define FSCSCY_SYSCTL_FAN3     1104       /* state, min, ripple, actual value fan 3 */
+#define FSCSCY_SYSCTL_FAN4     1105       /* state, min, ripple, actual value fan 4 */
+#define FSCSCY_SYSCTL_FAN5     1106       /* state, min, ripple, actual value fan 5 */
+#define FSCSCY_SYSCTL_TEMP0    1201       /* state and value of sensor 0, cpu die */
+#define FSCSCY_SYSCTL_TEMP1    1202       /* state and value of sensor 1, motherboard */
+#define FSCSCY_SYSCTL_TEMP2    1203       /* state and value of sensor 2, chassis */
+#define FSCSCY_SYSCTL_TEMP3    1204       /* state and value of sensor 3, chassis */
+#define FSCSCY_SYSCTL_REV     2000        /* Revision */
+#define FSCSCY_SYSCTL_EVENT   2001        /* global event status */
+#define FSCSCY_SYSCTL_CONTROL 2002        /* global control byte */
+#define FSCSCY_SYSCTL_WDOG     2003       /* state, min, ripple, actual value fan 2 */
+#define FSCSCY_SYSCTL_PCILOAD  2004       /* PCILoad value */
+#define FSCSCY_SYSCTL_INTRUSION 2005      /* state, control for intrusion sensor */
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected FSCSCY. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized 
+   when a new copy is allocated. */
+static ctl_table fscscy_dir_table_template[] = {
+	{FSCSCY_SYSCTL_REV, "rev", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_in},
+	{FSCSCY_SYSCTL_EVENT, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_in},
+	{FSCSCY_SYSCTL_CONTROL, "control", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_in},
+	{FSCSCY_SYSCTL_TEMP0, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_temp},
+	{FSCSCY_SYSCTL_TEMP1, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_temp},
+	{FSCSCY_SYSCTL_TEMP2, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_temp},
+	{FSCSCY_SYSCTL_TEMP3, "temp4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_temp},
+	{FSCSCY_SYSCTL_VOLT0, "in0", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_volt},
+	{FSCSCY_SYSCTL_VOLT1, "in1", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_volt},
+	{FSCSCY_SYSCTL_VOLT2, "in2", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_volt},
+	{FSCSCY_SYSCTL_FAN0, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_fan},
+	{FSCSCY_SYSCTL_FAN1, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_fan},
+	{FSCSCY_SYSCTL_FAN2, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_fan},
+	{FSCSCY_SYSCTL_FAN3, "fan4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_fan},
+	{FSCSCY_SYSCTL_FAN4, "fan5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_fan},
+	{FSCSCY_SYSCTL_FAN5, "fan6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_fan},
+	{FSCSCY_SYSCTL_WDOG, "wdog", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_wdog},
+	{FSCSCY_SYSCTL_PCILOAD, "pciload", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_pciload},
+	{FSCSCY_SYSCTL_INTRUSION, "intrusion", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &fscscy_intrusion},
+	{0}
+};
+
+static int fscscy_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, fscscy_detect);
+}
+
+int fscscy_detect(struct i2c_adapter *adapter, int address,
+		unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct fscscy_data *data;
+	int err = 0;
+	const char *type_name, *client_name;
+
+	/* Make sure we aren't probing the ISA bus!! This is just a safety check
+	   at this moment; i2c_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("fscscy.o: fscscy_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		goto ERROR0;
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access fscscy_{read,write}_value. */
+	if (!(data = kmalloc(sizeof(struct fscscy_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &fscscy_driver;
+	new_client->flags = 0;
+
+	/* Do the remaining detection unless force or force_fscscy parameter */
+	if (kind < 0) {
+		if (fscscy_read_value(new_client, FSCSCY_REG_IDENT_0) != 0x53)
+			goto ERROR1;
+		if (fscscy_read_value(new_client, FSCSCY_REG_IDENT_1) != 0x43)
+			goto ERROR1;
+		if (fscscy_read_value(new_client, FSCSCY_REG_IDENT_2) != 0x59)
+			goto ERROR1;
+	}
+
+	kind = fscscy;
+
+	type_name = "fscscy";
+	client_name = "fsc scylla chip";
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+
+	new_client->id = fscscy_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client, type_name,
+					fscscy_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	fscscy_init_client(new_client);
+	return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+      ERROR1:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int fscscy_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct fscscy_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("fscscy.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+static int fscscy_read_value(struct i2c_client *client, u8 reg)
+{
+#ifdef DEBUG
+	printk("fscscy: read reg 0x%02x\n",reg);
+#endif
+	return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int fscscy_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+#ifdef DEBUG
+	printk("fscscy: write reg 0x%02x, val 0x%02x\n",reg, value);
+#endif
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* Called when we have found a new FSCSCY. It should set limits, etc. */
+static void fscscy_init_client(struct i2c_client *client)
+{
+	struct fscscy_data *data = client->data;
+
+	/* read revision from chip */
+	data->revision =  fscscy_read_value(client,FSCSCY_REG_REVISION);
+
+        /* Initialize min/max values from chip */
+	data->fan_min[0]  = data->fan_max[0]  = fscscy_read_value(client, FSCSCY_REG_FAN0_ACT);
+	data->fan_min[1]  = data->fan_max[1]  = fscscy_read_value(client, FSCSCY_REG_FAN1_ACT);
+	data->fan_min[2]  = data->fan_max[2]  = fscscy_read_value(client, FSCSCY_REG_FAN2_ACT);
+	data->fan_min[3]  = data->fan_max[3]  = fscscy_read_value(client, FSCSCY_REG_FAN3_ACT);
+	data->fan_min[4]  = data->fan_max[4]  = fscscy_read_value(client, FSCSCY_REG_FAN4_ACT);
+	data->fan_min[4]  = data->fan_max[5]  = fscscy_read_value(client, FSCSCY_REG_FAN5_ACT);
+        data->temp_min[0] = data->temp_max[0] = fscscy_read_value(client, FSCSCY_REG_TEMP0_ACT);
+        data->temp_min[1] = data->temp_max[1] = fscscy_read_value(client, FSCSCY_REG_TEMP1_ACT);
+        data->temp_min[2] = data->temp_max[2] = fscscy_read_value(client, FSCSCY_REG_TEMP2_ACT);
+        data->temp_min[3] = data->temp_max[3] = fscscy_read_value(client, FSCSCY_REG_TEMP3_ACT);
+	data->volt_min[0] = data->volt_max[0] = fscscy_read_value(client, FSCSCY_REG_VOLT_12);
+	data->volt_min[1] = data->volt_max[1] = fscscy_read_value(client, FSCSCY_REG_VOLT_5);
+	data->volt_min[2] = data->volt_max[2] = fscscy_read_value(client, FSCSCY_REG_VOLT_BATT);
+}
+
+static void fscscy_update_client(struct i2c_client *client)
+{
+	struct fscscy_data *data = client->data;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > 2 * HZ) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting fscscy update\n");
+#endif
+		data->temp_act[0] = fscscy_read_value(client, FSCSCY_REG_TEMP0_ACT);
+		  if (data->temp_min[0] > data->temp_act[0]) data->temp_min[0] = data->temp_act[0];
+		  if (data->temp_max[0] < data->temp_act[0]) data->temp_max[0] = data->temp_act[0];
+		data->temp_act[1] = fscscy_read_value(client, FSCSCY_REG_TEMP1_ACT);
+		  if (data->temp_min[1] > data->temp_act[1]) data->temp_min[1] = data->temp_act[1];
+		  if (data->temp_max[1] < data->temp_act[1]) data->temp_max[1] = data->temp_act[1];
+		data->temp_act[2] = fscscy_read_value(client, FSCSCY_REG_TEMP2_ACT);
+		  if (data->temp_min[2] > data->temp_act[2]) data->temp_min[2] = data->temp_act[2];
+		  if (data->temp_max[2] < data->temp_act[2]) data->temp_max[2] = data->temp_act[2];
+		data->temp_act[3] = fscscy_read_value(client, FSCSCY_REG_TEMP3_ACT);
+		  if (data->temp_min[3] > data->temp_act[3]) data->temp_min[3] = data->temp_act[3];
+		  if (data->temp_max[3] < data->temp_act[3]) data->temp_max[3] = data->temp_act[3];
+		data->temp_status[0] = fscscy_read_value(client, FSCSCY_REG_TEMP0_STATE);
+		data->temp_status[1] = fscscy_read_value(client, FSCSCY_REG_TEMP1_STATE);
+		data->temp_status[2] = fscscy_read_value(client, FSCSCY_REG_TEMP2_STATE);
+		data->temp_status[3] = fscscy_read_value(client, FSCSCY_REG_TEMP3_STATE);
+		data->temp_lim[0] = fscscy_read_value(client, FSCSCY_REG_TEMP0_LIM);
+		data->temp_lim[1] = fscscy_read_value(client, FSCSCY_REG_TEMP1_LIM);
+		data->temp_lim[2] = fscscy_read_value(client, FSCSCY_REG_TEMP2_LIM);
+		data->temp_lim[3] = fscscy_read_value(client, FSCSCY_REG_TEMP3_LIM);
+
+		data->volt[0] = fscscy_read_value(client, FSCSCY_REG_VOLT_12);
+		  if (data->volt_min[0] > data->volt[0]) data->volt_min[0] = data->volt[0];
+		  if (data->volt_max[0] < data->volt[0]) data->volt_max[0] = data->volt[0];
+		data->volt[1] = fscscy_read_value(client, FSCSCY_REG_VOLT_5);
+		  if (data->volt_min[1] > data->volt[1]) data->volt_min[1] = data->volt[1];
+		  if (data->volt_max[1] < data->volt[1]) data->volt_max[1] = data->volt[1];
+		data->volt[2] = fscscy_read_value(client, FSCSCY_REG_VOLT_BATT);
+		  if (data->volt_min[2] > data->volt[2]) data->volt_min[2] = data->volt[2];
+		  if (data->volt_max[2] < data->volt[2]) data->volt_max[2] = data->volt[2];
+
+		data->fan_act[0] = fscscy_read_value(client, FSCSCY_REG_FAN0_ACT);
+		  if (data->fan_min[0] > data->fan_act[0]) data->fan_min[0] = data->fan_act[0];
+		  if (data->fan_max[0] < data->fan_act[0]) data->fan_max[0] = data->fan_act[0];
+		data->fan_act[1] = fscscy_read_value(client, FSCSCY_REG_FAN1_ACT);
+		  if (data->fan_min[1] > data->fan_act[1]) data->fan_min[1] = data->fan_act[1];
+		  if (data->fan_max[1] < data->fan_act[1]) data->fan_max[1] = data->fan_act[1];
+		data->fan_act[2] = fscscy_read_value(client, FSCSCY_REG_FAN2_ACT);
+		  if (data->fan_min[2] > data->fan_act[2]) data->fan_min[2] = data->fan_act[2];
+		  if (data->fan_max[2] < data->fan_act[2]) data->fan_max[2] = data->fan_act[2];
+		data->fan_act[3] = fscscy_read_value(client, FSCSCY_REG_FAN3_ACT);
+		  if (data->fan_min[3] > data->fan_act[3]) data->fan_min[3] = data->fan_act[3];
+		  if (data->fan_max[3] < data->fan_act[3]) data->fan_max[3] = data->fan_act[3];
+		data->fan_act[4] = fscscy_read_value(client, FSCSCY_REG_FAN4_ACT);
+		  if (data->fan_min[4] > data->fan_act[4]) data->fan_min[4] = data->fan_act[4];
+		  if (data->fan_max[4] < data->fan_act[4]) data->fan_max[4] = data->fan_act[4];
+		data->fan_act[5] = fscscy_read_value(client, FSCSCY_REG_FAN5_ACT);
+		  if (data->fan_min[5] > data->fan_act[5]) data->fan_min[5] = data->fan_act[5];
+		  if (data->fan_max[5] < data->fan_act[5]) data->fan_max[5] = data->fan_act[5];
+		data->fan_status[0] = fscscy_read_value(client, FSCSCY_REG_FAN0_STATE);
+		data->fan_status[1] = fscscy_read_value(client, FSCSCY_REG_FAN1_STATE);
+		data->fan_status[2] = fscscy_read_value(client, FSCSCY_REG_FAN2_STATE);
+		data->fan_status[3] = fscscy_read_value(client, FSCSCY_REG_FAN3_STATE);
+		data->fan_status[4] = fscscy_read_value(client, FSCSCY_REG_FAN4_STATE);
+		data->fan_status[5] = fscscy_read_value(client, FSCSCY_REG_FAN5_STATE);
+		data->fan_rpmmin[0] = fscscy_read_value(client, FSCSCY_REG_FAN0_RPMMIN);
+		data->fan_rpmmin[1] = fscscy_read_value(client, FSCSCY_REG_FAN1_RPMMIN);
+		data->fan_rpmmin[2] = fscscy_read_value(client, FSCSCY_REG_FAN2_RPMMIN);
+		data->fan_rpmmin[3] = fscscy_read_value(client, FSCSCY_REG_FAN3_RPMMIN);
+		data->fan_rpmmin[4] = fscscy_read_value(client, FSCSCY_REG_FAN4_RPMMIN);
+		data->fan_rpmmin[5] = fscscy_read_value(client, FSCSCY_REG_FAN5_RPMMIN);
+		data->fan_ripple[0] = fscscy_read_value(client, FSCSCY_REG_FAN0_RIPPLE);
+		data->fan_ripple[1] = fscscy_read_value(client, FSCSCY_REG_FAN1_RIPPLE);
+		data->fan_ripple[2] = fscscy_read_value(client, FSCSCY_REG_FAN2_RIPPLE);
+		data->fan_ripple[3] = fscscy_read_value(client, FSCSCY_REG_FAN3_RIPPLE);
+		data->fan_ripple[4] = fscscy_read_value(client, FSCSCY_REG_FAN4_RIPPLE);
+		data->fan_ripple[5] = fscscy_read_value(client, FSCSCY_REG_FAN5_RIPPLE);
+
+		data->watchdog[0] = fscscy_read_value(client, FSCSCY_REG_WDOG_PRESET);
+		data->watchdog[1] = fscscy_read_value(client, FSCSCY_REG_WDOG_STATE);
+		data->watchdog[2] = fscscy_read_value(client, FSCSCY_REG_WDOG_CONTROL);
+
+		data->global_event = fscscy_read_value(client, FSCSCY_REG_EVENT_STATE);
+		data->global_control = fscscy_read_value(client, FSCSCY_REG_CONTROL);
+		data->pciload = fscscy_read_value(client, FSCSCY_REG_PCILOAD);
+		data->intr_status = fscscy_read_value(client, FSCSCY_REG_INTR_STATE);
+		data->intr_control = fscscy_read_value(client, FSCSCY_REG_INTR_CTRL);
+
+                data->last_updated = jiffies;
+                data->valid = 1;                 
+	}
+
+	up(&data->update_lock);
+}
+
+
+/* The next few functions are the call-back functions of the /proc/sys and
+   sysctl files. Which function is used is defined in the ctl_table in
+   the extra1 field.
+   Each function must return the magnitude (power of 10 to divide the date
+   with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must
+   put a maximum of *nrels elements in results reflecting the data of this
+   file, and set *nrels to the number it actually put in it, if operation==
+   SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from
+   results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE.
+   Note that on SENSORS_PROC_REAL_READ, I do not check whether results is
+   large enough (by checking the incoming value of *nrels). This is not very
+   good practice, but as long as you put less than about 5 values in results,
+   you can assume it is large enough. */
+void fscscy_in(struct i2c_client *client, int operation, int ctl_name,
+	     int *nrels_mag, long *results)
+{
+	struct fscscy_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		fscscy_update_client(client);
+		switch(ctl_name) {
+			case FSCSCY_SYSCTL_REV:
+				results[0] = data->revision ;
+				break;
+			case FSCSCY_SYSCTL_EVENT:
+				results[0] = data->global_event & 0x9f; /* MKN */
+				break;
+			case FSCSCY_SYSCTL_CONTROL:
+				results[0] = data->global_control & 0x19; /* MKN */
+				break;
+			default:
+				printk("fscscy: ctl_name %d not supported\n",
+					ctl_name);
+				*nrels_mag = 0;
+				return;
+		}
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if((ctl_name == FSCSCY_SYSCTL_CONTROL) && (*nrels_mag >= 1)) {
+			data->global_control = (data->global_control & 0x18) | (results[0] & 0x01); /* MKN */
+			printk("fscscy: writing 0x%02x to global_control\n",
+				data->global_control);
+			fscscy_write_value(client,FSCSCY_REG_CONTROL,
+				data->global_control);
+		}
+		else
+			printk("fscscy: writing to chip not supported\n");
+	}
+}
+
+#define TEMP_FROM_REG(val)    (val-128)
+
+
+void fscscy_temp(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct fscscy_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		fscscy_update_client(client);
+		switch(ctl_name) {
+			case FSCSCY_SYSCTL_TEMP0:
+				results[0] = data->temp_status[0] & 0x03;
+				results[1] = TEMP_FROM_REG(data->temp_act[0]);
+				results[2] = TEMP_FROM_REG(data->temp_lim[0]);
+				results[3] = TEMP_FROM_REG(data->temp_min[0]);
+				results[4] = TEMP_FROM_REG(data->temp_max[0]);
+				break;
+			case FSCSCY_SYSCTL_TEMP1:
+				results[0] = data->temp_status[1] & 0x03;
+				results[1] = TEMP_FROM_REG(data->temp_act[1]);
+				results[2] = TEMP_FROM_REG(data->temp_lim[1]);
+				results[3] = TEMP_FROM_REG(data->temp_min[1]);
+				results[4] = TEMP_FROM_REG(data->temp_max[1]);
+				break;
+			case FSCSCY_SYSCTL_TEMP2:
+				results[0] = data->temp_status[2] & 0x03;
+				results[1] = TEMP_FROM_REG(data->temp_act[2]);
+				results[2] = TEMP_FROM_REG(data->temp_lim[2]);
+				results[3] = TEMP_FROM_REG(data->temp_min[2]);
+				results[4] = TEMP_FROM_REG(data->temp_max[2]);
+				break;
+			case FSCSCY_SYSCTL_TEMP3:
+				results[0] = data->temp_status[3] & 0x03;
+				results[1] = TEMP_FROM_REG(data->temp_act[3]);
+				results[2] = TEMP_FROM_REG(data->temp_lim[3]);
+				results[3] = TEMP_FROM_REG(data->temp_min[3]);
+				results[4] = TEMP_FROM_REG(data->temp_max[3]);
+				break;
+			default:
+				printk("fscscy: ctl_name %d not supported\n",
+					ctl_name);
+				*nrels_mag = 0;
+				return;
+		}
+		*nrels_mag = 5;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if(*nrels_mag >= 1) {
+			switch(ctl_name) {
+				case FSCSCY_SYSCTL_TEMP0:
+					data->temp_status[0] = 
+						(data->temp_status[0] & ~0x02) 
+						| (results[0] & 0x02);
+					printk("fscscy: writing value 0x%02x "
+						"to temp0_status\n",
+						data->temp_status[0]);
+					fscscy_write_value(client,
+						FSCSCY_REG_TEMP0_STATE,
+						data->temp_status[0] & 0x02);
+					break;
+				case FSCSCY_SYSCTL_TEMP1:
+					data->temp_status[1] = (data->temp_status[1] & ~0x02) | (results[0] & 0x02);
+					printk("fscscy: writing value 0x%02x to temp1_status\n", data->temp_status[1]);
+					fscscy_write_value(client,FSCSCY_REG_TEMP1_STATE,
+						data->temp_status[1] & 0x02);
+					break;
+				case FSCSCY_SYSCTL_TEMP2:
+					data->temp_status[2] = (data->temp_status[2] & ~0x02) | (results[0] & 0x02);
+					printk("fscscy: writing value 0x%02x to temp2_status\n", data->temp_status[2]);
+					fscscy_write_value(client,FSCSCY_REG_TEMP2_STATE,
+						data->temp_status[2] & 0x02);
+					break;
+				case FSCSCY_SYSCTL_TEMP3:
+					data->temp_status[3] = (data->temp_status[3] & ~0x02) | (results[0] & 0x02);
+					printk("fscscy: writing value 0x%02x to temp3_status\n", data->temp_status[3]);
+					fscscy_write_value(client,FSCSCY_REG_TEMP3_STATE,
+						data->temp_status[3] & 0x02);
+					break;
+				default:
+					printk("fscscy: ctl_name %d not supported\n",ctl_name);
+			}
+		}
+		else
+			printk("fscscy: writing to chip not supported\n");
+	}
+}
+
+#define VOLT_FROM_REG(val,mult)    (val*mult/255)
+
+void fscscy_volt(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct fscscy_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		fscscy_update_client(client);
+		switch(ctl_name) {
+			case FSCSCY_SYSCTL_VOLT0:
+				results[0] = VOLT_FROM_REG(data->volt[0],1420);
+				results[1] = VOLT_FROM_REG(data->volt_min[0],1420);
+				results[2] = VOLT_FROM_REG(data->volt_max[0],1420);
+				break;
+			case FSCSCY_SYSCTL_VOLT1:
+				results[0] = VOLT_FROM_REG(data->volt[1],660);
+				results[1] = VOLT_FROM_REG(data->volt_min[1],660);
+				results[2] = VOLT_FROM_REG(data->volt_max[1],660);
+				break;
+			case FSCSCY_SYSCTL_VOLT2:
+				results[0] = VOLT_FROM_REG(data->volt[2],330);
+				results[1] = VOLT_FROM_REG(data->volt_min[2],330);
+				results[2] = VOLT_FROM_REG(data->volt_max[2],330);
+				break;
+			default:
+				printk("fscscy: ctl_name %d not supported\n",
+					ctl_name);
+				*nrels_mag = 0;
+				return;
+		}
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+			printk("fscscy: writing to chip not supported\n");
+	}
+}
+
+void fscscy_fan(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+
+	switch(ctl_name) {
+		case FSCSCY_SYSCTL_FAN0:
+			fscscy_fan_internal(client,operation,ctl_name,nrels_mag,results,
+				0,FSCSCY_REG_FAN0_STATE,FSCSCY_REG_FAN0_RPMMIN,
+				FSCSCY_REG_FAN0_RIPPLE);
+			break;
+		case FSCSCY_SYSCTL_FAN1:
+			fscscy_fan_internal(client,operation,ctl_name,nrels_mag,results,
+				1,FSCSCY_REG_FAN1_STATE,FSCSCY_REG_FAN1_RPMMIN,
+				FSCSCY_REG_FAN1_RIPPLE);
+			break;
+		case FSCSCY_SYSCTL_FAN2:
+			fscscy_fan_internal(client,operation,ctl_name,nrels_mag,results,
+				2,FSCSCY_REG_FAN2_STATE,FSCSCY_REG_FAN2_RPMMIN,
+				FSCSCY_REG_FAN2_RIPPLE);
+			break;
+		case FSCSCY_SYSCTL_FAN3:
+			fscscy_fan_internal(client,operation,ctl_name,nrels_mag,results,
+				3,FSCSCY_REG_FAN3_STATE,FSCSCY_REG_FAN3_RPMMIN,
+				FSCSCY_REG_FAN3_RIPPLE);
+			break;
+		case FSCSCY_SYSCTL_FAN4:
+			fscscy_fan_internal(client,operation,ctl_name,nrels_mag,results,
+				4,FSCSCY_REG_FAN4_STATE,FSCSCY_REG_FAN4_RPMMIN,
+				FSCSCY_REG_FAN4_RIPPLE);
+			break;
+		case FSCSCY_SYSCTL_FAN5:
+			fscscy_fan_internal(client,operation,ctl_name,nrels_mag,results,
+				5,FSCSCY_REG_FAN5_STATE,FSCSCY_REG_FAN5_RPMMIN,
+				FSCSCY_REG_FAN5_RIPPLE);
+			break;
+		default:
+			printk("fscscy: illegal fan nr %d\n",ctl_name);
+	}
+}
+			
+#define RPM_FROM_REG(val)   (val*60)
+
+void fscscy_fan_internal(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results, int nr,
+	       int reg_state, int reg_min, int reg_ripple )
+{
+	struct fscscy_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		fscscy_update_client(client);
+		results[0] = data->fan_status[nr] & 0x0f; /* MKN */
+		results[1] = data->fan_rpmmin[nr];
+		results[2] = data->fan_ripple[nr] & 0x03;
+		results[3] = RPM_FROM_REG(data->fan_act[nr]);
+		results[4] = RPM_FROM_REG(data->fan_min[nr]);
+		results[5] = RPM_FROM_REG(data->fan_max[nr]);
+		*nrels_mag = 6;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if(*nrels_mag >= 1) {
+			data->fan_status[nr] = (data->fan_status[nr] & 0x0b) | (results[0] & 0x04); /* MKN */
+			printk("fscscy: writing value 0x%02x to fan%d_status\n",
+				data->fan_status[nr],nr);
+			fscscy_write_value(client,reg_state,
+				data->fan_status[nr]);
+		}
+		if(*nrels_mag >= 2)  {
+			if((results[1] & 0xff) == 0) {
+				 printk("fscscy: fan%d rpmmin 0 not allowed for safety reasons\n",nr);
+				 return;
+			}
+			data->fan_rpmmin[nr] = results[1];
+			printk("fscscy: writing value 0x%02x to fan%d_min\n",
+				data->fan_rpmmin[nr],nr);
+			fscscy_write_value(client,reg_min,
+				data->fan_rpmmin[nr]);
+		}
+		if(*nrels_mag >= 3) {
+			if((results[2] & 0x03) == 0) {
+				printk("fscscy: fan%d ripple 0 is nonsense/not allowed\n",nr);
+				return;
+			}
+			data->fan_ripple[nr] = results[2] & 0x03;
+			printk("fscscy: writing value 0x%02x to fan%d_ripple\n",
+				data->fan_ripple[nr],nr);
+			fscscy_write_value(client,reg_ripple,
+				data->fan_ripple[nr]);
+		}	
+	}
+}
+
+void fscscy_wdog(struct i2c_client *client, int operation, int ctl_name,
+	     int *nrels_mag, long *results)
+{
+	struct fscscy_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		fscscy_update_client(client);
+		results[0] = data->watchdog[0] ;
+		results[1] = data->watchdog[1] & 0x02;
+		results[2] = data->watchdog[2] & 0xb0;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->watchdog[0] = results[0] & 0xff;
+			printk("fscscy: writing value 0x%02x to wdog_preset\n",
+				data->watchdog[0]); 
+			fscscy_write_value(client,FSCSCY_REG_WDOG_PRESET,
+				data->watchdog[0]);
+		} 
+		if (*nrels_mag >= 2) {
+			data->watchdog[1] = results[1] & 0x02;
+			printk("fscscy: writing value 0x%02x to wdog_state\n",
+				data->watchdog[1]); 
+			fscscy_write_value(client,FSCSCY_REG_WDOG_STATE,
+				data->watchdog[1]);
+		}
+		if (*nrels_mag >= 3) {
+			data->watchdog[2] = results[2] & 0xb0;
+			printk("fscscy: writing value 0x%02x to wdog_control\n",
+				data->watchdog[2]); 
+			fscscy_write_value(client,FSCSCY_REG_WDOG_CONTROL,
+				data->watchdog[2]);
+		}
+	}
+}
+
+void fscscy_pciload(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct fscscy_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		fscscy_update_client(client);
+		results[0] = data->pciload;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+			printk("fscscy: writing PCILOAD to chip not supported\n");
+	}
+}
+
+void fscscy_intrusion(struct i2c_client *client, int operation, int ctl_name,
+	     int *nrels_mag, long *results)
+{
+	struct fscscy_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		fscscy_update_client(client);
+		results[0] = data->intr_control & 0x80;
+		results[1] = data->intr_status & 0xc0;
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->intr_control = results[0] & 0x80;
+			printk("fscscy: writing value 0x%02x to intr_control\n",
+				data->intr_control); 
+			fscscy_write_value(client,FSCSCY_REG_INTR_CTRL,
+				data->intr_control);
+		} 
+	}
+}
+
+static int __init sm_fscscy_init(void)
+{
+	printk("fscscy.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&fscscy_driver);
+}
+
+static void __exit sm_fscscy_exit(void)
+{
+	i2c_del_driver(&fscscy_driver);
+}
+
+
+
+MODULE_AUTHOR
+    ("Martin Knoblauch <mkn@teraport.de> based on work (fscpos) from  Hermann Jung <hej@odn.de>");
+MODULE_DESCRIPTION("fujitsu siemens scylla chip driver");
+
+module_init(sm_fscscy_init);
+module_exit(sm_fscscy_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/gl518sm.c linux-2.4.27-leo/drivers/sensors/gl518sm.c
--- linux-2.4.27/drivers/sensors/gl518sm.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/gl518sm.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,989 @@
+/*
+    gl518sm.c - Part of lm_sensors, Linux kernel modules for hardware
+                monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>,
+                              Kyösti Mälkki <kmalkki@cc.hut.fi>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#ifdef __SMP__
+#include <linux/smp_lock.h>
+#endif
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x2c, 0x2d, SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_2(gl518sm_r00, gl518sm_r80);
+
+/* Defining this will enable debug messages for the voltage iteration
+   code used with rev 0 ICs */
+#undef DEBUG_VIN
+
+/* Many GL518 constants specified below */
+
+/* The GL518 registers */
+#define GL518_REG_CHIP_ID 0x00
+#define GL518_REG_REVISION 0x01
+#define GL518_REG_VENDOR_ID 0x02
+#define GL518_REG_CONF 0x03
+#define GL518_REG_TEMP 0x04
+#define GL518_REG_TEMP_OVER 0x05
+#define GL518_REG_TEMP_HYST 0x06
+#define GL518_REG_FAN_COUNT 0x07
+#define GL518_REG_FAN_LIMIT 0x08
+#define GL518_REG_VIN1_LIMIT 0x09
+#define GL518_REG_VIN2_LIMIT 0x0a
+#define GL518_REG_VIN3_LIMIT 0x0b
+#define GL518_REG_VDD_LIMIT 0x0c
+#define GL518_REG_VIN3 0x0d
+#define GL518_REG_MISC 0x0f
+#define GL518_REG_ALARM 0x10
+#define GL518_REG_MASK 0x11
+#define GL518_REG_INT 0x12
+#define GL518_REG_VIN2 0x13
+#define GL518_REG_VIN1 0x14
+#define GL518_REG_VDD 0x15
+
+
+/* Conversions. Rounding and limit checking is only done on the TO_REG
+   variants. Note that you should be a bit careful with which arguments
+   these macros are called: arguments may be evaluated more than once.
+   Fixing this is just not worth it. */
+
+#define TEMP_TO_REG(val) (SENSORS_LIMIT(((((val)<0?(val)-5:(val)+5) / 10)+119),\
+                                        0,255))
+#define TEMP_FROM_REG(val) (((val) - 119) * 10)
+
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+	if (rpm == 0)
+		return 255;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT((960000 + rpm * div / 2) / (rpm * div), 1,
+			     254);
+}
+
+#define FAN_FROM_REG(val,div) \
+ ( (val)==0 ? 0 : (val)==255 ? 0 : (960000/((val)*(div))) )
+
+#define IN_TO_REG(val) (SENSORS_LIMIT((((val)*10+8)/19),0,255))
+#define IN_FROM_REG(val) (((val)*19)/10)
+
+#define VDD_TO_REG(val) (SENSORS_LIMIT((((val)*10+11)/23),0,255))
+#define VDD_FROM_REG(val) (((val)*23)/10)
+
+#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
+#define DIV_FROM_REG(val) (1 << (val))
+
+#define ALARMS_FROM_REG(val) val
+
+#define BEEP_ENABLE_TO_REG(val) ((val)?0:1)
+#define BEEP_ENABLE_FROM_REG(val) ((val)?0:1)
+
+#define BEEPS_TO_REG(val) ((val) & 0x7f)
+#define BEEPS_FROM_REG(val) ((val) & 0x7f)
+
+/* Each client has this additional data */
+struct gl518_data {
+	struct i2c_client client;
+	int sysctl_id;
+	enum chips type;
+
+	struct semaphore update_lock;
+
+	int iterate_lock;
+	int quit_thread;
+	struct task_struct *thread;
+	wait_queue_head_t wq;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+	unsigned long last_updated_v00;
+	/* In jiffies (used only by rev00 chips) */
+
+	u8 voltage[4];		/* Register values; [0] = VDD */
+	u8 voltage_min[4];	/* Register values; [0] = VDD */
+	u8 voltage_max[4];	/* Register values; [0] = VDD */
+	u8 iter_voltage[4];	/* Register values; [0] = VDD */
+	u8 fan[2];
+	u8 fan_min[2];
+	u8 temp;		/* Register values */
+	u8 temp_over;		/* Register values */
+	u8 temp_hyst;		/* Register values */
+	u8 alarms, beeps;	/* Register value */
+	u8 alarm_mask;		/* Register value */
+	u8 fan_div[2];		/* Register encoding, shifted right */
+	u8 beep_enable;		/* Boolean */
+	u8 iterate;		/* Voltage iteration mode */
+};
+
+static int gl518_attach_adapter(struct i2c_adapter *adapter);
+static int gl518_detect(struct i2c_adapter *adapter, int address,
+			unsigned short flags, int kind);
+static void gl518_init_client(struct i2c_client *client);
+static int gl518_detach_client(struct i2c_client *client);
+
+static int gl518_read_value(struct i2c_client *client, u8 reg);
+static int gl518_write_value(struct i2c_client *client, u8 reg, u16 value);
+static void gl518_update_client(struct i2c_client *client);
+
+static void gl518_update_client_rev00(struct i2c_client *client);
+static void gl518_update_iterate(struct i2c_client *client);
+
+static void gl518_vin(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void gl518_fan(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void gl518_temp(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void gl518_fan_div(struct i2c_client *client, int operation,
+			  int ctl_name, int *nrels_mag, long *results);
+static void gl518_alarms(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void gl518_beep(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void gl518_fan1off(struct i2c_client *client, int operation,
+			  int ctl_name, int *nrels_mag, long *results);
+static void gl518_iterate(struct i2c_client *client, int operation,
+			  int ctl_name, int *nrels_mag, long *results);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver gl518_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "GL518SM sensor chip driver",
+	.id		= I2C_DRIVERID_GL518,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= gl518_attach_adapter,
+	.detach_client	= gl518_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+
+#define GL518_SYSCTL_VDD  1000	/* Volts * 100 */
+#define GL518_SYSCTL_VIN1 1001
+#define GL518_SYSCTL_VIN2 1002
+#define GL518_SYSCTL_VIN3 1003
+#define GL518_SYSCTL_FAN1 1101	/* RPM */
+#define GL518_SYSCTL_FAN2 1102
+#define GL518_SYSCTL_TEMP 1200	/* Degrees Celcius * 10 */
+#define GL518_SYSCTL_FAN_DIV 2000	/* 1, 2, 4 or 8 */
+#define GL518_SYSCTL_ALARMS 2001	/* bitvector */
+#define GL518_SYSCTL_BEEP 2002	/* bitvector */
+#define GL518_SYSCTL_FAN1OFF 2003
+#define GL518_SYSCTL_ITERATE 2004
+
+#define GL518_ALARM_VDD 0x01
+#define GL518_ALARM_VIN1 0x02
+#define GL518_ALARM_VIN2 0x04
+#define GL518_ALARM_VIN3 0x08
+#define GL518_ALARM_TEMP 0x10
+#define GL518_ALARM_FAN1 0x20
+#define GL518_ALARM_FAN2 0x40
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected GL518. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized
+   when a new copy is allocated. */
+static ctl_table gl518_dir_table_template[] = {
+	{GL518_SYSCTL_VIN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl518_vin},
+	{GL518_SYSCTL_VIN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl518_vin},
+	{GL518_SYSCTL_VIN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl518_vin},
+	{GL518_SYSCTL_VDD, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl518_vin},
+	{GL518_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl518_fan},
+	{GL518_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl518_fan},
+	{GL518_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl518_temp},
+	{GL518_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl518_fan_div},
+	{GL518_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl518_alarms},
+	{GL518_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl518_beep},
+	{GL518_SYSCTL_FAN1OFF, "fan1off", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl518_fan1off},
+	{GL518_SYSCTL_ITERATE, "iterate", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl518_iterate},
+	{0}
+};
+
+/* I choose here for semi-static GL518SM allocation. Complete dynamic
+   allocation could also be used; the code needed for this would probably
+   take more memory than the datastructure takes now. */
+#define MAX_GL518_NR 4
+static struct i2c_client *gl518_list[MAX_GL518_NR];
+
+static int gl518_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, gl518_detect);
+}
+
+static int gl518_detect(struct i2c_adapter *adapter, int address,
+			unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct gl518_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+
+	/* Make sure we aren't probing the ISA bus!! This is just a safety check
+	   at this moment; i2c_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("gl518sm.o: gl518_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+				     I2C_FUNC_SMBUS_WORD_DATA))
+		    goto ERROR0;
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access gl518_{read,write}_value. */
+
+	if (!(data = kmalloc(sizeof(struct gl518_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &gl518_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+
+	if (kind < 0) {
+		if (
+		    (gl518_read_value(new_client, GL518_REG_CHIP_ID) !=
+		     0x80)
+		    || (gl518_read_value(new_client, GL518_REG_CONF) &
+			0x80)) goto ERROR1;
+	}
+
+	/* Determine the chip type. */
+	if (kind <= 0) {
+		i = gl518_read_value(new_client, GL518_REG_REVISION);
+		if (i == 0x00)
+			kind = gl518sm_r00;
+		else if (i == 0x80)
+			kind = gl518sm_r80;
+		else {
+			if (kind == 0)
+				printk
+				    ("gl518sm.o: Ignoring 'force' parameter for unknown chip at "
+				     "adapter %d, address 0x%02x\n",
+				     i2c_adapter_id(adapter), address);
+			goto ERROR1;
+		}
+	}
+
+	type_name = "gl518sm";
+	if (kind == gl518sm_r00) {
+		client_name = "GL518SM Revision 0x00 chip";
+	} else if (kind == gl518sm_r80) {
+		client_name = "GL518SM Revision 0x80 chip";
+	} else {
+#ifdef DEBUG
+		printk("gl518sm.o: Internal error: unknown kind (%d)?!?",
+		       kind);
+#endif
+		goto ERROR1;
+	}
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	data->type = kind;
+
+	for (i = 0; i < MAX_GL518_NR; i++)
+		if (!gl518_list[i])
+			break;
+	if (i == MAX_GL518_NR) {
+		printk
+		    ("gl518sm.o: No empty slots left, recompile and heighten "
+		     "MAX_GL518_NR!\n");
+		err = -ENOMEM;
+		goto ERROR2;
+	}
+	gl518_list[i] = new_client;
+	new_client->id = i;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry((struct i2c_client *) new_client,
+					type_name,
+					gl518_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the GL518SM chip */
+	if (kind == gl518sm_r00)
+		data->iterate = 0;
+	else
+		data->iterate = 3;
+	data->iterate_lock = 0;
+	data->quit_thread = 0;
+	data->thread = NULL;
+	data->alarm_mask = 0xff;
+	data->voltage[0]=data->voltage[1]=data->voltage[2]=0;
+	gl518_init_client((struct i2c_client *) new_client);
+	return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+	for (i = 0; i < MAX_GL518_NR; i++)
+		if (new_client == gl518_list[i])
+			gl518_list[i] = NULL;
+      ERROR2:
+      ERROR1:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+
+/* Called when we have found a new GL518SM. It should set limits, etc. */
+static void gl518_init_client(struct i2c_client *client)
+{
+	/* Power-on defaults (bit 7=1) */
+	gl518_write_value(client, GL518_REG_CONF, 0x80);
+
+	/* No noisy output (bit 2=1), Comparator mode (bit 3=0), two fans (bit4=0),
+	   standby mode (bit6=0) */
+	gl518_write_value(client, GL518_REG_CONF, 0x04);
+
+	/* Never interrupts */
+	gl518_write_value(client, GL518_REG_MASK, 0x00);
+
+	/* Clear status register (bit 5=1), start (bit6=1) */
+	gl518_write_value(client, GL518_REG_CONF, 0x24);
+	gl518_write_value(client, GL518_REG_CONF, 0x44);
+}
+
+static int gl518_detach_client(struct i2c_client *client)
+{
+	int err, i;
+	struct gl518_data *data = client->data;
+
+	i2c_deregister_entry(((struct gl518_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("gl518sm.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	for (i = 0; i < MAX_GL518_NR; i++)
+		if (client == gl518_list[i])
+			break;
+	if ((i == MAX_GL518_NR)) {
+		printk("gl518sm.o: Client to detach not found.\n");
+		return -ENOENT;
+	}
+	gl518_list[i] = NULL;
+
+	if (data->thread) {
+		data->quit_thread = 1;
+		wake_up_interruptible(&data->wq);
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+
+/* Registers 0x07 to 0x0c are word-sized, others are byte-sized 
+   GL518 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+static int gl518_read_value(struct i2c_client *client, u8 reg)
+{
+	if ((reg >= 0x07) && (reg <= 0x0c))
+		return swab16(i2c_smbus_read_word_data(client, reg));
+	else
+		return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* Registers 0x07 to 0x0c are word-sized, others are byte-sized 
+   GL518 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+static int gl518_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+	if ((reg >= 0x07) && (reg <= 0x0c))
+		return i2c_smbus_write_word_data(client, reg, swab16(value));
+	else
+		return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static void gl518_update_client(struct i2c_client *client)
+{
+	struct gl518_data *data = client->data;
+	int val;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting gl518 update\n");
+#endif
+
+		data->alarms = gl518_read_value(client, GL518_REG_INT);
+		data->beeps = gl518_read_value(client, GL518_REG_ALARM);
+
+		val = gl518_read_value(client, GL518_REG_VDD_LIMIT);
+		data->voltage_min[0] = val & 0xff;
+		data->voltage_max[0] = (val >> 8) & 0xff;
+		val = gl518_read_value(client, GL518_REG_VIN1_LIMIT);
+		data->voltage_min[1] = val & 0xff;
+		data->voltage_max[1] = (val >> 8) & 0xff;
+		val = gl518_read_value(client, GL518_REG_VIN2_LIMIT);
+		data->voltage_min[2] = val & 0xff;
+		data->voltage_max[2] = (val >> 8) & 0xff;
+		val = gl518_read_value(client, GL518_REG_VIN3_LIMIT);
+		data->voltage_min[3] = val & 0xff;
+		data->voltage_max[3] = (val >> 8) & 0xff;
+
+		val = gl518_read_value(client, GL518_REG_FAN_COUNT);
+		data->fan[0] = (val >> 8) & 0xff;
+		data->fan[1] = val & 0xff;
+
+		val = gl518_read_value(client, GL518_REG_FAN_LIMIT);
+		data->fan_min[0] = (val >> 8) & 0xff;
+		data->fan_min[1] = val & 0xff;
+
+		data->temp = gl518_read_value(client, GL518_REG_TEMP);
+		data->temp_over =
+		    gl518_read_value(client, GL518_REG_TEMP_OVER);
+		data->temp_hyst =
+		    gl518_read_value(client, GL518_REG_TEMP_HYST);
+
+		val = gl518_read_value(client, GL518_REG_MISC);
+		data->fan_div[0] = (val >> 6) & 0x03;
+		data->fan_div[1] = (val >> 4) & 0x03;
+
+		data->alarms &= data->alarm_mask;
+
+		val = gl518_read_value(client, GL518_REG_CONF);
+		data->beep_enable = (val >> 2) & 1;
+
+#ifndef DEBUG_VIN
+		if (data->type != gl518sm_r00) {
+			data->voltage[0] =
+			    gl518_read_value(client, GL518_REG_VDD);
+			data->voltage[1] =
+			    gl518_read_value(client, GL518_REG_VIN1);
+			data->voltage[2] =
+			    gl518_read_value(client, GL518_REG_VIN2);
+			data->voltage[3] =
+			    gl518_read_value(client, GL518_REG_VIN3);
+		} else
+			gl518_update_client_rev00(client);
+#else
+		gl518_update_client_rev00(client);
+#endif
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+/* Here we decide how to run the iteration code.
+   When called, we trigger the iteration and report the last
+   measured voltage. No delay for user apps */
+static void gl518_update_client_rev00(struct i2c_client *client)
+{
+	struct gl518_data *data = client->data;
+	int i;
+
+	if (data->iterate == 1) {	/* 10 sec delay */
+		/* as that update is slow, we consider the data valid for 30 seconds */
+		if (
+		    ((jiffies - data->last_updated_v00 > 30 * HZ)
+		     || (data->alarms & 7)
+		     || (!data->valid)) && (!data->iterate_lock)) {
+			data->iterate_lock = 1;
+			gl518_update_iterate(client);
+			data->iterate_lock = 0;
+		}
+		for (i = 0; i < 4; i++)
+			data->voltage[i] = data->iter_voltage[i];
+	} else if (data->iterate == 2) {	/* show results of last iteration */
+		for (i = 0; i < 4; i++)
+			data->voltage[i] = data->iter_voltage[i];
+		wake_up_interruptible(&data->wq);
+	} else {		/* no iteration */
+		data->voltage[3] =
+		    gl518_read_value(client, GL518_REG_VIN3);
+	}
+}
+
+static int gl518_update_thread(void *c)
+{
+	struct i2c_client *client = c;
+	struct gl518_data *data = client->data;
+
+#ifdef __SMP__
+	lock_kernel();
+#endif
+	exit_mm(current);
+	current->session = 1;
+	current->pgrp = 1;
+	sigfillset(&current->blocked);
+	current->fs->umask = 0;
+	strcpy(current->comm, "gl518sm");
+
+	init_waitqueue_head(&(data->wq));
+	data->thread = current;
+
+#ifdef __SMP__
+	unlock_kernel();
+#endif
+
+	for (;;) {
+		if (!data->iterate_lock) {
+			data->iterate_lock = 1;
+			gl518_update_iterate(client);
+			data->iterate_lock = 0;
+		}
+
+		if ((data->quit_thread) || signal_pending(current))
+			break;
+		interruptible_sleep_on(&data->wq);
+	}
+
+	data->thread = NULL;
+	data->quit_thread = 0;
+	return 0;
+}
+
+/* This updates vdd, vin1, vin2 values by doing slow and multiple
+   comparisons for the GL518SM rev 00 that lacks support for direct
+   reading of these values.   Values are kept in iter_voltage   */
+
+static void gl518_update_iterate(struct i2c_client *client)
+{
+	struct gl518_data *data = client->data;
+	int i, j, loop_more = 1, min[3], max[3], delta[3];
+	int alarm, beeps, irqs;
+
+#define VIN_REG(c) c==0?GL518_REG_VDD_LIMIT:\
+                   c==1?GL518_REG_VIN1_LIMIT:\
+                   GL518_REG_VIN2_LIMIT
+
+	/* disable beeps & irqs for vin0-2 */
+	beeps = gl518_read_value(client, GL518_REG_ALARM);
+	irqs = gl518_read_value(client, GL518_REG_MASK);
+	gl518_write_value(client, GL518_REG_ALARM, beeps & ~0x7);
+	gl518_write_value(client, GL518_REG_MASK, irqs & ~0x7);
+
+	alarm = data->alarms;
+
+	for (i = 0; i < 3; i++) {
+		if (alarm & (1 << i)) {
+			min[i] = 0;
+			max[i] = 127;
+		} else {
+			min[i] = data->voltage_min[i];
+			max[i] =
+			    (data->voltage_max[i] +
+			     data->voltage_min[i]) / 2;
+		}
+		delta[i] = (max[i] - min[i]) / 2;
+	}
+
+	for (j = 0; (j < 10 && loop_more); j++) {
+
+		for (i = 0; i < 3; i++)
+			gl518_write_value(client, VIN_REG(i),
+					  max[i] << 8 | min[i]);
+
+		if ((data->thread) &&
+		    ((data->quit_thread) || signal_pending(current)))
+			goto finish;
+
+		/* we wait now 1.5 seconds before comparing */
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(HZ + HZ / 2);
+
+		alarm = gl518_read_value(client, GL518_REG_INT);
+
+#ifdef DEBUG_VIN
+		printk("gl518sm: iteration %2d: %4d%c %4d%c %4d%c\n", j,
+		       max[0], (alarm & 1) ? '!' : ' ',
+		       max[1], (alarm & 2) ? '!' : ' ',
+		       max[2], (alarm & 4) ? '!' : ' ');
+#endif
+
+		for (loop_more = 0, i = 0; i < 3; i++) {
+			if (alarm & (1 << i))
+				max[i] += delta[i];
+			else
+				max[i] -= delta[i];
+
+			if (delta[i])
+				loop_more++;
+			delta[i] >>= 1;
+		}
+
+	}
+
+	for (i = 0; i < 3; i++)
+		if (alarm & (1 << i))
+			max[i]++;
+
+#ifdef DEBUG_VIN
+	printk("gl518sm:    final   :%5d %5d %5d\n", max[0], max[1],
+	       max[2]);
+	printk("gl518sm:    meter   :%5d %5d %5d\n", data->voltage[0],
+	       data->voltage[1], data->voltage[2]);
+#endif
+
+	/* update values, including vin3 */
+	for (i = 0; i < 3; i++) {
+		data->iter_voltage[i] = max[i];
+	}
+	data->iter_voltage[3] = gl518_read_value(client, GL518_REG_VIN3);
+	data->last_updated_v00 = jiffies;
+
+      finish:
+
+	/* reset values */
+	for (i = 0; i < 3; i++) {
+		gl518_write_value(client, VIN_REG(i),
+				  data->voltage_max[i] << 8 | data->
+				  voltage_min[i]);
+	}
+
+	gl518_write_value(client, GL518_REG_ALARM, beeps);
+	gl518_write_value(client, GL518_REG_MASK, irqs);
+
+#undef VIN_REG
+}
+
+void gl518_temp(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct gl518_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl518_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_over);
+		results[1] = TEMP_FROM_REG(data->temp_hyst);
+		results[2] = TEMP_FROM_REG(data->temp);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_over = TEMP_TO_REG(results[0]);
+			gl518_write_value(client, GL518_REG_TEMP_OVER,
+					  data->temp_over);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_hyst = TEMP_TO_REG(results[1]);
+			gl518_write_value(client, GL518_REG_TEMP_HYST,
+					  data->temp_hyst);
+		}
+	}
+}
+
+void gl518_vin(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct gl518_data *data = client->data;
+	int nr = ctl_name - GL518_SYSCTL_VDD;
+	int regnr, old = 0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl518_update_client(client);
+		results[0] = nr ? IN_FROM_REG(data->voltage_min[nr]) :
+		    VDD_FROM_REG(data->voltage_min[nr]);
+		results[1] = nr ? IN_FROM_REG(data->voltage_max[nr]) :
+		    VDD_FROM_REG(data->voltage_max[nr]);
+		results[2] = nr ? IN_FROM_REG(data->voltage[nr]) :
+		    VDD_FROM_REG(data->voltage[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		regnr =
+		    nr == 0 ? GL518_REG_VDD_LIMIT : nr ==
+		    1 ? GL518_REG_VIN1_LIMIT : nr ==
+		    2 ? GL518_REG_VIN2_LIMIT : GL518_REG_VIN3_LIMIT;
+		if (*nrels_mag == 1)
+			old = gl518_read_value(client, regnr) & 0xff00;
+		if (*nrels_mag >= 2) {
+			data->voltage_max[nr] =
+			    nr ? IN_TO_REG(results[1]) :
+			    VDD_TO_REG(results[1]);
+			old = data->voltage_max[nr] << 8;
+		}
+		if (*nrels_mag >= 1) {
+			data->voltage_min[nr] =
+			    nr ? IN_TO_REG(results[0]) :
+			    VDD_TO_REG(results[0]);
+			old |= data->voltage_min[nr];
+			gl518_write_value(client, regnr, old);
+		}
+	}
+}
+
+
+void gl518_fan(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct gl518_data *data = client->data;
+	int nr = ctl_name - GL518_SYSCTL_FAN1;
+	int old;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl518_update_client(client);
+		results[0] = FAN_FROM_REG(data->fan_min[nr],
+					  DIV_FROM_REG(data->fan_div[nr]));
+		results[1] =
+		    FAN_FROM_REG(data->fan[nr],
+				 DIV_FROM_REG(data->fan_div[nr]));
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr] = FAN_TO_REG(results[0],
+						       DIV_FROM_REG(data->
+								    fan_div
+								    [nr]));
+			old =
+			    gl518_read_value(client, GL518_REG_FAN_LIMIT);
+
+			if (nr == 0) {
+				old =
+				    (old & 0x00ff) | (data->
+						      fan_min[0] << 8);
+				if (results[0] == 0)
+					data->alarm_mask &= ~0x20;
+				else
+					data->alarm_mask |= 0x20;
+			} else {
+				old = (old & 0xff00) | data->fan_min[1];
+				if (results[0] == 0)
+					data->alarm_mask &= ~0x40;
+				else
+					data->alarm_mask |= 0x40;
+			}
+			gl518_write_value(client, GL518_REG_FAN_LIMIT,
+					  old);
+		}
+	}
+}
+
+
+void gl518_alarms(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct gl518_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl518_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->alarms);
+		*nrels_mag = 1;
+	}
+}
+
+void gl518_beep(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct gl518_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl518_update_client(client);
+		results[0] = BEEP_ENABLE_FROM_REG(data->beep_enable);
+		results[1] = BEEPS_FROM_REG(data->beeps);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->beep_enable = BEEP_ENABLE_TO_REG(results[0]);
+			gl518_write_value(client, GL518_REG_CONF,
+					  (gl518_read_value(client,
+							    GL518_REG_CONF)
+					   & 0xfb) | (data->
+						      beep_enable << 2));
+		}
+		if (*nrels_mag >= 2) {
+			data->beeps =
+			    BEEPS_TO_REG(results[1]) & data->alarm_mask;
+			gl518_write_value(client, GL518_REG_ALARM,
+					  data->beeps);
+		}
+	}
+}
+
+
+void gl518_fan_div(struct i2c_client *client, int operation, int ctl_name,
+		   int *nrels_mag, long *results)
+{
+	struct gl518_data *data = client->data;
+	int old;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl518_update_client(client);
+		results[0] = DIV_FROM_REG(data->fan_div[0]);
+		results[1] = DIV_FROM_REG(data->fan_div[1]);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		old = gl518_read_value(client, GL518_REG_MISC);
+		if (*nrels_mag >= 2) {
+			data->fan_div[1] = DIV_TO_REG(results[1]);
+			old = (old & 0xcf) | (data->fan_div[1] << 4);
+		}
+		if (*nrels_mag >= 1) {
+			data->fan_div[0] = DIV_TO_REG(results[0]);
+			old = (old & 0x3f) | (data->fan_div[0] << 6);
+		}
+		gl518_write_value(client, GL518_REG_MISC, old);
+	}
+}
+
+void gl518_fan1off(struct i2c_client *client, int operation, int ctl_name,
+		   int *nrels_mag, long *results)
+{
+	int old;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] =
+		    ((gl518_read_value(client, GL518_REG_MISC) & 0x08) !=
+		     0);
+		results[1] =
+		    ((gl518_read_value(client, GL518_REG_CONF) & 0x10) !=
+		     0);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			old =
+			    gl518_read_value(client,
+					     GL518_REG_MISC) & 0xf7;
+			if (results[0])
+				old |= 0x08;
+			gl518_write_value(client, GL518_REG_MISC, old);
+		}
+		if (*nrels_mag >= 2) {
+			old =
+			    gl518_read_value(client,
+					     GL518_REG_CONF) & 0xef;
+			if (results[1])
+				old |= 0x10;
+			gl518_write_value(client, GL518_REG_CONF, old);
+		}
+	}
+}
+
+void gl518_iterate(struct i2c_client *client, int operation, int ctl_name,
+		   int *nrels_mag, long *results)
+{
+	struct gl518_data *data = client->data;
+	int i;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = data->iterate;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE &&
+	           data->type == gl518sm_r00 ) {
+		if ((*nrels_mag >= 1) && (data->iterate != results[0])) {
+			data->iterate = results[0];
+			for (i = 0; i < 4; i++) {
+				data->voltage[i] = 0;
+				data->iter_voltage[i] = 0;
+			}
+			data->valid = 0;
+
+			if ((data->iterate != 2) && (data->thread)) {
+				data->quit_thread = 1;
+				wake_up_interruptible(&data->wq);
+			} else if ((data->iterate == 2) && (!data->thread)) {
+				init_waitqueue_head(&(data->wq));
+				kernel_thread(gl518_update_thread,
+					      (void *) client, 0);
+			}
+		}
+	}
+}
+
+static int __init sm_gl518sm_init(void)
+{
+	printk("gl518sm.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&gl518_driver);
+}
+
+static void __exit sm_gl518sm_exit(void)
+{
+	i2c_del_driver(&gl518_driver);
+}
+
+
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Kyösti Mälkki <kmalkki@cc.hut.fi>");
+MODULE_DESCRIPTION("GL518SM driver");
+
+module_init(sm_gl518sm_init);
+module_exit(sm_gl518sm_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/gl520sm.c linux-2.4.27-leo/drivers/sensors/gl520sm.c
--- linux-2.4.27/drivers/sensors/gl520sm.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/gl520sm.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,809 @@
+/*
+    gl520sm.c - Part of lm_sensors, Linux kernel modules for hardware
+                monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>,
+                              Kyösti Mälkki <kmalkki@cc.hut.fi>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x2c, 0x2d, SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(gl520sm);
+
+/* Many GL520 constants specified below 
+One of the inputs can be configured as either temp or voltage.
+That's why _TEMP2 and _VIN4 access the same register 
+*/
+
+/* The GL520 registers */
+#define GL520_REG_CHIP_ID 0x00
+#define GL520_REG_REVISION 0x01
+#define GL520_REG_VID 0x02
+#define GL520_REG_CONF 0x03
+#define GL520_REG_TEMP1 0x04
+#define GL520_REG_TEMP1_OVER 0x05
+#define GL520_REG_TEMP1_HYST 0x06
+#define GL520_REG_FAN_COUNT 0x07
+#define GL520_REG_FAN_LIMIT 0x08
+#define GL520_REG_VIN1_LIMIT 0x09
+#define GL520_REG_VIN2_LIMIT 0x0a
+#define GL520_REG_VIN3_LIMIT 0x0b
+#define GL520_REG_VDD_LIMIT 0x0c
+#define GL520_REG_VIN3 0x0d
+#define GL520_REG_VIN4 0x0e
+#define GL520_REG_TEMP2 0x0e
+#define GL520_REG_MISC 0x0f
+#define GL520_REG_ALARM 0x10
+#define GL520_REG_MASK 0x11
+#define GL520_REG_INT 0x12
+#define GL520_REG_VIN2 0x13
+#define GL520_REG_VIN1 0x14
+#define GL520_REG_VDD 0x15
+#define GL520_REG_TEMP2_OVER 0x17
+#define GL520_REG_VIN4_MAX 0x17
+#define GL520_REG_TEMP2_HYST 0x18
+#define GL520_REG_VIN4_MIN 0x18
+
+
+/* Conversions. Rounding and limit checking is only done on the TO_REG
+   variants. Note that you should be a bit careful with which arguments
+   these macros are called: arguments may be evaluated more than once.
+   Fixing this is just not worth it. */
+
+#define TEMP_TO_REG(val) (SENSORS_LIMIT(((((val)<0?(val)-5:(val)+5) / 10)+130),\
+                                        0,255))
+#define TEMP_FROM_REG(val) (((val) - 130) * 10)
+
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+	if (rpm == 0)
+		return 255;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT((960000 + rpm * div / 2) / (rpm * div), 1,
+			     254);
+}
+
+#define FAN_FROM_REG(val,div) \
+ ( (val)==0 ? 0 : (val)==255 ? 0 : (960000/((val)*(div))) )
+
+#define IN_TO_REG(val) (SENSORS_LIMIT((((val)*10+8)/19),0,255))
+#define IN_FROM_REG(val) (((val)*19)/10)
+
+#define VDD_TO_REG(val) (SENSORS_LIMIT((((val)*10+11)/23),0,255))
+#define VDD_FROM_REG(val) (((val)*23)/10)
+
+#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
+#define DIV_FROM_REG(val) (1 << (val))
+
+#define ALARMS_FROM_REG(val) val
+
+#define BEEP_ENABLE_TO_REG(val) ((val)?0:1)
+#define BEEP_ENABLE_FROM_REG(val) ((val)?0:1)
+
+#define BEEPS_TO_REG(val) (val)
+#define BEEPS_FROM_REG(val) (val)
+
+#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\
+                           205-(val)*5)
+
+/* Each client has this additional data */
+struct gl520_data {
+	struct i2c_client client;
+	int sysctl_id;
+	enum chips type;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 voltage[5];		/* Register values; [0] = VDD */
+	u8 voltage_min[5];	/* Register values; [0] = VDD */
+	u8 voltage_max[5];	/* Register values; [0] = VDD */
+	u8 fan[2];
+	u8 fan_min[2];
+	u8 temp[2];		/* Register values */
+	u8 temp_over[2];	/* Register values */
+	u8 temp_hyst[2];	/* Register values */
+	u8 alarms, beeps, vid;	/* Register value */
+	u8 alarm_mask;		/* Register value */
+	u8 fan_div[2];		/* Register encoding, shifted right */
+	u8 beep_enable;		/* Boolean */
+	u8 two_temps;		/* Boolean */
+};
+
+static int gl520_attach_adapter(struct i2c_adapter *adapter);
+static int gl520_detect(struct i2c_adapter *adapter, int address,
+			unsigned short flags, int kind);
+static void gl520_init_client(struct i2c_client *client);
+static int gl520_detach_client(struct i2c_client *client);
+
+static int gl520_read_value(struct i2c_client *client, u8 reg);
+static int gl520_write_value(struct i2c_client *client, u8 reg, u16 value);
+static void gl520_update_client(struct i2c_client *client);
+
+static void gl520_vin(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void gl520_vid(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void gl520_fan(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void gl520_temp(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void gl520_fan_div(struct i2c_client *client, int operation,
+			  int ctl_name, int *nrels_mag, long *results);
+static void gl520_alarms(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void gl520_beep(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void gl520_fan1off(struct i2c_client *client, int operation,
+			  int ctl_name, int *nrels_mag, long *results);
+static void gl520_config(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver gl520_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "GL520SM sensor chip driver",
+	.id		= I2C_DRIVERID_GL520,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= gl520_attach_adapter,
+	.detach_client	= gl520_detach_client,
+};
+/* -- SENSORS SYSCTL START -- */
+
+#define GL520_SYSCTL_VDD  1000	/* Volts * 100 */
+#define GL520_SYSCTL_VIN1 1001
+#define GL520_SYSCTL_VIN2 1002
+#define GL520_SYSCTL_VIN3 1003
+#define GL520_SYSCTL_VIN4 1004
+#define GL520_SYSCTL_FAN1 1101	/* RPM */
+#define GL520_SYSCTL_FAN2 1102
+#define GL520_SYSCTL_TEMP1 1200	/* Degrees Celcius * 10 */
+#define GL520_SYSCTL_TEMP2 1201	/* Degrees Celcius * 10 */
+#define GL520_SYSCTL_VID 1300
+#define GL520_SYSCTL_FAN_DIV 2000	/* 1, 2, 4 or 8 */
+#define GL520_SYSCTL_ALARMS 2001	/* bitvector */
+#define GL520_SYSCTL_BEEP 2002	/* bitvector */
+#define GL520_SYSCTL_FAN1OFF 2003
+#define GL520_SYSCTL_CONFIG 2004
+
+#define GL520_ALARM_VDD 0x01
+#define GL520_ALARM_VIN1 0x02
+#define GL520_ALARM_VIN2 0x04
+#define GL520_ALARM_VIN3 0x08
+#define GL520_ALARM_TEMP1 0x10
+#define GL520_ALARM_FAN1 0x20
+#define GL520_ALARM_FAN2 0x40
+#define GL520_ALARM_TEMP2 0x80
+#define GL520_ALARM_VIN4 0x80
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected GL520. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized
+   when a new copy is allocated. */
+static ctl_table gl520_dir_table_template[] = {
+	{GL520_SYSCTL_VIN1, "vin1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl520_vin},
+	{GL520_SYSCTL_VIN2, "vin2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl520_vin},
+	{GL520_SYSCTL_VIN3, "vin3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl520_vin},
+	{GL520_SYSCTL_VIN4, "vin4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl520_vin},
+	{GL520_SYSCTL_VDD, "vdd", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl520_vin},
+	{GL520_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl520_vid},
+	{GL520_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl520_fan},
+	{GL520_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl520_fan},
+	{GL520_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl520_temp},
+	{GL520_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl520_temp},
+	{GL520_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl520_fan_div},
+	{GL520_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl520_alarms},
+	{GL520_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl520_beep},
+	{GL520_SYSCTL_FAN1OFF, "fan1off", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl520_fan1off},
+	{GL520_SYSCTL_CONFIG, "config", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &gl520_config},
+	{0}
+};
+
+static int gl520_id = 0;
+
+static int gl520_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, gl520_detect);
+}
+
+static int gl520_detect(struct i2c_adapter *adapter, int address,
+			unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct gl520_data *data;
+	int err = 0;
+	const char *type_name = "";
+	char client_name[32];
+
+	/* Make sure we aren't probing the ISA bus!! This is just a safety check
+	   at this moment; i2c_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("gl520sm.o: gl520_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+				     I2C_FUNC_SMBUS_WORD_DATA))
+		    goto ERROR0;
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access gl520_{read,write}_value. */
+
+	if (!(data = kmalloc(sizeof(struct gl520_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &gl520_driver;
+	new_client->flags = 0;
+
+	/* Determine the chip type. */
+
+	if (gl520_read_value(new_client, GL520_REG_CHIP_ID) != 0x20) {
+		printk
+		    ("gl520sm.o: Ignoring 'force' parameter for unknown chip at "
+		     "adapter %d, address 0x%02x\n",
+		     i2c_adapter_id(adapter), address);
+		goto ERROR1;
+	} else {
+		kind = gl520sm;
+	}
+
+	i = gl520_read_value(new_client, GL520_REG_REVISION);
+	if (kind == gl520sm) {
+		type_name = "gl520sm";
+		sprintf(client_name, "GL520SM Revision %02x chip", i);
+	} else {
+#ifdef DEBUG
+		printk("gl520sm.o: Internal error: unknown kind (%d)?!?",
+		       kind);
+#endif
+		goto ERROR1;
+	}
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	data->type = kind;
+
+	new_client->id = gl520_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client,
+					type_name,
+					gl520_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the GL520SM chip */
+	data->alarm_mask = 0xff;
+	gl520_init_client(new_client);
+	if (data->two_temps)
+		data->voltage_max[4] = data->voltage_min[4] =
+			data->voltage[4] = 0;
+	else
+		data->temp_hyst[1] = data->temp_over[1] =
+			data->temp[1] = 0;
+
+	return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+      ERROR1:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+
+/* Called when we have found a new GL520SM. */
+static void gl520_init_client(struct i2c_client *client)
+{
+	struct gl520_data *data = (struct gl520_data *)(client->data);
+	u8 oldconf, conf;
+
+	conf = oldconf = gl520_read_value(client, GL520_REG_CONF);
+	data->two_temps = !(conf & 0x10);
+
+	/* If IRQ# is disabled, we can safely force comparator mode */
+	if (!(conf & 0x20))
+		conf &= 0xf7;
+
+	/* Enable monitoring if needed */
+	conf |= 0x40;
+
+	if (conf != oldconf)
+		gl520_write_value(client, GL520_REG_CONF, conf);
+}
+
+static int gl520_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct gl520_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("gl520sm.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+
+/* Registers 0x07 to 0x0c are word-sized, others are byte-sized 
+   GL520 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+static int gl520_read_value(struct i2c_client *client, u8 reg)
+{
+	if ((reg >= 0x07) && (reg <= 0x0c))
+		return swab16(i2c_smbus_read_word_data(client, reg));
+	else
+		return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* Registers 0x07 to 0x0c are word-sized, others are byte-sized 
+   GL520 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+static int gl520_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+	if ((reg >= 0x07) && (reg <= 0x0c))
+		return i2c_smbus_write_word_data(client, reg, swab16(value));
+	else
+		return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static void gl520_update_client(struct i2c_client *client)
+{
+	struct gl520_data *data = client->data;
+	int val;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting gl520 update\n");
+#endif
+
+		data->alarms = gl520_read_value(client, GL520_REG_INT);
+		data->beeps = gl520_read_value(client, GL520_REG_ALARM);
+		data->vid = gl520_read_value(client, GL520_REG_VID) & 0x1f;
+
+		val = gl520_read_value(client, GL520_REG_VDD_LIMIT);
+		data->voltage_min[0] = val & 0xff;
+		data->voltage_max[0] = (val >> 8) & 0xff;
+		val = gl520_read_value(client, GL520_REG_VIN1_LIMIT);
+		data->voltage_min[1] = val & 0xff;
+		data->voltage_max[1] = (val >> 8) & 0xff;
+		val = gl520_read_value(client, GL520_REG_VIN2_LIMIT);
+		data->voltage_min[2] = val & 0xff;
+		data->voltage_max[2] = (val >> 8) & 0xff;
+		val = gl520_read_value(client, GL520_REG_VIN3_LIMIT);
+		data->voltage_min[3] = val & 0xff;
+		data->voltage_max[3] = (val >> 8) & 0xff;
+
+		val = gl520_read_value(client, GL520_REG_FAN_COUNT);
+		data->fan[0] = (val >> 8) & 0xff;
+		data->fan[1] = val & 0xff;
+
+		val = gl520_read_value(client, GL520_REG_FAN_LIMIT);
+		data->fan_min[0] = (val >> 8) & 0xff;
+		data->fan_min[1] = val & 0xff;
+
+		data->temp[0] = gl520_read_value(client, GL520_REG_TEMP1);
+		data->temp_over[0] =
+		    gl520_read_value(client, GL520_REG_TEMP1_OVER);
+		data->temp_hyst[0] =
+		    gl520_read_value(client, GL520_REG_TEMP1_HYST);
+
+		val = gl520_read_value(client, GL520_REG_MISC);
+		data->fan_div[0] = (val >> 6) & 0x03;
+		data->fan_div[1] = (val >> 4) & 0x03;
+
+		data->alarms &= data->alarm_mask;
+
+		val = gl520_read_value(client, GL520_REG_CONF);
+		data->beep_enable = (val >> 2) & 1;
+
+		data->voltage[0] = gl520_read_value(client, GL520_REG_VDD);
+		data->voltage[1] =
+		    gl520_read_value(client, GL520_REG_VIN1);
+		data->voltage[2] =
+		    gl520_read_value(client, GL520_REG_VIN2);
+		data->voltage[3] =
+		    gl520_read_value(client, GL520_REG_VIN3);
+
+		/* Temp1 and Vin4 are the same input */
+		if (data->two_temps) {
+			data->temp[1] =
+			    gl520_read_value(client, GL520_REG_TEMP2);
+			data->temp_over[1] =
+			    gl520_read_value(client, GL520_REG_TEMP2_OVER);
+			data->temp_hyst[1] =
+			    gl520_read_value(client, GL520_REG_TEMP2_HYST);
+		} else {
+			data->voltage[4] =
+			    gl520_read_value(client, GL520_REG_VIN4);
+			data->voltage_min[4] =
+			    gl520_read_value(client, GL520_REG_VIN4_MIN);
+			data->voltage_max[4] =
+			    gl520_read_value(client, GL520_REG_VIN4_MAX);
+		}
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+void gl520_temp(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct gl520_data *data = client->data;
+	int nr = ctl_name - GL520_SYSCTL_TEMP1;
+	int regnr;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl520_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_over[nr]);
+		results[1] = TEMP_FROM_REG(data->temp_hyst[nr]);
+		results[2] = TEMP_FROM_REG(data->temp[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if ((nr == 1) && (!data->two_temps))
+			return;
+		regnr =
+		    nr == 0 ? GL520_REG_TEMP1_OVER : GL520_REG_TEMP2_OVER;
+		if (*nrels_mag >= 1) {
+			data->temp_over[nr] = TEMP_TO_REG(results[0]);
+			gl520_write_value(client, regnr,
+					  data->temp_over[nr]);
+		}
+		regnr =
+		    nr == 0 ? GL520_REG_TEMP1_HYST : GL520_REG_TEMP2_HYST;
+		if (*nrels_mag >= 2) {
+			data->temp_hyst[nr] = TEMP_TO_REG(results[1]);
+			gl520_write_value(client, regnr,
+					  data->temp_hyst[nr]);
+		}
+	}
+}
+
+void gl520_vin(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct gl520_data *data = client->data;
+	int nr = ctl_name - GL520_SYSCTL_VDD;
+	int regnr, old = 0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl520_update_client(client);
+		results[0] = nr ? IN_FROM_REG(data->voltage_min[nr]) :
+		    VDD_FROM_REG(data->voltage_min[nr]);
+		results[1] = nr ? IN_FROM_REG(data->voltage_max[nr]) :
+		    VDD_FROM_REG(data->voltage_max[nr]);
+		results[2] = nr ? IN_FROM_REG(data->voltage[nr]) :
+		    VDD_FROM_REG(data->voltage[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (nr != 4) {
+			regnr =
+			    nr == 0 ? GL520_REG_VDD_LIMIT : nr ==
+			    1 ? GL520_REG_VIN1_LIMIT : nr ==
+			    2 ? GL520_REG_VIN2_LIMIT :
+			    GL520_REG_VIN3_LIMIT;
+			if (*nrels_mag == 1)
+				old =
+				    gl520_read_value(client,
+						     regnr) & 0xff00;
+			if (*nrels_mag >= 2) {
+				data->voltage_max[nr] =
+				    nr ? IN_TO_REG(results[1]) :
+				    VDD_TO_REG(results[1]);
+				old = data->voltage_max[nr] << 8;
+			}
+			if (*nrels_mag >= 1) {
+				data->voltage_min[nr] =
+				    nr ? IN_TO_REG(results[0]) :
+				    VDD_TO_REG(results[0]);
+				old |= data->voltage_min[nr];
+				gl520_write_value(client, regnr, old);
+			}
+		} else if (!data->two_temps) {
+			if (*nrels_mag == 1)
+				gl520_write_value(client,
+						  GL520_REG_VIN4_MIN,
+						  IN_TO_REG(results[0]));
+			if (*nrels_mag >= 2)
+				gl520_write_value(client,
+						  GL520_REG_VIN4_MAX,
+						  IN_TO_REG(results[1]));
+		}
+	}
+}
+
+
+void gl520_fan(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct gl520_data *data = client->data;
+	int nr = ctl_name - GL520_SYSCTL_FAN1;
+	int old;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl520_update_client(client);
+		results[0] = FAN_FROM_REG(data->fan_min[nr],
+					  DIV_FROM_REG(data->fan_div[nr]));
+		results[1] =
+		    FAN_FROM_REG(data->fan[nr],
+				 DIV_FROM_REG(data->fan_div[nr]));
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr] = FAN_TO_REG(results[0],
+						       DIV_FROM_REG(data->
+								    fan_div
+								    [nr]));
+			old =
+			    gl520_read_value(client, GL520_REG_FAN_LIMIT);
+
+			if (nr == 0) {
+				old =
+				    (old & 0x00ff) | (data->
+						      fan_min[nr] << 8);
+				if (results[0] == 0)
+					data->alarm_mask &= ~0x20;
+				else
+					data->alarm_mask |= 0x20;
+			} else {
+				old = (old & 0xff00) | data->fan_min[nr];
+				if (results[0] == 0)
+					data->alarm_mask &= ~0x40;
+				else
+					data->alarm_mask |= 0x40;
+			}
+			gl520_write_value(client, GL520_REG_FAN_LIMIT,
+					  old);
+		}
+	}
+}
+
+
+void gl520_alarms(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct gl520_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl520_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->alarms);
+		*nrels_mag = 1;
+	}
+}
+
+void gl520_beep(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct gl520_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl520_update_client(client);
+		results[0] = BEEP_ENABLE_FROM_REG(data->beep_enable);
+		results[1] = BEEPS_FROM_REG(data->beeps);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->beep_enable = BEEP_ENABLE_TO_REG(results[0]);
+			gl520_write_value(client, GL520_REG_CONF,
+					  (gl520_read_value(client,
+							    GL520_REG_CONF)
+					   & 0xfb) | (data->
+						      beep_enable << 2));
+		}
+		if (*nrels_mag >= 2) {
+			data->beeps =
+			    BEEPS_TO_REG(results[1]) & data->alarm_mask;
+			gl520_write_value(client, GL520_REG_ALARM,
+					  data->beeps);
+		}
+	}
+}
+
+
+void gl520_fan_div(struct i2c_client *client, int operation, int ctl_name,
+		   int *nrels_mag, long *results)
+{
+	struct gl520_data *data = client->data;
+	int old;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl520_update_client(client);
+		results[0] = DIV_FROM_REG(data->fan_div[0]);
+		results[1] = DIV_FROM_REG(data->fan_div[1]);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		old = gl520_read_value(client, GL520_REG_MISC);
+		if (*nrels_mag >= 2) {
+			data->fan_div[1] = DIV_TO_REG(results[1]);
+			old = (old & 0xcf) | (data->fan_div[1] << 4);
+		}
+		if (*nrels_mag >= 1) {
+			data->fan_div[0] = DIV_TO_REG(results[0]);
+			old = (old & 0x3f) | (data->fan_div[0] << 6);
+		}
+		gl520_write_value(client, GL520_REG_MISC, old);
+	}
+}
+
+void gl520_vid(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct gl520_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		gl520_update_client(client);
+		results[0] = VID_FROM_REG(data->vid);
+		*nrels_mag = 1;
+	}
+}
+
+void gl520_fan1off(struct i2c_client *client, int operation, int ctl_name,
+		   int *nrels_mag, long *results)
+{
+	int old;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] =
+		    ((gl520_read_value(client, GL520_REG_MISC) & 0x04) !=
+		     0);
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			old =
+			    gl520_read_value(client,
+					     GL520_REG_MISC) & 0xfb;
+			if (results[0])
+				old |= 0x04;
+			gl520_write_value(client, GL520_REG_MISC, old);
+		}
+	}
+}
+
+void gl520_config(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct gl520_data *data = client->data;
+	int old;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] =
+		    ((gl520_read_value(client, GL520_REG_CONF) & 0x10) ==
+		     0);
+		data->two_temps = results[0];
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			old =
+			    gl520_read_value(client,
+					     GL520_REG_CONF) & 0xef;
+			if (!results[1]) {
+				old |= 0x10;
+				data->two_temps = 0;
+				data->temp_hyst[1] = data->temp_over[1] =
+					data->temp[1] = 0;
+			} else {
+				data->two_temps = 1;
+				data->voltage_max[4] = data->voltage_min[4] =
+					data->voltage[4] = 0;
+			}
+			gl520_write_value(client, GL520_REG_CONF, old);
+		}
+	}
+}
+
+static int __init sm_gl520sm_init(void)
+{
+	printk("gl520sm.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&gl520_driver);
+}
+
+static void __exit sm_gl520sm_exit(void)
+{
+	i2c_del_driver(&gl520_driver);
+}
+
+
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Kyösti Mälkki <kmalkki@cc.hut.fi>");
+MODULE_DESCRIPTION("GL520SM driver");
+
+module_init(sm_gl520sm_init);
+module_exit(sm_gl520sm_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/it87.c linux-2.4.27-leo/drivers/sensors/it87.c
--- linux-2.4.27/drivers/sensors/it87.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/it87.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,1128 @@
+/*
+    it87.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring.
+
+    Supports: IT8705F  Super I/O chip w/LPC interface
+              IT8712F  Super I/O chup w/LPC interface & SMbus
+              Sis950   A clone of the IT8705F
+
+    Copyright (c) 2001 Chris Gauthron <chrisg@0-in.com> 
+    Largely inspired by lm78.c of the same package
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+    djg@pdp8.net David Gesswein 7/18/01
+    Modified to fix bug with not all alarms enabled.
+    Added ability to read battery voltage and select temperature sensor
+    type at module load time.
+*/
+
+/*
+    michael.hufer@gmx.de Michael Hufer 09/07/03
+    Modified configure (enable/disable) chip reset at module load time.
+    Added ability to read and set fan pwm registers and the smart
+    guardian (sg) features of the chip.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x20, 0x2f, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { 0x0290, SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_4(it87, it8705, it8712, sis950);
+
+
+#define	REG	0x2e	/* The register to read/write */
+#define	DEV	0x07	/* Register: Logical device select */
+#define	VAL	0x2f	/* The value to read/write */
+#define PME	0x04	/* The device with the fan registers in it */
+#define	DEVID	0x20	/* Register: Device ID */
+
+static inline void
+superio_outb(int reg, int val)
+{
+	outb(reg, REG);
+	outb(val, VAL);
+}
+
+static inline int
+superio_inb(int reg)
+{
+	outb(reg, REG);
+	return inb(VAL);
+}
+
+static inline void
+superio_select(void)
+{
+	outb(DEV, REG);
+	outb(PME, VAL);
+}
+
+static inline void
+superio_enter(void)
+{
+	outb(0x87, REG);
+	outb(0x01, REG);
+	outb(0x55, REG);
+	outb(0x55, REG);
+}
+
+static inline void
+superio_exit(void)
+{
+	outb(0x02, REG);
+	outb(0x02, VAL);
+}
+
+/* just IT8712F for now - this should be extended to support the other
+   chips as well */
+#define IT87_DEVID_MATCH(id) ((id) == 0x8712)
+
+#define IT87_ACT_REG  0x30
+#define IT87_BASE_REG 0x60
+
+/* Update battery voltage after every reading if true */
+static int update_vbat = 0;
+
+/* Reset the registers on init */
+static int reset = 0;
+
+/* Many IT87 constants specified below */
+
+/* Length of ISA address segment */
+#define IT87_EXTENT 8
+
+/* Where are the ISA address/data registers relative to the base address */
+#define IT87_ADDR_REG_OFFSET 5
+#define IT87_DATA_REG_OFFSET 6
+
+/*----- The IT87 registers -----*/
+
+#define IT87_REG_CONFIG        0x00
+
+#define IT87_REG_ALARM1        0x01
+#define IT87_REG_ALARM2        0x02
+#define IT87_REG_ALARM3        0x03
+
+#define IT87_REG_VID           0x0a
+#define IT87_REG_FAN_DIV       0x0b
+
+#define IT87_REG_FAN(nr)       (0x0c + (nr))
+#define IT87_REG_FAN_MIN(nr)   (0x0f + (nr))
+#define IT87_REG_FAN_CTRL      0x13
+
+/* pwm and smart guardian registers */
+
+#define IT87_REG_FAN_ONOFF     0x14
+#define IT87_REG_PWM(nr)       (0x14 + (nr))
+#define IT87_REG_SG_TL_OFF(nr) (0x58 + (nr)*8)
+#define IT87_REG_SG_TL_LOW(nr) (0x59 + (nr)*8)
+#define IT87_REG_SG_TL_MED(nr) (0x5a + (nr)*8)
+#define IT87_REG_SG_TL_HI(nr)  (0x5b + (nr)*8)
+#define IT87_REG_SG_TL_OVR(nr) (0x5c + (nr)*8)
+#define IT87_REG_SG_PWM_LOW(nr) (0x5d + (nr)*8)
+#define IT87_REG_SG_PWM_MED(nr) (0x5e + (nr)*8)
+#define IT87_REG_SG_PWM_HI(nr)  (0x5f + (nr)*8)
+
+/* Monitors: 9 voltage (0 to 7, battery), 3 temp (1 to 3), 3 fan (1 to 3) */
+
+#define IT87_REG_VIN(nr)       (0x20 + (nr))
+#define IT87_REG_TEMP(nr)      (0x28 + (nr))
+
+#define IT87_REG_VIN_MAX(nr)   (0x30 + (nr) * 2)
+#define IT87_REG_VIN_MIN(nr)   (0x31 + (nr) * 2)
+#define IT87_REG_TEMP_HIGH(nr) (0x3e + (nr) * 2)
+#define IT87_REG_TEMP_LOW(nr)  (0x3f + (nr) * 2)
+
+#define IT87_REG_I2C_ADDR      0x48
+
+#define IT87_REG_VIN_ENABLE    0x50
+#define IT87_REG_TEMP_ENABLE   0x51
+
+#define IT87_REG_CHIPID        0x58
+
+/* sensor pin types */
+#define UNUSED		0
+#define THERMISTOR	2
+#define PIIDIODE	3
+
+/* Conversions. Limit checking is only done on the TO_REG 
+   variants. Note that you should be a bit careful with which arguments
+   these macros are called: arguments may be evaluated more than once.
+   Fixing this is just not worth it. */
+#define IN_TO_REG(val)  (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255))
+#define IN_FROM_REG(val) (((val) *  16 + 5) / 10)
+
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+	if (rpm == 0)
+		return 255;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1,
+			     254);
+}
+
+#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div)))
+
+#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/10):\
+                                                 ((val)+5)/10),-127,127))
+#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10)
+
+#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\
+                           205-(val)*5)
+#define ALARMS_FROM_REG(val) (val)
+
+extern inline u8 DIV_TO_REG(long val)
+{
+	u8 i;
+	for( i = 0; i <= 7; i++ )
+	{
+		if( val>>i == 1 )
+			return i;
+	}
+	return 1;
+}
+#define DIV_FROM_REG(val) (1 << (val))
+
+/* For each registered IT87, we need to keep some data in memory. That
+   data is pointed to by it87_list[NR]->data. The structure itself is
+   dynamically allocated, at the same time when a new it87 client is
+   allocated. */
+struct it87_data {
+	struct i2c_client client;
+	struct semaphore lock;
+	int sysctl_id;
+	enum chips type;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 in[9];		/* Register value */
+	u8 in_max[9];		/* Register value */
+	u8 in_min[9];		/* Register value */
+	u8 fan[3];		/* Register value */
+	u8 fan_min[3];		/* Register value */
+	u8 temp[3];		/* Register value */
+	u8 temp_high[3];	/* Register value */
+	u8 temp_low[3];		/* Register value */
+	u8 fan_div[3];		/* Register encoding, shifted right */
+	u8 vid;			/* Register encoding, combined */
+	u32 alarms;		/* Register encoding, combined */
+	u8 pwm[3];		/* Register value */
+	u8 fan_ctl[2];		/* Register encoding */
+	u8 sg_tl[3][5];		/* Register value */
+	u8 sg_pwm[3][3];	/* Register value */
+	u8 sens[3];		/* 2 = Thermistor,
+				   3 = PII/Celeron diode */
+};
+
+
+static int it87_attach_adapter(struct i2c_adapter *adapter);
+static int it87_find(int *address);
+static int it87_detect(struct i2c_adapter *adapter, int address,
+		       unsigned short flags, int kind);
+static int it87_detach_client(struct i2c_client *client);
+
+static int it87_read_value(struct i2c_client *client, u8 register);
+static int it87_write_value(struct i2c_client *client, u8 register,
+			    u8 value);
+static void it87_update_client(struct i2c_client *client);
+static void it87_init_client(struct i2c_client *client);
+
+
+static void it87_in(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results);
+static void it87_fan(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results);
+static void it87_temp(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void it87_vid(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results);
+static void it87_alarms(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void it87_fan_div(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void it87_fan_ctl(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void it87_pwm(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void it87_sgpwm(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void it87_sgtl(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void it87_sens(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+
+static struct i2c_driver it87_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "IT87xx sensor driver",
+	.id		= I2C_DRIVERID_IT87,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= it87_attach_adapter,
+	.detach_client	= it87_detach_client,
+};
+
+static int it87_id = 0;
+
+/* The /proc/sys entries */
+
+/* -- SENSORS SYSCTL START -- */
+#define IT87_SYSCTL_IN0 1000    /* Volts * 100 */
+#define IT87_SYSCTL_IN1 1001
+#define IT87_SYSCTL_IN2 1002
+#define IT87_SYSCTL_IN3 1003
+#define IT87_SYSCTL_IN4 1004
+#define IT87_SYSCTL_IN5 1005
+#define IT87_SYSCTL_IN6 1006
+#define IT87_SYSCTL_IN7 1007
+#define IT87_SYSCTL_IN8 1008
+#define IT87_SYSCTL_FAN1 1101   /* Rotations/min */
+#define IT87_SYSCTL_FAN2 1102
+#define IT87_SYSCTL_FAN3 1103
+#define IT87_SYSCTL_TEMP1 1200  /* Degrees Celcius * 10 */
+#define IT87_SYSCTL_TEMP2 1201  /* Degrees Celcius * 10 */
+#define IT87_SYSCTL_TEMP3 1202  /* Degrees Celcius * 10 */
+#define IT87_SYSCTL_VID 1300    /* Volts * 100 */
+#define IT87_SYSCTL_FAN_DIV 2000        /* 1, 2, 4 or 8 */
+#define IT87_SYSCTL_ALARMS 2004    /* bitvector */
+
+#define IT87_SYSCTL_PWM1 1401
+#define IT87_SYSCTL_PWM2 1402
+#define IT87_SYSCTL_PWM3 1403
+#define IT87_SYSCTL_FAN_CTL  1501
+#define IT87_SYSCTL_FAN_ON_OFF  1502
+#define IT87_SYSCTL_SENS1 1601	/* 1, 2, or Beta (3000-5000) */
+#define IT87_SYSCTL_SENS2 1602
+#define IT87_SYSCTL_SENS3 1603
+
+#define IT87_ALARM_IN0 0x000100
+#define IT87_ALARM_IN1 0x000200
+#define IT87_ALARM_IN2 0x000400
+#define IT87_ALARM_IN3 0x000800
+#define IT87_ALARM_IN4 0x001000
+#define IT87_ALARM_IN5 0x002000
+#define IT87_ALARM_IN6 0x004000
+#define IT87_ALARM_IN7 0x008000
+#define IT87_ALARM_FAN1 0x0001
+#define IT87_ALARM_FAN2 0x0002
+#define IT87_ALARM_FAN3 0x0004
+#define IT87_ALARM_TEMP1 0x00010000
+#define IT87_ALARM_TEMP2 0x00020000
+#define IT87_ALARM_TEMP3 0x00040000
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected IT87. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized 
+   when a new copy is allocated. */
+static ctl_table it87_dir_table_template[] = {
+	{IT87_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_in},
+	{IT87_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_in},
+	{IT87_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_in},
+	{IT87_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_in},
+	{IT87_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_in},
+	{IT87_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_in},
+	{IT87_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_in},
+	{IT87_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_in},
+	{IT87_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_in},
+	{IT87_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_fan},
+	{IT87_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_fan},
+	{IT87_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_fan},
+	{IT87_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_temp},
+	{IT87_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_temp},
+	{IT87_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_temp},
+	{IT87_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_vid},
+	{IT87_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_fan_div},
+	{IT87_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_alarms},
+	{IT87_SYSCTL_FAN_CTL, "fan_ctl", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_fan_ctl},
+	{IT87_SYSCTL_FAN_ON_OFF, "fan_on_off", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_fan_ctl},
+	{IT87_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_pwm},
+	{IT87_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_pwm},
+	{IT87_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_pwm},
+	{IT87_SYSCTL_PWM1, "sg_pwm1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_sgpwm},
+	{IT87_SYSCTL_PWM2, "sg_pwm2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_sgpwm},
+	{IT87_SYSCTL_PWM3, "sg_pwm3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_sgpwm},
+	{IT87_SYSCTL_PWM1, "sg_tl1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_sgtl},
+	{IT87_SYSCTL_PWM2, "sg_tl2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_sgtl},
+	{IT87_SYSCTL_PWM3, "sg_tl3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_sgtl},
+	{IT87_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_sens},
+	{IT87_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_sens},
+	{IT87_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &it87_sens},
+	{0}
+};
+
+
+/* This function is called when:
+     * it87_driver is inserted (when this module is loaded), for each
+       available adapter
+     * when a new adapter is inserted (and it87_driver is still present) */
+static int it87_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, it87_detect);
+}
+
+static int it87_find(int *address)
+{
+	u16 val;
+
+	superio_enter();
+	val = (superio_inb(DEVID) << 8) |
+	       superio_inb(DEVID + 1);
+	if (!IT87_DEVID_MATCH(val)) {
+		superio_exit();
+		return -ENODEV;
+	}
+
+	superio_select();
+	val = (superio_inb(IT87_BASE_REG) << 8) |
+	       superio_inb(IT87_BASE_REG + 1);
+	superio_exit();
+	*address = val & ~(IT87_EXTENT - 1);
+	if (*address == 0) {
+		return -ENODEV;
+	}
+	return 0;
+}
+
+/* This function is called by i2c_detect */
+int it87_detect(struct i2c_adapter *adapter, int address,
+		unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct it87_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+	int is_isa = i2c_is_isa_adapter(adapter);
+
+	if (!is_isa
+	 && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+	 	return 0;
+
+	if (is_isa
+	 && check_region(address, IT87_EXTENT))
+	 	return 0;
+
+	/* Probe whether there is anything available on this address. Already
+	   done for SMBus clients */
+	if (is_isa && kind < 0) {
+#define REALLY_SLOW_IO
+		/* We need the timeouts for at least some IT87-like chips.
+		   But only if we read 'undefined' registers. */
+		i = inb_p(address + 1);
+		if (inb_p(address + 2) != i
+		 || inb_p(address + 3) != i
+		 || inb_p(address + 7) != i)
+			return -ENODEV;
+#undef REALLY_SLOW_IO
+
+		/* Let's just hope nothing breaks here */
+		i = inb_p(address + 5) & 0x7f;
+		outb_p(~i & 0x7f, address + 5);
+		if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) {
+			outb_p(i, address + 5);
+			return -ENODEV;
+		}
+	}
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access it87_{read,write}_value. */
+
+	if (!(data = kmalloc(sizeof(struct it87_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	if (is_isa)
+		init_MUTEX(&data->lock);
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &it87_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+
+	if (kind < 0) {
+		if ((it87_read_value(new_client, IT87_REG_CONFIG) & 0x80)
+		 || (!is_isa
+		  && it87_read_value(new_client, IT87_REG_I2C_ADDR) != address)) {
+		  	err = -ENODEV;
+		 	goto ERROR1;
+		}
+	}
+
+	/* Determine the chip type. */
+	if (kind <= 0) {
+		i = it87_read_value(new_client, IT87_REG_CHIPID);
+		if (i == 0x90) {
+			kind = it87;
+		}
+		else {
+			if (kind == 0)
+				printk
+				    ("it87.o: Ignoring 'force' parameter for unknown chip at "
+				     "adapter %d, address 0x%02x\n",
+				     i2c_adapter_id(adapter), address);
+			err = -ENODEV;
+			goto ERROR1;
+		}
+	}
+
+	if (kind == it87) {
+		type_name = "it87";
+		client_name = "IT87 chip";
+	} /* else if (kind == it8712) {
+		type_name = "it8712";
+		client_name = "IT87-J chip";
+	} */ else {
+#ifdef DEBUG
+		printk("it87.o: Internal error: unknown kind (%d)?!?",
+		       kind);
+#endif
+		goto ERROR1;
+	}
+
+	/* Reserve the ISA region */
+	if (is_isa)
+		request_region(address, IT87_EXTENT, type_name);
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	data->type = kind;
+
+	new_client->id = it87_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client,
+				    type_name,
+				    it87_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the IT87 chip */
+	it87_init_client(new_client);
+	return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+	if (is_isa)
+		release_region(address, IT87_EXTENT);
+      ERROR1:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int it87_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct it87_data *) (client->data))->
+				sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("it87.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	if(i2c_is_isa_client(client))
+		release_region(client->addr, IT87_EXTENT);
+	kfree(client->data);
+
+	return 0;
+}
+
+/* The SMBus locks itself, but ISA access must be locked explicitely! 
+   We don't want to lock the whole ISA bus, so we lock each client
+   separately.
+   We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks,
+   would slow down the IT87 access and should not be necessary. 
+   There are some ugly typecasts here, but the good new is - they should
+   nowhere else be necessary! */
+static int it87_read_value(struct i2c_client *client, u8 reg)
+{
+	int res;
+	if (i2c_is_isa_client(client)) {
+		down(&(((struct it87_data *) (client->data))->lock));
+		outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET);
+		res = inb_p(client->addr + IT87_DATA_REG_OFFSET);
+		up(&(((struct it87_data *) (client->data))->lock));
+		return res;
+	} else
+		return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* The SMBus locks itself, but ISA access muse be locked explicitely! 
+   We don't want to lock the whole ISA bus, so we lock each client
+   separately.
+   We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks,
+   would slow down the IT87 access and should not be necessary. 
+   There are some ugly typecasts here, but the good new is - they should
+   nowhere else be necessary! */
+static int it87_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+	if (i2c_is_isa_client(client)) {
+		down(&(((struct it87_data *) (client->data))->lock));
+		outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET);
+		outb_p(value, client->addr + IT87_DATA_REG_OFFSET);
+		up(&(((struct it87_data *) (client->data))->lock));
+		return 0;
+	} else
+		return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* Called when we have found a new IT87. */
+static void it87_init_client(struct i2c_client *client)
+{
+	int tmp;
+
+	if (reset) {
+		/* Reset all except Watchdog values and last conversion values
+		   This sets fan-divs to 2, among others */
+		it87_write_value(client, IT87_REG_CONFIG, 0x80);
+	}
+
+	/* Check if temperature channnels are reset manually or by some reason */
+	tmp = it87_read_value(client, IT87_REG_TEMP_ENABLE);
+	if ((tmp & 0x3f) == 0) {
+		/* Temp1,Temp3=thermistor; Temp2=thermal diode */
+		tmp = (tmp & 0xc0) | 0x2a;
+		it87_write_value(client, IT87_REG_TEMP_ENABLE, tmp);
+	}
+
+	/* Check if voltage monitors are reset manually or by some reason */
+	tmp = it87_read_value(client, IT87_REG_VIN_ENABLE);
+	if ((tmp & 0xff) == 0) {
+		/* Enable all voltage monitors */
+		it87_write_value(client, IT87_REG_VIN_ENABLE, 0xff);
+	}
+
+	/* Check if tachometers are reset manually or by some reason */
+	tmp = it87_read_value(client, IT87_REG_FAN_CTRL);
+	if ((tmp & 0x70) == 0) {
+		/* Enable all fan tachometers */
+		tmp = (tmp & 0x8f) | 0x70;
+		it87_write_value(client, IT87_REG_FAN_CTRL, tmp);
+	}
+
+	/* Start monitoring */
+	it87_write_value(client, IT87_REG_CONFIG,
+			 (it87_read_value(client, IT87_REG_CONFIG) & 0x36)
+			 | (update_vbat ? 0x41 : 0x01));
+}
+
+static void it87_update_client(struct i2c_client *client)
+{
+	struct it87_data *data = client->data;
+	int i, tmp, tmp2;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+		if (update_vbat) {
+	/* Cleared after each update, so reenable.  Value
+	   returned by this read will be previous value */
+			it87_write_value(client, IT87_REG_CONFIG,
+			   it87_read_value(client, IT87_REG_CONFIG) | 0x40);
+		}
+		for (i = 0; i <= 7; i++) {
+			data->in[i] =
+			    it87_read_value(client, IT87_REG_VIN(i));
+			data->in_min[i] =
+			    it87_read_value(client, IT87_REG_VIN_MIN(i));
+			data->in_max[i] =
+			    it87_read_value(client, IT87_REG_VIN_MAX(i));
+		}
+		data->in[8] =
+		    it87_read_value(client, IT87_REG_VIN(8));
+		/* VBAT sensor doesn't have limit registers, set
+		   to min and max value */
+		data->in_min[8] = 0;
+		data->in_max[8] = 255;
+                
+		for (i = 1; i <= 3; i++) {
+			data->fan[i - 1] =
+			    it87_read_value(client, IT87_REG_FAN(i));
+			data->fan_min[i - 1] =
+			    it87_read_value(client, IT87_REG_FAN_MIN(i));
+		}
+		for (i = 1; i <= 3; i++) {
+			data->temp[i - 1] =
+			    it87_read_value(client, IT87_REG_TEMP(i));
+			data->temp_high[i - 1] =
+			    it87_read_value(client, IT87_REG_TEMP_HIGH(i));
+			data->temp_low[i - 1] =
+			    it87_read_value(client, IT87_REG_TEMP_LOW(i));
+		}
+
+		/* The 8705 does not have VID capability */
+		/*if (data->type == it8712) {
+			data->vid = it87_read_value(client, IT87_REG_VID);
+			data->vid &= 0x1f;
+		}
+		else */ {
+			data->vid = 0x1f;
+		}
+
+		i = it87_read_value(client, IT87_REG_FAN_DIV);
+		data->fan_div[0] = i & 0x07;
+		data->fan_div[1] = (i >> 3) & 0x07;
+		data->fan_div[2] = ( (i&0x40)==0x40 ? 3 : 1 );
+
+		for( i = 1; i <= 3; i++ ) {
+			data->pwm[i-1] = it87_read_value(client, IT87_REG_PWM(i));
+			data->sg_tl[i-1][0] = it87_read_value(client, IT87_REG_SG_TL_OFF(i));
+			data->sg_tl[i-1][1] = it87_read_value(client, IT87_REG_SG_TL_LOW(i));
+			data->sg_tl[i-1][2] = it87_read_value(client, IT87_REG_SG_TL_MED(i));
+			data->sg_tl[i-1][3] = it87_read_value(client, IT87_REG_SG_TL_HI(i));
+			data->sg_tl[i-1][4] = it87_read_value(client, IT87_REG_SG_TL_OVR(i));
+			data->sg_pwm[i-1][0] = it87_read_value(client, IT87_REG_SG_PWM_LOW(i));
+			data->sg_pwm[i-1][1] = it87_read_value(client, IT87_REG_SG_PWM_MED(i));
+			data->sg_pwm[i-1][2] = it87_read_value(client, IT87_REG_SG_PWM_HI(i));
+		}
+		data->alarms =
+			it87_read_value(client, IT87_REG_ALARM1) |
+			(it87_read_value(client, IT87_REG_ALARM2) << 8) |
+			(it87_read_value(client, IT87_REG_ALARM3) << 16);
+		data->fan_ctl[0] = it87_read_value(client, IT87_REG_FAN_CTRL);
+		data->fan_ctl[1] = it87_read_value(client, IT87_REG_FAN_ONOFF);
+
+		tmp = it87_read_value(client, IT87_REG_TEMP_ENABLE);
+		for(i = 0; i < 3; i++) {
+			tmp2 = (tmp >> i) & 0x09;
+			if(tmp2 == 0x01)
+				data->sens[i] = PIIDIODE;
+			else if(tmp2 == 0x08)
+				data->sens[i] = THERMISTOR;
+			else
+				data->sens[i] = UNUSED;
+		}
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+/* The next few functions are the call-back functions of the /proc/sys and
+   sysctl files. Which function is used is defined in the ctl_table in
+   the extra1 field.
+    - Each function must return the magnitude (power of 10 to divide the
+      data with) if it is called with operation==SENSORS_PROC_REAL_INFO.
+    - It must put a maximum of *nrels elements in results reflecting the
+      data of this file, and set *nrels to the number it actually put 
+      in it, if operation==SENSORS_PROC_REAL_READ.
+    - Finally, it must get upto *nrels elements from results and write them
+      to the chip, if operations==SENSORS_PROC_REAL_WRITE.
+   Note that on SENSORS_PROC_REAL_READ, I do not check whether results is
+   large enough (by checking the incoming value of *nrels). This is not very
+   good practice, but as long as you put less than about 5 values in results,
+   you can assume it is large enough. */
+void it87_in(struct i2c_client *client, int operation, int ctl_name,
+	     int *nrels_mag, long *results)
+{
+	struct it87_data *data = client->data;
+	int nr = ctl_name - IT87_SYSCTL_IN0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		it87_update_client(client);
+		results[0] = IN_FROM_REG(data->in_min[nr]);
+		results[1] = IN_FROM_REG(data->in_max[nr]);
+		results[2] = IN_FROM_REG(data->in[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->in_min[nr] = IN_TO_REG(results[0]);
+			it87_write_value(client, IT87_REG_VIN_MIN(nr),
+					 data->in_min[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->in_max[nr] = IN_TO_REG(results[1]);
+			it87_write_value(client, IT87_REG_VIN_MAX(nr),
+					 data->in_max[nr]);
+		}
+	}
+}
+
+void it87_fan(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct it87_data *data = client->data;
+	int nr = ctl_name - IT87_SYSCTL_FAN1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		it87_update_client(client);
+		results[0] = FAN_FROM_REG(data->fan_min[nr - 1],
+					  DIV_FROM_REG(data->fan_div[nr - 1]));
+		results[1] = FAN_FROM_REG(data->fan[nr - 1],
+				 DIV_FROM_REG(data->fan_div[nr - 1]));
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr - 1] = FAN_TO_REG(results[0],
+							   DIV_FROM_REG(data->fan_div[nr - 1]));
+			it87_write_value(client, IT87_REG_FAN_MIN(nr),
+					 data->fan_min[nr - 1]);
+		}
+	}
+}
+
+
+void it87_temp(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct it87_data *data = client->data;
+	int nr = ctl_name - IT87_SYSCTL_TEMP1 + 1;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		it87_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_high[nr - 1]);
+		results[1] = TEMP_FROM_REG(data->temp_low[nr - 1]);
+		results[2] = TEMP_FROM_REG(data->temp[nr - 1]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_high[nr - 1] = TEMP_TO_REG(results[0]);
+			it87_write_value(client, IT87_REG_TEMP_HIGH(nr),
+					 data->temp_high[nr - 1]);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_low[nr - 1] = TEMP_TO_REG(results[1]);
+			it87_write_value(client, IT87_REG_TEMP_LOW(nr),
+					 data->temp_low[nr - 1]);
+		}
+	}
+}
+
+void it87_pwm(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct it87_data *data = client->data;
+	int nr = ctl_name - IT87_SYSCTL_PWM1 + 1;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		it87_update_client(client);
+		results[0] = data->pwm[nr - 1];
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->pwm[nr - 1] = results[0];
+			it87_write_value(client, IT87_REG_PWM(nr), data->pwm[nr - 1]);
+		}
+	}
+}
+
+void it87_sgpwm(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct it87_data *data = client->data;
+	int nr = ctl_name - IT87_SYSCTL_PWM1 + 1;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		it87_update_client(client);
+		results[0] = data->sg_pwm[nr - 1][0];
+		results[1] = data->sg_pwm[nr - 1][1];
+		results[2] = data->sg_pwm[nr - 1][2];
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->sg_pwm[nr - 1][0] = results[0];
+			it87_write_value(client, IT87_REG_SG_PWM_LOW(nr), data->sg_pwm[nr - 1][0]);
+		}
+		if (*nrels_mag >= 2) {
+			data->sg_pwm[nr - 1][1] = results[1];
+			it87_write_value(client, IT87_REG_SG_PWM_MED(nr), data->sg_pwm[nr - 1][1]);
+		}
+		if (*nrels_mag >= 3) {
+			data->sg_pwm[nr - 1][2] = results[2];
+			it87_write_value(client, IT87_REG_SG_PWM_HI(nr), data->sg_pwm[nr - 1][2]);
+		}
+	}
+}
+
+void it87_sgtl(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct it87_data *data = client->data;
+	int nr = ctl_name - IT87_SYSCTL_PWM1 + 1;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		it87_update_client(client);
+		results[0] = TEMP_FROM_REG(data->sg_tl[nr - 1][0]);
+		results[1] = TEMP_FROM_REG(data->sg_tl[nr - 1][1]);
+		results[2] = TEMP_FROM_REG(data->sg_tl[nr - 1][2]);
+		results[3] = TEMP_FROM_REG(data->sg_tl[nr - 1][3]);
+		results[4] = TEMP_FROM_REG(data->sg_tl[nr - 1][4]);
+		*nrels_mag = 5;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->sg_tl[nr - 1][0] = TEMP_TO_REG(results[0]);
+			it87_write_value(client, IT87_REG_SG_TL_OFF(nr), data->sg_tl[nr - 1][0]);
+		}
+		if (*nrels_mag >= 2) {
+			data->sg_tl[nr - 1][1] = TEMP_TO_REG(results[1]);
+			it87_write_value(client, IT87_REG_SG_TL_LOW(nr), data->sg_tl[nr - 1][1]);
+		}
+		if (*nrels_mag >= 3) {
+			data->sg_tl[nr - 1][2] = TEMP_TO_REG(results[2]);
+			it87_write_value(client, IT87_REG_SG_TL_MED(nr), data->sg_tl[nr - 1][2]);
+		}
+		if (*nrels_mag >= 4) {
+			data->sg_tl[nr - 1][3] = TEMP_TO_REG(results[3]);
+			it87_write_value(client, IT87_REG_SG_TL_HI(nr), data->sg_tl[nr - 1][3]);
+		}
+		if (*nrels_mag >= 5) {
+			data->sg_tl[nr - 1][4] = TEMP_TO_REG(results[4]);
+			it87_write_value(client, IT87_REG_SG_TL_OVR(nr), data->sg_tl[nr - 1][4]);
+		}
+	}
+}
+
+void it87_vid(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct it87_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		it87_update_client(client);
+		results[0] = VID_FROM_REG(data->vid);
+		*nrels_mag = 1;
+	}
+}
+
+void it87_alarms(struct i2c_client *client, int operation,
+                     int ctl_name, int *nrels_mag, long *results)
+{
+	struct it87_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		it87_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->alarms);
+		*nrels_mag = 1;
+	}
+}
+
+void it87_fan_div(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct it87_data *data = client->data;
+	int old;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		it87_update_client(client);
+		results[0] = DIV_FROM_REG(data->fan_div[0]);
+		results[1] = DIV_FROM_REG(data->fan_div[1]);
+		results[2] = DIV_FROM_REG(data->fan_div[2]);;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		old = it87_read_value(client, IT87_REG_FAN_DIV);
+		if (*nrels_mag >= 3) {
+			data->fan_div[2] = DIV_TO_REG(results[2]);
+			if( data->fan[2]!=3 ) {
+				data->fan_div[2] = 1;
+				old = (old & 0xbf);
+			} else {
+				old = (old | 0x40);
+			}
+		}
+		if (*nrels_mag >= 2) {
+			data->fan_div[1] = DIV_TO_REG(results[1]);
+			old = (old & 0xc3) | (data->fan_div[1] << 3);
+		}
+		if (*nrels_mag >= 1) {
+			data->fan_div[0] = DIV_TO_REG(results[0]);
+			old = (old & 0xf8) | data->fan_div[0];
+			it87_write_value(client, IT87_REG_FAN_DIV, old);
+		}
+	}
+}
+
+void it87_fan_ctl(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct it87_data *data = client->data;
+	int index = ctl_name - IT87_SYSCTL_FAN_CTL;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		it87_update_client(client);
+		results[0] = data->fan_ctl[index];
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->fan_ctl[index] = results[0];
+			if( index == 0 )
+				it87_write_value(client, IT87_REG_FAN_CTRL, data->fan_ctl[index] );
+			else
+				it87_write_value(client, IT87_REG_FAN_ONOFF, data->fan_ctl[index] );
+		}
+	}
+}
+
+void it87_sens(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct it87_data *data = client->data;
+	int nr = 1 + ctl_name - IT87_SYSCTL_SENS1;
+	u8 tmp, val1, val2;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = data->sens[nr - 1];
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			val1 = 0x01 << (nr - 1);
+			val2 = 0x08 << (nr - 1);
+			tmp = it87_read_value(client, IT87_REG_TEMP_ENABLE);
+			switch (results[0]) {
+			case PIIDIODE:
+				tmp &= ~ val2;
+				tmp |= val1;
+				break;
+			case THERMISTOR:
+				tmp &= ~ val1;
+				tmp |= val2;
+				break;
+			case UNUSED:
+				tmp &= ~ val1;
+				tmp &= ~ val2;
+				break;
+			default:
+				printk(KERN_ERR "it87.o: Invalid sensor type %ld; "
+				       "must be 0 (unused), 2 (thermistor) "
+				       "or 3 (diode)\n", results[0]);
+				return;
+			}
+			it87_write_value(client,
+					 IT87_REG_TEMP_ENABLE, tmp);
+			data->sens[nr - 1] = results[0];
+		}
+	}
+}
+
+static int __init sm_it87_init(void)
+{
+	int addr;
+
+	printk("it87.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	if (!it87_find(&addr)) {
+		normal_isa[0] = addr;
+	}
+	return i2c_add_driver(&it87_driver);
+}
+
+static void __exit sm_it87_exit(void)
+{
+	i2c_del_driver(&it87_driver);
+}
+
+
+
+MODULE_AUTHOR("Chris Gauthron <chrisg@0-in.com>");
+MODULE_DESCRIPTION("IT8705F, IT8712F, Sis950 driver");
+MODULE_PARM(update_vbat, "i");
+MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value");
+MODULE_PARM(reset, "i");
+MODULE_PARM_DESC(reset, "Reset the chip's registers, default no");
+
+module_init(sm_it87_init);
+module_exit(sm_it87_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/lm75.c linux-2.4.27-leo/drivers/sensors/lm75.c
--- linux-2.4.27/drivers/sensors/lm75.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/lm75.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,312 @@
+/*
+    lm75.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+#include "lm75.h"
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x48, 0x4f, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(lm75);
+
+/* Many LM75 constants specified below */
+
+/* The LM75 registers */
+#define LM75_REG_TEMP 0x00
+#define LM75_REG_CONF 0x01
+#define LM75_REG_TEMP_HYST 0x02
+#define LM75_REG_TEMP_OS 0x03
+
+/* Each client has this additional data */
+struct lm75_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u16 temp, temp_os, temp_hyst;	/* Register values */
+};
+
+static int lm75_attach_adapter(struct i2c_adapter *adapter);
+static int lm75_detect(struct i2c_adapter *adapter, int address,
+		       unsigned short flags, int kind);
+static void lm75_init_client(struct i2c_client *client);
+static int lm75_detach_client(struct i2c_client *client);
+
+static int lm75_read_value(struct i2c_client *client, u8 reg);
+static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value);
+static void lm75_temp(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void lm75_update_client(struct i2c_client *client);
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver lm75_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "LM75 sensor chip driver",
+	.id		= I2C_DRIVERID_LM75,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= lm75_attach_adapter,
+	.detach_client	= lm75_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+
+#define LM75_SYSCTL_TEMP 1200	/* Degrees Celcius * 10 */
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected LM75. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized
+   when a new copy is allocated. */
+static ctl_table lm75_dir_table_template[] = {
+	{LM75_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm75_temp},
+	{0}
+};
+
+static int lm75_id = 0;
+
+static int lm75_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, lm75_detect);
+}
+
+/* This function is called by i2c_detect */
+int lm75_detect(struct i2c_adapter *adapter, int address,
+		unsigned short flags, int kind)
+{
+	int i, cur, conf, hyst, os;
+	struct i2c_client *new_client;
+	struct lm75_data *data;
+	int err = 0;
+	const char *type_name, *client_name;
+
+	/* Make sure we aren't probing the ISA bus!! This is just a safety check
+	   at this moment; i2c_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("lm75.o: lm75_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+				     I2C_FUNC_SMBUS_WORD_DATA))
+		    goto error0;
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access lm75_{read,write}_value. */
+	if (!(data = kmalloc(sizeof(struct lm75_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto error0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &lm75_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. It is lousy. */
+	if (kind < 0) {
+		cur = i2c_smbus_read_word_data(new_client, 0);
+		conf = i2c_smbus_read_byte_data(new_client, 1);
+		hyst = i2c_smbus_read_word_data(new_client, 2);
+		os = i2c_smbus_read_word_data(new_client, 3);
+		for (i = 0; i <= 0x1f; i++)
+			if (
+			    (i2c_smbus_read_byte_data
+			     (new_client, i * 8 + 1) != conf)
+			    ||
+			    (i2c_smbus_read_word_data
+			     (new_client, i * 8 + 2) != hyst)
+			    ||
+			    (i2c_smbus_read_word_data
+			     (new_client, i * 8 + 3) != os))
+				goto error1;
+	}
+
+	/* Determine the chip type - only one kind supported! */
+	if (kind <= 0)
+		kind = lm75;
+
+	if (kind == lm75) {
+		type_name = "lm75";
+		client_name = "LM75 chip";
+	} else {
+		pr_debug("lm75.o: Internal error: unknown kind (%d)?!?", kind);
+		goto error1;
+	}
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+
+	new_client->id = lm75_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto error3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client, type_name,
+					lm75_dir_table_template)) < 0) {
+		err = i;
+		goto error4;
+	}
+	data->sysctl_id = i;
+
+	lm75_init_client(new_client);
+	return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+
+      error4:
+	i2c_detach_client(new_client);
+      error3:
+      error1:
+	kfree(data);
+      error0:
+	return err;
+}
+
+static int lm75_detach_client(struct i2c_client *client)
+{
+	struct lm75_data *data = client->data;
+
+	i2c_deregister_entry(data->sysctl_id);
+	i2c_detach_client(client);
+	kfree(client->data);
+	return 0;
+}
+
+/* All registers are word-sized, except for the configuration register.
+   LM75 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+static int lm75_read_value(struct i2c_client *client, u8 reg)
+{
+	if (reg == LM75_REG_CONF)
+		return i2c_smbus_read_byte_data(client, reg);
+	else
+		return swab16(i2c_smbus_read_word_data(client, reg));
+}
+
+/* All registers are word-sized, except for the configuration register.
+   LM75 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+	if (reg == LM75_REG_CONF)
+		return i2c_smbus_write_byte_data(client, reg, value);
+	else
+		return i2c_smbus_write_word_data(client, reg, swab16(value));
+}
+
+static void lm75_init_client(struct i2c_client *client)
+{
+	/* Initialize the LM75 chip */
+	lm75_write_value(client, LM75_REG_CONF, 0);
+}
+
+static void lm75_update_client(struct i2c_client *client)
+{
+	struct lm75_data *data = client->data;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+		pr_debug("Starting lm75 update\n");
+
+		data->temp = lm75_read_value(client, LM75_REG_TEMP);
+		data->temp_os = lm75_read_value(client, LM75_REG_TEMP_OS);
+		data->temp_hyst =
+		    lm75_read_value(client, LM75_REG_TEMP_HYST);
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+void lm75_temp(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct lm75_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm75_update_client(client);
+		results[0] = LM75_TEMP_FROM_REG(data->temp_os);
+		results[1] = LM75_TEMP_FROM_REG(data->temp_hyst);
+		results[2] = LM75_TEMP_FROM_REG(data->temp);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_os = LM75_TEMP_TO_REG(results[0]);
+			lm75_write_value(client, LM75_REG_TEMP_OS,
+					 data->temp_os);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_hyst = LM75_TEMP_TO_REG(results[1]);
+			lm75_write_value(client, LM75_REG_TEMP_HYST,
+					 data->temp_hyst);
+		}
+	}
+}
+
+static int __init sm_lm75_init(void)
+{
+	printk(KERN_INFO "lm75.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&lm75_driver);
+}
+
+static void __exit sm_lm75_exit(void)
+{
+	i2c_del_driver(&lm75_driver);
+}
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
+MODULE_DESCRIPTION("LM75 driver");
+MODULE_LICENSE("GPL");
+
+module_init(sm_lm75_init);
+module_exit(sm_lm75_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/lm75.h linux-2.4.27-leo/drivers/sensors/lm75.h
--- linux-2.4.27/drivers/sensors/lm75.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/lm75.h	2004-09-20 22:02:33.000000000 +0100
@@ -0,0 +1,49 @@
+/*
+    lm75.h - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 2003 Mark M. Hoffman <mhoffman@lightlink.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+    This file contains common code for encoding/decoding LM75 type
+    temperature readings, which are emulated by many of the chips
+    we support.  As the user is unlikely to load more than one driver
+    which contains this code, we don't worry about the wasted space.
+*/
+
+#include <linux/i2c-proc.h>
+
+/* straight from the datasheet */
+#define LM75_TEMP_MIN (-550)
+#define LM75_TEMP_MAX 1250
+
+/* TEMP: 0.1C/bit (-55C to +125C)
+   REG: (0.5C/bit, two's complement) << 7 */
+static inline u16 LM75_TEMP_TO_REG(int temp)
+{
+	int ntemp = SENSORS_LIMIT(temp, LM75_TEMP_MIN, LM75_TEMP_MAX);
+	ntemp += (ntemp<0 ? -2 : 2);
+	return (u16)((ntemp / 5) << 7);
+}
+
+static inline int LM75_TEMP_FROM_REG(u16 reg)
+{
+	/* use integer division instead of equivalent right shift to
+	   guarantee arithmetic shift and preserve the sign */
+	return ((s16)reg / 128) * 5;
+}
+
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/lm78.c linux-2.4.27-leo/drivers/sensors/lm78.c
--- linux-2.4.27/drivers/sensors/lm78.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/lm78.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,742 @@
+/*
+    lm78.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl> 
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x20, 0x2f, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { 0x0290, SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_3(lm78, lm78j, lm79);
+
+/* Many LM78 constants specified below */
+
+/* Length of ISA address segment */
+#define LM78_EXTENT 8
+
+/* Where are the ISA address/data registers relative to the base address */
+#define LM78_ADDR_REG_OFFSET 5
+#define LM78_DATA_REG_OFFSET 6
+
+/* The LM78 registers */
+#define LM78_REG_IN_MAX(nr) (0x2b + (nr) * 2)
+#define LM78_REG_IN_MIN(nr) (0x2c + (nr) * 2)
+#define LM78_REG_IN(nr) (0x20 + (nr))
+
+#define LM78_REG_FAN_MIN(nr) (0x3a + (nr))
+#define LM78_REG_FAN(nr) (0x27 + (nr))
+
+#define LM78_REG_TEMP 0x27
+#define LM78_REG_TEMP_OVER 0x39
+#define LM78_REG_TEMP_HYST 0x3a
+
+#define LM78_REG_ALARM1 0x41
+#define LM78_REG_ALARM2 0x42
+
+#define LM78_REG_VID_FANDIV 0x47
+
+#define LM78_REG_CONFIG 0x40
+#define LM78_REG_CHIPID 0x49
+#define LM78_REG_I2C_ADDR 0x48
+
+
+/* Conversions. Limit checking is only done on the TO_REG 
+   variants. Note that you should be a bit careful with which arguments
+   these macros are called: arguments may be evaluated more than once.
+   Fixing this is just not worth it. */
+#define IN_TO_REG(val)  (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255))
+#define IN_FROM_REG(val) (((val) *  16 + 5) / 10)
+
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+	if (rpm == 0)
+		return 255;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1,
+			     254);
+}
+
+#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div)))
+
+#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/10):\
+                                                 ((val)+5)/10),0,255))
+#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10)
+
+#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\
+                           205-(val)*5)
+#define ALARMS_FROM_REG(val) (val)
+
+#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
+#define DIV_FROM_REG(val) (1 << (val))
+
+/* There are some complications in a module like this. First off, LM78 chips
+   may be both present on the SMBus and the ISA bus, and we have to handle
+   those cases separately at some places. Second, there might be several
+   LM78 chips available (well, actually, that is probably never done; but
+   it is a clean illustration of how to handle a case like that). Finally,
+   a specific chip may be attached to *both* ISA and SMBus, and we would
+   not like to detect it double. Fortunately, in the case of the LM78 at
+   least, a register tells us what SMBus address we are on, so that helps
+   a bit - except if there could be more than one SMBus. Groan. No solution
+   for this yet. */
+
+/* This module may seem overly long and complicated. In fact, it is not so
+   bad. Quite a lot of bookkeeping is done. A real driver can often cut
+   some corners. */
+
+/* For each registered LM78, we need to keep some data in memory. That
+   data is pointed to by lm78_list[NR]->data. The structure itself is
+   dynamically allocated, at the same time when a new lm78 client is
+   allocated. */
+struct lm78_data {
+	struct i2c_client client;
+	struct semaphore lock;
+	int sysctl_id;
+	enum chips type;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 in[7];		/* Register value */
+	u8 in_max[7];		/* Register value */
+	u8 in_min[7];		/* Register value */
+	u8 fan[3];		/* Register value */
+	u8 fan_min[3];		/* Register value */
+	u8 temp;		/* Register value */
+	u8 temp_over;		/* Register value */
+	u8 temp_hyst;		/* Register value */
+	u8 fan_div[3];		/* Register encoding, shifted right */
+	u8 vid;			/* Register encoding, combined */
+	u16 alarms;		/* Register encoding, combined */
+};
+
+
+static int lm78_attach_adapter(struct i2c_adapter *adapter);
+static int lm78_detect(struct i2c_adapter *adapter, int address,
+		       unsigned short flags, int kind);
+static int lm78_detach_client(struct i2c_client *client);
+
+static int lm78_read_value(struct i2c_client *client, u8 register);
+static int lm78_write_value(struct i2c_client *client, u8 register,
+			    u8 value);
+static void lm78_update_client(struct i2c_client *client);
+static void lm78_init_client(struct i2c_client *client);
+
+
+static void lm78_in(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results);
+static void lm78_fan(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results);
+static void lm78_temp(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void lm78_vid(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results);
+static void lm78_alarms(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm78_fan_div(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+
+static struct i2c_driver lm78_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "LM78(-J) and LM79 sensor driver",
+	.id		= I2C_DRIVERID_LM78,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= lm78_attach_adapter,
+	.detach_client	= lm78_detach_client,
+};
+
+static int lm78_id = 0;
+
+/* The /proc/sys entries */
+
+/* -- SENSORS SYSCTL START -- */
+#define LM78_SYSCTL_IN0 1000	/* Volts * 100 */
+#define LM78_SYSCTL_IN1 1001
+#define LM78_SYSCTL_IN2 1002
+#define LM78_SYSCTL_IN3 1003
+#define LM78_SYSCTL_IN4 1004
+#define LM78_SYSCTL_IN5 1005
+#define LM78_SYSCTL_IN6 1006
+#define LM78_SYSCTL_FAN1 1101	/* Rotations/min */
+#define LM78_SYSCTL_FAN2 1102
+#define LM78_SYSCTL_FAN3 1103
+#define LM78_SYSCTL_TEMP 1200	/* Degrees Celcius * 10 */
+#define LM78_SYSCTL_VID 1300	/* Volts * 100 */
+#define LM78_SYSCTL_FAN_DIV 2000	/* 1, 2, 4 or 8 */
+#define LM78_SYSCTL_ALARMS 2001	/* bitvector */
+
+#define LM78_ALARM_IN0 0x0001
+#define LM78_ALARM_IN1 0x0002
+#define LM78_ALARM_IN2 0x0004
+#define LM78_ALARM_IN3 0x0008
+#define LM78_ALARM_IN4 0x0100
+#define LM78_ALARM_IN5 0x0200
+#define LM78_ALARM_IN6 0x0400
+#define LM78_ALARM_FAN1 0x0040
+#define LM78_ALARM_FAN2 0x0080
+#define LM78_ALARM_FAN3 0x0800
+#define LM78_ALARM_TEMP 0x0010
+#define LM78_ALARM_BTI 0x0020
+#define LM78_ALARM_CHAS 0x1000
+#define LM78_ALARM_FIFO 0x2000
+#define LM78_ALARM_SMI_IN 0x4000
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected LM78. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized 
+   when a new copy is allocated. */
+static ctl_table lm78_dir_table_template[] = {
+	{LM78_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm78_in},
+	{LM78_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm78_in},
+	{LM78_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm78_in},
+	{LM78_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm78_in},
+	{LM78_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm78_in},
+	{LM78_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm78_in},
+	{LM78_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm78_in},
+	{LM78_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm78_fan},
+	{LM78_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm78_fan},
+	{LM78_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm78_fan},
+	{LM78_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm78_temp},
+	{LM78_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm78_vid},
+	{LM78_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm78_fan_div},
+	{LM78_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm78_alarms},
+	{0}
+};
+
+
+/* This function is called when:
+     * lm78_driver is inserted (when this module is loaded), for each
+       available adapter
+     * when a new adapter is inserted (and lm78_driver is still present) */
+static int lm78_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, lm78_detect);
+}
+
+/* This function is called by i2c_detect */
+int lm78_detect(struct i2c_adapter *adapter, int address,
+		unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct lm78_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+	int is_isa = i2c_is_isa_adapter(adapter);
+
+	if (!is_isa
+	    && !i2c_check_functionality(adapter,
+					I2C_FUNC_SMBUS_BYTE_DATA)) goto
+		    ERROR0;
+
+	if (is_isa) {
+		if (check_region(address, LM78_EXTENT))
+			goto ERROR0;
+	}
+
+	/* Probe whether there is anything available on this address. Already
+	   done for SMBus clients */
+	if (kind < 0) {
+		if (is_isa) {
+
+#define REALLY_SLOW_IO
+			/* We need the timeouts for at least some LM78-like chips. But only
+			   if we read 'undefined' registers. */
+			i = inb_p(address + 1);
+			if (inb_p(address + 2) != i)
+				goto ERROR0;
+			if (inb_p(address + 3) != i)
+				goto ERROR0;
+			if (inb_p(address + 7) != i)
+				goto ERROR0;
+#undef REALLY_SLOW_IO
+
+			/* Let's just hope nothing breaks here */
+			i = inb_p(address + 5) & 0x7f;
+			outb_p(~i & 0x7f, address + 5);
+			if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) {
+				outb_p(i, address + 5);
+				return 0;
+			}
+		}
+	}
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access lm78_{read,write}_value. */
+
+	if (!(data = kmalloc(sizeof(struct lm78_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	if (is_isa)
+		init_MUTEX(&data->lock);
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &lm78_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+
+	if (kind < 0) {
+		if (lm78_read_value(new_client, LM78_REG_CONFIG) & 0x80)
+			goto ERROR1;
+		if (!is_isa
+		    && (lm78_read_value(new_client, LM78_REG_I2C_ADDR) !=
+			address)) goto ERROR1;
+	}
+
+	/* Determine the chip type. */
+	if (kind <= 0) {
+		i = lm78_read_value(new_client, LM78_REG_CHIPID);
+		if (i == 0x00 || i == 0x20)
+			kind = lm78;
+		else if (i == 0x40)
+			kind = lm78j;
+		else if ((i & 0xfe) == 0xc0)
+			kind = lm79;
+		else {
+			if (kind == 0)
+				printk
+				    ("lm78.o: Ignoring 'force' parameter for unknown chip at "
+				     "adapter %d, address 0x%02x\n",
+				     i2c_adapter_id(adapter), address);
+			goto ERROR1;
+		}
+	}
+
+	if (kind == lm78) {
+		type_name = "lm78";
+		client_name = "LM78 chip";
+	} else if (kind == lm78j) {
+		type_name = "lm78-j";
+		client_name = "LM78-J chip";
+	} else if (kind == lm79) {
+		type_name = "lm79";
+		client_name = "LM79 chip";
+	} else {
+#ifdef DEBUG
+		printk("lm78.o: Internal error: unknown kind (%d)?!?",
+		       kind);
+#endif
+		goto ERROR1;
+	}
+
+	/* Reserve the ISA region */
+	if (is_isa)
+		request_region(address, LM78_EXTENT, type_name);
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	data->type = kind;
+
+	new_client->id = lm78_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client,
+					type_name,
+					lm78_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the LM78 chip */
+	lm78_init_client(new_client);
+	return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+	if (is_isa)
+		release_region(address, LM78_EXTENT);
+      ERROR1:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int lm78_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct lm78_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("lm78.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	if(i2c_is_isa_client(client))
+		release_region(client->addr, LM78_EXTENT);
+	kfree(client->data);
+
+	return 0;
+}
+
+/* The SMBus locks itself, but ISA access must be locked explicitely! 
+   We don't want to lock the whole ISA bus, so we lock each client
+   separately.
+   We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks,
+   would slow down the LM78 access and should not be necessary. 
+   There are some ugly typecasts here, but the good new is - they should
+   nowhere else be necessary! */
+static int lm78_read_value(struct i2c_client *client, u8 reg)
+{
+	int res;
+	if (i2c_is_isa_client(client)) {
+		down(&(((struct lm78_data *) (client->data))->lock));
+		outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET);
+		res = inb_p(client->addr + LM78_DATA_REG_OFFSET);
+		up(&(((struct lm78_data *) (client->data))->lock));
+		return res;
+	} else
+		return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* The SMBus locks itself, but ISA access muse be locked explicitely! 
+   We don't want to lock the whole ISA bus, so we lock each client
+   separately.
+   We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks,
+   would slow down the LM78 access and should not be necessary. 
+   There are some ugly typecasts here, but the good new is - they should
+   nowhere else be necessary! */
+static int lm78_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+	if (i2c_is_isa_client(client)) {
+		down(&(((struct lm78_data *) (client->data))->lock));
+		outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET);
+		outb_p(value, client->addr + LM78_DATA_REG_OFFSET);
+		up(&(((struct lm78_data *) (client->data))->lock));
+		return 0;
+	} else
+		return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* Called when we have found a new LM78. It should set limits, etc. */
+static void lm78_init_client(struct i2c_client *client)
+{
+	int vid;
+
+	/* Reset all except Watchdog values and last conversion values
+	   This sets fan-divs to 2, among others */
+	lm78_write_value(client, LM78_REG_CONFIG, 0x80);
+
+	vid = lm78_read_value(client, LM78_REG_VID_FANDIV) & 0x0f;
+	if (((struct lm78_data *) (client->data))->type == lm79)
+		vid |=
+		    (lm78_read_value(client, LM78_REG_CHIPID) & 0x01) << 4;
+	else
+		vid |= 0x10;
+	vid = VID_FROM_REG(vid);
+
+	/* Start monitoring */
+	lm78_write_value(client, LM78_REG_CONFIG,
+			 (lm78_read_value(client, LM78_REG_CONFIG) & 0xf7)
+			 | 0x01);
+
+}
+
+static void lm78_update_client(struct i2c_client *client)
+{
+	struct lm78_data *data = client->data;
+	int i;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting lm78 update\n");
+#endif
+		for (i = 0; i <= 6; i++) {
+			data->in[i] =
+			    lm78_read_value(client, LM78_REG_IN(i));
+			data->in_min[i] =
+			    lm78_read_value(client, LM78_REG_IN_MIN(i));
+			data->in_max[i] =
+			    lm78_read_value(client, LM78_REG_IN_MAX(i));
+		}
+		for (i = 1; i <= 3; i++) {
+			data->fan[i - 1] =
+			    lm78_read_value(client, LM78_REG_FAN(i));
+			data->fan_min[i - 1] =
+			    lm78_read_value(client, LM78_REG_FAN_MIN(i));
+		}
+		data->temp = lm78_read_value(client, LM78_REG_TEMP);
+		data->temp_over =
+		    lm78_read_value(client, LM78_REG_TEMP_OVER);
+		data->temp_hyst =
+		    lm78_read_value(client, LM78_REG_TEMP_HYST);
+		i = lm78_read_value(client, LM78_REG_VID_FANDIV);
+		data->vid = i & 0x0f;
+		if (data->type == lm79)
+			data->vid |=
+			    (lm78_read_value(client, LM78_REG_CHIPID) &
+			     0x01) << 4;
+		else
+			data->vid |= 0x10;
+		data->fan_div[0] = (i >> 4) & 0x03;
+		data->fan_div[1] = i >> 6;
+		data->alarms = lm78_read_value(client, LM78_REG_ALARM1) +
+		    (lm78_read_value(client, LM78_REG_ALARM2) << 8);
+		data->last_updated = jiffies;
+		data->valid = 1;
+
+		data->fan_div[2] = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+/* The next few functions are the call-back functions of the /proc/sys and
+   sysctl files. Which function is used is defined in the ctl_table in
+   the extra1 field.
+   Each function must return the magnitude (power of 10 to divide the date
+   with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must
+   put a maximum of *nrels elements in results reflecting the data of this
+   file, and set *nrels to the number it actually put in it, if operation==
+   SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from
+   results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE.
+   Note that on SENSORS_PROC_REAL_READ, I do not check whether results is
+   large enough (by checking the incoming value of *nrels). This is not very
+   good practice, but as long as you put less than about 5 values in results,
+   you can assume it is large enough. */
+void lm78_in(struct i2c_client *client, int operation, int ctl_name,
+	     int *nrels_mag, long *results)
+{
+	struct lm78_data *data = client->data;
+	int nr = ctl_name - LM78_SYSCTL_IN0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm78_update_client(client);
+		results[0] = IN_FROM_REG(data->in_min[nr]);
+		results[1] = IN_FROM_REG(data->in_max[nr]);
+		results[2] = IN_FROM_REG(data->in[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->in_min[nr] = IN_TO_REG(results[0]);
+			lm78_write_value(client, LM78_REG_IN_MIN(nr),
+					 data->in_min[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->in_max[nr] = IN_TO_REG(results[1]);
+			lm78_write_value(client, LM78_REG_IN_MAX(nr),
+					 data->in_max[nr]);
+		}
+	}
+}
+
+void lm78_fan(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct lm78_data *data = client->data;
+	int nr = ctl_name - LM78_SYSCTL_FAN1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm78_update_client(client);
+		results[0] = FAN_FROM_REG(data->fan_min[nr - 1],
+					  DIV_FROM_REG(data->
+						       fan_div[nr - 1]));
+		results[1] =
+		    FAN_FROM_REG(data->fan[nr - 1],
+				 DIV_FROM_REG(data->fan_div[nr - 1]));
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr - 1] = FAN_TO_REG(results[0],
+							   DIV_FROM_REG
+							   (data->
+							    fan_div[nr -
+								    1]));
+			lm78_write_value(client, LM78_REG_FAN_MIN(nr),
+					 data->fan_min[nr - 1]);
+		}
+	}
+}
+
+
+void lm78_temp(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct lm78_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm78_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_over);
+		results[1] = TEMP_FROM_REG(data->temp_hyst);
+		results[2] = TEMP_FROM_REG(data->temp);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_over = TEMP_TO_REG(results[0]);
+			lm78_write_value(client, LM78_REG_TEMP_OVER,
+					 data->temp_over);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_hyst = TEMP_TO_REG(results[1]);
+			lm78_write_value(client, LM78_REG_TEMP_HYST,
+					 data->temp_hyst);
+		}
+	}
+}
+
+void lm78_vid(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct lm78_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm78_update_client(client);
+		results[0] = VID_FROM_REG(data->vid);
+		*nrels_mag = 1;
+	}
+}
+
+void lm78_alarms(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct lm78_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm78_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->alarms);
+		*nrels_mag = 1;
+	}
+}
+
+/* Note: we save and restore the fan minimum here, because its value is
+   determined in part by the fan divisor.  This follows the principle of
+   least surprise: the user doesn't expect the fan minimum to change just
+   because the divisor changed. */
+void lm78_fan_div(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct lm78_data *data = client->data;
+	int old, min;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm78_update_client(client);
+		results[0] = DIV_FROM_REG(data->fan_div[0]);
+		results[1] = DIV_FROM_REG(data->fan_div[1]);
+		results[2] = 2;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		old = lm78_read_value(client, LM78_REG_VID_FANDIV);
+		if (*nrels_mag >= 2) {
+			min = FAN_FROM_REG(data->fan_min[1], 
+					DIV_FROM_REG(data->fan_div[1]));
+			data->fan_div[1] = DIV_TO_REG(results[1]);
+			old = (old & 0x3f) | (data->fan_div[1] << 6);
+			data->fan_min[1] = FAN_TO_REG(min,
+					DIV_FROM_REG(data->fan_div[1]));
+			lm78_write_value(client, LM78_REG_FAN_MIN(2),
+					data->fan_min[1]);
+		}
+		if (*nrels_mag >= 1) {
+			min = FAN_FROM_REG(data->fan_min[0],
+					DIV_FROM_REG(data->fan_div[0]));
+			data->fan_div[0] = DIV_TO_REG(results[0]);
+			old = (old & 0xcf) | (data->fan_div[0] << 4);
+			data->fan_min[0] = FAN_TO_REG(min,
+					DIV_FROM_REG(data->fan_div[0]));
+			lm78_write_value(client, LM78_REG_FAN_MIN(1),
+					data->fan_min[0]);
+			lm78_write_value(client, LM78_REG_VID_FANDIV, old);
+		}
+	}
+}
+
+static int __init sm_lm78_init(void)
+{
+	printk("lm78.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&lm78_driver);
+}
+
+static void __exit sm_lm78_exit(void)
+{
+	i2c_del_driver(&lm78_driver);
+}
+
+
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
+MODULE_DESCRIPTION("LM78, LM78-J and LM79 driver");
+
+module_init(sm_lm78_init);
+module_exit(sm_lm78_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/lm80.c linux-2.4.27-leo/drivers/sensors/lm80.c
--- linux-2.4.27/drivers/sensors/lm80.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/lm80.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,606 @@
+/*
+    lm80.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+    and Philip Edelbrock <phil@netroedge.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x20, 0x2f, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(lm80);
+
+/* Many LM80 constants specified below */
+
+/* The LM80 registers */
+#define LM80_REG_IN_MAX(nr) (0x2a + (nr) * 2)
+#define LM80_REG_IN_MIN(nr) (0x2b + (nr) * 2)
+#define LM80_REG_IN(nr) (0x20 + (nr))
+
+#define LM80_REG_FAN1_MIN 0x3c
+#define LM80_REG_FAN2_MIN 0x3d
+#define LM80_REG_FAN1 0x28
+#define LM80_REG_FAN2 0x29
+
+#define LM80_REG_TEMP 0x27
+#define LM80_REG_TEMP_HOT_MAX 0x38
+#define LM80_REG_TEMP_HOT_HYST 0x39
+#define LM80_REG_TEMP_OS_MAX 0x3a
+#define LM80_REG_TEMP_OS_HYST 0x3b
+
+#define LM80_REG_CONFIG 0x00
+#define LM80_REG_ALARM1 0x01
+#define LM80_REG_ALARM2 0x02
+#define LM80_REG_MASK1 0x03
+#define LM80_REG_MASK2 0x04
+#define LM80_REG_FANDIV 0x05
+#define LM80_REG_RES 0x06
+
+
+/* Conversions. Rounding and limit checking is only done on the TO_REG
+   variants. Note that you should be a bit careful with which arguments
+   these macros are called: arguments may be evaluated more than once.
+   Fixing this is just not worth it. */
+
+#define IN_TO_REG(val) (SENSORS_LIMIT((val),0,255))
+#define IN_FROM_REG(val) (val)
+
+static inline unsigned char FAN_TO_REG(unsigned rpm, unsigned div)
+{
+	if (rpm == 0)
+		return 255;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1,
+			     254);
+}
+
+#define FAN_FROM_REG(val,div) ((val)==0?-1:\
+                               (val)==255?0:1350000/((div)*(val)))
+
+static inline long TEMP_FROM_REG(u16 temp)
+{
+	long res;
+
+	temp >>= 4;
+	if (temp < 0x0800)
+		res = 625 * (long) temp;
+	else
+		res = ((long) temp - 0x01000) * 625;
+
+	return res / 100;
+}
+
+#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*100)
+
+#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT(((val)<0?(((val)-50)/100):\
+                                                      ((val)+50)/100), \
+                                             0,255)
+
+#define ALARMS_FROM_REG(val) (val)
+
+#define DIV_FROM_REG(val) (1 << (val))
+#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
+
+struct lm80_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 in[7];		/* Register value */
+	u8 in_max[7];		/* Register value */
+	u8 in_min[7];		/* Register value */
+	u8 fan[2];		/* Register value */
+	u8 fan_min[2];		/* Register value */
+	u8 fan_div[2];		/* Register encoding, shifted right */
+	u16 temp;		/* Register values, shifted right */
+	u8 temp_hot_max;	/* Register value */
+	u8 temp_hot_hyst;	/* Register value */
+	u8 temp_os_max;		/* Register value */
+	u8 temp_os_hyst;	/* Register value */
+	u16 alarms;		/* Register encoding, combined */
+};
+
+
+
+static int lm80_attach_adapter(struct i2c_adapter *adapter);
+static int lm80_detect(struct i2c_adapter *adapter, int address,
+		       unsigned short flags, int kind);
+static int lm80_detach_client(struct i2c_client *client);
+
+static int lm80_read_value(struct i2c_client *client, u8 reg);
+static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value);
+static void lm80_update_client(struct i2c_client *client);
+static void lm80_init_client(struct i2c_client *client);
+
+
+static void lm80_in(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results);
+static void lm80_fan(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results);
+static void lm80_temp(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void lm80_alarms(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm80_fan_div(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+
+static int lm80_id = 0;
+
+static struct i2c_driver lm80_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "LM80 sensor driver",
+	.id		= I2C_DRIVERID_LM80,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= lm80_attach_adapter,
+	.detach_client	= lm80_detach_client,
+};
+
+/* The /proc/sys entries */
+
+/* -- SENSORS SYSCTL START -- */
+
+#define LM80_SYSCTL_IN0 1000	/* Volts * 100 */
+#define LM80_SYSCTL_IN1 1001
+#define LM80_SYSCTL_IN2 1002
+#define LM80_SYSCTL_IN3 1003
+#define LM80_SYSCTL_IN4 1004
+#define LM80_SYSCTL_IN5 1005
+#define LM80_SYSCTL_IN6 1006
+#define LM80_SYSCTL_FAN1 1101	/* Rotations/min */
+#define LM80_SYSCTL_FAN2 1102
+#define LM80_SYSCTL_TEMP 1250	/* Degrees Celcius * 100 */
+#define LM80_SYSCTL_FAN_DIV 2000	/* 1, 2, 4 or 8 */
+#define LM80_SYSCTL_ALARMS 2001	/* bitvector */
+
+#define LM80_ALARM_IN0 0x0001
+#define LM80_ALARM_IN1 0x0002
+#define LM80_ALARM_IN2 0x0004
+#define LM80_ALARM_IN3 0x0008
+#define LM80_ALARM_IN4 0x0010
+#define LM80_ALARM_IN5 0x0020
+#define LM80_ALARM_IN6 0x0040
+#define LM80_ALARM_FAN1 0x0400
+#define LM80_ALARM_FAN2 0x0800
+#define LM80_ALARM_TEMP_HOT 0x0100
+#define LM80_ALARM_TEMP_OS 0x2000
+#define LM80_ALARM_CHAS 0x1000
+#define LM80_ALARM_BTI 0x0200
+#define LM80_ALARM_INT_IN 0x0080
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected LM80. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized 
+   when a new copy is allocated. */
+static ctl_table lm80_dir_table_template[] = {
+	{LM80_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm80_in},
+	{LM80_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm80_in},
+	{LM80_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm80_in},
+	{LM80_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm80_in},
+	{LM80_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm80_in},
+	{LM80_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm80_in},
+	{LM80_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm80_in},
+	{LM80_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm80_fan},
+	{LM80_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm80_fan},
+	{LM80_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm80_temp},
+	{LM80_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm80_fan_div},
+	{LM80_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm80_alarms},
+	{0}
+};
+
+static int lm80_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, lm80_detect);
+}
+
+int lm80_detect(struct i2c_adapter *adapter, int address,
+		unsigned short flags, int kind)
+{
+	int i, cur;
+	struct i2c_client *new_client;
+	struct lm80_data *data;
+	int err = 0;
+	const char *type_name, *client_name;
+
+	/* Make sure we aren't probing the ISA bus!! This is just a safety check
+	   at this moment; i2c_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("lm80.o: lm80_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		goto ERROR0;
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access lm80_{read,write}_value. */
+	if (!(data = kmalloc(sizeof(struct lm80_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &lm80_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. It is lousy. */
+	if (lm80_read_value(new_client, LM80_REG_ALARM2) & 0xc0)
+		goto ERROR1;
+	for (i = 0x2a; i <= 0x3d; i++) {
+		cur = i2c_smbus_read_byte_data(new_client, i);
+		if ((i2c_smbus_read_byte_data(new_client, i + 0x40) != cur)
+		    || (i2c_smbus_read_byte_data(new_client, i + 0x80) !=
+			cur)
+		    || (i2c_smbus_read_byte_data(new_client, i + 0xc0) !=
+			cur)) goto ERROR1;
+	}
+
+	/* Determine the chip type - only one kind supported! */
+	if (kind <= 0)
+		kind = lm80;
+
+	if (kind == lm80) {
+		type_name = "lm80";
+		client_name = "LM80 chip";
+	} else {
+#ifdef DEBUG
+		printk("lm80.o: Internal error: unknown kind (%d)?!?",
+		       kind);
+#endif
+		goto ERROR1;
+	}
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+
+	new_client->id = lm80_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client, type_name,
+					lm80_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	lm80_init_client(new_client);
+	return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+      ERROR1:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int lm80_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct lm80_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("lm80.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+static int lm80_read_value(struct i2c_client *client, u8 reg)
+{
+	return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* Called when we have found a new LM80. */
+static void lm80_init_client(struct i2c_client *client)
+{
+	/* Reset all except Watchdog values and last conversion values
+	   This sets fan-divs to 2, among others. This makes most other
+	   initializations unnecessary */
+	lm80_write_value(client, LM80_REG_CONFIG, 0x80);
+	/* Set 11-bit temperature resolution */
+	lm80_write_value(client, LM80_REG_RES, 0x08);
+
+	/* Start monitoring */
+	lm80_write_value(client, LM80_REG_CONFIG, 0x01);
+}
+
+static void lm80_update_client(struct i2c_client *client)
+{
+	struct lm80_data *data = client->data;
+	int i;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > 2 * HZ) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting lm80 update\n");
+#endif
+		for (i = 0; i <= 6; i++) {
+			data->in[i] =
+			    lm80_read_value(client, LM80_REG_IN(i));
+			data->in_min[i] =
+			    lm80_read_value(client, LM80_REG_IN_MIN(i));
+			data->in_max[i] =
+			    lm80_read_value(client, LM80_REG_IN_MAX(i));
+		}
+		data->fan[0] = lm80_read_value(client, LM80_REG_FAN1);
+		data->fan_min[0] =
+		    lm80_read_value(client, LM80_REG_FAN1_MIN);
+		data->fan[1] = lm80_read_value(client, LM80_REG_FAN2);
+		data->fan_min[1] =
+		    lm80_read_value(client, LM80_REG_FAN2_MIN);
+
+		data->temp =
+		    (lm80_read_value(client, LM80_REG_TEMP) << 8) |
+		    (lm80_read_value(client, LM80_REG_RES) & 0xf0);
+		data->temp_os_max =
+		    lm80_read_value(client, LM80_REG_TEMP_OS_MAX);
+		data->temp_os_hyst =
+		    lm80_read_value(client, LM80_REG_TEMP_OS_HYST);
+		data->temp_hot_max =
+		    lm80_read_value(client, LM80_REG_TEMP_HOT_MAX);
+		data->temp_hot_hyst =
+		    lm80_read_value(client, LM80_REG_TEMP_HOT_HYST);
+
+		i = lm80_read_value(client, LM80_REG_FANDIV);
+		data->fan_div[0] = (i >> 2) & 0x03;
+		data->fan_div[1] = (i >> 4) & 0x03;
+		data->alarms = lm80_read_value(client, LM80_REG_ALARM1) +
+		    (lm80_read_value(client, LM80_REG_ALARM2) << 8);
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+/* The next few functions are the call-back functions of the /proc/sys and
+   sysctl files. Which function is used is defined in the ctl_table in
+   the extra1 field.
+   Each function must return the magnitude (power of 10 to divide the date
+   with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must
+   put a maximum of *nrels elements in results reflecting the data of this
+   file, and set *nrels to the number it actually put in it, if operation==
+   SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from
+   results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE.
+   Note that on SENSORS_PROC_REAL_READ, I do not check whether results is
+   large enough (by checking the incoming value of *nrels). This is not very
+   good practice, but as long as you put less than about 5 values in results,
+   you can assume it is large enough. */
+void lm80_in(struct i2c_client *client, int operation, int ctl_name,
+	     int *nrels_mag, long *results)
+{
+	struct lm80_data *data = client->data;
+	int nr = ctl_name - LM80_SYSCTL_IN0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm80_update_client(client);
+		results[0] = IN_FROM_REG(data->in_min[nr]);
+		results[1] = IN_FROM_REG(data->in_max[nr]);
+		results[2] = IN_FROM_REG(data->in[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->in_min[nr] = IN_TO_REG(results[0]);
+			lm80_write_value(client, LM80_REG_IN_MIN(nr),
+					 data->in_min[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->in_max[nr] = IN_TO_REG(results[1]);
+			lm80_write_value(client, LM80_REG_IN_MAX(nr),
+					 data->in_max[nr]);
+		}
+	}
+}
+
+void lm80_fan(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct lm80_data *data = client->data;
+	int nr = ctl_name - LM80_SYSCTL_FAN1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm80_update_client(client);
+		results[0] = FAN_FROM_REG(data->fan_min[nr - 1],
+					  DIV_FROM_REG(data->
+						       fan_div[nr - 1]));
+		results[1] =
+		    FAN_FROM_REG(data->fan[nr - 1],
+				 DIV_FROM_REG(data->fan_div[nr - 1]));
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr - 1] = FAN_TO_REG(results[0],
+							   DIV_FROM_REG
+							   (data->
+							    fan_div[nr -
+								    1]));
+			lm80_write_value(client,
+					 nr ==
+					 1 ? LM80_REG_FAN1_MIN :
+					 LM80_REG_FAN2_MIN,
+					 data->fan_min[nr - 1]);
+		}
+	}
+}
+
+
+void lm80_temp(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct lm80_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm80_update_client(client);
+		results[0] = TEMP_LIMIT_FROM_REG(data->temp_hot_max);
+		results[1] = TEMP_LIMIT_FROM_REG(data->temp_hot_hyst);
+		results[2] = TEMP_LIMIT_FROM_REG(data->temp_os_max);
+		results[3] = TEMP_LIMIT_FROM_REG(data->temp_os_hyst);
+		results[4] = TEMP_FROM_REG(data->temp);
+		*nrels_mag = 5;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_hot_max = TEMP_LIMIT_TO_REG(results[0]);
+			lm80_write_value(client, LM80_REG_TEMP_HOT_MAX,
+					 data->temp_hot_max);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_hot_hyst =
+			    TEMP_LIMIT_TO_REG(results[1]);
+			lm80_write_value(client, LM80_REG_TEMP_HOT_HYST,
+					 data->temp_hot_hyst);
+		}
+		if (*nrels_mag >= 3) {
+			data->temp_os_max = TEMP_LIMIT_TO_REG(results[2]);
+			lm80_write_value(client, LM80_REG_TEMP_OS_MAX,
+					 data->temp_os_max);
+		}
+		if (*nrels_mag >= 4) {
+			data->temp_os_hyst = TEMP_LIMIT_TO_REG(results[3]);
+			lm80_write_value(client, LM80_REG_TEMP_OS_HYST,
+					 data->temp_os_hyst);
+		}
+	}
+}
+
+void lm80_alarms(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct lm80_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm80_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->alarms);
+		*nrels_mag = 1;
+	}
+}
+
+void lm80_fan_div(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct lm80_data *data = client->data;
+	int old;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm80_update_client(client);
+		results[0] = DIV_FROM_REG(data->fan_div[0]);
+		results[1] = DIV_FROM_REG(data->fan_div[1]);
+		results[2] = 2;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		old = lm80_read_value(client, LM80_REG_FANDIV);
+		if (*nrels_mag >= 2) {
+			data->fan_div[1] = DIV_TO_REG(results[1]);
+			old = (old & 0xcf) | (data->fan_div[1] << 4);
+		}
+		if (*nrels_mag >= 1) {
+			data->fan_div[0] = DIV_TO_REG(results[0]);
+			old = (old & 0xf3) | (data->fan_div[0] << 2);
+			lm80_write_value(client, LM80_REG_FANDIV, old);
+		}
+	}
+}
+
+static int __init sm_lm80_init(void)
+{
+ 	printk("lm80.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&lm80_driver);
+}
+
+static void __exit sm_lm80_exit(void)
+{
+	i2c_del_driver(&lm80_driver);
+}
+
+
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("LM80 driver");
+
+module_init(sm_lm80_init);
+module_exit(sm_lm80_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/lm83.c linux-2.4.27-leo/drivers/sensors/lm83.c
--- linux-2.4.27/drivers/sensors/lm83.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/lm83.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,506 @@
+/*
+ * lm83.c - Part of lm_sensors, Linux kernel modules for hardware
+ *          monitoring
+ * Copyright (C) 2003  Jean Delvare <khali@linux-fr.org>
+ *
+ * Heavily inspired from the lm78, lm75 and adm1021 drivers. The LM83 is
+ * a sensor chip made by National Semiconductor. It reports up to four
+ * temperatures (its own plus up to three external ones) with a 1 deg
+ * resolution and a 3-4 deg accuracy. Complete datasheet can be obtained
+ * from National's website at:
+ *   http://www.national.com/pf/LM/LM83.html
+ * Since the datasheet omits to give the chip stepping code, I give it
+ * here: 0x03 (at register 0xff).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+/*
+ * Addresses to scan
+ * Address is selected using 2 three-level pins, resulting in 9 possible
+ * addresses.
+ */
+
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x18, 0x1a, 0x29, 0x2b,
+	0x4c, 0x4e, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/*
+ * Insmod parameters
+ */
+
+SENSORS_INSMOD_1(lm83);
+
+/*
+ * The LM83 registers
+ * Manufacturer ID is 0x01 for National Semiconductor.
+ */
+
+#define LM83_REG_R_MAN_ID        0xFE
+#define LM83_REG_R_CHIP_ID       0xFF
+#define LM83_REG_R_CONFIG        0x03
+#define LM83_REG_W_CONFIG        0x09
+#define LM83_REG_R_STATUS1       0x02
+#define LM83_REG_R_STATUS2       0x35
+#define LM83_REG_R_LOCAL_TEMP    0x00
+#define LM83_REG_R_LOCAL_HIGH    0x05
+#define LM83_REG_W_LOCAL_HIGH    0x0B
+#define LM83_REG_R_REMOTE1_TEMP  0x30
+#define LM83_REG_R_REMOTE1_HIGH  0x38
+#define LM83_REG_W_REMOTE1_HIGH  0x50
+#define LM83_REG_R_REMOTE2_TEMP  0x01
+#define LM83_REG_R_REMOTE2_HIGH  0x07
+#define LM83_REG_W_REMOTE2_HIGH  0x0D
+#define LM83_REG_R_REMOTE3_TEMP  0x31
+#define LM83_REG_R_REMOTE3_HIGH  0x3A
+#define LM83_REG_W_REMOTE3_HIGH  0x52
+#define LM83_REG_R_TCRIT         0x42
+#define LM83_REG_W_TCRIT         0x5A
+
+/*
+ * Conversions and various macros
+ * The LM83 uses signed 8-bit values.
+ */
+
+#define TEMP_FROM_REG(val)  (val > 127 ? val-256 : val)
+#define TEMP_TO_REG(val)    (val < 0 ? val+256 : val)
+
+static const u8 LM83_REG_R_TEMP[] = {
+	LM83_REG_R_LOCAL_TEMP,
+	LM83_REG_R_REMOTE1_TEMP,
+	LM83_REG_R_REMOTE2_TEMP,
+	LM83_REG_R_REMOTE3_TEMP
+};
+
+static const u8 LM83_REG_R_HIGH[] = {
+	LM83_REG_R_LOCAL_HIGH,
+	LM83_REG_R_REMOTE1_HIGH,
+	LM83_REG_R_REMOTE2_HIGH,
+	LM83_REG_R_REMOTE3_HIGH
+};
+
+static const u8 LM83_REG_W_HIGH[] = {
+	LM83_REG_W_LOCAL_HIGH,
+	LM83_REG_W_REMOTE1_HIGH,
+	LM83_REG_W_REMOTE2_HIGH,
+	LM83_REG_W_REMOTE3_HIGH
+};
+
+/*
+ * Functions declaration
+ */
+
+static int lm83_attach_adapter(struct i2c_adapter *adapter);
+static int lm83_detect(struct i2c_adapter *adapter, int address, unsigned
+	short flags, int kind);
+static int lm83_detach_client(struct i2c_client *client);
+static void lm83_update_client(struct i2c_client *client);
+static void lm83_temp(struct i2c_client *client, int operation, int
+	ctl_name, int *nrels_mag, long *results);
+static void lm83_tcrit(struct i2c_client *client, int operation, int
+	ctl_name, int *nrels_mag, long *results);
+static void lm83_alarms(struct i2c_client *client, int operation, int
+	ctl_name, int *nrels_mag, long *results);
+
+/*
+ * Driver data (common to all clients)
+ */
+ 
+static struct i2c_driver lm83_driver = {
+	.owner          = THIS_MODULE,
+	.name           = "LM83 sensor driver",
+	.id             = I2C_DRIVERID_LM83,
+	.flags          = I2C_DF_NOTIFY,
+	.attach_adapter = lm83_attach_adapter,
+	.detach_client  = lm83_detach_client,
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct lm83_data
+{
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid; /* zero until following fields are valid */
+	unsigned long last_updated; /* in jiffies */
+
+	/* registers values */
+	u8 temp[4], temp_high[4], tcrit;
+	u16 alarms; /* bitvector, combined */
+};
+
+/*
+ * Proc entries
+ * These files are created for each detected LM83.
+ */
+
+/* -- SENSORS SYSCTL START -- */
+
+#define LM83_SYSCTL_LOCAL_TEMP    1200
+#define LM83_SYSCTL_REMOTE1_TEMP  1201
+#define LM83_SYSCTL_REMOTE2_TEMP  1202
+#define LM83_SYSCTL_REMOTE3_TEMP  1203
+#define LM83_SYSCTL_TCRIT         1208
+#define LM83_SYSCTL_ALARMS        1210
+
+#define LM83_ALARM_LOCAL_HIGH     0x0040
+#define LM83_ALARM_LOCAL_CRIT     0x0001
+#define LM83_ALARM_REMOTE1_HIGH   0x8000
+#define LM83_ALARM_REMOTE1_CRIT   0x0100
+#define LM83_ALARM_REMOTE1_OPEN   0x2000
+#define LM83_ALARM_REMOTE2_HIGH   0x0010
+#define LM83_ALARM_REMOTE2_CRIT   0x0002
+#define LM83_ALARM_REMOTE2_OPEN   0x0004
+#define LM83_ALARM_REMOTE3_HIGH   0x1000
+#define LM83_ALARM_REMOTE3_CRIT   0x0200
+#define LM83_ALARM_REMOTE3_OPEN   0x0400
+
+/* -- SENSORS SYSCTL END -- */
+
+
+static ctl_table lm83_dir_table_template[] =
+{
+	{LM83_SYSCTL_LOCAL_TEMP, "temp1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm83_temp},
+	{LM83_SYSCTL_REMOTE1_TEMP, "temp2", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm83_temp},
+	{LM83_SYSCTL_REMOTE2_TEMP, "temp3", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm83_temp},
+	{LM83_SYSCTL_REMOTE3_TEMP, "temp4", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm83_temp},
+	{LM83_SYSCTL_TCRIT, "tcrit", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm83_tcrit},
+	{LM83_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm83_alarms},
+	{0}
+};
+
+/*
+ * Internal variables
+ */
+
+static int lm83_id = 0;
+
+/*
+ * Real code
+ */
+
+static int lm83_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, lm83_detect);
+}
+
+/*
+ * The following function does more than just detection. If detection
+ * succeeds, it also registers the new chip.
+ */
+static int lm83_detect(struct i2c_adapter *adapter, int address, unsigned
+	short flags, int kind)
+{
+	struct i2c_client *new_client;
+	struct lm83_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter))
+	{
+		printk("lm83.o: Called for an ISA bus adapter, aborting.\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+	{
+#ifdef DEBUG
+		printk("lm83.o: I2C bus doesn't support byte read mode, "
+		       "skipping.\n");
+#endif
+		return 0;
+	}
+
+	if (!(data = kmalloc(sizeof(struct lm83_data), GFP_KERNEL)))
+	{
+		printk("lm83.o: Out of memory in lm83_detect (new_client).\n");
+		return -ENOMEM;
+	}
+
+	/*
+	 * The common I2C client data is placed right before the
+	 * LM83-specific data. The LM83-specific data is pointed to by the
+	 * data field from the I2C client data.
+	 */
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &lm83_driver;
+	new_client->flags = 0;
+
+	/*
+	 * Now we do the remaining detection. A negative kind means that
+	 * the driver was loaded with no force parameter (default), so we
+	 * must both detect and identify the chip (actually there is only
+	 * one possible kind of chip for now, LM83). A zero kind means that
+	 * the driver was loaded with the force parameter, the detection
+	 * step shall be skipped. A positive kind means that the driver
+	 * was loaded with the force parameter and a given kind of chip is
+	 * requested, so both the detection and the identification steps
+	 * are skipped.
+	 */
+
+	if (kind < 0) /* detection */
+	{
+		if (((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS1)
+		      & 0xA8) != 0x00)
+		||  ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS2)
+		      & 0x48) != 0x00)
+		||  ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_CONFIG)
+		      & 0x41) != 0x00))
+		{
+#ifdef DEBUG
+			printk(KERN_DEBUG "lm83.o: Detection failed at 0x%02x.\n",
+				address);
+#endif
+			goto ERROR1;
+		}
+	}
+
+	if (kind <= 0) /* identification */
+	{
+		u8 man_id, chip_id;
+
+		man_id = i2c_smbus_read_byte_data(new_client, LM83_REG_R_MAN_ID);
+		chip_id = i2c_smbus_read_byte_data(new_client, LM83_REG_R_CHIP_ID);
+		if (man_id == 0x01) /* National Semiconductor */
+		{
+			if (chip_id == 0x03)
+				kind = lm83;
+		}
+	}
+
+	if (kind <= 0) /* identification failed */
+	{
+		printk("lm83.o: Unsupported chip.\n");
+		goto ERROR1;
+	}
+
+	if (kind == lm83)
+	{
+		type_name = "lm83";
+		client_name = "LM83 chip";
+	}
+	else
+	{
+		printk("lm83.o: Unknown kind %d.\n", kind);
+		goto ERROR1;
+	}
+	
+	/*
+	 * OK, we got a valid chip so we can fill in the remaining client
+	 * fields.
+	 */
+
+	strcpy(new_client->name, client_name);
+	new_client->id = lm83_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/*
+	 * Tell the I2C layer a new client has arrived.
+	 */
+
+	if ((err = i2c_attach_client(new_client)))
+	{
+#ifdef DEBUG
+		printk("lm83.o: Failed attaching client.\n");
+#endif
+		goto ERROR1;
+	}
+
+	/*
+	 * Register a new directory entry.
+	 */
+
+	if ((err = i2c_register_entry(new_client, type_name,
+	     lm83_dir_table_template)) < 0)
+	{
+#ifdef DEBUG
+		printk("lm83.o: Failed registering directory entry.\n");
+#endif
+		goto ERROR2;
+	}
+	data->sysctl_id = err;
+
+	/*
+	 * Initialize the LM83 chip
+	 * (Nothing to do for this one.)
+	 */
+
+	return 0;
+
+	ERROR2:
+	i2c_detach_client(new_client);
+	ERROR1:
+	kfree(data);
+	return err;
+}
+
+static int lm83_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct lm83_data *) (client->data))->sysctl_id);
+	if ((err = i2c_detach_client(client)))
+	{
+		printk("lm83.o: Client deregistration failed, client not "
+		       "detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+	return 0;
+}
+
+static void lm83_update_client(struct i2c_client *client)
+{
+	struct lm83_data *data = client->data;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ * 2) ||
+	    (jiffies < data->last_updated) || !data->valid)
+	{
+		int nr;
+#ifdef DEBUG
+		printk("lm83.o: Updating LM83 data.\n");
+#endif
+		for (nr = 0; nr < 4 ; nr++)
+		{
+			data->temp[nr] =
+				i2c_smbus_read_byte_data(client, LM83_REG_R_TEMP[nr]);
+			data->temp_high[nr] =
+				i2c_smbus_read_byte_data(client, LM83_REG_R_HIGH[nr]);
+		}
+		data->tcrit = i2c_smbus_read_byte_data(client, LM83_REG_R_TCRIT);
+		data->alarms =
+			i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS1) +
+			(i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS2) << 8);
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+static void lm83_temp(struct i2c_client *client, int operation, int
+	ctl_name, int *nrels_mag, long *results)
+{
+	struct lm83_data *data = client->data;
+	int nr = ctl_name - LM83_SYSCTL_LOCAL_TEMP;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0; /* magnitude */
+	else if (operation == SENSORS_PROC_REAL_READ)
+	{
+		lm83_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_high[nr]);
+		results[1] = TEMP_FROM_REG(data->temp[nr]);
+		*nrels_mag = 2;
+	}
+	else if (operation == SENSORS_PROC_REAL_WRITE)
+	{
+		if (*nrels_mag >= 1)
+		{
+			data->temp_high[nr] = TEMP_TO_REG(results[0]);
+			i2c_smbus_write_byte_data(client, LM83_REG_W_HIGH[nr],
+					    data->temp_high[nr]);
+		}
+	}
+}
+
+static void lm83_tcrit(struct i2c_client *client, int operation, int
+	ctl_name, int *nrels_mag, long *results)
+{
+	struct lm83_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0; /* magnitude */
+	else if (operation == SENSORS_PROC_REAL_READ)
+	{
+		lm83_update_client(client);
+		results[0] = TEMP_FROM_REG(data->tcrit);
+		*nrels_mag = 1;
+	}
+	else if (operation == SENSORS_PROC_REAL_WRITE)
+	{
+		if (*nrels_mag >= 1)
+		{
+			data->tcrit = TEMP_TO_REG(results[0]);
+			i2c_smbus_write_byte_data(client, LM83_REG_W_TCRIT,
+				data->tcrit);
+		}
+	}
+}
+
+static void lm83_alarms(struct i2c_client *client, int operation, int
+	ctl_name, int *nrels_mag, long *results)
+{
+	struct lm83_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0; /* magnitude */
+	else if (operation == SENSORS_PROC_REAL_READ)
+	{
+		lm83_update_client(client);
+		results[0] = data->alarms;
+		*nrels_mag = 1;
+	}
+}
+
+static int __init sm_lm83_init(void)
+{
+	printk(KERN_INFO "lm83.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&lm83_driver);
+}
+
+static void __exit sm_lm83_exit(void)
+{
+	i2c_del_driver(&lm83_driver);
+}
+
+MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
+MODULE_DESCRIPTION("LM83 sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(sm_lm83_init);
+module_exit(sm_lm83_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/lm85.c linux-2.4.27-leo/drivers/sensors/lm85.c
--- linux-2.4.27/drivers/sensors/lm85.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/lm85.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,2043 @@
+/*
+    lm85.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl> 
+    Copyright (c) 2002, 2003  Philip Pokorny <ppokorny@penguincomputing.com>
+    Copyright (c) 2003        Margit Schubert-While <margitsw@t-online.de>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+    CHANGELOG
+
+    2002-11-13   First patch for LM85 functionality
+    2002-11-18   LM85 functionality mostly done
+    2002-12-02   Adding ADM1027 functionality
+    2002-12-06   Adding ADT7463 functionality
+    2003-01-09   Code cleanup.
+                 Save reserved bits in case they are implemented
+                    in a future chip.  (Solve problem with lockups
+                    on ADM1027 due to chip initialization)
+                 Added chip initialization bypass option
+    2003-02-12   Add THERM asserted counts for ADT7463
+                 Added #ifdef so we can compile against 2.6.5
+                    without updating i2c-ids.h
+    2003-02-17   Prepare for switch to 2.7.0 development
+                 Implement tmin_control for ADT7463
+                 Expose THERM asserted counts to /proc
+                 Code cleanup
+    2003-02-19   Working with Margit and LM_SENSORS developers
+    2003-02-23   Removed chip initialization entirely
+                 Scale voltages in driver at Margit's request
+                 Change PWM from 0-100% to 0-255 per LM sensors standard
+    2003-02-27   Documentation and code cleanups
+                 Added this CHANGELOG
+                 Print additional precision for temperatures and voltages
+                 Many thanks to Margit Schubert-While and Brandt xxxxxx
+                    for help testing this version
+    2003-02-28   More diagnostic messages regarding BIOS setup
+    2003-03-01   Added Interrupt mask register support.
+    2003-03-08   Fixed problem with pseudo 16-bit registers
+                 Cleaned up some compiler warnings.
+                 Fixed problem with Operating Point and THERM counting
+    2003-03-21   Initial support for EMC6D100 and EMC6D101 chips
+    2003-06-30   Add support for EMC6D100 extra voltage inputs.
+*/
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+#include <linux/sensors_vid.h>
+
+#ifndef I2C_DRIVERID_LM85
+#define I2C_DRIVERID_LM85  1039
+#endif
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_5(lm85b, lm85c, adm1027, adt7463, emc6d100);
+
+/* Many LM85 constants specified below */
+
+/* The LM85 registers */
+#define LM85_REG_IN(nr) (0x20 + (nr))
+#define LM85_REG_IN_MIN(nr) (0x44 + (nr) * 2)
+#define LM85_REG_IN_MAX(nr) (0x45 + (nr) * 2)
+
+#define LM85_REG_TEMP(nr) (0x25 + (nr))
+#define LM85_REG_TEMP_MIN(nr) (0x4e + (nr) * 2)
+#define LM85_REG_TEMP_MAX(nr) (0x4f + (nr) * 2)
+
+/* Fan speeds are LSB, MSB (2 bytes) */
+#define LM85_REG_FAN(nr) (0x28 + (nr) *2)
+#define LM85_REG_FAN_MIN(nr) (0x54 + (nr) *2)
+
+#define LM85_REG_PWM(nr) (0x30 + (nr))
+
+#define ADT7463_REG_OPPOINT(nr) (0x33 + (nr))
+
+#define ADT7463_REG_TMIN_CTL1 0x36
+#define ADT7463_REG_TMIN_CTL2 0x37
+#define ADT7463_REG_TMIN_CTL  0x0136
+
+#define LM85_REG_DEVICE 0x3d
+#define LM85_REG_COMPANY 0x3e
+#define LM85_REG_VERSTEP 0x3f
+/* These are the recognized values for the above regs */
+#define LM85_DEVICE_ADX 0x27
+#define LM85_COMPANY_NATIONAL 0x01
+#define LM85_COMPANY_ANALOG_DEV 0x41
+#define LM85_COMPANY_SMSC 0x5c
+#define LM85_VERSTEP_VMASK 0xf0
+#define LM85_VERSTEP_SMASK 0x0f
+#define LM85_VERSTEP_GENERIC 0x60
+#define LM85_VERSTEP_LM85C 0x60
+#define LM85_VERSTEP_LM85B 0x62
+#define LM85_VERSTEP_ADM1027 0x60
+#define LM85_VERSTEP_ADT7463 0x62
+#define LM85_VERSTEP_EMC6D100_A0 0x60
+#define LM85_VERSTEP_EMC6D100_A1 0x61
+
+#define LM85_REG_CONFIG 0x40
+
+#define LM85_REG_ALARM1 0x41
+#define LM85_REG_ALARM2 0x42
+#define LM85_REG_ALARM  0x0141
+
+#define LM85_REG_VID 0x43
+
+/* Automated FAN control */
+#define LM85_REG_AFAN_CONFIG(nr) (0x5c + (nr))
+#define LM85_REG_AFAN_RANGE(nr) (0x5f + (nr))
+#define LM85_REG_AFAN_SPIKE1 0x62
+#define LM85_REG_AFAN_SPIKE2 0x63
+#define LM85_REG_AFAN_MINPWM(nr) (0x64 + (nr))
+#define LM85_REG_AFAN_LIMIT(nr) (0x67 + (nr))
+#define LM85_REG_AFAN_CRITICAL(nr) (0x6a + (nr))
+#define LM85_REG_AFAN_HYST1 0x6d
+#define LM85_REG_AFAN_HYST2 0x6e
+
+#define LM85_REG_TACH_MODE 0x74
+#define LM85_REG_SPINUP_CTL 0x75
+
+#define ADM1027_REG_TEMP_OFFSET(nr) (0x70 + (nr))
+#define ADM1027_REG_CONFIG2 0x73
+#define ADM1027_REG_INTMASK1 0x74
+#define ADM1027_REG_INTMASK2 0x75
+#define ADM1027_REG_INTMASK  0x0174
+#define ADM1027_REG_EXTEND_ADC1 0x76
+#define ADM1027_REG_EXTEND_ADC2 0x77
+#define ADM1027_REG_EXTEND_ADC  0x0176
+#define ADM1027_REG_CONFIG3 0x78
+#define ADM1027_REG_FAN_PPR 0x7b
+
+#define ADT7463_REG_THERM 0x79
+#define ADT7463_REG_THERM_LIMIT 0x7A
+#define ADT7463_REG_CONFIG4 0x7D
+
+#define EMC6D100_REG_SFR  0x7c
+#define EMC6D100_REG_ALARM3  0x7d
+#define EMC6D100_REG_CONF  0x7f
+#define EMC6D100_REG_INT_EN  0x80
+/* IN5, IN6 and IN7 */
+#define EMC6D100_REG_IN(nr)  (0x70 + ((nr)-5))
+#define EMC6D100_REG_IN_MIN(nr) (0x73 + ((nr)-5) * 2)
+#define EMC6D100_REG_IN_MAX(nr) (0x74 + ((nr)-5) * 2)
+
+/* Conversions. Rounding and limit checking is only done on the TO_REG 
+   variants. Note that you should be a bit careful with which arguments
+   these macros are called: arguments may be evaluated more than once.
+ */
+
+/* IN are scaled 1.000 == 0xc0, mag = 3 */
+#define IN_TO_REG(val)  (SENSORS_LIMIT((((val)*0xc0+500)/1000),0,255))
+#define INEXT_FROM_REG(val,ext) (((val)*1000 + (ext)*250 + 96)/0xc0)
+#define IN_FROM_REG(val) (INEXT_FROM_REG(val,0))
+
+/* IN are scaled acording to built-in resistors */
+static int lm85_scaling[] = {  /* .001 Volts */
+		2500, 2250, 3300, 5000, 12000,
+		3300, 1500, 1800,	/* EMC6D100 */
+	};
+#define SCALE(val,from,to) (((val)*(to) + ((from)/2))/(from))
+#define INS_TO_REG(n,val)  (SENSORS_LIMIT(SCALE(val,lm85_scaling[n],192),0,255))
+#define INSEXT_FROM_REG(n,val,ext) (SCALE((val)*4 + (ext),192*4,lm85_scaling[n]))
+#define INS_FROM_REG(n,val) (INSEXT_FROM_REG(n,val,0))
+
+/* FAN speed is measured using 90kHz clock */
+#define FAN_TO_REG(val)  (SENSORS_LIMIT( (val)<=0?0: 5400000/(val),0,65534))
+#define FAN_FROM_REG(val) ((val)==0?-1:(val)==0xffff?0:5400000/(val))
+
+/* Temperature is reported in .01 degC increments */
+#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)+50)/100,-127,127))
+#define TEMPEXT_FROM_REG(val,ext) ((val)*100 + (ext)*25)
+#define TEMP_FROM_REG(val) (TEMPEXT_FROM_REG(val,0))
+#define EXTTEMP_TO_REG(val) (SENSORS_LIMIT((val)/25,-127,127))
+#define OPPOINT_TO_REG(val) (SENSORS_LIMIT(val,-127,127))
+#define OPPOINT_FROM_REG(val) (val)
+
+#define PWM_TO_REG(val) (SENSORS_LIMIT(val,0,255))
+#define PWM_FROM_REG(val) (val)
+
+#define EXT_FROM_REG(val,sensor) (((val)>>(sensor * 2))&0x03)
+
+/* ZONEs have the following parameters:
+ *    Limit (low) temp,           1. degC
+ *    Hysteresis (below limit),   1. degC (0-15)
+ *    Range of speed control,     .1 degC (2-80)
+ *    Critical (high) temp,       1. degC
+ *
+ * FAN PWMs have the following parameters:
+ *    Reference Zone,                 1, 2, 3, etc.
+ *    Spinup time,                    .05 sec
+ *    PWM value at limit/low temp,    1 count
+ *    PWM Frequency,                  1. Hz
+ *    PWM is Min or OFF below limit,  flag
+ *    Invert PWM output,              flag
+ *
+ * Some chips filter the temp, others the fan.
+ *    Filter constant (or disabled)   .1 seconds
+ */
+
+/* These are the zone temperature range encodings */
+static int lm85_range_map[] = {   /* .1 degC */
+		 20,  25,  33,  40,  50,  66,
+		 80, 100, 133, 160, 200, 266,
+		320, 400, 533, 800
+	};
+static int RANGE_TO_REG( int range )
+{
+	int i;
+
+	if( range >= lm85_range_map[15] ) { return 15 ; }
+	for( i = 0 ; i < 15 ; ++i )
+		if( range <= lm85_range_map[i] )
+			break ;
+	return( i & 0x0f );
+}
+#define RANGE_FROM_REG(val) (lm85_range_map[(val)&0x0f])
+
+/* These are the Acoustic Enhancement, or Temperature smoothing encodings
+ * NOTE: The enable/disable bit is INCLUDED in these encodings as the
+ *       MSB (bit 3, value 8).  If the enable bit is 0, the encoded value
+ *       is ignored, or set to 0.
+ */
+static int lm85_smooth_map[] = {  /* .1 sec */
+		350, 176, 118,  70,  44,   30,   16,    8
+/*    35.4 *    1/1, 1/2, 1/3, 1/5, 1/8, 1/12, 1/24, 1/48  */
+	};
+static int SMOOTH_TO_REG( int smooth )
+{
+	int i;
+
+	if( smooth <= 0 ) { return 0 ; }  /* Disabled */
+	for( i = 0 ; i < 7 ; ++i )
+		if( smooth >= lm85_smooth_map[i] )
+			break ;
+	return( (i & 0x07) | 0x08 );
+}
+#define SMOOTH_FROM_REG(val) ((val)&0x08?lm85_smooth_map[(val)&0x07]:0)
+
+/* These are the fan spinup delay time encodings */
+static int lm85_spinup_map[] = {  /* .1 sec */
+		0, 1, 2, 4, 7, 10, 20, 40
+	};
+static int SPINUP_TO_REG( int spinup )
+{
+	int i;
+
+	if( spinup >= lm85_spinup_map[7] ) { return 7 ; }
+	for( i = 0 ; i < 7 ; ++i )
+		if( spinup <= lm85_spinup_map[i] )
+			break ;
+	return( i & 0x07 );
+}
+#define SPINUP_FROM_REG(val) (lm85_spinup_map[(val)&0x07])
+
+/* These are the PWM frequency encodings */
+static int lm85_freq_map[] = { /* .1 Hz */
+		100, 150, 230, 300, 380, 470, 620, 980
+	};
+static int FREQ_TO_REG( int freq )
+{
+	int i;
+
+	if( freq >= lm85_freq_map[7] ) { return 7 ; }
+	for( i = 0 ; i < 7 ; ++i )
+		if( freq <= lm85_freq_map[i] )
+			break ;
+	return( i & 0x07 );
+}
+#define FREQ_FROM_REG(val) (lm85_freq_map[(val)&0x07])
+
+/* Since we can't use strings, I'm abusing these numbers
+ *   to stand in for the following meanings:
+ *      1 -- PWM responds to Zone 1
+ *      2 -- PWM responds to Zone 2
+ *      3 -- PWM responds to Zone 3
+ *     23 -- PWM responds to the higher temp of Zone 2 or 3
+ *    123 -- PWM responds to highest of Zone 1, 2, or 3
+ *      0 -- PWM is always at 0% (ie, off)
+ *     -1 -- PWM is always at 100%
+ *     -2 -- PWM responds to manual control
+ */
+static int lm85_zone_map[] = { 1, 2, 3, -1, 0, 23, 123, -2 };
+static int ZONE_TO_REG( int zone )
+{
+	int i;
+
+	for( i = 0 ; i <= 7 ; ++i )
+		if( zone == lm85_zone_map[i] )
+			break ;
+	if( i > 7 )   /* Not found. */
+		i = 3;  /* Always 100% */
+	return( (i & 0x07)<<5 );
+}
+#define ZONE_FROM_REG(val) (lm85_zone_map[((val)>>5)&0x07])
+
+#define HYST_TO_REG(val) (SENSORS_LIMIT((-(val)+5)/10,0,15))
+#define HYST_FROM_REG(val) (-(val)*10)
+
+#define OFFSET_TO_REG(val) (SENSORS_LIMIT((val)/25,-127,127))
+#define OFFSET_FROM_REG(val) ((val)*25)
+
+#define PPR_MASK(fan) (0x03<<(fan *2))
+#define PPR_TO_REG(val,fan) (SENSORS_LIMIT((val)-1,0,3)<<(fan *2))
+#define PPR_FROM_REG(val,fan) ((((val)>>(fan * 2))&0x03)+1)
+
+/* sensors_vid.h defines vid_from_reg() */
+#define VID_FROM_REG(val,vrm) (vid_from_reg((val),(vrm)))
+
+#define ALARMS_FROM_REG(val) (val)
+
+/* When converting to REG, we need to fixup the carry-over bit */
+#define INTMASK_FROM_REG(val) (val)
+#define INTMASK_TO_REG(val) (SENSORS_LIMIT((val)|((val)&0xff00?0x80:0),0,65535))
+
+/* Unlike some other drivers we DO NOT set initial limits.  Use
+ * the config file to set limits.  Some users have reported
+ * motherboards shutting down when we set limits in a previous
+ * version of this driver.  This may be caused by APM/ACPI
+ * detecting an out-of-limit condition when we had the wrong
+ * limits set.
+ */
+
+/* Typically used with Pentium 4 systems v9.1 VRM spec */
+#define LM85_INIT_VRM  91
+
+/* Chip sampling rates
+ *
+ * Some sensors are not updated more frequently than once per second
+ *    so it doesn't make sense to read them more often than that.
+ *    We cache the results and return the saved data if the driver
+ *    is called again before a second has elapsed.
+ *
+ * Also, there is significant configuration data for this chip
+ *    given the automatic PWM fan control that is possible.  There
+ *    are about 47 bytes of config data to only 22 bytes of actual
+ *    readings.  So, we keep the config data up to date in the cache
+ *    when it is written and only sample it once every 5 *minutes*
+ */
+#define LM85_DATA_INTERVAL  (1 * HZ)
+#define LM85_CONFIG_INTERVAL  (5 * 60 * HZ)
+
+/* For each registered LM85, we need to keep some data in memory. That
+   data is pointed to by client->data. The structure itself is
+   dynamically allocated, when a new lm85 client is allocated. */
+
+/* LM85 can automatically adjust fan speeds based on temperature
+ * This structure encapsulates an entire Zone config.  There are
+ * three zones (one for each temperature input) on the lm85
+ */
+struct lm85_zone {
+	s8 limit;	/* Low temp limit */
+	u8 hyst;	/* Low limit hysteresis. (0-15) */
+	u8 range;	/* Temp range, encoded */
+	s8 critical;	/* "All fans ON" temp limit */
+};
+
+struct lm85_autofan {
+	u8 config;	/* Register value */
+	u8 freq;	/* PWM frequency, encoded */
+	u8 min_pwm;	/* Minimum PWM value, encoded */
+	u8 min_off;	/* Min PWM or OFF below "limit", flag */
+};
+
+struct lm85_data {
+	struct i2c_client client;
+	struct semaphore lock;
+	int sysctl_id;
+	enum chips type;
+
+	struct semaphore update_lock;
+	int valid;		/* !=0 if following fields are valid */
+	unsigned long last_reading;	/* In jiffies */
+	unsigned long last_config;	/* In jiffies */
+
+	u8 in[8];		/* Register value */
+	u8 in_max[8];		/* Register value */
+	u8 in_min[8];		/* Register value */
+	s8 temp[3];		/* Register value */
+	s8 temp_min[3];		/* Register value */
+	s8 temp_max[3];		/* Register value */
+	s8 temp_offset[3];	/* Register value */
+	u16 fan[4];		/* Register value */
+	u16 fan_min[4];		/* Register value */
+	u8 pwm[3];		/* Register value */
+	u8 spinup_ctl;		/* Register encoding, combined */
+	u8 tach_mode;		/* Register encoding, combined */
+	u16 extend_adc;		/* Register value */
+	u8 fan_ppr;		/* Register value */
+	u8 smooth[3];		/* Register encoding */
+	u8 vid;			/* Register value */
+	u8 vrm;			/* VRM version */
+	u8 syncpwm3;		/* Saved PWM3 for TACH 2,3,4 config */
+	s8 oppoint[3];		/* Register value */
+	u16 tmin_ctl;		/* Register value */
+	long therm_total;	/* Cummulative therm count */
+	long therm_ovfl;	/* Count of therm overflows */
+	u8 therm_limit;		/* Register value */
+	u32 alarms;		/* Register encoding, combined */
+	u32 alarm_mask;		/* Register encoding, combined */
+	struct lm85_autofan autofan[3];
+	struct lm85_zone zone[3];
+};
+
+static int lm85_attach_adapter(struct i2c_adapter *adapter);
+static int lm85_detect(struct i2c_adapter *adapter, int address,
+			unsigned short flags, int kind);
+static int lm85_detach_client(struct i2c_client *client);
+static int lm85_read_value(struct i2c_client *client, u16 register);
+static int lm85_write_value(struct i2c_client *client, u16 register, int value);
+static void lm85_update_client(struct i2c_client *client);
+static void lm85_init_client(struct i2c_client *client);
+
+
+static void lm85_in(struct i2c_client *client, int operation, int ctl_name,
+			int *nrels_mag, long *results);
+static void lm85_fan(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm85_temp(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm85_vid(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm85_vrm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm85_alarms(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm85_pwm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm85_zone(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm85_pwm_config(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm85_pwm_zone(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm85_smooth(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+
+static void lm85_spinup_ctl(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm85_tach_mode(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+
+static void adm1027_tach_mode(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm1027_temp_offset(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm1027_fan_ppr(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adm1027_alarm_mask(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+
+static void adt7463_tmin_ctl(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void adt7463_therm_signal(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+
+static void emc6d100_in(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+
+static struct i2c_driver lm85_driver = {
+	.owner		= THIS_MODULE,
+	.name		=  "LM85 compatible sensor driver",
+	.id		= I2C_DRIVERID_LM85,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= &lm85_attach_adapter,
+	.detach_client	= &lm85_detach_client,
+};
+
+/* Unique ID assigned to each LM85 detected */
+static int lm85_id = 0;
+
+/* -- SENSORS SYSCTL START -- */
+/* Common parameters */
+#define LM85_SYSCTL_IN0                1000
+#define LM85_SYSCTL_IN1                1001
+#define LM85_SYSCTL_IN2                1002
+#define LM85_SYSCTL_IN3                1003
+#define LM85_SYSCTL_IN4                1004
+#define LM85_SYSCTL_FAN1               1005
+#define LM85_SYSCTL_FAN2               1006
+#define LM85_SYSCTL_FAN3               1007
+#define LM85_SYSCTL_FAN4               1008
+#define LM85_SYSCTL_TEMP1              1009
+#define LM85_SYSCTL_TEMP2              1010
+#define LM85_SYSCTL_TEMP3              1011
+#define LM85_SYSCTL_VID                1012
+#define LM85_SYSCTL_ALARMS             1013
+#define LM85_SYSCTL_PWM1               1014
+#define LM85_SYSCTL_PWM2               1015
+#define LM85_SYSCTL_PWM3               1016
+#define LM85_SYSCTL_VRM                1017
+#define LM85_SYSCTL_PWM_CFG1           1019
+#define LM85_SYSCTL_PWM_CFG2           1020
+#define LM85_SYSCTL_PWM_CFG3           1021
+#define LM85_SYSCTL_PWM_ZONE1          1022
+#define LM85_SYSCTL_PWM_ZONE2          1023
+#define LM85_SYSCTL_PWM_ZONE3          1024
+#define LM85_SYSCTL_ZONE1              1025
+#define LM85_SYSCTL_ZONE2              1026
+#define LM85_SYSCTL_ZONE3              1027
+#define LM85_SYSCTL_SMOOTH1            1028
+#define LM85_SYSCTL_SMOOTH2            1029
+#define LM85_SYSCTL_SMOOTH3            1030
+
+/* Vendor specific values */
+#define LM85_SYSCTL_SPINUP_CTL         1100
+#define LM85_SYSCTL_TACH_MODE          1101
+
+/* Analog Devices variant of the LM85 */
+#define ADM1027_SYSCTL_TACH_MODE       1200
+#define ADM1027_SYSCTL_TEMP_OFFSET1    1201
+#define ADM1027_SYSCTL_TEMP_OFFSET2    1202
+#define ADM1027_SYSCTL_TEMP_OFFSET3    1203
+#define ADM1027_SYSCTL_FAN_PPR         1204
+#define ADM1027_SYSCTL_ALARM_MASK      1205
+
+/* Analog Devices variant of the LM85/ADM1027 */
+#define ADT7463_SYSCTL_TMIN_CTL1       1300
+#define ADT7463_SYSCTL_TMIN_CTL2       1301
+#define ADT7463_SYSCTL_TMIN_CTL3       1302
+#define ADT7463_SYSCTL_THERM_SIGNAL    1303
+
+/* SMSC variant of the LM85 */
+#define EMC6D100_SYSCTL_IN5            1400
+#define EMC6D100_SYSCTL_IN6            1401
+#define EMC6D100_SYSCTL_IN7            1402
+
+#define LM85_ALARM_IN0          0x0001
+#define LM85_ALARM_IN1          0x0002
+#define LM85_ALARM_IN2          0x0004
+#define LM85_ALARM_IN3          0x0008
+#define LM85_ALARM_TEMP1        0x0010
+#define LM85_ALARM_TEMP2        0x0020
+#define LM85_ALARM_TEMP3        0x0040
+#define LM85_ALARM_ALARM2       0x0080
+#define LM85_ALARM_IN4          0x0100
+#define LM85_ALARM_RESERVED     0x0200
+#define LM85_ALARM_FAN1         0x0400
+#define LM85_ALARM_FAN2         0x0800
+#define LM85_ALARM_FAN3         0x1000
+#define LM85_ALARM_FAN4         0x2000
+#define LM85_ALARM_TEMP1_FAULT  0x4000
+#define LM85_ALARM_TEMP3_FAULT 0x08000
+#define LM85_ALARM_IN6         0x10000
+#define LM85_ALARM_IN7         0x20000
+#define LM85_ALARM_IN5         0x40000
+/* -- SENSORS SYSCTL END -- */
+
+/* The /proc/sys entries */
+/* These files are created for each detected LM85. This is just a template;
+ *    The actual list is built from this and additional per-chip
+ *    custom lists below.  Note the XXX_LEN macros.  These must be
+ *    compile time constants because they will be used to allocate
+ *    space for the final template passed to i2c_register_entry.
+ *    We depend on the ability of GCC to evaluate expressions at
+ *    compile time to turn these expressions into compile time
+ *    constants, but this can generate a warning.
+ */
+static ctl_table lm85_common[] = {
+	{LM85_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_in},
+	{LM85_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_in},
+	{LM85_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_in},
+	{LM85_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_in},
+	{LM85_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_in},
+	{LM85_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_fan},
+	{LM85_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_fan},
+	{LM85_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_fan},
+	{LM85_SYSCTL_FAN4, "fan4", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_fan},
+	{LM85_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_temp},
+	{LM85_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_temp},
+	{LM85_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_temp},
+	{LM85_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_vid},
+	{LM85_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_vrm},
+	{LM85_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_alarms},
+	{LM85_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_pwm},
+	{LM85_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_pwm},
+	{LM85_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_pwm},
+	{LM85_SYSCTL_PWM_CFG1, "pwm1_cfg", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_pwm_config},
+	{LM85_SYSCTL_PWM_CFG2, "pwm2_cfg", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_pwm_config},
+	{LM85_SYSCTL_PWM_CFG3, "pwm3_cfg", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_pwm_config},
+	{LM85_SYSCTL_PWM_ZONE1, "pwm1_zone", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &lm85_pwm_zone},
+	{LM85_SYSCTL_PWM_ZONE2, "pwm2_zone", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &lm85_pwm_zone},
+	{LM85_SYSCTL_PWM_ZONE3, "pwm3_zone", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &lm85_pwm_zone},
+	{LM85_SYSCTL_ZONE1, "zone1", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_zone},
+	{LM85_SYSCTL_ZONE2, "zone2", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_zone},
+	{LM85_SYSCTL_ZONE3, "zone3", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_zone},
+	{LM85_SYSCTL_SMOOTH1, "smooth1", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_smooth},
+	{LM85_SYSCTL_SMOOTH2, "smooth2", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_smooth},
+	{LM85_SYSCTL_SMOOTH3, "smooth3", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &lm85_smooth},
+	{0}
+};
+#define CTLTBL_COMMON (sizeof(lm85_common)/sizeof(lm85_common[0]))
+
+/* NOTE: tach_mode is a shared name, but implemented with
+ *   different functions
+ */
+static ctl_table lm85_specific[] = {
+	{LM85_SYSCTL_SPINUP_CTL, "spinup_ctl", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &lm85_spinup_ctl},
+	{LM85_SYSCTL_TACH_MODE, "tach_mode", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &lm85_tach_mode},
+/*	{0} The doc generator needs this. */
+};
+#define CTLTBL_LM85 (sizeof(lm85_specific)/sizeof(lm85_specific[0]))
+
+static ctl_table adm1027_specific[] = {
+	{ADM1027_SYSCTL_TACH_MODE, "tach_mode", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1027_tach_mode},
+	{ADM1027_SYSCTL_TEMP_OFFSET1, "temp1_offset", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1027_temp_offset},
+	{ADM1027_SYSCTL_TEMP_OFFSET2, "temp2_offset", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1027_temp_offset},
+	{ADM1027_SYSCTL_TEMP_OFFSET3, "temp3_offset", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1027_temp_offset},
+	{ADM1027_SYSCTL_FAN_PPR, "fan_ppr", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1027_fan_ppr},
+	{ADM1027_SYSCTL_ALARM_MASK, "alarm_mask", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adm1027_alarm_mask},
+/*	{0} The doc generator needs this. */
+};
+#define CTLTBL_ADM1027 (sizeof(adm1027_specific)/sizeof(adm1027_specific[0]))
+
+static ctl_table adt7463_specific[] = {
+	{ADT7463_SYSCTL_TMIN_CTL1, "tmin_ctl1", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adt7463_tmin_ctl},
+	{ADT7463_SYSCTL_TMIN_CTL2, "tmin_ctl2", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adt7463_tmin_ctl},
+	{ADT7463_SYSCTL_TMIN_CTL3, "tmin_ctl3", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adt7463_tmin_ctl},
+	{ADT7463_SYSCTL_THERM_SIGNAL, "therm_signal", NULL, 0, 0644, NULL,
+		&i2c_proc_real, &i2c_sysctl_real, NULL, &adt7463_therm_signal},
+/*	{0} The doc generator needs this. */
+};
+#define CTLTBL_ADT7463 (sizeof(adt7463_specific)/sizeof(adt7463_specific[0]))
+
+static ctl_table emc6d100_specific[] = {
+	{EMC6D100_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &emc6d100_in},
+	{EMC6D100_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &emc6d100_in},
+	{EMC6D100_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real,
+		&i2c_sysctl_real, NULL, &emc6d100_in},
+/*	{0} The doc generator needs this. */
+};
+#define CTLTBL_EMC6D100 (sizeof(emc6d100_specific)/sizeof(emc6d100_specific[0]))
+
+
+#define MAX2(a,b) ((a)>(b)?(a):(b))
+#define MAX3(a,b,c) ((a)>(b)?MAX2((a),(c)):MAX2((b),(c)))
+#define MAX4(a,b,c,d) ((a)>(b)?MAX3((a),(c),(d)):MAX3((b),(c),(d)))
+
+#define CTLTBL_MAX (CTLTBL_COMMON + MAX3(CTLTBL_LM85, CTLTBL_ADM1027+CTLTBL_ADT7463, CTLTBL_EMC6D100))
+
+/* This function is called when:
+     * lm85_driver is inserted (when this module is loaded), for each
+       available adapter
+     * when a new adapter is inserted (and lm85_driver is still present) */
+int lm85_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, lm85_detect);
+}
+
+/* This function is called by i2c_detect */
+int lm85_detect(struct i2c_adapter *adapter, int address,
+		unsigned short flags, int kind)
+{
+	int i;
+	int company, verstep ;
+	struct i2c_client *new_client;
+	struct lm85_data *data;
+	int err = 0;
+	const char *type_name = "";
+	struct ctl_table template[CTLTBL_MAX] ;
+	int template_used ;
+
+	if (i2c_is_isa_adapter(adapter)) {
+		/* This chip has no ISA interface */
+		goto ERROR0 ;
+	};
+
+	if (!i2c_check_functionality(adapter,
+					I2C_FUNC_SMBUS_BYTE_DATA)) {
+		/* We need to be able to do byte I/O */
+		goto ERROR0 ;
+	};
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access lm85_{read,write}_value. */
+
+	if (!(data = kmalloc(sizeof(struct lm85_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &lm85_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+
+	company = lm85_read_value(new_client, LM85_REG_COMPANY);
+	verstep = lm85_read_value(new_client, LM85_REG_VERSTEP);
+
+#ifdef DEBUG
+	printk("lm85: Detecting device at %d,0x%02x with"
+		" COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
+		i2c_adapter_id(new_client->adapter), new_client->addr,
+		company, verstep
+	    );
+#endif
+
+	/* If auto-detecting, Determine the chip type. */
+	if (kind <= 0) {
+#ifdef DEBUG
+		printk("lm85: Autodetecting device at %d,0x%02x ...\n",
+			i2c_adapter_id(adapter), address );
+#endif
+		if( company == LM85_COMPANY_NATIONAL
+		    && verstep == LM85_VERSTEP_LM85C ) {
+			kind = lm85c ;
+		} else if( company == LM85_COMPANY_NATIONAL
+		    && verstep == LM85_VERSTEP_LM85B ) {
+			kind = lm85b ;
+		} else if( company == LM85_COMPANY_NATIONAL
+		    && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) {
+			printk("lm85: Detected National Semiconductor chip\n");
+			printk("lm85: Unrecgonized version/stepping 0x%02x"
+			    " Defaulting to Generic LM85.\n", verstep );
+			kind = any_chip ;
+		} else if( company == LM85_COMPANY_ANALOG_DEV
+		    && verstep == LM85_VERSTEP_ADM1027 ) {
+			kind = adm1027 ;
+		} else if( company == LM85_COMPANY_ANALOG_DEV
+		    && verstep == LM85_VERSTEP_ADT7463 ) {
+			kind = adt7463 ;
+		} else if( company == LM85_COMPANY_ANALOG_DEV
+		    && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) {
+			printk("lm85: Detected Analog Devices chip\n");
+			printk("lm85: Unrecgonized version/stepping 0x%02x"
+			    " Defaulting to Generic LM85.\n", verstep );
+			kind = any_chip ;
+		} else if( company == LM85_COMPANY_SMSC
+		    && (verstep == LM85_VERSTEP_EMC6D100_A0
+			 || verstep == LM85_VERSTEP_EMC6D100_A1) ) {
+			/* Unfortunately, we can't tell a '100 from a '101
+			 *   from the registers.  Since a '101 is a '100
+			 *   in a package with fewer pins and therefore no
+			 *   3.3V, 1.5V or 1.8V inputs, perhaps if those
+			 *   inputs read 0, then it's a '101.
+			 */
+			kind = emc6d100 ;
+		} else if( company == LM85_COMPANY_SMSC
+		    && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) {
+			printk("lm85: Detected SMSC chip\n");
+			printk("lm85: Unrecognized version/stepping 0x%02x"
+			    " Defaulting to Generic LM85.\n", verstep );
+			kind = any_chip ;
+		} else if( kind == any_chip
+		    && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) {
+			printk("lm85: Generic LM85 Version 6 detected\n");
+			/* Leave kind as "any_chip" */
+		} else {
+#ifdef DEBUG
+			printk("lm85: Autodetection failed\n");
+#endif
+			/* Not an LM85 ... */
+			if( kind == any_chip ) {  /* User used force=x,y */
+			    printk("lm85: Generic LM85 Version 6 not"
+				" found at %d,0x%02x. Try force_lm85c.\n",
+				i2c_adapter_id(adapter), address );
+			}
+			err = 0 ;
+			goto ERROR1;
+		}
+	}
+
+	/* Fill in the chip specific driver values */
+	switch (kind) {
+	case any_chip :
+		type_name = "lm85";
+		strcpy(new_client->name, "Generic LM85");
+		template_used = 0 ;
+		break ;
+	case lm85b :
+		type_name = "lm85b";
+		strcpy(new_client->name, "National LM85-B");
+		memcpy( template, lm85_specific, sizeof(lm85_specific) );
+		template_used = CTLTBL_LM85 ;
+		break ;
+	case lm85c :
+		type_name = "lm85c";
+		strcpy(new_client->name, "National LM85-C");
+		memcpy( template, lm85_specific, sizeof(lm85_specific) );
+		template_used = CTLTBL_LM85 ;
+		break ;
+	case adm1027 :
+		type_name = "adm1027";
+		strcpy(new_client->name, "Analog Devices ADM1027");
+		memcpy( template, adm1027_specific, sizeof(adm1027_specific) );
+		template_used = CTLTBL_ADM1027 ;
+		break ;
+	case adt7463 :
+		type_name = "adt7463";
+		strcpy(new_client->name, "Analog Devices ADT7463");
+		memcpy( template, adt7463_specific, sizeof(adt7463_specific) );
+		template_used = CTLTBL_ADT7463 ;
+		memcpy( template+template_used, adm1027_specific, sizeof(adm1027_specific) );
+		template_used += CTLTBL_ADM1027 ;
+		break ;
+	case emc6d100 :
+		type_name = "emc6d100";
+		strcpy(new_client->name, "SMSC EMC6D100");
+		memcpy(template, emc6d100_specific, sizeof(emc6d100_specific));
+		template_used = CTLTBL_EMC6D100 ;
+		break ;
+	default :
+		printk("lm85: Internal error, invalid kind (%d)!", kind);
+		err = -EFAULT ;
+		goto ERROR1;
+	}
+
+	/* Fill in the remaining client fields */
+	new_client->id = lm85_id++;
+	printk("lm85: Assigning ID %d to %s at %d,0x%02x\n",
+		new_client->id, new_client->name,
+		i2c_adapter_id(new_client->adapter),
+		new_client->addr
+	    );
+
+	/* Housekeeping values */
+	data->type = kind;
+	data->valid = 0;
+
+	/* Set the VRM version */
+	data->vrm = LM85_INIT_VRM ;
+
+	/* Zero the accumulators */
+	data->therm_total = 0;
+	data->therm_ovfl = 0;
+
+	init_MUTEX(&data->update_lock);
+
+	/* Initialize the LM85 chip */
+	lm85_init_client(new_client);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR1;
+
+	/* Finish out the template */
+	memcpy( template + template_used, lm85_common, sizeof(lm85_common) );
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client,
+					type_name,
+					template)) < 0) {
+		err = i;
+		goto ERROR2;
+	}
+	data->sysctl_id = i;
+
+	return 0;
+
+	/* Error out and cleanup code */
+    ERROR2:
+	i2c_detach_client(new_client);
+    ERROR1:
+	kfree(data);
+    ERROR0:
+	return err;
+}
+
+int lm85_detach_client(struct i2c_client *client)
+{
+	int err;
+	int id ;
+
+	id = client->id;
+	i2c_deregister_entry(((struct lm85_data *)(client->data))->sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk("lm85(%d): Client deregistration failed,"
+			" client not detached.\n", id );
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+int lm85_read_value(struct i2c_client *client, u16 reg)
+{
+	int res;
+
+	/* What size location is it? */
+	switch( reg ) {
+	case LM85_REG_FAN(0) :  /* Read WORD data */
+	case LM85_REG_FAN(1) :
+	case LM85_REG_FAN(2) :
+	case LM85_REG_FAN(3) :
+	case LM85_REG_FAN_MIN(0) :
+	case LM85_REG_FAN_MIN(1) :
+	case LM85_REG_FAN_MIN(2) :
+	case LM85_REG_FAN_MIN(3) :
+	case LM85_REG_ALARM :  /* Read ALARM1 and ALARM2 */
+	case ADM1027_REG_INTMASK :  /* Read MASK1 and MASK2 */
+	case ADM1027_REG_EXTEND_ADC :  /* Read ADC1 and ADC2 */
+		reg &= 0xff ;  /* Pseudo words have address + 0x0100 */
+		res = i2c_smbus_read_byte_data(client, reg) & 0xff ;
+		res |= (i2c_smbus_read_byte_data(client, reg+1) & 0xff) << 8 ;
+		break ;
+	case ADT7463_REG_TMIN_CTL :  /* Read WORD MSB, LSB */
+		reg &= 0xff ;  /* Pseudo words have address + 0x0100 */
+		res = (i2c_smbus_read_byte_data(client, reg) & 0xff) << 8 ;
+		res |= i2c_smbus_read_byte_data(client, reg+1) & 0xff ;
+		break ;
+	default:	/* Read BYTE data */
+		res = i2c_smbus_read_byte_data(client, reg & 0xff) & 0xff ;
+		break ;
+	}
+
+	return res ;
+}
+
+int lm85_write_value(struct i2c_client *client, u16 reg, int value)
+{
+	int res ;
+
+	switch( reg ) {
+	case LM85_REG_FAN(0) :  /* Write WORD data */
+	case LM85_REG_FAN(1) :
+	case LM85_REG_FAN(2) :
+	case LM85_REG_FAN(3) :
+	case LM85_REG_FAN_MIN(0) :
+	case LM85_REG_FAN_MIN(1) :
+	case LM85_REG_FAN_MIN(2) :
+	case LM85_REG_FAN_MIN(3) :
+	case ADM1027_REG_INTMASK :
+	/* NOTE: ALARM and ADC are read only, so not included here */
+		reg &= 0xff ;  /* Pseudo words have address + 0x0100 */
+		res = i2c_smbus_write_byte_data(client, reg, value & 0xff) ;
+		res |= i2c_smbus_write_byte_data(client, reg+1, (value>>8) & 0xff) ;
+		break ;
+	case ADT7463_REG_TMIN_CTL :  /* Write WORD MSB, LSB */
+		reg &= 0xff ;  /* Pseudo words have address + 0x0100 */
+		res = i2c_smbus_write_byte_data(client, reg, (value>>8) & 0xff);
+		res |= i2c_smbus_write_byte_data(client, reg+1, value & 0xff) ;
+		break ;
+	default:	/* Write BYTE data */
+		res = i2c_smbus_write_byte_data(client, reg & 0xff, value);
+		break ;
+	}
+
+	return res ;
+}
+
+/* Called when we have found a new LM85. It should set limits, etc. */
+void lm85_init_client(struct i2c_client *client)
+{
+	int value;
+	struct lm85_data *data = client->data;
+
+#ifdef DEBUG
+	printk("lm85(%d): Initializing device\n", client->id);
+#endif
+
+	/* Warn if part was not "READY" */
+	value = lm85_read_value(client, LM85_REG_CONFIG);
+#ifdef DEBUG
+	printk("lm85(%d): LM85_REG_CONFIG is: 0x%02x\n", client->id, value );
+#endif
+	if( value & 0x02 ) {
+		printk("lm85(%d): Client (%d,0x%02x) config is locked.\n",
+			    client->id,
+			    i2c_adapter_id(client->adapter), client->addr );
+	};
+	if( ! (value & 0x04) ) {
+		printk("lm85(%d): Client (%d,0x%02x) is not ready.\n",
+			    client->id,
+			    i2c_adapter_id(client->adapter), client->addr );
+	};
+	if( (data->type == adm1027 || data->type == adt7463)
+	    && (value & 0x10)
+	) {
+		printk("lm85(%d): Client (%d,0x%02x) VxI mode is set.  "
+			"Please report this to the lm85 maintainer.\n",
+			    client->id,
+			    i2c_adapter_id(client->adapter), client->addr );
+	};
+
+	/* See if SYNC to PWM3 is set */
+	if( data->type == adt7463 
+	    && (lm85_read_value(client, LM85_REG_AFAN_SPIKE1) & 0x10)
+	) {
+		printk("lm85(%d): Sync to PWM3 is set.  Expect PWM3 "
+			"to control fans 2, 3, and 4\n",
+			client->id );
+	};
+
+	/* See if PWM2 is #SMBALERT */
+	if( (data->type == adm1027 || data->type == adt7463)
+	    && (lm85_read_value(client, ADM1027_REG_CONFIG3) & 0x01)
+	) {
+		printk("lm85(%d): PWM2 is SMBALERT.  PWM2 not available.\n",
+			client->id );
+	};
+
+	/* Check if 2.5V and 5V inputs are reconfigured */
+	if( data->type == adt7463 ) {
+		value = lm85_read_value(client, ADT7463_REG_CONFIG4);
+		if( value & 0x01 ) {
+			printk("lm85(%d): 2.5V input (in0) is SMBALERT.  "
+				"in0 not available.\n", client->id );
+		};
+		if( value & 0x02 ) {
+			printk("lm85(%d): 5V input (in3) is THERM.  "
+				"in3 not available.\n", client->id );
+		}
+	};
+
+	/* FIXME?  Display EMC6D100 config info? */
+
+	/* WE INTENTIONALLY make no changes to the limits,
+	 *   offsets, pwms, fans and zones.  If they were
+	 *   configured, we don't want to mess with them.
+	 *   If they weren't, the default is 100% PWM, no
+	 *   control and will suffice until 'sensors -s'
+	 *   can be run by the user.
+	 */
+
+	/* Start monitoring */
+	value = lm85_read_value(client, LM85_REG_CONFIG);
+	/* Try to clear LOCK, Set START, save everything else */
+	value = ((value & ~ 0x02) | 0x01) & 0xff ;
+#ifdef DEBUG
+	printk("lm85(%d): Setting CONFIG to: 0x%02x\n", client->id, value );
+#endif
+	lm85_write_value(client, LM85_REG_CONFIG, value);
+
+}
+
+void lm85_update_client(struct i2c_client *client)
+{
+	struct lm85_data *data = client->data;
+	int i;
+
+	down(&data->update_lock);
+
+	if (!data->valid
+	    || (jiffies - data->last_reading > LM85_DATA_INTERVAL )) {
+		/* Things that change quickly */
+
+#ifdef DEBUG
+		printk("lm85(%d): Reading sensor values\n", client->id);
+#endif
+		/* Have to read extended bits first to "freeze" the
+		 * more significant bits that are read later.
+		 */
+		switch( data->type ) {
+		case adm1027 :
+		case adt7463 :
+			data->extend_adc =
+			    lm85_read_value(client, ADM1027_REG_EXTEND_ADC);
+			break ;
+		default :
+			data->extend_adc = 0 ;
+			break ;
+		}
+
+		for (i = 0; i <= 4; ++i) {
+			data->in[i] =
+			    lm85_read_value(client, LM85_REG_IN(i));
+		}
+
+		for (i = 0; i <= 3; ++i) {
+			data->fan[i] =
+			    lm85_read_value(client, LM85_REG_FAN(i));
+		}
+
+		for (i = 0; i <= 2; ++i) {
+			data->temp[i] =
+			    lm85_read_value(client, LM85_REG_TEMP(i));
+		}
+
+		for (i = 0; i <= 2; ++i) {
+			data->pwm[i] =
+			    lm85_read_value(client, LM85_REG_PWM(i));
+		}
+
+		data->alarms = lm85_read_value(client, LM85_REG_ALARM);
+
+		switch( ((struct lm85_data *)(client->data))->type ) {
+		case adt7463 :
+			/* REG_THERM code duplicated in therm_signal() */
+			i = lm85_read_value(client, ADT7463_REG_THERM);
+			if( data->therm_total < LONG_MAX - 256 ) {
+			    data->therm_total += i ;
+			}
+			if( i >= 255 ) {
+				++data->therm_ovfl ;
+			}
+			break ;
+		case emc6d100 :
+			/* Three more voltage sensors */
+			for (i = 5; i <= 7; ++i) {
+			    data->in[i] =
+				lm85_read_value(client, EMC6D100_REG_IN(i));
+			}
+			/* More alarm bits */
+			data->alarms |=
+			    lm85_read_value(client, EMC6D100_REG_ALARM3) << 16;
+
+			break ;
+		default : break ; /* no warnings */
+		}
+
+		data->last_reading = jiffies ;
+	};  /* last_reading */
+
+	if (!data->valid
+	    || (jiffies - data->last_config > LM85_CONFIG_INTERVAL) ) {
+		/* Things that don't change often */
+
+#ifdef DEBUG
+		printk("lm85(%d): Reading config values\n", client->id);
+#endif
+		for (i = 0; i <= 4; ++i) {
+			data->in_min[i] =
+			    lm85_read_value(client, LM85_REG_IN_MIN(i));
+			data->in_max[i] =
+			    lm85_read_value(client, LM85_REG_IN_MAX(i));
+		}
+
+		for (i = 0; i <= 3; ++i) {
+			data->fan_min[i] =
+			    lm85_read_value(client, LM85_REG_FAN_MIN(i));
+		}
+
+		for (i = 0; i <= 2; ++i) {
+			data->temp_min[i] =
+			    lm85_read_value(client, LM85_REG_TEMP_MIN(i));
+			data->temp_max[i] =
+			    lm85_read_value(client, LM85_REG_TEMP_MAX(i));
+		}
+
+		data->vid = lm85_read_value(client, LM85_REG_VID);
+
+		for (i = 0; i <= 2; ++i) {
+			int val ;
+			data->autofan[i].config =
+			    lm85_read_value(client, LM85_REG_AFAN_CONFIG(i));
+			val = lm85_read_value(client, LM85_REG_AFAN_RANGE(i));
+			data->autofan[i].freq = val & 0x07 ;
+			data->zone[i].range = (val >> 4) & 0x0f ;
+			data->autofan[i].min_pwm =
+			    lm85_read_value(client, LM85_REG_AFAN_MINPWM(i));
+			data->zone[i].limit =
+			    lm85_read_value(client, LM85_REG_AFAN_LIMIT(i));
+			data->zone[i].critical =
+			    lm85_read_value(client, LM85_REG_AFAN_CRITICAL(i));
+		}
+
+		i = lm85_read_value(client, LM85_REG_AFAN_SPIKE1);
+		data->smooth[0] = i & 0x0f ;
+		data->syncpwm3 = i & 0x10 ;  /* Save PWM3 config */
+		data->autofan[0].min_off = i & 0x20 ;
+		data->autofan[1].min_off = i & 0x40 ;
+		data->autofan[2].min_off = i & 0x80 ;
+		i = lm85_read_value(client, LM85_REG_AFAN_SPIKE2);
+		data->smooth[1] = (i>>4) & 0x0f ;
+		data->smooth[2] = i & 0x0f ;
+
+		i = lm85_read_value(client, LM85_REG_AFAN_HYST1);
+		data->zone[0].hyst = (i>>4) & 0x0f ;
+		data->zone[1].hyst = i & 0x0f ;
+
+		i = lm85_read_value(client, LM85_REG_AFAN_HYST2);
+		data->zone[2].hyst = (i>>4) & 0x0f ;
+
+		switch( ((struct lm85_data *)(client->data))->type ) {
+		case lm85b :
+		case lm85c :
+			data->tach_mode = lm85_read_value(client,
+				LM85_REG_TACH_MODE );
+			data->spinup_ctl = lm85_read_value(client,
+				LM85_REG_SPINUP_CTL );
+			break ;
+		case adt7463 :
+			for (i = 0; i <= 2; ++i) {
+			    data->oppoint[i] = lm85_read_value(client,
+				ADT7463_REG_OPPOINT(i) );
+			}
+			data->tmin_ctl = lm85_read_value(client,
+				ADT7463_REG_TMIN_CTL );
+			data->therm_limit = lm85_read_value(client,
+				ADT7463_REG_THERM_LIMIT );
+		/* FALL THROUGH */
+		case adm1027 :
+			for (i = 0; i <= 2; ++i) {
+			    data->temp_offset[i] = lm85_read_value(client,
+				ADM1027_REG_TEMP_OFFSET(i) );
+			}
+			data->tach_mode = lm85_read_value(client,
+				ADM1027_REG_CONFIG3 );
+			data->fan_ppr = lm85_read_value(client,
+				ADM1027_REG_FAN_PPR );
+			data->alarm_mask = lm85_read_value(client,
+				ADM1027_REG_INTMASK );
+			break ;
+		case emc6d100 :
+			for (i = 5; i <= 7; ++i) {
+			    data->in_min[i] =
+				lm85_read_value(client, EMC6D100_REG_IN_MIN(i));
+			    data->in_max[i] =
+				lm85_read_value(client, EMC6D100_REG_IN_MAX(i));
+			}
+			break ;
+		default : break ; /* no warnings */
+		}
+	
+		data->last_config = jiffies;
+	};  /* last_config */
+
+	data->valid = 1;
+
+	up(&data->update_lock);
+}
+
+
+/* The next functions are the call-back functions of the /proc/sys and
+   sysctl files. Which function is used is defined in the ctl_table in
+   the extra1 field.
+   Each function must return the magnitude (power of 10 to divide the data
+   with) if it is called with operation==SENSORS_PROC_REAL_INFO.  It must
+   put a maximum of *nrels elements in results reflecting the data of this
+   file, and set *nrels to the number it actually put in it, if operation==
+   SENSORS_PROC_REAL_READ.  Finally, it must get upto *nrels elements from
+   results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE.
+ */
+void lm85_in(struct i2c_client *client, int operation, int ctl_name,
+	     int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int nr = ctl_name - LM85_SYSCTL_IN0;
+
+	if (nr < 0 || nr > 4)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 3;  /* 1.000 */
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		int  ext = 0 ;
+		lm85_update_client(client);
+		ext = EXT_FROM_REG(data->extend_adc, nr);
+		results[0] = INS_FROM_REG(nr,data->in_min[nr]);
+		results[1] = INS_FROM_REG(nr,data->in_max[nr]);
+		results[2] = INSEXT_FROM_REG(nr,data->in[nr],ext);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 1) {
+			data->in_max[nr] = INS_TO_REG(nr,results[1]);
+			lm85_write_value(client, LM85_REG_IN_MAX(nr),
+					 data->in_max[nr]);
+		}
+		if (*nrels_mag > 0) {
+			data->in_min[nr] = INS_TO_REG(nr,results[0]);
+			lm85_write_value(client, LM85_REG_IN_MIN(nr),
+					 data->in_min[nr]);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void lm85_fan(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int nr = ctl_name - LM85_SYSCTL_FAN1 ;
+
+	if (nr < 0 || nr > 3)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		results[0] = FAN_FROM_REG(data->fan_min[nr]);
+		results[1] = FAN_FROM_REG(data->fan[nr]);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 0) {
+			data->fan_min[nr] = FAN_TO_REG(results[0]);
+			lm85_write_value(client, LM85_REG_FAN_MIN(nr),
+					 data->fan_min[nr]);
+		}
+		up(&data->update_lock);
+	}
+}
+
+
+void lm85_temp(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int nr = ctl_name - LM85_SYSCTL_TEMP1 ;
+
+	if (nr < 0 || nr > 2)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		int  ext = 0 ;
+		lm85_update_client(client);
+
+		/* +5 for offset of temp data in ext reg */
+		ext = EXT_FROM_REG(data->extend_adc, nr+5);
+
+		results[0] = TEMP_FROM_REG(data->temp_min[nr]);
+		results[1] = TEMP_FROM_REG(data->temp_max[nr]);
+		results[2] = TEMPEXT_FROM_REG(data->temp[nr],ext);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 1) {
+			data->temp_max[nr] = TEMP_TO_REG(results[1]);
+			lm85_write_value(client, LM85_REG_TEMP_MAX(nr),
+					 data->temp_max[nr]);
+		}
+		if (*nrels_mag > 0) {
+			data->temp_min[nr] = TEMP_TO_REG(results[0]);
+			lm85_write_value(client, LM85_REG_TEMP_MIN(nr),
+					 data->temp_min[nr]);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void lm85_pwm(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int nr = ctl_name - LM85_SYSCTL_PWM1 ;
+	int pwm_zone ;
+
+	if (nr < 0 || nr > 2)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		results[0] = PWM_FROM_REG(data->pwm[nr]);
+		pwm_zone = ZONE_FROM_REG(data->autofan[nr].config);
+		/* PWM "enabled" if not off (0) nor on (-1) */
+		results[1] = pwm_zone != 0 && pwm_zone != -1 ;
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		/* PWM enable is read-only */
+		if (*nrels_mag > 0) {
+			data->pwm[nr] = PWM_TO_REG(results[0]);
+			lm85_write_value(client, LM85_REG_PWM(nr),
+					 data->pwm[nr]);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void lm85_vid(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+
+	if( ctl_name != LM85_SYSCTL_VID )
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 3;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		results[0] = VID_FROM_REG((data->vid)&0x3f,data->vrm);
+		*nrels_mag = 1;
+	}
+}
+
+void lm85_vrm(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+
+	if( ctl_name != LM85_SYSCTL_VRM )
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = data->vrm ;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 0) {
+			data->vrm = results[0] ;
+		}
+		up(&data->update_lock);
+	}
+}
+
+void lm85_alarms(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+
+	if( ctl_name != LM85_SYSCTL_ALARMS )
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->alarms);
+		*nrels_mag = 1;
+	}
+}
+
+void lm85_spinup_ctl(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int old;
+
+	if( ctl_name != LM85_SYSCTL_SPINUP_CTL )
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		results[0] = (data->spinup_ctl & 1) != 0 ;
+		results[1] = (data->spinup_ctl & 2) != 0 ;
+		results[2] = (data->spinup_ctl & 4) != 0 ;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		old = data->spinup_ctl ;
+		if (*nrels_mag > 2) {
+			old = (old & (~4)) | (results[2]?4:0) ;
+		}
+		if (*nrels_mag > 1) {
+			old = (old & (~2)) | (results[1]?2:0) ;
+		}
+		if (*nrels_mag > 0) {
+			old = (old & (~1)) | (results[0]?1:0) ;
+			lm85_write_value(client, LM85_REG_SPINUP_CTL, old);
+			data->spinup_ctl = old ;
+		}
+		up(&data->update_lock);
+	}
+}
+
+void lm85_tach_mode(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int old;
+
+	/* Tach Mode 1, Tach Mode 2, Tach Mode 3 & 4 */
+
+	if( ctl_name != LM85_SYSCTL_TACH_MODE )
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		results[0] = (data->tach_mode & 0x03) ;
+		results[1] = (data->tach_mode & 0x0c) >> 2 ;
+		results[2] = (data->tach_mode & 0x30) >> 4 ;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		old = data->tach_mode ;
+		if (*nrels_mag > 2) {
+			old = (old & (~0x30)) | ((results[2]&3) << 4) ;
+		}
+		if (*nrels_mag > 1) {
+			old = (old & (~0x0c)) | ((results[1]&3) << 2) ;
+		}
+		if (*nrels_mag > 0) {
+			old = (old & (~0x03)) |  (results[0]&3) ;
+			lm85_write_value(client, LM85_REG_TACH_MODE, old);
+			data->tach_mode = old ;
+		}
+		up(&data->update_lock);
+	}
+}
+
+void adm1027_tach_mode(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int old;
+
+	/* Tach/DC 1, Tach/DC 2, Tach/DC 3, Tach/DC 4 */
+
+	if( ctl_name != ADM1027_SYSCTL_TACH_MODE )
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		results[0] = (data->tach_mode & 0x10) != 0 ;
+		results[1] = (data->tach_mode & 0x20) != 0 ;
+		results[2] = (data->tach_mode & 0x40) != 0 ;
+		results[3] = (data->tach_mode & 0x80) != 0 ;
+		*nrels_mag = 4;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		old = data->tach_mode ;
+		if (*nrels_mag > 3) {
+			old = (old & (~0x80)) | (results[3] ? 0x80 : 0) ;
+		}
+		if (*nrels_mag > 2) {
+			old = (old & (~0x40)) | (results[2] ? 0x40 : 0) ;
+		}
+		if (*nrels_mag > 1) {
+			old = (old & (~0x20)) | (results[1] ? 0x20 : 0) ;
+		}
+		if (*nrels_mag > 0) {
+			old = (old & (~0x10)) | (results[0] ? 0x10 : 0) ;
+
+			/* Enable fast measurements if any TACH's are DC */
+			old = (old & (~0x08)) | ((old&0xf0) ? 0x08 : 0) ;
+
+			lm85_write_value(client, ADM1027_REG_CONFIG3, old);
+			data->tach_mode = old ;
+		}
+		up(&data->update_lock);
+	}
+}
+
+void lm85_pwm_config(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int nr = ctl_name - LM85_SYSCTL_PWM_CFG1 ;
+
+	/* Spinup, min PWM, PWM Frequency, min below limit, Invert */
+
+	if (nr < 0 || nr > 2)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+
+		results[0] = SPINUP_FROM_REG(data->autofan[nr].config);
+		results[1] = PWM_FROM_REG(data->autofan[nr].min_pwm)*10;
+		results[2] = FREQ_FROM_REG(data->autofan[nr].freq);
+		results[3] = data->autofan[nr].min_off ? 10 : 0 ;
+		results[4] = (data->autofan[nr].config & 0x10) ? 10 : 0 ;
+		*nrels_mag = 5;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		int  old_config ;
+
+		down(&data->update_lock);
+		old_config = data->autofan[nr].config ;
+		if (*nrels_mag > 4) {
+			old_config = (old_config & (~0x10)) | (results[4]?0x10:0) ;
+		}
+		if (*nrels_mag > 3) {
+			data->autofan[nr].min_off = results[3] != 0 ;
+			lm85_write_value(client, LM85_REG_AFAN_SPIKE1,
+				data->smooth[0]
+				| data->syncpwm3
+				| (data->autofan[0].min_off ? 0x20 : 0)
+				| (data->autofan[1].min_off ? 0x40 : 0)
+				| (data->autofan[2].min_off ? 0x80 : 0)
+			);
+		}
+		if (*nrels_mag > 2) {
+			data->autofan[nr].freq = FREQ_TO_REG(results[2]) ;
+			lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
+			    (data->zone[nr].range << 4)
+			    | data->autofan[nr].freq
+			);
+		}
+		if (*nrels_mag > 1) {
+			data->autofan[nr].min_pwm = PWM_TO_REG((results[1]+5)/10);
+			lm85_write_value(client, LM85_REG_AFAN_MINPWM(nr),
+					data->autofan[nr].min_pwm
+			);
+		}
+		if (*nrels_mag > 0) {
+			old_config = (old_config & (~0x07)) | SPINUP_TO_REG(results[0]) ;
+			lm85_write_value(client, LM85_REG_AFAN_CONFIG(nr), old_config);
+			data->autofan[nr].config = old_config ;
+		}
+		up(&data->update_lock);
+	}
+}
+
+void lm85_smooth(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int nr = ctl_name - LM85_SYSCTL_SMOOTH1 ;
+
+	if (nr < 0 || nr > 2)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		results[0] = SMOOTH_FROM_REG(data->smooth[nr]);
+		*nrels_mag = 1;
+
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if( *nrels_mag > 0 ) {
+			data->smooth[nr] = SMOOTH_TO_REG(results[0]);
+		}
+		if( nr == 0 ) {
+		    lm85_write_value(client, LM85_REG_AFAN_SPIKE1,
+			data->smooth[0]
+			| data->syncpwm3
+			| (data->autofan[0].min_off ? 0x20 : 0)
+			| (data->autofan[1].min_off ? 0x40 : 0)
+			| (data->autofan[2].min_off ? 0x80 : 0)
+		    );
+		} else {
+		    lm85_write_value(client, LM85_REG_AFAN_SPIKE2,
+			(data->smooth[1] << 4) | data->smooth[2]);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void lm85_zone(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int nr = ctl_name - LM85_SYSCTL_ZONE1 ;
+
+	/* Limit, Hysteresis (neg), Range, Critical */
+
+	if (nr < 0 || nr > 2)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+
+		results[0] = TEMP_FROM_REG(data->zone[nr].limit) / 10;
+		results[1] = HYST_FROM_REG(data->zone[nr].hyst);
+		results[2] = RANGE_FROM_REG(data->zone[nr].range);
+		results[3] = TEMP_FROM_REG(data->zone[nr].critical) / 10;
+		*nrels_mag = 4;
+
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 3) {
+			data->zone[nr].critical = TEMP_TO_REG(results[3]*10);
+			lm85_write_value(client, LM85_REG_AFAN_CRITICAL(nr),
+				data->zone[nr].critical );
+		}
+		if (*nrels_mag > 2) {
+			data->zone[nr].range = RANGE_TO_REG(results[2]);
+			lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
+			    (data->zone[nr].range << 4)
+			    | data->autofan[nr].freq
+			);
+		}
+		if (*nrels_mag > 1) {
+			data->zone[nr].hyst = HYST_TO_REG(results[1]);
+			if( nr == 0 || nr == 1 ) {
+			    lm85_write_value(client, LM85_REG_AFAN_HYST1,
+				(data->zone[0].hyst << 4)
+				| data->zone[1].hyst
+			    );
+			} else {
+			    lm85_write_value(client, LM85_REG_AFAN_HYST2,
+				(data->zone[2].hyst << 4)
+			    );
+			}
+		}
+		if (*nrels_mag > 0) {
+			data->zone[nr].limit = TEMP_TO_REG(results[0]*10);
+			lm85_write_value(client, LM85_REG_AFAN_LIMIT(nr),
+			    data->zone[nr].limit
+			);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void lm85_pwm_zone(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int nr = ctl_name - LM85_SYSCTL_PWM_ZONE1 ;
+
+	if (nr < 0 || nr > 2)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		results[0] = ZONE_FROM_REG(data->autofan[nr].config);
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 0) {
+			data->autofan[nr].config =
+			    (data->autofan[nr].config & (~0xe0))
+			    | ZONE_TO_REG(results[0]) ;
+			lm85_write_value(client, LM85_REG_AFAN_CONFIG(nr),
+			    data->autofan[nr].config);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void adm1027_temp_offset(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int nr = ctl_name - ADM1027_SYSCTL_TEMP_OFFSET1 ;
+
+	if (nr < 0 || nr > 2)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		switch( data->type ) {
+		case adm1027 :
+		default :
+			results[0] = TEMP_FROM_REG(data->temp_offset[nr]);
+			break ;
+		case adt7463 :
+			results[0] = TEMPEXT_FROM_REG(0,data->temp_offset[nr]);
+			break ;
+		}
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 0) {
+			switch( data->type ) {
+			case adm1027 :
+			default :
+			    data->temp_offset[nr] = TEMP_TO_REG(results[0]);
+			    break ;
+			case adt7463 :
+			    data->temp_offset[nr] = EXTTEMP_TO_REG(results[0]);
+			    break ;
+			};
+			lm85_write_value(client, ADM1027_REG_TEMP_OFFSET(nr),
+			    data->temp_offset[nr]);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void adm1027_fan_ppr(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int old ;
+
+	if (ctl_name != ADM1027_SYSCTL_FAN_PPR)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		results[0] = PPR_FROM_REG(data->fan_ppr,0);
+		results[1] = PPR_FROM_REG(data->fan_ppr,1);
+		results[2] = PPR_FROM_REG(data->fan_ppr,2);
+		results[3] = PPR_FROM_REG(data->fan_ppr,3);
+		*nrels_mag = 4;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		old = data->fan_ppr ;
+		if (*nrels_mag > 3) {
+			old = (old & ~PPR_MASK(3)) | PPR_TO_REG(results[3],3);
+		};
+		if (*nrels_mag > 2) {
+			old = (old & ~PPR_MASK(2)) | PPR_TO_REG(results[2],2);
+		};
+		if (*nrels_mag > 1) {
+			old = (old & ~PPR_MASK(1)) | PPR_TO_REG(results[1],1);
+		};
+		if (*nrels_mag > 0) {
+			old = (old & ~PPR_MASK(0)) | PPR_TO_REG(results[0],0);
+			lm85_write_value(client, ADM1027_REG_FAN_PPR, old);
+			data->fan_ppr = old ;
+		}
+		up(&data->update_lock);
+	}
+}
+
+void adm1027_alarm_mask(struct i2c_client *client, int operation,
+		int ctl_name, int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+
+	if( ctl_name != ADM1027_SYSCTL_ALARM_MASK )
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		results[0] = INTMASK_FROM_REG(data->alarm_mask);
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 0) {
+			data->alarm_mask = INTMASK_TO_REG(results[0]);
+			lm85_write_value(client, ADM1027_REG_INTMASK,
+			    data->alarm_mask);
+		}
+		up(&data->update_lock);
+	}
+}
+
+void adt7463_tmin_ctl(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int nr = ctl_name - ADT7463_SYSCTL_TMIN_CTL1 ;
+	u16 old ;
+
+	if (nr < 0 || nr > 2)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		old = data->tmin_ctl ;
+		results[0] = (old & ( 0x2000 << nr )) != 0 ;
+		results[1] = (old >> (nr*3)) & 0x07  ;
+		results[2] = (old & ( 0x0400 << nr )) != 0 ;
+		results[3] = OPPOINT_FROM_REG(data->oppoint[nr]);
+		*nrels_mag = 4;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		old = data->tmin_ctl ;
+		if (*nrels_mag > 3) {
+			data->oppoint[nr] = OPPOINT_TO_REG(results[3]);
+			lm85_write_value(client, ADT7463_REG_OPPOINT(nr),
+			    data->oppoint[nr]);
+		};
+		if (*nrels_mag > 2) {
+			if( results[2] ) {
+				old |= (0x0400 << nr) ;
+			} else {
+				old &= ~(0x0400 << nr) ;
+			}
+		};
+		if (*nrels_mag > 1) {
+			old &= ~(0x07 << (nr*3)) ;
+			old |= (results[1] & 0x07) << (nr*3) ;
+		};
+		if (*nrels_mag > 0) {
+			if( results[0] ) {
+				old |= 0x2000 << nr ;
+			} else {
+				old &= ~(0x2000 << nr) ;
+			}
+			lm85_write_value(client, ADT7463_REG_TMIN_CTL, old);
+			data->tmin_ctl = old ;
+		}
+		up(&data->update_lock);
+	}
+}
+
+void adt7463_therm_signal(struct i2c_client *client, int operation,
+		int ctl_name, int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int counts ;
+
+	if (ctl_name != ADT7463_SYSCTL_THERM_SIGNAL)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		/* Don't call update_client here because
+		 *   ADT7463_REG_THERM has to be read every
+		 *   5 seconds to prevent lost counts
+		 */
+		down(&data->update_lock);
+		counts = lm85_read_value(client, ADT7463_REG_THERM) & 0xff;
+		if( data->therm_total < LONG_MAX - 256 ) {
+		    data->therm_total += counts ;
+		}
+		if( counts >= 255 ) {
+		    ++data->therm_ovfl ;
+		}
+		up(&data->update_lock);
+
+		results[0] = data->therm_limit ;
+		results[1] = data->therm_total ;
+		results[2] = data->therm_ovfl ;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		/* therm_total and therm_ovfl are read only */
+		if (*nrels_mag > 0) {
+			data->therm_limit = SENSORS_LIMIT(results[0],0,255);
+			lm85_write_value(client, ADT7463_REG_THERM_LIMIT,
+			    data->therm_limit);
+		};
+		up(&data->update_lock);
+	}
+}
+
+
+void emc6d100_in(struct i2c_client *client, int operation, int ctl_name,
+	     int *nrels_mag, long *results)
+{
+	struct lm85_data *data = client->data;
+	int nr = ctl_name - EMC6D100_SYSCTL_IN5 +5;
+
+	if (nr < 5 || nr > 7)
+		return ;  /* ERROR */
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 3;  /* 1.000 */
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm85_update_client(client);
+		results[0] = INS_FROM_REG(nr,data->in_min[nr]);
+		results[1] = INS_FROM_REG(nr,data->in_max[nr]);
+		results[2] = INS_FROM_REG(nr,data->in[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		down(&data->update_lock);
+		if (*nrels_mag > 1) {
+			data->in_max[nr] = INS_TO_REG(nr,results[1]);
+			lm85_write_value(client, EMC6D100_REG_IN_MAX(nr),
+					 data->in_max[nr]);
+		}
+		if (*nrels_mag > 0) {
+			data->in_min[nr] = INS_TO_REG(nr,results[0]);
+			lm85_write_value(client, EMC6D100_REG_IN_MIN(nr),
+					 data->in_min[nr]);
+		}
+		up(&data->update_lock);
+	}
+}
+
+
+static int __init sm_lm85_init(void)
+{
+	printk("lm85: Version %s (%s)\n", LM_VERSION, LM_DATE);
+	printk("lm85: See http://www.penguincomputing.com/lm_sensors for more info.\n" );
+	return i2c_add_driver(&lm85_driver);
+}
+
+static void __exit sm_lm85_exit(void)
+{
+	i2c_del_driver(&lm85_driver);
+}
+
+/* Thanks to Richard Barrington for adding the LM85 to sensors-detect.
+ * Thanks to Margit Schubert-While <margitsw@t-online.de> for help with
+ *     post 2.7.0 CVS changes
+ */
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Philip Pokorny <ppokorny@penguincomputing.com");
+MODULE_DESCRIPTION("LM85-B, LM85-C driver");
+
+module_init(sm_lm85_init);
+module_exit(sm_lm85_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/lm87.c linux-2.4.27-leo/drivers/sensors/lm87.c
--- linux-2.4.27/drivers/sensors/lm87.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/lm87.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,988 @@
+/*
+    LM87.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 2000  Frodo Looijaard <frodol@dds.nl>
+                        Philip Edelbrock <phil@netroedge.com>
+			Stephen Rousset <stephen.rousset@rocketlogix.com>
+			Dan Eaton <dan.eaton@rocketlogix.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or 
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+#include <linux/sensors_vid.h>
+
+/* Chip configuration settings.  These should be set to reflect the
+HARDWARE configuration of your chip.  By default (read: when all of
+these are left commented out), this driver assumes that the
+configuration is the same as National's defaults for the Channel Mode
+register.
+
+Set to '1' the appropriate defines, as nessesary:
+
+ - External temp sensors 2 (possible second CPU temp)
+   This will disable the 2.5V and Vccp2 readings.
+   Ironically, National decided that you can read the
+   temperature of a second CPU or it's core voltage,
+   but not both!  Comment out if FAULT is reported.  */
+
+/* #define LM87_EXT2 1 */
+
+/* Aux analog input. When enabled, the Fan 1 reading 
+   will be disabled */
+
+/* #define LM87_AIN1 1 */
+
+/* Aux analog input 2. When enabled, the Fan 2 reading 
+   will be disabled */
+
+/* #define LM87_AIN2 1 */
+
+/* Internal Vcc is 5V instead of 3.3V */
+
+/* #define LM87_5V_VCC 1 */
+
+/* That's the end of the hardware config defines.  I would have made
+   them insmod params, but it would be too much work. ;') */
+
+
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x2c, 0x2e, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(lm87);
+
+/* The following is the calculation for the register offset
+ * for the monitored items minimum and maximum locations.
+ */
+#define LM87_REG_IN_MAX(nr) (0x2b + ((nr) * 2))
+#define LM87_REG_IN_MIN(nr) (0x2c + ((nr) * 2))
+#define LM87_REG_IN(nr) (0x20 + (nr))
+
+/* Initial limits */
+
+/*
+ * LM87 register definition
+ * 
+ */
+
+      /* The LM87 registers */
+#define LM87_INT_TEMP_HI_LIMIT_LOCKABLE  0x13
+#define LM87_EXT_TEMP_HI_LIMIT_LOCKABLE  0x14
+#define LM87_REG_TEST                    0x15
+#define LM87_REG_CHANNEL_MODE            0x16
+#define LM87_REG_INT_TEMP_HI_LIMIT       0x17
+#define LM87_REG_EXT_TEMP_HI_LIMIT       0x18
+#define LM87_REG_ANALOG_OUT              0x19
+
+      /* These are all read-only */
+#define LM87_REG_2_5V_EXT_TEMP_2         0x20
+#define LM87_REG_VCCP1                   0x21
+#define LM87_REG_3_3V                    0x22  
+#define LM87_REG_5V                      0x23
+#define LM87_REG_12V                     0x24
+#define LM87_REG_VCCP2                   0x25
+#define LM87_REG_EXT_TEMP_1              0x26
+#define LM87_REG_INT_TEMP                0x27  /* LM87 temp. */
+#define LM87_REG_FAN1_AIN1               0x28
+#define LM87_REG_FAN2_AIN2               0x29
+
+/* These are read/write */
+#define LM87_REG_AIN1_LOW                0x1A
+#define LM87_REG_AIN2_LOW                0x1B
+#define LM87_REG_2_5V_EXT_TEMP_2_HIGH    0x2B  
+#define LM87_REG_2_5V_EXT_TEMP_2_LOW     0x2C  
+#define LM87_REG_VCCP1_HIGH              0x2D  
+#define LM87_REG_VCCP1_LOW               0x2E  
+#define LM87_REG_3_3V_HIGH               0x2F
+#define LM87_REG_3_3V_LOW                0x30
+#define LM87_REG_5V_HIGH                 0x31
+#define LM87_REG_5V_LOW                  0x32
+#define LM87_REG_12V_HIGH                0x33
+#define LM87_REG_12V_LOW                 0x34
+#define LM87_REG_VCCP2_HIGH              0x35
+#define LM87_REG_VCCP2_LOW               0x36
+#define LM87_REG_EXT_TEMP_1_HIGH         0x37    
+#define LM87_REG_EXT_TEMP_1_LOW          0x38  
+#define LM87_REG_INT_TEMP_HIGH           0x39  
+#define LM87_REG_INT_TEMP_LOW            0x3A  
+#define LM87_REG_FAN1_AIN1_LIMIT         0x3B
+#define LM87_REG_FAN2_AIN2_LIMIT         0x3C
+#define LM87_REG_COMPANY_ID              0x3E 
+#define LM87_REG_DIE_REV                 0x3F
+
+#define LM87_REG_CONFIG                  0x40
+#define LM87_REG_INT1_STAT               0x41
+#define LM87_REG_INT2_STAT               0x42
+#define LM87_REG_INT1_MASK               0x43
+#define LM87_REG_INT2_MASK               0x44
+#define LM87_REG_CHASSIS_CLEAR           0x46
+#define LM87_REG_VID_FAN_DIV             0x47
+#define LM87_REG_VID4                    0x49
+#define LM87_REG_CONFIG_2                0x4A
+#define LM87_REG_INTRPT_STATUS_1_MIRROR  0x4C
+#define LM87_REG_INTRPT_STATUS_2_MIRROR  0x4D
+#define LM87_REG_SMBALERT_NUM_ENABLE     0x80
+
+
+
+/* Conversions. Rounding and limit checking is only done on the TO_REG
+   variants. Note that you should be a bit careful with which arguments
+   these macros are called: arguments may be evaluated more than once.
+   Fixing this is just not worth it. */
+
+#define IN_TO_REG(val,nr) (SENSORS_LIMIT(((val) & 0xff),0,255))
+#define IN_FROM_REG(val,nr) (val)
+
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+	if (rpm == 0)
+		return 255;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1,
+			     254);
+}
+
+#define FAN_FROM_REG(val,div) ((val)==0?-1:\
+                               (val)==255?0:1350000/((div)*(val)))
+
+#define TEMP_FROM_REG(temp)  (temp * 10)
+
+#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10)
+
+#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT(((val)<0?(((val)-5)/10):\
+                                                      ((val)+5)/10),0,255)
+#if 0
+#define TEMP_FROM_REG(temp) \
+   ((temp)<256?((((temp)&0x1fe) >> 1) * 10)      + ((temp) & 1) * 5:  \
+               ((((temp)&0x1fe) >> 1) -255) * 10 - ((temp) & 1) * 5)  \
+
+#define TEMP_LIMIT_FROM_REG(val) (val)
+
+#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT((val),0,255)
+#endif
+
+
+#define ALARMS_FROM_REG(val) (val)
+
+#define DIV_FROM_REG(val) (1 << (val))
+#define DIV_TO_REG(val) ((val)==1?0:((val)==8?3:((val)==4?2:1)))
+
+/* For each registered LM87, we need to keep some data in memory. That
+   data is pointed to by LM87_list[NR]->data. The structure itself is
+   dynamically allocated, at the same time when a new LM87 client is
+   allocated. */
+struct lm87_data {
+	struct i2c_client client;
+	int sysctl_id;
+	enum chips type;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8  in[6];		/* Scaled Register value */
+	u8  in_max[6];		/* Scaled Register value */
+	u8  in_min[6];		/* Scaled Register value */
+	u8  ain1;		/* Register value */
+	u8  ain1_min;		/* Register value */
+	u8  ain1_max;		/* Register value */
+	u8  ain2;		/* Register value */
+	u8  ain2_min;		/* Register value */
+	u8  ain2_max;		/* Register value */
+	u8  fan;		/* Register value */
+	u8  fan_min;		/* Register value */
+	u8  fan_div;		/* Register encoding, shifted right */
+	u8  fan2;		/* Register value */
+	u8  fan2_min;		/* Register value */
+	u8  fan2_div;		/* Register encoding, shifted right */
+	int ext2_temp;		/* Temp, shifted right */
+	int ext_temp;           /* Temp, shifted right */
+	int int_temp;		/* Temp, shifted right */
+	u8  ext_temp_max;       /* Register value */
+	u8  ext_temp_min;       /* Register value */
+	u8  ext2_temp_max; 	/* Register value */
+	u8  ext2_temp_min;	/* Register value */
+	u8  int_temp_max;       /* Register value */
+	u8  int_temp_min;	/* Register value */
+	u16 alarms;		/* Register encoding, combined */
+	u8  analog_out;		/* Register value */
+	u8  vid;		/* Register value combined */
+	u8  vrm;		/* VRM version * 10 */
+};
+
+static int lm87_attach_adapter(struct i2c_adapter *adapter);
+static int lm87_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int lm87_detach_client(struct i2c_client *client);
+
+static int lm87_read_value(struct i2c_client *client, u8 register);
+static int lm87_write_value(struct i2c_client *client, u8 register,
+			       u8 value);
+static void lm87_update_client(struct i2c_client *client);
+static void lm87_init_client(struct i2c_client *client);
+
+
+static void lm87_in(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+#if defined (LM87_AIN1) || defined (LM87_AIN2)
+static void lm87_ain(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+#endif
+static void lm87_fan(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm87_temp(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void lm87_alarms(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+static void lm87_fan_div(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void lm87_analog_out(struct i2c_client *client, int operation,
+			       int ctl_name, int *nrels_mag,
+			       long *results);
+static void lm87_vid(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void lm87_vrm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+
+static int lm87_id = 0;
+
+static struct i2c_driver LM87_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "LM87 sensor driver",
+	.id		= I2C_DRIVERID_LM87,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= lm87_attach_adapter,
+	.detach_client	= lm87_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+#define LM87_SYSCTL_IN0        1000 /* Volts * 100 */
+#define LM87_SYSCTL_IN1        1001
+#define LM87_SYSCTL_IN2        1002
+#define LM87_SYSCTL_IN3        1003
+#define LM87_SYSCTL_IN4        1004
+#define LM87_SYSCTL_IN5        1005
+#define LM87_SYSCTL_AIN1       1006
+#define LM87_SYSCTL_AIN2       1007
+#define LM87_SYSCTL_FAN1       1102
+#define LM87_SYSCTL_FAN2       1103
+#define LM87_SYSCTL_TEMP1  1250 /* Degrees Celcius * 100 */
+#define LM87_SYSCTL_TEMP2   1251 /* Degrees Celcius * 100 */
+#define LM87_SYSCTL_TEMP3   1252 /* Degrees Celcius * 100 */
+#define LM87_SYSCTL_FAN_DIV    2000 /* 1, 2, 4 or 8 */
+#define LM87_SYSCTL_ALARMS     2001 /* bitvector */
+#define LM87_SYSCTL_ANALOG_OUT 2002
+#define LM87_SYSCTL_VID        2003
+#define LM87_SYSCTL_VRM        2004
+
+#define LM87_ALARM_IN0          0x0001
+#define LM87_ALARM_IN1          0x0002
+#define LM87_ALARM_IN2          0x0004
+#define LM87_ALARM_IN3          0x0008
+#define LM87_ALARM_TEMP1        0x0010
+#define LM87_ALARM_TEMP2        0x0020
+#define LM87_ALARM_TEMP3        0x0020 /* same?? */
+#define LM87_ALARM_FAN1         0x0040
+#define LM87_ALARM_FAN2         0x0080
+#define LM87_ALARM_IN4          0x0100
+#define LM87_ALARM_IN5          0x0200
+#define LM87_ALARM_RESERVED1    0x0400
+#define LM87_ALARM_RESERVED2    0x0800
+#define LM87_ALARM_CHAS         0x1000
+#define LM87_ALARM_THERM_SIG    0x2000
+#define LM87_ALARM_TEMP2_FAULT  0x4000
+#define LM87_ALARM_TEMP3_FAULT 0x08000
+
+/* -- SENSORS SYSCTL END -- */
+
+/* The /proc/sys entries */
+/* These files are created for each detected LM87. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized 
+   when a new copy is allocated. */
+
+static ctl_table LM87_dir_table_template[] = {
+#ifdef LM87_AIN1
+	{LM87_SYSCTL_AIN1, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_ain},
+#endif
+#ifdef LM87_AIN2
+	{LM87_SYSCTL_AIN2, "in7", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_ain},
+#endif
+#ifndef LM87_EXT2
+	{LM87_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_in},
+	{LM87_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_in},
+#endif
+	{LM87_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_in},
+	{LM87_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_in},
+	{LM87_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_in},
+	{LM87_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_in},
+#ifndef LM87_AIN1
+	{LM87_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_fan},
+	{LM87_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_fan_div},
+#define LM87_FANDIV_FLAG
+#endif
+#ifndef LM87_AIN2
+	{LM87_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_fan},
+#ifndef LM87_FANDIV_FLAG
+	{LM87_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_fan_div},
+#endif /* LM87_FANDIV_FLAG */
+#endif /* LM87_AIN2 */
+#ifdef LM87_EXT2
+        {LM87_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_temp},
+#endif
+	{LM87_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_temp},
+	{LM87_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_temp},
+	{LM87_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_alarms},
+	{LM87_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_analog_out},
+	{LM87_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+	  &i2c_sysctl_real, NULL, &lm87_vid},
+	{LM87_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm87_vrm},
+	{0}
+};
+
+static int lm87_attach_adapter(struct i2c_adapter *adapter)
+{
+	int error;
+	struct i2c_client_address_data  lm87_client_data;
+
+	lm87_client_data.normal_i2c       = addr_data.normal_i2c;
+	lm87_client_data.normal_i2c_range = addr_data.normal_i2c_range;
+	lm87_client_data.probe            = addr_data.probe;
+	lm87_client_data.probe_range      = addr_data.probe_range;
+	lm87_client_data.ignore           = addr_data.ignore;
+	lm87_client_data.ignore_range     = addr_data.ignore_range;
+	lm87_client_data.force            = addr_data.forces->force;
+
+	error = i2c_probe(adapter, &lm87_client_data, lm87_detect);
+	i2c_detect(adapter, &addr_data, lm87_detect);
+
+        return error;
+}
+
+static int lm87_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct lm87_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		goto ERROR0;
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access LM87_{read,write}_value. */
+
+	if (!(data = kmalloc(sizeof(struct lm87_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &LM87_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+
+	if (kind < 0) {
+		if (((lm87_read_value(new_client, LM87_REG_CONFIG) & 0x80)
+		     != 0x00) ||
+		    (lm87_read_value(new_client, LM87_REG_COMPANY_ID) != 0x02))
+	       goto ERROR1;
+	}
+
+	/* Fill in the remaining client fields and put into the global list */
+        type_name = "lm87";
+        client_name = "LM87 chip";
+	strcpy(new_client->name, client_name);
+	data->type = kind;
+
+	new_client->id = lm87_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client,
+					type_name,
+					LM87_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the LM87 chip */
+	lm87_init_client(new_client);
+	return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+      ERROR1:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int lm87_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct lm87_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("lm87.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+static int lm87_read_value(struct i2c_client *client, u8 reg)
+{
+	return 0xFF & i2c_smbus_read_byte_data(client, reg);
+}
+
+static int lm87_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* Called when we have found a new LM87. It should set limits, etc. */
+static void lm87_init_client(struct i2c_client *client)
+{
+	struct lm87_data *data = client->data;
+
+	/* Reset all except Watchdog values and last conversion values
+	   This sets fan-divs to 2, among others. This makes most other
+	   initializations unnecessary */
+	lm87_write_value(client, LM87_REG_CONFIG, 0x80);
+
+        /* Setup Channel Mode register for configuration of monitoring 
+	 * Default is 00000000b
+	 * 	bit 0 - Configures Fan 1/AIN 1 input (1 = AIN)
+	 * 	bit 1 - Configures Fan 2/AIN 2 input (1 = AIN)
+	 * 	bit 2 - Configures 2.5V&Vccp2/D2 input (1 = 2nd Therm.) 
+	 * 	bit 3 - Configures Vcc for 5V/3.3V reading (0 = 3.3V)
+	 * 	bit 4 - Configures IRQ0 Enable if = 1
+	 * 	bit 5 - Configures IRQ1 Enable if = 1
+	 * 	bit 6 - Configures IRQ2 Enable if = 1
+	 * 	bit 7 - Configures VID/IRQ input as interrupts if = 1
+	 */
+
+/* I know, not clean, but it works. :'p */
+	lm87_write_value(client, LM87_REG_CHANNEL_MODE,
+#ifdef LM87_AIN1
+ 0x01
+#else
+0
+#endif
+ | 
+#ifdef LM87_AIN2
+ 0x02
+#else
+0
+#endif
+ |
+#ifdef LM87_EXT2
+ 0x04
+#else
+0
+#endif
+ | 
+#ifdef LM87_5V_VCC
+0x08
+#else   
+0
+#endif
+	);
+
+	data->vrm = DEFAULT_VRM;
+
+	/* Start monitoring */
+	lm87_write_value(client, LM87_REG_CONFIG, 0x01);
+}
+
+static void lm87_update_client(struct i2c_client *client)
+{
+	struct lm87_data *data = client->data;
+	int i;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ) ||  /* 1 sec cache */
+            (jiffies < data->last_updated)      || 
+             !data->valid) {
+		for (i = 0; i <= 5; i++) {  
+		 data->in[i] = 
+		    lm87_read_value(client,LM87_REG_IN(i));
+		 data->in_min[i] = 
+		    lm87_read_value(client,LM87_REG_IN_MIN(i));
+		 data->in_max[i] = 
+		    lm87_read_value(client,LM87_REG_IN_MAX(i));
+		}
+		 data->ain1 = 
+		    lm87_read_value(client,LM87_REG_FAN1_AIN1);
+		 data->ain1_min =
+		    lm87_read_value(client,LM87_REG_AIN1_LOW);
+		 data->ain1_max =
+		    lm87_read_value(client,LM87_REG_FAN1_AIN1_LIMIT);
+		 data->ain2 = 
+		    lm87_read_value(client,LM87_REG_FAN2_AIN2);
+		 data->ain2_min =
+		    lm87_read_value(client,LM87_REG_AIN2_LOW);
+		 data->ain2_max =
+		    lm87_read_value(client,LM87_REG_FAN2_AIN2_LIMIT);
+
+		data->fan =
+		    lm87_read_value(client, LM87_REG_FAN1_AIN1);
+		data->fan_min =
+		    lm87_read_value(client, LM87_REG_FAN1_AIN1_LIMIT);
+		data->fan2 =
+		    lm87_read_value(client, LM87_REG_FAN2_AIN2);
+		data->fan2_min =
+		    lm87_read_value(client, LM87_REG_FAN2_AIN2_LIMIT);
+
+		data->ext2_temp =
+		    lm87_read_value(client, LM87_REG_2_5V_EXT_TEMP_2);
+		data->ext_temp =
+		    lm87_read_value(client, LM87_REG_EXT_TEMP_1);
+		data->int_temp =
+		    lm87_read_value(client, LM87_REG_INT_TEMP);
+
+		data->ext2_temp_max =
+		    lm87_read_value(client, LM87_REG_2_5V_EXT_TEMP_2_HIGH);
+		data->ext2_temp_min =
+		    lm87_read_value(client, LM87_REG_2_5V_EXT_TEMP_2_LOW);
+
+		data->ext_temp_max =
+		    lm87_read_value(client, LM87_REG_EXT_TEMP_1_HIGH);
+		data->ext_temp_min =
+		    lm87_read_value(client, LM87_REG_EXT_TEMP_1_LOW);
+
+		data->int_temp_max =
+		    lm87_read_value(client, LM87_REG_INT_TEMP_HIGH);
+		data->int_temp_min =
+		    lm87_read_value(client, LM87_REG_INT_TEMP_LOW);
+
+		i = lm87_read_value(client, LM87_REG_VID_FAN_DIV);
+		data->fan_div = (i >> 4) & 0x03;
+		data->fan2_div = (i >> 6) & 0x03;
+		data->vid = i & 0x0f;
+		data->vid |=
+		    (lm87_read_value(client, LM87_REG_VID4) & 0x01)
+		    << 4;
+		data->alarms =
+		    lm87_read_value(client, LM87_REG_INT1_STAT) +
+		    (lm87_read_value(client, LM87_REG_INT2_STAT) << 8);
+		data->analog_out =
+		    lm87_read_value(client, LM87_REG_ANALOG_OUT);
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+	up(&data->update_lock);
+}
+
+
+/* The next few functions are the call-back functions of the /proc/sys and
+   sysctl files. Which function is used is defined in the ctl_table in
+   the extra1 field.
+   Each function must return the magnitude (power of 10 to divide the date
+   with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must
+   put a maximum of *nrels elements in results reflecting the data of this
+   file, and set *nrels to the number it actually put in it, if operation==
+   SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from
+   results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE.
+   Note that on SENSORS_PROC_REAL_READ, I do not check whether results is
+   large enough (by checking the incoming value of *nrels). This is not very
+   good practice, but as long as you put less than about 5 values in results,
+   you can assume it is large enough. */
+void lm87_in(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	long scales[6] = { 250, 270, 
+#ifdef LM87_5V_VCC
+500,
+#else
+330,
+#endif
+		500, 1200, 270 };
+
+	struct lm87_data *data = client->data;
+	int nr = ctl_name - LM87_SYSCTL_IN0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm87_update_client(client);
+		results[0] =
+		    ((long)data->in_min[nr] * scales[nr]) / 192;
+		results[1] =
+		    ((long)data->in_max[nr] * scales[nr]) / 192;
+		results[2] =
+		    ((long)data->in[nr] * scales[nr]) / 192;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->in_min[nr] =
+			    (results[0] * 192) / scales[nr];
+			lm87_write_value(client, LM87_REG_IN_MIN(nr),
+					    data->in_min[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->in_max[nr] =
+			    (results[1] * 192) / scales[nr];
+			lm87_write_value(client, LM87_REG_IN_MAX(nr),
+					    data->in_max[nr]);
+		}
+	}
+}
+
+#if defined (LM87_AIN1) || defined (LM87_AIN2)
+void lm87_ain(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct lm87_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm87_update_client(client);
+		if (ctl_name == LM87_SYSCTL_AIN1) {
+		 results[0] = data->ain1_min;
+		 results[1] = data->ain1_max;
+		 results[2] = data->ain1;
+		} else {
+		 results[0] = data->ain2_min;
+		 results[1] = data->ain2_max;
+		 results[2] = data->ain2;
+		}
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+		 if (ctl_name == LM87_SYSCTL_AIN1) {
+			data->ain1_min = results[0];
+			lm87_write_value(client, LM87_REG_AIN1_LOW,
+					    data->ain1_min);
+		 } else {
+			data->ain2_min = results[0];
+			lm87_write_value(client, LM87_REG_AIN2_LOW,
+					    data->ain2_min);
+		 }
+		}
+		if (*nrels_mag >= 2) {
+		 if (ctl_name == LM87_SYSCTL_AIN1) {
+			data->ain1_max = results[1];
+			lm87_write_value(client, LM87_REG_FAN1_AIN1_LIMIT,
+					    data->ain1_max);
+		 } else {
+			data->ain2_max = results[1];
+			lm87_write_value(client, LM87_REG_FAN2_AIN2_LIMIT,
+					    data->ain2_max);
+		 }
+		}
+	}
+}
+#endif
+
+void lm87_fan(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct lm87_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm87_update_client(client);
+		if (ctl_name == LM87_SYSCTL_FAN1) {
+		 results[0] = FAN_FROM_REG(data->fan_min,
+					  DIV_FROM_REG(data->fan_div));
+		 results[1] = FAN_FROM_REG(data->fan, 
+		                         DIV_FROM_REG(data->fan_div));
+		} else {
+		 results[0] = FAN_FROM_REG(data->fan2_min,
+					  DIV_FROM_REG(data->fan2_div));
+		 results[1] = FAN_FROM_REG(data->fan2, 
+		                         DIV_FROM_REG(data->fan2_div));
+		}
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 0) {
+			if (ctl_name == LM87_SYSCTL_FAN1) {
+			 data->fan_min = FAN_TO_REG(results[0],
+						   DIV_FROM_REG
+						   (data->fan_div));
+			 lm87_write_value(client, LM87_REG_FAN1_AIN1_LIMIT,
+					    data->fan_min);
+			} else {
+			 data->fan2_min = FAN_TO_REG(results[0],
+						   DIV_FROM_REG
+						   (data->fan2_div));
+			 lm87_write_value(client, LM87_REG_FAN2_AIN2_LIMIT,
+					    data->fan2_min);
+			}
+		}
+	}
+}
+
+
+void lm87_temp(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct lm87_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) 
+	{
+	   lm87_update_client(client);
+
+	   /* find out which temp. is being requested */
+	   if (ctl_name == LM87_SYSCTL_TEMP3) 
+	   {
+		results[0] = TEMP_LIMIT_FROM_REG(data->ext2_temp_max);
+		results[1] = TEMP_LIMIT_FROM_REG(data->ext2_temp_min);
+		results[2] = TEMP_FROM_REG(data->ext2_temp);
+	   }
+	   else if(ctl_name == LM87_SYSCTL_TEMP2)
+	   {
+		results[0] = TEMP_LIMIT_FROM_REG(data->ext_temp_max);
+		results[1] = TEMP_LIMIT_FROM_REG(data->ext_temp_min);
+		results[2] = TEMP_FROM_REG(data->ext_temp);
+	   }
+	   else if(ctl_name == LM87_SYSCTL_TEMP1)
+	   {
+		results[0] = TEMP_LIMIT_FROM_REG(data->int_temp_max);
+		results[1] = TEMP_LIMIT_FROM_REG(data->int_temp_min);
+		results[2] = TEMP_FROM_REG(data->int_temp);
+	   }
+	   *nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+	           if (ctl_name == LM87_SYSCTL_TEMP3) {
+			data->ext2_temp_max = TEMP_LIMIT_TO_REG(results[0]);
+			lm87_write_value(client, LM87_REG_2_5V_EXT_TEMP_2_HIGH,
+					    data->ext2_temp_max);
+		   }
+		   if (ctl_name == LM87_SYSCTL_TEMP2) {
+			data->ext_temp_max = TEMP_LIMIT_TO_REG(results[0]);
+			lm87_write_value(client, LM87_REG_EXT_TEMP_1_HIGH,
+					    data->ext_temp_max);
+		   }
+		   if (ctl_name == LM87_SYSCTL_TEMP1) {
+			data->int_temp_max = TEMP_LIMIT_TO_REG(results[0]);
+			lm87_write_value(client, LM87_REG_INT_TEMP_HIGH,
+					    data->int_temp_max);
+	           }
+		}
+		if (*nrels_mag >= 2) {
+	           if (ctl_name == LM87_SYSCTL_TEMP3) {
+			data->ext2_temp_min = TEMP_LIMIT_TO_REG(results[1]);
+			lm87_write_value(client, LM87_REG_2_5V_EXT_TEMP_2_LOW,
+					    data->ext2_temp_min);
+		   }
+		   if (ctl_name == LM87_SYSCTL_TEMP2) {
+			data->ext_temp_min = TEMP_LIMIT_TO_REG(results[1]);
+			lm87_write_value(client, LM87_REG_EXT_TEMP_1_LOW,
+					    data->ext_temp_min);
+		   }
+		   if (ctl_name == LM87_SYSCTL_TEMP1) {
+			data->int_temp_min = TEMP_LIMIT_TO_REG(results[1]);
+			lm87_write_value(client, LM87_REG_INT_TEMP_LOW,
+					    data->int_temp_min);
+	           }
+		}
+	}
+}
+
+void lm87_alarms(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results)
+{
+	struct lm87_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm87_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->alarms);
+		*nrels_mag = 1;
+	}
+}
+
+void lm87_fan_div(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+/* This gets a little hairy depending on the hardware config */
+
+	struct lm87_data *data = client->data;
+	int old;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm87_update_client(client);
+#ifndef LM87_AIN1
+		results[0] = DIV_FROM_REG(data->fan_div);
+# ifndef LM87_AIN2
+		results[1] = DIV_FROM_REG(data->fan2_div);
+		*nrels_mag = 2;
+# else
+		*nrels_mag = 1;
+# endif
+#else /* Must be referring to fan 2 */
+		results[0] = DIV_FROM_REG(data->fan2_div);
+		*nrels_mag = 1;
+#endif
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		old = lm87_read_value(client, LM87_REG_VID_FAN_DIV);
+/* Note: it's OK to change fan2 div even if fan2 isn't enabled */
+#ifndef LM87_AIN1
+		if (*nrels_mag >= 2) {
+			data->fan2_div = DIV_TO_REG(results[1]);
+			old = (old & 0x3f) | (data->fan2_div << 6);
+		}
+		if (*nrels_mag >= 1) {
+			data->fan_div = DIV_TO_REG(results[0]);
+			old = (old & 0xcf) | (data->fan_div << 4);
+			lm87_write_value(client, LM87_REG_VID_FAN_DIV, old);
+		}
+#else /* Must be referring to fan 2 */
+		if (*nrels_mag >= 1) {
+			data->fan2_div = DIV_TO_REG(results[0]);
+			old = (old & 0xcf) | (data->fan2_div << 6);
+			lm87_write_value(client, LM87_REG_VID_FAN_DIV, old);
+		}
+#endif
+	}
+}
+
+void lm87_analog_out(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results)
+{
+	struct lm87_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm87_update_client(client);
+		results[0] = data->analog_out;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->analog_out = results[0];
+			lm87_write_value(client, LM87_REG_ANALOG_OUT,
+					    data->analog_out);
+		}
+	}
+}
+
+void lm87_vid(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct lm87_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 3;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		lm87_update_client(client);
+		results[0] = vid_from_reg(data->vid, data->vrm);
+		*nrels_mag = 1;
+	}
+}
+
+void lm87_vrm(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct lm87_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = data->vrm;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1)
+			data->vrm = results[0];
+	}
+}
+
+static int __init sm_lm87_init(void)
+{
+	printk("lm87.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&LM87_driver);
+}
+
+static void __exit sm_lm87_exit(void)
+{
+	i2c_del_driver(&LM87_driver);
+}
+
+
+
+MODULE_LICENSE("GPL");
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, "
+     "Mark Studebaker <mdsxyz123@yahoo.com>, and Stephen Rousset <stephen.rousset@rocketlogix.com>");
+
+MODULE_DESCRIPTION("LM87 driver");
+
+module_init(sm_lm87_init);
+module_exit(sm_lm87_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/lm90.c linux-2.4.27-leo/drivers/sensors/lm90.c
--- linux-2.4.27/drivers/sensors/lm90.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/lm90.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,764 @@
+/*
+ * lm90.c - Part of lm_sensors, Linux kernel modules for hardware
+ *          monitoring
+ * Copyright (C) 2003-2004  Jean Delvare <khali@linux-fr.org>
+ *
+ * Based on the lm83 driver. The LM90 is a sensor chip made by National
+ * Semiconductor. It reports up to two temperatures (its own plus up to
+ * one external one) with a 0.125 deg resolution (1 deg for local
+ * temperature) and a 3-4 deg accuracy. Complete datasheet can be
+ * obtained from National's website at:
+ *   http://www.national.com/pf/LM/LM90.html
+ *
+ * This driver also supports the LM89 and LM99, two other sensor chips
+ * made by National Semiconductor. Both have an increased remote
+ * temperature measurement accuracy (1 degree), and the LM99
+ * additionally shifts remote temperatures (measured and limits) by 16
+ * degrees, which allows for higher temperatures measurement. The
+ * driver doesn't handle it since it can be done easily in user-space.
+ * Complete datasheets can be obtained from National's website at:
+ *   http://www.national.com/pf/LM/LM89.html
+ *   http://www.national.com/pf/LM/LM99.html
+ * Note that there is no way to differenciate between both chips.
+ *
+ * This driver also supports the ADM1032, a sensor chip made by Analog
+ * Devices. That chip is similar to the LM90, with a few differences
+ * that are not handled by this driver. Complete datasheet can be
+ * obtained from Analog's website at:
+ *   http://products.analog.com/products/info.asp?product=ADM1032
+ *
+ * Since the LM90 was the first chipset supported by this driver, most
+ * comments will refer to this chipset, but are actually general and
+ * concern all supported chipsets, unless mentioned otherwise.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+#ifndef I2C_DRIVERID_LM90
+#define I2C_DRIVERID_LM90	1042
+#endif
+
+/*
+ * Addresses to scan
+ * Address is fully defined internally and cannot be changed.
+ * LM89, LM90, LM99 and ADM1032 have address 0x4c.
+ * LM89-1, and LM99-1 have address 0x4d.
+ */
+
+static unsigned short normal_i2c[] = { 0x4c, 0x4d, SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/*
+ * Insmod parameters
+ */
+
+SENSORS_INSMOD_3(lm90, adm1032, lm99);
+
+/*
+ * The LM90 registers
+ */
+
+#define LM90_REG_R_MAN_ID        0xFE
+#define LM90_REG_R_CHIP_ID       0xFF
+#define LM90_REG_R_CONFIG1       0x03
+#define LM90_REG_W_CONFIG1       0x09
+#define LM90_REG_R_CONFIG2       0xBF
+#define LM90_REG_W_CONFIG2       0xBF
+#define LM90_REG_R_CONVRATE      0x04
+#define LM90_REG_W_CONVRATE      0x0A
+#define LM90_REG_R_STATUS        0x02
+#define LM90_REG_R_LOCAL_TEMP    0x00
+#define LM90_REG_R_LOCAL_HIGH    0x05
+#define LM90_REG_W_LOCAL_HIGH    0x0B
+#define LM90_REG_R_LOCAL_LOW     0x06
+#define LM90_REG_W_LOCAL_LOW     0x0C
+#define LM90_REG_R_LOCAL_CRIT    0x20
+#define LM90_REG_W_LOCAL_CRIT    0x20
+#define LM90_REG_R_REMOTE_TEMPH  0x01
+#define LM90_REG_R_REMOTE_TEMPL  0x10
+#define LM90_REG_R_REMOTE_OFFSH  0x11
+#define LM90_REG_W_REMOTE_OFFSH  0x11
+#define LM90_REG_R_REMOTE_OFFSL  0x12
+#define LM90_REG_W_REMOTE_OFFSL  0x12
+#define LM90_REG_R_REMOTE_HIGHH  0x07
+#define LM90_REG_W_REMOTE_HIGHH  0x0D
+#define LM90_REG_R_REMOTE_HIGHL  0x13
+#define LM90_REG_W_REMOTE_HIGHL  0x13
+#define LM90_REG_R_REMOTE_LOWH   0x08
+#define LM90_REG_W_REMOTE_LOWH   0x0E
+#define LM90_REG_R_REMOTE_LOWL   0x14
+#define LM90_REG_W_REMOTE_LOWL   0x14
+#define LM90_REG_R_REMOTE_CRIT   0x19
+#define LM90_REG_W_REMOTE_CRIT   0x19
+#define LM90_REG_R_TCRIT_HYST    0x21
+#define LM90_REG_W_TCRIT_HYST    0x21
+
+/*
+ * Conversions and various macros
+ * The LM90 uses signed 8-bit values for the local temperatures,
+ * and signed 11-bit values for the remote temperatures (except
+ * T_CRIT). The 11-bit conversion formulas may not round negative
+ * numbers perfectly, but who cares?
+ */
+
+#define TEMP1_FROM_REG(val)  (val & 0x80 ? val-0x100 : val)
+#define TEMP1_TO_REG(val)    (val < 0 ? val+0x100 : val)
+#define TEMP2_FROM_REG(val)  (((val & 0x8000 ? val-0x10000 : val) \
+                             * 10 + 128) >> 8)
+#define TEMP2_TO_REG(val)    (((val << 8) / 10 + (val < 0 ? \
+                             0x10000 : 0)) & 0xFFE0)
+#define HYST_TO_REG(val)     (val < 0 ? 0 : val > 31 ? 31 : val)
+
+/*
+ * Functions declaration
+ */
+
+static int lm90_attach_adapter(struct i2c_adapter *adapter);
+static int lm90_detect(struct i2c_adapter *adapter, int address,
+	unsigned short flags, int kind);
+static void lm90_init_client(struct i2c_client *client);
+static int lm90_detach_client(struct i2c_client *client);
+static void lm90_local_temp(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results);
+static void lm90_remote_temp(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results);
+static void lm90_local_tcrit(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results);
+static void lm90_remote_tcrit(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results);
+static void lm90_local_hyst(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results);
+static void lm90_remote_hyst(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results);
+static void lm90_alarms(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results);
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static struct i2c_driver lm90_driver = {
+	.owner          = THIS_MODULE,
+	.name           = "LM90/ADM1032 sensor driver",
+	.id             = I2C_DRIVERID_LM90,
+	.flags          = I2C_DF_NOTIFY,
+	.attach_adapter = lm90_attach_adapter,
+	.detach_client  = lm90_detach_client
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct lm90_data
+{
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid; /* zero until following fields are valid */
+	unsigned long last_updated; /* in jiffies */
+
+	/* registers values */
+	u8 local_temp, local_high, local_low;
+	u16 remote_temp, remote_high, remote_low; /* combined */
+	u8 local_crit, remote_crit;
+	u8 hyst; /* linked to two sysctl files (hyst1 RW, hyst2 RO) */
+	u16 alarms; /* bitvector, combined */
+};
+
+/*
+ * Proc entries
+ * These files are created for each detected LM90.
+ */
+
+/* -- SENSORS SYSCTL START -- */
+
+#define LM90_SYSCTL_LOCAL_TEMP    1200
+#define LM90_SYSCTL_REMOTE_TEMP   1201
+#define LM90_SYSCTL_LOCAL_TCRIT   1204
+#define LM90_SYSCTL_REMOTE_TCRIT  1205
+#define LM90_SYSCTL_LOCAL_HYST    1207
+#define LM90_SYSCTL_REMOTE_HYST   1208
+#define LM90_SYSCTL_ALARMS        1210
+
+#define LM90_ALARM_LOCAL_HIGH     0x40
+#define LM90_ALARM_LOCAL_LOW      0x20
+#define LM90_ALARM_LOCAL_CRIT     0x01
+#define LM90_ALARM_REMOTE_HIGH    0x10
+#define LM90_ALARM_REMOTE_LOW     0x08
+#define LM90_ALARM_REMOTE_CRIT    0x02
+#define LM90_ALARM_REMOTE_OPEN    0x04
+
+/* -- SENSORS SYSCTL END -- */
+
+
+static ctl_table lm90_dir_table_template[] =
+{
+	{LM90_SYSCTL_LOCAL_TEMP, "temp1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm90_local_temp},
+	{LM90_SYSCTL_REMOTE_TEMP, "temp2", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm90_remote_temp},
+	{LM90_SYSCTL_LOCAL_TCRIT, "tcrit1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm90_local_tcrit},
+	{LM90_SYSCTL_REMOTE_TCRIT, "tcrit2", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm90_remote_tcrit},
+	{LM90_SYSCTL_LOCAL_HYST, "hyst1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm90_local_hyst},
+	{LM90_SYSCTL_REMOTE_HYST, "hyst2", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm90_remote_hyst},
+	{LM90_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &lm90_alarms},
+	{0}
+};
+
+/*
+ * Internal variables
+ */
+
+static int lm90_id = 0;
+
+/*
+ * Real code
+ */
+
+static int lm90_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, lm90_detect);
+}
+
+/*
+ * The following function does more than just detection. If detection
+ * succeeds, it also registers the new chip.
+ */
+static int lm90_detect(struct i2c_adapter *adapter, int address,
+	unsigned short flags, int kind)
+{
+	struct i2c_client *new_client;
+	struct lm90_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+	u8 reg_config1=0, reg_convrate=0;
+
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter))
+	{
+		printk("lm90.o: Called for an ISA bus adapter, aborting.\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+	{
+#ifdef DEBUG
+		printk("lm90.o: I2C bus doesn't support byte read mode, "
+		       "skipping.\n");
+#endif
+		return 0;
+	}
+
+	if (!(data = kmalloc(sizeof(struct lm90_data), GFP_KERNEL)))
+	{
+		printk("lm90.o: Out of memory in lm90_detect (new_client).\n");
+		return -ENOMEM;
+	}
+
+	/*
+	 * The common I2C client data is placed right before the
+	 * LM90-specific data. The LM90-specific data is pointed to by the
+	 * data field from the I2C client data.
+	 */
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &lm90_driver;
+	new_client->flags = 0;
+
+	/*
+	 * Now we do the remaining detection. A negative kind means that
+	 * the driver was loaded with no force parameter (default), so we
+	 * must both detect and identify the chip. A zero kind means that
+	 * the driver was loaded with the force parameter, the detection
+	 * step shall be skipped. A positive kind means that the driver
+	 * was loaded with the force parameter and a given kind of chip is
+	 * requested, so both the detection and the identification steps
+	 * are skipped.
+	 */
+
+	if (kind < 0) /* detection */
+	{
+		reg_config1 = i2c_smbus_read_byte_data(new_client,
+			LM90_REG_R_CONFIG1);
+		reg_convrate = i2c_smbus_read_byte_data(new_client,
+			LM90_REG_R_CONVRATE);
+
+		if ((reg_config1 & 0x2A) != 0x00
+		 || reg_convrate > 0x0A)
+		{
+#ifdef DEBUG
+			printk(KERN_DEBUG "lm90.o: Detection failed at 0x%02x.\n",
+				address);
+#endif
+			goto ERROR1;
+		}
+	}
+
+	if (kind <= 0) /* identification */
+	{
+		u8 man_id, chip_id;
+
+		man_id = i2c_smbus_read_byte_data(new_client,
+			LM90_REG_R_MAN_ID);
+		chip_id = i2c_smbus_read_byte_data(new_client,
+			LM90_REG_R_CHIP_ID);
+		
+		if (man_id == 0x01) /* National Semiconductor */
+		{
+			u8 reg_config2;
+
+			reg_config2 = i2c_smbus_read_byte_data(new_client,
+				LM90_REG_R_CONFIG2);
+
+			if (kind == 0 /* skip detection */
+			 || ((reg_config2 & 0xF8) == 0x00
+			  && reg_convrate <= 0x09))
+			{
+				if (address == 0x4C
+				 && (chip_id & 0xF0) == 0x20) /* LM90 */
+				{
+					kind = lm90;
+				}
+				else if ((chip_id & 0xF0) == 0x30) /* LM89/LM99 */
+				{
+					kind = lm99;
+				}
+			}
+		}
+		else if (man_id == 0x41) /* Analog Devices */
+		{
+			if (address == 0x4C
+			 && (chip_id & 0xF0) == 0x40 /* ADM1032 */
+			 && (kind == 0 /* skip detection */
+			  || (reg_config1 & 0x3F) == 0x00))
+			{
+				kind = adm1032;
+			}
+		}
+	}
+
+	if (kind <= 0) /* identification failed */
+	{
+		printk("lm90.o: Unsupported chip.\n");
+		goto ERROR1;
+	}
+
+	if (kind == lm90)
+	{
+		type_name = "lm90";
+		client_name = "LM90 chip";
+	}
+	else if (kind == adm1032)
+	{
+		type_name = "adm1032";
+		client_name = "ADM1032 chip";
+	}
+	else if (kind == lm99)
+	{
+		type_name = "lm99";
+		client_name = "LM99 chip";
+	}
+	else
+	{
+		printk("lm90.o: Unknown kind %d.\n", kind);
+		goto ERROR1;
+	}
+
+	/*
+	 * OK, we got a valid chip so we can fill in the remaining client
+	 * fields.
+	 */
+
+	strcpy(new_client->name, client_name);
+	new_client->id = lm90_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/*
+	 * Tell the I2C layer a new client has arrived.
+	 */
+
+	if ((err = i2c_attach_client(new_client)))
+	{
+#ifdef DEBUG
+		printk("lm90.o: Failed attaching client.\n");
+#endif
+		goto ERROR1;
+	}
+
+	/*
+	 * Register a new directory entry.
+	 */
+
+	if ((err = i2c_register_entry(new_client, type_name,
+	     lm90_dir_table_template)) < 0)
+	{
+#ifdef DEBUG
+		printk("lm90.o: Failed registering directory entry.\n");
+#endif
+		goto ERROR2;
+	}
+	data->sysctl_id = err;
+
+	/*
+	 * Initialize the LM90 chip.
+	 */
+
+	lm90_init_client(new_client);
+	return 0;
+
+	ERROR2:
+	i2c_detach_client(new_client);
+	ERROR1:
+	kfree(data);
+	return err;
+}
+
+static void lm90_init_client(struct i2c_client *client)
+{
+	u8 config;
+
+	/*
+	 * Start the conversions.
+	 */
+
+	i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE,
+		5); /* 2 Hz */
+	config = i2c_smbus_read_byte_data(client, LM90_REG_R_CONFIG1);
+	if (config & 0x40)
+		i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1,
+			config & 0xBF); /* run */
+}
+
+
+static int lm90_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct lm90_data *) (client->data))->sysctl_id);
+	if ((err = i2c_detach_client(client)))
+	{
+		printk("lm90.o: Client deregistration failed, client not "
+		       "detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+	return 0;
+}
+
+static void lm90_update_client(struct i2c_client *client)
+{
+	struct lm90_data *data = client->data;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ * 2) ||
+	    (jiffies < data->last_updated) || !data->valid)
+	{
+		u8 oldh, newh;
+#ifdef DEBUG
+		printk("lm90.o: Updating data.\n");
+#endif
+
+		data->local_temp =
+			i2c_smbus_read_byte_data(client, LM90_REG_R_LOCAL_TEMP);
+		data->local_high =
+			i2c_smbus_read_byte_data(client, LM90_REG_R_LOCAL_HIGH);
+		data->local_low =
+			i2c_smbus_read_byte_data(client, LM90_REG_R_LOCAL_LOW);
+		data->local_crit =
+			i2c_smbus_read_byte_data(client, LM90_REG_R_LOCAL_CRIT);
+
+		/*
+		 * There is a trick here. We have to read two registers to
+		 * have the remote sensor temperature, but we have to beware
+		 * a conversion could occur inbetween the readings. The
+		 * datasheet says we should either use the one-shot
+		 * conversion register, which we don't want to do (disables
+		 * hardware monitoring) or monitor the busy bit, which is
+		 * impossible (we can't read the values and monitor that bit
+		 * at the exact same time). So the solution used here is to
+		 * read the high byte once, then the low byte, then the high
+		 * byte again. If the new high byte matches the old one,
+		 * then we have a valid reading. Else we have to read the low
+		 * byte again, and now we believe we have a correct reading.
+		 */
+
+		oldh =
+			i2c_smbus_read_byte_data(client, LM90_REG_R_REMOTE_TEMPH);
+		data->remote_temp =
+			i2c_smbus_read_byte_data(client, LM90_REG_R_REMOTE_TEMPL);
+		newh =
+			i2c_smbus_read_byte_data(client, LM90_REG_R_REMOTE_TEMPH);
+		if (newh != oldh)
+		{
+			data->remote_temp =
+				i2c_smbus_read_byte_data(client, LM90_REG_R_REMOTE_TEMPL);
+#ifdef DEBUG
+			oldh = /* actually newer */
+				i2c_smbus_read_byte_data(client, LM90_REG_R_REMOTE_TEMPH);
+			if (newh != oldh)
+				printk("lm90.o: Remote temperature may be wrong.\n");
+#endif
+		}
+		data->remote_temp |= (newh << 8);
+		data->remote_high =
+			(i2c_smbus_read_byte_data(client, LM90_REG_R_REMOTE_HIGHH) << 8)
+			+ i2c_smbus_read_byte_data(client, LM90_REG_R_REMOTE_HIGHL);
+		data->remote_low =
+			(i2c_smbus_read_byte_data(client, LM90_REG_R_REMOTE_LOWH) << 8)
+			+ i2c_smbus_read_byte_data(client, LM90_REG_R_REMOTE_LOWL);
+		data->remote_crit =
+			i2c_smbus_read_byte_data(client, LM90_REG_R_REMOTE_CRIT);
+
+		data->hyst =
+			i2c_smbus_read_byte_data(client, LM90_REG_R_TCRIT_HYST);
+		data->alarms =
+			i2c_smbus_read_byte_data(client, LM90_REG_R_STATUS);
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+static void lm90_local_temp(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results)
+{
+	struct lm90_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0; /* magnitude */
+	else if (operation == SENSORS_PROC_REAL_READ)
+	{
+		lm90_update_client(client);
+		results[0] = TEMP1_FROM_REG(data->local_high);
+		results[1] = TEMP1_FROM_REG(data->local_low);
+		results[2] = TEMP1_FROM_REG(data->local_temp);
+		*nrels_mag = 3;
+	}
+	else if (operation == SENSORS_PROC_REAL_WRITE)
+	{
+		if (*nrels_mag >= 1)
+		{
+			data->local_high = TEMP1_TO_REG(results[0]);
+			i2c_smbus_write_byte_data(client, LM90_REG_W_LOCAL_HIGH,
+				data->local_high);
+		}
+		if (*nrels_mag >= 2)
+		{
+			data->local_low = TEMP1_TO_REG(results[1]);
+			i2c_smbus_write_byte_data(client, LM90_REG_W_LOCAL_LOW,
+				data->local_low);
+		}
+	}
+}
+
+static void lm90_remote_temp(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results)
+{
+	struct lm90_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1; /* magnitude */
+	else if (operation == SENSORS_PROC_REAL_READ)
+	{
+		lm90_update_client(client);
+		results[0] = TEMP2_FROM_REG(data->remote_high);
+		results[1] = TEMP2_FROM_REG(data->remote_low);
+		results[2] = TEMP2_FROM_REG(data->remote_temp);
+		*nrels_mag = 3;
+	}
+	else if (operation == SENSORS_PROC_REAL_WRITE)
+	{
+		if (*nrels_mag >= 1)
+		{
+			data->remote_high = TEMP2_TO_REG(results[0]);
+			i2c_smbus_write_byte_data(client, LM90_REG_W_REMOTE_HIGHH,
+				data->remote_high >> 8);
+			i2c_smbus_write_byte_data(client, LM90_REG_W_REMOTE_HIGHL,
+				data->remote_high & 0xFF);
+		}
+		if (*nrels_mag >= 2)
+		{
+			data->remote_low = TEMP2_TO_REG(results[1]);
+			i2c_smbus_write_byte_data(client, LM90_REG_W_REMOTE_LOWH,
+				data->remote_low >> 8);
+			i2c_smbus_write_byte_data(client, LM90_REG_W_REMOTE_LOWL,
+				data->remote_low & 0xFF);
+		}
+	}
+}
+
+static void lm90_local_tcrit(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results)
+{
+	struct lm90_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0; /* magnitude */
+	else if (operation == SENSORS_PROC_REAL_READ)
+	{
+		lm90_update_client(client);
+		results[0] = TEMP1_FROM_REG(data->local_crit);
+		*nrels_mag = 1;
+	}
+	else if (operation == SENSORS_PROC_REAL_WRITE)
+	{
+		if (*nrels_mag >= 1)
+		{
+			data->local_crit = TEMP1_TO_REG(results[0]);
+			i2c_smbus_write_byte_data(client, LM90_REG_W_LOCAL_CRIT,
+				data->local_crit);
+		}
+	}
+}
+
+static void lm90_remote_tcrit(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results)
+{
+	struct lm90_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0; /* magnitude */
+	else if (operation == SENSORS_PROC_REAL_READ)
+	{
+		lm90_update_client(client);
+		results[0] = TEMP1_FROM_REG(data->remote_crit);
+		*nrels_mag = 1;
+	}
+	else if (operation == SENSORS_PROC_REAL_WRITE)
+	{
+		if (*nrels_mag >= 1)
+		{
+			data->remote_crit = TEMP1_TO_REG(results[0]);
+			i2c_smbus_write_byte_data(client, LM90_REG_W_REMOTE_CRIT,
+				data->remote_crit);
+		}
+	}
+}
+
+/*
+ * One quick note about hysteresis. Internally, the hysteresis value
+ * is held in a single register by the LM90, as a relative value.
+ * This relative value applies to both the local critical temperature
+ * and the remote critical temperature. Since all temperatures exported
+ * through procfs have to be absolute, we have to do some conversions.
+ * The solution retained here is to export two absolute values, one for
+ * each critical temperature. In order not to confuse the users too
+ * much, only one file is writable. Would we fail to do so, users
+ * would probably attempt to write to both files, as if they were
+ * independant, and since they aren't, they wouldn't understand why
+ * setting one affects the other one (and would probably claim there's
+ * a bug in the driver).
+ */
+
+static void lm90_local_hyst(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results)
+{
+	struct lm90_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0; /* magnitude */
+	else if (operation == SENSORS_PROC_REAL_READ)
+	{
+		lm90_update_client(client);
+		results[0] = TEMP1_FROM_REG(data->local_crit) -
+			TEMP1_FROM_REG(data->hyst);
+		*nrels_mag = 1;
+	}
+	else if (operation == SENSORS_PROC_REAL_WRITE)
+	{
+		if (*nrels_mag >= 1)
+		{
+			data->hyst = HYST_TO_REG(data->local_crit - results[0]);
+			i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST,
+				data->hyst);
+		}
+	}
+}
+
+static void lm90_remote_hyst(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results)
+{
+	struct lm90_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0; /* magnitude */
+	else if (operation == SENSORS_PROC_REAL_READ)
+	{
+		lm90_update_client(client);
+		results[0] = TEMP1_FROM_REG(data->remote_crit) -
+			TEMP1_FROM_REG(data->hyst);
+		*nrels_mag = 1;
+	}
+}
+
+static void lm90_alarms(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results)
+{
+	struct lm90_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0; /* magnitude */
+	else if (operation == SENSORS_PROC_REAL_READ)
+	{
+		lm90_update_client(client);
+		results[0] = data->alarms;
+		*nrels_mag = 1;
+	}
+}
+
+static int __init sm_lm90_init(void)
+{
+	printk(KERN_INFO "lm90.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&lm90_driver);
+}
+
+static void __exit sm_lm90_exit(void)
+{
+	i2c_del_driver(&lm90_driver);
+}
+
+MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
+MODULE_DESCRIPTION("LM90/ADM1032 sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(sm_lm90_init);
+module_exit(sm_lm90_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/lm92.c linux-2.4.27-leo/drivers/sensors/lm92.c
--- linux-2.4.27/drivers/sensors/lm92.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/lm92.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,421 @@
+
+/*
+ * LM92 - Part of lm_sensors, Linux kernel modules for hardware
+ *        monitoring
+ *
+ * Author: Abraham van der Merwe <abraham@2d3d.co.za>
+ *
+ * Linux support for the National Semiconductor LM92 Temperature
+ * Sensor.
+ *
+ * Based on code from the lm-sensors project which is available
+ * at http://www.lm-sensors.nu/. lm87.c have been particularly
+ * helpful (:
+ *
+ * This source code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/proc_fs.h>
+#include <linux/sysctl.h>
+#include <asm/semaphore.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+/* if defined, 4 faults must occur consecutively to set alarm flags */
+/* #define ENABLE_FAULT_QUEUE */
+
+#define LM92_REG_TEMPERATURE		0x00	/* ro, 16-bit	*/
+#define LM92_REG_CONFIGURATION		0x01	/* rw, 8-bit	*/
+#define LM92_REG_TRIP_HYSTERESIS	0x02	/* rw, 16-bit	*/
+#define LM92_REG_TRIP_CRITICAL		0x03	/* rw, 16-bit	*/
+#define LM92_REG_TRIP_LOW			0x04	/* rw, 16-bit	*/
+#define LM92_REG_TRIP_HIGH			0x05	/* rw, 16-bit	*/
+#define LM92_REG_MANUFACTURER		0x07	/* ro, 16-bit	*/
+
+#define LM92_MANUFACTURER_ID		0x8001
+
+#define TEMP_MIN	(-4096)
+#define TEMP_MAX	4095
+
+#define LIMIT(x) do {							\
+		if ((x) < TEMP_MIN) (x) = TEMP_MIN;		\
+		if ((x) > TEMP_MAX) (x) = TEMP_MAX;		\
+	} while (0)
+
+#define PROC_TO_NATIVE(x) ((x) / 625)
+#define NATIVE_TO_PROC(x) ((x) * 625)
+#define CELSIUS(x) ((x) * 16)
+
+static void lm92_temp (struct i2c_client *client,int operation,int ctl_name,int *nrels_mag,long *results);
+static void lm92_alarms (struct i2c_client *client,int operation,int ctl_name,int *nrels_mag,long *results);
+
+/* -- SENSORS SYSCTL START -- */
+#define LM92_SYSCTL_ALARMS		2001	/* high, low, critical */
+#define LM92_SYSCTL_TEMP		1200	/* high, low, critical, hysteresis, input */
+
+#define LM92_ALARM_TEMP_HIGH	0x01
+#define LM92_ALARM_TEMP_LOW		0x02
+#define LM92_ALARM_TEMP_CRIT	0x04
+#define LM92_TEMP_HIGH			0x08
+#define LM92_TEMP_LOW			0x10
+#define LM92_TEMP_CRIT			0x20
+#define LM92_TEMP_HYST			0x40
+#define LM92_TEMP_INPUT			0x80
+
+/* -- SENSORS SYSCTL END -- */
+
+static ctl_table lm92_dir_table[] = {
+	{LM92_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm92_temp, NULL},
+	{LM92_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &lm92_alarms, NULL},
+	{0}
+};
+
+/* NOTE: all temperatures are degrees centigrade * 16 */
+typedef struct {
+	struct i2c_client client;
+	int sysctl_id;
+	unsigned long timestamp;
+	struct {
+		long high;
+		long low;
+		long crit;
+		long hyst;
+		long input;
+	} temp;
+	struct {
+		long low;
+		long high;
+		long crit;
+	} alarms;
+} lm92_t;
+
+/* this is needed for each client driver method */
+static struct i2c_driver lm92_driver;
+
+/* ensure exclusive access to chip and static variables */
+static DECLARE_MUTEX (mutex);
+
+/* addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x48, 0x4f, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* insmod parameters */
+SENSORS_INSMOD_1 (lm92);
+
+static inline int lm92_write8 (struct i2c_client *client,u8 reg,u8 value)
+{
+	return (i2c_smbus_write_byte_data (client,reg,value) < 0 ? -EIO : 0);
+}
+
+static inline int lm92_read16 (struct i2c_client *client,u8 reg,u16 *value)
+{
+	s32 tmp = i2c_smbus_read_word_data (client,reg);
+
+	if (tmp < 0) return (-EIO);
+
+	/* convert the data to little endian format */
+	*value = swab16((u16) tmp);
+
+	return (0);
+}
+
+static inline int lm92_write16 (struct i2c_client *client,u8 reg,u16 value)
+{
+	/* convert the data to big endian format */
+	if (i2c_smbus_write_word_data(client, reg, swab16(value)) < 0)
+		return -EIO;
+
+	return 0;
+}
+
+static int lm92_read (struct i2c_client *client)
+{
+	lm92_t *data = (lm92_t *) client->data;
+	u16 value[5];
+
+	if ((jiffies - data->timestamp) > HZ) {
+		if (lm92_read16 (client,LM92_REG_TEMPERATURE,value) < 0 ||
+			lm92_read16 (client,LM92_REG_TRIP_HYSTERESIS,value + 1) < 0 ||
+			lm92_read16 (client,LM92_REG_TRIP_CRITICAL,value + 2) < 0 ||
+			lm92_read16 (client,LM92_REG_TRIP_LOW,value + 3) < 0 ||
+			lm92_read16 (client,LM92_REG_TRIP_HIGH,value + 4) < 0)
+			return (-EIO);
+
+		data->temp.input = (s16) value[0] >> 3;
+		data->temp.hyst = (s16) value[1] >> 3;
+		data->temp.crit = (s16) value[2] >> 3;
+		data->temp.low = (s16) value[3] >> 3;
+		data->temp.high = (s16) value[4] >> 3;
+
+		data->alarms.low = value[0] & 1;
+		data->alarms.high = (value[0] & 2) >> 1;
+		data->alarms.crit = (value[0] & 4) >> 2;
+
+		data->timestamp = jiffies;
+	}
+
+	return (0);
+}
+
+static int lm92_write (struct i2c_client *client)
+{
+	lm92_t *data = (lm92_t *) client->data;
+
+	LIMIT (data->temp.hyst);
+	LIMIT (data->temp.crit);
+	LIMIT (data->temp.low);
+	LIMIT (data->temp.high);
+
+	if (lm92_write16 (client,LM92_REG_TRIP_HYSTERESIS,((s16) data->temp.hyst << 3)) < 0 ||
+		lm92_write16 (client,LM92_REG_TRIP_CRITICAL,((s16) data->temp.crit << 3)) < 0 ||
+		lm92_write16 (client,LM92_REG_TRIP_LOW,((s16) data->temp.low << 3)) < 0 ||
+		lm92_write16 (client,LM92_REG_TRIP_HIGH,((s16) data->temp.high << 3)) < 0)
+		return (-EIO);
+
+	return (0);
+}
+
+static void lm92_temp (struct i2c_client *client,int operation,int ctl_name,int *nrels_mag,long *results)
+{
+	if (!down_interruptible (&mutex)) {
+		lm92_t *data = (lm92_t *) client->data;
+
+		if (operation == SENSORS_PROC_REAL_READ) {
+			lm92_read (client);
+			results[0] = NATIVE_TO_PROC (data->temp.input);
+			results[1] = NATIVE_TO_PROC (data->temp.high);
+			results[2] = NATIVE_TO_PROC (data->temp.low);
+			results[3] = NATIVE_TO_PROC (data->temp.crit);
+			results[4] = NATIVE_TO_PROC (data->temp.hyst);
+			*nrels_mag = 5;
+		} else if (operation == SENSORS_PROC_REAL_WRITE && *nrels_mag == 4) {
+			data->temp.high = PROC_TO_NATIVE (results[0]);
+			data->temp.low = PROC_TO_NATIVE (results[1]);
+			data->temp.crit = PROC_TO_NATIVE (results[2]);
+			data->temp.hyst = PROC_TO_NATIVE (results[3]);
+			lm92_write (client);
+		} else if (operation == SENSORS_PROC_REAL_INFO) {
+			*nrels_mag = 4;
+		}
+
+		up (&mutex);
+	}
+}
+
+static void lm92_alarms (struct i2c_client *client,int operation,int ctl_name,int *nrels_mag,long *results)
+{
+	if (!down_interruptible (&mutex)) {
+		lm92_t *data = (lm92_t *) client->data;
+
+		if (operation == SENSORS_PROC_REAL_READ) {
+			lm92_read (client);
+			results[0] = data->alarms.high || (data->alarms.low << 1) || (data->alarms.crit << 2);
+			*nrels_mag = 1;
+		} else if (operation == SENSORS_PROC_REAL_INFO) {
+			*nrels_mag = 0;
+		}
+
+		up (&mutex);
+	}
+}
+
+static int max6635_check(struct i2c_client *client)
+{
+	int i;
+	u16 temp_low, temp_high, temp_hyst, temp_crit;
+	u8 conf;
+
+	temp_low = i2c_smbus_read_word_data(client, LM92_REG_TRIP_LOW);
+	temp_high = i2c_smbus_read_word_data(client, LM92_REG_TRIP_HIGH);
+	temp_hyst = i2c_smbus_read_word_data(client, LM92_REG_TRIP_HYSTERESIS);
+	temp_crit = i2c_smbus_read_word_data(client, LM92_REG_TRIP_CRITICAL);
+	
+	if ((temp_low & 0x7f00) || (temp_high & 0x7f00)
+	 || (temp_hyst & 0x7f00) || (temp_crit & 0x7f00))
+		return 0;
+
+	conf = i2c_smbus_read_byte_data(client, LM92_REG_CONFIGURATION);
+
+	for (i=0; i<128; i+=16) {
+		if (temp_low != i2c_smbus_read_word_data(client, LM92_REG_TRIP_LOW + i)
+		 || temp_high != i2c_smbus_read_word_data(client, LM92_REG_TRIP_HIGH + i)
+		 || temp_hyst != i2c_smbus_read_word_data(client, LM92_REG_TRIP_HYSTERESIS + i)
+		 || temp_crit != i2c_smbus_read_word_data(client, LM92_REG_TRIP_CRITICAL + i)
+		 || conf != i2c_smbus_read_byte_data(client, LM92_REG_CONFIGURATION + i))
+			return 0;
+	}
+	
+	return 1;
+}
+
+static int lm92_init_client (struct i2c_client *client)
+{
+	lm92_t *data = (lm92_t *) client->data;
+	u8 value = 0;
+	int result;
+
+	/* force reads to query the chip */
+	data->timestamp = 0;
+
+	/* setup the configuration register */
+
+#ifdef ENABLE_FAULT_QUEUE
+	value |= 0x10;
+#endif	/* #ifdef ENABLE_FAULT_QUEUE */
+
+	if (lm92_write8 (client,LM92_REG_CONFIGURATION,value) < 0)
+		return (-ENODEV);
+
+	/* set default alarm trigger values */
+
+	data->temp.high = CELSIUS (64);
+	data->temp.low = CELSIUS (10);
+	data->temp.crit = CELSIUS (80);
+	data->temp.hyst = CELSIUS (2);
+
+	if ((result = lm92_write (client)) < 0)
+		return (result);
+
+	/* read everything once so that our cached data is updated */
+
+	if ((result = lm92_read (client)) < 0)
+		return (result);
+
+	return (0);
+}
+
+static int lm92_detect (struct i2c_adapter *adapter,int address,unsigned short flags,int kind)
+{
+	static int id = 0;
+	struct i2c_client *client;
+	lm92_t *data;
+	int result;
+	u16 manufacturer;
+
+	if (!i2c_check_functionality (adapter,I2C_FUNC_SMBUS_BYTE_DATA))
+		return (-ENODEV);
+
+	if (!(data = kmalloc(sizeof(lm92_t), GFP_KERNEL)))
+		return (-ENOMEM);
+
+	client = &data->client;
+	client->addr = address;
+	client->data = data;
+	client->adapter = adapter;
+	client->driver = &lm92_driver;
+	client->flags = 0;
+	strcpy (client->name,lm92_driver.name);
+
+	if (down_interruptible (&mutex)) {
+		result = -ERESTARTSYS;
+		goto ERROR1;
+	}
+
+	if (kind < 0) {
+		/* Is it an lm92? */
+		if (address < 0x4c
+		 && (lm92_read16(client,LM92_REG_MANUFACTURER,&manufacturer) < 0
+		  || manufacturer != LM92_MANUFACTURER_ID)) {
+		  	/* Is it a MAX6635/MAX6635/MAX6635? */
+			if (!max6635_check(client)) {
+				result = -ENODEV;
+				goto ERROR2;
+			}
+		}
+	}
+
+	if ((result = i2c_attach_client (client))) {
+		goto ERROR2;
+	}
+
+	if ((result = i2c_register_entry (client,client->name,lm92_dir_table)) < 0) {
+		goto ERROR3;
+	}
+	data->sysctl_id = result;
+
+	if ((result = lm92_init_client (client)) < 0) {
+		goto ERROR4;
+	}
+
+	client->id = id++;
+
+	up (&mutex);
+
+	return (0);
+
+ERROR4:
+	i2c_deregister_entry(data->sysctl_id);
+ERROR3:
+	i2c_detach_client(client);
+ERROR2:
+	up(&mutex);
+ERROR1:
+	kfree(data);
+	return result;
+}
+
+static int lm92_attach_adapter (struct i2c_adapter *adapter)
+{
+	return i2c_detect (adapter,&addr_data,lm92_detect);
+}
+
+static int lm92_detach_client (struct i2c_client *client)
+{
+	int result;
+
+	i2c_deregister_entry (((lm92_t *) (client->data))->sysctl_id);
+
+	if ((result = i2c_detach_client (client)))
+		return (result);
+
+	kfree(client->data);
+
+	return (0);
+}
+
+
+static struct i2c_driver lm92_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "lm92",
+	.id		= I2C_DRIVERID_LM92,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= lm92_attach_adapter,
+	.detach_client	= lm92_detach_client,
+};
+
+static int __init sm_lm92_init(void)
+{
+	printk ("lm92.o version %s (%s)\n",LM_VERSION,LM_DATE);
+	return i2c_add_driver(&lm92_driver);
+}
+
+
+static void __exit sm_lm92_exit(void)
+{
+	i2c_del_driver(&lm92_driver);
+}
+
+
+
+MODULE_AUTHOR ("Abraham van der Merwe <abraham@2d3d.co.za>");
+MODULE_DESCRIPTION ("Linux support for LM92 Temperature Sensor");
+
+MODULE_LICENSE ("GPL");
+
+module_init(sm_lm92_init);
+module_exit(sm_lm92_exit);
+
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/Makefile linux-2.4.27-leo/drivers/sensors/Makefile
--- linux-2.4.27/drivers/sensors/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/Makefile	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,47 @@
+#
+# Makefile for the kernel hardware sensors drivers.
+#
+
+MOD_LIST_NAME := SENSORS_MODULES
+O_TARGET := sensor.o
+
+obj-$(CONFIG_SENSORS_ADM1021)	+= adm1021.o
+obj-$(CONFIG_SENSORS_ADM1024)	+= adm1024.o
+obj-$(CONFIG_SENSORS_ADM1025)	+= adm1025.o
+obj-$(CONFIG_SENSORS_ADM1026)	+= adm1026.o
+obj-$(CONFIG_SENSORS_ADM9240)	+= adm9240.o
+obj-$(CONFIG_SENSORS_ASB100)	+= asb100.o
+obj-$(CONFIG_SENSORS_BT869)	+= bt869.o
+obj-$(CONFIG_SENSORS_DDCMON)	+= ddcmon.o
+obj-$(CONFIG_SENSORS_DS1621)	+= ds1621.o
+obj-$(CONFIG_SENSORS_EEPROM)	+= eeprom.o
+obj-$(CONFIG_SENSORS_FSCPOS)	+= fscpos.o
+obj-$(CONFIG_SENSORS_FSCSCY)	+= fscscy.o
+obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
+obj-$(CONFIG_SENSORS_GL520SM)	+= gl520sm.o
+obj-$(CONFIG_SENSORS_IT87)	+= it87.o
+obj-$(CONFIG_SENSORS_LM75)	+= lm75.o
+obj-$(CONFIG_SENSORS_LM78)	+= lm78.o
+obj-$(CONFIG_SENSORS_LM80)	+= lm80.o
+obj-$(CONFIG_SENSORS_LM83)	+= lm83.o
+obj-$(CONFIG_SENSORS_LM85)	+= lm85.o
+obj-$(CONFIG_SENSORS_LM87)	+= lm87.o
+obj-$(CONFIG_SENSORS_LM90)	+= lm90.o
+obj-$(CONFIG_SENSORS_LM92)	+= lm92.o
+obj-$(CONFIG_SENSORS_MAX6650)	+= max6650.o
+obj-$(CONFIG_SENSORS_MAXILIFE)	+= maxilife.o
+obj-$(CONFIG_SENSORS_MTP008)	+= mtp008.o
+obj-$(CONFIG_SENSORS_PCF8574)	+= pcf8574.o
+obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591.o
+obj-$(CONFIG_SENSORS_SIS5595)	+= sis5595.o
+obj-$(CONFIG_SENSORS_SMSC47M1)	+= smsc47m1.o
+obj-$(CONFIG_SENSORS_THMC50)	+= thmc50.o
+obj-$(CONFIG_SENSORS_VIA686A)	+= via686a.o
+obj-$(CONFIG_SENSORS_VT1211)	+= vt1211.o
+obj-$(CONFIG_SENSORS_VT8231)	+= vt8231.o
+obj-$(CONFIG_SENSORS_W83781D)	+= w83781d.o
+obj-$(CONFIG_SENSORS_W83627HF)	+= w83627hf.o
+obj-$(CONFIG_SENSORS_W83L785TS)	+= w83l785ts.o
+
+include $(TOPDIR)/Rules.make
+
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/matorb.c linux-2.4.27-leo/drivers/sensors/matorb.c
--- linux-2.4.27/drivers/sensors/matorb.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/matorb.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,286 @@
+/*
+    matorb.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+    and Philip Edelbrock <phil@netroedge.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+#define DEBUG 1
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x2E, SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(matorb);
+
+/* Many MATORB constants specified below */
+
+
+/* Each client has this additional data */
+struct matorb_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+};
+
+static int matorb_attach_adapter(struct i2c_adapter *adapter);
+static int matorb_detect(struct i2c_adapter *adapter, int address,
+			 unsigned short flags, int kind);
+static void matorb_init_client(struct i2c_client *client);
+static int matorb_detach_client(struct i2c_client *client);
+
+static int matorb_write_value(struct i2c_client *client, u8 reg,
+			      u16 value);
+static void matorb_disp(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void matorb_update_client(struct i2c_client *client);
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver matorb_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "Matrix Orbital LCD driver",
+	.id		= I2C_DRIVERID_MATORB,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= matorb_attach_adapter,
+	.detach_client	= matorb_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+#define MATORB_SYSCTL_DISP 1000
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected MATORB. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized
+   when a new copy is allocated. */
+static ctl_table matorb_dir_table_template[] = {
+	{MATORB_SYSCTL_DISP, "disp", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &matorb_disp},
+	{0}
+};
+
+static int matorb_id = 0;
+
+static int matorb_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, matorb_detect);
+}
+
+/* This function is called by i2c_detect */
+int matorb_detect(struct i2c_adapter *adapter, int address,
+		  unsigned short flags, int kind)
+{
+	int i, cur;
+	struct i2c_client *new_client;
+	struct matorb_data *data;
+	int err = 0;
+	const char *type_name = "matorb";
+	const char *client_name = "matorb";
+
+	/* Make sure we aren't probing the ISA bus!! This is just a safety check
+	   at this moment; i2c_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("matorb.o: matorb_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE |
+				     I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+		    goto ERROR0;
+
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access matorb_{read,write}_value. */
+	if (!(data = kmalloc(sizeof(struct matorb_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &matorb_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. It is lousy. */
+	cur = i2c_smbus_write_byte_data(new_client, 0x0FE, 0x58);	/* clear screen */
+
+	printk("matorb.o: debug detect 0x%X\n", cur);
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+
+	new_client->id = matorb_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client, type_name,
+					matorb_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	matorb_init_client(new_client);
+	return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int matorb_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct matorb_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("matorb.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+
+#if 0
+/* All registers are word-sized, except for the configuration register.
+   MATORB uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+static int matorb_read_value(struct i2c_client *client, u8 reg)
+{
+	return -1;		/* Doesn't support reads */
+}
+#endif
+
+/* All registers are word-sized, except for the configuration register.
+   MATORB uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+static int matorb_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+	if (reg == 0) {
+		return i2c_smbus_write_byte(client, value);
+	} else {
+		return i2c_smbus_write_byte_data(client, reg, value);
+	}
+}
+
+static void matorb_init_client(struct i2c_client *client)
+{
+	/* Initialize the MATORB chip */
+}
+
+static void matorb_update_client(struct i2c_client *client)
+{
+	struct matorb_data *data = client->data;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting matorb update\n");
+#endif
+
+/* nothing yet */
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+void matorb_disp(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	int i;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		matorb_update_client(client);
+		results[0] = 0;
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		for (i = 1; i <= *nrels_mag; i++) {
+			matorb_write_value(client, 0, results[i - 1]);
+		}
+	}
+}
+
+static int __init sm_matorb_init(void)
+{
+	printk("matorb.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&matorb_driver);
+}
+
+static void __exit sm_matorb_exit(void)
+{
+	i2c_del_driver(&matorb_driver);
+}
+
+
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("MATORB driver");
+
+module_init(sm_matorb_init);
+module_exit(sm_matorb_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/max6650.c linux-2.4.27-leo/drivers/sensors/max6650.c
--- linux-2.4.27/drivers/sensors/max6650.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/max6650.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,545 @@
+/*
+ * max6650.c - Part of lm_sensors, Linux kernel modules for hardware
+ *             monitoring. 
+ * 
+ * Author: John Morris <john.morris@spirentcom.com>
+ *
+ * Copyright (c) 2003 Spirent Communications
+ *
+ * This module has only been tested with the MAX6651 chip. It should
+ * work with the MAX6650 also, though with reduced functionality. It
+ * does not yet distinguish max6650 and max6651 chips.
+ * 
+ * Tha datasheet was last seen at: 
+ *
+ *        http://pdfserv.maxim-ic.com/en/ds/MAX6650-MAX6651.pdf
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/i2c-id.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+#ifndef I2C_DRIVERID_MAX6650
+#define I2C_DRIVERID_MAX6650	1044
+#endif
+
+/*
+ * Addresses to scan. There are four disjoint possibilities, by pin config.
+ */
+
+static unsigned short normal_i2c[] = {0x1b, 0x1f, 0x48, 0x4b, SENSORS_I2C_END};
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/*
+ * Insmod parameters
+ */
+
+SENSORS_INSMOD_1(max6650);
+
+/* 
+ * MAX 6650/6651 registers
+ */
+
+#define MAX6650_REG_SPEED       0x00
+#define MAX6650_REG_CONFIG      0x02
+#define MAX6650_REG_GPIO_DEF    0x04
+#define MAX6650_REG_DAC         0x06
+#define MAX6650_REG_ALARM_EN    0x08
+#define MAX6650_REG_ALARM       0x0A
+#define MAX6650_REG_TACH0       0x0C
+#define MAX6650_REG_TACH1       0x0E
+#define MAX6650_REG_TACH2       0x10
+#define MAX6650_REG_TACH3       0x12
+#define MAX6650_REG_GPIO_STAT   0x14
+#define MAX6650_REG_COUNT       0x16
+
+/*
+ * Config register bits
+ */
+ 
+#define MAX6650_CFG_MODE_MASK           0x30
+#define MAX6650_CFG_MODE_ON             0x00
+#define MAX6650_CFG_MODE_OFF            0x10
+#define MAX6650_CFG_MODE_CLOSED_LOOP    0x20
+#define MAX6650_CFG_MODE_OPEN_LOOP      0x30
+
+static const u8 tach_reg[] = 
+{
+    MAX6650_REG_TACH0, MAX6650_REG_TACH1, 
+    MAX6650_REG_TACH2, MAX6650_REG_TACH3 
+};
+
+#define MAX6650_INT_CLK 254000  /* Default clock speed - 254 kHz */
+
+/*
+ * Functions declaration
+ */
+
+static void max6650_fan (struct i2c_client *client, int operation, int
+	ctl_name, int *nrels_mag, long *results);
+static void max6650_speed (struct i2c_client *client, int operation, int
+	ctl_name, int *nrels_mag, long *results);
+static void max6650_xdump (struct i2c_client *client, int operation, int
+	ctl_name, int *nrels_mag, long *results);
+static int max6650_detect(struct i2c_adapter *adapter, int address, unsigned
+	short flags, int kind);
+static int max6650_attach_adapter(struct i2c_adapter *adapter);
+static int max6650_detach_client(struct i2c_client *client);
+static void max6650_init_client(struct i2c_client *client);
+static int max6650_read(struct i2c_client *client, u8 reg);
+
+/*
+ * Driver data (common to all clients)
+ */
+
+
+static struct i2c_driver max6650_driver = {
+    .owner          = THIS_MODULE,
+    .name           = "MAX6650/1 sensor driver",
+    .id             = I2C_DRIVERID_MAX6650,
+    .flags          = I2C_DF_NOTIFY,
+    .attach_adapter = max6650_attach_adapter,
+    .detach_client  = max6650_detach_client
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct max6650_data
+{
+	struct i2c_client client;
+    int sysctl_id;
+    struct semaphore update_lock;
+    char valid;                 /* zero until following fields are valid */
+    unsigned long last_updated; /* in jiffies */
+
+    /* register values */
+       
+    u8 speed;
+    u8 config;
+    u8 tach[4];
+    u8 count;
+};
+
+/*
+ * Proc entries
+ * These files are created for each detected max6650.
+ */
+
+/* -- SENSORS SYSCTL START -- */
+
+#define MAX6650_SYSCTL_FAN1     1101
+#define MAX6650_SYSCTL_FAN2     1102
+#define MAX6650_SYSCTL_FAN3     1103
+#define MAX6650_SYSCTL_FAN4     1104
+#define MAX6650_SYSCTL_SPEED    1105
+#define MAX6650_SYSCTL_XDUMP    1106
+
+
+/* -- SENSORS SYSCTL END -- */
+
+
+static ctl_table max6650_dir_table_template[] =
+{
+    {MAX6650_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL,
+        	 &i2c_proc_real, &i2c_sysctl_real, NULL, &max6650_fan},
+    {MAX6650_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL,
+        	 &i2c_proc_real, &i2c_sysctl_real, NULL, &max6650_fan},
+    {MAX6650_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL,
+        	 &i2c_proc_real, &i2c_sysctl_real, NULL, &max6650_fan},
+    {MAX6650_SYSCTL_FAN4, "fan4", NULL, 0, 0644, NULL,
+        	 &i2c_proc_real, &i2c_sysctl_real, NULL, &max6650_fan},
+    {MAX6650_SYSCTL_SPEED, "speed", NULL, 0, 0644, NULL,
+        	 &i2c_proc_real, &i2c_sysctl_real, NULL, &max6650_speed},
+    {MAX6650_SYSCTL_XDUMP, "xdump", NULL, 0, 0644, NULL,
+        	 &i2c_proc_real, &i2c_sysctl_real, NULL, &max6650_xdump},
+    {0}
+};
+
+/*
+ * Internal variables
+ */
+
+static int max6650_id = 0;
+
+/*
+ * Real code
+ */
+
+static int max6650_attach_adapter(struct i2c_adapter *adapter)
+{
+    return i2c_detect(adapter, &addr_data, max6650_detect);
+}
+
+/*
+ * The following function does more than just detection. If detection
+ * succeeds, it also registers the new chip.
+ */
+
+static int max6650_detect(struct i2c_adapter *adapter, int address, unsigned
+	short flags, int kind)
+{
+    struct i2c_client *new_client;
+    struct max6650_data *data;
+    int err = 0;
+    const char *type_name = "";
+    const char *client_name = "";
+
+#ifdef DEBUG
+    if (i2c_is_isa_adapter(adapter)) {
+        printk("max6650.o: Called for an ISA bus adapter, aborting.\n");
+        return 0;
+    }
+#endif
+
+    if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+#ifdef DEBUG
+        printk("max6650.o: I2C bus doesn't support byte read mode, skipping.\n");
+#endif
+        return 0;
+    }
+
+    if (!(data = kmalloc(sizeof(struct max6650_data), GFP_KERNEL))) {
+        printk("max6650.o: Out of memory in max6650_detect (new_client).\n");
+        return -ENOMEM;
+    }
+
+    /*
+     * The common I2C client data is placed right before the
+     * max6650-specific data. The max6650-specific data is pointed to by the
+	 * data field from the I2C client data.
+     */
+
+	new_client = &data->client;
+    new_client->addr = address;
+    new_client->data = data;
+    new_client->adapter = adapter;
+    new_client->driver = &max6650_driver;
+    new_client->flags = 0;
+
+    /*
+     * Now we do the remaining detection. A negative kind means that
+     * the driver was loaded with no force parameter (default), so we
+     * must both detect and identify the chip (actually there is only
+     * one possible kind of chip for now, max6650). A zero kind means that
+     * the driver was loaded with the force parameter, the detection
+     * step shall be skipped. A positive kind means that the driver
+     * was loaded with the force parameter and a given kind of chip is
+     * requested, so both the detection and the identification steps
+     * are skipped.
+     *
+     * Currently I can find no way to distinguish between a MAX6650 and 
+     * a MAX6651. This driver has only been tried on the latter.
+     */
+
+    if (kind < 0) {     /* detection */
+        if (
+            (max6650_read(new_client, MAX6650_REG_CONFIG) & 0xC0) ||
+            (max6650_read(new_client, MAX6650_REG_GPIO_STAT) & 0xE0) ||
+            (max6650_read(new_client, MAX6650_REG_ALARM_EN) & 0xE0) ||
+            (max6650_read(new_client, MAX6650_REG_ALARM) & 0xE0) ||
+            (max6650_read(new_client, MAX6650_REG_COUNT) & 0xFC) 
+        )
+        {
+#ifdef DEBUG
+            printk("max6650.o: max6650 detection failed at 0x%02x.\n",
+                                                                    address);
+#endif
+            goto ERROR1;
+        }
+    }
+
+    if (kind <= 0) { /* identification */
+        kind = max6650;
+    }
+
+    if (kind <= 0) {    /* identification failed */
+        printk("max6650.o: Unsupported chip.\n");
+        goto ERROR1;
+    }
+
+    if (kind == max6650) {
+        type_name = "max6650";
+        client_name = "max6650 chip";
+    } else {
+        printk("max6650.o: Unknown kind %d.\n", kind);
+        goto ERROR1;
+    }
+	
+    /*
+     * OK, we got a valid chip so we can fill in the remaining client
+     * fields.
+     */
+
+    strcpy(new_client->name, client_name);
+    new_client->id = max6650_id++;
+    data->valid = 0;
+    init_MUTEX(&data->update_lock);
+
+    /*
+     * Tell the I2C layer a new client has arrived.
+     */
+
+    if ((err = i2c_attach_client(new_client))) {
+#ifdef DEBUG
+        printk("max6650.o: Failed attaching client.\n");
+#endif
+        goto ERROR1;
+    }
+
+    /*
+     * Register a new directory entry.
+     */
+    if ((err = i2c_register_entry(new_client, type_name,
+                                    max6650_dir_table_template)) < 0) {
+#ifdef DEBUG
+        printk("max6650.o: Failed registering directory entry.\n");
+#endif
+        goto ERROR2;
+    }
+    data->sysctl_id = err;
+
+    /*
+     * Initialize the max6650 chip
+     */
+    max6650_init_client(new_client);
+    return 0;
+
+ERROR2:
+    i2c_detach_client(new_client);
+ERROR1:
+    kfree(data);
+    return err;
+}
+
+static void max6650_init_client(struct i2c_client *client)
+{
+    /* Nothing to do here - assume the BIOS has initialized the chip */
+}
+
+static int max6650_detach_client(struct i2c_client *client)
+{
+    int err;
+
+    i2c_deregister_entry(((struct max6650_data *) (client->data))->sysctl_id);
+    if ((err = i2c_detach_client(client))) {
+        printk("max6650.o: Client deregistration failed, "
+                                        "client not detached.\n");
+        return err;
+    }
+
+    kfree(client->data);
+    return 0;
+}
+
+static int max6650_read(struct i2c_client *client, u8 reg)
+{
+    return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int max6650_write(struct i2c_client *client, u8 reg, u8 value)
+{
+    return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static void max6650_update_client(struct i2c_client *client)
+{
+    int i;
+    struct max6650_data *data = client->data;
+
+    down(&data->update_lock);
+    
+    if ((jiffies - data->last_updated > HZ) ||
+        (jiffies < data->last_updated) || !data->valid) {
+#ifdef DEBUG
+        printk("max6650.o: Updating max6650 data.\n");
+#endif
+        data->speed  = max6650_read (client, MAX6650_REG_SPEED);
+        data->config = max6650_read (client, MAX6650_REG_CONFIG);
+        for (i = 0; i < 4; i++) {
+            data->tach[i] = max6650_read(client, tach_reg[i]);
+        }
+        data->count = max6650_read (client, MAX6650_REG_COUNT);
+        data->last_updated = jiffies;
+        data->valid = 1;
+    }
+    up(&data->update_lock);
+}
+
+static void max6650_fan (struct i2c_client *client, int operation, int
+	ctl_name, int *nrels_mag, long *results)
+{
+    int index = ctl_name - MAX6650_SYSCTL_FAN1;
+    struct max6650_data *data = client->data;
+    int tcount;         /* Tachometer count time, 0.25 second units */
+
+    if (operation == SENSORS_PROC_REAL_INFO) {
+	*nrels_mag = 0;
+    } else if (operation == SENSORS_PROC_REAL_READ) {
+        max6650_update_client(client);
+
+        /*
+         * Calculation details:
+         *
+         * Each tachometer counts over an interval given by the "count"
+         * register (0.25, 0.5, 1 or 2 seconds). This module assumes
+         * that the fans produce two pulses per revolution (this seems
+         * to be the most common).
+         */
+         
+        tcount = 1 << data->count;         /* 0.25 second units */
+        results[0] = (data->tach[index] * 240) / tcount;    /* counts per min */
+        results[0] /= 2;                   /* Assume two counts per rev */
+        *nrels_mag = 1;
+    }
+}
+
+/*
+ * Set the fan speed to the specified RPM (or read back the RPM setting).
+ *
+ * The MAX6650/1 will automatically control fan speed when in closed loop
+ * mode.
+ *
+ * Assumptions:
+ *
+ * 1) The MAX6650/1 is running from its internal 254kHz clock (perhaps
+ *    this should be made a module parameter).
+ *
+ * 2) The prescaler (low three bits of the config register) has already
+ *    been set to an appropriate value.
+ *
+ * The relevant equations are given on pages 21 and 22 of the datasheet.
+ *
+ * From the datasheet, the relevant equation when in regulation is:
+ *
+ *    [fCLK / (128 x (KTACH + 1))] = 2 x FanSpeed / KSCALE
+ *
+ * where:
+ *
+ *    fCLK is the oscillator frequency (either the 254kHz internal 
+ *         oscillator or the externally applied clock)
+ *
+ *    KTACH is the value in the speed register
+ *
+ *    FanSpeed is the speed of the fan in rps
+ *
+ *    KSCALE is the prescaler value (1, 2, 4, 8, or 16)
+ *
+ * When reading, we need to solve for FanSpeed. When writing, we need to
+ * solve for KTACH.
+ *
+ * Note: this tachometer is completely separate from the tachometers
+ * used to measure the fan speeds. Only one fan's speed (fan1) is
+ * controlled.
+ */
+
+static void max6650_speed (struct i2c_client *client, int operation, int
+                        	ctl_name, int *nrels_mag, long *results)
+{
+    struct max6650_data *data = client->data;
+    int kscale, ktach, fclk, rpm;
+    
+    if (operation == SENSORS_PROC_REAL_INFO) {
+        *nrels_mag = 0;
+    } else if (operation == SENSORS_PROC_REAL_READ) {
+        /*
+         * Use the datasheet equation:
+         *
+         *    FanSpeed = KSCALE x fCLK / [256 x (KTACH + 1)]
+         *
+         * then multiply by 60 to give rpm.
+         */
+
+        max6650_update_client(client);
+
+        kscale = 1 << (data->config & 7);
+        ktach  = data->speed;
+        fclk   = MAX6650_INT_CLK;
+        rpm    = 60 * kscale * fclk / (256 * (ktach + 1));
+
+        results[0] = rpm;
+        *nrels_mag = 1;
+    } else if (operation == SENSORS_PROC_REAL_WRITE && *nrels_mag >= 1) {
+        /*
+         * Divide the required speed by 60 to get from rpm to rps, then
+         * use the datasheet equation:
+         *
+         *     KTACH = [(fCLK x KSCALE) / (256 x FanSpeed)] - 1
+         */
+
+        max6650_update_client(client);
+
+        rpm    = results[0];
+        kscale = 1 << (data->config & 7);
+        fclk   = MAX6650_INT_CLK;
+        ktach  = ((fclk * kscale) / (256 * rpm / 60)) - 1;
+
+        data->speed  = ktach;
+        data->config = (data->config & ~MAX6650_CFG_MODE_MASK) | 
+                                            MAX6650_CFG_MODE_CLOSED_LOOP;
+        max6650_write (client, MAX6650_REG_CONFIG, data->config);
+        max6650_write (client, MAX6650_REG_SPEED,  data->speed);
+    }
+}
+
+/*
+ * Debug - dump all registers except the tach counts.
+ */
+                                 
+static void max6650_xdump (struct i2c_client *client, int operation, int
+	ctl_name, int *nrels_mag, long *results)
+{
+    if (operation == SENSORS_PROC_REAL_INFO) {
+        *nrels_mag = 0;
+    } else if (operation == SENSORS_PROC_REAL_READ) {
+        results[0] = max6650_read (client, MAX6650_REG_SPEED);
+        results[1] = max6650_read (client, MAX6650_REG_CONFIG);
+        results[2] = max6650_read (client, MAX6650_REG_GPIO_DEF);
+        results[3] = max6650_read (client, MAX6650_REG_DAC);
+        results[4] = max6650_read (client, MAX6650_REG_ALARM_EN);
+        results[5] = max6650_read (client, MAX6650_REG_ALARM);
+        results[6] = max6650_read (client, MAX6650_REG_GPIO_STAT);
+        results[7] = max6650_read (client, MAX6650_REG_COUNT);
+        *nrels_mag = 8;
+    }
+}
+
+static int __init sm_max6650_init(void)
+{
+    printk(KERN_INFO "max6650.o version %s (%s)\n", LM_VERSION, LM_DATE);
+    return i2c_add_driver(&max6650_driver);
+}
+
+static void __exit sm_max6650_exit(void)
+{
+    i2c_del_driver(&max6650_driver);
+}
+
+MODULE_AUTHOR("john.morris@spirentcom.com");
+MODULE_DESCRIPTION("max6650 sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(sm_max6650_init);
+module_exit(sm_max6650_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/maxilife.c linux-2.4.27-leo/drivers/sensors/maxilife.c
--- linux-2.4.27/drivers/sensors/maxilife.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/maxilife.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,1387 @@
+/*
+    maxilife.c - Part of lm_sensors, Linux kernel modules for hardware
+                 monitoring
+    Copyright (c) 1999-2000 Fons Rademakers <Fons.Rademakers@cern.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
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* The is the driver for the HP MaxiLife Health monitoring system
+   as used in the line of HP Kayak Workstation PC's.
+   
+   The driver supports the following MaxiLife firmware versions:
+   
+   0) HP KAYAK XU/XAs (Dual Pentium II Slot 1, Deschutes/Klamath)
+   1) HP KAYAK XU (Dual Xeon [Slot 2] 400/450 Mhz)
+   2) HP KAYAK XA (Pentium II Slot 1, monoprocessor)
+   
+   Currently firmware auto detection is not implemented. To use the
+   driver load it with the correct option for you Kayak. For example:
+   
+   insmod maxilife.o maxi_version=0 | 1 | 2
+   
+   maxi_version=0 is the default
+   
+   This version of MaxiLife is called MaxiLife'98 and has been
+   succeeded by MaxiLife'99, see below.
+   
+   The new version of the driver also supports MaxiLife NBA (New BIOS
+   Architecture). This new MaxiLife controller provides a much cleaner
+   machine independent abstraction layer to the MaxiLife controller.
+   Instead of accessing directly registers (different for each revision)
+   one now accesses the sensors via unique mailbox tokens that do not
+   change between revisions. Also the quantities are already in physical
+   units (degrees, rpms, voltages, etc.) and don't need special conversion
+   formulas. This new MaxiLife is available on the new 2000 machines,
+   like the Kayak XU800 and XM600. This hardware is also autodetected.
+*/
+
+static const char *version_str = "2.00 29/2/2000 Fons Rademakers";
+
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+MODULE_LICENSE("GPL");
+
+#undef AUTODETECT		/* try to autodetect MaxiLife version */
+/*#define AUTODETECT*/
+#define NOWRITE			/* don't allow writing to MaxiLife registers */
+
+#ifdef AUTODETECT
+#include <linux/vmalloc.h>
+#include <linux/ctype.h>
+#endif
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x10, 0x14, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(maxilife);
+
+/* Macro definitions */
+#define LOW(MyWord) ((u8) (MyWord))
+#define HIGH(MyWord) ((u8) (((u16)(MyWord) >> 8) & 0xFF))
+
+/*----------------- MaxiLife'98 registers and conversion formulas ------------*/
+#define MAXI_REG_TEMP(nr)      (0x60 + (nr))
+
+#define MAXI_REG_FAN(nr)       (0x65 + (nr))
+#define MAXI_REG_FAN_MIN(nr)   ((nr)==0 ? 0xb3 : (nr)==1 ? 0xb3 : 0xab)
+#define MAXI_REG_FAN_MINAS(nr) ((nr)==0 ? 0xb3 : (nr)==1 ? 0xab : 0xb3)
+#define MAXI_REG_FAN_SPEED(nr) ((nr)==0 ? 0xe4 : (nr)==1 ? 0xe5 : 0xe9)
+
+#define MAXI_REG_PLL           0xb9
+#define MAXI_REG_PLL_MIN       0xba
+#define MAXI_REG_PLL_MAX       0xbb
+
+#define MAXI_REG_VID(nr)       ((nr)==0 ? 0xd1 : (nr)==1 ? 0xd9 : \
+                                (nr)==2 ? 0xd4 : 0xc5)
+#define MAXI_REG_VID_MIN(nr)   MAXI_REG_VID(nr)+1
+#define MAXI_REG_VID_MAX(nr)   MAXI_REG_VID(nr)+2
+
+#define MAXI_REG_DIAG_RT1      0x2c
+#define MAXI_REG_DIAG_RT2      0x2d
+
+#define MAXI_REG_BIOS_CTRL     0x2a
+
+/* Conversions. Rounding and limit checking is only done on the TO_REG
+   variants. Note that you should be a bit careful with which arguments
+   these macros are called: arguments may be evaluated more than once.
+   Fixing this is just not worth it. */
+
+			       /* 0xfe: fan off, 0xff: stopped (alarm) */
+			       /* 19531 / val * 60 == 1171860 / val */
+#define FAN_FROM_REG(val)      ((val)==0xfe ? 0 : (val)==0xff ? -1 : \
+                                (val)==0x00 ? -1 : (1171860 / (val)))
+
+static inline u8 FAN_TO_REG(long rpm)
+{
+	if (rpm == 0)
+		return 255;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT((1171860 + rpm / 2) / (rpm), 1, 254);
+}
+
+#define TEMP_FROM_REG(val)     ((val) * 5)
+#define TEMP_TO_REG(val)       (SENSORS_LIMIT((val+2) / 5),0,0xff)
+#define PLL_FROM_REG(val)      (((val) * 1000) / 32)
+#define PLL_TO_REG(val)        (SENSORS_LIMIT((((val) * 32 + 500) / 1000),\
+                                              0,0xff))
+#define VID_FROM_REG(val)      ((val) ? (((val) * 27390) / 256) + 3208 : 0)
+#define VID_TO_REG(val)        (SENSORS_LIMIT((((val) - 3208) * 256) / 27390, \
+                                              0,255))
+#define ALARMS_FROM_REG(val)   (val)
+
+/*----------------- MaxiLife'99 mailbox and token definitions ----------------*/
+/* MaxiLife mailbox data register map */
+#define MAXI_REG_MBX_STATUS    0x5a
+#define MAXI_REG_MBX_CMD       0x5b
+#define MAXI_REG_MBX_TOKEN_H   0x5c
+#define MAXI_REG_MBX_TOKEN_L   0x5d
+#define MAXI_REG_MBX_DATA      0x60
+
+/* Mailbox status register definition */
+#define MAXI_STAT_IDLE         0xff
+#define MAXI_STAT_OK           0x00
+#define MAXI_STAT_BUSY         0x0b
+/* other values not used */
+
+/* Mailbox command register opcodes */
+#define MAXI_CMD_READ          0x02
+#define MAXI_CMD_WRITE         0x03
+/* other values not used */
+
+/* MaxiLife NBA Hardware monitoring tokens */
+
+/* Alarm tokens (0x1xxx) */
+#define MAXI_TOK_ALARM(nr)    (0x1000 + (nr))
+#define MAXI_TOK_ALARM_EVENT   0x1000
+#define MAXI_TOK_ALARM_FAN     0x1001
+#define MAXI_TOK_ALARM_TEMP    0x1002
+#define MAXI_TOK_ALARM_VID     0x1003	/* voltages */
+#define MAXI_TOK_ALARM_AVID    0x1004	/* additional voltages */
+#define MAXI_TOK_ALARM_PWR     0x1101	/* power supply glitch */
+
+/* Fan status tokens (0x20xx) */
+#define MAXI_TOK_FAN(nr)      (0x2000 + (nr))
+#define MAXI_TOK_FAN_CPU       0x2000
+#define MAXI_TOK_FAN_PCI       0x2001
+#define MAXI_TOK_FAN_HDD       0x2002	/* hard disk bay fan */
+#define MAXI_TOK_FAN_SINK      0x2003	/* heatsink */
+
+/* Temperature status tokens (0x21xx) */
+#define MAXI_TOK_TEMP(nr)     (0x2100 + (nr))
+#define MAXI_TOK_TEMP_CPU1     0x2100
+#define MAXI_TOK_TEMP_CPU2     0x2101
+#define MAXI_TOK_TEMP_PCI      0x2102	/* PCI/ambient temp */
+#define MAXI_TOK_TEMP_HDD      0x2103	/* hard disk bay temp */
+#define MAXI_TOK_TEMP_MEM      0x2104	/* mother board temp */
+#define MAXI_TOK_TEMP_CPU      0x2105	/* CPU reference temp */
+
+/* Voltage status tokens (0x22xx) */
+#define MAXI_TOK_VID(nr)      (0x2200 + (nr))
+#define MAXI_TOK_VID_12        0x2200	/* +12 volt */
+#define MAXI_TOK_VID_CPU1      0x2201	/* cpu 1 voltage */
+#define MAXI_TOK_VID_CPU2      0x2202	/* cpu 2 voltage */
+#define MAXI_TOK_VID_L2        0x2203	/* level 2 cache voltage */
+#define MAXI_TOK_VID_M12       0x2204	/* -12 volt */
+
+/* Additive voltage status tokens (0x23xx) */
+#define MAXI_TOK_AVID(nr)     (0x2300 + (nr))
+#define MAXI_TOK_AVID_15       0x2300	/* 1.5 volt */
+#define MAXI_TOK_AVID_18       0x2301	/* 1.8 volt */
+#define MAXI_TOK_AVID_25       0x2302	/* 2.5 volt */
+#define MAXI_TOK_AVID_33       0x2303	/* 3.3 volt */
+#define MAXI_TOK_AVID_5        0x2304	/* 5 volt */
+#define MAXI_TOK_AVID_M5       0x2305	/* -5 volt */
+#define MAXI_TOK_AVID_BAT      0x2306	/* battery voltage */
+
+/* Threshold tokens (0x3xxx) */
+#define MAXI_TOK_MIN(token)    ((token) + 0x1000)
+#define MAXI_TOK_MAX(token)    ((token) + 0x1800)
+
+/* LCD Panel (0x4xxx) */
+#define MAXI_TOK_LCD(nr)      (0x4000 + (nr))
+#define MAXI_TOK_LCD_LINE1     0x4000
+#define MAXI_TOK_LCD_LINE2     0x4001
+#define MAXI_TOK_LCD_LINE3     0x4002
+#define MAXI_TOK_LCD_LINE4     0x4003
+
+			       /* 0xfe: fan off, 0xff: stopped (alarm) */
+			       /* or not available */
+#define FAN99_FROM_REG(val)    ((val)==0xfe ? 0 : (val)==0xff ? -1 : ((val)*39))
+
+			       /* when no CPU2 temp is 127 (0x7f) */
+#define TEMP99_FROM_REG(val)   ((val)==0x7f ? -1 : (val)==0xff ? -1 : (val))
+
+#define VID99_FROM_REG(nr,val) ((val)==0xff ? 0 : \
+                                (nr)==1 ? ((val) * 608) : \
+                                (nr)==2 ? ((val) * 160) : \
+                                (nr)==3 ? ((val) * 160) : \
+                                (nr)==4 ? (val) /* no formula spcified */ : \
+                                (nr)==5 ? ((val) * 823 - 149140) : 0)
+
+
+/* The following product codenames apply:
+     Cristal/Geronimo: HP KAYAK XU/XAs
+                       (Dual Pentium II Slot 1, Deschutes/Klamath)
+     Cognac: HP KAYAK XU (Dual Xeon [Slot 2] 400/450 Mhz)
+     Ashaki: HP KAYAK XA (Pentium II Slot 1, monoprocessor)
+     NBA:    New BIOS Architecture, Kayak XU800, XM600, ... */
+
+enum maxi_type { cristal, cognac, ashaki, nba };
+enum sensor_type { fan, temp, vid, pll, lcd, alarm };
+
+/* For each registered MaxiLife controller, we need to keep some data in
+   memory. That data is pointed to by maxi_list[NR]->data. The structure
+   itself is dynamically allocated, at the same time when a new MaxiLife
+   client is allocated. We assume MaxiLife will only be present on the
+   SMBus and not on the ISA bus. */
+struct maxi_data {
+	struct i2c_client client;
+	struct semaphore lock;
+	int sysctl_id;
+	enum maxi_type type;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 fan[4];		/* Register value */
+	u8 fan_min[4];		/* Register value */
+	u8 fan_speed[4];	/* Register value */
+	u8 fan_div[4];		/* Static value */
+	u8 temp[6];		/* Register value */
+	u8 temp_max[6];		/* Static value */
+	u8 temp_hyst[6];	/* Static value */
+	u8 pll;			/* Register value */
+	u8 pll_min;		/* Register value */
+	u8 pll_max;		/* register value */
+	u8 vid[5];		/* Register value */
+	u8 vid_min[5];		/* Register value */
+	u8 vid_max[5];		/* Register value */
+	u8 lcd[4][17];		/* Four LCD lines */
+	u16 alarms;		/* Register encoding, combined */
+};
+
+
+static int maxi_attach_adapter(struct i2c_adapter *adapter);
+static int maxi_detect(struct i2c_adapter *adapter, int address,
+		       unsigned short flags, int kind);
+static int maxi_detach_client(struct i2c_client *client);
+
+static int maxi_read_value(struct i2c_client *client, u8 register);
+static int maxi_read_token(struct i2c_client *client, u16 token);
+#ifndef NOWRITE
+static int maxi_write_value(struct i2c_client *client, u8 register,
+			    u8 value);
+#endif
+static int maxi_write_token_loop(struct i2c_client *client, u16 token,
+				 u8 len, u8 * values);
+
+static void maxi_update_client(struct i2c_client *client);
+static void maxi99_update_client(struct i2c_client *client,
+				 enum sensor_type sensor, int which);
+static void maxi_init_client(struct i2c_client *client);
+
+static void maxi_fan(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results);
+static void maxi99_fan(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void maxi_temp(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void maxi99_temp(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void maxi_pll(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results);
+static void maxi_vid(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results);
+static void maxi99_vid(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void maxi_lcd(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results);
+static void maxi_alarms(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+
+/* The driver. I choose to use type i2c_driver, as at is identical to
+   the smbus_driver. */
+static struct i2c_driver maxi_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "HP MaxiLife driver",
+	.id		= I2C_DRIVERID_MAXILIFE,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= maxi_attach_adapter,
+	.detach_client	= maxi_detach_client,
+};
+
+static int maxi_id = 0;
+
+/* Default firmware version. Use module option "maxi_version"
+   to set desired version. Auto detect is not yet working */
+static int maxi_version = cristal;
+
+/* The /proc/sys entries */
+
+/* -- SENSORS SYSCTL START -- */
+#define MAXI_SYSCTL_FAN1   1101	/* Rotations/min */
+#define MAXI_SYSCTL_FAN2   1102	/* Rotations/min */
+#define MAXI_SYSCTL_FAN3   1103	/* Rotations/min */
+#define MAXI_SYSCTL_FAN4   1104	/* Rotations/min */
+#define MAXI_SYSCTL_TEMP1  1201	/* Degrees Celcius */
+#define MAXI_SYSCTL_TEMP2  1202	/* Degrees Celcius */
+#define MAXI_SYSCTL_TEMP3  1203	/* Degrees Celcius */
+#define MAXI_SYSCTL_TEMP4  1204	/* Degrees Celcius */
+#define MAXI_SYSCTL_TEMP5  1205	/* Degrees Celcius */
+#define MAXI_SYSCTL_TEMP6  1206	/* Degrees Celcius */
+#define MAXI_SYSCTL_PLL    1301	/* MHz */
+#define MAXI_SYSCTL_VID1   1401	/* Volts / 6.337, for nba just Volts */
+#define MAXI_SYSCTL_VID2   1402	/* Volts */
+#define MAXI_SYSCTL_VID3   1403	/* Volts */
+#define MAXI_SYSCTL_VID4   1404	/* Volts */
+#define MAXI_SYSCTL_VID5   1405	/* Volts */
+#define MAXI_SYSCTL_LCD1   1501	/* Line 1 of LCD */
+#define MAXI_SYSCTL_LCD2   1502	/* Line 2 of LCD */
+#define MAXI_SYSCTL_LCD3   1503	/* Line 3 of LCD */
+#define MAXI_SYSCTL_LCD4   1504	/* Line 4 of LCD */
+#define MAXI_SYSCTL_ALARMS 2001	/* Bitvector (see below) */
+
+#define MAXI_ALARM_VID4      0x0001
+#define MAXI_ALARM_TEMP2     0x0002
+#define MAXI_ALARM_VID1      0x0004
+#define MAXI_ALARM_VID2      0x0008
+#define MAXI_ALARM_VID3      0x0010
+#define MAXI_ALARM_PLL       0x0080
+#define MAXI_ALARM_TEMP4     0x0100
+#define MAXI_ALARM_TEMP5     0x0200
+#define MAXI_ALARM_FAN1      0x1000
+#define MAXI_ALARM_FAN2      0x2000
+#define MAXI_ALARM_FAN3      0x4000
+
+#define MAXI_ALARM_FAN       0x0100	/* To be used with  MaxiLife'99 */
+#define MAXI_ALARM_VID       0x0200	/* The MSB specifies which sensor */
+#define MAXI_ALARM_TEMP      0x0400	/* in the alarm group failed, i.e.: */
+#define MAXI_ALARM_VADD      0x0800	/* 0x0402 = TEMP2 failed = CPU2 temp */
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected MaxiLife processor.
+   This is just a template; though at first sight, you might think we
+   could use a statically allocated list, we need some way to get back
+   to the parent - which is done through one of the 'extra' fields 
+   which are initialized when a new copy is allocated. */
+static ctl_table maxi_dir_table_template[] = {
+	{MAXI_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_fan},
+	{MAXI_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_fan},
+	{MAXI_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_fan},
+	{MAXI_SYSCTL_FAN4, "fan4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_fan},
+	{MAXI_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_temp},
+	{MAXI_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_temp},
+	{MAXI_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_temp},
+	{MAXI_SYSCTL_TEMP4, "temp4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_temp},
+	{MAXI_SYSCTL_TEMP5, "temp5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_temp},
+	{MAXI_SYSCTL_TEMP6, "temp6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_temp},
+	{MAXI_SYSCTL_PLL, "pll", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_pll},
+	{MAXI_SYSCTL_VID1, "vid1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_vid},
+	{MAXI_SYSCTL_VID2, "vid2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_vid},
+	{MAXI_SYSCTL_VID3, "vid3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_vid},
+	{MAXI_SYSCTL_VID4, "vid4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_vid},
+	{MAXI_SYSCTL_VID5, "vid5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_vid},
+	{MAXI_SYSCTL_LCD1, "lcd1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_lcd},
+	{MAXI_SYSCTL_LCD2, "lcd2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_lcd},
+	{MAXI_SYSCTL_LCD3, "lcd3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_lcd},
+	{MAXI_SYSCTL_LCD4, "lcd4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_lcd},
+	{MAXI_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &maxi_alarms},
+	{0}
+};
+
+/* This function is called when:
+    - maxi_driver is inserted (when this module is loaded), for each
+      available adapter
+    - when a new adapter is inserted (and maxi_driver is still present) */
+static int maxi_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, maxi_detect);
+}
+
+/* This function is called by i2c_detect */
+int maxi_detect(struct i2c_adapter *adapter, int address,
+		unsigned short flags, int kind)
+{
+	struct i2c_client *new_client;
+	struct maxi_data *data;
+	enum maxi_type type = 0;
+	int i, j, err = 0;
+	const char *type_name = NULL, *client_name = NULL;
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		goto ERROR0;
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access maxi_{read,write}_value. */
+	if (!(data = kmalloc(sizeof(struct maxi_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	/* Fill the new client structure with data */
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &maxi_driver;
+	new_client->flags = 0;
+
+	/* Now we do the remaining detection. */
+	if (kind < 0) {
+		if (i2c_smbus_read_byte_data
+		    (new_client, MAXI_REG_MBX_STATUS) < 0)
+			goto ERROR2;
+	}
+
+	/* Determine the chip type - only one kind supported */
+	if (kind <= 0)
+		kind = maxilife;
+
+	if (kind == maxilife) {
+		/* Detect if the machine has a MaxiLife NBA controller.
+		   The right way to perform this check is to do a read/modify/write
+		   on register MbxStatus (5A):
+		   - Read 5A (value 0 for non-NBA firmware, FF (MbxIdle on NBA-firmware)
+		   - Write 55 on 5A, then read back 5A
+		   Non-NBA firmware: value is 55 (reg 5A is a standard writable reg)
+		   NBA firmaware: value is FF (write-protect on MbxStatus active) */
+		int stat;
+		i2c_smbus_write_byte_data(new_client, MAXI_REG_MBX_STATUS,
+					  0x55);
+		stat =
+		    i2c_smbus_read_byte_data(new_client,
+					     MAXI_REG_MBX_STATUS);
+
+		/*if (stat == MAXI_STAT_IDLE || stat == MAXI_STAT_OK) */
+		if (stat != 0x55)
+			maxi_version = nba;
+#ifdef AUTODETECT
+		else {
+			/* The right way to get the platform info is to read the firmware
+			   revision from serial EEPROM (addr=0x54), at offset 0x0045.
+			   This is a string as:
+			   "CG 00.04" -> Cristal [XU] / Geronimo [XAs]
+			   "CO 00.03" -> Cognac [XU]
+			   "AS 00.01" -> Ashaki [XA] */
+#if 0
+			int biosctl;
+			biosctl =
+			    i2c_smbus_read_byte_data(new_client,
+						     MAXI_REG_BIOS_CTRL);
+			i2c_smbus_write_byte_data(new_client,
+						  MAXI_REG_BIOS_CTRL,
+						  biosctl | 4);
+			err = eeprom_read_byte_data(adapter, 0x54, 0x45);
+			i2c_smbus_write_byte_data(new_client,
+						  MAXI_REG_BIOS_CTRL,
+						  biosctl);
+#endif
+			int i;
+			char *biosmem, *bm;
+			bm = biosmem = ioremap(0xe0000, 0x20000);
+			if (biosmem) {
+				printk("begin of bios search\n");
+				for (i = 0; i < 0x20000; i++) {
+					if (*bm == 'C') {
+						char *s = bm;
+						while (s && isprint(*s)) {
+							printk("%c", *s);
+							s++;
+						}
+						printk("\n");
+						if (!strncmp
+						    (bm, "CG 00.04", 8)) {
+							maxi_version =
+							    cristal;
+							printk
+							    ("maxilife: found MaxiLife Rev CG 00.04\n");
+							break;
+						}
+						if (!strncmp
+						    (bm, "CO 00.03", 8)) {
+							maxi_version =
+							    cognac;
+							printk
+							    ("maxilife: found MaxiLife Rev CO 00.03\n");
+							break;
+						}
+					}
+					if (*bm == 'A' && *(bm + 1) == 'S') {
+						char *s = bm;
+						while (s && isprint(*s)) {
+							printk("%c", *s);
+							s++;
+						}
+						printk("\n");
+						if (!strncmp
+						    (bm, "AS 00.01", 8)) {
+							maxi_version =
+							    ashaki;
+							printk
+							    ("maxilife: found MaxiLife Rev AS 00.01\n");
+							break;
+						}
+					}
+					bm++;
+				}
+				printk("end of bios search\n");
+			} else
+				printk("could not map bios memory\n");
+		}
+#endif
+
+		if (maxi_version == cristal) {
+			type = cristal;
+			type_name = "maxilife-cg";
+			client_name = "HP MaxiLife Rev CG 00.04";
+			printk
+			    ("maxilife: HP KAYAK XU/XAs (Dual Pentium II Slot 1)\n");
+		} else if (maxi_version == cognac) {
+			type = cognac;
+			type_name = "maxilife-co";
+			client_name = "HP MaxiLife Rev CO 00.03";
+			printk
+			    ("maxilife: HP KAYAK XU (Dual Xeon Slot 2 400/450 Mhz)\n");
+		} else if (maxi_version == ashaki) {
+			type = ashaki;
+			type_name = "maxilife-as";
+			client_name = "HP MaxiLife Rev AS 00.01";
+			printk
+			    ("maxilife: HP KAYAK XA (Pentium II Slot 1, monoprocessor)\n");
+		} else if (maxi_version == nba) {
+			type = nba;
+			type_name = "maxilife-nba";
+			client_name = "HP MaxiLife NBA";
+			printk("maxilife: HP KAYAK XU800/XM600\n");
+		} else {
+#ifdef AUTODETECT
+			printk
+			    ("maxilife: Warning: probed non-maxilife chip?!? (%x)\n",
+			     err);
+#else
+			printk
+			    ("maxilife: Error: specified wrong maxi_version (%d)\n",
+			     maxi_version);
+#endif
+			goto ERROR2;
+		}
+	}
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	((struct maxi_data *) (new_client->data))->type = type;
+
+	for (i = 0; i < 4; i++)
+		for (j = 0; j < 17; j++)
+			    ((struct maxi_data *) (new_client->data))->
+			    lcd[i][j] = (u8) 0;
+
+	new_client->id = maxi_id++;
+
+	data->valid = 0;
+	init_MUTEX(&data->lock);
+	init_MUTEX(&data->update_lock);
+
+	/* Tell i2c-core that a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR2;
+
+	/* Register a new directory entry with module sensors */
+	if ((err = i2c_register_entry(new_client, type_name,
+					  maxi_dir_table_template)) < 0)
+		goto ERROR4;
+	data->sysctl_id = err;
+
+	/* Initialize the MaxiLife chip */
+	maxi_init_client(new_client);
+	return 0;
+
+	/* OK, this is not exactly good programming practice, usually.
+	   But it is very code-efficient in this case. */
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR2:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+/* This function is called whenever a client should be removed:
+    - maxi_driver is removed (when this module is unloaded)
+    - when an adapter is removed which has a maxi client (and maxi_driver
+      is still present). */
+static int maxi_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct maxi_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("maxilife: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+	kfree(client->data);
+	return 0;
+}
+
+/* Read byte from specified register (-1 in case of error, value otherwise). */
+static int maxi_read_value(struct i2c_client *client, u8 reg)
+{
+	return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* Read the byte value for a MaxiLife token (-1 in case of error, value otherwise */
+static int maxi_read_token(struct i2c_client *client, u16 token)
+{
+	u8 lowToken, highToken;
+	int error, value;
+
+	lowToken = LOW(token);
+	highToken = HIGH(token);
+
+	/* Set mailbox status register to idle state. */
+	error =
+	    i2c_smbus_write_byte_data(client, MAXI_REG_MBX_STATUS,
+				      MAXI_STAT_IDLE);
+	if (error < 0)
+		return error;
+
+	/* Check for mailbox idle state. */
+	error = i2c_smbus_read_byte_data(client, MAXI_REG_MBX_STATUS);
+	if (error != MAXI_STAT_IDLE)
+		return -1;
+
+	/* Write the most significant byte of the token we want to read. */
+	error =
+	    i2c_smbus_write_byte_data(client, MAXI_REG_MBX_TOKEN_H,
+				      highToken);
+	if (error < 0)
+		return error;
+
+	/* Write the least significant byte of the token we want to read. */
+	error =
+	    i2c_smbus_write_byte_data(client, MAXI_REG_MBX_TOKEN_L,
+				      lowToken);
+	if (error < 0)
+		return error;
+
+	/* Write the read token opcode to the mailbox. */
+	error =
+	    i2c_smbus_write_byte_data(client, MAXI_REG_MBX_CMD,
+				      MAXI_CMD_READ);
+	if (error < 0)
+		return error;
+
+	/* Check for transaction completion */
+	do {
+		error =
+		    i2c_smbus_read_byte_data(client, MAXI_REG_MBX_STATUS);
+	} while (error == MAXI_STAT_BUSY);
+	if (error != MAXI_STAT_OK)
+		return -1;
+
+	/* Read the value of the token. */
+	value = i2c_smbus_read_byte_data(client, MAXI_REG_MBX_DATA);
+	if (value == -1)
+		return -1;
+
+	/* set mailbox status to idle to complete transaction. */
+	error =
+	    i2c_smbus_write_byte_data(client, MAXI_REG_MBX_STATUS,
+				      MAXI_STAT_IDLE);
+	if (error < 0)
+		return error;
+
+	return value;
+}
+
+#ifndef NOWRITE
+/* Write byte to specified register (-1 in case of error, 0 otherwise). */
+static int maxi_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+#endif
+
+/* Write a set of len byte values to MaxiLife token (-1 in case of error, 0 otherwise). */
+int maxi_write_token_loop(struct i2c_client *client, u16 token, u8 len,
+			  u8 * values)
+{
+	u8 lowToken, highToken, bCounter;
+	int error;
+
+	lowToken = LOW(token);
+	highToken = HIGH(token);
+
+	/* Set mailbox status register to idle state. */
+	error =
+	    i2c_smbus_write_byte_data(client, MAXI_REG_MBX_STATUS,
+				      MAXI_STAT_IDLE);
+	if (error < 0)
+		return error;
+
+	/* Check for mailbox idle state. */
+	error = i2c_smbus_read_byte_data(client, MAXI_REG_MBX_STATUS);
+	if (error != MAXI_STAT_IDLE)
+		return -1;
+
+	for (bCounter = 0; (bCounter < len && bCounter < 32); bCounter++) {
+		error =
+		    i2c_smbus_write_byte_data(client,
+					      (u8) (MAXI_REG_MBX_DATA +
+						    bCounter),
+					      values[bCounter]);
+		if (error < 0)
+			return error;
+	}
+
+	/* Write the most significant byte of the token we want to read. */
+	error =
+	    i2c_smbus_write_byte_data(client, MAXI_REG_MBX_TOKEN_H,
+				      highToken);
+	if (error < 0)
+		return error;
+
+	/* Write the least significant byte of the token we want to read. */
+	error =
+	    i2c_smbus_write_byte_data(client, MAXI_REG_MBX_TOKEN_L,
+				      lowToken);
+	if (error < 0)
+		return error;
+
+	/* Write the write token opcode to the mailbox. */
+	error =
+	    i2c_smbus_write_byte_data(client, MAXI_REG_MBX_CMD,
+				      MAXI_CMD_WRITE);
+	if (error < 0)
+		return error;
+
+	/* Check for transaction completion */
+	do {
+		error =
+		    i2c_smbus_read_byte_data(client, MAXI_REG_MBX_STATUS);
+	} while (error == MAXI_STAT_BUSY);
+	if (error != MAXI_STAT_OK)
+		return -1;
+
+	/* set mailbox status to idle to complete transaction. */
+	return i2c_smbus_write_byte_data(client, MAXI_REG_MBX_STATUS,
+					 MAXI_STAT_IDLE);
+}
+
+/* Called when we have found a new MaxiLife. It should set limits, etc. */
+static void maxi_init_client(struct i2c_client *client)
+{
+	struct maxi_data *data = client->data;
+
+	if (data->type == nba) {
+		strcpy(data->lcd[2], " Linux MaxiLife");
+		maxi_write_token_loop(client, MAXI_TOK_LCD(2),
+				      strlen(data->lcd[2]) + 1,
+				      data->lcd[2]);
+	}
+}
+
+static void maxi_update_client(struct i2c_client *client)
+{
+	struct maxi_data *data = client->data;
+	int i;
+
+	if (data->type == nba) {
+		printk
+		    ("maxi_update_client should never be called by nba\n");
+		return;
+	}
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("maxilife: Starting MaxiLife update\n");
+#endif
+		for (i = 0; i < 5; i++)
+			data->temp[i] =
+			    maxi_read_value(client, MAXI_REG_TEMP(i));
+		switch (data->type) {
+		case cristal:
+			data->temp[0] = 0;	/* not valid */
+			data->temp_max[0] = 0;
+			data->temp_hyst[0] = 0;
+			data->temp_max[1] = 110;	/* max PCI slot temp */
+			data->temp_hyst[1] = 100;
+			data->temp_max[2] = 120;	/* max BX chipset temp */
+			data->temp_hyst[2] = 110;
+			data->temp_max[3] = 100;	/* max HDD temp */
+			data->temp_hyst[3] = 90;
+			data->temp_max[4] = 120;	/* max CPU temp */
+			data->temp_hyst[4] = 110;
+			break;
+
+		case cognac:
+			data->temp_max[0] = 120;	/* max CPU1 temp */
+			data->temp_hyst[0] = 110;
+			data->temp_max[1] = 110;	/* max PCI slot temp */
+			data->temp_hyst[1] = 100;
+			data->temp_max[2] = 120;	/* max CPU2 temp */
+			data->temp_hyst[2] = 110;
+			data->temp_max[3] = 100;	/* max HDD temp */
+			data->temp_hyst[3] = 90;
+			data->temp_max[4] = 120;	/* max reference CPU temp */
+			data->temp_hyst[4] = 110;
+			break;
+
+		case ashaki:
+			data->temp[0] = 0;	/* not valid */
+			data->temp_max[0] = 0;
+			data->temp_hyst[0] = 0;
+			data->temp_max[1] = 110;	/* max PCI slot temp */
+			data->temp_hyst[1] = 100;
+			data->temp[2] = 0;	/* not valid */
+			data->temp_max[2] = 0;
+			data->temp_hyst[2] = 0;
+			data->temp_max[3] = 100;	/* max HDD temp */
+			data->temp_hyst[3] = 90;
+			data->temp_max[4] = 120;	/* max CPU temp */
+			data->temp_hyst[4] = 110;
+			break;
+
+		default:
+			printk("maxilife: Unknown MaxiLife chip\n");
+		}
+		data->temp[5] = 0;	/* only used by MaxiLife'99 */
+		data->temp_max[5] = 0;
+		data->temp_hyst[5] = 0;
+
+		for (i = 0; i < 3; i++) {
+			data->fan[i] =
+			    maxi_read_value(client, MAXI_REG_FAN(i));
+			data->fan_speed[i] =
+			    maxi_read_value(client, MAXI_REG_FAN_SPEED(i));
+			data->fan_div[i] = 4;
+			if (data->type == ashaki)
+				data->fan_min[i] =
+				    maxi_read_value(client,
+						    MAXI_REG_FAN_MINAS(i));
+			else
+				data->fan_min[i] =
+				    maxi_read_value(client,
+						    MAXI_REG_FAN_MIN(i));
+		}
+		data->fan[3] = 0xff;	/* only used by MaxiLife'99 */
+		data->fan_speed[3] = 0;
+		data->fan_div[3] = 4;	/* avoid possible /0 */
+		data->fan_min[3] = 0;
+
+		data->pll = maxi_read_value(client, MAXI_REG_PLL);
+		data->pll_min = maxi_read_value(client, MAXI_REG_PLL_MIN);
+		data->pll_max = maxi_read_value(client, MAXI_REG_PLL_MAX);
+
+		for (i = 0; i < 4; i++) {
+			data->vid[i] =
+			    maxi_read_value(client, MAXI_REG_VID(i));
+			data->vid_min[i] =
+			    maxi_read_value(client, MAXI_REG_VID_MIN(i));
+			data->vid_max[i] =
+			    maxi_read_value(client, MAXI_REG_VID_MAX(i));
+		}
+		switch (data->type) {
+		case cristal:
+			data->vid[3] = 0;	/* no voltage cache L2 */
+			data->vid_min[3] = 0;
+			data->vid_max[3] = 0;
+			break;
+
+		case cognac:
+			break;
+
+		case ashaki:
+			data->vid[1] = 0;	/* no voltage CPU 2 */
+			data->vid_min[1] = 0;
+			data->vid_max[1] = 0;
+			data->vid[3] = 0;	/* no voltage cache L2 */
+			data->vid_min[3] = 0;
+			data->vid_max[3] = 0;
+			break;
+
+		default:
+			printk("maxilife: Unknown MaxiLife chip\n");
+		}
+		data->vid[4] = 0;	/* only used by MaxliLife'99 */
+		data->vid_min[4] = 0;
+		data->vid_max[4] = 0;
+
+		data->alarms = maxi_read_value(client, MAXI_REG_DIAG_RT1) +
+		    (maxi_read_value(client, MAXI_REG_DIAG_RT2) << 8);
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+void maxi99_update_client(struct i2c_client *client,
+			  enum sensor_type sensor, int which)
+{
+	static unsigned long last_updated[6][6];	/* sensor, which */
+	struct maxi_data *data = client->data;
+
+	down(&data->update_lock);
+
+	/*maxi_write_token_loop(client, MAXI_TOK_LCD_LINE3, 13, "Linux 2.2.13"); */
+
+	if ((jiffies - last_updated[sensor][which] > 2 * HZ) ||
+	    (jiffies < last_updated[sensor][which]
+	     || !last_updated[sensor][which])) {
+
+		int tmp, i;
+
+		switch (sensor) {
+		case fan:
+			for (i = 0; i < 4; i++) {
+				if (i == which) {
+					tmp =
+					    maxi_read_token(client,
+							    MAXI_TOK_FAN
+							    (i));
+					data->fan[i] =
+					    maxi_read_token(client,
+							    MAXI_TOK_FAN
+							    (i));
+					data->fan_speed[i] =
+					    maxi_read_token(client,
+							    MAXI_TOK_MAX
+							    (MAXI_TOK_FAN
+							     (i)));
+					data->fan_div[i] = 1;
+					data->fan_min[i] = 0;
+				}
+			}
+			break;
+
+		case temp:
+			for (i = 0; i < 6; i++) {
+				if (i == which) {
+					data->temp[i] =
+					    maxi_read_token(client,
+							    MAXI_TOK_TEMP
+							    (i));
+					data->temp_max[i] =
+					    maxi_read_token(client,
+							    MAXI_TOK_MAX
+							    (MAXI_TOK_TEMP
+							     (i)));
+					data->temp_hyst[i] =
+					    data->temp_max[i] - 5;
+				}
+			}
+			break;
+
+		case vid:
+			for (i = 0; i < 5; i++) {
+				if (i == which) {
+					data->vid[i] =
+					    maxi_read_token(client,
+							    MAXI_TOK_VID
+							    (i));
+					data->vid_min[i] =
+					    maxi_read_token(client,
+							    MAXI_TOK_MIN
+							    (MAXI_TOK_VID
+							     (i)));
+					data->vid_max[i] =
+					    maxi_read_token(client,
+							    MAXI_TOK_MAX
+							    (MAXI_TOK_VID
+							     (i)));
+				}
+			}
+			break;
+
+		case pll:
+			data->pll = 0;
+			data->pll_min = 0;
+			data->pll_max = 0;
+			break;
+
+		case alarm:
+			data->alarms =
+			    (maxi_read_token(client, MAXI_TOK_ALARM_EVENT)
+			     << 8);
+			if (data->alarms)
+				data->alarms +=
+				    data->alarms ==
+				    (1 << 8) ? maxi_read_token(client,
+							       MAXI_TOK_ALARM_FAN)
+				    : data->alarms ==
+				    (2 << 8) ? maxi_read_token(client,
+							       MAXI_TOK_ALARM_VID)
+				    : data->alarms ==
+				    (4 << 8) ? maxi_read_token(client,
+							       MAXI_TOK_ALARM_TEMP)
+				    : data->alarms ==
+				    (8 << 8) ? maxi_read_token(client,
+							       MAXI_TOK_ALARM_FAN)
+				    : 0;
+			break;
+
+		default:
+			printk("maxilife: Unknown sensor type\n");
+		}
+
+		last_updated[sensor][which] = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+/* The next few functions are the call-back functions of the /proc/sys and
+   sysctl files. Which function is used is defined in the ctl_table in
+   the extra1 field.
+   Each function must return the magnitude (power of 10 to divide the data
+   with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must
+   put a maximum of *nrels elements in results reflecting the data of this
+   file, and set *nrels to the number it actually put in it, if operation==
+   SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from
+   results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE.
+   Note that on SENSORS_PROC_REAL_READ, I do not check whether results is
+   large enough (by checking the incoming value of *nrels). This is not very
+   good practice, but as long as you put less than about 5 values in results,
+   you can assume it is large enough. */
+void maxi_fan(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct maxi_data *data = client->data;
+	int nr;
+
+	if (data->type == nba) {
+		maxi99_fan(client, operation, ctl_name, nrels_mag,
+			   results);
+		return;
+	}
+
+	nr = ctl_name - MAXI_SYSCTL_FAN1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		maxi_update_client(client);
+		results[0] = FAN_FROM_REG(data->fan_min[nr - 1]);
+		results[1] = data->fan_div[nr - 1];
+		results[2] = FAN_FROM_REG(data->fan[nr - 1]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+#ifndef NOWRITE
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr - 1] = FAN_TO_REG(results[0]);
+			maxi_write_value(client, MAXI_REG_FAN_MIN(nr),
+					 data->fan_min[nr - 1]);
+		}
+#endif
+	}
+}
+
+void maxi99_fan(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct maxi_data *data = client->data;
+	int nr;
+
+	nr = ctl_name - MAXI_SYSCTL_FAN1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		maxi99_update_client(client, fan, nr - 1);
+		results[0] = FAN99_FROM_REG(data->fan_min[nr - 1]);	/* min rpm */
+		results[1] = data->fan_div[nr - 1];	/* divisor */
+		results[2] = FAN99_FROM_REG(data->fan[nr - 1]);	/* rpm */
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+#ifndef NOWRITE
+		/* still to do */
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr - 1] = FAN_TO_REG(results[0]);
+			maxi_write_value(client, MAXI_REG_FAN_MIN(nr),
+					 data->fan_min[nr - 1]);
+		}
+#endif
+	}
+}
+
+void maxi_temp(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	struct maxi_data *data = client->data;
+	int nr;
+
+	if (data->type == nba) {
+		maxi99_temp(client, operation, ctl_name, nrels_mag,
+			    results);
+		return;
+	}
+
+	nr = ctl_name - MAXI_SYSCTL_TEMP1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		maxi_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_max[nr - 1]);
+		results[1] = TEMP_FROM_REG(data->temp_hyst[nr - 1]);
+		results[2] = TEMP_FROM_REG(data->temp[nr - 1]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		/* temperature range can not be changed */
+	}
+}
+
+void maxi99_temp(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct maxi_data *data = client->data;
+	int nr;
+
+	nr = ctl_name - MAXI_SYSCTL_TEMP1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		maxi99_update_client(client, temp, nr - 1);
+		results[0] = TEMP99_FROM_REG(data->temp_max[nr - 1]);
+		results[1] = TEMP99_FROM_REG(data->temp_hyst[nr - 1]);
+		results[2] = TEMP99_FROM_REG(data->temp[nr - 1]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		/* temperature range can not be changed */
+	}
+}
+
+void maxi_pll(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct maxi_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		if (data->type == nba)
+			maxi99_update_client(client, pll, 0);
+		else
+			maxi_update_client(client);
+		results[0] = PLL_FROM_REG(data->pll_min);
+		results[1] = PLL_FROM_REG(data->pll_max);
+		results[2] = PLL_FROM_REG(data->pll);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+#ifndef NOWRITE
+		if (*nrels_mag >= 1) {
+			data->pll_min = PLL_TO_REG(results[0]);
+			maxi_write_value(client, MAXI_REG_PLL_MIN,
+					 data->pll_min);
+		}
+		if (*nrels_mag >= 2) {
+			data->pll_max = PLL_TO_REG(results[1]);
+			maxi_write_value(client, MAXI_REG_PLL_MAX,
+					 data->pll_max);
+		}
+#endif
+	}
+}
+
+void maxi_vid(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	struct maxi_data *data = client->data;
+	int nr;
+
+	if (data->type == nba) {
+		maxi99_vid(client, operation, ctl_name, nrels_mag,
+			   results);
+		return;
+	}
+
+	nr = ctl_name - MAXI_SYSCTL_VID1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 4;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		maxi_update_client(client);
+		results[0] = VID_FROM_REG(data->vid_min[nr - 1]);
+		results[1] = VID_FROM_REG(data->vid_max[nr - 1]);
+		results[2] = VID_FROM_REG(data->vid[nr - 1]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+#ifndef NOWRITE
+		if (*nrels_mag >= 1) {
+			data->vid_min[nr - 1] = VID_TO_REG(results[0]);
+			maxi_write_value(client, MAXI_REG_VID_MIN(nr),
+					 data->vid_min[nr - 1]);
+		}
+		if (*nrels_mag >= 2) {
+			data->vid_max[nr - 1] = VID_TO_REG(results[1]);
+			maxi_write_value(client, MAXI_REG_VID_MAX(nr),
+					 data->vid_max[nr - 1]);
+		}
+#endif
+	}
+}
+
+void maxi99_vid(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct maxi_data *data = client->data;
+	int nr = ctl_name - MAXI_SYSCTL_VID1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 4;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		maxi99_update_client(client, vid, nr - 1);
+		results[0] = VID99_FROM_REG(nr, data->vid_min[nr - 1]);
+		results[1] = VID99_FROM_REG(nr, data->vid_max[nr - 1]);
+		results[2] = VID99_FROM_REG(nr, data->vid[nr - 1]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+#ifndef NOWRITE
+		/* still to do */
+		if (*nrels_mag >= 1) {
+			data->vid_min[nr - 1] = VID_TO_REG(results[0]);
+			maxi_write_value(client, MAXI_REG_VID_MIN(nr),
+					 data->vid_min[nr - 1]);
+		}
+		if (*nrels_mag >= 2) {
+			data->vid_max[nr - 1] = VID_TO_REG(results[1]);
+			maxi_write_value(client, MAXI_REG_VID_MAX(nr),
+					 data->vid_max[nr - 1]);
+		}
+#endif
+	}
+}
+
+void maxi_lcd(struct i2c_client *client, int operation, int ctl_name,
+	      int *nrels_mag, long *results)
+{
+	/* Allows writing and reading from LCD display */
+
+	struct maxi_data *data = client->data;
+	int nr;
+
+	if (data->type != nba)
+		return;
+
+	nr = ctl_name - MAXI_SYSCTL_LCD1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = *((long *) &data->lcd[nr - 1][0]);
+		results[1] = *((long *) &data->lcd[nr - 1][4]);
+		results[2] = *((long *) &data->lcd[nr - 1][8]);
+		results[3] = *((long *) &data->lcd[nr - 1][12]);
+		*nrels_mag = 4;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		/* 
+		   Writing a string to line 3 of the LCD can be done like:
+		   echo -n "Linux MaxiLife" | od -A n -l > \
+		   /proc/sys/dev/sensors/maxilife-nba-i2c-0-14/lcd3
+		 */
+		if (*nrels_mag >= 1)
+			*((long *) &data->lcd[nr - 1][0]) = results[0];
+		if (*nrels_mag >= 2)
+			*((long *) &data->lcd[nr - 1][4]) = results[1];
+		if (*nrels_mag >= 3)
+			*((long *) &data->lcd[nr - 1][8]) = results[2];
+		if (*nrels_mag >= 4)
+			*((long *) &data->lcd[nr - 1][12]) = results[3];
+		maxi_write_token_loop(client, MAXI_TOK_LCD(nr - 1),
+				      strlen(data->lcd[nr - 1]) + 1,
+				      data->lcd[nr - 1]);
+#if 0
+		if (*nrels_mag >= 1)
+			printk("nr=%d, result[0] = %.4s\n", nr,
+			       (char *) &results[0]);
+		if (*nrels_mag >= 2)
+			printk("nr=%d, result[1] = %.4s\n", nr,
+			       (char *) &results[1]);
+		if (*nrels_mag >= 3)
+			printk("nr=%d, result[2] = %.4s\n", nr,
+			       (char *) &results[2]);
+		if (*nrels_mag >= 4)
+			printk("nr=%d, result[3] = %.4s\n", nr,
+			       (char *) &results[3]);
+#endif
+	}
+
+}
+
+void maxi_alarms(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct maxi_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		if (data->type == nba)
+			maxi99_update_client(client, alarm, 0);
+		else
+			maxi_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->alarms);
+		*nrels_mag = 1;
+	}
+}
+
+static int __init sm_maxilife_init(void)
+{
+	printk("maxilife: Version %s (lm_sensors %s (%s))\n", version_str,
+	       LM_VERSION, LM_DATE);
+	return i2c_add_driver(&maxi_driver);
+}
+
+static void __exit sm_maxilife_exit(void)
+{
+	i2c_del_driver(&maxi_driver);
+}
+
+
+
+MODULE_AUTHOR("Fons Rademakers <Fons.Rademakers@cern.ch>");
+MODULE_DESCRIPTION("HP MaxiLife driver");
+MODULE_PARM(maxi_version, "i");
+MODULE_PARM_DESC(maxi_version, "MaxiLife firmware version");
+
+module_init(sm_maxilife_init);
+module_exit(sm_maxilife_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/mtp008.c linux-2.4.27-leo/drivers/sensors/mtp008.c
--- linux-2.4.27/drivers/sensors/mtp008.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/mtp008.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,1101 @@
+/*
+   mtp008.c - Part of lm_sensors, Linux kernel modules for hardware
+   monitoring
+   Copyright (c) 2001  Kris Van Hees <aedil@alchar.org>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = {SENSORS_I2C_END};
+static unsigned short normal_i2c_range[] = {0x2c, 0x2e, SENSORS_I2C_END};
+static unsigned int normal_isa[] = {SENSORS_ISA_END};
+static unsigned int normal_isa_range[] = {SENSORS_ISA_END};
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(mtp008);
+
+/* The MTP008 registers */
+/*      in0 .. in6 */
+#define MTP008_REG_IN(nr)		(0x20 + (nr))
+#define MTP008_REG_IN_MAX(nr)		(0x2b + (nr) * 2)
+#define MTP008_REG_IN_MIN(nr)		(0x2c + (nr) * 2)
+
+/*      temp1 */
+#define MTP008_REG_TEMP			0x27
+#define MTP008_REG_TEMP_MAX		0x39
+#define MTP008_REG_TEMP_MIN		0x3a
+
+/*      fan1 .. fan3 */
+#define MTP008_REG_FAN(nr)		(0x27 + (nr))
+#define MTP008_REG_FAN_MIN(nr)		(0x3a + (nr))
+
+#define MTP008_REG_CONFIG		0x40
+#define MTP008_REG_INT_STAT1		0x41
+#define MTP008_REG_INT_STAT2		0x42
+
+#define MTP008_REG_SMI_MASK1		0x43
+#define MTP008_REG_SMI_MASK2		0x44
+
+#define MTP008_REG_NMI_MASK1		0x45
+#define MTP008_REG_NMI_MASK2		0x46
+
+#define MTP008_REG_VID_FANDIV		0x47
+
+#define MTP008_REG_I2C_ADDR		0x48
+
+#define MTP008_REG_RESET_VID4		0x49
+
+#define MTP008_REG_OVT_PROP		0x50
+
+#define MTP008_REG_BEEP_CTRL1		0x51
+#define MTP008_REG_BEEP_CTRL2		0x52
+
+/*      pwm1 .. pwm3  nr range 1-3 */
+#define MTP008_REG_PWM_CTRL(nr)		(0x52 + (nr))
+
+#define MTP008_REG_PIN_CTRL1		0x56
+#define MTP008_REG_PIN_CTRL2		0x57
+
+#define MTP008_REG_CHIPID		0x58
+
+/*
+ * Pin control register configuration constants.
+ */
+#define MTP008_CFG_VT1_PII		0x08
+#define MTP008_CFG_VT2_AIN		0x00
+#define MTP008_CFG_VT2_VT		0x03
+#define MTP008_CFG_VT2_PII		0x04
+#define MTP008_CFG_VT2_MASK		0x06
+#define MTP008_CFG_VT3_VT		0x01
+
+/* sensor pin types */
+#define VOLTAGE		1
+#define THERMISTOR	2
+#define PIIDIODE	3
+
+/*
+ * Conversion routines and macros.  Limit checking is only done on
+ * the TO_REG variants.
+ *
+ * Note that IN values are expressed as 100 times the actual voltage to avoid
+ * having to use floating point values.  As such, IN values are between 0 and
+ * 409 (0V to 4.096V).
+ */
+#define IN_TO_REG(val)		(SENSORS_LIMIT((((val) * 10 + 8) / 16), 0, 255))
+#define IN_FROM_REG(val)	(((val) * 16 + 5) / 10)
+
+/*
+ * The fan cotation count (as stored in the register) is calculated using the
+ * following formula:
+ *      count = (22.5K * 60) / (rpm * div) = 1350000 / (rpm * div)
+ * and the rpm is therefore:
+ *      rpm = 1350000 / (count * div)
+ */
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+	if (rpm == 0)
+		return 255;
+
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+
+	return SENSORS_LIMIT(
+		 (1350000 + rpm * div / 2) / (rpm * div),
+		 1, 254
+	       );
+}
+
+#define FAN_FROM_REG(val, div)	((val) == 0 ? -1			      \
+					    : (val) == 255 ? 0		      \
+							   : 1350000 /	      \
+							     ((val) * (div))  \
+				)
+
+/*
+ * Temperatures are stored as two's complement values of the Celsius value.  It
+ * actually uses 10 times the Celsius value to avoid using floating point
+ * values.
+ */
+#define TEMP_TO_REG(val)	(					      \
+				 (val) < 0				      \
+				    ? SENSORS_LIMIT(((val) - 5) / 10, 0, 255) \
+				    : SENSORS_LIMIT(((val) + 5) / 10, 0, 255) \
+				)
+#define TEMP_FROM_REG(val)	(					      \
+				 (					      \
+				  (val) > 0x80 ? (val) - 0x100		      \
+					       : (val)			      \
+				 ) * 10					      \
+				)
+
+/*
+ * VCORE voltage:
+ *      0x00 to 0x0f    = 2.05 to 1.30 (0.05 per unit)
+ *      0x10 to 0x1e    = 3.50 to 2.10 (0.10 per unit)
+ *      0x1f            = No CPU
+ */
+#define VID_FROM_REG(val)	((val) == 0x1f				      \
+					 ? 0				      \
+					 : (val) < 0x10 ? 205 - (val) * 5     \
+							: 510 - (val) * 10)
+
+/*
+ * Fan divider.
+ */
+#define DIV_FROM_REG(val)	(1 << (val))
+#define DIV_TO_REG(val)		((val) == 8 ? 3				      \
+					    : (val) == 4 ? 2		      \
+							 : (val) == 2 ? 1     \
+								      : 0)
+
+/*
+ * Alarms (interrupt status).
+ */
+#define ALARMS_FROM_REG(val)	(val)
+
+/*
+ * Beep controls.
+ */
+#define BEEPS_FROM_REG(val)	(val)
+#define BEEPS_TO_REG(val)	(val)
+
+/*
+ * PWM control. nr range 1 to 3
+ */
+#define PWM_FROM_REG(val)	(val)
+#define PWM_TO_REG(val)		(val)
+#define PWMENABLE_FROM_REG(nr, val)	(((val) >> ((nr) + 3)) & 1)
+
+/*
+ * For each registered MTP008, we need to keep some data in memory.  The
+ * structure itself is dynamically allocated, at the same time when a new
+ * mtp008 client is allocated.
+ */
+struct mtp008_data {
+	struct i2c_client client;
+	int sysctl_id;
+	enum chips type;
+
+	struct semaphore update_lock;
+	char valid;				/* !=0 if fields are valid */
+	unsigned long last_updated;		/* In jiffies */
+
+	u8 in[7];				/* Register value */
+	u8 in_max[7];				/* Register value */
+	u8 in_min[7];				/* Register value */
+	u8 temp;				/* Register value */
+	u8 temp_max;				/* Register value */
+	u8 temp_min;				/* Register value */
+	u8 fan[3];				/* Register value */
+	u8 fan_min[3];				/* Register value */
+	u8 vid;					/* Register encoding */
+	u8 fan_div[3];				/* Register encoding */
+	u16 alarms;				/* Register encoding */
+	u16 beeps;				/* Register encoding */
+	u8 pwm[4];				/* Register value */
+	u8 sens[3];				/* 1 = Analog input,
+						   2 = Thermistor,
+						   3 = PII/Celeron diode */
+	u8 pwmenable;				/* Register 0x57 value */
+};
+
+static int mtp008_attach_adapter(struct i2c_adapter *adapter);
+static int mtp008_detect(struct i2c_adapter *adapter, int address,
+			 unsigned short flags, int kind);
+static int mtp008_detach_client(struct i2c_client *client);
+
+static int mtp008_read_value(struct i2c_client *client, u8 register);
+static int mtp008_write_value(struct i2c_client *client, u8 register, u8 value);
+static void mtp008_update_client(struct i2c_client *client);
+static void mtp008_init_client(struct i2c_client *client);
+
+static void mtp008_in(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results);
+static void mtp008_fan(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void mtp008_temp(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void mtp008_temp_add(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void mtp008_vid(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void mtp008_fan_div(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+static void mtp008_alarms(struct i2c_client *client, int operation,
+			  int ctl_name, int *nrels_mag, long *results);
+static void mtp008_beep(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void mtp008_pwm(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void mtp008_sens(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void mtp008_getsensortype(struct mtp008_data *data, u8 inp);
+
+static int mtp008_id = 0;
+
+static struct i2c_driver mtp008_driver =
+{
+	.owner		= THIS_MODULE,
+	.name		= "MTP008 sensor driver",
+	.id		= I2C_DRIVERID_MTP008,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= mtp008_attach_adapter,
+	.detach_client	= mtp008_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+#define MTP008_SYSCTL_IN0	1000	/* Volts * 100 */
+#define MTP008_SYSCTL_IN1	1001
+#define MTP008_SYSCTL_IN2	1002
+#define MTP008_SYSCTL_IN3	1003
+#define MTP008_SYSCTL_IN4	1004
+#define MTP008_SYSCTL_IN5	1005
+#define MTP008_SYSCTL_IN6	1006
+#define MTP008_SYSCTL_FAN1	1101	/* Rotations/min */
+#define MTP008_SYSCTL_FAN2	1102
+#define MTP008_SYSCTL_FAN3	1103
+#define MTP008_SYSCTL_TEMP1	1200	/* Degrees Celcius * 10 */
+#define MTP008_SYSCTL_TEMP2	1201	/* Degrees Celcius * 10 */
+#define MTP008_SYSCTL_TEMP3	1202	/* Degrees Celcius * 10 */
+#define MTP008_SYSCTL_VID	1300	/* Volts * 100 */
+#define MTP008_SYSCTL_PWM1	1401
+#define MTP008_SYSCTL_PWM2	1402
+#define MTP008_SYSCTL_PWM3	1403
+#define MTP008_SYSCTL_SENS1	1501	/* 1, 2, or Beta (3000-5000) */
+#define MTP008_SYSCTL_SENS2	1502
+#define MTP008_SYSCTL_SENS3	1503
+#define MTP008_SYSCTL_FAN_DIV	2000	/* 1, 2, 4 or 8 */
+#define MTP008_SYSCTL_ALARMS	2001	/* bitvector */
+#define MTP008_SYSCTL_BEEP	2002	/* bitvector */
+
+#define MTP008_ALARM_IN0	0x0001
+#define MTP008_ALARM_IN1	0x0002
+#define MTP008_ALARM_IN2	0x0004
+#define MTP008_ALARM_IN3	0x0008
+#define MTP008_ALARM_IN4	0x0100
+#define MTP008_ALARM_IN5	0x0200
+#define MTP008_ALARM_IN6	0x0400
+#define MTP008_ALARM_FAN1	0x0040
+#define MTP008_ALARM_FAN2	0x0080
+#define MTP008_ALARM_FAN3	0x0800
+#define MTP008_ALARM_TEMP1	0x0010
+#define MTP008_ALARM_TEMP2	0x0100
+#define MTP008_ALARM_TEMP3	0x0200
+
+/* -- SENSORS SYSCTL END -- */
+
+/* The /proc/sys entries */
+/* These files are created for each detected chip. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized 
+   when a new copy is allocated. */
+
+static ctl_table mtp008_dir_table_template[] =
+{
+	{MTP008_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in},
+	{MTP008_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in},
+	{MTP008_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in},
+	{MTP008_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in},
+	{MTP008_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in},
+	{MTP008_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in},
+	{MTP008_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in},
+	{MTP008_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_fan},
+	{MTP008_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_fan},
+	{MTP008_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_fan},
+	{MTP008_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_temp},
+	{MTP008_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL,
+       &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_temp_add},
+	{MTP008_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL,
+       &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_temp_add},
+	{MTP008_SYSCTL_VID, "vid", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_vid},
+	{MTP008_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_fan_div},
+	{MTP008_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_alarms},
+	{MTP008_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_beep},
+	{MTP008_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_pwm},
+	{MTP008_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_pwm},
+	{MTP008_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_pwm},
+	{MTP008_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_sens},
+	{MTP008_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_sens},
+	{MTP008_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_sens},
+	{0}
+};
+
+/* This function is called when:
+ * mtp008_driver is inserted (when this module is loaded), for each available
+ * adapter when a new adapter is inserted (and mtp008_driver is still present)
+ */
+static int mtp008_attach_adapter(struct i2c_adapter *adapter)
+{
+	struct i2c_client_address_data mtp008_addr_data;
+
+	mtp008_addr_data.normal_i2c = addr_data.normal_i2c;
+	mtp008_addr_data.normal_i2c_range = addr_data.normal_i2c_range;
+	mtp008_addr_data.probe = addr_data.probe;
+	mtp008_addr_data.probe_range = addr_data.probe_range;
+	mtp008_addr_data.ignore = addr_data.ignore;
+	mtp008_addr_data.ignore_range = addr_data.ignore_range;
+	mtp008_addr_data.force = addr_data.forces->force;
+
+	return i2c_probe(adapter, &mtp008_addr_data, mtp008_detect);
+}
+
+int mtp008_detect(struct i2c_adapter *adapter, int address,
+		  unsigned short flags, int kind)
+{
+	const char *type_name = "";
+	const char *client_name = "";
+	int is_isa, err, sysid;
+	struct i2c_client *new_client;
+	struct mtp008_data *data;
+
+	err = 0;
+
+	is_isa = i2c_is_isa_adapter(adapter);
+	if (is_isa ||
+	    !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		goto ERROR0;
+
+	/*
+	 * We presume we have a valid client.  We now create the client
+	 * structure, even though we cannot fill it completely yet.  But it
+	 * allows us to use mtp008_(read|write)_value().
+	 */
+	if (!(data = kmalloc(sizeof(struct mtp008_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &mtp008_driver;
+	new_client->flags = 0;
+
+	/*
+	 * Remaining detection.
+	 */
+	if (kind < 0) {
+		if (mtp008_read_value(new_client, MTP008_REG_CHIPID) != 0xac)
+			goto ERROR1;
+	}
+	/*
+	 * Fill in the remaining client fields and put it into the global list.
+	 */
+	type_name = "mtp008";
+	client_name = "MTP008 chip";
+	strcpy(new_client->name, client_name);
+	data->type = kind;
+
+	new_client->id = mtp008_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/*
+	 * Tell the I2C layer that a new client has arrived.
+	 */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR1;
+
+	/*
+	 * Register a new directory entry with the sensors module.
+	 */
+	if ((sysid = i2c_register_entry(new_client, type_name,
+					    mtp008_dir_table_template)) < 0) {
+		err = sysid;
+		goto ERROR2;
+	}
+	data->sysctl_id = sysid;
+
+	/*
+	 * Initialize the MTP008 chip.
+	 */
+	mtp008_init_client(new_client);
+
+	return 0;
+
+	/*
+	 * Error handling.  Bad programming practise but very code efficient.
+	 */
+      ERROR2:
+	i2c_detach_client(new_client);
+      ERROR1:
+	kfree(data);
+
+      ERROR0:
+	return err;
+}
+
+static int mtp008_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(
+		((struct mtp008_data *) (client->data))->sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk("mtp008.o: Deregistration failed, "
+		       "client not detached.\n");
+		return err;
+	}
+	kfree(client->data);
+
+	return 0;
+}
+
+
+static int mtp008_read_value(struct i2c_client *client, u8 reg)
+{
+	return i2c_smbus_read_byte_data(client, reg) & 0xff;
+}
+
+static int mtp008_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* Called when we have found a new MTP008. It should set limits, etc. */
+static void mtp008_init_client(struct i2c_client *client)
+{
+	u8 save1, save2;
+	struct mtp008_data *data;
+
+	data = client->data;
+
+	/*
+	 * Initialize the Myson MTP008 hardware monitoring chip.
+	 * Save the pin settings that the BIOS hopefully set.
+	 */
+	save1 = mtp008_read_value(client, MTP008_REG_PIN_CTRL1);
+	save2 = mtp008_read_value(client, MTP008_REG_PIN_CTRL2);
+	mtp008_write_value(client, MTP008_REG_CONFIG,
+	     (mtp008_read_value(client, MTP008_REG_CONFIG) & 0x7f) | 0x80);
+	mtp008_write_value(client, MTP008_REG_PIN_CTRL1, save1);
+	mtp008_write_value(client, MTP008_REG_PIN_CTRL2, save2);
+
+	mtp008_getsensortype(data, save2);
+
+
+	/*
+	 * Start monitoring.
+	 */
+	mtp008_write_value(
+		client, MTP008_REG_CONFIG,
+		(mtp008_read_value(client, MTP008_REG_CONFIG) & 0xf7) | 0x01
+	);
+}
+
+static void mtp008_update_client(struct i2c_client *client)
+{
+	int i;
+	u8 inp;
+	struct mtp008_data *data;
+
+	data = client->data;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+#ifdef DEBUG
+		printk("Starting MTP008 update\n");
+#endif
+
+		/*
+		 * Read in the analog inputs.  We're reading AIN4 and AIN5 as
+		 * regular analog inputs, even though they may have been
+		 * configured as temperature readings instead.  Interpretation
+		 * of these values is done elsewhere.
+		 */
+		for (i = 0; i < 7; i++) {
+			data->in[i] =
+				mtp008_read_value(client, MTP008_REG_IN(i));
+			data->in_max[i] =
+				mtp008_read_value(client, MTP008_REG_IN_MAX(i));
+			data->in_min[i] =
+				mtp008_read_value(client, MTP008_REG_IN_MIN(i));
+		}
+
+		/*
+		 * Read the temperature sensor.
+		 */
+		data->temp = mtp008_read_value(client, MTP008_REG_TEMP);
+		data->temp_max = mtp008_read_value(client, MTP008_REG_TEMP_MAX);
+		data->temp_min = mtp008_read_value(client, MTP008_REG_TEMP_MIN);
+
+		/*
+		 * Read the first 2 fan dividers and the VID setting.  Read the
+		 * third fan divider from a different register.
+		 */
+		inp = mtp008_read_value(client, MTP008_REG_VID_FANDIV);
+		data->vid = inp & 0x0f;
+		data->vid |= (mtp008_read_value(client,
+				     MTP008_REG_RESET_VID4) & 0x01) << 4;
+
+		data->fan_div[0] = (inp >> 4) & 0x03;
+		data->fan_div[1] = inp >> 6;
+		data->fan_div[2] =
+			mtp008_read_value(client, MTP008_REG_PIN_CTRL1) >> 6;
+
+		/*
+		 * Read the interrupt status registers.
+		 */
+		data->alarms =
+			(mtp008_read_value(client,
+					   MTP008_REG_INT_STAT1) & 0xdf) |
+			(mtp008_read_value(client,
+					   MTP008_REG_INT_STAT2) & 0x0f) << 8;
+
+		/*
+		 * Read the beep control registers.
+		 */
+		data->beeps =
+			(mtp008_read_value(client,
+					   MTP008_REG_BEEP_CTRL1) & 0xdf) |
+			(mtp008_read_value(client,
+					   MTP008_REG_BEEP_CTRL2) & 0x8f) << 8;
+
+		/*
+		 * Read the sensor configuration.
+		 */
+		inp = mtp008_read_value(client, MTP008_REG_PIN_CTRL2);
+		mtp008_getsensortype(data, inp);
+		data->pwmenable = inp;
+
+		/*
+		 * Read the PWM registers if enabled.
+		 */
+		for (i = 1; i <= 3; i++)
+		{
+			if(PWMENABLE_FROM_REG(i, inp))
+				data->pwm[i-1] = mtp008_read_value(client,
+						  MTP008_REG_PWM_CTRL(i));
+			else
+				data->pwm[i-1] = 255;
+		}
+
+		/*
+		 * Read the fan sensors. Skip 3 if PWM1 enabled.
+		 */
+		for (i = 1; i <= 3; i++) {
+			if(i == 3 && PWMENABLE_FROM_REG(1, inp)) {
+				data->fan[2] = 0;
+				data->fan_min[2] = 0;
+			} else {
+				data->fan[i-1] = mtp008_read_value(client,
+					  MTP008_REG_FAN(i));
+				data->fan_min[i-1] = mtp008_read_value(client,
+					  MTP008_REG_FAN_MIN(i));
+			}
+		}
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+	up(&data->update_lock);
+}
+
+static void mtp008_getsensortype(struct mtp008_data *data, u8 inp)
+{
+	inp &= 0x0f;
+	data->sens[0] = (inp >> 3) + 2;			/* 2 or 3 */
+	data->sens[1] = ((inp >> 1) & 0x03) + 1;	/* 1, 2 or 3 */
+	data->sens[2] = (inp & 0x01) + 1;		/* 1 or 2 */
+}
+
+/* The next few functions are the call-back functions of the /proc/sys and
+   sysctl files. Which function is used is defined in the ctl_table in
+   the extra1 field.
+   Each function must return the magnitude (power of 10 to divide the date
+   with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must
+   put a maximum of *nrels elements in results reflecting the data of this
+   file, and set *nrels to the number it actually put in it, if operation==
+   SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from
+   results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE.
+   Note that on SENSORS_PROC_REAL_READ, I do not check whether results is
+   large enough (by checking the incoming value of *nrels). This is not very
+   good practice, but as long as you put less than about 5 values in results,
+   you can assume it is large enough. */
+void mtp008_in(struct i2c_client *client, int operation, int ctl_name,
+	       int *nrels_mag, long *results)
+{
+	int nr;
+	struct mtp008_data *data;
+
+	nr = ctl_name - MTP008_SYSCTL_IN0;
+	data = client->data;
+
+	switch (operation) {
+	case SENSORS_PROC_REAL_INFO:
+		*nrels_mag = 2;
+
+		break;
+	case SENSORS_PROC_REAL_READ:
+		mtp008_update_client(client);
+
+		if((nr != 4 && nr != 5) || data->sens[nr - 3] == VOLTAGE) {
+			results[0] = IN_FROM_REG(data->in_min[nr]);
+			results[1] = IN_FROM_REG(data->in_max[nr]);
+			results[2] = IN_FROM_REG(data->in[nr]);
+		} else {
+			results[0] = 0;
+			results[1] = 0;
+			results[2] = 0;
+		}
+
+		*nrels_mag = 3;
+
+		break;
+	case SENSORS_PROC_REAL_WRITE:
+		if((nr != 4 && nr != 5) || data->sens[nr - 3] == VOLTAGE) {
+			if (*nrels_mag >= 1) {
+				data->in_min[nr] = IN_TO_REG(results[0]);
+				mtp008_write_value(client, MTP008_REG_IN_MIN(nr),
+						   data->in_min[nr]);
+			}
+			if (*nrels_mag >= 2) {
+				data->in_max[nr] = IN_TO_REG(results[1]);
+				mtp008_write_value(client, MTP008_REG_IN_MAX(nr),
+						   data->in_max[nr]);
+			}
+		}
+	}
+}
+
+void mtp008_fan(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	int nr;
+	struct mtp008_data *data;
+
+	nr = ctl_name - MTP008_SYSCTL_FAN1;
+	data = client->data;
+
+	switch (operation) {
+	case SENSORS_PROC_REAL_INFO:
+		*nrels_mag = 0;
+
+		break;
+	case SENSORS_PROC_REAL_READ:
+		mtp008_update_client(client);
+
+		results[0] = FAN_FROM_REG(data->fan_min[nr],
+					DIV_FROM_REG(data->fan_div[nr]));
+		results[1] = FAN_FROM_REG(data->fan[nr],
+					DIV_FROM_REG(data->fan_div[nr]));
+
+		*nrels_mag = 2;
+
+		break;
+	case SENSORS_PROC_REAL_WRITE:
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr] =
+			    FAN_TO_REG(results[0],
+				       DIV_FROM_REG(data->fan_div[nr]));
+			mtp008_write_value(client, MTP008_REG_FAN_MIN(nr + 1),
+					   data->fan_min[nr]);
+		}
+	}
+}
+
+void mtp008_temp(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct mtp008_data *data;
+
+	data = client->data;
+
+	switch (operation) {
+	case SENSORS_PROC_REAL_INFO:
+		*nrels_mag = 1;
+
+		break;
+	case SENSORS_PROC_REAL_READ:
+		mtp008_update_client(client);
+
+		results[0] = TEMP_FROM_REG(data->temp_max);
+		results[1] = TEMP_FROM_REG(data->temp_min);
+		results[2] = TEMP_FROM_REG(data->temp);
+		*nrels_mag = 3;
+
+		break;
+	case SENSORS_PROC_REAL_WRITE:
+		if (*nrels_mag >= 1) {
+			data->temp_max = TEMP_TO_REG(results[0]);
+			mtp008_write_value(client, MTP008_REG_TEMP_MAX,
+					   data->temp_max);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_min = TEMP_TO_REG(results[1]);
+			mtp008_write_value(client, MTP008_REG_TEMP_MIN,
+					   data->temp_min);
+		}
+	}
+}
+
+void mtp008_temp_add(struct i2c_client *client, int operation, int ctl_name,
+		     int *nrels_mag, long *results)
+{
+	int nr;
+	struct mtp008_data *data;
+
+	nr = 3 + ctl_name - MTP008_SYSCTL_TEMP1;	/* AIN4 or AIN5 */
+	data = client->data;
+
+	switch (operation) {
+	case SENSORS_PROC_REAL_INFO:
+		*nrels_mag = 1;
+
+		break;
+	case SENSORS_PROC_REAL_READ:
+		mtp008_update_client(client);
+
+		if(data->sens[nr - 3] != VOLTAGE) {
+			results[0] = TEMP_FROM_REG(data->in_max[nr]);
+			results[1] = TEMP_FROM_REG(data->in_min[nr]);
+			results[2] = TEMP_FROM_REG(data->in[nr]);
+		} else {
+			results[0] = 0;
+			results[1] = 0;
+			results[2] = 0;
+		}
+		*nrels_mag = 3;
+
+		break;
+	case SENSORS_PROC_REAL_WRITE:
+		if(data->sens[nr - 3] != VOLTAGE) {
+			if (*nrels_mag >= 1) {
+				data->in_max[nr] = TEMP_TO_REG(results[0]);
+				mtp008_write_value(client, MTP008_REG_TEMP_MAX,
+						   data->in_max[nr]);
+			}
+			if (*nrels_mag >= 2) {
+				data->in_min[nr] = TEMP_TO_REG(results[1]);
+				mtp008_write_value(client, MTP008_REG_TEMP_MIN,
+						   data->in_min[nr]);
+			}
+		}
+	}
+}
+
+void mtp008_vid(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct mtp008_data *data;
+
+	data = client->data;
+
+	switch (operation) {
+	case SENSORS_PROC_REAL_INFO:
+		*nrels_mag = 2;
+
+		break;
+	case SENSORS_PROC_REAL_READ:
+		mtp008_update_client(client);
+
+		results[0] = VID_FROM_REG(data->vid);
+
+		*nrels_mag = 1;
+	}
+}
+
+void mtp008_fan_div(struct i2c_client *client, int operation,
+		    int ctl_name, int *nrels_mag, long *results)
+{
+	struct mtp008_data *data;
+	u8 val;
+
+	data = client->data;
+
+	switch (operation) {
+	case SENSORS_PROC_REAL_INFO:
+		*nrels_mag = 0;
+
+		break;
+	case SENSORS_PROC_REAL_READ:
+		mtp008_update_client(client);
+
+		results[0] = DIV_FROM_REG(data->fan_div[0]);
+		results[1] = DIV_FROM_REG(data->fan_div[1]);
+		results[2] = DIV_FROM_REG(data->fan_div[2]);
+
+		*nrels_mag = 3;
+
+		break;
+	case SENSORS_PROC_REAL_WRITE:
+		if (*nrels_mag >= 3) {
+			data->fan_div[2] = DIV_TO_REG(results[2]);
+			val = mtp008_read_value(client, MTP008_REG_PIN_CTRL1);
+			val = (val & 0x3f) | (data->fan_div[2] & 0x03) << 6;
+			mtp008_write_value(client, MTP008_REG_PIN_CTRL1, val);
+		}
+		if (*nrels_mag >= 1) {
+			val = mtp008_read_value(client, MTP008_REG_VID_FANDIV);
+			if (*nrels_mag >= 2) {
+				data->fan_div[1] = DIV_TO_REG(results[1]);
+				val = (val & 0x3f) |
+				      (data->fan_div[1] & 0x03) << 6;
+			}
+			data->fan_div[0] = DIV_TO_REG(results[0]);
+			val = (val & 0xcf) | (data->fan_div[0] & 0x03) << 4;
+			mtp008_write_value(client, MTP008_REG_VID_FANDIV, val);
+		}
+	}
+}
+
+void mtp008_alarms(struct i2c_client *client, int operation, int ctl_name,
+		   int *nrels_mag, long *results)
+{
+	struct mtp008_data *data;
+
+	data = client->data;
+
+	switch (operation) {
+	case SENSORS_PROC_REAL_INFO:
+		*nrels_mag = 0;
+
+		break;
+	case SENSORS_PROC_REAL_READ:
+		mtp008_update_client(client);
+
+		results[0] = ALARMS_FROM_REG(data->alarms);
+
+		*nrels_mag = 1;
+	}
+}
+
+void mtp008_beep(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct mtp008_data *data;
+
+	data = client->data;
+
+	switch (operation) {
+	case SENSORS_PROC_REAL_INFO:
+		*nrels_mag = 0;
+
+		break;
+	case SENSORS_PROC_REAL_READ:
+		mtp008_update_client(client);
+
+		results[0] = BEEPS_FROM_REG(data->beeps);
+
+		*nrels_mag = 1;
+
+		break;
+	case SENSORS_PROC_REAL_WRITE:
+		if (*nrels_mag >= 1) {
+			data->beeps = BEEPS_TO_REG(results[0]) & 0xdf8f;
+
+			mtp008_write_value(client, MTP008_REG_BEEP_CTRL1,
+					   data->beeps & 0xff);
+			mtp008_write_value(client, MTP008_REG_BEEP_CTRL2,
+					   data->beeps >> 8);
+		}
+	}
+}
+
+void mtp008_pwm(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	int nr;
+	struct mtp008_data *data;
+
+	nr = ctl_name - MTP008_SYSCTL_PWM1;
+	data = client->data;
+
+	switch (operation) {
+	case SENSORS_PROC_REAL_INFO:
+		*nrels_mag = 0;
+
+		break;
+	case SENSORS_PROC_REAL_READ:
+		mtp008_update_client(client);
+
+		results[0] = PWM_FROM_REG(data->pwm[nr]);
+		results[1] = PWMENABLE_FROM_REG(nr + 1, data->pwmenable);
+		*nrels_mag = 2;
+
+		break;
+	case SENSORS_PROC_REAL_WRITE:
+		if (*nrels_mag >= 1) {
+			if (*nrels_mag >= 2) {
+				if(results[1])
+					data->pwmenable |= 0x10 << nr;
+				else
+					data->pwmenable &= ~(0x10 << nr);
+				mtp008_write_value(client, MTP008_REG_PIN_CTRL2,
+					           data->pwmenable);
+			}
+			data->pwm[nr] = PWM_TO_REG(results[0]);
+			mtp008_write_value(client, MTP008_REG_PWM_CTRL(nr),
+					   data->pwm[nr]);
+		}
+	}
+}
+
+void mtp008_sens(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	const char *opts = "";
+	int nr;
+	u8 tmp;
+	struct mtp008_data *data;
+
+	nr = 1 + ctl_name - MTP008_SYSCTL_SENS1;
+	data = client->data;
+
+	switch (operation) {
+	case SENSORS_PROC_REAL_INFO:
+		*nrels_mag = 0;
+
+		break;
+	case SENSORS_PROC_REAL_READ:
+		results[0] = data->sens[nr - 1];
+
+		*nrels_mag = 1;
+
+		break;
+	case SENSORS_PROC_REAL_WRITE:
+		if (*nrels_mag >= 1) {
+			tmp = mtp008_read_value(client, MTP008_REG_PIN_CTRL2);
+
+			switch (nr) {
+			case 1:	/* VT or PII */
+				opts = "2 or 3";
+
+				switch (results[0]) {
+				case THERMISTOR:
+					mtp008_write_value(
+						client, MTP008_REG_PIN_CTRL2,
+						tmp & ~MTP008_CFG_VT1_PII);
+					data->sens[0] = 2;
+					return;
+				case PIIDIODE:
+					mtp008_write_value(
+						client, MTP008_REG_PIN_CTRL2,
+						tmp | MTP008_CFG_VT1_PII);
+					data->sens[0] = 3;
+					return;
+				}
+
+				break;
+			case 2:	/* AIN, VT or PII */
+				tmp &= ~MTP008_CFG_VT2_MASK;
+				opts = "1, 2 or 3";
+
+				switch (results[0]) {
+				case VOLTAGE:
+					mtp008_write_value(
+						client, MTP008_REG_PIN_CTRL2,
+						tmp | MTP008_CFG_VT2_AIN);
+					data->sens[1] = 1;
+					return;
+				case THERMISTOR:
+					mtp008_write_value(
+						client, MTP008_REG_PIN_CTRL2,
+						tmp | MTP008_CFG_VT2_VT);
+					data->sens[1] = 2;
+					return;
+				case PIIDIODE:
+					mtp008_write_value(
+						client, MTP008_REG_PIN_CTRL2,
+						tmp | MTP008_CFG_VT2_PII);
+					data->sens[1] = 3;
+					return;
+				}
+
+				break;
+			case 3:	/* AIN or VT */
+				opts = "1 or 2";
+
+				switch (results[0]) {
+				case VOLTAGE:
+					mtp008_write_value(
+						client, MTP008_REG_PIN_CTRL2,
+						tmp & ~MTP008_CFG_VT3_VT);
+					data->sens[2] = 1;
+					return;
+				case THERMISTOR:
+					mtp008_write_value(
+						client, MTP008_REG_PIN_CTRL2,
+						tmp | MTP008_CFG_VT3_VT);
+					data->sens[2] = 2;
+					return;
+				}
+
+				break;
+			}
+
+			printk("mtp008.o: Invalid sensor type %ld "
+			       "for sensor %d; must be %s.\n",
+			       results[0], nr, opts);
+		}
+	}
+}
+
+static int __init sm_mtp008_init(void)
+{
+	printk("mtp008.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&mtp008_driver);
+}
+
+static void __exit sm_mtp008_exit(void)
+{
+	i2c_del_driver(&mtp008_driver);
+}
+
+
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
+	      "Philip Edelbrock <phil@netroedge.com>, "
+	      "and Kris Van Hees <aedil@alchar.org>");
+MODULE_DESCRIPTION("MTP008 driver");
+
+module_init(sm_mtp008_init);
+module_exit(sm_mtp008_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/pcf8574.c linux-2.4.27-leo/drivers/sensors/pcf8574.c
--- linux-2.4.27/drivers/sensors/pcf8574.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/pcf8574.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,309 @@
+/*
+    pcf8574.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 2000  Frodo Looijaard <frodol@dds.nl>, 
+                        Philip Edelbrock <phil@netroedge.com>,
+                        Dan Eaton <dan.eaton@rocketlogix.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+    
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* A few notes about the PCF8574:
+
+* The PCF8574 is an 8-bit I/O expander for the I2C bus produced by
+  Philips Semiconductors.  It is designed to provide a byte I2C
+  interface to up to 8 separate devices.
+  
+* The PCF8574 appears as a very simple SMBus device which can be
+  read from or written to with SMBUS byte read/write accesses.
+
+* Because of the general purpose nature of this device, it will most
+  likely be necessary to customize the /proc interface to suit the
+  specific application.
+
+  --Dan
+
+*/
+
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x20, 0x27, 0x38, 0x3f, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_2(pcf8574, pcf8574a);
+
+/* The PCF8574 registers */
+
+/* (No registers.  [Wow! This thing is SIMPLE!] ) */
+
+/* Initial values */
+#define PCF8574_INIT 255	/* All outputs on (input mode) */
+
+/* Each client has this additional data */
+struct pcf8574_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+
+	u8 read, write;		/* Register values */
+};
+
+static int pcf8574_attach_adapter(struct i2c_adapter *adapter);
+static int pcf8574_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int pcf8574_detach_client(struct i2c_client *client);
+
+static void pcf8574_read(struct i2c_client *client, int operation,
+			               int ctl_name, int *nrels_mag, long *results);
+static void pcf8574_write(struct i2c_client *client, int operation,
+			               int ctl_name, int *nrels_mag, long *results);
+static void pcf8574_update_client(struct i2c_client *client);
+static void pcf8574_init_client(struct i2c_client *client);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver pcf8574_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "PCF8574 sensor chip driver",
+	.id		= I2C_DRIVERID_PCF8574,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= pcf8574_attach_adapter,
+	.detach_client	= pcf8574_detach_client,
+};
+
+
+/* -- SENSORS SYSCTL START -- */
+#define PCF8574_SYSCTL_READ     1000
+#define PCF8574_SYSCTL_WRITE    1001
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected PCF8574. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized
+   when a new copy is allocated. */
+static ctl_table pcf8574_dir_table_template[] = {
+	{PCF8574_SYSCTL_READ, "read", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &pcf8574_read},
+	{PCF8574_SYSCTL_WRITE, "write", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &pcf8574_write},
+	{0}
+};
+
+static int pcf8574_id = 0;
+
+static int pcf8574_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, pcf8574_detect);
+}
+
+/* This function is called by i2c_detect */
+int pcf8574_detect(struct i2c_adapter *adapter, int address,
+		   unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct pcf8574_data *data;
+	int err = 0;
+	const char *type_name, *client_name;
+
+	/* Make sure we aren't probing the ISA bus!! This is just a safety check
+	   at this moment; i2c_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("pcf8574.o: pcf8574_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
+		goto ERROR0;
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet. */
+	if (!(data = kmalloc(sizeof(struct pcf8574_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &pcf8574_driver;
+	new_client->flags = 0;
+
+	/* Now, we would do the remaining detection. But the PCF8574 is plainly
+	   impossible to detect! Stupid chip. */
+
+	/* Determine the chip type */
+	if (kind <= 0) {
+		if (address >= 0x38 && address <= 0x3f)
+			kind = pcf8574a;
+		else
+			kind = pcf8574;
+	}
+
+	if (kind == pcf8574a) {
+		type_name = "pcf8574a";
+		client_name = "PCF8574A chip";
+	} else {
+		type_name = "pcf8574a";
+		client_name = "PCF8574A chip";
+	}
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+
+	new_client->id = pcf8574_id++;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR1;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client, type_name,
+				    pcf8574_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR2;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the PCF8574 chip */
+	pcf8574_init_client(new_client);
+	return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+
+      ERROR2:
+	i2c_detach_client(new_client);
+      ERROR1:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int pcf8574_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct pcf8574_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk("pcf8574.o: Client deregistration failed, "
+		       "client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+/* Called when we have found a new PCF8574. */
+static void pcf8574_init_client(struct i2c_client *client)
+{
+	struct pcf8574_data *data = client->data;
+	data->write = PCF8574_INIT;
+	i2c_smbus_write_byte(client, data->write);
+}
+
+
+static void pcf8574_update_client(struct i2c_client *client)
+{
+	struct pcf8574_data *data = client->data;
+
+	down(&data->update_lock);
+
+#ifdef DEBUG
+	printk("Starting pcf8574 update\n");
+#endif
+
+	data->read = i2c_smbus_read_byte(client); 
+
+	up(&data->update_lock);
+}
+
+
+void pcf8574_read(struct i2c_client *client, int operation,
+		  int ctl_name, int *nrels_mag, long *results)
+{
+	struct pcf8574_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		pcf8574_update_client(client);
+		results[0] = data->read;
+		*nrels_mag = 1;
+	}  
+}
+void pcf8574_write(struct i2c_client *client, int operation,
+		   int ctl_name, int *nrels_mag, long *results)
+{
+	struct pcf8574_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = data->write; 
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->write = results[0];
+			i2c_smbus_write_byte(client, data->write);
+		}
+	}
+}
+
+
+static int __init sm_pcf8574_init(void)
+{
+	printk("pcf8574.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&pcf8574_driver);
+}
+
+static void __exit sm_pcf8574_exit(void)
+{
+	i2c_del_driver(&pcf8574_driver);
+}
+
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
+	      "Philip Edelbrock <phil@netroedge.com>, "
+	      "Dan Eaton <dan.eaton@rocketlogix.com> and "
+	      "Aurelien Jarno <aurelien@aurel32.net>");
+MODULE_DESCRIPTION("PCF8574 driver");
+
+module_init(sm_pcf8574_init);
+module_exit(sm_pcf8574_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/pcf8591.c linux-2.4.27-leo/drivers/sensors/pcf8591.c
--- linux-2.4.27/drivers/sensors/pcf8591.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/pcf8591.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,448 @@
+/*
+    pcf8591.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 2001  Aurelien Jarno <aurelien@aurel32.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x48, 0x4f, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(pcf8591);
+
+/* The PCF8591 control byte */
+/*    7    6    5    4    3    2    1    0   */
+/* |  0 |AOEF|   AIP   |  0 |AINC|  AICH   | */
+
+#define PCF8591_CONTROL_BYTE_AOEF 0x40  /* Analog Output Enable Flag */
+                                        /* (analog output active if 1) */
+
+#define PCF8591_CONTROL_BYTE_AIP 0x30   /* Analog Input Programming */
+                                        /* 0x00 = four single ended inputs */
+                                        /* 0x10 = three differential inputs */
+                                        /* 0x20 = single ended and differential mixed */
+                                        /* 0x30 = two differential inputs */
+
+#define PCF8591_CONTROL_BYTE_AINC 0x04  /* Autoincrement Flag */
+                                        /* (switch on if 1) */
+
+#define PCF8591_CONTROL_BYTE_AICH 0x03  /* Analog Output Enable Flag */
+                                        /* 0x00 = channel 0 */
+                                        /* 0x01 = channel 1 */
+                                        /* 0x02 = channel 2 */
+                                        /* 0x03 = channel 3 */
+
+
+/* Initial values */
+#define PCF8591_INIT_CONTROL_BYTE (PCF8591_CONTROL_BYTE_AOEF | PCF8591_CONTROL_BYTE_AINC)
+                /* DAC out enabled, four single ended inputs, autoincrement */
+
+#define PCF8591_INIT_AOUT 0             /* DAC out = 0 */
+
+
+/* Conversions. */
+#define REG_TO_SIGNED(reg) (reg & 0x80)?(reg - 256):(reg)
+                          /* Convert signed 8 bit value to signed value */
+
+
+struct pcf8591_data {
+	struct i2c_client client;
+        int sysctl_id;
+
+        struct semaphore update_lock;
+        char valid;             /* !=0 if following fields are valid */
+        unsigned long last_updated;     /* In jiffies */
+
+        u8 control_byte;
+        u8 ch[4];
+        u8 aout;
+};
+
+static int pcf8591_attach_adapter(struct i2c_adapter *adapter);
+static int pcf8591_detect(struct i2c_adapter *adapter, int address,
+                          unsigned short flags, int kind);
+static int pcf8591_detach_client(struct i2c_client *client);
+
+static void pcf8591_update_client(struct i2c_client *client);
+static void pcf8591_init_client(struct i2c_client *client);
+
+static void pcf8591_ain_conf(struct i2c_client *client, int operation,
+                             int ctl_name, int *nrels_mag, long *results);
+static void pcf8591_ain(struct i2c_client *client, int operation,
+                        int ctl_name, int *nrels_mag, long *results);
+static void pcf8591_aout_enable(struct i2c_client *client, int operation,
+                                int ctl_name, int *nrels_mag, long *results);
+static void pcf8591_aout(struct i2c_client *client, int operation,
+                         int ctl_name, int *nrels_mag, long *results);
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver pcf8591_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "PCF8591 sensor chip driver",
+	.id		= I2C_DRIVERID_PCF8591,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= pcf8591_attach_adapter,
+	.detach_client	= pcf8591_detach_client,
+};
+
+static int pcf8591_id = 0;
+
+/* The /proc/sys entries */
+
+/* -- SENSORS SYSCTL START -- */
+#define PCF8591_SYSCTL_AIN_CONF 1000      /* Analog input configuration */
+#define PCF8591_SYSCTL_CH0 1001           /* Input channel 1 */
+#define PCF8591_SYSCTL_CH1 1002           /* Input channel 2 */
+#define PCF8591_SYSCTL_CH2 1003           /* Input channel 3 */
+#define PCF8591_SYSCTL_CH3 1004           /* Input channel 4 */
+#define PCF8591_SYSCTL_AOUT_ENABLE 1005   /* Analog output enable flag */
+#define PCF8591_SYSCTL_AOUT 1006          /* Analog output */
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected PCF8591. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized
+   when a new copy is allocated. */
+static ctl_table pcf8591_dir_table_template[] = {
+        {PCF8591_SYSCTL_AIN_CONF, "ain_conf", NULL, 0, 0644, NULL, &i2c_proc_real,
+         &i2c_sysctl_real, NULL, &pcf8591_ain_conf},
+        {PCF8591_SYSCTL_CH0, "ch0", NULL, 0, 0444, NULL, &i2c_proc_real,
+         &i2c_sysctl_real, NULL, &pcf8591_ain},
+        {PCF8591_SYSCTL_CH1, "ch1", NULL, 0, 0444, NULL, &i2c_proc_real,
+         &i2c_sysctl_real, NULL, &pcf8591_ain},
+        {PCF8591_SYSCTL_CH2, "ch2", NULL, 0, 0444, NULL, &i2c_proc_real,
+         &i2c_sysctl_real, NULL, &pcf8591_ain},
+        {PCF8591_SYSCTL_CH3, "ch3", NULL, 0, 0444, NULL, &i2c_proc_real,
+         &i2c_sysctl_real, NULL, &pcf8591_ain},
+        {PCF8591_SYSCTL_AOUT_ENABLE, "aout_enable", NULL, 0, 0644, NULL, &i2c_proc_real,
+         &i2c_sysctl_real, NULL, &pcf8591_aout_enable},
+        {PCF8591_SYSCTL_AOUT, "aout", NULL, 0, 0644, NULL, &i2c_proc_real,
+         &i2c_sysctl_real, NULL, &pcf8591_aout},
+        {0}
+};
+
+
+/* This function is called when:
+     * pcf8591_driver is inserted (when this module is loaded), for each
+       available adapter
+     * when a new adapter is inserted (and pcf8591_driver is still present) */
+static int pcf8591_attach_adapter(struct i2c_adapter *adapter)
+{
+        return i2c_detect(adapter, &addr_data, pcf8591_detect);
+}
+
+/* This function is called by i2c_detect */
+int pcf8591_detect(struct i2c_adapter *adapter, int address,
+                unsigned short flags, int kind)
+{
+        int i;
+        struct i2c_client *new_client;
+        struct pcf8591_data *data;
+        int err = 0;
+
+        const char *type_name = "";
+        const char *client_name = "";
+
+        /* Make sure we aren't probing the ISA bus!! This is just a safety check at this moment; i2c_detect really won't call us. */
+#ifdef DEBUG
+        if (i2c_is_isa_adapter(adapter)) {
+                printk
+                    (KERN_ERR "pcf8591.o: pcf8591_detect called for an ISA bus adapter?!?\n");
+                return 0;
+        }
+#endif
+
+        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE
+                                     | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+                goto ERROR0;
+
+        /* OK. For now, we presume we have a valid client. We now create the
+           client structure, even though we cannot fill it completely yet. */
+	if (!(data = kmalloc(sizeof(struct pcf8591_data), GFP_KERNEL))) {
+                err = -ENOMEM;
+                goto ERROR0;
+        }
+
+	new_client = &data->client;
+        new_client->addr = address;
+        new_client->data = data;
+        new_client->adapter = adapter;
+        new_client->driver = &pcf8591_driver;
+        new_client->flags = 0;
+
+        /* Now, we would do the remaining detection. But the PCF8591 is plainly
+           impossible to detect! Stupid chip. */
+
+        /* Determine the chip type - only one kind supported! */
+        if (kind <= 0)
+                kind = pcf8591;
+
+	type_name = "pcf8591";
+	client_name = "PCF8591 chip";
+
+        /* Fill in the remaining client fields and put it into the global list */
+        strcpy(new_client->name, client_name);
+
+        new_client->id = pcf8591_id++;
+        data->valid = 0;
+        init_MUTEX(&data->update_lock);
+
+        /* Tell the I2C layer a new client has arrived */
+        if ((err = i2c_attach_client(new_client)))
+                goto ERROR3;
+
+        /* Register a new directory entry with module sensors */
+        if ((i = i2c_register_entry(new_client,
+                                        type_name,
+                                        pcf8591_dir_table_template)) < 0) {
+                err = i;
+                goto ERROR4;
+        }
+        data->sysctl_id = i;
+
+        /* Initialize the PCF8591 chip */
+        pcf8591_init_client(new_client);
+        return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+
+      ERROR4:
+        i2c_detach_client(new_client);
+      ERROR3:
+	kfree(data);
+      ERROR0:
+        return err;
+}
+
+static int pcf8591_detach_client(struct i2c_client *client)
+{
+        int err;
+
+        i2c_deregister_entry(((struct pcf8591_data *) (client->data))->
+                                 sysctl_id);
+
+        if ((err = i2c_detach_client(client))) {
+                printk
+                    (KERN_ERR "pcf8591.o: Client deregistration failed, client not detached.\n");
+                return err;
+        }
+
+	kfree(client->data);
+
+        return 0;
+}
+
+/* Called when we have found a new PCF8591. */
+static void pcf8591_init_client(struct i2c_client *client)
+{
+        struct pcf8591_data *data = client->data;
+        data->control_byte = PCF8591_INIT_CONTROL_BYTE;
+        data->aout = PCF8591_INIT_AOUT;
+
+        i2c_smbus_write_byte_data(client, data->control_byte, data->aout);
+}
+
+static void pcf8591_update_client(struct i2c_client *client)
+{
+        struct pcf8591_data *data = client->data;
+
+        down(&data->update_lock);
+
+        if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+            (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+                printk(KERN_DEBUG "Starting pcf8591 update\n");
+#endif
+
+                i2c_smbus_write_byte(client, data->control_byte);
+                i2c_smbus_read_byte(client);    /* The first byte transmitted contains the */
+                                                /* conversion code of the previous read cycled */
+                                                /* FLUSH IT ! */
+
+
+                /* Number of byte to read to signed depend on the analog input mode */
+                data->ch[0] = i2c_smbus_read_byte(client);
+                data->ch[1] = i2c_smbus_read_byte(client);
+                        /* In all case, read at least two values */
+
+                if ((data->control_byte & PCF8591_CONTROL_BYTE_AIP) != 0x30)
+                        data->ch[2] = i2c_smbus_read_byte(client);
+                        /* Read the third value if not in "two differential inputs" mode */
+
+                if ((data->control_byte & PCF8591_CONTROL_BYTE_AIP) == 0x00)
+                        data->ch[3] = i2c_smbus_read_byte(client);
+                        /* Read the fourth value only in "four single ended inputs" mode */
+
+                data->last_updated = jiffies;
+                data->valid = 1;
+        }
+
+        up(&data->update_lock);
+}
+
+/* The next few functions are the call-back functions of the /proc/sys and
+   sysctl files. Which function is used is defined in the ctl_table in
+   the extra1 field. */
+void pcf8591_ain_conf(struct i2c_client *client, int operation, int ctl_name,
+             int *nrels_mag, long *results)
+{
+        struct pcf8591_data *data = client->data;
+
+        if (operation == SENSORS_PROC_REAL_INFO)
+                *nrels_mag = 0;
+        else if (operation == SENSORS_PROC_REAL_READ) {
+                results[0] = (data->control_byte & PCF8591_CONTROL_BYTE_AIP) >> 4;
+                *nrels_mag = 1;
+        } else if (operation == SENSORS_PROC_REAL_WRITE) {
+                if (*nrels_mag >= 1) {
+                        if (results[0] >= 0 && results[0] <= 3)
+                        {
+                                data->control_byte &= ~PCF8591_CONTROL_BYTE_AIP;
+                                data->control_byte |= (results[0] << 4);
+                                i2c_smbus_write_byte(client, data->control_byte);
+                                data->valid = 0;
+                        }
+                }
+        }
+}
+
+void pcf8591_ain(struct i2c_client *client, int operation, int ctl_name,
+             int *nrels_mag, long *results)
+{
+        struct pcf8591_data *data = client->data;
+        int nr = ctl_name - PCF8591_SYSCTL_CH0;
+
+        if (operation == SENSORS_PROC_REAL_INFO)
+                *nrels_mag = 0;
+        else if (operation == SENSORS_PROC_REAL_READ) {
+                pcf8591_update_client(client);
+
+                /* Number of data to show and conversion to signed depend on */
+                /* the analog input mode */
+
+                switch(nr) {
+                        case 0:
+                                if (((data->control_byte & PCF8591_CONTROL_BYTE_AIP) == 0)
+                                   | ((data->control_byte & PCF8591_CONTROL_BYTE_AIP) == 2))
+                                        results[0] = data->ch[0];               /* single ended */
+                                else
+                                        results[0] = REG_TO_SIGNED(data->ch[0]);/* differential */
+                                break;
+                        case 1:
+                                if (((data->control_byte & PCF8591_CONTROL_BYTE_AIP) == 0)
+                                   | ((data->control_byte & PCF8591_CONTROL_BYTE_AIP) == 2))
+                                        results[0] = data->ch[1]; /* single ended */
+                                else
+                                        results[0] = REG_TO_SIGNED(data->ch[1]);/* differential */
+                                break;
+                        case 2:
+                                if ((data->control_byte & PCF8591_CONTROL_BYTE_AIP) == 3)
+                                        results[0] = 0;  /* channel not used */
+                                else if ((data->control_byte & PCF8591_CONTROL_BYTE_AIP) == 0)
+                                        results[0] = data->ch[2]; /* single ended */
+                                else
+                                        results[0] = REG_TO_SIGNED(data->ch[2]);/* differential */
+                                break;
+                        case 3:
+                                if (((data->control_byte & PCF8591_CONTROL_BYTE_AIP) == 0)
+                                   | ((data->control_byte & PCF8591_CONTROL_BYTE_AIP) == 2))
+                                        results[0] = data->ch[3]; /* single ended */
+                                else
+                                        results[0] = 0;  /* channel not used */
+                                break;
+                }
+        *nrels_mag = 1;
+        }
+}
+
+void pcf8591_aout_enable(struct i2c_client *client, int operation, int ctl_name,
+             int *nrels_mag, long *results)
+{
+        struct pcf8591_data *data = client->data;
+
+        if (operation == SENSORS_PROC_REAL_INFO)
+                *nrels_mag = 0;
+        else if (operation == SENSORS_PROC_REAL_READ) {
+                results[0] = !(!(data->control_byte & PCF8591_CONTROL_BYTE_AOEF));
+                *nrels_mag = 1;
+        } else if (operation == SENSORS_PROC_REAL_WRITE) {
+                if (*nrels_mag >= 1) {
+                        if (results[0])
+                                data->control_byte |= PCF8591_CONTROL_BYTE_AOEF;
+                        else
+                                data->control_byte &= ~PCF8591_CONTROL_BYTE_AOEF;
+
+                        i2c_smbus_write_byte(client, data->control_byte);
+                }
+        }
+}
+
+void pcf8591_aout(struct i2c_client *client, int operation, int ctl_name,
+             int *nrels_mag, long *results)
+{
+        struct pcf8591_data *data = client->data;
+
+        if (operation == SENSORS_PROC_REAL_INFO)
+                *nrels_mag = 0;
+        else if (operation == SENSORS_PROC_REAL_READ) {
+                results[0] = data->aout;
+                *nrels_mag = 1;
+        } else if (operation == SENSORS_PROC_REAL_WRITE) {
+                if (*nrels_mag >= 1) {
+                        if (results[0] >= 0 && results[0] <= 255) /* ignore values outside DAC range */
+                        {
+                                data->aout = results[0];
+                                i2c_smbus_write_byte_data(client, data->control_byte, data->aout);
+                        }
+                }
+        }
+}
+
+static int __init sm_pcf8591_init(void)
+{
+        printk(KERN_INFO "pcf8591.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&pcf8591_driver);
+}
+
+static void __exit sm_pcf8591_exit(void)
+{
+        i2c_del_driver(&pcf8591_driver);
+}
+
+
+
+MODULE_AUTHOR("Aurelien Jarno <aurelien@aurel32.net>");
+MODULE_DESCRIPTION("PCF8591 driver");
+MODULE_LICENSE("GPL");
+
+module_init(sm_pcf8591_init);
+module_exit(sm_pcf8591_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/sis5595.c linux-2.4.27-leo/drivers/sensors/sis5595.c
--- linux-2.4.27/drivers/sensors/sis5595.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/sis5595.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,735 @@
+/*
+    sis5595.c - Part of lm_sensors, Linux kernel modules
+                for hardware monitoring
+                
+    Copyright (c) 1998 - 2001 Frodo Looijaard <frodol@dds.nl>,
+                        Kyösti Mälkki <kmalkki@cc.hut.fi>, and
+			Mark D. Studebaker <mdsxyz123@yahoo.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* 
+    Supports following revisions:
+	Version		PCI ID		PCI Revision
+	1		1039/0008	AF or less
+	2		1039/0008	B0 or greater
+
+   Note: these chips contain a 0008 device which is incompatible with the
+         5595. We recognize these by the presence of the listed
+         "blacklist" PCI ID and refuse to load.
+
+   NOT SUPPORTED	PCI ID		BLACKLIST PCI ID	
+	 540		0008		0540
+	 550		0008		0550
+	5513		0008		5511
+	5581		0008		5597
+	5582		0008		5597
+	5597		0008		5597
+	5598		0008		5597/5598
+	 630		0008		0630
+	 645		0008		0645
+	 730		0008		0730
+	 735		0008		0735
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+MODULE_LICENSE("GPL");
+
+/* If force_addr is set to anything different from 0, we forcibly enable
+   the device at the given address. */
+static int force_addr = 0;
+MODULE_PARM(force_addr, "i");
+MODULE_PARM_DESC(force_addr,
+		 "Initialize the base address of the sensors");
+
+/* Addresses to scan.
+   Note that we can't determine the ISA address until we have initialized
+   our module */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { 0x0000, SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(sis5595);
+
+static int blacklist[] = {
+			PCI_DEVICE_ID_SI_540,
+			PCI_DEVICE_ID_SI_550,
+			PCI_DEVICE_ID_SI_630,
+			PCI_DEVICE_ID_SI_730,
+			PCI_DEVICE_ID_SI_5511, /* 5513 chip has the 0008 device but
+						  that ID shows up in other chips so we
+						  use the 5511 ID for recognition */
+			PCI_DEVICE_ID_SI_5597,
+			PCI_DEVICE_ID_SI_5598,
+			0x645,
+			0x735,
+                          0 };
+/*
+   SiS southbridge has a LM78-like chip integrated on the same IC.
+   This driver is a customized copy of lm78.c
+*/
+
+/* Many SIS5595 constants specified below */
+
+/* Length of ISA address segment */
+#define SIS5595_EXTENT 8
+/* PCI Config Registers */
+#define SIS5595_REVISION_REG 0x08
+#define SIS5595_BASE_REG 0x68
+#define SIS5595_PIN_REG 0x7A
+#define SIS5595_ENABLE_REG 0x7B
+
+/* Where are the ISA address/data registers relative to the base address */
+#define SIS5595_ADDR_REG_OFFSET 5
+#define SIS5595_DATA_REG_OFFSET 6
+
+/* The SIS5595 registers */
+#define SIS5595_REG_IN_MAX(nr) (0x2b + (nr) * 2)
+#define SIS5595_REG_IN_MIN(nr) (0x2c + (nr) * 2)
+#define SIS5595_REG_IN(nr) (0x20 + (nr))
+
+#define SIS5595_REG_FAN_MIN(nr) (0x3a + (nr))
+#define SIS5595_REG_FAN(nr) (0x27 + (nr))
+
+/* On the first version of the chip, the temp registers are separate.
+   On the second version,
+   TEMP pin is shared with IN4, configured in PCI register 0x7A.
+   The registers are the same as well.
+   OVER and HYST are really MAX and MIN. */
+
+#define REV2MIN	0xb0
+#define SIS5595_REG_TEMP 	(( data->revision) >= REV2MIN) ? \
+					SIS5595_REG_IN(4) : 0x27
+#define SIS5595_REG_TEMP_OVER	(( data->revision) >= REV2MIN) ? \
+					SIS5595_REG_IN_MAX(4) : 0x39
+#define SIS5595_REG_TEMP_HYST	(( data->revision) >= REV2MIN) ? \
+					SIS5595_REG_IN_MIN(4) : 0x3a
+
+#define SIS5595_REG_CONFIG 0x40
+#define SIS5595_REG_ALARM1 0x41
+#define SIS5595_REG_ALARM2 0x42
+#define SIS5595_REG_FANDIV 0x47
+
+/* Conversions. Limit checking is only done on the TO_REG
+   variants. Note that you should be a bit careful with which arguments
+   these macros are called: arguments may be evaluated more than once.
+   Fixing this is just not worth it. */
+
+#define IN_TO_REG(val)  (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255))
+#define IN_FROM_REG(val) (((val) *  16 + 5) / 10)
+
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+	if (rpm == 0)
+		return 255;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1,
+			     254);
+}
+
+#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div)))
+
+/* Version 1 datasheet temp=.83*reg + 52.12 */
+#define TEMP_FROM_REG(val) (((((val)>=0x80?(val)-0x100:(val))*83)+5212)/10)
+/* inverse 1.20*val - 62.77 */
+#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?\
+				((((val)*12)-6327)/100):\
+                                ((((val)*12)-6227)/100)),0,255))
+
+#define ALARMS_FROM_REG(val) (val)
+
+#define DIV_FROM_REG(val) (1 << (val))
+#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
+
+/* For the SIS5595, we need to keep some data in memory. That
+   data is pointed to by sis5595_list[NR]->data. The structure itself is
+   dynamically allocated, at the time when the new sis5595 client is
+   allocated. */
+struct sis5595_data {
+	struct i2c_client client;
+	struct semaphore lock;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+	char maxins;		/* == 3 if temp enabled, otherwise == 4 */
+	u8 revision;		/* Reg. value */
+
+	u8 in[5];		/* Register value */
+	u8 in_max[5];		/* Register value */
+	u8 in_min[5];		/* Register value */
+	u8 fan[2];		/* Register value */
+	u8 fan_min[2];		/* Register value */
+	u8 temp;		/* Register value */
+	u8 temp_over;		/* Register value  - really max */
+	u8 temp_hyst;		/* Register value  - really min */
+	u8 fan_div[2];		/* Register encoding, shifted right */
+	u16 alarms;		/* Register encoding, combined */
+};
+
+static struct pci_dev *s_bridge;	/* pointer to the (only) sis5595 */
+
+static int sis5595_attach_adapter(struct i2c_adapter *adapter);
+static int sis5595_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int sis5595_detach_client(struct i2c_client *client);
+
+static int sis5595_read_value(struct i2c_client *client, u8 register);
+static int sis5595_write_value(struct i2c_client *client, u8 register,
+			       u8 value);
+static void sis5595_update_client(struct i2c_client *client);
+static void sis5595_init_client(struct i2c_client *client);
+static int sis5595_find_sis(int *address);
+
+
+static void sis5595_in(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void sis5595_fan(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void sis5595_temp(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void sis5595_alarms(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+static void sis5595_fan_div(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+
+static int sis5595_id = 0;
+
+/* The driver. I choose to use type i2c_driver, as at is identical to both
+   smbus_driver and isa_driver, and clients could be of either kind */
+static struct i2c_driver sis5595_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "SiS 5595",
+	.id		= I2C_DRIVERID_SIS5595,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= sis5595_attach_adapter,
+	.detach_client	= sis5595_detach_client,
+};
+
+/* The /proc/sys entries */
+
+/* -- SENSORS SYSCTL START -- */
+#define SIS5595_SYSCTL_IN0 1000	/* Volts * 100 */
+#define SIS5595_SYSCTL_IN1 1001
+#define SIS5595_SYSCTL_IN2 1002
+#define SIS5595_SYSCTL_IN3 1003
+#define SIS5595_SYSCTL_IN4 1004
+#define SIS5595_SYSCTL_FAN1 1101	/* Rotations/min */
+#define SIS5595_SYSCTL_FAN2 1102
+#define SIS5595_SYSCTL_TEMP 1200	/* Degrees Celcius * 10 */
+#define SIS5595_SYSCTL_FAN_DIV 2000	/* 1, 2, 4 or 8 */
+#define SIS5595_SYSCTL_ALARMS 2001	/* bitvector */
+
+#define SIS5595_ALARM_IN0 0x01
+#define SIS5595_ALARM_IN1 0x02
+#define SIS5595_ALARM_IN2 0x04
+#define SIS5595_ALARM_IN3 0x08
+#define SIS5595_ALARM_BTI 0x20
+#define SIS5595_ALARM_FAN1 0x40
+#define SIS5595_ALARM_FAN2 0x80
+#define SIS5595_ALARM_IN4  0x8000
+#define SIS5595_ALARM_TEMP 0x8000
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected SIS5595. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized 
+   when a new copy is allocated. */
+static ctl_table sis5595_dir_table_template[] = {
+	{SIS5595_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &sis5595_in},
+	{SIS5595_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &sis5595_in},
+	{SIS5595_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &sis5595_in},
+	{SIS5595_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &sis5595_in},
+	{SIS5595_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &sis5595_in},
+	{SIS5595_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &sis5595_fan},
+	{SIS5595_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &sis5595_fan},
+	{SIS5595_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &sis5595_temp},
+	{SIS5595_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &sis5595_fan_div},
+	{SIS5595_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &sis5595_alarms},
+	{0}
+};
+
+/* This is called when the module is loaded */
+static int sis5595_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, sis5595_detect);
+}
+
+/* Locate SiS bridge and correct base address for SIS5595 */
+static int sis5595_find_sis(int *address)
+{
+	u16 val;
+	int *i;
+
+	if (!pci_present())
+		return -ENODEV;
+
+	if (!(s_bridge =
+	      pci_find_device(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503,
+			     NULL)))
+		return -ENODEV;
+
+	/* Look for imposters */
+	for(i = blacklist; *i != 0; i++) {
+		if (pci_find_device(PCI_VENDOR_ID_SI, *i, NULL)) {
+			printk("sis5595.o: Error: Looked for SIS5595 but found unsupported device %.4X\n", *i);
+			return -ENODEV;
+		}
+	}
+
+	if (PCIBIOS_SUCCESSFUL !=
+	    pci_read_config_word(s_bridge, SIS5595_BASE_REG, &val))
+		return -ENODEV;
+
+	*address = val & ~(SIS5595_EXTENT - 1);
+	if (*address == 0 && force_addr == 0) {
+		printk("sis5595.o: base address not set - upgrade BIOS or use force_addr=0xaddr\n");
+		return -ENODEV;
+	}
+	if (force_addr)
+		*address = force_addr;	/* so detect will get called */
+
+	return 0;
+}
+
+int sis5595_detect(struct i2c_adapter *adapter, int address,
+		   unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct sis5595_data *data;
+	int err = 0;
+	const char *type_name = "sis5595";
+	const char *client_name = "SIS5595 chip";
+	char val;
+	u16 a;
+
+	/* Make sure we are probing the ISA bus!!  */
+	if (!i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("sis5595.o: sis5595_detect called for an I2C bus adapter?!?\n");
+		return 0;
+	}
+
+	if(force_addr)
+		address = force_addr & ~(SIS5595_EXTENT - 1);
+	if (check_region(address, SIS5595_EXTENT)) {
+		printk("sis5595.o: region 0x%x already in use!\n", address);
+		return -ENODEV;
+	}
+	if(force_addr) {
+		printk("sis5595.o: forcing ISA address 0x%04X\n", address);
+		if (PCIBIOS_SUCCESSFUL !=
+		    pci_write_config_word(s_bridge, SIS5595_BASE_REG, address))
+			return -ENODEV;
+		if (PCIBIOS_SUCCESSFUL !=
+		    pci_read_config_word(s_bridge, SIS5595_BASE_REG, &a))
+			return -ENODEV;
+		if ((a & ~(SIS5595_EXTENT - 1)) != address) {
+			/* doesn't work for some chips? */
+			printk("sis5595.o: force address failed\n");
+			return -ENODEV;
+		}
+	}
+
+	if (PCIBIOS_SUCCESSFUL !=
+	    pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val))
+		return -ENODEV;
+	if((val & 0x80) == 0) {
+		printk("sis5595.o: enabling sensors\n");
+		if (PCIBIOS_SUCCESSFUL !=
+		    pci_write_config_byte(s_bridge, SIS5595_ENABLE_REG,
+		                      val | 0x80))
+			return -ENODEV;
+		if (PCIBIOS_SUCCESSFUL !=
+		    pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val))
+			return -ENODEV;
+		if((val & 0x80) == 0) {	/* doesn't work for some chips! */
+			printk("sis5595.o: sensors enable failed - not supported?\n");
+			return -ENODEV;
+		}
+	}
+
+	if (!(data = kmalloc(sizeof(struct sis5595_data), GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	init_MUTEX(&data->lock);
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &sis5595_driver;
+	new_client->flags = 0;
+
+	/* Reserve the ISA region */
+	request_region(address, SIS5595_EXTENT, type_name);
+
+	/* Check revision and pin registers to determine whether 3 or 4 voltages */
+	pci_read_config_byte(s_bridge, SIS5595_REVISION_REG, &(data->revision));
+	if(data->revision < REV2MIN) {
+		data->maxins = 3;
+	} else {
+		pci_read_config_byte(s_bridge, SIS5595_PIN_REG, &val);
+		if(val & 0x80)
+			/* 3 voltages, 1 temp */
+			data->maxins = 3;
+		else
+			/* 4 voltages, no temps */
+			data->maxins = 4;
+	}
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+
+	new_client->id = sis5595_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry((struct i2c_client *) new_client,
+					type_name,
+					sis5595_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the SIS5595 chip */
+	sis5595_init_client(new_client);
+	return 0;
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+	release_region(address, SIS5595_EXTENT);
+	kfree(data);
+	return err;
+}
+
+static int sis5595_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct sis5595_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("sis5595.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	release_region(client->addr, SIS5595_EXTENT);
+	kfree(client->data);
+
+	return 0;
+}
+
+
+/* ISA access must be locked explicitly.
+   There are some ugly typecasts here, but the good news is - they should
+   nowhere else be necessary! */
+static int sis5595_read_value(struct i2c_client *client, u8 reg)
+{
+	int res;
+
+	down(&(((struct sis5595_data *) (client->data))->lock));
+	outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET);
+	res = inb_p(client->addr + SIS5595_DATA_REG_OFFSET);
+	up(&(((struct sis5595_data *) (client->data))->lock));
+	return res;
+}
+
+static int sis5595_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+	down(&(((struct sis5595_data *) (client->data))->lock));
+	outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET);
+	outb_p(value, client->addr + SIS5595_DATA_REG_OFFSET);
+	up(&(((struct sis5595_data *) (client->data))->lock));
+	return 0;
+}
+
+/* Called when we have found a new SIS5595. */
+static void sis5595_init_client(struct i2c_client *client)
+{
+	u8 reg;
+
+	/* Start monitoring */
+	reg = i2c_smbus_read_byte_data(client, SIS5595_REG_CONFIG);
+	sis5595_write_value(client, SIS5595_REG_CONFIG, (reg|0x01)&0x7F);
+}
+
+static void sis5595_update_client(struct i2c_client *client)
+{
+	struct sis5595_data *data = client->data;
+	int i;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+		for (i = 0; i <= data->maxins; i++) {
+			data->in[i] =
+			    sis5595_read_value(client, SIS5595_REG_IN(i));
+			data->in_min[i] =
+			    sis5595_read_value(client,
+					       SIS5595_REG_IN_MIN(i));
+			data->in_max[i] =
+			    sis5595_read_value(client,
+					       SIS5595_REG_IN_MAX(i));
+		}
+		for (i = 1; i <= 2; i++) {
+			data->fan[i - 1] =
+			    sis5595_read_value(client, SIS5595_REG_FAN(i));
+			data->fan_min[i - 1] =
+			    sis5595_read_value(client,
+					       SIS5595_REG_FAN_MIN(i));
+		}
+		if(data->maxins == 3) {
+			data->temp =
+			    sis5595_read_value(client, SIS5595_REG_TEMP);
+			data->temp_over =
+			    sis5595_read_value(client, SIS5595_REG_TEMP_OVER);
+			data->temp_hyst =
+			    sis5595_read_value(client, SIS5595_REG_TEMP_HYST);
+		}
+		i = sis5595_read_value(client, SIS5595_REG_FANDIV);
+		data->fan_div[0] = (i >> 4) & 0x03;
+		data->fan_div[1] = i >> 6;
+		data->alarms =
+		    sis5595_read_value(client, SIS5595_REG_ALARM1) |
+		    (sis5595_read_value(client, SIS5595_REG_ALARM2) << 8);
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+/* The next few functions are the call-back functions of the /proc/sys and
+   sysctl files. Which function is used is defined in the ctl_table in
+   the extra1 field.
+   Each function must return the magnitude (power of 10 to divide the date
+   with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must
+   put a maximum of *nrels elements in results reflecting the data of this
+   file, and set *nrels to the number it actually put in it, if operation==
+   SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from
+   results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE.
+   Note that on SENSORS_PROC_REAL_READ, I do not check whether results is
+   large enough (by checking the incoming value of *nrels). This is not very
+   good practice, but as long as you put less than about 5 values in results,
+   you can assume it is large enough. */
+
+/* Return 0 for in4 and disallow writes if pin used for temp */
+void sis5595_in(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct sis5595_data *data = client->data;
+	int nr = ctl_name - SIS5595_SYSCTL_IN0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		if(nr <= 3 || data->maxins == 4) {
+			sis5595_update_client(client);
+			results[0] = IN_FROM_REG(data->in_min[nr]);
+			results[1] = IN_FROM_REG(data->in_max[nr]);
+			results[2] = IN_FROM_REG(data->in[nr]);
+		} else {
+			results[0] = 0;
+			results[1] = 0;
+			results[2] = 0;
+		}
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if(nr <= 3 || data->maxins == 4) {
+			if (*nrels_mag >= 1) {
+				data->in_min[nr] = IN_TO_REG(results[0]);
+				sis5595_write_value(client,
+				    SIS5595_REG_IN_MIN(nr), data->in_min[nr]);
+			}
+			if (*nrels_mag >= 2) {
+				data->in_max[nr] = IN_TO_REG(results[1]);
+				sis5595_write_value(client,
+				    SIS5595_REG_IN_MAX(nr), data->in_max[nr]);
+			}
+		}
+	}
+}
+
+void sis5595_fan(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct sis5595_data *data = client->data;
+	int nr = ctl_name - SIS5595_SYSCTL_FAN1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		sis5595_update_client(client);
+		results[0] = FAN_FROM_REG(data->fan_min[nr - 1],
+					  DIV_FROM_REG(data->fan_div[nr - 1]));
+		results[1] = FAN_FROM_REG(data->fan[nr - 1],
+					  DIV_FROM_REG(data->fan_div[nr - 1]));
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr - 1] = FAN_TO_REG(results[0],
+							   DIV_FROM_REG
+							   (data->
+							    fan_div[nr-1]));
+			sis5595_write_value(client,
+					    SIS5595_REG_FAN_MIN(nr),
+					    data->fan_min[nr - 1]);
+		}
+	}
+}
+
+
+/* Return 0 for temp and disallow writes if pin used for in4 */
+void sis5595_temp(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct sis5595_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		if(data->maxins == 3) {
+			sis5595_update_client(client);
+			results[0] = TEMP_FROM_REG(data->temp_over);
+			results[1] = TEMP_FROM_REG(data->temp_hyst);
+			results[2] = TEMP_FROM_REG(data->temp);
+		} else {
+			results[0] = 0;
+			results[1] = 0;
+			results[2] = 0;
+		}
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if(data->maxins == 3) {
+			if (*nrels_mag >= 1) {
+				data->temp_over = TEMP_TO_REG(results[0]);
+				sis5595_write_value(client,
+				    SIS5595_REG_TEMP_OVER, data->temp_over);
+			}
+			if (*nrels_mag >= 2) {
+				data->temp_hyst = TEMP_TO_REG(results[1]);
+				sis5595_write_value(client,
+				    SIS5595_REG_TEMP_HYST, data->temp_hyst);
+			}
+		}
+	}
+}
+
+void sis5595_alarms(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results)
+{
+	struct sis5595_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		sis5595_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->alarms);
+		*nrels_mag = 1;
+	}
+}
+
+void sis5595_fan_div(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	struct sis5595_data *data = client->data;
+	int old;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		sis5595_update_client(client);
+		results[0] = DIV_FROM_REG(data->fan_div[0]);
+		results[1] = DIV_FROM_REG(data->fan_div[1]);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		old = sis5595_read_value(client, SIS5595_REG_FANDIV);
+		if (*nrels_mag >= 2) {
+			data->fan_div[1] = DIV_TO_REG(results[1]);
+			old = (old & 0x3f) | (data->fan_div[1] << 6);
+		}
+		if (*nrels_mag >= 1) {
+			data->fan_div[0] = DIV_TO_REG(results[0]);
+			old = (old & 0xcf) | (data->fan_div[0] << 4);
+			sis5595_write_value(client, SIS5595_REG_FANDIV, old);
+		}
+	}
+}
+
+static int __init sm_sis5595_init(void)
+{
+	int addr;
+
+	printk("sis5595.o version %s (%s)\n", LM_VERSION, LM_DATE);
+
+	if (sis5595_find_sis(&addr)) {
+		printk("sis5595.o: SIS5595 not detected, module not inserted.\n");
+		return -ENODEV;
+	}
+	normal_isa[0] = addr;
+
+	return i2c_add_driver(&sis5595_driver);
+}
+
+static void __exit sm_sis5595_exit(void)
+{
+	i2c_del_driver(&sis5595_driver);
+}
+
+
+
+MODULE_AUTHOR("Kyösti Mälkki <kmalkki@cc.hut.fi>");
+MODULE_DESCRIPTION("SiS 5595 Sensor device");
+
+module_init(sm_sis5595_init);
+module_exit(sm_sis5595_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/smsc47m1.c linux-2.4.27-leo/drivers/sensors/smsc47m1.c
--- linux-2.4.27/drivers/sensors/smsc47m1.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/smsc47m1.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,515 @@
+/*
+    smsc47m1.c - Part of lm_sensors, Linux kernel modules
+                for hardware monitoring
+                
+    Copyright (c) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+static int force_addr = 0;
+MODULE_PARM(force_addr, "i");
+MODULE_PARM_DESC(force_addr,
+		 "Initialize the base address of the sensors");
+
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { 0x0000, SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+SENSORS_INSMOD_1(smsc47m1);
+
+/* modified from kernel/include/traps.c */
+#define	REG	0x2e	/* The register to read/write */
+#define	DEV	0x07	/* Register: Logical device select */
+#define	VAL	0x2f	/* The value to read/write */
+#define PME	0x0a	/* The device with the fan registers in it */
+#define	DEVID	0x20	/* Register: Device ID */
+
+static inline void
+superio_outb(int reg, int val)
+{
+	outb(reg, REG);
+	outb(val, VAL);
+}
+
+static inline int
+superio_inb(int reg)
+{
+	outb(reg, REG);
+	return inb(VAL);
+}
+
+static inline void
+superio_select(void)
+{
+	outb(DEV, REG);
+	outb(PME, VAL);
+}
+
+static inline void
+superio_enter(void)
+{
+	outb(0x55, REG);
+}
+
+static inline void
+superio_exit(void)
+{
+	outb(0xAA, REG);
+}
+
+/*
+ * SMSC LPC47M10x (device id 0x59), LPC47M14x (device id 0x5F) and
+ * LPC47B27x (device id 0x51) have fan control.
+ * The 47M15x and 47M192 chips "with hardware monitoring block"
+ * can do much more besides (device id 0x60).
+ */
+#define SMSC_DEVID_MATCH(id) ((id) == 0x51 || (id) == 0x59 || (id) == 0x5F)
+
+#define SMSC_ACT_REG 0x20
+#define SMSC_BASE_REG 0x60
+
+#define SMSC_EXTENT 0x80
+
+#define SMSC47M1_REG_ALARM1 0x04
+#define SMSC47M1_REG_TPIN2 0x33
+#define SMSC47M1_REG_TPIN1 0x34
+#define SMSC47M1_REG_PPIN(nr) (0x37 - (nr))
+#define SMSC47M1_REG_PWM(nr) (0x55 + (nr))
+#define SMSC47M1_REG_FANDIV 0x58
+#define SMSC47M1_REG_FAN(nr) (0x58 + (nr))
+#define SMSC47M1_REG_FAN_MIN(nr) (0x5a + (nr))
+
+static inline u8 MIN_TO_REG(long rpm, int div)
+{
+	if (rpm == 0)
+		return 0;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT(192 - ((983040 + rpm * div / 2) / (rpm * div)),
+			     0, 191);
+}
+
+#define MIN_FROM_REG(val,div) ((val)>=192?0: \
+                                983040/((192-(val))*(div)))
+#define FAN_FROM_REG(val,div,preload) ((val)==0?-1:(val)==255?0: \
+                                983040/(((val)-preload)*(div)))
+#define DIV_FROM_REG(val) (1 << (val))
+#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
+/* reg is 6 middle bits; /proc is 8 bits */
+#define PWM_FROM_REG(val) (((val) << 1) & 0xfc)
+#define PWM_TO_REG(val)   (((SENSORS_LIMIT((val), 0, 255)) >> 1) & 0x7e)
+
+struct smsc47m1_data {
+	struct i2c_client client;
+	struct semaphore lock;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 fan[2];		/* Register value */
+	u8 fan_min[2];		/* Register value */
+	u8 fan_div[2];		/* Register encoding, shifted right */
+	u8 alarms;		/* Register encoding */
+	u8 pwm[2];		/* Register value (bit 7 is enable) */
+};
+
+
+static int smsc47m1_attach_adapter(struct i2c_adapter *adapter);
+static int smsc47m1_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int smsc47m1_detach_client(struct i2c_client *client);
+
+static int smsc47m1_read_value(struct i2c_client *client, u8 register);
+static int smsc47m1_write_value(struct i2c_client *client, u8 register,
+			       u8 value);
+static void smsc47m1_update_client(struct i2c_client *client);
+static void smsc47m1_init_client(struct i2c_client *client);
+static int smsc47m1_find(int *address);
+
+
+static void smsc47m1_fan(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void smsc47m1_alarms(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+static void smsc47m1_fan_div(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void smsc47m1_pwm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+
+static int smsc47m1_id = 0;
+
+static struct i2c_driver smsc47m1_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "SMSC 47M1xx fan monitor",
+	.id		= I2C_DRIVERID_SMSC47M1,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= smsc47m1_attach_adapter,
+	.detach_client	= smsc47m1_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+#define SMSC47M1_SYSCTL_FAN1 1101   /* Rotations/min */
+#define SMSC47M1_SYSCTL_FAN2 1102
+#define SMSC47M1_SYSCTL_PWM1 1401
+#define SMSC47M1_SYSCTL_PWM2 1402
+#define SMSC47M1_SYSCTL_FAN_DIV 2000        /* 1, 2, 4 or 8 */
+#define SMSC47M1_SYSCTL_ALARMS 2004    /* bitvector */
+
+#define SMSC47M1_ALARM_FAN1 0x0001
+#define SMSC47M1_ALARM_FAN2 0x0002
+
+/* -- SENSORS SYSCTL END -- */
+
+static ctl_table smsc47m1_dir_table_template[] = {
+	{SMSC47M1_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &smsc47m1_fan},
+	{SMSC47M1_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &smsc47m1_fan},
+	{SMSC47M1_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &smsc47m1_fan_div},
+	{SMSC47M1_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &smsc47m1_alarms},
+	{SMSC47M1_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &smsc47m1_pwm},
+	{SMSC47M1_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &smsc47m1_pwm},
+	{0}
+};
+
+static int smsc47m1_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, smsc47m1_detect);
+}
+
+static int smsc47m1_find(int *address)
+{
+	u16 val;
+
+	superio_enter();
+	val= superio_inb(DEVID);
+	if (!SMSC_DEVID_MATCH(val)) {
+		superio_exit();
+		return -ENODEV;
+	}
+
+	superio_select();
+	val = (superio_inb(SMSC_BASE_REG) << 8) |
+	       superio_inb(SMSC_BASE_REG + 1);
+	*address = val & ~(SMSC_EXTENT - 1);
+	if (*address == 0 && force_addr == 0) {
+		printk("smsc47m1.o: base address not set - use force_addr=0xaddr\n");
+		superio_exit();
+		return -ENODEV;
+	}
+	if (force_addr)
+		*address = force_addr;	/* so detect will get called */
+
+	superio_exit();
+	return 0;
+}
+
+int smsc47m1_detect(struct i2c_adapter *adapter, int address,
+		   unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct smsc47m1_data *data;
+	int err = 0;
+	const char *type_name = "smsc47m1";
+	const char *client_name = "47M1xx chip";
+
+	if (!i2c_is_isa_adapter(adapter)) {
+		return 0;
+	}
+
+	if(force_addr)
+		address = force_addr & ~(SMSC_EXTENT - 1);
+	if (check_region(address, SMSC_EXTENT)) {
+		printk("smsc47m1.o: region 0x%x already in use!\n", address);
+		return -ENODEV;
+	}
+	if(force_addr) {
+		printk("smsc47m1.o: forcing ISA address 0x%04X\n", address);
+		superio_enter();
+		superio_select();
+		superio_outb(SMSC_BASE_REG, address >> 8);
+		superio_outb(SMSC_BASE_REG+1, address & 0xff);
+		superio_exit();
+	}
+
+	if (!(data = kmalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	init_MUTEX(&data->lock);
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &smsc47m1_driver;
+	new_client->flags = 0;
+
+	request_region(address, SMSC_EXTENT, "smsc47m1-fans");
+	strcpy(new_client->name, client_name);
+
+	new_client->id = smsc47m1_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	if ((i = i2c_register_entry((struct i2c_client *) new_client,
+					type_name,
+					smsc47m1_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	smsc47m1_init_client(new_client);
+	return 0;
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+	release_region(address, SMSC_EXTENT);
+	kfree(data);
+	return err;
+}
+
+static int smsc47m1_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct smsc47m1_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("smsc47m1.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	release_region(client->addr, SMSC_EXTENT);
+	kfree(client->data);
+
+	return 0;
+}
+
+static int smsc47m1_read_value(struct i2c_client *client, u8 reg)
+{
+	int res;
+
+	down(&(((struct smsc47m1_data *) (client->data))->lock));
+	res = inb_p(client->addr + reg);
+	up(&(((struct smsc47m1_data *) (client->data))->lock));
+	return res;
+}
+
+static int smsc47m1_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+	down(&(((struct smsc47m1_data *) (client->data))->lock));
+	outb_p(value, client->addr + reg);
+	up(&(((struct smsc47m1_data *) (client->data))->lock));
+	return 0;
+}
+
+static void smsc47m1_init_client(struct i2c_client *client)
+{
+	/* configure pins for tach function */
+	smsc47m1_write_value(client, SMSC47M1_REG_TPIN1, 0x05);
+	smsc47m1_write_value(client, SMSC47M1_REG_TPIN2, 0x05);
+}
+
+static void smsc47m1_update_client(struct i2c_client *client)
+{
+	struct smsc47m1_data *data = client->data;
+	int i;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+		for (i = 1; i <= 2; i++) {
+			data->fan[i - 1] =
+			    smsc47m1_read_value(client, SMSC47M1_REG_FAN(i));
+			data->fan_min[i - 1] =
+			    smsc47m1_read_value(client, SMSC47M1_REG_FAN_MIN(i));
+			data->pwm[i - 1] =
+			    smsc47m1_read_value(client, SMSC47M1_REG_PWM(i));
+		}
+
+		i = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV);
+		data->fan_div[0] = (i >> 4) & 0x03;
+		data->fan_div[1] = i >> 6;
+		data->alarms =
+		        smsc47m1_read_value(client, SMSC47M1_REG_ALARM1) >> 6;
+		if(data->alarms)
+			smsc47m1_write_value(client, SMSC47M1_REG_ALARM1, 0xc0);
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+void smsc47m1_fan(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct smsc47m1_data *data = client->data;
+	int nr = ctl_name - SMSC47M1_SYSCTL_FAN1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		smsc47m1_update_client(client);
+		results[0] = MIN_FROM_REG(data->fan_min[nr - 1],
+					  DIV_FROM_REG(data->fan_div[nr - 1]));
+		results[1] = FAN_FROM_REG(data->fan[nr - 1],
+					  DIV_FROM_REG(data->fan_div[nr - 1]),
+		                          data->fan_min[nr - 1]);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr - 1] = MIN_TO_REG(results[0],
+							   DIV_FROM_REG
+							   (data->
+							    fan_div[nr-1]));
+			smsc47m1_write_value(client, SMSC47M1_REG_FAN_MIN(nr),
+					    data->fan_min[nr - 1]);
+		}
+	}
+}
+
+
+void smsc47m1_alarms(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results)
+{
+	struct smsc47m1_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		smsc47m1_update_client(client);
+		results[0] = data->alarms;
+		*nrels_mag = 1;
+	}
+}
+
+void smsc47m1_fan_div(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	struct smsc47m1_data *data = client->data;
+	int old;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		smsc47m1_update_client(client);
+		results[0] = DIV_FROM_REG(data->fan_div[0]);
+		results[1] = DIV_FROM_REG(data->fan_div[1]);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		old = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV);
+		if (*nrels_mag >= 2) {
+			data->fan_div[1] = DIV_TO_REG(results[1]);
+			old = (old & 0x3f) | (data->fan_div[1] << 6);
+		}
+		if (*nrels_mag >= 1) {
+			data->fan_div[0] = DIV_TO_REG(results[0]);
+			old = (old & 0xcf) | (data->fan_div[0] << 4);
+			smsc47m1_write_value(client, SMSC47M1_REG_FANDIV, old);
+		}
+	}
+}
+
+void smsc47m1_pwm(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct smsc47m1_data *data = client->data;
+	int nr = 1 + ctl_name - SMSC47M1_SYSCTL_PWM1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		smsc47m1_update_client(client);
+		results[0] = PWM_FROM_REG(data->pwm[nr - 1]);
+		results[1] = data->pwm[nr - 1] & 0x01;
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->pwm[nr - 1] &= 0x81;
+			data->pwm[nr - 1] |= PWM_TO_REG(results[0]);
+			if (*nrels_mag >= 2) {
+				if(results[1] && (!(data->pwm[nr-1] & 0x01))) {
+					/* enable PWM */
+/* hope BIOS did it already
+					smsc47m1_write_value(client,
+					          SMSC47M1_REG_PPIN(nr), 0x04);
+*/
+					data->pwm[nr - 1] |= 0x01;
+				} else if((!results[1]) && (data->pwm[nr-1] & 0x01)) {
+					/* disable PWM */
+					data->pwm[nr - 1] &= 0xfe;
+				}
+			}
+			smsc47m1_write_value(client, SMSC47M1_REG_PWM(nr),
+					     data->pwm[nr - 1]);
+		}
+	}
+}
+
+static int __init sm_smsc47m1_init(void)
+{
+	int addr;
+
+	printk("smsc47m1.o version %s (%s)\n", LM_VERSION, LM_DATE);
+
+	if (smsc47m1_find(&addr)) {
+		printk("smsc47m1.o: SMSC 47M1xx not detected, module not inserted.\n");
+		return -ENODEV;
+	}
+	normal_isa[0] = addr;
+
+	return i2c_add_driver(&smsc47m1_driver);
+}
+
+static void __exit sm_smsc47m1_exit(void)
+{
+	i2c_del_driver(&smsc47m1_driver);
+}
+
+
+
+MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("SMSC 47M1xx Fan sensors");
+MODULE_LICENSE("GPL");
+
+module_init(sm_smsc47m1_init);
+module_exit(sm_smsc47m1_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/thmc50.c linux-2.4.27-leo/drivers/sensors/thmc50.c
--- linux-2.4.27/drivers/sensors/thmc50.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/thmc50.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,496 @@
+/*
+    thmc50.c - Part of lm_sensors, Linux kernel modules for hardware
+             monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl> and
+    Philip Edelbrock <phil@netroedge.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define DEBUG 1
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x2D, 0x2E, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(thmc50);
+
+/* Many THMC50 constants specified below */
+
+/* The THMC50 registers */
+#define THMC50_REG_TEMP 0x27
+#define THMC50_REG_CONF 0x40
+#define THMC50_REG_TEMP_HYST 0x3A
+#define THMC50_REG_TEMP_OS 0x39
+
+#define THMC50_REG_TEMP_TRIP 0x13
+#define THMC50_REG_TEMP_REMOTE_TRIP 0x14
+#define THMC50_REG_TEMP_DEFAULT_TRIP 0x17
+#define THMC50_REG_TEMP_REMOTE_DEFAULT_TRIP 0x18
+#define THMC50_REG_ANALOG_OUT 0x19
+#define THMC50_REG_REMOTE_TEMP 0x26
+#define THMC50_REG_REMOTE_TEMP_HYST 0x38
+#define THMC50_REG_REMOTE_TEMP_OS 0x37
+
+#define THMC50_REG_INTER 0x41
+#define THMC50_REG_INTER_MIRROR 0x4C
+#define THMC50_REG_INTER_MASK 0x43
+
+#define THMC50_REG_COMPANY_ID 0x3E
+#define THMC50_REG_DIE_CODE 0x3F
+
+
+/* Conversions. Rounding and limit checking is only done on the TO_REG
+   variants. Note that you should be a bit careful with which arguments
+   these macros are called: arguments may be evaluated more than once.
+   Fixing this is just not worth it. */
+#define TEMP_FROM_REG(val) ((val>127)?val - 0x0100:val)
+#define TEMP_TO_REG(val)   ((val<0)?0x0100+val:val)
+
+/* Each client has this additional data */
+struct thmc50_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u16 temp, temp_os, temp_hyst,
+	    remote_temp, remote_temp_os, remote_temp_hyst,
+	    inter, inter_mask, die_code, analog_out;	/* Register values */
+};
+
+static int thmc50_attach_adapter(struct i2c_adapter *adapter);
+static int thmc50_detect(struct i2c_adapter *adapter, int address,
+			 unsigned short flags, int kind);
+static void thmc50_init_client(struct i2c_client *client);
+static int thmc50_detach_client(struct i2c_client *client);
+
+static int thmc50_read_value(struct i2c_client *client, u8 reg);
+static int thmc50_write_value(struct i2c_client *client, u8 reg,
+			      u16 value);
+static void thmc50_temp(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void thmc50_remote_temp(struct i2c_client *client, int operation,
+			       int ctl_name, int *nrels_mag,
+			       long *results);
+static void thmc50_inter(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void thmc50_inter_mask(struct i2c_client *client, int operation,
+			      int ctl_name, int *nrels_mag, long *results);
+static void thmc50_die_code(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void thmc50_analog_out(struct i2c_client *client, int operation,
+			      int ctl_name, int *nrels_mag, long *results);
+static void thmc50_update_client(struct i2c_client *client);
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver thmc50_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "THMC50 sensor chip driver",
+	.id		= I2C_DRIVERID_THMC50,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= thmc50_attach_adapter,
+	.detach_client	= thmc50_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+
+#define THMC50_SYSCTL_TEMP 1200	/* Degrees Celcius */
+#define THMC50_SYSCTL_REMOTE_TEMP 1201	/* Degrees Celcius */
+#define THMC50_SYSCTL_INTER 1202
+#define THMC50_SYSCTL_INTER_MASK 1203
+#define THMC50_SYSCTL_DIE_CODE 1204
+#define THMC50_SYSCTL_ANALOG_OUT 1205
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected THMC50. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized
+   when a new copy is allocated. */
+static ctl_table thmc50_dir_table_template[] = {
+	{THMC50_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &thmc50_temp},
+	{THMC50_SYSCTL_REMOTE_TEMP, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &thmc50_remote_temp},
+	{THMC50_SYSCTL_INTER, "inter", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &thmc50_inter},
+	{THMC50_SYSCTL_INTER_MASK, "inter_mask", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &thmc50_inter_mask},
+	{THMC50_SYSCTL_DIE_CODE, "die_code", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &thmc50_die_code},
+	{THMC50_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &thmc50_analog_out},
+	{0}
+};
+
+
+static int thmc50_id = 0;
+
+static int thmc50_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, thmc50_detect);
+}
+
+/* This function is called by i2c_detect */
+int thmc50_detect(struct i2c_adapter *adapter, int address,
+		  unsigned short flags, int kind)
+{
+	int company, i;
+	struct i2c_client *new_client;
+	struct thmc50_data *data;
+	int err = 0;
+	const char *type_name, *client_name;
+
+#ifdef DEBUG
+	printk("thmc50.o: Probing for THMC50 at 0x%2X on bus %d\n",
+	       address, adapter->id);
+#endif
+
+	/* Make sure we aren't probing the ISA bus!! This is just a safety check
+	   at this moment; i2c_detect really won't call us. */
+#ifdef DEBUG
+	if (i2c_is_isa_adapter(adapter)) {
+		printk
+		    ("thmc50.o: thmc50_detect called for an ISA bus adapter?!?\n");
+		return 0;
+	}
+#endif
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		goto ERROR0;
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access thmc50_{read,write}_value. */
+	if (!(data = kmalloc(sizeof(struct thmc50_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &thmc50_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+	company =
+	    i2c_smbus_read_byte_data(new_client, THMC50_REG_COMPANY_ID);
+
+	if (company != 0x49) {
+#ifdef DEBUG
+		printk
+		    ("thmc50.o: Detect of THMC50 failed (reg 3E: 0x%X)\n",
+		     company);
+#endif
+		goto ERROR1;
+	}
+
+	/* Determine the chip type - only one kind supported! */
+	kind = thmc50;
+
+	if (kind == thmc50) {
+		type_name = "thmc50";
+		client_name = "THMC50 chip";
+	} else {
+#ifdef DEBUG
+		printk("thmc50.o: Internal error: unknown kind (%d)?!?",
+		       kind);
+#endif
+		goto ERROR1;
+	}
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+
+	new_client->id = thmc50_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client, type_name,
+					thmc50_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	thmc50_init_client(new_client);
+	return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+      ERROR1:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int thmc50_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct thmc50_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("thmc50.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+
+	return 0;
+}
+
+
+/* All registers are word-sized, except for the configuration register.
+   THMC50 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+static int thmc50_read_value(struct i2c_client *client, u8 reg)
+{
+	return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* All registers are word-sized, except for the configuration register.
+   THMC50 uses a high-byte first convention, which is exactly opposite to
+   the usual practice. */
+static int thmc50_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static void thmc50_init_client(struct i2c_client *client)
+{
+	thmc50_write_value(client, THMC50_REG_CONF, 1);
+}
+
+static void thmc50_update_client(struct i2c_client *client)
+{
+	struct thmc50_data *data = client->data;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+#ifdef DEBUG
+		printk("Starting thmc50 update\n");
+#endif
+
+		data->temp = thmc50_read_value(client, THMC50_REG_TEMP);
+		data->temp_os =
+		    thmc50_read_value(client, THMC50_REG_TEMP_OS);
+		data->temp_hyst =
+		    thmc50_read_value(client, THMC50_REG_TEMP_HYST);
+		data->remote_temp =
+		    thmc50_read_value(client, THMC50_REG_REMOTE_TEMP);
+		data->remote_temp_os =
+		    thmc50_read_value(client, THMC50_REG_REMOTE_TEMP_OS);
+		data->remote_temp_hyst =
+		    thmc50_read_value(client, THMC50_REG_REMOTE_TEMP_HYST);
+		data->inter = thmc50_read_value(client, THMC50_REG_INTER);
+		data->inter_mask =
+		    thmc50_read_value(client, THMC50_REG_INTER_MASK);
+		data->die_code =
+		    thmc50_read_value(client, THMC50_REG_DIE_CODE);
+		data->analog_out =
+		    thmc50_read_value(client, THMC50_REG_ANALOG_OUT);
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+void thmc50_temp(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct thmc50_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		thmc50_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_os);
+		results[1] = TEMP_FROM_REG(data->temp_hyst);
+		results[2] = TEMP_FROM_REG(data->temp);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_os = TEMP_TO_REG(results[0]);
+			thmc50_write_value(client, THMC50_REG_TEMP_OS,
+					   data->temp_os);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_hyst = TEMP_TO_REG(results[1]);
+			thmc50_write_value(client, THMC50_REG_TEMP_HYST,
+					   data->temp_hyst);
+		}
+	}
+}
+
+
+void thmc50_remote_temp(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results)
+{
+	struct thmc50_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		thmc50_update_client(client);
+		results[0] = TEMP_FROM_REG(data->remote_temp_os);
+		results[1] = TEMP_FROM_REG(data->remote_temp_hyst);
+		results[2] = TEMP_FROM_REG(data->remote_temp);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->remote_temp_os = TEMP_TO_REG(results[0]);
+			thmc50_write_value(client,
+					   THMC50_REG_REMOTE_TEMP_OS,
+					   data->remote_temp_os);
+		}
+		if (*nrels_mag >= 2) {
+			data->remote_temp_hyst = TEMP_TO_REG(results[1]);
+			thmc50_write_value(client,
+					   THMC50_REG_REMOTE_TEMP_HYST,
+					   data->remote_temp_hyst);
+		}
+	}
+}
+
+
+void thmc50_inter(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct thmc50_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		thmc50_update_client(client);
+		results[0] = data->inter;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		printk("thmc50.o: No writes to Interrupt register!\n");
+	}
+}
+
+
+void thmc50_inter_mask(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results)
+{
+	struct thmc50_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		thmc50_update_client(client);
+		results[0] = data->inter_mask;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->inter_mask = results[0];
+			thmc50_write_value(client, THMC50_REG_INTER_MASK,
+					   data->inter_mask);
+		}
+	}
+}
+
+
+void thmc50_die_code(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	struct thmc50_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		thmc50_update_client(client);
+		results[0] = data->die_code;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		printk("thmc50.o: No writes to Die-Code register!\n");
+	}
+}
+
+
+void thmc50_analog_out(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results)
+{
+	struct thmc50_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		thmc50_update_client(client);
+		results[0] = data->analog_out;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->analog_out = results[0];
+			thmc50_write_value(client, THMC50_REG_ANALOG_OUT,
+					   data->analog_out);
+		}
+	}
+}
+
+
+
+
+static int __init sm_thmc50_init(void)
+{
+	printk("thmc50.o version %s (%s)\n", LM_VERSION, LM_DATE);
+
+	return i2c_add_driver(&thmc50_driver);
+}
+
+static void __exit sm_thmc50_exit(void)
+{
+	i2c_del_driver(&thmc50_driver);
+}
+
+
+
+MODULE_AUTHOR
+    ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("THMC50 driver");
+
+module_init(sm_thmc50_init);
+module_exit(sm_thmc50_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/via686a.c linux-2.4.27-leo/drivers/sensors/via686a.c
--- linux-2.4.27/drivers/sensors/via686a.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/via686a.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,849 @@
+/*
+    via686a.c - Part of lm_sensors, Linux kernel modules
+                for hardware monitoring
+                
+    Copyright (c) 1998 - 2002  Frodo Looijaard <frodol@dds.nl>,
+                        Kyösti Mälkki <kmalkki@cc.hut.fi>,
+			Mark Studebaker <mdsxyz123@yahoo.com>,
+			and Bob Dougherty <bobd@stanford.edu>
+    (Some conversion-factor data were contributed by Jonathan Teh Soon Yew 
+    <j.teh@iname.com> and Alex van Kaam <darkside@chello.nl>.)
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+    Supports the Via VT82C686A, VT82C686B south bridges.
+    Reports all as a 686A.
+    See doc/chips/via686a for details.
+    Warning - only supports a single device.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+
+/* If force_addr is set to anything different from 0, we forcibly enable
+   the device at the given address. */
+static int force_addr = 0;
+MODULE_PARM(force_addr, "i");
+MODULE_PARM_DESC(force_addr,
+		 "Initialize the base address of the sensors");
+
+/* Addresses to scan.
+   Note that we can't determine the ISA address until we have initialized
+   our module */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { 0x0000, SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(via686a);
+
+/*
+   The Via 686a southbridge has a LM78-like chip integrated on the same IC.
+   This driver is a customized copy of lm78.c
+*/
+
+/* Many VIA686A constants specified below */
+
+/* Length of ISA address segment */
+#define VIA686A_EXTENT 0x80
+#define VIA686A_BASE_REG 0x70
+#define VIA686A_ENABLE_REG 0x74
+
+/* The VIA686A registers */
+/* ins numbered 0-4 */
+#define VIA686A_REG_IN_MAX(nr) (0x2b + ((nr) * 2))
+#define VIA686A_REG_IN_MIN(nr) (0x2c + ((nr) * 2))
+#define VIA686A_REG_IN(nr)     (0x22 + (nr))
+
+/* fans numbered 1-2 */
+#define VIA686A_REG_FAN_MIN(nr) (0x3a + (nr))
+#define VIA686A_REG_FAN(nr)     (0x28 + (nr))
+
+// the following values are as speced by VIA:
+static const u8 regtemp[] = { 0x20, 0x21, 0x1f };
+static const u8 regover[] = { 0x39, 0x3d, 0x1d };
+static const u8 reghyst[] = { 0x3a, 0x3e, 0x1e };
+
+/* temps numbered 1-3 */
+#define VIA686A_REG_TEMP(nr)		(regtemp[(nr) - 1])
+#define VIA686A_REG_TEMP_OVER(nr)	(regover[(nr) - 1])
+#define VIA686A_REG_TEMP_HYST(nr)	(reghyst[(nr) - 1])
+#define VIA686A_REG_TEMP_LOW1	0x4b	// bits 7-6
+#define VIA686A_REG_TEMP_LOW23	0x49	// 2 = bits 5-4, 3 = bits 7-6
+
+#define VIA686A_REG_ALARM1 0x41
+#define VIA686A_REG_ALARM2 0x42
+#define VIA686A_REG_FANDIV 0x47
+#define VIA686A_REG_CONFIG 0x40
+// The following register sets temp interrupt mode (bits 1-0 for temp1, 
+// 3-2 for temp2, 5-4 for temp3).  Modes are:
+//    00 interrupt stays as long as value is out-of-range
+//    01 interrupt is cleared once register is read (default)
+//    10 comparator mode- like 00, but ignores hysteresis
+//    11 same as 00
+#define VIA686A_REG_TEMP_MODE 0x4b
+// We'll just assume that you want to set all 3 simultaneously:
+#define VIA686A_TEMP_MODE_MASK 0x3F
+#define VIA686A_TEMP_MODE_CONTINUOUS (0x00)
+
+/* Conversions. Limit checking is only done on the TO_REG
+   variants. */
+
+/********* VOLTAGE CONVERSIONS (Bob Dougherty) ********/
+// From HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew):
+// voltagefactor[0]=1.25/2628; (2628/1.25=2102.4)   // Vccp
+// voltagefactor[1]=1.25/2628; (2628/1.25=2102.4)   // +2.5V
+// voltagefactor[2]=1.67/2628; (2628/1.67=1573.7)   // +3.3V
+// voltagefactor[3]=2.6/2628;  (2628/2.60=1010.8)   // +5V
+// voltagefactor[4]=6.3/2628;  (2628/6.30=417.14)   // +12V
+// in[i]=(data[i+2]*25.0+133)*voltagefactor[i];
+// That is:
+// volts = (25*regVal+133)*factor
+// regVal = (volts/factor-133)/25
+// (These conversions were contributed by Jonathan Teh Soon Yew 
+// <j.teh@iname.com>)
+static inline u8 IN_TO_REG(long val, int inNum)
+{
+	/* To avoid floating point, we multiply constants by 10 (100 for +12V).
+	   Rounding is done (120500 is actually 133000 - 12500).
+	   Remember that val is expressed in 0.01V/bit, which is why we divide
+	   by an additional 1000 (10000 for +12V): 100 for val and 10 (100)
+	   for the constants. */
+	if (inNum <= 1)
+		return (u8)
+		    SENSORS_LIMIT((val * 21024 - 120500) / 25000, 0, 255);
+	else if (inNum == 2)
+		return (u8)
+		    SENSORS_LIMIT((val * 15737 - 120500) / 25000, 0, 255);
+	else if (inNum == 3)
+		return (u8)
+		    SENSORS_LIMIT((val * 10108 - 120500) / 25000, 0, 255);
+	else
+		return (u8)
+		    SENSORS_LIMIT((val * 41714 - 1205000) / 250000, 0, 255);
+}
+
+static inline long IN_FROM_REG(u8 val, int inNum)
+{
+	/* To avoid floating point, we multiply constants by 10 (100 for +12V).
+	   We also multiply them by 100 because we want 0.01V/bit for the
+	   output value. Rounding is done. */
+	if (inNum <= 1)
+		return (long) ((25000 * val + 133000 + 21024 / 2) / 21024);
+	else if (inNum == 2)
+		return (long) ((25000 * val + 133000 + 15737 / 2) / 15737);
+	else if (inNum == 3)
+		return (long) ((25000 * val + 133000 + 10108 / 2) / 10108);
+	else
+		return (long) ((250000 * val + 1330000 + 41714 / 2) / 41714);
+}
+
+/********* FAN RPM CONVERSIONS ********/
+// Higher register values = slower fans (the fan's strobe gates a counter).
+// But this chip saturates back at 0, not at 255 like all the other chips.
+// So, 0 means 0 RPM
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+	if (rpm == 0)
+		return 0;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 255);
+}
+
+#define FAN_FROM_REG(val,div) ((val)==0?0:(val)==255?0:1350000/((val)*(div)))
+
+/******** TEMP CONVERSIONS (Bob Dougherty) *********/
+// linear fits from HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew)
+//      if(temp<169)
+//              return double(temp)*0.427-32.08;
+//      else if(temp>=169 && temp<=202)
+//              return double(temp)*0.582-58.16;
+//      else
+//              return double(temp)*0.924-127.33;
+//
+// A fifth-order polynomial fits the unofficial data (provided by Alex van 
+// Kaam <darkside@chello.nl>) a bit better.  It also give more reasonable 
+// numbers on my machine (ie. they agree with what my BIOS tells me).  
+// Here's the fifth-order fit to the 8-bit data:
+// temp = 1.625093e-10*val^5 - 1.001632e-07*val^4 + 2.457653e-05*val^3 - 
+//        2.967619e-03*val^2 + 2.175144e-01*val - 7.090067e+0.
+//
+// (2000-10-25- RFD: thanks to Uwe Andersen <uandersen@mayah.com> for 
+// finding my typos in this formula!)
+//
+// Alas, none of the elegant function-fit solutions will work because we 
+// aren't allowed to use floating point in the kernel and doing it with 
+// integers doesn't rpovide enough precision.  So we'll do boring old 
+// look-up table stuff.  The unofficial data (see below) have effectively 
+// 7-bit resolution (they are rounded to the nearest degree).  I'm assuming 
+// that the transfer function of the device is monotonic and smooth, so a 
+// smooth function fit to the data will allow us to get better precision.  
+// I used the 5th-order poly fit described above and solved for
+// VIA register values 0-255.  I *10 before rounding, so we get tenth-degree 
+// precision.  (I could have done all 1024 values for our 10-bit readings, 
+// but the function is very linear in the useful range (0-80 deg C), so 
+// we'll just use linear interpolation for 10-bit readings.)  So, tempLUT 
+// is the temp at via register values 0-255:
+static const long tempLUT[] =
+    { -709, -688, -667, -646, -627, -607, -589, -570, -553, -536, -519,
+	    -503, -487, -471, -456, -442, -428, -414, -400, -387, -375,
+	    -362, -350, -339, -327, -316, -305, -295, -285, -275, -265,
+	    -255, -246, -237, -229, -220, -212, -204, -196, -188, -180,
+	    -173, -166, -159, -152, -145, -139, -132, -126, -120, -114,
+	    -108, -102, -96, -91, -85, -80, -74, -69, -64, -59, -54, -49,
+	    -44, -39, -34, -29, -25, -20, -15, -11, -6, -2, 3, 7, 12, 16,
+	    20, 25, 29, 33, 37, 42, 46, 50, 54, 59, 63, 67, 71, 75, 79, 84,
+	    88, 92, 96, 100, 104, 109, 113, 117, 121, 125, 130, 134, 138,
+	    142, 146, 151, 155, 159, 163, 168, 172, 176, 181, 185, 189,
+	    193, 198, 202, 206, 211, 215, 219, 224, 228, 232, 237, 241,
+	    245, 250, 254, 259, 263, 267, 272, 276, 281, 285, 290, 294,
+	    299, 303, 307, 312, 316, 321, 325, 330, 334, 339, 344, 348,
+	    353, 357, 362, 366, 371, 376, 380, 385, 390, 395, 399, 404,
+	    409, 414, 419, 423, 428, 433, 438, 443, 449, 454, 459, 464,
+	    469, 475, 480, 486, 491, 497, 502, 508, 514, 520, 526, 532,
+	    538, 544, 551, 557, 564, 571, 578, 584, 592, 599, 606, 614,
+	    621, 629, 637, 645, 654, 662, 671, 680, 689, 698, 708, 718,
+	    728, 738, 749, 759, 770, 782, 793, 805, 818, 830, 843, 856,
+	    870, 883, 898, 912, 927, 943, 958, 975, 991, 1008, 1026, 1044,
+	    1062, 1081, 1101, 1121, 1141, 1162, 1184, 1206, 1229, 1252,
+	    1276, 1301, 1326, 1352, 1378, 1406, 1434, 1462
+};
+
+/* the original LUT values from Alex van Kaam <darkside@chello.nl> 
+   (for via register values 12-240):
+{-50,-49,-47,-45,-43,-41,-39,-38,-37,-35,-34,-33,-32,-31,
+-30,-29,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-20,-19,-18,-17,-17,-16,-15,
+-15,-14,-14,-13,-12,-12,-11,-11,-10,-9,-9,-8,-8,-7,-7,-6,-6,-5,-5,-4,-4,-3,
+-3,-2,-2,-1,-1,0,0,1,1,1,3,3,3,4,4,4,5,5,5,6,6,7,7,8,8,9,9,9,10,10,11,11,12,
+12,12,13,13,13,14,14,15,15,16,16,16,17,17,18,18,19,19,20,20,21,21,21,22,22,
+22,23,23,24,24,25,25,26,26,26,27,27,27,28,28,29,29,30,30,30,31,31,32,32,33,
+33,34,34,35,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,
+45,46,46,47,48,48,49,49,50,51,51,52,52,53,53,54,55,55,56,57,57,58,59,59,60,
+61,62,62,63,64,65,66,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,83,84,
+85,86,88,89,91,92,94,96,97,99,101,103,105,107,109,110};
+*/
+
+// Here's the reverse LUT.  I got it by doing a 6-th order poly fit (needed
+// an extra term for a good fit to these inverse data!) and then 
+// solving for each temp value from -50 to 110 (the useable range for 
+// this chip).  Here's the fit: 
+// viaRegVal = -1.160370e-10*val^6 +3.193693e-08*val^5 - 1.464447e-06*val^4 
+// - 2.525453e-04*val^3 + 1.424593e-02*val^2 + 2.148941e+00*val +7.275808e+01)
+// Note that n=161:
+static const u8 viaLUT[] =
+    { 12, 12, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 20, 21, 22, 23,
+	    23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 39, 40,
+	    41, 43, 45, 46, 48, 49, 51, 53, 55, 57, 59, 60, 62, 64, 66,
+	    69, 71, 73, 75, 77, 79, 82, 84, 86, 88, 91, 93, 95, 98, 100,
+	    103, 105, 107, 110, 112, 115, 117, 119, 122, 124, 126, 129,
+	    131, 134, 136, 138, 140, 143, 145, 147, 150, 152, 154, 156,
+	    158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180,
+	    182, 183, 185, 187, 188, 190, 192, 193, 195, 196, 198, 199,
+	    200, 202, 203, 205, 206, 207, 208, 209, 210, 211, 212, 213,
+	    214, 215, 216, 217, 218, 219, 220, 221, 222, 222, 223, 224,
+	    225, 226, 226, 227, 228, 228, 229, 230, 230, 231, 232, 232,
+	    233, 233, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239,
+	    239, 240
+};
+
+/* Converting temps to (8-bit) hyst and over registers
+   No interpolation here.
+   The +50 is because the temps start at -50 */
+static inline u8 TEMP_TO_REG(long val)
+{
+	return viaLUT[val <= -500 ? 0 : val >= 1100 ? 160 : 
+		      (val < 0 ? val - 5 : val + 5) / 10 + 50];
+}
+
+/* for 8-bit temperature hyst and over registers */
+#define TEMP_FROM_REG(val) (tempLUT[(val)])
+
+/* for 10-bit temperature readings */
+// You might _think_ this is too long to inline, but's it's really only
+// called once...
+static inline long TEMP_FROM_REG10(u16 val)
+{
+	// the temp values are already *10, so we don't need to do that.
+	long temp;
+	u16 eightBits = val >> 2;
+	u16 twoBits = val & 3;
+
+	/* no interpolation for these */
+	if (twoBits == 0 || eightBits == 255)
+		return (long) tempLUT[eightBits];
+
+	/* do some linear interpolation */
+	temp = (4 - twoBits) * tempLUT[eightBits]
+	     + twoBits * tempLUT[eightBits + 1];
+	/* achieve rounding */
+	return (temp < 0 ? temp - 2 : temp + 2) / 4;
+}
+
+#define ALARMS_FROM_REG(val) (val)
+
+#define DIV_FROM_REG(val) (1 << (val))
+#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
+
+/* For the VIA686A, we need to keep some data in memory.
+   The structure is dynamically allocated, at the same time when a new
+   via686a client is allocated. */
+struct via686a_data {
+	struct i2c_client client;
+	struct semaphore lock;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 in[5];		/* Register value */
+	u8 in_max[5];		/* Register value */
+	u8 in_min[5];		/* Register value */
+	u8 fan[2];		/* Register value */
+	u8 fan_min[2];		/* Register value */
+	u16 temp[3];		/* Register value 10 bit */
+	u8 temp_over[3];	/* Register value */
+	u8 temp_hyst[3];	/* Register value */
+	u8 fan_div[2];		/* Register encoding, shifted right */
+	u16 alarms;		/* Register encoding, combined */
+};
+
+static struct pci_dev *s_bridge;	/* pointer to the (only) via686a */
+
+static int via686a_attach_adapter(struct i2c_adapter *adapter);
+static int via686a_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int via686a_detach_client(struct i2c_client *client);
+
+static int via686a_read_value(struct i2c_client *client, u8 register);
+static void via686a_write_value(struct i2c_client *client, u8 register,
+				u8 value);
+static void via686a_update_client(struct i2c_client *client);
+static void via686a_init_client(struct i2c_client *client);
+
+
+static void via686a_in(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void via686a_fan(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void via686a_temp(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void via686a_alarms(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+static void via686a_fan_div(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+
+static int via686a_id = 0;
+
+/* The driver. I choose to use type i2c_driver, as at is identical to both
+   smbus_driver and isa_driver, and clients could be of either kind */
+static struct i2c_driver via686a_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "VIA 686A",
+	.id		= I2C_DRIVERID_VIA686A,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= via686a_attach_adapter,
+	.detach_client	= via686a_detach_client,
+};
+
+
+
+/* The /proc/sys entries */
+
+/* -- SENSORS SYSCTL START -- */
+#define VIA686A_SYSCTL_IN0 1000
+#define VIA686A_SYSCTL_IN1 1001
+#define VIA686A_SYSCTL_IN2 1002
+#define VIA686A_SYSCTL_IN3 1003
+#define VIA686A_SYSCTL_IN4 1004
+#define VIA686A_SYSCTL_FAN1 1101
+#define VIA686A_SYSCTL_FAN2 1102
+#define VIA686A_SYSCTL_TEMP 1200
+#define VIA686A_SYSCTL_TEMP2 1201
+#define VIA686A_SYSCTL_TEMP3 1202
+#define VIA686A_SYSCTL_FAN_DIV 2000
+#define VIA686A_SYSCTL_ALARMS 2001
+
+#define VIA686A_ALARM_IN0 0x01
+#define VIA686A_ALARM_IN1 0x02
+#define VIA686A_ALARM_IN2 0x04
+#define VIA686A_ALARM_IN3 0x08
+#define VIA686A_ALARM_TEMP 0x10
+#define VIA686A_ALARM_FAN1 0x40
+#define VIA686A_ALARM_FAN2 0x80
+#define VIA686A_ALARM_IN4 0x100
+#define VIA686A_ALARM_TEMP2 0x800
+#define VIA686A_ALARM_CHAS 0x1000
+#define VIA686A_ALARM_TEMP3 0x8000
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected VIA686A. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized 
+   when a new copy is allocated. */
+static ctl_table via686a_dir_table_template[] = {
+	{VIA686A_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &via686a_in},
+	{VIA686A_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &via686a_in},
+	{VIA686A_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &via686a_in},
+	{VIA686A_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &via686a_in},
+	{VIA686A_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &via686a_in},
+	{VIA686A_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &via686a_fan},
+	{VIA686A_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &via686a_fan},
+	{VIA686A_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &via686a_temp},
+	{VIA686A_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_temp},
+	{VIA686A_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_temp},
+	{VIA686A_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_fan_div},
+	{VIA686A_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_alarms},
+	{0}
+};
+
+static inline int via686a_read_value(struct i2c_client *client, u8 reg)
+{
+	return (inb_p(client->addr + reg));
+}
+
+static inline void via686a_write_value(struct i2c_client *client, u8 reg,
+				       u8 value)
+{
+	outb_p(value, client->addr + reg);
+}
+
+/* This is called when the module is loaded */
+static int via686a_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, via686a_detect);
+}
+
+int via686a_detect(struct i2c_adapter *adapter, int address,
+		   unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct via686a_data *data;
+	int err = 0;
+	const char *type_name = "via686a";
+	u16 val;
+
+	/* Make sure we are probing the ISA bus!!  */
+	if (!i2c_is_isa_adapter(adapter)) {
+		printk
+		("via686a.o: via686a_detect called for an I2C bus adapter?!?\n");
+		return 0;
+	}
+
+	/* 8231 requires multiple of 256, we enforce that on 686 as well */
+	if(force_addr)
+		address = force_addr & 0xFF00;
+	if (check_region(address, VIA686A_EXTENT)) {
+		printk("via686a.o: region 0x%x already in use!\n",
+		       address);
+		return -ENODEV;
+	}
+
+	if(force_addr) {
+		printk("via686a.o: forcing ISA address 0x%04X\n", address);
+		if (PCIBIOS_SUCCESSFUL !=
+		    pci_write_config_word(s_bridge, VIA686A_BASE_REG, address))
+			return -ENODEV;
+	}
+	if (PCIBIOS_SUCCESSFUL !=
+	    pci_read_config_word(s_bridge, VIA686A_ENABLE_REG, &val))
+		return -ENODEV;
+	if (!(val & 0x0001)) {
+		printk("via686a.o: enabling sensors\n");
+		if (PCIBIOS_SUCCESSFUL !=
+		    pci_write_config_word(s_bridge, VIA686A_ENABLE_REG,
+		                      val | 0x0001))
+			return -ENODEV;
+	}
+
+	if (!(data = kmalloc(sizeof(struct via686a_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	init_MUTEX(&data->lock);
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &via686a_driver;
+	new_client->flags = 0;
+
+	/* Reserve the ISA region */
+	request_region(address, VIA686A_EXTENT, "via686a-sensors");
+
+	/* Fill in the remaining client fields and put into the global list */
+	strcpy(new_client->name, "Via 686A Integrated Sensors");
+
+	new_client->id = via686a_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry((struct i2c_client *) new_client,
+					type_name,
+					via686a_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the VIA686A chip */
+	via686a_init_client(new_client);
+	return 0;
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+	release_region(address, VIA686A_EXTENT);
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int via686a_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct via686a_data *) 
+				  (client->data))->sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		("via686a.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	release_region(client->addr, VIA686A_EXTENT);
+	kfree(client->data);
+
+	return 0;
+}
+
+/* Called when we have found a new VIA686A. */
+static void via686a_init_client(struct i2c_client *client)
+{
+	u8 reg;
+
+	/* Start monitoring */
+	reg = via686a_read_value(client, VIA686A_REG_CONFIG);
+	via686a_write_value(client, VIA686A_REG_CONFIG, (reg|0x01)&0x7F);
+
+	/* Configure temp interrupt mode for continuous-interrupt operation */
+	via686a_write_value(client, VIA686A_REG_TEMP_MODE, 
+			    via686a_read_value(client, VIA686A_REG_TEMP_MODE) &
+			    !(VIA686A_TEMP_MODE_MASK | VIA686A_TEMP_MODE_CONTINUOUS));
+}
+
+static void via686a_update_client(struct i2c_client *client)
+{
+	struct via686a_data *data = client->data;
+	int i;
+
+	down(&data->update_lock);
+
+       if (time_after(jiffies - data->last_updated, HZ + HZ / 2) ||
+           time_before(jiffies, data->last_updated) || !data->valid) {
+
+		for (i = 0; i <= 4; i++) {
+			data->in[i] =
+			    via686a_read_value(client, VIA686A_REG_IN(i));
+			data->in_min[i] = via686a_read_value(client,
+							     VIA686A_REG_IN_MIN
+							     (i));
+			data->in_max[i] =
+			    via686a_read_value(client, VIA686A_REG_IN_MAX(i));
+		}
+		for (i = 1; i <= 2; i++) {
+			data->fan[i - 1] =
+			    via686a_read_value(client, VIA686A_REG_FAN(i));
+			data->fan_min[i - 1] = via686a_read_value(client,
+						     VIA686A_REG_FAN_MIN(i));
+		}
+		for (i = 1; i <= 3; i++) {
+			data->temp[i - 1] = via686a_read_value(client,
+						 VIA686A_REG_TEMP(i)) << 2;
+			data->temp_over[i - 1] =
+			    via686a_read_value(client,
+					       VIA686A_REG_TEMP_OVER(i));
+			data->temp_hyst[i - 1] =
+			    via686a_read_value(client,
+					       VIA686A_REG_TEMP_HYST(i));
+		}
+		/* add in lower 2 bits 
+		   temp1 uses bits 7-6 of VIA686A_REG_TEMP_LOW1
+		   temp2 uses bits 5-4 of VIA686A_REG_TEMP_LOW23
+		   temp3 uses bits 7-6 of VIA686A_REG_TEMP_LOW23
+		 */
+		data->temp[0] |= (via686a_read_value(client,
+						     VIA686A_REG_TEMP_LOW1)
+				  & 0xc0) >> 6;
+		data->temp[1] |=
+		    (via686a_read_value(client, VIA686A_REG_TEMP_LOW23) &
+		     0x30) >> 4;
+		data->temp[2] |=
+		    (via686a_read_value(client, VIA686A_REG_TEMP_LOW23) &
+		     0xc0) >> 6;
+
+		i = via686a_read_value(client, VIA686A_REG_FANDIV);
+		data->fan_div[0] = (i >> 4) & 0x03;
+		data->fan_div[1] = i >> 6;
+		data->alarms =
+		    via686a_read_value(client,
+				       VIA686A_REG_ALARM1) |
+		    (via686a_read_value(client, VIA686A_REG_ALARM2) << 8);
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+/* The next few functions are the call-back functions of the /proc/sys and
+   sysctl files. Which function is used is defined in the ctl_table in
+   the extra1 field.
+   Each function must return the magnitude (power of 10 to divide the date
+   with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must
+   put a maximum of *nrels elements in results reflecting the data of this
+   file, and set *nrels to the number it actually put in it, if operation==
+   SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from
+   results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE.
+   Note that on SENSORS_PROC_REAL_READ, I do not check whether results is
+   large enough (by checking the incoming value of *nrels). This is not very
+   good practice, but as long as you put less than about 5 values in results,
+   you can assume it is large enough. */
+static void via686a_in(struct i2c_client *client, int operation, int ctl_name,
+               int *nrels_mag, long *results)
+{
+	struct via686a_data *data = client->data;
+	int nr = ctl_name - VIA686A_SYSCTL_IN0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		via686a_update_client(client);
+		results[0] = IN_FROM_REG(data->in_min[nr], nr);
+		results[1] = IN_FROM_REG(data->in_max[nr], nr);
+		results[2] = IN_FROM_REG(data->in[nr], nr);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->in_min[nr] = IN_TO_REG(results[0], nr);
+			via686a_write_value(client, VIA686A_REG_IN_MIN(nr),
+					    data->in_min[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->in_max[nr] = IN_TO_REG(results[1], nr);
+			via686a_write_value(client, VIA686A_REG_IN_MAX(nr),
+					    data->in_max[nr]);
+		}
+	}
+}
+
+void via686a_fan(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct via686a_data *data = client->data;
+	int nr = ctl_name - VIA686A_SYSCTL_FAN1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		via686a_update_client(client);
+		results[0] = FAN_FROM_REG(data->fan_min[nr - 1],
+					  DIV_FROM_REG(data->fan_div
+						       [nr - 1]));
+		results[1] = FAN_FROM_REG(data->fan[nr - 1],
+				 DIV_FROM_REG(data->fan_div[nr - 1]));
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr - 1] = FAN_TO_REG(results[0], 
+							   DIV_FROM_REG(data->
+							      fan_div[nr -1]));
+			via686a_write_value(client,
+					    VIA686A_REG_FAN_MIN(nr),
+					    data->fan_min[nr - 1]);
+		}
+	}
+}
+
+void via686a_temp(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct via686a_data *data = client->data;
+	int nr = ctl_name - VIA686A_SYSCTL_TEMP;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		via686a_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_over[nr]);
+		results[1] = TEMP_FROM_REG(data->temp_hyst[nr]);
+		results[2] = TEMP_FROM_REG10(data->temp[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_over[nr] = TEMP_TO_REG(results[0]);
+			via686a_write_value(client,
+					    VIA686A_REG_TEMP_OVER(nr + 1),
+					    data->temp_over[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_hyst[nr] = TEMP_TO_REG(results[1]);
+			via686a_write_value(client,
+					    VIA686A_REG_TEMP_HYST(nr + 1),
+					    data->temp_hyst[nr]);
+		}
+	}
+}
+
+void via686a_alarms(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results)
+{
+	struct via686a_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		via686a_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->alarms);
+		*nrels_mag = 1;
+	}
+}
+
+void via686a_fan_div(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	struct via686a_data *data = client->data;
+	int old;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		via686a_update_client(client);
+		results[0] = DIV_FROM_REG(data->fan_div[0]);
+		results[1] = DIV_FROM_REG(data->fan_div[1]);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		old = via686a_read_value(client, VIA686A_REG_FANDIV);
+		if (*nrels_mag >= 2) {
+			data->fan_div[1] = DIV_TO_REG(results[1]);
+			old = (old & 0x3f) | (data->fan_div[1] << 6);
+		}
+		if (*nrels_mag >= 1) {
+			data->fan_div[0] = DIV_TO_REG(results[0]);
+			old = (old & 0xcf) | (data->fan_div[0] << 4);
+			via686a_write_value(client, VIA686A_REG_FANDIV,
+					    old);
+		}
+	}
+}
+
+
+static struct pci_device_id via686a_pci_ids[] __devinitdata = {
+       {PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+       { 0, }
+};
+
+static int __devinit via686a_pci_probe(struct pci_dev *dev,
+                                      const struct pci_device_id *id)
+{
+       u16 val;
+       int addr = 0;
+
+       if (PCIBIOS_SUCCESSFUL !=
+           pci_read_config_word(dev, VIA686A_BASE_REG, &val))
+               return -ENODEV;
+
+       addr = val & ~(VIA686A_EXTENT - 1);
+       if (addr == 0 && force_addr == 0) {
+               printk("via686a.o: base address not set - upgrade BIOS or use force_addr=0xaddr\n");
+               return -ENODEV;
+       }
+       if (force_addr)
+               addr = force_addr;      /* so detect will get called */
+
+       if (!addr) {
+               printk("via686a.o: No Via 686A sensors found.\n");
+               return -ENODEV;
+       }
+       normal_isa[0] = addr;
+       s_bridge = dev;
+       return i2c_add_driver(&via686a_driver);
+}
+
+static void __devexit via686a_pci_remove(struct pci_dev *dev)
+{
+       i2c_del_driver(&via686a_driver);
+}
+
+static struct pci_driver via686a_pci_driver = {
+       .name		= "via686a",
+       .id_table	= via686a_pci_ids,
+       .probe		= via686a_pci_probe,
+       .remove		= __devexit_p(via686a_pci_remove),
+};
+
+static int __init sm_via686a_init(void)
+{
+	printk("via686a.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return pci_module_init(&via686a_pci_driver);
+}
+
+static void __exit sm_via686a_exit(void)
+{
+       pci_unregister_driver(&via686a_pci_driver);
+}
+
+MODULE_AUTHOR("Kyösti Mälkki <kmalkki@cc.hut.fi>, "
+              "Mark Studebaker <mdsxyz123@yahoo.com> "
+             "and Bob Dougherty <bobd@stanford.edu>");
+MODULE_DESCRIPTION("VIA 686A Sensor device");
+MODULE_LICENSE("GPL");
+
+module_init(sm_via686a_init);
+module_exit(sm_via686a_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/vt1211.c linux-2.4.27-leo/drivers/sensors/vt1211.c
--- linux-2.4.27/drivers/sensors/vt1211.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/vt1211.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,823 @@
+/*
+    vt1211.c - Part of lm_sensors, Linux kernel modules
+                for hardware monitoring
+                
+    Copyright (c) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* Supports VIA VT1211 Super I/O sensors via ISA (LPC) accesses only. */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+#include <linux/sensors_vid.h>
+
+static int force_addr = 0;
+MODULE_PARM(force_addr, "i");
+MODULE_PARM_DESC(force_addr,
+		 "Initialize the base address of the sensors");
+
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { 0x0000, SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+SENSORS_INSMOD_1(vt1211);
+
+/* modified from kernel/include/traps.c */
+#define	REG	0x2e	/* The register to read/write */
+#define	DEV	0x07	/* Register: Logical device select */
+#define	VAL	0x2f	/* The value to read/write */
+#define PME	0x0b	/* The device with the hardware monitor */
+#define	DEVID	0x20	/* Register: Device ID */
+
+static inline void
+superio_outb(int reg, int val)
+{
+	outb(reg, REG);
+	outb(val, VAL);
+}
+
+static inline int
+superio_inb(int reg)
+{
+	outb(reg, REG);
+	return inb(VAL);
+}
+
+static inline void
+superio_select(void)
+{
+	outb(DEV, REG);
+	outb(PME, VAL);
+}
+
+static inline void
+superio_enter(void)
+{
+	outb(0x87, REG);
+	outb(0x87, REG);
+}
+
+static inline void
+superio_exit(void)
+{
+	outb(0xAA, REG);
+}
+
+#define VT1211_DEVID 0x3c
+#define VT1211_ACT_REG 0x30
+#define VT1211_BASE_REG 0x60
+
+#define VT1211_EXTENT 0x80
+
+/* pwm numbered 1-2 */
+#define VT1211_REG_PWM(nr) (0x5f + (nr))
+#define VT1211_REG_PWM_CTL 0x51
+
+/* The VT1211 registers */
+/* We define the sensors as follows. Somewhat convoluted to minimize
+   changes from via686a.
+	Sensor		Voltage Mode	Temp Mode
+	--------	------------	---------
+	Reading 1			temp3
+	Reading 3			temp1	not in vt1211
+	UCH1/Reading2	in0		temp2
+	UCH2		in1		temp4
+	UCH3		in2		temp5
+	UCH4		in3		temp6
+	UCH5		in4		temp7
+	3.3V		in5
+	-12V		in6			not in vt1211
+*/
+
+/* ins numbered 0-6 */
+#define VT1211_REG_IN_MAX(nr) ((nr)==0 ? 0x3d : 0x29 + ((nr) * 2))
+#define VT1211_REG_IN_MIN(nr) ((nr)==0 ? 0x3e : 0x2a + ((nr) * 2))
+#define VT1211_REG_IN(nr)     (0x21 + (nr))
+
+/* fans numbered 1-2 */
+#define VT1211_REG_FAN_MIN(nr) (0x3a + (nr))
+#define VT1211_REG_FAN(nr)     (0x28 + (nr))
+
+static const u8 regtemp[] = { 0x20, 0x21, 0x1f, 0x22, 0x23, 0x24, 0x25 };
+static const u8 regover[] = { 0x39, 0x3d, 0x1d, 0x2b, 0x2d, 0x2f, 0x31 };
+static const u8 reghyst[] = { 0x3a, 0x3e, 0x1e, 0x2c, 0x2e, 0x30, 0x32 };
+
+/* temps numbered 1-7 */
+#define VT1211_REG_TEMP(nr)		(regtemp[(nr) - 1])
+#define VT1211_REG_TEMP_OVER(nr)	(regover[(nr) - 1])
+#define VT1211_REG_TEMP_HYST(nr)	(reghyst[(nr) - 1])
+#define VT1211_REG_TEMP_LOW3	0x4b	/* bits 7-6 */
+#define VT1211_REG_TEMP_LOW2	0x49	/* bits 5-4 */
+#define VT1211_REG_TEMP_LOW47	0x4d
+
+#define VT1211_REG_CONFIG 0x40
+#define VT1211_REG_ALARM1 0x41
+#define VT1211_REG_ALARM2 0x42
+#define VT1211_REG_VID    0x45
+#define VT1211_REG_FANDIV 0x47
+#define VT1211_REG_UCH_CONFIG 0x4a
+#define VT1211_REG_TEMP1_CONFIG 0x4b
+#define VT1211_REG_TEMP2_CONFIG 0x4c
+
+/* temps 1-7; voltages 0-6 */
+#define ISTEMP(i, ch_config) ((i) == 1 ? 1 : \
+			      (i) == 3 ? 1 : \
+			      (i) == 2 ? ((ch_config) >> 1) & 0x01 : \
+			                 ((ch_config) >> ((i)-1)) & 0x01)
+#define ISVOLT(i, ch_config) ((i) > 4 ? 1 : !(((ch_config) >> ((i)+2)) & 0x01))
+
+#define DIV_FROM_REG(val) (1 << (val))
+#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
+#define PWM_FROM_REG(val) (val)
+#define PWM_TO_REG(val) SENSORS_LIMIT((val), 0, 255)
+
+#define TEMP_FROM_REG(val) ((val)*10)
+#define TEMP_FROM_REG10(val) (((val)*10)/4)
+#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/10):\
+                                                 ((val)+5)/10),0,255))
+#define IN_FROM_REG(val) /*(((val)*10+5)/10)*/ (val)
+#define IN_TO_REG(val)  (SENSORS_LIMIT((((val) * 10 + 5)/10),0,255))
+
+
+/********* FAN RPM CONVERSIONS ********/
+/* But this chip saturates back at 0, not at 255 like all the other chips.
+   So, 0 means 0 RPM */
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+	if (rpm == 0)
+		return 0;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT((1310720 + rpm * div / 2) / (rpm * div), 1, 255);
+}
+
+#define MIN_TO_REG(a,b) FAN_TO_REG(a,b)
+#define FAN_FROM_REG(val,div) ((val)==0?0:(val)==255?0:1310720/((val)*(div)))
+
+struct vt1211_data {
+	struct i2c_client client;
+	struct semaphore lock;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 in[7];		/* Register value */
+	u8 in_max[7];		/* Register value */
+	u8 in_min[7];		/* Register value */
+	u16 temp[7];		/* Register value 10 bit */
+	u8 temp_over[7];	/* Register value */
+	u8 temp_hyst[7];	/* Register value */
+	u8 fan[2];		/* Register value */
+	u8 fan_min[2];		/* Register value */
+	u8 fan_div[2];		/* Register encoding, shifted right */
+	u16 alarms;		/* Register encoding */
+	u8 pwm[2];		/* Register value */
+	u8 pwm_ctl;		/* Register value */
+	u8 vid;			/* Register encoding */
+	u8 vrm;
+	u8 uch_config;
+};
+
+static int vt1211_attach_adapter(struct i2c_adapter *adapter);
+static int vt1211_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int vt1211_detach_client(struct i2c_client *client);
+
+static inline int vt_rdval(struct i2c_client *client, u8 register);
+static inline void vt1211_write_value(struct i2c_client *client, u8 register,
+			       u8 value);
+static void vt1211_update_client(struct i2c_client *client);
+static void vt1211_init_client(struct i2c_client *client);
+static int vt1211_find(int *address);
+
+
+static void vt1211_fan(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void vt1211_alarms(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+static void vt1211_fan_div(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void vt1211_in(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void vt1211_pwm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void vt1211_vid(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void vt1211_vrm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void vt1211_uch(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void vt1211_temp(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+
+static int vt1211_id = 0;
+
+static struct i2c_driver vt1211_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "VT1211 sensors driver",
+	.id		= I2C_DRIVERID_VT1211,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= vt1211_attach_adapter,
+	.detach_client	= vt1211_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+#define VT1211_SYSCTL_IN0 1000
+#define VT1211_SYSCTL_IN1 1001
+#define VT1211_SYSCTL_IN2 1002
+#define VT1211_SYSCTL_IN3 1003
+#define VT1211_SYSCTL_IN4 1004
+#define VT1211_SYSCTL_IN5 1005
+#define VT1211_SYSCTL_IN6 1006
+#define VT1211_SYSCTL_FAN1 1101
+#define VT1211_SYSCTL_FAN2 1102
+#define VT1211_SYSCTL_TEMP 1200
+#define VT1211_SYSCTL_TEMP2 1201
+#define VT1211_SYSCTL_TEMP3 1202
+#define VT1211_SYSCTL_TEMP4 1203
+#define VT1211_SYSCTL_TEMP5 1204
+#define VT1211_SYSCTL_TEMP6 1205
+#define VT1211_SYSCTL_TEMP7 1206
+#define VT1211_SYSCTL_VID	1300
+#define VT1211_SYSCTL_PWM1	1401
+#define VT1211_SYSCTL_PWM2	1402
+#define VT1211_SYSCTL_VRM	1600
+#define VT1211_SYSCTL_UCH	1700
+#define VT1211_SYSCTL_FAN_DIV 2000
+#define VT1211_SYSCTL_ALARMS 2001
+
+#define VT1211_ALARM_IN1 0x01
+#define VT1211_ALARM_IN2 0x02
+#define VT1211_ALARM_IN5 0x04
+#define VT1211_ALARM_IN3 0x08
+#define VT1211_ALARM_TEMP 0x10
+#define VT1211_ALARM_FAN1 0x40
+#define VT1211_ALARM_FAN2 0x80
+#define VT1211_ALARM_IN4 0x100
+#define VT1211_ALARM_IN6 0x200
+#define VT1211_ALARM_TEMP2 0x800
+#define VT1211_ALARM_CHAS 0x1000
+#define VT1211_ALARM_TEMP3 0x8000
+/* duplicates */
+#define VT1211_ALARM_IN0 VT1211_ALARM_TEMP
+#define VT1211_ALARM_TEMP4 VT1211_ALARM_IN1
+#define VT1211_ALARM_TEMP5 VT1211_ALARM_IN2
+#define VT1211_ALARM_TEMP6 VT1211_ALARM_IN3
+#define VT1211_ALARM_TEMP7 VT1211_ALARM_IN4
+
+/* -- SENSORS SYSCTL END -- */
+
+static ctl_table vt1211_dir_table_template[] = {
+	{VT1211_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_in},
+	{VT1211_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_in},
+	{VT1211_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_in},
+	{VT1211_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_in},
+	{VT1211_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_in},
+	{VT1211_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_in},
+/*
+    datasheet says these are reserved
+	{VT1211_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_in},
+	{VT1211_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_temp},
+*/
+	{VT1211_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &vt1211_temp},
+	{VT1211_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &vt1211_temp},
+	{VT1211_SYSCTL_TEMP4, "temp4", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &vt1211_temp},
+	{VT1211_SYSCTL_TEMP5, "temp5", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &vt1211_temp},
+	{VT1211_SYSCTL_TEMP6, "temp6", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &vt1211_temp},
+	{VT1211_SYSCTL_TEMP7, "temp7", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &vt1211_temp},
+	{VT1211_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_fan},
+	{VT1211_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_fan},
+	{VT1211_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_fan_div},
+	{VT1211_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_alarms},
+	{VT1211_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_pwm},
+	{VT1211_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_pwm},
+	{VT1211_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_vid},
+	{VT1211_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_vrm},
+	{VT1211_SYSCTL_UCH, "uch_config", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt1211_uch},
+	{0}
+};
+
+static int vt1211_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, vt1211_detect);
+}
+
+static int vt1211_find(int *address)
+{
+	u16 val;
+
+	superio_enter();
+	val= superio_inb(DEVID);
+	if(VT1211_DEVID != val) {
+		superio_exit();
+		return -ENODEV;
+	}
+
+	superio_select();
+	val = (superio_inb(VT1211_BASE_REG) << 8) |
+	       superio_inb(VT1211_BASE_REG + 1);
+	*address = val & ~(VT1211_EXTENT - 1);
+	if (*address == 0 && force_addr == 0) {
+		printk("vt1211.o: base address not set - use force_addr=0xaddr\n");
+		superio_exit();
+		return -ENODEV;
+	}
+	if (force_addr)
+		*address = force_addr;	/* so detect will get called */
+
+	superio_exit();
+	return 0;
+}
+
+int vt1211_detect(struct i2c_adapter *adapter, int address,
+		   unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct vt1211_data *data;
+	int err = 0;
+	u8 val;
+	const char *type_name = "vt1211";
+	const char *client_name = "VT1211 chip";
+
+	if (!i2c_is_isa_adapter(adapter)) {
+		return 0;
+	}
+
+	if(force_addr)
+		address = force_addr & ~(VT1211_EXTENT - 1);
+	if (check_region(address, VT1211_EXTENT)) {
+		printk("vt1211.o: region 0x%x already in use!\n", address);
+		return -ENODEV;
+	}
+	if(force_addr) {
+		printk("vt1211.o: forcing ISA address 0x%04X\n", address);
+		superio_enter();
+		superio_select();
+		superio_outb(VT1211_BASE_REG, address >> 8);
+		superio_outb(VT1211_BASE_REG+1, address & 0xff);
+		superio_exit();
+	}
+
+	superio_enter();
+	superio_select();
+	if((val = 0x01 & superio_inb(VT1211_ACT_REG)) == 0)
+		superio_outb(VT1211_ACT_REG, 1);
+	superio_exit();
+
+	if (!(data = kmalloc(sizeof(struct vt1211_data), GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	init_MUTEX(&data->lock);
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &vt1211_driver;
+	new_client->flags = 0;
+
+	request_region(address, VT1211_EXTENT, "vt1211-sensors");
+	strcpy(new_client->name, client_name);
+
+	new_client->id = vt1211_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	if ((i = i2c_register_entry((struct i2c_client *) new_client,
+					type_name,
+					vt1211_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	vt1211_init_client(new_client);
+	return 0;
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+	release_region(address, VT1211_EXTENT);
+	kfree(data);
+	return err;
+}
+
+static int vt1211_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct vt1211_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("vt1211.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	release_region(client->addr, VT1211_EXTENT);
+	kfree(client->data);
+
+	return 0;
+}
+
+static inline int vt_rdval(struct i2c_client *client, u8 reg)
+{
+	return (inb_p(client->addr + reg));
+}
+
+static inline void vt1211_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+	outb_p(value, client->addr + reg);
+}
+
+static void vt1211_init_client(struct i2c_client *client)
+{
+	struct vt1211_data *data = client->data;
+
+	data->vrm = DEFAULT_VRM;
+	/* set "default" interrupt mode for alarms, which isn't the default */
+	vt1211_write_value(client, VT1211_REG_TEMP1_CONFIG, 0);
+	vt1211_write_value(client, VT1211_REG_TEMP2_CONFIG, 0);
+}
+
+static void vt1211_update_client(struct i2c_client *client)
+{
+	struct vt1211_data *data = client->data;
+	int i, j;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+		data->uch_config = vt_rdval(client, VT1211_REG_UCH_CONFIG);
+		for (i = 0; i <= 5; i++) {
+			if(ISVOLT(i, data->uch_config)) {
+				data->in[i] = vt_rdval(client, VT1211_REG_IN(i));
+				data->in_min[i] = vt_rdval(client,
+				                        VT1211_REG_IN_MIN(i));
+				data->in_max[i] = vt_rdval(client,
+				                        VT1211_REG_IN_MAX(i));
+			} else {
+				data->in[i] = 0;
+				data->in_min[i] = 0;
+				data->in_max[i] = 0;
+			}
+		}
+		for (i = 1; i <= 2; i++) {
+			data->fan[i - 1] = vt_rdval(client, VT1211_REG_FAN(i));
+			data->fan_min[i - 1] = vt_rdval(client,
+						     VT1211_REG_FAN_MIN(i));
+		}
+		for (i = 2; i <= 7; i++) {
+			if(ISTEMP(i, data->uch_config)) {
+				data->temp[i - 1] = vt_rdval(client,
+					             VT1211_REG_TEMP(i)) << 2;
+				switch(i) {
+					case 1:
+						/* ? */
+						j = 0;
+						break;
+					case 2:
+						j = (vt_rdval(client,
+						  VT1211_REG_TEMP_LOW2) &
+						                    0x30) >> 4;
+						break;
+					case 3:
+						j = (vt_rdval(client,
+						  VT1211_REG_TEMP_LOW3) &
+						                    0xc0) >> 6;
+						break;
+					case 4:
+					case 5:
+					case 6:
+					case 7:
+					default:
+						j = (vt_rdval(client,
+						  VT1211_REG_TEMP_LOW47) >>
+						            ((i-4)*2)) & 0x03;	
+						break;
+	
+				}
+				data->temp[i - 1] |= j;
+				data->temp_over[i - 1] = vt_rdval(client,
+					              VT1211_REG_TEMP_OVER(i));
+				data->temp_hyst[i - 1] = vt_rdval(client,
+					              VT1211_REG_TEMP_HYST(i));
+			} else {
+				data->temp[i - 1] = 0;
+				data->temp_over[i - 1] = 0;
+				data->temp_hyst[i - 1] = 0;
+			}
+		}
+
+		for (i = 1; i <= 2; i++) {
+			data->fan[i - 1] = vt_rdval(client, VT1211_REG_FAN(i));
+			data->fan_min[i - 1] = vt_rdval(client,
+			                                VT1211_REG_FAN_MIN(i));
+			data->pwm[i - 1] = vt_rdval(client, VT1211_REG_PWM(i));
+		}
+
+		data->pwm_ctl = vt_rdval(client, VT1211_REG_PWM_CTL);
+		i = vt_rdval(client, VT1211_REG_FANDIV);
+		data->fan_div[0] = (i >> 4) & 0x03;
+		data->fan_div[1] = i >> 6;
+		data->alarms = vt_rdval(client, VT1211_REG_ALARM1) |
+		                    (vt_rdval(client, VT1211_REG_ALARM2) << 8);
+		data->vid= vt_rdval(client, VT1211_REG_VID) & 0x1f;
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+void vt1211_in(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct vt1211_data *data = client->data;
+	int nr = ctl_name - VT1211_SYSCTL_IN0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		vt1211_update_client(client);
+		results[0] = IN_FROM_REG(data->in_min[nr]);
+		results[1] = IN_FROM_REG(data->in_max[nr]);
+		results[2] = IN_FROM_REG(data->in[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->in_min[nr] = IN_TO_REG(results[0]);
+			vt1211_write_value(client, VT1211_REG_IN_MIN(nr),
+					    data->in_min[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->in_max[nr] = IN_TO_REG(results[1]);
+			vt1211_write_value(client, VT1211_REG_IN_MAX(nr),
+					    data->in_max[nr]);
+		}
+	}
+}
+
+void vt1211_fan(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct vt1211_data *data = client->data;
+	int nr = ctl_name - VT1211_SYSCTL_FAN1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		vt1211_update_client(client);
+		results[0] = FAN_FROM_REG(data->fan_min[nr - 1],
+					  DIV_FROM_REG(data->fan_div
+						       [nr - 1]));
+		results[1] = FAN_FROM_REG(data->fan[nr - 1],
+				 DIV_FROM_REG(data->fan_div[nr - 1]));
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr - 1] = MIN_TO_REG(results[0],
+							   DIV_FROM_REG
+							   (data->
+							    fan_div[nr-1]));
+			vt1211_write_value(client, VT1211_REG_FAN_MIN(nr),
+					    data->fan_min[nr - 1]);
+		}
+	}
+}
+
+
+void vt1211_temp(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct vt1211_data *data = client->data;
+	int nr = ctl_name - VT1211_SYSCTL_TEMP;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		vt1211_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_over[nr]);
+		results[1] = TEMP_FROM_REG(data->temp_hyst[nr]);
+		results[2] = TEMP_FROM_REG10(data->temp[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_over[nr] = TEMP_TO_REG(results[0]);
+			vt1211_write_value(client,
+					    VT1211_REG_TEMP_OVER(nr + 1),
+					    data->temp_over[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_hyst[nr] = TEMP_TO_REG(results[1]);
+			vt1211_write_value(client,
+					    VT1211_REG_TEMP_HYST(nr + 1),
+					    data->temp_hyst[nr]);
+		}
+	}
+}
+
+void vt1211_alarms(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results)
+{
+	struct vt1211_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		vt1211_update_client(client);
+		results[0] = data->alarms;
+		*nrels_mag = 1;
+	}
+}
+
+void vt1211_fan_div(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	struct vt1211_data *data = client->data;
+	int old;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		vt1211_update_client(client);
+		results[0] = DIV_FROM_REG(data->fan_div[0]);
+		results[1] = DIV_FROM_REG(data->fan_div[1]);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		old = vt_rdval(client, VT1211_REG_FANDIV);
+		if (*nrels_mag >= 2) {
+			data->fan_div[1] = DIV_TO_REG(results[1]);
+			old = (old & 0x3f) | (data->fan_div[1] << 6);
+		}
+		if (*nrels_mag >= 1) {
+			data->fan_div[0] = DIV_TO_REG(results[0]);
+			old = (old & 0xcf) | (data->fan_div[0] << 4);
+			vt1211_write_value(client, VT1211_REG_FANDIV, old);
+		}
+	}
+}
+
+void vt1211_pwm(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct vt1211_data *data = client->data;
+	int nr = 1 + ctl_name - VT1211_SYSCTL_PWM1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		vt1211_update_client(client);
+		results[0] = PWM_FROM_REG(data->pwm[nr - 1]);
+		results[1] = (data->pwm_ctl >> (3 + (4 * (nr - 1)))) & 1;
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->pwm[nr - 1] = PWM_TO_REG(results[0]);
+			if (*nrels_mag >= 2) {
+				if(results[1]) {
+					data->pwm_ctl |=
+					          (0x08 << (4 * (nr - 1)));
+					vt1211_write_value(client,
+					                   VT1211_REG_PWM_CTL, 
+				                           data->pwm_ctl);
+				} else {
+					data->pwm_ctl &=
+					        ~ (0x08 << (4 * (nr - 1)));
+					vt1211_write_value(client,
+					                   VT1211_REG_PWM_CTL, 
+				                           data->pwm_ctl);
+				}
+			}
+			vt1211_write_value(client, VT1211_REG_PWM(nr),
+					    data->pwm[nr - 1]);
+		}
+	}
+}
+
+void vt1211_vid(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct vt1211_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 3;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		vt1211_update_client(client);
+		results[0] = vid_from_reg(data->vid, data->vrm);
+		*nrels_mag = 1;
+	}
+}
+
+void vt1211_vrm(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct vt1211_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = data->vrm;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1)
+			data->vrm = results[0];
+	}
+}
+
+void vt1211_uch(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct vt1211_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = data->uch_config & 0x7c;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->uch_config = (data->uch_config & 0x83)|(results[0] & 0x7c);
+			vt1211_write_value(client, VT1211_REG_UCH_CONFIG,
+			                   data->uch_config);
+		}
+	}
+}
+
+static int __init sm_vt1211_init(void)
+{
+	int addr;
+
+	printk("vt1211.o version %s (%s)\n", LM_VERSION, LM_DATE);
+
+	if (vt1211_find(&addr)) {
+		printk("vt1211.o: VT1211 not detected, module not inserted.\n");
+		return -ENODEV;
+	}
+	normal_isa[0] = addr;
+
+	return i2c_add_driver(&vt1211_driver);
+}
+
+static void __exit sm_vt1211_exit(void)
+{
+	i2c_del_driver(&vt1211_driver);
+}
+
+
+
+MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("VT1211 sensors");
+MODULE_LICENSE("GPL");
+
+module_init(sm_vt1211_init);
+module_exit(sm_vt1211_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/vt8231.c linux-2.4.27-leo/drivers/sensors/vt8231.c
--- linux-2.4.27/drivers/sensors/vt8231.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/vt8231.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,794 @@
+/*
+    vt8231.c - Part of lm_sensors, Linux kernel modules
+                for hardware monitoring
+                
+    Copyright (c) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* Supports VIA VT8231 South Bridge embedded sensors */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+#include <linux/sensors_vid.h>
+
+
+static int force_addr = 0;
+MODULE_PARM(force_addr, "i");
+MODULE_PARM_DESC(force_addr,
+		 "Initialize the base address of the sensors");
+
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { 0x0000, SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+SENSORS_INSMOD_1(vt8231);
+
+#define VIA686A_EXTENT 0x80
+#define VIA686A_BASE_REG 0x70
+#define VIA686A_ENABLE_REG 0x74
+
+/* pwm numbered 1-2 */
+#define VT8231_REG_PWM(nr) (0x5f + (nr))
+#define VT8231_REG_PWM_CTL 0x51
+
+/* The VT8231 registers */
+/* We define the sensors as follows. Somewhat convoluted to minimize
+   changes from via686a.
+	Sensor		Voltage Mode	Temp Mode
+	--------	------------	---------
+	Reading 1			temp3
+	Reading 3			temp1	not in vt8231
+	UCH1/Reading2	in0		temp2
+	UCH2		in1		temp4
+	UCH3		in2		temp5
+	UCH4		in3		temp6
+	UCH5		in4		temp7
+	3.3V		in5
+	-12V		in6			not in vt8231
+*/
+
+/* ins numbered 0-6 */
+#define VT8231_REG_IN_MAX(nr) ((nr)==0 ? 0x3d : 0x29 + ((nr) * 2))
+#define VT8231_REG_IN_MIN(nr) ((nr)==0 ? 0x3e : 0x2a + ((nr) * 2))
+#define VT8231_REG_IN(nr)     (0x21 + (nr))
+
+/* fans numbered 1-2 */
+#define VT8231_REG_FAN_MIN(nr) (0x3a + (nr))
+#define VT8231_REG_FAN(nr)     (0x28 + (nr))
+
+static const u8 regtemp[] = { 0x20, 0x21, 0x1f, 0x22, 0x23, 0x24, 0x25 };
+static const u8 regover[] = { 0x39, 0x3d, 0x1d, 0x2b, 0x2d, 0x2f, 0x31 };
+static const u8 reghyst[] = { 0x3a, 0x3e, 0x1e, 0x2c, 0x2e, 0x30, 0x32 };
+
+/* temps numbered 1-7 */
+#define VT8231_REG_TEMP(nr)		(regtemp[(nr) - 1])
+#define VT8231_REG_TEMP_OVER(nr)	(regover[(nr) - 1])
+#define VT8231_REG_TEMP_HYST(nr)	(reghyst[(nr) - 1])
+#define VT8231_REG_TEMP_LOW3	0x4b	/* bits 7-6 */
+#define VT8231_REG_TEMP_LOW2	0x49	/* bits 5-4 */
+#define VT8231_REG_TEMP_LOW47	0x4d
+
+#define VT8231_REG_CONFIG 0x40
+#define VT8231_REG_ALARM1 0x41
+#define VT8231_REG_ALARM2 0x42
+#define VT8231_REG_VID    0x45
+#define VT8231_REG_FANDIV 0x47
+#define VT8231_REG_UCH_CONFIG 0x4a
+#define VT8231_REG_TEMP1_CONFIG 0x4b
+#define VT8231_REG_TEMP2_CONFIG 0x4c
+
+/* temps 1-7; voltages 0-6 */
+#define ISTEMP(i, ch_config) ((i) == 1 ? 1 : \
+			      (i) == 3 ? 1 : \
+			      (i) == 2 ? ((ch_config) >> 1) & 0x01 : \
+			                 ((ch_config) >> ((i)-1)) & 0x01)
+#define ISVOLT(i, ch_config) ((i) > 4 ? 1 : !(((ch_config) >> ((i)+2)) & 0x01))
+
+#define DIV_FROM_REG(val) (1 << (val))
+#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
+#define PWM_FROM_REG(val) (val)
+#define PWM_TO_REG(val) SENSORS_LIMIT((val), 0, 255)
+
+#define TEMP_FROM_REG(val) ((val)*10)
+#define TEMP_FROM_REG10(val) (((val)*10)/4)
+#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/10):\
+                                                 ((val)+5)/10),0,255))
+#define IN_FROM_REG(val) /*(((val)*10+5)/10)*/ (val)
+#define IN_TO_REG(val)  (SENSORS_LIMIT((((val) * 10 + 5)/10),0,255))
+
+
+/********* FAN RPM CONVERSIONS ********/
+/* But this chip saturates back at 0, not at 255 like all the other chips.
+   So, 0 means 0 RPM */
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+	if (rpm == 0)
+		return 0;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT((1310720 + rpm * div / 2) / (rpm * div), 1, 255);
+}
+
+#define MIN_TO_REG(a,b) FAN_TO_REG(a,b)
+#define FAN_FROM_REG(val,div) ((val)==0?0:(val)==255?0:1310720/((val)*(div)))
+
+struct vt8231_data {
+	struct i2c_client client;
+	struct semaphore lock;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 in[7];		/* Register value */
+	u8 in_max[7];		/* Register value */
+	u8 in_min[7];		/* Register value */
+	u16 temp[7];		/* Register value 10 bit */
+	u8 temp_over[7];	/* Register value */
+	u8 temp_hyst[7];	/* Register value */
+	u8 fan[2];		/* Register value */
+	u8 fan_min[2];		/* Register value */
+	u8 fan_div[2];		/* Register encoding, shifted right */
+	u16 alarms;		/* Register encoding */
+	u8 pwm[2];		/* Register value */
+	u8 pwm_ctl;		/* Register value */
+	u8 vid;			/* Register encoding */
+	u8 vrm;
+	u8 uch_config;
+};
+
+static int vt8231_attach_adapter(struct i2c_adapter *adapter);
+static int vt8231_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int vt8231_detach_client(struct i2c_client *client);
+
+static inline int vt_rdval(struct i2c_client *client, u8 register);
+static inline void vt8231_write_value(struct i2c_client *client, u8 register,
+			       u8 value);
+static void vt8231_update_client(struct i2c_client *client);
+static void vt8231_init_client(struct i2c_client *client);
+static int vt8231_find(int *address);
+
+
+static void vt8231_fan(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void vt8231_alarms(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+static void vt8231_fan_div(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void vt8231_in(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void vt8231_pwm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void vt8231_vid(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void vt8231_vrm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void vt8231_uch(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void vt8231_temp(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+
+static int vt8231_id = 0;
+
+static struct i2c_driver vt8231_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "VT8231 sensors driver",
+	.id		= I2C_DRIVERID_VT8231,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= vt8231_attach_adapter,
+	.detach_client	= vt8231_detach_client,
+};
+
+/* -- SENSORS SYSCTL START -- */
+#define VT8231_SYSCTL_IN0 1000
+#define VT8231_SYSCTL_IN1 1001
+#define VT8231_SYSCTL_IN2 1002
+#define VT8231_SYSCTL_IN3 1003
+#define VT8231_SYSCTL_IN4 1004
+#define VT8231_SYSCTL_IN5 1005
+#define VT8231_SYSCTL_IN6 1006
+#define VT8231_SYSCTL_FAN1 1101
+#define VT8231_SYSCTL_FAN2 1102
+#define VT8231_SYSCTL_TEMP 1200
+#define VT8231_SYSCTL_TEMP2 1201
+#define VT8231_SYSCTL_TEMP3 1202
+#define VT8231_SYSCTL_TEMP4 1203
+#define VT8231_SYSCTL_TEMP5 1204
+#define VT8231_SYSCTL_TEMP6 1205
+#define VT8231_SYSCTL_TEMP7 1206
+#define VT8231_SYSCTL_VID	1300
+#define VT8231_SYSCTL_PWM1	1401
+#define VT8231_SYSCTL_PWM2	1402
+#define VT8231_SYSCTL_VRM	1600
+#define VT8231_SYSCTL_UCH	1700
+#define VT8231_SYSCTL_FAN_DIV 2000
+#define VT8231_SYSCTL_ALARMS 2001
+
+#define VT8231_ALARM_IN1 0x01
+#define VT8231_ALARM_IN2 0x02
+#define VT8231_ALARM_IN5 0x04
+#define VT8231_ALARM_IN3 0x08
+#define VT8231_ALARM_TEMP 0x10
+#define VT8231_ALARM_FAN1 0x40
+#define VT8231_ALARM_FAN2 0x80
+#define VT8231_ALARM_IN4 0x100
+#define VT8231_ALARM_IN6 0x200
+#define VT8231_ALARM_TEMP2 0x800
+#define VT8231_ALARM_CHAS 0x1000
+#define VT8231_ALARM_TEMP3 0x8000
+/* duplicates */
+#define VT8231_ALARM_IN0 VT8231_ALARM_TEMP
+#define VT8231_ALARM_TEMP4 VT8231_ALARM_IN1
+#define VT8231_ALARM_TEMP5 VT8231_ALARM_IN2
+#define VT8231_ALARM_TEMP6 VT8231_ALARM_IN3
+#define VT8231_ALARM_TEMP7 VT8231_ALARM_IN4
+
+/* -- SENSORS SYSCTL END -- */
+
+static ctl_table vt8231_dir_table_template[] = {
+	{VT8231_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt8231_in},
+	{VT8231_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt8231_in},
+	{VT8231_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt8231_in},
+	{VT8231_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt8231_in},
+	{VT8231_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt8231_in},
+	{VT8231_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt8231_in},
+/*
+    not in 8231
+	{VT8231_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt8231_in},
+	{VT8231_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt8231_temp},
+*/
+	{VT8231_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &vt8231_temp},
+	{VT8231_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &vt8231_temp},
+	{VT8231_SYSCTL_TEMP4, "temp4", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &vt8231_temp},
+	{VT8231_SYSCTL_TEMP5, "temp5", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &vt8231_temp},
+	{VT8231_SYSCTL_TEMP6, "temp6", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &vt8231_temp},
+	{VT8231_SYSCTL_TEMP7, "temp7", NULL, 0, 0644, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &vt8231_temp},
+	{VT8231_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt8231_fan},
+	{VT8231_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt8231_fan},
+	{VT8231_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt8231_fan_div},
+	{VT8231_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt8231_alarms},
+	{VT8231_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt8231_pwm},
+	{VT8231_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt8231_pwm},
+	{VT8231_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt8231_vid},
+	{VT8231_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt8231_vrm},
+	{VT8231_SYSCTL_UCH, "uch_config", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &vt8231_uch},
+	{0}
+};
+
+static struct pci_dev *s_bridge;
+
+static int vt8231_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, vt8231_detect);
+}
+
+/* Locate chip and get correct base address */
+static int vt8231_find(int *address)
+{
+	u16 val;
+
+	if (!pci_present())
+		return -ENODEV;
+
+	if (!(s_bridge = pci_find_device(PCI_VENDOR_ID_VIA,
+					 0x8235, NULL)))
+		return -ENODEV;
+
+	if (PCIBIOS_SUCCESSFUL !=
+	    pci_read_config_word(s_bridge, VIA686A_BASE_REG, &val))
+		return -ENODEV;
+	*address = val & ~(VIA686A_EXTENT - 1);
+	if (*address == 0 && force_addr == 0) {
+		printk("vt8231.o: base address not set - upgrade BIOS or use force_addr=0xaddr\n");
+		return -ENODEV;
+	}
+	if (force_addr)
+		*address = force_addr;	/* so detect will get called */
+
+	return 0;
+}
+
+int vt8231_detect(struct i2c_adapter *adapter, int address,
+		   unsigned short flags, int kind)
+{
+	int i;
+	struct i2c_client *new_client;
+	struct vt8231_data *data;
+	int err = 0;
+	const char *type_name = "vt8231";
+	u16 val;
+
+	if (!i2c_is_isa_adapter(adapter)) {
+		return 0;
+	}
+
+	/* 8231 requires multiple of 256 */
+	if(force_addr)
+		address = force_addr & 0xFF00;
+	if (check_region(address, VIA686A_EXTENT)) {
+		printk("vt8231.o: region 0x%x already in use!\n",
+		       address);
+		return -ENODEV;
+	}
+
+	if(force_addr) {
+		printk("vt8231.o: forcing ISA address 0x%04X\n", address);
+		if (PCIBIOS_SUCCESSFUL !=
+		    pci_write_config_word(s_bridge, VIA686A_BASE_REG, address))
+			return -ENODEV;
+	}
+	if (PCIBIOS_SUCCESSFUL !=
+	    pci_read_config_word(s_bridge, VIA686A_ENABLE_REG, &val))
+		return -ENODEV;
+	if (!(val & 0x0001)) {
+		printk("vt8231.o: enabling sensors\n");
+		if (PCIBIOS_SUCCESSFUL !=
+		    pci_write_config_word(s_bridge, VIA686A_ENABLE_REG,
+		                      val | 0x0001))
+			return -ENODEV;
+	}
+
+	if (!(data = kmalloc(sizeof(struct vt8231_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	init_MUTEX(&data->lock);
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &vt8231_driver;
+	new_client->flags = 0;
+
+	/* Reserve the ISA region */
+	request_region(address, VIA686A_EXTENT, "vt8231-sensors");
+
+	/* Fill in the remaining client fields and put into the global list */
+	strcpy(new_client->name, "Via 8231 Integrated Sensors");
+
+	new_client->id = vt8231_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry((struct i2c_client *) new_client,
+					type_name,
+					vt8231_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR4;
+	}
+	data->sysctl_id = i;
+
+	vt8231_init_client(new_client);
+	return 0;
+
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+	release_region(address, VIA686A_EXTENT);
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int vt8231_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct vt8231_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    ("vt8231.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	release_region(client->addr, VIA686A_EXTENT);
+	kfree(client->data);
+
+	return 0;
+}
+
+
+static inline int vt_rdval(struct i2c_client *client, u8 reg)
+{
+	return (inb_p(client->addr + reg));
+}
+
+static inline void vt8231_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+	outb_p(value, client->addr + reg);
+}
+
+static void vt8231_init_client(struct i2c_client *client)
+{
+	struct vt8231_data *data = client->data;
+
+	data->vrm = DEFAULT_VRM;
+	/* set "default" interrupt mode for alarms, which isn't the default */
+	vt8231_write_value(client, VT8231_REG_TEMP1_CONFIG, 0);
+	vt8231_write_value(client, VT8231_REG_TEMP2_CONFIG, 0);
+}
+
+static void vt8231_update_client(struct i2c_client *client)
+{
+	struct vt8231_data *data = client->data;
+	int i, j;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+		data->uch_config = vt_rdval(client, VT8231_REG_UCH_CONFIG);
+		for (i = 0; i <= 5; i++) {
+			if(ISVOLT(i, data->uch_config)) {
+				data->in[i] = vt_rdval(client, VT8231_REG_IN(i));
+				data->in_min[i] = vt_rdval(client,
+				                        VT8231_REG_IN_MIN(i));
+				data->in_max[i] = vt_rdval(client,
+				                        VT8231_REG_IN_MAX(i));
+			} else {
+				data->in[i] = 0;
+				data->in_min[i] = 0;
+				data->in_max[i] = 0;
+			}
+		}
+		for (i = 1; i <= 2; i++) {
+			data->fan[i - 1] = vt_rdval(client, VT8231_REG_FAN(i));
+			data->fan_min[i - 1] = vt_rdval(client,
+						     VT8231_REG_FAN_MIN(i));
+		}
+		for (i = 2; i <= 7; i++) {
+			if(ISTEMP(i, data->uch_config)) {
+				data->temp[i - 1] = vt_rdval(client,
+					             VT8231_REG_TEMP(i)) << 2;
+				switch(i) {
+					case 1:
+						/* ? */
+						j = 0;
+						break;
+					case 2:
+						j = (vt_rdval(client,
+						  VT8231_REG_TEMP_LOW2) &
+						                    0x30) >> 4;
+						break;
+					case 3:
+						j = (vt_rdval(client,
+						  VT8231_REG_TEMP_LOW3) &
+						                    0xc0) >> 6;
+						break;
+					case 4:
+					case 5:
+					case 6:
+					case 7:
+					default:
+						j = (vt_rdval(client,
+						  VT8231_REG_TEMP_LOW47) >>
+						            ((i-4)*2)) & 0x03;	
+						break;
+	
+				}
+				data->temp[i - 1] |= j;
+				data->temp_over[i - 1] = vt_rdval(client,
+					              VT8231_REG_TEMP_OVER(i));
+				data->temp_hyst[i - 1] = vt_rdval(client,
+					              VT8231_REG_TEMP_HYST(i));
+			} else {
+				data->temp[i - 1] = 0;
+				data->temp_over[i - 1] = 0;
+				data->temp_hyst[i - 1] = 0;
+			}
+		}
+
+		for (i = 1; i <= 2; i++) {
+			data->fan[i - 1] = vt_rdval(client, VT8231_REG_FAN(i));
+			data->fan_min[i - 1] = vt_rdval(client,
+			                                VT8231_REG_FAN_MIN(i));
+			data->pwm[i - 1] = vt_rdval(client, VT8231_REG_PWM(i));
+		}
+
+		data->pwm_ctl = vt_rdval(client, VT8231_REG_PWM_CTL);
+		i = vt_rdval(client, VT8231_REG_FANDIV);
+		data->fan_div[0] = (i >> 4) & 0x03;
+		data->fan_div[1] = i >> 6;
+		data->alarms = vt_rdval(client, VT8231_REG_ALARM1) |
+		                    (vt_rdval(client, VT8231_REG_ALARM2) << 8);
+		data->vid= vt_rdval(client, VT8231_REG_VID) & 0x1f;
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+
+void vt8231_in(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct vt8231_data *data = client->data;
+	int nr = ctl_name - VT8231_SYSCTL_IN0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		vt8231_update_client(client);
+		results[0] = IN_FROM_REG(data->in_min[nr]);
+		results[1] = IN_FROM_REG(data->in_max[nr]);
+		results[2] = IN_FROM_REG(data->in[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->in_min[nr] = IN_TO_REG(results[0]);
+			vt8231_write_value(client, VT8231_REG_IN_MIN(nr),
+					    data->in_min[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->in_max[nr] = IN_TO_REG(results[1]);
+			vt8231_write_value(client, VT8231_REG_IN_MAX(nr),
+					    data->in_max[nr]);
+		}
+	}
+}
+
+void vt8231_fan(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct vt8231_data *data = client->data;
+	int nr = ctl_name - VT8231_SYSCTL_FAN1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		vt8231_update_client(client);
+		results[0] = FAN_FROM_REG(data->fan_min[nr - 1],
+					  DIV_FROM_REG(data->fan_div
+						       [nr - 1]));
+		results[1] = FAN_FROM_REG(data->fan[nr - 1],
+				 DIV_FROM_REG(data->fan_div[nr - 1]));
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr - 1] = MIN_TO_REG(results[0],
+							   DIV_FROM_REG
+							   (data->
+							    fan_div[nr-1]));
+			vt8231_write_value(client, VT8231_REG_FAN_MIN(nr),
+					    data->fan_min[nr - 1]);
+		}
+	}
+}
+
+
+void vt8231_temp(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct vt8231_data *data = client->data;
+	int nr = ctl_name - VT8231_SYSCTL_TEMP;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		vt8231_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_over[nr]);
+		results[1] = TEMP_FROM_REG(data->temp_hyst[nr]);
+		results[2] = TEMP_FROM_REG10(data->temp[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_over[nr] = TEMP_TO_REG(results[0]);
+			vt8231_write_value(client,
+					    VT8231_REG_TEMP_OVER(nr + 1),
+					    data->temp_over[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_hyst[nr] = TEMP_TO_REG(results[1]);
+			vt8231_write_value(client,
+					    VT8231_REG_TEMP_HYST(nr + 1),
+					    data->temp_hyst[nr]);
+		}
+	}
+}
+
+void vt8231_alarms(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results)
+{
+	struct vt8231_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		vt8231_update_client(client);
+		results[0] = data->alarms;
+		*nrels_mag = 1;
+	}
+}
+
+void vt8231_fan_div(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	struct vt8231_data *data = client->data;
+	int old;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		vt8231_update_client(client);
+		results[0] = DIV_FROM_REG(data->fan_div[0]);
+		results[1] = DIV_FROM_REG(data->fan_div[1]);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		old = vt_rdval(client, VT8231_REG_FANDIV);
+		if (*nrels_mag >= 2) {
+			data->fan_div[1] = DIV_TO_REG(results[1]);
+			old = (old & 0x3f) | (data->fan_div[1] << 6);
+		}
+		if (*nrels_mag >= 1) {
+			data->fan_div[0] = DIV_TO_REG(results[0]);
+			old = (old & 0xcf) | (data->fan_div[0] << 4);
+			vt8231_write_value(client, VT8231_REG_FANDIV, old);
+		}
+	}
+}
+
+void vt8231_pwm(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct vt8231_data *data = client->data;
+	int nr = 1 + ctl_name - VT8231_SYSCTL_PWM1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		vt8231_update_client(client);
+		results[0] = PWM_FROM_REG(data->pwm[nr - 1]);
+		results[1] = (data->pwm_ctl >> (3 + (4 * (nr - 1)))) & 1;
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->pwm[nr - 1] = PWM_TO_REG(results[0]);
+			if (*nrels_mag >= 2) {
+				if(results[1]) {
+					data->pwm_ctl |=
+					          (0x08 << (4 * (nr - 1)));
+					vt8231_write_value(client,
+					                   VT8231_REG_PWM_CTL, 
+				                           data->pwm_ctl);
+				} else {
+					data->pwm_ctl &=
+					        ~ (0x08 << (4 * (nr - 1)));
+					vt8231_write_value(client,
+					                   VT8231_REG_PWM_CTL, 
+				                           data->pwm_ctl);
+				}
+			}
+			vt8231_write_value(client, VT8231_REG_PWM(nr),
+					    data->pwm[nr - 1]);
+		}
+	}
+}
+
+void vt8231_vid(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct vt8231_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 3;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		vt8231_update_client(client);
+		results[0] = vid_from_reg(data->vid, data->vrm);
+		*nrels_mag = 1;
+	}
+}
+
+void vt8231_vrm(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct vt8231_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = data->vrm;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1)
+			data->vrm = results[0];
+	}
+}
+
+void vt8231_uch(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct vt8231_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = data->uch_config & 0x7c;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->uch_config = (data->uch_config & 0x83)|(results[0] & 0x7c);
+			vt8231_write_value(client, VT8231_REG_UCH_CONFIG,
+			                   data->uch_config);
+		}
+	}
+}
+
+static int __init sm_vt8231_init(void)
+{
+	int addr;
+
+	printk("vt8231.o version %s (%s)\n", LM_VERSION, LM_DATE);
+
+	if (vt8231_find(&addr)) {
+		printk("vt8231.o: VT8231 not detected, module not inserted.\n");
+		return -ENODEV;
+	}
+	normal_isa[0] = addr;
+
+	return i2c_add_driver(&vt8231_driver);}
+
+static void __exit sm_vt8231_exit(void)
+{
+	i2c_del_driver(&vt8231_driver);
+}
+
+
+
+MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("VT8231 sensors");
+MODULE_LICENSE("GPL");
+
+module_init(sm_vt8231_init);
+module_exit(sm_vt8231_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/w83627hf.c linux-2.4.27-leo/drivers/sensors/w83627hf.c
--- linux-2.4.27/drivers/sensors/w83627hf.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/w83627hf.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,1403 @@
+/*
+    w83627hf.c - Part of lm_sensors, Linux kernel modules for hardware
+                monitoring
+    Copyright (c) 1998 - 2003  Frodo Looijaard <frodol@dds.nl>,
+    Philip Edelbrock <phil@netroedge.com>,
+    and Mark Studebaker <mdsxyz123@yahoo.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+    Supports following chips:
+
+    Chip	#vin	#fanin	#pwm	#temp	wchipid	vendid	i2c	ISA
+    w83627hf	9	3	2	3	0x20	0x5ca3	no	yes(LPC)
+    w83627thf	7	3	3	3	0x90	0x5ca3	no	yes(LPC)
+    w83637hf	7	3	3	3	0x80	0x5ca3	no	yes(LPC)
+    w83697hf	8	2	2	2	0x60	0x5ca3	no	yes(LPC)
+
+    For other winbond chips, and for i2c support in the above chips,
+    use w83781d.c.
+
+    Note: automatic ("cruise") fan control for 697, 637 & 627thf not
+    supported yet.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+#include <linux/sensors_vid.h>
+#include "lm75.h"
+
+static int force_addr;
+MODULE_PARM(force_addr, "i");
+MODULE_PARM_DESC(force_addr,
+		 "Initialize the base address of the sensors");
+static int force_i2c = 0x1f;
+MODULE_PARM(force_i2c, "i");
+MODULE_PARM_DESC(force_i2c,
+		 "Initialize the i2c address of the sensors");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { 0, SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_4(w83627hf, w83627thf, w83697hf, w83637hf);
+
+static int init = 1;
+MODULE_PARM(init, "i");
+MODULE_PARM_DESC(init, "Set to zero to bypass chip initialization");
+
+/* modified from kernel/include/traps.c */
+#define	REG	0x2e	/* The register to read/write */
+#define	DEV	0x07	/* Register: Logical device select */
+#define	VAL	0x2f	/* The value to read/write */
+
+/* logical device numbers for superio_select (below) */
+#define W83627HF_LD_FDC		0x00
+#define W83627HF_LD_PRT		0x01
+#define W83627HF_LD_UART1	0x02
+#define W83627HF_LD_UART2	0x03
+#define W83627HF_LD_KBC		0x05
+#define W83627HF_LD_CIR		0x06 /* w83627hf only */
+#define W83627HF_LD_GAME	0x07
+#define W83627HF_LD_MIDI	0x07
+#define W83627HF_LD_GPIO1	0x07
+#define W83627HF_LD_GPIO5	0x07 /* w83627thf only */
+#define W83627HF_LD_GPIO2	0x08
+#define W83627HF_LD_GPIO3	0x09
+#define W83627HF_LD_GPIO4	0x09 /* w83627thf only */
+#define W83627HF_LD_ACPI	0x0a
+#define W83627HF_LD_HWM		0x0b
+
+#define	DEVID	0x20	/* Register: Device ID */
+
+#define W83627THF_GPIO5_IOSR	0xf3 /* w83627thf only */
+#define W83627THF_GPIO5_DR	0xf4 /* w83627thf only */
+#define W83627THF_GPIO5_INVR	0xf5 /* w83627thf only */
+
+static inline void
+superio_outb(int reg, int val)
+{
+	outb(reg, REG);
+	outb(val, VAL);
+}
+
+static inline int
+superio_inb(int reg)
+{
+	outb(reg, REG);
+	return inb(VAL);
+}
+
+static inline void
+superio_select(int ld)
+{
+	outb(DEV, REG);
+	outb(ld, VAL);
+}
+
+static inline void
+superio_enter(void)
+{
+	outb(0x87, REG);
+	outb(0x87, REG);
+}
+
+static inline void
+superio_exit(void)
+{
+	outb(0xAA, REG);
+}
+
+#define W627_DEVID 0x52
+#define W627THF_DEVID 0x82
+#define W697_DEVID 0x60
+#define W637_DEVID 0x70
+#define WINB_ACT_REG 0x30
+#define WINB_BASE_REG 0x60
+/* Constants specified below */
+
+/* Length of ISA address segment */
+#define WINB_EXTENT 8
+
+/* Where are the ISA address/data registers relative to the base address */
+#define W83781D_ADDR_REG_OFFSET 5
+#define W83781D_DATA_REG_OFFSET 6
+
+/* The W83781D registers */
+/* The W83782D registers for nr=7,8 are in bank 5 */
+#define W83781D_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \
+					   (0x554 + (((nr) - 7) * 2)))
+#define W83781D_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \
+					   (0x555 + (((nr) - 7) * 2)))
+#define W83781D_REG_IN(nr)     ((nr < 7) ? (0x20 + (nr)) : \
+					   (0x550 + (nr) - 7))
+
+#define W83781D_REG_FAN_MIN(nr) (0x3a + (nr))
+#define W83781D_REG_FAN(nr) (0x27 + (nr))
+
+#define W83781D_REG_TEMP2 0x0150
+#define W83781D_REG_TEMP3 0x0250
+#define W83781D_REG_TEMP2_HYST 0x153
+#define W83781D_REG_TEMP3_HYST 0x253
+#define W83781D_REG_TEMP2_CONFIG 0x152
+#define W83781D_REG_TEMP3_CONFIG 0x252
+#define W83781D_REG_TEMP2_OVER 0x155
+#define W83781D_REG_TEMP3_OVER 0x255
+
+#define W83781D_REG_TEMP 0x27
+#define W83781D_REG_TEMP_OVER 0x39
+#define W83781D_REG_TEMP_HYST 0x3A
+#define W83781D_REG_BANK 0x4E
+
+#define W83781D_REG_CONFIG 0x40
+#define W83781D_REG_ALARM1 0x41
+#define W83781D_REG_ALARM2 0x42
+#define W83781D_REG_ALARM3 0x450
+
+#define W83781D_REG_IRQ 0x4C
+#define W83781D_REG_BEEP_CONFIG 0x4D
+#define W83781D_REG_BEEP_INTS1 0x56
+#define W83781D_REG_BEEP_INTS2 0x57
+#define W83781D_REG_BEEP_INTS3 0x453
+
+#define W83781D_REG_VID_FANDIV 0x47
+
+#define W83781D_REG_CHIPID 0x49
+#define W83781D_REG_WCHIPID 0x58
+#define W83781D_REG_CHIPMAN 0x4F
+#define W83781D_REG_PIN 0x4B
+
+#define W83781D_REG_VBAT 0x5D
+
+#define W83627HF_REG_PWM1 0x5A
+#define W83627HF_REG_PWM2 0x5B
+#define W83627HF_REG_PWMCLK12 0x5C
+
+#define W83627THF_REG_PWM1		0x01	/* 697HF and 637HF too */
+#define W83627THF_REG_PWM2		0x03	/* 697HF and 637HF too */
+#define W83627THF_REG_PWM3		0x11	/* 637HF too */
+
+#define W83627THF_REG_VRM_OVT_CFG 	0x18	/* 637HF too, unused yet */
+
+static const u8 regpwm_627hf[] = { W83627HF_REG_PWM1, W83627HF_REG_PWM2 };
+static const u8 regpwm[] = { W83627THF_REG_PWM1, W83627THF_REG_PWM2,
+                             W83627THF_REG_PWM3 };
+#define W836X7HF_REG_PWM(type, nr) (((type) == w83627hf) ? \
+                                     regpwm_627hf[(nr) - 1] : regpwm[(nr) - 1])
+
+#define W83781D_REG_I2C_ADDR 0x48
+#define W83781D_REG_I2C_SUBADDR 0x4A
+
+/* Sensor selection */
+#define W83781D_REG_SCFG1 0x5D
+static const u8 BIT_SCFG1[] = { 0x02, 0x04, 0x08 };
+#define W83781D_REG_SCFG2 0x59
+static const u8 BIT_SCFG2[] = { 0x10, 0x20, 0x40 };
+#define W83781D_DEFAULT_BETA 3435
+
+/* Conversions. Limit checking is only done on the TO_REG
+   variants. Note that you should be a bit careful with which arguments
+   these macros are called: arguments may be evaluated more than once.
+   Fixing this is just not worth it. */
+#define IN_TO_REG(val)  (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255))
+#define IN_FROM_REG(val) (((val) * 16 + 5) / 10)
+
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+	if (rpm == 0)
+		return 255;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1,
+			     254);
+}
+
+#define TEMP_MIN (-1280)
+#define TEMP_MAX ( 1270)
+
+/* TEMP: 1/10 degrees C (-128C to +127C)
+   REG: 1C/bit, two's complement */
+static u8 TEMP_TO_REG(int temp)
+{
+        int ntemp = SENSORS_LIMIT(temp, TEMP_MIN, TEMP_MAX);
+        ntemp += (ntemp<0 ? -5 : 5);
+        return (u8)(ntemp / 10);
+}
+                                                                                
+static int TEMP_FROM_REG(u8 reg)
+{
+        return (s8)reg * 10;
+}
+
+#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div)))
+
+#define PWM_TO_REG(val) (SENSORS_LIMIT((val),0,255))
+#define BEEPS_TO_REG(val) ((val) & 0xffffff)
+
+#define BEEP_ENABLE_TO_REG(val)   ((val)?1:0)
+#define BEEP_ENABLE_FROM_REG(val) ((val)?1:0)
+
+#define DIV_FROM_REG(val) (1 << (val))
+
+static inline u8 DIV_TO_REG(long val)
+{
+	int i;
+	val = SENSORS_LIMIT(val, 1, 128) >> 1;
+	for (i = 0; i < 6; i++) {
+		if (val == 0)
+			break;
+		val >>= 1;
+	}
+	return ((u8) i);
+}
+
+/* For each registered chip, we need to keep some data in memory. That
+   data is pointed to by w83627hf_list[NR]->data. The structure itself is
+   dynamically allocated, at the same time when a new client is allocated. */
+struct w83627hf_data {
+	struct i2c_client client;
+	struct semaphore lock;
+	int sysctl_id;
+	enum chips type;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	struct i2c_client *lm75;	/* for secondary I2C addresses */
+	/* pointer to array of 2 subclients */
+
+	u8 in[9];		/* Register value */
+	u8 in_max[9];		/* Register value */
+	u8 in_min[9];		/* Register value */
+	u8 fan[3];		/* Register value */
+	u8 fan_min[3];		/* Register value */
+	u8 temp;
+	u8 temp_over;		/* Register value */
+	u8 temp_hyst;		/* Register value */
+	u16 temp_add[2];	/* Register value */
+	u16 temp_add_over[2];	/* Register value */
+	u16 temp_add_hyst[2];	/* Register value */
+	u8 fan_div[3];		/* Register encoding, shifted right */
+	u8 vid;			/* Register encoding, combined */
+	u32 alarms;		/* Register encoding, combined */
+	u32 beeps;		/* Register encoding, combined */
+	u8 beep_enable;		/* Boolean */
+	u8 pwm[3];		/* Register value */
+	u8 pwmenable[3];	/* bool */
+	u16 sens[3];		/* 782D/783S only.
+				   1 = pentium diode; 2 = 3904 diode;
+				   3000-5000 = thermistor beta.
+				   Default = 3435. 
+				   Other Betas unimplemented */
+	u8 vrm;
+};
+
+
+static int w83627hf_attach_adapter(struct i2c_adapter *adapter);
+static int w83627hf_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int w83627hf_detach_client(struct i2c_client *client);
+
+static int w83627hf_read_value(struct i2c_client *client, u16 register);
+static int w83627hf_write_value(struct i2c_client *client, u16 register,
+			       u16 value);
+static void w83627hf_update_client(struct i2c_client *client);
+static void w83627hf_init_client(struct i2c_client *client);
+
+
+static void w83627hf_in(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void w83627hf_fan(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void w83627hf_temp(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void w83627hf_temp_add(struct i2c_client *client, int operation,
+			     int ctl_name, int *nrels_mag, long *results);
+static void w83627hf_vid(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void w83627hf_vrm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void w83627hf_alarms(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+static void w83627hf_beep(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void w83627hf_fan_div(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void w83627hf_pwm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void w83627hf_sens(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+
+static int w83627hf_id = 0;
+
+static struct i2c_driver w83627hf_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "W83627HF sensor driver",
+	.id		= I2C_DRIVERID_W83627HF,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= w83627hf_attach_adapter,
+	.detach_client	= w83627hf_detach_client,
+};
+
+/* The /proc/sys entries */
+/* WARNING these are copied from w83781d.c and have not been renamed.
+   Note that the 627hf and 697hf are supported by both drivers.
+   Do not make incompatible changes here or we will have errors
+   in the generated file ../include/sensors.h !!!
+*/
+/* -- SENSORS SYSCTL START -- */
+
+#define W83781D_SYSCTL_IN0 1000	/* Volts * 100 */
+#define W83781D_SYSCTL_IN1 1001
+#define W83781D_SYSCTL_IN2 1002
+#define W83781D_SYSCTL_IN3 1003
+#define W83781D_SYSCTL_IN4 1004
+#define W83781D_SYSCTL_IN5 1005
+#define W83781D_SYSCTL_IN6 1006
+#define W83781D_SYSCTL_IN7 1007
+#define W83781D_SYSCTL_IN8 1008
+#define W83781D_SYSCTL_FAN1 1101	/* Rotations/min */
+#define W83781D_SYSCTL_FAN2 1102
+#define W83781D_SYSCTL_FAN3 1103
+#define W83781D_SYSCTL_TEMP1 1200	/* Degrees Celcius * 10 */
+#define W83781D_SYSCTL_TEMP2 1201	/* Degrees Celcius * 10 */
+#define W83781D_SYSCTL_TEMP3 1202	/* Degrees Celcius * 10 */
+#define W83781D_SYSCTL_VID 1300		/* Volts * 1000 */
+#define W83781D_SYSCTL_VRM 1301
+#define W83781D_SYSCTL_PWM1 1401
+#define W83781D_SYSCTL_PWM2 1402
+#define W83781D_SYSCTL_PWM3 1403
+#define W83781D_SYSCTL_SENS1 1501	/* 1, 2, or Beta (3000-5000) */
+#define W83781D_SYSCTL_SENS2 1502
+#define W83781D_SYSCTL_SENS3 1503
+#define W83781D_SYSCTL_FAN_DIV 2000	/* 1, 2, 4 or 8 */
+#define W83781D_SYSCTL_ALARMS 2001	/* bitvector */
+#define W83781D_SYSCTL_BEEP 2002	/* bitvector */
+
+#define W83781D_ALARM_IN0 0x0001
+#define W83781D_ALARM_IN1 0x0002
+#define W83781D_ALARM_IN2 0x0004
+#define W83781D_ALARM_IN3 0x0008
+#define W83781D_ALARM_IN4 0x0100
+#define W83781D_ALARM_IN5 0x0200
+#define W83781D_ALARM_IN6 0x0400
+#define W83782D_ALARM_IN7 0x10000
+#define W83782D_ALARM_IN8 0x20000
+#define W83781D_ALARM_FAN1 0x0040
+#define W83781D_ALARM_FAN2 0x0080
+#define W83781D_ALARM_FAN3 0x0800
+#define W83781D_ALARM_TEMP1 0x0010
+#define W83781D_ALARM_TEMP23 0x0020	/* 781D only */
+#define W83781D_ALARM_TEMP2 0x0020	/* 782D/783S */
+#define W83781D_ALARM_TEMP3 0x2000	/* 782D only */
+#define W83781D_ALARM_CHAS 0x1000
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected chip. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized 
+   when a new copy is allocated. */
+
+/* without pwm3-4 */
+static ctl_table w83627hf_dir_table_template[] = {
+	{W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_fan},
+	{W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_fan},
+	{W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_fan},
+	{W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_temp},
+	{W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_temp_add},
+	{W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_temp_add},
+	{W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_vid},
+	{W83781D_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_vrm},
+	{W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_fan_div},
+	{W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_alarms},
+	{W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_beep},
+	{W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_pwm},
+	{W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_pwm},
+	{W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_sens},
+	{W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_sens},
+	{W83781D_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_sens},
+	{0}
+};
+
+/* similar to w83782d but no fan3, no vid */
+static ctl_table w83697hf_dir_table_template[] = {
+	{W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	/* no in1 to maintain compatibility with 781d and 782d. */
+	{W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_fan},
+	{W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_fan},
+	{W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_temp},
+	{W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_temp_add},
+	{W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_fan_div},
+	{W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_alarms},
+	{W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_beep},
+	{W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_pwm},
+	{W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_pwm},
+	{W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_sens},
+	{W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_sens},
+	{0}
+};
+
+/* no in5 and in6 */
+/* We use this one for W83637HF too */
+static ctl_table w83627thf_dir_table_template[] = {
+	{W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_in},
+	{W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_fan},
+	{W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_fan},
+	{W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_fan},
+	{W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_temp},
+	{W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_temp_add},
+	{W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_temp_add},
+	{W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_vid},
+	{W83781D_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_vrm},
+	{W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_fan_div},
+	{W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_alarms},
+	{W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_beep},
+	{W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_pwm},
+	{W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_pwm},
+	{W83781D_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_pwm},
+	{W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_sens},
+	{W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_sens},
+	{W83781D_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83627hf_sens},
+	{0}
+};
+
+
+/* This function is called when:
+     * w83627hf_driver is inserted (when this module is loaded), for each
+       available adapter
+     * when a new adapter is inserted (and w83627hf_driver is still present) */
+static int w83627hf_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, w83627hf_detect);
+}
+
+static int w83627hf_find(int *address)
+{
+	u16 val;
+
+	superio_enter();
+	val= superio_inb(DEVID);
+	if(val != W627_DEVID && val !=W627THF_DEVID && val != W697_DEVID && val != W637_DEVID) {
+		superio_exit();
+		return -ENODEV;
+	}
+
+	superio_select(W83627HF_LD_HWM);
+	val = (superio_inb(WINB_BASE_REG) << 8) |
+	       superio_inb(WINB_BASE_REG + 1);
+	*address = val & ~(WINB_EXTENT - 1);
+	if (*address == 0 && force_addr == 0) {
+		printk("w83627hf.o: base address not set - use force_addr=0xaddr\n");
+		superio_exit();
+		return -ENODEV;
+	}
+	if (force_addr)
+		*address = force_addr;	/* so detect will get called */
+
+	superio_exit();
+	return 0;
+}
+
+int w83627hf_detect(struct i2c_adapter *adapter, int address,
+		   unsigned short flags, int kind)
+{
+	int i, val;
+	struct i2c_client *new_client;
+	struct w83627hf_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+
+	if (!i2c_is_isa_adapter(adapter))
+		return 0;
+
+	if(force_addr)
+		address = force_addr & ~(WINB_EXTENT - 1);
+	if (check_region(address, WINB_EXTENT)) {
+		printk("w83627hf.o: region 0x%x already in use!\n", address);
+		return -ENODEV;
+	}
+	if(force_addr) {
+		printk("w83627hf.o: forcing ISA address 0x%04X\n", address);
+		superio_enter();
+		superio_select(W83627HF_LD_HWM);
+		superio_outb(WINB_BASE_REG, address >> 8);
+		superio_outb(WINB_BASE_REG+1, address & 0xff);
+		superio_exit();
+	}
+
+	superio_enter();
+	val= superio_inb(DEVID);
+	if(val == W627_DEVID)
+		kind = w83627hf;
+	else if(val == W697_DEVID)
+		kind = w83697hf;
+	else if(val == W627THF_DEVID)
+		kind = w83627thf;
+	else if(val == W637_DEVID)
+		kind = w83637hf;
+		
+	superio_select(W83627HF_LD_HWM);
+	if((val = 0x01 & superio_inb(WINB_ACT_REG)) == 0)
+		superio_outb(WINB_ACT_REG, 1);
+	superio_exit();
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access w83627hf_{read,write}_value. */
+
+	if (!(data = kmalloc(sizeof(struct w83627hf_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	init_MUTEX(&data->lock);
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &w83627hf_driver;
+	new_client->flags = 0;
+
+
+	if (kind == w83627hf) {
+		type_name = "w83627hf";
+		client_name = "W83627HF chip";
+	} else if (kind == w83627thf) {
+		type_name = "w83627thf";
+		client_name = "W83627THF chip";
+	} else if (kind == w83697hf) {
+		type_name = "w83697hf";
+		client_name = "W83697HF chip";
+	} else if (kind == w83637hf) {
+		type_name = "w83637hf";
+		client_name = "W83637HF chip";
+	} else {
+		goto ERROR1;
+	}
+
+	request_region(address, WINB_EXTENT, type_name);
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	data->type = kind;
+	new_client->id = w83627hf_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	data->lm75 = NULL;
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client,
+				type_name,
+				(kind == w83697hf) ?
+				   w83697hf_dir_table_template :
+				(kind == w83627hf) ?
+				   w83627hf_dir_table_template :
+				   /* w83627thf table also used for 637 */
+				   w83627thf_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR7;
+	}
+	data->sysctl_id = i;
+
+	/* Initialize the chip */
+	w83627hf_init_client(new_client);
+	return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+
+      ERROR7:
+	i2c_detach_client(new_client);
+      ERROR3:
+	release_region(address, WINB_EXTENT);
+      ERROR1:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int w83627hf_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct w83627hf_data *) (client->data))->
+				 sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    (KERN_ERR "w83627hf.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	release_region(client->addr, WINB_EXTENT);
+	kfree(client->data);
+
+	return 0;
+}
+
+
+/*
+   ISA access must always be locked explicitly! 
+   We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks,
+   would slow down the W83781D access and should not be necessary. 
+   There are some ugly typecasts here, but the good news is - they should
+   nowhere else be necessary! */
+static int w83627hf_read_value(struct i2c_client *client, u16 reg)
+{
+	int res, word_sized;
+
+	down(&(((struct w83627hf_data *) (client->data))->lock));
+	word_sized = (((reg & 0xff00) == 0x100)
+		      || ((reg & 0xff00) == 0x200))
+	    && (((reg & 0x00ff) == 0x50)
+		|| ((reg & 0x00ff) == 0x53)
+		|| ((reg & 0x00ff) == 0x55));
+	if (reg & 0xff00) {
+		outb_p(W83781D_REG_BANK,
+		       client->addr + W83781D_ADDR_REG_OFFSET);
+		outb_p(reg >> 8,
+		       client->addr + W83781D_DATA_REG_OFFSET);
+	}
+	outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET);
+	res = inb_p(client->addr + W83781D_DATA_REG_OFFSET);
+	if (word_sized) {
+		outb_p((reg & 0xff) + 1,
+		       client->addr + W83781D_ADDR_REG_OFFSET);
+		res =
+		    (res << 8) + inb_p(client->addr +
+				       W83781D_DATA_REG_OFFSET);
+	}
+	if (reg & 0xff00) {
+		outb_p(W83781D_REG_BANK,
+		       client->addr + W83781D_ADDR_REG_OFFSET);
+		outb_p(0, client->addr + W83781D_DATA_REG_OFFSET);
+	}
+	up(&(((struct w83627hf_data *) (client->data))->lock));
+	return res;
+}
+
+static int w83627thf_read_gpio5(struct i2c_client *client)
+{
+	int res, inv;
+
+	down(&(((struct w83627hf_data *) (client->data))->lock));
+	superio_enter();
+	superio_select(W83627HF_LD_GPIO5);
+	res = superio_inb(W83627THF_GPIO5_DR);
+	inv = superio_inb(W83627THF_GPIO5_INVR);
+	superio_exit();
+	up(&(((struct w83627hf_data *) (client->data))->lock));
+	return res;
+}
+
+static int w83627hf_write_value(struct i2c_client *client, u16 reg, u16 value)
+{
+	int word_sized;
+
+	down(&(((struct w83627hf_data *) (client->data))->lock));
+	word_sized = (((reg & 0xff00) == 0x100)
+		      || ((reg & 0xff00) == 0x200))
+	    && (((reg & 0x00ff) == 0x53)
+		|| ((reg & 0x00ff) == 0x55));
+	if (reg & 0xff00) {
+		outb_p(W83781D_REG_BANK,
+		       client->addr + W83781D_ADDR_REG_OFFSET);
+		outb_p(reg >> 8,
+		       client->addr + W83781D_DATA_REG_OFFSET);
+	}
+	outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET);
+	if (word_sized) {
+		outb_p(value >> 8,
+		       client->addr + W83781D_DATA_REG_OFFSET);
+		outb_p((reg & 0xff) + 1,
+		       client->addr + W83781D_ADDR_REG_OFFSET);
+	}
+	outb_p(value & 0xff,
+	       client->addr + W83781D_DATA_REG_OFFSET);
+	if (reg & 0xff00) {
+		outb_p(W83781D_REG_BANK,
+		       client->addr + W83781D_ADDR_REG_OFFSET);
+		outb_p(0, client->addr + W83781D_DATA_REG_OFFSET);
+	}
+	up(&(((struct w83627hf_data *) (client->data))->lock));
+	return 0;
+}
+
+/* Called when we have found a new W83781D. It should set limits, etc. */
+static void w83627hf_init_client(struct i2c_client *client)
+{
+	struct w83627hf_data *data = client->data;
+	int vid = 0, i;
+	int type = data->type;
+	u8 tmp;
+
+	if(init) {
+		/* save this register */
+		i = w83627hf_read_value(client, W83781D_REG_BEEP_CONFIG);
+		/* Reset all except Watchdog values and last conversion values
+		   This sets fan-divs to 2, among others */
+		w83627hf_write_value(client, W83781D_REG_CONFIG, 0x80);
+		/* Restore the register and disable power-on abnormal beep.
+		   This saves FAN 1/2/3 input/output values set by BIOS. */
+		w83627hf_write_value(client, W83781D_REG_BEEP_CONFIG, i | 0x80);
+		/* Disable master beep-enable (reset turns it on).
+		   Individual beeps should be reset to off but for some reason
+		   disabling this bit helps some people not get beeped */
+		w83627hf_write_value(client, W83781D_REG_BEEP_INTS2, 0);
+	}
+
+	/* Minimize conflicts with other winbond i2c-only clients...  */
+	/* disable i2c subclients... how to disable main i2c client?? */
+	/* force i2c address to relatively uncommon address */
+	w83627hf_write_value(client, W83781D_REG_I2C_SUBADDR, 0x89);
+	w83627hf_write_value(client, W83781D_REG_I2C_ADDR, force_i2c);
+
+	/* Read VID only once */
+	if (w83627hf == data->type || w83637hf == data->type) {
+		int lo = w83627hf_read_value(client, W83781D_REG_VID_FANDIV);
+		int hi = w83627hf_read_value(client, W83781D_REG_CHIPID);
+		data->vid = (lo & 0x0f) | ((hi & 0x01) << 4);
+	} else if (w83627thf == data->type) {
+		data->vid = w83627thf_read_gpio5(client) & 0x1f;
+	}
+
+	/* Convert VID to voltage based on default VRM */
+	data->vrm = DEFAULT_VRM;
+	if (type != w83697hf)
+		vid = vid_from_reg(vid, data->vrm);
+
+	tmp = w83627hf_read_value(client, W83781D_REG_SCFG1);
+	for (i = 1; i <= 3; i++) {
+		if (!(tmp & BIT_SCFG1[i - 1])) {
+			data->sens[i - 1] = W83781D_DEFAULT_BETA;
+		} else {
+			if (w83627hf_read_value
+			    (client,
+			     W83781D_REG_SCFG2) & BIT_SCFG2[i - 1])
+				data->sens[i - 1] = 1;
+			else
+				data->sens[i - 1] = 2;
+		}
+		if ((type == w83697hf) && (i == 2))
+			break;
+	}
+
+	data->pwmenable[0] = 1;
+	data->pwmenable[1] = 1;
+	data->pwmenable[2] = 1;
+
+	if(init) {
+		if (type == w83627hf) {
+			/* enable PWM2 control (can't hurt since PWM reg
+		           should have been reset to 0xff) */
+			w83627hf_write_value(client, W83627HF_REG_PWMCLK12, 0x19);
+		}
+		/* enable comparator mode for temp2 and temp3 so
+	           alarm indication will work correctly */
+		i = w83627hf_read_value(client, W83781D_REG_IRQ);
+		if (!(i & 0x40))
+			w83627hf_write_value(client, W83781D_REG_IRQ,
+					    i | 0x40);
+	}
+
+	/* Start monitoring */
+	w83627hf_write_value(client, W83781D_REG_CONFIG,
+			    (w83627hf_read_value(client,
+						W83781D_REG_CONFIG) & 0xf7)
+			    | 0x01);
+}
+
+static void w83627hf_update_client(struct i2c_client *client)
+{
+	struct w83627hf_data *data = client->data;
+	int i;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+		for (i = 0; i <= 8; i++) {
+			/* skip missing sensors */
+			if (((data->type == w83697hf) && (i == 1)) ||
+			    ((data->type == w83627thf || data->type == w83637hf) &&
+			     (i == 4 || i == 5)))
+				continue;
+			data->in[i] =
+			    w83627hf_read_value(client, W83781D_REG_IN(i));
+			data->in_min[i] =
+			    w83627hf_read_value(client,
+					       W83781D_REG_IN_MIN(i));
+			data->in_max[i] =
+			    w83627hf_read_value(client,
+					       W83781D_REG_IN_MAX(i));
+		}
+		for (i = 1; i <= 3; i++) {
+			data->fan[i - 1] =
+			    w83627hf_read_value(client, W83781D_REG_FAN(i));
+			data->fan_min[i - 1] =
+			    w83627hf_read_value(client,
+					       W83781D_REG_FAN_MIN(i));
+		}
+		for (i = 1; i <= 3; i++) {
+			u8 tmp = w83627hf_read_value(client,
+				W836X7HF_REG_PWM(data->type, i));
+			if (data->type == w83627thf)
+				tmp &= 0xf0; /* bits 0-3 are reserved  in 627THF */
+			data->pwm[i - 1] = tmp;
+			if(i == 2 && (data->type == w83627hf || data->type == w83697hf))
+				break;
+		}
+
+		data->temp = w83627hf_read_value(client, W83781D_REG_TEMP);
+		data->temp_over =
+		    w83627hf_read_value(client, W83781D_REG_TEMP_OVER);
+		data->temp_hyst =
+		    w83627hf_read_value(client, W83781D_REG_TEMP_HYST);
+		data->temp_add[0] =
+		    w83627hf_read_value(client, W83781D_REG_TEMP2);
+		data->temp_add_over[0] =
+		    w83627hf_read_value(client, W83781D_REG_TEMP2_OVER);
+		data->temp_add_hyst[0] =
+		    w83627hf_read_value(client, W83781D_REG_TEMP2_HYST);
+		if (data->type != w83697hf) {
+			data->temp_add[1] =
+			    w83627hf_read_value(client, W83781D_REG_TEMP3);
+			data->temp_add_over[1] =
+			    w83627hf_read_value(client, W83781D_REG_TEMP3_OVER);
+			data->temp_add_hyst[1] =
+			    w83627hf_read_value(client, W83781D_REG_TEMP3_HYST);
+		}
+
+		i = w83627hf_read_value(client, W83781D_REG_VID_FANDIV);
+		data->fan_div[0] = (i >> 4) & 0x03;
+		data->fan_div[1] = (i >> 6) & 0x03;
+		if (data->type != w83697hf) {
+			data->fan_div[2] = (w83627hf_read_value(client,
+					       W83781D_REG_PIN) >> 6) & 0x03;
+		}
+		i = w83627hf_read_value(client, W83781D_REG_VBAT);
+		data->fan_div[0] |= (i >> 3) & 0x04;
+		data->fan_div[1] |= (i >> 4) & 0x04;
+		if (data->type != w83697hf)
+			data->fan_div[2] |= (i >> 5) & 0x04;
+		data->alarms =
+		    w83627hf_read_value(client, W83781D_REG_ALARM1) |
+		    (w83627hf_read_value(client, W83781D_REG_ALARM2) << 8) |
+		    (w83627hf_read_value(client, W83781D_REG_ALARM3) << 16);
+		i = w83627hf_read_value(client, W83781D_REG_BEEP_INTS2);
+		data->beep_enable = i >> 7;
+		data->beeps = ((i & 0x7f) << 8) |
+		    w83627hf_read_value(client, W83781D_REG_BEEP_INTS1) |
+		    w83627hf_read_value(client, W83781D_REG_BEEP_INTS3) << 16;
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+void w83627hf_in(struct i2c_client *client, int operation, int ctl_name,
+		int *nrels_mag, long *results)
+{
+	struct w83627hf_data *data = client->data;
+	int nr = ctl_name - W83781D_SYSCTL_IN0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83627hf_update_client(client);
+		results[0] = IN_FROM_REG(data->in_min[nr]);
+		results[1] = IN_FROM_REG(data->in_max[nr]);
+		results[2] = IN_FROM_REG(data->in[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->in_min[nr] = IN_TO_REG(results[0]);
+			w83627hf_write_value(client, W83781D_REG_IN_MIN(nr),
+					    data->in_min[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->in_max[nr] = IN_TO_REG(results[1]);
+			w83627hf_write_value(client, W83781D_REG_IN_MAX(nr),
+					    data->in_max[nr]);
+		}
+	}
+}
+
+void w83627hf_fan(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct w83627hf_data *data = client->data;
+	int nr = ctl_name - W83781D_SYSCTL_FAN1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83627hf_update_client(client);
+		results[0] = FAN_FROM_REG(data->fan_min[nr - 1],
+				  DIV_FROM_REG(data->fan_div[nr - 1]));
+		results[1] = FAN_FROM_REG(data->fan[nr - 1],
+			          DIV_FROM_REG(data->fan_div[nr - 1]));
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr - 1] =
+			     FAN_TO_REG(results[0],
+			            DIV_FROM_REG(data->fan_div[nr-1]));
+			w83627hf_write_value(client,
+					    W83781D_REG_FAN_MIN(nr),
+					    data->fan_min[nr - 1]);
+		}
+	}
+}
+
+void w83627hf_temp(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct w83627hf_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83627hf_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_over);
+		results[1] = TEMP_FROM_REG(data->temp_hyst);
+		results[2] = TEMP_FROM_REG(data->temp);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_over = TEMP_TO_REG(results[0]);
+			w83627hf_write_value(client, W83781D_REG_TEMP_OVER,
+					    data->temp_over);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_hyst = TEMP_TO_REG(results[1]);
+			w83627hf_write_value(client, W83781D_REG_TEMP_HYST,
+					    data->temp_hyst);
+		}
+	}
+}
+
+void w83627hf_temp_add(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results)
+{
+	struct w83627hf_data *data = client->data;
+	int nr = ctl_name - W83781D_SYSCTL_TEMP2;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83627hf_update_client(client);
+			results[0] =
+			    LM75_TEMP_FROM_REG(data->temp_add_over[nr]);
+			results[1] =
+			    LM75_TEMP_FROM_REG(data->temp_add_hyst[nr]);
+			results[2] = LM75_TEMP_FROM_REG(data->temp_add[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+				data->temp_add_over[nr] =
+				    LM75_TEMP_TO_REG(results[0]);
+			w83627hf_write_value(client,
+					    nr ? W83781D_REG_TEMP3_OVER :
+					    W83781D_REG_TEMP2_OVER,
+					    data->temp_add_over[nr]);
+		}
+		if (*nrels_mag >= 2) {
+				data->temp_add_hyst[nr] =
+				    LM75_TEMP_TO_REG(results[1]);
+			w83627hf_write_value(client,
+					    nr ? W83781D_REG_TEMP3_HYST :
+					    W83781D_REG_TEMP2_HYST,
+					    data->temp_add_hyst[nr]);
+		}
+	}
+}
+
+void w83627hf_vid(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct w83627hf_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 3;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83627hf_update_client(client);
+		results[0] = vid_from_reg(data->vid, data->vrm);
+		*nrels_mag = 1;
+	}
+}
+
+void w83627hf_vrm(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct w83627hf_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = data->vrm;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1)
+			data->vrm = results[0];
+	}
+}
+
+void w83627hf_alarms(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results)
+{
+	struct w83627hf_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83627hf_update_client(client);
+		results[0] = data->alarms;
+		*nrels_mag = 1;
+	}
+}
+
+void w83627hf_beep(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct w83627hf_data *data = client->data;
+	int val;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83627hf_update_client(client);
+		results[0] = BEEP_ENABLE_FROM_REG(data->beep_enable);
+		results[1] = data->beeps;
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 2) {
+			data->beeps = BEEPS_TO_REG(results[1]);
+			w83627hf_write_value(client, W83781D_REG_BEEP_INTS1,
+					    data->beeps & 0xff);
+				w83627hf_write_value(client,
+						    W83781D_REG_BEEP_INTS3,
+						    ((data-> beeps) >> 16) &
+						      0xff);
+			val = (data->beeps >> 8) & 0x7f;
+		} else if (*nrels_mag >= 1)
+			val =
+			    w83627hf_read_value(client,
+					       W83781D_REG_BEEP_INTS2) &
+			    0x7f;
+		if (*nrels_mag >= 1) {
+			data->beep_enable = BEEP_ENABLE_TO_REG(results[0]);
+			w83627hf_write_value(client, W83781D_REG_BEEP_INTS2,
+					    val | data->beep_enable << 7);
+		}
+	}
+}
+
+/* w83697hf only has two fans */
+void w83627hf_fan_div(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	struct w83627hf_data *data = client->data;
+	int old, old2, old3 = 0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83627hf_update_client(client);
+		results[0] = DIV_FROM_REG(data->fan_div[0]);
+		results[1] = DIV_FROM_REG(data->fan_div[1]);
+		if (data->type == w83697hf) {
+			*nrels_mag = 2;
+		} else {
+			results[2] = DIV_FROM_REG(data->fan_div[2]);
+			*nrels_mag = 3;
+		}
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		old = w83627hf_read_value(client, W83781D_REG_VID_FANDIV);
+		/* w83627hf doesn't have extended divisor bits */
+			old3 =
+			    w83627hf_read_value(client, W83781D_REG_VBAT);
+		if (*nrels_mag >= 3 && data->type != w83697hf) {
+			data->fan_div[2] =
+			    DIV_TO_REG(results[2]);
+			old2 = w83627hf_read_value(client, W83781D_REG_PIN);
+			old2 =
+			    (old2 & 0x3f) | ((data->fan_div[2] & 0x03) << 6);
+			w83627hf_write_value(client, W83781D_REG_PIN, old2);
+				old3 =
+				    (old3 & 0x7f) |
+				    ((data->fan_div[2] & 0x04) << 5);
+		}
+		if (*nrels_mag >= 2) {
+			data->fan_div[1] =
+			    DIV_TO_REG(results[1]);
+			old =
+			    (old & 0x3f) | ((data->fan_div[1] & 0x03) << 6);
+				old3 =
+				    (old3 & 0xbf) |
+				    ((data->fan_div[1] & 0x04) << 4);
+		}
+		if (*nrels_mag >= 1) {
+			data->fan_div[0] =
+			    DIV_TO_REG(results[0]);
+			old =
+			    (old & 0xcf) | ((data->fan_div[0] & 0x03) << 4);
+			w83627hf_write_value(client, W83781D_REG_VID_FANDIV,
+					    old);
+				old3 =
+				    (old3 & 0xdf) |
+				    ((data->fan_div[0] & 0x04) << 3);
+				w83627hf_write_value(client,
+						    W83781D_REG_VBAT,
+						    old3);
+		}
+	}
+}
+
+/* we do not currently support disabling PWM with 2nd argument;
+  set first argument to 255 to disable */
+void w83627hf_pwm(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct w83627hf_data *data = client->data;
+	int nr = 1 + ctl_name - W83781D_SYSCTL_PWM1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83627hf_update_client(client);
+		results[0] = data->pwm[nr - 1];
+		results[1] = data->pwmenable[nr - 1];
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			if (data->type == w83627thf) {
+				/* bits 0-3 are reserved  in 627THF */
+				data->pwm[nr - 1] = PWM_TO_REG(results[0]) & 0xf0;
+				w83627hf_write_value(client,
+						     W836X7HF_REG_PWM(data->type, nr),
+						     data->pwm[nr - 1] |
+						     (w83627hf_read_value(client,
+						      W836X7HF_REG_PWM(data->type, nr)) & 0x0f));
+			} else {
+				data->pwm[nr - 1] = PWM_TO_REG(results[0]);
+				w83627hf_write_value(client,
+						     W836X7HF_REG_PWM(data->type, nr),
+						     data->pwm[nr - 1]);
+			}
+		}
+	}
+}
+
+void w83627hf_sens(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct w83627hf_data *data = client->data;
+	int nr = 1 + ctl_name - W83781D_SYSCTL_SENS1;
+	u8 tmp;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = data->sens[nr - 1];
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			switch (results[0]) {
+			case 1:	/* PII/Celeron diode */
+				tmp = w83627hf_read_value(client,
+						       W83781D_REG_SCFG1);
+				w83627hf_write_value(client,
+						    W83781D_REG_SCFG1,
+						    tmp | BIT_SCFG1[nr -
+								    1]);
+				tmp = w83627hf_read_value(client,
+						       W83781D_REG_SCFG2);
+				w83627hf_write_value(client,
+						    W83781D_REG_SCFG2,
+						    tmp | BIT_SCFG2[nr -
+								    1]);
+				data->sens[nr - 1] = results[0];
+				break;
+			case 2:	/* 3904 */
+				tmp = w83627hf_read_value(client,
+						       W83781D_REG_SCFG1);
+				w83627hf_write_value(client,
+						    W83781D_REG_SCFG1,
+						    tmp | BIT_SCFG1[nr -
+								    1]);
+				tmp = w83627hf_read_value(client,
+						       W83781D_REG_SCFG2);
+				w83627hf_write_value(client,
+						    W83781D_REG_SCFG2,
+						    tmp & ~BIT_SCFG2[nr -
+								     1]);
+				data->sens[nr - 1] = results[0];
+				break;
+			case W83781D_DEFAULT_BETA:	/* thermistor */
+				tmp = w83627hf_read_value(client,
+						       W83781D_REG_SCFG1);
+				w83627hf_write_value(client,
+						    W83781D_REG_SCFG1,
+						    tmp & ~BIT_SCFG1[nr -
+								     1]);
+				data->sens[nr - 1] = results[0];
+				break;
+			default:
+				printk
+				    (KERN_ERR "w83627hf.o: Invalid sensor type %ld; must be 1, 2, or %d\n",
+				     results[0], W83781D_DEFAULT_BETA);
+				break;
+			}
+		}
+	}
+}
+
+static int __init sm_w83627hf_init(void)
+{
+	int addr;
+
+	printk(KERN_INFO "w83627hf.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	if (w83627hf_find(&addr)) {
+		printk("w83627hf.o: W83627/697 not detected, module not inserted.\n");
+		return -ENODEV;
+	}
+	normal_isa[0] = addr;
+
+	return i2c_add_driver(&w83627hf_driver);
+}
+
+static void __exit sm_w83627hf_exit(void)
+{
+	i2c_del_driver(&w83627hf_driver);
+}
+
+
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
+	      "Philip Edelbrock <phil@netroedge.com>, "
+	      "and Mark Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("W83627HF driver");
+MODULE_LICENSE("GPL");
+
+module_init(sm_w83627hf_init);
+module_exit(sm_w83627hf_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/w83781d.c linux-2.4.27-leo/drivers/sensors/w83781d.c
--- linux-2.4.27/drivers/sensors/w83781d.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/w83781d.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,1967 @@
+/*
+    w83781d.c - Part of lm_sensors, Linux kernel modules for hardware
+                monitoring
+    Copyright (c) 1998 - 2003  Frodo Looijaard <frodol@dds.nl>,
+    Philip Edelbrock <phil@netroedge.com>,
+    and Mark Studebaker <mdsxyz123@yahoo.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+    Supports following chips:
+
+    Chip	#vin	#fanin	#pwm	#temp	wchipid	vendid	i2c	ISA
+    as99127f	7	3	0	3	0x31	0x12c3	yes	no
+    as99127f rev.2 (type name = as99127f)	0x31	0x5ca3	yes	no
+    w83627hf	9	3	2	3	0x21	0x5ca3	yes	yes(LPC)
+    w83697hf	8	2	2	2	0x60	0x5ca3	no	yes(LPC)
+    w83781d	7	3	0	3	0x10-1	0x5ca3	yes	yes
+    w83782d	9	3	2-4	3	0x30	0x5ca3	yes	yes
+    w83783s	5-6	3	2	1-2	0x40	0x5ca3	yes	no
+    w83791d	10	5	5	3	0x71	0x5ca3	yes	no
+
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+#include <linux/sensors_vid.h>
+#include "lm75.h"
+
+/* RT Table support #defined so we can take it out if it gets bothersome */
+#define W83781D_RT 1
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x20, 0x2f, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { 0x0290, SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_7(w83781d, w83782d, w83783s, w83627hf, as99127f, w83697hf, w83791d);
+SENSORS_MODULE_PARM(force_subclients, "List of subclient addresses: " \
+                      "{bus, clientaddr, subclientaddr1, subclientaddr2}");
+
+static int init = 1;
+MODULE_PARM(init, "i");
+MODULE_PARM_DESC(init, "Set to zero to bypass chip initialization");
+
+/* Constants specified below */
+
+/* Length of ISA address segment */
+#define W83781D_EXTENT 8
+
+/* Where are the ISA address/data registers relative to the base address */
+#define W83781D_ADDR_REG_OFFSET 5
+#define W83781D_DATA_REG_OFFSET 6
+
+/* The W83781D registers */
+/* The W83782D registers for nr=7,8 are in bank 5 */
+#define W83781D_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \
+					   (0x554 + (((nr) - 7) * 2)))
+#define W83781D_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \
+					   (0x555 + (((nr) - 7) * 2)))
+#define W83781D_REG_IN(nr)     ((nr < 7) ? (0x20 + (nr)) : \
+					   (0x550 + (nr) - 7))
+
+#define W83791D_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \
+					   (0xb4 + (((nr) - 7) * 2)))
+#define W83791D_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \
+					   (0xb5 + (((nr) - 7) * 2)))
+#define W83791D_REG_IN(nr)     ((nr < 7) ? (0x20 + (nr)) : \
+					   (0xb0 + (nr) - 7))
+
+#define W83781D_REG_FAN_MIN(nr) ((nr < 4) ? (0x3a + (nr)) : \
+                                            (0xba + (nr) - 4))
+#define W83781D_REG_FAN(nr)     ((nr < 4) ? (0x27 + (nr)) : \
+                                            (0xbc + (nr) - 4))
+
+#define W83781D_REG_TEMP2 0x0150
+#define W83781D_REG_TEMP3 0x0250
+#define W83781D_REG_TEMP2_HYST 0x153
+#define W83781D_REG_TEMP3_HYST 0x253
+#define W83781D_REG_TEMP2_CONFIG 0x152
+#define W83781D_REG_TEMP3_CONFIG 0x252
+#define W83781D_REG_TEMP2_OVER 0x155
+#define W83781D_REG_TEMP3_OVER 0x255
+
+#define W83781D_REG_TEMP 0x27
+#define W83781D_REG_TEMP_OVER 0x39
+#define W83781D_REG_TEMP_HYST 0x3A
+#define W83781D_REG_BANK 0x4E
+
+#define W83781D_REG_CONFIG 0x40
+#define W83781D_REG_ALARM1 0x41
+#define W83781D_REG_ALARM2 0x42
+#define W83781D_REG_ALARM3 0x450	/* not on W83781D */
+
+#define W83781D_REG_IRQ 0x4C
+#define W83781D_REG_BEEP_CONFIG 0x4D
+#define W83781D_REG_BEEP_INTS1 0x56
+#define W83781D_REG_BEEP_INTS2 0x57
+#define W83781D_REG_BEEP_INTS3 0x453	/* not on W83781D */
+
+#define W83781D_REG_VID_FANDIV 0x47
+
+#define W83781D_REG_CHIPID 0x49
+#define W83781D_REG_WCHIPID 0x58
+#define W83781D_REG_CHIPMAN 0x4F
+#define W83781D_REG_PIN 0x4B
+
+/* 782D/783S only */
+#define W83781D_REG_VBAT 0x5D
+
+/* PWM 782D (1-4) and 783S (1-2) only */
+#define W83781D_REG_PWM1 0x5B	/* 782d and 783s/627hf datasheets disagree */
+				/* on which is which; */
+#define W83781D_REG_PWM2 0x5A	/* We follow the 782d convention here, */
+				/* However 782d is probably wrong. */
+#define W83781D_REG_PWM3 0x5E
+#define W83781D_REG_PWM4 0x5F
+#define W83781D_REG_PWMCLK12 0x5C
+#define W83781D_REG_PWMCLK34 0x45C
+
+#define W83791D_REG_PWM1 0x81
+#define W83791D_REG_PWM2 0x83
+#define W83791D_REG_PWM3 0x94
+
+#define W83627HF_REG_PWM1 0x01
+#define W83627HF_REG_PWM2 0x03
+#define W83627HF_REG_PWMCLK1 0x00
+#define W83627HF_REG_PWMCLK2 0x02
+
+static const u8 regpwm[] = { W83781D_REG_PWM1, W83781D_REG_PWM2,
+	W83781D_REG_PWM3, W83781D_REG_PWM4
+};
+
+static const u8 regpwm_w83791d[] = { W83791D_REG_PWM1, W83791D_REG_PWM2,
+                                   W83791D_REG_PWM3
+};
+        
+#define W83781D_REG_PWM(type, nr) (((type) == w83791d) ? \
+                                         regpwm_w83791d[(nr) - 1] : \
+                                   ((type) == w83697hf) ? \
+                                         (((nr) * 2) - 1) : \
+                                         regpwm[(nr) - 1])
+
+#define W83781D_REG_I2C_ADDR 0x48
+#define W83781D_REG_I2C_SUBADDR 0x4A
+
+/* The following are undocumented in the data sheets however we
+   received the information in an email from Winbond tech support */
+/* Sensor selection - not on 781d */
+#define W83781D_REG_SCFG1 0x5D
+static const u8 BIT_SCFG1[] = { 0x02, 0x04, 0x08 };
+#define W83781D_REG_SCFG2 0x59
+static const u8 BIT_SCFG2[] = { 0x10, 0x20, 0x40 };
+#define W83781D_DEFAULT_BETA 3435
+
+/* RT Table registers */
+#define W83781D_REG_RT_IDX 0x50
+#define W83781D_REG_RT_VAL 0x51
+
+/* Conversions. Rounding and limit checking is only done on the TO_REG
+   variants. Note that you should be a bit careful with which arguments
+   these macros are called: arguments may be evaluated more than once.
+   Fixing this is just not worth it. */
+#define IN_TO_REG(val)  (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255))
+#define IN_FROM_REG(val) (((val) * 16 + 5) / 10)
+
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+	if (rpm == 0)
+		return 255;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1,
+			     254);
+}
+
+#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div)))
+
+#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/10):\
+                                                 ((val)+5)/10),0,255))
+#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10)
+
+#define AS99127_TEMP_ADD_TO_REG(val) (SENSORS_LIMIT((((((val) + 2)*4)/10) \
+                                               << 7),0,0xffff))
+#define AS99127_TEMP_ADD_FROM_REG(val) ((((val) >> 7) * 10) / 4)
+
+#define ALARMS_FROM_REG(val) (val)
+#define PWM_FROM_REG(val) (val)
+#define PWM_TO_REG(val) (SENSORS_LIMIT((val),0,255))
+#define BEEPS_FROM_REG(val,type) ((type)==as99127f?(val)^0x7FFF:(val))
+#define BEEPS_TO_REG(val,type) ((type)==as99127f?(~(val))&0x7FFF:(val)&0xffffff)
+
+#define BEEP_ENABLE_TO_REG(val)   ((val)?1:0)
+#define BEEP_ENABLE_FROM_REG(val) ((val)?1:0)
+
+#define DIV_FROM_REG(val) (1 << (val))
+
+static inline u8 DIV_TO_REG(long val, enum chips type)
+{
+	int i;
+	val = SENSORS_LIMIT(val, 1,
+		((type == w83781d || type == as99127f) ? 8 : 128)) >> 1;
+	for (i = 0; i < 6; i++) {
+		if (val == 0)
+			break;
+		val >>= 1;
+	}
+	return ((u8) i);
+}
+
+/* There are some complications in a module like this. First off, W83781D chips
+   may be both present on the SMBus and the ISA bus, and we have to handle
+   those cases separately at some places. Second, there might be several
+   W83781D chips available (well, actually, that is probably never done; but
+   it is a clean illustration of how to handle a case like that). Finally,
+   a specific chip may be attached to *both* ISA and SMBus, and we would
+   not like to detect it double. Fortunately, in the case of the W83781D at
+   least, a register tells us what SMBus address we are on, so that helps
+   a bit - except if there could be more than one SMBus. Groan. No solution
+   for this yet. */
+
+/* This module may seem overly long and complicated. In fact, it is not so
+   bad. Quite a lot of bookkeeping is done. A real driver can often cut
+   some corners. */
+
+/* For each registered W83781D, we need to keep some data in memory. That
+   data is pointed to by w83781d_list[NR]->data. The structure itself is
+   dynamically allocated, at the same time when a new w83781d client is
+   allocated. */
+struct w83781d_data {
+	struct i2c_client client;
+	struct semaphore lock;
+	int sysctl_id;
+	enum chips type;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	struct i2c_client *lm75;	/* for secondary I2C addresses */
+	/* pointer to array of 2 subclients */
+
+	u8 in[10];		/* Register value - 8 & 9 for 782D and 791D only 10 for 791D */
+	u8 in_max[10];		/* Register value - 8 & 9 for 782D and 791D only 10 for 791D */
+	u8 in_min[10];		/* Register value - 8 & 9 for 782D and 791D only 10 for 791D */
+	u8 fan[5];		/* Register value - 4 & 5 for 791D only */
+	u8 fan_min[5];		/* Register value - 4 & 5 for 791D only */
+	u8 temp;
+	u8 temp_over;		/* Register value */
+	u8 temp_hyst;		/* Register value */
+	u16 temp_add[2];	/* Register value */
+	u16 temp_add_over[2];	/* Register value */
+	u16 temp_add_hyst[2];	/* Register value */
+	u8 fan_div[3];		/* Register encoding, shifted right */
+	u8 vid;			/* Register encoding, combined */
+	u32 alarms;		/* Register encoding, combined */
+	u32 beeps;		/* Register encoding, combined */
+	u8 beep_enable;		/* Boolean */
+	u8 pwm[4];		/* Register value */
+	u8 pwmenable[4];	/* bool */
+	u16 sens[3];		/* 782D/783S only.
+				   1 = pentium diode; 2 = 3904 diode;
+				   3000-5000 = thermistor beta.
+				   Default = 3435. 
+				   Other Betas unimplemented */
+#ifdef W83781D_RT
+	u8 rt[3][32];		/* Register value */
+#endif
+	u8 vrm;
+};
+
+
+static int w83781d_attach_adapter(struct i2c_adapter *adapter);
+static int w83781d_detect(struct i2c_adapter *adapter, int address,
+			  unsigned short flags, int kind);
+static int w83781d_detach_client(struct i2c_client *client);
+
+static int w83781d_read_value(struct i2c_client *client, u16 register);
+static int w83781d_write_value(struct i2c_client *client, u16 register,
+			       u16 value);
+static void w83781d_update_client(struct i2c_client *client);
+static void w83781d_init_client(struct i2c_client *client);
+
+
+static void w83781d_in(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+static void w83781d_fan(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void w83781d_temp(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void w83781d_temp_add(struct i2c_client *client, int operation,
+			     int ctl_name, int *nrels_mag, long *results);
+static void w83781d_vid(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void w83781d_vrm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void w83781d_alarms(struct i2c_client *client, int operation,
+			   int ctl_name, int *nrels_mag, long *results);
+static void w83781d_beep(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+static void w83781d_fan_div(struct i2c_client *client, int operation,
+			    int ctl_name, int *nrels_mag, long *results);
+static void w83781d_pwm(struct i2c_client *client, int operation,
+			int ctl_name, int *nrels_mag, long *results);
+static void w83781d_sens(struct i2c_client *client, int operation,
+			 int ctl_name, int *nrels_mag, long *results);
+#ifdef W83781D_RT
+static void w83781d_rt(struct i2c_client *client, int operation,
+		       int ctl_name, int *nrels_mag, long *results);
+#endif
+
+static int w83781d_id = 0;
+
+static struct i2c_driver w83781d_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "W83781D sensor driver",
+	.id		= I2C_DRIVERID_W83781D,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= w83781d_attach_adapter,
+	.detach_client	= w83781d_detach_client,
+};
+
+/* The /proc/sys entries */
+/* -- SENSORS SYSCTL START -- */
+
+#define W83781D_SYSCTL_IN0 1000	/* Volts * 100 */
+#define W83781D_SYSCTL_IN1 1001
+#define W83781D_SYSCTL_IN2 1002
+#define W83781D_SYSCTL_IN3 1003
+#define W83781D_SYSCTL_IN4 1004
+#define W83781D_SYSCTL_IN5 1005
+#define W83781D_SYSCTL_IN6 1006
+#define W83781D_SYSCTL_IN7 1007
+#define W83781D_SYSCTL_IN8 1008
+#define W83781D_SYSCTL_IN9 1009
+#define W83781D_SYSCTL_FAN1 1101	/* Rotations/min */
+#define W83781D_SYSCTL_FAN2 1102
+#define W83781D_SYSCTL_FAN3 1103
+#define W83781D_SYSCTL_FAN4 1104
+#define W83781D_SYSCTL_FAN5 1105
+
+#define W83781D_SYSCTL_TEMP1 1200	/* Degrees Celcius * 10 */
+#define W83781D_SYSCTL_TEMP2 1201	/* Degrees Celcius * 10 */
+#define W83781D_SYSCTL_TEMP3 1202	/* Degrees Celcius * 10 */
+#define W83781D_SYSCTL_VID 1300		/* Volts * 1000 */
+#define W83781D_SYSCTL_VRM 1301
+#define W83781D_SYSCTL_PWM1 1401
+#define W83781D_SYSCTL_PWM2 1402
+#define W83781D_SYSCTL_PWM3 1403
+#define W83781D_SYSCTL_PWM4 1404
+#define W83781D_SYSCTL_SENS1 1501	/* 1, 2, or Beta (3000-5000) */
+#define W83781D_SYSCTL_SENS2 1502
+#define W83781D_SYSCTL_SENS3 1503
+#define W83781D_SYSCTL_RT1   1601	/* 32-entry table */
+#define W83781D_SYSCTL_RT2   1602	/* 32-entry table */
+#define W83781D_SYSCTL_RT3   1603	/* 32-entry table */
+#define W83781D_SYSCTL_FAN_DIV 2000	/* 1, 2, 4 or 8 */
+#define W83781D_SYSCTL_ALARMS 2001	/* bitvector */
+#define W83781D_SYSCTL_BEEP 2002	/* bitvector */
+
+#define W83781D_ALARM_IN0 0x0001
+#define W83781D_ALARM_IN1 0x0002
+#define W83781D_ALARM_IN2 0x0004
+#define W83781D_ALARM_IN3 0x0008
+#define W83781D_ALARM_IN4 0x0100
+#define W83781D_ALARM_IN5 0x0200
+#define W83781D_ALARM_IN6 0x0400
+#define W83782D_ALARM_IN7 0x10000
+#define W83782D_ALARM_IN8 0x20000
+#define W83781D_ALARM_FAN1 0x0040
+#define W83781D_ALARM_FAN2 0x0080
+#define W83781D_ALARM_FAN3 0x0800
+#define W83781D_ALARM_TEMP1 0x0010
+#define W83781D_ALARM_TEMP23 0x0020	/* 781D only */
+#define W83781D_ALARM_TEMP2 0x0020	/* 782D/783S */
+#define W83781D_ALARM_TEMP3 0x2000	/* 782D only */
+#define W83781D_ALARM_CHAS 0x1000
+
+/* -- SENSORS SYSCTL END -- */
+
+/* These files are created for each detected chip. This is just a template;
+   though at first sight, you might think we could use a statically
+   allocated list, we need some way to get back to the parent - which
+   is done through one of the 'extra' fields which are initialized 
+   when a new copy is allocated. */
+
+/* just a guess - no datasheet */
+static ctl_table as99127f_dir_table_template[] = {
+	{W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_temp},
+	{W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_temp_add},
+	{W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_temp_add},
+	{W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_vid},
+	{W83781D_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_vrm},
+	{W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan_div},
+	{W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_alarms},
+	{W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_beep},
+	{0}
+};
+
+static ctl_table w83781d_dir_table_template[] = {
+	{W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_temp},
+	{W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_temp_add},
+	{W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_temp_add},
+	{W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_vid},
+	{W83781D_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_vrm},
+	{W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan_div},
+	{W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_alarms},
+	{W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_beep},
+#ifdef W83781D_RT
+	{W83781D_SYSCTL_RT1, "rt1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_rt},
+	{W83781D_SYSCTL_RT2, "rt2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_rt},
+	{W83781D_SYSCTL_RT3, "rt3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_rt},
+#endif
+	{0}
+};
+
+/* without pwm3-4 */
+static ctl_table w83782d_isa_dir_table_template[] = {
+	{W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_temp},
+	{W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_temp_add},
+	{W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_temp_add},
+	{W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_vid},
+	{W83781D_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_vrm},
+	{W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan_div},
+	{W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_alarms},
+	{W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_beep},
+	{W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_pwm},
+	{W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_pwm},
+	{W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_sens},
+	{W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_sens},
+	{W83781D_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_sens},
+	{0}
+};
+
+/* with pwm3-4 */
+static ctl_table w83782d_i2c_dir_table_template[] = {
+	{W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_temp},
+	{W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_temp_add},
+	{W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_temp_add},
+	{W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_vid},
+	{W83781D_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_vrm},
+	{W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan_div},
+	{W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_alarms},
+	{W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_beep},
+	{W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_pwm},
+	{W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_pwm},
+	{W83781D_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_pwm},
+	{W83781D_SYSCTL_PWM4, "pwm4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_pwm},
+	{W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_sens},
+	{W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_sens},
+	{W83781D_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_sens},
+	{0}
+};
+
+/* w83791D has 10 voltages 5 fans and 3 temps.  2 of the temps are on other 
+ devices. */
+static ctl_table w83791d_dir_table_template[] = {
+	{W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN9, "in9", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN4, "fan4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN5, "fan5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_temp},
+	{W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_temp_add},
+	{W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_temp_add},
+	{W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_vid},
+	{W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan_div},
+	{W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_alarms},
+	{W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_beep},
+	{W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_pwm},
+	{W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_pwm},
+	{W83781D_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_pwm},
+	{W83781D_SYSCTL_PWM4, "pwm4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_pwm},
+	{W83781D_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_vrm},
+	{0}
+};
+
+static ctl_table w83783s_dir_table_template[] = {
+	{W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	/* no in1 to maintain compatibility with 781d and 782d. */
+	{W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_temp},
+	{W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_temp_add},
+	{W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_vid},
+	{W83781D_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_vrm},
+	{W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan_div},
+	{W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_alarms},
+	{W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_beep},
+	{W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_pwm},
+	{W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_pwm},
+	{W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_sens},
+	{W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_sens},
+	{0}
+};
+
+/* similar to w83782d but no fan3, no vid */
+static ctl_table w83697hf_dir_table_template[] = {
+	{W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	/* no in1 to maintain compatibility with 781d and 782d. */
+	{W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_in},
+	{W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan},
+	{W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_temp},
+	{W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_temp_add},
+	{W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_fan_div},
+	{W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_alarms},
+	{W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_beep},
+	{W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_pwm},
+	{W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_pwm},
+	{W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_sens},
+	{W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real,
+	 &i2c_sysctl_real, NULL, &w83781d_sens},
+	{0}
+};
+
+
+/* This function is called when:
+     * w83781d_driver is inserted (when this module is loaded), for each
+       available adapter
+     * when a new adapter is inserted (and w83781d_driver is still present) */
+static int w83781d_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, w83781d_detect);
+}
+
+static int w83781d_detect(struct i2c_adapter *adapter, int address,
+                  unsigned short flags, int kind)
+{
+	int i, val1 = 0, val2, id;
+	struct i2c_client *new_client;
+	struct w83781d_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+	int is_isa = i2c_is_isa_adapter(adapter);
+	enum vendor { winbond, asus } vendid;
+
+	if (!is_isa
+	    && !i2c_check_functionality(adapter,
+					I2C_FUNC_SMBUS_BYTE_DATA)) goto
+		    ERROR0;
+
+       if (is_isa) {
+               if (!request_region(address, W83781D_EXTENT, "w83781d"))
+                       goto ERROR0;
+               release_region(address, W83781D_EXTENT);
+       }
+
+	/* Probe whether there is anything available on this address. Already
+	   done for SMBus clients */
+	if (kind < 0) {
+		if (is_isa) {
+
+#define REALLY_SLOW_IO
+			/* We need the timeouts for at least some LM78-like chips. But only
+			   if we read 'undefined' registers. */
+			i = inb_p(address + 1);
+			if (inb_p(address + 2) != i)
+				goto ERROR0;
+			if (inb_p(address + 3) != i)
+				goto ERROR0;
+			if (inb_p(address + 7) != i)
+				goto ERROR0;
+#undef REALLY_SLOW_IO
+
+			/* Let's just hope nothing breaks here */
+			i = inb_p(address + 5) & 0x7f;
+			outb_p(~i & 0x7f, address + 5);
+			if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) {
+				outb_p(i, address + 5);
+				return 0;
+			}
+		}
+	}
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet.
+	   But it allows us to access w83781d_{read,write}_value. */
+
+	if (!(data = kmalloc(sizeof(struct w83781d_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+
+	new_client = &data->client;
+	new_client->addr = address;
+	init_MUTEX(&data->lock);
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &w83781d_driver;
+	new_client->flags = 0;
+
+	/* Now, we do the remaining detection. */
+
+	/* The w8378?d may be stuck in some other bank than bank 0. This may
+	   make reading other information impossible. Specify a force=... or
+	   force_*=... parameter, and the Winbond will be reset to the right
+	   bank. */
+	if (kind < 0) {
+		if (w83781d_read_value(new_client, W83781D_REG_CONFIG) &
+		    0x80) {
+			err = -ENODEV;
+			goto ERROR1;
+		}
+		val1 = w83781d_read_value(new_client, W83781D_REG_BANK);
+		val2 = w83781d_read_value(new_client, W83781D_REG_CHIPMAN);
+		/* Check for Winbond or Asus ID if in bank 0 */
+		if ((!(val1 & 0x07)) &&
+		    (((!(val1 & 0x80)) && (val2 != 0xa3) && (val2 != 0xc3))
+		     || ((val1 & 0x80) && (val2 != 0x5c) && (val2 != 0x12)))) {
+			err = -ENODEV;
+			goto ERROR1;
+		}
+		/* If Winbond SMBus, check address at 0x48.
+		   Asus doesn't support, except for the as99127f rev.2 */
+		if ((!is_isa) && (((!(val1 & 0x80)) && (val2 == 0xa3)) ||
+				  ((val1 & 0x80) && (val2 == 0x5c)))) {
+			if (w83781d_read_value
+			    (new_client, W83781D_REG_I2C_ADDR) != address) {
+				err = -ENODEV;
+				goto ERROR1;
+			}
+		}
+	}
+
+	/* We have either had a force parameter, or we have already detected the
+	   Winbond. Put it now into bank 0 and Vendor ID High Byte */
+	w83781d_write_value(new_client, W83781D_REG_BANK,
+			    (w83781d_read_value(new_client,
+						W83781D_REG_BANK) & 0x78) |
+			    0x80);
+
+	/* Determine the chip type. */
+	if (kind <= 0) {
+		/* get vendor ID */
+		val2 = w83781d_read_value(new_client, W83781D_REG_CHIPMAN);
+		if (val2 == 0x5c)
+			vendid = winbond;
+		else if (val2 == 0x12)
+			vendid = asus;
+		else {
+			err = -ENODEV;
+			goto ERROR1;
+		}
+		val1 =
+		    w83781d_read_value(new_client, W83781D_REG_WCHIPID);
+		if ((val1 == 0x10 || val1 == 0x11) && vendid == winbond)
+			kind = w83781d;
+		else if (val1 == 0x30 && vendid == winbond)
+			kind = w83782d;
+		else if (val1 == 0x40 && vendid == winbond && !is_isa && address == 0x2d)
+			kind = w83783s;
+		else if (val1 == 0x21 && vendid == winbond)
+			kind = w83627hf;
+		else if (val1 == 0x71 && vendid == winbond && address >= 0x2c)
+			kind = w83791d;
+		else if (val1 == 0x31 && !is_isa && address >= 0x28)
+			kind = as99127f;
+		else if (val1 == 0x60 && vendid == winbond && is_isa)
+			kind = w83697hf;
+		else {
+			if (kind == 0)
+				printk
+				    (KERN_WARNING "w83781d.o: Ignoring 'force' parameter for unknown chip at"
+				     "adapter %d, address 0x%02x\n",
+				     i2c_adapter_id(adapter), address);
+			err = -EINVAL;
+			goto ERROR1;
+		}
+	}
+
+	if (kind == w83781d) {
+		type_name = "w83781d";
+		client_name = "W83781D chip";
+	} else if (kind == w83782d) {
+		type_name = "w83782d";
+		client_name = "W83782D chip";
+	} else if (kind == w83783s) {
+		type_name = "w83783s";
+		client_name = "W83783S chip";
+	} else if (kind == w83627hf) {
+		type_name = "w83627hf";
+		client_name = "W83627HF chip";
+	} else if (kind == as99127f) {
+		type_name = "as99127f";
+		client_name = "AS99127F chip";
+	} else if (kind == w83697hf) {
+		type_name = "w83697hf";
+		client_name = "W83697HF chip";
+        } else if (kind == w83791d) {
+ 		type_name = "w83791d";
+ 		client_name = "W83791D chip";
+	} else {
+#ifdef DEBUG
+		printk(KERN_ERR "w83781d.o: Internal error: unknown kind (%d)?!?",
+		       kind);
+#endif
+		err = -ENODEV;
+		goto ERROR1;
+	}
+
+	/* Reserve the ISA region */
+	if (is_isa)
+		request_region(address, W83781D_EXTENT, type_name);
+
+	/* Fill in the remaining client fields and put it into the global list */
+	strcpy(new_client->name, client_name);
+	data->type = kind;
+
+	new_client->id = w83781d_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto ERROR3;
+
+	/* attach secondary i2c lm75-like clients */
+	if (!is_isa) {
+		if (!(data->lm75 = kmalloc(2 * sizeof(struct i2c_client),
+					   GFP_KERNEL))) {
+			err = -ENOMEM;
+			goto ERROR4;
+		}
+		id = i2c_adapter_id(adapter);
+		if(force_subclients[0] == id && force_subclients[1] == address) {
+			for(i = 2; i <= 3; i++) {
+				if(force_subclients[i] < 0x48 ||
+				   force_subclients[i] > 0x4f) {
+					printk(KERN_ERR "w83781d.o: Invalid subclient address %d; must be 0x48-0x4f\n",
+					        force_subclients[i]);
+					err = -EINVAL;
+					goto ERROR5;
+				}
+			}
+			w83781d_write_value(new_client,
+			                    W83781D_REG_I2C_SUBADDR,
+			                    (force_subclients[2] & 0x07) |
+			                    ((force_subclients[3] & 0x07) <<4));
+			data->lm75[0].addr = force_subclients[2];
+		} else {
+			val1 = w83781d_read_value(new_client,
+					          W83781D_REG_I2C_SUBADDR);
+			data->lm75[0].addr = 0x48 + (val1 & 0x07);
+		}
+		if (kind != w83783s) {
+			if(force_subclients[0] == id &&
+			   force_subclients[1] == address) {
+				data->lm75[1].addr = force_subclients[3];
+			} else {
+				data->lm75[1].addr = 0x48 + ((val1 >> 4) & 0x07);
+			}
+			if(data->lm75[0].addr == data->lm75[1].addr) {
+				printk(KERN_ERR "w83781d.o: Duplicate addresses 0x%x for subclients.\n",
+					data->lm75[0].addr);
+				err = -EBUSY;
+				goto ERROR5;
+			}
+		}
+		if (kind == w83781d)
+			client_name = "W83781D subclient";
+		else if (kind == w83782d)
+			client_name = "W83782D subclient";
+		else if (kind == w83783s)
+			client_name = "W83783S subclient";
+		else if (kind == w83627hf)
+			client_name = "W83627HF subclient";
+		else if (kind == as99127f)
+			client_name = "AS99127F subclient";
+                else if (kind == w83791d)
+			client_name = "W83791D subclient";
+
+
+		for (i = 0; i <= 1; i++) {
+			data->lm75[i].data = NULL;	/* store all data in w83781d */
+			data->lm75[i].adapter = adapter;
+			data->lm75[i].driver = &w83781d_driver;
+			data->lm75[i].flags = 0;
+			strcpy(data->lm75[i].name, client_name);
+			data->lm75[i].id = w83781d_id++;
+			if ((err = i2c_attach_client(&(data->lm75[i])))) {
+				printk(KERN_ERR "w83781d.o: Subclient %d registration at address 0x%x failed.\n",
+				       i, data->lm75[i].addr);
+				if (i == 1)
+					goto ERROR6;
+				goto ERROR5;
+			}
+			if (kind == w83783s)
+				break;
+		}
+	} else {
+		data->lm75 = NULL;
+	}
+
+	/* Register a new directory entry with module sensors */
+	if ((i = i2c_register_entry(new_client,
+					type_name,
+					(kind == as99127f) ?
+					   as99127f_dir_table_template :
+					(kind == w83781d) ?
+					   w83781d_dir_table_template :
+					(kind == w83783s) ?
+					   w83783s_dir_table_template :
+					(kind == w83697hf) ?
+					   w83697hf_dir_table_template :
+                                        (kind == w83791d ) ?
+                                            w83791d_dir_table_template :
+                                        (is_isa || kind == w83627hf) ?
+					   w83782d_isa_dir_table_template :
+					   w83782d_i2c_dir_table_template)) < 0) {
+		err = i;
+		goto ERROR7;
+	}
+	data->sysctl_id = i;
+
+	/* Only PWM2 can be disabled */
+	for(i = 0; i < 4; i++)
+		data->pwmenable[i] = 1;
+
+	/* Initialize the chip */
+	w83781d_init_client(new_client);
+	return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+   very code-efficient in this case. */
+
+      ERROR7:
+	if (!is_isa)
+		i2c_detach_client(&
+				  (((struct
+				     w83781d_data *) (new_client->data))->
+				   lm75[1]));
+      ERROR6:
+	if (!is_isa)
+		i2c_detach_client(&
+				  (((struct
+				     w83781d_data *) (new_client->data))->
+				   lm75[0]));
+      ERROR5:
+	if (!is_isa)
+		kfree(((struct w83781d_data *) (new_client->data))->lm75);
+      ERROR4:
+	i2c_detach_client(new_client);
+      ERROR3:
+	if (is_isa)
+		release_region(address, W83781D_EXTENT);
+      ERROR1:
+	kfree(data);
+      ERROR0:
+	return err;
+}
+
+static int w83781d_detach_client(struct i2c_client *client)
+{
+	int err;
+	struct w83781d_data *data = client->data;
+
+	i2c_deregister_entry(data->sysctl_id);
+
+	if ((err = i2c_detach_client(client))) {
+		printk
+		    (KERN_ERR "w83781d.o: Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	if(i2c_is_isa_client(client)) {
+		release_region(client->addr, W83781D_EXTENT);
+	} else {
+		i2c_detach_client(&(data->lm75[0]));
+		if (data->type != w83783s)
+			i2c_detach_client(&(data->lm75[1]));
+		kfree(data->lm75);
+	}
+	kfree(data);
+
+	return 0;
+}
+
+/* The SMBus locks itself, usually, but nothing may access the Winbond between
+   bank switches. ISA access must always be locked explicitly! 
+   We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks,
+   would slow down the W83781D access and should not be necessary. 
+   There are some ugly typecasts here, but the good news is - they should
+   nowhere else be necessary! */
+static int w83781d_read_value(struct i2c_client *client, u16 reg)
+{
+	int res, word_sized, bank;
+	struct i2c_client *cl;
+
+	down(&(((struct w83781d_data *) (client->data))->lock));
+	if (i2c_is_isa_client(client)) {
+		word_sized = (((reg & 0xff00) == 0x100)
+			      || ((reg & 0xff00) == 0x200))
+		    && (((reg & 0x00ff) == 0x50)
+			|| ((reg & 0x00ff) == 0x53)
+			|| ((reg & 0x00ff) == 0x55));
+		if (reg & 0xff00) {
+			outb_p(W83781D_REG_BANK,
+			       client->addr + W83781D_ADDR_REG_OFFSET);
+			outb_p(reg >> 8,
+			       client->addr + W83781D_DATA_REG_OFFSET);
+		}
+		outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET);
+		res = inb_p(client->addr + W83781D_DATA_REG_OFFSET);
+		if (word_sized) {
+			outb_p((reg & 0xff) + 1,
+			       client->addr + W83781D_ADDR_REG_OFFSET);
+			res =
+			    (res << 8) + inb_p(client->addr +
+					       W83781D_DATA_REG_OFFSET);
+		}
+		if (reg & 0xff00) {
+			outb_p(W83781D_REG_BANK,
+			       client->addr + W83781D_ADDR_REG_OFFSET);
+			outb_p(0, client->addr + W83781D_DATA_REG_OFFSET);
+		}
+	} else {
+		bank = (reg >> 8) & 0x0f;
+		if (bank > 2)
+			/* switch banks */
+			i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
+						  bank);
+		if (bank == 0 || bank > 2) {
+			res = i2c_smbus_read_byte_data(client, reg & 0xff);
+		} else {
+			/* switch to subclient */
+			cl =
+			    &(((struct w83781d_data *) (client->data))->
+			      lm75[bank - 1]);
+			/* convert from ISA to LM75 I2C addresses */
+			switch (reg & 0xff) {
+			case 0x50: /* TEMP */
+				res = swab16(i2c_smbus_read_word_data(cl, 0));
+				break;
+			case 0x52: /* CONFIG */
+				res = i2c_smbus_read_byte_data(cl, 1);
+				break;
+			case 0x53: /* HYST */
+				res = swab16(i2c_smbus_read_word_data(cl, 2));
+				break;
+			case 0x55: /* OVER */
+			default:
+				res = swab16(i2c_smbus_read_word_data(cl, 3));
+				break;
+			}
+		}
+		if (bank > 2)
+			i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
+						  0);
+	}
+	up(&(((struct w83781d_data *) (client->data))->lock));
+	return res;
+}
+
+static int w83781d_write_value(struct i2c_client *client, u16 reg, u16 value)
+{
+	int word_sized, bank;
+	struct i2c_client *cl;
+
+	down(&(((struct w83781d_data *) (client->data))->lock));
+	if (i2c_is_isa_client(client)) {
+		word_sized = (((reg & 0xff00) == 0x100)
+			      || ((reg & 0xff00) == 0x200))
+		    && (((reg & 0x00ff) == 0x53)
+			|| ((reg & 0x00ff) == 0x55));
+		if (reg & 0xff00) {
+			outb_p(W83781D_REG_BANK,
+			       client->addr + W83781D_ADDR_REG_OFFSET);
+			outb_p(reg >> 8,
+			       client->addr + W83781D_DATA_REG_OFFSET);
+		}
+		outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET);
+		if (word_sized) {
+			outb_p(value >> 8,
+			       client->addr + W83781D_DATA_REG_OFFSET);
+			outb_p((reg & 0xff) + 1,
+			       client->addr + W83781D_ADDR_REG_OFFSET);
+		}
+		outb_p(value & 0xff,
+		       client->addr + W83781D_DATA_REG_OFFSET);
+		if (reg & 0xff00) {
+			outb_p(W83781D_REG_BANK,
+			       client->addr + W83781D_ADDR_REG_OFFSET);
+			outb_p(0, client->addr + W83781D_DATA_REG_OFFSET);
+		}
+	} else {
+		bank = (reg >> 8) & 0x0f;
+		if (bank > 2)
+			/* switch banks */
+			i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
+						  bank);
+		if (bank == 0 || bank > 2) {
+			i2c_smbus_write_byte_data(client, reg & 0xff,
+						  value & 0xff);
+		} else {
+			/* switch to subclient */
+			cl = &(((struct w83781d_data *) (client->data))->
+			      lm75[bank - 1]);
+			/* convert from ISA to LM75 I2C addresses */
+			switch (reg & 0xff) {
+			case 0x52: /* CONFIG */
+				i2c_smbus_write_byte_data(cl, 1, value & 0xff);
+				break;
+			case 0x53: /* HYST */
+				i2c_smbus_write_word_data(cl, 2, swab16(value));
+				break;
+			case 0x55: /* OVER */
+				i2c_smbus_write_word_data(cl, 3, swab16(value));
+				break;
+			}
+		}
+		if (bank > 2)
+			i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
+						  0);
+	}
+	up(&(((struct w83781d_data *) (client->data))->lock));
+	return 0;
+}
+
+/* Called when we have found a new W83781D. It should set limits, etc. */
+static void w83781d_init_client(struct i2c_client *client)
+{
+	struct w83781d_data *data = client->data;
+	int vid = 0, i, p;
+	int type = data->type;
+	u8 tmp;
+
+	if(init && type != as99127f) { /* this resets registers we don't have
+			                  documentation for on the as99127f */
+		/* save these registers */
+		i = w83781d_read_value(client, W83781D_REG_BEEP_CONFIG);
+		p = w83781d_read_value(client, W83781D_REG_PWMCLK12);
+		/* Reset all except Watchdog values and last conversion values
+		   This sets fan-divs to 2, among others */
+		w83781d_write_value(client, W83781D_REG_CONFIG, 0x80);
+		/* Restore the registers and disable power-on abnormal beep.
+		   This saves FAN 1/2/3 input/output values set by BIOS. */
+		w83781d_write_value(client, W83781D_REG_BEEP_CONFIG, i | 0x80);
+		w83781d_write_value(client, W83781D_REG_PWMCLK12, p);
+		/* Disable master beep-enable (reset turns it on).
+		   Individual beeps should be reset to off but for some reason
+		   disabling this bit helps some people not get beeped */
+		w83781d_write_value(client, W83781D_REG_BEEP_INTS2, 0);
+	}
+
+	if (type != w83697hf) {
+		vid = w83781d_read_value(client, W83781D_REG_VID_FANDIV) & 0x0f;
+		vid |=
+		    (w83781d_read_value(client, W83781D_REG_CHIPID) & 0x01) << 4;
+                if (type == w83791d) {
+                        data->vrm = 92;
+                } else {
+		data->vrm = DEFAULT_VRM;
+                }
+		vid = vid_from_reg(vid, data->vrm);
+	}
+
+	if ((type != w83781d) && (type != as99127f)) {
+		tmp = w83781d_read_value(client, W83781D_REG_SCFG1);
+		for (i = 1; i <= 3; i++) {
+			if (!(tmp & BIT_SCFG1[i - 1])) {
+				data->sens[i - 1] = W83781D_DEFAULT_BETA;
+			} else {
+				if (w83781d_read_value
+				    (client,
+				     W83781D_REG_SCFG2) & BIT_SCFG2[i - 1])
+					data->sens[i - 1] = 1;
+				else
+					data->sens[i - 1] = 2;
+			}
+			if ((type == w83783s || type == w83697hf) && (i == 2))
+				break;
+		}
+	}
+#ifdef W83781D_RT
+/*
+   Fill up the RT Tables.
+   We assume that they are 32 bytes long, in order for temp 1-3.
+   Data sheet documentation is sparse.
+   We also assume that it is only for the 781D although I suspect
+   that the others support it as well....
+*/
+
+	if (init && type == w83781d) {
+		u16 k = 0;
+/*
+    Auto-indexing doesn't seem to work...
+    w83781d_write_value(client,W83781D_REG_RT_IDX,0);
+*/
+		for (i = 0; i < 3; i++) {
+			int j;
+			for (j = 0; j < 32; j++) {
+				w83781d_write_value(client,
+						    W83781D_REG_RT_IDX,
+						    k++);
+				data->rt[i][j] =
+				    w83781d_read_value(client,
+						       W83781D_REG_RT_VAL);
+			}
+		}
+	}
+#endif				/* W83781D_RT */
+
+	if(init) {
+		w83781d_write_value(client, W83781D_REG_TEMP2_CONFIG, 0x00);
+		if (type != w83783s && type != w83697hf) {
+			w83781d_write_value(client, W83781D_REG_TEMP3_CONFIG,
+					    0x00);
+		}
+		if (type != w83781d) {
+			/* enable comparator mode for temp2 and temp3 so
+		           alarm indication will work correctly */
+			i = w83781d_read_value(client, W83781D_REG_IRQ);
+			if (!(i & 0x40))
+				w83781d_write_value(client, W83781D_REG_IRQ,
+						    i | 0x40);
+		}
+	}
+
+	/* Start monitoring */
+	w83781d_write_value(client, W83781D_REG_CONFIG,
+			    (w83781d_read_value(client,
+						W83781D_REG_CONFIG) & 0xf7)
+			    | 0x01);
+}
+
+static void w83781d_update_client(struct i2c_client *client)
+{
+       struct w83781d_data *data = client->data;
+       int i;
+
+       down(&data->update_lock);
+
+       if (time_after(jiffies - data->last_updated, HZ + HZ / 2) ||
+           time_before(jiffies, data->last_updated) || !data->valid) {
+               pr_debug(KERN_DEBUG "Starting device update\n");
+
+               for (i = 0; i <= 9; i++) {
+                       if ((data->type == w83783s || data->type == w83697hf)
+                           && (i == 1))
+                               continue;       /* 783S has no in1 */
+                       if (data->type == w83791d) {
+                                data->in[i] =
+                                        w83781d_read_value(client, W83791D_REG_IN(i));
+                                data->in_min[i] =
+                                        w83781d_read_value(client,
+                                                           W83791D_REG_IN_MIN(i));
+                                data->in_max[i] =
+                                        w83781d_read_value(client,
+                                                           W83791D_REG_IN_MAX(i));
+                       } else {
+                       data->in[i] =
+                           w83781d_read_value(client, W83781D_REG_IN(i));
+                       data->in_min[i] =
+                           w83781d_read_value(client,
+                                              W83781D_REG_IN_MIN(i));
+                       data->in_max[i] =
+                           w83781d_read_value(client,
+                                              W83781D_REG_IN_MAX(i));
+                       }
+                       if ((data->type != w83782d) && (data->type != w83697hf)
+                           && (data->type != w83627hf) && (i == 6)
+                           && (data->type != w83791d))
+                               break;
+
+                       if (data->type != w83791d && i == 8) 
+                         break;
+               }
+               for (i = 1; i <= 5; i++) {
+                       data->fan[i - 1] =
+                           w83781d_read_value(client, W83781D_REG_FAN(i));
+                       data->fan_min[i - 1] =
+                           w83781d_read_value(client,
+                                              W83781D_REG_FAN_MIN(i));
+                       if (data->type != w83791d && i == 3) break;
+               }
+               if (data->type != w83781d && data->type != as99127f) {
+                       for (i = 1; i <= 4; i++) {
+                               data->pwm[i - 1] =
+                                   w83781d_read_value(client,
+				             W83781D_REG_PWM(data->type, i));
+                               if (((data->type == w83783s)
+                                    || (data->type == w83627hf)
+                                    || (data->type == w83697hf)
+                                    || ((data->type == w83782d)
+                                       && i2c_is_isa_client(client)))
+                                   && i == 2)
+                                       break;
+                       }
+			/* Only PWM2 can be disabled */
+			data->pwmenable[1] = (w83781d_read_value(client,
+					      W83781D_REG_PWMCLK12) & 0x08) >> 3;
+               }
+
+               data->temp = w83781d_read_value(client, W83781D_REG_TEMP);
+               data->temp_over =
+                   w83781d_read_value(client, W83781D_REG_TEMP_OVER);
+               data->temp_hyst =
+                   w83781d_read_value(client, W83781D_REG_TEMP_HYST);
+               data->temp_add[0] =
+                   w83781d_read_value(client, W83781D_REG_TEMP2);
+               data->temp_add_over[0] =
+                   w83781d_read_value(client, W83781D_REG_TEMP2_OVER);
+               data->temp_add_hyst[0] =
+                   w83781d_read_value(client, W83781D_REG_TEMP2_HYST);
+               if (data->type != w83783s && data->type != w83697hf) {
+                       data->temp_add[1] =
+                           w83781d_read_value(client, W83781D_REG_TEMP3);
+                       data->temp_add_over[1] =
+                           w83781d_read_value(client, W83781D_REG_TEMP3_OVER);
+                       data->temp_add_hyst[1] =
+                           w83781d_read_value(client, W83781D_REG_TEMP3_HYST);
+               }
+               i = w83781d_read_value(client, W83781D_REG_VID_FANDIV);
+               if (data->type != w83697hf) {
+                       data->vid = i & 0x0f;
+                       data->vid |=
+                           (w83781d_read_value(client, W83781D_REG_CHIPID) & 0x01)
+                           << 4;
+               }
+               data->fan_div[0] = (i >> 4) & 0x03;
+               data->fan_div[1] = (i >> 6) & 0x03;
+               if (data->type != w83697hf) {
+                       data->fan_div[2] = (w83781d_read_value(client,
+                                              W83781D_REG_PIN) >> 6) & 0x03;
+               }
+               if ((data->type != w83781d) && (data->type != as99127f)) {
+                       i = w83781d_read_value(client, W83781D_REG_VBAT);
+                       data->fan_div[0] |= (i >> 3) & 0x04;
+                       data->fan_div[1] |= (i >> 4) & 0x04;
+                       if (data->type != w83697hf)
+                               data->fan_div[2] |= (i >> 5) & 0x04;
+               }
+               data->alarms =
+                   w83781d_read_value(client,
+                                      W83781D_REG_ALARM1) +
+                   (w83781d_read_value(client, W83781D_REG_ALARM2) << 8);
+               if ((data->type == w83782d) || (data->type == w83627hf) ||
+		   (data->type == w83697hf)) {
+                       data->alarms |=
+                           w83781d_read_value(client,
+                                              W83781D_REG_ALARM3) << 16;
+               }
+               i = w83781d_read_value(client, W83781D_REG_BEEP_INTS2);
+               data->beep_enable = i >> 7;
+               data->beeps = ((i & 0x7f) << 8) +
+                   w83781d_read_value(client, W83781D_REG_BEEP_INTS1);
+               if ((data->type != w83781d) && (data->type != as99127f)
+                   && (data->type != w83791d)) {
+                       data->beeps |=
+                           w83781d_read_value(client,
+                                              W83781D_REG_BEEP_INTS3) << 16;
+               }
+               data->last_updated = jiffies;
+               data->valid = 1;
+       }
+
+	up(&data->update_lock);
+}
+
+
+/* The next few functions are the call-back functions of the /proc/sys and
+   sysctl files. Which function is used is defined in the ctl_table in
+   the extra1 field.
+   Each function must return the magnitude (power of 10 to divide the date
+   with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must
+   put a maximum of *nrels elements in results reflecting the data of this
+   file, and set *nrels to the number it actually put in it, if operation==
+   SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from
+   results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE.
+   Note that on SENSORS_PROC_REAL_READ, I do not check whether results is
+   large enough (by checking the incoming value of *nrels). This is not very
+   good practice, but as long as you put less than about 5 values in results,
+   you can assume it is large enough. */
+static void w83781d_in(struct i2c_client *client, int operation, int ctl_name,
+               int *nrels_mag, long *results)
+{
+	struct w83781d_data *data = client->data;
+	int nr = ctl_name - W83781D_SYSCTL_IN0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 2;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83781d_update_client(client);
+		results[0] = IN_FROM_REG(data->in_min[nr]);
+		results[1] = IN_FROM_REG(data->in_max[nr]);
+		results[2] = IN_FROM_REG(data->in[nr]);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->in_min[nr] = IN_TO_REG(results[0]);
+			w83781d_write_value(client, W83781D_REG_IN_MIN(nr),
+					    data->in_min[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			data->in_max[nr] = IN_TO_REG(results[1]);
+			w83781d_write_value(client, W83781D_REG_IN_MAX(nr),
+					    data->in_max[nr]);
+		}
+	}
+}
+
+void w83781d_fan(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct w83781d_data *data = client->data;
+	int nr = ctl_name - W83781D_SYSCTL_FAN1 + 1;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83781d_update_client(client);
+		results[0] = FAN_FROM_REG(data->fan_min[nr - 1],
+				  DIV_FROM_REG(data->fan_div[nr - 1]));
+		results[1] = FAN_FROM_REG(data->fan[nr - 1],
+			          DIV_FROM_REG(data->fan_div[nr - 1]));
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->fan_min[nr - 1] =
+			     FAN_TO_REG(results[0],
+			            DIV_FROM_REG(data->fan_div[nr-1]));
+			w83781d_write_value(client,
+					    W83781D_REG_FAN_MIN(nr),
+					    data->fan_min[nr - 1]);
+		}
+	}
+}
+
+void w83781d_temp(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct w83781d_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83781d_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_over);
+		results[1] = TEMP_FROM_REG(data->temp_hyst);
+		results[2] = TEMP_FROM_REG(data->temp);
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->temp_over = TEMP_TO_REG(results[0]);
+			w83781d_write_value(client, W83781D_REG_TEMP_OVER,
+					    data->temp_over);
+		}
+		if (*nrels_mag >= 2) {
+			data->temp_hyst = TEMP_TO_REG(results[1]);
+			w83781d_write_value(client, W83781D_REG_TEMP_HYST,
+					    data->temp_hyst);
+		}
+	}
+}
+
+void w83781d_temp_add(struct i2c_client *client, int operation,
+		      int ctl_name, int *nrels_mag, long *results)
+{
+	struct w83781d_data *data = client->data;
+	int nr = ctl_name - W83781D_SYSCTL_TEMP2;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83781d_update_client(client);
+		if (data->type == as99127f) {
+			results[0] =
+			    AS99127_TEMP_ADD_FROM_REG(data->
+						      temp_add_over[nr]);
+			results[1] =
+			    AS99127_TEMP_ADD_FROM_REG(data->
+						      temp_add_hyst[nr]);
+			results[2] =
+			    AS99127_TEMP_ADD_FROM_REG(data->temp_add[nr]);
+		} else {
+			results[0] =
+			    LM75_TEMP_FROM_REG(data->temp_add_over[nr]);
+			results[1] =
+			    LM75_TEMP_FROM_REG(data->temp_add_hyst[nr]);
+			results[2] = LM75_TEMP_FROM_REG(data->temp_add[nr]);
+		}
+		*nrels_mag = 3;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			if (data->type == as99127f)
+				data->temp_add_over[nr] =
+				    AS99127_TEMP_ADD_TO_REG(results[0]);
+			else
+				data->temp_add_over[nr] =
+				    LM75_TEMP_TO_REG(results[0]);
+			w83781d_write_value(client,
+					    nr ? W83781D_REG_TEMP3_OVER :
+					    W83781D_REG_TEMP2_OVER,
+					    data->temp_add_over[nr]);
+		}
+		if (*nrels_mag >= 2) {
+			if (data->type == as99127f)
+				data->temp_add_hyst[nr] =
+				    AS99127_TEMP_ADD_TO_REG(results[1]);
+			else
+				data->temp_add_hyst[nr] =
+				    LM75_TEMP_TO_REG(results[1]);
+			w83781d_write_value(client,
+					    nr ? W83781D_REG_TEMP3_HYST :
+					    W83781D_REG_TEMP2_HYST,
+					    data->temp_add_hyst[nr]);
+		}
+	}
+}
+
+
+void w83781d_vid(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct w83781d_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 3;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83781d_update_client(client);
+		results[0] = vid_from_reg(data->vid, data->vrm);
+		*nrels_mag = 1;
+	}
+}
+
+void w83781d_vrm(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct w83781d_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 1;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = data->vrm;
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1)
+			data->vrm = results[0];
+	}
+}
+
+void w83781d_alarms(struct i2c_client *client, int operation, int ctl_name,
+		    int *nrels_mag, long *results)
+{
+	struct w83781d_data *data = client->data;
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83781d_update_client(client);
+		results[0] = ALARMS_FROM_REG(data->alarms);
+		*nrels_mag = 1;
+	}
+}
+
+void w83781d_beep(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct w83781d_data *data = client->data;
+	int val;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83781d_update_client(client);
+		results[0] = BEEP_ENABLE_FROM_REG(data->beep_enable);
+		results[1] = BEEPS_FROM_REG(data->beeps, data->type);
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 2) {
+			data->beeps = BEEPS_TO_REG(results[1], data->type);
+			w83781d_write_value(client, W83781D_REG_BEEP_INTS1,
+					    data->beeps & 0xff);
+			if ((data->type != w83781d) &&
+			    (data->type != as99127f)) {
+				w83781d_write_value(client,
+						    W83781D_REG_BEEP_INTS3,
+						    ((data-> beeps) >> 16) &
+						      0xff);
+			}
+			val = (data->beeps >> 8) & 0x7f;
+		} else if (*nrels_mag >= 1)
+			val =
+			    w83781d_read_value(client,
+					       W83781D_REG_BEEP_INTS2) &
+			    0x7f;
+		if (*nrels_mag >= 1) {
+			data->beep_enable = BEEP_ENABLE_TO_REG(results[0]);
+			w83781d_write_value(client, W83781D_REG_BEEP_INTS2,
+					    val | data->beep_enable << 7);
+		}
+	}
+}
+
+/* w83697hf only has two fans */
+void w83781d_fan_div(struct i2c_client *client, int operation,
+		     int ctl_name, int *nrels_mag, long *results)
+{
+	struct w83781d_data *data = client->data;
+	int old, old2, old3 = 0;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83781d_update_client(client);
+		results[0] = DIV_FROM_REG(data->fan_div[0]);
+		results[1] = DIV_FROM_REG(data->fan_div[1]);
+		if (data->type == w83697hf) {
+			*nrels_mag = 2;
+		} else {
+			results[2] = DIV_FROM_REG(data->fan_div[2]);
+			*nrels_mag = 3;
+		}
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		old = w83781d_read_value(client, W83781D_REG_VID_FANDIV);
+		/* w83781d and as99127f don't have extended divisor bits */
+		if ((data->type != w83781d) && data->type != as99127f) {
+			old3 =
+			    w83781d_read_value(client, W83781D_REG_VBAT);
+		}
+		if (*nrels_mag >= 3 && data->type != w83697hf) {
+			data->fan_div[2] =
+			    DIV_TO_REG(results[2], data->type);
+			old2 = w83781d_read_value(client, W83781D_REG_PIN);
+			old2 =
+			    (old2 & 0x3f) | ((data->fan_div[2] & 0x03) << 6);
+			w83781d_write_value(client, W83781D_REG_PIN, old2);
+			if ((data->type != w83781d) &&
+			    (data->type != as99127f)) {
+				old3 =
+				    (old3 & 0x7f) |
+				    ((data->fan_div[2] & 0x04) << 5);
+			}
+		}
+		if (*nrels_mag >= 2) {
+			data->fan_div[1] =
+			    DIV_TO_REG(results[1], data->type);
+			old =
+			    (old & 0x3f) | ((data->fan_div[1] & 0x03) << 6);
+			if ((data->type != w83781d) &&
+			    (data->type != as99127f)) {
+				old3 =
+				    (old3 & 0xbf) |
+				    ((data->fan_div[1] & 0x04) << 4);
+			}
+		}
+		if (*nrels_mag >= 1) {
+			data->fan_div[0] =
+			    DIV_TO_REG(results[0], data->type);
+			old =
+			    (old & 0xcf) | ((data->fan_div[0] & 0x03) << 4);
+			w83781d_write_value(client, W83781D_REG_VID_FANDIV,
+					    old);
+			if ((data->type != w83781d) &&
+			    (data->type != as99127f)) {
+				old3 =
+				    (old3 & 0xdf) |
+				    ((data->fan_div[0] & 0x04) << 3);
+				w83781d_write_value(client,
+						    W83781D_REG_VBAT,
+						    old3);
+			}
+		}
+	}
+}
+
+void w83781d_pwm(struct i2c_client *client, int operation, int ctl_name,
+		 int *nrels_mag, long *results)
+{
+	struct w83781d_data *data = client->data;
+	int nr = 1 + ctl_name - W83781D_SYSCTL_PWM1;
+	int j, k;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		w83781d_update_client(client);
+		results[0] = PWM_FROM_REG(data->pwm[nr - 1]);
+		results[1] = data->pwmenable[nr - 1];
+		*nrels_mag = 2;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			data->pwm[nr - 1] = PWM_TO_REG(results[0]);
+			w83781d_write_value(client,
+			                    W83781D_REG_PWM(data->type, nr),
+					    data->pwm[nr - 1]);
+		}
+		/* only PWM2 can be enabled/disabled */
+		if (*nrels_mag >= 2 && nr == 2) {
+			j = w83781d_read_value(client, W83781D_REG_PWMCLK12);
+			k = w83781d_read_value(client, W83781D_REG_BEEP_CONFIG);
+			if(results[1]) {
+				if(!(j & 0x08))
+					w83781d_write_value(client,
+					     W83781D_REG_PWMCLK12, j | 0x08);
+				if(k & 0x10)
+					w83781d_write_value(client,
+					     W83781D_REG_BEEP_CONFIG, k & 0xef);
+				data->pwmenable[1] = 1;
+			} else {
+				if(j & 0x08)
+					w83781d_write_value(client,
+					     W83781D_REG_PWMCLK12, j & 0xf7);
+				if(!(k & 0x10))
+					w83781d_write_value(client,
+					     W83781D_REG_BEEP_CONFIG, j | 0x10);
+				data->pwmenable[1] = 0;
+			}
+		}
+	}
+}
+
+void w83781d_sens(struct i2c_client *client, int operation, int ctl_name,
+		  int *nrels_mag, long *results)
+{
+	struct w83781d_data *data = client->data;
+	int nr = 1 + ctl_name - W83781D_SYSCTL_SENS1;
+	u8 tmp;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		results[0] = data->sens[nr - 1];
+		*nrels_mag = 1;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag >= 1) {
+			switch (results[0]) {
+			case 1:	/* PII/Celeron diode */
+				tmp = w83781d_read_value(client,
+						       W83781D_REG_SCFG1);
+				w83781d_write_value(client,
+						    W83781D_REG_SCFG1,
+						    tmp | BIT_SCFG1[nr -
+								    1]);
+				tmp = w83781d_read_value(client,
+						       W83781D_REG_SCFG2);
+				w83781d_write_value(client,
+						    W83781D_REG_SCFG2,
+						    tmp | BIT_SCFG2[nr -
+								    1]);
+				data->sens[nr - 1] = results[0];
+				break;
+			case 2:	/* 3904 */
+				tmp = w83781d_read_value(client,
+						       W83781D_REG_SCFG1);
+				w83781d_write_value(client,
+						    W83781D_REG_SCFG1,
+						    tmp | BIT_SCFG1[nr -
+								    1]);
+				tmp = w83781d_read_value(client,
+						       W83781D_REG_SCFG2);
+				w83781d_write_value(client,
+						    W83781D_REG_SCFG2,
+						    tmp & ~BIT_SCFG2[nr -
+								     1]);
+				data->sens[nr - 1] = results[0];
+				break;
+			case W83781D_DEFAULT_BETA:	/* thermistor */
+				tmp = w83781d_read_value(client,
+						       W83781D_REG_SCFG1);
+				w83781d_write_value(client,
+						    W83781D_REG_SCFG1,
+						    tmp & ~BIT_SCFG1[nr -
+								     1]);
+				data->sens[nr - 1] = results[0];
+				break;
+			default:
+				printk
+				    (KERN_ERR "w83781d.o: Invalid sensor type %ld; must be 1, 2, or %d\n",
+				     results[0], W83781D_DEFAULT_BETA);
+				break;
+			}
+		}
+	}
+}
+
+#ifdef W83781D_RT
+static void w83781d_rt(struct i2c_client *client, int operation, int ctl_name,
+               int *nrels_mag, long *results)
+{
+	struct w83781d_data *data = client->data;
+	int nr = 1 + ctl_name - W83781D_SYSCTL_RT1;
+	int i;
+
+	if (operation == SENSORS_PROC_REAL_INFO)
+		*nrels_mag = 0;
+	else if (operation == SENSORS_PROC_REAL_READ) {
+		for (i = 0; i < 32; i++) {
+			results[i] = data->rt[nr - 1][i];
+		}
+		*nrels_mag = 32;
+	} else if (operation == SENSORS_PROC_REAL_WRITE) {
+		if (*nrels_mag > 32)
+			*nrels_mag = 32;
+		for (i = 0; i < *nrels_mag; i++) {
+			/* fixme: no bounds checking 0-255 */
+			data->rt[nr - 1][i] = results[i];
+			w83781d_write_value(client, W83781D_REG_RT_IDX, i);
+			w83781d_write_value(client, W83781D_REG_RT_VAL,
+					    data->rt[nr - 1][i]);
+		}
+	}
+}
+#endif
+
+static int __init sm_w83781d_init(void)
+{
+	printk(KERN_INFO "w83781d.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&w83781d_driver);
+}
+
+static void __exit sm_w83781d_exit(void)
+{
+	i2c_del_driver(&w83781d_driver);
+}
+
+
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
+	      "Philip Edelbrock <phil@netroedge.com>, "
+	      "and Mark Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("W83781D driver");
+MODULE_LICENSE("GPL");
+
+module_init(sm_w83781d_init);
+module_exit(sm_w83781d_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sensors/w83l785ts.c linux-2.4.27-leo/drivers/sensors/w83l785ts.c
--- linux-2.4.27/drivers/sensors/w83l785ts.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/drivers/sensors/w83l785ts.c	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,355 @@
+/*
+ * w83l785ts.c - Part of lm_sensors, Linux kernel modules for hardware
+ *               monitoring
+ * Copyright (C) 2003-2004  Jean Delvare <khali@linux-fr.org>
+ *
+ * Inspired from the lm83 driver. The W83L785TS-S is a sensor chip made
+ * by Winbond. It reports a single external temperature with a 1 deg
+ * resolution and a 3 deg accuracy. Data sheet can be obtained from
+ * Winbond's website at:
+ *   http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83L785TS-S.pdf
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-proc.h>
+#include <linux/init.h>
+#define LM_DATE "20040611"
+#define LM_VERSION "2.8.7"
+
+#ifndef I2C_DRIVERID_W83L785TS
+#define I2C_DRIVERID_W83L785TS	1047
+#endif
+
+/*
+ * Address to scan
+ * Address is fully defined internally and cannot be changed.
+ */
+
+static unsigned short normal_i2c[] = { 0x2e, SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/*
+ * Insmod parameters
+ */
+
+SENSORS_INSMOD_1(w83l785ts);
+
+/*
+ * The W83L785TS-S registers
+ * Manufacturer ID is 0x5CA3 for Winbond.
+ */
+
+#define W83L785TS_REG_MAN_ID1		0x4D
+#define W83L785TS_REG_MAN_ID2		0x4C
+#define W83L785TS_REG_CHIP_ID		0x4E
+#define W83L785TS_REG_CONFIG		0x40
+#define W83L785TS_REG_TYPE		0x52
+#define W83L785TS_REG_TEMP		0x27
+#define W83L785TS_REG_TEMP_OVER		0x53 /* not sure about this one */
+
+/*
+ * Conversions
+ * The W83L785TS-S uses signed 8-bit values.
+ */
+
+#define TEMP_FROM_REG(val)	(val & 0x80 ? val-0x100 : val)
+
+/*
+ * Functions declaration
+ */
+
+static int w83l785ts_attach_adapter(struct i2c_adapter *adapter);
+static int w83l785ts_detect(struct i2c_adapter *adapter, int address, unsigned
+	short flags, int kind);
+static int w83l785ts_detach_client(struct i2c_client *client);
+static void w83l785ts_update_client(struct i2c_client *client);
+static void w83l785ts_temp(struct i2c_client *client, int operation, int
+	ctl_name, int *nrels_mag, long *results);
+
+/*
+ * Driver data (common to all clients)
+ */
+ 
+static struct i2c_driver w83l785ts_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "W83L785S-S sensor driver",
+	.id		= I2C_DRIVERID_W83L785TS,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= w83l785ts_attach_adapter,
+	.detach_client	= w83l785ts_detach_client,
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct w83l785ts_data {
+	struct i2c_client client;
+	int sysctl_id;
+
+	struct semaphore update_lock;
+	char valid; /* zero until following fields are valid */
+	unsigned long last_updated; /* in jiffies */
+
+	/* registers values */
+	u8 temp, temp_over;
+};
+
+/*
+ * Proc entries
+ * These files are created for each detected W83L785TS-S.
+ */
+
+/* -- SENSORS SYSCTL START -- */
+
+#define W83L785TS_SYSCTL_TEMP	1200
+
+/* -- SENSORS SYSCTL END -- */
+
+
+static ctl_table w83l785ts_dir_table_template[] =
+{
+	{W83L785TS_SYSCTL_TEMP, "temp", NULL, 0, 0444, NULL,
+	 &i2c_proc_real, &i2c_sysctl_real, NULL, &w83l785ts_temp},
+	{0}
+};
+
+/*
+ * Internal variables
+ */
+
+static int w83l785ts_id = 0;
+
+/*
+ * Real code
+ */
+
+static int w83l785ts_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, w83l785ts_detect);
+}
+
+/*
+ * The following function does more than just detection. If detection
+ * succeeds, it also registers the new chip.
+ */
+static int w83l785ts_detect(struct i2c_adapter *adapter, int address,
+	unsigned short flags, int kind)
+{
+	struct i2c_client *new_client;
+	struct w83l785ts_data *data;
+	int err = 0;
+	const char *type_name = "";
+	const char *client_name = "";
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+#ifdef DEBUG
+		printk(KERN_DEBUG "w83l785ts.o: I2C bus doesn't support "
+			"byte read mode, skipping.\n");
+#endif
+		return 0;
+	}
+
+	if (!(data = kmalloc(sizeof(struct w83l785ts_data), GFP_KERNEL))) {
+		printk(KERN_ERR "w83l785ts.o: Out of memory in w83l785ts_detect "
+			"(new_client).\n");
+		return -ENOMEM;
+	}
+
+	/*
+	 * The common I2C client data is placed right after the
+	 * W83L785TS-specific. The W83L785TS-specific data is pointed to by the
+	 * data field from the I2C client data.
+	 */
+
+	new_client = &data->client;
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &w83l785ts_driver;
+	new_client->flags = 0;
+
+	/*
+	 * Now we do the remaining detection. A negative kind means that
+	 * the driver was loaded with no force parameter (default), so we
+	 * must both detect and identify the chip (actually there is only
+	 * one possible kind of chip for now, W83L785TS-S). A zero kind means
+	 * that the driver was loaded with the force parameter, the detection
+	 * step shall be skipped. A positive kind means that the driver
+	 * was loaded with the force parameter and a given kind of chip is
+	 * requested, so both the detection and the identification steps
+	 * are skipped.
+	 */
+
+	if (kind < 0) { /* detection */
+		if (((i2c_smbus_read_byte_data(new_client, W83L785TS_REG_CONFIG) & 0x80) != 0x00)
+		 || ((i2c_smbus_read_byte_data(new_client, W83L785TS_REG_TYPE) & 0xFC) != 0x00)) {
+#ifdef DEBUG
+			printk(KERN_DEBUG "w83l785ts.o: Detection failed at "
+				"0x%02x.\n", address);
+#endif
+			goto ERROR1;
+		}
+	}
+
+	if (kind <= 0) { /* identification */
+		u16 man_id;
+		u8 chip_id;
+
+		man_id = (i2c_smbus_read_byte_data(new_client, W83L785TS_REG_MAN_ID1) << 8)
+		       +  i2c_smbus_read_byte_data(new_client, W83L785TS_REG_MAN_ID2);
+		chip_id = i2c_smbus_read_byte_data(new_client, W83L785TS_REG_CHIP_ID);
+		if (man_id == 0x5CA3) { /* Winbond */
+			if (chip_id == 0x70)
+				kind = w83l785ts;
+		}
+	}
+
+	if (kind <= 0) { /* identification failed */
+		printk(KERN_INFO "w83l785ts.o: Unsupported chip.\n");
+		goto ERROR1;
+	}
+
+	if (kind == w83l785ts) {
+		type_name = "w83l785ts";
+		client_name = "W83L785TS-S chip";
+	} else {
+		printk(KERN_ERR "w83l785ts.o: Unknown kind %d.\n", kind);
+		goto ERROR1;
+	}
+	
+	/*
+	 * OK, we got a valid chip so we can fill in the remaining client
+	 * fields.
+	 */
+
+	strcpy(new_client->name, client_name);
+	new_client->id = w83l785ts_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/*
+	 * Tell the I2C layer a new client has arrived.
+	 */
+
+	if ((err = i2c_attach_client(new_client))) {
+#ifdef DEBUG
+		printk(KERN_ERR "w83l785ts.o: Failed attaching client.\n");
+#endif
+		goto ERROR1;
+	}
+
+	/*
+	 * Register a new directory entry.
+	 */
+
+	if ((err = i2c_register_entry(new_client, type_name,
+	    w83l785ts_dir_table_template)) < 0) {
+#ifdef DEBUG
+		printk(KERN_ERR "w83l785ts.o: Failed registering directory "
+			"entry.\n");
+#endif
+		goto ERROR2;
+	}
+	data->sysctl_id = err;
+
+	/*
+	 * Initialize the W83L785TS chip
+	 * Nothing yet, assume it is already started.
+	 */
+
+	return 0;
+
+	ERROR2:
+	i2c_detach_client(new_client);
+	ERROR1:
+	kfree(data);
+	return err;
+}
+
+static int w83l785ts_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	i2c_deregister_entry(((struct w83l785ts_data *) (client->data))->sysctl_id);
+	if ((err = i2c_detach_client(client))) {
+		printk(KERN_ERR "w83l785ts.o: Client deregistration failed, "
+			"client not detached.\n");
+		return err;
+	}
+
+	kfree(client->data);
+	return 0;
+}
+
+static void w83l785ts_update_client(struct i2c_client *client)
+{
+	struct w83l785ts_data *data = client->data;
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ * 2)
+	 || (jiffies < data->last_updated)
+	 || !data->valid) {
+#ifdef DEBUG
+		printk(KERN_DEBUG "w83l785ts.o: Updating data.\n");
+#endif
+		data->temp = i2c_smbus_read_byte_data(client, W83L785TS_REG_TEMP);
+		data->temp_over = i2c_smbus_read_byte_data(client, W83L785TS_REG_TEMP_OVER);
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+static void w83l785ts_temp(struct i2c_client *client, int operation,
+	int ctl_name, int *nrels_mag, long *results)
+{
+	struct w83l785ts_data *data = client->data;
+
+	if (operation == SENSORS_PROC_REAL_INFO) {
+		*nrels_mag = 0; /* magnitude */
+	} else if (operation == SENSORS_PROC_REAL_READ) {
+		w83l785ts_update_client(client);
+		results[0] = TEMP_FROM_REG(data->temp_over);
+		results[1] = TEMP_FROM_REG(data->temp);
+		*nrels_mag = 2;
+	}
+}
+
+static int __init sm_w83l785ts_init(void)
+{
+	printk(KERN_INFO "w83l785ts.o version %s (%s)\n", LM_VERSION, LM_DATE);
+	return i2c_add_driver(&w83l785ts_driver);
+}
+
+static void __exit sm_w83l785ts_exit(void)
+{
+	i2c_del_driver(&w83l785ts_driver);
+}
+
+MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
+MODULE_DESCRIPTION("W83L785TS-S sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(sm_w83l785ts_init);
+module_exit(sm_w83l785ts_exit);
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/sound/sound_core.c linux-2.4.27-leo/drivers/sound/sound_core.c
--- linux-2.4.27/drivers/sound/sound_core.c	2001-09-30 20:26:08.000000000 +0100
+++ linux-2.4.27-leo/drivers/sound/sound_core.c	2004-09-17 03:19:36.000000000 +0100
@@ -37,6 +37,7 @@
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/usb/storage/unusual_devs.h linux-2.4.27-leo/drivers/usb/storage/unusual_devs.h
--- linux-2.4.27/drivers/usb/storage/unusual_devs.h	2004-09-17 02:38:45.000000000 +0100
+++ linux-2.4.27-leo/drivers/usb/storage/unusual_devs.h	2004-09-20 22:10:03.000000000 +0100
@@ -338,6 +338,18 @@
 		"USB Hard Disk",
 		US_SC_RBC, US_PR_CB, NULL, 0 ), 
 
+/* Included by Paul Evans <leonerd@leonerd.org.uk>
+ * For A-mego Dual Slot USB Card Reader/Writer
+ * From instructions found on:
+ * http://www.qbik.ch/usb/devices/showdev.php?id=1262
+ */
+
+UNUSUAL_DEV(  0x0aec, 0x5010, 0x0100, 0x0100, 
+		"A-Mego", 
+		"CSU-LA1 Dual Slot USB Card Reader / Writer", 
+		US_SC_SCSI, US_PR_BULK, NULL, 
+		US_FL_FIX_INQUIRY ),
+
 #ifdef CONFIG_USB_STORAGE_ISD200
 UNUSUAL_DEV(  0x05ab, 0x0031, 0x0100, 0x0110,
 		"In-System",
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/video/matrox/i2c-matroxfb.c linux-2.4.27-leo/drivers/video/matrox/i2c-matroxfb.c
--- linux-2.4.27/drivers/video/matrox/i2c-matroxfb.c	2004-09-20 19:34:55.000000000 +0100
+++ linux-2.4.27-leo/drivers/video/matrox/i2c-matroxfb.c	2004-09-20 21:34:38.000000000 +0100
@@ -87,29 +87,20 @@
 	return (matroxfb_read_gpio(b->minfo) & b->mask.clock) ? 1 : 0;
 }
 
-static void matroxfb_dh_inc_use(struct i2c_adapter* dummy) {
-	MOD_INC_USE_COUNT;
-}
-
-static void matroxfb_dh_dec_use(struct i2c_adapter* dummy) {
-	MOD_DEC_USE_COUNT;
-}
-
-static struct i2c_adapter matrox_i2c_adapter_template =
-{
-	.id =		I2C_HW_B_G400,
-	.inc_use =	matroxfb_dh_inc_use,
-	.dec_use =	matroxfb_dh_dec_use,
+static struct i2c_adapter matrox_i2c_adapter_template = {
+	.owner		= THIS_MODULE,
+	.id		= I2C_HW_B_G400,
 };
 
-static struct i2c_algo_bit_data matrox_i2c_algo_template =
-{
-	NULL,
-	matroxfb_gpio_setsda,
-	matroxfb_gpio_setscl,
-	matroxfb_gpio_getsda,
-	matroxfb_gpio_getscl,
-	10, 10, 100,
+static struct i2c_algo_bit_data matrox_i2c_algo_template = {
+	.data		= NULL,
+	.setsda		= matroxfb_gpio_setsda,
+	.setscl		= matroxfb_gpio_setscl,
+	.getsda		= matroxfb_gpio_getsda,
+	.getscl		= matroxfb_gpio_getscl,
+	.udelay		= 10,
+	.mdelay		= 10,
+	.timeout	= 100,
 };
 
 static int i2c_bus_reg(struct i2c_bit_adapter* b, struct matrox_fb_info* minfo, 
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/video/matrox/matroxfb_maven.c linux-2.4.27-leo/drivers/video/matrox/matroxfb_maven.c
--- linux-2.4.27/drivers/video/matrox/matroxfb_maven.c	2003-06-13 15:51:37.000000000 +0100
+++ linux-2.4.27-leo/drivers/video/matrox/matroxfb_maven.c	2004-09-20 21:34:38.000000000 +0100
@@ -1246,14 +1246,6 @@
 static unsigned short normal_i2c_range[] = { MAVEN_I2CID, MAVEN_I2CID, I2C_CLIENT_END };
 I2C_CLIENT_INSMOD;
 
-static void maven_inc_use(struct i2c_client* clnt) {
-	MOD_INC_USE_COUNT;
-}
-
-static void maven_dec_use(struct i2c_client* clnt) {
-	MOD_DEC_USE_COUNT;
-}
-
 static struct i2c_driver maven_driver;
 
 static int maven_detect_client(struct i2c_adapter* adapter, int address, unsigned short flags,
@@ -1319,15 +1311,14 @@
 
 static int maven_driver_registered = 0;
 
-static struct i2c_driver maven_driver={
-	"maven",
-	I2C_DRIVERID_MGATVO,
-	I2C_DF_NOTIFY,
-	maven_attach_adapter,
-	maven_detach_client,
-	maven_command,
-	maven_inc_use,
-	maven_dec_use
+static struct i2c_driver maven_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "maven",
+	.id		= I2C_DRIVERID_MGATVO,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= maven_attach_adapter,
+	.detach_client	= maven_detach_client,
+	.command	= maven_command,
 };
 
 /* ************************** */
diff -urN --exclude-from=diff-exclude linux-2.4.27/drivers/video/vesafb.c linux-2.4.27-leo/drivers/video/vesafb.c
--- linux-2.4.27/drivers/video/vesafb.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.27-leo/drivers/video/vesafb.c	2004-09-17 03:19:58.000000000 +0100
@@ -546,7 +546,7 @@
 	video_visual = (video_bpp == 8) ?
 		FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
 
-#ifndef __i386__
+#if !defined(__i386__) || defined(CONFIG_GRKERNSEC_PAX_KERNEXEC)
 	screen_info.vesapm_seg = 0;
 #endif
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/adfs/map.c linux-2.4.27-leo/fs/adfs/map.c
--- linux-2.4.27/fs/adfs/map.c	2001-10-25 21:53:53.000000000 +0100
+++ linux-2.4.27-leo/fs/adfs/map.c	2004-09-17 03:19:36.000000000 +0100
@@ -12,6 +12,7 @@
 #include <linux/fs.h>
 #include <linux/adfs_fs.h>
 #include <linux/spinlock.h>
+#include <linux/sched.h>
 
 #include "adfs.h"
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/binfmt_aout.c linux-2.4.27-leo/fs/binfmt_aout.c
--- linux-2.4.27/fs/binfmt_aout.c	2001-11-03 01:39:20.000000000 +0000
+++ linux-2.4.27-leo/fs/binfmt_aout.c	2004-09-17 03:19:58.000000000 +0100
@@ -113,10 +113,12 @@
 /* If the size of the dump file exceeds the rlimit, then see what would happen
    if we wrote the stack, but not the data area.  */
 #ifdef __sparc__
+	gr_learn_resource(current, RLIMIT_CORE, dump.u_dsize+dump.u_ssize, 1);
 	if ((dump.u_dsize+dump.u_ssize) >
 	    current->rlim[RLIMIT_CORE].rlim_cur)
 		dump.u_dsize = 0;
 #else
+	gr_learn_resource(current, RLIMIT_CORE, (dump.u_dsize+dump.u_ssize+1) * PAGE_SIZE, 1);
 	if ((dump.u_dsize+dump.u_ssize+1) * PAGE_SIZE >
 	    current->rlim[RLIMIT_CORE].rlim_cur)
 		dump.u_dsize = 0;
@@ -124,10 +126,12 @@
 
 /* Make sure we have enough room to write the stack and data areas. */
 #ifdef __sparc__
+	gr_learn_resource(current, RLIMIT_CORE, dump.u_ssize, 1);
 	if ((dump.u_ssize) >
 	    current->rlim[RLIMIT_CORE].rlim_cur)
 		dump.u_ssize = 0;
 #else
+	gr_learn_resource(current, RLIMIT_CORE, (dump.u_ssize+1) * PAGE_SIZE, 1);
 	if ((dump.u_ssize+1) * PAGE_SIZE >
 	    current->rlim[RLIMIT_CORE].rlim_cur)
 		dump.u_ssize = 0;
@@ -276,6 +280,8 @@
 	rlim = current->rlim[RLIMIT_DATA].rlim_cur;
 	if (rlim >= RLIM_INFINITY)
 		rlim = ~0;
+
+	gr_learn_resource(current, RLIMIT_DATA, ex.a_data + ex.a_bss, 1);
 	if (ex.a_data + ex.a_bss > rlim)
 		return -ENOMEM;
 
@@ -307,6 +313,24 @@
 	current->mm->mmap = NULL;
 	compute_creds(bprm);
  	current->flags &= ~PF_FORKNOEXEC;
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+	if (!(N_FLAGS(ex) & F_PAX_PAGEEXEC)) {
+		current->flags |= PF_PAX_PAGEEXEC;
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUTRAMP
+		if (N_FLAGS(ex) & F_PAX_EMUTRAMP)
+			current->flags |= PF_PAX_EMUTRAMP;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_MPROTECT
+		if (!(N_FLAGS(ex) & F_PAX_MPROTECT))        
+			current->flags |= PF_PAX_MPROTECT;
+#endif
+
+	}
+#endif
+
 #ifdef __sparc__
 	if (N_MAGIC(ex) == NMAGIC) {
 		loff_t pos = fd_offset;
@@ -393,7 +417,7 @@
 
 		down_write(&current->mm->mmap_sem);
  		error = do_mmap(bprm->file, N_DATADDR(ex), ex.a_data,
-				PROT_READ | PROT_WRITE | PROT_EXEC,
+				PROT_READ | PROT_WRITE,
 				MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
 				fd_offset + ex.a_text);
 		up_write(&current->mm->mmap_sem);
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/binfmt_elf.c linux-2.4.27-leo/fs/binfmt_elf.c
--- linux-2.4.27/fs/binfmt_elf.c	2004-05-19 21:34:40.000000000 +0100
+++ linux-2.4.27-leo/fs/binfmt_elf.c	2004-09-17 03:19:58.000000000 +0100
@@ -33,10 +33,13 @@
 #include <linux/smp_lock.h>
 #include <linux/compiler.h>
 #include <linux/highmem.h>
+#include <linux/random.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 #include <asm/param.h>
 #include <asm/pgalloc.h>
+#include <asm/system.h>
 
 #define DLINFO_ITEMS 13
 
@@ -86,6 +89,12 @@
 	if (end <= start)
 		return;
 	do_brk(start, end - start);
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	if (current->flags & PF_PAX_RANDEXEC)
+		__do_mmap_pgoff(NULL, ELF_PAGEALIGN(start + current->mm->delta_exec), 0UL, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED | MAP_MIRROR, start);
+#endif
+
 }
 
 
@@ -414,6 +423,203 @@
 	return elf_entry;
 }
 
+#if (defined(CONFIG_GRKERNSEC_PAX_EI_PAX) || defined(CONFIG_GRKERNSEC_PAX_PT_PAX_FLAGS)) && defined(CONFIG_GRKERNSEC_PAX_SOFTMODE)
+static unsigned long pax_parse_softmode(const struct elf_phdr * const elf_phdata)
+{
+	unsigned long pax_flags = 0UL;
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+	if (elf_phdata->p_flags & PF_PAGEEXEC)
+		pax_flags |= PF_PAX_PAGEEXEC;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	if (elf_phdata->p_flags & PF_SEGMEXEC) {
+		pax_flags &= ~PF_PAX_PAGEEXEC;
+		pax_flags |= PF_PAX_SEGMEXEC;
+	}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUTRAMP
+	if (elf_phdata->p_flags & PF_EMUTRAMP)
+		pax_flags |= PF_PAX_EMUTRAMP;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_MPROTECT
+	if (elf_phdata->p_flags & PF_MPROTECT)
+		pax_flags |= PF_PAX_MPROTECT;
+#endif
+
+#if defined(CONFIG_GRKERNSEC_PAX_RANDMMAP) || defined(CONFIG_GRKERNSEC_PAX_RANDUSTACK)
+
+#ifdef CONFIG_GRKERNSEC_PAX_SOFTMODE
+	if (pax_aslr)
+#endif
+
+	if (elf_phdata->p_flags & PF_RANDMMAP)
+		pax_flags |= PF_PAX_RANDMMAP;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+
+#ifdef CONFIG_GRKERNSEC_PAX_SOFTMODE
+	if (pax_aslr)
+#endif
+
+	if (elf_phdata->p_flags & PF_RANDEXEC)
+		pax_flags |= PF_PAX_RANDEXEC;
+#endif
+
+	return pax_flags;
+}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_PT_PAX_FLAGS
+static unsigned long pax_parse_hardmode(const struct elf_phdr * const elf_phdata)
+{
+	unsigned long pax_flags = 0UL;
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+	if (!(elf_phdata->p_flags & PF_NOPAGEEXEC))
+		pax_flags |= PF_PAX_PAGEEXEC;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	if (!(elf_phdata->p_flags & PF_NOSEGMEXEC)) {
+		pax_flags &= ~PF_PAX_PAGEEXEC;
+		pax_flags |= PF_PAX_SEGMEXEC;
+	}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUTRAMP
+	if (!(elf_phdata->p_flags & PF_NOEMUTRAMP))
+		pax_flags |= PF_PAX_EMUTRAMP;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_MPROTECT
+	if (!(elf_phdata->p_flags & PF_NOMPROTECT))
+		pax_flags |= PF_PAX_MPROTECT;
+#endif
+
+#if defined(CONFIG_GRKERNSEC_PAX_RANDMMAP) || defined(CONFIG_GRKERNSEC_PAX_RANDUSTACK)
+
+#ifdef CONFIG_GRKERNSEC_PAX_SOFTMODE
+	if (pax_aslr)
+#endif
+
+	if (!(elf_phdata->p_flags & PF_NORANDMMAP))
+		pax_flags |= PF_PAX_RANDMMAP;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+
+#ifdef CONFIG_GRKERNSEC_PAX_SOFTMODE
+	if (pax_aslr)
+#endif
+
+	if (!(elf_phdata->p_flags & PF_NORANDEXEC))
+		pax_flags |= PF_PAX_RANDEXEC;
+#endif
+
+	return pax_flags;
+}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EI_PAX
+static int pax_parse_ei_pax(const struct elfhdr * const elf_ex)
+{
+	unsigned long pax_flags = 0UL;
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+	if (!(elf_ex->e_ident[EI_PAX] & EF_PAX_PAGEEXEC))
+		pax_flags |= PF_PAX_PAGEEXEC;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	if (!(elf_ex->e_ident[EI_PAX] & EF_PAX_SEGMEXEC)) {
+		pax_flags &= ~PF_PAX_PAGEEXEC;
+		pax_flags |= PF_PAX_SEGMEXEC;
+	}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUTRAMP
+	if ((pax_flags & (PF_PAX_PAGEEXEC | PF_PAX_SEGMEXEC)) && (elf_ex->e_ident[EI_PAX] & EF_PAX_EMUTRAMP))
+		pax_flags |= PF_PAX_EMUTRAMP;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_MPROTECT
+	if ((pax_flags & (PF_PAX_PAGEEXEC | PF_PAX_SEGMEXEC)) && !(elf_ex->e_ident[EI_PAX] & EF_PAX_MPROTECT))
+		pax_flags |= PF_PAX_MPROTECT;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_ASLR
+
+#ifdef CONFIG_GRKERNSEC_PAX_SOFTMODE
+	if (pax_aslr)
+#endif
+
+	if (!(elf_ex->e_ident[EI_PAX] & EF_PAX_RANDMMAP))
+		pax_flags |= PF_PAX_RANDMMAP;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+
+#ifdef CONFIG_GRKERNSEC_PAX_SOFTMODE
+	if (pax_aslr)
+#endif
+
+	if ((elf_ex->e_ident[EI_PAX] & EF_PAX_RANDEXEC) && (elf_ex->e_type == ET_EXEC) && (pax_flags & PF_PAX_MPROTECT))
+		pax_flags |= PF_PAX_RANDEXEC;
+#endif
+
+	return pax_flags;
+}
+#endif
+
+#if defined(CONFIG_GRKERNSEC_PAX_EI_PAX) || defined(CONFIG_GRKERNSEC_PAX_PT_PAX_FLAGS)
+static int pax_parse_elf_flags(const struct elfhdr * const elf_ex, const struct elf_phdr * const elf_phdata)
+{
+	unsigned long pax_flags = 0UL;
+
+#ifdef CONFIG_GRKERNSEC_PAX_PT_PAX_FLAGS
+	unsigned long i;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EI_PAX
+	pax_flags = pax_parse_ei_pax(elf_ex);
+#endif
+       
+#ifdef CONFIG_GRKERNSEC_PAX_PT_PAX_FLAGS
+	for (i = 0UL; i < elf_ex->e_phnum; i++)
+		if (elf_phdata[i].p_type == PT_PAX_FLAGS) {
+			if (((elf_phdata[i].p_flags & PF_PAGEEXEC) && (elf_phdata[i].p_flags & PF_NOPAGEEXEC)) ||
+			    ((elf_phdata[i].p_flags & PF_SEGMEXEC) && (elf_phdata[i].p_flags & PF_NOSEGMEXEC)) ||
+			    ((elf_phdata[i].p_flags & PF_EMUTRAMP) && (elf_phdata[i].p_flags & PF_NOEMUTRAMP)) ||
+			    ((elf_phdata[i].p_flags & PF_MPROTECT) && (elf_phdata[i].p_flags & PF_NOMPROTECT)) ||
+			    ((elf_phdata[i].p_flags & PF_RANDMMAP) && (elf_phdata[i].p_flags & PF_NORANDMMAP)) ||
+			    ((elf_phdata[i].p_flags & PF_RANDEXEC) && ((elf_phdata[i].p_flags & PF_NORANDEXEC) || elf_ex->e_type == ET_DYN || !(elf_phdata[i].p_flags & PF_MPROTECT))) ||
+			    (!(elf_phdata[i].p_flags & PF_NORANDEXEC) && (elf_ex->e_type == ET_DYN || (elf_phdata[i].p_flags & PF_NOMPROTECT))))
+				return -EINVAL;
+
+#ifdef CONFIG_GRKERNSEC_PAX_SOFTMODE
+			if (pax_softmode)
+				pax_flags = pax_parse_softmode(&elf_phdata[i]);
+			else
+#endif
+
+				pax_flags = pax_parse_hardmode(&elf_phdata[i]);
+			break;
+		}
+#endif
+
+	if (0 > pax_check_flags(&pax_flags))
+		return -EINVAL;
+
+	current->flags |= pax_flags;
+	return 0;
+}
+#endif
+
 /*
  * These are the functions used to load ELF style executables and shared
  * libraries.  There is no binary dependent code anywhere else.
@@ -446,6 +652,11 @@
   	struct exec interp_ex;
 	char passed_fileno[6];
 	struct files_struct *files;
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	unsigned long load_addr_random = 0UL;
+	unsigned long load_bias_random = 0UL;
+#endif
 	
 	/* Get the exec-header */
 	elf_ex = *((struct elfhdr *) bprm->buf);
@@ -623,7 +834,49 @@
 	current->mm->end_data = 0;
 	current->mm->end_code = 0;
 	current->mm->mmap = NULL;
+
+#ifdef CONFIG_GRKERNSEC_PAX_DLRESOLVE
+	current->mm->call_dl_resolve = 0UL;
+#endif
+
+#if defined(CONFIG_PPC32) && defined(CONFIG_GRKERNSEC_PAX_EMUSIGRT)
+	current->mm->call_syscall = 0UL;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_ASLR
+	current->mm->delta_mmap = 0UL;
+	current->mm->delta_exec = 0UL;
+	current->mm->delta_stack = 0UL;
+#endif
+
 	current->flags &= ~PF_FORKNOEXEC;
+
+#if defined(CONFIG_GRKERNSEC_PAX_EI_PAX) || defined(CONFIG_GRKERNSEC_PAX_PT_PAX_FLAGS)
+	if (0 > pax_parse_elf_flags(&elf_ex, elf_phdata)) {
+		send_sig(SIGKILL, current, 0);
+		goto out_free_dentry;
+	}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_HAVE_ACL_FLAGS
+	pax_set_flags(bprm);
+#elif defined(CONFIG_GRKERNSEC_PAX_HOOK_ACL_FLAGS)
+	if (pax_set_flags_func)
+		(*pax_set_flags_func)(bprm);
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_ASLR
+	if (current->flags & PF_PAX_RANDMMAP) {
+#define pax_delta_mask(delta, lsb, len) (((delta) & ((1UL << (len)) - 1)) << (lsb))
+
+		current->mm->delta_mmap = pax_delta_mask(get_random_long(), PAX_DELTA_MMAP_LSB(current), PAX_DELTA_MMAP_LEN(current));
+		current->mm->delta_exec = pax_delta_mask(get_random_long(), PAX_DELTA_EXEC_LSB(current), PAX_DELTA_EXEC_LEN(current));
+		current->mm->delta_stack = pax_delta_mask(get_random_long(), PAX_DELTA_STACK_LSB(current), PAX_DELTA_STACK_LEN(current));
+	}
+#endif
+
+	gr_set_pax_flags(current);	
+
 	elf_entry = (unsigned long) elf_ex.e_entry;
 
 	/* Do this so that we can load the interpreter, if need be.  We will
@@ -632,7 +885,7 @@
 	retval = setup_arg_pages(bprm);
 	if (retval < 0) {
 		send_sig(SIGKILL, current, 0);
-		return retval;
+		goto out_free_dentry;
 	}
 	
 	current->mm->start_stack = bprm->p;
@@ -679,11 +932,84 @@
 			   base, as well as whatever program they might try to exec.  This
 		           is because the brk will follow the loader, and is not movable.  */
 			load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr);
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDMMAP
+			/* PaX: randomize base address at the default exe base if requested */
+			if (current->flags & PF_PAX_RANDMMAP) {
+				load_bias = ELF_PAGESTART(PAX_ELF_ET_DYN_BASE(current) - vaddr + current->mm->delta_exec);
+				elf_flags |= MAP_FIXED;
+			}
+#endif
+
 		}
 
-		error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt, elf_prot, elf_flags);
-		if (BAD_ADDR(error))
-			continue;
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+		if ((current->flags & PF_PAX_RANDEXEC) && (elf_ex.e_type == ET_EXEC)) {
+			error = -ENOMEM;
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+			if (current->flags & PF_PAX_PAGEEXEC)
+				error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt, elf_prot & ~PROT_EXEC, elf_flags);
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+			if (current->flags & PF_PAX_SEGMEXEC) {
+				unsigned long addr, len;
+
+				addr = ELF_PAGESTART(load_bias + vaddr);
+				len = elf_ppnt->p_filesz + ELF_PAGEOFFSET(elf_ppnt->p_vaddr);
+				if (len > SEGMEXEC_TASK_SIZE || addr > SEGMEXEC_TASK_SIZE-len)
+					continue;
+				down_write(&current->mm->mmap_sem);
+				error = __do_mmap_pgoff(bprm->file, addr, len, elf_prot, elf_flags, (elf_ppnt->p_offset - ELF_PAGEOFFSET(elf_ppnt->p_vaddr)) >> PAGE_SHIFT);
+				up_write(&current->mm->mmap_sem);
+			}
+#endif
+
+			if (BAD_ADDR(error))
+				continue;
+
+			/* PaX: mirror at a randomized base */
+			down_write(&current->mm->mmap_sem);
+
+			if (!load_addr_set) {
+				load_addr_random = get_unmapped_area(bprm->file, 0UL, elf_ppnt->p_filesz + ELF_PAGEOFFSET(elf_ppnt->p_vaddr), (elf_ppnt->p_offset - ELF_PAGEOFFSET(elf_ppnt->p_vaddr)) >> PAGE_SHIFT, MAP_PRIVATE);
+				if (BAD_ADDR(load_addr_random)) {
+					up_write(&current->mm->mmap_sem);
+					continue;
+				}
+				load_bias_random = load_addr_random - vaddr;
+			}
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+			if (current->flags & PF_PAX_PAGEEXEC)
+				load_addr_random = __do_mmap_pgoff(NULL, ELF_PAGESTART(load_bias_random + vaddr), 0UL, elf_prot, elf_flags | MAP_MIRROR, error);
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+			if (current->flags & PF_PAX_SEGMEXEC) {
+				if (elf_prot & PROT_EXEC) {
+					load_addr_random = __do_mmap_pgoff(NULL, ELF_PAGESTART(load_bias_random + vaddr), elf_ppnt->p_memsz + ELF_PAGEOFFSET(elf_ppnt->p_vaddr), PROT_NONE, MAP_PRIVATE | MAP_FIXED, 0UL);
+					if (!BAD_ADDR(load_addr_random)) {
+						load_addr_random = __do_mmap_pgoff(NULL, ELF_PAGESTART(load_bias_random + vaddr + SEGMEXEC_TASK_SIZE), 0UL, elf_prot, elf_flags | MAP_MIRROR, error);
+						if (!BAD_ADDR(load_addr_random))
+							load_addr_random -= SEGMEXEC_TASK_SIZE;
+					}
+				} else
+					load_addr_random = __do_mmap_pgoff(NULL, ELF_PAGESTART(load_bias_random + vaddr), 0UL, elf_prot, elf_flags | MAP_MIRROR, error);
+			}
+#endif
+
+			up_write(&current->mm->mmap_sem);
+			if (BAD_ADDR(load_addr_random))
+				continue;
+		} else
+#endif
+		{
+			error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt, elf_prot, elf_flags);
+			if (BAD_ADDR(error))
+				continue;
+		}
 
 		if (!load_addr_set) {
 			load_addr_set = 1;
@@ -694,6 +1020,11 @@
 				load_addr += load_bias;
 				reloc_func_desc = load_addr;
 			}
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+			current->mm->delta_exec = load_addr_random - load_addr;
+#endif
+
 		}
 		k = elf_ppnt->p_vaddr;
 		if (k < start_code) start_code = k;
@@ -720,6 +1051,24 @@
 	start_data += load_bias;
 	end_data += load_bias;
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDMMAP
+
+#ifdef CONFIG_GRKERNSEC_PAX_SOFTMODE
+	if (pax_aslr)
+#endif
+
+	if (current->flags & PF_PAX_RANDMMAP)
+		elf_brk += PAGE_SIZE + pax_delta_mask(get_random_long(), 4, PAGE_SHIFT);
+#undef pax_delta_mask
+#endif
+
+	/* Calling set_brk effectively mmaps the pages that we need
+	 * for the bss and break sections
+	 */
+	set_brk(elf_bss, elf_brk);
+
+	padzero(elf_bss);
+
 	if (elf_interpreter) {
 		if (interpreter_type == INTERPRETER_AOUT)
 			elf_entry = load_aout_interp(&interp_ex,
@@ -768,13 +1117,6 @@
 	current->mm->end_data = end_data;
 	current->mm->start_stack = bprm->p;
 
-	/* Calling set_brk effectively mmaps the pages that we need
-	 * for the bss and break sections
-	 */
-	set_brk(elf_bss, elf_brk);
-
-	padzero(elf_bss);
-
 #if 0
 	printk("(start_brk) %lx\n" , (long) current->mm->start_brk);
 	printk("(end_code) %lx\n" , (long) current->mm->end_code);
@@ -811,6 +1153,10 @@
 	ELF_PLAT_INIT(regs, reloc_func_desc);
 #endif
 
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	pax_switch_segments(current);
+#endif
+
 	start_thread(regs, elf_entry, bprm->p);
 	if (current->ptrace & PT_PTRACED)
 		send_sig(SIGTRAP, current, 0);
@@ -1039,8 +1385,11 @@
 #undef DUMP_SEEK
 
 #define DUMP_WRITE(addr, nr)	\
+	do { \
+	gr_learn_resource(current, RLIMIT_CORE, size + (nr), 1); \
 	if ((size += (nr)) > limit || !dump_write(file, (addr), (nr))) \
-		goto end_coredump;
+		goto end_coredump; \
+	} while (0);
 #define DUMP_SEEK(off)	\
 	if (!dump_seek(file, (off))) \
 		goto end_coredump;
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/binfmt_misc.c linux-2.4.27-leo/fs/binfmt_misc.c
--- linux-2.4.27/fs/binfmt_misc.c	2002-08-03 01:39:45.000000000 +0100
+++ linux-2.4.27-leo/fs/binfmt_misc.c	2004-09-17 03:19:58.000000000 +0100
@@ -102,9 +102,11 @@
 	int retval;
 
 	retval = -ENOEXEC;
-	if (!enabled)
+	if (!enabled || bprm->misc)
 		goto _ret;
 
+	bprm->misc++;
+
 	/* to keep locking time low, we copy the interpreter string */
 	read_lock(&entries_lock);
 	fmt = check_file(bprm);
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/buffer.c linux-2.4.27-leo/fs/buffer.c
--- linux-2.4.27/fs/buffer.c	2004-09-17 02:38:46.000000000 +0100
+++ linux-2.4.27-leo/fs/buffer.c	2004-09-17 03:19:58.000000000 +0100
@@ -419,6 +419,34 @@
 	fsync_dev(dev);
 }
 
+int fsync_dev_lockfs(kdev_t dev)
+{
+	/* you are not allowed to try locking all the filesystems
+	** on the system, your chances of getting through without
+	** total deadlock are slim to none.
+	*/
+	if (!dev)
+		return fsync_dev(dev) ;
+
+	sync_buffers(dev, 0);
+
+	lock_kernel();
+	/* note, the FS might need to start transactions to 
+	** sync the inodes, or the quota, no locking until
+	** after these are done
+	*/
+	sync_inodes(dev);
+	DQUOT_SYNC_DEV(dev);
+	/* if inodes or quotas could be dirtied during the
+	** sync_supers_lockfs call, the FS is responsible for getting
+	** them on disk, without deadlocking against the lock
+	*/
+	sync_supers_lockfs(dev) ;
+	unlock_kernel();
+
+	return sync_buffers(dev, 1) ;
+}
+
 asmlinkage long sys_sync(void)
 {
 	fsync_dev(0);
@@ -799,6 +827,7 @@
 	bh->b_list = BUF_CLEAN;
 	bh->b_end_io = handler;
 	bh->b_private = private;
+	bh->b_journal_head = NULL;
 }
 
 void end_buffer_io_async(struct buffer_head * bh, int uptodate)
@@ -1863,6 +1892,9 @@
 	int err;
 
 	err = -EFBIG;
+
+	gr_learn_resource(current, RLIMIT_FSIZE, (unsigned long) size, 1);
+
         limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
 	if (limit != RLIM_INFINITY && size > (loff_t)limit) {
 		send_sig(SIGXFSZ, current, 0);
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/exec.c linux-2.4.27-leo/fs/exec.c
--- linux-2.4.27/fs/exec.c	2004-02-20 14:11:44.000000000 +0000
+++ linux-2.4.27-leo/fs/exec.c	2004-09-17 03:19:58.000000000 +0100
@@ -43,6 +43,9 @@
 #include <asm/uaccess.h>
 #include <asm/pgalloc.h>
 #include <asm/mmu_context.h>
+#include <linux/major.h>
+#include <linux/random.h>
+#include <linux/grsecurity.h>
 
 #ifdef CONFIG_KMOD
 #include <linux/kmod.h>
@@ -56,6 +59,20 @@
 static struct linux_binfmt *formats;
 static rwlock_t binfmt_lock = RW_LOCK_UNLOCKED;
 
+#ifdef CONFIG_GRKERNSEC_PAX_SOFTMODE
+
+#if defined(CONFIG_GRKERNSEC_PAX_RANDMMAP) || defined(CONFIG_GRKERNSEC_PAX_RANDUSTACK) || defined(CONFIG_GRKERNSEC_PAX_RANDKSTACK)
+unsigned int pax_aslr=1;
+#endif
+
+unsigned int pax_softmode;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_HOOK_ACL_FLAGS
+void (*pax_set_flags_func)(struct linux_binprm *bprm);
+EXPORT_SYMBOL(pax_set_flags_func);
+#endif
+
 int register_binfmt(struct linux_binfmt * fmt)
 {
 	struct linux_binfmt ** tmp = &formats;
@@ -290,7 +307,11 @@
 	struct vm_area_struct *vma; 
 	pgprot_t prot = PAGE_COPY; 
 
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	if (page_count(page) != 1 && (!(tsk->flags & PF_PAX_SEGMEXEC) || page_count(page) != 2))
+#else
 	if (page_count(page) != 1)
+#endif
 		printk(KERN_ERR "mem_map disagrees with %p at %08lx\n", page, address);
 	pgd = pgd_offset(tsk->mm, address);
 
@@ -303,9 +324,19 @@
 		goto out;
 	if (!pte_none(*pte))
 		goto out;
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	if (page_count(page) == 1) {
+#endif
+
 	lru_cache_add(page);
 	flush_dcache_page(page);
 	flush_page_to_ram(page);
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	}
+#endif
+
 	/* lookup is cheap because there is only a single entry in the list */
 	vma = find_vma(tsk->mm, address); 
 	if (vma) 
@@ -329,6 +360,10 @@
 	struct vm_area_struct *mpnt;
 	int i;
 
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	struct vm_area_struct *mpnt_m = NULL;
+#endif
+
 	stack_base = STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE;
 
 	bprm->p += stack_base;
@@ -340,12 +375,27 @@
 	if (!mpnt) 
 		return -ENOMEM; 
 	
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	if ((current->flags & PF_PAX_SEGMEXEC) && (VM_STACK_FLAGS & VM_MAYEXEC)) {
+		mpnt_m = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+		if (!mpnt_m) {
+			kmem_cache_free(vm_area_cachep, mpnt);
+			return -ENOMEM;
+		}
+	}
+#endif
+
 	down_write(&current->mm->mmap_sem);
 	{
 		mpnt->vm_mm = current->mm;
 		mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p;
 		mpnt->vm_end = STACK_TOP;
 		mpnt->vm_flags = VM_STACK_FLAGS;
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+		if (!(current->flags & PF_PAX_PAGEEXEC))
+			mpnt->vm_page_prot = protection_map[(VM_STACK_FLAGS | VM_EXEC) & 0x7];
+		else
+#endif
 		mpnt->vm_page_prot = protection_map[VM_STACK_FLAGS & 0x7];
 		mpnt->vm_ops = NULL;
 		mpnt->vm_pgoff = 0;
@@ -353,6 +403,25 @@
 		mpnt->vm_private_data = (void *) 0;
 		insert_vm_struct(current->mm, mpnt);
 		current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+		if (mpnt_m) {
+			*mpnt_m = *mpnt;
+			if (!(VM_STACK_FLAGS & VM_EXEC)) {
+				mpnt_m->vm_flags &= ~(VM_READ | VM_WRITE | VM_EXEC);
+				mpnt_m->vm_page_prot = PAGE_NONE;
+			}
+			mpnt_m->vm_start += SEGMEXEC_TASK_SIZE;
+			mpnt_m->vm_end += SEGMEXEC_TASK_SIZE;
+			mpnt_m->vm_flags |= VM_MIRROR;
+			mpnt->vm_flags |= VM_MIRROR;
+			mpnt_m->vm_private_data = (void *)(mpnt->vm_start - mpnt_m->vm_start);
+			mpnt->vm_private_data = (void *)(mpnt_m->vm_start - mpnt->vm_start);
+			insert_vm_struct(current->mm, mpnt_m);
+			current->mm->total_vm = (mpnt_m->vm_end - mpnt_m->vm_start) >> PAGE_SHIFT;
+		}
+#endif
+
 	} 
 
 	for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
@@ -360,6 +429,14 @@
 		if (page) {
 			bprm->page[i] = NULL;
 			put_dirty_page(current,page,stack_base);
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+			if (mpnt_m) {
+				page_cache_get(page);
+				put_dirty_page(current, page, stack_base + SEGMEXEC_TASK_SIZE);
+			}
+#endif
+
 		}
 		stack_base += PAGE_SIZE;
 	}
@@ -455,8 +532,8 @@
 		active_mm = current->active_mm;
 		current->mm = mm;
 		current->active_mm = mm;
-		task_unlock(current);
 		activate_mm(active_mm, mm);
+		task_unlock(current);
 		mm_release();
 		if (old_mm) {
 			if (active_mm != old_mm) BUG();
@@ -615,6 +692,30 @@
 	}
 	current->comm[i] = '\0';
 
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+	current->flags &= ~PF_PAX_PAGEEXEC;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUTRAMP
+	current->flags &= ~PF_PAX_EMUTRAMP;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_MPROTECT
+	current->flags &= ~PF_PAX_MPROTECT;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_ASLR
+	current->flags &= ~PF_PAX_RANDMMAP;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	current->flags &= ~PF_PAX_RANDEXEC;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	current->flags &= ~PF_PAX_SEGMEXEC;
+#endif
+
 	flush_thread();
 
 	de_thread(current);
@@ -776,8 +877,13 @@
 	
         /* AUD: Audit candidate if current->cap_effective is set */
 
-        current->suid = current->euid = current->fsuid = bprm->e_uid;
-        current->sgid = current->egid = current->fsgid = bprm->e_gid;
+	if (!gr_check_user_change(-1, bprm->e_uid, bprm->e_uid))
+		current->suid = current->euid = current->fsuid = bprm->e_uid;
+
+	if (!gr_check_group_change(-1, bprm->e_gid, bprm->e_gid))
+	        current->sgid = current->egid = current->fsgid = bprm->e_gid;
+
+	gr_handle_chroot_caps(current);
 
 	if(do_unlock)
 		unlock_kernel();
@@ -912,6 +1018,11 @@
 	struct file *file;
 	int retval;
 	int i;
+#ifdef CONFIG_GRKERNSEC
+	struct file *old_exec_file;
+	struct acl_subject_label *old_acl;
+	struct rlimit old_rlim[RLIM_NLIMITS];
+#endif
 
 	file = open_exec(filename);
 
@@ -919,12 +1030,37 @@
 	if (IS_ERR(file))
 		return retval;
 
+	gr_learn_resource(current, RLIMIT_NPROC, atomic_read(&current->user->processes), 1);
+
+	if (gr_handle_nproc()) {
+		allow_write_access(file);
+		fput(file);
+		return -EAGAIN;
+	}
+
+	if (!gr_acl_handle_execve(file->f_dentry, file->f_vfsmnt)) {
+		allow_write_access(file);
+		fput(file);
+		return -EACCES;
+	}
+
 	bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDUSTACK
+
+#ifdef CONFIG_GRKERNSEC_PAX_SOFTMODE
+	if (pax_aslr)
+#endif
+
+	bprm.p -= (get_random_long() & ~(sizeof(void *)-1)) & ~PAGE_MASK;
+#endif
+
 	memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page[0])); 
 
 	bprm.file = file;
 	bprm.filename = filename;
 	bprm.sh_bang = 0;
+	bprm.misc = 0;
 	bprm.loader = 0;
 	bprm.exec = 0;
 	if ((bprm.argc = count(argv, bprm.p / sizeof(void *))) < 0) {
@@ -943,11 +1079,26 @@
 	if (retval < 0) 
 		goto out; 
 
+	if (!gr_tpe_allow(file)) {
+		retval = -EACCES;
+		goto out;
+	}
+
+	if(gr_check_crash_exec(file)) {
+		retval = -EACCES;
+		goto out;
+	}
+
 	retval = copy_strings_kernel(1, &bprm.filename, &bprm);
 	if (retval < 0) 
 		goto out; 
 
 	bprm.exec = bprm.p;
+
+	gr_log_chroot_exec(file->f_dentry, file->f_vfsmnt);
+
+	gr_handle_exec_args(&bprm, argv);
+
 	retval = copy_strings(bprm.envc, envp, &bprm);
 	if (retval < 0) 
 		goto out; 
@@ -956,11 +1107,35 @@
 	if (retval < 0) 
 		goto out; 
 
+#ifdef CONFIG_GRKERNSEC
+	old_acl = current->acl;
+	memcpy(old_rlim, current->rlim, sizeof(old_rlim));
+	old_exec_file = current->exec_file;
+	get_file(file);
+	current->exec_file = file;
+#endif
+
+	retval = gr_set_proc_label(file->f_dentry, file->f_vfsmnt);
+	if (retval < 0)
+		goto out_fail;
+
 	retval = search_binary_handler(&bprm,regs);
-	if (retval >= 0)
+	if (retval >= 0) {
+#ifdef CONFIG_GRKERNSEC
+		if (old_exec_file)
+			fput(old_exec_file);
+#endif
 		/* execve success */
 		return retval;
+	}
 
+out_fail:
+#ifdef CONFIG_GRKERNSEC
+	current->acl = old_acl;
+	memcpy(current->rlim, old_rlim, sizeof(old_rlim));
+	fput(current->exec_file);
+	current->exec_file = old_exec_file;
+#endif
 out:
 	/* Something went wrong, return the inode and free the argument pages*/
 	allow_write_access(bprm.file);
@@ -1102,6 +1277,130 @@
 	*out_ptr = 0;
 }
 
+int pax_check_flags(unsigned long * flags)
+{
+	int retval = 0;
+
+#if !defined(__i386__) || !defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC)
+	if (*flags & PF_PAX_SEGMEXEC)
+	{
+		*flags &= ~PF_PAX_SEGMEXEC;
+		retval = -EINVAL;
+	}
+#endif
+
+	if ((*flags & PF_PAX_PAGEEXEC)
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+	    &&  (*flags & PF_PAX_SEGMEXEC)
+#endif
+
+	   )
+	{
+		*flags &= ~PF_PAX_PAGEEXEC;
+		retval = -EINVAL;
+	}
+
+	if ((*flags & PF_PAX_MPROTECT)
+
+#ifdef CONFIG_GRKERNSEC_PAX_MPROTECT
+	    && !(*flags & (PF_PAX_PAGEEXEC | PF_PAX_SEGMEXEC))
+#endif
+
+	   )
+	{
+		*flags &= ~PF_PAX_MPROTECT;
+		retval = -EINVAL;
+	}
+
+	if ((*flags & PF_PAX_EMUTRAMP)
+
+#ifdef CONFIG_GRKERNSEC_PAX_EMUTRAMP
+	    && !(*flags & (PF_PAX_PAGEEXEC | PF_PAX_SEGMEXEC))
+#endif
+
+	   )
+	{
+		*flags &= ~PF_PAX_EMUTRAMP;
+		retval = -EINVAL;
+	}
+
+	if ((*flags & PF_PAX_RANDEXEC)
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+	    && !(*flags & PF_PAX_MPROTECT)
+#endif
+
+	   )
+	{
+		*flags &= ~PF_PAX_RANDEXEC;
+		retval = -EINVAL;
+	}
+
+	return retval;
+}
+
+EXPORT_SYMBOL(pax_check_flags);
+
+#if defined(CONFIG_GRKERNSEC_PAX_PAGEEXEC) || defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC)
+void pax_report_fault(struct pt_regs *regs, void *pc, void *sp)
+{
+	struct task_struct *tsk = current;
+	struct mm_struct *mm = current->mm;
+	char* buffer_exec = (char*)__get_free_page(GFP_ATOMIC);
+	char* buffer_fault = (char*)__get_free_page(GFP_ATOMIC);
+	char* path_exec=NULL;
+	char* path_fault=NULL;
+	unsigned long start=0UL, end=0UL, offset=0UL;
+
+	if (buffer_exec && buffer_fault) {
+		struct vm_area_struct* vma, * vma_exec=NULL, * vma_fault=NULL;
+
+		down_read(&mm->mmap_sem);
+		vma = mm->mmap;
+		while (vma && (!vma_exec || !vma_fault)) {
+			if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file)
+				vma_exec = vma;
+			if (vma->vm_start <= (unsigned long)pc && (unsigned long)pc < vma->vm_end)
+				vma_fault = vma;
+			vma = vma->vm_next;
+		}
+		if (vma_exec) {
+			path_exec = d_path(vma_exec->vm_file->f_dentry, vma_exec->vm_file->f_vfsmnt, buffer_exec, PAGE_SIZE);
+			if (IS_ERR(path_exec))
+				path_exec = "<path too long>";
+		}
+		if (vma_fault) {
+			start = vma_fault->vm_start;
+			end = vma_fault->vm_end;
+			offset = vma_fault->vm_pgoff << PAGE_SHIFT;
+			if (vma_fault->vm_file) {
+				path_fault = d_path(vma_fault->vm_file->f_dentry, vma_fault->vm_file->f_vfsmnt, buffer_fault, PAGE_SIZE);
+				if (IS_ERR(path_fault))
+					path_fault = "<path too long>";
+			} else
+				path_fault = "<anonymous mapping>";
+		}
+		up_read(&mm->mmap_sem);
+	}
+	if (tsk->curr_ip) {
+		printk(KERN_ERR "PAX: From %u.%u.%u.%u: execution attempt in: %s, %08lx-%08lx %08lx\n", NIPQUAD(tsk->curr_ip), path_fault, start, end, offset);
+		printk(KERN_ERR "PAX: From %u.%u.%u.%u: terminating task: %s(%s):%d, uid/euid: %u/%u, "
+				"PC: %p, SP: %p\n", NIPQUAD(tsk->curr_ip), path_exec, tsk->comm, tsk->pid,
+				tsk->uid, tsk->euid, pc, sp);
+	} else {
+		printk(KERN_ERR "PAX: execution attempt in: %s, %08lx-%08lx %08lx\n", path_fault, start, end, offset);
+		printk(KERN_ERR "PAX: terminating task: %s(%s):%d, uid/euid: %u/%u, "
+				"PC: %p, SP: %p\n", path_exec, tsk->comm, tsk->pid,
+				tsk->uid, tsk->euid, pc, sp);
+	}
+	if (buffer_exec) free_page((unsigned long)buffer_exec);
+	if (buffer_fault) free_page((unsigned long)buffer_fault);
+	pax_report_insns(pc);
+	do_coredump(SIGKILL, regs);
+}
+#endif
+
 int do_coredump(long signr, struct pt_regs * regs)
 {
 	struct linux_binfmt * binfmt;
@@ -1122,6 +1421,11 @@
 		current->fsuid = 0;
 	}
 	current->mm->dumpable = 0;
+
+	if (signr == SIGKILL || signr == SIGILL)
+		gr_handle_brute_attach(current);
+
+	gr_learn_resource(current, RLIMIT_CORE, binfmt->min_coredump, 1);
 	if (current->rlim[RLIMIT_CORE].rlim_cur < binfmt->min_coredump)
 		goto fail;
 
@@ -1141,7 +1445,7 @@
 		goto close_fail;
 	if (!file->f_op->write)
 		goto close_fail;
-	if (do_truncate(file->f_dentry, 0) != 0)
+	if (do_truncate(file->f_dentry, 0, file->f_vfsmnt) != 0)
 		goto close_fail;
 
 	retval = binfmt->core_dump(signr, regs, file);
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/fat/cache.c linux-2.4.27-leo/fs/fat/cache.c
--- linux-2.4.27/fs/fat/cache.c	2001-10-12 21:48:42.000000000 +0100
+++ linux-2.4.27-leo/fs/fat/cache.c	2004-09-17 03:19:36.000000000 +0100
@@ -14,6 +14,7 @@
 #include <linux/string.h>
 #include <linux/stat.h>
 #include <linux/fat_cvf.h>
+#include <linux/sched.h>
 
 #if 0
 #  define PRINTK(x) printk x
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/fat/inode.c linux-2.4.27-leo/fs/fat/inode.c
--- linux-2.4.27/fs/fat/inode.c	2004-05-19 21:34:40.000000000 +0100
+++ linux-2.4.27-leo/fs/fat/inode.c	2004-09-17 03:19:13.000000000 +0100
@@ -8,6 +8,12 @@
  *  Fixes:
  *
  *  	Max Cohan: Fixed invalid FSINFO offset when info_sector is 0
+ *
+ *  Added:
+ *
+ *      Paul Evans: Added fmode/dmode options for separate file/dir permissions
+ *                  See also include/linux/msdos_fs_sb.h
+ *
  */
 
 #include <linux/module.h>
@@ -75,6 +81,9 @@
 static struct list_head fat_inode_hashtable[FAT_HASH_SIZE];
 spinlock_t fat_inode_lock = SPIN_LOCK_UNLOCKED;
 
+/* mask for inode permission bits - used by fmode/dmode */
+#define MODEMASK (~0777)
+
 void fat_hash_init(void)
 {
 	int i;
@@ -220,6 +229,8 @@
 	opts->fs_uid = current->uid;
 	opts->fs_gid = current->gid;
 	opts->fs_umask = current->fs->umask;
+	opts->fs_fmode = 0666; /* rw-rw-rw- */
+	opts->fs_dmode = 0777; /* rwxrwxrwx */
 	opts->quiet = opts->sys_immutable = opts->dotsOK = opts->showexec = 0;
 	opts->codepage = 0;
 	opts->nocase = 0;
@@ -299,6 +310,20 @@
 				if (*value) ret = 0;
 			}
 		}
+		else if (!strcmp(this_char,"fmode")) {
+			if (!value || !*value) ret = 0;
+			else {
+				opts->fs_fmode = simple_strtoul(value,&value,8);
+				if (*value) ret = 0;
+			}
+		}
+		else if (!strcmp(this_char,"dmode")) {
+			if (!value || !*value) ret = 0;
+			else {
+				opts->fs_dmode = simple_strtoul(value,&value,8);
+				if (*value) ret = 0;
+			}
+		}
 		else if (!strcmp(this_char,"debug")) {
 			if (value) ret = 0;
 			else *debug = 1;
@@ -385,7 +410,7 @@
 	inode->i_gid = sbi->options.fs_gid;
 	inode->i_version = ++event;
 	inode->i_generation = 0;
-	inode->i_mode = (S_IRWXUGO & ~sbi->options.fs_umask) | S_IFDIR;
+	inode->i_mode = (S_IRWXUGO & ~sbi->options.fs_umask & (sbi->options.fs_dmode | MODEMASK)) | S_IFDIR;
 	inode->i_op = sbi->dir_ops;
 	inode->i_fop = &fat_dir_operations;
 	if (sbi->fat_bits == 32) {
@@ -745,9 +770,10 @@
 	if (error || debug) {
 		/* The MSDOS_CAN_BMAP is obsolete, but left just to remember */
 		printk("[MS-DOS FS Rel. 12,FAT %d,check=%c,conv=%c,"
-		       "uid=%d,gid=%d,umask=%03o%s]\n",
+		       "uid=%d,gid=%d,umask=%03o,fmode=%03o,dmode=%03o%s]\n",
 		       sbi->fat_bits,opts.name_check,
 		       opts.conversion,opts.fs_uid,opts.fs_gid,opts.fs_umask,
+		       opts.fs_fmode,opts.fs_dmode,
 		       MSDOS_CAN_BMAP(sbi) ? ",bmap" : "");
 		printk("[me=0x%x,cs=%d,#f=%d,fs=%d,fl=%ld,ds=%ld,de=%d,data=%ld,"
 		       "se=%u,ts=%u,ls=%d,rc=%ld,fc=%u]\n",
@@ -913,7 +939,7 @@
 	if ((de->attr & ATTR_DIR) && !IS_FREE(de->name)) {
 		inode->i_generation &= ~1;
 		inode->i_mode = MSDOS_MKMODE(de->attr,S_IRWXUGO &
-		    ~sbi->options.fs_umask) | S_IFDIR;
+		    ~sbi->options.fs_umask & (sbi->options.fs_dmode | MODEMASK)) | S_IFDIR;
 		inode->i_op = sbi->dir_ops;
 		inode->i_fop = &fat_dir_operations;
 
@@ -946,7 +972,7 @@
 		    ((sbi->options.showexec &&
 		       !is_exec(de->ext))
 		    	? S_IRUGO|S_IWUGO : S_IRWXUGO)
-		    & ~sbi->options.fs_umask) | S_IFREG;
+		    & ~sbi->options.fs_umask & (sbi->options.fs_fmode | MODEMASK)) | S_IFREG;
 		MSDOS_I(inode)->i_start = CF_LE_W(de->start);
 		if (sbi->fat_bits == 32)
 			MSDOS_I(inode)->i_start |= (CF_LE_W(de->starthi) << 16);
@@ -1037,6 +1063,7 @@
 	struct super_block *sb = dentry->d_sb;
 	struct inode *inode = dentry->d_inode;
 	int error;
+	unsigned int mode;
 
 	/* FAT cannot truncate to a longer file */
 	if (attr->ia_valid & ATTR_SIZE) {
@@ -1063,12 +1090,17 @@
 	if (error)
 		return error;
 
-	if (S_ISDIR(inode->i_mode))
+	if (S_ISDIR(inode->i_mode)) {
 		inode->i_mode |= S_IXUGO;
+		mode = ~MSDOS_SB(sb)->options.fs_umask & (MSDOS_SB(sb)->options.fs_dmode | MODEMASK);
+		}
+	else
+		mode = ~MSDOS_SB(sb)->options.fs_umask & (MSDOS_SB(sb)->options.fs_fmode | MODEMASK);
+
+	inode->i_mode = ((inode->i_mode & S_IFMT) | 
+			 ((((inode->i_mode & S_IRWXU & mode) | S_IRUSR) >> 6)*S_IXUGO)
+			) & mode;
 
-	inode->i_mode = ((inode->i_mode & S_IFMT) | ((((inode->i_mode & S_IRWXU
-	    & ~MSDOS_SB(sb)->options.fs_umask) | S_IRUSR) >> 6)*S_IXUGO)) &
-	    ~MSDOS_SB(sb)->options.fs_umask;
 	return 0;
 }
 MODULE_LICENSE("GPL");
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/fcntl.c linux-2.4.27-leo/fs/fcntl.c
--- linux-2.4.27/fs/fcntl.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.27-leo/fs/fcntl.c	2004-09-17 03:19:58.000000000 +0100
@@ -12,6 +12,7 @@
 #include <linux/slab.h>
 #include <linux/iobuf.h>
 #include <linux/ptrace.h>
+#include <linux/grsecurity.h>
 
 #include <asm/poll.h>
 #include <asm/siginfo.h>
@@ -65,6 +66,8 @@
 	int error;
 	int start;
 
+	gr_learn_resource(current, RLIMIT_NOFILE, orig_start, 0);
+
 	write_lock(&files->file_lock);
 	
 	error = -EINVAL;
@@ -87,6 +90,7 @@
 	}
 	
 	error = -EMFILE;
+	gr_learn_resource(current, RLIMIT_NOFILE, newfd, 0);
 	if (newfd >= current->rlim[RLIMIT_NOFILE].rlim_cur)
 		goto out;
 
@@ -142,6 +146,8 @@
 	struct file * file, *tofree;
 	struct files_struct * files = current->files;
 
+	gr_learn_resource(current, RLIMIT_NOFILE, newfd, 0);
+
 	write_lock(&files->file_lock);
 	if (!(file = fcheck(oldfd)))
 		goto out_unlock;
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/jbd/journal.c linux-2.4.27-leo/fs/jbd/journal.c
--- linux-2.4.27/fs/jbd/journal.c	2004-05-19 21:34:40.000000000 +0100
+++ linux-2.4.27-leo/fs/jbd/journal.c	2004-09-17 03:19:08.000000000 +0100
@@ -1804,9 +1804,9 @@
 
 		if (buffer_jbd(bh)) {
 			/* Someone did it for us! */
-			J_ASSERT_BH(bh, bh->b_private != NULL);
+ 			J_ASSERT_BH(bh, bh->b_journal_head != NULL);
 			journal_free_journal_head(jh);
-			jh = bh->b_private;
+ 			jh = bh->b_journal_head;
 		} else {
 			/*
 			 * We actually don't need jh_splice_lock when
@@ -1814,7 +1814,7 @@
 			 */
 			spin_lock(&jh_splice_lock);
 			set_bit(BH_JBD, &bh->b_state);
-			bh->b_private = jh;
+			bh->b_journal_head = jh;
 			jh->b_bh = bh;
 			atomic_inc(&bh->b_count);
 			spin_unlock(&jh_splice_lock);
@@ -1823,7 +1823,7 @@
 	}
 	jh->b_jcount++;
 	spin_unlock(&journal_datalist_lock);
-	return bh->b_private;
+	return bh->b_journal_head;
 }
 
 /*
@@ -1856,7 +1856,7 @@
 			J_ASSERT_BH(bh, jh2bh(jh) == bh);
 			BUFFER_TRACE(bh, "remove journal_head");
 			spin_lock(&jh_splice_lock);
-			bh->b_private = NULL;
+			bh->b_journal_head = NULL;
 			jh->b_bh = NULL;	/* debug, really */
 			clear_bit(BH_JBD, &bh->b_state);
 			__brelse(bh);
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/Makefile linux-2.4.27-leo/fs/Makefile
--- linux-2.4.27/fs/Makefile	2004-02-20 14:11:44.000000000 +0000
+++ linux-2.4.27-leo/fs/Makefile	2004-09-17 03:19:58.000000000 +0100
@@ -7,7 +7,7 @@
 
 O_TARGET := fs.o
 
-export-objs :=	filesystems.o open.o dcache.o buffer.o dquot.o
+export-objs :=	filesystems.o open.o dcache.o buffer.o dquot.o exec.o
 mod-subdirs :=	nls
 
 obj-y :=	open.o read_write.o devices.o file_table.o buffer.o \
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/namei.c linux-2.4.27-leo/fs/namei.c
--- linux-2.4.27/fs/namei.c	2004-09-17 02:38:46.000000000 +0100
+++ linux-2.4.27-leo/fs/namei.c	2004-09-17 03:19:58.000000000 +0100
@@ -22,6 +22,7 @@
 #include <linux/dnotify.h>
 #include <linux/smp_lock.h>
 #include <linux/personality.h>
+#include <linux/grsecurity.h>
 
 #include <asm/namei.h>
 #include <asm/uaccess.h>
@@ -343,6 +344,13 @@
 		current->state = TASK_RUNNING;
 		schedule();
 	}
+
+	if (gr_handle_follow_link(dentry->d_parent->d_inode,
+				  dentry->d_inode, dentry, nd->mnt)) {
+		path_release(nd);
+		return -EACCES;
+	}
+
 	current->link_count++;
 	current->total_link_count++;
 	UPDATE_ATIME(dentry->d_inode);
@@ -643,6 +651,10 @@
 			}
 		}
 return_base:
+		if (!gr_acl_handle_hidden_file(nd->dentry, nd->mnt)) {
+			path_release(nd);
+			return -ENOENT;
+		}
 		return 0;
 out_dput:
 		dput(dentry);
@@ -1005,7 +1017,7 @@
 	struct dentry *dentry;
 	struct dentry *dir;
 	int count = 0;
-
+	
 	acc_mode = ACC_MODE(flag);
 
 	/*
@@ -1015,7 +1027,19 @@
 		error = path_lookup(pathname, lookup_flags(flag), nd);
 		if (error)
 			return error;
+
+		if (gr_handle_rawio(nd->dentry->d_inode)) {
+			error = -EPERM;
+			goto exit;
+		}
+	
+		if (!gr_acl_handle_open(nd->dentry, nd->mnt, flag)) {
+			error = -EACCES;
+			goto exit;
+		}
+
 		dentry = nd->dentry;
+
 		goto ok;
 	}
 
@@ -1048,8 +1072,22 @@
 
 	/* Negative dentry, just create the file */
 	if (!dentry->d_inode) {
+		if (gr_handle_chroot_chmod(dentry, nd->mnt, mode)) {
+			error = -EACCES;
+			up(&dir->d_inode->i_sem);
+			goto exit_dput;
+		}
+		if (!gr_acl_handle_creat(dentry, nd->dentry, nd->mnt, flag, mode)) {
+			error = -EACCES;
+			up(&dir->d_inode->i_sem);
+			goto exit_dput;
+		}
+
 		error = vfs_create(dir->d_inode, dentry,
 				   mode & ~current->fs->umask);
+		if (!error)
+			gr_handle_create(dentry, nd->mnt);
+
 		up(&dir->d_inode->i_sem);
 		dput(nd->dentry);
 		nd->dentry = dentry;
@@ -1058,12 +1096,34 @@
 		/* Don't check for write permission, don't truncate */
 		acc_mode = 0;
 		flag &= ~O_TRUNC;
+
 		goto ok;
 	}
 
 	/*
 	 * It already exists.
 	 */
+
+	if (gr_handle_rawio(dentry->d_inode)) {
+		error = -EPERM;
+		up(&dir->d_inode->i_sem);
+		goto exit_dput;
+	}
+
+	if (!gr_acl_handle_open(dentry, nd->mnt, flag)) {
+		error = -EACCES;
+		up(&dir->d_inode->i_sem);
+		goto exit_dput;
+	}
+
+	inode = dentry->d_inode;
+
+	if (gr_handle_fifo(dentry, nd->mnt, dir, flag, acc_mode)) {
+		up(&dir->d_inode->i_sem);
+		error = -EACCES;
+		goto exit_dput;
+	}
+
 	up(&dir->d_inode->i_sem);
 
 	error = -EEXIST;
@@ -1153,7 +1213,7 @@
 		if (!error) {
 			DQUOT_INIT(inode);
 			
-			error = do_truncate(dentry, 0);
+			error = do_truncate(dentry,0,nd->mnt);
 		}
 		put_write_access(inode);
 		if (error)
@@ -1184,6 +1244,13 @@
 	 * stored in nd->last.name and we will have to putname() it when we
 	 * are done. Procfs-like symlinks just set LAST_BIND.
 	 */
+
+	if (gr_handle_follow_link(dentry->d_parent->d_inode, dentry->d_inode,
+				  dentry, nd->mnt)) {
+		error = -EACCES;
+		goto exit_dput;
+	}
+
 	UPDATE_ATIME(dentry->d_inode);
 	error = dentry->d_inode->i_op->follow_link(dentry, nd);
 	dput(dentry);
@@ -1282,6 +1349,19 @@
 
 	mode &= ~current->fs->umask;
 	if (!IS_ERR(dentry)) {
+		if (gr_handle_chroot_mknod(dentry, nd.mnt, mode) ||
+		    gr_handle_chroot_chmod(dentry, nd.mnt, mode)) {
+			error = -EPERM;
+			dput(dentry);
+			goto out_dput;
+		}
+
+		if (!gr_acl_handle_mknod(dentry, nd.dentry, nd.mnt, mode)) {
+			error = -EACCES;
+			dput(dentry);
+			goto out_dput;
+		}
+	
 		switch (mode & S_IFMT) {
 		case 0: case S_IFREG:
 			error = vfs_create(nd.dentry->d_inode,dentry,mode);
@@ -1295,8 +1375,13 @@
 		default:
 			error = -EINVAL;
 		}
+
+		if(!error)
+			gr_handle_create(dentry, nd.mnt);
+
 		dput(dentry);
 	}
+out_dput:
 	up(&nd.dentry->d_inode->i_sem);
 	path_release(&nd);
 out:
@@ -1348,8 +1433,17 @@
 		dentry = lookup_create(&nd, 1);
 		error = PTR_ERR(dentry);
 		if (!IS_ERR(dentry)) {
-			error = vfs_mkdir(nd.dentry->d_inode, dentry,
+			error = 0;
+
+			if (!gr_acl_handle_mkdir(dentry, nd.dentry, nd.mnt))
+				error = -EACCES;
+
+			if(!error)
+				error = vfs_mkdir(nd.dentry->d_inode, dentry,
 					  mode & ~current->fs->umask);
+			if(!error)
+				gr_handle_create(dentry, nd.mnt);
+			
 			dput(dentry);
 		}
 		up(&nd.dentry->d_inode->i_sem);
@@ -1433,6 +1527,8 @@
 	char * name;
 	struct dentry *dentry;
 	struct nameidata nd;
+	ino_t saved_ino = 0;
+	kdev_t saved_dev = 0;
 
 	name = getname(pathname);
 	if(IS_ERR(name))
@@ -1457,7 +1553,22 @@
 	dentry = lookup_hash(&nd.last, nd.dentry);
 	error = PTR_ERR(dentry);
 	if (!IS_ERR(dentry)) {
-		error = vfs_rmdir(nd.dentry->d_inode, dentry);
+		error = 0;
+		if (dentry->d_inode) {
+			if (dentry->d_inode->i_nlink <= 1) {
+				saved_ino = dentry->d_inode->i_ino;
+				saved_dev = dentry->d_inode->i_dev;
+			}
+
+			if (!gr_acl_handle_rmdir(dentry, nd.mnt))
+				error = -EACCES;
+		}
+
+		if (!error)
+			error = vfs_rmdir(nd.dentry->d_inode, dentry);
+		if (!error && (saved_dev || saved_ino))
+			gr_handle_delete(saved_ino,saved_dev);
+
 		dput(dentry);
 	}
 	up(&nd.dentry->d_inode->i_sem);
@@ -1501,6 +1612,8 @@
 	char * name;
 	struct dentry *dentry;
 	struct nameidata nd;
+	ino_t saved_ino = 0;
+	kdev_t saved_dev = 0;
 
 	name = getname(pathname);
 	if(IS_ERR(name))
@@ -1519,7 +1632,21 @@
 		/* Why not before? Because we want correct error value */
 		if (nd.last.name[nd.last.len])
 			goto slashes;
-		error = vfs_unlink(nd.dentry->d_inode, dentry);
+		error = 0;
+		if (dentry->d_inode) {
+			if (dentry->d_inode->i_nlink <= 1) {
+				saved_ino = dentry->d_inode->i_ino;
+				saved_dev = dentry->d_inode->i_dev;
+			}
+
+			if (!gr_acl_handle_unlink(dentry, nd.mnt))
+				error = -EACCES;
+		}
+
+		if (!error)
+			error = vfs_unlink(nd.dentry->d_inode, dentry);
+		if (!error && (saved_ino || saved_dev))
+			gr_handle_delete(saved_ino,saved_dev);
 	exit2:
 		dput(dentry);
 	}
@@ -1583,7 +1710,15 @@
 		dentry = lookup_create(&nd, 0);
 		error = PTR_ERR(dentry);
 		if (!IS_ERR(dentry)) {
-			error = vfs_symlink(nd.dentry->d_inode, dentry, from);
+			error = 0;
+
+			if (!gr_acl_handle_symlink(dentry, nd.dentry, nd.mnt, from))
+				error = -EACCES;
+
+			if(!error)	
+				error = vfs_symlink(nd.dentry->d_inode, dentry, from);
+			if (!error)
+				gr_handle_create(dentry, nd.mnt);
 			dput(dentry);
 		}
 		up(&nd.dentry->d_inode->i_sem);
@@ -1667,7 +1802,27 @@
 		new_dentry = lookup_create(&nd, 0);
 		error = PTR_ERR(new_dentry);
 		if (!IS_ERR(new_dentry)) {
-			error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry);
+			error = 0;
+
+			if (gr_handle_hardlink(old_nd.dentry, old_nd.mnt,
+					       old_nd.dentry->d_inode,
+					       old_nd.dentry->d_inode->i_mode, to)) {
+				error = -EPERM;
+				goto out_error;
+			}
+
+			if (!gr_acl_handle_link(new_dentry, nd.dentry, nd.mnt,
+						 old_nd.dentry, old_nd.mnt, to)) {
+				error = -EACCES;
+				goto out_error;
+			}
+
+			error = vfs_link(old_nd.dentry, 
+					nd.dentry->d_inode, new_dentry);
+
+			if (!error)
+				gr_handle_create(new_dentry, nd.mnt);
+out_error:
 			dput(new_dentry);
 		}
 		up(&nd.dentry->d_inode->i_sem);
@@ -1898,10 +2053,15 @@
 	if (IS_ERR(new_dentry))
 		goto exit4;
 
-	lock_kernel();
-	error = vfs_rename(old_dir->d_inode, old_dentry,
+	error = gr_acl_handle_rename(new_dentry, newnd.dentry, newnd.mnt,
+				     old_dentry, old_dir->d_inode, oldnd.mnt, newname);
+
+	if (error == 1) {
+		lock_kernel();
+		error = vfs_rename(old_dir->d_inode, old_dentry,
 				   new_dir->d_inode, new_dentry);
-	unlock_kernel();
+		unlock_kernel();
+	}
 
 	dput(new_dentry);
 exit4:
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/namespace.c linux-2.4.27-leo/fs/namespace.c
--- linux-2.4.27/fs/namespace.c	2004-02-20 14:11:44.000000000 +0000
+++ linux-2.4.27-leo/fs/namespace.c	2004-09-17 03:19:58.000000000 +0100
@@ -15,6 +15,7 @@
 #include <linux/quotaops.h>
 #include <linux/acct.h>
 #include <linux/module.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 
@@ -325,6 +326,8 @@
 			lock_kernel();
 			retval = do_remount_sb(sb, MS_RDONLY, 0);
 			unlock_kernel();
+
+			gr_log_remount(mnt->mnt_devname, retval);
 		}
 		up_write(&sb->s_umount);
 		return retval;
@@ -350,6 +353,9 @@
 	}
 	spin_unlock(&dcache_lock);
 	up_write(&current->namespace->sem);
+
+	gr_log_unmount(mnt->mnt_devname, retval);
+
 	return retval;
 }
 
@@ -732,6 +738,12 @@
 	if (retval)
 		return retval;
 
+	if (gr_handle_chroot_mount(nd.dentry, nd.mnt, dev_name)) {
+		retval = -EPERM;
+		path_release(&nd);
+		return retval;
+	}
+
 	if (flags & MS_REMOUNT)
 		retval = do_remount(&nd, flags & ~MS_REMOUNT, mnt_flags,
 				    data_page);
@@ -743,6 +755,9 @@
 		retval = do_add_mount(&nd, type_page, flags, mnt_flags,
 				      dev_name, data_page);
 	path_release(&nd);
+
+	gr_log_mount(dev_name, dir_name, retval);
+
 	return retval;
 }
 
@@ -912,6 +927,9 @@
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 
+	if (gr_handle_chroot_pivot())
+		return -EPERM;
+
 	lock_kernel();
 
 	error = __user_walk(new_root, LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &new_nd);
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/nfsd/nfssvc.c linux-2.4.27-leo/fs/nfsd/nfssvc.c
--- linux-2.4.27/fs/nfsd/nfssvc.c	2002-11-28 23:53:15.000000000 +0000
+++ linux-2.4.27-leo/fs/nfsd/nfssvc.c	2004-09-17 03:19:36.000000000 +0100
@@ -250,6 +250,7 @@
 	svc_exit_thread(rqstp);
 
 	/* Release module */
+	unlock_kernel();
 	MOD_DEC_USE_COUNT;
 }
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/nls/nls_base.c linux-2.4.27-leo/fs/nls/nls_base.c
--- linux-2.4.27/fs/nls/nls_base.c	2002-08-03 01:39:45.000000000 +0100
+++ linux-2.4.27-leo/fs/nls/nls_base.c	2004-09-17 03:19:36.000000000 +0100
@@ -18,6 +18,7 @@
 #ifdef CONFIG_KMOD
 #include <linux/kmod.h>
 #endif
+#include <linux/sched.h>
 #include <linux/spinlock.h>
 
 static struct nls_table *tables;
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/open.c linux-2.4.27-leo/fs/open.c
--- linux-2.4.27/fs/open.c	2004-02-20 14:11:44.000000000 +0000
+++ linux-2.4.27-leo/fs/open.c	2004-09-17 03:19:58.000000000 +0100
@@ -15,6 +15,7 @@
 #include <linux/slab.h>
 #include <linux/tty.h>
 #include <linux/iobuf.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 
@@ -95,7 +96,7 @@
 	write_unlock(&files->file_lock);
 }
 
-int do_truncate(struct dentry *dentry, loff_t length)
+int do_truncate(struct dentry *dentry, loff_t length, struct vfsmount *mnt)
 {
 	struct inode *inode = dentry->d_inode;
 	int error;
@@ -105,6 +106,9 @@
 	if (length < 0)
 		return -EINVAL;
 
+	if (!gr_acl_handle_truncate(dentry, mnt))
+		return -EACCES;
+
 	down_write(&inode->i_alloc_sem);
 	down(&inode->i_sem);
 	newattrs.ia_size = length;
@@ -165,7 +169,7 @@
 	error = locks_verify_truncate(inode, NULL, length);
 	if (!error) {
 		DQUOT_INIT(inode);
-		error = do_truncate(nd.dentry, length);
+		error = do_truncate(nd.dentry, length, nd.mnt);
 	}
 	put_write_access(inode);
 
@@ -217,7 +221,7 @@
 
 	error = locks_verify_truncate(inode, file, length);
 	if (!error)
-		error = do_truncate(dentry, length);
+		error = do_truncate(dentry, length, file->f_vfsmnt);
 out_putf:
 	fput(file);
 out:
@@ -292,6 +296,12 @@
 		    (error = permission(inode,MAY_WRITE)) != 0)
 			goto dput_and_out;
 	}
+
+	if (!gr_acl_handle_utime(nd.dentry, nd.mnt)) {
+		error = -EACCES;
+		goto dput_and_out;
+	}
+
 	error = notify_change(nd.dentry, &newattrs);
 dput_and_out:
 	path_release(&nd);
@@ -344,6 +354,12 @@
 		    (error = permission(inode,MAY_WRITE)) != 0)
 			goto dput_and_out;
 	}
+
+	if (!gr_acl_handle_utime(nd.dentry, nd.mnt)) {
+		error = -EACCES;
+		goto dput_and_out;
+	}
+
 	error = notify_change(nd.dentry, &newattrs);
 dput_and_out:
 	path_release(&nd);
@@ -386,6 +402,10 @@
 		if(!res && (mode & S_IWOTH) && IS_RDONLY(nd.dentry->d_inode)
 		   && !special_file(nd.dentry->d_inode->i_mode))
 			res = -EROFS;
+		
+		if (!res && !gr_acl_handle_access(nd.dentry, nd.mnt, mode))
+			res =  -EACCES;
+
 		path_release(&nd);
 	}
 
@@ -409,6 +429,8 @@
 	if (error)
 		goto dput_and_out;
 
+	gr_log_chdir(nd.dentry, nd.mnt);
+
 	set_fs_pwd(current->fs, nd.mnt, nd.dentry);
 
 dput_and_out:
@@ -439,6 +461,13 @@
 		goto out_putf;
 
 	error = permission(inode, MAY_EXEC);
+
+	if (!error && !gr_chroot_fchdir(dentry, mnt))
+		error = -EPERM;
+
+	if (!error)
+		gr_log_chdir(dentry, mnt);
+
 	if (!error)
 		set_fs_pwd(current->fs, mnt, dentry);
 out_putf:
@@ -465,8 +494,16 @@
 	if (!capable(CAP_SYS_CHROOT))
 		goto dput_and_out;
 
+	if (gr_handle_chroot_chroot(nd.dentry, nd.mnt))
+		goto dput_and_out;
+
 	set_fs_root(current->fs, nd.mnt, nd.dentry);
 	set_fs_altroot();
+
+	gr_handle_chroot_caps(current);
+
+	gr_handle_chroot_chdir(nd.dentry, nd.mnt);
+
 	error = 0;
 dput_and_out:
 	path_release(&nd);
@@ -495,8 +532,20 @@
 	err = -EPERM;
 	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
 		goto out_putf;
+
+	if (!gr_acl_handle_fchmod(dentry, file->f_vfsmnt, mode)) {
+		err = -EACCES;
+		goto out_putf;
+	}
+
 	if (mode == (mode_t) -1)
 		mode = inode->i_mode;
+
+	if (gr_handle_chroot_chmod(dentry, file->f_vfsmnt, mode)) {
+		err = -EPERM;
+		goto out_putf;
+	}	    
+
 	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
 	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
 	err = notify_change(dentry, &newattrs);
@@ -527,8 +576,19 @@
 	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
 		goto dput_and_out;
 
+	if (!gr_acl_handle_chmod(nd.dentry, nd.mnt, mode)) {
+		error = -EACCES;
+		goto dput_and_out;
+	}
+
 	if (mode == (mode_t) -1)
 		mode = inode->i_mode;
+
+	if (gr_handle_chroot_chmod(nd.dentry, nd.mnt, mode)) {
+		error = -EACCES;
+		goto dput_and_out;
+	}
+
 	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
 	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
 	error = notify_change(nd.dentry, &newattrs);
@@ -539,7 +599,7 @@
 	return error;
 }
 
-static int chown_common(struct dentry * dentry, uid_t user, gid_t group)
+static int chown_common(struct dentry * dentry, uid_t user, gid_t group, struct vfsmount *mnt)
 {
 	struct inode * inode;
 	int error;
@@ -556,6 +616,12 @@
 	error = -EPERM;
 	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
 		goto out;
+
+	if (!gr_acl_handle_chown(dentry, mnt)) {
+		error = -EACCES;
+		goto out;
+	}
+
 	if (user == (uid_t) -1)
 		user = inode->i_uid;
 	if (group == (gid_t) -1)
@@ -606,7 +672,7 @@
 
 	error = user_path_walk(filename, &nd);
 	if (!error) {
-		error = chown_common(nd.dentry, user, group);
+		error = chown_common(nd.dentry, user, group, nd.mnt);
 		path_release(&nd);
 	}
 	return error;
@@ -619,7 +685,7 @@
 
 	error = user_path_walk_link(filename, &nd);
 	if (!error) {
-		error = chown_common(nd.dentry, user, group);
+		error = chown_common(nd.dentry, user, group, nd.mnt);
 		path_release(&nd);
 	}
 	return error;
@@ -633,7 +699,8 @@
 
 	file = fget(fd);
 	if (file) {
-		error = chown_common(file->f_dentry, user, group);
+		error = chown_common(file->f_dentry, user,
+				group, file->f_vfsmnt);
 		fput(file);
 	}
 	return error;
@@ -753,6 +820,7 @@
 	 * N.B. For clone tasks sharing a files structure, this test
 	 * will limit the total number of files that can be opened.
 	 */
+	gr_learn_resource(current, RLIMIT_NOFILE, fd, 0);
 	if (fd >= current->rlim[RLIMIT_NOFILE].rlim_cur)
 		goto out;
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/proc/array.c linux-2.4.27-leo/fs/proc/array.c
--- linux-2.4.27/fs/proc/array.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.27-leo/fs/proc/array.c	2004-09-17 03:19:58.000000000 +0100
@@ -273,6 +273,18 @@
 			    cap_t(p->cap_effective));
 }
 
+#if defined(CONFIG_GRKERNSEC_PAX_NOEXEC) || defined(CONFIG_GRKERNSEC_PAX_ASLR)
+static inline char *task_pax(struct task_struct *p, char *buffer)
+{
+	return buffer + sprintf(buffer, "PaX:\t%c%c%c%c%c%c\n",
+				p->flags & PF_PAX_PAGEEXEC ? 'P' : 'p',
+				p->flags & PF_PAX_EMUTRAMP ? 'E' : 'e',
+				p->flags & PF_PAX_MPROTECT ? 'M' : 'm',
+				p->flags & PF_PAX_RANDMMAP ? 'R' : 'r',
+				p->flags & PF_PAX_RANDEXEC ? 'X' : 'x',
+				p->flags & PF_PAX_SEGMEXEC ? 'S' : 's');
+}
+#endif
 
 int proc_pid_status(struct task_struct *task, char * buffer)
 {
@@ -295,9 +307,20 @@
 #if defined(CONFIG_ARCH_S390)
 	buffer = task_show_regs(task, buffer);
 #endif
+
+#if defined(CONFIG_GRKERNSEC_PAX_NOEXEC) || defined(CONFIG_GRKERNSEC_PAX_ASLR)
+	buffer = task_pax(task, buffer);
+#endif
+
 	return buffer - orig;
 }
 
+#ifdef CONFIG_GRKERNSEC_PROC_MEMMAP
+#define PAX_RAND_FLAGS (task->flags & PF_PAX_RANDMMAP || \
+		        task->flags & PF_PAX_SEGMEXEC || \
+			task->flags & PF_PAX_RANDEXEC)
+#endif
+
 int proc_pid_stat(struct task_struct *task, char * buffer)
 {
 	unsigned long vsize, eip, esp, wchan;
@@ -335,6 +358,19 @@
 
 	wchan = get_wchan(task);
 
+#ifdef CONFIG_GRKERNSEC_PROC_MEMMAP
+	if (PAX_RAND_FLAGS) {
+		eip = 0;
+		esp = 0;
+		wchan = 0;
+	}
+#endif
+#ifdef CONFIG_GRKERNSEC_HIDESYM
+	wchan = 0;
+	eip = 0;
+	esp = 0;
+#endif
+
 	collect_sigign_sigcatch(task, &sigign, &sigcatch);
 
 	/* scale priority and nice values from timeslices to -20..20 */
@@ -374,9 +410,15 @@
 		vsize,
 		mm ? mm->rss : 0, /* you might want to shift this left 3 */
 		task->rlim[RLIMIT_RSS].rlim_cur,
+#ifdef CONFIG_GRKERNSEC_PROC_MEMMAP
+		PAX_RAND_FLAGS ? 0 : (mm ? mm->start_code : 0),
+		PAX_RAND_FLAGS ? 0 : (mm ? mm->end_code : 0),
+		PAX_RAND_FLAGS ? 0 : (mm ? mm->start_stack : 0),
+#else
 		mm ? mm->start_code : 0,
 		mm ? mm->end_code : 0,
 		mm ? mm->start_stack : 0,
+#endif
 		esp,
 		eip,
 		/* The signal information here is obsolete.
@@ -514,6 +556,7 @@
 
 static int show_map(struct seq_file *m, void *v)
 {
+	struct task_struct *task = m->private;
 	struct vm_area_struct *map = v;
 	struct file *file = map->vm_file;
 	int flags = map->vm_flags;
@@ -528,8 +571,13 @@
 	}
 
 	seq_printf(m, "%08lx-%08lx %c%c%c%c %08lx %02x:%02x %lu %n",
+#ifdef CONFIG_GRKERNSEC_PROC_MEMMAP
+			PAX_RAND_FLAGS ? 0UL : map->vm_start,
+			PAX_RAND_FLAGS ? 0UL : map->vm_end,
+#else
 			map->vm_start,
 			map->vm_end,
+#endif
 			flags & VM_READ ? 'r' : '-',
 			flags & VM_WRITE ? 'w' : '-',
 			flags & VM_EXEC ? 'x' : '-',
@@ -602,6 +650,16 @@
 	.show	= show_map
 };
 
+#ifdef CONFIG_GRKERNSEC_PROC_IPADDR	 
+int proc_pid_ipaddr(struct task_struct *task, char * buffer)	 
+{	 
+	int len;	 
+
+	len = sprintf(buffer, "%u.%u.%u.%u\n", NIPQUAD(task->curr_ip));	 
+	return len;	 
+}	 
+#endif
+
 #ifdef CONFIG_SMP
 int proc_pid_cpu(struct task_struct *task, char * buffer)
 {
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/proc/base.c linux-2.4.27-leo/fs/proc/base.c
--- linux-2.4.27/fs/proc/base.c	2004-09-17 02:38:46.000000000 +0100
+++ linux-2.4.27-leo/fs/proc/base.c	2004-09-17 03:19:58.000000000 +0100
@@ -25,6 +25,7 @@
 #include <linux/string.h>
 #include <linux/seq_file.h>
 #include <linux/namespace.h>
+#include <linux/grsecurity.h>
 
 /*
  * For hysterical raisins we keep the same inumbers as in the old procfs.
@@ -40,6 +41,9 @@
 int proc_pid_status(struct task_struct*,char*);
 int proc_pid_statm(struct task_struct*,char*);
 int proc_pid_cpu(struct task_struct*,char*);
+#ifdef CONFIG_GRKERNSEC_PROC_IPADDR
+int proc_pid_ipaddr(struct task_struct*,char*);
+#endif
 
 static int proc_fd_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt)
 {
@@ -126,7 +130,8 @@
 #define MAY_PTRACE(task) \
 	(task == current || \
 	(task->p_pptr == current && \
-	(task->ptrace & PT_PTRACED) && task->state == TASK_STOPPED))
+	(task->ptrace & PT_PTRACED) && task->state == TASK_STOPPED && \
+	!gr_handle_proc_ptrace(task)))
 
 static int may_ptrace_attach(struct task_struct *task)
 {
@@ -145,6 +150,8 @@
 	rmb();
 	if (!is_dumpable(task) && !capable(CAP_SYS_PTRACE))
 		goto out;
+	if (gr_handle_proc_ptrace(task))
+		goto out;
 
 	retval = 1;
 
@@ -263,9 +270,22 @@
 
 static int proc_permission(struct inode *inode, int mask)
 {
+	int ret;
+	struct task_struct *task;
+
 	if (vfs_permission(inode, mask) != 0)
 		return -EACCES;
-	return proc_check_root(inode);
+	ret = proc_check_root(inode);
+
+	if (ret)
+		return ret;
+
+	task = inode->u.proc_i.task;
+
+	if (!task)
+		return 0;
+
+	return gr_acl_handle_procpidmem(task);
 }
 
 extern struct seq_operations proc_pid_maps_op;
@@ -599,6 +619,9 @@
 	PROC_PID_STATM,
 	PROC_PID_MAPS,
 	PROC_PID_CPU,
+#ifdef CONFIG_GRKERNSEC_PROC_IPADDR
+	PROC_PID_IPADDR,
+#endif
 	PROC_PID_MOUNTS,
 	PROC_PID_FD_DIR = 0x8000,	/* 0x8000-0xffff */
 };
@@ -614,6 +637,9 @@
 #ifdef CONFIG_SMP
   E(PROC_PID_CPU,	"cpu",		S_IFREG|S_IRUGO),
 #endif
+#ifdef CONFIG_GRKERNSEC_PROC_IPADDR
+  E(PROC_PID_IPADDR,	"ipaddr",	S_IFREG|S_IRUSR),
+#endif
   E(PROC_PID_MAPS,	"maps",		S_IFREG|S_IRUGO),
   E(PROC_PID_MEM,	"mem",		S_IFREG|S_IRUSR|S_IWUSR),
   E(PROC_PID_CWD,	"cwd",		S_IFLNK|S_IRWXUGO),
@@ -770,10 +796,17 @@
 	get_task_struct(task);
 	inode->u.proc_i.task = task;
 	inode->i_uid = 0;
+#ifdef CONFIG_GRKERNSEC_PROC_USERGROUP
+	inode->i_gid = CONFIG_GRKERNSEC_PROC_GID;
+#else
 	inode->i_gid = 0;
+#endif
+
 	if (ino == PROC_PID_INO || task_dumpable(task)) {
 		inode->i_uid = task->euid;
+#ifndef CONFIG_GRKERNSEC_PROC_USERGROUP
 		inode->i_gid = task->egid;
+#endif
 	}
 
 out:
@@ -981,6 +1014,12 @@
 			inode->u.proc_i.op.proc_read = proc_pid_cpu;
 			break;
 #endif
+#ifdef CONFIG_GRKERNSEC_PROC_IPADDR
+		case PROC_PID_IPADDR:
+			inode->i_fop = &proc_info_file_operations;
+			inode->u.proc_i.op.proc_read = proc_pid_ipaddr;
+			break;
+#endif
 		case PROC_PID_MEM:
 			inode->i_op = &proc_mem_inode_operations;
 			inode->i_fop = &proc_mem_operations;
@@ -1079,13 +1118,35 @@
 	if (!task)
 		goto out;
 
+	if(gr_check_hidden_task(task)) {
+		free_task_struct(task);
+		goto out;
+	}
+	
+#ifdef CONFIG_GRKERNSEC_PROC
+	if (current->uid && (task->uid != current->uid)
+#ifdef CONFIG_GRKERNSEC_PROC_USERGROUP
+	    && !in_group_p(CONFIG_GRKERNSEC_PROC_GID)
+#endif
+	) {
+		free_task_struct(task);
+		goto out;
+	}	
+#endif
 	inode = proc_pid_make_inode(dir->i_sb, task, PROC_PID_INO);
 
 	free_task_struct(task);
 
 	if (!inode)
 		goto out;
+#ifdef CONFIG_GRKERNSEC_PROC_USER
+	inode->i_mode = S_IFDIR|S_IRUSR|S_IXUSR;
+#elif CONFIG_GRKERNSEC_PROC_USERGROUP
+	inode->i_mode = S_IFDIR|S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP;
+	inode->i_gid = CONFIG_GRKERNSEC_PROC_GID;
+#else
 	inode->i_mode = S_IFDIR|S_IRUGO|S_IXUGO;
+#endif
 	inode->i_op = &proc_base_inode_operations;
 	inode->i_fop = &proc_base_operations;
 	inode->i_nlink = 3;
@@ -1125,6 +1186,18 @@
 		int pid = p->pid;
 		if (!pid)
 			continue;
+		if(gr_pid_is_chrooted(p))
+			continue;
+		if(gr_check_hidden_task(p)) 
+			continue;
+#ifdef CONFIG_GRKERNSEC_PROC
+		if (current->uid && (p->uid != current->uid)
+#ifdef CONFIG_GRKERNSEC_PROC_USERGROUP
+	    		&& !in_group_p(CONFIG_GRKERNSEC_PROC_GID)
+#endif
+		)
+			continue;	
+#endif
 		if (--index >= 0)
 			continue;
 		pids[nr_pids] = pid;
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/proc/generic.c linux-2.4.27-leo/fs/proc/generic.c
--- linux-2.4.27/fs/proc/generic.c	2004-09-17 02:38:46.000000000 +0100
+++ linux-2.4.27-leo/fs/proc/generic.c	2004-09-17 03:19:58.000000000 +0100
@@ -508,6 +508,32 @@
 	return ent;
 }
 
+#ifdef CONFIG_GRKERNSEC_PROC
+struct proc_dir_entry *proc_priv_mkdir(const char *name, struct proc_dir_entry *parent)
+{
+	struct proc_dir_entry *ent;
+	mode_t mode = 0;
+
+#ifdef CONFIG_GRKERNSEC_PROC_USER
+	mode = S_IFDIR | S_IRUSR | S_IXUSR;
+#elif CONFIG_GRKERNSEC_PROC_USERGROUP
+	mode = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP;
+#endif
+
+	ent = proc_create(&parent, name, mode, 2);
+	if (ent) {
+		ent->proc_fops = &proc_dir_operations;
+		ent->proc_iops = &proc_dir_inode_operations;
+
+		if (proc_register(parent, ent) < 0) {
+			kfree(ent);
+			ent = NULL;
+		}
+	}
+	return ent;
+}
+#endif
+
 struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
 					 struct proc_dir_entry *parent)
 {
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/proc/inode.c linux-2.4.27-leo/fs/proc/inode.c
--- linux-2.4.27/fs/proc/inode.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.27-leo/fs/proc/inode.c	2004-09-17 03:19:58.000000000 +0100
@@ -152,7 +152,11 @@
 		if (de->mode) {
 			inode->i_mode = de->mode;
 			inode->i_uid = de->uid;
+#ifdef CONFIG_GRKERNSEC_PROC_USERGROUP
+			inode->i_gid = CONFIG_GRKERNSEC_PROC_GID;
+#else
 			inode->i_gid = de->gid;
+#endif			
 		}
 		if (de->size)
 			inode->i_size = de->size;
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/proc/proc_misc.c linux-2.4.27-leo/fs/proc/proc_misc.c
--- linux-2.4.27/fs/proc/proc_misc.c	2004-09-17 02:38:46.000000000 +0100
+++ linux-2.4.27-leo/fs/proc/proc_misc.c	2004-09-17 03:19:58.000000000 +0100
@@ -591,6 +591,7 @@
 void __init proc_misc_init(void)
 {
 	struct proc_dir_entry *entry;
+	int gr_mode = 0;
 	static struct {
 		char *name;
 		int (*read_proc)(char*,char**,off_t,int,int*,void*);
@@ -605,17 +606,21 @@
 #ifdef CONFIG_STRAM_PROC
 		{"stram",	stram_read_proc},
 #endif
-#ifdef CONFIG_MODULES
+#if defined(CONFIG_MODULES) && !defined(CONFIG_GRKERNSEC_PROC)
 		{"modules",	modules_read_proc},
 #endif
 		{"stat",	kstat_read_proc},
+#ifndef CONFIG_GRKERNSEC_PROC_ADD
 		{"devices",	devices_read_proc},
-#if !defined(CONFIG_ARCH_S390) && !defined(CONFIG_X86)
+#endif
+#if !defined(CONFIG_ARCH_S390) && !defined(CONFIG_X86) && !defined(CONFIG_GRKERNSEC_PROC_ADD)
 		{"interrupts",	interrupts_read_proc},
 #endif
 		{"filesystems",	filesystems_read_proc},
+#ifndef CONFIG_GRKERNSEC_PROC_ADD
 		{"dma",		dma_read_proc},
 		{"cmdline",	cmdline_read_proc},
+#endif
 #ifdef CONFIG_SGI_DS1286
 		{"rtc",		ds1286_read_proc},
 #endif
@@ -627,29 +632,60 @@
 	for (p = simple_ones; p->name; p++)
 		create_proc_read_entry(p->name, 0, NULL, p->read_proc, NULL);
 
+#ifdef CONFIG_GRKERNSEC_PROC_USER
+	gr_mode = S_IRUSR;
+#elif CONFIG_GRKERNSEC_PROC_USERGROUP
+	gr_mode = S_IRUSR | S_IRGRP;
+#endif
+#if defined(CONFIG_GRKERNSEC_PROC) && defined(CONFIG_MODULES)
+	create_proc_read_entry("modules", gr_mode, NULL, &modules_read_proc, NULL);
+#endif
+#ifdef CONFIG_GRKERNSEC_PROC_ADD
+	create_proc_read_entry("devices", gr_mode, NULL, &devices_read_proc, NULL);
+	create_proc_read_entry("dma", gr_mode, NULL, &dma_read_proc, NULL);
+	create_proc_read_entry("cmdline", gr_mode, NULL, &cmdline_read_proc, NULL);
+#if !defined(CONFIG_ARCH_S390) && !defined(CONFIG_X86)
+	create_proc_read_entry("interrupts", gr_mode, NULL, &interrupts_read_proc, NULL);
+#endif
+#endif
+
 	proc_symlink("mounts", NULL, "self/mounts");
 
 	/* And now for trickier ones */
 	entry = create_proc_entry("kmsg", S_IRUSR, &proc_root);
 	if (entry)
 		entry->proc_fops = &proc_kmsg_operations;
+#ifdef CONFIG_GRKERNSEC_PROC_ADD
+	create_seq_entry("cpuinfo", gr_mode, &proc_cpuinfo_operations);
+#else
 	create_seq_entry("cpuinfo", 0, &proc_cpuinfo_operations);
-#if defined(CONFIG_X86)
+#endif
+#if defined(CONFIG_X86) && !defined(CONFIG_GRKERNSEC_PROC_ADD)
 	create_seq_entry("interrupts", 0, &proc_interrupts_operations);
+#elif defined(CONFIG_X86)
+	create_seq_entry("interrupts", gr_mode, &proc_interrupts_operations);
 #endif
+#ifdef CONFIG_GRKERNSEC_PROC_ADD
+	create_seq_entry("ioports", gr_mode, &proc_ioports_operations);
+	create_seq_entry("iomem", gr_mode, &proc_iomem_operations);
+	create_seq_entry("slabinfo",gr_mode,&proc_slabinfo_operations);
+#else
 	create_seq_entry("ioports", 0, &proc_ioports_operations);
 	create_seq_entry("iomem", 0, &proc_iomem_operations);
-	create_seq_entry("partitions", 0, &proc_partitions_operations);
 	create_seq_entry("slabinfo",S_IWUSR|S_IRUGO,&proc_slabinfo_operations);
+#endif
+	create_seq_entry("partitions", 0, &proc_partitions_operations);
 #ifdef CONFIG_MODULES
-	create_seq_entry("ksyms", 0, &proc_ksyms_operations);
+	create_seq_entry("ksyms", gr_mode, &proc_ksyms_operations);
 #endif
+#ifndef CONFIG_GRKERNSEC_PROC_ADD
 	proc_root_kcore = create_proc_entry("kcore", S_IRUSR, NULL);
 	if (proc_root_kcore) {
 		proc_root_kcore->proc_fops = &proc_kcore_operations;
 		proc_root_kcore->size =
 				(size_t)high_memory - PAGE_OFFSET + PAGE_SIZE;
 	}
+#endif
 	if (prof_shift) {
 		entry = create_proc_entry("profile", S_IWUSR | S_IRUGO, NULL);
 		if (entry) {
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/proc/proc_tty.c linux-2.4.27-leo/fs/proc/proc_tty.c
--- linux-2.4.27/fs/proc/proc_tty.c	2000-04-21 23:17:57.000000000 +0100
+++ linux-2.4.27-leo/fs/proc/proc_tty.c	2004-09-17 03:19:58.000000000 +0100
@@ -174,7 +174,11 @@
 	if (!proc_mkdir("tty", 0))
 		return;
 	proc_tty_ldisc = proc_mkdir("tty/ldisc", 0);
+#ifdef CONFIG_GRKERNSEC_PROC
+	proc_tty_driver = proc_priv_mkdir("tty/driver", 0);
+#else
 	proc_tty_driver = proc_mkdir("tty/driver", 0);
+#endif
 
 	create_proc_read_entry("tty/ldiscs", 0, 0, tty_ldiscs_read_proc,NULL);
 	create_proc_read_entry("tty/drivers", 0, 0, tty_drivers_read_proc,NULL);
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/proc/root.c linux-2.4.27-leo/fs/proc/root.c
--- linux-2.4.27/fs/proc/root.c	2002-08-03 01:39:45.000000000 +0100
+++ linux-2.4.27-leo/fs/proc/root.c	2004-09-17 03:19:58.000000000 +0100
@@ -37,13 +37,21 @@
 		return;
 	}
 	proc_misc_init();
+#ifdef CONFIG_GRKERNSEC_PROC
+	proc_net = proc_priv_mkdir("net", 0);
+#else
 	proc_net = proc_mkdir("net", 0);
+#endif
 #ifdef CONFIG_SYSVIPC
 	proc_mkdir("sysvipc", 0);
 #endif
 #ifdef CONFIG_SYSCTL
+#ifdef CONFIG_GRKERNSEC_PROC
+	proc_sys_root = proc_priv_mkdir("sys", 0);
+#else
 	proc_sys_root = proc_mkdir("sys", 0);
 #endif
+#endif
 #if defined(CONFIG_BINFMT_MISC) || defined(CONFIG_BINFMT_MISC_MODULE)
 	proc_mkdir("sys/fs", 0);
 	proc_mkdir("sys/fs/binfmt_misc", 0);
@@ -67,7 +75,12 @@
 #ifdef CONFIG_PPC_RTAS
 	proc_rtas_init();
 #endif
+
+#ifdef CONFIG_GRKERNSEC_PROC_ADD
+	proc_bus = proc_priv_mkdir("bus", 0);
+#else
 	proc_bus = proc_mkdir("bus", 0);
+#endif
 }
 
 static struct dentry *proc_root_lookup(struct inode * dir, struct dentry * dentry)
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/readdir.c linux-2.4.27-leo/fs/readdir.c
--- linux-2.4.27/fs/readdir.c	2004-02-20 14:11:44.000000000 +0000
+++ linux-2.4.27-leo/fs/readdir.c	2004-09-17 03:19:58.000000000 +0100
@@ -10,6 +10,7 @@
 #include <linux/stat.h>
 #include <linux/file.h>
 #include <linux/smp_lock.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 
@@ -182,6 +183,7 @@
 struct readdir_callback {
 	struct old_linux_dirent * dirent;
 	int count;
+	struct nameidata nd;
 };
 
 static int fillonedir(void * __buf, const char * name, int namlen, loff_t offset,
@@ -192,6 +194,10 @@
 
 	if (buf->count)
 		return -EINVAL;
+
+	if (!gr_acl_handle_filldir(buf->nd.dentry, buf->nd.mnt, ino))
+		return 0;
+	    
 	buf->count++;
 	dirent = buf->dirent;
 	put_user(ino, &dirent->d_ino);
@@ -216,6 +222,9 @@
 	buf.count = 0;
 	buf.dirent = dirent;
 
+	buf.nd.dentry = file->f_dentry;
+	buf.nd.mnt = file->f_vfsmnt;
+
 	error = vfs_readdir(file, fillonedir, &buf);
 	if (error >= 0)
 		error = buf.count;
@@ -243,6 +252,7 @@
 	struct linux_dirent * previous;
 	int count;
 	int error;
+	struct nameidata nd;
 };
 
 static int filldir(void * __buf, const char * name, int namlen, loff_t offset,
@@ -255,6 +265,10 @@
 	buf->error = -EINVAL;	/* only used if we fail.. */
 	if (reclen > buf->count)
 		return -EINVAL;
+
+	if (!gr_acl_handle_filldir(buf->nd.dentry, buf->nd.mnt, ino))
+		return 0;
+
 	dirent = buf->previous;
 	if (dirent)
 		put_user(offset, &dirent->d_off);
@@ -287,6 +301,9 @@
 	buf.count = count;
 	buf.error = 0;
 
+	buf.nd.dentry = file->f_dentry;
+	buf.nd.mnt = file->f_vfsmnt;
+
 	error = vfs_readdir(file, filldir, &buf);
 	if (error < 0)
 		goto out_putf;
@@ -321,6 +338,7 @@
 	struct linux_dirent64 * previous;
 	int count;
 	int error;
+	struct nameidata nd;
 };
 
 static int filldir64(void * __buf, const char * name, int namlen, loff_t offset,
@@ -333,6 +351,10 @@
 	buf->error = -EINVAL;	/* only used if we fail.. */
 	if (reclen > buf->count)
 		return -EINVAL;
+
+	if (!gr_acl_handle_filldir(buf->nd.dentry, buf->nd.mnt, ino))
+		return 0;
+	
 	dirent = buf->previous;
 	if (dirent) {
 		d.d_off = offset;
@@ -370,6 +392,9 @@
 	buf.count = count;
 	buf.error = 0;
 
+	buf.nd.mnt = file->f_vfsmnt;
+	buf.nd.dentry = file->f_dentry;
+
 	error = vfs_readdir(file, filldir64, &buf);
 	if (error < 0)
 		goto out_putf;
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/reiserfs/super.c linux-2.4.27-leo/fs/reiserfs/super.c
--- linux-2.4.27/fs/reiserfs/super.c	2003-08-25 12:44:43.000000000 +0100
+++ linux-2.4.27-leo/fs/reiserfs/super.c	2004-09-17 03:19:08.000000000 +0100
@@ -73,7 +73,7 @@
     reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1);
     journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB (s));
     reiserfs_block_writes(&th) ;
-    journal_end(&th, s, 1) ;
+    journal_end_sync(&th, s, 1) ;
   }
   s->s_dirt = dirty;
   unlock_kernel() ;
diff -urN --exclude-from=diff-exclude linux-2.4.27/fs/super.c linux-2.4.27-leo/fs/super.c
--- linux-2.4.27/fs/super.c	2003-08-25 12:44:43.000000000 +0100
+++ linux-2.4.27-leo/fs/super.c	2004-09-17 03:19:08.000000000 +0100
@@ -39,6 +39,12 @@
 spinlock_t sb_lock = SPIN_LOCK_UNLOCKED;
 
 /*
+ * stub of a filesystem used to make sure an FS isn't mounted
+ * in the middle of a lockfs call
+ */
+static DECLARE_FSTYPE_DEV(lockfs_fs_type, "lockfs", NULL);
+
+/*
  * Handling of filesystem drivers list.
  * Rules:
  *	Inclusion to/removals from/scanning of list are protected by spinlock.
@@ -436,6 +442,25 @@
 	put_super(sb);
 }
 
+static void write_super_lockfs(struct super_block *sb)
+{
+	lock_super(sb);
+	if (sb->s_root && sb->s_op) {
+		if (sb->s_dirt && sb->s_op->write_super)
+			sb->s_op->write_super(sb);
+		if (sb->s_op->write_super_lockfs)
+			sb->s_op->write_super_lockfs(sb);
+	}
+	unlock_super(sb);
+
+	/* 
+	 * if no lockfs call is provided, use the sync_fs call instead.
+	 * this must be done without the super lock held
+	 */
+	if (!sb->s_op->write_super_lockfs && sb->s_op->sync_fs)
+		sb->s_op->sync_fs(sb);
+}
+
 static inline void write_super(struct super_block *sb)
 {
 	lock_super(sb);
@@ -483,6 +508,119 @@
 	spin_unlock(&sb_lock);
 }
 
+static struct super_block *find_super_for_lockfs(kdev_t dev)
+{
+	struct super_block *lockfs_sb = alloc_super();
+	struct super_block * s;
+
+	if (!dev)
+		return NULL;
+restart:
+	spin_lock(&sb_lock);
+	s = find_super(dev);
+	if (s) {
+		spin_unlock(&sb_lock);
+		down_read(&s->s_umount);
+		if (s->s_root) {
+			destroy_super(lockfs_sb);
+			return s;
+		}
+		drop_super(s);
+		goto restart;
+	}
+	/* if (s) we either return or goto, so we know s == NULL here.
+	 * At this point, there are no mounted filesystems on this device,
+	 * so we pretend to mount one.
+	 */
+	if (!lockfs_sb) {
+		spin_unlock(&sb_lock);
+		return NULL;
+	}
+	s = lockfs_sb;
+	s->s_dev = dev;
+	if (lockfs_fs_type.fs_supers.prev == NULL)
+		INIT_LIST_HEAD(&lockfs_fs_type.fs_supers);
+	insert_super(s, &lockfs_fs_type);
+	s->s_root = (struct dentry *)1;
+	/* alloc_super gives us a write lock on s_umount, this
+	 * way we know there are no concurrent lockfs holders for this dev.  
+	 * It allows us to remove the temp super from the list of supers 
+	 * immediately when unlockfs is called
+	 */
+	return s;
+}
+/*
+ * Note: don't check the dirty flag before waiting, we want the lock
+ * to happen every time this is called.  dev must be non-zero
+ */
+void sync_supers_lockfs(kdev_t dev)
+{
+	struct super_block *sb;
+	sb = find_super_for_lockfs(dev);
+	if (sb) {
+		write_super_lockfs(sb);
+		/* the drop_super is done by unlockfs */
+	}
+}
+
+static void drop_super_lockfs(struct super_block *s)
+{
+	if (s->s_type == &lockfs_fs_type) {
+		struct file_system_type *fs = s->s_type;
+
+		/* 
+		 * nobody else is allowed to grab_super() on our temp
+		 */
+		if (!deactivate_super(s))
+			BUG();
+
+		spin_lock(&sb_lock);
+		s->s_root = NULL;
+		list_del(&s->s_list);
+		list_del(&s->s_instances);
+		spin_unlock(&sb_lock);
+
+		up_write(&s->s_umount);
+		put_super(s);
+		put_filesystem(fs);
+	} else
+		drop_super(s);
+}
+
+void unlockfs(kdev_t dev)
+{
+	struct super_block *s;
+	if (!dev)
+		return;
+
+	spin_lock(&sb_lock);
+	s = find_super(dev);
+	if (s) {
+		/* 
+		 * find_super and the original lockfs call both incremented
+		 * the reference count.  drop one of them
+		 */
+		s->s_count--;
+		spin_unlock(&sb_lock);
+		if (s->s_root) {
+			if (s->s_op->unlockfs)
+				s->s_op->unlockfs(s);
+			drop_super_lockfs(s);
+			goto out;
+		} else {
+			printk("unlockfs: no s_root, dev %s\n", kdevname(dev));
+			BUG();
+		}
+	} else {
+		printk("unlockfs: no super found, dev %s\n", kdevname(dev));
+		BUG();
+	}
+
+	spin_unlock(&sb_lock);
+out:
+	return;
+}
+
 /**
  *	get_super	-	get the superblock of a device
  *	@dev: device to get the superblock for
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/Config.in linux-2.4.27-leo/grsecurity/Config.in
--- linux-2.4.27/grsecurity/Config.in	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/Config.in	2004-09-17 03:20:32.000000000 +0100
@@ -0,0 +1,402 @@
+define_bool CONFIG_CRYPTO y
+define_bool CONFIG_CRYPTO_SHA256 y
+choice 'Security level' \
+        "Low		CONFIG_GRKERNSEC_LOW \
+         Medium		CONFIG_GRKERNSEC_MID \
+         High		CONFIG_GRKERNSEC_HI \
+	  Customized	CONFIG_GRKERNSEC_CUSTOM" Customized
+if [ "$CONFIG_GRKERNSEC_LOW" = "y" ]; then
+define_bool CONFIG_GRKERNSEC_RANDSRC n
+define_bool CONFIG_GRKERNSEC_RANDRPC n
+define_bool CONFIG_GRKERNSEC_FORKFAIL n
+define_bool CONFIG_GRKERNSEC_TIME n
+define_bool CONFIG_GRKERNSEC_SIGNAL n
+define_bool CONFIG_GRKERNSEC_CHROOT_SHMAT n
+define_bool CONFIG_GRKERNSEC_CHROOT_MOUNT n
+define_bool CONFIG_GRKERNSEC_CHROOT_FCHDIR n
+define_bool CONFIG_GRKERNSEC_CHROOT_DOUBLE n
+define_bool CONFIG_GRKERNSEC_CHROOT_PIVOT n
+define_bool CONFIG_GRKERNSEC_CHROOT_MKNOD n
+define_bool CONFIG_GRKERNSEC_PROC n
+define_bool CONFIG_GRKERNSEC_PROC_IPADDR n
+define_bool CONFIG_GRKERNSEC_PROC_MEMMAP n
+define_bool CONFIG_GRKERNSEC_HIDESYM n
+define_bool CONFIG_GRKERNSEC_BRUTE n
+define_bool CONFIG_GRKERNSEC_CHROOT_CAPS n
+define_bool CONFIG_GRKERNSEC_CHROOT_SYSCTL n
+define_bool CONFIG_GRKERNSEC_PROC_USERGROUP n
+define_bool CONFIG_GRKERNSEC_KMEM n
+define_bool CONFIG_GRKERNSEC_PORT n
+define_bool CONFIG_GRKERNSEC_PROC_ADD n
+define_bool CONFIG_GRKERNSEC_CHROOT_CHMOD n
+define_bool CONFIG_GRKERNSEC_CHROOT_NICE n
+define_bool CONFIG_GRKERNSEC_CHROOT_FINDTASK n
+define_bool CONFIG_GRKERNSEC_PAX_RANDUSTACK n
+define_bool CONFIG_GRKERNSEC_PAX_ASLR n
+define_bool CONFIG_GRKERNSEC_PAX_RANDMMAP n
+define_bool CONFIG_GRKERNSEC_PAX_NOEXEC n
+define_bool CONFIG_GRKERNSEC_PAX_PAGEEXEC n
+define_bool CONFIG_GRKERNSEC_PAX_NOELFRELOCS n
+define_bool CONFIG_GRKERNSEC_PAX_ETEXECRELOCS n
+define_bool CONFIG_GRKERNSEC_PAX_MPROTECT n
+define_bool CONFIG_GRKERNSEC_PAX_SOFTMODE n
+define_bool CONFIG_GRKERNSEC_PAX_EI_PAX n
+define_bool CONFIG_GRKERNSEC_PAX_PT_PAX_FLAGS n
+define_bool CONFIG_GRKERNSEC_PAX_NO_ACL_FLAGS n
+define_bool CONFIG_GRKERNSEC_PAX_RANDEXEC n
+define_bool CONFIG_GRKERNSEC_PAX_EMUTRAMP n
+define_bool CONFIG_GRKERNSEC_PAX_EMUSIGRT n
+if [ "$CONFIG_X86" = "y" ]; then
+define_bool CONFIG_GRKERNSEC_PAX_RANDKSTACK n
+define_bool CONFIG_GRKERNSEC_PAX_KERNEXEC n
+define_bool CONFIG_GRKERNSEC_IO n
+define_bool CONFIG_GRKERNSEC_PAX_SEGMEXEC n
+fi
+define_bool CONFIG_GRKERNSEC_AUDIT_MOUNT n
+define_bool CONFIG_GRKERNSEC_ACL_HIDEKERN n
+define_bool CONFIG_GRKERNSEC_RESLOG n
+define_int CONFIG_GRKERNSEC_ACL_MAXTRIES 3
+define_int CONFIG_GRKERNSEC_ACL_TIMEOUT 30
+
+define_int  CONFIG_GRKERNSEC_FLOODTIME 10
+define_int  CONFIG_GRKERNSEC_FLOODBURST 4
+define_bool CONFIG_GRKERNSEC_LINK y
+define_bool CONFIG_GRKERNSEC_FIFO y
+define_bool CONFIG_GRKERNSEC_RANDPID y
+define_bool CONFIG_GRKERNSEC_EXECVE y
+define_bool CONFIG_GRKERNSEC_RANDNET y
+define_bool CONFIG_GRKERNSEC_RANDISN n
+define_bool CONFIG_GRKERNSEC_DMESG y
+define_bool CONFIG_GRKERNSEC_RANDID y
+define_bool CONFIG_GRKERNSEC_CHROOT_CHDIR y
+fi
+if [ "$CONFIG_GRKERNSEC_MID" = "y" ]; then
+define_bool CONFIG_GRKERNSEC_KMEM n
+define_bool CONFIG_GRKERNSEC_PROC_IPADDR n
+define_bool CONFIG_GRKERNSEC_HIDESYM n
+define_bool CONFIG_GRKERNSEC_PROC_ADD n
+define_bool CONFIG_GRKERNSEC_CHROOT_CHMOD n
+define_bool CONFIG_GRKERNSEC_CHROOT_NICE n
+define_bool CONFIG_GRKERNSEC_CHROOT_FINDTASK n
+define_bool CONFIG_GRKERNSEC_PAX_NOEXEC n
+define_bool CONFIG_GRKERNSEC_PAX_PAGEEXEC n
+define_bool CONFIG_GRKERNSEC_PAX_NOELFRELOCS n
+define_bool CONFIG_GRKERNSEC_PAX_ETEXECRELOCS n
+define_bool CONFIG_GRKERNSEC_PAX_MPROTECT n
+define_bool CONFIG_GRKERNSEC_PAX_SOFTMODE n
+define_bool CONFIG_GRKERNSEC_PAX_EI_PAX y
+define_bool CONFIG_GRKERNSEC_PAX_PT_PAX_FLAGS y
+define_bool CONFIG_GRKERNSEC_PAX_NO_ACL_FLAGS y
+define_bool CONFIG_GRKERNSEC_PAX_RANDEXEC n
+define_bool CONFIG_GRKERNSEC_PAX_EMUTRAMP n
+define_bool CONFIG_GRKERNSEC_PAX_EMUSIGRT n
+if [ "$CONFIG_X86" = "y" ]; then
+define_bool CONFIG_GRKERNSEC_IO n
+define_bool CONFIG_GRKERNSEC_PAX_SEGMEXEC n
+fi
+define_bool CONFIG_GRKERNSEC_AUDIT_MOUNT n
+define_bool CONFIG_GRKERNSEC_CHROOT_CAPS n
+define_bool CONFIG_GRKERNSEC_AUDIT_MOUNT n
+define_bool CONFIG_GRKERNSEC_CHROOT_FCHDIR n
+define_bool CONFIG_GRKERNSEC_ACL_HIDEKERN n
+define_bool CONFIG_GRKERNSEC_RESLOG n
+define_int CONFIG_GRKERNSEC_ACL_MAXTRIES 3
+define_int CONFIG_GRKERNSEC_ACL_TIMEOUT 30
+
+define_int  CONFIG_GRKERNSEC_FLOODTIME 10
+define_int  CONFIG_GRKERNSEC_FLOODBURST 4
+define_bool CONFIG_GRKERNSEC_PROC_MEMMAP y
+define_bool CONFIG_GRKERNSEC_CHROOT_SYSCTL y
+define_bool CONFIG_GRKERNSEC_LINK y
+define_bool CONFIG_GRKERNSEC_FIFO y
+define_bool CONFIG_GRKERNSEC_RANDPID y
+define_bool CONFIG_GRKERNSEC_EXECVE y
+define_bool CONFIG_GRKERNSEC_DMESG y
+define_bool CONFIG_GRKERNSEC_RANDID y
+define_bool CONFIG_GRKERNSEC_RANDNET y
+define_bool CONFIG_GRKERNSEC_RANDISN y
+define_bool CONFIG_GRKERNSEC_RANDSRC y
+define_bool CONFIG_GRKERNSEC_RANDRPC y
+define_bool CONFIG_GRKERNSEC_FORKFAIL y
+define_bool CONFIG_GRKERNSEC_TIME y
+define_bool CONFIG_GRKERNSEC_SIGNAL y
+define_bool CONFIG_GRKERNSEC_CHROOT y
+define_bool CONFIG_GRKERNSEC_CHROOT_SHMAT n
+define_bool CONFIG_GRKERNSEC_CHROOT_UNIX y
+define_bool CONFIG_GRKERNSEC_CHROOT_MOUNT y
+define_bool CONFIG_GRKERNSEC_CHROOT_PIVOT y
+define_bool CONFIG_GRKERNSEC_CHROOT_DOUBLE y
+define_bool CONFIG_GRKERNSEC_CHROOT_CHDIR y
+define_bool CONFIG_GRKERNSEC_CHROOT_MKNOD y
+define_bool CONFIG_GRKERNSEC_PROC y
+define_bool CONFIG_GRKERNSEC_PROC_USERGROUP y
+define_int  CONFIG_GRKERNSEC_PROC_GID 10
+define_bool CONFIG_GRKERNSEC_PAX_RANDUSTACK y
+define_bool CONFIG_GRKERNSEC_PAX_RANDKSTACK n
+define_bool CONFIG_GRKERNSEC_PAX_KERNEXEC n
+define_bool CONFIG_GRKERNSEC_PAX_ASLR y
+define_bool CONFIG_GRKERNSEC_PAX_RANDMMAP y
+define_bool CONFIG_GRKERNSEC_BRUTE n
+fi
+if [ "$CONFIG_GRKERNSEC_HI" = "y" ]; then
+define_int CONFIG_GRKERNSEC_FLOODTIME 10
+define_int  CONFIG_GRKERNSEC_FLOODBURST 4
+define_bool CONFIG_GRKERNSEC_LINK y
+define_bool CONFIG_GRKERNSEC_FIFO y
+define_bool CONFIG_GRKERNSEC_RANDPID y
+define_bool CONFIG_GRKERNSEC_EXECVE y
+define_bool CONFIG_GRKERNSEC_DMESG y
+define_bool CONFIG_GRKERNSEC_RANDID y
+define_bool CONFIG_GRKERNSEC_RANDSRC y
+define_bool CONFIG_GRKERNSEC_RANDRPC y
+define_bool CONFIG_GRKERNSEC_FORKFAIL y
+define_bool CONFIG_GRKERNSEC_TIME y
+define_bool CONFIG_GRKERNSEC_SIGNAL y
+define_bool CONFIG_GRKERNSEC_CHROOT_SHMAT y
+define_bool CONFIG_GRKERNSEC_CHROOT_UNIX y
+define_bool CONFIG_GRKERNSEC_CHROOT_MOUNT y
+define_bool CONFIG_GRKERNSEC_CHROOT_FCHDIR y
+define_bool CONFIG_GRKERNSEC_CHROOT_PIVOT y
+define_bool CONFIG_GRKERNSEC_CHROOT_DOUBLE y
+define_bool CONFIG_GRKERNSEC_CHROOT_CHDIR y
+define_bool CONFIG_GRKERNSEC_CHROOT_MKNOD y
+define_bool CONFIG_GRKERNSEC_CHROOT_CAPS y
+define_bool CONFIG_GRKERNSEC_CHROOT_SYSCTL y
+define_bool CONFIG_GRKERNSEC_CHROOT_FINDTASK y
+define_bool CONFIG_GRKERNSEC_PROC y
+define_bool CONFIG_GRKERNSEC_PROC_IPADDR n
+define_bool CONFIG_GRKERNSEC_PROC_MEMMAP y
+define_bool CONFIG_GRKERNSEC_HIDESYM y
+define_bool CONFIG_GRKERNSEC_BRUTE y
+define_bool CONFIG_GRKERNSEC_PROC_USERGROUP y
+define_int  CONFIG_GRKERNSEC_PROC_GID 10
+define_bool CONFIG_GRKERNSEC_KMEM y
+define_bool CONFIG_GRKERNSEC_PORT y
+define_bool CONFIG_GRKERNSEC_RESLOG y
+define_bool CONFIG_GRKERNSEC_RANDNET y
+define_bool CONFIG_GRKERNSEC_RANDISN y
+
+define_bool CONFIG_GRKERNSEC_AUDIT_MOUNT n
+define_bool CONFIG_GRKERNSEC_ACL_HIDEKERN n
+define_int CONFIG_GRKERNSEC_ACL_MAXTRIES 3
+define_int CONFIG_GRKERNSEC_ACL_TIMEOUT 30
+
+define_bool CONFIG_GRKERNSEC_PROC_ADD y
+define_bool CONFIG_GRKERNSEC_CHROOT_CHMOD y
+define_bool CONFIG_GRKERNSEC_CHROOT_NICE y
+define_bool CONFIG_GRKERNSEC_PAX_RANDUSTACK y
+define_bool CONFIG_GRKERNSEC_PAX_ASLR y
+define_bool CONFIG_GRKERNSEC_PAX_RANDMMAP y
+define_bool CONFIG_GRKERNSEC_PAX_NOEXEC y
+define_bool CONFIG_GRKERNSEC_PAX_PAGEEXEC n
+define_bool CONFIG_GRKERNSEC_PAX_NOELFRELOCS n
+define_bool CONFIG_GRKERNSEC_PAX_MPROTECT y
+define_bool CONFIG_GRKERNSEC_PAX_ETEXECRELOCS n
+define_bool CONFIG_GRKERNSEC_PAX_SOFTMODE n
+define_bool CONFIG_GRKERNSEC_PAX_EI_PAX y
+define_bool CONFIG_GRKERNSEC_PAX_PT_PAX_FLAGS y
+define_bool CONFIG_GRKERNSEC_PAX_NO_ACL_FLAGS y
+if [ "$CONFIG_X86" = "y" ]; then
+define_bool CONFIG_GRKERNSEC_IO n
+if [ "$CONFIG_MODULES" != "y" -a "$CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM" != "y" ]; then
+define_bool CONFIG_GRKERNSEC_PAX_KERNEXEC y
+fi
+define_bool CONFIG_GRKERNSEC_PAX_RANDKSTACK y
+define_bool CONFIG_GRKERNSEC_PAX_RANDEXEC y
+define_bool CONFIG_GRKERNSEC_PAX_SEGMEXEC y
+define_bool CONFIG_GRKERNSEC_PAX_EMUTRAMP n
+define_bool CONFIG_GRKERNSEC_PAX_EMUSIGRT n
+fi
+if [ "$CONFIG_PARISC" = "y" ]; then
+define_bool CONFIG_GRKERNSEC_PAX_EMUTRAMP y
+define_bool CONFIG_GRKERNSEC_PAX_EMUSIGRT y
+fi
+define_bool CONFIG_GRKERNSEC_AUDIT_MOUNT y
+fi
+if [ "$CONFIG_GRKERNSEC_CUSTOM" = "y" ]; then
+mainmenu_option next_comment
+comment 'PaX Control'
+bool 'Support soft mode' CONFIG_GRKERNSEC_PAX_SOFTMODE
+bool 'Use legacy ELF header marking' CONFIG_GRKERNSEC_PAX_EI_PAX
+bool 'Use ELF program header marking' CONFIG_GRKERNSEC_PAX_PT_PAX_FLAGS
+choice 'MAC system integration' \
+	"none		CONFIG_GRKERNSEC_PAX_NO_ACL_FLAGS \
+	 direct		CONFIG_GRKERNSEC_PAX_HAVE_ACL_FLAGS \
+	 hook		CONFIG_GRKERNSEC_PAX_HOOK_ACL_FLAGS" none
+endmenu
+mainmenu_option next_comment
+comment 'Address Space Protection'
+if [ "$CONFIG_GRKERNSEC_PAX_EI_PAX" = "y" -o \
+     "$CONFIG_GRKERNSEC_PAX_PT_PAX_FLAGS" = "y" -o \
+     "$CONFIG_GRKERNSEC_PAX_HAVE_ACL_FLAGS" = "y" -o \
+     "$CONFIG_GRKERNSEC_PAX_HOOK_ACL_FLAGS" = "y" ]; then
+   bool 'Enforce Non-executable pages' CONFIG_GRKERNSEC_PAX_NOEXEC
+   if [ "$CONFIG_GRKERNSEC_PAX_NOEXEC" = "y" ]; then
+     bool 'Paging based non-executable pages' CONFIG_GRKERNSEC_PAX_PAGEEXEC
+     if [ "$CONFIG_X86" = "y" ]; then
+       bool 'Segmentation based non-executable pages' CONFIG_GRKERNSEC_PAX_SEGMEXEC
+     fi
+     if [ "$CONFIG_X86" = "y" -o "$CONFIG_PARISC" = "y" -o "$CONFIG_PPC32" = "y" ]; then
+       if [ "$CONFIG_GRKERNSEC_PAX_PAGEEXEC" = "y" -o "$CONFIG_GRKERNSEC_PAX_SEGMEXEC" = "y" ]; then
+         bool '   Emulate trampolines' CONFIG_GRKERNSEC_PAX_EMUTRAMP
+         if [ "$CONFIG_GRKERNSEC_PAX_EMUTRAMP" = "y" ]; then
+           bool '    Automatically emulate sigreturn trampolines' CONFIG_GRKERNSEC_PAX_EMUSIGRT
+         fi
+       fi
+     fi
+     bool '   Restrict mprotect()' CONFIG_GRKERNSEC_PAX_MPROTECT
+     if [ "$CONFIG_GRKERNSEC_PAX_MPROTECT" = "y" ]; then
+       if [ "$CONFIG_X86" = "y" ]; then
+         bool '    Disallow ELF text relocations (DANGEROUS)' CONFIG_GRKERNSEC_PAX_NOELFRELOCS
+       else
+       if [ "$CONFIG_ALPHA" = "y" -o "$CONFIG_PARISC" = "y" ]; then
+         bool '    Allow ELF ET_EXEC text relocations' CONFIG_GRKERNSEC_PAX_ETEXECRELOCS
+       fi
+       if [ "$CONFIG_PPC32" = "y" ]; then
+         define_bool CONFIG_GRKERNSEC_PAX_SYSCALL y
+       fi
+       if [ "$CONFIG_ALPHA" = "y" -o "$CONFIG_PARISC" = "y" -o "$CONFIG_SPARC32" = "y" -o "$CONFIG_SPARC64" = "y" -o "$CONFIG_PPC32" = "y" ]; then
+         bool '    Automatically emulate ELF PLT' CONFIG_GRKERNSEC_PAX_EMUPLT
+        if [ "$CONFIG_GRKERNSEC_PAX_EMUPLT" = "y" ]; then
+           if [ "$CONFIG_SPARC32" = "y" -o "$CONFIG_SPARC64" = "y" ]; then
+       	     define_bool CONFIG_GRKERNSEC_PAX_DLRESOLVE y
+           fi
+        fi
+       fi
+       fi
+     fi
+   fi
+   if [ "$CONFIG_X86" = "y" -a "$CONFIG_MODULES" != "y" -a "$CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM" != "y" ]; then
+     bool 'Enforce non-executable kernel pages' CONFIG_GRKERNSEC_PAX_KERNEXEC
+   fi
+   bool 'Address Space Layout Randomization' CONFIG_GRKERNSEC_PAX_ASLR
+   if [ "$CONFIG_GRKERNSEC_PAX_ASLR" = "y" ]; then
+     if [ "$CONFIG_X86_TSC" = "y" ]; then
+       bool '  Randomize kernel stack base' CONFIG_GRKERNSEC_PAX_RANDKSTACK
+     fi
+     bool '  Randomize user stack base' CONFIG_GRKERNSEC_PAX_RANDUSTACK
+     bool '  Randomize mmap() base' CONFIG_GRKERNSEC_PAX_RANDMMAP
+     if [ "$CONFIG_GRKERNSEC_PAX_RANDMMAP" = "y" -a "$CONFIG_GRKERNSEC_PAX_MPROTECT" = "y" ]; then
+       bool '  Randomize ET_EXEC base' CONFIG_GRKERNSEC_PAX_RANDEXEC
+     fi
+   fi
+fi
+
+bool 'Deny writing to /dev/kmem and /dev/mem' CONFIG_GRKERNSEC_KMEM
+bool 'Deny access to /dev/port' CONFIG_GRKERNSEC_PORT
+if [ "$CONFIG_X86" = "y" ]; then
+  bool 'Disable privileged I/O' CONFIG_GRKERNSEC_IO
+  if [ "$CONFIG_GRKERNSEC_IO" = "y" ]; then
+    define_bool CONFIG_RTC y
+  fi
+fi
+bool 'Remove addresses from /proc/pid/[maps|stat]' CONFIG_GRKERNSEC_PROC_MEMMAP
+bool 'Deter exploit bruteforcing' CONFIG_GRKERNSEC_BRUTE
+bool 'Hide kernel symbols' CONFIG_GRKERNSEC_HIDESYM
+endmenu
+mainmenu_option next_comment
+comment 'Role Based Access Control Options'
+bool 'Hide kernel processes' CONFIG_GRKERNSEC_ACL_HIDEKERN
+int 'Maximum tries before password lockout' CONFIG_GRKERNSEC_ACL_MAXTRIES 3
+int 'Time to wait after max password tries, in seconds' CONFIG_GRKERNSEC_ACL_TIMEOUT 30
+endmenu
+mainmenu_option next_comment
+comment 'Filesystem Protections'
+bool 'Proc restrictions' CONFIG_GRKERNSEC_PROC
+if [ "$CONFIG_GRKERNSEC_PROC" != "n" ]; then
+ bool '   Restrict to user only' CONFIG_GRKERNSEC_PROC_USER
+ if [ "$CONFIG_GRKERNSEC_PROC_USER" != "y" ]; then
+  bool '   Allow special group' CONFIG_GRKERNSEC_PROC_USERGROUP
+  if [ "$CONFIG_GRKERNSEC_PROC_USERGROUP" != "n" ]; then
+   int  '   GID for special group' CONFIG_GRKERNSEC_PROC_GID 1001
+  fi
+ fi
+ if [ "$CONFIG_GRKERNSEC_PROC_USER" != "n" -o "$CONFIG_GRKERNSEC_PROC_USERGROUP" != "n" ]; then
+  bool '   Additional restrictions' CONFIG_GRKERNSEC_PROC_ADD
+ fi
+fi
+bool 'Linking restrictions' CONFIG_GRKERNSEC_LINK
+bool 'FIFO restrictions' CONFIG_GRKERNSEC_FIFO
+bool 'Chroot jail restrictions' CONFIG_GRKERNSEC_CHROOT
+if [ "$CONFIG_GRKERNSEC_CHROOT" != "n" ]; then
+bool '   Deny mounts' CONFIG_GRKERNSEC_CHROOT_MOUNT
+bool '   Deny double-chroots' CONFIG_GRKERNSEC_CHROOT_DOUBLE
+bool '   Deny pivot_root in chroot' CONFIG_GRKERNSEC_CHROOT_PIVOT
+bool '   Enforce chdir("/") on all chroots' CONFIG_GRKERNSEC_CHROOT_CHDIR
+bool '   Deny (f)chmod +s' CONFIG_GRKERNSEC_CHROOT_CHMOD
+bool '   Deny fchdir out of chroot' CONFIG_GRKERNSEC_CHROOT_FCHDIR
+bool '   Deny mknod' CONFIG_GRKERNSEC_CHROOT_MKNOD
+bool '   Deny shmat() out of chroot' CONFIG_GRKERNSEC_CHROOT_SHMAT
+bool '   Deny access to abstract AF_UNIX sockets out of chroot' CONFIG_GRKERNSEC_CHROOT_UNIX
+bool '   Protect outside processes' CONFIG_GRKERNSEC_CHROOT_FINDTASK
+bool '   Restrict priority changes' CONFIG_GRKERNSEC_CHROOT_NICE
+bool '   Deny sysctl writes in chroot' CONFIG_GRKERNSEC_CHROOT_SYSCTL
+bool '   Capability restrictions within chroot' CONFIG_GRKERNSEC_CHROOT_CAPS
+fi
+endmenu
+mainmenu_option next_comment
+comment 'Kernel Auditing'
+bool 'Single group for auditing' CONFIG_GRKERNSEC_AUDIT_GROUP
+if [ "$CONFIG_GRKERNSEC_AUDIT_GROUP" != "n" ]; then
+int  '   GID for auditing' CONFIG_GRKERNSEC_AUDIT_GID 1007
+fi
+bool 'Exec logging' CONFIG_GRKERNSEC_EXECLOG
+bool 'Resource logging' CONFIG_GRKERNSEC_RESLOG
+bool 'Log execs within chroot' CONFIG_GRKERNSEC_CHROOT_EXECLOG
+bool 'Chdir logging' CONFIG_GRKERNSEC_AUDIT_CHDIR
+bool '(Un)Mount logging' CONFIG_GRKERNSEC_AUDIT_MOUNT
+bool 'IPC logging' CONFIG_GRKERNSEC_AUDIT_IPC
+bool 'Signal logging' CONFIG_GRKERNSEC_SIGNAL
+bool 'Fork failure logging' CONFIG_GRKERNSEC_FORKFAIL
+bool 'Time change logging' CONFIG_GRKERNSEC_TIME
+bool '/proc/<pid>/ipaddr support' CONFIG_GRKERNSEC_PROC_IPADDR
+if [ "$CONFIG_GRKERNSEC_PAX_MPROTECT" != "n" ]; then
+  bool 'ELF text relocations logging (READ HELP)' CONFIG_GRKERNSEC_AUDIT_TEXTREL
+fi
+endmenu
+mainmenu_option next_comment
+comment 'Executable Protections'
+bool 'Enforce RLIMIT_NPROC on execs' CONFIG_GRKERNSEC_EXECVE
+bool 'Dmesg(8) restriction' CONFIG_GRKERNSEC_DMESG
+bool 'Randomized PIDs' CONFIG_GRKERNSEC_RANDPID
+bool 'Trusted path execution' CONFIG_GRKERNSEC_TPE
+if [ "$CONFIG_GRKERNSEC_TPE" != "n" ]; then
+bool '   Partially restrict non-root users' CONFIG_GRKERNSEC_TPE_ALL
+int  '   GID for untrusted users:' CONFIG_GRKERNSEC_TPE_GID 1005
+fi
+endmenu
+mainmenu_option next_comment
+comment 'Network Protections'
+bool 'Larger entropy pools' CONFIG_GRKERNSEC_RANDNET
+bool 'Truly random TCP ISN selection' CONFIG_GRKERNSEC_RANDISN
+bool 'Randomized IP IDs' CONFIG_GRKERNSEC_RANDID
+bool 'Randomized TCP source ports' CONFIG_GRKERNSEC_RANDSRC
+bool 'Randomized RPC XIDs' CONFIG_GRKERNSEC_RANDRPC
+bool 'Socket restrictions' CONFIG_GRKERNSEC_SOCKET
+if [ "$CONFIG_GRKERNSEC_SOCKET" != "n" ]; then
+bool '  Deny any sockets to group' CONFIG_GRKERNSEC_SOCKET_ALL
+if [ "$CONFIG_GRKERNSEC_SOCKET_ALL" != "n" ]; then
+int  '   GID to deny all sockets for:' CONFIG_GRKERNSEC_SOCKET_ALL_GID 1004
+fi
+bool '  Deny client sockets to group' CONFIG_GRKERNSEC_SOCKET_CLIENT
+if [ "$CONFIG_GRKERNSEC_SOCKET_CLIENT" != "n" ]; then
+int  '   GID to deny client sockets for:' CONFIG_GRKERNSEC_SOCKET_CLIENT_GID 1003
+fi
+bool '  Deny server sockets to group' CONFIG_GRKERNSEC_SOCKET_SERVER
+if [ "$CONFIG_GRKERNSEC_SOCKET_SERVER" != "n" ]; then
+int  '   GID to deny server sockets for:' CONFIG_GRKERNSEC_SOCKET_SERVER_GID 1002
+fi
+fi
+endmenu
+if [ "$CONFIG_SYSCTL" != "n" ]; then
+mainmenu_option next_comment
+comment 'Sysctl support'
+bool 'Sysctl support' CONFIG_GRKERNSEC_SYSCTL
+endmenu
+fi
+mainmenu_option next_comment
+comment 'Logging options'
+int 'Seconds in between log messages (minimum)' CONFIG_GRKERNSEC_FLOODTIME 10
+int 'Number of messages in a burst (maximum)' CONFIG_GRKERNSEC_FLOODBURST 4
+endmenu
+fi
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/gracl_alloc.c linux-2.4.27-leo/grsecurity/gracl_alloc.c
--- linux-2.4.27/grsecurity/gracl_alloc.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/gracl_alloc.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,93 @@
+/* stack-based acl allocation tracking (c) Brad Spengler 2002,2003 */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/gracl.h>
+#include <linux/grsecurity.h>
+
+static unsigned long alloc_stack_next = 1;
+static unsigned long alloc_stack_size = 1;
+static void **alloc_stack;
+
+static __inline__ int
+alloc_pop(void)
+{
+	if (alloc_stack_next == 1)
+		return 0;
+
+	kfree(alloc_stack[alloc_stack_next - 2]);
+
+	alloc_stack_next--;
+
+	return 1;
+}
+
+static __inline__ void
+alloc_push(void *buf)
+{
+	if (alloc_stack_next >= alloc_stack_size)
+		BUG();
+
+	alloc_stack[alloc_stack_next - 1] = buf;
+
+	alloc_stack_next++;
+
+	return;
+}
+
+void *
+acl_alloc(unsigned long len)
+{
+	void *ret;
+
+	if (len > PAGE_SIZE)
+		BUG();
+
+	ret = kmalloc(len, GFP_KERNEL);
+
+	if (ret)
+		alloc_push(ret);
+
+	return ret;
+}
+
+void
+acl_free_all(void)
+{
+	if (gr_acl_is_enabled() || !alloc_stack)
+		return;
+
+	while (alloc_pop()) ;
+
+	if (alloc_stack) {
+		if ((alloc_stack_size * sizeof (void *)) <= PAGE_SIZE)
+			kfree(alloc_stack);
+		else
+			vfree(alloc_stack);
+	}
+
+	alloc_stack = NULL;
+	alloc_stack_size = 1;
+	alloc_stack_next = 1;
+
+	return;
+}
+
+int
+acl_alloc_stack_init(unsigned long size)
+{
+	if ((size * sizeof (void *)) <= PAGE_SIZE)
+		alloc_stack =
+		    (void **) kmalloc(size * sizeof (void *), GFP_KERNEL);
+	else
+		alloc_stack = (void **) vmalloc(size * sizeof (void *));
+
+	alloc_stack_size = size;
+
+	if (!alloc_stack)
+		return 0;
+	else
+		return 1;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/gracl.c linux-2.4.27-leo/grsecurity/gracl.c
--- linux-2.4.27/grsecurity/gracl.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/gracl.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,3400 @@
+/* 
+ * grsecurity/gracl.c
+ * Copyright Brad Spengler 2001, 2002, 2003
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/smp_lock.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/types.h>
+#include <linux/capability.h>
+#include <linux/sysctl.h>
+#include <linux/gracl.h>
+#include <linux/gralloc.h>
+#include <linux/grsecurity.h>
+#include <linux/grinternal.h>
+
+#include <asm/uaccess.h>
+#include <asm/errno.h>
+#include <asm/mman.h>
+
+static struct acl_role_db acl_role_set;
+static struct acl_role_label *role_list_head;
+static struct name_db name_set;
+static struct name_db inodev_set;
+
+/* for keeping track of userspace pointers used for subjects, so we
+   can share references in the kernel as well
+*/
+
+static struct dentry *real_root;
+static struct vfsmount *real_root_mnt;
+
+static struct acl_subj_map_db subj_map_set;
+
+
+static struct acl_role_label *default_role;
+
+static u16 acl_sp_role_value;
+
+static DECLARE_MUTEX(gr_dev_sem);
+rwlock_t gr_inode_lock = RW_LOCK_UNLOCKED;
+
+extern char *gr_shared_page[4][NR_CPUS];
+struct gr_arg *gr_usermode;
+
+static unsigned long gr_status = GR_STATUS_INIT;
+
+extern int chkpw(struct gr_arg *entry, unsigned char *salt, unsigned char *sum);
+extern void gr_clear_learn_entries(void);
+
+#ifdef CONFIG_GRKERNSEC_RESLOG
+extern void gr_log_resource(const struct task_struct *task,
+			    const int res, const unsigned long wanted, const int gt);
+#endif
+
+unsigned char *gr_system_salt;
+unsigned char *gr_system_sum;
+
+static struct sprole_pw **acl_special_roles = NULL;
+static __u16 num_sprole_pws = 0;
+
+static struct acl_role_label *kernel_role = NULL;
+
+/* The following are used to keep a place held in the hash table when we move
+   entries around.  They can be replaced during insert. */
+
+static struct acl_subject_label *deleted_subject;
+static struct acl_object_label *deleted_object;
+static struct name_entry *deleted_inodev;
+
+/* for keeping track of the last and final allocated subjects, since
+   nested subject parsing is tricky
+*/
+static struct acl_subject_label *s_last = NULL;
+static struct acl_subject_label *s_final = NULL;
+
+static unsigned int gr_auth_attempts = 0;
+static unsigned long gr_auth_expires = 0UL;
+
+extern int gr_init_uidset(void);
+extern void gr_free_uidset(void);
+extern void gr_remove_uid(uid_t uid);
+extern int gr_find_uid(uid_t uid);
+
+__inline__ int
+gr_acl_is_enabled(void)
+{
+	return (gr_status & GR_READY);
+}
+
+char gr_roletype_to_char(void)
+{
+	switch (current->role->roletype &
+		(GR_ROLE_DEFAULT | GR_ROLE_USER | GR_ROLE_GROUP |
+		 GR_ROLE_SPECIAL)) {
+	case GR_ROLE_DEFAULT:
+		return 'D';
+	case GR_ROLE_USER:
+		return 'U';
+	case GR_ROLE_GROUP:
+		return 'G';
+	case GR_ROLE_SPECIAL:
+		return 'S';
+	}
+
+	return 'X';
+}
+
+__inline__ int
+gr_acl_tpe_check(void)
+{
+	if (unlikely(!(gr_status & GR_READY)))
+		return 0;
+	if (current->role->roletype & GR_ROLE_TPE)
+		return 1;
+	else
+		return 0;
+}
+
+int
+gr_handle_rawio(const struct inode *inode)
+{
+	if (inode && S_ISBLK(inode->i_mode) && !capable(CAP_SYS_RAWIO) &&
+	    ((gr_status & GR_READY)
+#ifdef CONFIG_GRKERNSEC_CHROOT_CAPS
+	     || (grsec_enable_chroot_caps && proc_is_chrooted(current))
+#endif
+	    ))
+		return 1;
+	return 0;
+}
+
+
+static __inline__ int
+gr_streq(const char *a, const char *b, const __u16 lena, const __u16 lenb)
+{
+	int i;
+	unsigned long *l1;
+	unsigned long *l2;
+	unsigned char *c1;
+	unsigned char *c2;
+	int num_longs;
+
+	if (likely(lena != lenb))
+		return 0;
+
+	l1 = (unsigned long *)a;
+	l2 = (unsigned long *)b;
+
+	num_longs = lena / sizeof(unsigned long);
+
+	for (i = num_longs; i--; l1++, l2++) {
+		if (unlikely(*l1 != *l2))
+			return 0;
+	}
+
+	c1 = (unsigned char *) l1;
+	c2 = (unsigned char *) l2;
+
+	i = lena - (num_longs * sizeof(unsigned long));	
+
+	for (; i--; c1++, c2++) {
+		if (unlikely(*c1 != *c2))
+			return 0;
+	}
+
+	return 1;
+}
+		
+static char *
+d_real_path(const struct dentry *dentry, const struct vfsmount *vfsmnt,
+	    char *buf, int buflen)
+{
+	char *res;
+	struct dentry *root;
+	struct vfsmount *rootmnt;
+
+	/* we can't use real_root, real_root_mnt, because they belong only to the RBAC system */
+	read_lock(&child_reaper->fs->lock);
+	root = dget(child_reaper->fs->root);
+	rootmnt = mntget(child_reaper->fs->rootmnt);
+	read_unlock(&child_reaper->fs->lock);
+
+	spin_lock(&dcache_lock);
+	res = __d_path((struct dentry *)dentry, (struct vfsmount *)vfsmnt, root, rootmnt, buf, buflen);
+	spin_unlock(&dcache_lock);
+	if (unlikely(IS_ERR(res)))
+		res = strcpy(buf, "<path too long>");
+	dput(root);
+	mntput(rootmnt);
+	return res;
+}
+
+static __inline__ char *
+__d_real_path(const struct dentry *dentry, const struct vfsmount *vfsmnt,
+	    char *buf, int buflen)
+{
+	char *res;
+	struct dentry *root;
+	struct vfsmount *rootmnt;
+
+	/* we can't use real_root, real_root_mnt, because they belong only to the RBAC system */
+	read_lock(&child_reaper->fs->lock);
+	root = dget(child_reaper->fs->root);
+	rootmnt = mntget(child_reaper->fs->rootmnt);
+	read_unlock(&child_reaper->fs->lock);
+
+	res = __d_path((struct dentry *)dentry, (struct vfsmount *)vfsmnt, root, rootmnt, buf, buflen);
+	if (unlikely(IS_ERR(res)))
+		res = strcpy(buf, "<path too long>");
+	dput(root);
+	mntput(rootmnt);
+	return res;
+}
+
+char *
+gr_to_filename_nolock(const struct dentry *dentry, const struct vfsmount *mnt)
+{
+	return __d_real_path(dentry, mnt, gr_shared_page[0][smp_processor_id()],
+			   PAGE_SIZE);
+}
+
+char *
+gr_to_filename(const struct dentry *dentry, const struct vfsmount *mnt)
+{
+	return d_real_path(dentry, mnt, gr_shared_page[0][smp_processor_id()],
+			   PAGE_SIZE);
+}
+
+char *
+gr_to_filename1(const struct dentry *dentry, const struct vfsmount *mnt)
+{
+	return d_real_path(dentry, mnt, gr_shared_page[1][smp_processor_id()],
+			   PAGE_SIZE);
+}
+
+char *
+gr_to_filename2(const struct dentry *dentry, const struct vfsmount *mnt)
+{
+	return d_real_path(dentry, mnt, gr_shared_page[2][smp_processor_id()],
+			   PAGE_SIZE);
+}
+
+char *
+gr_to_filename3(const struct dentry *dentry, const struct vfsmount *mnt)
+{
+	return d_real_path(dentry, mnt, gr_shared_page[3][smp_processor_id()],
+			   PAGE_SIZE);
+}
+
+__inline__ __u32
+to_gr_audit(const __u32 reqmode)
+{
+	__u32 retmode = 0;
+
+	retmode |= (reqmode & GR_READ) ? GR_AUDIT_READ : 0;
+	retmode |= (reqmode & GR_WRITE) ? GR_AUDIT_WRITE | GR_AUDIT_APPEND : 0;
+	retmode |= (reqmode & GR_APPEND) ? GR_AUDIT_APPEND : 0;
+	retmode |= (reqmode & GR_EXEC) ? GR_AUDIT_EXEC : 0;
+	retmode |= (reqmode & GR_INHERIT) ? GR_AUDIT_INHERIT : 0;
+	retmode |= (reqmode & GR_FIND) ? GR_AUDIT_FIND : 0;
+	retmode |= (reqmode & GR_SETID) ? GR_AUDIT_SETID : 0;
+	retmode |= (reqmode & GR_CREATE) ? GR_AUDIT_CREATE : 0;
+	retmode |= (reqmode & GR_DELETE) ? GR_AUDIT_DELETE : 0;
+
+	return retmode;
+}
+
+__inline__ struct acl_subject_label *
+lookup_subject_map(const struct acl_subject_label *userp)
+{
+	unsigned long index = shash(userp, subj_map_set.s_size);
+	struct subject_map *match;
+	__u8 i = 0;
+
+	match = subj_map_set.s_hash[index];
+
+	while (match && match->user != userp) {
+		index = (index + (1 << i)) % subj_map_set.s_size;
+		match = subj_map_set.s_hash[index];
+		i = (i + 1) % 32;
+	}
+
+	if (match)
+		return match->kernel;
+	else
+		return NULL;
+}
+
+static void
+insert_subj_map_entry(struct subject_map *subjmap)
+{
+	unsigned long index = shash(subjmap->user, subj_map_set.s_size);
+	struct subject_map **curr;
+	__u8 i = 0;
+
+	curr = &subj_map_set.s_hash[index];
+
+	while (*curr) {
+		index = (index + (1 << i)) % subj_map_set.s_size;
+		curr = &subj_map_set.s_hash[index];
+		i = (i + 1) % 32;
+	}
+
+	*curr = subjmap;
+
+	return;
+}
+
+__inline__ struct acl_role_label *
+lookup_acl_role_label(const struct task_struct *task, const uid_t uid,
+		      const gid_t gid)
+{
+	unsigned long index = rhash(uid, GR_ROLE_USER, acl_role_set.r_size);
+	struct acl_role_label *match;
+	struct role_allowed_ip *ipp;
+	int x;
+	__u8 i = 0;
+
+	match = acl_role_set.r_hash[index];
+
+	while (match) {
+		if ((match->roletype & (GR_ROLE_DOMAIN | GR_ROLE_USER)) == (GR_ROLE_DOMAIN | GR_ROLE_USER)) {
+			for (x = 0; x < match->domain_child_num; x++) {
+				if (match->domain_children[x] == uid)
+					goto found;
+			}
+		} else if (match->uidgid == uid && match->roletype & GR_ROLE_USER)
+			break;
+		index = (index + (1 << i)) % acl_role_set.r_size;
+		match = acl_role_set.r_hash[index];
+		i = (i + 1) % 32;
+	}
+found:
+	if (match == NULL) {
+	      try_group:
+		index = rhash(gid, GR_ROLE_GROUP, acl_role_set.r_size);
+		match = acl_role_set.r_hash[index];
+		i = 0;
+
+		while (match) {
+			if ((match->roletype & (GR_ROLE_DOMAIN | GR_ROLE_GROUP)) == (GR_ROLE_DOMAIN | GR_ROLE_GROUP)) {
+				for (x = 0; x < match->domain_child_num; x++) {
+					if (match->domain_children[x] == gid)
+						goto found2;
+				}
+			} else if (match->uidgid == gid && match->roletype & GR_ROLE_GROUP)
+				break;
+			index = (index + (1 << i)) % acl_role_set.r_size;
+			match = acl_role_set.r_hash[index];
+			i = (i + 1) % 32;
+		}
+found2:
+		if (match == NULL)
+			match = default_role;
+		if (match->allowed_ips == NULL)
+			return match;
+		else {
+			for (ipp = match->allowed_ips; ipp; ipp = ipp->next) {
+				if (likely
+				    ((ntohl(task->curr_ip) & ipp->netmask) ==
+				     (ntohl(ipp->addr) & ipp->netmask)))
+					return match;
+			}
+			match = default_role;
+		}
+	} else if (match->allowed_ips == NULL) {
+		return match;
+	} else {
+		for (ipp = match->allowed_ips; ipp; ipp = ipp->next) {
+			if (likely
+			    ((ntohl(task->curr_ip) & ipp->netmask) ==
+			     (ntohl(ipp->addr) & ipp->netmask)))
+				return match;
+		}
+		goto try_group;
+	}
+
+	return match;
+}
+
+__inline__ struct acl_subject_label *
+lookup_acl_subj_label(const ino_t ino, const kdev_t dev,
+		      const struct acl_role_label *role)
+{
+	unsigned long subj_size = role->subj_hash_size;
+	struct acl_subject_label **s_hash = role->subj_hash;
+	unsigned long index = fhash(ino, dev, subj_size);
+	struct acl_subject_label *match;
+	__u8 i = 0;
+
+	match = s_hash[index];
+
+	while (match && (match->inode != ino || match->device != dev ||
+	       (match->mode & GR_DELETED))) {
+		index = (index + (1 << i)) % subj_size;
+		match = s_hash[index];
+		i = (i + 1) % 32;
+	}
+
+	if (match && (match != deleted_subject) && !(match->mode & GR_DELETED))
+		return match;
+	else
+		return NULL;
+}
+
+static __inline__ struct acl_object_label *
+lookup_acl_obj_label(const ino_t ino, const kdev_t dev,
+		     const struct acl_subject_label *subj)
+{
+	unsigned long obj_size = subj->obj_hash_size;
+	struct acl_object_label **o_hash = subj->obj_hash;
+	unsigned long index = fhash(ino, dev, obj_size);
+	struct acl_object_label *match;
+	__u8 i = 0;
+
+	match = o_hash[index];
+
+	while (match && (match->inode != ino || match->device != dev ||
+	       (match->mode & GR_DELETED))) {
+		index = (index + (1 << i)) % obj_size;
+		match = o_hash[index];
+		i = (i + 1) % 32;
+	}
+
+	if (match && (match != deleted_object) && !(match->mode & GR_DELETED))
+		return match;
+	else
+		return NULL;
+}
+
+static __inline__ struct acl_object_label *
+lookup_acl_obj_label_create(const ino_t ino, const kdev_t dev,
+		     const struct acl_subject_label *subj)
+{
+	unsigned long obj_size = subj->obj_hash_size;
+	struct acl_object_label **o_hash = subj->obj_hash;
+	unsigned long index = fhash(ino, dev, obj_size);
+	struct acl_object_label *match;
+	__u8 i = 0;
+
+	match = o_hash[index];
+
+	while (match && (match->inode != ino || match->device != dev ||
+	       !(match->mode & GR_DELETED))) {
+		index = (index + (1 << i)) % obj_size;
+		match = o_hash[index];
+		i = (i + 1) % 32;
+	}
+
+	if (match && (match != deleted_object) && (match->mode & GR_DELETED))
+		return match;
+
+	i = 0;
+	index = fhash(ino, dev, obj_size);
+	match = o_hash[index];
+
+	while (match && (match->inode != ino || match->device != dev ||
+	       (match->mode & GR_DELETED))) {
+		index = (index + (1 << i)) % obj_size;
+		match = o_hash[index];
+		i = (i + 1) % 32;
+	}
+
+	if (match && (match != deleted_object) && !(match->mode & GR_DELETED))
+		return match;
+	else
+		return NULL;
+}
+
+static __inline__ struct name_entry *
+lookup_name_entry(const char *name)
+{
+	__u16 len = strlen(name);
+	unsigned long index = nhash(name, len, name_set.n_size);
+	struct name_entry *match;
+	__u8 i = 0;
+
+	match = name_set.n_hash[index];
+
+	while (match && !gr_streq(match->name, name, match->len, len)) {
+		index = (index + (1 << i)) % name_set.n_size;
+		match = name_set.n_hash[index];
+		i = (i + 1) % 32;
+	}
+
+	return match;
+}
+
+static __inline__ struct name_entry *
+lookup_inodev_entry(const ino_t ino, const kdev_t dev)
+{
+	unsigned long index = fhash(ino, dev, inodev_set.n_size);
+	struct name_entry *match;
+	__u8 i = 0;
+
+	match = inodev_set.n_hash[index];
+
+	while (match && (match->inode != ino || match->device != dev)) {
+		index = (index + (1 << i)) % inodev_set.n_size;
+		match = inodev_set.n_hash[index];
+		i = (i + 1) % 32;
+	}
+
+	if (match && (match != deleted_inodev))
+		return match;
+	else
+		return NULL;
+}
+
+static void
+insert_inodev_entry(struct name_entry *nentry)
+{
+	unsigned long index = fhash(nentry->inode, nentry->device,
+				    inodev_set.n_size);
+	struct name_entry **curr;
+	__u8 i = 0;
+
+	curr = &inodev_set.n_hash[index];
+
+	while (*curr && *curr != deleted_inodev) {
+		index = (index + (1 << i)) % inodev_set.n_size;
+		curr = &inodev_set.n_hash[index];
+		i = (i + 1) % 32;
+	}
+
+	*curr = nentry;
+
+	return;
+}
+
+static void
+__insert_acl_role_label(struct acl_role_label *role, uid_t uidgid)
+{
+	unsigned long index =
+	    rhash(uidgid, role->roletype & (GR_ROLE_USER | GR_ROLE_GROUP), acl_role_set.r_size);
+	struct acl_role_label **curr;
+	__u8 i = 0;
+
+	curr = &acl_role_set.r_hash[index];
+
+	while (*curr) {
+		index = (index + (1 << i)) % acl_role_set.r_size;
+		curr = &acl_role_set.r_hash[index];
+		i = (i + 1) % 32;
+	}
+
+	*curr = role;
+
+	return;
+}
+
+static void
+insert_acl_role_label(struct acl_role_label *role)
+{
+	int i;
+
+	if (role->roletype & GR_ROLE_DOMAIN) {
+		for (i = 0; i < role->domain_child_num; i++)
+			__insert_acl_role_label(role, role->domain_children[i]);
+	} else
+		__insert_acl_role_label(role, role->uidgid);
+}
+					
+static int
+insert_name_entry(char *name, const ino_t inode, const kdev_t device)
+{
+	struct name_entry **curr;
+	__u8 i = 0;
+	__u16 len = strlen(name);
+	unsigned long index = nhash(name, len, name_set.n_size);
+
+	curr = &name_set.n_hash[index];
+
+	while (*curr && !gr_streq((*curr)->name, name, (*curr)->len, len)) {
+		index = (index + (1 << i)) % name_set.n_size;
+		curr = &name_set.n_hash[index];
+		i = (i + 1) % 32;
+	}
+
+	if (!(*curr)) {
+		struct name_entry *nentry =
+		    acl_alloc(sizeof (struct name_entry));
+		if (!nentry)
+			return 0;
+		nentry->name = name;
+		nentry->inode = inode;
+		nentry->device = device;
+		nentry->len = len;
+		*curr = nentry;
+		/* insert us into the table searchable by inode/dev */
+		insert_inodev_entry(nentry);
+	}
+
+	return 1;
+}
+
+static void
+insert_acl_obj_label(struct acl_object_label *obj,
+		     struct acl_subject_label *subj)
+{
+	unsigned long index =
+	    fhash(obj->inode, obj->device, subj->obj_hash_size);
+	struct acl_object_label **curr;
+	__u8 i = 0;
+
+	curr = &subj->obj_hash[index];
+
+	while (*curr && *curr != deleted_object) {
+		index = (index + (1 << i)) % subj->obj_hash_size;
+		curr = &subj->obj_hash[index];
+		i = (i + 1) % 32;
+	}
+
+	*curr = obj;
+
+	return;
+}
+
+static void
+insert_acl_subj_label(struct acl_subject_label *obj,
+		      struct acl_role_label *role)
+{
+	unsigned long subj_size = role->subj_hash_size;
+	struct acl_subject_label **s_hash = role->subj_hash;
+	unsigned long index = fhash(obj->inode, obj->device, subj_size);
+	struct acl_subject_label **curr;
+	__u8 i = 0;
+
+	curr = &s_hash[index];
+
+	while (*curr && *curr != deleted_subject) {
+		index = (index + (1 << i)) % subj_size;
+		curr = &s_hash[index];
+		i = (i + 1) % 32;
+	}
+
+	*curr = obj;
+
+	return;
+}
+
+static void **
+create_table(__u32 * len)
+{
+	unsigned long table_sizes[] = {
+		7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381,
+		32749, 65521, 131071, 262139, 524287, 1048573, 2097143,
+		4194301, 8388593, 16777213, 33554393, 67108859, 134217689,
+		268435399, 536870909, 1073741789, 2147483647
+	};
+	void *newtable = NULL;
+	unsigned int pwr = 0;
+
+	while ((pwr < ((sizeof (table_sizes) / sizeof (table_sizes[0])) - 1)) &&
+	       table_sizes[pwr] <= (2 * (*len)))
+		pwr++;
+
+	if (table_sizes[pwr] <= (2 * (*len)))
+		return newtable;
+
+	if ((table_sizes[pwr] * sizeof (void *)) <= PAGE_SIZE)
+		newtable =
+		    kmalloc(table_sizes[pwr] * sizeof (void *), GFP_KERNEL);
+	else
+		newtable = vmalloc(table_sizes[pwr] * sizeof (void *));
+
+	*len = table_sizes[pwr];
+
+	return newtable;
+}
+
+static int
+init_variables(const struct gr_arg *arg)
+{
+	unsigned long stacksize;
+
+	subj_map_set.s_size = arg->role_db.num_subjects;
+	acl_role_set.r_size = arg->role_db.num_roles + arg->role_db.num_domain_children;
+	name_set.n_size = arg->role_db.num_objects;
+	inodev_set.n_size = arg->role_db.num_objects;
+
+	if (!gr_init_uidset())
+		return 1;
+
+	/* set up the stack that holds allocation info */
+
+	stacksize = arg->role_db.num_pointers + 5;
+
+	if (!acl_alloc_stack_init(stacksize))
+		return 1;
+
+	/* create our empty, fake deleted acls */
+	deleted_subject =
+	    (struct acl_subject_label *)
+	    acl_alloc(sizeof (struct acl_subject_label));
+	deleted_object =
+	    (struct acl_object_label *)
+	    acl_alloc(sizeof (struct acl_object_label));
+	deleted_inodev =
+	    (struct name_entry *) acl_alloc(sizeof (struct name_entry));
+
+	if (!deleted_subject || !deleted_object || !deleted_inodev)
+		return 1;
+
+	memset(deleted_subject, 0, sizeof (struct acl_subject_label));
+	memset(deleted_object, 0, sizeof (struct acl_object_label));
+	memset(deleted_inodev, 0, sizeof (struct name_entry));
+
+	/* grab reference for the real root dentry and vfsmount */
+	read_lock(&child_reaper->fs->lock);
+	real_root_mnt = mntget(child_reaper->fs->rootmnt);
+	real_root = dget(child_reaper->fs->root);
+	read_unlock(&child_reaper->fs->lock);
+	
+
+	/* We only want 50% full tables for now */
+
+	subj_map_set.s_hash =
+	    (struct subject_map **) create_table(&subj_map_set.s_size);
+	acl_role_set.r_hash =
+	    (struct acl_role_label **) create_table(&acl_role_set.r_size);
+	name_set.n_hash = (struct name_entry **) create_table(&name_set.n_size);
+	inodev_set.n_hash =
+	    (struct name_entry **) create_table(&inodev_set.n_size);
+
+	if (!subj_map_set.s_hash || !acl_role_set.r_hash || 
+	    !name_set.n_hash || !inodev_set.n_hash)
+		return 1;
+
+	memset(subj_map_set.s_hash, 0,
+	       sizeof(struct subject_map *) * subj_map_set.s_size);
+	memset(acl_role_set.r_hash, 0,
+	       sizeof (struct acl_role_label *) * acl_role_set.r_size);
+	memset(name_set.n_hash, 0,
+	       sizeof (struct name_entry *) * name_set.n_size);
+	memset(inodev_set.n_hash, 0,
+	       sizeof (struct name_entry *) * inodev_set.n_size);
+
+	return 0;
+}
+
+/* free information not needed after startup
+   currently contains user->kernel pointer mappings for subjects
+*/
+
+static void
+free_init_variables(void)
+{
+	__u32 i;
+
+	if (subj_map_set.s_hash) {
+		for (i = 0; i < subj_map_set.s_size; i++) {
+			if (subj_map_set.s_hash[i]) {
+				kfree(subj_map_set.s_hash[i]);
+				subj_map_set.s_hash[i] = NULL;
+			}
+		}
+
+		if ((subj_map_set.s_size * sizeof (struct subject_map *)) <=
+		    PAGE_SIZE)
+			kfree(subj_map_set.s_hash);
+		else
+			vfree(subj_map_set.s_hash);
+	}
+
+	return;
+}
+
+static void
+free_variables(void)
+{
+	struct acl_subject_label *s;
+	struct acl_role_label *r;
+	struct task_struct *task;
+
+	gr_clear_learn_entries();
+
+	read_lock(&tasklist_lock);
+	for_each_task(task) {
+		task->acl_sp_role = 0;
+		task->acl_role_id = 0;
+		task->acl = NULL;
+		task->role = NULL;
+	}
+	read_unlock(&tasklist_lock);
+
+	/* release the reference to the real root dentry and vfsmount */
+	if (real_root)
+		dput(real_root);
+	real_root = NULL;
+	if (real_root_mnt)
+		mntput(real_root_mnt);
+	real_root_mnt = NULL;
+
+	/* free all object hash tables */
+
+	if (role_list_head) {
+		for (r = role_list_head; r; r = r->next) {
+			if (!r->subj_hash)
+				break;
+			for (s = r->hash->first; s; s = s->next) {
+				if (!s->obj_hash)
+					break;
+				if ((s->obj_hash_size *
+				     sizeof (struct acl_object_label *)) <=
+				    PAGE_SIZE)
+					kfree(s->obj_hash);
+				else
+					vfree(s->obj_hash);
+			}
+			if ((r->subj_hash_size *
+			     sizeof (struct acl_subject_label *)) <= PAGE_SIZE)
+				kfree(r->subj_hash);
+			else
+				vfree(r->subj_hash);
+		}
+	}
+
+	acl_free_all();
+
+	if (acl_role_set.r_hash) {
+		if ((acl_role_set.r_size * sizeof (struct acl_role_label *)) <=
+		    PAGE_SIZE)
+			kfree(acl_role_set.r_hash);
+		else
+			vfree(acl_role_set.r_hash);
+	}
+	if (name_set.n_hash) {
+		if ((name_set.n_size * sizeof (struct name_entry *)) <=
+		    PAGE_SIZE)
+			kfree(name_set.n_hash);
+		else
+			vfree(name_set.n_hash);
+	}
+
+	if (inodev_set.n_hash) {
+		if ((inodev_set.n_size * sizeof (struct name_entry *)) <=
+		    PAGE_SIZE)
+			kfree(inodev_set.n_hash);
+		else
+			vfree(inodev_set.n_hash);
+	}
+
+	gr_free_uidset();
+
+	memset(&name_set, 0, sizeof (struct name_db));
+	memset(&inodev_set, 0, sizeof (struct name_db));
+	memset(&acl_role_set, 0, sizeof (struct acl_role_db));
+	memset(&subj_map_set, 0, sizeof (struct acl_subj_map_db));
+
+	role_list_head = NULL;
+	default_role = NULL;
+
+	return;
+}
+
+static __u32
+count_user_objs(struct acl_object_label *userp)
+{
+	struct acl_object_label o_tmp;
+	__u32 num = 0;
+
+	while (userp) {
+		if (copy_from_user(&o_tmp, userp,
+				   sizeof (struct acl_object_label)))
+			break;
+
+		userp = o_tmp.prev;
+		num++;
+	}
+
+	return num;
+}
+
+static struct acl_subject_label *
+do_copy_user_subj(struct acl_subject_label *userp, struct acl_role_label *role);
+
+static int
+copy_user_glob(struct acl_object_label *obj)
+{
+	struct acl_object_label *g_tmp, **guser, *glast = NULL;
+	unsigned int len;
+	char *tmp;
+
+	if (obj->globbed == NULL)
+		return 0;
+
+	guser = &obj->globbed;
+	while (*guser) {
+		g_tmp = (struct acl_object_label *)
+			acl_alloc(sizeof (struct acl_object_label));
+		if (g_tmp == NULL)
+			return -ENOMEM;
+
+		if (copy_from_user(g_tmp, *guser,
+				   sizeof (struct acl_object_label)))
+			return -EFAULT;
+
+		len = strnlen_user(g_tmp->filename, PATH_MAX);
+
+		if (!len || len >= PATH_MAX)
+			return -EINVAL;
+
+		if ((tmp = (char *) acl_alloc(len)) == NULL)
+			return -ENOMEM;
+
+		if (copy_from_user(tmp, g_tmp->filename, len))
+			return -EFAULT;
+
+		g_tmp->filename = tmp;
+
+		if (glast)
+			glast->next = g_tmp;
+		g_tmp->prev = glast;
+		*guser = g_tmp;
+		glast = g_tmp;
+		guser = &((*guser)->next);
+	}
+		
+	return 0;
+}
+
+static int
+copy_user_objs(struct acl_object_label *userp, struct acl_subject_label *subj,
+	       struct acl_role_label *role)
+{
+	struct acl_object_label *o_tmp;
+	unsigned int len;
+	int ret;
+	char *tmp;
+
+	while (userp) {
+		if ((o_tmp = (struct acl_object_label *)
+		     acl_alloc(sizeof (struct acl_object_label))) == NULL)
+			return -ENOMEM;
+
+		if (copy_from_user(o_tmp, userp,
+				   sizeof (struct acl_object_label)))
+			return -EFAULT;
+
+		userp = o_tmp->prev;
+
+		len = strnlen_user(o_tmp->filename, PATH_MAX);
+
+		if (!len || len >= PATH_MAX)
+			return -EINVAL;
+
+		if ((tmp = (char *) acl_alloc(len)) == NULL)
+			return -ENOMEM;
+
+		if (copy_from_user(tmp, o_tmp->filename, len))
+			return -EFAULT;
+
+		o_tmp->filename = tmp;
+
+		insert_acl_obj_label(o_tmp, subj);
+		if (!insert_name_entry(o_tmp->filename, o_tmp->inode,
+				       o_tmp->device))
+			return -ENOMEM;
+
+		ret = copy_user_glob(o_tmp);
+		if (ret)
+			return ret;
+
+		if (o_tmp->nested) {
+			o_tmp->nested = do_copy_user_subj(o_tmp->nested, role);
+			if (IS_ERR(o_tmp->nested))
+				return PTR_ERR(o_tmp->nested);
+
+			s_final = o_tmp->nested;
+		}
+	}
+
+	return 0;
+}
+
+static __u32
+count_user_subjs(struct acl_subject_label *userp)
+{
+	struct acl_subject_label s_tmp;
+	__u32 num = 0;
+
+	while (userp) {
+		if (copy_from_user(&s_tmp, userp,
+				   sizeof (struct acl_subject_label)))
+			break;
+
+		userp = s_tmp.prev;
+		/* do not count nested subjects against this count, since
+		   they are not included in the hash table, but are
+		   attached to objects.  We have already counted
+		   the subjects in userspace for the allocation 
+		   stack
+		*/
+		if (!(s_tmp.mode & GR_NESTED))
+			num++;
+	}
+
+	return num;
+}
+
+static int
+copy_user_allowedips(struct acl_role_label *rolep)
+{
+	struct role_allowed_ip *ruserip, *rtmp = NULL, *rlast;
+
+	ruserip = rolep->allowed_ips;
+
+	while (ruserip) {
+		rlast = rtmp;
+
+		if ((rtmp = (struct role_allowed_ip *)
+		     acl_alloc(sizeof (struct role_allowed_ip))) == NULL)
+			return -ENOMEM;
+
+		if (copy_from_user(rtmp, ruserip,
+				   sizeof (struct role_allowed_ip)))
+			return -EFAULT;
+
+		ruserip = rtmp->prev;
+
+		if (!rlast) {
+			rtmp->prev = NULL;
+			rolep->allowed_ips = rtmp;
+		} else {
+			rlast->next = rtmp;
+			rtmp->prev = rlast;
+		}
+
+		if (!ruserip)
+			rtmp->next = NULL;
+	}
+
+	return 0;
+}
+
+static int
+copy_user_transitions(struct acl_role_label *rolep)
+{
+	struct role_transition *rusertp, *rtmp = NULL, *rlast;
+	unsigned int len;
+	char *tmp;
+
+	rusertp = rolep->transitions;
+
+	while (rusertp) {
+		rlast = rtmp;
+
+		if ((rtmp = (struct role_transition *)
+		     acl_alloc(sizeof (struct role_transition))) == NULL)
+			return -ENOMEM;
+
+		if (copy_from_user(rtmp, rusertp,
+				   sizeof (struct role_transition)))
+			return -EFAULT;
+
+		rusertp = rtmp->prev;
+
+		len = strnlen_user(rtmp->rolename, GR_SPROLE_LEN);
+
+		if (!len || len >= GR_SPROLE_LEN)
+			return -EINVAL;
+
+		if ((tmp = (char *) acl_alloc(len)) == NULL)
+			return -ENOMEM;
+
+		if (copy_from_user(tmp, rtmp->rolename, len))
+			return -EFAULT;
+
+		rtmp->rolename = tmp;
+
+		if (!rlast) {
+			rtmp->prev = NULL;
+			rolep->transitions = rtmp;
+		} else {
+			rlast->next = rtmp;
+			rtmp->prev = rlast;
+		}
+
+		if (!rusertp)
+			rtmp->next = NULL;
+	}
+
+	return 0;
+}
+
+static struct acl_subject_label *
+do_copy_user_subj(struct acl_subject_label *userp, struct acl_role_label *role)
+{
+	struct acl_subject_label *s_tmp = NULL, *s_tmp2;
+	unsigned int len;
+	char *tmp;
+	__u32 num_objs;
+	struct acl_ip_label **i_tmp, *i_utmp2;
+	struct gr_hash_struct ghash;
+	struct subject_map *subjmap;
+	unsigned long i_num;
+	int err;
+
+	s_tmp = lookup_subject_map(userp);
+
+	/* we've already copied this subject into the kernel, just return
+	   the reference to it, and don't copy it over again
+	*/
+	if (s_tmp)
+		return(s_tmp);
+
+	if ((s_tmp = (struct acl_subject_label *)
+	    acl_alloc(sizeof (struct acl_subject_label))) == NULL)
+		return ERR_PTR(-ENOMEM);
+
+	subjmap = (struct subject_map *)kmalloc(sizeof (struct subject_map), GFP_KERNEL);
+	if (subjmap == NULL)
+		return ERR_PTR(-ENOMEM);
+
+	subjmap->user = userp;
+	subjmap->kernel = s_tmp;
+	insert_subj_map_entry(subjmap);
+
+	if (copy_from_user(s_tmp, userp,
+			   sizeof (struct acl_subject_label)))
+		return ERR_PTR(-EFAULT);
+
+	if (!s_last) {
+		s_tmp->prev = NULL;
+		role->hash->first = s_tmp;
+	} else {
+		s_last->next = s_tmp;
+		s_tmp->prev = s_last;
+	}
+
+	s_last = s_tmp;
+
+	len = strnlen_user(s_tmp->filename, PATH_MAX);
+
+	if (!len || len >= PATH_MAX)
+		return ERR_PTR(-EINVAL);
+
+	if ((tmp = (char *) acl_alloc(len)) == NULL)
+		return ERR_PTR(-ENOMEM);
+
+	if (copy_from_user(tmp, s_tmp->filename, len))
+		return ERR_PTR(-EFAULT);
+
+	s_tmp->filename = tmp;
+
+	if (!strcmp(s_tmp->filename, "/"))
+		role->root_label = s_tmp;
+
+	if (copy_from_user(&ghash, s_tmp->hash, sizeof(struct gr_hash_struct)))
+		return ERR_PTR(-EFAULT);
+
+	/* copy user and group transition tables */
+
+	if (s_tmp->user_trans_num) {
+		uid_t *uidlist;
+
+		uidlist = (uid_t *)acl_alloc(s_tmp->user_trans_num * sizeof(uid_t));
+		if (uidlist == NULL)
+			return ERR_PTR(-ENOMEM);
+		if (copy_from_user(uidlist, s_tmp->user_transitions, s_tmp->user_trans_num * sizeof(uid_t)))
+			return ERR_PTR(-EFAULT);
+
+		s_tmp->user_transitions = uidlist;
+	}
+
+	if (s_tmp->group_trans_num) {
+		gid_t *gidlist;
+
+		gidlist = (gid_t *)acl_alloc(s_tmp->group_trans_num * sizeof(gid_t));
+		if (gidlist == NULL)
+			return ERR_PTR(-ENOMEM);
+		if (copy_from_user(gidlist, s_tmp->group_transitions, s_tmp->group_trans_num * sizeof(gid_t)))
+			return ERR_PTR(-EFAULT);
+
+		s_tmp->group_transitions = gidlist;
+	}
+
+	/* set up object hash table */
+	num_objs = count_user_objs(ghash.first);
+
+	s_tmp->obj_hash_size = num_objs;
+	s_tmp->obj_hash =
+	    (struct acl_object_label **)
+	    create_table(&(s_tmp->obj_hash_size));
+
+	if (!s_tmp->obj_hash)
+		return ERR_PTR(-ENOMEM);
+
+	memset(s_tmp->obj_hash, 0,
+	       s_tmp->obj_hash_size *
+	       sizeof (struct acl_object_label *));
+
+	/* copy before adding in objects, since a nested
+	   acl could be found and be the final subject
+	   copied
+	*/
+
+	s_final = s_tmp;
+
+	/* add in objects */
+	err = copy_user_objs(ghash.first, s_tmp, role);
+
+	if (err)
+		return ERR_PTR(err);
+
+	/* set pointer for parent subject */
+	if (s_tmp->parent_subject) {
+		s_tmp2 = do_copy_user_subj(s_tmp->parent_subject, role);
+
+		if (IS_ERR(s_tmp2))
+			return s_tmp2;
+
+		s_tmp->parent_subject = s_tmp2;
+	}
+
+	/* add in ip acls */
+
+	if (!s_tmp->ip_num) {
+		s_tmp->ips = NULL;
+		goto insert;
+	}
+
+	i_tmp =
+	    (struct acl_ip_label **) acl_alloc(s_tmp->ip_num *
+					       sizeof (struct
+						       acl_ip_label *));
+
+	if (!i_tmp)
+		return ERR_PTR(-ENOMEM);
+
+	for (i_num = 0; i_num < s_tmp->ip_num; i_num++) {
+		*(i_tmp + i_num) =
+		    (struct acl_ip_label *)
+		    acl_alloc(sizeof (struct acl_ip_label));
+		if (!*(i_tmp + i_num))
+			return ERR_PTR(-ENOMEM);
+
+		if (copy_from_user
+		    (&i_utmp2, s_tmp->ips + i_num,
+		     sizeof (struct acl_ip_label *)))
+			return ERR_PTR(-EFAULT);
+
+		if (copy_from_user
+		    (*(i_tmp + i_num), i_utmp2,
+		     sizeof (struct acl_ip_label)))
+			return ERR_PTR(-EFAULT);
+	}
+
+	s_tmp->ips = i_tmp;
+
+insert:
+	if (!insert_name_entry(s_tmp->filename, s_tmp->inode,
+			       s_tmp->device))
+		return ERR_PTR(-ENOMEM);
+
+	return s_tmp;
+}
+
+static int
+copy_user_subjs(struct acl_subject_label *userp, struct acl_role_label *role)
+{
+	struct acl_subject_label s_pre;
+	struct acl_subject_label * ret;
+	int err;
+
+	while (userp) {
+		if (copy_from_user(&s_pre, userp,
+				   sizeof (struct acl_subject_label)))
+			return -EFAULT;
+		
+		/* do not add nested subjects here, add
+		   while parsing objects
+		*/
+
+		if (s_pre.mode & GR_NESTED) {
+			userp = s_pre.prev;
+			continue;
+		}
+
+		ret = do_copy_user_subj(userp, role);
+
+		err = PTR_ERR(ret);
+		if (IS_ERR(ret))
+			return err;
+
+		insert_acl_subj_label(ret, role);
+
+		userp = s_pre.prev;
+	}
+
+	s_final->next = NULL;
+
+	return 0;
+}
+
+static int
+copy_user_acl(struct gr_arg *arg)
+{
+	struct acl_role_label *r_tmp = NULL, **r_utmp, *r_utmp2, *r_last;
+	struct sprole_pw *sptmp;
+	struct gr_hash_struct *ghash;
+	uid_t *domainlist;
+	unsigned long r_num;
+	unsigned int len;
+	char *tmp;
+	int err = 0;
+	__u16 i;
+	__u32 num_subjs;
+
+	/* we need a default and kernel role */
+	if (arg->role_db.num_roles < 2)
+		return -EINVAL;
+
+	/* copy special role authentication info from userspace */
+
+	num_sprole_pws = arg->num_sprole_pws;
+	acl_special_roles = (struct sprole_pw **) acl_alloc(num_sprole_pws * sizeof(struct sprole_pw *));
+
+	if (!acl_special_roles) {
+		err = -ENOMEM;
+		goto cleanup;
+	}
+
+	for (i = 0; i < num_sprole_pws; i++) {
+		sptmp = (struct sprole_pw *) acl_alloc(sizeof(struct sprole_pw));
+		if (!sptmp) {
+			err = -ENOMEM;
+			goto cleanup;
+		}
+		if (copy_from_user(sptmp, arg->sprole_pws + i,
+				   sizeof (struct sprole_pw))) {
+			err = -EFAULT;
+			goto cleanup;
+		}
+
+		len =
+		    strnlen_user(sptmp->rolename, GR_SPROLE_LEN);
+
+		if (!len || len >= GR_SPROLE_LEN) {
+			err = -EINVAL;
+			goto cleanup;
+		}
+
+		if ((tmp = (char *) acl_alloc(len)) == NULL) {
+			err = -ENOMEM;
+			goto cleanup;
+		}
+
+		if (copy_from_user(tmp, sptmp->rolename, len)) {
+			err = -EFAULT;
+			goto cleanup;
+		}
+
+#ifdef CONFIG_GRKERNSEC_ACL_DEBUG
+		printk(KERN_ALERT "Copying special role %s\n", tmp);
+#endif
+		sptmp->rolename = tmp;
+		acl_special_roles[i] = sptmp;
+	}
+
+	r_utmp = (struct acl_role_label **) arg->role_db.r_table;
+
+	for (r_num = 0; r_num < arg->role_db.num_roles; r_num++) {
+		r_last = r_tmp;
+
+		r_tmp = acl_alloc(sizeof (struct acl_role_label));
+
+		if (!r_tmp) {
+			err = -ENOMEM;
+			goto cleanup;
+		}
+
+		if (copy_from_user(&r_utmp2, r_utmp + r_num,
+				   sizeof (struct acl_role_label *))) {
+			err = -EFAULT;
+			goto cleanup;
+		}
+
+		if (copy_from_user(r_tmp, r_utmp2,
+				   sizeof (struct acl_role_label))) {
+			err = -EFAULT;
+			goto cleanup;
+		}
+
+		if (!r_last) {
+			r_tmp->prev = NULL;
+			role_list_head = r_tmp;
+		} else {
+			r_last->next = r_tmp;
+			r_tmp->prev = r_last;
+		}
+
+		if (r_num == (arg->role_db.num_roles - 1))
+			r_tmp->next = NULL;
+
+		len = strnlen_user(r_tmp->rolename, GR_SPROLE_LEN);
+
+		if (!len || len >= PATH_MAX) {
+			err = -EINVAL;
+			goto cleanup;
+		}
+
+		if ((tmp = (char *) acl_alloc(len)) == NULL) {
+			err = -ENOMEM;
+			goto cleanup;
+		}
+		if (copy_from_user(tmp, r_tmp->rolename, len)) {
+			err = -EFAULT;
+			goto cleanup;
+		}
+		r_tmp->rolename = tmp;
+
+		if (!strcmp(r_tmp->rolename, "default")
+		    && (r_tmp->roletype & GR_ROLE_DEFAULT)) {
+			default_role = r_tmp;
+		} else if (!strcmp(r_tmp->rolename, ":::kernel:::")) {
+			kernel_role = r_tmp;
+		}
+
+		if ((ghash = (struct gr_hash_struct *) acl_alloc(sizeof(struct gr_hash_struct))) == NULL) {
+			err = -ENOMEM;
+			goto cleanup;
+		}
+		if (copy_from_user(ghash, r_tmp->hash, sizeof(struct gr_hash_struct))) {
+			err = -EFAULT;
+			goto cleanup;
+		}
+
+		r_tmp->hash = ghash;
+
+		num_subjs = count_user_subjs(r_tmp->hash->first);
+
+		r_tmp->subj_hash_size = num_subjs;
+		r_tmp->subj_hash =
+		    (struct acl_subject_label **)
+		    create_table(&(r_tmp->subj_hash_size));
+
+		if (!r_tmp->subj_hash) {
+			err = -ENOMEM;
+			goto cleanup;
+		}
+
+		err = copy_user_allowedips(r_tmp);
+		if (err)
+			goto cleanup;
+
+		/* copy domain info */
+		if (r_tmp->domain_children != NULL) {
+			domainlist = acl_alloc(r_tmp->domain_child_num * sizeof(uid_t));
+			if (domainlist == NULL) {
+				err = -ENOMEM;
+				goto cleanup;
+			}
+			if (copy_from_user(domainlist, r_tmp->domain_children, r_tmp->domain_child_num * sizeof(uid_t))) {
+				err = -EFAULT;
+				goto cleanup;
+			}
+			r_tmp->domain_children = domainlist;
+		}
+
+		err = copy_user_transitions(r_tmp);
+		if (err)
+			goto cleanup;
+
+		memset(r_tmp->subj_hash, 0,
+		       r_tmp->subj_hash_size *
+		       sizeof (struct acl_subject_label *));
+
+		s_last = NULL;
+
+		err = copy_user_subjs(r_tmp->hash->first, r_tmp);
+
+		if (err)
+			goto cleanup;
+
+		insert_acl_role_label(r_tmp);
+	}
+
+      cleanup:
+	return err;
+
+}
+
+static int
+gracl_init(struct gr_arg *args)
+{
+	int error = 0;
+
+	memcpy(gr_system_salt, args->salt, GR_SALT_LEN);
+	memcpy(gr_system_sum, args->sum, GR_SHA_LEN);
+
+	if (init_variables(args)) {
+		security_alert_good(GR_INITF_ACL_MSG, GR_VERSION);
+		error = -ENOMEM;
+		free_variables();
+		goto out;
+	}
+
+	error = copy_user_acl(args);
+	free_init_variables();
+	if (error) {
+		free_variables();
+		goto out;
+	}
+
+	if ((error = gr_set_acls(0))) {
+		free_variables();
+		goto out;
+	}
+
+	gr_status |= GR_READY;
+      out:
+	return error;
+}
+
+/* derived from glibc fnmatch() 0: match, 1: no match*/
+
+static int
+glob_match(const char *pattern, const char *string)
+{
+	const char *p = pattern, *n = string;
+	char c;
+
+	while ((c = *p++) != '\0') {
+	switch (c) {
+		case '?':
+			if (*n == '\0')
+				return 1;
+			else if (*n == '/')
+				return 1;
+			break;
+		case '\\':
+			if (*n != c)
+				return 1;
+			break;
+		case '*':
+			for (c = *p++; c == '?' || c == '*'; c = *p++, ++n)
+				if ((*n == '/') || (c == '?' && *n == '\0'))
+					return 1;
+			if (c == '\0')
+				return 0;
+			{
+				char c1 = c;
+				for (--p; *n != '\0'; ++n)
+					if (((c == '[') || (*n == c1)) && !glob_match(p, n))
+						return 0;
+				return 1;
+			}
+		case '[':
+			{
+			int not;
+
+			if (*n == '\0')
+				return 1;
+			not = (*p == '!' || *p == '^');
+			if (not)
+				++p;
+
+			c = *p++;
+			for (;;) {
+				char cstart = c, cend = c;
+
+				if (c == '\0')
+					return 1;
+				c = *p++;
+
+				if (c == '/')
+					return 1;
+
+				if (c == '-' && *p != ']') {
+					cend = *p++;
+					if (cend == '\0')
+						return 1;
+					c = *p++;
+				}
+
+				if (*n >= cstart && *n <= cend)
+					goto matched;
+
+				if (c == ']')
+					break;
+			}
+			if (!not)
+				return 1;
+			break;
+		matched:
+			while (c != ']') {
+				if (c == '\0')
+					return 1;
+
+				c = *p++;
+			}
+			if (not)
+				return 1;
+		}
+		break;
+	default:
+		if (c != *n)
+			return 1;
+	}
+
+	++n;
+	}
+				
+	if (*n == '\0')
+		return 0;
+
+	if (*n == '/')
+		return 0;
+
+	return 1;
+}
+
+static struct acl_object_label *
+chk_glob_label(struct acl_object_label *globbed,
+		struct dentry *dentry, struct vfsmount *mnt, char **path)
+{
+	struct acl_object_label *tmp;
+
+	if (*path == NULL)
+		*path = gr_to_filename_nolock(dentry, mnt);
+
+	tmp = globbed;
+
+	while (tmp) {
+		if (!glob_match(tmp->filename, *path))
+			return tmp;
+		tmp = tmp->next;
+	}
+
+	return NULL;
+}
+
+static __inline__ struct acl_object_label *
+full_lookup(const struct dentry *orig_dentry, const struct vfsmount *orig_mnt,
+	    struct dentry *curr_dentry,
+	    const struct acl_subject_label *subj, char **path)
+{
+	struct acl_subject_label *tmpsubj;
+	struct acl_object_label *retval;
+	struct acl_object_label *retval2;
+
+	tmpsubj = (struct acl_subject_label *) subj;
+	read_lock(&gr_inode_lock);
+	do {
+		retval =
+		    	lookup_acl_obj_label(curr_dentry->d_inode->i_ino,
+					 curr_dentry->d_inode->i_dev, tmpsubj);
+		if (retval) {
+			if (retval->globbed) {
+				retval2 = chk_glob_label(retval->globbed, (struct dentry *)orig_dentry,
+						  (struct vfsmount *)orig_mnt, path);
+				if (retval2)
+					retval = retval2;
+			}
+			break;
+		}
+	} while ((tmpsubj = tmpsubj->parent_subject));
+	read_unlock(&gr_inode_lock);
+
+	return retval;
+}
+
+static struct acl_object_label *
+chk_obj_label(const struct dentry *l_dentry, const struct vfsmount *l_mnt,
+	      const struct acl_subject_label *subj)
+{
+	struct dentry *dentry = (struct dentry *) l_dentry;
+	struct vfsmount *mnt = (struct vfsmount *) l_mnt;
+	struct acl_object_label *retval;
+	char *path = NULL;
+
+	spin_lock(&dcache_lock);
+
+	for (;;) {
+		if (dentry == real_root && mnt == real_root_mnt)
+			break;
+		if (dentry == mnt->mnt_root || IS_ROOT(dentry)) {
+			if (mnt->mnt_parent == mnt)
+				break;
+
+			retval = full_lookup(l_dentry, l_mnt, dentry, subj, &path);
+			if (retval != NULL)
+				goto out;
+
+			dentry = mnt->mnt_mountpoint;
+			mnt = mnt->mnt_parent;
+			continue;
+		}
+
+		retval = full_lookup(l_dentry, l_mnt, dentry, subj, &path);
+		if (retval != NULL)
+			goto out;
+
+		dentry = dentry->d_parent;
+	}
+
+	retval = full_lookup(l_dentry, l_mnt, dentry, subj, &path);
+
+	if (retval == NULL)
+		retval = full_lookup(l_dentry, l_mnt, real_root, subj, &path);
+      out:
+	spin_unlock(&dcache_lock);
+
+	return retval;
+}
+
+static struct acl_object_label *
+chk_obj_create_label(const struct dentry *l_dentry, const struct vfsmount *l_mnt,
+	      const struct acl_subject_label *subj, char *path)
+{
+	struct dentry *dentry = (struct dentry *) l_dentry;
+	struct vfsmount *mnt = (struct vfsmount *) l_mnt;
+	struct acl_object_label *retval;
+
+	spin_lock(&dcache_lock);
+
+	for (;;) {
+		if (dentry == real_root && mnt == real_root_mnt)
+			break;
+		if (dentry == mnt->mnt_root || IS_ROOT(dentry)) {
+			if (mnt->mnt_parent == mnt)
+				break;
+
+			retval = full_lookup(l_dentry, l_mnt, dentry, subj, &path);
+			if (retval != NULL)
+				goto out;
+
+			dentry = mnt->mnt_mountpoint;
+			mnt = mnt->mnt_parent;
+			continue;
+		}
+
+		retval = full_lookup(l_dentry, l_mnt, dentry, subj, &path);
+		if (retval != NULL)
+			goto out;
+
+		dentry = dentry->d_parent;
+	}
+
+	retval = full_lookup(l_dentry, l_mnt, dentry, subj, &path);
+
+	if (retval == NULL)
+		retval = full_lookup(l_dentry, l_mnt, real_root, subj, &path);
+      out:
+	spin_unlock(&dcache_lock);
+
+	return retval;
+}
+
+static struct acl_subject_label *
+chk_subj_label(const struct dentry *l_dentry, const struct vfsmount *l_mnt,
+	       const struct acl_role_label *role)
+{
+	struct dentry *dentry = (struct dentry *) l_dentry;
+	struct vfsmount *mnt = (struct vfsmount *) l_mnt;
+	struct acl_subject_label *retval;
+
+	spin_lock(&dcache_lock);
+
+	for (;;) {
+		if (unlikely(dentry == real_root && mnt == real_root_mnt))
+			break;
+		if (unlikely(dentry == mnt->mnt_root || IS_ROOT(dentry))) {
+			if (mnt->mnt_parent == mnt)
+				break;
+
+			read_lock(&gr_inode_lock);
+			retval =
+			    lookup_acl_subj_label(dentry->d_inode->i_ino,
+						  dentry->d_inode->i_dev, role);
+			read_unlock(&gr_inode_lock);
+			if (unlikely(retval != NULL))
+				goto out;
+
+			dentry = mnt->mnt_mountpoint;
+			mnt = mnt->mnt_parent;
+			continue;
+		}
+
+		read_lock(&gr_inode_lock);
+		retval =
+		    lookup_acl_subj_label(dentry->d_inode->i_ino,
+					  dentry->d_inode->i_dev, role);
+		read_unlock(&gr_inode_lock);
+		if (unlikely(retval != NULL))
+			goto out;
+
+		dentry = dentry->d_parent;
+	}
+
+	read_lock(&gr_inode_lock);
+	retval =
+	    lookup_acl_subj_label(dentry->d_inode->i_ino,
+				  dentry->d_inode->i_dev, role);
+	read_unlock(&gr_inode_lock);
+
+	if (unlikely(retval == NULL)) {
+		read_lock(&gr_inode_lock);
+		retval =
+		    lookup_acl_subj_label(real_root->d_inode->i_ino,
+					  real_root->d_inode->i_dev, role);
+		read_unlock(&gr_inode_lock);
+	}
+      out:
+	spin_unlock(&dcache_lock);
+
+	return retval;
+}
+
+static __inline__ void
+gr_log_learn(const struct acl_role_label *role, const uid_t uid, const gid_t gid,
+	     const struct task_struct *task, const char *pathname,
+	     const __u32 mode)
+{
+	security_learn(GR_LEARN_AUDIT_MSG, role->rolename, role->roletype,
+		       uid, gid, task->exec_file ? gr_to_filename1(task->exec_file->f_dentry,
+		       task->exec_file->f_vfsmnt) : task->acl->filename, task->acl->filename,
+		       1, 1, pathname, (unsigned long) mode, NIPQUAD(task->curr_ip));
+
+	return;
+}
+
+__u32
+gr_check_link(const struct dentry * new_dentry,
+	      const struct dentry * parent_dentry,
+	      const struct vfsmount * parent_mnt,
+	      const struct dentry * old_dentry, const struct vfsmount * old_mnt)
+{
+	struct acl_object_label *obj;
+	__u32 oldmode, newmode;
+
+	if (unlikely(!(gr_status & GR_READY)))
+		return (GR_WRITE | GR_CREATE);
+
+	obj = chk_obj_label(old_dentry, old_mnt, current->acl);
+	oldmode = obj->mode;
+
+	if (current->acl->mode & GR_LEARN)
+		oldmode |= (GR_WRITE | GR_CREATE);
+	newmode =
+	    gr_check_create(new_dentry, parent_dentry, parent_mnt,
+			    oldmode | GR_CREATE | GR_AUDIT_CREATE |
+			    GR_AUDIT_WRITE | GR_SUPPRESS);
+
+	if ((newmode & oldmode) == oldmode)
+		return newmode;
+	else if (current->acl->mode & GR_LEARN) {
+		gr_log_learn(current->role, current->uid, current->gid,
+			current, gr_to_filename(old_dentry, old_mnt), oldmode);
+		return (GR_WRITE | GR_CREATE);
+	} else if (newmode & GR_SUPPRESS)
+		return GR_SUPPRESS;
+	else
+		return 0;
+}
+
+__u32
+gr_search_file(const struct dentry * dentry, const __u32 mode,
+	       const struct vfsmount * mnt)
+{
+	__u32 retval = mode;
+	struct acl_subject_label *curracl;
+	struct acl_object_label *currobj;
+
+	if (unlikely(!(gr_status & GR_READY)))
+		return (mode & ~GR_AUDITS);
+
+	curracl = current->acl;
+
+	currobj = chk_obj_label(dentry, mnt, curracl);
+	retval = currobj->mode & mode;
+
+	if (unlikely
+	    ((curracl->mode & GR_LEARN) && !(mode & GR_NOPTRACE)
+	     && (retval != (mode & ~(GR_AUDITS | GR_SUPPRESS))))) {
+		__u32 new_mode = mode;
+
+		new_mode &= ~(GR_AUDITS | GR_SUPPRESS);
+
+		retval = new_mode;
+
+		if (!(mode & GR_NOLEARN))
+			gr_log_learn(current->role, current->uid, current->gid,
+				     current, gr_to_filename(dentry, mnt), new_mode);
+	}
+
+	return retval;
+}
+
+__u32
+gr_check_create(const struct dentry * new_dentry, const struct dentry * parent,
+		const struct vfsmount * mnt, const __u32 mode)
+{
+	struct name_entry *match;
+	struct acl_object_label *matchpo;
+	struct acl_subject_label *curracl;
+	char *path;
+	__u32 retval;
+
+	if (unlikely(!(gr_status & GR_READY)))
+		return (mode & ~GR_AUDITS);
+
+	path = gr_to_filename(new_dentry, mnt);
+	match = lookup_name_entry(path);
+
+	if (!match)
+		goto check_parent;
+
+	curracl = current->acl;
+
+	read_lock(&gr_inode_lock);
+	matchpo = lookup_acl_obj_label_create(match->inode, match->device, curracl);
+	read_unlock(&gr_inode_lock);
+
+	if (matchpo) {
+		if ((matchpo->mode & mode) !=
+		    (mode & ~(GR_AUDITS | GR_SUPPRESS))
+		    && curracl->mode & GR_LEARN) {
+			__u32 new_mode = mode;
+
+			new_mode &= ~(GR_AUDITS | GR_SUPPRESS);
+
+			gr_log_learn(current->role, current->uid, current->gid,
+				     current, gr_to_filename(new_dentry, mnt), new_mode);
+
+			return new_mode;
+		}
+		return (matchpo->mode & mode);
+	}
+
+      check_parent:
+	curracl = current->acl;
+
+	matchpo = chk_obj_create_label(parent, mnt, curracl, path);
+	retval = matchpo->mode & mode;
+
+	if ((retval != (mode & ~(GR_AUDITS | GR_SUPPRESS)))
+	    && (curracl->mode & GR_LEARN)) {
+		__u32 new_mode = mode;
+
+		new_mode &= ~(GR_AUDITS | GR_SUPPRESS);
+
+		gr_log_learn(current->role, current->uid, current->gid, 
+			     current, gr_to_filename(new_dentry, mnt), new_mode);
+		return new_mode;
+	}
+
+	return retval;
+}
+
+int
+gr_check_hidden_task(const struct task_struct *task)
+{
+	if (unlikely(!(gr_status & GR_READY)))
+		return 0;
+
+	if (!(task->acl->mode & GR_FIND) && !(current->acl->mode & GR_VIEW))
+		return 1;
+
+	return 0;
+}
+
+int
+gr_check_protected_task(const struct task_struct *task)
+{
+	if (unlikely(!(gr_status & GR_READY) || !task))
+		return 0;
+
+	if ((task->acl->mode & GR_PROTECTED) && !(current->acl->mode & GR_KILL))
+		return 1;
+
+	return 0;
+}
+
+__inline__ void
+gr_copy_label(struct task_struct *tsk)
+{
+	tsk->used_accept = 0;
+	tsk->acl_sp_role = 0;
+	tsk->acl_role_id = current->acl_role_id;
+	tsk->acl = current->acl;
+	tsk->role = current->role;
+	tsk->curr_ip = current->curr_ip;
+	if (current->exec_file)
+		get_file(current->exec_file);
+	tsk->exec_file = current->exec_file;
+	tsk->is_writable = current->is_writable;
+	if (unlikely(current->used_accept))
+		current->curr_ip = 0;
+
+	return;
+}
+
+static __inline__ void
+gr_set_proc_res(void)
+{
+	struct acl_subject_label *proc;
+	unsigned short i;
+
+	proc = current->acl;
+
+	if (proc->mode & GR_LEARN)
+		return;
+
+	for (i = 0; i < RLIM_NLIMITS; i++) {
+		if (!(proc->resmask & (1 << i)))
+			continue;
+
+		current->rlim[i].rlim_cur = proc->res[i].rlim_cur;
+		current->rlim[i].rlim_max = proc->res[i].rlim_max;
+	}
+
+	return;
+}
+
+void
+gr_set_pax_flags(struct task_struct *task)
+{
+	struct acl_subject_label *proc;
+
+	if (unlikely(!(gr_status & GR_READY)))
+		return;
+
+	proc = task->acl;
+
+	if (proc->mode & GR_PAXPAGE)
+		task->flags &= ~PF_PAX_PAGEEXEC;
+	if (proc->mode & GR_PAXSEGM)
+		task->flags &= ~PF_PAX_SEGMEXEC;
+	if (proc->mode & GR_PAXGCC)
+		task->flags |= PF_PAX_EMUTRAMP;
+	if (proc->mode & GR_PAXMPROTECT)
+		task->flags &= ~PF_PAX_MPROTECT;
+	if (proc->mode & GR_PAXRANDMMAP)
+		task->flags &= ~PF_PAX_RANDMMAP;
+	if (proc->mode & GR_PAXRANDEXEC)
+		task->flags |= PF_PAX_RANDEXEC;
+
+	return;
+}
+
+static __inline__ void
+do_set_role_label(struct task_struct *task, const uid_t uid, const gid_t gid)
+{
+	task->role = lookup_acl_role_label(task, uid, gid);
+
+	return;
+}
+
+int
+gr_check_user_change(int real, int effective, int fs)
+{
+	unsigned int i;
+	__u16 num;
+	uid_t *uidlist;
+	int curuid;
+	int realok = 0;
+	int effectiveok = 0;
+	int fsok = 0;
+
+	if (unlikely(!(gr_status & GR_READY)))
+		return 0;
+
+	num = current->acl->user_trans_num;
+	uidlist = current->acl->user_transitions;
+
+	if (uidlist == NULL)
+		return 0;
+
+	if (real == -1)
+		realok = 1;
+	if (effective == -1)
+		effectiveok = 1;
+	if (fs == -1)
+		fsok = 1;
+
+	if (current->acl->user_trans_type & GR_ID_ALLOW) {
+		for (i = 0; i < num; i++) {
+			curuid = (int)uidlist[i];
+			if (real == curuid)
+				realok = 1;
+			if (effective == curuid)
+				effectiveok = 1;
+			if (fs == curuid)
+				fsok = 1;
+		}
+	} else if (current->acl->user_trans_type & GR_ID_DENY) {
+		for (i = 0; i < num; i++) {
+			curuid = (int)uidlist[i];
+			if (real == curuid)
+				break;
+			if (effective == curuid)
+				break;
+			if (fs == curuid)
+				break;
+		}
+		/* not in deny list */
+		if (i == num) {
+			realok = 1;
+			effectiveok = 1;
+			fsok = 1;
+		}
+	}
+
+	if (realok && effectiveok && fsok)
+		return 0;
+	else {
+		security_alert(GR_USRCHANGE_ACL_MSG,
+			realok ? (effectiveok ? (fsok ? 0 : fs) : effective) : real, DEFAULTSECARGS);
+		return 1;
+	}
+}
+
+int
+gr_check_group_change(int real, int effective, int fs)
+{
+	unsigned int i;
+	__u16 num;
+	gid_t *gidlist;
+	int curgid;
+	int realok = 0;
+	int effectiveok = 0;
+	int fsok = 0;
+
+	if (unlikely(!(gr_status & GR_READY)))
+		return 0;
+
+	num = current->acl->group_trans_num;
+	gidlist = current->acl->group_transitions;
+
+	if (gidlist == NULL)
+		return 0;
+
+	if (real == -1)
+		realok = 1;
+	if (effective == -1)
+		effectiveok = 1;
+	if (fs == -1)
+		fsok = 1;
+
+	if (current->acl->group_trans_type & GR_ID_ALLOW) {
+		for (i = 0; i < num; i++) {
+			curgid = (int)gidlist[i];
+			if (real == curgid)
+				realok = 1;
+			if (effective == curgid)
+				effectiveok = 1;
+			if (fs == curgid)
+				fsok = 1;
+		}
+	} else if (current->acl->group_trans_type & GR_ID_DENY) {
+		for (i = 0; i < num; i++) {
+			curgid = (int)gidlist[i];
+			if (real == curgid)
+				break;
+			if (effective == curgid)
+				break;
+			if (fs == curgid)
+				break;
+		}
+		/* not in deny list */
+		if (i == num) {
+			realok = 1;
+			effectiveok = 1;
+			fsok = 1;
+		}
+	}
+
+	if (realok && effectiveok && fsok)
+		return 0;
+	else {
+		security_alert(GR_GRPCHANGE_ACL_MSG,
+			realok ? (effectiveok ? (fsok ? 0 : fs) : effective) : real, DEFAULTSECARGS);
+		return 1;
+	}
+}
+
+void
+gr_set_role_label(struct task_struct *task, const uid_t uid, const uid_t gid)
+{
+	struct acl_object_label *obj;
+	struct file *filp;
+
+	if (unlikely(!(gr_status & GR_READY)))
+		return;
+
+	filp = task->exec_file;
+
+	/* kernel process, we'll give them the kernel role */
+	if (unlikely(!filp)) {
+		task->role = kernel_role;
+		task->acl = kernel_role->root_label;
+		return;
+	} else if (!task->role || !(task->role->roletype & GR_ROLE_SPECIAL))
+		do_set_role_label(task, uid, gid);
+
+	task->acl =
+	    chk_subj_label(filp->f_dentry, filp->f_vfsmnt, task->role);
+
+	task->is_writable = 0;
+
+	/* ignore additional mmap checks for processes that are writable 
+	   by the default ACL */
+	obj = chk_obj_label(filp->f_dentry, filp->f_vfsmnt, default_role->root_label);
+	if (unlikely(obj->mode & GR_WRITE))
+		task->is_writable = 1;
+	obj = chk_obj_label(filp->f_dentry, filp->f_vfsmnt, task->role->root_label);
+	if (unlikely(obj->mode & GR_WRITE))
+		task->is_writable = 1;
+
+#ifdef CONFIG_GRKERNSEC_ACL_DEBUG
+	printk(KERN_ALERT "Set role label for (%s:%d): role:%s, subject:%s\n", task->comm, task->pid, task->role->rolename, task->acl->filename);
+#endif
+
+	gr_set_proc_res();
+
+	return;
+}
+
+int
+gr_set_proc_label(const struct dentry *dentry, const struct vfsmount *mnt)
+{
+	struct task_struct *task = current;
+	struct acl_subject_label *newacl;
+	struct acl_object_label *obj;
+	__u32 retmode;
+
+	if (unlikely(!(gr_status & GR_READY)))
+		return 0;
+
+	newacl = chk_subj_label(dentry, mnt, task->role);
+
+	task_lock(task);
+	if (((task->ptrace & PT_PTRACED) && !(task->acl->mode & 
+	     GR_OVERRIDE) && (task->acl != newacl) && 
+	     !(task->role->roletype & GR_ROLE_GOD) && 
+	     !gr_search_file(dentry, GR_PTRACERD, mnt)) ||
+	    (atomic_read(&task->fs->count) > 1 ||
+	     atomic_read(&task->files->count) > 1 ||
+	     atomic_read(&task->sig->count) > 1)) {
+		task_unlock(task);
+		security_alert(GR_PTRACE_EXEC_ACL_MSG,
+			       gr_to_filename(dentry, mnt), DEFAULTSECARGS);
+		return -EACCES;
+	}
+	obj = chk_obj_label(dentry, mnt, task->acl);
+	retmode = obj->mode & (GR_INHERIT | GR_AUDIT_INHERIT);
+
+	if ((newacl->mode & GR_LEARN) || !(retmode & GR_INHERIT)) {
+		if (obj->nested)
+			task->acl = obj->nested;
+		else
+			task->acl = newacl;
+		task_unlock(task);
+	} else if (retmode & GR_INHERIT && retmode & GR_AUDIT_INHERIT) {
+		task_unlock(task);
+		security_audit(GR_INHERIT_ACL_MSG, task->acl->filename,
+			       gr_to_filename(dentry, mnt), DEFAULTSECARGS);
+	} else
+		task_unlock(task);
+
+	task->is_writable = 0;
+
+	/* ignore additional mmap checks for processes that are writable 
+	   by the default ACL */
+	obj = chk_obj_label(dentry, mnt, default_role->root_label);
+	if (unlikely(obj->mode & GR_WRITE))
+		task->is_writable = 1;
+	obj = chk_obj_label(dentry, mnt, task->role->root_label);
+	if (unlikely(obj->mode & GR_WRITE))
+		task->is_writable = 1;
+
+	gr_set_proc_res();
+
+#ifdef CONFIG_GRKERNSEC_ACL_DEBUG
+	printk(KERN_ALERT "Set subject label for (%s:%d): role:%s, subject:%s\n", task->comm, task->pid, task->role->rolename, task->acl->filename);
+#endif
+	return 0;
+}
+
+static __inline__ void
+do_handle_delete(const ino_t ino, const kdev_t dev)
+{
+	struct acl_object_label *matchpo;
+	struct acl_subject_label *matchps;
+	struct acl_subject_label *i;
+	struct acl_role_label *role;
+
+	for (role = role_list_head; role; role = role->next) {
+		for (i = role->hash->first; i; i = i->next) {
+			if (unlikely((i->mode & GR_NESTED) &&
+				     (i->inode == ino) &&
+				     (i->device == dev)))
+				i->mode |= GR_DELETED;
+			if (unlikely((matchpo =
+			     lookup_acl_obj_label(ino, dev, i)) != NULL))
+				matchpo->mode |= GR_DELETED;
+		}
+
+		if (unlikely((matchps = lookup_acl_subj_label(ino, dev, role)) != NULL))
+			matchps->mode |= GR_DELETED;
+	}
+
+	return;
+}
+
+void
+gr_handle_delete(const ino_t ino, const kdev_t dev)
+{
+	if (unlikely(!(gr_status & GR_READY)))
+		return;
+
+	write_lock(&gr_inode_lock);
+	if (unlikely((unsigned long)lookup_inodev_entry(ino, dev)))
+		do_handle_delete(ino, dev);
+	write_unlock(&gr_inode_lock);
+
+	return;
+}
+
+static __inline__ void
+update_acl_obj_label(const ino_t oldinode, const kdev_t olddevice,
+		     const ino_t newinode, const kdev_t newdevice,
+		     struct acl_subject_label *subj)
+{
+	unsigned long index = fhash(oldinode, olddevice, subj->obj_hash_size);
+	struct acl_object_label **match;
+	struct acl_object_label *tmp;
+	__u8 i = 0;
+
+	match = &subj->obj_hash[index];
+
+	while (*match && ((*match)->inode != oldinode ||
+	       (*match)->device != olddevice ||
+	       !((*match)->mode & GR_DELETED))) {
+		index = (index + (1 << i)) % subj->obj_hash_size;
+		match = &subj->obj_hash[index];
+		i = (i + 1) % 32;
+	}
+
+	if (*match && ((*match) != deleted_object)
+	    && ((*match)->inode == oldinode)
+	    && ((*match)->device == olddevice)
+	    && ((*match)->mode & GR_DELETED)) {
+		tmp = *match;
+		tmp->inode = newinode;
+		tmp->device = newdevice;
+		tmp->mode &= ~GR_DELETED;
+
+		*match = deleted_object;
+
+		insert_acl_obj_label(tmp, subj);
+	}
+
+	return;
+}
+
+static __inline__ void
+update_acl_subj_label(const ino_t oldinode, const kdev_t olddevice,
+		      const ino_t newinode, const kdev_t newdevice,
+		      struct acl_role_label *role)
+{
+	struct acl_subject_label **s_hash = role->subj_hash;
+	unsigned long subj_size = role->subj_hash_size;
+	unsigned long index = fhash(oldinode, olddevice, subj_size);
+	struct acl_subject_label **match;
+	struct acl_subject_label *tmp;
+	__u8 i = 0;
+
+	match = &s_hash[index];
+
+	while (*match && ((*match)->inode != oldinode ||
+	       (*match)->device != olddevice ||
+	       !((*match)->mode & GR_DELETED))) {
+		index = (index + (1 << i)) % subj_size;
+		i = (i + 1) % 32;
+		match = &s_hash[index];
+	}
+
+	if (*match && (*match != deleted_subject)
+	    && ((*match)->inode == oldinode)
+	    && ((*match)->device == olddevice)
+	    && ((*match)->mode & GR_DELETED)) {
+		tmp = *match;
+
+		tmp->inode = newinode;
+		tmp->device = newdevice;
+		tmp->mode &= ~GR_DELETED;
+
+		*match = deleted_subject;
+
+		insert_acl_subj_label(tmp, role);
+	}
+
+	return;
+}
+
+static __inline__ void
+update_inodev_entry(const ino_t oldinode, const kdev_t olddevice,
+		    const ino_t newinode, const kdev_t newdevice)
+{
+	unsigned long index = fhash(oldinode, olddevice, inodev_set.n_size);
+	struct name_entry **match;
+	struct name_entry *tmp;
+	__u8 i = 0;
+
+	match = &inodev_set.n_hash[index];
+
+	while (*match
+	       && ((*match)->inode != oldinode
+		   || (*match)->device != olddevice)) {
+		index = (index + (1 << i)) % inodev_set.n_size;
+		i = (i + 1) % 32;
+		match = &inodev_set.n_hash[index];
+	}
+
+	if (*match && (*match != deleted_inodev)
+	    && ((*match)->inode == oldinode)
+	    && ((*match)->device == olddevice)) {
+		tmp = *match;
+
+		tmp->inode = newinode;
+		tmp->device = newdevice;
+
+		*match = deleted_inodev;
+
+		insert_inodev_entry(tmp);
+	}
+
+	return;
+}
+
+static __inline__ void
+do_handle_create(const struct name_entry *matchn, const struct dentry *dentry,
+		 const struct vfsmount *mnt)
+{
+	struct acl_subject_label *i;
+	struct acl_role_label *role;
+
+	for (role = role_list_head; role; role = role->next) {
+		update_acl_subj_label(matchn->inode, matchn->device,
+				      dentry->d_inode->i_ino,
+				      dentry->d_inode->i_dev, role);
+
+		for (i = role->hash->first; i; i = i->next) {
+			if (unlikely((i->mode & GR_NESTED) &&
+				     (i->inode == dentry->d_inode->i_ino) &&
+				     (i->device == dentry->d_inode->i_dev))) {
+				i->inode = dentry->d_inode->i_ino;
+				i->device = dentry->d_inode->i_dev;
+			}
+			update_acl_obj_label(matchn->inode, matchn->device,
+					     dentry->d_inode->i_ino,
+					     dentry->d_inode->i_dev, i);
+		}
+	}
+
+	update_inodev_entry(matchn->inode, matchn->device,
+			    dentry->d_inode->i_ino, dentry->d_inode->i_dev);
+
+	return;
+}
+
+void
+gr_handle_create(const struct dentry *dentry, const struct vfsmount *mnt)
+{
+	struct name_entry *matchn;
+
+	if (unlikely(!(gr_status & GR_READY)))
+		return;
+
+	matchn = lookup_name_entry(gr_to_filename(dentry, mnt));
+
+	if (unlikely((unsigned long)matchn)) {
+		write_lock(&gr_inode_lock);
+		do_handle_create(matchn, dentry, mnt);
+		write_unlock(&gr_inode_lock);
+	}
+
+	return;
+}
+
+int
+gr_handle_rename(struct inode *old_dir, struct inode *new_dir,
+		 struct dentry *old_dentry,
+		 struct dentry *new_dentry,
+		 struct vfsmount *mnt, const __u8 replace)
+{
+	struct name_entry *matchn;
+	int error = 0;
+
+	matchn = lookup_name_entry(gr_to_filename(new_dentry, mnt));
+
+	lock_kernel();
+	error = vfs_rename(old_dir, old_dentry, new_dir, new_dentry);
+	unlock_kernel();
+
+	if (unlikely(error))
+		return error;
+
+	/* we wouldn't have to check d_inode if it weren't for
+	   NFS silly-renaming
+	 */
+
+	write_lock(&gr_inode_lock);
+	if (unlikely(replace && new_dentry->d_inode)) {
+		if (unlikely(lookup_inodev_entry(new_dentry->d_inode->i_ino,
+					new_dentry->d_inode->i_dev) &&
+		    (old_dentry->d_inode->i_nlink <= 1)))
+			do_handle_delete(new_dentry->d_inode->i_ino,
+					 new_dentry->d_inode->i_dev);
+	}
+
+	if (unlikely(lookup_inodev_entry(old_dentry->d_inode->i_ino,
+				old_dentry->d_inode->i_dev) &&
+	    (old_dentry->d_inode->i_nlink <= 1)))
+		do_handle_delete(old_dentry->d_inode->i_ino,
+				 old_dentry->d_inode->i_dev);
+
+	if (unlikely((unsigned long)matchn))
+		do_handle_create(matchn, old_dentry, mnt);
+	write_unlock(&gr_inode_lock);
+
+	return error;
+}
+
+static int
+lookup_special_role_auth(const char *rolename, unsigned char **salt,
+			 unsigned char **sum)
+{
+	struct acl_role_label *r;
+	struct role_allowed_ip *ipp;
+	struct role_transition *trans;
+	__u16 i;
+	int found = 0;
+
+	/* check transition table */
+
+	for (trans = current->role->transitions; trans; trans = trans->next) {
+		if (!strcmp(rolename, trans->rolename)) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found)
+		return 0;
+
+	/* handle special roles that do not require authentication 
+	   and check ip */
+
+	for (r = role_list_head; r; r = r->next) {
+		if (!strcmp(rolename, r->rolename) &&
+		    (r->roletype & GR_ROLE_SPECIAL)) {
+			found = 0;
+			if (r->allowed_ips != NULL) {
+				for (ipp = r->allowed_ips; ipp; ipp = ipp->next) {
+					if ((ntohl(current->curr_ip) & ipp->netmask) ==
+					     (ntohl(ipp->addr) & ipp->netmask))
+						found = 1;
+				}
+			} else
+				found = 2;
+			if (!found)
+				return 0;
+
+			if (r->roletype & GR_ROLE_NOPW) {
+				*salt = NULL;
+				*sum = NULL;
+				return 1;
+			}
+		}
+	}
+
+	for (i = 0; i < num_sprole_pws; i++) {
+		if (!strcmp(rolename, acl_special_roles[i]->rolename)) {
+			*salt = acl_special_roles[i]->salt;
+			*sum = acl_special_roles[i]->sum;
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static void
+assign_special_role(char *rolename)
+{
+	struct acl_object_label *obj;
+	struct acl_role_label *r;
+	struct acl_role_label *assigned = NULL;
+	struct task_struct *tsk;
+	struct file *filp;
+
+	for (r = role_list_head; r; r = r->next)
+		if (!strcmp(rolename, r->rolename) &&
+		    (r->roletype & GR_ROLE_SPECIAL))
+			assigned = r;
+
+	if (!assigned)
+		return;
+
+	read_lock(&tasklist_lock);
+	read_lock(&grsec_exec_file_lock);
+	tsk = current->p_pptr;
+	if (tsk == NULL) {
+		read_unlock(&grsec_exec_file_lock);
+		read_unlock(&tasklist_lock);
+		return;
+	}
+
+	filp = tsk->exec_file;
+	if (filp == NULL) {
+		read_unlock(&grsec_exec_file_lock);
+		read_unlock(&tasklist_lock);
+		return;
+	}
+
+	tsk->is_writable = 0;
+
+	acl_sp_role_value = (acl_sp_role_value % 65535) + 1;
+	tsk->acl_sp_role = 1;
+	tsk->acl_role_id = acl_sp_role_value;
+	tsk->role = assigned;
+	tsk->acl = chk_subj_label(filp->f_dentry, filp->f_vfsmnt, tsk->role);
+
+	/* ignore additional mmap checks for processes that are writable 
+	   by the default ACL */
+	obj = chk_obj_label(filp->f_dentry, filp->f_vfsmnt, default_role->root_label);
+	if (unlikely(obj->mode & GR_WRITE))
+		tsk->is_writable = 1;
+	obj = chk_obj_label(filp->f_dentry, filp->f_vfsmnt, tsk->role->root_label);
+	if (unlikely(obj->mode & GR_WRITE))
+		tsk->is_writable = 1;
+
+#ifdef CONFIG_GRKERNSEC_ACL_DEBUG
+	printk(KERN_ALERT "Assigning special role:%s subject:%s to process (%s:%d)\n", tsk->role->rolename, tsk->acl->filename, tsk->comm, tsk->pid);
+#endif
+
+	read_unlock(&grsec_exec_file_lock);
+	read_unlock(&tasklist_lock);
+	return;
+}
+
+ssize_t
+write_grsec_handler(struct file *file, const char * buf, size_t count, loff_t *ppos)
+{
+	struct gr_arg_wrapper uwrap;
+	unsigned char *sprole_salt;
+	unsigned char *sprole_sum;
+	int error = sizeof (struct gr_arg_wrapper);
+	int error2 = 0;
+
+	down(&gr_dev_sem);
+
+	if (count != sizeof (struct gr_arg_wrapper)) {
+		security_alert_good(GR_DEV_ACL_MSG, (int)count,
+				    (int) sizeof (struct gr_arg_wrapper));
+		error = -EINVAL;
+		goto out;
+	}
+
+	if ((gr_auth_attempts >= CONFIG_GRKERNSEC_ACL_MAXTRIES)
+	    && time_before_eq(gr_auth_expires, jiffies)) {
+		gr_auth_expires = 0;
+		gr_auth_attempts = 0;
+	}
+
+	if (copy_from_user(&uwrap, buf, sizeof (struct gr_arg_wrapper))) {
+		error = -EFAULT;
+		goto out;
+	}
+
+	if ((uwrap.version != GRSECURITY_VERSION) || (uwrap.size != sizeof(struct gr_arg))) {
+		error = -EINVAL;
+		goto out;
+	}
+
+	if (copy_from_user(gr_usermode, uwrap.arg, sizeof (struct gr_arg))) {
+		error = -EFAULT;
+		goto out;
+	}
+
+	if (gr_usermode->mode != SPROLE && time_after(gr_auth_expires, jiffies)) {
+		error = -EBUSY;
+		goto out;
+	}
+
+	/* if non-root trying to do anything other than use a special role,
+	   do not attempt authentication, do not count towards authentication
+	   locking
+	 */
+
+	if (gr_usermode->mode != SPROLE && current->uid) {
+		error = -EPERM;
+		goto out;
+	}
+
+	/* ensure pw and special role name are null terminated */
+
+	gr_usermode->pw[GR_PW_LEN - 1] = '\0';
+	gr_usermode->sp_role[GR_SPROLE_LEN - 1] = '\0';
+
+	/* Okay. 
+	 * We have our enough of the argument structure..(we have yet
+	 * to copy_from_user the tables themselves) . Copy the tables
+	 * only if we need them, i.e. for loading operations. */
+
+	switch (gr_usermode->mode) {
+	case STATUS:
+			if (gr_status & GR_READY)
+				error = 1;
+			else
+				error = 2;
+			goto out;
+	case SHUTDOWN:
+		if ((gr_status & GR_READY)
+		    && !(chkpw(gr_usermode, gr_system_salt, gr_system_sum))) {
+			gr_status &= ~GR_READY;
+			security_alert_good(GR_SHUTS_ACL_MSG, DEFAULTSECARGS);
+			free_variables();
+			memset(gr_usermode, 0, sizeof (struct gr_arg));
+			memset(gr_system_salt, 0, GR_SALT_LEN);
+			memset(gr_system_sum, 0, GR_SHA_LEN);
+		} else if (gr_status & GR_READY) {
+			security_alert(GR_SHUTF_ACL_MSG, DEFAULTSECARGS);
+			error = -EPERM;
+		} else {
+			security_alert_good(GR_SHUTI_ACL_MSG, DEFAULTSECARGS);
+			error = -EAGAIN;
+		}
+		break;
+	case ENABLE:
+		if (!(gr_status & GR_READY) && !(error2 = gracl_init(gr_usermode)))
+			security_alert_good(GR_ENABLE_ACL_MSG, GR_VERSION);
+		else {
+			if (gr_status & GR_READY)
+				error = -EAGAIN;
+			else
+				error = error2;
+			security_alert(GR_ENABLEF_ACL_MSG, GR_VERSION,
+				       DEFAULTSECARGS);
+		}
+		break;
+	case RELOAD:
+		if (!(gr_status & GR_READY)) {
+			security_alert_good(GR_RELOADI_ACL_MSG);
+			error = -EAGAIN;
+		} else if (!(chkpw(gr_usermode, gr_system_salt, gr_system_sum))) {
+			lock_kernel();
+			gr_status &= ~GR_READY;
+			free_variables();
+			if (!(error2 = gracl_init(gr_usermode))) {
+				unlock_kernel();
+				security_alert_good(GR_RELOAD_ACL_MSG,
+						    GR_VERSION);
+			} else {
+				unlock_kernel();
+				error = error2;
+				security_alert(GR_RELOADF_ACL_MSG, GR_VERSION,
+					       DEFAULTSECARGS);
+			}
+		} else {
+			security_alert(GR_RELOADF_ACL_MSG, GR_VERSION,
+				       DEFAULTSECARGS);
+			error = -EPERM;
+		}
+		break;
+	case SEGVMOD:
+		if (unlikely(!(gr_status & GR_READY))) {
+			security_alert_good(GR_SEGVMODI_ACL_MSG,
+					    DEFAULTSECARGS);
+			error = -EAGAIN;
+			break;
+		}
+
+		if (!(chkpw(gr_usermode, gr_system_salt, gr_system_sum))) {
+			security_alert_good(GR_SEGVMODS_ACL_MSG,
+					    DEFAULTSECARGS);
+			if (gr_usermode->segv_device && gr_usermode->segv_inode) {
+				struct acl_subject_label *segvacl;
+				segvacl =
+				    lookup_acl_subj_label(gr_usermode->segv_inode,
+							  gr_usermode->segv_device,
+							  current->role);
+				if (segvacl) {
+					segvacl->crashes = 0;
+					segvacl->expires = 0;
+				}
+			} else if (gr_find_uid(gr_usermode->segv_uid) >= 0) {
+				gr_remove_uid(gr_usermode->segv_uid);
+			}
+		} else {
+			security_alert(GR_SEGVMODF_ACL_MSG, DEFAULTSECARGS);
+			error = -EPERM;
+		}
+		break;
+	case SPROLE:
+		if (unlikely(!(gr_status & GR_READY))) {
+			security_alert_good(GR_SPROLEI_ACL_MSG, DEFAULTSECARGS);
+			error = -EAGAIN;
+			break;
+		}
+
+		if ((current->role->auth_attempts >= CONFIG_GRKERNSEC_ACL_MAXTRIES)
+		    && time_before_eq(current->role->expires, jiffies)) {
+			current->role->expires = 0;
+			current->role->auth_attempts = 0;
+		}
+
+		if (time_after(current->role->expires, jiffies)) {
+			error = -EBUSY;
+			goto out;
+		}
+
+		if (lookup_special_role_auth
+		    (gr_usermode->sp_role, &sprole_salt, &sprole_sum)
+		    && ((!sprole_salt && !sprole_sum)
+			|| !(chkpw(gr_usermode, sprole_salt, sprole_sum)))) {
+			assign_special_role(gr_usermode->sp_role);
+			security_alert_good(GR_SPROLES_ACL_MSG,
+					    (current->p_pptr) ? current->
+					    p_pptr->role->rolename : "",
+					    acl_sp_role_value, DEFAULTSECARGS);
+		} else {
+			security_alert(GR_SPROLEF_ACL_MSG, gr_usermode->sp_role,
+				       DEFAULTSECARGS);
+			error = -EPERM;
+			current->role->auth_attempts++;
+			if (current->role->auth_attempts >= CONFIG_GRKERNSEC_ACL_MAXTRIES) {
+				current->role->expires =
+				    jiffies + CONFIG_GRKERNSEC_ACL_TIMEOUT * HZ;
+				security_alert(GR_MAXROLEPW_ACL_MSG,
+				       CONFIG_GRKERNSEC_ACL_MAXTRIES,
+				       gr_usermode->sp_role, DEFAULTSECARGS);
+			}
+
+			goto out;
+		}
+		break;
+	case UNSPROLE:
+		if (unlikely(!(gr_status & GR_READY))) {
+			security_alert_good(GR_UNSPROLEI_ACL_MSG, DEFAULTSECARGS);
+			error = -EAGAIN;
+			break;
+		}
+
+		if (current->role->roletype & GR_ROLE_SPECIAL) {
+			security_alert_good(GR_UNSPROLES_ACL_MSG,
+					    (current->p_pptr) ? current->
+					    p_pptr->role->rolename : "",
+					    (current->p_pptr) ? current->
+					    p_pptr->acl_role_id : 0, DEFAULTSECARGS);
+			gr_set_acls(1);
+		} else {
+			security_alert(GR_UNSPROLEF_ACL_MSG, current->role->rolename,
+				       DEFAULTSECARGS);
+			error = -EPERM;
+			goto out;
+		}
+		break;
+	default:
+		security_alert(GR_INVMODE_ACL_MSG, gr_usermode->mode,
+			       DEFAULTSECARGS);
+		error = -EINVAL;
+		break;
+	}
+
+	if (error != -EPERM)
+		goto out;
+
+	gr_auth_attempts++;
+
+	if (gr_auth_attempts >= CONFIG_GRKERNSEC_ACL_MAXTRIES) {
+		security_alert(GR_MAXPW_ACL_MSG, CONFIG_GRKERNSEC_ACL_MAXTRIES);
+		gr_auth_expires = jiffies + CONFIG_GRKERNSEC_ACL_TIMEOUT * HZ;
+	}
+
+      out:
+	up(&gr_dev_sem);
+	return error;
+}
+
+int
+gr_set_acls(const int type)
+{
+	struct acl_object_label *obj;
+	struct task_struct *task;
+	struct file *filp;
+	unsigned short i;
+
+	read_lock(&tasklist_lock);
+	read_lock(&grsec_exec_file_lock);
+	for_each_task(task) {
+		/* check to see if we're called from the exit handler,
+		   if so, only replace ACLs that have inherited the admin
+		   ACL */
+
+		if (type && (task->role != current->role ||
+			     task->acl_role_id != current->acl_role_id))
+			continue;
+
+		task->acl_role_id = 0;
+		task->acl_sp_role = 0;
+
+		if ((filp = task->exec_file)) {
+			do_set_role_label(task, task->uid, task->gid);
+
+			task->acl =
+			    chk_subj_label(filp->f_dentry, filp->f_vfsmnt,
+					   task->role);
+			if (task->acl) {
+				struct acl_subject_label *curr;
+				curr = task->acl;
+
+				task->is_writable = 0;
+				/* ignore additional mmap checks for processes that are writable 
+				   by the default ACL */
+				obj = chk_obj_label(filp->f_dentry, filp->f_vfsmnt, default_role->root_label);
+				if (unlikely(obj->mode & GR_WRITE))
+					task->is_writable = 1;
+				obj = chk_obj_label(filp->f_dentry, filp->f_vfsmnt, task->role->root_label);
+				if (unlikely(obj->mode & GR_WRITE))
+					task->is_writable = 1;
+
+#ifdef CONFIG_GRKERNSEC_ACL_DEBUG
+				printk(KERN_ALERT "gr_set_acls for (%s:%d): role:%s, subject:%s\n", task->comm, task->pid, task->role->rolename, task->acl->filename);
+#endif
+				if (!(curr->mode & GR_LEARN))
+					for (i = 0; i < RLIM_NLIMITS; i++) {
+						if (!(curr->resmask & (1 << i)))
+							continue;
+
+						task->rlim[i].rlim_cur =
+						    curr->res[i].rlim_cur;
+						task->rlim[i].rlim_max =
+						    curr->res[i].rlim_max;
+					}
+			} else {
+				read_unlock(&grsec_exec_file_lock);
+				read_unlock(&tasklist_lock);
+				security_alert_good(GR_DEFACL_MSG, task->comm,
+						    task->pid);
+				return 1;
+			}
+		} else {
+			// it's a kernel process
+			task->role = kernel_role;
+			task->acl = kernel_role->root_label;
+#ifdef CONFIG_GRKERNSEC_ACL_HIDEKERN
+			task->acl->mode &= ~GR_FIND;
+#endif
+		}
+	}
+	read_unlock(&grsec_exec_file_lock);
+	read_unlock(&tasklist_lock);
+	return 0;
+}
+
+void
+gr_learn_resource(const struct task_struct *task,
+		  const int res, const unsigned long wanted, const int gt)
+{
+	struct acl_subject_label *acl;
+
+	if (unlikely((gr_status & GR_READY) &&
+		     task->acl && (task->acl->mode & GR_LEARN)))
+		goto skip_reslog;
+
+#ifdef CONFIG_GRKERNSEC_RESLOG
+	gr_log_resource(task, res, wanted, gt);
+#endif
+      skip_reslog:
+
+	if (unlikely(!(gr_status & GR_READY) || !wanted))
+		return;
+
+	acl = task->acl;
+
+	if (likely(!acl || !(acl->mode & GR_LEARN) ||
+		   !(acl->resmask & (1 << (unsigned short) res))))
+		return;
+
+	if (wanted >= acl->res[res].rlim_cur) {
+		unsigned long res_add;
+
+		res_add = wanted;
+		switch (res) {
+		case RLIMIT_CPU:
+			res_add += GR_RLIM_CPU_BUMP;
+			break;
+		case RLIMIT_FSIZE:
+			res_add += GR_RLIM_FSIZE_BUMP;
+			break;
+		case RLIMIT_DATA:
+			res_add += GR_RLIM_DATA_BUMP;
+			break;
+		case RLIMIT_STACK:
+			res_add += GR_RLIM_STACK_BUMP;
+			break;
+		case RLIMIT_CORE:
+			res_add += GR_RLIM_CORE_BUMP;
+			break;
+		case RLIMIT_RSS:
+			res_add += GR_RLIM_RSS_BUMP;
+			break;
+		case RLIMIT_NPROC:
+			res_add += GR_RLIM_NPROC_BUMP;
+			break;
+		case RLIMIT_NOFILE:
+			res_add += GR_RLIM_NOFILE_BUMP;
+			break;
+		case RLIMIT_MEMLOCK:
+			res_add += GR_RLIM_MEMLOCK_BUMP;
+			break;
+		case RLIMIT_AS:
+			res_add += GR_RLIM_AS_BUMP;
+			break;
+		case RLIMIT_LOCKS:
+			res_add += GR_RLIM_LOCKS_BUMP;
+			break;
+		}
+
+		acl->res[res].rlim_cur = res_add;
+
+		if (wanted > acl->res[res].rlim_max)
+			acl->res[res].rlim_max = res_add;
+
+		security_learn(GR_LEARN_AUDIT_MSG, current->role->rolename,
+			       current->role->roletype, acl->filename,
+			       acl->res[res].rlim_cur, acl->res[res].rlim_max,
+			       "", (unsigned long) res);
+	}
+
+	return;
+}
+
+#ifdef CONFIG_SYSCTL
+extern struct proc_dir_entry *proc_sys_root;
+
+__u32
+gr_handle_sysctl(const struct ctl_table *table, const void *oldval,
+		 const void *newval)
+{
+	struct proc_dir_entry *tmp;
+	struct nameidata nd;
+	const char *proc_sys = "/proc/sys";
+	char *path = gr_shared_page[0][smp_processor_id()];
+	struct acl_object_label *obj;
+	unsigned short len = 0, pos = 0, depth = 0, i;
+	__u32 err = 0;
+	__u32 mode = 0;
+
+	if (unlikely(!(gr_status & GR_READY)))
+		return 1;
+
+	if (oldval)
+		mode |= GR_READ;
+	if (newval)
+		mode |= GR_WRITE;
+
+	/* convert the requested sysctl entry into a pathname */
+
+	for (tmp = table->de; tmp != proc_sys_root; tmp = tmp->parent) {
+		len += strlen(tmp->name);
+		len++;
+		depth++;
+	}
+
+	if ((len + depth + strlen(proc_sys) + 1) > PAGE_SIZE)
+		return 0;	// deny
+
+	memset(path, 0, PAGE_SIZE);
+
+	memcpy(path, proc_sys, strlen(proc_sys));
+
+	pos += strlen(proc_sys);
+
+	for (; depth > 0; depth--) {
+		path[pos] = '/';
+		pos++;
+		for (i = 1, tmp = table->de; tmp != proc_sys_root;
+		     tmp = tmp->parent) {
+			if (depth == i) {
+				memcpy(path + pos, tmp->name,
+				       strlen(tmp->name));
+				pos += strlen(tmp->name);
+			}
+			i++;
+		}
+	}
+
+	if (path_init(path, LOOKUP_FOLLOW, &nd))
+		err = path_walk(path, &nd);
+
+	if (err)
+		goto out;
+
+	obj = chk_obj_label(nd.dentry, nd.mnt, current->acl);
+	err = obj->mode & (mode | to_gr_audit(mode) | GR_SUPPRESS);
+
+	if (unlikely((current->acl->mode & GR_LEARN) && ((err & mode) != mode))) {
+		__u32 new_mode = mode;
+
+		new_mode &= ~(GR_AUDITS | GR_SUPPRESS);
+
+		err = new_mode;
+		gr_log_learn(current->role, current->uid, current->gid,
+			     current, path, new_mode);
+	} else if ((err & mode) != mode && !(err & GR_SUPPRESS)) {
+		security_alert(GR_SYSCTL_ACL_MSG, "denied", path,
+			       (mode & GR_READ) ? " reading" : "",
+			       (mode & GR_WRITE) ? " writing" : "",
+			       DEFAULTSECARGS);
+		err = 0;
+	} else if ((err & mode) != mode) {
+		err = 0;
+	} else if (((err & mode) == mode) && (err & GR_AUDITS)) {
+		security_audit(GR_SYSCTL_ACL_MSG, "successful",
+			       path, (mode & GR_READ) ? " reading" : "",
+			       (mode & GR_WRITE) ? " writing" : "",
+			       DEFAULTSECARGS);
+	}
+
+	path_release(&nd);
+
+      out:
+	return err;
+}
+#endif
+
+int
+gr_handle_proc_ptrace(struct task_struct *task)
+{
+	struct file *filp;
+	struct task_struct *tmp = task;
+	struct task_struct *curtemp = current;
+	__u32 retmode;
+
+	if (unlikely(!(gr_status & GR_READY)))
+		return 0;
+
+	read_lock(&tasklist_lock);
+	read_lock(&grsec_exec_file_lock);
+	filp = task->exec_file;
+
+	while (tmp->pid > 0) {
+		if (tmp == curtemp)
+			break;
+		tmp = tmp->p_pptr;
+	}
+
+	if (!filp || (tmp->pid == 0 && !(current->acl->mode & GR_RELAXPTRACE))) {
+		read_unlock(&grsec_exec_file_lock);
+		read_unlock(&tasklist_lock);
+		return 1;
+	}
+
+	retmode = gr_search_file(filp->f_dentry, GR_NOPTRACE, filp->f_vfsmnt);
+	read_unlock(&grsec_exec_file_lock);
+	read_unlock(&tasklist_lock);
+
+	if (retmode & GR_NOPTRACE)
+		return 1;
+
+	if (!(current->acl->mode & GR_OVERRIDE) && !(current->role->roletype & GR_ROLE_GOD)
+	    && (current->acl != task->acl || (current->acl != current->role->root_label
+	    && current->pid != task->pid)))
+		return 1;
+
+	return 0;
+}
+
+int
+gr_handle_ptrace(struct task_struct *task, const long request)
+{
+	struct task_struct *tmp = task;
+	struct task_struct *curtemp = current;
+	__u32 retmode;
+
+	if (unlikely(!(gr_status & GR_READY)))
+		return 0;
+
+	read_lock(&tasklist_lock);
+	while (tmp->pid > 0) {
+		if (tmp == curtemp)
+			break;
+		tmp = tmp->p_pptr;
+	}
+	read_unlock(&tasklist_lock);
+
+	if (tmp->pid == 0 && !(current->acl->mode & GR_RELAXPTRACE)) {
+		security_alert(GR_PTRACE_ACL_MSG, task->exec_file ? 
+			       gr_to_filename(task->exec_file->f_dentry, task->exec_file->f_vfsmnt)
+			       : "(none)", task->comm, task->pid, 
+			       DEFAULTSECARGS);
+		return 1;
+	}
+
+	read_lock(&grsec_exec_file_lock);
+	if (unlikely(!task->exec_file)) {
+		read_unlock(&grsec_exec_file_lock);
+		return 0;
+	}
+
+	retmode = gr_search_file(task->exec_file->f_dentry, GR_PTRACERD | GR_NOPTRACE, task->exec_file->f_vfsmnt);
+	read_unlock(&grsec_exec_file_lock);
+
+	if (retmode & GR_NOPTRACE) {
+		security_alert(GR_PTRACE_ACL_MSG, gr_to_filename(task->exec_file->f_dentry, task->exec_file->f_vfsmnt),
+			       task->comm, task->pid, DEFAULTSECARGS);
+		return 1;
+	}
+		
+	if (retmode & GR_PTRACERD) {
+		switch (request) {
+		case PTRACE_POKETEXT:
+		case PTRACE_POKEDATA:
+		case PTRACE_POKEUSR:
+#if !defined(CONFIG_PPC32) && !defined(CONFIG_PARISC) && !defined(CONFIG_ALPHA)
+		case PTRACE_SETREGS:
+		case PTRACE_SETFPREGS:
+#endif
+#ifdef CONFIG_X86
+		case PTRACE_SETFPXREGS:
+#endif
+#ifdef CONFIG_ALTIVEC
+		case PTRACE_SETVRREGS:
+#endif
+			return 1;
+		default:
+			return 0;
+		}
+	} else if (!(current->acl->mode & GR_OVERRIDE) &&
+		   !(current->role->roletype & GR_ROLE_GOD) &&
+		   (current->acl != task->acl)) {
+		security_alert(GR_PTRACE_ACL_MSG,
+			       gr_to_filename(task->exec_file->f_dentry, task->exec_file->f_vfsmnt),
+					      task->comm, task->pid, DEFAULTSECARGS);
+		return 1;
+	}
+
+	return 0;
+}
+
+int
+gr_handle_mmap(const struct file *filp, const unsigned long prot)
+{
+	struct acl_object_label *obj, *obj2;
+
+	if (unlikely(!(gr_status & GR_READY) ||
+		     (current->acl->mode & GR_OVERRIDE) || !filp ||
+		     !(prot & PROT_EXEC)))
+		return 0;
+
+	if (unlikely(current->is_writable))
+		return 0;
+
+	obj = chk_obj_label(filp->f_dentry, filp->f_vfsmnt, default_role->root_label);
+	obj2 = chk_obj_label(filp->f_dentry, filp->f_vfsmnt,
+			     current->role->root_label);
+	if (unlikely((obj->mode & GR_WRITE) || (obj2->mode & GR_WRITE))) {
+		security_alert(GR_WRITLIB_ACL_MSG,
+			       gr_to_filename(filp->f_dentry, filp->f_vfsmnt),
+			       DEFAULTSECARGS);
+		return 1;
+	}
+
+	return 0;
+}
+
+int
+gr_acl_handle_mmap(const struct file *file, const unsigned long prot)
+{
+	__u32 mode;
+
+	if (unlikely(!file || !(prot & PROT_EXEC)))
+		return 1;
+
+	mode =
+	    gr_search_file(file->f_dentry,
+			   GR_EXEC | GR_AUDIT_EXEC | GR_SUPPRESS,
+			   file->f_vfsmnt);
+
+	if (unlikely(!gr_tpe_allow(file) || (!(mode & GR_EXEC) && !(mode & GR_SUPPRESS)))) {
+		security_alert(GR_MMAP_ACL_MSG, "denied",
+			       gr_to_filename(file->f_dentry, file->f_vfsmnt),
+			       DEFAULTSECARGS);
+		return 0;
+	} else if (unlikely(!gr_tpe_allow(file) || !(mode & GR_EXEC))) {
+		return 0;
+	} else if (unlikely(mode & GR_EXEC && mode & GR_AUDIT_EXEC)) {
+		security_audit(GR_MMAP_ACL_MSG, "successful",
+			       gr_to_filename(file->f_dentry, file->f_vfsmnt),
+			       DEFAULTSECARGS);
+		return 1;
+	}
+
+	return 1;
+}
+
+int
+gr_acl_handle_mprotect(const struct file *file, const unsigned long prot)
+{
+	__u32 mode;
+
+	if (unlikely(!file || !(prot & PROT_EXEC)))
+		return 1;
+
+	mode =
+	    gr_search_file(file->f_dentry,
+			   GR_EXEC | GR_AUDIT_EXEC | GR_SUPPRESS,
+			   file->f_vfsmnt);
+
+	if (unlikely(!gr_tpe_allow(file) || (!(mode & GR_EXEC) && !(mode & GR_SUPPRESS)))) {
+		security_alert(GR_MPROTECT_ACL_MSG, "denied",
+			       gr_to_filename(file->f_dentry, file->f_vfsmnt),
+			       DEFAULTSECARGS);
+		return 0;
+	} else if (unlikely(!gr_tpe_allow(file) || !(mode & GR_EXEC))) {
+		return 0;
+	} else if (unlikely(mode & GR_EXEC && mode & GR_AUDIT_EXEC)) {
+		security_audit(GR_MPROTECT_ACL_MSG, "successful",
+			       gr_to_filename(file->f_dentry, file->f_vfsmnt),
+			       DEFAULTSECARGS);
+		return 1;
+	}
+
+	return 1;
+}
+
+void
+gr_acl_handle_psacct(struct task_struct *task, const long code)
+{
+	unsigned long runtime;
+	unsigned long cputime;
+	unsigned int wday, cday;
+	__u8 whr, chr;
+	__u8 wmin, cmin;
+	__u8 wsec, csec;
+	char cur_tty[64] = { 0 };
+	char parent_tty[64] = { 0 };
+
+	if (unlikely(!(gr_status & GR_READY) || !task->acl ||
+		     !(task->acl->mode & GR_PROCACCT)))
+		return;
+
+	runtime = (jiffies - task->start_time) / HZ;
+	wday = runtime / (3600 * 24);
+	runtime -= wday * (3600 * 24);
+	whr = runtime / 3600;
+	runtime -= whr * 3600;
+	wmin = runtime / 60;
+	runtime -= wmin * 60;
+	wsec = runtime;
+
+	cputime = (task->times.tms_utime + task->times.tms_stime) / HZ;
+	cday = cputime / (3600 * 24);
+	cputime -= cday * (3600 * 24);
+	chr = cputime / 3600;
+	cputime -= chr * 3600;
+	cmin = cputime / 60;
+	cputime -= cmin * 60;
+	csec = cputime;
+
+	security_audit(GR_ACL_PROCACCT_MSG, gr_task_fullpath(task), task->comm,
+		       task->pid, NIPQUAD(task->curr_ip), tty_name(task->tty,
+								   cur_tty),
+		       task->uid, task->euid, task->gid, task->egid, wday, whr,
+		       wmin, wsec, cday, chr, cmin, csec,
+		       (task->
+			flags & PF_SIGNALED) ? "killed by signal" : "exited",
+		       code, gr_parent_task_fullpath(task), 
+		       task->p_pptr->comm, task->p_pptr->pid,
+		       NIPQUAD(task->p_pptr->curr_ip),
+		       tty_name(task->p_pptr->tty, parent_tty),
+		       task->p_pptr->uid, task->p_pptr->euid, task->p_pptr->gid,
+		       task->p_pptr->egid);
+
+	return;
+}
+
+void gr_set_kernel_label(struct task_struct *task)
+{
+	if (gr_status & GR_READY) {
+		task->role = kernel_role;
+		task->acl = kernel_role->root_label;
+	}
+	return;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/gracl_cap.c linux-2.4.27-leo/grsecurity/gracl_cap.c
--- linux-2.4.27/grsecurity/gracl_cap.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/gracl_cap.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,87 @@
+/* capability handling routines, (c) Brad Spengler 2002,2003 */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/capability.h>
+#include <linux/gracl.h>
+#include <linux/grsecurity.h>
+#include <linux/grinternal.h>
+
+static const char *captab_log[29] = {
+	"CAP_CHOWN",
+	"CAP_DAC_OVERRIDE",
+	"CAP_DAC_READ_SEARCH",
+	"CAP_FOWNER",
+	"CAP_FSETID",
+	"CAP_KILL",
+	"CAP_SETGID",
+	"CAP_SETUID",
+	"CAP_SETPCAP",
+	"CAP_LINUX_IMMUTABLE",
+	"CAP_NET_BIND_SERVICE",
+	"CAP_NET_BROADCAST",
+	"CAP_NET_ADMIN",
+	"CAP_NET_RAW",
+	"CAP_IPC_LOCK",
+	"CAP_IPC_OWNER",
+	"CAP_SYS_MODULE",
+	"CAP_SYS_RAWIO",
+	"CAP_SYS_CHROOT",
+	"CAP_SYS_PTRACE",
+	"CAP_SYS_PACCT",
+	"CAP_SYS_ADMIN",
+	"CAP_SYS_BOOT",
+	"CAP_SYS_NICE",
+	"CAP_SYS_RESOURCE",
+	"CAP_SYS_TIME",
+	"CAP_SYS_TTY_CONFIG",
+	"CAP_MKNOD",
+	"CAP_LEASE"
+};
+
+int
+gr_task_is_capable(struct task_struct *task, const int cap)
+{
+	struct acl_subject_label *curracl;
+	__u32 cap_drop = 0, cap_mask = 0;
+
+	if (!gr_acl_is_enabled())
+		return 1;
+
+	curracl = task->acl;
+
+	cap_drop = curracl->cap_lower;
+	cap_mask = curracl->cap_mask;
+
+	while ((curracl = curracl->parent_subject)) {
+		if (!(cap_mask & (1 << cap)) && (curracl->cap_mask & (1 << cap)))
+			cap_drop |= curracl->cap_lower & (1 << cap);
+		cap_mask |= curracl->cap_mask;
+	}
+
+	if (!cap_raised(cap_drop, cap))
+		return 1;
+
+	curracl = task->acl;
+
+	if ((curracl->mode & GR_LEARN)
+	    && cap_raised(task->cap_effective, cap)) {
+		security_learn(GR_LEARN_AUDIT_MSG, task->role->rolename,
+			       task->role->roletype, task->uid,
+			       task->gid, task->exec_file ?
+			       gr_to_filename(task->exec_file->f_dentry,
+			       task->exec_file->f_vfsmnt) : curracl->filename,
+			       curracl->filename, 0UL,
+			       0UL, "", (unsigned long) cap, NIPQUAD(task->curr_ip));
+		return 1;
+	}
+
+	if ((cap >= 0) && (cap < 29) && cap_raised(task->cap_effective, cap))
+		security_alert(GR_CAP_ACL_MSG, captab_log[cap], 
+				gr_task_fullpath(task), task->comm, task->pid, task->uid, task->euid, 
+				task->gid, task->egid, gr_parent_task_fullpath(task), 
+				task->p_pptr->comm, task->p_pptr->pid, task->p_pptr->uid, 
+				task->p_pptr->euid, task->p_pptr->gid, task->p_pptr->egid);
+
+	return 0;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/gracl_fs.c linux-2.4.27-leo/grsecurity/gracl_fs.c
--- linux-2.4.27/grsecurity/gracl_fs.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/gracl_fs.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,477 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/stat.h>
+#include <linux/grsecurity.h>
+#include <linux/grinternal.h>
+#include <linux/gracl.h>
+
+__u32
+gr_acl_handle_hidden_file(const struct dentry * dentry,
+			  const struct vfsmount * mnt)
+{
+	__u32 mode;
+
+	if (unlikely(!dentry->d_inode))
+		return GR_FIND;
+
+	mode =
+	    gr_search_file(dentry, GR_FIND | GR_AUDIT_FIND | GR_SUPPRESS, mnt);
+
+	if (unlikely(mode & GR_FIND && mode & GR_AUDIT_FIND)) {
+		security_audit(GR_HIDDEN_ACL_MSG, "successful",
+			       gr_to_filename(dentry, mnt), DEFAULTSECARGS);
+		return mode;
+	} else if (unlikely(!(mode & GR_FIND) && !(mode & GR_SUPPRESS))) {
+		security_alert(GR_HIDDEN_ACL_MSG, "denied",
+			       gr_to_filename(dentry, mnt),
+			       DEFAULTSECARGS);
+		return 0;
+	} else if (unlikely(!(mode & GR_FIND)))
+		return 0;
+
+	return GR_FIND;
+}
+
+__u32
+gr_acl_handle_open(const struct dentry * dentry, const struct vfsmount * mnt,
+		   const int fmode)
+{
+	__u32 reqmode = GR_FIND;
+	__u32 mode;
+
+	if (unlikely(!dentry->d_inode))
+		return reqmode;
+
+	if (unlikely(fmode & O_APPEND))
+		reqmode |= GR_APPEND;
+	else if (unlikely(fmode & FMODE_WRITE))
+		reqmode |= GR_WRITE;
+	if (likely((fmode & FMODE_READ) && !(fmode & O_DIRECTORY)))
+		reqmode |= GR_READ;
+
+	mode =
+	    gr_search_file(dentry, reqmode | to_gr_audit(reqmode) | GR_SUPPRESS,
+			   mnt);
+
+	if (unlikely(((mode & reqmode) == reqmode) && mode & GR_AUDITS)) {
+		security_audit(GR_OPEN_ACL_MSG, "successful",
+			       gr_to_filename(dentry, mnt),
+			       reqmode & GR_READ ? " reading" : "",
+			       reqmode & GR_WRITE ? " writing" :
+			       reqmode & GR_APPEND ? " appending" : "",
+			       DEFAULTSECARGS);
+		return reqmode;
+	} else
+	    if (unlikely((mode & reqmode) != reqmode && !(mode & GR_SUPPRESS)))
+	{
+		security_alert(GR_OPEN_ACL_MSG, "denied",
+			       gr_to_filename(dentry, mnt),
+			       reqmode & GR_READ ? " reading" : "",
+			       reqmode & GR_WRITE ? " writing" : reqmode &
+			       GR_APPEND ? " appending" : "", DEFAULTSECARGS);
+		return 0;
+	} else if (unlikely((mode & reqmode) != reqmode))
+		return 0;
+
+	return reqmode;
+}
+
+__u32
+gr_acl_handle_creat(const struct dentry * dentry,
+		    const struct dentry * p_dentry,
+		    const struct vfsmount * p_mnt, const int fmode,
+		    const int imode)
+{
+	__u32 reqmode = GR_WRITE | GR_CREATE;
+	__u32 mode;
+
+	if (unlikely(fmode & O_APPEND))
+		reqmode |= GR_APPEND;
+	if (unlikely((fmode & FMODE_READ) && !(fmode & O_DIRECTORY)))
+		reqmode |= GR_READ;
+	if (unlikely((fmode & O_CREAT) && (imode & (S_ISUID | S_ISGID))))
+		reqmode |= GR_SETID;
+
+	mode =
+	    gr_check_create(dentry, p_dentry, p_mnt,
+			    reqmode | to_gr_audit(reqmode) | GR_SUPPRESS);
+
+	if (unlikely(((mode & reqmode) == reqmode) && mode & GR_AUDITS)) {
+		security_audit(GR_CREATE_ACL_MSG, "successful",
+			       gr_to_filename(dentry, p_mnt),
+			       reqmode & GR_READ ? " reading" : "",
+			       reqmode & GR_WRITE ? " writing" :
+			       reqmode & GR_APPEND ? " appending" : "",
+			       DEFAULTSECARGS);
+		return reqmode;
+	} else
+	    if (unlikely((mode & reqmode) != reqmode && !(mode & GR_SUPPRESS)))
+	{
+		security_alert(GR_CREATE_ACL_MSG, "denied",
+			       gr_to_filename(dentry, p_mnt),
+			       reqmode & GR_READ ? " reading" : "",
+			       reqmode & GR_WRITE ? " writing" : reqmode &
+			       GR_APPEND ? " appending" : "", DEFAULTSECARGS);
+		return 0;
+	} else if (unlikely((mode & reqmode) != reqmode))
+		return 0;
+
+	return reqmode;
+}
+
+__u32
+gr_acl_handle_access(const struct dentry * dentry, const struct vfsmount * mnt,
+		     const int fmode)
+{
+	__u32 mode, reqmode = GR_FIND;
+
+	if ((fmode & S_IXOTH) && !S_ISDIR(dentry->d_inode->i_mode))
+		reqmode |= GR_EXEC;
+	if (fmode & S_IWOTH)
+		reqmode |= GR_WRITE;
+	if (fmode & S_IROTH)
+		reqmode |= GR_READ;
+
+	mode =
+	    gr_search_file(dentry, reqmode | to_gr_audit(reqmode) | GR_SUPPRESS,
+			   mnt);
+
+	if (unlikely(((mode & reqmode) == reqmode) && mode & GR_AUDITS)) {
+		security_audit(GR_ACCESS_ACL_MSG, "successful",
+			       gr_to_filename(dentry, mnt),
+			       reqmode & GR_READ ? " reading" : "",
+			       reqmode & GR_WRITE ? " writing" : "",
+			       reqmode & GR_EXEC ? " executing" : "",
+			       DEFAULTSECARGS);
+		return reqmode;
+	} else
+	    if (unlikely((mode & reqmode) != reqmode && !(mode & GR_SUPPRESS)))
+	{
+		security_alert(GR_ACCESS_ACL_MSG, "denied",
+			       gr_to_filename(dentry, mnt),
+			       reqmode & GR_READ ? " reading" : "",
+			       reqmode & GR_WRITE ? " writing" : "",
+			       reqmode & GR_EXEC ? " executing" : "",
+			       DEFAULTSECARGS);
+		return 0;
+	} else if (unlikely((mode & reqmode) != reqmode))
+		return 0;
+
+	return reqmode;
+}
+
+#define generic_fs_handler(dentry, mnt, reqmode, fmt) \
+{ \
+	__u32 mode; \
+	\
+	mode = gr_search_file(dentry, reqmode | to_gr_audit(reqmode) | GR_SUPPRESS, mnt); \
+	\
+	if (unlikely(((mode & (reqmode)) == (reqmode)) && mode & GR_AUDITS)) { \
+		security_audit(fmt, "successful", \
+				gr_to_filename(dentry, mnt), DEFAULTSECARGS); \
+		return mode; \
+	} else if (unlikely((mode & (reqmode)) != (reqmode) && !(mode & GR_SUPPRESS))) { \
+		security_alert(fmt, "denied", gr_to_filename(dentry, mnt), \
+				DEFAULTSECARGS); \
+		return 0; \
+	} else if (unlikely((mode & (reqmode)) != (reqmode))) \
+		return 0; \
+	\
+	return (reqmode); \
+}
+
+__u32
+gr_acl_handle_rmdir(const struct dentry * dentry, const struct vfsmount * mnt)
+{
+	generic_fs_handler(dentry, mnt, GR_WRITE | GR_DELETE , GR_RMDIR_ACL_MSG);
+}
+
+__u32
+gr_acl_handle_unlink(const struct dentry *dentry, const struct vfsmount *mnt)
+{
+	generic_fs_handler(dentry, mnt, GR_WRITE | GR_DELETE , GR_UNLINK_ACL_MSG);
+}
+
+__u32
+gr_acl_handle_truncate(const struct dentry *dentry, const struct vfsmount *mnt)
+{
+	generic_fs_handler(dentry, mnt, GR_WRITE, GR_TRUNCATE_ACL_MSG);
+}
+
+__u32
+gr_acl_handle_utime(const struct dentry *dentry, const struct vfsmount *mnt)
+{
+	generic_fs_handler(dentry, mnt, GR_WRITE, GR_ATIME_ACL_MSG);
+}
+
+__u32
+gr_acl_handle_fchmod(const struct dentry *dentry, const struct vfsmount *mnt,
+		     mode_t mode)
+{
+	if (unlikely(dentry->d_inode && S_ISSOCK(dentry->d_inode->i_mode)))
+		return 1;
+
+	if (unlikely((mode != (mode_t)-1) && (mode & (S_ISUID | S_ISGID)))) {
+		generic_fs_handler(dentry, mnt, GR_WRITE | GR_SETID,
+				   GR_FCHMOD_ACL_MSG);
+	} else {
+		generic_fs_handler(dentry, mnt, GR_WRITE, GR_FCHMOD_ACL_MSG);
+	}
+}
+
+__u32
+gr_acl_handle_chmod(const struct dentry *dentry, const struct vfsmount *mnt,
+		    mode_t mode)
+{
+	if (unlikely((mode != (mode_t)-1) && (mode & (S_ISUID | S_ISGID)))) {
+		generic_fs_handler(dentry, mnt, GR_WRITE | GR_SETID,
+				   GR_CHMOD_ACL_MSG);
+	} else {
+		generic_fs_handler(dentry, mnt, GR_WRITE, GR_CHMOD_ACL_MSG);
+	}
+}
+
+__u32
+gr_acl_handle_chown(const struct dentry *dentry, const struct vfsmount *mnt)
+{
+	generic_fs_handler(dentry, mnt, GR_WRITE, GR_CHOWN_ACL_MSG);
+}
+
+__u32
+gr_acl_handle_execve(const struct dentry *dentry, const struct vfsmount *mnt)
+{
+	generic_fs_handler(dentry, mnt, GR_EXEC, GR_EXEC_ACL_MSG);
+}
+
+__u32
+gr_acl_handle_unix(const struct dentry *dentry, const struct vfsmount *mnt)
+{
+	generic_fs_handler(dentry, mnt, GR_READ | GR_WRITE,
+			   GR_UNIXCONNECT_ACL_MSG);
+}
+
+__u32
+gr_acl_handle_filldir(const struct dentry *dentry, const struct vfsmount *mnt,
+		      const ino_t ino)
+{
+	if (likely((unsigned long)(dentry->d_inode))) {
+		struct dentry d = *dentry;
+		struct inode inode = *(dentry->d_inode);
+
+		inode.i_ino = ino;
+		d.d_inode = &inode;
+
+		if (unlikely(!gr_search_file(&d, GR_FIND | GR_NOLEARN, mnt)))
+			return 0;
+	}
+
+	return 1;
+}
+
+__u32
+gr_acl_handle_link(const struct dentry * new_dentry,
+		   const struct dentry * parent_dentry,
+		   const struct vfsmount * parent_mnt,
+		   const struct dentry * old_dentry,
+		   const struct vfsmount * old_mnt, const char *to)
+{
+	__u32 needmode = GR_WRITE | GR_CREATE;
+	__u32 mode;
+
+	mode =
+	    gr_check_link(new_dentry, parent_dentry, parent_mnt, old_dentry,
+			  old_mnt);
+
+	if (unlikely(((mode & needmode) == needmode) && mode & GR_AUDITS)) {
+		security_audit(GR_LINK_ACL_MSG, "successful",
+			       gr_to_filename(old_dentry, old_mnt), to,
+			       DEFAULTSECARGS);
+		return mode;
+	} else if (unlikely(((mode & needmode) != needmode) && !(mode & GR_SUPPRESS))) {
+		security_alert(GR_LINK_ACL_MSG, "denied",
+			       gr_to_filename(old_dentry, old_mnt), to,
+			       DEFAULTSECARGS);
+		return 0;
+	} else if (unlikely((mode & needmode) != needmode))
+		return 0;
+
+	return (GR_WRITE | GR_CREATE);
+}
+
+__u32
+gr_acl_handle_symlink(const struct dentry * new_dentry,
+		      const struct dentry * parent_dentry,
+		      const struct vfsmount * parent_mnt, const char *from)
+{
+	__u32 needmode = GR_WRITE | GR_CREATE;
+	__u32 mode;
+
+	mode =
+	    gr_check_create(new_dentry, parent_dentry, parent_mnt,
+			    GR_CREATE | GR_AUDIT_CREATE |
+			    GR_WRITE | GR_AUDIT_WRITE | GR_SUPPRESS);
+
+	if (unlikely(mode & GR_WRITE && mode & GR_AUDITS)) {
+		security_audit(GR_SYMLINK_ACL_MSG, "successful",
+			       from, gr_to_filename(new_dentry, parent_mnt),
+			       DEFAULTSECARGS);
+		return mode;
+	} else if (unlikely(((mode & needmode) != needmode) && !(mode & GR_SUPPRESS))) {
+		security_alert(GR_SYMLINK_ACL_MSG, "denied",
+			       from, gr_to_filename(new_dentry, parent_mnt),
+			       DEFAULTSECARGS);
+		return 0;
+	} else if (unlikely((mode & needmode) != needmode))
+		return 0;
+
+	return (GR_WRITE | GR_CREATE);
+}
+
+#define generic_fs_create_handler(new_dentry, parent_dentry, parent_mnt, reqmode, fmt) \
+{ \
+	__u32 mode; \
+	\
+	mode = gr_check_create(new_dentry, parent_dentry, parent_mnt, reqmode | to_gr_audit(reqmode) | GR_SUPPRESS); \
+	\
+	if (unlikely(((mode & (reqmode)) == (reqmode)) && mode & GR_AUDITS)) { \
+		security_audit(fmt, "successful", \
+				gr_to_filename(new_dentry, parent_mnt), \
+				DEFAULTSECARGS); \
+		return mode; \
+	} else if (unlikely((mode & (reqmode)) != (reqmode) && !(mode & GR_SUPPRESS))) { \
+		security_alert(fmt, "denied", \
+				gr_to_filename(new_dentry, parent_mnt), \
+				DEFAULTSECARGS); \
+		return 0; \
+	} else if (unlikely((mode & (reqmode)) != (reqmode))) \
+		return 0; \
+	\
+	return (reqmode); \
+}
+
+__u32
+gr_acl_handle_mknod(const struct dentry * new_dentry,
+		    const struct dentry * parent_dentry,
+		    const struct vfsmount * parent_mnt,
+		    const int mode)
+{
+	__u32 reqmode = GR_WRITE | GR_CREATE;
+	if (unlikely(mode & (S_ISUID | S_ISGID)))
+		reqmode |= GR_SETID;
+
+	generic_fs_create_handler(new_dentry, parent_dentry, parent_mnt,
+				  reqmode, GR_MKNOD_ACL_MSG);
+}
+
+__u32
+gr_acl_handle_mkdir(const struct dentry *new_dentry,
+		    const struct dentry *parent_dentry,
+		    const struct vfsmount *parent_mnt)
+{
+	generic_fs_create_handler(new_dentry, parent_dentry, parent_mnt,
+				  GR_WRITE | GR_CREATE, GR_MKDIR_ACL_MSG);
+}
+
+#define RENAME_CHECK_SUCCESS(old, new) \
+	(((old & (GR_WRITE | GR_READ)) == (GR_WRITE | GR_READ)) && \
+	 ((new & (GR_WRITE | GR_READ)) == (GR_WRITE | GR_READ)))
+
+int
+gr_acl_handle_rename(struct dentry *new_dentry,
+		     struct dentry *parent_dentry,
+		     const struct vfsmount *parent_mnt,
+		     struct dentry *old_dentry,
+		     struct inode *old_parent_inode,
+		     struct vfsmount *old_mnt, const char *newname)
+{
+	__u8 gr_replace = 1;
+	__u32 comp1, comp2;
+	int error = 0;
+
+	if (unlikely(!gr_acl_is_enabled()))
+		return 1;
+
+	if (!new_dentry->d_inode) {
+		gr_replace = 0;
+
+		comp1 = gr_check_create(new_dentry, parent_dentry, parent_mnt,
+					GR_READ | GR_WRITE | GR_CREATE | GR_AUDIT_READ |
+					GR_AUDIT_WRITE | GR_AUDIT_CREATE | GR_SUPPRESS);
+		comp2 = gr_search_file(old_dentry, GR_READ | GR_WRITE |
+				       GR_DELETE | GR_AUDIT_DELETE |
+				       GR_AUDIT_READ | GR_AUDIT_WRITE |
+				       GR_SUPPRESS, old_mnt);
+	} else {
+		comp1 = gr_search_file(new_dentry, GR_READ | GR_WRITE |
+				       GR_CREATE | GR_DELETE |
+				       GR_AUDIT_CREATE | GR_AUDIT_DELETE |
+				       GR_AUDIT_READ | GR_AUDIT_WRITE |
+				       GR_SUPPRESS, parent_mnt);
+		comp2 =
+		    gr_search_file(old_dentry,
+				   GR_READ | GR_WRITE | GR_AUDIT_READ |
+				   GR_DELETE | GR_AUDIT_DELETE |
+				   GR_AUDIT_WRITE | GR_SUPPRESS, old_mnt);
+	}
+
+	if (RENAME_CHECK_SUCCESS(comp1, comp2) &&
+	    ((comp1 & GR_AUDITS) || (comp2 & GR_AUDITS)))
+		security_audit(GR_RENAME_ACL_MSG, "successful",
+			       gr_to_filename(old_dentry, old_mnt),
+			       newname, DEFAULTSECARGS);
+	else if (!RENAME_CHECK_SUCCESS(comp1, comp2) && !(comp1 & GR_SUPPRESS)
+		 && !(comp2 & GR_SUPPRESS)) {
+		security_alert(GR_RENAME_ACL_MSG, "denied",
+			       gr_to_filename(old_dentry, old_mnt), newname,
+			       DEFAULTSECARGS);
+		error = -EACCES;
+	} else if (unlikely(!RENAME_CHECK_SUCCESS(comp1, comp2)))
+		error = -EACCES;
+
+	if (error)
+		return error;
+
+	error = gr_handle_rename(old_parent_inode, parent_dentry->d_inode,
+				 old_dentry, new_dentry, old_mnt, gr_replace);
+
+	return error;
+}
+
+void
+gr_acl_handle_exit(void)
+{
+	u16 id;
+	char *rolename;
+	struct file *exec_file;
+
+	if (unlikely(current->acl_sp_role && gr_acl_is_enabled())) {
+		id = current->acl_role_id;
+		rolename = current->role->rolename;
+		gr_set_acls(1);
+		security_alert_good(GR_SPROLEL_ACL_MSG,
+				    rolename, id, DEFAULTSECARGS);
+	}
+
+	write_lock(&grsec_exec_file_lock);
+	exec_file = current->exec_file;
+	current->exec_file = NULL;
+	write_unlock(&grsec_exec_file_lock);
+
+	if (exec_file)
+		fput(exec_file);
+}
+
+int
+gr_acl_handle_procpidmem(const struct task_struct *task)
+{
+	if (unlikely(!gr_acl_is_enabled()))
+		return 0;
+
+	if (task->acl->mode & GR_PROTPROCFD)
+		return -EACCES;
+
+	return 0;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/gracl_ip.c linux-2.4.27-leo/grsecurity/gracl_ip.c
--- linux-2.4.27/grsecurity/gracl_ip.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/gracl_ip.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,235 @@
+/* 
+ * grsecurity/gracl_ip.c
+ * Copyright Brad Spengler 2002, 2003
+ *
+ */
+
+#include <linux/kernel.h>
+#include <asm/uaccess.h>
+#include <asm/errno.h>
+#include <net/sock.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <linux/smp_lock.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/gracl.h>
+#include <linux/grsecurity.h>
+#include <linux/grinternal.h>
+
+#define GR_BIND 	0x01
+#define GR_CONNECT 	0x02
+
+static const char * gr_protocols[256] = {
+	"ip", "icmp", "igmp", "ggp", "ipencap", "st", "tcp", "cbt",
+	"egp", "igp", "bbn-rcc", "nvp", "pup", "argus", "emcon", "xnet",
+	"chaos", "udp", "mux", "dcn", "hmp", "prm", "xns-idp", "trunk-1",
+	"trunk-2", "leaf-1", "leaf-2", "rdp", "irtp", "iso-tp4", "netblt", "mfe-nsp",
+	"merit-inp", "sep", "3pc", "idpr", "xtp", "ddp", "idpr-cmtp", "tp++",
+	"il", "ipv6", "sdrp", "ipv6-route", "ipv6-frag", "idrp", "rsvp", "gre",
+	"mhrp", "bna", "ipv6-crypt", "ipv6-auth", "i-nlsp", "swipe", "narp", "mobile",
+	"tlsp", "skip", "ipv6-icmp", "ipv6-nonxt", "ipv6-opts", "unknown:61", "cftp", "unknown:63",
+	"sat-expak", "kryptolan", "rvd", "ippc", "unknown:68", "sat-mon", "visa", "ipcv",
+	"cpnx", "cphb", "wsn", "pvp", "br-sat-mon", "sun-nd", "wb-mon", "wb-expak", 
+	"iso-ip", "vmtp", "secure-vmtp", "vines", "ttp", "nfsnet-igp", "dgp", "tcf", 
+	"eigrp", "ospf", "sprite-rpc", "larp", "mtp", "ax.25", "ipip", "micp",
+	"scc-sp", "etherip", "encap", "unknown:99", "gmtp", "ifmp", "pnni", "pim",
+	"aris", "scps", "qnx", "a/n", "ipcomp", "snp", "compaq-peer", "ipx-in-ip",
+	"vrrp", "pgm", "unknown:114", "l2tp", "ddx", "iatp", "stp", "srp",
+	"uti", "smp", "sm", "ptp", "isis", "fire", "crtp", "crdup",
+	"sscopmce", "iplt", "sps", "pipe", "sctp", "fc", "unkown:134", "unknown:135",
+	"unknown:136", "unknown:137", "unknown:138", "unknown:139", "unknown:140", "unknown:141", "unknown:142", "unknown:143",
+	"unknown:144", "unknown:145", "unknown:146", "unknown:147", "unknown:148", "unknown:149", "unknown:150", "unknown:151",
+	"unknown:152", "unknown:153", "unknown:154", "unknown:155", "unknown:156", "unknown:157", "unknown:158", "unknown:159",
+	"unknown:160", "unknown:161", "unknown:162", "unknown:163", "unknown:164", "unknown:165", "unknown:166", "unknown:167",
+	"unknown:168", "unknown:169", "unknown:170", "unknown:171", "unknown:172", "unknown:173", "unknown:174", "unknown:175",
+	"unknown:176", "unknown:177", "unknown:178", "unknown:179", "unknown:180", "unknown:181", "unknown:182", "unknown:183",
+	"unknown:184", "unknown:185", "unknown:186", "unknown:187", "unknown:188", "unknown:189", "unknown:190", "unknown:191",
+	"unknown:192", "unknown:193", "unknown:194", "unknown:195", "unknown:196", "unknown:197", "unknown:198", "unknown:199",
+	"unknown:200", "unknown:201", "unknown:202", "unknown:203", "unknown:204", "unknown:205", "unknown:206", "unknown:207",
+	"unknown:208", "unknown:209", "unknown:210", "unknown:211", "unknown:212", "unknown:213", "unknown:214", "unknown:215",
+	"unknown:216", "unknown:217", "unknown:218", "unknown:219", "unknown:220", "unknown:221", "unknown:222", "unknown:223",
+	"unknown:224", "unknown:225", "unknown:226", "unknown:227", "unknown:228", "unknown:229", "unknown:230", "unknown:231",
+	"unknown:232", "unknown:233", "unknown:234", "unknown:235", "unknown:236", "unknown:237", "unknown:238", "unknown:239",
+	"unknown:240", "unknown:241", "unknown:242", "unknown:243", "unknown:244", "unknown:245", "unknown:246", "unknown:247",
+	"unknown:248", "unknown:249", "unknown:250", "unknown:251", "unknown:252", "unknown:253", "unknown:254", "unknown:255",
+	};
+
+static const char * gr_socktypes[11] = {
+	"unknown:0", "stream", "dgram", "raw", "rdm", "seqpacket", "unknown:6", 
+	"unknown:7", "unknown:8", "unknown:9", "packet"
+	};
+
+__inline__ const char *
+gr_proto_to_name(unsigned char proto)
+{
+	return gr_protocols[proto];
+}
+
+__inline__ const char *
+gr_socktype_to_name(unsigned char type)
+{
+	return gr_socktypes[type];
+}
+
+int
+gr_search_socket(const int domain, const int type, const int protocol)
+{
+	struct acl_subject_label *curr;
+
+	if (unlikely(!gr_acl_is_enabled()))
+		goto exit;
+
+	if ((domain < 0) || (type < 0) || (protocol < 0) || (domain != PF_INET)
+	    || (domain >= NPROTO) || (type >= SOCK_MAX) || (protocol > 255))
+		goto exit;	// let the kernel handle it
+
+	curr = current->acl;
+
+	if (!curr->ips)
+		goto exit;
+
+	if ((curr->ip_type & (1 << type)) &&
+	    (curr->ip_proto[protocol / 32] & (1 << (protocol % 32))))
+		goto exit;
+
+	if (curr->mode & GR_LEARN) {
+		/* we don't place acls on raw sockets , and sometimes
+		   dgram/ip sockets are opened for ioctl and not
+		   bind/connect, so we'll fake a bind learn log */
+		if (type == SOCK_RAW || type == SOCK_PACKET) {
+			__u32 fakeip = 0;
+			security_learn(GR_IP_LEARN_MSG, current->role->rolename,
+				       current->role->roletype, current->uid,
+				       current->gid, current->exec_file ?
+				       gr_to_filename(current->exec_file->f_dentry,
+				       current->exec_file->f_vfsmnt) :
+				       curr->filename, curr->filename,
+				       NIPQUAD(fakeip), 0, type,
+				       protocol, GR_CONNECT, NIPQUAD(current->curr_ip));
+		} else if ((type == SOCK_DGRAM) && (protocol == IPPROTO_IP)) {
+			__u32 fakeip = 0;
+			security_learn(GR_IP_LEARN_MSG, current->role->rolename,
+				       current->role->roletype, current->uid,
+				       current->gid, current->exec_file ?
+				       gr_to_filename(current->exec_file->f_dentry,
+				       current->exec_file->f_vfsmnt) :
+				       curr->filename, curr->filename,
+				       NIPQUAD(fakeip), 0, type,
+				       protocol, GR_BIND, NIPQUAD(current->curr_ip));
+		}
+		/* we'll log when they use connect or bind */
+		goto exit;
+	}
+
+	security_alert(GR_SOCK_MSG, "inet", gr_socktype_to_name(type),
+		       gr_proto_to_name(protocol), DEFAULTSECARGS);
+
+	return 0;
+      exit:
+	return 1;
+}
+
+static __inline__ int
+gr_search_connectbind(const int mode, const struct sock *sk,
+		      const struct sockaddr_in *addr, const int type)
+{
+	struct acl_subject_label *curr;
+	struct acl_ip_label *ip;
+	unsigned long i;
+	__u32 ip_addr = 0;
+	__u16 ip_port = 0;
+
+	if (unlikely(!gr_acl_is_enabled() || sk->family != PF_INET))
+		return 1;
+
+	curr = current->acl;
+
+	if (!curr->ips)
+		return 1;
+
+	ip_addr = addr->sin_addr.s_addr;
+	ip_port = ntohs(addr->sin_port);
+
+	for (i = 0; i < curr->ip_num; i++) {
+		ip = *(curr->ips + i);
+		if ((ip->mode & mode) &&
+		    (ip_port >= ip->low) &&
+		    (ip_port <= ip->high) &&
+		    ((ntohl(ip_addr) & ip->netmask) ==
+		     (ntohl(ip->addr) & ip->netmask))
+		    && (ip->
+			proto[sk->protocol / 32] & (1 << (sk->protocol % 32)))
+		    && (ip->type & (1 << type)))
+			return 1;
+	}
+
+	if (curr->mode & GR_LEARN) {
+		security_learn(GR_IP_LEARN_MSG, current->role->rolename,
+			       current->role->roletype, current->uid,
+			       current->gid, current->exec_file ?
+			       gr_to_filename(current->exec_file->f_dentry,
+			       current->exec_file->f_vfsmnt) :
+			       curr->filename, curr->filename,
+			       NIPQUAD(ip_addr), ip_port, type,
+			       sk->protocol, mode, NIPQUAD(current->curr_ip));
+		return 1;
+	}
+
+	if (mode == GR_BIND)
+		security_alert(GR_BIND_ACL_MSG, NIPQUAD(ip_addr), ip_port,
+			       gr_socktype_to_name(type), gr_proto_to_name(sk->protocol),
+			       DEFAULTSECARGS);
+	else if (mode == GR_CONNECT)
+		security_alert(GR_CONNECT_ACL_MSG, NIPQUAD(ip_addr), ip_port,
+			       gr_socktype_to_name(type), gr_proto_to_name(sk->protocol),
+			       DEFAULTSECARGS);
+
+	return 0;
+}
+
+int
+gr_search_connect(const struct socket *sock, const struct sockaddr_in *addr)
+{
+	return gr_search_connectbind(GR_CONNECT, sock->sk, addr, sock->type);
+}
+
+int
+gr_search_bind(const struct socket *sock, const struct sockaddr_in *addr)
+{
+	return gr_search_connectbind(GR_BIND, sock->sk, addr, sock->type);
+}
+
+int
+gr_search_udp_sendmsg(const struct sock *sk, const struct sockaddr_in *addr)
+{
+	if (addr)
+		return gr_search_connectbind(GR_CONNECT, sk, addr, SOCK_DGRAM);
+	else {
+		struct sockaddr_in sin;
+
+		sin.sin_addr.s_addr = sk->daddr;
+		sin.sin_port = sk->dport;
+
+		return gr_search_connectbind(GR_CONNECT, sk, &sin, SOCK_DGRAM);
+	}
+}
+
+int
+gr_search_udp_recvmsg(const struct sock *sk, const struct sk_buff *skb)
+{
+	struct sockaddr_in sin;
+
+	if (unlikely(skb->len < sizeof (struct udphdr)))
+		return 1;	// skip this packet
+
+	sin.sin_addr.s_addr = skb->nh.iph->saddr;
+	sin.sin_port = skb->h.uh->source;
+
+	return gr_search_connectbind(GR_CONNECT, sk, &sin, SOCK_DGRAM);
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/gracl_learn.c linux-2.4.27-leo/grsecurity/gracl_learn.c
--- linux-2.4.27/grsecurity/gracl_learn.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/gracl_learn.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,170 @@
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/smp_lock.h>
+#include <linux/string.h>
+#include <linux/file.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+#include <linux/grinternal.h>
+
+extern ssize_t write_grsec_handler(struct file * file, const char * buf,
+				   size_t count, loff_t *ppos);
+extern int gr_acl_is_enabled(void);
+
+static DECLARE_WAIT_QUEUE_HEAD(learn_wait);
+static int gr_learn_attached;
+
+/* use a 1MB buffer */
+#define LEARN_BUFFER_SIZE (1024 * 1024)
+
+static spinlock_t gr_learn_lock = SPIN_LOCK_UNLOCKED;
+static char *learn_buffer;
+static int learn_buffer_len;
+
+static ssize_t
+read_learn(struct file *file, char * buf, size_t count, loff_t * ppos)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t retval = 0;
+
+	add_wait_queue(&learn_wait, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+	do {
+		spin_lock(&gr_learn_lock);
+		if (learn_buffer_len)
+			break;
+		spin_unlock(&gr_learn_lock);
+
+		if (file->f_flags & O_NONBLOCK) {
+			retval = -EAGAIN;
+			goto out;
+		}
+		if (signal_pending(current)) {
+			retval = -ERESTARTSYS;
+			goto out;
+		}
+
+		schedule();
+	} while (1);
+
+	if (copy_to_user(buf, learn_buffer, learn_buffer_len)) {
+		retval = -EFAULT;
+		spin_unlock(&gr_learn_lock);
+		goto out;
+	}
+
+	retval = learn_buffer_len;
+	learn_buffer_len = 0;
+
+	spin_unlock(&gr_learn_lock);
+out:
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&learn_wait, &wait);
+	return retval;
+}
+
+static unsigned int
+poll_learn(struct file * file, poll_table * wait)
+{
+	poll_wait(file, &learn_wait, wait);
+
+	if (learn_buffer_len)
+		return (POLLIN | POLLRDNORM);
+
+	return 0;
+}
+
+void
+gr_clear_learn_entries(void)
+{
+	spin_lock(&gr_learn_lock);
+	learn_buffer_len = 0;
+	if (learn_buffer != NULL) {
+		vfree(learn_buffer);
+		learn_buffer = NULL;
+	}
+	spin_unlock(&gr_learn_lock);
+
+	return;
+}
+
+void
+gr_add_learn_entry(const char *fmt, ...)
+{
+	va_list args;
+	unsigned int len;
+
+	if (!gr_learn_attached)
+		return;
+
+	spin_lock(&gr_learn_lock);
+
+	/* leave a gap at the end so we know when it's "full" but don't have to
+	   compute the exact length of the string we're trying to append
+	*/
+	if (learn_buffer_len > LEARN_BUFFER_SIZE - 16384) {
+		spin_unlock(&gr_learn_lock);
+		wake_up_interruptible(&learn_wait);
+		return;
+	}
+	if (learn_buffer == NULL) {
+		spin_unlock(&gr_learn_lock);
+		return;
+	}
+
+	va_start(args, fmt);
+	len = vsnprintf(learn_buffer + learn_buffer_len, LEARN_BUFFER_SIZE - learn_buffer_len, fmt, args);
+	va_end(args);
+
+	learn_buffer_len += len + 1;
+
+	spin_unlock(&gr_learn_lock);
+	wake_up_interruptible(&learn_wait);
+
+	return;
+}
+
+static int
+open_learn(struct inode *inode, struct file *file)
+{
+	if (file->f_mode & FMODE_READ && gr_learn_attached)
+		return -EBUSY;
+	if (file->f_mode & FMODE_READ) {
+		spin_lock(&gr_learn_lock);
+		if (learn_buffer == NULL)
+			learn_buffer = vmalloc(LEARN_BUFFER_SIZE);
+		if (learn_buffer == NULL)
+			return -ENOMEM;
+		learn_buffer_len = 0;
+		gr_learn_attached = 1;
+		spin_unlock(&gr_learn_lock);
+	}
+	return 0;
+}
+
+static int
+close_learn(struct inode *inode, struct file *file)
+{
+	if (file->f_mode & FMODE_READ) {
+		spin_lock(&gr_learn_lock);
+		if (learn_buffer != NULL) {
+			vfree(learn_buffer);
+			learn_buffer = NULL;
+		}
+		learn_buffer_len = 0;
+		gr_learn_attached = 0;
+		spin_unlock(&gr_learn_lock);
+	}
+
+	return 0;
+}
+		
+struct file_operations grsec_fops = {
+	read:		read_learn,
+	write:		write_grsec_handler,
+	open:		open_learn,
+	release:	close_learn,
+	poll:		poll_learn,
+};
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/gracl_res.c linux-2.4.27-leo/grsecurity/gracl_res.c
--- linux-2.4.27/grsecurity/gracl_res.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/gracl_res.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,46 @@
+/* resource handling routines (c) Brad Spengler 2002, 2003 */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/gracl.h>
+#include <linux/grinternal.h>
+
+static const char *restab_log[11] = {
+	"RLIMIT_CPU",
+	"RLIMIT_FSIZE",
+	"RLIMIT_DATA",
+	"RLIMIT_STACK",
+	"RLIMIT_CORE",
+	"RLIMIT_RSS",
+	"RLIMIT_NPROC",
+	"RLIMIT_NOFILE",
+	"RLIMIT_MEMLOCK",
+	"RLIMIT_AS",
+	"RLIMIT_LOCKS"
+};
+
+__inline__ void
+gr_log_resource(const struct task_struct *task,
+		const int res, const unsigned long wanted, const int gt)
+{
+	if (unlikely(res == RLIMIT_NPROC && 
+	    (cap_raised(task->cap_effective, CAP_SYS_ADMIN) || 
+	     cap_raised(task->cap_effective, CAP_SYS_RESOURCE))))
+		return;
+
+	if (unlikely(((gt && wanted > task->rlim[res].rlim_cur) ||
+		      (!gt && wanted >= task->rlim[res].rlim_cur)) &&
+		     task->rlim[res].rlim_cur != RLIM_INFINITY))
+		security_alert(GR_RESOURCE_MSG, wanted, restab_log[res],
+			       task->rlim[res].rlim_cur,
+			       gr_task_fullpath(task), task->comm,
+			       task->pid, task->uid, task->euid,
+			       task->gid, task->egid,
+			       gr_parent_task_fullpath(task),
+			       task->p_pptr->comm,
+			       task->p_pptr->pid, task->p_pptr->uid, 
+			       task->p_pptr->euid, task->p_pptr->gid,
+			       task->p_pptr->egid);
+
+	return;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/gracl_segv.c linux-2.4.27-leo/grsecurity/gracl_segv.c
--- linux-2.4.27/grsecurity/gracl_segv.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/gracl_segv.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,323 @@
+/* 
+ * grsecurity/gracl_segv.c
+ * Copyright Brad Spengler 2002, 2003
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <asm/uaccess.h>
+#include <asm/errno.h>
+#include <asm/mman.h>
+#include <net/sock.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/smp_lock.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/gracl.h>
+#include <linux/grsecurity.h>
+#include <linux/grinternal.h>
+
+static struct crash_uid *uid_set;
+static unsigned short uid_used;
+static rwlock_t gr_uid_lock = RW_LOCK_UNLOCKED;
+extern rwlock_t gr_inode_lock;
+extern struct acl_subject_label *
+	lookup_acl_subj_label(const ino_t inode, const kdev_t dev,
+			      struct acl_role_label *role);
+
+int
+gr_init_uidset(void)
+{
+	uid_set =
+	    kmalloc(GR_UIDTABLE_MAX * sizeof (struct crash_uid), GFP_KERNEL);
+	uid_used = 0;
+
+	return uid_set ? 1 : 0;
+}
+
+void
+gr_free_uidset(void)
+{
+	if (uid_set)
+		kfree(uid_set);
+
+	return;
+}
+
+int
+gr_find_uid(const uid_t uid)
+{
+	struct crash_uid *tmp = uid_set;
+	uid_t buid;
+	int low = 0, high = uid_used - 1, mid;
+
+	while (high >= low) {
+		mid = (low + high) >> 1;
+		buid = tmp[mid].uid;
+		if (buid == uid)
+			return mid;
+		if (buid > uid)
+			high = mid - 1;
+		if (buid < uid)
+			low = mid + 1;
+	}
+
+	return -1;
+}
+
+static __inline__ void
+gr_insertsort(void)
+{
+	unsigned short i, j;
+	struct crash_uid index;
+
+	for (i = 1; i < uid_used; i++) {
+		index = uid_set[i];
+		j = i;
+		while ((j > 0) && uid_set[j - 1].uid > index.uid) {
+			uid_set[j] = uid_set[j - 1];
+			j--;
+		}
+		uid_set[j] = index;
+	}
+
+	return;
+}
+
+static __inline__ void
+gr_insert_uid(const uid_t uid, const unsigned long expires)
+{
+	int loc;
+
+	if (uid_used == GR_UIDTABLE_MAX)
+		return;
+
+	loc = gr_find_uid(uid);
+
+	if (loc >= 0) {
+		uid_set[loc].expires = expires;
+		return;
+	}
+
+	uid_set[uid_used].uid = uid;
+	uid_set[uid_used].expires = expires;
+	uid_used++;
+
+	gr_insertsort();
+
+	return;
+}
+
+void
+gr_remove_uid(const unsigned short loc)
+{
+	unsigned short i;
+
+	for (i = loc + 1; i < uid_used; i++)
+		uid_set[i - i] = uid_set[i];
+
+	uid_used--;
+
+	return;
+}
+
+int
+gr_check_crash_uid(const uid_t uid)
+{
+	int loc;
+
+	if (unlikely(!gr_acl_is_enabled()))
+		return 0;
+
+	read_lock(&gr_uid_lock);
+	loc = gr_find_uid(uid);
+	read_unlock(&gr_uid_lock);
+
+	if (loc < 0)
+		return 0;
+
+	write_lock(&gr_uid_lock);
+	if (time_before_eq(uid_set[loc].expires, jiffies))
+		gr_remove_uid(loc);
+	else {
+		write_unlock(&gr_uid_lock);
+		return 1;
+	}
+
+	write_unlock(&gr_uid_lock);
+	return 0;
+}
+
+static __inline__ int
+proc_is_setxid(const struct task_struct *task)
+{
+	if (task->uid != task->euid || task->uid != task->suid ||
+	    task->uid != task->fsuid)
+		return 1;
+	if (task->gid != task->egid || task->gid != task->sgid ||
+	    task->gid != task->fsgid)
+		return 1;
+
+	return 0;
+}
+static __inline__ int
+gr_fake_force_sig(int sig, struct task_struct *t)
+{
+	unsigned long int flags;
+
+	spin_lock_irqsave(&t->sigmask_lock, flags);
+	if (t->sig == NULL) {
+		spin_unlock_irqrestore(&t->sigmask_lock, flags);
+		return -ESRCH;
+	}
+
+	if (t->sig->action[sig - 1].sa.sa_handler == SIG_IGN)
+		t->sig->action[sig - 1].sa.sa_handler = SIG_DFL;
+	sigdelset(&t->blocked, sig);
+	recalc_sigpending(t);
+	spin_unlock_irqrestore(&t->sigmask_lock, flags);
+
+	return send_sig_info(sig, (void *) 1L, t);
+}
+
+void
+gr_handle_crash(struct task_struct *task, const int sig)
+{
+	struct acl_subject_label *curr;
+	struct acl_subject_label *curr2;
+	struct task_struct *tsk;
+
+	if (sig != SIGSEGV && sig != SIGKILL && sig != SIGBUS && sig != SIGILL)
+		return;
+
+	if (unlikely(!gr_acl_is_enabled()))
+		return;
+
+	curr = task->acl;
+
+	if (!(curr->resmask & (1 << GR_CRASH_RES)))
+		return;
+
+	if (time_before_eq(curr->expires, jiffies)) {
+		curr->expires = 0;
+		curr->crashes = 0;
+	}
+
+	curr->crashes++;
+
+	if (!curr->expires)
+		curr->expires = jiffies + curr->res[GR_CRASH_RES].rlim_max;
+
+	if ((curr->crashes >= curr->res[GR_CRASH_RES].rlim_cur) &&
+	    time_after(curr->expires, jiffies)) {
+		if (task->uid && proc_is_setxid(task)) {
+			security_alert(GR_SEGVSTART_ACL_MSG,
+				       gr_task_fullpath(task), task->comm,
+				       task->pid, task->uid, task->euid,
+				       task->gid, task->egid,
+				       gr_parent_task_fullpath(task),
+				       task->p_pptr->comm, task->p_pptr->pid,
+				       task->p_pptr->uid, task->p_pptr->euid,
+				       task->p_pptr->gid, task->p_pptr->egid,
+				       task->uid,
+				       curr->res[GR_CRASH_RES].rlim_max / HZ);
+			write_lock(&gr_uid_lock);
+			gr_insert_uid(task->uid, curr->expires);
+			write_unlock(&gr_uid_lock);
+			curr->expires = 0;
+			curr->crashes = 0;
+			read_lock(&tasklist_lock);
+			for_each_task(tsk) {
+				if (tsk != task && tsk->uid == task->uid)
+					gr_fake_force_sig(SIGKILL, tsk);
+			}
+			read_unlock(&tasklist_lock);
+		} else {
+			security_alert(GR_SEGVNOSUID_ACL_MSG,
+				       gr_task_fullpath(task), task->comm,
+				       task->pid, task->uid, task->euid,
+				       task->gid, task->egid,
+				       gr_parent_task_fullpath(task),
+				       task->p_pptr->comm, task->p_pptr->pid,
+				       task->p_pptr->uid, task->p_pptr->euid,
+				       task->p_pptr->gid, task->p_pptr->egid,
+				       kdevname(curr->device), curr->inode,
+				       curr->res[GR_CRASH_RES].rlim_max / HZ);
+			read_lock(&tasklist_lock);
+			for_each_task(tsk) {
+				if (likely(tsk != task)) {
+					curr2 = tsk->acl;
+
+					if (curr2->device == curr->device &&
+					    curr2->inode == curr->inode)
+						gr_fake_force_sig(SIGKILL, tsk);
+				}
+			}
+			read_unlock(&tasklist_lock);
+		}
+	}
+
+	return;
+}
+
+int
+gr_check_crash_exec(const struct file *filp)
+{
+	struct acl_subject_label *curr;
+
+	if (unlikely(!gr_acl_is_enabled()))
+		return 0;
+
+	read_lock(&gr_inode_lock);
+	curr = lookup_acl_subj_label(filp->f_dentry->d_inode->i_ino,
+				     filp->f_dentry->d_inode->i_dev,
+				     current->role);
+	read_unlock(&gr_inode_lock);
+
+	if (!curr || !(curr->resmask & (1 << GR_CRASH_RES)) ||
+	    (!curr->crashes && !curr->expires))
+		return 0;
+
+	if ((curr->crashes >= curr->res[GR_CRASH_RES].rlim_cur) &&
+	    time_after(curr->expires, jiffies))
+		return 1;
+	else if (time_before_eq(curr->expires, jiffies)) {
+		curr->crashes = 0;
+		curr->expires = 0;
+	}
+
+	return 0;
+}
+
+void
+gr_handle_alertkill(void)
+{
+	struct acl_subject_label *curracl;
+	__u32 curr_ip;
+	struct task_struct *task;
+
+	if (unlikely(!gr_acl_is_enabled()))
+		return;
+
+	curracl = current->acl;
+	curr_ip = current->curr_ip;
+
+	if ((curracl->mode & GR_KILLIPPROC) && curr_ip &&
+	    (curr_ip != 0xffffffff)) {
+		read_lock(&tasklist_lock);
+		for_each_task(task) {
+			if (task->curr_ip == curr_ip)
+				gr_fake_force_sig(SIGKILL, task);
+		}
+		read_unlock(&tasklist_lock);
+	} else if (curracl->mode & GR_KILLPROC)
+		gr_fake_force_sig(SIGKILL, current);
+
+	return;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/gracl_shm.c linux-2.4.27-leo/grsecurity/gracl_shm.c
--- linux-2.4.27/grsecurity/gracl_shm.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/gracl_shm.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,36 @@
+/* shared memory handling routines, (c) Brad Spengler 2002, 2003 */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/file.h>
+#include <linux/ipc.h>
+#include <linux/gracl.h>
+#include <linux/grsecurity.h>
+#include <linux/grinternal.h>
+
+int
+gr_handle_shmat(const pid_t shm_cprid, const pid_t shm_lapid,
+		const time_t shm_createtime, const uid_t cuid, const int shmid)
+{
+	struct task_struct *task;
+
+	if (!gr_acl_is_enabled())
+		return 1;
+
+	task = find_task_by_pid(shm_cprid);
+
+	if (unlikely(!task))
+		task = find_task_by_pid(shm_lapid);
+
+	if (unlikely(task && ((task->start_time < shm_createtime) ||
+			      (task->pid == shm_lapid)) &&
+		     (task->acl->mode & GR_PROTSHM) &&
+		     (task->acl != current->acl))) {
+		security_alert(GR_SHMAT_ACL_MSG, cuid, shm_cprid, shmid,
+			       DEFAULTSECARGS);
+		return 0;
+	}
+
+	return 1;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/grsec_chdir.c linux-2.4.27-leo/grsecurity/grsec_chdir.c
--- linux-2.4.27/grsecurity/grsec_chdir.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/grsec_chdir.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,20 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/grsecurity.h>
+#include <linux/grinternal.h>
+
+void
+gr_log_chdir(const struct dentry *dentry, const struct vfsmount *mnt)
+{
+#ifdef CONFIG_GRKERNSEC_AUDIT_CHDIR
+	if ((grsec_enable_chdir && grsec_enable_group &&
+	     in_group_p(grsec_audit_gid)) || (grsec_enable_chdir &&
+					      !grsec_enable_group)) {
+		security_audit(GR_CHDIR_AUDIT_MSG, gr_to_filename(dentry, mnt),
+			       DEFAULTSECARGS);
+	}
+#endif
+	return;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/grsec_chroot.c linux-2.4.27-leo/grsecurity/grsec_chroot.c
--- linux-2.4.27/grsecurity/grsec_chroot.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/grsec_chroot.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,355 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/grinternal.h>
+
+int
+gr_handle_chroot_unix(const pid_t pid)
+{
+#ifdef CONFIG_GRKERNSEC_CHROOT_UNIX
+	struct task_struct *p, **htable;
+
+	if (unlikely(!grsec_enable_chroot_unix))
+		return 1;
+
+	if (likely(!proc_is_chrooted(current)))
+		return 1;
+
+	read_lock(&tasklist_lock);
+
+	htable = &pidhash[pid_hashfn(pid)];
+
+	for (p = *htable; p && p->pid != pid; p = p->pidhash_next) ;
+
+	if (p) {
+		task_lock(p);
+		if (!have_same_root(current, p)) {
+			task_unlock(p);
+			read_unlock(&tasklist_lock);
+			security_alert(GR_UNIX_CHROOT_MSG, DEFAULTSECARGS);
+			return 0;
+		}
+		task_unlock(p);
+	}
+
+	read_unlock(&tasklist_lock);
+#endif
+	return 1;
+}
+
+int
+gr_handle_chroot_nice(void)
+{
+#ifdef CONFIG_GRKERNSEC_CHROOT_NICE
+	if (grsec_enable_chroot_nice && proc_is_chrooted(current)) {
+		security_alert(GR_NICE_CHROOT_MSG, DEFAULTSECARGS);
+		return -EPERM;
+	}
+#endif
+	return 0;
+}
+
+int
+gr_handle_chroot_setpriority(const struct task_struct *p, const int niceval)
+{
+#ifdef CONFIG_GRKERNSEC_CHROOT_NICE
+	if (grsec_enable_chroot_nice && (niceval < p->nice)
+			&& proc_is_chrooted(current)) {
+		security_alert(GR_PRIORITY_CHROOT_MSG, p->comm, p->pid,
+			       DEFAULTSECARGS);
+		return -EACCES;
+	}
+#endif
+	return 0;
+}
+
+int
+gr_handle_chroot_capset(struct task_struct *target)
+{
+#ifdef CONFIG_GRKERNSEC_CHROOT_CAPS
+	if (!grsec_enable_chroot_caps || !proc_is_chrooted(current))
+		return 0;
+	task_lock(target);
+	if (!have_same_root(current, target)) {
+		task_unlock(target);
+		security_alert(GR_CAPSET_CHROOT_MSG, target->comm, target->pid,
+			       DEFAULTSECARGS);
+		return 1;
+	}
+	task_unlock(target);
+#endif
+	return 0;
+}
+
+int
+gr_handle_chroot_rawio(const struct inode *inode)
+{
+#ifdef CONFIG_GRKERNSEC_CHROOT_CAPS
+	if (grsec_enable_chroot_caps && proc_is_chrooted(current) && 
+	    inode && S_ISBLK(inode->i_mode) && !capable(CAP_SYS_RAWIO))
+		return 1;
+#endif
+	return 0;
+}
+
+int
+gr_pid_is_chrooted(struct task_struct *p)
+{
+#ifdef CONFIG_GRKERNSEC_CHROOT_FINDTASK
+	if (!grsec_enable_chroot_findtask || !current->fs || 
+	    !proc_is_chrooted(current) || !p)
+		return 0;
+
+	task_lock(p);
+	if (p->fs && !have_same_root(current, p)) {
+		task_unlock(p);
+		return 1;
+	}
+	task_unlock(p);
+#endif
+	return 0;
+}
+
+#if defined(CONFIG_GRKERNSEC_CHROOT_DOUBLE) || defined(CONFIG_GRKERNSEC_CHROOT_FCHDIR)
+int gr_is_outside_chroot(const struct dentry *u_dentry, const struct vfsmount *u_mnt)
+{
+	struct dentry *dentry = (struct dentry *)u_dentry;
+	struct vfsmount *mnt = (struct vfsmount *)u_mnt;
+	struct dentry *realroot;
+	struct vfsmount *realrootmnt;
+	struct dentry *currentroot;
+	struct vfsmount *currentmnt;
+
+	read_lock(&child_reaper->fs->lock);
+	realrootmnt = mntget(child_reaper->fs->rootmnt);
+	realroot = dget(child_reaper->fs->root);
+	read_unlock(&child_reaper->fs->lock);
+
+	read_lock(&current->fs->lock);
+	currentmnt = mntget(current->fs->rootmnt);
+	currentroot = dget(current->fs->root);
+	read_unlock(&current->fs->lock);
+
+	spin_lock(&dcache_lock);
+	for (;;) {
+		if (unlikely((dentry == realroot && mnt == realrootmnt)
+		     || (dentry == currentroot && mnt == currentmnt)))
+			break;
+		if (unlikely(dentry == mnt->mnt_root || IS_ROOT(dentry))) {
+			if (mnt->mnt_parent == mnt)
+				break;
+			dentry = mnt->mnt_mountpoint;
+			mnt = mnt->mnt_parent;
+			continue;
+		}
+		dentry = dentry->d_parent;
+	}
+	spin_unlock(&dcache_lock);
+
+	dput(currentroot);
+	mntput(currentmnt);
+
+	if (dentry == realroot && mnt == realrootmnt) {
+		/* access is outside of chroot */
+		dput(realroot);
+		mntput(realrootmnt);
+		return 0;
+	}
+
+	dput(realroot);
+	mntput(realrootmnt);
+	return 1;
+}
+#endif
+
+int
+gr_chroot_fchdir(struct dentry *u_dentry, struct vfsmount *u_mnt)
+{
+#ifdef CONFIG_GRKERNSEC_CHROOT_FCHDIR
+	if (!grsec_enable_chroot_fchdir)
+		return 1;
+
+	if (!proc_is_chrooted(current))
+		return 1;
+	else if (!gr_is_outside_chroot(u_dentry, u_mnt)) {
+		security_alert(GR_CHROOT_FCHDIR_MSG,
+			       gr_to_filename(u_dentry, u_mnt),
+			       DEFAULTSECARGS);
+		return 0;
+	}
+#endif
+	return 1;
+}
+
+int
+gr_chroot_shmat(const pid_t shm_cprid, const pid_t shm_lapid,
+		const time_t shm_createtime)
+{
+#ifdef CONFIG_GRKERNSEC_CHROOT_SHMAT
+	struct task_struct *p, **htable;
+
+	if (unlikely(!grsec_enable_chroot_shmat))
+		return 1;
+
+	if (likely(!proc_is_chrooted(current)))
+		return 1;
+
+	read_lock(&tasklist_lock);
+
+	htable = &pidhash[pid_hashfn(shm_cprid)];
+
+	for (p = *htable; p && p->pid != shm_cprid; p = p->pidhash_next) ;
+
+	if (p) {
+		task_lock(p);
+		if (!have_same_root(current, p) &&
+		    (p->start_time < shm_createtime)) {
+			task_unlock(p);
+			read_unlock(&tasklist_lock);
+			security_alert(GR_SHMAT_CHROOT_MSG, DEFAULTSECARGS);
+			return 0;
+		}
+		task_unlock(p);
+	} else {
+		htable = &pidhash[pid_hashfn(shm_lapid)];
+		for (p = *htable; p && p->pid != shm_lapid;
+		     p = p->pidhash_next) ;
+
+		if (p) {
+			task_lock(p);
+			if (!have_same_root(current, p)) {
+				task_unlock(p);
+				read_unlock(&tasklist_lock);
+				security_alert(GR_SHMAT_CHROOT_MSG, DEFAULTSECARGS);
+				return 0;
+			}
+			task_unlock(p);
+		}
+	}
+
+	read_unlock(&tasklist_lock);
+#endif
+	return 1;
+}
+
+void
+gr_log_chroot_exec(const struct dentry *dentry, const struct vfsmount *mnt)
+{
+#ifdef CONFIG_GRKERNSEC_CHROOT_EXECLOG
+	if (grsec_enable_chroot_execlog && proc_is_chrooted(current))
+		security_audit(GR_EXEC_CHROOT_MSG, gr_to_filename(dentry, mnt),
+			       DEFAULTSECARGS);
+#endif
+	return;
+}
+
+int
+gr_handle_chroot_mknod(const struct dentry *dentry,
+		       const struct vfsmount *mnt, const int mode)
+{
+#ifdef CONFIG_GRKERNSEC_CHROOT_MKNOD
+	if (grsec_enable_chroot_mknod && !S_ISFIFO(mode) && !S_ISREG(mode) &&
+	    proc_is_chrooted(current)) {
+		security_alert(GR_MKNOD_CHROOT_MSG,
+			       gr_to_filename(dentry, mnt), DEFAULTSECARGS);
+		return -EPERM;
+	}
+#endif
+	return 0;
+}
+
+int
+gr_handle_chroot_mount(const struct dentry *dentry,
+		       const struct vfsmount *mnt, const char *dev_name)
+{
+#ifdef CONFIG_GRKERNSEC_CHROOT_MOUNT
+	if (grsec_enable_chroot_mount && proc_is_chrooted(current)) {
+		security_alert(GR_MOUNT_CHROOT_MSG, dev_name,
+			       gr_to_filename(dentry, mnt), DEFAULTSECARGS);
+		return -EPERM;
+	}
+#endif
+	return 0;
+}
+
+int
+gr_handle_chroot_pivot(void)
+{
+#ifdef CONFIG_GRKERNSEC_CHROOT_PIVOT
+	if (grsec_enable_chroot_pivot && proc_is_chrooted(current)) {
+		security_alert(GR_PIVOT_CHROOT_MSG, DEFAULTSECARGS);
+		return -EPERM;
+	}
+#endif
+	return 0;
+}
+
+int
+gr_handle_chroot_chroot(const struct dentry *dentry, const struct vfsmount *mnt)
+{
+#ifdef CONFIG_GRKERNSEC_CHROOT_DOUBLE
+	if (grsec_enable_chroot_double && proc_is_chrooted(current) &&
+	    !gr_is_outside_chroot(dentry, mnt)) {
+		security_alert(GR_CHROOT_CHROOT_MSG,
+			       gr_to_filename(dentry, mnt), DEFAULTSECARGS);
+		return -EPERM;
+	}
+#endif
+	return 0;
+}
+
+void
+gr_handle_chroot_caps(struct task_struct *task)
+{
+#ifdef CONFIG_GRKERNSEC_CHROOT_CAPS
+	if (grsec_enable_chroot_caps && proc_is_chrooted(task)) {
+		task->cap_permitted =
+		    cap_drop(task->cap_permitted, GR_CHROOT_CAPS);
+		task->cap_inheritable =
+		    cap_drop(task->cap_inheritable, GR_CHROOT_CAPS);
+		task->cap_effective =
+		    cap_drop(task->cap_effective, GR_CHROOT_CAPS);
+	}
+#endif
+	return;
+}
+
+int
+gr_handle_chroot_sysctl(const int op)
+{
+#ifdef CONFIG_GRKERNSEC_CHROOT_SYSCTL
+	if (grsec_enable_chroot_sysctl && proc_is_chrooted(current)
+	    && (op & 002))
+		return -EACCES;
+#endif
+	return 0;
+}
+
+void
+gr_handle_chroot_chdir(struct dentry *dentry, struct vfsmount *mnt)
+{
+#ifdef CONFIG_GRKERNSEC_CHROOT_CHDIR
+	if (grsec_enable_chroot_chdir)
+		set_fs_pwd(current->fs, mnt, dentry);
+#endif
+	return;
+}
+
+int
+gr_handle_chroot_chmod(const struct dentry *dentry,
+		       const struct vfsmount *mnt, const int mode)
+{
+#ifdef CONFIG_GRKERNSEC_CHROOT_CHMOD
+	if (grsec_enable_chroot_chmod &&
+	    ((mode & S_ISUID) || ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))) &&
+	    proc_is_chrooted(current)) {
+		security_alert(GR_CHMOD_CHROOT_MSG,
+			       gr_to_filename(dentry, mnt), DEFAULTSECARGS);
+		return -EPERM;
+	}
+#endif
+	return 0;
+}
+
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/grsec_disabled.c linux-2.4.27-leo/grsecurity/grsec_disabled.c
--- linux-2.4.27/grsecurity/grsec_disabled.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/grsec_disabled.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,393 @@
+/* 
+ * when grsecurity is disabled, compile all external functions into nothing
+ */
+
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/kdev_t.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/skbuff.h>
+#include <linux/sysctl.h>
+
+#ifdef CONFIG_SYSCTL
+__inline__ __u32
+gr_handle_sysctl(const struct ctl_table * table, __u32 mode)
+{
+	return mode;
+}
+#endif
+
+__inline__ int
+gr_acl_is_enabled(void)
+{
+	return 0;
+}
+
+__inline__ int
+gr_handle_rawio(const struct inode *inode)
+{
+	return 0;
+}
+
+__inline__ void
+gr_acl_handle_psacct(struct task_struct *task, const long code)
+{
+	return;
+}
+
+__inline__ int
+gr_handle_mmap(const struct file *filp, const unsigned long prot)
+{
+	return 0;
+}
+
+__inline__ int
+gr_handle_ptrace(struct task_struct *task, const long request)
+{
+	return 0;
+}
+
+__inline__ int
+gr_handle_proc_ptrace(struct task_struct *task)
+{
+	return 0;
+}
+
+__inline__ void
+gr_learn_resource(const struct task_struct *task,
+		  const int res, const unsigned long wanted, const int gt)
+{
+	return;
+}
+
+__inline__ int
+gr_set_acls(const int type)
+{
+	return 0;
+}
+
+__inline__ int
+gr_check_hidden_task(const struct task_struct *tsk)
+{
+	return 0;
+}
+
+__inline__ int
+gr_check_protected_task(const struct task_struct *task)
+{
+	return 0;
+}
+
+__inline__ void
+gr_copy_label(struct task_struct *tsk)
+{
+	return;
+}
+
+__inline__ void
+gr_set_pax_flags(struct task_struct *task)
+{
+	return;
+}
+
+__inline__ int
+gr_set_proc_label(const struct dentry *dentry, const struct vfsmount *mnt)
+{
+	return 0;
+}
+
+__inline__ void
+gr_handle_delete(const ino_t ino, const kdev_t dev)
+{
+	return;
+}
+
+__inline__ void
+gr_handle_create(const struct dentry *dentry, const struct vfsmount *mnt)
+{
+	return;
+}
+
+__inline__ void
+gr_handle_crash(struct task_struct *task, const int sig)
+{
+	return;
+}
+
+__inline__ int
+gr_check_crash_exec(const struct file *filp)
+{
+	return 0;
+}
+
+__inline__ int
+gr_check_crash_uid(const uid_t uid)
+{
+	return 0;
+}
+
+__inline__ int
+gr_handle_rename(struct inode *old_dir, struct inode *new_dir,
+		 struct dentry *old_dentry,
+		 struct dentry *new_dentry,
+		 struct vfsmount *mnt, const __u8 replace)
+{
+	return 0;
+}
+
+__inline__ int
+gr_search_socket(const int family, const int type, const int protocol)
+{
+	return 1;
+}
+
+__inline__ int
+gr_search_connectbind(const int mode, const struct socket *sock,
+		      const struct sockaddr_in *addr)
+{
+	return 1;
+}
+
+__inline__ int
+gr_task_is_capable(struct task_struct *task, const int cap)
+{
+	return 1;
+}
+
+__inline__ void
+gr_handle_alertkill(void)
+{
+	return;
+}
+
+__inline__ __u32
+gr_acl_handle_execve(const struct dentry * dentry, const struct vfsmount * mnt)
+{
+	return 1;
+}
+
+__inline__ __u32
+gr_acl_handle_hidden_file(const struct dentry * dentry,
+			  const struct vfsmount * mnt)
+{
+	return 1;
+}
+
+__inline__ __u32
+gr_acl_handle_open(const struct dentry * dentry, const struct vfsmount * mnt,
+		   const int fmode)
+{
+	return 1;
+}
+
+__inline__ __u32
+gr_acl_handle_rmdir(const struct dentry * dentry, const struct vfsmount * mnt)
+{
+	return 1;
+}
+
+__inline__ __u32
+gr_acl_handle_unlink(const struct dentry * dentry, const struct vfsmount * mnt)
+{
+	return 1;
+}
+
+__inline__ int
+gr_acl_handle_mmap(const struct file *file, const unsigned long prot,
+		   unsigned int *vm_flags)
+{
+	return 1;
+}
+
+__inline__ __u32
+gr_acl_handle_truncate(const struct dentry * dentry,
+		       const struct vfsmount * mnt)
+{
+	return 1;
+}
+
+__inline__ __u32
+gr_acl_handle_utime(const struct dentry * dentry, const struct vfsmount * mnt)
+{
+	return 1;
+}
+
+__inline__ __u32
+gr_acl_handle_access(const struct dentry * dentry,
+		     const struct vfsmount * mnt, const int fmode)
+{
+	return 1;
+}
+
+__inline__ __u32
+gr_acl_handle_fchmod(const struct dentry * dentry, const struct vfsmount * mnt,
+		     mode_t mode)
+{
+	return 1;
+}
+
+__inline__ __u32
+gr_acl_handle_chmod(const struct dentry * dentry, const struct vfsmount * mnt,
+		    mode_t mode)
+{
+	return 1;
+}
+
+__inline__ __u32
+gr_acl_handle_chown(const struct dentry * dentry, const struct vfsmount * mnt)
+{
+	return 1;
+}
+
+__inline__ void
+grsecurity_init(void)
+{
+	return;
+}
+
+__inline__ __u32
+gr_acl_handle_mknod(const struct dentry * new_dentry,
+		    const struct dentry * parent_dentry,
+		    const struct vfsmount * parent_mnt,
+		    const int mode)
+{
+	return 1;
+}
+
+__inline__ __u32
+gr_acl_handle_mkdir(const struct dentry * new_dentry,
+		    const struct dentry * parent_dentry,
+		    const struct vfsmount * parent_mnt)
+{
+	return 1;
+}
+
+__inline__ __u32
+gr_acl_handle_symlink(const struct dentry * new_dentry,
+		      const struct dentry * parent_dentry,
+		      const struct vfsmount * parent_mnt, const char *from)
+{
+	return 1;
+}
+
+__inline__ __u32
+gr_acl_handle_link(const struct dentry * new_dentry,
+		   const struct dentry * parent_dentry,
+		   const struct vfsmount * parent_mnt,
+		   const struct dentry * old_dentry,
+		   const struct vfsmount * old_mnt, const char *to)
+{
+	return 1;
+}
+
+__inline__ int
+gr_acl_handle_rename(const struct dentry *new_dentry,
+		     const struct dentry *parent_dentry,
+		     const struct vfsmount *parent_mnt,
+		     const struct dentry *old_dentry,
+		     const struct inode *old_parent_inode,
+		     const struct vfsmount *old_mnt, const char *newname)
+{
+	return 1;
+}
+
+__inline__ __u32
+gr_acl_handle_filldir(const struct dentry * dentry,
+		      const struct vfsmount * mnt, const ino_t ino)
+{
+	return 1;
+}
+
+__inline__ int
+gr_handle_shmat(const pid_t shm_cprid, const pid_t shm_lapid,
+		const time_t shm_createtime, const uid_t cuid, const int shmid)
+{
+	return 1;
+}
+
+__inline__ int
+gr_search_bind(const struct socket *sock, const struct sockaddr_in *addr)
+{
+	return 1;
+}
+
+__inline__ int
+gr_search_connect(const struct socket *sock, const struct sockaddr_in *addr)
+{
+	return 1;
+}
+
+__inline__ __u32
+gr_acl_handle_unix(const struct dentry * dentry, const struct vfsmount * mnt)
+{
+	return 1;
+}
+
+__inline__ __u32
+gr_acl_handle_creat(const struct dentry * dentry,
+		    const struct dentry * p_dentry,
+		    const struct vfsmount * p_mnt, const int fmode,
+		    const int imode)
+{
+	return 1;
+}
+
+__inline__ void
+gr_acl_handle_exit(void)
+{
+	return;
+}
+
+__inline__ int
+gr_acl_handle_mprotect(const struct file *file, const unsigned long prot)
+{
+	return 1;
+}
+
+__inline__ void
+gr_set_role_label(const uid_t uid, const gid_t gid)
+{
+	return;
+}
+
+__inline__ int
+gr_acl_handle_procpidmem(const struct task_struct *task)
+{
+	return 0;
+}
+
+__inline__ int
+gr_search_udp_recvmsg(const struct sock *sk, const struct sk_buff *skb)
+{
+	return 1;
+}
+
+__inline__ int
+gr_search_udp_sendmsg(const struct sock *sk, const struct sockaddr_in *addr)
+{
+	return 1;
+}
+
+__inline__ void
+gr_set_kernel_label(struct task_struct *task)
+{
+	return;
+}
+
+__inline__ int
+gr_check_user_change(int real, int effective, int fs)
+{
+	return 0;
+}
+
+__inline__ int
+gr_check_group_change(int real, int effective, int fs)
+{
+	return 0;
+}
+
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/grsec_exec.c linux-2.4.27-leo/grsecurity/grsec_exec.c
--- linux-2.4.27/grsecurity/grsec_exec.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/grsec_exec.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,70 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/grdefs.h>
+#include <linux/grinternal.h>
+#include <linux/capability.h>
+
+#include <asm/uaccess.h>
+
+int
+gr_handle_nproc(void)
+{
+#ifdef CONFIG_GRKERNSEC_EXECVE
+	if (grsec_enable_execve && current->user &&
+	    (atomic_read(&current->user->processes) >
+	     current->rlim[RLIMIT_NPROC].rlim_cur) &&
+	    !capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE)) {
+		security_alert(GR_NPROC_MSG, DEFAULTSECARGS);
+		return -EAGAIN;
+	}
+#endif
+	return 0;
+}
+
+void
+gr_handle_exec_args(struct linux_binprm *bprm, char **argv)
+{
+#ifdef CONFIG_GRKERNSEC_EXECLOG
+	char grarg[64] = { 0 };
+	__u8 execlen = 0;
+	unsigned int i;
+
+	if (!((grsec_enable_execlog && grsec_enable_group &&
+	       in_group_p(grsec_audit_gid))
+	      || (grsec_enable_execlog && !grsec_enable_group)))
+		return;
+
+	if (unlikely(!argv))
+		goto log;
+
+	for (i = 0; i < bprm->argc && execlen < 62; i++) {
+		char *p;
+		__u8 len;
+
+		if (get_user(p, argv + i))
+			goto log;
+		if (!p)
+			goto log;
+		len = strnlen_user(p, 62 - execlen);
+		if (len > 62 - execlen)
+			len = 62 - execlen;
+		else if (len > 0)
+			len--;
+		if (copy_from_user(grarg + execlen, p, len))
+			goto log;
+		execlen += len;
+		*(grarg + execlen) = ' ';
+		*(grarg + execlen + 1) = '\0';
+		execlen++;
+	}
+
+      log:
+	security_audit(GR_EXEC_AUDIT_MSG, gr_to_filename(bprm->file->f_dentry,
+							 bprm->file->f_vfsmnt),
+		       grarg, DEFAULTSECARGS);
+#endif
+	return;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/grsec_fifo.c linux-2.4.27-leo/grsecurity/grsec_fifo.c
--- linux-2.4.27/grsecurity/grsec_fifo.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/grsec_fifo.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,24 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/grinternal.h>
+
+int
+gr_handle_fifo(const struct dentry *dentry, const struct vfsmount *mnt,
+	       const struct dentry *dir, const int flag, const int acc_mode)
+{
+#ifdef CONFIG_GRKERNSEC_FIFO
+	if (grsec_enable_fifo && S_ISFIFO(dentry->d_inode->i_mode) &&
+	    !(flag & O_EXCL) && (dir->d_inode->i_mode & S_ISVTX) &&
+	    (dentry->d_inode->i_uid != dir->d_inode->i_uid) &&
+	    (current->fsuid != dentry->d_inode->i_uid)) {
+		if (!permission(dentry->d_inode, acc_mode))
+			security_alert(GR_FIFO_MSG, gr_to_filename(dentry, mnt),
+				       dentry->d_inode->i_uid,
+				       dentry->d_inode->i_gid, DEFAULTSECARGS);
+		return -EACCES;
+	}
+#endif
+	return 0;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/grsec_fork.c linux-2.4.27-leo/grsecurity/grsec_fork.c
--- linux-2.4.27/grsecurity/grsec_fork.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/grsec_fork.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,14 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/grsecurity.h>
+#include <linux/grinternal.h>
+
+void
+gr_log_forkfail(const int retval)
+{
+#ifdef CONFIG_GRKERNSEC_FORKFAIL
+	if (grsec_enable_forkfail)
+		security_alert(GR_FAILFORK_MSG, retval, DEFAULTSECARGS);
+#endif
+	return;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/grsec_init.c linux-2.4.27-leo/grsecurity/grsec_init.c
--- linux-2.4.27/grsecurity/grsec_init.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/grsec_init.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,224 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+#include <linux/gracl.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+int grsec_enable_link;
+int grsec_enable_dmesg;
+int grsec_enable_fifo;
+int grsec_enable_execve;
+int grsec_enable_execlog;
+int grsec_enable_signal;
+int grsec_enable_forkfail;
+int grsec_enable_time;
+int grsec_enable_audit_textrel;
+int grsec_enable_group;
+int grsec_audit_gid;
+int grsec_enable_chdir;
+int grsec_enable_audit_ipc;
+int grsec_enable_mount;
+int grsec_enable_chroot_findtask;
+int grsec_enable_chroot_mount;
+int grsec_enable_chroot_shmat;
+int grsec_enable_chroot_fchdir;
+int grsec_enable_chroot_double;
+int grsec_enable_chroot_pivot;
+int grsec_enable_chroot_chdir;
+int grsec_enable_chroot_chmod;
+int grsec_enable_chroot_mknod;
+int grsec_enable_chroot_nice;
+int grsec_enable_chroot_execlog;
+int grsec_enable_chroot_caps;
+int grsec_enable_chroot_sysctl;
+int grsec_enable_chroot_unix;
+int grsec_enable_tpe;
+int grsec_tpe_gid;
+int grsec_enable_tpe_all;
+int grsec_enable_randpid;
+int grsec_enable_randid;
+int grsec_enable_randisn;
+int grsec_enable_randsrc;
+int grsec_enable_randrpc;
+int grsec_enable_socket_all;
+int grsec_socket_all_gid;
+int grsec_enable_socket_client;
+int grsec_socket_client_gid;
+int grsec_enable_socket_server;
+int grsec_socket_server_gid;
+int grsec_lock;
+
+spinlock_t grsec_alert_lock = SPIN_LOCK_UNLOCKED;
+unsigned long grsec_alert_wtime = 0;
+unsigned long grsec_alert_fyet = 0;
+
+spinlock_t grsec_audit_lock = SPIN_LOCK_UNLOCKED;
+
+rwlock_t grsec_exec_file_lock = RW_LOCK_UNLOCKED;
+
+char *gr_shared_page[4][NR_CPUS];
+extern struct gr_arg *gr_usermode;
+extern unsigned char *gr_system_salt;
+extern unsigned char *gr_system_sum;
+extern struct task_struct **gr_conn_table;
+extern const unsigned int gr_conn_table_size;
+
+void
+grsecurity_init(void)
+{
+	int i, j;
+	/* create the per-cpu shared pages */
+
+	for (j = 0; j < 4; j++) {
+		for (i = 0; i < NR_CPUS; i++) {
+			gr_shared_page[j][i] = (char *) get_zeroed_page(GFP_KERNEL);
+			if (!gr_shared_page[j][i]) {
+				panic("Unable to allocate grsecurity shared page");
+				return;
+			}
+		}
+	}
+
+	/* create hash tables for ip tagging */
+
+	gr_conn_table = (struct task_struct **) vmalloc(gr_conn_table_size * sizeof(struct task_struct *));
+	if (gr_conn_table == NULL) {
+		panic("Unable to allocate grsecurity IP tagging table");
+		return;
+	}
+	memset(gr_conn_table, 0, gr_conn_table_size * sizeof(struct task_struct *));
+
+	/* allocate memory for authentication structure */
+	gr_usermode = kmalloc(sizeof(struct gr_arg), GFP_KERNEL);
+	gr_system_salt = kmalloc(GR_SALT_LEN, GFP_KERNEL);
+	gr_system_sum = kmalloc(GR_SHA_LEN, GFP_KERNEL);
+
+	if (!gr_usermode || !gr_system_salt || !gr_system_sum) {
+		panic("Unable to allocate grsecurity authentication structure");
+		return;
+	}
+
+#ifndef CONFIG_GRKERNSEC_SYSCTL
+	grsec_lock = 1;
+#ifdef CONFIG_GRKERNSEC_AUDIT_TEXTREL
+	grsec_enable_audit_textrel = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_AUDIT_GROUP
+	grsec_enable_group = 1;
+	grsec_audit_gid = CONFIG_GRKERNSEC_AUDIT_GID;
+#endif
+#ifdef CONFIG_GRKERNSEC_AUDIT_CHDIR
+	grsec_enable_chdir = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_AUDIT_IPC
+	grsec_enable_audit_ipc = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_AUDIT_MOUNT
+	grsec_enable_mount = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_LINK
+	grsec_enable_link = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_DMESG
+	grsec_enable_dmesg = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_FIFO
+	grsec_enable_fifo = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_EXECVE
+	grsec_enable_execve = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_EXECLOG
+	grsec_enable_execlog = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_SIGNAL
+	grsec_enable_signal = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_FORKFAIL
+	grsec_enable_forkfail = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_TIME
+	grsec_enable_time = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_FINDTASK
+	grsec_enable_chroot_findtask = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_UNIX
+	grsec_enable_chroot_unix = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_MOUNT
+	grsec_enable_chroot_mount = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_FCHDIR
+	grsec_enable_chroot_fchdir = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_SHMAT
+	grsec_enable_chroot_shmat = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_DOUBLE
+	grsec_enable_chroot_double = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_PIVOT
+	grsec_enable_chroot_pivot = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_CHDIR
+	grsec_enable_chroot_chdir = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_CHMOD
+	grsec_enable_chroot_chmod = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_MKNOD
+	grsec_enable_chroot_mknod = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_NICE
+	grsec_enable_chroot_nice = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_EXECLOG
+	grsec_enable_chroot_execlog = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_CAPS
+	grsec_enable_chroot_caps = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_SYSCTL
+	grsec_enable_chroot_sysctl = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_TPE
+	grsec_enable_tpe = 1;
+	grsec_tpe_gid = CONFIG_GRKERNSEC_TPE_GID;
+#ifdef CONFIG_GRKERNSEC_TPE_ALL
+	grsec_enable_tpe_all = 1;
+#endif
+#endif
+#ifdef CONFIG_GRKERNSEC_RANDPID
+	grsec_enable_randpid = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_RANDID
+	grsec_enable_randid = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_RANDISN
+	grsec_enable_randisn = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_RANDSRC
+	grsec_enable_randsrc = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_RANDRPC
+	grsec_enable_randrpc = 1;
+#endif
+#ifdef CONFIG_GRKERNSEC_SOCKET_ALL
+	grsec_enable_socket_all = 1;
+	grsec_socket_all_gid = CONFIG_GRKERNSEC_SOCKET_ALL_GID;
+#endif
+#ifdef CONFIG_GRKERNSEC_SOCKET_CLIENT
+	grsec_enable_socket_client = 1;
+	grsec_socket_client_gid = CONFIG_GRKERNSEC_SOCKET_CLIENT_GID;
+#endif
+#ifdef CONFIG_GRKERNSEC_SOCKET_SERVER
+	grsec_enable_socket_server = 1;
+	grsec_socket_server_gid = CONFIG_GRKERNSEC_SOCKET_SERVER_GID;
+#endif
+#endif
+
+	return;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/grsec_ipc.c linux-2.4.27-leo/grsecurity/grsec_ipc.c
--- linux-2.4.27/grsecurity/grsec_ipc.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/grsec_ipc.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,81 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/ipc.h>
+#include <linux/grsecurity.h>
+#include <linux/grinternal.h>
+
+void
+gr_log_msgget(const int ret, const int msgflg)
+{
+#ifdef CONFIG_GRKERNSEC_AUDIT_IPC
+	if (((grsec_enable_group && in_group_p(grsec_audit_gid) &&
+	      grsec_enable_audit_ipc) || (grsec_enable_audit_ipc &&
+					  !grsec_enable_group)) && (ret >= 0)
+	    && (msgflg & IPC_CREAT))
+		security_audit(GR_MSGQ_AUDIT_MSG, DEFAULTSECARGS);
+#endif
+	return;
+}
+
+void
+gr_log_msgrm(const uid_t uid, const uid_t cuid)
+{
+#ifdef CONFIG_GRKERNSEC_AUDIT_IPC
+	if ((grsec_enable_group && in_group_p(grsec_audit_gid) &&
+	     grsec_enable_audit_ipc) ||
+	    (grsec_enable_audit_ipc && !grsec_enable_group))
+		security_audit(GR_MSGQR_AUDIT_MSG, uid, cuid, DEFAULTSECARGS);
+#endif
+	return;
+}
+
+void
+gr_log_semget(const int err, const int semflg)
+{
+#ifdef CONFIG_GRKERNSEC_AUDIT_IPC
+	if (((grsec_enable_group && in_group_p(grsec_audit_gid) &&
+	      grsec_enable_audit_ipc) || (grsec_enable_audit_ipc &&
+					  !grsec_enable_group)) && (err >= 0)
+	    && (semflg & IPC_CREAT))
+		security_audit(GR_SEM_AUDIT_MSG, DEFAULTSECARGS);
+#endif
+	return;
+}
+
+void
+gr_log_semrm(const uid_t uid, const uid_t cuid)
+{
+#ifdef CONFIG_GRKERNSEC_AUDIT_IPC
+	if ((grsec_enable_group && in_group_p(grsec_audit_gid) &&
+	     grsec_enable_audit_ipc) ||
+	    (grsec_enable_audit_ipc && !grsec_enable_group))
+		security_audit(GR_SEMR_AUDIT_MSG, uid, cuid, DEFAULTSECARGS);
+#endif
+	return;
+}
+
+void
+gr_log_shmget(const int err, const int shmflg, const size_t size)
+{
+#ifdef CONFIG_GRKERNSEC_AUDIT_IPC
+	if (((grsec_enable_group && in_group_p(grsec_audit_gid) &&
+	      grsec_enable_audit_ipc) || (grsec_enable_audit_ipc &&
+					  !grsec_enable_group)) && (err >= 0)
+	    && (shmflg & IPC_CREAT))
+		security_audit(GR_SHM_AUDIT_MSG, size, DEFAULTSECARGS);
+#endif
+	return;
+}
+
+void
+gr_log_shmrm(const uid_t uid, const uid_t cuid)
+{
+#ifdef CONFIG_GRKERNSEC_AUDIT_IPC
+	if ((grsec_enable_group && in_group_p(grsec_audit_gid) &&
+	     grsec_enable_audit_ipc) ||
+	    (grsec_enable_audit_ipc && !grsec_enable_group))
+		security_audit(GR_SHMR_AUDIT_MSG, uid, cuid, DEFAULTSECARGS);
+#endif
+	return;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/grsec_link.c linux-2.4.27-leo/grsecurity/grsec_link.c
--- linux-2.4.27/grsecurity/grsec_link.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/grsec_link.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,41 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/grinternal.h>
+
+int
+gr_handle_follow_link(const struct inode *parent,
+		      const struct inode *inode,
+		      const struct dentry *dentry, const struct vfsmount *mnt)
+{
+#ifdef CONFIG_GRKERNSEC_LINK
+	if (grsec_enable_link && S_ISLNK(inode->i_mode) &&
+	    (parent->i_mode & S_ISVTX) && (parent->i_uid != inode->i_uid) &&
+	    (parent->i_mode & S_IWOTH) && (current->fsuid != inode->i_uid)) {
+		security_alert(GR_SYMLINK_MSG, gr_to_filename(dentry, mnt),
+			       inode->i_uid, inode->i_gid, DEFAULTSECARGS);
+		return -EACCES;
+	}
+#endif
+	return 0;
+}
+
+int
+gr_handle_hardlink(const struct dentry *dentry,
+		   const struct vfsmount *mnt,
+		   struct inode *inode, const int mode, const char *to)
+{
+#ifdef CONFIG_GRKERNSEC_LINK
+	if (grsec_enable_link && current->fsuid != inode->i_uid &&
+	    (!S_ISREG(mode) || (mode & S_ISUID) ||
+	     ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) ||
+	     (permission(inode, MAY_READ | MAY_WRITE))) &&
+	    !capable(CAP_FOWNER) && current->uid) {
+		security_alert(GR_HARDLINK_MSG, gr_to_filename(dentry, mnt),
+			       inode->i_uid, inode->i_gid, to, DEFAULTSECARGS);
+		return -EPERM;
+	}
+#endif
+	return 0;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/grsec_mem.c linux-2.4.27-leo/grsecurity/grsec_mem.c
--- linux-2.4.27/grsecurity/grsec_mem.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/grsec_mem.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,61 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/grinternal.h>
+
+void
+gr_handle_ioperm(void)
+{
+	security_alert(GR_IOPERM_MSG, DEFAULTSECARGS);
+	return;
+}
+
+void
+gr_handle_iopl(void)
+{
+	security_alert(GR_IOPL_MSG, DEFAULTSECARGS);
+	return;
+}
+
+void
+gr_handle_mem_write(void)
+{
+	security_alert(GR_MEM_WRITE_MSG, DEFAULTSECARGS);
+	return;
+}
+
+void
+gr_handle_kmem_write(void)
+{
+	security_alert(GR_KMEM_MSG, DEFAULTSECARGS);
+	return;
+}
+
+void
+gr_handle_open_port(void)
+{
+	security_alert(GR_PORT_OPEN_MSG, DEFAULTSECARGS);
+	return;
+}
+
+int
+gr_handle_mem_mmap(const unsigned long offset, struct vm_area_struct *vma)
+{
+	if (offset + vma->vm_end - vma->vm_start <= offset) {
+		security_alert(GR_MEM_MMAP_MSG, DEFAULTSECARGS);
+		return -EPERM;
+	}
+
+	if (offset < __pa(high_memory) && (vma->vm_flags & VM_WRITE)
+#ifdef CONFIG_X86
+ 	    && !(offset == 0xf0000 && ((vma->vm_end - vma->vm_start) <= 0x10000))
+	    && !(offset == 0xa0000 && ((vma->vm_end - vma->vm_start) <= 0x20000))
+#endif
+	   ) {
+		security_alert(GR_MEM_MMAP_MSG, DEFAULTSECARGS);
+		return -EPERM;
+	} else if (offset < __pa(high_memory))
+		vma->vm_flags &= ~VM_MAYWRITE;
+
+	return 0;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/grsec_mount.c linux-2.4.27-leo/grsecurity/grsec_mount.c
--- linux-2.4.27/grsecurity/grsec_mount.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/grsec_mount.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,34 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/grsecurity.h>
+#include <linux/grinternal.h>
+
+void
+gr_log_remount(const char *devname, const int retval)
+{
+#ifdef CONFIG_GRKERNSEC_AUDIT_MOUNT
+	if (grsec_enable_mount && (retval >= 0))
+		security_audit(GR_REMOUNT_AUDIT_MSG, devname ? devname : "none", DEFAULTSECARGS);
+#endif
+	return;
+}
+
+void
+gr_log_unmount(const char *devname, const int retval)
+{
+#ifdef CONFIG_GRKERNSEC_AUDIT_MOUNT
+	if (grsec_enable_mount && (retval >= 0))
+		security_audit(GR_UNMOUNT_AUDIT_MSG, devname ? devname : "none", DEFAULTSECARGS);
+#endif
+	return;
+}
+
+void
+gr_log_mount(const char *from, const char *to, const int retval)
+{
+#ifdef CONFIG_GRKERNSEC_AUDIT_MOUNT
+	if (grsec_enable_mount && (retval >= 0))
+		security_audit(GR_MOUNT_AUDIT_MSG, from, to, DEFAULTSECARGS);
+#endif
+	return;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/grsec_rand.c linux-2.4.27-leo/grsecurity/grsec_rand.c
--- linux-2.4.27/grsecurity/grsec_rand.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/grsec_rand.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,36 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/grsecurity.h>
+#include <linux/grinternal.h>
+
+extern int last_pid;
+
+int
+gr_random_pid(spinlock_t * pid_lock)
+{
+#ifdef CONFIG_GRKERNSEC_RANDPID
+	struct task_struct *p;
+	int pid;
+
+	if (grsec_enable_randpid && current->fs->root) {
+		read_lock(&tasklist_lock);
+		spin_lock(pid_lock);
+
+	      repeater:
+
+		pid = 1 + (get_random_long() % PID_MAX);
+
+		for_each_task(p) {
+			if (p->pid == pid || p->pgrp == pid ||
+			    p->tgid == pid || p->session == pid)
+				goto repeater;
+		}
+		last_pid = pid;
+		spin_unlock(pid_lock);
+		read_unlock(&tasklist_lock);
+		return pid;
+	}
+#endif
+	return 0;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/grsec_sig.c linux-2.4.27-leo/grsecurity/grsec_sig.c
--- linux-2.4.27/grsecurity/grsec_sig.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/grsec_sig.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,72 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/grinternal.h>
+
+void
+gr_log_signal(const int sig, const struct task_struct *t)
+{
+#ifdef CONFIG_GRKERNSEC_SIGNAL
+	if (grsec_enable_signal && ((sig == SIGSEGV) || (sig == SIGILL) ||
+				    (sig == SIGABRT) || (sig == SIGBUS))) {
+		if (t->pid == current->pid) {
+			security_alert_good(GR_UNISIGLOG_MSG, sig,
+					    DEFAULTSECARGS);
+		} else {
+			security_alert_good(GR_DUALSIGLOG_MSG, sig,
+					    gr_task_fullpath0(t), t->comm,
+					    t->pid, t->uid, t->euid, t->gid,
+					    t->egid, gr_parent_task_fullpath0(t),
+					    t->p_pptr->comm,
+					    t->p_pptr->pid, t->p_pptr->uid,
+					    t->p_pptr->euid, t->p_pptr->gid,
+					    t->p_pptr->egid, DEFAULTSECARGS);
+		}
+	}
+#endif
+	return;
+}
+
+int
+gr_handle_signal(const struct task_struct *p, const int sig)
+{
+#ifdef CONFIG_GRKERNSEC
+	if (current->pid > 1 && sig != SIGCHLD && gr_check_protected_task(p)) {
+		security_alert(GR_SIG_ACL_MSG, sig, gr_task_fullpath0(p),
+			       p->comm, p->pid, p->uid,
+			       p->euid, p->gid, p->egid,
+			       gr_parent_task_fullpath0(p), p->p_pptr->comm,
+			       p->p_pptr->pid, p->p_pptr->uid,
+			       p->p_pptr->euid, p->p_pptr->gid,
+			       p->p_pptr->egid, DEFAULTSECARGS);
+		return -EPERM;
+	} else if (gr_pid_is_chrooted((struct task_struct *)p)) {
+		return -EPERM;
+	}
+#endif
+	return 0;
+}
+
+void gr_handle_brute_attach(struct task_struct *p)
+{
+#ifdef CONFIG_GRKERNSEC_BRUTE
+	read_lock(&tasklist_lock);
+	read_lock(&grsec_exec_file_lock);
+	if (p->p_pptr && p->p_pptr->exec_file == p->exec_file)
+		p->p_pptr->brute = 1;
+	read_unlock(&grsec_exec_file_lock);
+	read_unlock(&tasklist_lock);
+#endif
+	return;
+}
+
+void gr_handle_brute_check(void)
+{
+#ifdef CONFIG_GRKERNSEC_BRUTE
+	if (current->brute) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(30 * HZ);
+	}	
+#endif
+	return;
+}
+
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/grsec_sock.c linux-2.4.27-leo/grsecurity/grsec_sock.c
--- linux-2.4.27/grsecurity/grsec_sock.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/grsec_sock.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,202 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/file.h>
+#include <linux/net.h>
+#include <net/sock.h>
+#include <linux/grsecurity.h>
+#include <linux/grinternal.h>
+#include <linux/gracl.h>
+
+#ifdef CONFIG_GRKERNSEC
+struct task_struct **gr_conn_table;
+const unsigned int gr_conn_table_size = 65521;
+struct task_struct *deleted_conn = (struct task_struct *)~0;
+spinlock_t gr_conn_table_lock = SPIN_LOCK_UNLOCKED;
+
+extern __inline__ const char * gr_socktype_to_name(unsigned char type);
+extern __inline__ const char * gr_proto_to_name(unsigned char proto);
+
+static __inline__ int 
+conn_hash(__u32 saddr, __u32 daddr, __u16 sport, __u16 dport, unsigned int size)
+{
+	return ((daddr + saddr + (sport << 8) + (dport << 16)) % size);
+}
+
+static __inline__ int
+conn_match(const struct task_struct *task, __u32 saddr, __u32 daddr, 
+	   __u16 sport, __u16 dport)
+{
+	if (unlikely(task != deleted_conn && task->gr_saddr == saddr && 
+		     task->gr_daddr == daddr && task->gr_sport == sport &&
+		     task->gr_dport == dport))
+		return 1;
+	else
+		return 0;
+}
+
+void gr_add_to_task_ip_table(struct task_struct *task)
+{
+	unsigned int index;
+
+	if (unlikely(gr_conn_table == NULL))
+		return;
+
+	index = conn_hash(task->gr_saddr, task->gr_daddr,
+			  task->gr_sport, task->gr_dport, 
+			  gr_conn_table_size);
+
+	spin_lock(&gr_conn_table_lock);
+
+	while (gr_conn_table[index] && gr_conn_table[index] != deleted_conn) {
+		index = (index + 1) % gr_conn_table_size;
+	}
+
+	gr_conn_table[index] = task;
+
+	spin_unlock(&gr_conn_table_lock);
+
+	return;
+}
+
+void gr_del_task_from_ip_table_nolock(struct task_struct *task)
+{
+	unsigned int index;
+
+	if (unlikely(gr_conn_table == NULL))
+		return;
+
+	index = conn_hash(task->gr_saddr, task->gr_daddr, 
+			  task->gr_sport, task->gr_dport, 
+			  gr_conn_table_size);
+
+	while (gr_conn_table[index] && !conn_match(gr_conn_table[index], 
+		task->gr_saddr, task->gr_daddr, task->gr_sport, 
+		task->gr_dport)) {
+		index = (index + 1) % gr_conn_table_size;
+	}
+
+	if (gr_conn_table[index]) {
+		if (gr_conn_table[(index + 1) % gr_conn_table_size])
+			gr_conn_table[index] = deleted_conn;
+		else
+			gr_conn_table[index] = NULL;
+	}
+
+	return;
+}
+
+struct task_struct * gr_lookup_task_ip_table(__u32 saddr, __u32 daddr,
+					     __u16 sport, __u16 dport)
+{
+	unsigned int index;
+
+	if (unlikely(gr_conn_table == NULL))
+		return NULL;
+
+	index = conn_hash(saddr, daddr, sport, dport, gr_conn_table_size);
+
+	while (gr_conn_table[index] && !conn_match(gr_conn_table[index], 
+		saddr, daddr, sport, dport)) {
+		index = (index + 1) % gr_conn_table_size;
+	}
+
+	return gr_conn_table[index];
+}
+
+#endif
+
+void gr_del_task_from_ip_table(struct task_struct *task)
+{
+#ifdef CONFIG_GRKERNSEC
+	spin_lock(&gr_conn_table_lock);
+	gr_del_task_from_ip_table_nolock(task);
+	spin_unlock(&gr_conn_table_lock);
+#endif
+	return;
+}
+
+void
+gr_attach_curr_ip(const struct sock *sk)
+{
+#ifdef CONFIG_GRKERNSEC
+	struct task_struct *p;
+
+	if (unlikely(sk->protocol != IPPROTO_TCP))
+		return;
+
+	spin_lock(&gr_conn_table_lock);
+	p = gr_lookup_task_ip_table(sk->daddr, sk->rcv_saddr,
+				    sk->dport, sk->sport);
+	if (unlikely(p != NULL)) {
+		current->curr_ip = p->curr_ip;
+		current->used_accept = 1;
+		gr_del_task_from_ip_table_nolock(p);
+		spin_unlock(&gr_conn_table_lock);
+		return;
+	}
+	spin_unlock(&gr_conn_table_lock);
+
+	current->curr_ip = sk->daddr;
+	current->used_accept = 1;
+#endif
+	return;
+}
+
+int
+gr_handle_sock_all(const int family, const int type, const int protocol)
+{
+#ifdef CONFIG_GRKERNSEC_SOCKET_ALL
+	if (grsec_enable_socket_all && in_group_p(grsec_socket_all_gid) &&
+	    (family != AF_UNIX) && (family != AF_LOCAL) && (type < SOCK_MAX)) {
+		security_alert(GR_SOCK2_MSG, family, gr_socktype_to_name(type), gr_proto_to_name(protocol),
+			       DEFAULTSECARGS);
+		return -EACCES;
+	}
+#endif
+	return 0;
+}
+
+int
+gr_handle_sock_server(const struct sockaddr *sck)
+{
+#ifdef CONFIG_GRKERNSEC_SOCKET_SERVER
+	if (grsec_enable_socket_server &&
+	    in_group_p(grsec_socket_server_gid) &&
+	    sck && (sck->sa_family != AF_UNIX) &&
+	    (sck->sa_family != AF_LOCAL)) {
+		security_alert(GR_BIND_MSG, DEFAULTSECARGS);
+		return -EACCES;
+	}
+#endif
+	return 0;
+}
+
+int
+gr_handle_sock_client(const struct sockaddr *sck)
+{
+#ifdef CONFIG_GRKERNSEC_SOCKET_CLIENT
+	if (grsec_enable_socket_client && in_group_p(grsec_socket_client_gid) &&
+	    sck && (sck->sa_family != AF_UNIX) &&
+	    (sck->sa_family != AF_LOCAL)) {
+		security_alert(GR_CONNECT_MSG, DEFAULTSECARGS);
+		return -EACCES;
+	}
+#endif
+	return 0;
+}
+
+__u32
+gr_cap_rtnetlink(void)
+{
+#ifdef CONFIG_GRKERNSEC
+	if (!gr_acl_is_enabled())
+		return current->cap_effective;
+	else if (cap_raised(current->cap_effective, CAP_NET_ADMIN) &&
+		 gr_task_is_capable(current, CAP_NET_ADMIN))
+		return current->cap_effective;
+	else
+		return 0;
+#else
+	return current->cap_effective;
+#endif
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/grsec_sysctl.c linux-2.4.27-leo/grsecurity/grsec_sysctl.c
--- linux-2.4.27/grsecurity/grsec_sysctl.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/grsec_sysctl.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,16 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/sysctl.h>
+#include <linux/grinternal.h>
+
+int
+gr_handle_sysctl_mod(const char *dirname, const char *name, const int op)
+{
+#ifdef CONFIG_GRKERNSEC_SYSCTL
+	if (!strcmp(dirname, "grsecurity") && grsec_lock && (op & 002)) {
+		security_alert(GR_SYSCTL_MSG, name, DEFAULTSECARGS);
+		return -EACCES;
+	}
+#endif
+	return 0;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/grsec_textrel.c linux-2.4.27-leo/grsecurity/grsec_textrel.c
--- linux-2.4.27/grsecurity/grsec_textrel.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/grsec_textrel.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,19 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/file.h>
+#include <linux/grinternal.h>
+#include <linux/grsecurity.h>
+
+void
+gr_log_textrel(struct vm_area_struct * vma)
+{
+#ifdef CONFIG_GRKERNSEC_AUDIT_TEXTREL
+	if (grsec_enable_audit_textrel)
+		security_audit(GR_TEXTREL_AUDIT_MSG, vma->vm_file ? 
+				gr_to_filename(vma->vm_file->f_dentry, vma->vm_file->f_vfsmnt)
+				: "<anonymous mapping>", vma->vm_start, 
+				vma->vm_pgoff, DEFAULTSECARGS);
+#endif
+	return;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/grsec_time.c linux-2.4.27-leo/grsecurity/grsec_time.c
--- linux-2.4.27/grsecurity/grsec_time.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/grsec_time.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,13 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/grinternal.h>
+
+void
+gr_log_timechange(void)
+{
+#ifdef CONFIG_GRKERNSEC_TIME
+	if (grsec_enable_time)
+		security_alert_good(GR_TIME_MSG, DEFAULTSECARGS);
+#endif
+	return;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/grsec_tpe.c linux-2.4.27-leo/grsecurity/grsec_tpe.c
--- linux-2.4.27/grsecurity/grsec_tpe.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/grsec_tpe.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,35 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/grinternal.h>
+
+extern int gr_acl_tpe_check(void);
+
+int
+gr_tpe_allow(const struct file *file)
+{
+#ifdef CONFIG_GRKERNSEC
+	struct inode *inode = file->f_dentry->d_parent->d_inode;
+
+	if (current->uid && ((grsec_enable_tpe && in_group_p(grsec_tpe_gid)) || gr_acl_tpe_check()) &&
+	    (inode->i_uid || (!inode->i_uid && ((inode->i_mode & S_IWGRP) ||
+						(inode->i_mode & S_IWOTH))))) {
+		security_alert(GR_EXEC_TPE_MSG,
+			       gr_to_filename(file->f_dentry, file->f_vfsmnt),
+			       DEFAULTSECARGS);
+		return 0;
+	}
+#ifdef CONFIG_GRKERNSEC_TPE_ALL
+	if (current->uid && grsec_enable_tpe && grsec_enable_tpe_all &&
+	    ((inode->i_uid && (inode->i_uid != current->uid)) ||
+	     (inode->i_mode & S_IWGRP) || (inode->i_mode & S_IWOTH))) {
+		security_alert(GR_EXEC_TPE_MSG,
+			       gr_to_filename(file->f_dentry, file->f_vfsmnt),
+			       DEFAULTSECARGS);
+		return 0;
+	}
+#endif
+#endif
+	return 1;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/grsum.c linux-2.4.27-leo/grsecurity/grsum.c
--- linux-2.4.27/grsecurity/grsum.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/grsum.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,59 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <asm/scatterlist.h>
+#include <linux/crypto.h>
+#include <linux/gracl.h>
+
+
+#if !defined(CONFIG_CRYPTO) || defined(CONFIG_CRYPTO_MODULE) || !defined(CONFIG_CRYPTO_SHA256) || defined(CONFIG_CRYPTO_SHA256_MODULE)
+#error "crypto and sha256 must be built into the kernel"
+#endif
+
+int
+chkpw(struct gr_arg *entry, unsigned char *salt, unsigned char *sum)
+{
+	char *p;
+	struct crypto_tfm *tfm;
+	unsigned char temp_sum[GR_SHA_LEN];
+	struct scatterlist sg[2];
+	volatile int retval = 0;
+	volatile int dummy = 0;
+	unsigned int i;
+
+	tfm = crypto_alloc_tfm("sha256", 0);
+	if (tfm == NULL) {
+		/* should never happen, since sha256 should be built in */
+		return 1;
+	}
+
+	crypto_digest_init(tfm);
+
+	p = salt;
+	sg[0].page = virt_to_page(p);
+	sg[0].offset = ((long) p & ~PAGE_MASK);
+	sg[0].length = GR_SALT_LEN;
+	
+	crypto_digest_update(tfm, sg, 1);
+
+	p = entry->pw;
+	sg[0].page = virt_to_page(p);
+	sg[0].offset = ((long) p & ~PAGE_MASK);
+	sg[0].length = strlen(entry->pw);
+
+	crypto_digest_update(tfm, sg, 1);
+
+	crypto_digest_final(tfm, temp_sum);
+
+	memset(entry->pw, 0, GR_PW_LEN);
+
+	for (i = 0; i < GR_SHA_LEN; i++)
+		if (sum[i] != temp_sum[i])
+			retval = 1;
+		else
+			dummy = 1;	// waste a cycle
+
+	crypto_free_tfm(tfm);
+
+	return retval;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/Makefile linux-2.4.27-leo/grsecurity/Makefile
--- linux-2.4.27/grsecurity/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/Makefile	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,24 @@
+# grsecurity's ACL system was originally written in 2001 by Michael Dalton
+# during 2001, 2002, and 2003 it has been completely redesigned by
+# Brad Spengler
+#
+# All code in this directory and various hooks inserted throughout the kernel
+# are copyright Brad Spengler, and released under the GPL, unless otherwise
+# noted (as in obsd_rand.c)
+
+O_TARGET := grsec.o
+
+obj-y = grsec_chdir.o grsec_chroot.o grsec_exec.o grsec_fifo.o grsec_fork.o \
+	grsec_mount.o grsec_rand.o grsec_sig.o grsec_sock.o grsec_sysctl.o \
+	grsec_time.o grsec_tpe.o grsec_ipc.o grsec_link.o
+
+ifeq ($(CONFIG_GRKERNSEC),y)
+obj-y += grsec_init.o grsum.o gracl.o gracl_ip.o gracl_segv.o obsd_rand.o \
+	gracl_cap.o gracl_alloc.o gracl_shm.o grsec_mem.o gracl_fs.o \
+	gracl_learn.o grsec_textrel.o
+obj-$(CONFIG_GRKERNSEC_RESLOG) += gracl_res.o
+else
+obj-y += grsec_disabled.o
+endif
+
+include $(TOPDIR)/Rules.make
diff -urN --exclude-from=diff-exclude linux-2.4.27/grsecurity/obsd_rand.c linux-2.4.27-leo/grsecurity/obsd_rand.c
--- linux-2.4.27/grsecurity/obsd_rand.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/grsecurity/obsd_rand.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,185 @@
+
+/*
+ * Copyright (c) 1996, 1997, 2000-2002 Michael Shalayeff.
+ * 
+ * Version 1.89, last modified 19-Sep-99
+ *    
+ * Copyright Theodore Ts'o, 1994, 1995, 1996, 1997, 1998, 1999.
+ * All rights reserved.
+ *
+ * Copyright 1998 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ * Theo de Raadt <deraadt@openbsd.org> came up with the idea of using
+ * such a mathematical system to generate more random (yet non-repeating)
+ * ids to solve the resolver/named problem.  But Niels designed the
+ * actual system based on the constraints.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/smp_lock.h>
+#include <linux/random.h>
+#include <linux/grsecurity.h>
+
+#define RU_OUT 180
+#define RU_MAX 30000
+#define RU_GEN 2
+#define RU_N 32749
+#define RU_AGEN 7
+#define RU_M 31104
+#define PFAC_N 3
+const static __u16 pfacts[PFAC_N] = { 2, 3, 2729 };
+
+static __u16 ru_x;
+static __u16 ru_seed, ru_seed2;
+static __u16 ru_a, ru_b;
+static __u16 ru_g;
+static __u16 ru_counter = 0;
+static __u16 ru_msb = 0;
+static unsigned long ru_reseed = 0;
+static __u32 tmp;
+
+#define TCP_RNDISS_ROUNDS	15
+#define TCP_RNDISS_OUT		7200
+#define TCP_RNDISS_MAX		30000
+
+static __u8 tcp_rndiss_sbox[128];
+static __u16 tcp_rndiss_msb;
+static __u16 tcp_rndiss_cnt;
+static unsigned long tcp_rndiss_reseed;
+
+static __u16 pmod(__u16, __u16, __u16);
+static void ip_initid(void);
+__u16 ip_randomid(void);
+
+static __u16
+pmod(__u16 gen, __u16 exp, __u16 mod)
+{
+	__u16 s, t, u;
+
+	s = 1;
+	t = gen;
+	u = exp;
+
+	while (u) {
+		if (u & 1)
+			s = (s * t) % mod;
+		u >>= 1;
+		t = (t * t) % mod;
+	}
+	return (s);
+}
+
+static void
+ip_initid(void)
+{
+	__u16 j, i;
+	int noprime = 1;
+
+	ru_x = ((tmp = get_random_long()) & 0xFFFF) % RU_M;
+
+	ru_seed = (tmp >> 16) & 0x7FFF;
+	ru_seed2 = get_random_long() & 0x7FFF;
+
+	ru_b = ((tmp = get_random_long()) & 0xfffe) | 1;
+	ru_a = pmod(RU_AGEN, (tmp >> 16) & 0xfffe, RU_M);
+	while (ru_b % 3 == 0)
+		ru_b += 2;
+
+	j = (tmp = get_random_long()) % RU_N;
+	tmp = tmp >> 16;
+
+	while (noprime) {
+		for (i = 0; i < PFAC_N; i++)
+			if (j % pfacts[i] == 0)
+				break;
+
+		if (i >= PFAC_N)
+			noprime = 0;
+		else
+			j = (j + 1) % RU_N;
+	}
+
+	ru_g = pmod(RU_GEN, j, RU_N);
+	ru_counter = 0;
+
+	ru_reseed = xtime.tv_sec + RU_OUT;
+	ru_msb = ru_msb == 0x8000 ? 0 : 0x8000;
+}
+
+__u16
+ip_randomid(void)
+{
+	int i, n;
+
+	if (ru_counter >= RU_MAX || time_after(xtime.tv_sec, ru_reseed))
+		ip_initid();
+
+	if (!tmp)
+		tmp = get_random_long();
+
+	n = tmp & 0x3;
+	tmp = tmp >> 2;
+	if (ru_counter + n >= RU_MAX)
+		ip_initid();
+	for (i = 0; i <= n; i++)
+		ru_x = (ru_a * ru_x + ru_b) % RU_M;
+	ru_counter += i;
+
+	return ((ru_seed ^ pmod(ru_g, ru_seed2 ^ ru_x, RU_N)) | ru_msb);
+}
+
+__u16
+tcp_rndiss_encrypt(__u16 val)
+{
+	__u16 sum = 0, i;
+
+	for (i = 0; i < TCP_RNDISS_ROUNDS; i++) {
+		sum += 0x79b9;
+		val ^= ((__u16) tcp_rndiss_sbox[(val ^ sum) & 0x7f]) << 7;
+		val = ((val & 0xff) << 7) | (val >> 8);
+	}
+
+	return val;
+}
+
+static void
+tcp_rndiss_init(void)
+{
+	get_random_bytes(tcp_rndiss_sbox, sizeof (tcp_rndiss_sbox));
+	tcp_rndiss_reseed = xtime.tv_sec + TCP_RNDISS_OUT;
+	tcp_rndiss_msb = tcp_rndiss_msb == 0x8000 ? 0 : 0x8000;
+	tcp_rndiss_cnt = 0;
+}
+
+__u32
+ip_randomisn(void)
+{
+	if (tcp_rndiss_cnt >= TCP_RNDISS_MAX ||
+	    time_after(xtime.tv_sec, tcp_rndiss_reseed))
+		tcp_rndiss_init();
+
+	return (((tcp_rndiss_encrypt(tcp_rndiss_cnt++) |
+		  tcp_rndiss_msb) << 16) | (get_random_long() & 0x7fff));
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-alpha/a.out.h linux-2.4.27-leo/include/asm-alpha/a.out.h
--- linux-2.4.27/include/asm-alpha/a.out.h	2002-08-03 01:39:45.000000000 +0100
+++ linux-2.4.27-leo/include/asm-alpha/a.out.h	2004-09-17 03:19:58.000000000 +0100
@@ -98,7 +98,7 @@
 	set_personality (((BFPM->sh_bang || EX.ah.entry < 0x100000000 \
 			   ? ADDR_LIMIT_32BIT : 0) | PER_OSF4))
 
-#define STACK_TOP \
+#define __STACK_TOP \
   (current->personality & ADDR_LIMIT_32BIT ? 0x80000000 : 0x00120000000UL)
 
 #endif
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-alpha/elf.h linux-2.4.27-leo/include/asm-alpha/elf.h
--- linux-2.4.27/include/asm-alpha/elf.h	2004-02-20 14:11:45.000000000 +0000
+++ linux-2.4.27-leo/include/asm-alpha/elf.h	2004-09-17 03:19:58.000000000 +0100
@@ -41,6 +41,18 @@
 
 #define ELF_ET_DYN_BASE		(TASK_UNMAPPED_BASE + 0x1000000)
 
+#ifdef CONFIG_GRKERNSEC_PAX_ASLR
+#define PAX_ELF_ET_DYN_BASE(tsk)        ((tsk)->personality & ADDR_LIMIT_32BIT ? 0x10000 : 0x120000000UL)
+
+#define PAX_DELTA_MMAP_LSB(tsk)         PAGE_SHIFT
+#define PAX_DELTA_MMAP_LEN(tsk)         ((tsk)->personality & ADDR_LIMIT_32BIT ? 14 : 28)
+#define PAX_DELTA_EXEC_LSB(tsk)         PAGE_SHIFT
+#define PAX_DELTA_EXEC_LEN(tsk)         ((tsk)->personality & ADDR_LIMIT_32BIT ? 14 : 28)
+#define PAX_DELTA_STACK_LSB(tsk)        PAGE_SHIFT
+#define PAX_DELTA_STACK_LEN(tsk)        ((tsk)->personality & ADDR_LIMIT_32BIT ? 14 : 19)
+#endif
+
+
 /* $0 is set by ld.so to a pointer to a function which might be 
    registered using atexit.  This provides a mean for the dynamic
    linker to call DT_FINI functions for shared libraries that have
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-alpha/mman.h linux-2.4.27-leo/include/asm-alpha/mman.h
--- linux-2.4.27/include/asm-alpha/mman.h	2000-03-16 22:07:09.000000000 +0000
+++ linux-2.4.27-leo/include/asm-alpha/mman.h	2004-09-17 03:19:58.000000000 +0100
@@ -24,6 +24,10 @@
 #define MAP_LOCKED	0x8000		/* lock the mapping */
 #define MAP_NORESERVE	0x10000		/* don't check for reservations */
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+#define MAP_MIRROR	0x20000
+#endif
+
 #define MS_ASYNC	1		/* sync memory asynchronously */
 #define MS_SYNC		2		/* synchronous memory sync */
 #define MS_INVALIDATE	4		/* invalidate the caches */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-alpha/pgtable.h linux-2.4.27-leo/include/asm-alpha/pgtable.h
--- linux-2.4.27/include/asm-alpha/pgtable.h	2002-08-03 01:39:45.000000000 +0100
+++ linux-2.4.27-leo/include/asm-alpha/pgtable.h	2004-09-17 03:19:58.000000000 +0100
@@ -96,6 +96,17 @@
 #define PAGE_SHARED	__pgprot(_PAGE_VALID | __ACCESS_BITS)
 #define PAGE_COPY	__pgprot(_PAGE_VALID | __ACCESS_BITS | _PAGE_FOW)
 #define PAGE_READONLY	__pgprot(_PAGE_VALID | __ACCESS_BITS | _PAGE_FOW)
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+#define PAGE_SHARED_NOEXEC    __pgprot(_PAGE_VALID | __ACCESS_BITS | _PAGE_FOE)
+#define PAGE_COPY_NOEXEC      __pgprot(_PAGE_VALID | __ACCESS_BITS | _PAGE_FOW | _PAGE_FOE)
+#define PAGE_READONLY_NOEXEC  __pgprot(_PAGE_VALID | __ACCESS_BITS | _PAGE_FOW | _PAGE_FOE)
+#else
+#define PAGE_SHARED_NOEXEC    PAGE_SHARED
+#define PAGE_COPY_NOEXEC      PAGE_COPY
+#define PAGE_READONLY_NOEXEC  PAGE_READONLY
+#endif
+
 #define PAGE_KERNEL	__pgprot(_PAGE_VALID | _PAGE_ASM | _PAGE_KRE | _PAGE_KWE)
 
 #define _PAGE_NORMAL(x) __pgprot(_PAGE_VALID | __ACCESS_BITS | (x))
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-i386/a.out.h linux-2.4.27-leo/include/asm-i386/a.out.h
--- linux-2.4.27/include/asm-i386/a.out.h	1995-06-16 19:33:06.000000000 +0100
+++ linux-2.4.27-leo/include/asm-i386/a.out.h	2004-09-20 22:08:42.000000000 +0100
@@ -19,7 +19,11 @@
 
 #ifdef __KERNEL__
 
-#define STACK_TOP	TASK_SIZE
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+#define __STACK_TOP ((current->flags & PF_PAX_SEGMEXEC)?TASK_SIZE/2:TASK_SIZE)
+#else
+#define __STACK_TOP	TASK_SIZE
+#endif
 
 #endif
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-i386/desc.h linux-2.4.27-leo/include/asm-i386/desc.h
--- linux-2.4.27/include/asm-i386/desc.h	2004-02-20 14:11:45.000000000 +0000
+++ linux-2.4.27-leo/include/asm-i386/desc.h	2004-09-17 03:19:58.000000000 +0100
@@ -46,7 +46,8 @@
 };
 
 extern struct desc_struct gdt_table[];
-extern struct desc_struct *idt, *gdt;
+extern struct desc_struct gdt_table2[];
+extern struct desc_struct *idt, *gdt, *gdt2;
 
 struct Xgt_desc_struct {
 	unsigned short size;
@@ -55,6 +56,7 @@
 
 #define idt_descr (*(struct Xgt_desc_struct *)((char *)&idt - 2))
 #define gdt_descr (*(struct Xgt_desc_struct *)((char *)&gdt - 2))
+#define gdt_descr2 (*(struct Xgt_desc_struct *)((char *)&gdt2 - 2))
 
 #define load_TR(n) __asm__ __volatile__("ltr %%ax"::"a" (__TSS(n)<<3))
 
@@ -64,16 +66,20 @@
  * This is the ldt that every process will get unless we need
  * something other than this.
  */
-extern struct desc_struct default_ldt[];
+extern const struct desc_struct default_ldt[];
 extern void set_intr_gate(unsigned int irq, void * addr);
-extern void set_ldt_desc(unsigned int n, void *addr, unsigned int size);
-extern void set_tss_desc(unsigned int n, void *addr);
+extern void set_ldt_desc(unsigned int n, const void *addr, unsigned int size);
+extern void __set_ldt_desc(unsigned int n, const void *addr, unsigned int size);
+extern void set_tss_desc(unsigned int n, const void *addr);
 
 static inline void clear_LDT(void)
 {
-	int cpu = smp_processor_id();
+	int cpu;
+	preempt_disable();
+	cpu = smp_processor_id();
 	set_ldt_desc(cpu, &default_ldt[0], 5);
 	__load_LDT(cpu);
+	preempt_enable();
 }
 
 /*
@@ -82,7 +88,7 @@
 static inline void load_LDT (mm_context_t *pc)
 {
 	int cpu = smp_processor_id();
-	void *segments = pc->ldt;
+	const void *segments = pc->ldt;
 	int count = pc->size;
 
 	if (!count) {
@@ -94,6 +100,34 @@
 	__load_LDT(cpu);
 }
 
+static inline void _load_LDT (mm_context_t *pc)
+{
+	int cpu = smp_processor_id();
+	const void *segments = pc->ldt;
+	int count = pc->size;
+
+	if (!count) {
+		segments = &default_ldt[0];
+		count = 5;
+	}
+		
+	__set_ldt_desc(cpu, segments, count);
+	__load_LDT(cpu);
+}
+
+#define pax_open_kernel(flags, cr3)		\
+do {						\
+	local_irq_save(flags);			\
+	asm("movl %%cr3,%0":"=r" (cr3));	\
+	load_cr3(kernexec_pg_dir);		\
+} while(0)
+
+#define pax_close_kernel(flags, cr3)		\
+do {						\
+	asm("movl %0,%%cr3": :"r" (cr3));	\
+	local_irq_restore(flags);		\
+} while(0)
+
 #endif /* !__ASSEMBLY__ */
 
 #endif
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-i386/elf.h linux-2.4.27-leo/include/asm-i386/elf.h
--- linux-2.4.27/include/asm-i386/elf.h	2004-05-26 23:43:46.000000000 +0100
+++ linux-2.4.27-leo/include/asm-i386/elf.h	2004-09-20 22:10:16.000000000 +0100
@@ -55,7 +55,22 @@
    the loader.  We need to make sure that it is out of the way of the program
    that it will "exec", and that there is sufficient room for the brk.  */
 
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+#define ELF_ET_DYN_BASE		((current->flags & PF_PAX_SEGMEXEC)?SEGMEXEC_TASK_SIZE/3*2:TASK_SIZE/3*2)
+#else
 #define ELF_ET_DYN_BASE         (TASK_SIZE / 3 * 2)
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_ASLR
+#define PAX_ELF_ET_DYN_BASE(tsk)	0x08048000UL
+
+#define PAX_DELTA_MMAP_LSB(tsk)		PAGE_SHIFT
+#define PAX_DELTA_MMAP_LEN(tsk)		16
+#define PAX_DELTA_EXEC_LSB(tsk)		PAGE_SHIFT
+#define PAX_DELTA_EXEC_LEN(tsk)		16
+#define PAX_DELTA_STACK_LSB(tsk)	PAGE_SHIFT  
+#define PAX_DELTA_STACK_LEN(tsk)	((tsk)->flags & PF_PAX_SEGMEXEC ? 15 : 16)
+#endif
 
 /* Wow, the "main" arch needs arch dependent functions too.. :) */
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-i386/hardirq.h linux-2.4.27-leo/include/asm-i386/hardirq.h
--- linux-2.4.27/include/asm-i386/hardirq.h	2004-02-20 13:06:56.000000000 +0000
+++ linux-2.4.27-leo/include/asm-i386/hardirq.h	2004-09-20 22:08:42.000000000 +0100
@@ -19,12 +19,16 @@
 
 /*
  * Are we in an interrupt context? Either doing bottom half
- * or hardware interrupt processing?
+ * or hardware interrupt processing?  Note the preempt check,
+ * this is both a bugfix and an optimization.  If we are
+ * preemptible, we cannot be in an interrupt.
  */
-#define in_interrupt() ({ int __cpu = smp_processor_id(); \
-	(local_irq_count(__cpu) + local_bh_count(__cpu) != 0); })
+#define in_interrupt() (preempt_is_disabled() && \
+	({unsigned long __cpu = smp_processor_id(); \
+	(local_irq_count(__cpu) + local_bh_count(__cpu) != 0); }))
 
-#define in_irq() (local_irq_count(smp_processor_id()) != 0)
+#define in_irq() (preempt_is_disabled() && \
+        (local_irq_count(smp_processor_id()) != 0))
 
 #ifndef CONFIG_SMP
 
@@ -36,6 +40,8 @@
 
 #define synchronize_irq()	barrier()
 
+#define release_irqlock(cpu)	do { } while (0)
+
 #else
 
 #include <asm/atomic.h>
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-i386/highmem.h linux-2.4.27-leo/include/asm-i386/highmem.h
--- linux-2.4.27/include/asm-i386/highmem.h	2004-05-19 21:34:41.000000000 +0100
+++ linux-2.4.27-leo/include/asm-i386/highmem.h	2004-09-20 22:08:43.000000000 +0100
@@ -91,6 +91,7 @@
 	enum fixed_addresses idx;
 	unsigned long vaddr;
 
+	preempt_disable();
 	if (page < highmem_start_page)
 		return page_address(page);
 
@@ -112,8 +113,10 @@
 	unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
 	enum fixed_addresses idx = type + KM_TYPE_NR*smp_processor_id();
 
-	if (vaddr < FIXADDR_START) // FIXME
+	if (vaddr < FIXADDR_START) { // FIXME
+		preempt_enable();
 		return;
+	}
 
 	if (vaddr != __fix_to_virt(FIX_KMAP_BEGIN+idx))
 		out_of_line_bug();
@@ -125,6 +128,8 @@
 	pte_clear(kmap_pte-idx);
 	__flush_tlb_one(vaddr);
 #endif
+
+	preempt_enable();
 }
 
 #endif /* __KERNEL__ */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-i386/hw_irq.h linux-2.4.27-leo/include/asm-i386/hw_irq.h
--- linux-2.4.27/include/asm-i386/hw_irq.h	2004-02-20 13:06:53.000000000 +0000
+++ linux-2.4.27-leo/include/asm-i386/hw_irq.h	2004-09-20 22:08:42.000000000 +0100
@@ -95,6 +95,18 @@
 #define __STR(x) #x
 #define STR(x) __STR(x)
 
+#define GET_CURRENT \
+	"movl %esp, %ebx\n\t" \
+	"andl $-8192, %ebx\n\t"
+
+#ifdef CONFIG_PREEMPT
+#define BUMP_LOCK_COUNT \
+	GET_CURRENT \
+	"incl 4(%ebx)\n\t"
+#else
+#define BUMP_LOCK_COUNT
+#endif
+
 #define SAVE_ALL \
 	"cld\n\t" \
 	"pushl %es\n\t" \
@@ -108,15 +120,12 @@
 	"pushl %ebx\n\t" \
 	"movl $" STR(__KERNEL_DS) ",%edx\n\t" \
 	"movl %edx,%ds\n\t" \
-	"movl %edx,%es\n\t"
+	"movl %edx,%es\n\t" \
+	BUMP_LOCK_COUNT
 
 #define IRQ_NAME2(nr) nr##_interrupt(void)
 #define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr)
 
-#define GET_CURRENT \
-	"movl %esp, %ebx\n\t" \
-	"andl $-8192, %ebx\n\t"
-
 /*
  *	SMP has a few special interrupts for IPI messages
  */
@@ -128,6 +137,7 @@
 asmlinkage void x(void); \
 asmlinkage void call_##x(void); \
 __asm__( \
+"\n .text" \
 "\n"__ALIGN_STR"\n" \
 SYMBOL_NAME_STR(x) ":\n\t" \
 	"pushl $"#v"-256\n\t" \
@@ -141,6 +151,7 @@
 asmlinkage void x(struct pt_regs * regs); \
 asmlinkage void call_##x(void); \
 __asm__( \
+"\n .text" \
 "\n"__ALIGN_STR"\n" \
 SYMBOL_NAME_STR(x) ":\n\t" \
 	"pushl $"#v"-256\n\t" \
@@ -155,6 +166,7 @@
 #define BUILD_COMMON_IRQ() \
 asmlinkage void call_do_IRQ(void); \
 __asm__( \
+	"\n .text" \
 	"\n" __ALIGN_STR"\n" \
 	"common_interrupt:\n\t" \
 	SAVE_ALL \
@@ -175,6 +187,7 @@
 #define BUILD_IRQ(nr) \
 asmlinkage void IRQ_NAME(nr); \
 __asm__( \
+"\n .text" \
 "\n"__ALIGN_STR"\n" \
 SYMBOL_NAME_STR(IRQ) #nr "_interrupt:\n\t" \
 	"pushl $"#nr"-256\n\t" \
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-i386/i387.h linux-2.4.27-leo/include/asm-i386/i387.h
--- linux-2.4.27/include/asm-i386/i387.h	2004-09-17 02:38:46.000000000 +0100
+++ linux-2.4.27-leo/include/asm-i386/i387.h	2004-09-20 22:13:09.000000000 +0100
@@ -12,6 +12,7 @@
 #define __ASM_I386_I387_H
 
 #include <linux/sched.h>
+#include <linux/spinlock.h>
 #include <asm/processor.h>
 #include <asm/sigcontext.h>
 #include <asm/user.h>
@@ -24,7 +25,7 @@
 extern void restore_fpu( struct task_struct *tsk );
 
 extern void kernel_fpu_begin(void);
-#define kernel_fpu_end() stts()
+#define kernel_fpu_end() do { stts(); preempt_enable(); } while(0)
 
 
 #define unlazy_fpu( tsk ) do { \
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-i386/mman.h linux-2.4.27-leo/include/asm-i386/mman.h
--- linux-2.4.27/include/asm-i386/mman.h	2000-03-15 01:45:20.000000000 +0000
+++ linux-2.4.27-leo/include/asm-i386/mman.h	2004-09-20 22:08:42.000000000 +0100
@@ -18,6 +18,10 @@
 #define MAP_LOCKED	0x2000		/* pages are locked */
 #define MAP_NORESERVE	0x4000		/* don't check for reservations */
 
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+#define MAP_MIRROR	0x8000
+#endif
+
 #define MS_ASYNC	1		/* sync memory asynchronously */
 #define MS_INVALIDATE	2		/* invalidate the caches */
 #define MS_SYNC		4		/* synchronous memory sync */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-i386/page.h linux-2.4.27-leo/include/asm-i386/page.h
--- linux-2.4.27/include/asm-i386/page.h	2004-05-26 23:40:33.000000000 +0100
+++ linux-2.4.27-leo/include/asm-i386/page.h	2004-09-20 22:08:42.000000000 +0100
@@ -80,6 +80,12 @@
 
 #define __PAGE_OFFSET		(0xC0000000)
 
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+#define __KERNEL_TEXT_OFFSET	(0xC0400000)
+#else
+#define __KERNEL_TEXT_OFFSET	(0)
+#endif
+
 /*
  * This much address space is reserved for vmalloc() and iomap()
  * as well as fixmap mappings.
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-i386/pgalloc.h linux-2.4.27-leo/include/asm-i386/pgalloc.h
--- linux-2.4.27/include/asm-i386/pgalloc.h	2004-02-20 13:06:56.000000000 +0000
+++ linux-2.4.27-leo/include/asm-i386/pgalloc.h	2004-09-20 22:08:42.000000000 +0100
@@ -14,6 +14,9 @@
 #define pmd_populate(mm, pmd, pte) \
 		set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(pte)))
 
+#define pmd_populate_kernel(mm, pmd, pte) \
+		set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(pte)))
+
 /*
  * Allocate and free page tables.
  */
@@ -75,20 +78,26 @@
 {
 	unsigned long *ret;
 
+	preempt_disable();
 	if ((ret = pgd_quicklist) != NULL) {
 		pgd_quicklist = (unsigned long *)(*ret);
 		ret[0] = 0;
 		pgtable_cache_size--;
-	} else
+		preempt_enable();
+	} else {
+		preempt_enable();
 		ret = (unsigned long *)get_pgd_slow();
+	}
 	return (pgd_t *)ret;
 }
 
 static inline void free_pgd_fast(pgd_t *pgd)
 {
+	preempt_disable();
 	*(unsigned long *)pgd = (unsigned long) pgd_quicklist;
 	pgd_quicklist = (unsigned long *) pgd;
 	pgtable_cache_size++;
+	preempt_enable();
 }
 
 static inline void free_pgd_slow(pgd_t *pgd)
@@ -119,19 +128,23 @@
 {
 	unsigned long *ret;
 
+	preempt_disable();
 	if ((ret = (unsigned long *)pte_quicklist) != NULL) {
 		pte_quicklist = (unsigned long *)(*ret);
 		ret[0] = ret[1];
 		pgtable_cache_size--;
 	}
+	preempt_enable();
 	return (pte_t *)ret;
 }
 
 static inline void pte_free_fast(pte_t *pte)
 {
+	preempt_disable();
 	*(unsigned long *)pte = (unsigned long) pte_quicklist;
 	pte_quicklist = (unsigned long *) pte;
 	pgtable_cache_size++;
+	preempt_enable();
 }
 
 static __inline__ void pte_free_slow(pte_t *pte)
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-i386/pgtable.h linux-2.4.27-leo/include/asm-i386/pgtable.h
--- linux-2.4.27/include/asm-i386/pgtable.h	2004-05-26 23:40:33.000000000 +0100
+++ linux-2.4.27-leo/include/asm-i386/pgtable.h	2004-09-20 22:08:42.000000000 +0100
@@ -21,7 +21,6 @@
 #include <asm/bitops.h>
 #endif
 
-extern pgd_t swapper_pg_dir[1024];
 extern void paging_init(void);
 
 /* Caches aren't brain-dead on the intel. */
@@ -104,14 +103,11 @@
 extern unsigned long empty_zero_page[1024];
 #define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page))
 
-#endif /* !__ASSEMBLY__ */
-
 /*
  * The Linux x86 paging architecture is 'compile-time dual-mode', it
  * implements both the traditional 2-level x86 page tables and the
  * newer 3-level PAE-mode page tables.
  */
-#ifndef __ASSEMBLY__
 #if CONFIG_X86_PAE
 # include <asm/pgtable-3level.h>
 
@@ -129,8 +125,15 @@
 #define pgtable_cache_init()	do { } while (0)
 
 #endif
+
+extern pgd_t swapper_pg_dir[PTRS_PER_PTE];
+
+#ifdef CONFIG_GRKERNSEC_PAX_KERNEXEC
+extern pgd_t kernexec_pg_dir[PTRS_PER_PTE];
 #endif
 
+#endif /* !__ASSEMBLY__ */
+
 #define __beep() asm("movb $0x3,%al; outb %al,$0x61")
 
 #define PMD_SIZE	(1UL << PMD_SHIFT)
@@ -205,6 +208,16 @@
 #define PAGE_COPY	__pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
 #define PAGE_READONLY	__pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
 
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+#define PAGE_SHARED_NOEXEC  __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED)
+#define PAGE_COPY_NOEXEC  __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED)
+#define PAGE_READONLY_NOEXEC  __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED) 
+#else
+#define PAGE_SHARED_NOEXEC    PAGE_SHARED
+#define PAGE_COPY_NOEXEC      PAGE_COPY
+#define PAGE_READONLY_NOEXEC  PAGE_READONLY
+#endif
+
 #define __PAGE_KERNEL \
 	(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED)
 #define __PAGE_KERNEL_NOCACHE \
@@ -237,18 +250,18 @@
  * This is the closest we can get..
  */
 #define __P000	PAGE_NONE
-#define __P001	PAGE_READONLY
-#define __P010	PAGE_COPY
-#define __P011	PAGE_COPY
+#define __P001  PAGE_READONLY_NOEXEC
+#define __P010  PAGE_COPY_NOEXEC
+#define __P011  PAGE_COPY_NOEXEC
 #define __P100	PAGE_READONLY
 #define __P101	PAGE_READONLY
 #define __P110	PAGE_COPY
 #define __P111	PAGE_COPY
 
 #define __S000	PAGE_NONE
-#define __S001	PAGE_READONLY
-#define __S010	PAGE_SHARED
-#define __S011	PAGE_SHARED
+#define __S001  PAGE_READONLY_NOEXEC
+#define __S010  PAGE_SHARED_NOEXEC
+#define __S011  PAGE_SHARED_NOEXEC
 #define __S100	PAGE_READONLY
 #define __S101	PAGE_READONLY
 #define __S110	PAGE_SHARED
@@ -324,7 +337,7 @@
 ((unsigned long) __va(pmd_val(pmd) & PAGE_MASK))
 
 /* to find an entry in a page-table-directory. */
-#define pgd_index(address) ((address >> PGDIR_SHIFT) & (PTRS_PER_PGD-1))
+#define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1))
 
 #define __pgd_offset(address) pgd_index(address)
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-i386/processor.h linux-2.4.27-leo/include/asm-i386/processor.h
--- linux-2.4.27/include/asm-i386/processor.h	2004-05-26 23:40:33.000000000 +0100
+++ linux-2.4.27-leo/include/asm-i386/processor.h	2004-09-20 22:08:42.000000000 +0100
@@ -261,10 +261,19 @@
  */
 #define TASK_SIZE	(PAGE_OFFSET)
 
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+#define SEGMEXEC_TASK_SIZE	((PAGE_OFFSET) / 2)
+#endif
+
 /* This decides where the kernel will search for a free chunk of vm
  * space during mmap's.
  */
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+#define TASK_UNMAPPED_BASE	((current->flags & PF_PAX_SEGMEXEC)?SEGMEXEC_TASK_SIZE/3:TASK_SIZE/3)
+#else
 #define TASK_UNMAPPED_BASE	(TASK_SIZE / 3)
+#endif
 
 /*
  * Size of io_bitmap in longwords: 32 is ports 0-0x3ff.
@@ -442,8 +451,16 @@
 }
 
 unsigned long get_wchan(struct task_struct *p);
-#define KSTK_EIP(tsk)	(((unsigned long *)(4096+(unsigned long)(tsk)))[1019])
-#define KSTK_ESP(tsk)	(((unsigned long *)(4096+(unsigned long)(tsk)))[1022])
+
+#define task_pt_regs(task)						\
+({									\
+	struct pt_regs *__regs__;					\
+	__regs__ = (struct pt_regs *)((task)->thread.esp0);		\
+	__regs__ - 1;							\
+})
+
+#define KSTK_EIP(tsk)  (task_pt_regs(tsk)->eip)
+#define KSTK_ESP(tsk)  (task_pt_regs(tsk)->esp)
 
 #define THREAD_SIZE (2*PAGE_SIZE)
 #define alloc_task_struct() ((struct task_struct *) __get_free_pages(GFP_KERNEL,1))
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-i386/smplock.h linux-2.4.27-leo/include/asm-i386/smplock.h
--- linux-2.4.27/include/asm-i386/smplock.h	2004-02-20 13:06:56.000000000 +0000
+++ linux-2.4.27-leo/include/asm-i386/smplock.h	2004-09-20 22:08:42.000000000 +0100
@@ -14,7 +14,15 @@
 extern spinlock_cacheline_t kernel_flag_cacheline;  
 #define kernel_flag kernel_flag_cacheline.lock      
 
+#ifdef CONFIG_SMP
 #define kernel_locked()		spin_is_locked(&kernel_flag)
+#else
+#ifdef CONFIG_PREEMPT
+#define kernel_locked()		preempt_get_count()
+#else
+#define kernel_locked()		1
+#endif
+#endif
 
 /*
  * Release global kernel lock and global interrupt lock
@@ -46,6 +54,11 @@
  */
 static __inline__ void lock_kernel(void)
 {
+#ifdef CONFIG_PREEMPT
+	if (current->lock_depth == -1)
+		spin_lock(&kernel_flag);
+	++current->lock_depth;
+#else
 #if 1
 	if (!++current->lock_depth)
 		spin_lock(&kernel_flag);
@@ -58,6 +71,7 @@
 		:"=m" (__dummy_lock(&kernel_flag)),
 		 "=m" (current->lock_depth));
 #endif
+#endif
 }
 
 static __inline__ void unlock_kernel(void)
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-i386/softirq.h linux-2.4.27-leo/include/asm-i386/softirq.h
--- linux-2.4.27/include/asm-i386/softirq.h	2004-02-20 13:06:56.000000000 +0000
+++ linux-2.4.27-leo/include/asm-i386/softirq.h	2004-09-20 22:08:42.000000000 +0100
@@ -5,14 +5,15 @@
 #include <asm/hardirq.h>
 
 #define __cpu_bh_enable(cpu) \
-		do { barrier(); local_bh_count(cpu)--; } while (0)
+		do { barrier(); local_bh_count(cpu)--; preempt_enable(); } while (0)
 #define cpu_bh_disable(cpu) \
-		do { local_bh_count(cpu)++; barrier(); } while (0)
+		do { preempt_disable(); local_bh_count(cpu)++; barrier(); } while (0)
 
 #define local_bh_disable()	cpu_bh_disable(smp_processor_id())
 #define __local_bh_enable()	__cpu_bh_enable(smp_processor_id())
 
-#define in_softirq() (local_bh_count(smp_processor_id()) != 0)
+#define in_softirq() ( preempt_is_disabled() & \
+			(local_bh_count(smp_processor_id()) != 0))
 
 /*
  * NOTE: this assembly code assumes:
@@ -22,7 +23,7 @@
  * If you change the offsets in irq_stat then you have to
  * update this code as well.
  */
-#define local_bh_enable()						\
+#define _local_bh_enable()						\
 do {									\
 	unsigned int *ptr = &local_bh_count(smp_processor_id());	\
 									\
@@ -45,4 +46,6 @@
 		/* no registers clobbered */ );				\
 } while (0)
 
+#define local_bh_enable() do { _local_bh_enable(); preempt_enable(); } while (0)
+
 #endif	/* __ASM_SOFTIRQ_H */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-i386/spinlock.h linux-2.4.27-leo/include/asm-i386/spinlock.h
--- linux-2.4.27/include/asm-i386/spinlock.h	2004-02-20 13:06:53.000000000 +0000
+++ linux-2.4.27-leo/include/asm-i386/spinlock.h	2004-09-20 22:08:42.000000000 +0100
@@ -77,7 +77,7 @@
 		:"=m" (lock->lock) : : "memory"
 
 
-static inline void spin_unlock(spinlock_t *lock)
+static inline void _raw_spin_unlock(spinlock_t *lock)
 {
 #if SPINLOCK_DEBUG
 	if (lock->magic != SPINLOCK_MAGIC)
@@ -97,7 +97,7 @@
 		:"=q" (oldval), "=m" (lock->lock) \
 		:"0" (oldval) : "memory"
 
-static inline void spin_unlock(spinlock_t *lock)
+static inline void _raw_spin_unlock(spinlock_t *lock)
 {
 	char oldval = 1;
 #if SPINLOCK_DEBUG
@@ -113,7 +113,7 @@
 
 #endif
 
-static inline int spin_trylock(spinlock_t *lock)
+static inline int _raw_spin_trylock(spinlock_t *lock)
 {
 	char oldval;
 	__asm__ __volatile__(
@@ -123,7 +123,7 @@
 	return oldval > 0;
 }
 
-static inline void spin_lock(spinlock_t *lock)
+static inline void _raw_spin_lock(spinlock_t *lock)
 {
 #if SPINLOCK_DEBUG
 	__label__ here;
@@ -179,7 +179,7 @@
  */
 /* the spinlock helpers are in arch/i386/kernel/semaphore.c */
 
-static inline void read_lock(rwlock_t *rw)
+static inline void _raw_read_lock(rwlock_t *rw)
 {
 #if SPINLOCK_DEBUG
 	if (rw->magic != RWLOCK_MAGIC)
@@ -188,7 +188,7 @@
 	__build_read_lock(rw, "__read_lock_failed");
 }
 
-static inline void write_lock(rwlock_t *rw)
+static inline void _raw_write_lock(rwlock_t *rw)
 {
 #if SPINLOCK_DEBUG
 	if (rw->magic != RWLOCK_MAGIC)
@@ -197,10 +197,10 @@
 	__build_write_lock(rw, "__write_lock_failed");
 }
 
-#define read_unlock(rw)		asm volatile("lock ; incl %0" :"=m" ((rw)->lock) : : "memory")
-#define write_unlock(rw)	asm volatile("lock ; addl $" RW_LOCK_BIAS_STR ",%0":"=m" ((rw)->lock) : : "memory")
+#define _raw_read_unlock(rw)		asm volatile("lock ; incl %0" :"=m" ((rw)->lock) : : "memory")
+#define _raw_write_unlock(rw)	asm volatile("lock ; addl $" RW_LOCK_BIAS_STR ",%0":"=m" ((rw)->lock) : : "memory")
 
-static inline int write_trylock(rwlock_t *lock)
+static inline int _raw_write_trylock(rwlock_t *lock)
 {
 	atomic_t *count = (atomic_t *)lock;
 	if (atomic_sub_and_test(RW_LOCK_BIAS, count))
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-i386/system.h linux-2.4.27-leo/include/asm-i386/system.h
--- linux-2.4.27/include/asm-i386/system.h	2004-05-19 21:34:41.000000000 +0100
+++ linux-2.4.27-leo/include/asm-i386/system.h	2004-09-20 22:08:42.000000000 +0100
@@ -12,6 +12,8 @@
 struct task_struct;	/* one of the stranger aspects of C forward declarations.. */
 extern void FASTCALL(__switch_to(struct task_struct *prev, struct task_struct *next));
 
+void pax_switch_segments(struct task_struct *);
+
 #define prepare_to_switch()	do { } while(0)
 #define switch_to(prev,next,last) do {					\
 	asm volatile("pushl %%esi\n\t"					\
@@ -323,6 +325,13 @@
 #define __save_and_cli(x)	do { __save_flags(x); __cli(); } while(0);
 #define __save_and_sti(x)	do { __save_flags(x); __sti(); } while(0);
 
+#define irqs_disabled()			\
+({					\
+	unsigned long flags;		\
+	__save_flags(flags);		\
+	!(flags & (1<<9));		\
+})
+
 /* For spinlocks etc */
 #if 0
 #define local_irq_save(x)	__asm__ __volatile__("pushfl ; popl %0 ; cli":"=g" (x): /* no input */ :"memory")
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-mips/smplock.h linux-2.4.27-leo/include/asm-mips/smplock.h
--- linux-2.4.27/include/asm-mips/smplock.h	2004-02-20 14:11:46.000000000 +0000
+++ linux-2.4.27-leo/include/asm-mips/smplock.h	2004-09-17 03:19:36.000000000 +0100
@@ -8,12 +8,21 @@
 #ifndef __ASM_SMPLOCK_H
 #define __ASM_SMPLOCK_H
 
+#include <linux/config.h>
 #include <linux/interrupt.h>
 #include <linux/spinlock.h>
 
 extern spinlock_t kernel_flag;
 
+#ifdef CONFIG_SMP
 #define kernel_locked()		spin_is_locked(&kernel_flag)
+#else
+#ifdef CONFIG_PREEMPT
+#define kernel_locked()         preempt_get_count()
+#else
+#define kernel_locked()         1
+#endif
+#endif
 
 /*
  * Release global kernel lock and global interrupt lock
@@ -45,8 +54,14 @@
  */
 static __inline__ void lock_kernel(void)
 {
+#ifdef CONFIG_PREEMPT
+	if (current->lock_depth == -1)
+		spin_lock(&kernel_flag);
+	++current->lock_depth;
+#else
 	if (!++current->lock_depth)
 		spin_lock(&kernel_flag);
+#endif
 }
 
 static __inline__ void unlock_kernel(void)
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-mips/softirq.h linux-2.4.27-leo/include/asm-mips/softirq.h
--- linux-2.4.27/include/asm-mips/softirq.h	2002-11-28 23:53:15.000000000 +0000
+++ linux-2.4.27-leo/include/asm-mips/softirq.h	2004-09-17 03:19:36.000000000 +0100
@@ -15,6 +15,7 @@
 
 static inline void cpu_bh_disable(int cpu)
 {
+	preempt_disable();
 	local_bh_count(cpu)++;
 	barrier();
 }
@@ -23,6 +24,7 @@
 {
 	barrier();
 	local_bh_count(cpu)--;
+	preempt_enable();
 }
 
 
@@ -36,6 +38,7 @@
 	cpu = smp_processor_id();				\
 	if (!--local_bh_count(cpu) && softirq_pending(cpu))	\
 		do_softirq();					\
+	preempt_enable();                                       \
 } while (0)
 
 #define in_softirq() (local_bh_count(smp_processor_id()) != 0)
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-mips/system.h linux-2.4.27-leo/include/asm-mips/system.h
--- linux-2.4.27/include/asm-mips/system.h	2004-02-20 14:11:46.000000000 +0000
+++ linux-2.4.27-leo/include/asm-mips/system.h	2004-09-17 03:19:36.000000000 +0100
@@ -333,4 +333,18 @@
 #define die_if_kernel(msg, regs)					\
 	__die_if_kernel(msg, regs, __FILE__ ":", __FUNCTION__, __LINE__)
 
+extern __inline__ int intr_on(void)
+{
+	unsigned long flags;
+	save_flags(flags);
+	return flags & 1;
+}
+
+extern __inline__ int intr_off(void)
+{
+	return ! intr_on();
+}
+
+#define irqs_disabled()	intr_off()
+
 #endif /* _ASM_SYSTEM_H */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-parisc/a.out.h linux-2.4.27-leo/include/asm-parisc/a.out.h
--- linux-2.4.27/include/asm-parisc/a.out.h	2000-12-05 20:29:39.000000000 +0000
+++ linux-2.4.27-leo/include/asm-parisc/a.out.h	2004-09-17 03:19:58.000000000 +0100
@@ -22,7 +22,7 @@
 /* XXX: STACK_TOP actually should be STACK_BOTTOM for parisc.
  * prumpf */
 
-#define STACK_TOP	TASK_SIZE
+#define __STACK_TOP	TASK_SIZE
 
 #endif
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-parisc/elf.h linux-2.4.27-leo/include/asm-parisc/elf.h
--- linux-2.4.27/include/asm-parisc/elf.h	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.27-leo/include/asm-parisc/elf.h	2004-09-17 03:19:58.000000000 +0100
@@ -135,6 +135,17 @@
 
 #define ELF_ET_DYN_BASE         (TASK_UNMAPPED_BASE + 0x01000000)
 
+#ifdef CONFIG_GRKERNSEC_PAX_ASLR
+#define PAX_ELF_ET_DYN_BASE(tsk)        0x10000UL
+
+#define PAX_DELTA_MMAP_LSB(tsk)         PAGE_SHIFT
+#define PAX_DELTA_MMAP_LEN(tsk)         16
+#define PAX_DELTA_EXEC_LSB(tsk)         PAGE_SHIFT
+#define PAX_DELTA_EXEC_LEN(tsk)         16
+#define PAX_DELTA_STACK_LSB(tsk)        PAGE_SHIFT
+#define PAX_DELTA_STACK_LEN(tsk)        16
+#endif
+
 /* This yields a mask that user programs can use to figure out what
    instruction set this CPU supports.  This could be done in user space,
    but it's not easy, and we've already done it here.  */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-parisc/mman.h linux-2.4.27-leo/include/asm-parisc/mman.h
--- linux-2.4.27/include/asm-parisc/mman.h	2000-12-05 20:29:39.000000000 +0000
+++ linux-2.4.27-leo/include/asm-parisc/mman.h	2004-09-17 03:19:58.000000000 +0100
@@ -18,6 +18,10 @@
 #define MAP_NORESERVE	0x4000		/* don't check for reservations */
 #define MAP_GROWSDOWN	0x8000		/* stack-like segment */
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+#define MAP_MIRROR	0x0400
+#endif
+
 #define MS_SYNC		1		/* synchronous memory sync */
 #define MS_ASYNC	2		/* sync memory asynchronously */
 #define MS_INVALIDATE	4		/* invalidate the caches */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-parisc/pgtable.h linux-2.4.27-leo/include/asm-parisc/pgtable.h
--- linux-2.4.27/include/asm-parisc/pgtable.h	2003-06-13 15:51:38.000000000 +0100
+++ linux-2.4.27-leo/include/asm-parisc/pgtable.h	2004-09-17 03:19:58.000000000 +0100
@@ -167,6 +167,17 @@
 #define PAGE_EXECREAD   __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_READ | _PAGE_EXEC |_PAGE_ACCESSED)
 #define PAGE_COPY       PAGE_EXECREAD
 #define PAGE_RWX        __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_READ | _PAGE_WRITE | _PAGE_EXEC |_PAGE_ACCESSED)
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+#define PAGE_SHARED_NOEXEC    __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_READ | _PAGE_WRITE | _PAGE_ACCESSED)
+#define PAGE_COPY_NOEXEC      __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_READ | _PAGE_ACCESSED)
+#define PAGE_READONLY_NOEXEC  __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_READ | _PAGE_ACCESSED)
+#else
+#define PAGE_SHARED_NOEXEC    PAGE_SHARED
+#define PAGE_COPY_NOEXEC      PAGE_COPY
+#define PAGE_READONLY_NOEXEC  PAGE_READONLY
+#endif
+
 #define PAGE_KERNEL	__pgprot(_PAGE_KERNEL)
 #define PAGE_KERNEL_RO	__pgprot(_PAGE_PRESENT | _PAGE_EXEC | _PAGE_READ | _PAGE_DIRTY | _PAGE_ACCESSED)
 #define PAGE_KERNEL_UNC	__pgprot(_PAGE_KERNEL | _PAGE_NO_CACHE)
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-ppc/a.out.h linux-2.4.27-leo/include/asm-ppc/a.out.h
--- linux-2.4.27/include/asm-ppc/a.out.h	2003-08-25 12:44:44.000000000 +0100
+++ linux-2.4.27-leo/include/asm-ppc/a.out.h	2004-09-17 03:19:58.000000000 +0100
@@ -2,7 +2,7 @@
 #define __PPC_A_OUT_H__
 
 /* grabbed from the intel stuff  */
-#define STACK_TOP TASK_SIZE
+#define __STACK_TOP TASK_SIZE
 
 
 struct exec
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-ppc/dma.h linux-2.4.27-leo/include/asm-ppc/dma.h
--- linux-2.4.27/include/asm-ppc/dma.h	2003-06-13 15:51:38.000000000 +0100
+++ linux-2.4.27-leo/include/asm-ppc/dma.h	2004-09-17 03:19:36.000000000 +0100
@@ -11,6 +11,7 @@
 #include <linux/config.h>
 #include <asm/io.h>
 #include <linux/spinlock.h>
+#include <linux/sched.h>
 #include <asm/system.h>
 
 /*
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-ppc/elf.h linux-2.4.27-leo/include/asm-ppc/elf.h
--- linux-2.4.27/include/asm-ppc/elf.h	2003-06-13 15:51:38.000000000 +0100
+++ linux-2.4.27-leo/include/asm-ppc/elf.h	2004-09-17 03:19:58.000000000 +0100
@@ -46,6 +46,17 @@
 
 #define ELF_ET_DYN_BASE         (0x08000000)
 
+#ifdef CONFIG_GRKERNSEC_PAX_ASLR
+#define PAX_ELF_ET_DYN_BASE(tsk)	0x10000000UL
+
+#define PAX_DELTA_MMAP_LSB(tsk)		PAGE_SHIFT
+#define PAX_DELTA_MMAP_LEN(tsk)		15
+#define PAX_DELTA_EXEC_LSB(tsk)		PAGE_SHIFT
+#define PAX_DELTA_EXEC_LEN(tsk)		15
+#define PAX_DELTA_STACK_LSB(tsk)	PAGE_SHIFT
+#define PAX_DELTA_STACK_LEN(tsk)	15
+#endif
+
 #define USE_ELF_CORE_DUMP
 #define ELF_EXEC_PAGESIZE	4096
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-ppc/hardirq.h linux-2.4.27-leo/include/asm-ppc/hardirq.h
--- linux-2.4.27/include/asm-ppc/hardirq.h	2003-08-25 12:44:44.000000000 +0100
+++ linux-2.4.27-leo/include/asm-ppc/hardirq.h	2004-09-17 03:19:36.000000000 +0100
@@ -31,10 +31,12 @@
  * Are we in an interrupt context? Either doing bottom half
  * or hardware interrupt processing?
  */
-#define in_interrupt() ({ int __cpu = smp_processor_id(); \
-	(local_irq_count(__cpu) + local_bh_count(__cpu) != 0); })
+#define in_interrupt() (preempt_is_disabled() && \
+	({ unsigned long __cpu = smp_processor_id(); \
+	(local_irq_count(__cpu) + local_bh_count(__cpu) != 0); }))
 
-#define in_irq() (local_irq_count(smp_processor_id()) != 0)
+#define in_irq() (preempt_is_disabled() && \
+	(local_irq_count(smp_processor_id()) != 0))
 
 #ifndef CONFIG_SMP
 
@@ -45,6 +47,7 @@
 #define hardirq_exit(cpu)	(local_irq_count(cpu)--)
 
 #define synchronize_irq()	do { } while (0)
+#define release_irqlock(cpu)	do { } while (0)
 
 #else /* CONFIG_SMP */
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-ppc/highmem.h linux-2.4.27-leo/include/asm-ppc/highmem.h
--- linux-2.4.27/include/asm-ppc/highmem.h	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.27-leo/include/asm-ppc/highmem.h	2004-09-17 03:19:36.000000000 +0100
@@ -84,6 +84,7 @@
 	unsigned int idx;
 	unsigned long vaddr;
 
+	preempt_disable();
 	if (page < highmem_start_page)
 		return page_address(page);
 
@@ -105,8 +106,10 @@
 	unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
 	unsigned int idx = type + KM_TYPE_NR*smp_processor_id();
 
-	if (vaddr < KMAP_FIX_BEGIN) // FIXME
+	if (vaddr < KMAP_FIX_BEGIN) { // FIXME
+		preempt_enable();
 		return;
+	}
 
 	if (vaddr != KMAP_FIX_BEGIN + idx * PAGE_SIZE)
 		BUG();
@@ -118,6 +121,7 @@
 	pte_clear(kmap_pte+idx);
 	flush_tlb_page(0, vaddr);
 #endif
+	preempt_enable();
 }
 
 #endif /* __KERNEL__ */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-ppc/hw_irq.h linux-2.4.27-leo/include/asm-ppc/hw_irq.h
--- linux-2.4.27/include/asm-ppc/hw_irq.h	2003-06-13 15:51:38.000000000 +0100
+++ linux-2.4.27-leo/include/asm-ppc/hw_irq.h	2004-09-17 03:19:36.000000000 +0100
@@ -20,6 +20,12 @@
 #define __save_and_cli(flags) ({__save_flags(flags);__cli();})
 #define __save_and_sti(flags) ({__save_flags(flags);__sti();})
 
+#define mfmsr()		({unsigned int rval; \
+			asm volatile("mfmsr %0" : "=r" (rval)); rval;})
+#define mtmsr(v)	asm volatile("mtmsr %0" : : "r" (v))
+
+#define irqs_disabled()	((mfmsr() & MSR_EE) == 0)
+
 extern void do_lost_interrupts(unsigned long);
 
 #define mask_irq(irq) ({if (irq_desc[irq].handler && irq_desc[irq].handler->disable) irq_desc[irq].handler->disable(irq);})
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-ppc/mman.h linux-2.4.27-leo/include/asm-ppc/mman.h
--- linux-2.4.27/include/asm-ppc/mman.h	2003-06-13 15:51:38.000000000 +0100
+++ linux-2.4.27-leo/include/asm-ppc/mman.h	2004-09-17 03:19:58.000000000 +0100
@@ -19,6 +19,10 @@
 #define MAP_DENYWRITE	0x0800		/* ETXTBSY */
 #define MAP_EXECUTABLE	0x1000		/* mark it as an executable */
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+#define MAP_MIRROR	0x0200
+#endif
+
 #define MS_ASYNC	1		/* sync memory asynchronously */
 #define MS_INVALIDATE	2		/* invalidate the caches */
 #define MS_SYNC		4		/* synchronous memory sync */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-ppc/mmu_context.h linux-2.4.27-leo/include/asm-ppc/mmu_context.h
--- linux-2.4.27/include/asm-ppc/mmu_context.h	2003-06-13 15:51:38.000000000 +0100
+++ linux-2.4.27-leo/include/asm-ppc/mmu_context.h	2004-09-17 03:19:36.000000000 +0100
@@ -155,6 +155,10 @@
 static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
 			     struct task_struct *tsk, int cpu)
 {
+#ifdef CONFIG_PREEMPT
+	if (preempt_get_count() == 0)
+		BUG();
+#endif
 	tsk->thread.pgdir = next->pgd;
 	get_mmu_context(next);
 	set_context(next->context, next->pgd);
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-ppc/pgalloc.h linux-2.4.27-leo/include/asm-ppc/pgalloc.h
--- linux-2.4.27/include/asm-ppc/pgalloc.h	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.27-leo/include/asm-ppc/pgalloc.h	2004-09-17 03:19:36.000000000 +0100
@@ -72,20 +72,25 @@
 {
         unsigned long *ret;
 
+	preempt_disable();
         if ((ret = pgd_quicklist) != NULL) {
                 pgd_quicklist = (unsigned long *)(*ret);
                 ret[0] = 0;
                 pgtable_cache_size--;
+		preempt_enable();
         } else
+		preempt_enable();
                 ret = (unsigned long *)get_pgd_slow();
         return (pgd_t *)ret;
 }
 
 extern __inline__ void free_pgd_fast(pgd_t *pgd)
 {
+	preempt_disable();
         *(unsigned long **)pgd = pgd_quicklist;
         pgd_quicklist = (unsigned long *) pgd;
         pgtable_cache_size++;
+	preempt_enable();
 }
 
 extern __inline__ void free_pgd_slow(pgd_t *pgd)
@@ -124,19 +129,23 @@
 {
         unsigned long *ret;
 
+	preempt_disable();
         if ((ret = pte_quicklist) != NULL) {
                 pte_quicklist = (unsigned long *)(*ret);
                 ret[0] = 0;
                 pgtable_cache_size--;
 	}
+	preempt_enable();
         return (pte_t *)ret;
 }
 
 extern __inline__ void pte_free_fast(pte_t *pte)
 {
+	preempt_disable();
         *(unsigned long **)pte = pte_quicklist;
         pte_quicklist = (unsigned long *) pte;
         pgtable_cache_size++;
+	preempt_enable();
 }
 
 extern __inline__ void pte_free_slow(pte_t *pte)
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-ppc/pgtable.h linux-2.4.27-leo/include/asm-ppc/pgtable.h
--- linux-2.4.27/include/asm-ppc/pgtable.h	2004-02-20 14:11:46.000000000 +0000
+++ linux-2.4.27-leo/include/asm-ppc/pgtable.h	2004-09-17 03:19:58.000000000 +0100
@@ -394,11 +394,21 @@
 
 #define PAGE_NONE	__pgprot(_PAGE_BASE)
 #define PAGE_READONLY	__pgprot(_PAGE_BASE | _PAGE_USER)
-#define PAGE_READONLY_X	__pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_EXEC)
+#define PAGE_READONLY_X	__pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_EXEC | _PAGE_HWEXEC)
 #define PAGE_SHARED	__pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_RW)
-#define PAGE_SHARED_X	__pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_RW | _PAGE_EXEC)
+#define PAGE_SHARED_X	__pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_RW | _PAGE_EXEC | _PAGE_HWEXEC)
 #define PAGE_COPY	__pgprot(_PAGE_BASE | _PAGE_USER)
-#define PAGE_COPY_X	__pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_EXEC)
+#define PAGE_COPY_X	__pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_EXEC | _PAGE_HWEXEC)
+
+#if defined(CONFIG_GRKERNSEC_PAX_PAGEEXEC) && !defined(CONFIG_40x) && !defined(CONFIG_44x)
+# define PAGE_SHARED_NOEXEC	__pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_RW | _PAGE_GUARDED)
+# define PAGE_COPY_NOEXEC	__pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_GUARDED)
+# define PAGE_READONLY_NOEXEC	__pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_GUARDED)
+#else
+# define PAGE_SHARED_NOEXEC	PAGE_SHARED
+# define PAGE_COPY_NOEXEC	PAGE_COPY
+# define PAGE_READONLY_NOEXEC	PAGE_READONLY
+#endif
 
 #define PAGE_KERNEL	__pgprot(_PAGE_KERNEL)
 #define PAGE_KERNEL_RO	__pgprot(_PAGE_BASE | _PAGE_SHARED)
@@ -411,21 +421,21 @@
  * This is the closest we can get..
  */
 #define __P000	PAGE_NONE
-#define __P001	PAGE_READONLY_X
-#define __P010	PAGE_COPY
-#define __P011	PAGE_COPY_X
-#define __P100	PAGE_READONLY
+#define __P001	PAGE_READONLY_NOEXEC
+#define __P010	PAGE_COPY_NOEXEC
+#define __P011	PAGE_COPY_NOEXEC
+#define __P100	PAGE_READONLY_X
 #define __P101	PAGE_READONLY_X
-#define __P110	PAGE_COPY
+#define __P110	PAGE_COPY_X
 #define __P111	PAGE_COPY_X
 
 #define __S000	PAGE_NONE
-#define __S001	PAGE_READONLY_X
-#define __S010	PAGE_SHARED
-#define __S011	PAGE_SHARED_X
-#define __S100	PAGE_READONLY
+#define __S001	PAGE_READONLY_NOEXEC
+#define __S010	PAGE_SHARED_NOEXEC
+#define __S011	PAGE_SHARED_NOEXEC
+#define __S100	PAGE_READONLY_X
 #define __S101	PAGE_READONLY_X
-#define __S110	PAGE_SHARED
+#define __S110	PAGE_SHARED_X
 #define __S111	PAGE_SHARED_X
 
 #ifndef __ASSEMBLY__
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-ppc/smplock.h linux-2.4.27-leo/include/asm-ppc/smplock.h
--- linux-2.4.27/include/asm-ppc/smplock.h	2003-06-13 15:51:38.000000000 +0100
+++ linux-2.4.27-leo/include/asm-ppc/smplock.h	2004-09-17 03:19:36.000000000 +0100
@@ -12,7 +12,15 @@
 
 extern spinlock_t kernel_flag;
 
+#ifdef CONFIG_SMP
 #define kernel_locked()		spin_is_locked(&kernel_flag)
+#else
+#ifdef CONFIG_PREEMPT
+#define kernel_locked()		preempt_get_count()
+#else
+#define kernel_locked()		1
+#endif
+#endif
 
 /*
  * Release global kernel lock and global interrupt lock
@@ -44,8 +52,14 @@
  */
 static __inline__ void lock_kernel(void)
 {
+#ifdef CONFIG_PREEMPT
+	if (current->lock_depth == -1)
+		spin_lock(&kernel_flag);
+	++current->lock_depth;
+#else
 	if (!++current->lock_depth)
 		spin_lock(&kernel_flag);
+#endif
 }
 
 static __inline__ void unlock_kernel(void)
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-ppc/softirq.h linux-2.4.27-leo/include/asm-ppc/softirq.h
--- linux-2.4.27/include/asm-ppc/softirq.h	2003-06-13 15:51:38.000000000 +0100
+++ linux-2.4.27-leo/include/asm-ppc/softirq.h	2004-09-17 03:19:36.000000000 +0100
@@ -7,6 +7,7 @@
 
 #define local_bh_disable()			\
 do {						\
+	preempt_disable();			\
 	local_bh_count(smp_processor_id())++;	\
 	barrier();				\
 } while (0)
@@ -15,9 +16,10 @@
 do {						\
 	barrier();				\
 	local_bh_count(smp_processor_id())--;	\
+	preempt_enable();			\
 } while (0)
 
-#define local_bh_enable()				\
+#define _local_bh_enable()				\
 do {							\
 	if (!--local_bh_count(smp_processor_id())	\
 	    && softirq_pending(smp_processor_id())) {	\
@@ -25,7 +27,14 @@
 	}						\
 } while (0)
 
-#define in_softirq() (local_bh_count(smp_processor_id()) != 0)
+#define local_bh_enable()			\
+do {						\
+	_local_bh_enable();			\
+	preempt_enable();			\
+} while (0)
+
+#define in_softirq() (preempt_is_disabled() && \
+			(local_bh_count(smp_processor_id()) != 0))
 
 #endif	/* __ASM_SOFTIRQ_H */
 #endif /* __KERNEL__ */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-sparc/a.out.h linux-2.4.27-leo/include/asm-sparc/a.out.h
--- linux-2.4.27/include/asm-sparc/a.out.h	2000-01-13 20:03:00.000000000 +0000
+++ linux-2.4.27-leo/include/asm-sparc/a.out.h	2004-09-17 03:19:58.000000000 +0100
@@ -91,7 +91,7 @@
 
 #include <asm/page.h>
 
-#define STACK_TOP	(PAGE_OFFSET - PAGE_SIZE)
+#define __STACK_TOP	(PAGE_OFFSET - PAGE_SIZE)
 
 #endif /* __KERNEL__ */
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-sparc/elf.h linux-2.4.27-leo/include/asm-sparc/elf.h
--- linux-2.4.27/include/asm-sparc/elf.h	2000-07-12 03:02:37.000000000 +0100
+++ linux-2.4.27-leo/include/asm-sparc/elf.h	2004-09-17 03:19:58.000000000 +0100
@@ -83,6 +83,18 @@
 
 #define ELF_ET_DYN_BASE         (0x08000000)
 
+#ifdef CONFIG_GRKERNSEC_PAX_ASLR
+#define PAX_ELF_ET_DYN_BASE(tsk)	0x10000UL
+
+#define PAX_DELTA_MMAP_LSB(tsk)		PAGE_SHIFT
+#define PAX_DELTA_MMAP_LEN(tsk)		16
+#define PAX_DELTA_EXEC_LSB(tsk)		PAGE_SHIFT
+#define PAX_DELTA_EXEC_LEN(tsk)		16
+#define PAX_DELTA_STACK_LSB(tsk)	PAGE_SHIFT
+#define PAX_DELTA_STACK_LEN(tsk)	16
+#endif
+
+
 /* This yields a mask that user programs can use to figure out what
    instruction set this cpu supports.  This can NOT be done in userspace
    on Sparc.  */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-sparc/mman.h linux-2.4.27-leo/include/asm-sparc/mman.h
--- linux-2.4.27/include/asm-sparc/mman.h	2003-06-13 15:51:38.000000000 +0100
+++ linux-2.4.27-leo/include/asm-sparc/mman.h	2004-09-17 03:19:58.000000000 +0100
@@ -24,6 +24,10 @@
 #define MAP_DENYWRITE	0x0800		/* ETXTBSY */
 #define MAP_EXECUTABLE	0x1000		/* mark it as an executable */
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+#define MAP_MIRROR	0x0400
+#endif
+
 #define MS_ASYNC	1		/* sync memory asynchronously */
 #define MS_INVALIDATE	2		/* invalidate the caches */
 #define MS_SYNC		4		/* synchronous memory sync */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-sparc/pgtable.h linux-2.4.27-leo/include/asm-sparc/pgtable.h
--- linux-2.4.27/include/asm-sparc/pgtable.h	2002-08-03 01:39:45.000000000 +0100
+++ linux-2.4.27-leo/include/asm-sparc/pgtable.h	2004-09-17 03:19:58.000000000 +0100
@@ -97,6 +97,13 @@
 BTFIXUPDEF_INT(page_shared)
 BTFIXUPDEF_INT(page_copy)
 BTFIXUPDEF_INT(page_readonly)
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+BTFIXUPDEF_INT(page_shared_noexec)
+BTFIXUPDEF_INT(page_copy_noexec)
+BTFIXUPDEF_INT(page_readonly_noexec)
+#endif
+
 BTFIXUPDEF_INT(page_kernel)
 
 #define PMD_SHIFT       	BTFIXUP_SIMM13(pmd_shift)
@@ -118,6 +125,16 @@
 #define PAGE_COPY      __pgprot(BTFIXUP_INT(page_copy))
 #define PAGE_READONLY  __pgprot(BTFIXUP_INT(page_readonly))
 
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+#define PAGE_SHARED_NOEXEC    __pgprot(BTFIXUP_INT(page_shared_noexec))
+#define PAGE_COPY_NOEXEC      __pgprot(BTFIXUP_INT(page_copy_noexec))
+#define PAGE_READONLY_NOEXEC  __pgprot(BTFIXUP_INT(page_readonly_noexec))
+#else
+#define PAGE_SHARED_NOEXEC    PAGE_SHARED
+#define PAGE_COPY_NOEXEC      PAGE_COPY
+#define PAGE_READONLY_NOEXEC  PAGE_READONLY
+#endif
+
 extern unsigned long page_kernel;
 
 #ifdef MODULE
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-sparc/pgtsrmmu.h linux-2.4.27-leo/include/asm-sparc/pgtsrmmu.h
--- linux-2.4.27/include/asm-sparc/pgtsrmmu.h	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.27-leo/include/asm-sparc/pgtsrmmu.h	2004-09-17 03:19:58.000000000 +0100
@@ -76,6 +76,15 @@
 				    SRMMU_EXEC | SRMMU_REF)
 #define SRMMU_PAGE_RDONLY  __pgprot(SRMMU_VALID | SRMMU_CACHE | \
 				    SRMMU_EXEC | SRMMU_REF)
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+#define SRMMU_PAGE_SHARED_NOEXEC  __pgprot(SRMMU_VALID | SRMMU_CACHE | \
+					   SRMMU_WRITE | SRMMU_REF)
+#define SRMMU_PAGE_COPY_NOEXEC    __pgprot(SRMMU_VALID | SRMMU_CACHE | \
+					   SRMMU_REF)
+#define SRMMU_PAGE_RDONLY_NOEXEC  __pgprot(SRMMU_VALID | SRMMU_CACHE | \
+					   SRMMU_REF)
+#endif
+
 #define SRMMU_PAGE_KERNEL  __pgprot(SRMMU_VALID | SRMMU_CACHE | SRMMU_PRIV | \
 				    SRMMU_DIRTY | SRMMU_REF)
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-sparc/uaccess.h linux-2.4.27-leo/include/asm-sparc/uaccess.h
--- linux-2.4.27/include/asm-sparc/uaccess.h	2003-06-13 15:51:38.000000000 +0100
+++ linux-2.4.27-leo/include/asm-sparc/uaccess.h	2004-09-17 03:19:58.000000000 +0100
@@ -39,7 +39,7 @@
  * No one can read/write anything from userland in the kernel space by setting
  * large size and address near to PAGE_OFFSET - a fault will break his intentions.
  */
-#define __user_ok(addr,size) ((addr) < STACK_TOP)
+#define __user_ok(addr,size) ((addr) < __STACK_TOP)
 #define __kernel_ok (segment_eq(get_fs(), KERNEL_DS))
 #define __access_ok(addr,size) (__user_ok((addr) & get_fs().seg,(size)))
 #define access_ok(type,addr,size) __access_ok((unsigned long)(addr),(size))
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-sparc64/a.out.h linux-2.4.27-leo/include/asm-sparc64/a.out.h
--- linux-2.4.27/include/asm-sparc64/a.out.h	2001-04-27 06:17:26.000000000 +0100
+++ linux-2.4.27-leo/include/asm-sparc64/a.out.h	2004-09-17 03:19:58.000000000 +0100
@@ -95,7 +95,7 @@
 
 #ifdef __KERNEL__
 
-#define STACK_TOP (current->thread.flags & SPARC_FLAG_32BIT ? 0xf0000000 : 0x80000000000L)
+#define __STACK_TOP (current->thread.flags & SPARC_FLAG_32BIT ? 0xf0000000 : 0x80000000000L)
 
 #endif
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-sparc64/elf.h linux-2.4.27-leo/include/asm-sparc64/elf.h
--- linux-2.4.27/include/asm-sparc64/elf.h	2003-06-13 15:51:38.000000000 +0100
+++ linux-2.4.27-leo/include/asm-sparc64/elf.h	2004-09-17 03:19:58.000000000 +0100
@@ -82,6 +82,17 @@
 #define ELF_ET_DYN_BASE         0x0000010000000000UL
 #endif
 
+#ifdef CONFIG_GRKERNSEC_PAX_ASLR
+#define PAX_ELF_ET_DYN_BASE(tsk)       ((tsk)->thread.flags & SPARC_FLAG_32BIT ? 0x10000UL : 0x100000UL)
+
+#define PAX_DELTA_MMAP_LSB(tsk)         (PAGE_SHIFT + 1)
+#define PAX_DELTA_MMAP_LEN(tsk)         ((tsk)->thread.flags & SPARC_FLAG_32BIT ? 14 : 28 )
+#define PAX_DELTA_EXEC_LSB(tsk)         (PAGE_SHIFT + 1)
+#define PAX_DELTA_EXEC_LEN(tsk)         ((tsk)->thread.flags & SPARC_FLAG_32BIT ? 14 : 28 )
+#define PAX_DELTA_STACK_LSB(tsk)        PAGE_SHIFT
+#define PAX_DELTA_STACK_LEN(tsk)        ((tsk)->thread.flags & SPARC_FLAG_32BIT ? 15 : 29 )
+#endif
+
 
 /* This yields a mask that user programs can use to figure out what
    instruction set this cpu supports.  */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-sparc64/mman.h linux-2.4.27-leo/include/asm-sparc64/mman.h
--- linux-2.4.27/include/asm-sparc64/mman.h	2003-06-13 15:51:38.000000000 +0100
+++ linux-2.4.27-leo/include/asm-sparc64/mman.h	2004-09-17 03:19:58.000000000 +0100
@@ -24,6 +24,10 @@
 #define MAP_DENYWRITE	0x0800		/* ETXTBSY */
 #define MAP_EXECUTABLE	0x1000		/* mark it as an executable */
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+#define MAP_MIRROR	0x0400
+#endif
+
 #define MS_ASYNC	1		/* sync memory asynchronously */
 #define MS_INVALIDATE	2		/* invalidate the caches */
 #define MS_SYNC		4		/* synchronous memory sync */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/asm-sparc64/pgtable.h linux-2.4.27-leo/include/asm-sparc64/pgtable.h
--- linux-2.4.27/include/asm-sparc64/pgtable.h	2004-09-17 02:38:46.000000000 +0100
+++ linux-2.4.27-leo/include/asm-sparc64/pgtable.h	2004-09-17 03:19:58.000000000 +0100
@@ -122,7 +122,8 @@
 #define _PAGE_G		0x0000000000000001	/* Global                             */
 
 /* Here are the SpitFire software bits we use in the TTE's. */
-#define _PAGE_MODIFIED	0x0000000000000800	/* Modified Page (ie. dirty)          */
+#define _PAGE_MODIFIED	0x0000000000001000	/* Modified Page (ie. dirty)          */
+#define _PAGE_EXEC	0x0000000000000800	/* Executable SW bit		      */
 #define _PAGE_ACCESSED	0x0000000000000400	/* Accessed Page (ie. referenced)     */
 #define _PAGE_READ	0x0000000000000200	/* Readable SW Bit                    */
 #define _PAGE_WRITE	0x0000000000000100	/* Writable SW Bit                    */
@@ -150,16 +151,30 @@
 
 /* Don't set the TTE _PAGE_W bit here, else the dirty bit never gets set. */
 #define PAGE_SHARED	__pgprot (_PAGE_PRESENT | _PAGE_VALID | _PAGE_CACHE | \
-				  __ACCESS_BITS | _PAGE_WRITE)
+				  __ACCESS_BITS | _PAGE_WRITE | _PAGE_EXEC)
 
 #define PAGE_COPY	__pgprot (_PAGE_PRESENT | _PAGE_VALID | _PAGE_CACHE | \
-				  __ACCESS_BITS)
+				  __ACCESS_BITS | _PAGE_EXEC)
 
 #define PAGE_READONLY	__pgprot (_PAGE_PRESENT | _PAGE_VALID | _PAGE_CACHE | \
-				  __ACCESS_BITS)
+				  __ACCESS_BITS | _PAGE_EXEC)
 
 #define PAGE_KERNEL	__pgprot (_PAGE_PRESENT | _PAGE_VALID | _PAGE_CACHE | \
-				  __PRIV_BITS | __ACCESS_BITS | __DIRTY_BITS)
+				  __PRIV_BITS | __ACCESS_BITS | __DIRTY_BITS | \
+				  _PAGE_EXEC)
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+#define PAGE_SHARED_NOEXEC    __pgprot (_PAGE_PRESENT | _PAGE_VALID | _PAGE_CACHE | \
+					__ACCESS_BITS | _PAGE_WRITE)
+#define PAGE_COPY_NOEXEC      __pgprot (_PAGE_PRESENT | _PAGE_VALID | _PAGE_CACHE | \
+					__ACCESS_BITS)
+#define PAGE_READONLY_NOEXEC  __pgprot (_PAGE_PRESENT | _PAGE_VALID | _PAGE_CACHE | \
+					__ACCESS_BITS)
+#else
+#define PAGE_SHARED_NOEXEC     PAGE_SHARED
+#define PAGE_COPY_NOEXEC       PAGE_COPY
+#define PAGE_READONLY_NOEXEC   PAGE_READONLY
+#endif
 
 #define PAGE_INVALID	__pgprot (0)
 
@@ -170,18 +185,18 @@
 #define pg_iobits (_PAGE_VALID | _PAGE_PRESENT | __DIRTY_BITS | __ACCESS_BITS | _PAGE_E)
 
 #define __P000	PAGE_NONE
-#define __P001	PAGE_READONLY
-#define __P010	PAGE_COPY
-#define __P011	PAGE_COPY
+#define __P001	PAGE_READONLY_NOEXEC
+#define __P010	PAGE_COPY_NOEXEC
+#define __P011	PAGE_COPY_NOEXEC
 #define __P100	PAGE_READONLY
 #define __P101	PAGE_READONLY
 #define __P110	PAGE_COPY
 #define __P111	PAGE_COPY
 
 #define __S000	PAGE_NONE
-#define __S001	PAGE_READONLY
-#define __S010	PAGE_SHARED
-#define __S011	PAGE_SHARED
+#define __S001	PAGE_READONLY_NOEXEC
+#define __S010	PAGE_SHARED_NOEXEC
+#define __S011	PAGE_SHARED_NOEXEC
 #define __S100	PAGE_READONLY
 #define __S101	PAGE_READONLY
 #define __S110	PAGE_SHARED
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/a.out.h linux-2.4.27-leo/include/linux/a.out.h
--- linux-2.4.27/include/linux/a.out.h	2004-05-26 23:40:33.000000000 +0100
+++ linux-2.4.27-leo/include/linux/a.out.h	2004-09-20 22:08:42.000000000 +0100
@@ -7,6 +7,16 @@
 
 #include <asm/a.out.h>
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDUSTACK
+#define __DELTA_STACK (current->mm->delta_stack)
+#else
+#define __DELTA_STACK 0UL
+#endif
+
+#ifndef STACK_TOP
+#define STACK_TOP	(__STACK_TOP - __DELTA_STACK)
+#endif
+
 #endif /* __STRUCT_EXEC_OVERRIDE__ */
 
 /* these go in the N_MACHTYPE field */
@@ -37,6 +47,14 @@
   M_MIPS2 = 152		/* MIPS R6000/R4000 binary */
 };
 
+/* Constants for the N_FLAGS field */
+#define F_PAX_PAGEEXEC 1	/* Paging based non-executable pages */
+#define F_PAX_EMUTRAMP 2	/* Emulate trampolines */
+#define F_PAX_MPROTECT 4	/* Restrict mprotect() */
+#define F_PAX_RANDMMAP 8	/* Randomize mmap() base */   
+#define F_PAX_RANDEXEC 16	/* Randomize ET_EXEC base */
+#define F_PAX_SEGMEXEC 32	/* Segmentation based non-executable pages */
+
 #if !defined (N_MAGIC)
 #define N_MAGIC(exec) ((exec).a_info & 0xffff)
 #endif
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/binfmts.h linux-2.4.27-leo/include/linux/binfmts.h
--- linux-2.4.27/include/linux/binfmts.h	2004-05-26 23:40:33.000000000 +0100
+++ linux-2.4.27-leo/include/linux/binfmts.h	2004-09-20 22:08:42.000000000 +0100
@@ -30,6 +30,7 @@
 	int argc, envc;
 	char * filename;	/* Name of binary */
 	unsigned long loader, exec;
+	int misc;
 };
 
 /*
@@ -59,6 +60,8 @@
 extern int do_coredump(long signr, struct pt_regs * regs);
 extern void set_binfmt(struct linux_binfmt *new);
 
+void pax_report_fault(struct pt_regs *regs, void *pc, void *sp);
+void pax_report_insns(void *pc);
 
 #if 0
 /* this went away now */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/brlock.h linux-2.4.27-leo/include/linux/brlock.h
--- linux-2.4.27/include/linux/brlock.h	2004-02-20 13:07:07.000000000 +0000
+++ linux-2.4.27-leo/include/linux/brlock.h	2004-09-20 22:11:21.000000000 +0100
@@ -171,11 +171,11 @@
 }
 
 #else
-# define br_read_lock(idx)	((void)(idx))
-# define br_read_unlock(idx)	((void)(idx))
-# define br_write_lock(idx)	((void)(idx))
-# define br_write_unlock(idx)	((void)(idx))
-#endif
+# define br_read_lock(idx)	({ (void)(idx); preempt_disable(); })
+# define br_read_unlock(idx)	({ (void)(idx); preempt_enable(); })
+# define br_write_lock(idx)	({ (void)(idx); preempt_disable(); })
+# define br_write_unlock(idx)	({ (void)(idx); preempt_enable(); })
+#endif	/* CONFIG_SMP */
 
 /*
  * Now enumerate all of the possible sw/hw IRQ protected
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/dcache.h linux-2.4.27-leo/include/linux/dcache.h
--- linux-2.4.27/include/linux/dcache.h	2004-02-20 13:06:53.000000000 +0000
+++ linux-2.4.27-leo/include/linux/dcache.h	2004-09-20 22:08:42.000000000 +0100
@@ -127,31 +127,6 @@
 
 extern spinlock_t dcache_lock;
 
-/**
- * d_drop - drop a dentry
- * @dentry: dentry to drop
- *
- * d_drop() unhashes the entry from the parent
- * dentry hashes, so that it won't be found through
- * a VFS lookup any more. Note that this is different
- * from deleting the dentry - d_delete will try to
- * mark the dentry negative if possible, giving a
- * successful _negative_ lookup, while d_drop will
- * just make the cache lookup fail.
- *
- * d_drop() is used mainly for stuff that wants
- * to invalidate a dentry for some reason (NFS
- * timeouts or autofs deletes).
- */
-
-static __inline__ void d_drop(struct dentry * dentry)
-{
-	spin_lock(&dcache_lock);
-	list_del(&dentry->d_hash);
-	INIT_LIST_HEAD(&dentry->d_hash);
-	spin_unlock(&dcache_lock);
-}
-
 static __inline__ int dname_external(struct dentry *d)
 {
 	return d->d_name.name != d->d_iname; 
@@ -276,3 +251,34 @@
 #endif /* __KERNEL__ */
 
 #endif	/* __LINUX_DCACHE_H */
+
+#if !defined(__LINUX_DCACHE_H_INLINES) && defined(_TASK_STRUCT_DEFINED)
+#define __LINUX_DCACHE_H_INLINES
+
+#ifdef __KERNEL__
+/**
+ * d_drop - drop a dentry
+ * @dentry: dentry to drop
+ *
+ * d_drop() unhashes the entry from the parent
+ * dentry hashes, so that it won't be found through
+ * a VFS lookup any more. Note that this is different
+ * from deleting the dentry - d_delete will try to
+ * mark the dentry negative if possible, giving a
+ * successful _negative_ lookup, while d_drop will
+ * just make the cache lookup fail.
+ *
+ * d_drop() is used mainly for stuff that wants
+ * to invalidate a dentry for some reason (NFS
+ * timeouts or autofs deletes).
+ */
+
+static __inline__ void d_drop(struct dentry * dentry)
+{
+	spin_lock(&dcache_lock);
+	list_del(&dentry->d_hash);
+	INIT_LIST_HEAD(&dentry->d_hash);
+	spin_unlock(&dcache_lock);
+}
+#endif
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/device-mapper.h linux-2.4.27-leo/include/linux/device-mapper.h
--- linux-2.4.27/include/linux/device-mapper.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/include/linux/device-mapper.h	2004-09-17 03:19:08.000000000 +0100
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2001 Sistina Software (UK) Limited.
+ *
+ * This file is released under the LGPL.
+ */
+
+#ifndef _LINUX_DEVICE_MAPPER_H
+#define _LINUX_DEVICE_MAPPER_H
+
+typedef unsigned long sector_t;
+
+struct dm_target;
+struct dm_table;
+struct dm_dev;
+
+typedef enum { STATUSTYPE_INFO, STATUSTYPE_TABLE } status_type_t;
+
+union map_info {
+	void *ptr;
+	unsigned long long ll;
+};
+
+/*
+ * In the constructor the target parameter will already have the
+ * table, type, begin and len fields filled in.
+ */
+typedef int (*dm_ctr_fn) (struct dm_target * target, unsigned int argc,
+			  char **argv);
+
+/*
+ * The destructor doesn't need to free the dm_target, just
+ * anything hidden ti->private.
+ */
+typedef void (*dm_dtr_fn) (struct dm_target * ti);
+
+/*
+ * The map function must return:
+ * < 0: error
+ * = 0: The target will handle the io by resubmitting it later
+ * > 0: simple remap complete
+ */
+typedef int (*dm_map_fn) (struct dm_target * ti, struct buffer_head * bh,
+			  int rw, union map_info *map_context);
+
+/*
+ * Returns:
+ * < 0 : error (currently ignored)
+ * 0   : ended successfully
+ * 1   : for some reason the io has still not completed (eg,
+ *       multipath target might want to requeue a failed io).
+ */
+typedef int (*dm_endio_fn) (struct dm_target * ti,
+			    struct buffer_head * bh, int rw, int error,
+			    union map_info *map_context);
+typedef void (*dm_suspend_fn) (struct dm_target *ti);
+typedef void (*dm_resume_fn) (struct dm_target *ti);
+typedef int (*dm_status_fn) (struct dm_target * ti, status_type_t status_type,
+			     char *result, unsigned int maxlen);
+
+void dm_error(const char *message);
+
+/*
+ * Constructors should call these functions to ensure destination devices
+ * are opened/closed correctly.
+ * FIXME: too many arguments.
+ */
+int dm_get_device(struct dm_target *ti, const char *path, sector_t start,
+		  sector_t len, int mode, struct dm_dev **result);
+void dm_put_device(struct dm_target *ti, struct dm_dev *d);
+
+/*
+ * Information about a target type
+ */
+struct target_type {
+	const char *name;
+	struct module *module;
+	dm_ctr_fn ctr;
+	dm_dtr_fn dtr;
+	dm_map_fn map;
+	dm_endio_fn end_io;
+	dm_suspend_fn suspend;
+	dm_resume_fn resume;
+	dm_status_fn status;
+};
+
+struct dm_target {
+	struct dm_table *table;
+	struct target_type *type;
+
+	/* target limits */
+	sector_t begin;
+	sector_t len;
+
+	/* target specific data */
+	void *private;
+
+	/* Used to provide an error string from the ctr */
+	char *error;
+};
+
+int dm_register_target(struct target_type *t);
+int dm_unregister_target(struct target_type *t);
+
+#endif				/* _LINUX_DEVICE_MAPPER_H */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/dm-ioctl.h linux-2.4.27-leo/include/linux/dm-ioctl.h
--- linux-2.4.27/include/linux/dm-ioctl.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/include/linux/dm-ioctl.h	2004-09-20 21:40:20.000000000 +0100
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2001 - 2003 Sistina Software (UK) Limited.
+ *
+ * This file is released under the LGPL.
+ */
+
+#ifndef _LINUX_DM_IOCTL_H
+#define _LINUX_DM_IOCTL_H
+
+#include <linux/types.h>
+
+#define DM_DIR "mapper"		/* Slashes not supported */
+#define DM_MAX_TYPE_NAME 16
+#define DM_NAME_LEN 128
+#define DM_UUID_LEN 129
+
+/*
+ * A traditional ioctl interface for the device mapper.
+ *
+ * Each device can have two tables associated with it, an
+ * 'active' table which is the one currently used by io passing
+ * through the device, and an 'inactive' one which is a table
+ * that is being prepared as a replacement for the 'active' one.
+ *
+ * DM_VERSION:
+ * Just get the version information for the ioctl interface.
+ *
+ * DM_REMOVE_ALL:
+ * Remove all dm devices, destroy all tables.  Only really used
+ * for debug.
+ *
+ * DM_LIST_DEVICES:
+ * Get a list of all the dm device names.
+ *
+ * DM_DEV_CREATE:
+ * Create a new device, neither the 'active' or 'inactive' table
+ * slots will be filled.  The device will be in suspended state
+ * after creation, however any io to the device will get errored
+ * since it will be out-of-bounds.
+ *
+ * DM_DEV_REMOVE:
+ * Remove a device, destroy any tables.
+ *
+ * DM_DEV_RENAME:
+ * Rename a device.
+ *
+ * DM_SUSPEND:
+ * This performs both suspend and resume, depending which flag is
+ * passed in.
+ * Suspend: This command will not return until all pending io to
+ * the device has completed.  Further io will be deferred until
+ * the device is resumed.
+ * Resume: It is no longer an error to issue this command on an
+ * unsuspended device.  If a table is present in the 'inactive'
+ * slot, it will be moved to the active slot, then the old table
+ * from the active slot will be _destroyed_.  Finally the device
+ * is resumed.
+ *
+ * DM_DEV_STATUS:
+ * Retrieves the status for the table in the 'active' slot.
+ *
+ * DM_DEV_WAIT:
+ * Wait for a significant event to occur to the device.  This
+ * could either be caused by an event triggered by one of the
+ * targets of the table in the 'active' slot, or a table change.
+ *
+ * DM_TABLE_LOAD:
+ * Load a table into the 'inactive' slot for the device.  The
+ * device does _not_ need to be suspended prior to this command.
+ *
+ * DM_TABLE_CLEAR:
+ * Destroy any table in the 'inactive' slot (ie. abort).
+ *
+ * DM_TABLE_DEPS:
+ * Return a set of device dependencies for the 'active' table.
+ *
+ * DM_TABLE_STATUS:
+ * Return the targets status for the 'active' table.
+ */
+
+/*
+ * All ioctl arguments consist of a single chunk of memory, with
+ * this structure at the start.  If a uuid is specified any
+ * lookup (eg. for a DM_INFO) will be done on that, *not* the
+ * name.
+ */
+struct dm_ioctl {
+	/*
+	 * The version number is made up of three parts:
+	 * major - no backward or forward compatibility,
+	 * minor - only backwards compatible,
+	 * patch - both backwards and forwards compatible.
+	 *
+	 * All clients of the ioctl interface should fill in the
+	 * version number of the interface that they were
+	 * compiled with.
+	 *
+	 * All recognised ioctl commands (ie. those that don't
+	 * return -ENOTTY) fill out this field, even if the
+	 * command failed.
+	 */
+	uint32_t version[3];	/* in/out */
+	uint32_t data_size;	/* total size of data passed in
+				 * including this struct */
+
+	uint32_t data_start;	/* offset to start of data
+				 * relative to start of this struct */
+
+	uint32_t target_count;	/* in/out */
+	int32_t open_count;	/* out */
+	uint32_t flags;		/* in/out */
+	uint32_t event_nr;      /* in/out */
+	uint32_t padding;
+
+	uint64_t dev;		/* in/out */
+
+	char name[DM_NAME_LEN];	/* device name */
+	char uuid[DM_UUID_LEN];	/* unique identifier for
+				 * the block device */
+};
+
+/*
+ * Used to specify tables.  These structures appear after the
+ * dm_ioctl.
+ */
+struct dm_target_spec {
+	uint64_t sector_start;
+	uint64_t length;
+	int32_t status;		/* used when reading from kernel only */
+
+	/*
+	 * Offset in bytes (from the start of this struct) to
+	 * next target_spec.
+	 */
+	uint32_t next;
+
+	char target_type[DM_MAX_TYPE_NAME];
+
+	/*
+	 * Parameter string starts immediately after this object.
+	 * Be careful to add padding after string to ensure correct
+	 * alignment of subsequent dm_target_spec.
+	 */
+};
+
+/*
+ * Used to retrieve the target dependencies.
+ */
+struct dm_target_deps {
+	uint32_t count;		/* Array size */
+	uint32_t padding;	/* unused */
+	uint64_t dev[0];	/* out */
+};
+
+/*
+ * Used to get a list of all dm devices.
+ */
+struct dm_name_list {
+	uint64_t dev;
+	uint32_t next;		/* offset to the next record from
+				   the _start_ of this */
+	char name[0];
+};
+
+/*
+ * If you change this make sure you make the corresponding change
+ * to dm-ioctl.c:lookup_ioctl()
+ */
+enum {
+	/* Top level cmds */
+	DM_VERSION_CMD = 0,
+	DM_REMOVE_ALL_CMD,
+	DM_LIST_DEVICES_CMD,
+
+	/* device level cmds */
+	DM_DEV_CREATE_CMD,
+	DM_DEV_REMOVE_CMD,
+	DM_DEV_RENAME_CMD,
+	DM_DEV_SUSPEND_CMD,
+	DM_DEV_STATUS_CMD,
+	DM_DEV_WAIT_CMD,
+
+	/* Table level cmds */
+	DM_TABLE_LOAD_CMD,
+	DM_TABLE_CLEAR_CMD,
+	DM_TABLE_DEPS_CMD,
+	DM_TABLE_STATUS_CMD,
+};
+
+#define DM_IOCTL 0xfd
+
+#define DM_VERSION       _IOWR(DM_IOCTL, DM_VERSION_CMD, struct dm_ioctl)
+#define DM_REMOVE_ALL    _IOWR(DM_IOCTL, DM_REMOVE_ALL_CMD, struct dm_ioctl)
+#define DM_LIST_DEVICES  _IOWR(DM_IOCTL, DM_LIST_DEVICES_CMD, struct dm_ioctl)
+
+#define DM_DEV_CREATE    _IOWR(DM_IOCTL, DM_DEV_CREATE_CMD, struct dm_ioctl)
+#define DM_DEV_REMOVE    _IOWR(DM_IOCTL, DM_DEV_REMOVE_CMD, struct dm_ioctl)
+#define DM_DEV_RENAME    _IOWR(DM_IOCTL, DM_DEV_RENAME_CMD, struct dm_ioctl)
+#define DM_DEV_SUSPEND   _IOWR(DM_IOCTL, DM_DEV_SUSPEND_CMD, struct dm_ioctl)
+#define DM_DEV_STATUS    _IOWR(DM_IOCTL, DM_DEV_STATUS_CMD, struct dm_ioctl)
+#define DM_DEV_WAIT      _IOWR(DM_IOCTL, DM_DEV_WAIT_CMD, struct dm_ioctl)
+
+#define DM_TABLE_LOAD    _IOWR(DM_IOCTL, DM_TABLE_LOAD_CMD, struct dm_ioctl)
+#define DM_TABLE_CLEAR   _IOWR(DM_IOCTL, DM_TABLE_CLEAR_CMD, struct dm_ioctl)
+#define DM_TABLE_DEPS    _IOWR(DM_IOCTL, DM_TABLE_DEPS_CMD, struct dm_ioctl)
+#define DM_TABLE_STATUS  _IOWR(DM_IOCTL, DM_TABLE_STATUS_CMD, struct dm_ioctl)
+
+#define DM_VERSION_MAJOR	4
+#define DM_VERSION_MINOR	0
+#define DM_VERSION_PATCHLEVEL	5
+#define DM_VERSION_EXTRA	"-ioctl (2003-11-18)"
+
+/* Status bits */
+#define DM_READONLY_FLAG	(1 << 0) /* In/Out */
+#define DM_SUSPEND_FLAG		(1 << 1) /* In/Out */
+#define DM_PERSISTENT_DEV_FLAG	(1 << 3) /* In */
+
+/*
+ * Flag passed into ioctl STATUS command to get table information
+ * rather than current status.
+ */
+#define DM_STATUS_TABLE_FLAG	(1 << 4) /* In */
+
+/*
+ * Flags that indicate whether a table is present in either of
+ * the two table slots that a device has.
+ */
+#define DM_ACTIVE_PRESENT_FLAG   (1 << 5) /* Out */
+#define DM_INACTIVE_PRESENT_FLAG (1 << 6) /* Out */
+
+/*
+ * Indicates that the buffer passed in wasn't big enough for the
+ * results.
+ */
+#define DM_BUFFER_FULL_FLAG	(1 << 8) /* Out */
+
+#endif				/* _LINUX_DM_IOCTL_H */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/elf.h linux-2.4.27-leo/include/linux/elf.h
--- linux-2.4.27/include/linux/elf.h	2004-05-26 23:43:46.000000000 +0100
+++ linux-2.4.27-leo/include/linux/elf.h	2004-09-20 22:10:16.000000000 +0100
@@ -34,6 +34,10 @@
 #define PT_MIPS_REGINFO		0x70000000
 #define PT_MIPS_OPTIONS		0x70000001
 
+#define PT_LOOS			0x60000000
+#define PT_GNU_STACK		(PT_LOOS + 0x474e551)
+#define PT_PAX_FLAGS		(PT_LOOS + 0x5041580)
+
 /* Flags in the e_flags field of the header */
 #define EF_MIPS_NOREORDER 0x00000001
 #define EF_MIPS_PIC       0x00000002
@@ -122,6 +126,8 @@
 #define DT_DEBUG	21
 #define DT_TEXTREL	22
 #define DT_JMPREL	23
+#define DT_FLAGS	30
+#define DF_TEXTREL	0x00000004
 #define DT_LOPROC	0x70000000
 #define DT_HIPROC	0x7fffffff
 #define DT_MIPS_RLD_VERSION	0x70000001
@@ -260,6 +266,13 @@
 #define R_MIPS_LOVENDOR		100
 #define R_MIPS_HIVENDOR		127
 
+/* Constants for the e_flags field */
+#define EF_PAX_PAGEEXEC               1       /* Paging based non-executable pages */
+#define EF_PAX_EMUTRAMP               2       /* Emulate trampolines */
+#define EF_PAX_MPROTECT               4       /* Restrict mprotect() */
+#define EF_PAX_RANDMMAP               8       /* Randomize mmap() base */ 
+#define EF_PAX_RANDEXEC		      16      /* Randomize ET_EXEC base */
+#define EF_PAX_SEGMEXEC		      32      /* Segmentation based non-executable pages */
 
 /*
  * Sparc ELF relocation types
@@ -458,6 +471,19 @@
 #define PF_W		0x2
 #define PF_X		0x1
 
+#define PF_PAGEEXEC	(1 << 4)	/* Enable  PAGEEXEC */
+#define PF_NOPAGEEXEC	(1 << 5)	/* Disable PAGEEXEC */
+#define PF_SEGMEXEC	(1 << 6)	/* Enable  SEGMEXEC */
+#define PF_NOSEGMEXEC	(1 << 7)	/* Disable SEGMEXEC */
+#define PF_MPROTECT	(1 << 8)	/* Enable  MPROTECT */
+#define PF_NOMPROTECT	(1 << 9)	/* Disable MPROTECT */
+#define PF_RANDEXEC	(1 << 10)	/* Enable  RANDEXEC */
+#define PF_NORANDEXEC	(1 << 11)	/* Disable RANDEXEC */
+#define PF_EMUTRAMP	(1 << 12)	/* Enable  EMUTRAMP */
+#define PF_NOEMUTRAMP	(1 << 13)	/* Disable EMUTRAMP */
+#define PF_RANDMMAP	(1 << 14)	/* Enable  RANDMMAP */
+#define PF_NORANDMMAP	(1 << 15)	/* Disable RANDMMAP */
+
 typedef struct elf32_phdr{
   Elf32_Word	p_type;
   Elf32_Off	p_offset;
@@ -555,6 +581,8 @@
 #define	EI_VERSION	6
 #define	EI_PAD		7
 
+#define EI_PAX		14
+
 #define	ELFMAG0		0x7f		/* EI_MAG */
 #define	ELFMAG1		'E'
 #define	ELFMAG2		'L'
@@ -602,6 +630,7 @@
 #define elfhdr		elf32_hdr
 #define elf_phdr	elf32_phdr
 #define elf_note	elf32_note
+#define elf_dyn		Elf32_Dyn
 
 #else
 
@@ -609,6 +638,7 @@
 #define elfhdr		elf64_hdr
 #define elf_phdr	elf64_phdr
 #define elf_note	elf64_note
+#define elf_dyn		Elf64_Dyn
 
 #endif
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/fs.h linux-2.4.27-leo/include/linux/fs.h
--- linux-2.4.27/include/linux/fs.h	2004-09-17 02:38:47.000000000 +0100
+++ linux-2.4.27-leo/include/linux/fs.h	2004-09-20 22:08:42.000000000 +0100
@@ -266,7 +266,7 @@
 	struct page *b_page;		/* the page this bh is mapped to */
 	void (*b_end_io)(struct buffer_head *bh, int uptodate); /* I/O completion */
  	void *b_private;		/* reserved for b_end_io */
-
+ 	void *b_journal_head;		/* ext3 journal_heads */
 	unsigned long b_rsector;	/* Real buffer location on disk */
 	wait_queue_head_t b_wait;
 
@@ -1090,7 +1090,7 @@
 
 asmlinkage long sys_open(const char *, int, int);
 asmlinkage long sys_close(unsigned int);	/* yes, it's really unsigned */
-extern int do_truncate(struct dentry *, loff_t start);
+extern int do_truncate(struct dentry *, loff_t start, struct vfsmount *);
 
 extern struct file *filp_open(const char *, int, int);
 extern struct file * dentry_open(struct dentry *, struct vfsmount *, int);
@@ -1277,6 +1277,7 @@
 extern int sync_buffers(kdev_t, int);
 extern void sync_dev(kdev_t);
 extern int fsync_dev(kdev_t);
+extern int fsync_dev_lockfs(kdev_t);
 extern int fsync_super(struct super_block *);
 extern int fsync_no_super(kdev_t);
 extern void sync_inodes_sb(struct super_block *);
@@ -1295,6 +1296,8 @@
 extern int filemap_fdatasync(struct address_space *);
 extern int filemap_fdatawait(struct address_space *);
 extern void sync_supers(kdev_t dev, int wait);
+extern void sync_supers_lockfs(kdev_t);
+extern void unlockfs(kdev_t);
 extern int bmap(struct inode *, int);
 extern int notify_change(struct dentry *, struct iattr *);
 extern int permission(struct inode *, int);
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/fs_struct.h linux-2.4.27-leo/include/linux/fs_struct.h
--- linux-2.4.27/include/linux/fs_struct.h	2001-07-13 23:10:44.000000000 +0100
+++ linux-2.4.27-leo/include/linux/fs_struct.h	2004-09-17 03:19:36.000000000 +0100
@@ -20,6 +20,15 @@
 extern void exit_fs(struct task_struct *);
 extern void set_fs_altroot(void);
 
+struct fs_struct *copy_fs_struct(struct fs_struct *old);
+void put_fs_struct(struct fs_struct *fs);
+
+#endif
+#endif
+
+#if !defined(_LINUX_FS_STRUCT_H_INLINES) && defined(_TASK_STRUCT_DEFINED)
+#define _LINUX_FS_STRUCT_H_INLINES
+#ifdef __KERNEL__
 /*
  * Replace the fs->{rootmnt,root} with {mnt,dentry}. Put the old values.
  * It can block. Requires the big lock held.
@@ -65,9 +74,5 @@
 		mntput(old_pwdmnt);
 	}
 }
-
-struct fs_struct *copy_fs_struct(struct fs_struct *old);
-void put_fs_struct(struct fs_struct *fs);
-
 #endif
 #endif
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/gracl.h linux-2.4.27-leo/include/linux/gracl.h
--- linux-2.4.27/include/linux/gracl.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/include/linux/gracl.h	2004-09-20 22:08:48.000000000 +0100
@@ -0,0 +1,249 @@
+#ifndef GR_ACL_H
+#define GR_ACL_H
+
+#include <linux/grdefs.h>
+#include <linux/resource.h>
+
+#include <asm/resource.h>
+
+/* Major status information */
+
+#define GR_VERSION  "grsecurity 2.0.1"
+#define GRSECURITY_VERSION 0x201
+
+enum {
+
+	SHUTDOWN = 0,
+	ENABLE = 1,
+	SPROLE = 2,
+	RELOAD = 3,
+	SEGVMOD = 4,
+	STATUS = 5,
+	UNSPROLE = 6
+};
+
+/* Password setup definitions
+ * kernel/grhash.c */
+enum {
+	GR_PW_LEN = 128,
+	GR_SALT_LEN = 16,
+	GR_SHA_LEN = 32,
+};
+
+enum {
+	GR_SPROLE_LEN = 64,
+};
+
+/* Begin Data Structures */
+
+struct sprole_pw {
+	unsigned char *rolename;
+	unsigned char salt[GR_SALT_LEN];
+	unsigned char sum[GR_SHA_LEN];	/* 256-bit SHA hash of the password */
+};
+
+struct name_entry {
+	ino_t inode;
+	kdev_t device;
+	char *name;
+	__u16 len;
+};
+
+struct acl_role_db {
+	struct acl_role_label **r_hash;
+	__u32 r_size;
+};
+
+struct name_db {
+	struct name_entry **n_hash;
+	__u32 n_size;
+};
+
+struct crash_uid {
+	uid_t uid;
+	unsigned long expires;
+};
+
+struct gr_hash_struct {
+	void **table;
+	void **nametable;
+	void *first;
+	__u32 table_size;
+	__u32 used_size;
+	int type;
+};
+
+/* Userspace Grsecurity ACL data structures */
+struct acl_subject_label {
+	char *filename;
+	ino_t inode;
+	kdev_t device;
+	__u32 mode;
+	__u32 cap_mask;
+	__u32 cap_lower;
+
+	struct rlimit res[RLIM_NLIMITS + 1];
+	__u16 resmask;
+
+	__u8 user_trans_type;
+	__u8 group_trans_type;
+	uid_t *user_transitions;
+	gid_t *group_transitions;
+	__u16 user_trans_num;
+	__u16 group_trans_num;
+
+	__u32 ip_proto[8];
+	__u32 ip_type;
+	struct acl_ip_label **ips;
+	__u32 ip_num;
+
+	__u32 crashes;
+	unsigned long expires;
+
+	struct acl_subject_label *parent_subject;
+	struct gr_hash_struct *hash;
+	struct acl_subject_label *prev;
+	struct acl_subject_label *next;
+
+	struct acl_object_label **obj_hash;
+	__u32 obj_hash_size;
+};
+
+struct role_allowed_ip {
+	__u32 addr;
+	__u32 netmask;
+
+	struct role_allowed_ip *prev;
+	struct role_allowed_ip *next;
+};
+
+struct role_transition {
+	char *rolename;
+
+	struct role_transition *prev;
+	struct role_transition *next;
+};
+
+struct acl_role_label {
+	char *rolename;
+	uid_t uidgid;
+	__u16 roletype;
+
+	__u16 auth_attempts;
+	unsigned long expires;
+
+	struct acl_subject_label *root_label;
+	struct gr_hash_struct *hash;
+
+	struct acl_role_label *prev;
+	struct acl_role_label *next;
+
+	struct role_transition *transitions;
+	struct role_allowed_ip *allowed_ips;
+	uid_t *domain_children;
+	__u16 domain_child_num;
+
+	struct acl_subject_label **subj_hash;
+	__u32 subj_hash_size;
+};
+
+struct user_acl_role_db {
+	struct acl_role_label **r_table;
+	__u32 num_pointers;		/* Number of allocations to track */
+	__u32 num_roles;		/* Number of roles */
+	__u32 num_domain_children;	/* Number of domain children */
+	__u32 num_subjects;		/* Number of subjects */
+	__u32 num_objects;		/* Number of objects */
+};
+
+struct acl_object_label {
+	char *filename;
+	ino_t inode;
+	kdev_t device;
+	__u32 mode;
+
+	struct acl_subject_label *nested;
+	struct acl_object_label *globbed;
+
+	/* next two structures not used */
+
+	struct acl_object_label *prev;
+	struct acl_object_label *next;
+};
+
+struct acl_ip_label {
+	__u32 addr;
+	__u32 netmask;
+	__u16 low, high;
+	__u8 mode;
+	__u32 type;
+	__u32 proto[8];
+
+	/* next two structures not used */
+
+	struct acl_ip_label *prev;
+	struct acl_ip_label *next;
+};
+
+struct gr_arg {
+	struct user_acl_role_db role_db;
+	unsigned char pw[GR_PW_LEN];
+	unsigned char salt[GR_SALT_LEN];
+	unsigned char sum[GR_SHA_LEN];
+	unsigned char sp_role[GR_SPROLE_LEN];
+	struct sprole_pw *sprole_pws;
+	kdev_t segv_device;
+	ino_t segv_inode;
+	uid_t segv_uid;
+	__u16 num_sprole_pws;
+	__u16 mode;
+};
+
+struct gr_arg_wrapper {
+	struct gr_arg *arg;
+	__u32 version;
+	__u32 size;
+};
+
+struct subject_map {
+	struct acl_subject_label *user;
+	struct acl_subject_label *kernel;
+};
+
+struct acl_subj_map_db {
+	struct subject_map **s_hash;
+	__u32 s_size;
+};
+
+/* End Data Structures Section */
+
+/* Hash functions generated by empirical testing by Brad Spengler
+   Makes good use of the low bits of the inode.  Generally 0-1 times
+   in loop for successful match.  0-3 for unsuccessful match.
+   Shift/add algorithm with modulus of table size and an XOR*/
+
+static __inline__ unsigned long
+rhash(const uid_t uid, const __u16 type, const unsigned long sz)
+{
+	return (((uid << type) + (uid ^ type)) % sz);
+}
+
+static __inline__ unsigned long
+shash(const struct acl_subject_label *userp, const unsigned long sz)
+{
+	return ((const unsigned long)userp % sz);
+}
+
+static __inline__ unsigned long
+fhash(const ino_t ino, const kdev_t dev, const unsigned long sz)
+{
+	return (((ino + dev) ^ ((ino << 13) + (ino << 23) + (dev << 9))) % sz);
+}
+
+static __inline__ unsigned long
+nhash(const char *name, const __u16 len, const unsigned long sz)
+{
+	return full_name_hash(name, len) % sz;
+}
+
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/gralloc.h linux-2.4.27-leo/include/linux/gralloc.h
--- linux-2.4.27/include/linux/gralloc.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/include/linux/gralloc.h	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,8 @@
+#ifndef __GRALLOC_H
+#define __GRALLOC_H
+
+void acl_free_all(void);
+int acl_alloc_stack_init(unsigned long size);
+void *acl_alloc(unsigned long len);
+
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/grdefs.h linux-2.4.27-leo/include/linux/grdefs.h
--- linux-2.4.27/include/linux/grdefs.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/include/linux/grdefs.h	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,117 @@
+#ifndef GRDEFS_H
+#define GRDEFS_H
+
+/* Begin grsecurity status declarations */
+
+enum {
+	GR_READY = 0x01,
+	GR_STATUS_INIT = 0x00	// disabled state
+};
+
+/* Begin  ACL declarations */
+
+/* Role flags */
+
+enum {
+	GR_ROLE_USER = 0x0001,
+	GR_ROLE_GROUP = 0x0002,
+	GR_ROLE_DEFAULT = 0x0004,
+	GR_ROLE_SPECIAL = 0x0008,
+	GR_ROLE_AUTH = 0x0010,
+	GR_ROLE_NOPW = 0x0020,
+	GR_ROLE_GOD = 0x0040,
+	GR_ROLE_LEARN = 0x0080,
+	GR_ROLE_TPE = 0x0100,
+	GR_ROLE_DOMAIN = 0x0200
+};
+
+/* ACL Subject and Object mode flags */
+enum {
+	GR_DELETED = 0x00000080
+};
+
+/* ACL Object-only mode flags */
+enum {
+	GR_READ 	= 0x00000001,
+	GR_APPEND 	= 0x00000002,
+	GR_WRITE 	= 0x00000004,
+	GR_EXEC 	= 0x00000008,
+	GR_FIND 	= 0x00000010,
+	GR_INHERIT 	= 0x00000040,
+	GR_PTRACERD 	= 0x00000100,
+	GR_SETID 	= 0x00000200,
+	GR_CREATE 	= 0x00000400,
+	GR_DELETE 	= 0x00000800,
+	GR_NOPTRACE	= 0x00001000,
+	GR_AUDIT_READ 	= 0x00002000,
+	GR_AUDIT_APPEND = 0x00004000,
+	GR_AUDIT_WRITE 	= 0x00008000,
+	GR_AUDIT_EXEC 	= 0x00010000,
+	GR_AUDIT_FIND 	= 0x00020000,
+	GR_AUDIT_INHERIT= 0x00040000,
+	GR_AUDIT_SETID 	= 0x00080000,
+	GR_AUDIT_CREATE = 0x00100000,
+	GR_AUDIT_DELETE = 0x00200000,
+	GR_SUPPRESS 	= 0x00400000,
+	GR_NOLEARN 	= 0x00800000
+};
+
+#define GR_AUDITS (GR_AUDIT_READ | GR_AUDIT_WRITE | GR_AUDIT_APPEND | GR_AUDIT_EXEC | \
+		   GR_AUDIT_FIND | GR_AUDIT_INHERIT | GR_AUDIT_SETID | \
+		   GR_AUDIT_CREATE | GR_AUDIT_DELETE)
+
+/* ACL subject-only mode flags */
+enum {
+	GR_KILL 	= 0x00000001,
+	GR_VIEW 	= 0x00000002,
+	GR_PROTECTED 	= 0x00000100,
+	GR_LEARN 	= 0x00000200,
+	GR_OVERRIDE 	= 0x00000400,
+	/* just a placeholder, this mode is only used in userspace */
+	GR_DUMMY 	= 0x00000800,
+	GR_PAXPAGE 	= 0x00001000,
+	GR_PAXSEGM 	= 0x00002000,
+	GR_PAXGCC 	= 0x00004000,
+	GR_PAXRANDMMAP 	= 0x00008000,
+	GR_PAXRANDEXEC 	= 0x00010000,
+	GR_PAXMPROTECT 	= 0x00020000,
+	GR_PROTSHM 	= 0x00040000,
+	GR_KILLPROC 	= 0x00080000,
+	GR_KILLIPPROC	= 0x00100000,
+	/* just a placeholder, this mode is only used in userspace */
+	GR_NOTROJAN 	= 0x00200000,
+	GR_PROTPROCFD 	= 0x00400000,
+	GR_PROCACCT 	= 0x00800000,
+	GR_RELAXPTRACE	= 0x01000000,
+	GR_NESTED	= 0x02000000
+};
+
+enum {
+	GR_ID_USER      = 0x01,
+	GR_ID_GROUP     = 0x02,
+};
+
+enum {
+	GR_ID_ALLOW     = 0x01,
+	GR_ID_DENY      = 0x02,
+};
+
+#define GR_CRASH_RES	11
+#define GR_UIDTABLE_MAX 500
+
+/* begin resource learning section */
+enum {
+	GR_RLIM_CPU_BUMP = 60,
+	GR_RLIM_FSIZE_BUMP = 50000,
+	GR_RLIM_DATA_BUMP = 10000,
+	GR_RLIM_STACK_BUMP = 1000,
+	GR_RLIM_CORE_BUMP = 10000,
+	GR_RLIM_RSS_BUMP = 500000,
+	GR_RLIM_NPROC_BUMP = 1,
+	GR_RLIM_NOFILE_BUMP = 5,
+	GR_RLIM_MEMLOCK_BUMP = 50000,
+	GR_RLIM_AS_BUMP = 500000,
+	GR_RLIM_LOCKS_BUMP = 2
+};
+
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/grinternal.h linux-2.4.27-leo/include/linux/grinternal.h
--- linux-2.4.27/include/linux/grinternal.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/include/linux/grinternal.h	2004-09-20 22:08:48.000000000 +0100
@@ -0,0 +1,201 @@
+#ifndef __GRINTERNAL_H
+#define __GRINTERNAL_H
+
+#ifdef CONFIG_GRKERNSEC
+
+#include <linux/grdefs.h>
+#include <linux/gracl.h>
+#include <linux/grmsg.h>
+
+extern void gr_add_learn_entry(const char *fmt, ...);
+extern __u32 gr_search_file(const struct dentry *dentry, const __u32 mode,
+			    const struct vfsmount *mnt);
+extern __u32 gr_check_create(const struct dentry *new_dentry,
+			     const struct dentry *parent,
+			     const struct vfsmount *mnt, const __u32 mode);
+extern int gr_check_protected_task(const struct task_struct *task);
+extern __u32 to_gr_audit(const __u32 reqmode);
+extern int gr_handle_rename(struct inode *old_dir, struct inode *new_dir,
+			    struct dentry *old_dentry,
+			    struct dentry *new_dentry,
+			    struct vfsmount *mnt, const __u8 replace);
+extern int gr_set_acls(const int type);
+extern int gr_acl_is_enabled(void);
+extern char gr_roletype_to_char(void);
+
+extern void gr_handle_alertkill(void);
+extern char *gr_to_filename(const struct dentry *dentry,
+			    const struct vfsmount *mnt);
+extern char *gr_to_filename1(const struct dentry *dentry,
+			    const struct vfsmount *mnt);
+extern char *gr_to_filename2(const struct dentry *dentry,
+			    const struct vfsmount *mnt);
+extern char *gr_to_filename3(const struct dentry *dentry,
+			    const struct vfsmount *mnt);
+
+extern int grsec_enable_link;
+extern int grsec_enable_fifo;
+extern int grsec_enable_execve;
+extern int grsec_enable_forkbomb;
+extern int grsec_forkbomb_gid;
+extern int grsec_forkbomb_sec;
+extern int grsec_forkbomb_max;
+extern int grsec_enable_execlog;
+extern int grsec_enable_signal;
+extern int grsec_enable_forkfail;
+extern int grsec_enable_time;
+extern int grsec_enable_chroot_shmat;
+extern int grsec_enable_chroot_findtask;
+extern int grsec_enable_chroot_mount;
+extern int grsec_enable_chroot_double;
+extern int grsec_enable_chroot_pivot;
+extern int grsec_enable_chroot_chdir;
+extern int grsec_enable_chroot_chmod;
+extern int grsec_enable_chroot_mknod;
+extern int grsec_enable_chroot_fchdir;
+extern int grsec_enable_chroot_nice;
+extern int grsec_enable_chroot_execlog;
+extern int grsec_enable_chroot_caps;
+extern int grsec_enable_chroot_sysctl;
+extern int grsec_enable_chroot_unix;
+extern int grsec_enable_tpe;
+extern int grsec_tpe_gid;
+extern int grsec_enable_tpe_all;
+extern int grsec_enable_randpid;
+extern int grsec_enable_socket_all;
+extern int grsec_socket_all_gid;
+extern int grsec_enable_socket_client;
+extern int grsec_socket_client_gid;
+extern int grsec_enable_socket_server;
+extern int grsec_socket_server_gid;
+extern int grsec_audit_gid;
+extern int grsec_enable_group;
+extern int grsec_enable_audit_ipc;
+extern int grsec_enable_audit_textrel;
+extern int grsec_enable_mount;
+extern int grsec_enable_chdir;
+extern int grsec_lock;
+
+extern struct task_struct *child_reaper;
+
+extern spinlock_t grsec_alert_lock;
+extern unsigned long grsec_alert_wtime;
+extern unsigned long grsec_alert_fyet;
+
+extern spinlock_t grsec_audit_lock;
+
+extern rwlock_t grsec_exec_file_lock;
+
+#define gr_task_fullpath(tsk) (tsk->exec_file ? \
+			gr_to_filename2(tsk->exec_file->f_dentry, \
+			tsk->exec_file->f_vfsmnt) : "/")
+
+#define gr_parent_task_fullpath(tsk) (tsk->p_pptr->exec_file ? \
+			gr_to_filename3(tsk->p_pptr->exec_file->f_dentry, \
+			tsk->p_pptr->exec_file->f_vfsmnt) : "/")
+
+#define gr_task_fullpath0(tsk) (tsk->exec_file ? \
+			gr_to_filename(tsk->exec_file->f_dentry, \
+			tsk->exec_file->f_vfsmnt) : "/")
+
+#define gr_parent_task_fullpath0(tsk) (tsk->p_pptr->exec_file ? \
+			gr_to_filename1(tsk->p_pptr->exec_file->f_dentry, \
+			tsk->p_pptr->exec_file->f_vfsmnt) : "/")
+
+#define proc_is_chrooted(tsk_a)  ((tsk_a->pid > 1) && \
+			  ((tsk_a->fs->root->d_inode->i_dev != \
+			  child_reaper->fs->root->d_inode->i_dev) || \
+			  (tsk_a->fs->root->d_inode->i_ino != \
+			  child_reaper->fs->root->d_inode->i_ino)))
+
+#define have_same_root(tsk_a,tsk_b) ((tsk_a->fs->root->d_inode->i_dev == \
+			  tsk_b->fs->root->d_inode->i_dev) && \
+			  (tsk_a->fs->root->d_inode->i_ino == \
+			  tsk_b->fs->root->d_inode->i_ino))
+
+#define DEFAULTSECARGS gr_task_fullpath(current), current->comm, \
+		       current->pid, current->uid, \
+		       current->euid, current->gid, current->egid, \
+		       gr_parent_task_fullpath(current), \
+		       current->p_pptr->comm, current->p_pptr->pid, \
+		       current->p_pptr->uid, current->p_pptr->euid, \
+		       current->p_pptr->gid, current->p_pptr->egid
+
+#define GR_CHROOT_CAPS ( \
+	CAP_TO_MASK(CAP_FOWNER) | \
+	CAP_TO_MASK(CAP_LINUX_IMMUTABLE) | CAP_TO_MASK(CAP_NET_ADMIN) | \
+	CAP_TO_MASK(CAP_SYS_MODULE) | CAP_TO_MASK(CAP_SYS_RAWIO) | \
+	CAP_TO_MASK(CAP_SYS_PACCT) | CAP_TO_MASK(CAP_SYS_ADMIN) | \
+	CAP_TO_MASK(CAP_SYS_BOOT) | CAP_TO_MASK(CAP_SYS_TIME) | \
+	CAP_TO_MASK(CAP_NET_RAW) | CAP_TO_MASK(CAP_SYS_TTY_CONFIG) | \
+	CAP_TO_MASK(CAP_IPC_OWNER))
+
+#define security_alert_good(normal_msg,args...) \
+({ \
+	read_lock(&tasklist_lock); \
+	read_lock(&grsec_exec_file_lock); \
+	spin_lock(&grsec_alert_lock); \
+	\
+	if (!grsec_alert_wtime || jiffies - grsec_alert_wtime > CONFIG_GRKERNSEC_FLOODTIME * HZ) { \
+	    grsec_alert_wtime = jiffies; grsec_alert_fyet = 0; \
+	    if (current->curr_ip && gr_acl_is_enabled()) \
+		printk(KERN_ALERT "grsec: From %u.%u.%u.%u: (%.64s:%c:%.950s) " normal_msg "\n", NIPQUAD(current->curr_ip), current->role->rolename, gr_roletype_to_char(), current->acl->filename , ## args); \
+	    else if (current->curr_ip) \
+		printk(KERN_ALERT "grsec: From %u.%u.%u.%u: " normal_msg "\n", NIPQUAD(current->curr_ip) , ## args); \
+	    else if (gr_acl_is_enabled()) \
+		printk(KERN_ALERT "grsec: (%.64s:%c:%.950s) " normal_msg "\n", current->role->rolename, gr_roletype_to_char(), current->acl->filename , ## args); \
+	    else \
+	    	printk(KERN_ALERT "grsec: " normal_msg "\n" , ## args); \
+	} else if((jiffies - grsec_alert_wtime < CONFIG_GRKERNSEC_FLOODTIME * HZ) && (grsec_alert_fyet < CONFIG_GRKERNSEC_FLOODBURST)) { \
+	    grsec_alert_fyet++; \
+	    if (current->curr_ip && gr_acl_is_enabled()) \
+		printk(KERN_ALERT "grsec: From %u.%u.%u.%u: (%.64s:%c:%.950s) " normal_msg "\n", NIPQUAD(current->curr_ip), current->role->rolename, gr_roletype_to_char(), current->acl->filename , ## args); \
+	    else if (current->curr_ip) \
+		printk(KERN_ALERT "grsec: From %u.%u.%u.%u: " normal_msg "\n", NIPQUAD(current->curr_ip) , ## args); \
+	    else if (gr_acl_is_enabled()) \
+		printk(KERN_ALERT "grsec: (%.64s:%c:%.950s) " normal_msg "\n", current->role->rolename, gr_roletype_to_char(), current->acl->filename , ## args); \
+	    else \
+	    	printk(KERN_ALERT "grsec: " normal_msg "\n" , ## args); \
+	} else if (grsec_alert_fyet == CONFIG_GRKERNSEC_FLOODBURST) { \
+	    grsec_alert_wtime = jiffies; grsec_alert_fyet++; \
+	    printk(KERN_ALERT "grsec: more alerts, logging disabled for " \
+		    "%d seconds\n", CONFIG_GRKERNSEC_FLOODTIME); \
+	} \
+	\
+	spin_unlock(&grsec_alert_lock); \
+	read_unlock(&grsec_exec_file_lock); \
+	read_unlock(&tasklist_lock); \
+})
+
+#define security_alert(normal_msg,args...) \
+({ \
+	security_alert_good(normal_msg,args); \
+	gr_handle_alertkill(); \
+})
+
+#define security_audit(normal_msg,args...) \
+({ \
+	read_lock(&tasklist_lock); \
+	read_lock(&grsec_exec_file_lock); \
+	spin_lock(&grsec_audit_lock); \
+	if (current->curr_ip && gr_acl_is_enabled()) \
+	    printk(KERN_INFO "grsec: From %u.%u.%u.%u: (%.64s:%.950s) " normal_msg "\n", NIPQUAD(current->curr_ip), current->role->rolename, current->acl->filename , ## args); \
+	else if (current->curr_ip) \
+	    printk(KERN_INFO "grsec: From %u.%u.%u.%u: " normal_msg "\n", NIPQUAD(current->curr_ip) , ## args); \
+	else if (gr_acl_is_enabled()) \
+	    printk(KERN_INFO "grsec: (%.64s:%.950s) " normal_msg "\n", current->role->rolename, current->acl->filename , ## args); \
+	else \
+	    printk(KERN_INFO "grsec: " normal_msg "\n" , ## args); \
+	spin_unlock(&grsec_audit_lock); \
+	read_unlock(&grsec_exec_file_lock); \
+	read_unlock(&tasklist_lock); \
+})
+
+#define security_learn(normal_msg,args...) \
+({ \
+	gr_add_learn_entry(normal_msg "\n", ## args); \
+})
+
+#endif
+
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/grmsg.h linux-2.4.27-leo/include/linux/grmsg.h
--- linux-2.4.27/include/linux/grmsg.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/include/linux/grmsg.h	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,108 @@
+#define DEFAULTSECMSG "%.256s[%.16s:%d] uid/euid:%d/%d gid/egid:%d/%d, parent %.256s[%.16s:%d] uid/euid:%d/%d gid/egid:%d/%d"
+#define GR_ACL_PROCACCT_MSG "%.256s[%.16s:%d] IP:%u.%u.%u.%u TTY:%.64s uid/euid:%d/%d gid/egid:%d/%d run time:[%ud %uh %um %us] cpu time:[%ud %uh %um %us] %s with exit code %ld, parent %.256s[%.16s:%d] IP:%u.%u.%u.%u TTY:%.64s uid/euid:%d/%d gid/egid:%d/%d"
+#define GR_PTRACE_ACL_MSG "denied ptrace of %.950s(%.16s:%d) by " DEFAULTSECMSG
+#define GR_IOPERM_MSG "denied use of ioperm() by " DEFAULTSECMSG
+#define GR_IOPL_MSG "denied use of iopl() by " DEFAULTSECMSG
+#define GR_SHMAT_ACL_MSG "denied attach of shared memory of UID %u, PID %d, ID %u by " DEFAULTSECMSG
+#define GR_UNIX_CHROOT_MSG "denied connect to abstract AF_UNIX socket outside of chroot by " DEFAULTSECMSG
+#define GR_SHMAT_CHROOT_MSG "denied attach of shared memory outside of chroot by " DEFAULTSECMSG
+#define GR_KMEM_MSG "attempted write to /dev/kmem by " DEFAULTSECMSG
+#define GR_PORT_OPEN_MSG "attempted open of /dev/port by " DEFAULTSECMSG
+#define GR_MEM_WRITE_MSG "attempted write of /dev/mem by " DEFAULTSECMSG
+#define GR_MEM_MMAP_MSG "attempted mmap write of /dev/[k]mem by " DEFAULTSECMSG
+#define GR_SYMLINK_MSG "not following symlink %.950s owned by %d.%d by " DEFAULTSECMSG
+#define GR_LEARN_AUDIT_MSG "%s\t%u\t%u\t%u\t%.4095s\t%.4095s\t%lu\t%lu\t%.4095s\t%lu\t%u.%u.%u.%u"
+#define GR_HIDDEN_ACL_MSG "%s access to hidden file %.950s by " DEFAULTSECMSG
+#define GR_OPEN_ACL_MSG "%s open of %.950s for%s%s by " DEFAULTSECMSG
+#define GR_CREATE_ACL_MSG "%s create of %.950s for%s%s by " DEFAULTSECMSG
+#define GR_FIFO_MSG "denied writing FIFO %.950s of %d.%d by " DEFAULTSECMSG
+#define GR_MKNOD_CHROOT_MSG "refused attempt to mknod %.950s from chroot by " DEFAULTSECMSG
+#define GR_MKNOD_ACL_MSG "%s mknod of %.950s by " DEFAULTSECMSG
+#define GR_UNIXCONNECT_ACL_MSG "%s connect to the unix domain socket %.950s by " DEFAULTSECMSG
+#define GR_MKDIR_ACL_MSG "%s mkdir of %.950s by " DEFAULTSECMSG
+#define GR_RMDIR_ACL_MSG "%s rmdir of %.950s by " DEFAULTSECMSG
+#define GR_UNLINK_ACL_MSG "%s unlink of %.950s by " DEFAULTSECMSG
+#define GR_SYMLINK_ACL_MSG "%s symlink from %.480s to %.480s by " DEFAULTSECMSG
+#define GR_HARDLINK_MSG "denied hardlink of %.930s (owned by %d.%d) to %.30s for " DEFAULTSECMSG
+#define GR_LINK_ACL_MSG "%s link of %.480s to %.480s by " DEFAULTSECMSG
+#define GR_INHERIT_ACL_MSG "successful inherit of %.480s's ACL for %.480s by " DEFAULTSECMSG
+#define GR_RENAME_ACL_MSG "%s rename of %.480s to %.480s by " DEFAULTSECMSG
+#define GR_PTRACE_EXEC_ACL_MSG "denied ptrace of %.950s by " DEFAULTSECMSG
+#define GR_NPROC_MSG "attempt to overstep process limit by " DEFAULTSECMSG
+#define GR_EXEC_ACL_MSG "%s execution of %.950s by " DEFAULTSECMSG
+#define GR_EXEC_TPE_MSG "denied untrusted exec of %.950s by " DEFAULTSECMSG
+#define GR_SEGVSTART_ACL_MSG "possible exploit bruteforcing on " DEFAULTSECMSG " Banning uid %u from login for %lu seconds"
+#define GR_SEGVNOSUID_ACL_MSG "possible exploit bruteforcing on " DEFAULTSECMSG " Banning execution of [%.16s:%lu] for %lu seconds"
+#define GR_MOUNT_CHROOT_MSG "denied attempt to mount %.30s as %.930s from chroot by " DEFAULTSECMSG
+#define GR_PIVOT_CHROOT_MSG "denied attempt to pivot_root from chroot by " DEFAULTSECMSG
+#define GR_TRUNCATE_ACL_MSG "%s truncate of %.950s by " DEFAULTSECMSG
+#define GR_ATIME_ACL_MSG "%s access time change of %.950s by " DEFAULTSECMSG
+#define GR_ACCESS_ACL_MSG "%s access of %.950s for%s%s%s by " DEFAULTSECMSG
+#define GR_CHROOT_CHROOT_MSG "denied attempt to double chroot to %.950s by " DEFAULTSECMSG
+#define GR_FCHMOD_ACL_MSG "%s fchmod of %.950s by " DEFAULTSECMSG
+#define GR_CHMOD_CHROOT_MSG "denied attempt to chmod +s %.950s by " DEFAULTSECMSG
+#define GR_CHMOD_ACL_MSG "%s chmod of %.950s by " DEFAULTSECMSG
+#define GR_CHROOT_FCHDIR_MSG "attempted fchdir outside of chroot to %.950s by " DEFAULTSECMSG
+#define GR_CHOWN_ACL_MSG "%s chown of %.950s by " DEFAULTSECMSG
+#define GR_WRITLIB_ACL_MSG "denied load of writable library %.950s by " DEFAULTSECMSG
+#define GR_INITF_ACL_MSG "init_variables() failed %s"
+#define GR_DISABLED_ACL_MSG "Error loading %s, trying to run kernel with acls disabled. To disable acls at startup use <kernel image name> gracl=off from your boot loader"
+#define GR_DEV_ACL_MSG "/dev/grsec: being fed garbage %d bytes sent %d required"
+#define GR_SHUTS_ACL_MSG "shutdown auth success for " DEFAULTSECMSG
+#define GR_SHUTF_ACL_MSG "shutdown auth failure for " DEFAULTSECMSG
+#define GR_SHUTI_ACL_MSG "ignoring shutdown for disabled RBAC system for " DEFAULTSECMSG
+#define GR_SEGVMODS_ACL_MSG "segvmod auth success for " DEFAULTSECMSG
+#define GR_SEGVMODF_ACL_MSG "segvmod auth failure for " DEFAULTSECMSG
+#define GR_SEGVMODI_ACL_MSG "ignoring segvmod for disabled RBAC system for " DEFAULTSECMSG
+#define GR_ENABLE_ACL_MSG "Loaded %s"
+#define GR_ENABLEF_ACL_MSG "Unable to load %s for " DEFAULTSECMSG " RBAC system may already be enabled."
+#define GR_RELOADI_ACL_MSG "Ignoring reload request for disabled RBAC system"
+#define GR_RELOAD_ACL_MSG "Reloaded %s"
+#define GR_RELOADF_ACL_MSG "Failed reload of %s for " DEFAULTSECMSG
+#define GR_SPROLEI_ACL_MSG "Ignoring change to special role for disabled RBAC system for " DEFAULTSECMSG
+#define GR_SPROLES_ACL_MSG "successful change to special role %s (id %d) by " DEFAULTSECMSG
+#define GR_SPROLEL_ACL_MSG "special role %s (id %d) exited by " DEFAULTSECMSG
+#define GR_SPROLEF_ACL_MSG "special role %s failure for " DEFAULTSECMSG
+#define GR_UNSPROLEI_ACL_MSG "Ignoring unauth of special role for disabled RBAC system for " DEFAULTSECMSG
+#define GR_UNSPROLES_ACL_MSG "successful unauth of special role %s (id %d) by " DEFAULTSECMSG
+#define GR_UNSPROLEF_ACL_MSG "special role unauth of %s failure for " DEFAULTSECMSG
+#define GR_INVMODE_ACL_MSG "Invalid mode %d by " DEFAULTSECMSG
+#define GR_MAXPW_ACL_MSG "Maximum pw attempts reached (%d), locking password authentication"
+#define GR_MAXROLEPW_ACL_MSG "Maximum pw attempts reached (%d) trying to auth to special role %s, locking auth for role of " DEFAULTSECMSG
+#define GR_PRIORITY_CHROOT_MSG "attempted priority change of process (%.16s:%d) by " DEFAULTSECMSG
+#define GR_CAPSET_CHROOT_MSG "denied capset of (%.16s:%d) within chroot by " DEFAULTSECMSG
+#define GR_FAILFORK_MSG "failed fork with errno %d by " DEFAULTSECMSG
+#define GR_NICE_CHROOT_MSG "attempted priority change by " DEFAULTSECMSG
+#define GR_UNISIGLOG_MSG "signal %d sent to " DEFAULTSECMSG
+#define GR_DUALSIGLOG_MSG "signal %d sent to " DEFAULTSECMSG " by " DEFAULTSECMSG
+#define GR_SIG_ACL_MSG "Attempted send of signal %d to protected task " DEFAULTSECMSG " by " DEFAULTSECMSG
+#define GR_SYSCTL_MSG "attempt to modify grsecurity sysctl value : %.32s by " DEFAULTSECMSG
+#define GR_SYSCTL_ACL_MSG "%s sysctl of %.950s for%s%s by " DEFAULTSECMSG
+#define GR_TIME_MSG "time set by " DEFAULTSECMSG
+#define GR_DEFACL_MSG "Fatal: Unable to find ACL for (%.16s:%d)"
+#define GR_MMAP_ACL_MSG "%s executable mmap of %.950s by " DEFAULTSECMSG
+#define GR_MPROTECT_ACL_MSG "%s executable mprotect of %.950s by " DEFAULTSECMSG
+#define GR_SOCK_MSG "attempted socket(%.16s,%.16s,%.16s) by " DEFAULTSECMSG
+#define GR_SOCK2_MSG "attempted socket(%d,%.16s,%.16s) by " DEFAULTSECMSG
+#define GR_BIND_MSG "attempted bind() by " DEFAULTSECMSG
+#define GR_CONNECT_MSG "attempted connect by " DEFAULTSECMSG
+#define GR_BIND_ACL_MSG "attempted bind to %u.%u.%u.%u port %u sock type %.16s protocol %.16s by " DEFAULTSECMSG
+#define GR_CONNECT_ACL_MSG "attempted connect to %u.%u.%u.%u port %u sock type %.16s protocol %.16s by " DEFAULTSECMSG
+#define GR_IP_LEARN_MSG "%s\t%u\t%u\t%u\t%.4095s\t%.4095s\t%u.%u.%u.%u\t%u\t%u\t%u\t%u\t%u.%u.%u.%u"
+#define GR_EXEC_CHROOT_MSG "exec of %.980s within chroot by process " DEFAULTSECMSG
+#define GR_CAP_ACL_MSG "use of %s denied for " DEFAULTSECMSG
+#define GR_USRCHANGE_ACL_MSG "change to uid %d denied for " DEFAULTSECMSG
+#define GR_GRPCHANGE_ACL_MSG "change to gid %d denied for " DEFAULTSECMSG
+#define GR_REMOUNT_AUDIT_MSG "remount of %.30s by " DEFAULTSECMSG
+#define GR_UNMOUNT_AUDIT_MSG "unmount of %.30s by " DEFAULTSECMSG
+#define GR_MOUNT_AUDIT_MSG "mount %.30s to %.64s by " DEFAULTSECMSG
+#define GR_CHDIR_AUDIT_MSG "chdir to %.980s by " DEFAULTSECMSG
+#define GR_EXEC_AUDIT_MSG "exec of %.930s (%.63s) by " DEFAULTSECMSG
+#define GR_MSGQ_AUDIT_MSG "message queue created by " DEFAULTSECMSG
+#define GR_MSGQR_AUDIT_MSG "message queue of uid:%d euid:%d removed by " DEFAULTSECMSG
+#define GR_SEM_AUDIT_MSG "semaphore created by " DEFAULTSECMSG
+#define GR_SEMR_AUDIT_MSG "semaphore of uid:%d euid:%d removed by " DEFAULTSECMSG
+#define GR_SHM_AUDIT_MSG "shared memory of size %d created by " DEFAULTSECMSG
+#define GR_SHMR_AUDIT_MSG "shared memory of uid:%d euid:%d removed by " DEFAULTSECMSG
+#define GR_RESOURCE_MSG "attempted resource overstep by requesting %lu for %.16s against limit %lu by " DEFAULTSECMSG
+#define GR_TEXTREL_AUDIT_MSG "text relocation in %s, VMA:0x%08lx 0x%08lx by " DEFAULTSECMSG
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/grsecurity.h linux-2.4.27-leo/include/linux/grsecurity.h
--- linux-2.4.27/include/linux/grsecurity.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/include/linux/grsecurity.h	2004-09-20 22:08:42.000000000 +0100
@@ -0,0 +1,183 @@
+#ifndef GR_SECURITY_H
+#define GR_SECURITY_H
+
+extern void gr_handle_brute_attach(struct task_struct *p);
+extern void gr_handle_brute_check(void);
+
+extern int gr_check_user_change(int real, int effective, int fs);
+extern int gr_check_group_change(int real, int effective, int fs);
+
+extern void gr_add_to_task_ip_table(struct task_struct *p);
+extern void gr_del_task_from_ip_table(struct task_struct *p);
+
+extern int gr_pid_is_chrooted(struct task_struct *p);
+extern int gr_handle_chroot_nice(void);
+extern int gr_handle_chroot_sysctl(const int op);
+extern int gr_handle_chroot_capset(struct task_struct *target);
+extern int gr_handle_chroot_setpriority(const struct task_struct *p,
+					const int niceval);
+extern int gr_chroot_fchdir(struct dentry *u_dentry, struct vfsmount *u_mnt);
+extern int gr_handle_chroot_chroot(const struct dentry *dentry,
+				   const struct vfsmount *mnt);
+extern void gr_handle_chroot_caps(struct task_struct *task);
+extern void gr_handle_chroot_chdir(struct dentry *dentry, struct vfsmount *mnt);
+extern int gr_handle_chroot_chmod(const struct dentry *dentry,
+				  const struct vfsmount *mnt, const int mode);
+extern int gr_handle_chroot_mknod(const struct dentry *dentry,
+				  const struct vfsmount *mnt, const int mode);
+extern int gr_handle_chroot_mount(const struct dentry *dentry,
+				  const struct vfsmount *mnt,
+				  const char *dev_name);
+extern int gr_handle_chroot_pivot(void);
+extern int gr_handle_chroot_unix(const pid_t pid);
+
+extern int gr_handle_rawio(const struct inode *inode);
+extern int gr_handle_nproc(void);
+
+extern void gr_handle_ioperm(void);
+extern void gr_handle_iopl(void);
+
+extern int gr_tpe_allow(const struct file *file);
+
+extern int gr_random_pid(spinlock_t * pid_lock);
+
+extern void gr_log_forkfail(const int retval);
+extern void gr_log_timechange(void);
+extern void gr_log_signal(const int sig, const struct task_struct *t);
+extern void gr_log_chdir(const struct dentry *dentry,
+			 const struct vfsmount *mnt);
+extern void gr_log_chroot_exec(const struct dentry *dentry,
+			       const struct vfsmount *mnt);
+extern void gr_handle_exec_args(struct linux_binprm *bprm, char **argv);
+extern void gr_log_remount(const char *devname, const int retval);
+extern void gr_log_unmount(const char *devname, const int retval);
+extern void gr_log_mount(const char *from, const char *to, const int retval);
+extern void gr_log_msgget(const int ret, const int msgflg);
+extern void gr_log_msgrm(const uid_t uid, const uid_t cuid);
+extern void gr_log_semget(const int err, const int semflg);
+extern void gr_log_semrm(const uid_t uid, const uid_t cuid);
+extern void gr_log_shmget(const int err, const int shmflg, const size_t size);
+extern void gr_log_shmrm(const uid_t uid, const uid_t cuid);
+extern void gr_log_textrel(struct vm_area_struct *vma);
+
+extern int gr_handle_follow_link(const struct inode *parent,
+				 const struct inode *inode,
+				 const struct dentry *dentry,
+				 const struct vfsmount *mnt);
+extern int gr_handle_fifo(const struct dentry *dentry,
+			  const struct vfsmount *mnt,
+			  const struct dentry *dir, const int flag,
+			  const int acc_mode);
+extern int gr_handle_hardlink(const struct dentry *dentry,
+			      const struct vfsmount *mnt,
+			      struct inode *inode,
+			      const int mode, const char *to);
+
+extern int gr_task_is_capable(struct task_struct *task, const int cap);
+extern void gr_learn_resource(const struct task_struct *task, const int limit,
+			      const unsigned long wanted, const int gt);
+extern void gr_copy_label(struct task_struct *tsk);
+extern void gr_handle_crash(struct task_struct *task, const int sig);
+extern int gr_handle_signal(const struct task_struct *p, const int sig);
+extern int gr_check_crash_uid(const uid_t uid);
+extern int gr_check_protected_task(const struct task_struct *task);
+extern int gr_acl_handle_mmap(const struct file *file,
+			      const unsigned long prot);
+extern int gr_acl_handle_mprotect(const struct file *file,
+				  const unsigned long prot);
+extern int gr_check_hidden_task(const struct task_struct *tsk);
+extern __u32 gr_acl_handle_truncate(const struct dentry *dentry,
+				    const struct vfsmount *mnt);
+extern __u32 gr_acl_handle_utime(const struct dentry *dentry,
+				 const struct vfsmount *mnt);
+extern __u32 gr_acl_handle_access(const struct dentry *dentry,
+				  const struct vfsmount *mnt, const int fmode);
+extern __u32 gr_acl_handle_fchmod(const struct dentry *dentry,
+				  const struct vfsmount *mnt, mode_t mode);
+extern __u32 gr_acl_handle_chmod(const struct dentry *dentry,
+				 const struct vfsmount *mnt, mode_t mode);
+extern __u32 gr_acl_handle_chown(const struct dentry *dentry,
+				 const struct vfsmount *mnt);
+extern int gr_handle_ptrace(struct task_struct *task, const long request);
+extern int gr_handle_proc_ptrace(struct task_struct *task);
+extern int gr_handle_mmap(const struct file *filp, const unsigned long prot);
+extern __u32 gr_acl_handle_execve(const struct dentry *dentry,
+				  const struct vfsmount *mnt);
+extern int gr_check_crash_exec(const struct file *filp);
+extern int gr_acl_is_enabled(void);
+extern void gr_set_kernel_label(struct task_struct *task);
+extern void gr_set_role_label(struct task_struct *task, const uid_t uid,
+			      const gid_t gid);
+extern int gr_set_proc_label(const struct dentry *dentry,
+			      const struct vfsmount *mnt);
+extern __u32 gr_acl_handle_hidden_file(const struct dentry *dentry,
+				       const struct vfsmount *mnt);
+extern __u32 gr_acl_handle_open(const struct dentry *dentry,
+				const struct vfsmount *mnt, const int fmode);
+extern __u32 gr_acl_handle_creat(const struct dentry *dentry,
+				 const struct dentry *p_dentry,
+				 const struct vfsmount *p_mnt, const int fmode,
+				 const int imode);
+extern void gr_handle_create(const struct dentry *dentry,
+			     const struct vfsmount *mnt);
+extern __u32 gr_acl_handle_mknod(const struct dentry *new_dentry,
+				 const struct dentry *parent_dentry,
+				 const struct vfsmount *parent_mnt,
+				 const int mode);
+extern __u32 gr_acl_handle_mkdir(const struct dentry *new_dentry,
+				 const struct dentry *parent_dentry,
+				 const struct vfsmount *parent_mnt);
+extern __u32 gr_acl_handle_rmdir(const struct dentry *dentry,
+				 const struct vfsmount *mnt);
+extern void gr_handle_delete(const ino_t ino, const kdev_t dev);
+extern __u32 gr_acl_handle_unlink(const struct dentry *dentry,
+				  const struct vfsmount *mnt);
+extern __u32 gr_acl_handle_symlink(const struct dentry *new_dentry,
+				   const struct dentry *parent_dentry,
+				   const struct vfsmount *parent_mnt,
+				   const char *from);
+extern __u32 gr_acl_handle_link(const struct dentry *new_dentry,
+				const struct dentry *parent_dentry,
+				const struct vfsmount *parent_mnt,
+				const struct dentry *old_dentry,
+				const struct vfsmount *old_mnt, const char *to);
+extern int gr_acl_handle_rename(struct dentry *new_dentry,
+				struct dentry *parent_dentry,
+				const struct vfsmount *parent_mnt,
+				struct dentry *old_dentry,
+				struct inode *old_parent_inode,
+				struct vfsmount *old_mnt, const char *newname);
+extern __u32 gr_check_link(const struct dentry *new_dentry,
+			   const struct dentry *parent_dentry,
+			   const struct vfsmount *parent_mnt,
+			   const struct dentry *old_dentry,
+			   const struct vfsmount *old_mnt);
+extern __u32 gr_acl_handle_filldir(const struct dentry *dentry,
+				   const struct vfsmount *mnt, const ino_t ino);
+extern __u32 gr_acl_handle_unix(const struct dentry *dentry,
+				const struct vfsmount *mnt);
+extern void gr_set_pax_flags(struct task_struct *task);
+extern void gr_acl_handle_exit(void);
+extern void gr_acl_handle_psacct(struct task_struct *task, const long code);
+extern int gr_acl_handle_procpidmem(const struct task_struct *task);
+extern __u32 gr_cap_rtnetlink(void);
+
+#ifdef CONFIG_GRKERNSEC
+extern void gr_handle_mem_write(void);
+extern void gr_handle_kmem_write(void);
+extern void gr_handle_open_port(void);
+extern int gr_handle_mem_mmap(const unsigned long offset,
+			      struct vm_area_struct *vma);
+
+extern __u16 ip_randomid(void);
+extern __u32 ip_randomisn(void);
+extern unsigned long get_random_long(void);
+
+extern int grsec_enable_dmesg;
+extern int grsec_enable_randid;
+extern int grsec_enable_randisn;
+extern int grsec_enable_randsrc;
+extern int grsec_enable_randrpc;
+#endif
+
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/highmem.h linux-2.4.27-leo/include/linux/highmem.h
--- linux-2.4.27/include/linux/highmem.h	2004-02-20 13:06:56.000000000 +0000
+++ linux-2.4.27-leo/include/linux/highmem.h	2004-09-20 22:08:43.000000000 +0100
@@ -33,18 +33,8 @@
 {
 	unsigned long addr;
 
-	__save_flags(*flags);
+	local_irq_save(*flags);
 
-	/*
-	 * could be low
-	 */
-	if (!PageHighMem(bh->b_page))
-		return bh->b_data;
-
-	/*
-	 * it's a highmem page
-	 */
-	__cli();
 	addr = (unsigned long) kmap_atomic(bh->b_page, KM_BH_IRQ);
 
 	if (addr & ~PAGE_MASK)
@@ -58,7 +48,7 @@
 	unsigned long ptr = (unsigned long) buffer & PAGE_MASK;
 
 	kunmap_atomic((void *) ptr, KM_BH_IRQ);
-	__restore_flags(*flags);
+	local_irq_restore(*flags);
 }
 
 #else /* CONFIG_HIGHMEM */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/i2c-algo-8xx.h linux-2.4.27-leo/include/linux/i2c-algo-8xx.h
--- linux-2.4.27/include/linux/i2c-algo-8xx.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/include/linux/i2c-algo-8xx.h	2004-09-20 21:34:38.000000000 +0100
@@ -0,0 +1,43 @@
+/* ------------------------------------------------------------------------- */
+/* i2c-algo-8xx.h i2c driver algorithms for MPX8XX CPM			     */
+/*
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                */
+/* ------------------------------------------------------------------------- */
+
+/* $Id: i2c-algo-8xx.h,v 1.7 2003/08/01 20:56:38 khali Exp $ */
+
+#ifndef _LINUX_I2C_ALGO_8XX_H
+#define _LINUX_I2C_ALGO_8XX_H
+
+#include "asm/commproc.h"
+
+struct i2c_algo_8xx_data {
+	uint dp_addr;
+	int reloc;
+	volatile i2c8xx_t *i2c;
+	volatile iic_t	*iip;
+	volatile cpm8xx_t *cp;
+
+	int	(*setisr) (int irq,
+			   void (*func)(void *, void *),
+			   void *data);
+
+	u_char	temp[513];
+};
+
+int i2c_8xx_add_bus(struct i2c_adapter *);
+int i2c_8xx_del_bus(struct i2c_adapter *);
+
+#endif /* _LINUX_I2C_ALGO_8XX_H */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/i2c-algo-bit.h linux-2.4.27-leo/include/linux/i2c-algo-bit.h
--- linux-2.4.27/include/linux/i2c-algo-bit.h	2004-09-17 03:28:56.000000000 +0100
+++ linux-2.4.27-leo/include/linux/i2c-algo-bit.h	2004-09-20 21:34:38.000000000 +0100
@@ -21,12 +21,10 @@
 /* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
    Frodo Looijaard <frodol@dds.nl> */
 
-/* $Id: i2c-algo-bit.h,v 1.7 1999/12/21 23:45:58 frodo Exp $ */
+/* $Id: i2c-algo-bit.h,v 1.11 2003/07/25 07:56:42 khali Exp $ */
 
-#ifndef I2C_ALGO_BIT_H
-#define I2C_ALGO_BIT_H 1
-
-#include <linux/i2c.h>
+#ifndef _LINUX_I2C_ALGO_BIT_H
+#define _LINUX_I2C_ALGO_BIT_H
 
 /* --- Defines for bit-adapters ---------------------------------------	*/
 /*
@@ -42,9 +40,10 @@
 	int  (*getscl) (void *data);
 
 	/* local settings */
-	int udelay;
-	int mdelay;
-	int timeout;
+	int udelay;		/* half-clock-cycle time in microsecs */
+				/* i.e. clock is (500 / udelay) KHz */
+	int mdelay;		/* in millisecs, unused */
+	int timeout;		/* in jiffies */
 };
 
 #define I2C_BIT_ADAP_MAX	16
@@ -52,4 +51,4 @@
 int i2c_bit_add_bus(struct i2c_adapter *);
 int i2c_bit_del_bus(struct i2c_adapter *);
 
-#endif /* I2C_ALGO_BIT_H */
+#endif /* _LINUX_I2C_ALGO_BIT_H */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/i2c-algo-ibm_ocp.h linux-2.4.27-leo/include/linux/i2c-algo-ibm_ocp.h
--- linux-2.4.27/include/linux/i2c-algo-ibm_ocp.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/include/linux/i2c-algo-ibm_ocp.h	2004-09-20 21:34:38.000000000 +0100
@@ -0,0 +1,52 @@
+/* ------------------------------------------------------------------------- */
+/* i2c-algo-ibm_ocp.h i2c driver algorithms for IBM PPC 405 IIC adapters         */
+/* ------------------------------------------------------------------------- */
+/*   Copyright (C) 1995-97 Simon G. Vogl
+                   1998-99 Hans Berglund
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                */
+/* ------------------------------------------------------------------------- */
+
+/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
+   Frodo Looijaard <frodol@dds.nl> */
+
+/* Modifications by MontaVista Software, August 2000
+   Changes made to support the IIC peripheral on the IBM PPC 405 */
+
+#ifndef _LINUX_I2C_ALGO_IBM_OCP_H
+#define _LINUX_I2C_ALGO_IBM_OCP_H
+
+struct i2c_algo_iic_data {
+	struct iic_regs *data;		/* private data for lolevel routines	*/
+	void (*setiic) (void *data, int ctl, int val);
+	int  (*getiic) (void *data, int ctl);
+	int  (*getown) (void *data);
+	int  (*getclock) (void *data);
+	void (*waitforpin) (void *data);     
+
+	/* local settings */
+	int udelay;
+	int mdelay;
+	int timeout;
+};
+
+
+#define I2C_IIC_ADAP_MAX	16
+
+
+int i2c_ocp_add_bus(struct i2c_adapter *);
+int i2c_ocp_del_bus(struct i2c_adapter *);
+
+#endif /* _LINUX_I2C_ALGO_IBM_OCP_H */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/i2c-algo-pcf.h linux-2.4.27-leo/include/linux/i2c-algo-pcf.h
--- linux-2.4.27/include/linux/i2c-algo-pcf.h	2000-12-11 21:22:34.000000000 +0000
+++ linux-2.4.27-leo/include/linux/i2c-algo-pcf.h	2004-09-20 21:34:38.000000000 +0100
@@ -22,13 +22,12 @@
 /* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
    Frodo Looijaard <frodol@dds.nl> */
 
-/* $Id: i2c-algo-pcf.h,v 1.7 2000/02/27 23:02:45 frodo Exp $ */
+/* $Id: i2c-algo-pcf.h,v 1.9 2003/07/25 07:56:42 khali Exp $ */
 
-#ifndef I2C_ALGO_PCF_H
-#define I2C_ALGO_PCF_H 1
+#ifndef _LINUX_I2C_ALGO_PCF_H
+#define _LINUX_I2C_ALGO_PCF_H
 
-/* --- Defines for pcf-adapters ---------------------------------------	*/
-#include <linux/i2c.h>
+#include <linux/i2c-pcf8584.h>
 
 struct i2c_algo_pcf_data {
 	void *data;		/* private data for lolevel routines	*/
@@ -49,4 +48,4 @@
 int i2c_pcf_add_bus(struct i2c_adapter *);
 int i2c_pcf_del_bus(struct i2c_adapter *);
 
-#endif /* I2C_ALGO_PCF_H */
+#endif /* _LINUX_I2C_ALGO_PCF_H */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/i2c-dev.h linux-2.4.27-leo/include/linux/i2c-dev.h
--- linux-2.4.27/include/linux/i2c-dev.h	2004-09-17 03:32:16.000000000 +0100
+++ linux-2.4.27-leo/include/linux/i2c-dev.h	2004-09-20 22:02:17.000000000 +0100
@@ -19,14 +19,16 @@
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
-/* $Id: i2c-dev.h,v 1.9 2001/08/15 03:04:58 mds Exp $ */
-
-#ifndef I2C_DEV_H
-#define I2C_DEV_H
+/* $Id: i2c-dev.h,v 1.14 2003/07/25 07:56:42 khali Exp $ */
 
+#ifndef _LINUX_I2C_DEV_H
+#define _LINUX_I2C_DEV_H
 
 #include <linux/types.h>
-#include <linux/i2c.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18)
+#define minor(d) MINOR(d)
+#endif
 
 /* Some IOCTL commands are defined in <linux/i2c.h> */
 /* Note: 10-bit addresses are NOT supported! */
@@ -45,137 +47,4 @@
 	__u32 nmsgs;		/* number of i2c_msgs */
 };
 
-#ifndef __KERNEL__
-
-#include <sys/ioctl.h>
-
-static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command, 
-                                     int size, union i2c_smbus_data *data)
-{
-	struct i2c_smbus_ioctl_data args;
-
-	args.read_write = read_write;
-	args.command = command;
-	args.size = size;
-	args.data = data;
-	return ioctl(file,I2C_SMBUS,&args);
-}
-
-
-static inline __s32 i2c_smbus_write_quick(int file, __u8 value)
-{
-	return i2c_smbus_access(file,value,0,I2C_SMBUS_QUICK,NULL);
-}
-	
-static inline __s32 i2c_smbus_read_byte(int file)
-{
-	union i2c_smbus_data data;
-	if (i2c_smbus_access(file,I2C_SMBUS_READ,0,I2C_SMBUS_BYTE,&data))
-		return -1;
-	else
-		return 0x0FF & data.byte;
-}
-
-static inline __s32 i2c_smbus_write_byte(int file, __u8 value)
-{
-	return i2c_smbus_access(file,I2C_SMBUS_WRITE,value,
-	                        I2C_SMBUS_BYTE,NULL);
-}
-
-static inline __s32 i2c_smbus_read_byte_data(int file, __u8 command)
-{
-	union i2c_smbus_data data;
-	if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
-	                     I2C_SMBUS_BYTE_DATA,&data))
-		return -1;
-	else
-		return 0x0FF & data.byte;
-}
-
-static inline __s32 i2c_smbus_write_byte_data(int file, __u8 command, 
-                                              __u8 value)
-{
-	union i2c_smbus_data data;
-	data.byte = value;
-	return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
-	                        I2C_SMBUS_BYTE_DATA, &data);
-}
-
-static inline __s32 i2c_smbus_read_word_data(int file, __u8 command)
-{
-	union i2c_smbus_data data;
-	if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
-	                     I2C_SMBUS_WORD_DATA,&data))
-		return -1;
-	else
-		return 0x0FFFF & data.word;
-}
-
-static inline __s32 i2c_smbus_write_word_data(int file, __u8 command, 
-                                              __u16 value)
-{
-	union i2c_smbus_data data;
-	data.word = value;
-	return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
-	                        I2C_SMBUS_WORD_DATA, &data);
-}
-
-static inline __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value)
-{
-	union i2c_smbus_data data;
-	data.word = value;
-	if (i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
-	                     I2C_SMBUS_PROC_CALL,&data))
-		return -1;
-	else
-		return 0x0FFFF & data.word;
-}
-
-
-/* Returns the number of read bytes */
-static inline __s32 i2c_smbus_read_block_data(int file, __u8 command, 
-                                              __u8 *values)
-{
-	union i2c_smbus_data data;
-	int i;
-	if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
-	                     I2C_SMBUS_BLOCK_DATA,&data))
-		return -1;
-	else {
-		for (i = 1; i <= data.block[0]; i++)
-			values[i-1] = data.block[i];
-			return data.block[0];
-	}
-}
-
-static inline __s32 i2c_smbus_write_block_data(int file, __u8 command, 
-                                               __u8 length, __u8 *values)
-{
-	union i2c_smbus_data data;
-	int i;
-	if (length > 32)
-		length = 32;
-	for (i = 1; i <= length; i++)
-		data.block[i] = values[i-1];
-	data.block[0] = length;
-	return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
-	                        I2C_SMBUS_BLOCK_DATA, &data);
-}
-
-static inline __s32 i2c_smbus_write_i2c_block_data(int file, __u8 command,
-                                               __u8 length, __u8 *values)
-{
-	union i2c_smbus_data data;
-	int i;
-	if (length > 32)
-		length = 32;
-	for (i = 1; i <= length; i++)
-		data.block[i] = values[i-1];
-	data.block[0] = length;
-	return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
-	                        I2C_SMBUS_I2C_BLOCK_DATA, &data);
-}
-
-#endif /* ndef __KERNEL__ */
-
-#endif
+#endif /* _LINUX_I2C_DEV_H */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/i2c-elektor.h linux-2.4.27-leo/include/linux/i2c-elektor.h
--- linux-2.4.27/include/linux/i2c-elektor.h	2001-10-11 16:05:47.000000000 +0100
+++ linux-2.4.27-leo/include/linux/i2c-elektor.h	1970-01-01 01:00:00.000000000 +0100
@@ -1,47 +0,0 @@
-/* ------------------------------------------------------------------------- */
-/* i2c-elektor.c i2c-hw access for PCF8584 style isa bus adaptes             */
-/* ------------------------------------------------------------------------- */
-/*   Copyright (C) 1995-97 Simon G. Vogl
-                   1998-99 Hans Berglund
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                */
-/* ------------------------------------------------------------------------- */
-
-/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
-   Frodo Looijaard <frodol@dds.nl> */
-
-/* $Id: i2c-elektor.h,v 1.5 2001/06/05 01:46:33 mds Exp $ */
-
-#ifndef I2C_PCF_ELEKTOR_H
-#define I2C_PCF_ELEKTOR_H 1
-
-/*
- * This struct contains the hw-dependent functions of PCF8584 adapters to
- * manipulate the registers, and to init any hw-specific features.
- * vdovikin: removed: this module in real supports only one device,
- * due to missing arguments in some functions, called from the algo-pcf module.
- * Sometimes it's need to be rewriten -
- * but for now just remove this for simpler reading */
-
-/*
-struct i2c_pcf_isa {
-	int pi_base;
-	int pi_irq;
-	int pi_clock;
-	int pi_own;
-};
-*/
-
-#endif /* PCF_ELEKTOR_H */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/i2c.h linux-2.4.27-leo/include/linux/i2c.h
--- linux-2.4.27/include/linux/i2c.h	2004-09-17 03:28:56.000000000 +0100
+++ linux-2.4.27-leo/include/linux/i2c.h	2004-09-20 22:02:16.000000000 +0100
@@ -23,36 +23,33 @@
 /* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and
    Frodo Looijaard <frodol@dds.nl> */
 
-/* $Id: i2c.h,v 1.46 2001/08/31 00:04:07 phil Exp $ */
+/* $Id: i2c.h,v 1.79 2004/06/11 18:58:43 phil Exp $ */
 
-#ifndef I2C_H
-#define I2C_H
+#ifndef _LINUX_I2C_H
+#define _LINUX_I2C_H
 
-#define I2C_DATE "20010830"
-#define I2C_VERSION "2.6.1"
+#define I2C_DATE "20040611"
+#define I2C_VERSION "2.8.7"
 
-#include <linux/i2c-id.h>	/* id values of adapters et. al. 	*/
+#include <linux/module.h>
 #include <linux/types.h>
-
-
-struct i2c_msg;
-
-
-#ifdef __KERNEL__
-
-/* --- Includes and compatibility declarations ------------------------ */
-
+#include <linux/errno.h>
+#include <linux/sched.h>
 #include <asm/semaphore.h>
-#include <linux/config.h>
+#include <linux/i2c-id.h>
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10)
+#define MODULE_LICENSE(x)
+#endif
 
 /* --- General options ------------------------------------------------	*/
 
-#define I2C_ALGO_MAX	4		/* control memory consumption	*/
-#define I2C_ADAP_MAX	16
+#define I2C_ADAP_MAX	16		/* control memory consumption	*/
 #define I2C_DRIVER_MAX	16
 #define I2C_CLIENT_MAX	32
-#define I2C_DUMMY_MAX 4
 
+struct i2c_msg;
 struct i2c_algorithm;
 struct i2c_adapter;
 struct i2c_client;
@@ -60,7 +57,6 @@
 struct i2c_client_address_data;
 union i2c_smbus_data;
 
-
 /*
  * The master routines are the ones normally used to transmit data to devices
  * on a bus (or read from them). Apart from two basic transfer functions to 
@@ -113,6 +109,8 @@
 extern s32 i2c_smbus_write_block_data(struct i2c_client * client,
                                       u8 command, u8 length,
                                       u8 *values);
+extern s32 i2c_smbus_read_i2c_block_data(struct i2c_client * client,
+                                         u8 command, u8 *values);
 extern s32 i2c_smbus_write_i2c_block_data(struct i2c_client * client,
                                           u8 command, u8 length,
                                           u8 *values);
@@ -125,6 +123,7 @@
  */
 
 struct i2c_driver {
+	struct module *owner;
 	char name[32];
 	int id;
 	unsigned int flags;		/* div., see below		*/
@@ -148,18 +147,6 @@
 	 * with the device.
 	 */
 	int (*command)(struct i2c_client *client,unsigned int cmd, void *arg);
-	
-	/* These two are mainly used for bookkeeping & dynamic unloading of 
-	 * kernel modules. inc_use tells the driver that a client is being  
-	 * used by another module & that it should increase its ref. counter.
-	 * dec_use is the inverse operation.
-	 * NB: Make sure you have no circular dependencies, or else you get a 
-	 * deadlock when trying to unload the modules.
-	* You should use the i2c_{inc,dec}_use_client functions instead of
-	* calling this function directly.
-	 */
-	void (*inc_use)(struct i2c_client *client);
-	void (*dec_use)(struct i2c_client *client);
 };
 
 /*
@@ -192,6 +179,7 @@
  * to name two of the most common.
  */
 struct i2c_algorithm {
+	struct module *owner;			/* future use --km	*/
 	char name[32];				/* textual description 	*/
 	unsigned int id;
 
@@ -221,16 +209,13 @@
  * with the access algorithms necessary to access it.
  */
 struct i2c_adapter {
+	struct module *owner;
 	char name[32];	/* some useful name to identify the adapter	*/
 	unsigned int id;/* == is algo->id | hwdep.struct->id, 		*/
 			/* for registered values see below		*/
 	struct i2c_algorithm *algo;/* the algorithm to access the bus	*/
 	void *algo_data;
 
-	/* --- These may be NULL, but should increase the module use count */
-	void (*inc_use)(struct i2c_adapter *);
-	void (*dec_use)(struct i2c_adapter *);
-
 	/* --- administration stuff. */
 	int (*client_register)(struct i2c_client *);
 	int (*client_unregister)(struct i2c_client *);
@@ -241,11 +226,11 @@
 			/* and can be set via the i2c_ioctl call	*/
 
 			/* data fields that are valid for all devices	*/
-	struct semaphore lock;  
+	struct semaphore bus;
+	struct semaphore list;  
 	unsigned int flags;/* flags specifying div. data		*/
 
 	struct i2c_client *clients[I2C_CLIENT_MAX];
-	int client_count;
 
 	int timeout;
 	int retries;
@@ -264,6 +249,9 @@
 #define I2C_CLIENT_ALLOW_USE		0x01	/* Client allows access */
 #define I2C_CLIENT_ALLOW_MULTIPLE_USE 	0x02  	/* Allow multiple access-locks */
 						/* on an i2c_client */
+#define I2C_CLIENT_PEC  0x04			/* Use Packet Error Checking */
+#define I2C_CLIENT_TEN	0x10			/* we have a ten bit chip address	*/
+						/* Must equal I2C_M_TEN below */
 
 /* i2c_client_address_data is the struct for holding default client
  * addresses for a driver and for the parameters supplied on the
@@ -302,12 +290,6 @@
 extern int i2c_attach_client(struct i2c_client *);
 extern int i2c_detach_client(struct i2c_client *);
 
-/* Only call these if you grab a resource that makes unloading the
-   client and the adapter it is on completely impossible. Like when a
-   /proc directory is entered. */
-extern void i2c_inc_use_client(struct i2c_client *);
-extern void i2c_dec_use_client(struct i2c_client *);
-
 /* New function: This is to get an i2c_client-struct for controlling the 
    client either by using i2c_control-function or having the 
    client-module export functions that can be used with the i2c_client
@@ -341,6 +323,15 @@
 		struct i2c_client_address_data *address_data,
 		i2c_client_found_addr_proc *found_proc);
 
+static inline int i2c_client_command(struct i2c_client *client,
+				     unsigned int cmd, void *arg)
+{
+	if (client->driver && client->driver->command)
+		return client->driver->command(client, cmd, arg);
+	else
+		return -EINVAL;
+}
+
 /* An ioctl like call to set div. parameters of the adapter.
  */
 extern int i2c_control(struct i2c_client *,unsigned int, unsigned long);
@@ -358,8 +349,6 @@
 /* Return 1 if adapter supports everything we need, 0 if not. */
 extern int i2c_check_functionality (struct i2c_adapter *adap, u32 func);
 
-#endif /* __KERNEL__ */
-
 /*
  * I2C Message - used for pure i2c transaction, also from /dev interface
  */
@@ -370,15 +359,28 @@
 #define I2C_M_RD	0x01
 #define I2C_M_NOSTART	0x4000
 #define I2C_M_REV_DIR_ADDR	0x2000
+#define I2C_M_IGNORE_NAK	0x1000
+#define I2C_M_NO_RD_ACK		0x0800
+#define I2C_M_RECV_LEN		0x0400 /* length will be first received byte */
+#define I2C_M_RECV_PEC		0x0200 /* receive one more than the returned
+					  length byte for the PEC */
 	__u16 len;		/* msg length				*/
 	__u8 *buf;		/* pointer to msg data			*/
+	int err;
+	short done;
 };
 
 /* To determine what functionality is present */
 
 #define I2C_FUNC_I2C			0x00000001
 #define I2C_FUNC_10BIT_ADDR		0x00000002
-#define I2C_FUNC_PROTOCOL_MANGLING	0x00000004 /* I2C_M_{REV_DIR_ADDR,NOSTART} */
+#define I2C_FUNC_PROTOCOL_MANGLING	0x00000004 /* I2C_M_{REV_DIR_ADDR,NOSTART,..} */
+#define I2C_FUNC_SMBUS_HWPEC_CALC	0x00000008 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_READ_WORD_DATA_PEC  0x00000800 /* SMBus 2.0 */ 
+#define I2C_FUNC_SMBUS_WRITE_WORD_DATA_PEC 0x00001000 /* SMBus 2.0 */ 
+#define I2C_FUNC_SMBUS_PROC_CALL_PEC	0x00002000 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL_PEC 0x00004000 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL	0x00008000 /* SMBus 2.0 */
 #define I2C_FUNC_SMBUS_QUICK		0x00010000 
 #define I2C_FUNC_SMBUS_READ_BYTE	0x00020000 
 #define I2C_FUNC_SMBUS_WRITE_BYTE	0x00040000 
@@ -389,8 +391,12 @@
 #define I2C_FUNC_SMBUS_PROC_CALL	0x00800000 
 #define I2C_FUNC_SMBUS_READ_BLOCK_DATA	0x01000000 
 #define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000 
-#define I2C_FUNC_SMBUS_READ_I2C_BLOCK	0x04000000 /* New I2C-like block */
-#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK	0x08000000 /* transfer */
+#define I2C_FUNC_SMBUS_READ_I2C_BLOCK	0x04000000 /* I2C-like block xfer  */
+#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK	0x08000000 /* w/ 1-byte reg. addr. */
+#define I2C_FUNC_SMBUS_READ_I2C_BLOCK_2	 0x10000000 /* I2C-like block xfer  */
+#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK_2 0x20000000 /* w/ 2-byte reg. addr. */
+#define I2C_FUNC_SMBUS_READ_BLOCK_DATA_PEC  0x40000000 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA_PEC 0x80000000 /* SMBus 2.0 */
 
 #define I2C_FUNC_SMBUS_BYTE I2C_FUNC_SMBUS_READ_BYTE | \
                             I2C_FUNC_SMBUS_WRITE_BYTE
@@ -402,13 +408,28 @@
                                   I2C_FUNC_SMBUS_WRITE_BLOCK_DATA
 #define I2C_FUNC_SMBUS_I2C_BLOCK I2C_FUNC_SMBUS_READ_I2C_BLOCK | \
                                   I2C_FUNC_SMBUS_WRITE_I2C_BLOCK
+#define I2C_FUNC_SMBUS_I2C_BLOCK_2 I2C_FUNC_SMBUS_READ_I2C_BLOCK_2 | \
+                                   I2C_FUNC_SMBUS_WRITE_I2C_BLOCK_2
+#define I2C_FUNC_SMBUS_BLOCK_DATA_PEC I2C_FUNC_SMBUS_READ_BLOCK_DATA_PEC | \
+                                      I2C_FUNC_SMBUS_WRITE_BLOCK_DATA_PEC
+#define I2C_FUNC_SMBUS_WORD_DATA_PEC  I2C_FUNC_SMBUS_READ_WORD_DATA_PEC | \
+                                      I2C_FUNC_SMBUS_WRITE_WORD_DATA_PEC
+
+#define I2C_FUNC_SMBUS_READ_BYTE_PEC		I2C_FUNC_SMBUS_READ_BYTE_DATA
+#define I2C_FUNC_SMBUS_WRITE_BYTE_PEC		I2C_FUNC_SMBUS_WRITE_BYTE_DATA
+#define I2C_FUNC_SMBUS_READ_BYTE_DATA_PEC	I2C_FUNC_SMBUS_READ_WORD_DATA
+#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA_PEC	I2C_FUNC_SMBUS_WRITE_WORD_DATA
+#define I2C_FUNC_SMBUS_BYTE_PEC			I2C_FUNC_SMBUS_BYTE_DATA
+#define I2C_FUNC_SMBUS_BYTE_DATA_PEC		I2C_FUNC_SMBUS_WORD_DATA
 
 #define I2C_FUNC_SMBUS_EMUL I2C_FUNC_SMBUS_QUICK | \
                             I2C_FUNC_SMBUS_BYTE | \
                             I2C_FUNC_SMBUS_BYTE_DATA | \
                             I2C_FUNC_SMBUS_WORD_DATA | \
                             I2C_FUNC_SMBUS_PROC_CALL | \
-                            I2C_FUNC_SMBUS_WRITE_BLOCK_DATA
+                            I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \
+                            I2C_FUNC_SMBUS_WRITE_BLOCK_DATA_PEC | \
+                            I2C_FUNC_SMBUS_I2C_BLOCK
 
 /* 
  * Data for SMBus Messages 
@@ -418,8 +439,9 @@
 union i2c_smbus_data {
 	__u8 byte;
 	__u16 word;
-	__u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */
+	__u8 block[I2C_SMBUS_BLOCK_MAX + 3]; /* block[0] is used for length */
 	                  /* one more for read length in block process call */
+	                                            /* and one more for PEC */
 };
 
 /* smbus_access read or write markers */
@@ -435,6 +457,11 @@
 #define I2C_SMBUS_PROC_CALL	    4
 #define I2C_SMBUS_BLOCK_DATA	    5
 #define I2C_SMBUS_I2C_BLOCK_DATA    6
+#define I2C_SMBUS_BLOCK_PROC_CALL   7		/* SMBus 2.0 */
+#define I2C_SMBUS_BLOCK_DATA_PEC    8		/* SMBus 2.0 */
+#define I2C_SMBUS_PROC_CALL_PEC     9		/* SMBus 2.0 */
+#define I2C_SMBUS_BLOCK_PROC_CALL_PEC  10	/* SMBus 2.0 */
+#define I2C_SMBUS_WORD_DATA_PEC	   11		/* SMBus 2.0 */
 
 
 /* ----- commands for the ioctl like i2c_command call:
@@ -460,6 +487,7 @@
 
 #define I2C_FUNCS	0x0705	/* Get the adapter functionality */
 #define I2C_RDWR	0x0707	/* Combined R/W transfer (one stop only)*/
+#define I2C_PEC		0x0708	/* != 0 for SMBus PEC                   */
 #if 0
 #define I2C_ACK_TEST	0x0710	/* See if a slave is at a specific address */
 #endif
@@ -475,16 +503,6 @@
 
 #define I2C_MAJOR	89		/* Device major number		*/
 
-#ifdef __KERNEL__
-
-#  ifndef NULL
-#    define NULL ( (void *) 0 )
-#  endif
-
-#  ifndef ENODEV
-#    include <asm/errno.h>
-#  endif
-
 /* These defines are used for probing i2c client addresses */
 /* Default fill of many variables */
 #define I2C_CLIENT_DEFAULTS {I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \
@@ -546,5 +564,11 @@
 #define i2c_is_isa_adapter(adapptr) \
         ((adapptr)->algo->id == I2C_ALGO_ISA)
 
-#endif /* def __KERNEL__ */
-#endif /* I2C_H */
+/* Tiny delay function used by the i2c bus drivers */
+static inline void i2c_delay(signed long timeout)
+{
+	set_current_state(TASK_INTERRUPTIBLE);
+	schedule_timeout(timeout);
+}
+
+#endif /* _LINUX_I2C_H */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/i2c-id.h linux-2.4.27-leo/include/linux/i2c-id.h
--- linux-2.4.27/include/linux/i2c-id.h	2004-02-20 14:11:47.000000000 +0000
+++ linux-2.4.27-leo/include/linux/i2c-id.h	2004-09-20 21:34:38.000000000 +0100
@@ -20,10 +20,11 @@
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.		     */
 /* ------------------------------------------------------------------------- */
 
-/* $Id: i2c-id.h,v 1.35 2001/08/12 17:22:20 mds Exp $ */
+/* $Id: i2c-id.h,v 1.91 2004/03/05 20:31:49 khali Exp $ */
+
+#ifndef LINUX_I2C_ID_H
+#define LINUX_I2C_ID_H
 
-#ifndef I2C_ID_H
-#define I2C_ID_H
 /*
  * This file is part of the i2c-bus package and contains the identifier
  * values for drivers, adapters and other folk populating these serial
@@ -90,10 +91,25 @@
 #define I2C_DRIVERID_DRP3510	43     /* ADR decoder (Astra Radio)	*/
 #define I2C_DRIVERID_SP5055	44     /* Satellite tuner		*/
 #define I2C_DRIVERID_STV0030	45     /* Multipurpose switch		*/
+#define I2C_DRIVERID_SAA7108	46     /* video decoder, image scaler   */
+#define I2C_DRIVERID_DS1307	47     /* DS1307 real time clock	*/
 #define I2C_DRIVERID_ADV7175	48     /* ADV 7175/7176 video encoder	*/
-#define I2C_DRIVERID_MAX1617	56     /* temp sensor			*/
-#define I2C_DRIVERID_SAA7191	57     /* video decoder                 */
-#define I2C_DRIVERID_INDYCAM	58     /* SGI IndyCam			*/
+#define I2C_DRIVERID_ZR36067	49     /* Zoran 36067 video encoder	*/
+#define I2C_DRIVERID_ZR36120	50     /* Zoran 36120 video encoder	*/
+#define I2C_DRIVERID_24LC32A	51	/* Microchip 24LC32A 32k EEPROM	*/
+#define I2C_DRIVERID_STM41T00	52	/* real time clock		*/
+#define I2C_DRIVERID_UDA1342	53	/* UDA1342 audio codec		*/
+#define I2C_DRIVERID_ADV7170	54	/* video encoder		*/
+#define I2C_DRIVERID_RADEON	55	/* I2C bus on Radeon boards	*/
+#define I2C_DRIVERID_MAX1617	56	/* temp sensor			*/
+#define I2C_DRIVERID_SAA7191	57	/* video encoder		*/
+#define I2C_DRIVERID_INDYCAM	58	/* SGI IndyCam			*/
+#define I2C_DRIVERID_BT832	59	/* CMOS camera video processor  */
+#define I2C_DRIVERID_TDA9887	60	/* TDA988x IF-PLL demodulator	*/
+#define I2C_DRIVERID_OVCAMCHIP	61	/* OmniVision CMOS image sens.	*/
+#define I2C_DRIVERID_TDA7313	62	/* TDA7313 audio processor	*/
+#define I2C_DRIVERID_MAX6900	63	/* MAX6900 real-time clock	*/
+
 
 #define I2C_DRIVERID_EXP0	0xF0	/* experimental use id's	*/
 #define I2C_DRIVERID_EXP1	0xF1
@@ -102,6 +118,8 @@
 
 #define I2C_DRIVERID_I2CDEV	900
 #define I2C_DRIVERID_I2CPROC	901
+#define I2C_DRIVERID_ARP        902    /* SMBus ARP Client              */
+#define I2C_DRIVERID_ALERT      903    /* SMBus Alert Responder Client  */
 
 /* IDs --   Use DRIVERIDs 1000-1999 for sensors. 
    These were originally in sensors.h in the lm_sensors package */
@@ -131,6 +149,27 @@
 #define I2C_DRIVERID_ADM1024 1025
 #define I2C_DRIVERID_IT87 1026
 #define I2C_DRIVERID_CH700X 1027 /* single driver for CH7003-7009 digital pc to tv encoders */
+#define I2C_DRIVERID_FSCPOS 1028
+#define I2C_DRIVERID_FSCSCY 1029
+#define I2C_DRIVERID_PCF8591 1030
+#define I2C_DRIVERID_SMSC47M1 1031
+#define I2C_DRIVERID_VT1211 1032
+#define I2C_DRIVERID_LM92 1033
+#define I2C_DRIVERID_VT8231 1034
+#define I2C_DRIVERID_SMARTBATT 1035
+#define I2C_DRIVERID_BMCSENSORS 1036
+#define I2C_DRIVERID_FS451 1037
+#define I2C_DRIVERID_W83627HF 1038
+#define I2C_DRIVERID_LM85 1039
+#define I2C_DRIVERID_LM83 1040
+#define I2C_DRIVERID_SAA1064 1041
+#define I2C_DRIVERID_LM90 1042
+#define I2C_DRIVERID_ASB100 1043
+#define I2C_DRIVERID_MAX6650 1044
+#define I2C_DRIVERID_XEONTEMP 1045
+#define I2C_DRIVERID_FSCHER 1046
+#define I2C_DRIVERID_W83L785TS 1047
+#define I2C_DRIVERID_ADM1026 1048
 
 /*
  * ---- Adapter types ----------------------------------------------------
@@ -147,14 +186,21 @@
 #define I2C_ALGO_ISA 	0x050000	/* lm_sensors ISA pseudo-adapter */
 #define I2C_ALGO_SAA7146 0x060000	/* SAA 7146 video decoder bus	*/
 #define I2C_ALGO_ACB 	0x070000	/* ACCESS.bus algorithm         */
-
+#define I2C_ALGO_IIC    0x080000 	/* ITE IIC bus */
+#define I2C_ALGO_SAA7134 0x090000
+#define I2C_ALGO_MPC824X 0x0a0000	/* Motorola 8240 / 8245         */
+#define I2C_ALGO_IPMI 	0x0b0000	/* IPMI dummy adapter */
+#define I2C_ALGO_IPMB 	0x0c0000	/* IPMB adapter */
+#define I2C_ALGO_MPC107 0x0d0000
 #define I2C_ALGO_EC     0x100000        /* ACPI embedded controller     */
 
 #define I2C_ALGO_MPC8XX 0x110000	/* MPC8xx PowerPC I2C algorithm */
-
-#define I2C_ALGO_SIBYTE 0x120000	/* Broadcom SiByte SOCs		*/
-
-#define I2C_ALGO_SGI	0x130000	/* SGI algorithm		*/
+#define I2C_ALGO_OCP    0x120000	/* IBM or otherwise On-chip I2C algorithm */
+#define I2C_ALGO_BITHS	0x130000	/* enhanced bit style adapters	*/
+#define I2C_ALGO_OCP_IOP3XX  0x140000	/* XSCALE IOP3XX On-chip I2C alg */
+#define I2C_ALGO_SIBYTE	0x150000	/* Broadcom SiByte SOCs         */
+#define I2C_ALGO_SGI	0x160000	/* SGI algorithm                */
+#define I2C_ALGO_USB	0x170000	/* USB algorithm		*/
 
 #define I2C_ALGO_EXP	0x800000	/* experimental			*/
 
@@ -182,21 +228,46 @@
 #define I2C_HW_B_I810	0x0a	/* Intel I810 				*/
 #define I2C_HW_B_VOO	0x0b	/* 3dfx Voodoo 3 / Banshee      	*/
 #define I2C_HW_B_PPORT  0x0c	/* Primitive parallel port adapter	*/
+#define I2C_HW_B_SAVG	0x0d	/* Savage 4                     	*/
+#define I2C_HW_B_SCX200	0x0e	/* Nat'l Semi SCx200 I2C        	*/
 #define I2C_HW_B_RIVA	0x10	/* Riva based graphics cards		*/
 #define I2C_HW_B_IOC	0x11	/* IOC bit-wiggling			*/
 #define I2C_HW_B_TSUNA  0x12	/* DEC Tsunami chipset			*/
+#define I2C_HW_B_FRODO  0x13    /* 2d3D, Inc. SA-1110 Development Board */
+#define I2C_HW_B_OMAHA  0x14    /* Omaha I2C interface (ARM)		*/
+#define I2C_HW_B_GUIDE  0x15    /* Guide bit-basher			*/
+#define I2C_HW_B_IXP2000 0x16	/* GPIO on IXP2000 systems              */
+#define I2C_HW_B_IXP425 0x17	/* GPIO on IXP425 systems		*/
+#define I2C_HW_B_S3VIA	0x18	/* S3Via ProSavage adapter		*/
+#define I2C_HW_B_ZR36067 0x19	/* Zoran-36057/36067 based boards	*/
+#define I2C_HW_B_PCILYNX 0x1a	/* TI PCILynx I2C adapter		*/
 
 /* --- PCF 8584 based algorithms					*/
 #define I2C_HW_P_LP	0x00	/* Parallel port interface		*/
 #define I2C_HW_P_ISA	0x01	/* generic ISA Bus inteface card	*/
 #define I2C_HW_P_ELEK	0x02	/* Elektor ISA Bus inteface card	*/
 
+/* --- USB based adapters						*/
+#define I2C_HW_USB_USBVISION	0x00
+
 /* --- ACPI Embedded controller algorithms                              */
 #define I2C_HW_ACPI_EC          0x00
 
+/* --- MPC824x PowerPC adapters						*/
+#define I2C_HW_MPC824X 0x00	/* Motorola 8240 / 8245                 */
+
 /* --- MPC8xx PowerPC adapters						*/
 #define I2C_HW_MPC8XX_EPON 0x00	/* Eponymous MPC8xx I2C adapter 	*/
 
+/* --- ITE based algorithms						*/
+#define I2C_HW_I_IIC	0x00	/* controller on the ITE */
+
+/* --- PowerPC on-chip adapters						*/
+#define I2C_HW_OCP 0x00	/* IBM on-chip I2C adapter 	*/
+
+/* --- XSCALE on-chip adapters                          */
+#define I2C_HW_IOP321 0x00
+
 /* --- Broadcom SiByte adapters						*/
 #define I2C_HW_SIBYTE	0x00
 
@@ -213,9 +284,27 @@
 #define I2C_HW_SMBUS_AMD756	0x05
 #define I2C_HW_SMBUS_SIS5595	0x06
 #define I2C_HW_SMBUS_ALI1535	0x07
+#define I2C_HW_SMBUS_SIS630	0x08
+#define I2C_HW_SMBUS_SIS645	0x09
+#define I2C_HW_SMBUS_AMD8111	0x0a
+#define I2C_HW_SMBUS_SCX200	0x0b
+#define I2C_HW_SMBUS_NFORCE2	0x0c
 #define I2C_HW_SMBUS_W9968CF	0x0d
+#define I2C_HW_SMBUS_OV511	0x0e	/* OV511(+) USB 1.1 webcam ICs	*/
+#define I2C_HW_SMBUS_OV518	0x0f	/* OV518(+) USB 1.1 webcam ICs	*/
+#define I2C_HW_SMBUS_OV519	0x10	/* OV519 USB 1.1 webcam IC	*/
+#define I2C_HW_SMBUS_OVFX2	0x11	/* Cypress/OmniVision FX2 webcam */
 
 /* --- ISA pseudo-adapter						*/
 #define I2C_HW_ISA 0x00
 
-#endif /* I2C_ID_H */
+/* --- IPMI pseudo-adapter						*/
+#define I2C_HW_IPMI 0x00
+
+/* --- IPMB adapter						*/
+#define I2C_HW_IPMB 0x00
+
+/* --- MCP107 adapter */
+#define I2C_HW_MPC107 0x00
+
+#endif /* LINUX_I2C_ID_H */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/i2c-pcf8584.h linux-2.4.27-leo/include/linux/i2c-pcf8584.h
--- linux-2.4.27/include/linux/i2c-pcf8584.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/include/linux/i2c-pcf8584.h	2004-09-20 21:34:38.000000000 +0100
@@ -0,0 +1,78 @@
+/* -------------------------------------------------------------------- */
+/* i2c-pcf8584.h: PCF 8584 global defines				*/
+/* -------------------------------------------------------------------- */
+/*   Copyright (C) 1996 Simon G. Vogl
+                   1999 Hans Berglund
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.		*/
+/* --------------------------------------------------------------------	*/
+
+/* With some changes from Frodo Looijaard <frodol@dds.nl> */
+
+/* $Id: i2c-pcf8584.h,v 1.6 2003/07/25 07:56:42 khali Exp $ */
+
+#ifndef _LINUX_I2C_PCF8584_H
+#define _LINUX_I2C_PCF8584_H
+
+/* ----- Control register bits ----------------------------------------	*/
+#define I2C_PCF_PIN	0x80
+#define I2C_PCF_ESO	0x40
+#define I2C_PCF_ES1	0x20
+#define I2C_PCF_ES2	0x10
+#define I2C_PCF_ENI	0x08
+#define I2C_PCF_STA	0x04
+#define I2C_PCF_STO	0x02
+#define I2C_PCF_ACK	0x01
+
+#define I2C_PCF_START    (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK)
+#define I2C_PCF_STOP     (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STO | I2C_PCF_ACK)
+#define I2C_PCF_REPSTART (              I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK)
+#define I2C_PCF_IDLE     (I2C_PCF_PIN | I2C_PCF_ESO               | I2C_PCF_ACK)
+
+/* ----- Status register bits -----------------------------------------	*/
+/*#define I2C_PCF_PIN  0x80    as above*/
+
+#define I2C_PCF_INI 0x40   /* 1 if not initialized */
+#define I2C_PCF_STS 0x20
+#define I2C_PCF_BER 0x10
+#define I2C_PCF_AD0 0x08
+#define I2C_PCF_LRB 0x08
+#define I2C_PCF_AAS 0x04
+#define I2C_PCF_LAB 0x02
+#define I2C_PCF_BB  0x01
+
+/* ----- Chip clock frequencies ---------------------------------------	*/
+#define I2C_PCF_CLK3	0x00
+#define I2C_PCF_CLK443	0x10
+#define I2C_PCF_CLK6	0x14
+#define I2C_PCF_CLK	0x18
+#define I2C_PCF_CLK12	0x1c
+
+/* ----- transmission frequencies -------------------------------------	*/
+#define I2C_PCF_TRNS90 0x00	/*  90 kHz */
+#define I2C_PCF_TRNS45 0x01	/*  45 kHz */
+#define I2C_PCF_TRNS11 0x02	/*  11 kHz */
+#define I2C_PCF_TRNS15 0x03	/* 1.5 kHz */
+
+
+/* ----- Access to internal registers according to ES1,ES2 ------------	*/
+/* they are mapped to the data port ( a0 = 0 ) 				*/
+/* available when ESO == 0 :						*/
+
+#define I2C_PCF_OWNADR	0
+#define I2C_PCF_INTREG	I2C_PCF_ES2
+#define I2C_PCF_CLKREG	I2C_PCF_ES1
+
+#endif /* _LINUX_I2C_PCF8584_H */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/i2c-proc.h linux-2.4.27-leo/include/linux/i2c-proc.h
--- linux-2.4.27/include/linux/i2c-proc.h	2004-09-17 03:32:17.000000000 +0100
+++ linux-2.4.27-leo/include/linux/i2c-proc.h	2004-09-20 22:02:17.000000000 +0100
@@ -1,6 +1,7 @@
 /*
-    sensors.h - Part of lm_sensors, Linux kernel modules for hardware
-                monitoring
+    i2c-proc.h - Part of the i2c package
+    was originally sensors.h - Part of lm_sensors, Linux kernel modules
+                               for hardware monitoring
     Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
 
     This program is free software; you can redistribute it and/or modify
@@ -18,14 +19,9 @@
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
-#ifndef SENSORS_SENSORS_H
-#define SENSORS_SENSORS_H
+#ifndef _LINUX_I2C_PROC_H
+#define _LINUX_I2C_PROC_H
 
-#ifdef __KERNEL__
-
-/* Next two must be included before sysctl.h can be included, in 2.0 kernels */
-#include <linux/types.h>
-#include <linux/fs.h>
 #include <linux/sysctl.h>
 
 /* The type of callback functions used in sensors_{proc,sysctl}_real */
@@ -73,8 +69,7 @@
    these functions must be updated! */
 extern int i2c_register_entry(struct i2c_client *client,
 				  const char *prefix,
-				  ctl_table * ctl_template,
-				  struct module *controlling_mod);
+				  ctl_table * ctl_template);
 
 extern void i2c_deregister_entry(int id);
 
@@ -347,6 +342,31 @@
                                                  {NULL}}; \
   SENSORS_INSMOD
 
+#define SENSORS_INSMOD_8(chip1,chip2,chip3,chip4,chip5,chip6,chip7,chip8) \
+  enum chips { any_chip, chip1, chip2, chip3, chip4, chip5, chip6, chip7, chip8 }; \
+  SENSORS_MODULE_PARM(force, \
+                      "List of adapter,address pairs to boldly assume " \
+                      "to be present"); \
+  SENSORS_MODULE_PARM_FORCE(chip1); \
+  SENSORS_MODULE_PARM_FORCE(chip2); \
+  SENSORS_MODULE_PARM_FORCE(chip3); \
+  SENSORS_MODULE_PARM_FORCE(chip4); \
+  SENSORS_MODULE_PARM_FORCE(chip5); \
+  SENSORS_MODULE_PARM_FORCE(chip6); \
+  SENSORS_MODULE_PARM_FORCE(chip7); \
+  SENSORS_MODULE_PARM_FORCE(chip8); \
+  static struct i2c_force_data forces[] = {{force,any_chip}, \
+                                                 {force_ ## chip1,chip1}, \
+                                                 {force_ ## chip2,chip2}, \
+                                                 {force_ ## chip3,chip3}, \
+                                                 {force_ ## chip4,chip4}, \
+                                                 {force_ ## chip5,chip5}, \
+                                                 {force_ ## chip6,chip6}, \
+                                                 {force_ ## chip7,chip7}, \
+                                                 {force_ ## chip8,chip8}, \
+                                                 {NULL}}; \
+  SENSORS_INSMOD
+
 typedef int i2c_found_addr_proc(struct i2c_adapter *adapter,
 				    int addr, unsigned short flags,
 				    int kind);
@@ -362,7 +382,7 @@
 
 /* This macro is used to scale user-input to sensible values in almost all
    chip drivers. */
-extern inline int SENSORS_LIMIT(long value, long low, long high)
+static inline int SENSORS_LIMIT(long value, long low, long high)
 {
 	if (value < low)
 		return low;
@@ -372,8 +392,6 @@
 		return value;
 }
 
-#endif				/* def __KERNEL__ */
-
 
 /* The maximum length of the prefix */
 #define SENSORS_PREFIX_MAX 20
@@ -392,5 +410,5 @@
 	char name[SENSORS_PREFIX_MAX + 13];
 };
 
-#endif				/* def SENSORS_SENSORS_H */
+#endif				/* def _LINUX_I2C_PROC_H */
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/jbd.h linux-2.4.27-leo/include/linux/jbd.h
--- linux-2.4.27/include/linux/jbd.h	2003-12-02 14:30:15.000000000 +0000
+++ linux-2.4.27-leo/include/linux/jbd.h	2004-09-20 21:59:48.000000000 +0100
@@ -311,7 +311,7 @@
 
 static inline struct journal_head *bh2jh(struct buffer_head *bh)
 {
-	return bh->b_private;
+	return bh->b_journal_head;
 }
 
 #define HAVE_JOURNAL_CALLBACK_STATUS
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/kernel.h linux-2.4.27-leo/include/linux/kernel.h
--- linux-2.4.27/include/linux/kernel.h	2004-09-17 02:38:47.000000000 +0100
+++ linux-2.4.27-leo/include/linux/kernel.h	2004-09-20 22:08:42.000000000 +0100
@@ -71,14 +71,17 @@
 extern long long simple_strtoll(const char *,char **,unsigned int);
 extern int sprintf(char * buf, const char * fmt, ...)
 	__attribute__ ((format (printf, 2, 3)));
-extern int vsprintf(char *buf, const char *, va_list);
+extern int vsprintf(char *buf, const char *, va_list)
+	__attribute__ ((format (printf, 2, 0)));
 extern int snprintf(char * buf, size_t size, const char * fmt, ...)
 	__attribute__ ((format (printf, 3, 4)));
-extern int vsnprintf(char *buf, size_t size, const char *fmt, va_list args);
+extern int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
+	__attribute__ ((format (printf, 3, 0)));
 
 extern int sscanf(const char *, const char *, ...)
 	__attribute__ ((format (scanf,2,3)));
-extern int vsscanf(const char *, const char *, va_list);
+extern int vsscanf(const char *, const char *, va_list)
+	__attribute__ ((format (scanf, 2, 0)));
 
 extern int get_option(char **str, int *pint);
 extern char *get_options(char *str, int nints, int *ints);
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/loop.h linux-2.4.27-leo/include/linux/loop.h
--- linux-2.4.27/include/linux/loop.h	2003-12-02 14:32:20.000000000 +0000
+++ linux-2.4.27-leo/include/linux/loop.h	2004-09-17 03:19:04.000000000 +0100
@@ -16,6 +16,14 @@
 #define LO_KEY_SIZE	32
 
 #ifdef __KERNEL__
+typedef int sector_t; /* for 2.6 this is defined in <asm/types.h> and
+			 most likely an u64; but since cryptoloop uses
+			 only the lower 32 bits of the block number
+			 passed, let's just use an 32bit int for now */
+
+/* definitions for IV metric */
+#define LOOP_IV_SECTOR_BITS 9
+#define LOOP_IV_SECTOR_SIZE (1 << LOOP_IV_SECTOR_BITS)
 
 /* Possible states of device */
 enum {
@@ -24,6 +32,12 @@
 	Lo_rundown,
 };
 
+struct loop_device;
+
+typedef	int (* transfer_proc_t)(struct loop_device *, int cmd,
+				char *raw_buf, char *loop_buf, int size,
+				sector_t real_block);
+
 struct loop_device {
 	int		lo_number;
 	int		lo_refcnt;
@@ -32,9 +46,7 @@
 	int		lo_encrypt_type;
 	int		lo_encrypt_key_size;
 	int		lo_flags;
-	int		(*transfer)(struct loop_device *, int cmd,
-				    char *raw_buf, char *loop_buf, int size,
-				    int real_block);
+	transfer_proc_t transfer;
 	char		lo_name[LO_NAME_SIZE];
 	char		lo_encrypt_key[LO_KEY_SIZE];
 	__u32           lo_init[2];
@@ -58,17 +70,13 @@
 	atomic_t		lo_pending;
 };
 
-typedef	int (* transfer_proc_t)(struct loop_device *, int cmd,
-				char *raw_buf, char *loop_buf, int size,
-				int real_block);
-
 static inline int lo_do_transfer(struct loop_device *lo, int cmd, char *rbuf,
-				 char *lbuf, int size, int rblock)
+				 char *lbuf, int size, sector_t real_block)
 {
 	if (!lo->transfer)
 		return 0;
 
-	return lo->transfer(lo, cmd, rbuf, lbuf, size, rblock);
+	return lo->transfer(lo, cmd, rbuf, lbuf, size, real_block);
 }
 #endif /* __KERNEL__ */
 
@@ -122,6 +130,7 @@
 #define LO_CRYPT_IDEA     6
 #define LO_CRYPT_DUMMY    9
 #define LO_CRYPT_SKIPJACK 10
+#define LO_CRYPT_CRYPTOAPI 18
 #define MAX_LO_CRYPT	20
 
 #ifdef __KERNEL__
@@ -129,7 +138,7 @@
 struct loop_func_table {
 	int number; 	/* filter type */ 
 	int (*transfer)(struct loop_device *lo, int cmd, char *raw_buf,
-			char *loop_buf, int size, int real_block);
+			char *loop_buf, int size, sector_t real_block);
 	int (*init)(struct loop_device *, struct loop_info *); 
 	/* release is called from loop_unregister_transfer or clr_fd */
 	int (*release)(struct loop_device *); 
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/mempool.h linux-2.4.27-leo/include/linux/mempool.h
--- linux-2.4.27/include/linux/mempool.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/include/linux/mempool.h	2004-09-20 22:10:23.000000000 +0100
@@ -0,0 +1,31 @@
+/*
+ * memory buffer pool support
+ */
+#ifndef _LINUX_MEMPOOL_H
+#define _LINUX_MEMPOOL_H
+
+#include <linux/list.h>
+#include <linux/wait.h>
+
+struct mempool_s;
+typedef struct mempool_s mempool_t;
+
+typedef void * (mempool_alloc_t)(int gfp_mask, void *pool_data);
+typedef void (mempool_free_t)(void *element, void *pool_data);
+
+extern mempool_t * mempool_create(int min_nr, mempool_alloc_t *alloc_fn,
+				 mempool_free_t *free_fn, void *pool_data);
+extern int mempool_resize(mempool_t *pool, int new_min_nr, int gfp_mask);
+extern void mempool_destroy(mempool_t *pool);
+extern void * mempool_alloc(mempool_t *pool, int gfp_mask);
+extern void mempool_free(void *element, mempool_t *pool);
+
+/*
+ * A mempool_alloc_t and mempool_free_t that get the memory from
+ * a slab that is passed in through pool_data.
+ */
+void *mempool_alloc_slab(int gfp_mask, void *pool_data);
+void mempool_free_slab(void *element, void *pool_data);
+
+
+#endif /* _LINUX_MEMPOOL_H */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/mm.h linux-2.4.27-leo/include/linux/mm.h
--- linux-2.4.27/include/linux/mm.h	2004-05-26 23:40:33.000000000 +0100
+++ linux-2.4.27-leo/include/linux/mm.h	2004-09-20 22:08:42.000000000 +0100
@@ -22,9 +22,13 @@
 extern struct list_head active_list;
 extern struct list_head inactive_list;
 
+extern void gr_learn_resource(const struct task_struct * task, const int limit,
+			      const unsigned long wanted, const int gt);
+
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include <asm/atomic.h>
+#include <asm/mman.h>
 
 /*
  * Linux kernel virtual memory manager primitives.
@@ -104,6 +108,33 @@
 #define VM_DONTEXPAND	0x00040000	/* Cannot expand with mremap() */
 #define VM_RESERVED	0x00080000	/* Don't unmap it from swap_out */
 
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+#define VM_MIRROR	0x00100000	/* vma is mirroring another */
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_MPROTECT
+#define VM_MAYNOTWRITE	0x00200000	/* vma cannot be granted VM_WRITE any more */
+#endif
+
+#ifdef ARCH_STACK_GROWSUP
+#define __VM_STACK_FLAGS	0x00000233
+#else
+#define __VM_STACK_FLAGS	0x00000133
+#endif
+
+#if defined(CONFIG_GRKERNSEC_PAX_PAGEEXEC) || defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC)
+#ifdef CONFIG_GRKERNSEC_PAX_MPROTECT
+#define VM_STACK_FLAGS	(__VM_STACK_FLAGS | \
+			((current->flags & PF_PAX_MPROTECT)?0:VM_MAYEXEC) | \
+			((current->flags & (PF_PAX_PAGEEXEC|PF_PAX_SEGMEXEC))?0:VM_EXEC))
+#else
+#define VM_STACK_FLAGS	(__VM_STACK_FLAGS | VM_MAYEXEC | \
+			((current->flags & (PF_PAX_PAGEEXEC|PF_PAX_SEGMEXEC))?0:VM_EXEC))
+#endif
+#else
+#define VM_STACK_FLAGS (__VM_STACK_FLAGS | VM_EXEC | VM_MAYEXEC)
+#endif
+
 #ifndef VM_STACK_FLAGS
 #define VM_STACK_FLAGS	0x00000177
 #endif
@@ -492,6 +523,7 @@
 extern int vmtruncate(struct inode * inode, loff_t offset);
 extern pmd_t *FASTCALL(__pmd_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address));
 extern pte_t *FASTCALL(pte_alloc(struct mm_struct *mm, pmd_t *pmd, unsigned long address));
+extern pte_t *FASTCALL(pte_alloc_kernel(struct mm_struct *mm, pmd_t *pmd, unsigned long address));
 extern int handle_mm_fault(struct mm_struct *mm,struct vm_area_struct *vma, unsigned long address, int write_access);
 extern int make_pages_present(unsigned long addr, unsigned long end);
 extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write);
@@ -556,11 +588,16 @@
 	unsigned long len, unsigned long prot,
 	unsigned long flag, unsigned long pgoff);
 
+extern unsigned long __do_mmap_pgoff(struct file *file, unsigned long addr,
+	unsigned long len, unsigned long prot,
+	unsigned long flag, unsigned long pgoff);
+
 static inline unsigned long do_mmap(struct file *file, unsigned long addr,
 	unsigned long len, unsigned long prot,
 	unsigned long flag, unsigned long offset)
 {
 	unsigned long ret = -EINVAL;
+
 	if ((offset + PAGE_ALIGN(len)) < offset)
 		goto out;
 	if (!(offset & ~PAGE_MASK))
@@ -583,6 +620,12 @@
 
 static inline int can_vma_merge(struct vm_area_struct * vma, unsigned long vm_flags)
 {
+
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+	if ((vma->vm_flags | vm_flags) & VM_MIRROR)
+		return 0;
+#endif
+
 	if (!vma->vm_file && vma->vm_flags == vm_flags)
 		return 1;
 	else
@@ -636,13 +679,23 @@
 
 	return gfp_mask;
 }
-	
+
+/* Look up the first VMA which satisfies  addr < vm_end,  NULL if none. */
+extern struct vm_area_struct * find_vma(struct mm_struct * mm, unsigned long addr);
+extern struct vm_area_struct * find_vma_prev(struct mm_struct * mm, unsigned long addr,
+					     struct vm_area_struct **pprev);
+
 /* vma is the first one with  address < vma->vm_end,
  * and even  address < vma->vm_start. Have to extend vma. */
 static inline int expand_stack(struct vm_area_struct * vma, unsigned long address)
 {
 	unsigned long grow;
 
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+	struct vm_area_struct * vma_m = NULL;
+	unsigned long address_m = 0UL;
+#endif
+
 	/*
 	 * vma->vm_start/vm_end cannot change under us because the caller is required
 	 * to hold the mmap_sem in write mode. We need to get the spinlock only
@@ -651,25 +704,62 @@
 	address &= PAGE_MASK;
  	spin_lock(&vma->vm_mm->page_table_lock);
 	grow = (vma->vm_start - address) >> PAGE_SHIFT;
+
+	gr_learn_resource(current, RLIMIT_STACK, vma->vm_end - address, 1);
+	gr_learn_resource(current, RLIMIT_AS, (vma->vm_mm->total_vm + grow) << PAGE_SHIFT, 1);
+	gr_learn_resource(current, RLIMIT_MEMLOCK, (vma->vm_mm->locked_vm + grow) << PAGE_SHIFT, 1);
+
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+	if (vma->vm_flags & VM_MIRROR) {
+		address_m = vma->vm_start + (unsigned long)vma->vm_private_data;
+		vma_m = find_vma(vma->vm_mm, address_m);
+		if (!vma_m || vma_m->vm_start != address_m || !(vma_m->vm_flags & VM_MIRROR) ||
+		    vma->vm_end - vma->vm_start != vma_m->vm_end - vma_m->vm_start) {
+			spin_unlock(&vma->vm_mm->page_table_lock);
+			printk(KERN_ERR "PAX: VMMIRROR: expand bug, %08lx, %08lx, %08lx, %08lx, %08lx\n",
+			       address, vma->vm_start, vma_m->vm_start, vma->vm_end, vma_m->vm_end);
+			return -ENOMEM;
+		}
+
+		address_m = address + (unsigned long)vma->vm_private_data;
+		if (vma_m->vm_end - address_m > current->rlim[RLIMIT_STACK].rlim_cur ||
+		    ((vma_m->vm_mm->total_vm + 2*grow) << PAGE_SHIFT) > current->rlim[RLIMIT_AS].rlim_cur ||
+		    ((vma_m->vm_flags & VM_LOCKED) && ((vma_m->vm_mm->locked_vm + 2*grow) << PAGE_SHIFT) >
+		     current->rlim[RLIMIT_MEMLOCK].rlim_cur)) {
+			spin_unlock(&vma->vm_mm->page_table_lock);
+			return -ENOMEM;
+		}
+	} else
+#endif
+
 	if (vma->vm_end - address > current->rlim[RLIMIT_STACK].rlim_cur ||
-	    ((vma->vm_mm->total_vm + grow) << PAGE_SHIFT) > current->rlim[RLIMIT_AS].rlim_cur) {
+	    ((vma->vm_mm->total_vm + grow) << PAGE_SHIFT) > current->rlim[RLIMIT_AS].rlim_cur ||
+	    ((vma->vm_flags & VM_LOCKED) && ((vma->vm_mm->locked_vm + grow) << PAGE_SHIFT) >
+	     current->rlim[RLIMIT_MEMLOCK].rlim_cur)) {
 		spin_unlock(&vma->vm_mm->page_table_lock);
 		return -ENOMEM;
 	}
+
 	vma->vm_start = address;
 	vma->vm_pgoff -= grow;
 	vma->vm_mm->total_vm += grow;
 	if (vma->vm_flags & VM_LOCKED)
 		vma->vm_mm->locked_vm += grow;
+
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+	if (vma->vm_flags & VM_MIRROR) {
+		vma_m->vm_start = address_m;
+		vma_m->vm_pgoff -= grow;
+		vma_m->vm_mm->total_vm += grow;
+		if (vma_m->vm_flags & VM_LOCKED)
+			vma_m->vm_mm->locked_vm += grow;
+	}
+#endif
+
 	spin_unlock(&vma->vm_mm->page_table_lock);
 	return 0;
 }
 
-/* Look up the first VMA which satisfies  addr < vm_end,  NULL if none. */
-extern struct vm_area_struct * find_vma(struct mm_struct * mm, unsigned long addr);
-extern struct vm_area_struct * find_vma_prev(struct mm_struct * mm, unsigned long addr,
-					     struct vm_area_struct **pprev);
-
 /* Look up the first VMA which intersects the interval start_addr..end_addr-1,
    NULL if none.  Assume start_addr < end_addr. */
 static inline struct vm_area_struct * find_vma_intersection(struct mm_struct * mm, unsigned long start_addr, unsigned long end_addr)
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/msdos_fs_sb.h linux-2.4.27-leo/include/linux/msdos_fs_sb.h
--- linux-2.4.27/include/linux/msdos_fs_sb.h	2001-10-12 21:48:42.000000000 +0100
+++ linux-2.4.27-leo/include/linux/msdos_fs_sb.h	2004-09-17 03:19:13.000000000 +0100
@@ -9,7 +9,9 @@
 struct fat_mount_options {
 	uid_t fs_uid;
 	gid_t fs_gid;
-	unsigned short fs_umask;
+	unsigned short fs_umask;  /* global mask that masks off bits from either following two fields */
+	unsigned short fs_fmode;  /* mode bits for files for fdmode */
+	unsigned short fs_dmode;  /* mode bits for directories for fdmode */
 	unsigned short codepage;  /* Codepage for shortname conversions */
 	char *iocharset;          /* Charset used for filename input/display */
 	unsigned short shortname; /* flags for shortname display/create rule */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/proc_fs.h linux-2.4.27-leo/include/linux/proc_fs.h
--- linux-2.4.27/include/linux/proc_fs.h	2004-05-26 23:40:33.000000000 +0100
+++ linux-2.4.27-leo/include/linux/proc_fs.h	2004-09-20 22:08:48.000000000 +0100
@@ -143,6 +143,9 @@
 extern struct proc_dir_entry *proc_mknod(const char *,mode_t,
 		struct proc_dir_entry *,kdev_t);
 extern struct proc_dir_entry *proc_mkdir(const char *,struct proc_dir_entry *);
+#ifdef CONFIG_GRKERNSEC_PROC
+extern struct proc_dir_entry *proc_priv_mkdir(const char *, struct proc_dir_entry *);
+#endif
 
 static inline struct proc_dir_entry *create_proc_read_entry(const char *name,
 	mode_t mode, struct proc_dir_entry *base, 
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/raid/md.h linux-2.4.27-leo/include/linux/raid/md.h
--- linux-2.4.27/include/linux/raid/md.h	2004-02-20 13:07:46.000000000 +0000
+++ linux-2.4.27-leo/include/linux/raid/md.h	2004-09-20 22:10:47.000000000 +0100
@@ -18,6 +18,8 @@
 #ifndef _MD_H
 #define _MD_H
 
+#include <linux/config.h>
+
 #include <linux/mm.h>
 #include <linux/fs.h>
 #include <linux/blkdev.h>
@@ -88,5 +90,15 @@
 
 #define MD_BUG(x...) { printk("md: bug in file %s, line %d\n", __FILE__, __LINE__); md_print_devices(); }
 
+/* Verbose startup message option
+ *  Added by Paul Evans, <nerd@freeuk.com>
+ */
+
+#ifdef CONFIG_MD_VERBMSG
+# define VERBMSG(c) c;
+#else
+# define VERBMSG(c)
+#endif
+
 #endif 
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/sched.h linux-2.4.27-leo/include/linux/sched.h
--- linux-2.4.27/include/linux/sched.h	2004-09-17 02:38:47.000000000 +0100
+++ linux-2.4.27-leo/include/linux/sched.h	2004-09-20 22:08:42.000000000 +0100
@@ -27,6 +27,9 @@
 #include <linux/securebits.h>
 #include <linux/fs_struct.h>
 
+extern int gr_task_is_capable(struct task_struct *task, const int cap);
+extern int gr_pid_is_chrooted(struct task_struct *p);
+
 struct exec_domain;
 
 /*
@@ -91,6 +94,7 @@
 #define TASK_UNINTERRUPTIBLE	2
 #define TASK_ZOMBIE		4
 #define TASK_STOPPED		8
+#define PREEMPT_ACTIVE		0x4000000
 
 #define __set_task_state(tsk, state_value)		\
 	do { (tsk)->state = (state_value); } while (0)
@@ -147,6 +151,9 @@
 #define	MAX_SCHEDULE_TIMEOUT	LONG_MAX
 extern signed long FASTCALL(schedule_timeout(signed long timeout));
 asmlinkage void schedule(void);
+#ifdef CONFIG_PREEMPT
+asmlinkage void preempt_schedule(void);
+#endif
 
 extern int schedule_task(struct tq_struct *task);
 extern void flush_scheduled_tasks(void);
@@ -227,10 +234,24 @@
 	unsigned long cpu_vm_mask;
 	unsigned long swap_address;
 
-	unsigned dumpable:1;
+ 	unsigned dumpable:1;
 
 	/* Architecture-specific MM context */
 	mm_context_t context;
+
+#ifdef CONFIG_GRKERNSEC_PAX_DLRESOLVE
+	unsigned long call_dl_resolve;
+#endif
+
+#if defined(CONFIG_PPC32) && defined(CONFIG_GRKERNSEC_PAX_EMUSIGRT)
+	unsigned long call_syscall;
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_ASLR
+	unsigned long delta_mmap;		/* PaX: randomized offset */
+	unsigned long delta_exec;		/* PaX: randomized offset */
+	unsigned long delta_stack;		/* PaX: randomized offset */
+#endif
 };
 
 extern int mmlist_nr;
@@ -285,7 +306,7 @@
 	 * offsets of these are hardcoded elsewhere - touch with care
 	 */
 	volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped */
-	unsigned long flags;	/* per process flags, defined below */
+	int preempt_count;	/* 0 => preemptable, <0 => BUG */
 	int sigpending;
 	mm_segment_t addr_limit;	/* thread address space:
 					 	0-0xBFFFFFFF for user-thead
@@ -327,6 +348,7 @@
 	struct mm_struct *active_mm;
 	struct list_head local_pages;
 	unsigned int allocation_order, nr_local_pages;
+	unsigned long flags;
 
 /* task state */
 	struct linux_binfmt *binfmt;
@@ -406,7 +428,7 @@
 	int (*notifier)(void *priv);
 	void *notifier_data;
 	sigset_t *notifier_mask;
-	
+
 /* Thread group tracking */
    	u32 parent_exec_id;
    	u32 self_exec_id;
@@ -415,6 +437,23 @@
 
 /* journalling filesystem info */
 	void *journal_info;
+
+#ifdef CONFIG_GRKERNSEC
+/* added by grsecurity's ACL system */	
+	struct acl_subject_label *acl;
+	struct acl_role_label *role;
+	struct file *exec_file;
+	u32 curr_ip;
+	u32 gr_saddr;
+	u32 gr_daddr;
+	u16 gr_sport;
+	u16 gr_dport;
+	u16 acl_role_id;	
+	u8 acl_sp_role:1;	
+	u8 used_accept:1;	
+	u8 is_writable:1;
+	u8 brute:1;
+#endif
 };
 
 /*
@@ -436,6 +475,29 @@
 
 #define PF_USEDFPU	0x00100000	/* task used FPU this quantum (SMP) */
 
+#define PF_PAX_PAGEEXEC       0x01000000      /* Paging based non-executable pages */
+#define PF_PAX_EMUTRAMP       0x02000000      /* Emulate trampolines */
+#define PF_PAX_MPROTECT       0x04000000      /* Restrict mprotect() */
+#define PF_PAX_RANDMMAP       0x08000000      /* Randomize mmap() base */
+#define PF_PAX_RANDEXEC	      0x10000000      /* Randomize ET_EXEC base */
+#define PF_PAX_SEGMEXEC	      0x20000000      /* Segmentation based non-executable pages */
+
+#ifdef CONFIG_GRKERNSEC_PAX_SOFTMODE
+#if defined(CONFIG_GRKERNSEC_PAX_RANDMMAP) || defined(CONFIG_GRKERNSEC_PAX_RANDUSTACK) || defined(CONFIG_GRKERNSEC_PAX_RANDKSTACK)
+extern unsigned int pax_aslr;
+#endif
+
+extern unsigned int pax_softmode;
+#endif
+
+extern int pax_check_flags(unsigned long *);
+        
+#ifdef CONFIG_GRKERNSEC_PAX_HAVE_ACL_FLAGS
+extern void pax_set_flags(struct linux_binprm * bprm);
+#elif defined(CONFIG_GRKERNSEC_PAX_HOOK_ACL_FLAGS)
+extern void (*pax_set_flags_func)(struct linux_binprm * bprm);
+#endif
+
 /*
  * Ptrace flags
  */
@@ -550,6 +612,8 @@
 	*p->pidhash_pprev = p->pidhash_next;
 }
 
+#include <asm/current.h>
+
 static inline struct task_struct *find_task_by_pid(int pid)
 {
 	struct task_struct *p, **htable = &pidhash[pid_hashfn(pid)];
@@ -557,6 +621,8 @@
 	for(p = *htable; p && p->pid != pid; p = p->pidhash_next)
 		;
 
+	if(gr_pid_is_chrooted(p)) p = NULL;
+
 	return p;
 }
 
@@ -578,8 +644,6 @@
 extern void free_uid(struct user_struct *);
 extern void switch_uid(struct user_struct *);
 
-#include <asm/current.h>
-
 extern unsigned long volatile jiffies;
 extern unsigned long itimer_ticks;
 extern unsigned long itimer_next;
@@ -743,7 +807,7 @@
 static inline int capable(int cap)
 {
 #if 1 /* ok now */
-	if (cap_raised(current->cap_effective, cap))
+	if (cap_raised(current->cap_effective, cap) && gr_task_is_capable(current, cap))
 #else
 	if (cap_is_fs_cap(cap) ? current->fsuid == 0 : current->euid == 0)
 #endif
@@ -958,5 +1022,10 @@
 		__cond_resched();
 }
 
+#define _TASK_STRUCT_DEFINED
+#include <linux/dcache.h>
+#include <linux/tqueue.h>
+#include <linux/fs_struct.h>
+
 #endif /* __KERNEL__ */
 #endif
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/sensors_compat.h linux-2.4.27-leo/include/linux/sensors_compat.h
--- linux-2.4.27/include/linux/sensors_compat.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/include/linux/sensors_compat.h	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,31 @@
+/*
+ * Stolen from kernel 2.5.69
+ * device.h - generic, centralized driver model
+ * To make it easier to backport from 2.5
+ *
+ * Copyright (c) 2001-2003 Patrick Mochel <mochel@osdl.org>
+ *
+ */
+
+#ifndef _SENSORS_COMPAT_H_
+#define _SENSORS_COMPAT_H_
+
+/* debugging and troubleshooting/diagnostic helpers. */
+#define dev_printk(level, dev, format, arg...)	\
+	printk(level "%s: " format , (dev)->name , ## arg)
+
+#ifdef DEBUG
+#define dev_dbg(dev, format, arg...)		\
+	dev_printk(KERN_DEBUG , dev , format , ## arg)
+#else
+#define dev_dbg(dev, format, arg...) do {} while (0)
+#endif
+
+#define dev_err(dev, format, arg...)		\
+	dev_printk(KERN_ERR , dev , format , ## arg)
+#define dev_info(dev, format, arg...)		\
+	dev_printk(KERN_INFO , dev , format , ## arg)
+#define dev_warn(dev, format, arg...)		\
+	dev_printk(KERN_WARNING , dev , format , ## arg)
+
+#endif /* _SENSORS_COMPAT_H_ */
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/sensors_vid.h linux-2.4.27-leo/include/linux/sensors_vid.h
--- linux-2.4.27/include/linux/sensors_vid.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/include/linux/sensors_vid.h	2004-09-20 21:34:47.000000000 +0100
@@ -0,0 +1,80 @@
+/*
+    sensors_vid.h - Part of lm_sensors, Linux kernel modules for hardware
+               monitoring
+    Copyright (c) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
+    With assistance from Trent Piepho <xyzzy@speakeasy.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+    This file contains common code for decoding VID pins.
+    This file is #included in various chip drivers in this directory.
+    As the user is unlikely to load more than one driver which
+    includes this code we don't worry about the wasted space.
+    Reference: VRM x.y DC-DC Converter Design Guidelines,
+    available at http://developer.intel.com
+*/
+
+/*
+    AMD Opteron processors don't follow the Intel VRM spec.
+    I'm going to "make up" 2.4 as the VRM spec for the Opterons.
+    No good reason just a mnemonic for the 24x Opteron processor
+    series
+
+    Opteron VID encoding is:
+
+       00000  =  1.550 V
+       00001  =  1.525 V
+        . . . .
+       11110  =  0.800 V
+       11111  =  0.000 V (off)
+ */
+
+/*
+    Legal val values 00 - 1F.
+    vrm is the Intel VRM document version.
+    Note: vrm version is scaled by 10 and the return value is scaled by 1000
+    to avoid floating point in the kernel.
+*/
+
+#define DEFAULT_VRM	82
+
+static inline int vid_from_reg(int val, int vrm)
+{
+	switch(vrm) {
+
+	case 24:		/* Opteron processor */
+		return(val == 0x1f ? 0 : 1550 - val * 25);
+
+	case 91:		/* VRM 9.1 */
+	case 90:		/* VRM 9.0 */
+		return(val == 0x1f ? 0 :
+		                       1850 - val * 25);
+
+	case 85:		/* VRM 8.5 */
+		return((val & 0x10  ? 25 : 0) +
+		       ((val & 0x0f) > 0x04 ? 2050 : 1250) -
+		       ((val & 0x0f) * 50));
+
+	case 84:		/* VRM 8.4 */
+		val &= 0x0f;
+				/* fall through */
+	default:		/* VRM 8.2 */
+		return(val == 0x1f ? 0 :
+		       val & 0x10  ? 5100 - (val) * 100 :
+		                     2050 - (val) * 50);
+	}
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/smp_lock.h linux-2.4.27-leo/include/linux/smp_lock.h
--- linux-2.4.27/include/linux/smp_lock.h	2004-02-20 13:06:56.000000000 +0000
+++ linux-2.4.27-leo/include/linux/smp_lock.h	2004-09-20 22:08:42.000000000 +0100
@@ -3,7 +3,7 @@
 
 #include <linux/config.h>
 
-#ifndef CONFIG_SMP
+#if !defined(CONFIG_SMP) && !defined(CONFIG_PREEMPT)
 
 #define lock_kernel()				do { } while(0)
 #define unlock_kernel()				do { } while(0)
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/spinlock.h linux-2.4.27-leo/include/linux/spinlock.h
--- linux-2.4.27/include/linux/spinlock.h	2004-02-20 14:11:47.000000000 +0000
+++ linux-2.4.27-leo/include/linux/spinlock.h	2004-09-20 22:08:42.000000000 +0100
@@ -2,6 +2,7 @@
 #define __LINUX_SPINLOCK_H
 
 #include <linux/config.h>
+#include <linux/compiler.h>
 
 #include <asm/system.h>
 
@@ -64,8 +65,10 @@
 
 #if (DEBUG_SPINLOCKS < 1)
 
+#ifndef CONFIG_PREEMPT
 #define atomic_dec_and_lock(atomic,lock) atomic_dec_and_test(atomic)
 #define ATOMIC_DEC_AND_LOCK
+#endif
 
 /*
  * Your basic spinlocks, allowing only a single CPU anywhere
@@ -82,11 +85,11 @@
 #endif
 
 #define spin_lock_init(lock)	do { } while(0)
-#define spin_lock(lock)		(void)(lock) /* Not "unused variable". */
+#define _raw_spin_lock(lock)	(void)(lock) /* Not "unused variable". */
 #define spin_is_locked(lock)	(0)
-#define spin_trylock(lock)	({1; })
+#define _raw_spin_trylock(lock)	({1; })
 #define spin_unlock_wait(lock)	do { } while(0)
-#define spin_unlock(lock)	do { } while(0)
+#define _raw_spin_unlock(lock)	do { } while(0)
 
 #elif (DEBUG_SPINLOCKS < 2)
 
@@ -146,13 +149,78 @@
 #endif
 
 #define rwlock_init(lock)	do { } while(0)
-#define read_lock(lock)		(void)(lock) /* Not "unused variable". */
-#define read_unlock(lock)	do { } while(0)
-#define write_lock(lock)	(void)(lock) /* Not "unused variable". */
-#define write_unlock(lock)	do { } while(0)
+#define _raw_read_lock(lock)	(void)(lock) /* Not "unused variable". */
+#define _raw_read_unlock(lock)	do { } while(0)
+#define _raw_write_lock(lock)	(void)(lock) /* Not "unused variable". */
+#define _raw_write_unlock(lock)	do { } while(0)
 
 #endif /* !SMP */
 
+#ifdef CONFIG_PREEMPT
+
+#define preempt_get_count()	(current->preempt_count)
+#define preempt_is_disabled()	(preempt_get_count() != 0)
+
+#define preempt_disable() \
+do { \
+	++current->preempt_count; \
+	barrier(); \
+} while (0)
+
+#define preempt_enable_no_resched() \
+do { \
+	--current->preempt_count; \
+	barrier(); \
+} while (0)
+
+#define preempt_enable() \
+do { \
+	--current->preempt_count; \
+	barrier(); \
+	if (unlikely(current->preempt_count < current->need_resched)) \
+		preempt_schedule(); \
+} while (0)
+
+#define spin_lock(lock)	\
+do { \
+	preempt_disable(); \
+	_raw_spin_lock(lock); \
+} while(0)
+
+#define spin_trylock(lock)	({preempt_disable(); _raw_spin_trylock(lock) ? \
+				1 : ({preempt_enable(); 0;});})
+#define spin_unlock(lock) \
+do { \
+	_raw_spin_unlock(lock); \
+	preempt_enable(); \
+} while (0)
+
+#define read_lock(lock)		({preempt_disable(); _raw_read_lock(lock);})
+#define read_unlock(lock)	({_raw_read_unlock(lock); preempt_enable();})
+#define write_lock(lock)	({preempt_disable(); _raw_write_lock(lock);})
+#define write_unlock(lock)	({_raw_write_unlock(lock); preempt_enable();})
+#define write_trylock(lock)	({preempt_disable();_raw_write_trylock(lock) ? \
+				1 : ({preempt_enable(); 0;});})
+
+#else
+
+#define preempt_get_count()	(0)
+#define preempt_is_disabled()	(1)
+#define preempt_disable()	do { } while (0)
+#define preempt_enable_no_resched()	do {} while(0)
+#define preempt_enable()	do { } while (0)
+
+#define spin_lock(lock)		_raw_spin_lock(lock)
+#define spin_trylock(lock)	_raw_spin_trylock(lock)
+#define spin_unlock(lock)	_raw_spin_unlock(lock)
+
+#define read_lock(lock)		_raw_read_lock(lock)
+#define read_unlock(lock)	_raw_read_unlock(lock)
+#define write_lock(lock)	_raw_write_lock(lock)
+#define write_unlock(lock)	_raw_write_unlock(lock)
+#define write_trylock(lock)	_raw_write_trylock(lock)
+#endif
+
 /* "lock on reference count zero" */
 #ifndef ATOMIC_DEC_AND_LOCK
 #include <asm/atomic.h>
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/sysctl.h linux-2.4.27-leo/include/linux/sysctl.h
--- linux-2.4.27/include/linux/sysctl.h	2004-09-17 02:38:47.000000000 +0100
+++ linux-2.4.27-leo/include/linux/sysctl.h	2004-09-20 22:08:44.000000000 +0100
@@ -129,8 +129,20 @@
 	KERN_EXCEPTION_TRACE=58, /* boolean: exception trace */
  	KERN_CORE_SETUID=59,	/* int: set to allow core dumps of setuid apps */
 	KERN_SPARC_SCONS_PWROFF=64, /* int: serial console power-off halt */
+	KERN_GRSECURITY=68,	/* grsecurity */
+
+#ifdef CONFIG_GRKERNSEC_PAX_SOFTMODE
+	KERN_PAX=69,		/* PaX control */
+#endif
+
 };
 
+#ifdef CONFIG_GRKERNSEC_PAX_SOFTMODE
+enum {
+	PAX_ASLR=1,		/* PaX: disable/enable all randomization features */
+	PAX_SOFTMODE=2,		/* PaX: disable/enable soft mode */
+};
+#endif
 
 /* CTL_VM names: */
 enum
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/tqueue.h linux-2.4.27-leo/include/linux/tqueue.h
--- linux-2.4.27/include/linux/tqueue.h	2004-02-20 13:06:56.000000000 +0000
+++ linux-2.4.27-leo/include/linux/tqueue.h	2004-09-20 22:08:42.000000000 +0100
@@ -94,6 +94,22 @@
 extern spinlock_t tqueue_lock;
 
 /*
+ * Call all "bottom halfs" on a given list.
+ */
+
+extern void __run_task_queue(task_queue *list);
+
+static inline void run_task_queue(task_queue *list)
+{
+	if (TQ_ACTIVE(*list))
+		__run_task_queue(list);
+}
+
+#endif /* _LINUX_TQUEUE_H */
+
+#if !defined(_LINUX_TQUEUE_H_INLINES) && defined(_TASK_STRUCT_DEFINED)
+#define _LINUX_TQUEUE_H_INLINES
+/*
  * Queue a task on a tq.  Return non-zero if it was successfully
  * added.
  */
@@ -109,17 +125,4 @@
 	}
 	return ret;
 }
-
-/*
- * Call all "bottom halfs" on a given list.
- */
-
-extern void __run_task_queue(task_queue *list);
-
-static inline void run_task_queue(task_queue *list)
-{
-	if (TQ_ACTIVE(*list))
-		__run_task_queue(list);
-}
-
-#endif /* _LINUX_TQUEUE_H */
+#endif
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/linux/vmalloc.h linux-2.4.27-leo/include/linux/vmalloc.h
--- linux-2.4.27/include/linux/vmalloc.h	2003-12-02 14:28:19.000000000 +0000
+++ linux-2.4.27-leo/include/linux/vmalloc.h	2004-09-20 22:08:43.000000000 +0100
@@ -29,6 +29,7 @@
 extern void vmfree_area_pages(unsigned long address, unsigned long size);
 extern int vmalloc_area_pages(unsigned long address, unsigned long size,
                               int gfp_mask, pgprot_t prot);
+extern void *vcalloc(unsigned long nmemb, unsigned long elem_size);
 
 /*
  *	Allocate any pages
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/net/inetpeer.h linux-2.4.27-leo/include/net/inetpeer.h
--- linux-2.4.27/include/net/inetpeer.h	2004-05-26 23:42:13.000000000 +0100
+++ linux-2.4.27-leo/include/net/inetpeer.h	2004-09-20 22:09:34.000000000 +0100
@@ -13,6 +13,7 @@
 #include <linux/init.h>
 #include <linux/sched.h>
 #include <linux/spinlock.h>
+
 #include <asm/atomic.h>
 
 struct inet_peer
@@ -34,6 +35,11 @@
 /* can be called with or without local BH being disabled */
 struct inet_peer	*inet_getpeer(__u32 daddr, int create);
 
+#ifdef CONFIG_GRKERNSEC_RANDID
+extern int grsec_enable_randid;
+extern __u16 ip_randomid(void);
+#endif
+
 extern spinlock_t inet_peer_unused_lock;
 extern struct inet_peer *inet_peer_unused_head;
 extern struct inet_peer **inet_peer_unused_tailp;
@@ -58,7 +64,14 @@
 	__u16 id;
 
 	spin_lock_bh(&inet_peer_idlock);
-	id = p->ip_id_count++;
+
+#ifdef CONFIG_GRKERNSEC_RANDID
+	if(grsec_enable_randid)
+		id = htons(ip_randomid());
+	else
+#endif
+		id = p->ip_id_count++;
+
 	spin_unlock_bh(&inet_peer_idlock);
 	return id;
 }
diff -urN --exclude-from=diff-exclude linux-2.4.27/include/net/ip.h linux-2.4.27-leo/include/net/ip.h
--- linux-2.4.27/include/net/ip.h	2004-05-26 23:42:13.000000000 +0100
+++ linux-2.4.27-leo/include/net/ip.h	2004-09-20 22:09:34.000000000 +0100
@@ -64,6 +64,11 @@
 	void			(*destructor)(struct sock *);
 };
 
+#ifdef CONFIG_GRKERNSEC_RANDID
+extern int grsec_enable_randid;
+extern __u16 ip_randomid(void);
+#endif
+
 extern struct ip_ra_chain *ip_ra_chain;
 extern rwlock_t ip_ra_lock;
 
@@ -197,7 +202,13 @@
 		 * does not change, they drop every other packet in
 		 * a TCP stream using header compression.
 		 */
-		iph->id = ((sk && sk->daddr) ? htons(sk->protinfo.af_inet.id++) : 0);
+
+#ifdef CONFIG_GRKERNSEC_RANDID
+		if(grsec_enable_randid)
+			iph->id = htons(ip_randomid());
+		else
+#endif
+			iph->id = ((sk && sk->daddr) ? htons(sk->protinfo.af_inet.id++) : 0);
 	} else
 		__ip_select_ident(iph, dst);
 }
diff -urN --exclude-from=diff-exclude linux-2.4.27/init/main.c linux-2.4.27-leo/init/main.c
--- linux-2.4.27/init/main.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.27-leo/init/main.c	2004-09-17 03:19:58.000000000 +0100
@@ -28,6 +28,7 @@
 #include <linux/bootmem.h>
 #include <linux/file.h>
 #include <linux/tty.h>
+#include <linux/grsecurity.h>
 
 #include <asm/io.h>
 #include <asm/bugs.h>
@@ -112,6 +113,8 @@
 extern void ipc_init(void);
 #endif
 
+extern void grsecurity_init(void);
+
 /*
  * Boot command-line arguments
  */
@@ -560,6 +563,7 @@
 	do_basic_setup();
 
 	prepare_namespace();
+	grsecurity_init();
 
 	/*
 	 * Ok, we have completed the initial bootup, and
diff -urN --exclude-from=diff-exclude linux-2.4.27/ipc/msg.c linux-2.4.27-leo/ipc/msg.c
--- linux-2.4.27/ipc/msg.c	2003-06-13 15:51:39.000000000 +0100
+++ linux-2.4.27-leo/ipc/msg.c	2004-09-17 03:19:58.000000000 +0100
@@ -22,6 +22,7 @@
 #include <linux/init.h>
 #include <linux/proc_fs.h>
 #include <linux/list.h>
+#include <linux/grsecurity.h>
 #include <asm/uaccess.h>
 #include "util.h"
 
@@ -326,6 +327,9 @@
 		msg_unlock(id);
 	}
 	up(&msg_ids.sem);
+
+	gr_log_msgget(ret, msgflg);
+
 	return ret;
 }
 
@@ -560,6 +564,8 @@
 		break;
 	}
 	case IPC_RMID:
+		gr_log_msgrm(ipcp->uid, ipcp->cuid);
+
 		freeque (msqid); 
 		break;
 	}
diff -urN --exclude-from=diff-exclude linux-2.4.27/ipc/sem.c linux-2.4.27-leo/ipc/sem.c
--- linux-2.4.27/ipc/sem.c	2003-08-25 12:44:44.000000000 +0100
+++ linux-2.4.27-leo/ipc/sem.c	2004-09-17 03:19:58.000000000 +0100
@@ -63,6 +63,7 @@
 #include <linux/init.h>
 #include <linux/proc_fs.h>
 #include <linux/time.h>
+#include <linux/grsecurity.h>
 #include <asm/uaccess.h>
 #include "util.h"
 
@@ -182,6 +183,9 @@
 	}
 
 	up(&sem_ids.sem);
+
+	gr_log_semget(err, semflg);
+
 	return err;
 }
 
@@ -724,6 +728,8 @@
 
 	switch(cmd){
 	case IPC_RMID:
+		gr_log_semrm(ipcp->uid, ipcp->cuid);
+
 		freeary(semid);
 		err = 0;
 		break;
diff -urN --exclude-from=diff-exclude linux-2.4.27/ipc/shm.c linux-2.4.27-leo/ipc/shm.c
--- linux-2.4.27/ipc/shm.c	2002-08-03 01:39:46.000000000 +0100
+++ linux-2.4.27-leo/ipc/shm.c	2004-09-17 03:19:58.000000000 +0100
@@ -23,6 +23,7 @@
 #include <linux/mman.h>
 #include <linux/proc_fs.h>
 #include <asm/uaccess.h>
+#include <linux/grsecurity.h>
 
 #include "util.h"
 
@@ -38,8 +39,21 @@
 	time_t			shm_ctim;
 	pid_t			shm_cprid;
 	pid_t			shm_lprid;
+
+#ifdef CONFIG_GRKERNSEC
+	time_t			shm_createtime;
+	pid_t			shm_lapid;
+#endif
 };
 
+#ifdef CONFIG_GRKERNSEC
+extern int gr_handle_shmat(const pid_t shm_cprid, const pid_t shm_lapid,
+        	            const time_t shm_createtime, const uid_t cuid,
+                	    const int shmid);
+extern int gr_chroot_shmat(const pid_t shm_cprid, const pid_t shm_lapid,
+                    const time_t shm_createtime);
+#endif
+
 #define shm_flags	shm_perm.mode
 
 static struct file_operations shm_file_operations;
@@ -209,6 +223,9 @@
 	shp->shm_lprid = 0;
 	shp->shm_atim = shp->shm_dtim = 0;
 	shp->shm_ctim = CURRENT_TIME;
+#ifdef CONFIG_GRKERNSEC
+	shp->shm_createtime = CURRENT_TIME;
+#endif
 	shp->shm_segsz = size;
 	shp->shm_nattch = 0;
 	shp->id = shm_buildid(id,shp->shm_perm.seq);
@@ -254,6 +271,9 @@
 		shm_unlock(id);
 	}
 	up(&shm_ids.sem);
+
+	gr_log_shmget(err, shmflg, size);
+
 	return err;
 }
 
@@ -509,6 +529,9 @@
 			err=-EPERM;
 			goto out_unlock_up;
 		}
+
+		gr_log_shmrm(shp->shm_perm.uid, shp->shm_perm.cuid);
+
 		if (shp->shm_nattch){
 			shp->shm_flags |= SHM_DEST;
 			/* Do not find it any more */
@@ -622,9 +645,28 @@
 		shm_unlock(shmid);
 		return -EACCES;
 	}
+
+#ifdef CONFIG_GRKERNSEC
+	if (!gr_handle_shmat(shp->shm_cprid, shp->shm_lapid, shp->shm_createtime,
+			     shp->shm_perm.cuid, shmid)) {
+		shm_unlock(shmid);
+		return -EACCES;
+	}
+
+	if (!gr_chroot_shmat(shp->shm_cprid, shp->shm_lapid, shp->shm_createtime)) {
+		shm_unlock(shmid);
+		return -EACCES;
+	}
+#endif
+
 	file = shp->shm_file;
 	size = file->f_dentry->d_inode->i_size;
 	shp->shm_nattch++;
+
+#ifdef CONFIG_GRKERNSEC
+	shp->shm_lapid = current->pid;
+#endif
+
 	shm_unlock(shmid);
 
 	down_write(&current->mm->mmap_sem);
diff -urN --exclude-from=diff-exclude linux-2.4.27/kernel/capability.c linux-2.4.27-leo/kernel/capability.c
--- linux-2.4.27/kernel/capability.c	2000-06-24 05:06:37.000000000 +0100
+++ linux-2.4.27-leo/kernel/capability.c	2004-09-17 03:19:58.000000000 +0100
@@ -7,6 +7,7 @@
 
 #include <linux/mm.h>
 #include <asm/uaccess.h>
+#include <linux/grsecurity.h>
 
 kernel_cap_t cap_bset = CAP_INIT_EFF_SET;
 
@@ -168,6 +169,10 @@
              target = current;
      }
 
+     if (gr_handle_chroot_capset(target)) {
+	     error = -ESRCH;
+	     goto out;
+     }
 
      /* verify restrictions on target's new Inheritable set */
      if (!cap_issubset(inheritable,
diff -urN --exclude-from=diff-exclude linux-2.4.27/kernel/exit.c linux-2.4.27-leo/kernel/exit.c
--- linux-2.4.27/kernel/exit.c	2002-11-28 23:53:15.000000000 +0000
+++ linux-2.4.27-leo/kernel/exit.c	2004-09-17 03:19:58.000000000 +0100
@@ -16,6 +16,7 @@
 #ifdef CONFIG_BSD_PROCESS_ACCT
 #include <linux/acct.h>
 #endif
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
@@ -282,7 +283,9 @@
 	current->mm = NULL;
 	/* active_mm is still 'mm' */
 	atomic_inc(&mm->mm_count);
+	preempt_disable();
 	enter_lazy_tlb(mm, current, smp_processor_id());
+	preempt_enable();
 	return mm;
 }
 
@@ -313,8 +316,8 @@
 		/* more a memory barrier than a real lock */
 		task_lock(tsk);
 		tsk->mm = NULL;
-		task_unlock(tsk);
 		enter_lazy_tlb(mm, current, smp_processor_id());
+		task_unlock(tsk);
 		mmput(mm);
 	}
 }
@@ -435,10 +438,20 @@
 	tsk->flags |= PF_EXITING;
 	del_timer_sync(&tsk->real_timer);
 
+	if (unlikely(preempt_get_count()))
+		printk(KERN_INFO "note: %s[%d] exited with preempt_count %d\n",
+				current->comm, current->pid,
+				preempt_get_count());
+
 fake_volatile:
 #ifdef CONFIG_BSD_PROCESS_ACCT
 	acct_process(code);
 #endif
+
+	gr_acl_handle_psacct(tsk, code);
+	gr_acl_handle_exit();
+	gr_del_task_from_ip_table(tsk);
+
 	__exit_mm(tsk);
 
 	lock_kernel();
diff -urN --exclude-from=diff-exclude linux-2.4.27/kernel/fork.c linux-2.4.27-leo/kernel/fork.c
--- linux-2.4.27/kernel/fork.c	2004-05-19 21:34:41.000000000 +0100
+++ linux-2.4.27-leo/kernel/fork.c	2004-09-17 03:19:58.000000000 +0100
@@ -22,6 +22,7 @@
 #include <linux/namespace.h>
 #include <linux/personality.h>
 #include <linux/compiler.h>
+#include <linux/grsecurity.h>
 
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>
@@ -93,6 +94,10 @@
 	if (flags & CLONE_PID)
 		return current->pid;
 
+	pid = gr_random_pid(&lastpid_lock);
+	if (pid)
+		return pid;
+
 	spin_lock(&lastpid_lock);
 	beginpid = last_pid;
 	if((++last_pid) & 0xffff8000) {
@@ -656,6 +661,8 @@
 			goto fork_out;
 	}
 
+	gr_handle_brute_check();
+
 	retval = -ENOMEM;
 	p = alloc_task_struct();
 	if (!p)
@@ -670,6 +677,9 @@
 	 * friends to set the per-user process limit to something lower
 	 * than the amount of processes root is running. -- Rik
 	 */
+
+	gr_learn_resource(p, RLIMIT_NPROC, atomic_read(&p->user->processes), 0);
+
 	if (atomic_read(&p->user->processes) >= p->rlim[RLIMIT_NPROC].rlim_cur
 		      && p->user != &root_user
 	              && !capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE))
@@ -691,6 +701,13 @@
 	if (p->binfmt && p->binfmt->module)
 		__MOD_INC_USE_COUNT(p->binfmt->module);
 
+#ifdef CONFIG_PREEMPT
+	/*
+	 * Continue with preemption disabled as part of the context
+	 * switch, so start with preempt_count set to 1.
+	 */
+	p->preempt_count = 1;
+#endif
 	p->did_exec = 0;
 	p->swappable = 0;
 	p->state = TASK_UNINTERRUPTIBLE;
@@ -756,6 +773,7 @@
 	retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
 	if (retval)
 		goto bad_fork_cleanup_namespace;
+	gr_copy_label(p);
 	p->semundo = NULL;
 	
 	/* Our parent execution domain becomes current domain
@@ -843,6 +861,9 @@
 	free_uid(p->user);
 bad_fork_free:
 	free_task_struct(p);
+
+	gr_log_forkfail(retval);
+
 	goto fork_out;
 }
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/kernel/ksyms.c linux-2.4.27-leo/kernel/ksyms.c
--- linux-2.4.27/kernel/ksyms.c	2004-02-20 14:11:47.000000000 +0000
+++ linux-2.4.27-leo/kernel/ksyms.c	2004-09-17 03:19:58.000000000 +0100
@@ -50,6 +50,7 @@
 #include <linux/dnotify.h>
 #include <linux/crc32.h>
 #include <linux/firmware.h>
+#include <linux/grsecurity.h>
 #include <asm/checksum.h>
 
 #if defined(CONFIG_PROC_FS)
@@ -86,6 +87,7 @@
 
 /* process memory management */
 EXPORT_SYMBOL(do_mmap_pgoff);
+EXPORT_SYMBOL(__do_mmap_pgoff);
 EXPORT_SYMBOL(do_munmap);
 EXPORT_SYMBOL(do_brk);
 EXPORT_SYMBOL(exit_mm);
@@ -115,6 +117,7 @@
 EXPORT_SYMBOL(__vmalloc);
 EXPORT_SYMBOL(vmap);
 EXPORT_SYMBOL(vmalloc_to_page);
+EXPORT_SYMBOL(vcalloc);
 EXPORT_SYMBOL(mem_map);
 EXPORT_SYMBOL(remap_page_range);
 EXPORT_SYMBOL(max_mapnr);
@@ -194,6 +197,8 @@
 EXPORT_SYMBOL(invalidate_inode_pages);
 EXPORT_SYMBOL(truncate_inode_pages);
 EXPORT_SYMBOL(fsync_dev);
+EXPORT_SYMBOL(fsync_dev_lockfs);
+EXPORT_SYMBOL(unlockfs);
 EXPORT_SYMBOL(fsync_no_super);
 EXPORT_SYMBOL(permission);
 EXPORT_SYMBOL(vfs_permission);
@@ -468,6 +473,9 @@
 EXPORT_SYMBOL(interruptible_sleep_on);
 EXPORT_SYMBOL(interruptible_sleep_on_timeout);
 EXPORT_SYMBOL(schedule);
+#ifdef CONFIG_PREEMPT
+EXPORT_SYMBOL(preempt_schedule);
+#endif
 EXPORT_SYMBOL(schedule_timeout);
 #if CONFIG_SMP
 EXPORT_SYMBOL(set_cpus_allowed);
@@ -622,3 +630,9 @@
 /* To match ksyms with System.map */
 extern const char _end[];
 EXPORT_SYMBOL(_end);
+
+/* grsecurity */
+EXPORT_SYMBOL(gr_task_is_capable);
+EXPORT_SYMBOL(gr_pid_is_chrooted);
+EXPORT_SYMBOL(gr_learn_resource);
+EXPORT_SYMBOL(gr_set_kernel_label);
diff -urN --exclude-from=diff-exclude linux-2.4.27/kernel/module.c linux-2.4.27-leo/kernel/module.c
--- linux-2.4.27/kernel/module.c	2003-08-25 12:44:44.000000000 +0100
+++ linux-2.4.27-leo/kernel/module.c	2004-09-17 03:19:58.000000000 +0100
@@ -900,6 +900,11 @@
 	struct module *mod;
 	int err;
 
+#ifdef CONFIG_GRKERNSEC_HIDESYM
+	if (!capable(CAP_SYS_MODULE))
+		return -EPERM;
+#endif
+
 	lock_kernel();
 	if (name_user == NULL)
 		mod = &kernel_module;
@@ -969,6 +974,11 @@
 	int i;
 	struct kernel_sym ksym;
 
+#ifdef CONFIG_GRKERNSEC_HIDESYM
+	if (!capable(CAP_SYS_MODULE))
+		return 0;
+#endif
+
 	lock_kernel();
 	for (mod = module_list, i = 0; mod; mod = mod->next) {
 		/* include the count for the module name! */
diff -urN --exclude-from=diff-exclude linux-2.4.27/kernel/printk.c linux-2.4.27-leo/kernel/printk.c
--- linux-2.4.27/kernel/printk.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.27-leo/kernel/printk.c	2004-09-17 03:19:58.000000000 +0100
@@ -26,6 +26,7 @@
 #include <linux/module.h>
 #include <linux/interrupt.h>			/* For in_interrupt() */
 #include <linux/config.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 
@@ -298,6 +299,11 @@
 
 asmlinkage long sys_syslog(int type, char * buf, int len)
 {
+#ifdef CONFIG_GRKERNSEC_DMESG
+	if (!capable(CAP_SYS_ADMIN) && grsec_enable_dmesg)
+		return -EPERM;
+	else
+#endif
 	if ((type != 3) && !capable(CAP_SYS_ADMIN))
 		return -EPERM;
 	return do_syslog(type, buf, len);
diff -urN --exclude-from=diff-exclude linux-2.4.27/kernel/sched.c linux-2.4.27-leo/kernel/sched.c
--- linux-2.4.27/kernel/sched.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.27-leo/kernel/sched.c	2004-09-17 03:19:58.000000000 +0100
@@ -22,6 +22,7 @@
 #include <linux/config.h>
 #include <linux/mm.h>
 #include <linux/init.h>
+#include <linux/file.h>
 #include <linux/smp_lock.h>
 #include <linux/nmi.h>
 #include <linux/interrupt.h>
@@ -29,6 +30,11 @@
 #include <linux/completion.h>
 #include <linux/prefetch.h>
 #include <linux/compiler.h>
+#include <linux/grsecurity.h>
+
+#ifdef CONFIG_GRKERNSEC
+extern rwlock_t grsec_exec_file_lock;
+#endif
 
 #include <asm/uaccess.h>
 #include <asm/mmu_context.h>
@@ -489,7 +495,7 @@
 	task_lock(prev);
 	task_release_cpu(prev);
 	mb();
-	if (prev->state == TASK_RUNNING)
+	if (task_on_runqueue(prev))
 		goto needs_resched;
 
 out_unlock:
@@ -519,7 +525,7 @@
 			goto out_unlock;
 
 		spin_lock_irqsave(&runqueue_lock, flags);
-		if ((prev->state == TASK_RUNNING) && !task_has_cpu(prev))
+		if (task_on_runqueue(prev) && !task_has_cpu(prev))
 			reschedule_idle(prev);
 		spin_unlock_irqrestore(&runqueue_lock, flags);
 		goto out_unlock;
@@ -532,6 +538,7 @@
 asmlinkage void schedule_tail(struct task_struct *prev)
 {
 	__schedule_tail(prev);
+	preempt_enable();
 }
 
 /*
@@ -551,9 +558,10 @@
 	struct list_head *tmp;
 	int this_cpu, c;
 
-
 	spin_lock_prefetch(&runqueue_lock);
 
+	preempt_disable();
+
 	BUG_ON(!current->active_mm);
 need_resched_back:
 	prev = current;
@@ -581,6 +589,14 @@
 			move_last_runqueue(prev);
 		}
 
+#ifdef CONFIG_PREEMPT
+	/*
+	 * entering from preempt_schedule, off a kernel preemption,
+	 * go straight to picking the next task.
+	 */
+	if (unlikely(preempt_get_count() & PREEMPT_ACTIVE))
+		goto treat_like_run;
+#endif
 	switch (prev->state) {
 		case TASK_INTERRUPTIBLE:
 			if (signal_pending(prev)) {
@@ -591,6 +607,9 @@
 			del_from_runqueue(prev);
 		case TASK_RUNNING:;
 	}
+#ifdef CONFIG_PREEMPT
+	treat_like_run:
+#endif
 	prev->need_resched = 0;
 
 	/*
@@ -699,9 +718,31 @@
 	reacquire_kernel_lock(current);
 	if (current->need_resched)
 		goto need_resched_back;
+	preempt_enable_no_resched();
 	return;
 }
 
+#ifdef CONFIG_PREEMPT
+/*
+ * this is is the entry point to schedule() from in-kernel preemption
+ */
+asmlinkage void preempt_schedule(void)
+{
+	if (unlikely(irqs_disabled()))
+			return;
+
+need_resched:
+	current->preempt_count += PREEMPT_ACTIVE;
+	schedule();
+	current->preempt_count -= PREEMPT_ACTIVE;
+
+	/* we could miss a preemption opportunity between schedule and now */
+	barrier();
+	if (unlikely(current->need_resched))
+		goto need_resched;
+}
+#endif /* CONFIG_PREEMPT */
+
 /*
  * The core wakeup function.  Non-exclusive wakeups (nr_exclusive == 0) just wake everything
  * up.  If it's an exclusive wakeup (nr_exclusive == small +ve number) then we wake all the
@@ -910,6 +951,9 @@
 			return -EPERM;
 		if (increment < -40)
 			increment = -40;
+
+		if (gr_handle_chroot_nice())
+			return -EPERM;
 	}
 	if (increment > 40)
 		increment = 40;
@@ -1288,12 +1332,23 @@
 
 	write_lock_irq(&tasklist_lock);
 
+#ifdef CONFIG_GRKERNSEC
+	write_lock(&grsec_exec_file_lock);
+	if (this_task->exec_file) {
+		fput(this_task->exec_file);
+		this_task->exec_file = NULL;
+	}
+	write_unlock(&grsec_exec_file_lock);
+#endif
+
 	/* Reparent to init */
 	REMOVE_LINKS(this_task);
 	this_task->p_pptr = child_reaper;
 	this_task->p_opptr = child_reaper;
 	SET_LINKS(this_task);
 
+	gr_set_kernel_label(this_task);
+
 	/* Set the exit signal to SIGCHLD so we signal init on exit */
 	this_task->exit_signal = SIGCHLD;
 
@@ -1327,6 +1382,15 @@
 {
 	struct fs_struct *fs;
 
+#ifdef CONFIG_GRKERNSEC
+	write_lock(&grsec_exec_file_lock);
+	if (current->exec_file) {
+		fput(current->exec_file);
+		current->exec_file = NULL;
+	}
+	write_unlock(&grsec_exec_file_lock);
+#endif
+	gr_set_kernel_label(current);
 
 	/*
 	 * If we were started as result of loading a module, close all of the
@@ -1365,6 +1429,13 @@
 	sched_data->curr = current;
 	sched_data->last_schedule = get_cycles();
 	clear_bit(current->processor, &wait_init_idle);
+#ifdef CONFIG_PREEMPT
+	/*
+	 * fix up the preempt_count for non-CPU0 idle threads
+	 */
+	if (current->processor)
+		current->preempt_count = 0;
+#endif
 }
 
 extern void init_timervecs (void);
diff -urN --exclude-from=diff-exclude linux-2.4.27/kernel/signal.c linux-2.4.27-leo/kernel/signal.c
--- linux-2.4.27/kernel/signal.c	2004-02-20 14:11:47.000000000 +0000
+++ linux-2.4.27-leo/kernel/signal.c	2004-09-17 03:19:58.000000000 +0100
@@ -13,6 +13,8 @@
 #include <linux/smp_lock.h>
 #include <linux/init.h>
 #include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 
@@ -340,7 +342,7 @@
 	    && ((sig != SIGCONT) || (current->session != t->session))
 	    && (current->euid ^ t->suid) && (current->euid ^ t->uid)
 	    && (current->uid ^ t->suid) && (current->uid ^ t->uid)
-	    && !capable(CAP_KILL);
+	    && !capable(CAP_KILL) && !gr_handle_signal(t, sig);
 }
 
 /*
@@ -554,6 +556,8 @@
 	if (!sig || !t->sig)
 		goto out_nolock;
 
+	gr_log_signal(sig, t);
+
 	spin_lock_irqsave(&t->sigmask_lock, flags);
 	handle_stop_signal(sig, t);
 
@@ -603,6 +607,8 @@
 	recalc_sigpending(t);
 	spin_unlock_irqrestore(&t->sigmask_lock, flags);
 
+	gr_handle_crash(t, sig);
+
 	return send_sig_info(sig, info, t);
 }
 
@@ -1048,7 +1054,7 @@
        p = find_task_by_pid(pid);
        error = -ESRCH;
        if (p) {
-               error = send_sig_info(sig, &info, p);
+              error = send_sig_info(sig, &info, p);
        }
        read_unlock(&tasklist_lock);
        return error;
diff -urN --exclude-from=diff-exclude linux-2.4.27/kernel/softirq.c linux-2.4.27-leo/kernel/softirq.c
--- linux-2.4.27/kernel/softirq.c	2002-11-28 23:53:15.000000000 +0000
+++ linux-2.4.27-leo/kernel/softirq.c	2004-09-17 03:19:36.000000000 +0100
@@ -60,7 +60,7 @@
 
 asmlinkage void do_softirq()
 {
-	int cpu = smp_processor_id();
+	int cpu;
 	__u32 pending;
 	unsigned long flags;
 	__u32 mask;
@@ -70,6 +70,8 @@
 
 	local_irq_save(flags);
 
+	cpu = smp_processor_id();
+
 	pending = softirq_pending(cpu);
 
 	if (pending) {
@@ -151,10 +153,11 @@
 
 void __tasklet_schedule(struct tasklet_struct *t)
 {
-	int cpu = smp_processor_id();
+	int cpu;
 	unsigned long flags;
 
 	local_irq_save(flags);
+	cpu = smp_processor_id();
 	t->next = tasklet_vec[cpu].list;
 	tasklet_vec[cpu].list = t;
 	cpu_raise_softirq(cpu, TASKLET_SOFTIRQ);
@@ -175,10 +178,11 @@
 
 static void tasklet_action(struct softirq_action *a)
 {
-	int cpu = smp_processor_id();
+	int cpu;
 	struct tasklet_struct *list;
 
 	local_irq_disable();
+	cpu = smp_processor_id();
 	list = tasklet_vec[cpu].list;
 	tasklet_vec[cpu].list = NULL;
 	local_irq_enable();
@@ -209,10 +213,11 @@
 
 static void tasklet_hi_action(struct softirq_action *a)
 {
-	int cpu = smp_processor_id();
+	int cpu;
 	struct tasklet_struct *list;
 
 	local_irq_disable();
+	cpu = smp_processor_id();
 	list = tasklet_hi_vec[cpu].list;
 	tasklet_hi_vec[cpu].list = NULL;
 	local_irq_enable();
diff -urN --exclude-from=diff-exclude linux-2.4.27/kernel/sys.c linux-2.4.27-leo/kernel/sys.c
--- linux-2.4.27/kernel/sys.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.27-leo/kernel/sys.c	2004-09-17 03:19:58.000000000 +0100
@@ -4,6 +4,7 @@
  *  Copyright (C) 1991, 1992  Linus Torvalds
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/mm.h>
 #include <linux/utsname.h>
@@ -14,6 +15,7 @@
 #include <linux/prctl.h>
 #include <linux/init.h>
 #include <linux/highuid.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -239,6 +241,12 @@
 		}
 		if (error == -ESRCH)
 			error = 0;
+
+		if (gr_handle_chroot_setpriority(p, niceval)) {
+			read_unlock(&tasklist_lock);
+			return -EACCES;
+		}
+
 		if (niceval < p->nice && !capable(CAP_SYS_NICE))
 			error = -EACCES;
 		else
@@ -320,6 +328,7 @@
 		notifier_call_chain(&reboot_notifier_list, SYS_HALT, NULL);
 		printk(KERN_EMERG "System halted.\n");
 		machine_halt();
+		unlock_kernel();
 		do_exit(0);
 		break;
 
@@ -327,6 +336,7 @@
 		notifier_call_chain(&reboot_notifier_list, SYS_POWER_OFF, NULL);
 		printk(KERN_EMERG "Power down.\n");
 		machine_power_off();
+		unlock_kernel();
 		do_exit(0);
 		break;
 
@@ -417,6 +427,10 @@
 			return -EPERM;
 		}
 	}
+
+	if (gr_check_group_change(new_rgid, new_egid, -1))
+		return -EPERM;
+
 	if (new_egid != old_egid)
 	{
 		current->mm->dumpable = 0;
@@ -425,6 +439,9 @@
 	if (rgid != (gid_t) -1 ||
 	    (egid != (gid_t) -1 && egid != old_rgid))
 		current->sgid = new_egid;
+
+	gr_set_role_label(current, current->uid, new_rgid);
+
 	current->fsgid = new_egid;
 	current->egid = new_egid;
 	current->gid = new_rgid;
@@ -440,6 +457,9 @@
 {
 	int old_egid = current->egid;
 
+	if (gr_check_group_change(gid, gid, gid))
+		return -EPERM;
+
 	if (capable(CAP_SETGID))
 	{
 		if(old_egid != gid)
@@ -447,6 +467,9 @@
 			current->mm->dumpable=0;
 			wmb();
 		}
+
+		gr_set_role_label(current, current->uid, gid);
+
 		current->gid = current->egid = current->sgid = current->fsgid = gid;
 	}
 	else if ((gid == current->gid) || (gid == current->sgid))
@@ -523,6 +546,9 @@
 		current->mm->dumpable = 0;
 		wmb();
 	}
+
+	gr_set_role_label(current, new_ruid, current->gid);
+
 	current->uid = new_ruid;
 	return 0;
 }
@@ -567,6 +593,9 @@
 			return -EPERM;
 	}
 
+	if (gr_check_user_change(new_ruid, new_euid, -1))
+		return -EPERM;
+
 	if (new_ruid != old_ruid && set_user(new_ruid, new_euid != old_euid) < 0)
 		return -EAGAIN;
 
@@ -610,6 +639,12 @@
 	old_suid = current->suid;
 	new_suid = old_suid;
 	
+	if (gr_check_crash_uid(uid))
+		return -EPERM;
+
+	if (gr_check_user_change(uid, uid, uid))
+		return -EPERM;
+
 	if (capable(CAP_SETUID)) {
 		if (uid != old_ruid && set_user(uid, old_euid != uid) < 0)
 			return -EAGAIN;
@@ -654,6 +689,10 @@
 		    (suid != current->euid) && (suid != current->suid))
 			return -EPERM;
 	}
+
+	if (gr_check_user_change(ruid, euid, -1))
+		return -EPERM;
+
 	if (ruid != (uid_t) -1) {
 		if (ruid != current->uid && set_user(ruid, euid != current->euid) < 0)
 			return -EAGAIN;
@@ -704,6 +743,10 @@
 		    (sgid != current->egid) && (sgid != current->sgid))
 			return -EPERM;
 	}
+
+	if (gr_check_group_change(rgid, egid, -1))
+		return -EPERM;
+
 	if (egid != (gid_t) -1) {
 		if (egid != current->egid)
 		{
@@ -713,8 +756,10 @@
 		current->egid = egid;
 	}
 	current->fsgid = current->egid;
-	if (rgid != (gid_t) -1)
+	if (rgid != (gid_t) -1) {
+		gr_set_role_label(current, current->uid, rgid);
 		current->gid = rgid;
+	}
 	if (sgid != (gid_t) -1)
 		current->sgid = sgid;
 	return 0;
@@ -747,6 +792,9 @@
 	    uid == current->suid || uid == current->fsuid || 
 	    capable(CAP_SETUID))
 	{
+		if (gr_check_user_change(-1, -1, uid))
+			return -EPERM;
+
 		if (uid != old_fsuid)
 		{
 			current->mm->dumpable = 0;
@@ -789,6 +837,9 @@
 	    gid == current->sgid || gid == current->fsgid || 
 	    capable(CAP_SETGID))
 	{
+		if (gr_check_group_change(-1, -1, gid))
+			return -EPERM;
+
 		if (gid != old_fsgid)
 		{
 			current->mm->dumpable = 0;
@@ -1137,6 +1188,10 @@
        if (new_rlim.rlim_cur > new_rlim.rlim_max)
                return -EINVAL;
 	old_rlim = current->rlim + resource;
+
+	if (old_rlim->rlim_max < old_rlim->rlim_cur)
+		return -EINVAL;
+
 	if (((new_rlim.rlim_cur > old_rlim->rlim_max) ||
 	     (new_rlim.rlim_max > old_rlim->rlim_max)) &&
 	    !capable(CAP_SYS_RESOURCE))
diff -urN --exclude-from=diff-exclude linux-2.4.27/kernel/sysctl.c linux-2.4.27-leo/kernel/sysctl.c
--- linux-2.4.27/kernel/sysctl.c	2004-09-17 02:38:47.000000000 +0100
+++ linux-2.4.27-leo/kernel/sysctl.c	2004-09-17 03:19:58.000000000 +0100
@@ -39,6 +39,13 @@
 #endif
 
 #if defined(CONFIG_SYSCTL)
+#include <linux/grsecurity.h>
+#include <linux/grinternal.h>
+
+extern __u32 gr_handle_sysctl(const ctl_table * table, const void *oldval,
+			      const void *newval);
+extern int gr_handle_sysctl_mod(const char *dirname, const char *name, const int op);
+extern int gr_handle_chroot_sysctl(const int op);
 
 /* External variables not in a header file. */
 extern int panic_timeout;
@@ -128,6 +135,19 @@
 static ctl_table dev_table[];
 extern ctl_table random_table[];
 
+static ctl_table grsecurity_table[];
+
+#ifdef CONFIG_GRKERNSEC_PAX_SOFTMODE
+static ctl_table pax_table[] = {
+
+#if defined(CONFIG_GRKERNSEC_PAX_RANDMMAP) || defined(CONFIG_GRKERNSEC_PAX_RANDUSTACK) || defined(CONFIG_GRKERNSEC_PAX_RANDKSTACK)
+	{PAX_ASLR, "aslr", &pax_aslr, sizeof(unsigned int), 0600, NULL, &proc_dointvec},
+#endif
+
+	{PAX_SOFTMODE, "softmode", &pax_softmode, sizeof(unsigned int), 0600, NULL, &proc_dointvec}
+};
+#endif
+
 /* /proc declarations: */
 
 #ifdef CONFIG_PROC_FS
@@ -278,9 +298,201 @@
 	{KERN_EXCEPTION_TRACE,"exception-trace",
 	 &exception_trace,sizeof(int),0644,NULL,&proc_dointvec},
 #endif	
+#ifdef CONFIG_GRKERNSEC_SYSCTL
+	{KERN_GRSECURITY, "grsecurity", NULL, 0, 0500, grsecurity_table},
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_SOFTMODE
+	{KERN_PAX,"pax",NULL,0,0500,pax_table},
+#endif
+
 	{0}
 };
 
+#ifdef CONFIG_GRKERNSEC_SYSCTL
+enum {GS_LINK=1, GS_FIFO, GS_EXECVE, GS_EXECLOG, GS_SIGNAL, 
+GS_FORKFAIL, GS_TIME, GS_CHROOT_SHMAT, GS_CHROOT_UNIX, GS_CHROOT_MNT,
+GS_CHROOT_FCHDIR, GS_CHROOT_DBL, GS_CHROOT_PVT, GS_CHROOT_CD, GS_CHROOT_CM,
+GS_CHROOT_MK, GS_CHROOT_NI, GS_CHROOT_EXECLOG, GS_CHROOT_CAPS, 
+GS_CHROOT_SYSCTL, GS_TPE, GS_TPE_GID, GS_TPE_ALL,
+GS_RANDPID, GS_RANDID, GS_RANDSRC, GS_RANDISN,
+GS_SOCKET_ALL, GS_SOCKET_ALL_GID, GS_SOCKET_CLIENT,
+GS_SOCKET_CLIENT_GID, GS_SOCKET_SERVER, GS_SOCKET_SERVER_GID,
+GS_GROUP, GS_GID, GS_ACHDIR, GS_AMOUNT, GS_AIPC, GS_DMSG, GS_RANDRPC,
+GS_TEXTREL, GS_FINDTASK, GS_LOCK};
+
+static ctl_table grsecurity_table[] = {
+#ifdef CONFIG_GRKERNSEC_LINK
+	{GS_LINK, "linking_restrictions", &grsec_enable_link, sizeof (int),
+	 0600, NULL, &proc_dointvec}, 
+#endif
+#ifdef CONFIG_GRKERNSEC_FIFO
+	{GS_FIFO, "fifo_restrictions", &grsec_enable_fifo, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_EXECVE
+	{GS_EXECVE, "execve_limiting", &grsec_enable_execve, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_EXECLOG
+	{GS_EXECLOG, "exec_logging", &grsec_enable_execlog, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_SIGNAL
+	{GS_SIGNAL, "signal_logging", &grsec_enable_signal, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_FORKFAIL
+	{GS_FORKFAIL, "forkfail_logging", &grsec_enable_forkfail, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_TIME
+	{GS_TIME, "timechange_logging", &grsec_enable_time, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_SHMAT
+	{GS_CHROOT_SHMAT, "chroot_deny_shmat", &grsec_enable_chroot_shmat, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_UNIX
+	{GS_CHROOT_UNIX, "chroot_deny_unix", &grsec_enable_chroot_unix, sizeof(int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_MOUNT
+	{GS_CHROOT_MNT, "chroot_deny_mount", &grsec_enable_chroot_mount, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_FCHDIR
+	{GS_CHROOT_FCHDIR, "chroot_deny_fchdir", &grsec_enable_chroot_fchdir, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_DOUBLE
+	{GS_CHROOT_DBL, "chroot_deny_chroot", &grsec_enable_chroot_double, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_PIVOT
+	{GS_CHROOT_PVT, "chroot_deny_pivot", &grsec_enable_chroot_pivot, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_CHDIR
+	{GS_CHROOT_CD, "chroot_enforce_chdir", &grsec_enable_chroot_chdir, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_CHMOD
+	{GS_CHROOT_CM, "chroot_deny_chmod", &grsec_enable_chroot_chmod, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_MKNOD
+	{GS_CHROOT_MK, "chroot_deny_mknod", &grsec_enable_chroot_mknod, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_NICE
+	{GS_CHROOT_NI, "chroot_restrict_nice", &grsec_enable_chroot_nice, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_EXECLOG
+	{GS_CHROOT_EXECLOG, "chroot_execlog",
+	 &grsec_enable_chroot_execlog, sizeof (int),
+         0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_CAPS
+	{GS_CHROOT_CAPS, "chroot_caps", &grsec_enable_chroot_caps, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_SYSCTL
+	{GS_CHROOT_SYSCTL, "chroot_deny_sysctl", &grsec_enable_chroot_sysctl, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_TPE
+	{GS_TPE, "tpe", &grsec_enable_tpe, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+	{GS_TPE_GID, "tpe_gid", &grsec_tpe_gid, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_TPE_ALL
+	{GS_TPE_ALL, "tpe_restrict_all", &grsec_enable_tpe_all, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_RANDPID
+	{GS_RANDPID, "rand_pids", &grsec_enable_randpid, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_RANDID
+	{GS_RANDID, "rand_ip_ids", &grsec_enable_randid, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_RANDSRC
+	{GS_RANDSRC, "rand_tcp_src_ports", &grsec_enable_randsrc, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_RANDISN
+	{GS_RANDISN, "rand_isns", &grsec_enable_randisn, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_SOCKET_ALL
+	{GS_SOCKET_ALL, "socket_all", &grsec_enable_socket_all, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+	{GS_SOCKET_ALL_GID, "socket_all_gid",
+	 &grsec_socket_all_gid, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_SOCKET_CLIENT
+	{GS_SOCKET_CLIENT, "socket_client", 
+	 &grsec_enable_socket_client, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+	{GS_SOCKET_CLIENT_GID, "socket_client_gid", 
+	 &grsec_socket_client_gid, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_SOCKET_SERVER
+	{GS_SOCKET_SERVER, "socket_server", 
+	 &grsec_enable_socket_server, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+	{GS_SOCKET_SERVER_GID, "socket_server_gid",
+	 &grsec_socket_server_gid, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_AUDIT_GROUP
+	{GS_GROUP, "audit_group", &grsec_enable_group, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+	{GS_GID, "audit_gid",
+	 &grsec_audit_gid, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_AUDIT_CHDIR
+	{GS_ACHDIR, "audit_chdir", &grsec_enable_chdir, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_AUDIT_MOUNT
+	{GS_AMOUNT, "audit_mount", &grsec_enable_mount, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_AUDIT_TEXTREL
+	{GS_TEXTREL, "audit_textrel", &grsec_enable_audit_textrel, sizeof(int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_AUDIT_IPC
+	{GS_AIPC, "audit_ipc", &grsec_enable_audit_ipc, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_DMESG
+	{GS_DMSG, "dmesg", &grsec_enable_dmesg, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_RANDRPC
+	{GS_RANDRPC, "rand_rpc", &grsec_enable_randrpc, sizeof (int),
+	 0600, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_GRKERNSEC_CHROOT_FINDTASK
+	{GS_FINDTASK, "chroot_findtask", &grsec_enable_chroot_findtask, 
+	 sizeof (int), 0600, NULL, &proc_dointvec},
+#endif
+	{GS_LOCK, "grsec_lock", &grsec_lock, sizeof (int), 0600, NULL,
+	 &proc_dointvec},
+	{0}
+};
+#endif
+
 static ctl_table vm_table[] = {
 	{VM_GFP_DEBUG, "vm_gfp_debug", 
 	 &vm_gfp_debug, sizeof(int), 0644, NULL, &proc_dointvec},
@@ -428,6 +640,11 @@
 
 static inline int ctl_perm(ctl_table *table, int op)
 {
+	if (table->de && gr_handle_sysctl_mod(table->de->parent->name, table->de->name, op))
+		return -EACCES;
+	if (gr_handle_chroot_sysctl(op))
+		return -EACCES;
+
 	return test_perm(table->mode, op);
 }
 
@@ -461,6 +678,10 @@
 				table = table->child;
 				goto repeat;
 			}
+
+			if (!gr_handle_sysctl(table, oldval, newval))
+				return -EACCES;
+
 			error = do_sysctl_strategy(table, name, nlen,
 						   oldval, oldlenp,
 						   newval, newlen, context);
diff -urN --exclude-from=diff-exclude linux-2.4.27/kernel/time.c linux-2.4.27-leo/kernel/time.c
--- linux-2.4.27/kernel/time.c	2002-11-28 23:53:15.000000000 +0000
+++ linux-2.4.27-leo/kernel/time.c	2004-09-17 03:19:58.000000000 +0100
@@ -27,6 +27,7 @@
 #include <linux/mm.h>
 #include <linux/timex.h>
 #include <linux/smp_lock.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 
@@ -89,6 +90,9 @@
 	time_maxerror = NTP_PHASE_LIMIT;
 	time_esterror = NTP_PHASE_LIMIT;
 	write_unlock_irq(&xtime_lock);
+
+	gr_log_timechange();
+
 	return 0;
 }
 
@@ -167,6 +171,8 @@
 		 * globally block out interrupts when it runs.
 		 */
 		do_settimeofday(tv);
+
+		gr_log_timechange();
 	}
 	return 0;
 }
diff -urN --exclude-from=diff-exclude linux-2.4.27/kernel/timer.c linux-2.4.27-leo/kernel/timer.c
--- linux-2.4.27/kernel/timer.c	2002-11-28 23:53:15.000000000 +0000
+++ linux-2.4.27-leo/kernel/timer.c	2004-09-17 03:19:58.000000000 +0100
@@ -541,6 +541,9 @@
 
 	psecs = (p->times.tms_utime += user);
 	psecs += (p->times.tms_stime += system);
+
+	gr_learn_resource(p, RLIMIT_CPU, psecs / HZ, 1);
+
 	if (psecs / HZ > p->rlim[RLIMIT_CPU].rlim_cur) {
 		/* Send SIGXCPU every second.. */
 		if (!(psecs % HZ))
diff -urN --exclude-from=diff-exclude linux-2.4.27/lib/dec_and_lock.c linux-2.4.27-leo/lib/dec_and_lock.c
--- linux-2.4.27/lib/dec_and_lock.c	2001-10-03 17:11:26.000000000 +0100
+++ linux-2.4.27-leo/lib/dec_and_lock.c	2004-09-17 03:19:36.000000000 +0100
@@ -1,5 +1,6 @@
 #include <linux/module.h>
 #include <linux/spinlock.h>
+#include <linux/sched.h>
 #include <asm/atomic.h>
 
 /*
diff -urN --exclude-from=diff-exclude linux-2.4.27/MAINTAINERS linux-2.4.27-leo/MAINTAINERS
--- linux-2.4.27/MAINTAINERS	2004-09-17 02:38:42.000000000 +0100
+++ linux-2.4.27-leo/MAINTAINERS	2004-09-20 21:34:47.000000000 +0100
@@ -589,6 +589,13 @@
 W:	http://www.debian.org/~dz/i8k/
 S:	Maintained
 
+DEVICE MAPPER
+P:	Joe Thornber
+M:	dm@uk.sistina.com
+L:	linux-LVM@sistina.com
+W:	http://www.sistina.com/lvm
+S:	Maintained
+
 DEVICE NUMBER REGISTRY
 P:	H. Peter Anvin
 M:	hpa@zytor.com
@@ -1525,6 +1532,14 @@
 M:	mostrows@styx.uwaterloo.ca
 S:	Maintained
 
+PREEMPTIBLE KERNEL
+P:	Robert M. Love
+M:	rml@tech9.net
+L:	linux-kernel@vger.kernel.org
+L:	kpreempt-tech@lists.sourceforge.net
+W:	http://tech9.net/rml/linux
+S:	Supported
+
 PROMISE DC4030 CACHING DISK CONTROLLER DRIVER
 P:	Peter Denison
 M:	promise@pnd-pc.demon.co.uk
@@ -1661,6 +1676,15 @@
 L:	linux-ide@vger.kernel.org
 S:	Supported
 
+SENSORS DRIVERS
+P:      Frodo Looijaard
+M:      frodol@dds.nl
+P:      Philip Edelbrock
+M:      phil@netroedge.com
+L:      sensors@stimpy.netroedge.com
+W:      http://www.lm-sensors.nu/
+S:      Maintained
+
 SGI VISUAL WORKSTATION 320 AND 540
 P:	Bent Hagemark
 M:	bh@sgi.com
diff -urN --exclude-from=diff-exclude linux-2.4.27/Makefile linux-2.4.27-leo/Makefile
--- linux-2.4.27/Makefile	2004-09-17 02:38:42.000000000 +0100
+++ linux-2.4.27-leo/Makefile	2004-09-20 21:35:40.000000000 +0100
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 27
-EXTRAVERSION =
+EXTRAVERSION = -leo
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
@@ -126,9 +126,10 @@
 
 CORE_FILES	=kernel/kernel.o mm/mm.o fs/fs.o ipc/ipc.o
 NETWORKS	=net/network.o
+GRSECURITY	=grsecurity/grsec.o
 
 LIBS		=$(TOPDIR)/lib/lib.a
-SUBDIRS		=kernel drivers mm fs net ipc lib crypto
+SUBDIRS		=kernel drivers mm fs net ipc lib crypto grsecurity
 
 DRIVERS-n :=
 DRIVERS-y :=
@@ -195,6 +196,7 @@
 DRIVERS-$(CONFIG_ISDN_BOOL) += drivers/isdn/vmlinux-obj.o
 DRIVERS-$(CONFIG_CRYPTO) += crypto/crypto.o
 
+DRIVERS-$(CONFIG_SENSORS) += drivers/sensors/sensor.o
 DRIVERS := $(DRIVERS-y)
 
 
@@ -272,7 +274,7 @@
 
 export	CPPFLAGS CFLAGS CFLAGS_KERNEL AFLAGS AFLAGS_KERNEL
 
-export	NETWORKS DRIVERS LIBS HEAD LDFLAGS LINKFLAGS MAKEBOOT ASFLAGS
+export	NETWORKS DRIVERS LIBS HEAD LDFLAGS LINKFLAGS MAKEBOOT ASFLAGS GRSECURITY
 
 .S.s:
 	$(CPP) $(AFLAGS) $(AFLAGS_KERNEL) -traditional -o $*.s $<
@@ -291,6 +293,7 @@
 		$(CORE_FILES) \
 		$(DRIVERS) \
 		$(NETWORKS) \
+		$(GRSECURITY) \
 		$(LIBS) \
 		--end-group \
 		-o vmlinux
diff -urN --exclude-from=diff-exclude linux-2.4.27/mm/filemap.c linux-2.4.27-leo/mm/filemap.c
--- linux-2.4.27/mm/filemap.c	2004-05-19 21:34:41.000000000 +0100
+++ linux-2.4.27-leo/mm/filemap.c	2004-09-17 03:19:58.000000000 +0100
@@ -1809,7 +1809,8 @@
 			retval = do_generic_direct_read(filp, buf, count, ppos);
 		up(&inode->i_sem);
 		up_read(&inode->i_alloc_sem);
-		UPDATE_ATIME(filp->f_dentry->d_inode);
+		if (!S_ISBLK(inode->i_mode))
+			UPDATE_ATIME(filp->f_dentry->d_inode);
 		goto out;
 	}
 }
@@ -2324,6 +2325,12 @@
 	}
 	if (!mapping->a_ops->readpage)
 		return -ENOEXEC;
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+	if (current->flags & PF_PAX_PAGEEXEC)
+		vma->vm_page_prot = protection_map[vma->vm_flags & 0x0f];
+#endif
+
 	UPDATE_ATIME(inode);
 	vma->vm_ops = &generic_file_vm_ops;
 	return 0;
@@ -2553,8 +2560,42 @@
  * We can potentially split a vm area into separate
  * areas, each area with its own behavior.
  */
+
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+static long __madvise_behavior(struct vm_area_struct * vma,
+	unsigned long start, unsigned long end, int behavior);
+
+static long madvise_behavior(struct vm_area_struct * vma,
+	unsigned long start, unsigned long end, int behavior)
+{
+	if (vma->vm_flags & VM_MIRROR) {
+		struct vm_area_struct * vma_m, * prev_m;
+		unsigned long start_m, end_m;
+		int error;
+
+		start_m = vma->vm_start + (unsigned long)vma->vm_private_data;
+		vma_m = find_vma_prev(vma->vm_mm, start_m, &prev_m);
+		if (vma_m && vma_m->vm_start == start_m && (vma_m->vm_flags & VM_MIRROR)) {
+			start_m = start + (unsigned long)vma->vm_private_data;
+			end_m = end + (unsigned long)vma->vm_private_data;
+			error = __madvise_behavior(vma_m, start_m, end_m, behavior);
+			if (error)
+				return error;
+		} else {
+			printk("PAX: VMMIRROR: madvise bug in %s, %08lx\n", current->comm, vma->vm_start);
+			return -ENOMEM;
+		}
+	}
+
+	return __madvise_behavior(vma, start, end, behavior);
+}
+
+static long __madvise_behavior(struct vm_area_struct * vma,
+	unsigned long start, unsigned long end, int behavior)
+#else
 static long madvise_behavior(struct vm_area_struct * vma,
 	unsigned long start, unsigned long end, int behavior)
+#endif
 {
 	int error = 0;
 
@@ -2608,6 +2649,7 @@
 	error = -EIO;
 	rlim_rss = current->rlim ?  current->rlim[RLIMIT_RSS].rlim_cur :
 				LONG_MAX; /* default: see resource.h */
+	gr_learn_resource(current, RLIMIT_RSS, vma->vm_mm->rss + (end - start), 1);
 	if ((vma->vm_mm->rss + (end - start)) > rlim_rss)
 		return error;
 
@@ -3083,6 +3125,7 @@
 	err = -EFBIG;
 	
 	if (!S_ISBLK(inode->i_mode) && limit != RLIM_INFINITY) {
+		gr_learn_resource(current, RLIMIT_FSIZE, pos, 0);
 		if (pos >= limit) {
 			send_sig(SIGXFSZ, current, 0);
 			goto out;
@@ -3118,6 +3161,7 @@
 	 */
 	 
 	if (!S_ISBLK(inode->i_mode)) {
+		gr_learn_resource(current, RLIMIT_FSIZE, *count + (u32)pos, 0);
 		if (pos >= inode->i_sb->s_maxbytes)
 		{
 			if (*count || pos > inode->i_sb->s_maxbytes) {
@@ -3187,8 +3231,12 @@
 		goto out;
 
 	remove_suid(inode);
-	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
-	mark_inode_dirty_sync(inode);
+
+	/* Don't update times for block devices using O_DIRECT */
+	if (!(file->f_flags & O_DIRECT) || !S_ISBLK(inode->i_mode)) {
+		inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+		mark_inode_dirty_sync(inode);
+	}
 
 	do {
 		unsigned long index, offset;
diff -urN --exclude-from=diff-exclude linux-2.4.27/mm/Makefile linux-2.4.27-leo/mm/Makefile
--- linux-2.4.27/mm/Makefile	2002-08-03 01:39:46.000000000 +0100
+++ linux-2.4.27-leo/mm/Makefile	2004-09-17 03:19:08.000000000 +0100
@@ -9,12 +9,12 @@
 
 O_TARGET := mm.o
 
-export-objs := shmem.o filemap.o memory.o page_alloc.o
+export-objs := shmem.o filemap.o memory.o page_alloc.o mempool.o
 
 obj-y	 := memory.o mmap.o filemap.o mprotect.o mlock.o mremap.o \
 	    vmalloc.o slab.o bootmem.o swap.o vmscan.o page_io.o \
 	    page_alloc.o swap_state.o swapfile.o numa.o oom_kill.o \
-	    shmem.o
+	    shmem.o mempool.o
 
 obj-$(CONFIG_HIGHMEM) += highmem.o
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/mm/memory.c linux-2.4.27-leo/mm/memory.c
--- linux-2.4.27/mm/memory.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.27-leo/mm/memory.c	2004-09-17 03:19:58.000000000 +0100
@@ -925,6 +925,69 @@
 	establish_pte(vma, address, page_table, pte_mkwrite(pte_mkdirty(mk_pte(new_page, vma->vm_page_prot))));
 }
 
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+/* PaX: if vma is mirrored, synchronize the mirror's PTE
+ *
+ * mm->page_table_lock is held on entry and is not released on exit or inside
+ * to ensure atomic changes to the PTE states (swapout, mremap, munmap, etc)
+ */
+static void pax_mirror_fault(struct mm_struct *mm, struct vm_area_struct * vma,
+       unsigned long address, pte_t *pte)
+{
+	unsigned long address_m;
+	struct vm_area_struct * vma_m = NULL;
+	pte_t * pte_m, entry_m;
+	struct page * page_m;
+
+	if (!(vma->vm_flags & VM_MIRROR))
+		return;
+
+	address_m = vma->vm_start + (unsigned long)vma->vm_private_data;
+	vma_m = find_vma(mm, address_m);
+	BUG_ON(!vma_m || vma_m->vm_start != address_m);
+
+	address_m = address + (unsigned long)vma->vm_private_data;
+
+	{
+		pgd_t *pgd_m;
+		pmd_t *pmd_m;
+
+		pgd_m = pgd_offset(mm, address_m);
+		pmd_m = pmd_offset(pgd_m, address_m);
+		pte_m = pte_offset(pmd_m, address_m);
+	}
+
+	if (pte_present(*pte_m)) {
+		flush_cache_page(vma_m, address_m);
+		flush_icache_page(vma_m, pte_page(*pte_m));
+	}
+	entry_m = ptep_get_and_clear(pte_m);
+	if (pte_present(entry_m))
+		flush_tlb_page(vma_m, address_m);  
+
+	if (pte_none(entry_m)) {
+		++mm->rss;
+	} else if (pte_present(entry_m)) {
+		page_m = pte_page(entry_m);
+		if (!PageReserved(page_m))
+			page_cache_release(page_m);
+		else
+			++mm->rss;
+	} else {
+		free_swap_and_cache(pte_to_swp_entry(entry_m));
+		++mm->rss;
+	}
+
+	page_m = pte_page(*pte);
+	if (!PageReserved(page_m))
+		page_cache_get(page_m);
+	entry_m = mk_pte(page_m, vma_m->vm_page_prot);
+	if (pte_write(*pte) && (vma_m->vm_flags & VM_WRITE))
+		entry_m = pte_mkdirty(pte_mkwrite(entry_m));
+	establish_pte(vma_m, address_m, pte_m, entry_m);  
+}                      
+#endif
+
 /*
  * This routine handles present pages, when users try to write
  * to a shared page. It is done by copying the page to a new address
@@ -988,6 +1051,11 @@
 
 		/* Free the old page.. */
 		new_page = old_page;
+
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+		pax_mirror_fault(mm, vma, address, page_table);
+#endif
+
 	}
 	spin_unlock(&mm->page_table_lock);
 	page_cache_release(new_page);
@@ -1065,6 +1133,7 @@
 
 do_expand:
 	limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
+	gr_learn_resource(current, RLIMIT_FSIZE, offset, 1);
 	if (limit != RLIM_INFINITY && offset > limit)
 		goto out_sig;
 	if (offset > inode->i_sb->s_maxbytes)
@@ -1178,6 +1247,11 @@
 
 	/* No need to invalidate - it was non-present before */
 	update_mmu_cache(vma, address, pte);
+
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+	pax_mirror_fault(mm, vma, address, page_table);
+#endif
+
 	spin_unlock(&mm->page_table_lock);
 	return ret;
 }
@@ -1223,6 +1297,11 @@
 
 	/* No need to invalidate - it was non-present before */
 	update_mmu_cache(vma, addr, entry);
+
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+	pax_mirror_fault(mm, vma, addr, page_table);
+#endif
+
 	spin_unlock(&mm->page_table_lock);
 	return 1;	/* Minor fault */
 
@@ -1304,6 +1383,11 @@
 
 	/* no need to invalidate: a not-present page shouldn't be cached */
 	update_mmu_cache(vma, address, entry);
+
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+	pax_mirror_fault(mm, vma, address, page_table);
+#endif
+
 	spin_unlock(&mm->page_table_lock);
 	return 2;	/* Major fault */
 }
@@ -1368,6 +1452,11 @@
 	pgd_t *pgd;
 	pmd_t *pmd;
 
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+	unsigned long address_m = 0UL;
+	struct vm_area_struct * vma_m = NULL;
+#endif
+
 	current->state = TASK_RUNNING;
 	pgd = pgd_offset(mm, address);
 
@@ -1376,6 +1465,44 @@
 	 * and the SMP-safe atomic PTE updates.
 	 */
 	spin_lock(&mm->page_table_lock);
+
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+	if (vma->vm_flags & VM_MIRROR) {
+		pgd_t *pgd_m;
+		pmd_t *pmd_m;
+		pte_t *pte_m;
+
+		address_m = vma->vm_start + (unsigned long)vma->vm_private_data;
+		vma_m = find_vma(mm, address_m);
+
+		/* PaX: sanity checks */
+		if (!vma_m) {
+			spin_unlock(&mm->page_table_lock);
+			printk(KERN_ERR "PAX: VMMIRROR: fault bug, %08lx, %p, %08lx, %p\n",
+			       address, vma, address_m, vma_m);
+			return 0;
+		} else if (!(vma_m->vm_flags & VM_MIRROR) ||
+			   vma_m->vm_start != address_m ||
+			   vma->vm_end - vma->vm_start != vma_m->vm_end - vma_m->vm_start)
+		{
+			spin_unlock(&mm->page_table_lock);
+			printk(KERN_ERR "PAX: VMMIRROR: fault bug2, %08lx, %08lx, %08lx, %08lx, %08lx\n",
+				address, vma->vm_start, vma_m->vm_start, vma->vm_end, vma_m->vm_end);
+			return 0;
+		}
+
+		address_m = address + (unsigned long)vma->vm_private_data;
+		pgd_m = pgd_offset(mm, address_m);
+		pmd_m = pmd_alloc(mm, pgd_m, address_m);
+		if (pmd_m)
+			pte_m = pte_alloc(mm, pmd_m, address_m);
+		if (!pmd_m || !pte_m) {
+			spin_unlock(&mm->page_table_lock);
+			return -1;
+		}
+	}			
+#endif
+
 	pmd = pmd_alloc(mm, pgd, address);
 
 	if (pmd) {
@@ -1460,6 +1587,36 @@
 	return pte_offset(pmd, address);
 }
 
+#ifndef pmd_populate_kernel
+#define pmd_populate_kernel(mm,pmd,new) pmd_populate(mm,pmd,new)
+#endif
+
+pte_t *pte_alloc_kernel(struct mm_struct *mm, pmd_t *pmd, unsigned long address)
+{
+	if (pmd_none(*pmd)) {
+		pte_t *new;
+
+		spin_unlock(&mm->page_table_lock);
+		new = pte_alloc_one(mm, address);
+		spin_lock(&mm->page_table_lock);
+		if (!new)
+			return NULL;
+
+		/*
+		 * Because we dropped the lock, we should re-check the
+		 * entry, as somebody else could have populated it..
+		 */
+		if (!pmd_none(*pmd)) {
+			pte_free(new);
+			check_pgt_cache();
+			goto out;
+		}
+		pmd_populate_kernel(mm, pmd, new);
+	}
+out:
+	return pte_offset(pmd, address);
+}
+
 int make_pages_present(unsigned long addr, unsigned long end)
 {
 	int ret, len, write;
diff -urN --exclude-from=diff-exclude linux-2.4.27/mm/mempool.c linux-2.4.27-leo/mm/mempool.c
--- linux-2.4.27/mm/mempool.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/mm/mempool.c	2004-09-17 03:19:08.000000000 +0100
@@ -0,0 +1,299 @@
+/*
+ *  linux/mm/mempool.c
+ *
+ *  memory buffer pool support. Such pools are mostly used
+ *  for guaranteed, deadlock-free memory allocations during
+ *  extreme VM load.
+ *
+ *  started by Ingo Molnar, Copyright (C) 2001
+ */
+
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mempool.h>
+
+struct mempool_s {
+	spinlock_t lock;
+	int min_nr;		/* nr of elements at *elements */
+	int curr_nr;		/* Current nr of elements at *elements */
+	void **elements;
+
+	void *pool_data;
+	mempool_alloc_t *alloc;
+	mempool_free_t *free;
+	wait_queue_head_t wait;
+};
+
+static void add_element(mempool_t *pool, void *element)
+{
+	BUG_ON(pool->curr_nr >= pool->min_nr);
+	pool->elements[pool->curr_nr++] = element;
+}
+
+static void *remove_element(mempool_t *pool)
+{
+	BUG_ON(pool->curr_nr <= 0);
+	return pool->elements[--pool->curr_nr];
+}
+
+static void free_pool(mempool_t *pool)
+{
+	while (pool->curr_nr) {
+		void *element = remove_element(pool);
+		pool->free(element, pool->pool_data);
+	}
+	kfree(pool->elements);
+	kfree(pool);
+}
+
+/**
+ * mempool_create - create a memory pool
+ * @min_nr:    the minimum number of elements guaranteed to be
+ *             allocated for this pool.
+ * @alloc_fn:  user-defined element-allocation function.
+ * @free_fn:   user-defined element-freeing function.
+ * @pool_data: optional private data available to the user-defined functions.
+ *
+ * this function creates and allocates a guaranteed size, preallocated
+ * memory pool. The pool can be used from the mempool_alloc and mempool_free
+ * functions. This function might sleep. Both the alloc_fn() and the free_fn()
+ * functions might sleep - as long as the mempool_alloc function is not called
+ * from IRQ contexts.
+ */
+mempool_t * mempool_create(int min_nr, mempool_alloc_t *alloc_fn,
+				mempool_free_t *free_fn, void *pool_data)
+{
+	mempool_t *pool;
+
+	pool = kmalloc(sizeof(*pool), GFP_KERNEL);
+	if (!pool)
+		return NULL;
+	memset(pool, 0, sizeof(*pool));
+	pool->elements = kmalloc(min_nr * sizeof(void *), GFP_KERNEL);
+	if (!pool->elements) {
+		kfree(pool);
+		return NULL;
+	}
+	spin_lock_init(&pool->lock);
+	pool->min_nr = min_nr;
+	pool->pool_data = pool_data;
+	init_waitqueue_head(&pool->wait);
+	pool->alloc = alloc_fn;
+	pool->free = free_fn;
+
+	/*
+	 * First pre-allocate the guaranteed number of buffers.
+	 */
+	while (pool->curr_nr < pool->min_nr) {
+		void *element;
+
+		element = pool->alloc(GFP_KERNEL, pool->pool_data);
+		if (unlikely(!element)) {
+			free_pool(pool);
+			return NULL;
+		}
+		add_element(pool, element);
+	}
+	return pool;
+}
+
+/**
+ * mempool_resize - resize an existing memory pool
+ * @pool:       pointer to the memory pool which was allocated via
+ *              mempool_create().
+ * @new_min_nr: the new minimum number of elements guaranteed to be
+ *              allocated for this pool.
+ * @gfp_mask:   the usual allocation bitmask.
+ *
+ * This function shrinks/grows the pool. In the case of growing,
+ * it cannot be guaranteed that the pool will be grown to the new
+ * size immediately, but new mempool_free() calls will refill it.
+ *
+ * Note, the caller must guarantee that no mempool_destroy is called
+ * while this function is running. mempool_alloc() & mempool_free()
+ * might be called (eg. from IRQ contexts) while this function executes.
+ */
+int mempool_resize(mempool_t *pool, int new_min_nr, int gfp_mask)
+{
+	void *element;
+	void **new_elements;
+	unsigned long flags;
+
+	BUG_ON(new_min_nr <= 0);
+
+	spin_lock_irqsave(&pool->lock, flags);
+	if (new_min_nr < pool->min_nr) {
+		while (pool->curr_nr > new_min_nr) {
+			element = remove_element(pool);
+			spin_unlock_irqrestore(&pool->lock, flags);
+			pool->free(element, pool->pool_data);
+			spin_lock_irqsave(&pool->lock, flags);
+		}
+		pool->min_nr = new_min_nr;
+		goto out_unlock;
+	}
+	spin_unlock_irqrestore(&pool->lock, flags);
+
+	/* Grow the pool */
+	new_elements = kmalloc(new_min_nr * sizeof(*new_elements), gfp_mask);
+	if (!new_elements)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&pool->lock, flags);
+	memcpy(new_elements, pool->elements,
+			pool->curr_nr * sizeof(*new_elements));
+	kfree(pool->elements);
+	pool->elements = new_elements;
+	pool->min_nr = new_min_nr;
+
+	while (pool->curr_nr < pool->min_nr) {
+		spin_unlock_irqrestore(&pool->lock, flags);
+		element = pool->alloc(gfp_mask, pool->pool_data);
+		if (!element)
+			goto out;
+		spin_lock_irqsave(&pool->lock, flags);
+		if (pool->curr_nr < pool->min_nr)
+			add_element(pool, element);
+		else
+			kfree(element);		/* Raced */
+	}
+out_unlock:
+	spin_unlock_irqrestore(&pool->lock, flags);
+out:
+	return 0;
+}
+
+/**
+ * mempool_destroy - deallocate a memory pool
+ * @pool:      pointer to the memory pool which was allocated via
+ *             mempool_create().
+ *
+ * this function only sleeps if the free_fn() function sleeps. The caller
+ * has to guarantee that all elements have been returned to the pool (ie:
+ * freed) prior to calling mempool_destroy().
+ */
+void mempool_destroy(mempool_t *pool)
+{
+	if (pool->curr_nr != pool->min_nr)
+		BUG();		/* There were outstanding elements */
+	free_pool(pool);
+}
+
+/**
+ * mempool_alloc - allocate an element from a specific memory pool
+ * @pool:      pointer to the memory pool which was allocated via
+ *             mempool_create().
+ * @gfp_mask:  the usual allocation bitmask.
+ *
+ * this function only sleeps if the alloc_fn function sleeps or
+ * returns NULL. Note that due to preallocation, this function
+ * *never* fails when called from process contexts. (it might
+ * fail if called from an IRQ context.)
+ */
+void * mempool_alloc(mempool_t *pool, int gfp_mask)
+{
+	void *element;
+	unsigned long flags;
+	int curr_nr;
+	DECLARE_WAITQUEUE(wait, current);
+	int gfp_nowait = gfp_mask & ~(__GFP_WAIT | __GFP_IO);
+
+repeat_alloc:
+	element = pool->alloc(gfp_nowait, pool->pool_data);
+	if (likely(element != NULL))
+		return element;
+
+	/*
+	 * If the pool is less than 50% full then try harder
+	 * to allocate an element:
+	 */
+	if ((gfp_mask != gfp_nowait) && (pool->curr_nr <= pool->min_nr/2)) {
+		element = pool->alloc(gfp_mask, pool->pool_data);
+		if (likely(element != NULL))
+			return element;
+	}
+
+	/*
+	 * Kick the VM at this point.
+	 */
+	wakeup_bdflush();
+
+	spin_lock_irqsave(&pool->lock, flags);
+	if (likely(pool->curr_nr)) {
+		element = remove_element(pool);
+		spin_unlock_irqrestore(&pool->lock, flags);
+		return element;
+	}
+	spin_unlock_irqrestore(&pool->lock, flags);
+
+	/* We must not sleep in the GFP_ATOMIC case */
+	if (gfp_mask == gfp_nowait)
+		return NULL;
+
+	run_task_queue(&tq_disk);
+
+	add_wait_queue_exclusive(&pool->wait, &wait);
+	set_task_state(current, TASK_UNINTERRUPTIBLE);
+
+	spin_lock_irqsave(&pool->lock, flags);
+	curr_nr = pool->curr_nr;
+	spin_unlock_irqrestore(&pool->lock, flags);
+
+	if (!curr_nr)
+		schedule();
+
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&pool->wait, &wait);
+
+	goto repeat_alloc;
+}
+
+/**
+ * mempool_free - return an element to the pool.
+ * @element:   pool element pointer.
+ * @pool:      pointer to the memory pool which was allocated via
+ *             mempool_create().
+ *
+ * this function only sleeps if the free_fn() function sleeps.
+ */
+void mempool_free(void *element, mempool_t *pool)
+{
+	unsigned long flags;
+
+	if (pool->curr_nr < pool->min_nr) {
+		spin_lock_irqsave(&pool->lock, flags);
+		if (pool->curr_nr < pool->min_nr) {
+			add_element(pool, element);
+			spin_unlock_irqrestore(&pool->lock, flags);
+			wake_up(&pool->wait);
+			return;
+		}
+		spin_unlock_irqrestore(&pool->lock, flags);
+	}
+	pool->free(element, pool->pool_data);
+}
+
+/*
+ * A commonly used alloc and free fn.
+ */
+void *mempool_alloc_slab(int gfp_mask, void *pool_data)
+{
+	kmem_cache_t *mem = (kmem_cache_t *) pool_data;
+	return kmem_cache_alloc(mem, gfp_mask);
+}
+
+void mempool_free_slab(void *element, void *pool_data)
+{
+	kmem_cache_t *mem = (kmem_cache_t *) pool_data;
+	kmem_cache_free(mem, element);
+}
+
+
+EXPORT_SYMBOL(mempool_create);
+EXPORT_SYMBOL(mempool_resize);
+EXPORT_SYMBOL(mempool_destroy);
+EXPORT_SYMBOL(mempool_alloc);
+EXPORT_SYMBOL(mempool_free);
+EXPORT_SYMBOL(mempool_alloc_slab);
+EXPORT_SYMBOL(mempool_free_slab);
diff -urN --exclude-from=diff-exclude linux-2.4.27/mm/mlock.c linux-2.4.27-leo/mm/mlock.c
--- linux-2.4.27/mm/mlock.c	2001-09-17 23:30:23.000000000 +0100
+++ linux-2.4.27-leo/mm/mlock.c	2004-09-17 03:19:58.000000000 +0100
@@ -114,9 +114,40 @@
 	return 0;
 }
 
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+static int __mlock_fixup(struct vm_area_struct * vma,
+	unsigned long start, unsigned long end, unsigned int newflags);
 static int mlock_fixup(struct vm_area_struct * vma, 
 	unsigned long start, unsigned long end, unsigned int newflags)
 {
+	if (vma->vm_flags & VM_MIRROR) {
+		struct vm_area_struct * vma_m;
+		unsigned long start_m, end_m;
+		int error;
+
+		start_m = vma->vm_start + (unsigned long)vma->vm_private_data;
+		vma_m = find_vma(vma->vm_mm, start_m);
+		if (vma_m && vma_m->vm_start == start_m && (vma_m->vm_flags & VM_MIRROR)) {
+			start_m = start + (unsigned long)vma->vm_private_data;
+			end_m = end + (unsigned long)vma->vm_private_data;
+			error = __mlock_fixup(vma_m, start_m, end_m, newflags);
+			if (error)
+				return error;
+		} else {
+			printk("PAX: VMMIRROR: mlock bug in %s, %08lx\n", current->comm, vma->vm_start);
+			return -ENOMEM;
+		}
+	}
+	return __mlock_fixup(vma, start, end, newflags);
+}
+
+static int __mlock_fixup(struct vm_area_struct * vma,
+	unsigned long start, unsigned long end, unsigned int newflags)
+#else
+static int mlock_fixup(struct vm_area_struct * vma,
+	unsigned long start, unsigned long end, unsigned int newflags)
+#endif
+{
 	int pages, retval;
 
 	if (newflags == vma->vm_flags)
@@ -159,6 +190,17 @@
 		return -EINVAL;
 	if (end == start)
 		return 0;
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	if (current->flags & PF_PAX_SEGMEXEC) {
+		if (end > SEGMEXEC_TASK_SIZE)
+			return -EINVAL;
+	} else
+#endif
+
+	if (end > TASK_SIZE)
+		return -EINVAL;
+
 	vma = find_vma(current->mm, start);
 	if (!vma || vma->vm_start > start)
 		return -ENOMEM;
@@ -209,6 +251,7 @@
 	lock_limit >>= PAGE_SHIFT;
 
 	/* check against resource limits */
+	gr_learn_resource(current, RLIMIT_MEMLOCK, locked, 1);
 	if (locked > lock_limit)
 		goto out;
 
@@ -253,6 +296,16 @@
 	for (vma = current->mm->mmap; vma ; vma = vma->vm_next) {
 		unsigned int newflags;
 
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+		if (current->flags & PF_PAX_SEGMEXEC) {
+			if (vma->vm_end > SEGMEXEC_TASK_SIZE)
+				break;
+		} else
+#endif
+
+		if (vma->vm_end > TASK_SIZE)
+			break;
+
 		newflags = vma->vm_flags | VM_LOCKED;
 		if (!(flags & MCL_CURRENT))
 			newflags &= ~VM_LOCKED;
@@ -276,6 +329,7 @@
 	lock_limit >>= PAGE_SHIFT;
 
 	ret = -ENOMEM;
+	gr_learn_resource(current, RLIMIT_MEMLOCK, current->mm->total_vm, 1);
 	if (current->mm->total_vm > lock_limit)
 		goto out;
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/mm/mmap.c linux-2.4.27-leo/mm/mmap.c
--- linux-2.4.27/mm/mmap.c	2004-02-20 14:11:47.000000000 +0000
+++ linux-2.4.27-leo/mm/mmap.c	2004-09-17 03:19:58.000000000 +0100
@@ -15,6 +15,8 @@
 #include <linux/fs.h>
 #include <linux/personality.h>
 #include <linux/mount.h>
+#include <linux/random.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 #include <asm/pgalloc.h>
@@ -169,6 +171,7 @@
 
 	/* Check against rlimit.. */
 	rlim = current->rlim[RLIMIT_DATA].rlim_cur;
+	gr_learn_resource(current, RLIMIT_DATA, brk - mm->start_data, 1);
 	if (rlim < RLIM_INFINITY && brk - mm->start_data > rlim)
 		goto out;
 
@@ -206,6 +209,11 @@
 		_trans(prot, PROT_WRITE, VM_WRITE) |
 		_trans(prot, PROT_EXEC, VM_EXEC);
 	flag_bits =
+
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+		_trans(flags, MAP_MIRROR, VM_MIRROR) |
+#endif
+
 		_trans(flags, MAP_GROWSDOWN, VM_GROWSDOWN) |
 		_trans(flags, MAP_DENYWRITE, VM_DENYWRITE) |
 		_trans(flags, MAP_EXECUTABLE, VM_EXECUTABLE);
@@ -391,7 +399,43 @@
 	return 0;
 }
 
-unsigned long do_mmap_pgoff(struct file * file, unsigned long addr, unsigned long len,
+unsigned long do_mmap_pgoff(struct file *file, unsigned long addr, unsigned long len,
+	unsigned long prot, unsigned long flag, unsigned long pgoff)
+{
+	unsigned long ret = -EINVAL;
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	if ((current->flags & PF_PAX_SEGMEXEC) &&
+	    (len > SEGMEXEC_TASK_SIZE || (addr && addr > SEGMEXEC_TASK_SIZE-len)))
+		goto out;
+#endif
+
+	ret = __do_mmap_pgoff(file, addr, len, prot, flag, pgoff);
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	if ((current->flags & PF_PAX_SEGMEXEC) && ret < TASK_SIZE && ((flag & MAP_TYPE) == MAP_PRIVATE)
+
+#ifdef CONFIG_GRKERNSEC_PAX_MPROTECT
+	    && (!(current->flags & PF_PAX_MPROTECT) || ((prot & PROT_EXEC) && file && !(prot & PROT_WRITE)))
+#endif
+
+	   )
+	{
+		unsigned long ret_m;
+		prot = prot & PROT_EXEC ? prot : PROT_NONE;
+		ret_m = __do_mmap_pgoff(NULL, ret + SEGMEXEC_TASK_SIZE, 0UL, prot, flag | MAP_MIRROR | MAP_FIXED, ret);
+		if (ret_m >= TASK_SIZE) {
+			do_munmap(current->mm, ret, len);
+			ret = ret_m;
+		}
+	}
+#endif
+
+out:
+	return ret;
+}
+
+unsigned long __do_mmap_pgoff(struct file * file, unsigned long addr, unsigned long len,
 	unsigned long prot, unsigned long flags, unsigned long pgoff)
 {
 	struct mm_struct * mm = current->mm;
@@ -401,6 +445,28 @@
 	int error;
 	rb_node_t ** rb_link, * rb_parent;
 
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+	struct vm_area_struct * vma_m = NULL;
+
+	if (flags & MAP_MIRROR) {
+		/* PaX: sanity checks, to be removed when proved to be stable */
+		if (file || len || ((flags & MAP_TYPE) != MAP_PRIVATE))
+			return -EINVAL;
+
+		vma_m = find_vma(mm, pgoff);
+
+		if (!vma_m ||
+		    vma_m->vm_start != pgoff ||
+		    (vma_m->vm_flags & VM_MIRROR) ||
+		    (!(vma_m->vm_flags & VM_WRITE) && (prot & PROT_WRITE)))
+			return -EINVAL;
+
+		file = vma_m->vm_file;
+		pgoff = vma_m->vm_pgoff;
+		len = vma_m->vm_end - vma_m->vm_start;
+	}
+#endif
+
 	if (file) {
 		if (!file->f_op || !file->f_op->mmap)
 			return -ENODEV;
@@ -438,10 +504,35 @@
 	 */
 	vm_flags = calc_vm_flags(prot,flags) | mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
 
+	if (file && (file->f_vfsmnt->mnt_flags & MNT_NOEXEC))
+		vm_flags &= ~VM_MAYEXEC;
+
+#if defined(CONFIG_GRKERNSEC_PAX_PAGEEXEC) || defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC)
+	if (current->flags & (PF_PAX_PAGEEXEC | PF_PAX_SEGMEXEC)) {
+
+#ifdef CONFIG_GRKERNSEC_PAX_MPROTECT
+		if (current->flags & PF_PAX_MPROTECT) {
+			if (!file || (prot & PROT_WRITE))
+				vm_flags &= ~(VM_EXEC | VM_MAYEXEC);
+			else
+				vm_flags &= ~VM_MAYWRITE;
+
+#ifdef CONFIG_GRKERNSEC_PAX_RANDEXEC
+			if (file && (flags & MAP_MIRROR) && (vm_flags & VM_EXEC))
+				vma_m->vm_flags &= ~VM_MAYWRITE;
+#endif
+
+		}
+#endif
+
+	}
+#endif
+
 	/* mlock MCL_FUTURE? */
 	if (vm_flags & VM_LOCKED) {
 		unsigned long locked = mm->locked_vm << PAGE_SHIFT;
 		locked += len;
+		gr_learn_resource(current, RLIMIT_MEMLOCK, locked, 1);
 		if (locked > current->rlim[RLIMIT_MEMLOCK].rlim_cur)
 			return -EAGAIN;
 	}
@@ -486,6 +577,9 @@
 		}
 	}
 
+	if (!gr_acl_handle_mmap(file, prot))
+		return -EACCES;
+
 	/* Clear old maps */
 munmap_back:
 	vma = find_vma_prepare(mm, addr, &prev, &rb_link, &rb_parent);
@@ -496,10 +590,16 @@
 	}
 
 	/* Check against address space limit. */
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+	if (!(vm_flags & VM_MIRROR)) {
+#endif
+	gr_learn_resource(current, RLIMIT_AS, (mm->total_vm << PAGE_SHIFT) + len, 1);
 	if ((mm->total_vm << PAGE_SHIFT) + len
 	    > current->rlim[RLIMIT_AS].rlim_cur)
 		return -ENOMEM;
-
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+	}
+#endif
 	/* Private writable mapping? Check memory availability.. */
 	if ((vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE &&
 	    !(flags & MAP_NORESERVE)				 &&
@@ -523,6 +623,13 @@
 	vma->vm_start = addr;
 	vma->vm_end = addr + len;
 	vma->vm_flags = vm_flags;
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+	if ((file || !(current->flags & PF_PAX_PAGEEXEC)) && (vm_flags & (VM_READ|VM_WRITE)))
+		vma->vm_page_prot = protection_map[(vm_flags | VM_EXEC) & 0x0f];
+	else
+#endif
+
 	vma->vm_page_prot = protection_map[vm_flags & 0x0f];
 	vma->vm_ops = NULL;
 	vma->vm_pgoff = pgoff;
@@ -551,6 +658,14 @@
 			goto free_vma;
 	}
 
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+	if (flags & MAP_MIRROR) {
+		vma_m->vm_flags |= VM_MIRROR;
+		vma_m->vm_private_data = (void *)(vma->vm_start - vma_m->vm_start);
+		vma->vm_private_data = (void *)(vma_m->vm_start - vma->vm_start);
+	}
+#endif
+
 	/* Can addr have changed??
 	 *
 	 * Answer: Yes, several device drivers can do it in their
@@ -586,6 +701,9 @@
 		atomic_inc(&file->f_dentry->d_inode->i_writecount);
 
 out:	
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+	if (!(flags & MAP_MIRROR))
+#endif
 	mm->total_vm += len >> PAGE_SHIFT;
 	if (vm_flags & VM_LOCKED) {
 		mm->locked_vm += len >> PAGE_SHIFT;
@@ -621,22 +739,39 @@
 static inline unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags)
 {
 	struct vm_area_struct *vma;
+	unsigned long task_size = TASK_SIZE;
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	if (current->flags & PF_PAX_SEGMEXEC)
+		task_size = SEGMEXEC_TASK_SIZE;
+#endif
 
-	if (len > TASK_SIZE)
+	if (len > task_size)
 		return -ENOMEM;
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDMMAP
+	if (!(current->flags & PF_PAX_RANDMMAP) || !filp)
+#endif
+
 	if (addr) {
 		addr = PAGE_ALIGN(addr);
 		vma = find_vma(current->mm, addr);
-		if (TASK_SIZE - len >= addr &&
+
+		if (task_size - len >= addr &&
 		    (!vma || addr + len <= vma->vm_start))
 			return addr;
 	}
 	addr = PAGE_ALIGN(TASK_UNMAPPED_BASE);
 
+#ifdef CONFIG_GRKERNSEC_PAX_RANDMMAP
+	/* PaX: randomize base address if requested */
+	if (current->flags & PF_PAX_RANDMMAP)
+		addr += current->mm->delta_mmap;
+#endif
+
 	for (vma = find_vma(current->mm, addr); ; vma = vma->vm_next) {
 		/* At this point:  (!vma || addr < vma->vm_end). */
-		if (TASK_SIZE - len < addr)
+		if (task_size - len < addr)
 			return -ENOMEM;
 		if (!vma || addr + len <= vma->vm_start)
 			return addr;
@@ -797,6 +932,9 @@
 	struct vm_area_struct *mpnt;
 	unsigned long end = addr + len;
 
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+	if (!(area->vm_flags & VM_MIRROR))
+#endif
 	area->vm_mm->total_vm -= len >> PAGE_SHIFT;
 	if (area->vm_flags & VM_LOCKED)
 		area->vm_mm->locked_vm -= len >> PAGE_SHIFT;
@@ -922,6 +1060,83 @@
 	}
 }
 
+static inline struct vm_area_struct *unmap_vma(struct mm_struct *mm,
+	unsigned long addr, size_t len, struct vm_area_struct *mpnt,
+	struct vm_area_struct *extra)
+{
+	unsigned long st, end, size;
+	struct file *file = NULL;
+
+	st = addr < mpnt->vm_start ? mpnt->vm_start : addr;
+	end = addr+len;
+	end = end > mpnt->vm_end ? mpnt->vm_end : end;
+	size = end - st;
+
+	if (mpnt->vm_flags & VM_DENYWRITE &&
+	    (st != mpnt->vm_start || end != mpnt->vm_end) &&
+	    (file = mpnt->vm_file) != NULL) {
+		atomic_dec(&file->f_dentry->d_inode->i_writecount);
+	}
+	remove_shared_vm_struct(mpnt);
+	zap_page_range(mm, st, size);
+
+	/*
+	 * Fix the mapping, and free the old area if it wasn't reused.
+	 */
+	extra = unmap_fixup(mm, mpnt, st, size, extra);
+	if (file)
+		atomic_inc(&file->f_dentry->d_inode->i_writecount);
+	return extra;
+}
+       
+static struct vm_area_struct *unmap_vma_list(struct mm_struct *mm,
+	unsigned long addr, size_t len, struct vm_area_struct *free,
+	struct vm_area_struct *extra, struct vm_area_struct *prev)
+{
+	struct vm_area_struct *mpnt;
+
+	/* Ok - we have the memory areas we should free on the 'free' list,
+	 * so release them, and unmap the page range..
+	 * If the one of the segments is only being partially unmapped,
+	 * it will put new vm_area_struct(s) into the address space.
+	 * In that case we have to be careful with VM_DENYWRITE.
+	 */
+	while ((mpnt = free) != NULL) {
+		free = free->vm_next;
+		extra = unmap_vma(mm, addr, len, mpnt, extra);
+	}
+
+	free_pgtables(mm, prev, addr, addr+len);
+
+	return extra;
+}
+
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+static struct vm_area_struct *unmap_vma_mirror_list(struct mm_struct *mm,
+	unsigned long addr, size_t len, struct vm_area_struct *free_m,
+	struct vm_area_struct *extra_m)
+{
+	struct vm_area_struct *mpnt, *prev;
+
+	while ((mpnt = free_m) != NULL) {
+		unsigned long addr_m, start, end;
+
+		free_m = free_m->vm_next;
+
+		addr_m = addr - (unsigned long)mpnt->vm_private_data;
+		start = addr_m < mpnt->vm_start ? mpnt->vm_start : addr_m;
+		end = addr_m+len;
+		end = end > mpnt->vm_end ? mpnt->vm_end : end;
+		find_vma_prev(mm, mpnt->vm_start, &prev);
+		extra_m = unmap_vma(mm, addr_m, len, mpnt, extra_m);
+
+		free_pgtables(mm, prev, start, end);
+	}               
+
+	return extra_m;
+}
+#endif
+
 /* Munmap is split into 2 main parts -- this part which finds
  * what needs doing, and the areas themselves, which do the
  * work.  This now handles partial unmappings.
@@ -931,6 +1146,10 @@
 {
 	struct vm_area_struct *mpnt, *prev, **npp, *free, *extra;
 
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+	struct vm_area_struct *free_m, *extra_m;
+#endif
+
 	if ((addr & ~PAGE_MASK) || addr > TASK_SIZE || len > TASK_SIZE-addr)
 		return -EINVAL;
 
@@ -963,60 +1182,69 @@
 	if (!extra)
 		return -ENOMEM;
 
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+	if (current->flags & (PF_PAX_SEGMEXEC | PF_PAX_RANDEXEC)) { 
+		extra_m = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+		if (!extra_m) {
+			kmem_cache_free(vm_area_cachep, extra);
+			return -ENOMEM;
+		}
+	} else
+		extra_m = NULL;
+
+	free_m = NULL;
+#endif
+
 	npp = (prev ? &prev->vm_next : &mm->mmap);
 	free = NULL;
 	spin_lock(&mm->page_table_lock);
 	for ( ; mpnt && mpnt->vm_start < addr+len; mpnt = *npp) {
+		mm->map_count--;
 		*npp = mpnt->vm_next;
 		mpnt->vm_next = free;
 		free = mpnt;
 		rb_erase(&mpnt->vm_rb, &mm->mm_rb);
+
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+		if (free->vm_flags & VM_MIRROR) {
+			struct vm_area_struct *mpnt_m, *prev_m, **npp_m;
+			unsigned long addr_m = free->vm_start + (unsigned long)free->vm_private_data;
+
+			mm->mmap_cache = NULL;  /* Kill the cache. */
+			mpnt_m = find_vma_prev(mm, addr_m, &prev_m);
+			if (mpnt_m && mpnt_m->vm_start == addr_m && (mpnt_m->vm_flags & VM_MIRROR)) {
+				mm->map_count--;
+				npp_m = (prev_m ? &prev_m->vm_next : &mm->mmap);
+				*npp_m = mpnt_m->vm_next;
+				mpnt_m->vm_next = free_m;
+				free_m = mpnt_m;
+				rb_erase(&mpnt_m->vm_rb, &mm->mm_rb);
+			} else
+				printk("PAX: VMMIRROR: munmap bug in %s, %08lx\n", current->comm, free->vm_start);
+		}
+#endif
+
 	}
 	mm->mmap_cache = NULL;	/* Kill the cache. */
 	spin_unlock(&mm->page_table_lock);
 
-	/* Ok - we have the memory areas we should free on the 'free' list,
-	 * so release them, and unmap the page range..
-	 * If the one of the segments is only being partially unmapped,
-	 * it will put new vm_area_struct(s) into the address space.
-	 * In that case we have to be careful with VM_DENYWRITE.
-	 */
-	while ((mpnt = free) != NULL) {
-		unsigned long st, end, size;
-		struct file *file = NULL;
-
-		free = free->vm_next;
-
-		st = addr < mpnt->vm_start ? mpnt->vm_start : addr;
-		end = addr+len;
-		end = end > mpnt->vm_end ? mpnt->vm_end : end;
-		size = end - st;
-
-		if (mpnt->vm_flags & VM_DENYWRITE &&
-		    (st != mpnt->vm_start || end != mpnt->vm_end) &&
-		    (file = mpnt->vm_file) != NULL) {
-			atomic_dec(&file->f_dentry->d_inode->i_writecount);
-		}
-		remove_shared_vm_struct(mpnt);
-		mm->map_count--;
+	extra = unmap_vma_list(mm, addr, len, free, extra, prev);
 
-		zap_page_range(mm, st, size);
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+	extra_m = unmap_vma_mirror_list(mm, addr, len, free_m, extra_m);
+#endif
 
-		/*
-		 * Fix the mapping, and free the old area if it wasn't reused.
-		 */
-		extra = unmap_fixup(mm, mpnt, st, size, extra);
-		if (file)
-			atomic_inc(&file->f_dentry->d_inode->i_writecount);
-	}
 	validate_mm(mm);
 
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+	if (extra_m)
+		kmem_cache_free(vm_area_cachep, extra_m);
+#endif
+
 	/* Release the extra vma struct if it wasn't used */
 	if (extra)
 		kmem_cache_free(vm_area_cachep, extra);
 
-	free_pgtables(mm, prev, addr, addr+len);
-
 	return 0;
 }
 
@@ -1025,8 +1253,15 @@
 	int ret;
 	struct mm_struct *mm = current->mm;
 
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	if ((current->flags & PF_PAX_SEGMEXEC) &&
+	    (len > SEGMEXEC_TASK_SIZE || addr > SEGMEXEC_TASK_SIZE-len))
+		return -EINVAL;
+#endif
+
 	down_write(&mm->mmap_sem);
 	ret = do_munmap(mm, addr, len);
+
 	up_write(&mm->mmap_sem);
 	return ret;
 }
@@ -1036,8 +1271,32 @@
  *  anonymous maps.  eventually we may be able to do some
  *  brk-specific accounting here.
  */
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) && defined(CONFIG_GRKERNSEC_PAX_MPROTECT)
+unsigned long __do_brk(unsigned long addr, unsigned long len);
+
 unsigned long do_brk(unsigned long addr, unsigned long len)
 {
+	unsigned long ret;
+
+	ret = __do_brk(addr, len);
+	if (ret == addr && (current->flags & (PF_PAX_SEGMEXEC | PF_PAX_MPROTECT)) == PF_PAX_SEGMEXEC) {
+		unsigned long ret_m;
+
+		ret_m = __do_mmap_pgoff(NULL, addr + SEGMEXEC_TASK_SIZE, 0UL, PROT_NONE, MAP_PRIVATE | MAP_FIXED | MAP_MIRROR, addr);
+		if (ret_m > TASK_SIZE) {
+			do_munmap(current->mm, addr, len);
+			ret = ret_m;
+		}
+	}
+
+	return ret;
+}
+
+unsigned long __do_brk(unsigned long addr, unsigned long len)
+#else
+unsigned long do_brk(unsigned long addr, unsigned long len)
+#endif
+{
 	struct mm_struct * mm = current->mm;
 	struct vm_area_struct * vma, * prev;
 	unsigned long flags;
@@ -1047,6 +1306,13 @@
 	if (!len)
 		return addr;
 
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	if (current->flags & PF_PAX_SEGMEXEC) {
+		if ((addr + len) > SEGMEXEC_TASK_SIZE || (addr + len) < addr)
+			return -EINVAL;
+	} else
+#endif
+
 	if ((addr + len) > TASK_SIZE || (addr + len) < addr)
 		return -EINVAL;
 
@@ -1056,6 +1322,7 @@
 	if (mm->def_flags & VM_LOCKED) {
 		unsigned long locked = mm->locked_vm << PAGE_SHIFT;
 		locked += len;
+		gr_learn_resource(current, RLIMIT_MEMLOCK, locked, 1);
 		if (locked > current->rlim[RLIMIT_MEMLOCK].rlim_cur)
 			return -EAGAIN;
 	}
@@ -1072,6 +1339,7 @@
 	}
 
 	/* Check against address space limits *after* clearing old maps... */
+	gr_learn_resource(current, RLIMIT_AS, (mm->total_vm << PAGE_SHIFT) + len, 1);
 	if ((mm->total_vm << PAGE_SHIFT) + len
 	    > current->rlim[RLIMIT_AS].rlim_cur)
 		return -ENOMEM;
@@ -1084,6 +1352,17 @@
 
 	flags = VM_DATA_DEFAULT_FLAGS | mm->def_flags;
 
+#if defined(CONFIG_GRKERNSEC_PAX_PAGEEXEC) || defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC)
+	if (current->flags & (PF_PAX_PAGEEXEC | PF_PAX_SEGMEXEC)) {
+		flags &= ~VM_EXEC;
+
+#ifdef CONFIG_GRKERNSEC_PAX_MPROTECT
+		if (current->flags & PF_PAX_MPROTECT)
+			flags &= ~VM_MAYEXEC;
+#endif
+
+	}
+#endif
 	/* Can we just expand an old anonymous mapping? */
 	if (rb_parent && vma_merge(mm, prev, rb_parent, addr, addr + len, flags))
 		goto out;
@@ -1099,6 +1378,12 @@
 	vma->vm_start = addr;
 	vma->vm_end = addr + len;
 	vma->vm_flags = flags;
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+	if (!(current->flags & PF_PAX_PAGEEXEC) && (flags & (VM_READ|VM_WRITE)))
+		vma->vm_page_prot = protection_map[(flags | VM_EXEC) & 0x0f];
+	else
+#endif
 	vma->vm_page_prot = protection_map[flags & 0x0f];
 	vma->vm_ops = NULL;
 	vma->vm_pgoff = 0;
diff -urN --exclude-from=diff-exclude linux-2.4.27/mm/mprotect.c linux-2.4.27-leo/mm/mprotect.c
--- linux-2.4.27/mm/mprotect.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.27-leo/mm/mprotect.c	2004-09-17 03:19:58.000000000 +0100
@@ -7,6 +7,12 @@
 #include <linux/smp_lock.h>
 #include <linux/shm.h>
 #include <linux/mman.h>
+#include <linux/grsecurity.h>
+
+#ifdef CONFIG_GRKERNSEC_PAX_MPROTECT
+#include <linux/elf.h>
+#include <linux/fs.h>
+#endif
 
 #include <asm/uaccess.h>
 #include <asm/pgalloc.h>
@@ -236,6 +242,44 @@
 	return 0;
 }
 
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+static int __mprotect_fixup(struct vm_area_struct * vma, struct vm_area_struct ** pprev,
+	unsigned long start, unsigned long end, unsigned int newflags);
+
+static int mprotect_fixup(struct vm_area_struct * vma, struct vm_area_struct ** pprev,
+	unsigned long start, unsigned long end, unsigned int newflags)
+{
+	if (vma->vm_flags & VM_MIRROR) {
+		struct vm_area_struct * vma_m, * prev_m;
+		unsigned long start_m, end_m;
+		int error;
+
+		start_m = vma->vm_start + (unsigned long)vma->vm_private_data;
+		vma_m = find_vma_prev(vma->vm_mm, start_m, &prev_m);
+		if (vma_m && vma_m->vm_start == start_m && (vma_m->vm_flags & VM_MIRROR)) {
+			start_m = start + (unsigned long)vma->vm_private_data;
+			end_m = end + (unsigned long)vma->vm_private_data;
+			if ((current->flags & PF_PAX_SEGMEXEC) && !(newflags & VM_EXEC))
+				error = __mprotect_fixup(vma_m, &prev_m, start_m, end_m, vma_m->vm_flags & ~(PROT_READ | PROT_WRITE | PROT_EXEC));
+			else
+				error = __mprotect_fixup(vma_m, &prev_m, start_m, end_m, newflags);
+			if (error)
+				return error;
+		} else {
+			printk("PAX: VMMIRROR: mprotect bug in %s, %08lx\n", current->comm, vma->vm_start);
+			return -ENOMEM;
+		}
+	}
+
+	return __mprotect_fixup(vma, pprev, start, end, newflags);
+}
+
+static int __mprotect_fixup(struct vm_area_struct * vma, struct vm_area_struct ** pprev,
+	unsigned long start, unsigned long end, unsigned int newflags)
+{
+	pgprot_t newprot;
+	int error;
+#else
 static int mprotect_fixup(struct vm_area_struct * vma, struct vm_area_struct ** pprev,
 	unsigned long start, unsigned long end, unsigned int newflags)
 {
@@ -246,6 +290,13 @@
 		*pprev = vma;
 		return 0;
 	}
+#endif
+
+#ifdef CONFIG_GRKERNSEC_PAX_PAGEEXEC
+	if (!(current->flags & PF_PAX_PAGEEXEC) && (newflags & (VM_READ|VM_WRITE)))
+		newprot = protection_map[(newflags | VM_EXEC) & 0xf];
+	else
+#endif
 	newprot = protection_map[newflags & 0xf];
 	if (start == vma->vm_start) {
 		if (end == vma->vm_end)
@@ -264,6 +315,69 @@
 	return 0;
 }
 
+#ifdef CONFIG_GRKERNSEC_PAX_MPROTECT
+/* PaX: non-PIC ELF libraries need relocations on their executable segments
+ * therefore we'll grant them VM_MAYWRITE once during their life.
+ *
+ * The checks favor ld-linux.so behaviour which operates on a per ELF segment
+ * basis because we want to allow the common case and not the special ones.
+ */
+static inline void pax_handle_maywrite(struct vm_area_struct * vma, unsigned long start)
+{
+	struct elfhdr elf_h;
+	struct elf_phdr elf_p, p_dyn;
+	elf_dyn dyn;
+	unsigned long i, j = 65536UL / sizeof(struct elf_phdr);
+
+#ifndef CONFIG_GRKERNSEC_PAX_NOELFRELOCS
+	if ((vma->vm_start != start) ||
+	    !vma->vm_file ||
+	    !(vma->vm_flags & VM_MAYEXEC) ||
+	    (vma->vm_flags & VM_MAYNOTWRITE))
+#endif
+
+		return;
+
+	if (0 > kernel_read(vma->vm_file, 0UL, (char*)&elf_h, sizeof(elf_h)) ||
+	    memcmp(elf_h.e_ident, ELFMAG, SELFMAG) ||
+
+#ifdef CONFIG_GRKERNSEC_PAX_ETEXECRELOCS
+	    (elf_h.e_type != ET_DYN && elf_h.e_type != ET_EXEC) ||
+#else
+	    elf_h.e_type != ET_DYN ||
+#endif
+
+	    !elf_check_arch(&elf_h) ||
+	    elf_h.e_phentsize != sizeof(struct elf_phdr) ||
+	    elf_h.e_phnum > j)
+		return;
+
+	for (i = 0UL; i < elf_h.e_phnum; i++) {
+		if (0 > kernel_read(vma->vm_file, elf_h.e_phoff + i*sizeof(elf_p), (char*)&elf_p, sizeof(elf_p)))
+			return;
+		if (elf_p.p_type == PT_DYNAMIC) {
+			p_dyn = elf_p;
+			j = i;
+		}
+	}
+	if (elf_h.e_phnum <= j)
+		return;
+
+	i = 0UL;
+	do {
+		if (0 > kernel_read(vma->vm_file, p_dyn.p_offset + i*sizeof(dyn), (char*)&dyn, sizeof(dyn)))
+			return;
+		if (dyn.d_tag == DT_TEXTREL || (dyn.d_tag == DT_FLAGS && (dyn.d_un.d_val & DF_TEXTREL))) {
+			vma->vm_flags |= VM_MAYWRITE | VM_MAYNOTWRITE;
+			gr_log_textrel(vma);
+			return;
+		}
+		i++;
+	} while (dyn.d_tag != DT_NULL);
+	return;
+}
+#endif
+
 asmlinkage long sys_mprotect(unsigned long start, size_t len, unsigned long prot)
 {
 	unsigned long nstart, end, tmp;
@@ -276,6 +390,17 @@
 	end = start + len;
 	if (end < start)
 		return -ENOMEM;
+
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	if (current->flags & PF_PAX_SEGMEXEC) {
+		if (end > SEGMEXEC_TASK_SIZE)
+			return -EINVAL;
+	} else
+#endif
+
+	if (end > TASK_SIZE)
+		return -EINVAL;
+
 	if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
 		return -EINVAL;
 	if (end == start)
@@ -288,6 +413,16 @@
 	if (!vma || vma->vm_start > start)
 		goto out;
 
+	if (!gr_acl_handle_mprotect(vma->vm_file, prot)) {
+		error = -EACCES;
+		goto out;
+	}
+
+#ifdef CONFIG_GRKERNSEC_PAX_MPROTECT
+	if ((current->flags & PF_PAX_MPROTECT) && (prot & PROT_WRITE))
+		pax_handle_maywrite(vma, start);
+#endif
+
 	for (nstart = start ; ; ) {
 		unsigned int newflags;
 		int last = 0;
@@ -300,6 +435,12 @@
 			goto out;
 		}
 
+#ifdef CONFIG_GRKERNSEC_PAX_MPROTECT
+		/* PaX: disallow write access after relocs are done, hopefully noone else needs it... */
+		if ((current->flags & PF_PAX_MPROTECT) && (prot & PROT_WRITE) && (vma->vm_flags & VM_MAYNOTWRITE))
+			newflags &= ~VM_MAYWRITE;
+#endif
+
 		if (vma->vm_end > end) {
 			error = mprotect_fixup(vma, &prev, nstart, end, newflags);
 			goto out;
@@ -332,6 +473,7 @@
 		prev->vm_mm->map_count--;
 	}
 out:
+
 	up_write(&current->mm->mmap_sem);
 	return error;
 }
diff -urN --exclude-from=diff-exclude linux-2.4.27/mm/mremap.c linux-2.4.27-leo/mm/mremap.c
--- linux-2.4.27/mm/mremap.c	2004-05-19 21:34:41.000000000 +0100
+++ linux-2.4.27-leo/mm/mremap.c	2004-09-17 03:19:58.000000000 +0100
@@ -197,7 +197,9 @@
 		}
 
 		do_munmap(current->mm, addr, old_len);
-
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+		if (!(new_vma->vm_flags & VM_MIRROR))
+#endif
 		current->mm->total_vm += new_len >> PAGE_SHIFT;
 		if (vm_locked) {
 			current->mm->locked_vm += new_len >> PAGE_SHIFT;
@@ -236,6 +238,18 @@
 	old_len = PAGE_ALIGN(old_len);
 	new_len = PAGE_ALIGN(new_len);
 
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+	if (current->flags & PF_PAX_SEGMEXEC) {
+		if (new_len > SEGMEXEC_TASK_SIZE || addr > SEGMEXEC_TASK_SIZE-new_len ||
+	            old_len > SEGMEXEC_TASK_SIZE || addr > SEGMEXEC_TASK_SIZE-old_len)
+			goto out;
+	} else
+#endif
+
+	if (new_len > TASK_SIZE || addr > TASK_SIZE-new_len ||
+	    old_len > TASK_SIZE || addr > TASK_SIZE-old_len)
+		goto out;
+
 	/* new_addr is only valid if MREMAP_FIXED is specified */
 	if (flags & MREMAP_FIXED) {
 		if (new_addr & ~PAGE_MASK)
@@ -243,6 +257,13 @@
 		if (!(flags & MREMAP_MAYMOVE))
 			goto out;
 
+#ifdef CONFIG_GRKERNSEC_PAX_SEGMEXEC
+		if (current->flags & PF_PAX_SEGMEXEC) {
+			if (new_len > SEGMEXEC_TASK_SIZE || new_addr > SEGMEXEC_TASK_SIZE-new_len)
+			goto out;
+		} else
+#endif
+
 		if (new_len > TASK_SIZE || new_addr > TASK_SIZE - new_len)
 			goto out;
 		/*
@@ -287,6 +308,16 @@
 	vma = find_vma(current->mm, addr);
 	if (!vma || vma->vm_start > addr)
 		goto out;
+
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+	if ((current->flags & (PF_PAX_SEGMEXEC | PF_PAX_RANDEXEC)) &&
+	    (vma->vm_flags & VM_MIRROR))
+	{
+		ret = -EINVAL;
+		goto out;
+	}
+#endif
+
 	/* We can't remap across vm area boundaries */
 	if (old_len > vma->vm_end - addr)
 		goto out;
@@ -298,13 +329,22 @@
 		unsigned long locked = current->mm->locked_vm << PAGE_SHIFT;
 		locked += new_len - old_len;
 		ret = -EAGAIN;
+		gr_learn_resource(current, RLIMIT_MEMLOCK, locked, 1);
 		if (locked > current->rlim[RLIMIT_MEMLOCK].rlim_cur)
 			goto out;
 	}
 	ret = -ENOMEM;
+
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+	if (!(vma->vm_flags & VM_MIRROR)) {
+#endif   
+	gr_learn_resource(current, RLIMIT_AS, (current->mm->total_vm << PAGE_SHIFT) + (new_len - old_len), 1);
 	if ((current->mm->total_vm << PAGE_SHIFT) + (new_len - old_len)
 	    > current->rlim[RLIMIT_AS].rlim_cur)
 		goto out;
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+	}
+#endif
 	/* Private writable mapping? Check memory availability.. */
 	if ((vma->vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE &&
 	    !(flags & MAP_NORESERVE)				 &&
@@ -326,6 +366,9 @@
 			spin_lock(&vma->vm_mm->page_table_lock);
 			vma->vm_end = addr + new_len;
 			spin_unlock(&vma->vm_mm->page_table_lock);
+#if defined(CONFIG_GRKERNSEC_PAX_SEGMEXEC) || defined(CONFIG_GRKERNSEC_PAX_RANDEXEC)
+			if (!(vma->vm_flags & VM_MIRROR))
+#endif   
 			current->mm->total_vm += pages;
 			if (vma->vm_flags & VM_LOCKED) {
 				current->mm->locked_vm += pages;
diff -urN --exclude-from=diff-exclude linux-2.4.27/mm/slab.c linux-2.4.27-leo/mm/slab.c
--- linux-2.4.27/mm/slab.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.27-leo/mm/slab.c	2004-09-17 03:19:36.000000000 +0100
@@ -49,7 +49,8 @@
  *  constructors and destructors are called without any locking.
  *  Several members in kmem_cache_t and slab_t never change, they
  *	are accessed without any locking.
- *  The per-cpu arrays are never accessed from the wrong cpu, no locking.
+ *  The per-cpu arrays are never accessed from the wrong cpu, no locking,
+ *  	and local interrupts are disabled so slab code is preempt-safe.
  *  The non-constant members are protected with a per-cache irq spinlock.
  *
  * Further notes from the original documentation:
@@ -858,12 +859,14 @@
  */
 static void smp_call_function_all_cpus(void (*func) (void *arg), void *arg)
 {
+	preempt_disable();
 	local_irq_disable();
 	func(arg);
 	local_irq_enable();
 
 	if (smp_call_function(func, arg, 1, 1))
 		BUG();
+	preempt_enable();
 }
 typedef struct ccupdate_struct_s
 {
diff -urN --exclude-from=diff-exclude linux-2.4.27/mm/vmalloc.c linux-2.4.27-leo/mm/vmalloc.c
--- linux-2.4.27/mm/vmalloc.c	2004-05-19 21:34:41.000000000 +0100
+++ linux-2.4.27-leo/mm/vmalloc.c	2004-09-17 03:19:58.000000000 +0100
@@ -140,7 +140,7 @@
 	if (end > PGDIR_SIZE)
 		end = PGDIR_SIZE;
 	do {
-		pte_t * pte = pte_alloc(&init_mm, pmd, address);
+		pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address);
 		if (!pte)
 			return -ENOMEM;
 		if (alloc_area_pte(pte, address, end - address,
@@ -382,3 +382,22 @@
 	read_unlock(&vmlist_lock);
 	return buf - buf_start;
 }
+
+void *vcalloc(unsigned long nmemb, unsigned long elem_size)
+{
+	unsigned long size;
+	void *addr;
+
+	/*
+	 * Check that we're not going to overflow.
+	 */
+	if (nmemb > (ULONG_MAX / elem_size))
+		return NULL;
+
+	size = nmemb * elem_size;
+	addr = vmalloc(size);
+	if (addr)
+		memset(addr, 0, size);
+
+	return addr;
+}
diff -urN --exclude-from=diff-exclude linux-2.4.27/net/core/dev.c linux-2.4.27-leo/net/core/dev.c
--- linux-2.4.27/net/core/dev.c	2004-05-19 21:34:42.000000000 +0100
+++ linux-2.4.27-leo/net/core/dev.c	2004-09-17 03:19:36.000000000 +0100
@@ -1093,9 +1093,15 @@
 		int cpu = smp_processor_id();
 
 		if (dev->xmit_lock_owner != cpu) {
+			/*
+			 * The spin_lock effectivly does a preempt lock, but 
+			 * we are about to drop that...
+			 */
+			preempt_disable();
 			spin_unlock(&dev->queue_lock);
 			spin_lock(&dev->xmit_lock);
 			dev->xmit_lock_owner = cpu;
+			preempt_enable();
 
 			if (!netif_queue_stopped(dev)) {
 				if (netdev_nit)
@@ -1274,7 +1280,7 @@
 
 int netif_rx(struct sk_buff *skb)
 {
-	int this_cpu = smp_processor_id();
+	int this_cpu;
 	struct softnet_data *queue;
 	unsigned long flags;
 
@@ -1284,9 +1290,10 @@
 	/* The code is rearranged so that the path is the most
 	   short when CPU is congested, but is still operating.
 	 */
-	queue = &softnet_data[this_cpu];
 
 	local_irq_save(flags);
+	this_cpu = smp_processor_id();
+	queue = &softnet_data[this_cpu];
 
 	netdev_rx_stat[this_cpu].total++;
 	if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {
diff -urN --exclude-from=diff-exclude linux-2.4.27/net/core/skbuff.c linux-2.4.27-leo/net/core/skbuff.c
--- linux-2.4.27/net/core/skbuff.c	2003-08-25 12:44:44.000000000 +0100
+++ linux-2.4.27-leo/net/core/skbuff.c	2004-09-17 03:19:36.000000000 +0100
@@ -111,33 +111,37 @@
 
 static __inline__ struct sk_buff *skb_head_from_pool(void)
 {
-	struct sk_buff_head *list = &skb_head_pool[smp_processor_id()].list;
+	struct sk_buff_head *list;
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
 
-	if (skb_queue_len(list)) {
-		struct sk_buff *skb;
-		unsigned long flags;
+	local_irq_save(flags);
 
-		local_irq_save(flags);
+	list = &skb_head_pool[smp_processor_id()].list;
+
+	if (skb_queue_len(list))
 		skb = __skb_dequeue(list);
-		local_irq_restore(flags);
-		return skb;
-	}
-	return NULL;
+
+	local_irq_restore(flags);
+	return skb;
 }
 
 static __inline__ void skb_head_to_pool(struct sk_buff *skb)
 {
-	struct sk_buff_head *list = &skb_head_pool[smp_processor_id()].list;
+	struct sk_buff_head *list;
+	unsigned long flags;
 
-	if (skb_queue_len(list) < sysctl_hot_list_len) {
-		unsigned long flags;
+	local_irq_save(flags);
+	list = &skb_head_pool[smp_processor_id()].list;
 
-		local_irq_save(flags);
+	if (skb_queue_len(list) < sysctl_hot_list_len) {
 		__skb_queue_head(list, skb);
 		local_irq_restore(flags);
 
 		return;
 	}
+
+	local_irq_restore(flags);
 	kmem_cache_free(skbuff_head_cache, skb);
 }
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/net/ipv4/af_inet.c linux-2.4.27-leo/net/ipv4/af_inet.c
--- linux-2.4.27/net/ipv4/af_inet.c	2004-09-17 02:38:47.000000000 +0100
+++ linux-2.4.27-leo/net/ipv4/af_inet.c	2004-09-17 03:19:58.000000000 +0100
@@ -83,6 +83,7 @@
 #include <linux/init.h>
 #include <linux/poll.h>
 #include <linux/netfilter_ipv4.h>
+#include <linux/grsecurity.h>
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -374,7 +375,12 @@
 	else
 		sk->protinfo.af_inet.pmtudisc = IP_PMTUDISC_WANT;
 
-	sk->protinfo.af_inet.id = 0;
+#ifdef CONFIG_GRKERNSEC_RANDID
+	if(grsec_enable_randid)
+		sk->protinfo.af_inet.id = htons(ip_randomid());
+	else
+#endif
+		sk->protinfo.af_inet.id = 0;
 
 	sock_init_data(sock,sk);
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/net/ipv4/ip_output.c linux-2.4.27-leo/net/ipv4/ip_output.c
--- linux-2.4.27/net/ipv4/ip_output.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.27-leo/net/ipv4/ip_output.c	2004-09-17 03:19:58.000000000 +0100
@@ -77,6 +77,7 @@
 #include <linux/netfilter_ipv4.h>
 #include <linux/mroute.h>
 #include <linux/netlink.h>
+#include <linux/grsecurity.h>
 
 /*
  *      Shall we try to damage output packets if routing dev changes?
@@ -514,7 +515,13 @@
 	 *	Begin outputting the bytes.
 	 */
 
-	id = sk->protinfo.af_inet.id++;
+#ifdef CONFIG_GRKERNSEC_RANDID
+	if(grsec_enable_randid) { 
+		id = htons(ip_randomid());	
+		sk->protinfo.af_inet.id = htons(ip_randomid());
+	} else
+#endif
+		id = sk->protinfo.af_inet.id++;
 
 	do {
 		char *data;
diff -urN --exclude-from=diff-exclude linux-2.4.27/net/ipv4/netfilter/Config.in linux-2.4.27-leo/net/ipv4/netfilter/Config.in
--- linux-2.4.27/net/ipv4/netfilter/Config.in	2003-08-25 12:44:44.000000000 +0100
+++ linux-2.4.27-leo/net/ipv4/netfilter/Config.in	2004-09-17 03:19:58.000000000 +0100
@@ -33,6 +33,7 @@
   dep_tristate '  LENGTH match support' CONFIG_IP_NF_MATCH_LENGTH $CONFIG_IP_NF_IPTABLES
   dep_tristate '  TTL match support' CONFIG_IP_NF_MATCH_TTL $CONFIG_IP_NF_IPTABLES
   dep_tristate '  tcpmss match support' CONFIG_IP_NF_MATCH_TCPMSS $CONFIG_IP_NF_IPTABLES
+  dep_tristate '  stealth match support' CONFIG_IP_NF_MATCH_STEALTH $CONFIG_IP_NF_IPTABLES
   if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then
     dep_tristate '  Helper match support' CONFIG_IP_NF_MATCH_HELPER $CONFIG_IP_NF_IPTABLES
   fi
diff -urN --exclude-from=diff-exclude linux-2.4.27/net/ipv4/netfilter/ipt_stealth.c linux-2.4.27-leo/net/ipv4/netfilter/ipt_stealth.c
--- linux-2.4.27/net/ipv4/netfilter/ipt_stealth.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.27-leo/net/ipv4/netfilter/ipt_stealth.c	2004-09-17 03:19:58.000000000 +0100
@@ -0,0 +1,109 @@
+/* Kernel module to add stealth support.
+ *
+ * Copyright (C) 2002 Brad Spengler  <spender@grsecurity.net>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/net.h>
+#include <linux/sched.h>
+#include <linux/inet.h>
+#include <linux/stddef.h>
+
+#include <net/ip.h>
+#include <net/sock.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <net/route.h>
+#include <net/inet_common.h>
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+MODULE_LICENSE("GPL");
+
+extern struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, int dif);
+
+static int
+match(const struct sk_buff *skb,
+      const struct net_device *in,
+      const struct net_device *out,
+      const void *matchinfo,
+      int offset,
+      const void *hdr,
+      u_int16_t datalen,
+      int *hotdrop)
+{
+	struct iphdr *ip = skb->nh.iph;
+	struct tcphdr *th = (struct tcphdr *) hdr;
+	struct udphdr *uh = (struct udphdr *) hdr;
+	struct sock *sk = NULL;
+
+	if (!ip || !hdr || offset) return 0;
+
+	switch(ip->protocol) {
+	case IPPROTO_TCP:
+		if (datalen < sizeof(struct tcphdr)) {
+			*hotdrop = 1;
+			return 0;
+		}
+		if (!(th->syn && !th->ack)) return 0;
+		sk = tcp_v4_lookup_listener(ip->daddr, ntohs(th->dest), ((struct rtable*)skb->dst)->rt_iif);	
+		break;
+	case IPPROTO_UDP:
+		if (datalen < sizeof(struct udphdr)) {
+			*hotdrop = 1;
+			return 0;
+		}
+		sk = udp_v4_lookup(ip->saddr, uh->source, ip->daddr, uh->dest, skb->dev->ifindex);
+		break;
+	default:
+		return 0;
+	}
+
+	if(!sk) // port is being listened on, match this
+		return 1;
+	else {
+		sock_put(sk);
+		return 0;
+	}
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+checkentry(const char *tablename,
+           const struct ipt_ip *ip,
+           void *matchinfo,
+           unsigned int matchsize,
+           unsigned int hook_mask)
+{
+        if (matchsize != IPT_ALIGN(0))
+                return 0;
+
+	if(((ip->proto == IPPROTO_TCP && !(ip->invflags & IPT_INV_PROTO)) ||
+		((ip->proto == IPPROTO_UDP) && !(ip->invflags & IPT_INV_PROTO)))
+		&& (hook_mask & (1 << NF_IP_LOCAL_IN)))
+			return 1;
+
+	printk("stealth: Only works on TCP and UDP for the INPUT chain.\n");
+
+        return 0;
+}
+
+
+static struct ipt_match stealth_match
+= { { NULL, NULL }, "stealth", &match, &checkentry, NULL, THIS_MODULE };
+
+static int __init init(void)
+{
+	return ipt_register_match(&stealth_match);
+}
+
+static void __exit fini(void)
+{
+	ipt_unregister_match(&stealth_match);
+}
+
+module_init(init);
+module_exit(fini);
diff -urN --exclude-from=diff-exclude linux-2.4.27/net/ipv4/netfilter/Makefile linux-2.4.27-leo/net/ipv4/netfilter/Makefile
--- linux-2.4.27/net/ipv4/netfilter/Makefile	2003-08-25 12:44:44.000000000 +0100
+++ linux-2.4.27-leo/net/ipv4/netfilter/Makefile	2004-09-17 03:19:58.000000000 +0100
@@ -86,6 +86,7 @@
 obj-$(CONFIG_IP_NF_MATCH_CONNTRACK) += ipt_conntrack.o
 obj-$(CONFIG_IP_NF_MATCH_UNCLEAN) += ipt_unclean.o
 obj-$(CONFIG_IP_NF_MATCH_TCPMSS) += ipt_tcpmss.o
+obj-$(CONFIG_IP_NF_MATCH_STEALTH) += ipt_stealth.o
 
 # targets
 obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o
diff -urN --exclude-from=diff-exclude linux-2.4.27/net/ipv4/tcp_ipv4.c linux-2.4.27-leo/net/ipv4/tcp_ipv4.c
--- linux-2.4.27/net/ipv4/tcp_ipv4.c	2004-05-19 21:34:42.000000000 +0100
+++ linux-2.4.27-leo/net/ipv4/tcp_ipv4.c	2004-09-17 03:19:58.000000000 +0100
@@ -67,6 +67,7 @@
 #include <linux/inet.h>
 #include <linux/stddef.h>
 #include <linux/ipsec.h>
+#include <linux/grsecurity.h>
 
 extern int sysctl_ip_dynaddr;
 extern int sysctl_ip_default_ttl;
@@ -223,9 +224,15 @@
 
 		spin_lock(&tcp_portalloc_lock);
 		rover = tcp_port_rover;
-		do {	rover++;
+#ifdef CONFIG_GRKERNSEC_RANDSRC
+		if (grsec_enable_randsrc && (high > low))
+			rover = low + (get_random_long() % remaining);
+#endif
+                do {
+			rover++;
 			if ((rover < low) || (rover > high))
 				rover = low;
+
 			head = &tcp_bhash[tcp_bhashfn(rover)];
 			spin_lock(&head->lock);
 			for (tb = head->chain; tb; tb = tb->next)
@@ -548,6 +555,11 @@
 
 static inline __u32 tcp_v4_init_sequence(struct sock *sk, struct sk_buff *skb)
 {
+#ifdef CONFIG_GRKERNSEC_RANDISN
+	if (likely(grsec_enable_randisn))
+		return ip_randomisn();
+	else
+#endif
 	return secure_tcp_sequence_number(skb->nh.iph->daddr,
 					  skb->nh.iph->saddr,
 					  skb->h.th->dest,
@@ -681,11 +693,15 @@
 		 */
 		spin_lock(&tcp_portalloc_lock);
 		rover = tcp_port_rover;
-
+#ifdef CONFIG_GRKERNSEC_RANDSRC
+		if (grsec_enable_randsrc && (high > low))
+			rover = low + (get_random_long() % remaining);
+#endif
 		do {
 			rover++;
 			if ((rover < low) || (rover > high))
 				rover = low;
+
 			head = &tcp_bhash[tcp_bhashfn(rover)];
 			spin_lock(&head->lock);		
 
@@ -734,6 +750,15 @@
 		}
 		spin_unlock(&head->lock);
 
+#ifdef CONFIG_GRKERNSEC
+		gr_del_task_from_ip_table(current);
+		current->gr_saddr = sk->rcv_saddr;
+		current->gr_daddr = sk->daddr;
+		current->gr_sport = sk->sport;
+		current->gr_dport = sk->dport;
+		gr_add_to_task_ip_table(current);
+#endif
+
 		if (tw) {
 			tcp_tw_deschedule(tw);
 			tcp_timewait_kill(tw);
@@ -846,11 +871,22 @@
 	if (err)
 		goto failure;
 
-	if (!tp->write_seq)
+	if (!tp->write_seq) {
+#ifdef CONFIG_GRKERNSEC_RANDISN
+		if (likely(grsec_enable_randisn))
+			tp->write_seq = ip_randomisn();
+		else
+#endif
 		tp->write_seq = secure_tcp_sequence_number(sk->saddr, sk->daddr,
 							   sk->sport, usin->sin_port);
+	}
 
-	sk->protinfo.af_inet.id = tp->write_seq^jiffies;
+#ifdef CONFIG_GRKERNSEC_RANDID
+	if(grsec_enable_randid)
+		sk->protinfo.af_inet.id = htons(ip_randomid());
+	else
+#endif
+		sk->protinfo.af_inet.id = tp->write_seq^jiffies;
 
 	err = tcp_connect(sk);
 	if (err)
@@ -1572,7 +1608,13 @@
 	newtp->ext_header_len = 0;
 	if (newsk->protinfo.af_inet.opt)
 		newtp->ext_header_len = newsk->protinfo.af_inet.opt->optlen;
-	newsk->protinfo.af_inet.id = newtp->write_seq^jiffies;
+
+#ifdef CONFIG_GRKERNSEC_RANDID
+	if(grsec_enable_randid)
+		newsk->protinfo.af_inet.id = htons(ip_randomid());
+	else
+#endif
+		newsk->protinfo.af_inet.id = newtp->write_seq^jiffies;
 
 	tcp_sync_mss(newsk, dst->pmtu);
 	newtp->advmss = dst->advmss;
diff -urN --exclude-from=diff-exclude linux-2.4.27/net/ipv4/udp.c linux-2.4.27-leo/net/ipv4/udp.c
--- linux-2.4.27/net/ipv4/udp.c	2004-09-17 02:38:47.000000000 +0100
+++ linux-2.4.27-leo/net/ipv4/udp.c	2004-09-17 03:19:58.000000000 +0100
@@ -91,6 +91,7 @@
 #include <net/ipv6.h>
 #include <net/protocol.h>
 #include <linux/skbuff.h>
+#include <linux/grsecurity.h>
 #include <net/sock.h>
 #include <net/udp.h>
 #include <net/icmp.h>
@@ -98,6 +99,11 @@
 #include <net/inet_common.h>
 #include <net/checksum.h>
 
+extern int gr_search_udp_recvmsg(const struct sock *sk,
+					const struct sk_buff *skb);
+extern int gr_search_udp_sendmsg(const struct sock *sk,
+					const struct sockaddr_in *addr);
+
 /*
  *	Snmp MIB for the UDP layer
  */
@@ -480,9 +486,16 @@
 		ufh.uh.dest = usin->sin_port;
 		if (ufh.uh.dest == 0)
 			return -EINVAL;
+
+		if (!gr_search_udp_sendmsg(sk, usin))
+			return -EPERM;
 	} else {
 		if (sk->state != TCP_ESTABLISHED)
 			return -EDESTADDRREQ;
+
+		if (!gr_search_udp_sendmsg(sk, NULL))
+			return -EPERM;
+
 		ufh.daddr = sk->daddr;
 		ufh.uh.dest = sk->dport;
 		/* Open fast path for connected socket.
@@ -662,6 +675,11 @@
 	if (!skb)
 		goto out;
   
+	if (!gr_search_udp_recvmsg(sk, skb)) {
+		err = -EPERM;
+		goto out_free;
+	}
+
   	copied = skb->len - sizeof(struct udphdr);
 	if (copied > len) {
 		copied = len;
@@ -771,7 +789,13 @@
 	sk->daddr = rt->rt_dst;
 	sk->dport = usin->sin_port;
 	sk->state = TCP_ESTABLISHED;
-	sk->protinfo.af_inet.id = jiffies;
+
+#ifdef CONFIG_GRKERNSEC_RANDID
+	if(grsec_enable_randid)
+		sk->protinfo.af_inet.id = htons(ip_randomid());
+	else
+#endif
+		sk->protinfo.af_inet.id = jiffies;
 
 	sk_dst_set(sk, &rt->u.dst);
 	return(0);
diff -urN --exclude-from=diff-exclude linux-2.4.27/net/netlink/af_netlink.c linux-2.4.27-leo/net/netlink/af_netlink.c
--- linux-2.4.27/net/netlink/af_netlink.c	2004-02-20 14:11:47.000000000 +0000
+++ linux-2.4.27-leo/net/netlink/af_netlink.c	2004-09-17 03:19:58.000000000 +0100
@@ -40,6 +40,7 @@
 #include <linux/proc_fs.h>
 #include <linux/smp_lock.h>
 #include <linux/notifier.h>
+#include <linux/grsecurity.h>
 #include <net/sock.h>
 #include <net/scm.h>
 
@@ -626,7 +627,8 @@
 	   check them, when this message will be delivered
 	   to corresponding kernel module.   --ANK (980802)
 	 */
-	NETLINK_CB(skb).eff_cap = current->cap_effective;
+
+	NETLINK_CB(skb).eff_cap = gr_cap_rtnetlink();
 
 	err = -EFAULT;
 	if (memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len)) {
diff -urN --exclude-from=diff-exclude linux-2.4.27/net/netsyms.c linux-2.4.27-leo/net/netsyms.c
--- linux-2.4.27/net/netsyms.c	2004-09-17 02:38:47.000000000 +0100
+++ linux-2.4.27-leo/net/netsyms.c	2004-09-17 03:19:58.000000000 +0100
@@ -24,6 +24,7 @@
 #include <net/checksum.h>
 #include <linux/etherdevice.h>
 #include <net/route.h>
+#include <linux/grsecurity.h>
 #ifdef CONFIG_HIPPI
 #include <linux/hippidevice.h>
 #endif
@@ -611,6 +612,49 @@
 
 EXPORT_SYMBOL(softnet_data);
 
+#if defined(CONFIG_IP_NF_MATCH_STEALTH_MODULE)
+#if !defined (CONFIG_IPV6_MODULE) && !defined (CONFIG_KHTTPD) && !defined (CONFIG_KHTTPD_MODULE) && !defined (CONFIG_IP_SCTP_MODULE)
+EXPORT_SYMBOL(tcp_v4_lookup_listener);
+#endif
+extern struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, int dif);
+EXPORT_SYMBOL(udp_v4_lookup);
+#endif
+
+#if defined(CONFIG_GRKERNSEC_RANDID)
+EXPORT_SYMBOL(ip_randomid);
+#endif
+#if defined(CONFIG_GRKERNSEC_RANDSRC) || defined(CONFIG_GRKERNSEC_RANDRPC)
+EXPORT_SYMBOL(get_random_long);
+#endif
+#ifdef CONFIG_GRKERNSEC_RANDISN
+EXPORT_SYMBOL(ip_randomisn);
+EXPORT_SYMBOL(grsec_enable_randisn);
+#endif
+#ifdef CONFIG_GRKERNSEC_RANDID
+EXPORT_SYMBOL(grsec_enable_randid);
+#endif
+#ifdef CONFIG_GRKERNSEC_RANDSRC
+EXPORT_SYMBOL(grsec_enable_randsrc);
+#endif
+#ifdef CONFIG_GRKERNSEC_RANDRPC
+EXPORT_SYMBOL(grsec_enable_randrpc);
+#endif
+
+EXPORT_SYMBOL(gr_cap_rtnetlink);
+
+extern int gr_search_udp_recvmsg(const struct sock *sk, const struct sk_buff *skb);
+extern int gr_search_udp_sendmsg(const struct sock *sk, const struct sockaddr_in *addr);
+
+EXPORT_SYMBOL(gr_search_udp_recvmsg);
+EXPORT_SYMBOL(gr_search_udp_sendmsg);
+
+#ifdef CONFIG_UNIX_MODULE
+EXPORT_SYMBOL(gr_acl_handle_unix);
+EXPORT_SYMBOL(gr_acl_handle_mknod);
+EXPORT_SYMBOL(gr_handle_chroot_unix);
+EXPORT_SYMBOL(gr_handle_create);
+#endif
+
 #if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO)
 #include <net/iw_handler.h>
 EXPORT_SYMBOL(wireless_send_event);
diff -urN --exclude-from=diff-exclude linux-2.4.27/net/socket.c linux-2.4.27-leo/net/socket.c
--- linux-2.4.27/net/socket.c	2004-02-20 14:11:48.000000000 +0000
+++ linux-2.4.27-leo/net/socket.c	2004-09-17 03:19:58.000000000 +0100
@@ -85,6 +85,18 @@
 #include <net/scm.h>
 #include <linux/netfilter.h>
 
+extern void gr_attach_curr_ip(const struct sock *sk);
+extern int gr_handle_sock_all(const int family, const int type,
+			      const int protocol);
+extern int gr_handle_sock_server(const struct sockaddr *sck);  
+extern int gr_handle_sock_client(const struct sockaddr *sck);
+extern int gr_search_connect(const struct socket * sock,
+			     const struct sockaddr_in * addr);  
+extern int gr_search_bind(const struct socket * sock,
+			  const struct sockaddr_in * addr);
+extern int gr_search_socket(const int domain, const int type,
+			    const int protocol);
+
 static int sock_no_open(struct inode *irrelevant, struct file *dontcare);
 static ssize_t sock_read(struct file *file, char *buf,
 			 size_t size, loff_t *ppos);
@@ -132,7 +144,7 @@
 
 static struct net_proto_family *net_families[NPROTO];
 
-#ifdef CONFIG_SMP
+#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT)
 static atomic_t net_family_lockct = ATOMIC_INIT(0);
 static spinlock_t net_family_lock = SPIN_LOCK_UNLOCKED;
 
@@ -903,6 +915,16 @@
 	int retval;
 	struct socket *sock;
 
+	if(!gr_search_socket(family, type, protocol)) {
+		retval = -EACCES;
+		goto out;
+	}
+
+	if (gr_handle_sock_all(family, type, protocol)) {
+		retval = -EACCES;
+		goto out;
+	}
+
 	retval = sock_create(family, type, protocol, &sock);
 	if (retval < 0)
 		goto out;
@@ -998,12 +1020,26 @@
 {
 	struct socket *sock;
 	char address[MAX_SOCK_ADDR];
+	struct sockaddr * sck;
 	int err;
 
 	if((sock = sockfd_lookup(fd,&err))!=NULL)
 	{
-		if((err=move_addr_to_kernel(umyaddr,addrlen,address))>=0)
+		if((err=move_addr_to_kernel(umyaddr,addrlen,address))>=0) {
+			sck = (struct sockaddr *) address;
+
+			if(!gr_search_bind(sock, (struct sockaddr_in *) sck)) {
+				sockfd_put(sock);
+				return -EACCES;
+			}
+
+			if (gr_handle_sock_server(sck)) {
+				sockfd_put(sock);
+				return -EACCES;
+			}
+
 			err = sock->ops->bind(sock, (struct sockaddr *)address, addrlen);
+		}
 		sockfd_put(sock);
 	}			
 	return err;
@@ -1081,6 +1117,8 @@
 	if ((err = sock_map_fd(newsock)) < 0)
 		goto out_release;
 
+	gr_attach_curr_ip(newsock->sk);
+
 out_put:
 	sockfd_put(sock);
 out:
@@ -1108,6 +1146,7 @@
 {
 	struct socket *sock;
 	char address[MAX_SOCK_ADDR];
+	struct sockaddr * sck;
 	int err;
 
 	sock = sockfd_lookup(fd, &err);
@@ -1116,6 +1155,19 @@
 	err = move_addr_to_kernel(uservaddr, addrlen, address);
 	if (err < 0)
 		goto out_put;
+
+	sck = (struct sockaddr *) address;
+
+	if (!gr_search_connect(sock, (struct sockaddr_in *) sck)) {
+		err = -EACCES;
+		goto out_put;
+	}
+
+	if (gr_handle_sock_client(sck)) {
+		err = -EACCES;
+		goto out_put;
+	}
+
 	err = sock->ops->connect(sock, (struct sockaddr *) address, addrlen,
 				 sock->file->f_flags);
 out_put:
diff -urN --exclude-from=diff-exclude linux-2.4.27/net/sunrpc/pmap_clnt.c linux-2.4.27-leo/net/sunrpc/pmap_clnt.c
--- linux-2.4.27/net/sunrpc/pmap_clnt.c	2002-08-03 01:39:46.000000000 +0100
+++ linux-2.4.27-leo/net/sunrpc/pmap_clnt.c	2004-09-17 03:19:36.000000000 +0100
@@ -12,6 +12,7 @@
 #include <linux/config.h>
 #include <linux/types.h>
 #include <linux/socket.h>
+#include <linux/sched.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/uio.h>
diff -urN --exclude-from=diff-exclude linux-2.4.27/net/sunrpc/xprt.c linux-2.4.27-leo/net/sunrpc/xprt.c
--- linux-2.4.27/net/sunrpc/xprt.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.27-leo/net/sunrpc/xprt.c	2004-09-17 03:19:58.000000000 +0100
@@ -59,6 +59,7 @@
 #include <linux/unistd.h>
 #include <linux/sunrpc/clnt.h>
 #include <linux/file.h>
+#include <linux/grsecurity.h>
 
 #include <net/sock.h>
 #include <net/checksum.h>
@@ -1297,6 +1298,12 @@
 	}
 	ret = xid++;
 	spin_unlock(&xid_lock);
+
+#ifdef CONFIG_GRKERNSEC_RANDRPC
+	if (grsec_enable_randrpc)
+		ret = (u32) get_random_long();
+#endif
+
 	return ret;
 }
 
diff -urN --exclude-from=diff-exclude linux-2.4.27/net/unix/af_unix.c linux-2.4.27-leo/net/unix/af_unix.c
--- linux-2.4.27/net/unix/af_unix.c	2002-11-28 23:53:16.000000000 +0000
+++ linux-2.4.27-leo/net/unix/af_unix.c	2004-09-17 03:19:58.000000000 +0100
@@ -109,6 +109,7 @@
 #include <linux/poll.h>
 #include <linux/smp_lock.h>
 #include <linux/rtnetlink.h>
+#include <linux/grsecurity.h>
 
 #include <asm/checksum.h>
 
@@ -599,6 +600,11 @@
 		if (err)
 			goto put_fail;
 
+		if (!gr_acl_handle_unix(nd.dentry, nd.mnt)) {
+			err = -EACCES;
+			goto put_fail;
+		}
+		
 		err = -ECONNREFUSED;
 		if (!S_ISSOCK(nd.dentry->d_inode->i_mode))
 			goto put_fail;
@@ -622,6 +628,13 @@
 		if (u) {
 			struct dentry *dentry;
 			dentry = u->protinfo.af_unix.dentry;
+
+			if (!gr_handle_chroot_unix(u->peercred.pid)) {
+				err = -EPERM;
+				sock_put(u);
+				goto fail;
+			}
+
 			if (dentry)
 				UPDATE_ATIME(dentry->d_inode);
 		} else
@@ -720,9 +733,19 @@
 		 * All right, let's create it.
 		 */
 		mode = S_IFSOCK | (sock->inode->i_mode & ~current->fs->umask);
+	
+		if (!gr_acl_handle_mknod(dentry, nd.dentry, nd.mnt, mode)) {
+			err = -EACCES;
+			goto out_mknod_dput;
+		}	
+
 		err = vfs_mknod(nd.dentry->d_inode, dentry, mode, 0);
+
 		if (err)
 			goto out_mknod_dput;
+
+		gr_handle_create(dentry, nd.mnt);
+
 		up(&nd.dentry->d_inode->i_sem);
 		dput(nd.dentry);
 		nd.dentry = dentry;
@@ -740,6 +763,10 @@
 			goto out_unlock;
 		}
 
+#ifdef CONFIG_GRKERNSEC_CHROOT_UNIX
+		sk->peercred.pid = current->pid;
+#endif
+
 		list = &unix_socket_table[addr->hash];
 	} else {
 		list = &unix_socket_table[dentry->d_inode->i_ino & (UNIX_HASH_SIZE-1)];
@@ -866,6 +893,9 @@
 	int st;
 	int err;
 	long timeo;
+#ifdef CONFIG_GRKERNSEC
+	struct task_struct *p, **htable;
+#endif
 
 	err = unix_mkname(sunaddr, addr_len, &hash);
 	if (err < 0)
@@ -989,6 +1019,17 @@
 	/* Set credentials */
 	sk->peercred = other->peercred;
 
+#ifdef CONFIG_GRKERNSEC
+	read_lock(&tasklist_lock);
+	htable = &pidhash[pid_hashfn(other->peercred.pid)];
+	for (p = *htable; p && p->pid != other->peercred.pid; p = p->pidhash_next);
+	if (p) {
+		p->curr_ip = current->curr_ip;
+		p->used_accept = 1;
+	}
+	read_unlock(&tasklist_lock);
+#endif
+
 	sock_hold(newsk);
 	unix_peer(sk)=newsk;
 	sock->state=SS_CONNECTED;
